@crouton-kit/crouter 0.3.14 → 0.3.16
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/build-root.d.ts +8 -0
- package/dist/build-root.js +30 -0
- package/dist/builtin-personas/design/base.md +3 -7
- package/dist/builtin-personas/design/orchestrator.md +4 -3
- package/dist/builtin-personas/developer/base.md +3 -7
- package/dist/builtin-personas/developer/orchestrator.md +5 -4
- package/dist/builtin-personas/explore/base.md +3 -7
- package/dist/builtin-personas/explore/orchestrator.md +1 -5
- package/dist/builtin-personas/general/base.md +2 -4
- package/dist/builtin-personas/general/orchestrator.md +2 -4
- package/dist/builtin-personas/lifecycle/resident.md +2 -0
- package/dist/builtin-personas/lifecycle/terminal.md +6 -0
- package/dist/builtin-personas/orchestration-kernel.md +42 -3
- package/dist/builtin-personas/plan/base.md +3 -5
- package/dist/builtin-personas/plan/orchestrator.md +5 -4
- package/dist/builtin-personas/plan/reviewers/architecture-fit/base.md +9 -0
- package/dist/builtin-personas/plan/reviewers/code-smells/base.md +9 -0
- package/dist/builtin-personas/plan/reviewers/pattern-consistency/base.md +9 -0
- package/dist/builtin-personas/plan/reviewers/requirements-coverage/base.md +9 -0
- package/dist/builtin-personas/plan/reviewers/security/base.md +9 -0
- package/dist/builtin-personas/review/base.md +3 -5
- package/dist/builtin-personas/review/orchestrator.md +2 -6
- package/dist/builtin-personas/runtime-base.md +3 -19
- package/dist/builtin-personas/spec/base.md +3 -5
- package/dist/builtin-personas/spec/orchestrator.md +4 -3
- package/dist/builtin-personas/spine/has-manager.md +10 -0
- package/dist/builtin-personas/spine/no-manager.md +2 -0
- package/dist/builtin-skills/skills/crouter-development/personas/SKILL.md +96 -0
- package/dist/builtin-skills/skills/crouter-development/personas/base-prompt/SKILL.md +49 -0
- package/dist/builtin-skills/skills/crouter-development/personas/orchestrator-prompt/SKILL.md +49 -0
- package/dist/builtin-skills/skills/planning/SKILL.md +1 -1
- package/dist/builtin-skills/skills/spec/SKILL.md +2 -2
- package/dist/cli.js +6 -29
- package/dist/commands/attention.js +76 -7
- package/dist/commands/canvas-prune.d.ts +2 -0
- package/dist/commands/canvas-prune.js +66 -0
- package/dist/commands/canvas.js +5 -8
- package/dist/commands/chord.d.ts +2 -0
- package/dist/commands/chord.js +143 -0
- package/dist/commands/daemon.js +8 -5
- package/dist/commands/dashboard.js +2 -0
- package/dist/commands/human/prompts.js +28 -27
- package/dist/commands/human/queue.js +30 -14
- package/dist/commands/human/shared.d.ts +26 -21
- package/dist/commands/human/shared.js +45 -67
- package/dist/commands/human.js +4 -14
- package/dist/commands/node.d.ts +11 -0
- package/dist/commands/node.js +221 -99
- package/dist/commands/pkg/market-inspect.js +6 -4
- package/dist/commands/pkg/market-manage.js +10 -6
- package/dist/commands/pkg/market.js +2 -4
- package/dist/commands/pkg/plugin-inspect.js +6 -4
- package/dist/commands/pkg/plugin-manage.js +12 -7
- package/dist/commands/pkg/plugin.js +2 -4
- package/dist/commands/pkg.js +0 -4
- package/dist/commands/push.js +178 -15
- package/dist/commands/revive.js +5 -3
- package/dist/commands/skill/author.js +6 -4
- package/dist/commands/skill/find.js +8 -5
- package/dist/commands/skill/read.js +2 -0
- package/dist/commands/skill/state.js +6 -4
- package/dist/commands/skill.js +0 -6
- package/dist/commands/sys/config.js +21 -7
- package/dist/commands/sys/doctor.js +2 -0
- package/dist/commands/sys/update.js +4 -0
- package/dist/commands/sys.js +0 -6
- package/dist/commands/tmux-spread.d.ts +2 -0
- package/dist/commands/tmux-spread.js +129 -0
- package/dist/core/__tests__/canvas-inbox-watcher.test.js +25 -0
- package/dist/core/__tests__/child-followup.test.d.ts +1 -0
- package/dist/core/__tests__/child-followup.test.js +83 -0
- package/dist/core/__tests__/close.test.d.ts +1 -0
- package/dist/core/__tests__/close.test.js +148 -0
- package/dist/core/__tests__/context-intro.test.d.ts +1 -0
- package/dist/core/__tests__/context-intro.test.js +196 -0
- package/dist/core/__tests__/daemon-boot.test.d.ts +1 -0
- package/dist/core/__tests__/daemon-boot.test.js +93 -0
- package/dist/core/__tests__/daemon-liveness.test.d.ts +1 -0
- package/dist/core/__tests__/daemon-liveness.test.js +223 -0
- package/dist/core/__tests__/focuses.test.d.ts +1 -0
- package/dist/core/__tests__/focuses.test.js +196 -0
- package/dist/core/__tests__/fork.test.d.ts +1 -0
- package/dist/core/__tests__/fork.test.js +91 -0
- package/dist/core/__tests__/home-session.test.d.ts +1 -0
- package/dist/core/__tests__/home-session.test.js +153 -0
- package/dist/core/__tests__/human-cancel-guard.test.d.ts +1 -0
- package/dist/core/__tests__/human-cancel-guard.test.js +49 -0
- package/dist/core/__tests__/keystone.test.d.ts +1 -0
- package/dist/core/__tests__/keystone.test.js +185 -0
- package/dist/core/__tests__/kickoff.test.d.ts +1 -0
- package/dist/core/__tests__/kickoff.test.js +89 -0
- package/dist/core/__tests__/lifecycle.test.d.ts +1 -0
- package/dist/core/__tests__/lifecycle.test.js +178 -0
- package/dist/core/__tests__/listing-completeness.test.d.ts +1 -0
- package/dist/core/__tests__/listing-completeness.test.js +31 -0
- package/dist/core/__tests__/memory.test.d.ts +1 -0
- package/dist/core/__tests__/memory.test.js +152 -0
- package/dist/core/__tests__/migration.test.d.ts +1 -0
- package/dist/core/__tests__/migration.test.js +238 -0
- package/dist/core/__tests__/pane-column.test.d.ts +1 -0
- package/dist/core/__tests__/pane-column.test.js +153 -0
- package/dist/core/__tests__/passive-subscription.test.js +24 -1
- package/dist/core/__tests__/persona-compose.test.d.ts +1 -0
- package/dist/core/__tests__/persona-compose.test.js +53 -0
- package/dist/core/__tests__/persona-subkind.test.d.ts +1 -0
- package/dist/core/__tests__/persona-subkind.test.js +62 -0
- package/dist/core/__tests__/persona.test.d.ts +1 -0
- package/dist/core/__tests__/persona.test.js +107 -0
- package/dist/core/__tests__/placement-focus.test.d.ts +1 -0
- package/dist/core/__tests__/placement-focus.test.js +266 -0
- package/dist/core/__tests__/placement-reconcile.test.d.ts +1 -0
- package/dist/core/__tests__/placement-reconcile.test.js +212 -0
- package/dist/core/__tests__/placement-revive.test.d.ts +1 -0
- package/dist/core/__tests__/placement-revive.test.js +238 -0
- package/dist/core/__tests__/placement-teardown.test.d.ts +1 -0
- package/dist/core/__tests__/placement-teardown.test.js +178 -0
- package/dist/core/__tests__/prune.test.d.ts +1 -0
- package/dist/core/__tests__/prune.test.js +116 -0
- package/dist/core/__tests__/push-final-guard.test.d.ts +1 -0
- package/dist/core/__tests__/push-final-guard.test.js +71 -0
- package/dist/core/__tests__/relaunch.test.d.ts +1 -0
- package/dist/core/__tests__/relaunch.test.js +334 -0
- package/dist/core/__tests__/reset.test.js +26 -7
- package/dist/core/__tests__/revive.test.d.ts +1 -0
- package/dist/core/__tests__/revive.test.js +217 -0
- package/dist/core/__tests__/spawn-root.test.d.ts +1 -0
- package/dist/core/__tests__/spawn-root.test.js +73 -0
- package/dist/core/__tests__/steer-note.test.d.ts +1 -0
- package/dist/core/__tests__/steer-note.test.js +39 -0
- package/dist/core/__tests__/stop-guard.test.d.ts +1 -0
- package/dist/core/__tests__/stop-guard.test.js +82 -0
- package/dist/core/__tests__/subcommand-tier.test.js +35 -33
- package/dist/core/__tests__/tmux-surface.test.d.ts +1 -0
- package/dist/core/__tests__/tmux-surface.test.js +105 -0
- package/dist/core/__tests__/unknown-path.test.js +8 -2
- package/dist/core/canvas/attention.d.ts +10 -0
- package/dist/core/canvas/attention.js +40 -0
- package/dist/core/canvas/canvas.d.ts +66 -7
- package/dist/core/canvas/canvas.js +209 -21
- package/dist/core/canvas/db.d.ts +8 -0
- package/dist/core/canvas/db.js +205 -4
- package/dist/core/canvas/focuses.d.ts +22 -0
- package/dist/core/canvas/focuses.js +81 -0
- package/dist/core/canvas/index.d.ts +3 -0
- package/dist/core/canvas/index.js +3 -0
- package/dist/core/canvas/labels.d.ts +27 -0
- package/dist/core/canvas/labels.js +36 -0
- package/dist/core/canvas/render.js +25 -10
- package/dist/core/canvas/telemetry.d.ts +14 -0
- package/dist/core/canvas/telemetry.js +35 -0
- package/dist/core/canvas/types.d.ts +115 -12
- package/dist/core/command.d.ts +25 -1
- package/dist/core/command.js +23 -15
- package/dist/core/config.js +36 -2
- package/dist/core/feed/feed.js +3 -3
- package/dist/core/feed/inbox.d.ts +3 -1
- package/dist/core/feed/inbox.js +45 -5
- package/dist/core/feed/passive.js +24 -11
- package/dist/core/help.d.ts +26 -13
- package/dist/core/help.js +44 -37
- package/dist/core/personas/index.d.ts +1 -1
- package/dist/core/personas/index.js +1 -1
- package/dist/core/personas/loader.d.ts +40 -1
- package/dist/core/personas/loader.js +63 -1
- package/dist/core/personas/resolve.d.ts +13 -6
- package/dist/core/personas/resolve.js +46 -34
- package/dist/core/runtime/bearings.d.ts +20 -0
- package/dist/core/runtime/bearings.js +92 -0
- package/dist/core/runtime/close.d.ts +14 -0
- package/dist/core/runtime/close.js +151 -0
- package/dist/core/runtime/demote.js +24 -12
- package/dist/core/runtime/front-door.js +1 -1
- package/dist/core/runtime/kickoff.d.ts +23 -6
- package/dist/core/runtime/kickoff.js +92 -36
- package/dist/core/runtime/launch.d.ts +26 -12
- package/dist/core/runtime/launch.js +78 -19
- package/dist/core/runtime/lifecycle.d.ts +13 -0
- package/dist/core/runtime/lifecycle.js +86 -0
- package/dist/core/runtime/memory.d.ts +43 -0
- package/dist/core/runtime/memory.js +165 -0
- package/dist/core/runtime/naming.d.ts +22 -0
- package/dist/core/runtime/naming.js +166 -0
- package/dist/core/runtime/nodes.d.ts +39 -1
- package/dist/core/runtime/nodes.js +69 -10
- package/dist/core/runtime/persona.d.ts +25 -0
- package/dist/core/runtime/persona.js +139 -0
- package/dist/core/runtime/placement.d.ts +299 -0
- package/dist/core/runtime/placement.js +688 -0
- package/dist/core/runtime/promote.d.ts +14 -7
- package/dist/core/runtime/promote.js +57 -67
- package/dist/core/runtime/reset.d.ts +47 -4
- package/dist/core/runtime/reset.js +223 -52
- package/dist/core/runtime/revive.d.ts +26 -2
- package/dist/core/runtime/revive.js +166 -39
- package/dist/core/runtime/spawn.d.ts +20 -5
- package/dist/core/runtime/spawn.js +163 -43
- package/dist/core/runtime/stop-guard.d.ts +1 -1
- package/dist/core/runtime/stop-guard.js +18 -8
- package/dist/core/runtime/tmux-chrome.d.ts +1 -0
- package/dist/core/runtime/tmux-chrome.js +4 -0
- package/dist/core/runtime/tmux.d.ts +113 -20
- package/dist/core/runtime/tmux.js +221 -39
- package/dist/core/spawn.js +15 -0
- package/dist/daemon/crtrd.d.ts +12 -1
- package/dist/daemon/crtrd.js +152 -34
- package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.d.ts +1 -0
- package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +266 -0
- package/dist/pi-extensions/canvas-commands.js +16 -13
- package/dist/pi-extensions/canvas-context-intro.d.ts +70 -0
- package/dist/pi-extensions/canvas-context-intro.js +164 -0
- package/dist/pi-extensions/canvas-goal-capture.d.ts +3 -0
- package/dist/pi-extensions/canvas-goal-capture.js +15 -1
- package/dist/pi-extensions/canvas-inbox-watcher.js +11 -0
- package/dist/pi-extensions/canvas-nav.d.ts +12 -4
- package/dist/pi-extensions/canvas-nav.js +594 -262
- package/dist/pi-extensions/canvas-resume.d.ts +22 -0
- package/dist/pi-extensions/canvas-resume.js +173 -0
- package/dist/pi-extensions/canvas-stophook.d.ts +16 -0
- package/dist/pi-extensions/canvas-stophook.js +340 -228
- package/dist/types.d.ts +28 -0
- package/dist/types.js +16 -0
- package/package.json +2 -2
- package/dist/core/runtime/presence.d.ts +0 -38
- package/dist/core/runtime/presence.js +0 -154
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import { type NodeRow, type FocusRow } from '../canvas/index.js';
|
|
2
|
+
import { homeSessionOf } from './nodes.js';
|
|
3
|
+
export { homeSessionOf };
|
|
4
|
+
export type { FocusRow };
|
|
5
|
+
export { piCommand, paneLocation, currentTmux, inTmux, ensureSession, openNodeWindow, focusWindow, windowAlive, windowOfPane, respawnPane, } from './tmux.js';
|
|
6
|
+
export type { RespawnPaneOpts } from './tmux.js';
|
|
7
|
+
export { nodeSession } from './nodes.js';
|
|
8
|
+
/** The focus a node occupies, or null. UNIQUE(node_id) ⇒ at most one. */
|
|
9
|
+
export declare function focusOf(nodeId: string): FocusRow | null;
|
|
10
|
+
/** Is this node on a viewport? */
|
|
11
|
+
export declare function isFocused(nodeId: string): boolean;
|
|
12
|
+
/** The focus realized by a given pane (`%id`), or null. */
|
|
13
|
+
export declare function focusByPane(pane: string): FocusRow | null;
|
|
14
|
+
/** The set of node ids currently on some focus. */
|
|
15
|
+
export declare function focusedNodes(): Set<string>;
|
|
16
|
+
/** Every focus row (every live viewport). */
|
|
17
|
+
export declare function listFocuses(): FocusRow[];
|
|
18
|
+
/** The cached LOCATION as stored on a node row: the authoritative `pane` handle
|
|
19
|
+
* plus its derived window/session cache. */
|
|
20
|
+
export interface CachedLocation {
|
|
21
|
+
pane: string | null;
|
|
22
|
+
tmux_session: string | null;
|
|
23
|
+
window: string | null;
|
|
24
|
+
}
|
|
25
|
+
/** What `reconcile` resolved from tmux for the cached location. The shell does
|
|
26
|
+
* the two driver reads; the pure decision interprets them.
|
|
27
|
+
* - `paneLoc`: `paneLocation(cached.pane)` — the pane's CURRENT session/window,
|
|
28
|
+
* or null when the pane is gone. Only meaningful when `cached.pane != null`.
|
|
29
|
+
* - `windowPane`: `paneOfWindow(cached.tmux_session, cached.window)` — the live
|
|
30
|
+
* window's active pane, for the legacy backfill case (`cached.pane == null`). */
|
|
31
|
+
export interface LiveProbe {
|
|
32
|
+
paneLoc: {
|
|
33
|
+
session: string;
|
|
34
|
+
window: string;
|
|
35
|
+
} | null;
|
|
36
|
+
windowPane: string | null;
|
|
37
|
+
}
|
|
38
|
+
/** The presence patch `reconcile` should write, or `{ kind: 'none' }` for a no-op.
|
|
39
|
+
* - `none` — the cache already matches reality (or there's nothing to do).
|
|
40
|
+
* - `gone` — the durable pane is gone → null the whole LOCATION.
|
|
41
|
+
* - `follow` — the pane moved (user move) → re-point the cache at its new
|
|
42
|
+
* window/session, keeping the same pane id.
|
|
43
|
+
* - `backfill` — a legacy row had no pane but a live window → adopt the
|
|
44
|
+
* window's active pane as the durable handle (begins populating
|
|
45
|
+
* `pane` for pre-existing nodes). */
|
|
46
|
+
export type ReconcileDecision = {
|
|
47
|
+
kind: 'none';
|
|
48
|
+
} | {
|
|
49
|
+
kind: 'gone';
|
|
50
|
+
} | {
|
|
51
|
+
kind: 'follow';
|
|
52
|
+
pane: string;
|
|
53
|
+
tmux_session: string;
|
|
54
|
+
window: string;
|
|
55
|
+
} | {
|
|
56
|
+
kind: 'backfill';
|
|
57
|
+
pane: string;
|
|
58
|
+
tmux_session: string;
|
|
59
|
+
window: string;
|
|
60
|
+
};
|
|
61
|
+
/** PURE reconciliation decision (§2.4) — unit-testable without a live tmux.
|
|
62
|
+
* Given the cached row LOCATION and what tmux currently reports, decide the
|
|
63
|
+
* presence patch. Mirrors the pure-core/impure-shell split (cf. `livenessVerdict`
|
|
64
|
+
* vs `handleLiveWindow`): this is the decision, `reconcile` wires it to the
|
|
65
|
+
* driver reads + `setPresence`. */
|
|
66
|
+
export declare function reconcileDecision(cached: CachedLocation, live: LiveProbe): ReconcileDecision;
|
|
67
|
+
/** Reconcile a node's LOCATION against tmux reality (§2.4) — the impure shell.
|
|
68
|
+
* Reads `row.pane`, resolves its CURRENT session/window via the driver, and
|
|
69
|
+
* writes the resulting presence patch through `setPresence` (never a raw UPDATE):
|
|
70
|
+
* - pane moved → FOLLOW (re-point window/session, keep the pane id)
|
|
71
|
+
* - pane gone → null the whole LOCATION
|
|
72
|
+
* - legacy/no pane + live window → backfill the pane from `paneOfWindow`
|
|
73
|
+
* A no-op when there's nothing to resolve (genuinely no pane, or the cache is
|
|
74
|
+
* already current). Call this before any swap/kill/focus/revive so the act lands
|
|
75
|
+
* on the pane's current window, never a stale one. */
|
|
76
|
+
export declare function reconcile(nodeId: string): void;
|
|
77
|
+
/** Reconcile a FOCUS's derived `session` cache against tmux reality (§2.4, Q4) —
|
|
78
|
+
* the focus-row analogue of `reconcile`. A focus is anchored on its durable
|
|
79
|
+
* `%pane_id`; `session` is a derived cache. If the user moved the focus pane to
|
|
80
|
+
* another session, re-point the cache so a resume-into-focus lands in the pane's
|
|
81
|
+
* CURRENT session. A no-op when the focus has no pane, the cache is already
|
|
82
|
+
* current, or the pane is GONE — in the gone case reconcileFocus does NOT null
|
|
83
|
+
* the row; the caller (reviveIntoPlacement) instead falls to the backstage
|
|
84
|
+
* branch via `paneExists(pane)` being false. */
|
|
85
|
+
export declare function reconcileFocus(focusId: string): void;
|
|
86
|
+
/** Is this node's pane (its LOCATION) alive? The v3 PRIMARY liveness probe,
|
|
87
|
+
* PURE / non-mutating so the daemon can gate on it without side effects:
|
|
88
|
+
* - `pane != null` → `paneExists(pane)` (display-message on the `%id`), so a
|
|
89
|
+
* user moving the pane to another window/session never reads as "gone".
|
|
90
|
+
* - `pane == null` → window-keyed FALLBACK (`windowAlive`) for legacy/no-pane
|
|
91
|
+
* rows that haven't been backfilled yet.
|
|
92
|
+
* Accepts a node id (re-reads the row) or a `NodeRow` already in hand. */
|
|
93
|
+
export declare function isNodePaneAlive(node: string | NodeRow): boolean;
|
|
94
|
+
/** The launch recipe `reviveIntoPlacement` plays into a pane. `command` is the
|
|
95
|
+
* full shell string (`piCommand(argv)`); `env`/`cwd`/`name` describe the
|
|
96
|
+
* window/pane; `resuming` is carried through for the caller's ReviveResult. */
|
|
97
|
+
export interface ReviveLaunch {
|
|
98
|
+
command: string;
|
|
99
|
+
env: Record<string, string>;
|
|
100
|
+
cwd: string;
|
|
101
|
+
name: string;
|
|
102
|
+
resuming: boolean;
|
|
103
|
+
}
|
|
104
|
+
/** Where a revive physically landed: the new/derived window, the session it ran
|
|
105
|
+
* in, and the durable pane id. */
|
|
106
|
+
export interface PlacementResult {
|
|
107
|
+
window: string | null;
|
|
108
|
+
session: string;
|
|
109
|
+
pane: string | null;
|
|
110
|
+
}
|
|
111
|
+
/** The PURE revive-target decision (§1.4/§5.1) — THE assertion that the
|
|
112
|
+
* "unbidden windows" bug is structurally dead. Given a node's focus (or null),
|
|
113
|
+
* whether that focus's pane is still alive, and the node's durable REVIVE-HOME
|
|
114
|
+
* (`home_session`), decide WHERE a revive must land:
|
|
115
|
+
* - occupies a LIVE focus → resume IN PLACE in that focus pane (no new window).
|
|
116
|
+
* - otherwise → a new window in `homeSession`, and NOTHING ELSE.
|
|
117
|
+
*
|
|
118
|
+
* The backstage branch's session is `homeSession` ONLY — never
|
|
119
|
+
* `meta.tmux_session`, the field focus taints to a user session. For a
|
|
120
|
+
* post-Step-1 child `homeSession` is the backstage `crtr` (never a user
|
|
121
|
+
* session), so a non-focused child — INCLUDING a once-focused-now-unfocused
|
|
122
|
+
* child whose `tmux_session` was tainted — can NEVER revive into a user session.
|
|
123
|
+
* A root's `homeSession` is its own session, so reviving a root into its own
|
|
124
|
+
* session is correct, not the bug. */
|
|
125
|
+
export type ReviveTargetDecision = {
|
|
126
|
+
kind: 'focus-pane';
|
|
127
|
+
pane: string;
|
|
128
|
+
session: string;
|
|
129
|
+
} | {
|
|
130
|
+
kind: 'backstage';
|
|
131
|
+
session: string;
|
|
132
|
+
};
|
|
133
|
+
export declare function reviveTarget(focus: FocusRow | null, focusPaneAlive: boolean, homeSession: string): ReviveTargetDecision;
|
|
134
|
+
/** Place a reviving node into its CORRECT location (§1.4) — the single decision
|
|
135
|
+
* that replaces revive.ts's old `session = meta.tmux_session ?? nodeSession()` +
|
|
136
|
+
* `openNodeWindow`. Reconcile first (§2.4), then dispatch on `reviveTarget`:
|
|
137
|
+
* - the node occupies a LIVE focus → `reconcileFocus` (resolve the pane's
|
|
138
|
+
* CURRENT session, Q4) and `respawn-pane -k` the pi INTO that focus pane —
|
|
139
|
+
* no new window (F3 resume-in-place).
|
|
140
|
+
* - otherwise → the node is NOT focused (or its focus pane already collapsed,
|
|
141
|
+
* the Step-5 limitation: remain-on-exit lands in Step 6), so it may ONLY
|
|
142
|
+
* (re)appear in its durable REVIVE-HOME: a fresh window in `homeSession`.
|
|
143
|
+
* **There is NO code path here by which a non-focused node's new-window
|
|
144
|
+
* targets a user session** — `openNodeWindow`'s session is `homeSession` and
|
|
145
|
+
* nothing else. That is the structural bug-kill.
|
|
146
|
+
*
|
|
147
|
+
* `setPresence` (the one atomic LOCATION write) records where the node landed.
|
|
148
|
+
* CRTR_ROOT_SESSION is forced to `homeSession` in BOTH branches so the node's
|
|
149
|
+
* children always flow to the backstage, never into the focus session. */
|
|
150
|
+
export declare function reviveIntoPlacement(nodeId: string, launch: ReviveLaunch): PlacementResult;
|
|
151
|
+
/** Relocate a node's still-running agent to the background `crtr` session,
|
|
152
|
+
* freeing the foreground pane WITHOUT killing the pi. `break-pane` moves the
|
|
153
|
+
* pane out of the foreground window into a fresh window in the shared backstage
|
|
154
|
+
* (the pi keeps generating); the node becomes a background window — switchable
|
|
155
|
+
* but not rendered, like any other node. Reconcile first (act on the pane's
|
|
156
|
+
* CURRENT location, §2.4) and again after (presence FOLLOWS the move). No-op
|
|
157
|
+
* (false) when there is no live pane to relocate or tmux refuses the break.
|
|
158
|
+
* `pane` is the authoritative node pane the caller acts on (the Alt+C menu's
|
|
159
|
+
* `#{pane_id}`); falls back to the node's durable handle. */
|
|
160
|
+
export declare function detachToBackground(nodeId: string, pane?: string): boolean;
|
|
161
|
+
/** A reviver: resume a DORMANT node into its backstage placement (a fresh `crtr`
|
|
162
|
+
* window via reviveIntoPlacement). Injected so placement.ts need not import
|
|
163
|
+
* revive.ts (which imports placement.ts — a cycle). The node's landed pane is
|
|
164
|
+
* read back from its row afterwards. */
|
|
165
|
+
export type Reviver = (nodeId: string) => void;
|
|
166
|
+
/** Result of a focus/retarget op. */
|
|
167
|
+
export interface FocusResult {
|
|
168
|
+
focused: boolean;
|
|
169
|
+
session: string | null;
|
|
170
|
+
inPlace: boolean;
|
|
171
|
+
revived: boolean;
|
|
172
|
+
}
|
|
173
|
+
/** PURE disposition of a focus's outgoing occupant after a retarget swap (§2.5/
|
|
174
|
+
* §1.3): a still-generating node moves to backstage (F2); a holder pane or a
|
|
175
|
+
* done/dormant node has its (now-backstage) pane reaped (Invariant P: a
|
|
176
|
+
* not-focused + not-generating node has NO pane). Unit-testable in isolation. */
|
|
177
|
+
export type OutgoingAction = {
|
|
178
|
+
kind: 'backstage';
|
|
179
|
+
} | {
|
|
180
|
+
kind: 'kill';
|
|
181
|
+
};
|
|
182
|
+
export declare function outgoingDisposition(o: {
|
|
183
|
+
exists: boolean;
|
|
184
|
+
generating: boolean;
|
|
185
|
+
}): OutgoingAction;
|
|
186
|
+
/** Open a NEW viewport (§2.3, F4) — the ONLY path a new pane appears in a user
|
|
187
|
+
* session. Default: `splitWindow(callerPane)` beside (Q3); `newWindow` opens a
|
|
188
|
+
* fresh window in the caller pane's session instead. Arms `remain-on-exit` on
|
|
189
|
+
* the new pane's window (F3) and inserts a focuses row anchored on it, occupied
|
|
190
|
+
* by a HOLDER until retargetFocus swaps a real node in. A benign long-sleep
|
|
191
|
+
* holds the pane open until the swap; retargetFocus reaps it. Returns the row,
|
|
192
|
+
* or null if tmux failed. */
|
|
193
|
+
export declare function openFocus(callerPane: string, opts?: {
|
|
194
|
+
newWindow?: boolean;
|
|
195
|
+
}): FocusRow | null;
|
|
196
|
+
/** Register the FOREGROUND root's pane as focus #1 at boot (§2.6). The inline
|
|
197
|
+
* root owns the user's viewport, so its own pane becomes a durable focus — with
|
|
198
|
+
* `remain-on-exit` so a clean exit FREEZES the pane rather than detaching the
|
|
199
|
+
* terminal (F1). A background `--root` does NOT call this (§6): it stays a plain
|
|
200
|
+
* window until the user `node focus`es it. No-op when the pane or this node is
|
|
201
|
+
* already a focus. The focus row IS the record — no pointer to mirror. */
|
|
202
|
+
export declare function registerRootFocus(nodeId: string, pane: string, session: string | null, window: string | null): FocusRow | null;
|
|
203
|
+
/** retargetFocus — the unified hot-swap (§2.5, Invariant P + Q5). Swap `incoming`
|
|
204
|
+
* onto focus `focusId`'s viewport, keeping the screen position invariant (no new
|
|
205
|
+
* window). One sqlite txn updates the focus row + BOTH nodes' presence:
|
|
206
|
+
* - Q5: if `incoming` already occupies ANOTHER focus, VACATE it first (close
|
|
207
|
+
* its row + kill its pane — the node MOVES here, no auto-retarget).
|
|
208
|
+
* - resolve `incoming`'s live pin pane (a backstage pane), else `revive` it
|
|
209
|
+
* into the backstage and read back its pane.
|
|
210
|
+
* - `swapPaneInPlace(pin, focusPane)`: incoming → the viewport slot; the
|
|
211
|
+
* outgoing occupant → incoming's old (backstage) slot, %ids preserved
|
|
212
|
+
* (cross-session swap confirmed by the spike).
|
|
213
|
+
* - outgoing still generating → backstage (F2); else reap its now-backstage
|
|
214
|
+
* pane (Invariant P). A holder occupant (no node row) is always reaped.
|
|
215
|
+
* Arms remain-on-exit on the viewport (F3); the focus row is the record. */
|
|
216
|
+
export declare function retargetFocus(focusId: string, incoming: string, revive: Reviver): FocusResult;
|
|
217
|
+
/** The front door for `node focus` / `node cycle` (§2.3): resolve which focus the
|
|
218
|
+
* caller's pane acts on, then retarget `nodeId` onto it.
|
|
219
|
+
* - `newPane` → `openFocus` a fresh viewport beside the caller (F4), then
|
|
220
|
+
* retarget into it.
|
|
221
|
+
* - else → retarget the caller pane's focus IN PLACE (`focusByPane`); if the
|
|
222
|
+
* caller's pane is not yet a viewport, adopt it as one (occupied by whatever
|
|
223
|
+
* node sits there now — `callerNode`, else resolved by pane).
|
|
224
|
+
* - no caller pane (not in tmux) → best-effort: reconcile + report status,
|
|
225
|
+
* not-in-place (no viewport to swap into). */
|
|
226
|
+
export declare function focus(nodeId: string, opts: {
|
|
227
|
+
pane?: string;
|
|
228
|
+
newPane?: boolean;
|
|
229
|
+
callerNode?: string;
|
|
230
|
+
revive: Reviver;
|
|
231
|
+
}): FocusResult;
|
|
232
|
+
/** Tear a node off its placement (close/reset teardown, §2.3, flow (e)).
|
|
233
|
+
* Reconcile first (follow a manual move / backfill a legacy pane), close the
|
|
234
|
+
* focus row it occupies (if any), kill its pane (pane-keyed via the durable
|
|
235
|
+
* `%id` — the window collapses once its last pane goes), and null its LOCATION.
|
|
236
|
+
* The focus row close is the record. Best-effort tmux; the
|
|
237
|
+
* DB writes always land. The pane kill is the sole teardown unit (Q1/§6: a
|
|
238
|
+
* split-pane focus returns its space to the surviving split; a standalone-window
|
|
239
|
+
* focus closes the window). */
|
|
240
|
+
export declare function tearDownNode(nodeId: string): void;
|
|
241
|
+
/** Demote's in-pane relaunch (§2.3, flow (e)): respawn `nodeId`'s launch into an
|
|
242
|
+
* EXISTING `pane`, keeping the durable `%id` (respawn-pane -k), and record its
|
|
243
|
+
* presence keyed on that pane. The session/window are DERIVED from the pane
|
|
244
|
+
* itself (paneLocation), so the recycled node's LOCATION follows the pane it was
|
|
245
|
+
* recycled into. `launch.env` is passed through verbatim — the caller (demote)
|
|
246
|
+
* already sets CRTR_ROOT_SESSION (children → backstage) + FRONT_DOOR. Detached
|
|
247
|
+
* respawn, since the pane is often the caller's own. Returns whether the respawn
|
|
248
|
+
* dispatched. */
|
|
249
|
+
export declare function recycleFocusPane(nodeId: string, pane: string, launch: ReviveLaunch): boolean;
|
|
250
|
+
/** §1.6 lifecycle successor — hand a truly-done focused node's viewport to its
|
|
251
|
+
* manager. Repoints the focus row `focusId` to `managerId` (a DB swap of the
|
|
252
|
+
* occupant). Two takeover realizations, split on the manager's liveness:
|
|
253
|
+
* - DORMANT manager (dead pi): the row repoint is all this does; the manager,
|
|
254
|
+
* woken by the finished node's `push final` landing in its inbox, is revived
|
|
255
|
+
* by the external daemon INTO this node's now-frozen focus pane
|
|
256
|
+
* (remain-on-exit), where reviveIntoPlacement's focus-pane branch resumes it
|
|
257
|
+
* in place — no new window, no taint. (UNCHANGED — the canonical takeover.)
|
|
258
|
+
* - LIVE manager (pi alive in the backstage, the normal multi-child state):
|
|
259
|
+
* the daemon never revives it (it only respawns dead-pi nodes), so we must
|
|
260
|
+
* bring it into the viewport SYNCHRONOUSLY here — swap its backstage pane
|
|
261
|
+
* into the focus slot (MAJOR 1). Otherwise the manager runs off-screen
|
|
262
|
+
* forever while %m sits orphaned in the viewport and the focus row lies
|
|
263
|
+
* about LOCATION.
|
|
264
|
+
* Returns false — the caller closes the focus (Q1) — when there is no manager,
|
|
265
|
+
* the manager IS this node, or the manager already occupies another viewport
|
|
266
|
+
* (UNIQUE node_id: do NOT move it, §1.6 edge).
|
|
267
|
+
*
|
|
268
|
+
* Why the live swap is NOT the forbidden self-saw: `swap-pane -d` only EXCHANGES
|
|
269
|
+
* two panes' slot positions; it never respawns or kills the finishing node's own
|
|
270
|
+
* pi. The forbidden move is a synchronous `respawn-pane -k %m` from inside %m —
|
|
271
|
+
* we never do that here. After the swap, %m (the dying node's pane) sits in the
|
|
272
|
+
* manager's old backstage slot; the caller nulls this node's presence so nothing
|
|
273
|
+
* tracks the corpse. */
|
|
274
|
+
export declare function handFocusToManager(focusId: string, managerId: string | null): boolean;
|
|
275
|
+
/** Q1 close-to-shell for a truly-done focused node with no successor (§1.6 /
|
|
276
|
+
* flow (b)): close its focus row and DISARM the pane's
|
|
277
|
+
* freeze (`remain-on-exit` off) so it reaps when the finishing pi exits. The
|
|
278
|
+
* stophook calls this instead of `tearDownNode` because it runs INSIDE the pane
|
|
279
|
+
* it is closing: it cannot `closePane` its own pane (self-saw), but it is still
|
|
280
|
+
* alive to disarm the freeze, so the pane closes on exit (return-to-shell)
|
|
281
|
+
* rather than freezing into an orphan. (Keeps the stophook off the tmux driver,
|
|
282
|
+
* §2.1.) */
|
|
283
|
+
export declare function closeFocusToShell(focusId: string, nodeId: string): void;
|
|
284
|
+
export interface SpreadResult {
|
|
285
|
+
window: string | null;
|
|
286
|
+
session: string | null;
|
|
287
|
+
/** Child node ids whose panes were joined into the target window. */
|
|
288
|
+
joined: string[];
|
|
289
|
+
focused: boolean;
|
|
290
|
+
}
|
|
291
|
+
/** Join each of `childIds`' live panes into `targetId`'s window, lay them out
|
|
292
|
+
* (target wide on the left, children stacked right), and focus it. Reconcile
|
|
293
|
+
* drives both the target resolution and the per-join fix-up (a joined pane keeps
|
|
294
|
+
* its `%id` but changes window, so its LOCATION must FOLLOW — else the daemon
|
|
295
|
+
* reads it dormant). Caller revives dormant nodes first so they have live panes.
|
|
296
|
+
* No-op result when the target has no live pane. */
|
|
297
|
+
export declare function spreadNode(targetId: string, childIds: string[], opts?: {
|
|
298
|
+
mainPaneWidth?: string;
|
|
299
|
+
}): SpreadResult;
|