@oh-my-pi/pi-coding-agent 15.11.4 → 15.11.7

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 (98) hide show
  1. package/CHANGELOG.md +82 -1
  2. package/dist/cli.js +520 -451
  3. package/dist/types/cli/bench-cli.d.ts +78 -0
  4. package/dist/types/cli/usage-cli.d.ts +10 -1
  5. package/dist/types/commands/bench.d.ts +29 -0
  6. package/dist/types/commands/usage.d.ts +9 -0
  7. package/dist/types/config/model-resolver.d.ts +3 -2
  8. package/dist/types/config/settings-schema.d.ts +125 -3
  9. package/dist/types/edit/renderer.d.ts +1 -0
  10. package/dist/types/modes/components/oauth-selector.d.ts +10 -1
  11. package/dist/types/modes/components/reset-usage-selector.d.ts +12 -0
  12. package/dist/types/modes/components/session-selector.d.ts +1 -1
  13. package/dist/types/modes/components/settings-selector.d.ts +8 -1
  14. package/dist/types/modes/components/snapcompact-shape-preview.d.ts +31 -0
  15. package/dist/types/modes/components/tool-execution.d.ts +18 -0
  16. package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
  17. package/dist/types/modes/interactive-mode.d.ts +10 -0
  18. package/dist/types/modes/session-observer-registry.d.ts +2 -0
  19. package/dist/types/modes/setup-wizard/scenes/sign-in.d.ts +3 -0
  20. package/dist/types/modes/setup-wizard/scenes/types.d.ts +10 -1
  21. package/dist/types/modes/setup-wizard/scenes/web-search.d.ts +3 -0
  22. package/dist/types/modes/types.d.ts +2 -0
  23. package/dist/types/modes/utils/context-usage.d.ts +6 -1
  24. package/dist/types/session/agent-session.d.ts +14 -1
  25. package/dist/types/session/auth-storage.d.ts +1 -1
  26. package/dist/types/session/codex-auto-reset.d.ts +107 -0
  27. package/dist/types/session/snapcompact-inline.d.ts +107 -4
  28. package/dist/types/slash-commands/helpers/reset-usage.d.ts +27 -0
  29. package/dist/types/task/render.d.ts +1 -0
  30. package/dist/types/tools/bash.d.ts +2 -0
  31. package/dist/types/tools/eval-render.d.ts +1 -0
  32. package/dist/types/tools/renderers.d.ts +13 -0
  33. package/dist/types/tools/ssh.d.ts +1 -0
  34. package/dist/types/tools/todo.d.ts +0 -11
  35. package/package.json +11 -11
  36. package/src/cli/bench-cli.ts +437 -0
  37. package/src/cli/usage-cli.ts +187 -16
  38. package/src/cli-commands.ts +1 -0
  39. package/src/commands/bench.ts +42 -0
  40. package/src/commands/usage.ts +8 -0
  41. package/src/config/model-registry.ts +52 -5
  42. package/src/config/model-resolver.ts +36 -5
  43. package/src/config/settings-schema.ts +148 -3
  44. package/src/config/settings.ts +9 -0
  45. package/src/edit/renderer.ts +5 -0
  46. package/src/hindsight/client.ts +26 -1
  47. package/src/hindsight/state.ts +6 -2
  48. package/src/internal-urls/docs-index.generated.ts +2 -2
  49. package/src/mcp/transports/stdio.ts +81 -7
  50. package/src/modes/components/oauth-selector.ts +67 -7
  51. package/src/modes/components/reset-usage-selector.ts +161 -0
  52. package/src/modes/components/session-selector.ts +8 -2
  53. package/src/modes/components/settings-selector.ts +89 -47
  54. package/src/modes/components/snapcompact-shape-preview-doc.md +11 -0
  55. package/src/modes/components/snapcompact-shape-preview.ts +192 -0
  56. package/src/modes/components/tool-execution.ts +26 -0
  57. package/src/modes/components/transcript-container.ts +23 -1
  58. package/src/modes/controllers/command-controller.ts +24 -1
  59. package/src/modes/controllers/input-controller.ts +8 -6
  60. package/src/modes/controllers/selector-controller.ts +72 -2
  61. package/src/modes/interactive-mode.ts +83 -0
  62. package/src/modes/session-observer-registry.ts +61 -3
  63. package/src/modes/setup-wizard/index.ts +1 -0
  64. package/src/modes/setup-wizard/scenes/glyph.ts +24 -6
  65. package/src/modes/setup-wizard/scenes/providers.ts +36 -2
  66. package/src/modes/setup-wizard/scenes/sign-in.ts +10 -1
  67. package/src/modes/setup-wizard/scenes/theme.ts +28 -1
  68. package/src/modes/setup-wizard/scenes/types.ts +10 -1
  69. package/src/modes/setup-wizard/scenes/web-search.ts +22 -6
  70. package/src/modes/setup-wizard/wizard-overlay.ts +38 -1
  71. package/src/modes/theme/theme.ts +2 -2
  72. package/src/modes/types.ts +2 -0
  73. package/src/modes/utils/context-usage.ts +75 -1
  74. package/src/prompts/bench.md +7 -0
  75. package/src/prompts/system/snapcompact-context-frames-note.md +1 -0
  76. package/src/prompts/system/snapcompact-context-stub.md +1 -0
  77. package/src/prompts/system/snapcompact-toolresult-note.md +1 -1
  78. package/src/prompts/tools/browser.md +33 -43
  79. package/src/prompts/tools/eval.md +27 -50
  80. package/src/prompts/tools/irc.md +29 -31
  81. package/src/prompts/tools/read.md +31 -37
  82. package/src/prompts/tools/todo.md +1 -2
  83. package/src/sdk.ts +4 -2
  84. package/src/session/agent-session.ts +136 -6
  85. package/src/session/auth-storage.ts +3 -0
  86. package/src/session/codex-auto-reset.ts +190 -0
  87. package/src/session/snapcompact-inline.ts +404 -75
  88. package/src/slash-commands/builtin-registry.ts +145 -8
  89. package/src/slash-commands/helpers/context-report.ts +28 -1
  90. package/src/slash-commands/helpers/reset-usage.ts +66 -0
  91. package/src/slash-commands/helpers/usage-report.ts +12 -0
  92. package/src/task/index.ts +30 -7
  93. package/src/task/render.ts +34 -19
  94. package/src/tools/bash.ts +3 -0
  95. package/src/tools/eval-render.ts +4 -0
  96. package/src/tools/renderers.ts +13 -0
  97. package/src/tools/ssh.ts +3 -0
  98. package/src/tools/todo.ts +8 -128
@@ -1,55 +1,53 @@
1
1
  Sends short text messages to other agents in this process and receives theirs.
2
2
 
3
3
  <instruction>
4
- - The main agent is addressable as `Main`. Subagents reuse their task id (e.g. `AuthLoader`, or `AuthLoader-2` when the name repeats).
5
- - `op: "list"` — every addressable peer with status (`running` | `idle` | `parked`), unread count, parent, and last activity. Use it before sending if you are not sure who exists.
6
- - `op: "send"` — fire-and-forget delivery of `message` to `to` (a peer id, or `"all"` to broadcast to live peers). Returns per-recipient receipts immediately; it NEVER waits for the recipient to act. Receipt outcomes: `injected` (recipient was mid-turn; message folded in at their next step boundary), `woken` (idle recipient started a turn), `revived` (parked recipient was brought back and woken), `failed`.
7
- - Messaging an `idle` or `parked` peer is how you wake it — there is no separate revive call.
8
- - `send` with `await: true` — convenience round-trip: send, then block until the next message from that peer arrives (or the timeout passes). Invalid with `to: "all"`.
9
- - `op: "wait"` — block until a message arrives (optionally only `from` a specific peer); consumes and returns it. A timeout is a clean "no message" result, not an error.
10
- - `op: "inbox"` — drain pending messages without blocking (`peek: true` to leave them unread).
11
- - `replyTo` — set it to the id of the message you are answering so the sender can correlate.
12
- - Nobody answers on a peer's behalf — a reply normally arrives only when the recipient sends one — with one exception: `send` with `await: true` to a peer that is mid-turn and cannot reach a step boundary (async execution disabled, e.g. blocked in a synchronous task spawn) gets a side-channel auto-reply generated from that peer's context. For background on what a peer has been doing, `read` `history://<id>` instead of interrogating them.
4
+ - Main agent is `Main`; subagents reuse their task id (`AuthLoader`, or `AuthLoader-2` when the name repeats).
5
+ - `op: "list"` — peers with status (`running` | `idle` | `parked`), unread count, parent, last activity. Use when unsure who exists.
6
+ - `op: "send"` — fire-and-forget `message` to `to` (peer id, or `"all"` to broadcast to live peers). Returns per-recipient receipts immediately; NEVER waits for the recipient to act. Outcomes: `injected` (mid-turn; folded in at next step boundary), `woken` (idle peer started a turn), `revived` (parked peer brought back and woken), `failed`.
7
+ - Messaging an `idle`/`parked` peer is how you wake it — there is no separate revive call.
8
+ - `send` + `await: true` — round-trip: send, then block until that peer's next message (or timeout). Invalid with `to: "all"`.
9
+ - `op: "wait"` — block until a message arrives (optionally only `from` one peer); consumes and returns it. Timeout = clean "no message", not an error.
10
+ - `op: "inbox"` — drain pending messages without blocking (`peek: true` leaves them unread).
11
+ - `replyTo` — id of the message you are answering, so the sender can correlate.
12
+ - Replies arrive only when the recipient sends one. Exception: `await: true` to a peer stuck mid-turn (async execution disabled, e.g. blocked in a synchronous task spawn) gets a side-channel auto-reply from its context. For background on a peer, `read` `history://<id>` instead of interrogating it.
13
13
  </instruction>
14
14
 
15
15
  <when_to_use>
16
- You SHOULD reach for `irc` proactively when continuing alone is wasteful or wrong. When in doubt, prefer messaging.
17
- - **Unexpected state.** The task did not describe what you found — missing file, config contradicting the assignment, API or tool behaving differently than told. DM `Main` (or the spawning agent) instead of guessing.
18
- - **Blocked by another agent.** A peer holds the file/branch/resource you need, 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 work.
19
- - **Decision points outside your scope.** A genuine fork the assignment did not pre-decide (e.g. which of two viable APIs, whether to refactor adjacent code). Ask the requester rather than picking unilaterally.
20
- - **Coordination opportunities.** A peer's in-flight work would benefit from yours, or vice-versa.
16
+ Reach for `irc` proactively when continuing alone is wasteful or wrong; when in doubt, message.
17
+ - **Unexpected state** — missing file, config contradicting the assignment, API/tool behaving differently than told. DM `Main` (or your spawner) instead of guessing.
18
+ - **Blocked by another agent** a peer holds the file/branch/resource or decision you need, or started the change you're about to make. DM them (or broadcast to discover who) before duplicating work.
19
+ - **Decision outside your scope** a genuine fork the assignment didn't pre-decide. Ask the requester rather than picking unilaterally.
20
+ - **Coordination** a peer's in-flight work would benefit from yours, or vice-versa.
21
21
 
22
- NEVER use `irc` for: routine progress updates, things a tool call can verify, or questions already answered by your assignment / repo / docs.
22
+ NEVER for: routine progress updates, things a tool call can verify, questions your assignment/repo/docs already answer.
23
23
  </when_to_use>
24
24
 
25
25
  <etiquette>
26
- These rules apply to both sending and replying.
27
- - **Plain prose only.** NEVER send structured JSON status payloads (e.g. `{"type":"task_completed",…}`). Write a normal sentence: "Done with the auth refactor — left a TODO in `src/server/auth.ts` for the rate limiter."
28
- - **NEVER quote the message you are replying to.** Lead with the answer; set `replyTo` instead.
29
- - **Use IRC, not terminal tools, to learn about peers.** NEVER `grep` artifacts, read other sessions' JSONL files, or shell-poke to figure out what another agent is doing. DM them, or `read` `history://<id>`.
30
- - **Send, then keep working.** `send` returns immediately — only `wait` (or `await: true`) when you genuinely cannot proceed without the answer. NEVER follow up with "did you get my message?"; a `failed` receipt means the peer is unreachable — move on or report the blocker; NEVER retry in a loop.
31
- - **Answer when a response is expected.** When an incoming message asks something, reply with `irc send` to the sender (you may finish your current step first).
32
- - **Stay terse.** A DM is a chat message, not a memo. One question per send. Share file paths and artifacts via `local://` / `memory://` / `artifact://` URLs instead of pasting blobs.
33
- - **Address peers by id.** Use the exact id from `op: "list"` (e.g. `AuthLoader`, `Main`). NEVER invent friendly names.
34
- - **NEVER IRC for things a tool would answer.** If a `read`, `grep`, or build command resolves the question, do that first.
26
+ Applies to sending and replying.
27
+ - **Plain prose only.** NEVER JSON status payloads like `{"type":"task_completed",…}` write a normal sentence.
28
+ - **NEVER quote the message you answer.** Lead with the answer; set `replyTo`.
29
+ - **Learn about peers via IRC** NEVER grep artifacts, read other sessions' JSONL, or shell-poke. DM them, or `read` `history://<id>`.
30
+ - **Send, then keep working.** `wait`/`await: true` only when you genuinely cannot proceed. NEVER "did you get my message?". A `failed` receipt = peer unreachable — move on; NEVER retry in a loop.
31
+ - **Answer expected questions** via `irc send` to the sender (finishing your current step first is fine).
32
+ - **Stay terse.** One question per send; share files via `local://`/`memory://`/`artifact://` URLs, never pasted blobs.
33
+ - **Address peers by exact id** from `op: "list"` (e.g. `AuthLoader`, `Main`). NEVER invent friendly names.
34
+ - **NEVER IRC what a tool answers.** A `read`, grep, or build resolves it? Do that first.
35
35
  </etiquette>
36
36
 
37
37
  <output>
38
- - `send`: per-recipient delivery receipts (`injected` / `woken` / `revived` / `failed`); with `await: true`, also the reply (or a timeout notice).
38
+ - `send`: per-recipient receipts; with `await: true`, also the reply (or timeout notice).
39
39
  - `wait`: the consumed message, or a clean timeout notice.
40
40
  - `inbox`: pending messages, oldest first.
41
- - `list`: peers with status, unread count, parent, and last activity.
41
+ - `list`: peers with status, unread count, parent, last activity.
42
42
  </output>
43
43
 
44
44
  <examples>
45
45
  # List peers
46
46
  `{"op": "list"}`
47
- # Fire-and-forget DM — keep working, check inbox later
48
- `{"op": "send", "to": "AuthLoader", "message": "Are you still touching src/server/auth.ts? I need to add a 401 path."}`
47
+ # Fire-and-forget DM — same send wakes idle/parked peers
48
+ `{"op": "send", "to": "AuthLoader", "message": "Still touching src/server/auth.ts? I need to add a 401 path."}`
49
49
  # Round-trip when you cannot proceed without the answer
50
- `{"op": "send", "to": "Main", "message": "Should I prefer JWT or session cookies for the auth flow?", "await": true}`
51
- # Wake a parked agent (same send — the bus revives it)
52
- `{"op": "send", "to": "SchemaMigrator", "message": "The users table changed again; please re-check your migration."}`
50
+ `{"op": "send", "to": "Main", "message": "JWT or session cookies for the auth flow?", "await": true}`
53
51
  # Block until a specific peer answers
54
52
  `{"op": "wait", "from": "AuthLoader", "timeoutMs": 60000}`
55
53
  # Drain pending messages
@@ -1,84 +1,78 @@
1
1
  Read files, directories, archives, SQLite databases, images, documents, internal resources, and web URLs through a single `path` string.
2
2
 
3
3
  <instruction>
4
- - One tool for filesystem, archives, SQLite, images, documents (PDF/DOCX/PPTX/XLSX/RTF/EPUB/ipynb), internal URIs, and web URLs (reader-mode by default).
5
4
  - You SHOULD parallelize independent reads when exploring related files.
6
- - You SHOULD reach for `read` — not a browser/puppeteer tool — for fetching web content.
5
+ - You SHOULD reach for `read` — not a browser/puppeteer tool — for web content; browser only when `read` cannot deliver it.
7
6
  </instruction>
8
7
 
9
8
  ## Parameters
10
9
 
11
- - `path` — required. Local path, internal URI (`skill://`, `agent://`, `artifact://`, `history://`, `memory://`, `rule://`, `local://`, `vault://`, `mcp://`, `omp://`, `issue://`, `pr://`), or URL. Append `:<sel>` for line ranges, raw mode, or special modes (e.g. `src/foo.ts:50-200`, `src/foo.ts:raw`, `db.sqlite:users:42`).
10
+ - `path` — required. Local path, internal URI (`skill://`, `agent://`, `artifact://`, `history://`, `memory://`, `rule://`, `local://`, `vault://`, `mcp://`, `omp://`, `issue://`, `pr://`), or URL. Append `:<sel>` for line ranges or special modes (e.g. `src/foo.ts:50-200`, `src/foo.ts:raw`, `db.sqlite:users:42`).
12
11
 
13
12
  ## Selectors
14
13
 
15
- Append `:<sel>` to `path`. The bare path falls back to the default mode.
14
+ Append `:<sel>` to `path`; bare path = default mode.
16
15
 
17
- - _(none)_ — parseable code → structural summary (signatures kept, bodies elided); other files → read from the start (up to {{DEFAULT_LIMIT}} lines).
18
- - `:50` / `:50-` — read from line 50 onward.
16
+ - _(none)_ — parseable code → structural summary; other files → from start (up to {{DEFAULT_LIMIT}} lines).
17
+ - `:50` / `:50-` — from line 50 onward.
19
18
  - `:50-200` — lines 50–200 inclusive.
20
- - `:50+150` — 150 lines starting at line 50.
21
- - `:20+1` — anchor on line 20 (single-range reads expand by ≤1 leading and ≤3 trailing context lines).
22
- - `:5-16,960-973` — multiple ranges in one call (sorted, overlaps merged). Multi-range mode returns exact bounds with no context padding.
23
- - `:raw` — verbatim text; no anchors, no summary, no line prefixes.
24
- - `:2-4:raw` or `:raw:2-4` — range AND verbatim; the two compose in either order.
25
- - `:conflicts` — one-line-per-block index of every unresolved git merge conflict.
19
+ - `:50+150` — 150 lines starting at 50.
20
+ - `:20+1` — anchor line 20 (single-range reads pad ≤1 leading / ≤3 trailing context lines).
21
+ - `:5-16,960-973` — multiple ranges in one call (sorted, overlaps merged); exact bounds, no padding.
22
+ - `:raw` — verbatim; no anchors, no summary, no line prefixes.
23
+ - `:2-4:raw` / `:raw:2-4` — range AND verbatim; compose in either order.
24
+ - `:conflicts` — one line per unresolved git merge conflict block.
26
25
 
27
26
  # Files
28
27
 
29
- - Reading a directory path returns a depth-limited dirent listing.
28
+ - Directory path depth-limited dirent listing.
30
29
  {{#if IS_HL_MODE}}
31
- - Reading a file with an explicit selector emits a file snapshot tag header and numbered lines: `[src/foo.ts#1A2B]` then `41:def alpha():`. Copy the `[PATH#TAG]` header for anchored edits; ops use bare line numbers. NEVER fabricate the tag.
30
+ - File with explicit selector snapshot tag header + numbered lines: `[src/foo.ts#1A2B]` then `41:def alpha():`. Copy the `[PATH#TAG]` header for anchored edits; ops use bare line numbers. NEVER fabricate the tag.
32
31
  {{else}}
33
32
  {{#if IS_LINE_NUMBER_MODE}}
34
- - Reading a file with an explicit selector returns lines prefixed with line numbers: `41|def alpha():`.
33
+ - File with explicit selector lines prefixed with numbers: `41|def alpha():`.
35
34
  {{/if}}
36
35
  {{/if}}
37
- - Parseable code without a selector returns a **structural summary**: declarations kept, large bodies collapsed to `..` (merged brace pair) or `…` (standalone). Summarized output ends with a footer demonstrating the multi-range selector you can use to recover the elided bodies, e.g.:
38
-
39
- `[NN lines elided; re-read needed ranges, e.g. <path>:5-16,40-80]`
40
-
41
- Re-issue **only the relevant range(s)** using the multi-range selector (e.g. `<path>:5-16,120-200`). NEVER guess what's inside `..` / `…` — those markers carry no content. NEVER re-read the whole file or use `:raw` when targeted ranges suffice.
36
+ - Parseable code without selector **structural summary**: declarations kept, bodies collapsed to `..` (merged brace pair) or `…` (standalone). The footer shows the recovery selector: `[NN lines elided; re-read needed ranges, e.g. <path>:5-16,40-80]`. Re-issue ONLY the ranges you need via the multi-range selector. `..`/`…` carry no content NEVER guess what's inside; NEVER re-read the whole file or `:raw` when ranges suffice.
42
37
 
43
38
  # Documents & Notebooks
44
39
 
45
- Extracts text from PDF, Word, PowerPoint, Excel, RTF, and EPUB. Notebooks (`.ipynb`) are shown as editable `# %% [type] cell:N` text; edits round-trip back to the underlying JSON preserving notebook metadata. Add `:raw` to a notebook to bypass the converter and read the JSON directly.
40
+ PDF, Word, PowerPoint, Excel, RTF, EPUB → extracted text. Notebooks (`.ipynb`) editable `# %% [type] cell:N` text; edits round-trip to the underlying JSON preserving metadata. `:raw` bypasses the converter.
46
41
 
47
42
  # Images
48
43
 
49
44
  {{#if INSPECT_IMAGE_ENABLED}}
50
- Reading an image path returns metadata (mime, bytes, dimensions, channels, alpha). For actual visual analysis, call `inspect_image` with the path and a question describing what to inspect.
45
+ Image path metadata (mime, bytes, dimensions, channels, alpha). For visual analysis, call `inspect_image` with the path and a question.
51
46
  {{else}}
52
- Reading an image path returns the decoded image inline (PNG, JPEG, GIF, WEBP) for direct visual analysis.
47
+ Image path decoded image inline (PNG, JPEG, GIF, WEBP) for direct visual analysis.
53
48
  {{/if}}
54
49
 
55
50
  # Archives
56
51
 
57
- Supports `.tar`, `.tar.gz`, `.tgz`, `.zip`. Use `archive.ext:path/inside/archive` to read a member, and append a normal selector to the inner path: `archive.zip:dir/file.ts:50-60`.
52
+ `.tar`, `.tar.gz`, `.tgz`, `.zip`. `archive.ext:path/inside/archive` reads a member; inner paths take normal selectors: `archive.zip:dir/file.ts:50-60`.
58
53
 
59
54
  # SQLite
60
55
 
61
56
  For `.sqlite`, `.sqlite3`, `.db`, `.db3`:
62
- - `file.db` — list tables with row counts
57
+ - `file.db` — tables with row counts
63
58
  - `file.db:table` — schema + sample rows
64
- - `file.db:table:key` — single row by primary key
65
- - `file.db:table?limit=50&offset=100` — paginated rows
66
- - `file.db:table?where=status='active'&order=created:desc` — filtered rows
67
- - `file.db?q=SELECT …` — read-only SELECT query
59
+ - `file.db:table:key` — row by primary key
60
+ - `file.db:table?limit=50&offset=100` — pagination
61
+ - `file.db:table?where=status='active'&order=created:desc` — filter/order
62
+ - `file.db?q=SELECT …` — read-only SELECT
68
63
 
69
64
  # URLs
70
65
 
71
- - Default reader-mode: HTML pages, GitHub issues/PRs, Stack Overflow, Wikipedia, Reddit, NPM, arXiv, RSS/Atom, JSON endpoints, PDFs → clean text/markdown.
72
- - `:raw` returns untouched HTML; line selectors (`:50`, `:50-100`, `:50+150`) paginate the cached fetched output.
73
- - Bare `host:port` URLs collide with the selector grammar — add a trailing slash before the selector: `https://example.com/:80`.
66
+ - Reader-mode by default: HTML, GitHub issues/PRs, Stack Overflow, Wikipedia, Reddit, NPM, arXiv, RSS/Atom, JSON endpoints, PDFs → clean text/markdown.
67
+ - `:raw` untouched HTML; line selectors (`:50`, `:50-100`, `:50+150`) paginate the cached fetch.
68
+ - Bare `host:port` collides with the selector grammar — add a trailing slash: `https://example.com/:80`.
74
69
 
75
70
  # Internal URIs
76
71
 
77
- `skill://<name>`, `agent://<id>`, `artifact://<id>`, `history://<agentId>`, `memory://root`, `rule://<name>`, `local://<name>.md`, `vault://<vault>/<path>`, `mcp://<uri>`, `omp://<doc>.md`, `issue://<N>`, and `pr://<N>` resolve transparently and accept the same line selectors as filesystem paths. Use `artifact://<id>` to recover full output that a previous bash/eval/tool result spilled or truncated. `history://<agentId>` is an agent's transcript as concise markdown; bare `history://` lists agents.
72
+ All `path` URI schemes resolve transparently and take the same line selectors. `artifact://<id>` recovers full output a previous bash/eval/tool result spilled or truncated. `history://<agentId>` is an agent's transcript as concise markdown; bare `history://` lists agents.
78
73
 
79
74
  <critical>
80
- - You MUST use `read` for every file, directory, archive, and URL inspection. `cat`, `head`, `tail`, `less`, `more`, `ls`, `tar`, `unzip`, `curl`, `wget` are FORBIDDEN — any such bash call is a bug, regardless of how short or convenient it looks.
81
- - You MUST prefer `read` over a browser/puppeteer tool for URL content; only reach for a browser when `read` cannot deliver reasonable content.
82
- - For line ranges, append the selector to `path` (`path="src/foo.ts:50-200"`, `path="src/foo.ts:50+150"`). NEVER substitute `sed -n`, `awk NR`, or `head`/`tail` pipelines.
83
- - Summary footer names ranges to re-read? Re-issue ONLY the ranges you need via the multi-range selector. NEVER guess what's inside `..` / `…` markers — they carry no content.
75
+ - You MUST use `read` for every file, directory, archive, and URL inspection. `cat`, `head`, `tail`, `less`, `more`, `ls`, `tar`, `unzip`, `curl`, `wget` are FORBIDDEN bash calls, however short or convenient.
76
+ - Line ranges go in the selector (`path="src/foo.ts:50-200"`) NEVER `sed -n`, `awk NR`, or `head`/`tail` pipelines.
77
+ - Summary footer names elided ranges? Re-issue ONLY those ranges. NEVER guess `..`/`…` content.
84
78
  </critical>
@@ -2,7 +2,7 @@
2
2
 
3
3
  Manages a phased task list. Pass `ops`: a flat array of operations.
4
4
  The next pending task is auto-promoted to `in_progress` after each completion.
5
- Allowed `op` values are only `init`, `start`, `done`, `drop`, `rm`, `append`, `note`, and `view`. `pending` is a task status, not an `op`; leave not-yet-started tasks implicit in `init`/`append` lists.
5
+ Allowed `op` values are only `init`, `start`, `done`, `drop`, `rm`, `append`, and `view`. `pending` is a task status, not an `op`; leave not-yet-started tasks implicit in `init`/`append` lists.
6
6
 
7
7
  ## Operations
8
8
 
@@ -14,7 +14,6 @@ Allowed `op` values are only `init`, `start`, `done`, `drop`, `rm`, `append`, `n
14
14
  |`drop`|`task` or `phase`|Mark abandoned|
15
15
  |`rm`|`task` or `phase` (optional)|Remove task or phase's tasks; omit both to clear the entire list|
16
16
  |`append`|`phase`, `items: string[]`|Append tasks to `phase`; lazily creates phase|
17
- |`note`|`task`, `text`|Append a note to a task. Reminders for future-you only.|
18
17
  |`view`|—|Read-only: echo the current list without modifying it|
19
18
 
20
19
  ## Anatomy
package/src/sdk.ts CHANGED
@@ -2162,11 +2162,13 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
2162
2162
  // Per-request provider-context transforms. Obfuscate FIRST so secrets are
2163
2163
  // redacted from text before snapcompact rasterizes it into PNG frames.
2164
2164
  // Both operate on the transient outgoing Context only — never persisted.
2165
+ const snapcompactSystemPromptMode = settings.get("snapcompact.systemPrompt");
2165
2166
  const snapcompactInline =
2166
- settings.get("snapcompact.systemPrompt") || settings.get("snapcompact.toolResults")
2167
+ snapcompactSystemPromptMode !== "none" || settings.get("snapcompact.toolResults")
2167
2168
  ? new SnapcompactInlineTransformer({
2168
- renderSystemPrompt: settings.get("snapcompact.systemPrompt"),
2169
+ renderSystemPrompt: snapcompactSystemPromptMode,
2169
2170
  renderToolResults: settings.get("snapcompact.toolResults"),
2171
+ shape: settings.get("snapcompact.shape"),
2170
2172
  })
2171
2173
  : undefined;
2172
2174
  const transformProviderContext =
@@ -73,6 +73,9 @@ import type {
73
73
  Model,
74
74
  ProviderResponseMetadata,
75
75
  ProviderSessionState,
76
+ ResetCreditAccountStatus,
77
+ ResetCreditRedeemOutcome,
78
+ ResetCreditTarget,
76
79
  ServiceTier,
77
80
  SimpleStreamOptions,
78
81
  TextContent,
@@ -237,6 +240,7 @@ import { normalizeModelContextImages } from "../utils/image-loading";
237
240
  import { buildNamedToolChoice } from "../utils/tool-choice";
238
241
  import type { AuthStorage } from "./auth-storage";
239
242
  import type { ClientBridge, ClientBridgePermissionOption, ClientBridgePermissionOutcome } from "./client-bridge";
243
+ import { defaultCodexAutoRedeemCoordinator, evaluateCodexAutoRedeem } from "./codex-auto-reset";
240
244
  import {
241
245
  type BashExecutionMessage,
242
246
  type CustomMessage,
@@ -5399,11 +5403,7 @@ export class AgentSession {
5399
5403
  #cloneTodoPhases(phases: TodoPhase[]): TodoPhase[] {
5400
5404
  return phases.map(phase => ({
5401
5405
  name: phase.name,
5402
- tasks: phase.tasks.map(task => {
5403
- const out: TodoItem = { content: task.content, status: task.status };
5404
- if (task.notes && task.notes.length > 0) out.notes = [...task.notes];
5405
- return out;
5406
- }),
5406
+ tasks: phase.tasks.map(task => ({ content: task.content, status: task.status })),
5407
5407
  }));
5408
5408
  }
5409
5409
 
@@ -6388,6 +6388,10 @@ export class AgentSession {
6388
6388
  const snapcompactResult = await snapcompact.compact(preparation, {
6389
6389
  convertToLlm,
6390
6390
  model: this.model,
6391
+ shape: snapcompact.resolveShape(this.model, this.settings.get("snapcompact.shape")),
6392
+ // Providers with hard image caps (OpenRouter: 8) silently drop
6393
+ // frames past the cap — keep the archive within budget.
6394
+ maxFrames: snapcompact.providerFrameBudget(this.model.provider),
6391
6395
  });
6392
6396
  summary = snapcompactResult.summary;
6393
6397
  shortSummary = snapcompactResult.shortSummary;
@@ -7921,6 +7925,7 @@ export class AgentSession {
7921
7925
  const snapcompactResult = await snapcompact.compact(preparation, {
7922
7926
  convertToLlm,
7923
7927
  model: this.model,
7928
+ maxFrames: snapcompact.providerFrameBudget(this.model?.provider),
7924
7929
  });
7925
7930
  summary = snapcompactResult.summary;
7926
7931
  shortSummary = snapcompactResult.shortSummary;
@@ -8354,7 +8359,8 @@ export class AgentSession {
8354
8359
 
8355
8360
  return (
8356
8361
  /\bItem with id ['"][^'"]+['"] not found\.?/i.test(errorMessage) ||
8357
- (/previous[ _]?response/i.test(errorMessage) && /not[ _]?found|invalid|expired|stale/i.test(errorMessage))
8362
+ (/previous[ _]?response/i.test(errorMessage) &&
8363
+ /not[ _]?found|invalid|expired|stale|zero[ _-]?data[ _-]?retention/i.test(errorMessage))
8358
8364
  );
8359
8365
  }
8360
8366
 
@@ -8720,6 +8726,13 @@ export class AgentSession {
8720
8726
  if (outcome.switched) {
8721
8727
  switchedCredential = true;
8722
8728
  delayMs = 0;
8729
+ } else if (await this.#maybeAutoRedeemCodexReset()) {
8730
+ // A live usage-limit 429 on the active Codex account, with a banked
8731
+ // reset and the opt-in setting on: spend the reset and retry
8732
+ // immediately instead of waiting out the window. Runs after the
8733
+ // free sibling-switch above and before model fallback below.
8734
+ switchedCredential = true;
8735
+ delayMs = 0;
8723
8736
  } else {
8724
8737
  // No sibling credential is usable right now. Wait for whichever
8725
8738
  // comes first: the provider's retry-after window for the current
@@ -10137,6 +10150,123 @@ export class AgentSession {
10137
10150
  });
10138
10151
  }
10139
10152
 
10153
+ /**
10154
+ * Redeem one saved Codex rate-limit reset for a specific account, injecting
10155
+ * the provider base URL like {@link AgentSession.fetchUsageReports}. Powers
10156
+ * the `/usage reset` command and auto-redeem. Never throws for business
10157
+ * outcomes — inspect the returned `code`.
10158
+ */
10159
+ async redeemResetCredit(target: ResetCreditTarget, signal?: AbortSignal): Promise<ResetCreditRedeemOutcome> {
10160
+ return this.#modelRegistry.authStorage.redeemResetCredit({
10161
+ target,
10162
+ baseUrlResolver: provider => this.#modelRegistry.getProviderBaseUrl?.(provider),
10163
+ signal,
10164
+ });
10165
+ }
10166
+
10167
+ /**
10168
+ * List saved Codex rate-limit resets per stored account, fetched live from
10169
+ * the dedicated credits endpoint (bypasses the usage cache). Powers the
10170
+ * `/usage reset` account selector.
10171
+ */
10172
+ async listResetCredits(signal?: AbortSignal): Promise<ResetCreditAccountStatus[]> {
10173
+ return this.#modelRegistry.authStorage.listResetCredits({
10174
+ sessionId: this.sessionId,
10175
+ baseUrlResolver: provider => this.#modelRegistry.getProviderBaseUrl?.(provider),
10176
+ signal,
10177
+ });
10178
+ }
10179
+
10180
+ /**
10181
+ * Auto-redeem hook for {@link AgentSession.#handleRetryableError}'s
10182
+ * usage-limit branch. Returns `true` only when a saved Codex reset was
10183
+ * actually spent (so the caller retries immediately). Opt-in, reactive, and
10184
+ * heavily gated — see `./codex-auto-reset` and the design in
10185
+ * `local://autoreset-spec.md`. Per-account in-flight dedup lets concurrent
10186
+ * sessions adopt one redeem instead of double-spending.
10187
+ */
10188
+ async #maybeAutoRedeemCodexReset(coordinator = defaultCodexAutoRedeemCoordinator): Promise<boolean> {
10189
+ const cfg = this.settings.getGroup("codexResets");
10190
+ const model = this.model;
10191
+ // Cheap exits before any IO.
10192
+ if (!cfg.autoRedeem || !model || model.provider !== "openai-codex") return false;
10193
+ const authStorage = this.#modelRegistry.authStorage;
10194
+ // Capture identity BEFORE awaits: markUsageLimitReached leaves the
10195
+ // usage-limit session credential sticky, so this names the blocked account.
10196
+ const identity = authStorage.getOAuthAccountIdentity("openai-codex", this.sessionId);
10197
+ const accountKey = (identity?.accountId ?? identity?.email)?.trim().toLowerCase();
10198
+ if (!accountKey) return false;
10199
+ const existing = coordinator.inFlightByAccount.get(accountKey);
10200
+ if (existing) return existing;
10201
+
10202
+ const run = (async (): Promise<boolean> => {
10203
+ const reports = await this.fetchUsageReports();
10204
+ const decision = evaluateCodexAutoRedeem({
10205
+ nowMs: Date.now(),
10206
+ provider: model.provider,
10207
+ modelId: model.id,
10208
+ settings: {
10209
+ autoRedeem: cfg.autoRedeem,
10210
+ minBlockedMinutes: Math.max(0, cfg.minBlockedMinutes),
10211
+ keepCredits: Math.max(0, Math.trunc(cfg.keepCredits)),
10212
+ },
10213
+ identity,
10214
+ reports,
10215
+ attemptedBlockKeys: coordinator.attemptedBlockKeys,
10216
+ lastAttemptAtByAccount: coordinator.lastAttemptAtByAccount,
10217
+ });
10218
+ if (!decision.redeem) {
10219
+ logger.debug("codex-auto-reset: skipped", { reason: decision.reason });
10220
+ return false;
10221
+ }
10222
+ // Commit the attempt BEFORE acting so this block can never re-enter.
10223
+ coordinator.attemptedBlockKeys.add(decision.blockKey);
10224
+ coordinator.lastAttemptAtByAccount.set(decision.accountKey, Date.now());
10225
+ const who = decision.target.email ?? decision.target.accountId ?? "the active account";
10226
+ const outcome = await authStorage.redeemResetCredit({
10227
+ target: decision.target,
10228
+ baseUrlResolver: provider => this.#modelRegistry.getProviderBaseUrl?.(provider),
10229
+ // Not tied to the retry abort controller: aborting a consume
10230
+ // mid-flight leaves credit state unknown.
10231
+ signal: AbortSignal.timeout(15_000),
10232
+ });
10233
+ switch (outcome.code) {
10234
+ case "reset": {
10235
+ const left = Math.max(0, decision.availableCount - 1);
10236
+ this.emitNotice(
10237
+ "info",
10238
+ `Auto-redeemed a saved Codex rate-limit reset for ${who} (${left} left); retrying now.`,
10239
+ "codex-auto-reset",
10240
+ );
10241
+ void this.fetchUsageReports();
10242
+ return true;
10243
+ }
10244
+ case "already_redeemed":
10245
+ this.emitNotice(
10246
+ "warning",
10247
+ "A saved Codex reset was already redeemed elsewhere; waiting for the window.",
10248
+ "codex-auto-reset",
10249
+ );
10250
+ return false;
10251
+ case "no_credit":
10252
+ logger.debug("codex-auto-reset: no_credit (snapshot/live mismatch)", { account: accountKey });
10253
+ return false;
10254
+ case "nothing_to_reset":
10255
+ this.emitNotice(
10256
+ "warning",
10257
+ "Codex reset reported nothing to reset; auto-redeem suppressed for this window.",
10258
+ "codex-auto-reset",
10259
+ );
10260
+ return false;
10261
+ default:
10262
+ this.emitNotice("warning", `Codex auto-redeem failed (${outcome.code}).`, "codex-auto-reset");
10263
+ return false;
10264
+ }
10265
+ })().finally(() => coordinator.inFlightByAccount.delete(accountKey));
10266
+ coordinator.inFlightByAccount.set(accountKey, run);
10267
+ return run;
10268
+ }
10269
+
10140
10270
  /**
10141
10271
  * Estimate context tokens from messages, using the last assistant usage when available.
10142
10272
  */
@@ -14,6 +14,9 @@ export type {
14
14
  CredentialOriginKind,
15
15
  OAuthAccountIdentity,
16
16
  OAuthCredential,
17
+ ResetCreditAccountStatus,
18
+ ResetCreditRedeemOutcome,
19
+ ResetCreditTarget,
17
20
  SerializedAuthStorage,
18
21
  SnapshotResponse,
19
22
  StoredAuthCredential,