@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.js
CHANGED
|
@@ -316,10 +316,10 @@ function readInjected(value) {
|
|
|
316
316
|
}
|
|
317
317
|
function getDaemonBuildInfo() {
|
|
318
318
|
if (cached) return cached;
|
|
319
|
-
const commit = readInjected(true ? "
|
|
320
|
-
const commitShort = readInjected(true ? "
|
|
321
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
322
|
-
const builtAt = readInjected(true ? "2026-06-
|
|
319
|
+
const commit = readInjected(true ? "4c8bb331f13862335f134d8a1b97fa17eaca01e0" : void 0) ?? "unknown";
|
|
320
|
+
const commitShort = readInjected(true ? "4c8bb331" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
321
|
+
const version = readInjected(true ? "0.9.82-rc.361" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
322
|
+
const builtAt = readInjected(true ? "2026-06-23T11:56:37.654Z" : void 0);
|
|
323
323
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
324
324
|
return cached;
|
|
325
325
|
}
|
|
@@ -14164,7 +14164,7 @@ function forwardUnresolvedDelegateEvent(components, routing, event) {
|
|
|
14164
14164
|
}
|
|
14165
14165
|
function ackUnresolvedDelegateForwardByFingerprint(coordinatorDaemonId, eventName, payload) {
|
|
14166
14166
|
const match = peekUnresolvedDelegateForwards().find(
|
|
14167
|
-
(entry) => entry.coordinatorDaemonId
|
|
14167
|
+
(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)
|
|
14168
14168
|
);
|
|
14169
14169
|
if (match) ackUnresolvedDelegateForward(match.id);
|
|
14170
14170
|
}
|
|
@@ -15825,1011 +15825,1601 @@ var init_external_sources = __esm({
|
|
|
15825
15825
|
}
|
|
15826
15826
|
});
|
|
15827
15827
|
|
|
15828
|
-
// src/
|
|
15829
|
-
|
|
15830
|
-
|
|
15831
|
-
NodePtyTransportFactory: () => NodePtyTransportFactory
|
|
15832
|
-
});
|
|
15833
|
-
function loadNodePty() {
|
|
15834
|
-
if (cachedPty !== void 0) return cachedPty;
|
|
15835
|
-
try {
|
|
15836
|
-
cachedPty = require("node-pty");
|
|
15837
|
-
(0, import_session_host_core2.ensureNodePtySpawnHelperPermissions)();
|
|
15838
|
-
} catch {
|
|
15839
|
-
cachedPty = null;
|
|
15840
|
-
}
|
|
15841
|
-
return cachedPty;
|
|
15828
|
+
// src/providers/spec/fsm-types.ts
|
|
15829
|
+
function isV4Spec(raw) {
|
|
15830
|
+
return !!raw && typeof raw === "object" && raw.$schema === "adhdev:cli/spec@4";
|
|
15842
15831
|
}
|
|
15843
|
-
|
|
15844
|
-
|
|
15845
|
-
"src/cli-adapters/pty-transport.ts"() {
|
|
15846
|
-
"use strict";
|
|
15847
|
-
os13 = __toESM(require("os"));
|
|
15848
|
-
init_spawn_env();
|
|
15849
|
-
init_resolve_executable();
|
|
15850
|
-
NodePtyRuntimeTransport = class {
|
|
15851
|
-
constructor(handle) {
|
|
15852
|
-
this.handle = handle;
|
|
15853
|
-
}
|
|
15854
|
-
ready = Promise.resolve();
|
|
15855
|
-
terminalQueriesHandled = false;
|
|
15856
|
-
get pid() {
|
|
15857
|
-
return this.handle.pid;
|
|
15858
|
-
}
|
|
15859
|
-
write(data) {
|
|
15860
|
-
this.handle.write(data);
|
|
15861
|
-
}
|
|
15862
|
-
resize(cols, rows) {
|
|
15863
|
-
this.handle.resize(cols, rows);
|
|
15864
|
-
}
|
|
15865
|
-
kill() {
|
|
15866
|
-
this.handle.kill();
|
|
15867
|
-
}
|
|
15868
|
-
getMetadata() {
|
|
15869
|
-
return null;
|
|
15870
|
-
}
|
|
15871
|
-
onData(callback) {
|
|
15872
|
-
this.handle.onData(callback);
|
|
15873
|
-
}
|
|
15874
|
-
onExit(callback) {
|
|
15875
|
-
this.handle.onExit(callback);
|
|
15876
|
-
}
|
|
15877
|
-
};
|
|
15878
|
-
NodePtyTransportFactory = class {
|
|
15879
|
-
spawn(command, args, options) {
|
|
15880
|
-
const pty = loadNodePty();
|
|
15881
|
-
if (!pty) throw new Error("node-pty is not installed");
|
|
15882
|
-
let cwd = options.cwd;
|
|
15883
|
-
if (cwd) {
|
|
15884
|
-
try {
|
|
15885
|
-
const fs32 = require("fs");
|
|
15886
|
-
const stat2 = fs32.statSync(cwd);
|
|
15887
|
-
if (!stat2.isDirectory()) cwd = os13.homedir();
|
|
15888
|
-
} catch {
|
|
15889
|
-
cwd = os13.homedir();
|
|
15890
|
-
}
|
|
15891
|
-
}
|
|
15892
|
-
const handle = pty.spawn(resolveWin32Executable(command), args, {
|
|
15893
|
-
name: "xterm-256color",
|
|
15894
|
-
cols: options.cols,
|
|
15895
|
-
rows: options.rows,
|
|
15896
|
-
cwd,
|
|
15897
|
-
env: options.env
|
|
15898
|
-
});
|
|
15899
|
-
return new NodePtyRuntimeTransport(handle);
|
|
15900
|
-
}
|
|
15901
|
-
};
|
|
15902
|
-
}
|
|
15903
|
-
});
|
|
15904
|
-
|
|
15905
|
-
// src/providers/sdk/v1/builders/cli/visible-region.ts
|
|
15906
|
-
function compile(re, flags) {
|
|
15907
|
-
try {
|
|
15908
|
-
return new RegExp(re, flags ?? "");
|
|
15909
|
-
} catch (e) {
|
|
15910
|
-
throw new Error(`Invalid visible-region anchor regex /${re}/${flags ?? ""}: ${e.message}`);
|
|
15911
|
-
}
|
|
15832
|
+
function initialState(spec) {
|
|
15833
|
+
return spec.states.find((s2) => s2.initial) ?? spec.states[0];
|
|
15912
15834
|
}
|
|
15913
|
-
function
|
|
15914
|
-
|
|
15915
|
-
const iter = new RegExp(re.source, re.flags.replace("g", "") + "g");
|
|
15916
|
-
let m;
|
|
15917
|
-
while ((m = iter.exec(text)) !== null) {
|
|
15918
|
-
results.push(m);
|
|
15919
|
-
if (m[0].length === 0) iter.lastIndex += 1;
|
|
15920
|
-
}
|
|
15921
|
-
return results;
|
|
15835
|
+
function stateById(spec, id) {
|
|
15836
|
+
return spec.states.find((s2) => s2.id === id);
|
|
15922
15837
|
}
|
|
15923
|
-
function
|
|
15924
|
-
|
|
15925
|
-
|
|
15926
|
-
|
|
15927
|
-
|
|
15928
|
-
|
|
15929
|
-
|
|
15930
|
-
const limit = spec.tailChars ?? 4e3;
|
|
15931
|
-
if (text.length <= limit) return text;
|
|
15932
|
-
return text.slice(-limit);
|
|
15933
|
-
}
|
|
15934
|
-
case "between-anchors": {
|
|
15935
|
-
const anchors = spec.anchors;
|
|
15936
|
-
if (!anchors?.top && !anchors?.bottom) return text;
|
|
15937
|
-
const selectLast = (spec.selectAnchor ?? "first") === "last";
|
|
15938
|
-
let topEnd = 0;
|
|
15939
|
-
if (anchors.top) {
|
|
15940
|
-
const topRe = compile(anchors.top.pattern, anchors.top.flags);
|
|
15941
|
-
const topMatches = findAllMatches(topRe, text);
|
|
15942
|
-
if (topMatches.length === 0) {
|
|
15943
|
-
return text;
|
|
15944
|
-
}
|
|
15945
|
-
const chosen = selectLast ? topMatches[topMatches.length - 1] : topMatches[0];
|
|
15946
|
-
topEnd = chosen.index + chosen[0].length;
|
|
15947
|
-
}
|
|
15948
|
-
let bottomStart = text.length;
|
|
15949
|
-
if (anchors.bottom) {
|
|
15950
|
-
const bottomRe = compile(anchors.bottom.pattern, anchors.bottom.flags);
|
|
15951
|
-
const searchFrom = topEnd;
|
|
15952
|
-
const suffix = text.slice(searchFrom);
|
|
15953
|
-
const bottomMatches = findAllMatches(bottomRe, suffix);
|
|
15954
|
-
if (bottomMatches.length === 0) {
|
|
15955
|
-
return text;
|
|
15956
|
-
}
|
|
15957
|
-
const chosen = selectLast ? bottomMatches[bottomMatches.length - 1] : bottomMatches[0];
|
|
15958
|
-
bottomStart = searchFrom + chosen.index;
|
|
15959
|
-
}
|
|
15960
|
-
if (topEnd >= bottomStart) {
|
|
15961
|
-
return text;
|
|
15962
|
-
}
|
|
15963
|
-
return text.slice(topEnd, bottomStart);
|
|
15964
|
-
}
|
|
15965
|
-
default:
|
|
15966
|
-
return text;
|
|
15967
|
-
}
|
|
15838
|
+
function outgoingTransitions(spec, stateId) {
|
|
15839
|
+
const matches = spec.transitions.filter((t) => {
|
|
15840
|
+
if (t.from === "*") return true;
|
|
15841
|
+
if (Array.isArray(t.from)) return t.from.includes(stateId);
|
|
15842
|
+
return t.from === stateId;
|
|
15843
|
+
});
|
|
15844
|
+
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);
|
|
15968
15845
|
}
|
|
15969
|
-
|
|
15970
|
-
|
|
15846
|
+
function statusForState(state) {
|
|
15847
|
+
if (state.status) return state.status;
|
|
15848
|
+
if (state.modal) return "approval";
|
|
15849
|
+
if (state.id === "busy" || state.id === "generating") return "generating";
|
|
15850
|
+
return "idle";
|
|
15851
|
+
}
|
|
15852
|
+
function modalKindForState(state) {
|
|
15853
|
+
if (state.modal_kind) return state.modal_kind;
|
|
15854
|
+
if (state.modal) return "approval";
|
|
15855
|
+
return null;
|
|
15856
|
+
}
|
|
15857
|
+
var init_fsm_types = __esm({
|
|
15858
|
+
"src/providers/spec/fsm-types.ts"() {
|
|
15971
15859
|
"use strict";
|
|
15972
15860
|
}
|
|
15973
15861
|
});
|
|
15974
15862
|
|
|
15975
|
-
// src/providers/
|
|
15976
|
-
|
|
15863
|
+
// src/providers/spec/fsm-loader.ts
|
|
15864
|
+
var fsm_loader_exports = {};
|
|
15865
|
+
__export(fsm_loader_exports, {
|
|
15866
|
+
loadFsmSpec: () => loadFsmSpec,
|
|
15867
|
+
validateFsmSpec: () => validateFsmSpec
|
|
15868
|
+
});
|
|
15869
|
+
function loadFsmSpec(sourcePath) {
|
|
15870
|
+
let raw;
|
|
15977
15871
|
try {
|
|
15978
|
-
|
|
15979
|
-
} catch (
|
|
15980
|
-
|
|
15872
|
+
raw = JSON.parse(fs10.readFileSync(sourcePath, "utf8"));
|
|
15873
|
+
} catch (err) {
|
|
15874
|
+
return { ok: false, errors: [`Failed to read/parse spec: ${err.message}`], sourcePath };
|
|
15981
15875
|
}
|
|
15876
|
+
const errors = validateFsmSpec(raw);
|
|
15877
|
+
if (errors.length) return { ok: false, errors, sourcePath };
|
|
15878
|
+
return { ok: true, spec: raw, sourcePath };
|
|
15982
15879
|
}
|
|
15983
|
-
function
|
|
15984
|
-
|
|
15985
|
-
|
|
15986
|
-
|
|
15987
|
-
|
|
15988
|
-
|
|
15989
|
-
|
|
15990
|
-
|
|
15991
|
-
|
|
15992
|
-
|
|
15993
|
-
|
|
15994
|
-
|
|
15995
|
-
|
|
15996
|
-
|
|
15997
|
-
|
|
15998
|
-
case void 0:
|
|
15999
|
-
default:
|
|
16000
|
-
return takeTail(input.screenText ?? "", lines);
|
|
15880
|
+
function validateFsmSpec(raw) {
|
|
15881
|
+
const errs = [];
|
|
15882
|
+
if (!isV4Spec(raw)) return ['$schema must be "adhdev:cli/spec@4"'];
|
|
15883
|
+
const spec = raw;
|
|
15884
|
+
if (!spec.id) errs.push("id is required");
|
|
15885
|
+
if (!spec.binary) errs.push("binary is required");
|
|
15886
|
+
if (!spec.send_message?.submit_key) errs.push("send_message.submit_key is required");
|
|
15887
|
+
if (spec.pre_launch_trust !== void 0) {
|
|
15888
|
+
const t = spec.pre_launch_trust;
|
|
15889
|
+
if (!t || typeof t !== "object" || Array.isArray(t)) {
|
|
15890
|
+
errs.push("pre_launch_trust must be an object");
|
|
15891
|
+
} else {
|
|
15892
|
+
if (typeof t.settings_path !== "string" || !t.settings_path) errs.push("pre_launch_trust.settings_path is required");
|
|
15893
|
+
if (typeof t.key !== "string" || !t.key) errs.push("pre_launch_trust.key is required");
|
|
15894
|
+
}
|
|
16001
15895
|
}
|
|
16002
|
-
|
|
16003
|
-
|
|
16004
|
-
|
|
16005
|
-
}
|
|
16006
|
-
|
|
16007
|
-
const prompt = compile2(spec.regex, spec.flags ?? "m");
|
|
16008
|
-
const footers = (spec.withFooter ?? []).map((f) => {
|
|
16009
|
-
if (f.kind === "regex") {
|
|
16010
|
-
const re = compile2(f.pattern, f.flags ?? "i");
|
|
16011
|
-
return { test: (s2) => re.test(s2) };
|
|
15896
|
+
if (spec.refocus_when_stalled_ms !== void 0) {
|
|
15897
|
+
if (typeof spec.refocus_when_stalled_ms !== "number" || !(spec.refocus_when_stalled_ms > 0)) {
|
|
15898
|
+
errs.push("refocus_when_stalled_ms must be a positive number");
|
|
15899
|
+
} else if (!Array.isArray(spec.send_on_spawn) || spec.send_on_spawn.length === 0) {
|
|
15900
|
+
errs.push("refocus_when_stalled_ms requires send_on_spawn (the wake sequence to re-inject)");
|
|
16012
15901
|
}
|
|
16013
|
-
const needle = f.pattern.toLowerCase();
|
|
16014
|
-
return { test: (s2) => s2.toLowerCase().includes(needle) };
|
|
16015
|
-
});
|
|
16016
|
-
return { prompt, footers };
|
|
16017
|
-
}
|
|
16018
|
-
function extractButtonLabels(spec, text) {
|
|
16019
|
-
if (!text) return [];
|
|
16020
|
-
const flags = spec.buttonFlags && spec.buttonFlags.includes("m") ? spec.buttonFlags : `${spec.buttonFlags ?? ""}m`;
|
|
16021
|
-
const buttonRe = compile2(spec.buttonPattern, flags);
|
|
16022
|
-
const labelGroup = Number.isInteger(spec.buttonLabelGroup) && (spec.buttonLabelGroup ?? 0) > 0 ? spec.buttonLabelGroup : 1;
|
|
16023
|
-
const out = [];
|
|
16024
|
-
for (const line of text.split("\n")) {
|
|
16025
|
-
buttonRe.lastIndex = 0;
|
|
16026
|
-
const m = buttonRe.exec(line);
|
|
16027
|
-
if (!m) continue;
|
|
16028
|
-
const captured = m[labelGroup] ?? (labelGroup === 1 && m.length > 2 ? m[m.length - 1] : void 0);
|
|
16029
|
-
if (captured && captured.trim()) out.push(captured.trim());
|
|
16030
15902
|
}
|
|
16031
|
-
|
|
16032
|
-
|
|
16033
|
-
|
|
16034
|
-
const labels = extractButtonLabels(spec, text);
|
|
16035
|
-
if (labels.length < 2) return false;
|
|
16036
|
-
if (pickApprovalButton(labels).index < 0) return false;
|
|
16037
|
-
return hasNegativeApprovalOption(labels);
|
|
16038
|
-
}
|
|
16039
|
-
function modalMatches(spec, input) {
|
|
16040
|
-
const text = input.screenText ?? "";
|
|
16041
|
-
const question = compile2(spec.questionPattern, spec.questionFlags ?? "i");
|
|
16042
|
-
if (question.test(text)) return true;
|
|
16043
|
-
for (const variant of spec.questionVariants ?? []) {
|
|
16044
|
-
const re = compile2(variant.regex, variant.flags ?? "i");
|
|
16045
|
-
if (re.test(text)) return true;
|
|
15903
|
+
if (!Array.isArray(spec.states) || spec.states.length === 0) {
|
|
15904
|
+
errs.push("states[] must be a non-empty array");
|
|
15905
|
+
return errs;
|
|
16046
15906
|
}
|
|
16047
|
-
if (
|
|
16048
|
-
|
|
16049
|
-
|
|
16050
|
-
|
|
16051
|
-
|
|
16052
|
-
|
|
16053
|
-
|
|
16054
|
-
|
|
16055
|
-
|
|
16056
|
-
|
|
16057
|
-
case "modal": {
|
|
16058
|
-
if (!spec.modal) return null;
|
|
16059
|
-
return modalMatches(spec.modal, input) ? "waiting_approval" : null;
|
|
15907
|
+
if (!Array.isArray(spec.transitions)) {
|
|
15908
|
+
errs.push("transitions[] must be an array");
|
|
15909
|
+
return errs;
|
|
15910
|
+
}
|
|
15911
|
+
const ids = /* @__PURE__ */ new Set();
|
|
15912
|
+
let initialCount = 0;
|
|
15913
|
+
for (const [i, s2] of spec.states.entries()) {
|
|
15914
|
+
if (!s2.id) {
|
|
15915
|
+
errs.push(`states[${i}].id is required`);
|
|
15916
|
+
continue;
|
|
16060
15917
|
}
|
|
16061
|
-
|
|
16062
|
-
|
|
16063
|
-
|
|
16064
|
-
|
|
16065
|
-
|
|
16066
|
-
|
|
15918
|
+
if (ids.has(s2.id)) errs.push(`states[${i}].id "${s2.id}" is duplicated`);
|
|
15919
|
+
ids.add(s2.id);
|
|
15920
|
+
if (!s2.label) errs.push(`states[${i}].label is required`);
|
|
15921
|
+
if (s2.initial) initialCount += 1;
|
|
15922
|
+
if (s2.status && !["idle", "generating", "approval"].includes(s2.status)) {
|
|
15923
|
+
errs.push(`states[${i}].status "${s2.status}" must be idle|generating|approval`);
|
|
16067
15924
|
}
|
|
16068
|
-
// Groups declared in the catalog but not yet implemented as builder steps
|
|
16069
|
-
// return null so they are no-ops in dispatch — declaring them in `order`
|
|
16070
|
-
// is forward-compatible. Phase 2 Week 8+ will wire them.
|
|
16071
|
-
case "cue-ordering":
|
|
16072
|
-
case "error-detection":
|
|
16073
|
-
case "approval-stitching":
|
|
16074
|
-
return null;
|
|
16075
|
-
default:
|
|
16076
|
-
return null;
|
|
16077
15925
|
}
|
|
16078
|
-
|
|
16079
|
-
|
|
16080
|
-
const
|
|
16081
|
-
const
|
|
16082
|
-
|
|
16083
|
-
|
|
16084
|
-
|
|
16085
|
-
const effectiveInput = spec.visibleRegion ? {
|
|
16086
|
-
...input,
|
|
16087
|
-
screenText: applyVisibleRegion(spec.visibleRegion, input.screenText ?? ""),
|
|
16088
|
-
tail: applyVisibleRegion(spec.visibleRegion, input.tail)
|
|
16089
|
-
} : input;
|
|
16090
|
-
for (const group of order) {
|
|
16091
|
-
const verdict = evaluateGroup(group, spec, effectiveInput, compiled);
|
|
16092
|
-
if (verdict !== null) return verdict;
|
|
15926
|
+
if (initialCount === 0) errs.push("exactly one state must have initial:true (none found)");
|
|
15927
|
+
if (initialCount > 1) errs.push(`exactly one state must have initial:true (${initialCount} found)`);
|
|
15928
|
+
const sectionIds = new Set(Object.keys(spec.sections ?? {}));
|
|
15929
|
+
for (const [i, t] of spec.transitions.entries()) {
|
|
15930
|
+
const froms = t.from === "*" ? [] : Array.isArray(t.from) ? t.from : [t.from];
|
|
15931
|
+
for (const f of froms) {
|
|
15932
|
+
if (!ids.has(f)) errs.push(`transitions[${i}].from references unknown state "${f}"`);
|
|
16093
15933
|
}
|
|
16094
|
-
|
|
16095
|
-
|
|
16096
|
-
}
|
|
16097
|
-
|
|
16098
|
-
var init_detect_status = __esm({
|
|
16099
|
-
"src/providers/sdk/v1/builders/cli/detect-status.ts"() {
|
|
16100
|
-
"use strict";
|
|
16101
|
-
init_visible_region();
|
|
16102
|
-
init_approval_utils();
|
|
16103
|
-
DEFAULT_ORDER = ["spinner", "modal", "settled-prompt"];
|
|
15934
|
+
if (t.from !== "*" && froms.length === 0) errs.push(`transitions[${i}].from is required`);
|
|
15935
|
+
if (!t.to) errs.push(`transitions[${i}].to is required`);
|
|
15936
|
+
else if (!ids.has(t.to)) errs.push(`transitions[${i}].to references unknown state "${t.to}"`);
|
|
15937
|
+
if (t.when) errs.push(...validateCondition(t.when, sectionIds, `transitions[${i}].when`));
|
|
16104
15938
|
}
|
|
16105
|
-
|
|
16106
|
-
|
|
16107
|
-
|
|
16108
|
-
|
|
16109
|
-
|
|
16110
|
-
return new RegExp(re, flags);
|
|
16111
|
-
} catch (e) {
|
|
16112
|
-
throw new Error(`Invalid regex /${re}/${flags ?? ""}: ${e.message}`);
|
|
15939
|
+
for (const [i, s2] of spec.states.entries()) {
|
|
15940
|
+
const sec = s2.extract?.title?.section;
|
|
15941
|
+
if (sec && !sectionIds.has(sec)) errs.push(`states[${i}].extract.title.section "${sec}" unknown`);
|
|
15942
|
+
const bsec = s2.extract?.buttons?.section;
|
|
15943
|
+
if (bsec && !sectionIds.has(bsec)) errs.push(`states[${i}].extract.buttons.section "${bsec}" unknown`);
|
|
16113
15944
|
}
|
|
15945
|
+
return errs;
|
|
16114
15946
|
}
|
|
16115
|
-
function
|
|
16116
|
-
const
|
|
16117
|
-
|
|
16118
|
-
|
|
16119
|
-
|
|
16120
|
-
|
|
16121
|
-
const re = compile3(variant.regex, variant.flags ?? "i");
|
|
16122
|
-
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
16123
|
-
if (re.test(lines[i])) return { index: i, matchedSource: variant.label ?? "variant" };
|
|
16124
|
-
}
|
|
15947
|
+
function validateCondition(c, sectionIds, path42) {
|
|
15948
|
+
const errs = [];
|
|
15949
|
+
const w = c;
|
|
15950
|
+
if ("all" in w) {
|
|
15951
|
+
w.all.forEach((x, i) => errs.push(...validateCondition(x, sectionIds, `${path42}.all[${i}]`)));
|
|
15952
|
+
return errs;
|
|
16125
15953
|
}
|
|
16126
|
-
|
|
16127
|
-
}
|
|
16128
|
-
|
|
16129
|
-
const scope = spec.scope ?? "between-last-two-separators";
|
|
16130
|
-
if (scope === "whole-screen") {
|
|
16131
|
-
return { start: 0, end: lines.length };
|
|
15954
|
+
if ("any" in w) {
|
|
15955
|
+
w.any.forEach((x, i) => errs.push(...validateCondition(x, sectionIds, `${path42}.any[${i}]`)));
|
|
15956
|
+
return errs;
|
|
16132
15957
|
}
|
|
16133
|
-
if (
|
|
16134
|
-
|
|
16135
|
-
return
|
|
16136
|
-
start: Math.max(0, questionIndex - 2),
|
|
16137
|
-
end: Math.min(lines.length, questionIndex + window)
|
|
16138
|
-
};
|
|
15958
|
+
if ("not" in w) {
|
|
15959
|
+
errs.push(...validateCondition(w.not, sectionIds, `${path42}.not`));
|
|
15960
|
+
return errs;
|
|
16139
15961
|
}
|
|
16140
|
-
|
|
16141
|
-
|
|
16142
|
-
|
|
16143
|
-
|
|
16144
|
-
|
|
16145
|
-
|
|
16146
|
-
prevSep = i;
|
|
16147
|
-
break;
|
|
16148
|
-
}
|
|
15962
|
+
if ("matches" in w) {
|
|
15963
|
+
if (w.section && !sectionIds.has(w.section)) errs.push(`${path42}.section "${w.section}" unknown`);
|
|
15964
|
+
try {
|
|
15965
|
+
new RegExp(w.matches, w.flags ?? "i");
|
|
15966
|
+
} catch (e) {
|
|
15967
|
+
errs.push(`${path42}.matches invalid regex: ${e.message}`);
|
|
16149
15968
|
}
|
|
15969
|
+
return errs;
|
|
16150
15970
|
}
|
|
16151
|
-
if (
|
|
16152
|
-
|
|
16153
|
-
|
|
16154
|
-
|
|
16155
|
-
start: Math.max(0, questionIndex - 4),
|
|
16156
|
-
end: Math.min(lines.length, questionIndex + 16)
|
|
16157
|
-
};
|
|
16158
|
-
}
|
|
16159
|
-
function extractButtons(spec, lines, windowStart, windowEnd) {
|
|
16160
|
-
const buttonRe = compile3(spec.buttonPattern, spec.buttonFlags ?? "m");
|
|
16161
|
-
const out = [];
|
|
16162
|
-
const labelGroup = Number.isInteger(spec.buttonLabelGroup) && (spec.buttonLabelGroup ?? 0) > 0 ? spec.buttonLabelGroup : 1;
|
|
16163
|
-
let i = windowStart;
|
|
16164
|
-
while (i < windowEnd) {
|
|
16165
|
-
const line = lines[i];
|
|
16166
|
-
const m = buttonRe.exec(line);
|
|
16167
|
-
const captured = m?.[labelGroup] ?? (labelGroup === 1 && m && m.length > 2 ? m[m.length - 1] : void 0);
|
|
16168
|
-
if (m && captured) {
|
|
16169
|
-
let label = captured.trim();
|
|
16170
|
-
if (spec.continuationLines) {
|
|
16171
|
-
let j = i + 1;
|
|
16172
|
-
while (j < windowEnd) {
|
|
16173
|
-
const next = lines[j];
|
|
16174
|
-
if (!next.trim()) break;
|
|
16175
|
-
if (buttonRe.test(next)) break;
|
|
16176
|
-
if (!/^\s+/.test(next)) break;
|
|
16177
|
-
label += " " + next.trim();
|
|
16178
|
-
j += 1;
|
|
16179
|
-
}
|
|
16180
|
-
i = j;
|
|
16181
|
-
} else {
|
|
16182
|
-
i += 1;
|
|
16183
|
-
}
|
|
16184
|
-
out.push(label);
|
|
16185
|
-
} else {
|
|
16186
|
-
i += 1;
|
|
16187
|
-
}
|
|
15971
|
+
if ("cursor_above" in w && "changed" in w) return errs;
|
|
15972
|
+
if ("elapsed_ms" in w) {
|
|
15973
|
+
if (typeof w.elapsed_ms !== "number") errs.push(`${path42}.elapsed_ms must be a number`);
|
|
15974
|
+
return errs;
|
|
16188
15975
|
}
|
|
16189
|
-
|
|
16190
|
-
}
|
|
16191
|
-
|
|
16192
|
-
const messageParts = [];
|
|
16193
|
-
if (spec.contextHeader) {
|
|
16194
|
-
const ctxRe = compile3(spec.contextHeader.regex, (spec.contextHeader.flags ?? "i") + "m");
|
|
16195
|
-
const haystack = lines.slice(windowStart, windowEnd).join("\n");
|
|
16196
|
-
const m = ctxRe.exec(haystack);
|
|
16197
|
-
if (m) messageParts.push(m[1] ? m[1].trim() : m[0].trim());
|
|
15976
|
+
if ("stable_ms" in w) {
|
|
15977
|
+
if (typeof w.stable_ms !== "number") errs.push(`${path42}.stable_ms must be a number`);
|
|
15978
|
+
return errs;
|
|
16198
15979
|
}
|
|
16199
|
-
|
|
16200
|
-
return
|
|
15980
|
+
errs.push(`${path42} is not a recognized condition`);
|
|
15981
|
+
return errs;
|
|
16201
15982
|
}
|
|
16202
|
-
|
|
16203
|
-
|
|
16204
|
-
|
|
16205
|
-
|
|
16206
|
-
|
|
16207
|
-
|
|
16208
|
-
for (let i = windowStart; i < windowEnd; i += 1) {
|
|
16209
|
-
let m;
|
|
16210
|
-
re.lastIndex = 0;
|
|
16211
|
-
while ((m = re.exec(lines[i])) !== null) {
|
|
16212
|
-
const label = (m[1] ?? m[0]).trim();
|
|
16213
|
-
if (label && !out.includes(label)) out.push(label);
|
|
16214
|
-
if (m.index === re.lastIndex) re.lastIndex += 1;
|
|
16215
|
-
}
|
|
15983
|
+
var fs10;
|
|
15984
|
+
var init_fsm_loader = __esm({
|
|
15985
|
+
"src/providers/spec/fsm-loader.ts"() {
|
|
15986
|
+
"use strict";
|
|
15987
|
+
fs10 = __toESM(require("fs"));
|
|
15988
|
+
init_fsm_types();
|
|
16216
15989
|
}
|
|
16217
|
-
|
|
15990
|
+
});
|
|
15991
|
+
|
|
15992
|
+
// src/providers/spec/evaluator.ts
|
|
15993
|
+
var evaluator_exports = {};
|
|
15994
|
+
__export(evaluator_exports, {
|
|
15995
|
+
evaluateCondition: () => evaluateCondition,
|
|
15996
|
+
extractButtonsFromRule: () => extractButtonsFromRule,
|
|
15997
|
+
extractTitle: () => extractTitle,
|
|
15998
|
+
lastContiguousNumberedBlock: () => lastContiguousNumberedBlock,
|
|
15999
|
+
resolveSections: () => resolveSections,
|
|
16000
|
+
sectionText: () => sectionText
|
|
16001
|
+
});
|
|
16002
|
+
function resolveSize(size, total) {
|
|
16003
|
+
if (size === void 0) return 0;
|
|
16004
|
+
if (typeof size === "number") return Math.max(0, Math.min(total, size));
|
|
16005
|
+
const m = /^(\d+(?:\.\d+)?)%$/.exec(size);
|
|
16006
|
+
if (!m) return 0;
|
|
16007
|
+
const pct = Number(m[1]);
|
|
16008
|
+
return Math.max(0, Math.min(total, Math.round(total * pct / 100)));
|
|
16218
16009
|
}
|
|
16219
|
-
function
|
|
16220
|
-
const
|
|
16221
|
-
|
|
16222
|
-
|
|
16223
|
-
|
|
16224
|
-
|
|
16225
|
-
|
|
16226
|
-
|
|
16227
|
-
|
|
16228
|
-
|
|
16229
|
-
|
|
16230
|
-
|
|
16231
|
-
|
|
16232
|
-
|
|
16010
|
+
function resolveSections(sectionsObj, lines) {
|
|
16011
|
+
const total = lines.length;
|
|
16012
|
+
const anchored = /* @__PURE__ */ new Map();
|
|
16013
|
+
const sectionEntries = Object.entries(sectionsObj);
|
|
16014
|
+
for (const [id, sec] of sectionEntries) {
|
|
16015
|
+
let from = 0;
|
|
16016
|
+
let to = total;
|
|
16017
|
+
if (sec.anchor !== void 0) {
|
|
16018
|
+
try {
|
|
16019
|
+
const anchorPatterns = Array.isArray(sec.anchor) ? sec.anchor : [sec.anchor];
|
|
16020
|
+
const sharedCtx = Array.isArray(sec.anchor_context) ? null : sec.anchor_context ?? null;
|
|
16021
|
+
const ctxList = Array.isArray(sec.anchor_context) ? sec.anchor_context : anchorPatterns.map(() => sharedCtx);
|
|
16022
|
+
const candidates = anchorPatterns.map((pattern, k) => {
|
|
16023
|
+
const ctx = ctxList[k] ?? null;
|
|
16024
|
+
return {
|
|
16025
|
+
re: new RegExp(pattern, sec.anchor_flags ?? ""),
|
|
16026
|
+
prevRe: ctx?.prev !== void 0 ? new RegExp(ctx.prev, ctx.prev_flags ?? "") : null,
|
|
16027
|
+
nextRe: ctx?.next !== void 0 ? new RegExp(ctx.next, ctx.next_flags ?? "") : null
|
|
16028
|
+
};
|
|
16029
|
+
});
|
|
16030
|
+
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]));
|
|
16031
|
+
let idx = -1;
|
|
16032
|
+
for (const c of candidates) {
|
|
16033
|
+
let candIdx = -1;
|
|
16034
|
+
if (sec.anchor_last) {
|
|
16035
|
+
for (let i = total - 1; i >= 0; i--) {
|
|
16036
|
+
if (matchesCandidate(c, i)) {
|
|
16037
|
+
candIdx = i;
|
|
16038
|
+
break;
|
|
16039
|
+
}
|
|
16040
|
+
}
|
|
16041
|
+
} else {
|
|
16042
|
+
for (let i = 0; i < total; i++) {
|
|
16043
|
+
if (matchesCandidate(c, i)) {
|
|
16044
|
+
candIdx = i;
|
|
16045
|
+
break;
|
|
16046
|
+
}
|
|
16047
|
+
}
|
|
16048
|
+
}
|
|
16049
|
+
if (candIdx !== -1 && (idx === -1 || candIdx < idx)) idx = candIdx;
|
|
16050
|
+
}
|
|
16051
|
+
if (idx !== -1) {
|
|
16052
|
+
from = idx;
|
|
16053
|
+
to = total;
|
|
16054
|
+
if (sec.until_regex !== void 0) {
|
|
16055
|
+
try {
|
|
16056
|
+
const ure = new RegExp(sec.until_regex, sec.until_regex_flags ?? "");
|
|
16057
|
+
const end = lines.findIndex((l, i) => i > idx && ure.test(l));
|
|
16058
|
+
if (end !== -1) to = end;
|
|
16059
|
+
} catch {
|
|
16060
|
+
}
|
|
16061
|
+
} else if (sec.lines !== void 0) {
|
|
16062
|
+
to = Math.min(total, from + sec.lines);
|
|
16063
|
+
}
|
|
16064
|
+
}
|
|
16065
|
+
} catch {
|
|
16066
|
+
}
|
|
16067
|
+
} else if (sec.from_top !== void 0) {
|
|
16068
|
+
from = resolveSize(sec.from_top, total);
|
|
16069
|
+
to = total;
|
|
16070
|
+
} else if (sec.from_bottom !== void 0) {
|
|
16071
|
+
const sz = resolveSize(sec.from_bottom, total);
|
|
16072
|
+
from = total - sz;
|
|
16073
|
+
to = total;
|
|
16233
16074
|
}
|
|
16234
|
-
|
|
16235
|
-
const message = buildMessage(spec, lines, question.index, start, end);
|
|
16236
|
-
return { message, buttons };
|
|
16237
|
-
};
|
|
16238
|
-
}
|
|
16239
|
-
var SEPARATOR_RE;
|
|
16240
|
-
var init_parse_approval = __esm({
|
|
16241
|
-
"src/providers/sdk/v1/builders/cli/parse-approval.ts"() {
|
|
16242
|
-
"use strict";
|
|
16243
|
-
init_visible_region();
|
|
16244
|
-
SEPARATOR_RE = /^(?:─|━|═|━){10,}\s*$/;
|
|
16075
|
+
anchored.set(id, { fromLine: from, toLine: to });
|
|
16245
16076
|
}
|
|
16246
|
-
|
|
16247
|
-
|
|
16248
|
-
|
|
16249
|
-
|
|
16250
|
-
|
|
16077
|
+
const resolved = [];
|
|
16078
|
+
for (const [id, sec] of sectionEntries) {
|
|
16079
|
+
let { fromLine, toLine } = anchored.get(id);
|
|
16080
|
+
if (sec.until !== void 0) {
|
|
16081
|
+
if (sec.until.startsWith("^")) {
|
|
16082
|
+
try {
|
|
16083
|
+
const ure = new RegExp(sec.until);
|
|
16084
|
+
const end = lines.findIndex((l, i) => i > fromLine && ure.test(l));
|
|
16085
|
+
if (end !== -1) toLine = end;
|
|
16086
|
+
} catch {
|
|
16087
|
+
}
|
|
16088
|
+
} else {
|
|
16089
|
+
const target = anchored.get(sec.until);
|
|
16090
|
+
if (target) toLine = target.fromLine;
|
|
16091
|
+
}
|
|
16092
|
+
}
|
|
16093
|
+
if (toLine < fromLine) toLine = fromLine;
|
|
16094
|
+
const text = lines.slice(fromLine, toLine).join("\n");
|
|
16095
|
+
resolved.push({ id, fromLine, toLine, text });
|
|
16096
|
+
}
|
|
16097
|
+
return resolved;
|
|
16251
16098
|
}
|
|
16252
|
-
function
|
|
16253
|
-
|
|
16099
|
+
function sectionText(sections, sectionId, fullScreen) {
|
|
16100
|
+
if (!sectionId) return fullScreen;
|
|
16101
|
+
const found = sections.find((s2) => s2.id === sectionId);
|
|
16102
|
+
return found ? found.text : "";
|
|
16254
16103
|
}
|
|
16255
|
-
function
|
|
16256
|
-
|
|
16257
|
-
if (scope === "screen") return String(input.screenText || input.screen?.text || "");
|
|
16258
|
-
if (scope === "tail") return String(input.tail || input.recentBuffer || input.buffer || "");
|
|
16259
|
-
return String(input.buffer || input.rawBuffer || input.screenText || "");
|
|
16104
|
+
function isRegexCondition(c) {
|
|
16105
|
+
return "matches" in c;
|
|
16260
16106
|
}
|
|
16261
|
-
function
|
|
16262
|
-
return
|
|
16107
|
+
function isChangedCondition(c) {
|
|
16108
|
+
return "cursor_above" in c && "changed" in c;
|
|
16263
16109
|
}
|
|
16264
|
-
function
|
|
16265
|
-
|
|
16266
|
-
let turnIndex = -1;
|
|
16267
|
-
return list.map((message, index) => {
|
|
16268
|
-
const role = message?.role || "assistant";
|
|
16269
|
-
const kind = message?.kind || "standard";
|
|
16270
|
-
const content = typeof message?.content === "string" ? message.content : "";
|
|
16271
|
-
if (role === "user" || turnIndex < 0) turnIndex += 1;
|
|
16272
|
-
const seed = [role, kind, "", index, content].join("\n");
|
|
16273
|
-
const providerUnitKey = `v2-pty:${role}:${kind}:${index}:${stableHash(seed)}`;
|
|
16274
|
-
const bubbleId = `bubble:${providerUnitKey}`;
|
|
16275
|
-
const turnKey = `turn:${turnIndex}`;
|
|
16276
|
-
const isStreamingTail = status === "generating" && role === "assistant" && index === list.length - 1;
|
|
16277
|
-
return {
|
|
16278
|
-
...message,
|
|
16279
|
-
providerUnitKey,
|
|
16280
|
-
bubbleId,
|
|
16281
|
-
sequence: index,
|
|
16282
|
-
_turnKey: turnKey,
|
|
16283
|
-
bubbleState: isStreamingTail ? "streaming" : "final"
|
|
16284
|
-
};
|
|
16285
|
-
});
|
|
16110
|
+
function isAllCondition(c) {
|
|
16111
|
+
return "all" in c;
|
|
16286
16112
|
}
|
|
16287
|
-
function
|
|
16288
|
-
|
|
16289
|
-
|
|
16113
|
+
function isAnyCondition(c) {
|
|
16114
|
+
return "any" in c;
|
|
16115
|
+
}
|
|
16116
|
+
function evaluateCondition(cond, sections, fullScreen, cursor, prevLines, trace, stateId) {
|
|
16117
|
+
if (isAllCondition(cond)) {
|
|
16118
|
+
for (const child of cond.all) {
|
|
16119
|
+
if (!evaluateCondition(child, sections, fullScreen, cursor, prevLines, trace, stateId)) {
|
|
16120
|
+
return false;
|
|
16121
|
+
}
|
|
16122
|
+
}
|
|
16123
|
+
return true;
|
|
16290
16124
|
}
|
|
16291
|
-
|
|
16292
|
-
|
|
16293
|
-
|
|
16294
|
-
|
|
16295
|
-
const chromeRes = (spec.transcriptPty.chromePatterns || []).map((p) => new RegExp(p.regex, p.flags || ""));
|
|
16296
|
-
const requireIndentForContinuation = spec.transcriptPty.continuationLine?.indented ?? false;
|
|
16297
|
-
const stripLeadingChrome = spec.transcriptPty.stripLeadingChrome ?? true;
|
|
16298
|
-
const scope = spec.transcriptPty.scope ?? "buffer";
|
|
16299
|
-
const detectStatus = spec.spinner || spec.settledPrompt || spec.modal || spec.dispatchOrder ? buildDetectStatusFromTui({
|
|
16300
|
-
spinner: spec.spinner,
|
|
16301
|
-
settledPrompt: spec.settledPrompt,
|
|
16302
|
-
modal: spec.modal,
|
|
16303
|
-
dispatchOrder: spec.dispatchOrder
|
|
16304
|
-
}) : () => null;
|
|
16305
|
-
const parseApproval = spec.modal ? buildParseApprovalFromTui(spec.modal) : () => null;
|
|
16306
|
-
const sessionIdRe = spec.sessionIdExtraction ? new RegExp(
|
|
16307
|
-
spec.sessionIdExtraction.regex,
|
|
16308
|
-
spec.sessionIdExtraction.flags ?? "i"
|
|
16309
|
-
) : null;
|
|
16310
|
-
const sessionIdScope = spec.sessionIdExtraction?.scope ?? "tail";
|
|
16311
|
-
return function parseSession(input) {
|
|
16312
|
-
const status = detectStatus(input) ?? "idle";
|
|
16313
|
-
const modal = parseApproval(input);
|
|
16314
|
-
const text = pickInputText(input, scope);
|
|
16315
|
-
const lines = splitLines(text);
|
|
16316
|
-
const messages = [];
|
|
16317
|
-
let seenFirstRoleLine = !stripLeadingChrome;
|
|
16318
|
-
for (const raw of lines) {
|
|
16319
|
-
const line = raw;
|
|
16320
|
-
if (line.trim() === "") {
|
|
16321
|
-
continue;
|
|
16125
|
+
if (isAnyCondition(cond)) {
|
|
16126
|
+
for (const child of cond.any) {
|
|
16127
|
+
if (evaluateCondition(child, sections, fullScreen, cursor, prevLines, trace, stateId)) {
|
|
16128
|
+
return true;
|
|
16322
16129
|
}
|
|
16323
|
-
|
|
16324
|
-
|
|
16325
|
-
|
|
16326
|
-
|
|
16327
|
-
|
|
16328
|
-
|
|
16130
|
+
}
|
|
16131
|
+
return false;
|
|
16132
|
+
}
|
|
16133
|
+
if (isChangedCondition(cond)) {
|
|
16134
|
+
if (!cursor || !prevLines || prevLines.length === 0) return false;
|
|
16135
|
+
const curLines = fullScreen.split("\n");
|
|
16136
|
+
const startRow = Math.max(0, cursor.row - cond.cursor_above);
|
|
16137
|
+
const endRow = cursor.row;
|
|
16138
|
+
const currentSlice = curLines.slice(startRow, endRow).join("\n");
|
|
16139
|
+
const prevSlice = prevLines.slice(startRow, endRow).join("\n");
|
|
16140
|
+
const didChange = currentSlice !== prevSlice;
|
|
16141
|
+
const result = cond.changed ? didChange : !didChange;
|
|
16142
|
+
const stableSuffix = cond.stable_ms != null ? ` stable_ms=${cond.stable_ms}` : "";
|
|
16143
|
+
trace.push({
|
|
16144
|
+
kind: result ? "state_match" : "state_skip",
|
|
16145
|
+
text: `state[${stateId}] changed cond cursor_above=${cond.cursor_above} rows[${startRow},${endRow}) changed=${didChange} expected=${cond.changed}${stableSuffix} result=${result}`
|
|
16146
|
+
});
|
|
16147
|
+
return result;
|
|
16148
|
+
}
|
|
16149
|
+
if (isRegexCondition(cond)) {
|
|
16150
|
+
const haystack = sectionText(sections, cond.section, fullScreen);
|
|
16151
|
+
let matched = false;
|
|
16152
|
+
try {
|
|
16153
|
+
const re = new RegExp(cond.matches, cond.flags ?? "i");
|
|
16154
|
+
matched = re.test(haystack);
|
|
16155
|
+
} catch {
|
|
16156
|
+
matched = false;
|
|
16157
|
+
}
|
|
16158
|
+
if (!matched) {
|
|
16159
|
+
trace.push({ kind: "state_skip", text: `state[${stateId}] regex cond ${cond.section ?? "*"}~/${cond.matches}/ no match` });
|
|
16160
|
+
return false;
|
|
16161
|
+
}
|
|
16162
|
+
if (cursor !== void 0) {
|
|
16163
|
+
if (cond.cursor_row_min !== void 0 && cursor.row < cond.cursor_row_min) {
|
|
16164
|
+
trace.push({ kind: "state_skip", text: `state[${stateId}] cursor row ${cursor.row} < cursor_row_min ${cond.cursor_row_min}` });
|
|
16165
|
+
return false;
|
|
16329
16166
|
}
|
|
16330
|
-
if (
|
|
16331
|
-
|
|
16332
|
-
|
|
16333
|
-
const assistMatch = line.match(assistantRe);
|
|
16334
|
-
if (userMatch) {
|
|
16335
|
-
seenFirstRoleLine = true;
|
|
16336
|
-
const content = (userMatch[1] ?? userMatch[0]).trim();
|
|
16337
|
-
if (content) messages.push({ role: "user", kind: "standard", content });
|
|
16338
|
-
continue;
|
|
16167
|
+
if (cond.cursor_row_max !== void 0 && cursor.row > cond.cursor_row_max) {
|
|
16168
|
+
trace.push({ kind: "state_skip", text: `state[${stateId}] cursor row ${cursor.row} > cursor_row_max ${cond.cursor_row_max}` });
|
|
16169
|
+
return false;
|
|
16339
16170
|
}
|
|
16340
|
-
if (
|
|
16341
|
-
|
|
16342
|
-
|
|
16343
|
-
const content = (toolMatch[1] ?? toolMatch[0]).trim();
|
|
16344
|
-
if (content) messages.push({ role: "assistant", kind: "tool", content });
|
|
16345
|
-
continue;
|
|
16171
|
+
if (cond.cursor_col_min !== void 0 && cursor.col < cond.cursor_col_min) {
|
|
16172
|
+
trace.push({ kind: "state_skip", text: `state[${stateId}] cursor col ${cursor.col} < cursor_col_min ${cond.cursor_col_min}` });
|
|
16173
|
+
return false;
|
|
16346
16174
|
}
|
|
16347
|
-
if (
|
|
16348
|
-
|
|
16349
|
-
|
|
16350
|
-
if (content) messages.push({ role: "assistant", kind: "standard", content });
|
|
16351
|
-
continue;
|
|
16175
|
+
if (cond.cursor_col_max !== void 0 && cursor.col > cond.cursor_col_max) {
|
|
16176
|
+
trace.push({ kind: "state_skip", text: `state[${stateId}] cursor col ${cursor.col} > cursor_col_max ${cond.cursor_col_max}` });
|
|
16177
|
+
return false;
|
|
16352
16178
|
}
|
|
16353
|
-
if (!seenFirstRoleLine) continue;
|
|
16354
|
-
if (requireIndentForContinuation && !/^\s/.test(line)) continue;
|
|
16355
|
-
const last = messages[messages.length - 1];
|
|
16356
|
-
if (!last) continue;
|
|
16357
|
-
const cont = line.replace(/^\s+/, "");
|
|
16358
|
-
if (cont) last.content = last.content ? `${last.content}
|
|
16359
|
-
${cont}` : cont;
|
|
16360
16179
|
}
|
|
16361
|
-
|
|
16362
|
-
|
|
16363
|
-
|
|
16364
|
-
|
|
16365
|
-
|
|
16366
|
-
|
|
16367
|
-
|
|
16368
|
-
|
|
16369
|
-
|
|
16370
|
-
|
|
16371
|
-
|
|
16372
|
-
|
|
16180
|
+
trace.push({ kind: "state_match", text: `state[${stateId}] regex cond ${cond.section ?? "*"}~/${cond.matches}/ matched${cursor !== void 0 ? ` cursor=(${cursor.row},${cursor.col})` : ""}` });
|
|
16181
|
+
return true;
|
|
16182
|
+
}
|
|
16183
|
+
return false;
|
|
16184
|
+
}
|
|
16185
|
+
function extractTitle(rule, sections, fullScreen) {
|
|
16186
|
+
const hay = sectionText(sections, rule.section, fullScreen);
|
|
16187
|
+
if (!hay) return null;
|
|
16188
|
+
if (rule.first_line) {
|
|
16189
|
+
const lines = hay.split("\n");
|
|
16190
|
+
for (const line of lines) {
|
|
16191
|
+
const stripped = line.trim();
|
|
16192
|
+
if (stripped && !/^[─╌═─\s]+$/.test(stripped)) {
|
|
16193
|
+
return stripped;
|
|
16194
|
+
}
|
|
16373
16195
|
}
|
|
16374
|
-
return
|
|
16375
|
-
}
|
|
16196
|
+
return null;
|
|
16197
|
+
}
|
|
16198
|
+
if (rule.regex) {
|
|
16199
|
+
try {
|
|
16200
|
+
const re = new RegExp(rule.regex, rule.flags ?? "i");
|
|
16201
|
+
const m = re.exec(hay);
|
|
16202
|
+
if (m) return (m[1] ?? m[0]).trim();
|
|
16203
|
+
} catch {
|
|
16204
|
+
}
|
|
16205
|
+
}
|
|
16206
|
+
return null;
|
|
16376
16207
|
}
|
|
16377
|
-
|
|
16378
|
-
|
|
16379
|
-
|
|
16208
|
+
function compilePattern(ref) {
|
|
16209
|
+
const flags = ref.flags ?? "gm";
|
|
16210
|
+
return new RegExp(ref.pattern, flags.includes("g") ? flags : flags + "g");
|
|
16211
|
+
}
|
|
16212
|
+
function compileLinePattern(ref) {
|
|
16213
|
+
const flags = (ref.flags ?? "m").replace(/g/g, "");
|
|
16214
|
+
return new RegExp(ref.pattern, flags);
|
|
16215
|
+
}
|
|
16216
|
+
function extractButtonsFromRule(rule, hay) {
|
|
16217
|
+
const keyTemplate = rule.key_for_index;
|
|
16218
|
+
const continuationLines = rule.continuation_lines ?? false;
|
|
16219
|
+
const buttons = [];
|
|
16220
|
+
if (continuationLines) {
|
|
16221
|
+
const re = compileLinePattern(rule);
|
|
16222
|
+
const lines = hay.split("\n");
|
|
16223
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
16224
|
+
const m = re.exec(lines[i]);
|
|
16225
|
+
if (!m) continue;
|
|
16226
|
+
const idx = Number(m[1]);
|
|
16227
|
+
let label = String(m[2] ?? "").trim();
|
|
16228
|
+
if (!Number.isFinite(idx) || idx <= 0 || !label) continue;
|
|
16229
|
+
const current = hasCursorMarker(lines[i]);
|
|
16230
|
+
let j = i + 1;
|
|
16231
|
+
while (j < lines.length) {
|
|
16232
|
+
const next = lines[j];
|
|
16233
|
+
if (!next.trim()) break;
|
|
16234
|
+
if (re.test(next)) break;
|
|
16235
|
+
if (!/^\s+/.test(next)) break;
|
|
16236
|
+
label += " " + next.trim();
|
|
16237
|
+
j += 1;
|
|
16238
|
+
}
|
|
16239
|
+
const key = keyTemplate.replace(/\{index\}/g, String(idx));
|
|
16240
|
+
buttons.push({ index: idx, label, key, current });
|
|
16241
|
+
i = j - 1;
|
|
16242
|
+
}
|
|
16243
|
+
} else {
|
|
16244
|
+
const re = compilePattern(rule);
|
|
16245
|
+
let m;
|
|
16246
|
+
while ((m = re.exec(hay)) !== null) {
|
|
16247
|
+
const idx = Number(m[1]);
|
|
16248
|
+
const label = String(m[2] ?? "").trim();
|
|
16249
|
+
if (!Number.isFinite(idx) || idx <= 0 || !label) continue;
|
|
16250
|
+
const key = keyTemplate.replace(/\{index\}/g, String(idx));
|
|
16251
|
+
buttons.push({ index: idx, label, key, current: hasCursorMarker(m[0]) });
|
|
16252
|
+
}
|
|
16253
|
+
}
|
|
16254
|
+
const block2 = lastContiguousNumberedBlock(buttons);
|
|
16255
|
+
block2.sort((a, b) => a.index - b.index);
|
|
16256
|
+
return block2;
|
|
16257
|
+
}
|
|
16258
|
+
function lastContiguousNumberedBlock(entries) {
|
|
16259
|
+
if (entries.length <= 1) return entries.slice();
|
|
16260
|
+
let start = entries.length - 1;
|
|
16261
|
+
for (let i = entries.length - 1; i > 0; i -= 1) {
|
|
16262
|
+
if (entries[i - 1].index === entries[i].index - 1) start = i - 1;
|
|
16263
|
+
else break;
|
|
16264
|
+
}
|
|
16265
|
+
return entries.slice(start);
|
|
16266
|
+
}
|
|
16267
|
+
function hasCursorMarker(text) {
|
|
16268
|
+
return /^\s*[❯›>]/.test(text);
|
|
16269
|
+
}
|
|
16270
|
+
var init_evaluator = __esm({
|
|
16271
|
+
"src/providers/spec/evaluator.ts"() {
|
|
16380
16272
|
"use strict";
|
|
16381
|
-
init_detect_status();
|
|
16382
|
-
init_parse_approval();
|
|
16383
|
-
crypto3 = __toESM(require("crypto"));
|
|
16384
|
-
ANSI_RE = /\x1b\[[0-?]*[ -/]*[@-~]/g;
|
|
16385
|
-
OSC_RE = /\x1b\][^\x07\x1b\n]*(?:\x07|\x1b\\|(?=\n|$))/g;
|
|
16386
16273
|
}
|
|
16387
16274
|
});
|
|
16388
16275
|
|
|
16389
|
-
// src/
|
|
16390
|
-
|
|
16391
|
-
|
|
16392
|
-
|
|
16393
|
-
|
|
16394
|
-
|
|
16395
|
-
|
|
16396
|
-
|
|
16397
|
-
tailLen: tail.length,
|
|
16398
|
-
isWaitingForResponse: typeof input?.isWaitingForResponse === "boolean" ? input.isWaitingForResponse : void 0,
|
|
16399
|
-
screenTextHead: screenText ? screenText.slice(0, 200) : void 0
|
|
16400
|
-
};
|
|
16276
|
+
// src/providers/spec/fsm-evaluator.ts
|
|
16277
|
+
var fsm_evaluator_exports = {};
|
|
16278
|
+
__export(fsm_evaluator_exports, {
|
|
16279
|
+
evaluateConditionPreview: () => evaluateConditionPreview,
|
|
16280
|
+
evaluateFsm: () => evaluateFsm
|
|
16281
|
+
});
|
|
16282
|
+
function regionKey(cursorAbove) {
|
|
16283
|
+
return cursorAbove && cursorAbove > 0 ? cursorAbove : WHOLE_SCREEN;
|
|
16401
16284
|
}
|
|
16402
|
-
function
|
|
16403
|
-
|
|
16404
|
-
const json = JSON.stringify(result);
|
|
16405
|
-
return json && json.length > 400 ? `${json.slice(0, 400)}\u2026[truncated ${json.length - 400}]` : json ?? "undefined";
|
|
16406
|
-
} catch (e) {
|
|
16407
|
-
return `<unserializable: ${e?.message || e}>`;
|
|
16408
|
-
}
|
|
16285
|
+
function isRegex(c) {
|
|
16286
|
+
return "matches" in c;
|
|
16409
16287
|
}
|
|
16410
|
-
|
|
16411
|
-
|
|
16412
|
-
|
|
16413
|
-
|
|
16414
|
-
|
|
16415
|
-
|
|
16416
|
-
|
|
16417
|
-
|
|
16418
|
-
|
|
16419
|
-
|
|
16420
|
-
|
|
16421
|
-
|
|
16422
|
-
|
|
16423
|
-
|
|
16424
|
-
|
|
16425
|
-
|
|
16426
|
-
|
|
16427
|
-
|
|
16428
|
-
|
|
16429
|
-
|
|
16430
|
-
|
|
16431
|
-
|
|
16432
|
-
|
|
16433
|
-
|
|
16434
|
-
|
|
16288
|
+
function isChanged(c) {
|
|
16289
|
+
return "cursor_above" in c && "changed" in c;
|
|
16290
|
+
}
|
|
16291
|
+
function isElapsed(c) {
|
|
16292
|
+
return "elapsed_ms" in c;
|
|
16293
|
+
}
|
|
16294
|
+
function isStable(c) {
|
|
16295
|
+
return "stable_ms" in c;
|
|
16296
|
+
}
|
|
16297
|
+
function isAll(c) {
|
|
16298
|
+
return "all" in c;
|
|
16299
|
+
}
|
|
16300
|
+
function isAny(c) {
|
|
16301
|
+
return "any" in c;
|
|
16302
|
+
}
|
|
16303
|
+
function isNot(c) {
|
|
16304
|
+
return "not" in c;
|
|
16305
|
+
}
|
|
16306
|
+
function evalCond(cond, sections, fullScreen, cursor, prevLines, clock, legacyTrace, stateId) {
|
|
16307
|
+
if (isAll(cond)) {
|
|
16308
|
+
const children = cond.all.map((c) => evalCond(c, sections, fullScreen, cursor, prevLines, clock, legacyTrace, stateId));
|
|
16309
|
+
const result = children.every((c) => c.result);
|
|
16310
|
+
const remainingMs = result ? 0 : Math.max(0, ...children.filter((c) => !c.result).map((c) => c.remainingMs ?? 0));
|
|
16311
|
+
return { kind: "all", result, detail: `all(${children.length})`, remainingMs, children };
|
|
16312
|
+
}
|
|
16313
|
+
if (isAny(cond)) {
|
|
16314
|
+
const children = cond.any.map((c) => evalCond(c, sections, fullScreen, cursor, prevLines, clock, legacyTrace, stateId));
|
|
16315
|
+
const result = children.some((c) => c.result);
|
|
16316
|
+
const pending = children.filter((c) => !c.result).map((c) => c.remainingMs ?? Infinity);
|
|
16317
|
+
const remainingMs = result ? 0 : pending.length ? Math.min(...pending) : 0;
|
|
16318
|
+
return { kind: "any", result, detail: `any(${children.length})`, remainingMs: Number.isFinite(remainingMs) ? remainingMs : 0, children };
|
|
16319
|
+
}
|
|
16320
|
+
if (isNot(cond)) {
|
|
16321
|
+
const child = evalCond(cond.not, sections, fullScreen, cursor, prevLines, clock, legacyTrace, stateId);
|
|
16322
|
+
return { kind: "not", result: !child.result, detail: `not`, remainingMs: 0, children: [child] };
|
|
16323
|
+
}
|
|
16324
|
+
if (isElapsed(cond)) {
|
|
16325
|
+
const age = clock.now - clock.stateEnteredAt;
|
|
16326
|
+
const result = age >= cond.elapsed_ms;
|
|
16327
|
+
const remainingMs = result ? 0 : cond.elapsed_ms - age;
|
|
16328
|
+
return { kind: "elapsed", result, detail: `elapsed ${age}ms / ${cond.elapsed_ms}ms`, remainingMs };
|
|
16329
|
+
}
|
|
16330
|
+
if (isStable(cond)) {
|
|
16331
|
+
const key = regionKey(cond.cursor_above);
|
|
16332
|
+
const lastChanged = clock.regionLastChangedAt.get(key) ?? clock.stateEnteredAt;
|
|
16333
|
+
const stableFor = clock.now - lastChanged;
|
|
16334
|
+
const result = stableFor >= cond.stable_ms;
|
|
16335
|
+
const remainingMs = result ? 0 : cond.stable_ms - stableFor;
|
|
16336
|
+
const where = key === WHOLE_SCREEN ? "screen" : `cursor_above=${cond.cursor_above}`;
|
|
16337
|
+
return { kind: "stable", result, detail: `stable ${where} ${stableFor}ms / ${cond.stable_ms}ms`, remainingMs };
|
|
16338
|
+
}
|
|
16339
|
+
if (isRegex(cond) || isChanged(cond)) {
|
|
16340
|
+
const result = evaluateCondition(cond, sections, fullScreen, cursor, prevLines, legacyTrace, stateId);
|
|
16341
|
+
const kind = isRegex(cond) ? "regex" : "changed";
|
|
16342
|
+
const detail = isRegex(cond) ? `${cond.section ?? "*"}~/${cond.matches}/` : `cursor_above=${cond.cursor_above} changed=${cond.changed}`;
|
|
16343
|
+
let matchedText;
|
|
16344
|
+
if (result && isRegex(cond)) {
|
|
16345
|
+
try {
|
|
16346
|
+
const hay = sectionText(sections, cond.section, fullScreen);
|
|
16347
|
+
const re = new RegExp(cond.matches, cond.flags ?? "i");
|
|
16348
|
+
const m = re.exec(hay);
|
|
16349
|
+
if (m && m[0]) matchedText = m[0].replace(/\s+/g, " ").trim().slice(0, 160);
|
|
16350
|
+
} catch {
|
|
16435
16351
|
}
|
|
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
|
-
|
|
16524
|
-
|
|
16525
|
-
|
|
16526
|
-
|
|
16527
|
-
} catch (e) {
|
|
16528
|
-
LOG.warn("CLI", `[${this.cliType}] buildParseApprovalFromTui failed: ${e?.message || e}`);
|
|
16529
|
-
}
|
|
16530
|
-
}
|
|
16531
|
-
return sdk;
|
|
16532
|
-
}
|
|
16533
|
-
/** Reset per-session state — called when the PTY process exits. */
|
|
16534
|
-
resetSessionState() {
|
|
16535
|
-
this.scriptState = null;
|
|
16536
|
-
this.invocationTrace = [];
|
|
16537
|
-
this.lastBudgetWarnAt.clear();
|
|
16538
|
-
}
|
|
16539
|
-
// ─── Script access (for reflection and test patching) ────────────────────
|
|
16540
|
-
/** Returns the live scripts object. Direct property assignment on this object
|
|
16541
|
-
* patches individual scripts without replacing others (used in tests). */
|
|
16542
|
-
get cliScripts() {
|
|
16543
|
-
return this.scripts;
|
|
16352
|
+
}
|
|
16353
|
+
return matchedText ? { kind, result, detail, matchedText } : { kind, result, detail };
|
|
16354
|
+
}
|
|
16355
|
+
return { kind: "all", result: false, detail: "unknown condition" };
|
|
16356
|
+
}
|
|
16357
|
+
function fromLabel(t) {
|
|
16358
|
+
const from = Array.isArray(t.from) ? t.from.join("|") : t.from;
|
|
16359
|
+
return t.label ?? `${from}\u2192${t.to}`;
|
|
16360
|
+
}
|
|
16361
|
+
function evaluateFsm(spec, currentStateId, screenText, cursor, prevLines, clock) {
|
|
16362
|
+
const legacyTrace = [];
|
|
16363
|
+
const lines = screenText.split("\n").map((l) => l.endsWith("\r") ? l.slice(0, -1) : l);
|
|
16364
|
+
const cleanScreen = lines.join("\n");
|
|
16365
|
+
const sections = resolveSections(spec.sections ?? {}, lines);
|
|
16366
|
+
const outgoing = outgoingTransitions(spec, currentStateId);
|
|
16367
|
+
const transitions = [];
|
|
16368
|
+
let fired = null;
|
|
16369
|
+
for (const t of outgoing) {
|
|
16370
|
+
const holdMs = t.min_hold_ms ?? 0;
|
|
16371
|
+
const heldFor = clock.now - clock.stateEnteredAt;
|
|
16372
|
+
const holdSatisfied = heldFor >= holdMs;
|
|
16373
|
+
const holdRemainingMs = holdSatisfied ? 0 : holdMs - heldFor;
|
|
16374
|
+
let cond;
|
|
16375
|
+
let condResult = true;
|
|
16376
|
+
if (t.when) {
|
|
16377
|
+
cond = evalCond(t.when, sections, cleanScreen, cursor, prevLines, clock, legacyTrace, `${currentStateId}\u2192${t.to}`);
|
|
16378
|
+
condResult = cond.result;
|
|
16379
|
+
}
|
|
16380
|
+
const fires = holdSatisfied && condResult;
|
|
16381
|
+
const te = {
|
|
16382
|
+
to: t.to,
|
|
16383
|
+
label: fromLabel(t),
|
|
16384
|
+
eligible: true,
|
|
16385
|
+
holdSatisfied,
|
|
16386
|
+
holdRemainingMs,
|
|
16387
|
+
condResult,
|
|
16388
|
+
cond,
|
|
16389
|
+
fires,
|
|
16390
|
+
priority: t.priority ?? 0
|
|
16391
|
+
};
|
|
16392
|
+
transitions.push(te);
|
|
16393
|
+
if (fires && !fired) fired = te;
|
|
16394
|
+
}
|
|
16395
|
+
if (fired && !stateById(spec, fired.to)) {
|
|
16396
|
+
legacyTrace.push({ kind: "state_skip", text: `transition target ${fired.to} not found` });
|
|
16397
|
+
fired = null;
|
|
16398
|
+
}
|
|
16399
|
+
return { sections, transitions, fired, trace: legacyTrace };
|
|
16400
|
+
}
|
|
16401
|
+
function evaluateConditionPreview(cond, sections, screenText, cursor) {
|
|
16402
|
+
const lines = screenText.split("\n").map((l) => l.endsWith("\r") ? l.slice(0, -1) : l);
|
|
16403
|
+
const cleanScreen = lines.join("\n");
|
|
16404
|
+
const resolved = resolveSections(sections ?? {}, lines);
|
|
16405
|
+
const clock = { now: 0, stateEnteredAt: 0, regionLastChangedAt: /* @__PURE__ */ new Map() };
|
|
16406
|
+
return evalCond(cond, resolved, cleanScreen, cursor, void 0, clock, [], "preview");
|
|
16407
|
+
}
|
|
16408
|
+
var WHOLE_SCREEN;
|
|
16409
|
+
var init_fsm_evaluator = __esm({
|
|
16410
|
+
"src/providers/spec/fsm-evaluator.ts"() {
|
|
16411
|
+
"use strict";
|
|
16412
|
+
init_evaluator();
|
|
16413
|
+
init_fsm_types();
|
|
16414
|
+
WHOLE_SCREEN = -1;
|
|
16415
|
+
}
|
|
16416
|
+
});
|
|
16417
|
+
|
|
16418
|
+
// src/cli-adapters/pty-transport.ts
|
|
16419
|
+
var pty_transport_exports = {};
|
|
16420
|
+
__export(pty_transport_exports, {
|
|
16421
|
+
NodePtyTransportFactory: () => NodePtyTransportFactory
|
|
16422
|
+
});
|
|
16423
|
+
function loadNodePty() {
|
|
16424
|
+
if (cachedPty !== void 0) return cachedPty;
|
|
16425
|
+
try {
|
|
16426
|
+
cachedPty = require("node-pty");
|
|
16427
|
+
(0, import_session_host_core2.ensureNodePtySpawnHelperPermissions)();
|
|
16428
|
+
} catch {
|
|
16429
|
+
cachedPty = null;
|
|
16430
|
+
}
|
|
16431
|
+
return cachedPty;
|
|
16432
|
+
}
|
|
16433
|
+
var os13, cachedPty, NodePtyRuntimeTransport, NodePtyTransportFactory;
|
|
16434
|
+
var init_pty_transport = __esm({
|
|
16435
|
+
"src/cli-adapters/pty-transport.ts"() {
|
|
16436
|
+
"use strict";
|
|
16437
|
+
os13 = __toESM(require("os"));
|
|
16438
|
+
init_spawn_env();
|
|
16439
|
+
init_resolve_executable();
|
|
16440
|
+
NodePtyRuntimeTransport = class {
|
|
16441
|
+
constructor(handle) {
|
|
16442
|
+
this.handle = handle;
|
|
16544
16443
|
}
|
|
16545
|
-
|
|
16546
|
-
|
|
16547
|
-
|
|
16444
|
+
ready = Promise.resolve();
|
|
16445
|
+
terminalQueriesHandled = false;
|
|
16446
|
+
get pid() {
|
|
16447
|
+
return this.handle.pid;
|
|
16548
16448
|
}
|
|
16549
|
-
|
|
16550
|
-
|
|
16449
|
+
write(data) {
|
|
16450
|
+
this.handle.write(data);
|
|
16551
16451
|
}
|
|
16552
|
-
|
|
16553
|
-
|
|
16452
|
+
resize(cols, rows) {
|
|
16453
|
+
this.handle.resize(cols, rows);
|
|
16554
16454
|
}
|
|
16555
|
-
|
|
16556
|
-
|
|
16557
|
-
return this._parseErrorMessage;
|
|
16455
|
+
kill() {
|
|
16456
|
+
this.handle.kill();
|
|
16558
16457
|
}
|
|
16559
|
-
|
|
16560
|
-
|
|
16458
|
+
getMetadata() {
|
|
16459
|
+
return null;
|
|
16561
16460
|
}
|
|
16562
|
-
|
|
16563
|
-
|
|
16564
|
-
if (!this.scripts.detectStatus) return null;
|
|
16565
|
-
try {
|
|
16566
|
-
return this.invoke("detectStatus", this.scripts.detectStatus, input);
|
|
16567
|
-
} catch (e) {
|
|
16568
|
-
LOG.warn("CLI", `[${this.cliType}] detectStatus error: ${e?.message || e}`);
|
|
16569
|
-
return null;
|
|
16570
|
-
}
|
|
16461
|
+
onData(callback) {
|
|
16462
|
+
this.handle.onData(callback);
|
|
16571
16463
|
}
|
|
16572
|
-
|
|
16573
|
-
|
|
16574
|
-
try {
|
|
16575
|
-
return this.invoke(
|
|
16576
|
-
"parseApproval",
|
|
16577
|
-
this.scripts.parseApproval,
|
|
16578
|
-
input
|
|
16579
|
-
);
|
|
16580
|
-
} catch (e) {
|
|
16581
|
-
LOG.warn("CLI", `[${this.cliType}] parseApproval error: ${e?.message || e}`);
|
|
16582
|
-
return null;
|
|
16583
|
-
}
|
|
16464
|
+
onExit(callback) {
|
|
16465
|
+
this.handle.onExit(callback);
|
|
16584
16466
|
}
|
|
16585
|
-
|
|
16586
|
-
|
|
16587
|
-
|
|
16588
|
-
|
|
16589
|
-
|
|
16590
|
-
|
|
16591
|
-
|
|
16592
|
-
|
|
16593
|
-
|
|
16594
|
-
|
|
16595
|
-
|
|
16596
|
-
|
|
16597
|
-
|
|
16467
|
+
};
|
|
16468
|
+
NodePtyTransportFactory = class {
|
|
16469
|
+
spawn(command, args, options) {
|
|
16470
|
+
const pty = loadNodePty();
|
|
16471
|
+
if (!pty) throw new Error("node-pty is not installed");
|
|
16472
|
+
let cwd = options.cwd;
|
|
16473
|
+
if (cwd) {
|
|
16474
|
+
try {
|
|
16475
|
+
const fs32 = require("fs");
|
|
16476
|
+
const stat2 = fs32.statSync(cwd);
|
|
16477
|
+
if (!stat2.isDirectory()) cwd = os13.homedir();
|
|
16478
|
+
} catch {
|
|
16479
|
+
cwd = os13.homedir();
|
|
16480
|
+
}
|
|
16598
16481
|
}
|
|
16482
|
+
const handle = pty.spawn(resolveWin32Executable(command), args, {
|
|
16483
|
+
name: "xterm-256color",
|
|
16484
|
+
cols: options.cols,
|
|
16485
|
+
rows: options.rows,
|
|
16486
|
+
cwd,
|
|
16487
|
+
env: options.env
|
|
16488
|
+
});
|
|
16489
|
+
return new NodePtyRuntimeTransport(handle);
|
|
16599
16490
|
}
|
|
16600
|
-
|
|
16601
|
-
|
|
16602
|
-
|
|
16603
|
-
|
|
16604
|
-
|
|
16605
|
-
|
|
16606
|
-
|
|
16607
|
-
|
|
16491
|
+
};
|
|
16492
|
+
}
|
|
16493
|
+
});
|
|
16494
|
+
|
|
16495
|
+
// src/providers/sdk/v1/builders/cli/visible-region.ts
|
|
16496
|
+
function compile(re, flags) {
|
|
16497
|
+
try {
|
|
16498
|
+
return new RegExp(re, flags ?? "");
|
|
16499
|
+
} catch (e) {
|
|
16500
|
+
throw new Error(`Invalid visible-region anchor regex /${re}/${flags ?? ""}: ${e.message}`);
|
|
16501
|
+
}
|
|
16502
|
+
}
|
|
16503
|
+
function findAllMatches(re, text) {
|
|
16504
|
+
const results = [];
|
|
16505
|
+
const iter = new RegExp(re.source, re.flags.replace("g", "") + "g");
|
|
16506
|
+
let m;
|
|
16507
|
+
while ((m = iter.exec(text)) !== null) {
|
|
16508
|
+
results.push(m);
|
|
16509
|
+
if (m[0].length === 0) iter.lastIndex += 1;
|
|
16510
|
+
}
|
|
16511
|
+
return results;
|
|
16512
|
+
}
|
|
16513
|
+
function applyVisibleRegion(spec, text) {
|
|
16514
|
+
if (!text) return text;
|
|
16515
|
+
switch (spec.scope) {
|
|
16516
|
+
case "screen":
|
|
16517
|
+
case "buffer":
|
|
16518
|
+
return text;
|
|
16519
|
+
case "tail": {
|
|
16520
|
+
const limit = spec.tailChars ?? 4e3;
|
|
16521
|
+
if (text.length <= limit) return text;
|
|
16522
|
+
return text.slice(-limit);
|
|
16523
|
+
}
|
|
16524
|
+
case "between-anchors": {
|
|
16525
|
+
const anchors = spec.anchors;
|
|
16526
|
+
if (!anchors?.top && !anchors?.bottom) return text;
|
|
16527
|
+
const selectLast = (spec.selectAnchor ?? "first") === "last";
|
|
16528
|
+
let topEnd = 0;
|
|
16529
|
+
if (anchors.top) {
|
|
16530
|
+
const topRe = compile(anchors.top.pattern, anchors.top.flags);
|
|
16531
|
+
const topMatches = findAllMatches(topRe, text);
|
|
16532
|
+
if (topMatches.length === 0) {
|
|
16533
|
+
return text;
|
|
16608
16534
|
}
|
|
16609
|
-
|
|
16535
|
+
const chosen = selectLast ? topMatches[topMatches.length - 1] : topMatches[0];
|
|
16536
|
+
topEnd = chosen.index + chosen[0].length;
|
|
16610
16537
|
}
|
|
16611
|
-
|
|
16612
|
-
|
|
16613
|
-
const
|
|
16614
|
-
const
|
|
16615
|
-
const
|
|
16616
|
-
|
|
16617
|
-
|
|
16618
|
-
|
|
16619
|
-
result = fn(this.scriptState, input, this.sdk);
|
|
16620
|
-
} else if (arity === 2) {
|
|
16621
|
-
result = fn(this.scriptState, input);
|
|
16622
|
-
} else {
|
|
16623
|
-
result = fn(input);
|
|
16624
|
-
}
|
|
16625
|
-
const elapsedUs = startedHr ? Number((process.hrtime.bigint() - startedHr) / 1000n) : 0;
|
|
16626
|
-
const timedOut = this.checkBudget(scriptName, elapsedUs);
|
|
16627
|
-
this.recordTrace({
|
|
16628
|
-
at: startedAt,
|
|
16629
|
-
scriptName,
|
|
16630
|
-
arity,
|
|
16631
|
-
inputSummary: summarizeInput(input),
|
|
16632
|
-
ok: true,
|
|
16633
|
-
elapsedUs,
|
|
16634
|
-
resultSummary: summarizeResult(result),
|
|
16635
|
-
...timedOut ? { timedOut: true } : {}
|
|
16636
|
-
});
|
|
16637
|
-
return result;
|
|
16638
|
-
} catch (e) {
|
|
16639
|
-
const elapsedUs = startedHr ? Number((process.hrtime.bigint() - startedHr) / 1000n) : 0;
|
|
16640
|
-
const timedOut = this.checkBudget(scriptName, elapsedUs);
|
|
16641
|
-
this.recordTrace({
|
|
16642
|
-
at: startedAt,
|
|
16643
|
-
scriptName,
|
|
16644
|
-
arity,
|
|
16645
|
-
inputSummary: summarizeInput(input),
|
|
16646
|
-
ok: false,
|
|
16647
|
-
elapsedUs,
|
|
16648
|
-
error: e?.message ? String(e.message).slice(0, 400) : String(e).slice(0, 400),
|
|
16649
|
-
...timedOut ? { timedOut: true } : {}
|
|
16650
|
-
});
|
|
16651
|
-
throw e;
|
|
16538
|
+
let bottomStart = text.length;
|
|
16539
|
+
if (anchors.bottom) {
|
|
16540
|
+
const bottomRe = compile(anchors.bottom.pattern, anchors.bottom.flags);
|
|
16541
|
+
const searchFrom = topEnd;
|
|
16542
|
+
const suffix = text.slice(searchFrom);
|
|
16543
|
+
const bottomMatches = findAllMatches(bottomRe, suffix);
|
|
16544
|
+
if (bottomMatches.length === 0) {
|
|
16545
|
+
return text;
|
|
16652
16546
|
}
|
|
16547
|
+
const chosen = selectLast ? bottomMatches[bottomMatches.length - 1] : bottomMatches[0];
|
|
16548
|
+
bottomStart = searchFrom + chosen.index;
|
|
16653
16549
|
}
|
|
16654
|
-
|
|
16655
|
-
|
|
16656
|
-
* first violation per script (or after the throttle window expires) we
|
|
16657
|
-
* emit a single WARN so the operator learns which provider is slow.
|
|
16658
|
-
*
|
|
16659
|
-
* We deliberately throttle per-scriptName so a chronically-slow
|
|
16660
|
-
* detectStatus doesn't spam the log on every PTY frame settle.
|
|
16661
|
-
*/
|
|
16662
|
-
checkBudget(scriptName, elapsedUs) {
|
|
16663
|
-
const budgetUs = this.scriptCallBudgetMs * 1e3;
|
|
16664
|
-
if (elapsedUs <= budgetUs) return false;
|
|
16665
|
-
const now = Date.now();
|
|
16666
|
-
const last = this.lastBudgetWarnAt.get(scriptName) ?? 0;
|
|
16667
|
-
if (now - last >= BUDGET_WARN_THROTTLE_MS) {
|
|
16668
|
-
this.lastBudgetWarnAt.set(scriptName, now);
|
|
16669
|
-
LOG.warn(
|
|
16670
|
-
"CLI",
|
|
16671
|
-
`[${this.cliType}] script ${scriptName} took ${elapsedUs}us, budget ${budgetUs}us`
|
|
16672
|
-
);
|
|
16673
|
-
}
|
|
16674
|
-
return true;
|
|
16550
|
+
if (topEnd >= bottomStart) {
|
|
16551
|
+
return text;
|
|
16675
16552
|
}
|
|
16676
|
-
|
|
16553
|
+
return text.slice(topEnd, bottomStart);
|
|
16554
|
+
}
|
|
16555
|
+
default:
|
|
16556
|
+
return text;
|
|
16557
|
+
}
|
|
16558
|
+
}
|
|
16559
|
+
var init_visible_region = __esm({
|
|
16560
|
+
"src/providers/sdk/v1/builders/cli/visible-region.ts"() {
|
|
16561
|
+
"use strict";
|
|
16677
16562
|
}
|
|
16678
16563
|
});
|
|
16679
16564
|
|
|
16680
|
-
// src/cli
|
|
16681
|
-
function
|
|
16565
|
+
// src/providers/sdk/v1/builders/cli/detect-status.ts
|
|
16566
|
+
function compile2(re, flags) {
|
|
16567
|
+
try {
|
|
16568
|
+
return new RegExp(re, flags ?? "");
|
|
16569
|
+
} catch (e) {
|
|
16570
|
+
throw new Error(`Invalid regex /${re}/${flags ?? ""}: ${e.message}`);
|
|
16571
|
+
}
|
|
16572
|
+
}
|
|
16573
|
+
function takeTail(text, lines) {
|
|
16682
16574
|
if (!text) return "";
|
|
16683
|
-
|
|
16684
|
-
if (
|
|
16685
|
-
return
|
|
16575
|
+
const split = text.split("\n");
|
|
16576
|
+
if (split.length <= lines) return text;
|
|
16577
|
+
return split.slice(-lines).join("\n");
|
|
16686
16578
|
}
|
|
16687
|
-
function
|
|
16688
|
-
|
|
16579
|
+
function scopeText(input, scope, windowLines) {
|
|
16580
|
+
const lines = windowLines && windowLines > 0 ? windowLines : 8;
|
|
16581
|
+
switch (scope) {
|
|
16582
|
+
case "whole-screen":
|
|
16583
|
+
return input.screenText ?? "";
|
|
16584
|
+
case "recent-buffer":
|
|
16585
|
+
return input.tail ?? "";
|
|
16586
|
+
case "last-n-lines":
|
|
16587
|
+
case "live-frame-tail":
|
|
16588
|
+
case void 0:
|
|
16589
|
+
default:
|
|
16590
|
+
return takeTail(input.screenText ?? "", lines);
|
|
16591
|
+
}
|
|
16689
16592
|
}
|
|
16690
|
-
function
|
|
16691
|
-
|
|
16692
|
-
accumulatedBuffer,
|
|
16693
|
-
accumulatedRawBuffer,
|
|
16694
|
-
recentOutputBuffer,
|
|
16695
|
-
terminalScreenText,
|
|
16696
|
-
workingDir,
|
|
16697
|
-
providerSessionId,
|
|
16698
|
-
historySessionId,
|
|
16699
|
-
baseMessages,
|
|
16700
|
-
partialResponse,
|
|
16701
|
-
isWaitingForResponse,
|
|
16702
|
-
scope,
|
|
16703
|
-
runtimeSettings,
|
|
16704
|
-
spawnAt
|
|
16705
|
-
} = options;
|
|
16706
|
-
const buffer = scope ? sliceFromOffset(accumulatedBuffer, scope.bufferStart) : accumulatedBuffer;
|
|
16707
|
-
const rawBuffer = scope ? sliceFromOffset(accumulatedRawBuffer, scope.rawBufferStart) : accumulatedRawBuffer;
|
|
16708
|
-
const screenText = terminalScreenText;
|
|
16709
|
-
const recentBuffer = buffer.slice(-1e3) || recentOutputBuffer;
|
|
16710
|
-
return {
|
|
16711
|
-
buffer,
|
|
16712
|
-
rawBuffer,
|
|
16713
|
-
recentBuffer,
|
|
16714
|
-
screenText,
|
|
16715
|
-
workspace: workingDir,
|
|
16716
|
-
workingDir,
|
|
16717
|
-
providerSessionId,
|
|
16718
|
-
historySessionId,
|
|
16719
|
-
screen: buildCliScreenSnapshot(screenText),
|
|
16720
|
-
bufferScreen: buildCliScreenSnapshot(buffer),
|
|
16721
|
-
recentScreen: buildCliScreenSnapshot(recentBuffer),
|
|
16722
|
-
messages: [...baseMessages],
|
|
16723
|
-
partialResponse,
|
|
16724
|
-
isWaitingForResponse,
|
|
16725
|
-
promptText: scope?.prompt || "",
|
|
16726
|
-
settings: { ...runtimeSettings },
|
|
16727
|
-
...typeof spawnAt === "number" && spawnAt > 0 ? { spawnAt } : {}
|
|
16728
|
-
};
|
|
16593
|
+
function compileSpinnerMatchers(spec) {
|
|
16594
|
+
return spec.patterns.map((p) => compile2(p.regex, p.flags ?? "i"));
|
|
16729
16595
|
}
|
|
16730
|
-
function
|
|
16731
|
-
const
|
|
16732
|
-
|
|
16733
|
-
|
|
16596
|
+
function compileSettledPromptMatchers(spec) {
|
|
16597
|
+
const prompt = compile2(spec.regex, spec.flags ?? "m");
|
|
16598
|
+
const footers = (spec.withFooter ?? []).map((f) => {
|
|
16599
|
+
if (f.kind === "regex") {
|
|
16600
|
+
const re = compile2(f.pattern, f.flags ?? "i");
|
|
16601
|
+
return { test: (s2) => re.test(s2) };
|
|
16602
|
+
}
|
|
16603
|
+
const needle = f.pattern.toLowerCase();
|
|
16604
|
+
return { test: (s2) => s2.toLowerCase().includes(needle) };
|
|
16605
|
+
});
|
|
16606
|
+
return { prompt, footers };
|
|
16734
16607
|
}
|
|
16735
|
-
|
|
16736
|
-
|
|
16737
|
-
|
|
16738
|
-
|
|
16739
|
-
|
|
16608
|
+
function extractButtonLabels(spec, text) {
|
|
16609
|
+
if (!text) return [];
|
|
16610
|
+
const flags = spec.buttonFlags && spec.buttonFlags.includes("m") ? spec.buttonFlags : `${spec.buttonFlags ?? ""}m`;
|
|
16611
|
+
const buttonRe = compile2(spec.buttonPattern, flags);
|
|
16612
|
+
const labelGroup = Number.isInteger(spec.buttonLabelGroup) && (spec.buttonLabelGroup ?? 0) > 0 ? spec.buttonLabelGroup : 1;
|
|
16613
|
+
const out = [];
|
|
16614
|
+
for (const line of text.split("\n")) {
|
|
16615
|
+
buttonRe.lastIndex = 0;
|
|
16616
|
+
const m = buttonRe.exec(line);
|
|
16617
|
+
if (!m) continue;
|
|
16618
|
+
const captured = m[labelGroup] ?? (labelGroup === 1 && m.length > 2 ? m[m.length - 1] : void 0);
|
|
16619
|
+
if (captured && captured.trim()) out.push(captured.trim());
|
|
16620
|
+
}
|
|
16621
|
+
return out;
|
|
16622
|
+
}
|
|
16623
|
+
function buttonBlockApprovalCue(spec, text) {
|
|
16624
|
+
const labels = extractButtonLabels(spec, text);
|
|
16625
|
+
if (labels.length < 2) return false;
|
|
16626
|
+
if (pickApprovalButton(labels).index < 0) return false;
|
|
16627
|
+
return hasNegativeApprovalOption(labels);
|
|
16628
|
+
}
|
|
16629
|
+
function modalMatches(spec, input) {
|
|
16630
|
+
const text = input.screenText ?? "";
|
|
16631
|
+
const question = compile2(spec.questionPattern, spec.questionFlags ?? "i");
|
|
16632
|
+
if (question.test(text)) return true;
|
|
16633
|
+
for (const variant of spec.questionVariants ?? []) {
|
|
16634
|
+
const re = compile2(variant.regex, variant.flags ?? "i");
|
|
16635
|
+
if (re.test(text)) return true;
|
|
16636
|
+
}
|
|
16637
|
+
if (buttonBlockApprovalCue(spec, text)) return true;
|
|
16638
|
+
return false;
|
|
16639
|
+
}
|
|
16640
|
+
function evaluateGroup(group, spec, input, compiled) {
|
|
16641
|
+
switch (group) {
|
|
16642
|
+
case "spinner": {
|
|
16643
|
+
if (!spec.spinner || !compiled.spinner) return null;
|
|
16644
|
+
const text = scopeText(input, spec.spinner.scope, spec.spinner.scopeWindowLines);
|
|
16645
|
+
return compiled.spinner.some((re) => re.test(text)) ? "generating" : null;
|
|
16646
|
+
}
|
|
16647
|
+
case "modal": {
|
|
16648
|
+
if (!spec.modal) return null;
|
|
16649
|
+
return modalMatches(spec.modal, input) ? "waiting_approval" : null;
|
|
16650
|
+
}
|
|
16651
|
+
case "settled-prompt": {
|
|
16652
|
+
if (!spec.settledPrompt || !compiled.settled) return null;
|
|
16653
|
+
const text = scopeText(input, spec.settledPrompt.scope, spec.settledPrompt.scopeWindowLines);
|
|
16654
|
+
if (!compiled.settled.prompt.test(text)) return null;
|
|
16655
|
+
if (compiled.settled.footers.length === 0) return "idle";
|
|
16656
|
+
return compiled.settled.footers.every((f) => f.test(text)) ? "idle" : null;
|
|
16657
|
+
}
|
|
16658
|
+
// Groups declared in the catalog but not yet implemented as builder steps
|
|
16659
|
+
// return null so they are no-ops in dispatch — declaring them in `order`
|
|
16660
|
+
// is forward-compatible. Phase 2 Week 8+ will wire them.
|
|
16661
|
+
case "cue-ordering":
|
|
16662
|
+
case "error-detection":
|
|
16663
|
+
case "approval-stitching":
|
|
16664
|
+
return null;
|
|
16665
|
+
default:
|
|
16666
|
+
return null;
|
|
16667
|
+
}
|
|
16668
|
+
}
|
|
16669
|
+
function buildDetectStatusFromTui(spec) {
|
|
16670
|
+
const compiledSpinner = spec.spinner ? compileSpinnerMatchers(spec.spinner) : null;
|
|
16671
|
+
const compiledSettled = spec.settledPrompt ? compileSettledPromptMatchers(spec.settledPrompt) : null;
|
|
16672
|
+
const compiled = { spinner: compiledSpinner, settled: compiledSettled };
|
|
16673
|
+
const order = spec.dispatchOrder?.order && spec.dispatchOrder.order.length > 0 ? spec.dispatchOrder.order : DEFAULT_ORDER;
|
|
16674
|
+
return function detectStatus(input) {
|
|
16675
|
+
const effectiveInput = spec.visibleRegion ? {
|
|
16676
|
+
...input,
|
|
16677
|
+
screenText: applyVisibleRegion(spec.visibleRegion, input.screenText ?? ""),
|
|
16678
|
+
tail: applyVisibleRegion(spec.visibleRegion, input.tail)
|
|
16679
|
+
} : input;
|
|
16680
|
+
for (const group of order) {
|
|
16681
|
+
const verdict = evaluateGroup(group, spec, effectiveInput, compiled);
|
|
16682
|
+
if (verdict !== null) return verdict;
|
|
16683
|
+
}
|
|
16684
|
+
return null;
|
|
16685
|
+
};
|
|
16686
|
+
}
|
|
16687
|
+
var DEFAULT_ORDER;
|
|
16688
|
+
var init_detect_status = __esm({
|
|
16689
|
+
"src/providers/sdk/v1/builders/cli/detect-status.ts"() {
|
|
16690
|
+
"use strict";
|
|
16691
|
+
init_visible_region();
|
|
16692
|
+
init_approval_utils();
|
|
16693
|
+
DEFAULT_ORDER = ["spinner", "modal", "settled-prompt"];
|
|
16694
|
+
}
|
|
16740
16695
|
});
|
|
16741
16696
|
|
|
16742
|
-
// src/cli
|
|
16743
|
-
|
|
16744
|
-
|
|
16745
|
-
|
|
16697
|
+
// src/providers/sdk/v1/builders/cli/parse-approval.ts
|
|
16698
|
+
function compile3(re, flags) {
|
|
16699
|
+
try {
|
|
16700
|
+
return new RegExp(re, flags);
|
|
16701
|
+
} catch (e) {
|
|
16702
|
+
throw new Error(`Invalid regex /${re}/${flags ?? ""}: ${e.message}`);
|
|
16703
|
+
}
|
|
16704
|
+
}
|
|
16705
|
+
function findQuestionLineIndex(spec, lines) {
|
|
16706
|
+
const primary = compile3(spec.questionPattern, spec.questionFlags ?? "i");
|
|
16707
|
+
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
16708
|
+
if (primary.test(lines[i])) return { index: i, matchedSource: "primary" };
|
|
16709
|
+
}
|
|
16710
|
+
for (const variant of spec.questionVariants ?? []) {
|
|
16711
|
+
const re = compile3(variant.regex, variant.flags ?? "i");
|
|
16712
|
+
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
16713
|
+
if (re.test(lines[i])) return { index: i, matchedSource: variant.label ?? "variant" };
|
|
16714
|
+
}
|
|
16715
|
+
}
|
|
16716
|
+
return null;
|
|
16717
|
+
}
|
|
16718
|
+
function scopeLines(spec, lines, questionIndex) {
|
|
16719
|
+
const scope = spec.scope ?? "between-last-two-separators";
|
|
16720
|
+
if (scope === "whole-screen") {
|
|
16721
|
+
return { start: 0, end: lines.length };
|
|
16722
|
+
}
|
|
16723
|
+
if (scope === "window-around-question") {
|
|
16724
|
+
const window = spec.scopeWindowLines ?? 16;
|
|
16725
|
+
return {
|
|
16726
|
+
start: Math.max(0, questionIndex - 2),
|
|
16727
|
+
end: Math.min(lines.length, questionIndex + window)
|
|
16728
|
+
};
|
|
16729
|
+
}
|
|
16730
|
+
let lastSep = -1;
|
|
16731
|
+
let prevSep = -1;
|
|
16732
|
+
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
16733
|
+
if (SEPARATOR_RE.test(lines[i].trim())) {
|
|
16734
|
+
if (lastSep < 0) lastSep = i;
|
|
16735
|
+
else if (prevSep < 0) {
|
|
16736
|
+
prevSep = i;
|
|
16737
|
+
break;
|
|
16738
|
+
}
|
|
16739
|
+
}
|
|
16740
|
+
}
|
|
16741
|
+
if (lastSep >= 0 && prevSep >= 0) {
|
|
16742
|
+
return { start: prevSep, end: lastSep + 1 };
|
|
16743
|
+
}
|
|
16744
|
+
return {
|
|
16745
|
+
start: Math.max(0, questionIndex - 4),
|
|
16746
|
+
end: Math.min(lines.length, questionIndex + 16)
|
|
16747
|
+
};
|
|
16748
|
+
}
|
|
16749
|
+
function extractButtons(spec, lines, windowStart, windowEnd) {
|
|
16750
|
+
const buttonRe = compile3(spec.buttonPattern, spec.buttonFlags ?? "m");
|
|
16751
|
+
const out = [];
|
|
16752
|
+
const labelGroup = Number.isInteger(spec.buttonLabelGroup) && (spec.buttonLabelGroup ?? 0) > 0 ? spec.buttonLabelGroup : 1;
|
|
16753
|
+
let i = windowStart;
|
|
16754
|
+
while (i < windowEnd) {
|
|
16755
|
+
const line = lines[i];
|
|
16756
|
+
const m = buttonRe.exec(line);
|
|
16757
|
+
const captured = m?.[labelGroup] ?? (labelGroup === 1 && m && m.length > 2 ? m[m.length - 1] : void 0);
|
|
16758
|
+
if (m && captured) {
|
|
16759
|
+
let label = captured.trim();
|
|
16760
|
+
if (spec.continuationLines) {
|
|
16761
|
+
let j = i + 1;
|
|
16762
|
+
while (j < windowEnd) {
|
|
16763
|
+
const next = lines[j];
|
|
16764
|
+
if (!next.trim()) break;
|
|
16765
|
+
if (buttonRe.test(next)) break;
|
|
16766
|
+
if (!/^\s+/.test(next)) break;
|
|
16767
|
+
label += " " + next.trim();
|
|
16768
|
+
j += 1;
|
|
16769
|
+
}
|
|
16770
|
+
i = j;
|
|
16771
|
+
} else {
|
|
16772
|
+
i += 1;
|
|
16773
|
+
}
|
|
16774
|
+
out.push(label);
|
|
16775
|
+
} else {
|
|
16776
|
+
i += 1;
|
|
16777
|
+
}
|
|
16778
|
+
}
|
|
16779
|
+
return out;
|
|
16780
|
+
}
|
|
16781
|
+
function buildMessage(spec, lines, questionIndex, windowStart, windowEnd) {
|
|
16782
|
+
const messageParts = [];
|
|
16783
|
+
if (spec.contextHeader) {
|
|
16784
|
+
const ctxRe = compile3(spec.contextHeader.regex, (spec.contextHeader.flags ?? "i") + "m");
|
|
16785
|
+
const haystack = lines.slice(windowStart, windowEnd).join("\n");
|
|
16786
|
+
const m = ctxRe.exec(haystack);
|
|
16787
|
+
if (m) messageParts.push(m[1] ? m[1].trim() : m[0].trim());
|
|
16788
|
+
}
|
|
16789
|
+
messageParts.push(lines[questionIndex].trim());
|
|
16790
|
+
return messageParts.filter(Boolean).join(" \u2014 ");
|
|
16791
|
+
}
|
|
16792
|
+
function extractInlineButtons(spec, lines, windowStart, windowEnd) {
|
|
16793
|
+
if (!spec.inlineButtonPattern) return [];
|
|
16794
|
+
const declaredFlags = spec.inlineButtonFlags ?? "gi";
|
|
16795
|
+
const flags = declaredFlags.includes("g") ? declaredFlags : declaredFlags + "g";
|
|
16796
|
+
const re = compile3(spec.inlineButtonPattern, flags);
|
|
16797
|
+
const out = [];
|
|
16798
|
+
for (let i = windowStart; i < windowEnd; i += 1) {
|
|
16799
|
+
let m;
|
|
16800
|
+
re.lastIndex = 0;
|
|
16801
|
+
while ((m = re.exec(lines[i])) !== null) {
|
|
16802
|
+
const label = (m[1] ?? m[0]).trim();
|
|
16803
|
+
if (label && !out.includes(label)) out.push(label);
|
|
16804
|
+
if (m.index === re.lastIndex) re.lastIndex += 1;
|
|
16805
|
+
}
|
|
16806
|
+
}
|
|
16807
|
+
return out;
|
|
16808
|
+
}
|
|
16809
|
+
function buildParseApprovalFromTui(spec, visibleRegion) {
|
|
16810
|
+
const minButtons = spec.minButtons ?? 2;
|
|
16811
|
+
return function parseApproval(input) {
|
|
16812
|
+
const rawText = input.screenText ?? input.buffer ?? "";
|
|
16813
|
+
if (!rawText) return null;
|
|
16814
|
+
const text = visibleRegion ? applyVisibleRegion(visibleRegion, rawText) : rawText;
|
|
16815
|
+
const lines = text.split("\n");
|
|
16816
|
+
const question = findQuestionLineIndex(spec, lines);
|
|
16817
|
+
if (!question) return null;
|
|
16818
|
+
const { start, end } = scopeLines(spec, lines, question.index);
|
|
16819
|
+
if (question.index < start || question.index >= end) return null;
|
|
16820
|
+
let buttons = extractButtons(spec, lines, question.index + 1, end);
|
|
16821
|
+
if (buttons.length < minButtons && spec.inlineButtonPattern) {
|
|
16822
|
+
buttons = extractInlineButtons(spec, lines, question.index, end);
|
|
16823
|
+
}
|
|
16824
|
+
if (buttons.length < minButtons) return null;
|
|
16825
|
+
const message = buildMessage(spec, lines, question.index, start, end);
|
|
16826
|
+
return { message, buttons };
|
|
16827
|
+
};
|
|
16828
|
+
}
|
|
16829
|
+
var SEPARATOR_RE;
|
|
16830
|
+
var init_parse_approval = __esm({
|
|
16831
|
+
"src/providers/sdk/v1/builders/cli/parse-approval.ts"() {
|
|
16832
|
+
"use strict";
|
|
16833
|
+
init_visible_region();
|
|
16834
|
+
SEPARATOR_RE = /^(?:─|━|═|━){10,}\s*$/;
|
|
16835
|
+
}
|
|
16836
|
+
});
|
|
16837
|
+
|
|
16838
|
+
// src/providers/sdk/v1/builders/cli/parse-session.ts
|
|
16839
|
+
function stripAnsi2(text) {
|
|
16840
|
+
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, "");
|
|
16841
|
+
}
|
|
16842
|
+
function splitLines(text) {
|
|
16843
|
+
return stripAnsi2(text).replace(//g, "").split(/\r?\n/).map((l) => l.replace(/\s+$/, ""));
|
|
16844
|
+
}
|
|
16845
|
+
function pickInputText(input, scope) {
|
|
16846
|
+
if (!input) return "";
|
|
16847
|
+
if (scope === "screen") return String(input.screenText || input.screen?.text || "");
|
|
16848
|
+
if (scope === "tail") return String(input.tail || input.recentBuffer || input.buffer || "");
|
|
16849
|
+
return String(input.buffer || input.rawBuffer || input.screenText || "");
|
|
16850
|
+
}
|
|
16851
|
+
function stableHash(value) {
|
|
16852
|
+
return crypto3.createHash("sha1").update(String(value || "")).digest("hex").slice(0, 12);
|
|
16853
|
+
}
|
|
16854
|
+
function normalizeMessageIdentity(messages, status) {
|
|
16855
|
+
const list = Array.isArray(messages) ? messages : [];
|
|
16856
|
+
let turnIndex = -1;
|
|
16857
|
+
return list.map((message, index) => {
|
|
16858
|
+
const role = message?.role || "assistant";
|
|
16859
|
+
const kind = message?.kind || "standard";
|
|
16860
|
+
const content = typeof message?.content === "string" ? message.content : "";
|
|
16861
|
+
if (role === "user" || turnIndex < 0) turnIndex += 1;
|
|
16862
|
+
const seed = [role, kind, "", index, content].join("\n");
|
|
16863
|
+
const providerUnitKey = `v2-pty:${role}:${kind}:${index}:${stableHash(seed)}`;
|
|
16864
|
+
const bubbleId = `bubble:${providerUnitKey}`;
|
|
16865
|
+
const turnKey = `turn:${turnIndex}`;
|
|
16866
|
+
const isStreamingTail = status === "generating" && role === "assistant" && index === list.length - 1;
|
|
16867
|
+
return {
|
|
16868
|
+
...message,
|
|
16869
|
+
providerUnitKey,
|
|
16870
|
+
bubbleId,
|
|
16871
|
+
sequence: index,
|
|
16872
|
+
_turnKey: turnKey,
|
|
16873
|
+
bubbleState: isStreamingTail ? "streaming" : "final"
|
|
16874
|
+
};
|
|
16875
|
+
});
|
|
16876
|
+
}
|
|
16877
|
+
function buildParseSessionFromTui(spec) {
|
|
16878
|
+
if (!spec.transcriptPty) {
|
|
16879
|
+
throw new Error("buildParseSessionFromTui: spec.transcriptPty is required");
|
|
16880
|
+
}
|
|
16881
|
+
const assistantRe = new RegExp(spec.transcriptPty.assistantPrefix.regex, spec.transcriptPty.assistantPrefix.flags || "");
|
|
16882
|
+
const userRe = spec.transcriptPty.userPrefix ? new RegExp(spec.transcriptPty.userPrefix.regex, spec.transcriptPty.userPrefix.flags || "") : null;
|
|
16883
|
+
const toolRe = spec.transcriptPty.toolPrefix ? new RegExp(spec.transcriptPty.toolPrefix.regex, spec.transcriptPty.toolPrefix.flags || "") : null;
|
|
16884
|
+
const toolSkip = spec.transcriptPty.toolPrefix?.skip ?? false;
|
|
16885
|
+
const chromeRes = (spec.transcriptPty.chromePatterns || []).map((p) => new RegExp(p.regex, p.flags || ""));
|
|
16886
|
+
const requireIndentForContinuation = spec.transcriptPty.continuationLine?.indented ?? false;
|
|
16887
|
+
const stripLeadingChrome = spec.transcriptPty.stripLeadingChrome ?? true;
|
|
16888
|
+
const scope = spec.transcriptPty.scope ?? "buffer";
|
|
16889
|
+
const detectStatus = spec.spinner || spec.settledPrompt || spec.modal || spec.dispatchOrder ? buildDetectStatusFromTui({
|
|
16890
|
+
spinner: spec.spinner,
|
|
16891
|
+
settledPrompt: spec.settledPrompt,
|
|
16892
|
+
modal: spec.modal,
|
|
16893
|
+
dispatchOrder: spec.dispatchOrder
|
|
16894
|
+
}) : () => null;
|
|
16895
|
+
const parseApproval = spec.modal ? buildParseApprovalFromTui(spec.modal) : () => null;
|
|
16896
|
+
const sessionIdRe = spec.sessionIdExtraction ? new RegExp(
|
|
16897
|
+
spec.sessionIdExtraction.regex,
|
|
16898
|
+
spec.sessionIdExtraction.flags ?? "i"
|
|
16899
|
+
) : null;
|
|
16900
|
+
const sessionIdScope = spec.sessionIdExtraction?.scope ?? "tail";
|
|
16901
|
+
return function parseSession(input) {
|
|
16902
|
+
const status = detectStatus(input) ?? "idle";
|
|
16903
|
+
const modal = parseApproval(input);
|
|
16904
|
+
const text = pickInputText(input, scope);
|
|
16905
|
+
const lines = splitLines(text);
|
|
16906
|
+
const messages = [];
|
|
16907
|
+
let seenFirstRoleLine = !stripLeadingChrome;
|
|
16908
|
+
for (const raw of lines) {
|
|
16909
|
+
const line = raw;
|
|
16910
|
+
if (line.trim() === "") {
|
|
16911
|
+
continue;
|
|
16912
|
+
}
|
|
16913
|
+
let isChrome = false;
|
|
16914
|
+
for (const cre of chromeRes) {
|
|
16915
|
+
if (cre.test(line)) {
|
|
16916
|
+
isChrome = true;
|
|
16917
|
+
break;
|
|
16918
|
+
}
|
|
16919
|
+
}
|
|
16920
|
+
if (isChrome) continue;
|
|
16921
|
+
const userMatch = userRe ? line.match(userRe) : null;
|
|
16922
|
+
const toolMatch = toolRe ? line.match(toolRe) : null;
|
|
16923
|
+
const assistMatch = line.match(assistantRe);
|
|
16924
|
+
if (userMatch) {
|
|
16925
|
+
seenFirstRoleLine = true;
|
|
16926
|
+
const content = (userMatch[1] ?? userMatch[0]).trim();
|
|
16927
|
+
if (content) messages.push({ role: "user", kind: "standard", content });
|
|
16928
|
+
continue;
|
|
16929
|
+
}
|
|
16930
|
+
if (toolMatch) {
|
|
16931
|
+
seenFirstRoleLine = true;
|
|
16932
|
+
if (toolSkip) continue;
|
|
16933
|
+
const content = (toolMatch[1] ?? toolMatch[0]).trim();
|
|
16934
|
+
if (content) messages.push({ role: "assistant", kind: "tool", content });
|
|
16935
|
+
continue;
|
|
16936
|
+
}
|
|
16937
|
+
if (assistMatch) {
|
|
16938
|
+
seenFirstRoleLine = true;
|
|
16939
|
+
const content = (assistMatch[1] ?? assistMatch[0]).trim();
|
|
16940
|
+
if (content) messages.push({ role: "assistant", kind: "standard", content });
|
|
16941
|
+
continue;
|
|
16942
|
+
}
|
|
16943
|
+
if (!seenFirstRoleLine) continue;
|
|
16944
|
+
if (requireIndentForContinuation && !/^\s/.test(line)) continue;
|
|
16945
|
+
const last = messages[messages.length - 1];
|
|
16946
|
+
if (!last) continue;
|
|
16947
|
+
const cont = line.replace(/^\s+/, "");
|
|
16948
|
+
if (cont) last.content = last.content ? `${last.content}
|
|
16949
|
+
${cont}` : cont;
|
|
16950
|
+
}
|
|
16951
|
+
const result = {
|
|
16952
|
+
status,
|
|
16953
|
+
messages,
|
|
16954
|
+
activeModal: modal,
|
|
16955
|
+
modal,
|
|
16956
|
+
parsedStatus: status
|
|
16957
|
+
};
|
|
16958
|
+
if (sessionIdRe) {
|
|
16959
|
+
const haystack = stripAnsi2(pickInputText(input, sessionIdScope));
|
|
16960
|
+
const match = haystack.match(sessionIdRe);
|
|
16961
|
+
const captured = match?.[1]?.trim();
|
|
16962
|
+
if (captured) result.providerSessionId = captured;
|
|
16963
|
+
}
|
|
16964
|
+
return result;
|
|
16965
|
+
};
|
|
16966
|
+
}
|
|
16967
|
+
var crypto3, ANSI_RE, OSC_RE;
|
|
16968
|
+
var init_parse_session = __esm({
|
|
16969
|
+
"src/providers/sdk/v1/builders/cli/parse-session.ts"() {
|
|
16970
|
+
"use strict";
|
|
16971
|
+
init_detect_status();
|
|
16972
|
+
init_parse_approval();
|
|
16973
|
+
crypto3 = __toESM(require("crypto"));
|
|
16974
|
+
ANSI_RE = /\x1b\[[0-?]*[ -/]*[@-~]/g;
|
|
16975
|
+
OSC_RE = /\x1b\][^\x07\x1b\n]*(?:\x07|\x1b\\|(?=\n|$))/g;
|
|
16976
|
+
}
|
|
16977
|
+
});
|
|
16978
|
+
|
|
16979
|
+
// src/cli-adapters/cli-script-runner.ts
|
|
16980
|
+
function summarizeInput(input) {
|
|
16981
|
+
const screenText = typeof input?.screenText === "string" ? input.screenText : "";
|
|
16982
|
+
const rawBuffer = typeof input?.rawBuffer === "string" ? input.rawBuffer : "";
|
|
16983
|
+
const tail = typeof input?.tail === "string" ? input.tail : "";
|
|
16984
|
+
return {
|
|
16985
|
+
screenTextLen: screenText.length,
|
|
16986
|
+
rawBufferLen: rawBuffer.length,
|
|
16987
|
+
tailLen: tail.length,
|
|
16988
|
+
isWaitingForResponse: typeof input?.isWaitingForResponse === "boolean" ? input.isWaitingForResponse : void 0,
|
|
16989
|
+
screenTextHead: screenText ? screenText.slice(0, 200) : void 0
|
|
16990
|
+
};
|
|
16991
|
+
}
|
|
16992
|
+
function summarizeResult(result) {
|
|
16993
|
+
try {
|
|
16994
|
+
const json = JSON.stringify(result);
|
|
16995
|
+
return json && json.length > 400 ? `${json.slice(0, 400)}\u2026[truncated ${json.length - 400}]` : json ?? "undefined";
|
|
16996
|
+
} catch (e) {
|
|
16997
|
+
return `<unserializable: ${e?.message || e}>`;
|
|
16998
|
+
}
|
|
16999
|
+
}
|
|
17000
|
+
var TRACE_RING_CAPACITY, DEFAULT_SCRIPT_CALL_BUDGET_MS, BUDGET_WARN_THROTTLE_MS, CliScriptRunner;
|
|
17001
|
+
var init_cli_script_runner = __esm({
|
|
17002
|
+
"src/cli-adapters/cli-script-runner.ts"() {
|
|
16746
17003
|
"use strict";
|
|
16747
17004
|
init_logger();
|
|
16748
|
-
init_provider_cli_parse();
|
|
16749
17005
|
init_provider_cli_shared();
|
|
16750
|
-
|
|
16751
|
-
|
|
16752
|
-
|
|
16753
|
-
|
|
16754
|
-
|
|
16755
|
-
|
|
16756
|
-
|
|
16757
|
-
|
|
16758
|
-
|
|
16759
|
-
|
|
16760
|
-
|
|
16761
|
-
|
|
16762
|
-
|
|
17006
|
+
init_detect_status();
|
|
17007
|
+
init_parse_approval();
|
|
17008
|
+
init_parse_session();
|
|
17009
|
+
TRACE_RING_CAPACITY = 64;
|
|
17010
|
+
DEFAULT_SCRIPT_CALL_BUDGET_MS = 50;
|
|
17011
|
+
BUDGET_WARN_THROTTLE_MS = 3e4;
|
|
17012
|
+
CliScriptRunner = class {
|
|
17013
|
+
scripts = {};
|
|
17014
|
+
scriptState = null;
|
|
17015
|
+
_parseErrorMessage = null;
|
|
17016
|
+
cliType;
|
|
17017
|
+
sdk = {};
|
|
17018
|
+
invocationTrace = [];
|
|
17019
|
+
/** Per-invocation wall-clock budget (ms). Configurable via setScriptCallBudget. */
|
|
17020
|
+
scriptCallBudgetMs = DEFAULT_SCRIPT_CALL_BUDGET_MS;
|
|
17021
|
+
/** Last WARN emit time per scriptName, used to throttle repeated budget violations. */
|
|
17022
|
+
lastBudgetWarnAt = /* @__PURE__ */ new Map();
|
|
17023
|
+
constructor(cliType) {
|
|
17024
|
+
this.cliType = cliType;
|
|
17025
|
+
}
|
|
17026
|
+
/** Returns the most-recent script invocation traces (oldest → newest). */
|
|
17027
|
+
getInvocationTrace() {
|
|
17028
|
+
return this.invocationTrace.slice();
|
|
17029
|
+
}
|
|
17030
|
+
/** Clear the trace ring — used by tests and after PTY reset. */
|
|
17031
|
+
clearInvocationTrace() {
|
|
17032
|
+
this.invocationTrace = [];
|
|
16763
17033
|
}
|
|
16764
|
-
// ── Status ───────────────────────────────────────
|
|
16765
|
-
currentStatus = "starting";
|
|
16766
|
-
isWaitingForResponse = false;
|
|
16767
|
-
currentTurnScope = null;
|
|
16768
|
-
activeModal = null;
|
|
16769
|
-
// ── Approval ─────────────────────────────────────
|
|
16770
|
-
lastApprovalResolvedAt = 0;
|
|
16771
|
-
lastResolvedModalMessage = "";
|
|
16772
17034
|
/**
|
|
16773
|
-
*
|
|
16774
|
-
* with a freshly captured modal (see `applyWaitingApproval`). It is the
|
|
16775
|
-
* single discriminator between "the same approval re-observed across TUI
|
|
16776
|
-
* paint flaps" and "a genuinely new, distinct approval".
|
|
16777
|
-
*
|
|
16778
|
-
* The message-equality cooldown below (`lastResolvedModalMessage`) cannot
|
|
16779
|
-
* tell these apart on its own: claude-cli routinely presents consecutive
|
|
16780
|
-
* approvals whose modal message text is identical (e.g. two back-to-back
|
|
16781
|
-
* Bash-command prompts). When that second approval arrived inside
|
|
16782
|
-
* `approvalCooldown`, the message-equality guard silently swallowed the
|
|
16783
|
-
* key write and the approval stuck forever — fatal under auto-approval.
|
|
17035
|
+
* Configure the wall-clock budget (ms) applied to every script invocation.
|
|
16784
17036
|
*
|
|
16785
|
-
*
|
|
16786
|
-
*
|
|
16787
|
-
*
|
|
16788
|
-
*
|
|
16789
|
-
|
|
16790
|
-
|
|
16791
|
-
|
|
16792
|
-
/**
|
|
16793
|
-
* When the engine previously held a modal but the latest parse failed
|
|
16794
|
-
* to extract one, we record the timestamp here and only drop the modal
|
|
16795
|
-
* after the configured `approvalCooldown` to avoid flapping between
|
|
16796
|
-
* waiting_approval and generating on every Claude TUI redraw — that
|
|
16797
|
-
* flapping is what fed auto-approve a fresh modal signature on each
|
|
16798
|
-
* paint and made the engine type "1" repeatedly into the prompt.
|
|
17037
|
+
* Out-of-range or non-finite values are clamped to [1, 5000] and the
|
|
17038
|
+
* default (50ms) is used as a fallback. The budget is enforced per-call,
|
|
17039
|
+
* not aggregated — it does not abort a runaway script (Node CJS cannot
|
|
17040
|
+
* interrupt synchronous code without a worker thread). Instead, an
|
|
17041
|
+
* exceeded budget flags the trace entry with `timedOut: true` and emits
|
|
17042
|
+
* a throttled WARN naming the script and elapsed time so an operator
|
|
17043
|
+
* can identify which provider is hanging the settle loop.
|
|
16799
17044
|
*/
|
|
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
|
-
|
|
16828
|
-
|
|
16829
|
-
|
|
16830
|
-
|
|
16831
|
-
|
|
16832
|
-
|
|
17045
|
+
setScriptCallBudget(ms) {
|
|
17046
|
+
if (typeof ms !== "number" || !Number.isFinite(ms)) {
|
|
17047
|
+
this.scriptCallBudgetMs = DEFAULT_SCRIPT_CALL_BUDGET_MS;
|
|
17048
|
+
return;
|
|
17049
|
+
}
|
|
17050
|
+
const clamped = Math.max(1, Math.min(5e3, Math.floor(ms)));
|
|
17051
|
+
this.scriptCallBudgetMs = clamped;
|
|
17052
|
+
}
|
|
17053
|
+
/** Test/debug accessor — current effective budget in ms. */
|
|
17054
|
+
getScriptCallBudgetMs() {
|
|
17055
|
+
return this.scriptCallBudgetMs;
|
|
17056
|
+
}
|
|
17057
|
+
recordTrace(entry) {
|
|
17058
|
+
this.invocationTrace.push(entry);
|
|
17059
|
+
if (this.invocationTrace.length > TRACE_RING_CAPACITY) {
|
|
17060
|
+
this.invocationTrace.splice(0, this.invocationTrace.length - TRACE_RING_CAPACITY);
|
|
17061
|
+
}
|
|
17062
|
+
}
|
|
17063
|
+
// ─── Script lifecycle ─────────────────────────────
|
|
17064
|
+
setScripts(scripts, providerTui) {
|
|
17065
|
+
this.sdk = this.buildSdk(providerTui);
|
|
17066
|
+
const tui = providerTui;
|
|
17067
|
+
const enriched = { ...scripts };
|
|
17068
|
+
if (typeof enriched.detectStatus !== "function" && this.sdk.declarativeDetectStatus) {
|
|
17069
|
+
enriched.detectStatus = this.sdk.declarativeDetectStatus;
|
|
17070
|
+
}
|
|
17071
|
+
if (typeof enriched.parseApproval !== "function" && this.sdk.declarativeParseApproval) {
|
|
17072
|
+
enriched.parseApproval = this.sdk.declarativeParseApproval;
|
|
17073
|
+
}
|
|
17074
|
+
if (typeof enriched.parseSession !== "function" && tui?.transcriptPty) {
|
|
17075
|
+
try {
|
|
17076
|
+
const synth = buildParseSessionFromTui({
|
|
17077
|
+
spinner: tui.spinner,
|
|
17078
|
+
settledPrompt: tui.settledPrompt,
|
|
17079
|
+
modal: tui.modal,
|
|
17080
|
+
dispatchOrder: tui.dispatchOrder,
|
|
17081
|
+
transcriptPty: tui.transcriptPty
|
|
17082
|
+
});
|
|
17083
|
+
enriched.parseSession = ((input) => {
|
|
17084
|
+
const out = synth(input);
|
|
17085
|
+
return {
|
|
17086
|
+
...out,
|
|
17087
|
+
messages: normalizeMessageIdentity(out.messages, out.status ?? "idle")
|
|
17088
|
+
};
|
|
17089
|
+
});
|
|
17090
|
+
} catch (e) {
|
|
17091
|
+
LOG.warn("CLI", `[${this.cliType}] buildParseSessionFromTui failed: ${e?.message || e}`);
|
|
17092
|
+
}
|
|
17093
|
+
}
|
|
17094
|
+
this.scripts = enriched;
|
|
17095
|
+
this._parseErrorMessage = null;
|
|
17096
|
+
this.scriptState = typeof enriched.createState === "function" ? enriched.createState() ?? null : null;
|
|
17097
|
+
}
|
|
17098
|
+
buildSdk(providerTui) {
|
|
17099
|
+
const tui = providerTui;
|
|
17100
|
+
if (!tui) return {};
|
|
17101
|
+
const sdk = {};
|
|
17102
|
+
if (tui.spinner || tui.settledPrompt || tui.modal || tui.dispatchOrder) {
|
|
17103
|
+
try {
|
|
17104
|
+
sdk.declarativeDetectStatus = buildDetectStatusFromTui({
|
|
17105
|
+
spinner: tui.spinner,
|
|
17106
|
+
settledPrompt: tui.settledPrompt,
|
|
17107
|
+
modal: tui.modal,
|
|
17108
|
+
dispatchOrder: tui.dispatchOrder
|
|
17109
|
+
});
|
|
17110
|
+
} catch (e) {
|
|
17111
|
+
LOG.warn("CLI", `[${this.cliType}] buildDetectStatusFromTui failed: ${e?.message || e}`);
|
|
17112
|
+
}
|
|
17113
|
+
}
|
|
17114
|
+
if (tui.modal) {
|
|
17115
|
+
try {
|
|
17116
|
+
sdk.declarativeParseApproval = buildParseApprovalFromTui(tui.modal);
|
|
17117
|
+
} catch (e) {
|
|
17118
|
+
LOG.warn("CLI", `[${this.cliType}] buildParseApprovalFromTui failed: ${e?.message || e}`);
|
|
17119
|
+
}
|
|
17120
|
+
}
|
|
17121
|
+
return sdk;
|
|
17122
|
+
}
|
|
17123
|
+
/** Reset per-session state — called when the PTY process exits. */
|
|
17124
|
+
resetSessionState() {
|
|
17125
|
+
this.scriptState = null;
|
|
17126
|
+
this.invocationTrace = [];
|
|
17127
|
+
this.lastBudgetWarnAt.clear();
|
|
17128
|
+
}
|
|
17129
|
+
// ─── Script access (for reflection and test patching) ────────────────────
|
|
17130
|
+
/** Returns the live scripts object. Direct property assignment on this object
|
|
17131
|
+
* patches individual scripts without replacing others (used in tests). */
|
|
17132
|
+
get cliScripts() {
|
|
17133
|
+
return this.scripts;
|
|
17134
|
+
}
|
|
17135
|
+
// ─── Capability checks ────────────────────────────
|
|
17136
|
+
hasDetectStatus() {
|
|
17137
|
+
return typeof this.scripts.detectStatus === "function";
|
|
17138
|
+
}
|
|
17139
|
+
hasParseSession() {
|
|
17140
|
+
return typeof this.scripts.parseSession === "function";
|
|
17141
|
+
}
|
|
17142
|
+
getScriptNames() {
|
|
17143
|
+
return listCliScriptNames(this.scripts);
|
|
17144
|
+
}
|
|
17145
|
+
// ─── Error state ──────────────────────────────────
|
|
17146
|
+
get parseErrorMessage() {
|
|
17147
|
+
return this._parseErrorMessage;
|
|
17148
|
+
}
|
|
17149
|
+
clearParseError() {
|
|
17150
|
+
this._parseErrorMessage = null;
|
|
17151
|
+
}
|
|
17152
|
+
// ─── Script invocation ────────────────────────────
|
|
17153
|
+
detectStatus(input) {
|
|
17154
|
+
if (!this.scripts.detectStatus) return null;
|
|
17155
|
+
try {
|
|
17156
|
+
return this.invoke("detectStatus", this.scripts.detectStatus, input);
|
|
17157
|
+
} catch (e) {
|
|
17158
|
+
LOG.warn("CLI", `[${this.cliType}] detectStatus error: ${e?.message || e}`);
|
|
17159
|
+
return null;
|
|
17160
|
+
}
|
|
17161
|
+
}
|
|
17162
|
+
parseApproval(input) {
|
|
17163
|
+
if (!this.scripts.parseApproval) return null;
|
|
17164
|
+
try {
|
|
17165
|
+
return this.invoke(
|
|
17166
|
+
"parseApproval",
|
|
17167
|
+
this.scripts.parseApproval,
|
|
17168
|
+
input
|
|
17169
|
+
);
|
|
17170
|
+
} catch (e) {
|
|
17171
|
+
LOG.warn("CLI", `[${this.cliType}] parseApproval error: ${e?.message || e}`);
|
|
17172
|
+
return null;
|
|
17173
|
+
}
|
|
17174
|
+
}
|
|
17175
|
+
parseSession(input) {
|
|
17176
|
+
if (!this.scripts.parseSession) {
|
|
17177
|
+
this._parseErrorMessage = `${this.cliType} parseSession unavailable`;
|
|
17178
|
+
return null;
|
|
17179
|
+
}
|
|
17180
|
+
try {
|
|
17181
|
+
const result = this.invoke("parseSession", this.scripts.parseSession, input);
|
|
17182
|
+
this._parseErrorMessage = null;
|
|
17183
|
+
return result && typeof result === "object" ? result : null;
|
|
17184
|
+
} catch (e) {
|
|
17185
|
+
this._parseErrorMessage = e?.message || String(e);
|
|
17186
|
+
LOG.warn("CLI", `[${this.cliType}] parseSession error: ${this._parseErrorMessage}`);
|
|
17187
|
+
return null;
|
|
17188
|
+
}
|
|
17189
|
+
}
|
|
17190
|
+
/**
|
|
17191
|
+
* Invoke an arbitrary named script (e.g. setModel, openModelPicker).
|
|
17192
|
+
* Throws if the script is not available.
|
|
17193
|
+
*/
|
|
17194
|
+
invokeByName(name, input) {
|
|
17195
|
+
const fn = this.scripts[name];
|
|
17196
|
+
if (typeof fn !== "function") {
|
|
17197
|
+
throw new Error(`CLI script '${name}' not available`);
|
|
17198
|
+
}
|
|
17199
|
+
return this.invoke(name, fn, input);
|
|
17200
|
+
}
|
|
17201
|
+
// ─── Internal ─────────────────────────────────────
|
|
17202
|
+
invoke(scriptName, fn, input) {
|
|
17203
|
+
const arity = fn.length;
|
|
17204
|
+
const startedAt = Date.now();
|
|
17205
|
+
const startedHr = typeof process !== "undefined" && typeof process.hrtime === "function" ? process.hrtime.bigint() : null;
|
|
17206
|
+
let result;
|
|
17207
|
+
try {
|
|
17208
|
+
if (arity >= 3) {
|
|
17209
|
+
result = fn(this.scriptState, input, this.sdk);
|
|
17210
|
+
} else if (arity === 2) {
|
|
17211
|
+
result = fn(this.scriptState, input);
|
|
17212
|
+
} else {
|
|
17213
|
+
result = fn(input);
|
|
17214
|
+
}
|
|
17215
|
+
const elapsedUs = startedHr ? Number((process.hrtime.bigint() - startedHr) / 1000n) : 0;
|
|
17216
|
+
const timedOut = this.checkBudget(scriptName, elapsedUs);
|
|
17217
|
+
this.recordTrace({
|
|
17218
|
+
at: startedAt,
|
|
17219
|
+
scriptName,
|
|
17220
|
+
arity,
|
|
17221
|
+
inputSummary: summarizeInput(input),
|
|
17222
|
+
ok: true,
|
|
17223
|
+
elapsedUs,
|
|
17224
|
+
resultSummary: summarizeResult(result),
|
|
17225
|
+
...timedOut ? { timedOut: true } : {}
|
|
17226
|
+
});
|
|
17227
|
+
return result;
|
|
17228
|
+
} catch (e) {
|
|
17229
|
+
const elapsedUs = startedHr ? Number((process.hrtime.bigint() - startedHr) / 1000n) : 0;
|
|
17230
|
+
const timedOut = this.checkBudget(scriptName, elapsedUs);
|
|
17231
|
+
this.recordTrace({
|
|
17232
|
+
at: startedAt,
|
|
17233
|
+
scriptName,
|
|
17234
|
+
arity,
|
|
17235
|
+
inputSummary: summarizeInput(input),
|
|
17236
|
+
ok: false,
|
|
17237
|
+
elapsedUs,
|
|
17238
|
+
error: e?.message ? String(e.message).slice(0, 400) : String(e).slice(0, 400),
|
|
17239
|
+
...timedOut ? { timedOut: true } : {}
|
|
17240
|
+
});
|
|
17241
|
+
throw e;
|
|
17242
|
+
}
|
|
17243
|
+
}
|
|
17244
|
+
/**
|
|
17245
|
+
* Returns true when `elapsedUs` exceeded the configured budget. On the
|
|
17246
|
+
* first violation per script (or after the throttle window expires) we
|
|
17247
|
+
* emit a single WARN so the operator learns which provider is slow.
|
|
17248
|
+
*
|
|
17249
|
+
* We deliberately throttle per-scriptName so a chronically-slow
|
|
17250
|
+
* detectStatus doesn't spam the log on every PTY frame settle.
|
|
17251
|
+
*/
|
|
17252
|
+
checkBudget(scriptName, elapsedUs) {
|
|
17253
|
+
const budgetUs = this.scriptCallBudgetMs * 1e3;
|
|
17254
|
+
if (elapsedUs <= budgetUs) return false;
|
|
17255
|
+
const now = Date.now();
|
|
17256
|
+
const last = this.lastBudgetWarnAt.get(scriptName) ?? 0;
|
|
17257
|
+
if (now - last >= BUDGET_WARN_THROTTLE_MS) {
|
|
17258
|
+
this.lastBudgetWarnAt.set(scriptName, now);
|
|
17259
|
+
LOG.warn(
|
|
17260
|
+
"CLI",
|
|
17261
|
+
`[${this.cliType}] script ${scriptName} took ${elapsedUs}us, budget ${budgetUs}us`
|
|
17262
|
+
);
|
|
17263
|
+
}
|
|
17264
|
+
return true;
|
|
17265
|
+
}
|
|
17266
|
+
};
|
|
17267
|
+
}
|
|
17268
|
+
});
|
|
17269
|
+
|
|
17270
|
+
// src/cli-adapters/provider-cli-parse.ts
|
|
17271
|
+
function sliceFromOffset(text, start) {
|
|
17272
|
+
if (!text) return "";
|
|
17273
|
+
if (!Number.isFinite(start) || start <= 0) return text;
|
|
17274
|
+
if (start >= text.length) return "";
|
|
17275
|
+
return text.slice(start);
|
|
17276
|
+
}
|
|
17277
|
+
function normalizeCliParsedMessages(parsedMessages, _options) {
|
|
17278
|
+
return Array.isArray(parsedMessages) ? parsedMessages : [];
|
|
17279
|
+
}
|
|
17280
|
+
function buildCliParseInput(options) {
|
|
17281
|
+
const {
|
|
17282
|
+
accumulatedBuffer,
|
|
17283
|
+
accumulatedRawBuffer,
|
|
17284
|
+
recentOutputBuffer,
|
|
17285
|
+
terminalScreenText,
|
|
17286
|
+
workingDir,
|
|
17287
|
+
providerSessionId,
|
|
17288
|
+
historySessionId,
|
|
17289
|
+
baseMessages,
|
|
17290
|
+
partialResponse,
|
|
17291
|
+
isWaitingForResponse,
|
|
17292
|
+
scope,
|
|
17293
|
+
runtimeSettings,
|
|
17294
|
+
spawnAt
|
|
17295
|
+
} = options;
|
|
17296
|
+
const buffer = scope ? sliceFromOffset(accumulatedBuffer, scope.bufferStart) : accumulatedBuffer;
|
|
17297
|
+
const rawBuffer = scope ? sliceFromOffset(accumulatedRawBuffer, scope.rawBufferStart) : accumulatedRawBuffer;
|
|
17298
|
+
const screenText = terminalScreenText;
|
|
17299
|
+
const recentBuffer = buffer.slice(-1e3) || recentOutputBuffer;
|
|
17300
|
+
return {
|
|
17301
|
+
buffer,
|
|
17302
|
+
rawBuffer,
|
|
17303
|
+
recentBuffer,
|
|
17304
|
+
screenText,
|
|
17305
|
+
workspace: workingDir,
|
|
17306
|
+
workingDir,
|
|
17307
|
+
providerSessionId,
|
|
17308
|
+
historySessionId,
|
|
17309
|
+
screen: buildCliScreenSnapshot(screenText),
|
|
17310
|
+
bufferScreen: buildCliScreenSnapshot(buffer),
|
|
17311
|
+
recentScreen: buildCliScreenSnapshot(recentBuffer),
|
|
17312
|
+
messages: [...baseMessages],
|
|
17313
|
+
partialResponse,
|
|
17314
|
+
isWaitingForResponse,
|
|
17315
|
+
promptText: scope?.prompt || "",
|
|
17316
|
+
settings: { ...runtimeSettings },
|
|
17317
|
+
...typeof spawnAt === "number" && spawnAt > 0 ? { spawnAt } : {}
|
|
17318
|
+
};
|
|
17319
|
+
}
|
|
17320
|
+
function summarizeCliTraceText(text, max = 800) {
|
|
17321
|
+
const value = sanitizeTerminalText(String(text || ""));
|
|
17322
|
+
if (value.length <= max) return value;
|
|
17323
|
+
return `\u2026${value.slice(-max)}`;
|
|
17324
|
+
}
|
|
17325
|
+
var init_provider_cli_parse = __esm({
|
|
17326
|
+
"src/cli-adapters/provider-cli-parse.ts"() {
|
|
17327
|
+
"use strict";
|
|
17328
|
+
init_provider_cli_shared();
|
|
17329
|
+
}
|
|
17330
|
+
});
|
|
17331
|
+
|
|
17332
|
+
// src/cli-adapters/cli-state-engine.ts
|
|
17333
|
+
var SCRIPT_STATUS_DEBOUNCE_MS, MAX_FINISH_RETRIES, FINISH_RETRY_DELAY_MS, MAX_TRACE_ENTRIES, APPROVAL_EXIT_TIMEOUT_MS, IDLE_CONFIRMATION_GRACE_MS, CliStateEngine;
|
|
17334
|
+
var init_cli_state_engine = __esm({
|
|
17335
|
+
"src/cli-adapters/cli-state-engine.ts"() {
|
|
17336
|
+
"use strict";
|
|
17337
|
+
init_logger();
|
|
17338
|
+
init_provider_cli_parse();
|
|
17339
|
+
init_provider_cli_shared();
|
|
17340
|
+
SCRIPT_STATUS_DEBOUNCE_MS = 3e3;
|
|
17341
|
+
MAX_FINISH_RETRIES = 2;
|
|
17342
|
+
FINISH_RETRY_DELAY_MS = 300;
|
|
17343
|
+
MAX_TRACE_ENTRIES = 250;
|
|
17344
|
+
APPROVAL_EXIT_TIMEOUT_MS = 6e4;
|
|
17345
|
+
IDLE_CONFIRMATION_GRACE_MS = 2e3;
|
|
17346
|
+
CliStateEngine = class {
|
|
17347
|
+
constructor(provider, runner, transport, callbacks, timeouts) {
|
|
17348
|
+
this.provider = provider;
|
|
17349
|
+
this.runner = runner;
|
|
17350
|
+
this.transport = transport;
|
|
17351
|
+
this.callbacks = callbacks;
|
|
17352
|
+
this.timeouts = timeouts;
|
|
17353
|
+
}
|
|
17354
|
+
// ── Status ───────────────────────────────────────
|
|
17355
|
+
currentStatus = "starting";
|
|
17356
|
+
isWaitingForResponse = false;
|
|
17357
|
+
currentTurnScope = null;
|
|
17358
|
+
activeModal = null;
|
|
17359
|
+
// ── Approval ─────────────────────────────────────
|
|
17360
|
+
lastApprovalResolvedAt = 0;
|
|
17361
|
+
lastResolvedModalMessage = "";
|
|
17362
|
+
/**
|
|
17363
|
+
* Monotonic counter bumped every time the FSM *enters* waiting_approval
|
|
17364
|
+
* with a freshly captured modal (see `applyWaitingApproval`). It is the
|
|
17365
|
+
* single discriminator between "the same approval re-observed across TUI
|
|
17366
|
+
* paint flaps" and "a genuinely new, distinct approval".
|
|
17367
|
+
*
|
|
17368
|
+
* The message-equality cooldown below (`lastResolvedModalMessage`) cannot
|
|
17369
|
+
* tell these apart on its own: claude-cli routinely presents consecutive
|
|
17370
|
+
* approvals whose modal message text is identical (e.g. two back-to-back
|
|
17371
|
+
* Bash-command prompts). When that second approval arrived inside
|
|
17372
|
+
* `approvalCooldown`, the message-equality guard silently swallowed the
|
|
17373
|
+
* key write and the approval stuck forever — fatal under auto-approval.
|
|
17374
|
+
*
|
|
17375
|
+
* `approvalEntrySeq` increments on every fresh entry; `lastResolvedEntrySeq`
|
|
17376
|
+
* records which entry the cooldown belongs to. We only short-circuit the
|
|
17377
|
+
* write when we are still resolving *that same* entry — a new entry (new
|
|
17378
|
+
* seq) is always a real, distinct approval and must be written.
|
|
17379
|
+
*/
|
|
17380
|
+
approvalEntrySeq = 0;
|
|
17381
|
+
lastResolvedEntrySeq = -1;
|
|
17382
|
+
/**
|
|
17383
|
+
* When the engine previously held a modal but the latest parse failed
|
|
17384
|
+
* to extract one, we record the timestamp here and only drop the modal
|
|
17385
|
+
* after the configured `approvalCooldown` to avoid flapping between
|
|
17386
|
+
* waiting_approval and generating on every Claude TUI redraw — that
|
|
17387
|
+
* flapping is what fed auto-approve a fresh modal signature on each
|
|
17388
|
+
* paint and made the engine type "1" repeatedly into the prompt.
|
|
17389
|
+
*/
|
|
17390
|
+
modalLostAt = 0;
|
|
17391
|
+
approvalExitTimeout = null;
|
|
17392
|
+
// ── Response tracking ────────────────────────────
|
|
17393
|
+
responseEpoch = 0;
|
|
17394
|
+
submitPendingUntil = 0;
|
|
17395
|
+
responseSettleIgnoreUntil = 0;
|
|
17396
|
+
submitRetryUsed = false;
|
|
17397
|
+
submitRetryPromptSnippet = "";
|
|
17398
|
+
finishRetryCount = 0;
|
|
17399
|
+
providerErrorMessage = null;
|
|
17400
|
+
providerErrorReason = null;
|
|
17401
|
+
// ── Timers ───────────────────────────────────────
|
|
17402
|
+
settleTimer = null;
|
|
17403
|
+
idleTimeout = null;
|
|
17404
|
+
finishRetryTimer = null;
|
|
17405
|
+
providerErrorRetryTimer = null;
|
|
17406
|
+
providerErrorRetryKey = "";
|
|
17407
|
+
// ── Debounce ─────────────────────────────────────
|
|
17408
|
+
pendingScriptStatus = null;
|
|
17409
|
+
pendingScriptStatusSince = 0;
|
|
17410
|
+
pendingScriptStatusTimer = null;
|
|
17411
|
+
// ── Idle candidate ───────────────────────────────
|
|
17412
|
+
idleFinishCandidate = null;
|
|
17413
|
+
// ── Idle confirmation grace ──────────────────────
|
|
17414
|
+
/**
|
|
17415
|
+
* `finishResponse` produces the `generating → idle` transition that
|
|
17416
|
+
* coordinators interpret as "task complete". Some providers (antigravity-
|
|
17417
|
+
* cli observed in the wild) briefly paint a screen that looks like an
|
|
17418
|
+
* idle prompt between tool result frames while still actively running,
|
|
17419
|
+
* which fired `response_finished` and broke completion semantics.
|
|
17420
|
+
* We defer the actual idle transition by IDLE_CONFIRMATION_GRACE_MS and
|
|
17421
|
+
* cancel it if the scripted detection re-detects generating during that
|
|
17422
|
+
* window — a true completion stays idle for many seconds, so a 2-second
|
|
16833
17423
|
* grace is sufficient to filter the paint blip.
|
|
16834
17424
|
*/
|
|
16835
17425
|
pendingIdleFinishTimer = null;
|
|
@@ -19534,596 +20124,6 @@ ${lastSnapshot}`;
|
|
|
19534
20124
|
}
|
|
19535
20125
|
});
|
|
19536
20126
|
|
|
19537
|
-
// src/providers/spec/evaluator.ts
|
|
19538
|
-
var evaluator_exports = {};
|
|
19539
|
-
__export(evaluator_exports, {
|
|
19540
|
-
evaluateCondition: () => evaluateCondition,
|
|
19541
|
-
extractButtonsFromRule: () => extractButtonsFromRule,
|
|
19542
|
-
extractTitle: () => extractTitle,
|
|
19543
|
-
lastContiguousNumberedBlock: () => lastContiguousNumberedBlock,
|
|
19544
|
-
resolveSections: () => resolveSections,
|
|
19545
|
-
sectionText: () => sectionText
|
|
19546
|
-
});
|
|
19547
|
-
function resolveSize(size, total) {
|
|
19548
|
-
if (size === void 0) return 0;
|
|
19549
|
-
if (typeof size === "number") return Math.max(0, Math.min(total, size));
|
|
19550
|
-
const m = /^(\d+(?:\.\d+)?)%$/.exec(size);
|
|
19551
|
-
if (!m) return 0;
|
|
19552
|
-
const pct = Number(m[1]);
|
|
19553
|
-
return Math.max(0, Math.min(total, Math.round(total * pct / 100)));
|
|
19554
|
-
}
|
|
19555
|
-
function resolveSections(sectionsObj, lines) {
|
|
19556
|
-
const total = lines.length;
|
|
19557
|
-
const anchored = /* @__PURE__ */ new Map();
|
|
19558
|
-
const sectionEntries = Object.entries(sectionsObj);
|
|
19559
|
-
for (const [id, sec] of sectionEntries) {
|
|
19560
|
-
let from = 0;
|
|
19561
|
-
let to = total;
|
|
19562
|
-
if (sec.anchor !== void 0) {
|
|
19563
|
-
try {
|
|
19564
|
-
const anchorPatterns = Array.isArray(sec.anchor) ? sec.anchor : [sec.anchor];
|
|
19565
|
-
const sharedCtx = Array.isArray(sec.anchor_context) ? null : sec.anchor_context ?? null;
|
|
19566
|
-
const ctxList = Array.isArray(sec.anchor_context) ? sec.anchor_context : anchorPatterns.map(() => sharedCtx);
|
|
19567
|
-
const candidates = anchorPatterns.map((pattern, k) => {
|
|
19568
|
-
const ctx = ctxList[k] ?? null;
|
|
19569
|
-
return {
|
|
19570
|
-
re: new RegExp(pattern, sec.anchor_flags ?? ""),
|
|
19571
|
-
prevRe: ctx?.prev !== void 0 ? new RegExp(ctx.prev, ctx.prev_flags ?? "") : null,
|
|
19572
|
-
nextRe: ctx?.next !== void 0 ? new RegExp(ctx.next, ctx.next_flags ?? "") : null
|
|
19573
|
-
};
|
|
19574
|
-
});
|
|
19575
|
-
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]));
|
|
19576
|
-
let idx = -1;
|
|
19577
|
-
for (const c of candidates) {
|
|
19578
|
-
let candIdx = -1;
|
|
19579
|
-
if (sec.anchor_last) {
|
|
19580
|
-
for (let i = total - 1; i >= 0; i--) {
|
|
19581
|
-
if (matchesCandidate(c, i)) {
|
|
19582
|
-
candIdx = i;
|
|
19583
|
-
break;
|
|
19584
|
-
}
|
|
19585
|
-
}
|
|
19586
|
-
} else {
|
|
19587
|
-
for (let i = 0; i < total; i++) {
|
|
19588
|
-
if (matchesCandidate(c, i)) {
|
|
19589
|
-
candIdx = i;
|
|
19590
|
-
break;
|
|
19591
|
-
}
|
|
19592
|
-
}
|
|
19593
|
-
}
|
|
19594
|
-
if (candIdx !== -1 && (idx === -1 || candIdx < idx)) idx = candIdx;
|
|
19595
|
-
}
|
|
19596
|
-
if (idx !== -1) {
|
|
19597
|
-
from = idx;
|
|
19598
|
-
to = total;
|
|
19599
|
-
if (sec.until_regex !== void 0) {
|
|
19600
|
-
try {
|
|
19601
|
-
const ure = new RegExp(sec.until_regex, sec.until_regex_flags ?? "");
|
|
19602
|
-
const end = lines.findIndex((l, i) => i > idx && ure.test(l));
|
|
19603
|
-
if (end !== -1) to = end;
|
|
19604
|
-
} catch {
|
|
19605
|
-
}
|
|
19606
|
-
} else if (sec.lines !== void 0) {
|
|
19607
|
-
to = Math.min(total, from + sec.lines);
|
|
19608
|
-
}
|
|
19609
|
-
}
|
|
19610
|
-
} catch {
|
|
19611
|
-
}
|
|
19612
|
-
} else if (sec.from_top !== void 0) {
|
|
19613
|
-
from = resolveSize(sec.from_top, total);
|
|
19614
|
-
to = total;
|
|
19615
|
-
} else if (sec.from_bottom !== void 0) {
|
|
19616
|
-
const sz = resolveSize(sec.from_bottom, total);
|
|
19617
|
-
from = total - sz;
|
|
19618
|
-
to = total;
|
|
19619
|
-
}
|
|
19620
|
-
anchored.set(id, { fromLine: from, toLine: to });
|
|
19621
|
-
}
|
|
19622
|
-
const resolved = [];
|
|
19623
|
-
for (const [id, sec] of sectionEntries) {
|
|
19624
|
-
let { fromLine, toLine } = anchored.get(id);
|
|
19625
|
-
if (sec.until !== void 0) {
|
|
19626
|
-
if (sec.until.startsWith("^")) {
|
|
19627
|
-
try {
|
|
19628
|
-
const ure = new RegExp(sec.until);
|
|
19629
|
-
const end = lines.findIndex((l, i) => i > fromLine && ure.test(l));
|
|
19630
|
-
if (end !== -1) toLine = end;
|
|
19631
|
-
} catch {
|
|
19632
|
-
}
|
|
19633
|
-
} else {
|
|
19634
|
-
const target = anchored.get(sec.until);
|
|
19635
|
-
if (target) toLine = target.fromLine;
|
|
19636
|
-
}
|
|
19637
|
-
}
|
|
19638
|
-
if (toLine < fromLine) toLine = fromLine;
|
|
19639
|
-
const text = lines.slice(fromLine, toLine).join("\n");
|
|
19640
|
-
resolved.push({ id, fromLine, toLine, text });
|
|
19641
|
-
}
|
|
19642
|
-
return resolved;
|
|
19643
|
-
}
|
|
19644
|
-
function sectionText(sections, sectionId, fullScreen) {
|
|
19645
|
-
if (!sectionId) return fullScreen;
|
|
19646
|
-
const found = sections.find((s2) => s2.id === sectionId);
|
|
19647
|
-
return found ? found.text : "";
|
|
19648
|
-
}
|
|
19649
|
-
function isRegexCondition(c) {
|
|
19650
|
-
return "matches" in c;
|
|
19651
|
-
}
|
|
19652
|
-
function isChangedCondition(c) {
|
|
19653
|
-
return "cursor_above" in c && "changed" in c;
|
|
19654
|
-
}
|
|
19655
|
-
function isAllCondition(c) {
|
|
19656
|
-
return "all" in c;
|
|
19657
|
-
}
|
|
19658
|
-
function isAnyCondition(c) {
|
|
19659
|
-
return "any" in c;
|
|
19660
|
-
}
|
|
19661
|
-
function evaluateCondition(cond, sections, fullScreen, cursor, prevLines, trace, stateId) {
|
|
19662
|
-
if (isAllCondition(cond)) {
|
|
19663
|
-
for (const child of cond.all) {
|
|
19664
|
-
if (!evaluateCondition(child, sections, fullScreen, cursor, prevLines, trace, stateId)) {
|
|
19665
|
-
return false;
|
|
19666
|
-
}
|
|
19667
|
-
}
|
|
19668
|
-
return true;
|
|
19669
|
-
}
|
|
19670
|
-
if (isAnyCondition(cond)) {
|
|
19671
|
-
for (const child of cond.any) {
|
|
19672
|
-
if (evaluateCondition(child, sections, fullScreen, cursor, prevLines, trace, stateId)) {
|
|
19673
|
-
return true;
|
|
19674
|
-
}
|
|
19675
|
-
}
|
|
19676
|
-
return false;
|
|
19677
|
-
}
|
|
19678
|
-
if (isChangedCondition(cond)) {
|
|
19679
|
-
if (!cursor || !prevLines || prevLines.length === 0) return false;
|
|
19680
|
-
const curLines = fullScreen.split("\n");
|
|
19681
|
-
const startRow = Math.max(0, cursor.row - cond.cursor_above);
|
|
19682
|
-
const endRow = cursor.row;
|
|
19683
|
-
const currentSlice = curLines.slice(startRow, endRow).join("\n");
|
|
19684
|
-
const prevSlice = prevLines.slice(startRow, endRow).join("\n");
|
|
19685
|
-
const didChange = currentSlice !== prevSlice;
|
|
19686
|
-
const result = cond.changed ? didChange : !didChange;
|
|
19687
|
-
const stableSuffix = cond.stable_ms != null ? ` stable_ms=${cond.stable_ms}` : "";
|
|
19688
|
-
trace.push({
|
|
19689
|
-
kind: result ? "state_match" : "state_skip",
|
|
19690
|
-
text: `state[${stateId}] changed cond cursor_above=${cond.cursor_above} rows[${startRow},${endRow}) changed=${didChange} expected=${cond.changed}${stableSuffix} result=${result}`
|
|
19691
|
-
});
|
|
19692
|
-
return result;
|
|
19693
|
-
}
|
|
19694
|
-
if (isRegexCondition(cond)) {
|
|
19695
|
-
const haystack = sectionText(sections, cond.section, fullScreen);
|
|
19696
|
-
let matched = false;
|
|
19697
|
-
try {
|
|
19698
|
-
const re = new RegExp(cond.matches, cond.flags ?? "i");
|
|
19699
|
-
matched = re.test(haystack);
|
|
19700
|
-
} catch {
|
|
19701
|
-
matched = false;
|
|
19702
|
-
}
|
|
19703
|
-
if (!matched) {
|
|
19704
|
-
trace.push({ kind: "state_skip", text: `state[${stateId}] regex cond ${cond.section ?? "*"}~/${cond.matches}/ no match` });
|
|
19705
|
-
return false;
|
|
19706
|
-
}
|
|
19707
|
-
if (cursor !== void 0) {
|
|
19708
|
-
if (cond.cursor_row_min !== void 0 && cursor.row < cond.cursor_row_min) {
|
|
19709
|
-
trace.push({ kind: "state_skip", text: `state[${stateId}] cursor row ${cursor.row} < cursor_row_min ${cond.cursor_row_min}` });
|
|
19710
|
-
return false;
|
|
19711
|
-
}
|
|
19712
|
-
if (cond.cursor_row_max !== void 0 && cursor.row > cond.cursor_row_max) {
|
|
19713
|
-
trace.push({ kind: "state_skip", text: `state[${stateId}] cursor row ${cursor.row} > cursor_row_max ${cond.cursor_row_max}` });
|
|
19714
|
-
return false;
|
|
19715
|
-
}
|
|
19716
|
-
if (cond.cursor_col_min !== void 0 && cursor.col < cond.cursor_col_min) {
|
|
19717
|
-
trace.push({ kind: "state_skip", text: `state[${stateId}] cursor col ${cursor.col} < cursor_col_min ${cond.cursor_col_min}` });
|
|
19718
|
-
return false;
|
|
19719
|
-
}
|
|
19720
|
-
if (cond.cursor_col_max !== void 0 && cursor.col > cond.cursor_col_max) {
|
|
19721
|
-
trace.push({ kind: "state_skip", text: `state[${stateId}] cursor col ${cursor.col} > cursor_col_max ${cond.cursor_col_max}` });
|
|
19722
|
-
return false;
|
|
19723
|
-
}
|
|
19724
|
-
}
|
|
19725
|
-
trace.push({ kind: "state_match", text: `state[${stateId}] regex cond ${cond.section ?? "*"}~/${cond.matches}/ matched${cursor !== void 0 ? ` cursor=(${cursor.row},${cursor.col})` : ""}` });
|
|
19726
|
-
return true;
|
|
19727
|
-
}
|
|
19728
|
-
return false;
|
|
19729
|
-
}
|
|
19730
|
-
function extractTitle(rule, sections, fullScreen) {
|
|
19731
|
-
const hay = sectionText(sections, rule.section, fullScreen);
|
|
19732
|
-
if (!hay) return null;
|
|
19733
|
-
if (rule.first_line) {
|
|
19734
|
-
const lines = hay.split("\n");
|
|
19735
|
-
for (const line of lines) {
|
|
19736
|
-
const stripped = line.trim();
|
|
19737
|
-
if (stripped && !/^[─╌═─\s]+$/.test(stripped)) {
|
|
19738
|
-
return stripped;
|
|
19739
|
-
}
|
|
19740
|
-
}
|
|
19741
|
-
return null;
|
|
19742
|
-
}
|
|
19743
|
-
if (rule.regex) {
|
|
19744
|
-
try {
|
|
19745
|
-
const re = new RegExp(rule.regex, rule.flags ?? "i");
|
|
19746
|
-
const m = re.exec(hay);
|
|
19747
|
-
if (m) return (m[1] ?? m[0]).trim();
|
|
19748
|
-
} catch {
|
|
19749
|
-
}
|
|
19750
|
-
}
|
|
19751
|
-
return null;
|
|
19752
|
-
}
|
|
19753
|
-
function compilePattern(ref) {
|
|
19754
|
-
const flags = ref.flags ?? "gm";
|
|
19755
|
-
return new RegExp(ref.pattern, flags.includes("g") ? flags : flags + "g");
|
|
19756
|
-
}
|
|
19757
|
-
function compileLinePattern(ref) {
|
|
19758
|
-
const flags = (ref.flags ?? "m").replace(/g/g, "");
|
|
19759
|
-
return new RegExp(ref.pattern, flags);
|
|
19760
|
-
}
|
|
19761
|
-
function extractButtonsFromRule(rule, hay) {
|
|
19762
|
-
const keyTemplate = rule.key_for_index;
|
|
19763
|
-
const continuationLines = rule.continuation_lines ?? false;
|
|
19764
|
-
const buttons = [];
|
|
19765
|
-
if (continuationLines) {
|
|
19766
|
-
const re = compileLinePattern(rule);
|
|
19767
|
-
const lines = hay.split("\n");
|
|
19768
|
-
for (let i = 0; i < lines.length; i += 1) {
|
|
19769
|
-
const m = re.exec(lines[i]);
|
|
19770
|
-
if (!m) continue;
|
|
19771
|
-
const idx = Number(m[1]);
|
|
19772
|
-
let label = String(m[2] ?? "").trim();
|
|
19773
|
-
if (!Number.isFinite(idx) || idx <= 0 || !label) continue;
|
|
19774
|
-
const current = hasCursorMarker(lines[i]);
|
|
19775
|
-
let j = i + 1;
|
|
19776
|
-
while (j < lines.length) {
|
|
19777
|
-
const next = lines[j];
|
|
19778
|
-
if (!next.trim()) break;
|
|
19779
|
-
if (re.test(next)) break;
|
|
19780
|
-
if (!/^\s+/.test(next)) break;
|
|
19781
|
-
label += " " + next.trim();
|
|
19782
|
-
j += 1;
|
|
19783
|
-
}
|
|
19784
|
-
const key = keyTemplate.replace(/\{index\}/g, String(idx));
|
|
19785
|
-
buttons.push({ index: idx, label, key, current });
|
|
19786
|
-
i = j - 1;
|
|
19787
|
-
}
|
|
19788
|
-
} else {
|
|
19789
|
-
const re = compilePattern(rule);
|
|
19790
|
-
let m;
|
|
19791
|
-
while ((m = re.exec(hay)) !== null) {
|
|
19792
|
-
const idx = Number(m[1]);
|
|
19793
|
-
const label = String(m[2] ?? "").trim();
|
|
19794
|
-
if (!Number.isFinite(idx) || idx <= 0 || !label) continue;
|
|
19795
|
-
const key = keyTemplate.replace(/\{index\}/g, String(idx));
|
|
19796
|
-
buttons.push({ index: idx, label, key, current: hasCursorMarker(m[0]) });
|
|
19797
|
-
}
|
|
19798
|
-
}
|
|
19799
|
-
const block2 = lastContiguousNumberedBlock(buttons);
|
|
19800
|
-
block2.sort((a, b) => a.index - b.index);
|
|
19801
|
-
return block2;
|
|
19802
|
-
}
|
|
19803
|
-
function lastContiguousNumberedBlock(entries) {
|
|
19804
|
-
if (entries.length <= 1) return entries.slice();
|
|
19805
|
-
let start = entries.length - 1;
|
|
19806
|
-
for (let i = entries.length - 1; i > 0; i -= 1) {
|
|
19807
|
-
if (entries[i - 1].index === entries[i].index - 1) start = i - 1;
|
|
19808
|
-
else break;
|
|
19809
|
-
}
|
|
19810
|
-
return entries.slice(start);
|
|
19811
|
-
}
|
|
19812
|
-
function hasCursorMarker(text) {
|
|
19813
|
-
return /^\s*[❯›>]/.test(text);
|
|
19814
|
-
}
|
|
19815
|
-
var init_evaluator = __esm({
|
|
19816
|
-
"src/providers/spec/evaluator.ts"() {
|
|
19817
|
-
"use strict";
|
|
19818
|
-
}
|
|
19819
|
-
});
|
|
19820
|
-
|
|
19821
|
-
// src/providers/spec/fsm-types.ts
|
|
19822
|
-
function isV4Spec(raw) {
|
|
19823
|
-
return !!raw && typeof raw === "object" && raw.$schema === "adhdev:cli/spec@4";
|
|
19824
|
-
}
|
|
19825
|
-
function initialState(spec) {
|
|
19826
|
-
return spec.states.find((s2) => s2.initial) ?? spec.states[0];
|
|
19827
|
-
}
|
|
19828
|
-
function stateById(spec, id) {
|
|
19829
|
-
return spec.states.find((s2) => s2.id === id);
|
|
19830
|
-
}
|
|
19831
|
-
function outgoingTransitions(spec, stateId) {
|
|
19832
|
-
const matches = spec.transitions.filter((t) => {
|
|
19833
|
-
if (t.from === "*") return true;
|
|
19834
|
-
if (Array.isArray(t.from)) return t.from.includes(stateId);
|
|
19835
|
-
return t.from === stateId;
|
|
19836
|
-
});
|
|
19837
|
-
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);
|
|
19838
|
-
}
|
|
19839
|
-
function statusForState(state) {
|
|
19840
|
-
if (state.status) return state.status;
|
|
19841
|
-
if (state.modal) return "approval";
|
|
19842
|
-
if (state.id === "busy" || state.id === "generating") return "generating";
|
|
19843
|
-
return "idle";
|
|
19844
|
-
}
|
|
19845
|
-
function modalKindForState(state) {
|
|
19846
|
-
if (state.modal_kind) return state.modal_kind;
|
|
19847
|
-
if (state.modal) return "approval";
|
|
19848
|
-
return null;
|
|
19849
|
-
}
|
|
19850
|
-
var init_fsm_types = __esm({
|
|
19851
|
-
"src/providers/spec/fsm-types.ts"() {
|
|
19852
|
-
"use strict";
|
|
19853
|
-
}
|
|
19854
|
-
});
|
|
19855
|
-
|
|
19856
|
-
// src/providers/spec/fsm-evaluator.ts
|
|
19857
|
-
var fsm_evaluator_exports = {};
|
|
19858
|
-
__export(fsm_evaluator_exports, {
|
|
19859
|
-
evaluateConditionPreview: () => evaluateConditionPreview,
|
|
19860
|
-
evaluateFsm: () => evaluateFsm
|
|
19861
|
-
});
|
|
19862
|
-
function regionKey(cursorAbove) {
|
|
19863
|
-
return cursorAbove && cursorAbove > 0 ? cursorAbove : WHOLE_SCREEN;
|
|
19864
|
-
}
|
|
19865
|
-
function isRegex(c) {
|
|
19866
|
-
return "matches" in c;
|
|
19867
|
-
}
|
|
19868
|
-
function isChanged(c) {
|
|
19869
|
-
return "cursor_above" in c && "changed" in c;
|
|
19870
|
-
}
|
|
19871
|
-
function isElapsed(c) {
|
|
19872
|
-
return "elapsed_ms" in c;
|
|
19873
|
-
}
|
|
19874
|
-
function isStable(c) {
|
|
19875
|
-
return "stable_ms" in c;
|
|
19876
|
-
}
|
|
19877
|
-
function isAll(c) {
|
|
19878
|
-
return "all" in c;
|
|
19879
|
-
}
|
|
19880
|
-
function isAny(c) {
|
|
19881
|
-
return "any" in c;
|
|
19882
|
-
}
|
|
19883
|
-
function isNot(c) {
|
|
19884
|
-
return "not" in c;
|
|
19885
|
-
}
|
|
19886
|
-
function evalCond(cond, sections, fullScreen, cursor, prevLines, clock, legacyTrace, stateId) {
|
|
19887
|
-
if (isAll(cond)) {
|
|
19888
|
-
const children = cond.all.map((c) => evalCond(c, sections, fullScreen, cursor, prevLines, clock, legacyTrace, stateId));
|
|
19889
|
-
const result = children.every((c) => c.result);
|
|
19890
|
-
const remainingMs = result ? 0 : Math.max(0, ...children.filter((c) => !c.result).map((c) => c.remainingMs ?? 0));
|
|
19891
|
-
return { kind: "all", result, detail: `all(${children.length})`, remainingMs, children };
|
|
19892
|
-
}
|
|
19893
|
-
if (isAny(cond)) {
|
|
19894
|
-
const children = cond.any.map((c) => evalCond(c, sections, fullScreen, cursor, prevLines, clock, legacyTrace, stateId));
|
|
19895
|
-
const result = children.some((c) => c.result);
|
|
19896
|
-
const pending = children.filter((c) => !c.result).map((c) => c.remainingMs ?? Infinity);
|
|
19897
|
-
const remainingMs = result ? 0 : pending.length ? Math.min(...pending) : 0;
|
|
19898
|
-
return { kind: "any", result, detail: `any(${children.length})`, remainingMs: Number.isFinite(remainingMs) ? remainingMs : 0, children };
|
|
19899
|
-
}
|
|
19900
|
-
if (isNot(cond)) {
|
|
19901
|
-
const child = evalCond(cond.not, sections, fullScreen, cursor, prevLines, clock, legacyTrace, stateId);
|
|
19902
|
-
return { kind: "not", result: !child.result, detail: `not`, remainingMs: 0, children: [child] };
|
|
19903
|
-
}
|
|
19904
|
-
if (isElapsed(cond)) {
|
|
19905
|
-
const age = clock.now - clock.stateEnteredAt;
|
|
19906
|
-
const result = age >= cond.elapsed_ms;
|
|
19907
|
-
const remainingMs = result ? 0 : cond.elapsed_ms - age;
|
|
19908
|
-
return { kind: "elapsed", result, detail: `elapsed ${age}ms / ${cond.elapsed_ms}ms`, remainingMs };
|
|
19909
|
-
}
|
|
19910
|
-
if (isStable(cond)) {
|
|
19911
|
-
const key = regionKey(cond.cursor_above);
|
|
19912
|
-
const lastChanged = clock.regionLastChangedAt.get(key) ?? clock.stateEnteredAt;
|
|
19913
|
-
const stableFor = clock.now - lastChanged;
|
|
19914
|
-
const result = stableFor >= cond.stable_ms;
|
|
19915
|
-
const remainingMs = result ? 0 : cond.stable_ms - stableFor;
|
|
19916
|
-
const where = key === WHOLE_SCREEN ? "screen" : `cursor_above=${cond.cursor_above}`;
|
|
19917
|
-
return { kind: "stable", result, detail: `stable ${where} ${stableFor}ms / ${cond.stable_ms}ms`, remainingMs };
|
|
19918
|
-
}
|
|
19919
|
-
if (isRegex(cond) || isChanged(cond)) {
|
|
19920
|
-
const result = evaluateCondition(cond, sections, fullScreen, cursor, prevLines, legacyTrace, stateId);
|
|
19921
|
-
const kind = isRegex(cond) ? "regex" : "changed";
|
|
19922
|
-
const detail = isRegex(cond) ? `${cond.section ?? "*"}~/${cond.matches}/` : `cursor_above=${cond.cursor_above} changed=${cond.changed}`;
|
|
19923
|
-
let matchedText;
|
|
19924
|
-
if (result && isRegex(cond)) {
|
|
19925
|
-
try {
|
|
19926
|
-
const hay = sectionText(sections, cond.section, fullScreen);
|
|
19927
|
-
const re = new RegExp(cond.matches, cond.flags ?? "i");
|
|
19928
|
-
const m = re.exec(hay);
|
|
19929
|
-
if (m && m[0]) matchedText = m[0].replace(/\s+/g, " ").trim().slice(0, 160);
|
|
19930
|
-
} catch {
|
|
19931
|
-
}
|
|
19932
|
-
}
|
|
19933
|
-
return matchedText ? { kind, result, detail, matchedText } : { kind, result, detail };
|
|
19934
|
-
}
|
|
19935
|
-
return { kind: "all", result: false, detail: "unknown condition" };
|
|
19936
|
-
}
|
|
19937
|
-
function fromLabel(t) {
|
|
19938
|
-
const from = Array.isArray(t.from) ? t.from.join("|") : t.from;
|
|
19939
|
-
return t.label ?? `${from}\u2192${t.to}`;
|
|
19940
|
-
}
|
|
19941
|
-
function evaluateFsm(spec, currentStateId, screenText, cursor, prevLines, clock) {
|
|
19942
|
-
const legacyTrace = [];
|
|
19943
|
-
const lines = screenText.split("\n").map((l) => l.endsWith("\r") ? l.slice(0, -1) : l);
|
|
19944
|
-
const cleanScreen = lines.join("\n");
|
|
19945
|
-
const sections = resolveSections(spec.sections ?? {}, lines);
|
|
19946
|
-
const outgoing = outgoingTransitions(spec, currentStateId);
|
|
19947
|
-
const transitions = [];
|
|
19948
|
-
let fired = null;
|
|
19949
|
-
for (const t of outgoing) {
|
|
19950
|
-
const holdMs = t.min_hold_ms ?? 0;
|
|
19951
|
-
const heldFor = clock.now - clock.stateEnteredAt;
|
|
19952
|
-
const holdSatisfied = heldFor >= holdMs;
|
|
19953
|
-
const holdRemainingMs = holdSatisfied ? 0 : holdMs - heldFor;
|
|
19954
|
-
let cond;
|
|
19955
|
-
let condResult = true;
|
|
19956
|
-
if (t.when) {
|
|
19957
|
-
cond = evalCond(t.when, sections, cleanScreen, cursor, prevLines, clock, legacyTrace, `${currentStateId}\u2192${t.to}`);
|
|
19958
|
-
condResult = cond.result;
|
|
19959
|
-
}
|
|
19960
|
-
const fires = holdSatisfied && condResult;
|
|
19961
|
-
const te = {
|
|
19962
|
-
to: t.to,
|
|
19963
|
-
label: fromLabel(t),
|
|
19964
|
-
eligible: true,
|
|
19965
|
-
holdSatisfied,
|
|
19966
|
-
holdRemainingMs,
|
|
19967
|
-
condResult,
|
|
19968
|
-
cond,
|
|
19969
|
-
fires,
|
|
19970
|
-
priority: t.priority ?? 0
|
|
19971
|
-
};
|
|
19972
|
-
transitions.push(te);
|
|
19973
|
-
if (fires && !fired) fired = te;
|
|
19974
|
-
}
|
|
19975
|
-
if (fired && !stateById(spec, fired.to)) {
|
|
19976
|
-
legacyTrace.push({ kind: "state_skip", text: `transition target ${fired.to} not found` });
|
|
19977
|
-
fired = null;
|
|
19978
|
-
}
|
|
19979
|
-
return { sections, transitions, fired, trace: legacyTrace };
|
|
19980
|
-
}
|
|
19981
|
-
function evaluateConditionPreview(cond, sections, screenText, cursor) {
|
|
19982
|
-
const lines = screenText.split("\n").map((l) => l.endsWith("\r") ? l.slice(0, -1) : l);
|
|
19983
|
-
const cleanScreen = lines.join("\n");
|
|
19984
|
-
const resolved = resolveSections(sections ?? {}, lines);
|
|
19985
|
-
const clock = { now: 0, stateEnteredAt: 0, regionLastChangedAt: /* @__PURE__ */ new Map() };
|
|
19986
|
-
return evalCond(cond, resolved, cleanScreen, cursor, void 0, clock, [], "preview");
|
|
19987
|
-
}
|
|
19988
|
-
var WHOLE_SCREEN;
|
|
19989
|
-
var init_fsm_evaluator = __esm({
|
|
19990
|
-
"src/providers/spec/fsm-evaluator.ts"() {
|
|
19991
|
-
"use strict";
|
|
19992
|
-
init_evaluator();
|
|
19993
|
-
init_fsm_types();
|
|
19994
|
-
WHOLE_SCREEN = -1;
|
|
19995
|
-
}
|
|
19996
|
-
});
|
|
19997
|
-
|
|
19998
|
-
// src/providers/spec/fsm-loader.ts
|
|
19999
|
-
var fsm_loader_exports = {};
|
|
20000
|
-
__export(fsm_loader_exports, {
|
|
20001
|
-
loadFsmSpec: () => loadFsmSpec,
|
|
20002
|
-
validateFsmSpec: () => validateFsmSpec
|
|
20003
|
-
});
|
|
20004
|
-
function loadFsmSpec(sourcePath) {
|
|
20005
|
-
let raw;
|
|
20006
|
-
try {
|
|
20007
|
-
raw = JSON.parse(fs10.readFileSync(sourcePath, "utf8"));
|
|
20008
|
-
} catch (err) {
|
|
20009
|
-
return { ok: false, errors: [`Failed to read/parse spec: ${err.message}`], sourcePath };
|
|
20010
|
-
}
|
|
20011
|
-
const errors = validateFsmSpec(raw);
|
|
20012
|
-
if (errors.length) return { ok: false, errors, sourcePath };
|
|
20013
|
-
return { ok: true, spec: raw, sourcePath };
|
|
20014
|
-
}
|
|
20015
|
-
function validateFsmSpec(raw) {
|
|
20016
|
-
const errs = [];
|
|
20017
|
-
if (!isV4Spec(raw)) return ['$schema must be "adhdev:cli/spec@4"'];
|
|
20018
|
-
const spec = raw;
|
|
20019
|
-
if (!spec.id) errs.push("id is required");
|
|
20020
|
-
if (!spec.binary) errs.push("binary is required");
|
|
20021
|
-
if (!spec.send_message?.submit_key) errs.push("send_message.submit_key is required");
|
|
20022
|
-
if (spec.pre_launch_trust !== void 0) {
|
|
20023
|
-
const t = spec.pre_launch_trust;
|
|
20024
|
-
if (!t || typeof t !== "object" || Array.isArray(t)) {
|
|
20025
|
-
errs.push("pre_launch_trust must be an object");
|
|
20026
|
-
} else {
|
|
20027
|
-
if (typeof t.settings_path !== "string" || !t.settings_path) errs.push("pre_launch_trust.settings_path is required");
|
|
20028
|
-
if (typeof t.key !== "string" || !t.key) errs.push("pre_launch_trust.key is required");
|
|
20029
|
-
}
|
|
20030
|
-
}
|
|
20031
|
-
if (spec.refocus_when_stalled_ms !== void 0) {
|
|
20032
|
-
if (typeof spec.refocus_when_stalled_ms !== "number" || !(spec.refocus_when_stalled_ms > 0)) {
|
|
20033
|
-
errs.push("refocus_when_stalled_ms must be a positive number");
|
|
20034
|
-
} else if (!Array.isArray(spec.send_on_spawn) || spec.send_on_spawn.length === 0) {
|
|
20035
|
-
errs.push("refocus_when_stalled_ms requires send_on_spawn (the wake sequence to re-inject)");
|
|
20036
|
-
}
|
|
20037
|
-
}
|
|
20038
|
-
if (!Array.isArray(spec.states) || spec.states.length === 0) {
|
|
20039
|
-
errs.push("states[] must be a non-empty array");
|
|
20040
|
-
return errs;
|
|
20041
|
-
}
|
|
20042
|
-
if (!Array.isArray(spec.transitions)) {
|
|
20043
|
-
errs.push("transitions[] must be an array");
|
|
20044
|
-
return errs;
|
|
20045
|
-
}
|
|
20046
|
-
const ids = /* @__PURE__ */ new Set();
|
|
20047
|
-
let initialCount = 0;
|
|
20048
|
-
for (const [i, s2] of spec.states.entries()) {
|
|
20049
|
-
if (!s2.id) {
|
|
20050
|
-
errs.push(`states[${i}].id is required`);
|
|
20051
|
-
continue;
|
|
20052
|
-
}
|
|
20053
|
-
if (ids.has(s2.id)) errs.push(`states[${i}].id "${s2.id}" is duplicated`);
|
|
20054
|
-
ids.add(s2.id);
|
|
20055
|
-
if (!s2.label) errs.push(`states[${i}].label is required`);
|
|
20056
|
-
if (s2.initial) initialCount += 1;
|
|
20057
|
-
if (s2.status && !["idle", "generating", "approval"].includes(s2.status)) {
|
|
20058
|
-
errs.push(`states[${i}].status "${s2.status}" must be idle|generating|approval`);
|
|
20059
|
-
}
|
|
20060
|
-
}
|
|
20061
|
-
if (initialCount === 0) errs.push("exactly one state must have initial:true (none found)");
|
|
20062
|
-
if (initialCount > 1) errs.push(`exactly one state must have initial:true (${initialCount} found)`);
|
|
20063
|
-
const sectionIds = new Set(Object.keys(spec.sections ?? {}));
|
|
20064
|
-
for (const [i, t] of spec.transitions.entries()) {
|
|
20065
|
-
const froms = t.from === "*" ? [] : Array.isArray(t.from) ? t.from : [t.from];
|
|
20066
|
-
for (const f of froms) {
|
|
20067
|
-
if (!ids.has(f)) errs.push(`transitions[${i}].from references unknown state "${f}"`);
|
|
20068
|
-
}
|
|
20069
|
-
if (t.from !== "*" && froms.length === 0) errs.push(`transitions[${i}].from is required`);
|
|
20070
|
-
if (!t.to) errs.push(`transitions[${i}].to is required`);
|
|
20071
|
-
else if (!ids.has(t.to)) errs.push(`transitions[${i}].to references unknown state "${t.to}"`);
|
|
20072
|
-
if (t.when) errs.push(...validateCondition(t.when, sectionIds, `transitions[${i}].when`));
|
|
20073
|
-
}
|
|
20074
|
-
for (const [i, s2] of spec.states.entries()) {
|
|
20075
|
-
const sec = s2.extract?.title?.section;
|
|
20076
|
-
if (sec && !sectionIds.has(sec)) errs.push(`states[${i}].extract.title.section "${sec}" unknown`);
|
|
20077
|
-
const bsec = s2.extract?.buttons?.section;
|
|
20078
|
-
if (bsec && !sectionIds.has(bsec)) errs.push(`states[${i}].extract.buttons.section "${bsec}" unknown`);
|
|
20079
|
-
}
|
|
20080
|
-
return errs;
|
|
20081
|
-
}
|
|
20082
|
-
function validateCondition(c, sectionIds, path42) {
|
|
20083
|
-
const errs = [];
|
|
20084
|
-
const w = c;
|
|
20085
|
-
if ("all" in w) {
|
|
20086
|
-
w.all.forEach((x, i) => errs.push(...validateCondition(x, sectionIds, `${path42}.all[${i}]`)));
|
|
20087
|
-
return errs;
|
|
20088
|
-
}
|
|
20089
|
-
if ("any" in w) {
|
|
20090
|
-
w.any.forEach((x, i) => errs.push(...validateCondition(x, sectionIds, `${path42}.any[${i}]`)));
|
|
20091
|
-
return errs;
|
|
20092
|
-
}
|
|
20093
|
-
if ("not" in w) {
|
|
20094
|
-
errs.push(...validateCondition(w.not, sectionIds, `${path42}.not`));
|
|
20095
|
-
return errs;
|
|
20096
|
-
}
|
|
20097
|
-
if ("matches" in w) {
|
|
20098
|
-
if (w.section && !sectionIds.has(w.section)) errs.push(`${path42}.section "${w.section}" unknown`);
|
|
20099
|
-
try {
|
|
20100
|
-
new RegExp(w.matches, w.flags ?? "i");
|
|
20101
|
-
} catch (e) {
|
|
20102
|
-
errs.push(`${path42}.matches invalid regex: ${e.message}`);
|
|
20103
|
-
}
|
|
20104
|
-
return errs;
|
|
20105
|
-
}
|
|
20106
|
-
if ("cursor_above" in w && "changed" in w) return errs;
|
|
20107
|
-
if ("elapsed_ms" in w) {
|
|
20108
|
-
if (typeof w.elapsed_ms !== "number") errs.push(`${path42}.elapsed_ms must be a number`);
|
|
20109
|
-
return errs;
|
|
20110
|
-
}
|
|
20111
|
-
if ("stable_ms" in w) {
|
|
20112
|
-
if (typeof w.stable_ms !== "number") errs.push(`${path42}.stable_ms must be a number`);
|
|
20113
|
-
return errs;
|
|
20114
|
-
}
|
|
20115
|
-
errs.push(`${path42} is not a recognized condition`);
|
|
20116
|
-
return errs;
|
|
20117
|
-
}
|
|
20118
|
-
var fs10;
|
|
20119
|
-
var init_fsm_loader = __esm({
|
|
20120
|
-
"src/providers/spec/fsm-loader.ts"() {
|
|
20121
|
-
"use strict";
|
|
20122
|
-
fs10 = __toESM(require("fs"));
|
|
20123
|
-
init_fsm_types();
|
|
20124
|
-
}
|
|
20125
|
-
});
|
|
20126
|
-
|
|
20127
20127
|
// src/providers/sdk/v1/sandbox/require-whitelist.ts
|
|
20128
20128
|
var require_whitelist_exports = {};
|
|
20129
20129
|
__export(require_whitelist_exports, {
|
|
@@ -33053,6 +33053,503 @@ ${formatManifestValidationIssues2(validation.issues)}`,
|
|
|
33053
33053
|
}
|
|
33054
33054
|
};
|
|
33055
33055
|
|
|
33056
|
+
// src/commands/low-family/session-host.ts
|
|
33057
|
+
function toHostedCliRuntimeDescriptor(record) {
|
|
33058
|
+
if (!record || typeof record !== "object") return null;
|
|
33059
|
+
const runtimeId = typeof record.sessionId === "string" ? record.sessionId : "";
|
|
33060
|
+
const cliType = typeof record.providerType === "string" ? record.providerType : "";
|
|
33061
|
+
const workspace = typeof record.workspace === "string" ? record.workspace : "";
|
|
33062
|
+
if (!runtimeId || !cliType || !workspace) return null;
|
|
33063
|
+
return {
|
|
33064
|
+
runtimeId,
|
|
33065
|
+
runtimeKey: typeof record.runtimeKey === "string" ? record.runtimeKey : void 0,
|
|
33066
|
+
displayName: typeof record.displayName === "string" ? record.displayName : void 0,
|
|
33067
|
+
workspaceLabel: typeof record.workspaceLabel === "string" ? record.workspaceLabel : void 0,
|
|
33068
|
+
lifecycle: typeof record.lifecycle === "string" ? record.lifecycle : void 0,
|
|
33069
|
+
recoveryState: typeof record.meta?.runtimeRecoveryState === "string" ? String(record.meta.runtimeRecoveryState) : null,
|
|
33070
|
+
cliType,
|
|
33071
|
+
workspace,
|
|
33072
|
+
cliArgs: Array.isArray(record.meta?.cliArgs) ? record.meta.cliArgs : [],
|
|
33073
|
+
providerSessionId: typeof record.meta?.providerSessionId === "string" ? String(record.meta.providerSessionId) : void 0
|
|
33074
|
+
};
|
|
33075
|
+
}
|
|
33076
|
+
function getWriteConflictOwnerClientId(error) {
|
|
33077
|
+
const message = typeof error === "string" ? error : error instanceof Error ? error.message : "";
|
|
33078
|
+
const match = /^Write owned by\s+(.+)$/.exec(message.trim());
|
|
33079
|
+
return match?.[1]?.trim() || void 0;
|
|
33080
|
+
}
|
|
33081
|
+
function summarizeSessionHostRecord(result) {
|
|
33082
|
+
if (!result || typeof result !== "object") return {};
|
|
33083
|
+
const record = result;
|
|
33084
|
+
return {
|
|
33085
|
+
runtimeKey: typeof record.runtimeKey === "string" ? record.runtimeKey : void 0,
|
|
33086
|
+
lifecycle: typeof record.lifecycle === "string" ? record.lifecycle : void 0,
|
|
33087
|
+
surfaceKind: getSessionHostSurfaceKind(record),
|
|
33088
|
+
attachedClientCount: Array.isArray(record.attachedClients) ? record.attachedClients.length : void 0,
|
|
33089
|
+
hasWriteOwner: !!record.writeOwner,
|
|
33090
|
+
writeOwnerClientId: typeof record.writeOwner?.clientId === "string" ? record.writeOwner.clientId : void 0
|
|
33091
|
+
};
|
|
33092
|
+
}
|
|
33093
|
+
function summarizeSessionHostRecords(result) {
|
|
33094
|
+
const records = Array.isArray(result) ? result : [];
|
|
33095
|
+
const groups = partitionSessionHostRecords(records);
|
|
33096
|
+
return {
|
|
33097
|
+
sessionCount: records.length,
|
|
33098
|
+
liveRuntimeCount: groups.liveRuntimes.length,
|
|
33099
|
+
recoverySnapshotCount: groups.recoverySnapshots.length,
|
|
33100
|
+
inactiveRecordCount: groups.inactiveRecords.length
|
|
33101
|
+
};
|
|
33102
|
+
}
|
|
33103
|
+
function summarizeSessionHostDiagnostics(result) {
|
|
33104
|
+
const diagnostics = result && typeof result === "object" ? result : {};
|
|
33105
|
+
const sessions = Array.isArray(diagnostics.sessions) ? diagnostics.sessions : [];
|
|
33106
|
+
return {
|
|
33107
|
+
runtimeCount: typeof diagnostics.runtimeCount === "number" ? diagnostics.runtimeCount : void 0,
|
|
33108
|
+
...summarizeSessionHostRecords(sessions)
|
|
33109
|
+
};
|
|
33110
|
+
}
|
|
33111
|
+
function summarizeSessionHostPruneResult(result) {
|
|
33112
|
+
const value = result && typeof result === "object" ? result : {};
|
|
33113
|
+
return {
|
|
33114
|
+
duplicateGroupCount: typeof value.duplicateGroupCount === "number" ? value.duplicateGroupCount : void 0,
|
|
33115
|
+
prunedCount: Array.isArray(value.prunedSessionIds) ? value.prunedSessionIds.length : void 0,
|
|
33116
|
+
keptCount: Array.isArray(value.keptSessionIds) ? value.keptSessionIds.length : void 0
|
|
33117
|
+
};
|
|
33118
|
+
}
|
|
33119
|
+
async function traceSessionHostAction(action, args, run, summarizeResult2) {
|
|
33120
|
+
const interactionId = typeof args?._interactionId === "string" ? args._interactionId : void 0;
|
|
33121
|
+
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : void 0;
|
|
33122
|
+
const requestedPayload = { action };
|
|
33123
|
+
if (sessionId) requestedPayload.sessionId = sessionId;
|
|
33124
|
+
if (typeof args?.clientId === "string") requestedPayload.clientId = args.clientId;
|
|
33125
|
+
if (typeof args?.signal === "string") requestedPayload.signal = args.signal;
|
|
33126
|
+
if (typeof args?.providerType === "string") requestedPayload.providerType = args.providerType;
|
|
33127
|
+
if (typeof args?.workspace === "string") requestedPayload.workspace = args.workspace;
|
|
33128
|
+
if (typeof args?.dryRun === "boolean") requestedPayload.dryRun = args.dryRun;
|
|
33129
|
+
recordDebugTrace({
|
|
33130
|
+
interactionId,
|
|
33131
|
+
category: "session_host",
|
|
33132
|
+
stage: "action_requested",
|
|
33133
|
+
level: "info",
|
|
33134
|
+
sessionId,
|
|
33135
|
+
payload: requestedPayload
|
|
33136
|
+
});
|
|
33137
|
+
try {
|
|
33138
|
+
const result = await run();
|
|
33139
|
+
recordDebugTrace({
|
|
33140
|
+
interactionId,
|
|
33141
|
+
category: "session_host",
|
|
33142
|
+
stage: "action_result",
|
|
33143
|
+
level: "info",
|
|
33144
|
+
sessionId,
|
|
33145
|
+
payload: {
|
|
33146
|
+
...requestedPayload,
|
|
33147
|
+
success: true,
|
|
33148
|
+
...summarizeResult2 ? summarizeResult2(result) : {}
|
|
33149
|
+
}
|
|
33150
|
+
});
|
|
33151
|
+
return result;
|
|
33152
|
+
} catch (error) {
|
|
33153
|
+
recordDebugTrace({
|
|
33154
|
+
interactionId,
|
|
33155
|
+
category: "session_host",
|
|
33156
|
+
stage: "action_failed",
|
|
33157
|
+
level: "error",
|
|
33158
|
+
sessionId,
|
|
33159
|
+
payload: {
|
|
33160
|
+
...requestedPayload,
|
|
33161
|
+
error: error?.message || String(error),
|
|
33162
|
+
failureKind: getWriteConflictOwnerClientId(error) ? "write_conflict" : "request_failed",
|
|
33163
|
+
conflictOwnerClientId: getWriteConflictOwnerClientId(error)
|
|
33164
|
+
}
|
|
33165
|
+
});
|
|
33166
|
+
throw error;
|
|
33167
|
+
}
|
|
33168
|
+
}
|
|
33169
|
+
var sessionHostHandlers = {
|
|
33170
|
+
session_host_get_diagnostics: async (ctx, args) => {
|
|
33171
|
+
if (!ctx.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
33172
|
+
const diagnostics = await traceSessionHostAction("session_host_get_diagnostics", args, () => ctx.deps.sessionHostControl.getDiagnostics({
|
|
33173
|
+
includeSessions: args?.includeSessions !== false,
|
|
33174
|
+
limit: Number(args?.limit) || void 0
|
|
33175
|
+
}), (result) => ({
|
|
33176
|
+
includeSessions: args?.includeSessions !== false,
|
|
33177
|
+
limit: Number(args?.limit) || void 0,
|
|
33178
|
+
...summarizeSessionHostDiagnostics(result)
|
|
33179
|
+
}));
|
|
33180
|
+
return { success: true, diagnostics };
|
|
33181
|
+
},
|
|
33182
|
+
session_host_list_sessions: async (ctx, args) => {
|
|
33183
|
+
if (!ctx.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
33184
|
+
const sessions = await traceSessionHostAction("session_host_list_sessions", args, () => ctx.deps.sessionHostControl.listSessions(), (records) => summarizeSessionHostRecords(records));
|
|
33185
|
+
return { success: true, sessions };
|
|
33186
|
+
},
|
|
33187
|
+
session_host_stop_session: async (ctx, args) => {
|
|
33188
|
+
if (!ctx.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
33189
|
+
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
33190
|
+
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
33191
|
+
const record = await traceSessionHostAction("session_host_stop_session", args, () => ctx.deps.sessionHostControl.stopSession(sessionId), (result) => summarizeSessionHostRecord(result));
|
|
33192
|
+
return { success: true, record };
|
|
33193
|
+
},
|
|
33194
|
+
session_host_resume_session: async (ctx, args) => {
|
|
33195
|
+
if (!ctx.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
33196
|
+
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
33197
|
+
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
33198
|
+
const record = await traceSessionHostAction("session_host_resume_session", args, async () => {
|
|
33199
|
+
const nextRecord = await ctx.deps.sessionHostControl.resumeSession(sessionId);
|
|
33200
|
+
const hosted = toHostedCliRuntimeDescriptor(nextRecord);
|
|
33201
|
+
if (hosted) {
|
|
33202
|
+
await ctx.deps.cliManager.restoreHostedSessions([hosted]);
|
|
33203
|
+
}
|
|
33204
|
+
return nextRecord;
|
|
33205
|
+
}, (result) => ({
|
|
33206
|
+
...summarizeSessionHostRecord(result),
|
|
33207
|
+
restoredHostedSession: !!toHostedCliRuntimeDescriptor(result)
|
|
33208
|
+
}));
|
|
33209
|
+
return { success: true, record };
|
|
33210
|
+
},
|
|
33211
|
+
session_host_restart_session: async (ctx, args) => {
|
|
33212
|
+
if (!ctx.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
33213
|
+
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
33214
|
+
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
33215
|
+
const record = await traceSessionHostAction("session_host_restart_session", args, async () => {
|
|
33216
|
+
const nextRecord = await ctx.deps.sessionHostControl.restartSession(sessionId);
|
|
33217
|
+
const hosted = toHostedCliRuntimeDescriptor(nextRecord);
|
|
33218
|
+
if (hosted) {
|
|
33219
|
+
await ctx.deps.cliManager.restoreHostedSessions([hosted]);
|
|
33220
|
+
}
|
|
33221
|
+
return nextRecord;
|
|
33222
|
+
}, (result) => ({
|
|
33223
|
+
...summarizeSessionHostRecord(result),
|
|
33224
|
+
restoredHostedSession: !!toHostedCliRuntimeDescriptor(result)
|
|
33225
|
+
}));
|
|
33226
|
+
return { success: true, record };
|
|
33227
|
+
},
|
|
33228
|
+
session_host_send_signal: async (ctx, args) => {
|
|
33229
|
+
if (!ctx.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
33230
|
+
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
33231
|
+
const signal = typeof args?.signal === "string" ? args.signal : "";
|
|
33232
|
+
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
33233
|
+
if (!signal) return { success: false, error: "signal required" };
|
|
33234
|
+
const record = await traceSessionHostAction("session_host_send_signal", args, () => ctx.deps.sessionHostControl.sendSignal(sessionId, signal), (result) => summarizeSessionHostRecord(result));
|
|
33235
|
+
return { success: true, record };
|
|
33236
|
+
},
|
|
33237
|
+
session_host_force_detach_client: async (ctx, args) => {
|
|
33238
|
+
if (!ctx.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
33239
|
+
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
33240
|
+
const clientId = typeof args?.clientId === "string" ? args.clientId : "";
|
|
33241
|
+
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
33242
|
+
if (!clientId) return { success: false, error: "clientId required" };
|
|
33243
|
+
const record = await traceSessionHostAction("session_host_force_detach_client", args, () => ctx.deps.sessionHostControl.forceDetachClient(sessionId, clientId), (result) => summarizeSessionHostRecord(result));
|
|
33244
|
+
return { success: true, record };
|
|
33245
|
+
},
|
|
33246
|
+
session_host_prune_duplicate_sessions: async (ctx, args) => {
|
|
33247
|
+
if (!ctx.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
33248
|
+
const result = await traceSessionHostAction("session_host_prune_duplicate_sessions", args, () => ctx.deps.sessionHostControl.pruneDuplicateSessions({
|
|
33249
|
+
providerType: typeof args?.providerType === "string" ? args.providerType : void 0,
|
|
33250
|
+
workspace: typeof args?.workspace === "string" ? args.workspace : void 0,
|
|
33251
|
+
dryRun: args?.dryRun === true
|
|
33252
|
+
}), (value) => summarizeSessionHostPruneResult(value));
|
|
33253
|
+
return { success: true, result };
|
|
33254
|
+
},
|
|
33255
|
+
session_host_acquire_write: async (ctx, args) => {
|
|
33256
|
+
if (!ctx.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
33257
|
+
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
33258
|
+
const clientId = typeof args?.clientId === "string" ? args.clientId : "";
|
|
33259
|
+
const ownerType = args?.ownerType === "agent" ? "agent" : "user";
|
|
33260
|
+
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
33261
|
+
if (!clientId) return { success: false, error: "clientId required" };
|
|
33262
|
+
const record = await traceSessionHostAction("session_host_acquire_write", args, () => ctx.deps.sessionHostControl.acquireWrite({
|
|
33263
|
+
sessionId,
|
|
33264
|
+
clientId,
|
|
33265
|
+
ownerType,
|
|
33266
|
+
force: args?.force !== false
|
|
33267
|
+
}), (result) => ({
|
|
33268
|
+
...summarizeSessionHostRecord(result),
|
|
33269
|
+
ownerType
|
|
33270
|
+
}));
|
|
33271
|
+
return { success: true, record };
|
|
33272
|
+
},
|
|
33273
|
+
session_host_release_write: async (ctx, args) => {
|
|
33274
|
+
if (!ctx.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
33275
|
+
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
33276
|
+
const clientId = typeof args?.clientId === "string" ? args.clientId : "";
|
|
33277
|
+
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
33278
|
+
if (!clientId) return { success: false, error: "clientId required" };
|
|
33279
|
+
const record = await traceSessionHostAction("session_host_release_write", args, () => ctx.deps.sessionHostControl.releaseWrite({
|
|
33280
|
+
sessionId,
|
|
33281
|
+
clientId
|
|
33282
|
+
}), (result) => summarizeSessionHostRecord(result));
|
|
33283
|
+
return { success: true, record };
|
|
33284
|
+
}
|
|
33285
|
+
};
|
|
33286
|
+
|
|
33287
|
+
// src/commands/low-family/spec-providerdev.ts
|
|
33288
|
+
function resolveSpecPathInProviders(specPath, fsm, pathm, osm) {
|
|
33289
|
+
let rootReal;
|
|
33290
|
+
try {
|
|
33291
|
+
rootReal = fsm.realpathSync(pathm.join(osm.homedir(), ".adhdev", "providers"));
|
|
33292
|
+
} catch (e) {
|
|
33293
|
+
return { ok: false, error: `providers root unavailable: ${e.message}` };
|
|
33294
|
+
}
|
|
33295
|
+
const resolved = pathm.resolve(specPath);
|
|
33296
|
+
const base = pathm.basename(resolved);
|
|
33297
|
+
if (!/^[\w.-]+\.json$/.test(base)) {
|
|
33298
|
+
return { ok: false, error: "refused: spec file must be a *.json basename" };
|
|
33299
|
+
}
|
|
33300
|
+
let parentReal;
|
|
33301
|
+
try {
|
|
33302
|
+
parentReal = fsm.realpathSync(pathm.dirname(resolved));
|
|
33303
|
+
} catch (e) {
|
|
33304
|
+
return { ok: false, error: `spec directory not found: ${e.message}` };
|
|
33305
|
+
}
|
|
33306
|
+
if (parentReal !== rootReal && !parentReal.startsWith(rootReal + pathm.sep)) {
|
|
33307
|
+
return { ok: false, error: "refused: spec path must be under the providers root" };
|
|
33308
|
+
}
|
|
33309
|
+
const safe = pathm.join(parentReal, base);
|
|
33310
|
+
try {
|
|
33311
|
+
const st = fsm.lstatSync(safe);
|
|
33312
|
+
if (st.isSymbolicLink()) return { ok: false, error: "refused: spec path is a symlink" };
|
|
33313
|
+
} catch {
|
|
33314
|
+
}
|
|
33315
|
+
return { ok: true, path: safe };
|
|
33316
|
+
}
|
|
33317
|
+
var specProviderDevHandlers = {
|
|
33318
|
+
get_spec_debug: async (ctx, args) => {
|
|
33319
|
+
const sessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : typeof args?.sessionId === "string" ? args.sessionId.trim() : "";
|
|
33320
|
+
if (!sessionId) return { success: false, error: "targetSessionId required" };
|
|
33321
|
+
const target = ctx.deps.sessionRegistry.get(sessionId);
|
|
33322
|
+
if (!target) return { success: false, error: "Session not found", sessionId };
|
|
33323
|
+
const adapterObj = ctx.deps.cliManager.findAdapter(target.providerType, { instanceKey: sessionId })?.adapter;
|
|
33324
|
+
const snapshot = adapterObj ? typeof adapterObj.getDebugSnapshot === "function" ? adapterObj.getDebugSnapshot() : typeof adapterObj.getDebugState === "function" ? adapterObj.getDebugState() : null : null;
|
|
33325
|
+
return {
|
|
33326
|
+
success: true,
|
|
33327
|
+
sessionId,
|
|
33328
|
+
providerType: target.providerType,
|
|
33329
|
+
isSpecProvider: snapshot !== null,
|
|
33330
|
+
snapshot
|
|
33331
|
+
};
|
|
33332
|
+
},
|
|
33333
|
+
// ── Spec source read/write for the debug panel's live editor.
|
|
33334
|
+
// Lets the dashboard load a session's spec.json, edit it, and save it back —
|
|
33335
|
+
// the driver's fs.watch picks up the change and hot-reloads the FSM with no
|
|
33336
|
+
// restart. Writes are confined to files under ~/.adhdev/providers.
|
|
33337
|
+
get_spec_source: async (ctx, args) => {
|
|
33338
|
+
const fsm = await import("fs");
|
|
33339
|
+
const pathm = await import("path");
|
|
33340
|
+
const osm = await import("os");
|
|
33341
|
+
const sessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : typeof args?.sessionId === "string" ? args.sessionId.trim() : "";
|
|
33342
|
+
let specPath = typeof args?.specPath === "string" ? args.specPath : "";
|
|
33343
|
+
if (!specPath && sessionId) {
|
|
33344
|
+
const target = ctx.deps.sessionRegistry.get(sessionId);
|
|
33345
|
+
const adapterObj = target ? ctx.deps.cliManager.findAdapter(target.providerType, { instanceKey: sessionId })?.adapter : null;
|
|
33346
|
+
const snap = adapterObj && typeof adapterObj.getDebugSnapshot === "function" ? adapterObj.getDebugSnapshot() : null;
|
|
33347
|
+
specPath = snap?.specPath ?? "";
|
|
33348
|
+
}
|
|
33349
|
+
if (!specPath) return { success: false, error: "specPath or resolvable targetSessionId required" };
|
|
33350
|
+
const safe = resolveSpecPathInProviders(specPath, fsm, pathm, osm);
|
|
33351
|
+
if (!safe.ok) return { success: false, error: safe.error, specPath };
|
|
33352
|
+
try {
|
|
33353
|
+
const content = fsm.readFileSync(safe.path, "utf8");
|
|
33354
|
+
return { success: true, specPath: safe.path, content };
|
|
33355
|
+
} catch (e) {
|
|
33356
|
+
return { success: false, error: `read failed: ${e.message}`, specPath };
|
|
33357
|
+
}
|
|
33358
|
+
},
|
|
33359
|
+
write_spec_source: async (_ctx, args) => {
|
|
33360
|
+
const fsm = await import("fs");
|
|
33361
|
+
const pathm = await import("path");
|
|
33362
|
+
const osm = await import("os");
|
|
33363
|
+
const specPath = typeof args?.specPath === "string" ? args.specPath : "";
|
|
33364
|
+
const content = typeof args?.content === "string" ? args.content : "";
|
|
33365
|
+
if (!specPath) return { success: false, error: "specPath required" };
|
|
33366
|
+
if (!content) return { success: false, error: "content required" };
|
|
33367
|
+
const safe = resolveSpecPathInProviders(specPath, fsm, pathm, osm);
|
|
33368
|
+
if (!safe.ok) return { success: false, error: safe.error };
|
|
33369
|
+
let parsed;
|
|
33370
|
+
try {
|
|
33371
|
+
parsed = JSON.parse(content);
|
|
33372
|
+
} catch (e) {
|
|
33373
|
+
return { success: false, error: `invalid JSON: ${e.message}` };
|
|
33374
|
+
}
|
|
33375
|
+
if (parsed?.$schema === "adhdev:cli/spec@4") {
|
|
33376
|
+
const { validateFsmSpec: validateFsmSpec2 } = await Promise.resolve().then(() => (init_fsm_loader(), fsm_loader_exports));
|
|
33377
|
+
const errs = validateFsmSpec2(parsed);
|
|
33378
|
+
if (errs.length) return { success: false, error: "spec invalid", validationErrors: errs };
|
|
33379
|
+
}
|
|
33380
|
+
try {
|
|
33381
|
+
fsm.writeFileSync(safe.path, content, "utf8");
|
|
33382
|
+
return { success: true, specPath: safe.path };
|
|
33383
|
+
} catch (e) {
|
|
33384
|
+
return { success: false, error: `write failed: ${e.message}` };
|
|
33385
|
+
}
|
|
33386
|
+
},
|
|
33387
|
+
// ── Validate an in-progress spec (string or object) without writing. The form
|
|
33388
|
+
// builder calls this on every change so Save can stay disabled while there
|
|
33389
|
+
// are structural / reference / regex errors.
|
|
33390
|
+
validate_spec: async (_ctx, args) => {
|
|
33391
|
+
let parsed = args?.spec;
|
|
33392
|
+
if (typeof args?.content === "string") {
|
|
33393
|
+
try {
|
|
33394
|
+
parsed = JSON.parse(args.content);
|
|
33395
|
+
} catch (e) {
|
|
33396
|
+
return { success: true, valid: false, errors: [`invalid JSON: ${e.message}`] };
|
|
33397
|
+
}
|
|
33398
|
+
}
|
|
33399
|
+
if (!parsed || typeof parsed !== "object") {
|
|
33400
|
+
return { success: true, valid: false, errors: ["spec must be an object or content string"] };
|
|
33401
|
+
}
|
|
33402
|
+
const schema = parsed.$schema;
|
|
33403
|
+
if (schema === "adhdev:cli/spec@4") {
|
|
33404
|
+
const { validateFsmSpec: validateFsmSpec2 } = await Promise.resolve().then(() => (init_fsm_loader(), fsm_loader_exports));
|
|
33405
|
+
const errors = validateFsmSpec2(parsed);
|
|
33406
|
+
return { success: true, valid: errors.length === 0, errors };
|
|
33407
|
+
}
|
|
33408
|
+
return { success: true, valid: false, errors: [`unsupported $schema "${schema}" \u2014 form builder is v4-only`] };
|
|
33409
|
+
},
|
|
33410
|
+
// ── Evaluate a single condition against a live session's current screen —
|
|
33411
|
+
// powers the editor's "does this match right now?" preview. Returns the
|
|
33412
|
+
// recursive match tree.
|
|
33413
|
+
eval_condition_preview: async (ctx, args) => {
|
|
33414
|
+
const sessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : typeof args?.sessionId === "string" ? args.sessionId.trim() : "";
|
|
33415
|
+
if (!sessionId) return { success: false, error: "targetSessionId required" };
|
|
33416
|
+
if (!args?.condition || typeof args.condition !== "object") return { success: false, error: "condition required" };
|
|
33417
|
+
const target = ctx.deps.sessionRegistry.get(sessionId);
|
|
33418
|
+
if (!target) return { success: false, error: "Session not found", sessionId };
|
|
33419
|
+
const adapterObj = ctx.deps.cliManager.findAdapter(target.providerType, { instanceKey: sessionId })?.adapter;
|
|
33420
|
+
const snap = adapterObj && typeof adapterObj.getDebugSnapshot === "function" ? adapterObj.getDebugSnapshot() : null;
|
|
33421
|
+
if (!snap?.screen) return { success: false, error: "no live screen for session" };
|
|
33422
|
+
let sectionsDef;
|
|
33423
|
+
try {
|
|
33424
|
+
const fsm2 = await import("fs");
|
|
33425
|
+
if (snap.specPath) {
|
|
33426
|
+
const raw = JSON.parse(fsm2.readFileSync(snap.specPath, "utf8"));
|
|
33427
|
+
sectionsDef = raw?.sections;
|
|
33428
|
+
}
|
|
33429
|
+
} catch {
|
|
33430
|
+
}
|
|
33431
|
+
const { evaluateConditionPreview: evaluateConditionPreview2 } = await Promise.resolve().then(() => (init_fsm_evaluator(), fsm_evaluator_exports));
|
|
33432
|
+
try {
|
|
33433
|
+
const result = evaluateConditionPreview2(
|
|
33434
|
+
args.condition,
|
|
33435
|
+
sectionsDef,
|
|
33436
|
+
snap.screen,
|
|
33437
|
+
snap.cursorPosition ?? void 0
|
|
33438
|
+
);
|
|
33439
|
+
return { success: true, result, sections: snap.sections ?? null };
|
|
33440
|
+
} catch (e) {
|
|
33441
|
+
return { success: false, error: `eval failed: ${e.message}` };
|
|
33442
|
+
}
|
|
33443
|
+
},
|
|
33444
|
+
// ── Resolve a sections map against a live session's screen — the section
|
|
33445
|
+
// editor's "test" button. Returns, for each section id, the line range + the
|
|
33446
|
+
// text it captures. Accepts an in-progress sections map so it previews
|
|
33447
|
+
// unsaved edits.
|
|
33448
|
+
resolve_section_preview: async (ctx, args) => {
|
|
33449
|
+
const sessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : typeof args?.sessionId === "string" ? args.sessionId.trim() : "";
|
|
33450
|
+
if (!sessionId) return { success: false, error: "targetSessionId required" };
|
|
33451
|
+
if (!args?.sections || typeof args.sections !== "object") return { success: false, error: "sections map required" };
|
|
33452
|
+
const target = ctx.deps.sessionRegistry.get(sessionId);
|
|
33453
|
+
if (!target) return { success: false, error: "Session not found", sessionId };
|
|
33454
|
+
const adapterObj = ctx.deps.cliManager.findAdapter(target.providerType, { instanceKey: sessionId })?.adapter;
|
|
33455
|
+
const snap = adapterObj && typeof adapterObj.getDebugSnapshot === "function" ? adapterObj.getDebugSnapshot() : null;
|
|
33456
|
+
if (!snap?.screen) return { success: false, error: "no live screen for session" };
|
|
33457
|
+
const { resolveSections: resolveSections2 } = await Promise.resolve().then(() => (init_evaluator(), evaluator_exports));
|
|
33458
|
+
try {
|
|
33459
|
+
const lines = String(snap.screen).split("\n").map((l) => l.endsWith("\r") ? l.slice(0, -1) : l);
|
|
33460
|
+
const resolved = resolveSections2(args.sections, lines);
|
|
33461
|
+
return {
|
|
33462
|
+
success: true,
|
|
33463
|
+
screenLineCount: lines.length,
|
|
33464
|
+
sections: resolved.map((s2) => ({ id: s2.id, fromLine: s2.fromLine, toLine: s2.toLine, text: s2.text }))
|
|
33465
|
+
};
|
|
33466
|
+
} catch (e) {
|
|
33467
|
+
return { success: false, error: `resolve failed: ${e.message}` };
|
|
33468
|
+
}
|
|
33469
|
+
}
|
|
33470
|
+
};
|
|
33471
|
+
|
|
33472
|
+
// src/commands/low-family/refine-config.ts
|
|
33473
|
+
init_change_impact_config();
|
|
33474
|
+
var refineConfigHandlers = {
|
|
33475
|
+
get_mesh_refine_config_schema: async (_ctx, _args) => {
|
|
33476
|
+
return {
|
|
33477
|
+
success: true,
|
|
33478
|
+
schema: MESH_REFINE_CONFIG_SCHEMA,
|
|
33479
|
+
locations: MESH_REFINE_CONFIG_LOCATIONS,
|
|
33480
|
+
worktreeBootstrap: {
|
|
33481
|
+
schema: MESH_WORKTREE_BOOTSTRAP_CONFIG_SCHEMA,
|
|
33482
|
+
locations: MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS,
|
|
33483
|
+
sourceOfTruth: "repo worktree bootstrap config",
|
|
33484
|
+
runBehavior: "When present and enabled, clone_mesh_node runs commands after submodule initialization and records status on the worktree node."
|
|
33485
|
+
},
|
|
33486
|
+
sourceOfTruth: "repo mesh/refine config",
|
|
33487
|
+
heuristicRole: "suggestions_only_not_execution_path"
|
|
33488
|
+
};
|
|
33489
|
+
},
|
|
33490
|
+
validate_mesh_refine_config: async (_ctx, args) => {
|
|
33491
|
+
const workspace = typeof args?.workspace === "string" ? args.workspace : process.cwd();
|
|
33492
|
+
const mesh = args?.inlineMesh || {};
|
|
33493
|
+
const loaded = args?.config !== void 0 ? { config: args.config, source: "inline", sourceType: "mesh_policy" } : loadMeshRefineConfig(mesh, workspace);
|
|
33494
|
+
const validation = loaded.config ? validateMeshRefineConfig(loaded.config, loaded.source) : { valid: false, errors: [loaded.error || "repo mesh/refine config unavailable"], commands: [], rejectedCommands: [] };
|
|
33495
|
+
return { success: validation.valid, ...loaded, ...validation };
|
|
33496
|
+
},
|
|
33497
|
+
suggest_mesh_refine_config: async (_ctx, args) => {
|
|
33498
|
+
const workspace = typeof args?.workspace === "string" ? args.workspace : process.cwd();
|
|
33499
|
+
const mesh = args?.inlineMesh || {};
|
|
33500
|
+
return {
|
|
33501
|
+
success: true,
|
|
33502
|
+
...suggestMeshRefineConfig(mesh, workspace),
|
|
33503
|
+
note: "Suggestions are heuristic scaffold only; Refinery will not execute them until saved into repo mesh/refine config."
|
|
33504
|
+
};
|
|
33505
|
+
},
|
|
33506
|
+
get_mesh_change_impact_config_schema: async (_ctx, _args) => {
|
|
33507
|
+
return {
|
|
33508
|
+
success: true,
|
|
33509
|
+
schema: CHANGE_IMPACT_CONFIG_SCHEMA,
|
|
33510
|
+
locations: CHANGE_IMPACT_CONFIG_LOCATIONS,
|
|
33511
|
+
sourceOfTruth: "repo change-impact config",
|
|
33512
|
+
heuristicRole: "suggestions_only_not_execution_path",
|
|
33513
|
+
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."
|
|
33514
|
+
};
|
|
33515
|
+
},
|
|
33516
|
+
validate_mesh_change_impact_config: async (_ctx, args) => {
|
|
33517
|
+
const workspace = typeof args?.workspace === "string" ? args.workspace : process.cwd();
|
|
33518
|
+
if (args?.config !== void 0) {
|
|
33519
|
+
const validation = validateChangeImpactConfig(args.config, "inline");
|
|
33520
|
+
return { success: validation.valid, source: "inline", sourceType: "mesh_policy", ...validation };
|
|
33521
|
+
}
|
|
33522
|
+
const loaded = loadChangeImpactConfig(workspace);
|
|
33523
|
+
if (loaded.sourceType === "repo_file") {
|
|
33524
|
+
const validation = validateChangeImpactConfig(loaded.config, loaded.source);
|
|
33525
|
+
return { success: validation.valid, ...loaded, ...validation };
|
|
33526
|
+
}
|
|
33527
|
+
return {
|
|
33528
|
+
success: false,
|
|
33529
|
+
...loaded,
|
|
33530
|
+
valid: false,
|
|
33531
|
+
errors: [loaded.error || "repo change-impact config unavailable"]
|
|
33532
|
+
};
|
|
33533
|
+
},
|
|
33534
|
+
suggest_mesh_change_impact_config: async (_ctx, args) => {
|
|
33535
|
+
const workspace = typeof args?.workspace === "string" ? args.workspace : process.cwd();
|
|
33536
|
+
return {
|
|
33537
|
+
success: true,
|
|
33538
|
+
...suggestChangeImpactConfig(workspace),
|
|
33539
|
+
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."
|
|
33540
|
+
};
|
|
33541
|
+
}
|
|
33542
|
+
};
|
|
33543
|
+
|
|
33544
|
+
// src/commands/low-family/index.ts
|
|
33545
|
+
var lowFamilyRegistry = new Map(
|
|
33546
|
+
Object.entries({
|
|
33547
|
+
...sessionHostHandlers,
|
|
33548
|
+
...specProviderDevHandlers,
|
|
33549
|
+
...refineConfigHandlers
|
|
33550
|
+
})
|
|
33551
|
+
);
|
|
33552
|
+
|
|
33056
33553
|
// src/commands/cli-manager.ts
|
|
33057
33554
|
var os20 = __toESM(require("os"));
|
|
33058
33555
|
var path26 = __toESM(require("path"));
|
|
@@ -36588,7 +37085,13 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
36588
37085
|
this.settings = {
|
|
36589
37086
|
...this.settings,
|
|
36590
37087
|
meshNodeFor: assignment.meshId,
|
|
36591
|
-
|
|
37088
|
+
// WTCLAIM (A): track the bound node id under BOTH the active marker
|
|
37089
|
+
// (meshNodeId, cleared on detach) and a sticky marker (meshLastNodeId,
|
|
37090
|
+
// preserved across detach). The sticky marker lets a detached but still
|
|
37091
|
+
// coordinator-owned session be re-picked ONLY for the SAME node it served
|
|
37092
|
+
// — never auto-adopted for a sibling node (e.g. a cloned worktree) that
|
|
37093
|
+
// shares this daemon. See isMeshOwnedDelegateSession's post-detach gate.
|
|
37094
|
+
...assignment.nodeId ? { meshNodeId: assignment.nodeId, meshLastNodeId: assignment.nodeId } : {},
|
|
36592
37095
|
...assignment.taskId ? { meshActiveTaskId: assignment.taskId } : {},
|
|
36593
37096
|
...assignment.coordinatorDaemonId ? { meshCoordinatorDaemonId: assignment.coordinatorDaemonId } : {},
|
|
36594
37097
|
// Session-level routing anchor: the originating coordinator session, so this
|
|
@@ -36607,9 +37110,9 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
36607
37110
|
if (!this.settings.meshNodeFor && !this.settings.meshActiveTaskId && !this.settings.meshNodeId) return;
|
|
36608
37111
|
const { meshNodeFor, meshNodeId, meshActiveTaskId, ...rest } = this.settings;
|
|
36609
37112
|
void meshNodeFor;
|
|
36610
|
-
void meshNodeId;
|
|
36611
37113
|
void meshActiveTaskId;
|
|
36612
|
-
|
|
37114
|
+
const lastNodeId = typeof meshNodeId === "string" && meshNodeId.trim() ? meshNodeId.trim() : typeof rest.meshLastNodeId === "string" && rest.meshLastNodeId.trim() ? rest.meshLastNodeId.trim() : void 0;
|
|
37115
|
+
this.settings = lastNodeId ? { ...rest, meshLastNodeId: lastNodeId } : rest;
|
|
36613
37116
|
this.adapter.updateRuntimeSettings?.(this.settings);
|
|
36614
37117
|
}
|
|
36615
37118
|
/**
|
|
@@ -39280,6 +39783,10 @@ function hasCompletedStartingLaunch(adapter) {
|
|
|
39280
39783
|
if (hasNonEmptyModalButtons2(adapterStatus?.activeModal ?? adapterStatus?.modal ?? parsedStatus?.activeModal ?? parsedStatus?.modal)) return false;
|
|
39281
39784
|
return hasFinalAssistantMessage(parsedStatus?.messages);
|
|
39282
39785
|
}
|
|
39786
|
+
function normalizeDirForCompare(dir) {
|
|
39787
|
+
if (typeof dir !== "string") return "";
|
|
39788
|
+
return dir.trim().replace(/[\\/]+/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
39789
|
+
}
|
|
39283
39790
|
function shouldSuppressStaleParsedBusyStatus(adapterStatus, parsedStatus, adapter) {
|
|
39284
39791
|
const parsedRawStatus = normalizeAgentStatus(parsedStatus?.status);
|
|
39285
39792
|
if (!BUSY_AGENT_STATUSES.has(parsedRawStatus)) return false;
|
|
@@ -40028,6 +40535,33 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
40028
40535
|
const adapter = this.adapters.get(ik);
|
|
40029
40536
|
return adapter ? { adapter, key: ik } : null;
|
|
40030
40537
|
}
|
|
40538
|
+
/**
|
|
40539
|
+
* WTCLAIM (B): resolve the adapter for a mesh dispatch that named a node
|
|
40540
|
+
* (meshContext.nodeId) but carried no explicit session. Matches by the
|
|
40541
|
+
* instance's bound mesh node id (settings.meshNodeId, falling back to the
|
|
40542
|
+
* sticky meshLastNodeId) first, then by the node workspace (workingDir).
|
|
40543
|
+
*
|
|
40544
|
+
* Unlike findAdapter's step-2 fuzzy fallback, this NEVER degrades to a
|
|
40545
|
+
* provider-only first-match: on a daemon hosting BOTH a base node and a
|
|
40546
|
+
* cloned worktree node (same daemonId), that fuzzy match could land a
|
|
40547
|
+
* worktree-targeted task on the base session. Returns null when no session
|
|
40548
|
+
* is bound to this node so the caller fails closed.
|
|
40549
|
+
*/
|
|
40550
|
+
findMeshNodeAdapter(agentType, nodeId, dir) {
|
|
40551
|
+
const instanceManager = this.deps.getInstanceManager();
|
|
40552
|
+
const targetDir = normalizeDirForCompare(dir);
|
|
40553
|
+
let workspaceMatch = null;
|
|
40554
|
+
for (const [k, a] of this.adapters) {
|
|
40555
|
+
if (a.cliType !== agentType) continue;
|
|
40556
|
+
const settings = instanceManager?.getInstance(k)?.getState?.()?.settings;
|
|
40557
|
+
const boundNodeId = typeof settings?.meshNodeId === "string" && settings.meshNodeId.trim() ? settings.meshNodeId.trim() : typeof settings?.meshLastNodeId === "string" ? settings.meshLastNodeId.trim() : "";
|
|
40558
|
+
if (boundNodeId && boundNodeId === nodeId) return { adapter: a, key: k };
|
|
40559
|
+
if (!workspaceMatch && targetDir && normalizeDirForCompare(a.workingDir) === targetDir) {
|
|
40560
|
+
workspaceMatch = { adapter: a, key: k };
|
|
40561
|
+
}
|
|
40562
|
+
}
|
|
40563
|
+
return workspaceMatch;
|
|
40564
|
+
}
|
|
40031
40565
|
// ─── CLI command handling ────────────────────────────
|
|
40032
40566
|
async handleCliCommand(cmd, args) {
|
|
40033
40567
|
switch (cmd) {
|
|
@@ -40213,10 +40747,22 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
40213
40747
|
const agentType = args?.agentType || args?.cliType;
|
|
40214
40748
|
const action = args?.action;
|
|
40215
40749
|
if (!agentType || !action) throw new Error("agentType and action required");
|
|
40216
|
-
const
|
|
40217
|
-
|
|
40218
|
-
|
|
40219
|
-
});
|
|
40750
|
+
const meshScopeNodeId = (() => {
|
|
40751
|
+
const mc = args?.meshContext;
|
|
40752
|
+
return mc && typeof mc === "object" && typeof mc.nodeId === "string" ? mc.nodeId.trim() : "";
|
|
40753
|
+
})();
|
|
40754
|
+
let found;
|
|
40755
|
+
if (meshScopeNodeId && !args?.targetSessionId) {
|
|
40756
|
+
found = this.findMeshNodeAdapter(agentType, meshScopeNodeId, args?.dir);
|
|
40757
|
+
if (!found) {
|
|
40758
|
+
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`);
|
|
40759
|
+
}
|
|
40760
|
+
} else {
|
|
40761
|
+
found = this.findAdapter(agentType, {
|
|
40762
|
+
dir: args?.dir,
|
|
40763
|
+
instanceKey: args?.targetSessionId
|
|
40764
|
+
});
|
|
40765
|
+
}
|
|
40220
40766
|
if (!found) throw new Error(`CLI agent not running: ${agentType}`);
|
|
40221
40767
|
const { adapter, key } = found;
|
|
40222
40768
|
if (action === "send_chat") {
|
|
@@ -44110,7 +44656,6 @@ init_workspaces();
|
|
|
44110
44656
|
init_recent_activity();
|
|
44111
44657
|
init_cli_detector();
|
|
44112
44658
|
init_git_status();
|
|
44113
|
-
init_change_impact_config();
|
|
44114
44659
|
init_dist();
|
|
44115
44660
|
init_logger();
|
|
44116
44661
|
|
|
@@ -45453,7 +45998,7 @@ function buildMeshNodeMachineIdentity(node, opts) {
|
|
|
45453
45998
|
const machineName = readMeshNodeDisplayMachineName(node);
|
|
45454
45999
|
const coordinatorHostname = readStringValue(opts.coordinatorHostname);
|
|
45455
46000
|
const machineIdMatches = Boolean(opts.localMachineId && machineId && opts.localMachineId === machineId);
|
|
45456
|
-
const daemonIdMatches = Boolean(opts.localDaemonId && daemonId && opts.localDaemonId
|
|
46001
|
+
const daemonIdMatches = Boolean(opts.localDaemonId && daemonId && daemonIdsEquivalent(opts.localDaemonId, daemonId));
|
|
45457
46002
|
const hostnameMatches = Boolean(
|
|
45458
46003
|
normalizeMeshHostname(hostname3) && normalizeMeshHostname(coordinatorHostname) && normalizeMeshHostname(hostname3) === normalizeMeshHostname(coordinatorHostname)
|
|
45459
46004
|
);
|
|
@@ -46168,10 +46713,10 @@ async function hydrateInlineMeshDirectTruth(args) {
|
|
|
46168
46713
|
const isSelfNode = Boolean(
|
|
46169
46714
|
nodeId && selectedCoordinatorNodeId && nodeId === selectedCoordinatorNodeId
|
|
46170
46715
|
) || Boolean(
|
|
46171
|
-
daemonId && (daemonId
|
|
46716
|
+
daemonId && (daemonIdsEquivalent(daemonId, args.localMachineId) || daemonIdsEquivalent(daemonId, args.statusInstanceId))
|
|
46172
46717
|
) || Boolean(args.meshSource !== "local_config" && nodeIndex === 0);
|
|
46173
46718
|
const isSelfDaemonNode = Boolean(
|
|
46174
|
-
daemonId && (daemonId
|
|
46719
|
+
daemonId && (daemonIdsEquivalent(daemonId, args.localMachineId) || daemonIdsEquivalent(daemonId, args.statusInstanceId))
|
|
46175
46720
|
);
|
|
46176
46721
|
if ((isSelfNode || isSelfDaemonNode) && isDeadLocalWorktreeNode(node)) {
|
|
46177
46722
|
deadNodeIds.push(nodeId);
|
|
@@ -47510,97 +48055,6 @@ function normalizeCommandArgsWithInteractionId(args) {
|
|
|
47510
48055
|
}
|
|
47511
48056
|
return base;
|
|
47512
48057
|
}
|
|
47513
|
-
function resolveSpecPathInProviders(specPath, fsm, pathm, osm) {
|
|
47514
|
-
let rootReal;
|
|
47515
|
-
try {
|
|
47516
|
-
rootReal = fsm.realpathSync(pathm.join(osm.homedir(), ".adhdev", "providers"));
|
|
47517
|
-
} catch (e) {
|
|
47518
|
-
return { ok: false, error: `providers root unavailable: ${e.message}` };
|
|
47519
|
-
}
|
|
47520
|
-
const resolved = pathm.resolve(specPath);
|
|
47521
|
-
const base = pathm.basename(resolved);
|
|
47522
|
-
if (!/^[\w.-]+\.json$/.test(base)) {
|
|
47523
|
-
return { ok: false, error: "refused: spec file must be a *.json basename" };
|
|
47524
|
-
}
|
|
47525
|
-
let parentReal;
|
|
47526
|
-
try {
|
|
47527
|
-
parentReal = fsm.realpathSync(pathm.dirname(resolved));
|
|
47528
|
-
} catch (e) {
|
|
47529
|
-
return { ok: false, error: `spec directory not found: ${e.message}` };
|
|
47530
|
-
}
|
|
47531
|
-
if (parentReal !== rootReal && !parentReal.startsWith(rootReal + pathm.sep)) {
|
|
47532
|
-
return { ok: false, error: "refused: spec path must be under the providers root" };
|
|
47533
|
-
}
|
|
47534
|
-
const safe = pathm.join(parentReal, base);
|
|
47535
|
-
try {
|
|
47536
|
-
const st = fsm.lstatSync(safe);
|
|
47537
|
-
if (st.isSymbolicLink()) return { ok: false, error: "refused: spec path is a symlink" };
|
|
47538
|
-
} catch {
|
|
47539
|
-
}
|
|
47540
|
-
return { ok: true, path: safe };
|
|
47541
|
-
}
|
|
47542
|
-
function toHostedCliRuntimeDescriptor(record) {
|
|
47543
|
-
if (!record || typeof record !== "object") return null;
|
|
47544
|
-
const runtimeId = typeof record.sessionId === "string" ? record.sessionId : "";
|
|
47545
|
-
const cliType = typeof record.providerType === "string" ? record.providerType : "";
|
|
47546
|
-
const workspace = typeof record.workspace === "string" ? record.workspace : "";
|
|
47547
|
-
if (!runtimeId || !cliType || !workspace) return null;
|
|
47548
|
-
return {
|
|
47549
|
-
runtimeId,
|
|
47550
|
-
runtimeKey: typeof record.runtimeKey === "string" ? record.runtimeKey : void 0,
|
|
47551
|
-
displayName: typeof record.displayName === "string" ? record.displayName : void 0,
|
|
47552
|
-
workspaceLabel: typeof record.workspaceLabel === "string" ? record.workspaceLabel : void 0,
|
|
47553
|
-
lifecycle: typeof record.lifecycle === "string" ? record.lifecycle : void 0,
|
|
47554
|
-
recoveryState: typeof record.meta?.runtimeRecoveryState === "string" ? String(record.meta.runtimeRecoveryState) : null,
|
|
47555
|
-
cliType,
|
|
47556
|
-
workspace,
|
|
47557
|
-
cliArgs: Array.isArray(record.meta?.cliArgs) ? record.meta.cliArgs : [],
|
|
47558
|
-
providerSessionId: typeof record.meta?.providerSessionId === "string" ? String(record.meta.providerSessionId) : void 0
|
|
47559
|
-
};
|
|
47560
|
-
}
|
|
47561
|
-
function getWriteConflictOwnerClientId(error) {
|
|
47562
|
-
const message = typeof error === "string" ? error : error instanceof Error ? error.message : "";
|
|
47563
|
-
const match = /^Write owned by\s+(.+)$/.exec(message.trim());
|
|
47564
|
-
return match?.[1]?.trim() || void 0;
|
|
47565
|
-
}
|
|
47566
|
-
function summarizeSessionHostRecord(result) {
|
|
47567
|
-
if (!result || typeof result !== "object") return {};
|
|
47568
|
-
const record = result;
|
|
47569
|
-
return {
|
|
47570
|
-
runtimeKey: typeof record.runtimeKey === "string" ? record.runtimeKey : void 0,
|
|
47571
|
-
lifecycle: typeof record.lifecycle === "string" ? record.lifecycle : void 0,
|
|
47572
|
-
surfaceKind: getSessionHostSurfaceKind(record),
|
|
47573
|
-
attachedClientCount: Array.isArray(record.attachedClients) ? record.attachedClients.length : void 0,
|
|
47574
|
-
hasWriteOwner: !!record.writeOwner,
|
|
47575
|
-
writeOwnerClientId: typeof record.writeOwner?.clientId === "string" ? record.writeOwner.clientId : void 0
|
|
47576
|
-
};
|
|
47577
|
-
}
|
|
47578
|
-
function summarizeSessionHostRecords(result) {
|
|
47579
|
-
const records = Array.isArray(result) ? result : [];
|
|
47580
|
-
const groups = partitionSessionHostRecords(records);
|
|
47581
|
-
return {
|
|
47582
|
-
sessionCount: records.length,
|
|
47583
|
-
liveRuntimeCount: groups.liveRuntimes.length,
|
|
47584
|
-
recoverySnapshotCount: groups.recoverySnapshots.length,
|
|
47585
|
-
inactiveRecordCount: groups.inactiveRecords.length
|
|
47586
|
-
};
|
|
47587
|
-
}
|
|
47588
|
-
function summarizeSessionHostDiagnostics(result) {
|
|
47589
|
-
const diagnostics = result && typeof result === "object" ? result : {};
|
|
47590
|
-
const sessions = Array.isArray(diagnostics.sessions) ? diagnostics.sessions : [];
|
|
47591
|
-
return {
|
|
47592
|
-
runtimeCount: typeof diagnostics.runtimeCount === "number" ? diagnostics.runtimeCount : void 0,
|
|
47593
|
-
...summarizeSessionHostRecords(sessions)
|
|
47594
|
-
};
|
|
47595
|
-
}
|
|
47596
|
-
function summarizeSessionHostPruneResult(result) {
|
|
47597
|
-
const value = result && typeof result === "object" ? result : {};
|
|
47598
|
-
return {
|
|
47599
|
-
duplicateGroupCount: typeof value.duplicateGroupCount === "number" ? value.duplicateGroupCount : void 0,
|
|
47600
|
-
prunedCount: Array.isArray(value.prunedSessionIds) ? value.prunedSessionIds.length : void 0,
|
|
47601
|
-
keptCount: Array.isArray(value.keptSessionIds) ? value.keptSessionIds.length : void 0
|
|
47602
|
-
};
|
|
47603
|
-
}
|
|
47604
48058
|
function normalizeStandaloneHostCommandUrl(hostAddress) {
|
|
47605
48059
|
const raw = hostAddress.trim();
|
|
47606
48060
|
if (!raw) throw new Error("hostAddress required");
|
|
@@ -48450,56 +48904,6 @@ var DaemonCommandRouter = class {
|
|
|
48450
48904
|
...errors.length ? { errors } : {}
|
|
48451
48905
|
};
|
|
48452
48906
|
}
|
|
48453
|
-
async traceSessionHostAction(action, args, run, summarizeResult2) {
|
|
48454
|
-
const interactionId = typeof args?._interactionId === "string" ? args._interactionId : void 0;
|
|
48455
|
-
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : void 0;
|
|
48456
|
-
const requestedPayload = { action };
|
|
48457
|
-
if (sessionId) requestedPayload.sessionId = sessionId;
|
|
48458
|
-
if (typeof args?.clientId === "string") requestedPayload.clientId = args.clientId;
|
|
48459
|
-
if (typeof args?.signal === "string") requestedPayload.signal = args.signal;
|
|
48460
|
-
if (typeof args?.providerType === "string") requestedPayload.providerType = args.providerType;
|
|
48461
|
-
if (typeof args?.workspace === "string") requestedPayload.workspace = args.workspace;
|
|
48462
|
-
if (typeof args?.dryRun === "boolean") requestedPayload.dryRun = args.dryRun;
|
|
48463
|
-
recordDebugTrace({
|
|
48464
|
-
interactionId,
|
|
48465
|
-
category: "session_host",
|
|
48466
|
-
stage: "action_requested",
|
|
48467
|
-
level: "info",
|
|
48468
|
-
sessionId,
|
|
48469
|
-
payload: requestedPayload
|
|
48470
|
-
});
|
|
48471
|
-
try {
|
|
48472
|
-
const result = await run();
|
|
48473
|
-
recordDebugTrace({
|
|
48474
|
-
interactionId,
|
|
48475
|
-
category: "session_host",
|
|
48476
|
-
stage: "action_result",
|
|
48477
|
-
level: "info",
|
|
48478
|
-
sessionId,
|
|
48479
|
-
payload: {
|
|
48480
|
-
...requestedPayload,
|
|
48481
|
-
success: true,
|
|
48482
|
-
...summarizeResult2 ? summarizeResult2(result) : {}
|
|
48483
|
-
}
|
|
48484
|
-
});
|
|
48485
|
-
return result;
|
|
48486
|
-
} catch (error) {
|
|
48487
|
-
recordDebugTrace({
|
|
48488
|
-
interactionId,
|
|
48489
|
-
category: "session_host",
|
|
48490
|
-
stage: "action_failed",
|
|
48491
|
-
level: "error",
|
|
48492
|
-
sessionId,
|
|
48493
|
-
payload: {
|
|
48494
|
-
...requestedPayload,
|
|
48495
|
-
error: error?.message || String(error),
|
|
48496
|
-
failureKind: getWriteConflictOwnerClientId(error) ? "write_conflict" : "request_failed",
|
|
48497
|
-
conflictOwnerClientId: getWriteConflictOwnerClientId(error)
|
|
48498
|
-
}
|
|
48499
|
-
});
|
|
48500
|
-
throw error;
|
|
48501
|
-
}
|
|
48502
|
-
}
|
|
48503
48907
|
/**
|
|
48504
48908
|
* Unified command routing.
|
|
48505
48909
|
* Returns result for all commands:
|
|
@@ -49874,6 +50278,10 @@ ${hintLines.join("\n")}` : "",
|
|
|
49874
50278
|
}
|
|
49875
50279
|
}
|
|
49876
50280
|
}
|
|
50281
|
+
const lowFamilyHandler = lowFamilyRegistry.get(cmd);
|
|
50282
|
+
if (lowFamilyHandler) {
|
|
50283
|
+
return await lowFamilyHandler({ deps: this.deps }, args);
|
|
50284
|
+
}
|
|
49877
50285
|
switch (cmd) {
|
|
49878
50286
|
// ─── CLI / ACP commands ───
|
|
49879
50287
|
case "mesh_forward_event": {
|
|
@@ -50001,121 +50409,6 @@ ${hintLines.join("\n")}` : "",
|
|
|
50001
50409
|
const trace = getRecentDebugTrace({ interactionId, category, limit: count }).filter((entry) => !sinceTs || entry.ts > sinceTs);
|
|
50002
50410
|
return { success: true, trace, count: trace.length };
|
|
50003
50411
|
}
|
|
50004
|
-
case "session_host_get_diagnostics": {
|
|
50005
|
-
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
50006
|
-
const diagnostics = await this.traceSessionHostAction("session_host_get_diagnostics", args, () => this.deps.sessionHostControl.getDiagnostics({
|
|
50007
|
-
includeSessions: args?.includeSessions !== false,
|
|
50008
|
-
limit: Number(args?.limit) || void 0
|
|
50009
|
-
}), (result) => ({
|
|
50010
|
-
includeSessions: args?.includeSessions !== false,
|
|
50011
|
-
limit: Number(args?.limit) || void 0,
|
|
50012
|
-
...summarizeSessionHostDiagnostics(result)
|
|
50013
|
-
}));
|
|
50014
|
-
return { success: true, diagnostics };
|
|
50015
|
-
}
|
|
50016
|
-
case "session_host_list_sessions": {
|
|
50017
|
-
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
50018
|
-
const sessions = await this.traceSessionHostAction("session_host_list_sessions", args, () => this.deps.sessionHostControl.listSessions(), (records) => summarizeSessionHostRecords(records));
|
|
50019
|
-
return { success: true, sessions };
|
|
50020
|
-
}
|
|
50021
|
-
case "session_host_stop_session": {
|
|
50022
|
-
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
50023
|
-
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
50024
|
-
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
50025
|
-
const record = await this.traceSessionHostAction("session_host_stop_session", args, () => this.deps.sessionHostControl.stopSession(sessionId), (result) => summarizeSessionHostRecord(result));
|
|
50026
|
-
return { success: true, record };
|
|
50027
|
-
}
|
|
50028
|
-
case "session_host_resume_session": {
|
|
50029
|
-
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
50030
|
-
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
50031
|
-
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
50032
|
-
const record = await this.traceSessionHostAction("session_host_resume_session", args, async () => {
|
|
50033
|
-
const nextRecord = await this.deps.sessionHostControl.resumeSession(sessionId);
|
|
50034
|
-
const hosted = toHostedCliRuntimeDescriptor(nextRecord);
|
|
50035
|
-
if (hosted) {
|
|
50036
|
-
await this.deps.cliManager.restoreHostedSessions([hosted]);
|
|
50037
|
-
}
|
|
50038
|
-
return nextRecord;
|
|
50039
|
-
}, (result) => ({
|
|
50040
|
-
...summarizeSessionHostRecord(result),
|
|
50041
|
-
restoredHostedSession: !!toHostedCliRuntimeDescriptor(result)
|
|
50042
|
-
}));
|
|
50043
|
-
return { success: true, record };
|
|
50044
|
-
}
|
|
50045
|
-
case "session_host_restart_session": {
|
|
50046
|
-
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
50047
|
-
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
50048
|
-
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
50049
|
-
const record = await this.traceSessionHostAction("session_host_restart_session", args, async () => {
|
|
50050
|
-
const nextRecord = await this.deps.sessionHostControl.restartSession(sessionId);
|
|
50051
|
-
const hosted = toHostedCliRuntimeDescriptor(nextRecord);
|
|
50052
|
-
if (hosted) {
|
|
50053
|
-
await this.deps.cliManager.restoreHostedSessions([hosted]);
|
|
50054
|
-
}
|
|
50055
|
-
return nextRecord;
|
|
50056
|
-
}, (result) => ({
|
|
50057
|
-
...summarizeSessionHostRecord(result),
|
|
50058
|
-
restoredHostedSession: !!toHostedCliRuntimeDescriptor(result)
|
|
50059
|
-
}));
|
|
50060
|
-
return { success: true, record };
|
|
50061
|
-
}
|
|
50062
|
-
case "session_host_send_signal": {
|
|
50063
|
-
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
50064
|
-
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
50065
|
-
const signal = typeof args?.signal === "string" ? args.signal : "";
|
|
50066
|
-
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
50067
|
-
if (!signal) return { success: false, error: "signal required" };
|
|
50068
|
-
const record = await this.traceSessionHostAction("session_host_send_signal", args, () => this.deps.sessionHostControl.sendSignal(sessionId, signal), (result) => summarizeSessionHostRecord(result));
|
|
50069
|
-
return { success: true, record };
|
|
50070
|
-
}
|
|
50071
|
-
case "session_host_force_detach_client": {
|
|
50072
|
-
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
50073
|
-
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
50074
|
-
const clientId = typeof args?.clientId === "string" ? args.clientId : "";
|
|
50075
|
-
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
50076
|
-
if (!clientId) return { success: false, error: "clientId required" };
|
|
50077
|
-
const record = await this.traceSessionHostAction("session_host_force_detach_client", args, () => this.deps.sessionHostControl.forceDetachClient(sessionId, clientId), (result) => summarizeSessionHostRecord(result));
|
|
50078
|
-
return { success: true, record };
|
|
50079
|
-
}
|
|
50080
|
-
case "session_host_prune_duplicate_sessions": {
|
|
50081
|
-
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
50082
|
-
const result = await this.traceSessionHostAction("session_host_prune_duplicate_sessions", args, () => this.deps.sessionHostControl.pruneDuplicateSessions({
|
|
50083
|
-
providerType: typeof args?.providerType === "string" ? args.providerType : void 0,
|
|
50084
|
-
workspace: typeof args?.workspace === "string" ? args.workspace : void 0,
|
|
50085
|
-
dryRun: args?.dryRun === true
|
|
50086
|
-
}), (value) => summarizeSessionHostPruneResult(value));
|
|
50087
|
-
return { success: true, result };
|
|
50088
|
-
}
|
|
50089
|
-
case "session_host_acquire_write": {
|
|
50090
|
-
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
50091
|
-
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
50092
|
-
const clientId = typeof args?.clientId === "string" ? args.clientId : "";
|
|
50093
|
-
const ownerType = args?.ownerType === "agent" ? "agent" : "user";
|
|
50094
|
-
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
50095
|
-
if (!clientId) return { success: false, error: "clientId required" };
|
|
50096
|
-
const record = await this.traceSessionHostAction("session_host_acquire_write", args, () => this.deps.sessionHostControl.acquireWrite({
|
|
50097
|
-
sessionId,
|
|
50098
|
-
clientId,
|
|
50099
|
-
ownerType,
|
|
50100
|
-
force: args?.force !== false
|
|
50101
|
-
}), (result) => ({
|
|
50102
|
-
...summarizeSessionHostRecord(result),
|
|
50103
|
-
ownerType
|
|
50104
|
-
}));
|
|
50105
|
-
return { success: true, record };
|
|
50106
|
-
}
|
|
50107
|
-
case "session_host_release_write": {
|
|
50108
|
-
if (!this.deps.sessionHostControl) return { success: false, error: "Session host control unavailable" };
|
|
50109
|
-
const sessionId = typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
50110
|
-
const clientId = typeof args?.clientId === "string" ? args.clientId : "";
|
|
50111
|
-
if (!sessionId) return { success: false, error: "sessionId required" };
|
|
50112
|
-
if (!clientId) return { success: false, error: "clientId required" };
|
|
50113
|
-
const record = await this.traceSessionHostAction("session_host_release_write", args, () => this.deps.sessionHostControl.releaseWrite({
|
|
50114
|
-
sessionId,
|
|
50115
|
-
clientId
|
|
50116
|
-
}), (result) => summarizeSessionHostRecord(result));
|
|
50117
|
-
return { success: true, record };
|
|
50118
|
-
}
|
|
50119
50412
|
case "list_saved_sessions": {
|
|
50120
50413
|
const providerType = typeof args?.providerType === "string" ? args.providerType.trim() : typeof args?.agentType === "string" ? args.agentType.trim() : "";
|
|
50121
50414
|
const kind = args?.kind === "acp" ? "acp" : "cli";
|
|
@@ -50371,166 +50664,6 @@ ${hintLines.join("\n")}` : "",
|
|
|
50371
50664
|
} : null
|
|
50372
50665
|
};
|
|
50373
50666
|
}
|
|
50374
|
-
case "get_spec_debug": {
|
|
50375
|
-
const sessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : typeof args?.sessionId === "string" ? args.sessionId.trim() : "";
|
|
50376
|
-
if (!sessionId) return { success: false, error: "targetSessionId required" };
|
|
50377
|
-
const target = this.deps.sessionRegistry.get(sessionId);
|
|
50378
|
-
if (!target) return { success: false, error: "Session not found", sessionId };
|
|
50379
|
-
const adapterObj = this.deps.cliManager.findAdapter(target.providerType, { instanceKey: sessionId })?.adapter;
|
|
50380
|
-
const snapshot = adapterObj ? typeof adapterObj.getDebugSnapshot === "function" ? adapterObj.getDebugSnapshot() : typeof adapterObj.getDebugState === "function" ? adapterObj.getDebugState() : null : null;
|
|
50381
|
-
return {
|
|
50382
|
-
success: true,
|
|
50383
|
-
sessionId,
|
|
50384
|
-
providerType: target.providerType,
|
|
50385
|
-
isSpecProvider: snapshot !== null,
|
|
50386
|
-
snapshot
|
|
50387
|
-
};
|
|
50388
|
-
}
|
|
50389
|
-
// ── Spec source read/write for the debug panel's live editor.
|
|
50390
|
-
// Lets the dashboard load a session's spec.json, edit it, and
|
|
50391
|
-
// save it back — the driver's fs.watch picks up the change and
|
|
50392
|
-
// hot-reloads the FSM with no restart. Writes are confined to
|
|
50393
|
-
// files under ~/.adhdev/providers to avoid arbitrary fs access.
|
|
50394
|
-
case "get_spec_source": {
|
|
50395
|
-
const fsm = await import("fs");
|
|
50396
|
-
const pathm = await import("path");
|
|
50397
|
-
const osm = await import("os");
|
|
50398
|
-
const sessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : typeof args?.sessionId === "string" ? args.sessionId.trim() : "";
|
|
50399
|
-
let specPath = typeof args?.specPath === "string" ? args.specPath : "";
|
|
50400
|
-
if (!specPath && sessionId) {
|
|
50401
|
-
const target = this.deps.sessionRegistry.get(sessionId);
|
|
50402
|
-
const adapterObj = target ? this.deps.cliManager.findAdapter(target.providerType, { instanceKey: sessionId })?.adapter : null;
|
|
50403
|
-
const snap = adapterObj && typeof adapterObj.getDebugSnapshot === "function" ? adapterObj.getDebugSnapshot() : null;
|
|
50404
|
-
specPath = snap?.specPath ?? "";
|
|
50405
|
-
}
|
|
50406
|
-
if (!specPath) return { success: false, error: "specPath or resolvable targetSessionId required" };
|
|
50407
|
-
const safe = resolveSpecPathInProviders(specPath, fsm, pathm, osm);
|
|
50408
|
-
if (!safe.ok) return { success: false, error: safe.error, specPath };
|
|
50409
|
-
try {
|
|
50410
|
-
const content = fsm.readFileSync(safe.path, "utf8");
|
|
50411
|
-
return { success: true, specPath: safe.path, content };
|
|
50412
|
-
} catch (e) {
|
|
50413
|
-
return { success: false, error: `read failed: ${e.message}`, specPath };
|
|
50414
|
-
}
|
|
50415
|
-
}
|
|
50416
|
-
case "write_spec_source": {
|
|
50417
|
-
const fsm = await import("fs");
|
|
50418
|
-
const pathm = await import("path");
|
|
50419
|
-
const osm = await import("os");
|
|
50420
|
-
const specPath = typeof args?.specPath === "string" ? args.specPath : "";
|
|
50421
|
-
const content = typeof args?.content === "string" ? args.content : "";
|
|
50422
|
-
if (!specPath) return { success: false, error: "specPath required" };
|
|
50423
|
-
if (!content) return { success: false, error: "content required" };
|
|
50424
|
-
const safe = resolveSpecPathInProviders(specPath, fsm, pathm, osm);
|
|
50425
|
-
if (!safe.ok) return { success: false, error: safe.error };
|
|
50426
|
-
let parsed;
|
|
50427
|
-
try {
|
|
50428
|
-
parsed = JSON.parse(content);
|
|
50429
|
-
} catch (e) {
|
|
50430
|
-
return { success: false, error: `invalid JSON: ${e.message}` };
|
|
50431
|
-
}
|
|
50432
|
-
if (parsed?.$schema === "adhdev:cli/spec@4") {
|
|
50433
|
-
const { validateFsmSpec: validateFsmSpec2 } = await Promise.resolve().then(() => (init_fsm_loader(), fsm_loader_exports));
|
|
50434
|
-
const errs = validateFsmSpec2(parsed);
|
|
50435
|
-
if (errs.length) return { success: false, error: "spec invalid", validationErrors: errs };
|
|
50436
|
-
}
|
|
50437
|
-
try {
|
|
50438
|
-
fsm.writeFileSync(safe.path, content, "utf8");
|
|
50439
|
-
return { success: true, specPath: safe.path };
|
|
50440
|
-
} catch (e) {
|
|
50441
|
-
return { success: false, error: `write failed: ${e.message}` };
|
|
50442
|
-
}
|
|
50443
|
-
}
|
|
50444
|
-
// ── Validate an in-progress spec (string or object) without writing.
|
|
50445
|
-
// The form builder calls this on every change so Save can stay
|
|
50446
|
-
// disabled while there are structural / reference / regex errors.
|
|
50447
|
-
case "validate_spec": {
|
|
50448
|
-
let parsed = args?.spec;
|
|
50449
|
-
if (typeof args?.content === "string") {
|
|
50450
|
-
try {
|
|
50451
|
-
parsed = JSON.parse(args.content);
|
|
50452
|
-
} catch (e) {
|
|
50453
|
-
return { success: true, valid: false, errors: [`invalid JSON: ${e.message}`] };
|
|
50454
|
-
}
|
|
50455
|
-
}
|
|
50456
|
-
if (!parsed || typeof parsed !== "object") {
|
|
50457
|
-
return { success: true, valid: false, errors: ["spec must be an object or content string"] };
|
|
50458
|
-
}
|
|
50459
|
-
const schema = parsed.$schema;
|
|
50460
|
-
if (schema === "adhdev:cli/spec@4") {
|
|
50461
|
-
const { validateFsmSpec: validateFsmSpec2 } = await Promise.resolve().then(() => (init_fsm_loader(), fsm_loader_exports));
|
|
50462
|
-
const errors = validateFsmSpec2(parsed);
|
|
50463
|
-
return { success: true, valid: errors.length === 0, errors };
|
|
50464
|
-
}
|
|
50465
|
-
return { success: true, valid: false, errors: [`unsupported $schema "${schema}" \u2014 form builder is v4-only`] };
|
|
50466
|
-
}
|
|
50467
|
-
// ── Evaluate a single condition against a live session's current
|
|
50468
|
-
// screen — powers the editor's "does this match right now?"
|
|
50469
|
-
// preview. Returns the recursive match tree.
|
|
50470
|
-
case "eval_condition_preview": {
|
|
50471
|
-
const sessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : typeof args?.sessionId === "string" ? args.sessionId.trim() : "";
|
|
50472
|
-
if (!sessionId) return { success: false, error: "targetSessionId required" };
|
|
50473
|
-
if (!args?.condition || typeof args.condition !== "object") return { success: false, error: "condition required" };
|
|
50474
|
-
const target = this.deps.sessionRegistry.get(sessionId);
|
|
50475
|
-
if (!target) return { success: false, error: "Session not found", sessionId };
|
|
50476
|
-
const adapterObj = this.deps.cliManager.findAdapter(target.providerType, { instanceKey: sessionId })?.adapter;
|
|
50477
|
-
const snap = adapterObj && typeof adapterObj.getDebugSnapshot === "function" ? adapterObj.getDebugSnapshot() : null;
|
|
50478
|
-
if (!snap?.screen) return { success: false, error: "no live screen for session" };
|
|
50479
|
-
let sectionsDef;
|
|
50480
|
-
try {
|
|
50481
|
-
const fsm2 = await import("fs");
|
|
50482
|
-
if (snap.specPath) {
|
|
50483
|
-
const raw = JSON.parse(fsm2.readFileSync(snap.specPath, "utf8"));
|
|
50484
|
-
sectionsDef = raw?.sections;
|
|
50485
|
-
}
|
|
50486
|
-
} catch {
|
|
50487
|
-
}
|
|
50488
|
-
const { evaluateConditionPreview: evaluateConditionPreview2 } = await Promise.resolve().then(() => (init_fsm_evaluator(), fsm_evaluator_exports));
|
|
50489
|
-
try {
|
|
50490
|
-
const result = evaluateConditionPreview2(
|
|
50491
|
-
args.condition,
|
|
50492
|
-
sectionsDef,
|
|
50493
|
-
snap.screen,
|
|
50494
|
-
snap.cursorPosition ?? void 0
|
|
50495
|
-
);
|
|
50496
|
-
return { success: true, result, sections: snap.sections ?? null };
|
|
50497
|
-
} catch (e) {
|
|
50498
|
-
return { success: false, error: `eval failed: ${e.message}` };
|
|
50499
|
-
}
|
|
50500
|
-
}
|
|
50501
|
-
// ── Resolve a sections map against a live session's screen — the
|
|
50502
|
-
// section editor's "test" button. Returns, for each section id,
|
|
50503
|
-
// the line range + the text it captures, so the author can SEE
|
|
50504
|
-
// whether a from_top/until/anchor definition carves the screen
|
|
50505
|
-
// the way they intend. Accepts an in-progress sections map so it
|
|
50506
|
-
// previews unsaved edits.
|
|
50507
|
-
case "resolve_section_preview": {
|
|
50508
|
-
const sessionId = typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : typeof args?.sessionId === "string" ? args.sessionId.trim() : "";
|
|
50509
|
-
if (!sessionId) return { success: false, error: "targetSessionId required" };
|
|
50510
|
-
if (!args?.sections || typeof args.sections !== "object") return { success: false, error: "sections map required" };
|
|
50511
|
-
const target = this.deps.sessionRegistry.get(sessionId);
|
|
50512
|
-
if (!target) return { success: false, error: "Session not found", sessionId };
|
|
50513
|
-
const adapterObj = this.deps.cliManager.findAdapter(target.providerType, { instanceKey: sessionId })?.adapter;
|
|
50514
|
-
const snap = adapterObj && typeof adapterObj.getDebugSnapshot === "function" ? adapterObj.getDebugSnapshot() : null;
|
|
50515
|
-
if (!snap?.screen) return { success: false, error: "no live screen for session" };
|
|
50516
|
-
const { resolveSections: resolveSections2 } = await Promise.resolve().then(() => (init_evaluator(), evaluator_exports));
|
|
50517
|
-
try {
|
|
50518
|
-
const lines = String(snap.screen).split("\n").map((l) => l.endsWith("\r") ? l.slice(0, -1) : l);
|
|
50519
|
-
const resolved = resolveSections2(args.sections, lines);
|
|
50520
|
-
return {
|
|
50521
|
-
success: true,
|
|
50522
|
-
screenLineCount: lines.length,
|
|
50523
|
-
sections: resolved.map((s2) => ({ id: s2.id, fromLine: s2.fromLine, toLine: s2.toLine, text: s2.text }))
|
|
50524
|
-
};
|
|
50525
|
-
} catch (e) {
|
|
50526
|
-
return { success: false, error: `resolve failed: ${e.message}` };
|
|
50527
|
-
}
|
|
50528
|
-
}
|
|
50529
|
-
// ── User-level coordinator-prompt files (~/.adhdev/coordinator-prompts/).
|
|
50530
|
-
// These live on this daemon's filesystem and never sync to the
|
|
50531
|
-
// cloud / other daemons — they're per-machine config. The
|
|
50532
|
-
// Settings page in the dashboard reads/writes via these two
|
|
50533
|
-
// commands instead of going through fs from the browser.
|
|
50534
50667
|
case "list_coordinator_prompts": {
|
|
50535
50668
|
const fs32 = await import("fs");
|
|
50536
50669
|
const path42 = await import("path");
|
|
@@ -51251,73 +51384,6 @@ ${hintLines.join("\n")}` : "",
|
|
|
51251
51384
|
return { success: false, error: e.message };
|
|
51252
51385
|
}
|
|
51253
51386
|
}
|
|
51254
|
-
case "get_mesh_refine_config_schema": {
|
|
51255
|
-
return {
|
|
51256
|
-
success: true,
|
|
51257
|
-
schema: MESH_REFINE_CONFIG_SCHEMA,
|
|
51258
|
-
locations: MESH_REFINE_CONFIG_LOCATIONS,
|
|
51259
|
-
worktreeBootstrap: {
|
|
51260
|
-
schema: MESH_WORKTREE_BOOTSTRAP_CONFIG_SCHEMA,
|
|
51261
|
-
locations: MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS,
|
|
51262
|
-
sourceOfTruth: "repo worktree bootstrap config",
|
|
51263
|
-
runBehavior: "When present and enabled, clone_mesh_node runs commands after submodule initialization and records status on the worktree node."
|
|
51264
|
-
},
|
|
51265
|
-
sourceOfTruth: "repo mesh/refine config",
|
|
51266
|
-
heuristicRole: "suggestions_only_not_execution_path"
|
|
51267
|
-
};
|
|
51268
|
-
}
|
|
51269
|
-
case "validate_mesh_refine_config": {
|
|
51270
|
-
const workspace = typeof args?.workspace === "string" ? args.workspace : process.cwd();
|
|
51271
|
-
const mesh = args?.inlineMesh || {};
|
|
51272
|
-
const loaded = args?.config !== void 0 ? { config: args.config, source: "inline", sourceType: "mesh_policy" } : loadMeshRefineConfig(mesh, workspace);
|
|
51273
|
-
const validation = loaded.config ? validateMeshRefineConfig(loaded.config, loaded.source) : { valid: false, errors: [loaded.error || "repo mesh/refine config unavailable"], commands: [], rejectedCommands: [] };
|
|
51274
|
-
return { success: validation.valid, ...loaded, ...validation };
|
|
51275
|
-
}
|
|
51276
|
-
case "suggest_mesh_refine_config": {
|
|
51277
|
-
const workspace = typeof args?.workspace === "string" ? args.workspace : process.cwd();
|
|
51278
|
-
const mesh = args?.inlineMesh || {};
|
|
51279
|
-
return {
|
|
51280
|
-
success: true,
|
|
51281
|
-
...suggestMeshRefineConfig(mesh, workspace),
|
|
51282
|
-
note: "Suggestions are heuristic scaffold only; Refinery will not execute them until saved into repo mesh/refine config."
|
|
51283
|
-
};
|
|
51284
|
-
}
|
|
51285
|
-
case "get_mesh_change_impact_config_schema": {
|
|
51286
|
-
return {
|
|
51287
|
-
success: true,
|
|
51288
|
-
schema: CHANGE_IMPACT_CONFIG_SCHEMA,
|
|
51289
|
-
locations: CHANGE_IMPACT_CONFIG_LOCATIONS,
|
|
51290
|
-
sourceOfTruth: "repo change-impact config",
|
|
51291
|
-
heuristicRole: "suggestions_only_not_execution_path",
|
|
51292
|
-
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."
|
|
51293
|
-
};
|
|
51294
|
-
}
|
|
51295
|
-
case "validate_mesh_change_impact_config": {
|
|
51296
|
-
const workspace = typeof args?.workspace === "string" ? args.workspace : process.cwd();
|
|
51297
|
-
if (args?.config !== void 0) {
|
|
51298
|
-
const validation = validateChangeImpactConfig(args.config, "inline");
|
|
51299
|
-
return { success: validation.valid, source: "inline", sourceType: "mesh_policy", ...validation };
|
|
51300
|
-
}
|
|
51301
|
-
const loaded = loadChangeImpactConfig(workspace);
|
|
51302
|
-
if (loaded.sourceType === "repo_file") {
|
|
51303
|
-
const validation = validateChangeImpactConfig(loaded.config, loaded.source);
|
|
51304
|
-
return { success: validation.valid, ...loaded, ...validation };
|
|
51305
|
-
}
|
|
51306
|
-
return {
|
|
51307
|
-
success: false,
|
|
51308
|
-
...loaded,
|
|
51309
|
-
valid: false,
|
|
51310
|
-
errors: [loaded.error || "repo change-impact config unavailable"]
|
|
51311
|
-
};
|
|
51312
|
-
}
|
|
51313
|
-
case "suggest_mesh_change_impact_config": {
|
|
51314
|
-
const workspace = typeof args?.workspace === "string" ? args.workspace : process.cwd();
|
|
51315
|
-
return {
|
|
51316
|
-
success: true,
|
|
51317
|
-
...suggestChangeImpactConfig(workspace),
|
|
51318
|
-
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."
|
|
51319
|
-
};
|
|
51320
|
-
}
|
|
51321
51387
|
case "mesh_init": {
|
|
51322
51388
|
const workspace = typeof args?.workspace === "string" && args.workspace.trim() ? args.workspace.trim() : process.cwd();
|
|
51323
51389
|
const mesh = args?.inlineMesh || {};
|
|
@@ -52520,7 +52586,7 @@ ${ptyResult.output.slice(-2e3)}`);
|
|
|
52520
52586
|
const isSelfNode = Boolean(
|
|
52521
52587
|
nodeId && inlineCoordinatorNodeId && nodeId === inlineCoordinatorNodeId
|
|
52522
52588
|
) || Boolean(
|
|
52523
|
-
daemonId && (daemonId
|
|
52589
|
+
daemonId && (daemonIdsEquivalent(daemonId, localMachineId) || daemonIdsEquivalent(daemonId, this.deps.statusInstanceId))
|
|
52524
52590
|
) || Boolean(meshRecord?.inline && nodeIndex === 0) || sparseConfiguredCoordinatorNode;
|
|
52525
52591
|
const machineIdentity = buildMeshNodeMachineIdentity(node, {
|
|
52526
52592
|
localMachineId,
|