@north-light/crouter 0.3.248 → 0.3.250

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 (172) hide show
  1. package/README.md +1 -1
  2. package/dist/api/__tests__/integration/client.test.js +28 -7
  3. package/dist/api/client.d.ts +1 -1
  4. package/dist/api/client.js +2 -2
  5. package/dist/api/dto/lifecycle.d.ts +3 -0
  6. package/dist/api/dto/messages.d.ts +4 -0
  7. package/dist/api/dto/nodes.d.ts +4 -0
  8. package/dist/api/dto/reports.d.ts +7 -2
  9. package/dist/builtin-memory/00-runtime-base/01-escalation.md +1 -1
  10. package/dist/builtin-memory/05-kinds/review/01-orchestrator.md +3 -0
  11. package/dist/builtin-memory/internal/nodes-and-canvas.md +1 -1
  12. package/dist/builtin-pi-packages/pi-crtr-extensions/README.md +0 -1
  13. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +4 -3
  14. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +4 -3
  15. package/dist/clients/attach/session/chrome-refresh.d.ts +3 -0
  16. package/dist/clients/attach/session/chrome-refresh.js +1 -0
  17. package/dist/clients/attach/viewer.js +532 -532
  18. package/dist/clients/inbox/__tests__/integration/stale-row-activation.test.d.ts +1 -0
  19. package/dist/clients/inbox/__tests__/integration/stale-row-activation.test.js +53 -0
  20. package/dist/clients/inbox/controller.js +32 -5
  21. package/dist/clients/inbox/tui/ansi.d.ts +3 -1
  22. package/dist/clients/inbox/tui/ansi.js +14 -10
  23. package/dist/commands/__tests__/api-client-cold-start-diagnostic.test.js +7 -7
  24. package/dist/commands/api-client.d.ts +2 -2
  25. package/dist/commands/api-client.js +5 -5
  26. package/dist/commands/canvas-browse.js +1 -1
  27. package/dist/commands/node/create.js +3 -1
  28. package/dist/commands/node/inspect.js +1 -0
  29. package/dist/commands/node/lifecycle.js +1 -1
  30. package/dist/commands/node/message.js +6 -4
  31. package/dist/commands/node/wait.js +1 -1
  32. package/dist/commands/push.js +36 -37
  33. package/dist/commands/sys/daemon.js +21 -8
  34. package/dist/commands/sys/setup-core.js +2 -2
  35. package/dist/core/__tests__/broker-stream-watchdog-floor.test.d.ts +1 -0
  36. package/dist/core/__tests__/broker-stream-watchdog-floor.test.js +84 -0
  37. package/dist/core/__tests__/broker-turn-admission.test.d.ts +1 -0
  38. package/dist/core/__tests__/broker-turn-admission.test.js +44 -0
  39. package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +5 -1
  40. package/dist/core/__tests__/child-death-wake.test.js +11 -7
  41. package/dist/core/__tests__/collectible.test.d.ts +1 -0
  42. package/dist/core/__tests__/collectible.test.js +88 -0
  43. package/dist/core/__tests__/context-intro.test.js +4 -41
  44. package/dist/core/__tests__/daemon-boot.test.js +30 -14
  45. package/dist/core/__tests__/daemon-wedge.test.js +25 -19
  46. package/dist/core/__tests__/fixtures/fake-engine.js +11 -0
  47. package/dist/core/__tests__/human-deliver.test.js +5 -5
  48. package/dist/core/__tests__/integration/deferred-no-wake.test.js +65 -4
  49. package/dist/core/__tests__/integration/human-deliver-e2e.test.js +3 -3
  50. package/dist/core/__tests__/integration/revive.test.js +48 -0
  51. package/dist/core/__tests__/integration/spawn-root.test.js +36 -1
  52. package/dist/core/__tests__/integration/worktree-land.test.js +82 -3
  53. package/dist/core/__tests__/integration/worktree-reap.test.js +44 -6
  54. package/dist/core/__tests__/migration.test.js +30 -0
  55. package/dist/core/__tests__/passive-subscription.test.js +39 -1
  56. package/dist/core/__tests__/prune.test.js +12 -3
  57. package/dist/core/__tests__/relaunch-root.test.js +1 -1
  58. package/dist/core/__tests__/respawn-throttle.test.js +16 -146
  59. package/dist/core/__tests__/seam/broker-crash-teardown.test.js +23 -11
  60. package/dist/core/__tests__/seam/broker-provider-retry.test.js +81 -2
  61. package/dist/core/__tests__/seam/dormancy-release.test.js +1 -1
  62. package/dist/core/__tests__/seam/self-close-teardown.test.d.ts +1 -0
  63. package/dist/core/__tests__/seam/self-close-teardown.test.js +49 -0
  64. package/dist/core/__tests__/seam/yield-refresh-transaction.test.js +18 -10
  65. package/dist/core/canvas/canvas.d.ts +9 -15
  66. package/dist/core/canvas/canvas.js +41 -47
  67. package/dist/core/canvas/crons.d.ts +5 -8
  68. package/dist/core/canvas/crons.js +5 -8
  69. package/dist/core/canvas/history.d.ts +5 -0
  70. package/dist/core/canvas/history.js +12 -1
  71. package/dist/core/canvas/migrations.js +58 -1
  72. package/dist/core/canvas/pid.d.ts +1 -1
  73. package/dist/core/canvas/pid.js +5 -2
  74. package/dist/core/canvas/types.d.ts +4 -0
  75. package/dist/core/feed/feed.d.ts +4 -0
  76. package/dist/core/feed/feed.js +13 -8
  77. package/dist/core/feed/inbox.d.ts +2 -1
  78. package/dist/core/feed/inbox.js +22 -3
  79. package/dist/core/git.d.ts +3 -0
  80. package/dist/core/git.js +3 -1
  81. package/dist/core/human/feedback-companion.js +1 -0
  82. package/dist/core/preview-registry.js +0 -1
  83. package/dist/core/review/companion.js +1 -0
  84. package/dist/core/review/realize.js +1 -0
  85. package/dist/core/runtime/broker/auth-reload.d.ts +2 -0
  86. package/dist/core/runtime/broker/auth-reload.js +9 -3
  87. package/dist/core/runtime/broker/engine-drive.d.ts +2 -0
  88. package/dist/core/runtime/broker/engine-drive.js +28 -23
  89. package/dist/core/runtime/broker/event-projection.d.ts +4 -0
  90. package/dist/core/runtime/broker/event-projection.js +43 -23
  91. package/dist/core/runtime/broker/fault-retry.d.ts +9 -0
  92. package/dist/core/runtime/broker/fault-retry.js +94 -8
  93. package/dist/core/runtime/broker/frame-dispatch.d.ts +2 -0
  94. package/dist/core/runtime/broker/frame-dispatch.js +4 -1
  95. package/dist/core/runtime/broker/inbox.js +4 -0
  96. package/dist/core/runtime/broker/rebind.js +18 -0
  97. package/dist/core/runtime/broker/turn-admission.d.ts +11 -0
  98. package/dist/core/runtime/broker/turn-admission.js +31 -0
  99. package/dist/core/runtime/broker/turn-ignition.d.ts +2 -0
  100. package/dist/core/runtime/broker/turn-ignition.js +25 -15
  101. package/dist/core/runtime/broker.js +21 -2
  102. package/dist/core/runtime/close.d.ts +11 -9
  103. package/dist/core/runtime/close.js +23 -19
  104. package/dist/core/runtime/fault.d.ts +21 -0
  105. package/dist/core/runtime/fault.js +129 -19
  106. package/dist/core/runtime/fleet.d.ts +0 -6
  107. package/dist/core/runtime/host.d.ts +7 -1
  108. package/dist/core/runtime/host.js +20 -2
  109. package/dist/core/runtime/nodes.d.ts +5 -1
  110. package/dist/core/runtime/nodes.js +24 -1
  111. package/dist/core/runtime/placement.d.ts +6 -6
  112. package/dist/core/runtime/placement.js +15 -8
  113. package/dist/core/runtime/reset.js +2 -2
  114. package/dist/core/runtime/revive.js +40 -4
  115. package/dist/core/runtime/spawn.d.ts +3 -0
  116. package/dist/core/runtime/spawn.js +1 -0
  117. package/dist/core/runtime/warm-pool.d.ts +4 -0
  118. package/dist/core/runtime/warm-pool.js +2 -0
  119. package/dist/core/worktree.d.ts +4 -3
  120. package/dist/core/worktree.js +73 -27
  121. package/dist/daemon/__tests__/integration/api-startup-readiness.test.d.ts +1 -0
  122. package/dist/daemon/__tests__/integration/api-startup-readiness.test.js +81 -0
  123. package/dist/daemon/api/__tests__/reopen-delivery.test.js +38 -0
  124. package/dist/daemon/api/__tests__/seam/api-server.test.js +1 -0
  125. package/dist/daemon/api/__tests__/seam/leaf-api-parity.test.js +13 -2
  126. package/dist/daemon/api/handlers/bash-jobs.js +2 -0
  127. package/dist/daemon/api/handlers/broker-ops.js +23 -2
  128. package/dist/daemon/api/handlers/human.js +1 -1
  129. package/dist/daemon/api/handlers/messages.js +11 -2
  130. package/dist/daemon/api/handlers/nodes.js +13 -1
  131. package/dist/daemon/api/handlers/reports.js +14 -6
  132. package/dist/daemon/api/map.js +1 -0
  133. package/dist/daemon/api/server.d.ts +9 -12
  134. package/dist/daemon/api/server.js +30 -19
  135. package/dist/daemon/companion-retire.js +3 -9
  136. package/dist/daemon/cron/sinks.js +7 -2
  137. package/dist/daemon/crtrd.js +4 -1
  138. package/dist/daemon/fleet.d.ts +2 -16
  139. package/dist/daemon/fleet.js +63 -150
  140. package/dist/daemon/human/finish.js +2 -0
  141. package/dist/daemon/manage.d.ts +11 -0
  142. package/dist/daemon/manage.js +32 -3
  143. package/dist/daemon/messaging/node-message.js +7 -1
  144. package/dist/daemon/reconcilers/bash-deadline.js +2 -0
  145. package/dist/daemon/reconcilers/broker-supervision.d.ts +1 -0
  146. package/dist/daemon/reconcilers/broker-supervision.js +22 -9
  147. package/dist/daemon/reconcilers/live-obligation.d.ts +16 -6
  148. package/dist/daemon/reconcilers/live-obligation.js +18 -6
  149. package/dist/daemon/reconcilers/node-lifecycle/respawn-policy.d.ts +15 -4
  150. package/dist/daemon/reconcilers/node-lifecycle/respawn-policy.js +34 -3
  151. package/dist/daemon/reconcilers/node-lifecycle/terminating.d.ts +4 -1
  152. package/dist/daemon/reconcilers/node-lifecycle/terminating.js +11 -2
  153. package/dist/daemon/reconcilers/node-lifecycle/tick.js +14 -7
  154. package/dist/daemon/reconcilers/storage-maintenance.d.ts +2 -2
  155. package/dist/daemon/reconcilers/storage-maintenance.js +3 -3
  156. package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +4 -4
  157. package/dist/pi-extensions/canvas-context-intro.d.ts +3 -34
  158. package/dist/pi-extensions/canvas-context-intro.js +4 -76
  159. package/dist/pi-extensions/canvas-review-boundary.d.ts +3 -24
  160. package/dist/pi-extensions/canvas-review-boundary.js +4 -29
  161. package/dist/pi-extensions/canvas-stophook.js +1 -6
  162. package/dist/shared/birth-announcement.d.ts +12 -0
  163. package/dist/shared/birth-announcement.js +25 -0
  164. package/dist/shared/generated-context.d.ts +8 -4
  165. package/dist/shared/generated-context.js +15 -9
  166. package/package.json +4 -4
  167. package/runtime.lock.json +2 -2
  168. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/statusline.ts +0 -254
  169. package/dist/clients/inbox/tui/clipboard.d.ts +0 -2
  170. package/dist/clients/inbox/tui/clipboard.js +0 -26
  171. package/dist/pi-extensions/truncate.d.ts +0 -5
  172. package/dist/pi-extensions/truncate.js +0 -14
package/README.md CHANGED
@@ -45,5 +45,5 @@ To opt out of the bootstrap (e.g. in CI), set `CRTR_NO_BOOTSTRAP=1`.
45
45
  The node runtime hosts the [pi coding agent](https://pi.dev) in each broker. To set your pi install up the way crouter's author runs it, add these to the `packages` array in `~/.pi/agent/settings.json` (neither ships with pi — you add them yourself):
46
46
 
47
47
  - **[pi-claude-oauth-adapter](https://github.com/minzique/dotfiles-agents/tree/main/packages/pi-claude-oauth-adapter)** — third-party npm package; Anthropic OAuth / Claude Code compatibility adapter for pi. Install with `"npm:pi-claude-oauth-adapter"`.
48
- - **pi-crtr-extensions** — crtr's engine-side pi extensions (crtr help-gate, slash-command surfacing, provider rotation, frontmatter rules, statusline). Bundled in this repo at `src/builtin-pi-packages/pi-crtr-extensions` and wired into `~/.pi/agent/settings.json` automatically by `crtr sys setup` — nothing to install separately.
48
+ - **pi-crtr-extensions** — crtr's engine-side pi extensions (crtr help-gate, slash-command surfacing, provider rotation, frontmatter rules). Bundled in this repo at `src/builtin-pi-packages/pi-crtr-extensions` and wired into `~/.pi/agent/settings.json` automatically by `crtr sys setup` — nothing to install separately.
49
49
 
@@ -1,6 +1,6 @@
1
1
  // client.test.ts — regression for issue #516: a cold-start `/healthz` timeout
2
2
  // used to throw a bare `daemon_unavailable` message that discarded the actual
3
- // startup failure (the tail of crtrd.err). `coldStartTimeoutMessage` is the
3
+ // startup failure (the tail of crtrd.log). `coldStartTimeoutMessage` is the
4
4
  // pure composition `handleColdSocket` uses on timeout; this proves an injected
5
5
  // diagnostic is appended (not dropped) and that a missing/empty diagnostic
6
6
  // still falls back to the original bare message unchanged. `safeColdStartDiagnostic`
@@ -11,19 +11,19 @@
11
11
  import { test } from 'node:test';
12
12
  import assert from 'node:assert/strict';
13
13
  import { createServer } from 'node:http';
14
- import { mkdtempSync, rmSync } from 'node:fs';
14
+ import { mkdirSync, mkdtempSync, rmSync } from 'node:fs';
15
15
  import { tmpdir } from 'node:os';
16
16
  import { join } from 'node:path';
17
17
  import { coldStartTimeoutMessage, CrtrClient, safeColdStartDiagnostic } from '../../client.js';
18
18
  import { ApiError } from '../../errors.js';
19
19
  test('coldStartTimeoutMessage appends a present diagnostic to the base message', () => {
20
- const msg = coldStartTimeoutMessage('crtrd.err (tail):\nError: bind EADDRINUSE');
20
+ const msg = coldStartTimeoutMessage('crtrd.log (tail):\nError: bind EADDRINUSE');
21
21
  assert.match(msg, /crtrd did not start/);
22
- assert.match(msg, /crtrd\.err \(tail\):\nError: bind EADDRINUSE/);
22
+ assert.match(msg, /crtrd\.log \(tail\):\nError: bind EADDRINUSE/);
23
23
  });
24
24
  test('coldStartTimeoutMessage falls back to the bare message when no diagnostic is available', () => {
25
- assert.equal(coldStartTimeoutMessage(undefined), 'crtrd did not start; run `crtr sys daemon start` and check crtrd.err.');
26
- assert.equal(coldStartTimeoutMessage(''), 'crtrd did not start; run `crtr sys daemon start` and check crtrd.err.');
25
+ assert.equal(coldStartTimeoutMessage(undefined), 'crtrd did not start; run `crtr sys daemon start` and check crtrd.log.');
26
+ assert.equal(coldStartTimeoutMessage(''), 'crtrd did not start; run `crtr sys daemon start` and check crtrd.log.');
27
27
  });
28
28
  function startDelayedHealthzServer(socketPath, delayMs) {
29
29
  const server = createServer((_req, res) => {
@@ -85,6 +85,27 @@ test('a hang-up mid-mutation reports daemon_restarting, not daemon_unavailable',
85
85
  rmSync(dir, { recursive: true, force: true });
86
86
  }
87
87
  });
88
+ test('a non-socket cold-start path surfaces its diagnostic after autostart times out', async () => {
89
+ const dir = mkdtempSync(join(tmpdir(), 'crtr-client-nonsocket-'));
90
+ const socketPath = join(dir, 'crtrd.sock');
91
+ mkdirSync(socketPath);
92
+ const diagnostic = 'crtrd.log (tail):\napi.server.failed: EADDRINUSE';
93
+ const client = new CrtrClient({
94
+ socketPath,
95
+ autostart: true,
96
+ onColdSocket: async () => { },
97
+ coldStartPollWindowMs: 50,
98
+ coldStartDiagnostic: () => diagnostic,
99
+ });
100
+ try {
101
+ await assert.rejects(() => client.healthz(), (error) => error instanceof ApiError
102
+ && error.code === 'daemon_unavailable'
103
+ && error.message.includes(diagnostic));
104
+ }
105
+ finally {
106
+ rmSync(dir, { recursive: true, force: true });
107
+ }
108
+ });
88
109
  test('cliClient-style cold start fails loud when the injected poll window is shorter than the (valid) startup delay', async () => {
89
110
  const dir = mkdtempSync(join(tmpdir(), 'crtr-client-coldstart-'));
90
111
  const socketPath = join(dir, 'crtrd.sock');
@@ -132,7 +153,7 @@ test('safeColdStartDiagnostic returns undefined for an absent hook', () => {
132
153
  assert.equal(safeColdStartDiagnostic(undefined), undefined);
133
154
  });
134
155
  test('safeColdStartDiagnostic returns the hook result when it succeeds', () => {
135
- assert.equal(safeColdStartDiagnostic(() => 'crtrd.err (tail):\nboom'), 'crtrd.err (tail):\nboom');
156
+ assert.equal(safeColdStartDiagnostic(() => 'crtrd.log (tail):\nboom'), 'crtrd.log (tail):\nboom');
136
157
  });
137
158
  test('safeColdStartDiagnostic treats a THROWING hook as absent, not a propagated error', () => {
138
159
  assert.equal(safeColdStartDiagnostic(() => {
@@ -39,7 +39,7 @@ export interface CrtrClientOptions {
39
39
  onColdSocket?: () => Promise<void>;
40
40
  /** Injected cold-start diagnostic. Called ONLY when the bounded
41
41
  * `/healthz` poll times out after `onColdSocket`, so the caller can attach
42
- * operator-useful context (e.g. a bounded tail of crtrd's stderr log) to the
42
+ * operator-useful context (e.g. a bounded tail of `crtrd.log`) to the
43
43
  * `daemon_unavailable` error instead of it staying a bare message. Must
44
44
  * return synchronously and cheaply — it runs on the failure path, not the
45
45
  * happy path. A thrown/undefined result is treated as "no diagnostic". */
@@ -668,7 +668,7 @@ export class CrtrClient {
668
668
  if (this.socketPath === undefined)
669
669
  return false;
670
670
  const code = err?.code;
671
- return code === 'ECONNREFUSED' || code === 'ENOENT';
671
+ return code === 'ECONNREFUSED' || code === 'ENOENT' || code === 'ENOTSOCK';
672
672
  }
673
673
  /** A connection torn down MID-request (Node's "socket hang up" / a broken
674
674
  * pipe) — as distinct from a refused connect, which means nothing is
@@ -817,7 +817,7 @@ function sleep(ms) {
817
817
  * design) purely so the regression test can assert the composition without
818
818
  * waiting out the real poll window. */
819
819
  export function coldStartTimeoutMessage(diagnostic) {
820
- const base = 'crtrd did not start; run `crtr sys daemon start` and check crtrd.err.';
820
+ const base = 'crtrd did not start; run `crtr sys daemon start` and check crtrd.log.';
821
821
  return diagnostic !== undefined && diagnostic !== '' ? `${base}\n${diagnostic}` : base;
822
822
  }
823
823
  /** Invoke the injected `coldStartDiagnostic` hook, treating a THROW the same
@@ -47,6 +47,9 @@ export interface ReviveAllResultDTO {
47
47
  * from edges; the flag only opts out of cascading descendants. */
48
48
  export interface CloseRequest {
49
49
  cascade?: boolean;
50
+ /** PID of the shell that invoked this close. The daemon excludes this process
51
+ * and its descendants from broker teardown so a node can close itself. */
52
+ caller_pid?: number;
50
53
  /** Root close disposition: `true` finalizes the root to `done` (the browse `x`
51
54
  * "finish" semantics); default/`false` cancels it. The cascade set (computed
52
55
  * server-side) is torn down either way. */
@@ -62,6 +62,10 @@ export interface MessageResultDTO {
62
62
  * frozen and the daemon relaunches it when one frees. The entry is durably
63
63
  * appended either way, and delivery follows that relaunch. */
64
64
  not_revived_reason?: 'capacity_frozen';
65
+ /** Present only when the entry was appended ABOVE the requested tier: a
66
+ * terminal target never takes the later cycle `normal`/`deferred` wait for,
67
+ * so its mail is raised to the steering tier. */
68
+ delivered_tier?: InboxTierDTO;
65
69
  delivered_at?: IsoTime;
66
70
  /** Which channel a `delivery:'interactive'` send actually used: `'engine'`
67
71
  * (live broker frame loop, no inbox entry) or `'inbox'` (durable fallback).
@@ -36,6 +36,8 @@ export interface CreateNodeRequest {
36
36
  /** Caller-supplied short description, preserved against automatic naming. */
37
37
  description?: string;
38
38
  parent?: NodeIdDTO | null;
39
+ /** Node that created this request; absent for an external process. */
40
+ creator?: NodeIdDTO;
39
41
  root?: boolean;
40
42
  /** Lifecycle of the new node. Roots default to `resident`; managed children
41
43
  * default to `terminal`. Use `resident` for a child that remains wakeable
@@ -165,6 +167,8 @@ export interface NodeFaultDTO {
165
167
  /** The full node view — summary ∪ identity extras ∪ edges ∪ paths. Returned by
166
168
  * `GET /v1/nodes/{id}` and by the create/lifecycle actions that yield a node. */
167
169
  export interface NodeDetailDTO extends NodeSummaryDTO {
170
+ /** Node that created this node, or null when an external process did. */
171
+ creator: NodeIdDTO | null;
168
172
  description?: string;
169
173
  /** The namer's prose form of `description` — sentence case, punctuation intact
170
174
  * (`NodeMeta.title`). What a surface showing this node to a person reads;
@@ -1,9 +1,14 @@
1
1
  import type { IsoTime, NodeIdDTO } from './common.js';
2
- /** Report tier — `crtr push {update,urgent,final}`. */
2
+ /** Report kind — `crtr push {update,final}`; legacy `urgent` rows remain readable. */
3
3
  export type ReportTierDTO = 'update' | 'urgent' | 'final';
4
- /** `POST /v1/nodes/{id}/reports` body ({id} = the reporting node). */
4
+ /** Delivery urgency for an update report's subscriber fan-out. */
5
+ export type ReportDeliveryTierDTO = 'deferred' | 'normal' | 'urgent';
6
+ /** `POST /v1/nodes/{id}/reports` body ({id} = the reporting node).
7
+ * `tier` is the stored report kind; `delivery_tier` is the optional inbox
8
+ * delivery urgency for an update's subscriber fan-out. */
5
9
  export interface PushReportRequest {
6
10
  tier: ReportTierDTO;
11
+ delivery_tier?: ReportDeliveryTierDTO;
7
12
  body: string;
8
13
  }
9
14
  /** Result of a push. `transitioned` is present only for `final`, which drives a
@@ -8,7 +8,7 @@ surfaces:
8
8
  ---
9
9
 
10
10
  ## When blocked, want feedback, or need the user
11
- Ask the user only when the work genuinely requires them: intent that stays ambiguous after you have dug, a tradeoff that is genuinely theirs, a real scope or direction change, a destructive or irreversible action, or sign-off on finished work. Settle everything the code, a tool, or a delegate can settle first — a whole goal costs a handful of asks, not a stream. Ask through the human inbox (`crtr human send -h`), then end your turn: a question posed as prose in a reply or report pings nobody, while an ask lands on their screen and its answer wakes you.
11
+ Ask the user only when the work genuinely requires them: intent that stays ambiguous after you have dug, a tradeoff that is genuinely theirs, a real scope or direction change, a destructive or irreversible action, or sign-off on finished work. Settle everything the code, a tool, or a delegate can settle first — a whole goal costs a handful of asks, not a stream. Ask through the human inbox (`crtr human -h`), then end your turn: a question posed as prose in a reply or report pings nobody, while an ask lands on their screen and its answer wakes you.
12
12
 
13
13
  ## When crtr itself misbehaves
14
14
  A `crtr` command that errors unexpectedly, hangs, churns, double-spawns, or contradicts its own `-h` is a harness bug — don't silently work around it. Run `crtr sys feedback` to report it (`-h` for how), then continue.
@@ -15,3 +15,6 @@ Choose the one decomposition axis that best covers this surface: **units** (file
15
15
  Synthesize the child reports yourself into the final review output: one deduplicated, severity-normalized verdict, most important first. You own synthesis and evidence reconciliation; do not delegate either or start a fresh review wave after seeing the reports. The owner disposes findings and validates changed behavior. Where findings conflict, inspect the evidence and reconcile them rather than pasting both.
16
16
 
17
17
  Steer bug findings toward substance in synthesis: weight a traced failing path over a speculative "could break", and prune speculation no child attempted to confirm rather than forwarding it. Code-quality findings are observable facts and carry no such burden. A child's security concern meets the same exploit-path bar — an unknown threat-model assumption it surfaces comes out of your severity-rated verdict and becomes the question you ask.
18
+
19
+ ## Avoiding overcomplication
20
+ The nodes beneath you can be nitpicky, but your job is to maintain the greater picture and vision. If a reviewer node raises an issue that is out of scope of the intent of the feature or change that's being reviewed, then it's your responsibility to not get "lost in the sauce" and just flag every issue that could possibly happen, but instead have taste as an engineer and an understanding of the broader vision.
@@ -29,7 +29,7 @@ Navigate/steer: `surface node focus` (put an attach viewer for a node in your pa
29
29
  Nothing is reported automatically — the feed contains only what a node **pushes**.
30
30
 
31
31
  - `push update` — routine progress; fans a lightweight pointer to subscribers, no forced wake.
32
- - `push urgent` — force-wakes every subscriber (you're blocked, scope changed, an error derails the plan).
32
+ - `push update --tier urgent` — force-wakes every subscriber (you're blocked, scope changed, an error derails the plan).
33
33
  - `push final` — the ONLY way a terminal node finishes: writes the canonical result, marks the node done, and tears down its broker. Stopping without it is not finishing. Resident/user-facing nodes stay dormant instead of finishing unless explicitly forced.
34
34
 
35
35
  A push fans a ~30-token **pointer** (a ref path), not the content; subscribers dereference lazily. When a subscriber push wakes you, **the wake message already IS the coalesced digest** — dereference the refs that matter rather than trying to re-read it. An empty feed while workers run is normal — a worker that hasn't pushed yet leaves no pointer; the wake it fires when it does push is automatic, so there's nothing to poll or check in the meantime.
@@ -37,7 +37,6 @@ directory's `node_modules/`.
37
37
  | `crtr-commands/` | Auto-generates a slash command per `crtr` CLI node. The tree is derived in-process from crtr's own `buildRoot()` (no subprocesses, no cache on disk); `filters.json` controls which nodes are exposed. |
38
38
  | `sysprompt-window.ts` | Registers `/sysprompt`, which runs `crtr sys context sysprompt --window` without injecting the prompt into context. |
39
39
  | `frontmatter-rules/` | Injects `.pi/rules/*.md` whose `when:` frontmatter matches a read markdown file. `.claude/rules` are migrated into substrate docs with read-glob `surfaces` entries via `crtr sys sync project-guidance`. Needs the `yaml` dep. See the `pi-frontmatter-rules` skill. |
40
- | `statusline.ts` | Custom status line. |
41
40
 
42
41
  Removing pi's self-referential prompt text (the harness identity sentence and
43
42
  the pi documentation block) is NOT done here. It runs as a canvas extension, so
@@ -1,6 +1,7 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import { createAssistantMessageEventStream, } from '@earendil-works/pi-ai';
3
- import { streamSimpleAnthropic, streamSimpleOpenAICodexResponses } from '@earendil-works/pi-ai/compat';
3
+ import { streamSimple as streamAnthropicMessages } from '@earendil-works/pi-ai/api/anthropic-messages';
4
+ import { streamSimple as streamOpenAICodexResponses } from '@earendil-works/pi-ai/api/openai-codex-responses';
4
5
  import { ANTHROPIC_PROVIDER_ID, OPENAI_CODEX_PROVIDER_ID, INVALID_REFRESH_TOKEN_BACKOFF_MS, formatStatusLine, getProviderLabel, isManagedProvider, isInvalidRefreshTokenError, addSubscription, enrichManagedLoginIdentity, markSubscriptionAuthenticationFailed, markSubscriptionRateLimited, markSubscriptionSuccess, parseRetryAfterHeader, promoteSubscription, readRotationConfig, readSubscriptionPool, refreshSubscriptionCredential, removeManagedAccount, resolveProviderCandidates, upsertSubscriptionWithUniqueAccount, } from '../../../core/subscription-state.js';
5
6
  import { readMergedLaunchConfig } from '../../../core/config.js';
6
7
  import { expandModelCandidates, modelRequestFromConfig, probeRouteAvailability } from '../../../core/model-routes.js';
@@ -305,9 +306,9 @@ async function reauthenticateCredential(state, providerId, credential) {
305
306
  }
306
307
  function defaultStreamForProvider(model, context, options) {
307
308
  if (model.provider === ANTHROPIC_PROVIDER_ID) {
308
- return streamSimpleAnthropic(model, context, options);
309
+ return streamAnthropicMessages(model, context, options);
309
310
  }
310
- return streamSimpleOpenAICodexResponses(model, context, options);
311
+ return streamOpenAICodexResponses(model, context, options);
311
312
  }
312
313
  // Indirection so tests can inject a fake per-provider stream without real network calls.
313
314
  let activeStreamForProvider = defaultStreamForProvider;
@@ -12,7 +12,8 @@ import {
12
12
  type OAuthAuth,
13
13
  type SimpleStreamOptions,
14
14
  } from '@earendil-works/pi-ai';
15
- import { streamSimpleAnthropic, streamSimpleOpenAICodexResponses } from '@earendil-works/pi-ai/compat';
15
+ import { streamSimple as streamAnthropicMessages } from '@earendil-works/pi-ai/api/anthropic-messages';
16
+ import { streamSimple as streamOpenAICodexResponses } from '@earendil-works/pi-ai/api/openai-codex-responses';
16
17
  import {
17
18
  ANTHROPIC_PROVIDER_ID,
18
19
  OPENAI_CODEX_PROVIDER_ID,
@@ -441,9 +442,9 @@ async function reauthenticateCredential(state: ProviderRotationState, providerId
441
442
 
442
443
  function defaultStreamForProvider(model: Model<any>, context: Context, options: SimpleStreamOptions | undefined): AssistantMessageEventStream {
443
444
  if (model.provider === ANTHROPIC_PROVIDER_ID) {
444
- return streamSimpleAnthropic(model as Model<"anthropic-messages">, context, options);
445
+ return streamAnthropicMessages(model as Model<"anthropic-messages">, context, options);
445
446
  }
446
- return streamSimpleOpenAICodexResponses(model as Model<"openai-codex-responses">, context, options);
447
+ return streamOpenAICodexResponses(model as Model<"openai-codex-responses">, context, options);
447
448
  }
448
449
 
449
450
  // Indirection so tests can inject a fake per-provider stream without real network calls.
@@ -8,6 +8,9 @@ export interface ChromeRefreshHooks {
8
8
  refreshRoster: () => Promise<void>;
9
9
  /** Repaint the graph overlay if open. */
10
10
  refreshGraph: () => void;
11
+ /** Re-read the node's persona/lifecycle/icon from canvas — a runtime
12
+ * promote/reshape changes it with no broker event, so the poll catches it. */
13
+ refreshIdentity: () => void;
11
14
  renderFooter: () => void;
12
15
  renderExtensionWidgets: () => void;
13
16
  /** Re-poll this pane's own git context (owned by the editor chrome). */
@@ -59,6 +59,7 @@ export function createChromeRefresh(ctx, hooks) {
59
59
  // source (child status flips emit no broker event) and re-poll the ask map asynchronously.
60
60
  const pollChrome = () => {
61
61
  refresh();
62
+ hooks.refreshIdentity();
62
63
  hooks.renderExtensionWidgets();
63
64
  hooks.pollGit();
64
65
  // R4: re-evaluate the bash-hint threshold on the existing 5s cadence, so a