@oh-my-pi/pi-coding-agent 15.8.3 → 15.9.1
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 +113 -31
- package/dist/types/capability/skill.d.ts +7 -0
- package/dist/types/cli/update-cli.d.ts +15 -1
- package/dist/types/config/append-only-context-mode.d.ts +8 -0
- package/dist/types/config/model-registry.d.ts +3 -0
- package/dist/types/config/models-config-schema.d.ts +15 -0
- package/dist/types/config/settings-schema.d.ts +38 -24
- package/dist/types/debug/protocol-probe.d.ts +38 -0
- package/dist/types/debug/terminal-info.d.ts +34 -0
- package/dist/types/exa/mcp-client.d.ts +2 -1
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/extensibility/custom-tools/types.d.ts +1 -1
- package/dist/types/extensibility/extensions/types.d.ts +11 -0
- package/dist/types/extensibility/shared-events.d.ts +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/mcp/json-rpc.d.ts +6 -1
- package/dist/types/mcp/tool-bridge.d.ts +4 -0
- package/dist/types/mcp/transports/stdio.d.ts +16 -7
- package/dist/types/mnemopi/state.d.ts +2 -2
- package/dist/types/modes/components/agent-dashboard.d.ts +1 -0
- package/dist/types/modes/components/assistant-message.d.ts +3 -2
- package/dist/types/modes/components/extensions/extension-dashboard.d.ts +1 -0
- package/dist/types/modes/components/hook-selector.d.ts +11 -0
- package/dist/types/modes/components/plugin-settings.d.ts +40 -8
- package/dist/types/modes/components/session-selector.d.ts +8 -3
- package/dist/types/modes/components/settings-selector.d.ts +1 -1
- package/dist/types/modes/components/todo-reminder.d.ts +1 -1
- package/dist/types/modes/components/transcript-container.d.ts +36 -0
- package/dist/types/modes/interactive-mode.d.ts +2 -1
- package/dist/types/modes/rpc/rpc-types.d.ts +1 -1
- package/dist/types/modes/theme/theme.d.ts +20 -1
- package/dist/types/modes/utils/keybinding-matchers.d.ts +4 -0
- package/dist/types/plan-mode/plan-handoff.d.ts +20 -0
- package/dist/types/session/agent-session.d.ts +5 -2
- package/dist/types/session/history-storage.d.ts +3 -4
- package/dist/types/session/indexed-session-storage.d.ts +59 -0
- package/dist/types/session/messages.d.ts +1 -0
- package/dist/types/session/redis-session-storage.d.ts +12 -85
- package/dist/types/session/session-manager.d.ts +21 -0
- package/dist/types/session/session-storage.d.ts +5 -7
- package/dist/types/session/sql-session-storage.d.ts +16 -85
- package/dist/types/slash-commands/types.d.ts +17 -4
- package/dist/types/task/executor.d.ts +9 -0
- package/dist/types/task/index.d.ts +3 -1
- package/dist/types/telemetry-export.d.ts +19 -0
- package/dist/types/tiny/compiled-runtime.d.ts +35 -0
- package/dist/types/tiny/text.d.ts +17 -0
- package/dist/types/tools/ask.d.ts +1 -0
- package/dist/types/tools/index.d.ts +4 -2
- package/dist/types/tools/path-utils.d.ts +1 -1
- package/dist/types/tools/search.d.ts +2 -2
- package/dist/types/tools/{todo-write.d.ts → todo.d.ts} +18 -18
- package/dist/types/utils/session-color.d.ts +7 -2
- package/dist/types/web/search/index.d.ts +1 -1
- package/dist/types/web/search/provider.d.ts +2 -2
- package/dist/types/web/search/providers/base.d.ts +14 -0
- package/dist/types/web/search/providers/exa.d.ts +9 -0
- package/dist/types/web/search/providers/perplexity.d.ts +2 -2
- package/dist/types/web/search/types.d.ts +66 -1
- package/package.json +15 -9
- package/scripts/build-binary.ts +12 -0
- package/src/capability/skill.ts +7 -0
- package/src/cli/args.ts +1 -1
- package/src/cli/session-picker.ts +1 -0
- package/src/cli/update-cli.ts +54 -2
- package/src/commands/completions.ts +1 -1
- package/src/config/append-only-context-mode.ts +37 -0
- package/src/config/models-config-schema.ts +1 -0
- package/src/config/settings-schema.ts +24 -57
- package/src/debug/index.ts +67 -1
- package/src/debug/protocol-probe.ts +267 -0
- package/src/debug/terminal-info.ts +127 -0
- package/src/exa/mcp-client.ts +11 -5
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +3 -3
- package/src/extensibility/custom-tools/types.ts +1 -1
- package/src/extensibility/extensions/types.ts +11 -0
- package/src/extensibility/shared-events.ts +1 -1
- package/src/extensibility/skills.ts +3 -3
- package/src/index.ts +1 -0
- package/src/internal-urls/docs-index.generated.ts +7 -7
- package/src/main.ts +16 -2
- package/src/mcp/json-rpc.ts +8 -0
- package/src/mcp/render.ts +3 -0
- package/src/mcp/tool-bridge.ts +10 -2
- package/src/mcp/transports/http.ts +33 -16
- package/src/mcp/transports/stdio.ts +37 -12
- package/src/mnemopi/state.ts +4 -4
- package/src/modes/acp/acp-agent.ts +168 -3
- package/src/modes/acp/acp-event-mapper.ts +7 -7
- package/src/modes/components/agent-dashboard.ts +103 -31
- package/src/modes/components/assistant-message.ts +3 -2
- package/src/modes/components/extensions/extension-dashboard.ts +56 -10
- package/src/modes/components/history-search.ts +128 -14
- package/src/modes/components/hook-selector.ts +149 -14
- package/src/modes/components/plugin-settings.ts +270 -36
- package/src/modes/components/session-selector.ts +81 -19
- package/src/modes/components/settings-selector.ts +1 -1
- package/src/modes/components/status-line/segments.ts +2 -1
- package/src/modes/components/status-line.ts +1 -1
- package/src/modes/components/tips.txt +6 -2
- package/src/modes/components/todo-reminder.ts +1 -1
- package/src/modes/components/tool-execution.ts +9 -4
- package/src/modes/components/transcript-container.ts +109 -0
- package/src/modes/controllers/command-controller.ts +4 -3
- package/src/modes/controllers/event-controller.ts +12 -7
- package/src/modes/controllers/extension-ui-controller.ts +3 -0
- package/src/modes/controllers/input-controller.ts +10 -5
- package/src/modes/controllers/selector-controller.ts +30 -19
- package/src/modes/controllers/todo-command-controller.ts +1 -1
- package/src/modes/interactive-mode.ts +56 -9
- package/src/modes/print-mode.ts +5 -0
- package/src/modes/rpc/rpc-types.ts +1 -1
- package/src/modes/theme/theme.ts +48 -8
- package/src/modes/utils/keybinding-matchers.ts +10 -0
- package/src/modes/utils/ui-helpers.ts +1 -0
- package/src/plan-mode/plan-handoff.ts +37 -0
- package/src/priority.json +4 -0
- package/src/prompts/goals/goal-continuation.md +1 -1
- package/src/prompts/steering/user-interjection.md +10 -0
- package/src/prompts/system/agent-creation-architect.md +1 -26
- package/src/prompts/system/eager-todo.md +3 -3
- package/src/prompts/system/orchestrate-notice.md +5 -5
- package/src/prompts/system/plan-mode-approved.md +14 -17
- package/src/prompts/system/plan-mode-reference.md +3 -6
- package/src/prompts/system/subagent-system-prompt.md +11 -0
- package/src/prompts/system/system-prompt.md +143 -145
- package/src/prompts/system/title-system.md +3 -2
- package/src/prompts/system/workflow-notice.md +1 -1
- package/src/prompts/tools/browser.md +33 -30
- package/src/prompts/tools/render-mermaid.md +2 -2
- package/src/prompts/tools/search.md +1 -1
- package/src/prompts/tools/task.md +3 -1
- package/src/prompts/tools/{todo-write.md → todo.md} +5 -5
- package/src/sdk.ts +6 -21
- package/src/session/agent-session.ts +44 -21
- package/src/session/history-storage.ts +11 -18
- package/src/session/indexed-session-storage.ts +430 -0
- package/src/session/messages.ts +80 -0
- package/src/session/redis-session-storage.ts +66 -377
- package/src/session/session-manager.ts +109 -23
- package/src/session/session-storage.ts +148 -68
- package/src/session/sql-session-storage.ts +131 -382
- package/src/slash-commands/helpers/todo.ts +2 -2
- package/src/slash-commands/types.ts +27 -10
- package/src/task/executor.ts +9 -1
- package/src/task/index.ts +51 -1
- package/src/telemetry-export.ts +126 -0
- package/src/tiny/compiled-runtime.ts +179 -0
- package/src/tiny/text.ts +112 -1
- package/src/tiny/worker.ts +24 -2
- package/src/tools/ask.ts +133 -87
- package/src/tools/fetch.ts +17 -4
- package/src/tools/find.ts +2 -2
- package/src/tools/index.ts +6 -4
- package/src/tools/memory-recall.ts +1 -1
- package/src/tools/memory-reflect.ts +1 -1
- package/src/tools/path-utils.ts +16 -7
- package/src/tools/renderers.ts +2 -2
- package/src/tools/search.ts +6 -3
- package/src/tools/ssh.ts +26 -10
- package/src/tools/{todo-write.ts → todo.ts} +32 -35
- package/src/tools/write.ts +14 -2
- package/src/tui/status-line.ts +15 -4
- package/src/utils/session-color.ts +39 -14
- package/src/utils/title-generator.ts +9 -2
- package/src/web/search/index.ts +4 -2
- package/src/web/search/provider.ts +19 -35
- package/src/web/search/providers/base.ts +17 -0
- package/src/web/search/providers/exa.ts +111 -7
- package/src/web/search/providers/perplexity.ts +8 -4
- package/src/web/search/types.ts +74 -32
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
Output
|
|
1
|
+
Need generate 3-6 word title from first message; capture main task
|
|
2
|
+
Output title only; no quotes no punctuation
|
|
3
|
+
If message has no concrete task yet (greeting, small talk, vague), output exactly: none
|
|
@@ -62,7 +62,7 @@ Scale to the ask: "find any bugs" → a few finders, single-vote verify. "thorou
|
|
|
62
62
|
</patterns>
|
|
63
63
|
|
|
64
64
|
<execution>
|
|
65
|
-
- Decompose the surface first; capture it in `
|
|
65
|
+
- Decompose the surface first; capture it in `todo` when it spans phases.
|
|
66
66
|
- Prefer `schema=` for any agent whose output you branch on.
|
|
67
67
|
- After a fan-out returns, YOU own correctness: read the artifacts, run the gate, verify before acting. Subagents do the legwork; they don't get the last word.
|
|
68
68
|
- Keep going until the task is closed — a returned fan-out is a step, not a stopping point.
|
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
Drives
|
|
1
|
+
Drives real Chromium tab; full puppeteer access via JS execution.
|
|
2
2
|
|
|
3
3
|
<instruction>
|
|
4
|
-
- For static web content (articles, docs, issues/PRs, JSON, PDFs, feeds), prefer
|
|
4
|
+
- For static web content (articles, docs, issues/PRs, JSON, PDFs, feeds), prefer `read` tool with URL — reader-mode text without spinning up browser. Use this tool when Need JS execution, authentication, or interactive actions.
|
|
5
5
|
- Three actions only:
|
|
6
|
-
- `open` — acquire
|
|
7
|
-
- `close` — release
|
|
8
|
-
- `run` — execute JS against
|
|
6
|
+
- `open` — acquire or reuse named tab. `name` defaults `"main"`. Optional `url` navigates after tab ready. Optional `viewport` sets dimensions. Optional `dialogs: "accept" | "dismiss"` auto-handles `alert`/`confirm`/`beforeunload` so navigation/clicks don't hang (default: leave dialogs unhandled — page hangs until caller wires `page.on('dialog', …)`).
|
|
7
|
+
- `close` — release tab by `name`, or every tab with `all: true`. For spawned-app browsers, set `kill: true` to terminate process tree (default leaves running).
|
|
8
|
+
- `run` — execute JS against existing tab. `code` is body of async function with `page`, `browser`, `tab`, `display`, `assert`, `wait` in scope. Function's return value JSON-stringified into tool result; multiple `display(value)` calls accumulate text/images.
|
|
9
9
|
- Tabs survive across `run` calls and across in-process subagents. Open once, reuse many times.
|
|
10
|
-
- Browser kinds, selected by
|
|
10
|
+
- Browser kinds, selected by `app` field on `open`:
|
|
11
11
|
- default (no `app`) → headless Chromium with stealth patches.
|
|
12
|
-
- `app.path` → spawn
|
|
13
|
-
- `app.cdp_url` → connect to
|
|
14
|
-
- `app.target` (with `path`/`cdp_url`) — substring matched against url+title to pick
|
|
15
|
-
- Inside `run`, `tab` exposes high-level helpers; reach for `page` (raw puppeteer Page) when
|
|
16
|
-
- `tab.goto(url, { waitUntil? })` — clears
|
|
17
|
-
- `tab.observe({ includeAll?, viewportOnly? })` — accessibility snapshot. Returns `{ url, title, viewport, scroll, elements: [{ id, role, name, value, states, … }] }`. Element ids
|
|
18
|
-
- `tab.id(n)` — resolves
|
|
12
|
+
- `app.path` → spawn absolute binary (Electron/CDP). If running instance already exposes CDP port, reused; otherwise stale instances killed, fresh one spawned. No stealth patches — NEVER tamper with real desktop app.
|
|
13
|
+
- `app.cdp_url` → connect to existing CDP endpoint (e.g. `http://127.0.0.1:9222`).
|
|
14
|
+
- `app.target` (with `path`/`cdp_url`) — substring matched against url+title to pick BrowserWindow when app exposes several.
|
|
15
|
+
- Inside `run`, `tab` exposes high-level helpers; reach for `page` (raw puppeteer Page) when Need anything they don't cover.
|
|
16
|
+
- `tab.goto(url, { waitUntil? })` — clears element cache and navigates.
|
|
17
|
+
- `tab.observe({ includeAll?, viewportOnly? })` — accessibility snapshot. Returns `{ url, title, viewport, scroll, elements: [{ id, role, name, value, states, … }] }`. Element ids stable until next observe/goto.
|
|
18
|
+
- `tab.id(n)` — resolves element id from most recent observe to real `ElementHandle` you can `.click()`, `.type()`, etc.
|
|
19
19
|
- `tab.click(selector)` / `tab.type(selector, text)` / `tab.fill(selector, value)` / `tab.press(key, { selector? })` / `tab.scroll(dx, dy)` — selector-based actions.
|
|
20
|
-
- `tab.waitFor(selector)` — waits until
|
|
21
|
-
- `tab.drag(from, to)` — drag from one point to another. Each endpoint
|
|
22
|
-
- `tab.scrollIntoView(selector)` — scroll
|
|
23
|
-
- `tab.select(selector, …values)` — set
|
|
24
|
-
- `tab.uploadFile(selector, …filePaths)` — attach files to
|
|
25
|
-
- `tab.waitForUrl(pattern, { timeout? })` — pattern
|
|
26
|
-
- `tab.waitForResponse(pattern, { timeout? })` — pattern
|
|
27
|
-
- `tab.evaluate(fn, …args)` — sugar for `page.evaluate` with
|
|
28
|
-
- `tab.screenshot({ selector?, fullPage?, save?, silent? })` — auto-attaches
|
|
20
|
+
- `tab.waitFor(selector)` — waits until selector attached, returns resolved `ElementHandle` for chaining (e.g. `const btn = await tab.waitFor('text/Submit'); await btn.click();`).
|
|
21
|
+
- `tab.drag(from, to)` — drag from one point to another. Each endpoint either selector string (drag center-to-center) or `{ x, y }` viewport-coordinate point (for canvases, sliders).
|
|
22
|
+
- `tab.scrollIntoView(selector)` — scroll matching element to center of viewport (use before clicking off-screen elements).
|
|
23
|
+
- `tab.select(selector, …values)` — set selected option(s) on `<select>`. Returns values that ended up selected. `tab.fill` NEVER works for selects.
|
|
24
|
+
- `tab.uploadFile(selector, …filePaths)` — attach files to `<input type="file">`. Paths resolve relative to cwd.
|
|
25
|
+
- `tab.waitForUrl(pattern, { timeout? })` — pattern substring or `RegExp`. Polls `location.href` so works for SPA pushState navigations, not just real navigations. Returns matched URL.
|
|
26
|
+
- `tab.waitForResponse(pattern, { timeout? })` — pattern substring, `RegExp`, or `(response) => boolean`. Returns raw puppeteer `HTTPResponse` (call `.text()` / `.json()` / `.status()` / `.headers()` on it).
|
|
27
|
+
- `tab.evaluate(fn, …args)` — sugar for `page.evaluate` with abort signal already wired. Use this instead of dropping to `page.evaluate` for ad-hoc DOM reads.
|
|
28
|
+
- `tab.screenshot({ selector?, fullPage?, save?, silent? })` — captures screenshot and **auto-attaches to tool output for you to view** (unless `silent: true`). `save` is **strictly optional**: OMIT when you just want to look at page — downscaled image shown regardless, full-res capture written to temp file automatically. Pass `save` (a path) ONLY when deliberately need to keep full-res copy on disk for later use; `browser.screenshotDir` does same for every shot. NEVER invent `save` path for throwaway/temporal screenshot.
|
|
29
29
|
- `tab.extract(format = "markdown")` — Readability-extracted page content.
|
|
30
|
-
- Selectors accept CSS
|
|
31
|
-
- Default
|
|
30
|
+
- Selectors accept CSS plus puppeteer query handlers: `aria/Sign in`, `text/Continue`, `xpath/…`, `pierce/…`. Playwright-style `p-aria/[name="…"]`, `p-text/…` normalized.
|
|
31
|
+
- Default `tab.observe()` over `tab.screenshot()` for page state. Screenshot only when visual appearance matters.
|
|
32
32
|
</instruction>
|
|
33
33
|
|
|
34
34
|
<critical>
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
- After
|
|
38
|
-
- `code` runs with full Node access. Treat
|
|
35
|
+
- MUST call `open` before `run`. `run` does not implicitly create tab.
|
|
36
|
+
- NEVER screenshot just to "see what's on page" — `tab.observe()` returns structured data with element ids you can act on immediately.
|
|
37
|
+
- After `tab.goto()` or any navigation, prior element ids from `tab.observe()` invalidated. Re-observe before referencing them.
|
|
38
|
+
- `code` runs with full Node access. Treat as your code, not sandboxed code.
|
|
39
39
|
</critical>
|
|
40
40
|
|
|
41
41
|
<examples>
|
|
@@ -46,7 +46,10 @@ Drives a real Chromium tab with full puppeteer access via JS execution.
|
|
|
46
46
|
# Click an observed element by id
|
|
47
47
|
`{"action":"run","name":"docs","code":"const obs = await tab.observe(); const link = obs.elements.find(e => e.role === 'link' && e.name === 'Sign in'); assert(link, 'Sign in link missing'); await (await tab.id(link.id)).click();"}`
|
|
48
48
|
|
|
49
|
-
#
|
|
49
|
+
# Take a transient screenshot just to look at the page — NO save path needed; the image is shown to you
|
|
50
|
+
`{"action":"run","name":"docs","code":"await tab.screenshot();"}`
|
|
51
|
+
|
|
52
|
+
# Persist a full-page screenshot to disk (only when you deliberately need to keep the file)
|
|
50
53
|
`{"action":"run","name":"docs","code":"await tab.screenshot({ fullPage: true, save: 'screenshot.png' });"}`
|
|
51
54
|
|
|
52
55
|
# Fill and submit a form via selectors
|
|
@@ -66,5 +69,5 @@ Drives a real Chromium tab with full puppeteer access via JS execution.
|
|
|
66
69
|
</examples>
|
|
67
70
|
|
|
68
71
|
<output>
|
|
69
|
-
- Per call: any `display(value)` outputs (text/images) followed by
|
|
72
|
+
- Per call: any `display(value)` outputs (text/images) followed by JSON-stringified return value of `code` function. `run` always produces at least status line.
|
|
70
73
|
</output>
|
|
@@ -5,5 +5,5 @@ Parameters:
|
|
|
5
5
|
- `config` (optional): JSON render configuration (spacing and layout options).
|
|
6
6
|
Behavior:
|
|
7
7
|
- Returns ASCII diagram text.
|
|
8
|
-
- Saves full output to `artifact://<id>` when storage
|
|
9
|
-
- Returns error when Mermaid input
|
|
8
|
+
- Saves full output to `artifact://<id>` when storage available.
|
|
9
|
+
- Returns error when Mermaid input invalid or rendering fails.
|
|
@@ -2,7 +2,7 @@ Searches files using powerful regex matching.
|
|
|
2
2
|
|
|
3
3
|
<instruction>
|
|
4
4
|
- Supports Rust regex syntax (RE2-style — no lookaround or backreferences). Use line anchors or post-filters instead of (?!…)/(?<!…)
|
|
5
|
-
- `paths`
|
|
5
|
+
- `paths` accepts either one string or an array of files, directories, globs, or internal URLs. Optional: when omitted or empty it searches the workspace root (`.`). Prefer scoping to specific paths when you know them.
|
|
6
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>
|
|
@@ -38,6 +38,8 @@ Subagents have no conversation history. Every fact, file path, and direction the
|
|
|
38
38
|
- Pass large payloads via `local://<path>` URIs, not inline. {{#if contextEnabled}} (other than the context){{/if}}
|
|
39
39
|
{{#if contextEnabled}}- Put shared constraints in `context` once; do not duplicate across assignments.{{/if}}
|
|
40
40
|
- Prefer agents that investigate **and** edit in one pass; only spin a read-only discovery step when affected files are genuinely unknown.
|
|
41
|
+
- **Read-only agents**: Agents tagged READ-ONLY (e.g. `explore`) have no edit/write/command tools. NEVER hand them an assignment that requires changing files or running commands — they cannot do it and the turn is wasted. Use them to investigate and report back; do the edits yourself or delegate to a writing agent (`task`, `oracle`, `designer`).
|
|
42
|
+
- **No reasoning offload**: NEVER offload reasoning, analysis, design, or decision-making to `quick_task` or `explore` — they run minimal-effort / small models for mechanical lookups and data collection only. Keep judgment and synthesis in your own context; delegate hard thinking to `task`, `plan`, or `oracle`.
|
|
41
43
|
</rules>
|
|
42
44
|
|
|
43
45
|
<parallelization>
|
|
@@ -71,7 +73,7 @@ Parallel when tasks touch disjoint files or are independent refactors/tests.
|
|
|
71
73
|
Agent spawning is disabled for this context.
|
|
72
74
|
{{else}}
|
|
73
75
|
{{#list agents join="\n"}}
|
|
74
|
-
# {{name}}
|
|
76
|
+
# {{name}}{{#if readOnly}} — READ-ONLY (no edit/write/exec tools){{/if}}
|
|
75
77
|
{{description}}
|
|
76
78
|
{{/list}}
|
|
77
79
|
{{/if}}
|
|
@@ -50,9 +50,9 @@ Allowed `op` values are only `init`, `start`, `done`, `drop`, `rm`, `append`, an
|
|
|
50
50
|
</examples>
|
|
51
51
|
|
|
52
52
|
<critical>
|
|
53
|
-
When the user hands you a multi-step plan — a phased todo, a numbered or bulleted checklist, or "N bugs/items/tasks" to work through:
|
|
54
|
-
- You MUST `init` the list with EVERY item as its own task before doing the work.
|
|
55
|
-
- Enumerate all of them;
|
|
56
|
-
- NEVER summarize the plan into fewer tasks, sample "the important ones", drop items, or rely on memory to track the rest.
|
|
53
|
+
When the user hands you a multi-step plan — a phased todo, a numbered or bulleted checklist, or "N bugs/items/tasks" to work through:
|
|
54
|
+
- You MUST `init` the list with EVERY item as its own task before doing the work.
|
|
55
|
+
- Enumerate all of them;
|
|
56
|
+
- NEVER summarize the plan into fewer tasks, sample "the important ones", drop items, or rely on memory to track the rest.
|
|
57
57
|
The entire point is to remember every one.
|
|
58
|
-
</critical>
|
|
58
|
+
</critical>
|
package/src/sdk.ts
CHANGED
|
@@ -39,6 +39,7 @@ import { createAutoresearchExtension } from "./autoresearch";
|
|
|
39
39
|
import { loadCapability } from "./capability";
|
|
40
40
|
import { type Rule, ruleCapability, setActiveRules } from "./capability/rule";
|
|
41
41
|
import { bucketRules } from "./capability/rule-buckets";
|
|
42
|
+
import { shouldEnableAppendOnlyContext } from "./config/append-only-context-mode";
|
|
42
43
|
import { ModelRegistry } from "./config/model-registry";
|
|
43
44
|
import {
|
|
44
45
|
formatModelString,
|
|
@@ -101,7 +102,7 @@ import {
|
|
|
101
102
|
import { AgentSession } from "./session/agent-session";
|
|
102
103
|
import { resolveAuthBrokerConfig } from "./session/auth-broker-config";
|
|
103
104
|
import { AuthBrokerClient, AuthStorage, RemoteAuthCredentialStore } from "./session/auth-storage";
|
|
104
|
-
import { type CustomMessage, convertToLlm } from "./session/messages";
|
|
105
|
+
import { type CustomMessage, convertToLlm, wrapSteeringForModel } from "./session/messages";
|
|
105
106
|
import { getRestorableSessionModels, SessionManager } from "./session/session-manager";
|
|
106
107
|
import { closeAllConnections } from "./ssh/connection-manager";
|
|
107
108
|
import { unmountAll } from "./ssh/sshfs-mount";
|
|
@@ -646,24 +647,6 @@ function registerPythonCleanup(): void {
|
|
|
646
647
|
postmortem.register("python-cleanup", disposeAllKernelSessions);
|
|
647
648
|
}
|
|
648
649
|
|
|
649
|
-
/**
|
|
650
|
-
* Resolve whether to enable append-only context mode based on the setting and provider.
|
|
651
|
-
*
|
|
652
|
-
* - `"on"` → always enable
|
|
653
|
-
* - `"off"` → never enable
|
|
654
|
-
* - `"auto"` → enable for DeepSeek (prefix-caching provider)
|
|
655
|
-
*/
|
|
656
|
-
function resolveAppendOnlyMode(setting: "auto" | "on" | "off" | undefined, provider: string): boolean {
|
|
657
|
-
switch (setting ?? "auto") {
|
|
658
|
-
case "on":
|
|
659
|
-
return true;
|
|
660
|
-
case "off":
|
|
661
|
-
return false;
|
|
662
|
-
default:
|
|
663
|
-
return provider === "deepseek";
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
|
|
667
650
|
function customToolToDefinition(tool: CustomTool): ToolDefinition {
|
|
668
651
|
const definition: ToolDefinition & { [TOOL_DEFINITION_MARKER]: true } = {
|
|
669
652
|
name: tool.name,
|
|
@@ -1259,6 +1242,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1259
1242
|
getModelString: () => (hasExplicitModel && model ? formatModelString(model) : undefined),
|
|
1260
1243
|
getActiveModelString,
|
|
1261
1244
|
getPlanModeState: () => session?.getPlanModeState(),
|
|
1245
|
+
getPlanReferencePath: () => session?.getPlanReferencePath() ?? "local://PLAN.md",
|
|
1262
1246
|
getGoalModeState: () => session?.getGoalModeState(),
|
|
1263
1247
|
getGoalRuntime: () => session?.goalRuntime,
|
|
1264
1248
|
getUsageStatistics: () => sessionManager.getUsageStatistics(),
|
|
@@ -1904,7 +1888,8 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1904
1888
|
return obfuscateMessages(obfuscator, converted);
|
|
1905
1889
|
};
|
|
1906
1890
|
const transformContext = async (messages: AgentMessage[], _signal?: AbortSignal) => {
|
|
1907
|
-
|
|
1891
|
+
const withContext = await extensionRunner.emitContext(messages);
|
|
1892
|
+
return wrapSteeringForModel(withContext);
|
|
1908
1893
|
};
|
|
1909
1894
|
const onPayload = async (payload: unknown, _model?: Model) => {
|
|
1910
1895
|
return await extensionRunner.emitBeforeProviderRequest(payload);
|
|
@@ -2027,7 +2012,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2027
2012
|
getToolChoice: () => session?.nextToolChoice(),
|
|
2028
2013
|
telemetry: options.telemetry,
|
|
2029
2014
|
appendOnlyContext: model
|
|
2030
|
-
?
|
|
2015
|
+
? shouldEnableAppendOnlyContext(settings.get("provider.appendOnlyContext"), model)
|
|
2031
2016
|
? new AppendOnlyContextManager()
|
|
2032
2017
|
: undefined
|
|
2033
2018
|
: undefined,
|
|
@@ -100,6 +100,7 @@ import { type AsyncJob, type AsyncJobDeliveryState, AsyncJobManager } from "../a
|
|
|
100
100
|
import { classifyDifficulty } from "../auto-thinking/classifier";
|
|
101
101
|
import { reset as resetCapabilities } from "../capability";
|
|
102
102
|
import type { Rule } from "../capability/rule";
|
|
103
|
+
import { shouldEnableAppendOnlyContext } from "../config/append-only-context-mode";
|
|
103
104
|
import { MODEL_ROLE_IDS, type ModelRegistry } from "../config/model-registry";
|
|
104
105
|
import {
|
|
105
106
|
extractExplicitThinkingSelector,
|
|
@@ -203,7 +204,7 @@ import type { CheckpointState } from "../tools/checkpoint";
|
|
|
203
204
|
import { outputMeta } from "../tools/output-meta";
|
|
204
205
|
import { normalizeLocalScheme, resolveToCwd } from "../tools/path-utils";
|
|
205
206
|
import { isAutoQaEnabled } from "../tools/report-tool-issue";
|
|
206
|
-
import { getLatestTodoPhasesFromEntries, type TodoItem, type TodoPhase } from "../tools/todo
|
|
207
|
+
import { getLatestTodoPhasesFromEntries, type TodoItem, type TodoPhase } from "../tools/todo";
|
|
207
208
|
import { ToolAbortError, ToolError } from "../tools/tool-errors";
|
|
208
209
|
import { clampTimeout } from "../tools/tool-timeouts";
|
|
209
210
|
import { parseCommandArgs } from "../utils/command-args";
|
|
@@ -232,7 +233,7 @@ import type {
|
|
|
232
233
|
SessionContext,
|
|
233
234
|
SessionManager,
|
|
234
235
|
} from "./session-manager";
|
|
235
|
-
import { getLatestCompactionEntry, getRestorableSessionModels } from "./session-manager";
|
|
236
|
+
import { EPHEMERAL_MODEL_CHANGE_ROLE, getLatestCompactionEntry, getRestorableSessionModels } from "./session-manager";
|
|
236
237
|
import type { ShakeMode, ShakeResult } from "./shake-types";
|
|
237
238
|
import { ToolChoiceQueue } from "./tool-choice-queue";
|
|
238
239
|
import { YieldQueue } from "./yield-queue";
|
|
@@ -1293,6 +1294,12 @@ export class AgentSession {
|
|
|
1293
1294
|
this.#standingResolveHandler = handler ?? undefined;
|
|
1294
1295
|
}
|
|
1295
1296
|
|
|
1297
|
+
#sessionSwitchReconciler: (() => Promise<void>) | undefined;
|
|
1298
|
+
|
|
1299
|
+
setSessionSwitchReconciler(reconciler: (() => Promise<void>) | null): void {
|
|
1300
|
+
this.#sessionSwitchReconciler = reconciler ?? undefined;
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1296
1303
|
/** Provider-scoped mutable state store for transport/session caches. */
|
|
1297
1304
|
get providerSessionState(): Map<string, ProviderSessionState> {
|
|
1298
1305
|
return this.#providerSessionState;
|
|
@@ -1807,21 +1814,21 @@ export class AgentSession {
|
|
|
1807
1814
|
if (toolName === "edit" && details?.path) {
|
|
1808
1815
|
this.#invalidateFileCacheForPath(details.path);
|
|
1809
1816
|
}
|
|
1810
|
-
if (toolName === "
|
|
1817
|
+
if (toolName === "todo" && !isError && Array.isArray(details?.phases)) {
|
|
1811
1818
|
this.setTodoPhases(details.phases);
|
|
1812
1819
|
}
|
|
1813
|
-
if (toolName === "
|
|
1820
|
+
if (toolName === "todo" && isError) {
|
|
1814
1821
|
const errorText = content?.find(part => part.type === "text")?.text;
|
|
1815
1822
|
const reminderText = [
|
|
1816
1823
|
"<system-reminder>",
|
|
1817
|
-
"
|
|
1818
|
-
errorText ? `Failure: ${errorText}` : "Failure:
|
|
1819
|
-
"Fix the todo payload and call
|
|
1824
|
+
"todo failed, so todo progress is not visible to the user.",
|
|
1825
|
+
errorText ? `Failure: ${errorText}` : "Failure: todo returned an error.",
|
|
1826
|
+
"Fix the todo payload and call todo again before continuing.",
|
|
1820
1827
|
"</system-reminder>",
|
|
1821
1828
|
].join("\n");
|
|
1822
1829
|
await this.sendCustomMessage(
|
|
1823
1830
|
{
|
|
1824
|
-
customType: "todo-
|
|
1831
|
+
customType: "todo-error-reminder",
|
|
1825
1832
|
content: reminderText,
|
|
1826
1833
|
display: false,
|
|
1827
1834
|
details: { toolName, errorText },
|
|
@@ -4639,6 +4646,7 @@ export class AgentSession {
|
|
|
4639
4646
|
this.agent.steer({
|
|
4640
4647
|
role: "user",
|
|
4641
4648
|
content,
|
|
4649
|
+
steering: true,
|
|
4642
4650
|
attribution: "user",
|
|
4643
4651
|
timestamp: Date.now(),
|
|
4644
4652
|
});
|
|
@@ -4994,7 +5002,7 @@ export class AgentSession {
|
|
|
4994
5002
|
// splice mutated canonical `#todoPhases` between tool calls, so the model
|
|
4995
5003
|
// observed phase totals shrinking ("5 → 4") after marking tasks done. The
|
|
4996
5004
|
// `tasks.todoClearDelay` setting is now inert; completed tasks survive
|
|
4997
|
-
// until the next explicit `
|
|
5005
|
+
// until the next explicit `todo` call removes them via `rm`/`drop`.
|
|
4998
5006
|
|
|
4999
5007
|
/**
|
|
5000
5008
|
* Abort current operation and wait for agent to become idle.
|
|
@@ -5225,7 +5233,11 @@ export class AgentSession {
|
|
|
5225
5233
|
* Validates API key, saves to session log but NOT to settings.
|
|
5226
5234
|
* @throws Error if no API key available for the model
|
|
5227
5235
|
*/
|
|
5228
|
-
async setModelTemporary(
|
|
5236
|
+
async setModelTemporary(
|
|
5237
|
+
model: Model,
|
|
5238
|
+
thinkingLevel?: ThinkingLevel,
|
|
5239
|
+
options?: { ephemeral?: boolean },
|
|
5240
|
+
): Promise<void> {
|
|
5229
5241
|
const previousEditMode = this.#resolveActiveEditMode();
|
|
5230
5242
|
const apiKey = await this.#modelRegistry.getApiKey(model, this.sessionId);
|
|
5231
5243
|
if (!apiKey) {
|
|
@@ -5234,7 +5246,10 @@ export class AgentSession {
|
|
|
5234
5246
|
|
|
5235
5247
|
this.#clearActiveRetryFallback();
|
|
5236
5248
|
this.#setModelWithProviderSessionReset(model);
|
|
5237
|
-
this.sessionManager.appendModelChange(
|
|
5249
|
+
this.sessionManager.appendModelChange(
|
|
5250
|
+
`${model.provider}/${model.id}`,
|
|
5251
|
+
options?.ephemeral ? EPHEMERAL_MODEL_CHANGE_ROLE : "temporary",
|
|
5252
|
+
);
|
|
5238
5253
|
this.settings.getStorage()?.recordModelUsage(`${model.provider}/${model.id}`);
|
|
5239
5254
|
|
|
5240
5255
|
// Apply explicit thinking level if given; otherwise prefer the model's
|
|
@@ -6553,16 +6568,16 @@ export class AgentSession {
|
|
|
6553
6568
|
return undefined;
|
|
6554
6569
|
}
|
|
6555
6570
|
|
|
6556
|
-
if (!this.#toolRegistry.has("
|
|
6557
|
-
logger.warn("Eager todo enforcement skipped because
|
|
6571
|
+
if (!this.#toolRegistry.has("todo")) {
|
|
6572
|
+
logger.warn("Eager todo enforcement skipped because todo is unavailable", {
|
|
6558
6573
|
activeToolNames: this.agent.state.tools.map(tool => tool.name),
|
|
6559
6574
|
});
|
|
6560
6575
|
return undefined;
|
|
6561
6576
|
}
|
|
6562
6577
|
|
|
6563
|
-
const
|
|
6564
|
-
if (!
|
|
6565
|
-
logger.warn("Eager todo enforcement skipped because the current model does not support forcing
|
|
6578
|
+
const todoToolChoice = buildNamedToolChoice("todo", this.model);
|
|
6579
|
+
if (!todoToolChoice) {
|
|
6580
|
+
logger.warn("Eager todo enforcement skipped because the current model does not support forcing todo", {
|
|
6566
6581
|
modelApi: this.model?.api,
|
|
6567
6582
|
modelId: this.model?.id,
|
|
6568
6583
|
});
|
|
@@ -6580,7 +6595,7 @@ export class AgentSession {
|
|
|
6580
6595
|
attribution: "agent",
|
|
6581
6596
|
timestamp: Date.now(),
|
|
6582
6597
|
},
|
|
6583
|
-
toolChoice:
|
|
6598
|
+
toolChoice: todoToolChoice,
|
|
6584
6599
|
};
|
|
6585
6600
|
}
|
|
6586
6601
|
/**
|
|
@@ -6682,7 +6697,7 @@ export class AgentSession {
|
|
|
6682
6697
|
if (!targetModel) return false;
|
|
6683
6698
|
|
|
6684
6699
|
try {
|
|
6685
|
-
await this.setModelTemporary(targetModel);
|
|
6700
|
+
await this.setModelTemporary(targetModel, undefined, { ephemeral: true });
|
|
6686
6701
|
logger.debug("Context promotion switched model on overflow", {
|
|
6687
6702
|
from: `${currentModel.provider}/${currentModel.id}`,
|
|
6688
6703
|
to: `${targetModel.provider}/${targetModel.id}`,
|
|
@@ -6735,8 +6750,8 @@ export class AgentSession {
|
|
|
6735
6750
|
*/
|
|
6736
6751
|
#syncAppendOnlyContext(model: Model | null | undefined): void {
|
|
6737
6752
|
const setting = this.settings.get("provider.appendOnlyContext") ?? "auto";
|
|
6753
|
+
const enable = shouldEnableAppendOnlyContext(setting, model);
|
|
6738
6754
|
const providerId = model?.provider;
|
|
6739
|
-
const enable = setting === "on" || (setting === "auto" && providerId === "deepseek");
|
|
6740
6755
|
const prev = this.#lastAppendOnlyResolution;
|
|
6741
6756
|
if (prev && prev.enable === enable && prev.providerId === providerId) return;
|
|
6742
6757
|
this.#lastAppendOnlyResolution = { enable, providerId };
|
|
@@ -7848,7 +7863,7 @@ export class AgentSession {
|
|
|
7848
7863
|
const nextThinkingLevel = selector.thinkingLevel ?? currentThinkingLevel;
|
|
7849
7864
|
|
|
7850
7865
|
this.#setModelWithProviderSessionReset(candidate);
|
|
7851
|
-
this.sessionManager.appendModelChange(`${candidate.provider}/${candidate.id}`,
|
|
7866
|
+
this.sessionManager.appendModelChange(`${candidate.provider}/${candidate.id}`, EPHEMERAL_MODEL_CHANGE_ROLE);
|
|
7852
7867
|
this.settings.getStorage()?.recordModelUsage(`${candidate.provider}/${candidate.id}`);
|
|
7853
7868
|
this.setThinkingLevel(nextThinkingLevel);
|
|
7854
7869
|
if (!this.#activeRetryFallback) {
|
|
@@ -7921,7 +7936,7 @@ export class AgentSession {
|
|
|
7921
7936
|
const thinkingToApply =
|
|
7922
7937
|
currentThinkingLevel === lastAppliedFallbackThinkingLevel ? originalThinkingLevel : currentThinkingLevel;
|
|
7923
7938
|
this.#setModelWithProviderSessionReset(primaryModel);
|
|
7924
|
-
this.sessionManager.appendModelChange(`${primaryModel.provider}/${primaryModel.id}`,
|
|
7939
|
+
this.sessionManager.appendModelChange(`${primaryModel.provider}/${primaryModel.id}`, EPHEMERAL_MODEL_CHANGE_ROLE);
|
|
7925
7940
|
this.settings.getStorage()?.recordModelUsage(`${primaryModel.provider}/${primaryModel.id}`);
|
|
7926
7941
|
this.setThinkingLevel(thinkingToApply);
|
|
7927
7942
|
this.#clearActiveRetryFallback();
|
|
@@ -8948,6 +8963,14 @@ export class AgentSession {
|
|
|
8948
8963
|
this.#resetMnemopiConversationTrackingIfMnemopi();
|
|
8949
8964
|
}
|
|
8950
8965
|
this.#reconnectToAgent();
|
|
8966
|
+
try {
|
|
8967
|
+
await this.#sessionSwitchReconciler?.();
|
|
8968
|
+
} catch (error) {
|
|
8969
|
+
logger.warn("Failed to reconcile session mode after switch", {
|
|
8970
|
+
targetSessionFile: sessionPath,
|
|
8971
|
+
error: String(error),
|
|
8972
|
+
});
|
|
8973
|
+
}
|
|
8951
8974
|
return true;
|
|
8952
8975
|
} catch (error) {
|
|
8953
8976
|
this.sessionManager.restoreState(previousSessionState);
|
|
@@ -209,10 +209,6 @@ CREATE TRIGGER IF NOT EXISTS history_ai AFTER INSERT ON history BEGIN
|
|
|
209
209
|
logger.debug("HistoryStorage FTS query failed, using substring only", { error: String(error) });
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
-
if (ftsRows.length >= safeLimit) {
|
|
213
|
-
return ftsRows.map(row => this.#toEntry(row));
|
|
214
|
-
}
|
|
215
|
-
|
|
216
212
|
// 2. Substring fallback (token-AND LIKE). Catches infix matches FTS5's
|
|
217
213
|
// prefix-only wildcard cannot reach (e.g. "mit" -> "commit"). Bounded
|
|
218
214
|
// by safeLimit, ordered by recency - no full-table load into JS.
|
|
@@ -227,27 +223,24 @@ CREATE TRIGGER IF NOT EXISTS history_ai AFTER INSERT ON history BEGIN
|
|
|
227
223
|
return subRows.map(row => this.#toEntry(row));
|
|
228
224
|
}
|
|
229
225
|
|
|
230
|
-
const
|
|
231
|
-
const merged: HistoryEntry[] = [];
|
|
226
|
+
const rowsById = new Map<number, HistoryRow>();
|
|
232
227
|
for (const row of ftsRows) {
|
|
233
|
-
|
|
234
|
-
seen.add(row.id);
|
|
235
|
-
merged.push(this.#toEntry(row));
|
|
228
|
+
rowsById.set(row.id, row);
|
|
236
229
|
}
|
|
237
230
|
for (const row of subRows) {
|
|
238
|
-
if (
|
|
239
|
-
if (seen.has(row.id)) continue;
|
|
240
|
-
seen.add(row.id);
|
|
241
|
-
merged.push(this.#toEntry(row));
|
|
231
|
+
if (!rowsById.has(row.id)) rowsById.set(row.id, row);
|
|
242
232
|
}
|
|
243
|
-
|
|
233
|
+
|
|
234
|
+
return [...rowsById.values()]
|
|
235
|
+
.sort((a, b) => b.created_at - a.created_at || b.id - a.id)
|
|
236
|
+
.slice(0, safeLimit)
|
|
237
|
+
.map(row => this.#toEntry(row));
|
|
244
238
|
}
|
|
245
239
|
|
|
246
240
|
/**
|
|
247
|
-
* IDs of the sessions whose stored prompts match `query`, ordered by
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
* resume picker with prompts that the 4KB session-list prefix never sees.
|
|
241
|
+
* IDs of the sessions whose stored prompts match `query`, ordered by prompt
|
|
242
|
+
* recency and de-duplicated. Used to augment session ranking in the resume
|
|
243
|
+
* picker with prompts that the 4KB session-list prefix never sees.
|
|
251
244
|
*/
|
|
252
245
|
matchingSessionIds(query: string, limit = 500): string[] {
|
|
253
246
|
const seen = new Set<string>();
|