@byok-sdk/client 0.10.1 → 0.11.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.
Files changed (48) hide show
  1. package/README.md +71 -3
  2. package/dist/adapters/claude/claude-adapter.d.ts +3 -2
  3. package/dist/adapters/claude/permission-mapping.d.ts +48 -1
  4. package/dist/adapters/claude/resolve-approval-mcp-bin.d.ts +3 -2
  5. package/dist/adapters/codex/permission-mapping.d.ts +16 -0
  6. package/dist/adapters/index.js +292 -23
  7. package/dist/adapters/index.js.map +1 -1
  8. package/dist/adapters/mcp-tool-grants.d.ts +36 -0
  9. package/dist/adapters/pi/resolve-extensions.d.ts +4 -1
  10. package/dist/adapters/pi/subagents-policy-config.d.ts +7 -0
  11. package/dist/adapters/pi/subagents-policy-extension.js +137 -0
  12. package/dist/adapters/pi/subagents-policy-extension.js.map +1 -0
  13. package/dist/agent-memory/index.d.ts +1 -1
  14. package/dist/agent-memory/index.js +4 -4
  15. package/dist/agent-memory/index.js.map +1 -1
  16. package/dist/bin/agent-memory-mcp-server.d.ts +2 -2
  17. package/dist/bin/agent-message-mcp-server.d.ts +0 -1
  18. package/dist/bin/byok-agent-memory-mcp.js +340 -7
  19. package/dist/bin/byok-agent-memory-mcp.js.map +1 -1
  20. package/dist/bin/byok-agent-message-mcp.js +331 -7
  21. package/dist/bin/byok-agent-message-mcp.js.map +1 -1
  22. package/dist/bin/byok-agent-team-mcp.d.ts +2 -0
  23. package/dist/bin/byok-agent-team-mcp.js +765 -0
  24. package/dist/bin/byok-agent-team-mcp.js.map +1 -0
  25. package/dist/bin/byok-agent.js +15889 -14610
  26. package/dist/bin/byok-agent.js.map +1 -1
  27. package/dist/bin/byok-approval-mcp.js +312 -45
  28. package/dist/bin/byok-approval-mcp.js.map +1 -1
  29. package/dist/bin/commands/team.d.ts +15 -0
  30. package/dist/bin/sdk-reserved-helper-runners.d.ts +2 -0
  31. package/dist/bin/team-mcp-server.d.ts +27 -0
  32. package/dist/bin/team-tmux-view.d.ts +19 -0
  33. package/dist/daemon/agent-message-mcp-preflight.d.ts +17 -0
  34. package/dist/daemon/control-protocol.d.ts +38 -0
  35. package/dist/daemon/create-daemon.d.ts +7 -0
  36. package/dist/daemon/mcp-tools-probe.d.ts +105 -0
  37. package/dist/daemon/resolve-agent-memory-mcp-bin.d.ts +2 -1
  38. package/dist/daemon/resolve-agent-message-mcp-bin.d.ts +2 -1
  39. package/dist/daemon/task-runner.d.ts +18 -1
  40. package/dist/daemon/team-workspace.d.ts +202 -0
  41. package/dist/daemon/toolset-registry.d.ts +0 -2
  42. package/dist/index.d.ts +4 -0
  43. package/dist/index.js +2682 -1090
  44. package/dist/index.js.map +1 -1
  45. package/dist/sdk-reserved-helper-host.d.ts +25 -0
  46. package/dist/sdk-reserved-mcp.d.ts +23 -0
  47. package/dist/types.d.ts +36 -0
  48. package/package.json +10 -5
@@ -0,0 +1,36 @@
1
+ import type { McpStdioServerConfig, McpToolsetToolObservation } from '../types';
2
+ /** One projected toolset server and the exact tool names observed on it. */
3
+ export interface McpToolsetGrant {
4
+ readonly server: string;
5
+ readonly tools: readonly string[];
6
+ }
7
+ /** Static grant candidates for exactly the SDK-owned pre-grantable servers present in one task. */
8
+ export declare function resolveReservedMcpToolGrants(servers: Readonly<Record<string, McpStdioServerConfig>> | undefined): readonly McpToolsetGrant[];
9
+ export type McpToolsetGrantResolution = {
10
+ ok: true;
11
+ grants: readonly McpToolsetGrant[];
12
+ } | {
13
+ ok: false;
14
+ reason: string;
15
+ };
16
+ /**
17
+ * Pair every projected (non-reserved) MCP server with the tool names the
18
+ * daemon actually observed on it, or fail closed.
19
+ *
20
+ * Both runtimes this SDK drives non-interactively refuse an MCP tool call
21
+ * that was not pre-granted — claude auto-denies it under `--permission-mode
22
+ * default` and under `acceptEdits`, codex rejects it under
23
+ * `approval_policy=never` — so a projected toolset is only genuinely usable
24
+ * if the adapter can name its tools in the runtime's own grant surface. That
25
+ * makes an unobserved server an inexpressible policy, not a smaller one: the
26
+ * task would claim, spawn a model, and then be told it may not call the very
27
+ * tools it was offered for. Rejecting before spawn is the same fail-closed
28
+ * posture the permission mappers already take for an inexpressible
29
+ * `denyTools` or `network` constraint.
30
+ *
31
+ * Reserved SDK servers are deliberately absent from the result: each carries
32
+ * a fixed grant its own protocol defines, never an observed one.
33
+ */
34
+ export declare function resolveMcpToolsetGrants(servers: Readonly<Record<string, McpStdioServerConfig>> | undefined, observation: McpToolsetToolObservation | undefined): McpToolsetGrantResolution;
35
+ /** Order-independent identity of one grant set, for adapters that must prove start() received the authority prepare() was admitted with. */
36
+ export declare function grantFingerprint(grants: readonly McpToolsetGrant[]): string;
@@ -1,9 +1,12 @@
1
1
  export interface ResolvedPiExtensions {
2
2
  readonly webAccess: string;
3
3
  readonly mcpAdapter: string;
4
+ readonly subagentsPolicy: string;
5
+ readonly subagents: string;
6
+ readonly todo: string;
4
7
  }
5
8
  /**
6
- * Resolve the two Pi extensions shipped as required `@byok-sdk/client`
9
+ * Resolve the Pi extensions shipped as required `@byok-sdk/client`
7
10
  * dependencies. Pi receives explicit extension paths so runtime behavior is
8
11
  * pinned to this package graph rather than a user's mutable global Pi package
9
12
  * settings.
@@ -0,0 +1,7 @@
1
+ export declare const BYOK_PI_PERMISSION_MODE = "BYOK_PI_PERMISSION_MODE";
2
+ /** SDK-owned extension tools that do not mutate the task workspace. */
3
+ export declare const BYOK_PI_READONLY_PARENT_TOOLS: readonly ['subagent', 'todo'];
4
+ /** Child tools retained when a readonly parent delegates through pi-subagents. */
5
+ export declare const BYOK_PI_READONLY_SUBAGENT_TOOLS: readonly ['read', 'grep', 'find', 'ls'];
6
+ /** Package-provided read-only roles; writer and ambient custom roles stay unavailable. */
7
+ export declare const BYOK_PI_READONLY_SUBAGENT_AGENTS: readonly ['reviewer', 'oracle', 'codex-exec', 'claude-code', 'cursor-agent'];
@@ -0,0 +1,137 @@
1
+ import { Buffer } from 'buffer';
2
+
3
+ // ../../node_modules/.bun/pi-subagents@0.60.0+ba57dcb6e433cb73/node_modules/pi-subagents/src/runs/shared/capability-ceiling.ts
4
+ var SUBAGENT_CAPABILITY_CEILING_VERSION = 1;
5
+ var SUBAGENT_CAPABILITY_CEILING_REGISTRY_KEY = "pi-subagents.capability-ceiling.v1";
6
+ function registry() {
7
+ const key = Symbol.for(SUBAGENT_CAPABILITY_CEILING_REGISTRY_KEY);
8
+ const store = globalThis;
9
+ const existing = store[key];
10
+ if (existing instanceof Map) return existing;
11
+ const created = /* @__PURE__ */ new Map();
12
+ store[key] = created;
13
+ return created;
14
+ }
15
+ function validateText(value, field) {
16
+ if (typeof value !== "string" || !value.trim() || /[\u0000-\u001f\u007f]/u.test(value) || Buffer.byteLength(value.trim(), "utf8") > 256) {
17
+ throw new Error(`Invalid capability ceiling ${field}; expected a non-empty string without control characters (max 256 UTF-8 bytes).`);
18
+ }
19
+ return value.trim();
20
+ }
21
+ function normalizeCeiling(ceiling) {
22
+ if (!ceiling || typeof ceiling !== "object" || Array.isArray(ceiling)) throw new Error("Invalid capability ceiling; expected an object.");
23
+ const hasAllowedTools = Object.hasOwn(ceiling, "allowedTools");
24
+ const hasAllowedAgents = Object.hasOwn(ceiling, "allowedAgents");
25
+ const hasDenyExtensions = Object.hasOwn(ceiling, "denyExtensions");
26
+ if (!hasAllowedTools && !hasAllowedAgents && !hasDenyExtensions) throw new Error("Invalid capability ceiling; expected allowedTools, allowedAgents, or denyExtensions.");
27
+ if (hasDenyExtensions && typeof ceiling.denyExtensions !== "boolean") throw new Error("Invalid capability ceiling denyExtensions; expected a boolean.");
28
+ const normalizeList = (field, pattern) => {
29
+ if (!Object.hasOwn(ceiling, field)) return void 0;
30
+ const values = ceiling[field];
31
+ if (!Array.isArray(values)) throw new Error(`Invalid capability ceiling ${field}; expected an array.`);
32
+ if (values.length > 256) throw new Error(`Invalid capability ceiling ${field}; expected at most 256 names.`);
33
+ return [...new Set(values.map((entry) => {
34
+ const name = validateText(entry, `${field} entry`);
35
+ if (!pattern.test(name)) throw new Error(`Invalid capability ceiling ${field} entry '${name}'.`);
36
+ if (Buffer.byteLength(name, "utf8") > 128) throw new Error(`Invalid capability ceiling ${field} entry '${name}'; max 128 UTF-8 bytes.`);
37
+ return name;
38
+ }))].sort();
39
+ };
40
+ const allowedTools = normalizeList("allowedTools", /^[A-Za-z0-9_.:-]+$/u);
41
+ const allowedAgents = normalizeList("allowedAgents", /^[A-Za-z0-9_.:-]+$/u);
42
+ return {
43
+ version: SUBAGENT_CAPABILITY_CEILING_VERSION,
44
+ ...allowedTools !== void 0 ? { allowedTools } : {},
45
+ ...allowedAgents !== void 0 ? { allowedAgents } : {},
46
+ denyExtensions: ceiling.denyExtensions === true,
47
+ sources: []
48
+ };
49
+ }
50
+ function registerSubagentCapabilityCeiling(options) {
51
+ const sessionId = validateText(options.sessionId, "sessionId");
52
+ const source = validateText(options.source, "source");
53
+ let normalized = normalizeCeiling(options.ceiling);
54
+ const token = Symbol(source);
55
+ const store = registry();
56
+ let session = store.get(sessionId);
57
+ if (!session) {
58
+ session = /* @__PURE__ */ new Map();
59
+ store.set(sessionId, session);
60
+ }
61
+ const setRegistration = () => {
62
+ normalized = normalizeCeiling(normalized);
63
+ normalized.sources = [source];
64
+ session.set(token, { source, ceiling: normalized });
65
+ };
66
+ setRegistration();
67
+ let disposed = false;
68
+ return {
69
+ update(ceiling) {
70
+ if (disposed) throw new Error("Cannot update a disposed capability ceiling handle.");
71
+ normalized = normalizeCeiling(ceiling);
72
+ normalized.sources = [source];
73
+ session.set(token, { source, ceiling: normalized });
74
+ },
75
+ dispose() {
76
+ if (disposed) return;
77
+ disposed = true;
78
+ session.delete(token);
79
+ if (session.size === 0) store.delete(sessionId);
80
+ }
81
+ };
82
+ }
83
+
84
+ // src/adapters/pi/subagents-policy-config.ts
85
+ var BYOK_PI_PERMISSION_MODE = "BYOK_PI_PERMISSION_MODE";
86
+ var BYOK_PI_READONLY_SUBAGENT_TOOLS = ["read", "grep", "find", "ls"];
87
+ var BYOK_PI_READONLY_SUBAGENT_AGENTS = [
88
+ "reviewer",
89
+ "oracle",
90
+ "codex-exec",
91
+ "claude-code",
92
+ "cursor-agent"
93
+ ];
94
+
95
+ // src/adapters/pi/subagents-policy-extension.ts
96
+ function permissionMode() {
97
+ const mode = process.env[BYOK_PI_PERMISSION_MODE];
98
+ if (mode === "auto" || mode === "readonly") return mode;
99
+ throw new Error(`${BYOK_PI_PERMISSION_MODE} must be "auto" or "readonly"`);
100
+ }
101
+ function registerByokSubagentsPolicy(pi) {
102
+ const mode = permissionMode();
103
+ let ceiling;
104
+ pi.on("session_start", (_event, ctx) => {
105
+ ceiling?.dispose();
106
+ ceiling = void 0;
107
+ if (mode === "auto") return;
108
+ const sessionId = ctx.sessionManager.getSessionId();
109
+ if (!sessionId) throw new Error("readonly Pi subagent policy requires an authoritative session id");
110
+ ceiling = registerSubagentCapabilityCeiling({
111
+ sessionId,
112
+ source: "byok-sdk-readonly",
113
+ ceiling: {
114
+ allowedTools: BYOK_PI_READONLY_SUBAGENT_TOOLS,
115
+ allowedAgents: BYOK_PI_READONLY_SUBAGENT_AGENTS,
116
+ denyExtensions: true
117
+ }
118
+ });
119
+ });
120
+ pi.on("tool_call", (event) => {
121
+ if (mode === "readonly" && event.toolName === "subagent" && ceiling === void 0) {
122
+ return {
123
+ block: true,
124
+ terminate: true,
125
+ reason: "readonly Pi subagent capability ceiling is unavailable"
126
+ };
127
+ }
128
+ });
129
+ pi.on("session_shutdown", () => {
130
+ ceiling?.dispose();
131
+ ceiling = void 0;
132
+ });
133
+ }
134
+
135
+ export { registerByokSubagentsPolicy as default };
136
+ //# sourceMappingURL=subagents-policy-extension.js.map
137
+ //# sourceMappingURL=subagents-policy-extension.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../node_modules/.bun/pi-subagents@0.60.0+ba57dcb6e433cb73/node_modules/pi-subagents/src/runs/shared/capability-ceiling.ts","../../../src/adapters/pi/subagents-policy-config.ts","../../../src/adapters/pi/subagents-policy-extension.ts"],"names":[],"mappings":"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","file":"subagents-policy-extension.js","sourcesContent":["import { Buffer } from \"node:buffer\";\n\nexport const SUBAGENT_CAPABILITY_CEILING_VERSION = 1 as const;\nexport const SUBAGENT_CAPABILITY_CEILING_REGISTRY_KEY = \"pi-subagents.capability-ceiling.v1\";\nexport const SUBAGENT_CAPABILITY_CEILING_ENV = \"PI_SUBAGENT_CAPABILITY_CEILING_V1\";\n\nexport type SubagentCapabilityCeiling =\n\t| { allowedTools: readonly string[]; allowedAgents?: readonly string[]; denyExtensions?: boolean }\n\t| { allowedTools?: readonly string[]; allowedAgents?: readonly string[]; denyExtensions: boolean }\n\t| { allowedTools?: readonly string[]; allowedAgents: readonly string[]; denyExtensions?: boolean };\n\nexport interface ResolvedSubagentCapabilityCeiling {\n\tversion: typeof SUBAGENT_CAPABILITY_CEILING_VERSION;\n\tallowedTools?: string[];\n\tallowedAgents?: string[];\n\tdenyExtensions: boolean;\n\tsources: string[];\n}\n\nexport interface SubagentCapabilityAudit {\n\tceiling: ResolvedSubagentCapabilityCeiling;\n\trequestedTools?: string[];\n\teffectiveTools: string[];\n\tremovedTools: string[];\n\tinternalTools: string[];\n\textensionsDenied: boolean;\n\tremovedExtensionCount: number;\n\trequestedMcpToolCount: number;\n\teffectiveMcpTools: string[];\n\tagentAllowed: boolean;\n\tagentRestrictionSources?: string[];\n}\n\nexport interface RegisterSubagentCapabilityCeilingOptions {\n\tsessionId: string;\n\tsource: string;\n\tceiling: SubagentCapabilityCeiling;\n}\n\nexport interface SubagentCapabilityCeilingHandle {\n\tupdate(ceiling: SubagentCapabilityCeiling): void;\n\tdispose(): void;\n}\n\ntype Registration = { source: string; ceiling: ResolvedSubagentCapabilityCeiling };\ntype Registry = Map<string, Map<symbol, Registration>>;\n\nfunction registry(): Registry {\n\tconst key = Symbol.for(SUBAGENT_CAPABILITY_CEILING_REGISTRY_KEY);\n\tconst store = globalThis as typeof globalThis & { [key: symbol]: unknown };\n\tconst existing = store[key];\n\tif (existing instanceof Map) return existing as Registry;\n\tconst created: Registry = new Map();\n\tstore[key] = created;\n\treturn created;\n}\n\nfunction validateText(value: unknown, field: string): string {\n\tif (typeof value !== \"string\" || !value.trim() || /[\\u0000-\\u001f\\u007f]/u.test(value) || Buffer.byteLength(value.trim(), \"utf8\") > 256) {\n\t\tthrow new Error(`Invalid capability ceiling ${field}; expected a non-empty string without control characters (max 256 UTF-8 bytes).`);\n\t}\n\treturn value.trim();\n}\n\nfunction normalizeCeiling(ceiling: SubagentCapabilityCeiling): ResolvedSubagentCapabilityCeiling {\n\tif (!ceiling || typeof ceiling !== \"object\" || Array.isArray(ceiling)) throw new Error(\"Invalid capability ceiling; expected an object.\");\n\tconst hasAllowedTools = Object.hasOwn(ceiling, \"allowedTools\");\n\tconst hasAllowedAgents = Object.hasOwn(ceiling, \"allowedAgents\");\n\tconst hasDenyExtensions = Object.hasOwn(ceiling, \"denyExtensions\");\n\tif (!hasAllowedTools && !hasAllowedAgents && !hasDenyExtensions) throw new Error(\"Invalid capability ceiling; expected allowedTools, allowedAgents, or denyExtensions.\");\n\tif (hasDenyExtensions && typeof ceiling.denyExtensions !== \"boolean\") throw new Error(\"Invalid capability ceiling denyExtensions; expected a boolean.\");\n\tconst normalizeList = (field: \"allowedTools\" | \"allowedAgents\", pattern: RegExp): string[] | undefined => {\n\t\tif (!Object.hasOwn(ceiling, field)) return undefined;\n\t\tconst values = ceiling[field];\n\t\tif (!Array.isArray(values)) throw new Error(`Invalid capability ceiling ${field}; expected an array.`);\n\t\tif (values.length > 256) throw new Error(`Invalid capability ceiling ${field}; expected at most 256 names.`);\n\t\treturn [...new Set(values.map((entry) => {\n\t\t\tconst name = validateText(entry, `${field} entry`);\n\t\t\tif (!pattern.test(name)) throw new Error(`Invalid capability ceiling ${field} entry '${name}'.`);\n\t\t\tif (Buffer.byteLength(name, \"utf8\") > 128) throw new Error(`Invalid capability ceiling ${field} entry '${name}'; max 128 UTF-8 bytes.`);\n\t\t\treturn name;\n\t\t}))].sort();\n\t};\n\tconst allowedTools = normalizeList(\"allowedTools\", /^[A-Za-z0-9_.:-]+$/u);\n\tconst allowedAgents = normalizeList(\"allowedAgents\", /^[A-Za-z0-9_.:-]+$/u);\n\treturn {\n\t\tversion: SUBAGENT_CAPABILITY_CEILING_VERSION,\n\t\t...(allowedTools !== undefined ? { allowedTools } : {}),\n\t\t...(allowedAgents !== undefined ? { allowedAgents } : {}),\n\t\tdenyExtensions: ceiling.denyExtensions === true,\n\t\tsources: [],\n\t};\n}\n\nexport function parseSubagentCapabilityCeiling(value: unknown, field = \"capability ceiling\"): ResolvedSubagentCapabilityCeiling {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) throw new Error(`Invalid ${field}; expected an object.`);\n\tconst record = value as Record<string, unknown>;\n\tif (record.version !== SUBAGENT_CAPABILITY_CEILING_VERSION) throw new Error(`Invalid ${field} version.`);\n\tconst normalized = normalizeCeiling(record as SubagentCapabilityCeiling);\n\tconst sources = record.sources;\n\tif (!Array.isArray(sources) || sources.some((source) => typeof source !== \"string\")) throw new Error(`Invalid ${field} sources; expected an array of strings.`);\n\tnormalized.sources = [...new Set(sources.map((source) => validateText(source, `${field} source`)))].sort();\n\treturn normalized;\n}\n\nexport function registerSubagentCapabilityCeiling(options: RegisterSubagentCapabilityCeilingOptions): SubagentCapabilityCeilingHandle {\n\tconst sessionId = validateText(options.sessionId, \"sessionId\");\n\tconst source = validateText(options.source, \"source\");\n\tlet normalized = normalizeCeiling(options.ceiling);\n\tconst token = Symbol(source);\n\tconst store = registry();\n\tlet session = store.get(sessionId);\n\tif (!session) {\n\t\tsession = new Map();\n\t\tstore.set(sessionId, session);\n\t}\n\tconst setRegistration = () => {\n\t\tnormalized = normalizeCeiling(normalized);\n\t\tnormalized.sources = [source];\n\t\tsession!.set(token, { source, ceiling: normalized });\n\t};\n\tsetRegistration();\n\tlet disposed = false;\n\treturn {\n\t\tupdate(ceiling) {\n\t\t\tif (disposed) throw new Error(\"Cannot update a disposed capability ceiling handle.\");\n\t\t\tnormalized = normalizeCeiling(ceiling);\n\t\t\tnormalized.sources = [source];\n\t\t\tsession!.set(token, { source, ceiling: normalized });\n\t\t},\n\t\tdispose() {\n\t\t\tif (disposed) return;\n\t\t\tdisposed = true;\n\t\t\tsession!.delete(token);\n\t\t\tif (session!.size === 0) store.delete(sessionId);\n\t\t},\n\t};\n}\n\nexport function intersectSubagentCapabilityCeilings(...ceilings: Array<ResolvedSubagentCapabilityCeiling | undefined>): ResolvedSubagentCapabilityCeiling | undefined {\n\tconst active = ceilings.filter((ceiling): ceiling is ResolvedSubagentCapabilityCeiling => ceiling !== undefined);\n\tif (active.length === 0) return undefined;\n\tconst intersectLists = (field: \"allowedTools\" | \"allowedAgents\"): string[] | undefined => {\n\t\tconst definedLists = active.filter((ceiling) => ceiling[field] !== undefined).map((ceiling) => new Set(ceiling[field]));\n\t\tif (definedLists.length === 0) return undefined;\n\t\treturn [...definedLists[0]!].filter((entry) => definedLists.every((list) => list.has(entry))).sort();\n\t};\n\tconst allowedTools = intersectLists(\"allowedTools\");\n\tconst allowedAgents = intersectLists(\"allowedAgents\");\n\treturn {\n\t\tversion: SUBAGENT_CAPABILITY_CEILING_VERSION,\n\t\t...(allowedTools !== undefined ? { allowedTools } : {}),\n\t\t...(allowedAgents !== undefined ? { allowedAgents } : {}),\n\t\tdenyExtensions: active.some((ceiling) => ceiling.denyExtensions),\n\t\tsources: [...new Set(active.flatMap((ceiling) => ceiling.sources))].sort(),\n\t};\n}\n\nexport function resolveSubagentCapabilityCeiling(sessionId: string | undefined, inherited?: ResolvedSubagentCapabilityCeiling): ResolvedSubagentCapabilityCeiling | undefined {\n\tconst active: ResolvedSubagentCapabilityCeiling[] = [];\n\tif (sessionId) {\n\t\tconst registrations = registry().get(sessionId);\n\t\tif (registrations) active.push(...Array.from(registrations.values(), ({ ceiling }) => ceiling));\n\t}\n\treturn intersectSubagentCapabilityCeilings(inherited, ...active);\n}\n\nexport function resolveCurrentSubagentCapabilityCeiling(sessionId: string | undefined): ResolvedSubagentCapabilityCeiling | undefined {\n\treturn resolveSubagentCapabilityCeiling(sessionId, decodeSubagentCapabilityCeiling(process.env[SUBAGENT_CAPABILITY_CEILING_ENV]));\n}\n\nexport function isAgentAllowedByCapabilityCeiling(agentName: string, ceiling: ResolvedSubagentCapabilityCeiling | undefined): boolean {\n\treturn ceiling?.allowedAgents === undefined || ceiling.allowedAgents.includes(agentName);\n}\n\nexport function capabilityCeilingAgentRestrictionMessage(agentName: string, ceiling: ResolvedSubagentCapabilityCeiling | undefined): string | undefined {\n\tif (isAgentAllowedByCapabilityCeiling(agentName, ceiling)) return undefined;\n\tconst sources = ceiling?.sources.length ? ceiling.sources.join(\", \") : \"unknown source\";\n\tconst allowed = ceiling?.allowedAgents?.length ? ceiling.allowedAgents.join(\", \") : \"(none)\";\n\treturn `Capability ceiling from ${sources} does not allow agent '${agentName}'. Allowed agents: ${allowed}.`;\n}\n\nexport function assertAgentAllowedByCapabilityCeiling(agentName: string, ceiling: ResolvedSubagentCapabilityCeiling | undefined): void {\n\tconst message = capabilityCeilingAgentRestrictionMessage(agentName, ceiling);\n\tif (message) throw new Error(message);\n}\n\nexport function capabilityCeilingAgentRestrictionSources(ceiling: ResolvedSubagentCapabilityCeiling | undefined): string[] | undefined {\n\treturn ceiling?.allowedAgents === undefined ? undefined : [...ceiling.sources];\n}\n\nexport function encodeSubagentCapabilityCeiling(ceiling: ResolvedSubagentCapabilityCeiling | undefined): string | undefined {\n\tif (!ceiling) return undefined;\n\treturn Buffer.from(JSON.stringify(ceiling), \"utf8\").toString(\"base64url\");\n}\n\nexport function decodeSubagentCapabilityCeiling(value: string | undefined): ResolvedSubagentCapabilityCeiling | undefined {\n\tif (value === undefined || value === \"\") return undefined;\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(Buffer.from(value, \"base64url\").toString(\"utf8\"));\n\t} catch (error) {\n\t\tthrow new Error(`Invalid inherited capability ceiling: ${error instanceof Error ? error.message : String(error)}`);\n\t}\n\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed) || (parsed as { version?: unknown }).version !== SUBAGENT_CAPABILITY_CEILING_VERSION) {\n\t\tthrow new Error(\"Invalid inherited capability ceiling version.\");\n\t}\n\treturn parseSubagentCapabilityCeiling(parsed, \"inherited capability ceiling\");\n}\n","export const BYOK_PI_PERMISSION_MODE = 'BYOK_PI_PERMISSION_MODE';\n\n/** SDK-owned extension tools that do not mutate the task workspace. */\nexport const BYOK_PI_READONLY_PARENT_TOOLS = ['subagent', 'todo'] as const;\n\n/** Child tools retained when a readonly parent delegates through pi-subagents. */\nexport const BYOK_PI_READONLY_SUBAGENT_TOOLS = ['read', 'grep', 'find', 'ls'] as const;\n\n/** Package-provided read-only roles; writer and ambient custom roles stay unavailable. */\nexport const BYOK_PI_READONLY_SUBAGENT_AGENTS = [\n 'reviewer',\n 'oracle',\n 'codex-exec',\n 'claude-code',\n 'cursor-agent',\n] as const;\n","import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport {\n registerSubagentCapabilityCeiling,\n type SubagentCapabilityCeilingHandle,\n} from 'pi-subagents/capability-ceiling';\nimport {\n BYOK_PI_PERMISSION_MODE,\n BYOK_PI_READONLY_SUBAGENT_AGENTS,\n BYOK_PI_READONLY_SUBAGENT_TOOLS,\n} from './subagents-policy-config';\n\nfunction permissionMode(): 'auto' | 'readonly' {\n const mode = process.env[BYOK_PI_PERMISSION_MODE];\n if (mode === 'auto' || mode === 'readonly') return mode;\n throw new Error(`${BYOK_PI_PERMISSION_MODE} must be \"auto\" or \"readonly\"`);\n}\n\n/**\n * Keep pi-subagents available in every Pi session without letting a readonly\n * parent widen its task contract through a child process.\n */\nexport default function registerByokSubagentsPolicy(pi: ExtensionAPI): void {\n const mode = permissionMode();\n let ceiling: SubagentCapabilityCeilingHandle | undefined;\n\n pi.on('session_start', (_event, ctx) => {\n ceiling?.dispose();\n ceiling = undefined;\n if (mode === 'auto') return;\n\n const sessionId = ctx.sessionManager.getSessionId();\n if (!sessionId) throw new Error('readonly Pi subagent policy requires an authoritative session id');\n ceiling = registerSubagentCapabilityCeiling({\n sessionId,\n source: 'byok-sdk-readonly',\n ceiling: {\n allowedTools: BYOK_PI_READONLY_SUBAGENT_TOOLS,\n allowedAgents: BYOK_PI_READONLY_SUBAGENT_AGENTS,\n denyExtensions: true,\n },\n });\n });\n\n pi.on('tool_call', (event) => {\n if (mode === 'readonly' && event.toolName === 'subagent' && ceiling === undefined) {\n return {\n block: true,\n terminate: true,\n reason: 'readonly Pi subagent capability ceiling is unavailable',\n };\n }\n });\n\n pi.on('session_shutdown', () => {\n ceiling?.dispose();\n ceiling = undefined;\n });\n}\n"]}
@@ -80,7 +80,7 @@ export type { AgentMemoryFilesystem } from '../daemon/agent-memory-filesystem';
80
80
  export type { AgentMemoryFilesystemFileState } from '../daemon/agent-memory-filesystem';
81
81
  /** Product-owned deployment pointer to the helper binary; the SDK never searches PATH. */
82
82
  export type { AgentMemoryFilesystemHelperConfig } from '../daemon/agent-memory-filesystem';
83
- /** Serves `memory.recall`/`memory.save` as a stdio MCP server over host-provided streams. */
83
+ /** Serves `memory_recall`/`memory_save` as a stdio MCP server over host-provided streams. */
84
84
  export { serveAgentMemoryMcpOverStdio } from '../bin/agent-memory-mcp-server';
85
85
  /** MCP tool name a host must allowlist for reads. */
86
86
  export { AGENT_MEMORY_RECALL_TOOL_NAME } from '../bin/agent-memory-mcp-server';
@@ -678,8 +678,8 @@ var AgentMemoryFilesystemHelperClient = class _AgentMemoryFilesystemHelperClient
678
678
  async function openAgentMemoryFilesystemHelper(input) {
679
679
  return AgentMemoryFilesystemHelperClient.open(input);
680
680
  }
681
- var AGENT_MEMORY_RECALL_TOOL_NAME = "memory.recall";
682
- var AGENT_MEMORY_SAVE_TOOL_NAME = "memory.save";
681
+ var AGENT_MEMORY_RECALL_TOOL_NAME = "memory_recall";
682
+ var AGENT_MEMORY_SAVE_TOOL_NAME = "memory_save";
683
683
  function record(value) {
684
684
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
685
685
  }
@@ -710,11 +710,11 @@ async function handleAgentMemoryMcpRequest(request, deps) {
710
710
  if (!params || !args || typeof params.name !== "string") return invalid(id, "memory tool input must be an object");
711
711
  try {
712
712
  if (params.name === AGENT_MEMORY_RECALL_TOOL_NAME) {
713
- if (Object.keys(args).some((key) => key !== "path" && key !== "ifRevision") || typeof args.path !== "string" || args.ifRevision !== void 0 && typeof args.ifRevision !== "string") return invalid(id, "memory.recall accepts only path and optional ifRevision");
713
+ if (Object.keys(args).some((key) => key !== "path" && key !== "ifRevision") || typeof args.path !== "string" || args.ifRevision !== void 0 && typeof args.ifRevision !== "string") return invalid(id, "memory_recall accepts only path and optional ifRevision");
714
714
  return success(id, await deps.recall({ path: args.path, ...args.ifRevision === void 0 ? {} : { ifRevision: args.ifRevision } }));
715
715
  }
716
716
  if (params.name === AGENT_MEMORY_SAVE_TOOL_NAME) {
717
- if (Object.keys(args).some((key) => key !== "op" && key !== "path" && key !== "expectedRevision" && key !== "content") || args.op !== "replace" && args.op !== "delete" || typeof args.path !== "string" || typeof args.expectedRevision !== "string" || args.op === "replace" && typeof args.content !== "string" || args.op === "delete" && args.content !== void 0) return invalid(id, "memory.save requires replace|delete, path, expectedRevision, and content only for replace");
717
+ if (Object.keys(args).some((key) => key !== "op" && key !== "path" && key !== "expectedRevision" && key !== "content") || args.op !== "replace" && args.op !== "delete" || typeof args.path !== "string" || typeof args.expectedRevision !== "string" || args.op === "replace" && typeof args.content !== "string" || args.op === "delete" && args.content !== void 0) return invalid(id, "memory_save requires replace|delete, path, expectedRevision, and content only for replace");
718
718
  const content = args.content;
719
719
  return success(id, await deps.save({ op: args.op, path: args.path, expectedRevision: args.expectedRevision, ...typeof content === "string" ? { content } : {} }));
720
720
  }