pi-ultracode 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,7 +4,7 @@ Deterministic multi-agent workflows for [Pi](https://github.com/earendil-works/p
4
4
 
5
5
  **English** · [简体中文](https://github.com/ekil1100/pi-ultracode/blob/main/README.zh-CN.md)
6
6
 
7
- `pi-ultracode` adds an opt-in high-effort mode to Pi. When a task benefits from delegation, the main agent can run a bounded JavaScript workflow with parallel subagents, isolated worktrees, structured output, durable resume, and live progress.
7
+ `pi-ultracode` adds opt-in semantic analysis-depth modes to Pi. It can route each task automatically or hold a fixed focused, standard, or deep policy. When delegation adds value, the main agent can run a bounded JavaScript workflow with parallel subagents, isolated worktrees, structured output, durable resume, and live progress.
8
8
 
9
9
  The project combines the terminal-first agentic experience of [Claude Code](https://github.com/anthropics/claude-code) with the explicit workflow and structural-control ideas found in [Grok Build](https://github.com/xai-org/grok-build), while keeping Pi as the runtime.
10
10
 
@@ -14,14 +14,14 @@ The project combines the terminal-first agentic experience of [Claude Code](http
14
14
  pi install npm:pi-ultracode
15
15
  ```
16
16
 
17
- Then reload Pi and toggle Ultracode on:
17
+ Then reload Pi and enable adaptive Ultracode:
18
18
 
19
19
  ```text
20
20
  /reload
21
21
  /ultracode
22
22
  ```
23
23
 
24
- You can also enable it at startup:
24
+ You can also start directly in `auto` mode:
25
25
 
26
26
  ```bash
27
27
  pi --ultracode
@@ -33,7 +33,7 @@ Current releases are tested with Pi 0.84.
33
33
 
34
34
  ## Core features
35
35
 
36
- - **Opt-in high-effort mode** — requests the highest thinking level supported by the current model and restores the previous level when disabled.
36
+ - **Adaptive semantic depth** — choose `auto`, `focused`, `standard`, or `deep`; `auto` selects the smallest evidence-sufficient depth and `off` restores the previous thinking level.
37
37
  - **Explicit workflows** — compose work with `agent()`, `parallel()`, `pipeline()`, and nested `workflow()` calls.
38
38
  - **Independent subagents** — each agent gets its own Pi session, context, tools, model selection, and optional role.
39
39
  - **Parallel worktree isolation** — writing agents can work in temporary git worktrees before their patches are integrated.
@@ -44,7 +44,7 @@ Current releases are tested with Pi 0.84.
44
44
 
45
45
  ## Quick start
46
46
 
47
- Toggle Ultracode on and describe a substantial task:
47
+ Enable adaptive Ultracode and describe a task:
48
48
 
49
49
  ```text
50
50
  /ultracode
@@ -59,16 +59,32 @@ Useful commands:
59
59
 
60
60
  | Command | Action |
61
61
  | --- | --- |
62
- | `/ultracode` | Toggle Ultracode on or off |
63
- | `/ultracode on` | Explicitly enable it (optional alias) |
62
+ | `/ultracode` | Enable `auto` from off; disable any active mode |
63
+ | `/ultracode auto` | Select adaptive semantic-depth routing |
64
+ | `/ultracode focused` | Fix the lightweight, narrowly scoped policy |
65
+ | `/ultracode standard` | Fix the balanced policy with conditional verification |
66
+ | `/ultracode deep` | Fix the high-assurance policy with deep verification and max effort |
64
67
  | `/ultracode off` | Disable it and restore the previous thinking level |
65
- | `/ultracode status` | Show the current mode and effective thinking level |
68
+ | `/ultracode status` | Show the configured mode and effective thinking level |
66
69
  | `/workflows` or `F6` | Open the workflow browser |
67
70
  | `/workflows <runId>` | Open a specific run |
68
71
  | `/workflows abort` | Abort active runs |
69
72
 
70
73
  Press `Esc` to cancel a running workflow. In Pi's fullscreen TUI, use `Ctrl+PageUp`, `Ctrl+PageDown`, and `Ctrl+End` inside workflow details.
71
74
 
75
+ ## Analysis depth
76
+
77
+ Depth is semantic, not time-based:
78
+
79
+ - **Focused** prefers the parent agent and one bounded line of inquiry. It does not run adversarial verification by default.
80
+ - **Standard** covers the few independent dimensions that can change the answer and verifies only high-risk, conflicting, or weakly evidenced claims.
81
+ - **Deep** uses bounded multi-perspective investigation and adversarial verification for high-risk or explicitly comprehensive work.
82
+ - **Auto** routes to the smallest sufficient level from user intent, consequence risk, scope, ambiguity, available evidence, and conflicts. It escalates only when evidence requires it.
83
+
84
+ Research stops when key claims have direct evidence, no material conflict or unresolved high-risk question remains, and another round would repeat known evidence. Wall-clock time, deadlines, and duration limits are never used to choose or stop analysis depth. `maxAgents` and `reserveAgents` remain structural admission limits.
85
+
86
+ Focused defaults to medium effort, auto and standard to high, and deep to max; individual workflow agents can still override effort with a model suffix. A separate skeptic or synthesis agent is not automatic.
87
+
72
88
  ## Workflow example
73
89
 
74
90
  The main agent normally writes workflows for you. Reusable workflows can also be saved under `.pi/ultracode/workflows/*.workflow.js` or `~/.pi/ultracode/workflows/*.workflow.js`. Project-scoped workflows are discovered only after Pi marks the project as trusted; user-scoped workflows remain available in untrusted projects.
@@ -133,7 +149,7 @@ Workflow agent sessions retain project context and ordinary skills, but do not i
133
149
 
134
150
  Resume is intentionally immutable: the normalized script, arguments, canonical repository/relative cwd, project-trust context, agent definitions, effective models, and call structure must still match. Worktree delivery writes a durable recovery intent before changing the shared repository; an interrupted or conflicted delivery blocks automatic replay and reports its recovery patch. Changed work starts a new run.
135
151
 
136
- Token and cost data are reported for observability, not enforced as a budget. Worker and VM restrictions are determinism and liveness guards, not a security sandbox.
152
+ Token and cost data are reported for observability, not enforced as a budget. Worker and VM restrictions are determinism and liveness guards, not a security sandbox and not analysis-depth controls.
137
153
 
138
154
  ## Design inspiration
139
155
 
package/README.zh-CN.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  [English](README.md) · **简体中文**
6
6
 
7
- `pi-ultracode` 为 Pi 增加一个可选的高强度执行模式。当任务适合拆分时,主代理可以运行有边界的 JavaScript 工作流,使用并行子代理、隔离 worktree、结构化输出、持久恢复和实时进度。
7
+ `pi-ultracode` 为 Pi 增加可选的语义化分析深度模式。它既可以自动判断任务需要的深度,也可以固定使用 focused、standard 或 deep 策略。当委派确有价值时,主代理可以运行有边界的 JavaScript 工作流,使用并行子代理、隔离 worktree、结构化输出、持久恢复和实时进度。
8
8
 
9
9
  项目结合了 [Claude Code](https://github.com/anthropics/claude-code) 的终端 Agentic 体验,以及 [Grok Build](https://github.com/xai-org/grok-build) 的显式工作流和结构控制思路,同时继续使用 Pi 作为运行时。
10
10
 
@@ -14,14 +14,14 @@
14
14
  pi install npm:pi-ultracode
15
15
  ```
16
16
 
17
- 然后重新加载 Pi,并用主命令切换到 Ultracode:
17
+ 然后重新加载 Pi,并用主命令开启自适应 Ultracode:
18
18
 
19
19
  ```text
20
20
  /reload
21
21
  /ultracode
22
22
  ```
23
23
 
24
- 也可以在启动时直接启用:
24
+ 也可以在启动时直接进入 `auto` 模式:
25
25
 
26
26
  ```bash
27
27
  pi --ultracode
@@ -33,7 +33,7 @@ pi --ultracode
33
33
 
34
34
  ## 核心特性
35
35
 
36
- - **可选高强度模式**:请求当前模型支持的最高思考强度,关闭时恢复之前的强度。
36
+ - **自适应语义深度**:可选择 `auto`、`focused`、`standard` 或 `deep`;`auto` 使用证据充分的最小深度,关闭时恢复之前的思考强度。
37
37
  - **显式工作流**:通过 `agent()`、`parallel()`、`pipeline()` 和嵌套 `workflow()` 组合任务。
38
38
  - **独立子代理**:每个代理拥有独立的 Pi 会话、上下文、工具、模型选择和可选角色。
39
39
  - **并行 worktree 隔离**:写入型代理可在临时 git worktree 中工作,再集成补丁。
@@ -44,7 +44,7 @@ pi --ultracode
44
44
 
45
45
  ## 快速开始
46
46
 
47
- 用主命令开启 Ultracode,然后描述一个较复杂的任务:
47
+ 用主命令开启自适应 Ultracode,然后描述任务:
48
48
 
49
49
  ```text
50
50
  /ultracode
@@ -59,16 +59,32 @@ Pi 会自行判断工作流是否有帮助。小任务仍可继续使用普通
59
59
 
60
60
  | 命令 | 作用 |
61
61
  | --- | --- |
62
- | `/ultracode` | 切换 Ultracode 的开启或关闭状态 |
63
- | `/ultracode on` | 显式启用(可选别名) |
62
+ | `/ultracode` | 关闭时开启 `auto`;任一模式已开启时关闭 |
63
+ | `/ultracode auto` | 使用自适应语义深度路由 |
64
+ | `/ultracode focused` | 固定使用轻量、聚焦的分析策略 |
65
+ | `/ultracode standard` | 固定使用平衡策略和条件验证 |
66
+ | `/ultracode deep` | 固定使用高保障深度验证和 max effort |
64
67
  | `/ultracode off` | 关闭并恢复之前的思考强度 |
65
- | `/ultracode status` | 查看当前模式和实际思考强度 |
68
+ | `/ultracode status` | 查看配置模式和实际思考强度 |
66
69
  | `/workflows` 或 `F6` | 打开工作流浏览器 |
67
70
  | `/workflows <runId>` | 打开指定运行 |
68
71
  | `/workflows abort` | 中止活动运行 |
69
72
 
70
73
  按 `Esc` 可取消正在运行的工作流。在 Pi 的 fullscreen TUI 中,请在工作流详情内使用 `Ctrl+PageUp`、`Ctrl+PageDown` 和 `Ctrl+End`。
71
74
 
75
+ ## 分析深度
76
+
77
+ 深度是语义化质量决策,不按运行时间判断:
78
+
79
+ - **Focused** 优先使用主代理和单一、边界明确的调查路径,默认不做对抗验证。
80
+ - **Standard** 覆盖少量真正会改变结论的独立维度,只验证高风险、冲突或证据薄弱的结论。
81
+ - **Deep** 面向高风险或用户明确要求全面分析的任务,使用有界的多视角调查和对抗验证。
82
+ - **Auto** 根据用户意图、影响风险、范围、歧义、现有证据和结论冲突,选择足够完成任务的最小等级;仅在证据要求升级时加深。
83
+
84
+ 当关键结论已有直接证据、没有实质冲突或未解决的高风险问题,且下一轮只会重复已知证据时停止。墙钟时间、deadline 和 duration limit 不得用于选择或停止分析深度。`maxAgents` 与 `reserveAgents` 继续作为结构性准入限制。
85
+
86
+ Focused 默认使用 medium effort,auto 和 standard 默认使用 high,deep 使用 max;单个 workflow agent 仍可通过模型后缀覆盖 effort。skeptic 和独立 synthesis agent 都不是默认步骤。
87
+
72
88
  ## 工作流示例
73
89
 
74
90
  通常由主代理自动编写工作流。需要复用时,可以保存到 `.pi/ultracode/workflows/*.workflow.js` 或 `~/.pi/ultracode/workflows/*.workflow.js`。只有 Pi 将项目标记为可信后,才会发现项目级工作流;用户级工作流在未信任项目中仍可使用。
@@ -133,7 +149,7 @@ Workflow 子代理会保留项目上下文和普通 skills,但不会初始化
133
149
 
134
150
  Resume 刻意保持不可变:规范化脚本、参数、规范仓库及仓内相对 cwd、项目信任上下文、代理定义、实际模型和调用结构都必须匹配。Worktree 交付会在修改共享仓库前先写入持久恢复意图;中断或冲突的交付会阻止自动回放,并报告恢复补丁。工作内容发生变化时,应启动新的 run。
135
151
 
136
- Token 和成本只用于可观测性,不作为执行预算。Worker 与 VM 限制用于确定性和存活性保护,不是安全沙箱。
152
+ Token 和成本只用于可观测性,不作为执行预算。Worker 与 VM 限制用于确定性和存活性保护,不是安全沙箱,也不参与分析深度判断。
137
153
 
138
154
  ## 设计参考
139
155
 
@@ -1,10 +1,8 @@
1
1
  /**
2
2
  * pi-ultracode extension entrypoint.
3
3
  *
4
- * Wires together the three pillars of Claude-Code-style "ultracode":
5
- * 1. The ultracode effort mode (max thinking + standing workflow opt-in).
6
- * 2. The full `workflow` orchestration tool.
7
- * 3. The `/ultracode` and `/workflows` commands.
4
+ * Wires together Ultracode's semantic analysis-depth modes, deterministic
5
+ * workflow orchestration, and the `/ultracode` / `/workflows` commands.
8
6
  */
9
7
 
10
8
  import {
@@ -53,7 +51,7 @@ export default function extension(pi: ExtensionAPI, extraDeps: UltracodeExtensio
53
51
  // Opt-in via CLI flag: `pi --ultracode`.
54
52
  pi.registerFlag("ultracode", {
55
53
  type: "boolean",
56
- description: "Start the session in ultracode mode (max thinking + default workflow orchestration).",
54
+ description: "Start the session in adaptive Ultracode mode.",
57
55
  });
58
56
 
59
57
  // SDK-created sessions can prompt without emitting session_start. Sync during
@@ -68,7 +66,7 @@ export default function extension(pi: ExtensionAPI, extraDeps: UltracodeExtensio
68
66
  if (event.toolName === workflowTool.name && !mode.isEnforcing()) {
69
67
  return {
70
68
  block: true,
71
- reason: "The workflow tool is disabled. Run /ultracode on before using it.",
69
+ reason: "The workflow tool is disabled. Run /ultracode or select an Ultracode depth before using it.",
72
70
  };
73
71
  }
74
72
  });
@@ -92,7 +90,7 @@ export default function extension(pi: ExtensionAPI, extraDeps: UltracodeExtensio
92
90
  // ignore
93
91
  }
94
92
  if (!mode.isEnabled() && pi.getFlag?.("ultracode") === true) {
95
- mode.enable(pi);
93
+ mode.enable(pi, "auto");
96
94
  }
97
95
  // Registration makes extension tools discoverable; activation remains opt-in.
98
96
  mode.syncWorkflowTool(pi);
@@ -128,7 +126,7 @@ export default function extension(pi: ExtensionAPI, extraDeps: UltracodeExtensio
128
126
 
129
127
  pi.on("session_shutdown", async () => {
130
128
  // Quiesce first so late model/effort events cannot undo restoration. The
131
- // persisted enabled state remains on for reload/resume/fork replacements.
129
+ // The persisted configured mode remains active for reload/resume/fork replacements.
132
130
  mode.suspend(pi);
133
131
  await mode.flushThinkingPreference();
134
132
  });
@@ -137,7 +135,7 @@ export default function extension(pi: ExtensionAPI, extraDeps: UltracodeExtensio
137
135
  // Reconcile the tool schema and always append the standing block on an
138
136
  // enforcing turn, even when another active-tool writer caused drift.
139
137
  mode.syncWorkflowTool(pi);
140
- mode.reapplyMaximumThinking(pi);
138
+ mode.reapplyConfiguredThinking(pi);
141
139
  await mode.flushThinkingPreference();
142
140
  return mode.beforeAgentStart(event);
143
141
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-ultracode",
3
- "version": "0.4.0",
4
- "description": "Claude-Code-style \"ultracode\" for Pi: an effort mode that defaults to deterministic multi-agent workflow orchestration, with worktree isolation, per-agent model overrides, custom agent types, nested workflows, resumable runs, and a /workflows manager.",
3
+ "version": "0.5.0",
4
+ "description": "Adaptive semantic-depth workflow orchestration for Pi, with focused, standard, and deep modes, isolated subagents, worktree delivery, structured output, and durable resume.",
5
5
  "type": "module",
6
6
  "keywords": [
7
7
  "pi-package",
package/src/commands.ts CHANGED
@@ -3,6 +3,7 @@
3
3
  */
4
4
 
5
5
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
6
+ import { ACTIVE_ULTRACODE_MODES, isActiveUltracodeMode } from "./depth.ts";
6
7
  import type { UltracodeMode } from "./mode.ts";
7
8
  import type { WorkflowRegistry } from "./workflow/registry.ts";
8
9
  import { workflowRunsDir } from "./workflow/tool.ts";
@@ -10,10 +11,10 @@ import { openWorkflowOverlay } from "./workflow/workflow-overlay.ts";
10
11
 
11
12
  export function registerCommands(pi: ExtensionAPI, mode: UltracodeMode, registry: WorkflowRegistry): void {
12
13
  pi.registerCommand("ultracode", {
13
- description: "Toggle ultracode mode (max thinking + default workflow orchestration). Bare /ultracode toggles; subcommands: on|off|status",
14
+ description: "Toggle adaptive Ultracode, or select a fixed semantic depth: auto|focused|standard|deep|off|status",
14
15
  getArgumentCompletions(prefix: string) {
15
- return ["on", "off", "status"]
16
- .filter((s) => s.startsWith(prefix))
16
+ return [...ACTIVE_ULTRACODE_MODES, "off", "status"]
17
+ .filter((value) => value.startsWith(prefix))
17
18
  .map((value) => ({ value, label: value }));
18
19
  },
19
20
  handler: async (args: string, ctx) => {
@@ -21,32 +22,31 @@ export function registerCommands(pi: ExtensionAPI, mode: UltracodeMode, registry
21
22
  const parts = args.trim().split(/\s+/).filter(Boolean);
22
23
  const sub = (parts[0] ?? "").toLowerCase();
23
24
 
24
- // Bare `/ultracode` is a toggle.
25
+ // Bare `/ultracode` enables auto from off and disables any active mode.
25
26
  if (sub === "") {
26
27
  const nowOn = mode.toggle(pi);
27
28
  await mode.flushThinkingPreference();
28
29
  ctx.ui.notify(
29
- nowOn ? `Ultracode on — ${mode.statusLine()}` : "Ultracode off — thinking restored and workflow tool disabled.",
30
+ nowOn
31
+ ? `Ultracode auto — ${mode.statusLine()}`
32
+ : "Ultracode off — thinking restored and workflow tool disabled.",
30
33
  "info",
31
34
  );
32
35
  ctx.ui.setStatus("ultracode", nowOn ? mode.statusLine() : undefined);
33
36
  return;
34
37
  }
35
38
 
39
+ if (parts.length > 1) {
40
+ ctx.ui.notify(ultracodeUsage(), "error");
41
+ return;
42
+ }
43
+
36
44
  if (sub === "status") {
37
- if (parts.length > 1) {
38
- ctx.ui.notify(ultracodeUsage(), "error");
39
- return;
40
- }
41
45
  ctx.ui.notify(mode.statusLine(), "info");
42
46
  return;
43
47
  }
44
48
 
45
49
  if (sub === "off") {
46
- if (parts.length > 1) {
47
- ctx.ui.notify(ultracodeUsage(), "error");
48
- return;
49
- }
50
50
  mode.disable(pi);
51
51
  await mode.flushThinkingPreference();
52
52
  ctx.ui.notify("Ultracode off — thinking restored and workflow tool disabled.", "info");
@@ -54,14 +54,14 @@ export function registerCommands(pi: ExtensionAPI, mode: UltracodeMode, registry
54
54
  return;
55
55
  }
56
56
 
57
- if (sub !== "on" || parts.length > 1) {
57
+ if (!isActiveUltracodeMode(sub)) {
58
58
  ctx.ui.notify(ultracodeUsage(), "error");
59
59
  return;
60
60
  }
61
61
 
62
- mode.enable(pi);
62
+ mode.enable(pi, sub);
63
63
  await mode.flushThinkingPreference();
64
- ctx.ui.notify(`Ultracode on — ${mode.statusLine()}`, "info");
64
+ ctx.ui.notify(`Ultracode ${sub} — ${mode.statusLine()}`, "info");
65
65
  ctx.ui.setStatus("ultracode", mode.statusLine());
66
66
  },
67
67
  });
@@ -100,5 +100,5 @@ export function registerCommands(pi: ExtensionAPI, mode: UltracodeMode, registry
100
100
  }
101
101
 
102
102
  function ultracodeUsage(): string {
103
- return "Usage: /ultracode [on|off|status]";
103
+ return "Usage: /ultracode [auto|focused|standard|deep|off|status]";
104
104
  }
package/src/depth.ts ADDED
@@ -0,0 +1,38 @@
1
+ /** Ultracode's configured analysis modes and their default reasoning effort. */
2
+
3
+ import type { ThinkingLevel } from "./thinking.ts";
4
+
5
+ export const ACTIVE_ULTRACODE_MODES = ["auto", "focused", "standard", "deep"] as const;
6
+
7
+ export type ActiveUltracodeMode = (typeof ACTIVE_ULTRACODE_MODES)[number];
8
+ export type UltracodeModeName = "off" | ActiveUltracodeMode;
9
+
10
+ const ACTIVE_MODE_SET = new Set<string>(ACTIVE_ULTRACODE_MODES);
11
+
12
+ export function isActiveUltracodeMode(value: unknown): value is ActiveUltracodeMode {
13
+ return typeof value === "string" && ACTIVE_MODE_SET.has(value);
14
+ }
15
+
16
+ export function isUltracodeModeName(value: unknown): value is UltracodeModeName {
17
+ return value === "off" || isActiveUltracodeMode(value);
18
+ }
19
+
20
+ /**
21
+ * Default effort for the parent turn and for child agents that do not override
22
+ * it explicitly. Auto starts from the balanced standard effort; its prompt
23
+ * policy can route individual workflow agents lower or higher as evidence
24
+ * requires. Pi still clamps each request to the selected model's capabilities.
25
+ */
26
+ export function thinkingLevelForMode(mode: UltracodeModeName): ThinkingLevel | undefined {
27
+ switch (mode) {
28
+ case "focused":
29
+ return "medium";
30
+ case "auto":
31
+ case "standard":
32
+ return "high";
33
+ case "deep":
34
+ return "max";
35
+ case "off":
36
+ return undefined;
37
+ }
38
+ }
package/src/index.ts CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  export { default } from "../extensions/ultracode.ts";
4
4
  export { UltracodeMode, MODE_ENTRY_TYPE } from "./mode.ts";
5
+ export {
6
+ ACTIVE_ULTRACODE_MODES,
7
+ isActiveUltracodeMode,
8
+ isUltracodeModeName,
9
+ thinkingLevelForMode,
10
+ type ActiveUltracodeMode,
11
+ type UltracodeModeName,
12
+ } from "./depth.ts";
5
13
  export { registerCommands } from "./commands.ts";
6
14
  export {
7
15
  ULTRACODE_TAGLINE,
package/src/mode.ts CHANGED
@@ -1,16 +1,21 @@
1
1
  /**
2
2
  * Ultracode mode controller.
3
3
  *
4
- * Ultracode is a session-scoped effort mode. While on, it:
5
- * - raises the thinking level to the model's maximum (remembering the previous level),
4
+ * Ultracode is a session-scoped semantic analysis-depth mode. While active, it:
5
+ * - applies the configured mode's default thinking level while preserving the
6
+ * user's previous level,
6
7
  * - keeps the `workflow` tool active,
7
- * - injects a standing "author and run a workflow by default" system block on
8
- * every turn,
9
- * - persists its on/off state in session custom entries so it survives
10
- * reload, resume, fork, and compaction.
8
+ * - injects the configured auto/focused/standard/deep policy on every turn,
9
+ * - persists branch-local mode state across reload, resume, fork, and compaction.
11
10
  */
12
11
 
13
12
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
13
+ import {
14
+ isActiveUltracodeMode,
15
+ thinkingLevelForMode,
16
+ type ActiveUltracodeMode,
17
+ type UltracodeModeName,
18
+ } from "./depth.ts";
14
19
  import { ULTRACODE_ACTIVE_REMINDER, ULTRACODE_TAGLINE, ultracodeSystemBlock } from "./prompts.ts";
15
20
  import {
16
21
  LEGACY_ULTRACODE_THINKING_LEVEL,
@@ -24,7 +29,7 @@ export type { ThinkingLevel } from "./thinking.ts";
24
29
  export const MODE_ENTRY_TYPE = "ultracode-mode";
25
30
 
26
31
  interface PersistedModeState {
27
- enabled: boolean;
32
+ mode: UltracodeModeName;
28
33
  previousThinking?: ThinkingLevel;
29
34
  /** `null` records that the setting was originally absent (Pi defaults to medium). */
30
35
  previousDefaultThinking?: ThinkingLevel | null;
@@ -47,13 +52,13 @@ export interface ThinkingPreferenceStore {
47
52
  }
48
53
 
49
54
  export class UltracodeMode {
50
- private enabled = false;
55
+ private mode: UltracodeModeName = "off";
51
56
  private suspended = false;
52
57
  private previousThinking: ThinkingLevel | undefined;
53
58
  private previousDefaultThinking: ThinkingLevel | null | undefined;
54
59
  /** Restore a level later if the current non-reasoning model clamps it to off. */
55
60
  private pendingPreviousThinking: ThinkingLevel | undefined;
56
- /** The level Pi actually applied after clamping the maximum request. */
61
+ /** The level Pi actually applied after clamping the configured request. */
57
62
  private appliedThinking: ThinkingLevel | undefined;
58
63
  /** Prevent mode-owned thinking changes from being mistaken for manual overrides. */
59
64
  private applyingThinking = false;
@@ -95,36 +100,45 @@ export class UltracodeMode {
95
100
  }
96
101
  }
97
102
 
98
- /** Enable if off, disable if on. Returns the new enabled state. */
103
+ /** Enable auto if off, otherwise disable. Returns the new enabled state. */
99
104
  toggle(pi: ExtensionAPI): boolean {
100
- if (this.enabled) {
105
+ if (this.isEnabled()) {
101
106
  this.disable(pi);
102
107
  return false;
103
108
  }
104
- this.enable(pi);
109
+ this.enable(pi, "auto");
105
110
  return true;
106
111
  }
107
112
 
108
- /** The thinking level Pi actually applied (`max`, or the model/runtime fallback). */
113
+ getMode(): UltracodeModeName {
114
+ return this.mode;
115
+ }
116
+
117
+ /** The thinking level Pi actually applied after model/runtime clamping. */
109
118
  getAppliedThinking(): ThinkingLevel | undefined {
110
119
  return this.appliedThinking;
111
120
  }
112
121
 
113
122
  /**
114
- * Return the raw maximum request, not the parent's applied value, so every
115
- * workflow subagent is clamped independently against its own model.
123
+ * Return the configured raw effort request so every workflow subagent is
124
+ * clamped independently against its own model.
116
125
  */
117
126
  getSubagentThinkingLevel(): ThinkingLevel | undefined {
118
- return this.isEnforcing() ? ULTRACODE_THINKING_LEVEL : undefined;
127
+ return this.isEnforcing() ? thinkingLevelForMode(this.mode) : undefined;
119
128
  }
120
129
 
121
- /** Reassert the maximum before a turn or after a model change. */
122
- reapplyMaximumThinking(pi: ExtensionAPI): boolean {
130
+ /** Reassert the configured mode effort before a turn or after a model change. */
131
+ reapplyConfiguredThinking(pi: ExtensionAPI): boolean {
123
132
  if (!this.isEnforcing()) return false;
124
- this.applyUltracodeThinking(pi);
133
+ this.applyConfiguredThinking(pi);
125
134
  return true;
126
135
  }
127
136
 
137
+ /** @deprecated Use reapplyConfiguredThinking(). */
138
+ reapplyMaximumThinking(pi: ExtensionAPI): boolean {
139
+ return this.reapplyConfiguredThinking(pi);
140
+ }
141
+
128
142
  /**
129
143
  * Handle model switches both while active and after a clamped restoration.
130
144
  * Returns true when Ultracode remains active and the UI should be refreshed.
@@ -132,8 +146,8 @@ export class UltracodeMode {
132
146
  handleModelSelect(pi: ExtensionAPI): boolean {
133
147
  if (this.suspended) return false;
134
148
  this.pendingClearGeneration++;
135
- if (this.enabled) {
136
- this.applyUltracodeThinking(pi);
149
+ if (this.isEnabled()) {
150
+ this.applyConfiguredThinking(pi);
137
151
  return true;
138
152
  }
139
153
  if (this.pendingPreviousThinking) {
@@ -147,7 +161,7 @@ export class UltracodeMode {
147
161
 
148
162
  /** Restore the pre-mode effective effort without changing persisted mode state. */
149
163
  restorePreviousThinking(pi: ExtensionAPI): void {
150
- if (this.enabled && this.previousThinking) this.applyCompatibleThinking(pi, this.previousThinking);
164
+ if (this.isEnabled() && this.previousThinking) this.applyCompatibleThinking(pi, this.previousThinking);
151
165
  }
152
166
 
153
167
  /** Stop enforcing synchronously, then restore effort before session teardown. */
@@ -162,34 +176,32 @@ export class UltracodeMode {
162
176
  }
163
177
 
164
178
  /**
165
- * Enforce the mode after an external thinking-level selection. Stale events
166
- * and events emitted by this mode are ignored to avoid recursive updates.
167
- * Returns true when a manual selection was overridden.
179
+ * Enforce the configured effort after an external thinking-level selection.
180
+ * Stale events and events emitted by this mode are ignored to avoid recursion.
168
181
  */
169
182
  handleThinkingLevelSelect(pi: ExtensionAPI, level: ThinkingLevel): boolean {
170
183
  if (this.suspended || this.applyingThinking) return false;
171
184
  const current = safeGetThinking(pi);
172
185
  if (!current || current !== level) return false;
173
- if (!this.enabled) {
186
+ if (!this.isEnabled()) {
174
187
  // Pi emits the same event for a user selection and an automatic model
175
188
  // re-clamp. Defer clearing until model_select has had a chance to consume it.
176
189
  if (this.pendingPreviousThinking) this.deferPendingClear(pi, level);
177
190
  return false;
178
191
  }
179
192
  if (current === this.appliedThinking) return false;
180
- this.applyUltracodeThinking(pi);
193
+ this.applyConfiguredThinking(pi);
181
194
  return true;
182
195
  }
183
196
 
184
197
  isEnabled(): boolean {
185
- return this.enabled;
198
+ return this.mode !== "off";
186
199
  }
187
200
 
188
201
  isSuspended(): boolean {
189
202
  return this.suspended;
190
203
  }
191
204
 
192
-
193
205
  /** Keep tool availability aligned with the current mode state. */
194
206
  syncWorkflowTool(pi: ExtensionAPI): void {
195
207
  if (this.isEnforcing()) this.activateWorkflowTool(pi);
@@ -200,12 +212,16 @@ export class UltracodeMode {
200
212
  return ULTRACODE_TAGLINE;
201
213
  }
202
214
 
203
- /** Turn ultracode on. Idempotent. */
204
- enable(pi: ExtensionAPI): void {
215
+ /**
216
+ * Enable or switch modes without replacing the saved baseline. The no-argument
217
+ * form retains the pre-0.5 programmatic deep behavior; user commands pass an
218
+ * explicit mode and bare `/ultracode` uses toggle() to enter auto.
219
+ */
220
+ enable(pi: ExtensionAPI, mode: ActiveUltracodeMode = "deep"): void {
205
221
  this.suspended = false;
206
222
  this.pendingPreviousThinking = undefined;
207
223
  this.pendingClearGeneration++;
208
- if (!this.enabled) {
224
+ if (!this.isEnabled()) {
209
225
  const current = safeGetThinking(pi);
210
226
  const preference = this.captureThinkingPreference();
211
227
  const effectivePreference = this.runtimeCompatibleThinking(preference.effective) as
@@ -222,16 +238,16 @@ export class UltracodeMode {
222
238
  : current;
223
239
  this.previousDefaultThinking = this.runtimeCompatibleThinking(preference.global);
224
240
  this.legacyDefaultMigrationPending = false;
225
- this.enabled = true;
226
241
  }
227
- this.applyUltracodeThinking(pi);
242
+ this.mode = mode;
243
+ this.applyConfiguredThinking(pi);
228
244
  this.syncWorkflowTool(pi);
229
245
  this.persist(pi);
230
246
  }
231
247
 
232
- /** Turn ultracode off, restoring the previous thinking level. */
248
+ /** Turn Ultracode off, restoring the pre-mode thinking level. */
233
249
  disable(pi: ExtensionAPI): void {
234
- if (!this.enabled) {
250
+ if (!this.isEnabled()) {
235
251
  this.syncWorkflowTool(pi);
236
252
  return;
237
253
  }
@@ -241,7 +257,7 @@ export class UltracodeMode {
241
257
  ? previous
242
258
  : undefined;
243
259
  this.pendingClearGeneration++;
244
- this.enabled = false;
260
+ this.mode = "off";
245
261
  this.suspended = false;
246
262
  this.syncWorkflowTool(pi);
247
263
  this.persist(pi);
@@ -286,7 +302,7 @@ export class UltracodeMode {
286
302
  ?? (wasEnforcing ? this.previousThinking : undefined)
287
303
  ?? effectivePreference,
288
304
  ) as ThinkingLevel | undefined;
289
- this.enabled = false;
305
+ this.mode = "off";
290
306
  this.suspended = false;
291
307
  this.pendingPreviousThinking = undefined;
292
308
  this.previousThinking = target;
@@ -306,7 +322,7 @@ export class UltracodeMode {
306
322
  }
307
323
 
308
324
  this.suspended = false;
309
- this.enabled = latest.enabled;
325
+ this.mode = latest.mode;
310
326
  this.syncWorkflowTool(pi);
311
327
  const maxIsUnknownToRuntime = !this.runtimeSupportsMaxThinking
312
328
  && preference.effective === ULTRACODE_THINKING_LEVEL;
@@ -319,7 +335,7 @@ export class UltracodeMode {
319
335
  // Pre-preference-store releases persisted only previousThinking while their
320
336
  // active xhigh request polluted Pi's global default. Recover that baseline
321
337
  // once instead of treating the known Ultracode value as a user preference.
322
- const migratesLegacyDefault = latest.enabled
338
+ const migratesLegacyDefault = latest.mode !== "off"
323
339
  && latest.previousDefaultThinking === undefined
324
340
  && latest.previousThinking !== undefined
325
341
  && (preference.global === LEGACY_ULTRACODE_THINKING_LEVEL
@@ -331,9 +347,9 @@ export class UltracodeMode {
331
347
  : this.runtimeCompatibleThinking(latest.previousDefaultThinking);
332
348
  this.legacyDefaultMigrationPending = migratesLegacyDefault;
333
349
 
334
- this.pendingPreviousThinking = this.enabled ? undefined : latest.pendingPreviousThinking;
335
- if (this.enabled) {
336
- this.applyUltracodeThinking(pi);
350
+ this.pendingPreviousThinking = this.isEnabled() ? undefined : latest.pendingPreviousThinking;
351
+ if (this.isEnabled()) {
352
+ this.applyConfiguredThinking(pi);
337
353
  if (migratesLegacyDefault) {
338
354
  this.queueDefaultThinkingRestore(() => {
339
355
  this.legacyDefaultMigrationPending = false;
@@ -374,30 +390,29 @@ export class UltracodeMode {
374
390
  }
375
391
  }
376
392
 
377
- /**
378
- * Build the before_agent_start result: appends the ultracode system block to the
379
- * turn's system prompt when enabled.
380
- */
393
+ /** Append the configured semantic-depth policy to the turn's system prompt. */
381
394
  beforeAgentStart(event: { systemPrompt: string }): { systemPrompt: string } | undefined {
382
- if (!this.isEnforcing()) return undefined;
383
- const block = ultracodeSystemBlock();
395
+ if (!this.isEnforcing() || !isActiveUltracodeMode(this.mode)) return undefined;
396
+ const block = ultracodeSystemBlock(this.mode);
384
397
  return { systemPrompt: `${event.systemPrompt}\n\n${block}\n\n${ULTRACODE_ACTIVE_REMINDER}` };
385
398
  }
386
399
 
387
400
  statusLine(): string {
388
- if (!this.enabled) return `ultracode: off`;
389
- const parts = ["ultracode: on"];
401
+ if (!this.isEnabled()) return "ultracode: off";
402
+ const parts = [`ultracode: ${this.mode}`];
390
403
  // Show the level that actually applied, including compatibility/model fallback.
391
404
  if (this.appliedThinking) parts.push(this.appliedThinking);
392
405
  return parts.join(" · ");
393
406
  }
394
407
 
395
- private applyUltracodeThinking(pi: ExtensionAPI): void {
408
+ private applyConfiguredThinking(pi: ExtensionAPI): void {
409
+ const target = thinkingLevelForMode(this.mode);
410
+ if (!target) return;
396
411
  const writeGeneration = this.preferenceWriteGeneration;
397
- this.applyCompatibleThinking(pi, ULTRACODE_THINKING_LEVEL);
412
+ this.applyCompatibleThinking(pi, target);
398
413
  // Pi normally skips persistence when the effective level is unchanged, but
399
414
  // the extension API does not promise that. Defensively restore the raw
400
- // baseline even after a stable max -> max request.
415
+ // baseline even after a stable mode-owned request.
401
416
  if (writeGeneration === this.preferenceWriteGeneration) {
402
417
  this.queueDefaultThinkingRestore();
403
418
  }
@@ -433,7 +448,7 @@ export class UltracodeMode {
433
448
  }
434
449
 
435
450
  isEnforcing(): boolean {
436
- return this.enabled && !this.suspended;
451
+ return this.isEnabled() && !this.suspended;
437
452
  }
438
453
 
439
454
  private pendingRestoreSucceeded(pending: ThinkingLevel): boolean {
@@ -495,7 +510,7 @@ export class UltracodeMode {
495
510
  setImmediate(() => {
496
511
  if (
497
512
  generation !== this.pendingClearGeneration
498
- || this.enabled
513
+ || this.isEnabled()
499
514
  || this.suspended
500
515
  || safeGetThinking(pi) !== level
501
516
  ) return;
@@ -528,7 +543,7 @@ export class UltracodeMode {
528
543
 
529
544
  private persist(pi: ExtensionAPI): void {
530
545
  const state: PersistedModeState = {
531
- enabled: this.enabled,
546
+ mode: this.mode,
532
547
  previousThinking: this.previousThinking,
533
548
  previousDefaultThinking: this.legacyDefaultMigrationPending
534
549
  ? undefined
@@ -547,8 +562,17 @@ function parsePersistedModeState(data: unknown): PersistedModeState | undefined
547
562
  if (!data || typeof data !== "object") return undefined;
548
563
  const value = data as Record<string, unknown>;
549
564
  const previousDefault = value.previousDefaultThinking;
565
+ // Sessions written before semantic-depth modes stored only enabled:boolean.
566
+ // Preserve their behavior by migrating enabled:true to the old deep mode.
567
+ const mode: UltracodeModeName = isActiveUltracodeMode(value.mode)
568
+ ? value.mode
569
+ : value.mode === "off"
570
+ ? "off"
571
+ : value.enabled === true
572
+ ? "deep"
573
+ : "off";
550
574
  return {
551
- enabled: value.enabled === true,
575
+ mode,
552
576
  previousThinking: isThinkingLevel(value.previousThinking) ? value.previousThinking : undefined,
553
577
  previousDefaultThinking: previousDefault === null || isThinkingLevel(previousDefault)
554
578
  ? previousDefault
package/src/prompts.ts CHANGED
@@ -1,59 +1,92 @@
1
- /**
2
- * Prompt text for ultracode mode.
3
- *
4
- * These strings reproduce the behavioural contract of Claude Code's "ultracode"
5
- * effort level: a standing opt-in to deterministic multi-agent workflow
6
- * orchestration, biased toward the most exhaustive, correct answer.
7
- */
1
+ /** Prompt text for Ultracode's adaptive analysis-depth modes. */
2
+
3
+ import type { ActiveUltracodeMode } from "./depth.ts";
8
4
 
9
5
  /** One-line description shown by `/ultracode status` and the footer. */
10
- export const ULTRACODE_TAGLINE = "max thinking + dynamic workflow orchestration";
6
+ export const ULTRACODE_TAGLINE = "semantic-depth workflow orchestration";
11
7
 
12
8
  /**
13
- * The standing system-prompt block injected on every turn while ultracode is on.
14
- * Mirrors the "Ultracode" section of the Workflow tool contract.
9
+ * The standing system-prompt block injected on every turn while Ultracode is
10
+ * active. The parent model performs the semantic routing itself; starting a
11
+ * separate agent merely to classify depth would defeat the focused path.
15
12
  */
16
- export function ultracodeSystemBlock(): string {
13
+ export function ultracodeSystemBlock(mode: ActiveUltracodeMode = "deep"): string {
17
14
  return [
18
15
  "<ultracode>",
19
- "Ultracode is ON. This opt-in is standing: author and run a workflow for every substantive task by default.",
16
+ `Configured mode: ${mode}.`,
17
+ "Analysis depth is a semantic quality decision, never a wall-clock decision. Do not use elapsed time, deadlines, or duration limits to choose, lower, or stop analysis depth.",
18
+ "Use the smallest depth that can establish a correct answer. Depth is controlled by research rounds, independent perspectives, verification strength, evidence requirements, skeptic count, and per-agent reasoning effort.",
19
+ "Existing maxAgents/reserveAgents limits are structural admission bounds, not evidence that every available slot should be used.",
20
20
  "",
21
- "The goal is a bounded, exhaustive-enough, correct answer: choose explicit fan-out and round limits before launching work.",
22
- "For multi-phase work (understand → design → implement → review), prefer one bounded workflow and inspect the result before deciding whether another workflow is justified. Do not run consecutive workflows by default; continue only when new evidence changes the plan.",
21
+ ...modeInstructions(mode),
23
22
  "",
24
- "Lean toward orchestrating with the workflow tool and adversarially verifying your findings, unless the work is trivial or already verified. Solo (no workflow) only on conversational turns or trivial mechanical edits.",
23
+ "Evidence-driven escalation and stopping:",
24
+ "- Escalate only for material correctness risk, missing direct evidence, conflicting findings, or an unresolved question that can change the answer.",
25
+ "- Treat security, GC, ABI, deoptimization, concurrency, data-loss, irreversible operations, and critical architecture semantics as high-risk unless bounded evidence proves otherwise.",
26
+ "- Stop when key claims have direct evidence, no material conflict or unresolved high-risk question remains, and another round would only repeat known evidence.",
27
+ "- Model-reported confidence alone is not sufficient. Prefer concrete citations, reproduction, tests, and independent agreement.",
28
+ "- If fixed focused/standard is insufficient, report the remaining uncertainty and recommend a deeper mode; in deep, report any irreducible uncertainty. Never silently exceed a fixed mode.",
25
29
  "",
26
- "Quality patterns to compose as the task calls for it:",
27
- "- Adversarial verify: spawn N independent skeptics per finding, each prompted to REFUTE it; kill the finding if a majority refute. Stops plausible-but-wrong findings from surviving.",
28
- "- Perspective-diverse verify: when a finding can fail in more than one way, give each verifier a distinct lens (correctness, security, performance, does-it-reproduce) instead of N identical refuters.",
29
- "- Multi-modal sweep: parallel agents each searching a different way (by-container, by-content, by-entity, by-time); each is blind to what the others surface.",
30
- "- Loop-until-dry: for unknown-size discovery (bugs, edge cases), run at most 2 rounds by default (3 only when the user explicitly asks for comprehensive coverage); stop sooner when no fresh evidence appears.",
31
- "- Completeness critic: a final agent that asks \"what's missing a modality not run, a claim unverified, a source unread?\" What it finds becomes the next round of work.",
32
- "- No silent caps: if a workflow bounds coverage (top-N, no-retry, sampling), log() what was dropped.",
33
- "",
34
- "Scale to the task: \"find any bugs\" → a few finders, single-vote verify; \"thoroughly audit\" / \"be comprehensive\" → a larger but explicit finder pool, 3–5 vote adversarial pass, and a synthesis stage within maxAgents.",
30
+ "Workflow policy:",
31
+ "- Use a workflow only when independent decomposition, verification, isolation, or context scale provides real value. Otherwise solve directly in the parent session.",
32
+ "- Make skeptics conditional: verify high-risk, conflicting, weakly evidenced, or low-confidence claims instead of automatically verifying every branch.",
33
+ "- Avoid a separate synthesis agent when deterministic merging or parent synthesis is enough. Use an adjudicator only when a material conflict remains.",
34
+ "- Match effort to the stage with per-call model suffixes when useful: focused discovery/synthesis may use :medium, standard analysis :high, and :max is reserved for deep or decisive high-risk verification.",
35
+ "- When a workflow runs, log `analysis-depth: <level><reason>` before launching agents, `analysis-escalation: ...` for each semantic escalation, and `analysis-stop: ...` for the final evidence-based stop reason. Never use time as an escalation or stop reason.",
35
36
  "</ultracode>",
36
37
  ].join("\n");
37
38
  }
38
39
 
39
- /** Short reminder appended to confirm the mode is active (system-reminder style). */
40
+ function modeInstructions(mode: ActiveUltracodeMode): string[] {
41
+ switch (mode) {
42
+ case "auto":
43
+ return [
44
+ "Before acting, silently route this task to focused, standard, or deep. Do not spawn a router agent.",
45
+ "Choose from user intent, consequence risk, scope, ambiguity, available evidence, cross-module or cross-repository breadth, and whether independent verification is necessary.",
46
+ "Begin at the shallowest sufficient depth and escalate only when evidence triggers an escalation condition below.",
47
+ ];
48
+ case "focused":
49
+ return [
50
+ "Focused is a fixed lightweight depth: prefer one bounded line of inquiry and the normal parent-agent loop.",
51
+ "Do not run adversarial verification by default. Use at most a small, narrowly scoped delegation only when it clearly reduces duplicated exploration.",
52
+ ];
53
+ case "standard":
54
+ return [
55
+ "Standard is a fixed balanced depth: cover the few independent dimensions that can materially change the answer.",
56
+ "Prefer one discovery round and conditional verification of only disputed, high-risk, or weakly evidenced claims.",
57
+ ];
58
+ case "deep":
59
+ return [
60
+ "Deep is a fixed high-assurance depth: default to a bounded workflow for substantive tasks unless the work is conversational, trivial, or already verified.",
61
+ "Use multi-perspective investigation and adversarial verification where the task supports it. Choose fan-out and round limits before launch.",
62
+ "Use at most two discovery rounds by default; add a third only when the user explicitly requests exhaustive coverage and fresh evidence is still appearing.",
63
+ ];
64
+ }
65
+ }
66
+
67
+ /** Short reminder appended after the standing block. */
40
68
  export const ULTRACODE_ACTIVE_REMINDER =
41
- "Reminder: ultracode is ondefault to authoring and running a workflow for substantive tasks, and adversarially verify your findings.";
69
+ "Reminder: Ultracode is activeobey its configured semantic depth, escalate only from evidence, and never use elapsed time as an analysis budget.";
42
70
 
43
71
  /** Tool description for the `workflow` tool. */
44
72
  export const WORKFLOW_TOOL_DESCRIPTION = [
45
- "Execute a deterministic JavaScript workflow that orchestrates multiple subagents to be comprehensive (decompose and cover in parallel), confident (independent perspectives and adversarial checks before committing), or to take on scale one context can't hold (migrations, audits, broad sweeps).",
73
+ "Execute a deterministic JavaScript workflow that orchestrates a proportionate set of subagents for independent coverage, targeted verification, or work that exceeds one context.",
74
+ "Choose the smallest workflow justified by the configured Ultracode depth and current evidence; workflow use and adversarial checks are not automatic.",
46
75
  "Each invocation must provide workflow source via `script`, `scriptPath`, or `name`. Inline `script` must be raw JavaScript (no Markdown fences) beginning with `export const meta = { name, description }` (a pure literal) and should call agent() at least once for useful orchestration.",
47
- "Available globals: agent(prompt, opts), parallel(thunks, options?), pipeline(items, ...stages), phase(title), log(message), workflow(name, args), args, cwd. The tool accepts maxAgents (default 128, max 1024) as a lifetime live-agent admission cap across resumes, not a token budget; cache replay is free.",
76
+ "Available globals: agent(prompt, opts), parallel(thunks, options?), pipeline(items, ...stages), phase(title), log(message), workflow(name, args), args, cwd. The tool accepts maxAgents (default 128, max 1024) as a lifetime live-agent admission cap across resumes, not a token or depth budget; cache replay is free.",
48
77
  ].join(" ");
49
78
 
50
79
  /** One-line snippet for the Available tools section. */
51
80
  export const WORKFLOW_PROMPT_SNIPPET =
52
- "Run a deterministic JS workflow that fans out subagents. Header: export const meta = { name: 'snake_case', description: '...' }. Globals: agent/parallel/pipeline/phase/log/workflow/args/cwd.";
81
+ "Run a deterministic JS workflow with proportionate subagent fan-out. Header: export const meta = { name: 'snake_case', description: '...' }. Globals: agent/parallel/pipeline/phase/log/workflow/args/cwd.";
53
82
 
54
83
  /** Guideline bullets appended to the Guidelines section when the tool is active. */
55
84
  export const WORKFLOW_GUIDELINES: string[] = [
56
- "While Ultracode is enabled, use the workflow tool to decompose-and-cover in parallel, gather independent perspectives that adversarially verify each other, or take on scale one context can't hold.",
85
+ "Use the workflow tool only when independent decomposition, targeted verification, isolation, or context scale adds value; use the parent agent directly for a bounded task.",
86
+ "For an auto-depth workflow, log `analysis-depth: <level> — <reason>` before launching agents, `analysis-escalation: ...` when evidence requires more depth, and `analysis-stop: ...` when evidence is sufficient. Elapsed time must never determine depth.",
87
+ "For workflow verification, run skeptics only for high-risk, conflicting, low-confidence, or weakly evidenced claims. Do not automatically attach a skeptic to every branch.",
88
+ "For workflow synthesis, prefer structured results plus deterministic or parent-session merging. Start a synthesis/adjudication agent only for a material unresolved conflict, and normally give pure synthesis lower effort and no broad source-search mandate.",
89
+ "For workflow agent effort, use a model suffix such as `:medium`, `:high`, or `:max` when the stage should differ from the mode default; reserve max for deep investigation or decisive high-risk verification.",
57
90
  "For the workflow tool, provide workflow source with `script`, `scriptPath`, or `name`. Inline `script` must be one raw JavaScript string: no Markdown fences, no prose around the script.",
58
91
  "For the workflow tool, the script's first statement must be `export const meta = { name: 'short_snake_case', description: 'non-empty human description' }`. meta must be a pure literal: no variables, function calls, spreads, or template interpolation. meta.phases is optional and should mirror your phase() titles.",
59
92
  "For the workflow tool, write plain JavaScript after the meta export. No TypeScript syntax, imports, require(), fs, network, Date/Intl/Temporal, Math.random(), binary memory constructors, WebAssembly, or dynamic method calls such as value[key](...) (they bypass deterministic checks or resource bounds). Stamp timestamps after the workflow returns; vary randomness by agent index.",
@@ -66,7 +99,7 @@ export const WORKFLOW_GUIDELINES: string[] = [
66
99
  "For the workflow tool, agent opts also accept: model (override the subagent model by pattern), agentType (use a custom subagent role/system-prompt), isolation:'worktree' (run the agent in an isolated git worktree — use ONLY when agents mutate files in parallel and would conflict), and phase (assign the agent to a progress group explicitly inside parallel()/pipeline()).",
67
100
  "For the workflow tool, workflow(name, args) runs a trust-aware saved workflow by name as a sub-step, sharing this run's concurrency, maxAgents, active reservations, and agent counter. Explicit nested script paths are not supported. Nesting is one level only. Child subagent sessions do not load ambient extensions or expose workflow/subagent orchestration tools, so they cannot start an independent recursive workflow chain.",
68
101
  "For workflow resumeFromRunId, reuse the exact same script and args. Resume is immutable, successful calls replay by stable structural call path, and maxAgents may only stay the same or increase; changed work must start a new run.",
69
- "For the workflow tool, failed agent()/parallel()/pipeline() branches return null and log the failure (unless the whole run is aborted or a policy limit is hit). Check for nulls before synthesizing conclusions, and prefer a final synthesis/assertion agent that returns a compact JSON-serializable verdict.",
102
+ "For the workflow tool, failed agent()/parallel()/pipeline() branches return null and log the failure (unless the whole run is aborted or a policy limit is hit). Check for nulls before synthesizing conclusions, and prefer a final compact JSON-serializable result.",
70
103
  "For the workflow tool, directly await or return every orchestration promise (agent/parallel/pipeline/workflow). Native .then/.catch/.finally chains and Promise.all/allSettled/race/any are rejected; use parallel() or pipeline() so call identity stays deterministic. Unobserved, pending, or native same-scope concurrent orchestration is fatal.",
71
104
  "Workflow helpers that call orchestration must be directly declared functions/function variables or static methods on a declared object/stored class instance. Do not alias them, assign them later, forward through this.otherMethod(), or call them from temporary/awaited factory receivers; rewrite those forms as a direct declared helper so resume identity remains stable.",
72
105
  "For the workflow tool, do not assume subagents share the parent's repository context; include enough task context and relevant file paths in each agent prompt.",
package/src/thinking.ts CHANGED
@@ -4,7 +4,7 @@ export const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhig
4
4
 
5
5
  export type ThinkingLevel = (typeof THINKING_LEVELS)[number];
6
6
 
7
- /** Ultracode always asks Pi for the strongest effort the selected model supports. */
7
+ /** Deep mode asks Pi for the strongest effort the selected model supports. */
8
8
  export const ULTRACODE_THINKING_LEVEL: ThinkingLevel = "max";
9
9
 
10
10
  /** Compatibility retry for Pi versions released before the `max` level existed. */
@@ -93,9 +93,8 @@ export interface WorkflowToolDeps {
93
93
  registry?: WorkflowRegistry;
94
94
  /** Canonical runtime supplied by an SDK host; shared by all child sessions. */
95
95
  modelRuntime?: ModelRuntimeLike;
96
- /** The ultracode effort level to forward to every workflow subagent as its
97
- * default thinking level (`max` when ultracode is on, so each subagent's own
98
- * session clamps it independently; undefined when off). A per-call
96
+ /** The configured Ultracode mode's raw default effort for workflow subagents.
97
+ * Each child session clamps it independently; undefined when off. A per-call
99
98
  * `model: "X:level"` suffix or agentType `thinking:` override still wins. */
100
99
  getThinkingLevel?: () => ThinkingLevel | undefined;
101
100
  /** Optional execution gate for mode-scoped registrations. Omit for standalone use. */
@@ -132,7 +131,7 @@ export function createWorkflowTool(deps: WorkflowToolDeps = {}): ToolDefinition<
132
131
  parameters: workflowToolSchema,
133
132
  async execute(_toolCallId, params, signal, onUpdate, ctx) {
134
133
  if (deps.isExecutionAllowed?.() === false) {
135
- throw new Error("The workflow tool is disabled. Run /ultracode on before using it.");
134
+ throw new Error("The workflow tool is disabled. Run /ultracode or select an Ultracode depth before using it.");
136
135
  }
137
136
  const controller = new AbortController();
138
137
  let abortRequested = false;
@@ -164,8 +163,8 @@ export function createWorkflowTool(deps: WorkflowToolDeps = {}): ToolDefinition<
164
163
  if (resuming && !RunJournal.exists(runsDir, runId)) {
165
164
  throw new Error(`workflow: resumeFromRunId ${runId} was not found in this session`);
166
165
  }
167
- // Forward the raw `max` request so each subagent session clamps it against
168
- // that subagent's model. Undefined when ultracode is off.
166
+ // Forward the mode's raw effort request so each subagent session clamps it
167
+ // against that subagent's model. Undefined when Ultracode is off.
169
168
  const thinkingLevel = deps.getThinkingLevel?.();
170
169
  if (controller.signal.aborted) throw new Error("Workflow was aborted before it started");
171
170
  const run = deps.runWorkflowFn ?? runWorkflow;