@oh-my-pi/pi-coding-agent 17.2.4 → 17.2.5
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/CHANGELOG.md +51 -0
- package/dist/{CHANGELOG-bpmhv26t.md → CHANGELOG-q2w43aw3.md} +51 -0
- package/dist/cli.js +4148 -3717
- package/dist/{template-8vdv6xb4.js → template-c2hyaytt.js} +8 -26
- package/dist/types/cli/args.d.ts +4 -1
- package/dist/types/cli/browser-relay-cli.d.ts +15 -0
- package/dist/types/cli/command-help.d.ts +99 -0
- package/dist/types/cli/help-extra.d.ts +2 -0
- package/dist/types/cli/thinking-levels.d.ts +5 -0
- package/dist/types/cli/update-cli.d.ts +13 -0
- package/dist/types/commands/browser-relay.d.ts +38 -0
- package/dist/types/commands/commit.d.ts +3 -0
- package/dist/types/commands/complete.d.ts +1 -1
- package/dist/types/commands/completions.d.ts +6 -0
- package/dist/types/commands/grep.d.ts +3 -0
- package/dist/types/commands/join.d.ts +4 -0
- package/dist/types/commands/launch-help.d.ts +171 -0
- package/dist/types/commands/launch.d.ts +5 -1
- package/dist/types/commands/models.d.ts +3 -0
- package/dist/types/commands/say.d.ts +10 -0
- package/dist/types/commands/worktree.d.ts +3 -0
- package/dist/types/config/keybindings.d.ts +5 -0
- package/dist/types/config/model-discovery.d.ts +2 -2
- package/dist/types/config/models-config-schema-bundle.d.ts +3 -0
- package/dist/types/config/models-config-schema.d.ts +2 -0
- package/dist/types/config/models-config.d.ts +1 -0
- package/dist/types/config/service-tier.d.ts +2 -0
- package/dist/types/config/settings-schema.d.ts +69 -21
- package/dist/types/cursor-bridge-tools.d.ts +1 -1
- package/dist/types/cursor.d.ts +7 -4
- package/dist/types/edit/index.d.ts +3 -3
- package/dist/types/edit/modes/replace.d.ts +22 -15
- package/dist/types/edit/renderer.d.ts +1 -1
- package/dist/types/eval/py/kernel.d.ts +3 -1
- package/dist/types/eval/py/spawn-options.d.ts +12 -16
- package/dist/types/extensibility/legacy-pi-ai-shim.d.ts +8 -1
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +1 -1
- package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +2 -0
- package/dist/types/launch/broker.d.ts +1 -1
- package/dist/types/launch/client.d.ts +6 -3
- package/dist/types/launch/ensure.d.ts +8 -0
- package/dist/types/launch/paths.d.ts +1 -1
- package/dist/types/launch/protocol.d.ts +2 -2
- package/dist/types/lsp/client.d.ts +2 -0
- package/dist/types/lsp/mux/daemon.d.ts +15 -0
- package/dist/types/lsp/mux/protocol.d.ts +57 -0
- package/dist/types/lsp/mux/server.d.ts +18 -0
- package/dist/types/lsp/types.d.ts +22 -2
- package/dist/types/main.d.ts +2 -0
- package/dist/types/mnemopi/embed-client.d.ts +1 -1
- package/dist/types/mnemopi/state.d.ts +0 -23
- package/dist/types/modes/components/assistant-message.d.ts +2 -0
- package/dist/types/modes/components/custom-editor.d.ts +2 -1
- package/dist/types/modes/components/read-tool-group.d.ts +2 -0
- package/dist/types/modes/components/stripped-tool-calls-placeholder.d.ts +12 -0
- package/dist/types/modes/components/tool-execution.d.ts +2 -0
- package/dist/types/modes/controllers/input-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +1 -0
- package/dist/types/modes/types.d.ts +1 -0
- package/dist/types/modes/workflow.d.ts +2 -1
- package/dist/types/sdk.d.ts +3 -1
- package/dist/types/secrets/index.d.ts +1 -1
- package/dist/types/session/agent-session-types.d.ts +3 -1
- package/dist/types/session/indexed-session-storage.d.ts +1 -0
- package/dist/types/session/session-advisors.d.ts +1 -1
- package/dist/types/session/session-context.d.ts +2 -1
- package/dist/types/session/session-maintenance.d.ts +1 -0
- package/dist/types/session/session-manager.d.ts +16 -11
- package/dist/types/session/session-stats.d.ts +10 -0
- package/dist/types/session/session-storage.d.ts +15 -3
- package/dist/types/system-prompt.d.ts +4 -1
- package/dist/types/task/index.d.ts +2 -1
- package/dist/types/task/spawn-policy.d.ts +5 -0
- package/dist/types/thinking.d.ts +1 -7
- package/dist/types/tools/ast-grep.d.ts +1 -1
- package/dist/types/tools/browser/registry.d.ts +2 -1
- package/dist/types/tools/browser/relay/bridge.d.ts +35 -0
- package/dist/types/tools/browser/relay/daemon.d.ts +16 -0
- package/dist/types/tools/browser/relay/kind.d.ts +19 -0
- package/dist/types/tools/browser/relay/protocol.d.ts +97 -0
- package/dist/types/tools/browser/relay/server.d.ts +35 -0
- package/dist/types/tools/browser/render.d.ts +1 -0
- package/dist/types/tools/browser.d.ts +3 -0
- package/dist/types/tools/computer/exposure.d.ts +9 -7
- package/dist/types/tools/computer/protocol.d.ts +70 -17
- package/dist/types/tools/computer/supervisor.d.ts +23 -10
- package/dist/types/tools/computer/worker-entry.d.ts +1 -0
- package/dist/types/tools/computer/worker.d.ts +42 -7
- package/dist/types/tools/computer-renderer.d.ts +7 -5
- package/dist/types/tools/computer.d.ts +25 -44
- package/dist/types/tools/context.d.ts +3 -0
- package/dist/types/tools/glob.d.ts +1 -1
- package/dist/types/tools/grep.d.ts +1 -1
- package/dist/types/tools/render-utils.d.ts +2 -0
- package/dist/types/tools/{browser/run-cancellation.d.ts → run-scope.d.ts} +8 -8
- package/dist/types/tools/tool-timeouts.d.ts +5 -0
- package/dist/types/tools/xdev.d.ts +8 -0
- package/dist/types/utils/clipboard.d.ts +2 -2
- package/dist/types/web/kagi.d.ts +1 -0
- package/dist/types/web/search/index.d.ts +1 -1
- package/dist/types/web/search/providers/anthropic.d.ts +1 -0
- package/dist/types/web/search/providers/base.d.ts +2 -0
- package/dist/types/web/search/providers/brave.d.ts +1 -0
- package/dist/types/web/search/providers/browser-page.d.ts +1 -0
- package/dist/types/web/search/providers/codex.d.ts +1 -0
- package/dist/types/web/search/providers/exa.d.ts +1 -0
- package/dist/types/web/search/providers/firecrawl.d.ts +1 -0
- package/dist/types/web/search/providers/gemini.d.ts +1 -0
- package/dist/types/web/search/providers/jina.d.ts +1 -0
- package/dist/types/web/search/providers/kagi.d.ts +1 -0
- package/dist/types/web/search/providers/kimi.d.ts +1 -0
- package/dist/types/web/search/providers/parallel.d.ts +1 -0
- package/dist/types/web/search/providers/perplexity.d.ts +1 -0
- package/dist/types/web/search/providers/searxng.d.ts +1 -0
- package/dist/types/web/search/providers/tavily.d.ts +1 -0
- package/dist/types/web/search/providers/tinyfish.d.ts +1 -0
- package/dist/types/web/search/providers/utils.d.ts +6 -9
- package/dist/types/web/search/providers/zai.d.ts +1 -0
- package/dist/types/web/search/types.d.ts +4 -0
- package/package.json +12 -12
- package/src/cli/args.ts +7 -87
- package/src/cli/browser-relay-cli.ts +119 -0
- package/src/cli/command-help.ts +103 -0
- package/src/cli/completion-gen.ts +5 -5
- package/src/cli/flag-tables.ts +9 -0
- package/src/cli/help-extra.ts +89 -0
- package/src/cli/setup-cli.ts +13 -21
- package/src/cli/thinking-levels.ts +7 -0
- package/src/cli/update-cli.ts +26 -7
- package/src/cli-commands.ts +170 -33
- package/src/cli.ts +17 -5
- package/src/commands/acp.ts +3 -1
- package/src/commands/agents.ts +3 -2
- package/src/commands/auth-broker.ts +3 -2
- package/src/commands/auth-gateway.ts +3 -2
- package/src/commands/bench.ts +2 -3
- package/src/commands/browser-relay.ts +53 -0
- package/src/commands/cleanse.ts +2 -2
- package/src/commands/commit.ts +3 -2
- package/src/commands/complete.ts +2 -1
- package/src/commands/completions.ts +3 -2
- package/src/commands/config.ts +3 -2
- package/src/commands/dry-balance.ts +2 -2
- package/src/commands/gallery.ts +3 -2
- package/src/commands/gc.ts +3 -2
- package/src/commands/grep.ts +3 -2
- package/src/commands/grievances.ts +3 -2
- package/src/commands/install.ts +2 -2
- package/src/commands/join.ts +3 -2
- package/src/commands/launch-help.ts +116 -0
- package/src/commands/launch.ts +7 -191
- package/src/commands/models.ts +3 -2
- package/src/commands/plugin.ts +3 -2
- package/src/commands/read.ts +3 -2
- package/src/commands/say.ts +3 -2
- package/src/commands/setup.ts +3 -2
- package/src/commands/shell.ts +3 -2
- package/src/commands/ssh.ts +3 -2
- package/src/commands/stats.ts +3 -2
- package/src/commands/tiny-models.ts +2 -2
- package/src/commands/token.ts +2 -2
- package/src/commands/ttsr.ts +2 -2
- package/src/commands/update.ts +3 -2
- package/src/commands/usage.ts +3 -2
- package/src/commands/web-search.ts +3 -2
- package/src/commands/worktree.ts +3 -2
- package/src/config/keybindings.ts +5 -0
- package/src/config/model-discovery.ts +30 -13
- package/src/config/model-registry.ts +9 -5
- package/src/config/models-config-schema-bundle.ts +9 -0
- package/src/config/resolve-config-value.ts +2 -1
- package/src/config/service-tier.ts +5 -0
- package/src/config/settings-schema.ts +72 -20
- package/src/cursor-bridge-tools.ts +1 -1
- package/src/cursor.ts +10 -14
- package/src/edit/diff.ts +17 -8
- package/src/edit/index.ts +20 -7
- package/src/edit/modes/replace.ts +93 -40
- package/src/edit/renderer.ts +1 -1
- package/src/edit/streaming.ts +14 -17
- package/src/eval/py/kernel.ts +2 -1
- package/src/eval/py/spawn-options.ts +24 -29
- package/src/export/html/template.js +8 -26
- package/src/extensibility/extensions/wrapper.ts +1 -0
- package/src/extensibility/legacy-pi-ai-shim.ts +22 -1
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +7 -6
- package/src/extensibility/plugins/legacy-pi-compat.ts +124 -57
- package/src/extensibility/skills.ts +11 -0
- package/src/extensibility/utils.ts +10 -2
- package/src/launch/broker.ts +1 -1
- package/src/launch/client.ts +32 -13
- package/src/launch/ensure.ts +73 -0
- package/src/launch/paths.ts +1 -1
- package/src/launch/protocol.ts +2 -2
- package/src/lsp/client.ts +23 -2
- package/src/lsp/index.ts +8 -0
- package/src/lsp/mux/daemon.ts +348 -0
- package/src/lsp/mux/protocol.ts +96 -0
- package/src/lsp/mux/server.ts +797 -0
- package/src/lsp/types.ts +28 -2
- package/src/main.ts +7 -2
- package/src/mcp/oauth-flow.ts +11 -1
- package/src/mnemopi/embed-client.ts +49 -2
- package/src/mnemopi/state.ts +57 -23
- package/src/modes/components/assistant-message.ts +11 -0
- package/src/modes/components/chat-transcript-builder.ts +4 -0
- package/src/modes/components/custom-editor.ts +9 -0
- package/src/modes/components/read-tool-group.ts +10 -0
- package/src/modes/components/status-line/component.ts +1 -1
- package/src/modes/components/stripped-tool-calls-placeholder.ts +35 -0
- package/src/modes/components/tool-execution.ts +8 -0
- package/src/modes/components/tree-selector.ts +46 -48
- package/src/modes/controllers/event-controller.ts +17 -0
- package/src/modes/controllers/input-controller.ts +39 -1
- package/src/modes/controllers/mcp-command-controller.ts +11 -5
- package/src/modes/controllers/selector-controller.ts +20 -0
- package/src/modes/interactive-mode.ts +2 -0
- package/src/modes/types.ts +1 -0
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/modes/utils/interactive-context-helpers.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +6 -10
- package/src/modes/workflow.ts +8 -2
- package/src/prompts/agents/init.md +1 -1
- package/src/prompts/system/plan-mode-active.md +2 -2
- package/src/prompts/system/system-prompt.md +26 -52
- package/src/prompts/system/workflow-notice.md +3 -3
- package/src/prompts/tools/ast-grep.md +1 -1
- package/src/prompts/tools/bash.md +1 -1
- package/src/prompts/tools/browser.md +1 -0
- package/src/prompts/tools/computer.md +21 -21
- package/src/prompts/tools/glob.md +1 -1
- package/src/prompts/tools/grep.md +1 -1
- package/src/prompts/tools/replace.md +5 -4
- package/src/prompts/tools/task.md +4 -4
- package/src/sdk.ts +34 -6
- package/src/secrets/index.ts +1 -1
- package/src/session/agent-session-types.ts +3 -1
- package/src/session/agent-session.ts +13 -1
- package/src/session/indexed-session-storage.ts +9 -0
- package/src/session/prewalk.ts +24 -2
- package/src/session/session-advisors.ts +2 -2
- package/src/session/session-context.ts +16 -12
- package/src/session/session-dump-format.ts +1 -1
- package/src/session/session-maintenance.ts +38 -4
- package/src/session/session-manager.ts +102 -67
- package/src/session/session-paths.ts +118 -55
- package/src/session/session-stats.ts +48 -3
- package/src/session/session-storage.ts +30 -22
- package/src/session/stream-guards.ts +1 -1
- package/src/slash-commands/builtin-registry.ts +27 -24
- package/src/system-prompt.ts +8 -3
- package/src/task/index.ts +23 -8
- package/src/task/spawn-policy.ts +14 -0
- package/src/thinking.ts +2 -8
- package/src/tools/ast-grep.ts +10 -4
- package/src/tools/browser/cmux/cmux-tab.ts +5 -10
- package/src/tools/browser/cmux/rpc.ts +2 -8
- package/src/tools/browser/registry.ts +53 -2
- package/src/tools/browser/relay/bridge.ts +945 -0
- package/src/tools/browser/relay/daemon.ts +117 -0
- package/src/tools/browser/relay/extension-assets/background.js.txt +242 -0
- package/src/tools/browser/relay/extension-assets/manifest.json.txt +14 -0
- package/src/tools/browser/relay/extension-assets/options.html.txt +53 -0
- package/src/tools/browser/relay/extension-assets/options.js.txt +23 -0
- package/src/tools/browser/relay/kind.ts +41 -0
- package/src/tools/browser/relay/protocol.ts +54 -0
- package/src/tools/browser/relay/server.ts +141 -0
- package/src/tools/browser/render.ts +4 -1
- package/src/tools/browser/shared-daemon.ts +7 -58
- package/src/tools/browser/tab-supervisor.ts +5 -4
- package/src/tools/browser/tab-worker.ts +33 -12
- package/src/tools/browser.ts +26 -1
- package/src/tools/computer/exposure.ts +11 -35
- package/src/tools/computer/protocol.ts +51 -10
- package/src/tools/computer/supervisor.ts +227 -124
- package/src/tools/computer/worker-entry.ts +11 -6
- package/src/tools/computer/worker.ts +695 -81
- package/src/tools/computer-renderer.ts +104 -65
- package/src/tools/computer.ts +134 -428
- package/src/tools/context.ts +3 -0
- package/src/tools/glob.ts +9 -2
- package/src/tools/grep.ts +12 -6
- package/src/tools/inspect-image.ts +32 -6
- package/src/tools/render-utils.ts +2 -0
- package/src/tools/{browser/run-cancellation.ts → run-scope.ts} +11 -11
- package/src/tools/tool-timeouts.ts +1 -0
- package/src/tools/xdev.ts +29 -2
- package/src/utils/clipboard.ts +40 -14
- package/src/utils/command-args.ts +26 -28
- package/src/utils/tool-choice.ts +0 -13
- package/src/web/kagi.ts +2 -1
- package/src/web/search/index.ts +18 -2
- package/src/web/search/providers/anthropic.ts +4 -1
- package/src/web/search/providers/base.ts +2 -0
- package/src/web/search/providers/brave.ts +3 -1
- package/src/web/search/providers/browser-page.ts +6 -4
- package/src/web/search/providers/codex.ts +4 -1
- package/src/web/search/providers/duckduckgo.ts +2 -1
- package/src/web/search/providers/ecosia.ts +2 -1
- package/src/web/search/providers/exa.ts +4 -2
- package/src/web/search/providers/firecrawl.ts +3 -1
- package/src/web/search/providers/gemini.ts +8 -2
- package/src/web/search/providers/google.ts +2 -1
- package/src/web/search/providers/jina.ts +12 -2
- package/src/web/search/providers/kagi.ts +3 -0
- package/src/web/search/providers/kimi.ts +5 -1
- package/src/web/search/providers/mojeek.ts +2 -1
- package/src/web/search/providers/parallel.ts +5 -1
- package/src/web/search/providers/perplexity.ts +6 -3
- package/src/web/search/providers/public.ts +1 -1
- package/src/web/search/providers/searxng.ts +11 -5
- package/src/web/search/providers/startpage.ts +10 -4
- package/src/web/search/providers/synthetic.ts +11 -5
- package/src/web/search/providers/tavily.ts +3 -1
- package/src/web/search/providers/tinyfish.ts +3 -1
- package/src/web/search/providers/utils.ts +12 -10
- package/src/web/search/providers/xai.ts +2 -2
- package/src/web/search/providers/zai.ts +18 -3
- package/src/web/search/types.ts +6 -0
|
@@ -2,10 +2,17 @@ import * as fs from "node:fs";
|
|
|
2
2
|
import * as os from "node:os";
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import { getTerminalId } from "@oh-my-pi/pi-tui";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
getSessionsDir,
|
|
7
|
+
getTerminalSessionsDir,
|
|
8
|
+
isEnoent,
|
|
9
|
+
isRecord,
|
|
10
|
+
logger,
|
|
11
|
+
resolveEquivalentPath,
|
|
12
|
+
} from "@oh-my-pi/pi-utils";
|
|
6
13
|
import type { SessionStorage } from "./session-storage";
|
|
7
14
|
|
|
8
|
-
const
|
|
15
|
+
const SESSION_HEADER_PREFIX_BYTES = 4096;
|
|
9
16
|
|
|
10
17
|
/**
|
|
11
18
|
* Merge or rename a legacy session directory into its canonical target.
|
|
@@ -35,12 +42,16 @@ function encodeLegacyAbsoluteSessionDirName(cwd: string): string {
|
|
|
35
42
|
return `--${resolvedCwd.replace(/^[/\\]/, "").replace(/[/\\:]/g, "-")}--`;
|
|
36
43
|
}
|
|
37
44
|
|
|
38
|
-
function
|
|
45
|
+
function encodeLegacyRelativeSessionDirName(prefix: string, relative: string): string {
|
|
39
46
|
const encoded = relative.replace(/[/\\:]/g, "-");
|
|
40
47
|
return encoded ? (prefix.endsWith("-") ? `${prefix}${encoded}` : `${prefix}-${encoded}`) : prefix;
|
|
41
48
|
}
|
|
42
49
|
|
|
43
|
-
function getDefaultSessionDirName(cwd: string): {
|
|
50
|
+
function getDefaultSessionDirName(cwd: string): {
|
|
51
|
+
encodedDirName: string;
|
|
52
|
+
legacyRelativeDirName: string | undefined;
|
|
53
|
+
resolvedCwd: string;
|
|
54
|
+
} {
|
|
44
55
|
const resolvedCwd = path.resolve(cwd);
|
|
45
56
|
const canonicalCwd = resolveEquivalentPath(resolvedCwd);
|
|
46
57
|
const home = os.homedir();
|
|
@@ -49,72 +60,111 @@ function getDefaultSessionDirName(cwd: string): { encodedDirName: string; resolv
|
|
|
49
60
|
const canonicalTempRoot = resolveEquivalentPath(tempRoot);
|
|
50
61
|
const homeRelative = path.relative(canonicalHome, canonicalCwd);
|
|
51
62
|
const tempRelative = path.relative(canonicalTempRoot, canonicalCwd);
|
|
52
|
-
const encodedDirName =
|
|
53
|
-
homeRelative === "" || (!homeRelative.startsWith("..") && !path.isAbsolute(homeRelative))
|
|
54
|
-
? encodeRelativeSessionDirName("-", homeRelative)
|
|
55
|
-
: tempRelative === "" || (!tempRelative.startsWith("..") && !path.isAbsolute(tempRelative))
|
|
56
|
-
? encodeRelativeSessionDirName("-tmp", tempRelative)
|
|
57
|
-
: encodeLegacyAbsoluteSessionDirName(canonicalCwd);
|
|
58
|
-
return { encodedDirName, resolvedCwd };
|
|
59
|
-
}
|
|
60
63
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (
|
|
67
|
-
|
|
64
|
+
let scope: "home" | "tmp" | "abs";
|
|
65
|
+
let legacyRelativeDirName: string | undefined;
|
|
66
|
+
if (homeRelative === "" || (!homeRelative.startsWith("..") && !path.isAbsolute(homeRelative))) {
|
|
67
|
+
scope = "home";
|
|
68
|
+
legacyRelativeDirName = encodeLegacyRelativeSessionDirName("-", homeRelative);
|
|
69
|
+
} else if (tempRelative === "" || (!tempRelative.startsWith("..") && !path.isAbsolute(tempRelative))) {
|
|
70
|
+
scope = "tmp";
|
|
71
|
+
legacyRelativeDirName = encodeLegacyRelativeSessionDirName("-tmp", tempRelative);
|
|
72
|
+
} else {
|
|
73
|
+
scope = "abs";
|
|
74
|
+
}
|
|
68
75
|
|
|
69
|
-
const
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
76
|
+
const normalized = canonicalCwd.replaceAll("\\", "/");
|
|
77
|
+
const readable = path
|
|
78
|
+
.basename(canonicalCwd)
|
|
79
|
+
.replace(/[^a-zA-Z0-9._-]+/g, "-")
|
|
80
|
+
.replace(/^-+|-+$/g, "")
|
|
81
|
+
.slice(-80);
|
|
82
|
+
const digest = Bun.SHA256.hash(normalized, "hex");
|
|
83
|
+
const encodedDirName = `${scope}-${readable || "project"}-${digest}`;
|
|
84
|
+
return { encodedDirName, legacyRelativeDirName, resolvedCwd };
|
|
85
|
+
}
|
|
73
86
|
|
|
74
|
-
|
|
87
|
+
function readSessionCwd(sessionFile: string, buffer: Buffer): string | undefined {
|
|
88
|
+
let descriptor: number | undefined;
|
|
75
89
|
try {
|
|
76
|
-
|
|
90
|
+
descriptor = fs.openSync(sessionFile, "r");
|
|
91
|
+
const bytesRead = fs.readSync(descriptor, buffer, 0, buffer.length, 0);
|
|
92
|
+
const prefix = buffer.toString("utf8", 0, bytesRead);
|
|
93
|
+
for (const line of prefix.split(/\r?\n/, 3)) {
|
|
94
|
+
try {
|
|
95
|
+
const record: unknown = JSON.parse(line);
|
|
96
|
+
if (isRecord(record) && record.type === "session" && typeof record.cwd === "string") {
|
|
97
|
+
return record.cwd;
|
|
98
|
+
}
|
|
99
|
+
} catch {
|
|
100
|
+
// Ignore title slots or truncated/corrupt headers.
|
|
101
|
+
}
|
|
102
|
+
}
|
|
77
103
|
} catch {
|
|
78
|
-
|
|
104
|
+
// Best-effort migration leaves unreadable sessions in the current bucket.
|
|
105
|
+
} finally {
|
|
106
|
+
if (descriptor !== undefined) fs.closeSync(descriptor);
|
|
79
107
|
}
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
80
110
|
|
|
111
|
+
function moveSessionBundle(sourceDir: string, targetDir: string, sessionName: string, entries: string[]): void {
|
|
112
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
113
|
+
const artifactsName = path.basename(sessionName, ".jsonl");
|
|
81
114
|
for (const entry of entries) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
115
|
+
if (entry !== sessionName && entry !== artifactsName && !entry.startsWith(`${sessionName}.`)) continue;
|
|
116
|
+
const source = path.join(sourceDir, entry);
|
|
117
|
+
if (!fs.existsSync(source)) continue;
|
|
118
|
+
const target = path.join(targetDir, entry);
|
|
119
|
+
const existing = fs.statSync(target, { throwIfNoEntry: false });
|
|
120
|
+
if (!existing) {
|
|
121
|
+
fs.renameSync(source, target);
|
|
122
|
+
} else if (existing.isDirectory() && fs.statSync(source).isDirectory()) {
|
|
123
|
+
migrateSessionDirPath(source, target);
|
|
87
124
|
} else {
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const newName = remainder ? `-${remainder}` : "-";
|
|
92
|
-
const oldPath = path.join(sessionsRoot, entry);
|
|
93
|
-
const newPath = path.join(sessionsRoot, newName);
|
|
94
|
-
|
|
95
|
-
try {
|
|
96
|
-
migrateSessionDirPath(oldPath, newPath);
|
|
97
|
-
} catch {
|
|
98
|
-
// Best effort
|
|
125
|
+
fs.rmSync(source, { recursive: true, force: true });
|
|
99
126
|
}
|
|
100
127
|
}
|
|
101
128
|
}
|
|
102
129
|
|
|
103
|
-
function
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
130
|
+
function rerouteCollidingSessions(
|
|
131
|
+
cwd: string,
|
|
132
|
+
legacyDir: string,
|
|
133
|
+
sessionsRoot: string,
|
|
134
|
+
kind: "relative" | "absolute",
|
|
135
|
+
): void {
|
|
136
|
+
const entries = fs.readdirSync(legacyDir);
|
|
137
|
+
const currentCwd = resolveEquivalentPath(path.resolve(cwd));
|
|
138
|
+
const buffer = Buffer.allocUnsafe(SESSION_HEADER_PREFIX_BYTES);
|
|
139
|
+
for (const entry of entries) {
|
|
140
|
+
if (!entry.endsWith(".jsonl")) continue;
|
|
141
|
+
const recordedCwd = readSessionCwd(path.join(legacyDir, entry), buffer);
|
|
142
|
+
if (!recordedCwd) continue;
|
|
143
|
+
const recordedCanonical = resolveEquivalentPath(path.resolve(recordedCwd));
|
|
144
|
+
if (
|
|
145
|
+
recordedCanonical === currentCwd ||
|
|
146
|
+
!fs.statSync(recordedCanonical, { throwIfNoEntry: false })?.isDirectory()
|
|
147
|
+
) {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
const recordedNames = getDefaultSessionDirName(recordedCanonical);
|
|
151
|
+
const recordedLegacyName =
|
|
152
|
+
kind === "relative"
|
|
153
|
+
? recordedNames.legacyRelativeDirName
|
|
154
|
+
: encodeLegacyAbsoluteSessionDirName(recordedCanonical);
|
|
155
|
+
if (recordedLegacyName !== path.basename(legacyDir)) continue;
|
|
156
|
+
moveSessionBundle(legacyDir, path.join(sessionsRoot, recordedNames.encodedDirName), entry, entries);
|
|
111
157
|
}
|
|
112
158
|
}
|
|
113
159
|
|
|
114
160
|
export function resolveManagedSessionRoot(sessionDir: string, cwd: string): string | undefined {
|
|
115
161
|
const currentDirName = path.basename(sessionDir);
|
|
116
|
-
const { encodedDirName } = getDefaultSessionDirName(cwd);
|
|
117
|
-
if (
|
|
162
|
+
const { encodedDirName, legacyRelativeDirName } = getDefaultSessionDirName(cwd);
|
|
163
|
+
if (
|
|
164
|
+
currentDirName !== encodedDirName &&
|
|
165
|
+
currentDirName !== legacyRelativeDirName &&
|
|
166
|
+
currentDirName !== encodeLegacyAbsoluteSessionDirName(cwd)
|
|
167
|
+
) {
|
|
118
168
|
return undefined;
|
|
119
169
|
}
|
|
120
170
|
return path.dirname(sessionDir);
|
|
@@ -130,10 +180,23 @@ export function computeDefaultSessionDir(
|
|
|
130
180
|
storage: SessionStorage,
|
|
131
181
|
sessionsRoot: string = getSessionsDir(),
|
|
132
182
|
): string {
|
|
133
|
-
const { encodedDirName, resolvedCwd } = getDefaultSessionDirName(cwd);
|
|
134
|
-
migrateHomeSessionDirs(sessionsRoot);
|
|
183
|
+
const { encodedDirName, legacyRelativeDirName, resolvedCwd } = getDefaultSessionDirName(cwd);
|
|
135
184
|
const sessionDir = path.join(sessionsRoot, encodedDirName);
|
|
136
|
-
|
|
185
|
+
const legacyDirs: Array<{ kind: "relative" | "absolute"; name: string | undefined }> = [
|
|
186
|
+
{ kind: "relative", name: legacyRelativeDirName },
|
|
187
|
+
{ kind: "absolute", name: encodeLegacyAbsoluteSessionDirName(resolvedCwd) },
|
|
188
|
+
];
|
|
189
|
+
for (const legacy of legacyDirs) {
|
|
190
|
+
if (!legacy.name) continue;
|
|
191
|
+
const legacyDir = path.join(sessionsRoot, legacy.name);
|
|
192
|
+
if (legacyDir === sessionDir || !fs.existsSync(legacyDir)) continue;
|
|
193
|
+
try {
|
|
194
|
+
rerouteCollidingSessions(resolvedCwd, legacyDir, sessionsRoot, legacy.kind);
|
|
195
|
+
migrateSessionDirPath(legacyDir, sessionDir);
|
|
196
|
+
} catch {
|
|
197
|
+
// Best effort
|
|
198
|
+
}
|
|
199
|
+
}
|
|
137
200
|
storage.ensureDirSync(sessionDir);
|
|
138
201
|
return sessionDir;
|
|
139
202
|
}
|
|
@@ -34,6 +34,11 @@ export interface SessionStatsTrackerHost {
|
|
|
34
34
|
sessionId(): string;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
function correctedPromptTokens(assistant: AssistantMessage): number {
|
|
38
|
+
const providerPromptTokens = assistant.contextSnapshot?.promptTokens ?? calculatePromptTokens(assistant.usage);
|
|
39
|
+
return Math.max(0, providerPromptTokens - (assistant.contextSnapshot?.historyRewriteTokensRemoved ?? 0));
|
|
40
|
+
}
|
|
41
|
+
|
|
37
42
|
/** Computes session totals and tracks the in-flight context estimate. */
|
|
38
43
|
export class SessionStatsTracker {
|
|
39
44
|
readonly #host: SessionStatsTrackerHost;
|
|
@@ -160,8 +165,7 @@ export class SessionStatsTracker {
|
|
|
160
165
|
const useAnchor =
|
|
161
166
|
anchorAssistant !== undefined && anchorIndex !== -1 && (!pending || anchorIndex >= pending.cutoffCount);
|
|
162
167
|
if (useAnchor && anchorAssistant) {
|
|
163
|
-
const promptTokens =
|
|
164
|
-
anchorAssistant.contextSnapshot?.promptTokens ?? calculatePromptTokens(anchorAssistant.usage);
|
|
168
|
+
const promptTokens = correctedPromptTokens(anchorAssistant);
|
|
165
169
|
const nonMessageTokens =
|
|
166
170
|
anchorAssistant.contextSnapshot?.nonMessageTokens ?? computeNonMessageTokens(this.#host.session);
|
|
167
171
|
anchored = true;
|
|
@@ -199,7 +203,7 @@ export class SessionStatsTracker {
|
|
|
199
203
|
) {
|
|
200
204
|
continue;
|
|
201
205
|
}
|
|
202
|
-
const promptTokens =
|
|
206
|
+
const promptTokens = correctedPromptTokens(message);
|
|
203
207
|
const nonMessageTokens =
|
|
204
208
|
message.contextSnapshot?.nonMessageTokens ?? computeNonMessageTokens(this.#host.session);
|
|
205
209
|
let tailTokens = 0;
|
|
@@ -256,6 +260,47 @@ export class SessionStatsTracker {
|
|
|
256
260
|
return this.#pendingContextSnapshot?.nonMessageTokens;
|
|
257
261
|
}
|
|
258
262
|
|
|
263
|
+
/**
|
|
264
|
+
* Apply an estimated prompt-prefix reduction to the current provider anchor.
|
|
265
|
+
*
|
|
266
|
+
* History after the anchor is estimated live by {@link getContextBreakdown};
|
|
267
|
+
* callers must pass only savings from entries already included in the
|
|
268
|
+
* anchor's provider-reported prompt. Persisting the correction on the
|
|
269
|
+
* assistant snapshot keeps reloads accurate, and the next successful
|
|
270
|
+
* assistant response naturally replaces it with a fresh provider anchor.
|
|
271
|
+
*/
|
|
272
|
+
recordAnchoredHistoryRewrite(tokensRemoved: number): void {
|
|
273
|
+
if (!Number.isFinite(tokensRemoved) || tokensRemoved <= 0) return;
|
|
274
|
+
|
|
275
|
+
const branchEntries = this.#host.sessionManager.getBranch();
|
|
276
|
+
const latestCompaction = getLatestCompactionEntry(branchEntries);
|
|
277
|
+
const compactionIndex = latestCompaction ? branchEntries.lastIndexOf(latestCompaction) : -1;
|
|
278
|
+
for (let index = branchEntries.length - 1; index > compactionIndex; index--) {
|
|
279
|
+
const entry = branchEntries[index];
|
|
280
|
+
if (entry.type !== "message" || entry.message.role !== "assistant") continue;
|
|
281
|
+
const assistant = entry.message;
|
|
282
|
+
if (
|
|
283
|
+
assistant.stopReason === "aborted" ||
|
|
284
|
+
assistant.stopReason === "error" ||
|
|
285
|
+
!assistant.usage ||
|
|
286
|
+
!hasContextTokenUsage(assistant.usage)
|
|
287
|
+
) {
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (!assistant.contextSnapshot) {
|
|
292
|
+
assistant.contextSnapshot = {
|
|
293
|
+
promptTokens: calculatePromptTokens(assistant.usage),
|
|
294
|
+
nonMessageTokens: computeNonMessageTokens(this.#host.session),
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
const snapshot = assistant.contextSnapshot;
|
|
298
|
+
snapshot.historyRewriteTokensRemoved = (snapshot.historyRewriteTokensRemoved ?? 0) + Math.floor(tokensRemoved);
|
|
299
|
+
this.#contextUsageRevision++;
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
259
304
|
/** Sets or clears the in-flight context snapshot. */
|
|
260
305
|
setPendingSnapshot(snapshot: PendingContextSnapshot | undefined): void {
|
|
261
306
|
this.#pendingContextSnapshot = snapshot;
|
|
@@ -14,13 +14,25 @@ export interface SessionStorageStat {
|
|
|
14
14
|
|
|
15
15
|
export interface SessionStorageWriter {
|
|
16
16
|
/**
|
|
17
|
-
* Append one newline-terminated line.
|
|
18
|
-
*
|
|
19
|
-
*
|
|
17
|
+
* Append one newline-terminated line.
|
|
18
|
+
*
|
|
19
|
+
* File and memory storage apply the line synchronously before the returned
|
|
20
|
+
* promise settles, so a software crash after `append` returns (or after a
|
|
21
|
+
* fire-and-forget call begins) still sees the entry on disk / in body. No
|
|
22
|
+
* `fsync` — power loss may still drop the last page. Indexed backends update
|
|
23
|
+
* the local index immediately and queue the remote publish in call order.
|
|
20
24
|
*
|
|
21
25
|
* `line` MUST include the trailing newline.
|
|
22
26
|
*/
|
|
23
27
|
append(line: string): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Synchronous append when the backend can apply the line before return.
|
|
30
|
+
* File and memory implement this so {@link SessionManager} can latch the
|
|
31
|
+
* first write failure before the appending call returns (surfaced by a later
|
|
32
|
+
* flushSync/close/next append — the turn loop does not throw from append).
|
|
33
|
+
* Indexed backends update the local index immediately and queue remote I/O.
|
|
34
|
+
*/
|
|
35
|
+
appendSync?(line: string): void;
|
|
24
36
|
/** Resolve once all queued appends complete. No fsync. */
|
|
25
37
|
flush(): Promise<void>;
|
|
26
38
|
/** Drain synchronously flushable queued work when the backend supports it. No fsync. */
|
|
@@ -93,8 +105,6 @@ class FileSessionStorageWriter implements SessionStorageWriter {
|
|
|
93
105
|
#closed = false;
|
|
94
106
|
#error: Error | undefined;
|
|
95
107
|
#onError: ((err: Error) => void) | undefined;
|
|
96
|
-
#pending = "";
|
|
97
|
-
#flushScheduled = false;
|
|
98
108
|
|
|
99
109
|
constructor(fpath: string, options?: { flags?: "a" | "w"; onError?: (err: Error) => void }) {
|
|
100
110
|
this.#onError = options?.onError;
|
|
@@ -129,34 +139,29 @@ class FileSessionStorageWriter implements SessionStorageWriter {
|
|
|
129
139
|
}
|
|
130
140
|
}
|
|
131
141
|
|
|
132
|
-
|
|
133
|
-
this.#
|
|
134
|
-
if (this.#
|
|
135
|
-
|
|
136
|
-
|
|
142
|
+
appendSync(line: string): void {
|
|
143
|
+
if (this.#closed) throw new Error("Writer closed");
|
|
144
|
+
if (this.#error) throw this.#error;
|
|
145
|
+
// Write in-body so software crash after the call still sees the entry.
|
|
146
|
+
// Microtask batching used to leave completed transcript lines only in
|
|
147
|
+
// memory until the next event-loop turn; process crash then lost every
|
|
148
|
+
// post-checkpoint event. flush/flushSync remain no-op drains (no fsync).
|
|
137
149
|
try {
|
|
138
|
-
this.#writeNow(
|
|
150
|
+
this.#writeNow(line);
|
|
139
151
|
} catch (err) {
|
|
140
|
-
this.#recordError(err);
|
|
152
|
+
throw this.#recordError(err);
|
|
141
153
|
}
|
|
142
154
|
}
|
|
143
155
|
|
|
144
156
|
async append(line: string): Promise<void> {
|
|
145
|
-
|
|
146
|
-
if (this.#error) throw this.#error;
|
|
147
|
-
this.#pending += line;
|
|
148
|
-
if (this.#flushScheduled) return;
|
|
149
|
-
this.#flushScheduled = true;
|
|
150
|
-
queueMicrotask(() => this.#flushPendingNow());
|
|
157
|
+
this.appendSync(line);
|
|
151
158
|
}
|
|
152
159
|
|
|
153
160
|
async flush(): Promise<void> {
|
|
154
|
-
this.#flushPendingNow();
|
|
155
161
|
if (this.#error) throw this.#error;
|
|
156
162
|
}
|
|
157
163
|
|
|
158
164
|
flushSync(): void {
|
|
159
|
-
this.#flushPendingNow();
|
|
160
165
|
if (this.#error) throw this.#error;
|
|
161
166
|
}
|
|
162
167
|
|
|
@@ -166,7 +171,6 @@ class FileSessionStorageWriter implements SessionStorageWriter {
|
|
|
166
171
|
|
|
167
172
|
async close(): Promise<void> {
|
|
168
173
|
if (this.#closed) return;
|
|
169
|
-
this.#flushPendingNow();
|
|
170
174
|
this.#closed = true;
|
|
171
175
|
// Unregister from finalization - we're closing properly
|
|
172
176
|
writerRegistry.unregister(this);
|
|
@@ -487,7 +491,7 @@ class MemorySessionStorageWriter implements SessionStorageWriter {
|
|
|
487
491
|
return error;
|
|
488
492
|
}
|
|
489
493
|
|
|
490
|
-
|
|
494
|
+
appendSync(line: string): void {
|
|
491
495
|
if (this.#closed) throw new Error("Writer closed");
|
|
492
496
|
if (this.#error) throw this.#error;
|
|
493
497
|
try {
|
|
@@ -498,6 +502,10 @@ class MemorySessionStorageWriter implements SessionStorageWriter {
|
|
|
498
502
|
}
|
|
499
503
|
}
|
|
500
504
|
|
|
505
|
+
async append(line: string): Promise<void> {
|
|
506
|
+
this.appendSync(line);
|
|
507
|
+
}
|
|
508
|
+
|
|
501
509
|
async flush(): Promise<void> {
|
|
502
510
|
if (this.#error) throw this.#error;
|
|
503
511
|
}
|
|
@@ -164,7 +164,7 @@ export class StreamingEditGuard {
|
|
|
164
164
|
const toolCall = messageContent[contentIndex] as ToolCall;
|
|
165
165
|
if (toolCall.name !== "edit") return undefined;
|
|
166
166
|
const args = toolCall.arguments;
|
|
167
|
-
if (!args || typeof args !== "object" || Array.isArray(args) || "
|
|
167
|
+
if (!args || typeof args !== "object" || Array.isArray(args) || "old_string" in args || "new_string" in args) {
|
|
168
168
|
return undefined;
|
|
169
169
|
}
|
|
170
170
|
const filePath = typeof args.path === "string" ? args.path : undefined;
|
|
@@ -102,39 +102,42 @@ function formatFastModeStatus(session: AgentSession): string {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
/** Detailed, session-effective `/computer status` diagnostics. */
|
|
105
|
-
function formatComputerUseStatus(session: AgentSession): string {
|
|
105
|
+
async function formatComputerUseStatus(session: AgentSession): Promise<string> {
|
|
106
106
|
const enabled = session.settings.get("computer.enabled");
|
|
107
107
|
const active = session.getEnabledToolNames().includes("computer");
|
|
108
108
|
const model = session.model;
|
|
109
109
|
const modelName = model ? formatModelString(model) : "none";
|
|
110
|
-
const exposure = !enabled
|
|
111
|
-
|
|
110
|
+
const exposure = !enabled
|
|
111
|
+
? "not exposed (disabled)"
|
|
112
|
+
: !active
|
|
113
|
+
? "not exposed (tool inactive)"
|
|
114
|
+
: computerExposureMode(model);
|
|
112
115
|
const configured = {
|
|
113
|
-
backend: session.settings.get("computer.backend"),
|
|
114
116
|
display: session.settings.get("computer.display"),
|
|
115
117
|
maxWidth: session.settings.get("computer.maxWidth"),
|
|
116
118
|
maxHeight: session.settings.get("computer.maxHeight"),
|
|
117
119
|
};
|
|
118
|
-
const computerTool =
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
120
|
+
const computerTool = active
|
|
121
|
+
? (session.getToolByName("computer") as Pick<ComputerTool, "capabilities"> | undefined)
|
|
122
|
+
: undefined;
|
|
123
|
+
const capabilities = await computerTool?.capabilities();
|
|
124
|
+
const capabilityStatus = capabilities
|
|
125
|
+
? [
|
|
126
|
+
`backend=${capabilities.backend}${capabilities.displayServer ? `/${capabilities.displayServer}` : ""}`,
|
|
127
|
+
`capture=${capabilities.capture} (${capabilities.capturePermission})`,
|
|
128
|
+
`input=${capabilities.input} (${capabilities.inputPermission})`,
|
|
129
|
+
`ax=${capabilities.ax} (${capabilities.axPermission})`,
|
|
130
|
+
`backgroundWindowInput=${capabilities.backgroundWindowInput}`,
|
|
131
|
+
`deliveryModes=${capabilities.deliveryModes.join(",") || "none"}`,
|
|
132
|
+
].join(", ")
|
|
133
|
+
: "session not started";
|
|
125
134
|
return [
|
|
126
135
|
`Computer use: ${enabled ? "enabled" : "disabled"}`,
|
|
127
|
-
`tool: ${
|
|
128
|
-
`backend: ${effective.backend}`,
|
|
129
|
-
`display: ${effective.display}`,
|
|
130
|
-
`capture: ${effective.maxWidth}×${effective.maxHeight}`,
|
|
131
|
-
...(configurationChanged
|
|
132
|
-
? [
|
|
133
|
-
`next-session settings: backend=${configured.backend}, display=${configured.display}, capture=${configured.maxWidth}×${configured.maxHeight}`,
|
|
134
|
-
]
|
|
135
|
-
: []),
|
|
136
|
-
`model: ${modelName}`,
|
|
136
|
+
`tool: ${active ? "active" : "inactive"}`,
|
|
137
137
|
`exposure: ${exposure}`,
|
|
138
|
+
`model: ${modelName}`,
|
|
139
|
+
`configured: display=${configured.display}, maxWidth=${configured.maxWidth}, maxHeight=${configured.maxHeight}`,
|
|
140
|
+
`capabilities: ${capabilityStatus}`,
|
|
138
141
|
].join(" · ");
|
|
139
142
|
}
|
|
140
143
|
|
|
@@ -151,7 +154,7 @@ async function applyComputerUseToggle(session: AgentSession, enable: boolean): P
|
|
|
151
154
|
}
|
|
152
155
|
session.settings.override("computer.enabled", enable);
|
|
153
156
|
return enable
|
|
154
|
-
? `Computer use enabled for this session. ${formatComputerUseStatus(session)}`
|
|
157
|
+
? `Computer use enabled for this session. ${await formatComputerUseStatus(session)}`
|
|
155
158
|
: "Computer use disabled for this session.";
|
|
156
159
|
}
|
|
157
160
|
|
|
@@ -673,7 +676,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
673
676
|
handle: async (command, runtime) => {
|
|
674
677
|
const arg = command.args.trim().toLowerCase();
|
|
675
678
|
if (arg === "status") {
|
|
676
|
-
await runtime.output(formatComputerUseStatus(runtime.session));
|
|
679
|
+
await runtime.output(await formatComputerUseStatus(runtime.session));
|
|
677
680
|
return commandConsumed();
|
|
678
681
|
}
|
|
679
682
|
if (!arg || arg === "toggle" || arg === "on" || arg === "off") {
|
|
@@ -686,7 +689,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
686
689
|
handleTui: async (command, runtime) => {
|
|
687
690
|
const arg = command.args.trim().toLowerCase();
|
|
688
691
|
if (arg === "status") {
|
|
689
|
-
runtime.ctx.showStatus(formatComputerUseStatus(runtime.ctx.session));
|
|
692
|
+
runtime.ctx.showStatus(await formatComputerUseStatus(runtime.ctx.session));
|
|
690
693
|
runtime.ctx.editor.setText("");
|
|
691
694
|
return;
|
|
692
695
|
}
|
package/src/system-prompt.ts
CHANGED
|
@@ -498,7 +498,8 @@ export interface BuildSystemPromptOptions {
|
|
|
498
498
|
/**
|
|
499
499
|
* Whether provider-native tool calling is active (no owned/in-band syntax).
|
|
500
500
|
* When true and `inlineToolDescriptors` is false, the inventory renders as a
|
|
501
|
-
* compact tool-name list; otherwise it renders full
|
|
501
|
+
* compact tool-name list; otherwise it renders the full Harmony-style
|
|
502
|
+
* `namespace functions { … }` catalog. Default: true
|
|
502
503
|
*/
|
|
503
504
|
nativeTools?: boolean;
|
|
504
505
|
/** Skills settings for discovery. */
|
|
@@ -525,6 +526,9 @@ export interface BuildSystemPromptOptions {
|
|
|
525
526
|
taskMaxConcurrency?: number;
|
|
526
527
|
/** Whether IRC-backed parallel coordination can be included in delegation policy. */
|
|
527
528
|
taskIrcEnabled?: boolean;
|
|
529
|
+
/** Whether the read-only `scout` subagent is spawnable (not disabled, allowed by spawn policy). Defaults to true. */
|
|
530
|
+
scoutAvailable?: boolean;
|
|
531
|
+
|
|
528
532
|
/** Rules with alwaysApply=true — their full content is injected into the prompt. */
|
|
529
533
|
alwaysApplyRules?: AlwaysApplyRule[];
|
|
530
534
|
/** Whether secret obfuscation is active. When true, explains the redaction format in the prompt. */
|
|
@@ -599,6 +603,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
599
603
|
taskIrcEnabled = false,
|
|
600
604
|
secretsEnabled = false,
|
|
601
605
|
workspaceTree: providedWorkspaceTree,
|
|
606
|
+
scoutAvailable = true,
|
|
602
607
|
memoryRootEnabled = false,
|
|
603
608
|
securityEnabled = false,
|
|
604
609
|
model,
|
|
@@ -793,7 +798,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
793
798
|
|
|
794
799
|
// List mode shows a compact tool-name list; it only applies when descriptors
|
|
795
800
|
// stay in provider-native tool schemas AND native tool calling is active.
|
|
796
|
-
// Otherwise render full
|
|
801
|
+
// Otherwise render the full functions-namespace catalog in the system prompt.
|
|
797
802
|
const toolListMode = !inlineToolDescriptors && nativeTools;
|
|
798
803
|
// Build tool descriptions for system prompt rendering.
|
|
799
804
|
const toolPromptNames = new Map<string, string>(toolNames.map(name => [name, tools?.get(name)?.wireName ?? name]));
|
|
@@ -826,7 +831,6 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
826
831
|
examples: meta?.examples,
|
|
827
832
|
};
|
|
828
833
|
}),
|
|
829
|
-
model ?? "",
|
|
830
834
|
);
|
|
831
835
|
|
|
832
836
|
// Filter skills for the rendered system prompt:
|
|
@@ -879,6 +883,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
879
883
|
eagerTasksAlways,
|
|
880
884
|
taskBatch,
|
|
881
885
|
MAX_CONCURRENCY: normalizeConcurrencyLimit(taskMaxConcurrency),
|
|
886
|
+
scoutAvailable,
|
|
882
887
|
taskIrcEnabled,
|
|
883
888
|
secretsEnabled,
|
|
884
889
|
hasMemoryRoot: memoryRootEnabled,
|
package/src/task/index.ts
CHANGED
|
@@ -27,7 +27,7 @@ import { TASK_EFFORTS, type TaskEffort } from "../thinking";
|
|
|
27
27
|
import { truncateForPrompt } from "../tools/approval";
|
|
28
28
|
import { isIrcEnabled } from "../tools/hub";
|
|
29
29
|
import { formatBytes, formatDuration } from "../tools/render-utils";
|
|
30
|
-
import { resolveSpawnPolicy } from "./spawn-policy";
|
|
30
|
+
import { isScoutSpawnable, resolveSpawnPolicy } from "./spawn-policy";
|
|
31
31
|
import {
|
|
32
32
|
type AgentDefinition,
|
|
33
33
|
type AgentProgress,
|
|
@@ -188,8 +188,10 @@ function renderDescription(options: TaskDescriptionOptions): string {
|
|
|
188
188
|
readOnly: isReadOnlyAgent(agent),
|
|
189
189
|
blocking: agent.blocking === true,
|
|
190
190
|
}));
|
|
191
|
+
const scoutAvailable = isScoutSpawnable(options.disabledAgents, options.parentSpawns);
|
|
191
192
|
return prompt.render(taskDescriptionTemplate, {
|
|
192
193
|
agents: renderedAgents,
|
|
194
|
+
scoutAvailable,
|
|
193
195
|
spawningDisabled,
|
|
194
196
|
defaultAgent: spawnPolicy.defaultAgent,
|
|
195
197
|
isolationEnabled: options.isolationEnabled,
|
|
@@ -398,15 +400,19 @@ const GENERIC_SPAWN_AGENTS: ReadonlySet<string> = new Set(["task", "sonic"]);
|
|
|
398
400
|
* (DepthCapacity: it currently has the `task` tool). `agentNames` are the
|
|
399
401
|
* per-item resolved agent types. Returns undefined when no nudge applies.
|
|
400
402
|
*/
|
|
401
|
-
export function buildSpecializationAdvisory(
|
|
403
|
+
export function buildSpecializationAdvisory(
|
|
404
|
+
agentNames: string[],
|
|
405
|
+
depthCapacity: boolean,
|
|
406
|
+
scoutAvailable = true,
|
|
407
|
+
): string | undefined {
|
|
402
408
|
if (!depthCapacity) return undefined;
|
|
403
409
|
const generics = agentNames.filter(name => GENERIC_SPAWN_AGENTS.has(name));
|
|
404
410
|
if (generics.length < 2) return undefined;
|
|
405
|
-
|
|
406
|
-
`
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
411
|
+
const specialist = scoutAvailable
|
|
412
|
+
? `Check the agent list for a closer specialist type — e.g. read-only research belongs on ` +
|
|
413
|
+
`\`agent: "scout"\`, which runs on a faster model.`
|
|
414
|
+
: `Check the agent list for a closer specialist type.`;
|
|
415
|
+
return `Tip: this call spawned ${generics.length} generic \`${generics[0]}\` workers. ${specialist}`;
|
|
410
416
|
}
|
|
411
417
|
|
|
412
418
|
/**
|
|
@@ -443,10 +449,11 @@ export function composeSpawnAdvisory(args: {
|
|
|
443
449
|
depthCapacity: boolean;
|
|
444
450
|
ircEnabled: boolean;
|
|
445
451
|
willRunAsync: boolean;
|
|
452
|
+
scoutAvailable?: boolean;
|
|
446
453
|
}): string | undefined {
|
|
447
454
|
return (
|
|
448
455
|
[
|
|
449
|
-
buildSpecializationAdvisory(args.agents, args.depthCapacity),
|
|
456
|
+
buildSpecializationAdvisory(args.agents, args.depthCapacity, args.scoutAvailable),
|
|
450
457
|
args.willRunAsync ? buildCoordinationAdvisory(args.items, args.depthCapacity, args.ircEnabled) : undefined,
|
|
451
458
|
]
|
|
452
459
|
.filter(Boolean)
|
|
@@ -751,6 +758,10 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
751
758
|
depthCapacity,
|
|
752
759
|
ircEnabled,
|
|
753
760
|
willRunAsync: false,
|
|
761
|
+
scoutAvailable: isScoutSpawnable(
|
|
762
|
+
this.session.settings.get("task.disabledAgents") as string[] | undefined,
|
|
763
|
+
this.session.getSessionSpawns?.() ?? "*",
|
|
764
|
+
),
|
|
754
765
|
});
|
|
755
766
|
const result = await this.#executeSyncFanout(
|
|
756
767
|
toolCallId,
|
|
@@ -784,6 +795,10 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
784
795
|
depthCapacity,
|
|
785
796
|
ircEnabled,
|
|
786
797
|
willRunAsync: asyncItems.length > 0,
|
|
798
|
+
scoutAvailable: isScoutSpawnable(
|
|
799
|
+
this.session.settings.get("task.disabledAgents") as string[] | undefined,
|
|
800
|
+
this.session.getSessionSpawns?.() ?? "*",
|
|
801
|
+
),
|
|
787
802
|
});
|
|
788
803
|
// Returns a fresh result (copied content array, copied text part) rather
|
|
789
804
|
// than mutating the caller's — task results are short-lived here, but an
|
package/src/task/spawn-policy.ts
CHANGED
|
@@ -56,3 +56,17 @@ export function resolveSpawnPolicy(parentSpawns: string | boolean | null | undef
|
|
|
56
56
|
allowedPromptText: allowedAgents.map(agent => `\`${agent}\``).join(", "),
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Whether the `scout` agent is spawnable in a session: not disabled via
|
|
62
|
+
* `task.disabledAgents`, and permitted by the session spawn policy.
|
|
63
|
+
*/
|
|
64
|
+
export function isScoutSpawnable(
|
|
65
|
+
disabledAgents: readonly string[] | undefined,
|
|
66
|
+
spawns: string | boolean | null | undefined,
|
|
67
|
+
): boolean {
|
|
68
|
+
if (disabledAgents?.includes("scout")) return false;
|
|
69
|
+
const policy = resolveSpawnPolicy(spawns);
|
|
70
|
+
if (!policy.enabled) return false;
|
|
71
|
+
return policy.allowedAgents === null || policy.allowedAgents.includes("scout");
|
|
72
|
+
}
|