@agentproto/runtime 3.6.1 → 4.0.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.
@@ -26,6 +26,16 @@
26
26
  interface ExportedMessage {
27
27
  role: "user" | "assistant" | "tool" | "system";
28
28
  text?: string;
29
+ /** Set on a `user`-role message that is a typed inter-session message
30
+ * (a `session-message` record) rather than the human: the daemon-attested
31
+ * sender. Absent for a human prompt. */
32
+ from?: {
33
+ sessionId?: string;
34
+ label?: string;
35
+ relation: "child" | "parent" | "sibling" | "human" | "system";
36
+ kind: string;
37
+ messageId: string;
38
+ };
29
39
  reasoning?: string;
30
40
  toolName?: string;
31
41
  toolCalls?: {
@@ -1,5 +1,107 @@
1
+ import { C as ContextContinuityPolicy, S as SpawnBrowserMode } from './context-continuity-Bn99UtVC.js';
1
2
  import { CatalogProvider } from '@agentproto/model-catalog';
2
- import { C as ContextContinuityPolicy } from './context-continuity-ib9_bVYM.js';
3
+
4
+ /**
5
+ * Deferred/lazy MCP tool loading (harness-parity item 3).
6
+ *
7
+ * Motivation: a root `/mcp` session sees every tool the daemon registers —
8
+ * today that's 60+ tools across families (agent_*, session_*, policy_*,
9
+ * routine_*, workflow_*, terminal_*, command_*, file_*, directory_*,
10
+ * browser_*, tunnel_*, mcp_*, inbound_watcher_*) — all with full schemas,
11
+ * loaded upfront, whether or not the session ever touches most of them.
12
+ *
13
+ * NB: imported third-party MCP servers (`mcp_import`) do NOT have this
14
+ * problem — they're already proxied behind `mcp_imported_tool_list` /
15
+ * `mcp_imported_call` (session-tools.ts), a search-then-call pattern that
16
+ * never mounts an upstream server's tools directly. This module addresses
17
+ * the daemon's OWN gateway tools, the part of the surface still eager.
18
+ *
19
+ * ## Why this does NOT use the SDK's enable()/disable()
20
+ *
21
+ * `@modelcontextprotocol/sdk` 1.29.0's `server.tool(...)` returns a
22
+ * `RegisteredTool` with `.enable()`/`.disable()`, `tools/list` filters on
23
+ * `.enabled`, and the server auto-advertises `tools: { listChanged: true }`
24
+ * — a real, SDK-supported dynamic-registration mechanism. An earlier draft
25
+ * of this module used exactly that: register tools disabled, `.enable()`
26
+ * them from `tool_search`.
27
+ *
28
+ * It does not work here. `http-server.ts` documents (and a live daemon
29
+ * run confirmed): "MCP transport: stateless mode for v1 — each request is
30
+ * independent" — a FRESH `McpServer` is built via `mcpServerFactory()` for
31
+ * EVERY request, including `tools/call`. An `.enable()` mutation made
32
+ * during a `tool_search` call lives only on that call's own throwaway
33
+ * server instance; the very next `tools/call` for the newly-"enabled"
34
+ * tool gets a brand-new instance where it's disabled again, and fails
35
+ * with `Tool X disabled`. Confirmed by running exactly that sequence
36
+ * against a live gateway — the mutation never survives past the request
37
+ * that made it, so a real orchestrator would search successfully, read
38
+ * a schema, then have the follow-up call rejected. Worse than doing
39
+ * nothing: it looks like it works, right up until the second call.
40
+ *
41
+ * ## What this does instead
42
+ *
43
+ * Every tool stays fully registered AND enabled (`tools/call` always
44
+ * works for any tool name, deferred or not — the daemon is stateless
45
+ * regardless, so there is no session-scoped "have they earned the right
46
+ * to call this" state to enforce even if it were desirable). "Deferred"
47
+ * only means: excluded from the `tools/list` response, so a session
48
+ * doesn't pay the schema-loading token tax for tools it never asked
49
+ * about. The `tools/list` handler is overridden directly on the
50
+ * underlying low-level `Server` (`mcpServer.server.setRequestHandler` —
51
+ * the SDK's own documented escape hatch: "For advanced usage ... use the
52
+ * underlying Server instance") to return only the `alwaysOn` set plus
53
+ * `tool_search` itself; this is a pure per-request read, so it's
54
+ * naturally correct under statelessness — there's no history to lose.
55
+ * `tool_search` matches a keyword (or `select:name1,name2` for exact
56
+ * names) against a catalog of every OTHER registered tool and returns
57
+ * full JSON Schema for the hits directly in its own result text — the
58
+ * one channel that reliably survives the stateless boundary, since it's
59
+ * read from the SAME call that produced it.
60
+ *
61
+ * Opt-in and backward-compatible by construction: nothing calls this
62
+ * unless a host explicitly wires `deferredTools` into `createGateway` —
63
+ * see `index.ts`. Every existing daemon keeps registering (and exposing)
64
+ * everything eagerly, exactly as before.
65
+ *
66
+ * Both SDK registration entry points are intercepted — `server.tool(...)`
67
+ * AND `server.registerTool(...)` (the config-object overload; e.g.
68
+ * mcp-apps-adapter.ts's `agentproto_sessions` panel tool uses the latter,
69
+ * and would silently skip the catalog if only one were patched).
70
+ *
71
+ * Tools registered on the server BEFORE it's wrapped — e.g. `self_inspect`,
72
+ * added inside `@agentproto/mcp-server`'s `createMcpServer()` — are
73
+ * automatically excluded from `tools/list` too: the override only lists
74
+ * names present in `registry` (built from tools that passed through the
75
+ * proxy), so anything registered earlier is simply absent from that set,
76
+ * with no extra bookkeeping needed. They stay fully callable, same as
77
+ * every other tool here — the override only ever touches what `tools/list`
78
+ * shows.
79
+ */
80
+
81
+ /**
82
+ * `~/.agentproto/config.json`'s `defaults.mcp.deferredTools` shape —
83
+ * `false`/absent (the default) keeps every daemon boot eager, byte-identical
84
+ * to pre-existing behaviour. `true` turns deferred tools on gateway-wide
85
+ * using the daemon's own default always-on set (see `DEFAULT_ALWAYS_ON_TOOLS`
86
+ * in index.ts). `{ alwaysOn }` turns it on with a custom always-on set
87
+ * instead of the default one.
88
+ */
89
+ type DeferredToolsConfig = boolean | {
90
+ alwaysOn: string[];
91
+ };
92
+ /**
93
+ * Convert the config-file value (tri-state: off / on-with-default /
94
+ * on-with-custom-set) into the shape `CreateGatewayOptions.deferredTools`
95
+ * expects. Pure — no fs, easy to unit test independent of `loadConfig`.
96
+ * `undefined`/`false` ⇒ `undefined` (omit the key entirely, so
97
+ * `createGateway` never wraps `withDeferredTools` — today's eager
98
+ * behaviour). `true` ⇒ `{}` (deferred, `alwaysOn` left for the gateway's
99
+ * own `DEFAULT_ALWAYS_ON_TOOLS` fallback). `{ alwaysOn }` ⇒ passed through
100
+ * verbatim.
101
+ */
102
+ declare function resolveDeferredToolsGatewayOption(config: DeferredToolsConfig | undefined): {
103
+ alwaysOn?: readonly string[];
104
+ } | undefined;
3
105
 
4
106
  /**
5
107
  * Pure resolver for `~/.agentproto/config.json`'s `defaults` block —
@@ -25,6 +127,22 @@ import { C as ContextContinuityPolicy } from './context-continuity-ib9_bVYM.js';
25
127
  * store — never read from the ambient shell env.
26
128
  */
27
129
 
130
+ /**
131
+ * `defaults.mcp` block — daemon-wide MCP gateway policy, distinct from the
132
+ * per-adapter spawn defaults above (it's read once at daemon boot, not
133
+ * per-spawn — see `serve.ts`'s `createGateway({ deferredTools })` call).
134
+ */
135
+ interface McpDefaultsConfig {
136
+ /** Gateway-wide deferred/lazy `tools/list` loading (harness-parity item
137
+ * 3 — see `deferred-tools.ts`). Default false/absent: every daemon boot
138
+ * stays eager, byte-identical to pre-existing behaviour — turning this
139
+ * on is a deliberate operator opt-in. Independent of the per-role
140
+ * default (`RoleProfile.deferredTools`, on for `executor`) and the
141
+ * per-spawn `agent_start.deferredTools` override, and independent of
142
+ * the per-mount `?deferred=1|0` query override on `/mcp` — this is only
143
+ * the BOOT-TIME default for connections that specify neither. */
144
+ deferredTools?: DeferredToolsConfig;
145
+ }
28
146
  /**
29
147
  * Deterministic billing-auth config for one adapter slug (today, only
30
148
  * claude-code interprets it — see `AgentCliStartOptions.auth` in
@@ -95,6 +213,14 @@ interface SpawnDefaultsConfig {
95
213
  * omits `trace`. Default false — sessions trace only when they opt in or
96
214
  * this is on. See `filterSessionObserver` / `SpawnAgentInput.trace`. */
97
215
  langfuseTracing?: boolean;
216
+ /** Wake an idle agent session when one of its background tasks settles
217
+ * (see `SessionsRegistryOptions.backgroundTaskWake`). On by default;
218
+ * `{ enabled: false }` opts out. `graceMs` is how long the daemon waits
219
+ * for the agent to wake ITSELF (Claude Code does) before prompting it. */
220
+ backgroundTaskWake?: {
221
+ enabled?: boolean;
222
+ graceMs?: number;
223
+ };
98
224
  /** Redactor slug applied to traced session content before it's sent to
99
225
  * Langfuse (see `@agentproto/redaction`'s registry). Default "secrets"
100
226
  * (deny-list by key + value-scan for secret shapes). */
@@ -107,6 +233,24 @@ interface SpawnDefaultsConfig {
107
233
  * EXPLICIT `interrupt` on the call (true OR false) always wins over this
108
234
  * default. */
109
235
  agentPromptInterrupt?: boolean;
236
+ /** Inter-session messaging policy (AIP-46 §Session messages). */
237
+ messaging?: {
238
+ /** Let `message_send` / `message_reply` reach a sibling session (same
239
+ * parent). Default false — only child↔parent. */
240
+ allowSiblings?: boolean;
241
+ /** Whether a SESSION sender may use urgency `interrupt` (cancel the
242
+ * recipient's in-flight turn). Default "deny": downgraded to `steer`
243
+ * and reported as such. Human (HTTP/CLI) senders always may. */
244
+ agentInterrupt?: "allow" | "deny";
245
+ };
246
+ /** Daemon-wide MCP gateway policy. See {@link McpDefaultsConfig}. */
247
+ mcp?: McpDefaultsConfig;
248
+ /** Spawn-shape defaults applied when neither the `agent_start` call, its
249
+ * role nor its user preset says otherwise. */
250
+ spawn?: {
251
+ /** Default `agent_start.browser` (`"headless"` | `false`). Default off. */
252
+ browser?: SpawnBrowserMode;
253
+ };
110
254
  }
111
255
  interface ResolveSpawnDefaultsInput {
112
256
  /** Explicit-call `skills`. Undefined ⇒ caller expressed no preference,
@@ -433,4 +577,4 @@ interface DeclaredAdapterOption {
433
577
  */
434
578
  declare function normalizeSkillsOption(skills: string[], options: Record<string, boolean | number | string>, declaredOptions: readonly DeclaredAdapterOption[] | undefined): Record<string, boolean | number | string>;
435
579
 
436
- export { type AdapterAuthDescriptor as A, CLAUDE_CODE_OAUTH_SOURCE as C, type DeclaredAdapterOption as D, type ResolvedAuthSpec as R, type SpawnDefaultsConfig as S, type AuthEcho as a, AuthResolutionError as b, type CredentialSource as c, type DefaultsAdapterAuthConfig as d, type DefaultsAdapterConfig as e, type ResolveSubscriptionCredentialInput as f, type ResolvedSpawnAuthMaterial as g, type ResolvedSpawnDefaults as h, type SubscriptionCredentialResolution as i, SubscriptionSourceError as j, credentialFingerprint as k, resolveSpawnDefaults as l, resolveSubscriptionCredential as m, normalizeSkillsOption as n, resolveAuthSpec as r };
580
+ export { type AdapterAuthDescriptor as A, CLAUDE_CODE_OAUTH_SOURCE as C, type DefaultsAdapterAuthConfig as D, type ResolvedAuthSpec as R, type SpawnDefaultsConfig as S, type DeclaredAdapterOption as a, type AuthEcho as b, AuthResolutionError as c, type CredentialSource as d, type DefaultsAdapterConfig as e, type DeferredToolsConfig as f, type ResolveSubscriptionCredentialInput as g, type ResolvedSpawnAuthMaterial as h, type ResolvedSpawnDefaults as i, type SubscriptionCredentialResolution as j, SubscriptionSourceError as k, credentialFingerprint as l, resolveDeferredToolsGatewayOption as m, normalizeSkillsOption as n, resolveSpawnDefaults as o, resolveSubscriptionCredential as p, resolveAuthSpec as r };
@@ -1,7 +1,9 @@
1
1
  import { z } from 'zod';
2
- import { S as SessionConfig, R as RouteSpec, P as Posture, E as EffortLevel, C as ContextProfile } from './session-config-DbWP9RRj.js';
2
+ import { S as SessionConfig, R as RouteSpec, P as Posture, E as EffortLevel, C as ContextProfile } from './session-config-ByfVeEQJ.js';
3
+ import { S as SpawnBrowserMode } from './context-continuity-Bn99UtVC.js';
3
4
  import '@agentproto/auth';
4
- import './context-continuity-ib9_bVYM.js';
5
+ import '@agentproto/acp';
6
+ import '@agentproto/command-sandbox';
5
7
 
6
8
  /**
7
9
  * User-owned spawn presets.
@@ -38,6 +40,9 @@ interface UserPreset extends Partial<SessionConfig> {
38
40
  * `SpawnAgentSessionInput.skills`. Omitted means the adapter/defaults
39
41
  * decide. */
40
42
  skills?: string[];
43
+ /** `agent_start.browser` for spawns from this preset. Ranks below the
44
+ * role's own default (see `resolveBrowserMode`). */
45
+ browser?: SpawnBrowserMode;
41
46
  }
42
47
  declare const userPresetsFileSchema: z.ZodObject<{
43
48
  version: z.ZodLiteral<1>;
@@ -74,6 +79,7 @@ declare const userPresetsFileSchema: z.ZodObject<{
74
79
  contextProfile: z.ZodOptional<z.ZodString>;
75
80
  cwd: z.ZodOptional<z.ZodString>;
76
81
  skills: z.ZodOptional<z.ZodArray<z.ZodString>>;
82
+ browser: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"headless">, z.ZodLiteral<false>]>>;
77
83
  }, z.core.$strip>>;
78
84
  }, z.core.$strip>;
79
85
  type UserPresetsFile = z.infer<typeof userPresetsFileSchema>;
@@ -29,7 +29,8 @@ var userPresetSchema = z.object({
29
29
  effort: effortSchema.optional(),
30
30
  contextProfile: z.string().min(1).optional(),
31
31
  cwd: z.string().min(1).optional(),
32
- skills: z.array(z.string().min(1)).optional()
32
+ skills: z.array(z.string().min(1)).optional(),
33
+ browser: z.union([z.literal("headless"), z.literal(false)]).optional()
33
34
  });
34
35
  var userPresetsFileSchema = z.object({
35
36
  version: z.literal(1),
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/user-presets.ts"],"names":[],"mappings":"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","file":"user-presets.mjs","sourcesContent":["/**\n * User-owned spawn presets.\n *\n * This is deliberately separate from `preset-tools.ts`: provider presets are\n * static gateway definitions shipped by packages, while a UserPreset is a\n * private saved combination of the orthogonal session-config axes.\n */\n\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\"\nimport { homedir } from \"node:os\"\nimport { join, resolve } from \"node:path\"\nimport { z } from \"zod\"\nimport type {\n ContextProfile,\n EffortLevel,\n Posture,\n RouteSpec,\n SessionConfig,\n} from \"./session-config.js\"\n\nconst effortSchema = z.enum([\"low\", \"medium\", \"high\", \"xhigh\", \"max\", \"ultracode\"])\nconst postureSchema = z.union([\n z.enum([\"default\", \"plan\", \"accept-edits\", \"bypass\", \"read-only\"]),\n z.object({ harnessModeId: z.string().min(1) }),\n])\nconst routeSchema = z.object({\n gateway: z.string().min(1),\n baseUrl: z.string().url().optional(),\n})\n\n/** A reusable, user-scoped subset of the spawn/session config axes. */\nexport interface UserPreset extends Partial<SessionConfig> {\n /** Stable machine-local id, e.g. `fast-deepseek`. */\n id: string\n /** Human-readable name shown by CLI and editor pickers. */\n label: string\n /** Adapter harness to use. Omitted means the caller selects one. */\n adapter?: string\n /** Canonical harness slug — alias for `adapter`. */\n harness?: string\n model?: string\n route?: RouteSpec\n access?: { profileRef?: string }\n posture?: Posture\n effort?: EffortLevel\n contextProfile?: ContextProfile\n /** Working directory the favorite pins to. When set, a spawn from this\n * preset lands here regardless of the caller's active folder — the axis\n * that makes a favorite fully location-pinned (true zero-input). Omitted\n * means the caller's cwd ladder resolves it as before. */\n cwd?: string\n /** Skills to preload for a spawn from this preset — the same axis as\n * `SpawnAgentSessionInput.skills`. Omitted means the adapter/defaults\n * decide. */\n skills?: string[]\n}\n\nconst userPresetSchema = z.object({\n id: z.string().regex(/^[a-z0-9][a-z0-9-]*$/),\n label: z.string().min(1),\n adapter: z.string().min(1).optional(),\n harness: z.string().min(1).optional(),\n model: z.string().min(1).optional(),\n route: routeSchema.optional(),\n access: z.object({ profileRef: z.string().min(1).optional() }).optional(),\n posture: postureSchema.optional(),\n effort: effortSchema.optional(),\n contextProfile: z.string().min(1).optional(),\n cwd: z.string().min(1).optional(),\n skills: z.array(z.string().min(1)).optional(),\n}) satisfies z.ZodType<UserPreset>\n\nconst userPresetsFileSchema = z.object({\n version: z.literal(1),\n presets: z.array(userPresetSchema),\n})\n\nexport type UserPresetsFile = z.infer<typeof userPresetsFileSchema>\n\nfunction emptyFile(): UserPresetsFile {\n return { version: 1, presets: [] }\n}\n\nexport function userPresetsPath(): string {\n return resolve(homedir(), \".agentproto\", \"presets.json\")\n}\n\n/** Missing or malformed user config is treated as empty — a bad preset must\n * never prevent the daemon from starting. Writes always restore valid JSON. */\nexport async function loadUserPresets(): Promise<UserPresetsFile> {\n try {\n return userPresetsFileSchema.parse(JSON.parse(await readFile(userPresetsPath(), \"utf8\")))\n } catch {\n return emptyFile()\n }\n}\n\nasync function writeUserPresets(file: UserPresetsFile): Promise<void> {\n const dir = join(homedir(), \".agentproto\")\n await mkdir(dir, { recursive: true })\n await writeFile(userPresetsPath(), JSON.stringify(file, null, 2) + \"\\n\", {\n encoding: \"utf8\",\n mode: 0o600,\n })\n}\n\nexport async function listUserPresets(): Promise<UserPreset[]> {\n return (await loadUserPresets()).presets\n}\n\nexport async function getUserPreset(id: string): Promise<UserPreset | undefined> {\n return (await loadUserPresets()).presets.find(preset => preset.id === id)\n}\n\n/** Add or replace a preset by id. The parser makes this the single validation\n * boundary for CLI, MCP and editor callers. */\nexport async function saveUserPreset(preset: UserPreset): Promise<void> {\n const validated = userPresetSchema.parse(preset)\n const file = await loadUserPresets()\n const index = file.presets.findIndex(existing => existing.id === validated.id)\n if (index === -1) file.presets.push(validated)\n else file.presets[index] = validated\n await writeUserPresets(file)\n}\n\nexport async function deleteUserPreset(id: string): Promise<boolean> {\n const file = await loadUserPresets()\n const index = file.presets.findIndex(preset => preset.id === id)\n if (index === -1) return false\n file.presets.splice(index, 1)\n await writeUserPresets(file)\n return true\n}\n"]}
1
+ {"version":3,"sources":["../src/user-presets.ts"],"names":[],"mappings":"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","file":"user-presets.mjs","sourcesContent":["/**\n * User-owned spawn presets.\n *\n * This is deliberately separate from `preset-tools.ts`: provider presets are\n * static gateway definitions shipped by packages, while a UserPreset is a\n * private saved combination of the orthogonal session-config axes.\n */\n\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\"\nimport { homedir } from \"node:os\"\nimport { join, resolve } from \"node:path\"\nimport { z } from \"zod\"\nimport type {\n ContextProfile,\n EffortLevel,\n Posture,\n RouteSpec,\n SessionConfig,\n} from \"./session-config.js\"\nimport type { SpawnBrowserMode } from \"./browser-mount.js\"\n\nconst effortSchema = z.enum([\"low\", \"medium\", \"high\", \"xhigh\", \"max\", \"ultracode\"])\nconst postureSchema = z.union([\n z.enum([\"default\", \"plan\", \"accept-edits\", \"bypass\", \"read-only\"]),\n z.object({ harnessModeId: z.string().min(1) }),\n])\nconst routeSchema = z.object({\n gateway: z.string().min(1),\n baseUrl: z.string().url().optional(),\n})\n\n/** A reusable, user-scoped subset of the spawn/session config axes. */\nexport interface UserPreset extends Partial<SessionConfig> {\n /** Stable machine-local id, e.g. `fast-deepseek`. */\n id: string\n /** Human-readable name shown by CLI and editor pickers. */\n label: string\n /** Adapter harness to use. Omitted means the caller selects one. */\n adapter?: string\n /** Canonical harness slug — alias for `adapter`. */\n harness?: string\n model?: string\n route?: RouteSpec\n access?: { profileRef?: string }\n posture?: Posture\n effort?: EffortLevel\n contextProfile?: ContextProfile\n /** Working directory the favorite pins to. When set, a spawn from this\n * preset lands here regardless of the caller's active folder — the axis\n * that makes a favorite fully location-pinned (true zero-input). Omitted\n * means the caller's cwd ladder resolves it as before. */\n cwd?: string\n /** Skills to preload for a spawn from this preset — the same axis as\n * `SpawnAgentSessionInput.skills`. Omitted means the adapter/defaults\n * decide. */\n skills?: string[]\n /** `agent_start.browser` for spawns from this preset. Ranks below the\n * role's own default (see `resolveBrowserMode`). */\n browser?: SpawnBrowserMode\n}\n\nconst userPresetSchema = z.object({\n id: z.string().regex(/^[a-z0-9][a-z0-9-]*$/),\n label: z.string().min(1),\n adapter: z.string().min(1).optional(),\n harness: z.string().min(1).optional(),\n model: z.string().min(1).optional(),\n route: routeSchema.optional(),\n access: z.object({ profileRef: z.string().min(1).optional() }).optional(),\n posture: postureSchema.optional(),\n effort: effortSchema.optional(),\n contextProfile: z.string().min(1).optional(),\n cwd: z.string().min(1).optional(),\n skills: z.array(z.string().min(1)).optional(),\n browser: z.union([z.literal(\"headless\"), z.literal(false)]).optional(),\n}) satisfies z.ZodType<UserPreset>\n\nconst userPresetsFileSchema = z.object({\n version: z.literal(1),\n presets: z.array(userPresetSchema),\n})\n\nexport type UserPresetsFile = z.infer<typeof userPresetsFileSchema>\n\nfunction emptyFile(): UserPresetsFile {\n return { version: 1, presets: [] }\n}\n\nexport function userPresetsPath(): string {\n return resolve(homedir(), \".agentproto\", \"presets.json\")\n}\n\n/** Missing or malformed user config is treated as empty — a bad preset must\n * never prevent the daemon from starting. Writes always restore valid JSON. */\nexport async function loadUserPresets(): Promise<UserPresetsFile> {\n try {\n return userPresetsFileSchema.parse(JSON.parse(await readFile(userPresetsPath(), \"utf8\")))\n } catch {\n return emptyFile()\n }\n}\n\nasync function writeUserPresets(file: UserPresetsFile): Promise<void> {\n const dir = join(homedir(), \".agentproto\")\n await mkdir(dir, { recursive: true })\n await writeFile(userPresetsPath(), JSON.stringify(file, null, 2) + \"\\n\", {\n encoding: \"utf8\",\n mode: 0o600,\n })\n}\n\nexport async function listUserPresets(): Promise<UserPreset[]> {\n return (await loadUserPresets()).presets\n}\n\nexport async function getUserPreset(id: string): Promise<UserPreset | undefined> {\n return (await loadUserPresets()).presets.find(preset => preset.id === id)\n}\n\n/** Add or replace a preset by id. The parser makes this the single validation\n * boundary for CLI, MCP and editor callers. */\nexport async function saveUserPreset(preset: UserPreset): Promise<void> {\n const validated = userPresetSchema.parse(preset)\n const file = await loadUserPresets()\n const index = file.presets.findIndex(existing => existing.id === validated.id)\n if (index === -1) file.presets.push(validated)\n else file.presets[index] = validated\n await writeUserPresets(file)\n}\n\nexport async function deleteUserPreset(id: string): Promise<boolean> {\n const file = await loadUserPresets()\n const index = file.presets.findIndex(preset => preset.id === id)\n if (index === -1) return false\n file.presets.splice(index, 1)\n await writeUserPresets(file)\n return true\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentproto/runtime",
3
- "version": "3.6.1",
3
+ "version": "4.0.0",
4
4
  "description": "@agentproto/runtime — long-running gateway that turns an agentproto workspace into a live runtime. Composes @agentproto/mcp-server (CRUD verbs) with HTTP transport, HEARTBEAT.md autonomy loop, and append-only conversation persistence. Drop a workspace dir, point your MCP client at it, and the agent ticks on its own.",
5
5
  "keywords": [
6
6
  "agentproto",
@@ -71,6 +71,11 @@
71
71
  "import": "./dist/config.mjs",
72
72
  "default": "./dist/config.mjs"
73
73
  },
74
+ "./config-schema": {
75
+ "types": "./dist/config-schema.d.ts",
76
+ "import": "./dist/config-schema.mjs",
77
+ "default": "./dist/config-schema.mjs"
78
+ },
74
79
  "./providers-store": {
75
80
  "types": "./dist/providers-store.d.ts",
76
81
  "import": "./dist/providers-store.mjs",
@@ -115,6 +120,11 @@
115
120
  "types": "./dist/tool-envelope.d.ts",
116
121
  "import": "./dist/tool-envelope.mjs",
117
122
  "default": "./dist/tool-envelope.mjs"
123
+ },
124
+ "./app-ui-delivery": {
125
+ "types": "./dist/app-ui-delivery.d.ts",
126
+ "import": "./dist/app-ui-delivery.mjs",
127
+ "default": "./dist/app-ui-delivery.mjs"
118
128
  }
119
129
  },
120
130
  "files": [
@@ -126,42 +136,45 @@
126
136
  "access": "public"
127
137
  },
128
138
  "dependencies": {
139
+ "@modelcontextprotocol/ext-apps": "1.7.5",
129
140
  "@modelcontextprotocol/sdk": "^1.30.0",
130
141
  "ai": "^6.0.277",
131
142
  "croner": "^9.1.0",
132
143
  "gray-matter": "^4.0.3",
133
144
  "ws": "^8.21.3",
134
145
  "zod": "^4.6.5",
135
- "@agentproto/acp": "0.8.2",
146
+ "@agentproto/acp": "0.9.0",
136
147
  "@agentproto/agent": "0.2.4",
137
- "@agentproto/app-client": "0.3.3",
138
- "@agentproto/app-kit": "1.2.1",
139
- "@agentproto/apps": "0.12.0",
140
- "@agentproto/auth": "1.0.3",
148
+ "@agentproto/app-client": "0.4.0",
149
+ "@agentproto/app-kit": "1.3.0",
150
+ "@agentproto/apps": "0.13.0",
151
+ "@agentproto/auth": "1.1.0",
152
+ "@agentproto/driver": "0.2.4",
141
153
  "@agentproto/command-sandbox": "0.2.2",
142
- "@agentproto/driver": "0.2.3",
143
- "@agentproto/driver-agent-cli": "2.4.4",
144
- "@agentproto/driver-http": "0.1.7",
145
- "@agentproto/eval-reporters": "0.2.15",
154
+ "@agentproto/driver-agent-cli": "2.5.0",
155
+ "@agentproto/driver-http": "0.1.8",
156
+ "@agentproto/eval-reporters": "0.2.16",
146
157
  "@agentproto/manifest": "0.2.2",
147
- "@agentproto/mcp-server": "0.3.1",
148
- "@agentproto/model-catalog": "0.10.3",
158
+ "@agentproto/mcp-server": "0.4.0",
159
+ "@agentproto/model-catalog": "0.11.0",
160
+ "@agentproto/plugin-local-browser": "0.3.0",
149
161
  "@agentproto/provider-kit": "0.4.5",
150
162
  "@agentproto/provider-presets": "0.7.0",
151
- "@agentproto/providers-store": "0.3.17",
163
+ "@agentproto/providers-store": "0.3.18",
152
164
  "@agentproto/redaction": "0.2.2",
153
- "@agentproto/routine": "0.2.3",
154
- "@agentproto/sandbox": "0.5.3",
155
- "@agentproto/secrets": "0.2.6",
156
- "@agentproto/telemetry-langfuse": "0.2.13",
165
+ "@agentproto/routine": "0.3.0",
166
+ "@agentproto/review": "0.2.0",
167
+ "@agentproto/secrets": "1.0.0",
168
+ "@agentproto/telemetry-langfuse": "0.2.14",
157
169
  "@agentproto/tool": "0.3.1",
158
- "@agentproto/workflow": "0.6.1",
159
- "@agentproto/workflow-loader": "0.2.3",
160
- "@agentproto/workflow-runtime": "0.12.0",
161
- "@agentproto/workspace-brain": "0.4.7"
170
+ "@agentproto/workflow-loader": "0.2.4",
171
+ "@agentproto/workflow": "0.7.0",
172
+ "@agentproto/workspace-brain": "0.4.8",
173
+ "@agentproto/workflow-runtime": "0.13.0",
174
+ "@agentproto/sandbox": "0.6.0"
162
175
  },
163
176
  "optionalDependencies": {
164
- "@agentproto/sandbox-e2b": "0.5.3"
177
+ "@agentproto/sandbox-e2b": "0.5.4"
165
178
  },
166
179
  "devDependencies": {
167
180
  "@types/node": "^25.9.5",
@@ -172,7 +185,7 @@
172
185
  "vitest": "^3.2.7",
173
186
  "@agentproto/rendezvous": "0.2.3",
174
187
  "@agentproto/tooling": "0.1.0",
175
- "@agentproto/transcript-fixtures": "0.2.1"
188
+ "@agentproto/transcript-fixtures": "0.3.0"
176
189
  },
177
190
  "scripts": {
178
191
  "dev": "tsup --watch",