@byok-sdk/client 0.13.0 → 0.14.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 +80 -0
- package/dist/adapters/claude/process-client.d.ts +24 -0
- package/dist/adapters/codex/process-runner.d.ts +46 -1
- package/dist/adapters/index.js +359 -28
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/pi/events.d.ts +1 -1
- package/dist/adapters/pi/rpc-client.d.ts +47 -1
- package/dist/adapters/pi/subagents-policy-extension.js +1 -1
- package/dist/adapters/pi/subagents-policy-extension.js.map +1 -1
- package/dist/adapters/pi/team-interaction-extension.d.ts +24 -0
- package/dist/adapters/pi/team-interaction-extension.js +81 -0
- package/dist/adapters/pi/team-interaction-extension.js.map +1 -0
- package/dist/adapters/process-tree.d.ts +74 -4
- package/dist/adapters/provider-credential-environment.d.ts +1 -1
- package/dist/adapters/win32-job-object.d.ts +101 -0
- package/dist/bin/byok-agent-memory-mcp.js.map +1 -1
- package/dist/bin/byok-agent-message-mcp.js.map +1 -1
- package/dist/bin/byok-agent-team-mcp.js.map +1 -1
- package/dist/bin/byok-agent.js +9305 -7988
- package/dist/bin/byok-agent.js.map +1 -1
- package/dist/bin/byok-approval-mcp.js.map +1 -1
- package/dist/bin/commands/team-pi-relay.d.ts +24 -0
- package/dist/bin/commands/team-relay.d.ts +11 -0
- package/dist/bin/team-codex-relay.d.ts +32 -0
- package/dist/bin/team-notification-relay.d.ts +40 -0
- package/dist/bin/team-pi-session.d.ts +59 -0
- package/dist/daemon/agent-egress-policy.d.ts +8 -0
- package/dist/daemon/connection-manager.d.ts +6 -100
- package/dist/daemon/control-protocol.d.ts +2 -0
- package/dist/daemon/create-daemon.d.ts +32 -0
- package/dist/daemon/event-spill.d.ts +90 -0
- package/dist/daemon/journal/journal.d.ts +15 -3
- package/dist/daemon/journal/sqlite-journal.d.ts +7 -2
- package/dist/daemon/long-poll-transport.d.ts +2 -50
- package/dist/daemon/task-runner.d.ts +13 -0
- package/dist/daemon/team-workspace.d.ts +9 -0
- package/dist/index.js +897 -241
- package/dist/index.js.map +1 -1
- package/package.json +8 -5
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AgentEvent } from '@byok-sdk/protocol';
|
|
2
2
|
import type { PiRpcMessage } from './rpc-client';
|
|
3
3
|
/**
|
|
4
|
-
* Map pi 0.
|
|
4
|
+
* Map pi 0.85.1 RPC frames into BYOK's runtime-neutral event contract.
|
|
5
5
|
*
|
|
6
6
|
* `agent_settled` is the only whole-task completion authority. `agent_end`
|
|
7
7
|
* ends one low-level agent run, but pi may still perform retry/compaction or
|
|
@@ -18,6 +18,24 @@ export interface PiRpcClientOptions {
|
|
|
18
18
|
cwd: string;
|
|
19
19
|
env: NodeJS.ProcessEnv;
|
|
20
20
|
spawnFn?: SpawnFn;
|
|
21
|
+
/** Explicit GUI-owned interaction lane; omission retains unattended cancellation. */
|
|
22
|
+
extensionUi?: {
|
|
23
|
+
mode: 'hold';
|
|
24
|
+
onRequest: (request: PiRpcMessage) => void;
|
|
25
|
+
};
|
|
26
|
+
/** Synchronous observation before command receipts resolve, for an owned relay. */
|
|
27
|
+
onFrame?: (frame: PiRpcMessage) => void;
|
|
28
|
+
/**
|
|
29
|
+
* DI seam scoped to ADOPTION only (`../process-tree.ts`'s
|
|
30
|
+
* `adoptOwnedProcessTree`), so the win32 job-object branch is exercisable
|
|
31
|
+
* from POSIX. Disposal keeps `process.platform` as its own authority — this
|
|
32
|
+
* must never silently reroute the taskkill sweep on a real host.
|
|
33
|
+
*/
|
|
34
|
+
platform?: NodeJS.Platform;
|
|
35
|
+
/** DI seam for the win32 job-object backstop; see `../win32-job-object.ts`. */
|
|
36
|
+
jobObject?: {
|
|
37
|
+
assign(pid: number): Promise<void>;
|
|
38
|
+
};
|
|
21
39
|
}
|
|
22
40
|
/**
|
|
23
41
|
* JSONL request/response + event-stream client for `pi --mode rpc`.
|
|
@@ -32,6 +50,7 @@ export interface PiRpcClientOptions {
|
|
|
32
50
|
* response can overtake a slower in-flight command's response).
|
|
33
51
|
*/
|
|
34
52
|
export declare class PiRpcClient {
|
|
53
|
+
private readonly options;
|
|
35
54
|
private readonly child;
|
|
36
55
|
private buffer;
|
|
37
56
|
private nextId;
|
|
@@ -42,12 +61,23 @@ export declare class PiRpcClient {
|
|
|
42
61
|
private readonly closedPromise;
|
|
43
62
|
private resolveClosed;
|
|
44
63
|
private disposalAttempt;
|
|
64
|
+
/** Resolves once this tree is backstopped (see `adoptOwnedProcessTree`); rejects with the adoption failure, having already terminated the tree. */
|
|
65
|
+
private readonly adopted;
|
|
66
|
+
/** Set before the fail-closed termination starts, so it — not the exit code of the kill we ourselves requested — becomes this client's exit error. */
|
|
67
|
+
private adoptionFailure;
|
|
45
68
|
/** Bounded tail of recent stderr lines — pi discarded this entirely before (nothing ever read `child.stderr`), which is exactly why finding #1 (`Error: Unknown option: --session-id`, exit 1) had to be root-caused by hand instead of reading it off a thrown error. See `buildExitError`. */
|
|
46
69
|
private readonly stderrRing;
|
|
47
70
|
/** Count of pi RPC message types `PiSession` (pi-adapter.ts) has told us have no `AgentEvent` mapping and aren't routine bookkeeping — see `recordUnmappedFrame`. */
|
|
48
71
|
private readonly unmappedFrameCounts;
|
|
49
72
|
constructor(options: PiRpcClientOptions);
|
|
50
|
-
/**
|
|
73
|
+
/**
|
|
74
|
+
* Send a command, resolved with its correlated `response` message.
|
|
75
|
+
*
|
|
76
|
+
* Adoption is awaited FIRST — this is `PiAdapter.start()`'s first awaited
|
|
77
|
+
* operation, so an unbackstopped tree never receives the initial prompt and
|
|
78
|
+
* never yields a session. The wait costs one settled-promise turn once the
|
|
79
|
+
* tree is adopted; every later command sees it already resolved.
|
|
80
|
+
*/
|
|
51
81
|
send(command: Record<string, unknown> & {
|
|
52
82
|
type: string;
|
|
53
83
|
id?: string;
|
|
@@ -77,8 +107,24 @@ export declare class PiRpcClient {
|
|
|
77
107
|
waitClosed(): Promise<void>;
|
|
78
108
|
dispose(): Promise<void>;
|
|
79
109
|
private processTreeOptions;
|
|
110
|
+
/**
|
|
111
|
+
* Backstop this tree, or tear it down. Adoption failure is a start-time
|
|
112
|
+
* precondition, not a degraded mode: the child is terminated through the one
|
|
113
|
+
* disposal authority and the failure is re-thrown, which is what makes the
|
|
114
|
+
* first `send()` — and therefore `PiAdapter.start()` — fail before any
|
|
115
|
+
* session is published. Both cleanup attempts are best-effort because the
|
|
116
|
+
* adoption failure, not a terminator's own complaint, is the reason to report.
|
|
117
|
+
*/
|
|
118
|
+
private adoptOwnedTree;
|
|
80
119
|
private onData;
|
|
81
120
|
private onLine;
|
|
121
|
+
/** Write an explicit host-owned response; completion is a transport receipt only. */
|
|
122
|
+
respondExtensionUi(response: {
|
|
123
|
+
id: string;
|
|
124
|
+
cancelled?: true;
|
|
125
|
+
confirmed?: boolean;
|
|
126
|
+
value?: string;
|
|
127
|
+
}): Promise<void>;
|
|
82
128
|
/**
|
|
83
129
|
* Answer pi's extension-UI blocking protocol headlessly (rpc.md's
|
|
84
130
|
* "Extension UI Protocol"). Fail-closed policy, stated explicitly because
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Buffer } from 'buffer';
|
|
2
2
|
|
|
3
|
-
// ../../node_modules/.bun/pi-subagents@0.60.0+
|
|
3
|
+
// ../../node_modules/.bun/pi-subagents@0.60.0+5c5e823a15db0d8e/node_modules/pi-subagents/src/runs/shared/capability-ceiling.ts
|
|
4
4
|
var SUBAGENT_CAPABILITY_CEILING_VERSION = 1;
|
|
5
5
|
var SUBAGENT_CAPABILITY_CEILING_REGISTRY_KEY = "pi-subagents.capability-ceiling.v1";
|
|
6
6
|
function registry() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../node_modules/.bun/pi-subagents@0.60.0+5b15fedd58145c46/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"]}
|
|
1
|
+
{"version":3,"sources":["../../../../../node_modules/.bun/pi-subagents@0.60.0+5c5e823a15db0d8e/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"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
|
|
2
|
+
/** Tracks the public coalesced Pi UI span, never infers state from timers or isIdle. */
|
|
3
|
+
export declare class PiInteractionGate {
|
|
4
|
+
private readonly emit;
|
|
5
|
+
private phase;
|
|
6
|
+
private sessionId;
|
|
7
|
+
private revision;
|
|
8
|
+
private readonly waiters;
|
|
9
|
+
constructor(emit: (event: {
|
|
10
|
+
type: 'byok_team_gate';
|
|
11
|
+
sessionId?: string;
|
|
12
|
+
revision: number;
|
|
13
|
+
phase: string;
|
|
14
|
+
}) => void);
|
|
15
|
+
start(sessionId: string): void;
|
|
16
|
+
openPrompt(): void;
|
|
17
|
+
closePrompt(): void;
|
|
18
|
+
fail(): void;
|
|
19
|
+
close(): void;
|
|
20
|
+
private report;
|
|
21
|
+
wait(signal?: AbortSignal): Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
/** Load first, RPC only. The GUI host owns the process and exact-ID UI responses. */
|
|
24
|
+
export default function teamInteractionExtension(pi: ExtensionAPI): void;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// src/adapters/pi/team-interaction-extension.ts
|
|
2
|
+
var PiInteractionGate = class {
|
|
3
|
+
constructor(emit) {
|
|
4
|
+
this.emit = emit;
|
|
5
|
+
}
|
|
6
|
+
emit;
|
|
7
|
+
phase = "starting";
|
|
8
|
+
sessionId;
|
|
9
|
+
revision = 0;
|
|
10
|
+
waiters = /* @__PURE__ */ new Set();
|
|
11
|
+
start(sessionId) {
|
|
12
|
+
if (this.phase !== "starting" || !sessionId) return this.fail();
|
|
13
|
+
this.sessionId = sessionId;
|
|
14
|
+
this.phase = "open";
|
|
15
|
+
this.report();
|
|
16
|
+
}
|
|
17
|
+
openPrompt() {
|
|
18
|
+
if (this.phase !== "open") return this.fail();
|
|
19
|
+
this.phase = "waiting";
|
|
20
|
+
this.report();
|
|
21
|
+
}
|
|
22
|
+
closePrompt() {
|
|
23
|
+
if (this.phase !== "waiting") return this.fail();
|
|
24
|
+
this.phase = "open";
|
|
25
|
+
this.report();
|
|
26
|
+
for (const resolve of this.waiters) resolve();
|
|
27
|
+
this.waiters.clear();
|
|
28
|
+
}
|
|
29
|
+
fail() {
|
|
30
|
+
this.phase = "failed";
|
|
31
|
+
this.report();
|
|
32
|
+
}
|
|
33
|
+
close() {
|
|
34
|
+
this.phase = "closed";
|
|
35
|
+
this.report();
|
|
36
|
+
}
|
|
37
|
+
report() {
|
|
38
|
+
this.emit({ type: "byok_team_gate", ...this.sessionId ? { sessionId: this.sessionId } : {}, revision: ++this.revision, phase: this.phase });
|
|
39
|
+
}
|
|
40
|
+
async wait(signal) {
|
|
41
|
+
await Promise.resolve();
|
|
42
|
+
while (this.phase !== "open") {
|
|
43
|
+
if (signal?.aborted && this.phase === "waiting") return;
|
|
44
|
+
await new Promise((resolve) => {
|
|
45
|
+
const done = () => {
|
|
46
|
+
this.waiters.delete(done);
|
|
47
|
+
signal?.removeEventListener("abort", aborted);
|
|
48
|
+
resolve();
|
|
49
|
+
};
|
|
50
|
+
const aborted = () => {
|
|
51
|
+
if (this.phase === "waiting") done();
|
|
52
|
+
};
|
|
53
|
+
this.waiters.add(done);
|
|
54
|
+
signal?.addEventListener("abort", aborted, { once: true });
|
|
55
|
+
});
|
|
56
|
+
await Promise.resolve();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
function teamInteractionExtension(pi) {
|
|
61
|
+
let ui;
|
|
62
|
+
const gate = new PiInteractionGate((event) => ui.setStatus("byok_team_gate", JSON.stringify(event)));
|
|
63
|
+
pi.on("session_start", (_event, ctx) => {
|
|
64
|
+
ui = ctx.ui;
|
|
65
|
+
gate.start(ctx.sessionManager.getSessionId());
|
|
66
|
+
});
|
|
67
|
+
pi.on("ui_prompt_start", () => gate.openPrompt());
|
|
68
|
+
pi.on("ui_prompt_end", () => gate.closePrompt());
|
|
69
|
+
pi.on("input", async () => {
|
|
70
|
+
await gate.wait();
|
|
71
|
+
return { action: "continue" };
|
|
72
|
+
});
|
|
73
|
+
pi.on("before_provider_request", async (_event, ctx) => {
|
|
74
|
+
await gate.wait(ctx.signal);
|
|
75
|
+
});
|
|
76
|
+
pi.on("session_shutdown", () => gate.close());
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export { PiInteractionGate, teamInteractionExtension as default };
|
|
80
|
+
//# sourceMappingURL=team-interaction-extension.js.map
|
|
81
|
+
//# sourceMappingURL=team-interaction-extension.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/adapters/pi/team-interaction-extension.ts"],"names":[],"mappings":"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","file":"team-interaction-extension.js","sourcesContent":["import type { ExtensionAPI, ExtensionUIContext } from '@earendil-works/pi-coding-agent';\n\n/** Tracks the public coalesced Pi UI span, never infers state from timers or isIdle. */\nexport class PiInteractionGate {\n private phase: 'starting' | 'open' | 'waiting' | 'failed' | 'closed' = 'starting';\n private sessionId: string | undefined;\n private revision = 0;\n private readonly waiters = new Set<() => void>();\n constructor(private readonly emit: (event: { type: 'byok_team_gate'; sessionId?: string; revision: number; phase: string }) => void) {}\n start(sessionId: string): void {\n if (this.phase !== 'starting' || !sessionId) return this.fail();\n this.sessionId = sessionId; this.phase = 'open'; this.report();\n }\n openPrompt(): void {\n if (this.phase !== 'open') return this.fail();\n this.phase = 'waiting'; this.report();\n }\n closePrompt(): void {\n if (this.phase !== 'waiting') return this.fail();\n this.phase = 'open'; this.report();\n for (const resolve of this.waiters) resolve(); this.waiters.clear();\n }\n fail(): void { this.phase = 'failed'; this.report(); }\n close(): void { this.phase = 'closed'; this.report(); }\n private report(): void { this.emit({ type: 'byok_team_gate', ...(this.sessionId ? { sessionId: this.sessionId } : {}), revision: ++this.revision, phase: this.phase }); }\n async wait(signal?: AbortSignal): Promise<void> {\n // Pi publishes span lifecycle through queueMicrotask. Drain events already\n // scheduled before checking the admission state in this process.\n await Promise.resolve();\n while (this.phase !== 'open') {\n // Unknown state never throws into Pi's best-effort extension dispatcher,\n // which would swallow the exception and continue. Host tears down on fatal.\n if (signal?.aborted && this.phase === 'waiting') return;\n await new Promise<void>(resolve => {\n const done = () => { this.waiters.delete(done); signal?.removeEventListener('abort', aborted); resolve(); };\n const aborted = () => { if (this.phase === 'waiting') done(); };\n this.waiters.add(done); signal?.addEventListener('abort', aborted, { once: true });\n });\n // A continuation may open another dialog before its start event runs.\n await Promise.resolve();\n }\n }\n}\n\n/** Load first, RPC only. The GUI host owns the process and exact-ID UI responses. */\nexport default function teamInteractionExtension(pi: ExtensionAPI): void {\n let ui: ExtensionUIContext;\n const gate = new PiInteractionGate(event => ui.setStatus('byok_team_gate', JSON.stringify(event)));\n pi.on('session_start', (_event, ctx) => { ui = ctx.ui; gate.start(ctx.sessionManager.getSessionId()); });\n pi.on('ui_prompt_start', () => gate.openPrompt());\n pi.on('ui_prompt_end', () => gate.closePrompt());\n pi.on('input', async () => { await gate.wait(); return { action: 'continue' }; });\n pi.on('before_provider_request', async (_event, ctx) => { await gate.wait(ctx.signal); });\n pi.on('session_shutdown', () => gate.close());\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { spawn, type ChildProcess, type SpawnOptions } from 'node:child_process';
|
|
2
|
-
type KillFn = (pid: number, signal: NodeJS.Signals | number) => void;
|
|
1
|
+
import { spawn, spawnSync, type ChildProcess, type SpawnOptions } from 'node:child_process';
|
|
2
|
+
export type KillFn = (pid: number, signal: NodeJS.Signals | number) => void;
|
|
3
3
|
export interface OwnedProcessTreeOptions {
|
|
4
4
|
child: ChildProcess;
|
|
5
5
|
waitClosed: () => Promise<void>;
|
|
@@ -20,6 +20,68 @@ export interface OwnedProcessTreeOptions {
|
|
|
20
20
|
* the runtime outlive the daemon. Windows uses taskkill's `/T` tree authority.
|
|
21
21
|
*/
|
|
22
22
|
export declare function withOwnedProcessTree<T extends SpawnOptions>(options: T): T;
|
|
23
|
+
/**
|
|
24
|
+
* The `exit` listener itself. Three constraints, all load-bearing:
|
|
25
|
+
* it never awaits (Node runs nothing asynchronous after `exit` begins), it
|
|
26
|
+
* never throws (a throw here would replace the host's own exit reason), and
|
|
27
|
+
* each target is swept in its own try/catch so one unreachable tree cannot
|
|
28
|
+
* strand the rest. POSIX kills the owned process GROUP — `withOwnedProcessTree`
|
|
29
|
+
* made the root a group leader precisely so this one signal reaches the whole
|
|
30
|
+
* tree. win32 has no process groups to signal, so it pays for a synchronous
|
|
31
|
+
* `taskkill /T /F` per target.
|
|
32
|
+
*/
|
|
33
|
+
declare function terminateOwnedTreesForHostExit(): void;
|
|
34
|
+
/**
|
|
35
|
+
* Test-only view of the host-exit backstop. Not part of any package entry
|
|
36
|
+
* point (`process-tree.ts` is internal to `@byok-sdk/client`); it exists so
|
|
37
|
+
* the registry contract can be asserted without a second production seam.
|
|
38
|
+
*/
|
|
39
|
+
export declare const __hostExitBackstopForTests: {
|
|
40
|
+
/** The exact function Node invokes on `exit`. */
|
|
41
|
+
run: typeof terminateOwnedTreesForHostExit;
|
|
42
|
+
registeredPids(): number[];
|
|
43
|
+
listenerCount(): number;
|
|
44
|
+
};
|
|
45
|
+
export interface AdoptOwnedProcessTreeOptions {
|
|
46
|
+
child: ChildProcess;
|
|
47
|
+
label: string;
|
|
48
|
+
/** DI seam — defaults to `process.platform`, mirroring {@link OwnedProcessTreeOptions}. */
|
|
49
|
+
platform?: NodeJS.Platform;
|
|
50
|
+
/** DI seam for the win32 job-object backstop — defaults to the lazily imported `./win32-job-object`. */
|
|
51
|
+
jobObject?: {
|
|
52
|
+
assign(pid: number): Promise<void>;
|
|
53
|
+
};
|
|
54
|
+
/** DI seam for the POSIX host-exit sweep — defaults to `process.kill`. */
|
|
55
|
+
killFn?: KillFn;
|
|
56
|
+
/** DI seam for the win32 host-exit sweep — defaults to `node:child_process`'s `spawnSync`. */
|
|
57
|
+
spawnSyncFn?: typeof spawnSync;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Take ownership of a just-spawned runtime tree, beyond what disposal covers.
|
|
61
|
+
*
|
|
62
|
+
* Two backstops with different coverage, both kernel- or runtime-owned rather
|
|
63
|
+
* than sweep-based:
|
|
64
|
+
*
|
|
65
|
+
* 1. Every platform: the tree joins a registry a once-installed, synchronous
|
|
66
|
+
* `process` `exit` listener sweeps. This covers an ordinary daemon exit —
|
|
67
|
+
* the case where disposal simply never ran.
|
|
68
|
+
* 2. win32 only: the root is assigned to the daemon-wide
|
|
69
|
+
* `JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE` job (see `./win32-job-object.ts`).
|
|
70
|
+
* That one is the kernel's, so it also covers the daemon deaths no listener
|
|
71
|
+
* can observe.
|
|
72
|
+
*
|
|
73
|
+
* Fail closed: a rejected assignment propagates, and the caller must terminate
|
|
74
|
+
* the child and refuse to publish a run handle rather than run a tree nothing
|
|
75
|
+
* owns. The registration happens FIRST so a child that fails assignment is
|
|
76
|
+
* still swept if the daemon exits during the caller's cleanup.
|
|
77
|
+
*
|
|
78
|
+
* Residual boundary, stated rather than hidden: assignment happens after
|
|
79
|
+
* `spawn` returns, so a grandchild forked inside that window is outside the
|
|
80
|
+
* job; and neither backstop can run when the daemon itself is SIGKILLed or
|
|
81
|
+
* OOM-killed on POSIX, where only the job object's kernel guarantee — which
|
|
82
|
+
* POSIX does not have — would help.
|
|
83
|
+
*/
|
|
84
|
+
export declare function adoptOwnedProcessTree(options: AdoptOwnedProcessTreeOptions): Promise<void>;
|
|
23
85
|
/**
|
|
24
86
|
* Immediate termination request used by interrupt paths; close remains the
|
|
25
87
|
* receipt. On win32 the request also RECORDS the pid set taskkill walked,
|
|
@@ -53,8 +115,16 @@ export declare function requestOwnedProcessTreeTermination(options: OwnedProcess
|
|
|
53
115
|
* window means a recycled pid could read as alive. And if taskkill's output is
|
|
54
116
|
* empty or unparseable, the walked set collapses to `{root}`: disposal then
|
|
55
117
|
* measures the root alone and reports quiescence on that basis, which is a
|
|
56
|
-
* narrower claim than the tree, not a false one.
|
|
57
|
-
*
|
|
118
|
+
* narrower claim than the tree, not a false one.
|
|
119
|
+
*
|
|
120
|
+
* Orphans left behind by a DAEMON death are no longer out of scope, but they
|
|
121
|
+
* are not THIS function's job either — nothing here runs when the daemon is
|
|
122
|
+
* gone. {@link adoptOwnedProcessTree} owns that case with two backstops
|
|
123
|
+
* installed at spawn time: a synchronous host-`exit` sweep on every platform,
|
|
124
|
+
* and on win32 a kernel-owned kill-on-close Job Object. Their own residuals
|
|
125
|
+
* remain: the tiny window between `spawn` returning and the job assignment,
|
|
126
|
+
* and a daemon SIGKILL/OOM on POSIX, where no in-process listener can run and
|
|
127
|
+
* there is no job object to fall back on.
|
|
58
128
|
*/
|
|
59
129
|
export declare function disposeOwnedProcessTree(options: OwnedProcessTreeOptions): Promise<void>;
|
|
60
130
|
export {};
|
|
@@ -13,6 +13,6 @@ export declare const PROVIDER_CREDENTIAL_ENV_NAMES: readonly ['ANTHROPIC_API_KEY
|
|
|
13
13
|
* denying a credential is safe, while allowing every cloud credential Pi
|
|
14
14
|
* could consume would regress the daemon's ambient-environment isolation.
|
|
15
15
|
*/
|
|
16
|
-
export declare const PROVIDER_CREDENTIAL_ENV_DENY_NAMES: readonly ["ANTHROPIC_API_KEY", "ANTHROPIC_OAUTH_TOKEN", "OPENAI_API_KEY", "GEMINI_API_KEY", "AZURE_OPENAI_API_KEY", "DEEPSEEK_API_KEY", "GROQ_API_KEY", "MISTRAL_API_KEY", "OPENROUTER_API_KEY", "XAI_API_KEY", "ZAI_API_KEY", "ANT_LING_API_KEY", "NVIDIA_API_KEY", "CEREBRAS_API_KEY", "CLOUDFLARE_API_KEY", "AI_GATEWAY_API_KEY", "ZAI_CODING_CN_API_KEY", "OPENCODE_API_KEY", "RADIUS_API_KEY", "FIREWORKS_API_KEY", "TOGETHER_API_KEY", "BASETEN_API_KEY", "KIMI_API_KEY", "MINIMAX_API_KEY", "MINIMAX_CN_API_KEY", "QWEN_TOKEN_PLAN_API_KEY", "QWEN_TOKEN_PLAN_CN_API_KEY", "XIAOMI_API_KEY", "XIAOMI_TOKEN_PLAN_CN_API_KEY", "XIAOMI_TOKEN_PLAN_AMS_API_KEY", "XIAOMI_TOKEN_PLAN_SGP_API_KEY", "AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN", "GOOGLE_APPLICATION_CREDENTIALS", "PI_PROVIDER_API_KEY"];
|
|
16
|
+
export declare const PROVIDER_CREDENTIAL_ENV_DENY_NAMES: readonly ["ANTHROPIC_API_KEY", "ANTHROPIC_OAUTH_TOKEN", "OPENAI_API_KEY", "GEMINI_API_KEY", "AZURE_OPENAI_API_KEY", "DEEPSEEK_API_KEY", "GROQ_API_KEY", "MISTRAL_API_KEY", "OPENROUTER_API_KEY", "XAI_API_KEY", "ZAI_API_KEY", "ANT_LING_API_KEY", "NVIDIA_API_KEY", "CEREBRAS_API_KEY", "CLOUDFLARE_API_KEY", "AI_GATEWAY_API_KEY", "ZAI_CODING_CN_API_KEY", "OPENCODE_API_KEY", "RADIUS_API_KEY", "FIREWORKS_API_KEY", "TOGETHER_API_KEY", "BASETEN_API_KEY", "KIMI_API_KEY", "HF_TOKEN", "MOONSHOT_API_KEY", "MINIMAX_API_KEY", "MINIMAX_CN_API_KEY", "QWEN_TOKEN_PLAN_API_KEY", "QWEN_TOKEN_PLAN_CN_API_KEY", "XIAOMI_API_KEY", "XIAOMI_TOKEN_PLAN_CN_API_KEY", "XIAOMI_TOKEN_PLAN_AMS_API_KEY", "XIAOMI_TOKEN_PLAN_SGP_API_KEY", "AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN", "GOOGLE_APPLICATION_CREDENTIALS", "PI_PROVIDER_API_KEY"];
|
|
17
17
|
/** Return a copy that cannot pass ambient provider credentials to a child. */
|
|
18
18
|
export declare function withoutProviderCredentials(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Windows kill-on-close Job Object ownership for owned runtime process trees.
|
|
3
|
+
*
|
|
4
|
+
* `adapters/process-tree.ts` can terminate a tree the daemon still controls.
|
|
5
|
+
* It cannot terminate one the daemon is no longer around to sweep: Windows
|
|
6
|
+
* never re-parents orphans, so descendants of a daemon that died (crash,
|
|
7
|
+
* SIGKILL, OOM) are unreachable to any later `taskkill /T`. A Job Object with
|
|
8
|
+
* `JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE` moves that guarantee into the kernel —
|
|
9
|
+
* ONE job is created per daemon process, every owned runtime root is assigned
|
|
10
|
+
* to it right after spawn, and the job handle is deliberately never closed.
|
|
11
|
+
* When the daemon's last handle to it goes away — for ANY reason, including a
|
|
12
|
+
* kill the daemon cannot observe — Windows terminates every process in the
|
|
13
|
+
* job.
|
|
14
|
+
*
|
|
15
|
+
* Fail-closed, no degraded path. `koffi` is an `optionalDependencies` entry of
|
|
16
|
+
* this package precisely because npm has no other per-platform install
|
|
17
|
+
* mechanism; the runtime rule, not the manifest field, carries the hardness.
|
|
18
|
+
* A missing module, a NULL handle, or a zero return from any of the four Win32
|
|
19
|
+
* calls throws {@link Win32JobObjectFailure} carrying the `GetLastError()`
|
|
20
|
+
* code, and the caller refuses to publish a run handle. Nothing here retries
|
|
21
|
+
* and nothing degrades to an unbacked tree.
|
|
22
|
+
*
|
|
23
|
+
* POSIX never loads this module: `process-tree.ts` reaches it through a
|
|
24
|
+
* dynamic `import()` taken only on the win32 branch, and `koffi` itself is
|
|
25
|
+
* imported only inside {@link loadBindings}. `__tests__/win32-job-object.test.ts`
|
|
26
|
+
* asserts that isolation from a real non-win32 subprocess.
|
|
27
|
+
*
|
|
28
|
+
* Signatures follow kernel32's documented ABI and mirror the binding table in
|
|
29
|
+
* deepseek-harness `sandbox-windows-acl` (`src/ffi.ts`), whose layout constants
|
|
30
|
+
* were verified against the real Windows headers by its own ABI probe.
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* A Win32 HANDLE as koffi 3 hands it back: a BigInt address, or null/0n for
|
|
34
|
+
* failure. Deliberately opaque — nothing here does pointer arithmetic, and a
|
|
35
|
+
* concrete numeric type would invite it.
|
|
36
|
+
*/
|
|
37
|
+
export type Win32Handle = unknown;
|
|
38
|
+
/** The steps a job assignment can fail at, in the order they are attempted. */
|
|
39
|
+
export type Win32JobObjectStep = 'loadKoffi' | 'CreateJobObjectW' | 'SetInformationJobObject' | 'OpenProcess' | 'AssignProcessToJobObject';
|
|
40
|
+
export interface Win32JobObjectFailureInput {
|
|
41
|
+
step: Win32JobObjectStep;
|
|
42
|
+
reason: string;
|
|
43
|
+
/** The `GetLastError()` code read immediately after the failing call. Absent only when the failure happened before any Win32 call (module load). */
|
|
44
|
+
win32Code?: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Start-time precondition failure of the win32 job-object backstop.
|
|
48
|
+
*
|
|
49
|
+
* Module-local by design. `RuntimeExecutionFailure` is the adapter boundary's
|
|
50
|
+
* terminal control value and is frozen, so it cannot carry `win32Code`; this
|
|
51
|
+
* failure is a transport-level precondition that each client folds into its
|
|
52
|
+
* OWN existing exit-error channel, which is what the adapter boundary already
|
|
53
|
+
* classifies. Nothing new is exported from the package index.
|
|
54
|
+
*/
|
|
55
|
+
export declare class Win32JobObjectFailure extends Error {
|
|
56
|
+
readonly step: Win32JobObjectStep;
|
|
57
|
+
readonly win32Code: number | undefined;
|
|
58
|
+
constructor(input: Win32JobObjectFailureInput, options?: ErrorOptions);
|
|
59
|
+
}
|
|
60
|
+
/** Narrowing guard usable across the two independently bundled package entries. */
|
|
61
|
+
export declare function isWin32JobObjectFailure(value: unknown): value is Win32JobObjectFailure;
|
|
62
|
+
/**
|
|
63
|
+
* The kernel32 calls the backstop needs. An interface, not a hard-wired koffi
|
|
64
|
+
* table, so the branch is exercisable from POSIX with a recording fake — the
|
|
65
|
+
* same DI convention `process-tree.ts` uses for `platform`/`spawnFn`/`killFn`.
|
|
66
|
+
*/
|
|
67
|
+
export interface JobObjectBindings {
|
|
68
|
+
/** `HANDLE CreateJobObjectW(LPSECURITY_ATTRIBUTES, LPCWSTR)` — both NULL for an unnamed, default-security job. */
|
|
69
|
+
createJobObjectW(attributes: Win32Handle, name: string | null): Win32Handle;
|
|
70
|
+
/** `BOOL SetInformationJobObject(HANDLE, JOBOBJECTINFOCLASS, LPVOID, DWORD)`. */
|
|
71
|
+
setInformationJobObject(job: Win32Handle, infoClass: number, info: Buffer, infoLength: number): number;
|
|
72
|
+
/** `HANDLE OpenProcess(DWORD, BOOL, DWORD)`. */
|
|
73
|
+
openProcess(desiredAccess: number, inheritHandle: number, pid: number): Win32Handle;
|
|
74
|
+
/** `BOOL AssignProcessToJobObject(HANDLE, HANDLE)`. */
|
|
75
|
+
assignProcessToJobObject(job: Win32Handle, process: Win32Handle): number;
|
|
76
|
+
/** `BOOL CloseHandle(HANDLE)`. */
|
|
77
|
+
closeHandle(handle: Win32Handle): number;
|
|
78
|
+
/** `DWORD GetLastError()`. */
|
|
79
|
+
getLastError(): number;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Resolve the kernel32 binding table, importing `koffi` on first use only.
|
|
83
|
+
*
|
|
84
|
+
* The import is dynamic and lives here alone so that merely importing
|
|
85
|
+
* `process-tree.ts` — which every adapter does, on every platform — never
|
|
86
|
+
* pulls a native addon into the process.
|
|
87
|
+
*/
|
|
88
|
+
export declare function loadBindings(): Promise<JobObjectBindings>;
|
|
89
|
+
export interface AssignOwnedProcessToJobOptions {
|
|
90
|
+
/** DI seam — defaults to the lazily loaded real kernel32 table. */
|
|
91
|
+
bindings?: JobObjectBindings;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Assign one owned runtime root to the daemon-wide kill-on-close job.
|
|
95
|
+
*
|
|
96
|
+
* Every failure throws {@link Win32JobObjectFailure} naming the step and
|
|
97
|
+
* carrying the Win32 error code. The process handle is opened solely to make
|
|
98
|
+
* the assignment and is closed in a `finally`, including on the failure path —
|
|
99
|
+
* the JOB holds the process afterwards, not this handle.
|
|
100
|
+
*/
|
|
101
|
+
export declare function assignOwnedProcessToJob(pid: number, options?: AssignOwnedProcessToJobOptions): Promise<void>;
|