@asterxsk/kiln 0.1.0 → 0.2.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/LICENSE +21 -21
- package/README.md +170 -170
- package/agent/AGENTS.md +67 -67
- package/agent/README.md +5 -5
- package/agent/extensions/AGENTS.md +68 -68
- package/agent/extensions/ask-user/index.ts +418 -418
- package/agent/extensions/ask-user/package-lock.json +769 -769
- package/agent/extensions/ask-user/package.json +19 -19
- package/agent/extensions/ask-user/prompt.ts +45 -45
- package/agent/extensions/ask-user/tsconfig.json +7 -7
- package/agent/extensions/background-terminals/docs/implementation-guide.md +942 -942
- package/agent/extensions/background-terminals/index.ts +627 -627
- package/agent/extensions/background-terminals/manager.test.ts +735 -735
- package/agent/extensions/background-terminals/output.test.ts +109 -109
- package/agent/extensions/background-terminals/package-lock.json +769 -769
- package/agent/extensions/background-terminals/package.json +17 -17
- package/agent/extensions/background-terminals/prompt.test.ts +125 -125
- package/agent/extensions/background-terminals/ps.test.ts +82 -82
- package/agent/extensions/background-terminals/result-delivery.test.ts +44 -44
- package/agent/extensions/background-terminals/src/domain.ts +87 -87
- package/agent/extensions/background-terminals/src/manager.ts +907 -907
- package/agent/extensions/background-terminals/src/output.ts +84 -84
- package/agent/extensions/background-terminals/src/prompt.ts +142 -142
- package/agent/extensions/background-terminals/src/result-delivery.ts +27 -27
- package/agent/extensions/background-terminals/src/runtime.ts +36 -36
- package/agent/extensions/background-terminals/src/ui/output-view.ts +79 -79
- package/agent/extensions/background-terminals/src/ui/ps.ts +621 -621
- package/agent/extensions/background-terminals/tsconfig.json +7 -7
- package/agent/extensions/file-search/index.spec.ts +443 -443
- package/agent/extensions/file-search/index.ts +459 -459
- package/agent/extensions/file-search/package-lock.json +2253 -2253
- package/agent/extensions/file-search/package.json +23 -23
- package/agent/extensions/file-search/src/args.ts +122 -122
- package/agent/extensions/file-search/src/binaries.ts +422 -422
- package/agent/extensions/file-search/src/output.ts +126 -126
- package/agent/extensions/file-search/src/process.ts +146 -146
- package/agent/extensions/file-search/src/prompt.ts +52 -52
- package/agent/extensions/file-search/tsconfig.json +7 -7
- package/agent/extensions/modelconf/PLAN.md +915 -915
- package/agent/extensions/modelconf/index.ts +296 -296
- package/agent/extensions/modelconf/src/ui/ModelConfView.ts +1101 -1101
- package/agent/extensions/pi-web-access/CHANGELOG.md +690 -690
- package/agent/extensions/pi-web-access/LICENSE +21 -21
- package/agent/extensions/pi-web-access/README.md +470 -470
- package/agent/extensions/pi-web-access/SECURITY.md +5 -5
- package/agent/extensions/pi-web-access/activity.ts +101 -101
- package/agent/extensions/pi-web-access/auth-fetch.ts +148 -148
- package/agent/extensions/pi-web-access/brightdata-unlocker.ts +272 -272
- package/agent/extensions/pi-web-access/chrome-cookies.ts +669 -669
- package/agent/extensions/pi-web-access/content-find.ts +139 -139
- package/agent/extensions/pi-web-access/credential-source.ts +191 -191
- package/agent/extensions/pi-web-access/data-uri-sanitize.ts +406 -406
- package/agent/extensions/pi-web-access/datalab-pdf-extract.ts +568 -568
- package/agent/extensions/pi-web-access/declared-web-links.ts +173 -173
- package/agent/extensions/pi-web-access/evidence/CONTRACT-EVIDENCE.md +496 -496
- package/agent/extensions/pi-web-access/evidence/contract-probe.mjs +140 -140
- package/agent/extensions/pi-web-access/exa.ts +526 -526
- package/agent/extensions/pi-web-access/extract.ts +1196 -1196
- package/agent/extensions/pi-web-access/feature-config.ts +29 -29
- package/agent/extensions/pi-web-access/fetch-params.ts +111 -111
- package/agent/extensions/pi-web-access/gemini-adc.ts +298 -298
- package/agent/extensions/pi-web-access/gemini-api.ts +353 -353
- package/agent/extensions/pi-web-access/gemini-pdf-extract.ts +108 -108
- package/agent/extensions/pi-web-access/gemini-url-context.ts +128 -128
- package/agent/extensions/pi-web-access/gemini-web-config.ts +101 -101
- package/agent/extensions/pi-web-access/gemini-web.ts +487 -487
- package/agent/extensions/pi-web-access/github-api.ts +197 -197
- package/agent/extensions/pi-web-access/github-extract.ts +746 -746
- package/agent/extensions/pi-web-access/github-issue-pr.ts +700 -700
- package/agent/extensions/pi-web-access/index.ts +1737 -1737
- package/agent/extensions/pi-web-access/package-lock.json +5808 -5808
- package/agent/extensions/pi-web-access/package.json +64 -64
- package/agent/extensions/pi-web-access/page-query.ts +96 -96
- package/agent/extensions/pi-web-access/pdf-extract.ts +409 -409
- package/agent/extensions/pi-web-access/promise-try.d.ts +7 -7
- package/agent/extensions/pi-web-access/query-rewrite.ts +51 -51
- package/agent/extensions/pi-web-access/render-search-error.ts +170 -170
- package/agent/extensions/pi-web-access/rsc-extract.ts +338 -338
- package/agent/extensions/pi-web-access/source-check.ts +282 -282
- package/agent/extensions/pi-web-access/ssrf-protection.ts +526 -526
- package/agent/extensions/pi-web-access/storage.ts +521 -521
- package/agent/extensions/pi-web-access/summary-model-scope.ts +125 -125
- package/agent/extensions/pi-web-access/test/auth-fetch.test.mjs +208 -208
- package/agent/extensions/pi-web-access/test/brightdata-unlocker.test.mjs +840 -840
- package/agent/extensions/pi-web-access/test/chrome-cookie-extraction.test.mjs +441 -441
- package/agent/extensions/pi-web-access/test/config-path.test.mjs +283 -283
- package/agent/extensions/pi-web-access/test/content-find.test.mjs +25 -25
- package/agent/extensions/pi-web-access/test/credential-source.test.mjs +118 -118
- package/agent/extensions/pi-web-access/test/data-uri-sanitize.test.mjs +210 -210
- package/agent/extensions/pi-web-access/test/datalab-pdf-extract.test.mjs +552 -552
- package/agent/extensions/pi-web-access/test/declared-web-links.test.mjs +212 -212
- package/agent/extensions/pi-web-access/test/fetch-answer-storage.test.mjs +40 -40
- package/agent/extensions/pi-web-access/test/fetch-cache-storage.test.mjs +334 -334
- package/agent/extensions/pi-web-access/test/fetch-content-domain-policy.test.mjs +95 -95
- package/agent/extensions/pi-web-access/test/fetch-modes.test.mjs +53 -53
- package/agent/extensions/pi-web-access/test/fetch-not-found-guidance.test.mjs +92 -92
- package/agent/extensions/pi-web-access/test/fetch-params.test.mjs +86 -86
- package/agent/extensions/pi-web-access/test/fetch-render-call.test.mjs +34 -34
- package/agent/extensions/pi-web-access/test/fetch-routing.test.mjs +173 -173
- package/agent/extensions/pi-web-access/test/gemini-adc-auth.test.mjs +257 -257
- package/agent/extensions/pi-web-access/test/gemini-api-transport.test.mjs +170 -170
- package/agent/extensions/pi-web-access/test/gemini-pdf-extract.test.mjs +133 -133
- package/agent/extensions/pi-web-access/test/gemini-web-cookie-opt-in.test.mjs +178 -178
- package/agent/extensions/pi-web-access/test/gemini-web-header-overflow.test.mjs +148 -148
- package/agent/extensions/pi-web-access/test/get-search-content.test.mjs +223 -223
- package/agent/extensions/pi-web-access/test/github-extract.test.mjs +378 -378
- package/agent/extensions/pi-web-access/test/github-issue-pr.test.mjs +565 -565
- package/agent/extensions/pi-web-access/test/inline-content-config.test.mjs +99 -99
- package/agent/extensions/pi-web-access/test/lazy-extract-load.test.mjs +118 -118
- package/agent/extensions/pi-web-access/test/local-video-oversize.test.mjs +52 -52
- package/agent/extensions/pi-web-access/test/package-typebox-dependency.test.mjs +50 -50
- package/agent/extensions/pi-web-access/test/page-query.test.mjs +51 -51
- package/agent/extensions/pi-web-access/test/pdf-config.test.mjs +140 -140
- package/agent/extensions/pi-web-access/test/pdf-extract.test.mjs +500 -500
- package/agent/extensions/pi-web-access/test/proxy-transport.test.mjs +286 -286
- package/agent/extensions/pi-web-access/test/query-rewrite.test.mjs +52 -52
- package/agent/extensions/pi-web-access/test/rsc-fallback.test.mjs +102 -102
- package/agent/extensions/pi-web-access/test/search-error-render.test.mjs +152 -152
- package/agent/extensions/pi-web-access/test/search-providers.test.mjs +274 -274
- package/agent/extensions/pi-web-access/test/source-check.test.mjs +179 -179
- package/agent/extensions/pi-web-access/test/ssrf-allow-ranges-config.test.mjs +205 -205
- package/agent/extensions/pi-web-access/test/ssrf-protection.test.mjs +456 -456
- package/agent/extensions/pi-web-access/test/tool-registration-config.test.mjs +182 -182
- package/agent/extensions/pi-web-access/test/youtube-extract-errors.test.mjs +64 -64
- package/agent/extensions/pi-web-access/tsconfig.json +11 -11
- package/agent/extensions/pi-web-access/utils.ts +451 -451
- package/agent/extensions/pi-web-access/video-extract.ts +392 -392
- package/agent/extensions/pi-web-access/youtube-extract.ts +328 -328
- package/agent/extensions/shared/activity-status.ts +31 -31
- package/agent/extensions/shared/child-session.test.ts +270 -270
- package/agent/extensions/shared/child-session.ts +148 -148
- package/agent/extensions/shared/context-utilization.test.ts +48 -48
- package/agent/extensions/shared/context-utilization.ts +47 -47
- package/agent/extensions/shared/dashboard-state.ts +99 -99
- package/agent/extensions/shared/tool-call-timeout.test.ts +117 -117
- package/agent/extensions/shared/tool-call-timeout.ts +104 -104
- package/agent/extensions/subagents/by-the-way.test.ts +29 -29
- package/agent/extensions/subagents/claude.test.ts +119 -119
- package/agent/extensions/subagents/codex.test.ts +102 -102
- package/agent/extensions/subagents/context-usage.test.ts +107 -107
- package/agent/extensions/subagents/docs/design-plan.md +568 -568
- package/agent/extensions/subagents/docs/effect-v4-extension-guide.md +354 -354
- package/agent/extensions/subagents/docs/effect-v4-notes.md +571 -571
- package/agent/extensions/subagents/index.ts +779 -779
- package/agent/extensions/subagents/manager.test.ts +276 -276
- package/agent/extensions/subagents/package-lock.json +2244 -2244
- package/agent/extensions/subagents/package.json +19 -19
- package/agent/extensions/subagents/result-delivery.test.ts +27 -27
- package/agent/extensions/subagents/src/backend.ts +73 -73
- package/agent/extensions/subagents/src/backends/claude.ts +701 -701
- package/agent/extensions/subagents/src/backends/codex.ts +1060 -1060
- package/agent/extensions/subagents/src/backends/pi.ts +575 -575
- package/agent/extensions/subagents/src/backends/stub.ts +300 -300
- package/agent/extensions/subagents/src/by-the-way.ts +21 -21
- package/agent/extensions/subagents/src/domain.ts +253 -253
- package/agent/extensions/subagents/src/format.ts +74 -74
- package/agent/extensions/subagents/src/manager.ts +736 -736
- package/agent/extensions/subagents/src/prompt.ts +92 -92
- package/agent/extensions/subagents/src/result-delivery.ts +20 -20
- package/agent/extensions/subagents/src/runtime.ts +53 -53
- package/agent/extensions/subagents/src/ui/takeover.ts +583 -583
- package/agent/extensions/subagents/src/ui/transcript.ts +201 -201
- package/agent/extensions/subagents/takeover.test.ts +29 -29
- package/agent/extensions/subagents/tsconfig.json +7 -7
- package/agent/extensions/todo/AGENTS.md +38 -38
- package/agent/extensions/todo/LICENSE +21 -21
- package/agent/extensions/todo/config.ts +55 -55
- package/agent/extensions/todo/index.ts +151 -151
- package/agent/extensions/todo/locales/de.json +17 -17
- package/agent/extensions/todo/locales/en.json +15 -15
- package/agent/extensions/todo/locales/es.json +17 -17
- package/agent/extensions/todo/locales/fr.json +17 -17
- package/agent/extensions/todo/locales/pt-BR.json +17 -17
- package/agent/extensions/todo/locales/pt.json +17 -17
- package/agent/extensions/todo/locales/ru.json +17 -17
- package/agent/extensions/todo/locales/uk.json +17 -17
- package/agent/extensions/todo/locales/zh.json +17 -17
- package/agent/extensions/todo/package-lock.json +3358 -3358
- package/agent/extensions/todo/package.json +67 -67
- package/agent/extensions/todo/state/i18n-bridge.ts +64 -64
- package/agent/extensions/todo/state/invariants.ts +20 -20
- package/agent/extensions/todo/state/replay.ts +38 -38
- package/agent/extensions/todo/state/selectors.ts +107 -107
- package/agent/extensions/todo/state/state-reducer.ts +326 -326
- package/agent/extensions/todo/state/state.ts +18 -18
- package/agent/extensions/todo/state/store.ts +82 -82
- package/agent/extensions/todo/state/task-graph.ts +57 -57
- package/agent/extensions/todo/todo-overlay.ts +200 -200
- package/agent/extensions/todo/todo.ts +155 -155
- package/agent/extensions/todo/tool/response-envelope.ts +109 -109
- package/agent/extensions/todo/tool/types.ts +206 -206
- package/agent/extensions/todo/view/format.ts +177 -177
- package/agent/install.ps1 +637 -527
- package/agent/install.sh +620 -511
- package/agent/keybindings.json +7 -7
- package/bin/kiln.js +124 -11
- package/package.json +8 -2
- package/agent/extensions/taste/index.ts +0 -443
- package/agent/extensions/taste/install.ps1 +0 -23
- package/agent/extensions/taste/install.sh +0 -21
- /package/agent/extensions/{status line → statusline}/index.ts +0 -0
- /package/agent/extensions/{status line → statusline}/install.ps1 +0 -0
- /package/agent/extensions/{status line → statusline}/install.sh +0 -0
|
@@ -1,148 +1,148 @@
|
|
|
1
|
-
import * as path from "node:path";
|
|
2
|
-
import {
|
|
3
|
-
DefaultResourceLoader,
|
|
4
|
-
getAgentDir,
|
|
5
|
-
ProjectTrustStore,
|
|
6
|
-
SettingsManager,
|
|
7
|
-
type AgentSession,
|
|
8
|
-
type SessionShutdownEvent,
|
|
9
|
-
} from "@earendil-works/pi-coding-agent";
|
|
10
|
-
|
|
11
|
-
const CHILD_SHUTDOWN_TIMEOUT_MS = 5_000;
|
|
12
|
-
|
|
13
|
-
/** Tools that headless children must not receive. Everything else stays enabled. */
|
|
14
|
-
export const CHILD_EXCLUDED_TOOL_NAMES = [
|
|
15
|
-
"subagent_spawn",
|
|
16
|
-
"subagent_wait",
|
|
17
|
-
"subagent_cancel",
|
|
18
|
-
"subagent_check",
|
|
19
|
-
"subagent_list",
|
|
20
|
-
"workflow",
|
|
21
|
-
"ask_user",
|
|
22
|
-
] as const;
|
|
23
|
-
|
|
24
|
-
/** Fresh SDK options avoid turning the denylist into an accidental allowlist. */
|
|
25
|
-
export function childToolPolicy() {
|
|
26
|
-
return { excludeTools: [...CHILD_EXCLUDED_TOOL_NAMES] };
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface ChildResourceOptions {
|
|
30
|
-
cwd: string;
|
|
31
|
-
projectTrusted: boolean;
|
|
32
|
-
appendSystemPrompt?: string[];
|
|
33
|
-
agentDir?: string;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/** Load normal global/package resources and trust-gated project resources. */
|
|
37
|
-
export async function createChildResources(options: ChildResourceOptions) {
|
|
38
|
-
const agentDir = options.agentDir ?? getAgentDir();
|
|
39
|
-
const settingsManager = SettingsManager.create(options.cwd, agentDir, {
|
|
40
|
-
projectTrusted: options.projectTrusted,
|
|
41
|
-
});
|
|
42
|
-
const loader = new DefaultResourceLoader({
|
|
43
|
-
cwd: options.cwd,
|
|
44
|
-
agentDir,
|
|
45
|
-
settingsManager,
|
|
46
|
-
...(options.appendSystemPrompt
|
|
47
|
-
? { appendSystemPrompt: options.appendSystemPrompt }
|
|
48
|
-
: {}),
|
|
49
|
-
});
|
|
50
|
-
await loader.reload();
|
|
51
|
-
return { loader, settingsManager };
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Same-directory children inherit the live parent decision. An alternate cwd
|
|
56
|
-
* is trusted only when Pi's persisted trust store explicitly trusts it (or a
|
|
57
|
-
* containing directory); unreadable/invalid trust data fails closed.
|
|
58
|
-
*/
|
|
59
|
-
export function resolveStandaloneChildProjectTrust(options: {
|
|
60
|
-
parentCwd: string;
|
|
61
|
-
childCwd: string;
|
|
62
|
-
parentTrusted: boolean;
|
|
63
|
-
agentDir?: string;
|
|
64
|
-
}) {
|
|
65
|
-
if (path.resolve(options.childCwd) === path.resolve(options.parentCwd)) {
|
|
66
|
-
return options.parentTrusted;
|
|
67
|
-
}
|
|
68
|
-
try {
|
|
69
|
-
const trustStore = new ProjectTrustStore(options.agentDir ?? getAgentDir());
|
|
70
|
-
return trustStore.get(options.childCwd) === true;
|
|
71
|
-
} catch {
|
|
72
|
-
return false;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/** Start child extension session hooks/resources in headless print mode. */
|
|
77
|
-
export async function bindChildSessionExtensions(
|
|
78
|
-
session: Pick<AgentSession, "bindExtensions">,
|
|
79
|
-
) {
|
|
80
|
-
await session.bindExtensions({ mode: "print" });
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
interface ChildExtensionRunner {
|
|
84
|
-
hasHandlers(eventType: string): boolean;
|
|
85
|
-
emit(event: SessionShutdownEvent): Promise<unknown>;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export interface DisposableChildSession {
|
|
89
|
-
readonly extensionRunner: ChildExtensionRunner;
|
|
90
|
-
dispose(): void;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const childShutdowns = new WeakMap<object, Promise<void>>();
|
|
94
|
-
|
|
95
|
-
function waitBounded(operation: Promise<unknown>, timeoutMs: number) {
|
|
96
|
-
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
97
|
-
const timeout = new Promise<void>((resolve) => {
|
|
98
|
-
timer = setTimeout(resolve, timeoutMs);
|
|
99
|
-
});
|
|
100
|
-
return Promise.race([
|
|
101
|
-
operation.then(
|
|
102
|
-
() => undefined,
|
|
103
|
-
() => undefined,
|
|
104
|
-
),
|
|
105
|
-
timeout,
|
|
106
|
-
])
|
|
107
|
-
.catch(() => {})
|
|
108
|
-
.finally(() => {
|
|
109
|
-
if (timer) clearTimeout(timer);
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Emit child session_shutdown once, then dispose once. Hook failures and a
|
|
115
|
-
* bounded hook deadline never prevent disposal.
|
|
116
|
-
*/
|
|
117
|
-
export function shutdownAndDisposeChildSession(
|
|
118
|
-
session: DisposableChildSession,
|
|
119
|
-
options: { timeoutMs?: number } = {},
|
|
120
|
-
) {
|
|
121
|
-
const existing = childShutdowns.get(session);
|
|
122
|
-
if (existing) return existing;
|
|
123
|
-
|
|
124
|
-
const shutdown = (async () => {
|
|
125
|
-
try {
|
|
126
|
-
if (session.extensionRunner.hasHandlers("session_shutdown")) {
|
|
127
|
-
await waitBounded(
|
|
128
|
-
session.extensionRunner.emit({
|
|
129
|
-
type: "session_shutdown",
|
|
130
|
-
reason: "quit",
|
|
131
|
-
}),
|
|
132
|
-
options.timeoutMs ?? CHILD_SHUTDOWN_TIMEOUT_MS,
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
} catch {
|
|
136
|
-
// Extension runner inspection/emission is best-effort during teardown.
|
|
137
|
-
} finally {
|
|
138
|
-
try {
|
|
139
|
-
session.dispose();
|
|
140
|
-
} catch {
|
|
141
|
-
// Disposal is terminal and must remain idempotent for callers.
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
})();
|
|
145
|
-
|
|
146
|
-
childShutdowns.set(session, shutdown);
|
|
147
|
-
return shutdown;
|
|
148
|
-
}
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
import {
|
|
3
|
+
DefaultResourceLoader,
|
|
4
|
+
getAgentDir,
|
|
5
|
+
ProjectTrustStore,
|
|
6
|
+
SettingsManager,
|
|
7
|
+
type AgentSession,
|
|
8
|
+
type SessionShutdownEvent,
|
|
9
|
+
} from "@earendil-works/pi-coding-agent";
|
|
10
|
+
|
|
11
|
+
const CHILD_SHUTDOWN_TIMEOUT_MS = 5_000;
|
|
12
|
+
|
|
13
|
+
/** Tools that headless children must not receive. Everything else stays enabled. */
|
|
14
|
+
export const CHILD_EXCLUDED_TOOL_NAMES = [
|
|
15
|
+
"subagent_spawn",
|
|
16
|
+
"subagent_wait",
|
|
17
|
+
"subagent_cancel",
|
|
18
|
+
"subagent_check",
|
|
19
|
+
"subagent_list",
|
|
20
|
+
"workflow",
|
|
21
|
+
"ask_user",
|
|
22
|
+
] as const;
|
|
23
|
+
|
|
24
|
+
/** Fresh SDK options avoid turning the denylist into an accidental allowlist. */
|
|
25
|
+
export function childToolPolicy() {
|
|
26
|
+
return { excludeTools: [...CHILD_EXCLUDED_TOOL_NAMES] };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ChildResourceOptions {
|
|
30
|
+
cwd: string;
|
|
31
|
+
projectTrusted: boolean;
|
|
32
|
+
appendSystemPrompt?: string[];
|
|
33
|
+
agentDir?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Load normal global/package resources and trust-gated project resources. */
|
|
37
|
+
export async function createChildResources(options: ChildResourceOptions) {
|
|
38
|
+
const agentDir = options.agentDir ?? getAgentDir();
|
|
39
|
+
const settingsManager = SettingsManager.create(options.cwd, agentDir, {
|
|
40
|
+
projectTrusted: options.projectTrusted,
|
|
41
|
+
});
|
|
42
|
+
const loader = new DefaultResourceLoader({
|
|
43
|
+
cwd: options.cwd,
|
|
44
|
+
agentDir,
|
|
45
|
+
settingsManager,
|
|
46
|
+
...(options.appendSystemPrompt
|
|
47
|
+
? { appendSystemPrompt: options.appendSystemPrompt }
|
|
48
|
+
: {}),
|
|
49
|
+
});
|
|
50
|
+
await loader.reload();
|
|
51
|
+
return { loader, settingsManager };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Same-directory children inherit the live parent decision. An alternate cwd
|
|
56
|
+
* is trusted only when Pi's persisted trust store explicitly trusts it (or a
|
|
57
|
+
* containing directory); unreadable/invalid trust data fails closed.
|
|
58
|
+
*/
|
|
59
|
+
export function resolveStandaloneChildProjectTrust(options: {
|
|
60
|
+
parentCwd: string;
|
|
61
|
+
childCwd: string;
|
|
62
|
+
parentTrusted: boolean;
|
|
63
|
+
agentDir?: string;
|
|
64
|
+
}) {
|
|
65
|
+
if (path.resolve(options.childCwd) === path.resolve(options.parentCwd)) {
|
|
66
|
+
return options.parentTrusted;
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
const trustStore = new ProjectTrustStore(options.agentDir ?? getAgentDir());
|
|
70
|
+
return trustStore.get(options.childCwd) === true;
|
|
71
|
+
} catch {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Start child extension session hooks/resources in headless print mode. */
|
|
77
|
+
export async function bindChildSessionExtensions(
|
|
78
|
+
session: Pick<AgentSession, "bindExtensions">,
|
|
79
|
+
) {
|
|
80
|
+
await session.bindExtensions({ mode: "print" });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
interface ChildExtensionRunner {
|
|
84
|
+
hasHandlers(eventType: string): boolean;
|
|
85
|
+
emit(event: SessionShutdownEvent): Promise<unknown>;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface DisposableChildSession {
|
|
89
|
+
readonly extensionRunner: ChildExtensionRunner;
|
|
90
|
+
dispose(): void;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const childShutdowns = new WeakMap<object, Promise<void>>();
|
|
94
|
+
|
|
95
|
+
function waitBounded(operation: Promise<unknown>, timeoutMs: number) {
|
|
96
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
97
|
+
const timeout = new Promise<void>((resolve) => {
|
|
98
|
+
timer = setTimeout(resolve, timeoutMs);
|
|
99
|
+
});
|
|
100
|
+
return Promise.race([
|
|
101
|
+
operation.then(
|
|
102
|
+
() => undefined,
|
|
103
|
+
() => undefined,
|
|
104
|
+
),
|
|
105
|
+
timeout,
|
|
106
|
+
])
|
|
107
|
+
.catch(() => {})
|
|
108
|
+
.finally(() => {
|
|
109
|
+
if (timer) clearTimeout(timer);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Emit child session_shutdown once, then dispose once. Hook failures and a
|
|
115
|
+
* bounded hook deadline never prevent disposal.
|
|
116
|
+
*/
|
|
117
|
+
export function shutdownAndDisposeChildSession(
|
|
118
|
+
session: DisposableChildSession,
|
|
119
|
+
options: { timeoutMs?: number } = {},
|
|
120
|
+
) {
|
|
121
|
+
const existing = childShutdowns.get(session);
|
|
122
|
+
if (existing) return existing;
|
|
123
|
+
|
|
124
|
+
const shutdown = (async () => {
|
|
125
|
+
try {
|
|
126
|
+
if (session.extensionRunner.hasHandlers("session_shutdown")) {
|
|
127
|
+
await waitBounded(
|
|
128
|
+
session.extensionRunner.emit({
|
|
129
|
+
type: "session_shutdown",
|
|
130
|
+
reason: "quit",
|
|
131
|
+
}),
|
|
132
|
+
options.timeoutMs ?? CHILD_SHUTDOWN_TIMEOUT_MS,
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
} catch {
|
|
136
|
+
// Extension runner inspection/emission is best-effort during teardown.
|
|
137
|
+
} finally {
|
|
138
|
+
try {
|
|
139
|
+
session.dispose();
|
|
140
|
+
} catch {
|
|
141
|
+
// Disposal is terminal and must remain idempotent for callers.
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
})();
|
|
145
|
+
|
|
146
|
+
childShutdowns.set(session, shutdown);
|
|
147
|
+
return shutdown;
|
|
148
|
+
}
|
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { test } from "node:test";
|
|
3
|
-
import {
|
|
4
|
-
contextPercent,
|
|
5
|
-
formatContextUtilization,
|
|
6
|
-
} from "./context-utilization.ts";
|
|
7
|
-
|
|
8
|
-
test("formats current context occupancy against model capacity", () => {
|
|
9
|
-
assert.equal(
|
|
10
|
-
formatContextUtilization({ tokens: 26_040, contextWindow: 372_000 }),
|
|
11
|
-
"7%/372k",
|
|
12
|
-
);
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
test("formats the latest post-compaction usage rather than prior cumulative usage", () => {
|
|
16
|
-
const latestUsage = { tokens: 18_000, contextWindow: 200_000 };
|
|
17
|
-
assert.equal(formatContextUtilization(latestUsage), "9%/200k");
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
test("clamps over-capacity and nonsensical token values", () => {
|
|
21
|
-
assert.equal(
|
|
22
|
-
contextPercent({ tokens: 500_000, contextWindow: 200_000 }),
|
|
23
|
-
100,
|
|
24
|
-
);
|
|
25
|
-
assert.equal(
|
|
26
|
-
formatContextUtilization({
|
|
27
|
-
tokens: Number.POSITIVE_INFINITY,
|
|
28
|
-
contextWindow: 200_000,
|
|
29
|
-
}),
|
|
30
|
-
"?%/200k",
|
|
31
|
-
);
|
|
32
|
-
assert.equal(
|
|
33
|
-
formatContextUtilization({ tokens: -1, contextWindow: 200_000 }),
|
|
34
|
-
"?%/200k",
|
|
35
|
-
);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
test("handles missing usage or capacity without NaN or Infinity", () => {
|
|
39
|
-
assert.equal(
|
|
40
|
-
formatContextUtilization({ tokens: null, contextWindow: 372_000 }),
|
|
41
|
-
"?%/372k",
|
|
42
|
-
);
|
|
43
|
-
assert.equal(formatContextUtilization({ tokens: 12_000 }), "");
|
|
44
|
-
assert.equal(
|
|
45
|
-
formatContextUtilization({ tokens: 12_000, contextWindow: 0 }),
|
|
46
|
-
"",
|
|
47
|
-
);
|
|
48
|
-
});
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { test } from "node:test";
|
|
3
|
+
import {
|
|
4
|
+
contextPercent,
|
|
5
|
+
formatContextUtilization,
|
|
6
|
+
} from "./context-utilization.ts";
|
|
7
|
+
|
|
8
|
+
test("formats current context occupancy against model capacity", () => {
|
|
9
|
+
assert.equal(
|
|
10
|
+
formatContextUtilization({ tokens: 26_040, contextWindow: 372_000 }),
|
|
11
|
+
"7%/372k",
|
|
12
|
+
);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test("formats the latest post-compaction usage rather than prior cumulative usage", () => {
|
|
16
|
+
const latestUsage = { tokens: 18_000, contextWindow: 200_000 };
|
|
17
|
+
assert.equal(formatContextUtilization(latestUsage), "9%/200k");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test("clamps over-capacity and nonsensical token values", () => {
|
|
21
|
+
assert.equal(
|
|
22
|
+
contextPercent({ tokens: 500_000, contextWindow: 200_000 }),
|
|
23
|
+
100,
|
|
24
|
+
);
|
|
25
|
+
assert.equal(
|
|
26
|
+
formatContextUtilization({
|
|
27
|
+
tokens: Number.POSITIVE_INFINITY,
|
|
28
|
+
contextWindow: 200_000,
|
|
29
|
+
}),
|
|
30
|
+
"?%/200k",
|
|
31
|
+
);
|
|
32
|
+
assert.equal(
|
|
33
|
+
formatContextUtilization({ tokens: -1, contextWindow: 200_000 }),
|
|
34
|
+
"?%/200k",
|
|
35
|
+
);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("handles missing usage or capacity without NaN or Infinity", () => {
|
|
39
|
+
assert.equal(
|
|
40
|
+
formatContextUtilization({ tokens: null, contextWindow: 372_000 }),
|
|
41
|
+
"?%/372k",
|
|
42
|
+
);
|
|
43
|
+
assert.equal(formatContextUtilization({ tokens: 12_000 }), "");
|
|
44
|
+
assert.equal(
|
|
45
|
+
formatContextUtilization({ tokens: 12_000, contextWindow: 0 }),
|
|
46
|
+
"",
|
|
47
|
+
);
|
|
48
|
+
});
|
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
/** Compact, defensive context-window utilization formatting for child agents. */
|
|
2
|
-
|
|
3
|
-
export interface ContextUtilization {
|
|
4
|
-
/** Current conversation context occupancy; null while it is unknown after compaction. */
|
|
5
|
-
tokens?: number | null;
|
|
6
|
-
/** Capacity of the model currently serving the conversation. */
|
|
7
|
-
contextWindow?: number | null;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
function usableTokens(value: number | null | undefined) {
|
|
11
|
-
return typeof value === "number" && Number.isFinite(value) && value >= 0
|
|
12
|
-
? value
|
|
13
|
-
: undefined;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function usableCapacity(value: number | null | undefined) {
|
|
17
|
-
return typeof value === "number" && Number.isFinite(value) && value > 0
|
|
18
|
-
? value
|
|
19
|
-
: undefined;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function contextPercent(usage: ContextUtilization) {
|
|
23
|
-
const tokens = usableTokens(usage.tokens);
|
|
24
|
-
const capacity = usableCapacity(usage.contextWindow);
|
|
25
|
-
if (tokens === undefined || capacity === undefined) return undefined;
|
|
26
|
-
return Math.round(Math.min(100, Math.max(0, (tokens / capacity) * 100)));
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function formatCompactTokens(count: number) {
|
|
30
|
-
if (count < 1000) return Math.round(count).toString();
|
|
31
|
-
if (count < 10000) return `${(count / 1000).toFixed(1)}k`;
|
|
32
|
-
if (count < 1000000) return `${Math.round(count / 1000)}k`;
|
|
33
|
-
return `${(count / 1000000).toFixed(1)}M`;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Render `%/capacity`. If occupancy is temporarily unknown (notably directly
|
|
38
|
-
* after compaction), retain the useful model capacity as `?%/capacity`. If no
|
|
39
|
-
* valid capacity is available, omit the statistic because no percentage can
|
|
40
|
-
* be computed.
|
|
41
|
-
*/
|
|
42
|
-
export function formatContextUtilization(usage: ContextUtilization) {
|
|
43
|
-
const capacity = usableCapacity(usage.contextWindow);
|
|
44
|
-
if (capacity === undefined) return "";
|
|
45
|
-
const percent = contextPercent(usage);
|
|
46
|
-
return `${percent === undefined ? "?" : percent}%/${formatCompactTokens(capacity)}`;
|
|
47
|
-
}
|
|
1
|
+
/** Compact, defensive context-window utilization formatting for child agents. */
|
|
2
|
+
|
|
3
|
+
export interface ContextUtilization {
|
|
4
|
+
/** Current conversation context occupancy; null while it is unknown after compaction. */
|
|
5
|
+
tokens?: number | null;
|
|
6
|
+
/** Capacity of the model currently serving the conversation. */
|
|
7
|
+
contextWindow?: number | null;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function usableTokens(value: number | null | undefined) {
|
|
11
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0
|
|
12
|
+
? value
|
|
13
|
+
: undefined;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function usableCapacity(value: number | null | undefined) {
|
|
17
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0
|
|
18
|
+
? value
|
|
19
|
+
: undefined;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function contextPercent(usage: ContextUtilization) {
|
|
23
|
+
const tokens = usableTokens(usage.tokens);
|
|
24
|
+
const capacity = usableCapacity(usage.contextWindow);
|
|
25
|
+
if (tokens === undefined || capacity === undefined) return undefined;
|
|
26
|
+
return Math.round(Math.min(100, Math.max(0, (tokens / capacity) * 100)));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function formatCompactTokens(count: number) {
|
|
30
|
+
if (count < 1000) return Math.round(count).toString();
|
|
31
|
+
if (count < 10000) return `${(count / 1000).toFixed(1)}k`;
|
|
32
|
+
if (count < 1000000) return `${Math.round(count / 1000)}k`;
|
|
33
|
+
return `${(count / 1000000).toFixed(1)}M`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Render `%/capacity`. If occupancy is temporarily unknown (notably directly
|
|
38
|
+
* after compaction), retain the useful model capacity as `?%/capacity`. If no
|
|
39
|
+
* valid capacity is available, omit the statistic because no percentage can
|
|
40
|
+
* be computed.
|
|
41
|
+
*/
|
|
42
|
+
export function formatContextUtilization(usage: ContextUtilization) {
|
|
43
|
+
const capacity = usableCapacity(usage.contextWindow);
|
|
44
|
+
if (capacity === undefined) return "";
|
|
45
|
+
const percent = contextPercent(usage);
|
|
46
|
+
return `${percent === undefined ? "?" : percent}%/${formatCompactTokens(capacity)}`;
|
|
47
|
+
}
|