@agentproto/app-kit 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -91,6 +91,37 @@ An app may also declare `requires: ["@acme/shared", ...]` — app ids that must
91
91
  applied to the same scope before this one can run. The runtime validates the
92
92
  graph when mounting apps via `app_apply`.
93
93
 
94
+ ### Placement, requirements, and A2A exposure
95
+
96
+ Four optional `APP.md` keys describe where an app can run and what it exposes.
97
+ They are **semantics only** — nothing schedules or serves on them yet. All are
98
+ validated by `defineApp` (`AppDefinitionError`) and `loadAppHandle`
99
+ (`AppLoadError`), surfaced on `AppHandle`, and written by `emit` only when they
100
+ differ from the default.
101
+
102
+ ```yaml
103
+ placement: any # "local" | "box" | "any" | "split" (default "any")
104
+ requires:
105
+ browser: false # needs the user's real browser (Bureau / local-browser)
106
+ fs: false # needs the user's local filesystem beyond app data
107
+ gpu: false
108
+ secrets: [] # env/secret names the app needs
109
+ apps: [] # other app ids this app depends on
110
+ exposes: # A2A-visible surfaces (default: none exposed)
111
+ agents: [] # ids from `agents:`
112
+ workflows: [] # ids from `workflows:`
113
+ accepts:
114
+ tasks: false # accept A2A tasks (default false)
115
+ ```
116
+
117
+ - An unknown `placement`, or an `exposes.*` id that isn't one of the app's own
118
+ agents/workflows, is a validation error.
119
+ - `requires` also still accepts the legacy flat array (`requires: ["@acme/shared"]`,
120
+ equivalent to `requires: { apps: [...] }`).
121
+ - On the handle: `placement`, `exposes`, `accepts` and `requirements` (every key
122
+ resolved to its default) are always present. `handle.requires` stays the flat
123
+ list of app ids (absent when none) so existing dependency checks keep working.
124
+
94
125
  ## UI surfaces, artifacts, dev-launch, and the artifact surface
95
126
 
96
127
  Beyond agents and workflows, an app can declare four optional surfaces that
@@ -99,7 +130,20 @@ runtime app registry:
99
130
 
100
131
  - **`ui`** — an HTML dashboard/panel. `html` is written to
101
132
  `.agentproto/ui/index.html`; `APP.md` frontmatter carries the relative path
102
- plus optional `title`, `description`, `tools`, and `csp`.
133
+ plus optional `title`, `description`, `tools`, `csp`, and `build`.
134
+ - **`ui.build`** — how to (re)build `ui.path` when it's missing or stale,
135
+ so the bundle doesn't have to be committed. `{ command, cwd?, sources? }`:
136
+ `command` is a shell command line run with cwd `cwd` (default: the app
137
+ dir); `sources` is a list of globs (relative to `cwd`, default
138
+ `["src/**"]`) whose newest mtime is compared against `ui.path`'s to decide
139
+ staleness. The daemon/CLI run it — `app_install`, the first `GET
140
+ /apps/:appId/ui`, the MCP panel cache, and `agentproto app serve` all call
141
+ the same `ensureAppUiBuilt` (`@agentproto/runtime/app-ui-build`) before
142
+ serving, single-flight per bundle path so concurrent first requests only
143
+ build once. Output is captured to `<appDir>/.agentproto/ui-build.log`; a
144
+ failing build surfaces that log's tail as the tool/route error instead of
145
+ a bare 404. No `ui.build` declared ⇒ today's behavior: the bundle must
146
+ already exist on disk.
103
147
  - **`artifact`** — a persistent HTML dashboard (Cowork artifact). The app
104
148
  provides a path to an HTML file on disk; `emit` copies it to
105
149
  `.agentproto/artifact/index.html`. The daemon never writes the host manifest
@@ -225,6 +269,14 @@ export const dashboardApp = defineApp({
225
269
  html: "<!doctype html><html>…</html>",
226
270
  title: "Ops Dashboard",
227
271
  tools: ["terminal_start", "agent_start"],
272
+ // Only needed when `ui.path`'s bundle isn't committed to the repo —
273
+ // the daemon/CLI build it on demand before serving. See the APP.md
274
+ // frontmatter equivalent below.
275
+ build: {
276
+ command: "pnpm run build",
277
+ cwd: "ui",
278
+ sources: ["ui/src/**", "ui/index.html", "ui/vite.config.ts"],
279
+ },
228
280
  },
229
281
  artifact: {
230
282
  path: "/path/to/dashboard.html",
@@ -321,6 +373,86 @@ const app = await loadAppHandle("/path/to/emitted/app")
321
373
  app.agents.map((e) => e.agent.id) // ["@agentik/reviewer", "fixer"]
322
374
  ```
323
375
 
376
+ ### `ui.build` — don't commit the generated bundle
377
+
378
+ Add a `build` block under `ui` in `APP.md`'s frontmatter and gitignore
379
+ `.agentproto/ui/index.html` — the daemon builds it on demand instead:
380
+
381
+ ```yaml
382
+ ui:
383
+ path: .agentproto/ui/index.html
384
+ build:
385
+ command: pnpm run build
386
+ cwd: ui
387
+ sources:
388
+ - ui/src/**
389
+ - ui/index.html
390
+ - ui/vite.config.ts
391
+ ```
392
+
393
+ `app_install`, the first `GET /apps/:appId/ui`, the MCP panel, and
394
+ `agentproto app serve` all resolve this the same way (`peekAppUi` +
395
+ `ensureAppUiBuilt` from `@agentproto/runtime/app-ui-build`): missing or
396
+ older than the newest matching source → run `command` once (single-flight
397
+ per bundle path, output captured to `.agentproto/ui-build.log`) → serve.
398
+ Omit `build` to keep committing the bundle, unchanged from before this
399
+ existed.
400
+
401
+ ## Boundaries — fs zones for app-spawned sessions
402
+
403
+ Set `boundaries: { enforce: "required" | "best-effort" }` on `defineApp` (or
404
+ `boundaries:` in `APP.md`'s frontmatter) to declare how strictly the daemon
405
+ must confine sessions the app spawns — `app_run`, and workflow agent steps
406
+ whose workflow carries this app's `appId`:
407
+
408
+ ```ts
409
+ defineApp({
410
+ id: "@acme/transcriber",
411
+ boundaries: { enforce: "required" },
412
+ // ...
413
+ })
414
+ ```
415
+
416
+ For every such session the daemon builds one **boundary** with three zones:
417
+
418
+ | Zone | Contents | Access |
419
+ | --- | --- | --- |
420
+ | read-only | the installed app dir (source, `.agentproto/`, `scripts/`) | read |
421
+ | writable | the app's `data/` dir, and the workflow run's `$run.workspace` (`app_run` has no run workspace: writable is `data/` only) | read + write |
422
+ | everything else | including the daemon's own workspace and any host monorepo the app happens to be installed inside | denied |
423
+
424
+ The boundary is enforced on two independent surfaces:
425
+
426
+ 1. **Daemon file/command tools** (`file_read`, `file_write`, `directory_*`,
427
+ `command_execute`, …) — always enforced, identity-based: the daemon
428
+ recovers the caller's boundary from its own session record, resolves
429
+ relative paths against the app dir, and rejects reads/writes outside the
430
+ zones (including through symlinks). This does not depend on the harness.
431
+ 2. **The harness's own native tools** (a claude-code session's Bash, Write,
432
+ Edit, …) — enforced by wrapping the harness in an OS sandbox
433
+ (`@agentproto/command-sandbox`: macOS Seatbelt or Linux bubblewrap) built
434
+ from the same zones, plus, on adapters that support it, excluding the host
435
+ repository's `CLAUDE.md`/`AGENTS.md` from context so an app session never
436
+ inherits instructions from the monorepo it's installed inside.
437
+
438
+ Surface 2 needs an adapter that supports fs zones and an available OS sandbox
439
+ backend. When it can't be enforced:
440
+
441
+ - `boundaries.enforce: "best-effort"` (the default) still spawns the session
442
+ — daemon tools remain confined — and emits a `session:harness-warning`
443
+ naming the reason (no sandbox backend on this platform, the adapter doesn't
444
+ support `fsZones`, or `commandSandbox: "off"` was requested explicitly).
445
+ - `boundaries.enforce: "required"` refuses the spawn instead of downgrading
446
+ silently (`app_boundary_unenforceable`).
447
+
448
+ **Deferred:** the `AGENT.md` `tools:` allowlist becoming an *enforced*
449
+ allowlist on adapters that support it (today it's advisory — an agent can
450
+ still reach native Bash next to an allowlisted `command_execute`). Also out
451
+ of scope for this phase: a network-capable sandboxed process reaching the
452
+ daemon's HTTP API directly (bypasses the identity-based check the MCP path
453
+ uses), and boundary enforcement on daemon-wide MCP tools other than
454
+ file/command/terminal.
455
+
324
456
  ## Runtime note
325
457
 
326
458
  `toMastraAgents` is the path where a `body` becomes a **true** model
@@ -1 +1 @@
1
- {"version":3,"file":"define-app.d.ts","sourceRoot":"","sources":["../src/define-app.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAQH,OAAO,KAAK,EAEV,aAAa,EACb,SAAS,EACT,aAAa,EAGd,MAAM,YAAY,CAAA;AAInB,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,aAAa,GAAG,SAAS,CA8IvD;AAiID,YAAY,EAAE,aAAa,EAAE,CAAA"}
1
+ {"version":3,"file":"define-app.d.ts","sourceRoot":"","sources":["../src/define-app.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAQH,OAAO,KAAK,EAEV,aAAa,EACb,SAAS,EACT,aAAa,EAGd,MAAM,YAAY,CAAA;AAKnB,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,aAAa,GAAG,SAAS,CAyKvD;AAiID,YAAY,EAAE,aAAa,EAAE,CAAA"}
package/dist/emit.d.ts CHANGED
@@ -32,7 +32,7 @@
32
32
  */
33
33
  import type { WorkflowHandle } from "@agentproto/workflow";
34
34
  import type { WorkspaceHandle } from "@agentproto/workspace";
35
- import type { AgentEntry, AppArtifactDecl, AppArtifactSurface, AppDataDefinition, AppDevDefinition, AppSkillSurface, AppUiDefinition, EmittedApp } from "./types.js";
35
+ import type { AgentEntry, AppAccepts, AppArtifactDecl, AppArtifactSurface, AppBoundariesDefinition, AppDataDefinition, AppDevDefinition, AppExposes, AppPlacement, AppRequirements, AppSkillSurface, AppUiDefinition, EmittedApp } from "./types.js";
36
36
  interface EmitInput {
37
37
  readonly agents: readonly AgentEntry[];
38
38
  readonly workflows: readonly WorkflowHandle[];
@@ -42,6 +42,10 @@ interface EmitInput {
42
42
  readonly version?: string;
43
43
  readonly description?: string;
44
44
  readonly requires?: readonly string[];
45
+ readonly requirements?: AppRequirements;
46
+ readonly placement?: AppPlacement;
47
+ readonly exposes?: AppExposes;
48
+ readonly accepts?: AppAccepts;
45
49
  readonly ui?: AppUiDefinition;
46
50
  readonly artifact?: AppArtifactSurface;
47
51
  readonly skill?: AppSkillSurface;
@@ -49,6 +53,7 @@ interface EmitInput {
49
53
  readonly dev?: AppDevDefinition;
50
54
  readonly data?: AppDataDefinition;
51
55
  readonly externalReadRoots?: readonly string[];
56
+ readonly boundaries?: AppBoundariesDefinition;
52
57
  readonly category?: string;
53
58
  }
54
59
  export declare function emitApp(app: EmitInput, dir: string): Promise<EmittedApp>;
@@ -1 +1 @@
1
- {"version":3,"file":"emit.d.ts","sourceRoot":"","sources":["../src/emit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,eAAe,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAGpK,UAAU,SAAS;IACjB,QAAQ,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,CAAA;IACtC,QAAQ,CAAC,SAAS,EAAE,SAAS,cAAc,EAAE,CAAA;IAC7C,QAAQ,CAAC,SAAS,CAAC,EAAE,eAAe,CAAA;IACpC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACrC,QAAQ,CAAC,EAAE,CAAC,EAAE,eAAe,CAAA;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,kBAAkB,CAAA;IACtC,QAAQ,CAAC,KAAK,CAAC,EAAE,eAAe,CAAA;IAChC,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,eAAe,EAAE,CAAA;IAC/C,QAAQ,CAAC,GAAG,CAAC,EAAE,gBAAgB,CAAA;IAC/B,QAAQ,CAAC,IAAI,CAAC,EAAE,iBAAiB,CAAA;IACjC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC9C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,wBAAsB,OAAO,CAAC,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAgH9E"}
1
+ {"version":3,"file":"emit.d.ts","sourceRoot":"","sources":["../src/emit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,UAAU,EAAE,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAGpP,UAAU,SAAS;IACjB,QAAQ,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,CAAA;IACtC,QAAQ,CAAC,SAAS,EAAE,SAAS,cAAc,EAAE,CAAA;IAC7C,QAAQ,CAAC,SAAS,CAAC,EAAE,eAAe,CAAA;IACpC,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACrC,QAAQ,CAAC,YAAY,CAAC,EAAE,eAAe,CAAA;IACvC,QAAQ,CAAC,SAAS,CAAC,EAAE,YAAY,CAAA;IACjC,QAAQ,CAAC,OAAO,CAAC,EAAE,UAAU,CAAA;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,UAAU,CAAA;IAC7B,QAAQ,CAAC,EAAE,CAAC,EAAE,eAAe,CAAA;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,kBAAkB,CAAA;IACtC,QAAQ,CAAC,KAAK,CAAC,EAAE,eAAe,CAAA;IAChC,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,eAAe,EAAE,CAAA;IAC/C,QAAQ,CAAC,GAAG,CAAC,EAAE,gBAAgB,CAAA;IAC/B,QAAQ,CAAC,IAAI,CAAC,EAAE,iBAAiB,CAAA;IACjC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC9C,QAAQ,CAAC,UAAU,CAAC,EAAE,uBAAuB,CAAA;IAC7C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,wBAAsB,OAAO,CAAC,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAuH9E"}
package/dist/index.d.ts CHANGED
@@ -38,8 +38,11 @@
38
38
  */
39
39
  export { defineApp, AppDefinitionError } from "./define-app.js";
40
40
  export { emitApp } from "./emit.js";
41
- export { loadAppHandle, AppLoadError, resolveAppUIRoot } from "./load-app.js";
41
+ export { loadAppHandle, AppLoadError, resolveAppUIRoot, peekAppUi } from "./load-app.js";
42
+ export type { AppUiPeek } from "./load-app.js";
42
43
  export { loadAppBundledTools } from "./load-app-tools.js";
44
+ export { packApp, unpackApp, aggregateSha256, collectFiles, isManifest, safeId, AGENTAPP_FORMAT, AgentAppPackError, } from "./pack.js";
45
+ export type { AgentAppManifest, BundleFile, AgentAppPackErrorCode } from "./pack.js";
43
46
  export { refKey, stripOwner } from "./refs.js";
44
- export type { AppDefinition, AppHandle, AgentEntry, DoctypeHandle, WorkspaceShorthand, WorkspaceInput, AppUiDefinition, AppArtifactSurface, AppSkillSurface, AppArtifactDecl, AppDevLaunchConfig, AppDataDefinition, AppDevDefinition, ToMastraAgentOptions, EmittedApp, } from "./types.js";
47
+ export type { AppDefinition, AppHandle, AgentEntry, DoctypeHandle, WorkspaceShorthand, WorkspaceInput, AppUiDefinition, AppUiBuildConfig, AppArtifactSurface, AppSkillSurface, AppArtifactDecl, AppDevLaunchConfig, AppBoundariesDefinition, AppDataDefinition, AppDevDefinition, AppPlacement, AppRequirements, AppExposes, AppAccepts, ToMastraAgentOptions, EmittedApp, } from "./types.js";
45
48
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAC/D,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAC7E,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AACzD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAC9C,YAAY,EACV,aAAa,EACb,SAAS,EACT,UAAU,EACV,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,oBAAoB,EACpB,UAAU,GACX,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAC/D,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AACxF,YAAY,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AACzD,OAAO,EACL,OAAO,EACP,SAAS,EACT,eAAe,EACf,YAAY,EACZ,UAAU,EACV,MAAM,EACN,eAAe,EACf,iBAAiB,GAClB,MAAM,WAAW,CAAA;AAClB,YAAY,EAAE,gBAAgB,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAA;AACpF,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAC9C,YAAY,EACV,aAAa,EACb,SAAS,EACT,UAAU,EACV,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,uBAAuB,EACvB,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,eAAe,EACf,UAAU,EACV,UAAU,EACV,oBAAoB,EACpB,UAAU,GACX,MAAM,YAAY,CAAA"}