pi-ultracode 0.10.3 → 0.11.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.
Files changed (38) hide show
  1. package/README.md +105 -44
  2. package/README.zh-CN.md +101 -47
  3. package/examples/workflows/loop-until-dry-bugs.workflow.js +69 -45
  4. package/extensions/ultracode.ts +63 -42
  5. package/package.json +38 -24
  6. package/src/commands.ts +266 -93
  7. package/src/depth.ts +6 -3
  8. package/src/effort-policy.ts +9 -5
  9. package/src/index.ts +15 -3
  10. package/src/jev.ts +271 -69
  11. package/src/mode.ts +120 -192
  12. package/src/model-selector.ts +209 -0
  13. package/src/preferences.ts +60 -18
  14. package/src/prompts.ts +18 -9
  15. package/src/selector-backend.ts +26 -0
  16. package/src/workflow/admission.ts +29 -8
  17. package/src/workflow/agent-runner.ts +903 -627
  18. package/src/workflow/agent-types.ts +8 -3
  19. package/src/workflow/display-text.ts +77 -34
  20. package/src/workflow/display.ts +67 -31
  21. package/src/workflow/effort-context.ts +15 -9
  22. package/src/workflow/journal.ts +235 -140
  23. package/src/workflow/json-schema.ts +14 -8
  24. package/src/workflow/parser.ts +420 -223
  25. package/src/workflow/registry.ts +2 -1
  26. package/src/workflow/repository-context.ts +3 -1
  27. package/src/workflow/run-artifacts.ts +19 -6
  28. package/src/workflow/run-details.ts +250 -123
  29. package/src/workflow/runtime.ts +1425 -928
  30. package/src/workflow/saved-workflow.ts +1 -4
  31. package/src/workflow/script-executor.ts +403 -235
  32. package/src/workflow/script-worker.mjs +208 -114
  33. package/src/workflow/structured-output.ts +17 -9
  34. package/src/workflow/tool.ts +876 -534
  35. package/src/workflow/value-limits.ts +72 -24
  36. package/src/workflow/workflow-overlay.ts +475 -324
  37. package/src/workflow/worktree.ts +348 -251
  38. package/types/workflow.d.ts +12 -3
package/README.md CHANGED
@@ -57,18 +57,18 @@ Pi decides whether a workflow is useful. Small tasks can still use the normal si
57
57
 
58
58
  Useful commands:
59
59
 
60
- | Command | Action |
61
- | --- | --- |
62
- | `/ultracode` | Enable `auto` from off; disable any active mode |
63
- | `/ultracode auto` | Let the parent choose depth using the task and context |
64
- | `/ultracode focused` | Use fixed depth for bounded work with direct local verification |
65
- | `/ultracode standard` | Use fixed depth for independent dimensions or competing hypotheses, with targeted verification |
66
- | `/ultracode deep` | Use fixed depth for interacting constraints or critical invariants, with independent evidence and counterexample checks |
67
- | `/ultracode off` | Disable it without changing the parent effort |
68
- | `/ultracode status` | Show the configured mode and its applicability / verification requirements |
69
- | `/workflows` or `F6` | Open the workflow browser |
70
- | `/workflows <runId>` | Open a specific run |
71
- | `/workflows abort` | Abort active runs |
60
+ | Command | Action |
61
+ | --------------------- | ----------------------------------------------------------------------------------------------------------------------- |
62
+ | `/ultracode` | Enable `auto` from off; disable any active mode |
63
+ | `/ultracode auto` | Let the parent choose depth using the task and context |
64
+ | `/ultracode focused` | Use fixed depth for bounded work with direct local verification |
65
+ | `/ultracode standard` | Use fixed depth for independent dimensions or competing hypotheses, with targeted verification |
66
+ | `/ultracode deep` | Use fixed depth for interacting constraints or critical invariants, with independent evidence and counterexample checks |
67
+ | `/ultracode off` | Disable it without changing the parent effort |
68
+ | `/ultracode status` | Show the configured mode and its applicability / verification requirements |
69
+ | `/workflows` or `F6` | Open the workflow browser |
70
+ | `/workflows <runId>` | Open a specific run |
71
+ | `/workflows abort` | Abort active runs |
72
72
 
73
73
  Press `Esc` to cancel a running workflow. In Pi's fullscreen TUI, use `Ctrl+PageUp`, `Ctrl+PageDown`, and `Ctrl+End` inside workflow details.
74
74
 
@@ -76,12 +76,12 @@ Press `Esc` to cancel a running workflow. In Pi's fullscreen TUI, use `Ctrl+Page
76
76
 
77
77
  Depth defines **what must be investigated and verified**, not model effort or a quota of agents and rounds:
78
78
 
79
- | Depth | Applicable conditions | Required verification |
80
- |---|---|---|
81
- | **Focused** | Bounded work with a known approach and locally verifiable results; e.g. a specified validation rule | Directly check the changed behavior or key claim, usually in the parent loop. No broad investigation or adversarial panel by default; verification is not skipped. |
82
- | **Standard** | Multiple independent dimensions or unresolved hypotheses can materially change the answer; e.g. a fault with several plausible causes | Investigate those dimensions or compare causes, then target disputed or weakly evidenced claims. Known multi-step work alone is not enough to require standard. |
83
- | **Deep** | Tightly interacting hard constraints or critical invariants need independent evidence and counterexamples; e.g. cancellation racing a durable commit | Trace interactions, test boundary/failure cases, and challenge key claims with a distinct test, reproduction, proof, or source. A second agent is neither necessary nor sufficient. |
84
- | **Auto** | Choose the smallest depth sufficient for the current task | Apply the selected depth's evidence requirements; escalate only when material new evidence or unresolved conflicts justify it. |
79
+ | Depth | Applicable conditions | Required verification |
80
+ | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
81
+ | **Focused** | Bounded work with a known approach and locally verifiable results; e.g. a specified validation rule | Directly check the changed behavior or key claim, usually in the parent loop. No broad investigation or adversarial panel by default; verification is not skipped. |
82
+ | **Standard** | Multiple independent dimensions or unresolved hypotheses can materially change the answer; e.g. a fault with several plausible causes | Investigate those dimensions or compare causes, then target disputed or weakly evidenced claims. Known multi-step work alone is not enough to require standard. |
83
+ | **Deep** | Tightly interacting hard constraints or critical invariants need independent evidence and counterexamples; e.g. cancellation racing a durable commit | Trace interactions, test boundary/failure cases, and challenge key claims with a distinct test, reproduction, proof, or source. A second agent is neither necessary nor sufficient. |
84
+ | **Auto** | Choose the smallest depth sufficient for the current task | Apply the selected depth's evidence requirements; escalate only when material new evidence or unresolved conflicts justify it. |
85
85
 
86
86
  Risk determines **which claims need verification**. Security, concurrency, data loss, file count, or a request for review alone never mandates `deep`; missing information does not prove hidden complexity. Deep does not automatically mean a workflow, more agents, or repeated rounds. Explicit `focused` / `standard` / `deep` modes remain fixed: if insufficient, report the uncertainty and recommend a deeper mode rather than silently exceeding the boundary.
87
87
 
@@ -105,30 +105,41 @@ Before each parent run, the extension supplies a local Pi capability snapshot: t
105
105
 
106
106
  The snapshot describes local registered capabilities, not a live provider probe, and may become stale during a run. Runtime clamping remains a safety net for explicit requests or stale metadata. Standalone SDK use of `createWorkflowTool()` without the extension does not inject this section; the host must supply capability context to enable informed automatic suffix selection. Neither the snapshot nor its absence rewrites explicit user effort choices or no-suffix defaults.
107
107
 
108
- | Suffix | Criteria and exclusions | Example |
109
- |---|---|---|
110
- | `:off` | Fully specified mechanical work; no locating changes, interpreting meaning, or hidden-impact reasoning | Fix a typo at an exact location using a supplied replacement |
111
- | `:minimal` | One clear local judgment; no cross-file tracing or multi-step implementation | Explain a short self-contained expression |
112
- | `:low` | Small implementation or question with a known approach and straightforward checks; not an unknown-root-cause fault | Add a specified validation rule and its test |
113
- | `:medium` | Bounded multi-step work coordinating known constraints; not interacting unresolved hypotheses | Wire a configuration option through loading, usage, and tests |
114
- | `:high` | Test competing hypotheses, trace cross-module effects, or reason about critical correctness constraints; security/architecture keywords alone do not qualify | Diagnose cross-module state desynchronization by comparing plausible causes |
115
- | `:xhigh` | Interacting hard problems requiring comparison of approaches and validation of cross-module invariants; file count or vague complexity claims do not qualify | Investigate an intermittent lifecycle, cache, and cancellation interaction |
116
- | `:max` | Exceptionally difficult synthesis requiring rigorous argument and adversarial validation, or concrete evidence of insufficient lower-effort reasoning; risk keywords alone do not qualify | Prove and repair system-wide concurrency, memory-safety, and low-level invariants |
108
+ | Suffix | Criteria and exclusions | Example |
109
+ | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
110
+ | `:off` | Fully specified mechanical work; no locating changes, interpreting meaning, or hidden-impact reasoning | Fix a typo at an exact location using a supplied replacement |
111
+ | `:minimal` | One clear local judgment; no cross-file tracing or multi-step implementation | Explain a short self-contained expression |
112
+ | `:low` | Small implementation or question with a known approach and straightforward checks; not an unknown-root-cause fault | Add a specified validation rule and its test |
113
+ | `:medium` | Bounded multi-step work coordinating known constraints; not interacting unresolved hypotheses | Wire a configuration option through loading, usage, and tests |
114
+ | `:high` | Test competing hypotheses, trace cross-module effects, or reason about critical correctness constraints; security/architecture keywords alone do not qualify | Diagnose cross-module state desynchronization by comparing plausible causes |
115
+ | `:xhigh` | Interacting hard problems requiring comparison of approaches and validation of cross-module invariants; file count or vague complexity claims do not qualify | Investigate an intermittent lifecycle, cache, and cancellation interaction |
116
+ | `:max` | Exceptionally difficult synthesis requiring rigorous argument and adversarial validation, or concrete evidence of insufficient lower-effort reasoning; risk keywords alone do not qualify | Prove and repair system-wide concurrency, memory-safety, and low-level invariants |
117
117
 
118
118
  Implementation, review, input length, file count, risk keywords, and depth mode are not sufficient reasons for high effort. Missing information does not automatically imply `medium/high`; do not invent complexity. For `high/xhigh/max`, briefly identify the concrete reasoning difficulty. The highest supported effort requires neither a failed lower-level attempt nor an available intermediate level. Risk determines what needs verification, not a uniform high effort for every child.
119
119
 
120
120
  These are task-selection heuristics, not a universal provider capability scale. The workflow UI reports actual effort; an omitted suffix retains normal child user/model configuration unless Jev overrides it. The system prompt, workflow guidelines, and Jev share the criteria in `src/effort-policy.ts`; none changes the parent's effort. A separate skeptic or synthesis agent is not automatic.
121
121
 
122
- #### Optional Jev selection
122
+ #### Configurable effort selector
123
123
 
124
- Jev selection runs automatically when Pi’s native `getAvailableOfType("classifier", "typesafe")` includes `typesafe/jev-latest`. This checks Pi’s credential configuration, not merely catalog presence or remote service health. If Jev is unavailable, no classification request is made and existing parent-selected suffixes and user/model defaults remain unchanged.
124
+ Run `/ultracode model` to choose the model that judges child effort. The searchable picker has **chat model** and **classifier** tabs; use Tab to switch, Enter to save, and Esc to cancel. This changes only the effort judge, leaving parent and child execution models unchanged.
125
125
 
126
- - Ready subtasks in one scheduler batch share **one** Pi-native `modelRuntime` / `modelRegistry.classify()` request to `typesafe/jev-latest`, with one `questions` entry per eligible task. Each question maps to that task’s full prompt, **actual child session model**, and supported effort levels. Answers are applied independently; a successful selection changes neither the execution model nor the parent session or global defaults.
127
- - Selection reuses `src/effort-policy.ts` and judges each task separately, not the complexity of the whole workflow. Shared background contains the existing workflow `meta` (including name/description and any supplied goal, acceptance criteria, or dependencies) and `args`. Per-task context includes phase, workflow path, known parallel branch/pipeline item-stage structure, and nested workflow metadata/arguments when applicable. Full task prompts include roles, additional instructions, labels, output requirements, and any upstream results already supplied by the script. Missing goals or dependency edges are not invented; no summarization model call, additional repository read, full script, or complete parent conversation is sent. By default, requests go to `https://api.typesafe.ai` through Pi’s provider configuration. Configure Jev in Pi only if these prompts, metadata, and arguments may be shared with TypeSafe; available credentials automatically enable selection.
126
+ Specify a model directly; Ultracode detects its type from Pi's available models:
127
+
128
+ ```text
129
+ /ultracode model openai/gpt-5.4-mini
130
+ /ultracode model typesafe/jev-latest
131
+ ```
132
+
133
+ If the same model supports both types, a dialog asks which type to use. Without UI, ambiguous choices leave the saved preference unchanged; use the TUI picker to select a type.
134
+
135
+ The choice is saved globally in `~/.pi/agent/ultracode.json` (under `PI_CODING_AGENT_DIR` when configured) and read at the start of each workflow run. Existing runs retain their selector. The default remains the `typesafe/jev-latest` classifier. Pi checks credentials through `getAvailableOfType()`; unavailable selectors preserve parent-selected suffixes and user/model defaults.
136
+
137
+ - Ready subtasks in one scheduler batch share **one** Pi-native request to the selected backend (`classify()` for classifiers or `streamSimple(...).result()` with validated JSON answers for chat models), with one `questions` entry per eligible task. Each question maps to that task’s full prompt, **actual child session model**, and supported effort levels. Answers are applied independently; a successful selection changes neither the execution model nor the parent session or global defaults.
138
+ - Selection reuses `src/effort-policy.ts` and judges each task separately, not the complexity of the whole workflow. Shared background contains the existing workflow `meta` (including name/description and any supplied goal, acceptance criteria, or dependencies) and `args`. Per-task context includes phase, workflow path, known parallel branch/pipeline item-stage structure, and nested workflow metadata/arguments when applicable. Full task prompts include roles, additional instructions, labels, output requirements, and any upstream results already supplied by the script. Missing goals or dependency edges are not invented; no summarization model call, additional repository read, full script, or complete parent conversation is sent. By default, requests go to `https://api.typesafe.ai` through Pi’s provider configuration. A custom selector receives the same data through its configured Pi provider. Choose a selector only if these prompts, metadata, and arguments may be shared with that provider; available credentials automatically enable selection.
128
139
  - Pi resolves credentials at request time from its runtime credentials, stored credentials, provider configuration, or normal environment sources (including `TYPESAFE_API_KEY`). Ultracode does not read that variable as a switch or pass an explicit `apiKey`, and does not change Pi’s stored credentials. No alternate provider or model is queried.
129
140
  - Requests have a **10-second timeout and no retries**. An invalid or unsupported answer exposed by Pi retains only that task’s already-resolved parent selection/default. A request-level error retains every task’s own default. **Pi 0.99.1 limitation:** its TypeSafe adapter discards all answers when any wire answer is missing or malformed; those cases necessarily fall back for the whole batch. Valid wire answers with unsupported choices still fall back independently. **Fallback never makes another model request.**
130
141
  - User cancellation aborts selection and terminates the subtasks, including those queued for execution, rather than executing fallback tasks. Cached resume results, models supporting only one level, and unknown actual models are excluded from classification; an entirely excluded batch makes no request.
131
- - The UI reports actual effort. The selector does not log keys, raw responses, or service error bodies. Jev selection usage is not included in child execution usage.
142
+ - The UI reports actual effort. The selector does not log keys, raw responses, or service error bodies. Selector request usage is not included in child execution usage.
132
143
 
133
144
  **Batch boundary:** `parallel()` and `pipeline()` collect calls actually issued while synchronously launching their thunks/items, then seal that batch. No debounce, elapsed-time heuristic, full-script pre-evaluation, or waiting for future tasks is used. Calls after an `await`, nested workflow/panel loads, and later pipeline stages form new batches as they become ready (often a single task); a slow sibling does not block an already-ready later stage. A later explicit fan-out is batched again. This is not one request for an arbitrary lazy JavaScript workflow.
134
145
 
@@ -145,14 +156,14 @@ export const meta = {
145
156
  };
146
157
 
147
158
  const findings = await parallel([
148
- () => agent(
149
- "Review the current branch for correctness and lifecycle bugs.",
150
- { label: "correctness review" },
151
- ),
152
- () => agent(
153
- "Review the current branch for missing or weak regression tests.",
154
- { label: "test review" },
155
- ),
159
+ () =>
160
+ agent("Review the current branch for correctness and lifecycle bugs.", {
161
+ label: "correctness review",
162
+ }),
163
+ () =>
164
+ agent("Review the current branch for missing or weak regression tests.", {
165
+ label: "test review",
166
+ }),
156
167
  ]);
157
168
 
158
169
  return findings.filter(Boolean);
@@ -210,13 +221,63 @@ Token and cost data are reported for observability, not enforced as a budget. Wo
210
221
 
211
222
  ## Development
212
223
 
224
+ Development uses Node.js 24.21.0 (`.node-version`) and Vite+ 1.0.0. The
225
+ `packageManager` field pins npm 11.19.0 and selects the installer used by
226
+ `vp install`, `vp add`, and `vp remove`.
227
+
228
+ With the global Vite+ CLI installed:
229
+
213
230
  ```bash
214
- npm ci
215
- npm run typecheck
216
- npm test
217
- npm pack --dry-run
231
+ vp install --frozen-lockfile --ignore-scripts
232
+ vp run check
233
+ vp run typecheck
234
+ vp run test
235
+ vp pm pack -- --dry-run
218
236
  ```
219
237
 
238
+ Without the global CLI, run `npm ci --ignore-scripts`, then `npm run check`,
239
+ `npm run typecheck`, and `npm test`. These scripts use the pinned local `vp`;
240
+ release CI follows this path and preserves npm trusted publishing and version gates.
241
+
242
+ `vp test` invokes the built-in test runner once; use `vp test watch` for watch mode.
243
+ `vp run test` executes this package's `test` script, which uses `vp test run` for a
244
+ single run. `vp dev` starts Vite's development server; this source-distributed
245
+ extension has no web development server or `dev` task.
246
+
247
+ ### Static checks
248
+
249
+ `vp check` and `vp run check` run the same complete formatting, lint, and type
250
+ checks. The `check` script calls `vp check` without bypass flags or path filters,
251
+ and release CI uses this same script. Lint warnings remain visible for review.
252
+ Use `vp fmt <files>` to format changed files.
253
+
254
+ `vp run typecheck` runs only TypeScript (`vp exec tsc --noEmit`) against
255
+ `tsconfig.json`. It is a focused development command; the complete `check` task
256
+ remains the CI gate.
257
+
258
+ Saved JavaScript workflows in `examples/` run in the workflow sandbox. The example
259
+ uses a JSDoc type-only import from `pi-ultracode/workflow` to load the ambient
260
+ workflow declarations in both the checkout and an installed package, without
261
+ adding a runtime import or a checkout-only alias.
262
+
263
+ Tests retain isolated fork workers, automatic mock restoration, and the previous
264
+ runner's unlimited default test timeout.
265
+
266
+ ### Package validation
267
+
268
+ The published package intentionally contains `extensions/`, `src/`, `types/`,
269
+ examples, and documentation. Pi loads the TypeScript entry directly, and
270
+ `src/workflow/script-executor.ts` locates its sibling `script-worker.mjs`.
271
+ `npm pack --dry-run` checks the actual publication contents; an extracted tarball
272
+ should also resolve `/// <reference types="pi-ultracode/workflow" />` from a
273
+ consumer TypeScript project.
274
+
275
+ `vp pack` is a tsdown bundle builder, not a replacement for `npm pack` here. An
276
+ optional smoke check such as
277
+ `vp pack --no-config src/depth.ts --out-dir /tmp/pi-ultracode-pack-smoke --no-dts`
278
+ only tests the bundler. It does not validate the extension's release artifact.
279
+ There is intentionally no generated `dist/`, build step, or rewritten export map.
280
+
220
281
  ## License
221
282
 
222
283
  MIT
package/README.zh-CN.md CHANGED
@@ -57,21 +57,21 @@ Pi 会自行判断工作流是否有帮助。小任务仍可继续使用普通
57
57
 
58
58
  常用命令:
59
59
 
60
- | 命令 | 作用 |
61
- | --- | --- |
62
- | `/ultracode` | 关闭时开启 `auto`;任一模式已开启时关闭 |
63
- | `/ultracode auto` | 始终由父代理结合任务和上下文选择深度 |
64
- | `/ultracode focused` | 固定处理方法已知、范围明确且能局部直接验证的工作 |
65
- | `/ultracode standard` | 固定调查独立维度或竞争假设,并做针对性验证 |
66
- | `/ultracode deep` | 固定检查相互作用的约束或关键不变量,要求独立证据和反例检验 |
67
- | `/ultracode off` | 关闭且不改变父代理 effort |
68
- | `/ultracode status` | 查看配置模式及其适用条件、验证要求 |
69
- | `/ultracode default on` | 全局默认开启,并立即开启当前会话;已开启时保留当前深度 |
70
- | `/ultracode default off` | 取消默认开启 |
71
- | `/ultracode default` | 查看全局启动默认值 |
72
- | `/workflows` 或 `F6` | 打开工作流浏览器 |
73
- | `/workflows <runId>` | 打开指定运行 |
74
- | `/workflows abort` | 中止活动运行 |
60
+ | 命令 | 作用 |
61
+ | ------------------------ | ---------------------------------------------------------- |
62
+ | `/ultracode` | 关闭时开启 `auto`;任一模式已开启时关闭 |
63
+ | `/ultracode auto` | 始终由父代理结合任务和上下文选择深度 |
64
+ | `/ultracode focused` | 固定处理方法已知、范围明确且能局部直接验证的工作 |
65
+ | `/ultracode standard` | 固定调查独立维度或竞争假设,并做针对性验证 |
66
+ | `/ultracode deep` | 固定检查相互作用的约束或关键不变量,要求独立证据和反例检验 |
67
+ | `/ultracode off` | 关闭且不改变父代理 effort |
68
+ | `/ultracode status` | 查看配置模式及其适用条件、验证要求 |
69
+ | `/ultracode default on` | 全局默认开启,并立即开启当前会话;已开启时保留当前深度 |
70
+ | `/ultracode default off` | 取消默认开启 |
71
+ | `/ultracode default` | 查看全局启动默认值 |
72
+ | `/workflows` 或 `F6` | 打开工作流浏览器 |
73
+ | `/workflows <runId>` | 打开指定运行 |
74
+ | `/workflows abort` | 中止活动运行 |
75
75
 
76
76
  `/ultracode default on` 保存全局启动默认值,并立即将关闭的当前会话切换为 `auto`;当前已开启时保留所选深度。`/ultracode default off` 只取消默认开启,不关闭当前会话;关闭当前会话请用 `/ultracode off`。这些操作均不改变 effort。设置保存在 `~/.pi/agent/ultracode.json`(遵循 `PI_CODING_AGENT_DIR`),跨项目和重启生效;未设置时默认关闭。启动时,只有当前分支没有保存 Ultracode 模式才应用默认值,已有的 `off` 或深度选择在 reload、resume、fork 后继续保留。显式 `pi --ultracode` 仍可启用 `auto`。
77
77
 
@@ -81,12 +81,12 @@ Pi 会自行判断工作流是否有帮助。小任务仍可继续使用普通
81
81
 
82
82
  深度决定**要调查和验证什么**,不是模型 effort,也不是代理数量或调查轮数配额:
83
83
 
84
- | 深度 | 适用条件 | 验证要求 |
85
- |---|---|---|
86
- | **Focused** | 范围明确、方法已知、结果可局部验证;例如添加指定校验规则 | 直接检查修改行为或关键结论,通常使用父代理循环。默认不做广泛调查或对抗评审,但不能省略验证。 |
87
- | **Standard** | 多个独立维度或未解决的竞争假设会实质影响答案;例如故障存在多个合理根因 | 调查相关维度或比较根因,再针对冲突、证据薄弱的结论验证。仅仅步骤多且方法已知,不足以要求 standard。 |
88
- | **Deep** | 紧密相互作用的硬约束或关键不变量,需要独立证据与反例检验;例如取消与持久提交发生竞争 | 追踪相互影响,检查边界及失败路径,用不同测试、复现、证明或来源挑战关键结论。第二个代理既非必要条件,也不保证证据独立。 |
89
- | **Auto** | 选择足以处理当前任务的最小深度 | 遵守所选深度的证据要求;仅在实质新证据或未解决冲突要求时升级。 |
84
+ | 深度 | 适用条件 | 验证要求 |
85
+ | ------------ | ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------- |
86
+ | **Focused** | 范围明确、方法已知、结果可局部验证;例如添加指定校验规则 | 直接检查修改行为或关键结论,通常使用父代理循环。默认不做广泛调查或对抗评审,但不能省略验证。 |
87
+ | **Standard** | 多个独立维度或未解决的竞争假设会实质影响答案;例如故障存在多个合理根因 | 调查相关维度或比较根因,再针对冲突、证据薄弱的结论验证。仅仅步骤多且方法已知,不足以要求 standard。 |
88
+ | **Deep** | 紧密相互作用的硬约束或关键不变量,需要独立证据与反例检验;例如取消与持久提交发生竞争 | 追踪相互影响,检查边界及失败路径,用不同测试、复现、证明或来源挑战关键结论。第二个代理既非必要条件,也不保证证据独立。 |
89
+ | **Auto** | 选择足以处理当前任务的最小深度 | 遵守所选深度的证据要求;仅在实质新证据或未解决冲突要求时升级。 |
90
90
 
91
91
  风险决定**哪些结论需要验证**。安全、并发、数据丢失等关键词、文件数量或“做一次 review”本身不能自动触发 `deep`;信息不足也不证明存在隐藏复杂度。Deep 不自动意味着必须使用工作流、增加代理或反复调查。显式 `focused` / `standard` / `deep` 保持固定:若不足以确认结论,应说明不确定性并建议更深模式,不能悄悄越界。
92
92
 
@@ -108,30 +108,41 @@ Ultracode 不会修改父会话的 effort;以下父代理选择作为默认和
108
108
 
109
109
  快照来自本地注册信息,不会在线探测提供商,且运行期间可能过时;执行端仍用 clamp 保护显式请求或过时信息。SDK 单独使用 `createWorkflowTool()` 而不加载扩展时,不会注入此区块,宿主需自行提供能力上下文才能让父代理据此自动选择后缀。快照存在与否,都不会改写用户显式选择的 effort 或无后缀默认行为。
110
110
 
111
- | 后缀 | 适用条件与排除条件 | 例子 |
112
- |---|---|---|
113
- | `:off` | 完全指定的机械操作;不涉及寻找修改位置、判断含义或隐藏影响 | 在指定位置按给定内容修正错字 |
114
- | `:minimal` | 单一范围内的一次明确局部判断;不涉及跨文件追踪或多步骤实现 | 解释一个自包含短表达式 |
115
- | `:low` | 目标清楚、方法已知、验证直接的小实现或问题;不包含根因未知的故障 | 给已有函数添加指定校验规则及测试 |
116
- | `:medium` | 协调多个已知约束的有界多步骤实现或调查;不包含相互影响的未解决假设 | 在既有架构中贯通配置加载、使用和测试 |
117
- | `:high` | 检验多个合理假设、追踪跨模块影响,或推理关键正确性约束;不能只凭安全或架构关键词升级 | 比较竞争根因,诊断跨模块状态不同步 |
118
- | `:xhigh` | 多个困难问题相互作用,需要比较方案、验证跨模块不变量;文件多或笼统声称复杂不够 | 排查生命周期、缓存、取消相互影响的偶发故障 |
119
- | `:max` | 极难的综合推理,需要严密论证及反例检验,或已有低档推理不足的具体证据;风险关键词本身不够 | 证明并修复涉及并发、内存安全和底层语义的全系统不变量 |
111
+ | 后缀 | 适用条件与排除条件 | 例子 |
112
+ | ---------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------- |
113
+ | `:off` | 完全指定的机械操作;不涉及寻找修改位置、判断含义或隐藏影响 | 在指定位置按给定内容修正错字 |
114
+ | `:minimal` | 单一范围内的一次明确局部判断;不涉及跨文件追踪或多步骤实现 | 解释一个自包含短表达式 |
115
+ | `:low` | 目标清楚、方法已知、验证直接的小实现或问题;不包含根因未知的故障 | 给已有函数添加指定校验规则及测试 |
116
+ | `:medium` | 协调多个已知约束的有界多步骤实现或调查;不包含相互影响的未解决假设 | 在既有架构中贯通配置加载、使用和测试 |
117
+ | `:high` | 检验多个合理假设、追踪跨模块影响,或推理关键正确性约束;不能只凭安全或架构关键词升级 | 比较竞争根因,诊断跨模块状态不同步 |
118
+ | `:xhigh` | 多个困难问题相互作用,需要比较方案、验证跨模块不变量;文件多或笼统声称复杂不够 | 排查生命周期、缓存、取消相互影响的偶发故障 |
119
+ | `:max` | 极难的综合推理,需要严密论证及反例检验,或已有低档推理不足的具体证据;风险关键词本身不够 | 证明并修复涉及并发、内存安全和底层语义的全系统不变量 |
120
120
 
121
121
  写代码、审查、文本长度、文件数量、风险关键词及 depth 模式都不能单独决定高档;信息不足不自动意味着 `medium/high`,也不能臆造复杂度。选择 `high/xhigh/max` 时须简述具体推理难点。最高档不要求先经历低档失败,也不要求模型支持中间档。风险决定需要验证什么,不等于所有参与代理都要高 effort。
122
122
 
123
123
  这些分档是任务选择标准,不是跨提供商统一的能力刻度。Workflow UI 展示实际生效的 effort;未指定后缀且 Jev 未覆盖时,子会话使用正常的用户/模型配置。系统提示词、workflow 指南和 Jev 共用 `src/effort-policy.ts` 的分档标准,均不改变父会话 effort。skeptic 和独立 synthesis agent 都不是默认步骤。
124
124
 
125
- #### 可选 Jev 自动选择
125
+ #### 配置 effort 选择模型
126
126
 
127
- 当 Pi 原生 `getAvailableOfType("classifier", "typesafe")` 返回的可用模型中包含 `typesafe/jev-latest` 时,自动进行 Jev 选择。该接口检查 Pi 的凭证配置,不只是模型目录中是否存在,也不保证远端服务正常。Jev 不可用时不发分类请求,完全保留现有父代理后缀及用户/模型默认行为。
127
+ 运行 `/ultracode model` 打开可搜索的选择器,支持 **chat model** 和 **classifier**(分类模型)两个标签页。按 Tab 切换,Enter 保存,Esc 取消。所选模型只负责判断子代理 effort,父代理和子代理的执行模型保持原状。
128
128
 
129
- - 同一调度批次中的已就绪子任务共用**一次** Pi 原生 `modelRuntime` / `modelRegistry.classify()` 请求,每个符合条件的任务在 `questions` 中有独立问题,映射到其完整提示词、**实际子会话模型**及支持档位。回答独立应用;成功选择只覆盖对应子任务的 effort,不更换执行模型,不修改父会话或全局默认值。
130
- - 选择标准复用 `src/effort-policy.ts`,各任务按自身复杂度判断,不因整个工作流复杂而统一提高档位。共享背景包含现有工作流 `meta`(名称、描述,以及实际提供的总目标、验收或依赖信息)和 `args`;每项任务附带阶段、工作流路径、已知的并行分支/流水线项目与阶段结构,嵌套工作流还包含自身元数据和参数。完整提示词包含角色、额外指令、标签、输出要求及脚本已经传入的上游结果。不编造缺失目标或依赖关系,不额外调用模型总结,不额外读取仓库,不发送完整脚本或父会话历史。请求通过 Pi 的提供商配置发送,默认地址为 `https://api.typesafe.ai`。在 Pi 中配置 Jev 前,请确认这些提示词、元数据和参数可以发送给 TypeSafe;可用凭证会自动启用选择。
129
+ 可直接指定模型,由 Ultracode 根据 Pi 的可用模型自动识别类型:
130
+
131
+ ```text
132
+ /ultracode model openai/gpt-5.4-mini
133
+ /ultracode model typesafe/jev-latest
134
+ ```
135
+
136
+ 同一模型同时支持两种类型时,会弹出类型选择框。无 UI 时遇到此情况保留原配置,请通过 TUI 选择器指定类型。
137
+
138
+ 选择保存到全局 `~/.pi/agent/ultracode.json`(配置 `PI_CODING_AGENT_DIR` 时使用该目录),每次 workflow 启动时读取,已有运行继续使用原来的选择器。默认使用 `typesafe/jev-latest` 分类模型。Pi 通过 `getAvailableOfType()` 检查凭证;模型不可用时保留父代理后缀及用户/模型默认值。
139
+
140
+ - 同一调度批次中的已就绪子任务共用**一次** Pi 原生请求:分类模型使用 `classify()`,聊天模型使用 `streamSimple(...).result()` 并校验 JSON 答案,每个符合条件的任务在 `questions` 中有独立问题,映射到其完整提示词、**实际子会话模型**及支持档位。回答独立应用;成功选择只覆盖对应子任务的 effort,不更换执行模型,不修改父会话或全局默认值。
141
+ - 选择标准复用 `src/effort-policy.ts`,各任务按自身复杂度判断,不因整个工作流复杂而统一提高档位。共享背景包含现有工作流 `meta`(名称、描述,以及实际提供的总目标、验收或依赖信息)和 `args`;每项任务附带阶段、工作流路径、已知的并行分支/流水线项目与阶段结构,嵌套工作流还包含自身元数据和参数。完整提示词包含角色、额外指令、标签、输出要求及脚本已经传入的上游结果。不编造缺失目标或依赖关系,不额外调用模型总结,不额外读取仓库,不发送完整脚本或父会话历史。请求通过 Pi 的提供商配置发送,默认地址为 `https://api.typesafe.ai`。自定义选择器通过其 Pi 提供商配置接收同样的数据。选择前请确认这些提示词、元数据和参数可以发送给对应提供商;可用凭证会自动启用选择。
131
142
  - 凭证由 Pi 在请求时解析,可来自运行时凭证、已保存凭证、提供商配置或标准环境变量来源(包括 `TYPESAFE_API_KEY`)。Ultracode 不读取该变量作为开关,也不显式传入 `apiKey`,不修改 Pi 已保存的凭证,不请求其他提供商或模型。
132
143
  - 请求超时为 **10 秒,不重试**。Pi 暴露的单项答案无效或档位不受支持时,只保留对应任务原来的父代理选择/默认值;请求级错误则各自保留整批任务的原值。**Pi 0.99.1 限制:**TypeSafe 适配器遇到任一缺失或格式损坏的网络答案,会丢弃整批答案,这种情况只能整批回退;格式有效但档位不受支持的答案仍可独立回退。**回退不会额外请求模型重新判断**。
133
144
  - 用户取消会中止选择并终止子任务,包括等待执行名额的任务,不会执行回退任务。恢复缓存命中、仅支持一个档位或无法确定实际模型的任务均不参与分类;整批都无需分类时不发请求。
134
- - UI 展示实际生效的 effort。选择器不记录密钥、原始响应或服务错误正文;Jev 的选择请求用量不计入子代理执行用量。
145
+ - UI 展示实际生效的 effort。选择器不记录密钥、原始响应或服务错误正文;选择器的请求用量不计入子代理执行用量。
135
146
 
136
147
  **批次边界:**`parallel()` 和 `pipeline()` 收集同步启动各分支/项目时实际发出的任务,然后立即封闭批次。不使用防抖计时、不预执行完整脚本、不等待未来任务。`await` 之后、嵌套工作流/并行面板加载之后,以及流水线后续阶段产生的任务,就绪后进入新批次(常为单任务);不会为了等待慢分支而阻塞已就绪的后续阶段。后续显式并行启动仍会再次合批。因此,任意惰性 JavaScript 工作流不能保证全程只发一次分类请求。
137
148
 
@@ -148,14 +159,14 @@ export const meta = {
148
159
  };
149
160
 
150
161
  const findings = await parallel([
151
- () => agent(
152
- "Review the current branch for correctness and lifecycle bugs.",
153
- { label: "correctness review" },
154
- ),
155
- () => agent(
156
- "Review the current branch for missing or weak regression tests.",
157
- { label: "test review" },
158
- ),
162
+ () =>
163
+ agent("Review the current branch for correctness and lifecycle bugs.", {
164
+ label: "correctness review",
165
+ }),
166
+ () =>
167
+ agent("Review the current branch for missing or weak regression tests.", {
168
+ label: "test review",
169
+ }),
159
170
  ]);
160
171
 
161
172
  return findings.filter(Boolean);
@@ -213,13 +224,56 @@ Token 和成本只用于可观测性,不作为执行预算。Worker 与 VM 限
213
224
 
214
225
  ## 开发
215
226
 
227
+ 开发使用 Node.js 24.21.0(`.node-version`)和 Vite+ 1.0.0。
228
+ `packageManager` 固定 npm 11.19.0,并决定 `vp install`、`vp add`、`vp remove`
229
+ 使用的包管理器。
230
+
231
+ 已安装全局 Vite+ CLI 时:
232
+
216
233
  ```bash
217
- npm ci
218
- npm run typecheck
219
- npm test
220
- npm pack --dry-run
234
+ vp install --frozen-lockfile --ignore-scripts
235
+ vp run check
236
+ vp run typecheck
237
+ vp run test
238
+ vp pm pack -- --dry-run
221
239
  ```
222
240
 
241
+ 也可以先运行 `npm ci --ignore-scripts`,再运行 `npm run check`、
242
+ `npm run typecheck` 和 `npm test`。这些脚本使用固定版本的本地 `vp`;
243
+ 发布 CI 使用此方式,并保留 npm 可信发布与版本校验。
244
+
245
+ `vp test` 调用内置测试运行器并执行一次;使用 `vp test watch` 监听文件变化。
246
+ `vp run test` 执行本项目的 `test` 脚本,通过 `vp test run` 单次运行测试。
247
+ `vp dev` 启动 Vite 开发服务器;本项目按源码发布扩展,未配置网页开发服务器或 `dev` 任务。
248
+
249
+ ### 静态检查
250
+
251
+ `vp check` 和 `vp run check` 执行相同的完整格式、lint 和类型检查。
252
+ `check` 脚本直接调用 `vp check`,不添加跳过参数或路径过滤;发布 CI 使用同一脚本。
253
+ Lint 警告保持可见,供后续审查。使用 `vp fmt <files>` 格式化修改的文件。
254
+
255
+ `vp run typecheck` 仅通过 `vp exec tsc --noEmit` 检查 `tsconfig.json` 中的 TypeScript。
256
+ 此命令用于开发时的定向检查,CI 仍执行完整 `check` 任务。
257
+
258
+ `examples/` 中的 JavaScript 工作流在工作流沙箱中运行。示例使用 JSDoc 类型导入引用
259
+ `pi-ultracode/workflow`,在源码目录和已安装包中加载全局工作流声明,保持运行时零导入,
260
+ 无需添加源码目录专用别名。
261
+
262
+ 测试保留隔离的 fork worker、自动恢复 mock,以及原测试运行器默认无超时限制的行为。
263
+
264
+ ### 发布包验证
265
+
266
+ 发布包保留 `extensions/`、`src/`、`types/`、示例和文档。Pi 直接加载 TypeScript
267
+ 入口,`src/workflow/script-executor.ts` 按相邻路径定位 `script-worker.mjs`。
268
+ `npm pack --dry-run` 检查实际发布内容;还应解压 tarball,在消费方 TypeScript
269
+ 项目中验证 `/// <reference types="pi-ultracode/workflow" />` 的类型解析。
270
+
271
+ `vp pack` 使用 tsdown 生成 bundle,与本项目的 `npm pack` 发布流程用途不同。
272
+ 可选冒烟命令
273
+ `vp pack --no-config src/depth.ts --out-dir /tmp/pi-ultracode-pack-smoke --no-dts`
274
+ 仅验证打包器,扩展发布产物仍需上述检查。项目保持源码布局,无需生成 `dist/`、
275
+ 增加构建步骤或改写导出映射。
276
+
223
277
  ## 许可证
224
278
 
225
279
  MIT
@@ -1,4 +1,4 @@
1
- /// <reference types="pi-ultracode/workflow" />
1
+ /** @import {} from "pi-ultracode/workflow" */
2
2
  //
3
3
  // Example saved workflow. Copy to `.pi/ultracode/workflows/` and run it with
4
4
  // the `workflow` tool's `name` parameter, or call `workflow('loop-until-dry-bugs')`
@@ -9,76 +9,100 @@
9
9
  // confirms each fresh bug with three distinct lenses.
10
10
 
11
11
  export const meta = {
12
- name: 'loop-until-dry-bugs',
13
- description: 'Find bugs until the well runs dry, verifying each with three lenses',
14
- phases: [{ title: 'Find' }, { title: 'Verify' }],
15
- }
12
+ name: "loop-until-dry-bugs",
13
+ description: "Find bugs until the well runs dry, verifying each with three lenses",
14
+ phases: [{ title: "Find" }, { title: "Verify" }],
15
+ };
16
16
 
17
17
  const FINDERS = [
18
- 'Find correctness bugs by reading the changed files closely.',
19
- 'Find bugs by tracing data flow across module boundaries.',
20
- 'Find bugs by hunting error-handling and edge-case gaps.',
21
- ]
18
+ "Find correctness bugs by reading the changed files closely.",
19
+ "Find bugs by tracing data flow across module boundaries.",
20
+ "Find bugs by hunting error-handling and edge-case gaps.",
21
+ ];
22
22
 
23
23
  const BUGS = {
24
- type: 'object',
24
+ type: "object",
25
25
  properties: {
26
26
  bugs: {
27
- type: 'array',
27
+ type: "array",
28
28
  items: {
29
- type: 'object',
30
- properties: { file: { type: 'string' }, line: { type: 'number' }, desc: { type: 'string' } },
31
- required: ['file', 'desc'],
29
+ type: "object",
30
+ properties: {
31
+ file: { type: "string" },
32
+ line: { type: "number" },
33
+ desc: { type: "string" },
34
+ },
35
+ required: ["file", "desc"],
32
36
  },
33
37
  },
34
38
  },
35
- required: ['bugs'],
36
- }
39
+ required: ["bugs"],
40
+ };
37
41
 
38
42
  const VERDICT = {
39
- type: 'object',
40
- properties: { real: { type: 'boolean' }, why: { type: 'string' } },
41
- required: ['real'],
42
- }
43
+ type: "object",
44
+ properties: { real: { type: "boolean" }, why: { type: "string" } },
45
+ required: ["real"],
46
+ };
43
47
 
44
- const seen = new Set()
45
- const confirmed = []
46
- let dry = 0
47
- let roundsRun = 0
48
- const MAX_ROUNDS = 2
48
+ const seen = new Set();
49
+ const confirmed = [];
50
+ let dry = 0;
51
+ let roundsRun = 0;
52
+ const MAX_ROUNDS = 2;
49
53
 
50
54
  while (dry < 2 && roundsRun < MAX_ROUNDS) {
51
- roundsRun++
55
+ roundsRun++;
52
56
  const rounds = await parallel(
53
- FINDERS.map((prompt, i) => () => agent(prompt, { label: 'find ' + i, phase: 'Find', schema: BUGS })),
54
- )
55
- const found = rounds.filter(Boolean).flatMap((r) => r.bugs ?? [])
56
- const fresh = found.filter((b) => !seen.has(b.file + ':' + b.desc))
57
+ FINDERS.map(
58
+ (prompt, i) => () => agent(prompt, { label: "find " + i, phase: "Find", schema: BUGS }),
59
+ ),
60
+ );
61
+ const found = rounds.filter(Boolean).flatMap((r) => r.bugs ?? []);
62
+ const fresh = found.filter((b) => !seen.has(b.file + ":" + b.desc));
57
63
  if (fresh.length === 0) {
58
- dry++
59
- continue
64
+ dry++;
65
+ continue;
60
66
  }
61
- dry = 0
62
- for (const b of fresh) seen.add(b.file + ':' + b.desc)
67
+ dry = 0;
68
+ for (const b of fresh) seen.add(b.file + ":" + b.desc);
63
69
 
64
70
  const judged = await parallel(
65
71
  fresh.map((b) => async () => {
66
72
  const votes = await parallel(
67
- ['correctness', 'security', 'does-it-reproduce'].map((lens) => () =>
68
- agent('Judge via the ' + lens + ' lens — is this real? "' + b.desc + '" (' + b.file + '). Default to real:false if unsure.',
69
- { label: 'verify ' + lens, phase: 'Verify', agentType: 'code-reviewer', schema: VERDICT }),
73
+ ["correctness", "security", "does-it-reproduce"].map(
74
+ (lens) => () =>
75
+ agent(
76
+ "Judge via the " +
77
+ lens +
78
+ ' lens — is this real? "' +
79
+ b.desc +
80
+ '" (' +
81
+ b.file +
82
+ "). Default to real:false if unsure.",
83
+ {
84
+ label: "verify " + lens,
85
+ phase: "Verify",
86
+ agentType: "code-reviewer",
87
+ schema: VERDICT,
88
+ },
89
+ ),
70
90
  ),
71
- )
72
- return { bug: b, real: votes.filter(Boolean).filter((v) => v.real).length >= 2 }
91
+ );
92
+ return { bug: b, real: votes.filter(Boolean).filter((v) => v.real).length >= 2 };
73
93
  }),
74
- )
75
- confirmed.push(...judged.filter((j) => j.real).map((j) => j.bug))
94
+ );
95
+ confirmed.push(...judged.filter((j) => j.real).map((j) => j.bug));
76
96
  }
77
97
 
78
- const complete = dry >= 2
79
- const truncated = !complete
98
+ const complete = dry >= 2;
99
+ const truncated = !complete;
80
100
  if (truncated) {
81
- log('Stopped after MAX_ROUNDS=' + MAX_ROUNDS + ' before reaching two consecutive dry rounds; returning truncated results.')
101
+ log(
102
+ "Stopped after MAX_ROUNDS=" +
103
+ MAX_ROUNDS +
104
+ " before reaching two consecutive dry rounds; returning truncated results.",
105
+ );
82
106
  }
83
107
 
84
- return { confirmed, totalSeen: seen.size, roundsRun, complete, truncated }
108
+ return { confirmed, totalSeen: seen.size, roundsRun, complete, truncated };