@adhdev/daemon-core 1.0.18-rc.10 → 1.0.18-rc.11
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/index.js +690 -35
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +690 -35
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-active-work.d.ts +1 -1
- package/dist/mesh/mesh-ledger.d.ts +1 -1
- package/dist/mesh/mesh-node-identity.d.ts +23 -0
- package/dist/mesh/mesh-refine-gates.d.ts +89 -0
- package/dist/mesh/mesh-work-queue.d.ts +24 -1
- package/dist/providers/cli-provider-instance-types.d.ts +2 -0
- package/dist/providers/sdk/v1/builders/cli/detect-status.d.ts +14 -0
- package/dist/providers/types/interactive-prompt.d.ts +18 -0
- package/package.json +3 -3
- package/src/commands/high-family/mesh-events.ts +13 -2
- package/src/commands/med-family/mesh-queue.ts +74 -1
- package/src/commands/router-refine.ts +71 -4
- package/src/commands/router.ts +8 -0
- package/src/mesh/coordinator-prompt.ts +2 -1
- package/src/mesh/mesh-active-work.ts +11 -1
- package/src/mesh/mesh-event-classify.ts +19 -0
- package/src/mesh/mesh-event-forwarding.ts +46 -4
- package/src/mesh/mesh-events-utils.ts +42 -0
- package/src/mesh/mesh-ledger.ts +5 -0
- package/src/mesh/mesh-node-identity.ts +49 -0
- package/src/mesh/mesh-queue-assignment.ts +18 -1
- package/src/mesh/mesh-reconcile-loop.ts +6 -1
- package/src/mesh/mesh-refine-gates.ts +300 -0
- package/src/mesh/mesh-work-queue.ts +85 -2
- package/src/providers/cli-provider-instance-types.ts +28 -0
- package/src/providers/cli-provider-instance.ts +90 -15
- package/src/providers/sdk/v1/builders/cli/detect-status.ts +23 -0
- package/src/providers/sdk/v1/builders/cli/parse-approval.ts +20 -0
- package/src/providers/types/interactive-prompt.ts +77 -0
package/dist/index.js
CHANGED
|
@@ -442,10 +442,10 @@ function readInjected(value) {
|
|
|
442
442
|
}
|
|
443
443
|
function getDaemonBuildInfo() {
|
|
444
444
|
if (cached) return cached;
|
|
445
|
-
const commit = readInjected(true ? "
|
|
446
|
-
const commitShort = readInjected(true ? "
|
|
447
|
-
const version = readInjected(true ? "1.0.18-rc.
|
|
448
|
-
const builtAt = readInjected(true ? "2026-07-
|
|
445
|
+
const commit = readInjected(true ? "99f9c20bcf4ceec1e7ab2bfe6c8f641ca88da360" : void 0) ?? "unknown";
|
|
446
|
+
const commitShort = readInjected(true ? "99f9c20b" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
447
|
+
const version = readInjected(true ? "1.0.18-rc.11" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
448
|
+
const builtAt = readInjected(true ? "2026-07-22T10:18:44.739Z" : void 0);
|
|
449
449
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
450
450
|
return cached;
|
|
451
451
|
}
|
|
@@ -2635,6 +2635,51 @@ var init_hash = __esm({
|
|
|
2635
2635
|
});
|
|
2636
2636
|
|
|
2637
2637
|
// ../mesh-shared/dist/index.mjs
|
|
2638
|
+
var dist_exports = {};
|
|
2639
|
+
__export(dist_exports, {
|
|
2640
|
+
CANONICAL_MESH_TOOL_COUNT: () => CANONICAL_MESH_TOOL_COUNT,
|
|
2641
|
+
CANONICAL_MESH_TOOL_NAMES: () => CANONICAL_MESH_TOOL_NAMES,
|
|
2642
|
+
DEFAULT_DIFFICULTY_BRAINS: () => DEFAULT_DIFFICULTY_BRAINS,
|
|
2643
|
+
MAGI_RAW_ANSWER_CAP: () => MAGI_RAW_ANSWER_CAP,
|
|
2644
|
+
MESH_TASK_DIFFICULTIES: () => MESH_TASK_DIFFICULTIES,
|
|
2645
|
+
STATUS_PROBE_ARG_KEY: () => STATUS_PROBE_ARG_KEY,
|
|
2646
|
+
argsCarryStatusProbeMarker: () => argsCarryStatusProbeMarker,
|
|
2647
|
+
canonicalDaemonId: () => canonicalDaemonId,
|
|
2648
|
+
daemonIdsEquivalent: () => daemonIdsEquivalent,
|
|
2649
|
+
deriveSlotsFromLegacy: () => deriveSlotsFromLegacy,
|
|
2650
|
+
expandDaemonIdForms: () => expandDaemonIdForms,
|
|
2651
|
+
hasGitStatusEvidence: () => hasGitStatusEvidence,
|
|
2652
|
+
interpolateArgs: () => interpolateArgs,
|
|
2653
|
+
interpolateString: () => interpolateString,
|
|
2654
|
+
isMeshTaskDifficulty: () => isMeshTaskDifficulty,
|
|
2655
|
+
joinRepoPath: () => joinRepoPath,
|
|
2656
|
+
machineCoreFromDaemonId: () => machineCoreFromDaemonId,
|
|
2657
|
+
meshNodeIdMatches: () => meshNodeIdMatches,
|
|
2658
|
+
meshWorkspacesEquivalent: () => meshWorkspacesEquivalent,
|
|
2659
|
+
normalizeBrainSlot: () => normalizeBrainSlot,
|
|
2660
|
+
normalizeDifficultyBrainMap: () => normalizeDifficultyBrainMap,
|
|
2661
|
+
normalizeGitStatus: () => normalizeGitStatus,
|
|
2662
|
+
normalizeMeshNodeId: () => normalizeMeshNodeId,
|
|
2663
|
+
normalizeMeshSessionRecord: () => normalizeMeshSessionRecord,
|
|
2664
|
+
normalizeMeshWorkspaceForCompare: () => normalizeMeshWorkspaceForCompare,
|
|
2665
|
+
normalizeNodeCapabilitySlot: () => normalizeNodeCapabilitySlot,
|
|
2666
|
+
normalizeNodeCapabilitySlots: () => normalizeNodeCapabilitySlots,
|
|
2667
|
+
normalizeThinkingLevel: () => normalizeThinkingLevel,
|
|
2668
|
+
parseJsonRecord: () => parseJsonRecord,
|
|
2669
|
+
pickBestTransitGitStatus: () => pickBestTransitGitStatus,
|
|
2670
|
+
readBoolean: () => readBoolean,
|
|
2671
|
+
readGitSubmodules: () => readGitSubmodules,
|
|
2672
|
+
readNumber: () => readNumber,
|
|
2673
|
+
readRecord: () => readRecord,
|
|
2674
|
+
readString: () => readString2,
|
|
2675
|
+
readStringArray: () => readStringArray2,
|
|
2676
|
+
scoreGitStatusCandidate: () => scoreGitStatusCandidate,
|
|
2677
|
+
scoreGitUpstreamFreshness: () => scoreGitUpstreamFreshness,
|
|
2678
|
+
sessionIdsEquivalent: () => sessionIdsEquivalent,
|
|
2679
|
+
stripStatusProbeMarker: () => stripStatusProbeMarker,
|
|
2680
|
+
summarizeGitShape: () => summarizeGitShape,
|
|
2681
|
+
withStatusProbeMarker: () => withStatusProbeMarker
|
|
2682
|
+
});
|
|
2638
2683
|
function readRecord(value) {
|
|
2639
2684
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
2640
2685
|
}
|
|
@@ -2658,6 +2703,21 @@ function readBoolean(...values) {
|
|
|
2658
2703
|
}
|
|
2659
2704
|
return void 0;
|
|
2660
2705
|
}
|
|
2706
|
+
function readStringArray2(value) {
|
|
2707
|
+
return Array.isArray(value) ? value.filter((entry) => typeof entry === "string" && entry.trim().length > 0) : [];
|
|
2708
|
+
}
|
|
2709
|
+
function parseJsonRecord(value) {
|
|
2710
|
+
if (!value) return {};
|
|
2711
|
+
if (typeof value === "object") return readRecord(value);
|
|
2712
|
+
if (typeof value !== "string") return {};
|
|
2713
|
+
const trimmed = value.trim();
|
|
2714
|
+
if (!trimmed) return {};
|
|
2715
|
+
try {
|
|
2716
|
+
return readRecord(JSON.parse(trimmed));
|
|
2717
|
+
} catch {
|
|
2718
|
+
return {};
|
|
2719
|
+
}
|
|
2720
|
+
}
|
|
2661
2721
|
function joinRepoPath(root, relativePath) {
|
|
2662
2722
|
const normalizedRoot = typeof root === "string" ? root.trim().replace(/[\\/]+$/, "") : "";
|
|
2663
2723
|
const normalizedPath = typeof relativePath === "string" ? relativePath.trim() : "";
|
|
@@ -2798,12 +2858,48 @@ function pickBestTransitGitStatus(node, options) {
|
|
|
2798
2858
|
}
|
|
2799
2859
|
return best?.git;
|
|
2800
2860
|
}
|
|
2861
|
+
function deriveSyntheticSessionId(record) {
|
|
2862
|
+
const parts = [
|
|
2863
|
+
readString2(record.workspace),
|
|
2864
|
+
readString2(record.providerType, record.provider),
|
|
2865
|
+
readString2(record.role),
|
|
2866
|
+
readString2(record.state, record.status),
|
|
2867
|
+
readString2(record.title),
|
|
2868
|
+
readString2(record.createdAt, record.created_at),
|
|
2869
|
+
readString2(record.startedAt, record.started_at)
|
|
2870
|
+
].filter((part) => Boolean(part));
|
|
2871
|
+
if (parts.length === 0) return void 0;
|
|
2872
|
+
return `synthetic:${parts.join("|")}`;
|
|
2873
|
+
}
|
|
2801
2874
|
function sessionIdsEquivalent(a, b) {
|
|
2802
2875
|
const idA = readString2(a);
|
|
2803
2876
|
const idB = readString2(b);
|
|
2804
2877
|
if (!idA || !idB) return false;
|
|
2805
2878
|
return idA === idB;
|
|
2806
2879
|
}
|
|
2880
|
+
function normalizeMeshSessionRecord(entry) {
|
|
2881
|
+
const record = readRecord(entry);
|
|
2882
|
+
const sessionId = readString2(record.sessionId, record.session_id, record.id) ?? deriveSyntheticSessionId(record);
|
|
2883
|
+
if (!sessionId) return null;
|
|
2884
|
+
return {
|
|
2885
|
+
sessionId,
|
|
2886
|
+
...readString2(record.providerType, record.provider) ? { providerType: readString2(record.providerType, record.provider) } : {},
|
|
2887
|
+
...readString2(record.state, record.status) ? { state: readString2(record.state, record.status) } : {},
|
|
2888
|
+
...readString2(record.chatStatus, record.chat_status) ? { chatStatus: readString2(record.chatStatus, record.chat_status) } : {},
|
|
2889
|
+
...readString2(record.lifecycle) ? { lifecycle: readString2(record.lifecycle) } : {},
|
|
2890
|
+
...readString2(record.surfaceKind, record.surface_kind) ? { surfaceKind: readString2(record.surfaceKind, record.surface_kind) } : {},
|
|
2891
|
+
...readString2(record.recoveryState, record.recovery_state) ? { recoveryState: readString2(record.recoveryState, record.recovery_state) } : {},
|
|
2892
|
+
...readString2(record.workspace) ? { workspace: readString2(record.workspace) } : {},
|
|
2893
|
+
...readString2(record.title) ? { title: readString2(record.title) } : {},
|
|
2894
|
+
...readString2(record.role) ? { role: readString2(record.role) } : {},
|
|
2895
|
+
...readBoolean(record.isSelfCoordinator, record.is_self_coordinator) !== void 0 ? { isSelfCoordinator: readBoolean(record.isSelfCoordinator, record.is_self_coordinator) } : {},
|
|
2896
|
+
...readString2(record.statusNote, record.status_note) ? { statusNote: readString2(record.statusNote, record.status_note) } : {},
|
|
2897
|
+
...readString2(record.createdAt, record.created_at) ? { createdAt: readString2(record.createdAt, record.created_at) } : {},
|
|
2898
|
+
...readString2(record.startedAt, record.started_at) ? { startedAt: readString2(record.startedAt, record.started_at) } : {},
|
|
2899
|
+
...readString2(record.lastActivityAt, record.last_activity_at) ? { lastActivityAt: readString2(record.lastActivityAt, record.last_activity_at) } : {},
|
|
2900
|
+
...readBoolean(record.isCached, record.is_cached) !== void 0 ? { isCached: readBoolean(record.isCached, record.is_cached) } : {}
|
|
2901
|
+
};
|
|
2902
|
+
}
|
|
2807
2903
|
function normalizeMeshNodeId(node) {
|
|
2808
2904
|
const record = node && typeof node === "object" ? node : {};
|
|
2809
2905
|
return readString2(record.id, record.nodeId, record.node_id);
|
|
@@ -2986,9 +3082,30 @@ function deriveSlotsFromLegacy(input) {
|
|
|
2986
3082
|
};
|
|
2987
3083
|
});
|
|
2988
3084
|
}
|
|
3085
|
+
function interpolateArgs(args, context) {
|
|
3086
|
+
const result = {};
|
|
3087
|
+
for (const [k, v] of Object.entries(args)) {
|
|
3088
|
+
result[k] = typeof v === "string" ? interpolateString(v, context) : v;
|
|
3089
|
+
}
|
|
3090
|
+
return result;
|
|
3091
|
+
}
|
|
3092
|
+
function interpolateString(template, ctx) {
|
|
3093
|
+
return template.replace(/\{\{(\w+)\}\}/g, (_, key2) => {
|
|
3094
|
+
const val = ctx[key2];
|
|
3095
|
+
return val !== void 0 ? String(val) : `{{${key2}}}`;
|
|
3096
|
+
});
|
|
3097
|
+
}
|
|
2989
3098
|
function withStatusProbeMarker(args = {}) {
|
|
2990
3099
|
return { ...args, [STATUS_PROBE_ARG_KEY]: true };
|
|
2991
3100
|
}
|
|
3101
|
+
function argsCarryStatusProbeMarker(args) {
|
|
3102
|
+
return !!args && typeof args === "object" && args[STATUS_PROBE_ARG_KEY] === true;
|
|
3103
|
+
}
|
|
3104
|
+
function stripStatusProbeMarker(args) {
|
|
3105
|
+
if (!argsCarryStatusProbeMarker(args)) return args;
|
|
3106
|
+
const { [STATUS_PROBE_ARG_KEY]: _drop, ...rest } = args;
|
|
3107
|
+
return rest;
|
|
3108
|
+
}
|
|
2992
3109
|
var DAEMON_ID_PREFIXES, MAGI_RAW_ANSWER_CAP, MESH_TASK_DIFFICULTIES, DEFAULT_DIFFICULTY_BRAINS, CANONICAL_MESH_TOOL_NAMES, CANONICAL_MESH_TOOL_COUNT, STATUS_PROBE_ARG_KEY;
|
|
2993
3110
|
var init_dist = __esm({
|
|
2994
3111
|
"../mesh-shared/dist/index.mjs"() {
|
|
@@ -3020,6 +3137,7 @@ var init_dist = __esm({
|
|
|
3020
3137
|
"mesh_restart_daemon",
|
|
3021
3138
|
"mesh_checkpoint",
|
|
3022
3139
|
"mesh_approve",
|
|
3140
|
+
"mesh_answer_question",
|
|
3023
3141
|
"mesh_list_pending_approvals",
|
|
3024
3142
|
"mesh_clone_node",
|
|
3025
3143
|
"mesh_remove_node",
|
|
@@ -4458,6 +4576,42 @@ ${surfaced}`;
|
|
|
4458
4576
|
if (args.event === "agent:waiting_approval") {
|
|
4459
4577
|
return `[System] ${args.nodeLabel} is waiting for approval to proceed${metadata}. You may use mesh_read_chat and mesh_approve to handle it.`;
|
|
4460
4578
|
}
|
|
4579
|
+
if (args.event === "agent:waiting_choice") {
|
|
4580
|
+
const prompt = args.metadataEvent.interactivePrompt;
|
|
4581
|
+
const promptId = readNonEmptyString(args.metadataEvent.promptId) || prompt && readNonEmptyString(prompt.promptId);
|
|
4582
|
+
const lines = [
|
|
4583
|
+
`[System] ${args.nodeLabel} is asking a question and is waiting for your answer${metadata}.`
|
|
4584
|
+
];
|
|
4585
|
+
const questions = Array.isArray(prompt?.questions) ? prompt.questions : [];
|
|
4586
|
+
if (questions.length > 0) {
|
|
4587
|
+
for (const q of questions) {
|
|
4588
|
+
const header = readNonEmptyString(q.header);
|
|
4589
|
+
const question = readNonEmptyString(q.question);
|
|
4590
|
+
const multiSelect = q.multiSelect === true;
|
|
4591
|
+
if (question) {
|
|
4592
|
+
lines.push(`
|
|
4593
|
+
**${header ? `${header}: ` : ""}${question}**${multiSelect ? " (select one or more)" : ""}`);
|
|
4594
|
+
}
|
|
4595
|
+
const options = Array.isArray(q.options) ? q.options : [];
|
|
4596
|
+
options.forEach((opt, i) => {
|
|
4597
|
+
const record = opt && typeof opt === "object" ? opt : {};
|
|
4598
|
+
const label = readNonEmptyString(record.label);
|
|
4599
|
+
if (!label) return;
|
|
4600
|
+
const description = readNonEmptyString(record.description);
|
|
4601
|
+
lines.push(` ${i + 1}. ${label}${description ? ` \u2014 ${description}` : ""}`);
|
|
4602
|
+
});
|
|
4603
|
+
}
|
|
4604
|
+
} else {
|
|
4605
|
+
const modalMessage = readNonEmptyString(args.metadataEvent.modalMessage);
|
|
4606
|
+
if (modalMessage) lines.push(`
|
|
4607
|
+
${modalMessage}`);
|
|
4608
|
+
}
|
|
4609
|
+
lines.push(
|
|
4610
|
+
`
|
|
4611
|
+
Answer with mesh_answer_question(node_id, session_id${promptId ? `, promptId: "${promptId}"` : ""}, answers). Do NOT use mesh_approve \u2014 that only resolves yes/no consent modals, not a question. Use mesh_read_chat once if you need the full context first.`
|
|
4612
|
+
);
|
|
4613
|
+
return lines.join("\n");
|
|
4614
|
+
}
|
|
4461
4615
|
if (args.event === "agent:stopped") {
|
|
4462
4616
|
const rc = args.recoveryContext;
|
|
4463
4617
|
if (rc && rc.consecutiveNodeFailures > 0) {
|
|
@@ -5959,6 +6113,7 @@ __export(mesh_work_queue_exports, {
|
|
|
5959
6113
|
requeueTask: () => requeueTask,
|
|
5960
6114
|
resolveConvergeRequiredTags: () => resolveConvergeRequiredTags,
|
|
5961
6115
|
resolveNotBefore: () => resolveNotBefore,
|
|
6116
|
+
takeCancelledTaskAssignment: () => takeCancelledTaskAssignment,
|
|
5962
6117
|
taskDependenciesSatisfied: () => taskDependenciesSatisfied,
|
|
5963
6118
|
updateDirectDispatchStatus: () => updateDirectDispatchStatus,
|
|
5964
6119
|
updateSessionTaskStatus: () => updateSessionTaskStatus,
|
|
@@ -6518,6 +6673,10 @@ function updateTaskStatus(meshId, taskId, status, opts) {
|
|
|
6518
6673
|
const result = withQueueLock(meshId, () => {
|
|
6519
6674
|
const entry = MeshRuntimeStore.getInstance().findQueueEntryById(meshId, taskId);
|
|
6520
6675
|
if (!entry) return null;
|
|
6676
|
+
if (!opts?.force && TERMINAL_TASK_STATUSES.has(entry.status) && !TERMINAL_TASK_STATUSES.has(status)) {
|
|
6677
|
+
LOG.debug("MeshQueue", `Refusing updateTaskStatus(${taskId} \u2192 ${status}) on mesh ${meshId}: row is terminal (${entry.status}). A late writer (e.g. dispatch-failure requeue) must not resurrect a cancelled/completed/failed task. Pass force to override.`);
|
|
6678
|
+
return { entry, cascaded: [] };
|
|
6679
|
+
}
|
|
6521
6680
|
entry.status = status;
|
|
6522
6681
|
MeshRuntimeStore.getInstance().updateQueueEntry(entry);
|
|
6523
6682
|
if (status !== "assigned") endTaskDispatchInFlight(meshId, taskId);
|
|
@@ -6543,17 +6702,34 @@ function cancelTask(meshId, taskId, opts) {
|
|
|
6543
6702
|
const entry = MeshRuntimeStore.getInstance().findQueueEntryById(meshId, taskId);
|
|
6544
6703
|
if (!entry) return null;
|
|
6545
6704
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6705
|
+
const priorAssignment = entry.assignedSessionId ? {
|
|
6706
|
+
sessionId: entry.assignedSessionId,
|
|
6707
|
+
nodeId: entry.assignedNodeId,
|
|
6708
|
+
providerType: entry.assignedProviderType
|
|
6709
|
+
} : void 0;
|
|
6546
6710
|
entry.status = "cancelled";
|
|
6547
6711
|
entry.cancelledAt = now;
|
|
6548
6712
|
if (opts?.reason) entry.cancelReason = opts.reason;
|
|
6713
|
+
delete entry.assignedNodeId;
|
|
6714
|
+
delete entry.assignedSessionId;
|
|
6715
|
+
delete entry.assignedProviderType;
|
|
6716
|
+
delete entry.dispatchTimestamp;
|
|
6717
|
+
entry.dispatchNonce = (entry.dispatchNonce || 0) + 1;
|
|
6549
6718
|
MeshRuntimeStore.getInstance().updateQueueEntry(entry);
|
|
6550
6719
|
endTaskDispatchInFlight(meshId, taskId);
|
|
6551
6720
|
const cascaded = propagateDependencyFailure(meshId, taskId);
|
|
6552
|
-
return { entry, cascaded };
|
|
6721
|
+
return { entry, cascaded, priorAssignment };
|
|
6553
6722
|
});
|
|
6554
6723
|
if (result) scheduleMissionCloseCandidateCheck(meshId, [result.entry, ...result.cascaded]);
|
|
6724
|
+
if (result?.priorAssignment) lastCancelledTaskAssignment.set(`${meshId}::${taskId}`, result.priorAssignment);
|
|
6555
6725
|
return result ? result.entry : null;
|
|
6556
6726
|
}
|
|
6727
|
+
function takeCancelledTaskAssignment(meshId, taskId) {
|
|
6728
|
+
const key2 = `${meshId}::${taskId}`;
|
|
6729
|
+
const value = lastCancelledTaskAssignment.get(key2);
|
|
6730
|
+
if (value) lastCancelledTaskAssignment.delete(key2);
|
|
6731
|
+
return value;
|
|
6732
|
+
}
|
|
6557
6733
|
function requeueTask(meshId, taskId, opts) {
|
|
6558
6734
|
requireMeshHostQueueOwner(opts);
|
|
6559
6735
|
const result = withQueueLock(meshId, () => {
|
|
@@ -6768,7 +6944,7 @@ function recordMeshToolCall(opts) {
|
|
|
6768
6944
|
return { rateLimitExceeded: false, callsInWindow: 0, advisory: null };
|
|
6769
6945
|
}
|
|
6770
6946
|
}
|
|
6771
|
-
var import_crypto7, ACTIVE_MESH_QUEUE_STATUSES, HISTORICAL_MESH_QUEUE_STATUSES, MESH_TASK_MODES, MESH_TASK_PRIORITIES, NOT_BEFORE_RELATIVE_THRESHOLD_MS, LIVE_DEBUG_READONLY_FORBIDDEN, NEGATION_CUES, NEGATION_WINDOW_TOKENS, GIT_MUTATION_SUBCOMMANDS, GIT_STASH_READONLY_SUBCOMMANDS, DEPENDENCY_FAILURE_TERMINALS, MAX_STRANDED_RECLAIMS, REDRIVE_RECLAIM_REASONS, REDRIVE_SUPERSEDE_WINDOW_MS;
|
|
6947
|
+
var import_crypto7, ACTIVE_MESH_QUEUE_STATUSES, HISTORICAL_MESH_QUEUE_STATUSES, MESH_TASK_MODES, MESH_TASK_PRIORITIES, NOT_BEFORE_RELATIVE_THRESHOLD_MS, LIVE_DEBUG_READONLY_FORBIDDEN, NEGATION_CUES, NEGATION_WINDOW_TOKENS, GIT_MUTATION_SUBCOMMANDS, GIT_STASH_READONLY_SUBCOMMANDS, DEPENDENCY_FAILURE_TERMINALS, TERMINAL_TASK_STATUSES, lastCancelledTaskAssignment, MAX_STRANDED_RECLAIMS, REDRIVE_RECLAIM_REASONS, REDRIVE_SUPERSEDE_WINDOW_MS;
|
|
6772
6948
|
var init_mesh_work_queue = __esm({
|
|
6773
6949
|
"src/mesh/mesh-work-queue.ts"() {
|
|
6774
6950
|
"use strict";
|
|
@@ -6836,6 +7012,8 @@ var init_mesh_work_queue = __esm({
|
|
|
6836
7012
|
]);
|
|
6837
7013
|
GIT_STASH_READONLY_SUBCOMMANDS = /* @__PURE__ */ new Set(["list", "show"]);
|
|
6838
7014
|
DEPENDENCY_FAILURE_TERMINALS = /* @__PURE__ */ new Set(["failed", "cancelled"]);
|
|
7015
|
+
TERMINAL_TASK_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "cancelled"]);
|
|
7016
|
+
lastCancelledTaskAssignment = /* @__PURE__ */ new Map();
|
|
6839
7017
|
MAX_STRANDED_RECLAIMS = 3;
|
|
6840
7018
|
REDRIVE_RECLAIM_REASONS = /* @__PURE__ */ new Set([
|
|
6841
7019
|
"delivered_no_turn_deadline",
|
|
@@ -8932,7 +9110,7 @@ function compactLedger(meshId) {
|
|
|
8932
9110
|
function readNonEmptyString2(value) {
|
|
8933
9111
|
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
8934
9112
|
}
|
|
8935
|
-
function
|
|
9113
|
+
function readStringArray3(value) {
|
|
8936
9114
|
if (!Array.isArray(value)) return [];
|
|
8937
9115
|
return value.map((item) => readNonEmptyString2(item)).filter(Boolean);
|
|
8938
9116
|
}
|
|
@@ -8992,11 +9170,11 @@ function normalizeMeshWorkerResult(input, source = "explicit_metadata") {
|
|
|
8992
9170
|
return {
|
|
8993
9171
|
status,
|
|
8994
9172
|
...readNonEmptyString2(raw.classification) ? { classification: readNonEmptyString2(raw.classification) } : {},
|
|
8995
|
-
changedFiles:
|
|
9173
|
+
changedFiles: readStringArray3(raw.changedFiles),
|
|
8996
9174
|
validationResults: normalizeValidationResults(raw.validationResults),
|
|
8997
9175
|
...gitStatus ? { gitStatus } : {},
|
|
8998
9176
|
processArtifacts: normalizeProcessArtifacts(raw.processArtifacts),
|
|
8999
|
-
errors:
|
|
9177
|
+
errors: readStringArray3(raw.errors),
|
|
9000
9178
|
...readNonEmptyString2(raw.nextAction) ? { nextAction: readNonEmptyString2(raw.nextAction) } : {},
|
|
9001
9179
|
requiresUserAction: raw.requiresUserAction === true,
|
|
9002
9180
|
source
|
|
@@ -10145,7 +10323,8 @@ var init_coordinator_prompt = __esm({
|
|
|
10145
10323
|
| \`mesh_fast_forward_node\` | Safely dry-run or explicitly execute an obvious clean fast-forward without launching an agent session |
|
|
10146
10324
|
| \`mesh_restart_daemon\` | Update a node's daemon to the latest published version on its channel and restart it (the dashboard "preview update" path, as a mesh command) |
|
|
10147
10325
|
| \`mesh_checkpoint\` | Create a git checkpoint on a node |
|
|
10148
|
-
| \`mesh_approve\` | Approve/reject a pending agent action |
|
|
10326
|
+
| \`mesh_approve\` | Approve/reject a pending agent action (a yes/no tool-consent modal) |
|
|
10327
|
+
| \`mesh_answer_question\` | Answer a delegated session's multi-choice QUESTION (AskUserQuestion / status awaiting_choice). NOT an approval \u2014 it offers labelled options (possibly multi-select or freeform). Pass the promptId from the agent:waiting_choice event + one answer per question (select by option label or 1-based index). Use this, never mesh_approve, for a question |
|
|
10149
10328
|
| \`mesh_list_pending_approvals\` | List every session across the mesh awaiting an approval decision (the approval inbox) \u2014 read-only; enumerate all blocked sessions at once, then drive a mesh_approve for each |
|
|
10150
10329
|
| \`mesh_clone_node\` | Create a worktree node for isolated parallel branch work |
|
|
10151
10330
|
| \`mesh_refine_node\` | Validate and merge a completed worktree node back into its base branch |
|
|
@@ -10383,7 +10562,7 @@ function readString5(value) {
|
|
|
10383
10562
|
function readRecord4(value) {
|
|
10384
10563
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
10385
10564
|
}
|
|
10386
|
-
function
|
|
10565
|
+
function readStringArray4(value, max) {
|
|
10387
10566
|
if (!Array.isArray(value)) return { values: [], truncated: false };
|
|
10388
10567
|
const values = value.filter((item) => typeof item === "string" && item.trim().length > 0);
|
|
10389
10568
|
return { values: values.slice(0, max), truncated: values.length > max };
|
|
@@ -10413,14 +10592,14 @@ function isMergeCandidate(convergence) {
|
|
|
10413
10592
|
function readWorkerArtifact(value) {
|
|
10414
10593
|
const worker = readRecord4(value);
|
|
10415
10594
|
if (!worker) return null;
|
|
10416
|
-
const changed =
|
|
10595
|
+
const changed = readStringArray4(worker.changedFiles, MAX_CHANGED_FILES);
|
|
10417
10596
|
return {
|
|
10418
10597
|
status: readString5(worker.status) ?? "unknown",
|
|
10419
10598
|
...readString5(worker.classification) ? { classification: readString5(worker.classification) } : {},
|
|
10420
10599
|
changedFiles: changed.values,
|
|
10421
10600
|
...changed.truncated ? { changedFilesTruncated: true } : {},
|
|
10422
10601
|
validationResults: Array.isArray(worker.validationResults) ? worker.validationResults.map((item) => readRecord4(item)).filter((item) => item !== null) : [],
|
|
10423
|
-
errors:
|
|
10602
|
+
errors: readStringArray4(worker.errors, 20).values,
|
|
10424
10603
|
requiresUserAction: worker.requiresUserAction === true
|
|
10425
10604
|
};
|
|
10426
10605
|
}
|
|
@@ -13549,6 +13728,61 @@ var init_runtime_defaults = __esm({
|
|
|
13549
13728
|
});
|
|
13550
13729
|
|
|
13551
13730
|
// src/mesh/mesh-node-identity.ts
|
|
13731
|
+
var mesh_node_identity_exports = {};
|
|
13732
|
+
__export(mesh_node_identity_exports, {
|
|
13733
|
+
MESH_DIRECT_PROBE_CONNECT_TIMEOUT_MS: () => MESH_DIRECT_PROBE_CONNECT_TIMEOUT_MS,
|
|
13734
|
+
MESH_DIRECT_PROBE_RETRY_TIMEOUT_MS: () => MESH_DIRECT_PROBE_RETRY_TIMEOUT_MS,
|
|
13735
|
+
MESH_DIRECT_PROBE_REUSE_MS: () => MESH_DIRECT_PROBE_REUSE_MS,
|
|
13736
|
+
MESH_DIRECT_PROBE_TIMEOUT_MS: () => MESH_DIRECT_PROBE_TIMEOUT_MS,
|
|
13737
|
+
MESH_NODE_LIVE_TRUTH_MARKER: () => MESH_NODE_LIVE_TRUTH_MARKER,
|
|
13738
|
+
MeshGitProbeCache: () => MeshGitProbeCache,
|
|
13739
|
+
applyCachedInlineMeshNodeStatus: () => applyCachedInlineMeshNodeStatus,
|
|
13740
|
+
applyInlineMeshBranchConvergence: () => applyInlineMeshBranchConvergence,
|
|
13741
|
+
awaitWithWarmupDeadline: () => awaitWithWarmupDeadline,
|
|
13742
|
+
buildHistoricalMeshSessions: () => buildHistoricalMeshSessions,
|
|
13743
|
+
buildInlineMeshTransitGitStatus: () => buildInlineMeshTransitGitStatus,
|
|
13744
|
+
buildLivePeerGitConnection: () => buildLivePeerGitConnection,
|
|
13745
|
+
buildMeshNodeDataFreshness: () => buildMeshNodeDataFreshness,
|
|
13746
|
+
buildMeshNodeDisplayLabel: () => buildMeshNodeDisplayLabel,
|
|
13747
|
+
buildMeshNodeMachineIdentity: () => buildMeshNodeMachineIdentity,
|
|
13748
|
+
buildMeshNodeProbeFreshness: () => buildMeshNodeProbeFreshness,
|
|
13749
|
+
collectLiveMeshSessionRecords: () => collectLiveMeshSessionRecords,
|
|
13750
|
+
collectMeshNodeHostedSessionIds: () => collectMeshNodeHostedSessionIds,
|
|
13751
|
+
deriveMeshNodeHealthFromGit: () => deriveMeshNodeHealthFromGit,
|
|
13752
|
+
finalizeMeshNodeStatus: () => finalizeMeshNodeStatus,
|
|
13753
|
+
foldMeshNodeIdentityToCanonical: () => foldMeshNodeIdentityToCanonical,
|
|
13754
|
+
hydrateInlineMeshDirectTruth: () => hydrateInlineMeshDirectTruth,
|
|
13755
|
+
inlineMeshCarriesTransientNodeTruth: () => inlineMeshCarriesTransientNodeTruth,
|
|
13756
|
+
isDeadLocalWorktreeNode: () => isDeadLocalWorktreeNode,
|
|
13757
|
+
isMeshNodeFreshEnoughToLaunch: () => isMeshNodeFreshEnoughToLaunch,
|
|
13758
|
+
isMeshNodeHealthLaunchable: () => isMeshNodeHealthLaunchable,
|
|
13759
|
+
logRepoMeshStatusDebug: () => logRepoMeshStatusDebug,
|
|
13760
|
+
normalizeInlineMeshNodeIdentity: () => normalizeInlineMeshNodeIdentity,
|
|
13761
|
+
normalizeProviderRoles: () => normalizeProviderRoles,
|
|
13762
|
+
persistNodeReporterPlatform: () => persistNodeReporterPlatform,
|
|
13763
|
+
probeRemoteMeshGitStatusWithRetry: () => probeRemoteMeshGitStatusWithRetry,
|
|
13764
|
+
readBooleanValue: () => readBooleanValue,
|
|
13765
|
+
readCachedInlineMeshActiveSessionDetails: () => readCachedInlineMeshActiveSessionDetails,
|
|
13766
|
+
readCachedInlineMeshActiveSessions: () => readCachedInlineMeshActiveSessions,
|
|
13767
|
+
readInlineMeshNodeId: () => readInlineMeshNodeId,
|
|
13768
|
+
readLiveMeshNodeWorkspace: () => readLiveMeshNodeWorkspace,
|
|
13769
|
+
readMeshNodeDaemonId: () => readMeshNodeDaemonId,
|
|
13770
|
+
readMeshNodeHostname: () => readMeshNodeHostname,
|
|
13771
|
+
readMeshNodeMachineId: () => readMeshNodeMachineId,
|
|
13772
|
+
readObjectRecord: () => readObjectRecord,
|
|
13773
|
+
readProviderPriorityFromPolicy: () => readProviderPriorityFromPolicy,
|
|
13774
|
+
readStringValue: () => readStringValue,
|
|
13775
|
+
reconcileInlineMeshCache: () => reconcileInlineMeshCache,
|
|
13776
|
+
recordInlineMeshDirectGitTruth: () => recordInlineMeshDirectGitTruth,
|
|
13777
|
+
resolveEffectiveMeshNodeHealth: () => resolveEffectiveMeshNodeHealth,
|
|
13778
|
+
resolveMeshNodeAttribution: () => resolveMeshNodeAttribution,
|
|
13779
|
+
resolveProviderTypeFromPriority: () => resolveProviderTypeFromPriority,
|
|
13780
|
+
sanitizeInlineMesh: () => sanitizeInlineMesh,
|
|
13781
|
+
shouldRefreshStalePendingAggregate: () => shouldRefreshStalePendingAggregate,
|
|
13782
|
+
summarizeInlineMeshBranchConvergence: () => summarizeInlineMeshBranchConvergence,
|
|
13783
|
+
summarizeMeshSessionRecord: () => summarizeMeshSessionRecord,
|
|
13784
|
+
summarizeRepoMeshStatusDebug: () => summarizeRepoMeshStatusDebug
|
|
13785
|
+
});
|
|
13552
13786
|
function readProviderPriorityFromPolicy(policy) {
|
|
13553
13787
|
const record = policy && typeof policy === "object" && !Array.isArray(policy) ? policy : {};
|
|
13554
13788
|
const raw = record.providerPriority;
|
|
@@ -14112,6 +14346,23 @@ function isMeshNodeHealthLaunchable(node) {
|
|
|
14112
14346
|
const health = resolveEffectiveMeshNodeHealth(node);
|
|
14113
14347
|
return health === "online" || health === "unknown";
|
|
14114
14348
|
}
|
|
14349
|
+
function resolveEffectiveNodeGit(node) {
|
|
14350
|
+
const git = readObjectRecord(node?.git);
|
|
14351
|
+
if (Object.keys(git).length > 0) return git;
|
|
14352
|
+
const cachedStatus = readObjectRecord(node?.cachedStatus);
|
|
14353
|
+
return readObjectRecord(cachedStatus.git);
|
|
14354
|
+
}
|
|
14355
|
+
function isMeshNodeFreshEnoughToLaunch(node, opts) {
|
|
14356
|
+
const git = resolveEffectiveNodeGit(node);
|
|
14357
|
+
if (Object.keys(git).length === 0) return true;
|
|
14358
|
+
if (readBooleanValue(git.isGitRepo) === false) return true;
|
|
14359
|
+
const submoduleDrift = getGitSubmoduleDriftState(git);
|
|
14360
|
+
if (submoduleDrift.outOfSync) return false;
|
|
14361
|
+
const behind = readNumberValue(git.behind);
|
|
14362
|
+
if (behind === void 0) return true;
|
|
14363
|
+
const maxBehind = Number.isFinite(opts?.maxBehind) && opts?.maxBehind >= 0 ? Math.floor(opts.maxBehind) : 0;
|
|
14364
|
+
return behind <= maxBehind;
|
|
14365
|
+
}
|
|
14115
14366
|
function readMeshNodeLabel(status, node) {
|
|
14116
14367
|
return readStringValue(status.nodeId, normalizeMeshNodeId(node)) ?? "unknown";
|
|
14117
14368
|
}
|
|
@@ -15008,6 +15259,7 @@ function sessionStatusFromNodes(nodes, nodeId, sessionId) {
|
|
|
15008
15259
|
if (!session) return { staleReason: "direct task session is not present in live session records" };
|
|
15009
15260
|
if (typeof session === "string") return {};
|
|
15010
15261
|
const raw = `${readString6(session.status) || ""} ${readString6(session.lifecycle) || ""} ${readString6(session.state) || ""} ${readString6(session.activeChat?.status) || ""}`.toLowerCase();
|
|
15262
|
+
if (raw.includes("waiting_choice") || raw.includes("choice")) return { status: "awaiting_choice" };
|
|
15011
15263
|
if (raw.includes("approval")) return { status: "awaiting_approval" };
|
|
15012
15264
|
if (raw.includes("generating") || raw.includes("running") || raw.includes("busy")) return { status: "generating" };
|
|
15013
15265
|
if (raw.includes("failed") || raw.includes("stopped") || raw.includes("terminated") || raw.includes("exited")) return { status: "failed" };
|
|
@@ -15033,6 +15285,7 @@ function terminalMatchesDispatch(terminal, dispatch, taskId) {
|
|
|
15033
15285
|
}
|
|
15034
15286
|
function statusFromTerminal(entry) {
|
|
15035
15287
|
if (entry.kind === "task_approval_needed") return "awaiting_approval";
|
|
15288
|
+
if (entry.kind === "task_question_pending") return "awaiting_choice";
|
|
15036
15289
|
if (entry.kind === "task_completed") return "idle";
|
|
15037
15290
|
return "failed";
|
|
15038
15291
|
}
|
|
@@ -15111,7 +15364,8 @@ function buildMeshActiveWorkSummary(activeWork) {
|
|
|
15111
15364
|
generating: 0,
|
|
15112
15365
|
idle: 0,
|
|
15113
15366
|
failed: 0,
|
|
15114
|
-
awaiting_approval: 0
|
|
15367
|
+
awaiting_approval: 0,
|
|
15368
|
+
awaiting_choice: 0
|
|
15115
15369
|
};
|
|
15116
15370
|
const sourceCounts = { queue: 0, direct: 0 };
|
|
15117
15371
|
for (const item of activeWork) {
|
|
@@ -15146,7 +15400,7 @@ function buildMeshActiveWork(opts) {
|
|
|
15146
15400
|
const queueNodeId = task.assignedNodeId || task.targetNodeId;
|
|
15147
15401
|
const queueSessionId = task.assignedSessionId || task.targetSessionId;
|
|
15148
15402
|
const queueLive = task.status === "assigned" ? sessionStatusFromNodes(opts.nodes, queueNodeId ?? void 0, queueSessionId ?? void 0) : {};
|
|
15149
|
-
const queueStatus = queueLive.status === "awaiting_approval" || queueLive.status === "generating" ? queueLive.status : task.status;
|
|
15403
|
+
const queueStatus = queueLive.status === "awaiting_approval" || queueLive.status === "awaiting_choice" || queueLive.status === "generating" ? queueLive.status : task.status;
|
|
15150
15404
|
records.push({
|
|
15151
15405
|
taskId: task.id,
|
|
15152
15406
|
source: "queue",
|
|
@@ -16142,6 +16396,11 @@ var init_mesh_event_classify = __esm({
|
|
|
16142
16396
|
"agent:generating_started",
|
|
16143
16397
|
"agent:generating_completed",
|
|
16144
16398
|
"agent:waiting_approval",
|
|
16399
|
+
// A worker parked on an AskUserQuestion multi-choice prompt. DISTINCT from
|
|
16400
|
+
// agent:waiting_approval (a yes/no tool-consent modal): a question is answered
|
|
16401
|
+
// with mesh_answer_question, never mesh_approve (mission f1d25e11). Carries the
|
|
16402
|
+
// full InteractivePrompt payload so the coordinator can render + answer.
|
|
16403
|
+
"agent:waiting_choice",
|
|
16145
16404
|
"agent:stopped",
|
|
16146
16405
|
"agent:ready",
|
|
16147
16406
|
"monitor:no_progress",
|
|
@@ -16159,6 +16418,7 @@ var init_mesh_event_classify = __esm({
|
|
|
16159
16418
|
EVENT_TO_LEDGER_KIND = {
|
|
16160
16419
|
"agent:generating_completed": "task_completed",
|
|
16161
16420
|
"agent:waiting_approval": "task_approval_needed",
|
|
16421
|
+
"agent:waiting_choice": "task_question_pending",
|
|
16162
16422
|
"agent:stopped": "task_failed",
|
|
16163
16423
|
"monitor:no_progress": "task_stalled"
|
|
16164
16424
|
};
|
|
@@ -16166,13 +16426,26 @@ var init_mesh_event_classify = __esm({
|
|
|
16166
16426
|
"agent:generating_completed",
|
|
16167
16427
|
"agent:stopped",
|
|
16168
16428
|
"agent:waiting_approval",
|
|
16429
|
+
// A worker's question (waiting_choice) is real-time and the coordinator may be
|
|
16430
|
+
// generating when it arrives; force-inject it like approval so the busy coordinator
|
|
16431
|
+
// learns it must answer (mesh_answer_question) rather than the nudge sitting in the
|
|
16432
|
+
// adapter's outbound queue until the coordinator idles on its own (mission f1d25e11).
|
|
16433
|
+
"agent:waiting_choice",
|
|
16169
16434
|
"refine:completed",
|
|
16170
16435
|
"refine:failed",
|
|
16171
16436
|
"worktree_bootstrap_complete",
|
|
16172
16437
|
"worktree_bootstrap_failed"
|
|
16173
16438
|
]);
|
|
16174
16439
|
MESH_APPROVAL_EVENTS = /* @__PURE__ */ new Set([
|
|
16175
|
-
"agent:waiting_approval"
|
|
16440
|
+
"agent:waiting_approval",
|
|
16441
|
+
// agent:waiting_choice (a worker's AskUserQuestion) shares the approval class's
|
|
16442
|
+
// real-time-nudge / level-backed contract: its authoritative state is recorded at
|
|
16443
|
+
// LEVEL the moment it is processed (task_question_pending → mesh_status
|
|
16444
|
+
// awaiting_choice), so the pending event is only a NUDGE and can be delivered to a
|
|
16445
|
+
// busy coordinator's inbox (and dropped) without data loss — the level state
|
|
16446
|
+
// re-derives it. It is therefore exempt from the idle-edge hold completions require,
|
|
16447
|
+
// exactly like an approval nudge (mission f1d25e11).
|
|
16448
|
+
"agent:waiting_choice"
|
|
16176
16449
|
]);
|
|
16177
16450
|
}
|
|
16178
16451
|
});
|
|
@@ -17161,6 +17434,7 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
|
|
|
17161
17434
|
}
|
|
17162
17435
|
}
|
|
17163
17436
|
if (!pending.length) return false;
|
|
17437
|
+
const freshnessGate = { maxBehind: resolveAutoFastForwardPolicy(mesh).maxBehind };
|
|
17164
17438
|
const maxParallelTasks = resolveMaxParallelTasks(mesh?.policy?.maxParallelTasks);
|
|
17165
17439
|
const maxReadonlyParallelTasks = resolveMaxReadonlyParallelTasks(maxParallelTasks);
|
|
17166
17440
|
for (const task of pending) {
|
|
@@ -17275,6 +17549,10 @@ async function maybeAutoLaunchOneQueueSession(components, meshId, mesh) {
|
|
|
17275
17549
|
markAutoLaunch(meshId, task.id, { status: "skipped", reason: "node_not_launch_ready", nodeId });
|
|
17276
17550
|
continue;
|
|
17277
17551
|
}
|
|
17552
|
+
if (!isMeshNodeFreshEnoughToLaunch(node, freshnessGate)) {
|
|
17553
|
+
markAutoLaunch(meshId, task.id, { status: "skipped", reason: "node_stale_behind_upstream", nodeId });
|
|
17554
|
+
continue;
|
|
17555
|
+
}
|
|
17278
17556
|
const launchTarget = resolveAutoLaunchTarget(components, node);
|
|
17279
17557
|
if (launchTarget.mode === "skip") {
|
|
17280
17558
|
markAutoLaunch(meshId, task.id, { status: "skipped", reason: launchTarget.reason || "auto_launch_unavailable", nodeId });
|
|
@@ -20976,10 +21254,19 @@ function injectMeshSystemMessage(components, args) {
|
|
|
20976
21254
|
}
|
|
20977
21255
|
function markSessionTerminal(sessionId, outcome, occurredAtMs, opts) {
|
|
20978
21256
|
const eventTaskId = readNonEmptyString(args.metadataEvent.taskId) || void 0;
|
|
20979
|
-
const
|
|
21257
|
+
const completionDiagnostic = args.metadataEvent.completionDiagnostic && typeof args.metadataEvent.completionDiagnostic === "object" ? args.metadataEvent.completionDiagnostic : void 0;
|
|
21258
|
+
const emittedAfterFinalizationTimeout = completionDiagnostic?.emittedAfterFinalizationTimeout === true;
|
|
21259
|
+
const weakCompleted = outcome === "completed" && isWeakCompletionEvidence(args.metadataEvent) && !emittedAfterFinalizationTimeout;
|
|
21260
|
+
const task = weakCompleted ? updateSessionTaskStatus(args.meshId, sessionId, "assigned", {
|
|
21261
|
+
occurredAt: occurredAtMs != null ? new Date(occurredAtMs).toISOString() : void 0,
|
|
21262
|
+
taskId: eventTaskId
|
|
21263
|
+
}) : updateSessionTaskStatus(args.meshId, sessionId, outcome, {
|
|
20980
21264
|
occurredAt: occurredAtMs != null ? new Date(occurredAtMs).toISOString() : void 0,
|
|
20981
21265
|
taskId: eventTaskId
|
|
20982
21266
|
});
|
|
21267
|
+
if (weakCompleted && task) {
|
|
21268
|
+
LOG.info("MeshQueue", `Weak completion (${readNonEmptyString(args.metadataEvent.evidenceLevel) || "missing_final_assistant"}) kept queue task ${task.id} tentative (session ${sessionId}); reconcile owns the genuine terminal`);
|
|
21269
|
+
}
|
|
20983
21270
|
const leaveDirectDispatchActive = !task && opts?.tentativeIfDirect === true || !eventTaskId && !sessionHasActiveAssignment(args.meshId, sessionId);
|
|
20984
21271
|
if (!leaveDirectDispatchActive) {
|
|
20985
21272
|
updateDirectDispatchStatus(args.meshId, sessionId, outcome, eventTaskId);
|
|
@@ -21436,6 +21723,14 @@ function buildRelayMetadataEvent(payload) {
|
|
|
21436
21723
|
retryOfJobId: readNonEmptyString(payload.retryOfJobId),
|
|
21437
21724
|
...relayModalMessage ? { modalMessage: relayModalMessage } : {},
|
|
21438
21725
|
...relayModalButtons && relayModalButtons.length > 0 ? { modalButtons: relayModalButtons } : {},
|
|
21726
|
+
// agent:waiting_choice (mission f1d25e11): carry the FULL structured question
|
|
21727
|
+
// payload across the machine boundary so a REMOTE worker's AskUserQuestion reaches
|
|
21728
|
+
// the coordinator with every question + option intact — the coordinator renders
|
|
21729
|
+
// these and answers with mesh_answer_question. The local in-process forward path
|
|
21730
|
+
// preserves the whole event for free; this mirrors the fields for the remote relay.
|
|
21731
|
+
...payload.interactivePrompt && typeof payload.interactivePrompt === "object" && !Array.isArray(payload.interactivePrompt) ? { interactivePrompt: payload.interactivePrompt } : {},
|
|
21732
|
+
...readNonEmptyString(payload.promptId) ? { promptId: readNonEmptyString(payload.promptId) } : {},
|
|
21733
|
+
...payload.multiSelect === true ? { multiSelect: true } : {},
|
|
21439
21734
|
...payload.result && typeof payload.result === "object" && !Array.isArray(payload.result) ? { result: payload.result } : {},
|
|
21440
21735
|
...payload.completionDiagnostic && typeof payload.completionDiagnostic === "object" && !Array.isArray(payload.completionDiagnostic) ? { completionDiagnostic: payload.completionDiagnostic } : {},
|
|
21441
21736
|
...payload.workerResult && typeof payload.workerResult === "object" && !Array.isArray(payload.workerResult) ? { workerResult: payload.workerResult } : {},
|
|
@@ -22855,7 +23150,8 @@ function drainAndDeliverApprovalNudges(meshId, drainDaemonIds, localDaemonId, me
|
|
|
22855
23150
|
function assignedRowLiveStatusIsAwaitingApproval(mesh, nodeId, sessionId) {
|
|
22856
23151
|
if (!nodeId || !sessionId) return false;
|
|
22857
23152
|
try {
|
|
22858
|
-
|
|
23153
|
+
const liveStatus = sessionStatusFromNodes(mesh.nodes, nodeId, sessionId).status;
|
|
23154
|
+
return liveStatus === "awaiting_approval" || liveStatus === "awaiting_choice";
|
|
22859
23155
|
} catch {
|
|
22860
23156
|
return false;
|
|
22861
23157
|
}
|
|
@@ -25336,8 +25632,15 @@ function buttonBlockApprovalCue(spec, text) {
|
|
|
25336
25632
|
if (pickApprovalButton(labels).index < 0) return false;
|
|
25337
25633
|
return hasNegativeApprovalOption(labels);
|
|
25338
25634
|
}
|
|
25635
|
+
function isAskUserQuestionPickerSignature(text) {
|
|
25636
|
+
if (!text) return false;
|
|
25637
|
+
const hasSelectFooter = /Enter to select/i.test(text) && /Esc to cancel/i.test(text);
|
|
25638
|
+
if (!hasSelectFooter) return false;
|
|
25639
|
+
return /Type something\.?|Chat about this/i.test(text);
|
|
25640
|
+
}
|
|
25339
25641
|
function modalMatches(spec, input) {
|
|
25340
25642
|
const text = input.screenText ?? "";
|
|
25643
|
+
if (isAskUserQuestionPickerSignature(text)) return false;
|
|
25341
25644
|
const question = compile2(spec.questionPattern, spec.questionFlags ?? "i");
|
|
25342
25645
|
if (question.test(text)) return true;
|
|
25343
25646
|
for (const variant of spec.questionVariants ?? []) {
|
|
@@ -25479,6 +25782,12 @@ function compile3(re, flags) {
|
|
|
25479
25782
|
throw new Error(`Invalid regex /${re}/${flags ?? ""}: ${e.message}`);
|
|
25480
25783
|
}
|
|
25481
25784
|
}
|
|
25785
|
+
function isAskUserQuestionPickerSignature2(text) {
|
|
25786
|
+
if (!text) return false;
|
|
25787
|
+
const hasSelectFooter = /Enter to select/i.test(text) && /Esc to cancel/i.test(text);
|
|
25788
|
+
if (!hasSelectFooter) return false;
|
|
25789
|
+
return /Type something\.?|Chat about this/i.test(text);
|
|
25790
|
+
}
|
|
25482
25791
|
function findQuestionLineIndex(spec, lines) {
|
|
25483
25792
|
const primary = compile3(spec.questionPattern, spec.questionFlags ?? "i");
|
|
25484
25793
|
const buttonFlags = spec.buttonFlags && spec.buttonFlags.includes("m") ? spec.buttonFlags : `${spec.buttonFlags ?? ""}m`;
|
|
@@ -25603,6 +25912,7 @@ function buildParseApprovalFromTui(spec, visibleRegion) {
|
|
|
25603
25912
|
return function parseApproval(input) {
|
|
25604
25913
|
const rawText = input.screenText ?? input.buffer ?? "";
|
|
25605
25914
|
if (!rawText) return null;
|
|
25915
|
+
if (isAskUserQuestionPickerSignature2(rawText)) return null;
|
|
25606
25916
|
const text = visibleRegion ? applyVisibleRegion(visibleRegion, rawText) : rawText;
|
|
25607
25917
|
const lines = text.split("\n");
|
|
25608
25918
|
const question = findQuestionLineIndex(spec, lines);
|
|
@@ -30411,6 +30721,57 @@ function normalizeInteractivePromptResponse(raw) {
|
|
|
30411
30721
|
}
|
|
30412
30722
|
return { promptId, answers };
|
|
30413
30723
|
}
|
|
30724
|
+
function resolveInteractivePromptResponse(prompt, raw) {
|
|
30725
|
+
if (!raw || typeof raw !== "object") throw new Error("Interactive prompt response must be an object");
|
|
30726
|
+
const record = raw;
|
|
30727
|
+
const promptId = readString(record.promptId);
|
|
30728
|
+
if (!promptId) throw new Error("promptId must be a non-empty string");
|
|
30729
|
+
if (promptId !== prompt.promptId) throw new Error("Interactive prompt response does not match active prompt");
|
|
30730
|
+
if (record.answers && typeof record.answers === "object" && !Array.isArray(record.answers)) {
|
|
30731
|
+
return normalizeInteractivePromptResponse(record);
|
|
30732
|
+
}
|
|
30733
|
+
if (!Array.isArray(record.answers)) throw new Error("answers must be an array or a questionId-keyed object");
|
|
30734
|
+
const resolveOneLabel = (question, sel) => {
|
|
30735
|
+
if (typeof sel === "number" && Number.isFinite(sel)) {
|
|
30736
|
+
const idx = Math.trunc(sel) - 1;
|
|
30737
|
+
const option = question.options[idx];
|
|
30738
|
+
if (!option) throw new Error(`Option index ${sel} out of range for ${question.questionId}`);
|
|
30739
|
+
return option.label;
|
|
30740
|
+
}
|
|
30741
|
+
const label = readString(sel);
|
|
30742
|
+
if (!label) throw new Error(`Empty selection for ${question.questionId}`);
|
|
30743
|
+
const exact = question.options.find((o) => o.label === label);
|
|
30744
|
+
if (exact) return exact.label;
|
|
30745
|
+
const asIndex = Number(label);
|
|
30746
|
+
if (Number.isInteger(asIndex)) {
|
|
30747
|
+
const option = question.options[asIndex - 1];
|
|
30748
|
+
if (option) return option.label;
|
|
30749
|
+
}
|
|
30750
|
+
throw new Error(`Unknown option for ${question.questionId}: ${label}`);
|
|
30751
|
+
};
|
|
30752
|
+
const answers = {};
|
|
30753
|
+
const entries = record.answers;
|
|
30754
|
+
entries.forEach((entryRaw, index) => {
|
|
30755
|
+
if (!entryRaw || typeof entryRaw !== "object" || Array.isArray(entryRaw)) return;
|
|
30756
|
+
const entry = entryRaw;
|
|
30757
|
+
const questionId = readString(entry.questionId);
|
|
30758
|
+
const question = (questionId ? prompt.questions.find((q) => q.questionId === questionId) : void 0) ?? prompt.questions[index];
|
|
30759
|
+
if (!question) throw new Error(`No matching question for answer entry ${index}`);
|
|
30760
|
+
const freeformText = readString(entry.freeform) ?? readString(entry.freeformText);
|
|
30761
|
+
const selectedLabels = [];
|
|
30762
|
+
const select = entry.select;
|
|
30763
|
+
if (Array.isArray(select)) {
|
|
30764
|
+
for (const sel of select) selectedLabels.push(resolveOneLabel(question, sel));
|
|
30765
|
+
} else if (select !== void 0 && select !== null) {
|
|
30766
|
+
selectedLabels.push(resolveOneLabel(question, select));
|
|
30767
|
+
}
|
|
30768
|
+
answers[question.questionId] = {
|
|
30769
|
+
selectedLabels,
|
|
30770
|
+
...freeformText ? { freeformText } : {}
|
|
30771
|
+
};
|
|
30772
|
+
});
|
|
30773
|
+
return { promptId, answers };
|
|
30774
|
+
}
|
|
30414
30775
|
function buildClaudeInteractiveToolResult(response) {
|
|
30415
30776
|
return JSON.stringify({
|
|
30416
30777
|
type: "user",
|
|
@@ -48803,6 +49164,7 @@ async function waitForCliAdapterReady(adapter, options) {
|
|
|
48803
49164
|
var STATUS_HYDRATION_TAIL_LIMIT = 200;
|
|
48804
49165
|
var COMPLETED_FINALIZATION_RETRY_MS = 1e3;
|
|
48805
49166
|
var COMPLETED_FINALIZATION_MAX_WAIT_MS = 3e4;
|
|
49167
|
+
var CANON_C_MISSING_ASSISTANT_MIN_ELAPSED_MS = 2e4;
|
|
48806
49168
|
var NATIVE_HISTORY_MESH_IDLE_SETTLE_MS = 4e3;
|
|
48807
49169
|
var PTY_PARSED_FINAL_ASSISTANT_QUIET_DWELL_MS = 1200;
|
|
48808
49170
|
var ANTIGRAVITY_HOLD_QUIET_DWELL_MS = 3e3;
|
|
@@ -49223,10 +49585,12 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
49223
49585
|
// `waiting_choice` overlay in getState(); the raw adapter status stays idle/generating,
|
|
49224
49586
|
// so detectStatusTransition()'s status-keyed arms never fire and no push-worthy
|
|
49225
49587
|
// agent:* event is emitted — the owner misses the "ACTION REQUIRED" prompt when the
|
|
49226
|
-
// app is backgrounded. We emit one agent:
|
|
49227
|
-
// state (
|
|
49228
|
-
//
|
|
49229
|
-
//
|
|
49588
|
+
// app is backgrounded. We emit one agent:waiting_choice on ENTRY into the prompt
|
|
49589
|
+
// state (its own coordinator event, NOT the approval channel — a multi-choice
|
|
49590
|
+
// question is answered with mesh_answer_question, never mesh_approve) carrying the
|
|
49591
|
+
// FULL InteractivePrompt payload, and dedupe on this key so repeated status ticks
|
|
49592
|
+
// with the same prompt do not re-fire. '' means no prompt is currently active
|
|
49593
|
+
// (cleared when the prompt is answered/gone).
|
|
49230
49594
|
lastInteractivePromptEventKey = "";
|
|
49231
49595
|
autoApproveBusy = false;
|
|
49232
49596
|
autoApproveBusyTimer = null;
|
|
@@ -49871,7 +50235,7 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
49871
50235
|
}
|
|
49872
50236
|
} else if (event === "interactive_prompt_response" && data) {
|
|
49873
50237
|
try {
|
|
49874
|
-
const response = normalizeInteractivePromptResponse(data);
|
|
50238
|
+
const response = this.activeInteractivePrompt && this.activeInteractivePrompt.promptId === data?.promptId && Array.isArray(data?.answers) ? resolveInteractivePromptResponse(this.activeInteractivePrompt, data) : normalizeInteractivePromptResponse(data);
|
|
49875
50239
|
if (this.activeInteractivePrompt?.promptId === response.promptId) {
|
|
49876
50240
|
this.activeInteractivePrompt = null;
|
|
49877
50241
|
}
|
|
@@ -50410,7 +50774,12 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
50410
50774
|
return {
|
|
50411
50775
|
reason: "missing_final_assistant",
|
|
50412
50776
|
terminal: this.provider.requiresFinalAssistantBeforeIdle === true,
|
|
50413
|
-
allowTimeout: allowMissingAssistantTimeout
|
|
50777
|
+
allowTimeout: allowMissingAssistantTimeout,
|
|
50778
|
+
// PTY-parsed provider (codex-cli): no external transcript trails the idle
|
|
50779
|
+
// transition, so the CANON-C decoupled emit must observe the min-elapsed floor
|
|
50780
|
+
// rather than fire at the first-poll waitedMs. (claude-cli's external-native
|
|
50781
|
+
// branch above is exempt — its transcript legitimately lands moments later.)
|
|
50782
|
+
noExternalTranscriptSource: true
|
|
50414
50783
|
};
|
|
50415
50784
|
}
|
|
50416
50785
|
}
|
|
@@ -50908,6 +51277,24 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
50908
51277
|
this.scheduleCompletedDebounceFlush(COMPLETED_FINALIZATION_RETRY_MS);
|
|
50909
51278
|
return;
|
|
50910
51279
|
}
|
|
51280
|
+
if (isTranscriptEvidenceGate && blockReason === "missing_final_assistant" && block2.noExternalTranscriptSource === true && waitedMs < CANON_C_MISSING_ASSISTANT_MIN_ELAPSED_MS) {
|
|
51281
|
+
if (pending.loggedBlockReason !== "canon_c_min_elapsed_floor") {
|
|
51282
|
+
LOG.info("CLI", `[${this.type}] holding CANON-C decoupled emit until min-elapsed floor (waitedMs=${waitedMs} floor=${CANON_C_MISSING_ASSISTANT_MIN_ELAPSED_MS}); no final assistant yet (${blockReason})`);
|
|
51283
|
+
if (this.isMeshWorkerSession()) {
|
|
51284
|
+
traceMeshEventDrop("completion_gate_hold", this.meshTraceCtx(), `canon_c_min_elapsed_floor waited=${waitedMs}ms`);
|
|
51285
|
+
}
|
|
51286
|
+
if (this.completionTraceOn()) this.recordCompletionGateTrace("hold", {
|
|
51287
|
+
blockReason,
|
|
51288
|
+
latestVisibleStatus,
|
|
51289
|
+
terminal: block2.terminal === true,
|
|
51290
|
+
canonCMinElapsedFloor: true,
|
|
51291
|
+
waitedMs
|
|
51292
|
+
});
|
|
51293
|
+
pending.loggedBlockReason = "canon_c_min_elapsed_floor";
|
|
51294
|
+
}
|
|
51295
|
+
this.scheduleCompletedDebounceFlush(COMPLETED_FINALIZATION_RETRY_MS);
|
|
51296
|
+
return;
|
|
51297
|
+
}
|
|
50911
51298
|
if (this.type === "antigravity-cli" && block2.holdForTranscript === true && waitedMs < ANTIGRAVITY_HOLD_HARD_CAP_MS && this.antigravityHoldPtyStillActive()) {
|
|
50912
51299
|
if (pending.loggedBlockReason !== "antigravity_hold_pty_active") {
|
|
50913
51300
|
LOG.info("CLI", `[${this.type}] 30s cap reached but PTY still generating; holding antigravity completion past cap (waitedMs=${waitedMs} hardCap=${ANTIGRAVITY_HOLD_HARD_CAP_MS}) (${blockReason})`);
|
|
@@ -51607,9 +51994,16 @@ ${buttons.join("\n")}`;
|
|
|
51607
51994
|
const modalMessage = firstQuestion ? firstQuestion.header ? `${firstQuestion.header}: ${firstQuestion.question}` : firstQuestion.question : void 0;
|
|
51608
51995
|
const modalButtons = firstQuestion?.options?.map((option) => option.label);
|
|
51609
51996
|
this.pushEvent({
|
|
51610
|
-
event: "agent:
|
|
51997
|
+
event: "agent:waiting_choice",
|
|
51611
51998
|
chatTitle,
|
|
51612
51999
|
timestamp: now,
|
|
52000
|
+
// Structured, authoritative payload — the coordinator renders these and
|
|
52001
|
+
// answers via mesh_answer_question. Carried whole (all questions, all
|
|
52002
|
+
// options) so multi-question / multi-select prompts round-trip fully.
|
|
52003
|
+
interactivePrompt,
|
|
52004
|
+
promptId: interactivePrompt.promptId,
|
|
52005
|
+
multiSelect: firstQuestion?.multiSelect === true,
|
|
52006
|
+
// Push-notification-friendly projection (server push path reads these).
|
|
51613
52007
|
modalMessage,
|
|
51614
52008
|
modalButtons
|
|
51615
52009
|
});
|
|
@@ -60521,6 +60915,50 @@ var meshHostPairingHandlers = {
|
|
|
60521
60915
|
};
|
|
60522
60916
|
|
|
60523
60917
|
// src/commands/med-family/mesh-queue.ts
|
|
60918
|
+
init_logger();
|
|
60919
|
+
async function stopCancelledTaskWorker(ctx, meshId, prior) {
|
|
60920
|
+
const { sessionId, nodeId, providerType } = prior;
|
|
60921
|
+
const stopArgs = {
|
|
60922
|
+
targetSessionId: sessionId,
|
|
60923
|
+
...providerType ? { cliType: providerType } : {},
|
|
60924
|
+
mode: "hard",
|
|
60925
|
+
reason: "mesh_task_cancelled"
|
|
60926
|
+
};
|
|
60927
|
+
try {
|
|
60928
|
+
const cliManager = ctx.deps.cliManager;
|
|
60929
|
+
const isLocal = cliManager?.adapters?.has?.(sessionId) === true;
|
|
60930
|
+
if (isLocal) {
|
|
60931
|
+
if (!stopArgs.cliType) {
|
|
60932
|
+
const localType = cliManager?.adapters?.get?.(sessionId)?.cliType;
|
|
60933
|
+
if (localType) stopArgs.cliType = localType;
|
|
60934
|
+
}
|
|
60935
|
+
await Promise.resolve(cliManager?.handleCliCommand?.("stop_cli", stopArgs)).catch((e) => LOG.warn("MeshQueue", `Local stop of cancelled worker ${sessionId} failed: ${e?.message || e}`));
|
|
60936
|
+
return;
|
|
60937
|
+
}
|
|
60938
|
+
const dispatch = ctx.deps.dispatchMeshCommand;
|
|
60939
|
+
let daemonId;
|
|
60940
|
+
if (nodeId) {
|
|
60941
|
+
try {
|
|
60942
|
+
const [{ getMesh: getMesh2 }, { meshNodeIdMatches: meshNodeIdMatches2 }, { readMeshNodeDaemonId: readMeshNodeDaemonId2 }] = await Promise.all([
|
|
60943
|
+
Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports)),
|
|
60944
|
+
Promise.resolve().then(() => (init_dist(), dist_exports)),
|
|
60945
|
+
Promise.resolve().then(() => (init_mesh_node_identity(), mesh_node_identity_exports))
|
|
60946
|
+
]);
|
|
60947
|
+
const mesh = getMesh2(meshId);
|
|
60948
|
+
const node = mesh?.nodes?.find((n) => meshNodeIdMatches2(n, nodeId));
|
|
60949
|
+
daemonId = node ? readMeshNodeDaemonId2(node) || void 0 : void 0;
|
|
60950
|
+
} catch {
|
|
60951
|
+
}
|
|
60952
|
+
}
|
|
60953
|
+
if (daemonId && dispatch) {
|
|
60954
|
+
await Promise.resolve(dispatch(daemonId, "stop_cli", stopArgs)).catch((e) => LOG.warn("MeshQueue", `Remote stop of cancelled worker ${sessionId} on daemon ${daemonId} failed: ${e?.message || e}`));
|
|
60955
|
+
} else {
|
|
60956
|
+
LOG.warn("MeshQueue", `Cannot stop cancelled worker ${sessionId}: no local adapter and no resolvable remote daemon id (node ${nodeId ?? "?"}). Row is already terminal; if the worker completes it strand-fails harmlessly.`);
|
|
60957
|
+
}
|
|
60958
|
+
} catch (e) {
|
|
60959
|
+
LOG.warn("MeshQueue", `stopCancelledTaskWorker error for ${sessionId}: ${e?.message || e}`);
|
|
60960
|
+
}
|
|
60961
|
+
}
|
|
60524
60962
|
var meshQueueHandlers = {
|
|
60525
60963
|
get_mesh_queue: async (_ctx, args) => {
|
|
60526
60964
|
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
@@ -60554,10 +60992,14 @@ var meshQueueHandlers = {
|
|
|
60554
60992
|
const ownerFailure = await ctx.requireMeshHostMutationOwner(meshId, args?.inlineMesh, "queue cancellation");
|
|
60555
60993
|
if (ownerFailure) return ownerFailure;
|
|
60556
60994
|
try {
|
|
60557
|
-
const { cancelTask: cancelTask2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
|
|
60995
|
+
const { cancelTask: cancelTask2, takeCancelledTaskAssignment: takeCancelledTaskAssignment2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
|
|
60558
60996
|
const reason = typeof args?.reason === "string" ? args.reason : void 0;
|
|
60559
60997
|
const task = cancelTask2(meshId, taskId, { reason });
|
|
60560
60998
|
if (!task) return { success: false, error: `Queue task '${taskId}' not found` };
|
|
60999
|
+
const prior = takeCancelledTaskAssignment2(meshId, taskId);
|
|
61000
|
+
if (prior?.sessionId) {
|
|
61001
|
+
void stopCancelledTaskWorker(ctx, meshId, prior);
|
|
61002
|
+
}
|
|
60561
61003
|
return { success: true, task };
|
|
60562
61004
|
} catch (e) {
|
|
60563
61005
|
return { success: false, error: e.message };
|
|
@@ -61036,10 +61478,12 @@ var meshEventsHandlers = {
|
|
|
61036
61478
|
interactive_prompt_response: async (ctx, args) => {
|
|
61037
61479
|
const sessionId = typeof args?.targetSessionId === "string" && args.targetSessionId.trim() ? args.targetSessionId.trim() : typeof args?.sessionId === "string" && args.sessionId.trim() ? args.sessionId.trim() : "";
|
|
61038
61480
|
if (!sessionId) return { success: false, error: "targetSessionId required" };
|
|
61039
|
-
const
|
|
61481
|
+
const rawResponse = args?.response ?? args;
|
|
61040
61482
|
const instance = ctx.deps.instanceManager.getInstance(sessionId);
|
|
61041
61483
|
if (!instance) return { success: false, error: `No running instance for session ${sessionId}` };
|
|
61042
|
-
|
|
61484
|
+
const isFriendlyArrayForm = rawResponse && typeof rawResponse === "object" && Array.isArray(rawResponse.answers);
|
|
61485
|
+
const payload = isFriendlyArrayForm ? rawResponse : normalizeInteractivePromptResponse(rawResponse);
|
|
61486
|
+
ctx.deps.instanceManager.sendEvent(sessionId, "interactive_prompt_response", payload);
|
|
61043
61487
|
return { success: true };
|
|
61044
61488
|
}
|
|
61045
61489
|
};
|
|
@@ -63104,6 +63548,171 @@ function collectFastForwardGitlinkPaths(repoRoot, baseHead, branchHead) {
|
|
|
63104
63548
|
return isSubmoduleFastForward((0, import_path15.resolve)(repoRoot, path46), baseCommit, branchCommit);
|
|
63105
63549
|
});
|
|
63106
63550
|
}
|
|
63551
|
+
function isSubmoduleDivergedSibling(submoduleRepoPath, baseCommit, branchCommit) {
|
|
63552
|
+
if (!baseCommit || !branchCommit || baseCommit === branchCommit) return false;
|
|
63553
|
+
try {
|
|
63554
|
+
if (!fs34.existsSync(submoduleRepoPath)) return false;
|
|
63555
|
+
(0, import_node_child_process6.execFileSync)(GIT2, ["cat-file", "-e", `${baseCommit}^{commit}`], { cwd: submoduleRepoPath, stdio: "ignore" });
|
|
63556
|
+
(0, import_node_child_process6.execFileSync)(GIT2, ["cat-file", "-e", `${branchCommit}^{commit}`], { cwd: submoduleRepoPath, stdio: "ignore" });
|
|
63557
|
+
} catch {
|
|
63558
|
+
return false;
|
|
63559
|
+
}
|
|
63560
|
+
try {
|
|
63561
|
+
(0, import_node_child_process6.execFileSync)(GIT2, ["merge-base", "--is-ancestor", baseCommit, branchCommit], { cwd: submoduleRepoPath, stdio: "ignore" });
|
|
63562
|
+
return false;
|
|
63563
|
+
} catch {
|
|
63564
|
+
}
|
|
63565
|
+
try {
|
|
63566
|
+
(0, import_node_child_process6.execFileSync)(GIT2, ["merge-base", "--is-ancestor", branchCommit, baseCommit], { cwd: submoduleRepoPath, stdio: "ignore" });
|
|
63567
|
+
return false;
|
|
63568
|
+
} catch {
|
|
63569
|
+
}
|
|
63570
|
+
try {
|
|
63571
|
+
const mb = (0, import_node_child_process6.execFileSync)(GIT2, ["merge-base", baseCommit, branchCommit], { cwd: submoduleRepoPath, encoding: "utf8" }).trim();
|
|
63572
|
+
return !!mb;
|
|
63573
|
+
} catch {
|
|
63574
|
+
return false;
|
|
63575
|
+
}
|
|
63576
|
+
}
|
|
63577
|
+
function ensureSubmoduleCommitLocal(submoduleRepoPath, baseSubmoduleRepoPath, commit) {
|
|
63578
|
+
if (!commit) return;
|
|
63579
|
+
try {
|
|
63580
|
+
(0, import_node_child_process6.execFileSync)(GIT2, ["cat-file", "-e", `${commit}^{commit}`], { cwd: submoduleRepoPath, stdio: "ignore" });
|
|
63581
|
+
return;
|
|
63582
|
+
} catch {
|
|
63583
|
+
}
|
|
63584
|
+
try {
|
|
63585
|
+
if (!fs34.existsSync(submoduleRepoPath) || !fs34.existsSync(baseSubmoduleRepoPath)) return;
|
|
63586
|
+
(0, import_node_child_process6.execFileSync)(GIT2, ["-c", "protocol.file.allow=always", "fetch", "-q", baseSubmoduleRepoPath, "+refs/heads/*:refs/adhdev-refine-base/*"], {
|
|
63587
|
+
cwd: submoduleRepoPath,
|
|
63588
|
+
stdio: ["ignore", "ignore", "pipe"]
|
|
63589
|
+
});
|
|
63590
|
+
} catch {
|
|
63591
|
+
}
|
|
63592
|
+
}
|
|
63593
|
+
function convergeDivergedSubmoduleGitlinks(worktreeRoot, baseRepoRoot, baseHead, branchHead) {
|
|
63594
|
+
const changed = readChangedGitlinkPaths(worktreeRoot, baseHead, branchHead);
|
|
63595
|
+
if (changed.length === 0) {
|
|
63596
|
+
return { converged: false, reason: "no_changed_gitlinks", resolutions: [], gitlinks: [] };
|
|
63597
|
+
}
|
|
63598
|
+
const gitlinks = [];
|
|
63599
|
+
const resolutions = [];
|
|
63600
|
+
let sawDiverged = false;
|
|
63601
|
+
for (const path46 of changed) {
|
|
63602
|
+
const baseCommit = readTreeObject(baseRepoRoot, baseHead, path46);
|
|
63603
|
+
const branchCommit = readTreeObject(worktreeRoot, branchHead, path46);
|
|
63604
|
+
const submoduleRepoPath = (0, import_path15.resolve)(worktreeRoot, path46);
|
|
63605
|
+
if (baseCommit) {
|
|
63606
|
+
ensureSubmoduleCommitLocal(submoduleRepoPath, (0, import_path15.resolve)(baseRepoRoot, path46), baseCommit);
|
|
63607
|
+
}
|
|
63608
|
+
if (!baseCommit || !branchCommit || !isSubmoduleDivergedSibling(submoduleRepoPath, baseCommit, branchCommit)) {
|
|
63609
|
+
gitlinks.push({ path: path46, baseCommit, branchCommit, action: "skipped_not_diverged" });
|
|
63610
|
+
continue;
|
|
63611
|
+
}
|
|
63612
|
+
sawDiverged = true;
|
|
63613
|
+
let rebasedCommit;
|
|
63614
|
+
try {
|
|
63615
|
+
(0, import_node_child_process6.execFileSync)(GIT2, ["checkout", "-q", "--detach", branchCommit], { cwd: submoduleRepoPath, stdio: ["ignore", "ignore", "pipe"] });
|
|
63616
|
+
(0, import_node_child_process6.execFileSync)(GIT2, ["rebase", baseCommit], { cwd: submoduleRepoPath, stdio: ["ignore", "pipe", "pipe"] });
|
|
63617
|
+
rebasedCommit = (0, import_node_child_process6.execFileSync)(GIT2, ["rev-parse", "HEAD"], { cwd: submoduleRepoPath, encoding: "utf8" }).trim();
|
|
63618
|
+
} catch {
|
|
63619
|
+
try {
|
|
63620
|
+
(0, import_node_child_process6.execFileSync)(GIT2, ["rebase", "--abort"], { cwd: submoduleRepoPath, stdio: "ignore" });
|
|
63621
|
+
} catch {
|
|
63622
|
+
}
|
|
63623
|
+
try {
|
|
63624
|
+
(0, import_node_child_process6.execFileSync)(GIT2, ["checkout", "-q", "--detach", branchCommit], { cwd: submoduleRepoPath, stdio: "ignore" });
|
|
63625
|
+
} catch {
|
|
63626
|
+
}
|
|
63627
|
+
gitlinks.push({ path: path46, baseCommit, branchCommit, action: "rebase_conflict" });
|
|
63628
|
+
return { converged: false, reason: "rebase_conflict", resolutions: [], gitlinks };
|
|
63629
|
+
}
|
|
63630
|
+
gitlinks.push({ path: path46, baseCommit, branchCommit, rebasedCommit, action: "rebased" });
|
|
63631
|
+
resolutions.push({ path: path46, baseCommit, branchCommit, rebasedCommit });
|
|
63632
|
+
}
|
|
63633
|
+
if (!sawDiverged) {
|
|
63634
|
+
return { converged: false, reason: "not_diverged", resolutions: [], gitlinks };
|
|
63635
|
+
}
|
|
63636
|
+
return { converged: resolutions.length > 0, resolutions, gitlinks };
|
|
63637
|
+
}
|
|
63638
|
+
function rootRebaseResolvingGitlinks(worktreeRoot, baseHead, resolutions) {
|
|
63639
|
+
const resolveByPath = new Map(resolutions.map((r) => [r.path, r.rebasedCommit]));
|
|
63640
|
+
const runRebase = (args) => {
|
|
63641
|
+
try {
|
|
63642
|
+
(0, import_node_child_process6.execFileSync)(GIT2, args, {
|
|
63643
|
+
cwd: worktreeRoot,
|
|
63644
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
63645
|
+
// A rebase editor prompt would hang; keep it non-interactive.
|
|
63646
|
+
env: { ...process.env, GIT_EDITOR: "true", GIT_SEQUENCE_EDITOR: "true" }
|
|
63647
|
+
});
|
|
63648
|
+
return { ok: true };
|
|
63649
|
+
} catch {
|
|
63650
|
+
return { ok: false };
|
|
63651
|
+
}
|
|
63652
|
+
};
|
|
63653
|
+
const unmergedPaths = () => {
|
|
63654
|
+
try {
|
|
63655
|
+
return (0, import_node_child_process6.execFileSync)(GIT2, ["diff", "--name-only", "--diff-filter=U"], { cwd: worktreeRoot, encoding: "utf8" }).split("\n").map((s2) => s2.trim()).filter(Boolean);
|
|
63656
|
+
} catch {
|
|
63657
|
+
return [];
|
|
63658
|
+
}
|
|
63659
|
+
};
|
|
63660
|
+
const abort = (reason, conflictPaths) => {
|
|
63661
|
+
try {
|
|
63662
|
+
(0, import_node_child_process6.execFileSync)(GIT2, ["rebase", "--abort"], { cwd: worktreeRoot, stdio: "ignore" });
|
|
63663
|
+
} catch {
|
|
63664
|
+
}
|
|
63665
|
+
return { ok: false, reason, conflictPaths };
|
|
63666
|
+
};
|
|
63667
|
+
let progress = runRebase(["rebase", baseHead]);
|
|
63668
|
+
let guard = 0;
|
|
63669
|
+
while (!progress.ok) {
|
|
63670
|
+
if (guard++ > 100) return abort("rebase_error");
|
|
63671
|
+
const conflicts = unmergedPaths();
|
|
63672
|
+
if (conflicts.length === 0) {
|
|
63673
|
+
return abort("rebase_error");
|
|
63674
|
+
}
|
|
63675
|
+
const unresolvable = conflicts.filter((p) => !resolveByPath.has(p));
|
|
63676
|
+
if (unresolvable.length > 0) {
|
|
63677
|
+
const unresolvableGitlink = unresolvable.some((p) => {
|
|
63678
|
+
try {
|
|
63679
|
+
const staged = (0, import_node_child_process6.execFileSync)(GIT2, ["ls-files", "--stage", "--", p], { cwd: worktreeRoot, encoding: "utf8" });
|
|
63680
|
+
return /^160000\s/m.test(staged);
|
|
63681
|
+
} catch {
|
|
63682
|
+
return false;
|
|
63683
|
+
}
|
|
63684
|
+
});
|
|
63685
|
+
const allGitlink = unresolvable.every((p) => {
|
|
63686
|
+
try {
|
|
63687
|
+
const staged = (0, import_node_child_process6.execFileSync)(GIT2, ["ls-files", "--stage", "--", p], { cwd: worktreeRoot, encoding: "utf8" });
|
|
63688
|
+
return /^160000\s/m.test(staged);
|
|
63689
|
+
} catch {
|
|
63690
|
+
return false;
|
|
63691
|
+
}
|
|
63692
|
+
});
|
|
63693
|
+
return abort(allGitlink && unresolvableGitlink ? "unexpected_gitlink" : "non_gitlink_conflict", conflicts);
|
|
63694
|
+
}
|
|
63695
|
+
for (const p of conflicts) {
|
|
63696
|
+
const commit = resolveByPath.get(p);
|
|
63697
|
+
try {
|
|
63698
|
+
(0, import_node_child_process6.execFileSync)(GIT2, ["checkout", "-q", "--detach", commit], { cwd: (0, import_path15.resolve)(worktreeRoot, p), stdio: "ignore" });
|
|
63699
|
+
} catch {
|
|
63700
|
+
}
|
|
63701
|
+
try {
|
|
63702
|
+
(0, import_node_child_process6.execFileSync)(GIT2, ["add", p], { cwd: worktreeRoot, stdio: "ignore" });
|
|
63703
|
+
} catch {
|
|
63704
|
+
return abort("rebase_error", conflicts);
|
|
63705
|
+
}
|
|
63706
|
+
}
|
|
63707
|
+
progress = runRebase(["rebase", "--continue"]);
|
|
63708
|
+
}
|
|
63709
|
+
let branchHead;
|
|
63710
|
+
try {
|
|
63711
|
+
branchHead = (0, import_node_child_process6.execFileSync)(GIT2, ["rev-parse", "HEAD"], { cwd: worktreeRoot, encoding: "utf8" }).trim();
|
|
63712
|
+
} catch {
|
|
63713
|
+
}
|
|
63714
|
+
return { ok: true, branchHead };
|
|
63715
|
+
}
|
|
63107
63716
|
function evaluateGitlinkTrivialFastForward(repoRoot, baseHead, branchHead) {
|
|
63108
63717
|
const changedGitlinks = readChangedGitlinkPaths(repoRoot, baseHead, branchHead).map((path46) => {
|
|
63109
63718
|
const baseCommit = readTreeObject(repoRoot, baseHead, path46);
|
|
@@ -64135,6 +64744,7 @@ async function computeBranchBaseDivergence(execFileAsync4, cwd, baseHead, branch
|
|
|
64135
64744
|
async function refineSyncBaseStage(self, ctx) {
|
|
64136
64745
|
const { repoRoot, baseHead, node, branch, baseBranch, refineStages, execFileAsync: execFileAsync4 } = ctx;
|
|
64137
64746
|
let branchHead = ctx.branchHead;
|
|
64747
|
+
let gitlinkResolutions = [];
|
|
64138
64748
|
const syncStarted = Date.now();
|
|
64139
64749
|
const divergence = await computeBranchBaseDivergence(execFileAsync4, node.workspace, baseHead, branchHead);
|
|
64140
64750
|
if (divergence.behind === 0) {
|
|
@@ -64150,24 +64760,61 @@ async function refineSyncBaseStage(self, ctx) {
|
|
|
64150
64760
|
const preRebasePe = await runMeshRefinePatchEquivalenceGate(repoRoot, baseHead, branchHead);
|
|
64151
64761
|
const alreadyMerged = !preRebasePe.actualPatchId && !!preRebasePe.expectedPatchId;
|
|
64152
64762
|
const submoduleConflict = preRebasePe.actionableHint?.kind === "submodule_conflict";
|
|
64153
|
-
if (alreadyMerged
|
|
64763
|
+
if (alreadyMerged) {
|
|
64154
64764
|
recordMeshRefineStage(refineStages, "sync_base", "passed", syncStarted, {
|
|
64155
64765
|
ahead: divergence.ahead,
|
|
64156
64766
|
behind: divergence.behind,
|
|
64157
64767
|
rebased: false,
|
|
64158
|
-
reason:
|
|
64768
|
+
reason: "already_merged_via_other_path_skip_rebase"
|
|
64159
64769
|
});
|
|
64160
64770
|
return { kind: "continue", ctx };
|
|
64161
64771
|
}
|
|
64772
|
+
if (submoduleConflict) {
|
|
64773
|
+
const converge = convergeDivergedSubmoduleGitlinks(node.workspace, repoRoot, baseHead, branchHead);
|
|
64774
|
+
if (converge.converged) {
|
|
64775
|
+
gitlinkResolutions = converge.resolutions;
|
|
64776
|
+
recordMeshRefineStage(refineStages, "submodule_gitlink_converge", "passed", syncStarted, {
|
|
64777
|
+
reason: "submodule_diverged_auto_rebased",
|
|
64778
|
+
gitlinks: converge.gitlinks
|
|
64779
|
+
});
|
|
64780
|
+
LOG.info("Mesh", `[Refinery] Auto-converged diverged submodule gitlink(s) onto base for node ${node.id}: ` + converge.resolutions.map((r) => `${r.path}\u2192${r.rebasedCommit.slice(0, 12)}`).join(", "));
|
|
64781
|
+
} else {
|
|
64782
|
+
recordMeshRefineStage(refineStages, "submodule_gitlink_converge", "skipped", syncStarted, {
|
|
64783
|
+
reason: "submodule_conflict_defer_to_patch_equivalence",
|
|
64784
|
+
convergeReason: converge.reason,
|
|
64785
|
+
gitlinks: converge.gitlinks
|
|
64786
|
+
});
|
|
64787
|
+
recordMeshRefineStage(refineStages, "sync_base", "passed", syncStarted, {
|
|
64788
|
+
ahead: divergence.ahead,
|
|
64789
|
+
behind: divergence.behind,
|
|
64790
|
+
rebased: false,
|
|
64791
|
+
reason: "submodule_conflict_defer_to_patch_equivalence"
|
|
64792
|
+
});
|
|
64793
|
+
return { kind: "continue", ctx };
|
|
64794
|
+
}
|
|
64795
|
+
}
|
|
64162
64796
|
} catch {
|
|
64163
64797
|
}
|
|
64164
64798
|
const rebaseStarted = Date.now();
|
|
64165
64799
|
try {
|
|
64166
|
-
(
|
|
64800
|
+
if (gitlinkResolutions.length > 0) {
|
|
64801
|
+
const gitlinkRebase = rootRebaseResolvingGitlinks(node.workspace, baseHead, gitlinkResolutions);
|
|
64802
|
+
if (!gitlinkRebase.ok) {
|
|
64803
|
+
const err = new Error(`gitlink-aware rebase aborted: ${gitlinkRebase.reason || "unknown"}`);
|
|
64804
|
+
err.gitlinkRebaseReason = gitlinkRebase.reason;
|
|
64805
|
+
err.gitlinkRebaseConflicts = gitlinkRebase.conflictPaths;
|
|
64806
|
+
err.alreadyAborted = true;
|
|
64807
|
+
throw err;
|
|
64808
|
+
}
|
|
64809
|
+
} else {
|
|
64810
|
+
(0, import_node_child_process7.execFileSync)("git", ["rebase", baseHead], { cwd: node.workspace, stdio: ["ignore", "pipe", "pipe"] });
|
|
64811
|
+
}
|
|
64167
64812
|
} catch (rebaseErr) {
|
|
64168
|
-
|
|
64169
|
-
|
|
64170
|
-
|
|
64813
|
+
if (!rebaseErr?.alreadyAborted) {
|
|
64814
|
+
try {
|
|
64815
|
+
(0, import_node_child_process7.execFileSync)("git", ["rebase", "--abort"], { cwd: node.workspace, stdio: "ignore" });
|
|
64816
|
+
} catch {
|
|
64817
|
+
}
|
|
64171
64818
|
}
|
|
64172
64819
|
let submoduleHintPatchEquivalence;
|
|
64173
64820
|
try {
|
|
@@ -66486,7 +67133,15 @@ var MESH_FORWARDABLE_SESSION_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
66486
67133
|
// returns 'Session not found' (same class as mission 6938892f). Unlike read_terminal this
|
|
66487
67134
|
// MUTATES the worker PTY, so forwarding to the real owner (not a wrong local session) is
|
|
66488
67135
|
// doubly important. The daemon re-enforces the destructive-key confirm gate after the forward.
|
|
66489
|
-
"send_keys"
|
|
67136
|
+
"send_keys",
|
|
67137
|
+
// interactive_prompt_response (mesh_answer_question, mission f1d25e11): the coordinator
|
|
67138
|
+
// answers a REMOTE worker's AskUserQuestion (waiting_choice). The answer must reach the
|
|
67139
|
+
// OWNING worker session's live instance — its activeInteractivePrompt (the authoritative
|
|
67140
|
+
// prompt the labels/indexes resolve against) and its adapter.setInteractivePromptResponse
|
|
67141
|
+
// live only there. Without forwarding, the coordinator's local high-family handler returns
|
|
67142
|
+
// 'No running instance for session …' and the question is never answered — the exact
|
|
67143
|
+
// remote-worker forwarding gap of mission 6938892f, now closed for questions too.
|
|
67144
|
+
"interactive_prompt_response"
|
|
66490
67145
|
]);
|
|
66491
67146
|
function normalizeCommandSource(source) {
|
|
66492
67147
|
switch (source) {
|