@north-light/crouter 0.3.263 → 0.3.270
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/dist/api/client.d.ts +5 -1
- package/dist/api/client.js +6 -0
- package/dist/api/dto/broker.d.ts +11 -2
- package/dist/api/dto/broker.js +2 -2
- package/dist/api/dto/canvas.d.ts +53 -2
- package/dist/api/dto/nodes.d.ts +10 -2
- package/dist/api/routes.d.ts +1 -0
- package/dist/api/routes.js +1 -0
- package/dist/builtin-memory/05-kinds/advisor/advice-contract.md +2 -2
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/integration/provider-rotation.test.ts +50 -0
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +46 -6
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +6 -2
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +7 -2
- package/dist/clients/attach/session/whip-achievements.d.ts +4 -0
- package/dist/clients/attach/session/whip-achievements.js +145 -0
- package/dist/clients/attach/slash/dispatch.js +5 -3
- package/dist/clients/attach/viewer.js +607 -606
- package/dist/commands/canvas-history/grep.js +3 -22
- package/dist/commands/canvas-history/read.js +5 -3
- package/dist/commands/canvas-history/search.js +3 -22
- package/dist/commands/canvas-history/shared.d.ts +8 -0
- package/dist/commands/canvas-history/shared.js +64 -1
- package/dist/commands/canvas-history/stats.d.ts +1 -0
- package/dist/commands/canvas-history/stats.js +68 -0
- package/dist/commands/canvas-history.js +4 -3
- package/dist/commands/node/create.js +2 -2
- package/dist/commands/pkg/plugin-inspect.js +2 -2
- package/dist/commands/pkg/plugin-manage.js +3 -3
- package/dist/commands/sys/config.js +19 -2
- package/dist/commands/sys/doctor.js +4 -3
- package/dist/commands/sys/panels/models-panel.js +3 -1
- package/dist/core/__tests__/human-deliver.test.js +1 -1
- package/dist/core/__tests__/integration/lifecycle-hooks.test.d.ts +1 -0
- package/dist/core/__tests__/integration/lifecycle-hooks.test.js +182 -0
- package/dist/core/__tests__/integration/revive.test.js +19 -17
- package/dist/core/__tests__/model-pin-durability.test.js +25 -1
- package/dist/core/__tests__/push-final-guard.test.js +1 -1
- package/dist/core/__tests__/relaunch-root.test.js +17 -0
- package/dist/core/__tests__/revive-capacity.test.js +20 -20
- package/dist/core/__tests__/revive-parked-fresh.test.js +14 -14
- package/dist/core/__tests__/seam/broker-cap-freeze.test.js +2 -2
- package/dist/core/__tests__/seam/broker-provider-retry.test.js +19 -4
- package/dist/core/__tests__/seam/dormancy-release.test.js +7 -7
- package/dist/core/__tests__/seam/held-deferred-human-prompt.test.js +1 -1
- package/dist/core/__tests__/seam/memory-slash-node-relative-inventory.test.js +19 -2
- package/dist/core/__tests__/seam/yield-refresh-transaction.test.js +2 -2
- package/dist/core/broker-client/__tests__/transport-relay.test.js +3 -1
- package/dist/core/broker-client/transport-relay.js +5 -1
- package/dist/core/canvas/__tests__/history-transcript.test.d.ts +1 -0
- package/dist/core/canvas/__tests__/history-transcript.test.js +102 -0
- package/dist/core/canvas/browse/app.d.ts +1 -1
- package/dist/core/canvas/browse/app.js +13 -10
- package/dist/core/canvas/browse/model.js +16 -1
- package/dist/core/canvas/browse/render.d.ts +23 -3
- package/dist/core/canvas/browse/render.js +429 -64
- package/dist/core/canvas/canvas.js +12 -8
- package/dist/core/canvas/extensions.d.ts +1 -1
- package/dist/core/canvas/extensions.js +7 -1
- package/dist/core/canvas/history.d.ts +36 -1
- package/dist/core/canvas/history.js +328 -7
- package/dist/core/canvas/labels.d.ts +2 -2
- package/dist/core/canvas/migrations.js +32 -0
- package/dist/core/canvas/node-recap.d.ts +23 -0
- package/dist/core/canvas/node-recap.js +67 -0
- package/dist/core/canvas/render-source.d.ts +33 -1
- package/dist/core/canvas/render-source.js +80 -22
- package/dist/core/canvas/types.d.ts +9 -0
- package/dist/core/command-hooks/discovery.d.ts +27 -1
- package/dist/core/command-hooks/discovery.js +52 -0
- package/dist/core/command-hooks/index.d.ts +4 -3
- package/dist/core/command-hooks/index.js +2 -1
- package/dist/core/command-hooks/lifecycle-catalog.d.ts +3 -0
- package/dist/core/command-hooks/lifecycle-catalog.js +4 -0
- package/dist/core/command-hooks/report.d.ts +11 -2
- package/dist/core/command-hooks/report.js +10 -1
- package/dist/core/command-hooks/schema.d.ts +16 -1
- package/dist/core/command-hooks/schema.js +128 -40
- package/dist/core/command-hooks/transport/exec-lifecycle.d.ts +12 -0
- package/dist/core/command-hooks/transport/exec-lifecycle.js +152 -0
- package/dist/core/config.js +13 -5
- package/dist/core/human/feedback-companion.js +1 -1
- package/dist/core/keybindings/catalog.d.ts +2 -2
- package/dist/core/keybindings/catalog.js +2 -1
- package/dist/core/model-routes.js +1 -1
- package/dist/core/review/realize.js +1 -1
- package/dist/core/runtime/broker/auth-reload.js +4 -1
- package/dist/core/runtime/broker/engine-drive.d.ts +14 -3
- package/dist/core/runtime/broker/engine-drive.js +29 -1
- package/dist/core/runtime/broker/fault-retry.js +7 -2
- package/dist/core/runtime/broker/frame-client.js +3 -0
- package/dist/core/runtime/broker/frame-dispatch.js +7 -0
- package/dist/core/runtime/broker/inbox.d.ts +12 -2
- package/dist/core/runtime/broker/inbox.js +68 -38
- package/dist/core/runtime/broker/rebind.js +7 -0
- package/dist/core/runtime/broker-protocol.d.ts +16 -1
- package/dist/core/runtime/broker.d.ts +1 -1
- package/dist/core/runtime/broker.js +27 -3
- package/dist/core/runtime/canvas-extensions.d.ts +1 -0
- package/dist/core/runtime/canvas-extensions.js +2 -0
- package/dist/core/runtime/interactive-deliver.d.ts +8 -2
- package/dist/core/runtime/interactive-deliver.js +12 -3
- package/dist/core/runtime/launch-target.js +1 -1
- package/dist/core/runtime/launch.d.ts +12 -6
- package/dist/core/runtime/launch.js +19 -17
- package/dist/core/runtime/model-swap.d.ts +5 -4
- package/dist/core/runtime/model-swap.js +7 -5
- package/dist/core/runtime/node-read.d.ts +2 -0
- package/dist/core/runtime/node-read.js +18 -9
- package/dist/core/runtime/promote.js +2 -1
- package/dist/core/runtime/reset.js +4 -2
- package/dist/core/runtime/revive-all.d.ts +1 -1
- package/dist/core/runtime/revive-all.js +2 -2
- package/dist/core/runtime/revive.d.ts +2 -2
- package/dist/core/runtime/revive.js +71 -13
- package/dist/core/runtime/session-visibility.d.ts +10 -14
- package/dist/core/runtime/session-visibility.js +43 -30
- package/dist/core/runtime/spawn.d.ts +3 -3
- package/dist/core/runtime/spawn.js +20 -3
- package/dist/core/runtime/stamp/channel.d.ts +22 -0
- package/dist/core/runtime/stamp/channel.js +83 -0
- package/dist/core/runtime/stamp/protocol.d.ts +90 -0
- package/dist/core/runtime/stamp/protocol.js +113 -0
- package/dist/core/runtime/tmux-driver.d.ts +11 -1
- package/dist/core/runtime/tmux-driver.js +27 -11
- package/dist/core/runtime/turn-visibility.d.ts +10 -0
- package/dist/core/runtime/turn-visibility.js +31 -0
- package/dist/core/self-update.js +2 -5
- package/dist/core/tui/draw.d.ts +1 -0
- package/dist/core/tui/draw.js +5 -1
- package/dist/core/tui/markdown.d.ts +17 -0
- package/dist/core/tui/markdown.js +407 -0
- package/dist/core/user-settings.d.ts +4 -0
- package/dist/core/user-settings.js +1 -0
- package/dist/daemon/api/__tests__/node-model-validation.test.js +21 -0
- package/dist/daemon/api/__tests__/profile-launch-gates.test.js +2 -2
- package/dist/daemon/api/bridge.d.ts +3 -2
- package/dist/daemon/api/bridge.js +34 -9
- package/dist/daemon/api/handlers/attach.js +4 -4
- package/dist/daemon/api/handlers/bash-jobs.js +1 -1
- package/dist/daemon/api/handlers/canvas.js +62 -16
- package/dist/daemon/api/handlers/messages.js +14 -3
- package/dist/daemon/api/handlers/nodes.js +17 -7
- package/dist/daemon/api/map.js +2 -0
- package/dist/daemon/cron/sinks.js +3 -3
- package/dist/daemon/fleet.js +1 -1
- package/dist/daemon/messaging/node-message.js +16 -2
- package/dist/daemon/profile-delete.js +1 -1
- package/dist/daemon/reconcilers/broker-supervision.js +26 -9
- package/dist/daemon/reconcilers/live-obligation.d.ts +8 -1
- package/dist/daemon/reconcilers/live-obligation.js +17 -0
- package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +6 -6
- package/dist/hook-authoring.d.ts +31 -1
- package/dist/hook-authoring.js +42 -5
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -0
- package/dist/pi-extensions/canvas-inbox-watcher.js +27 -2
- package/dist/pi-extensions/canvas-recap.js +4 -0
- package/dist/pi-extensions/canvas-stamp.d.ts +14 -0
- package/dist/pi-extensions/canvas-stamp.js +77 -0
- package/dist/pi-extensions/canvas-stophook.js +3 -0
- package/dist/shared/__tests__/env-boundary.test.js +4 -2
- package/dist/shared/crtr-version.d.ts +2 -0
- package/dist/shared/crtr-version.js +18 -0
- package/dist/shared/env.d.ts +10 -0
- package/dist/shared/env.js +19 -4
- package/dist/shared/generated-context.d.ts +1 -2
- package/dist/shared/generated-context.js +3 -4
- package/dist/types.d.ts +10 -0
- package/dist/types.js +4 -0
- package/package.json +1 -1
- package/runtime.lock.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export type { CloseNodeResult } from './core/runtime/close.js';
|
|
|
5
5
|
export { appendInbox } from './core/feed/inbox.js';
|
|
6
6
|
export type { InboxEntry, InboxTier, InboxKind } from './core/feed/inbox.js';
|
|
7
7
|
export { appendSituationalContext } from './core/runtime/situational-context.js';
|
|
8
|
+
export { isTurnInternal } from './core/runtime/turn-visibility.js';
|
|
8
9
|
export { getNode, listNodes } from './core/canvas/canvas.js';
|
|
9
10
|
export { nodeDir } from './core/canvas/paths.js';
|
|
10
11
|
export type { NodeMeta, NodeRow, NodeStatus, Lifecycle, Mode, } from './core/canvas/types.js';
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,9 @@ export { appendInbox } from './core/feed/inbox.js';
|
|
|
21
21
|
// upsert-only, narrow surface so an external composer (e.g. Hearth) never needs to
|
|
22
22
|
// learn the storage file itself.
|
|
23
23
|
export { appendSituationalContext } from './core/runtime/situational-context.js';
|
|
24
|
+
// In-process extensions use crouter's admitted turn classification rather than
|
|
25
|
+
// attempting to reconstruct origin and route state independently.
|
|
26
|
+
export { isTurnInternal } from './core/runtime/turn-visibility.js';
|
|
24
27
|
// ── Canvas reads ─────────────────────────────────────────────────────────
|
|
25
28
|
export { getNode, listNodes } from './core/canvas/canvas.js';
|
|
26
29
|
export { nodeDir } from './core/canvas/paths.js';
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
import { envNodeId, envProfileId, envWatcherDebounceMs, envWatcherTickMs } from '../shared/env.js';
|
|
18
18
|
import { coalesceBrokerInbox, entryCards, readBrokerCanceledEntryIds, readBrokerCursor, readBrokerInboxSince, reportRefNodeId, } from '../core/runtime/broker/inbox.js';
|
|
19
19
|
import { advanceBrokerInboxCursor, brokerExtensionState } from '../core/runtime/broker/daemon-ops.js';
|
|
20
|
+
import { announceMessageOrigin } from '../core/runtime/stamp/channel.js';
|
|
20
21
|
import { captureGoalIfAbsent, generateAndCommitInitialName } from './broker-local.js';
|
|
21
22
|
import { emitEvent } from '../core/events/emit.js';
|
|
22
23
|
import { errorClassFromError } from '../core/events/errors.js';
|
|
@@ -97,6 +98,26 @@ function emitStoredBestEffort(operationId, input) {
|
|
|
97
98
|
// Delivery state remains authoritative when its observational record cannot append.
|
|
98
99
|
}
|
|
99
100
|
}
|
|
101
|
+
/** The authorship stamped onto a delivered message. A person's words arrive
|
|
102
|
+
* verbatim and are stamped as theirs — no channel, because the durable inbox
|
|
103
|
+
* is not a transport a reader can attribute. Every other delivery is the
|
|
104
|
+
* runtime's inbox card, carrying each entry's attribution as the coalescer
|
|
105
|
+
* received it. */
|
|
106
|
+
function deliveryOrigin(delivery) {
|
|
107
|
+
if (delivery.kind === 'human')
|
|
108
|
+
return { type: 'human' };
|
|
109
|
+
return {
|
|
110
|
+
type: 'runtime',
|
|
111
|
+
kind: 'inbox',
|
|
112
|
+
entries: delivery.entries.map((entry) => ({
|
|
113
|
+
...(entry.from === null ? {} : { from: entry.from }),
|
|
114
|
+
...(entry.from_name === undefined ? {} : { fromName: entry.from_name }),
|
|
115
|
+
kind: entry.kind,
|
|
116
|
+
tier: entry.tier,
|
|
117
|
+
...(entry.ref === undefined ? {} : { ref: entry.ref }),
|
|
118
|
+
})),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
100
121
|
function deliveryFacts(entries, channel) {
|
|
101
122
|
if (entries.length === 0)
|
|
102
123
|
throw new Error(`cannot create an empty ${channel} delivery`);
|
|
@@ -729,7 +750,7 @@ class CanvasInboxWatcher {
|
|
|
729
750
|
* unit: a throw anywhere requeues the whole entry, so a retry may repeat a
|
|
730
751
|
* card already handed off (context is restatable; the human's body is not,
|
|
731
752
|
* and it is sent last). */
|
|
732
|
-
sendVisible(entries, cards, content, route, stage) {
|
|
753
|
+
sendVisible(entries, cards, content, origin, route, stage) {
|
|
733
754
|
return this.sendDelivery('visible', entries, () => {
|
|
734
755
|
if (route !== 'idle' && route !== 'steer' && route !== 'followUp') {
|
|
735
756
|
throw new Error(`visible send requested for non-send route ${route}`);
|
|
@@ -737,6 +758,10 @@ class CanvasInboxWatcher {
|
|
|
737
758
|
for (const card of cards) {
|
|
738
759
|
this.pi.sendMessage({ customType: SITUATIONAL_CONTEXT_CUSTOM_TYPE, content: card, display: true }, route === 'idle' ? {} : { deliverAs: route });
|
|
739
760
|
}
|
|
761
|
+
// Claimed by the stamp writer against the message pi persists, so a send
|
|
762
|
+
// that throws here leaves an announcement nobody claims rather than one
|
|
763
|
+
// that mislabels a later message.
|
|
764
|
+
announceMessageOrigin(content, origin);
|
|
740
765
|
if (route === 'idle') {
|
|
741
766
|
const start = brokerIdleTurnStart();
|
|
742
767
|
if (start === undefined)
|
|
@@ -803,7 +828,7 @@ class CanvasInboxWatcher {
|
|
|
803
828
|
if (!this.canMutate())
|
|
804
829
|
return { inactive: true, requeueEntryIds };
|
|
805
830
|
if (delivery !== undefined) {
|
|
806
|
-
const result = this.sendVisible(delivery.entries, delivery.cards, delivery.text, route, stage);
|
|
831
|
+
const result = this.sendVisible(delivery.entries, delivery.cards, delivery.text, deliveryOrigin(delivery), route, stage);
|
|
807
832
|
if (result === 'inactive')
|
|
808
833
|
return { inactive: true, requeueEntryIds };
|
|
809
834
|
if (result === 'failed') {
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
import { envNodeId, envRecapIdleMs } from '../shared/env.js';
|
|
42
42
|
import { brokerExtensionState } from '../core/runtime/broker/daemon-ops.js';
|
|
43
43
|
import { generateRecap } from '../core/runtime/recap.js';
|
|
44
|
+
import { writeNodeRecap } from '../core/canvas/node-recap.js';
|
|
44
45
|
import { isReviewBoundary } from '../core/runtime/session-visibility.js';
|
|
45
46
|
import { editorLabelForBrokerNode } from '../core/runtime/broker-extension-render.js';
|
|
46
47
|
import { generateAndCommitRecapName, readRoadmap } from './broker-local.js';
|
|
@@ -315,6 +316,9 @@ export function registerCanvasRecap(pi, recapGenerator = generateRecap, nameRege
|
|
|
315
316
|
// Automatic work paints only while its idle snapshot remains current.
|
|
316
317
|
if ((!manual && activityVersion !== snapshotVersion) || !canPaint())
|
|
317
318
|
return;
|
|
319
|
+
// Persist what we are about to paint. The widget dies with the broker; the
|
|
320
|
+
// file is what `crtr canvas browse` shows for a dormant node.
|
|
321
|
+
writeNodeRecap(nodeId, briefing);
|
|
318
322
|
recapShown = true;
|
|
319
323
|
recapStale = activityVersion !== snapshotVersion;
|
|
320
324
|
showRecap(briefing);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
interface StampCtxLike {
|
|
2
|
+
sessionManager: {
|
|
3
|
+
/** Whole file, not the active branch: a session stamp on an abandoned
|
|
4
|
+
* branch still describes this file's owner. */
|
|
5
|
+
getEntries: () => unknown[];
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
interface PiLike {
|
|
9
|
+
on: (event: 'session_start' | 'message_end', handler: (event: unknown, ctx: StampCtxLike) => void | Promise<void>) => void;
|
|
10
|
+
appendEntry: (customType: string, data?: unknown) => void;
|
|
11
|
+
}
|
|
12
|
+
/** Register the stamp writer. */
|
|
13
|
+
export declare function registerCanvasStamp(pi: PiLike): void;
|
|
14
|
+
export default registerCanvasStamp;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// canvas-stamp.ts — the sole writer of `crtr-stamp` entries.
|
|
2
|
+
//
|
|
3
|
+
// Two records land in the session jsonl. A SESSION stamp names the node and the
|
|
4
|
+
// crtr release that own the file, appended at session start only when the
|
|
5
|
+
// latest one on disk disagrees — which covers a fresh session, a fork (whose
|
|
6
|
+
// copied history still carries the source node's stamp), a new cycle, and a
|
|
7
|
+
// runtime upgrade mid-node, without a rule per case. A MESSAGE stamp records
|
|
8
|
+
// where one user message came from, joined to it by the message's own
|
|
9
|
+
// timestamp.
|
|
10
|
+
//
|
|
11
|
+
// Every user message is stamped; an origin appears only when a delivery seam
|
|
12
|
+
// announced one (absence means unknown, never a guess). An assistant message is
|
|
13
|
+
// stamped only when an `ext` contribution is pending, so an ordinary turn costs
|
|
14
|
+
// no line.
|
|
15
|
+
//
|
|
16
|
+
// Ordering note: pi runs message_end handlers BEFORE it persists the finalized
|
|
17
|
+
// message, so the stamp is written as that message's structural parent. The
|
|
18
|
+
// join is by `msgTs`, so tree position carries no meaning here.
|
|
19
|
+
import { envNodeId } from '../shared/env.js';
|
|
20
|
+
import { crtrVersion } from '../shared/crtr-version.js';
|
|
21
|
+
import { claimMessageAnnouncement, drainStampExt, hasPendingStampExt, } from '../core/runtime/stamp/channel.js';
|
|
22
|
+
import { CRTR_STAMP_CUSTOM_TYPE, latestSessionStamp, STAMP_SCHEMA_VERSION, } from '../core/runtime/stamp/protocol.js';
|
|
23
|
+
/** Plain text of a message's content (string or content blocks). */
|
|
24
|
+
function messageText(content) {
|
|
25
|
+
if (typeof content === 'string')
|
|
26
|
+
return content;
|
|
27
|
+
if (Array.isArray(content)) {
|
|
28
|
+
return content
|
|
29
|
+
.filter((block) => typeof block === 'object' && block !== null && typeof block.text === 'string')
|
|
30
|
+
.map((block) => block.text)
|
|
31
|
+
.join('\n');
|
|
32
|
+
}
|
|
33
|
+
return '';
|
|
34
|
+
}
|
|
35
|
+
function envelope(kind, payload) {
|
|
36
|
+
const ext = drainStampExt();
|
|
37
|
+
return {
|
|
38
|
+
v: STAMP_SCHEMA_VERSION,
|
|
39
|
+
kind,
|
|
40
|
+
payload,
|
|
41
|
+
...(ext === undefined ? {} : { ext }),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** Register the stamp writer. */
|
|
45
|
+
export function registerCanvasStamp(pi) {
|
|
46
|
+
pi.on('session_start', (_event, ctx) => {
|
|
47
|
+
const node = envNodeId();
|
|
48
|
+
if (node === undefined || node.trim() === '')
|
|
49
|
+
return;
|
|
50
|
+
const crtr = crtrVersion();
|
|
51
|
+
const latest = latestSessionStamp(ctx.sessionManager.getEntries());
|
|
52
|
+
if (latest !== null && latest.payload.node === node && latest.payload.crtr === crtr)
|
|
53
|
+
return;
|
|
54
|
+
pi.appendEntry(CRTR_STAMP_CUSTOM_TYPE, envelope('session', { node, crtr }));
|
|
55
|
+
});
|
|
56
|
+
pi.on('message_end', (event) => {
|
|
57
|
+
const message = event?.message;
|
|
58
|
+
if (message === undefined || message === null)
|
|
59
|
+
return;
|
|
60
|
+
if (typeof message.timestamp !== 'number')
|
|
61
|
+
return;
|
|
62
|
+
const msgTs = message.timestamp;
|
|
63
|
+
if (message.role === 'user') {
|
|
64
|
+
const announcement = claimMessageAnnouncement(messageText(message.content));
|
|
65
|
+
pi.appendEntry(CRTR_STAMP_CUSTOM_TYPE, envelope('message', {
|
|
66
|
+
msgTs,
|
|
67
|
+
...(announcement === null ? {} : announcement),
|
|
68
|
+
}));
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
// Any other message earns a line only for a pending contribution.
|
|
72
|
+
if (!hasPendingStampExt())
|
|
73
|
+
return;
|
|
74
|
+
pi.appendEntry(CRTR_STAMP_CUSTOM_TYPE, envelope('message', { msgTs }));
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
export default registerCanvasStamp;
|
|
@@ -33,6 +33,7 @@ import { activeBackgroundBashJobs } from '../core/bash-jobs.js';
|
|
|
33
33
|
import { CONTEXT_NUDGE_CUSTOM_TYPE, formatCard } from '../shared/generated-context.js';
|
|
34
34
|
import { isReviewBoundary } from '../core/runtime/session-visibility.js';
|
|
35
35
|
import { runRefreshAbort } from '../core/runtime/broker/extension-abort.js';
|
|
36
|
+
import { announceMessageOrigin } from '../core/runtime/stamp/channel.js';
|
|
36
37
|
/** Condense a tool call into one short line for the activity cue. Picks the most
|
|
37
38
|
* identifying arg (command / path / pattern / description), collapses whitespace,
|
|
38
39
|
* and hard-caps the length so a giant bash command can't bloat telemetry.json. */
|
|
@@ -271,6 +272,7 @@ export function registerCanvasStophook(pi) {
|
|
|
271
272
|
};
|
|
272
273
|
const enactSettleDirective = (directive, ctx) => {
|
|
273
274
|
if (directive.action === 'reprompt') {
|
|
275
|
+
announceMessageOrigin(directive.prompt, { type: 'runtime', kind: 'stop-guard' });
|
|
274
276
|
pi.sendUserMessage(directive.prompt, { deliverAs: 'followUp' });
|
|
275
277
|
return;
|
|
276
278
|
}
|
|
@@ -566,6 +568,7 @@ export function registerCanvasStophook(pi) {
|
|
|
566
568
|
firedBands.add(at);
|
|
567
569
|
const note = formatCard('context-nudge', {}, steerNote(at, node.lifecycle, node.mode));
|
|
568
570
|
if (runContinues) {
|
|
571
|
+
announceMessageOrigin(note, { type: 'runtime', kind: 'context-nudge' });
|
|
569
572
|
pi.sendUserMessage(note, { deliverAs: 'steer' });
|
|
570
573
|
}
|
|
571
574
|
else {
|
|
@@ -17,6 +17,7 @@ const MIGRATED_VARS = [
|
|
|
17
17
|
'CRTR_PROFILE_ID',
|
|
18
18
|
'CRTR_NODE_CWD',
|
|
19
19
|
'CRTR_MODEL_INTENT',
|
|
20
|
+
'CRTR_MODEL_EXACT',
|
|
20
21
|
'CRTR_HOME',
|
|
21
22
|
'CRTR_STREAM_WATCHDOG_MS',
|
|
22
23
|
'CRTR_WATCHER_TICK_MS',
|
|
@@ -35,6 +36,7 @@ const MIGRATED_VARS = [
|
|
|
35
36
|
'CRTR_WARN_LIVE_BROKERS',
|
|
36
37
|
'CRTR_TEST_UNATTENDED_PARK_MS',
|
|
37
38
|
'CRTR_TEST_PARK_SUMMARY_GRACE_MS',
|
|
39
|
+
'CRTR_UNATTENDED_PARK_MS',
|
|
38
40
|
];
|
|
39
41
|
// Matches `process.env['VAR']`, `process.env.VAR`, or `process.env[\`VAR\`]`
|
|
40
42
|
// for exactly one of MIGRATED_VARS — not a longer name that merely starts
|
|
@@ -53,8 +55,8 @@ const EXEMPTIONS = new Map([
|
|
|
53
55
|
],
|
|
54
56
|
[
|
|
55
57
|
'core/runtime/broker.ts',
|
|
56
|
-
"clears CRTR_MODEL_INTENT via
|
|
57
|
-
'not
|
|
58
|
+
"clears CRTR_MODEL_INTENT and sets CRTR_MODEL_EXACT via process.env — mutations, " +
|
|
59
|
+
'not reads, so they stay here per shared/env.ts\'s header comment.',
|
|
58
60
|
],
|
|
59
61
|
]);
|
|
60
62
|
function tsFilesUnder(dir) {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// The installed crtr release string, read once per process from the package
|
|
2
|
+
// manifest. Callers that record a version into durable state (the session
|
|
3
|
+
// stamp) and callers that compare against npm (self-update) read this one
|
|
4
|
+
// implementation.
|
|
5
|
+
import { readFileSync } from 'node:fs';
|
|
6
|
+
import { dirname, join } from 'node:path';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
let cached = null;
|
|
9
|
+
/** The running crouter package version (`package.json` `version`). */
|
|
10
|
+
export function crtrVersion() {
|
|
11
|
+
if (cached !== null)
|
|
12
|
+
return cached;
|
|
13
|
+
// src|dist/shared/crtr-version.ts → up to src|dist/ → up to pkg root
|
|
14
|
+
const root = join(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
15
|
+
const parsed = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'));
|
|
16
|
+
cached = parsed.version;
|
|
17
|
+
return cached;
|
|
18
|
+
}
|
package/dist/shared/env.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ export declare function envNodeCwd(): string | undefined;
|
|
|
8
8
|
* undefined. Cleared (not read) via `delete process.env['CRTR_MODEL_INTENT']`
|
|
9
9
|
* in `runtime/broker.ts` — a mutation, not a read, so it stays there. */
|
|
10
10
|
export declare function envModelIntent(): string | undefined;
|
|
11
|
+
/** Whether the launch recipe requires this broker to keep its exact provider/model. */
|
|
12
|
+
export declare function envModelExact(): boolean;
|
|
11
13
|
/** The raw `CRTR_HOME` override, unresolved. Use `crtrHome()`
|
|
12
14
|
* (`core/canvas/paths.ts`) to get the resolved canvas-home path; use this
|
|
13
15
|
* only when a caller needs the override itself — to check whether one is
|
|
@@ -67,6 +69,14 @@ export declare function envWarnLiveBrokers(): number | undefined;
|
|
|
67
69
|
/** Test-only unattended-park interval override (`CRTR_TEST_UNATTENDED_PARK_MS`),
|
|
68
70
|
* gated by `envNoDaemonAutostart()` at the call site; no production default. */
|
|
69
71
|
export declare function envTestUnattendedParkMs(): number | undefined;
|
|
72
|
+
/** Production unattended-park interval override (`CRTR_UNATTENDED_PARK_MS`), in
|
|
73
|
+
* milliseconds; wins over the user-scope config value
|
|
74
|
+
* (`readConfig('user').lifecycle.unattendedParkMs`, itself defaulting to 15
|
|
75
|
+
* min) whenever set, autostart or not. Unlike `CRTR_TEST_UNATTENDED_PARK_MS`
|
|
76
|
+
* this is not gated on `envNoDaemonAutostart()`: it is the env escape hatch
|
|
77
|
+
* above the configured knob a deployment sets to tune how long an eligible
|
|
78
|
+
* resident idles before its parking turn. */
|
|
79
|
+
export declare function envUnattendedParkMs(): number | undefined;
|
|
70
80
|
/** Test-only park-summary grace-period override
|
|
71
81
|
* (`CRTR_TEST_PARK_SUMMARY_GRACE_MS`); no production default. */
|
|
72
82
|
export declare function envTestParkSummaryGraceMs(): number | undefined;
|
package/dist/shared/env.js
CHANGED
|
@@ -40,6 +40,10 @@ export function envNodeCwd() {
|
|
|
40
40
|
export function envModelIntent() {
|
|
41
41
|
return process.env['CRTR_MODEL_INTENT'];
|
|
42
42
|
}
|
|
43
|
+
/** Whether the launch recipe requires this broker to keep its exact provider/model. */
|
|
44
|
+
export function envModelExact() {
|
|
45
|
+
return process.env['CRTR_MODEL_EXACT'] === '1';
|
|
46
|
+
}
|
|
43
47
|
/** The raw `CRTR_HOME` override, unresolved. Use `crtrHome()`
|
|
44
48
|
* (`core/canvas/paths.ts`) to get the resolved canvas-home path; use this
|
|
45
49
|
* only when a caller needs the override itself — to check whether one is
|
|
@@ -140,10 +144,11 @@ export function envDebug() {
|
|
|
140
144
|
return process.env['CRTR_DEBUG'] === '1';
|
|
141
145
|
}
|
|
142
146
|
// Precedence — broker-supervision.ts's threshold/interval overrides. Each
|
|
143
|
-
// returns the raw parsed value with NO default: the caller
|
|
144
|
-
//
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
+
// returns the raw parsed value with NO default: the caller supplies the
|
|
148
|
+
// default (user config for the broker thresholds and the unattended-park
|
|
149
|
+
// interval, a hardcoded constant for the test-only park-summary grace), and
|
|
150
|
+
// env wins over it when set. Undefined, unparseable, or non-positive all mean
|
|
151
|
+
// "no override" — the caller's own default applies.
|
|
147
152
|
function parsePositiveInteger(raw) {
|
|
148
153
|
if (raw === undefined || raw === '')
|
|
149
154
|
return undefined;
|
|
@@ -171,6 +176,16 @@ function parsePositiveMsNoDefault(raw) {
|
|
|
171
176
|
export function envTestUnattendedParkMs() {
|
|
172
177
|
return parsePositiveMsNoDefault(process.env['CRTR_TEST_UNATTENDED_PARK_MS']);
|
|
173
178
|
}
|
|
179
|
+
/** Production unattended-park interval override (`CRTR_UNATTENDED_PARK_MS`), in
|
|
180
|
+
* milliseconds; wins over the user-scope config value
|
|
181
|
+
* (`readConfig('user').lifecycle.unattendedParkMs`, itself defaulting to 15
|
|
182
|
+
* min) whenever set, autostart or not. Unlike `CRTR_TEST_UNATTENDED_PARK_MS`
|
|
183
|
+
* this is not gated on `envNoDaemonAutostart()`: it is the env escape hatch
|
|
184
|
+
* above the configured knob a deployment sets to tune how long an eligible
|
|
185
|
+
* resident idles before its parking turn. */
|
|
186
|
+
export function envUnattendedParkMs() {
|
|
187
|
+
return parsePositiveMsNoDefault(process.env['CRTR_UNATTENDED_PARK_MS']);
|
|
188
|
+
}
|
|
174
189
|
/** Test-only park-summary grace-period override
|
|
175
190
|
* (`CRTR_TEST_PARK_SUMMARY_GRACE_MS`); no production default. */
|
|
176
191
|
export function envTestParkSummaryGraceMs() {
|
|
@@ -11,8 +11,7 @@ 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
13
|
* governs both the node's durable inheritance and reader-facing output:
|
|
14
|
-
* one update for subscribers and history, then a
|
|
15
|
-
* prose for an external channel to relay.
|
|
14
|
+
* one update for subscribers and history, then a brief completion reply.
|
|
16
15
|
*
|
|
17
16
|
* The durable inheritance shares a CONTRACT with `node yield`'s pre-invocation
|
|
18
17
|
* guide (roadmap current, short and shrinking; context dir for in-progress
|
|
@@ -16,8 +16,7 @@ export const STALL_REPROMPT = "You've stopped but you're not waiting on anyone a
|
|
|
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
18
|
* governs both the node's durable inheritance and reader-facing output:
|
|
19
|
-
* one update for subscribers and history, then a
|
|
20
|
-
* prose for an external channel to relay.
|
|
19
|
+
* one update for subscribers and history, then a brief completion reply.
|
|
21
20
|
*
|
|
22
21
|
* The durable inheritance shares a CONTRACT with `node yield`'s pre-invocation
|
|
23
22
|
* guide (roadmap current, short and shrinking; context dir for in-progress
|
|
@@ -28,8 +27,8 @@ export const PARK_SUMMARY_PROMPT = 'This conversation has been idle with nothing
|
|
|
28
27
|
+ '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
28
|
+ '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
29
|
+ '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
|
|
32
|
-
+ '6.
|
|
30
|
+
+ '5. Push exactly one regular update with `crtr push update --tier deferred`, never `crtr push final`. Write for someone who has not seen this conversation: name the work in plain terms, say where it stands, and say what remains or is blocked. Put the current outcome in the first line; include only needed decisions and recovery handles. This concludes the conversation, not the mandate.\n\n'
|
|
31
|
+
+ '6. When finished, reply with exactly `done`.';
|
|
33
32
|
/** Static recovery prompts shared by the broker producer and display classifier. */
|
|
34
33
|
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.';
|
|
35
34
|
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/dist/types.d.ts
CHANGED
|
@@ -157,6 +157,14 @@ export interface BrokerThresholdsConfig {
|
|
|
157
157
|
/** Defer daemon-initiated revives once the live broker census reaches this count. */
|
|
158
158
|
automaticReviveCap: number;
|
|
159
159
|
}
|
|
160
|
+
/** Host-wide node-lifecycle timing. Read only from user scope by crtrd, like
|
|
161
|
+
* `brokerThresholds`: project configuration must not change another user's
|
|
162
|
+
* daemon. */
|
|
163
|
+
export interface LifecycleConfig {
|
|
164
|
+
/** How long an eligible resident idles before its unattended-parking turn
|
|
165
|
+
* begins, in milliseconds. `CRTR_UNATTENDED_PARK_MS` overrides this. */
|
|
166
|
+
unattendedParkMs: number;
|
|
167
|
+
}
|
|
160
168
|
export type KeybindingOverrides = Record<string, string[]>;
|
|
161
169
|
/** Compile-time seam for runtime consumers migrating to semantic binding IDs. */
|
|
162
170
|
export type CanvasAction = {
|
|
@@ -251,6 +259,8 @@ export interface ScopeConfig {
|
|
|
251
259
|
* Independent attended roots keep their normal viewer behavior. */
|
|
252
260
|
auto_open_child_viewers: boolean;
|
|
253
261
|
brokerThresholds: BrokerThresholdsConfig;
|
|
262
|
+
/** Host-wide node-lifecycle timing (user scope only). */
|
|
263
|
+
lifecycle: LifecycleConfig;
|
|
254
264
|
/** Ring a short completion sound in the attach viewer when the node you are
|
|
255
265
|
* watching finishes a turn and hands control back (the Claude-Code Stop-hook
|
|
256
266
|
* ding). Only ever fires inside a live viewer pane — a headless/background
|
package/dist/types.js
CHANGED
|
@@ -82,6 +82,10 @@ export function defaultScopeConfig() {
|
|
|
82
82
|
// sized as a backstop that actually engages before the machine does.
|
|
83
83
|
// Override per host with CRTR_MAX_LIVE_BROKERS / CRTR_WARN_LIVE_BROKERS.
|
|
84
84
|
brokerThresholds: { warning: 16, automaticReviveCap: 32 },
|
|
85
|
+
// The unattended-park idle-eligibility delay: how long an eligible resident
|
|
86
|
+
// idles before its parking turn begins. Override per host with
|
|
87
|
+
// CRTR_UNATTENDED_PARK_MS.
|
|
88
|
+
lifecycle: { unattendedParkMs: 15 * 60_000 },
|
|
85
89
|
completion_bell: true,
|
|
86
90
|
working_gerunds: [...DEFAULT_WORKING_GERUNDS],
|
|
87
91
|
whip_mode: false,
|
package/package.json
CHANGED
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.270",
|
|
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.270",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"dependencies": {
|