Skip to content

feat(public-apps): run public coded apps without a sign-in - #773

Draft
Raina451 wants to merge 120 commits into
feat/public-coded-app-sdk-modefrom
fix/pca-sdk-public-runtime
Draft

Raina451 wants to merge 120 commits into
feat/public-coded-app-sdk-modefrom
fix/pca-sdk-public-runtime

Conversation

@Raina451

@Raina451 Raina451 commented Sep 24, 2026 •

Copy link
Copy Markdown
Collaborator

What

This PR lets a public coded app use the SDK without a sign-in. When the page carries uipath:app-key, the SDK switches to public mode and sends supported calls to the Apps service with the visitor's session cookie. It renews the session on a 401.

Supported in public mode:

  • processes.start and jobs.getOutput
  • entities.insertRecord, getRecordByName and getRecordsByName (with pageSize / jumpToPage / cursor)
  • buckets.uploadFile: the service picks the stored path, which comes back as path

Resources are named by their binding (name, plus folderPath when the binding is folder-scoped), the same way bindings_v2.json keys them. The service applies Solutions overrides, so a page's override table is ignored here. Ids and keys are rejected. Any other call throws a ValidationError before reaching the network.

Why

Public apps have no user token, so these calls have to go through the Apps service endpoints that act as the app (business-apps-jamjam /integrations/codedapp/*).

Docs: docs/coded-apps/public-apps.md.

Generated with Claude Code

ninja-shreyash and others added 30 commits August 3, 2026 13:34
…variables pipeline [PLT-104204] (#626)

* feat(cases): add getVariables to CaseInstances [PLT-104204]

Adds getVariables (service-level + bound method) to CaseInstances, reusing
the wire-identical PIMS instances variables endpoint. Shared BPMN variable
parsing/enrichment extracted from ProcessInstancesService into
src/services/maestro/instance-variables.ts (insights.ts precedent); shared
types moved to instance-variables.types.ts with ProcessInstance*/CaseInstance*
aliases kept for API compatibility. Also fixes pre-existing double telemetry
(GetVariables no longer fires GetBpmn) and hoists parsing regexes to module
level.

Co-Authored-By: Claude Fable 5 <[email protected]>

* fix(cases): address review feedback [PLT-104204]

- Replace any with unknown in shared instance-variables types
- Inline {@link} in getVariables @returns; drop misleading optional
  chaining from the JSDoc example

Co-Authored-By: Claude Fable 5 <[email protected]>

* docs(cases): correct bound getVariables @returns wording [PLT-104204]

Co-Authored-By: Claude Fable 5 <[email protected]>

* docs(cases): correct getVariables OAuth scope to PIMS OR.Execution.Read [PLT-104204]

PIMS alone fails the folder-permission check on the variables endpoint
(verified manually); PIMS + OR.Execution.Read is the combination proven
working in E2E and stated in the ticket. Applies to both ProcessInstances
and CaseInstances rows since they share the endpoint.

Co-Authored-By: Claude Fable 5 <[email protected]>

* fix(maestro): handle any BPMN attribute order when resolving variable source [PLT-104204]

The element-name regex required id to precede name; XML does not
guarantee attribute ordering, so name-first elements silently lost
their human-readable source. Extract attributes independently,
mirroring parseBpmnVariables. Adds a reverse-order regression test.

Co-Authored-By: Claude Fable 5 <[email protected]>

* test(cases): drop duplicated BPMN-failure test from getVariables suite [PLT-104204]

Shared-helper degradation behavior is covered in the process-instances
suite; the case-side suite keeps its own wiring, enrichment,
attribute-order, parentElementId, and error tests — matching how
Cases/MaestroProcesses test shared insights methods per service.

Co-Authored-By: Claude Fable 5 <[email protected]>

* fix(maestro): fetch BPMN and variables in parallel in getVariables [PLT-104204]

The two calls are independent; Promise.allSettled preserves the existing
failure contract — BPMN stays best-effort (warn + unenriched globals)
while a variables failure propagates to the caller. Requested in PR review.

Co-Authored-By: Claude Fable 5 <[email protected]>

* refactor(maestro): replace PaginationServiceAccess with AuthenticatedGet [PLT-104204]

Neither endpoint paginates — the accessor was only a bridge to
BaseService's protected get() from the module-level shared helper.
A purpose-named AuthenticatedGet capability declares exactly what the
helper needs; each service passes an arrow closing over its this.get().

Co-Authored-By: Claude Fable 5 <[email protected]>

---------

Co-authored-by: Claude Fable 5 <[email protected]>
* ci(publish): attach packed sdk tarball to GitHub release

Pack the SDK in the same publish run (so the tarball matches the npm
artifact bit-for-bit, telemetry version included) and attach it to the
GitHub release for the version tag — creating the release if it doesn't
exist yet. Gives air-gapped consumers a downloadable install artifact
instead of only the auto-generated source archives.

Co-Authored-By: Claude Fable 5 <[email protected]>

* fix: address convention review comments

Pin gh release create to the built commit via --target GITHUB_SHA and
retry the attach step (it is the only fallible step after npm publish;
a full re-run would fail on the already-published version).

Co-Authored-By: Claude Fable 5 <[email protected]>

* ci(publish): attach air-gapped bundle to GitHub release

Every SDK publish now also builds and attaches a zip containing the
exact published tarballs for @uipath/uipath-typescript (the version just
published) and @uipath/coded-action-app (latest) plus their full
transitive dependency closure, resolved via lockfile walk — nothing
hand-listed. Offline install recipe (npm cache add + npm install
--offline) verified against an unreachable registry.

Co-Authored-By: Claude Fable 5 <[email protected]>

* refactor: simplify airgap bundle script

Same behavior, ~40% smaller: drop redundant guards (execFileSync already
fails loudly; the manifest wipe-guard subsumes the path checks), inline
the single-use functions, and tighten the README.

Co-Authored-By: Claude Fable 5 <[email protected]>

* ci: adopt UiPath/cli export-airgapped pattern for the SDK bundle

Move the bundle out of publish-sdk into a dedicated workflow mirroring
UiPath/cli's export-airgapped.yml: build -> verify -> attach. The verify
job publishes every bundle tarball into a throwaway no-uplink Verdaccio
and installs both SDKs from it alone, so an incomplete bundle can never
reach a release. Triggered by release publication (covers manually
created releases) and dispatched explicitly from publish.yml, whose
GITHUB_TOKEN-created releases cannot fire the release event.

Co-Authored-By: Claude Fable 5 <[email protected]>

* fix: address review comments

Skip the retry sleep after the final attach attempt and fail loudly if
a root package is missing from the resolved closure.

Co-Authored-By: Claude Fable 5 <[email protected]>

* ci: attach assets on release publication, leave publish.yml untouched

Releases stay human-created (draft notes published manually) — publishing
the draft fires this workflow, which attaches both the registry-fetched
SDK tarball and the verified bundle ZIP. Reverts all publish.yml changes
(no release creation, no dispatch, no extra permissions) and gates the
attach job behind the production environment per review feedback.

Co-Authored-By: Claude Fable 5 <[email protected]>

* ci: make airgap bundle workflow release-triggered only

Publishing the draft release is the human approval, so the environment
gate and the manual dispatch path are removed. The workflow publishes
nothing to any registry (verify's npm publish targets a throwaway
localhost Verdaccio); its only side effect is attaching assets to the
release that fired it. Backfill for older releases = run the bundle
script locally + gh release upload.

Co-Authored-By: Claude Fable 5 <[email protected]>

* ci: attach only the bundle zip, matching the cli approach

The standalone SDK tarball was a carryover from this PR's first
iteration — it is not independently installable offline and the same
file ships inside the bundle. One asset, like UiPath/cli.

Co-Authored-By: Claude Fable 5 <[email protected]>

---------

Co-authored-by: Claude Fable 5 <[email protected]>
* chore: bump version to 1.5.6

Co-Authored-By: Claude Fable 5 <[email protected]>

* chore(release-metadata): regenerate for 1.5.6

* chore: bump version to 1.6.0

1.5.6 -> 1.6.0: PR #619 renamed public input/response fields on the
Data Fabric entity schema APIs (fieldName -> name, isInsightsEnabled ->
isAnalyticsEnabled), which is a breaking change and warrants a minor.

Co-Authored-By: Claude Fable 5 <[email protected]>

* chore(release-metadata): regenerate for 1.6.0

---------

Co-authored-by: Claude Fable 5 <[email protected]>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…PLT-106037] (#614)

* feat(functions): add Functions service for invoking coded functions [PLT-106037]

Adds a modular `@uipath/uipath-typescript/functions` service that discovers
and invokes JS/Python coded functions, masking the underlying process + HTTP
trigger plumbing.

- getAll(options?): folder-scoped, paginated listing over /odata/HttpTriggers
- invoke(name, input?, options?): resolves the function by name, resolves the
  folder key, and calls the function's HTTP trigger; returns typed output
- bound invoke() on each getAll item

Folder context accepted as folderId / folderKey / folderPath (or SDK-init
folder context). Verified end-to-end against a live tenant.

Co-Authored-By: Claude Fable 5 <[email protected]>

* feat(functions): long-running invoke support and parent-job attribution [PLT-106037]

- invoke() now follows the gateway's 303 status long-poll chain for functions
  exceeding the ~25s response window: explicit redirect handling, fresh auth
  token per poll leg, bounded by a new maxWaitSeconds option (default 300)
  with a clear timeout error. Browsers fall back to the engine's automatic
  redirect handling (manual redirects are not readable there).
- New jobKey option on invoke: sent as X-UIPATH-JobKey so the run is recorded
  with the parent job's key (parentJobKey) and inherits its context and
  licensing transaction. Applied to the invocation leg only.
- ApiClient: RequestSpec.redirect passthrough and a raw response type so the
  invoke leg can observe the 303 chain.

Co-Authored-By: Claude Fable 5 <[email protected]>

* refactor(functions): single invoke path, package filtering, better lookup errors [PLT-106037]

Coded functions over HTTP are request/response only, so the long-running
handling is removed: no platform branch, no manual 303 loop, no job
polling. invoke() issues one request and returns the output, letting the
engine follow any redirect. Drops the maxWaitSeconds option.

Also:
- filter on processName / processSlug now maps to the Release navigation
  path, so functions can be narrowed to one package server-side
- a name lookup miss lists the folder's function names, since the usual
  cause is passing a package name
- ApiClient no longer sends Content-Type on GET/HEAD, which Orchestrator
  rejects for GET function invocations

Co-Authored-By: Claude <[email protected]>

* refactor(functions): remove dead code from the Functions PR [PLT-106037]

Drops machinery left behind by the abandoned 303 long-poll invoke approach and
tightens the surface the PR adds, so the diff reflects only what the feature needs.

Shared core (reverts unused additions to the HTTP layer):
- RESPONSE_TYPES.RAW, the 'raw' member of ResponseType, and the RAW passthrough
  branch in ApiClient — nothing ever set responseType 'raw'. The platform
  long-polls the job server-side and returns the output as the response body, so
  no caller drives a redirect chain.
- RequestSpec.redirect and its `redirect: options.redirect` fetch passthrough —
  no call site ever set it.

Functions service:
- release.id in RawFunctionTrigger: declared but never read, contradicting the
  type's own "only the fields consumed" contract.
- fn.method.toUpperCase() at the invoke call site: BaseService.request() already
  uppercases the verb.
- Private helpers took `options?` and defended with `options ?? {}` / `options?.x`
  while their only call site always passes a defined object; narrowed to a
  required FolderScopedOptions.
- The "and N more" suffix on the name-lookup error could only ever render
  "and 1 more" ($top caps the response one row past the limit), so it now says
  "and more".
- resourceType label 'Function.getByName' named a method the service does not
  expose; now 'Functions.invoke', matching the convention used elsewhere.
- The subpath barrel exported the class under both Functions and FunctionService;
  only the public alias is exported now, matching Notifications and Governance.
  The unit test imports the class from the implementation file instead.

Docs:
- Every FunctionServiceModel @example called methods on `fns`, a variable no
  example declares (the Usage block declares `functions`). Examples now use
  `functions` throughout, with the getAll result renamed to `deployed` where it
  would otherwise shadow it.
- The module @example used a literal folderId; now the <folderId> placeholder.

Tests:
- Deleted functions.browser.test.ts. Its only distinguishing assertion was
  `expect.not.objectContaining({ redirect: 'manual' })` against the now-removed
  field, and the service has no platform branch, so the rest duplicated
  functions.test.ts.
- Removed the unused ERROR_FUNCTION_NOT_FOUND test constant.

Also drops an unrelated "AI App Builders" mkdocs nav section that leaked into
this branch; it points at pages that live on the docs/ai-app-builders branch and
would have published five dead nav entries.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

* refactor(functions): test hygiene, mock types and doc comments [PLT-106037]

- functions.test.ts called vi.unstubAllGlobals() in afterEach while stubbing no
  globals at all — the repo's only unpaired instance (jobs and
  conversational-agent each pair one unstub with 4-8 stubs).
- RawFunctionTrigger.folderName's "(null on list responses)" qualifier could not
  discriminate anything: the service only ever reads the HttpTriggers collection,
  so every response it sees is a list response.
- Dropped an orphaned {@link} line TypeDoc would render as stray text.
- createMockRawFunctionTrigger typed as Record<string, unknown> instead of any.
- Added the missing afterEach teardown to the model test.
- FunctionMap's comment claimed it mapped responses; it is only used by
  transformOptions for OData query rewriting.

Co-Authored-By: Claude <[email protected]>

* refactor(functions): address review feedback on docs and types [PLT-106037]

- Drop folderName: HttpTriggers is folder-scoped (400s without a folder
  header), so OrganizationUnitFullyQualifiedName is always null — confirmed on
  the list query, with $select, and on HttpTriggers({id}). The mock asserted a
  value the API never returns.
- Remove the execution-limit note from invoke(); the platform has not settled
  on a duration yet.
- Bound-method guards throw ValidationError instead of a generic Error.
- jobKey doc is one line and no longer names the internal header.
- Note that inputArguments carries sample values, not a schema.
- Link the OR.Default scope docs.

Co-Authored-By: Claude <[email protected]>

* refactor(functions): wrap invoke identifier in a FunctionRef object [PLT-106037]

Take a FunctionRef ({ name }) instead of a bare name string so the
identifier can be extended (e.g. to accept id) without breaking the
signature.

Generated with Claude Code

Co-Authored-By: Claude <[email protected]>

* refactor(functions): drop `any` from function test types [PLT-106037]

- Type createMockTransformedFunctionCollection's return as
  NonPaginatedResponse<FunctionGetResponse> (the base shape callers
  spread pagination fields onto).
- Type mockApiClient as ReturnType<typeof createMockApiClient>.

Generated with Claude Code

Co-Authored-By: Claude <[email protected]>

* chore(functions): wire CI secrets and address convention feedback [PLT-106037]

CI:
- Add UIPATH_FUNCTIONS_TEST_FOLDER_ID(_DEV) and
  UIPATH_FUNCTIONS_TEST_FUNCTION_NAME(_DEV) to the Set/Create Integration
  Test Configuration steps so tests/.env.integration exposes
  FUNCTIONS_TEST_FOLDER_ID / FUNCTIONS_TEST_FUNCTION_NAME. Without this,
  the functions integration test's beforeAll throws in CI. The
  corresponding secrets and a deployed coded function must be provisioned
  separately.

Convention:
- Drop functions.types / functions.models from the orchestrator
  top-level barrel. New services must be modularized only —
  @uipath/uipath-typescript/functions remains the sole public surface.
- Replace \`as any\` on the PaginationHelpers.getAll return with the
  typed \`as Promise<...>\` pattern used by agents.ts.

Generated with Claude Code

Co-Authored-By: Claude <[email protected]>

* fix(api-client): preserve caller-set Content-Type on bodyless requests [PLT-106037]

Previously the default json Content-Type was always added by
getDefaultHeaders() and then unconditionally deleted for GET / HEAD /
FormData. The delete also stripped any Content-Type coming from
clientConfig.headers, so a caller that configured a custom Content-Type
lost it on any bodyless call. Only per-request options.headers survived
because they were spread after the delete.

Flip the logic: only inject the default Content-Type when the request
has a JSON body. No delete anywhere. clientConfig.headers and
options.headers overrides now both survive regardless of the request
method.

Add ApiClient unit tests covering the new precedence:
- GET omits Content-Type by default
- POST with a JSON body sends application/json
- POST with a FormData body omits Content-Type (browser sets boundary)
- clientConfig.headers['Content-Type'] survives on GET
- options.headers['Content-Type'] overrides the default on GET
- clientConfig.headers['Content-Type'] overrides the default on POST

Generated with Claude Code

Co-Authored-By: Claude <[email protected]>

---------

Co-authored-by: Claude Fable 5 <[email protected]>
#608)

The SDK appended acr_values (tenant:/tenantName:) to the authorize URL,
which routes auth directly to the org's SAML IdP. That blocked Basic Auth
users in mixed-auth orgs ("account not found"). setMultiLogin() existed only
to skip acr_values as a workaround.

Alpha testing confirms Identity now resolves the target org from client_id
alone, so acr_values is never needed and all auth types work without it.
Make no-acr the permanent default and remove the now-redundant setMultiLogin()
(it was @internal and became a no-op).

BREAKING CHANGE: removes the @internal setMultiLogin() from UiPath and IUiPath.
No-acr behavior is now automatic, so callers should delete setMultiLogin() calls.

Generated with Claude Code

Co-authored-by: Claude <[email protected]>
…08-03) (#634)

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Adds a github_packages_only boolean input to the Publish SDK package
workflow. When checked, the npm publish step is skipped in all three
publish jobs (ts-sdk, coded-action-app-sdk, telemetry), and the
deploy-docs and trigger-cf-worker jobs are skipped, so prerelease
builds can be published to GitHub Packages without touching the
public npm registry.

Co-authored-by: Claude Fable 5 <[email protected]>
…version (#638)

* ci: tag GitHub Packages-only publishes as dev and require prerelease version

Addresses review comments from #628 that were missed before merge:
- publish with --tag dev when github_packages_only is true so prerelease
  builds don't take over the latest tag in GitHub Packages
- fail the workflow when github_packages_only is true but package.json
  does not contain a prerelease version

Co-Authored-By: Claude Fable 5 <[email protected]>

* ci: validate version in both publish modes and pass inputs via env

Review follow-ups: block publishing a prerelease version to public npm
when github_packages_only is false (mirror of the original guard), and
pass workflow inputs to run scripts via env vars per Actions hardening
guidance, matching the existing Generate Summary pattern.

Co-Authored-By: Claude Fable 5 <[email protected]>

* ci: scope version guard to github_packages_only mode only

Drop the inverse guard (blocking prerelease versions from npm publishes)
to keep this PR limited to the review comments from #628.

Co-Authored-By: Claude Fable 5 <[email protected]>

---------

Co-authored-by: Claude Fable 5 <[email protected]>
…640)

The production environment requires reviewer approval on every run,
which blocks github_packages_only prerelease publishes behind an admin
gate even though they never touch the public npm registry. Route runs
with github_packages_only=true to a separate github-packages-dev
environment (no required reviewers) so prereleases publish without
approval, while official npm releases keep the production gate.

Co-authored-by: Claude Fable 5 <[email protected]>
* docs(samples): add preview GIFs for the five coded action apps

Converted from each app's README demo video (1600px wide, 15fps,
256-color palette, static loading stretches collapsed) and wired into
the Template Gallery, which previously showed generated posters for
these apps.

Co-Authored-By: Claude Fable 5 <[email protected]>

* docs(samples): add Preview sections to action app READMEs

Satisfies the check-samples readme-preview rule: each README now embeds
its screenshots/preview.gif under a Preview heading, in addition to the
original demo video at the end.

Co-Authored-By: Claude Fable 5 <[email protected]>

* docs(samples): drop demo video links superseded by preview GIFs

Co-Authored-By: Claude Fable 5 <[email protected]>

---------

Co-authored-by: Claude Fable 5 <[email protected]>
…#630)

* docs(samples): refresh data-fabric-app preview GIF in HD

Co-Authored-By: Claude Fable 5 <[email protected]>

* docs(samples): refresh data-fabric-app-angular preview GIF in HD

Co-Authored-By: Claude Fable 5 <[email protected]>

* docs(samples): refresh process-app-v1 preview GIF in HD

Co-Authored-By: Claude Fable 5 <[email protected]>

* docs(samples): refresh process-app-v0 preview GIF in HD

Co-Authored-By: Claude Fable 5 <[email protected]>

* fix(samples): add missing Traces.Api scope and gitignore uipath.json

- process-app-v0/v1 call getExecutionHistory(), which requires the
  Traces.Api scope per docs/oauth-scopes.md — without it the execution
  history call fails with 401. Add it to uipath.json.example and READMEs.
- conversational-agent-app, process-app-v0 and process-app-v1 did not
  gitignore uipath.json, risking accidental commits of real org config.

Co-Authored-By: Claude Fable 5 <[email protected]>

* docs(samples): refresh document-validation-app preview GIF in HD

Co-Authored-By: Claude Fable 5 <[email protected]>

* docs(samples): refresh conversational-agent-app preview GIF in HD

Co-Authored-By: Claude Fable 5 <[email protected]>

* revert(samples): allow committing uipath.json in samples

Client IDs for non-confidential PKCE apps are not secrets; keeping
uipath.json pushable per team decision.

Co-Authored-By: Claude Fable 5 <[email protected]>

---------

Co-authored-by: Claude Fable 5 <[email protected]>
…OL-3062] (#612)

The DataFabricRoleService and DataFabricDirectoryService classes were
exported from the /entities subpath, but their option/response types and
enums (e.g. DataFabricDirectoryEntityTypeInput) were not re-exported,
forcing consumers like the CLI to derive them via Parameters<> from
method signatures. Re-export roles/directory types and ServiceModel
interfaces through the entities subpath and main barrels. All symbols
keep their @internal JSDoc tags, so the generated docs are unchanged.

Follow-up to review comments on UiPath/cli#2891.

Co-authored-by: Claude Fable 5 <[email protected]>
…k data/tags/metadata (#632)

Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
…652)

The generic SaveTaskData endpoint rejects Form and App tasks (backend
AllowedTaskTypes). Route saveData by task type: Form and App go to their
own save endpoints, everything else keeps the generic one. Bound
task.saveData() auto-injects the task's own type. Follow-up to #632.

Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
* chore: change to centralized managed GitHub pool

11 workflow file(s) modified, 31 action(s) pinned
Runners migrated: ubuntu-latest ubuntu-24.04 ubuntu-22.04 ubuntu-24.04-arm ubuntu-22.04-arm
ubuntu-slim ubuntu-18.04 ubuntu-20.04 windows-latest

* fix(ci): raise Node heap for builds on managed runners

The uipath-ubuntu-* runners have less RAM than ubuntu-latest, so Node
auto-sizes its heap to ~2 GB and the rollup build aborts with an
out-of-memory error (exit 134).

Co-Authored-By: Claude Opus 5 <[email protected]>

---------

Co-authored-by: Claude Opus 5 <[email protected]>
* feat: make client side tools public and available with examples

* chore: remove internal flag from executingToolCall event
#629)

* feat(identity): add Identity Settings service (GET + PUT api/Setting)

Adds a new modular `IdentitySettings` service exposing the two Identity
Setting endpoints available to external apps:

- `getAll()` — GET api/Setting, bulk read of the organization's settings
  (scope `PM.Setting` or `PM.Setting.Read`)
- `updateSettings()` — PUT api/Setting, bulk create/update of settings
  (scope `PM.Setting` or `PM.Setting.Write`)

Both accept an optional `partitionGlobalId`; when omitted the API falls back
to the partition the calling token is scoped to.

The Identity API routes at the organization level (no tenant segment), so
`IDENTITY_API_BASE` uses the documented `../` URL-normalization pattern to
collapse the tenant segment ApiClient inserts — mirroring NOTIFICATION_BASE.

Exposed via the `@uipath/uipath-typescript/identity-settings` subpath, with
unit tests, integration tests, JSDoc on the ServiceModel, and OAuth scope +
mkdocs nav entries.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013Q3NWsanguD7ZWnYjFSEf6

* refactor(identity): rename module to Identity and align with real API shape

Renames the service from `IdentitySettings` to `Identity`, exposed via the
`@uipath/uipath-typescript/identity` subpath:

- `IdentitySettingService` -> `IdentityService`, exported as `Identity`
- files moved to identity.ts / identity.types.ts / identity.models.ts,
  mirroring the single-service layout used by Governance
- `getAll()` -> `getSettings()`, so the method still names what it returns now
  that the service is named after the domain rather than the resource

Also corrects the request and response shapes against a captured live response:

- adds the `key` query param (repeated per key) and makes `keys` a required
  argument — the endpoint is a bulk get-by-key
- adds the `userId` scope param; settings are per-user within a partition
- `IdentitySetting` now carries the fields the API actually returns: `id`,
  `key`, `value`, `partitionGlobalId`, `userId`
- `value` is always a string (JSON-valued settings arrive as a JSON string);
  keys with nothing stored are omitted from the response rather than returned
  with an empty value
- splits the write shape into `IdentitySettingUpsert` (`key` + `value` only)

The integration suite now snapshots a configured existing key, overwrites it,
and restores the snapshot, replacing the previous throwaway-key approach.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013Q3NWsanguD7ZWnYjFSEf6

* fix(identity): correct updateSettings request and response shape

Validated against the first-party Identity settings client. Two real defects
in the write path:

- the PUT body is an object, not a bare array: `{ settings, partitionGlobalId,
  userId? }`. Scope travels in the body on writes, in the query string on reads.
- the PUT responds with the stored rows, so `updateSettings` now returns
  `IdentitySetting[]` (including each generated `id`) instead of an
  `OperationResponse` echoing the submitted payload.

`partitionGlobalId` is now a required argument on `updateSettings` — the write
payload types it as required, and unlike a read the partition cannot be
inferred. Callers that lack it can read any setting first; every
`IdentitySetting` carries its own `partitionGlobalId`.

The read path needed no change: `IdentitySetting` already matched the client's
`ISetting` field-for-field.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013Q3NWsanguD7ZWnYjFSEf6

* feat(identity): restrict setting keys to the IdentitySettingKey enum

Adds `IdentitySettingKey` and types both operations against it, so only the
supported keys can be read or written:

  UserLanguage, UserLanguageDate, UserTheme, UserAccessibility, UserAlert,
  UserCaseAppOrder, UserCasePinnedInstancesByTenant,
  UserCaseInstancesTableFiltersByTenant

`getSettings(keys)` and `IdentitySettingUpsert.key` now take the enum rather
than a raw string, and `IdentitySetting.key` is typed as the enum so callers
can compare response keys without casting. A unit test pins the members and
their wire values, since adding or renaming one is a public API change.

The integration suite no longer needs a configured key: it reads every
supported key and round-trips whichever one has a stored value, so the
IDENTITY_TEST_SETTING_KEY env var is removed again.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013Q3NWsanguD7ZWnYjFSEf6

* feat(identity): make all settings operations user-scoped

The settings store distinguishes organization-scoped from user-scoped rows by
whether `userId` is present in the request — omitting it targets the
partition-wide row rather than falling back to the calling user. The SDK now
only exposes the user-scoped path:

- `userId` is a required argument on both operations and is always sent
- on writes it goes in the body alongside `settings` and `partitionGlobalId`
- on reads it goes in the query string
- both throw ValidationError when it is missing, so no call can silently
  become organization-scoped

Signatures are now `getSettings(keys, userId, options?)` and
`updateSettings(settings, userId, partitionGlobalId)`, dropping the options
bag from the write path since `userId` was its only member.

This also settles the response typing: `IdentitySetting.userId` stays a
non-null string because only user-scoped rows are ever read.

Corrects JSDoc that described omitted `userId` as defaulting to the calling
user, and a stale module comment calling the settings organization-level.

Integration tests take the user GUID from IDENTITY_TEST_USER_ID; the SDK
cannot derive it from a PAT.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013Q3NWsanguD7ZWnYjFSEf6

* refactor(identity): expose the organization GUID as organizationId

The Identity API calls the organization a "partition", but every other service
in this package names that GUID `organizationId` (see the traces types, where
it is documented as the organization/account GUID). `partitionGlobalId` was
leaking an Identity-internal abstraction into the public surface.

The wire contract is unchanged — `partitionGlobalId` is still what goes over
HTTP — but it is now confined to the request boundary:

- adds `IdentitySettingMap` and applies `transformData` to both read and write
  responses, renaming the field on the way out
- adds `RawIdentitySetting` in identity.internal-types.ts for the wire shape,
  not re-exported from the barrel
- `IdentitySetting.organizationId` replaces `partitionGlobalId`, and the
  option/argument are renamed to match
- mock factories now return the raw wire shape, with tests asserting the
  renamed field is present and the wire field is gone on both operations

`IdentitySettingMap` is deliberately not exported from the models barrel,
matching AssetMap/QueueMap which are not part of the public API either.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013Q3NWsanguD7ZWnYjFSEf6

* test(identity): close coverage gaps and import via the subpath barrel

Line coverage was already 100%, but several API behaviours were unexercised:

- the unit suite imported the service from its deep path, so the subpath
  barrel consumers actually use was never loaded. It now imports `Identity`
  and `IdentitySettingKey` from `src/services/identity`, with an assertion
  that both survive as runtime values — the failure mode an `export type *`
  regression would cause.
- adds a read that passes all eight enum members in one request, so every
  key's plumbing is covered rather than the three that happened to be used
- adds empty-response cases for both operations
- adds a multi-setting write asserting each submitted row comes back, and the
  same bulk path to the integration suite, writing existing values back so the
  call is a no-op against the shared environment
- fixes a misleading assertion in the bulk write test, where the mocked
  response carried fewer rows than the batch and the length was hardcoded to
  the mock factory's size rather than the batch's

Also confirms the remaining field names already match the rest of the package:
`userId: string` as in the notification and conversational-agent user types,
and `id: number` as in jobs/buckets/queues/assets.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013Q3NWsanguD7ZWnYjFSEf6

* ci(identity): wire IDENTITY_TEST_USER_ID and drop an unused test constant

The Identity integration suite throws when IDENTITY_TEST_USER_ID is absent, so
the coverage job needs the variable plumbed through like every other
integration secret. Adds it to the config step and the generated
tests/.env.integration, reading UIPATH_IDENTITY_TEST_USER_ID_DEV with a
fallback to UIPATH_IDENTITY_TEST_USER_ID.

The secret itself still has to be created for the job to go green — until then
the suite reports honestly that it is not being exercised rather than passing
without running.

Also removes ERROR_SETTING_NOT_FOUND, which was defined but never referenced
(flagged in review).

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013Q3NWsanguD7ZWnYjFSEf6

* test(identity): default the integration user GUID instead of requiring config

The user GUID is an identifier, not a credential, so the Identity suite now
falls back to the dev test user rather than throwing when
IDENTITY_TEST_USER_ID is unset. The env var still takes precedence, since
settings are scoped to (organization, user) and a GUID from one organization
returns no rows in another.

The "nothing stored" guard now names the user and points at the likely cause,
so a wrong-organization run reports that rather than looking like a user who
simply has no settings.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013Q3NWsanguD7ZWnYjFSEf6

* test(identity): skip the integration suite until the CI app is authorized

The coverage job's only failure is the Identity suite: the first getSettings
call returns 403 Forbidden, so the request is rejected before it reaches the
resource. No user GUID or configuration value changes that — the CI external
application is not authorized for identity settings.

Skips the suite with the reason recorded, matching the existing precedent for
the Data Fabric access tests, which are skipped until the CI app has the
required scopes. The body is left intact so it runs unchanged once the
`PM.Setting` scope is granted.

The Data Fabric importRecordsById timeout in the previous run was flaky and
passed this time, so this was the last thing keeping the job red.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013Q3NWsanguD7ZWnYjFSEf6

* refactor(identity): singularise the options type name

Renames IdentitySettingsGetOptions to IdentitySettingGetOptions. Every options
type in the package uses the singular entity name (AssetGetByIdOptions,
AgentGetSummaryOptions, AgentMemoryGetTimelineOptions), and every other type in
this module already does too — IdentitySetting, IdentitySettingUpsert,
IdentitySettingKey, IdentitySettingMap, RawIdentitySetting. This was the one
outlier.

Raised in review.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013Q3NWsanguD7ZWnYjFSEf6

* Revert "test(identity): skip the integration suite until the CI app is authorized"

The skip's stated reason was wrong. Three consecutive coverage runs each
403'd in a different, unrelated suite:

  05:08  Identity     getSettings
  05:31  Data Fabric  EntityService.getAll
  05:58  Agent Feedback  deleteCategory, submit

Data Fabric passed in the third run. A 403 that roams between services on
every run is environment-level authorization trouble, not a missing
`PM.Setting` scope on the Identity endpoint — so Identity is in the same
position as every other suite, none of which are skipped.

Restores the suite with its beforeAll guard, which is what review asked for.
The guard deliberately does not wrap the call in try/catch, since rules.md
requires integration test API calls to let errors propagate.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013Q3NWsanguD7ZWnYjFSEf6

* docs(identity): inline the @returns type links

main added a convention while this branch was open: a `{@link}` on a standalone
line after `@returns` renders as stray text in TypeDoc rather than a link, so it
belongs inline in the sentence. Both Identity methods had the standalone form.

Also drops an orphaned comment fragment left in test-config.ts by the rebase
conflict resolution.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013Q3NWsanguD7ZWnYjFSEf6

* test(identity): require IDENTITY_TEST_USER_ID instead of a hardcoded fallback

The secret already exists in the repository, so the fallback GUID was both
unnecessary and actively harmful: it pointed at a user in a different
organization than the CI PAT authenticates against, and identity settings are
scoped to (organization, user).

That may well be the cause of the 403 this suite has been reporting — a userId
outside the caller's organization is plausibly rejected rather than returned
empty, which is the assumption I had been reasoning from.

The suite now throws when the variable is absent, so a misconfigured
environment says so instead of silently querying a foreign user.

Raised in review by @Sarath1018.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013Q3NWsanguD7ZWnYjFSEf6

* docs(identity): add the missing import to the bulk-update example

The second `updateSettings` @example referenced IdentitySettingKey without
opening with its import, so it was not copy-pasteable on its own. The other
three example blocks already carried it.

Raised in review.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013Q3NWsanguD7ZWnYjFSEf6

* test(identity): send the organization GUID on reads

Reads were relying on the API to infer the organization, which it does not do:
omitting the parameter falls back to the *host* partition, not the caller's own
organization, and an external application is not authorized for that — hence
the 403 this suite has been reporting. Confirmed by a reviewer who ran the suite
locally with both values supplied and had all 8 tests pass.

- adds `UIPATH_ORGANIZATION_ID` to the integration config and plumbs it through
  coverage.yml from the existing secret, alongside IDENTITY_TEST_USER_ID
- every read in the suite now names the organization explicitly, and beforeAll
  requires both values
- corrects the JSDoc, which claimed the fallback was the caller's organization.
  That would have walked SDK consumers into the same 403, so `getSettings`
  examples now pass `organizationId` too

This also retracts the scope request in my earlier comments: `PM.Setting` was
not the problem.

Raised in review by @Sarath1018.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013Q3NWsanguD7ZWnYjFSEf6

* refactor(platform)!: rename the Identity settings service to Platform

"Identity" is the internal service name, which the SDK conventions rule out
for developer-facing paths and classes. Platform Management expects further
surface (organization settings, licensing, tenants) to land in the same
namespace, so the module is named for it.

Renames across the public surface:

- subpath `./identity` -> `./platform` (package.json, rollup.config.js)
- `IdentityService as Identity` -> `PlatformService as Platform`
- `IdentityServiceModel`, `IdentitySetting`, `IdentitySettingKey`,
  `IdentitySettingUpsert`, `IdentitySettingGetOptions`,
  `IdentitySettingMap`, `RawIdentitySetting` -> `Platform*`
- `@track('Identity.*')` -> `@track('Platform.*')`
- `src/{models,services}/identity/` -> `.../platform/`, files renamed to match
- `docs/oauth-scopes.md` section and `mkdocs.yml` nav entry

`PLATFORM_SETTING_ENDPOINTS` moves to a new endpoints/platform.ts, leaving
endpoints/identity.ts to the OAuth `connect/*` endpoints it already held.
`IDENTITY_BASE` / `IDENTITY_API_BASE` and the `identity_/api/Setting` URL are
unchanged: they name the wire reality, not the SDK surface.

Test plumbing keeps the `IDENTITY_TEST_USER_ID` env var and its
`identityTestUserId` config field, which track the already-provisioned
`UIPATH_IDENTITY_TEST_USER_ID` repository secret and are not developer-facing.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013Q3NWsanguD7ZWnYjFSEf6

* refactor(platform)!: name the settings methods getUserSettings/updateUserSettings

Both operations are user-scoped by construction: userId is a required
argument and always sent, so neither call can act on an organization-wide
row. Under the Platform namespace, where organization settings, licensing
and tenants are expected to land, getSettings/updateSettings would not say
which scope they act on.

Renames the methods, their @track names (Platform.GetUserSettings,
Platform.UpdateUserSettings), the ValidationError messages, and the
oauth-scopes table. The Conversational Agent user service keeps its own
getSettings/updateSettings — different service, unchanged.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013Q3NWsanguD7ZWnYjFSEf6

* refactor(platform): transform setting rows with transformData directly

transformData already accepts T | T[] and maps element-wise, so the
per-item toPlatformSetting helper was redundant. Calling it on the array
matches the existing idiom (see FeedbackService.getById).

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013Q3NWsanguD7ZWnYjFSEf6

* fix(platform): harden setting argument validation and fix the module example

- Guard keys/settings with `!x?.length` so untyped JS callers passing
  null/undefined get a ValidationError instead of a TypeError.
- Reject an explicitly empty organizationId on reads instead of dropping
  it. Dropping it made the request target the host partition, so the
  caller got an opaque 403 rather than being told what was wrong.
  Omitting the option entirely is still allowed.
- The module @example called getUserSettings without organizationId,
  which would 403 for the same reason — it now passes one.

Adds 6 unit tests; each was confirmed to fail with its guard reverted.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013Q3NWsanguD7ZWnYjFSEf6

---------

Co-authored-by: Claude <[email protected]>
The release-metadata-gen workflow runs `npm run build` on the managed
uipath-ubuntu-* runners introduced in #648, but was missed when that PR
raised the Node heap on the other build workflows (coverage.yml,
publish.yml). Node auto-sizes its heap to ~2 GB there, so the rollup
build aborts with an out-of-memory error (exit 134) and the metadata is
never regenerated/committed onto version-bump PRs. Add the same
NODE_OPTIONS=--max-old-space-size=6144 the sibling workflows already use.

Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
Adds attachments.create() to upload a file, plus jobs.getAttachments()
and jobs.linkAttachment() to work with a job's attachments.

Refs APPS-35700

Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
* chore: bump version to 1.6.1

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

* chore(release-metadata): regenerate for 1.6.1

* fix(release-metadata): correct CaseInstances.getVariables since to 1.6.0

It shipped in 1.6.0 (#626) but was never recorded in the 1.6.0 metadata —
that bump regenerated against an earlier (1.5.6) branch state before #626
landed, then only the sdkVersion string was updated. The current regen
therefore stamped it 1.6.1; correct it to 1.6.0. The generator carries
this value forward on future regens.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

* chore(release-metadata): regenerate for 1.6.1

---------

Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* docs(functions): mark the coded Functions service as preview

Marks the entire Functions (coded functions) surface as experimental,
following the governance/agent-memory pattern (@experimental + a Preview
warning admonition). Applied to the service-level JSDoc, both
FunctionServiceModel methods (getAll, invoke), and the bound
FunctionMethods.invoke.

Functions is modular-only (no UiPath-class wrapper, not a TypeDoc entry
point), so the service class needs no tag — same as GovernanceService.
@experimental does not affect release-metadata (only @internal is
excluded), so coded functions still ships as a public, documented API.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

* fix(functions): export function models from the orchestrator barrel

The orchestrator models barrel omitted functions, so FunctionServiceModel
never reached TypeDoc's entry point (src/index.ts) and its docs page was
never generated — the mkdocs nav link (added in #614) 404'd. Re-export
functions.types and functions.models like the sibling orchestrator
services, so the Functions API reference page renders.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

* docs(functions): mark the FunctionMethods interface as preview

FunctionMethods renders as its own TypeDoc page; without @experimental on
the interface declaration the page showed no preview warning even though
its method was tagged. Tag the interface declaration to match
FunctionServiceModel (both interface and method levels). Addresses review
comment on #659.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
#647)

* feat(data-fabric): add havingFilter to entity record queries

Post-aggregation filter (SQL HAVING) on grouped aggregate results: conditions
reference declared aggregate aliases (aggregates-only server contract,
DS-9077/DS-9078). Requires aggregates + groupBy - enforced client-side with an
actionable ValidationError. havingFilter is registered in excludeFromPrefix so
the key reaches the wire un-prefixed; without this the pagination helper would
send it as $havingFilter and the server would never see it.

Server-side: native (LDO) entities only, gated by the enable-having-on-query
feature flag (400 otherwise).

Co-Authored-By: Claude Fable 5 <[email protected]>

* refactor(data-fabric): use an enum for having operators, document havingFilter

Review follow-ups: enum matching the QueryFilterOperator convention (drop the
redundant <> - the server normalizes !=), tests on enum members, and
havingFilter added to both query methods @PARAM lists with HAVING examples.

Co-Authored-By: Claude Fable 5 <[email protected]>

* test(data-fabric): add havingFilter integration coverage

Data-independent HAVING assertions on the fixture entity: a satisfiable
threshold (cnt >= 1) must return every group and an unsatisfiable one must
return none - a backend that ignores havingFilter returns every group there,
which is the failing signal. Requires the enable-having-on-query tenant flag;
without it the server 400s naming the flag, failing loud as requested.

Co-Authored-By: Claude Fable 5 <[email protected]>

* test(data-fabric): decouple having test from join fixture, jsdoc nits

Group by Id (always present) instead of the join fixture field, drop the
unexplained LDO acronym from the JSDoc, and use unknown over any in the
integration assertion.

Co-Authored-By: Claude Fable 5 <[email protected]>

* test(data-fabric): use toHaveLength for the empty-groups assertion

Sonar S5906: toHaveLength reports the actual array contents on failure.

Co-Authored-By: Claude Fable 5 <[email protected]>

---------

Co-authored-by: Claude Fable 5 <[email protected]>
* feat: cas add personal connections support

* test: add tests for cas connections feat

* feat: update per conventions

* refactor: update expiresAt prop

* test: update test for transform

* test: remove unused import

* docs: remove jsdoc

* docs: update jsdoc for model returns

* chore: feedback from comments

* docs: update scope reqs

* test: add integration tests

* chore: update config for cas integration
* fix(telemetry): declare the forceFlush LogRecordExporter requires

`ApplicationInsightsEventExporter implements LogRecordExporter` but never
declared `forceFlush`, which that interface requires at the pinned
`@opentelemetry/sdk-logs`. `npm run typecheck` fails on main today with TS2420
and TS2345 because of it.

Implemented as a no-op, matching the existing `shutdown`: records are posted
fire-and-forget in `sendAsCustomEvent`, so there is no queue of in-flight
requests here to await. Making it flush for real would mean tracking those
promises, which is a behaviour change and not this commit's business.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HwgxkTUnqbWRN3xUwr6B46

* feat(telemetry): let a consumer supply its own connection string

`TelemetryClientInitOptions` had no way to name an Application Insights
resource, so every consumer was pinned to the one `sed`-patched into this
package at publish time. That is a gap in a package whose design is "each
consumer builds its own client": a consumer could set its own `cloudRoleName`,
`serviceName` and context, but not its own destination. The Coded Action App
SDK telemetry doc already described this as supported — "custom connection
string: passed to provider constructor" — and it is true internally, since
`setupTelemetryProvider` takes it as a parameter. It was simply never exposed
on the public `initialize()`.

Add an optional `connectionString`, defaulting to the packaged value, so the
SDK and coded-action-app are unaffected. An empty string disables telemetry
rather than falling back, so a consumer whose own value is unset emits nothing
instead of silently reporting to the shared resource.

Requested and approved in #feat-codedapps.

No version bump — that is a separate PR per the release workflow.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HwgxkTUnqbWRN3xUwr6B46

* test(telemetry): assert getLogger too in the placeholder no-op test

Matches the sibling no-op tests, which all assert both. Logically redundant —
`getLogger` lives on the provider — but the inconsistency was the point.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HwgxkTUnqbWRN3xUwr6B46

---------

Co-authored-by: Claude <[email protected]>
Bumps the pip group with 1 update in the /docs directory: [pymdown-extensions](https://github.com/facelessuser/pymdown-extensions).


Updates `pymdown-extensions` from 10.21.3 to 11.0.1
- [Release notes](https://github.com/facelessuser/pymdown-extensions/releases)
- [Commits](facelessuser/pymdown-extensions@10.21.3...11.0.1)

---
updated-dependencies:
- dependency-name: pymdown-extensions
  dependency-version: 11.0.1
  dependency-type: direct:production
  dependency-group: pip
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
#650 bumped pymdown-extensions to 11.0.1, but mkdocs-material 9.6.18
declares pymdown-extensions~=10.2 (>=10.2,<11), so pip cannot satisfy
both and every docs build dies at install with ResolutionImpossible.

9.7.0 is the first mkdocs-material release to relax that to >=10.2.
Bumping to 9.7.7 keeps the 11.0.1 upgrade instead of reverting it.

Co-authored-by: Claude Opus 5 <[email protected]>
…ers, updateTopicGroups) (#518)

Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
* docs: update claude docs from PR review analysis (2026-08-03 to 2026-08-10)

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* fix: address convention review comments

* docs: trim and compact PR-review convention insights

- Drop the endpoint-specific RetentionAction sentinel war-story from the
  read-modify-write rule, keeping only its generalizable kernel
- Lead the nested-payload rule with the open-map type discriminator so an
  agent can actually detect "user-defined"
- Remove restatements and info already covered elsewhere (pipeline step 1,
  transform-completeness bullet, DF exception)

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

* docs: drop clauses already covered elsewhere in the conventions

- Remove the untracked-helper/double-telemetry note from read-modify-write
  (already stated as the @track delegation anti-pattern)
- Cross-reference the read-path header-destructuring rule from write-body
  hygiene instead of re-explaining the leak

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

* docs: apply write-body hygiene in the read-modify-write example

Strip query/header-only fields before merging into the PUT body so the
example doesn't contradict the Write request body hygiene section.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

* docs: update claude docs from PR review analysis (2026-08-10 to 2026-08-17)

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

---------

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <[email protected]>
Co-authored-by: shreyash0502 <[email protected]>
shivendra6720 and others added 26 commits September 28, 2026 12:13
* fix(auth): refresh access tokens ahead of expiry [APPS-37340]

External apps can now be configured with a shortened access token
lifetime (300-3600s). At 300s the SDK refreshes 12x more often than at
the 3600s default, so a request signed in a token's final moments can be
rejected server-side once network latency and clock skew are added — and
the resulting 401 is not retried.

Treat a token as expired 60s before its actual expiry so it is renewed
before it can expire in flight. The buffer is applied only where a
refresh is actually possible; validity checks and stored-token loading
keep using the true expiry, so a page reload late in a token's life
still restores the session instead of forcing a re-login.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>

* refactor(auth): extract getExpiryMs so the expiry parse is explicit

Both expiry checks built a Date unconditionally, which cloned an
already-correct Date on every request and left the string handling
implicit. Move it behind a named helper that branches on the type, so
the accepted shapes are stated in one signature rather than implied by
a constructor call in two places.

Behaviour is unchanged: a Date returns its own timestamp, an ISO string
parses to the same value, and an unparseable value still yields NaN and
so leaves the token treated as not expired.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>

* fix(auth): apply the expiry buffer on the OAuth path only [APPS-37340]

Action Center and embedded hosts return their cached token until it
actually expires, so a buffer on those paths cannot renew anything
early. Restore those files to main and keep the buffer in TokenManager.

getExpiryMs now returns the epoch for an unreadable expiry, so the token
reads as expired instead of never expiring. Docs trimmed to the renewal
behaviour only.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

---------

Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
* feat(du-validation): add validation start and result poll service

* feat(du-validation): add tests and module wiring

* feat(du-validation): wire document-understanding-validation entry

* feat(du-validation): wire document-understanding-validation build entry

* test(du-validation): address review JSDoc, types, and integration coverage

* fix(du-validation): transform PascalCase API responses to camelCase

* fix(du-validation): keep oauth and endpoint exports off main conflict hunks

* fix(du-validation): rename operation timestamps to *Time

Map createdAt and lastUpdatedAt to createdTime and lastModifiedTime after
PascalCase conversion, and drop the double cast in the unit test.

Co-authored-by: Bogdan Nikolov <[email protected]>

* fix(du-validation): shallow-case only validation envelopes

Recursive case conversion rewrote framework leaves inside result, so
DocumentId was undefined at runtime while the type still required it.
Convert the operation, error, and result envelopes and leave generated
payloads unchanged.

* refactor(du): apply Naren review on scopes and DocumentUnderstanding

Rename DuValidationService to DocumentUnderstanding and drop the DuValidation public alias.
Use a Document Understanding scopes heading and point the API reference at the overview.

* refactor(du): rename service class to DocumentUnderstanding

Drop the DuValidation and DuValidationService public names so later
methods can live on the same class.

* docs(oauth): use Document Understanding heading

Point the framework API reference at the Document Understanding API
overview so later methods can share this section.

* fix(du-validation): use DocumentUnderstanding prefix for start telemetry

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>

* fix(du-validation): use DocumentUnderstanding prefix for result telemetry

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>

* fix(du-validation): cover all validation guards and add docs nav entry

- Unit tests for missing tag/documentTypeId (start) and projectId/tag/documentTypeId (result)
- Export validation types/models from the root barrel so TypeDoc renders DuValidationServiceModel
- Add Document Understanding entry to mkdocs.yml nav

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Naren <[email protected]>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
* feat(agenthub): add chat completions via LLM gateway

* feat(agenthub): wire agenthub endpoints, headers, and models

* feat(agenthub): wire agenthub entry

* feat(agenthub): wire agenthub build entry

* fix(agenthub): restore trailing newlines in src/index.ts

* fix(agenthub): apply review convention fixes

* test(agenthub): assert ValidationError class and add integration coverage

* fix(agenthub): map snake_case chat completion wire fields

Send maxTokens as max_tokens and map finish_reason to finishReason so
the OpenAI-compatible gateway fields round-trip through the SDK types.

Entire-Checkpoint: 70406c9e3c32

* fix(agenthub): add snake_case key transforms for chat completions

Entire-Checkpoint: 70406c9e3c32

* fix(agenthub): add snakeToCamelCaseKeys and camelToSnakeCaseKeys

Entire-Checkpoint: 70406c9e3c32

* chore(release-metadata): regenerate for 1.7.1

* fix(agenthub): address chat completion review comments

Use enums for message roles and tool types, type the POST response as the
snake_case wire shape, and split the method examples into a minimal call
and one with generation parameters.

Co-authored-by: Bogdan Nikolov <[email protected]>

* fix(agenthub): keep chat completions internal and out of public docs

- Mark createChatCompletion @internal, following the Notification service pattern
- Drop root barrel export, mkdocs nav entry and OAuth scope doc (LLMGateway is an internal scope)
- Revert release-metadata.json changes for already-released 1.7.1/1.7.2

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

* fix(agenthub): type tool calls and usage, take model and messages positionally

- createChatCompletion(model, messages, options?) replaces the request
  object; generation params, tools, toolChoice and signal move into the
  options bag, and the model is sent in the gateway header only
- response now carries object, model, createdTime and usage; choice
  messages expose toolCalls with nullable content, mapped by hand so
  tool-call argument keys are never rewritten
- request messages accept toolCalls and toolCallId; tool results are sent
  in the normalized { result, call_id } content shape
- drop @experimental and the bare AgentHubService export to match the
  Notification internal-service pattern
- integration test covers usage, transform completeness and a forced
  tool-call round trip

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* revert(agenthub): drop tool-call typing and response expansion

Reverting the batch of changes to keep #754 focused; the positional
signature and tag cleanup are re-applied separately in the next commit.

This reverts commit a56a09b.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* fix(agenthub): take model and messages positionally, drop experimental tag

- createChatCompletion(model, messages, options?) replaces the request
  object; maxTokens, temperature and tools join signal in the options bag
- remove the @experimental banners: the method is @internal, so it is not
  part of the documented surface and the preview warning has nothing to
  attach to

Co-Authored-By: Claude Fable 5.1 <[email protected]>

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
Co-authored-by: Sarath1018 <[email protected]>
* docs: update claude docs from PR review analysis (2026-08-31 to 2026-09-07)

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* fix: address convention review comments on claude docs update

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* fix: clarify ref resolution ?? order and regex sharing framing

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* docs: update claude docs from PR review analysis (2026-09-07 to 2026-09-14)

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* docs: update Claude docs from PR review analysis (2026-09-21 to 2026-09-28)

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

---------

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <[email protected]>
…#769)

The four videos are now published on the UiPath Product channel, so the
embeds use those ids instead of the originals.

Co-authored-by: Claude Opus 5 <[email protected]>
… [APPS-37257] (#722)

* fix(core): reject a config carrying both auth methods at construction [APPS-37257]

`#mergeConfigSources` elects an auth method only when the caller names exactly
one of them. When the caller names a secret *and* at least one OAuth field, the
election XOR is false, nothing is dropped, and the merged config carries both
methods — so `isCompleteConfig`'s XOR fails and the constructor silently stores
`#partialConfig` without registering internals. The caller first hears about it
from an unrelated service constructor ("Invalid SDK instance"), or from
`initialize()` reporting a configuration that was never "not found".

The method's own comment claimed the case was "left intact for validateConfig()
to reject". It was not: `validateConfig`'s only call site sits inside
`#initializeWithConfig`, which runs only once `isCompleteConfig` has returned
true — precisely what a both-methods config makes false. The function was
unreachable dead code and is removed.

- Guard the merge, after the existing drop block, so PR #675's cross-source
  resolution is preserved: a pure secret over injected OAuth meta tags still
  works. Only a caller contradicting themselves throws.
- `conflictingAuthMessage` names every auth field and the layer that supplied it
  (constructor argument / `<meta name="uipath:...">` / env var), and states which
  fields to remove. It never interpolates a field value — `secret` is a bearer
  token and this string reaches browser consoles.
- `missingConfigMessage` now describes a config that was found but is short,
  instead of claiming none was found.
- Export `PartialUiPathConfig`, `BaseConfig` and `OAuthFields` from `/core`. The
  constructor's declared parameter type was unreachable from that entry point,
  which is what pushed callers to `as unknown as ConstructorParameters<...>[0]`.
- Drop `scope` from the secret-auth JSDoc example: pasted into a coded app it
  inherits the other two OAuth fields from the meta tags and hits the new guard.

Every input that now throws already failed — later, and somewhere else.

Verified: typecheck, oxlint, 2760/2760 unit tests, build; and end to end against
the built dist with the dev plugin's meta tags faked — the reporter's scenario,
their explicit-undefined workaround, and `new UiPath()` with no config all still
construct and register.

Co-Authored-By: Claude Opus 5 <[email protected]>

* docs(core): tighten the precedence note and correct a test description

Review feedback on #722:

- The precedence admonition in docs/authentication.md said the same thing three
  ways over thirteen lines. Four lines, same facts.
- `falls back to generic guidance when no layer named an OAuth field` was wrong:
  the test's own `metaConfig` names all three OAuth fields. The condition is that
  the *constructor argument* named none — `namedOAuth` filters `sources.config`
  only — so a failure would have read as if no layer had OAuth at all.

Co-Authored-By: Claude Opus 5 <[email protected]>

* refactor(core): hoist the config field lists to module constants

Review feedback on #722: describeGaps built ['clientId', 'redirectUri', 'scope']
inline twice and ['baseUrl', 'orgName', 'tenantName'] once, which
agent_docs/conventions.md forbids — static lookup tables belong at module scope.

BASE_FIELDS and OAUTH_FIELDS now sit beside AUTH_FIELDS, and AUTH_FIELDS derives
from OAUTH_FIELDS so the OAuth triple is declared once. The spread keeps the
literal tuple, so AuthField is still "secret" | "clientId" | "redirectUri" |
"scope" and AUTH_FIELD_SOURCES' Record stays exhaustive.

No behaviour change: every message string is byte-identical, pinned by the
existing tests.

Co-Authored-By: Claude Opus 5 <[email protected]>

---------

Co-authored-by: Claude Opus 5 <[email protected]>
* feat(folders): add getByKey lookup on odata/Folders

* feat(folders): wire folders endpoints and models

* feat(folders): wire folders entry

* feat(folders): wire folders build entry

* docs(folders): align getByKey JSDoc with SDK conventions

* docs(folders): use SDK names in folder JSDoc and tests

* fix(folders): export Folders only from the folders subpath

Drop the legacy dual export of FolderService. Callers import Folders.

* docs(folders): split getByKey JSDoc into bare and select examples

First example is a bare call; the second passes select.

* test(folders): move unit constants to FOLDER_TEST_CONSTANTS

Share folder test data from tests/utils/constants/folders.ts.

Entire-Checkpoint: 6cf46c9463ed

* fix(folders): address getByKey review comments

Remove the internal action name from the class JSDoc, cover empty and
whitespace key validation, and drop isPersonal. GetByKey does not return
that field, so the integration assertion failed.

Co-authored-by: Bogdan Nikolov <[email protected]>

* test(folders): type the unit mock client

Use ReturnType<typeof createMockApiClient> so the mock methods are typed,
and cast the mock once as ApiClient in mockImplementation.

* fix(folders): type isActive/fullyQualifiedNameOrderable and drop unsupported expand

- Add isActive and fullyQualifiedNameOrderable to FolderGetResponse (returned by GetByKey)
- FolderGetByKeyOptions now accepts select only; every expand value returns 400

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

* fix(folders): strip @odata.context and share integration preconditions

- Drop @odata.context from the getByKey response, matching the attachments service
- Move the folders service and folder key checks into a single beforeAll

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
…ts, execution-history fixture) (#745)

* test(maestro): give the Insights SLA tests a 90s budget

getSlaSummary / getStagesSlaSummary hit insightsrtm_, whose SLA aggregations
routinely run 15-30s+ server-side (measured 16s, 23s, >30s, >30s across the
last four coverage runs). They run only under the user token and were failing
on the 30s default while the endpoint was still working. Same treatment as
the Data Fabric schema DDL tests: a budget for a slow-but-succeeding server.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* test(maestro): read execution history from a settled process instance

The suite's shared instance is whatever processInstances.getAll() returns
first. The list is newest-first, so under concurrent CI legs that is
routinely an instance another suite cancelled before it executed a single
element, and its history is empty - the transform test then throws its own
precondition (seen on #736 run 35093134016). Pick a settled instance that
has actually run something instead of depending on list position.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

---------

Co-authored-by: Claude Fable 5.1 <[email protected]>
…quential group (#747)

Entity create/delete in the tenant makes concurrent bulk inserts on other
entities fail server-side with "Insert bulk failed due to a schema change
of the target table". Over the last 30h of CI every one of these 500s
(7 of 88 bulk writes, 8%) landed while our own schema suite was creating
sdk_* entities; bulk writes with no DDL in flight failed 0 of 27 times.

Split the integration config into two vitest projects: everything except
entities-schema in group 0, the schema suite alone in group 1, so the DDL
never overlaps the record-writing suites. One invocation, one coverage
report; file and name filters keep working.

Co-authored-by: Claude Fable 5.1 <[email protected]>
…xed-limit assertions (#768)

* test(data-fabric): share one fixture entity across sqlType default/fixed-limit assertions

Seven read-only tests in the `sqlType constraint defaults` block each created
and deleted their own entity to verify one field's default or fixed lengthLimit.
Consolidated into a single beforeAll fixture entity carrying all seven field
types; each `it` now asserts against the shared entity.

Cuts seven `CreateEntity` + `SoftDeleteEntity` DDL cycles down to one per PR run
per credential cell, easing pressure on the shared alpha entity SQL elastic pool
during CI (currently hitting its max_workers=100 ceiling under integration load).

Custom-constraint tests (user-provided lengthLimit / decimalPrecision) and
mutation tests (update STRING lengthLimit / DECIMAL constraints) keep their own
entities — they mutate schema, so they can't share.

Verified locally: all 12 `sqlType constraint defaults` tests pass; the 8 shared
assertions run sub-1ms each after the beforeAll create.

Co-Authored-By: Claude Opus 4.7 <[email protected]>

* test(data-fabric): drop dual-credential run — DF suites run PAT-only

Every DF integration suite was declared with describeIntegration(..., 'both', ...),
which runs the suite twice per PR — once under PAT, once under the Minter user
token — against the same shared alpha entity SQL elastic pool. DF endpoints
accept PAT fine and the backend has no branching on token type
(verified: no PersonalAccessToken/client_id/token_type conditionals in
WebAppCommon/EntityServiceRuntime middleware), so the second run exercises
no unique code path yet doubles the load on the pool.

Flip access, attachment, choicesets, entities-query, entities-records,
entities-schema to 'pat' — halves DF requests per PR run per the arithmetic
of credential-cell count. Ceiling errors were 186 in one 32-min CI window
on the shared pool (max_workers=100); this is the biggest single lever we
can pull SDK-side without scaling infra.

Revert to 'both' once the pool is either scaled or the CI tenant DB is
isolated from production tenants.

Co-Authored-By: Claude Opus 4.7 <[email protected]>

* test(data-fabric): scope PAT-only narrowing back to entities-schema; drop redundant strDefaultField assertion

Per review feedback:

1. Revert 'both' → 'pat' on attachment, choicesets, entities-query,
   entities-records. Narrowing to PAT for shared-pool relief violated the
   convention that 'pat' is reserved for services specific to the
   external-application identity. Keep 'pat' only on entities-schema, where
   the beforeAll-fixture consolidation is the primary lever this PR delivers.

2. Drop `strDefaultField` from the shared fixture entity. It duplicated
   `strField` (both STRING with no lengthLimit) and the corresponding
   'confirmed via GET' assertion added no independent verification — the
   shared beforeAll already calls getById.

Verified locally: the sqlType constraint defaults block runs 11 tests, all
pass; the 7 shared-fixture assertions run sub-1ms after beforeAll.

Co-Authored-By: Claude Opus 4.7 <[email protected]>

* test(data-fabric): revert PAT-only narrowing on access and entities-schema

Per the same review rule that reverted the other four DF suites: 'pat' is
reserved for services specific to the external-application identity. Neither
access (whose comment describes a scope-related 403, not an identity-model
distinction) nor entities-schema (kept previously on a performance argument)
satisfies that criterion.

Every DF suite now runs 'both' again. The net effective change on this PR
is the beforeAll fixture consolidation in entities-schema — the DDL lever
the reviewer accepted — and the drop of the redundant strDefaultField
assertion.

Co-Authored-By: Claude Opus 4.7 <[email protected]>

---------

Co-authored-by: Claude Opus 4.7 <[email protected]>
* feat(ci): build changed sample apps on pull requests

* fix: build changed sample apps in check-samples

---------

Co-authored-by: swati354 <[email protected]>
* feat(core): add trace() markers and the @trace method decorator

trace(label, values?) records a point in code, and @trace records every call
of a class method. When tracing is available, both are added to the trace;
otherwise they are printed with console.debug.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

* fix(core): address review feedback

A host may record a span asynchronously. trace() and @trace ignored what
recordSpan returned, so a rejected promise escaped the try/catch as an
unhandled rejection and crashed the process. Markers and traced calls now
record through one helper that catches a throw or a rejection and warns
the same way.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
…RL origin [SW-31977] (#782)

A coded function passes its handler context to `new UiPath(ctx)`; four gaps closed for that path:

- `ctx.platform.folderKey` becomes the default folder for calls that need one and name none, the
  way the `uipath:folder-key` meta tag already does for coded apps (the context wins over the tag).
  Orchestrator name lookups and the other calls that threw "requires folder context" run against
  it, and a by-name lookup that finds nothing there names the folder in its NotFoundError.
  Integration Service calls are left out and keep falling back to the meta tag alone: Integration
  Service rejects a connection outside the folder named in X-UIPATH-FolderKey (403 CNS1045 on
  getById, 404 on execute), and a function's connection often lives in another folder.
- The context's `baseUrl` is reduced to its http(s) origin: the organization and tenant are
  appended to it, so a host handing over a longer URL would address a tenant one level too deep.
  A value that is not an http(s) URL is left out of the configuration, like a blank coordinate, so
  the environment can fill it and the error can name it; a context with a token but no platform
  contributes the token alone. Configuration passed directly is not inspected.
- When a handler context was passed and the merged configuration is still incomplete, the error
  opens with "UiPath SDK configuration is incomplete:" and names the missing coordinate
  (`ctx.platform is null`, `ctx.robot.accessToken is null`, ...) and the variable that could fill
  it, instead of advising to pass the context. The reason is recorded in the internals registry
  through a `Symbol.for` global, so a service constructed from another bundle reports it too;
  coded functions never call `initialize()`, so that is the path they see.
- `HttpMethod`, `Headers`, `QueryParams` and `ResponseType` are exported from `/core`, beside the
  `HttpRequestInit` and `RetryOptions` that reference them (already public from the root entry).

Verify: `npm run typecheck`, `npm run lint`, `npm run test:unit` (3164 tests), `npm run build`,
`npm run docs:validate`.

Co-authored-by: Claude Fable 5.1 <[email protected]>
…7918] (#775)

* ci: run only the integration suites a pull request can affect

PR coverage runs used to execute every integration suite for every change,
so a docs- or packages-only PR spent ~17 minutes per leg creating Data
Fabric entities and Maestro instances it could not influence, and every
platform hiccup in ~3,000 requests failed the PR.

A new `scope` job classifies the files changed against the base branch
(scripts/integration-scope.mjs). Suites are the folders under
tests/integration/shared; a change under src/services/<name>,
src/models/<name> or tests/integration/shared/<name> runs that suite, docs,
samples, packages and unit tests run nothing, and anything else (core,
utils, the harness, config, workflows, a domain with no suite folder) runs
everything. The cross-cutting smoke, http and auth-errors suites run
whenever any suite runs. The `integration` job receives the resulting
vitest path filters and is skipped only when the resolver explicitly says
nothing is affected; a missing output still runs. The `ci:full-integration`
label forces a full run; weekly-coverage.yml keeps running everything.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* ci: address review on the integration-test scoping

- Rename the `scope` job to `integration-scope`, matching the script.
- Treat all three always-on entries alike: a change to the smoke test,
  `shared/http/` or `auth-errors` runs only the always-on suites. Other
  loose test files still run everything.
- Make the full-run reason name the missing suite folder for a domain
  that has none, instead of calling the path "outside the per-domain
  folders".
- Move the full-run label check into the script (`--labels`); the
  workflow passes the PR's labels through and `FULL_RUN_LABEL` is the
  only place the name lives.
- Document the scoping in tests/integration/README.md ("Which suites run
  on a pull request") instead of an agent_docs/rules.md bullet; keep only
  the suite-folder naming clause there.
- Unit tests for the always-on paths, both reason messages, a
  version-bump change set and the label path.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* docs: leave agent_docs/rules.md untouched

The integration-test scoping is documented in tests/integration/README.md
only; the suite-folder naming clause goes too.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* ci: run nothing for a service that has no integration suite

A change under src/services/<name> or src/models/<name> where the
service folder exists but tests/integration/shared/<name> does not
(integration-service today) has no suite to run, so it no longer falls
back to the full run. Only real service folders qualify; src/models
folders that are not a service (common, document-understanding) are
shared code and still run everything.

The former safety net for a mis-named suite folder — fall back to the
full run — is replaced by a unit test asserting every suite folder is
named after a src/services folder.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* test: cover the git diff failure path of the scope resolver

A base ref git does not know must fall back to the full run, never throw
or exit. Also covers the --base success path and --files parsing.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

---------

Co-authored-by: Claude Fable 5.1 <[email protected]>
…ateExternalConnection (#783)

Follow-on to #642. Adds federated source and join deltas to EntityUpdateByIdOptions
(all @experimental). They are applied inside the existing full re-upsert
(buildFederatedUpsertParts):

- addExternalSources / removeExternalSources
- addExternalFields / removeExternalFields (fieldNames are the entity's column names)
- addSourceJoins: connect a source added in the same call
- updateSourceJoin: change only the join fields of an existing join
- replaceSourceJoins: set the complete join set. This is how the primary source
  changes, because the backend derives the primary from the joins (the source every
  join starts from). A join can't be removed on its own, so there is no
  removeSourceJoins. It can't be combined with addSourceJoins or updateSourceJoin.
- updateExternalConnection: point a connector source at another connection, keeping
  its object, fields and joins and re-pointing the joins at the new connection id.
  Native sources are rejected.

Every delta that names an existing source identifies it by BOTH sourceObjectName and
sourceConnectionId (a connector connectionId, or the entityId for a native source),
because an entity can have two sources with the same object name on different
connections. Shared FederatedSourceRef type.

The SDK does not repeat validation the backend already does (join graph, duplicate
sources, empty sources, duplicate field names).

Also fixes the join cascade on removeExternalFields: joins refer to external field
names, so the removed columns are mapped to their external names before joins are
dropped.

JSDoc on every option explains what it does and how the deltas fit together. Adds unit
tests for each delta and integration tests (gated on the DF_FED_* env vars) for adding
a native source with its join, changing the primary, cascading a source removal and
swapping a connection.

Also fixes the by-name updateRecord/updateRecords integration tests: they updated
writableFields[0] with generateFieldValue, which has no choiceset case and falls back
to a string; when the shared entity's first writable field resolves to an integer
choiceset the backend rejects it. p_GetFieldsByEntityId has no ORDER BY, so field
order is query-plan-dependent — a plan flip on alpha (Sep 26-28) made the choiceset
field first on every branch. Mirror the choiceset-skip the by-id update tests apply.

Co-authored-by: Claude Opus 4.8 <[email protected]>
…quest CI (#802)

* refactor(endpoints): one folder per service domain, scoped by pull-request CI

The endpoint-constant files did not match the service folders the
integration-test scoping (#775) is keyed on: Action Center's endpoints
lived in orchestrator.ts, platform's in identity.ts and authorization.ts,
agents' in feedback.ts and memory.ts, observability's in traces.ts and
agent-traces.ts. Every endpoint change therefore ran the full suite.

- src/utils/constants/endpoints/<domain>/..., one folder per
  src/services/ domain, files keeping their names. orchestrator.ts is
  split: the task constants move to action-center/tasks.ts. identity.ts
  moves whole into platform/. base.ts stays loose as the shared prefixes.
  The top-level barrel lists the files; no per-folder barrels.
- scripts/integration-scope.mjs: endpoints/<name>/** is scoped like
  src/services/<name>/**; the barrel is ignored; base.ts runs everything.
- Guard tests: every endpoint folder is named after a src/services
  folder; a folder's exports are used only by that domain's code (or
  src/core); loose files are not used by services.
- Accepted: platform/identity.ts is also imported by core's organization
  resolver; a change there runs the platform suite only.
- Docs: tests/integration/README.md, agent_docs/conventions.md,
  agent_docs/architecture.md.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* ci: ignore endpoints/base.ts in the integration-test scoping

Every new service adds a base path there, and its own endpoint folder and
suite already trigger its run; running the whole suite for the shared file
would make every onboarding PR a full run again. Editing an existing base
path is therefore not covered by a pull-request run.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* refactor(endpoints): import endpoint constants through the barrel

The eight deep imports (data-fabric/data-fabric, platform/identity) were
carried over from the flat layout; every other service already imports
from src/utils/constants/endpoints.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

---------

Co-authored-by: Claude Fable 5.1 <[email protected]>
)

* ci: scope new-service pull requests to their own integration suite

A new-service PR still ran every suite because three files outside the
domain folders change on each one:

- package.json (exports, scripts, version) is ignored. A dependency
  change or version bump still runs everything through package-lock.json.
- tests/utils/constants/<name>.ts is scoped like src/services/<name>/
  when <name> is a suite domain; common.ts and entity-named files still
  run everything.
- tests/integration/config/unified-setup.ts, where services are
  registered, runs only the always-on suites when the change only adds
  lines (learned from `git diff --numstat`); any other edit runs
  everything. The always-on suites load the file.

A drift guard fails when a domain-named constants file is used by another
suite. It found the observability agent-traces suite using agent fixtures
from agents.ts; those now live in common.ts as TEST_AGENT, which
AGENT_TEST_CONSTANTS references so the unit tests are unchanged.

Verified against #664 (feat/platform-groups): scope=platform.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* ci: ignore scripts/ in the integration-test scoping

Everything under scripts/ is CI and release tooling (API-surface check,
sample builds, docs, release metadata, the scoping resolver itself);
nothing there takes part in an integration run, and each script has its
own unit tests. A broken resolver fails the fail-closed integration-scope
job rather than skipping anything silently.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* ci: ignore tests/utils as a whole in the integration-test scoping

tests/unit was already ignored; tests/utils holds unit-test fixtures and
helpers. Four fixture files under tests/utils/constants are read by the
agents, http and observability suites, so a fixture-only edit is knowingly
uncovered until the weekly run or the next change to that suite.

This supersedes the by-name rule for tests/utils/constants/<name>.ts and
its drift guard, and reverts the TEST_AGENT fixture move they required.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

---------

Co-authored-by: Claude Fable 5.1 <[email protected]>
…[APPS-37218] (#664)

* feat(platform): add Groups service for organization group management

Adds PlatformGroupService (exported as Groups) to the /platform subpath:

- getAll(organizationId) — all local and built-in groups
- getById(groupId, organizationId)
- create(name, organizationId, options?) — the API requires a
  client-generated group GUID, so the SDK generates it; initial members
  via memberUserIds
- updateById(groupId, organizationId, name, options?) — the API
  requires the name on every update (409 without it, live-verified), so
  it is a required positional; membership edits via memberUserIdsToAdd/
  memberUserIdsToRemove; the bound group.update() auto-fills the
  current name
- deleteById(groupId, organizationId)
- getMembers(groupId, organizationId, options?) — paged member
  references with fetch-all default

Groups are enriched with organizationId (not on the wire) so bound
update/delete/getMembers capture full context. Numeric GroupType codes
mapped to enums; members/mappedRole/scope dropped from the public
shape (members is always empty on the wire — membership is served by
getMembers).

Verified against the live API: 2389 unit tests passing, integration
8/8 (full CRUD, membership round-trips from both group and user side,
pagination), runtime E2E through packed dist bundles.

Co-Authored-By: Claude Fable 5 <[email protected]>

* fix(platform): address review comments on the Groups module doc and bound-method tests

- Say which /platform operations are user-scoped vs organization-scoped in the module JSDoc
- Cover the missing organization id guard for the bound delete() and getMembers()

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* refactor(platform): resolve the organization id inside Groups; drop it from every method

Groups now uses the shared organization id resolver like Users: no method takes
organizationId, and the response no longer carries the SDK-added organizationId
field. Bound methods only guard the group id.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* test(platform): declare the Groups suite with describeIntegration

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* fix(platform): address review comments on Groups — /groups subpath, update options, member paging

- Expose Groups on its own /groups subpath like Users and Roles
- updateById(groupId, update: PlatformGroupUpdateOptions) reads the current name when the
  caller omits it, so membership edits no longer require passing the name
- getMembers fetch-all mirrors Users: dedupe by id, short page terminal, advance by page size
- Docs call the service Groups, matching the SDK

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* refactor(platform): fold the Groups write renames into the single PlatformGroupMap

One map per service, as Users does: transformData() applies it to responses and
transformRequest() reverses it for the create/update bodies.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* fix(platform): reject an explicitly empty group name on update; document the read-before-write

Drop the dead !update guard (the parameter is required), distinguish an omitted
name from an empty one the API rejects, and note in the JSDoc that a
membership-only update reads the current name first.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* docs(platform): mirror the read-before-write note on the bound group.update()

Co-Authored-By: Claude Fable 5.1 <[email protected]>

---------

Co-authored-by: Claude Fable 5 <[email protected]>
…suites (#791)

* ci(pr-checks): run one PR Checks run per pull request

A new push to a pull request now cancels the run already in flight for that
PR. Without a concurrency group every push ran a full integration pass, and
two legs of the same branch shared the dev tenant: runs 35974091102 and
35974404828 (pushes 28 s apart) renamed the same fixture user under each
other and failed the users suite. weekly-coverage.yml keeps its own group.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* test(integration): budget the insightsrtm_ suites and the smoke Data Fabric call

Agents, Agent Traces, Maestro Processes and Maestro Cases call insightsrtm_,
whose aggregations take 15-30 s server-side on the CI tenant (agents.getAll
p90 17 s, getTopRunCount tail up to 29.6 s), so vitest's 30 s default fails
healthy responses in 38 of the last 99 failed user legs. Each suite gets the
120 s budget the Action Center suite already uses. The smoke Data Fabric
connectivity call goes from 15 s to 60 s; it queues behind schema DDL from
concurrent runs. No retries, no assertion changes. Same treatment #745 gives
the SLA tests in case-instances.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* test(integration): budget the Agent Memory and Governance suites too

Both sit on insightsrtm_ (memory.ts and governance.ts build on
INSIGHTS_RTM_BASE) and timed out at 30 s in 7 of the last 99 failed user
legs. Same 120 s suite budget as the other insightsrtm_ suites.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* test(maestro): keep concurrent runs off each other's case-instance fixtures

The suite scavenges tenant-wide state that every concurrent leg sees: the
newest Completed timer instance for reopen, and any Running instance of the
fixture process for pause/close. Two legs on the tenant at once pick the
same instance; one reopens and closes it, the other polls it for 180 s
("did not complete within 180s", 21 of the last 100 runs) or has its pause
rejected ("Canceling->Pausing is not a valid state transition"). Observed
live on the #745 user leg against the #746 leg, Sep 16 15:54-16:12.

- reopen claims a shared orphan by calling reopen as soon as it reads
  Completed; if the orphan is no longer Completed or the reopen is rejected,
  it starts an instance of its own and waits for that one. A reopen failure
  on our own instance still propagates. Own instances that end Cancelled or
  Faulted fail immediately instead of after 180 s.
- seedRunningInstance reuses a Running orphan only when it is older than an
  hour, so it cannot belong to a leg still in flight.
- getVariables' beforeAll gets a 60 s hook budget (ten enriched lookups).

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* test(maestro): budget the Process Instances suite

A pageSize: 1 getAll timed out at 30 s on the #791 pat leg with no other run
on the tenant; PIMS list and lookup calls stall under load. Same 120 s
suite budget as the other Maestro suites. No retries, no assertion changes.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* test(integration): retry Data Fabric record suites once on failure

On the #791 user leg, with no other run on the tenant, Data Fabric answered
503 to getAll, 504 to deleteAttachment, and the entities-query cleanup hook
overran 60 s. The record and attachment suites move into their own vitest
project (same group order as `integration`, so scheduling is unchanged)
carrying retry: 1, so one re-run separates a service fault from a
regression; repeated assertion failures still fail and hooks are not
retried. The entities-query cleanup hook gets 120 s like the other Data
Fabric cleanups. Collection is unchanged: 1198 tests, same set, no file in
two projects.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* test(integration): budget the Agent Feedback suite, give the reopen wait the test budget

On the #791 user leg (run 36837133226, alone on the tenant) three
llmopstenant_ feedback calls that take under a second on the pat leg timed
out at 30 s, the getVariables hook overran 60 s, and the [v1] reopen cell
started an instance of its own that the engine had not completed after
180 s. Feedback gets the 120 s suite budget, the getVariables hook 120 s,
and the reopen wait 240 s inside a 300 s test. The reopen poll now logs
which fixture path it took and warns when getById fails instead of
swallowing it, so the next failure names the cause.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* test(integration): skip the stages SLA tests at the gateway ceiling, retry Insights suites once

getStagesSlaSummary now takes 43-60 s server-side on the CI tenant (12 calls
measured on 2026-10-01; p50 was 26 s a week earlier) and the gateway answers
504 at 60 s, so the two tests fail on roughly every other call and no client
budget can change that. They are it.skip with the reason, next to the
getSlaSummary test already skipped in the same block, until Insights RTM
brings the aggregation back under the gateway limit.

The read-only insightsrtm_/llmopstenant_ suites (agents, feedback, memory,
governance, traces/agent, maestro processes and cases) move into an
integration-insights project with retry: 1, matching the Data Fabric one:
getTopRunCount answered in 0.4-10 s all day and then 504'd once after 60 s.
case-instances stays out of it because its pause/resume/reopen tests mutate
state. Collection is unchanged: 1194 tests, same set as main, no file in
two projects.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* test(integration): drop the retry projects, settle the slow-suite budgets at 90 s

Across four PR Checks runs with retry: 1 active, no test passed on a second
attempt; the one case it was meant for (a Data Fabric 504) failed twice
because a 504 means the server took over 60 s and a retry asks it again.
Both projects go, and vitest.integration.config.ts returns to the #747
layout. The suite budgets stay but at 90 s: a single request cannot succeed
past the 60 s gateway limit, so 90 s covers one slow call plus a follow-up
and nothing above that is reachable. The getVariables and entities-query
cleanup hooks use the same value. The reopen wait returns to 180 s / 240 s;
the atomic claim, the orphan age filter and the diagnostics stay.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* test(integration): skip choice-set fields in generated records, wait 120 s for seeded instances

Two failures that kept recurring after #745/#747/#768/#775 landed:

- The shared fixture entity carries a choice-set field. getWritableFields let
  it through and generateFieldValue wrote a random string into it, which the
  server rejects ("Single choiceset value Test_x is not integer"): 23 legs in
  September, 2 more on Oct 4. Choice-set fields are now excluded, like
  relationship and file fields already are.
- The process-instances cancel test polled 20 x 2 s for its own instance to
  reach Running and gave up at ~60 s; PIMS took longer four times since Oct 2.
  The poll now runs against a 120 s deadline inside a 180 s test.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* test(maestro): wait on a deadline for the resumed case instance to run

The pause/resume test polled 30 x 2 s after resume and asserted on the last
reading; on the #791 pat leg the instance was still Resuming when the polls
ran out while another PR's run shared the tenant. The wait is now a 120 s
deadline, matching the cancel test, inside a 240 s test.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* fix(tests): import the data-fabric endpoint constant from the endpoints barrel

#802 moved src/utils/constants/endpoints/data-fabric.ts into
endpoints/data-fabric/data-fabric.ts without an index, so the bare folder
import in entities-schema.integration.test.ts no longer resolves and every
integration leg on main has failed at import since 09:58 UTC on Oct 5
(ERR_MODULE_NOT_FOUND, seen on #791 and feat/platform-groups). Import from
the endpoints barrel like the other tests do.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

---------

Co-authored-by: Claude Fable 5.1 <[email protected]>
)

* refactor(platform)!: source the organization from the SDK instance

Platform settings are scoped to an (organization, user) pair, but the
organization was passed in per call — as an option on getUserSettings and
a required positional argument on updateUserSettings. The SDK already
knows which organization it was initialized against, so both methods now
read it from the instance and callers only supply the user.

BREAKING CHANGE: updateUserSettings() no longer takes an organizationId
argument and getUserSettings() no longer takes an options argument.
PlatformSettingGetOptions is removed. Both shipped in 1.6.1.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>

* fix: address convention review comments

Remove the integration-test organizationId config, orphaned when the
Platform suite stopped needing it; drop the stale README row; stop the
module @example deriving userId from a read result that may omit the key.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>

* docs(platform): keep the existing JSDoc wording

Restore the service class comment and drop the per-method scoping notes,
so the module, service class, and ServiceModel comments all read as they
did before. The signature change alone drives the remaining JSDoc edits.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>

* refactor(platform): drop the partitionGlobalId query param

The organization is already on the request path — ApiClient builds
{baseUrl}/{orgName}/identity_/api/Setting — so sending it again as
partitionGlobalId on the read and in the body on the write is redundant.

Reverts the BaseService.config.orgName accessor added earlier on this
branch: Platform was its only consumer, and nothing reads it now.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>

* fix(platform): send the resolved organization GUID as partitionGlobalId

Identity scopes settings by partition GUID, not by the org name in the URL:
without partitionGlobalId a read answers "User is not host admin" (401) and
the API gateway rejects it (403). Feed the param from the shared organization
id resolver, so callers still never pass an organization, and drop the unused
UIPATH_ORGANIZATION_ID wiring from the coverage workflow.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* refactor(platform)!: rename the Platform service to Settings on its own /settings subpath

Mirrors the Users, Groups, and Roles layout: PlatformSettingService is exported as
Settings from @uipath/uipath-typescript/settings, the model interface is
PlatformSettingServiceModel, and the platform.* model files become settings.*.
The /platform subpath is removed. The stale header example that still passed the
removed options argument is fixed.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* ci: do not run the integration suites for coverage.yml or tests/integration/config changes

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
Co-authored-by: Sarath1018 <[email protected]>
Minor bump: #691 removes the /platform subpath and moves user settings
to /settings. release-metadata.json regenerated from a fresh build.

Co-authored-by: Claude Opus 5.5 <[email protected]>
… ExternalReference (#787)

* fix(functions): resolve process-prefixed trigger names and invoke via ExternalReference

Functions.invoke() looked triggers up by exact name, but Orchestrator stores
triggers created since July 2026 as `<process name>_<name>`, so the lookup
missed. It also built the URL from Release.Slug, which is null on tenants
without release slugs, producing `/t/<key>/<process>/null` and a 404.

The lookup now accepts the declared or the full stored name, with an
optional `processName` to pick between processes that declare the same
name. The invoke route and folder key come from the trigger's
ExternalReference, falling back to the slugs only when it is absent.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(functions): keep processSlug typed as string

The route now comes from ExternalReference, so the fix no longer needs the
public type change. buildInvokeRoute still accepts a null process slug,
which the API returns on tenants without release slugs.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(functions): warn when listing function names for a not-found error fails

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(functions): match the ExternalReference route as non-space characters

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* ci: scope a loose service file to the domains that import it

src/services/base.ts and src/services/folder-scoped.ts sit beside the
domain folders, so any change to them ran the full integration suite.
The resolver now follows src/ imports (through the services barrel) and
takes the union of the importers' classifications: folder-scoped.ts runs
action-center and orchestrator, base.ts still runs everything because
core imports it. #787 would have run two suites instead of all of them.

The resolver also emits `projects`, the vitest projects the scope needs,
so the workflow can skip the Data Fabric schema leg when Data Fabric is
not in scope.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 <[email protected]>
…tion (#774)

A coded function's robot token alone always got 204 from StartTransaction:
Orchestrator only hands out an item when RobotIdentifier names the robot.
new UiPath(ctx) now keeps ctx.robot.key and startTransaction sends it.

Co-authored-by: Claude Opus 5.5 <[email protected]>
The rollup .d.ts bundling has outgrown the 4 GB heap SETUP.md grants: npm run build has aborted
with a JavaScript heap OOM on every Agentic Inner Loop run since 24 Sep, even on a 32 GB agent,
because the cap is the explicit --max-old-space-size, not the machine. The repo's CI already
builds with 6144 (coverage.yml, publish.yml); match it.

Co-authored-by: Claude Opus 5.5 <[email protected]>
…28] (#665)

* feat(platform): add Directory service for principal lookups

Adds PlatformDirectoryService (exported as Directory) to the /platform
subpath — the read-only lookup layer over an organization's principals:

- search(organizationId, options?) — find users, groups, and
  applications by name prefix, entity type, and source
- getGroupMembership(userId, groupIds, organizationId) — returns the
  subset of the given groups the user belongs to; the membership check
  behind RBAC gating ("is this user in the Administrators group?")

Transforms: identifier→id and identityName→name renames, redundant
objectType dropped, numeric entity-type codes mapped to enums (live
API sends codes despite the spec declaring strings). Read-only service
— no bound methods, per convention.

Also hardens the Users integration suite against parallel-file races:
sibling suites mutate the shared test user's group memberships, so the
replace-semantics assertion now checks membership survival across the
update call instead of exact snapshot equality.

Verified against the live API: 2403 unit tests passing, all four
platform integration suites green in a parallel run (31/31).

Co-Authored-By: Claude Fable 5 <[email protected]>

* fix(platform): address review comments on Directory; resolve the organization id internally

- Declare the suite with describeIntegration and throw when the Everyone group is missing
- Self-contained getGroupMembership example
- Directory resolves the organization GUID like Users, Groups, and Roles: search(options?) and
  getGroupMembership(userId, groupIds) no longer take organizationId

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* fix(platform): address review comments on Directory — /directory subpath, required search term

- Expose Directory on its own /directory subpath like Users, Groups, and Roles; the
  /platform module is settings-only again
- search(startsWith, options?): the API rejects a search without a term and returns
  every match unpaged, so the prefix is a required positional argument and the JSDoc
  says so
- Throw a clear error when the live search returns nothing instead of dereferencing
  results[0]; drop the Users suite change that belongs with #663; collapse double
  blank lines; call the service Directory in docs and suite titles

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* fix(platform): reject a whitespace-only directory search term

The API answers 400 for it just as for an empty string, so the guard trims
before checking. The value itself is still sent as given — the API does not
trim, and a padded prefix simply matches nothing.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* fix: integration test

---------

Co-authored-by: Claude Fable 5 <[email protected]>
@Raina451
Raina451 force-pushed the fix/pca-sdk-public-runtime branch from 80993d1 to 4a6189a Compare October 7, 2026 08:00
@Raina451 Raina451 changed the title fix(public-apps): run public apps without a sign-in [APPS-35455] feat(public-apps): run public coded apps without a sign-in Oct 7, 2026
@Raina451
Raina451 force-pushed the fix/pca-sdk-public-runtime branch from 4a6189a to c89f87f Compare October 7, 2026 08:35

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.