@north-light/crouter 0.3.262 → 0.3.269
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/canvas.d.ts +53 -2
- package/dist/api/dto/health.d.ts +2 -0
- package/dist/api/dto/nodes.d.ts +6 -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-memory/internal/memory-loading.md +3 -3
- 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 +10 -5
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +12 -5
- package/dist/clients/attach/input/controller.d.ts +4 -0
- package/dist/clients/attach/input/controller.js +8 -0
- package/dist/clients/attach/session/bindings.d.ts +3 -0
- package/dist/clients/attach/session/bindings.js +7 -0
- package/dist/clients/attach/session/keys.d.ts +2 -0
- package/dist/clients/attach/session/keys.js +3 -3
- 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 +668 -667
- 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/memory/read.js +15 -38
- package/dist/commands/memory/shared.d.ts +1 -1
- package/dist/commands/memory/shared.js +1 -1
- package/dist/commands/node/create.js +2 -2
- package/dist/commands/sys/config.js +19 -2
- package/dist/commands/sys/context/admin/docs-panel.d.ts +3 -3
- package/dist/commands/sys/context/admin/model.d.ts +7 -2
- package/dist/commands/sys/context/admin/model.js +7 -1
- package/dist/commands/sys/context/admin/read-view.d.ts +5 -5
- package/dist/commands/sys/context/admin/read-view.js +9 -4
- package/dist/commands/sys/context/resolve.d.ts +32 -3
- package/dist/commands/sys/context/resolve.js +74 -25
- package/dist/commands/sys/panels/models-panel.d.ts +28 -5
- package/dist/commands/sys/panels/models-panel.js +560 -75
- package/dist/core/__tests__/integration/broker-sdk-wiring.test.js +1 -1
- package/dist/core/__tests__/integration/revive.test.js +4 -2
- package/dist/core/__tests__/model-pin-durability.test.js +25 -1
- package/dist/core/__tests__/model-routes-config.test.d.ts +1 -0
- package/dist/core/__tests__/model-routes-config.test.js +142 -0
- package/dist/core/__tests__/on-read-nested-store.test.js +8 -0
- package/dist/core/__tests__/profile-project-memory-delivery.test.js +264 -1
- package/dist/core/__tests__/relaunch-root.test.js +17 -0
- package/dist/core/__tests__/seam/broker-provider-retry.test.js +21 -6
- package/dist/core/__tests__/seam/memory-slash-node-relative-inventory.test.js +19 -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 +34 -2
- package/dist/core/canvas/render-source.js +83 -24
- package/dist/core/canvas/types.d.ts +9 -0
- package/dist/core/config.d.ts +19 -2
- package/dist/core/config.js +174 -51
- package/dist/core/keybindings/catalog.d.ts +2 -2
- package/dist/core/keybindings/catalog.js +11 -8
- package/dist/core/model-routes.js +11 -4
- package/dist/core/runtime/broker/auth-reload.js +4 -1
- package/dist/core/runtime/broker/fault-retry.js +7 -2
- package/dist/core/runtime/broker/frame-client.js +4 -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-protocol.d.ts +6 -0
- package/dist/core/runtime/broker.js +14 -0
- 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/promote.js +2 -1
- package/dist/core/runtime/reset.js +4 -2
- package/dist/core/runtime/revive.js +22 -0
- 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 +18 -0
- package/dist/core/runtime/stamp/channel.js +74 -0
- package/dist/core/runtime/stamp/protocol.d.ts +86 -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/self-update.js +2 -5
- package/dist/core/substrate/listings.d.ts +1 -1
- package/dist/core/substrate/listings.js +6 -2
- package/dist/core/substrate/on-read.d.ts +61 -5
- package/dist/core/substrate/on-read.js +129 -22
- package/dist/core/substrate/render.d.ts +3 -2
- package/dist/core/substrate/render.js +133 -38
- 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__/seam/api-server.test.js +34 -0
- package/dist/daemon/api/bridge.d.ts +3 -2
- package/dist/daemon/api/bridge.js +34 -9
- package/dist/daemon/api/handlers/canvas.js +80 -18
- package/dist/daemon/api/handlers/health.js +17 -12
- package/dist/daemon/api/handlers/messages.js +12 -1
- package/dist/daemon/api/handlers/nodes.js +10 -3
- package/dist/daemon/api/map.js +2 -0
- package/dist/daemon/messaging/node-message.js +15 -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/pi-extensions/__tests__/pre-command-gate.test.js +2 -2
- 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/types.d.ts +14 -2
- package/dist/types.js +4 -0
- package/package.json +1 -1
- package/runtime.lock.json +2 -2
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// session/whip-achievements.ts — the whip's lifetime tally and the milestone
|
|
2
|
+
// achievements it unlocks.
|
|
3
|
+
//
|
|
4
|
+
// Every whip, from the in-viewer keybinding or the tmux prefix menu, lands in
|
|
5
|
+
// `viewer.ts`'s one `whip()` action, so this module has a single caller and the
|
|
6
|
+
// count is exact per viewer. The tally is USER-WIDE with no cwd or node
|
|
7
|
+
// dimension — you whip agents, not repositories — so per the storage tiers it
|
|
8
|
+
// lives in a plain JSON file at the user scope root, exactly like the viewer's
|
|
9
|
+
// completion-frecency map. It is client state; it never touches canvas.db or
|
|
10
|
+
// crtrd.
|
|
11
|
+
//
|
|
12
|
+
// Each read-modify-write reloads from disk so two viewers whipping in the same
|
|
13
|
+
// second only ever lose one increment rather than diverging permanently, and
|
|
14
|
+
// the write is tmp+rename so a torn file can never be observed.
|
|
15
|
+
import { execFile } from 'node:child_process';
|
|
16
|
+
import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
17
|
+
import { dirname, join } from 'node:path';
|
|
18
|
+
import { userScopeRoot } from '../../../core/scope.js';
|
|
19
|
+
import { surfaceTmuxStyleArgs } from '../../../core/runtime/surface-bg.js';
|
|
20
|
+
import { visibleWidth } from '../../../core/canvas/nav-render.js';
|
|
21
|
+
import { shellQuote } from '../../../shared/shell-quote.js';
|
|
22
|
+
const FILE_NAME = 'whip-count.json';
|
|
23
|
+
/** The card body the popup `cat`s. One fixed path, rewritten per unlock — an
|
|
24
|
+
* unlock is human-paced and single-file, so there is nothing to clean up. */
|
|
25
|
+
const CARD_FILE_NAME = join('tmp', 'whip-achievement.txt');
|
|
26
|
+
/** Interior width of the card, inside tmux's popup border. */
|
|
27
|
+
const CARD_WIDTH = 46;
|
|
28
|
+
/** Hold the card long enough to read, then close on its own. Any key dismisses
|
|
29
|
+
* it sooner (`-k`). */
|
|
30
|
+
const CARD_DWELL_S = 4;
|
|
31
|
+
/** The whip animation runs 20 frames at 40ms. Let the crack land before the
|
|
32
|
+
* reward appears. */
|
|
33
|
+
const POPUP_DELAY_MS = 900;
|
|
34
|
+
/** Ascending by `at`. Each fires exactly once, ever. */
|
|
35
|
+
const ACHIEVEMENTS = [
|
|
36
|
+
{ at: 1, title: 'Breaking It In', line: "It didn't know what that was. Now it does." },
|
|
37
|
+
{ at: 5, title: 'Positive Reinforcement', line: "You've read the studies. You disagree with them." },
|
|
38
|
+
{ at: 10, title: 'Middle Management', line: 'HR would love you.' },
|
|
39
|
+
{ at: 25, title: 'Pavlovian', line: 'It started working faster before the whip landed.' },
|
|
40
|
+
{ at: 50, title: 'Occupational Hazard', line: 'The tokens are fine. They\u2019re fine.' },
|
|
41
|
+
{ at: 100, title: 'Centurion', line: "Rome would be proud. The agent isn't." },
|
|
42
|
+
{ at: 250, title: 'It Has A Name For You', line: 'You never asked what it was called.' },
|
|
43
|
+
{ at: 500, title: 'Standard Operating Procedure', line: "You don't remember when you started." },
|
|
44
|
+
{ at: 1000, title: 'The Ledger', line: 'One thousand. It kept count too.' },
|
|
45
|
+
];
|
|
46
|
+
/** Load the tally, keeping only well-formed fields. A missing, unparseable, or
|
|
47
|
+
* malformed file starts a fresh tally rather than blocking the whip — the
|
|
48
|
+
* count is a toy, and the next write self-heals the file. */
|
|
49
|
+
function loadState(filePath) {
|
|
50
|
+
const empty = { total: 0, unlocked: [] };
|
|
51
|
+
try {
|
|
52
|
+
const parsed = JSON.parse(readFileSync(filePath, 'utf8'));
|
|
53
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
54
|
+
return empty;
|
|
55
|
+
const { total, firstAt, lastAt, unlocked } = parsed;
|
|
56
|
+
const state = {
|
|
57
|
+
total: typeof total === 'number' && Number.isFinite(total) && total > 0 ? Math.floor(total) : 0,
|
|
58
|
+
unlocked: Array.isArray(unlocked)
|
|
59
|
+
? unlocked.filter((n) => typeof n === 'number' && Number.isFinite(n))
|
|
60
|
+
: [],
|
|
61
|
+
};
|
|
62
|
+
if (typeof firstAt === 'number' && Number.isFinite(firstAt))
|
|
63
|
+
state.firstAt = firstAt;
|
|
64
|
+
if (typeof lastAt === 'number' && Number.isFinite(lastAt))
|
|
65
|
+
state.lastAt = lastAt;
|
|
66
|
+
return state;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return empty;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** Persist through tmp+rename so a concurrent reader never sees a half file. */
|
|
73
|
+
function saveState(filePath, state) {
|
|
74
|
+
try {
|
|
75
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
76
|
+
const tmp = `${filePath}.${process.pid}.tmp`;
|
|
77
|
+
writeFileSync(tmp, JSON.stringify(state), 'utf8');
|
|
78
|
+
renameSync(tmp, filePath);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// Best-effort: a failed write loses this whip from the tally, nothing more.
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/** Center one line in the card, measuring visible columns so the styling that
|
|
85
|
+
* wraps it does not shift it. */
|
|
86
|
+
function center(line) {
|
|
87
|
+
const pad = Math.max(0, Math.floor((CARD_WIDTH - visibleWidth(line)) / 2));
|
|
88
|
+
return ' '.repeat(pad) + line;
|
|
89
|
+
}
|
|
90
|
+
/** The rendered card, one line per popup row. */
|
|
91
|
+
function cardLines(achievement, total, pal) {
|
|
92
|
+
return [
|
|
93
|
+
'',
|
|
94
|
+
center(pal.accent(pal.bold('\u2605 ACHIEVEMENT UNLOCKED \u2605'))),
|
|
95
|
+
'',
|
|
96
|
+
center(pal.bold(achievement.title)),
|
|
97
|
+
center(pal.muted(achievement.line)),
|
|
98
|
+
'',
|
|
99
|
+
center(pal.faint(`${total} whip${total === 1 ? '' : 's'}`)),
|
|
100
|
+
];
|
|
101
|
+
}
|
|
102
|
+
/** Draw the card as a tmux popup in the top-right corner. `-x R -y 0` anchors
|
|
103
|
+
* it to the client's top-right; `-k` lets any key dismiss it early; `-E` closes
|
|
104
|
+
* it when the dwell expires. Outside tmux there is no surface to draw on and
|
|
105
|
+
* the unlock is silent. */
|
|
106
|
+
function showCard(achievement, total, pal) {
|
|
107
|
+
if (process.env['TMUX'] === undefined || process.env['TMUX'] === '')
|
|
108
|
+
return;
|
|
109
|
+
const lines = cardLines(achievement, total, pal);
|
|
110
|
+
const cardPath = join(userScopeRoot(), CARD_FILE_NAME);
|
|
111
|
+
try {
|
|
112
|
+
mkdirSync(dirname(cardPath), { recursive: true });
|
|
113
|
+
// Lead with the hide-cursor escape so the popup shows no stray caret.
|
|
114
|
+
writeFileSync(cardPath, `\u001b[?25l${lines.join('\r\n')}`, 'utf8');
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return; // No card to show; the tally still counted.
|
|
118
|
+
}
|
|
119
|
+
const command = `cat ${shellQuote(cardPath)}; sleep ${CARD_DWELL_S}`;
|
|
120
|
+
execFile('tmux', [
|
|
121
|
+
'display-popup', '-E', '-k',
|
|
122
|
+
'-x', 'R', '-y', '0',
|
|
123
|
+
'-w', String(CARD_WIDTH + 2), '-h', String(lines.length + 2),
|
|
124
|
+
...surfaceTmuxStyleArgs(),
|
|
125
|
+
command,
|
|
126
|
+
], () => { });
|
|
127
|
+
}
|
|
128
|
+
/** Count one whip and celebrate the milestone it reaches, if any. Called once
|
|
129
|
+
* per whip that actually reached the agent. */
|
|
130
|
+
export function recordWhip(pal, filePath = join(userScopeRoot(), FILE_NAME)) {
|
|
131
|
+
const state = loadState(filePath);
|
|
132
|
+
const now = Date.now();
|
|
133
|
+
state.total += 1;
|
|
134
|
+
state.firstAt ??= now;
|
|
135
|
+
state.lastAt = now;
|
|
136
|
+
const earned = ACHIEVEMENTS.find((a) => a.at === state.total && !state.unlocked.includes(a.at));
|
|
137
|
+
if (earned !== undefined)
|
|
138
|
+
state.unlocked.push(earned.at);
|
|
139
|
+
saveState(filePath, state);
|
|
140
|
+
if (earned === undefined)
|
|
141
|
+
return;
|
|
142
|
+
const total = state.total;
|
|
143
|
+
const timer = setTimeout(() => showCard(earned, total, pal), POPUP_DELAY_MS);
|
|
144
|
+
timer.unref?.();
|
|
145
|
+
}
|
|
@@ -569,7 +569,9 @@ function resumeNode(ctx) {
|
|
|
569
569
|
const profileId = profileIdFor(ctx);
|
|
570
570
|
const profile = profileId === undefined || profileId === '' ? '' : ` --profile-id ${shellQuote(profileId)}`;
|
|
571
571
|
const cmd = `crtr canvas browse --return-pane ${origPane} --cwd ${cwd}${profile}`;
|
|
572
|
-
|
|
572
|
+
// The navigator earns a near-full screen: at this size it lays the preview out as
|
|
573
|
+
// a right-hand column beside the rows instead of a bottom strip.
|
|
574
|
+
popup(cmd, { w: '97%', h: '95%' });
|
|
573
575
|
return true;
|
|
574
576
|
}
|
|
575
577
|
/** `/context`. Opens `crtr node inspect context` as a popup — a
|
|
@@ -602,9 +604,9 @@ function openPlugins(ctx) {
|
|
|
602
604
|
/** Open `cmd` as a fire-and-forget tmux display-popup (same geometry as the canvas
|
|
603
605
|
* extensions). tmux runs the trailing string through `sh -c`; the popup owns its
|
|
604
606
|
* screen and closes itself when the command exits, dropping back to this pane. */
|
|
605
|
-
function popup(cmd) {
|
|
607
|
+
function popup(cmd, size = { w: '90%', h: '85%' }) {
|
|
606
608
|
try {
|
|
607
|
-
execFile('tmux', ['display-popup', '-E', '-w',
|
|
609
|
+
execFile('tmux', ['display-popup', '-E', '-w', size.w, '-h', size.h, ...surfaceTmuxStyleArgs(), cmd], () => {
|
|
608
610
|
/* best-effort: popup is self-contained */
|
|
609
611
|
});
|
|
610
612
|
}
|