@kingcrab/pi-imessage 0.0.43 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/README.md +101 -127
  2. package/dist/agent/behavior.js +79 -0
  3. package/dist/agent/chats.js +67 -0
  4. package/dist/agent/commands.js +163 -0
  5. package/dist/agent/compaction.js +51 -0
  6. package/dist/agent/deliver.js +139 -0
  7. package/dist/agent/direct-send.js +68 -0
  8. package/dist/agent/harness.js +79 -0
  9. package/dist/agent/health.js +32 -0
  10. package/dist/agent/models.js +69 -0
  11. package/dist/agent/progress-format.js +87 -0
  12. package/dist/agent/progress.js +135 -0
  13. package/dist/agent/prompt.js +79 -0
  14. package/dist/agent/replies.js +9 -0
  15. package/dist/agent/run.js +159 -0
  16. package/dist/agent/scheduling.js +290 -0
  17. package/dist/agent/time.js +18 -0
  18. package/dist/cli.js +88 -191
  19. package/dist/config/settings.js +60 -0
  20. package/dist/extensions/browser.js +326 -0
  21. package/dist/extensions/final-text.js +23 -0
  22. package/dist/extensions/memory.js +109 -0
  23. package/dist/extensions/message.js +33 -0
  24. package/dist/extensions/read-image.js +72 -0
  25. package/dist/extensions/schedules.js +41 -0
  26. package/dist/extensions/subagent.js +54 -0
  27. package/dist/extensions/web.js +99 -0
  28. package/dist/extensions/workspace.js +98 -0
  29. package/dist/main.js +568 -88
  30. package/dist/transport/attachments.js +37 -0
  31. package/dist/transport/http.js +110 -0
  32. package/dist/{rich-text.js → transport/rich-text.js} +25 -13
  33. package/dist/{send.js → transport/send.js} +13 -13
  34. package/dist/transport/watch.js +124 -0
  35. package/dist/web/app.js +748 -0
  36. package/dist/web/history.js +87 -0
  37. package/dist/web/memory.js +81 -0
  38. package/dist/web/page.js +28 -0
  39. package/dist/web/schedules.js +74 -0
  40. package/dist/web/server.js +348 -0
  41. package/dist/web/style.css +543 -0
  42. package/dist/web/tasks.js +63 -0
  43. package/docs/adr/0001-durable-owns-state.md +11 -0
  44. package/docs/adr/0002-module-layering.md +11 -0
  45. package/docs/adr/0003-replies-at-most-once.md +11 -0
  46. package/docs/adr/0004-native-extensions.md +11 -0
  47. package/docs/adr/0005-history-migration.md +11 -0
  48. package/docs/adr/0006-jsonl-storage.md +11 -0
  49. package/docs/adr/0007-images-by-path.md +11 -0
  50. package/docs/adr/0008-compaction-and-run.md +11 -0
  51. package/docs/adr/0009-one-shot-cutover.md +11 -0
  52. package/docs/adr/0010-test-budget.md +11 -0
  53. package/docs/adr/0011-reconcile-unsent-answers.md +24 -0
  54. package/docs/adr/0012-run-in-owned-conversation.md +22 -0
  55. package/docs/adr/0013-thin-adapters-around-durable.md +18 -0
  56. package/docs/adr/0014-ephemeral-private-storage.md +13 -0
  57. package/docs/adr/0015-ordinary-stop-keeps-native-inbox.md +21 -0
  58. package/docs/adr/0016-process-http-transport.md +21 -0
  59. package/docs/adr/0017-browser-cli-owned-scope.md +22 -0
  60. package/docs/adr/0018-tasks-without-prompt-api.md +19 -0
  61. package/docs/adr/0019-remove-scheduled-services.md +41 -0
  62. package/docs/adr/0020-chat-task-progress.md +41 -0
  63. package/docs/adr/0021-readable-progress-without-link-previews.md +33 -0
  64. package/docs/adr/0022-progress-admission-and-settlement.md +35 -0
  65. package/docs/adr/0023-retain-progress-type-prefixes.md +18 -0
  66. package/docs/adr/0024-status-marker-before-type-prefix.md +13 -0
  67. package/docs/adr/0025-reusable-progress-and-command-preview.md +49 -0
  68. package/docs/adr/0026-inline-command-only-requests.md +16 -0
  69. package/docs/adr/0027-command-preview-width.md +10 -0
  70. package/docs/adr/0028-command-preview-120.md +8 -0
  71. package/docs/adr/0030-shared-web-read-cache.md +33 -0
  72. package/docs/adr/0031-quiet-compaction-progress.md +32 -0
  73. package/docs/adr/0032-native-background-schedules.md +58 -0
  74. package/docs/adr/0033-operator-scheduled-run.md +23 -0
  75. package/docs/adr/0034-machine-local-schedule-time.md +36 -0
  76. package/docs/adr/0035-external-business-task-extensions.md +56 -0
  77. package/docs/adr/0036-workspace-policy-and-neutral-service-id.md +37 -0
  78. package/docs/adr/0037-restart-during-in-flight-work.md +30 -0
  79. package/docs/adr/0038-restart-any-time.md +25 -0
  80. package/docs/adr/0039-rerun-interrupted-tools.md +24 -0
  81. package/docs/adr/0040-chat-progress-notices.md +44 -0
  82. package/docs/adr/0041-restart-and-recovery.md +28 -0
  83. package/docs/adr/0042-background-schedules-and-workspace-extensions.md +44 -0
  84. package/docs/adr/0043-remove-legacy-migration.md +23 -0
  85. package/docs/adr/0044-message-channels.md +32 -0
  86. package/docs/adr/0045-chat-scheduling-tools.md +51 -0
  87. package/docs/adr/0046-selective-command-redaction.md +31 -0
  88. package/docs/adr/0047-one-scheduling-path.md +158 -0
  89. package/docs/adr/README.md +50 -0
  90. package/docs/api.md +92 -0
  91. package/docs/architecture.md +164 -0
  92. package/docs/browser.md +93 -0
  93. package/docs/memory.md +27 -0
  94. package/docs/screenshot.png +0 -0
  95. package/docs/workspace-extensions.md +133 -0
  96. package/examples/workspace-extensions/system-context/index.ts +69 -0
  97. package/examples/workspace-extensions/system-context/package.json +3 -0
  98. package/ops/README.md +267 -0
  99. package/ops/preflight.mjs +28 -0
  100. package/package.json +36 -37
  101. package/dist/agent.d.ts +0 -84
  102. package/dist/agent.d.ts.map +0 -1
  103. package/dist/agent.js +0 -780
  104. package/dist/agent.js.map +0 -1
  105. package/dist/cli.d.ts +0 -15
  106. package/dist/cli.d.ts.map +0 -1
  107. package/dist/cli.js.map +0 -1
  108. package/dist/imessage.d.ts +0 -33
  109. package/dist/imessage.d.ts.map +0 -1
  110. package/dist/imessage.js +0 -76
  111. package/dist/imessage.js.map +0 -1
  112. package/dist/logger.d.ts +0 -33
  113. package/dist/logger.d.ts.map +0 -1
  114. package/dist/logger.js +0 -104
  115. package/dist/logger.js.map +0 -1
  116. package/dist/main.d.ts +0 -5
  117. package/dist/main.d.ts.map +0 -1
  118. package/dist/main.js.map +0 -1
  119. package/dist/memory.d.ts +0 -53
  120. package/dist/memory.d.ts.map +0 -1
  121. package/dist/memory.js +0 -196
  122. package/dist/memory.js.map +0 -1
  123. package/dist/model-health.d.ts +0 -11
  124. package/dist/model-health.d.ts.map +0 -1
  125. package/dist/model-health.js +0 -69
  126. package/dist/model-health.js.map +0 -1
  127. package/dist/pipeline.d.ts +0 -27
  128. package/dist/pipeline.d.ts.map +0 -1
  129. package/dist/pipeline.js +0 -59
  130. package/dist/pipeline.js.map +0 -1
  131. package/dist/queue.d.ts +0 -23
  132. package/dist/queue.d.ts.map +0 -1
  133. package/dist/queue.js +0 -89
  134. package/dist/queue.js.map +0 -1
  135. package/dist/reminders.d.ts +0 -42
  136. package/dist/reminders.d.ts.map +0 -1
  137. package/dist/reminders.js +0 -207
  138. package/dist/reminders.js.map +0 -1
  139. package/dist/rich-text.d.ts +0 -26
  140. package/dist/rich-text.d.ts.map +0 -1
  141. package/dist/rich-text.js.map +0 -1
  142. package/dist/self-echo.d.ts +0 -19
  143. package/dist/self-echo.d.ts.map +0 -1
  144. package/dist/self-echo.js +0 -54
  145. package/dist/self-echo.js.map +0 -1
  146. package/dist/send.d.ts +0 -24
  147. package/dist/send.d.ts.map +0 -1
  148. package/dist/send.js.map +0 -1
  149. package/dist/settings.d.ts +0 -44
  150. package/dist/settings.d.ts.map +0 -1
  151. package/dist/settings.js +0 -80
  152. package/dist/settings.js.map +0 -1
  153. package/dist/store.d.ts +0 -57
  154. package/dist/store.d.ts.map +0 -1
  155. package/dist/store.js +0 -124
  156. package/dist/store.js.map +0 -1
  157. package/dist/tasks.d.ts +0 -95
  158. package/dist/tasks.d.ts.map +0 -1
  159. package/dist/tasks.js +0 -384
  160. package/dist/tasks.js.map +0 -1
  161. package/dist/types.d.ts +0 -93
  162. package/dist/types.d.ts.map +0 -1
  163. package/dist/types.js +0 -38
  164. package/dist/types.js.map +0 -1
  165. package/dist/watch.d.ts +0 -21
  166. package/dist/watch.d.ts.map +0 -1
  167. package/dist/watch.js +0 -219
  168. package/dist/watch.js.map +0 -1
  169. package/dist/web/data.d.ts +0 -11
  170. package/dist/web/data.d.ts.map +0 -1
  171. package/dist/web/data.js +0 -56
  172. package/dist/web/data.js.map +0 -1
  173. package/dist/web/html.d.ts +0 -5
  174. package/dist/web/html.d.ts.map +0 -1
  175. package/dist/web/html.js +0 -16
  176. package/dist/web/html.js.map +0 -1
  177. package/dist/web/index.d.ts +0 -25
  178. package/dist/web/index.d.ts.map +0 -1
  179. package/dist/web/index.js +0 -335
  180. package/dist/web/index.js.map +0 -1
  181. package/dist/web/render.d.ts +0 -11
  182. package/dist/web/render.d.ts.map +0 -1
  183. package/dist/web/render.js +0 -56
  184. package/dist/web/render.js.map +0 -1
  185. package/dist/web/templates/logs.eta +0 -75
  186. package/dist/web/templates/memory.eta +0 -91
  187. package/dist/web/templates/page.eta +0 -98
@@ -0,0 +1,11 @@
1
+ # 0010. About 40 tests, only for our code
2
+
3
+ Status: accepted (2026-10-04; owner accepted 2026-10-05)
4
+
5
+ **Context.** #31 reached 404 tests. Many retested Durable's own guarantees, or killed subprocesses to probe crash windows.
6
+
7
+ **Decision.** At most about 40 tests, each for a distinct behavior of our code. Faux provider, temp directories and a fake sender; never real Messages, models or production data. Durable's guarantees (crash resume, tool replay, request dedupe) are not retested. To add a test over budget, delete a weaker one first. Real model and Messages checks live in a manual `ops/smoke.ts`.
8
+
9
+ **Consequences.** Some bugs will ship; each gets a regression test when found.
10
+
11
+ **Rejected.** Subprocess crash tests. A unit test per function.
@@ -0,0 +1,24 @@
1
+ # 0011. Reconcile unsent answers after intervening input
2
+
3
+ Status: accepted (2026-10-04; owner chose reconciliation and explicitly included different senders)
4
+
5
+ **Context.** Native steer places all selected messages after a tool round when configured with `steeringMode: "all"`. At a final-answer boundary, however, Durable first records the answer and settles that run's inputs; selected new messages start another run. The earlier answer remains eligible for delivery even when a newer message corrects its assumptions. An `onYield` continuation does not change this: it is ignored when the boundary selects user input. See [the state ownership decision](0001-durable-owns-state.md) and [at-most-once replies](0003-replies-at-most-once.md).
6
+
7
+ **Example.** The model is finishing a two-day itinerary when the user says “three days instead.” Sending both itineraries is confusing. Conversely, blindly dropping the first answer when the new message asks an unrelated question can lose useful work.
8
+
9
+ **Decision.** Reconcile delivery, not execution:
10
+
11
+ - If newer ordinary input was durably accepted before an answer's transport claim, hold that unattempted answer for reconciliation. Do not abort or replay the completed tools.
12
+ - Give the next generation truthful information about which assistant answers were actually sent and which remain drafts. It must apply corrections, retain valid completed work and answer unrelated questions separately in its final response.
13
+ - Include messages from different senders in that decision. Keep each sender's identity and request distinct; a later sender does not automatically retract or correct another person's request. Reconcile shared tasks where appropriate and ask for clarification when their requirements conflict.
14
+ - Supersede held drafts only when a replacement final answer is durably ready. Preserve all original entries and explicit delivery disposition for inspection.
15
+ - If reconciliation fails or is cancelled, retain the held drafts for inspection; do not automatically release possibly obsolete answers. Operator-facing state must explain why a reply is held. No silent deletion.
16
+ - A reply already claimed as `sending` cannot reliably be retracted. Later input affects future replies. Existing `sent`/`unknown` receipts remain authoritative and are never automatically retried.
17
+
18
+ This is not permission to change Durable's inbox, introduce a host model loop, add a debounce window, replay a turn or classify topics with a second model call. Slash-command cancellation requires its own command-boundary handling.
19
+
20
+ **Consequences.** A late unrelated message can delay an otherwise valid answer; the next response must preserve that answer rather than assume it was delivered. Continuous input can extend this delay. Reconciliation failure can leave visible undelivered drafts requiring intervention. The owner accepted these product trade-offs.
21
+
22
+ **Alternatives.** Always send every completed answer (preserves delivery timing but exposes obsolete answers). Drop all but the latest answer (can silently lose unrelated answers). Guess corrections from keywords (unreliable). Change or wrap Durable's internal finalization logic (violates state ownership).
23
+
24
+ **Approval.** The owner selected waiting for the next turn and replying together, explicitly including different senders in the decision. This approval covers reconciliation of unattempted replies, not retraction or replay of claimed sends.
@@ -0,0 +1,22 @@
1
+ # 0012. `/run` works in a conversation owned by one task
2
+
3
+ Status: accepted (2026-10-04; owner accepted 2026-10-05). Replaces the `/run` part of [0008](0008-compaction-and-run.md); its compaction part stays.
4
+
5
+ **Context.** In 0008, `/run` works inside the chat's own conversation. Stopping it then means telling run work apart from ordinary work: `Conversation.abort()` also withdraws ordinary queued messages, so the draft tracked submission IDs and command receipts by hand. Durable already scopes abort by ownership: aborting a task aborts the conversations and work it owns.
6
+
7
+ **Example.** The user sends `/run 30m plan a two-day trip`, then "make it three days". The correction must reach the run. A later `/stop` must end the run without touching anything else.
8
+
9
+ **Decision.**
10
+
11
+ - `/run <duration> [task]` creates one Durable task that owns a fork of the chat conversation at its newest entry. The fork inherits the context; nothing is copied or summarized. The task text (default: continue the current task) is the fork's first input. One run per chat.
12
+ - `/run` starts only when the chat conversation is idle. Otherwise it replies that the chat is busy and does nothing: no queueing, no moving of running work. A repeated `/run` during a run updates the deadline; a task text in it is sent to the run like an ordinary message.
13
+ - While a run is active, all ordinary messages of the chat go to the run conversation as steers, with the message GUID as request ID. Commands do not.
14
+ - An `onYield` hook continues the run until the model calls `end_run` (done, or the user is needed) or the deadline passes. The deadline only stops continuation: the current turn finishes, so a run can overrun. Ordinary messages do not extend it. Without unfinished work, the model ends the run instead of inventing a task.
15
+ - Final answers of the run conversation are delivered as they are committed, including those between continuations, under [0003](0003-replies-at-most-once.md) and [0011](0011-reconcile-unsent-answers.md).
16
+ - `/stop` aborts the run task. Durable aborts the run's work and withdraws its queued messages; they stay in history, unanswered, and are not resubmitted anywhere. Without an active run, `/stop` replies that nothing is running. There is no `/run-stop`. `/new` also stops the run.
17
+ - A restart does not stop a run. `resume()` continues the run task like any other pending work, with the deadline from our run document; if it passed during the downtime, the current turn finishes and the run ends. Replies interrupted mid-send follow [0003](0003-replies-at-most-once.md).
18
+ - The chat conversation keeps the record: the run's user messages and sent replies are written to it as passive entries, plus how the run ended. Tool calls and results stay in the run conversation, searchable under `durable/` like other history.
19
+
20
+ **Consequences.** Stopping needs no submission bookkeeping and no command receipts. During a run, the chat has one place where work happens. Messages still queued in the run when `/stop` arrives are dropped, a narrower promise than before. After the run, the chat conversation knows what was said, not how; the model searches the run conversation when it needs details.
21
+
22
+ **Rejected.** Running in the chat conversation (per-submission abort bookkeeping). A blank run conversation (loses earlier requirements). Queueing `/run` behind ordinary work, or moving running work into the run. Durable's background-subagent example as is (named agents and reporter tasks we don't need). A separate `/run-stop`. Aborting runs at startup (extra code against Durable's resume, and a silent stop the user would wait on).
@@ -0,0 +1,18 @@
1
+ # 0013. Use Durable directly; keep adapters thin
2
+
3
+ Status: accepted (2026-10-04; owner accepted 2026-10-05)
4
+
5
+ **Context.** The drafts grew host state that Durable already keeps (per-command receipts with running/unknown recovery, a reply scan over every settled submission) and copies of other systems (a gzip/base64 Python memory backend inside TypeScript, a vendored 1,000-line scheduler). [0001](0001-durable-owns-state.md) says Durable owns chat state; this records where our code stops.
6
+
7
+ **Decision.**
8
+
9
+ - **Commands** call Durable APIs directly (`configure`, `compact`, `reset`, task abort). Each command is first written to the chat conversation as a passive entry with the message GUID as request ID, so a repeated row is skipped by Durable's dedupe. An interrupted command is not replayed. No command receipt state.
10
+ - **`/reload`** rereads settings, models and auth, AGENTS and skills, and reinstalls our extensions under the same names; Durable's registry replaces them in place. Running calls finish on their old code. Nothing is paused or cancelled.
11
+ - **Memory tools** run the store's own CLI, `WORKING_DIR/skills/file-memory/memory_cli.py`, which finds its data next to itself. No embedded or rewritten backend; a missing CLI is a tool error.
12
+ - **Scheduler** keeps the clock: cron expressions, timezones, reminders and their files (`cron/jobs.json`, reminders). When a job fires it submits to Durable with a stable request ID, or sends directly with a receipt. After acceptance, Durable owns execution and recovery; the scheduler never retries model work. Durable 1.0.2 has task `sleep` but no cron or reminder service, so it is not used as one.
13
+ - **Web pages** are read-only: they show Harness state and watches (history, live state, task graph, usage) and have no buttons. They keep no execution log of their own. The endpoints in [the HTTP API](../api.md) stay; endpoints used only by the old page buttons (per-chat reply toggle, PT task actions) are removed. Replies are enabled through the settings allowlist; cron jobs are run or paused through the API.
14
+ - **Our documents** hold only what Durable cannot know: the `chat.db` cursor, the chat-to-conversation map, send receipts and the active run with its deadline.
15
+
16
+ **Consequences.** Little host recovery code beyond send receipts. Memory needs Python and the store's CLI at runtime (Python is already a prerequisite). The scheduler stays a larger, separate module, isolated behind one submit or send call.
17
+
18
+ **Rejected.** Command receipts and a command state machine. Pausing work around `/reload`. Embedding or reimplementing the memory backend. Rewriting cron and reminders as Durable tasks. A separate execution log for the UI. Page buttons, including a resend button for `unknown` replies.
@@ -0,0 +1,13 @@
1
+ # 0014. Keep ephemeral transcripts in disposable private storage
2
+
3
+ Status: superseded by [0018](0018-tasks-without-prompt-api.md) (2026-10-05). Was: proposed (2026-10-04)
4
+
5
+ **Context.** The retained HTTP `ephemeral:true` contract removes an isolated session after its queued work finishes. Durable 1.0.2 has reset/compaction but no public single-conversation deletion API. Reset hides old context from the model; it does not erase stored prompts, tool results or child conversations.
6
+
7
+ **Example.** An automated task reads private files. Its final answer may stay in the destination chat, but its private task transcript must not remain searchable in the ordinary conversation store after cleanup.
8
+
9
+ **Decision proposed for owner acceptance.** Give each active ephemeral `(chatGuid, sessionKey)` its own private JSONL storage and native Harness. Let Durable own that storage's inbox, dedupe, steering, tasks and recovery. The main store keeps only opaque identity/lifecycle metadata needed to locate it and external send receipts, not its private prompt/tool transcript. Admission success follows persisted private admission. On restart, reopen recorded private storage and resume its native work; never replay a whole turn in the host. Deliver completed answers with stable main-store receipts. Once the private Harness is idle and its completed answers have an authoritative sent/unknown disposition, close it and remove its directory with restart-safe cleanup. A later batch with the same key starts a fresh private context. Do not remove an active directory or silently lose queued work.
10
+
11
+ **Consequences.** Extra storage/lifecycle wiring is necessary for the old privacy promise, but no second model loop or mirrored inbox. Repeated request IDs and uncertain sends need checks across cleanup/restart. HTTP ephemeral admission remains blocked until this design is accepted, implemented and verified. Ordinary API/cron contexts stay in the shared store.
12
+
13
+ **Rejected.** Calling reset and claiming deletion. Editing shared append-only storage behind Durable. Retaining private transcripts in the main store. Dropping ephemeral behavior without owner approval.
@@ -0,0 +1,21 @@
1
+ # 0015. Ordinary `/stop` keeps the native inbox
2
+
3
+ Status: superseded by [0018](0018-tasks-without-prompt-api.md) (2026-10-05). Was: proposed (2026-10-04). Replaces only the no-active-run `/stop` clause of proposed [0012](0012-run-in-owned-conversation.md) if accepted. Does not approve its other behavior changes.
4
+
5
+ **Problem.** Ordinary `/stop` must cancel current work and foreground children, suppress unclaimed old replies, and preserve later unprocessed ordinary messages. `Conversation.abort()` in Durable 1.0.2 cancels the conversation's foreground scope but withdraws queued inputs. Looking up `LiveDoc.run.taskId` and calling `abortTask()` preserves the parent inbox, but is not equivalent: generation hands over to a new conversation-owned task between turns. The old task can become terminal between lookup and abort; aborting it then does nothing to the new generation.
6
+
7
+ **Example.** A request reads a file and continues into another model turn. A later ordinary message is queued. `/stop` must cancel the ongoing request, even at that turn boundary, without losing or resubmitting the later message.
8
+
9
+ **Decision.**
10
+
11
+ - Preserve this ordinary-chat behavior; do not silently replace it with “nothing is running” or queue withdrawal.
12
+ - Require a supported native conversation-scope cancellation operation that atomically selects current foreground work while retaining the root chat's queued inputs. Cancellation of work owned below that scope must still follow Durable's ownership rules. The operation must define admission during cancellation and behavior across generation handover.
13
+ - Use the native operation directly. After cancellation settles, suppress old unclaimed drafts using existing delivery records; never change `sending`, `sent` or `unknown` receipts or claim that a started send was undone.
14
+ - If preserved inputs require a new boundary, submit one passive cancellation note through the public conversation API. Durable places existing inbox items; the host never copies their content, creates replacement input submissions, or replays the cancelled request.
15
+ - Keep `/run` cancellation and isolated API/cron/background/health sessions separately scoped. This decision does not accept dropping queued run messages or cancelling unrelated sessions.
16
+
17
+ **API boundary.** Durable 1.0.2 exposes no retain-inbox option on `Conversation.abort()`, and its public transaction API has no atomic task-abort operation. An upstream API change and dependency upgrade are required before this design can be implemented as specified. Do not patch installed package files or write the built-in live/inbox documents to simulate that operation.
18
+
19
+ **Evidence.** The [inbox example](https://github.com/earendil-works/pi/blob/main/packages/durable/test/examples/20-inbox.ts), installed `harness/generation.js` (`createGeneration`, `handOver`), `harness/scheduler.js` (`abort`, `abortConversation`) and public `Tx` declarations establish the distinction. A bounded faux-provider experiment reproduces generation-ID handover and the stale-ID abort no-op; aborting the current generation preserves the queued input, and a passive write places it without a replacement submission. That experiment does not establish a race-free host cancellation implementation.
20
+
21
+ **Rejected.** Repeated lookup/abort polling, a host cancellation epoch or mirrored inbox, withdrawing and resubmitting queued inputs, direct edits of Durable internals, and treating a cancelled wait as cancelled work. Also rejected: claiming a single generation-task abort cancels a whole multi-turn request.
@@ -0,0 +1,21 @@
1
+ # 0016. Initialize one process-owned HTTP transport
2
+
3
+ Status: accepted (2026-10-05; owner accepted 2026-10-05)
4
+
5
+ **Problem.** Pi's global `httpProxy` and `httpIdleTimeoutMs` settings need HTTP transport setup, not just request-option forwarding. Coding-agent's experimental Durable startup calls `applyHttpProxySettings()` and `configureHttpDispatcher()`, but neither is a public export of coding-agent 1.0.2. Merely assigning `HTTP_PROXY` and `HTTPS_PROXY` after Node starts does not reconfigure the dispatcher created by `--use-env-proxy`.
6
+
7
+ **Example.** An operator sets `httpProxy` in Pi settings without shell proxy variables. The service must use that proxy for authentication and model HTTP requests. Setting the variables at service startup can otherwise leave fetch connecting directly. A provider request timeout also does not replace a socket's inactivity timeout.
8
+
9
+ **Decision.**
10
+
11
+ - The host owns one process-wide HTTP setup, initialized before opening model/auth runtime. Individual chats and private Harness instances do not install global dispatchers.
12
+ - Use public Undici APIs, with an exact direct dependency matching the supported SDK implementation, rather than importing private coding-agent files. Supply the global Pi proxy setting only as environment defaults; retain explicit environment proxy values, lowercase precedence and NO_PROXY behavior. Do not accept project-local proxy overrides where Pi accepts only global configuration.
13
+ - Configure native HTTP headers/body idle timeouts from Pi settings, including disabled timeout semantics. Keep transport fetch and dispatcher on the same Undici implementation so compressed responses are decoded consistently. Preserve deliberately supplied fetch implementations. Follow the SDK's connection-family and client-error handling rather than inventing transport retries.
14
+ - `/reload` may replace transport for subsequent requests without cancelling active work. Track transport resources in memory only, let active requests finish on their original clients, and retire old clients safely. During shutdown, stop producers and Harness work before releasing transport; restore globals only if they are still ours. Never close a dispatcher owned by another caller.
15
+ - Durable/provider retry policy and request timeout options remain unchanged. This setup is not a host activity timer, prompt deadline, compaction deadline, inbox or execution loop.
16
+
17
+ **Consequences.** Process-global HTTP setup belongs to application lifecycle, not per-conversation configuration. Standalone callers with an injected runtime/fetch keep control of their transport. The implementation must verify proxy precedence/bypass, compressed responses, idle timeout enable/disable, reload with active requests, cleanup and custom-fetch preservation against loopback fixtures before claiming compatibility.
18
+
19
+ **Evidence.** The installed coding-agent `core/http-dispatcher.js` and experimental [harness setup](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/src/experimental/durable/harness-setup.ts) use Undici EnvHttpProxyAgent and matching fetch globals. A bounded Node 26 loopback experiment confirms late environment defaults leave built-in fetch direct, while public Undici 8.10.2 dispatcher plus matching fetch routes through the configured proxy. This establishes an implementation path, not completed lifecycle/timeout behavior or real-provider connectivity.
20
+
21
+ **Rejected.** Environment mutation as proof of effective transport, request timeout as proof of socket-idle enforcement, private SDK imports, patching installed packages, a new HTTP retry algorithm, and relying on newer Node-only proxy-reset APIs while retaining Node >=22.22.2 support.
@@ -0,0 +1,22 @@
1
+ # 0017. Invoke browser CLI in a native conversation-owned scope
2
+
3
+ Status: accepted (2026-10-05; owner accepted 2026-10-05). Replaces only the browser-through-unrestricted-bash part of proposed [0004](0004-native-extensions.md) if accepted.
4
+
5
+ **Problem.** `PI_BROWSER_HOME` scopes the pi-browser wrapper's profiles/output, not Playwright CLI's daemon registry. CLI 0.1.19 keeps that registry in the user's cache directory, derives daemon workspace identity from `.playwright` or the installed package, and enumerates other workspaces. All chats currently share the same shell environment and working directory. A named session or separate profile directory alone therefore does not preserve the old browser adapter's private-session rules.
6
+
7
+ **Example.** Two chats both use `-s=task`. Even with different PI_BROWSER_HOME values, they can resolve the same daemon/session and see other registry entries. Giving the model a suggested name is not enforcement.
8
+
9
+ **Decision.**
10
+
11
+ - Offer a thin native Durable browser tool that invokes the installed, pinned pi-browser CLI by argv. Do not load an SDK browser extension, emulate ExtensionAPI, vendor CLI internals or implement another browser driver.
12
+ - Derive browser scope from canonical storage identity plus native conversation identity, not a model-selected name. Ordinary chats, runs and isolated API/cron contexts get distinct scope; disallowed read-only contexts do not get the tool.
13
+ - Give only browser child processes a dedicated HOME, profile/output root and scoped working directory. Leave ordinary coding tools' HOME/cwd unchanged. Use public process/CLI controls rather than PWTEST daemon/socket environment variables. Verify daemon discovery and attachment cannot escape the scope, including environment-inherited external browser targets.
14
+ - Always select the owned session/profile and headless mode. Deny attachment/discovery, global close/kill, arbitrary profile/config paths and headed overrides, retaining the old service's restrictions. Expose the ordinary browsing operations needed by the product through validated arguments; operation results remain untrusted data.
15
+ - Close owned browsers at the agreed stop/shutdown boundaries without touching unrelated sessions. Track only external browser resources and cleanup facts, not a second task state machine. Define restart cleanup and privacy retention before claiming isolation. Do not silently delete saved login profiles or replay uncertain browser mutations.
16
+ - Do not mistake this tool boundary for an OS sandbox: unrestricted bash/read can still access host files and invoke other executables. State that limit explicitly; stronger containment requires a separate decision.
17
+
18
+ **Consequences.** Native tool registration replaces browser instructions as the enforcement boundary for normal product use. The browser skill can still explain workflows, but cannot be the sole privacy mechanism. Actual Chrome/CLI fixtures must verify same-name sessions in different scopes, registry isolation, profile/config rejection and owned cleanup before release.
19
+
20
+ **Evidence.** The installed wrapper source separates PI_BROWSER_HOME from Playwright's registry. In a temporary copy installed with the locked manifests (`npm ci --ignore-scripts`), CLI 0.1.19's `cli-client/registry.js` uses OS HOME/cache, scans every workspace and exposes only a PWTEST-specific daemon-root override. A bounded temporary-HOME experiment confirms different PI_BROWSER_HOME values share the daemon directory and registry enumeration sees another synthetic workspace. No browser or personal profile was opened. The experiment describes the pinned implementation; production must not import those private files.
21
+
22
+ **Rejected.** Profile-directory changes or prompt-only session naming as proof of isolation, private PWTEST controls, changing HOME/cwd for all bash tools, SDK extension adaptation, unscoped global browser cleanup and claiming unrestricted coding tools are sandboxed.
@@ -0,0 +1,19 @@
1
+ # 0018. Ordinary `/stop`; scheduled tasks without HTTP `/prompt` and `/send`
2
+
3
+ Status: accepted (2026-10-05; owner accepted 2026-10-05). Supersedes proposed [0014](0014-ephemeral-private-storage.md) and [0015](0015-ordinary-stop-keeps-native-inbox.md). Replaces the HTTP `/prompt` clause of proposed [0012](0012-run-in-owned-conversation.md) and the `/send` resend hint of [0003](0003-replies-at-most-once.md).
4
+
5
+ **Context.** Every production caller of HTTP `/prompt` and `/send` is a script or cron job on the same machine, plus the agent sending files through curl. The work they start runs in a separate context, but its result never reaches the chat's own conversation: when the user replies to a morning summary, the chat does not know what was sent. 0014 and 0015 wait for storage lifecycle work and an upstream Durable API.
6
+
7
+ **Example.** At 08:00 a cron job runs a script that builds the newsletter prompt; the agent works in its own conversation and sends five links. The user replies "expand the third one". The chat conversation must already hold the five links, not the prompt or tool calls that produced them.
8
+
9
+ **Decision.**
10
+
11
+ - **Ordinary `/stop`** (no active `/run`) aborts the generation task that owns the current inputs (`LiveDoc.run`), not the conversation, so later queued messages stay in the inbox. If generation hands the same inputs to a new task meanwhile, abort that one too. Then write a passive `[/stop]` entry so Durable places the queued messages. `/stop` during a run is unchanged.
12
+ - **Tasks.** Cron `prompt` jobs, `schedule_task` firings and background summaries each run in a fresh ownerless conversation, keyed by request ID. Nothing carries over between runs; continuity lives in the chat conversation and memory. Transcripts are kept, not deleted. The model health check reuses one conversation.
13
+ - **The chat records what was sent.** A delivered task answer, and every service or tool send (reminders, cron `send`, background summaries, Automation, `send_message`), is written to the chat conversation as one passive entry with its delivery status, deduplicated by request ID. No extra model turn; the chat's next turn sees it.
14
+ - **No HTTP `/prompt`, `/send` or `ephemeral`.** Cron `send` and `prompt` actions accept an optional `command` (absolute argv). Its stdout replaces the text or prompt; empty output skips the run. Scripts print instead of POSTing, and are scheduled in `cron/jobs.json`. The agent sends files and extra messages with the native `send_message` tool, using the existing direct-send receipts (tool task ID as request ID). Remaining HTTP: read-only pages, reminders, cron management and model health.
15
+ - **Cutover.** The old service still needs `/prompt` and `/send`, so production scripts, crontab entries and `jobs.json` change in the same step as the cutover ([0009](0009-one-shot-cutover.md)).
16
+
17
+ **Consequences.** One way to start agent work from outside a chat, and one place (the chat conversation) that knows what the user saw. `/stop` uses a bounded lookup/abort retry instead of an atomic native operation. Old `ephemeral` deletion is dropped; private tool output stays searchable under `durable/`. External callers on other machines can no longer prompt the agent; a future multi-entry design decides that. The shared scheduler's `jobs.json` parser gains the optional `command` field.
18
+
19
+ **Rejected.** Handing task results to the chat as input (an extra model turn, interrupts current work, rewrites the sent text). Private per-session storage (0014). Waiting for an upstream cancellation API (0015). Keeping `/prompt` and `/send` as thin HTTP entries.
@@ -0,0 +1,41 @@
1
+ # 0019. Remove scheduled services from the chat application
2
+
3
+ Status: proposed
4
+
5
+ **Problem.** The owner wants scheduling removed from this project and will design
6
+ it separately later. Keeping empty job files or startup switches still leaves
7
+ scheduled effects and their dependencies in the chat application's lifecycle.
8
+
9
+ **Example.** Starting messaging must not create reminder databases, register cron
10
+ jobs, run PT checks or watch completion files, even when old configuration exists.
11
+
12
+ **Decision.**
13
+
14
+ - Remove cron, reminders, delayed/repeating prompts, background completion
15
+ watchers/summaries and PT Automation from source, tools, routes, pages, startup,
16
+ preflight and direct dependencies. Do not replace them with disabled stubs.
17
+ - Keep messaging, immediate `send_message`, foreground subagents, `/run`, ordinary
18
+ `/stop`, native task inspection and explicit tool-less model health checks.
19
+ Keep the six-hour context compaction required by ADR 0008; it is a chat
20
+ maintenance timer, not user scheduling.
21
+ - Keep existing settings, memory, history, receipts and offline backup/import
22
+ data intact. Removal does not authorize deleting stored job data, modifying
23
+ installed Pi extensions, or changing the operating system's crontab/launchd.
24
+ This application does not manage or stop independent operating-system jobs.
25
+ - Removed scheduling and Automation endpoints return 404. There is no
26
+ compatibility adapter or automatic migration to another scheduler.
27
+ - Retain historical isolated-conversation metadata for read-only inspection,
28
+ but do not deliver answers from retired scheduled conversations.
29
+
30
+ This proposal supersedes the scheduled-service clauses of ADR 0013 and ADR 0018
31
+ only when accepted. Their ordinary stop, immediate sending and receipt decisions
32
+ remain unchanged. Accepted ADR files are not edited. Scope and implementation
33
+ progress are tracked in GitHub #33, not in this document.
34
+
35
+ **Consequences.** A future scheduler needs its own design and approval. Startup
36
+ still needs independent old producers to be reviewed, and model/Messages live
37
+ acceptance remains separate from removal of scheduled services.
38
+
39
+ **Rejected.** Merely clearing job definitions, hiding tools while retaining
40
+ workers, reviving HTTP `/prompt` or `/send`, and deleting production state as
41
+ part of a source-code change.
@@ -0,0 +1,41 @@
1
+ # 0020. Report native task calls in iMessage
2
+
3
+ Status: accepted (owner approved the format, exclusions, implementation and restart)
4
+
5
+ **Problem.** Final-only replies leave the owner unable to see tool and task
6
+ progress in iMessage while work is running.
7
+
8
+ **Example.** A search emits `[tool] web_search · query=上海浦东 私立医院`
9
+ before the final answer. Other custom tasks emit their native task name. A
10
+ foreground subagent's calls use `[subagent/tool]` and go to its parent chat.
11
+
12
+ **Decision.**
13
+
14
+ - Observe committed native task admission with Durable's public commit API;
15
+ do not wrap tools, poll task phases or introduce another execution loop.
16
+ - With `settings.json` setting `progressMessages: true`, send one plain-text
17
+ notice per new task to its owning, reply-enabled chat, except `pi.generation`
18
+ which the owner excludes. A `pi.tool` task is one tool notice, not a duplicate
19
+ task notice. Keep IDs internal for deduplication; do not display them.
20
+ - Include native names, parent names and bounded argument summaries, not tool
21
+ outputs, thinking, free-form commands, prompts or document contents. Path and
22
+ search-query previews are screened for known credential indicators; URL
23
+ previews omit user info, paths, queries and fragments. Arbitrary user-authored
24
+ secrets cannot be guaranteed detectable.
25
+ - Follow Durable's conversation ownership for child/subagent attribution.
26
+ Health and unrelated ownerless conversations stay local.
27
+ - Use existing direct-send receipts keyed by task ID. Unknown sends are not
28
+ retried. Do not replay pre-existing live tasks on startup or generate model
29
+ context entries for progress notices.
30
+ - Notifications do not gate task execution; drain admitted notices before
31
+ automatic final replies. Stop observation and join pending sends on shutdown.
32
+ - Missing or false configuration retains final-only delivery. No stored
33
+ settings or receipt formats are removed.
34
+
35
+ **Consequences.** iMessage can receive many notices. Progress reflects task
36
+ admission, not confirmed completion. Final answers retain their existing
37
+ reconciliation and at-most-once rules. This expands final-only chat behavior;
38
+ accepted ADR files remain unchanged. Scope and validation belong in GitHub #33.
39
+
40
+ **Rejected.** Asking the model to narrate every call, forwarding raw outputs,
41
+ replaying old task history, or using unreliable delivery confirmation to retry.
@@ -0,0 +1,33 @@
1
+ # 0021. Readable progress without automatic link previews
2
+
3
+ Status: accepted (owner approved implementation and restart on 2026-10-05)
4
+
5
+ Supersedes the argument-display details of ADR 0020; routing, admission,
6
+ receipts, exclusions and recovery remain unchanged.
7
+
8
+ **Problem.** `command=[128 chars]` does not explain the work. Clickable URLs
9
+ in progress and default answers create unwanted iMessage preview cards.
10
+
11
+ **Example.** `[tool] bash · 搜索上海天气 · timeout=30` reports intent without
12
+ revealing a shell command. `url=weather.com.cn` identifies a source without
13
+ a clickable URL.
14
+
15
+ **Decision.**
16
+
17
+ - Add an optional short `description` to the native bash schema. Ask the model
18
+ for a non-sensitive purpose in the user's language; delegate execution,
19
+ timeout validation and output handling unchanged to native bash.
20
+ - Show the screened, bounded description instead of the command or its length.
21
+ Missing or credential-like descriptions use `执行命令`. Raw commands remain
22
+ private. Descriptions report intent, not verified completion.
23
+ - URL arguments show only hostnames with fullwidth dots, never schemes,
24
+ user information, paths, queries or fragments. Screened path/query previews
25
+ omit explicit HTTP(S) URLs.
26
+ - Default answers name sources without URLs. Provide original links when the
27
+ user asks. This is prompt policy, not a transport-level preview toggle or
28
+ a prohibition on explicitly requested links.
29
+ - Credential screening is best effort; arbitrary secrets in model-authored
30
+ labels cannot be guaranteed detectable.
31
+
32
+ **Rejected.** Raw shell snippets, another model call to summarize commands,
33
+ transport retries, and claiming an unsupported Messages preview-disable option.
@@ -0,0 +1,35 @@
1
+ # 0022. Arrow and settlement progress notices
2
+
3
+ Status: accepted (owner requested arrow/checkmark style and omitted timeout on 2026-10-05)
4
+
5
+ Supersedes the admission-only notification/display decisions of ADR 0020 and
6
+ the displayed timeout example in ADR 0021. Other safety and routing decisions
7
+ remain unchanged.
8
+
9
+ **Problem.** Character counts and bracketed tool headers are noisy. The owner
10
+ prefers the earlier arrow/checkmark style and does not need timeout parameters
11
+ in chat.
12
+
13
+ **Example.** `→ bash: 搜索上海天气`, then `✓ bash (0.8s)` on successful settlement.
14
+ An unsuccessful call gets `× bash (0.8s)`, never a success checkmark.
15
+
16
+ **Decision.**
17
+
18
+ - Observe native committed task admission and terminal settlement through the
19
+ existing public commit subscription, without another execution loop.
20
+ - Emit one arrow at admission and one checkmark/cross at settlement for tasks
21
+ admitted during this process lifetime. Exclude generation tasks and retain
22
+ native parent/child ownership, allowlist and private-health routing.
23
+ - For native tools, success requires both a completed task outcome and the
24
+ stored tool-result message having no error flag. Aborted, failed and missing
25
+ or error tool results are not success.
26
+ - Duration is local elapsed admission-to-settlement time, including queueing;
27
+ it is not exact subprocess-only runtime or recipient delivery latency.
28
+ - Keep separate direct-send receipt IDs for admission and settlement. Do not
29
+ replay historical starts/completions or retry uncertain sends after restart.
30
+ - Omit timeout from displayed argument summaries only; native enforcement is
31
+ unchanged. Keep short screened purpose labels and non-clickable hostnames,
32
+ never raw commands or outputs.
33
+ - Final replies drain already queued progress notices. Progress remains
34
+ optional through the existing setting. This intentionally doubles most
35
+ task progress notices compared with admission-only reporting.
@@ -0,0 +1,18 @@
1
+ # 0023. Retain progress type prefixes
2
+
3
+ Status: accepted (owner explicitly corrected the format on 2026-10-05)
4
+
5
+ Supersedes the prefix-free display examples of ADR 0022 only. Admission,
6
+ settlement, privacy, elapsed time and receipt/recovery behavior are unchanged.
7
+
8
+ **Problem.** Arrows and checkmarks replaced useful type labels. The owner wants
9
+ bracketed prefixes retained alongside the new status markers.
10
+
11
+ **Example.** `[tool] → bash: 搜索上海天气`, followed by `[tool] ✓ bash (0.8s)`.
12
+ Owned child tools use `[subagent/tool] → read: path=…` and
13
+ `[subagent/tool] ✓ read (0.8s)`. Native non-tool tasks retain `[task]` and
14
+ parent-qualified task prefixes. Unsuccessful settlement uses `×`.
15
+
16
+ **Decision.** Render the native type/parent prefix first, then the status marker,
17
+ then the native tool/task name and short safe summary or elapsed time. Do not
18
+ restore timeout, raw shell commands or clickable URLs.
@@ -0,0 +1,13 @@
1
+ # 0024. Status marker before the type prefix
2
+
3
+ Status: accepted (owner explicitly corrected marker placement on 2026-10-05)
4
+
5
+ Supersedes the prefix-first display order in ADR 0023 only. Preserve all
6
+ native routing, safe descriptions, settlement verification and receipt rules.
7
+
8
+ **Example.** `→ [tool] bash: Check the weather`, then `✓ [tool] bash (0.8s)`.
9
+ Child tools use `→ [subagent/tool] read: path=…`. Failure uses `×` first.
10
+
11
+ **Decision.** Put the status marker at the very beginning, immediately followed
12
+ by the retained bracketed type/parent label. Ask for short everyday action
13
+ wording instead of implementation jargon. Omit timeout and raw commands.
@@ -0,0 +1,49 @@
1
+ # 0025. Reusable progress data and short command previews
2
+
3
+ Status: accepted (owner approved option 5, then clarified request/response names and app reuse on 2026-10-05)
4
+
5
+ Supersedes the command-opaque/request-name display rules of ADR 0021 and the
6
+ request display examples of ADR 0024. Native lifecycle, routing, receipt IDs,
7
+ uncertain-effect handling and response formatting are unchanged.
8
+
9
+ **Problem.** The owner wants readable request purposes, a useful short bash
10
+ preview, no tool names in any request, and reusable rules for a future app.
11
+
12
+ **Example.**
13
+
14
+ ```text
15
+ → [tool] Check the weather
16
+ node search.mjs "Shanghai weather" --count 5
17
+ ✓ [tool] bash (0.8s)
18
+ ```
19
+
20
+ **Decision.**
21
+
22
+ - Requests keep the leading arrow and bracketed type/parent label. Tool names
23
+ do not appear as separate request labels or parenthetical suffixes. Responses
24
+ keep the deployed format: marker, type/parent label, native name and elapsed
25
+ time. This latest clarification replaces the earlier proposed hidden-bash
26
+ response and option-5 parenthetical response examples.
27
+ - Bash requests add a second line with at most 100 Unicode code points,
28
+ including a trailing ellipsis when truncated. Screen the whole command
29
+ before truncation. Recognized credential indicators suppress the entire
30
+ preview; explicit URLs and common bare domains are defanged. Native
31
+ execution and timeout enforcement remain unchanged.
32
+ - Screening is conservative and best effort, not a guarantee against arbitrary
33
+ unknown secrets. Do not restore raw tool outputs or document-content notices.
34
+ - `src/agent/progress-format.ts` defines a transport-independent structured
35
+ ProgressEvent and pure summarization, screening and text formatting functions.
36
+ The native observer creates safe request/response events; iMessage is the
37
+ current plain-text adapter. Future apps can render event fields and reuse
38
+ the safety functions without parsing iMessage text or copying its lifecycle.
39
+ - The event retains the native name internally, allowing app rendering and
40
+ response formatting, even though request text omits it. Raw command strings
41
+ do not belong in exported progress events; only screened previews do.
42
+ - No app transport, new scheduler, listener, database or delivery replay is
43
+ introduced in this change. A future app connection must wire its own
44
+ delivery/receipt policy explicitly.
45
+
46
+ **Verification.** Isolated regressions cover the request/response event format,
47
+ all-request name omission, native command execution/failure, preview length and
48
+ Unicode, screening before truncation, credential suppression, URL defanging,
49
+ child attribution, unknown receipts and no restart replay.
@@ -0,0 +1,16 @@
1
+ # 0026. Inline command-only requests
2
+
3
+ Status: accepted (owner specified `bash: <command>` and a 50-character limit on 2026-10-05)
4
+
5
+ Supersedes the request-name omission, second-line preview and 100-character
6
+ limit in ADR 0025. Preserve shared ProgressEvent data, screening, native
7
+ execution, ownership, response formatting and receipt/recovery rules.
8
+
9
+ **Example.** `→ [tool] bash: printf hi`, then `✓ [tool] bash (0.8s)`.
10
+ Other tools retain the screenshot format, e.g. `→ [tool] read: path=/tmp/a.jpg`.
11
+
12
+ **Decision.** Requests show native name and safe arguments inline. Bash uses
13
+ only its screened command preview, not purpose/description text. The preview
14
+ is at most 50 Unicode code points including an ellipsis when truncated.
15
+ Screen the entire original command before truncation. No actual execution
16
+ limit or output handling changes. Response notices remain unchanged.
@@ -0,0 +1,10 @@
1
+ # 0027. Modest command preview width
2
+
3
+ Status: accepted (owner delegated a slightly longer preview on 2026-10-05)
4
+
5
+ Supersedes only ADR 0026's fixed 50-character width. The current default is
6
+ 80 Unicode code points, including a trailing ellipsis when truncated. This
7
+ balances useful context with phone readability. Keep the same inline
8
+ `→ [tool] bash: <screened command>` format and unchanged response notices.
9
+ Whole-command screening still occurs before truncation; no purpose text,
10
+ raw output, tool execution or channel architecture changes.
@@ -0,0 +1,8 @@
1
+ # 0028. Use 120 characters for command previews
2
+
3
+ Status: accepted (owner selected120 after viewing80/120/150/180 examples on 2026-10-05)
4
+
5
+ Supersedes only ADR 0027's80-character default. Show at most120 Unicode code
6
+ points in the command portion, including a trailing ellipsis when truncated.
7
+ Retain the inline command-only request, existing response, whole-command
8
+ screening before truncation and transport-independent event/formatter.
@@ -0,0 +1,33 @@
1
+ # 0030. Share a versioned web read cache
2
+
3
+ Status: rejected (owner decided on 2026-10-06: a cold read takes about 40 ms, and every commit invalidated the cache anyway; the cache was removed)
4
+
5
+ **Context.** The chat overview requests each conversation separately and repeats
6
+ reads on refresh and navigation. Large native payloads include model-only data
7
+ that the page never renders. Time-based caching would hide recent changes.
8
+
9
+ **Decision.** Keep one bounded, process-local cache in `web/`, shared by pages
10
+ and read-only APIs. Read through the owning Harness, never its storage files.
11
+ Every non-empty Durable commit changes the native cache version. File-backed
12
+ views validate inode, size and nanosecond modification/change timestamps on
13
+ each access; enumerate memory namespaces on each request to detect additions
14
+ and removals. Coalesce concurrent reads and recheck the version after loading;
15
+ retry if it changed. Do not retain rejected reads or oversized results.
16
+
17
+ Limit the cache to 128 entries and a 16 MiB serialized-size budget using
18
+ least-recently-used eviction. This budget is not an exact JavaScript heap limit.
19
+ Do not cache model health requests or resolved registry-dependent agent choices.
20
+
21
+ Embed display-only chat data in the initial HTML and batch overview refreshes.
22
+ Keep existing native data response shapes. SSE readiness includes a process and
23
+ revision marker so the client can detect changes between HTML loading and stream
24
+ attachment, without always fetching the initial view twice. Refreshes already
25
+ in flight do not discard subsequent change notices.
26
+
27
+ **Consequences.** Restart discards the cache. There is no second durable state,
28
+ execution log, writer or model call. A response reflects versions checked while
29
+ it is read; changes during network transit still require a later refresh. File
30
+ freshness relies on filesystem metadata, not a fixed stale-data interval.
31
+
32
+ **Rejected.** Browser-local copies as the shared cache, fixed cache TTLs,
33
+ persistent materialized views, and rebuilding all page data on every commit.
@@ -0,0 +1,32 @@
1
+ # 0031. Hide only six-hour compaction progress
2
+
3
+ Status: proposed (owner clarified trigger-specific visibility on 2026-10-06)
4
+
5
+ Narrows ADR 0020's compaction notices only for six-hour maintenance ticks.
6
+
7
+ **Problem.** The six-hour timer emits `pi.compaction` progress into chat. The
8
+ owner wants that timer quiet, but wants progress for manual `/compact` and
9
+ automatic context-threshold/overflow compaction.
10
+
11
+ **Example.** A timer tick sends neither a start nor completion notice. Manual
12
+ `/compact` shows both notices and its command result. A native context-triggered
13
+ compaction shows both notices.
14
+
15
+ **Decision.** Track exact task IDs admitted by `compactChats` using process-local
16
+ Harness-scoped state. Before a compaction admission notice, wait for in-flight
17
+ timer admissions to resolve so the observer can classify the committed task
18
+ before `compact()` has returned its ID. Suppress only a matching timer task's
19
+ notice; without an admission notice its settlement also remains quiet. Consume
20
+ the classification after observation. Do not mute an entire conversation or
21
+ filter by native reason: both the timer and `/compact` use reason `manual`.
22
+
23
+ Native manual, threshold and overflow task progress keeps its existing routing,
24
+ receipts and settlement formatting. Timer execution, logs, six-hour cadence,
25
+ manual command results and other progress are unchanged. Classification is not
26
+ persisted because pre-start task progress is never replayed.
27
+
28
+ **Verification.** An existing isolated faux-provider messaging regression with
29
+ progress enabled checks timer admission/settlement create no notices or progress
30
+ receipts, manual `/compact` shows progress and its result, and native tasks with
31
+ threshold/overflow reasons retain both progress receipts. Existing tool and
32
+ child-task progress regressions remain enabled.
@@ -0,0 +1,58 @@
1
+ # 0032. Native background schedules and read-only execution history
2
+
3
+ Status: proposed (owner requested native scheduling, compact migration and Web history)
4
+
5
+ **Problem.** The retired scheduler called HTTP `/prompt`, which no longer exists.
6
+ The six-hour maintenance `setInterval` also loses its next deadline on restart.
7
+
8
+ **Example.** Restart at 07:40 must retain tomorrow's English deadline and the
9
+ maintenance deadline, without starting another message pipeline or recreating
10
+ uncertain sends. A Web refresh must show both jobs without invoking a model.
11
+
12
+ **Decision.**
13
+
14
+ - Register one Durable scheduling extension in the agent layer. Each configured
15
+ job has a dedicated ownerless conversation and a conversation-owned task with
16
+ `background: true`. Its saved `sleep` checkpoint contains the absolute next
17
+ deadline. `runtime.sleep()` is an invocation-local wait, not a persisted timer.
18
+ - An occurrence is a child task admitted atomically with the scheduler's waiting
19
+ checkpoint. Join with `allSettled`, then compute the next deadline, including
20
+ after generation failure. Native task records are the execution history; there
21
+ is no separate host worker database or UI execution log.
22
+ - Support exactly six-hour chat compaction and the explicitly configured daily
23
+ English card. Keep native compaction, its quiet maintenance progress routing,
24
+ and automatic/manual compaction behavior. No arbitrary cron parser or LLM
25
+ scheduling tools. Do not revive legacy job files, HTTP prompt/send, reminders,
26
+ background completion watchers or PT Automation.
27
+ - Daily English uses Asia/Shanghai calendar deadlines. A late start processes
28
+ only the current day after its scheduled time; missed days are not backfilled.
29
+ Compaction processes one overdue tick and skips missed interval slots. Long
30
+ executions never overlap themselves. Configuration is read at startup; existing
31
+ saved deadlines remain, with changed cadence applied to the following slot.
32
+ - Import the configured English `used.json` once, read-only, preserving unknown
33
+ fields. Durable owns subsequent learning progress. Code selects due reviews
34
+ and enforces the existing three-expression daily limit and new-learning quota;
35
+ a tool-less, isolated model conversation provides wording, never updates files
36
+ or sends. Commit learning changes, the actual card and its outbox together.
37
+ Per-date card records and stable submission/request IDs prevent regeneration.
38
+ - Deliver the outbox using existing direct-send receipts and passive chat records.
39
+ Sending is not transactional with Messages.app. `sending` becomes `unknown` on
40
+ restart; uncertain or previously accepted sends are not automatically replayed.
41
+ Stored learning progress means generated/displayed content, not proof of delivery.
42
+ - `/scheduled` and `/scheduled/data` show all native configured schedules, their
43
+ task state/deadline, per-job latest ten occurrence tasks, and the latest ten
44
+ across all jobs. Show generation result separately from delivery receipt. These
45
+ are read-only routes; viewing them never resumes tasks or starts inference.
46
+
47
+ This proposal supersedes ADR 0013's rejection of native recurring task adapters
48
+ and ADR 0019's removal of these two scheduling features only. Other retired
49
+ features and at-most-once transport safeguards remain unchanged.
50
+
51
+ **Consequences.** The service must be running to act at a deadline. Stored
52
+ checkpoints recover execution but cannot wake a stopped machine or process.
53
+ Schedules remain independent of chat abort/reset. Explicitly aborting their task
54
+ pauses them; startup does not silently recreate a terminal scheduler.
55
+
56
+ **Rejected.** An external cron daemon, a second message pipeline, restoring
57
+ `/prompt`, a timer that reschedules from each process startup, asking the model
58
+ to remember to schedule tomorrow, and claiming exactly-once external delivery.