@crouton-kit/crouter 0.3.70 → 0.3.79
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/build-root.d.ts +12 -4
- package/dist/build-root.js +25 -6
- package/dist/builtin-memory/00-runtime-base.md +3 -10
- package/dist/builtin-memory/04-base-worker.md +18 -0
- package/dist/builtin-memory/04-orchestration-kernel.md +1 -1
- package/dist/builtin-memory/crouter-development/plugins.md +82 -5
- package/dist/builtin-pi-packages/pi-crtr-extensions/README.md +13 -34
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/provider-rotation.test.ts +1115 -1
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +234 -71
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/statusline.ts +2 -9
- package/dist/builtin-pi-packages/pi-crtr-extensions/lib/subscription-state.ts +2 -516
- package/dist/builtin-pi-packages/pi-crtr-extensions/package-lock.json +2 -2
- package/dist/builtin-pi-packages/pi-mode-switch/README.md +11 -39
- package/dist/builtin-pi-packages/pi-mode-switch/extensions/index.ts +20 -15
- package/dist/builtin-pi-packages/pi-mode-switch/package.json +1 -1
- package/dist/builtin-views/canvas/tui.mjs +8 -9
- package/dist/builtin-views/chat/tui.mjs +12 -12
- package/dist/builtin-views/git-pr/tui.mjs +7 -8
- package/dist/builtin-views/inbox/tui.mjs +27 -41
- package/dist/builtin-views/linkedin/tui.mjs +23 -37
- package/dist/builtin-views/prompt-review/tui.mjs +11 -11
- package/dist/builtin-views/settings/tui.mjs +11 -11
- package/dist/builtin-views/workspace-sidebar/tui.mjs +8 -9
- package/dist/clients/attach/__tests__/attach-chrome-remote.test.js +11 -6
- package/dist/clients/attach/__tests__/attach-keybindings.test.d.ts +1 -0
- package/dist/clients/attach/__tests__/attach-keybindings.test.js +113 -0
- package/dist/clients/attach/__tests__/crtr-output-render.test.js +1 -1
- package/dist/clients/attach/__tests__/mermaid-render.test.d.ts +1 -0
- package/dist/clients/attach/__tests__/mermaid-render.test.js +28 -0
- package/dist/clients/attach/__tests__/oauth-dialog-lifecycle.test.d.ts +1 -0
- package/dist/clients/attach/__tests__/oauth-dialog-lifecycle.test.js +110 -0
- package/dist/clients/attach/attach-cmd.d.ts +9 -1
- package/dist/clients/attach/attach-cmd.js +841 -792
- package/dist/clients/attach/auth-pickers.d.ts +0 -12
- package/dist/clients/attach/auth-pickers.js +64 -15
- package/dist/clients/attach/canvas-panels.js +2 -3
- package/dist/clients/attach/chat-view.d.ts +7 -8
- package/dist/clients/attach/chat-view.js +119 -79
- package/dist/clients/attach/edit-diff-render.d.ts +6 -0
- package/dist/clients/attach/edit-diff-render.js +263 -0
- package/dist/clients/attach/graph-overlay.d.ts +12 -2
- package/dist/clients/attach/graph-overlay.js +83 -33
- package/dist/clients/attach/input-controller.d.ts +28 -2
- package/dist/clients/attach/input-controller.js +38 -5
- package/dist/clients/attach/mermaid-render.js +5 -2
- package/dist/clients/attach/pickers.d.ts +8 -7
- package/dist/clients/attach/pickers.js +11 -17
- package/dist/clients/attach/slash-commands.d.ts +9 -0
- package/dist/clients/attach/slash-commands.js +127 -7
- package/dist/clients/attach/titled-editor.d.ts +12 -1
- package/dist/clients/attach/titled-editor.js +103 -8
- package/dist/commands/canvas-browse.js +2 -2
- package/dist/commands/human/queue.js +3 -4
- package/dist/commands/memory/lint.js +39 -5
- package/dist/commands/memory/write.js +1 -0
- package/dist/commands/node.js +9 -2
- package/dist/commands/pkg/plugin-inspect.js +22 -1
- package/dist/commands/pkg/plugin-manage.js +31 -9
- package/dist/commands/surface-tmux-spread.js +1 -3
- package/dist/commands/sys/__tests__/config-keybindings.test.d.ts +1 -0
- package/dist/commands/sys/__tests__/config-keybindings.test.js +55 -0
- package/dist/commands/sys/__tests__/config-model-ladders.test.d.ts +1 -0
- package/dist/commands/sys/__tests__/config-model-ladders.test.js +121 -0
- package/dist/commands/sys/__tests__/setup-core.test.js +158 -1
- package/dist/commands/sys/config.js +18 -21
- package/dist/commands/sys/doctor.js +42 -4
- package/dist/commands/sys/setup-core.d.ts +49 -1
- package/dist/commands/sys/setup-core.js +161 -4
- package/dist/commands/sys/setup.d.ts +88 -0
- package/dist/commands/sys/setup.js +943 -169
- package/dist/commands/view-pick.d.ts +4 -0
- package/dist/commands/view-pick.js +17 -7
- package/dist/core/__tests__/base-worker-prompt.test.d.ts +1 -0
- package/dist/core/__tests__/base-worker-prompt.test.js +24 -0
- package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +232 -1
- package/dist/core/__tests__/canvas-inbox-watcher.test.js +34 -9
- package/dist/core/__tests__/command-plugins-surfaces.test.d.ts +1 -0
- package/dist/core/__tests__/command-plugins-surfaces.test.js +298 -0
- package/dist/core/__tests__/command-plugins.test.d.ts +1 -0
- package/dist/core/__tests__/command-plugins.test.js +444 -0
- package/dist/core/__tests__/fault-classifier.test.js +15 -0
- package/dist/core/__tests__/fixtures/fake-engine.d.ts +6 -0
- package/dist/core/__tests__/fixtures/fake-engine.js +9 -1
- package/dist/core/__tests__/full/broker-dialogs.test.js +7 -2
- package/dist/core/__tests__/host-teardown-process-group.test.js +15 -4
- package/dist/core/__tests__/preview-registry-sync.test.js +30 -1
- package/dist/core/__tests__/scope-crouter-home-fence.test.d.ts +1 -0
- package/dist/core/__tests__/scope-crouter-home-fence.test.js +55 -0
- package/dist/core/__tests__/stream-watchdog.test.d.ts +1 -0
- package/dist/core/__tests__/stream-watchdog.test.js +70 -0
- package/dist/core/__tests__/tmux-surface.test.js +72 -0
- package/dist/core/canvas/browse/__tests__/model.test.js +23 -5
- package/dist/core/canvas/browse/__tests__/render.test.js +1 -0
- package/dist/core/canvas/browse/app.d.ts +6 -0
- package/dist/core/canvas/browse/app.js +230 -41
- package/dist/core/canvas/browse/model.d.ts +10 -6
- package/dist/core/canvas/browse/model.js +20 -12
- package/dist/core/canvas/browse/pins.d.ts +4 -0
- package/dist/core/canvas/browse/pins.js +29 -0
- package/dist/core/canvas/browse/render.d.ts +13 -1
- package/dist/core/canvas/browse/render.js +72 -15
- package/dist/core/canvas/nav-model.js +5 -11
- package/dist/core/canvas/paths.d.ts +5 -0
- package/dist/core/canvas/paths.js +7 -0
- package/dist/core/command-plugins/adapter.d.ts +15 -0
- package/dist/core/command-plugins/adapter.js +145 -0
- package/dist/core/command-plugins/compose.d.ts +5 -0
- package/dist/core/command-plugins/compose.js +56 -0
- package/dist/core/command-plugins/discovery.d.ts +104 -0
- package/dist/core/command-plugins/discovery.js +565 -0
- package/dist/core/config.d.ts +48 -0
- package/dist/core/config.js +228 -110
- package/dist/core/fault-classifier.js +1 -1
- package/dist/core/fs-utils.d.ts +1 -0
- package/dist/core/fs-utils.js +15 -1
- package/dist/core/keybindings/__tests__/bespoke-consumers.test.d.ts +1 -0
- package/dist/core/keybindings/__tests__/bespoke-consumers.test.js +40 -0
- package/dist/core/keybindings/__tests__/resolve.test.d.ts +1 -0
- package/dist/core/keybindings/__tests__/resolve.test.js +224 -0
- package/dist/core/keybindings/catalog.d.ts +14 -0
- package/dist/core/keybindings/catalog.js +257 -0
- package/dist/core/keybindings/index.d.ts +5 -0
- package/dist/core/keybindings/index.js +4 -0
- package/dist/core/keybindings/match.d.ts +29 -0
- package/dist/core/keybindings/match.js +99 -0
- package/dist/core/keybindings/persistence.d.ts +14 -0
- package/dist/core/keybindings/persistence.js +31 -0
- package/dist/core/keybindings/resolve.d.ts +14 -0
- package/dist/core/keybindings/resolve.js +305 -0
- package/dist/core/keybindings/types.d.ts +37 -0
- package/dist/core/keybindings/types.js +1 -0
- package/dist/core/memory-resolver.d.ts +1 -1
- package/dist/core/memory-resolver.js +10 -3
- package/dist/core/predicate.d.ts +5 -3
- package/dist/core/predicate.js +5 -3
- package/dist/core/profiles/select.d.ts +6 -0
- package/dist/core/profiles/select.js +86 -52
- package/dist/core/provider-management.d.ts +12 -0
- package/dist/core/provider-management.js +24 -0
- package/dist/core/runtime/broker-protocol.d.ts +33 -5
- package/dist/core/runtime/broker.js +164 -14
- package/dist/core/runtime/launch.d.ts +39 -6
- package/dist/core/runtime/launch.js +78 -20
- package/dist/core/runtime/naming.js +3 -3
- package/dist/core/runtime/pi-cli.d.ts +6 -0
- package/dist/core/runtime/pi-cli.js +16 -2
- package/dist/core/runtime/pi-vendored.d.ts +8 -0
- package/dist/core/runtime/pi-vendored.js +14 -0
- package/dist/core/runtime/placement.d.ts +2 -2
- package/dist/core/runtime/placement.js +4 -1
- package/dist/core/runtime/promote.js +4 -0
- package/dist/core/runtime/recap.d.ts +3 -3
- package/dist/core/runtime/recap.js +75 -47
- package/dist/core/runtime/recycle.js +6 -1
- package/dist/core/runtime/reset.js +5 -0
- package/dist/core/runtime/session-list-cache.d.ts +33 -0
- package/dist/core/runtime/session-list-cache.js +338 -0
- package/dist/core/runtime/spawn.js +52 -45
- package/dist/core/runtime/stream-watchdog.d.ts +26 -0
- package/dist/core/runtime/stream-watchdog.js +75 -0
- package/dist/core/runtime/tmux-chrome.d.ts +1 -1
- package/dist/core/runtime/tmux-chrome.js +2 -2
- package/dist/core/runtime/tmux.d.ts +18 -15
- package/dist/core/runtime/tmux.js +217 -110
- package/dist/core/scope.js +27 -4
- package/dist/core/subscription-state.d.ts +90 -0
- package/dist/core/subscription-state.js +762 -0
- package/dist/core/tui/__tests__/host-keybindings.test.d.ts +1 -0
- package/dist/core/tui/__tests__/host-keybindings.test.js +112 -0
- package/dist/core/tui/host.d.ts +30 -1
- package/dist/core/tui/host.js +148 -34
- package/dist/core/view/contract.d.ts +17 -3
- package/dist/daemon/crtrd.js +253 -12
- package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +41 -0
- package/dist/pi-extensions/__tests__/canvas-stophook-context-nudge.test.d.ts +1 -0
- package/dist/pi-extensions/__tests__/canvas-stophook-context-nudge.test.js +121 -0
- package/dist/pi-extensions/canvas-inbox-watcher.d.ts +1 -1
- package/dist/pi-extensions/canvas-inbox-watcher.js +203 -25
- package/dist/pi-extensions/canvas-recap.d.ts +4 -0
- package/dist/pi-extensions/canvas-recap.js +73 -46
- package/dist/pi-extensions/canvas-stophook.d.ts +11 -0
- package/dist/pi-extensions/canvas-stophook.js +33 -4
- package/dist/types.d.ts +9 -26
- package/dist/types.js +1 -28
- package/dist/web-client/assets/{index-DiFuLcp6.js → index--md2ylfi.js} +1 -1
- package/dist/web-client/index.html +1 -1
- package/dist/web-client/sw.js +1 -1
- package/package.json +8 -4
- package/dist/pi-extensions/canvas-nav.d.ts +0 -43
- package/dist/pi-extensions/canvas-nav.js +0 -640
- package/dist/pi-extensions/widget-order-bus.d.ts +0 -6
- package/dist/pi-extensions/widget-order-bus.js +0 -34
|
@@ -10,6 +10,20 @@
|
|
|
10
10
|
// `node_modules/@earendil-works/pi-coding-agent/dist/core/slash-commands.js`.
|
|
11
11
|
// If the upstream list changes (entries added/removed/reworded), update the copy
|
|
12
12
|
// below to match and adjust the count note.
|
|
13
|
+
import { homedir } from 'node:os';
|
|
14
|
+
import { join } from 'node:path';
|
|
15
|
+
/** pi's sessions root, VENDORED from pi `config.getSessionsDir()` (= `<agentDir>/
|
|
16
|
+
* sessions`). pi's package `exports` map is `.`-only, so config.js can't be
|
|
17
|
+
* deep-imported, and a ROOT import of `getAgentDir` would eager-load the entire
|
|
18
|
+
* heavy pi SDK index on crtr's front-door hot path (the reason broker-sdk.ts
|
|
19
|
+
* dynamic-imports the engine). Mirrors pi: `PI_CODING_AGENT_DIR` env, else
|
|
20
|
+
* `~/.pi/agent` (APP_NAME defaults to 'pi'). Re-sync on a pi SDK bump that moves
|
|
21
|
+
* the sessions dir — same vendoring rationale as the slash-command list below. */
|
|
22
|
+
export function piSessionsRoot() {
|
|
23
|
+
const env = process.env['PI_CODING_AGENT_DIR'];
|
|
24
|
+
const agentDir = env !== undefined && env !== '' ? env : join(homedir(), '.pi', 'agent');
|
|
25
|
+
return join(agentDir, 'sessions');
|
|
26
|
+
}
|
|
13
27
|
/**
|
|
14
28
|
* pi's builtin slash commands — vendored verbatim from pi `core/slash-commands.js`
|
|
15
29
|
* `BUILTIN_SLASH_COMMANDS` (review C1). **21 entries at 0.78.1** (review n1 — NOT 23).
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type FocusRow } from '../canvas/index.js';
|
|
2
2
|
export type { FocusRow };
|
|
3
|
-
export { splitWindow, breakPane, selectLayout, selectPane, focusWindow, selectWindow, switchClient, openNodeWindow, ensureSession, paneLocation, paneExists, paneRunning, paneCurrentPath, currentTmux, inTmux, installMenuBinding, setPaneOption, getPaneOption, piCommand, shellQuote, respawnPaneSync, closePane, windowOfPane, renameWindow, paneOfWindow, windowAlive, listLivePanes, } from './tmux.js';
|
|
4
|
-
export type { RespawnPaneOpts } from './tmux.js';
|
|
3
|
+
export { splitWindow, breakPane, selectLayout, selectPane, focusWindow, selectWindow, switchClient, openNodeWindow, ensureSession, paneLocation, paneExists, paneRunning, paneCurrentPath, currentTmux, inTmux, installTmuxBindings, installTmuxBindings as installMenuBinding, setPaneOption, getPaneOption, piCommand, shellQuote, respawnPaneSync, closePane, windowOfPane, renameWindow, paneOfWindow, windowAlive, listLivePanes, } from './tmux.js';
|
|
4
|
+
export type { RespawnPaneOpts, TmuxInstalledPair, TmuxInstallDiagnostic, TmuxInstallResult } from './tmux.js';
|
|
5
5
|
/** The viewer a node has on screen, or null. UNIQUE(node_id) ⇒ at most one. */
|
|
6
6
|
export declare function focusOf(nodeId: string): FocusRow | null;
|
|
7
7
|
/** Does this node have a live viewer on screen? */
|
|
@@ -48,7 +48,10 @@ import { isPidAlive } from '../canvas/pid.js';
|
|
|
48
48
|
// re-exported for the same reason — callers building the seam's expected
|
|
49
49
|
// bundled-pi command string (e.g. the M1 seam test) need the identical quoting
|
|
50
50
|
// piCommand uses internally, without importing tmux.ts directly.
|
|
51
|
-
export { splitWindow, breakPane, selectLayout, selectPane, focusWindow, selectWindow, switchClient, openNodeWindow, ensureSession, paneLocation, paneExists, paneRunning, paneCurrentPath, currentTmux, inTmux,
|
|
51
|
+
export { splitWindow, breakPane, selectLayout, selectPane, focusWindow, selectWindow, switchClient, openNodeWindow, ensureSession, paneLocation, paneExists, paneRunning, paneCurrentPath, currentTmux, inTmux, installTmuxBindings,
|
|
52
|
+
// Compile seam for attach-cmd.ts, which still calls installMenuBinding();
|
|
53
|
+
// deleted when the attach unit imports installTmuxBindings directly.
|
|
54
|
+
installTmuxBindings as installMenuBinding, setPaneOption, getPaneOption, piCommand, shellQuote, respawnPaneSync, closePane, windowOfPane, renameWindow, paneOfWindow, windowAlive, listLivePanes, } from './tmux.js';
|
|
52
55
|
// ---------------------------------------------------------------------------
|
|
53
56
|
// Viewer registry reads — COMPOSE over the canvas focuses table (§2.3/§4).
|
|
54
57
|
//
|
|
@@ -55,6 +55,10 @@ export function promote(nodeId, opts = {}) {
|
|
|
55
55
|
// across the polymorph (the persona default is recomputed fresh for
|
|
56
56
|
// targetKind).
|
|
57
57
|
model: targetModel,
|
|
58
|
+
// Resolve kind/ladder config against the NODE's own cwd + profile, not
|
|
59
|
+
// this process's ambient scope.
|
|
60
|
+
cwd: node.cwd,
|
|
61
|
+
profileId: node.profile_id,
|
|
58
62
|
});
|
|
59
63
|
// Seed a barebones roadmap scaffold if absent so the file exists for a
|
|
60
64
|
// refresh. Pre-fill its Goal from the node's goal doc when present (set at
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
/** Ask pi headlessly for a
|
|
2
|
-
* `onRecap` with the parsed
|
|
1
|
+
/** Ask pi headlessly for a concise briefing of `conversation`, async. Invokes
|
|
2
|
+
* `onRecap` with the parsed string on success, or never (silent) on any
|
|
3
3
|
* failure — non-zero exit, timeout, empty/garbled output. Owns the subprocess
|
|
4
4
|
* mechanics — crucially it hands pi an immediate stdin EOF: `pi -p` reads stdin,
|
|
5
5
|
* and execFile's default stdin is an OPEN pipe that never closes, so without
|
|
6
6
|
* this pi blocks waiting for EOF and the call exits non-zero (the same gotcha
|
|
7
7
|
* naming.ts documents). */
|
|
8
|
-
export declare function generateRecap(conversation: string, onRecap: (
|
|
8
|
+
export declare function generateRecap(conversation: string, onRecap: (briefing: string) => void, liveModel?: string, roadmap?: string): void;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
// Inactivity recap — turn a dormant node's conversation into
|
|
2
|
-
//
|
|
1
|
+
// Inactivity recap — turn a dormant node's conversation into one concise
|
|
2
|
+
// returning-developer briefing for the recap widget.
|
|
3
3
|
//
|
|
4
4
|
// One entry point: generateRecap — async (execFile, non-blocking), mirroring
|
|
5
|
-
// naming.ts's headless-`pi -p` pattern exactly. It runs
|
|
6
|
-
// back-and-forth (the caller concatenates user
|
|
5
|
+
// naming.ts's headless-`pi -p` pattern exactly. It runs the live conversation's
|
|
6
|
+
// working model over the literal back-and-forth (the caller concatenates user and
|
|
7
|
+
// assistant text, plus the node's roadmap when one exists) with a stripped-
|
|
7
8
|
// down pi invocation (no tools/session/context/extensions/skills), so it's fast
|
|
8
9
|
// and side-effect free and can never recurse into another spawn.
|
|
9
10
|
//
|
|
@@ -15,37 +16,67 @@
|
|
|
15
16
|
// Plain TS-with-types — no imports from @earendil-works/* so it compiles inside
|
|
16
17
|
// crouter's own tsc build without a dep on the pi packages.
|
|
17
18
|
import { execFile } from 'node:child_process';
|
|
18
|
-
import { resolveBundledPiCliPath } from './pi-cli.js';
|
|
19
|
+
import { bundledPiSubprocessEnv, resolveBundledPiCliPath } from './pi-cli.js';
|
|
19
20
|
/** Wall-clock budget for the headless pi call before we give up (no recap). */
|
|
20
21
|
const RECAP_TIMEOUT_MS = 25_000;
|
|
21
|
-
/**
|
|
22
|
-
* the
|
|
23
|
-
*
|
|
22
|
+
/** Fallback model when the live session has not resolved one yet. Ordinarily the
|
|
23
|
+
* caller passes the provider/model that just completed the conversation's last
|
|
24
|
+
* turn, avoiding a dead hard-pinned provider. CRTR_RECAP_MODEL overrides both. */
|
|
24
25
|
const DEFAULT_RECAP_MODEL = 'anthropic/claude-haiku-4-5';
|
|
25
|
-
const RECAP_SYSTEM_PROMPT = 'You
|
|
26
|
-
'
|
|
27
|
-
'
|
|
28
|
-
'
|
|
29
|
-
'
|
|
30
|
-
'
|
|
31
|
-
'
|
|
32
|
-
'
|
|
33
|
-
'
|
|
34
|
-
'
|
|
35
|
-
'
|
|
36
|
-
'
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
26
|
+
const RECAP_SYSTEM_PROMPT = 'You brief a developer returning to a paused coding-agent conversation. They run ' +
|
|
27
|
+
'many agent sessions in parallel and have lost this one\'s thread: they no longer ' +
|
|
28
|
+
'know what this conversation is about or what they originally asked for. The ' +
|
|
29
|
+
'brief exists to solve exactly that — pinned above their input box, it must tell ' +
|
|
30
|
+
'them at a glance what this conversation is FOR and where it actually stands. ' +
|
|
31
|
+
'You are given the first and most recent messages of the literal user/agent ' +
|
|
32
|
+
'back-and-forth (tool output omitted), and the agent\'s roadmap file when one ' +
|
|
33
|
+
'exists.\n' +
|
|
34
|
+
'\n' +
|
|
35
|
+
'Output EXACTLY two plain-text lines, nothing else:\n' +
|
|
36
|
+
'Goal: <what was requested — the original ask / what this conversation is for>\n' +
|
|
37
|
+
'State: <catch-them-up: what is ACTUALLY going on right now>\n' +
|
|
38
|
+
'\n' +
|
|
39
|
+
'Both lines are extremely short — telegraphic fragments, roughly 8-15 words, no ' +
|
|
40
|
+
'filler. The State line must NOT restate the most recent message — the last ' +
|
|
41
|
+
'message is often mid-progress, a question, or noise; instead synthesize the ' +
|
|
42
|
+
'real position: what has landed, what is in flight, and the live blocker or ' +
|
|
43
|
+
'needed answer. If the agent is waiting on the developer, say exactly what for. ' +
|
|
44
|
+
'Be concrete (real file names, branches, SHAs, error messages — one specific ' +
|
|
45
|
+
'anchor beats abstractions). Never narrate turn by turn, hedge, or pad. If the ' +
|
|
46
|
+
'conversation has barely started, still emit your best two lines.\n' +
|
|
47
|
+
'\n' +
|
|
48
|
+
'Examples of the shape (from other conversations):\n' +
|
|
49
|
+
'Goal: Validate + commit the Unit 3 attach-keybinding migration in this worktree.\n' +
|
|
50
|
+
'State: Done — 17/17 tests green, committed and rebased onto main.\n' +
|
|
51
|
+
'\n' +
|
|
52
|
+
'Goal: Build the trimmed-v1 command-plugin seam (orchestrating phases).\n' +
|
|
53
|
+
'State: Phase 1 landed; waiting on the core-seam worker\'s final.\n' +
|
|
54
|
+
'\n' +
|
|
55
|
+
'Goal: Standing personal assistant — iMessage wakes, Reddit scans, small tasks.\n' +
|
|
56
|
+
'State: Idle — waiting on you: hook pick, map export, Omi timing.';
|
|
57
|
+
/** Put the content FIRST in delimited blocks (roadmap, then conversation), then
|
|
58
|
+
* the instruction, so the model reads the content before being told what to do. */
|
|
59
|
+
function recapUserPrompt(conversation, roadmap) {
|
|
60
|
+
const rm = roadmap !== undefined && roadmap.trim() !== ''
|
|
61
|
+
? `<roadmap>\n${roadmap.trim()}\n</roadmap>\n\n`
|
|
62
|
+
: '';
|
|
63
|
+
return `${rm}<conversation>\n${conversation}\n</conversation>\n\nWrite the two-line Goal:/State: briefing for the conversation above. Output JUST the two lines.`;
|
|
41
64
|
}
|
|
42
65
|
/** The pi argv for a headless recap request. Stripped down (no tools, session,
|
|
43
66
|
* context files, extensions, skills, templates, themes) so it's fast and
|
|
44
|
-
* side-effect free.
|
|
45
|
-
*
|
|
46
|
-
function recapArgs(conversation) {
|
|
67
|
+
* side-effect free. Thinking stays off; model precedence is explicit env
|
|
68
|
+
* override → the conversation's live working model → the small fallback. */
|
|
69
|
+
function recapArgs(conversation, liveModel, roadmap) {
|
|
47
70
|
const override = process.env['CRTR_RECAP_MODEL'];
|
|
48
|
-
|
|
71
|
+
// An explicit operator override wins. Otherwise prefer the live conversation's
|
|
72
|
+
// current model: it just completed a successful turn, while a hard-pinned
|
|
73
|
+
// provider (the old Haiku-only path) may be rate-limited and silently produce
|
|
74
|
+
// no brief. The small default remains for sessions with no resolved model yet.
|
|
75
|
+
const model = override !== undefined && override.trim() !== ''
|
|
76
|
+
? override.trim()
|
|
77
|
+
: liveModel !== undefined && liveModel.trim() !== ''
|
|
78
|
+
? liveModel.trim()
|
|
79
|
+
: DEFAULT_RECAP_MODEL;
|
|
49
80
|
const argv = [
|
|
50
81
|
'-p',
|
|
51
82
|
'--no-session',
|
|
@@ -59,44 +90,41 @@ function recapArgs(conversation) {
|
|
|
59
90
|
'--thinking', 'off',
|
|
60
91
|
'--model', model,
|
|
61
92
|
'--system-prompt', RECAP_SYSTEM_PROMPT,
|
|
62
|
-
recapUserPrompt(conversation),
|
|
93
|
+
recapUserPrompt(conversation, roadmap),
|
|
63
94
|
];
|
|
64
95
|
return argv;
|
|
65
96
|
}
|
|
66
|
-
/**
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
97
|
+
/** Normalize model stdout to the two-line `Goal:`/`State:` briefing. Collapses
|
|
98
|
+
* intra-line whitespace and drops blank lines; the viewer paints each line
|
|
99
|
+
* (wrapping to its width). A garbled shape still returns whatever lines came
|
|
100
|
+
* back — the widget is best-effort chrome, not a contract. */
|
|
70
101
|
function parseRecap(stdout) {
|
|
71
|
-
|
|
72
|
-
const lines = stdout
|
|
102
|
+
return stdout
|
|
73
103
|
.split('\n')
|
|
74
|
-
.map((l) => l.trim())
|
|
75
|
-
|
|
76
|
-
.
|
|
77
|
-
.filter((l) => l !== '');
|
|
78
|
-
return lines.slice(0, 3).map((l) => (l.length > LINE_CAP ? l.slice(0, LINE_CAP) : l));
|
|
104
|
+
.map((l) => l.replace(/\s+/g, ' ').trim())
|
|
105
|
+
.filter((l) => l !== '')
|
|
106
|
+
.join('\n');
|
|
79
107
|
}
|
|
80
|
-
/** Ask pi headlessly for a
|
|
81
|
-
* `onRecap` with the parsed
|
|
108
|
+
/** Ask pi headlessly for a concise briefing of `conversation`, async. Invokes
|
|
109
|
+
* `onRecap` with the parsed string on success, or never (silent) on any
|
|
82
110
|
* failure — non-zero exit, timeout, empty/garbled output. Owns the subprocess
|
|
83
111
|
* mechanics — crucially it hands pi an immediate stdin EOF: `pi -p` reads stdin,
|
|
84
112
|
* and execFile's default stdin is an OPEN pipe that never closes, so without
|
|
85
113
|
* this pi blocks waiting for EOF and the call exits non-zero (the same gotcha
|
|
86
114
|
* naming.ts documents). */
|
|
87
|
-
export function generateRecap(conversation, onRecap) {
|
|
115
|
+
export function generateRecap(conversation, onRecap, liveModel, roadmap) {
|
|
88
116
|
const body = (conversation ?? '').trim();
|
|
89
117
|
if (body === '')
|
|
90
118
|
return;
|
|
91
119
|
try {
|
|
92
|
-
const child = execFile(process.execPath, [resolveBundledPiCliPath(), ...recapArgs(body)], { encoding: 'utf8', timeout: RECAP_TIMEOUT_MS }, (err, stdout) => {
|
|
120
|
+
const child = execFile(process.execPath, [resolveBundledPiCliPath(), ...recapArgs(body, liveModel, roadmap)], { encoding: 'utf8', timeout: RECAP_TIMEOUT_MS, env: bundledPiSubprocessEnv() }, (err, stdout) => {
|
|
93
121
|
if (err || typeof stdout !== 'string')
|
|
94
122
|
return; // silent — no recap
|
|
95
|
-
const
|
|
96
|
-
if (
|
|
123
|
+
const briefing = parseRecap(stdout);
|
|
124
|
+
if (briefing === '')
|
|
97
125
|
return;
|
|
98
126
|
try {
|
|
99
|
-
onRecap(
|
|
127
|
+
onRecap(briefing);
|
|
100
128
|
}
|
|
101
129
|
catch { /* best-effort */ }
|
|
102
130
|
});
|
|
@@ -97,7 +97,11 @@ export async function recycleNode(nodeId, callerPane) {
|
|
|
97
97
|
ensureDaemon();
|
|
98
98
|
}
|
|
99
99
|
catch { /* daemon is best-effort */ }
|
|
100
|
-
|
|
100
|
+
// Resolve against the RECYCLED node's cwd + profile (not this process's
|
|
101
|
+
// ambient scope) and carry the profile onto the fresh root — it is the same
|
|
102
|
+
// person's seat, so profile-scope defaults (e.g. a persisted /model default)
|
|
103
|
+
// must keep applying.
|
|
104
|
+
const { launch } = buildLaunchSpec('general', 'base', { lifecycle: 'resident', hasManager: false, cwd: meta.cwd, profileId: meta.profile_id });
|
|
101
105
|
const root = spawnNode({
|
|
102
106
|
kind: 'general',
|
|
103
107
|
mode: 'base',
|
|
@@ -105,6 +109,7 @@ export async function recycleNode(nodeId, callerPane) {
|
|
|
105
109
|
cwd: meta.cwd,
|
|
106
110
|
name: 'general',
|
|
107
111
|
parent: null,
|
|
112
|
+
profile_id: meta.profile_id,
|
|
108
113
|
launch,
|
|
109
114
|
});
|
|
110
115
|
// Hand the viewport to the fresh root: reuse M's focus row over the SAME pane
|
|
@@ -86,6 +86,10 @@ export async function relaunchRoot(oldId, deps = {}) {
|
|
|
86
86
|
lifecycle: 'resident',
|
|
87
87
|
hasManager: false,
|
|
88
88
|
model: old.model_override ?? undefined,
|
|
89
|
+
// The OLD root's cwd + profile, not this process's ambient scope — the
|
|
90
|
+
// relaunch is the same seat, so profile-scope kind defaults keep applying.
|
|
91
|
+
cwd: old.cwd,
|
|
92
|
+
profileId: old.profile_id,
|
|
89
93
|
});
|
|
90
94
|
const newMeta = spawnNode({
|
|
91
95
|
kind: old.kind,
|
|
@@ -94,6 +98,7 @@ export async function relaunchRoot(oldId, deps = {}) {
|
|
|
94
98
|
cwd: old.cwd,
|
|
95
99
|
name: old.kind,
|
|
96
100
|
parent: null,
|
|
101
|
+
profile_id: old.profile_id,
|
|
97
102
|
launch,
|
|
98
103
|
modelOverride: old.model_override ?? undefined,
|
|
99
104
|
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { WireSessionInfo } from './broker-protocol.js';
|
|
2
|
+
export declare function compactSessionSearchText(text: string): string;
|
|
3
|
+
/** In-memory + file-backed cache keyed by absolute `.jsonl` path. One instance
|
|
4
|
+
* per broker; the file is shared across brokers (last-writer-wins via atomic
|
|
5
|
+
* rename — a lost update just costs one file's rebuild next open, never
|
|
6
|
+
* corruption). */
|
|
7
|
+
export declare class SessionListCache {
|
|
8
|
+
private readonly cacheFile;
|
|
9
|
+
private cache;
|
|
10
|
+
private loaded;
|
|
11
|
+
private persisting;
|
|
12
|
+
private persistAgain;
|
|
13
|
+
private persistTimer;
|
|
14
|
+
constructor(cacheFile: string);
|
|
15
|
+
/** Fire-and-forget warm of the cwd scope at broker start, so the first
|
|
16
|
+
* `/resume` open after this broker launches is already warm. The cold scan
|
|
17
|
+
* streams each file via readline (yields per chunk), so it never blocks the
|
|
18
|
+
* broker loop — pre-warming beats worker threads here. Errors are swallowed
|
|
19
|
+
* by the same paths that guard a normal open. */
|
|
20
|
+
prewarm(sessionDir: string): void;
|
|
21
|
+
/** Sessions for one cwd's session dir (the picker's default `cwd` scope). */
|
|
22
|
+
listDir(sessionDir: string): Promise<WireSessionInfo[]>;
|
|
23
|
+
/** Sessions across every cwd's dir under the sessions root (`all` scope). */
|
|
24
|
+
listAll(sessionsRoot: string): Promise<WireSessionInfo[]>;
|
|
25
|
+
private build;
|
|
26
|
+
private ensureLoaded;
|
|
27
|
+
/** Coalesce persists onto a single trailing timer so the tens-of-MB
|
|
28
|
+
* `JSON.stringify` never runs on the reply path and rapid opens write once. */
|
|
29
|
+
private schedulePersist;
|
|
30
|
+
/** Atomic write (temp + rename) so a concurrent reader never sees a partial
|
|
31
|
+
* file. Coalesces overlapping writes into one trailing flush. */
|
|
32
|
+
private persist;
|
|
33
|
+
}
|