@adhdev/daemon-core 0.9.82-rc.552 → 0.9.82-rc.553
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/cli-adapters/provider-cli-adapter.d.ts +80 -1
- package/dist/cli-adapters/provider-cli-shared.d.ts +55 -0
- package/dist/cli-adapters/terminal-screen.d.ts +5 -0
- package/dist/commands/stream-commands.d.ts +31 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +453 -9
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +449 -6
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-ledger.d.ts +1 -1
- package/dist/providers/cli-provider-instance.d.ts +82 -0
- package/dist/providers/provider-instance.d.ts +9 -0
- package/dist/repo-mesh-types.d.ts +23 -0
- package/package.json +3 -3
- package/src/cli-adapters/provider-cli-adapter.ts +135 -0
- package/src/cli-adapters/provider-cli-shared.ts +172 -0
- package/src/cli-adapters/terminal-screen.ts +5 -0
- package/src/commands/handler.ts +4 -0
- package/src/commands/router.ts +15 -0
- package/src/commands/stream-commands.ts +125 -0
- package/src/index.ts +1 -0
- package/src/mesh/coordinator-prompt.ts +2 -0
- package/src/mesh/mesh-events-utils.ts +15 -0
- package/src/mesh/mesh-ledger.ts +6 -0
- package/src/providers/cli-provider-instance.ts +196 -0
- package/src/providers/provider-instance-manager.ts +11 -0
- package/src/providers/provider-instance.ts +10 -0
- package/src/repo-mesh-types.ts +35 -0
package/dist/index.js
CHANGED
|
@@ -124,6 +124,15 @@ function resolveDelegatedWorkerAutoApprove(meshPolicy, nodePolicy) {
|
|
|
124
124
|
}
|
|
125
125
|
return true;
|
|
126
126
|
}
|
|
127
|
+
function resolveAllowSendKeysDestructive(meshPolicy, nodePolicy) {
|
|
128
|
+
if (typeof nodePolicy?.allowSendKeysDestructive === "boolean") {
|
|
129
|
+
return nodePolicy.allowSendKeysDestructive;
|
|
130
|
+
}
|
|
131
|
+
if (typeof meshPolicy?.allowSendKeysDestructive === "boolean") {
|
|
132
|
+
return meshPolicy.allowSendKeysDestructive;
|
|
133
|
+
}
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
127
136
|
function resolveProviderMaxParallel(slots, providerType) {
|
|
128
137
|
const wanted = typeof providerType === "string" ? providerType.trim().toLowerCase() : "";
|
|
129
138
|
if (!wanted) return void 0;
|
|
@@ -419,10 +428,10 @@ function readInjected(value) {
|
|
|
419
428
|
}
|
|
420
429
|
function getDaemonBuildInfo() {
|
|
421
430
|
if (cached) return cached;
|
|
422
|
-
const commit = readInjected(true ? "
|
|
423
|
-
const commitShort = readInjected(true ? "
|
|
424
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
425
|
-
const builtAt = readInjected(true ? "2026-07-
|
|
431
|
+
const commit = readInjected(true ? "3fc4f272513b6f628ec1732ef57d9ed94e08b8ff" : void 0) ?? "unknown";
|
|
432
|
+
const commitShort = readInjected(true ? "3fc4f272" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
433
|
+
const version = readInjected(true ? "0.9.82-rc.553" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
434
|
+
const builtAt = readInjected(true ? "2026-07-17T06:04:22.090Z" : void 0);
|
|
426
435
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
427
436
|
return cached;
|
|
428
437
|
}
|
|
@@ -2988,6 +2997,8 @@ var init_dist = __esm({
|
|
|
2988
2997
|
"mesh_send_task",
|
|
2989
2998
|
"mesh_read_chat",
|
|
2990
2999
|
"mesh_read_debug",
|
|
3000
|
+
"mesh_read_terminal",
|
|
3001
|
+
"mesh_send_keys",
|
|
2991
3002
|
"mesh_launch_session",
|
|
2992
3003
|
"mesh_git_status",
|
|
2993
3004
|
"mesh_read_node_logs",
|
|
@@ -4134,6 +4145,8 @@ var init_coordinator_prompt = __esm({
|
|
|
4134
4145
|
| \`mesh_launch_session\` | Start a new agent session on a node |
|
|
4135
4146
|
| \`mesh_read_chat\` | Read recent chat messages from a delegated agent session |
|
|
4136
4147
|
| \`mesh_read_debug\` | Collect a daemon-side chat/parser debug bundle for a session |
|
|
4148
|
+
| \`mesh_read_terminal\` | Read a worker session's CURRENT raw terminal screen (the live rendered PTY viewport \u2014 prompt/modal/spinner/unparsed output), not the parsed chat. Byte-bounded (32KiB default, 64KiB max; bottom of screen kept). Use to see exactly what a worker is showing when mesh_read_chat is not enough (e.g. after a stall alert). Screen text may contain secrets \u2014 treat as sensitive |
|
|
4149
|
+
| \`mesh_send_keys\` | Inject a STRUCTURED key sequence into a worker's live PTY (text + named keys ENTER/ESC/CTRL_C/UP/DOWN/LEFT/RIGHT/TAB/BACKSPACE). For interactions mesh_send_task can't express \u2014 answer a non-approval prompt, navigate a picker, submit a typed line, or interrupt (CTRL_C). Use mesh_approve for approval modals (send_keys is refused on one). Destructive keys (CTRL_C/ESC) need confirm_destructive=true AND mesh policy allowSendKeysDestructive. Refused on a pending submit/echo race. Audited (key enums only) |
|
|
4137
4150
|
| \`mesh_task_history\` | Read the task ledger \u2014 dispatches, completions, failures. Use to understand what has been done before deciding next steps |
|
|
4138
4151
|
| \`mesh_ledger_query\` | Read-only ledger query along the kind/time/node axes (complement to task-axis mesh_task_history): filter by kind, since, node, tail \u2014 answer "what happened on node X / what failed since T" without scanning transcripts |
|
|
4139
4152
|
| \`mesh_reconcile_ledger\` | Reconcile daemon-local ledgers over P2P \u2014 import missing entries from remote nodes into the coordinator local ledger |
|
|
@@ -8792,6 +8805,13 @@ Do NOT retry on this node. Consider reassigning to a different node or asking th
|
|
|
8792
8805
|
return `[System] ${args.nodeLabel} has stopped${metadata}. Use mesh_read_chat once if you need to inspect its last output.`;
|
|
8793
8806
|
}
|
|
8794
8807
|
if (args.event === "monitor:no_progress") {
|
|
8808
|
+
if (args.metadataEvent.meshWorkerStall === true) {
|
|
8809
|
+
const observedStatus = readNonEmptyString2(args.metadataEvent.observedStatus);
|
|
8810
|
+
const stalledMs = typeof args.metadataEvent.stalledMs === "number" ? args.metadataEvent.stalledMs : void 0;
|
|
8811
|
+
const stalledSuffix = stalledMs !== void 0 ? ` for ${Math.round(stalledMs / 1e3)}s` : "";
|
|
8812
|
+
const statusSuffix = observedStatus ? ` (observed status: ${observedStatus})` : "";
|
|
8813
|
+
return `[System] ${args.nodeLabel}: PTY output unchanged${stalledSuffix}${statusSuffix}${metadata}. This is an informational stall \u2014 the worker's screen has been static regardless of its reported status; it may be genuinely idle, waiting, or wedged, so this is NOT a failure or auto-restart. Judge whether to inspect it: wait for pendingCoordinatorEvents/a completion event, or make one bounded mesh_read_chat check if you need to see its current screen, then wait again.`;
|
|
8814
|
+
}
|
|
8795
8815
|
return `[System] ${args.nodeLabel} is still reported as generating after a long interval${metadata}. Wait for pendingCoordinatorEvents or a completion/status event; if the user explicitly asks for status, make one bounded status check and then wait again.`;
|
|
8796
8816
|
}
|
|
8797
8817
|
if (args.event === "worktree_bootstrap_complete") {
|
|
@@ -12584,6 +12604,82 @@ function sanitizeTerminalText(str) {
|
|
|
12584
12604
|
const accumulator = new TerminalTranscriptAccumulator();
|
|
12585
12605
|
return stripTerminalNoise(stripAnsi(accumulator.append(str)));
|
|
12586
12606
|
}
|
|
12607
|
+
function encodeMeshSendKeys(items) {
|
|
12608
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
12609
|
+
throw new Error("send_keys: sequence must be a non-empty array");
|
|
12610
|
+
}
|
|
12611
|
+
if (items.length > MESH_SEND_KEYS_MAX_ITEMS) {
|
|
12612
|
+
throw new Error(`send_keys: sequence exceeds ${MESH_SEND_KEYS_MAX_ITEMS} items`);
|
|
12613
|
+
}
|
|
12614
|
+
const parts = [];
|
|
12615
|
+
const keys = [];
|
|
12616
|
+
let hasDestructive = false;
|
|
12617
|
+
let submits = false;
|
|
12618
|
+
let textBytes = 0;
|
|
12619
|
+
for (const item of items) {
|
|
12620
|
+
if (item && typeof item.text === "string") {
|
|
12621
|
+
const text = item.text;
|
|
12622
|
+
textBytes += Buffer.byteLength(text, "utf8");
|
|
12623
|
+
if (textBytes > MESH_SEND_KEYS_MAX_TEXT_BYTES) {
|
|
12624
|
+
throw new Error(`send_keys: total literal text exceeds ${MESH_SEND_KEYS_MAX_TEXT_BYTES} bytes`);
|
|
12625
|
+
}
|
|
12626
|
+
parts.push(text);
|
|
12627
|
+
submits = false;
|
|
12628
|
+
continue;
|
|
12629
|
+
}
|
|
12630
|
+
const keyName = item && typeof item.key === "string" ? item.key : "";
|
|
12631
|
+
if (!(keyName in MESH_SEND_KEY_ENCODING)) {
|
|
12632
|
+
throw new Error(`send_keys: unknown key '${keyName}'`);
|
|
12633
|
+
}
|
|
12634
|
+
const key2 = keyName;
|
|
12635
|
+
parts.push(MESH_SEND_KEY_ENCODING[key2]);
|
|
12636
|
+
keys.push(key2);
|
|
12637
|
+
if (MESH_DESTRUCTIVE_KEYS.has(key2)) hasDestructive = true;
|
|
12638
|
+
submits = key2 === "ENTER";
|
|
12639
|
+
}
|
|
12640
|
+
return { sequence: parts.join(""), keys, hasDestructive, submits };
|
|
12641
|
+
}
|
|
12642
|
+
function truncateToByteTailByLine(text, maxBytes) {
|
|
12643
|
+
const input = String(text ?? "");
|
|
12644
|
+
const originalBytes = Buffer.byteLength(input, "utf8");
|
|
12645
|
+
if (originalBytes <= maxBytes) {
|
|
12646
|
+
return { text: input, truncated: false, originalBytes, returnedBytes: originalBytes };
|
|
12647
|
+
}
|
|
12648
|
+
const lines = input.split("\n");
|
|
12649
|
+
const kept = [];
|
|
12650
|
+
let bytes = 0;
|
|
12651
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
12652
|
+
const line = lines[i];
|
|
12653
|
+
const lineBytes = Buffer.byteLength(line, "utf8");
|
|
12654
|
+
const joinCost = kept.length > 0 ? 1 : 0;
|
|
12655
|
+
if (bytes + lineBytes + joinCost > maxBytes) break;
|
|
12656
|
+
bytes += lineBytes + joinCost;
|
|
12657
|
+
kept.unshift(line);
|
|
12658
|
+
}
|
|
12659
|
+
if (kept.length > 0) {
|
|
12660
|
+
const out = kept.join("\n");
|
|
12661
|
+
return {
|
|
12662
|
+
text: out,
|
|
12663
|
+
truncated: true,
|
|
12664
|
+
originalBytes,
|
|
12665
|
+
returnedBytes: Buffer.byteLength(out, "utf8")
|
|
12666
|
+
};
|
|
12667
|
+
}
|
|
12668
|
+
const lastLine = lines[lines.length - 1] ?? "";
|
|
12669
|
+
const buf = Buffer.from(lastLine, "utf8");
|
|
12670
|
+
let slice = buf.subarray(Math.max(0, buf.length - maxBytes));
|
|
12671
|
+
let decoded = slice.toString("utf8");
|
|
12672
|
+
while (decoded.length > 0 && decoded.charCodeAt(0) === 65533 && slice.length > 0) {
|
|
12673
|
+
slice = slice.subarray(1);
|
|
12674
|
+
decoded = slice.toString("utf8");
|
|
12675
|
+
}
|
|
12676
|
+
return {
|
|
12677
|
+
text: decoded,
|
|
12678
|
+
truncated: true,
|
|
12679
|
+
originalBytes,
|
|
12680
|
+
returnedBytes: Buffer.byteLength(decoded, "utf8")
|
|
12681
|
+
};
|
|
12682
|
+
}
|
|
12587
12683
|
function listCliScriptNames(scripts) {
|
|
12588
12684
|
if (!scripts) return [];
|
|
12589
12685
|
return Object.entries(scripts).filter(([, fn]) => typeof fn === "function").map(([name]) => name);
|
|
@@ -12785,7 +12881,7 @@ function normalizeCliProviderForRuntime(raw) {
|
|
|
12785
12881
|
}
|
|
12786
12882
|
};
|
|
12787
12883
|
}
|
|
12788
|
-
var os5, path11, TerminalTranscriptAccumulator, buildCliSpawnEnv;
|
|
12884
|
+
var os5, path11, TerminalTranscriptAccumulator, MESH_SEND_KEY_ENCODING, MESH_DESTRUCTIVE_KEYS, MESH_SEND_KEYS_MAX_ITEMS, MESH_SEND_KEYS_MAX_TEXT_BYTES, buildCliSpawnEnv;
|
|
12789
12885
|
var init_provider_cli_shared = __esm({
|
|
12790
12886
|
"src/cli-adapters/provider-cli-shared.ts"() {
|
|
12791
12887
|
"use strict";
|
|
@@ -12948,6 +13044,20 @@ var init_provider_cli_shared = __esm({
|
|
|
12948
13044
|
this.ensureRow();
|
|
12949
13045
|
}
|
|
12950
13046
|
};
|
|
13047
|
+
MESH_SEND_KEY_ENCODING = {
|
|
13048
|
+
ENTER: "\r",
|
|
13049
|
+
ESC: "\x1B",
|
|
13050
|
+
CTRL_C: "",
|
|
13051
|
+
UP: "\x1B[A",
|
|
13052
|
+
DOWN: "\x1B[B",
|
|
13053
|
+
RIGHT: "\x1B[C",
|
|
13054
|
+
LEFT: "\x1B[D",
|
|
13055
|
+
TAB: " ",
|
|
13056
|
+
BACKSPACE: "\x7F"
|
|
13057
|
+
};
|
|
13058
|
+
MESH_DESTRUCTIVE_KEYS = /* @__PURE__ */ new Set(["CTRL_C", "ESC"]);
|
|
13059
|
+
MESH_SEND_KEYS_MAX_ITEMS = 64;
|
|
13060
|
+
MESH_SEND_KEYS_MAX_TEXT_BYTES = 4096;
|
|
12951
13061
|
buildCliSpawnEnv = import_session_host_core2.sanitizeSpawnEnv;
|
|
12952
13062
|
}
|
|
12953
13063
|
});
|
|
@@ -18072,6 +18182,10 @@ var init_terminal_screen = __esm({
|
|
|
18072
18182
|
getCursorPosition() {
|
|
18073
18183
|
return this.terminal.getCursorPosition();
|
|
18074
18184
|
}
|
|
18185
|
+
/** Current viewport dimensions (cols × rows). */
|
|
18186
|
+
getSize() {
|
|
18187
|
+
return { cols: this.cols, rows: this.rows };
|
|
18188
|
+
}
|
|
18075
18189
|
dispose() {
|
|
18076
18190
|
this.terminal.dispose();
|
|
18077
18191
|
}
|
|
@@ -26827,11 +26941,12 @@ function appendBoundedText(current, chunk, maxChars) {
|
|
|
26827
26941
|
if (current.length <= keepFromCurrent) return current + chunk;
|
|
26828
26942
|
return current.slice(-keepFromCurrent) + chunk;
|
|
26829
26943
|
}
|
|
26830
|
-
var os16, import_session_host_core6, FORCE_SUBMIT_SETTLE_MS, ProviderCliAdapter;
|
|
26944
|
+
var os16, import_crypto10, import_session_host_core6, FORCE_SUBMIT_SETTLE_MS, ProviderCliAdapter;
|
|
26831
26945
|
var init_provider_cli_adapter = __esm({
|
|
26832
26946
|
"src/cli-adapters/provider-cli-adapter.ts"() {
|
|
26833
26947
|
"use strict";
|
|
26834
26948
|
os16 = __toESM(require("os"));
|
|
26949
|
+
import_crypto10 = require("crypto");
|
|
26835
26950
|
init_logger();
|
|
26836
26951
|
init_debug_config();
|
|
26837
26952
|
init_terminal_screen();
|
|
@@ -27033,6 +27148,11 @@ var init_provider_cli_adapter = __esm({
|
|
|
27033
27148
|
static MAX_ACCUMULATED_BUFFER = 262144;
|
|
27034
27149
|
parsedStatusCache = null;
|
|
27035
27150
|
static SCREEN_SNAPSHOT_MIN_INTERVAL_MS = 250;
|
|
27151
|
+
// MESH-READ-TERMINAL (feature 2): byte caps for getTerminalScreenSnapshot.
|
|
27152
|
+
// Byte, not char — a multi-byte-glyph screen can exceed an MCP payload cap
|
|
27153
|
+
// while the char count still looks safe. 32KiB default, 64KiB absolute hard cap.
|
|
27154
|
+
static TERMINAL_SNAPSHOT_DEFAULT_MAX_BYTES = 32 * 1024;
|
|
27155
|
+
static TERMINAL_SNAPSHOT_ABSOLUTE_MAX_BYTES = 64 * 1024;
|
|
27036
27156
|
// (FALSEIDLE Path-C) Consecutive gate-eligible getStatus polls a mesh/autonomous
|
|
27037
27157
|
// session must show before the poll-static-idle confirm fires. 2 = one extra
|
|
27038
27158
|
// status tick of hysteresis: enough to reject a single momentary-silence
|
|
@@ -28611,6 +28731,103 @@ ${lastSnapshot}`;
|
|
|
28611
28731
|
isAlive() {
|
|
28612
28732
|
return this.ptyProcess !== null;
|
|
28613
28733
|
}
|
|
28734
|
+
/**
|
|
28735
|
+
* MESH-READ-TERMINAL (feature 2: RAW terminal read). Narrow, least-privilege
|
|
28736
|
+
* read of the CURRENT rendered viewport for mesh_read_terminal. Deliberately
|
|
28737
|
+
* NARROWER than getSnapshot()/getDebugSnapshot():
|
|
28738
|
+
* - It returns ONLY the terminal's current rendered viewport (what a human
|
|
28739
|
+
* would see on screen right now), the cursor position and the viewport
|
|
28740
|
+
* size. NO debug buffers, NO parser/FSM state, NO scrollback/history.
|
|
28741
|
+
* - It does NOT call getParseScreenText() (which may graft an older snapshot
|
|
28742
|
+
* onto the current frame for parse accuracy) — the caller asked for the
|
|
28743
|
+
* live viewport, not a parse-optimized composite.
|
|
28744
|
+
* - The payload is bounded in BYTES (UTF-8) with bottom-tail preservation so
|
|
28745
|
+
* a screen of multi-byte glyphs can never exceed the MCP payload cap. See
|
|
28746
|
+
* truncateToByteTailByLine.
|
|
28747
|
+
*
|
|
28748
|
+
* SECURITY NOTE: the raw viewport can contain tokens / command args / env
|
|
28749
|
+
* values / user data. Callers MUST gate this on mesh ownership and MUST NOT
|
|
28750
|
+
* log the returned text. Opt-in redaction is intentionally out of scope for
|
|
28751
|
+
* this feature and left as a future enhancement.
|
|
28752
|
+
*
|
|
28753
|
+
* `maxBytes` is clamped to [1KiB, ABSOLUTE_MAX] (default 32KiB, hard cap 64KiB).
|
|
28754
|
+
*/
|
|
28755
|
+
getTerminalScreenSnapshot(maxBytes = _ProviderCliAdapter.TERMINAL_SNAPSHOT_DEFAULT_MAX_BYTES) {
|
|
28756
|
+
const cap = Math.min(
|
|
28757
|
+
_ProviderCliAdapter.TERMINAL_SNAPSHOT_ABSOLUTE_MAX_BYTES,
|
|
28758
|
+
Math.max(1024, Math.floor(maxBytes) || _ProviderCliAdapter.TERMINAL_SNAPSHOT_DEFAULT_MAX_BYTES)
|
|
28759
|
+
);
|
|
28760
|
+
const rawViewport = this.terminalScreen.getText() || "";
|
|
28761
|
+
const size = this.terminalScreen.getSize();
|
|
28762
|
+
const cursor = this.terminalScreen.getCursorPosition();
|
|
28763
|
+
const truncation = truncateToByteTailByLine(rawViewport, cap);
|
|
28764
|
+
const hash = (0, import_crypto10.createHash)("sha256").update(rawViewport, "utf8").digest("hex").slice(0, 16);
|
|
28765
|
+
return {
|
|
28766
|
+
text: truncation.text,
|
|
28767
|
+
cursor,
|
|
28768
|
+
cols: size.cols,
|
|
28769
|
+
rows: size.rows,
|
|
28770
|
+
truncated: truncation.truncated,
|
|
28771
|
+
originalBytes: truncation.originalBytes,
|
|
28772
|
+
returnedBytes: truncation.returnedBytes,
|
|
28773
|
+
hash
|
|
28774
|
+
};
|
|
28775
|
+
}
|
|
28776
|
+
/**
|
|
28777
|
+
* MESH-SEND-KEYS (feature 3: key injection). Inject a STRUCTURED key sequence
|
|
28778
|
+
* into the PTY for the mesh_send_keys tool. Reuses the same serialized write
|
|
28779
|
+
* path as sends (writeToPty via writeRaw semantics) so the injection FIFO-
|
|
28780
|
+
* orders behind any in-flight write.
|
|
28781
|
+
*
|
|
28782
|
+
* Two guards run INSIDE this method, immediately before the write, so they see
|
|
28783
|
+
* a consistent snapshot of the adapter's submit/echo/queue state (no async gap
|
|
28784
|
+
* between check and write — the encode is synchronous and the write is chained
|
|
28785
|
+
* atomically after it):
|
|
28786
|
+
*
|
|
28787
|
+
* 1. submit-race recheck (echo-gate): even though writeToPty is FIFO, an
|
|
28788
|
+
* already-SCHEDULED echo-gated Enter (engine.submitPendingUntil in the
|
|
28789
|
+
* future), an armed stuck-submit retry (submitRetryTimer), or an in-flight
|
|
28790
|
+
* pending-outbound flush can submit at a DIFFERENT tick than our write —
|
|
28791
|
+
* so an injected literal could be submitted by a pending Enter, or our
|
|
28792
|
+
* ENTER could submit a half-typed pending body. When any of those is live
|
|
28793
|
+
* we REFUSE the injection rather than interleave.
|
|
28794
|
+
*
|
|
28795
|
+
* 2. modal fail-closed: if the session is parked on an actionable approval
|
|
28796
|
+
* modal, refuse a NON-destructive injection (ENTER/text/arrows) and direct
|
|
28797
|
+
* the caller to mesh_approve — so a modal choice can't be confirmed via
|
|
28798
|
+
* send_keys to bypass the approval policy. (A destructive ESC/CTRL_C, which
|
|
28799
|
+
* dismisses rather than confirms, is allowed to proceed past this gate; it
|
|
28800
|
+
* is separately gated by confirm_destructive + policy at the tool layer.)
|
|
28801
|
+
*
|
|
28802
|
+
* The caller (tool layer) owns the destructive-key double gate and the audit
|
|
28803
|
+
* ledger. This method NEVER logs the literal text (only key enums / byte len).
|
|
28804
|
+
*/
|
|
28805
|
+
async injectKeys(items, opts = {}) {
|
|
28806
|
+
if (!this.ptyProcess) throw new Error(`${this.cliName} is not running`);
|
|
28807
|
+
const encoded = encodeMeshSendKeys(items);
|
|
28808
|
+
const now = Date.now();
|
|
28809
|
+
const submitPending = this.engine.submitPendingUntil > now;
|
|
28810
|
+
const submitRetryArmed = this.submitRetryTimer !== null;
|
|
28811
|
+
const outboundBusy = this.pendingOutboundFlushInFlight || this.pendingOutboundQueue.length > 0;
|
|
28812
|
+
if (submitPending || submitRetryArmed || outboundBusy) {
|
|
28813
|
+
LOG.warn("CLI", `[${this.cliType}] send_keys refused (submit_race): submitPending=${submitPending} submitRetry=${submitRetryArmed} outboundBusy=${outboundBusy} keys=${encoded.keys.join(",")}`);
|
|
28814
|
+
return { ok: false, refused: "submit_race", keys: encoded.keys, hasDestructive: encoded.hasDestructive };
|
|
28815
|
+
}
|
|
28816
|
+
const modalActive = this.engine.hasActionableApproval();
|
|
28817
|
+
if (modalActive && !encoded.hasDestructive && !opts.allowModalOverride) {
|
|
28818
|
+
LOG.warn("CLI", `[${this.cliType}] send_keys refused (actionable_modal): keys=${encoded.keys.join(",")} \u2014 use mesh_approve`);
|
|
28819
|
+
return { ok: false, refused: "actionable_modal", keys: encoded.keys, hasDestructive: encoded.hasDestructive };
|
|
28820
|
+
}
|
|
28821
|
+
await this.writeToPty(encoded.sequence);
|
|
28822
|
+
LOG.info("CLI", `[${this.cliType}] send_keys injected keys=${encoded.keys.join(",") || "(text-only)"} bytes=${Buffer.byteLength(encoded.sequence, "utf8")} destructive=${encoded.hasDestructive}`);
|
|
28823
|
+
return {
|
|
28824
|
+
ok: true,
|
|
28825
|
+
keys: encoded.keys,
|
|
28826
|
+
hasDestructive: encoded.hasDestructive,
|
|
28827
|
+
submits: encoded.submits,
|
|
28828
|
+
bytes: Buffer.byteLength(encoded.sequence, "utf8")
|
|
28829
|
+
};
|
|
28830
|
+
}
|
|
28614
28831
|
flushOutboundQueue() {
|
|
28615
28832
|
this.schedulePendingOutboundFlush();
|
|
28616
28833
|
}
|
|
@@ -29436,6 +29653,7 @@ __export(index_exports, {
|
|
|
29436
29653
|
resetConfig: () => resetConfig,
|
|
29437
29654
|
resetDebugRuntimeConfig: () => resetDebugRuntimeConfig,
|
|
29438
29655
|
resetState: () => resetState,
|
|
29656
|
+
resolveAllowSendKeysDestructive: () => resolveAllowSendKeysDestructive,
|
|
29439
29657
|
resolveAutoConvergeCodeChange: () => resolveAutoConvergeCodeChange,
|
|
29440
29658
|
resolveChatMessageKind: () => resolveChatMessageKind,
|
|
29441
29659
|
resolveConvergeRequiredTags: () => resolveConvergeRequiredTags,
|
|
@@ -39838,6 +40056,63 @@ function handlePtyResize(_h, args) {
|
|
|
39838
40056
|
if (!cols || !rows) return { success: false, error: "cols and rows required" };
|
|
39839
40057
|
return { success: false, error: "PTY resize temporarily disabled", code: "PTY_RESIZE_DISABLED" };
|
|
39840
40058
|
}
|
|
40059
|
+
function handleReadTerminal(h, args) {
|
|
40060
|
+
const targetSessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : "";
|
|
40061
|
+
const sessionId = targetSessionId || h.currentSession?.sessionId || "";
|
|
40062
|
+
if (!sessionId) return { success: false, error: "targetSessionId required" };
|
|
40063
|
+
const session = h.ctx.sessionRegistry?.get(sessionId);
|
|
40064
|
+
const instanceKey = session?.adapterKey || session?.instanceKey || sessionId;
|
|
40065
|
+
const instance = h.ctx.instanceManager?.getInstance(instanceKey);
|
|
40066
|
+
if (!instance) return { success: false, error: `Session not found: ${sessionId.split("_")[0]}` };
|
|
40067
|
+
if (instance.category !== "cli" || typeof instance.getTerminalScreenSnapshot !== "function") {
|
|
40068
|
+
return { success: false, error: "read_terminal is only supported for CLI (PTY) sessions" };
|
|
40069
|
+
}
|
|
40070
|
+
const requestedMaxBytes = typeof args?.maxBytes === "number" && Number.isFinite(args.maxBytes) ? args.maxBytes : void 0;
|
|
40071
|
+
const snapshot = instance.getTerminalScreenSnapshot(requestedMaxBytes);
|
|
40072
|
+
if (!snapshot) {
|
|
40073
|
+
return { success: false, error: "read_terminal is only available for coordinator-spawned mesh worker sessions" };
|
|
40074
|
+
}
|
|
40075
|
+
LOG.info("Command", `[readTerminal] session=${sessionId.split("_")[0]} bytes=${snapshot.returnedBytes}/${snapshot.originalBytes} truncated=${snapshot.truncated} cols=${snapshot.cols} rows=${snapshot.rows}`);
|
|
40076
|
+
return { success: true, ...snapshot };
|
|
40077
|
+
}
|
|
40078
|
+
async function handleSendKeys(h, args) {
|
|
40079
|
+
const targetSessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : "";
|
|
40080
|
+
const sessionId = targetSessionId || h.currentSession?.sessionId || "";
|
|
40081
|
+
if (!sessionId) return { success: false, error: "targetSessionId required" };
|
|
40082
|
+
const items = Array.isArray(args?.sequence) ? args.sequence : null;
|
|
40083
|
+
if (!items || items.length === 0) {
|
|
40084
|
+
return { success: false, error: "sequence (non-empty array of {text}|{key}) required" };
|
|
40085
|
+
}
|
|
40086
|
+
const session = h.ctx.sessionRegistry?.get(sessionId);
|
|
40087
|
+
const instanceKey = session?.adapterKey || session?.instanceKey || sessionId;
|
|
40088
|
+
const instance = h.ctx.instanceManager?.getInstance(instanceKey);
|
|
40089
|
+
if (!instance) return { success: false, error: `Session not found: ${sessionId.split("_")[0]}` };
|
|
40090
|
+
if (instance.category !== "cli" || typeof instance.injectKeys !== "function") {
|
|
40091
|
+
return { success: false, error: "send_keys is only supported for CLI (PTY) sessions" };
|
|
40092
|
+
}
|
|
40093
|
+
const DESTRUCTIVE = /* @__PURE__ */ new Set(["CTRL_C", "ESC"]);
|
|
40094
|
+
const hasDestructiveRequested = items.some((it) => it && typeof it.key === "string" && DESTRUCTIVE.has(it.key));
|
|
40095
|
+
if (hasDestructiveRequested && args?.confirm_destructive !== true) {
|
|
40096
|
+
return {
|
|
40097
|
+
success: false,
|
|
40098
|
+
error: "destructive key (CTRL_C/ESC) requires confirm_destructive=true",
|
|
40099
|
+
refused: "destructive_unconfirmed"
|
|
40100
|
+
};
|
|
40101
|
+
}
|
|
40102
|
+
try {
|
|
40103
|
+
const result = await instance.injectKeys(items, {
|
|
40104
|
+
allowModalOverride: args?.allow_modal_override === true
|
|
40105
|
+
});
|
|
40106
|
+
if (!result.ok) {
|
|
40107
|
+
LOG.info("Command", `[sendKeys] session=${sessionId.split("_")[0]} refused=${result.refused} keys=${result.keys.join(",")} destructive=${result.hasDestructive}`);
|
|
40108
|
+
return { success: false, error: `send_keys refused: ${result.refused}`, refused: result.refused, keys: result.keys, hasDestructive: result.hasDestructive };
|
|
40109
|
+
}
|
|
40110
|
+
LOG.info("Command", `[sendKeys] session=${sessionId.split("_")[0]} injected keys=${result.keys.join(",") || "(text-only)"} bytes=${result.bytes} destructive=${result.hasDestructive} submits=${result.submits}`);
|
|
40111
|
+
return { success: true, keys: result.keys, hasDestructive: result.hasDestructive, submits: result.submits, bytes: result.bytes };
|
|
40112
|
+
} catch (e) {
|
|
40113
|
+
return { success: false, error: `send_keys: ${e?.message || String(e)}` };
|
|
40114
|
+
}
|
|
40115
|
+
}
|
|
39841
40116
|
function handleGetProviderSettings(h, args) {
|
|
39842
40117
|
const loader = h.ctx.providerLoader;
|
|
39843
40118
|
const { providerType } = args || {};
|
|
@@ -40695,6 +40970,12 @@ var DaemonCommandHandler = class {
|
|
|
40695
40970
|
return handlePtyInput(this, args);
|
|
40696
40971
|
case "pty_resize":
|
|
40697
40972
|
return handlePtyResize(this, args);
|
|
40973
|
+
// ─── MESH-READ-TERMINAL (feature 2): raw viewport read ──────────
|
|
40974
|
+
case "read_terminal":
|
|
40975
|
+
return handleReadTerminal(this, args);
|
|
40976
|
+
// ─── MESH-SEND-KEYS (feature 3): structured key injection ────────
|
|
40977
|
+
case "send_keys":
|
|
40978
|
+
return handleSendKeys(this, args);
|
|
40698
40979
|
// ─── Provider Settings (stream-commands.ts) ──────────
|
|
40699
40980
|
case "get_provider_settings":
|
|
40700
40981
|
return handleGetProviderSettings(this, args);
|
|
@@ -47307,6 +47588,15 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
47307
47588
|
* with no approval never carries recency and is never held (no regression).
|
|
47308
47589
|
*/
|
|
47309
47590
|
static APPROVAL_RESUME_GRACE_MS = 18e3;
|
|
47591
|
+
// MESH-STALL-WATCH (feature 1: STALL detection): how long a coordinator-spawned
|
|
47592
|
+
// mesh worker's raw PTY output (lastOutputAt) may stay unchanged before the
|
|
47593
|
+
// status-agnostic stall watchdog fires ONE informational monitor:no_progress
|
|
47594
|
+
// event. Unlike the StatusMonitor no-progress watchdog (which only runs while a
|
|
47595
|
+
// turn is generating), this observes pure screen stasis regardless of the
|
|
47596
|
+
// reported status — a worker parked idle, wedged mid-turn, or spawned with no
|
|
47597
|
+
// output at all. 180s matches DEFAULT_MONITOR_CONFIG.noProgressThresholdSec so
|
|
47598
|
+
// the two watchdogs agree on the same "long interval" bound.
|
|
47599
|
+
static MESH_WORKER_STALL_THRESHOLD_MS = 18e4;
|
|
47310
47600
|
adapter;
|
|
47311
47601
|
context = null;
|
|
47312
47602
|
events = [];
|
|
@@ -47320,6 +47610,16 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
47320
47610
|
// first sets it; the other becomes a no-op.
|
|
47321
47611
|
agentReadyEmitted = false;
|
|
47322
47612
|
generatingStartedAt = 0;
|
|
47613
|
+
// MESH-STALL-WATCH (feature 1): the lastOutputAt value the stall episode is
|
|
47614
|
+
// currently armed against. A stall episode is "the raw PTY output has not
|
|
47615
|
+
// advanced past this anchor". When the adapter has never emitted output
|
|
47616
|
+
// (lastOutputAt === 0) the anchor is the spawn time (this.startedAt) so a
|
|
47617
|
+
// worker that produced NOTHING is still caught. On any new output the anchor
|
|
47618
|
+
// re-arms to the fresh lastOutputAt and meshStallEmittedForAnchor resets, so a
|
|
47619
|
+
// single continuous stall fires AT MOST ONCE and a later stall re-arms cleanly.
|
|
47620
|
+
// -1 = not yet initialised for this session.
|
|
47621
|
+
meshStallAnchorAt = -1;
|
|
47622
|
+
meshStallEmittedForAnchor = false;
|
|
47323
47623
|
// FALSE-IDLE continuity epoch: monotonically bumped on EVERY entry into a busy
|
|
47324
47624
|
// phase (→generating or →waiting_approval). The completedDebouncePending snapshots
|
|
47325
47625
|
// this value at arm time (busyEpochAtArm); the flush guard requires it UNCHANGED
|
|
@@ -48498,6 +48798,124 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
48498
48798
|
isMeshWorkerSession() {
|
|
48499
48799
|
return !!(this.settings.meshNodeFor || this.settings.meshActiveTaskId || this.settings.meshNodeId || this.settings.launchedByCoordinator);
|
|
48500
48800
|
}
|
|
48801
|
+
/**
|
|
48802
|
+
* MESH-READ-TERMINAL (feature 2: RAW terminal read). Public read of the
|
|
48803
|
+
* CURRENT rendered PTY viewport for the mesh_read_terminal tool, delegating to
|
|
48804
|
+
* the adapter's narrow getTerminalScreenSnapshot() (viewport + cursor + size
|
|
48805
|
+
* only; no debug buffers / parser state / history; byte-bounded, bottom-tail
|
|
48806
|
+
* preserved).
|
|
48807
|
+
*
|
|
48808
|
+
* Gated on isMeshWorkerSession(): this raw viewport can expose tokens /
|
|
48809
|
+
* command args / env / user data, so only a coordinator-spawned worker session
|
|
48810
|
+
* is readable. The MCP layer ALSO cross-checks mesh/session/node ownership
|
|
48811
|
+
* (isMeshOwnedDelegateSession) — isMeshWorkerSession alone is a broad
|
|
48812
|
+
* "delegated" gate, so the two together block cross-mesh access. Returns null
|
|
48813
|
+
* for a non-mesh session so the daemon command surfaces a clean refusal.
|
|
48814
|
+
*/
|
|
48815
|
+
getTerminalScreenSnapshot(maxBytes) {
|
|
48816
|
+
if (!this.isMeshWorkerSession()) return null;
|
|
48817
|
+
return this.adapter.getTerminalScreenSnapshot(maxBytes);
|
|
48818
|
+
}
|
|
48819
|
+
/**
|
|
48820
|
+
* MESH-SEND-KEYS (feature 3: key injection). Public entry for the
|
|
48821
|
+
* mesh_send_keys tool, delegating to the adapter's injectKeys() (structured
|
|
48822
|
+
* key encoding + atomic write + submit-race recheck + modal fail-closed).
|
|
48823
|
+
*
|
|
48824
|
+
* Gated on isMeshWorkerSession(): PTY input into a worker is a
|
|
48825
|
+
* coordinator-only capability. The MCP layer ALSO cross-checks mesh/session/
|
|
48826
|
+
* node ownership (isMeshOwnedDelegateSession) and owns the destructive-key
|
|
48827
|
+
* double gate (confirm_destructive + policy) and the audit ledger. Returns a
|
|
48828
|
+
* refusal object for a non-mesh session so the daemon command surfaces a clean
|
|
48829
|
+
* error (never silently writes to a non-worker PTY).
|
|
48830
|
+
*/
|
|
48831
|
+
async injectKeys(items, opts = {}) {
|
|
48832
|
+
if (!this.isMeshWorkerSession()) {
|
|
48833
|
+
return { ok: false, refused: "not_mesh_worker", keys: [], hasDestructive: false };
|
|
48834
|
+
}
|
|
48835
|
+
return this.adapter.injectKeys(items, opts);
|
|
48836
|
+
}
|
|
48837
|
+
/**
|
|
48838
|
+
* MESH-STALL-WATCH (feature 1: STALL detection). Status-agnostic stall
|
|
48839
|
+
* watchdog for coordinator-spawned mesh worker sessions. Driven by the
|
|
48840
|
+
* ProviderInstanceManager's existing 5s onTick loop (NO new timer) — see
|
|
48841
|
+
* ProviderInstanceManager.startTicking. Reuses the adapter's raw-PTY-output
|
|
48842
|
+
* clock (lastOutputAt, bumped on every output chunk) as the sole signal: if a
|
|
48843
|
+
* live worker's screen has been byte-for-byte unchanged for
|
|
48844
|
+
* MESH_WORKER_STALL_THRESHOLD_MS (180s), fire ONE informational
|
|
48845
|
+
* monitor:no_progress event down the existing task_stalled ledger +
|
|
48846
|
+
* pendingCoordinatorEvent path.
|
|
48847
|
+
*
|
|
48848
|
+
* Deliberately status-agnostic: it does NOT read getStatus()'s reported status
|
|
48849
|
+
* (which would couple it to the generating-only StatusMonitor and the
|
|
48850
|
+
* idle-timeout FSM). A normally-idle worker CAN trip this after 3 quiet
|
|
48851
|
+
* minutes; that is accepted and surfaced as an informational stall (NOT a
|
|
48852
|
+
* failure/auto-restart) so the coordinator judges. getStatus/getState are NOT
|
|
48853
|
+
* called here, so status heartbeats never move the stall anchor.
|
|
48854
|
+
*
|
|
48855
|
+
* Anchoring: the episode arms against the current lastOutputAt; a worker that
|
|
48856
|
+
* has emitted nothing yet (lastOutputAt === 0) anchors on this.startedAt (spawn
|
|
48857
|
+
* time) so a silent spawn is still caught. Any new output re-arms the anchor
|
|
48858
|
+
* and clears the emitted flag, so one continuous stall emits at most once and a
|
|
48859
|
+
* later stall re-arms cleanly.
|
|
48860
|
+
*/
|
|
48861
|
+
checkMeshWorkerStall(now = Date.now()) {
|
|
48862
|
+
if (!this.isMeshWorkerSession()) {
|
|
48863
|
+
this.meshStallAnchorAt = -1;
|
|
48864
|
+
this.meshStallEmittedForAnchor = false;
|
|
48865
|
+
return;
|
|
48866
|
+
}
|
|
48867
|
+
if (!this.adapter.isAlive()) {
|
|
48868
|
+
this.meshStallAnchorAt = -1;
|
|
48869
|
+
this.meshStallEmittedForAnchor = false;
|
|
48870
|
+
return;
|
|
48871
|
+
}
|
|
48872
|
+
let lastOutputAt;
|
|
48873
|
+
try {
|
|
48874
|
+
const status = this.adapter.getStatus({ allowParse: false });
|
|
48875
|
+
lastOutputAt = typeof status?.lastOutputAt === "number" && Number.isFinite(status.lastOutputAt) ? status.lastOutputAt : 0;
|
|
48876
|
+
} catch {
|
|
48877
|
+
return;
|
|
48878
|
+
}
|
|
48879
|
+
const anchor = lastOutputAt > 0 ? lastOutputAt : this.startedAt;
|
|
48880
|
+
if (this.meshStallAnchorAt === -1) {
|
|
48881
|
+
this.meshStallAnchorAt = anchor;
|
|
48882
|
+
this.meshStallEmittedForAnchor = false;
|
|
48883
|
+
return;
|
|
48884
|
+
}
|
|
48885
|
+
if (anchor > this.meshStallAnchorAt) {
|
|
48886
|
+
this.meshStallAnchorAt = anchor;
|
|
48887
|
+
this.meshStallEmittedForAnchor = false;
|
|
48888
|
+
return;
|
|
48889
|
+
}
|
|
48890
|
+
if (this.meshStallEmittedForAnchor) return;
|
|
48891
|
+
const stalledMs = now - this.meshStallAnchorAt;
|
|
48892
|
+
if (stalledMs < _CliProviderInstance.MESH_WORKER_STALL_THRESHOLD_MS) return;
|
|
48893
|
+
this.meshStallEmittedForAnchor = true;
|
|
48894
|
+
let observedStatus = "unknown";
|
|
48895
|
+
try {
|
|
48896
|
+
const s2 = this.adapter.getStatus({ allowParse: false })?.status;
|
|
48897
|
+
if (typeof s2 === "string" && s2) observedStatus = s2;
|
|
48898
|
+
} catch {
|
|
48899
|
+
}
|
|
48900
|
+
if (this.isMeshWorkerSession()) {
|
|
48901
|
+
traceMeshEventStage("fired", this.meshTraceCtx("monitor:no_progress"), "mesh_worker_stall_watchdog");
|
|
48902
|
+
}
|
|
48903
|
+
const stalledSec = Math.round(stalledMs / 1e3);
|
|
48904
|
+
this.pushEvent({
|
|
48905
|
+
event: "monitor:no_progress",
|
|
48906
|
+
agentKey: `${this.type}:cli`,
|
|
48907
|
+
elapsedSec: stalledSec,
|
|
48908
|
+
timestamp: now,
|
|
48909
|
+
// MESH-STALL-WATCH marker: buildMeshSystemMessage generalizes the
|
|
48910
|
+
// coordinator message (generating-specific → "PTY output unchanged")
|
|
48911
|
+
// when this is set, since this watchdog fires status-agnostically.
|
|
48912
|
+
meshWorkerStall: true,
|
|
48913
|
+
lastOutputAt: this.meshStallAnchorAt,
|
|
48914
|
+
stalledMs,
|
|
48915
|
+
observedStatus,
|
|
48916
|
+
taskId: this.completingTurnTaskId()
|
|
48917
|
+
});
|
|
48918
|
+
}
|
|
48501
48919
|
/**
|
|
48502
48920
|
* AUTOAPPROVE-FLAP-RECUR (Fix A+B): how long a busy blip / modal scroll-out may
|
|
48503
48921
|
* persist before the in-progress settle gate is torn down. For a delegated
|
|
@@ -49459,6 +49877,10 @@ ${buttons.join("\n")}`;
|
|
|
49459
49877
|
this.completedDebouncePending = null;
|
|
49460
49878
|
continue;
|
|
49461
49879
|
}
|
|
49880
|
+
if (me.type === "monitor:no_progress" && this.isMeshWorkerSession()) {
|
|
49881
|
+
traceMeshEventDrop("mesh_worker_stall_watchdog_owns_no_progress", this.meshTraceCtx("monitor:no_progress"));
|
|
49882
|
+
continue;
|
|
49883
|
+
}
|
|
49462
49884
|
this.pushEvent({ event: me.type, agentKey: me.agentKey, message: me.message, elapsedSec: me.elapsedSec, timestamp: me.timestamp });
|
|
49463
49885
|
}
|
|
49464
49886
|
}
|
|
@@ -64056,7 +64478,22 @@ var MESH_FORWARDABLE_SESSION_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
64056
64478
|
// worker session that reaches the wrong daemon used to fuzzy-inject the task body into that
|
|
64057
64479
|
// daemon's own CLI session (TASKECHO coordinator self-echo). Forwarding to the owning daemon
|
|
64058
64480
|
// delivers it to the real worker instead. (findAdapter is also fail-closed as the backstop.)
|
|
64059
|
-
"agent_command"
|
|
64481
|
+
"agent_command",
|
|
64482
|
+
// read_terminal (MESH-READ-TERMINAL feature 2): mesh_read_terminal reads the CURRENT
|
|
64483
|
+
// rendered PTY viewport of a specific worker session. The live viewport lives ONLY on the
|
|
64484
|
+
// OWNING session's adapter, so when the target is a REMOTE worker the coordinator has no
|
|
64485
|
+
// local instance and the handler would return 'Session not found' — the exact
|
|
64486
|
+
// remote-worker forwarding gap of mission 6938892f. Forward it to the owning worker daemon
|
|
64487
|
+
// so it reads its own live screen. (It is read-only; unlike the mutations above it makes no
|
|
64488
|
+
// state change, but it is session-scoped identically and must reach the owning daemon.)
|
|
64489
|
+
"read_terminal",
|
|
64490
|
+
// send_keys (MESH-SEND-KEYS feature 3): mesh_send_keys injects a structured key sequence
|
|
64491
|
+
// into a specific worker session's PTY. The live PTY lives ONLY on the OWNING session's
|
|
64492
|
+
// adapter, so a remote-worker target must be forwarded to the owning daemon or the handler
|
|
64493
|
+
// returns 'Session not found' (same class as mission 6938892f). Unlike read_terminal this
|
|
64494
|
+
// MUTATES the worker PTY, so forwarding to the real owner (not a wrong local session) is
|
|
64495
|
+
// doubly important. The daemon re-enforces the destructive-key confirm gate after the forward.
|
|
64496
|
+
"send_keys"
|
|
64060
64497
|
]);
|
|
64061
64498
|
function normalizeCommandSource(source) {
|
|
64062
64499
|
switch (source) {
|
|
@@ -66201,7 +66638,13 @@ var ProviderInstanceManager = class {
|
|
|
66201
66638
|
if (this.tickTimer) return;
|
|
66202
66639
|
this.tickInterval = intervalMs || this.tickInterval;
|
|
66203
66640
|
this.tickTimer = setInterval(async () => {
|
|
66641
|
+
const now = Date.now();
|
|
66204
66642
|
for (const [id, instance] of this.instances) {
|
|
66643
|
+
try {
|
|
66644
|
+
instance.checkMeshWorkerStall?.(now);
|
|
66645
|
+
} catch (e) {
|
|
66646
|
+
LOG.warn("InstanceMgr", `[InstanceManager] Mesh stall check failed for ${id}: ${e.message}`);
|
|
66647
|
+
}
|
|
66205
66648
|
try {
|
|
66206
66649
|
await instance.onTick();
|
|
66207
66650
|
} catch (e) {
|
|
@@ -71674,7 +72117,7 @@ var SessionHostPtyTransportFactory = class {
|
|
|
71674
72117
|
};
|
|
71675
72118
|
|
|
71676
72119
|
// src/cli-adapters/raw-terminal-io.ts
|
|
71677
|
-
var
|
|
72120
|
+
var import_crypto11 = require("crypto");
|
|
71678
72121
|
var import_session_host_core11 = require("@adhdev/session-host-core");
|
|
71679
72122
|
var BASE_KEY_SEQUENCES = {
|
|
71680
72123
|
enter: "\r",
|
|
@@ -71772,7 +72215,7 @@ var RawTerminalAttachment = class _RawTerminalAttachment {
|
|
|
71772
72215
|
const sessionId = String(options.sessionId || "").trim();
|
|
71773
72216
|
if (!sessionId) throw new Error("sessionId is required");
|
|
71774
72217
|
const mode = options.mode || "read";
|
|
71775
|
-
const clientId = options.clientId || `raw-terminal-${process.pid}-${(0,
|
|
72218
|
+
const clientId = options.clientId || `raw-terminal-${process.pid}-${(0, import_crypto11.randomUUID)().slice(0, 8)}`;
|
|
71776
72219
|
const client = options.client || new import_session_host_core11.SessionHostClient({ endpoint: options.endpoint });
|
|
71777
72220
|
await client.connect();
|
|
71778
72221
|
const attachResponse = await client.request({
|
|
@@ -73338,6 +73781,7 @@ var V1_CONTRACT_VERSION = "1.0.0";
|
|
|
73338
73781
|
resetConfig,
|
|
73339
73782
|
resetDebugRuntimeConfig,
|
|
73340
73783
|
resetState,
|
|
73784
|
+
resolveAllowSendKeysDestructive,
|
|
73341
73785
|
resolveAutoConvergeCodeChange,
|
|
73342
73786
|
resolveChatMessageKind,
|
|
73343
73787
|
resolveConvergeRequiredTags,
|