@oh-my-pi/pi-coding-agent 15.11.3 → 15.11.6

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 (135) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/dist/cli.js +692 -607
  3. package/dist/types/cli/usage-cli.d.ts +10 -1
  4. package/dist/types/commands/usage.d.ts +9 -0
  5. package/dist/types/config/api-key-resolver.d.ts +9 -3
  6. package/dist/types/config/keybindings.d.ts +1 -1
  7. package/dist/types/config/model-discovery.d.ts +6 -4
  8. package/dist/types/config/model-registry.d.ts +7 -4
  9. package/dist/types/config/settings-schema.d.ts +508 -155
  10. package/dist/types/export/html/template.generated.d.ts +1 -1
  11. package/dist/types/mnemopi/config.d.ts +3 -1
  12. package/dist/types/modes/components/reset-usage-selector.d.ts +12 -0
  13. package/dist/types/modes/components/session-selector.d.ts +1 -1
  14. package/dist/types/modes/components/settings-defs.d.ts +9 -2
  15. package/dist/types/modes/components/settings-selector.d.ts +9 -4
  16. package/dist/types/modes/components/tool-execution.d.ts +26 -1
  17. package/dist/types/modes/components/transcript-container.d.ts +12 -0
  18. package/dist/types/modes/controllers/input-controller.d.ts +9 -1
  19. package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
  20. package/dist/types/modes/interactive-mode.d.ts +10 -0
  21. package/dist/types/modes/session-observer-registry.d.ts +2 -0
  22. package/dist/types/modes/theme/theme.d.ts +23 -3
  23. package/dist/types/modes/types.d.ts +2 -0
  24. package/dist/types/modes/utils/context-usage.d.ts +6 -1
  25. package/dist/types/session/agent-session.d.ts +28 -8
  26. package/dist/types/session/auth-storage.d.ts +1 -1
  27. package/dist/types/session/codex-auto-reset.d.ts +107 -0
  28. package/dist/types/session/snapcompact-inline.d.ts +129 -0
  29. package/dist/types/slash-commands/helpers/active-oauth-account.d.ts +14 -0
  30. package/dist/types/slash-commands/helpers/reset-usage.d.ts +27 -0
  31. package/dist/types/system-prompt.d.ts +3 -1
  32. package/dist/types/task/render.d.ts +17 -6
  33. package/dist/types/tools/gh.d.ts +3 -0
  34. package/dist/types/tools/render-utils.d.ts +8 -16
  35. package/dist/types/tools/todo.d.ts +0 -11
  36. package/dist/types/utils/session-color.d.ts +15 -3
  37. package/dist/types/web/kagi.d.ts +1 -2
  38. package/dist/types/web/search/providers/codex.d.ts +1 -1
  39. package/dist/types/web/search/providers/gemini.d.ts +9 -6
  40. package/package.json +11 -11
  41. package/src/auto-thinking/classifier.ts +1 -5
  42. package/src/cli/usage-cli.ts +187 -16
  43. package/src/commands/usage.ts +8 -0
  44. package/src/commit/model-selection.ts +3 -6
  45. package/src/config/api-key-resolver.ts +10 -3
  46. package/src/config/keybindings.ts +1 -1
  47. package/src/config/model-discovery.ts +60 -46
  48. package/src/config/model-registry.ts +21 -8
  49. package/src/config/model-resolver.ts +57 -3
  50. package/src/config/settings-schema.ts +654 -153
  51. package/src/config/settings.ts +9 -0
  52. package/src/eval/completion-bridge.ts +1 -5
  53. package/src/export/html/template.generated.ts +1 -1
  54. package/src/export/html/template.js +13 -6
  55. package/src/internal-urls/docs-index.generated.ts +6 -6
  56. package/src/internal-urls/issue-pr-protocol.ts +10 -4
  57. package/src/memories/index.ts +2 -10
  58. package/src/mnemopi/backend.ts +30 -8
  59. package/src/mnemopi/config.ts +6 -1
  60. package/src/mnemopi/state.ts +6 -0
  61. package/src/modes/components/extensions/inspector-panel.ts +6 -2
  62. package/src/modes/components/plan-review-overlay.ts +15 -17
  63. package/src/modes/components/plugin-settings.ts +22 -5
  64. package/src/modes/components/reset-usage-selector.ts +161 -0
  65. package/src/modes/components/session-selector.ts +8 -2
  66. package/src/modes/components/settings-defs.ts +19 -4
  67. package/src/modes/components/settings-selector.ts +510 -95
  68. package/src/modes/components/status-line/component.ts +3 -1
  69. package/src/modes/components/status-line/segments.ts +3 -1
  70. package/src/modes/components/tool-execution.ts +87 -12
  71. package/src/modes/components/transcript-container.ts +49 -1
  72. package/src/modes/components/tree-selector.ts +16 -6
  73. package/src/modes/controllers/command-controller.ts +61 -8
  74. package/src/modes/controllers/event-controller.ts +1 -0
  75. package/src/modes/controllers/input-controller.ts +68 -6
  76. package/src/modes/controllers/selector-controller.ts +149 -61
  77. package/src/modes/interactive-mode.ts +63 -2
  78. package/src/modes/rpc/rpc-mode.ts +2 -1
  79. package/src/modes/session-observer-registry.ts +61 -3
  80. package/src/modes/shared.ts +2 -0
  81. package/src/modes/theme/theme.ts +102 -9
  82. package/src/modes/types.ts +2 -0
  83. package/src/modes/utils/context-usage.ts +78 -2
  84. package/src/modes/utils/hotkeys-markdown.ts +1 -1
  85. package/src/modes/utils/ui-helpers.ts +9 -5
  86. package/src/prompts/system/personalities/default.md +26 -0
  87. package/src/prompts/system/personalities/friendly.md +17 -0
  88. package/src/prompts/system/personalities/pragmatic.md +15 -0
  89. package/src/prompts/system/snapcompact-context-frames-note.md +1 -0
  90. package/src/prompts/system/snapcompact-context-stub.md +1 -0
  91. package/src/prompts/system/snapcompact-system-frames-note.md +1 -0
  92. package/src/prompts/system/snapcompact-system-stub.md +1 -0
  93. package/src/prompts/system/snapcompact-toolresult-note.md +1 -0
  94. package/src/prompts/system/system-prompt.md +5 -22
  95. package/src/prompts/tools/browser.md +33 -43
  96. package/src/prompts/tools/eval.md +27 -50
  97. package/src/prompts/tools/irc.md +29 -31
  98. package/src/prompts/tools/read.md +31 -37
  99. package/src/prompts/tools/task.md +3 -3
  100. package/src/prompts/tools/todo.md +1 -2
  101. package/src/sdk.ts +23 -1
  102. package/src/session/agent-session.ts +221 -29
  103. package/src/session/auth-storage.ts +4 -0
  104. package/src/session/codex-auto-reset.ts +190 -0
  105. package/src/session/session-dump-format.ts +8 -1
  106. package/src/session/session-manager.ts +5 -5
  107. package/src/session/snapcompact-inline.ts +524 -0
  108. package/src/slash-commands/builtin-registry.ts +145 -8
  109. package/src/slash-commands/helpers/active-oauth-account.ts +44 -0
  110. package/src/slash-commands/helpers/context-report.ts +28 -1
  111. package/src/slash-commands/helpers/reset-usage.ts +66 -0
  112. package/src/slash-commands/helpers/usage-report.ts +36 -3
  113. package/src/system-prompt.ts +15 -1
  114. package/src/task/index.ts +30 -7
  115. package/src/task/render.ts +57 -32
  116. package/src/tool-discovery/tool-index.ts +2 -0
  117. package/src/tools/bash.ts +10 -3
  118. package/src/tools/eval-render.ts +13 -8
  119. package/src/tools/gh.ts +39 -1
  120. package/src/tools/image-gen.ts +114 -78
  121. package/src/tools/inspect-image.ts +1 -5
  122. package/src/tools/job.ts +25 -5
  123. package/src/tools/read.ts +1 -57
  124. package/src/tools/render-utils.ts +29 -31
  125. package/src/tools/ssh.ts +3 -3
  126. package/src/tools/todo.ts +8 -128
  127. package/src/tools/tts.ts +40 -20
  128. package/src/utils/clipboard.ts +56 -4
  129. package/src/utils/commit-message-generator.ts +1 -5
  130. package/src/utils/session-color.ts +83 -9
  131. package/src/utils/title-generator.ts +1 -1
  132. package/src/web/kagi.ts +26 -27
  133. package/src/web/search/providers/codex.ts +42 -40
  134. package/src/web/search/providers/gemini.ts +42 -22
  135. package/src/web/search/providers/perplexity.ts +22 -10
@@ -1,92 +1,69 @@
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. Lean on this: stage helpers, loaded datasets, or live clients once, then fan out `task` subagents that use them directly. No re-importing, re-fetching, or serializing across the boundary.
4
+ Cells run in array order. State persists per language — across cells, tool calls, and `task` subagents: variables either side defines are visible to the other. Stage helpers, datasets, or live clients once; subagents use them directly no re-importing 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
- - `code` — cell body, verbatim. Newlines, quotes, and indentation are JSON-encoded; no fences, no headers.
10
- - `title` (optional) — short label shown in the transcript (e.g. `"imports"`, `"load config"`).
11
- - `timeout` (optional) — per-cell wall-clock budget in seconds (1-3600). Default 30. It bounds the cell's **own** work: compute, `print`/stdout, `log()`/`phase()`, and ordinary tool calls all count. The clock pauses while an `agent()`/`parallel()`/`completion()` call is in flight, so long fanouts and slow completions never need a raised `timeout`. Raise it only for heavy local work or long non-agent tool calls.
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}}
9
+ - `code` — cell body, verbatim. Newlines and quotes JSON-encoded; no fences, no headers.
10
+ - `title` (optional) — short transcript label (e.g. `"imports"`).
11
+ - `timeout` (optional) — per-cell seconds (1-3600, default 30). Bounds the cell's own work only; the clock pauses while `agent()`/`parallel()`/`completion()` calls are in flight, so fanouts never need a raise. Raise only for heavy local compute or long non-agent tool calls.
12
+ - `reset` (optional) — wipe this cell's language kernel first.{{#ifAll py js}} Per-language: a `py` reset never touches the JS VM.{{/ifAll}}
13
13
 
14
- **Work incrementally:**
15
-
16
- - One logical step per cell (imports, define, test, use).
17
- - Pass multiple small cells in one call.
18
- - Define small reusable functions for individual debugging.
19
- - Put workflow explanations in the assistant message or `title` — never inside cell code.
20
- {{#if py}}- Python cells run inside an IPython kernel with a live event loop. Use top-level `await` directly (e.g. `await main()`); `asyncio.run(…)` raises "cannot be called from a running event loop".{{/if}}
21
- **On failure:** errors identify the failing cell (e.g., "Cell 3 failed"). Resubmit only the fixed cell (or fixed cell + remaining cells).
14
+ Work incrementally: one logical step per cell (imports, define, test, use); pass multiple small cells per call; define small reusable functions for individual debugging. Workflow explanations go in the assistant message or `title`, never inside cell code.
15
+ {{#if py}}Python runs in IPython with a live event loop: use top-level `await` directly; `asyncio.run(…)` raises "cannot be called from a running event loop".{{/if}}
16
+ On failure, errors name the failing cell ("Cell 3 failed") — resubmit only the fixed cell (plus any remaining).
22
17
  </instruction>
23
18
 
24
19
  <prelude>
25
- {{#ifAll py js}}Same helpers in both runtimes with the same positional argument order. Python: trailing options as keyword args. JavaScript: trailing options are a single trailing object literal, never positional — passing options positionally (or any extra positional arg) throws. JavaScript helpers are async and `await`able; Python helpers run synchronously.{{else}}{{#if py}}Helpers run synchronously. Trailing options are keyword arguments.{{/if}}{{#if js}}Helpers are async and `await`able. Trailing options are a single trailing object literal, never positional — passing options positionally (or any extra positional arg) throws.{{/if}}{{/ifAll}}
20
+ {{#ifAll py js}}Same helpers in both runtimes, same positional order. Python: helpers run synchronously; trailing options are keyword args. JavaScript: helpers are async and `await`able; trailing options are ONE trailing object literal, never positional (extra positional args throw).{{else}}{{#if py}}Helpers run synchronously. Trailing options are keyword arguments.{{/if}}{{#if js}}Helpers are async and `await`able. Trailing options are ONE trailing object literal, never positional (extra positional args throw).{{/if}}{{/ifAll}}
26
21
  ```
27
22
  display(value) → None
28
- Render a value in the current cell output.
23
+ Render value in cell output, shows presentable values natively (figures, images, dataframes)
29
24
  print(value, ...) → None
30
- Print to the cell's text output.
25
+ Print to text output.
31
26
  read(path, offset?=1, limit?=None) → str
32
- Read file contents as text. offset/limit are 1-indexed line bounds. Accepts `local://…` (resolved to the session-local root, same place `read local://…` reads).
27
+ Read file as text; offset/limit are 1-indexed lines. Accepts `local://…`.
33
28
  write(path, content) → str
34
- Write content to a file (creates parent directories). Returns the resolved path. Accepts `local://…` to persist artifacts across turns / share with subagents.
29
+ Write file (creates parents); returns resolved path. `local://…` persists across turns / subagents.
35
30
  append(path, content) → str
36
- Append content to a file. Returns the resolved path. Accepts `local://…`.
31
+ Append to file; returns resolved path. Accepts `local://…`.
37
32
  tree(path?=".", max_depth?=3, show_hidden?=False) → str
38
- Render a directory tree.
33
+ Directory tree.
39
34
  diff(a, b) → str
40
- Unified diff between two files.
35
+ Unified diff of two files.
41
36
  env(key?=None, value?=None) → str | None | dict
42
- No args → full environment as dict. One arg → value of `key`. Two args → set `key=value` and return value.
37
+ No args → full env dict; one → value of `key`; two → set `key=value`, return value.
43
38
  output(*ids, format?="raw", query?=None, offset?=None, limit?=None) → str | dict | list[dict]
44
- Read task/agent output by ID. Single id returns text/dict; multiple ids return a list.
39
+ Read task/agent output by id; one id text/dict, multiple list.
45
40
  tool.<name>(args) → unknown
46
- Invoke any session tool by name. `args` is the tool's parameter object.
41
+ Invoke any session tool; `args` is its parameter object.
47
42
  completion(prompt, model?="default", system?=None, schema?=None) → str | dict
48
- Oneshot, stateless completion (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.
43
+ Oneshot stateless completion (no history, no tools). `model` tier: "smol" (fast) | "default" (session model) | "slow" (most capable). JSON-Schema `schema` forces structured output, returns parsed object.
49
44
  {{#if spawns}}agent(prompt, agent_type?="task", model?=None, label?=None, schema?=None) → str | dict
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. Share background by writing a `local://` file and referencing it in the prompt.
51
- {{#if js}} In JS, pass options as one trailing object — never positional: agent(prompt, { agentType, schema }).
45
+ Run a subagent, return its final output. `agent_type`/`agentType` picks another discovered agent; `schema` as in completion(). Share background via `local://` files referenced in the prompt.
46
+ {{#if js}} JS: options are ONE trailing object — agent(prompt, { agentType, schema }).
52
47
  {{/if}}
53
48
  {{/if}}
54
49
  parallel(thunks) → list
55
- Run thunks (callables) through a bounded pool, preserving input order. The pool is as wide as a `task` tool batch, so fan out as wide as the work divides — don't pre-shrink it. Barrier: returns once all finish; a thunk that throws propagates.
50
+ Run thunks through a bounded pool (as wide as a `task` batch — don't pre-shrink), preserving input order. Barrier: returns when all finish; a throwing thunk propagates.
56
51
  pipeline(items, ...stages) → list
57
- 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().
52
+ Map items through one-arg stages left-to-right, barrier between stages; stage 1 gets the item, later stages the previous result. Same pool width as parallel().
58
53
  log(message) → None
59
- Emit a progress line above the status tree.
54
+ Progress line above the status tree.
60
55
  phase(title) → None
61
- Start a phase; the status lines that follow group under it.
56
+ Start a phase grouping subsequent status lines.
62
57
  budget → per-turn token budget
63
- {{#if py}}`budget.total` (ceiling or None), `budget.spent()` (output tokens this turn), `budget.remaining()` (math.inf when no ceiling), `budget.hard` (bool).{{/if}}{{#if js}}`await budget.total()` (ceiling or null), `await budget.spent()`, `await budget.remaining()` (Infinity when no ceiling), `await budget.hard()`.{{/if}} A ceiling is set by a `+Nk` message directive (advisory) or `+Nk!`/Goal Mode (hard — `agent()` refuses to spawn past it); otherwise total is None/null and spend is still tracked across the turn (main loop + eval subagents).
58
+ {{#if py}}`budget.total` (ceiling or None), `budget.spent()`, `budget.remaining()` (math.inf when no ceiling), `budget.hard` (bool).{{/if}}{{#if js}}`await budget.total()` (ceiling or null), `await budget.spent()`, `await budget.remaining()` (Infinity when no ceiling), `await budget.hard()`.{{/if}} Ceiling comes from a `+Nk` directive (advisory) or `+Nk!`/Goal Mode (hard — `agent()` refuses to spawn past it); otherwise None/null, spend still tracked across the turn.
64
59
  ```
65
60
  </prelude>
66
61
 
67
- <output>
68
- Cells render like a Jupyter notebook. `display(value)` renders non-presentable data as an interactive JSON tree. Presentable values (figures, images, dataframes, etc.) use their native representation.
69
- </output>
70
-
71
- <caution>
72
- {{#if js}}- **js**: the VM exposes a selective `process` subset, Web APIs, `Buffer`, `fs/promises`, and the `Bun` global.
73
- {{/if}}</caution>
74
-
75
62
  <example>
76
- {{#if py}}```json
77
63
  {
78
64
  "cells": [
79
65
  { "language": "py", "title": "imports", "timeout": 10, "code": "import json\nfrom pathlib import Path" },
80
66
  { "language": "py", "title": "load config", "code": "data = json.loads(read('package.json'))\ndisplay(data)" }
81
67
  ]
82
68
  }
83
- ```{{/if}}{{#ifAll py js}}
84
-
85
- {{/ifAll}}{{#if js}}```json
86
- {
87
- "cells": [
88
- { "language": "js", "title": "summary", "reset": true, "code": "const data = JSON.parse(await read('package.json'));\ndisplay(data);\nreturn data.name;" }
89
- ]
90
- }
91
- ```{{/if}}
92
69
  </example>
@@ -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>
@@ -1,12 +1,12 @@
1
1
  {{#if asyncEnabled}}{{#if batchEnabled}}Spawns subagents to work in the background — one per `tasks[]` item; a single spawn is a one-item batch.{{else}}Spawns ONE subagent per call to work in the background.{{/if}}
2
2
 
3
3
  - Spawning is non-blocking: the call returns immediately with the agent id{{#if batchEnabled}}s{{/if}} and job id{{#if batchEnabled}}s{{/if}}; each result is delivered automatically when that agent yields.
4
- - Parallelism = {{#if batchEnabled}}`tasks[]` items in one call, and/or multiple `task` calls in one assistant message{{else}}multiple `task` calls in one assistant message{{/if}}. Concurrency is bounded at {{MAX_CONCURRENCY}} running subagents per session.
4
+ - Parallelism = {{#if batchEnabled}}multiple `tasks[]` items in ONE call. To launch several subagents, you MUST batch them into a single call's `tasks[]` — they share `context` once instead of duplicating it. Separate `task` calls in one message are ONLY for spawns needing a different `agent` type or unrelated `context`{{else}}multiple `task` calls in one assistant message{{/if}}. Concurrency is bounded at {{MAX_CONCURRENCY}} running subagents per session.
5
5
  - If genuinely blocked on a result, wait with `job poll`; otherwise keep working. `job cancel` terminates a task and **cannot carry a message** — only for stalled/abandoned work.
6
6
  {{else}}{{#if batchEnabled}}Runs subagents synchronously — one per `tasks[]` item; a single spawn is a one-item batch.{{else}}Runs ONE subagent synchronously per call.{{/if}}
7
7
 
8
8
  - Spawning is blocking: the call returns only after the agent{{#if batchEnabled}}s{{/if}} finish; results arrive inline.
9
- - Parallelism = {{#if batchEnabled}}`tasks[]` items in one call, and/or multiple `task` calls in one assistant message{{else}}multiple `task` calls in one assistant message{{/if}}. Concurrency is bounded at {{MAX_CONCURRENCY}} running subagents per session.
9
+ - Parallelism = {{#if batchEnabled}}multiple `tasks[]` items in ONE call. To launch several subagents, you MUST batch them into a single call's `tasks[]` — they share `context` once instead of duplicating it. Separate `task` calls in one message are ONLY for spawns needing a different `agent` type or unrelated `context`{{else}}multiple `task` calls in one assistant message{{/if}}. Concurrency is bounded at {{MAX_CONCURRENCY}} running subagents per session.
10
10
  {{/if}}
11
11
  {{#if ircEnabled}}
12
12
  - Coordinate with agents via `irc` using their ids. Agents reach you and their siblings live the same way.
@@ -39,7 +39,7 @@
39
39
  </parameters>
40
40
 
41
41
  <rules>
42
- - **Maximize fan-out.** Issue the widest {{#if batchEnabled}}`tasks[]` batch (or set of parallel `task` calls){{else}}set of parallel `task` calls{{/if}} the work decomposes into. NEVER serialize work that could run concurrently.
42
+ - **Maximize fan-out.** Issue the widest {{#if batchEnabled}}`tasks[]` batch{{else}}set of parallel `task` calls{{/if}} the work decomposes into. NEVER serialize work that could run concurrently.
43
43
  - **Subagents do not verify, lint, or format.** Every assignment MUST instruct the subagent to skip all gates, formatters, and project-wide build/test/lint. You run them once at the end across the union of changed files.
44
44
  - No globs, no "update all", no package-wide scope. Fan out.
45
45
  - NEVER slow down or serialize because tasks might overlap on some files. Agents resolve collisions among themselves in real time.
@@ -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
@@ -9,6 +9,7 @@ import {
9
9
  type ThinkingLevel,
10
10
  } from "@oh-my-pi/pi-agent-core";
11
11
  import {
12
+ type Context,
12
13
  type CredentialDisabledEvent,
13
14
  type Message,
14
15
  type Model,
@@ -121,6 +122,7 @@ import {
121
122
  wrapSteeringForModel,
122
123
  } from "./session/messages";
123
124
  import { getRestorableSessionModels, SessionManager } from "./session/session-manager";
125
+ import { SnapcompactInlineTransformer } from "./session/snapcompact-inline";
124
126
  import { closeAllConnections } from "./ssh/connection-manager";
125
127
  import { unmountAll } from "./ssh/sshfs-mount";
126
128
  import {
@@ -1977,6 +1979,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
1977
1979
  workspaceTree: workspaceTreePromise,
1978
1980
  memoryRootEnabled: memoryBackend.id === "local",
1979
1981
  model: settings.get("includeModelInPrompt") ? getActiveModelString() : undefined,
1982
+ personality: agentKind === "sub" ? "none" : settings.get("personality"),
1980
1983
  });
1981
1984
 
1982
1985
  if (options.systemPrompt === undefined) {
@@ -2156,6 +2159,25 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
2156
2159
  const withContext = await extensionRunner.emitContext(messages);
2157
2160
  return wrapSteeringForModel(withContext);
2158
2161
  };
2162
+ // Per-request provider-context transforms. Obfuscate FIRST so secrets are
2163
+ // redacted from text before snapcompact rasterizes it into PNG frames.
2164
+ // Both operate on the transient outgoing Context only — never persisted.
2165
+ const snapcompactSystemPromptMode = settings.get("snapcompact.systemPrompt");
2166
+ const snapcompactInline =
2167
+ snapcompactSystemPromptMode !== "none" || settings.get("snapcompact.toolResults")
2168
+ ? new SnapcompactInlineTransformer({
2169
+ renderSystemPrompt: snapcompactSystemPromptMode,
2170
+ renderToolResults: settings.get("snapcompact.toolResults"),
2171
+ })
2172
+ : undefined;
2173
+ const transformProviderContext =
2174
+ obfuscator || snapcompactInline
2175
+ ? (context: Context, transformModel: Model): Context => {
2176
+ let transformed = obfuscator ? obfuscateProviderContext(obfuscator, context) : context;
2177
+ if (snapcompactInline) transformed = snapcompactInline.transform(transformed, transformModel);
2178
+ return transformed;
2179
+ }
2180
+ : undefined;
2159
2181
  const onPayload = async (payload: unknown, _model?: Model) => {
2160
2182
  return await extensionRunner.emitBeforeProviderRequest(payload);
2161
2183
  };
@@ -2196,7 +2218,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
2196
2218
  sessionId: providerSessionId,
2197
2219
  promptCacheKey: options.providerPromptCacheKey,
2198
2220
  transformContext,
2199
- transformProviderContext: obfuscator ? context => obfuscateProviderContext(obfuscator, context) : undefined,
2221
+ transformProviderContext,
2200
2222
  steeringMode: settings.get("steeringMode") ?? "one-at-a-time",
2201
2223
  followUpMode: settings.get("followUpMode") ?? "one-at-a-time",
2202
2224
  interruptMode: settings.get("interruptMode") ?? "immediate",