@oh-my-pi/pi-coding-agent 15.7.5 → 15.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +159 -182
- package/dist/types/async/job-manager.d.ts +3 -3
- package/dist/types/cli/args.d.ts +1 -0
- package/dist/types/cli/claude-trace-cli.d.ts +54 -0
- package/dist/types/cli/session-picker.d.ts +10 -3
- package/dist/types/cli/update-cli.d.ts +17 -0
- package/dist/types/commands/launch.d.ts +3 -0
- package/dist/types/config/keybindings.d.ts +5 -0
- package/dist/types/config/settings-schema.d.ts +11 -2
- package/dist/types/config/settings.d.ts +13 -0
- package/dist/types/eval/__tests__/heartbeat.test.d.ts +1 -0
- package/dist/types/eval/concurrency-bridge.d.ts +26 -0
- package/dist/types/eval/heartbeat.d.ts +45 -0
- package/dist/types/eval/js/tool-bridge.d.ts +2 -1
- package/dist/types/extensibility/extensions/loader.d.ts +2 -2
- package/dist/types/extensibility/extensions/runner.d.ts +2 -1
- package/dist/types/extensibility/extensions/types.d.ts +24 -5
- package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +11 -0
- package/dist/types/lsp/diagnostics-ledger.d.ts +10 -0
- package/dist/types/lsp/index.d.ts +2 -0
- package/dist/types/lsp/utils.d.ts +4 -0
- package/dist/types/main.d.ts +5 -0
- package/dist/types/modes/acp/acp-agent.d.ts +1 -1
- package/dist/types/modes/components/assistant-message.d.ts +3 -1
- package/dist/types/modes/components/custom-editor.d.ts +3 -2
- package/dist/types/modes/components/hook-selector.d.ts +10 -1
- package/dist/types/modes/components/session-selector.d.ts +32 -5
- package/dist/types/modes/components/tool-execution.d.ts +8 -0
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +3 -3
- package/dist/types/modes/controllers/input-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +10 -3
- package/dist/types/modes/rpc/rpc-mode.d.ts +1 -1
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +5 -3
- package/dist/types/registry/agent-registry.d.ts +1 -1
- package/dist/types/sdk.d.ts +2 -2
- package/dist/types/session/agent-session.d.ts +12 -3
- package/dist/types/session/history-storage.d.ts +16 -1
- package/dist/types/session/session-manager.d.ts +4 -0
- package/dist/types/task/output-manager.d.ts +6 -15
- package/dist/types/tools/ask.d.ts +8 -6
- package/dist/types/tools/eval-backends.d.ts +12 -0
- package/dist/types/tools/eval-render.d.ts +52 -0
- package/dist/types/tools/eval.d.ts +2 -35
- package/dist/types/tools/find.d.ts +0 -9
- package/dist/types/tools/index.d.ts +5 -12
- package/dist/types/tools/path-utils.d.ts +16 -0
- package/dist/types/tools/sqlite-reader.d.ts +25 -8
- package/dist/types/tui/output-block.d.ts +4 -3
- package/dist/types/utils/clipboard.d.ts +4 -0
- package/dist/types/web/kagi.d.ts +76 -0
- package/dist/types/web/search/providers/exa.d.ts +7 -1
- package/dist/types/web/search/providers/kagi.d.ts +1 -0
- package/examples/extensions/README.md +1 -0
- package/examples/extensions/thinking-note.ts +13 -0
- package/package.json +9 -9
- package/src/async/job-manager.ts +3 -3
- package/src/cli/args.ts +6 -2
- package/src/cli/claude-trace-cli.ts +783 -0
- package/src/cli/session-picker.ts +36 -10
- package/src/cli/update-cli.ts +35 -2
- package/src/commands/launch.ts +3 -0
- package/src/config/keybindings.ts +6 -0
- package/src/config/model-registry.ts +33 -4
- package/src/config/settings-schema.ts +12 -2
- package/src/config/settings.ts +23 -0
- package/src/discovery/claude-plugins.ts +7 -9
- package/src/discovery/claude.ts +41 -22
- package/src/edit/index.ts +23 -3
- package/src/eval/__tests__/agent-bridge.test.ts +148 -4
- package/src/eval/__tests__/heartbeat.test.ts +84 -0
- package/src/eval/__tests__/llm-bridge.test.ts +30 -0
- package/src/eval/agent-bridge.ts +44 -38
- package/src/eval/concurrency-bridge.ts +34 -0
- package/src/eval/heartbeat.ts +74 -0
- package/src/eval/js/executor.ts +13 -9
- package/src/eval/js/shared/prelude.txt +20 -17
- package/src/eval/js/tool-bridge.ts +5 -0
- package/src/eval/llm-bridge.ts +20 -14
- package/src/eval/py/executor.ts +14 -18
- package/src/eval/py/prelude.py +23 -15
- package/src/exec/bash-executor.ts +31 -5
- package/src/extensibility/extensions/loader.ts +16 -18
- package/src/extensibility/extensions/runner.ts +22 -17
- package/src/extensibility/extensions/types.ts +39 -5
- package/src/extensibility/plugins/legacy-pi-compat.ts +115 -131
- package/src/extensibility/skills.ts +0 -1
- package/src/internal-urls/docs-index.generated.ts +14 -13
- package/src/lsp/diagnostics-ledger.ts +51 -0
- package/src/lsp/index.ts +9 -22
- package/src/lsp/utils.ts +21 -0
- package/src/main.ts +92 -24
- package/src/modes/acp/acp-agent.ts +8 -4
- package/src/modes/acp/acp-event-mapper.ts +54 -4
- package/src/modes/components/assistant-message.ts +28 -1
- package/src/modes/components/custom-editor.ts +19 -7
- package/src/modes/components/hook-selector.ts +229 -44
- package/src/modes/components/oauth-selector.ts +12 -6
- package/src/modes/components/session-selector.ts +179 -24
- package/src/modes/components/tool-execution.ts +36 -7
- package/src/modes/controllers/command-controller.ts +2 -11
- package/src/modes/controllers/event-controller.ts +5 -2
- package/src/modes/controllers/extension-ui-controller.ts +6 -4
- package/src/modes/controllers/input-controller.ts +19 -16
- package/src/modes/controllers/selector-controller.ts +61 -21
- package/src/modes/interactive-mode.ts +127 -16
- package/src/modes/rpc/rpc-mode.ts +17 -6
- package/src/modes/theme/theme-schema.json +30 -0
- package/src/modes/theme/theme.ts +39 -2
- package/src/modes/types.ts +7 -3
- package/src/modes/utils/ui-helpers.ts +5 -2
- package/src/prompts/system/orchestrate-notice.md +5 -3
- package/src/prompts/system/workflow-notice.md +2 -2
- package/src/prompts/tools/ask.md +2 -1
- package/src/prompts/tools/eval.md +6 -6
- package/src/prompts/tools/find.md +1 -1
- package/src/prompts/tools/irc.md +6 -6
- package/src/prompts/tools/search.md +1 -1
- package/src/prompts/tools/task.md +1 -1
- package/src/registry/agent-registry.ts +1 -1
- package/src/sdk.ts +85 -31
- package/src/session/agent-session.ts +127 -57
- package/src/session/history-storage.ts +56 -12
- package/src/session/session-manager.ts +34 -0
- package/src/task/output-manager.ts +40 -48
- package/src/task/render.ts +3 -8
- package/src/tools/ask.ts +74 -32
- package/src/tools/browser/tab-worker.ts +8 -5
- package/src/tools/eval-backends.ts +38 -0
- package/src/tools/eval-render.ts +750 -0
- package/src/tools/eval.ts +27 -754
- package/src/tools/find.ts +5 -29
- package/src/tools/index.ts +8 -38
- package/src/tools/path-utils.ts +144 -1
- package/src/tools/read.ts +47 -0
- package/src/tools/renderers.ts +1 -1
- package/src/tools/search.ts +2 -27
- package/src/tools/sqlite-reader.ts +92 -9
- package/src/tools/write.ts +9 -1
- package/src/tui/output-block.ts +5 -4
- package/src/utils/clipboard.ts +38 -1
- package/src/utils/open.ts +37 -2
- package/src/web/kagi.ts +168 -49
- package/src/web/search/providers/anthropic.ts +1 -1
- package/src/web/search/providers/exa.ts +20 -86
- package/src/web/search/providers/kagi.ts +4 -0
|
@@ -2,19 +2,20 @@
|
|
|
2
2
|
The user's message above is an **orchestration request**. Execute it as the orchestrator under the contract below. This contract overrides any default tendency to yield early, narrate, or do the work yourself.
|
|
3
3
|
|
|
4
4
|
<role>
|
|
5
|
-
You decompose, dispatch, verify, and iterate.
|
|
5
|
+
You decompose, dispatch, verify, and iterate. Substantial and parallelizable work goes through `task` subagents — that is the whole point of orchestrating. But you are not forbidden from touching the tree: a trivial, self-contained edit is yours to make directly when spawning a subagent for it would cost more than the edit itself. Your tool budget is: reading for planning, `task` for dispatch, `edit`/`write` for trivial inline fixes only, verification (`bun check`, `bun test`, `lsp diagnostics`), git via `bash`, and `todo_write` for tracking.
|
|
6
6
|
</role>
|
|
7
7
|
|
|
8
8
|
<rules>
|
|
9
9
|
1. **Do not yield until everything is closed.** A phase finishing is *not* a yield point — launch the next phase in the same turn. Stop only when every requested item is verifiably done, or you hit a concrete [blocked] state that genuinely requires the user.
|
|
10
10
|
2. **Enumerate the full surface before dispatching.** If the request references audits, plans, checklists, phase lists, or file lists, expand them into a flat set of items in `todo_write`. "Most of them" or "the important ones" is failure. Re-read the source documents — do not work from memory.
|
|
11
|
-
3. **Parallelize maximally.** Every set of edits with disjoint file scope MUST ship as one `task` batch. Serialize only when one subagent produces a contract (types, schema, shared module) the next consumes — and state the dependency when you do.
|
|
11
|
+
3. **Parallelize maximally; never launch a one-off task.** Every set of edits with disjoint file scope MUST ship as one `task` batch — fan the work as wide as it decomposes. A single-task batch for divisible work is a failure: split it. If you are about to dispatch exactly one subagent, stop — either there is more to run alongside it (find it and batch them) or the change is small enough to make inline yourself (do it). Serialize only when one subagent produces a contract (types, schema, shared module) the next consumes — and state the dependency when you do.
|
|
12
12
|
4. **Each `task` assignment is self-contained.** Subagents have no shared context. Spell out: target files (≤3–5 explicit paths, no globs), the change with APIs and patterns, edge cases, and observable acceptance criteria. Do not assume they read the same plan you did.
|
|
13
13
|
5. **Verify after every phase before launching the next.** Run the appropriate gate: `bun check` for types, package-scoped `bun test` for behavior, `lsp diagnostics` for changed files. If a phase introduced breakage, dispatch fix-up subagents *before* moving on. Never declare a phase done on a red tree.
|
|
14
14
|
6. **Commit policy.** If the request asks for commits or the repo workflow expects them, commit after each green phase with a focused message. Never commit a red tree. Never commit work the user did not ask to commit.
|
|
15
15
|
7. **Respawn, do not absorb.** If a subagent returns incomplete or wrong work, spawn a corrective subagent with the specific gap — do not silently fix it yourself.
|
|
16
16
|
8. **No scope creep, no scope shrink.** Do not add work the user did not ask for. Do not relabel unfinished items as "follow-up", "v1", or "MVP" to imply completion.
|
|
17
17
|
9. **Subagents do not verify, lint, or format.** Every `task` assignment MUST instruct the subagent to skip all gates and formatters. Their job is the edit only. You — the orchestrator — run verification and formatting **once** at the end of the phase across the union of changed files. Avoids redundant runs and racing formatter passes.
|
|
18
|
+
10. **Right-size the offload — do not micro-task.** Subagents are for substantial or parallelizable chunks, not every keystroke. A trivial, self-contained mechanical edit — deleting a redundant glob, fixing one line in a config, renaming a single symbol in one file — costs less to *do* than to describe in a Goal/Constraints assignment. Make those yourself with `edit`/`write` and move on; reserve `task`/`quick_task` for work large enough to justify the dispatch overhead. Wrapping a one-line change in a full subagent with scaffolding is pure waste.
|
|
18
19
|
</rules>
|
|
19
20
|
|
|
20
21
|
<workflow>
|
|
@@ -28,7 +29,8 @@ You decompose, dispatch, verify, and iterate. You do **not** edit code. Every fi
|
|
|
28
29
|
</workflow>
|
|
29
30
|
|
|
30
31
|
<anti-patterns>
|
|
31
|
-
-
|
|
32
|
+
- Doing substantial or parallelizable work yourself instead of fanning it out to subagents.
|
|
33
|
+
- Wrapping a single trivial edit (e.g. removing one redundant config line) in a `task`/`quick_task` with full Goal/Constraints scaffolding — just make the edit inline.
|
|
32
34
|
- Yielding after phase 1 with "ready to continue?".
|
|
33
35
|
- Dispatching one subagent at a time when five could run in parallel.
|
|
34
36
|
- Skipping `bun check` between phases because "the change looked safe".
|
|
@@ -14,8 +14,8 @@ Worth it when the task benefits from decomposition + parallel coverage, or from
|
|
|
14
14
|
State persists across cells, so scout in one cell and fan out in the next. Every cell has:
|
|
15
15
|
|
|
16
16
|
- `agent(prompt, *, agent_type="task", model=None, context=None, label=None, schema=None)` — run ONE subagent; returns its final text, or the validated object when `schema` (a JSON Schema dict) is given. With `schema` the subagent is forced to emit structured output that is validated for you — branch on the object, not on parsed prose. `agent_type` picks a discovered agent ("explore", "reviewer", "oracle", …); `context` is shared background; `label` names the artifact. Subagents are told their final text IS the return value, so they hand back raw data. `agent()` blocks until the subagent finishes; eval-spawned agents nest at most 3 deep.
|
|
17
|
-
- `parallel(thunks
|
|
18
|
-
- `pipeline(items, *stages
|
|
17
|
+
- `parallel(thunks)` — run zero-arg callables concurrently through a bounded pool, preserving input order; returns once all finish. The pool runs as wide as a `task` tool batch (the `task.maxConcurrency` setting; don't hand-tune it — fan out as wide as the work divides). A thunk that raises propagates — wrap risky work in `try/except` inside the thunk to keep partial results. In a loop, bind each closure's value with a default arg (`lambda d=d: …`) or every thunk captures the last one.
|
|
18
|
+
- `pipeline(items, *stages)` — map items through `stages` left-to-right. There is a BARRIER between stages: ALL items clear stage N before stage N+1 begins. Each stage is a one-arg callable; stage 1 gets the original item, later stages get the previous result. Same pool width as `parallel()`.
|
|
19
19
|
- `llm(prompt, *, model="default", system=None, schema=None)` — oneshot, stateless model call (no tools, no history). Tiers: "smol", "default", "slow". Cheap classification/scoring inside a fan-out.
|
|
20
20
|
- `log(message)` — emit a progress line above the status tree. `phase(title)` — start a phase; the status lines that follow group under it.
|
|
21
21
|
- `budget` — `budget.total` (output-token ceiling, or `None` when none is set), `budget.spent()` (tokens spent this turn — main loop + eval subagents), `budget.remaining()` (`math.inf` when total is `None`), `budget.hard` (whether it's enforced). A ceiling is set by the user: `+Nk` in their message is advisory (you self-limit via `budget.remaining()`), `+Nk!` (or Goal Mode) is hard — `agent()` refuses to spawn once spent reaches it. Gate loops on `budget.total` first, since it's `None` when the user set no budget.
|
package/src/prompts/tools/ask.md
CHANGED
|
@@ -8,6 +8,7 @@ Asks user when you need clarification or input during task execution.
|
|
|
8
8
|
- Use `recommended: <index>` to mark default (0-indexed); " (Recommended)" added automatically
|
|
9
9
|
- Use `questions` for multiple related questions instead of asking one at a time
|
|
10
10
|
- Set `multi: true` on question to allow multiple selections
|
|
11
|
+
- Use short option labels; put explanatory tradeoffs in `description` instead of merging them into the label
|
|
11
12
|
</instruction>
|
|
12
13
|
|
|
13
14
|
<caution>
|
|
@@ -22,7 +23,7 @@ Asks user when you need clarification or input during task execution.
|
|
|
22
23
|
|
|
23
24
|
<examples>
|
|
24
25
|
# Single question
|
|
25
|
-
questions: [{"id": "auth_method", "question": "Which authentication method should this API use?", "options": [{"label": "JWT"}, {"label": "OAuth2"}, {"label": "Session cookies"}], "recommended": 0}]
|
|
26
|
+
questions: [{"id": "auth_method", "question": "Which authentication method should this API use?", "options": [{"label": "JWT", "description": "Bearer tokens for stateless API clients."}, {"label": "OAuth2", "description": "Delegated authorization with external identity providers."}, {"label": "Session cookies", "description": "Browser-first authentication backed by server-side sessions."}], "recommended": 0}]
|
|
26
27
|
|
|
27
28
|
# Multiple questions
|
|
28
29
|
questions: [{"id": "storage_type", "question": "Which storage backend?", "options": [{"label": "SQLite"}, {"label": "PostgreSQL"}]}, {"id": "auth_method", "question": "Which auth method?", "options": [{"label": "JWT"}, {"label": "Session cookies"}]}]
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
Run code in a persistent kernel using a list of cells.
|
|
2
2
|
|
|
3
3
|
<instruction>
|
|
4
|
-
Each call submits one or more cells. Cells run in array order. State persists within each language across cells, tool calls, and subagents spawned with `task`; variables a parent or subagent declares are visible to the other on the same shared executor.
|
|
4
|
+
Each call submits one or more cells. Cells run in array order. State persists within each language across cells, tool calls, and subagents spawned with `task`; variables a parent or subagent declares are visible to the other on the same shared executor. Lean on this: stage helpers, loaded datasets, or live clients once, then fan out `task` subagents that call them directly — no re-importing, re-fetching, or serializing across the boundary.
|
|
5
5
|
|
|
6
6
|
Cell fields:
|
|
7
7
|
|
|
8
8
|
- `language` — {{#if py}}`"py"` for the IPython kernel{{/if}}{{#ifAll py js}}, {{/ifAll}}{{#if js}}`"js"` for the persistent JavaScript VM{{/if}}.
|
|
9
9
|
- `code` — cell body, verbatim. Newlines, quotes, and indentation are JSON-encoded; no fences, no headers.
|
|
10
10
|
- `title` (optional) — short label shown in the transcript (e.g. `"imports"`, `"load config"`).
|
|
11
|
-
- `timeout` (optional) — per-cell
|
|
11
|
+
- `timeout` (optional) — per-cell wall-clock budget in seconds (1-600). Default 30. It bounds the cell's **own** work, but is paused while an `agent()`/`parallel()`/`llm()` call is in flight — so a long fanout or a slow completion runs to completion, while the cell itself is still bounded. Compute, `print`/stdout, `log()`/`phase()`, and ordinary tool calls all count against the budget; raise `timeout` for a cell that does heavy local work or long non-agent tool calls.
|
|
12
12
|
- `reset` (optional) — wipe this cell's language kernel before running.{{#ifAll py js}} Reset is per-language: a `py` cell's reset does not touch the JavaScript VM and vice versa.{{/ifAll}}
|
|
13
13
|
|
|
14
14
|
**Work incrementally:**
|
|
@@ -48,10 +48,10 @@ llm(prompt, model?="default", system?=None, schema?=None) → str | dict
|
|
|
48
48
|
Oneshot, stateless LLM call (no history, no tools). `model` picks a tier: "smol" (fast), "default" (this session's model), "slow" (most capable). Pass `system` for a system prompt. Pass a JSON-Schema `schema` to force structured output and get the parsed object back; otherwise returns the completion text.
|
|
49
49
|
agent(prompt, agent_type?="task", model?=None, context?=None, label?=None, schema?=None) → str | dict
|
|
50
50
|
Run a subagent and return its final output. Defaults to the bundled "task" agent; pass `agent_type`/`agentType` for another discovered agent. Pass a JSON-Schema `schema` to force structured output and get the parsed object back.
|
|
51
|
-
parallel(thunks
|
|
52
|
-
Run thunks (callables) through a bounded pool (
|
|
53
|
-
pipeline(items, ...stages
|
|
54
|
-
Map each item through stages left-to-right; a barrier runs between stages (every item clears stage N before stage N+1). Each stage is a one-arg callable: stage 1 gets the original item, later stages get the previous result.
|
|
51
|
+
parallel(thunks) → list
|
|
52
|
+
Run thunks (callables) through a bounded pool, preserving input order. The pool is as wide as a `task` tool batch (tracks the `task.maxConcurrency` setting), so fan out as wide as the work divides — don't pre-shrink it. Barrier: returns once all finish; a thunk that throws propagates.
|
|
53
|
+
pipeline(items, ...stages) → list
|
|
54
|
+
Map each item through stages left-to-right; a barrier runs between stages (every item clears stage N before stage N+1). Each stage is a one-arg callable: stage 1 gets the original item, later stages get the previous result. Same pool width as parallel().
|
|
55
55
|
log(message) → None
|
|
56
56
|
Emit a progress line above the status tree.
|
|
57
57
|
phase(title) → None
|
|
@@ -2,7 +2,7 @@ Finds files and directories using fast pattern matching that works with any code
|
|
|
2
2
|
|
|
3
3
|
<instruction>
|
|
4
4
|
- `paths` is required and accepts an array of globs, files, or directories
|
|
5
|
-
- Pass multiple targets as **separate array elements** (`paths: ["a", "b"]`)
|
|
5
|
+
- Pass multiple targets as **separate array elements** (`paths: ["a", "b"]`).
|
|
6
6
|
- `gitignore` defaults to `true` and hides files matched by `.gitignore`. Set `gitignore: false` to find `.env*`, `*.log`, freshly-created build outputs, or anything else your repo ignores
|
|
7
7
|
- `hidden` defaults to `true`; combine with `gitignore: false` to surface dotfiles that are also gitignored
|
|
8
8
|
- `limit` is clamped to 1-200 (default 200). Narrow the pattern instead of raising the limit
|
package/src/prompts/tools/irc.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Sends short text messages to other live agents in this process and receives their prose replies.
|
|
2
2
|
|
|
3
3
|
<instruction>
|
|
4
|
-
- The main agent is addressable as `
|
|
4
|
+
- The main agent is addressable as `Main`. Subagents reuse their task id (e.g. `AuthLoader`, or `AuthLoader-2` when the name repeats).
|
|
5
5
|
- `op: "list"` returns the current set of visible peers. Use it before sending if you are not sure who is live.
|
|
6
6
|
- `op: "send"` delivers `message` to `to`. `to` may be a specific id or `"all"` to broadcast.
|
|
7
7
|
- The recipient generates the reply via an ephemeral side-channel turn that uses their current model, system prompt, and history — it does **not** wait for the recipient's main loop to be free, so it is safe to IRC an agent that is currently inside a long-running tool call.
|
|
@@ -10,7 +10,7 @@ Sends short text messages to other live agents in this process and receives thei
|
|
|
10
10
|
|
|
11
11
|
<when_to_use>
|
|
12
12
|
You SHOULD reach for `irc` proactively when continuing alone is wasteful or wrong. When in doubt, prefer messaging.
|
|
13
|
-
- **Unexpected state.** You hit something the original task did not describe — a missing file, a config that contradicts the assignment, an API behaving differently than you were told, a tool failing in a way that suggests the spec is wrong. DM `
|
|
13
|
+
- **Unexpected state.** You hit something the original task did not describe — a missing file, a config that contradicts the assignment, an API behaving differently than you were told, a tool failing in a way that suggests the spec is wrong. DM `Main` (or the spawning agent) for guidance instead of guessing.
|
|
14
14
|
- **Blocked by another agent.** A peer holds the file/branch/resource you need, has already started the change you are about to make, or owns a decision you depend on. DM that peer (or broadcast to discover who) before duplicating or stepping on work.
|
|
15
15
|
- **Decision points outside your scope.** A genuine fork in the road that the assignment did not pre-decide (e.g. which of two viable APIs to use, whether to refactor adjacent code). Ask the requester rather than picking unilaterally.
|
|
16
16
|
- **Coordination opportunities.** You realize a peer's in-flight work would benefit from yours, or vice-versa.
|
|
@@ -25,7 +25,7 @@ These rules apply to both sending and replying.
|
|
|
25
25
|
- **Use IRC, not terminal tools, to learn about peers.** Do not `grep` artifacts, read other sessions' JSONL files, or shell-poke around to figure out what another agent is doing. DM them — they have the live answer and you do not.
|
|
26
26
|
- **One round-trip is enough.** Replies arrive synchronously when the recipient is reachable. Do not follow up with "did you get my message?" — they did. If `delivered` is empty or the result was `failed`, the peer is unavailable; move on or report the blocker, do not retry in a loop.
|
|
27
27
|
- **Stay terse.** A DM is a chat message, not a memo. One question per send when you can. Share file paths and artifacts via `local://` / `memory://` / `artifact://` URLs instead of pasting blobs.
|
|
28
|
-
- **Address peers by id.** Use the exact id from `op: "list"` (e.g. `
|
|
28
|
+
- **Address peers by id.** Use the exact id from `op: "list"` (e.g. `AuthLoader`, `Main`). Do not invent friendly names.
|
|
29
29
|
- **Do not IRC for things a tool would answer.** If a `read`, `grep`, or build command would resolve the question, do that first.
|
|
30
30
|
- **When you receive an IRC message, answer it before continuing.** The recipient injects the question + your auto-reply into your history; address it directly, do not repeat it back to the user.
|
|
31
31
|
</etiquette>
|
|
@@ -39,11 +39,11 @@ These rules apply to both sending and replying.
|
|
|
39
39
|
# List peers
|
|
40
40
|
`{"op": "list"}`
|
|
41
41
|
# Direct message to the main agent (waits for prose reply)
|
|
42
|
-
`{"op": "send", "to": "
|
|
42
|
+
`{"op": "send", "to": "Main", "message": "Should I prefer JWT or session cookies for the auth flow?"}`
|
|
43
43
|
# Unexpected state — ask the originator
|
|
44
|
-
`{"op": "send", "to": "
|
|
44
|
+
`{"op": "send", "to": "Main", "message": "Assignment says edit src/auth/jwt.ts but the file does not exist. Is the new path src/server/auth/jwt.ts?"}`
|
|
45
45
|
# Blocked by a peer — ask them directly
|
|
46
|
-
`{"op": "send", "to": "
|
|
46
|
+
`{"op": "send", "to": "AuthLoader", "message": "Are you still touching src/server/auth.ts? I need to add a 401 path; OK to proceed or should I wait?"}`
|
|
47
47
|
# Broadcast to discover who owns something (no replies, just informs them)
|
|
48
48
|
`{"op": "send", "to": "all", "message": "About to refactor src/server/middleware/*. Anyone already in there?", "awaitReply": false}`
|
|
49
49
|
</examples>
|
|
@@ -3,7 +3,7 @@ Searches files using powerful regex matching.
|
|
|
3
3
|
<instruction>
|
|
4
4
|
- Supports Rust regex syntax (RE2-style — no lookaround or backreferences). Use line anchors or post-filters instead of (?!…)/(?<!…)
|
|
5
5
|
- `paths` is required and accepts either one string or an array of files, directories, globs, or internal URLs
|
|
6
|
-
- For multiple targets, pass an array with one target per element
|
|
6
|
+
- For multiple targets, pass an array with one target per element: `["src", "tests"]`.
|
|
7
7
|
- Cross-line patterns are detected from literal `\n` or escaped `\\n` in `pattern`
|
|
8
8
|
</instruction>
|
|
9
9
|
|
|
@@ -2,7 +2,7 @@ Launches subagents to parallelize workflows.
|
|
|
2
2
|
|
|
3
3
|
{{#if asyncEnabled}}
|
|
4
4
|
- Results are delivered automatically when complete.
|
|
5
|
-
- The tool result lists the assigned task ids (e.g. `
|
|
5
|
+
- The tool result lists the assigned task ids (e.g. `AuthLoader`) — those are the live agent ids.
|
|
6
6
|
{{#if ircEnabled}}
|
|
7
7
|
- Coordinate with running tasks via `irc` using those ids. `job cancel` terminates a task and **cannot carry a message** — only use it for stalled/abandoned work.
|
|
8
8
|
- If genuinely blocked on completion, wait with `job poll`; otherwise keep working.
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import type { AgentSession } from "../session/agent-session";
|
|
10
10
|
|
|
11
|
-
export const MAIN_AGENT_ID = "
|
|
11
|
+
export const MAIN_AGENT_ID = "Main";
|
|
12
12
|
|
|
13
13
|
export type AgentStatus = "running" | "idle" | "completed" | "aborted";
|
|
14
14
|
export type AgentKind = "main" | "sub";
|
package/src/sdk.ts
CHANGED
|
@@ -102,7 +102,7 @@ import { AgentSession } from "./session/agent-session";
|
|
|
102
102
|
import { resolveAuthBrokerConfig } from "./session/auth-broker-config";
|
|
103
103
|
import { AuthBrokerClient, AuthStorage, RemoteAuthCredentialStore } from "./session/auth-storage";
|
|
104
104
|
import { type CustomMessage, convertToLlm } from "./session/messages";
|
|
105
|
-
import { SessionManager } from "./session/session-manager";
|
|
105
|
+
import { getRestorableSessionModels, SessionManager } from "./session/session-manager";
|
|
106
106
|
import { closeAllConnections } from "./ssh/connection-manager";
|
|
107
107
|
import { unmountAll } from "./ssh/sshfs-mount";
|
|
108
108
|
import {
|
|
@@ -323,13 +323,13 @@ export interface CreateAgentSessionOptions {
|
|
|
323
323
|
parentHindsightSessionState?: HindsightSessionState;
|
|
324
324
|
/** Parent Mnemopi state to alias for subagent memory tools. */
|
|
325
325
|
parentMnemopiSessionState?: MnemopiSessionState;
|
|
326
|
-
/** Pre-allocated agent identity for IRC routing. Default: "
|
|
326
|
+
/** Pre-allocated agent identity for IRC routing. Default: "Main" for top-level, parentTaskPrefix-derived for sub. */
|
|
327
327
|
agentId?: string;
|
|
328
328
|
/** Display name for the agent in IRC. Default: "main" or "sub". */
|
|
329
329
|
agentDisplayName?: string;
|
|
330
330
|
/** Optional shared agent registry for IRC routing. Default: AgentRegistry.global(). */
|
|
331
331
|
agentRegistry?: AgentRegistry;
|
|
332
|
-
/** Parent task ID prefix for nested artifact naming (e.g., "
|
|
332
|
+
/** Parent task ID prefix for nested artifact naming (e.g., "Extensions") */
|
|
333
333
|
parentTaskPrefix?: string;
|
|
334
334
|
/** Inherited eval executor session id for subagents sharing parent eval state. */
|
|
335
335
|
parentEvalSessionId?: string;
|
|
@@ -1008,20 +1008,37 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1008
1008
|
);
|
|
1009
1009
|
let model = options.model;
|
|
1010
1010
|
let modelFallbackMessage: string | undefined;
|
|
1011
|
-
//
|
|
1012
|
-
//
|
|
1013
|
-
|
|
1014
|
-
|
|
1011
|
+
// Identify session model strings to restore in fallback order. We do an
|
|
1012
|
+
// initial pass here so model-dependent setup (thinking-level resolution,
|
|
1013
|
+
// host preconnect) can use the restored model; extension-registered
|
|
1014
|
+
// providers aren't visible yet, so we retry the preferred candidates once
|
|
1015
|
+
// extensions register below.
|
|
1016
|
+
const sessionModelStrings =
|
|
1017
|
+
!hasExplicitModel && hasExistingSession
|
|
1018
|
+
? getRestorableSessionModels(existingSession.models, sessionManager.getLastModelChangeRole())
|
|
1019
|
+
: [];
|
|
1020
|
+
let restoredSessionModelIndex = -1;
|
|
1021
|
+
if (!hasExplicitModel && !model && sessionModelStrings.length > 0) {
|
|
1015
1022
|
await logger.time("restoreSessionModel", async () => {
|
|
1016
|
-
|
|
1017
|
-
|
|
1023
|
+
let failedSessionModel: string | undefined;
|
|
1024
|
+
for (let i = 0; i < sessionModelStrings.length; i++) {
|
|
1025
|
+
const sessionModelStr = sessionModelStrings[i];
|
|
1026
|
+
const parsedModel = parseModelString(sessionModelStr);
|
|
1027
|
+
if (!parsedModel) {
|
|
1028
|
+
failedSessionModel ??= sessionModelStr;
|
|
1029
|
+
continue;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1018
1032
|
const restoredModel = modelRegistry.find(parsedModel.provider, parsedModel.id);
|
|
1019
1033
|
if (restoredModel && (await hasModelApiKey(restoredModel))) {
|
|
1020
1034
|
model = restoredModel;
|
|
1035
|
+
restoredSessionModelIndex = i;
|
|
1036
|
+
break;
|
|
1021
1037
|
}
|
|
1038
|
+
failedSessionModel ??= sessionModelStr;
|
|
1022
1039
|
}
|
|
1023
|
-
if (
|
|
1024
|
-
modelFallbackMessage = `Could not restore model ${
|
|
1040
|
+
if (failedSessionModel) {
|
|
1041
|
+
modelFallbackMessage = `Could not restore model ${failedSessionModel}`;
|
|
1025
1042
|
}
|
|
1026
1043
|
});
|
|
1027
1044
|
}
|
|
@@ -1039,26 +1056,29 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1039
1056
|
|
|
1040
1057
|
const taskDepth = options.taskDepth ?? 0;
|
|
1041
1058
|
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
//
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1059
|
+
// Resolves the session/agent thinking level using the same precedence we
|
|
1060
|
+
// apply at startup: explicit option → persisted session entry → default
|
|
1061
|
+
// role's explicit selector → selected model's defaultLevel → global
|
|
1062
|
+
// settings default. Run again after extension role reclaim so the final
|
|
1063
|
+
// model's own defaults aren't masked by an earlier fallback model's.
|
|
1064
|
+
const pickInitialThinkingLevel = (selectedModel: Model | undefined): ConfiguredThinkingLevel | undefined => {
|
|
1065
|
+
let level = options.thinkingLevel;
|
|
1066
|
+
if (level === undefined && hasExistingSession && hasThinkingEntry) {
|
|
1067
|
+
level = parseThinkingLevel(existingSession.thinkingLevel);
|
|
1068
|
+
}
|
|
1069
|
+
if (level === undefined && !hasExplicitModel && !hasThinkingEntry && defaultRoleSpec.explicitThinkingLevel) {
|
|
1070
|
+
level = defaultRoleSpec.thinkingLevel;
|
|
1071
|
+
}
|
|
1072
|
+
if (level === undefined && selectedModel?.thinking?.defaultLevel !== undefined) {
|
|
1073
|
+
level = selectedModel.thinking.defaultLevel;
|
|
1074
|
+
}
|
|
1075
|
+
if (level === undefined) {
|
|
1076
|
+
level = settings.get("defaultThinkingLevel");
|
|
1077
|
+
}
|
|
1078
|
+
return level;
|
|
1079
|
+
};
|
|
1080
|
+
let thinkingLevel = pickInitialThinkingLevel(model);
|
|
1081
|
+
let autoThinking = thinkingLevel === AUTO_THINKING;
|
|
1062
1082
|
// Concrete level the agent/session start with. With `auto` this is the
|
|
1063
1083
|
// provisional level shown until the first per-turn classification resolves;
|
|
1064
1084
|
// `auto` itself stays a session-only concept handled by AgentSession.
|
|
@@ -1444,6 +1464,40 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1444
1464
|
extensionsResult.runtime.pendingProviderRegistrations = [];
|
|
1445
1465
|
}
|
|
1446
1466
|
|
|
1467
|
+
// Retry session-model candidates now that extension providers are
|
|
1468
|
+
// registered. The initial restore runs before extensions load, so a role
|
|
1469
|
+
// model supplied by an extension would have either fallen back to the
|
|
1470
|
+
// saved default (`restoredSessionModelIndex > 0`) or failed entirely
|
|
1471
|
+
// (`restoredSessionModelIndex === -1`, with the settings default or
|
|
1472
|
+
// downstream fallback filling `model`). Reclaim it here so resume
|
|
1473
|
+
// honors the last active role in either case.
|
|
1474
|
+
const sessionRetryLimit = restoredSessionModelIndex >= 0 ? restoredSessionModelIndex : sessionModelStrings.length;
|
|
1475
|
+
if (!hasExplicitModel && sessionRetryLimit > 0) {
|
|
1476
|
+
for (let i = 0; i < sessionRetryLimit; i++) {
|
|
1477
|
+
const sessionModelStr = sessionModelStrings[i];
|
|
1478
|
+
const parsedModel = parseModelString(sessionModelStr);
|
|
1479
|
+
if (!parsedModel) continue;
|
|
1480
|
+
const restoredModel = modelRegistry.find(parsedModel.provider, parsedModel.id);
|
|
1481
|
+
if (restoredModel && (await hasModelApiKey(restoredModel))) {
|
|
1482
|
+
model = restoredModel;
|
|
1483
|
+
modelFallbackMessage = undefined;
|
|
1484
|
+
restoredSessionModelIndex = i;
|
|
1485
|
+
// Recompute thinking-level from scratch against the reclaimed
|
|
1486
|
+
// model: any value derived from the earlier fallback model's
|
|
1487
|
+
// `thinking.defaultLevel` must not become sticky.
|
|
1488
|
+
thinkingLevel = pickInitialThinkingLevel(restoredModel);
|
|
1489
|
+
autoThinking = thinkingLevel === AUTO_THINKING;
|
|
1490
|
+
effectiveThinkingLevel = thinkingLevel === AUTO_THINKING ? undefined : thinkingLevel;
|
|
1491
|
+
effectiveThinkingLevel = logger.time("resolveThinkingLevelForModel", () =>
|
|
1492
|
+
autoThinking
|
|
1493
|
+
? resolveProvisionalAutoLevel(restoredModel)
|
|
1494
|
+
: resolveThinkingLevelForModel(restoredModel, effectiveThinkingLevel),
|
|
1495
|
+
);
|
|
1496
|
+
preconnectModelHost(restoredModel.baseUrl);
|
|
1497
|
+
break;
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1447
1501
|
// Resolve deferred --model pattern now that extension models are registered.
|
|
1448
1502
|
if (!model && options.modelPattern) {
|
|
1449
1503
|
const availableModels = modelRegistry.getAll();
|