Skip to content

feat(appkit): establish ambient user execution scopes - #594

Open
MarioCadenas wants to merge 10 commits into
resource-and-execution-base-prfrom
execution-user-scope
Open

MarioCadenas wants to merge 10 commits into
resource-and-execution-base-prfrom
execution-user-scope

Conversation

@MarioCadenas

@MarioCadenas MarioCadenas commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Add top-level appkit.asUser(req) with a shared caller snapshot for shorthand calls, run blocks, and lazy streams. This is the second layer of the execution-model stack, based on #592. Do not merge independently of the base.

  • Keep per-plugin asUser(req) as a deprecated compatibility wrapper.
  • Partition cache entries and in-flight requests by app or user:<id>.
  • Preserve legacy context types, helpers, and deprecated warehouse access. Resource bindings remain separate from execution identity.
  • SP stays the default outside caller execution. Group is deferred and there is no public asApp().

Fail-closed tool dispatch

PluginContext.executeTool inherits an existing ALS caller. With no caller, it establishes the same request-based user scope as asUser(req) before opening the tool span or executing the provider. Missing production credentials reject. This preserves the original direct OBO dispatch behavior; callers do not need to adopt the new top-level API.

The agents HTTP execution routes (/invocations, /responses, /api/agents/chat) also establish user scope at entry. The marked development fallback remains the only missing-token exception. Standalone runAgent retains its separate SP default.

The testing kit uses production header validation and ALS with only the workspace client factory faked. It records the identity actually observed by the tool.

Design reference: design-docs/execution-identity-e2e.md, sections 5.1.2, 5.1.3, 5.1.4, and 9. Per the updated compatibility requirement, this is additive and preserves legacy behavior and exports. No breaking-change marker or bypass label is needed.

Verification

  • pnpm -r typecheck
  • Full monorepo unit suite: 5,237 passed, one existing skip
  • Both original dev-playground OBO regression tests pass unchanged
  • Coverage for default user dispatch, missing credentials, ambient caller precedence, concurrency, scope restoration, and public compatibility aliases
  • HTTP tests cover user-scoped agent and tool execution on all three routes
  • pnpm build, pnpm docs:build, pnpm check:fix, pnpm knip, and generated-type freshness

@MarioCadenas
MarioCadenas requested a review from a team as a code owner September 23, 2026 16:00
@MarioCadenas
MarioCadenas requested review from atilafassina and removed request for a team September 23, 2026 16:00
@MarioCadenas
MarioCadenas added this pull request to stack #602 September 24, 2026 08:14
@github-actions

github-actions Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

📦 Bundle size report

Compared against bundle-size-baseline.json (main).

@databricks/appkit

npm tarball (packed): 1.2 MB (+12 KB) — gzipped download (dist + bin; excludes release-only docs/NOTICE).

dist raw gzip
JS (runtime) 1.2 MB (+12 KB) 432 KB (+4.4 KB)
Type declarations 451 KB (+4.8 KB) 163 KB (+1.7 KB)
Source maps 2.4 MB (+22 KB) 811 KB (+8.5 KB)
Other 11 KB 3.7 KB
Total 4.0 MB (+38 KB) 1.4 MB (+15 KB)
Per-entry composition (own code — deps external (as shipped))
Entry Initial (gz) Lazy (gz) Total (gz) node_modules (min) Own code (min)
. 98 KB (+1.4 KB) 2.5 KB (-1 B) 100 KB (+1.4 KB) external 320 KB (+4.5 KB)
./beta 95 KB (+1.0 KB) 479 B (+1 B) 95 KB (+1.1 KB) external 285 KB (+2.8 KB)
./testing 40 KB (+1.5 KB) 31 KB (-22 B) 71 KB (+1.5 KB) external 206 KB (+3.7 KB)
./tsdown 520 B 0 B 520 B external 813 B
./type-generator 23 KB 0 B 23 KB external 65 KB

Chunks:

Entry Chunk Load Size (gz)
. index.js initial 93 KB
. utils.js initial 4.6 KB
. remote-tunnel-manager.js lazy 2.5 KB
./beta beta.js initial 77 KB
./beta stream-manager.js initial 5.8 KB
./beta databricks.js initial 3.3 KB
./beta wide-event-emitter.js initial 3.1 KB
./beta configuration.js initial 2.3 KB
./beta service-context.js initial 1.9 KB
./beta client.js initial 593 B
./beta client-options.js initial 219 B
./beta supervisor-api.js lazy 191 B
./beta databricks.js lazy 165 B
./beta index.js lazy 123 B
./testing manifest.js initial 27 KB
./testing index.js initial 10 KB
./testing wide-event-emitter.js initial 2.9 KB
./testing index.js lazy 26 KB
./testing remote-tunnel-manager.js lazy 2.5 KB
./testing utils.js lazy 1.8 KB
./tsdown index.js initial 520 B
./type-generator index.js initial 23 KB

@databricks/appkit-ui

npm tarball (packed): 350 KB — gzipped download (dist + bin; excludes release-only docs/NOTICE).

dist raw gzip
JS (runtime) 395 KB 132 KB
Type declarations 229 KB 84 KB
Source maps 766 KB 253 KB (+1 B)
CSS 16 KB 3.2 KB
Total 1.4 MB 472 KB (+1 B)
Per-entry composition (consumer bundle — deps bundled, peerDeps external)
Entry Initial (gz) Lazy (gz) Total (gz) node_modules (min) Own code (min)
./js 5.3 KB 49 KB 55 KB 208 KB 14 KB
./js/beta 20 B 0 B 20 B 0 B 0 B
./react 432 KB 49 KB 481 KB 1.3 MB 177 KB
./react/beta 1.0 KB 0 B 1.0 KB 0 B 1.9 KB

Chunks:

Entry Chunk Load Size (gz)
./js index.js initial 5.2 KB
./js chunk initial 120 B
./js apache-arrow lazy 49 KB
./js/beta beta.js initial 20 B
./react index.js initial 430 KB
./react tslib initial 2.1 KB
./react apache-arrow lazy 49 KB
./react/beta beta.js initial 1.0 KB

@github-actions

github-actions Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

🤖 AppKit PR bot

🔬 Run evals

Start an eval for this PR from the evals-monitor app: Go to Evals Monitor →

📦 Try this PR's app template

Scaffolds a new app from this PR's SDK build. Run it in any folder (requires the GitHub CLI — gh auth login — and the Databricks CLI):

gh run download 36878383395 -R databricks/appkit -n appkit-template-0.81.0-pr.e021ae4-execution-user-scope-594 -D appkit-pr-594 \
  && unzip -o "appkit-pr-594/appkit-template-0.81.0-pr.e021ae4-execution-user-scope-594.zip" -d "appkit-pr-594" \
  && databricks apps init --template "appkit-pr-594"

The template pins @databricks/appkit and @databricks/appkit-ui to tarballs built from this branch, so the scaffolded app runs against this PR's code.

@MarioCadenas
MarioCadenas force-pushed the execution-user-scope branch 4 times, most recently from 94af654 to b4e81f5 Compare September 29, 2026 15:03
Inherit ambient callers and retain fail-closed request-based tool dispatch.
Preserve deprecated context exports and warehouse access for existing callers.

Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
Share the plain-object helper while preserving existing exports and object semantics.
Remove redundant proxy exclusions and clarify fail-closed tool scope selection.

Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
MarioCadenas and others added 6 commits October 1, 2026 10:50
Brings in main through #592, including #625. Resolves the conflict in
docs/docs/plugins/execution-context.md by keeping this branch's caller
scope and agents sections and keeping #625's surface table, updated for
behavior this branch changes: the agents HTTP routes now open user scope,
so plugin-toolkit and hand-rolled tool calls run as the user, while the
model call stays on the service principal because the adapter builds its
own client. Analytics `.obo.sql` queries are listed as a user-lane surface.

Co-authored-by: Isaac <no-reply@databricks.com>
Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
The agents HTTP routes wrapped the whole run in user scope, so hand-rolled
tool({ execute }) calls ran as the user. On main they run as the app
service principal, and an app relying on that would break.

User scope now applies only where plugin-toolkit tools dispatch:
executeTool opens it per call and still rejects without user credentials,
so a plugin tool never silently runs as the service principal. The model
call and hand-rolled tools run in the app context, as on main. Sub-agent
tool calls go through the same dispatch.

Tests cover all three execution routes and sub-agents. Docs describe
hand-rolled tools as running as the app service principal.

Co-authored-by: Isaac <no-reply@databricks.com>
Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
scoped-api.ts re-exported isPlainObject from utils only so the agreement
test could import it from here. Remove the re-export and point the test at
the utility directly; the dedicated util remains the single source.

Co-authored-by: Isaac <no-reply@databricks.com>
Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
Resolves the public barrel export list: keeps this branch's exports and
drops `Principal`, removed in the base PR.

Co-authored-by: Isaac <no-reply@databricks.com>
Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
Co-authored-by: Isaac <no-reply@databricks.com>
Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
Repeated property access on one asUser(req) handle rebuilt the scoped
proxy on every read (measured ~854 ns per access on a cached handle).
Memoize each plugin's wrapped export in a Map created inside the asUser
call, so repeated access reuses the wrapper (~12 ns) and a method call
drops from ~1500 ns to ~490 ns.

The memo lives only on the per-request handle: the Map is created per
asUser(req) call and captured solely by that handle's getters, so it dies
with the request and is never shared across principals. A new test proves
two handles get different wrappers and each resolves to its own principal.
Fail-closed and per-principal cache partitioning are unchanged.

Co-authored-by: Isaac <no-reply@databricks.com>
Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>

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.

1 participant