@oh-my-pi/pi-coding-agent 17.0.8 → 17.1.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 +87 -1
- package/README.md +1 -1
- package/dist/cli.js +5495 -5021
- package/dist/types/async/job-manager.d.ts +37 -1
- package/dist/types/cli/args.d.ts +2 -0
- package/dist/types/cli/bench-cli.d.ts +41 -0
- package/dist/types/commands/bench.d.ts +15 -3
- package/dist/types/commands/launch.d.ts +4 -0
- package/dist/types/config/__tests__/model-registry.test.d.ts +1 -0
- package/dist/types/config/model-registry.d.ts +30 -1
- package/dist/types/config/models-config-schema.d.ts +18 -6
- package/dist/types/config/models-config.d.ts +14 -5
- package/dist/types/config/provider-globals.d.ts +2 -2
- package/dist/types/config/service-tier.d.ts +5 -1
- package/dist/types/config/settings-schema.d.ts +350 -50
- package/dist/types/config/settings.d.ts +7 -0
- package/dist/types/dap/client.d.ts +20 -0
- package/dist/types/exec/bash-executor.d.ts +38 -0
- package/dist/types/exec/direnv.d.ts +33 -0
- package/dist/types/export/html/args.d.ts +15 -0
- package/dist/types/export/html/index.d.ts +11 -26
- package/dist/types/export/html/web-palette.d.ts +55 -116
- package/dist/types/extensibility/extensions/loader.d.ts +3 -0
- package/dist/types/extensibility/extensions/types.d.ts +17 -2
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +1 -0
- package/dist/types/extensibility/legacy-pi-tui-shim.d.ts +10 -0
- package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +14 -1
- package/dist/types/internal-urls/registry-helpers.d.ts +11 -0
- package/dist/types/live/controller.d.ts +40 -0
- package/dist/types/live/protocol.d.ts +85 -0
- package/dist/types/live/protocol.test.d.ts +1 -0
- package/dist/types/live/transport.d.ts +48 -0
- package/dist/types/live/visualizer.d.ts +33 -0
- package/dist/types/lsp/client.d.ts +18 -1
- package/dist/types/mcp/config-writer.test.d.ts +1 -0
- package/dist/types/mcp/manager.d.ts +6 -2
- package/dist/types/mcp/oauth-discovery.d.ts +2 -0
- package/dist/types/mcp/render.d.ts +2 -2
- package/dist/types/mcp/smithery-auth.d.ts +1 -1
- package/dist/types/mcp/smithery-auth.test.d.ts +1 -0
- package/dist/types/mcp/tool-bridge.d.ts +7 -3
- package/dist/types/mcp/types.d.ts +6 -0
- package/dist/types/memory-backend/tool-names.d.ts +2 -0
- package/dist/types/modes/components/login-dialog.d.ts +2 -2
- package/dist/types/modes/components/oauth-selector.d.ts +8 -0
- package/dist/types/modes/components/plan-review-overlay.d.ts +26 -0
- package/dist/types/modes/components/settings-defs.d.ts +8 -1
- package/dist/types/modes/components/status-line/component.jj-cache.test.d.ts +1 -0
- package/dist/types/modes/components/tool-execution.d.ts +9 -1
- package/dist/types/modes/controllers/command-controller.d.ts +1 -1
- package/dist/types/modes/controllers/event-controller.d.ts +6 -1
- package/dist/types/modes/controllers/live-command-controller.d.ts +14 -0
- package/dist/types/modes/controllers/mcp-command-controller.d.ts +3 -0
- package/dist/types/modes/interactive-mode.d.ts +5 -0
- package/dist/types/modes/rpc/rpc-client.d.ts +10 -1
- package/dist/types/modes/rpc/rpc-frame.d.ts +16 -0
- package/dist/types/modes/rpc/rpc-messages.d.ts +26 -0
- package/dist/types/modes/rpc/rpc-types.d.ts +40 -0
- package/dist/types/modes/setup-wizard/scenes/sign-in.d.ts +1 -1
- package/dist/types/modes/setup-wizard/scenes/types.d.ts +9 -1
- package/dist/types/modes/setup-wizard/scenes/web-search.d.ts +1 -1
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +3 -1
- package/dist/types/modes/utils/context-usage.d.ts +12 -2
- package/dist/types/plan-mode/plan-files.d.ts +10 -0
- package/dist/types/registry/agent-lifecycle.d.ts +21 -9
- package/dist/types/registry/agent-registry.d.ts +11 -4
- package/dist/types/sdk.d.ts +15 -1
- package/dist/types/secrets/index.d.ts +15 -2
- package/dist/types/secrets/obfuscator.d.ts +113 -15
- package/dist/types/session/acp-permission-gate.d.ts +19 -0
- package/dist/types/session/agent-session-events.d.ts +84 -0
- package/dist/types/session/agent-session-types.d.ts +314 -0
- package/dist/types/session/agent-session.d.ts +120 -750
- package/dist/types/session/async-job-delivery.d.ts +28 -0
- package/dist/types/session/bash-runner.d.ts +75 -0
- package/dist/types/session/blob-store.d.ts +11 -1
- package/dist/types/session/blob-store.test.d.ts +1 -0
- package/dist/types/session/checkpoint-entries.d.ts +28 -0
- package/dist/types/session/eval-runner.d.ts +49 -0
- package/dist/types/session/irc-bridge.d.ts +46 -0
- package/dist/types/session/messages.d.ts +22 -0
- package/dist/types/session/model-controls.d.ts +180 -0
- package/dist/types/session/prewalk.d.ts +46 -0
- package/dist/types/session/queued-messages.d.ts +22 -0
- package/dist/types/session/redis-session-storage.d.ts +1 -0
- package/dist/types/session/retry-fallback-chains.d.ts +74 -0
- package/dist/types/session/role-models.d.ts +15 -0
- package/dist/types/session/session-advisors.d.ts +228 -0
- package/dist/types/session/session-entries.d.ts +8 -0
- package/dist/types/session/session-handoff.d.ts +70 -0
- package/dist/types/session/session-history-format.d.ts +6 -1
- package/dist/types/session/session-loader.d.ts +6 -4
- package/dist/types/session/session-maintenance.d.ts +246 -0
- package/dist/types/session/session-manager.d.ts +41 -0
- package/dist/types/session/session-memory.d.ts +56 -0
- package/dist/types/session/session-provider-boundary.d.ts +62 -0
- package/dist/types/session/session-stats.d.ts +48 -0
- package/dist/types/session/session-tools.d.ts +143 -0
- package/dist/types/session/session-workspace.d.ts +32 -0
- package/dist/types/session/stream-guards.d.ts +44 -0
- package/dist/types/session/todo-tracker.d.ts +56 -0
- package/dist/types/session/ttsr-coordinator.d.ts +51 -0
- package/dist/types/session/turn-recovery.d.ts +225 -0
- package/dist/types/system-prompt.d.ts +2 -0
- package/dist/types/task/executor.d.ts +4 -1
- package/dist/types/task/output-manager.d.ts +3 -1
- package/dist/types/task/types.d.ts +14 -0
- package/dist/types/tools/approval.d.ts +1 -0
- package/dist/types/tools/browser/launch.d.ts +5 -0
- package/dist/types/tools/hub/jobs.d.ts +1 -1
- package/dist/types/tools/image-gen.d.ts +4 -4
- package/dist/types/tools/image-providers.d.ts +38 -0
- package/dist/types/tools/index.d.ts +8 -0
- package/dist/types/tools/path-utils.d.ts +8 -0
- package/dist/types/tools/report-tool-issue.d.ts +24 -9
- package/dist/types/tools/todo.d.ts +10 -4
- package/dist/types/tools/xdev.d.ts +5 -1
- package/dist/types/tts/streaming-player.d.ts +14 -0
- package/dist/types/tts/vocalizer.d.ts +5 -0
- package/dist/types/utils/jj.d.ts +29 -0
- package/dist/types/utils/lang-from-path.d.ts +1 -0
- package/dist/types/utils/title-generator.d.ts +30 -0
- package/dist/types/vibe/runtime.d.ts +36 -8
- package/dist/types/web/search/index.d.ts +1 -1
- package/dist/types/web/search/provider.d.ts +15 -5
- package/dist/types/web/search/providers/firecrawl.d.ts +9 -0
- package/dist/types/web/search/types.d.ts +95 -1
- package/package.json +13 -12
- package/scripts/generate-share-viewer.ts +4 -6
- package/scripts/legacy-pi-virtual-module.ts +1 -1
- package/src/advisor/__tests__/advisor.test.ts +643 -0
- package/src/advisor/runtime.ts +144 -43
- package/src/async/job-manager.ts +90 -2
- package/src/cli/args.ts +2 -0
- package/src/cli/auth-gateway-cli.ts +18 -3
- package/src/cli/bench-cli.ts +395 -29
- package/src/cli/flag-tables.ts +3 -0
- package/src/cli/gc-cli.ts +2 -2
- package/src/cli/grievances-cli.ts +2 -2
- package/src/cli/usage-cli.ts +11 -2
- package/src/collab/host.ts +3 -2
- package/src/commands/bench.ts +18 -4
- package/src/commands/launch.ts +4 -0
- package/src/config/__tests__/model-registry.test.ts +147 -0
- package/src/config/model-registry.ts +50 -2
- package/src/config/model-resolver.ts +0 -1
- package/src/config/models-config-schema.ts +15 -4
- package/src/config/provider-globals.ts +9 -9
- package/src/config/service-tier.ts +26 -1
- package/src/config/settings-schema.ts +250 -54
- package/src/config/settings.ts +81 -2
- package/src/dap/client.ts +37 -4
- package/src/edit/index.ts +49 -12
- package/src/eval/py/executor.ts +103 -33
- package/src/exec/bash-executor.ts +89 -6
- package/src/exec/direnv.ts +145 -0
- package/src/export/html/args.ts +20 -0
- package/src/export/html/index.ts +45 -47
- package/src/export/html/template.css +19 -1
- package/src/export/html/template.html +6 -0
- package/src/export/html/template.js +21 -0
- package/src/export/html/tool-views.generated.js +31 -31
- package/src/export/html/web-palette.ts +116 -132
- package/src/export/share.ts +249 -49
- package/src/extensibility/extensions/loader.ts +29 -1
- package/src/extensibility/extensions/runner.ts +6 -0
- package/src/extensibility/extensions/types.ts +33 -2
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +1 -0
- package/src/extensibility/legacy-pi-tui-shim.ts +10 -0
- package/src/extensibility/plugins/legacy-pi-compat.ts +870 -311
- package/src/hindsight/state.ts +64 -6
- package/src/internal-urls/local-protocol.ts +2 -1
- package/src/internal-urls/memory-protocol.ts +2 -2
- package/src/internal-urls/registry-helpers.ts +40 -0
- package/src/internal-urls/skill-protocol.ts +2 -2
- package/src/internal-urls/ssh-protocol.ts +2 -1
- package/src/internal-urls/vault-protocol.ts +2 -1
- package/src/live/audio-worklet.txt +59 -0
- package/src/live/browser-runtime.txt +221 -0
- package/src/live/controller.ts +464 -0
- package/src/live/prompts/agent-final-message.md +3 -0
- package/src/live/prompts/live-instructions.md +23 -0
- package/src/live/protocol.test.ts +140 -0
- package/src/live/protocol.ts +233 -0
- package/src/live/transport.ts +490 -0
- package/src/live/visualizer.ts +234 -0
- package/src/lsp/client.ts +52 -9
- package/src/lsp/clients/biome-client.ts +3 -4
- package/src/lsp/index.ts +44 -11
- package/src/main.ts +9 -1
- package/src/mcp/config-writer.test.ts +43 -0
- package/src/mcp/config-writer.ts +109 -82
- package/src/mcp/manager.ts +39 -8
- package/src/mcp/oauth-discovery.ts +10 -2
- package/src/mcp/render.ts +94 -35
- package/src/mcp/smithery-auth.test.ts +29 -0
- package/src/mcp/smithery-auth.ts +3 -2
- package/src/mcp/tool-bridge.ts +107 -11
- package/src/mcp/types.ts +7 -0
- package/src/memories/index.ts +40 -20
- package/src/memory-backend/tool-names.ts +2 -0
- package/src/mnemopi/backend.ts +24 -7
- package/src/modes/acp/acp-agent.ts +56 -10
- package/src/modes/acp/acp-event-mapper.ts +8 -1
- package/src/modes/components/agent-hub.ts +27 -10
- package/src/modes/components/login-dialog.ts +14 -4
- package/src/modes/components/oauth-selector.ts +24 -7
- package/src/modes/components/plan-review-overlay.ts +350 -35
- package/src/modes/components/settings-defs.ts +28 -3
- package/src/modes/components/settings-selector.ts +178 -6
- package/src/modes/components/status-line/component.jj-cache.test.ts +229 -0
- package/src/modes/components/status-line/component.ts +123 -2
- package/src/modes/components/tool-execution.test.ts +63 -2
- package/src/modes/components/tool-execution.ts +11 -2
- package/src/modes/controllers/command-controller.ts +70 -22
- package/src/modes/controllers/event-controller.ts +111 -16
- package/src/modes/controllers/extension-ui-controller.test.ts +1 -0
- package/src/modes/controllers/extension-ui-controller.ts +20 -4
- package/src/modes/controllers/live-command-controller.ts +178 -0
- package/src/modes/controllers/mcp-command-controller.ts +80 -41
- package/src/modes/controllers/omfg-controller.ts +44 -40
- package/src/modes/controllers/selector-controller.ts +18 -10
- package/src/modes/controllers/session-focus-controller.ts +6 -1
- package/src/modes/interactive-mode.ts +160 -38
- package/src/modes/rpc/rpc-client.ts +94 -3
- package/src/modes/rpc/rpc-frame.ts +164 -4
- package/src/modes/rpc/rpc-messages.ts +127 -0
- package/src/modes/rpc/rpc-mode.ts +79 -7
- package/src/modes/rpc/rpc-types.ts +34 -2
- package/src/modes/runtime-init.ts +2 -0
- package/src/modes/setup-wizard/scenes/model.ts +5 -7
- package/src/modes/setup-wizard/scenes/providers.ts +3 -2
- package/src/modes/setup-wizard/scenes/sign-in.ts +8 -2
- package/src/modes/setup-wizard/scenes/theme.ts +13 -3
- package/src/modes/setup-wizard/scenes/types.ts +9 -1
- package/src/modes/setup-wizard/scenes/web-search.ts +23 -10
- package/src/modes/setup-wizard/wizard-overlay.ts +1 -1
- package/src/modes/theme/theme.ts +1 -1
- package/src/modes/types.ts +3 -1
- package/src/modes/utils/context-usage.ts +23 -7
- package/src/modes/utils/ui-helpers.ts +10 -5
- package/src/plan-mode/plan-files.ts +40 -0
- package/src/prompts/bench/cache-prefix-chunk.md +1 -0
- package/src/prompts/bench/cache-prefix.md +3 -0
- package/src/prompts/bench/cache-suffix.md +1 -0
- package/src/prompts/goals/guided-goal-system.md +24 -3
- package/src/prompts/system/custom-system-prompt.md +1 -1
- package/src/prompts/system/project-prompt.md +8 -1
- package/src/prompts/system/subagent-async-pending.md +6 -0
- package/src/prompts/system/system-prompt.md +1 -1
- package/src/prompts/system/workflow-notice.md +48 -40
- package/src/prompts/system/xdev-mount-notice.md +4 -0
- package/src/prompts/tools/task.md +12 -2
- package/src/prompts/tools/todo.md +3 -1
- package/src/registry/agent-lifecycle.ts +116 -46
- package/src/registry/agent-registry.ts +35 -8
- package/src/registry/persisted-agents.ts +26 -2
- package/src/sdk.ts +255 -139
- package/src/secrets/index.ts +127 -4
- package/src/secrets/obfuscator.ts +2328 -105
- package/src/session/acp-permission-gate.ts +165 -0
- package/src/session/agent-session-error-log.test.ts +1 -1
- package/src/session/agent-session-events.ts +66 -0
- package/src/session/agent-session-types.ts +334 -0
- package/src/session/agent-session.ts +1910 -12213
- package/src/session/artifacts.ts +6 -5
- package/src/session/async-job-delivery.ts +74 -0
- package/src/session/bash-runner.ts +326 -0
- package/src/session/blob-store.test.ts +56 -0
- package/src/session/blob-store.ts +18 -2
- package/src/session/checkpoint-entries.ts +81 -0
- package/src/session/eval-runner.ts +212 -0
- package/src/session/indexed-session-storage.ts +9 -2
- package/src/session/irc-bridge.ts +203 -0
- package/src/session/messages.ts +263 -1
- package/src/session/model-controls.ts +714 -0
- package/src/session/prewalk.ts +252 -0
- package/src/session/queued-messages.ts +93 -0
- package/src/session/redis-session-storage.ts +51 -11
- package/src/session/retry-fallback-chains.ts +455 -0
- package/src/session/role-models.ts +85 -0
- package/src/session/session-advisors.ts +1679 -0
- package/src/session/session-context.test.ts +224 -1
- package/src/session/session-context.ts +47 -5
- package/src/session/session-entries.ts +8 -0
- package/src/session/session-handoff.ts +308 -0
- package/src/session/session-history-format.ts +20 -9
- package/src/session/session-loader.ts +10 -51
- package/src/session/session-maintenance.ts +2983 -0
- package/src/session/session-manager.ts +390 -30
- package/src/session/session-memory.ts +222 -0
- package/src/session/session-provider-boundary.ts +307 -0
- package/src/session/session-stats.ts +293 -0
- package/src/session/session-tools.ts +943 -0
- package/src/session/session-workspace.ts +53 -0
- package/src/session/stream-guards.ts +417 -0
- package/src/session/todo-tracker.ts +380 -0
- package/src/session/ttsr-coordinator.ts +496 -0
- package/src/session/turn-recovery.ts +1629 -0
- package/src/slash-commands/builtin-registry.ts +92 -11
- package/src/slash-commands/helpers/usage-report.ts +25 -4
- package/src/system-prompt.ts +41 -17
- package/src/task/executor.ts +288 -70
- package/src/task/index.ts +148 -111
- package/src/task/output-manager.ts +25 -3
- package/src/task/persisted-revive.ts +4 -3
- package/src/task/structured-subagent.ts +4 -1
- package/src/task/types.ts +16 -0
- package/src/tools/approval.ts +55 -10
- package/src/tools/bash-interactive.ts +26 -0
- package/src/tools/bash-skill-urls.ts +28 -2
- package/src/tools/bash.ts +339 -123
- package/src/tools/browser/launch.ts +9 -1
- package/src/tools/browser/tab-supervisor.ts +8 -5
- package/src/tools/browser.ts +94 -47
- package/src/tools/hub/index.ts +1 -1
- package/src/tools/hub/jobs.ts +67 -8
- package/src/tools/image-gen.ts +20 -28
- package/src/tools/image-providers.ts +50 -0
- package/src/tools/index.ts +12 -0
- package/src/tools/path-utils.ts +49 -2
- package/src/tools/read.ts +156 -88
- package/src/tools/renderers.ts +7 -1
- package/src/tools/report-tool-issue.ts +79 -28
- package/src/tools/todo.ts +101 -11
- package/src/tools/vibe.ts +1 -2
- package/src/tools/write.ts +51 -1
- package/src/tools/xdev.ts +67 -3
- package/src/tts/streaming-player.ts +62 -4
- package/src/tts/vocalizer.ts +18 -1
- package/src/utils/jj.ts +125 -4
- package/src/utils/lang-from-path.ts +7 -0
- package/src/utils/title-generator.ts +137 -1
- package/src/vibe/runtime.ts +852 -72
- package/src/web/search/index.ts +3 -6
- package/src/web/search/provider.ts +31 -16
- package/src/web/search/providers/firecrawl.ts +46 -13
- package/src/web/search/providers/xai.ts +7 -1
- package/src/web/search/types.ts +8 -1
package/src/task/index.ts
CHANGED
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
// Import review tools for side effects (registers subagent tool handlers)
|
|
42
42
|
import "../tools/review";
|
|
43
43
|
import type { AsyncJobManager } from "../async";
|
|
44
|
+
import { hasResolvableTranscript } from "../internal-urls/registry-helpers";
|
|
44
45
|
import { AgentRegistry } from "../registry/agent-registry";
|
|
45
46
|
import { type DiscoveryResult, discoverAgents } from "./discovery";
|
|
46
47
|
import { generateTaskName } from "./name-generator";
|
|
@@ -160,6 +161,7 @@ export function formatResultOutputFallback(result: Pick<SingleResult, "output" |
|
|
|
160
161
|
function renderDescription(
|
|
161
162
|
agents: AgentDefinition[],
|
|
162
163
|
isolationEnabled: boolean,
|
|
164
|
+
applyIsolatedChanges: boolean,
|
|
163
165
|
disabledAgents: string[],
|
|
164
166
|
batchEnabled: boolean,
|
|
165
167
|
asyncEnabled: boolean,
|
|
@@ -185,8 +187,8 @@ function renderDescription(
|
|
|
185
187
|
agents: renderedAgents,
|
|
186
188
|
spawningDisabled,
|
|
187
189
|
defaultAgent: spawnPolicy.defaultAgent,
|
|
188
|
-
allowedAgentsText: spawnPolicy.allowedPromptText,
|
|
189
190
|
isolationEnabled,
|
|
191
|
+
applyIsolatedChanges,
|
|
190
192
|
batchEnabled,
|
|
191
193
|
asyncEnabled,
|
|
192
194
|
hasBlockingAgents: renderedAgents.some(agent => agent.blocking),
|
|
@@ -229,6 +231,19 @@ function validateShapeParams(batchEnabled: boolean, params: TaskParams): string
|
|
|
229
231
|
* policy later, in `spawnParamsFor`. Returns a problem description, or
|
|
230
232
|
* undefined when valid.
|
|
231
233
|
*/
|
|
234
|
+
function hasInvalidModelSelector(model: unknown): boolean {
|
|
235
|
+
if (model === undefined) return false;
|
|
236
|
+
const selectors = typeof model === "string" ? [model] : Array.isArray(model) ? model : undefined;
|
|
237
|
+
const materializedSelectors = selectors ? Array.from(selectors) : [];
|
|
238
|
+
return (
|
|
239
|
+
!selectors ||
|
|
240
|
+
materializedSelectors.length === 0 ||
|
|
241
|
+
materializedSelectors.some(
|
|
242
|
+
selector => typeof selector !== "string" || !selector.split(",").some(pattern => pattern.trim()),
|
|
243
|
+
)
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
232
247
|
function validateSpawnParams(params: TaskParams, batchEnabled: boolean): string | undefined {
|
|
233
248
|
const hasTask = typeof params.task === "string" && params.task.trim() !== "";
|
|
234
249
|
const tasks = params.tasks;
|
|
@@ -244,6 +259,9 @@ function validateSpawnParams(params: TaskParams, batchEnabled: boolean): string
|
|
|
244
259
|
if (!item || typeof item.task !== "string" || item.task.trim() === "") {
|
|
245
260
|
return `Task ${i + 1}${item?.name ? ` (\`${item.name}\`)` : ""} is missing \`task\`. Every task needs complete, self-contained instructions.`;
|
|
246
261
|
}
|
|
262
|
+
if (hasInvalidModelSelector(item.model)) {
|
|
263
|
+
return `Task ${i + 1}${item.name ? ` (\`${item.name}\`)` : ""} has an invalid \`model\`. Provide a non-empty selector or a non-empty array of non-empty selectors.`;
|
|
264
|
+
}
|
|
247
265
|
}
|
|
248
266
|
const seen = new Map<string, string>();
|
|
249
267
|
for (const item of tasks) {
|
|
@@ -266,6 +284,9 @@ function validateSpawnParams(params: TaskParams, batchEnabled: boolean): string
|
|
|
266
284
|
? "Missing `tasks`. Provide a `tasks` array (one subagent per item) with a shared `context`."
|
|
267
285
|
: "Missing `task`. Provide complete, self-contained instructions for the agent.";
|
|
268
286
|
}
|
|
287
|
+
if (hasInvalidModelSelector(params.model)) {
|
|
288
|
+
return "Invalid `model`. Provide a non-empty selector or a non-empty array of non-empty selectors.";
|
|
289
|
+
}
|
|
269
290
|
return undefined;
|
|
270
291
|
}
|
|
271
292
|
|
|
@@ -279,7 +300,7 @@ function resolveSpawnItems(params: TaskParams): TaskItem[] {
|
|
|
279
300
|
if (Array.isArray(params.tasks) && params.tasks.length > 0) {
|
|
280
301
|
return params.tasks;
|
|
281
302
|
}
|
|
282
|
-
const item: TaskItem = { name: params.name, agent: params.agent, task: params.task };
|
|
303
|
+
const item: TaskItem = { name: params.name, agent: params.agent, task: params.task, model: params.model };
|
|
283
304
|
if ("outputSchema" in params) item.outputSchema = params.outputSchema;
|
|
284
305
|
if ("schemaMode" in params) item.schemaMode = params.schemaMode;
|
|
285
306
|
if ("isolated" in params) item.isolated = params.isolated;
|
|
@@ -299,6 +320,7 @@ function spawnParamsFor(params: TaskParams, item: TaskItem, defaultAgent: string
|
|
|
299
320
|
const spawn: TaskParams = { agent: item.agent?.trim() || defaultAgent };
|
|
300
321
|
if (item.name !== undefined) spawn.name = item.name;
|
|
301
322
|
if (item.task !== undefined) spawn.task = item.task;
|
|
323
|
+
if (item.model !== undefined) spawn.model = item.model;
|
|
302
324
|
if (params.context !== undefined) spawn.context = params.context;
|
|
303
325
|
if ("outputSchema" in item) spawn.outputSchema = item.outputSchema;
|
|
304
326
|
if ("schemaMode" in item) spawn.schemaMode = item.schemaMode;
|
|
@@ -469,6 +491,14 @@ function discoverAgentsForCreate(cwd: string): Promise<DiscoveryResult> {
|
|
|
469
491
|
return pending;
|
|
470
492
|
}
|
|
471
493
|
|
|
494
|
+
function formatModelForApproval(model: unknown): string | undefined {
|
|
495
|
+
const selectors = typeof model === "string" ? [model] : Array.isArray(model) ? model : [];
|
|
496
|
+
const normalized = selectors.filter(
|
|
497
|
+
(selector): selector is string => typeof selector === "string" && !!selector.trim(),
|
|
498
|
+
);
|
|
499
|
+
return normalized.length > 0 ? truncateForPrompt(normalized.join(" → ")) : undefined;
|
|
500
|
+
}
|
|
501
|
+
|
|
472
502
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
473
503
|
// Tool Class
|
|
474
504
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -492,23 +522,43 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
492
522
|
if (typeof params.name === "string" && params.name.trim()) {
|
|
493
523
|
lines.push(`Name: ${truncateForPrompt(params.name)}`);
|
|
494
524
|
}
|
|
525
|
+
const model = formatModelForApproval(params.model);
|
|
526
|
+
if (model) lines.push(`Model: ${model}`);
|
|
495
527
|
if (typeof params.task === "string") {
|
|
496
528
|
lines.push(`Task:\n${truncateForPrompt(params.task)}`);
|
|
497
529
|
}
|
|
498
530
|
if (typeof params.context === "string" && params.context.trim()) {
|
|
499
531
|
lines.push(`Context:\n${truncateForPrompt(params.context)}`);
|
|
500
532
|
}
|
|
501
|
-
const tasks = Array.isArray(params.tasks) ? params.tasks : [];
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
533
|
+
const tasks: unknown[] = Array.isArray(params.tasks) ? params.tasks : [];
|
|
534
|
+
if (tasks.length > 0) {
|
|
535
|
+
const defaultAgent = resolveSpawnPolicy(this.session.getSessionSpawns()).defaultAgent;
|
|
536
|
+
const effectiveAgent = (item: unknown): string => {
|
|
537
|
+
if (item && typeof item === "object" && "agent" in item) {
|
|
538
|
+
const agent = item.agent;
|
|
539
|
+
if (typeof agent === "string" && agent.trim()) return agent.trim();
|
|
540
|
+
}
|
|
541
|
+
return defaultAgent;
|
|
542
|
+
};
|
|
543
|
+
const agentCounts = new Map<string, number>();
|
|
544
|
+
for (const item of tasks) {
|
|
545
|
+
const agent = effectiveAgent(item);
|
|
546
|
+
agentCounts.set(agent, (agentCounts.get(agent) ?? 0) + 1);
|
|
509
547
|
}
|
|
510
|
-
|
|
511
|
-
|
|
548
|
+
const agentSummary = [...agentCounts].map(([agent, count]) => `${agent} ×${count}`).join(", ");
|
|
549
|
+
lines.push(`Batch agents: ${truncateForPrompt(agentSummary)}`);
|
|
550
|
+
|
|
551
|
+
const firstTask = tasks[0];
|
|
552
|
+
if (firstTask && typeof firstTask === "object") {
|
|
553
|
+
if ("name" in firstTask && typeof firstTask.name === "string" && firstTask.name.trim()) {
|
|
554
|
+
lines.push(`Name: ${truncateForPrompt(firstTask.name)}`);
|
|
555
|
+
}
|
|
556
|
+
lines.push(`Agent: ${truncateForPrompt(effectiveAgent(firstTask))}`);
|
|
557
|
+
const itemModel = formatModelForApproval("model" in firstTask ? firstTask.model : undefined);
|
|
558
|
+
if (itemModel) lines.push(`Model: ${itemModel}`);
|
|
559
|
+
if ("task" in firstTask && typeof firstTask.task === "string") {
|
|
560
|
+
lines.push(`Task:\n${truncateForPrompt(firstTask.task)}`);
|
|
561
|
+
}
|
|
512
562
|
}
|
|
513
563
|
if (tasks.length > 1) {
|
|
514
564
|
lines.push(`+${tasks.length - 1} more task${tasks.length === 2 ? "" : "s"}`);
|
|
@@ -566,6 +616,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
566
616
|
return renderDescription(
|
|
567
617
|
this.#discoveredAgents,
|
|
568
618
|
!planMode && isolationMode !== "none",
|
|
619
|
+
this.session.settings.get("task.isolation.apply"),
|
|
569
620
|
disabledAgents,
|
|
570
621
|
this.#isBatchEnabled(),
|
|
571
622
|
this.session.settings.get("async.enabled"),
|
|
@@ -612,6 +663,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
612
663
|
assignment: (params.task ?? "").trim(),
|
|
613
664
|
context: this.#isBatchEnabled() ? params.context?.trim() || undefined : undefined,
|
|
614
665
|
agent: params.agent,
|
|
666
|
+
model: params.model,
|
|
615
667
|
...(Object.hasOwn(params, "outputSchema") ? { outputSchema: params.outputSchema } : {}),
|
|
616
668
|
...(Object.hasOwn(params, "schemaMode") ? { schemaMode: params.schemaMode } : {}),
|
|
617
669
|
...("isolated" in params ? { isolation: { requested: params.isolated } } : {}),
|
|
@@ -650,27 +702,53 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
650
702
|
const spawnItems = resolveSpawnItems(params);
|
|
651
703
|
const normalizedSpawnParams = spawnItems.map(item => spawnParamsFor(params, item, defaultAgent));
|
|
652
704
|
const resolvedAgents = normalizedSpawnParams.map(spawn => spawn.agent ?? defaultAgent);
|
|
705
|
+
// Resolve every item before choosing an execution path. No executor or
|
|
706
|
+
// job manager may observe a batch unless every effective policy is valid.
|
|
707
|
+
const preflights = await Promise.all(
|
|
708
|
+
normalizedSpawnParams.map(async spawn => {
|
|
709
|
+
try {
|
|
710
|
+
return { policy: await this.#resolveSpawnPreflight(spawn) };
|
|
711
|
+
} catch (error) {
|
|
712
|
+
return { error: error instanceof StructuredSubagentError ? error.message : String(error) };
|
|
713
|
+
}
|
|
714
|
+
}),
|
|
715
|
+
);
|
|
716
|
+
const preflightFailures = preflights
|
|
717
|
+
.map((preflight, index) => ("error" in preflight ? { index, error: preflight.error } : undefined))
|
|
718
|
+
.filter((failure): failure is { index: number; error: string } => failure !== undefined);
|
|
719
|
+
if (preflightFailures.length > 0) {
|
|
720
|
+
if (!batchEnabled) {
|
|
721
|
+
return createTaskModeError(`Task execution failed: ${preflightFailures[0]!.error}`);
|
|
722
|
+
}
|
|
723
|
+
return createTaskModeError(
|
|
724
|
+
preflightFailures
|
|
725
|
+
.map(({ index, error }) => {
|
|
726
|
+
const item = spawnItems[index]!;
|
|
727
|
+
return `Task ${item.name?.trim() || `#${index + 1}`} failed preflight: ${error}`;
|
|
728
|
+
})
|
|
729
|
+
.join("\n"),
|
|
730
|
+
);
|
|
731
|
+
}
|
|
732
|
+
const policies = preflights.map(preflight => preflight.policy!);
|
|
733
|
+
const itemBlocking = policies.map(policy => policy.effectiveAgent.blocking === true);
|
|
734
|
+
|
|
653
735
|
// Execution mode is per item: an item whose agent type declares
|
|
654
736
|
// `blocking: true` runs inline on this turn (the parent waits on its
|
|
655
737
|
// result); every other item becomes a background job when async
|
|
656
738
|
// execution is available.
|
|
657
|
-
const provisionalBlocking = resolvedAgents.map(
|
|
658
|
-
name => this.#discoveredAgents.find(agent => agent.name === name)?.blocking === true,
|
|
659
|
-
);
|
|
660
739
|
const asyncEnabled = this.session.settings.get("async.enabled");
|
|
661
740
|
const manager = asyncEnabled ? this.session.asyncJobManager : undefined;
|
|
662
|
-
const
|
|
741
|
+
const asyncItems = manager ? spawnItems.filter((_, index) => !itemBlocking[index]) : [];
|
|
663
742
|
const depthCapacity = canSpawnAtDepth(
|
|
664
743
|
this.session.settings.get("task.maxRecursionDepth") ?? 2,
|
|
665
744
|
this.session.taskDepth ?? 0,
|
|
666
745
|
);
|
|
667
746
|
const ircEnabled = isIrcEnabled(this.session.settings, this.session.taskDepth ?? 0);
|
|
668
747
|
|
|
669
|
-
if (!manager ||
|
|
748
|
+
if (!manager || asyncItems.length === 0) {
|
|
670
749
|
// Sync fallback: async execution disabled, orphaned host that never
|
|
671
750
|
// wired a job manager, or every item's agent type declares
|
|
672
|
-
// `blocking: true`.
|
|
673
|
-
// preflight before reserving an id in these inline paths.
|
|
751
|
+
// `blocking: true`.
|
|
674
752
|
if (asyncEnabled && !this.session.asyncJobManager) {
|
|
675
753
|
logger.warn("task: no AsyncJobManager registered; falling back to sync execution");
|
|
676
754
|
}
|
|
@@ -678,7 +756,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
678
756
|
? undefined
|
|
679
757
|
: composeSpawnAdvisory({
|
|
680
758
|
agents: resolvedAgents,
|
|
681
|
-
items:
|
|
759
|
+
items: asyncItems,
|
|
682
760
|
depthCapacity,
|
|
683
761
|
ircEnabled,
|
|
684
762
|
willRunAsync: false,
|
|
@@ -704,43 +782,13 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
704
782
|
return { ...result, content };
|
|
705
783
|
}
|
|
706
784
|
|
|
707
|
-
// Async jobs are otherwise registered before their body can reach
|
|
708
|
-
// `runStructuredSubagent`. Resolve the shared policy first so policy
|
|
709
|
-
// failures remain synchronous and cannot leave a queued invalid job.
|
|
710
|
-
const preflights = await Promise.all(
|
|
711
|
-
normalizedSpawnParams.map(async spawn => {
|
|
712
|
-
try {
|
|
713
|
-
return { policy: await this.#resolveSpawnPreflight(spawn) };
|
|
714
|
-
} catch (error) {
|
|
715
|
-
return { error: error instanceof StructuredSubagentError ? error.message : String(error) };
|
|
716
|
-
}
|
|
717
|
-
}),
|
|
718
|
-
);
|
|
719
|
-
const preflightFailures = preflights
|
|
720
|
-
.map((preflight, index) => ("error" in preflight ? { index, error: preflight.error } : undefined))
|
|
721
|
-
.filter((failure): failure is { index: number; error: string } => failure !== undefined);
|
|
722
|
-
const renderPreflightFailures = () =>
|
|
723
|
-
preflightFailures
|
|
724
|
-
.map(({ index, error }) => {
|
|
725
|
-
const item = spawnItems[index]!;
|
|
726
|
-
return `Task ${item.name?.trim() || `#${index + 1}`} failed preflight: ${error}`;
|
|
727
|
-
})
|
|
728
|
-
.join("\n");
|
|
729
|
-
if (preflightFailures.length === spawnItems.length) {
|
|
730
|
-
return createTaskModeError(renderPreflightFailures());
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
const validIndices = preflights.flatMap((preflight, index) => (preflight.policy ? [index] : []));
|
|
734
|
-
const validSpawns = validIndices.map(index => ({ item: spawnItems[index]!, index }));
|
|
735
|
-
const itemBlocking = preflights.map(preflight => preflight.policy?.effectiveAgent.blocking === true);
|
|
736
|
-
const asyncItems = validIndices.filter(index => !itemBlocking[index]).map(index => spawnItems[index]!);
|
|
737
785
|
// Coordination only makes sense for spawns that keep running after this
|
|
738
786
|
// call returns (the async subset). Blocking items have already completed
|
|
739
787
|
// by then, so a "coordinate while they run" hint would misfire.
|
|
740
788
|
const advisory = this.session.suppressSpawnAdvisory
|
|
741
789
|
? undefined
|
|
742
790
|
: composeSpawnAdvisory({
|
|
743
|
-
agents:
|
|
791
|
+
agents: resolvedAgents,
|
|
744
792
|
items: asyncItems,
|
|
745
793
|
depthCapacity,
|
|
746
794
|
ircEnabled,
|
|
@@ -762,24 +810,15 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
762
810
|
if (!appended) content.push({ type: "text", text: advisory });
|
|
763
811
|
return { ...result, content };
|
|
764
812
|
};
|
|
765
|
-
const withPreflightFailures = (result: AgentToolResult<TaskToolDetails>): AgentToolResult<TaskToolDetails> => {
|
|
766
|
-
if (preflightFailures.length === 0) return result;
|
|
767
|
-
const failures = renderPreflightFailures();
|
|
768
|
-
let prepended = false;
|
|
769
|
-
const content = result.content.map(part => {
|
|
770
|
-
if (!prepended && part.type === "text" && typeof part.text === "string") {
|
|
771
|
-
prepended = true;
|
|
772
|
-
return { ...part, text: `${failures}\n\n${part.text}` };
|
|
773
|
-
}
|
|
774
|
-
return part;
|
|
775
|
-
});
|
|
776
|
-
if (!prepended) content.unshift({ type: "text", text: failures });
|
|
777
|
-
return { ...result, content };
|
|
778
|
-
};
|
|
779
813
|
if (asyncItems.length === 0) {
|
|
780
|
-
return
|
|
781
|
-
|
|
782
|
-
|
|
814
|
+
return withAdvisory(
|
|
815
|
+
await this.#executeSyncFanout(
|
|
816
|
+
toolCallId,
|
|
817
|
+
params,
|
|
818
|
+
spawnItems.map((item, index) => ({ item, index })),
|
|
819
|
+
defaultAgent,
|
|
820
|
+
signal,
|
|
821
|
+
onUpdate,
|
|
783
822
|
),
|
|
784
823
|
);
|
|
785
824
|
}
|
|
@@ -799,12 +838,9 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
799
838
|
blocking: boolean;
|
|
800
839
|
progress: AgentProgress;
|
|
801
840
|
}> = [];
|
|
802
|
-
for (const index of
|
|
803
|
-
const item = spawnItems[index]!;
|
|
841
|
+
for (const [index, item] of spawnItems.entries()) {
|
|
804
842
|
const agentType = resolvedAgents[index]!;
|
|
805
|
-
const
|
|
806
|
-
const policy = preflight.policy;
|
|
807
|
-
if (!policy) continue;
|
|
843
|
+
const policy = policies[index]!;
|
|
808
844
|
const agentSource = policy.agent.source;
|
|
809
845
|
const agentId = await outputManager.allocate(item.name?.trim() || generateTaskName());
|
|
810
846
|
const assignment = (item.task ?? "").trim();
|
|
@@ -841,7 +877,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
841
877
|
// failed. Blocking spawns run inline below and land in `results` before
|
|
842
878
|
// the call returns, so post-return job updates never drop them.
|
|
843
879
|
let settledCount = 0;
|
|
844
|
-
let failedCount =
|
|
880
|
+
let failedCount = 0;
|
|
845
881
|
let primaryJobId = asyncSpawns[0].agentId;
|
|
846
882
|
const syncResults: SingleResult[] = [];
|
|
847
883
|
let syncUsage: Usage | undefined;
|
|
@@ -891,7 +927,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
891
927
|
}
|
|
892
928
|
|
|
893
929
|
if (started.length === 0 && syncSpawns.length === 0) {
|
|
894
|
-
return
|
|
930
|
+
return {
|
|
895
931
|
content: [
|
|
896
932
|
{
|
|
897
933
|
type: "text",
|
|
@@ -899,7 +935,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
899
935
|
},
|
|
900
936
|
],
|
|
901
937
|
details: { projectAgentsDir: null, results: [], totalDurationMs: 0 },
|
|
902
|
-
}
|
|
938
|
+
};
|
|
903
939
|
}
|
|
904
940
|
|
|
905
941
|
const scheduleFailureSummary =
|
|
@@ -924,34 +960,30 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
924
960
|
content: [{ type: "text", text: `Spawned agent \`${agentId}\`...` }],
|
|
925
961
|
details: buildAsyncDetails(),
|
|
926
962
|
});
|
|
927
|
-
return
|
|
928
|
-
withAdvisory({
|
|
929
|
-
content: [
|
|
930
|
-
{
|
|
931
|
-
type: "text",
|
|
932
|
-
text: `Spawned agent \`${agentId}\` (job \`${jobId}\`). Its result auto-delivers on yield unless a settled \`hub jobs\`/\`wait\` snapshot consumes it first. ${coordinationHint}`,
|
|
933
|
-
},
|
|
934
|
-
],
|
|
935
|
-
details: buildAsyncDetails(),
|
|
936
|
-
}),
|
|
937
|
-
);
|
|
938
|
-
}
|
|
939
|
-
const startedListing = started.map(({ agentId, jobId }) => `- \`${agentId}\` (job \`${jobId}\`)`).join("\n");
|
|
940
|
-
onUpdate?.({
|
|
941
|
-
content: [{ type: "text", text: `Spawned ${started.length} agents...` }],
|
|
942
|
-
details: buildAsyncDetails(),
|
|
943
|
-
});
|
|
944
|
-
return withPreflightFailures(
|
|
945
|
-
withAdvisory({
|
|
963
|
+
return withAdvisory({
|
|
946
964
|
content: [
|
|
947
965
|
{
|
|
948
966
|
type: "text",
|
|
949
|
-
text: `Spawned
|
|
967
|
+
text: `Spawned agent \`${agentId}\` (job \`${jobId}\`). Its result auto-delivers on yield unless a settled \`hub jobs\`/\`wait\` snapshot consumes it first. ${coordinationHint}`,
|
|
950
968
|
},
|
|
951
969
|
],
|
|
952
970
|
details: buildAsyncDetails(),
|
|
953
|
-
})
|
|
954
|
-
|
|
971
|
+
});
|
|
972
|
+
}
|
|
973
|
+
const startedListing = started.map(({ agentId, jobId }) => `- \`${agentId}\` (job \`${jobId}\`)`).join("\n");
|
|
974
|
+
onUpdate?.({
|
|
975
|
+
content: [{ type: "text", text: `Spawned ${started.length} agents...` }],
|
|
976
|
+
details: buildAsyncDetails(),
|
|
977
|
+
});
|
|
978
|
+
return withAdvisory({
|
|
979
|
+
content: [
|
|
980
|
+
{
|
|
981
|
+
type: "text",
|
|
982
|
+
text: `Spawned ${started.length} background agents using ${agentLabel}.${scheduleFailureSummary} Each result auto-delivers on yield unless a settled \`hub jobs\`/\`wait\` snapshot consumes it first.\n${startedListing}\n${coordinationHint}`,
|
|
983
|
+
},
|
|
984
|
+
],
|
|
985
|
+
details: buildAsyncDetails(),
|
|
986
|
+
});
|
|
955
987
|
}
|
|
956
988
|
|
|
957
989
|
// Mixed call: the async jobs above already run detached; the blocking
|
|
@@ -1016,12 +1048,10 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1016
1048
|
const text = [merged.contentParts.join("\n\n"), spawnedSummary]
|
|
1017
1049
|
.filter(section => section.trim().length > 0)
|
|
1018
1050
|
.join("\n\n");
|
|
1019
|
-
return
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
}),
|
|
1024
|
-
);
|
|
1051
|
+
return withAdvisory({
|
|
1052
|
+
content: [{ type: "text", text: text.length > 0 ? text : "No results." }],
|
|
1053
|
+
details: buildAsyncDetails(),
|
|
1054
|
+
});
|
|
1025
1055
|
}
|
|
1026
1056
|
|
|
1027
1057
|
/**
|
|
@@ -1043,14 +1073,17 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1043
1073
|
}): string {
|
|
1044
1074
|
const { manager, toolCallId, spawnParams, agentId, progress, ircEnabled, buildDetails, onUpdate, onSettled } =
|
|
1045
1075
|
options;
|
|
1046
|
-
const buildFollowUpHint = (aborted: boolean): string => {
|
|
1076
|
+
const buildFollowUpHint = async (aborted: boolean): Promise<string> => {
|
|
1047
1077
|
if (aborted) {
|
|
1048
|
-
const
|
|
1049
|
-
|
|
1078
|
+
const ref = AgentRegistry.global().get(agentId);
|
|
1079
|
+
const transcript = (await hasResolvableTranscript(agentId))
|
|
1080
|
+
? `transcript at history://${agentId}`
|
|
1081
|
+
: "transcript unavailable";
|
|
1082
|
+
if (ref?.status === "idle" || ref?.status === "parked") {
|
|
1050
1083
|
const followUp = ircEnabled ? "message it via `hub` to resume; " : "";
|
|
1051
|
-
return `\n\n${agentId} was stopped but is still resumable — ${followUp}transcript
|
|
1084
|
+
return `\n\n${agentId} was stopped but is still resumable — ${followUp}${transcript}`;
|
|
1052
1085
|
}
|
|
1053
|
-
return `\n\n${agentId} was aborted — transcript
|
|
1086
|
+
return `\n\n${agentId} was aborted — ${transcript}`;
|
|
1054
1087
|
}
|
|
1055
1088
|
const followUp = ircEnabled ? "message it via `hub` to follow up; " : "";
|
|
1056
1089
|
return `\n\n${agentId} is now idle — ${followUp}transcript at history://${agentId}`;
|
|
@@ -1105,6 +1138,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1105
1138
|
// and running counters without reverting the "running"
|
|
1106
1139
|
// status back to the subagent's initial "pending" snapshot.
|
|
1107
1140
|
progress.resolvedModel = nextProgress.resolvedModel;
|
|
1141
|
+
progress.resolvedModelIsFallback = nextProgress.resolvedModelIsFallback;
|
|
1108
1142
|
progress.tokens = nextProgress.tokens;
|
|
1109
1143
|
progress.requests = nextProgress.requests;
|
|
1110
1144
|
progress.contextTokens = nextProgress.contextTokens;
|
|
@@ -1149,15 +1183,17 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1149
1183
|
progress.retryState = undefined;
|
|
1150
1184
|
if (singleResult?.resolvedModel) {
|
|
1151
1185
|
progress.resolvedModel = singleResult.resolvedModel;
|
|
1186
|
+
progress.resolvedModelIsFallback = singleResult.resolvedModelIsFallback;
|
|
1152
1187
|
} else {
|
|
1153
1188
|
delete progress.resolvedModel;
|
|
1189
|
+
delete progress.resolvedModelIsFallback;
|
|
1154
1190
|
}
|
|
1155
1191
|
onSettled?.(resultFailed);
|
|
1156
1192
|
const statusText = resultFailed
|
|
1157
1193
|
? `Background task ${agentId} failed.`
|
|
1158
1194
|
: `Background task ${agentId} complete.`;
|
|
1159
1195
|
await reportProgress(statusText, buildDetails() as unknown as Record<string, unknown>);
|
|
1160
|
-
const deliveryText = `${finalText}${buildFollowUpHint(singleResult?.aborted === true)}`;
|
|
1196
|
+
const deliveryText = `${finalText}${await buildFollowUpHint(singleResult?.aborted === true)}`;
|
|
1161
1197
|
if (resultFailed) {
|
|
1162
1198
|
// Mark the job itself failed; the failed agent stays interrogable.
|
|
1163
1199
|
throw new TaskJobError(deliveryText);
|
|
@@ -1173,7 +1209,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1173
1209
|
const statusText = `Background task ${agentId} failed.`;
|
|
1174
1210
|
await reportProgress(statusText, buildDetails() as unknown as Record<string, unknown>);
|
|
1175
1211
|
const message = error instanceof Error ? error.message : String(error);
|
|
1176
|
-
const hint = AgentRegistry.global().get(agentId) ? buildFollowUpHint(false) : "";
|
|
1212
|
+
const hint = AgentRegistry.global().get(agentId) ? await buildFollowUpHint(false) : "";
|
|
1177
1213
|
throw new TaskJobError(`${message}${hint}`);
|
|
1178
1214
|
} finally {
|
|
1179
1215
|
releasePermit();
|
|
@@ -1383,6 +1419,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1383
1419
|
assignment,
|
|
1384
1420
|
context,
|
|
1385
1421
|
agent: params.agent,
|
|
1422
|
+
model: params.model,
|
|
1386
1423
|
...(Object.hasOwn(params, "outputSchema") ? { outputSchema: params.outputSchema } : {}),
|
|
1387
1424
|
...(Object.hasOwn(params, "schemaMode") ? { schemaMode: params.schemaMode } : {}),
|
|
1388
1425
|
identity: { id: preAllocatedId, label: params.name },
|
|
@@ -18,10 +18,11 @@ import { ADVISOR_TRANSCRIPT_STEM } from "../advisor/transcript-recorder";
|
|
|
18
18
|
*
|
|
19
19
|
* The first allocation of a given name keeps the name as-is; subsequent
|
|
20
20
|
* allocations of the same name get a `-2`, `-3`, … suffix. On resume, scans
|
|
21
|
-
* existing output files so
|
|
21
|
+
* existing output and child-session files so prior state is never overwritten.
|
|
22
22
|
*/
|
|
23
23
|
export class AgentOutputManager {
|
|
24
24
|
#initialized = false;
|
|
25
|
+
#initializing: Promise<void> | undefined;
|
|
25
26
|
/** Final ids already handed out, relative to this manager's scope. */
|
|
26
27
|
readonly #taken = new Set<string>();
|
|
27
28
|
readonly #getArtifactsDir: () => string | null;
|
|
@@ -42,8 +43,12 @@ export class AgentOutputManager {
|
|
|
42
43
|
*/
|
|
43
44
|
async #ensureInitialized(): Promise<void> {
|
|
44
45
|
if (this.#initialized) return;
|
|
46
|
+
this.#initializing ??= this.#seedFromDisk();
|
|
47
|
+
await this.#initializing;
|
|
45
48
|
this.#initialized = true;
|
|
49
|
+
}
|
|
46
50
|
|
|
51
|
+
async #seedFromDisk(): Promise<void> {
|
|
47
52
|
const dir = this.#getArtifactsDir();
|
|
48
53
|
if (!dir) return;
|
|
49
54
|
|
|
@@ -56,8 +61,9 @@ export class AgentOutputManager {
|
|
|
56
61
|
|
|
57
62
|
const prefix = this.#parentPrefix ? `${this.#parentPrefix}.` : "";
|
|
58
63
|
for (const file of files) {
|
|
59
|
-
|
|
60
|
-
|
|
64
|
+
const extension = file.endsWith(".jsonl") ? ".jsonl" : file.endsWith(".md") ? ".md" : undefined;
|
|
65
|
+
if (!extension) continue;
|
|
66
|
+
let rest = file.slice(0, -extension.length);
|
|
61
67
|
if (prefix) {
|
|
62
68
|
if (!rest.startsWith(prefix)) continue;
|
|
63
69
|
rest = rest.slice(prefix.length);
|
|
@@ -80,6 +86,22 @@ export class AgentOutputManager {
|
|
|
80
86
|
return this.#parentPrefix ? `${this.#parentPrefix}.${candidate}` : candidate;
|
|
81
87
|
}
|
|
82
88
|
|
|
89
|
+
/** Reserve final IDs discovered outside the output directory scan. */
|
|
90
|
+
async reserve(ids: Iterable<string>): Promise<void> {
|
|
91
|
+
await this.#ensureInitialized();
|
|
92
|
+
const prefix = this.#parentPrefix ? `${this.#parentPrefix}.` : "";
|
|
93
|
+
for (const id of ids) {
|
|
94
|
+
let rest = id;
|
|
95
|
+
if (prefix) {
|
|
96
|
+
if (!rest.startsWith(prefix)) continue;
|
|
97
|
+
rest = rest.slice(prefix.length);
|
|
98
|
+
}
|
|
99
|
+
const dot = rest.indexOf(".");
|
|
100
|
+
const segment = dot === -1 ? rest : rest.slice(0, dot);
|
|
101
|
+
if (segment) this.#taken.add(segment);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
83
105
|
/**
|
|
84
106
|
* Allocate a unique ID.
|
|
85
107
|
*
|
|
@@ -71,7 +71,7 @@ export function createPersistedSubagentReviverFactory(
|
|
|
71
71
|
taskDepth++;
|
|
72
72
|
parentId = registry.get(parentId)?.parentId;
|
|
73
73
|
}
|
|
74
|
-
return async
|
|
74
|
+
return async expectedRef => {
|
|
75
75
|
// Re-open fresh on every revive: park closes the writer, so this takes
|
|
76
76
|
// the single-writer lock cleanly and restores the full message history.
|
|
77
77
|
const reopened = await SessionManager.open(sessionFile, undefined, undefined, {
|
|
@@ -97,6 +97,7 @@ export function createPersistedSubagentReviverFactory(
|
|
|
97
97
|
agentDisplayName: ref.displayName,
|
|
98
98
|
parentTaskPrefix: ref.id,
|
|
99
99
|
parentAgentId: ref.parentId,
|
|
100
|
+
expectedAgentRef: expectedRef,
|
|
100
101
|
taskDepth,
|
|
101
102
|
toolNames: init.tools,
|
|
102
103
|
outputSchema: init.outputSchema,
|
|
@@ -131,8 +132,8 @@ export function createPersistedSubagentReviverFactory(
|
|
|
131
132
|
// Without it the idle-TTL timer never clears on a turn and the lifecycle
|
|
132
133
|
// could park the agent mid-run.
|
|
133
134
|
session.subscribe(event => {
|
|
134
|
-
if (event.type === "agent_start") registry.setStatus(ref.id, "running");
|
|
135
|
-
else if (event.type === "agent_end") registry.setStatus(ref.id, "idle");
|
|
135
|
+
if (event.type === "agent_start") registry.setStatus(ref.id, "running", session);
|
|
136
|
+
else if (event.type === "agent_end") registry.setStatus(ref.id, "idle", session);
|
|
136
137
|
});
|
|
137
138
|
return session;
|
|
138
139
|
};
|
|
@@ -300,7 +300,9 @@ export async function resolveEffectiveSubagentPolicy(
|
|
|
300
300
|
planMode,
|
|
301
301
|
isIsolated,
|
|
302
302
|
mergeMode: request.isolation?.merge ?? request.session.settings.get("task.isolation.merge"),
|
|
303
|
-
applyChanges:
|
|
303
|
+
applyChanges:
|
|
304
|
+
request.isolation?.apply ??
|
|
305
|
+
(request.invocationKind === "task" ? request.session.settings.get("task.isolation.apply") : true),
|
|
304
306
|
enableLsp:
|
|
305
307
|
!planMode &&
|
|
306
308
|
(request.enableLsp ?? ((request.session.enableLsp ?? true) && request.session.settings.get("task.enableLsp"))),
|
|
@@ -371,6 +373,7 @@ function buildExecutorOptions(
|
|
|
371
373
|
const enableMCP = !policy.planMode && (session.enableMCP ?? true);
|
|
372
374
|
return {
|
|
373
375
|
cwd: session.cwd,
|
|
376
|
+
additionalDirectories: session.additionalDirectories,
|
|
374
377
|
agent: policy.effectiveAgent,
|
|
375
378
|
task: renderSubagentPrompt(request.assignment),
|
|
376
379
|
assignment: request.assignment.trim(),
|