one-code-extension 0.5.0 → 0.6.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 +1 -1
- package/docs/guide/differences-from-claude-code.md +9 -6
- package/docs/guide/installation.md +3 -4
- package/docs/guide/troubleshooting.md +1 -1
- package/extensions/auto-mode/denials.ts +3 -2
- package/extensions/background/cron.ts +127 -0
- package/extensions/background/index.ts +135 -47
- package/extensions/background/loop-fire.ts +39 -2
- package/extensions/background/switch-guard.ts +10 -6
- package/extensions/background/wakeup.ts +21 -2
- package/extensions/bash/index.ts +8 -30
- package/extensions/btw/index.ts +32 -18
- package/extensions/cache-marks/index.ts +42 -0
- package/extensions/claude-context/index.ts +157 -172
- package/extensions/compaction/index.ts +11 -8
- package/extensions/context-budget/budget.ts +16 -2
- package/extensions/context-budget/index.ts +15 -14
- package/extensions/context-management/index.ts +11 -28
- package/extensions/effort/index.ts +70 -7
- package/extensions/file-tracker/index.ts +111 -44
- package/extensions/file-tracker/replay.ts +76 -49
- package/extensions/file-tracker/restore.ts +12 -9
- package/extensions/file-tracker/tracker.ts +14 -1
- package/extensions/hooks/index.ts +60 -76
- package/extensions/hooks/prompt-context.ts +59 -0
- package/extensions/hooks/trust.ts +13 -7
- package/extensions/ledger/index.ts +46 -0
- package/extensions/ledger/wire.ts +154 -0
- package/extensions/lib/agent-cron.ts +17 -1
- package/extensions/lib/agent-loader.ts +3 -2
- package/extensions/lib/anthropic-auth.ts +98 -0
- package/extensions/lib/child-extensions.ts +14 -4
- package/extensions/lib/context-facts.ts +75 -56
- package/extensions/lib/covered-messages.ts +23 -0
- package/extensions/lib/deferred.ts +152 -12
- package/extensions/lib/ledger/fold-memo.ts +49 -0
- package/extensions/lib/ledger/fold.ts +265 -0
- package/extensions/lib/ledger/layout.ts +72 -0
- package/extensions/lib/ledger/plan.ts +102 -0
- package/extensions/lib/ledger/records.ts +276 -0
- package/extensions/lib/ledger/state.ts +47 -0
- package/extensions/lib/ledger/validate.ts +141 -0
- package/extensions/lib/ledger/wire.ts +632 -0
- package/extensions/lib/local-command.ts +24 -6
- package/extensions/lib/model-catalog/huggingface.json +5 -1
- package/extensions/lib/model-catalog/models-dev.json +10535 -10299
- package/extensions/lib/model-catalog/openrouter.json +191 -393
- package/extensions/lib/notifications.ts +57 -17
- package/extensions/lib/pi-version.ts +25 -8
- package/extensions/lib/plan-mode-channels.ts +29 -7
- package/extensions/lib/previous-mark.ts +101 -0
- package/extensions/lib/process-state.ts +48 -0
- package/extensions/lib/process-tree.ts +3 -2
- package/extensions/lib/reminders.ts +327 -621
- package/extensions/lib/request-replay.ts +186 -78
- package/extensions/lib/side-call.ts +11 -0
- package/extensions/lib/subagent-handback.ts +0 -1
- package/extensions/lib/tool-additions.ts +2 -22
- package/extensions/lib/turn-budget-layout.ts +18 -2
- package/extensions/lsp/index.ts +6 -8
- package/extensions/mcp/announce.ts +63 -98
- package/extensions/mcp/index.ts +51 -90
- package/extensions/mcp/trust.ts +13 -6
- package/extensions/model-default/index.ts +86 -19
- package/extensions/permissions/index.ts +157 -48
- package/extensions/permissions/modes.ts +5 -0
- package/extensions/plan-mode/index.ts +142 -45
- package/extensions/plan-mode/run-state.ts +26 -0
- package/extensions/plugins/index.ts +4 -1
- package/extensions/powershell/index.ts +2 -0
- package/extensions/recap/index.ts +11 -14
- package/extensions/search-tools/index.ts +2 -0
- package/extensions/skill/announce.ts +30 -0
- package/extensions/skill/index.ts +65 -48
- package/extensions/skill/invoke.ts +0 -67
- package/extensions/subagents/agents.ts +30 -8
- package/extensions/subagents/fork-cache.ts +9 -3
- package/extensions/subagents/index.ts +180 -107
- package/extensions/subagents/outcome.ts +3 -0
- package/extensions/subagents/parent-mode.ts +50 -0
- package/extensions/subagents/pending-claim.ts +18 -1
- package/extensions/subagents/runner.ts +29 -2
- package/extensions/subagents/runs.ts +5 -0
- package/extensions/system-prompt/index.ts +40 -6
- package/extensions/system-prompt/template.ts +10 -1
- package/extensions/system-reminder/index.ts +174 -202
- package/extensions/tool-search/index.ts +241 -167
- package/extensions/workflow/agent-session.ts +4 -1
- package/extensions/workflow/index.ts +16 -4
- package/extensions/workflow/run-manager.ts +4 -0
- package/extensions/worktree/index.ts +50 -14
- package/package.json +5 -2
- package/extensions/lib/context-stack.ts +0 -81
- package/extensions/subagents/announce.ts +0 -69
- package/extensions/tool-search/announce.ts +0 -58
|
@@ -29,6 +29,8 @@ import { CHILD_EXTENSION_PATHS } from "../lib/child-extensions.ts";
|
|
|
29
29
|
import { type HandbackSlot, SUBAGENT_HANDBACK, subagentHandbackExtension } from "../lib/subagent-handback.ts";
|
|
30
30
|
import type { RequestCapture } from "../lib/request-replay.ts";
|
|
31
31
|
import { forkCacheExtension } from "./fork-cache.ts";
|
|
32
|
+
import { parentModeExtension } from "./parent-mode.ts";
|
|
33
|
+
import { type DeferRequest, deferLikeParentExtension, injectedByParent } from "../lib/deferred.ts";
|
|
32
34
|
import type { ChildHandle, ChildOutcome, RpcChildHandle } from "./outcome.ts";
|
|
33
35
|
import { sendToMainTool } from "./send-to-main-tool.ts";
|
|
34
36
|
import { SessionTurnTracker } from "./session-turns.ts";
|
|
@@ -162,6 +164,10 @@ export class SubagentRuntime {
|
|
|
162
164
|
* still-connecting (possibly empty) one.
|
|
163
165
|
*/
|
|
164
166
|
private readonly getMcpTools: () => ToolDefinition[] | Promise<ToolDefinition[]>;
|
|
167
|
+
/** The tools the parent session defers (lib/deferred.ts queryDeferred): a child defers the ones injected into it the same way. */
|
|
168
|
+
private readonly getParentDeferred: () => DeferRequest[];
|
|
169
|
+
/** The main session's live permission mode, for a fork: told to it when it differs from what it was told (parent-mode.ts). */
|
|
170
|
+
private readonly getParentMode: (() => string) | undefined;
|
|
165
171
|
/**
|
|
166
172
|
* The parent's permission decision closure (undefined until permissions
|
|
167
173
|
* publishes it), wrapped so every call carries the asking run's name: the
|
|
@@ -200,8 +206,12 @@ export class SubagentRuntime {
|
|
|
200
206
|
onExtensionError: (runName: string | undefined, error: ExtensionError) => void,
|
|
201
207
|
onModelUnusable: (model: string, reason: string) => void,
|
|
202
208
|
onChildWrite: (path: string, runName: string | undefined) => void,
|
|
209
|
+
getParentDeferred: () => DeferRequest[],
|
|
210
|
+
getParentMode: (() => string) | undefined,
|
|
203
211
|
) {
|
|
204
212
|
this.modelRuntime = modelRuntime;
|
|
213
|
+
this.getParentDeferred = getParentDeferred;
|
|
214
|
+
this.getParentMode = getParentMode;
|
|
205
215
|
this.onChildWrite = onChildWrite;
|
|
206
216
|
this.baseCwd = baseCwd;
|
|
207
217
|
this.getMcpTools = getMcpTools;
|
|
@@ -231,11 +241,13 @@ export class SubagentRuntime {
|
|
|
231
241
|
onExtensionError: (runName: string | undefined, error: ExtensionError) => void = () => {},
|
|
232
242
|
onModelUnusable: (model: string, reason: string) => void = () => {},
|
|
233
243
|
onChildWrite: (path: string, runName: string | undefined) => void = () => {},
|
|
244
|
+
getParentDeferred: () => DeferRequest[] = () => [],
|
|
245
|
+
getParentMode?: () => string,
|
|
234
246
|
): Promise<SubagentRuntime> {
|
|
235
247
|
const modelRuntime = await createSharedModelRuntime(getAgentDir());
|
|
236
248
|
// Prime the catalog once; spawns read the live snapshot (see resolveModel).
|
|
237
249
|
await modelRuntime.getAvailable();
|
|
238
|
-
return new SubagentRuntime(modelRuntime, cwd, getMcpTools, getPermissionBridge, getHookBridge, onExtensionError, onModelUnusable, onChildWrite);
|
|
250
|
+
return new SubagentRuntime(modelRuntime, cwd, getMcpTools, getPermissionBridge, getHookBridge, onExtensionError, onModelUnusable, onChildWrite, getParentDeferred, getParentMode);
|
|
239
251
|
}
|
|
240
252
|
|
|
241
253
|
/**
|
|
@@ -275,7 +287,11 @@ export class SubagentRuntime {
|
|
|
275
287
|
// lib/subagent-handback.ts). Inline extensions load after the paths
|
|
276
288
|
// above, so the fork-cache one, last, sees the final body.
|
|
277
289
|
extraFactories: [
|
|
290
|
+
// The tools injected below (the spawn and cron tools, the shared MCP
|
|
291
|
+
// tools) are deferred as the parent defers them: their owners run there.
|
|
292
|
+
deferLikeParentExtension(this.getParentDeferred(), injectedByParent(spec.extraTools ?? [])),
|
|
278
293
|
subagentHandbackExtension(handback, { deferred: fork }),
|
|
294
|
+
...(fork && this.getParentMode ? [parentModeExtension(this.getParentMode)] : []),
|
|
279
295
|
...(fork && spec.parentRequest ? [forkCacheExtension(spec.parentRequest)] : []),
|
|
280
296
|
],
|
|
281
297
|
// claude-context (above) injects the CLAUDE.md instructions on the child's session_start;
|
|
@@ -390,7 +406,10 @@ export class SubagentRuntime {
|
|
|
390
406
|
* agent's own prompt and toolset.
|
|
391
407
|
*/
|
|
392
408
|
private async buildChildSession(spec: ChildSessionSpec, handback: HandbackSlot): Promise<{ session: Session; note?: string }> {
|
|
393
|
-
|
|
409
|
+
// The MCP wait first: the loader takes the parent's deferrals, which a server
|
|
410
|
+
// connecting during the wait adds to (it is instant once the connects settled).
|
|
411
|
+
const mcpTools = await this.getMcpTools();
|
|
412
|
+
const loader = await buildAgentLoader(this.childLoaderOptions(spec, handback));
|
|
394
413
|
const newSessionManager = () => newChildSessionManager(spec);
|
|
395
414
|
// A fork keeps the parent's toolset; only a named agent carries tool lists.
|
|
396
415
|
const agent = spec.forkFrom ? undefined : spec.agent;
|
|
@@ -659,6 +678,14 @@ export class SubagentRuntime {
|
|
|
659
678
|
await session.steer(message);
|
|
660
679
|
return "steered";
|
|
661
680
|
},
|
|
681
|
+
// The background extension is not loaded in a child, so the parent's
|
|
682
|
+
// cron fires record what they sent straight into the child's session.
|
|
683
|
+
conversation: {
|
|
684
|
+
getBranch: () => session.sessionManager.getBranch(),
|
|
685
|
+
appendEntry: (customType, data) => {
|
|
686
|
+
session.sessionManager.appendCustomEntry(customType, data);
|
|
687
|
+
},
|
|
688
|
+
},
|
|
662
689
|
busy: () => turnActive || !session.isIdle,
|
|
663
690
|
exited: () => exited,
|
|
664
691
|
kill: () => {
|
|
@@ -140,6 +140,11 @@ export class RunRegistry {
|
|
|
140
140
|
this.reserved.clear();
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
/** Keep only the records `keep` accepts; names held for runs being spawned stay held. */
|
|
144
|
+
retain(keep: (record: AgentRunRecord) => boolean): void {
|
|
145
|
+
for (const record of [...this.byId.values()]) if (!keep(record)) this.remove(record.taskId);
|
|
146
|
+
}
|
|
147
|
+
|
|
143
148
|
/** Hold a name until its record is added. */
|
|
144
149
|
reserve(name: string): void {
|
|
145
150
|
this.reserved.add(name);
|
|
@@ -11,16 +11,22 @@
|
|
|
11
11
|
* which a model that takes a mid-conversation system message gets there
|
|
12
12
|
* instead (system-reminder). The scratchpad path the block names embeds the
|
|
13
13
|
* session id — derived at session_start, constant within the session.
|
|
14
|
+
*
|
|
15
|
+
* Both are live state with facts: the scratchpad the block names and whether
|
|
16
|
+
* the run is one-shot. Inside an epoch message 1 is the record, so a /fork
|
|
17
|
+
* (a new session id, so a new scratchpad) or a session continued in another
|
|
18
|
+
* run mode is announced on the request's tail, live minus what the epoch
|
|
19
|
+
* told (RENDER_CHANNEL), never by rewriting message 1.
|
|
14
20
|
*/
|
|
15
21
|
|
|
16
22
|
import { getCurrentSystemMessage } from "@earendil-works/pi-ai";
|
|
17
23
|
import type { BuildSystemPromptOptions, ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
18
24
|
import { environmentBlock, modelLine } from "../lib/environment-block.ts";
|
|
19
25
|
import { GIT_SNAPSHOT_OWNER_CHANNEL } from "../lib/git-status.ts";
|
|
20
|
-
import { oneShotSessionNote, sessionOutlivesTurn } from "../lib/notifications.ts";
|
|
26
|
+
import { interactiveSessionNote, oneShotSessionNote, sessionOutlivesTurn } from "../lib/notifications.ts";
|
|
21
27
|
import { taskToolsEnabled } from "../lib/model-tier.ts";
|
|
22
28
|
import { sessionModelTier } from "../lib/session-model-tier.ts";
|
|
23
|
-
import { CONTEXT_ORDER, REMINDER_CHANNEL } from "../lib/reminders.ts";
|
|
29
|
+
import { CONTEXT_ORDER, REMINDER_CHANNEL, RENDER_CHANNEL, type RenderRequest, toldBlockFact } from "../lib/reminders.ts";
|
|
24
30
|
import { privateSessionScratchpadDir } from "../lib/scratchpad.ts";
|
|
25
31
|
import { collectEnvironment, type EnvironmentInfo } from "./environment.ts";
|
|
26
32
|
import { totalTokensBlock, turnTokenBudget } from "../context-budget/budget.ts";
|
|
@@ -29,6 +35,16 @@ import { buildClaudeCodeSystemPrompt } from "./template.ts";
|
|
|
29
35
|
import { WORKSPACE_CHANNEL, type WorkspaceAnnouncement } from "../lib/workspace-channel.ts";
|
|
30
36
|
import { PROMPT_OPTIONS_CHANNEL, type PromptOptionsAnnouncement, SYSTEM_PROMPT_REQUEST_CHANNEL, type SystemPromptRequest } from "../lib/prompt-options.ts";
|
|
31
37
|
|
|
38
|
+
/** The told fact of the scratchpad the environment block names. */
|
|
39
|
+
const SCRATCHPAD_FACT = "environment:scratchpad";
|
|
40
|
+
/** The told mode of the one-shot run note. */
|
|
41
|
+
const ONE_SHOT_MODE = "one-shot";
|
|
42
|
+
|
|
43
|
+
/** The notice that this session's scratchpad is not the one message 1 names (a /fork is a new session). */
|
|
44
|
+
function scratchpadChangedNote(dir: string): string {
|
|
45
|
+
return `This session's scratchpad directory is now ${dir}. Use it for temporary files instead of the scratchpad named earlier in this conversation.`;
|
|
46
|
+
}
|
|
47
|
+
|
|
32
48
|
export default function systemPromptExtension(pi: ExtensionAPI) {
|
|
33
49
|
const requestTier = sessionModelTier(pi);
|
|
34
50
|
let cachedEnv: EnvironmentInfo | undefined;
|
|
@@ -40,6 +56,8 @@ export default function systemPromptExtension(pi: ExtensionAPI) {
|
|
|
40
56
|
let workspaceDirs: string[] = [];
|
|
41
57
|
/** The options the last before_agent_start saw; a turn opened from idle rebuilds from them. */
|
|
42
58
|
let lastOptions: BuildSystemPromptOptions | undefined;
|
|
59
|
+
/** Whether this run is one-shot (`-p`, json): the turn_start that queued the blocks saw it. */
|
|
60
|
+
let oneShotRun = false;
|
|
43
61
|
pi.events.on(WORKSPACE_CHANNEL, (data) => {
|
|
44
62
|
const dirs = (data as WorkspaceAnnouncement | undefined)?.dirs;
|
|
45
63
|
workspaceDirs = Array.isArray(dirs) ? dirs.filter((dir): dir is string => typeof dir === "string") : [];
|
|
@@ -88,24 +106,26 @@ export default function systemPromptExtension(pi: ExtensionAPI) {
|
|
|
88
106
|
const env = environment(ctx.cwd);
|
|
89
107
|
pi.events.emit(REMINDER_CHANNEL, {
|
|
90
108
|
text: environmentBlock({ ...env, scratchpadDir: scratchpad, workspaceDirs }),
|
|
91
|
-
scope: "every-turn",
|
|
92
109
|
key: "environment",
|
|
93
110
|
placement: "first-prepend",
|
|
94
111
|
order: CONTEXT_ORDER.environment,
|
|
112
|
+
facts: [toldBlockFact(SCRATCHPAD_FACT, scratchpad ?? "")],
|
|
95
113
|
});
|
|
96
|
-
|
|
114
|
+
oneShotRun = !sessionOutlivesTurn(ctx.mode);
|
|
115
|
+
if (oneShotRun) {
|
|
97
116
|
pi.events.emit(REMINDER_CHANNEL, {
|
|
98
117
|
text: oneShotSessionNote(),
|
|
99
|
-
scope: "every-turn",
|
|
100
118
|
key: "one-shot",
|
|
101
119
|
placement: "first-prepend",
|
|
102
120
|
order: CONTEXT_ORDER.oneShot,
|
|
121
|
+
facts: [{ kind: "mode", key: ONE_SHOT_MODE, on: true }],
|
|
103
122
|
});
|
|
123
|
+
} else {
|
|
124
|
+
pi.events.emit(REMINDER_CHANNEL, { key: "one-shot", remove: true });
|
|
104
125
|
}
|
|
105
126
|
if (ctx.model) {
|
|
106
127
|
pi.events.emit(REMINDER_CHANNEL, {
|
|
107
128
|
text: modelLine(ctx.model),
|
|
108
|
-
scope: "every-turn",
|
|
109
129
|
key: "model-line",
|
|
110
130
|
placement: "first-prepend",
|
|
111
131
|
order: CONTEXT_ORDER.modelLine,
|
|
@@ -113,6 +133,20 @@ export default function systemPromptExtension(pi: ExtensionAPI) {
|
|
|
113
133
|
}
|
|
114
134
|
});
|
|
115
135
|
|
|
136
|
+
// Inside an epoch, what changed since message 1 was rendered goes on the tail.
|
|
137
|
+
pi.events.on(RENDER_CHANNEL, (data) => {
|
|
138
|
+
const request = data as RenderRequest;
|
|
139
|
+
if (request.epoch || lastOptions?.customPrompt) return;
|
|
140
|
+
const told = request.toldBlocks.get(SCRATCHPAD_FACT);
|
|
141
|
+
const live = toldBlockFact(SCRATCHPAD_FACT, scratchpad ?? "");
|
|
142
|
+
if (told !== undefined && told !== live.hash && scratchpad) {
|
|
143
|
+
request.blocks.push({ text: scratchpadChangedNote(scratchpad), facts: [live] });
|
|
144
|
+
}
|
|
145
|
+
if ((request.modes.get(ONE_SHOT_MODE) ?? false) !== oneShotRun) {
|
|
146
|
+
request.blocks.push({ text: oneShotRun ? oneShotSessionNote() : interactiveSessionNote(), facts: [{ kind: "mode", key: ONE_SHOT_MODE, on: oneShotRun }] });
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
116
150
|
// A copy: pi goes on mutating this object after the handlers return
|
|
117
151
|
// (forceSystemPrompt, the live selectedTools).
|
|
118
152
|
const copyOptions = (options: BuildSystemPromptOptions): BuildSystemPromptOptions => ({
|
|
@@ -39,7 +39,16 @@ const BUNDLES: Record<PromptTier, PromptBundle> = {
|
|
|
39
39
|
|
|
40
40
|
function buildToolsSection(options: BuildSystemPromptOptions): string {
|
|
41
41
|
const tools = options.selectedTools ?? ["read", "bash", "edit", "write"];
|
|
42
|
-
|
|
42
|
+
// In registration order (the snippets' own), never activation order: a tool
|
|
43
|
+
// withdrawn and brought back (a model hop through another tier) is appended
|
|
44
|
+
// to pi's active list, and a prompt built from that order would depend on
|
|
45
|
+
// the process's history instead of the branch (R3, the fresh-process oracle).
|
|
46
|
+
const order = Object.keys(options.toolSnippets ?? {});
|
|
47
|
+
const rank = (name: string) => {
|
|
48
|
+
const at = order.indexOf(name);
|
|
49
|
+
return at === -1 ? order.length : at;
|
|
50
|
+
};
|
|
51
|
+
const visible = tools.filter((name) => !!options.toolSnippets?.[name]).sort((a, b) => rank(a) - rank(b));
|
|
43
52
|
const toolsList =
|
|
44
53
|
visible.length > 0 ? visible.map((name) => `- ${name}: ${options.toolSnippets![name]}`).join("\n") : "(none)";
|
|
45
54
|
|