@adhdev/daemon-core 0.9.82-rc.360 → 0.9.82-rc.361
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/commands/cli-manager.d.ts +13 -0
- package/dist/commands/low-family/index.d.ts +3 -0
- package/dist/commands/low-family/refine-config.d.ts +2 -0
- package/dist/commands/low-family/session-host.d.ts +2 -0
- package/dist/commands/low-family/spec-providerdev.d.ts +11 -0
- package/dist/commands/low-family/types.d.ts +16 -0
- package/dist/commands/router.d.ts +0 -1
- package/dist/index.js +2074 -2008
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2073 -2007
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-events-utils.d.ts +0 -2
- package/package.json +2 -2
- package/src/commands/cli-manager.ts +69 -4
- package/src/commands/low-family/index.ts +23 -0
- package/src/commands/low-family/refine-config.ts +106 -0
- package/src/commands/low-family/session-host.ts +274 -0
- package/src/commands/low-family/spec-providerdev.ts +217 -0
- package/src/commands/low-family/types.ts +19 -0
- package/src/commands/router.ts +15 -555
- package/src/mesh/contracts.ts +12 -3
- package/src/mesh/mesh-events-coordinator.ts +1 -1
- package/src/mesh/mesh-events-utils.ts +0 -20
- package/src/providers/cli-provider-instance.ts +16 -3
package/dist/index.mjs
CHANGED
|
@@ -311,10 +311,10 @@ function readInjected(value) {
|
|
|
311
311
|
}
|
|
312
312
|
function getDaemonBuildInfo() {
|
|
313
313
|
if (cached) return cached;
|
|
314
|
-
const commit = readInjected(true ? "
|
|
315
|
-
const commitShort = readInjected(true ? "
|
|
316
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
317
|
-
const builtAt = readInjected(true ? "2026-06-
|
|
314
|
+
const commit = readInjected(true ? "4c8bb331f13862335f134d8a1b97fa17eaca01e0" : void 0) ?? "unknown";
|
|
315
|
+
const commitShort = readInjected(true ? "4c8bb331" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
316
|
+
const version = readInjected(true ? "0.9.82-rc.361" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
317
|
+
const builtAt = readInjected(true ? "2026-06-23T11:56:37.654Z" : void 0);
|
|
318
318
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
319
319
|
return cached;
|
|
320
320
|
}
|
|
@@ -14161,7 +14161,7 @@ function forwardUnresolvedDelegateEvent(components, routing, event) {
|
|
|
14161
14161
|
}
|
|
14162
14162
|
function ackUnresolvedDelegateForwardByFingerprint(coordinatorDaemonId, eventName, payload) {
|
|
14163
14163
|
const match = peekUnresolvedDelegateForwards().find(
|
|
14164
|
-
(entry) => entry.coordinatorDaemonId
|
|
14164
|
+
(entry) => daemonIdsEquivalent(entry.coordinatorDaemonId, coordinatorDaemonId) && readNonEmptyString2(entry.payload.event) === eventName && readNonEmptyString2(entry.payload.targetSessionId || entry.payload.sessionId || entry.payload.instanceId) === readNonEmptyString2(payload.targetSessionId || payload.sessionId || payload.instanceId) && readNonEmptyString2(entry.payload.workspace) === readNonEmptyString2(payload.workspace)
|
|
14165
14165
|
);
|
|
14166
14166
|
if (match) ackUnresolvedDelegateForward(match.id);
|
|
14167
14167
|
}
|
|
@@ -15821,1010 +15821,1599 @@ var init_external_sources = __esm({
|
|
|
15821
15821
|
}
|
|
15822
15822
|
});
|
|
15823
15823
|
|
|
15824
|
-
// src/
|
|
15825
|
-
|
|
15826
|
-
|
|
15827
|
-
NodePtyTransportFactory: () => NodePtyTransportFactory
|
|
15828
|
-
});
|
|
15829
|
-
import * as os13 from "os";
|
|
15830
|
-
function loadNodePty() {
|
|
15831
|
-
if (cachedPty !== void 0) return cachedPty;
|
|
15832
|
-
try {
|
|
15833
|
-
cachedPty = __require("node-pty");
|
|
15834
|
-
ensureNodePtySpawnHelperPermissions();
|
|
15835
|
-
} catch {
|
|
15836
|
-
cachedPty = null;
|
|
15837
|
-
}
|
|
15838
|
-
return cachedPty;
|
|
15824
|
+
// src/providers/spec/fsm-types.ts
|
|
15825
|
+
function isV4Spec(raw) {
|
|
15826
|
+
return !!raw && typeof raw === "object" && raw.$schema === "adhdev:cli/spec@4";
|
|
15839
15827
|
}
|
|
15840
|
-
|
|
15841
|
-
|
|
15842
|
-
"src/cli-adapters/pty-transport.ts"() {
|
|
15843
|
-
"use strict";
|
|
15844
|
-
init_spawn_env();
|
|
15845
|
-
init_resolve_executable();
|
|
15846
|
-
NodePtyRuntimeTransport = class {
|
|
15847
|
-
constructor(handle) {
|
|
15848
|
-
this.handle = handle;
|
|
15849
|
-
}
|
|
15850
|
-
ready = Promise.resolve();
|
|
15851
|
-
terminalQueriesHandled = false;
|
|
15852
|
-
get pid() {
|
|
15853
|
-
return this.handle.pid;
|
|
15854
|
-
}
|
|
15855
|
-
write(data) {
|
|
15856
|
-
this.handle.write(data);
|
|
15857
|
-
}
|
|
15858
|
-
resize(cols, rows) {
|
|
15859
|
-
this.handle.resize(cols, rows);
|
|
15860
|
-
}
|
|
15861
|
-
kill() {
|
|
15862
|
-
this.handle.kill();
|
|
15863
|
-
}
|
|
15864
|
-
getMetadata() {
|
|
15865
|
-
return null;
|
|
15866
|
-
}
|
|
15867
|
-
onData(callback) {
|
|
15868
|
-
this.handle.onData(callback);
|
|
15869
|
-
}
|
|
15870
|
-
onExit(callback) {
|
|
15871
|
-
this.handle.onExit(callback);
|
|
15872
|
-
}
|
|
15873
|
-
};
|
|
15874
|
-
NodePtyTransportFactory = class {
|
|
15875
|
-
spawn(command, args, options) {
|
|
15876
|
-
const pty = loadNodePty();
|
|
15877
|
-
if (!pty) throw new Error("node-pty is not installed");
|
|
15878
|
-
let cwd = options.cwd;
|
|
15879
|
-
if (cwd) {
|
|
15880
|
-
try {
|
|
15881
|
-
const fs32 = __require("fs");
|
|
15882
|
-
const stat2 = fs32.statSync(cwd);
|
|
15883
|
-
if (!stat2.isDirectory()) cwd = os13.homedir();
|
|
15884
|
-
} catch {
|
|
15885
|
-
cwd = os13.homedir();
|
|
15886
|
-
}
|
|
15887
|
-
}
|
|
15888
|
-
const handle = pty.spawn(resolveWin32Executable(command), args, {
|
|
15889
|
-
name: "xterm-256color",
|
|
15890
|
-
cols: options.cols,
|
|
15891
|
-
rows: options.rows,
|
|
15892
|
-
cwd,
|
|
15893
|
-
env: options.env
|
|
15894
|
-
});
|
|
15895
|
-
return new NodePtyRuntimeTransport(handle);
|
|
15896
|
-
}
|
|
15897
|
-
};
|
|
15898
|
-
}
|
|
15899
|
-
});
|
|
15900
|
-
|
|
15901
|
-
// src/providers/sdk/v1/builders/cli/visible-region.ts
|
|
15902
|
-
function compile(re, flags) {
|
|
15903
|
-
try {
|
|
15904
|
-
return new RegExp(re, flags ?? "");
|
|
15905
|
-
} catch (e) {
|
|
15906
|
-
throw new Error(`Invalid visible-region anchor regex /${re}/${flags ?? ""}: ${e.message}`);
|
|
15907
|
-
}
|
|
15828
|
+
function initialState(spec) {
|
|
15829
|
+
return spec.states.find((s2) => s2.initial) ?? spec.states[0];
|
|
15908
15830
|
}
|
|
15909
|
-
function
|
|
15910
|
-
|
|
15911
|
-
const iter = new RegExp(re.source, re.flags.replace("g", "") + "g");
|
|
15912
|
-
let m;
|
|
15913
|
-
while ((m = iter.exec(text)) !== null) {
|
|
15914
|
-
results.push(m);
|
|
15915
|
-
if (m[0].length === 0) iter.lastIndex += 1;
|
|
15916
|
-
}
|
|
15917
|
-
return results;
|
|
15831
|
+
function stateById(spec, id) {
|
|
15832
|
+
return spec.states.find((s2) => s2.id === id);
|
|
15918
15833
|
}
|
|
15919
|
-
function
|
|
15920
|
-
|
|
15921
|
-
|
|
15922
|
-
|
|
15923
|
-
|
|
15924
|
-
|
|
15925
|
-
|
|
15926
|
-
const limit = spec.tailChars ?? 4e3;
|
|
15927
|
-
if (text.length <= limit) return text;
|
|
15928
|
-
return text.slice(-limit);
|
|
15929
|
-
}
|
|
15930
|
-
case "between-anchors": {
|
|
15931
|
-
const anchors = spec.anchors;
|
|
15932
|
-
if (!anchors?.top && !anchors?.bottom) return text;
|
|
15933
|
-
const selectLast = (spec.selectAnchor ?? "first") === "last";
|
|
15934
|
-
let topEnd = 0;
|
|
15935
|
-
if (anchors.top) {
|
|
15936
|
-
const topRe = compile(anchors.top.pattern, anchors.top.flags);
|
|
15937
|
-
const topMatches = findAllMatches(topRe, text);
|
|
15938
|
-
if (topMatches.length === 0) {
|
|
15939
|
-
return text;
|
|
15940
|
-
}
|
|
15941
|
-
const chosen = selectLast ? topMatches[topMatches.length - 1] : topMatches[0];
|
|
15942
|
-
topEnd = chosen.index + chosen[0].length;
|
|
15943
|
-
}
|
|
15944
|
-
let bottomStart = text.length;
|
|
15945
|
-
if (anchors.bottom) {
|
|
15946
|
-
const bottomRe = compile(anchors.bottom.pattern, anchors.bottom.flags);
|
|
15947
|
-
const searchFrom = topEnd;
|
|
15948
|
-
const suffix = text.slice(searchFrom);
|
|
15949
|
-
const bottomMatches = findAllMatches(bottomRe, suffix);
|
|
15950
|
-
if (bottomMatches.length === 0) {
|
|
15951
|
-
return text;
|
|
15952
|
-
}
|
|
15953
|
-
const chosen = selectLast ? bottomMatches[bottomMatches.length - 1] : bottomMatches[0];
|
|
15954
|
-
bottomStart = searchFrom + chosen.index;
|
|
15955
|
-
}
|
|
15956
|
-
if (topEnd >= bottomStart) {
|
|
15957
|
-
return text;
|
|
15958
|
-
}
|
|
15959
|
-
return text.slice(topEnd, bottomStart);
|
|
15960
|
-
}
|
|
15961
|
-
default:
|
|
15962
|
-
return text;
|
|
15963
|
-
}
|
|
15834
|
+
function outgoingTransitions(spec, stateId) {
|
|
15835
|
+
const matches = spec.transitions.filter((t) => {
|
|
15836
|
+
if (t.from === "*") return true;
|
|
15837
|
+
if (Array.isArray(t.from)) return t.from.includes(stateId);
|
|
15838
|
+
return t.from === stateId;
|
|
15839
|
+
});
|
|
15840
|
+
return matches.map((t, i) => ({ t, i })).sort((a, b) => (b.t.priority ?? 0) - (a.t.priority ?? 0) || a.i - b.i).map((x) => x.t);
|
|
15964
15841
|
}
|
|
15965
|
-
|
|
15966
|
-
|
|
15842
|
+
function statusForState(state) {
|
|
15843
|
+
if (state.status) return state.status;
|
|
15844
|
+
if (state.modal) return "approval";
|
|
15845
|
+
if (state.id === "busy" || state.id === "generating") return "generating";
|
|
15846
|
+
return "idle";
|
|
15847
|
+
}
|
|
15848
|
+
function modalKindForState(state) {
|
|
15849
|
+
if (state.modal_kind) return state.modal_kind;
|
|
15850
|
+
if (state.modal) return "approval";
|
|
15851
|
+
return null;
|
|
15852
|
+
}
|
|
15853
|
+
var init_fsm_types = __esm({
|
|
15854
|
+
"src/providers/spec/fsm-types.ts"() {
|
|
15967
15855
|
"use strict";
|
|
15968
15856
|
}
|
|
15969
15857
|
});
|
|
15970
15858
|
|
|
15971
|
-
// src/providers/
|
|
15972
|
-
|
|
15859
|
+
// src/providers/spec/fsm-loader.ts
|
|
15860
|
+
var fsm_loader_exports = {};
|
|
15861
|
+
__export(fsm_loader_exports, {
|
|
15862
|
+
loadFsmSpec: () => loadFsmSpec,
|
|
15863
|
+
validateFsmSpec: () => validateFsmSpec
|
|
15864
|
+
});
|
|
15865
|
+
import * as fs10 from "fs";
|
|
15866
|
+
function loadFsmSpec(sourcePath) {
|
|
15867
|
+
let raw;
|
|
15973
15868
|
try {
|
|
15974
|
-
|
|
15975
|
-
} catch (
|
|
15976
|
-
|
|
15869
|
+
raw = JSON.parse(fs10.readFileSync(sourcePath, "utf8"));
|
|
15870
|
+
} catch (err) {
|
|
15871
|
+
return { ok: false, errors: [`Failed to read/parse spec: ${err.message}`], sourcePath };
|
|
15977
15872
|
}
|
|
15873
|
+
const errors = validateFsmSpec(raw);
|
|
15874
|
+
if (errors.length) return { ok: false, errors, sourcePath };
|
|
15875
|
+
return { ok: true, spec: raw, sourcePath };
|
|
15978
15876
|
}
|
|
15979
|
-
function
|
|
15980
|
-
|
|
15981
|
-
|
|
15982
|
-
|
|
15983
|
-
|
|
15984
|
-
|
|
15985
|
-
|
|
15986
|
-
|
|
15987
|
-
|
|
15988
|
-
|
|
15989
|
-
|
|
15990
|
-
|
|
15991
|
-
|
|
15992
|
-
|
|
15993
|
-
|
|
15994
|
-
case void 0:
|
|
15995
|
-
default:
|
|
15996
|
-
return takeTail(input.screenText ?? "", lines);
|
|
15877
|
+
function validateFsmSpec(raw) {
|
|
15878
|
+
const errs = [];
|
|
15879
|
+
if (!isV4Spec(raw)) return ['$schema must be "adhdev:cli/spec@4"'];
|
|
15880
|
+
const spec = raw;
|
|
15881
|
+
if (!spec.id) errs.push("id is required");
|
|
15882
|
+
if (!spec.binary) errs.push("binary is required");
|
|
15883
|
+
if (!spec.send_message?.submit_key) errs.push("send_message.submit_key is required");
|
|
15884
|
+
if (spec.pre_launch_trust !== void 0) {
|
|
15885
|
+
const t = spec.pre_launch_trust;
|
|
15886
|
+
if (!t || typeof t !== "object" || Array.isArray(t)) {
|
|
15887
|
+
errs.push("pre_launch_trust must be an object");
|
|
15888
|
+
} else {
|
|
15889
|
+
if (typeof t.settings_path !== "string" || !t.settings_path) errs.push("pre_launch_trust.settings_path is required");
|
|
15890
|
+
if (typeof t.key !== "string" || !t.key) errs.push("pre_launch_trust.key is required");
|
|
15891
|
+
}
|
|
15997
15892
|
}
|
|
15998
|
-
|
|
15999
|
-
|
|
16000
|
-
|
|
16001
|
-
}
|
|
16002
|
-
|
|
16003
|
-
const prompt = compile2(spec.regex, spec.flags ?? "m");
|
|
16004
|
-
const footers = (spec.withFooter ?? []).map((f) => {
|
|
16005
|
-
if (f.kind === "regex") {
|
|
16006
|
-
const re = compile2(f.pattern, f.flags ?? "i");
|
|
16007
|
-
return { test: (s2) => re.test(s2) };
|
|
15893
|
+
if (spec.refocus_when_stalled_ms !== void 0) {
|
|
15894
|
+
if (typeof spec.refocus_when_stalled_ms !== "number" || !(spec.refocus_when_stalled_ms > 0)) {
|
|
15895
|
+
errs.push("refocus_when_stalled_ms must be a positive number");
|
|
15896
|
+
} else if (!Array.isArray(spec.send_on_spawn) || spec.send_on_spawn.length === 0) {
|
|
15897
|
+
errs.push("refocus_when_stalled_ms requires send_on_spawn (the wake sequence to re-inject)");
|
|
16008
15898
|
}
|
|
16009
|
-
const needle = f.pattern.toLowerCase();
|
|
16010
|
-
return { test: (s2) => s2.toLowerCase().includes(needle) };
|
|
16011
|
-
});
|
|
16012
|
-
return { prompt, footers };
|
|
16013
|
-
}
|
|
16014
|
-
function extractButtonLabels(spec, text) {
|
|
16015
|
-
if (!text) return [];
|
|
16016
|
-
const flags = spec.buttonFlags && spec.buttonFlags.includes("m") ? spec.buttonFlags : `${spec.buttonFlags ?? ""}m`;
|
|
16017
|
-
const buttonRe = compile2(spec.buttonPattern, flags);
|
|
16018
|
-
const labelGroup = Number.isInteger(spec.buttonLabelGroup) && (spec.buttonLabelGroup ?? 0) > 0 ? spec.buttonLabelGroup : 1;
|
|
16019
|
-
const out = [];
|
|
16020
|
-
for (const line of text.split("\n")) {
|
|
16021
|
-
buttonRe.lastIndex = 0;
|
|
16022
|
-
const m = buttonRe.exec(line);
|
|
16023
|
-
if (!m) continue;
|
|
16024
|
-
const captured = m[labelGroup] ?? (labelGroup === 1 && m.length > 2 ? m[m.length - 1] : void 0);
|
|
16025
|
-
if (captured && captured.trim()) out.push(captured.trim());
|
|
16026
15899
|
}
|
|
16027
|
-
|
|
16028
|
-
|
|
16029
|
-
|
|
16030
|
-
const labels = extractButtonLabels(spec, text);
|
|
16031
|
-
if (labels.length < 2) return false;
|
|
16032
|
-
if (pickApprovalButton(labels).index < 0) return false;
|
|
16033
|
-
return hasNegativeApprovalOption(labels);
|
|
16034
|
-
}
|
|
16035
|
-
function modalMatches(spec, input) {
|
|
16036
|
-
const text = input.screenText ?? "";
|
|
16037
|
-
const question = compile2(spec.questionPattern, spec.questionFlags ?? "i");
|
|
16038
|
-
if (question.test(text)) return true;
|
|
16039
|
-
for (const variant of spec.questionVariants ?? []) {
|
|
16040
|
-
const re = compile2(variant.regex, variant.flags ?? "i");
|
|
16041
|
-
if (re.test(text)) return true;
|
|
15900
|
+
if (!Array.isArray(spec.states) || spec.states.length === 0) {
|
|
15901
|
+
errs.push("states[] must be a non-empty array");
|
|
15902
|
+
return errs;
|
|
16042
15903
|
}
|
|
16043
|
-
if (
|
|
16044
|
-
|
|
16045
|
-
|
|
16046
|
-
|
|
16047
|
-
|
|
16048
|
-
|
|
16049
|
-
|
|
16050
|
-
|
|
16051
|
-
|
|
16052
|
-
|
|
16053
|
-
case "modal": {
|
|
16054
|
-
if (!spec.modal) return null;
|
|
16055
|
-
return modalMatches(spec.modal, input) ? "waiting_approval" : null;
|
|
15904
|
+
if (!Array.isArray(spec.transitions)) {
|
|
15905
|
+
errs.push("transitions[] must be an array");
|
|
15906
|
+
return errs;
|
|
15907
|
+
}
|
|
15908
|
+
const ids = /* @__PURE__ */ new Set();
|
|
15909
|
+
let initialCount = 0;
|
|
15910
|
+
for (const [i, s2] of spec.states.entries()) {
|
|
15911
|
+
if (!s2.id) {
|
|
15912
|
+
errs.push(`states[${i}].id is required`);
|
|
15913
|
+
continue;
|
|
16056
15914
|
}
|
|
16057
|
-
|
|
16058
|
-
|
|
16059
|
-
|
|
16060
|
-
|
|
16061
|
-
|
|
16062
|
-
|
|
15915
|
+
if (ids.has(s2.id)) errs.push(`states[${i}].id "${s2.id}" is duplicated`);
|
|
15916
|
+
ids.add(s2.id);
|
|
15917
|
+
if (!s2.label) errs.push(`states[${i}].label is required`);
|
|
15918
|
+
if (s2.initial) initialCount += 1;
|
|
15919
|
+
if (s2.status && !["idle", "generating", "approval"].includes(s2.status)) {
|
|
15920
|
+
errs.push(`states[${i}].status "${s2.status}" must be idle|generating|approval`);
|
|
16063
15921
|
}
|
|
16064
|
-
// Groups declared in the catalog but not yet implemented as builder steps
|
|
16065
|
-
// return null so they are no-ops in dispatch — declaring them in `order`
|
|
16066
|
-
// is forward-compatible. Phase 2 Week 8+ will wire them.
|
|
16067
|
-
case "cue-ordering":
|
|
16068
|
-
case "error-detection":
|
|
16069
|
-
case "approval-stitching":
|
|
16070
|
-
return null;
|
|
16071
|
-
default:
|
|
16072
|
-
return null;
|
|
16073
15922
|
}
|
|
16074
|
-
|
|
16075
|
-
|
|
16076
|
-
const
|
|
16077
|
-
const
|
|
16078
|
-
|
|
16079
|
-
|
|
16080
|
-
|
|
16081
|
-
const effectiveInput = spec.visibleRegion ? {
|
|
16082
|
-
...input,
|
|
16083
|
-
screenText: applyVisibleRegion(spec.visibleRegion, input.screenText ?? ""),
|
|
16084
|
-
tail: applyVisibleRegion(spec.visibleRegion, input.tail)
|
|
16085
|
-
} : input;
|
|
16086
|
-
for (const group of order) {
|
|
16087
|
-
const verdict = evaluateGroup(group, spec, effectiveInput, compiled);
|
|
16088
|
-
if (verdict !== null) return verdict;
|
|
15923
|
+
if (initialCount === 0) errs.push("exactly one state must have initial:true (none found)");
|
|
15924
|
+
if (initialCount > 1) errs.push(`exactly one state must have initial:true (${initialCount} found)`);
|
|
15925
|
+
const sectionIds = new Set(Object.keys(spec.sections ?? {}));
|
|
15926
|
+
for (const [i, t] of spec.transitions.entries()) {
|
|
15927
|
+
const froms = t.from === "*" ? [] : Array.isArray(t.from) ? t.from : [t.from];
|
|
15928
|
+
for (const f of froms) {
|
|
15929
|
+
if (!ids.has(f)) errs.push(`transitions[${i}].from references unknown state "${f}"`);
|
|
16089
15930
|
}
|
|
16090
|
-
|
|
16091
|
-
|
|
16092
|
-
}
|
|
16093
|
-
|
|
16094
|
-
var init_detect_status = __esm({
|
|
16095
|
-
"src/providers/sdk/v1/builders/cli/detect-status.ts"() {
|
|
16096
|
-
"use strict";
|
|
16097
|
-
init_visible_region();
|
|
16098
|
-
init_approval_utils();
|
|
16099
|
-
DEFAULT_ORDER = ["spinner", "modal", "settled-prompt"];
|
|
15931
|
+
if (t.from !== "*" && froms.length === 0) errs.push(`transitions[${i}].from is required`);
|
|
15932
|
+
if (!t.to) errs.push(`transitions[${i}].to is required`);
|
|
15933
|
+
else if (!ids.has(t.to)) errs.push(`transitions[${i}].to references unknown state "${t.to}"`);
|
|
15934
|
+
if (t.when) errs.push(...validateCondition(t.when, sectionIds, `transitions[${i}].when`));
|
|
16100
15935
|
}
|
|
16101
|
-
|
|
16102
|
-
|
|
16103
|
-
|
|
16104
|
-
|
|
16105
|
-
|
|
16106
|
-
return new RegExp(re, flags);
|
|
16107
|
-
} catch (e) {
|
|
16108
|
-
throw new Error(`Invalid regex /${re}/${flags ?? ""}: ${e.message}`);
|
|
15936
|
+
for (const [i, s2] of spec.states.entries()) {
|
|
15937
|
+
const sec = s2.extract?.title?.section;
|
|
15938
|
+
if (sec && !sectionIds.has(sec)) errs.push(`states[${i}].extract.title.section "${sec}" unknown`);
|
|
15939
|
+
const bsec = s2.extract?.buttons?.section;
|
|
15940
|
+
if (bsec && !sectionIds.has(bsec)) errs.push(`states[${i}].extract.buttons.section "${bsec}" unknown`);
|
|
16109
15941
|
}
|
|
15942
|
+
return errs;
|
|
16110
15943
|
}
|
|
16111
|
-
function
|
|
16112
|
-
const
|
|
16113
|
-
|
|
16114
|
-
|
|
16115
|
-
|
|
16116
|
-
|
|
16117
|
-
const re = compile3(variant.regex, variant.flags ?? "i");
|
|
16118
|
-
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
16119
|
-
if (re.test(lines[i])) return { index: i, matchedSource: variant.label ?? "variant" };
|
|
16120
|
-
}
|
|
15944
|
+
function validateCondition(c, sectionIds, path42) {
|
|
15945
|
+
const errs = [];
|
|
15946
|
+
const w = c;
|
|
15947
|
+
if ("all" in w) {
|
|
15948
|
+
w.all.forEach((x, i) => errs.push(...validateCondition(x, sectionIds, `${path42}.all[${i}]`)));
|
|
15949
|
+
return errs;
|
|
16121
15950
|
}
|
|
16122
|
-
|
|
16123
|
-
}
|
|
16124
|
-
|
|
16125
|
-
const scope = spec.scope ?? "between-last-two-separators";
|
|
16126
|
-
if (scope === "whole-screen") {
|
|
16127
|
-
return { start: 0, end: lines.length };
|
|
15951
|
+
if ("any" in w) {
|
|
15952
|
+
w.any.forEach((x, i) => errs.push(...validateCondition(x, sectionIds, `${path42}.any[${i}]`)));
|
|
15953
|
+
return errs;
|
|
16128
15954
|
}
|
|
16129
|
-
if (
|
|
16130
|
-
|
|
16131
|
-
return
|
|
16132
|
-
start: Math.max(0, questionIndex - 2),
|
|
16133
|
-
end: Math.min(lines.length, questionIndex + window)
|
|
16134
|
-
};
|
|
15955
|
+
if ("not" in w) {
|
|
15956
|
+
errs.push(...validateCondition(w.not, sectionIds, `${path42}.not`));
|
|
15957
|
+
return errs;
|
|
16135
15958
|
}
|
|
16136
|
-
|
|
16137
|
-
|
|
16138
|
-
|
|
16139
|
-
|
|
16140
|
-
|
|
16141
|
-
|
|
16142
|
-
prevSep = i;
|
|
16143
|
-
break;
|
|
16144
|
-
}
|
|
15959
|
+
if ("matches" in w) {
|
|
15960
|
+
if (w.section && !sectionIds.has(w.section)) errs.push(`${path42}.section "${w.section}" unknown`);
|
|
15961
|
+
try {
|
|
15962
|
+
new RegExp(w.matches, w.flags ?? "i");
|
|
15963
|
+
} catch (e) {
|
|
15964
|
+
errs.push(`${path42}.matches invalid regex: ${e.message}`);
|
|
16145
15965
|
}
|
|
15966
|
+
return errs;
|
|
16146
15967
|
}
|
|
16147
|
-
if (
|
|
16148
|
-
|
|
16149
|
-
|
|
16150
|
-
|
|
16151
|
-
start: Math.max(0, questionIndex - 4),
|
|
16152
|
-
end: Math.min(lines.length, questionIndex + 16)
|
|
16153
|
-
};
|
|
16154
|
-
}
|
|
16155
|
-
function extractButtons(spec, lines, windowStart, windowEnd) {
|
|
16156
|
-
const buttonRe = compile3(spec.buttonPattern, spec.buttonFlags ?? "m");
|
|
16157
|
-
const out = [];
|
|
16158
|
-
const labelGroup = Number.isInteger(spec.buttonLabelGroup) && (spec.buttonLabelGroup ?? 0) > 0 ? spec.buttonLabelGroup : 1;
|
|
16159
|
-
let i = windowStart;
|
|
16160
|
-
while (i < windowEnd) {
|
|
16161
|
-
const line = lines[i];
|
|
16162
|
-
const m = buttonRe.exec(line);
|
|
16163
|
-
const captured = m?.[labelGroup] ?? (labelGroup === 1 && m && m.length > 2 ? m[m.length - 1] : void 0);
|
|
16164
|
-
if (m && captured) {
|
|
16165
|
-
let label = captured.trim();
|
|
16166
|
-
if (spec.continuationLines) {
|
|
16167
|
-
let j = i + 1;
|
|
16168
|
-
while (j < windowEnd) {
|
|
16169
|
-
const next = lines[j];
|
|
16170
|
-
if (!next.trim()) break;
|
|
16171
|
-
if (buttonRe.test(next)) break;
|
|
16172
|
-
if (!/^\s+/.test(next)) break;
|
|
16173
|
-
label += " " + next.trim();
|
|
16174
|
-
j += 1;
|
|
16175
|
-
}
|
|
16176
|
-
i = j;
|
|
16177
|
-
} else {
|
|
16178
|
-
i += 1;
|
|
16179
|
-
}
|
|
16180
|
-
out.push(label);
|
|
16181
|
-
} else {
|
|
16182
|
-
i += 1;
|
|
16183
|
-
}
|
|
15968
|
+
if ("cursor_above" in w && "changed" in w) return errs;
|
|
15969
|
+
if ("elapsed_ms" in w) {
|
|
15970
|
+
if (typeof w.elapsed_ms !== "number") errs.push(`${path42}.elapsed_ms must be a number`);
|
|
15971
|
+
return errs;
|
|
16184
15972
|
}
|
|
16185
|
-
|
|
16186
|
-
}
|
|
16187
|
-
|
|
16188
|
-
const messageParts = [];
|
|
16189
|
-
if (spec.contextHeader) {
|
|
16190
|
-
const ctxRe = compile3(spec.contextHeader.regex, (spec.contextHeader.flags ?? "i") + "m");
|
|
16191
|
-
const haystack = lines.slice(windowStart, windowEnd).join("\n");
|
|
16192
|
-
const m = ctxRe.exec(haystack);
|
|
16193
|
-
if (m) messageParts.push(m[1] ? m[1].trim() : m[0].trim());
|
|
15973
|
+
if ("stable_ms" in w) {
|
|
15974
|
+
if (typeof w.stable_ms !== "number") errs.push(`${path42}.stable_ms must be a number`);
|
|
15975
|
+
return errs;
|
|
16194
15976
|
}
|
|
16195
|
-
|
|
16196
|
-
return
|
|
15977
|
+
errs.push(`${path42} is not a recognized condition`);
|
|
15978
|
+
return errs;
|
|
16197
15979
|
}
|
|
16198
|
-
|
|
16199
|
-
|
|
16200
|
-
|
|
16201
|
-
|
|
16202
|
-
const re = compile3(spec.inlineButtonPattern, flags);
|
|
16203
|
-
const out = [];
|
|
16204
|
-
for (let i = windowStart; i < windowEnd; i += 1) {
|
|
16205
|
-
let m;
|
|
16206
|
-
re.lastIndex = 0;
|
|
16207
|
-
while ((m = re.exec(lines[i])) !== null) {
|
|
16208
|
-
const label = (m[1] ?? m[0]).trim();
|
|
16209
|
-
if (label && !out.includes(label)) out.push(label);
|
|
16210
|
-
if (m.index === re.lastIndex) re.lastIndex += 1;
|
|
16211
|
-
}
|
|
15980
|
+
var init_fsm_loader = __esm({
|
|
15981
|
+
"src/providers/spec/fsm-loader.ts"() {
|
|
15982
|
+
"use strict";
|
|
15983
|
+
init_fsm_types();
|
|
16212
15984
|
}
|
|
16213
|
-
|
|
15985
|
+
});
|
|
15986
|
+
|
|
15987
|
+
// src/providers/spec/evaluator.ts
|
|
15988
|
+
var evaluator_exports = {};
|
|
15989
|
+
__export(evaluator_exports, {
|
|
15990
|
+
evaluateCondition: () => evaluateCondition,
|
|
15991
|
+
extractButtonsFromRule: () => extractButtonsFromRule,
|
|
15992
|
+
extractTitle: () => extractTitle,
|
|
15993
|
+
lastContiguousNumberedBlock: () => lastContiguousNumberedBlock,
|
|
15994
|
+
resolveSections: () => resolveSections,
|
|
15995
|
+
sectionText: () => sectionText
|
|
15996
|
+
});
|
|
15997
|
+
function resolveSize(size, total) {
|
|
15998
|
+
if (size === void 0) return 0;
|
|
15999
|
+
if (typeof size === "number") return Math.max(0, Math.min(total, size));
|
|
16000
|
+
const m = /^(\d+(?:\.\d+)?)%$/.exec(size);
|
|
16001
|
+
if (!m) return 0;
|
|
16002
|
+
const pct = Number(m[1]);
|
|
16003
|
+
return Math.max(0, Math.min(total, Math.round(total * pct / 100)));
|
|
16214
16004
|
}
|
|
16215
|
-
function
|
|
16216
|
-
const
|
|
16217
|
-
|
|
16218
|
-
|
|
16219
|
-
|
|
16220
|
-
|
|
16221
|
-
|
|
16222
|
-
|
|
16223
|
-
|
|
16224
|
-
|
|
16225
|
-
|
|
16226
|
-
|
|
16227
|
-
|
|
16228
|
-
|
|
16005
|
+
function resolveSections(sectionsObj, lines) {
|
|
16006
|
+
const total = lines.length;
|
|
16007
|
+
const anchored = /* @__PURE__ */ new Map();
|
|
16008
|
+
const sectionEntries = Object.entries(sectionsObj);
|
|
16009
|
+
for (const [id, sec] of sectionEntries) {
|
|
16010
|
+
let from = 0;
|
|
16011
|
+
let to = total;
|
|
16012
|
+
if (sec.anchor !== void 0) {
|
|
16013
|
+
try {
|
|
16014
|
+
const anchorPatterns = Array.isArray(sec.anchor) ? sec.anchor : [sec.anchor];
|
|
16015
|
+
const sharedCtx = Array.isArray(sec.anchor_context) ? null : sec.anchor_context ?? null;
|
|
16016
|
+
const ctxList = Array.isArray(sec.anchor_context) ? sec.anchor_context : anchorPatterns.map(() => sharedCtx);
|
|
16017
|
+
const candidates = anchorPatterns.map((pattern, k) => {
|
|
16018
|
+
const ctx = ctxList[k] ?? null;
|
|
16019
|
+
return {
|
|
16020
|
+
re: new RegExp(pattern, sec.anchor_flags ?? ""),
|
|
16021
|
+
prevRe: ctx?.prev !== void 0 ? new RegExp(ctx.prev, ctx.prev_flags ?? "") : null,
|
|
16022
|
+
nextRe: ctx?.next !== void 0 ? new RegExp(ctx.next, ctx.next_flags ?? "") : null
|
|
16023
|
+
};
|
|
16024
|
+
});
|
|
16025
|
+
const matchesCandidate = (c, i) => c.re.test(lines[i]) && (c.prevRe === null || i > 0 && c.prevRe.test(lines[i - 1])) && (c.nextRe === null || i < total - 1 && c.nextRe.test(lines[i + 1]));
|
|
16026
|
+
let idx = -1;
|
|
16027
|
+
for (const c of candidates) {
|
|
16028
|
+
let candIdx = -1;
|
|
16029
|
+
if (sec.anchor_last) {
|
|
16030
|
+
for (let i = total - 1; i >= 0; i--) {
|
|
16031
|
+
if (matchesCandidate(c, i)) {
|
|
16032
|
+
candIdx = i;
|
|
16033
|
+
break;
|
|
16034
|
+
}
|
|
16035
|
+
}
|
|
16036
|
+
} else {
|
|
16037
|
+
for (let i = 0; i < total; i++) {
|
|
16038
|
+
if (matchesCandidate(c, i)) {
|
|
16039
|
+
candIdx = i;
|
|
16040
|
+
break;
|
|
16041
|
+
}
|
|
16042
|
+
}
|
|
16043
|
+
}
|
|
16044
|
+
if (candIdx !== -1 && (idx === -1 || candIdx < idx)) idx = candIdx;
|
|
16045
|
+
}
|
|
16046
|
+
if (idx !== -1) {
|
|
16047
|
+
from = idx;
|
|
16048
|
+
to = total;
|
|
16049
|
+
if (sec.until_regex !== void 0) {
|
|
16050
|
+
try {
|
|
16051
|
+
const ure = new RegExp(sec.until_regex, sec.until_regex_flags ?? "");
|
|
16052
|
+
const end = lines.findIndex((l, i) => i > idx && ure.test(l));
|
|
16053
|
+
if (end !== -1) to = end;
|
|
16054
|
+
} catch {
|
|
16055
|
+
}
|
|
16056
|
+
} else if (sec.lines !== void 0) {
|
|
16057
|
+
to = Math.min(total, from + sec.lines);
|
|
16058
|
+
}
|
|
16059
|
+
}
|
|
16060
|
+
} catch {
|
|
16061
|
+
}
|
|
16062
|
+
} else if (sec.from_top !== void 0) {
|
|
16063
|
+
from = resolveSize(sec.from_top, total);
|
|
16064
|
+
to = total;
|
|
16065
|
+
} else if (sec.from_bottom !== void 0) {
|
|
16066
|
+
const sz = resolveSize(sec.from_bottom, total);
|
|
16067
|
+
from = total - sz;
|
|
16068
|
+
to = total;
|
|
16229
16069
|
}
|
|
16230
|
-
|
|
16231
|
-
const message = buildMessage(spec, lines, question.index, start, end);
|
|
16232
|
-
return { message, buttons };
|
|
16233
|
-
};
|
|
16234
|
-
}
|
|
16235
|
-
var SEPARATOR_RE;
|
|
16236
|
-
var init_parse_approval = __esm({
|
|
16237
|
-
"src/providers/sdk/v1/builders/cli/parse-approval.ts"() {
|
|
16238
|
-
"use strict";
|
|
16239
|
-
init_visible_region();
|
|
16240
|
-
SEPARATOR_RE = /^(?:─|━|═|━){10,}\s*$/;
|
|
16070
|
+
anchored.set(id, { fromLine: from, toLine: to });
|
|
16241
16071
|
}
|
|
16242
|
-
|
|
16243
|
-
|
|
16244
|
-
|
|
16245
|
-
|
|
16246
|
-
|
|
16247
|
-
|
|
16072
|
+
const resolved = [];
|
|
16073
|
+
for (const [id, sec] of sectionEntries) {
|
|
16074
|
+
let { fromLine, toLine } = anchored.get(id);
|
|
16075
|
+
if (sec.until !== void 0) {
|
|
16076
|
+
if (sec.until.startsWith("^")) {
|
|
16077
|
+
try {
|
|
16078
|
+
const ure = new RegExp(sec.until);
|
|
16079
|
+
const end = lines.findIndex((l, i) => i > fromLine && ure.test(l));
|
|
16080
|
+
if (end !== -1) toLine = end;
|
|
16081
|
+
} catch {
|
|
16082
|
+
}
|
|
16083
|
+
} else {
|
|
16084
|
+
const target = anchored.get(sec.until);
|
|
16085
|
+
if (target) toLine = target.fromLine;
|
|
16086
|
+
}
|
|
16087
|
+
}
|
|
16088
|
+
if (toLine < fromLine) toLine = fromLine;
|
|
16089
|
+
const text = lines.slice(fromLine, toLine).join("\n");
|
|
16090
|
+
resolved.push({ id, fromLine, toLine, text });
|
|
16091
|
+
}
|
|
16092
|
+
return resolved;
|
|
16248
16093
|
}
|
|
16249
|
-
function
|
|
16250
|
-
|
|
16094
|
+
function sectionText(sections, sectionId, fullScreen) {
|
|
16095
|
+
if (!sectionId) return fullScreen;
|
|
16096
|
+
const found = sections.find((s2) => s2.id === sectionId);
|
|
16097
|
+
return found ? found.text : "";
|
|
16251
16098
|
}
|
|
16252
|
-
function
|
|
16253
|
-
|
|
16254
|
-
if (scope === "screen") return String(input.screenText || input.screen?.text || "");
|
|
16255
|
-
if (scope === "tail") return String(input.tail || input.recentBuffer || input.buffer || "");
|
|
16256
|
-
return String(input.buffer || input.rawBuffer || input.screenText || "");
|
|
16099
|
+
function isRegexCondition(c) {
|
|
16100
|
+
return "matches" in c;
|
|
16257
16101
|
}
|
|
16258
|
-
function
|
|
16259
|
-
return
|
|
16102
|
+
function isChangedCondition(c) {
|
|
16103
|
+
return "cursor_above" in c && "changed" in c;
|
|
16260
16104
|
}
|
|
16261
|
-
function
|
|
16262
|
-
|
|
16263
|
-
let turnIndex = -1;
|
|
16264
|
-
return list.map((message, index) => {
|
|
16265
|
-
const role = message?.role || "assistant";
|
|
16266
|
-
const kind = message?.kind || "standard";
|
|
16267
|
-
const content = typeof message?.content === "string" ? message.content : "";
|
|
16268
|
-
if (role === "user" || turnIndex < 0) turnIndex += 1;
|
|
16269
|
-
const seed = [role, kind, "", index, content].join("\n");
|
|
16270
|
-
const providerUnitKey = `v2-pty:${role}:${kind}:${index}:${stableHash(seed)}`;
|
|
16271
|
-
const bubbleId = `bubble:${providerUnitKey}`;
|
|
16272
|
-
const turnKey = `turn:${turnIndex}`;
|
|
16273
|
-
const isStreamingTail = status === "generating" && role === "assistant" && index === list.length - 1;
|
|
16274
|
-
return {
|
|
16275
|
-
...message,
|
|
16276
|
-
providerUnitKey,
|
|
16277
|
-
bubbleId,
|
|
16278
|
-
sequence: index,
|
|
16279
|
-
_turnKey: turnKey,
|
|
16280
|
-
bubbleState: isStreamingTail ? "streaming" : "final"
|
|
16281
|
-
};
|
|
16282
|
-
});
|
|
16105
|
+
function isAllCondition(c) {
|
|
16106
|
+
return "all" in c;
|
|
16283
16107
|
}
|
|
16284
|
-
function
|
|
16285
|
-
|
|
16286
|
-
|
|
16108
|
+
function isAnyCondition(c) {
|
|
16109
|
+
return "any" in c;
|
|
16110
|
+
}
|
|
16111
|
+
function evaluateCondition(cond, sections, fullScreen, cursor, prevLines, trace, stateId) {
|
|
16112
|
+
if (isAllCondition(cond)) {
|
|
16113
|
+
for (const child of cond.all) {
|
|
16114
|
+
if (!evaluateCondition(child, sections, fullScreen, cursor, prevLines, trace, stateId)) {
|
|
16115
|
+
return false;
|
|
16116
|
+
}
|
|
16117
|
+
}
|
|
16118
|
+
return true;
|
|
16287
16119
|
}
|
|
16288
|
-
|
|
16289
|
-
|
|
16290
|
-
|
|
16291
|
-
|
|
16292
|
-
const chromeRes = (spec.transcriptPty.chromePatterns || []).map((p) => new RegExp(p.regex, p.flags || ""));
|
|
16293
|
-
const requireIndentForContinuation = spec.transcriptPty.continuationLine?.indented ?? false;
|
|
16294
|
-
const stripLeadingChrome = spec.transcriptPty.stripLeadingChrome ?? true;
|
|
16295
|
-
const scope = spec.transcriptPty.scope ?? "buffer";
|
|
16296
|
-
const detectStatus = spec.spinner || spec.settledPrompt || spec.modal || spec.dispatchOrder ? buildDetectStatusFromTui({
|
|
16297
|
-
spinner: spec.spinner,
|
|
16298
|
-
settledPrompt: spec.settledPrompt,
|
|
16299
|
-
modal: spec.modal,
|
|
16300
|
-
dispatchOrder: spec.dispatchOrder
|
|
16301
|
-
}) : () => null;
|
|
16302
|
-
const parseApproval = spec.modal ? buildParseApprovalFromTui(spec.modal) : () => null;
|
|
16303
|
-
const sessionIdRe = spec.sessionIdExtraction ? new RegExp(
|
|
16304
|
-
spec.sessionIdExtraction.regex,
|
|
16305
|
-
spec.sessionIdExtraction.flags ?? "i"
|
|
16306
|
-
) : null;
|
|
16307
|
-
const sessionIdScope = spec.sessionIdExtraction?.scope ?? "tail";
|
|
16308
|
-
return function parseSession(input) {
|
|
16309
|
-
const status = detectStatus(input) ?? "idle";
|
|
16310
|
-
const modal = parseApproval(input);
|
|
16311
|
-
const text = pickInputText(input, scope);
|
|
16312
|
-
const lines = splitLines(text);
|
|
16313
|
-
const messages = [];
|
|
16314
|
-
let seenFirstRoleLine = !stripLeadingChrome;
|
|
16315
|
-
for (const raw of lines) {
|
|
16316
|
-
const line = raw;
|
|
16317
|
-
if (line.trim() === "") {
|
|
16318
|
-
continue;
|
|
16120
|
+
if (isAnyCondition(cond)) {
|
|
16121
|
+
for (const child of cond.any) {
|
|
16122
|
+
if (evaluateCondition(child, sections, fullScreen, cursor, prevLines, trace, stateId)) {
|
|
16123
|
+
return true;
|
|
16319
16124
|
}
|
|
16320
|
-
|
|
16321
|
-
|
|
16322
|
-
|
|
16323
|
-
|
|
16324
|
-
|
|
16325
|
-
|
|
16125
|
+
}
|
|
16126
|
+
return false;
|
|
16127
|
+
}
|
|
16128
|
+
if (isChangedCondition(cond)) {
|
|
16129
|
+
if (!cursor || !prevLines || prevLines.length === 0) return false;
|
|
16130
|
+
const curLines = fullScreen.split("\n");
|
|
16131
|
+
const startRow = Math.max(0, cursor.row - cond.cursor_above);
|
|
16132
|
+
const endRow = cursor.row;
|
|
16133
|
+
const currentSlice = curLines.slice(startRow, endRow).join("\n");
|
|
16134
|
+
const prevSlice = prevLines.slice(startRow, endRow).join("\n");
|
|
16135
|
+
const didChange = currentSlice !== prevSlice;
|
|
16136
|
+
const result = cond.changed ? didChange : !didChange;
|
|
16137
|
+
const stableSuffix = cond.stable_ms != null ? ` stable_ms=${cond.stable_ms}` : "";
|
|
16138
|
+
trace.push({
|
|
16139
|
+
kind: result ? "state_match" : "state_skip",
|
|
16140
|
+
text: `state[${stateId}] changed cond cursor_above=${cond.cursor_above} rows[${startRow},${endRow}) changed=${didChange} expected=${cond.changed}${stableSuffix} result=${result}`
|
|
16141
|
+
});
|
|
16142
|
+
return result;
|
|
16143
|
+
}
|
|
16144
|
+
if (isRegexCondition(cond)) {
|
|
16145
|
+
const haystack = sectionText(sections, cond.section, fullScreen);
|
|
16146
|
+
let matched = false;
|
|
16147
|
+
try {
|
|
16148
|
+
const re = new RegExp(cond.matches, cond.flags ?? "i");
|
|
16149
|
+
matched = re.test(haystack);
|
|
16150
|
+
} catch {
|
|
16151
|
+
matched = false;
|
|
16152
|
+
}
|
|
16153
|
+
if (!matched) {
|
|
16154
|
+
trace.push({ kind: "state_skip", text: `state[${stateId}] regex cond ${cond.section ?? "*"}~/${cond.matches}/ no match` });
|
|
16155
|
+
return false;
|
|
16156
|
+
}
|
|
16157
|
+
if (cursor !== void 0) {
|
|
16158
|
+
if (cond.cursor_row_min !== void 0 && cursor.row < cond.cursor_row_min) {
|
|
16159
|
+
trace.push({ kind: "state_skip", text: `state[${stateId}] cursor row ${cursor.row} < cursor_row_min ${cond.cursor_row_min}` });
|
|
16160
|
+
return false;
|
|
16326
16161
|
}
|
|
16327
|
-
if (
|
|
16328
|
-
|
|
16329
|
-
|
|
16330
|
-
const assistMatch = line.match(assistantRe);
|
|
16331
|
-
if (userMatch) {
|
|
16332
|
-
seenFirstRoleLine = true;
|
|
16333
|
-
const content = (userMatch[1] ?? userMatch[0]).trim();
|
|
16334
|
-
if (content) messages.push({ role: "user", kind: "standard", content });
|
|
16335
|
-
continue;
|
|
16162
|
+
if (cond.cursor_row_max !== void 0 && cursor.row > cond.cursor_row_max) {
|
|
16163
|
+
trace.push({ kind: "state_skip", text: `state[${stateId}] cursor row ${cursor.row} > cursor_row_max ${cond.cursor_row_max}` });
|
|
16164
|
+
return false;
|
|
16336
16165
|
}
|
|
16337
|
-
if (
|
|
16338
|
-
|
|
16339
|
-
|
|
16340
|
-
const content = (toolMatch[1] ?? toolMatch[0]).trim();
|
|
16341
|
-
if (content) messages.push({ role: "assistant", kind: "tool", content });
|
|
16342
|
-
continue;
|
|
16166
|
+
if (cond.cursor_col_min !== void 0 && cursor.col < cond.cursor_col_min) {
|
|
16167
|
+
trace.push({ kind: "state_skip", text: `state[${stateId}] cursor col ${cursor.col} < cursor_col_min ${cond.cursor_col_min}` });
|
|
16168
|
+
return false;
|
|
16343
16169
|
}
|
|
16344
|
-
if (
|
|
16345
|
-
|
|
16346
|
-
|
|
16347
|
-
if (content) messages.push({ role: "assistant", kind: "standard", content });
|
|
16348
|
-
continue;
|
|
16170
|
+
if (cond.cursor_col_max !== void 0 && cursor.col > cond.cursor_col_max) {
|
|
16171
|
+
trace.push({ kind: "state_skip", text: `state[${stateId}] cursor col ${cursor.col} > cursor_col_max ${cond.cursor_col_max}` });
|
|
16172
|
+
return false;
|
|
16349
16173
|
}
|
|
16350
|
-
if (!seenFirstRoleLine) continue;
|
|
16351
|
-
if (requireIndentForContinuation && !/^\s/.test(line)) continue;
|
|
16352
|
-
const last = messages[messages.length - 1];
|
|
16353
|
-
if (!last) continue;
|
|
16354
|
-
const cont = line.replace(/^\s+/, "");
|
|
16355
|
-
if (cont) last.content = last.content ? `${last.content}
|
|
16356
|
-
${cont}` : cont;
|
|
16357
16174
|
}
|
|
16358
|
-
|
|
16359
|
-
|
|
16360
|
-
|
|
16361
|
-
|
|
16362
|
-
|
|
16363
|
-
|
|
16364
|
-
|
|
16365
|
-
|
|
16366
|
-
|
|
16367
|
-
|
|
16368
|
-
|
|
16369
|
-
|
|
16175
|
+
trace.push({ kind: "state_match", text: `state[${stateId}] regex cond ${cond.section ?? "*"}~/${cond.matches}/ matched${cursor !== void 0 ? ` cursor=(${cursor.row},${cursor.col})` : ""}` });
|
|
16176
|
+
return true;
|
|
16177
|
+
}
|
|
16178
|
+
return false;
|
|
16179
|
+
}
|
|
16180
|
+
function extractTitle(rule, sections, fullScreen) {
|
|
16181
|
+
const hay = sectionText(sections, rule.section, fullScreen);
|
|
16182
|
+
if (!hay) return null;
|
|
16183
|
+
if (rule.first_line) {
|
|
16184
|
+
const lines = hay.split("\n");
|
|
16185
|
+
for (const line of lines) {
|
|
16186
|
+
const stripped = line.trim();
|
|
16187
|
+
if (stripped && !/^[─╌═─\s]+$/.test(stripped)) {
|
|
16188
|
+
return stripped;
|
|
16189
|
+
}
|
|
16370
16190
|
}
|
|
16371
|
-
return
|
|
16372
|
-
}
|
|
16191
|
+
return null;
|
|
16192
|
+
}
|
|
16193
|
+
if (rule.regex) {
|
|
16194
|
+
try {
|
|
16195
|
+
const re = new RegExp(rule.regex, rule.flags ?? "i");
|
|
16196
|
+
const m = re.exec(hay);
|
|
16197
|
+
if (m) return (m[1] ?? m[0]).trim();
|
|
16198
|
+
} catch {
|
|
16199
|
+
}
|
|
16200
|
+
}
|
|
16201
|
+
return null;
|
|
16373
16202
|
}
|
|
16374
|
-
|
|
16375
|
-
|
|
16376
|
-
|
|
16203
|
+
function compilePattern(ref) {
|
|
16204
|
+
const flags = ref.flags ?? "gm";
|
|
16205
|
+
return new RegExp(ref.pattern, flags.includes("g") ? flags : flags + "g");
|
|
16206
|
+
}
|
|
16207
|
+
function compileLinePattern(ref) {
|
|
16208
|
+
const flags = (ref.flags ?? "m").replace(/g/g, "");
|
|
16209
|
+
return new RegExp(ref.pattern, flags);
|
|
16210
|
+
}
|
|
16211
|
+
function extractButtonsFromRule(rule, hay) {
|
|
16212
|
+
const keyTemplate = rule.key_for_index;
|
|
16213
|
+
const continuationLines = rule.continuation_lines ?? false;
|
|
16214
|
+
const buttons = [];
|
|
16215
|
+
if (continuationLines) {
|
|
16216
|
+
const re = compileLinePattern(rule);
|
|
16217
|
+
const lines = hay.split("\n");
|
|
16218
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
16219
|
+
const m = re.exec(lines[i]);
|
|
16220
|
+
if (!m) continue;
|
|
16221
|
+
const idx = Number(m[1]);
|
|
16222
|
+
let label = String(m[2] ?? "").trim();
|
|
16223
|
+
if (!Number.isFinite(idx) || idx <= 0 || !label) continue;
|
|
16224
|
+
const current = hasCursorMarker(lines[i]);
|
|
16225
|
+
let j = i + 1;
|
|
16226
|
+
while (j < lines.length) {
|
|
16227
|
+
const next = lines[j];
|
|
16228
|
+
if (!next.trim()) break;
|
|
16229
|
+
if (re.test(next)) break;
|
|
16230
|
+
if (!/^\s+/.test(next)) break;
|
|
16231
|
+
label += " " + next.trim();
|
|
16232
|
+
j += 1;
|
|
16233
|
+
}
|
|
16234
|
+
const key = keyTemplate.replace(/\{index\}/g, String(idx));
|
|
16235
|
+
buttons.push({ index: idx, label, key, current });
|
|
16236
|
+
i = j - 1;
|
|
16237
|
+
}
|
|
16238
|
+
} else {
|
|
16239
|
+
const re = compilePattern(rule);
|
|
16240
|
+
let m;
|
|
16241
|
+
while ((m = re.exec(hay)) !== null) {
|
|
16242
|
+
const idx = Number(m[1]);
|
|
16243
|
+
const label = String(m[2] ?? "").trim();
|
|
16244
|
+
if (!Number.isFinite(idx) || idx <= 0 || !label) continue;
|
|
16245
|
+
const key = keyTemplate.replace(/\{index\}/g, String(idx));
|
|
16246
|
+
buttons.push({ index: idx, label, key, current: hasCursorMarker(m[0]) });
|
|
16247
|
+
}
|
|
16248
|
+
}
|
|
16249
|
+
const block2 = lastContiguousNumberedBlock(buttons);
|
|
16250
|
+
block2.sort((a, b) => a.index - b.index);
|
|
16251
|
+
return block2;
|
|
16252
|
+
}
|
|
16253
|
+
function lastContiguousNumberedBlock(entries) {
|
|
16254
|
+
if (entries.length <= 1) return entries.slice();
|
|
16255
|
+
let start = entries.length - 1;
|
|
16256
|
+
for (let i = entries.length - 1; i > 0; i -= 1) {
|
|
16257
|
+
if (entries[i - 1].index === entries[i].index - 1) start = i - 1;
|
|
16258
|
+
else break;
|
|
16259
|
+
}
|
|
16260
|
+
return entries.slice(start);
|
|
16261
|
+
}
|
|
16262
|
+
function hasCursorMarker(text) {
|
|
16263
|
+
return /^\s*[❯›>]/.test(text);
|
|
16264
|
+
}
|
|
16265
|
+
var init_evaluator = __esm({
|
|
16266
|
+
"src/providers/spec/evaluator.ts"() {
|
|
16377
16267
|
"use strict";
|
|
16378
|
-
init_detect_status();
|
|
16379
|
-
init_parse_approval();
|
|
16380
|
-
ANSI_RE = /\x1b\[[0-?]*[ -/]*[@-~]/g;
|
|
16381
|
-
OSC_RE = /\x1b\][^\x07\x1b\n]*(?:\x07|\x1b\\|(?=\n|$))/g;
|
|
16382
16268
|
}
|
|
16383
16269
|
});
|
|
16384
16270
|
|
|
16385
|
-
// src/
|
|
16386
|
-
|
|
16387
|
-
|
|
16388
|
-
|
|
16389
|
-
|
|
16390
|
-
|
|
16391
|
-
|
|
16392
|
-
|
|
16393
|
-
tailLen: tail.length,
|
|
16394
|
-
isWaitingForResponse: typeof input?.isWaitingForResponse === "boolean" ? input.isWaitingForResponse : void 0,
|
|
16395
|
-
screenTextHead: screenText ? screenText.slice(0, 200) : void 0
|
|
16396
|
-
};
|
|
16271
|
+
// src/providers/spec/fsm-evaluator.ts
|
|
16272
|
+
var fsm_evaluator_exports = {};
|
|
16273
|
+
__export(fsm_evaluator_exports, {
|
|
16274
|
+
evaluateConditionPreview: () => evaluateConditionPreview,
|
|
16275
|
+
evaluateFsm: () => evaluateFsm
|
|
16276
|
+
});
|
|
16277
|
+
function regionKey(cursorAbove) {
|
|
16278
|
+
return cursorAbove && cursorAbove > 0 ? cursorAbove : WHOLE_SCREEN;
|
|
16397
16279
|
}
|
|
16398
|
-
function
|
|
16399
|
-
|
|
16400
|
-
const json = JSON.stringify(result);
|
|
16401
|
-
return json && json.length > 400 ? `${json.slice(0, 400)}\u2026[truncated ${json.length - 400}]` : json ?? "undefined";
|
|
16402
|
-
} catch (e) {
|
|
16403
|
-
return `<unserializable: ${e?.message || e}>`;
|
|
16404
|
-
}
|
|
16280
|
+
function isRegex(c) {
|
|
16281
|
+
return "matches" in c;
|
|
16405
16282
|
}
|
|
16406
|
-
|
|
16407
|
-
|
|
16408
|
-
|
|
16409
|
-
|
|
16410
|
-
|
|
16411
|
-
|
|
16412
|
-
|
|
16413
|
-
|
|
16414
|
-
|
|
16415
|
-
|
|
16416
|
-
|
|
16417
|
-
|
|
16418
|
-
|
|
16419
|
-
|
|
16420
|
-
|
|
16421
|
-
|
|
16422
|
-
|
|
16423
|
-
|
|
16424
|
-
|
|
16425
|
-
|
|
16426
|
-
|
|
16427
|
-
|
|
16428
|
-
|
|
16429
|
-
|
|
16430
|
-
|
|
16283
|
+
function isChanged(c) {
|
|
16284
|
+
return "cursor_above" in c && "changed" in c;
|
|
16285
|
+
}
|
|
16286
|
+
function isElapsed(c) {
|
|
16287
|
+
return "elapsed_ms" in c;
|
|
16288
|
+
}
|
|
16289
|
+
function isStable(c) {
|
|
16290
|
+
return "stable_ms" in c;
|
|
16291
|
+
}
|
|
16292
|
+
function isAll(c) {
|
|
16293
|
+
return "all" in c;
|
|
16294
|
+
}
|
|
16295
|
+
function isAny(c) {
|
|
16296
|
+
return "any" in c;
|
|
16297
|
+
}
|
|
16298
|
+
function isNot(c) {
|
|
16299
|
+
return "not" in c;
|
|
16300
|
+
}
|
|
16301
|
+
function evalCond(cond, sections, fullScreen, cursor, prevLines, clock, legacyTrace, stateId) {
|
|
16302
|
+
if (isAll(cond)) {
|
|
16303
|
+
const children = cond.all.map((c) => evalCond(c, sections, fullScreen, cursor, prevLines, clock, legacyTrace, stateId));
|
|
16304
|
+
const result = children.every((c) => c.result);
|
|
16305
|
+
const remainingMs = result ? 0 : Math.max(0, ...children.filter((c) => !c.result).map((c) => c.remainingMs ?? 0));
|
|
16306
|
+
return { kind: "all", result, detail: `all(${children.length})`, remainingMs, children };
|
|
16307
|
+
}
|
|
16308
|
+
if (isAny(cond)) {
|
|
16309
|
+
const children = cond.any.map((c) => evalCond(c, sections, fullScreen, cursor, prevLines, clock, legacyTrace, stateId));
|
|
16310
|
+
const result = children.some((c) => c.result);
|
|
16311
|
+
const pending = children.filter((c) => !c.result).map((c) => c.remainingMs ?? Infinity);
|
|
16312
|
+
const remainingMs = result ? 0 : pending.length ? Math.min(...pending) : 0;
|
|
16313
|
+
return { kind: "any", result, detail: `any(${children.length})`, remainingMs: Number.isFinite(remainingMs) ? remainingMs : 0, children };
|
|
16314
|
+
}
|
|
16315
|
+
if (isNot(cond)) {
|
|
16316
|
+
const child = evalCond(cond.not, sections, fullScreen, cursor, prevLines, clock, legacyTrace, stateId);
|
|
16317
|
+
return { kind: "not", result: !child.result, detail: `not`, remainingMs: 0, children: [child] };
|
|
16318
|
+
}
|
|
16319
|
+
if (isElapsed(cond)) {
|
|
16320
|
+
const age = clock.now - clock.stateEnteredAt;
|
|
16321
|
+
const result = age >= cond.elapsed_ms;
|
|
16322
|
+
const remainingMs = result ? 0 : cond.elapsed_ms - age;
|
|
16323
|
+
return { kind: "elapsed", result, detail: `elapsed ${age}ms / ${cond.elapsed_ms}ms`, remainingMs };
|
|
16324
|
+
}
|
|
16325
|
+
if (isStable(cond)) {
|
|
16326
|
+
const key = regionKey(cond.cursor_above);
|
|
16327
|
+
const lastChanged = clock.regionLastChangedAt.get(key) ?? clock.stateEnteredAt;
|
|
16328
|
+
const stableFor = clock.now - lastChanged;
|
|
16329
|
+
const result = stableFor >= cond.stable_ms;
|
|
16330
|
+
const remainingMs = result ? 0 : cond.stable_ms - stableFor;
|
|
16331
|
+
const where = key === WHOLE_SCREEN ? "screen" : `cursor_above=${cond.cursor_above}`;
|
|
16332
|
+
return { kind: "stable", result, detail: `stable ${where} ${stableFor}ms / ${cond.stable_ms}ms`, remainingMs };
|
|
16333
|
+
}
|
|
16334
|
+
if (isRegex(cond) || isChanged(cond)) {
|
|
16335
|
+
const result = evaluateCondition(cond, sections, fullScreen, cursor, prevLines, legacyTrace, stateId);
|
|
16336
|
+
const kind = isRegex(cond) ? "regex" : "changed";
|
|
16337
|
+
const detail = isRegex(cond) ? `${cond.section ?? "*"}~/${cond.matches}/` : `cursor_above=${cond.cursor_above} changed=${cond.changed}`;
|
|
16338
|
+
let matchedText;
|
|
16339
|
+
if (result && isRegex(cond)) {
|
|
16340
|
+
try {
|
|
16341
|
+
const hay = sectionText(sections, cond.section, fullScreen);
|
|
16342
|
+
const re = new RegExp(cond.matches, cond.flags ?? "i");
|
|
16343
|
+
const m = re.exec(hay);
|
|
16344
|
+
if (m && m[0]) matchedText = m[0].replace(/\s+/g, " ").trim().slice(0, 160);
|
|
16345
|
+
} catch {
|
|
16431
16346
|
}
|
|
16432
|
-
|
|
16433
|
-
|
|
16434
|
-
|
|
16435
|
-
|
|
16436
|
-
|
|
16437
|
-
|
|
16438
|
-
|
|
16439
|
-
|
|
16440
|
-
|
|
16441
|
-
|
|
16442
|
-
|
|
16443
|
-
|
|
16444
|
-
|
|
16445
|
-
|
|
16446
|
-
|
|
16447
|
-
|
|
16448
|
-
|
|
16449
|
-
|
|
16450
|
-
|
|
16451
|
-
|
|
16452
|
-
|
|
16453
|
-
|
|
16454
|
-
|
|
16455
|
-
|
|
16456
|
-
|
|
16457
|
-
|
|
16458
|
-
|
|
16459
|
-
|
|
16460
|
-
|
|
16461
|
-
|
|
16462
|
-
|
|
16463
|
-
|
|
16464
|
-
|
|
16465
|
-
|
|
16466
|
-
|
|
16467
|
-
|
|
16468
|
-
|
|
16469
|
-
|
|
16470
|
-
|
|
16471
|
-
|
|
16472
|
-
|
|
16473
|
-
|
|
16474
|
-
|
|
16475
|
-
|
|
16476
|
-
|
|
16477
|
-
|
|
16478
|
-
|
|
16479
|
-
|
|
16480
|
-
|
|
16481
|
-
|
|
16482
|
-
|
|
16483
|
-
|
|
16484
|
-
|
|
16485
|
-
|
|
16486
|
-
|
|
16487
|
-
|
|
16488
|
-
|
|
16489
|
-
|
|
16490
|
-
|
|
16491
|
-
|
|
16492
|
-
|
|
16493
|
-
|
|
16494
|
-
|
|
16495
|
-
|
|
16496
|
-
|
|
16497
|
-
|
|
16498
|
-
|
|
16499
|
-
|
|
16500
|
-
|
|
16501
|
-
|
|
16502
|
-
|
|
16503
|
-
|
|
16504
|
-
|
|
16505
|
-
|
|
16506
|
-
|
|
16507
|
-
|
|
16508
|
-
|
|
16509
|
-
|
|
16510
|
-
|
|
16511
|
-
|
|
16512
|
-
|
|
16513
|
-
|
|
16514
|
-
|
|
16515
|
-
|
|
16516
|
-
|
|
16517
|
-
|
|
16518
|
-
|
|
16519
|
-
|
|
16520
|
-
|
|
16521
|
-
|
|
16522
|
-
|
|
16523
|
-
} catch (e) {
|
|
16524
|
-
LOG.warn("CLI", `[${this.cliType}] buildParseApprovalFromTui failed: ${e?.message || e}`);
|
|
16525
|
-
}
|
|
16526
|
-
}
|
|
16527
|
-
return sdk;
|
|
16528
|
-
}
|
|
16529
|
-
/** Reset per-session state — called when the PTY process exits. */
|
|
16530
|
-
resetSessionState() {
|
|
16531
|
-
this.scriptState = null;
|
|
16532
|
-
this.invocationTrace = [];
|
|
16533
|
-
this.lastBudgetWarnAt.clear();
|
|
16534
|
-
}
|
|
16535
|
-
// ─── Script access (for reflection and test patching) ────────────────────
|
|
16536
|
-
/** Returns the live scripts object. Direct property assignment on this object
|
|
16537
|
-
* patches individual scripts without replacing others (used in tests). */
|
|
16538
|
-
get cliScripts() {
|
|
16539
|
-
return this.scripts;
|
|
16347
|
+
}
|
|
16348
|
+
return matchedText ? { kind, result, detail, matchedText } : { kind, result, detail };
|
|
16349
|
+
}
|
|
16350
|
+
return { kind: "all", result: false, detail: "unknown condition" };
|
|
16351
|
+
}
|
|
16352
|
+
function fromLabel(t) {
|
|
16353
|
+
const from = Array.isArray(t.from) ? t.from.join("|") : t.from;
|
|
16354
|
+
return t.label ?? `${from}\u2192${t.to}`;
|
|
16355
|
+
}
|
|
16356
|
+
function evaluateFsm(spec, currentStateId, screenText, cursor, prevLines, clock) {
|
|
16357
|
+
const legacyTrace = [];
|
|
16358
|
+
const lines = screenText.split("\n").map((l) => l.endsWith("\r") ? l.slice(0, -1) : l);
|
|
16359
|
+
const cleanScreen = lines.join("\n");
|
|
16360
|
+
const sections = resolveSections(spec.sections ?? {}, lines);
|
|
16361
|
+
const outgoing = outgoingTransitions(spec, currentStateId);
|
|
16362
|
+
const transitions = [];
|
|
16363
|
+
let fired = null;
|
|
16364
|
+
for (const t of outgoing) {
|
|
16365
|
+
const holdMs = t.min_hold_ms ?? 0;
|
|
16366
|
+
const heldFor = clock.now - clock.stateEnteredAt;
|
|
16367
|
+
const holdSatisfied = heldFor >= holdMs;
|
|
16368
|
+
const holdRemainingMs = holdSatisfied ? 0 : holdMs - heldFor;
|
|
16369
|
+
let cond;
|
|
16370
|
+
let condResult = true;
|
|
16371
|
+
if (t.when) {
|
|
16372
|
+
cond = evalCond(t.when, sections, cleanScreen, cursor, prevLines, clock, legacyTrace, `${currentStateId}\u2192${t.to}`);
|
|
16373
|
+
condResult = cond.result;
|
|
16374
|
+
}
|
|
16375
|
+
const fires = holdSatisfied && condResult;
|
|
16376
|
+
const te = {
|
|
16377
|
+
to: t.to,
|
|
16378
|
+
label: fromLabel(t),
|
|
16379
|
+
eligible: true,
|
|
16380
|
+
holdSatisfied,
|
|
16381
|
+
holdRemainingMs,
|
|
16382
|
+
condResult,
|
|
16383
|
+
cond,
|
|
16384
|
+
fires,
|
|
16385
|
+
priority: t.priority ?? 0
|
|
16386
|
+
};
|
|
16387
|
+
transitions.push(te);
|
|
16388
|
+
if (fires && !fired) fired = te;
|
|
16389
|
+
}
|
|
16390
|
+
if (fired && !stateById(spec, fired.to)) {
|
|
16391
|
+
legacyTrace.push({ kind: "state_skip", text: `transition target ${fired.to} not found` });
|
|
16392
|
+
fired = null;
|
|
16393
|
+
}
|
|
16394
|
+
return { sections, transitions, fired, trace: legacyTrace };
|
|
16395
|
+
}
|
|
16396
|
+
function evaluateConditionPreview(cond, sections, screenText, cursor) {
|
|
16397
|
+
const lines = screenText.split("\n").map((l) => l.endsWith("\r") ? l.slice(0, -1) : l);
|
|
16398
|
+
const cleanScreen = lines.join("\n");
|
|
16399
|
+
const resolved = resolveSections(sections ?? {}, lines);
|
|
16400
|
+
const clock = { now: 0, stateEnteredAt: 0, regionLastChangedAt: /* @__PURE__ */ new Map() };
|
|
16401
|
+
return evalCond(cond, resolved, cleanScreen, cursor, void 0, clock, [], "preview");
|
|
16402
|
+
}
|
|
16403
|
+
var WHOLE_SCREEN;
|
|
16404
|
+
var init_fsm_evaluator = __esm({
|
|
16405
|
+
"src/providers/spec/fsm-evaluator.ts"() {
|
|
16406
|
+
"use strict";
|
|
16407
|
+
init_evaluator();
|
|
16408
|
+
init_fsm_types();
|
|
16409
|
+
WHOLE_SCREEN = -1;
|
|
16410
|
+
}
|
|
16411
|
+
});
|
|
16412
|
+
|
|
16413
|
+
// src/cli-adapters/pty-transport.ts
|
|
16414
|
+
var pty_transport_exports = {};
|
|
16415
|
+
__export(pty_transport_exports, {
|
|
16416
|
+
NodePtyTransportFactory: () => NodePtyTransportFactory
|
|
16417
|
+
});
|
|
16418
|
+
import * as os13 from "os";
|
|
16419
|
+
function loadNodePty() {
|
|
16420
|
+
if (cachedPty !== void 0) return cachedPty;
|
|
16421
|
+
try {
|
|
16422
|
+
cachedPty = __require("node-pty");
|
|
16423
|
+
ensureNodePtySpawnHelperPermissions();
|
|
16424
|
+
} catch {
|
|
16425
|
+
cachedPty = null;
|
|
16426
|
+
}
|
|
16427
|
+
return cachedPty;
|
|
16428
|
+
}
|
|
16429
|
+
var cachedPty, NodePtyRuntimeTransport, NodePtyTransportFactory;
|
|
16430
|
+
var init_pty_transport = __esm({
|
|
16431
|
+
"src/cli-adapters/pty-transport.ts"() {
|
|
16432
|
+
"use strict";
|
|
16433
|
+
init_spawn_env();
|
|
16434
|
+
init_resolve_executable();
|
|
16435
|
+
NodePtyRuntimeTransport = class {
|
|
16436
|
+
constructor(handle) {
|
|
16437
|
+
this.handle = handle;
|
|
16540
16438
|
}
|
|
16541
|
-
|
|
16542
|
-
|
|
16543
|
-
|
|
16439
|
+
ready = Promise.resolve();
|
|
16440
|
+
terminalQueriesHandled = false;
|
|
16441
|
+
get pid() {
|
|
16442
|
+
return this.handle.pid;
|
|
16544
16443
|
}
|
|
16545
|
-
|
|
16546
|
-
|
|
16444
|
+
write(data) {
|
|
16445
|
+
this.handle.write(data);
|
|
16547
16446
|
}
|
|
16548
|
-
|
|
16549
|
-
|
|
16447
|
+
resize(cols, rows) {
|
|
16448
|
+
this.handle.resize(cols, rows);
|
|
16550
16449
|
}
|
|
16551
|
-
|
|
16552
|
-
|
|
16553
|
-
return this._parseErrorMessage;
|
|
16450
|
+
kill() {
|
|
16451
|
+
this.handle.kill();
|
|
16554
16452
|
}
|
|
16555
|
-
|
|
16556
|
-
|
|
16453
|
+
getMetadata() {
|
|
16454
|
+
return null;
|
|
16557
16455
|
}
|
|
16558
|
-
|
|
16559
|
-
|
|
16560
|
-
if (!this.scripts.detectStatus) return null;
|
|
16561
|
-
try {
|
|
16562
|
-
return this.invoke("detectStatus", this.scripts.detectStatus, input);
|
|
16563
|
-
} catch (e) {
|
|
16564
|
-
LOG.warn("CLI", `[${this.cliType}] detectStatus error: ${e?.message || e}`);
|
|
16565
|
-
return null;
|
|
16566
|
-
}
|
|
16456
|
+
onData(callback) {
|
|
16457
|
+
this.handle.onData(callback);
|
|
16567
16458
|
}
|
|
16568
|
-
|
|
16569
|
-
|
|
16570
|
-
try {
|
|
16571
|
-
return this.invoke(
|
|
16572
|
-
"parseApproval",
|
|
16573
|
-
this.scripts.parseApproval,
|
|
16574
|
-
input
|
|
16575
|
-
);
|
|
16576
|
-
} catch (e) {
|
|
16577
|
-
LOG.warn("CLI", `[${this.cliType}] parseApproval error: ${e?.message || e}`);
|
|
16578
|
-
return null;
|
|
16579
|
-
}
|
|
16459
|
+
onExit(callback) {
|
|
16460
|
+
this.handle.onExit(callback);
|
|
16580
16461
|
}
|
|
16581
|
-
|
|
16582
|
-
|
|
16583
|
-
|
|
16584
|
-
|
|
16585
|
-
|
|
16586
|
-
|
|
16587
|
-
|
|
16588
|
-
|
|
16589
|
-
|
|
16590
|
-
|
|
16591
|
-
|
|
16592
|
-
|
|
16593
|
-
|
|
16462
|
+
};
|
|
16463
|
+
NodePtyTransportFactory = class {
|
|
16464
|
+
spawn(command, args, options) {
|
|
16465
|
+
const pty = loadNodePty();
|
|
16466
|
+
if (!pty) throw new Error("node-pty is not installed");
|
|
16467
|
+
let cwd = options.cwd;
|
|
16468
|
+
if (cwd) {
|
|
16469
|
+
try {
|
|
16470
|
+
const fs32 = __require("fs");
|
|
16471
|
+
const stat2 = fs32.statSync(cwd);
|
|
16472
|
+
if (!stat2.isDirectory()) cwd = os13.homedir();
|
|
16473
|
+
} catch {
|
|
16474
|
+
cwd = os13.homedir();
|
|
16475
|
+
}
|
|
16594
16476
|
}
|
|
16477
|
+
const handle = pty.spawn(resolveWin32Executable(command), args, {
|
|
16478
|
+
name: "xterm-256color",
|
|
16479
|
+
cols: options.cols,
|
|
16480
|
+
rows: options.rows,
|
|
16481
|
+
cwd,
|
|
16482
|
+
env: options.env
|
|
16483
|
+
});
|
|
16484
|
+
return new NodePtyRuntimeTransport(handle);
|
|
16595
16485
|
}
|
|
16596
|
-
|
|
16597
|
-
|
|
16598
|
-
|
|
16599
|
-
|
|
16600
|
-
|
|
16601
|
-
|
|
16602
|
-
|
|
16603
|
-
|
|
16486
|
+
};
|
|
16487
|
+
}
|
|
16488
|
+
});
|
|
16489
|
+
|
|
16490
|
+
// src/providers/sdk/v1/builders/cli/visible-region.ts
|
|
16491
|
+
function compile(re, flags) {
|
|
16492
|
+
try {
|
|
16493
|
+
return new RegExp(re, flags ?? "");
|
|
16494
|
+
} catch (e) {
|
|
16495
|
+
throw new Error(`Invalid visible-region anchor regex /${re}/${flags ?? ""}: ${e.message}`);
|
|
16496
|
+
}
|
|
16497
|
+
}
|
|
16498
|
+
function findAllMatches(re, text) {
|
|
16499
|
+
const results = [];
|
|
16500
|
+
const iter = new RegExp(re.source, re.flags.replace("g", "") + "g");
|
|
16501
|
+
let m;
|
|
16502
|
+
while ((m = iter.exec(text)) !== null) {
|
|
16503
|
+
results.push(m);
|
|
16504
|
+
if (m[0].length === 0) iter.lastIndex += 1;
|
|
16505
|
+
}
|
|
16506
|
+
return results;
|
|
16507
|
+
}
|
|
16508
|
+
function applyVisibleRegion(spec, text) {
|
|
16509
|
+
if (!text) return text;
|
|
16510
|
+
switch (spec.scope) {
|
|
16511
|
+
case "screen":
|
|
16512
|
+
case "buffer":
|
|
16513
|
+
return text;
|
|
16514
|
+
case "tail": {
|
|
16515
|
+
const limit = spec.tailChars ?? 4e3;
|
|
16516
|
+
if (text.length <= limit) return text;
|
|
16517
|
+
return text.slice(-limit);
|
|
16518
|
+
}
|
|
16519
|
+
case "between-anchors": {
|
|
16520
|
+
const anchors = spec.anchors;
|
|
16521
|
+
if (!anchors?.top && !anchors?.bottom) return text;
|
|
16522
|
+
const selectLast = (spec.selectAnchor ?? "first") === "last";
|
|
16523
|
+
let topEnd = 0;
|
|
16524
|
+
if (anchors.top) {
|
|
16525
|
+
const topRe = compile(anchors.top.pattern, anchors.top.flags);
|
|
16526
|
+
const topMatches = findAllMatches(topRe, text);
|
|
16527
|
+
if (topMatches.length === 0) {
|
|
16528
|
+
return text;
|
|
16604
16529
|
}
|
|
16605
|
-
|
|
16530
|
+
const chosen = selectLast ? topMatches[topMatches.length - 1] : topMatches[0];
|
|
16531
|
+
topEnd = chosen.index + chosen[0].length;
|
|
16606
16532
|
}
|
|
16607
|
-
|
|
16608
|
-
|
|
16609
|
-
const
|
|
16610
|
-
const
|
|
16611
|
-
const
|
|
16612
|
-
|
|
16613
|
-
|
|
16614
|
-
|
|
16615
|
-
result = fn(this.scriptState, input, this.sdk);
|
|
16616
|
-
} else if (arity === 2) {
|
|
16617
|
-
result = fn(this.scriptState, input);
|
|
16618
|
-
} else {
|
|
16619
|
-
result = fn(input);
|
|
16620
|
-
}
|
|
16621
|
-
const elapsedUs = startedHr ? Number((process.hrtime.bigint() - startedHr) / 1000n) : 0;
|
|
16622
|
-
const timedOut = this.checkBudget(scriptName, elapsedUs);
|
|
16623
|
-
this.recordTrace({
|
|
16624
|
-
at: startedAt,
|
|
16625
|
-
scriptName,
|
|
16626
|
-
arity,
|
|
16627
|
-
inputSummary: summarizeInput(input),
|
|
16628
|
-
ok: true,
|
|
16629
|
-
elapsedUs,
|
|
16630
|
-
resultSummary: summarizeResult(result),
|
|
16631
|
-
...timedOut ? { timedOut: true } : {}
|
|
16632
|
-
});
|
|
16633
|
-
return result;
|
|
16634
|
-
} catch (e) {
|
|
16635
|
-
const elapsedUs = startedHr ? Number((process.hrtime.bigint() - startedHr) / 1000n) : 0;
|
|
16636
|
-
const timedOut = this.checkBudget(scriptName, elapsedUs);
|
|
16637
|
-
this.recordTrace({
|
|
16638
|
-
at: startedAt,
|
|
16639
|
-
scriptName,
|
|
16640
|
-
arity,
|
|
16641
|
-
inputSummary: summarizeInput(input),
|
|
16642
|
-
ok: false,
|
|
16643
|
-
elapsedUs,
|
|
16644
|
-
error: e?.message ? String(e.message).slice(0, 400) : String(e).slice(0, 400),
|
|
16645
|
-
...timedOut ? { timedOut: true } : {}
|
|
16646
|
-
});
|
|
16647
|
-
throw e;
|
|
16533
|
+
let bottomStart = text.length;
|
|
16534
|
+
if (anchors.bottom) {
|
|
16535
|
+
const bottomRe = compile(anchors.bottom.pattern, anchors.bottom.flags);
|
|
16536
|
+
const searchFrom = topEnd;
|
|
16537
|
+
const suffix = text.slice(searchFrom);
|
|
16538
|
+
const bottomMatches = findAllMatches(bottomRe, suffix);
|
|
16539
|
+
if (bottomMatches.length === 0) {
|
|
16540
|
+
return text;
|
|
16648
16541
|
}
|
|
16542
|
+
const chosen = selectLast ? bottomMatches[bottomMatches.length - 1] : bottomMatches[0];
|
|
16543
|
+
bottomStart = searchFrom + chosen.index;
|
|
16649
16544
|
}
|
|
16650
|
-
|
|
16651
|
-
|
|
16652
|
-
* first violation per script (or after the throttle window expires) we
|
|
16653
|
-
* emit a single WARN so the operator learns which provider is slow.
|
|
16654
|
-
*
|
|
16655
|
-
* We deliberately throttle per-scriptName so a chronically-slow
|
|
16656
|
-
* detectStatus doesn't spam the log on every PTY frame settle.
|
|
16657
|
-
*/
|
|
16658
|
-
checkBudget(scriptName, elapsedUs) {
|
|
16659
|
-
const budgetUs = this.scriptCallBudgetMs * 1e3;
|
|
16660
|
-
if (elapsedUs <= budgetUs) return false;
|
|
16661
|
-
const now = Date.now();
|
|
16662
|
-
const last = this.lastBudgetWarnAt.get(scriptName) ?? 0;
|
|
16663
|
-
if (now - last >= BUDGET_WARN_THROTTLE_MS) {
|
|
16664
|
-
this.lastBudgetWarnAt.set(scriptName, now);
|
|
16665
|
-
LOG.warn(
|
|
16666
|
-
"CLI",
|
|
16667
|
-
`[${this.cliType}] script ${scriptName} took ${elapsedUs}us, budget ${budgetUs}us`
|
|
16668
|
-
);
|
|
16669
|
-
}
|
|
16670
|
-
return true;
|
|
16545
|
+
if (topEnd >= bottomStart) {
|
|
16546
|
+
return text;
|
|
16671
16547
|
}
|
|
16672
|
-
|
|
16548
|
+
return text.slice(topEnd, bottomStart);
|
|
16549
|
+
}
|
|
16550
|
+
default:
|
|
16551
|
+
return text;
|
|
16552
|
+
}
|
|
16553
|
+
}
|
|
16554
|
+
var init_visible_region = __esm({
|
|
16555
|
+
"src/providers/sdk/v1/builders/cli/visible-region.ts"() {
|
|
16556
|
+
"use strict";
|
|
16673
16557
|
}
|
|
16674
16558
|
});
|
|
16675
16559
|
|
|
16676
|
-
// src/cli
|
|
16677
|
-
function
|
|
16560
|
+
// src/providers/sdk/v1/builders/cli/detect-status.ts
|
|
16561
|
+
function compile2(re, flags) {
|
|
16562
|
+
try {
|
|
16563
|
+
return new RegExp(re, flags ?? "");
|
|
16564
|
+
} catch (e) {
|
|
16565
|
+
throw new Error(`Invalid regex /${re}/${flags ?? ""}: ${e.message}`);
|
|
16566
|
+
}
|
|
16567
|
+
}
|
|
16568
|
+
function takeTail(text, lines) {
|
|
16678
16569
|
if (!text) return "";
|
|
16679
|
-
|
|
16680
|
-
if (
|
|
16681
|
-
return
|
|
16570
|
+
const split = text.split("\n");
|
|
16571
|
+
if (split.length <= lines) return text;
|
|
16572
|
+
return split.slice(-lines).join("\n");
|
|
16682
16573
|
}
|
|
16683
|
-
function
|
|
16684
|
-
|
|
16574
|
+
function scopeText(input, scope, windowLines) {
|
|
16575
|
+
const lines = windowLines && windowLines > 0 ? windowLines : 8;
|
|
16576
|
+
switch (scope) {
|
|
16577
|
+
case "whole-screen":
|
|
16578
|
+
return input.screenText ?? "";
|
|
16579
|
+
case "recent-buffer":
|
|
16580
|
+
return input.tail ?? "";
|
|
16581
|
+
case "last-n-lines":
|
|
16582
|
+
case "live-frame-tail":
|
|
16583
|
+
case void 0:
|
|
16584
|
+
default:
|
|
16585
|
+
return takeTail(input.screenText ?? "", lines);
|
|
16586
|
+
}
|
|
16685
16587
|
}
|
|
16686
|
-
function
|
|
16687
|
-
|
|
16688
|
-
accumulatedBuffer,
|
|
16689
|
-
accumulatedRawBuffer,
|
|
16690
|
-
recentOutputBuffer,
|
|
16691
|
-
terminalScreenText,
|
|
16692
|
-
workingDir,
|
|
16693
|
-
providerSessionId,
|
|
16694
|
-
historySessionId,
|
|
16695
|
-
baseMessages,
|
|
16696
|
-
partialResponse,
|
|
16697
|
-
isWaitingForResponse,
|
|
16698
|
-
scope,
|
|
16699
|
-
runtimeSettings,
|
|
16700
|
-
spawnAt
|
|
16701
|
-
} = options;
|
|
16702
|
-
const buffer = scope ? sliceFromOffset(accumulatedBuffer, scope.bufferStart) : accumulatedBuffer;
|
|
16703
|
-
const rawBuffer = scope ? sliceFromOffset(accumulatedRawBuffer, scope.rawBufferStart) : accumulatedRawBuffer;
|
|
16704
|
-
const screenText = terminalScreenText;
|
|
16705
|
-
const recentBuffer = buffer.slice(-1e3) || recentOutputBuffer;
|
|
16706
|
-
return {
|
|
16707
|
-
buffer,
|
|
16708
|
-
rawBuffer,
|
|
16709
|
-
recentBuffer,
|
|
16710
|
-
screenText,
|
|
16711
|
-
workspace: workingDir,
|
|
16712
|
-
workingDir,
|
|
16713
|
-
providerSessionId,
|
|
16714
|
-
historySessionId,
|
|
16715
|
-
screen: buildCliScreenSnapshot(screenText),
|
|
16716
|
-
bufferScreen: buildCliScreenSnapshot(buffer),
|
|
16717
|
-
recentScreen: buildCliScreenSnapshot(recentBuffer),
|
|
16718
|
-
messages: [...baseMessages],
|
|
16719
|
-
partialResponse,
|
|
16720
|
-
isWaitingForResponse,
|
|
16721
|
-
promptText: scope?.prompt || "",
|
|
16722
|
-
settings: { ...runtimeSettings },
|
|
16723
|
-
...typeof spawnAt === "number" && spawnAt > 0 ? { spawnAt } : {}
|
|
16724
|
-
};
|
|
16588
|
+
function compileSpinnerMatchers(spec) {
|
|
16589
|
+
return spec.patterns.map((p) => compile2(p.regex, p.flags ?? "i"));
|
|
16725
16590
|
}
|
|
16726
|
-
function
|
|
16727
|
-
const
|
|
16728
|
-
|
|
16729
|
-
|
|
16591
|
+
function compileSettledPromptMatchers(spec) {
|
|
16592
|
+
const prompt = compile2(spec.regex, spec.flags ?? "m");
|
|
16593
|
+
const footers = (spec.withFooter ?? []).map((f) => {
|
|
16594
|
+
if (f.kind === "regex") {
|
|
16595
|
+
const re = compile2(f.pattern, f.flags ?? "i");
|
|
16596
|
+
return { test: (s2) => re.test(s2) };
|
|
16597
|
+
}
|
|
16598
|
+
const needle = f.pattern.toLowerCase();
|
|
16599
|
+
return { test: (s2) => s2.toLowerCase().includes(needle) };
|
|
16600
|
+
});
|
|
16601
|
+
return { prompt, footers };
|
|
16730
16602
|
}
|
|
16731
|
-
|
|
16732
|
-
|
|
16733
|
-
|
|
16734
|
-
|
|
16735
|
-
|
|
16736
|
-
|
|
16603
|
+
function extractButtonLabels(spec, text) {
|
|
16604
|
+
if (!text) return [];
|
|
16605
|
+
const flags = spec.buttonFlags && spec.buttonFlags.includes("m") ? spec.buttonFlags : `${spec.buttonFlags ?? ""}m`;
|
|
16606
|
+
const buttonRe = compile2(spec.buttonPattern, flags);
|
|
16607
|
+
const labelGroup = Number.isInteger(spec.buttonLabelGroup) && (spec.buttonLabelGroup ?? 0) > 0 ? spec.buttonLabelGroup : 1;
|
|
16608
|
+
const out = [];
|
|
16609
|
+
for (const line of text.split("\n")) {
|
|
16610
|
+
buttonRe.lastIndex = 0;
|
|
16611
|
+
const m = buttonRe.exec(line);
|
|
16612
|
+
if (!m) continue;
|
|
16613
|
+
const captured = m[labelGroup] ?? (labelGroup === 1 && m.length > 2 ? m[m.length - 1] : void 0);
|
|
16614
|
+
if (captured && captured.trim()) out.push(captured.trim());
|
|
16615
|
+
}
|
|
16616
|
+
return out;
|
|
16617
|
+
}
|
|
16618
|
+
function buttonBlockApprovalCue(spec, text) {
|
|
16619
|
+
const labels = extractButtonLabels(spec, text);
|
|
16620
|
+
if (labels.length < 2) return false;
|
|
16621
|
+
if (pickApprovalButton(labels).index < 0) return false;
|
|
16622
|
+
return hasNegativeApprovalOption(labels);
|
|
16623
|
+
}
|
|
16624
|
+
function modalMatches(spec, input) {
|
|
16625
|
+
const text = input.screenText ?? "";
|
|
16626
|
+
const question = compile2(spec.questionPattern, spec.questionFlags ?? "i");
|
|
16627
|
+
if (question.test(text)) return true;
|
|
16628
|
+
for (const variant of spec.questionVariants ?? []) {
|
|
16629
|
+
const re = compile2(variant.regex, variant.flags ?? "i");
|
|
16630
|
+
if (re.test(text)) return true;
|
|
16631
|
+
}
|
|
16632
|
+
if (buttonBlockApprovalCue(spec, text)) return true;
|
|
16633
|
+
return false;
|
|
16634
|
+
}
|
|
16635
|
+
function evaluateGroup(group, spec, input, compiled) {
|
|
16636
|
+
switch (group) {
|
|
16637
|
+
case "spinner": {
|
|
16638
|
+
if (!spec.spinner || !compiled.spinner) return null;
|
|
16639
|
+
const text = scopeText(input, spec.spinner.scope, spec.spinner.scopeWindowLines);
|
|
16640
|
+
return compiled.spinner.some((re) => re.test(text)) ? "generating" : null;
|
|
16641
|
+
}
|
|
16642
|
+
case "modal": {
|
|
16643
|
+
if (!spec.modal) return null;
|
|
16644
|
+
return modalMatches(spec.modal, input) ? "waiting_approval" : null;
|
|
16645
|
+
}
|
|
16646
|
+
case "settled-prompt": {
|
|
16647
|
+
if (!spec.settledPrompt || !compiled.settled) return null;
|
|
16648
|
+
const text = scopeText(input, spec.settledPrompt.scope, spec.settledPrompt.scopeWindowLines);
|
|
16649
|
+
if (!compiled.settled.prompt.test(text)) return null;
|
|
16650
|
+
if (compiled.settled.footers.length === 0) return "idle";
|
|
16651
|
+
return compiled.settled.footers.every((f) => f.test(text)) ? "idle" : null;
|
|
16652
|
+
}
|
|
16653
|
+
// Groups declared in the catalog but not yet implemented as builder steps
|
|
16654
|
+
// return null so they are no-ops in dispatch — declaring them in `order`
|
|
16655
|
+
// is forward-compatible. Phase 2 Week 8+ will wire them.
|
|
16656
|
+
case "cue-ordering":
|
|
16657
|
+
case "error-detection":
|
|
16658
|
+
case "approval-stitching":
|
|
16659
|
+
return null;
|
|
16660
|
+
default:
|
|
16661
|
+
return null;
|
|
16662
|
+
}
|
|
16663
|
+
}
|
|
16664
|
+
function buildDetectStatusFromTui(spec) {
|
|
16665
|
+
const compiledSpinner = spec.spinner ? compileSpinnerMatchers(spec.spinner) : null;
|
|
16666
|
+
const compiledSettled = spec.settledPrompt ? compileSettledPromptMatchers(spec.settledPrompt) : null;
|
|
16667
|
+
const compiled = { spinner: compiledSpinner, settled: compiledSettled };
|
|
16668
|
+
const order = spec.dispatchOrder?.order && spec.dispatchOrder.order.length > 0 ? spec.dispatchOrder.order : DEFAULT_ORDER;
|
|
16669
|
+
return function detectStatus(input) {
|
|
16670
|
+
const effectiveInput = spec.visibleRegion ? {
|
|
16671
|
+
...input,
|
|
16672
|
+
screenText: applyVisibleRegion(spec.visibleRegion, input.screenText ?? ""),
|
|
16673
|
+
tail: applyVisibleRegion(spec.visibleRegion, input.tail)
|
|
16674
|
+
} : input;
|
|
16675
|
+
for (const group of order) {
|
|
16676
|
+
const verdict = evaluateGroup(group, spec, effectiveInput, compiled);
|
|
16677
|
+
if (verdict !== null) return verdict;
|
|
16678
|
+
}
|
|
16679
|
+
return null;
|
|
16680
|
+
};
|
|
16681
|
+
}
|
|
16682
|
+
var DEFAULT_ORDER;
|
|
16683
|
+
var init_detect_status = __esm({
|
|
16684
|
+
"src/providers/sdk/v1/builders/cli/detect-status.ts"() {
|
|
16685
|
+
"use strict";
|
|
16686
|
+
init_visible_region();
|
|
16687
|
+
init_approval_utils();
|
|
16688
|
+
DEFAULT_ORDER = ["spinner", "modal", "settled-prompt"];
|
|
16689
|
+
}
|
|
16690
|
+
});
|
|
16737
16691
|
|
|
16738
|
-
// src/cli
|
|
16739
|
-
|
|
16740
|
-
|
|
16741
|
-
|
|
16692
|
+
// src/providers/sdk/v1/builders/cli/parse-approval.ts
|
|
16693
|
+
function compile3(re, flags) {
|
|
16694
|
+
try {
|
|
16695
|
+
return new RegExp(re, flags);
|
|
16696
|
+
} catch (e) {
|
|
16697
|
+
throw new Error(`Invalid regex /${re}/${flags ?? ""}: ${e.message}`);
|
|
16698
|
+
}
|
|
16699
|
+
}
|
|
16700
|
+
function findQuestionLineIndex(spec, lines) {
|
|
16701
|
+
const primary = compile3(spec.questionPattern, spec.questionFlags ?? "i");
|
|
16702
|
+
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
16703
|
+
if (primary.test(lines[i])) return { index: i, matchedSource: "primary" };
|
|
16704
|
+
}
|
|
16705
|
+
for (const variant of spec.questionVariants ?? []) {
|
|
16706
|
+
const re = compile3(variant.regex, variant.flags ?? "i");
|
|
16707
|
+
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
16708
|
+
if (re.test(lines[i])) return { index: i, matchedSource: variant.label ?? "variant" };
|
|
16709
|
+
}
|
|
16710
|
+
}
|
|
16711
|
+
return null;
|
|
16712
|
+
}
|
|
16713
|
+
function scopeLines(spec, lines, questionIndex) {
|
|
16714
|
+
const scope = spec.scope ?? "between-last-two-separators";
|
|
16715
|
+
if (scope === "whole-screen") {
|
|
16716
|
+
return { start: 0, end: lines.length };
|
|
16717
|
+
}
|
|
16718
|
+
if (scope === "window-around-question") {
|
|
16719
|
+
const window = spec.scopeWindowLines ?? 16;
|
|
16720
|
+
return {
|
|
16721
|
+
start: Math.max(0, questionIndex - 2),
|
|
16722
|
+
end: Math.min(lines.length, questionIndex + window)
|
|
16723
|
+
};
|
|
16724
|
+
}
|
|
16725
|
+
let lastSep = -1;
|
|
16726
|
+
let prevSep = -1;
|
|
16727
|
+
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
16728
|
+
if (SEPARATOR_RE.test(lines[i].trim())) {
|
|
16729
|
+
if (lastSep < 0) lastSep = i;
|
|
16730
|
+
else if (prevSep < 0) {
|
|
16731
|
+
prevSep = i;
|
|
16732
|
+
break;
|
|
16733
|
+
}
|
|
16734
|
+
}
|
|
16735
|
+
}
|
|
16736
|
+
if (lastSep >= 0 && prevSep >= 0) {
|
|
16737
|
+
return { start: prevSep, end: lastSep + 1 };
|
|
16738
|
+
}
|
|
16739
|
+
return {
|
|
16740
|
+
start: Math.max(0, questionIndex - 4),
|
|
16741
|
+
end: Math.min(lines.length, questionIndex + 16)
|
|
16742
|
+
};
|
|
16743
|
+
}
|
|
16744
|
+
function extractButtons(spec, lines, windowStart, windowEnd) {
|
|
16745
|
+
const buttonRe = compile3(spec.buttonPattern, spec.buttonFlags ?? "m");
|
|
16746
|
+
const out = [];
|
|
16747
|
+
const labelGroup = Number.isInteger(spec.buttonLabelGroup) && (spec.buttonLabelGroup ?? 0) > 0 ? spec.buttonLabelGroup : 1;
|
|
16748
|
+
let i = windowStart;
|
|
16749
|
+
while (i < windowEnd) {
|
|
16750
|
+
const line = lines[i];
|
|
16751
|
+
const m = buttonRe.exec(line);
|
|
16752
|
+
const captured = m?.[labelGroup] ?? (labelGroup === 1 && m && m.length > 2 ? m[m.length - 1] : void 0);
|
|
16753
|
+
if (m && captured) {
|
|
16754
|
+
let label = captured.trim();
|
|
16755
|
+
if (spec.continuationLines) {
|
|
16756
|
+
let j = i + 1;
|
|
16757
|
+
while (j < windowEnd) {
|
|
16758
|
+
const next = lines[j];
|
|
16759
|
+
if (!next.trim()) break;
|
|
16760
|
+
if (buttonRe.test(next)) break;
|
|
16761
|
+
if (!/^\s+/.test(next)) break;
|
|
16762
|
+
label += " " + next.trim();
|
|
16763
|
+
j += 1;
|
|
16764
|
+
}
|
|
16765
|
+
i = j;
|
|
16766
|
+
} else {
|
|
16767
|
+
i += 1;
|
|
16768
|
+
}
|
|
16769
|
+
out.push(label);
|
|
16770
|
+
} else {
|
|
16771
|
+
i += 1;
|
|
16772
|
+
}
|
|
16773
|
+
}
|
|
16774
|
+
return out;
|
|
16775
|
+
}
|
|
16776
|
+
function buildMessage(spec, lines, questionIndex, windowStart, windowEnd) {
|
|
16777
|
+
const messageParts = [];
|
|
16778
|
+
if (spec.contextHeader) {
|
|
16779
|
+
const ctxRe = compile3(spec.contextHeader.regex, (spec.contextHeader.flags ?? "i") + "m");
|
|
16780
|
+
const haystack = lines.slice(windowStart, windowEnd).join("\n");
|
|
16781
|
+
const m = ctxRe.exec(haystack);
|
|
16782
|
+
if (m) messageParts.push(m[1] ? m[1].trim() : m[0].trim());
|
|
16783
|
+
}
|
|
16784
|
+
messageParts.push(lines[questionIndex].trim());
|
|
16785
|
+
return messageParts.filter(Boolean).join(" \u2014 ");
|
|
16786
|
+
}
|
|
16787
|
+
function extractInlineButtons(spec, lines, windowStart, windowEnd) {
|
|
16788
|
+
if (!spec.inlineButtonPattern) return [];
|
|
16789
|
+
const declaredFlags = spec.inlineButtonFlags ?? "gi";
|
|
16790
|
+
const flags = declaredFlags.includes("g") ? declaredFlags : declaredFlags + "g";
|
|
16791
|
+
const re = compile3(spec.inlineButtonPattern, flags);
|
|
16792
|
+
const out = [];
|
|
16793
|
+
for (let i = windowStart; i < windowEnd; i += 1) {
|
|
16794
|
+
let m;
|
|
16795
|
+
re.lastIndex = 0;
|
|
16796
|
+
while ((m = re.exec(lines[i])) !== null) {
|
|
16797
|
+
const label = (m[1] ?? m[0]).trim();
|
|
16798
|
+
if (label && !out.includes(label)) out.push(label);
|
|
16799
|
+
if (m.index === re.lastIndex) re.lastIndex += 1;
|
|
16800
|
+
}
|
|
16801
|
+
}
|
|
16802
|
+
return out;
|
|
16803
|
+
}
|
|
16804
|
+
function buildParseApprovalFromTui(spec, visibleRegion) {
|
|
16805
|
+
const minButtons = spec.minButtons ?? 2;
|
|
16806
|
+
return function parseApproval(input) {
|
|
16807
|
+
const rawText = input.screenText ?? input.buffer ?? "";
|
|
16808
|
+
if (!rawText) return null;
|
|
16809
|
+
const text = visibleRegion ? applyVisibleRegion(visibleRegion, rawText) : rawText;
|
|
16810
|
+
const lines = text.split("\n");
|
|
16811
|
+
const question = findQuestionLineIndex(spec, lines);
|
|
16812
|
+
if (!question) return null;
|
|
16813
|
+
const { start, end } = scopeLines(spec, lines, question.index);
|
|
16814
|
+
if (question.index < start || question.index >= end) return null;
|
|
16815
|
+
let buttons = extractButtons(spec, lines, question.index + 1, end);
|
|
16816
|
+
if (buttons.length < minButtons && spec.inlineButtonPattern) {
|
|
16817
|
+
buttons = extractInlineButtons(spec, lines, question.index, end);
|
|
16818
|
+
}
|
|
16819
|
+
if (buttons.length < minButtons) return null;
|
|
16820
|
+
const message = buildMessage(spec, lines, question.index, start, end);
|
|
16821
|
+
return { message, buttons };
|
|
16822
|
+
};
|
|
16823
|
+
}
|
|
16824
|
+
var SEPARATOR_RE;
|
|
16825
|
+
var init_parse_approval = __esm({
|
|
16826
|
+
"src/providers/sdk/v1/builders/cli/parse-approval.ts"() {
|
|
16827
|
+
"use strict";
|
|
16828
|
+
init_visible_region();
|
|
16829
|
+
SEPARATOR_RE = /^(?:─|━|═|━){10,}\s*$/;
|
|
16830
|
+
}
|
|
16831
|
+
});
|
|
16832
|
+
|
|
16833
|
+
// src/providers/sdk/v1/builders/cli/parse-session.ts
|
|
16834
|
+
import * as crypto3 from "crypto";
|
|
16835
|
+
function stripAnsi2(text) {
|
|
16836
|
+
return String(text || "").replace(/\x1b\[(\d*)C/g, (_m, n) => " ".repeat(Math.max(1, Number(n) || 1))).replace(/\x1b\[\d*D/g, "").replace(ANSI_RE, "").replace(OSC_RE, "").replace(/\x1b[P^_X][\s\S]*?(?:\x07|\x1b\\)/g, "").replace(/\x1b(?:[@-Z\\-_])/g, "");
|
|
16837
|
+
}
|
|
16838
|
+
function splitLines(text) {
|
|
16839
|
+
return stripAnsi2(text).replace(//g, "").split(/\r?\n/).map((l) => l.replace(/\s+$/, ""));
|
|
16840
|
+
}
|
|
16841
|
+
function pickInputText(input, scope) {
|
|
16842
|
+
if (!input) return "";
|
|
16843
|
+
if (scope === "screen") return String(input.screenText || input.screen?.text || "");
|
|
16844
|
+
if (scope === "tail") return String(input.tail || input.recentBuffer || input.buffer || "");
|
|
16845
|
+
return String(input.buffer || input.rawBuffer || input.screenText || "");
|
|
16846
|
+
}
|
|
16847
|
+
function stableHash(value) {
|
|
16848
|
+
return crypto3.createHash("sha1").update(String(value || "")).digest("hex").slice(0, 12);
|
|
16849
|
+
}
|
|
16850
|
+
function normalizeMessageIdentity(messages, status) {
|
|
16851
|
+
const list = Array.isArray(messages) ? messages : [];
|
|
16852
|
+
let turnIndex = -1;
|
|
16853
|
+
return list.map((message, index) => {
|
|
16854
|
+
const role = message?.role || "assistant";
|
|
16855
|
+
const kind = message?.kind || "standard";
|
|
16856
|
+
const content = typeof message?.content === "string" ? message.content : "";
|
|
16857
|
+
if (role === "user" || turnIndex < 0) turnIndex += 1;
|
|
16858
|
+
const seed = [role, kind, "", index, content].join("\n");
|
|
16859
|
+
const providerUnitKey = `v2-pty:${role}:${kind}:${index}:${stableHash(seed)}`;
|
|
16860
|
+
const bubbleId = `bubble:${providerUnitKey}`;
|
|
16861
|
+
const turnKey = `turn:${turnIndex}`;
|
|
16862
|
+
const isStreamingTail = status === "generating" && role === "assistant" && index === list.length - 1;
|
|
16863
|
+
return {
|
|
16864
|
+
...message,
|
|
16865
|
+
providerUnitKey,
|
|
16866
|
+
bubbleId,
|
|
16867
|
+
sequence: index,
|
|
16868
|
+
_turnKey: turnKey,
|
|
16869
|
+
bubbleState: isStreamingTail ? "streaming" : "final"
|
|
16870
|
+
};
|
|
16871
|
+
});
|
|
16872
|
+
}
|
|
16873
|
+
function buildParseSessionFromTui(spec) {
|
|
16874
|
+
if (!spec.transcriptPty) {
|
|
16875
|
+
throw new Error("buildParseSessionFromTui: spec.transcriptPty is required");
|
|
16876
|
+
}
|
|
16877
|
+
const assistantRe = new RegExp(spec.transcriptPty.assistantPrefix.regex, spec.transcriptPty.assistantPrefix.flags || "");
|
|
16878
|
+
const userRe = spec.transcriptPty.userPrefix ? new RegExp(spec.transcriptPty.userPrefix.regex, spec.transcriptPty.userPrefix.flags || "") : null;
|
|
16879
|
+
const toolRe = spec.transcriptPty.toolPrefix ? new RegExp(spec.transcriptPty.toolPrefix.regex, spec.transcriptPty.toolPrefix.flags || "") : null;
|
|
16880
|
+
const toolSkip = spec.transcriptPty.toolPrefix?.skip ?? false;
|
|
16881
|
+
const chromeRes = (spec.transcriptPty.chromePatterns || []).map((p) => new RegExp(p.regex, p.flags || ""));
|
|
16882
|
+
const requireIndentForContinuation = spec.transcriptPty.continuationLine?.indented ?? false;
|
|
16883
|
+
const stripLeadingChrome = spec.transcriptPty.stripLeadingChrome ?? true;
|
|
16884
|
+
const scope = spec.transcriptPty.scope ?? "buffer";
|
|
16885
|
+
const detectStatus = spec.spinner || spec.settledPrompt || spec.modal || spec.dispatchOrder ? buildDetectStatusFromTui({
|
|
16886
|
+
spinner: spec.spinner,
|
|
16887
|
+
settledPrompt: spec.settledPrompt,
|
|
16888
|
+
modal: spec.modal,
|
|
16889
|
+
dispatchOrder: spec.dispatchOrder
|
|
16890
|
+
}) : () => null;
|
|
16891
|
+
const parseApproval = spec.modal ? buildParseApprovalFromTui(spec.modal) : () => null;
|
|
16892
|
+
const sessionIdRe = spec.sessionIdExtraction ? new RegExp(
|
|
16893
|
+
spec.sessionIdExtraction.regex,
|
|
16894
|
+
spec.sessionIdExtraction.flags ?? "i"
|
|
16895
|
+
) : null;
|
|
16896
|
+
const sessionIdScope = spec.sessionIdExtraction?.scope ?? "tail";
|
|
16897
|
+
return function parseSession(input) {
|
|
16898
|
+
const status = detectStatus(input) ?? "idle";
|
|
16899
|
+
const modal = parseApproval(input);
|
|
16900
|
+
const text = pickInputText(input, scope);
|
|
16901
|
+
const lines = splitLines(text);
|
|
16902
|
+
const messages = [];
|
|
16903
|
+
let seenFirstRoleLine = !stripLeadingChrome;
|
|
16904
|
+
for (const raw of lines) {
|
|
16905
|
+
const line = raw;
|
|
16906
|
+
if (line.trim() === "") {
|
|
16907
|
+
continue;
|
|
16908
|
+
}
|
|
16909
|
+
let isChrome = false;
|
|
16910
|
+
for (const cre of chromeRes) {
|
|
16911
|
+
if (cre.test(line)) {
|
|
16912
|
+
isChrome = true;
|
|
16913
|
+
break;
|
|
16914
|
+
}
|
|
16915
|
+
}
|
|
16916
|
+
if (isChrome) continue;
|
|
16917
|
+
const userMatch = userRe ? line.match(userRe) : null;
|
|
16918
|
+
const toolMatch = toolRe ? line.match(toolRe) : null;
|
|
16919
|
+
const assistMatch = line.match(assistantRe);
|
|
16920
|
+
if (userMatch) {
|
|
16921
|
+
seenFirstRoleLine = true;
|
|
16922
|
+
const content = (userMatch[1] ?? userMatch[0]).trim();
|
|
16923
|
+
if (content) messages.push({ role: "user", kind: "standard", content });
|
|
16924
|
+
continue;
|
|
16925
|
+
}
|
|
16926
|
+
if (toolMatch) {
|
|
16927
|
+
seenFirstRoleLine = true;
|
|
16928
|
+
if (toolSkip) continue;
|
|
16929
|
+
const content = (toolMatch[1] ?? toolMatch[0]).trim();
|
|
16930
|
+
if (content) messages.push({ role: "assistant", kind: "tool", content });
|
|
16931
|
+
continue;
|
|
16932
|
+
}
|
|
16933
|
+
if (assistMatch) {
|
|
16934
|
+
seenFirstRoleLine = true;
|
|
16935
|
+
const content = (assistMatch[1] ?? assistMatch[0]).trim();
|
|
16936
|
+
if (content) messages.push({ role: "assistant", kind: "standard", content });
|
|
16937
|
+
continue;
|
|
16938
|
+
}
|
|
16939
|
+
if (!seenFirstRoleLine) continue;
|
|
16940
|
+
if (requireIndentForContinuation && !/^\s/.test(line)) continue;
|
|
16941
|
+
const last = messages[messages.length - 1];
|
|
16942
|
+
if (!last) continue;
|
|
16943
|
+
const cont = line.replace(/^\s+/, "");
|
|
16944
|
+
if (cont) last.content = last.content ? `${last.content}
|
|
16945
|
+
${cont}` : cont;
|
|
16946
|
+
}
|
|
16947
|
+
const result = {
|
|
16948
|
+
status,
|
|
16949
|
+
messages,
|
|
16950
|
+
activeModal: modal,
|
|
16951
|
+
modal,
|
|
16952
|
+
parsedStatus: status
|
|
16953
|
+
};
|
|
16954
|
+
if (sessionIdRe) {
|
|
16955
|
+
const haystack = stripAnsi2(pickInputText(input, sessionIdScope));
|
|
16956
|
+
const match = haystack.match(sessionIdRe);
|
|
16957
|
+
const captured = match?.[1]?.trim();
|
|
16958
|
+
if (captured) result.providerSessionId = captured;
|
|
16959
|
+
}
|
|
16960
|
+
return result;
|
|
16961
|
+
};
|
|
16962
|
+
}
|
|
16963
|
+
var ANSI_RE, OSC_RE;
|
|
16964
|
+
var init_parse_session = __esm({
|
|
16965
|
+
"src/providers/sdk/v1/builders/cli/parse-session.ts"() {
|
|
16966
|
+
"use strict";
|
|
16967
|
+
init_detect_status();
|
|
16968
|
+
init_parse_approval();
|
|
16969
|
+
ANSI_RE = /\x1b\[[0-?]*[ -/]*[@-~]/g;
|
|
16970
|
+
OSC_RE = /\x1b\][^\x07\x1b\n]*(?:\x07|\x1b\\|(?=\n|$))/g;
|
|
16971
|
+
}
|
|
16972
|
+
});
|
|
16973
|
+
|
|
16974
|
+
// src/cli-adapters/cli-script-runner.ts
|
|
16975
|
+
function summarizeInput(input) {
|
|
16976
|
+
const screenText = typeof input?.screenText === "string" ? input.screenText : "";
|
|
16977
|
+
const rawBuffer = typeof input?.rawBuffer === "string" ? input.rawBuffer : "";
|
|
16978
|
+
const tail = typeof input?.tail === "string" ? input.tail : "";
|
|
16979
|
+
return {
|
|
16980
|
+
screenTextLen: screenText.length,
|
|
16981
|
+
rawBufferLen: rawBuffer.length,
|
|
16982
|
+
tailLen: tail.length,
|
|
16983
|
+
isWaitingForResponse: typeof input?.isWaitingForResponse === "boolean" ? input.isWaitingForResponse : void 0,
|
|
16984
|
+
screenTextHead: screenText ? screenText.slice(0, 200) : void 0
|
|
16985
|
+
};
|
|
16986
|
+
}
|
|
16987
|
+
function summarizeResult(result) {
|
|
16988
|
+
try {
|
|
16989
|
+
const json = JSON.stringify(result);
|
|
16990
|
+
return json && json.length > 400 ? `${json.slice(0, 400)}\u2026[truncated ${json.length - 400}]` : json ?? "undefined";
|
|
16991
|
+
} catch (e) {
|
|
16992
|
+
return `<unserializable: ${e?.message || e}>`;
|
|
16993
|
+
}
|
|
16994
|
+
}
|
|
16995
|
+
var TRACE_RING_CAPACITY, DEFAULT_SCRIPT_CALL_BUDGET_MS, BUDGET_WARN_THROTTLE_MS, CliScriptRunner;
|
|
16996
|
+
var init_cli_script_runner = __esm({
|
|
16997
|
+
"src/cli-adapters/cli-script-runner.ts"() {
|
|
16742
16998
|
"use strict";
|
|
16743
16999
|
init_logger();
|
|
16744
|
-
init_provider_cli_parse();
|
|
16745
17000
|
init_provider_cli_shared();
|
|
16746
|
-
|
|
16747
|
-
|
|
16748
|
-
|
|
16749
|
-
|
|
16750
|
-
|
|
16751
|
-
|
|
16752
|
-
|
|
16753
|
-
|
|
16754
|
-
|
|
16755
|
-
|
|
16756
|
-
|
|
16757
|
-
|
|
16758
|
-
|
|
17001
|
+
init_detect_status();
|
|
17002
|
+
init_parse_approval();
|
|
17003
|
+
init_parse_session();
|
|
17004
|
+
TRACE_RING_CAPACITY = 64;
|
|
17005
|
+
DEFAULT_SCRIPT_CALL_BUDGET_MS = 50;
|
|
17006
|
+
BUDGET_WARN_THROTTLE_MS = 3e4;
|
|
17007
|
+
CliScriptRunner = class {
|
|
17008
|
+
scripts = {};
|
|
17009
|
+
scriptState = null;
|
|
17010
|
+
_parseErrorMessage = null;
|
|
17011
|
+
cliType;
|
|
17012
|
+
sdk = {};
|
|
17013
|
+
invocationTrace = [];
|
|
17014
|
+
/** Per-invocation wall-clock budget (ms). Configurable via setScriptCallBudget. */
|
|
17015
|
+
scriptCallBudgetMs = DEFAULT_SCRIPT_CALL_BUDGET_MS;
|
|
17016
|
+
/** Last WARN emit time per scriptName, used to throttle repeated budget violations. */
|
|
17017
|
+
lastBudgetWarnAt = /* @__PURE__ */ new Map();
|
|
17018
|
+
constructor(cliType) {
|
|
17019
|
+
this.cliType = cliType;
|
|
17020
|
+
}
|
|
17021
|
+
/** Returns the most-recent script invocation traces (oldest → newest). */
|
|
17022
|
+
getInvocationTrace() {
|
|
17023
|
+
return this.invocationTrace.slice();
|
|
17024
|
+
}
|
|
17025
|
+
/** Clear the trace ring — used by tests and after PTY reset. */
|
|
17026
|
+
clearInvocationTrace() {
|
|
17027
|
+
this.invocationTrace = [];
|
|
16759
17028
|
}
|
|
16760
|
-
// ── Status ───────────────────────────────────────
|
|
16761
|
-
currentStatus = "starting";
|
|
16762
|
-
isWaitingForResponse = false;
|
|
16763
|
-
currentTurnScope = null;
|
|
16764
|
-
activeModal = null;
|
|
16765
|
-
// ── Approval ─────────────────────────────────────
|
|
16766
|
-
lastApprovalResolvedAt = 0;
|
|
16767
|
-
lastResolvedModalMessage = "";
|
|
16768
17029
|
/**
|
|
16769
|
-
*
|
|
16770
|
-
* with a freshly captured modal (see `applyWaitingApproval`). It is the
|
|
16771
|
-
* single discriminator between "the same approval re-observed across TUI
|
|
16772
|
-
* paint flaps" and "a genuinely new, distinct approval".
|
|
16773
|
-
*
|
|
16774
|
-
* The message-equality cooldown below (`lastResolvedModalMessage`) cannot
|
|
16775
|
-
* tell these apart on its own: claude-cli routinely presents consecutive
|
|
16776
|
-
* approvals whose modal message text is identical (e.g. two back-to-back
|
|
16777
|
-
* Bash-command prompts). When that second approval arrived inside
|
|
16778
|
-
* `approvalCooldown`, the message-equality guard silently swallowed the
|
|
16779
|
-
* key write and the approval stuck forever — fatal under auto-approval.
|
|
17030
|
+
* Configure the wall-clock budget (ms) applied to every script invocation.
|
|
16780
17031
|
*
|
|
16781
|
-
*
|
|
16782
|
-
*
|
|
16783
|
-
*
|
|
16784
|
-
*
|
|
16785
|
-
|
|
16786
|
-
|
|
16787
|
-
|
|
16788
|
-
/**
|
|
16789
|
-
* When the engine previously held a modal but the latest parse failed
|
|
16790
|
-
* to extract one, we record the timestamp here and only drop the modal
|
|
16791
|
-
* after the configured `approvalCooldown` to avoid flapping between
|
|
16792
|
-
* waiting_approval and generating on every Claude TUI redraw — that
|
|
16793
|
-
* flapping is what fed auto-approve a fresh modal signature on each
|
|
16794
|
-
* paint and made the engine type "1" repeatedly into the prompt.
|
|
17032
|
+
* Out-of-range or non-finite values are clamped to [1, 5000] and the
|
|
17033
|
+
* default (50ms) is used as a fallback. The budget is enforced per-call,
|
|
17034
|
+
* not aggregated — it does not abort a runaway script (Node CJS cannot
|
|
17035
|
+
* interrupt synchronous code without a worker thread). Instead, an
|
|
17036
|
+
* exceeded budget flags the trace entry with `timedOut: true` and emits
|
|
17037
|
+
* a throttled WARN naming the script and elapsed time so an operator
|
|
17038
|
+
* can identify which provider is hanging the settle loop.
|
|
16795
17039
|
*/
|
|
16796
|
-
|
|
16797
|
-
|
|
16798
|
-
|
|
16799
|
-
|
|
16800
|
-
|
|
16801
|
-
|
|
16802
|
-
|
|
16803
|
-
|
|
16804
|
-
|
|
16805
|
-
|
|
16806
|
-
|
|
16807
|
-
|
|
16808
|
-
|
|
16809
|
-
|
|
16810
|
-
|
|
16811
|
-
|
|
16812
|
-
|
|
16813
|
-
|
|
16814
|
-
|
|
16815
|
-
|
|
16816
|
-
|
|
16817
|
-
|
|
16818
|
-
|
|
16819
|
-
|
|
16820
|
-
|
|
16821
|
-
|
|
16822
|
-
|
|
16823
|
-
|
|
16824
|
-
|
|
16825
|
-
|
|
16826
|
-
|
|
16827
|
-
|
|
17040
|
+
setScriptCallBudget(ms) {
|
|
17041
|
+
if (typeof ms !== "number" || !Number.isFinite(ms)) {
|
|
17042
|
+
this.scriptCallBudgetMs = DEFAULT_SCRIPT_CALL_BUDGET_MS;
|
|
17043
|
+
return;
|
|
17044
|
+
}
|
|
17045
|
+
const clamped = Math.max(1, Math.min(5e3, Math.floor(ms)));
|
|
17046
|
+
this.scriptCallBudgetMs = clamped;
|
|
17047
|
+
}
|
|
17048
|
+
/** Test/debug accessor — current effective budget in ms. */
|
|
17049
|
+
getScriptCallBudgetMs() {
|
|
17050
|
+
return this.scriptCallBudgetMs;
|
|
17051
|
+
}
|
|
17052
|
+
recordTrace(entry) {
|
|
17053
|
+
this.invocationTrace.push(entry);
|
|
17054
|
+
if (this.invocationTrace.length > TRACE_RING_CAPACITY) {
|
|
17055
|
+
this.invocationTrace.splice(0, this.invocationTrace.length - TRACE_RING_CAPACITY);
|
|
17056
|
+
}
|
|
17057
|
+
}
|
|
17058
|
+
// ─── Script lifecycle ─────────────────────────────
|
|
17059
|
+
setScripts(scripts, providerTui) {
|
|
17060
|
+
this.sdk = this.buildSdk(providerTui);
|
|
17061
|
+
const tui = providerTui;
|
|
17062
|
+
const enriched = { ...scripts };
|
|
17063
|
+
if (typeof enriched.detectStatus !== "function" && this.sdk.declarativeDetectStatus) {
|
|
17064
|
+
enriched.detectStatus = this.sdk.declarativeDetectStatus;
|
|
17065
|
+
}
|
|
17066
|
+
if (typeof enriched.parseApproval !== "function" && this.sdk.declarativeParseApproval) {
|
|
17067
|
+
enriched.parseApproval = this.sdk.declarativeParseApproval;
|
|
17068
|
+
}
|
|
17069
|
+
if (typeof enriched.parseSession !== "function" && tui?.transcriptPty) {
|
|
17070
|
+
try {
|
|
17071
|
+
const synth = buildParseSessionFromTui({
|
|
17072
|
+
spinner: tui.spinner,
|
|
17073
|
+
settledPrompt: tui.settledPrompt,
|
|
17074
|
+
modal: tui.modal,
|
|
17075
|
+
dispatchOrder: tui.dispatchOrder,
|
|
17076
|
+
transcriptPty: tui.transcriptPty
|
|
17077
|
+
});
|
|
17078
|
+
enriched.parseSession = ((input) => {
|
|
17079
|
+
const out = synth(input);
|
|
17080
|
+
return {
|
|
17081
|
+
...out,
|
|
17082
|
+
messages: normalizeMessageIdentity(out.messages, out.status ?? "idle")
|
|
17083
|
+
};
|
|
17084
|
+
});
|
|
17085
|
+
} catch (e) {
|
|
17086
|
+
LOG.warn("CLI", `[${this.cliType}] buildParseSessionFromTui failed: ${e?.message || e}`);
|
|
17087
|
+
}
|
|
17088
|
+
}
|
|
17089
|
+
this.scripts = enriched;
|
|
17090
|
+
this._parseErrorMessage = null;
|
|
17091
|
+
this.scriptState = typeof enriched.createState === "function" ? enriched.createState() ?? null : null;
|
|
17092
|
+
}
|
|
17093
|
+
buildSdk(providerTui) {
|
|
17094
|
+
const tui = providerTui;
|
|
17095
|
+
if (!tui) return {};
|
|
17096
|
+
const sdk = {};
|
|
17097
|
+
if (tui.spinner || tui.settledPrompt || tui.modal || tui.dispatchOrder) {
|
|
17098
|
+
try {
|
|
17099
|
+
sdk.declarativeDetectStatus = buildDetectStatusFromTui({
|
|
17100
|
+
spinner: tui.spinner,
|
|
17101
|
+
settledPrompt: tui.settledPrompt,
|
|
17102
|
+
modal: tui.modal,
|
|
17103
|
+
dispatchOrder: tui.dispatchOrder
|
|
17104
|
+
});
|
|
17105
|
+
} catch (e) {
|
|
17106
|
+
LOG.warn("CLI", `[${this.cliType}] buildDetectStatusFromTui failed: ${e?.message || e}`);
|
|
17107
|
+
}
|
|
17108
|
+
}
|
|
17109
|
+
if (tui.modal) {
|
|
17110
|
+
try {
|
|
17111
|
+
sdk.declarativeParseApproval = buildParseApprovalFromTui(tui.modal);
|
|
17112
|
+
} catch (e) {
|
|
17113
|
+
LOG.warn("CLI", `[${this.cliType}] buildParseApprovalFromTui failed: ${e?.message || e}`);
|
|
17114
|
+
}
|
|
17115
|
+
}
|
|
17116
|
+
return sdk;
|
|
17117
|
+
}
|
|
17118
|
+
/** Reset per-session state — called when the PTY process exits. */
|
|
17119
|
+
resetSessionState() {
|
|
17120
|
+
this.scriptState = null;
|
|
17121
|
+
this.invocationTrace = [];
|
|
17122
|
+
this.lastBudgetWarnAt.clear();
|
|
17123
|
+
}
|
|
17124
|
+
// ─── Script access (for reflection and test patching) ────────────────────
|
|
17125
|
+
/** Returns the live scripts object. Direct property assignment on this object
|
|
17126
|
+
* patches individual scripts without replacing others (used in tests). */
|
|
17127
|
+
get cliScripts() {
|
|
17128
|
+
return this.scripts;
|
|
17129
|
+
}
|
|
17130
|
+
// ─── Capability checks ────────────────────────────
|
|
17131
|
+
hasDetectStatus() {
|
|
17132
|
+
return typeof this.scripts.detectStatus === "function";
|
|
17133
|
+
}
|
|
17134
|
+
hasParseSession() {
|
|
17135
|
+
return typeof this.scripts.parseSession === "function";
|
|
17136
|
+
}
|
|
17137
|
+
getScriptNames() {
|
|
17138
|
+
return listCliScriptNames(this.scripts);
|
|
17139
|
+
}
|
|
17140
|
+
// ─── Error state ──────────────────────────────────
|
|
17141
|
+
get parseErrorMessage() {
|
|
17142
|
+
return this._parseErrorMessage;
|
|
17143
|
+
}
|
|
17144
|
+
clearParseError() {
|
|
17145
|
+
this._parseErrorMessage = null;
|
|
17146
|
+
}
|
|
17147
|
+
// ─── Script invocation ────────────────────────────
|
|
17148
|
+
detectStatus(input) {
|
|
17149
|
+
if (!this.scripts.detectStatus) return null;
|
|
17150
|
+
try {
|
|
17151
|
+
return this.invoke("detectStatus", this.scripts.detectStatus, input);
|
|
17152
|
+
} catch (e) {
|
|
17153
|
+
LOG.warn("CLI", `[${this.cliType}] detectStatus error: ${e?.message || e}`);
|
|
17154
|
+
return null;
|
|
17155
|
+
}
|
|
17156
|
+
}
|
|
17157
|
+
parseApproval(input) {
|
|
17158
|
+
if (!this.scripts.parseApproval) return null;
|
|
17159
|
+
try {
|
|
17160
|
+
return this.invoke(
|
|
17161
|
+
"parseApproval",
|
|
17162
|
+
this.scripts.parseApproval,
|
|
17163
|
+
input
|
|
17164
|
+
);
|
|
17165
|
+
} catch (e) {
|
|
17166
|
+
LOG.warn("CLI", `[${this.cliType}] parseApproval error: ${e?.message || e}`);
|
|
17167
|
+
return null;
|
|
17168
|
+
}
|
|
17169
|
+
}
|
|
17170
|
+
parseSession(input) {
|
|
17171
|
+
if (!this.scripts.parseSession) {
|
|
17172
|
+
this._parseErrorMessage = `${this.cliType} parseSession unavailable`;
|
|
17173
|
+
return null;
|
|
17174
|
+
}
|
|
17175
|
+
try {
|
|
17176
|
+
const result = this.invoke("parseSession", this.scripts.parseSession, input);
|
|
17177
|
+
this._parseErrorMessage = null;
|
|
17178
|
+
return result && typeof result === "object" ? result : null;
|
|
17179
|
+
} catch (e) {
|
|
17180
|
+
this._parseErrorMessage = e?.message || String(e);
|
|
17181
|
+
LOG.warn("CLI", `[${this.cliType}] parseSession error: ${this._parseErrorMessage}`);
|
|
17182
|
+
return null;
|
|
17183
|
+
}
|
|
17184
|
+
}
|
|
17185
|
+
/**
|
|
17186
|
+
* Invoke an arbitrary named script (e.g. setModel, openModelPicker).
|
|
17187
|
+
* Throws if the script is not available.
|
|
17188
|
+
*/
|
|
17189
|
+
invokeByName(name, input) {
|
|
17190
|
+
const fn = this.scripts[name];
|
|
17191
|
+
if (typeof fn !== "function") {
|
|
17192
|
+
throw new Error(`CLI script '${name}' not available`);
|
|
17193
|
+
}
|
|
17194
|
+
return this.invoke(name, fn, input);
|
|
17195
|
+
}
|
|
17196
|
+
// ─── Internal ─────────────────────────────────────
|
|
17197
|
+
invoke(scriptName, fn, input) {
|
|
17198
|
+
const arity = fn.length;
|
|
17199
|
+
const startedAt = Date.now();
|
|
17200
|
+
const startedHr = typeof process !== "undefined" && typeof process.hrtime === "function" ? process.hrtime.bigint() : null;
|
|
17201
|
+
let result;
|
|
17202
|
+
try {
|
|
17203
|
+
if (arity >= 3) {
|
|
17204
|
+
result = fn(this.scriptState, input, this.sdk);
|
|
17205
|
+
} else if (arity === 2) {
|
|
17206
|
+
result = fn(this.scriptState, input);
|
|
17207
|
+
} else {
|
|
17208
|
+
result = fn(input);
|
|
17209
|
+
}
|
|
17210
|
+
const elapsedUs = startedHr ? Number((process.hrtime.bigint() - startedHr) / 1000n) : 0;
|
|
17211
|
+
const timedOut = this.checkBudget(scriptName, elapsedUs);
|
|
17212
|
+
this.recordTrace({
|
|
17213
|
+
at: startedAt,
|
|
17214
|
+
scriptName,
|
|
17215
|
+
arity,
|
|
17216
|
+
inputSummary: summarizeInput(input),
|
|
17217
|
+
ok: true,
|
|
17218
|
+
elapsedUs,
|
|
17219
|
+
resultSummary: summarizeResult(result),
|
|
17220
|
+
...timedOut ? { timedOut: true } : {}
|
|
17221
|
+
});
|
|
17222
|
+
return result;
|
|
17223
|
+
} catch (e) {
|
|
17224
|
+
const elapsedUs = startedHr ? Number((process.hrtime.bigint() - startedHr) / 1000n) : 0;
|
|
17225
|
+
const timedOut = this.checkBudget(scriptName, elapsedUs);
|
|
17226
|
+
this.recordTrace({
|
|
17227
|
+
at: startedAt,
|
|
17228
|
+
scriptName,
|
|
17229
|
+
arity,
|
|
17230
|
+
inputSummary: summarizeInput(input),
|
|
17231
|
+
ok: false,
|
|
17232
|
+
elapsedUs,
|
|
17233
|
+
error: e?.message ? String(e.message).slice(0, 400) : String(e).slice(0, 400),
|
|
17234
|
+
...timedOut ? { timedOut: true } : {}
|
|
17235
|
+
});
|
|
17236
|
+
throw e;
|
|
17237
|
+
}
|
|
17238
|
+
}
|
|
17239
|
+
/**
|
|
17240
|
+
* Returns true when `elapsedUs` exceeded the configured budget. On the
|
|
17241
|
+
* first violation per script (or after the throttle window expires) we
|
|
17242
|
+
* emit a single WARN so the operator learns which provider is slow.
|
|
17243
|
+
*
|
|
17244
|
+
* We deliberately throttle per-scriptName so a chronically-slow
|
|
17245
|
+
* detectStatus doesn't spam the log on every PTY frame settle.
|
|
17246
|
+
*/
|
|
17247
|
+
checkBudget(scriptName, elapsedUs) {
|
|
17248
|
+
const budgetUs = this.scriptCallBudgetMs * 1e3;
|
|
17249
|
+
if (elapsedUs <= budgetUs) return false;
|
|
17250
|
+
const now = Date.now();
|
|
17251
|
+
const last = this.lastBudgetWarnAt.get(scriptName) ?? 0;
|
|
17252
|
+
if (now - last >= BUDGET_WARN_THROTTLE_MS) {
|
|
17253
|
+
this.lastBudgetWarnAt.set(scriptName, now);
|
|
17254
|
+
LOG.warn(
|
|
17255
|
+
"CLI",
|
|
17256
|
+
`[${this.cliType}] script ${scriptName} took ${elapsedUs}us, budget ${budgetUs}us`
|
|
17257
|
+
);
|
|
17258
|
+
}
|
|
17259
|
+
return true;
|
|
17260
|
+
}
|
|
17261
|
+
};
|
|
17262
|
+
}
|
|
17263
|
+
});
|
|
17264
|
+
|
|
17265
|
+
// src/cli-adapters/provider-cli-parse.ts
|
|
17266
|
+
function sliceFromOffset(text, start) {
|
|
17267
|
+
if (!text) return "";
|
|
17268
|
+
if (!Number.isFinite(start) || start <= 0) return text;
|
|
17269
|
+
if (start >= text.length) return "";
|
|
17270
|
+
return text.slice(start);
|
|
17271
|
+
}
|
|
17272
|
+
function normalizeCliParsedMessages(parsedMessages, _options) {
|
|
17273
|
+
return Array.isArray(parsedMessages) ? parsedMessages : [];
|
|
17274
|
+
}
|
|
17275
|
+
function buildCliParseInput(options) {
|
|
17276
|
+
const {
|
|
17277
|
+
accumulatedBuffer,
|
|
17278
|
+
accumulatedRawBuffer,
|
|
17279
|
+
recentOutputBuffer,
|
|
17280
|
+
terminalScreenText,
|
|
17281
|
+
workingDir,
|
|
17282
|
+
providerSessionId,
|
|
17283
|
+
historySessionId,
|
|
17284
|
+
baseMessages,
|
|
17285
|
+
partialResponse,
|
|
17286
|
+
isWaitingForResponse,
|
|
17287
|
+
scope,
|
|
17288
|
+
runtimeSettings,
|
|
17289
|
+
spawnAt
|
|
17290
|
+
} = options;
|
|
17291
|
+
const buffer = scope ? sliceFromOffset(accumulatedBuffer, scope.bufferStart) : accumulatedBuffer;
|
|
17292
|
+
const rawBuffer = scope ? sliceFromOffset(accumulatedRawBuffer, scope.rawBufferStart) : accumulatedRawBuffer;
|
|
17293
|
+
const screenText = terminalScreenText;
|
|
17294
|
+
const recentBuffer = buffer.slice(-1e3) || recentOutputBuffer;
|
|
17295
|
+
return {
|
|
17296
|
+
buffer,
|
|
17297
|
+
rawBuffer,
|
|
17298
|
+
recentBuffer,
|
|
17299
|
+
screenText,
|
|
17300
|
+
workspace: workingDir,
|
|
17301
|
+
workingDir,
|
|
17302
|
+
providerSessionId,
|
|
17303
|
+
historySessionId,
|
|
17304
|
+
screen: buildCliScreenSnapshot(screenText),
|
|
17305
|
+
bufferScreen: buildCliScreenSnapshot(buffer),
|
|
17306
|
+
recentScreen: buildCliScreenSnapshot(recentBuffer),
|
|
17307
|
+
messages: [...baseMessages],
|
|
17308
|
+
partialResponse,
|
|
17309
|
+
isWaitingForResponse,
|
|
17310
|
+
promptText: scope?.prompt || "",
|
|
17311
|
+
settings: { ...runtimeSettings },
|
|
17312
|
+
...typeof spawnAt === "number" && spawnAt > 0 ? { spawnAt } : {}
|
|
17313
|
+
};
|
|
17314
|
+
}
|
|
17315
|
+
function summarizeCliTraceText(text, max = 800) {
|
|
17316
|
+
const value = sanitizeTerminalText(String(text || ""));
|
|
17317
|
+
if (value.length <= max) return value;
|
|
17318
|
+
return `\u2026${value.slice(-max)}`;
|
|
17319
|
+
}
|
|
17320
|
+
var init_provider_cli_parse = __esm({
|
|
17321
|
+
"src/cli-adapters/provider-cli-parse.ts"() {
|
|
17322
|
+
"use strict";
|
|
17323
|
+
init_provider_cli_shared();
|
|
17324
|
+
}
|
|
17325
|
+
});
|
|
17326
|
+
|
|
17327
|
+
// src/cli-adapters/cli-state-engine.ts
|
|
17328
|
+
var SCRIPT_STATUS_DEBOUNCE_MS, MAX_FINISH_RETRIES, FINISH_RETRY_DELAY_MS, MAX_TRACE_ENTRIES, APPROVAL_EXIT_TIMEOUT_MS, IDLE_CONFIRMATION_GRACE_MS, CliStateEngine;
|
|
17329
|
+
var init_cli_state_engine = __esm({
|
|
17330
|
+
"src/cli-adapters/cli-state-engine.ts"() {
|
|
17331
|
+
"use strict";
|
|
17332
|
+
init_logger();
|
|
17333
|
+
init_provider_cli_parse();
|
|
17334
|
+
init_provider_cli_shared();
|
|
17335
|
+
SCRIPT_STATUS_DEBOUNCE_MS = 3e3;
|
|
17336
|
+
MAX_FINISH_RETRIES = 2;
|
|
17337
|
+
FINISH_RETRY_DELAY_MS = 300;
|
|
17338
|
+
MAX_TRACE_ENTRIES = 250;
|
|
17339
|
+
APPROVAL_EXIT_TIMEOUT_MS = 6e4;
|
|
17340
|
+
IDLE_CONFIRMATION_GRACE_MS = 2e3;
|
|
17341
|
+
CliStateEngine = class {
|
|
17342
|
+
constructor(provider, runner, transport, callbacks, timeouts) {
|
|
17343
|
+
this.provider = provider;
|
|
17344
|
+
this.runner = runner;
|
|
17345
|
+
this.transport = transport;
|
|
17346
|
+
this.callbacks = callbacks;
|
|
17347
|
+
this.timeouts = timeouts;
|
|
17348
|
+
}
|
|
17349
|
+
// ── Status ───────────────────────────────────────
|
|
17350
|
+
currentStatus = "starting";
|
|
17351
|
+
isWaitingForResponse = false;
|
|
17352
|
+
currentTurnScope = null;
|
|
17353
|
+
activeModal = null;
|
|
17354
|
+
// ── Approval ─────────────────────────────────────
|
|
17355
|
+
lastApprovalResolvedAt = 0;
|
|
17356
|
+
lastResolvedModalMessage = "";
|
|
17357
|
+
/**
|
|
17358
|
+
* Monotonic counter bumped every time the FSM *enters* waiting_approval
|
|
17359
|
+
* with a freshly captured modal (see `applyWaitingApproval`). It is the
|
|
17360
|
+
* single discriminator between "the same approval re-observed across TUI
|
|
17361
|
+
* paint flaps" and "a genuinely new, distinct approval".
|
|
17362
|
+
*
|
|
17363
|
+
* The message-equality cooldown below (`lastResolvedModalMessage`) cannot
|
|
17364
|
+
* tell these apart on its own: claude-cli routinely presents consecutive
|
|
17365
|
+
* approvals whose modal message text is identical (e.g. two back-to-back
|
|
17366
|
+
* Bash-command prompts). When that second approval arrived inside
|
|
17367
|
+
* `approvalCooldown`, the message-equality guard silently swallowed the
|
|
17368
|
+
* key write and the approval stuck forever — fatal under auto-approval.
|
|
17369
|
+
*
|
|
17370
|
+
* `approvalEntrySeq` increments on every fresh entry; `lastResolvedEntrySeq`
|
|
17371
|
+
* records which entry the cooldown belongs to. We only short-circuit the
|
|
17372
|
+
* write when we are still resolving *that same* entry — a new entry (new
|
|
17373
|
+
* seq) is always a real, distinct approval and must be written.
|
|
17374
|
+
*/
|
|
17375
|
+
approvalEntrySeq = 0;
|
|
17376
|
+
lastResolvedEntrySeq = -1;
|
|
17377
|
+
/**
|
|
17378
|
+
* When the engine previously held a modal but the latest parse failed
|
|
17379
|
+
* to extract one, we record the timestamp here and only drop the modal
|
|
17380
|
+
* after the configured `approvalCooldown` to avoid flapping between
|
|
17381
|
+
* waiting_approval and generating on every Claude TUI redraw — that
|
|
17382
|
+
* flapping is what fed auto-approve a fresh modal signature on each
|
|
17383
|
+
* paint and made the engine type "1" repeatedly into the prompt.
|
|
17384
|
+
*/
|
|
17385
|
+
modalLostAt = 0;
|
|
17386
|
+
approvalExitTimeout = null;
|
|
17387
|
+
// ── Response tracking ────────────────────────────
|
|
17388
|
+
responseEpoch = 0;
|
|
17389
|
+
submitPendingUntil = 0;
|
|
17390
|
+
responseSettleIgnoreUntil = 0;
|
|
17391
|
+
submitRetryUsed = false;
|
|
17392
|
+
submitRetryPromptSnippet = "";
|
|
17393
|
+
finishRetryCount = 0;
|
|
17394
|
+
providerErrorMessage = null;
|
|
17395
|
+
providerErrorReason = null;
|
|
17396
|
+
// ── Timers ───────────────────────────────────────
|
|
17397
|
+
settleTimer = null;
|
|
17398
|
+
idleTimeout = null;
|
|
17399
|
+
finishRetryTimer = null;
|
|
17400
|
+
providerErrorRetryTimer = null;
|
|
17401
|
+
providerErrorRetryKey = "";
|
|
17402
|
+
// ── Debounce ─────────────────────────────────────
|
|
17403
|
+
pendingScriptStatus = null;
|
|
17404
|
+
pendingScriptStatusSince = 0;
|
|
17405
|
+
pendingScriptStatusTimer = null;
|
|
17406
|
+
// ── Idle candidate ───────────────────────────────
|
|
17407
|
+
idleFinishCandidate = null;
|
|
17408
|
+
// ── Idle confirmation grace ──────────────────────
|
|
17409
|
+
/**
|
|
17410
|
+
* `finishResponse` produces the `generating → idle` transition that
|
|
17411
|
+
* coordinators interpret as "task complete". Some providers (antigravity-
|
|
17412
|
+
* cli observed in the wild) briefly paint a screen that looks like an
|
|
17413
|
+
* idle prompt between tool result frames while still actively running,
|
|
17414
|
+
* which fired `response_finished` and broke completion semantics.
|
|
17415
|
+
* We defer the actual idle transition by IDLE_CONFIRMATION_GRACE_MS and
|
|
17416
|
+
* cancel it if the scripted detection re-detects generating during that
|
|
16828
17417
|
* window — a true completion stays idle for many seconds, so a 2-second
|
|
16829
17418
|
* grace is sufficient to filter the paint blip.
|
|
16830
17419
|
*/
|
|
@@ -19529,595 +20118,6 @@ ${lastSnapshot}`;
|
|
|
19529
20118
|
}
|
|
19530
20119
|
});
|
|
19531
20120
|
|
|
19532
|
-
// src/providers/spec/evaluator.ts
|
|
19533
|
-
var evaluator_exports = {};
|
|
19534
|
-
__export(evaluator_exports, {
|
|
19535
|
-
evaluateCondition: () => evaluateCondition,
|
|
19536
|
-
extractButtonsFromRule: () => extractButtonsFromRule,
|
|
19537
|
-
extractTitle: () => extractTitle,
|
|
19538
|
-
lastContiguousNumberedBlock: () => lastContiguousNumberedBlock,
|
|
19539
|
-
resolveSections: () => resolveSections,
|
|
19540
|
-
sectionText: () => sectionText
|
|
19541
|
-
});
|
|
19542
|
-
function resolveSize(size, total) {
|
|
19543
|
-
if (size === void 0) return 0;
|
|
19544
|
-
if (typeof size === "number") return Math.max(0, Math.min(total, size));
|
|
19545
|
-
const m = /^(\d+(?:\.\d+)?)%$/.exec(size);
|
|
19546
|
-
if (!m) return 0;
|
|
19547
|
-
const pct = Number(m[1]);
|
|
19548
|
-
return Math.max(0, Math.min(total, Math.round(total * pct / 100)));
|
|
19549
|
-
}
|
|
19550
|
-
function resolveSections(sectionsObj, lines) {
|
|
19551
|
-
const total = lines.length;
|
|
19552
|
-
const anchored = /* @__PURE__ */ new Map();
|
|
19553
|
-
const sectionEntries = Object.entries(sectionsObj);
|
|
19554
|
-
for (const [id, sec] of sectionEntries) {
|
|
19555
|
-
let from = 0;
|
|
19556
|
-
let to = total;
|
|
19557
|
-
if (sec.anchor !== void 0) {
|
|
19558
|
-
try {
|
|
19559
|
-
const anchorPatterns = Array.isArray(sec.anchor) ? sec.anchor : [sec.anchor];
|
|
19560
|
-
const sharedCtx = Array.isArray(sec.anchor_context) ? null : sec.anchor_context ?? null;
|
|
19561
|
-
const ctxList = Array.isArray(sec.anchor_context) ? sec.anchor_context : anchorPatterns.map(() => sharedCtx);
|
|
19562
|
-
const candidates = anchorPatterns.map((pattern, k) => {
|
|
19563
|
-
const ctx = ctxList[k] ?? null;
|
|
19564
|
-
return {
|
|
19565
|
-
re: new RegExp(pattern, sec.anchor_flags ?? ""),
|
|
19566
|
-
prevRe: ctx?.prev !== void 0 ? new RegExp(ctx.prev, ctx.prev_flags ?? "") : null,
|
|
19567
|
-
nextRe: ctx?.next !== void 0 ? new RegExp(ctx.next, ctx.next_flags ?? "") : null
|
|
19568
|
-
};
|
|
19569
|
-
});
|
|
19570
|
-
const matchesCandidate = (c, i) => c.re.test(lines[i]) && (c.prevRe === null || i > 0 && c.prevRe.test(lines[i - 1])) && (c.nextRe === null || i < total - 1 && c.nextRe.test(lines[i + 1]));
|
|
19571
|
-
let idx = -1;
|
|
19572
|
-
for (const c of candidates) {
|
|
19573
|
-
let candIdx = -1;
|
|
19574
|
-
if (sec.anchor_last) {
|
|
19575
|
-
for (let i = total - 1; i >= 0; i--) {
|
|
19576
|
-
if (matchesCandidate(c, i)) {
|
|
19577
|
-
candIdx = i;
|
|
19578
|
-
break;
|
|
19579
|
-
}
|
|
19580
|
-
}
|
|
19581
|
-
} else {
|
|
19582
|
-
for (let i = 0; i < total; i++) {
|
|
19583
|
-
if (matchesCandidate(c, i)) {
|
|
19584
|
-
candIdx = i;
|
|
19585
|
-
break;
|
|
19586
|
-
}
|
|
19587
|
-
}
|
|
19588
|
-
}
|
|
19589
|
-
if (candIdx !== -1 && (idx === -1 || candIdx < idx)) idx = candIdx;
|
|
19590
|
-
}
|
|
19591
|
-
if (idx !== -1) {
|
|
19592
|
-
from = idx;
|
|
19593
|
-
to = total;
|
|
19594
|
-
if (sec.until_regex !== void 0) {
|
|
19595
|
-
try {
|
|
19596
|
-
const ure = new RegExp(sec.until_regex, sec.until_regex_flags ?? "");
|
|
19597
|
-
const end = lines.findIndex((l, i) => i > idx && ure.test(l));
|
|
19598
|
-
if (end !== -1) to = end;
|
|
19599
|
-
} catch {
|
|
19600
|
-
}
|
|
19601
|
-
} else if (sec.lines !== void 0) {
|
|
19602
|
-
to = Math.min(total, from + sec.lines);
|
|
19603
|
-
}
|
|
19604
|
-
}
|
|
19605
|
-
} catch {
|
|
19606
|
-
}
|
|
19607
|
-
} else if (sec.from_top !== void 0) {
|
|
19608
|
-
from = resolveSize(sec.from_top, total);
|
|
19609
|
-
to = total;
|
|
19610
|
-
} else if (sec.from_bottom !== void 0) {
|
|
19611
|
-
const sz = resolveSize(sec.from_bottom, total);
|
|
19612
|
-
from = total - sz;
|
|
19613
|
-
to = total;
|
|
19614
|
-
}
|
|
19615
|
-
anchored.set(id, { fromLine: from, toLine: to });
|
|
19616
|
-
}
|
|
19617
|
-
const resolved = [];
|
|
19618
|
-
for (const [id, sec] of sectionEntries) {
|
|
19619
|
-
let { fromLine, toLine } = anchored.get(id);
|
|
19620
|
-
if (sec.until !== void 0) {
|
|
19621
|
-
if (sec.until.startsWith("^")) {
|
|
19622
|
-
try {
|
|
19623
|
-
const ure = new RegExp(sec.until);
|
|
19624
|
-
const end = lines.findIndex((l, i) => i > fromLine && ure.test(l));
|
|
19625
|
-
if (end !== -1) toLine = end;
|
|
19626
|
-
} catch {
|
|
19627
|
-
}
|
|
19628
|
-
} else {
|
|
19629
|
-
const target = anchored.get(sec.until);
|
|
19630
|
-
if (target) toLine = target.fromLine;
|
|
19631
|
-
}
|
|
19632
|
-
}
|
|
19633
|
-
if (toLine < fromLine) toLine = fromLine;
|
|
19634
|
-
const text = lines.slice(fromLine, toLine).join("\n");
|
|
19635
|
-
resolved.push({ id, fromLine, toLine, text });
|
|
19636
|
-
}
|
|
19637
|
-
return resolved;
|
|
19638
|
-
}
|
|
19639
|
-
function sectionText(sections, sectionId, fullScreen) {
|
|
19640
|
-
if (!sectionId) return fullScreen;
|
|
19641
|
-
const found = sections.find((s2) => s2.id === sectionId);
|
|
19642
|
-
return found ? found.text : "";
|
|
19643
|
-
}
|
|
19644
|
-
function isRegexCondition(c) {
|
|
19645
|
-
return "matches" in c;
|
|
19646
|
-
}
|
|
19647
|
-
function isChangedCondition(c) {
|
|
19648
|
-
return "cursor_above" in c && "changed" in c;
|
|
19649
|
-
}
|
|
19650
|
-
function isAllCondition(c) {
|
|
19651
|
-
return "all" in c;
|
|
19652
|
-
}
|
|
19653
|
-
function isAnyCondition(c) {
|
|
19654
|
-
return "any" in c;
|
|
19655
|
-
}
|
|
19656
|
-
function evaluateCondition(cond, sections, fullScreen, cursor, prevLines, trace, stateId) {
|
|
19657
|
-
if (isAllCondition(cond)) {
|
|
19658
|
-
for (const child of cond.all) {
|
|
19659
|
-
if (!evaluateCondition(child, sections, fullScreen, cursor, prevLines, trace, stateId)) {
|
|
19660
|
-
return false;
|
|
19661
|
-
}
|
|
19662
|
-
}
|
|
19663
|
-
return true;
|
|
19664
|
-
}
|
|
19665
|
-
if (isAnyCondition(cond)) {
|
|
19666
|
-
for (const child of cond.any) {
|
|
19667
|
-
if (evaluateCondition(child, sections, fullScreen, cursor, prevLines, trace, stateId)) {
|
|
19668
|
-
return true;
|
|
19669
|
-
}
|
|
19670
|
-
}
|
|
19671
|
-
return false;
|
|
19672
|
-
}
|
|
19673
|
-
if (isChangedCondition(cond)) {
|
|
19674
|
-
if (!cursor || !prevLines || prevLines.length === 0) return false;
|
|
19675
|
-
const curLines = fullScreen.split("\n");
|
|
19676
|
-
const startRow = Math.max(0, cursor.row - cond.cursor_above);
|
|
19677
|
-
const endRow = cursor.row;
|
|
19678
|
-
const currentSlice = curLines.slice(startRow, endRow).join("\n");
|
|
19679
|
-
const prevSlice = prevLines.slice(startRow, endRow).join("\n");
|
|
19680
|
-
const didChange = currentSlice !== prevSlice;
|
|
19681
|
-
const result = cond.changed ? didChange : !didChange;
|
|
19682
|
-
const stableSuffix = cond.stable_ms != null ? ` stable_ms=${cond.stable_ms}` : "";
|
|
19683
|
-
trace.push({
|
|
19684
|
-
kind: result ? "state_match" : "state_skip",
|
|
19685
|
-
text: `state[${stateId}] changed cond cursor_above=${cond.cursor_above} rows[${startRow},${endRow}) changed=${didChange} expected=${cond.changed}${stableSuffix} result=${result}`
|
|
19686
|
-
});
|
|
19687
|
-
return result;
|
|
19688
|
-
}
|
|
19689
|
-
if (isRegexCondition(cond)) {
|
|
19690
|
-
const haystack = sectionText(sections, cond.section, fullScreen);
|
|
19691
|
-
let matched = false;
|
|
19692
|
-
try {
|
|
19693
|
-
const re = new RegExp(cond.matches, cond.flags ?? "i");
|
|
19694
|
-
matched = re.test(haystack);
|
|
19695
|
-
} catch {
|
|
19696
|
-
matched = false;
|
|
19697
|
-
}
|
|
19698
|
-
if (!matched) {
|
|
19699
|
-
trace.push({ kind: "state_skip", text: `state[${stateId}] regex cond ${cond.section ?? "*"}~/${cond.matches}/ no match` });
|
|
19700
|
-
return false;
|
|
19701
|
-
}
|
|
19702
|
-
if (cursor !== void 0) {
|
|
19703
|
-
if (cond.cursor_row_min !== void 0 && cursor.row < cond.cursor_row_min) {
|
|
19704
|
-
trace.push({ kind: "state_skip", text: `state[${stateId}] cursor row ${cursor.row} < cursor_row_min ${cond.cursor_row_min}` });
|
|
19705
|
-
return false;
|
|
19706
|
-
}
|
|
19707
|
-
if (cond.cursor_row_max !== void 0 && cursor.row > cond.cursor_row_max) {
|
|
19708
|
-
trace.push({ kind: "state_skip", text: `state[${stateId}] cursor row ${cursor.row} > cursor_row_max ${cond.cursor_row_max}` });
|
|
19709
|
-
return false;
|
|
19710
|
-
}
|
|
19711
|
-
if (cond.cursor_col_min !== void 0 && cursor.col < cond.cursor_col_min) {
|
|
19712
|
-
trace.push({ kind: "state_skip", text: `state[${stateId}] cursor col ${cursor.col} < cursor_col_min ${cond.cursor_col_min}` });
|
|
19713
|
-
return false;
|
|
19714
|
-
}
|
|
19715
|
-
if (cond.cursor_col_max !== void 0 && cursor.col > cond.cursor_col_max) {
|
|
19716
|
-
trace.push({ kind: "state_skip", text: `state[${stateId}] cursor col ${cursor.col} > cursor_col_max ${cond.cursor_col_max}` });
|
|
19717
|
-
return false;
|
|
19718
|
-
}
|
|
19719
|
-
}
|
|
19720
|
-
trace.push({ kind: "state_match", text: `state[${stateId}] regex cond ${cond.section ?? "*"}~/${cond.matches}/ matched${cursor !== void 0 ? ` cursor=(${cursor.row},${cursor.col})` : ""}` });
|
|
19721
|
-
return true;
|
|
19722
|
-
}
|
|
19723
|
-
return false;
|
|
19724
|
-
}
|
|
19725
|
-
function extractTitle(rule, sections, fullScreen) {
|
|
19726
|
-
const hay = sectionText(sections, rule.section, fullScreen);
|
|
19727
|
-
if (!hay) return null;
|
|
19728
|
-
if (rule.first_line) {
|
|
19729
|
-
const lines = hay.split("\n");
|
|
19730
|
-
for (const line of lines) {
|
|
19731
|
-
const stripped = line.trim();
|
|
19732
|
-
if (stripped && !/^[─╌═─\s]+$/.test(stripped)) {
|
|
19733
|
-
return stripped;
|
|
19734
|
-
}
|
|
19735
|
-
}
|
|
19736
|
-
return null;
|
|
19737
|
-
}
|
|
19738
|
-
if (rule.regex) {
|
|
19739
|
-
try {
|
|
19740
|
-
const re = new RegExp(rule.regex, rule.flags ?? "i");
|
|
19741
|
-
const m = re.exec(hay);
|
|
19742
|
-
if (m) return (m[1] ?? m[0]).trim();
|
|
19743
|
-
} catch {
|
|
19744
|
-
}
|
|
19745
|
-
}
|
|
19746
|
-
return null;
|
|
19747
|
-
}
|
|
19748
|
-
function compilePattern(ref) {
|
|
19749
|
-
const flags = ref.flags ?? "gm";
|
|
19750
|
-
return new RegExp(ref.pattern, flags.includes("g") ? flags : flags + "g");
|
|
19751
|
-
}
|
|
19752
|
-
function compileLinePattern(ref) {
|
|
19753
|
-
const flags = (ref.flags ?? "m").replace(/g/g, "");
|
|
19754
|
-
return new RegExp(ref.pattern, flags);
|
|
19755
|
-
}
|
|
19756
|
-
function extractButtonsFromRule(rule, hay) {
|
|
19757
|
-
const keyTemplate = rule.key_for_index;
|
|
19758
|
-
const continuationLines = rule.continuation_lines ?? false;
|
|
19759
|
-
const buttons = [];
|
|
19760
|
-
if (continuationLines) {
|
|
19761
|
-
const re = compileLinePattern(rule);
|
|
19762
|
-
const lines = hay.split("\n");
|
|
19763
|
-
for (let i = 0; i < lines.length; i += 1) {
|
|
19764
|
-
const m = re.exec(lines[i]);
|
|
19765
|
-
if (!m) continue;
|
|
19766
|
-
const idx = Number(m[1]);
|
|
19767
|
-
let label = String(m[2] ?? "").trim();
|
|
19768
|
-
if (!Number.isFinite(idx) || idx <= 0 || !label) continue;
|
|
19769
|
-
const current = hasCursorMarker(lines[i]);
|
|
19770
|
-
let j = i + 1;
|
|
19771
|
-
while (j < lines.length) {
|
|
19772
|
-
const next = lines[j];
|
|
19773
|
-
if (!next.trim()) break;
|
|
19774
|
-
if (re.test(next)) break;
|
|
19775
|
-
if (!/^\s+/.test(next)) break;
|
|
19776
|
-
label += " " + next.trim();
|
|
19777
|
-
j += 1;
|
|
19778
|
-
}
|
|
19779
|
-
const key = keyTemplate.replace(/\{index\}/g, String(idx));
|
|
19780
|
-
buttons.push({ index: idx, label, key, current });
|
|
19781
|
-
i = j - 1;
|
|
19782
|
-
}
|
|
19783
|
-
} else {
|
|
19784
|
-
const re = compilePattern(rule);
|
|
19785
|
-
let m;
|
|
19786
|
-
while ((m = re.exec(hay)) !== null) {
|
|
19787
|
-
const idx = Number(m[1]);
|
|
19788
|
-
const label = String(m[2] ?? "").trim();
|
|
19789
|
-
if (!Number.isFinite(idx) || idx <= 0 || !label) continue;
|
|
19790
|
-
const key = keyTemplate.replace(/\{index\}/g, String(idx));
|
|
19791
|
-
buttons.push({ index: idx, label, key, current: hasCursorMarker(m[0]) });
|
|
19792
|
-
}
|
|
19793
|
-
}
|
|
19794
|
-
const block2 = lastContiguousNumberedBlock(buttons);
|
|
19795
|
-
block2.sort((a, b) => a.index - b.index);
|
|
19796
|
-
return block2;
|
|
19797
|
-
}
|
|
19798
|
-
function lastContiguousNumberedBlock(entries) {
|
|
19799
|
-
if (entries.length <= 1) return entries.slice();
|
|
19800
|
-
let start = entries.length - 1;
|
|
19801
|
-
for (let i = entries.length - 1; i > 0; i -= 1) {
|
|
19802
|
-
if (entries[i - 1].index === entries[i].index - 1) start = i - 1;
|
|
19803
|
-
else break;
|
|
19804
|
-
}
|
|
19805
|
-
return entries.slice(start);
|
|
19806
|
-
}
|
|
19807
|
-
function hasCursorMarker(text) {
|
|
19808
|
-
return /^\s*[❯›>]/.test(text);
|
|
19809
|
-
}
|
|
19810
|
-
var init_evaluator = __esm({
|
|
19811
|
-
"src/providers/spec/evaluator.ts"() {
|
|
19812
|
-
"use strict";
|
|
19813
|
-
}
|
|
19814
|
-
});
|
|
19815
|
-
|
|
19816
|
-
// src/providers/spec/fsm-types.ts
|
|
19817
|
-
function isV4Spec(raw) {
|
|
19818
|
-
return !!raw && typeof raw === "object" && raw.$schema === "adhdev:cli/spec@4";
|
|
19819
|
-
}
|
|
19820
|
-
function initialState(spec) {
|
|
19821
|
-
return spec.states.find((s2) => s2.initial) ?? spec.states[0];
|
|
19822
|
-
}
|
|
19823
|
-
function stateById(spec, id) {
|
|
19824
|
-
return spec.states.find((s2) => s2.id === id);
|
|
19825
|
-
}
|
|
19826
|
-
function outgoingTransitions(spec, stateId) {
|
|
19827
|
-
const matches = spec.transitions.filter((t) => {
|
|
19828
|
-
if (t.from === "*") return true;
|
|
19829
|
-
if (Array.isArray(t.from)) return t.from.includes(stateId);
|
|
19830
|
-
return t.from === stateId;
|
|
19831
|
-
});
|
|
19832
|
-
return matches.map((t, i) => ({ t, i })).sort((a, b) => (b.t.priority ?? 0) - (a.t.priority ?? 0) || a.i - b.i).map((x) => x.t);
|
|
19833
|
-
}
|
|
19834
|
-
function statusForState(state) {
|
|
19835
|
-
if (state.status) return state.status;
|
|
19836
|
-
if (state.modal) return "approval";
|
|
19837
|
-
if (state.id === "busy" || state.id === "generating") return "generating";
|
|
19838
|
-
return "idle";
|
|
19839
|
-
}
|
|
19840
|
-
function modalKindForState(state) {
|
|
19841
|
-
if (state.modal_kind) return state.modal_kind;
|
|
19842
|
-
if (state.modal) return "approval";
|
|
19843
|
-
return null;
|
|
19844
|
-
}
|
|
19845
|
-
var init_fsm_types = __esm({
|
|
19846
|
-
"src/providers/spec/fsm-types.ts"() {
|
|
19847
|
-
"use strict";
|
|
19848
|
-
}
|
|
19849
|
-
});
|
|
19850
|
-
|
|
19851
|
-
// src/providers/spec/fsm-evaluator.ts
|
|
19852
|
-
var fsm_evaluator_exports = {};
|
|
19853
|
-
__export(fsm_evaluator_exports, {
|
|
19854
|
-
evaluateConditionPreview: () => evaluateConditionPreview,
|
|
19855
|
-
evaluateFsm: () => evaluateFsm
|
|
19856
|
-
});
|
|
19857
|
-
function regionKey(cursorAbove) {
|
|
19858
|
-
return cursorAbove && cursorAbove > 0 ? cursorAbove : WHOLE_SCREEN;
|
|
19859
|
-
}
|
|
19860
|
-
function isRegex(c) {
|
|
19861
|
-
return "matches" in c;
|
|
19862
|
-
}
|
|
19863
|
-
function isChanged(c) {
|
|
19864
|
-
return "cursor_above" in c && "changed" in c;
|
|
19865
|
-
}
|
|
19866
|
-
function isElapsed(c) {
|
|
19867
|
-
return "elapsed_ms" in c;
|
|
19868
|
-
}
|
|
19869
|
-
function isStable(c) {
|
|
19870
|
-
return "stable_ms" in c;
|
|
19871
|
-
}
|
|
19872
|
-
function isAll(c) {
|
|
19873
|
-
return "all" in c;
|
|
19874
|
-
}
|
|
19875
|
-
function isAny(c) {
|
|
19876
|
-
return "any" in c;
|
|
19877
|
-
}
|
|
19878
|
-
function isNot(c) {
|
|
19879
|
-
return "not" in c;
|
|
19880
|
-
}
|
|
19881
|
-
function evalCond(cond, sections, fullScreen, cursor, prevLines, clock, legacyTrace, stateId) {
|
|
19882
|
-
if (isAll(cond)) {
|
|
19883
|
-
const children = cond.all.map((c) => evalCond(c, sections, fullScreen, cursor, prevLines, clock, legacyTrace, stateId));
|
|
19884
|
-
const result = children.every((c) => c.result);
|
|
19885
|
-
const remainingMs = result ? 0 : Math.max(0, ...children.filter((c) => !c.result).map((c) => c.remainingMs ?? 0));
|
|
19886
|
-
return { kind: "all", result, detail: `all(${children.length})`, remainingMs, children };
|
|
19887
|
-
}
|
|
19888
|
-
if (isAny(cond)) {
|
|
19889
|
-
const children = cond.any.map((c) => evalCond(c, sections, fullScreen, cursor, prevLines, clock, legacyTrace, stateId));
|
|
19890
|
-
const result = children.some((c) => c.result);
|
|
19891
|
-
const pending = children.filter((c) => !c.result).map((c) => c.remainingMs ?? Infinity);
|
|
19892
|
-
const remainingMs = result ? 0 : pending.length ? Math.min(...pending) : 0;
|
|
19893
|
-
return { kind: "any", result, detail: `any(${children.length})`, remainingMs: Number.isFinite(remainingMs) ? remainingMs : 0, children };
|
|
19894
|
-
}
|
|
19895
|
-
if (isNot(cond)) {
|
|
19896
|
-
const child = evalCond(cond.not, sections, fullScreen, cursor, prevLines, clock, legacyTrace, stateId);
|
|
19897
|
-
return { kind: "not", result: !child.result, detail: `not`, remainingMs: 0, children: [child] };
|
|
19898
|
-
}
|
|
19899
|
-
if (isElapsed(cond)) {
|
|
19900
|
-
const age = clock.now - clock.stateEnteredAt;
|
|
19901
|
-
const result = age >= cond.elapsed_ms;
|
|
19902
|
-
const remainingMs = result ? 0 : cond.elapsed_ms - age;
|
|
19903
|
-
return { kind: "elapsed", result, detail: `elapsed ${age}ms / ${cond.elapsed_ms}ms`, remainingMs };
|
|
19904
|
-
}
|
|
19905
|
-
if (isStable(cond)) {
|
|
19906
|
-
const key = regionKey(cond.cursor_above);
|
|
19907
|
-
const lastChanged = clock.regionLastChangedAt.get(key) ?? clock.stateEnteredAt;
|
|
19908
|
-
const stableFor = clock.now - lastChanged;
|
|
19909
|
-
const result = stableFor >= cond.stable_ms;
|
|
19910
|
-
const remainingMs = result ? 0 : cond.stable_ms - stableFor;
|
|
19911
|
-
const where = key === WHOLE_SCREEN ? "screen" : `cursor_above=${cond.cursor_above}`;
|
|
19912
|
-
return { kind: "stable", result, detail: `stable ${where} ${stableFor}ms / ${cond.stable_ms}ms`, remainingMs };
|
|
19913
|
-
}
|
|
19914
|
-
if (isRegex(cond) || isChanged(cond)) {
|
|
19915
|
-
const result = evaluateCondition(cond, sections, fullScreen, cursor, prevLines, legacyTrace, stateId);
|
|
19916
|
-
const kind = isRegex(cond) ? "regex" : "changed";
|
|
19917
|
-
const detail = isRegex(cond) ? `${cond.section ?? "*"}~/${cond.matches}/` : `cursor_above=${cond.cursor_above} changed=${cond.changed}`;
|
|
19918
|
-
let matchedText;
|
|
19919
|
-
if (result && isRegex(cond)) {
|
|
19920
|
-
try {
|
|
19921
|
-
const hay = sectionText(sections, cond.section, fullScreen);
|
|
19922
|
-
const re = new RegExp(cond.matches, cond.flags ?? "i");
|
|
19923
|
-
const m = re.exec(hay);
|
|
19924
|
-
if (m && m[0]) matchedText = m[0].replace(/\s+/g, " ").trim().slice(0, 160);
|
|
19925
|
-
} catch {
|
|
19926
|
-
}
|
|
19927
|
-
}
|
|
19928
|
-
return matchedText ? { kind, result, detail, matchedText } : { kind, result, detail };
|
|
19929
|
-
}
|
|
19930
|
-
return { kind: "all", result: false, detail: "unknown condition" };
|
|
19931
|
-
}
|
|
19932
|
-
function fromLabel(t) {
|
|
19933
|
-
const from = Array.isArray(t.from) ? t.from.join("|") : t.from;
|
|
19934
|
-
return t.label ?? `${from}\u2192${t.to}`;
|
|
19935
|
-
}
|
|
19936
|
-
function evaluateFsm(spec, currentStateId, screenText, cursor, prevLines, clock) {
|
|
19937
|
-
const legacyTrace = [];
|
|
19938
|
-
const lines = screenText.split("\n").map((l) => l.endsWith("\r") ? l.slice(0, -1) : l);
|
|
19939
|
-
const cleanScreen = lines.join("\n");
|
|
19940
|
-
const sections = resolveSections(spec.sections ?? {}, lines);
|
|
19941
|
-
const outgoing = outgoingTransitions(spec, currentStateId);
|
|
19942
|
-
const transitions = [];
|
|
19943
|
-
let fired = null;
|
|
19944
|
-
for (const t of outgoing) {
|
|
19945
|
-
const holdMs = t.min_hold_ms ?? 0;
|
|
19946
|
-
const heldFor = clock.now - clock.stateEnteredAt;
|
|
19947
|
-
const holdSatisfied = heldFor >= holdMs;
|
|
19948
|
-
const holdRemainingMs = holdSatisfied ? 0 : holdMs - heldFor;
|
|
19949
|
-
let cond;
|
|
19950
|
-
let condResult = true;
|
|
19951
|
-
if (t.when) {
|
|
19952
|
-
cond = evalCond(t.when, sections, cleanScreen, cursor, prevLines, clock, legacyTrace, `${currentStateId}\u2192${t.to}`);
|
|
19953
|
-
condResult = cond.result;
|
|
19954
|
-
}
|
|
19955
|
-
const fires = holdSatisfied && condResult;
|
|
19956
|
-
const te = {
|
|
19957
|
-
to: t.to,
|
|
19958
|
-
label: fromLabel(t),
|
|
19959
|
-
eligible: true,
|
|
19960
|
-
holdSatisfied,
|
|
19961
|
-
holdRemainingMs,
|
|
19962
|
-
condResult,
|
|
19963
|
-
cond,
|
|
19964
|
-
fires,
|
|
19965
|
-
priority: t.priority ?? 0
|
|
19966
|
-
};
|
|
19967
|
-
transitions.push(te);
|
|
19968
|
-
if (fires && !fired) fired = te;
|
|
19969
|
-
}
|
|
19970
|
-
if (fired && !stateById(spec, fired.to)) {
|
|
19971
|
-
legacyTrace.push({ kind: "state_skip", text: `transition target ${fired.to} not found` });
|
|
19972
|
-
fired = null;
|
|
19973
|
-
}
|
|
19974
|
-
return { sections, transitions, fired, trace: legacyTrace };
|
|
19975
|
-
}
|
|
19976
|
-
function evaluateConditionPreview(cond, sections, screenText, cursor) {
|
|
19977
|
-
const lines = screenText.split("\n").map((l) => l.endsWith("\r") ? l.slice(0, -1) : l);
|
|
19978
|
-
const cleanScreen = lines.join("\n");
|
|
19979
|
-
const resolved = resolveSections(sections ?? {}, lines);
|
|
19980
|
-
const clock = { now: 0, stateEnteredAt: 0, regionLastChangedAt: /* @__PURE__ */ new Map() };
|
|
19981
|
-
return evalCond(cond, resolved, cleanScreen, cursor, void 0, clock, [], "preview");
|
|
19982
|
-
}
|
|
19983
|
-
var WHOLE_SCREEN;
|
|
19984
|
-
var init_fsm_evaluator = __esm({
|
|
19985
|
-
"src/providers/spec/fsm-evaluator.ts"() {
|
|
19986
|
-
"use strict";
|
|
19987
|
-
init_evaluator();
|
|
19988
|
-
init_fsm_types();
|
|
19989
|
-
WHOLE_SCREEN = -1;
|
|
19990
|
-
}
|
|
19991
|
-
});
|
|
19992
|
-
|
|
19993
|
-
// src/providers/spec/fsm-loader.ts
|
|
19994
|
-
var fsm_loader_exports = {};
|
|
19995
|
-
__export(fsm_loader_exports, {
|
|
19996
|
-
loadFsmSpec: () => loadFsmSpec,
|
|
19997
|
-
validateFsmSpec: () => validateFsmSpec
|
|
19998
|
-
});
|
|
19999
|
-
import * as fs10 from "fs";
|
|
20000
|
-
function loadFsmSpec(sourcePath) {
|
|
20001
|
-
let raw;
|
|
20002
|
-
try {
|
|
20003
|
-
raw = JSON.parse(fs10.readFileSync(sourcePath, "utf8"));
|
|
20004
|
-
} catch (err) {
|
|
20005
|
-
return { ok: false, errors: [`Failed to read/parse spec: ${err.message}`], sourcePath };
|
|
20006
|
-
}
|
|
20007
|
-
const errors = validateFsmSpec(raw);
|
|
20008
|
-
if (errors.length) return { ok: false, errors, sourcePath };
|
|
20009
|
-
return { ok: true, spec: raw, sourcePath };
|
|
20010
|
-
}
|
|
20011
|
-
function validateFsmSpec(raw) {
|
|
20012
|
-
const errs = [];
|
|
20013
|
-
if (!isV4Spec(raw)) return ['$schema must be "adhdev:cli/spec@4"'];
|
|
20014
|
-
const spec = raw;
|
|
20015
|
-
if (!spec.id) errs.push("id is required");
|
|
20016
|
-
if (!spec.binary) errs.push("binary is required");
|
|
20017
|
-
if (!spec.send_message?.submit_key) errs.push("send_message.submit_key is required");
|
|
20018
|
-
if (spec.pre_launch_trust !== void 0) {
|
|
20019
|
-
const t = spec.pre_launch_trust;
|
|
20020
|
-
if (!t || typeof t !== "object" || Array.isArray(t)) {
|
|
20021
|
-
errs.push("pre_launch_trust must be an object");
|
|
20022
|
-
} else {
|
|
20023
|
-
if (typeof t.settings_path !== "string" || !t.settings_path) errs.push("pre_launch_trust.settings_path is required");
|
|
20024
|
-
if (typeof t.key !== "string" || !t.key) errs.push("pre_launch_trust.key is required");
|
|
20025
|
-
}
|
|
20026
|
-
}
|
|
20027
|
-
if (spec.refocus_when_stalled_ms !== void 0) {
|
|
20028
|
-
if (typeof spec.refocus_when_stalled_ms !== "number" || !(spec.refocus_when_stalled_ms > 0)) {
|
|
20029
|
-
errs.push("refocus_when_stalled_ms must be a positive number");
|
|
20030
|
-
} else if (!Array.isArray(spec.send_on_spawn) || spec.send_on_spawn.length === 0) {
|
|
20031
|
-
errs.push("refocus_when_stalled_ms requires send_on_spawn (the wake sequence to re-inject)");
|
|
20032
|
-
}
|
|
20033
|
-
}
|
|
20034
|
-
if (!Array.isArray(spec.states) || spec.states.length === 0) {
|
|
20035
|
-
errs.push("states[] must be a non-empty array");
|
|
20036
|
-
return errs;
|
|
20037
|
-
}
|
|
20038
|
-
if (!Array.isArray(spec.transitions)) {
|
|
20039
|
-
errs.push("transitions[] must be an array");
|
|
20040
|
-
return errs;
|
|
20041
|
-
}
|
|
20042
|
-
const ids = /* @__PURE__ */ new Set();
|
|
20043
|
-
let initialCount = 0;
|
|
20044
|
-
for (const [i, s2] of spec.states.entries()) {
|
|
20045
|
-
if (!s2.id) {
|
|
20046
|
-
errs.push(`states[${i}].id is required`);
|
|
20047
|
-
continue;
|
|
20048
|
-
}
|
|
20049
|
-
if (ids.has(s2.id)) errs.push(`states[${i}].id "${s2.id}" is duplicated`);
|
|
20050
|
-
ids.add(s2.id);
|
|
20051
|
-
if (!s2.label) errs.push(`states[${i}].label is required`);
|
|
20052
|
-
if (s2.initial) initialCount += 1;
|
|
20053
|
-
if (s2.status && !["idle", "generating", "approval"].includes(s2.status)) {
|
|
20054
|
-
errs.push(`states[${i}].status "${s2.status}" must be idle|generating|approval`);
|
|
20055
|
-
}
|
|
20056
|
-
}
|
|
20057
|
-
if (initialCount === 0) errs.push("exactly one state must have initial:true (none found)");
|
|
20058
|
-
if (initialCount > 1) errs.push(`exactly one state must have initial:true (${initialCount} found)`);
|
|
20059
|
-
const sectionIds = new Set(Object.keys(spec.sections ?? {}));
|
|
20060
|
-
for (const [i, t] of spec.transitions.entries()) {
|
|
20061
|
-
const froms = t.from === "*" ? [] : Array.isArray(t.from) ? t.from : [t.from];
|
|
20062
|
-
for (const f of froms) {
|
|
20063
|
-
if (!ids.has(f)) errs.push(`transitions[${i}].from references unknown state "${f}"`);
|
|
20064
|
-
}
|
|
20065
|
-
if (t.from !== "*" && froms.length === 0) errs.push(`transitions[${i}].from is required`);
|
|
20066
|
-
if (!t.to) errs.push(`transitions[${i}].to is required`);
|
|
20067
|
-
else if (!ids.has(t.to)) errs.push(`transitions[${i}].to references unknown state "${t.to}"`);
|
|
20068
|
-
if (t.when) errs.push(...validateCondition(t.when, sectionIds, `transitions[${i}].when`));
|
|
20069
|
-
}
|
|
20070
|
-
for (const [i, s2] of spec.states.entries()) {
|
|
20071
|
-
const sec = s2.extract?.title?.section;
|
|
20072
|
-
if (sec && !sectionIds.has(sec)) errs.push(`states[${i}].extract.title.section "${sec}" unknown`);
|
|
20073
|
-
const bsec = s2.extract?.buttons?.section;
|
|
20074
|
-
if (bsec && !sectionIds.has(bsec)) errs.push(`states[${i}].extract.buttons.section "${bsec}" unknown`);
|
|
20075
|
-
}
|
|
20076
|
-
return errs;
|
|
20077
|
-
}
|
|
20078
|
-
function validateCondition(c, sectionIds, path42) {
|
|
20079
|
-
const errs = [];
|
|
20080
|
-
const w = c;
|
|
20081
|
-
if ("all" in w) {
|
|
20082
|
-
w.all.forEach((x, i) => errs.push(...validateCondition(x, sectionIds, `${path42}.all[${i}]`)));
|
|
20083
|
-
return errs;
|
|
20084
|
-
}
|
|
20085
|
-
if ("any" in w) {
|
|
20086
|
-
w.any.forEach((x, i) => errs.push(...validateCondition(x, sectionIds, `${path42}.any[${i}]`)));
|
|
20087
|
-
return errs;
|
|
20088
|
-
}
|
|
20089
|
-
if ("not" in w) {
|
|
20090
|
-
errs.push(...validateCondition(w.not, sectionIds, `${path42}.not`));
|
|
20091
|
-
return errs;
|
|
20092
|
-
}
|
|
20093
|
-
if ("matches" in w) {
|
|
20094
|
-
if (w.section && !sectionIds.has(w.section)) errs.push(`${path42}.section "${w.section}" unknown`);
|
|
20095
|
-
try {
|
|
20096
|
-
new RegExp(w.matches, w.flags ?? "i");
|
|
20097
|
-
} catch (e) {
|
|
20098
|
-
errs.push(`${path42}.matches invalid regex: ${e.message}`);
|
|
20099
|
-
}
|
|
20100
|
-
return errs;
|
|
20101
|
-
}
|
|
20102
|
-
if ("cursor_above" in w && "changed" in w) return errs;
|
|
20103
|
-
if ("elapsed_ms" in w) {
|
|
20104
|
-
if (typeof w.elapsed_ms !== "number") errs.push(`${path42}.elapsed_ms must be a number`);
|
|
20105
|
-
return errs;
|
|
20106
|
-
}
|
|
20107
|
-
if ("stable_ms" in w) {
|
|
20108
|
-
if (typeof w.stable_ms !== "number") errs.push(`${path42}.stable_ms must be a number`);
|
|
20109
|
-
return errs;
|
|
20110
|
-
}
|
|
20111
|
-
errs.push(`${path42} is not a recognized condition`);
|
|
20112
|
-
return errs;
|
|
20113
|
-
}
|
|
20114
|
-
var init_fsm_loader = __esm({
|
|
20115
|
-
"src/providers/spec/fsm-loader.ts"() {
|
|
20116
|
-
"use strict";
|
|
20117
|
-
init_fsm_types();
|
|
20118
|
-
}
|
|
20119
|
-
});
|
|
20120
|
-
|
|
20121
20121
|
// src/providers/sdk/v1/sandbox/require-whitelist.ts
|
|
20122
20122
|
var require_whitelist_exports = {};
|
|
20123
20123
|
__export(require_whitelist_exports, {
|
|
@@ -32685,6 +32685,503 @@ ${formatManifestValidationIssues2(validation.issues)}`,
|
|
|
32685
32685
|
}
|
|
32686
32686
|
};
|
|
32687
32687
|
|
|
32688
|
+
// src/commands/low-family/session-host.ts
|
|
32689
|
+
function toHostedCliRuntimeDescriptor(record) {
|
|
32690
|
+
if (!record || typeof record !== "object") return null;
|
|
32691
|
+
const runtimeId = typeof record.sessionId === "string" ? record.sessionId : "";
|
|
32692
|
+
const cliType = typeof record.providerType === "string" ? record.providerType : "";
|
|
32693
|
+
const workspace = typeof record.workspace === "string" ? record.workspace : "";
|
|
32694
|
+
if (!runtimeId || !cliType || !workspace) return null;
|
|
32695
|
+
return {
|
|
32696
|
+
runtimeId,
|
|
32697
|
+
runtimeKey: typeof record.runtimeKey === "string" ? record.runtimeKey : void 0,
|
|
32698
|
+
displayName: typeof record.displayName === "string" ? record.displayName : void 0,
|
|
32699
|
+
workspaceLabel: typeof record.workspaceLabel === "string" ? record.workspaceLabel : void 0,
|
|
32700
|
+
lifecycle: typeof record.lifecycle === "string" ? record.lifecycle : void 0,
|
|
32701
|
+
recoveryState: typeof record.meta?.runtimeRecoveryState === "string" ? String(record.meta.runtimeRecoveryState) : null,
|
|
32702
|
+
cliType,
|
|
32703
|
+
workspace,
|
|
32704
|
+
cliArgs: Array.isArray(record.meta?.cliArgs) ? record.meta.cliArgs : [],
|
|
32705
|
+
providerSessionId: typeof record.meta?.providerSessionId === "string" ? String(record.meta.providerSessionId) : void 0
|
|
32706
|
+
};
|
|
32707
|
+
}
|
|
32708
|
+
function getWriteConflictOwnerClientId(error) {
|
|
32709
|
+
const message = typeof error === "string" ? error : error instanceof Error ? error.message : "";
|
|
32710
|
+
const match = /^Write owned by\s+(.+)$/.exec(message.trim());
|
|
32711
|
+
return match?.[1]?.trim() || void 0;
|
|
32712
|
+
}
|
|
32713
|
+
function summarizeSessionHostRecord(result) {
|
|
32714
|
+
if (!result || typeof result !== "object") return {};
|
|
32715
|
+
const record = result;
|
|
32716
|
+
return {
|
|
32717
|
+
runtimeKey: typeof record.runtimeKey === "string" ? record.runtimeKey : void 0,
|
|
32718
|
+
lifecycle: typeof record.lifecycle === "string" ? record.lifecycle : void 0,
|
|
32719
|
+
surfaceKind: getSessionHostSurfaceKind(record),
|
|
32720
|
+
attachedClientCount: Array.isArray(record.attachedClients) ? record.attachedClients.length : void 0,
|
|
32721
|
+
hasWriteOwner: !!record.writeOwner,
|
|
32722
|
+
writeOwnerClientId: typeof record.writeOwner?.clientId === "string" ? record.writeOwner.clientId : void 0
|
|
32723
|
+
};
|
|
32724
|
+
}
|
|
32725
|
+
function summarizeSessionHostRecords(result) {
|
|
32726
|
+
const records = Array.isArray(result) ? result : [];
|
|
32727
|
+
const groups = partitionSessionHostRecords(records);
|
|
32728
|
+
return {
|
|
32729
|
+
sessionCount: records.length,
|
|
32730
|
+
liveRuntimeCount: groups.liveRuntimes.length,
|
|
32731
|
+
recoverySnapshotCount: groups.recoverySnapshots.length,
|
|
32732
|
+
inactiveRecordCount: groups.inactiveRecords.length
|
|
32733
|
+
};
|
|
32734
|
+
}
|
|
32735
|
+
function summarizeSessionHostDiagnostics(result) {
|
|
32736
|
+
const diagnostics = result && typeof result === "object" ? result : {};
|
|
32737
|
+
const sessions = Array.isArray(diagnostics.sessions) ? diagnostics.sessions : [];
|
|
32738
|
+
return {
|
|
32739
|
+
runtimeCount: typeof diagnostics.runtimeCount === "number" ? diagnostics.runtimeCount : void 0,
|
|
32740
|
+
...summarizeSessionHostRecords(sessions)
|
|
32741
|
+
};
|
|
32742
|
+
}
|
|
32743
|
+
function summarizeSessionHostPruneResult(result) {
|
|
32744
|
+
const value = result && typeof result === "object" ? result : {};
|
|
32745
|
+
return {
|
|
32746
|
+
duplicateGroupCount: typeof value.duplicateGroupCount === "number" ? value.duplicateGroupCount : void 0,
|
|
32747
|
+
prunedCount: Array.isArray(value.prunedSessionIds) ? value.prunedSessionIds.length : void 0,
|
|
32748
|
+
keptCount: Array.isArray(value.keptSessionIds) ? value.keptSessionIds.length : void 0
|
|
32749
|
+
};
|
|
32750
|
+
}
|
|
32751
|
+
async function traceSessionHostAction(action, args, run, summarizeResult2) {
|
|
32752
|
+
const interactionId = typeof args?._interactionId === "string" ? args._interactionId : void 0;
|
|
32753
|
+
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : void 0;
|
|
32754
|
+
const requestedPayload = { action };
|
|
32755
|
+
if (sessionId) requestedPayload.sessionId = sessionId;
|
|
32756
|
+
if (typeof args?.clientId === "string") requestedPayload.clientId = args.clientId;
|
|
32757
|
+
if (typeof args?.signal === "string") requestedPayload.signal = args.signal;
|
|
32758
|
+
if (typeof args?.providerType === "string") requestedPayload.providerType = args.providerType;
|
|
32759
|
+
if (typeof args?.workspace === "string") requestedPayload.workspace = args.workspace;
|
|
32760
|
+
if (typeof args?.dryRun === "boolean") requestedPayload.dryRun = args.dryRun;
|
|
32761
|
+
recordDebugTrace({
|
|
32762
|
+
interactionId,
|
|
32763
|
+
category: "session_host",
|
|
32764
|
+
stage: "action_requested",
|
|
32765
|
+
level: "info",
|
|
32766
|
+
sessionId,
|
|
32767
|
+
payload: requestedPayload
|
|
32768
|
+
});
|
|
32769
|
+
try {
|
|
32770
|
+
const result = await run();
|
|
32771
|
+
recordDebugTrace({
|
|
32772
|
+
interactionId,
|
|
32773
|
+
category: "session_host",
|
|
32774
|
+
stage: "action_result",
|
|
32775
|
+
level: "info",
|
|
32776
|
+
sessionId,
|
|
32777
|
+
payload: {
|
|
32778
|
+
...requestedPayload,
|
|
32779
|
+
success: true,
|
|
32780
|
+
...summarizeResult2 ? summarizeResult2(result) : {}
|
|
32781
|
+
}
|
|
32782
|
+
});
|
|
32783
|
+
return result;
|
|
32784
|
+
} catch (error) {
|
|
32785
|
+
recordDebugTrace({
|
|
32786
|
+
interactionId,
|
|
32787
|
+
category: "session_host",
|
|
32788
|
+
stage: "action_failed",
|
|
32789
|
+
level: "error",
|
|
32790
|
+
sessionId,
|
|
32791
|
+
payload: {
|
|
32792
|
+
...requestedPayload,
|
|
32793
|
+
error: error?.message || String(error),
|
|
32794
|
+
failureKind: getWriteConflictOwnerClientId(error) ? "write_conflict" : "request_failed",
|
|
32795
|
+
conflictOwnerClientId: getWriteConflictOwnerClientId(error)
|
|
32796
|
+
}
|
|
32797
|
+
});
|
|
32798
|
+
throw error;
|
|
32799
|
+
}
|
|
32800
|
+
}
|
|
32801
|
+
var sessionHostHandlers = {
|
|
32802
|
+
session_host_get_diagnostics: async (ctx, args) => {
|
|
32803
|
+
if (!ctx.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
32804
|
+
const diagnostics = await traceSessionHostAction("session_host_get_diagnostics", args, () => ctx.deps.sessionHostControl.getDiagnostics({
|
|
32805
|
+
includeSessions: args?.includeSessions !== false,
|
|
32806
|
+
limit: Number(args?.limit) || void 0
|
|
32807
|
+
}), (result) => ({
|
|
32808
|
+
includeSessions: args?.includeSessions !== false,
|
|
32809
|
+
limit: Number(args?.limit) || void 0,
|
|
32810
|
+
...summarizeSessionHostDiagnostics(result)
|
|
32811
|
+
}));
|
|
32812
|
+
return { success: true, diagnostics };
|
|
32813
|
+
},
|
|
32814
|
+
session_host_list_sessions: async (ctx, args) => {
|
|
32815
|
+
if (!ctx.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
32816
|
+
const sessions = await traceSessionHostAction("session_host_list_sessions", args, () => ctx.deps.sessionHostControl.listSessions(), (records) => summarizeSessionHostRecords(records));
|
|
32817
|
+
return { success: true, sessions };
|
|
32818
|
+
},
|
|
32819
|
+
session_host_stop_session: async (ctx, args) => {
|
|
32820
|
+
if (!ctx.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
32821
|
+
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
32822
|
+
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
32823
|
+
const record = await traceSessionHostAction("session_host_stop_session", args, () => ctx.deps.sessionHostControl.stopSession(sessionId), (result) => summarizeSessionHostRecord(result));
|
|
32824
|
+
return { success: true, record };
|
|
32825
|
+
},
|
|
32826
|
+
session_host_resume_session: async (ctx, args) => {
|
|
32827
|
+
if (!ctx.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
32828
|
+
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
32829
|
+
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
32830
|
+
const record = await traceSessionHostAction("session_host_resume_session", args, async () => {
|
|
32831
|
+
const nextRecord = await ctx.deps.sessionHostControl.resumeSession(sessionId);
|
|
32832
|
+
const hosted = toHostedCliRuntimeDescriptor(nextRecord);
|
|
32833
|
+
if (hosted) {
|
|
32834
|
+
await ctx.deps.cliManager.restoreHostedSessions([hosted]);
|
|
32835
|
+
}
|
|
32836
|
+
return nextRecord;
|
|
32837
|
+
}, (result) => ({
|
|
32838
|
+
...summarizeSessionHostRecord(result),
|
|
32839
|
+
restoredHostedSession: !!toHostedCliRuntimeDescriptor(result)
|
|
32840
|
+
}));
|
|
32841
|
+
return { success: true, record };
|
|
32842
|
+
},
|
|
32843
|
+
session_host_restart_session: async (ctx, args) => {
|
|
32844
|
+
if (!ctx.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
32845
|
+
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
32846
|
+
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
32847
|
+
const record = await traceSessionHostAction("session_host_restart_session", args, async () => {
|
|
32848
|
+
const nextRecord = await ctx.deps.sessionHostControl.restartSession(sessionId);
|
|
32849
|
+
const hosted = toHostedCliRuntimeDescriptor(nextRecord);
|
|
32850
|
+
if (hosted) {
|
|
32851
|
+
await ctx.deps.cliManager.restoreHostedSessions([hosted]);
|
|
32852
|
+
}
|
|
32853
|
+
return nextRecord;
|
|
32854
|
+
}, (result) => ({
|
|
32855
|
+
...summarizeSessionHostRecord(result),
|
|
32856
|
+
restoredHostedSession: !!toHostedCliRuntimeDescriptor(result)
|
|
32857
|
+
}));
|
|
32858
|
+
return { success: true, record };
|
|
32859
|
+
},
|
|
32860
|
+
session_host_send_signal: async (ctx, args) => {
|
|
32861
|
+
if (!ctx.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
32862
|
+
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
32863
|
+
const signal = typeof args?.signal === "string" ? args.signal : "";
|
|
32864
|
+
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
32865
|
+
if (!signal) return { success: false, error: "signal required" };
|
|
32866
|
+
const record = await traceSessionHostAction("session_host_send_signal", args, () => ctx.deps.sessionHostControl.sendSignal(sessionId, signal), (result) => summarizeSessionHostRecord(result));
|
|
32867
|
+
return { success: true, record };
|
|
32868
|
+
},
|
|
32869
|
+
session_host_force_detach_client: async (ctx, args) => {
|
|
32870
|
+
if (!ctx.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
32871
|
+
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
32872
|
+
const clientId = typeof args?.clientId === "string" ? args.clientId : "";
|
|
32873
|
+
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
32874
|
+
if (!clientId) return { success: false, error: "clientId required" };
|
|
32875
|
+
const record = await traceSessionHostAction("session_host_force_detach_client", args, () => ctx.deps.sessionHostControl.forceDetachClient(sessionId, clientId), (result) => summarizeSessionHostRecord(result));
|
|
32876
|
+
return { success: true, record };
|
|
32877
|
+
},
|
|
32878
|
+
session_host_prune_duplicate_sessions: async (ctx, args) => {
|
|
32879
|
+
if (!ctx.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
32880
|
+
const result = await traceSessionHostAction("session_host_prune_duplicate_sessions", args, () => ctx.deps.sessionHostControl.pruneDuplicateSessions({
|
|
32881
|
+
providerType: typeof args?.providerType === "string" ? args.providerType : void 0,
|
|
32882
|
+
workspace: typeof args?.workspace === "string" ? args.workspace : void 0,
|
|
32883
|
+
dryRun: args?.dryRun === true
|
|
32884
|
+
}), (value) => summarizeSessionHostPruneResult(value));
|
|
32885
|
+
return { success: true, result };
|
|
32886
|
+
},
|
|
32887
|
+
session_host_acquire_write: async (ctx, args) => {
|
|
32888
|
+
if (!ctx.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
32889
|
+
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
32890
|
+
const clientId = typeof args?.clientId === "string" ? args.clientId : "";
|
|
32891
|
+
const ownerType = args?.ownerType === "agent" ? "agent" : "user";
|
|
32892
|
+
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
32893
|
+
if (!clientId) return { success: false, error: "clientId required" };
|
|
32894
|
+
const record = await traceSessionHostAction("session_host_acquire_write", args, () => ctx.deps.sessionHostControl.acquireWrite({
|
|
32895
|
+
sessionId,
|
|
32896
|
+
clientId,
|
|
32897
|
+
ownerType,
|
|
32898
|
+
force: args?.force !== false
|
|
32899
|
+
}), (result) => ({
|
|
32900
|
+
...summarizeSessionHostRecord(result),
|
|
32901
|
+
ownerType
|
|
32902
|
+
}));
|
|
32903
|
+
return { success: true, record };
|
|
32904
|
+
},
|
|
32905
|
+
session_host_release_write: async (ctx, args) => {
|
|
32906
|
+
if (!ctx.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
32907
|
+
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
32908
|
+
const clientId = typeof args?.clientId === "string" ? args.clientId : "";
|
|
32909
|
+
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
32910
|
+
if (!clientId) return { success: false, error: "clientId required" };
|
|
32911
|
+
const record = await traceSessionHostAction("session_host_release_write", args, () => ctx.deps.sessionHostControl.releaseWrite({
|
|
32912
|
+
sessionId,
|
|
32913
|
+
clientId
|
|
32914
|
+
}), (result) => summarizeSessionHostRecord(result));
|
|
32915
|
+
return { success: true, record };
|
|
32916
|
+
}
|
|
32917
|
+
};
|
|
32918
|
+
|
|
32919
|
+
// src/commands/low-family/spec-providerdev.ts
|
|
32920
|
+
function resolveSpecPathInProviders(specPath, fsm, pathm, osm) {
|
|
32921
|
+
let rootReal;
|
|
32922
|
+
try {
|
|
32923
|
+
rootReal = fsm.realpathSync(pathm.join(osm.homedir(), ".adhdev", "providers"));
|
|
32924
|
+
} catch (e) {
|
|
32925
|
+
return { ok: false, error: `providers root unavailable: ${e.message}` };
|
|
32926
|
+
}
|
|
32927
|
+
const resolved = pathm.resolve(specPath);
|
|
32928
|
+
const base = pathm.basename(resolved);
|
|
32929
|
+
if (!/^[\w.-]+\.json$/.test(base)) {
|
|
32930
|
+
return { ok: false, error: "refused: spec file must be a *.json basename" };
|
|
32931
|
+
}
|
|
32932
|
+
let parentReal;
|
|
32933
|
+
try {
|
|
32934
|
+
parentReal = fsm.realpathSync(pathm.dirname(resolved));
|
|
32935
|
+
} catch (e) {
|
|
32936
|
+
return { ok: false, error: `spec directory not found: ${e.message}` };
|
|
32937
|
+
}
|
|
32938
|
+
if (parentReal !== rootReal && !parentReal.startsWith(rootReal + pathm.sep)) {
|
|
32939
|
+
return { ok: false, error: "refused: spec path must be under the providers root" };
|
|
32940
|
+
}
|
|
32941
|
+
const safe = pathm.join(parentReal, base);
|
|
32942
|
+
try {
|
|
32943
|
+
const st = fsm.lstatSync(safe);
|
|
32944
|
+
if (st.isSymbolicLink()) return { ok: false, error: "refused: spec path is a symlink" };
|
|
32945
|
+
} catch {
|
|
32946
|
+
}
|
|
32947
|
+
return { ok: true, path: safe };
|
|
32948
|
+
}
|
|
32949
|
+
var specProviderDevHandlers = {
|
|
32950
|
+
get_spec_debug: async (ctx, args) => {
|
|
32951
|
+
const sessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : typeof args?.sessionId === "string" ? args.sessionId.trim() : "";
|
|
32952
|
+
if (!sessionId) return { success: false, error: "targetSessionId required" };
|
|
32953
|
+
const target = ctx.deps.sessionRegistry.get(sessionId);
|
|
32954
|
+
if (!target) return { success: false, error: "Session not found", sessionId };
|
|
32955
|
+
const adapterObj = ctx.deps.cliManager.findAdapter(target.providerType, { instanceKey: sessionId })?.adapter;
|
|
32956
|
+
const snapshot = adapterObj ? typeof adapterObj.getDebugSnapshot === "function" ? adapterObj.getDebugSnapshot() : typeof adapterObj.getDebugState === "function" ? adapterObj.getDebugState() : null : null;
|
|
32957
|
+
return {
|
|
32958
|
+
success: true,
|
|
32959
|
+
sessionId,
|
|
32960
|
+
providerType: target.providerType,
|
|
32961
|
+
isSpecProvider: snapshot !== null,
|
|
32962
|
+
snapshot
|
|
32963
|
+
};
|
|
32964
|
+
},
|
|
32965
|
+
// ── Spec source read/write for the debug panel's live editor.
|
|
32966
|
+
// Lets the dashboard load a session's spec.json, edit it, and save it back —
|
|
32967
|
+
// the driver's fs.watch picks up the change and hot-reloads the FSM with no
|
|
32968
|
+
// restart. Writes are confined to files under ~/.adhdev/providers.
|
|
32969
|
+
get_spec_source: async (ctx, args) => {
|
|
32970
|
+
const fsm = await import("fs");
|
|
32971
|
+
const pathm = await import("path");
|
|
32972
|
+
const osm = await import("os");
|
|
32973
|
+
const sessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : typeof args?.sessionId === "string" ? args.sessionId.trim() : "";
|
|
32974
|
+
let specPath = typeof args?.specPath === "string" ? args.specPath : "";
|
|
32975
|
+
if (!specPath && sessionId) {
|
|
32976
|
+
const target = ctx.deps.sessionRegistry.get(sessionId);
|
|
32977
|
+
const adapterObj = target ? ctx.deps.cliManager.findAdapter(target.providerType, { instanceKey: sessionId })?.adapter : null;
|
|
32978
|
+
const snap = adapterObj && typeof adapterObj.getDebugSnapshot === "function" ? adapterObj.getDebugSnapshot() : null;
|
|
32979
|
+
specPath = snap?.specPath ?? "";
|
|
32980
|
+
}
|
|
32981
|
+
if (!specPath) return { success: false, error: "specPath or resolvable targetSessionId required" };
|
|
32982
|
+
const safe = resolveSpecPathInProviders(specPath, fsm, pathm, osm);
|
|
32983
|
+
if (!safe.ok) return { success: false, error: safe.error, specPath };
|
|
32984
|
+
try {
|
|
32985
|
+
const content = fsm.readFileSync(safe.path, "utf8");
|
|
32986
|
+
return { success: true, specPath: safe.path, content };
|
|
32987
|
+
} catch (e) {
|
|
32988
|
+
return { success: false, error: `read failed: ${e.message}`, specPath };
|
|
32989
|
+
}
|
|
32990
|
+
},
|
|
32991
|
+
write_spec_source: async (_ctx, args) => {
|
|
32992
|
+
const fsm = await import("fs");
|
|
32993
|
+
const pathm = await import("path");
|
|
32994
|
+
const osm = await import("os");
|
|
32995
|
+
const specPath = typeof args?.specPath === "string" ? args.specPath : "";
|
|
32996
|
+
const content = typeof args?.content === "string" ? args.content : "";
|
|
32997
|
+
if (!specPath) return { success: false, error: "specPath required" };
|
|
32998
|
+
if (!content) return { success: false, error: "content required" };
|
|
32999
|
+
const safe = resolveSpecPathInProviders(specPath, fsm, pathm, osm);
|
|
33000
|
+
if (!safe.ok) return { success: false, error: safe.error };
|
|
33001
|
+
let parsed;
|
|
33002
|
+
try {
|
|
33003
|
+
parsed = JSON.parse(content);
|
|
33004
|
+
} catch (e) {
|
|
33005
|
+
return { success: false, error: `invalid JSON: ${e.message}` };
|
|
33006
|
+
}
|
|
33007
|
+
if (parsed?.$schema === "adhdev:cli/spec@4") {
|
|
33008
|
+
const { validateFsmSpec: validateFsmSpec2 } = await Promise.resolve().then(() => (init_fsm_loader(), fsm_loader_exports));
|
|
33009
|
+
const errs = validateFsmSpec2(parsed);
|
|
33010
|
+
if (errs.length) return { success: false, error: "spec invalid", validationErrors: errs };
|
|
33011
|
+
}
|
|
33012
|
+
try {
|
|
33013
|
+
fsm.writeFileSync(safe.path, content, "utf8");
|
|
33014
|
+
return { success: true, specPath: safe.path };
|
|
33015
|
+
} catch (e) {
|
|
33016
|
+
return { success: false, error: `write failed: ${e.message}` };
|
|
33017
|
+
}
|
|
33018
|
+
},
|
|
33019
|
+
// ── Validate an in-progress spec (string or object) without writing. The form
|
|
33020
|
+
// builder calls this on every change so Save can stay disabled while there
|
|
33021
|
+
// are structural / reference / regex errors.
|
|
33022
|
+
validate_spec: async (_ctx, args) => {
|
|
33023
|
+
let parsed = args?.spec;
|
|
33024
|
+
if (typeof args?.content === "string") {
|
|
33025
|
+
try {
|
|
33026
|
+
parsed = JSON.parse(args.content);
|
|
33027
|
+
} catch (e) {
|
|
33028
|
+
return { success: true, valid: false, errors: [`invalid JSON: ${e.message}`] };
|
|
33029
|
+
}
|
|
33030
|
+
}
|
|
33031
|
+
if (!parsed || typeof parsed !== "object") {
|
|
33032
|
+
return { success: true, valid: false, errors: ["spec must be an object or content string"] };
|
|
33033
|
+
}
|
|
33034
|
+
const schema = parsed.$schema;
|
|
33035
|
+
if (schema === "adhdev:cli/spec@4") {
|
|
33036
|
+
const { validateFsmSpec: validateFsmSpec2 } = await Promise.resolve().then(() => (init_fsm_loader(), fsm_loader_exports));
|
|
33037
|
+
const errors = validateFsmSpec2(parsed);
|
|
33038
|
+
return { success: true, valid: errors.length === 0, errors };
|
|
33039
|
+
}
|
|
33040
|
+
return { success: true, valid: false, errors: [`unsupported $schema "${schema}" \u2014 form builder is v4-only`] };
|
|
33041
|
+
},
|
|
33042
|
+
// ── Evaluate a single condition against a live session's current screen —
|
|
33043
|
+
// powers the editor's "does this match right now?" preview. Returns the
|
|
33044
|
+
// recursive match tree.
|
|
33045
|
+
eval_condition_preview: async (ctx, args) => {
|
|
33046
|
+
const sessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : typeof args?.sessionId === "string" ? args.sessionId.trim() : "";
|
|
33047
|
+
if (!sessionId) return { success: false, error: "targetSessionId required" };
|
|
33048
|
+
if (!args?.condition || typeof args.condition !== "object") return { success: false, error: "condition required" };
|
|
33049
|
+
const target = ctx.deps.sessionRegistry.get(sessionId);
|
|
33050
|
+
if (!target) return { success: false, error: "Session not found", sessionId };
|
|
33051
|
+
const adapterObj = ctx.deps.cliManager.findAdapter(target.providerType, { instanceKey: sessionId })?.adapter;
|
|
33052
|
+
const snap = adapterObj && typeof adapterObj.getDebugSnapshot === "function" ? adapterObj.getDebugSnapshot() : null;
|
|
33053
|
+
if (!snap?.screen) return { success: false, error: "no live screen for session" };
|
|
33054
|
+
let sectionsDef;
|
|
33055
|
+
try {
|
|
33056
|
+
const fsm2 = await import("fs");
|
|
33057
|
+
if (snap.specPath) {
|
|
33058
|
+
const raw = JSON.parse(fsm2.readFileSync(snap.specPath, "utf8"));
|
|
33059
|
+
sectionsDef = raw?.sections;
|
|
33060
|
+
}
|
|
33061
|
+
} catch {
|
|
33062
|
+
}
|
|
33063
|
+
const { evaluateConditionPreview: evaluateConditionPreview2 } = await Promise.resolve().then(() => (init_fsm_evaluator(), fsm_evaluator_exports));
|
|
33064
|
+
try {
|
|
33065
|
+
const result = evaluateConditionPreview2(
|
|
33066
|
+
args.condition,
|
|
33067
|
+
sectionsDef,
|
|
33068
|
+
snap.screen,
|
|
33069
|
+
snap.cursorPosition ?? void 0
|
|
33070
|
+
);
|
|
33071
|
+
return { success: true, result, sections: snap.sections ?? null };
|
|
33072
|
+
} catch (e) {
|
|
33073
|
+
return { success: false, error: `eval failed: ${e.message}` };
|
|
33074
|
+
}
|
|
33075
|
+
},
|
|
33076
|
+
// ── Resolve a sections map against a live session's screen — the section
|
|
33077
|
+
// editor's "test" button. Returns, for each section id, the line range + the
|
|
33078
|
+
// text it captures. Accepts an in-progress sections map so it previews
|
|
33079
|
+
// unsaved edits.
|
|
33080
|
+
resolve_section_preview: async (ctx, args) => {
|
|
33081
|
+
const sessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : typeof args?.sessionId === "string" ? args.sessionId.trim() : "";
|
|
33082
|
+
if (!sessionId) return { success: false, error: "targetSessionId required" };
|
|
33083
|
+
if (!args?.sections || typeof args.sections !== "object") return { success: false, error: "sections map required" };
|
|
33084
|
+
const target = ctx.deps.sessionRegistry.get(sessionId);
|
|
33085
|
+
if (!target) return { success: false, error: "Session not found", sessionId };
|
|
33086
|
+
const adapterObj = ctx.deps.cliManager.findAdapter(target.providerType, { instanceKey: sessionId })?.adapter;
|
|
33087
|
+
const snap = adapterObj && typeof adapterObj.getDebugSnapshot === "function" ? adapterObj.getDebugSnapshot() : null;
|
|
33088
|
+
if (!snap?.screen) return { success: false, error: "no live screen for session" };
|
|
33089
|
+
const { resolveSections: resolveSections2 } = await Promise.resolve().then(() => (init_evaluator(), evaluator_exports));
|
|
33090
|
+
try {
|
|
33091
|
+
const lines = String(snap.screen).split("\n").map((l) => l.endsWith("\r") ? l.slice(0, -1) : l);
|
|
33092
|
+
const resolved = resolveSections2(args.sections, lines);
|
|
33093
|
+
return {
|
|
33094
|
+
success: true,
|
|
33095
|
+
screenLineCount: lines.length,
|
|
33096
|
+
sections: resolved.map((s2) => ({ id: s2.id, fromLine: s2.fromLine, toLine: s2.toLine, text: s2.text }))
|
|
33097
|
+
};
|
|
33098
|
+
} catch (e) {
|
|
33099
|
+
return { success: false, error: `resolve failed: ${e.message}` };
|
|
33100
|
+
}
|
|
33101
|
+
}
|
|
33102
|
+
};
|
|
33103
|
+
|
|
33104
|
+
// src/commands/low-family/refine-config.ts
|
|
33105
|
+
init_change_impact_config();
|
|
33106
|
+
var refineConfigHandlers = {
|
|
33107
|
+
get_mesh_refine_config_schema: async (_ctx, _args) => {
|
|
33108
|
+
return {
|
|
33109
|
+
success: true,
|
|
33110
|
+
schema: MESH_REFINE_CONFIG_SCHEMA,
|
|
33111
|
+
locations: MESH_REFINE_CONFIG_LOCATIONS,
|
|
33112
|
+
worktreeBootstrap: {
|
|
33113
|
+
schema: MESH_WORKTREE_BOOTSTRAP_CONFIG_SCHEMA,
|
|
33114
|
+
locations: MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS,
|
|
33115
|
+
sourceOfTruth: "repo worktree bootstrap config",
|
|
33116
|
+
runBehavior: "When present and enabled, clone_mesh_node runs commands after submodule initialization and records status on the worktree node."
|
|
33117
|
+
},
|
|
33118
|
+
sourceOfTruth: "repo mesh/refine config",
|
|
33119
|
+
heuristicRole: "suggestions_only_not_execution_path"
|
|
33120
|
+
};
|
|
33121
|
+
},
|
|
33122
|
+
validate_mesh_refine_config: async (_ctx, args) => {
|
|
33123
|
+
const workspace = typeof args?.workspace === "string" ? args.workspace : process.cwd();
|
|
33124
|
+
const mesh = args?.inlineMesh || {};
|
|
33125
|
+
const loaded = args?.config !== void 0 ? { config: args.config, source: "inline", sourceType: "mesh_policy" } : loadMeshRefineConfig(mesh, workspace);
|
|
33126
|
+
const validation = loaded.config ? validateMeshRefineConfig(loaded.config, loaded.source) : { valid: false, errors: [loaded.error || "repo mesh/refine config unavailable"], commands: [], rejectedCommands: [] };
|
|
33127
|
+
return { success: validation.valid, ...loaded, ...validation };
|
|
33128
|
+
},
|
|
33129
|
+
suggest_mesh_refine_config: async (_ctx, args) => {
|
|
33130
|
+
const workspace = typeof args?.workspace === "string" ? args.workspace : process.cwd();
|
|
33131
|
+
const mesh = args?.inlineMesh || {};
|
|
33132
|
+
return {
|
|
33133
|
+
success: true,
|
|
33134
|
+
...suggestMeshRefineConfig(mesh, workspace),
|
|
33135
|
+
note: "Suggestions are heuristic scaffold only; Refinery will not execute them until saved into repo mesh/refine config."
|
|
33136
|
+
};
|
|
33137
|
+
},
|
|
33138
|
+
get_mesh_change_impact_config_schema: async (_ctx, _args) => {
|
|
33139
|
+
return {
|
|
33140
|
+
success: true,
|
|
33141
|
+
schema: CHANGE_IMPACT_CONFIG_SCHEMA,
|
|
33142
|
+
locations: CHANGE_IMPACT_CONFIG_LOCATIONS,
|
|
33143
|
+
sourceOfTruth: "repo change-impact config",
|
|
33144
|
+
heuristicRole: "suggestions_only_not_execution_path",
|
|
33145
|
+
note: "Declarative config only \u2014 JSON/YAML are parsed but never executed. Defines which package/file changes require a daemon rebuild/restart vs. a web-only redeploy vs. nothing."
|
|
33146
|
+
};
|
|
33147
|
+
},
|
|
33148
|
+
validate_mesh_change_impact_config: async (_ctx, args) => {
|
|
33149
|
+
const workspace = typeof args?.workspace === "string" ? args.workspace : process.cwd();
|
|
33150
|
+
if (args?.config !== void 0) {
|
|
33151
|
+
const validation = validateChangeImpactConfig(args.config, "inline");
|
|
33152
|
+
return { success: validation.valid, source: "inline", sourceType: "mesh_policy", ...validation };
|
|
33153
|
+
}
|
|
33154
|
+
const loaded = loadChangeImpactConfig(workspace);
|
|
33155
|
+
if (loaded.sourceType === "repo_file") {
|
|
33156
|
+
const validation = validateChangeImpactConfig(loaded.config, loaded.source);
|
|
33157
|
+
return { success: validation.valid, ...loaded, ...validation };
|
|
33158
|
+
}
|
|
33159
|
+
return {
|
|
33160
|
+
success: false,
|
|
33161
|
+
...loaded,
|
|
33162
|
+
valid: false,
|
|
33163
|
+
errors: [loaded.error || "repo change-impact config unavailable"]
|
|
33164
|
+
};
|
|
33165
|
+
},
|
|
33166
|
+
suggest_mesh_change_impact_config: async (_ctx, args) => {
|
|
33167
|
+
const workspace = typeof args?.workspace === "string" ? args.workspace : process.cwd();
|
|
33168
|
+
return {
|
|
33169
|
+
success: true,
|
|
33170
|
+
...suggestChangeImpactConfig(workspace),
|
|
33171
|
+
note: "Suggestions are heuristic scaffold only; the draft must be reviewed and saved into repo change-impact config before it takes effect. Nothing is executed."
|
|
33172
|
+
};
|
|
33173
|
+
}
|
|
33174
|
+
};
|
|
33175
|
+
|
|
33176
|
+
// src/commands/low-family/index.ts
|
|
33177
|
+
var lowFamilyRegistry = new Map(
|
|
33178
|
+
Object.entries({
|
|
33179
|
+
...sessionHostHandlers,
|
|
33180
|
+
...specProviderDevHandlers,
|
|
33181
|
+
...refineConfigHandlers
|
|
33182
|
+
})
|
|
33183
|
+
);
|
|
33184
|
+
|
|
32688
33185
|
// src/commands/cli-manager.ts
|
|
32689
33186
|
init_provider_cli_adapter();
|
|
32690
33187
|
init_cli_detector();
|
|
@@ -36220,7 +36717,13 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
36220
36717
|
this.settings = {
|
|
36221
36718
|
...this.settings,
|
|
36222
36719
|
meshNodeFor: assignment.meshId,
|
|
36223
|
-
|
|
36720
|
+
// WTCLAIM (A): track the bound node id under BOTH the active marker
|
|
36721
|
+
// (meshNodeId, cleared on detach) and a sticky marker (meshLastNodeId,
|
|
36722
|
+
// preserved across detach). The sticky marker lets a detached but still
|
|
36723
|
+
// coordinator-owned session be re-picked ONLY for the SAME node it served
|
|
36724
|
+
// — never auto-adopted for a sibling node (e.g. a cloned worktree) that
|
|
36725
|
+
// shares this daemon. See isMeshOwnedDelegateSession's post-detach gate.
|
|
36726
|
+
...assignment.nodeId ? { meshNodeId: assignment.nodeId, meshLastNodeId: assignment.nodeId } : {},
|
|
36224
36727
|
...assignment.taskId ? { meshActiveTaskId: assignment.taskId } : {},
|
|
36225
36728
|
...assignment.coordinatorDaemonId ? { meshCoordinatorDaemonId: assignment.coordinatorDaemonId } : {},
|
|
36226
36729
|
// Session-level routing anchor: the originating coordinator session, so this
|
|
@@ -36239,9 +36742,9 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
36239
36742
|
if (!this.settings.meshNodeFor && !this.settings.meshActiveTaskId && !this.settings.meshNodeId) return;
|
|
36240
36743
|
const { meshNodeFor, meshNodeId, meshActiveTaskId, ...rest } = this.settings;
|
|
36241
36744
|
void meshNodeFor;
|
|
36242
|
-
void meshNodeId;
|
|
36243
36745
|
void meshActiveTaskId;
|
|
36244
|
-
|
|
36746
|
+
const lastNodeId = typeof meshNodeId === "string" && meshNodeId.trim() ? meshNodeId.trim() : typeof rest.meshLastNodeId === "string" && rest.meshLastNodeId.trim() ? rest.meshLastNodeId.trim() : void 0;
|
|
36747
|
+
this.settings = lastNodeId ? { ...rest, meshLastNodeId: lastNodeId } : rest;
|
|
36245
36748
|
this.adapter.updateRuntimeSettings?.(this.settings);
|
|
36246
36749
|
}
|
|
36247
36750
|
/**
|
|
@@ -38917,6 +39420,10 @@ function hasCompletedStartingLaunch(adapter) {
|
|
|
38917
39420
|
if (hasNonEmptyModalButtons2(adapterStatus?.activeModal ?? adapterStatus?.modal ?? parsedStatus?.activeModal ?? parsedStatus?.modal)) return false;
|
|
38918
39421
|
return hasFinalAssistantMessage(parsedStatus?.messages);
|
|
38919
39422
|
}
|
|
39423
|
+
function normalizeDirForCompare(dir) {
|
|
39424
|
+
if (typeof dir !== "string") return "";
|
|
39425
|
+
return dir.trim().replace(/[\\/]+/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
39426
|
+
}
|
|
38920
39427
|
function shouldSuppressStaleParsedBusyStatus(adapterStatus, parsedStatus, adapter) {
|
|
38921
39428
|
const parsedRawStatus = normalizeAgentStatus(parsedStatus?.status);
|
|
38922
39429
|
if (!BUSY_AGENT_STATUSES.has(parsedRawStatus)) return false;
|
|
@@ -39665,6 +40172,33 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
39665
40172
|
const adapter = this.adapters.get(ik);
|
|
39666
40173
|
return adapter ? { adapter, key: ik } : null;
|
|
39667
40174
|
}
|
|
40175
|
+
/**
|
|
40176
|
+
* WTCLAIM (B): resolve the adapter for a mesh dispatch that named a node
|
|
40177
|
+
* (meshContext.nodeId) but carried no explicit session. Matches by the
|
|
40178
|
+
* instance's bound mesh node id (settings.meshNodeId, falling back to the
|
|
40179
|
+
* sticky meshLastNodeId) first, then by the node workspace (workingDir).
|
|
40180
|
+
*
|
|
40181
|
+
* Unlike findAdapter's step-2 fuzzy fallback, this NEVER degrades to a
|
|
40182
|
+
* provider-only first-match: on a daemon hosting BOTH a base node and a
|
|
40183
|
+
* cloned worktree node (same daemonId), that fuzzy match could land a
|
|
40184
|
+
* worktree-targeted task on the base session. Returns null when no session
|
|
40185
|
+
* is bound to this node so the caller fails closed.
|
|
40186
|
+
*/
|
|
40187
|
+
findMeshNodeAdapter(agentType, nodeId, dir) {
|
|
40188
|
+
const instanceManager = this.deps.getInstanceManager();
|
|
40189
|
+
const targetDir = normalizeDirForCompare(dir);
|
|
40190
|
+
let workspaceMatch = null;
|
|
40191
|
+
for (const [k, a] of this.adapters) {
|
|
40192
|
+
if (a.cliType !== agentType) continue;
|
|
40193
|
+
const settings = instanceManager?.getInstance(k)?.getState?.()?.settings;
|
|
40194
|
+
const boundNodeId = typeof settings?.meshNodeId === "string" && settings.meshNodeId.trim() ? settings.meshNodeId.trim() : typeof settings?.meshLastNodeId === "string" ? settings.meshLastNodeId.trim() : "";
|
|
40195
|
+
if (boundNodeId && boundNodeId === nodeId) return { adapter: a, key: k };
|
|
40196
|
+
if (!workspaceMatch && targetDir && normalizeDirForCompare(a.workingDir) === targetDir) {
|
|
40197
|
+
workspaceMatch = { adapter: a, key: k };
|
|
40198
|
+
}
|
|
40199
|
+
}
|
|
40200
|
+
return workspaceMatch;
|
|
40201
|
+
}
|
|
39668
40202
|
// ─── CLI command handling ────────────────────────────
|
|
39669
40203
|
async handleCliCommand(cmd, args) {
|
|
39670
40204
|
switch (cmd) {
|
|
@@ -39850,10 +40384,22 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
39850
40384
|
const agentType = args?.agentType || args?.cliType;
|
|
39851
40385
|
const action = args?.action;
|
|
39852
40386
|
if (!agentType || !action) throw new Error("agentType and action required");
|
|
39853
|
-
const
|
|
39854
|
-
|
|
39855
|
-
|
|
39856
|
-
});
|
|
40387
|
+
const meshScopeNodeId = (() => {
|
|
40388
|
+
const mc = args?.meshContext;
|
|
40389
|
+
return mc && typeof mc === "object" && typeof mc.nodeId === "string" ? mc.nodeId.trim() : "";
|
|
40390
|
+
})();
|
|
40391
|
+
let found;
|
|
40392
|
+
if (meshScopeNodeId && !args?.targetSessionId) {
|
|
40393
|
+
found = this.findMeshNodeAdapter(agentType, meshScopeNodeId, args?.dir);
|
|
40394
|
+
if (!found) {
|
|
40395
|
+
throw new Error(`No mesh worker session bound to node '${meshScopeNodeId}' for agent '${agentType}' on this daemon; refusing provider-only fuzzy match to avoid cross-node dispatch`);
|
|
40396
|
+
}
|
|
40397
|
+
} else {
|
|
40398
|
+
found = this.findAdapter(agentType, {
|
|
40399
|
+
dir: args?.dir,
|
|
40400
|
+
instanceKey: args?.targetSessionId
|
|
40401
|
+
});
|
|
40402
|
+
}
|
|
39857
40403
|
if (!found) throw new Error(`CLI agent not running: ${agentType}`);
|
|
39858
40404
|
const { adapter, key } = found;
|
|
39859
40405
|
if (action === "send_chat") {
|
|
@@ -43747,7 +44293,6 @@ init_workspaces();
|
|
|
43747
44293
|
init_recent_activity();
|
|
43748
44294
|
init_cli_detector();
|
|
43749
44295
|
init_git_status();
|
|
43750
|
-
init_change_impact_config();
|
|
43751
44296
|
init_dist();
|
|
43752
44297
|
init_logger();
|
|
43753
44298
|
|
|
@@ -45090,7 +45635,7 @@ function buildMeshNodeMachineIdentity(node, opts) {
|
|
|
45090
45635
|
const machineName = readMeshNodeDisplayMachineName(node);
|
|
45091
45636
|
const coordinatorHostname = readStringValue(opts.coordinatorHostname);
|
|
45092
45637
|
const machineIdMatches = Boolean(opts.localMachineId && machineId && opts.localMachineId === machineId);
|
|
45093
|
-
const daemonIdMatches = Boolean(opts.localDaemonId && daemonId && opts.localDaemonId
|
|
45638
|
+
const daemonIdMatches = Boolean(opts.localDaemonId && daemonId && daemonIdsEquivalent(opts.localDaemonId, daemonId));
|
|
45094
45639
|
const hostnameMatches = Boolean(
|
|
45095
45640
|
normalizeMeshHostname(hostname3) && normalizeMeshHostname(coordinatorHostname) && normalizeMeshHostname(hostname3) === normalizeMeshHostname(coordinatorHostname)
|
|
45096
45641
|
);
|
|
@@ -45805,10 +46350,10 @@ async function hydrateInlineMeshDirectTruth(args) {
|
|
|
45805
46350
|
const isSelfNode = Boolean(
|
|
45806
46351
|
nodeId && selectedCoordinatorNodeId && nodeId === selectedCoordinatorNodeId
|
|
45807
46352
|
) || Boolean(
|
|
45808
|
-
daemonId && (daemonId
|
|
46353
|
+
daemonId && (daemonIdsEquivalent(daemonId, args.localMachineId) || daemonIdsEquivalent(daemonId, args.statusInstanceId))
|
|
45809
46354
|
) || Boolean(args.meshSource !== "local_config" && nodeIndex === 0);
|
|
45810
46355
|
const isSelfDaemonNode = Boolean(
|
|
45811
|
-
daemonId && (daemonId
|
|
46356
|
+
daemonId && (daemonIdsEquivalent(daemonId, args.localMachineId) || daemonIdsEquivalent(daemonId, args.statusInstanceId))
|
|
45812
46357
|
);
|
|
45813
46358
|
if ((isSelfNode || isSelfDaemonNode) && isDeadLocalWorktreeNode(node)) {
|
|
45814
46359
|
deadNodeIds.push(nodeId);
|
|
@@ -47147,97 +47692,6 @@ function normalizeCommandArgsWithInteractionId(args) {
|
|
|
47147
47692
|
}
|
|
47148
47693
|
return base;
|
|
47149
47694
|
}
|
|
47150
|
-
function resolveSpecPathInProviders(specPath, fsm, pathm, osm) {
|
|
47151
|
-
let rootReal;
|
|
47152
|
-
try {
|
|
47153
|
-
rootReal = fsm.realpathSync(pathm.join(osm.homedir(), ".adhdev", "providers"));
|
|
47154
|
-
} catch (e) {
|
|
47155
|
-
return { ok: false, error: `providers root unavailable: ${e.message}` };
|
|
47156
|
-
}
|
|
47157
|
-
const resolved = pathm.resolve(specPath);
|
|
47158
|
-
const base = pathm.basename(resolved);
|
|
47159
|
-
if (!/^[\w.-]+\.json$/.test(base)) {
|
|
47160
|
-
return { ok: false, error: "refused: spec file must be a *.json basename" };
|
|
47161
|
-
}
|
|
47162
|
-
let parentReal;
|
|
47163
|
-
try {
|
|
47164
|
-
parentReal = fsm.realpathSync(pathm.dirname(resolved));
|
|
47165
|
-
} catch (e) {
|
|
47166
|
-
return { ok: false, error: `spec directory not found: ${e.message}` };
|
|
47167
|
-
}
|
|
47168
|
-
if (parentReal !== rootReal && !parentReal.startsWith(rootReal + pathm.sep)) {
|
|
47169
|
-
return { ok: false, error: "refused: spec path must be under the providers root" };
|
|
47170
|
-
}
|
|
47171
|
-
const safe = pathm.join(parentReal, base);
|
|
47172
|
-
try {
|
|
47173
|
-
const st = fsm.lstatSync(safe);
|
|
47174
|
-
if (st.isSymbolicLink()) return { ok: false, error: "refused: spec path is a symlink" };
|
|
47175
|
-
} catch {
|
|
47176
|
-
}
|
|
47177
|
-
return { ok: true, path: safe };
|
|
47178
|
-
}
|
|
47179
|
-
function toHostedCliRuntimeDescriptor(record) {
|
|
47180
|
-
if (!record || typeof record !== "object") return null;
|
|
47181
|
-
const runtimeId = typeof record.sessionId === "string" ? record.sessionId : "";
|
|
47182
|
-
const cliType = typeof record.providerType === "string" ? record.providerType : "";
|
|
47183
|
-
const workspace = typeof record.workspace === "string" ? record.workspace : "";
|
|
47184
|
-
if (!runtimeId || !cliType || !workspace) return null;
|
|
47185
|
-
return {
|
|
47186
|
-
runtimeId,
|
|
47187
|
-
runtimeKey: typeof record.runtimeKey === "string" ? record.runtimeKey : void 0,
|
|
47188
|
-
displayName: typeof record.displayName === "string" ? record.displayName : void 0,
|
|
47189
|
-
workspaceLabel: typeof record.workspaceLabel === "string" ? record.workspaceLabel : void 0,
|
|
47190
|
-
lifecycle: typeof record.lifecycle === "string" ? record.lifecycle : void 0,
|
|
47191
|
-
recoveryState: typeof record.meta?.runtimeRecoveryState === "string" ? String(record.meta.runtimeRecoveryState) : null,
|
|
47192
|
-
cliType,
|
|
47193
|
-
workspace,
|
|
47194
|
-
cliArgs: Array.isArray(record.meta?.cliArgs) ? record.meta.cliArgs : [],
|
|
47195
|
-
providerSessionId: typeof record.meta?.providerSessionId === "string" ? String(record.meta.providerSessionId) : void 0
|
|
47196
|
-
};
|
|
47197
|
-
}
|
|
47198
|
-
function getWriteConflictOwnerClientId(error) {
|
|
47199
|
-
const message = typeof error === "string" ? error : error instanceof Error ? error.message : "";
|
|
47200
|
-
const match = /^Write owned by\s+(.+)$/.exec(message.trim());
|
|
47201
|
-
return match?.[1]?.trim() || void 0;
|
|
47202
|
-
}
|
|
47203
|
-
function summarizeSessionHostRecord(result) {
|
|
47204
|
-
if (!result || typeof result !== "object") return {};
|
|
47205
|
-
const record = result;
|
|
47206
|
-
return {
|
|
47207
|
-
runtimeKey: typeof record.runtimeKey === "string" ? record.runtimeKey : void 0,
|
|
47208
|
-
lifecycle: typeof record.lifecycle === "string" ? record.lifecycle : void 0,
|
|
47209
|
-
surfaceKind: getSessionHostSurfaceKind(record),
|
|
47210
|
-
attachedClientCount: Array.isArray(record.attachedClients) ? record.attachedClients.length : void 0,
|
|
47211
|
-
hasWriteOwner: !!record.writeOwner,
|
|
47212
|
-
writeOwnerClientId: typeof record.writeOwner?.clientId === "string" ? record.writeOwner.clientId : void 0
|
|
47213
|
-
};
|
|
47214
|
-
}
|
|
47215
|
-
function summarizeSessionHostRecords(result) {
|
|
47216
|
-
const records = Array.isArray(result) ? result : [];
|
|
47217
|
-
const groups = partitionSessionHostRecords(records);
|
|
47218
|
-
return {
|
|
47219
|
-
sessionCount: records.length,
|
|
47220
|
-
liveRuntimeCount: groups.liveRuntimes.length,
|
|
47221
|
-
recoverySnapshotCount: groups.recoverySnapshots.length,
|
|
47222
|
-
inactiveRecordCount: groups.inactiveRecords.length
|
|
47223
|
-
};
|
|
47224
|
-
}
|
|
47225
|
-
function summarizeSessionHostDiagnostics(result) {
|
|
47226
|
-
const diagnostics = result && typeof result === "object" ? result : {};
|
|
47227
|
-
const sessions = Array.isArray(diagnostics.sessions) ? diagnostics.sessions : [];
|
|
47228
|
-
return {
|
|
47229
|
-
runtimeCount: typeof diagnostics.runtimeCount === "number" ? diagnostics.runtimeCount : void 0,
|
|
47230
|
-
...summarizeSessionHostRecords(sessions)
|
|
47231
|
-
};
|
|
47232
|
-
}
|
|
47233
|
-
function summarizeSessionHostPruneResult(result) {
|
|
47234
|
-
const value = result && typeof result === "object" ? result : {};
|
|
47235
|
-
return {
|
|
47236
|
-
duplicateGroupCount: typeof value.duplicateGroupCount === "number" ? value.duplicateGroupCount : void 0,
|
|
47237
|
-
prunedCount: Array.isArray(value.prunedSessionIds) ? value.prunedSessionIds.length : void 0,
|
|
47238
|
-
keptCount: Array.isArray(value.keptSessionIds) ? value.keptSessionIds.length : void 0
|
|
47239
|
-
};
|
|
47240
|
-
}
|
|
47241
47695
|
function normalizeStandaloneHostCommandUrl(hostAddress) {
|
|
47242
47696
|
const raw = hostAddress.trim();
|
|
47243
47697
|
if (!raw) throw new Error("hostAddress required");
|
|
@@ -48087,56 +48541,6 @@ var DaemonCommandRouter = class {
|
|
|
48087
48541
|
...errors.length ? { errors } : {}
|
|
48088
48542
|
};
|
|
48089
48543
|
}
|
|
48090
|
-
async traceSessionHostAction(action, args, run, summarizeResult2) {
|
|
48091
|
-
const interactionId = typeof args?._interactionId === "string" ? args._interactionId : void 0;
|
|
48092
|
-
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : void 0;
|
|
48093
|
-
const requestedPayload = { action };
|
|
48094
|
-
if (sessionId) requestedPayload.sessionId = sessionId;
|
|
48095
|
-
if (typeof args?.clientId === "string") requestedPayload.clientId = args.clientId;
|
|
48096
|
-
if (typeof args?.signal === "string") requestedPayload.signal = args.signal;
|
|
48097
|
-
if (typeof args?.providerType === "string") requestedPayload.providerType = args.providerType;
|
|
48098
|
-
if (typeof args?.workspace === "string") requestedPayload.workspace = args.workspace;
|
|
48099
|
-
if (typeof args?.dryRun === "boolean") requestedPayload.dryRun = args.dryRun;
|
|
48100
|
-
recordDebugTrace({
|
|
48101
|
-
interactionId,
|
|
48102
|
-
category: "session_host",
|
|
48103
|
-
stage: "action_requested",
|
|
48104
|
-
level: "info",
|
|
48105
|
-
sessionId,
|
|
48106
|
-
payload: requestedPayload
|
|
48107
|
-
});
|
|
48108
|
-
try {
|
|
48109
|
-
const result = await run();
|
|
48110
|
-
recordDebugTrace({
|
|
48111
|
-
interactionId,
|
|
48112
|
-
category: "session_host",
|
|
48113
|
-
stage: "action_result",
|
|
48114
|
-
level: "info",
|
|
48115
|
-
sessionId,
|
|
48116
|
-
payload: {
|
|
48117
|
-
...requestedPayload,
|
|
48118
|
-
success: true,
|
|
48119
|
-
...summarizeResult2 ? summarizeResult2(result) : {}
|
|
48120
|
-
}
|
|
48121
|
-
});
|
|
48122
|
-
return result;
|
|
48123
|
-
} catch (error) {
|
|
48124
|
-
recordDebugTrace({
|
|
48125
|
-
interactionId,
|
|
48126
|
-
category: "session_host",
|
|
48127
|
-
stage: "action_failed",
|
|
48128
|
-
level: "error",
|
|
48129
|
-
sessionId,
|
|
48130
|
-
payload: {
|
|
48131
|
-
...requestedPayload,
|
|
48132
|
-
error: error?.message || String(error),
|
|
48133
|
-
failureKind: getWriteConflictOwnerClientId(error) ? "write_conflict" : "request_failed",
|
|
48134
|
-
conflictOwnerClientId: getWriteConflictOwnerClientId(error)
|
|
48135
|
-
}
|
|
48136
|
-
});
|
|
48137
|
-
throw error;
|
|
48138
|
-
}
|
|
48139
|
-
}
|
|
48140
48544
|
/**
|
|
48141
48545
|
* Unified command routing.
|
|
48142
48546
|
* Returns result for all commands:
|
|
@@ -49511,6 +49915,10 @@ ${hintLines.join("\n")}` : "",
|
|
|
49511
49915
|
}
|
|
49512
49916
|
}
|
|
49513
49917
|
}
|
|
49918
|
+
const lowFamilyHandler = lowFamilyRegistry.get(cmd);
|
|
49919
|
+
if (lowFamilyHandler) {
|
|
49920
|
+
return await lowFamilyHandler({ deps: this.deps }, args);
|
|
49921
|
+
}
|
|
49514
49922
|
switch (cmd) {
|
|
49515
49923
|
// ─── CLI / ACP commands ───
|
|
49516
49924
|
case "mesh_forward_event": {
|
|
@@ -49638,121 +50046,6 @@ ${hintLines.join("\n")}` : "",
|
|
|
49638
50046
|
const trace = getRecentDebugTrace({ interactionId, category, limit: count }).filter((entry) => !sinceTs || entry.ts > sinceTs);
|
|
49639
50047
|
return { success: true, trace, count: trace.length };
|
|
49640
50048
|
}
|
|
49641
|
-
case "session_host_get_diagnostics": {
|
|
49642
|
-
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
49643
|
-
const diagnostics = await this.traceSessionHostAction("session_host_get_diagnostics", args, () => this.deps.sessionHostControl.getDiagnostics({
|
|
49644
|
-
includeSessions: args?.includeSessions !== false,
|
|
49645
|
-
limit: Number(args?.limit) || void 0
|
|
49646
|
-
}), (result) => ({
|
|
49647
|
-
includeSessions: args?.includeSessions !== false,
|
|
49648
|
-
limit: Number(args?.limit) || void 0,
|
|
49649
|
-
...summarizeSessionHostDiagnostics(result)
|
|
49650
|
-
}));
|
|
49651
|
-
return { success: true, diagnostics };
|
|
49652
|
-
}
|
|
49653
|
-
case "session_host_list_sessions": {
|
|
49654
|
-
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
49655
|
-
const sessions = await this.traceSessionHostAction("session_host_list_sessions", args, () => this.deps.sessionHostControl.listSessions(), (records) => summarizeSessionHostRecords(records));
|
|
49656
|
-
return { success: true, sessions };
|
|
49657
|
-
}
|
|
49658
|
-
case "session_host_stop_session": {
|
|
49659
|
-
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
49660
|
-
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
49661
|
-
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
49662
|
-
const record = await this.traceSessionHostAction("session_host_stop_session", args, () => this.deps.sessionHostControl.stopSession(sessionId), (result) => summarizeSessionHostRecord(result));
|
|
49663
|
-
return { success: true, record };
|
|
49664
|
-
}
|
|
49665
|
-
case "session_host_resume_session": {
|
|
49666
|
-
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
49667
|
-
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
49668
|
-
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
49669
|
-
const record = await this.traceSessionHostAction("session_host_resume_session", args, async () => {
|
|
49670
|
-
const nextRecord = await this.deps.sessionHostControl.resumeSession(sessionId);
|
|
49671
|
-
const hosted = toHostedCliRuntimeDescriptor(nextRecord);
|
|
49672
|
-
if (hosted) {
|
|
49673
|
-
await this.deps.cliManager.restoreHostedSessions([hosted]);
|
|
49674
|
-
}
|
|
49675
|
-
return nextRecord;
|
|
49676
|
-
}, (result) => ({
|
|
49677
|
-
...summarizeSessionHostRecord(result),
|
|
49678
|
-
restoredHostedSession: !!toHostedCliRuntimeDescriptor(result)
|
|
49679
|
-
}));
|
|
49680
|
-
return { success: true, record };
|
|
49681
|
-
}
|
|
49682
|
-
case "session_host_restart_session": {
|
|
49683
|
-
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
49684
|
-
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
49685
|
-
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
49686
|
-
const record = await this.traceSessionHostAction("session_host_restart_session", args, async () => {
|
|
49687
|
-
const nextRecord = await this.deps.sessionHostControl.restartSession(sessionId);
|
|
49688
|
-
const hosted = toHostedCliRuntimeDescriptor(nextRecord);
|
|
49689
|
-
if (hosted) {
|
|
49690
|
-
await this.deps.cliManager.restoreHostedSessions([hosted]);
|
|
49691
|
-
}
|
|
49692
|
-
return nextRecord;
|
|
49693
|
-
}, (result) => ({
|
|
49694
|
-
...summarizeSessionHostRecord(result),
|
|
49695
|
-
restoredHostedSession: !!toHostedCliRuntimeDescriptor(result)
|
|
49696
|
-
}));
|
|
49697
|
-
return { success: true, record };
|
|
49698
|
-
}
|
|
49699
|
-
case "session_host_send_signal": {
|
|
49700
|
-
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
49701
|
-
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
49702
|
-
const signal = typeof args?.signal === "string" ? args.signal : "";
|
|
49703
|
-
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
49704
|
-
if (!signal) return { success: false, error: "signal required" };
|
|
49705
|
-
const record = await this.traceSessionHostAction("session_host_send_signal", args, () => this.deps.sessionHostControl.sendSignal(sessionId, signal), (result) => summarizeSessionHostRecord(result));
|
|
49706
|
-
return { success: true, record };
|
|
49707
|
-
}
|
|
49708
|
-
case "session_host_force_detach_client": {
|
|
49709
|
-
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
49710
|
-
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
49711
|
-
const clientId = typeof args?.clientId === "string" ? args.clientId : "";
|
|
49712
|
-
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
49713
|
-
if (!clientId) return { success: false, error: "clientId required" };
|
|
49714
|
-
const record = await this.traceSessionHostAction("session_host_force_detach_client", args, () => this.deps.sessionHostControl.forceDetachClient(sessionId, clientId), (result) => summarizeSessionHostRecord(result));
|
|
49715
|
-
return { success: true, record };
|
|
49716
|
-
}
|
|
49717
|
-
case "session_host_prune_duplicate_sessions": {
|
|
49718
|
-
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
49719
|
-
const result = await this.traceSessionHostAction("session_host_prune_duplicate_sessions", args, () => this.deps.sessionHostControl.pruneDuplicateSessions({
|
|
49720
|
-
providerType: typeof args?.providerType === "string" ? args.providerType : void 0,
|
|
49721
|
-
workspace: typeof args?.workspace === "string" ? args.workspace : void 0,
|
|
49722
|
-
dryRun: args?.dryRun === true
|
|
49723
|
-
}), (value) => summarizeSessionHostPruneResult(value));
|
|
49724
|
-
return { success: true, result };
|
|
49725
|
-
}
|
|
49726
|
-
case "session_host_acquire_write": {
|
|
49727
|
-
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
49728
|
-
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
49729
|
-
const clientId = typeof args?.clientId === "string" ? args.clientId : "";
|
|
49730
|
-
const ownerType = args?.ownerType === "agent" ? "agent" : "user";
|
|
49731
|
-
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
49732
|
-
if (!clientId) return { success: false, error: "clientId required" };
|
|
49733
|
-
const record = await this.traceSessionHostAction("session_host_acquire_write", args, () => this.deps.sessionHostControl.acquireWrite({
|
|
49734
|
-
sessionId,
|
|
49735
|
-
clientId,
|
|
49736
|
-
ownerType,
|
|
49737
|
-
force: args?.force !== false
|
|
49738
|
-
}), (result) => ({
|
|
49739
|
-
...summarizeSessionHostRecord(result),
|
|
49740
|
-
ownerType
|
|
49741
|
-
}));
|
|
49742
|
-
return { success: true, record };
|
|
49743
|
-
}
|
|
49744
|
-
case "session_host_release_write": {
|
|
49745
|
-
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
49746
|
-
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
49747
|
-
const clientId = typeof args?.clientId === "string" ? args.clientId : "";
|
|
49748
|
-
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
49749
|
-
if (!clientId) return { success: false, error: "clientId required" };
|
|
49750
|
-
const record = await this.traceSessionHostAction("session_host_release_write", args, () => this.deps.sessionHostControl.releaseWrite({
|
|
49751
|
-
sessionId,
|
|
49752
|
-
clientId
|
|
49753
|
-
}), (result) => summarizeSessionHostRecord(result));
|
|
49754
|
-
return { success: true, record };
|
|
49755
|
-
}
|
|
49756
50049
|
case "list_saved_sessions": {
|
|
49757
50050
|
const providerType = typeof args?.providerType === "string" ? args.providerType.trim() : typeof args?.agentType === "string" ? args.agentType.trim() : "";
|
|
49758
50051
|
const kind = args?.kind === "acp" ? "acp" : "cli";
|
|
@@ -50008,166 +50301,6 @@ ${hintLines.join("\n")}` : "",
|
|
|
50008
50301
|
} : null
|
|
50009
50302
|
};
|
|
50010
50303
|
}
|
|
50011
|
-
case "get_spec_debug": {
|
|
50012
|
-
const sessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : typeof args?.sessionId === "string" ? args.sessionId.trim() : "";
|
|
50013
|
-
if (!sessionId) return { success: false, error: "targetSessionId required" };
|
|
50014
|
-
const target = this.deps.sessionRegistry.get(sessionId);
|
|
50015
|
-
if (!target) return { success: false, error: "Session not found", sessionId };
|
|
50016
|
-
const adapterObj = this.deps.cliManager.findAdapter(target.providerType, { instanceKey: sessionId })?.adapter;
|
|
50017
|
-
const snapshot = adapterObj ? typeof adapterObj.getDebugSnapshot === "function" ? adapterObj.getDebugSnapshot() : typeof adapterObj.getDebugState === "function" ? adapterObj.getDebugState() : null : null;
|
|
50018
|
-
return {
|
|
50019
|
-
success: true,
|
|
50020
|
-
sessionId,
|
|
50021
|
-
providerType: target.providerType,
|
|
50022
|
-
isSpecProvider: snapshot !== null,
|
|
50023
|
-
snapshot
|
|
50024
|
-
};
|
|
50025
|
-
}
|
|
50026
|
-
// ── Spec source read/write for the debug panel's live editor.
|
|
50027
|
-
// Lets the dashboard load a session's spec.json, edit it, and
|
|
50028
|
-
// save it back — the driver's fs.watch picks up the change and
|
|
50029
|
-
// hot-reloads the FSM with no restart. Writes are confined to
|
|
50030
|
-
// files under ~/.adhdev/providers to avoid arbitrary fs access.
|
|
50031
|
-
case "get_spec_source": {
|
|
50032
|
-
const fsm = await import("fs");
|
|
50033
|
-
const pathm = await import("path");
|
|
50034
|
-
const osm = await import("os");
|
|
50035
|
-
const sessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : typeof args?.sessionId === "string" ? args.sessionId.trim() : "";
|
|
50036
|
-
let specPath = typeof args?.specPath === "string" ? args.specPath : "";
|
|
50037
|
-
if (!specPath && sessionId) {
|
|
50038
|
-
const target = this.deps.sessionRegistry.get(sessionId);
|
|
50039
|
-
const adapterObj = target ? this.deps.cliManager.findAdapter(target.providerType, { instanceKey: sessionId })?.adapter : null;
|
|
50040
|
-
const snap = adapterObj && typeof adapterObj.getDebugSnapshot === "function" ? adapterObj.getDebugSnapshot() : null;
|
|
50041
|
-
specPath = snap?.specPath ?? "";
|
|
50042
|
-
}
|
|
50043
|
-
if (!specPath) return { success: false, error: "specPath or resolvable targetSessionId required" };
|
|
50044
|
-
const safe = resolveSpecPathInProviders(specPath, fsm, pathm, osm);
|
|
50045
|
-
if (!safe.ok) return { success: false, error: safe.error, specPath };
|
|
50046
|
-
try {
|
|
50047
|
-
const content = fsm.readFileSync(safe.path, "utf8");
|
|
50048
|
-
return { success: true, specPath: safe.path, content };
|
|
50049
|
-
} catch (e) {
|
|
50050
|
-
return { success: false, error: `read failed: ${e.message}`, specPath };
|
|
50051
|
-
}
|
|
50052
|
-
}
|
|
50053
|
-
case "write_spec_source": {
|
|
50054
|
-
const fsm = await import("fs");
|
|
50055
|
-
const pathm = await import("path");
|
|
50056
|
-
const osm = await import("os");
|
|
50057
|
-
const specPath = typeof args?.specPath === "string" ? args.specPath : "";
|
|
50058
|
-
const content = typeof args?.content === "string" ? args.content : "";
|
|
50059
|
-
if (!specPath) return { success: false, error: "specPath required" };
|
|
50060
|
-
if (!content) return { success: false, error: "content required" };
|
|
50061
|
-
const safe = resolveSpecPathInProviders(specPath, fsm, pathm, osm);
|
|
50062
|
-
if (!safe.ok) return { success: false, error: safe.error };
|
|
50063
|
-
let parsed;
|
|
50064
|
-
try {
|
|
50065
|
-
parsed = JSON.parse(content);
|
|
50066
|
-
} catch (e) {
|
|
50067
|
-
return { success: false, error: `invalid JSON: ${e.message}` };
|
|
50068
|
-
}
|
|
50069
|
-
if (parsed?.$schema === "adhdev:cli/spec@4") {
|
|
50070
|
-
const { validateFsmSpec: validateFsmSpec2 } = await Promise.resolve().then(() => (init_fsm_loader(), fsm_loader_exports));
|
|
50071
|
-
const errs = validateFsmSpec2(parsed);
|
|
50072
|
-
if (errs.length) return { success: false, error: "spec invalid", validationErrors: errs };
|
|
50073
|
-
}
|
|
50074
|
-
try {
|
|
50075
|
-
fsm.writeFileSync(safe.path, content, "utf8");
|
|
50076
|
-
return { success: true, specPath: safe.path };
|
|
50077
|
-
} catch (e) {
|
|
50078
|
-
return { success: false, error: `write failed: ${e.message}` };
|
|
50079
|
-
}
|
|
50080
|
-
}
|
|
50081
|
-
// ── Validate an in-progress spec (string or object) without writing.
|
|
50082
|
-
// The form builder calls this on every change so Save can stay
|
|
50083
|
-
// disabled while there are structural / reference / regex errors.
|
|
50084
|
-
case "validate_spec": {
|
|
50085
|
-
let parsed = args?.spec;
|
|
50086
|
-
if (typeof args?.content === "string") {
|
|
50087
|
-
try {
|
|
50088
|
-
parsed = JSON.parse(args.content);
|
|
50089
|
-
} catch (e) {
|
|
50090
|
-
return { success: true, valid: false, errors: [`invalid JSON: ${e.message}`] };
|
|
50091
|
-
}
|
|
50092
|
-
}
|
|
50093
|
-
if (!parsed || typeof parsed !== "object") {
|
|
50094
|
-
return { success: true, valid: false, errors: ["spec must be an object or content string"] };
|
|
50095
|
-
}
|
|
50096
|
-
const schema = parsed.$schema;
|
|
50097
|
-
if (schema === "adhdev:cli/spec@4") {
|
|
50098
|
-
const { validateFsmSpec: validateFsmSpec2 } = await Promise.resolve().then(() => (init_fsm_loader(), fsm_loader_exports));
|
|
50099
|
-
const errors = validateFsmSpec2(parsed);
|
|
50100
|
-
return { success: true, valid: errors.length === 0, errors };
|
|
50101
|
-
}
|
|
50102
|
-
return { success: true, valid: false, errors: [`unsupported $schema "${schema}" \u2014 form builder is v4-only`] };
|
|
50103
|
-
}
|
|
50104
|
-
// ── Evaluate a single condition against a live session's current
|
|
50105
|
-
// screen — powers the editor's "does this match right now?"
|
|
50106
|
-
// preview. Returns the recursive match tree.
|
|
50107
|
-
case "eval_condition_preview": {
|
|
50108
|
-
const sessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : typeof args?.sessionId === "string" ? args.sessionId.trim() : "";
|
|
50109
|
-
if (!sessionId) return { success: false, error: "targetSessionId required" };
|
|
50110
|
-
if (!args?.condition || typeof args.condition !== "object") return { success: false, error: "condition required" };
|
|
50111
|
-
const target = this.deps.sessionRegistry.get(sessionId);
|
|
50112
|
-
if (!target) return { success: false, error: "Session not found", sessionId };
|
|
50113
|
-
const adapterObj = this.deps.cliManager.findAdapter(target.providerType, { instanceKey: sessionId })?.adapter;
|
|
50114
|
-
const snap = adapterObj && typeof adapterObj.getDebugSnapshot === "function" ? adapterObj.getDebugSnapshot() : null;
|
|
50115
|
-
if (!snap?.screen) return { success: false, error: "no live screen for session" };
|
|
50116
|
-
let sectionsDef;
|
|
50117
|
-
try {
|
|
50118
|
-
const fsm2 = await import("fs");
|
|
50119
|
-
if (snap.specPath) {
|
|
50120
|
-
const raw = JSON.parse(fsm2.readFileSync(snap.specPath, "utf8"));
|
|
50121
|
-
sectionsDef = raw?.sections;
|
|
50122
|
-
}
|
|
50123
|
-
} catch {
|
|
50124
|
-
}
|
|
50125
|
-
const { evaluateConditionPreview: evaluateConditionPreview2 } = await Promise.resolve().then(() => (init_fsm_evaluator(), fsm_evaluator_exports));
|
|
50126
|
-
try {
|
|
50127
|
-
const result = evaluateConditionPreview2(
|
|
50128
|
-
args.condition,
|
|
50129
|
-
sectionsDef,
|
|
50130
|
-
snap.screen,
|
|
50131
|
-
snap.cursorPosition ?? void 0
|
|
50132
|
-
);
|
|
50133
|
-
return { success: true, result, sections: snap.sections ?? null };
|
|
50134
|
-
} catch (e) {
|
|
50135
|
-
return { success: false, error: `eval failed: ${e.message}` };
|
|
50136
|
-
}
|
|
50137
|
-
}
|
|
50138
|
-
// ── Resolve a sections map against a live session's screen — the
|
|
50139
|
-
// section editor's "test" button. Returns, for each section id,
|
|
50140
|
-
// the line range + the text it captures, so the author can SEE
|
|
50141
|
-
// whether a from_top/until/anchor definition carves the screen
|
|
50142
|
-
// the way they intend. Accepts an in-progress sections map so it
|
|
50143
|
-
// previews unsaved edits.
|
|
50144
|
-
case "resolve_section_preview": {
|
|
50145
|
-
const sessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : typeof args?.sessionId === "string" ? args.sessionId.trim() : "";
|
|
50146
|
-
if (!sessionId) return { success: false, error: "targetSessionId required" };
|
|
50147
|
-
if (!args?.sections || typeof args.sections !== "object") return { success: false, error: "sections map required" };
|
|
50148
|
-
const target = this.deps.sessionRegistry.get(sessionId);
|
|
50149
|
-
if (!target) return { success: false, error: "Session not found", sessionId };
|
|
50150
|
-
const adapterObj = this.deps.cliManager.findAdapter(target.providerType, { instanceKey: sessionId })?.adapter;
|
|
50151
|
-
const snap = adapterObj && typeof adapterObj.getDebugSnapshot === "function" ? adapterObj.getDebugSnapshot() : null;
|
|
50152
|
-
if (!snap?.screen) return { success: false, error: "no live screen for session" };
|
|
50153
|
-
const { resolveSections: resolveSections2 } = await Promise.resolve().then(() => (init_evaluator(), evaluator_exports));
|
|
50154
|
-
try {
|
|
50155
|
-
const lines = String(snap.screen).split("\n").map((l) => l.endsWith("\r") ? l.slice(0, -1) : l);
|
|
50156
|
-
const resolved = resolveSections2(args.sections, lines);
|
|
50157
|
-
return {
|
|
50158
|
-
success: true,
|
|
50159
|
-
screenLineCount: lines.length,
|
|
50160
|
-
sections: resolved.map((s2) => ({ id: s2.id, fromLine: s2.fromLine, toLine: s2.toLine, text: s2.text }))
|
|
50161
|
-
};
|
|
50162
|
-
} catch (e) {
|
|
50163
|
-
return { success: false, error: `resolve failed: ${e.message}` };
|
|
50164
|
-
}
|
|
50165
|
-
}
|
|
50166
|
-
// ── User-level coordinator-prompt files (~/.adhdev/coordinator-prompts/).
|
|
50167
|
-
// These live on this daemon's filesystem and never sync to the
|
|
50168
|
-
// cloud / other daemons — they're per-machine config. The
|
|
50169
|
-
// Settings page in the dashboard reads/writes via these two
|
|
50170
|
-
// commands instead of going through fs from the browser.
|
|
50171
50304
|
case "list_coordinator_prompts": {
|
|
50172
50305
|
const fs32 = await import("fs");
|
|
50173
50306
|
const path42 = await import("path");
|
|
@@ -50888,73 +51021,6 @@ ${hintLines.join("\n")}` : "",
|
|
|
50888
51021
|
return { success: false, error: e.message };
|
|
50889
51022
|
}
|
|
50890
51023
|
}
|
|
50891
|
-
case "get_mesh_refine_config_schema": {
|
|
50892
|
-
return {
|
|
50893
|
-
success: true,
|
|
50894
|
-
schema: MESH_REFINE_CONFIG_SCHEMA,
|
|
50895
|
-
locations: MESH_REFINE_CONFIG_LOCATIONS,
|
|
50896
|
-
worktreeBootstrap: {
|
|
50897
|
-
schema: MESH_WORKTREE_BOOTSTRAP_CONFIG_SCHEMA,
|
|
50898
|
-
locations: MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS,
|
|
50899
|
-
sourceOfTruth: "repo worktree bootstrap config",
|
|
50900
|
-
runBehavior: "When present and enabled, clone_mesh_node runs commands after submodule initialization and records status on the worktree node."
|
|
50901
|
-
},
|
|
50902
|
-
sourceOfTruth: "repo mesh/refine config",
|
|
50903
|
-
heuristicRole: "suggestions_only_not_execution_path"
|
|
50904
|
-
};
|
|
50905
|
-
}
|
|
50906
|
-
case "validate_mesh_refine_config": {
|
|
50907
|
-
const workspace = typeof args?.workspace === "string" ? args.workspace : process.cwd();
|
|
50908
|
-
const mesh = args?.inlineMesh || {};
|
|
50909
|
-
const loaded = args?.config !== void 0 ? { config: args.config, source: "inline", sourceType: "mesh_policy" } : loadMeshRefineConfig(mesh, workspace);
|
|
50910
|
-
const validation = loaded.config ? validateMeshRefineConfig(loaded.config, loaded.source) : { valid: false, errors: [loaded.error || "repo mesh/refine config unavailable"], commands: [], rejectedCommands: [] };
|
|
50911
|
-
return { success: validation.valid, ...loaded, ...validation };
|
|
50912
|
-
}
|
|
50913
|
-
case "suggest_mesh_refine_config": {
|
|
50914
|
-
const workspace = typeof args?.workspace === "string" ? args.workspace : process.cwd();
|
|
50915
|
-
const mesh = args?.inlineMesh || {};
|
|
50916
|
-
return {
|
|
50917
|
-
success: true,
|
|
50918
|
-
...suggestMeshRefineConfig(mesh, workspace),
|
|
50919
|
-
note: "Suggestions are heuristic scaffold only; Refinery will not execute them until saved into repo mesh/refine config."
|
|
50920
|
-
};
|
|
50921
|
-
}
|
|
50922
|
-
case "get_mesh_change_impact_config_schema": {
|
|
50923
|
-
return {
|
|
50924
|
-
success: true,
|
|
50925
|
-
schema: CHANGE_IMPACT_CONFIG_SCHEMA,
|
|
50926
|
-
locations: CHANGE_IMPACT_CONFIG_LOCATIONS,
|
|
50927
|
-
sourceOfTruth: "repo change-impact config",
|
|
50928
|
-
heuristicRole: "suggestions_only_not_execution_path",
|
|
50929
|
-
note: "Declarative config only \u2014 JSON/YAML are parsed but never executed. Defines which package/file changes require a daemon rebuild/restart vs. a web-only redeploy vs. nothing."
|
|
50930
|
-
};
|
|
50931
|
-
}
|
|
50932
|
-
case "validate_mesh_change_impact_config": {
|
|
50933
|
-
const workspace = typeof args?.workspace === "string" ? args.workspace : process.cwd();
|
|
50934
|
-
if (args?.config !== void 0) {
|
|
50935
|
-
const validation = validateChangeImpactConfig(args.config, "inline");
|
|
50936
|
-
return { success: validation.valid, source: "inline", sourceType: "mesh_policy", ...validation };
|
|
50937
|
-
}
|
|
50938
|
-
const loaded = loadChangeImpactConfig(workspace);
|
|
50939
|
-
if (loaded.sourceType === "repo_file") {
|
|
50940
|
-
const validation = validateChangeImpactConfig(loaded.config, loaded.source);
|
|
50941
|
-
return { success: validation.valid, ...loaded, ...validation };
|
|
50942
|
-
}
|
|
50943
|
-
return {
|
|
50944
|
-
success: false,
|
|
50945
|
-
...loaded,
|
|
50946
|
-
valid: false,
|
|
50947
|
-
errors: [loaded.error || "repo change-impact config unavailable"]
|
|
50948
|
-
};
|
|
50949
|
-
}
|
|
50950
|
-
case "suggest_mesh_change_impact_config": {
|
|
50951
|
-
const workspace = typeof args?.workspace === "string" ? args.workspace : process.cwd();
|
|
50952
|
-
return {
|
|
50953
|
-
success: true,
|
|
50954
|
-
...suggestChangeImpactConfig(workspace),
|
|
50955
|
-
note: "Suggestions are heuristic scaffold only; the draft must be reviewed and saved into repo change-impact config before it takes effect. Nothing is executed."
|
|
50956
|
-
};
|
|
50957
|
-
}
|
|
50958
51024
|
case "mesh_init": {
|
|
50959
51025
|
const workspace = typeof args?.workspace === "string" && args.workspace.trim() ? args.workspace.trim() : process.cwd();
|
|
50960
51026
|
const mesh = args?.inlineMesh || {};
|
|
@@ -52157,7 +52223,7 @@ ${ptyResult.output.slice(-2e3)}`);
|
|
|
52157
52223
|
const isSelfNode = Boolean(
|
|
52158
52224
|
nodeId && inlineCoordinatorNodeId && nodeId === inlineCoordinatorNodeId
|
|
52159
52225
|
) || Boolean(
|
|
52160
|
-
daemonId && (daemonId
|
|
52226
|
+
daemonId && (daemonIdsEquivalent(daemonId, localMachineId) || daemonIdsEquivalent(daemonId, this.deps.statusInstanceId))
|
|
52161
52227
|
) || Boolean(meshRecord?.inline && nodeIndex === 0) || sparseConfiguredCoordinatorNode;
|
|
52162
52228
|
const machineIdentity = buildMeshNodeMachineIdentity(node, {
|
|
52163
52229
|
localMachineId,
|