@cjhyy/code-shell-core 0.5.0-rc.1 → 0.5.0-rc.2
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/dist/agent/agent-definition-registry.d.ts +18 -3
- package/dist/agent/agent-definition-registry.js +47 -18
- package/dist/agent/agent-definition.d.ts +17 -0
- package/dist/agent/agent-definition.js +22 -1
- package/dist/arena/arena.js +11 -12
- package/dist/arena/context/context-tools.js +2 -4
- package/dist/arena/context/within-root.d.ts +7 -0
- package/dist/arena/context/within-root.js +15 -0
- package/dist/arena/iterate/phases/argue.js +8 -2
- package/dist/arena/iterate/phases/revise.js +1 -1
- package/dist/arena/iterate/phases/tournament.js +3 -3
- package/dist/arena/phases/adjudication.js +1 -4
- package/dist/arena/phases/build-consensus.js +1 -4
- package/dist/arena/phases/cross-review.js +2 -8
- package/dist/arena/phases/debate-rounds.js +1 -4
- package/dist/arena/phases/participant-research.js +1 -4
- package/dist/arena/phases/planning-detail-expansion.js +8 -10
- package/dist/arena/planner.js +0 -1
- package/dist/arena/providers/docs.d.ts +1 -0
- package/dist/arena/providers/docs.js +5 -2
- package/dist/arena/providers/repo.d.ts +1 -0
- package/dist/arena/providers/repo.js +5 -2
- package/dist/arena/strategies/utils.js +36 -3
- package/dist/arena/transitions.d.ts +7 -2
- package/dist/arena/transitions.js +9 -4
- package/dist/arena/types.d.ts +8 -2
- package/dist/automation/cron-expr.d.ts +31 -0
- package/dist/automation/cron-expr.js +151 -0
- package/dist/automation/index.d.ts +41 -0
- package/dist/automation/index.js +39 -0
- package/dist/automation/runner.d.ts +67 -0
- package/dist/automation/runner.js +56 -0
- package/dist/automation/scheduler.d.ts +156 -0
- package/dist/automation/scheduler.js +546 -0
- package/dist/automation/store.d.ts +39 -0
- package/dist/automation/store.js +119 -0
- package/dist/automation/write-policy.d.ts +34 -0
- package/dist/automation/write-policy.js +113 -0
- package/dist/automation/write-run.d.ts +51 -0
- package/dist/automation/write-run.js +38 -0
- package/dist/capability-control/index.d.ts +5 -0
- package/dist/capability-control/index.js +3 -0
- package/dist/capability-control/overlay.d.ts +49 -0
- package/dist/capability-control/overlay.js +89 -0
- package/dist/capability-control/project.d.ts +65 -0
- package/dist/capability-control/project.js +147 -0
- package/dist/capability-control/service.d.ts +70 -0
- package/dist/capability-control/service.js +147 -0
- package/dist/capability-control/types.d.ts +62 -0
- package/dist/capability-control/types.js +17 -0
- package/dist/cli/agent-server-stdio.d.ts +12 -1
- package/dist/cli/agent-server-stdio.js +114 -19
- package/dist/cli/agent-server-tcp.d.ts +20 -0
- package/dist/cli/agent-server-tcp.js +120 -0
- package/dist/cli/graceful-shutdown.d.ts +27 -0
- package/dist/cli/graceful-shutdown.js +30 -0
- package/dist/context/compaction.js +23 -15
- package/dist/context/manager.d.ts +0 -20
- package/dist/context/manager.js +0 -37
- package/dist/cron/cron-runtime.d.ts +2 -0
- package/dist/cron/cron-runtime.js +2 -0
- package/dist/cron/cron-store.d.ts +2 -0
- package/dist/cron/cron-store.js +2 -0
- package/dist/cron/scheduler.d.ts +5 -32
- package/dist/cron/scheduler.js +5 -113
- package/dist/data/openrouter-sync.js +1 -1
- package/dist/engine/engine.d.ts +208 -14
- package/dist/engine/engine.js +754 -169
- package/dist/engine/goal.d.ts +42 -0
- package/dist/engine/goal.js +52 -0
- package/dist/engine/image-compression.d.ts +81 -0
- package/dist/engine/image-compression.js +186 -0
- package/dist/engine/image-policy.d.ts +152 -0
- package/dist/engine/image-policy.js +165 -0
- package/dist/engine/reactive-threshold.d.ts +13 -0
- package/dist/engine/reactive-threshold.js +17 -0
- package/dist/engine/runtime.js +7 -0
- package/dist/engine/session-title.d.ts +13 -0
- package/dist/engine/session-title.js +34 -0
- package/dist/engine/turn-loop.d.ts +29 -8
- package/dist/engine/turn-loop.js +498 -316
- package/dist/git/parse-log.d.ts +13 -0
- package/dist/git/parse-log.js +21 -0
- package/dist/git/utils.d.ts +8 -6
- package/dist/git/utils.js +57 -26
- package/dist/git/worktree.js +33 -18
- package/dist/hooks/events.d.ts +23 -1
- package/dist/hooks/goal-stop-hook.d.ts +30 -0
- package/dist/hooks/goal-stop-hook.js +102 -0
- package/dist/hooks/hook-output.d.ts +33 -0
- package/dist/hooks/hook-output.js +79 -0
- package/dist/hooks/registry.d.ts +7 -0
- package/dist/hooks/registry.js +38 -1
- package/dist/hooks/shell-runner.d.ts +2 -0
- package/dist/hooks/shell-runner.js +43 -2
- package/dist/index.d.ts +31 -6
- package/dist/index.js +30 -3
- package/dist/llm/capabilities/reasoning-control.d.ts +24 -0
- package/dist/llm/capabilities/reasoning-control.js +38 -0
- package/dist/llm/capabilities/rules.js +33 -3
- package/dist/llm/capabilities/types.d.ts +22 -2
- package/dist/llm/clamp-max-tokens.d.ts +11 -0
- package/dist/llm/clamp-max-tokens.js +17 -0
- package/dist/llm/client-base.d.ts +41 -4
- package/dist/llm/client-base.js +67 -13
- package/dist/llm/client-factory.d.ts +3 -3
- package/dist/llm/client-factory.js +2 -2
- package/dist/llm/model-pool.d.ts +15 -11
- package/dist/llm/model-pool.js +23 -20
- package/dist/llm/provider-catalog.d.ts +2 -2
- package/dist/llm/providers/anthropic.d.ts +32 -2
- package/dist/llm/providers/anthropic.js +147 -8
- package/dist/llm/providers/openai.d.ts +11 -2
- package/dist/llm/providers/openai.js +253 -52
- package/dist/llm/reasoning-setting.d.ts +51 -0
- package/dist/llm/reasoning-setting.js +32 -0
- package/dist/llm/stop-reason.d.ts +13 -0
- package/dist/llm/stop-reason.js +21 -0
- package/dist/llm/strip-vision.d.ts +32 -0
- package/dist/llm/strip-vision.js +94 -0
- package/dist/llm/types.d.ts +5 -6
- package/dist/logging/logger.js +18 -4
- package/dist/logging/sanitize-messages.d.ts +10 -0
- package/dist/logging/sanitize-messages.js +90 -0
- package/dist/lsp/client.js +28 -8
- package/dist/lsp/manager.js +2 -1
- package/dist/lsp/root-path.d.ts +9 -0
- package/dist/lsp/root-path.js +12 -0
- package/dist/onboarding.js +18 -11
- package/dist/plugins/gitOps.d.ts +34 -0
- package/dist/plugins/gitOps.js +59 -3
- package/dist/plugins/installer/codex/convertAgents.d.ts +7 -0
- package/dist/plugins/installer/codex/convertAgents.js +55 -0
- package/dist/plugins/installer/codex/convertMcp.d.ts +8 -0
- package/dist/plugins/installer/codex/convertMcp.js +45 -0
- package/dist/plugins/installer/codex/convertSkills.d.ts +12 -0
- package/dist/plugins/installer/codex/convertSkills.js +33 -0
- package/dist/plugins/installer/detectFormat.d.ts +2 -0
- package/dist/plugins/installer/detectFormat.js +6 -0
- package/dist/plugins/installer/install.d.ts +7 -0
- package/dist/plugins/installer/install.js +99 -0
- package/dist/plugins/installer/installFromSource.d.ts +13 -0
- package/dist/plugins/installer/installFromSource.js +45 -0
- package/dist/plugins/installer/list.d.ts +9 -0
- package/dist/plugins/installer/list.js +30 -0
- package/dist/plugins/installer/loadPluginAgents.d.ts +3 -0
- package/dist/plugins/installer/loadPluginAgents.js +23 -0
- package/dist/plugins/installer/loadPluginMcp.d.ts +7 -0
- package/dist/plugins/installer/loadPluginMcp.js +74 -0
- package/dist/plugins/installer/parseSource.d.ts +20 -0
- package/dist/plugins/installer/parseSource.js +74 -0
- package/dist/plugins/installer/paths.d.ts +5 -0
- package/dist/plugins/installer/paths.js +25 -0
- package/dist/plugins/installer/types.d.ts +49 -0
- package/dist/plugins/installer/types.js +27 -0
- package/dist/plugins/installer/uninstall.d.ts +2 -0
- package/dist/plugins/installer/uninstall.js +14 -0
- package/dist/plugins/installer/update.d.ts +14 -0
- package/dist/plugins/installer/update.js +65 -0
- package/dist/plugins/loadPluginHooks.d.ts +8 -1
- package/dist/plugins/loadPluginHooks.js +11 -1
- package/dist/plugins/marketplaceManager.d.ts +8 -1
- package/dist/plugins/marketplaceManager.js +38 -4
- package/dist/plugins/pluginCommandHook.js +34 -0
- package/dist/plugins/pluginInstaller.d.ts +21 -0
- package/dist/plugins/pluginInstaller.js +128 -29
- package/dist/plugins/schemas.js +40 -6
- package/dist/plugins/types.d.ts +4 -0
- package/dist/preset/index.js +16 -0
- package/dist/prompt/composer.d.ts +4 -0
- package/dist/prompt/composer.js +28 -4
- package/dist/prompt/sections/orchestration.md +8 -0
- package/dist/protocol/chat-session-manager.d.ts +8 -1
- package/dist/protocol/chat-session-manager.js +10 -0
- package/dist/protocol/chat-session.d.ts +17 -0
- package/dist/protocol/chat-session.js +28 -0
- package/dist/protocol/redact.d.ts +50 -0
- package/dist/protocol/redact.js +71 -0
- package/dist/protocol/server.d.ts +41 -2
- package/dist/protocol/server.js +231 -33
- package/dist/protocol/tcp-transport.d.ts +45 -0
- package/dist/protocol/tcp-transport.js +74 -0
- package/dist/protocol/types.d.ts +23 -0
- package/dist/protocol/types.js +6 -0
- package/dist/remote/bridge.d.ts +11 -2
- package/dist/remote/bridge.js +61 -33
- package/dist/run/ArtifactTracker.js +5 -4
- package/dist/run/EngineRunner.d.ts +27 -1
- package/dist/run/EngineRunner.js +46 -8
- package/dist/run/FileRunStore.js +32 -8
- package/dist/run/RunLock.d.ts +28 -2
- package/dist/run/RunLock.js +49 -7
- package/dist/run/RunManager.js +51 -3
- package/dist/run/RunQueue.d.ts +1 -0
- package/dist/run/RunQueue.js +8 -2
- package/dist/run/factory.d.ts +7 -0
- package/dist/run/factory.js +1 -0
- package/dist/run/index.d.ts +2 -2
- package/dist/run/index.js +1 -1
- package/dist/run/redirect-target.d.ts +7 -0
- package/dist/run/redirect-target.js +13 -0
- package/dist/run/types.js +1 -1
- package/dist/services/browser-open.d.ts +13 -0
- package/dist/services/browser-open.js +16 -0
- package/dist/services/dream-consolidation.d.ts +57 -0
- package/dist/services/dream-consolidation.js +151 -0
- package/dist/services/memory-orchestrator.js +18 -0
- package/dist/services/notifier.d.ts +8 -0
- package/dist/services/notifier.js +39 -25
- package/dist/services/oauth.js +5 -7
- package/dist/services/session-memory-sort.d.ts +8 -0
- package/dist/services/session-memory-sort.js +9 -0
- package/dist/services/session-memory.js +8 -9
- package/dist/session/file-history.js +10 -3
- package/dist/session/session-manager.d.ts +23 -1
- package/dist/session/session-manager.js +73 -2
- package/dist/settings/disk-defaults.d.ts +35 -0
- package/dist/settings/disk-defaults.js +24 -0
- package/dist/settings/manager.d.ts +33 -0
- package/dist/settings/manager.js +94 -1
- package/dist/settings/personalization.d.ts +19 -0
- package/dist/settings/personalization.js +7 -0
- package/dist/settings/schema.d.ts +1142 -126
- package/dist/settings/schema.js +119 -10
- package/dist/tool-system/builtin/add-marketplace.d.ts +12 -0
- package/dist/tool-system/builtin/add-marketplace.js +76 -0
- package/dist/tool-system/builtin/agent-registry.d.ts +11 -0
- package/dist/tool-system/builtin/agent-registry.js +8 -4
- package/dist/tool-system/builtin/agent.d.ts +29 -10
- package/dist/tool-system/builtin/agent.js +108 -52
- package/dist/tool-system/builtin/apply-patch/index.js +22 -3
- package/dist/tool-system/builtin/arena.js +6 -7
- package/dist/tool-system/builtin/complete-goal.d.ts +25 -0
- package/dist/tool-system/builtin/complete-goal.js +45 -0
- package/dist/tool-system/builtin/cron.js +53 -9
- package/dist/tool-system/builtin/edit.d.ts +2 -1
- package/dist/tool-system/builtin/edit.js +6 -1
- package/dist/tool-system/builtin/generate-image.d.ts +25 -0
- package/dist/tool-system/builtin/generate-image.js +146 -0
- package/dist/tool-system/builtin/glob.js +7 -0
- package/dist/tool-system/builtin/grep.js +6 -0
- package/dist/tool-system/builtin/index.d.ts +18 -1
- package/dist/tool-system/builtin/index.js +62 -1
- package/dist/tool-system/builtin/notebook-edit.d.ts +2 -1
- package/dist/tool-system/builtin/notebook-edit.js +8 -1
- package/dist/tool-system/builtin/read.d.ts +2 -1
- package/dist/tool-system/builtin/read.js +9 -1
- package/dist/tool-system/builtin/sleep.js +9 -3
- package/dist/tool-system/builtin/task.js +4 -1
- package/dist/tool-system/builtin/update-automation-memory.d.ts +24 -0
- package/dist/tool-system/builtin/update-automation-memory.js +60 -0
- package/dist/tool-system/builtin/view-image.d.ts +18 -0
- package/dist/tool-system/builtin/view-image.js +96 -0
- package/dist/tool-system/builtin/web-search.d.ts +4 -1
- package/dist/tool-system/builtin/web-search.js +36 -3
- package/dist/tool-system/builtin/write.d.ts +2 -1
- package/dist/tool-system/builtin/write.js +7 -1
- package/dist/tool-system/context.d.ts +12 -0
- package/dist/tool-system/executor.d.ts +0 -1
- package/dist/tool-system/executor.js +34 -40
- package/dist/tool-system/investigation-guard.d.ts +4 -1
- package/dist/tool-system/investigation-guard.js +19 -3
- package/dist/tool-system/mcp-manager.d.ts +36 -12
- package/dist/tool-system/mcp-manager.js +173 -10
- package/dist/tool-system/path-policy.d.ts +94 -0
- package/dist/tool-system/path-policy.js +279 -0
- package/dist/tool-system/permission.js +43 -50
- package/dist/tool-system/plan-mode-allowlist.d.ts +21 -0
- package/dist/tool-system/plan-mode-allowlist.js +43 -0
- package/dist/tool-system/registry.js +12 -1
- package/dist/tool-system/validation.d.ts +10 -0
- package/dist/tool-system/validation.js +16 -3
- package/dist/types.d.ts +90 -15
- package/dist/utils/format.js +4 -2
- package/dist/utils/lockfile.js +9 -2
- package/dist/utils/theme.d.ts +6 -0
- package/dist/utils/theme.js +4 -2
- package/package.json +2 -1
package/dist/settings/schema.js
CHANGED
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
* Settings schema with Zod validation.
|
|
3
3
|
*/
|
|
4
4
|
import { z } from "zod";
|
|
5
|
+
import { ReasoningSettingSchema } from "../llm/reasoning-setting.js";
|
|
6
|
+
/**
|
|
7
|
+
* Tri-state project capability overlay. Lives in PROJECT settings only and
|
|
8
|
+
* layers over the global baseline (disabledSkills / disabledPlugins /
|
|
9
|
+
* mcpServers.<name>.enabled / disabledAgents). NOT a second denylist:
|
|
10
|
+
* - "on" → force enabled (even if globally disabled)
|
|
11
|
+
* - "off" → force disabled (even if globally enabled)
|
|
12
|
+
* - "inherit" → take the global baseline. We don't persist "inherit"; an
|
|
13
|
+
* absent key means inherit. See the project-scoped
|
|
14
|
+
* capabilities design (spec §4).
|
|
15
|
+
*/
|
|
16
|
+
export const CapabilityOverrideSchema = z.enum(["inherit", "on", "off"]);
|
|
17
|
+
export const CapabilityOverridesSchema = z
|
|
18
|
+
.object({
|
|
19
|
+
skills: z.record(CapabilityOverrideSchema).optional(),
|
|
20
|
+
plugins: z.record(CapabilityOverrideSchema).optional(),
|
|
21
|
+
agents: z.record(CapabilityOverrideSchema).optional(),
|
|
22
|
+
mcp: z.record(CapabilityOverrideSchema).optional(),
|
|
23
|
+
builtin: z.record(CapabilityOverrideSchema).optional(),
|
|
24
|
+
})
|
|
25
|
+
.optional();
|
|
5
26
|
export const SettingsSchema = z
|
|
6
27
|
.object({
|
|
7
28
|
agent: z
|
|
@@ -12,6 +33,14 @@ export const SettingsSchema = z
|
|
|
12
33
|
disabledBuiltinTools: z.array(z.string()).default([]),
|
|
13
34
|
customSystemPrompt: z.string().optional(),
|
|
14
35
|
appendSystemPrompt: z.string().optional(),
|
|
36
|
+
responseLanguage: z.string().optional(),
|
|
37
|
+
userProfile: z.string().optional(),
|
|
38
|
+
instructions: z
|
|
39
|
+
.object({
|
|
40
|
+
compatClaude: z.boolean().default(true),
|
|
41
|
+
compatCodex: z.boolean().default(true),
|
|
42
|
+
})
|
|
43
|
+
.optional(),
|
|
15
44
|
})
|
|
16
45
|
.default({}),
|
|
17
46
|
/**
|
|
@@ -22,6 +51,15 @@ export const SettingsSchema = z
|
|
|
22
51
|
* both — appendOnboardingResult does this in one shot.
|
|
23
52
|
*/
|
|
24
53
|
activeKey: z.string().optional(),
|
|
54
|
+
/**
|
|
55
|
+
* Auxiliary-task model — points at a models[].key. Background, non-user-
|
|
56
|
+
* facing LLM calls (memory extraction, the auto-dream loop) use this model
|
|
57
|
+
* instead of the active one, so per-turn book-keeping doesn't burn the
|
|
58
|
+
* expensive primary model. Pick something fast/cheap (e.g. a Haiku or
|
|
59
|
+
* DeepSeek key). When unset, those calls fall back to the active model
|
|
60
|
+
* (legacy behavior). An invalid/missing key also falls back to active.
|
|
61
|
+
*/
|
|
62
|
+
auxModelKey: z.string().optional(),
|
|
25
63
|
/**
|
|
26
64
|
* Toggle background auto-update. When true (default), code-shell checks
|
|
27
65
|
* npm for newer versions and — if the npm global prefix is writable —
|
|
@@ -62,12 +100,11 @@ export const SettingsSchema = z
|
|
|
62
100
|
protocol: z.enum(["openai-compat", "anthropic-style"]).optional(),
|
|
63
101
|
modelsPath: z.string().optional(),
|
|
64
102
|
/**
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* other than DeepSeek V4. Per-call overrides still win.
|
|
103
|
+
* Default reasoning/thinking setting for this provider's models.
|
|
104
|
+
* Rich shape: {mode:"off"|"on"} | {mode:"effort",effort} |
|
|
105
|
+
* {mode:"budget",budgetTokens}. Per-model `reasoning` wins.
|
|
69
106
|
*/
|
|
70
|
-
|
|
107
|
+
reasoning: ReasoningSettingSchema.optional(),
|
|
71
108
|
}))
|
|
72
109
|
.default([]),
|
|
73
110
|
models: z
|
|
@@ -90,12 +127,12 @@ export const SettingsSchema = z
|
|
|
90
127
|
baseUrl: z.string().optional(),
|
|
91
128
|
apiKey: z.string().optional(),
|
|
92
129
|
/**
|
|
93
|
-
* Per-model
|
|
94
|
-
* setting (settings.providers[].
|
|
95
|
-
* different models under the same provider need different
|
|
96
|
-
* defaults — e.g.
|
|
130
|
+
* Per-model reasoning override. Wins over the provider-level
|
|
131
|
+
* `reasoning` setting (settings.providers[].reasoning). Use this
|
|
132
|
+
* when different models under the same provider need different
|
|
133
|
+
* defaults — e.g. one model off (faster) and another on.
|
|
97
134
|
*/
|
|
98
|
-
|
|
135
|
+
reasoning: ReasoningSettingSchema.optional(),
|
|
99
136
|
})
|
|
100
137
|
.transform((m) => {
|
|
101
138
|
// Normalize: prefer `protocol`; fall back to legacy `provider`.
|
|
@@ -146,6 +183,9 @@ export const SettingsSchema = z
|
|
|
146
183
|
url: z.string().optional(),
|
|
147
184
|
transport: z.enum(["stdio", "sse", "streamable-http", "inprocess"]).optional(),
|
|
148
185
|
headers: z.record(z.string()).optional(),
|
|
186
|
+
// Codex-style on/off switch. Absent or true → connected; only
|
|
187
|
+
// literal false disables. Filtered in MCPManager.connectAll.
|
|
188
|
+
enabled: z.boolean().optional(),
|
|
149
189
|
}))
|
|
150
190
|
.default({}),
|
|
151
191
|
/**
|
|
@@ -162,6 +202,20 @@ export const SettingsSchema = z
|
|
|
162
202
|
* names (no colon suffix). See scanSkills(opts.disabledPlugins).
|
|
163
203
|
*/
|
|
164
204
|
disabledPlugins: z.array(z.string()).default([]),
|
|
205
|
+
/**
|
|
206
|
+
* Sub-agent role names (the `name` in .code-shell/agents/*.md) to
|
|
207
|
+
* hide from the registry. A disabled role is filtered out at load
|
|
208
|
+
* so it never appears in registry.list()/get() — the Agent tool's
|
|
209
|
+
* agent_type list won't show it and the LLM can't pick it. Mirrors
|
|
210
|
+
* disabledSkills / disabledPlugins.
|
|
211
|
+
*/
|
|
212
|
+
disabledAgents: z.array(z.string()).default([]),
|
|
213
|
+
/**
|
|
214
|
+
* Project-scoped tri-state capability overlay. Only meaningful in
|
|
215
|
+
* ${cwd}/.code-shell/settings.json; layered over the global baseline at
|
|
216
|
+
* capability-assembly time. Optional — absence === everything inherits.
|
|
217
|
+
*/
|
|
218
|
+
capabilityOverrides: CapabilityOverridesSchema,
|
|
165
219
|
instructions: z
|
|
166
220
|
.object({
|
|
167
221
|
fileName: z.string().default("CODESHELL.md"),
|
|
@@ -204,6 +258,15 @@ export const SettingsSchema = z
|
|
|
204
258
|
.record(z.enum(["serper", "tavily", "searxng"]), z.object({
|
|
205
259
|
apiKey: z.string().optional(),
|
|
206
260
|
baseUrl: z.string().optional(),
|
|
261
|
+
lastProbe: z
|
|
262
|
+
.object({
|
|
263
|
+
status: z.enum(["ok", "error", "unconfigured"]),
|
|
264
|
+
sampleTitles: z.array(z.string()).optional(),
|
|
265
|
+
errorMessage: z.string().optional(),
|
|
266
|
+
errorDetail: z.string().optional(),
|
|
267
|
+
lastProbedAt: z.string(),
|
|
268
|
+
})
|
|
269
|
+
.optional(),
|
|
207
270
|
}))
|
|
208
271
|
.optional(),
|
|
209
272
|
})
|
|
@@ -213,6 +276,24 @@ export const SettingsSchema = z
|
|
|
213
276
|
format: z.enum(["text", "json", "jsonl", "stream-json"]).default("text"),
|
|
214
277
|
})
|
|
215
278
|
.default({}),
|
|
279
|
+
/**
|
|
280
|
+
* Image attachment handling. Sub-fields:
|
|
281
|
+
* - detail: OpenAI-style fidelity hint applied to every user-
|
|
282
|
+
* attached image. "low" = 85 tokens/image fixed (cheap
|
|
283
|
+
* thumbnail), "high" = ~768 px tiles (default), "original" =
|
|
284
|
+
* keep client-side dimensions (most expensive). Anthropic
|
|
285
|
+
* ignores this field today; that's fine — it just gets
|
|
286
|
+
* dropped on the Claude path.
|
|
287
|
+
* - mcpImageTokenBudget: per-turn cap on token cost of images
|
|
288
|
+
* returned by MCP tools. Codex doesn't enforce this; CC uses
|
|
289
|
+
* 25 000. Set to 0 to disable.
|
|
290
|
+
*/
|
|
291
|
+
images: z
|
|
292
|
+
.object({
|
|
293
|
+
detail: z.enum(["low", "high", "original"]).optional(),
|
|
294
|
+
mcpImageTokenBudget: z.number().int().nonnegative().optional(),
|
|
295
|
+
})
|
|
296
|
+
.optional(),
|
|
216
297
|
/**
|
|
217
298
|
* OS-level sandbox for shell-tool execution. "auto" picks per platform:
|
|
218
299
|
* Seatbelt on macOS, bubblewrap on Linux when installed, otherwise off.
|
|
@@ -227,6 +308,34 @@ export const SettingsSchema = z
|
|
|
227
308
|
network: z.enum(["allow", "deny"]).optional(),
|
|
228
309
|
})
|
|
229
310
|
.optional(),
|
|
311
|
+
/**
|
|
312
|
+
* Project-local environment profile. Mirrors Codex's local-environment
|
|
313
|
+
* shape at the settings layer: setup/cleanup scripts can be customized per
|
|
314
|
+
* platform, and env values are stored as KEY=VALUE pairs. Runtime wiring is
|
|
315
|
+
* intentionally separate from the MCP server env model above.
|
|
316
|
+
*/
|
|
317
|
+
localEnvironment: z
|
|
318
|
+
.object({
|
|
319
|
+
name: z.string().optional(),
|
|
320
|
+
setupScripts: z
|
|
321
|
+
.object({
|
|
322
|
+
default: z.string().optional(),
|
|
323
|
+
macos: z.string().optional(),
|
|
324
|
+
linux: z.string().optional(),
|
|
325
|
+
windows: z.string().optional(),
|
|
326
|
+
})
|
|
327
|
+
.optional(),
|
|
328
|
+
cleanupScripts: z
|
|
329
|
+
.object({
|
|
330
|
+
default: z.string().optional(),
|
|
331
|
+
macos: z.string().optional(),
|
|
332
|
+
linux: z.string().optional(),
|
|
333
|
+
windows: z.string().optional(),
|
|
334
|
+
})
|
|
335
|
+
.optional(),
|
|
336
|
+
env: z.record(z.string()).optional(),
|
|
337
|
+
})
|
|
338
|
+
.optional(),
|
|
230
339
|
/**
|
|
231
340
|
* Shell-hook configuration. Each entry binds a HookEventName to a
|
|
232
341
|
* shell command; the command receives ctx JSON on stdin and returns
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in AddMarketplace tool — register a plugin marketplace source so the
|
|
3
|
+
* user can browse/install plugins from it in the UI. The tool only *adds the
|
|
4
|
+
* source* (git clone + validate marketplace.json via core's addMarketplace);
|
|
5
|
+
* which plugin to install is left to the user. Pairs with WebSearch/WebFetch:
|
|
6
|
+
* the model can discover a marketplace repo, then add it.
|
|
7
|
+
*
|
|
8
|
+
* Side effects (network + git clone + disk write) → permissionDefault "ask".
|
|
9
|
+
*/
|
|
10
|
+
import type { ToolDefinition } from "../../types.js";
|
|
11
|
+
export declare const addMarketplaceToolDef: ToolDefinition;
|
|
12
|
+
export declare function addMarketplaceTool(args: Record<string, unknown>): Promise<string>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in AddMarketplace tool — register a plugin marketplace source so the
|
|
3
|
+
* user can browse/install plugins from it in the UI. The tool only *adds the
|
|
4
|
+
* source* (git clone + validate marketplace.json via core's addMarketplace);
|
|
5
|
+
* which plugin to install is left to the user. Pairs with WebSearch/WebFetch:
|
|
6
|
+
* the model can discover a marketplace repo, then add it.
|
|
7
|
+
*
|
|
8
|
+
* Side effects (network + git clone + disk write) → permissionDefault "ask".
|
|
9
|
+
*/
|
|
10
|
+
import { addMarketplace } from "../../plugins/marketplaceManager.js";
|
|
11
|
+
export const addMarketplaceToolDef = {
|
|
12
|
+
name: "AddMarketplace",
|
|
13
|
+
description: "Register a plugin marketplace source so the user can browse and install " +
|
|
14
|
+
"plugins from it. Provide a short name and a source: either a GitHub repo " +
|
|
15
|
+
"(owner/name) or a git URL. This only ADDS the source — it does not install " +
|
|
16
|
+
"any plugin. Use WebSearch/WebFetch first to find a marketplace repo if you " +
|
|
17
|
+
"don't already have one.",
|
|
18
|
+
inputSchema: {
|
|
19
|
+
type: "object",
|
|
20
|
+
properties: {
|
|
21
|
+
name: {
|
|
22
|
+
type: "string",
|
|
23
|
+
description: "Short local name for this marketplace (e.g. 'official').",
|
|
24
|
+
},
|
|
25
|
+
source_type: {
|
|
26
|
+
type: "string",
|
|
27
|
+
enum: ["github", "git"],
|
|
28
|
+
description: "'github' for an owner/name repo, 'git' for a clone URL.",
|
|
29
|
+
},
|
|
30
|
+
repo: {
|
|
31
|
+
type: "string",
|
|
32
|
+
description: "GitHub repo in owner/name form (required when source_type='github').",
|
|
33
|
+
},
|
|
34
|
+
url: {
|
|
35
|
+
type: "string",
|
|
36
|
+
description: "Git clone URL (required when source_type='git').",
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
required: ["name", "source_type"],
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
export async function addMarketplaceTool(args) {
|
|
43
|
+
const name = args.name;
|
|
44
|
+
if (typeof name !== "string" || !name.trim()) {
|
|
45
|
+
return "Error: name is required";
|
|
46
|
+
}
|
|
47
|
+
const sourceType = args.source_type;
|
|
48
|
+
let source;
|
|
49
|
+
if (sourceType === "github") {
|
|
50
|
+
const repo = args.repo;
|
|
51
|
+
if (typeof repo !== "string" || !repo.includes("/")) {
|
|
52
|
+
return "Error: github source requires repo in owner/name form";
|
|
53
|
+
}
|
|
54
|
+
source = { source: "github", repo };
|
|
55
|
+
}
|
|
56
|
+
else if (sourceType === "git") {
|
|
57
|
+
const url = args.url;
|
|
58
|
+
if (typeof url !== "string" || !url.trim()) {
|
|
59
|
+
return "Error: git source requires a url";
|
|
60
|
+
}
|
|
61
|
+
source = { source: "git", url };
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
return "Error: source_type must be 'github' or 'git'";
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
const result = await addMarketplace(name, source);
|
|
68
|
+
if (!result.ok) {
|
|
69
|
+
return `Error adding marketplace ${name}: ${result.error}`;
|
|
70
|
+
}
|
|
71
|
+
return `Marketplace '${name}' added. The user can now browse and install plugins from it in the Extensions → Market UI.`;
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
return `Error adding marketplace ${name}: ${err.message}`;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -29,7 +29,15 @@ export interface AsyncAgentEntry {
|
|
|
29
29
|
agentId: string;
|
|
30
30
|
/** Short kind label shown in the dock (e.g. "Explore", "Plan"). */
|
|
31
31
|
name?: string;
|
|
32
|
+
/** Dispatched role (e.g. "general-purpose", "explorer"); shown in the dock
|
|
33
|
+
* so you can see what kind of agent is running. */
|
|
34
|
+
agentType?: string;
|
|
32
35
|
description: string;
|
|
36
|
+
/** Session that spawned this background agent. Lets the spawning
|
|
37
|
+
* Engine.run wait only on ITS OWN background agents (hasRunningForSession),
|
|
38
|
+
* not every concurrent session's. Undefined only for legacy/ad-hoc callers
|
|
39
|
+
* outside Engine.run. */
|
|
40
|
+
sessionId?: string;
|
|
33
41
|
status: AsyncAgentStatus;
|
|
34
42
|
startedAt: number;
|
|
35
43
|
finishedAt?: number;
|
|
@@ -46,6 +54,9 @@ declare class AsyncAgentRegistry {
|
|
|
46
54
|
subscribe: (cb: () => void) => (() => void);
|
|
47
55
|
getSnapshot: () => AsyncAgentEntry[];
|
|
48
56
|
hasRunning: () => boolean;
|
|
57
|
+
/** True if any background agent spawned by `sessionId` is still running.
|
|
58
|
+
* Drives Engine.run's "wait for my background agents before resolving". */
|
|
59
|
+
hasRunningForSession: (sessionId: string) => boolean;
|
|
49
60
|
/** Count of agents currently in the "running" state (cap enforcement). */
|
|
50
61
|
runningCount(): number;
|
|
51
62
|
private notify;
|
|
@@ -30,6 +30,11 @@ class AsyncAgentRegistry {
|
|
|
30
30
|
hasRunning = () => {
|
|
31
31
|
return this.snapshot.some((e) => e.status === "running");
|
|
32
32
|
};
|
|
33
|
+
/** True if any background agent spawned by `sessionId` is still running.
|
|
34
|
+
* Drives Engine.run's "wait for my background agents before resolving". */
|
|
35
|
+
hasRunningForSession = (sessionId) => {
|
|
36
|
+
return this.snapshot.some((e) => e.status === "running" && e.sessionId === sessionId);
|
|
37
|
+
};
|
|
33
38
|
/** Count of agents currently in the "running" state (cap enforcement). */
|
|
34
39
|
runningCount() {
|
|
35
40
|
return this.snapshot.filter((e) => e.status === "running").length;
|
|
@@ -108,10 +113,9 @@ class AsyncAgentRegistry {
|
|
|
108
113
|
catch {
|
|
109
114
|
// ignore abort errors — we still mark cancelled
|
|
110
115
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
this.notify();
|
|
116
|
+
// Reuse markFinished for the status/timestamp/notify bookkeeping rather
|
|
117
|
+
// than duplicating it (the entry is still "running" after abort()).
|
|
118
|
+
this.markFinished(agentId, "cancelled");
|
|
115
119
|
return true;
|
|
116
120
|
}
|
|
117
121
|
reset() {
|
|
@@ -11,6 +11,7 @@ import type { ToolContext } from "../context.js";
|
|
|
11
11
|
import type { AgentDefinitionRegistry } from "../../agent/agent-definition-registry.js";
|
|
12
12
|
import type { HookRegistry } from "../../hooks/registry.js";
|
|
13
13
|
export interface AgentTypeOverrides {
|
|
14
|
+
resolvedType?: string;
|
|
14
15
|
model?: string;
|
|
15
16
|
maxTurns?: number;
|
|
16
17
|
toolAllowlist?: string[];
|
|
@@ -18,10 +19,36 @@ export interface AgentTypeOverrides {
|
|
|
18
19
|
}
|
|
19
20
|
/**
|
|
20
21
|
* Resolve an `agent_type` against the role registry into spawn overrides.
|
|
21
|
-
*
|
|
22
|
-
*
|
|
22
|
+
*
|
|
23
|
+
* - Omitted type + non-empty registry → fall back to a configured role
|
|
24
|
+
* ("general-purpose" if present, else the first available) instead of
|
|
25
|
+
* running a nameless ephemeral agent. Relaxed from the earlier "throw when
|
|
26
|
+
* agent_type omitted": the model habitually omits it, and a hard error just
|
|
27
|
+
* turned every spawn into a failure. Falling back to a real role keeps the
|
|
28
|
+
* tool-allowlist / system-prompt benefits without breaking the call.
|
|
29
|
+
* - Omitted type + empty registry → empty overrides (true ephemeral; nothing
|
|
30
|
+
* to fall back to).
|
|
31
|
+
* - Unknown explicit type → throw, so the LLM gets a clear correction rather
|
|
32
|
+
* than silently running a generic agent.
|
|
23
33
|
*/
|
|
24
34
|
export declare function resolveAgentTypeOverrides(agentType: string | undefined, registry: AgentDefinitionRegistry | undefined): AgentTypeOverrides;
|
|
35
|
+
/**
|
|
36
|
+
* Render the "Available agent types" block injected into the Agent tool's
|
|
37
|
+
* description, listing the roles defined in .code-shell/agents/*.md so the
|
|
38
|
+
* model knows it can pass `agent_type` instead of hand-rolling an ad-hoc
|
|
39
|
+
* agent. Without this the model never sees the registry (it lives per-engine,
|
|
40
|
+
* not in the static tool def) and falls back to nameless ephemeral agents —
|
|
41
|
+
* see the Core A/B/C incident. Returns "" when no roles are defined, so the
|
|
42
|
+
* base description is left untouched.
|
|
43
|
+
*/
|
|
44
|
+
export declare function buildAgentTypesBlock(registry: AgentDefinitionRegistry | undefined): string;
|
|
45
|
+
/**
|
|
46
|
+
* Produce an Agent tool definition whose description ends with the live
|
|
47
|
+
* available-agent-types listing. Pure: takes the registry, returns a new def
|
|
48
|
+
* (the base `agentToolDef` const is never mutated). When no roles exist the
|
|
49
|
+
* base def is returned unchanged.
|
|
50
|
+
*/
|
|
51
|
+
export declare function agentToolDefWithTypes(registry: AgentDefinitionRegistry | undefined): ToolDefinition;
|
|
25
52
|
type SubAgentLifecycle = "subagent_start" | "subagent_finish" | "subagent_error";
|
|
26
53
|
/**
|
|
27
54
|
* Emit a sub-agent lifecycle event via the existing `notification` hook,
|
|
@@ -35,14 +62,6 @@ export declare function emitSubAgentHook(hooks: HookRegistry | undefined, kind:
|
|
|
35
62
|
text?: string;
|
|
36
63
|
error?: string;
|
|
37
64
|
}): void;
|
|
38
|
-
/** Default per-sub-agent wall-clock timeout (5 minutes). */
|
|
39
|
-
export declare const DEFAULT_SUBAGENT_TIMEOUT_MS: number;
|
|
40
|
-
/**
|
|
41
|
-
* Run `work()` with a timeout. On expiry, calls `onTimeout` (to abort the
|
|
42
|
-
* child) and rejects with a timeout error. The child's own abort handling
|
|
43
|
-
* unwinds its resources.
|
|
44
|
-
*/
|
|
45
|
-
export declare function runWithTimeout<T>(work: () => Promise<T>, timeoutMs: number, onTimeout: () => void): Promise<T>;
|
|
46
65
|
export declare const agentToolDef: ToolDefinition;
|
|
47
66
|
export declare function agentTool(args: Record<string, unknown>, ctx?: ToolContext): Promise<string>;
|
|
48
67
|
export declare const agentStatusToolDef: ToolDefinition;
|
|
@@ -32,26 +32,79 @@ function safeEmit(sink, event) {
|
|
|
32
32
|
});
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
|
+
/** Preferred default role when the caller omits agent_type but roles exist. */
|
|
36
|
+
const DEFAULT_AGENT_TYPE = "general-purpose";
|
|
35
37
|
/**
|
|
36
38
|
* Resolve an `agent_type` against the role registry into spawn overrides.
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
+
*
|
|
40
|
+
* - Omitted type + non-empty registry → fall back to a configured role
|
|
41
|
+
* ("general-purpose" if present, else the first available) instead of
|
|
42
|
+
* running a nameless ephemeral agent. Relaxed from the earlier "throw when
|
|
43
|
+
* agent_type omitted": the model habitually omits it, and a hard error just
|
|
44
|
+
* turned every spawn into a failure. Falling back to a real role keeps the
|
|
45
|
+
* tool-allowlist / system-prompt benefits without breaking the call.
|
|
46
|
+
* - Omitted type + empty registry → empty overrides (true ephemeral; nothing
|
|
47
|
+
* to fall back to).
|
|
48
|
+
* - Unknown explicit type → throw, so the LLM gets a clear correction rather
|
|
49
|
+
* than silently running a generic agent.
|
|
39
50
|
*/
|
|
40
51
|
export function resolveAgentTypeOverrides(agentType, registry) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
52
|
+
const available = registry?.list().map((d) => d.name) ?? [];
|
|
53
|
+
let resolvedType = agentType;
|
|
54
|
+
if (!resolvedType) {
|
|
55
|
+
if (available.length === 0)
|
|
56
|
+
return {};
|
|
57
|
+
resolvedType = available.includes(DEFAULT_AGENT_TYPE) ? DEFAULT_AGENT_TYPE : available[0];
|
|
58
|
+
}
|
|
59
|
+
const def = registry?.get(resolvedType);
|
|
44
60
|
if (!def) {
|
|
45
|
-
const
|
|
46
|
-
throw new Error(`unknown agent_type '${
|
|
61
|
+
const list = available.join(", ") || "(none defined)";
|
|
62
|
+
throw new Error(`unknown agent_type '${resolvedType}'. Available: ${list}`);
|
|
47
63
|
}
|
|
48
64
|
return {
|
|
65
|
+
resolvedType,
|
|
49
66
|
model: def.model,
|
|
50
67
|
maxTurns: def.maxTurns,
|
|
51
68
|
toolAllowlist: def.tools,
|
|
52
69
|
appendSystemPrompt: def.systemPrompt,
|
|
53
70
|
};
|
|
54
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Render the "Available agent types" block injected into the Agent tool's
|
|
74
|
+
* description, listing the roles defined in .code-shell/agents/*.md so the
|
|
75
|
+
* model knows it can pass `agent_type` instead of hand-rolling an ad-hoc
|
|
76
|
+
* agent. Without this the model never sees the registry (it lives per-engine,
|
|
77
|
+
* not in the static tool def) and falls back to nameless ephemeral agents —
|
|
78
|
+
* see the Core A/B/C incident. Returns "" when no roles are defined, so the
|
|
79
|
+
* base description is left untouched.
|
|
80
|
+
*/
|
|
81
|
+
export function buildAgentTypesBlock(registry) {
|
|
82
|
+
const defs = registry?.list() ?? [];
|
|
83
|
+
if (defs.length === 0)
|
|
84
|
+
return "";
|
|
85
|
+
const lines = defs.map((d) => {
|
|
86
|
+
const tools = d.tools && d.tools.length > 0 ? d.tools.join(", ") : "all parent tools";
|
|
87
|
+
return `- ${d.name}: ${d.description} (tools: ${tools})`;
|
|
88
|
+
});
|
|
89
|
+
return [
|
|
90
|
+
"",
|
|
91
|
+
"Available agent types (pass one as `agent_type` to reuse its role, tool allowlist, and turn cap):",
|
|
92
|
+
...lines,
|
|
93
|
+
"Pass the closest matching agent_type (e.g. read-only investigation → researcher/explorer, planning → planner, full multi-step work → general-purpose). If you omit agent_type it defaults to general-purpose (or the first available role) — passing an explicit one is preferred.",
|
|
94
|
+
].join("\n");
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Produce an Agent tool definition whose description ends with the live
|
|
98
|
+
* available-agent-types listing. Pure: takes the registry, returns a new def
|
|
99
|
+
* (the base `agentToolDef` const is never mutated). When no roles exist the
|
|
100
|
+
* base def is returned unchanged.
|
|
101
|
+
*/
|
|
102
|
+
export function agentToolDefWithTypes(registry) {
|
|
103
|
+
const block = buildAgentTypesBlock(registry);
|
|
104
|
+
if (!block)
|
|
105
|
+
return agentToolDef;
|
|
106
|
+
return { ...agentToolDef, description: agentToolDef.description + "\n" + block };
|
|
107
|
+
}
|
|
55
108
|
/**
|
|
56
109
|
* Emit a sub-agent lifecycle event via the existing `notification` hook,
|
|
57
110
|
* tagged with a `kind`. No-op when hooks are absent. Fire-and-forget: emit is
|
|
@@ -61,41 +114,25 @@ export function resolveAgentTypeOverrides(agentType, registry) {
|
|
|
61
114
|
export function emitSubAgentHook(hooks, kind, payload) {
|
|
62
115
|
void hooks?.emit("notification", { kind, ...payload });
|
|
63
116
|
}
|
|
64
|
-
/** Default per-sub-agent wall-clock timeout (5 minutes). */
|
|
65
|
-
export const DEFAULT_SUBAGENT_TIMEOUT_MS = 5 * 60_000;
|
|
66
|
-
/**
|
|
67
|
-
* Run `work()` with a timeout. On expiry, calls `onTimeout` (to abort the
|
|
68
|
-
* child) and rejects with a timeout error. The child's own abort handling
|
|
69
|
-
* unwinds its resources.
|
|
70
|
-
*/
|
|
71
|
-
export async function runWithTimeout(work, timeoutMs, onTimeout) {
|
|
72
|
-
let timer;
|
|
73
|
-
const timeout = new Promise((_, reject) => {
|
|
74
|
-
timer = setTimeout(() => {
|
|
75
|
-
onTimeout();
|
|
76
|
-
reject(new Error(`Sub-agent timed out after ${timeoutMs}ms`));
|
|
77
|
-
}, timeoutMs);
|
|
78
|
-
});
|
|
79
|
-
try {
|
|
80
|
-
return await Promise.race([work(), timeout]);
|
|
81
|
-
}
|
|
82
|
-
finally {
|
|
83
|
-
if (timer)
|
|
84
|
-
clearTimeout(timer);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
117
|
export const agentToolDef = {
|
|
88
118
|
name: "Agent",
|
|
89
|
-
description: "Launch a sub-agent to handle a
|
|
90
|
-
"The sub-agent
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
"
|
|
94
|
-
"
|
|
95
|
-
"
|
|
96
|
-
"
|
|
97
|
-
"
|
|
98
|
-
"
|
|
119
|
+
description: "Launch a sub-agent to handle a task in its own clean, isolated context. " +
|
|
120
|
+
"The sub-agent runs independently with access to the same tools, and its final " +
|
|
121
|
+
"report is the ONLY thing returned to you — it is stateless, so you cannot send " +
|
|
122
|
+
"follow-up messages. Write a complete, self-contained task description.\n\n" +
|
|
123
|
+
"PRIMARY USE — context isolation: when a task needs to read many files or run a " +
|
|
124
|
+
"broad investigation but you only need the conclusion, delegate it. The sub-agent " +
|
|
125
|
+
"absorbs the noisy intermediate output in its own context; you keep the answer, not " +
|
|
126
|
+
"the file dumps. This protects your main context from being flooded.\n\n" +
|
|
127
|
+
"DON'T use this for a quick lookup where you know the file/symbol and expect a few " +
|
|
128
|
+
"matches — use Read/Grep/Glob directly instead; spawning an agent wastes a turn.\n\n" +
|
|
129
|
+
"Parallel fan-out is the EXCEPTION: only launch several agents in one message when " +
|
|
130
|
+
"the work truly splits into independent pieces with no shared state. Prefer one " +
|
|
131
|
+
"well-scoped delegation over a swarm.\n\n" +
|
|
132
|
+
"You can optionally run an agent in the background using the run_in_background " +
|
|
133
|
+
"parameter. When it runs in the background, you will be automatically notified when " +
|
|
134
|
+
"it completes — do NOT sleep, poll, or proactively check on its progress. Continue " +
|
|
135
|
+
"with other work or respond to the user instead.",
|
|
99
136
|
inputSchema: {
|
|
100
137
|
type: "object",
|
|
101
138
|
properties: {
|
|
@@ -109,7 +146,10 @@ export const agentToolDef = {
|
|
|
109
146
|
type: "string",
|
|
110
147
|
description: "Optional reusable role defined in .code-shell/agents/*.md (e.g. 'researcher'). " +
|
|
111
148
|
"Loads that role's model, tool allowlist, turn cap, and system prompt. " +
|
|
112
|
-
"
|
|
149
|
+
"Disabled roles are not available. If you pass an unknown role you'll get " +
|
|
150
|
+
"an error listing the currently available roles. " +
|
|
151
|
+
"If omitted, defaults to a configured role (general-purpose or the first available); " +
|
|
152
|
+
"with no roles configured it runs an ephemeral agent. Passing an explicit role is preferred.",
|
|
113
153
|
},
|
|
114
154
|
description: {
|
|
115
155
|
type: "string",
|
|
@@ -155,9 +195,9 @@ uiStream,
|
|
|
155
195
|
* sub-agents.
|
|
156
196
|
*/
|
|
157
197
|
streamOverride) {
|
|
158
|
-
const { agentId, name, description } = opts;
|
|
198
|
+
const { agentId, name, description, agentType } = opts;
|
|
159
199
|
const startEndSink = uiStream ?? spawner.parentStream;
|
|
160
|
-
safeEmit(startEndSink, { type: "agent_start", agentId, name, description });
|
|
200
|
+
safeEmit(startEndSink, { type: "agent_start", agentId, name, description, agentType });
|
|
161
201
|
emitSubAgentHook(opts.hooks, "subagent_start", { agentId, description });
|
|
162
202
|
// `resetPlanMode` / `restorePlanMode` operated on a module-level singleton
|
|
163
203
|
// that no longer exists. The child Engine is a fresh instance; plan-mode
|
|
@@ -165,7 +205,7 @@ streamOverride) {
|
|
|
165
205
|
// instances (finalized in T6).
|
|
166
206
|
const text = await spawner.spawn({ ...opts, streamOverride });
|
|
167
207
|
const finalText = text || `Agent completed but produced no text output.`;
|
|
168
|
-
safeEmit(startEndSink, { type: "agent_end", agentId, name, description, text: finalText });
|
|
208
|
+
safeEmit(startEndSink, { type: "agent_end", agentId, name, description, text: finalText, agentType });
|
|
169
209
|
emitSubAgentHook(opts.hooks, "subagent_finish", { agentId, description, text: finalText });
|
|
170
210
|
return finalText;
|
|
171
211
|
}
|
|
@@ -218,7 +258,11 @@ export async function agentTool(args, ctx) {
|
|
|
218
258
|
asyncAgentRegistry.register({
|
|
219
259
|
agentId,
|
|
220
260
|
name,
|
|
261
|
+
agentType: overrides.resolvedType,
|
|
221
262
|
description,
|
|
263
|
+
// Tag with the spawning session so the parent Engine.run waits only on
|
|
264
|
+
// its own background agents (hasRunningForSession).
|
|
265
|
+
sessionId: ctx?.sessionId,
|
|
222
266
|
status: "running",
|
|
223
267
|
startedAt: Date.now(),
|
|
224
268
|
abort: () => controller.abort(),
|
|
@@ -241,11 +285,13 @@ export async function agentTool(args, ctx) {
|
|
|
241
285
|
agentId,
|
|
242
286
|
name,
|
|
243
287
|
description,
|
|
288
|
+
agentType: overrides.resolvedType,
|
|
244
289
|
prompt,
|
|
245
290
|
maxTurns,
|
|
246
291
|
model: overrides.model,
|
|
247
292
|
toolAllowlist: overrides.toolAllowlist,
|
|
248
293
|
appendSystemPrompt: overrides.appendSystemPrompt,
|
|
294
|
+
readOnlySession: overrides.resolvedType === "researcher" || overrides.resolvedType === "explorer",
|
|
249
295
|
hooks: ctx?.hooks,
|
|
250
296
|
signal: controller.signal,
|
|
251
297
|
}, parentStream, // uiStream: agent_start/end → main feed
|
|
@@ -338,31 +384,41 @@ export async function agentTool(args, ctx) {
|
|
|
338
384
|
].join("\n");
|
|
339
385
|
}
|
|
340
386
|
// ─── Synchronous path ──────────────────────────────────────────
|
|
341
|
-
//
|
|
342
|
-
//
|
|
343
|
-
//
|
|
344
|
-
//
|
|
345
|
-
//
|
|
387
|
+
// No wall-clock timeout on the sub-agent lifecycle — that matches Claude
|
|
388
|
+
// Code / Codex, where a sub-agent (Task) is bounded by maxTurns + per-tool
|
|
389
|
+
// timeouts (Bash etc. carry their own) + parent/user abort, NOT by a global
|
|
390
|
+
// countdown that kills legitimate heavy work mid-task. The old
|
|
391
|
+
// runWithTimeout(5min) both (a) murdered a "read 40 files" agent at 5:00 and
|
|
392
|
+
// (b) raced with the normal completion path: the timeout aborted the child,
|
|
393
|
+
// the catch emitted agent_end{error}, AND the aborted child still let
|
|
394
|
+
// runSubAgent reach its line-280 agent_end{text} — two agent_end events for
|
|
395
|
+
// one agent, the later text one overwriting the error so a timed-out agent
|
|
396
|
+
// rendered as "done". Dropping the timeout removes the race entirely: a
|
|
397
|
+
// genuine spawn error throws BEFORE the success emit, so only the catch
|
|
398
|
+
// fires; a clean run emits agent_end{text} exactly once. (fix:
|
|
399
|
+
// subagent-timeout-double-agent-end)
|
|
346
400
|
const syncController = new AbortController();
|
|
347
401
|
const onParentAbort = () => syncController.abort();
|
|
348
402
|
parentSignal?.addEventListener("abort", onParentAbort, { once: true });
|
|
349
403
|
try {
|
|
350
|
-
return await
|
|
404
|
+
return await runSubAgent(spawner, {
|
|
351
405
|
agentId,
|
|
352
406
|
name,
|
|
353
407
|
description,
|
|
408
|
+
agentType: overrides.resolvedType,
|
|
354
409
|
prompt,
|
|
355
410
|
maxTurns,
|
|
356
411
|
model: overrides.model,
|
|
357
412
|
toolAllowlist: overrides.toolAllowlist,
|
|
358
413
|
appendSystemPrompt: overrides.appendSystemPrompt,
|
|
414
|
+
readOnlySession: overrides.resolvedType === "researcher" || overrides.resolvedType === "explorer",
|
|
359
415
|
hooks: ctx?.hooks,
|
|
360
416
|
signal: syncController.signal,
|
|
361
|
-
})
|
|
417
|
+
});
|
|
362
418
|
}
|
|
363
419
|
catch (err) {
|
|
364
420
|
emitSubAgentHook(ctx?.hooks, "subagent_error", { agentId, description, error: err.message });
|
|
365
|
-
safeEmit(parentStream, { type: "agent_end", agentId, name, description, error: err.message });
|
|
421
|
+
safeEmit(parentStream, { type: "agent_end", agentId, name, description, error: err.message, agentType: overrides.resolvedType });
|
|
366
422
|
if (parentSignal?.aborted)
|
|
367
423
|
return "Agent was aborted.";
|
|
368
424
|
return `Agent error: ${err.message}`;
|