@songsid/agend 2.1.2-beta.2 → 2.1.2-beta.21
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/access-path.js +15 -6
- package/dist/access-path.js.map +1 -1
- package/dist/agent-endpoint.d.ts +2 -0
- package/dist/agent-endpoint.js +17 -1
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/backend/antigravity.d.ts +18 -0
- package/dist/backend/antigravity.js +32 -3
- package/dist/backend/antigravity.js.map +1 -1
- package/dist/backend/claude-code.d.ts +37 -0
- package/dist/backend/claude-code.js +43 -1
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/codex.d.ts +1 -0
- package/dist/backend/codex.js +25 -0
- package/dist/backend/codex.js.map +1 -1
- package/dist/backend/grok.d.ts +23 -0
- package/dist/backend/grok.js +45 -0
- package/dist/backend/grok.js.map +1 -1
- package/dist/backend/kiro.d.ts +20 -0
- package/dist/backend/kiro.js +77 -2
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/types.d.ts +34 -0
- package/dist/backend/types.js +1 -1
- package/dist/backend/types.js.map +1 -1
- package/dist/channel/adapters/discord.d.ts +15 -0
- package/dist/channel/adapters/discord.js +91 -2
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.d.ts +21 -0
- package/dist/channel/adapters/telegram.js +82 -0
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/ipc-bridge.d.ts +17 -1
- package/dist/channel/ipc-bridge.js +53 -17
- package/dist/channel/ipc-bridge.js.map +1 -1
- package/dist/channel/ipc-timeouts.d.ts +46 -0
- package/dist/channel/ipc-timeouts.js +65 -0
- package/dist/channel/ipc-timeouts.js.map +1 -0
- package/dist/channel/mcp-server.js +30 -14
- package/dist/channel/mcp-server.js.map +1 -1
- package/dist/channel/mcp-tools.js +28 -1
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/channel/reconnect-backoff.d.ts +17 -0
- package/dist/channel/reconnect-backoff.js +21 -0
- package/dist/channel/reconnect-backoff.js.map +1 -0
- package/dist/channel/types.d.ts +29 -0
- package/dist/classic-channel-manager.js +1 -5
- package/dist/classic-channel-manager.js.map +1 -1
- package/dist/cli.js +132 -52
- package/dist/cli.js.map +1 -1
- package/dist/completion.d.ts +27 -0
- package/dist/completion.js +121 -0
- package/dist/completion.js.map +1 -0
- package/dist/config-validator.js +9 -0
- package/dist/config-validator.js.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/cost-guard.d.ts +3 -1
- package/dist/cost-guard.js +3 -1
- package/dist/cost-guard.js.map +1 -1
- package/dist/daemon.d.ts +219 -13
- package/dist/daemon.js +919 -303
- package/dist/daemon.js.map +1 -1
- package/dist/event-log.d.ts +31 -0
- package/dist/event-log.js +96 -0
- package/dist/event-log.js.map +1 -1
- package/dist/fleet-manager.d.ts +238 -2
- package/dist/fleet-manager.js +1008 -90
- package/dist/fleet-manager.js.map +1 -1
- package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +22 -0
- package/dist/general-knowledge/skills/model-discovery/SKILL.md +14 -23
- package/dist/general-knowledge/skills/session-management/SKILL.md +15 -34
- package/dist/hang-detector.d.ts +19 -13
- package/dist/hang-detector.js +19 -49
- package/dist/hang-detector.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +13 -1
- package/dist/instance-lifecycle.js +84 -3
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/instructions.d.ts +5 -0
- package/dist/instructions.js +9 -11
- package/dist/instructions.js.map +1 -1
- package/dist/locale.js +2 -1
- package/dist/locale.js.map +1 -1
- package/dist/logger.js +14 -0
- package/dist/logger.js.map +1 -1
- package/dist/mcp-liveness.d.ts +21 -0
- package/dist/mcp-liveness.js +27 -0
- package/dist/mcp-liveness.js.map +1 -0
- package/dist/outbound-handlers.d.ts +16 -0
- package/dist/outbound-handlers.js +121 -27
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +8 -3
- package/dist/outbound-schemas.js +13 -2
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/pane-write-lock.d.ts +48 -0
- package/dist/pane-write-lock.js +73 -0
- package/dist/pane-write-lock.js.map +1 -0
- package/dist/process-memory.d.ts +31 -0
- package/dist/process-memory.js +79 -0
- package/dist/process-memory.js.map +1 -0
- package/dist/quickstart.js +17 -16
- package/dist/quickstart.js.map +1 -1
- package/dist/reply-dedup.d.ts +41 -0
- package/dist/reply-dedup.js +0 -0
- package/dist/reply-dedup.js.map +1 -0
- package/dist/scheduler/db.js +3 -0
- package/dist/scheduler/db.js.map +1 -1
- package/dist/sd-notify.d.ts +27 -0
- package/dist/sd-notify.js +33 -1
- package/dist/sd-notify.js.map +1 -1
- package/dist/secret-file.d.ts +33 -0
- package/dist/secret-file.js +36 -0
- package/dist/secret-file.js.map +1 -0
- package/dist/setup-wizard.js +9 -7
- package/dist/setup-wizard.js.map +1 -1
- package/dist/tmux-control.d.ts +58 -5
- package/dist/tmux-control.js +102 -14
- package/dist/tmux-control.js.map +1 -1
- package/dist/tmux-manager.d.ts +31 -1
- package/dist/tmux-manager.js +40 -11
- package/dist/tmux-manager.js.map +1 -1
- package/dist/topic-commands.d.ts +50 -10
- package/dist/topic-commands.js +242 -106
- package/dist/topic-commands.js.map +1 -1
- package/dist/tui-glyphs.d.ts +25 -0
- package/dist/tui-glyphs.js +26 -0
- package/dist/tui-glyphs.js.map +1 -0
- package/dist/types.d.ts +6 -0
- package/dist/ui/view.html +123 -2
- package/dist/usage/format-rich.d.ts +11 -0
- package/dist/usage/format-rich.js +108 -0
- package/dist/usage/format-rich.js.map +1 -0
- package/dist/usage/providers.d.ts +97 -0
- package/dist/usage/providers.js +834 -0
- package/dist/usage/providers.js.map +1 -0
- package/dist/usage/usage-api.d.ts +50 -0
- package/dist/usage/usage-api.js +111 -0
- package/dist/usage/usage-api.js.map +1 -0
- package/dist/view-api.d.ts +3 -0
- package/dist/view-api.js +11 -2
- package/dist/view-api.js.map +1 -1
- package/dist/web-api.js +5 -2
- package/dist/web-api.js.map +1 -1
- package/package.json +4 -1
- package/dist/channel/tool-tracker.d.ts +0 -13
- package/dist/channel/tool-tracker.js +0 -58
- package/dist/channel/tool-tracker.js.map +0 -1
- package/dist/daemon-entry.d.ts +0 -1
- package/dist/daemon-entry.js +0 -30
- package/dist/daemon-entry.js.map +0 -1
package/dist/daemon.d.ts
CHANGED
|
@@ -1,21 +1,65 @@
|
|
|
1
1
|
import { EventEmitter } from "node:events";
|
|
2
2
|
import type { InstanceConfig, RotationSnapshot } from "./types.js";
|
|
3
|
-
import type
|
|
3
|
+
import { type Logger } from "./logger.js";
|
|
4
4
|
import { MessageBus } from "./channel/message-bus.js";
|
|
5
5
|
import type { CliBackend, InstanceState, InstanceStateSnapshot } from "./backend/types.js";
|
|
6
6
|
import { HangDetector } from "./hang-detector.js";
|
|
7
7
|
import type { TmuxControlClient } from "./tmux-control.js";
|
|
8
|
+
import type { FleetInstructionsParams } from "./instructions.js";
|
|
8
9
|
/** Point a resumed CLI at its one backend-native instruction source. */
|
|
9
10
|
export declare function buildInstructionReloadNotice(binaryName: string, instanceName: string, instanceDir: string): string;
|
|
10
11
|
export declare const DEFAULT_STUCK_TIMEOUT_MS: number;
|
|
11
12
|
export declare const DEFAULT_STATE_IDLE_DEBOUNCE_MS = 2000;
|
|
12
13
|
export declare const DEFAULT_STATE_SAFETY_SWEEP_MS = 60000;
|
|
13
|
-
/**
|
|
14
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Whether two working directories belong to the same project, so a fleet-scoped
|
|
16
|
+
* decision recorded in one reaches the other. Covers the worktree/checkout
|
|
17
|
+
* layout AgEnD fleets actually use — `AgEnD`, `AgEnD-dev1`, `AgEnD-dev2`,
|
|
18
|
+
* `AgEnD-reviewer`, `AgEnD-main` are one project; `DouPo_Server` is not.
|
|
19
|
+
* Nesting counts too (a subdirectory of a project belongs to it).
|
|
20
|
+
*/
|
|
21
|
+
export declare function sameProjectFamily(a: string, b: string): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Pick the decisions worth injecting into one instance's system prompt.
|
|
24
|
+
*
|
|
25
|
+
* `scope: "fleet"` previously bypassed the project check outright, so every
|
|
26
|
+
* instance carried every other project's playbook — measured at 8 of 14 here,
|
|
27
|
+
* resent on every API call. A fleet decision must now also be relevant: global
|
|
28
|
+
* (no project_root), the same project, or the same project family.
|
|
29
|
+
*
|
|
30
|
+
* `isDispatcher` (a general) opts out of that narrowing on purpose: it routes
|
|
31
|
+
* work across all projects, so cross-project rules ("X 文件操作由 Y 負責") are
|
|
32
|
+
* precisely what it must know. Narrowing a general would cause misrouting.
|
|
33
|
+
*/
|
|
34
|
+
export declare function selectRelevantDecisions<T extends {
|
|
35
|
+
scope?: string;
|
|
36
|
+
project_root?: string;
|
|
37
|
+
title: string;
|
|
38
|
+
}>(all: T[], workDir: string, isDispatcher?: boolean): T[];
|
|
15
39
|
/** Read the last real channel inbound timestamp persisted across daemon restarts. */
|
|
16
40
|
export declare function readLastInboundAt(instanceDir: string, now?: number): number | null;
|
|
17
41
|
/** Atomically persist the last real channel inbound timestamp. */
|
|
18
42
|
export declare function writeLastInboundAt(instanceDir: string, timestamp: number): void;
|
|
43
|
+
/**
|
|
44
|
+
* Render the handoff/routing metadata that rides along with an inbound message
|
|
45
|
+
* into the block actually pasted into the agent's pane.
|
|
46
|
+
*
|
|
47
|
+
* These fields were all populated by the sender (outbound-handlers builds them
|
|
48
|
+
* into `ipcMeta`) and delivered over IPC, but never rendered — so the receiving
|
|
49
|
+
* agent could not see them, and several documented flows could not work:
|
|
50
|
+
*
|
|
51
|
+
* - `report_result` requires a `correlation_id` the agent had no way to know, so
|
|
52
|
+
* the "correlation_id not recognized" warning fired on essentially every call.
|
|
53
|
+
* - Delegation cancel buttons are retired by correlation id, so they never retired.
|
|
54
|
+
* - `react`, `edit_message` and `reply.reply_to` need `message_id`, which the tool
|
|
55
|
+
* descriptions tell the agent to take "from the inbound block".
|
|
56
|
+
* - `download_attachment` needs `attachment_file_id`.
|
|
57
|
+
* - `requires_reply` was invisible, so a delegated task looked like an FYI.
|
|
58
|
+
*
|
|
59
|
+
* Only non-empty fields are emitted, so a plain user message gains at most a
|
|
60
|
+
* message_id line.
|
|
61
|
+
*/
|
|
62
|
+
export declare function renderHandoffMetadata(meta: Record<string, string>): string;
|
|
19
63
|
/** Headless inactivity timer used by the daemon and unit tests. */
|
|
20
64
|
export declare class AutoPauseController {
|
|
21
65
|
private readonly thresholdMs;
|
|
@@ -36,16 +80,32 @@ export declare class AutoPauseController {
|
|
|
36
80
|
* Pane motion wins over a ready match because several backends keep their ready
|
|
37
81
|
* marker in a persistent header/footer while generating. A stable ready pane is
|
|
38
82
|
* idle; changing content is working; stable non-ready content eventually sticks.
|
|
83
|
+
*
|
|
84
|
+
* ## Why an optional busy pattern exists
|
|
85
|
+
*
|
|
86
|
+
* "Motion wins" degrades badly when a backend's ready marker is *always* on
|
|
87
|
+
* screen. `stuck` requires `!ready`, so for such a backend the state can only ever
|
|
88
|
+
* be idle or working — the stuck edge, and with it `handleStuckTransition` and the
|
|
89
|
+
* hang notification, is unreachable. A frozen CLI is then reported as idle, which
|
|
90
|
+
* also clears pending work: the message the user sent is quietly booked as done.
|
|
91
|
+
*
|
|
92
|
+
* claude-code was exactly this case (see ClaudeCodeBackend.getReadyPattern). A
|
|
93
|
+
* backend that can point at a marker meaning "generating right now" supplies
|
|
94
|
+
* `getBusyPattern()`, and that match vetoes ready regardless of what the ready
|
|
95
|
+
* pattern says.
|
|
39
96
|
*/
|
|
40
97
|
export declare class PaneStateMachine {
|
|
41
98
|
private readonly stuckTimeoutMs;
|
|
42
99
|
private readonly readyPattern;
|
|
100
|
+
private readonly busyPattern;
|
|
43
101
|
private lastPaneHash;
|
|
44
102
|
private lastPaneChangeAt;
|
|
45
103
|
private lastObservedAt;
|
|
46
104
|
private stateChangedAt;
|
|
47
105
|
private currentState;
|
|
48
|
-
constructor(readyPattern: RegExp, stuckTimeoutMs?: number, now?: number);
|
|
106
|
+
constructor(readyPattern: RegExp, stuckTimeoutMs?: number, now?: number, busyPattern?: RegExp | null);
|
|
107
|
+
/** Ready, unless the backend can positively see that it is still generating. */
|
|
108
|
+
private isReady;
|
|
49
109
|
observe(pane: string, now?: number): InstanceStateSnapshot;
|
|
50
110
|
/** Record pane motion from tmux control mode without capturing pane content. */
|
|
51
111
|
recordOutput(now?: number): InstanceStateSnapshot;
|
|
@@ -83,13 +143,13 @@ export declare class Daemon extends EventEmitter {
|
|
|
83
143
|
private topicMode;
|
|
84
144
|
private backend?;
|
|
85
145
|
private controlClient?;
|
|
146
|
+
private runtimeIdentity?;
|
|
86
147
|
private logger;
|
|
87
148
|
private tmuxSessionName;
|
|
88
149
|
private tmux;
|
|
89
150
|
private ipcServer;
|
|
90
151
|
private messageBus;
|
|
91
152
|
private transcriptMonitor;
|
|
92
|
-
private toolTracker;
|
|
93
153
|
private guardian;
|
|
94
154
|
private adapter;
|
|
95
155
|
private pendingIpcRequests;
|
|
@@ -97,6 +157,8 @@ export declare class Daemon extends EventEmitter {
|
|
|
97
157
|
private lastThreadId;
|
|
98
158
|
private lastAdapterId;
|
|
99
159
|
private pendingAckMessage;
|
|
160
|
+
/** Last activity published to the fleet manager; null when nothing is running. */
|
|
161
|
+
private currentActivity;
|
|
100
162
|
private toolStatusMessageId;
|
|
101
163
|
private toolStatusLines;
|
|
102
164
|
private toolStatusDebounce;
|
|
@@ -110,9 +172,19 @@ export declare class Daemon extends EventEmitter {
|
|
|
110
172
|
private lastSpawnAt;
|
|
111
173
|
private crashTimestamps;
|
|
112
174
|
private healthCheckPaused;
|
|
175
|
+
private lastHealthErrorNotifyAt;
|
|
113
176
|
/** CLI pane availability, independent from the daemon process and tri-state. */
|
|
114
177
|
private processStatus;
|
|
115
178
|
private spawning;
|
|
179
|
+
/**
|
|
180
|
+
* Resolves when the in-flight spawn finishes; null when none is running.
|
|
181
|
+
*
|
|
182
|
+
* `spawning` alone can only be polled. Delivery needs to *wait*, and a boolean
|
|
183
|
+
* poll would either busy-loop or race the flag being cleared.
|
|
184
|
+
*/
|
|
185
|
+
private spawnSettled;
|
|
186
|
+
private resolveSpawnSettled;
|
|
187
|
+
private spawnDepth;
|
|
116
188
|
private skipResume;
|
|
117
189
|
private backgroundSessionRecoveryAttempted;
|
|
118
190
|
/** Whether the last spawn started a fresh session (not resumed). */
|
|
@@ -134,10 +206,18 @@ export declare class Daemon extends EventEmitter {
|
|
|
134
206
|
private instanceStateIdleDebounceMs;
|
|
135
207
|
private instanceStateStuckTimeoutMs;
|
|
136
208
|
private instanceStateReadyPattern;
|
|
209
|
+
private instanceStateBusyPattern;
|
|
137
210
|
private instanceStateMonitorActive;
|
|
138
211
|
private statePollInFlight;
|
|
139
212
|
private autoPauseController;
|
|
140
213
|
private pauseRequested;
|
|
214
|
+
/**
|
|
215
|
+
* Sticky "pause as soon as possible" (auth failure). Unlike pauseRequested —
|
|
216
|
+
* which the state monitor clears whenever the pane is not idle — this survives
|
|
217
|
+
* busy/stuck states, because an auth error fires mid-turn and a plain pause()
|
|
218
|
+
* would silently no-op exactly when we most need to stop feeding the CLI.
|
|
219
|
+
*/
|
|
220
|
+
private pausePending;
|
|
141
221
|
private pauseWakeState;
|
|
142
222
|
private pauseWakeTransition;
|
|
143
223
|
private modelOverride;
|
|
@@ -145,7 +225,13 @@ export declare class Daemon extends EventEmitter {
|
|
|
145
225
|
private recentEvents;
|
|
146
226
|
private recentToolActivity;
|
|
147
227
|
private snapshotConsumed;
|
|
228
|
+
/** Orders inbound channel messages against each other (queue-depth accounting
|
|
229
|
+
* and ⏳/👀/✅ reactions live here). It does NOT cover the other writers that
|
|
230
|
+
* reach the pane — {@link paneWriteLock} does. */
|
|
148
231
|
private pasteLock;
|
|
232
|
+
/** Mutual exclusion for *every* write into the pane, whichever subsystem it
|
|
233
|
+
* comes from. See PaneWriteLock for why interleaving is destructive. */
|
|
234
|
+
private readonly paneWriteLock;
|
|
149
235
|
private pendingInstructionsUpdate;
|
|
150
236
|
private pendingInstructionsNotice;
|
|
151
237
|
private warmupNeeded;
|
|
@@ -153,6 +239,10 @@ export declare class Daemon extends EventEmitter {
|
|
|
153
239
|
private pasteQueueDepth;
|
|
154
240
|
private firstDeliveryDelay;
|
|
155
241
|
private errorMonitorTimer;
|
|
242
|
+
/** Same 5-min gate the error monitor uses, so a dead MCP server alerts once. */
|
|
243
|
+
private static readonly MCP_DEATH_COOLDOWN_MS;
|
|
244
|
+
private lastMcpDeathNotifiedAt;
|
|
245
|
+
private mcpDeathNotifiedForPid;
|
|
156
246
|
/** Prevent in-flight monitor callbacks from re-arming after a pause. */
|
|
157
247
|
private runtimeMonitorsFrozen;
|
|
158
248
|
private errorWaitingForRecovery;
|
|
@@ -177,14 +267,42 @@ export declare class Daemon extends EventEmitter {
|
|
|
177
267
|
private lastDetectedErrorType;
|
|
178
268
|
private static errorPatternKey;
|
|
179
269
|
private clearErrorRecoveryGate;
|
|
180
|
-
constructor(name: string, config: InstanceConfig, instanceDir: string, topicMode?: boolean, backend?: CliBackend | undefined, controlClient?: TmuxControlClient | undefined, rootLogger?: Logger);
|
|
270
|
+
constructor(name: string, config: InstanceConfig, instanceDir: string, topicMode?: boolean, backend?: CliBackend | undefined, controlClient?: TmuxControlClient | undefined, rootLogger?: Logger, runtimeIdentity?: FleetInstructionsParams["runtimeIdentity"]);
|
|
181
271
|
start(): Promise<void>;
|
|
272
|
+
/**
|
|
273
|
+
* Detect a CRASHED MCP server and report it once.
|
|
274
|
+
*
|
|
275
|
+
* The MCP server writes its pid to channel.mcp.pid at startup and unlinks it on
|
|
276
|
+
* a clean exit, so "file present + pid dead" specifically means it died without
|
|
277
|
+
* cleaning up (crash / OOM / SIGKILL). A missing file is either "not started
|
|
278
|
+
* yet" or "exited cleanly" — neither is an incident, and an orphan exit implies
|
|
279
|
+
* the CLI itself died, which the crash detector already covers.
|
|
280
|
+
*
|
|
281
|
+
* The daemon deliberately does NOT try to respawn it: MCP is a stdio protocol
|
|
282
|
+
* where the CLI spawns the server and owns its pipes, so a daemon-spawned
|
|
283
|
+
* process would have no client reading it. Only the CLI can restore its own
|
|
284
|
+
* tools — hence notify, and let the operator decide about restarting.
|
|
285
|
+
*/
|
|
286
|
+
private checkMcpServerAlive;
|
|
182
287
|
private startHealthCheck;
|
|
183
288
|
/**
|
|
184
289
|
* Publish pane-process availability separately from idle/working/stuck.
|
|
185
290
|
* A dead remain-on-exit pane still contains the old ready prompt, so allowing
|
|
186
291
|
* the pane monitor to capture it would re-create a false idle state.
|
|
187
292
|
*/
|
|
293
|
+
/**
|
|
294
|
+
* Announce that this instance is no longer being supervised.
|
|
295
|
+
*
|
|
296
|
+
* Four health-check exits set `healthCheckPaused = true` and returned without
|
|
297
|
+
* telling anyone: a clean CLI exit, `max_retries <= 0`, crash retries exhausted,
|
|
298
|
+
* and the instance directory being deleted. Only the crash-LOOP case emitted an
|
|
299
|
+
* event, so a fleet could quietly contain a dead instance that still looked fine
|
|
300
|
+
* on the dashboard — while messages routed to it queued or failed with a bare ❌.
|
|
301
|
+
*
|
|
302
|
+
* `crash_loop` already had this treatment; this is the same bridge for the other
|
|
303
|
+
* four, carrying a human-readable cause and the operator's next step.
|
|
304
|
+
*/
|
|
305
|
+
private emitSupervisionEnded;
|
|
188
306
|
private setProcessStatus;
|
|
189
307
|
/**
|
|
190
308
|
* Periodically scan PTY output for backend-defined error patterns.
|
|
@@ -211,6 +329,12 @@ export declare class Daemon extends EventEmitter {
|
|
|
211
329
|
* Interrupt the CLI's current generation (cancel button / `/cancel`).
|
|
212
330
|
* Direct tmux key event (not a paste) so it registers as the interrupt key.
|
|
213
331
|
* kiro-cli interrupts on Ctrl+C; the others (claude-code, codex, …) on Escape.
|
|
332
|
+
*
|
|
333
|
+
* Deliberately NOT taken through `paneWriteLock`. Cancel is the user's way out
|
|
334
|
+
* of a pane that is busy or wedged — queueing it behind the very delivery chain
|
|
335
|
+
* it exists to unblock would make the button useless exactly when it is needed.
|
|
336
|
+
* The cost is accepted: an Escape landing between a paste and its Enter discards
|
|
337
|
+
* that message, which is what the user asked for anyway.
|
|
214
338
|
*/
|
|
215
339
|
sendEscape(): Promise<void>;
|
|
216
340
|
/** Send the backend-specific graceful quit command/key sequence. */
|
|
@@ -220,6 +344,12 @@ export declare class Daemon extends EventEmitter {
|
|
|
220
344
|
getInstanceState(): InstanceState | "paused";
|
|
221
345
|
getInstanceStateSnapshot(): InstanceStateSnapshot;
|
|
222
346
|
/** Gracefully stop the CLI while keeping its remain-on-exit tmux window. */
|
|
347
|
+
/**
|
|
348
|
+
* Mark the instance to pause as soon as its pane is idle. Used for auth
|
|
349
|
+
* failures: pause() alone no-ops while the CLI is busy/stuck, which is the
|
|
350
|
+
* usual state when the error surfaces. Cleared by a successful pause or wake.
|
|
351
|
+
*/
|
|
352
|
+
requestPauseWhenIdle(): void;
|
|
223
353
|
pause(): Promise<void>;
|
|
224
354
|
/** Respawn the CLI in the preserved window and block until its prompt is ready. */
|
|
225
355
|
wake(timeoutMs?: number): Promise<void>;
|
|
@@ -239,6 +369,28 @@ export declare class Daemon extends EventEmitter {
|
|
|
239
369
|
/** Restore the same monitor objects after wake without adding event listeners. */
|
|
240
370
|
private resumeRuntimeMonitors;
|
|
241
371
|
getMessageBus(): MessageBus;
|
|
372
|
+
/**
|
|
373
|
+
* Tell the fleet manager what this instance is doing right now, for the live
|
|
374
|
+
* progress line on the cancel button.
|
|
375
|
+
*
|
|
376
|
+
* Purely cosmetic — nothing decides anything from it, so it is fine that only
|
|
377
|
+
* backends with a transcript feed report at all (claude-code today). For the
|
|
378
|
+
* rest the progress line keeps showing just elapsed time.
|
|
379
|
+
*
|
|
380
|
+
* Repeats are dropped: the ticker only edits the channel message when the text
|
|
381
|
+
* changes, and a stream of identical broadcasts would defeat that.
|
|
382
|
+
*/
|
|
383
|
+
private publishActivity;
|
|
384
|
+
/**
|
|
385
|
+
* Options for every system-initiated paste (startup notice, session snapshot,
|
|
386
|
+
* runtime-dialog keys).
|
|
387
|
+
*
|
|
388
|
+
* One place, so the three call sites cannot drift apart on it. Backends with a
|
|
389
|
+
* native input queue do not get the retry Enter: on those, a second bare Enter
|
|
390
|
+
* is not the no-op the retry assumed but a queue mutation — the same reason
|
|
391
|
+
* `deliverMessage` refuses to probe for busy with one.
|
|
392
|
+
*/
|
|
393
|
+
private systemPasteOptions;
|
|
242
394
|
private summarizeTool;
|
|
243
395
|
private addToolStatus;
|
|
244
396
|
/** Debounce tool status updates to avoid channel rate limits */
|
|
@@ -254,23 +406,52 @@ export declare class Daemon extends EventEmitter {
|
|
|
254
406
|
pushChannelMessage(content: string, meta: Record<string, string>, _targetSession?: string): void;
|
|
255
407
|
/**
|
|
256
408
|
* Deliver a single message and drive its status reactions:
|
|
257
|
-
* ⏳ message_queued — CLI busy; queued
|
|
409
|
+
* ⏳ message_queued — CLI busy; queued locally or by the backend
|
|
258
410
|
* 👀 message_delivered — pasted + Enter sent; agent now has it
|
|
259
|
-
* ✅ message_confirmed —
|
|
411
|
+
* ✅ message_confirmed — processing observed, or native queue handoff succeeded
|
|
260
412
|
* ❌ message_failed — tmux window gone, paste retries exhausted
|
|
261
413
|
* Returns true once the message is in the CLI, false only on real delivery failure.
|
|
262
414
|
*
|
|
263
415
|
* Bug A (silent message loss): paste failures retry with backoff (window recovery)
|
|
264
416
|
* and emit `message_failed` if all attempts fail.
|
|
265
|
-
* Busy handling (UX):
|
|
266
|
-
*
|
|
267
|
-
*
|
|
268
|
-
*
|
|
417
|
+
* Busy handling (UX): backends without a native input queue show ⏳ and wait for
|
|
418
|
+
* idle indefinitely (a genuinely hung CLI is the hang detector's job). Backends
|
|
419
|
+
* that explicitly support queued input receive one complete paste+Enter transaction
|
|
420
|
+
* immediately and own the application-level queue themselves. The pasteLock remains
|
|
421
|
+
* serial in both cases so separate PTY writes can never overlap.
|
|
269
422
|
*/
|
|
270
423
|
private deliverMessage;
|
|
424
|
+
/**
|
|
425
|
+
* The write half of a delivery: paste → Enter → confirm, with retries.
|
|
426
|
+
*
|
|
427
|
+
* Always called under {@link paneWriteLock}. Split out from `deliverMessage`
|
|
428
|
+
* precisely so the lock's scope is visible at the call site rather than being
|
|
429
|
+
* an invariant maintained by comments.
|
|
430
|
+
*/
|
|
431
|
+
private writeMessageToPane;
|
|
432
|
+
/**
|
|
433
|
+
* True when a busy native-queue paste appears to have landed: Codex shows a
|
|
434
|
+
* `↳` queue marker, or a distinctive slice of the pasted text is on screen.
|
|
435
|
+
* Used only to detect silent paste loss — not as a general ready check.
|
|
436
|
+
*/
|
|
437
|
+
private nativeQueuePasteVisible;
|
|
271
438
|
/** Re-resolve this instance's tmux window by name (stale id after crash/respawn). */
|
|
272
439
|
private recoverWindow;
|
|
273
|
-
/**
|
|
440
|
+
/**
|
|
441
|
+
* Poll up to ~2s (200ms × 10) for the pane to emit output after `since`.
|
|
442
|
+
*
|
|
443
|
+
* A `false` here is not "no output" — it is read as **"the message was pasted but
|
|
444
|
+
* never submitted"**, which re-sends Enter and then reports ❌ to the user. So a
|
|
445
|
+
* false negative costs a possible double submit and a failure notice for a
|
|
446
|
+
* message that actually arrived.
|
|
447
|
+
*
|
|
448
|
+
* That is exactly what a control-mode reconnect used to produce. `connect()`
|
|
449
|
+
* drops every output timestamp, so `hasOutputSince` answers `false` for a pane
|
|
450
|
+
* that did react — the evidence was thrown away, not absent. Blind time is
|
|
451
|
+
* therefore not counted against the budget: after a reset the poll restarts from
|
|
452
|
+
* the moment observation resumed. A hard wall-clock cap keeps a reconnect loop
|
|
453
|
+
* from extending this forever.
|
|
454
|
+
*/
|
|
274
455
|
private confirmBusyAfterEnter;
|
|
275
456
|
private getWindowId;
|
|
276
457
|
/** Find the IPC socket for a given sessionName */
|
|
@@ -288,6 +469,31 @@ export declare class Daemon extends EventEmitter {
|
|
|
288
469
|
* This replaces the old system-prompt injection approach.
|
|
289
470
|
*/
|
|
290
471
|
private injectSnapshotMessage;
|
|
472
|
+
/**
|
|
473
|
+
* Depth, not a flag: the wake path marks a spawn and then calls trySpawn, which
|
|
474
|
+
* marks another. With a plain boolean the inner one's completion would clear the
|
|
475
|
+
* latch while the outer spawn was still dismissing dialogs, releasing delivery
|
|
476
|
+
* into exactly the window this exists to close.
|
|
477
|
+
*/
|
|
478
|
+
private beginSpawn;
|
|
479
|
+
private endSpawn;
|
|
480
|
+
/**
|
|
481
|
+
* Hold until the CLI has finished starting up.
|
|
482
|
+
*
|
|
483
|
+
* Startup is not a quiet period: `dismissDialogsUntilReady` is clicking through
|
|
484
|
+
* trust prompts and session pickers. A pane showing a modal dialog produces no
|
|
485
|
+
* output, so `waitUntilIdle` reports it idle and a queued message gets pasted
|
|
486
|
+
* *into the dialog* — where the text is discarded and the Enter picks a menu
|
|
487
|
+
* item. The paste and the Enter both "succeed", so this loses the message
|
|
488
|
+
* without even a ❌.
|
|
489
|
+
*
|
|
490
|
+
* The pane write lock (#414) does not cover this: it serialises each key
|
|
491
|
+
* sequence, but it is released between one dialog and the next.
|
|
492
|
+
*
|
|
493
|
+
* Bounded, and called BEFORE the pane lock is taken — waiting on the spawn while
|
|
494
|
+
* holding the lock the spawn itself needs would deadlock.
|
|
495
|
+
*/
|
|
496
|
+
private waitForSpawnToSettle;
|
|
291
497
|
/** Spawn a CLI window. Returns true if --resume was used successfully. */
|
|
292
498
|
private spawnClaudeWindow;
|
|
293
499
|
/** Kill the entire process tree of the current tmux pane (CLI + MCP server). */
|