@gajae-code/coding-agent 0.14.2 → 0.15.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/CHANGELOG.md +120 -1
- package/dist/types/async/job-manager.d.ts +4 -1
- package/dist/types/autoresearch/dashboard.d.ts +29 -4
- package/dist/types/autoresearch/data-context.d.ts +25 -0
- package/dist/types/autoresearch/git.d.ts +65 -7
- package/dist/types/autoresearch/harness.d.ts +58 -0
- package/dist/types/autoresearch/prompts.d.ts +51 -0
- package/dist/types/autoresearch/runs.d.ts +173 -0
- package/dist/types/autoresearch/types.d.ts +9 -139
- package/dist/types/capability/types.d.ts +20 -0
- package/dist/types/cli/accounts-cli.d.ts +1 -0
- package/dist/types/commands/{rlm.d.ts → autoresearch.d.ts} +2 -4
- package/dist/types/config/autorouting-contract.d.ts +146 -0
- package/dist/types/config/autorouting-generator.d.ts +40 -0
- package/dist/types/config/autorouting-tier-map.d.ts +16844 -0
- package/dist/types/config/autorouting.d.ts +35 -0
- package/dist/types/config/model-discovery-manager.d.ts +8 -1
- package/dist/types/config/model-registry.d.ts +30 -1
- package/dist/types/config/provider-selection-policy.d.ts +23 -0
- package/dist/types/config/settings-schema.d.ts +61 -3
- package/dist/types/config/settings.d.ts +3 -0
- package/dist/types/coordinator/contract.d.ts +1 -1
- package/dist/types/coordinator-mcp/durability.d.ts +29 -0
- package/dist/types/coordinator-mcp/event-webhook.d.ts +50 -0
- package/dist/types/coordinator-mcp/question-state.d.ts +1 -1
- package/dist/types/coordinator-mcp/server.d.ts +14 -0
- package/dist/types/crash/index-store.d.ts +3 -0
- package/dist/types/crash/record-loader.d.ts +2 -0
- package/dist/types/crash/upstream/relay.d.ts +5 -3
- package/dist/types/cursor.d.ts +12 -0
- package/dist/types/defaults/gjc-defaults.d.ts +35 -1
- package/dist/types/defaults/gjc-skills.generated.d.ts +1 -1
- package/dist/types/discovery/helpers.d.ts +1 -1
- package/dist/types/edit/index.d.ts +2 -1
- package/dist/types/eval/py/executor.d.ts +2 -0
- package/dist/types/extensibility/extensions/loader.d.ts +32 -0
- package/dist/types/extensibility/gjc-plugins/types.d.ts +1 -1
- package/dist/types/extensibility/skills.d.ts +2 -0
- package/dist/types/gjc-runtime/autoresearch-runtime.d.ts +194 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +1 -1
- package/dist/types/gjc-runtime/psmux-detect.d.ts +1 -1
- package/dist/types/gjc-runtime/python-transcript.d.ts +19 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +2 -8
- package/dist/types/gjc-runtime/session-layout.d.ts +6 -3
- package/dist/types/gjc-runtime/state-renderer.d.ts +0 -22
- package/dist/types/gjc-runtime/state-schema.d.ts +7 -7
- package/dist/types/gjc-runtime/tmux-common.d.ts +2 -2
- package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +38 -1
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +2 -24
- package/dist/types/gjc-runtime/ultragoal-validation-policy.d.ts +73 -0
- package/dist/types/gjc-runtime/workflow-recovery-projection.d.ts +100 -0
- package/dist/types/hooks/skill-keywords.d.ts +1 -1
- package/dist/types/main.d.ts +0 -10
- package/dist/types/modes/acp/acp-agent.d.ts +13 -6
- package/dist/types/modes/components/model-selector.d.ts +14 -0
- package/dist/types/modes/components/skill-hud/render.d.ts +6 -1
- package/dist/types/modes/components/smart-routing-panel.d.ts +82 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +14 -0
- package/dist/types/modes/interactive-mode.d.ts +3 -0
- package/dist/types/modes/types.d.ts +9 -0
- package/dist/types/rlm/artifacts.d.ts +4 -4
- package/dist/types/runtime/optional-runtime-services.d.ts +2 -1
- package/dist/types/runtime/workspace-tree-service.d.ts +5 -1
- package/dist/types/runtime-mcp/config.d.ts +6 -0
- package/dist/types/runtime-mcp/manager.d.ts +10 -0
- package/dist/types/sdk/acp/adapter.d.ts +0 -4
- package/dist/types/sdk/broker/broker.d.ts +1 -1
- package/dist/types/sdk/broker/lifecycle.d.ts +57 -2
- package/dist/types/sdk/broker/session-index.d.ts +15 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +6 -0
- package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +4 -0
- package/dist/types/sdk/bus/index.d.ts +8 -0
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +2 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +12 -2
- package/dist/types/sdk/client/client.d.ts +39 -4
- package/dist/types/sdk/host/host.d.ts +1 -0
- package/dist/types/sdk/host/internal-autorouting-state.d.ts +3 -0
- package/dist/types/sdk/host/model-pin.d.ts +29 -0
- package/dist/types/sdk/host/session-runtime.d.ts +2 -1
- package/dist/types/sdk/lifecycle-session.d.ts +13 -0
- package/dist/types/sdk/router/session-router.d.ts +27 -1
- package/dist/types/sdk/session.d.ts +11 -1
- package/dist/types/session/agent-session.d.ts +36 -6
- package/dist/types/session/artifacts.d.ts +16 -1
- package/dist/types/session/internal/managed-session-storage.d.ts +7 -0
- package/dist/types/session/session-manager.d.ts +84 -2
- package/dist/types/session/session-staging-paths.d.ts +3 -0
- package/dist/types/skill-state/canonical-skills.d.ts +1 -1
- package/dist/types/skill-state/workflow-hud.d.ts +12 -21
- package/dist/types/skill-state/workflow-mutation-guard.d.ts +1 -0
- package/dist/types/slash-commands/helpers/autorouting-status.d.ts +8 -0
- package/dist/types/system-prompt.d.ts +23 -0
- package/dist/types/task/executor.d.ts +35 -4
- package/dist/types/task/index.d.ts +17 -3
- package/dist/types/task/receipt.d.ts +3 -1
- package/dist/types/task/types.d.ts +144 -0
- package/dist/types/tools/atomic-file-write.d.ts +72 -0
- package/dist/types/tools/descriptors.d.ts +1 -0
- package/dist/types/tools/index.d.ts +36 -1
- package/dist/types/tools/move-session.d.ts +40 -0
- package/dist/types/tools/python.d.ts +12 -0
- package/dist/types/tools/resource-gc.d.ts +0 -3
- package/dist/types/tools/search.d.ts +8 -1
- package/dist/types/tools/skill.d.ts +9 -4
- package/dist/types/tools/subagent.d.ts +33 -3
- package/dist/types/utils/xml-escape.d.ts +2 -0
- package/dist/types/workflow/workflow-intent-diff.d.ts +2 -2
- package/dist/types/workflow/workflow-intent-report-consumer.d.ts +1 -1
- package/package.json +11 -12
- package/scripts/build-sdk-package-smoke.ts +7 -5
- package/scripts/check-autorouting-tier-map.ts +183 -0
- package/scripts/generate-sdk-operation-inventory.ts +12 -0
- package/scripts/generate-tool-catalog.ts +40 -5
- package/src/async/job-manager.ts +5 -2
- package/src/autoresearch/dashboard.ts +134 -314
- package/src/autoresearch/data-context.ts +35 -0
- package/src/autoresearch/git.ts +186 -26
- package/src/autoresearch/{helpers.ts → harness.ts} +65 -40
- package/src/autoresearch/{prompt-setup.md → prompts/prompt-setup.md} +7 -7
- package/src/autoresearch/prompts/prompt.md +103 -0
- package/src/autoresearch/prompts.ts +126 -0
- package/src/autoresearch/runs.ts +708 -0
- package/src/autoresearch/types.ts +9 -150
- package/src/capability/index.ts +4 -4
- package/src/capability/types.ts +20 -0
- package/src/cli/accounts-cli.ts +21 -5
- package/src/cli/config-cli.ts +25 -1
- package/src/cli/fast-help.ts +1 -2
- package/src/cli/list-models.ts +5 -8
- package/src/cli/setup-cli.ts +8 -0
- package/src/cli.ts +2 -21
- package/src/commands/autoresearch.ts +33 -0
- package/src/commands/coordinator.ts +15 -0
- package/src/commands/launch.ts +2 -4
- package/src/commands/quick-lane.ts +1 -1
- package/src/commands/sdk.ts +24 -0
- package/src/commands/state.ts +1 -1
- package/src/config/autorouting-contract.ts +474 -0
- package/src/config/autorouting-generator.ts +238 -0
- package/src/config/autorouting-tier-map.ts +6108 -0
- package/src/config/autorouting.ts +120 -0
- package/src/config/model-discovery-manager.ts +42 -11
- package/src/config/model-registry.ts +348 -68
- package/src/config/provider-selection-policy.ts +60 -9
- package/src/config/settings-schema.ts +195 -6
- package/src/config/settings.ts +62 -1
- package/src/config.ts +2 -3
- package/src/coordinator/contract.ts +0 -1
- package/src/coordinator-mcp/codex-handoff.ts +68 -42
- package/src/coordinator-mcp/durability.ts +200 -0
- package/src/coordinator-mcp/event-webhook.ts +278 -0
- package/src/coordinator-mcp/question-state.ts +109 -46
- package/src/coordinator-mcp/server.ts +932 -504
- package/src/crash/index-store.ts +126 -22
- package/src/crash/record-loader.ts +7 -0
- package/src/crash/upstream/relay.ts +82 -34
- package/src/cursor.ts +84 -1
- package/src/debug/index.ts +26 -0
- package/src/defaults/gjc/skills/autoresearch/SKILL.md +116 -0
- package/src/defaults/gjc/skills/autoresearch/auto-critic.md +46 -0
- package/src/defaults/gjc/skills/autoresearch/auto-iterate.md +42 -0
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +17 -37
- package/src/defaults/gjc/skills/ralplan/SKILL.md +21 -18
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +6 -17
- package/src/defaults/gjc/skills/ultragoal/ai-slop-cleaner.md +2 -2
- package/src/defaults/gjc-defaults.ts +89 -2
- package/src/defaults/gjc-skills.generated.ts +24 -17
- package/src/discovery/builtin.ts +18 -5
- package/src/discovery/helpers.ts +6 -6
- package/src/edit/index.ts +5 -2
- package/src/eval/py/executor.ts +8 -0
- package/src/extensibility/extensions/loader.ts +135 -13
- package/src/extensibility/extensions/wrapper.ts +1 -2
- package/src/extensibility/gjc-plugins/schema.ts +1 -1
- package/src/extensibility/runtime-skill-discovery.ts +2 -2
- package/src/extensibility/skill-management.ts +2 -3
- package/src/extensibility/skills.ts +16 -8
- package/src/gjc-runtime/autoresearch-runtime.ts +1509 -0
- package/src/gjc-runtime/gc-render.ts +0 -1
- package/src/gjc-runtime/gc-runtime.ts +1 -11
- package/src/gjc-runtime/launch-tmux.ts +3 -3
- package/src/gjc-runtime/psmux-detect.ts +2 -2
- package/src/gjc-runtime/python-transcript.ts +58 -0
- package/src/gjc-runtime/ralplan-runtime.ts +6 -18
- package/src/gjc-runtime/session-layout.ts +18 -7
- package/src/gjc-runtime/session-state-sidecar.ts +58 -9
- package/src/gjc-runtime/state-renderer.ts +0 -38
- package/src/gjc-runtime/state-runtime.ts +43 -22
- package/src/gjc-runtime/state-schema.ts +1 -1
- package/src/gjc-runtime/tmux-common.ts +7 -7
- package/src/gjc-runtime/ultragoal-change-set.ts +232 -11
- package/src/gjc-runtime/ultragoal-runtime.ts +309 -114
- package/src/gjc-runtime/ultragoal-validation-policy.ts +247 -0
- package/src/gjc-runtime/workflow-command-ref.ts +15 -11
- package/src/gjc-runtime/workflow-manifest.generated.json +413 -532
- package/src/gjc-runtime/workflow-manifest.ts +43 -83
- package/src/gjc-runtime/workflow-recovery-projection.ts +616 -0
- package/src/hooks/skill-keywords.ts +3 -3
- package/src/hooks/skill-state.ts +2 -2
- package/src/internal-urls/docs-index.generated.ts +26 -24
- package/src/lsp/index.ts +25 -5
- package/src/main.ts +2 -27
- package/src/modes/acp/acp-agent.ts +200 -75
- package/src/modes/components/model-selector.ts +216 -13
- package/src/modes/components/oauth-selector.ts +5 -5
- package/src/modes/components/skill-hud/render.ts +173 -35
- package/src/modes/components/smart-routing-panel.ts +531 -0
- package/src/modes/components/tool-status-header.ts +6 -3
- package/src/modes/controllers/command-controller.ts +17 -7
- package/src/modes/controllers/input-controller.ts +44 -9
- package/src/modes/controllers/runtime-mcp-command-controller.ts +4 -0
- package/src/modes/controllers/selector-controller.ts +298 -65
- package/src/modes/interactive-mode.ts +8 -1
- package/src/modes/shared/agent-wire/workflow-gate-types.ts +1 -1
- package/src/modes/types.ts +9 -1
- package/src/prompts/agents/planner.md +1 -1
- package/src/prompts/system/system-prompt.md +1 -1
- package/src/prompts/system/volatile-project-context.md +2 -1
- package/src/prompts/tools/move-session.md +15 -0
- package/src/prompts/tools/python.md +12 -0
- package/src/prompts/tools/skill-discovery.md +1 -1
- package/src/prompts/tools/skill.md +2 -2
- package/src/prompts/tools/task-summary.md +1 -0
- package/src/prompts/tools/task.md +6 -0
- package/src/rlm/artifacts.ts +12 -10
- package/src/runtime/optional-runtime-services.ts +3 -2
- package/src/runtime/workspace-tree-service.ts +11 -3
- package/src/runtime-mcp/config.ts +8 -1
- package/src/runtime-mcp/manager.ts +33 -0
- package/src/sdk/acp/adapter.ts +48 -1
- package/src/sdk/broker/broker.ts +30 -2
- package/src/sdk/broker/discovery.ts +217 -2
- package/src/sdk/broker/lifecycle.ts +385 -59
- package/src/sdk/broker/session-index.ts +243 -48
- package/src/sdk/broker/transport.ts +1 -0
- package/src/sdk/bus/chat-daemon-control.ts +8 -2
- package/src/sdk/bus/chat-daemon-runtime.ts +5 -0
- package/src/sdk/bus/index.ts +208 -11
- package/src/sdk/bus/kind-aware-reconciliation.ts +16 -0
- package/src/sdk/bus/reconciliation-store.ts +27 -5
- package/src/sdk/bus/telegram-daemon-contract.ts +12 -2
- package/src/sdk/client/client.ts +204 -22
- package/src/sdk/host/host.ts +16 -0
- package/src/sdk/host/internal-autorouting-state.ts +13 -0
- package/src/sdk/host/model-pin.ts +101 -0
- package/src/sdk/host/reverse-leases.ts +2 -1
- package/src/sdk/host/session-runtime.test.ts +224 -1
- package/src/sdk/host/session-runtime.ts +125 -37
- package/src/sdk/lifecycle/service.ts +3 -1
- package/src/sdk/lifecycle-session.ts +42 -1
- package/src/sdk/protocol/operation-inventory.generated.json +66 -0
- package/src/sdk/router/session-router.ts +375 -36
- package/src/sdk/session.ts +547 -47
- package/src/session/account-inventory.ts +1 -1
- package/src/session/agent-session.ts +765 -189
- package/src/session/artifacts.ts +234 -4
- package/src/session/internal/managed-session-storage.ts +91 -4
- package/src/session/provider-safety-stop-hint.ts +3 -2
- package/src/session/session-manager.ts +775 -16
- package/src/session/session-staging-paths.ts +8 -0
- package/src/setup/hermes/templates/operator-instructions.v1.md +0 -1
- package/src/setup/host-plugin-setup.ts +2 -2
- package/src/skill-state/active-state.ts +2 -2
- package/src/skill-state/canonical-skills.ts +1 -1
- package/src/skill-state/initial-phase.ts +1 -1
- package/src/skill-state/workflow-hud.ts +41 -41
- package/src/skill-state/workflow-mutation-guard.ts +26 -7
- package/src/slash-commands/builtin-registry.ts +77 -0
- package/src/slash-commands/helpers/autorouting-status.ts +55 -0
- package/src/system-prompt.ts +102 -5
- package/src/task/discovery.ts +2 -3
- package/src/task/executor.ts +617 -43
- package/src/task/index.ts +262 -15
- package/src/task/receipt.ts +30 -0
- package/src/task/render.ts +11 -1
- package/src/task/types.ts +316 -0
- package/src/tools/atomic-file-write.ts +594 -0
- package/src/tools/bisect.ts +3 -2
- package/src/tools/descriptors.test.ts +3 -0
- package/src/tools/descriptors.ts +24 -0
- package/src/tools/image-gen.ts +5 -1
- package/src/tools/index.ts +62 -5
- package/src/tools/move-session.ts +107 -0
- package/src/tools/python.ts +205 -0
- package/src/tools/read.ts +88 -15
- package/src/tools/renderers.ts +2 -0
- package/src/tools/resource-gc.ts +8 -177
- package/src/tools/search.ts +30 -6
- package/src/tools/skill-discovery.ts +1 -0
- package/src/tools/skill.ts +37 -7
- package/src/tools/subagent-render.ts +119 -13
- package/src/tools/subagent.ts +91 -74
- package/src/tools/tool-catalog.generated.ts +64 -2
- package/src/tools/write.ts +16 -12
- package/src/utils/xml-escape.ts +14 -0
- package/src/web/search/providers/codex.ts +2 -2
- package/src/workflow/workflow-intent-diff.ts +13 -12
- package/src/workflow/workflow-intent-report-consumer.ts +6 -6
- package/dist/types/autoresearch/helpers.d.ts +0 -24
- package/dist/types/autoresearch/index.d.ts +0 -2
- package/dist/types/autoresearch/state.d.ts +0 -17
- package/dist/types/autoresearch/storage.d.ts +0 -142
- package/dist/types/autoresearch/tools/init-experiment.d.ts +0 -31
- package/dist/types/autoresearch/tools/log-experiment.d.ts +0 -23
- package/dist/types/autoresearch/tools/run-experiment.d.ts +0 -8
- package/dist/types/autoresearch/tools/update-notes.d.ts +0 -12
- package/dist/types/commands/team.d.ts +0 -24
- package/dist/types/gjc-runtime/team-gc.d.ts +0 -7
- package/dist/types/gjc-runtime/team-launch.d.ts +0 -61
- package/dist/types/gjc-runtime/team-notify.d.ts +0 -45
- package/dist/types/gjc-runtime/team-runtime.d.ts +0 -336
- package/dist/types/gjc-runtime/team-store.d.ts +0 -168
- package/dist/types/gjc-runtime/team-worker-heartbeat.d.ts +0 -43
- package/dist/types/gjc-runtime/team-worker-memory-guard.d.ts +0 -92
- package/dist/types/gjc-runtime/team-workers.d.ts +0 -83
- package/dist/types/rlm/index.d.ts +0 -47
- package/dist/types/rlm/preset.d.ts +0 -23
- package/dist/types/rlm/python-tool.d.ts +0 -16
- package/src/autoresearch/command-resume.md +0 -14
- package/src/autoresearch/index.ts +0 -536
- package/src/autoresearch/prompt.md +0 -103
- package/src/autoresearch/resume-message.md +0 -10
- package/src/autoresearch/state.ts +0 -273
- package/src/autoresearch/storage.ts +0 -699
- package/src/autoresearch/tools/init-experiment.ts +0 -272
- package/src/autoresearch/tools/log-experiment.ts +0 -524
- package/src/autoresearch/tools/run-experiment.ts +0 -475
- package/src/autoresearch/tools/update-notes.ts +0 -109
- package/src/commands/rlm.ts +0 -19
- package/src/commands/team.ts +0 -238
- package/src/defaults/gjc/skills/deep-interview/auto-research-greenfield.md +0 -42
- package/src/defaults/gjc/skills/team/SKILL.md +0 -467
- package/src/gjc-runtime/team-gc.ts +0 -49
- package/src/gjc-runtime/team-launch.ts +0 -404
- package/src/gjc-runtime/team-notify.ts +0 -326
- package/src/gjc-runtime/team-runtime.ts +0 -5923
- package/src/gjc-runtime/team-store.ts +0 -878
- package/src/gjc-runtime/team-worker-heartbeat.ts +0 -149
- package/src/gjc-runtime/team-worker-memory-guard.ts +0 -363
- package/src/gjc-runtime/team-workers.ts +0 -1123
- package/src/prompts/system/rlm-report-command.md +0 -1
- package/src/prompts/system/rlm-research.md +0 -23
- package/src/rlm/index.ts +0 -365
- package/src/rlm/preset.ts +0 -76
- package/src/rlm/python-tool.ts +0 -68
package/src/tools/descriptors.ts
CHANGED
|
@@ -244,6 +244,7 @@ function defaultAvailabilityContext(session: ToolSession): ToolAvailabilityConte
|
|
|
244
244
|
|
|
245
245
|
function availableFor(name: string, session: ToolSession, context = defaultAvailabilityContext(session)): boolean {
|
|
246
246
|
if (name === "goal") return context.goalEnabled === true;
|
|
247
|
+
if (name === "move_session") return typeof session.rescopeSessionCwd === "function";
|
|
247
248
|
if (context.goalStateToolNames?.includes(name)) return context.goalEnabled === true;
|
|
248
249
|
if (name === "lsp") return (context.enableLsp ?? true) && Boolean(session.settings.get("lsp.enabled"));
|
|
249
250
|
if (name === "eval") return context.allowEval ?? true;
|
|
@@ -296,6 +297,9 @@ function cached(key: string, load: () => Promise<any>): Promise<any> {
|
|
|
296
297
|
}
|
|
297
298
|
return promise;
|
|
298
299
|
}
|
|
300
|
+
export function evictCachedTool(key: string): void {
|
|
301
|
+
moduleCache.delete(key);
|
|
302
|
+
}
|
|
299
303
|
|
|
300
304
|
const loaders: Record<string, Loader> = {
|
|
301
305
|
read: session => cached("read", () => import("./read")).then(module => new module.ReadTool(session)),
|
|
@@ -324,6 +328,17 @@ const loaders: Record<string, Loader> = {
|
|
|
324
328
|
cached("checkpoint", () => import("./checkpoint")).then(module => module.RewindTool.createIf(session)),
|
|
325
329
|
task: session => cached("task", () => import("../task")).then(module => module.TaskTool.create(session)),
|
|
326
330
|
subagent: session => cached("subagent", () => import("./subagent")).then(module => new module.SubagentTool(session)),
|
|
331
|
+
python: session =>
|
|
332
|
+
cached("python", () => import("./python")).then(module =>
|
|
333
|
+
module.createSessionPythonTool({
|
|
334
|
+
cwd: session.cwd,
|
|
335
|
+
getCwd: () => session.cwd,
|
|
336
|
+
getSessionId: () => session.getSessionId?.() ?? null,
|
|
337
|
+
registerSessionCleanup: (cleanup: () => Promise<void> | void) => {
|
|
338
|
+
session.registerSessionCleanup?.(cleanup);
|
|
339
|
+
},
|
|
340
|
+
}),
|
|
341
|
+
),
|
|
327
342
|
job: session => cached("job", () => import("./job")).then(module => module.JobTool.createIf(session)),
|
|
328
343
|
monitor: session =>
|
|
329
344
|
cached("monitor", () => import("./monitor")).then(module => module.MonitorTool.createIf(session)),
|
|
@@ -350,6 +365,8 @@ const loaders: Record<string, Loader> = {
|
|
|
350
365
|
skill: session => cached("skill", () => import("./skill")).then(module => module.SkillTool.createIf(session)),
|
|
351
366
|
goal: session =>
|
|
352
367
|
cached("goal", () => import("../goals/tools/goal-tool")).then(module => new module.GoalTool(session)),
|
|
368
|
+
move_session: session =>
|
|
369
|
+
cached("move_session", () => import("./move-session")).then(module => new module.MoveSessionTool(session)),
|
|
353
370
|
yield: session => cached("yield", () => import("./yield")).then(module => new module.YieldTool(session)),
|
|
354
371
|
report_finding: _session => cached("review", () => import("./review")).then(module => module.reportFindingTool),
|
|
355
372
|
resolve: session => cached("resolve", () => import("./resolve")).then(module => new module.ResolveTool(session)),
|
|
@@ -404,6 +421,12 @@ const names: Array<[name: string, label: string, summary: string | undefined, lo
|
|
|
404
421
|
["debug", "Debug", "Debug a running process with DAP (debugger adapter protocol)", "discoverable"],
|
|
405
422
|
["bisect", "Bisect", "Find the commit that introduced a regression", "discoverable"],
|
|
406
423
|
["eval", "Eval", "Execute Python or JavaScript code in an in-process eval backend", "discoverable"],
|
|
424
|
+
[
|
|
425
|
+
"python",
|
|
426
|
+
"Python",
|
|
427
|
+
"Execute Python in a persistent per-session REPL kernel (every call is appended to the session transcript)",
|
|
428
|
+
"discoverable",
|
|
429
|
+
],
|
|
407
430
|
["calc", "Calc", "Evaluate a mathematical expression", "discoverable"],
|
|
408
431
|
["ssh", "SSH", "Execute a command on a remote host over SSH", "discoverable"],
|
|
409
432
|
["github", "GitHub", "Interact with GitHub issues, pull requests, and repositories", "discoverable"],
|
|
@@ -429,6 +452,7 @@ const names: Array<[name: string, label: string, summary: string | undefined, lo
|
|
|
429
452
|
["write", "Write", "Write content to a file", "discoverable"],
|
|
430
453
|
["skill", "Skill", "Chain into another available skill", "essential"],
|
|
431
454
|
["goal", "Goal", undefined, "essential"],
|
|
455
|
+
["move_session", "Move Session", undefined, "essential"],
|
|
432
456
|
];
|
|
433
457
|
|
|
434
458
|
const descriptorRawArgumentValidations: Readonly<Record<string, ToolDescriptorMetadata["rawArgumentValidation"]>> = {
|
package/src/tools/image-gen.ts
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
ptree,
|
|
20
20
|
readSseJson,
|
|
21
21
|
Snowflake,
|
|
22
|
+
sanitizeHeaderComponent,
|
|
22
23
|
untilAborted,
|
|
23
24
|
} from "@gajae-code/utils";
|
|
24
25
|
import * as z from "zod/v4";
|
|
@@ -1259,7 +1260,10 @@ function buildOpenAIImageHeaders(model: Model, apiKey: string, sessionId: string
|
|
|
1259
1260
|
headers.set(OPENAI_HEADERS.ACCOUNT_ID, accountId);
|
|
1260
1261
|
headers.set(OPENAI_HEADERS.BETA, OPENAI_HEADER_VALUES.BETA_RESPONSES);
|
|
1261
1262
|
headers.set(OPENAI_HEADERS.ORIGINATOR, OPENAI_HEADER_VALUES.ORIGINATOR_CODEX);
|
|
1262
|
-
headers.set(
|
|
1263
|
+
headers.set(
|
|
1264
|
+
"User-Agent",
|
|
1265
|
+
`pi/${packageJson.version} (${sanitizeHeaderComponent(os.platform())} ${sanitizeHeaderComponent(os.release())}; ${sanitizeHeaderComponent(os.arch())})`,
|
|
1266
|
+
);
|
|
1263
1267
|
if (sessionId) {
|
|
1264
1268
|
headers.set(OPENAI_HEADERS.CONVERSATION_ID, sessionId);
|
|
1265
1269
|
headers.set(OPENAI_HEADERS.SESSION_ID, sessionId);
|
package/src/tools/index.ts
CHANGED
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
BUILTIN_TOOLS,
|
|
33
33
|
HIDDEN_TOOL_DESCRIPTORS,
|
|
34
34
|
LazyAgentTool,
|
|
35
|
+
PLATFORM_EXCLUDED_TOOL_DESCRIPTORS,
|
|
35
36
|
resolveEffectiveDiscoveryMode,
|
|
36
37
|
type ToolAvailabilityContext,
|
|
37
38
|
} from "./descriptors";
|
|
@@ -50,6 +51,18 @@ export type { WriteToolDetails, WriteToolInput } from "./write";
|
|
|
50
51
|
/** Tool type (AgentTool from pi-ai) */
|
|
51
52
|
export type Tool = AgentTool<any, any, any>;
|
|
52
53
|
|
|
54
|
+
/** Built-in automation surfaces that an SDK host may back with its own transport. */
|
|
55
|
+
export type AutomationToolName = "browser" | "computer";
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Host-owned implementations for the built-in automation surfaces.
|
|
59
|
+
*
|
|
60
|
+
* Each implementation must use the matching built-in name. The normal
|
|
61
|
+
* AgentTool execute signature carries cancellation through its AbortSignal,
|
|
62
|
+
* while the implementation owns the model-facing schema and description.
|
|
63
|
+
*/
|
|
64
|
+
export type AutomationTools = Partial<Record<AutomationToolName, Tool>>;
|
|
65
|
+
|
|
53
66
|
export type ContextFileEntry = {
|
|
54
67
|
path: string;
|
|
55
68
|
content: string;
|
|
@@ -179,6 +192,16 @@ export interface ToolSession {
|
|
|
179
192
|
/** Pre-loaded workspace tree (forwarded to subagents to skip re-scanning) */
|
|
180
193
|
workspaceTree?: WorkspaceTree;
|
|
181
194
|
/** Pre-loaded skills */
|
|
195
|
+
/**
|
|
196
|
+
* Explicit user home for runtime skill discovery. Tests construct sessions
|
|
197
|
+
* against an isolated home because the trusted-home resolver deliberately
|
|
198
|
+
* ignores `$HOME` on Linux (it reads the NSS account database instead), so a
|
|
199
|
+
* `process.env.HOME` override cannot steer user-scope discovery there.
|
|
200
|
+
*
|
|
201
|
+
* Production sessions leave this unset and the trusted OS home governs.
|
|
202
|
+
* Runtime discovery only; never threaded into capability loading.
|
|
203
|
+
*/
|
|
204
|
+
home?: string;
|
|
182
205
|
skills?: Skill[];
|
|
183
206
|
/** Currently executing skill prompt, when this tool session is inside one. */
|
|
184
207
|
getActiveSkillState?: () => Pick<SkillActiveEntry, "skill" | "session_id"> | undefined;
|
|
@@ -309,6 +332,18 @@ export interface ToolSession {
|
|
|
309
332
|
goalToolAllowedOps?: readonly ("create" | "get" | "complete" | "resume" | "drop" | "pause")[];
|
|
310
333
|
/** Goal runtime for the active agent session. */
|
|
311
334
|
getGoalRuntime?: () => GoalRuntime | undefined;
|
|
335
|
+
/**
|
|
336
|
+
* Agent-invokable session rescope (issue #4629): move the whole session to
|
|
337
|
+
* an existing directory, running the same sequence as the `/move` handler
|
|
338
|
+
* (flush → moveTo → setProjectDir → plugin/capability cache reset). Absent
|
|
339
|
+
* in contexts where relocation must not happen: subagent sessions
|
|
340
|
+
* (taskDepth > 0) and read-only/restricted bash profiles. Bound to one
|
|
341
|
+
* successful move per session, rejects re-entrant calls, and only narrows:
|
|
342
|
+
* the canonical (realpath) target must be a strict descendant of the
|
|
343
|
+
* canonical current cwd. Throws when the target does not exist, is not a
|
|
344
|
+
* directory, or escapes the current scope.
|
|
345
|
+
*/
|
|
346
|
+
rescopeSessionCwd?: (path: string) => Promise<{ from: string; to: string }>;
|
|
312
347
|
/** Bridge to the connected client (e.g. ACP editor host). Tools should route fs/terminal/permission requests through this when available. */
|
|
313
348
|
getClientBridge?: () => ClientBridge | undefined;
|
|
314
349
|
/** Get compact conversation context for subagents (excludes tool results, system prompts) */
|
|
@@ -533,7 +568,11 @@ export function resolveEvalBackends(session: ToolSession): EvalBackendsAllowance
|
|
|
533
568
|
/**
|
|
534
569
|
* Create tools from the descriptor registry.
|
|
535
570
|
*/
|
|
536
|
-
export async function createTools(
|
|
571
|
+
export async function createTools(
|
|
572
|
+
session: ToolSession,
|
|
573
|
+
toolNames?: string[],
|
|
574
|
+
automationTools: AutomationTools = {},
|
|
575
|
+
): Promise<Tool[]> {
|
|
537
576
|
const includeYield = session.requireYieldTool === true;
|
|
538
577
|
const enableLsp = session.enableLsp ?? true;
|
|
539
578
|
let requestedTools =
|
|
@@ -614,8 +653,14 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
|
|
614
653
|
allowEval,
|
|
615
654
|
discoveryActive,
|
|
616
655
|
};
|
|
617
|
-
const
|
|
656
|
+
const builtinToolDescriptors: Record<string, (typeof BUILTIN_TOOL_DESCRIPTORS)[string]> = {
|
|
618
657
|
...BUILTIN_TOOL_DESCRIPTORS,
|
|
658
|
+
...(automationTools.computer && !BUILTIN_TOOL_DESCRIPTORS.computer
|
|
659
|
+
? { computer: PLATFORM_EXCLUDED_TOOL_DESCRIPTORS.computer }
|
|
660
|
+
: {}),
|
|
661
|
+
};
|
|
662
|
+
const allToolDescriptors: Record<string, (typeof BUILTIN_TOOL_DESCRIPTORS)[string]> = {
|
|
663
|
+
...builtinToolDescriptors,
|
|
619
664
|
...HIDDEN_TOOL_DESCRIPTORS,
|
|
620
665
|
};
|
|
621
666
|
const allToolDescriptorEntries = Object.entries(allToolDescriptors) as Array<
|
|
@@ -640,13 +685,21 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
|
|
640
685
|
const filteredRequestedTools = requestedTools
|
|
641
686
|
?.map(name => allToolsByRequestName.get(name))
|
|
642
687
|
.filter((entry): entry is [string, (typeof BUILTIN_TOOL_DESCRIPTORS)[string]] => entry !== undefined)
|
|
643
|
-
.filter(
|
|
688
|
+
.filter(
|
|
689
|
+
([name, descriptor]) =>
|
|
690
|
+
automationTools[name as AutomationToolName] !== undefined ||
|
|
691
|
+
descriptor.isAvailable(session, availabilityContext),
|
|
692
|
+
);
|
|
644
693
|
const baseEntries =
|
|
645
694
|
filteredRequestedTools !== undefined
|
|
646
695
|
? filteredRequestedTools.filter(([name]) => name !== "resolve")
|
|
647
696
|
: [
|
|
648
|
-
...Object.entries(
|
|
649
|
-
.filter(
|
|
697
|
+
...Object.entries(builtinToolDescriptors)
|
|
698
|
+
.filter(
|
|
699
|
+
([name, descriptor]) =>
|
|
700
|
+
automationTools[name as AutomationToolName] !== undefined ||
|
|
701
|
+
descriptor.isAvailable(session, availabilityContext),
|
|
702
|
+
)
|
|
650
703
|
.map(([name, descriptor]) => [name, descriptor] as const),
|
|
651
704
|
...(includeYield ? ([["yield", HIDDEN_TOOL_DESCRIPTORS.yield]] as const) : []),
|
|
652
705
|
];
|
|
@@ -657,6 +710,10 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
|
|
657
710
|
),
|
|
658
711
|
);
|
|
659
712
|
const materialize = async ([name, descriptor]: readonly [string, (typeof BUILTIN_TOOL_DESCRIPTORS)[string]]) => {
|
|
713
|
+
const externalAutomationTool = automationTools[name as AutomationToolName];
|
|
714
|
+
if (externalAutomationTool) {
|
|
715
|
+
return wrapToolWithMetaNotice(new LazyAgentTool(descriptor, externalAutomationTool, undefined, session));
|
|
716
|
+
}
|
|
660
717
|
const defer =
|
|
661
718
|
effectiveDiscoveryMode !== "off" &&
|
|
662
719
|
descriptor.metadata.loadMode === "discoverable" &&
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import type { AgentTool, AgentToolResult } from "@gajae-code/agent-core";
|
|
2
|
+
import type { Component } from "@gajae-code/tui";
|
|
3
|
+
import { Text } from "@gajae-code/tui";
|
|
4
|
+
import { prompt, sanitizeText } from "@gajae-code/utils";
|
|
5
|
+
import * as z from "zod/v4";
|
|
6
|
+
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
7
|
+
import type { Theme } from "../modes/theme/theme";
|
|
8
|
+
import moveSessionDescription from "../prompts/tools/move-session.md" with { type: "text" };
|
|
9
|
+
import type { ToolSession } from "./index";
|
|
10
|
+
import { Ellipsis, replaceTabs, shortenPath, truncateToWidth } from "./render-utils";
|
|
11
|
+
import { ToolError } from "./tool-errors";
|
|
12
|
+
|
|
13
|
+
const moveSessionSchema = z.object({
|
|
14
|
+
path: z.string().describe("target directory: absolute, or relative to the current session cwd"),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export type MoveSessionToolInput = z.infer<typeof moveSessionSchema>;
|
|
18
|
+
|
|
19
|
+
export interface MoveSessionToolDetails {
|
|
20
|
+
from: string;
|
|
21
|
+
to: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class MoveSessionTool implements AgentTool<typeof moveSessionSchema, MoveSessionToolDetails> {
|
|
25
|
+
readonly name = "move_session";
|
|
26
|
+
readonly label = "Move Session";
|
|
27
|
+
readonly loadMode = "essential" as const;
|
|
28
|
+
readonly description = prompt.render(moveSessionDescription);
|
|
29
|
+
readonly parameters = moveSessionSchema;
|
|
30
|
+
readonly strict = true;
|
|
31
|
+
readonly intent = "omit" as const;
|
|
32
|
+
// The move chdirs the process and resets global caches; it must not
|
|
33
|
+
// interleave with other tool executions across flush→moveTo→chdir.
|
|
34
|
+
readonly concurrency = "exclusive" as const;
|
|
35
|
+
// The move commits session-file relocation; aborting mid-sequence would
|
|
36
|
+
// leave a half-moved session with no rollback path.
|
|
37
|
+
readonly nonAbortable = true;
|
|
38
|
+
readonly #session: ToolSession;
|
|
39
|
+
|
|
40
|
+
constructor(session: ToolSession) {
|
|
41
|
+
this.#session = session;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async execute(_toolCallId: string, params: MoveSessionToolInput): Promise<AgentToolResult<MoveSessionToolDetails>> {
|
|
45
|
+
const rescope = this.#session.rescopeSessionCwd;
|
|
46
|
+
if (!rescope) {
|
|
47
|
+
throw new ToolError(
|
|
48
|
+
"This session cannot rescope its working directory; only top-level unrestrained sessions can move.",
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
if (typeof params.path !== "string" || params.path.trim() === "") {
|
|
52
|
+
throw new ToolError("path is required and must be a directory path.");
|
|
53
|
+
}
|
|
54
|
+
let moved: { from: string; to: string };
|
|
55
|
+
try {
|
|
56
|
+
moved = await rescope(params.path);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
throw new ToolError(error instanceof Error ? error.message : String(error));
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
content: [{ type: "text", text: `Session moved to ${moved.to} (from ${moved.from}).` }],
|
|
62
|
+
details: { from: moved.from, to: moved.to },
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface MoveSessionRenderArgs {
|
|
68
|
+
from: string;
|
|
69
|
+
to: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const MOVE_SESSION_PREVIEW_WIDTH = 120;
|
|
73
|
+
|
|
74
|
+
export const moveSessionToolRenderer = {
|
|
75
|
+
renderCall: (args: unknown): Component => {
|
|
76
|
+
const target =
|
|
77
|
+
typeof args === "object" && args !== null && typeof (args as { path?: unknown }).path === "string"
|
|
78
|
+
? (args as { path: string }).path
|
|
79
|
+
: "";
|
|
80
|
+
const text = truncateToWidth(
|
|
81
|
+
replaceTabs(sanitizeText(`move_session ${target}`)),
|
|
82
|
+
MOVE_SESSION_PREVIEW_WIDTH,
|
|
83
|
+
Ellipsis.Omit,
|
|
84
|
+
);
|
|
85
|
+
return new Text(text, 1, 1);
|
|
86
|
+
},
|
|
87
|
+
renderResult: (
|
|
88
|
+
result: { details?: unknown; isError?: boolean },
|
|
89
|
+
_options: RenderResultOptions & { renderContext?: Record<string, unknown> },
|
|
90
|
+
theme: Theme,
|
|
91
|
+
): Component => {
|
|
92
|
+
const details = (result.details ?? {}) as Partial<MoveSessionRenderArgs>;
|
|
93
|
+
const from = typeof details.from === "string" ? details.from : "";
|
|
94
|
+
const to = typeof details.to === "string" ? details.to : "";
|
|
95
|
+
const body = result.isError
|
|
96
|
+
? "move_session failed"
|
|
97
|
+
: `Session moved: ${shortenPath(sanitizeText(from))} → ${shortenPath(sanitizeText(to))}`;
|
|
98
|
+
return new Text(
|
|
99
|
+
theme.fg(
|
|
100
|
+
result.isError ? "error" : "accent",
|
|
101
|
+
truncateToWidth(replaceTabs(sanitizeText(body)), MOVE_SESSION_PREVIEW_WIDTH, Ellipsis.Omit),
|
|
102
|
+
),
|
|
103
|
+
1,
|
|
104
|
+
1,
|
|
105
|
+
);
|
|
106
|
+
},
|
|
107
|
+
};
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import type { AgentTool, AgentToolResult } from "@gajae-code/agent-core";
|
|
2
|
+
import { type Static, z } from "@gajae-code/ai/core";
|
|
3
|
+
import { logger } from "@gajae-code/utils";
|
|
4
|
+
import { disposeKernelSessionsByOwner, executePython } from "../eval/py/executor";
|
|
5
|
+
import type { ToolDefinition } from "../extensibility/extensions/types";
|
|
6
|
+
import { applyToolProxy } from "../extensibility/tool-proxy";
|
|
7
|
+
import {
|
|
8
|
+
openPythonKernelTranscript,
|
|
9
|
+
type PythonKernelTranscript,
|
|
10
|
+
type PythonTranscriptRecord,
|
|
11
|
+
} from "../gjc-runtime/python-transcript";
|
|
12
|
+
import { sessionIpykernelsArtifactsDir } from "../gjc-runtime/session-layout";
|
|
13
|
+
import pythonToolDescription from "../prompts/tools/python.md" with { type: "text" };
|
|
14
|
+
|
|
15
|
+
export const PYTHON_TOOL_NAME = "python";
|
|
16
|
+
|
|
17
|
+
export function pythonKernelOwnerId(sessionId: string): string {
|
|
18
|
+
return `python:${sessionId}`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface SessionPythonToolInput {
|
|
22
|
+
/** Working directory for kernel execution (session cwd). */
|
|
23
|
+
cwd: string;
|
|
24
|
+
/** Resolve the GJC session id used for the kernel owner and transcript paths. */
|
|
25
|
+
getSessionId: () => string | null;
|
|
26
|
+
/** Register cleanup with the current logical session lifecycle. */
|
|
27
|
+
registerSessionCleanup: (cleanup: () => Promise<void> | void) => void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const paramsSchema = z.object({
|
|
31
|
+
action: z
|
|
32
|
+
.enum(["execute", "clear"])
|
|
33
|
+
.default("execute")
|
|
34
|
+
.describe(
|
|
35
|
+
'"execute" runs `code` in the persistent per-session REPL and is the default. "clear" disposes this session\'s kernel; the next execute starts a fresh kernel.',
|
|
36
|
+
),
|
|
37
|
+
code: z
|
|
38
|
+
.string()
|
|
39
|
+
.optional()
|
|
40
|
+
.describe('Python source to execute when action is "execute" (required then, ignored for "clear").'),
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const NO_SESSION_ERROR = "Python requires a GJC session id. Start or resume a session before using this tool.";
|
|
44
|
+
|
|
45
|
+
interface TranscriptExecutionResult {
|
|
46
|
+
output: string;
|
|
47
|
+
exitCode: number | null;
|
|
48
|
+
cancelled: boolean;
|
|
49
|
+
truncated: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function errorMessage(error: unknown): string {
|
|
53
|
+
return error instanceof Error ? error.message : String(error);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function isCancellationError(error: unknown, signal: AbortSignal | undefined): boolean {
|
|
57
|
+
return (
|
|
58
|
+
signal?.aborted === true ||
|
|
59
|
+
(error instanceof DOMException && (error.name === "AbortError" || error.name === "TimeoutError")) ||
|
|
60
|
+
(error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError"))
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function appendFailureTrailer(output: string, appendFailure: string | undefined): string {
|
|
65
|
+
if (appendFailure === undefined) return output;
|
|
66
|
+
const trailer = `[transcript append failed: ${appendFailure}]`;
|
|
67
|
+
return output.length > 0 ? `${output}\n${trailer}` : trailer;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function createSessionPythonTool(input: SessionPythonToolInput): AgentTool {
|
|
71
|
+
let armedForSession: string | null = null;
|
|
72
|
+
const seenOwnerIds = new Set<string>();
|
|
73
|
+
let currentTranscript: PythonKernelTranscript | null = null;
|
|
74
|
+
|
|
75
|
+
const armCleanupForSession = (sessionId: string): void => {
|
|
76
|
+
if (armedForSession === sessionId) return;
|
|
77
|
+
input.registerSessionCleanup(async () => {
|
|
78
|
+
await Promise.all([...seenOwnerIds].map(ownerId => disposeKernelSessionsByOwner(ownerId)));
|
|
79
|
+
seenOwnerIds.clear();
|
|
80
|
+
currentTranscript = null;
|
|
81
|
+
armedForSession = null;
|
|
82
|
+
});
|
|
83
|
+
armedForSession = sessionId;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const appendTranscript = async (
|
|
87
|
+
sessionId: string,
|
|
88
|
+
code: string,
|
|
89
|
+
result: TranscriptExecutionResult,
|
|
90
|
+
): Promise<string | undefined> => {
|
|
91
|
+
if (currentTranscript === null) {
|
|
92
|
+
currentTranscript = openPythonKernelTranscript({
|
|
93
|
+
cwd: input.cwd,
|
|
94
|
+
sessionId,
|
|
95
|
+
kernelInstanceId: crypto.randomUUID(),
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
const record: PythonTranscriptRecord = {
|
|
99
|
+
timestamp: new Date().toISOString(),
|
|
100
|
+
code,
|
|
101
|
+
output: result.output,
|
|
102
|
+
exitCode: result.exitCode,
|
|
103
|
+
cancelled: result.cancelled,
|
|
104
|
+
truncated: result.truncated,
|
|
105
|
+
};
|
|
106
|
+
try {
|
|
107
|
+
await currentTranscript.append(record);
|
|
108
|
+
return undefined;
|
|
109
|
+
} catch (error) {
|
|
110
|
+
const message = errorMessage(error);
|
|
111
|
+
logger.warn("Python transcript append failed", { sessionId, error: message });
|
|
112
|
+
return message;
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const definition: ToolDefinition<typeof paramsSchema> = {
|
|
117
|
+
name: PYTHON_TOOL_NAME,
|
|
118
|
+
label: "Python",
|
|
119
|
+
description: pythonToolDescription,
|
|
120
|
+
parameters: paramsSchema,
|
|
121
|
+
defaultInactive: true,
|
|
122
|
+
concurrency: "exclusive",
|
|
123
|
+
async execute(
|
|
124
|
+
_toolCallId: string,
|
|
125
|
+
params: Static<typeof paramsSchema>,
|
|
126
|
+
signal?: AbortSignal,
|
|
127
|
+
): Promise<AgentToolResult> {
|
|
128
|
+
const sessionId = input.getSessionId();
|
|
129
|
+
if (sessionId === null) {
|
|
130
|
+
return {
|
|
131
|
+
content: [{ type: "text", text: NO_SESSION_ERROR }],
|
|
132
|
+
isError: true,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
armCleanupForSession(sessionId);
|
|
136
|
+
const ownerId = pythonKernelOwnerId(sessionId);
|
|
137
|
+
if (params.action === "clear") {
|
|
138
|
+
await disposeKernelSessionsByOwner(ownerId);
|
|
139
|
+
currentTranscript = null;
|
|
140
|
+
return {
|
|
141
|
+
content: [{ type: "text", text: "Python kernel cleared; the next execute starts a fresh kernel." }],
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
const code = params.code;
|
|
145
|
+
if (code === undefined) {
|
|
146
|
+
return {
|
|
147
|
+
content: [{ type: "text", text: 'Missing required "code" parameter for action "execute".' }],
|
|
148
|
+
isError: true,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
seenOwnerIds.add(ownerId);
|
|
153
|
+
try {
|
|
154
|
+
const result = await executePython(code, {
|
|
155
|
+
cwd: input.cwd,
|
|
156
|
+
kernelMode: "session",
|
|
157
|
+
sessionId: ownerId,
|
|
158
|
+
kernelOwnerId: ownerId,
|
|
159
|
+
artifactsDir: sessionIpykernelsArtifactsDir(input.cwd, sessionId),
|
|
160
|
+
signal,
|
|
161
|
+
onKernelStart: kernelInstanceId => {
|
|
162
|
+
if (currentTranscript?.kernelInstanceId !== kernelInstanceId) {
|
|
163
|
+
currentTranscript = openPythonKernelTranscript({
|
|
164
|
+
cwd: input.cwd,
|
|
165
|
+
sessionId,
|
|
166
|
+
kernelInstanceId,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
const appendFailure = await appendTranscript(sessionId, code, {
|
|
172
|
+
output: result.output,
|
|
173
|
+
exitCode: result.exitCode ?? null,
|
|
174
|
+
cancelled: result.cancelled,
|
|
175
|
+
truncated: result.truncated,
|
|
176
|
+
});
|
|
177
|
+
const output = result.output.length > 0 ? result.output : "(no output)";
|
|
178
|
+
return { content: [{ type: "text", text: appendFailureTrailer(output, appendFailure) }] };
|
|
179
|
+
} catch (error) {
|
|
180
|
+
const output = errorMessage(error);
|
|
181
|
+
const appendFailure = await appendTranscript(sessionId, code, {
|
|
182
|
+
output,
|
|
183
|
+
exitCode: null,
|
|
184
|
+
cancelled: isCancellationError(error, signal),
|
|
185
|
+
truncated: false,
|
|
186
|
+
});
|
|
187
|
+
return {
|
|
188
|
+
content: [{ type: "text", text: appendFailureTrailer(output, appendFailure) }],
|
|
189
|
+
isError: true,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
const agentTool = {
|
|
195
|
+
async execute(
|
|
196
|
+
toolCallId: string,
|
|
197
|
+
params: Static<typeof paramsSchema>,
|
|
198
|
+
signal?: AbortSignal,
|
|
199
|
+
): Promise<AgentToolResult> {
|
|
200
|
+
return definition.execute(toolCallId, params, signal, undefined, undefined as never);
|
|
201
|
+
},
|
|
202
|
+
} as AgentTool;
|
|
203
|
+
applyToolProxy(definition, agentTool);
|
|
204
|
+
return agentTool;
|
|
205
|
+
}
|
package/src/tools/read.ts
CHANGED
|
@@ -1429,18 +1429,28 @@ interface ResolvedSqliteReadPath {
|
|
|
1429
1429
|
* Directories return a formatted listing with modification times.
|
|
1430
1430
|
*/
|
|
1431
1431
|
/**
|
|
1432
|
-
*
|
|
1433
|
-
*
|
|
1434
|
-
*
|
|
1432
|
+
* Only an explicit bridge transport-unavailable marker authorizes falling back to
|
|
1433
|
+
* the agent host's disk. Raw OS errno values, including EACCES/EPERM, are
|
|
1434
|
+
* ambiguous at this boundary: treating them as transport failures can bypass a
|
|
1435
|
+
* remote client's access decision by reading the path locally.
|
|
1435
1436
|
*/
|
|
1436
|
-
function
|
|
1437
|
-
const
|
|
1437
|
+
function isExplicitTransportUnavailable(error: unknown): boolean {
|
|
1438
|
+
const directCode =
|
|
1438
1439
|
typeof error === "object" && error !== null && "code" in error ? (error as { code?: unknown }).code : undefined;
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1440
|
+
const namedCode = error instanceof Error ? error.name : undefined;
|
|
1441
|
+
const nestedCode =
|
|
1442
|
+
typeof error === "object" &&
|
|
1443
|
+
error !== null &&
|
|
1444
|
+
"data" in error &&
|
|
1445
|
+
typeof (error as { data?: unknown }).data === "object"
|
|
1446
|
+
? ((error as { data?: { code?: unknown } }).data?.code ?? undefined)
|
|
1447
|
+
: undefined;
|
|
1448
|
+
const code = directCode ?? nestedCode ?? namedCode;
|
|
1449
|
+
return code === "transport_unavailable" || code === "bridge_unavailable";
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
function isClientAuthorityDenial(error: unknown): boolean {
|
|
1453
|
+
return !isExplicitTransportUnavailable(error);
|
|
1444
1454
|
}
|
|
1445
1455
|
|
|
1446
1456
|
export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
@@ -2395,6 +2405,53 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
2395
2405
|
if (!bridge?.capabilities.readTextFile || !bridge.readTextFile) return undefined;
|
|
2396
2406
|
return bridge.readTextFile({ path: absolutePath, ...options });
|
|
2397
2407
|
}
|
|
2408
|
+
async #readMissingPathThroughBridge(
|
|
2409
|
+
absolutePath: string,
|
|
2410
|
+
parsed: ParsedSelector,
|
|
2411
|
+
_localReadPath: string,
|
|
2412
|
+
truncation: TruncationDirection | undefined,
|
|
2413
|
+
signal: AbortSignal | undefined,
|
|
2414
|
+
): Promise<AgentToolResult<ReadToolDetails> | undefined> {
|
|
2415
|
+
const bridgePromise = this.#routeReadThroughBridge(absolutePath);
|
|
2416
|
+
if (bridgePromise === undefined) return undefined;
|
|
2417
|
+
throwIfAborted(signal);
|
|
2418
|
+
try {
|
|
2419
|
+
const bridgeText = await bridgePromise;
|
|
2420
|
+
if (parsed.kind === "conflicts") return undefined;
|
|
2421
|
+
const bareEligible = parsed.kind === "none";
|
|
2422
|
+
const route = isMultiRange(parsed)
|
|
2423
|
+
? "local-multi-range"
|
|
2424
|
+
: bareEligible
|
|
2425
|
+
? "local-bare-stream"
|
|
2426
|
+
: isRawSelector(parsed)
|
|
2427
|
+
? "local-raw"
|
|
2428
|
+
: "local-range";
|
|
2429
|
+
const direction = resolveEffectiveDirection(truncation, route, this.session.settings);
|
|
2430
|
+
if (isMultiRange(parsed) && parsed.kind === "lines") {
|
|
2431
|
+
return this.#buildInMemoryMultiRangeResult(bridgeText, parsed.ranges, {
|
|
2432
|
+
details: { resolvedPath: absolutePath },
|
|
2433
|
+
sourcePath: absolutePath,
|
|
2434
|
+
entityLabel: "file",
|
|
2435
|
+
raw: isRawSelector(parsed),
|
|
2436
|
+
truncationDirection: direction,
|
|
2437
|
+
cacheLinesFor: absolutePath,
|
|
2438
|
+
});
|
|
2439
|
+
}
|
|
2440
|
+
const { offset, limit } = selToOffsetLimit(parsed);
|
|
2441
|
+
return this.#buildInMemoryTextResult(bridgeText, offset, limit, {
|
|
2442
|
+
details: { resolvedPath: absolutePath },
|
|
2443
|
+
sourcePath: absolutePath,
|
|
2444
|
+
entityLabel: "file",
|
|
2445
|
+
raw: isRawSelector(parsed),
|
|
2446
|
+
cacheLinesFor: absolutePath,
|
|
2447
|
+
truncationDirection: direction,
|
|
2448
|
+
});
|
|
2449
|
+
} catch (error) {
|
|
2450
|
+
if (isClientAuthorityDenial(error)) throw error;
|
|
2451
|
+
logger.warn("ACP fs readTextFile failed for a path missing on disk", { path: absolutePath, error });
|
|
2452
|
+
return undefined;
|
|
2453
|
+
}
|
|
2454
|
+
}
|
|
2398
2455
|
|
|
2399
2456
|
async #trySummarize(absolutePath: string, fileSize: number, signal?: AbortSignal): Promise<SummaryResult | null> {
|
|
2400
2457
|
if (fileSize > MAX_SUMMARY_BYTES) return null;
|
|
@@ -2402,10 +2459,17 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
2402
2459
|
try {
|
|
2403
2460
|
throwIfAborted(signal);
|
|
2404
2461
|
const bridgePromise = this.#routeReadThroughBridge(absolutePath);
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2462
|
+
let code: string;
|
|
2463
|
+
if (bridgePromise !== undefined) {
|
|
2464
|
+
try {
|
|
2465
|
+
code = await bridgePromise;
|
|
2466
|
+
} catch (error) {
|
|
2467
|
+
if (isClientAuthorityDenial(error)) throw error;
|
|
2468
|
+
code = await Bun.file(absolutePath).text();
|
|
2469
|
+
}
|
|
2470
|
+
} else {
|
|
2471
|
+
code = await Bun.file(absolutePath).text();
|
|
2472
|
+
}
|
|
2409
2473
|
throwIfAborted(signal);
|
|
2410
2474
|
if (countTextLines(code) > MAX_SUMMARY_LINES) return null;
|
|
2411
2475
|
|
|
@@ -2416,7 +2480,8 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
2416
2480
|
minBodyLines: this.session.settings.get("read.summarize.minBodyLines"),
|
|
2417
2481
|
minCommentLines: this.session.settings.get("read.summarize.minCommentLines"),
|
|
2418
2482
|
});
|
|
2419
|
-
} catch {
|
|
2483
|
+
} catch (error) {
|
|
2484
|
+
if (isClientAuthorityDenial(error)) throw error;
|
|
2420
2485
|
return null;
|
|
2421
2486
|
}
|
|
2422
2487
|
}
|
|
@@ -2663,6 +2728,14 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
2663
2728
|
isDirectory = stat.isDirectory();
|
|
2664
2729
|
} catch (error) {
|
|
2665
2730
|
if (isNotFoundError(error)) {
|
|
2731
|
+
const bridged = await this.#readMissingPathThroughBridge(
|
|
2732
|
+
absolutePath,
|
|
2733
|
+
parsed,
|
|
2734
|
+
localReadPath,
|
|
2735
|
+
params.truncation,
|
|
2736
|
+
signal,
|
|
2737
|
+
);
|
|
2738
|
+
if (bridged) return bridged;
|
|
2666
2739
|
// Attempt unique suffix resolution before falling back to fuzzy suggestions
|
|
2667
2740
|
if (!isRemoteMountPath(absolutePath)) {
|
|
2668
2741
|
const suffixMatch = await findUniqueSuffixMatch(localReadPath, this.session.cwd, signal);
|
package/src/tools/renderers.ts
CHANGED
|
@@ -23,6 +23,7 @@ import { evalToolRenderer } from "./eval";
|
|
|
23
23
|
import { findToolRenderer } from "./find";
|
|
24
24
|
import { githubToolRenderer } from "./gh-renderer";
|
|
25
25
|
import { jobToolRenderer } from "./job";
|
|
26
|
+
import { moveSessionToolRenderer } from "./move-session";
|
|
26
27
|
import { readToolRenderer } from "./read";
|
|
27
28
|
import { recipeToolRenderer } from "./recipe/render";
|
|
28
29
|
import { resolveToolRenderer } from "./resolve";
|
|
@@ -72,6 +73,7 @@ export const toolRenderers: Record<string, ToolRenderer> = {
|
|
|
72
73
|
todo_write: todoWriteToolRenderer as ToolRenderer,
|
|
73
74
|
github: githubToolRenderer as ToolRenderer,
|
|
74
75
|
goal: goalToolRenderer as ToolRenderer,
|
|
76
|
+
move_session: moveSessionToolRenderer as ToolRenderer,
|
|
75
77
|
web_search: webSearchToolRenderer as ToolRenderer,
|
|
76
78
|
write: writeToolRenderer as ToolRenderer,
|
|
77
79
|
};
|