@north-light/crouter 0.3.249 → 0.3.251
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/api/__tests__/integration/client.test.js +28 -7
- package/dist/api/client.d.ts +1 -1
- package/dist/api/client.js +2 -2
- package/dist/api/dto/lifecycle.d.ts +3 -0
- package/dist/api/dto/messages.d.ts +4 -0
- package/dist/api/dto/nodes.d.ts +4 -0
- package/dist/api/dto/reports.d.ts +7 -2
- package/dist/builtin-memory/00-runtime-base/01-escalation.md +1 -1
- package/dist/builtin-memory/05-kinds/review/01-orchestrator.md +3 -0
- package/dist/builtin-memory/internal/nodes-and-canvas.md +1 -1
- package/dist/builtin-pi-packages/pi-crtr-extensions/README.md +0 -1
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +4 -3
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +4 -3
- package/dist/clients/attach/session/chrome-refresh.d.ts +3 -0
- package/dist/clients/attach/session/chrome-refresh.js +1 -0
- package/dist/clients/attach/viewer.js +532 -532
- package/dist/clients/inbox/__tests__/integration/stale-row-activation.test.d.ts +1 -0
- package/dist/clients/inbox/__tests__/integration/stale-row-activation.test.js +53 -0
- package/dist/clients/inbox/controller.js +32 -5
- package/dist/clients/inbox/tui/ansi.d.ts +3 -1
- package/dist/clients/inbox/tui/ansi.js +14 -10
- package/dist/commands/__tests__/api-client-cold-start-diagnostic.test.js +7 -7
- package/dist/commands/api-client.d.ts +2 -2
- package/dist/commands/api-client.js +5 -5
- package/dist/commands/canvas-browse.js +1 -1
- package/dist/commands/node/create.js +3 -1
- package/dist/commands/node/inspect.js +1 -0
- package/dist/commands/node/lifecycle.js +1 -1
- package/dist/commands/node/message.js +6 -4
- package/dist/commands/node/wait.js +1 -1
- package/dist/commands/push.js +36 -37
- package/dist/commands/sys/daemon.js +21 -8
- package/dist/commands/sys/setup-core.js +2 -2
- package/dist/core/__tests__/broker-stream-watchdog-floor.test.d.ts +1 -0
- package/dist/core/__tests__/broker-stream-watchdog-floor.test.js +84 -0
- package/dist/core/__tests__/broker-turn-admission.test.d.ts +1 -0
- package/dist/core/__tests__/broker-turn-admission.test.js +44 -0
- package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +100 -1
- package/dist/core/__tests__/child-death-wake.test.js +11 -7
- package/dist/core/__tests__/collectible.test.d.ts +1 -0
- package/dist/core/__tests__/collectible.test.js +88 -0
- package/dist/core/__tests__/context-intro.test.js +4 -41
- package/dist/core/__tests__/daemon-boot.test.js +30 -14
- package/dist/core/__tests__/daemon-wedge.test.js +25 -19
- package/dist/core/__tests__/fixtures/fake-engine.js +20 -2
- package/dist/core/__tests__/human-deliver.test.js +5 -5
- package/dist/core/__tests__/integration/deferred-no-wake.test.js +65 -4
- package/dist/core/__tests__/integration/human-deliver-e2e.test.js +3 -3
- package/dist/core/__tests__/integration/revive.test.js +48 -0
- package/dist/core/__tests__/integration/spawn-root.test.js +36 -1
- package/dist/core/__tests__/integration/worktree-land.test.js +82 -3
- package/dist/core/__tests__/integration/worktree-reap.test.js +44 -6
- package/dist/core/__tests__/migration.test.js +30 -0
- package/dist/core/__tests__/passive-subscription.test.js +39 -1
- package/dist/core/__tests__/prune.test.js +12 -3
- package/dist/core/__tests__/relaunch-root.test.js +1 -1
- package/dist/core/__tests__/respawn-throttle.test.js +16 -146
- package/dist/core/__tests__/seam/broker-crash-teardown.test.js +23 -11
- package/dist/core/__tests__/seam/broker-provider-retry.test.js +81 -2
- package/dist/core/__tests__/seam/dormancy-release.test.js +1 -1
- package/dist/core/__tests__/seam/held-deferred-human-prompt.test.d.ts +1 -0
- package/dist/core/__tests__/seam/held-deferred-human-prompt.test.js +63 -0
- package/dist/core/__tests__/seam/self-close-teardown.test.d.ts +1 -0
- package/dist/core/__tests__/seam/self-close-teardown.test.js +49 -0
- package/dist/core/__tests__/seam/yield-refresh-transaction.test.js +18 -10
- package/dist/core/canvas/canvas.d.ts +9 -15
- package/dist/core/canvas/canvas.js +41 -47
- package/dist/core/canvas/crons.d.ts +5 -8
- package/dist/core/canvas/crons.js +5 -8
- package/dist/core/canvas/history.d.ts +5 -0
- package/dist/core/canvas/history.js +12 -1
- package/dist/core/canvas/migrations.js +58 -1
- package/dist/core/canvas/pid.d.ts +1 -1
- package/dist/core/canvas/pid.js +5 -2
- package/dist/core/canvas/types.d.ts +4 -0
- package/dist/core/feed/feed.d.ts +4 -0
- package/dist/core/feed/feed.js +13 -8
- package/dist/core/feed/inbox.d.ts +2 -1
- package/dist/core/feed/inbox.js +22 -3
- package/dist/core/git.d.ts +3 -0
- package/dist/core/git.js +3 -1
- package/dist/core/human/feedback-companion.js +1 -0
- package/dist/core/preview-registry.js +0 -1
- package/dist/core/review/companion.js +1 -0
- package/dist/core/review/realize.js +1 -0
- package/dist/core/runtime/broker/auth-reload.d.ts +2 -0
- package/dist/core/runtime/broker/auth-reload.js +9 -3
- package/dist/core/runtime/broker/engine-drive.d.ts +2 -0
- package/dist/core/runtime/broker/engine-drive.js +39 -23
- package/dist/core/runtime/broker/event-projection.d.ts +4 -0
- package/dist/core/runtime/broker/event-projection.js +43 -23
- package/dist/core/runtime/broker/fault-retry.d.ts +9 -0
- package/dist/core/runtime/broker/fault-retry.js +94 -8
- package/dist/core/runtime/broker/frame-dispatch.d.ts +2 -0
- package/dist/core/runtime/broker/frame-dispatch.js +4 -1
- package/dist/core/runtime/broker/held-deferred-inbox.d.ts +9 -0
- package/dist/core/runtime/broker/held-deferred-inbox.js +8 -0
- package/dist/core/runtime/broker/inbox.js +4 -0
- package/dist/core/runtime/broker/rebind.js +18 -0
- package/dist/core/runtime/broker/turn-admission.d.ts +11 -0
- package/dist/core/runtime/broker/turn-admission.js +31 -0
- package/dist/core/runtime/broker/turn-ignition.d.ts +2 -0
- package/dist/core/runtime/broker/turn-ignition.js +25 -15
- package/dist/core/runtime/broker.js +21 -2
- package/dist/core/runtime/close.d.ts +11 -9
- package/dist/core/runtime/close.js +23 -19
- package/dist/core/runtime/fault.d.ts +21 -0
- package/dist/core/runtime/fault.js +129 -19
- package/dist/core/runtime/fleet.d.ts +0 -6
- package/dist/core/runtime/host.d.ts +7 -1
- package/dist/core/runtime/host.js +20 -2
- package/dist/core/runtime/nodes.d.ts +5 -1
- package/dist/core/runtime/nodes.js +24 -1
- package/dist/core/runtime/placement.d.ts +6 -6
- package/dist/core/runtime/placement.js +15 -8
- package/dist/core/runtime/reset.js +2 -2
- package/dist/core/runtime/revive.js +40 -4
- package/dist/core/runtime/spawn.d.ts +3 -0
- package/dist/core/runtime/spawn.js +1 -0
- package/dist/core/runtime/warm-pool.d.ts +4 -0
- package/dist/core/runtime/warm-pool.js +2 -0
- package/dist/core/worktree.d.ts +4 -3
- package/dist/core/worktree.js +73 -27
- package/dist/daemon/__tests__/integration/api-startup-readiness.test.d.ts +1 -0
- package/dist/daemon/__tests__/integration/api-startup-readiness.test.js +81 -0
- package/dist/daemon/api/__tests__/reopen-delivery.test.js +38 -0
- package/dist/daemon/api/__tests__/seam/api-server.test.js +1 -0
- package/dist/daemon/api/__tests__/seam/leaf-api-parity.test.js +13 -2
- package/dist/daemon/api/handlers/bash-jobs.js +2 -0
- package/dist/daemon/api/handlers/broker-ops.js +23 -2
- package/dist/daemon/api/handlers/human.js +1 -1
- package/dist/daemon/api/handlers/messages.js +11 -2
- package/dist/daemon/api/handlers/nodes.js +13 -1
- package/dist/daemon/api/handlers/reports.js +14 -6
- package/dist/daemon/api/map.js +1 -0
- package/dist/daemon/api/server.d.ts +9 -12
- package/dist/daemon/api/server.js +30 -19
- package/dist/daemon/companion-retire.js +3 -9
- package/dist/daemon/cron/sinks.js +7 -2
- package/dist/daemon/crtrd.js +4 -1
- package/dist/daemon/fleet.d.ts +2 -16
- package/dist/daemon/fleet.js +63 -150
- package/dist/daemon/human/finish.js +2 -0
- package/dist/daemon/manage.d.ts +11 -0
- package/dist/daemon/manage.js +32 -3
- package/dist/daemon/messaging/node-message.js +7 -1
- package/dist/daemon/reconcilers/bash-deadline.js +2 -0
- package/dist/daemon/reconcilers/broker-supervision.d.ts +1 -0
- package/dist/daemon/reconcilers/broker-supervision.js +22 -9
- package/dist/daemon/reconcilers/live-obligation.d.ts +16 -6
- package/dist/daemon/reconcilers/live-obligation.js +18 -6
- package/dist/daemon/reconcilers/node-lifecycle/respawn-policy.d.ts +15 -4
- package/dist/daemon/reconcilers/node-lifecycle/respawn-policy.js +34 -3
- package/dist/daemon/reconcilers/node-lifecycle/terminating.d.ts +4 -1
- package/dist/daemon/reconcilers/node-lifecycle/terminating.js +11 -2
- package/dist/daemon/reconcilers/node-lifecycle/tick.js +14 -7
- package/dist/daemon/reconcilers/storage-maintenance.d.ts +2 -2
- package/dist/daemon/reconcilers/storage-maintenance.js +3 -3
- package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +4 -4
- package/dist/pi-extensions/canvas-context-intro.d.ts +3 -34
- package/dist/pi-extensions/canvas-context-intro.js +4 -76
- package/dist/pi-extensions/canvas-inbox-watcher.js +105 -4
- package/dist/pi-extensions/canvas-review-boundary.d.ts +3 -24
- package/dist/pi-extensions/canvas-review-boundary.js +4 -29
- package/dist/pi-extensions/canvas-stophook.js +1 -6
- package/dist/shared/birth-announcement.d.ts +12 -0
- package/dist/shared/birth-announcement.js +25 -0
- package/dist/shared/generated-context.d.ts +8 -4
- package/dist/shared/generated-context.js +15 -9
- package/package.json +4 -4
- package/runtime.lock.json +2 -2
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/statusline.ts +0 -254
- package/dist/pi-extensions/truncate.d.ts +0 -5
- package/dist/pi-extensions/truncate.js +0 -14
|
@@ -11,26 +11,16 @@
|
|
|
11
11
|
// stamp and receives a new baseline. The preference snapshot and persona
|
|
12
12
|
// acknowledgment persist only after Pi materializes that baseline.
|
|
13
13
|
//
|
|
14
|
-
// COLLAPSED BY DEFAULT: a `registerMessageRenderer` keyed to our customType
|
|
15
|
-
// renders the block as a single one-line stub; the full body only appears when
|
|
16
|
-
// the user expands tool output (Ctrl+O / `app.tools.expand`). pi drives this via
|
|
17
|
-
// `CustomMessageComponent.setExpanded(toolOutputExpanded)`, so the same toggle
|
|
18
|
-
// that expands tool results expands the bearings. The renderer returns a plain
|
|
19
|
-
// object satisfying pi's structural `Component` interface ({ render, invalidate })
|
|
20
|
-
// — no pi-tui class needed. The LLM always sees the full `content` regardless of
|
|
21
|
-
// how it renders; the renderer is display-only.
|
|
22
|
-
//
|
|
23
14
|
// Plain TS-with-types — no imports from @earendil-works/* so this compiles inside
|
|
24
15
|
// crouter's own tsc build without a dep on the pi packages.
|
|
25
16
|
import { envNodeId } from '../shared/env.js';
|
|
26
|
-
import { CONTEXT_INTRO_CUSTOM_TYPE, formatCard
|
|
17
|
+
import { CONTEXT_INTRO_CUSTOM_TYPE, formatCard } from '../shared/generated-context.js';
|
|
27
18
|
import { brokerExtensionState } from '../core/runtime/broker/daemon-ops.js';
|
|
28
19
|
import { buildContextBearingsFromState, buildForkBearingsFromState, buildProjectContextBlockForBroker, renderPreferencesFromState, } from '../core/runtime/broker-extension-render.js';
|
|
29
20
|
import { acknowledgePersonaBaseline } from '../core/runtime/broker-persona-guidance.js';
|
|
30
21
|
import { situationalContextEnvelope } from '../core/runtime/situational-context.js';
|
|
31
22
|
import { clearContextExposureState, cloneTranscriptExposureState, emptyContextExposureState, exposureTarget, freezePreferenceSnapshot, hasTranscriptExposure, loadContextExposureState, replaceContextExposureState, saveContextExposureState, sharedContextExposureState, } from '../core/substrate/injected-store.js';
|
|
32
23
|
import { clearSessionCache } from '../core/substrate/session-cache.js';
|
|
33
|
-
import { truncateToWidth } from './truncate.js';
|
|
34
24
|
/** The `customType` stamped on the injected session message. Used both to write
|
|
35
25
|
* the entry and to detect it on resume (the idempotency guard). Defined beside
|
|
36
26
|
* the block builder in `core/runtime/bearings.ts` because the warm-pool claim's
|
|
@@ -49,16 +39,7 @@ export async function buildForkContextIntro(nodeId) {
|
|
|
49
39
|
const state = await brokerExtensionState(nodeId);
|
|
50
40
|
return buildForkBearingsFromState(state, situationalContextEnvelope(nodeId) ?? '');
|
|
51
41
|
}
|
|
52
|
-
//
|
|
53
|
-
/** Pull the plain text out of a custom message's content (string or blocks). */
|
|
54
|
-
function messageText(message) {
|
|
55
|
-
if (typeof message.content === 'string')
|
|
56
|
-
return message.content;
|
|
57
|
-
return message.content
|
|
58
|
-
.filter((c) => c.type === 'text' && typeof c.text === 'string')
|
|
59
|
-
.map((c) => c.text)
|
|
60
|
-
.join('\n');
|
|
61
|
-
}
|
|
42
|
+
// Message extraction
|
|
62
43
|
/** Plain text of a session entry's content (string or text blocks), '' when
|
|
63
44
|
* absent. Used by the idempotency guard to tell OUR own injected bearings
|
|
64
45
|
* (which name our node id) from a SOURCE's bearings copied in by `--fork-from`. */
|
|
@@ -74,62 +55,9 @@ function entryText(e) {
|
|
|
74
55
|
}
|
|
75
56
|
return '';
|
|
76
57
|
}
|
|
77
|
-
function activeBranch(ctx) {
|
|
78
|
-
return typeof ctx.sessionManager.getBranch === 'function'
|
|
79
|
-
? ctx.sessionManager.getBranch()
|
|
80
|
-
: ctx.sessionManager.getEntries();
|
|
81
|
-
}
|
|
82
|
-
/** Hard-wrap a single logical line to `width` columns (content carries no ANSI).
|
|
83
|
-
* Code-point aware so wide-string slicing never splits a surrogate pair; the
|
|
84
|
-
* bearings prose is plain text, so code-point count == visible columns. */
|
|
85
|
-
function wrapLine(line, width) {
|
|
86
|
-
if (width <= 0)
|
|
87
|
-
return [''];
|
|
88
|
-
const chars = Array.from(line);
|
|
89
|
-
if (chars.length <= width)
|
|
90
|
-
return [line];
|
|
91
|
-
const out = [];
|
|
92
|
-
for (let i = 0; i < chars.length; i += width)
|
|
93
|
-
out.push(chars.slice(i, i + width).join(''));
|
|
94
|
-
return out;
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Renderer for `crtr-context` messages. Collapsed (default) shows a one-line
|
|
98
|
-
* stub; expanded (Ctrl+O) shows the label + full body. Returns a plain object
|
|
99
|
-
* matching pi's structural `Component` interface — no pi-tui import. Exported for
|
|
100
|
-
* testing.
|
|
101
|
-
*/
|
|
102
|
-
export function renderContextMessage(message, options, theme) {
|
|
103
|
-
const expanded = options?.expanded === true;
|
|
104
|
-
const paint = (color, text) => typeof theme?.fg === 'function' ? theme.fg(color, text) : text;
|
|
105
|
-
return {
|
|
106
|
-
render(width) {
|
|
107
|
-
const w = typeof width === 'number' && width > 0 ? width : 80;
|
|
108
|
-
if (!expanded) {
|
|
109
|
-
// Truncate BEFORE painting so the ANSI wrapper never inflates the
|
|
110
|
-
// measured width; an over-wide line aborts pi's entire TUI render.
|
|
111
|
-
const stub = `[${CONTEXT_INTRO_CUSTOM_TYPE}] orienting bearings — ctrl+o to expand`;
|
|
112
|
-
return [paint('dim', truncateToWidth(stub, w))];
|
|
113
|
-
}
|
|
114
|
-
const lines = [paint('customMessageLabel', truncateToWidth(`[${CONTEXT_INTRO_CUSTOM_TYPE}]`, w)), ''];
|
|
115
|
-
const body = parseCard({ role: 'custom', customType: message.customType, content: messageText(message) })?.body ?? messageText(message);
|
|
116
|
-
for (const raw of body.split('\n')) {
|
|
117
|
-
for (const wrapped of wrapLine(raw, w))
|
|
118
|
-
lines.push(paint('customMessageText', wrapped));
|
|
119
|
-
}
|
|
120
|
-
return lines;
|
|
121
|
-
},
|
|
122
|
-
invalidate() {
|
|
123
|
-
/* stateless — nothing to clear */
|
|
124
|
-
},
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
58
|
// Extension
|
|
128
59
|
/** Register new-context baseline assembly; a strict resume changes nothing. */
|
|
129
60
|
export function registerCanvasContextIntro(pi) {
|
|
130
|
-
// Collapse the block to a one-liner until the user expands tool output (Ctrl+O).
|
|
131
|
-
// Harmless to register outside TUI mode (it's only consulted while rendering).
|
|
132
|
-
pi.registerMessageRenderer(CONTEXT_INTRO_CUSTOM_TYPE, renderContextMessage);
|
|
133
61
|
pi.on('session_start', async (event, ctx) => {
|
|
134
62
|
const nodeId = envNodeId();
|
|
135
63
|
if (nodeId === undefined || nodeId.trim() === '')
|
|
@@ -150,7 +78,7 @@ export function registerCanvasContextIntro(pi) {
|
|
|
150
78
|
}
|
|
151
79
|
// The active branch, rather than the whole session file, is the context the
|
|
152
80
|
// next turn sees. A new cycle therefore receives its own current baseline.
|
|
153
|
-
const contextEntries =
|
|
81
|
+
const contextEntries = ctx.sessionManager.getBranch().filter((e) => e.type === 'custom_message' && e.customType === CONTEXT_INTRO_CUSTOM_TYPE);
|
|
154
82
|
const ours = contextEntries.some((e) => e.details?.nodeId === nodeId || entryText(e).includes(nodeId));
|
|
155
83
|
if (ours)
|
|
156
84
|
return;
|
|
@@ -170,7 +98,7 @@ export function registerCanvasContextIntro(pi) {
|
|
|
170
98
|
display: true,
|
|
171
99
|
details: { nodeId },
|
|
172
100
|
});
|
|
173
|
-
const delivered =
|
|
101
|
+
const delivered = ctx.sessionManager.getBranch().some((entry) => entry.type === 'custom_message'
|
|
174
102
|
&& entry.customType === CONTEXT_INTRO_CUSTOM_TYPE
|
|
175
103
|
&& entry.details?.nodeId === nodeId
|
|
176
104
|
&& entryText(entry) === messageContent);
|
|
@@ -10,7 +10,10 @@
|
|
|
10
10
|
// Context sends never trigger a turn: the visible human delivery is the sole
|
|
11
11
|
// trigger, so the agent cannot run on context alone before hearing the person.
|
|
12
12
|
// A context-only batch keeps its wake by triggering on its LAST card while idle,
|
|
13
|
-
// and rides the running turn while streaming.
|
|
13
|
+
// and rides the running turn while streaming. A deferred-only idle batch stays
|
|
14
|
+
// held until either durable mail wakes a turn or the broker prepends it to an
|
|
15
|
+
// already-admitted live human prompt; that bridge still commits only at
|
|
16
|
+
// agent_settled.
|
|
14
17
|
import { envNodeId, envProfileId, envWatcherDebounceMs, envWatcherTickMs } from '../shared/env.js';
|
|
15
18
|
import { coalesceBrokerInbox, entryCards, readBrokerCanceledEntryIds, readBrokerCursor, readBrokerInboxSince, reportRefNodeId, } from '../core/runtime/broker/inbox.js';
|
|
16
19
|
import { advanceBrokerInboxCursor, brokerExtensionState } from '../core/runtime/broker/daemon-ops.js';
|
|
@@ -20,6 +23,7 @@ import { errorClassFromError } from '../core/events/errors.js';
|
|
|
20
23
|
import { operationIdContext } from '../core/events/operation-id.js';
|
|
21
24
|
import { situationalContextEnvelope, SITUATIONAL_CONTEXT_CUSTOM_TYPE } from '../core/runtime/situational-context.js';
|
|
22
25
|
import { isProfilePaused } from '../core/profiles/manifest.js';
|
|
26
|
+
import { HELD_DEFERRED_INBOX_PROMPT_JOIN, } from '../core/runtime/broker/held-deferred-inbox.js';
|
|
23
27
|
const MAX_PENDING_HANDOFFS = 64;
|
|
24
28
|
const MAX_SOURCE_OPERATION_IDS = 16;
|
|
25
29
|
let currentWatcher;
|
|
@@ -188,6 +192,7 @@ class CanvasInboxWatcher {
|
|
|
188
192
|
lastArrival = 0;
|
|
189
193
|
pendingHandoffs = [];
|
|
190
194
|
pendingCommit;
|
|
195
|
+
promptJoinInFlight = false;
|
|
191
196
|
seeded = false;
|
|
192
197
|
disposed = false;
|
|
193
198
|
disposalReason;
|
|
@@ -250,7 +255,7 @@ class CanvasInboxWatcher {
|
|
|
250
255
|
// every queued continuation has drained, so hasPendingMessages() must
|
|
251
256
|
// not veto the commit here. Await crtrd before changing the in-memory
|
|
252
257
|
// durable cursor; a failed commit remains replayable after process exit.
|
|
253
|
-
const result = await this.settle(nodeId, ctx, false);
|
|
258
|
+
const result = await this.settle(nodeId, ctx, false, true);
|
|
254
259
|
if (result === 'refresh_held')
|
|
255
260
|
this.dispose('refresh', false);
|
|
256
261
|
}
|
|
@@ -383,6 +388,7 @@ class CanvasInboxWatcher {
|
|
|
383
388
|
this.reportNodes.clear();
|
|
384
389
|
this.reportNodesAsked.clear();
|
|
385
390
|
this.pendingCommit = undefined;
|
|
391
|
+
this.promptJoinInFlight = false;
|
|
386
392
|
this.nodeId = undefined;
|
|
387
393
|
this.lastCtx = undefined;
|
|
388
394
|
this.lastArrival = 0;
|
|
@@ -531,6 +537,8 @@ class CanvasInboxWatcher {
|
|
|
531
537
|
this.retryPendingCommitOnTick(nodeId);
|
|
532
538
|
return;
|
|
533
539
|
}
|
|
540
|
+
if (this.promptJoinInFlight)
|
|
541
|
+
return;
|
|
534
542
|
// A paused profile is inert: read nothing, send nothing, leave the cursor
|
|
535
543
|
// where it is, so every queued entry — critical included, since aborting the
|
|
536
544
|
// live turn is exactly the punch-through pause forbids — is still there to
|
|
@@ -599,6 +607,9 @@ class CanvasInboxWatcher {
|
|
|
599
607
|
const hiddenIds = new Set(hiddenEntries.map((entry) => entry.entry_id));
|
|
600
608
|
const visibleIds = new Set(visibleEntries.map((entry) => entry.entry_id));
|
|
601
609
|
const routedEntries = entries.filter((entry) => hiddenIds.has(entry.entry_id) || visibleIds.has(entry.entry_id));
|
|
610
|
+
// Deferred-only idle mail remains in this buffer until a non-deferred inbox
|
|
611
|
+
// entry starts delivery, or a live human prompt claims it through
|
|
612
|
+
// prependHeldDeferredInbox(). Neither path wakes the node on its own.
|
|
602
613
|
const deferredHold = this.isIdle()
|
|
603
614
|
&& routedEntries.length > 0
|
|
604
615
|
&& routedEntries.every((entry) => entry.tier === 'deferred');
|
|
@@ -826,6 +837,83 @@ class CanvasInboxWatcher {
|
|
|
826
837
|
this.requeueFromBatch(batch, execution.requeueEntryIds);
|
|
827
838
|
return 'attempted';
|
|
828
839
|
}
|
|
840
|
+
/** Claim the deferred-only idle batch for the human prompt already admitted
|
|
841
|
+
* by the broker. Each rendered delivery is persisted before that prompt, but
|
|
842
|
+
* no cursor may advance until the prompt's agent_settled event proves the
|
|
843
|
+
* turn consumed it. */
|
|
844
|
+
async prependHeldDeferredInbox(session) {
|
|
845
|
+
if (!this.canMutate() || this.nodeId === undefined || profilePaused())
|
|
846
|
+
return;
|
|
847
|
+
const plan = this.ordinaryPlan();
|
|
848
|
+
if (!plan.deferredHold)
|
|
849
|
+
return;
|
|
850
|
+
const deliveries = plan.visibleEntries.length === 0
|
|
851
|
+
? []
|
|
852
|
+
: this.digest(plan.visibleEntries, 'poll');
|
|
853
|
+
const handoffCount = Number(plan.hiddenEntries.length > 0) + deliveries.length;
|
|
854
|
+
if (this.pendingHandoffs.length + handoffCount > MAX_PENDING_HANDOFFS)
|
|
855
|
+
return;
|
|
856
|
+
const batch = [...this.buffer];
|
|
857
|
+
this.promptJoinInFlight = true;
|
|
858
|
+
try {
|
|
859
|
+
if (plan.hiddenEntries.length > 0) {
|
|
860
|
+
await this.sendPromptJoinDelivery(session, 'hidden', plan.hiddenEntries, this.contextCards(this.nodeId, plan.hiddenEntries));
|
|
861
|
+
}
|
|
862
|
+
for (const delivery of deliveries) {
|
|
863
|
+
await this.sendPromptJoinDelivery(session, 'visible', delivery.entries, [...delivery.cards, delivery.text]);
|
|
864
|
+
}
|
|
865
|
+
if (!this.canMutate())
|
|
866
|
+
return;
|
|
867
|
+
this.buffer.splice(0, batch.length);
|
|
868
|
+
}
|
|
869
|
+
finally {
|
|
870
|
+
this.promptJoinInFlight = false;
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
async sendPromptJoinDelivery(session, channel, entries, messages) {
|
|
874
|
+
let facts;
|
|
875
|
+
try {
|
|
876
|
+
facts = deliveryFacts(entries, channel);
|
|
877
|
+
}
|
|
878
|
+
catch (error) {
|
|
879
|
+
this.raiseReadFatal('poll', error);
|
|
880
|
+
}
|
|
881
|
+
let operationId;
|
|
882
|
+
try {
|
|
883
|
+
await operationIdContext.fresh(async (freshOperationId) => {
|
|
884
|
+
operationId = freshOperationId;
|
|
885
|
+
emitBestEffort({
|
|
886
|
+
level: 'info',
|
|
887
|
+
event: 'inbox.delivery.started',
|
|
888
|
+
operation_id: freshOperationId,
|
|
889
|
+
fields: deliveryFields(facts),
|
|
890
|
+
});
|
|
891
|
+
for (const content of messages) {
|
|
892
|
+
await session.sendCustomMessage({
|
|
893
|
+
customType: SITUATIONAL_CONTEXT_CUSTOM_TYPE,
|
|
894
|
+
content,
|
|
895
|
+
display: true,
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
});
|
|
899
|
+
}
|
|
900
|
+
catch (error) {
|
|
901
|
+
if (operationId === undefined)
|
|
902
|
+
this.raiseReadFatal('poll', error);
|
|
903
|
+
emitDeliveryFailure(operationId, facts, 'send', error);
|
|
904
|
+
throw error;
|
|
905
|
+
}
|
|
906
|
+
if (operationId === undefined) {
|
|
907
|
+
this.raiseReadFatal('poll', new Error(`${channel} prompt-join delivery returned without an operation id`));
|
|
908
|
+
}
|
|
909
|
+
const handoff = { operationId, ...facts, settleAtAgentSettled: true };
|
|
910
|
+
if (!this.canMutate()) {
|
|
911
|
+
if (this.disposed && this.disposalReason !== undefined)
|
|
912
|
+
emitDeliveryAbandoned(handoff, this.disposalReason);
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
915
|
+
this.pendingHandoffs.push(handoff);
|
|
916
|
+
}
|
|
829
917
|
coveredEntryIds(channel) {
|
|
830
918
|
const covered = new Set();
|
|
831
919
|
for (const handoff of this.pendingHandoffs) {
|
|
@@ -883,7 +971,7 @@ class CanvasInboxWatcher {
|
|
|
883
971
|
emitDeliveryCompleted(handoff);
|
|
884
972
|
return 'settled';
|
|
885
973
|
}
|
|
886
|
-
async settle(nodeId, ctx, holdPendingMessages) {
|
|
974
|
+
async settle(nodeId, ctx, holdPendingMessages, atAgentSettled = false) {
|
|
887
975
|
if (holdPendingMessages) {
|
|
888
976
|
try {
|
|
889
977
|
if (ctx?.hasPendingMessages?.() === true)
|
|
@@ -903,9 +991,17 @@ class CanvasInboxWatcher {
|
|
|
903
991
|
for (const handoff of alreadyDurable)
|
|
904
992
|
emitDeliveryCompleted(handoff);
|
|
905
993
|
}
|
|
994
|
+
const agentSettledBlock = atAgentSettled
|
|
995
|
+
? undefined
|
|
996
|
+
: this.pendingHandoffs
|
|
997
|
+
.filter((handoff) => handoff.settleAtAgentSettled === true)
|
|
998
|
+
.map((handoff) => physical.throughIndexes.get(handoff))
|
|
999
|
+
.reduce((first, index) => first === undefined ? index : Math.min(first, index), undefined);
|
|
906
1000
|
const eligible = this.pendingHandoffs.filter((handoff) => {
|
|
907
1001
|
const throughIndex = physical.throughIndexes.get(handoff);
|
|
908
|
-
return
|
|
1002
|
+
return (handoff.settleAtAgentSettled !== true || atAgentSettled)
|
|
1003
|
+
&& (physical.blockedIndex === undefined || throughIndex < physical.blockedIndex)
|
|
1004
|
+
&& (agentSettledBlock === undefined || throughIndex < agentSettledBlock);
|
|
909
1005
|
});
|
|
910
1006
|
if (eligible.length === 0)
|
|
911
1007
|
return 'settled';
|
|
@@ -1006,6 +1102,11 @@ function installWatcher(pi, cadence) {
|
|
|
1006
1102
|
currentWatcher?.dispose('reload', false);
|
|
1007
1103
|
const watcher = new CanvasInboxWatcher(pi, cadence, ++nextGeneration);
|
|
1008
1104
|
currentWatcher = watcher;
|
|
1105
|
+
// Pi loads this extension through Jiti while the broker driver is native ESM,
|
|
1106
|
+
// so their module-level singletons differ. Register the bound watcher on the
|
|
1107
|
+
// process-global key that the broker reads immediately before a human prompt.
|
|
1108
|
+
const promptJoin = (session) => watcher.prependHeldDeferredInbox(session);
|
|
1109
|
+
globalThis[HELD_DEFERRED_INBOX_PROMPT_JOIN] = promptJoin;
|
|
1009
1110
|
try {
|
|
1010
1111
|
watcher.start();
|
|
1011
1112
|
}
|
|
@@ -7,8 +7,9 @@ interface SessionEntryLike {
|
|
|
7
7
|
}
|
|
8
8
|
interface SessionStartCtxLike {
|
|
9
9
|
sessionManager: {
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
/** Required by pi's `ReadonlySessionManager`: the active branch, which is
|
|
11
|
+
* the context the next turn actually sees. */
|
|
12
|
+
getBranch: (fromId?: string) => SessionEntryLike[];
|
|
12
13
|
};
|
|
13
14
|
}
|
|
14
15
|
interface CustomMessageLike {
|
|
@@ -25,35 +26,13 @@ interface CustomMessageLike {
|
|
|
25
26
|
kind: 'review' | 'page_feedback';
|
|
26
27
|
};
|
|
27
28
|
}
|
|
28
|
-
interface RenderedMessageLike {
|
|
29
|
-
customType: string;
|
|
30
|
-
content: string | Array<{
|
|
31
|
-
type: string;
|
|
32
|
-
text?: string;
|
|
33
|
-
}>;
|
|
34
|
-
}
|
|
35
|
-
interface ComponentLike {
|
|
36
|
-
render: (width: number) => string[];
|
|
37
|
-
invalidate: () => void;
|
|
38
|
-
}
|
|
39
|
-
interface ThemeLike {
|
|
40
|
-
fg?: (color: string, text: string) => string;
|
|
41
|
-
}
|
|
42
29
|
interface PiLike {
|
|
43
30
|
on: (event: 'session_start', handler: (event: unknown, ctx: SessionStartCtxLike) => void | Promise<void>) => void;
|
|
44
31
|
sendMessage: (message: CustomMessageLike, options?: {
|
|
45
32
|
deliverAs?: string;
|
|
46
33
|
triggerTurn?: boolean;
|
|
47
34
|
}) => void;
|
|
48
|
-
registerMessageRenderer: (customType: string, renderer: (message: RenderedMessageLike, options: {
|
|
49
|
-
expanded?: boolean;
|
|
50
|
-
}, theme: ThemeLike) => ComponentLike | undefined) => void;
|
|
51
35
|
}
|
|
52
|
-
/** Render the marker as a single compact divider; its full content remains in
|
|
53
|
-
* the message list for the model and generated-context presenters. */
|
|
54
|
-
export declare function renderReviewBoundary(message: RenderedMessageLike, options: {
|
|
55
|
-
expanded?: boolean;
|
|
56
|
-
}, theme: ThemeLike): ComponentLike;
|
|
57
36
|
/**
|
|
58
37
|
* Register the review boundary marker. Companion nodes receive one boundary
|
|
59
38
|
* marker on session_start that separates inherited context from the visible
|
|
@@ -7,30 +7,7 @@
|
|
|
7
7
|
import { envNodeId } from '../shared/env.js';
|
|
8
8
|
import { brokerExtensionState } from '../core/runtime/broker/daemon-ops.js';
|
|
9
9
|
import { isReviewBoundary, REVIEW_BOUNDARY_CUSTOM_TYPE } from '../core/runtime/session-visibility.js';
|
|
10
|
-
import { formatCard
|
|
11
|
-
import { truncateToWidth } from './truncate.js';
|
|
12
|
-
// Collapsed-by-default rendering
|
|
13
|
-
/** Render the marker as a single compact divider; its full content remains in
|
|
14
|
-
* the message list for the model and generated-context presenters. */
|
|
15
|
-
export function renderReviewBoundary(message, options, theme) {
|
|
16
|
-
const paint = (color, text) => typeof theme?.fg === 'function' ? theme.fg(color, text) : text;
|
|
17
|
-
const text = typeof message.content === 'string'
|
|
18
|
-
? message.content
|
|
19
|
-
: message.content.filter((block) => block.type === 'text' && typeof block.text === 'string').map((block) => block.text).join('\n');
|
|
20
|
-
const body = parseCard({ role: 'custom', customType: message.customType, content: text })?.body ?? text;
|
|
21
|
-
return {
|
|
22
|
-
render(width) {
|
|
23
|
-
const w = typeof width === 'number' && width > 0 ? width : 80;
|
|
24
|
-
const divider = paint('dim', truncateToWidth('[review boundary] earlier conversation is not shown', w));
|
|
25
|
-
if (options?.expanded !== true)
|
|
26
|
-
return [divider];
|
|
27
|
-
return [divider, '', ...body.split('\n').map((line) => paint('customMessageText', truncateToWidth(line, w)))];
|
|
28
|
-
},
|
|
29
|
-
invalidate() {
|
|
30
|
-
/* stateless — nothing to clear */
|
|
31
|
-
},
|
|
32
|
-
};
|
|
33
|
-
}
|
|
10
|
+
import { formatCard } from '../shared/generated-context.js';
|
|
34
11
|
// Extension
|
|
35
12
|
/**
|
|
36
13
|
* Register the review boundary marker. Companion nodes receive one boundary
|
|
@@ -38,7 +15,6 @@ export function renderReviewBoundary(message, options, theme) {
|
|
|
38
15
|
* transcript. Ordinary nodes get nothing.
|
|
39
16
|
*/
|
|
40
17
|
export function registerCanvasReviewBoundary(pi) {
|
|
41
|
-
pi.registerMessageRenderer(REVIEW_BOUNDARY_CUSTOM_TYPE, renderReviewBoundary);
|
|
42
18
|
pi.on('session_start', async (_event, ctx) => {
|
|
43
19
|
const nodeId = envNodeId();
|
|
44
20
|
if (!nodeId || nodeId.trim() === '')
|
|
@@ -54,10 +30,9 @@ export function registerCanvasReviewBoundary(pi) {
|
|
|
54
30
|
return;
|
|
55
31
|
// The active branch, not the whole session file, is the context the next
|
|
56
32
|
// turn sees. Check for an existing boundary marker on this branch.
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const alreadyMarked = scan.some((entry) => isReviewBoundary(entry, node.review_binding?.review_id));
|
|
33
|
+
const alreadyMarked = ctx.sessionManager
|
|
34
|
+
.getBranch()
|
|
35
|
+
.some((entry) => isReviewBoundary(entry, node.review_binding?.review_id));
|
|
61
36
|
if (alreadyMarked)
|
|
62
37
|
return;
|
|
63
38
|
const binding = node.review_binding;
|
|
@@ -94,12 +94,7 @@ function lastAssistantMessage(messages) {
|
|
|
94
94
|
* Pi-owned fact; crtrd compares it with the node's daemon-owned review binding. */
|
|
95
95
|
function currentReviewBoundaryIds(ctx) {
|
|
96
96
|
try {
|
|
97
|
-
const
|
|
98
|
-
const entries = typeof manager?.getBranch === 'function'
|
|
99
|
-
? manager.getBranch()
|
|
100
|
-
: typeof manager?.getEntries === 'function'
|
|
101
|
-
? manager.getEntries()
|
|
102
|
-
: [];
|
|
97
|
+
const entries = ctx?.sessionManager?.getBranch();
|
|
103
98
|
if (!Array.isArray(entries))
|
|
104
99
|
return [];
|
|
105
100
|
const ids = new Set();
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** The entry marker keeps delivery-time rendering exclusive to birth notices. */
|
|
2
|
+
export declare const BIRTH_ANNOUNCEMENT_MARKER = "crtr.birth-announcement.v1";
|
|
3
|
+
export declare const BIRTH_ANNOUNCEMENT_AGE_TOKEN = "[[CRTR_BIRTH_ANNOUNCEMENT_AGE]]";
|
|
4
|
+
export declare function birthAnnouncementBody(child: {
|
|
5
|
+
name: string;
|
|
6
|
+
nodeId: string;
|
|
7
|
+
kind: string;
|
|
8
|
+
creator: string;
|
|
9
|
+
}): string;
|
|
10
|
+
export declare function birthAnnouncementAge(createdAt: string, now?: number): string;
|
|
11
|
+
/** Return a birth notice's body rendered at delivery, or null for every other entry. */
|
|
12
|
+
export declare function renderBirthAnnouncementBody(data: Record<string, unknown> | undefined, createdAt: string, now?: number): string | null;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** The entry marker keeps delivery-time rendering exclusive to birth notices. */
|
|
2
|
+
export const BIRTH_ANNOUNCEMENT_MARKER = 'crtr.birth-announcement.v1';
|
|
3
|
+
export const BIRTH_ANNOUNCEMENT_AGE_TOKEN = '[[CRTR_BIRTH_ANNOUNCEMENT_AGE]]';
|
|
4
|
+
export function birthAnnouncementBody(child) {
|
|
5
|
+
return `${child.name} (${child.nodeId}, ${child.kind}) was created under you by ${child.creator} ${BIRTH_ANNOUNCEMENT_AGE_TOKEN} ago. For your awareness only — no response or action is needed, and no need to mention it unless asked. Treat its future reports as background you already know about; investigate only if something genuinely needs you.`;
|
|
6
|
+
}
|
|
7
|
+
export function birthAnnouncementAge(createdAt, now = Date.now()) {
|
|
8
|
+
const elapsed = Math.max(0, now - Date.parse(createdAt));
|
|
9
|
+
if (elapsed < 60_000)
|
|
10
|
+
return 'moments';
|
|
11
|
+
const minutes = Math.floor(elapsed / 60_000);
|
|
12
|
+
if (minutes < 60)
|
|
13
|
+
return `${minutes} minute${minutes === 1 ? '' : 's'}`;
|
|
14
|
+
const hours = Math.floor(elapsed / 3_600_000);
|
|
15
|
+
if (hours < 48)
|
|
16
|
+
return `${hours} hour${hours === 1 ? '' : 's'}`;
|
|
17
|
+
const days = Math.floor(elapsed / 86_400_000);
|
|
18
|
+
return `${days} day${days === 1 ? '' : 's'}`;
|
|
19
|
+
}
|
|
20
|
+
/** Return a birth notice's body rendered at delivery, or null for every other entry. */
|
|
21
|
+
export function renderBirthAnnouncementBody(data, createdAt, now = Date.now()) {
|
|
22
|
+
if (data?.['birth_announcement'] !== BIRTH_ANNOUNCEMENT_MARKER || typeof data['body'] !== 'string')
|
|
23
|
+
return null;
|
|
24
|
+
return data['body'].replace(BIRTH_ANNOUNCEMENT_AGE_TOKEN, birthAnnouncementAge(createdAt, now));
|
|
25
|
+
}
|
|
@@ -10,10 +10,14 @@ export declare const REVIEW_BOUNDARY_CUSTOM_TYPE = "crtr-review-boundary";
|
|
|
10
10
|
export declare const STALL_REPROMPT: string;
|
|
11
11
|
/** The daemon's parking mandate: the last turn of a conversation the unattended
|
|
12
12
|
* clock is concluding. Delivered live, wrapped in a `park` runtime card. It
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
13
|
+
* governs both the node's durable inheritance and reader-facing output:
|
|
14
|
+
* one update for subscribers and history, then a goodbye that does not narrate
|
|
15
|
+
* the parking.
|
|
16
|
+
*
|
|
17
|
+
* The durable inheritance shares a CONTRACT with `node yield`'s pre-invocation
|
|
18
|
+
* guide (roadmap current, short and shrinking; context dir for in-progress
|
|
19
|
+
* material; memory only for gated permanent lessons) but not a string: yield is
|
|
20
|
+
* read by an agent choosing to refresh, this by an agent being told to conclude. */
|
|
17
21
|
export declare const PARK_SUMMARY_PROMPT: string;
|
|
18
22
|
/** Static recovery prompts shared by the broker producer and display classifier. */
|
|
19
23
|
export declare const AUTH_FAULT_RECOVERY_BODY = "Provider credentials were just updated (a new login landed). Your previous turn stopped on a provider authentication failure. Continue from where you left off and retry the work that failed.";
|
|
@@ -15,15 +15,21 @@ export const STALL_REPROMPT = "You've stopped but you're not waiting on anyone a
|
|
|
15
15
|
"Pipe the result to `crtr push final` through a single-quoted heredoc if the work is done, or use `crtr human send` if you are blocked or need the user.";
|
|
16
16
|
/** The daemon's parking mandate: the last turn of a conversation the unattended
|
|
17
17
|
* clock is concluding. Delivered live, wrapped in a `park` runtime card. It
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
18
|
+
* governs both the node's durable inheritance and reader-facing output:
|
|
19
|
+
* one update for subscribers and history, then a goodbye that does not narrate
|
|
20
|
+
* the parking.
|
|
21
|
+
*
|
|
22
|
+
* The durable inheritance shares a CONTRACT with `node yield`'s pre-invocation
|
|
23
|
+
* guide (roadmap current, short and shrinking; context dir for in-progress
|
|
24
|
+
* material; memory only for gated permanent lessons) but not a string: yield is
|
|
25
|
+
* read by an agent choosing to refresh, this by an agent being told to conclude. */
|
|
26
|
+
export const PARK_SUMMARY_PROMPT = 'This conversation has been idle with nothing left to wake it, so it is being concluded. This is your last turn. Use it to leave a trustworthy inheritance, not to restart or broaden the work. Do these six things now, then stop.\n\n'
|
|
27
|
+
+ '1. Establish current truth. Check only state that may have changed outside the transcript and matters to resuming—such as the working tree, a remote run, or an external decision. Do not start new work; perform only a quick check needed to avoid recording an unverified claim. If no mandate or work ever began, say that plainly and keep every artifact minimal.\n\n'
|
|
28
|
+
+ '2. Rewrite `$CRTR_CONTEXT_DIR/roadmap.md` for a fresh context window. This is the only handoff document a later fresh cycle receives in full. Preserve the current goal and exit criteria when they still apply, then state the present outcome; what remains or is blocked; decisions or questions still open; exact recovery handles for in-flight state; and the first safe move on return. Keep strategy and present state, not a transcript recap. Delete stale and completed steps instead of marking them done; the roadmap should stay short and shrink. Write a minimal one now if none exists.\n\n'
|
|
29
|
+
+ '3. Put supporting material in your context directory only when the roadmap would become bulky without it. Rewrite existing living documents rather than leave superseded versions. Name every supporting file the next cycle must read from the roadmap and say what it is for—the revive shows filenames but does not inject their contents. Task state, identifiers, and recovery detail belong here, not in memory.\n\n'
|
|
30
|
+
+ '4. Use memory only for a non-obvious, reusable lesson that should survive this task and is not already recorded. Read `crtr memory write -h`, find before writing, and choose the narrowest scope that will reach the next agent who needs it. Do not put a conversation recap, task status, recovery handles, or facts already captured in code or docs into memory.\n\n'
|
|
31
|
+
+ '5. Push exactly one regular update with `crtr push update --tier deferred`, never `crtr push final`. Write it for subscribers and history, not as a second roadmap. Its first line must stand alone as the current outcome, blocker, or decision that matters; then include only unfinished work, a needed decision, and concrete handles a subscriber may need. This concludes the conversation; it does not finish the mandate.\n\n'
|
|
32
|
+
+ '6. End with a short sign-off to the reader in second person: say you are putting your notes in order and pausing here until they come back, and mention anything genuinely worth their attention, such as unfinished work or an open question. Keep all visible text this turn—including the update\'s first line—about their work, not crouter\'s machinery: do not mention roadmap or context paths, filing a report, parking, idling, residency, or node ids. Then stop. A later message reopens you on a fresh context window grounded in your goal and roadmap, so the inheritance you leave now is what you get back.';
|
|
27
33
|
/** Static recovery prompts shared by the broker producer and display classifier. */
|
|
28
34
|
export const AUTH_FAULT_RECOVERY_BODY = 'Provider credentials were just updated (a new login landed). Your previous turn stopped on a provider authentication failure. Continue from where you left off and retry the work that failed.';
|
|
29
35
|
export const CONNECTION_FAULT_RECOVERY_BODY = 'The network connection is back online. Your previous turn stopped on a connection error (the network was down). Continue from where you left off and retry the work that failed.';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/crouter",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.251",
|
|
4
4
|
"description": "crtr — agent runtime with memory, plugins, and marketplaces",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -83,10 +83,10 @@
|
|
|
83
83
|
"dev": "tsx --conditions=crtr-src src/cli.ts",
|
|
84
84
|
"link": "npm link",
|
|
85
85
|
"prepublishOnly": "npm run build",
|
|
86
|
-
"test": "CRTR_NO_DAEMON_AUTOSTART=1 node --conditions=crtr-src --import tsx/esm --test --test-force-exit --test-concurrency=$(node -p 'Math.max(4, require(`node:os`).availableParallelism() - 1)') --test-timeout=10000 'src/**/__tests__/*.test.ts'",
|
|
87
|
-
"test:integration": "CRTR_NO_DAEMON_AUTOSTART=1 node --conditions=crtr-src --import tsx/esm --test --test-concurrency=$(node -p 'Math.min(8, Math.max(2, require(`node:os`).availableParallelism() - 1))') --test-timeout=60000 'src/**/__tests__/integration/*.test.ts'",
|
|
86
|
+
"test": "CRTR_NO_DAEMON_AUTOSTART=1 node --conditions=crtr-src --import tsx/esm --import ./scripts/test-env-scrub.mjs --test --test-force-exit --test-concurrency=$(node -p 'Math.max(4, require(`node:os`).availableParallelism() - 1)') --test-timeout=10000 'src/**/__tests__/*.test.ts'",
|
|
87
|
+
"test:integration": "CRTR_NO_DAEMON_AUTOSTART=1 node --conditions=crtr-src --import tsx/esm --import ./scripts/test-env-scrub.mjs --test --test-concurrency=$(node -p 'Math.min(8, Math.max(2, require(`node:os`).availableParallelism() - 1))') --test-timeout=60000 'src/**/__tests__/integration/*.test.ts'",
|
|
88
88
|
"test:seam": "npm run build && npm run test:seam:built",
|
|
89
|
-
"test:seam:built": "CRTR_NO_DAEMON_AUTOSTART=1 node --test --test-concurrency=4 --test-timeout=180000 'dist/**/__tests__/seam/*.test.js'",
|
|
89
|
+
"test:seam:built": "CRTR_NO_DAEMON_AUTOSTART=1 node --import ./scripts/test-env-scrub.mjs --test --test-concurrency=4 --test-timeout=180000 'dist/**/__tests__/seam/*.test.js'",
|
|
90
90
|
"test:all": "npm test && npm run test:integration && npm run test:seam"
|
|
91
91
|
},
|
|
92
92
|
"overrides": {
|
package/runtime.lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/crouter",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.251",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@north-light/crouter",
|
|
9
|
-
"version": "0.3.
|
|
9
|
+
"version": "0.3.251",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"dependencies": {
|