@adhdev/daemon-standalone 0.9.82-rc.310 → 0.9.82-rc.311
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 +520 -53
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/public/assets/index-Cw4o3csc.css +1 -0
- package/public/assets/index-DiyV4rNX.js +113 -0
- package/public/index.html +2 -2
package/dist/index.js
CHANGED
|
@@ -29735,6 +29735,18 @@ var require_dist3 = __commonJS({
|
|
|
29735
29735
|
mod
|
|
29736
29736
|
));
|
|
29737
29737
|
var __toCommonJS2 = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
|
|
29738
|
+
function normalizeMeshSchedulingStrategy(value) {
|
|
29739
|
+
if (typeof value !== "string") return DEFAULT_MESH_SCHEDULING_STRATEGY;
|
|
29740
|
+
const trimmed = value.trim();
|
|
29741
|
+
return MESH_SCHEDULING_STRATEGIES.includes(trimmed) ? trimmed : DEFAULT_MESH_SCHEDULING_STRATEGY;
|
|
29742
|
+
}
|
|
29743
|
+
function resolveNodeSchedulingPriority(nodePolicy) {
|
|
29744
|
+
const raw = Number(nodePolicy?.schedulingPriority);
|
|
29745
|
+
return Number.isFinite(raw) ? raw : 0;
|
|
29746
|
+
}
|
|
29747
|
+
function resolveAutoConvergeCodeChange(policy) {
|
|
29748
|
+
return policy?.autoConvergeCodeChange === true;
|
|
29749
|
+
}
|
|
29738
29750
|
function resolveDelegatedWorkerAutoApprove(meshPolicy, nodePolicy) {
|
|
29739
29751
|
if (typeof nodePolicy?.delegatedWorkerAutoApprove === "boolean") {
|
|
29740
29752
|
return nodePolicy.delegatedWorkerAutoApprove;
|
|
@@ -29762,10 +29774,23 @@ var require_dist3 = __commonJS({
|
|
|
29762
29774
|
if (!Number.isFinite(raw) || raw < 0) return void 0;
|
|
29763
29775
|
return Math.floor(raw);
|
|
29764
29776
|
}
|
|
29777
|
+
var MESH_SCHEDULING_STRATEGIES;
|
|
29778
|
+
var DEFAULT_MESH_SCHEDULING_STRATEGY;
|
|
29779
|
+
var MESH_CONVERGE_REFINE_TAG;
|
|
29780
|
+
var MESH_CONVERGE_FAST_FORWARD_TAG;
|
|
29765
29781
|
var DEFAULT_MESH_POLICY;
|
|
29766
29782
|
var init_repo_mesh_types = __esm2({
|
|
29767
29783
|
"src/repo-mesh-types.ts"() {
|
|
29768
29784
|
"use strict";
|
|
29785
|
+
MESH_SCHEDULING_STRATEGIES = [
|
|
29786
|
+
"first_eligible",
|
|
29787
|
+
"least_loaded",
|
|
29788
|
+
"round_robin",
|
|
29789
|
+
"priority_only"
|
|
29790
|
+
];
|
|
29791
|
+
DEFAULT_MESH_SCHEDULING_STRATEGY = "first_eligible";
|
|
29792
|
+
MESH_CONVERGE_REFINE_TAG = "converge=refine";
|
|
29793
|
+
MESH_CONVERGE_FAST_FORWARD_TAG = "converge=fast_forward";
|
|
29769
29794
|
DEFAULT_MESH_POLICY = {
|
|
29770
29795
|
requirePreTaskCheckpoint: false,
|
|
29771
29796
|
requirePostTaskCheckpoint: true,
|
|
@@ -30004,10 +30029,10 @@ var require_dist3 = __commonJS({
|
|
|
30004
30029
|
}
|
|
30005
30030
|
function getDaemonBuildInfo() {
|
|
30006
30031
|
if (cached2) return cached2;
|
|
30007
|
-
const commit = readInjected(true ? "
|
|
30008
|
-
const commitShort = readInjected(true ? "
|
|
30009
|
-
const version2 = readInjected(true ? "0.9.82-rc.
|
|
30010
|
-
const builtAt = readInjected(true ? "2026-06-
|
|
30032
|
+
const commit = readInjected(true ? "3902f3d63eabe7b9e34d0e849dc0fed2fd66c026" : void 0) ?? "unknown";
|
|
30033
|
+
const commitShort = readInjected(true ? "3902f3d6" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
30034
|
+
const version2 = readInjected(true ? "0.9.82-rc.311" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
30035
|
+
const builtAt = readInjected(true ? "2026-06-17T12:22:27.984Z" : void 0);
|
|
30011
30036
|
cached2 = builtAt ? { commit, commitShort, version: version2, builtAt } : { commit, commitShort, version: version2 };
|
|
30012
30037
|
return cached2;
|
|
30013
30038
|
}
|
|
@@ -31261,6 +31286,17 @@ ${error48.message || ""}`;
|
|
|
31261
31286
|
if (!SPAWNED_SESSION_VISIBILITY_MODES.has(String(policy.spawnedSessionVisibility))) {
|
|
31262
31287
|
policy.spawnedSessionVisibility = "visible";
|
|
31263
31288
|
}
|
|
31289
|
+
const normalizedStrategy = normalizeMeshSchedulingStrategy(policy.schedulingStrategy);
|
|
31290
|
+
if (normalizedStrategy === "first_eligible") {
|
|
31291
|
+
delete policy.schedulingStrategy;
|
|
31292
|
+
} else {
|
|
31293
|
+
policy.schedulingStrategy = normalizedStrategy;
|
|
31294
|
+
}
|
|
31295
|
+
if (policy.autoConvergeCodeChange === true) {
|
|
31296
|
+
policy.autoConvergeCodeChange = true;
|
|
31297
|
+
} else {
|
|
31298
|
+
delete policy.autoConvergeCodeChange;
|
|
31299
|
+
}
|
|
31264
31300
|
return policy;
|
|
31265
31301
|
}
|
|
31266
31302
|
function normalizeAutoFastForwardPolicy(value) {
|
|
@@ -32573,6 +32609,7 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
|
|
|
32573
32609
|
recordMeshToolCall: () => recordMeshToolCall,
|
|
32574
32610
|
recordTaskAutoLaunch: () => recordTaskAutoLaunch,
|
|
32575
32611
|
requeueTask: () => requeueTask,
|
|
32612
|
+
resolveConvergeRequiredTags: () => resolveConvergeRequiredTags,
|
|
32576
32613
|
updateDirectDispatchStatus: () => updateDirectDispatchStatus,
|
|
32577
32614
|
updateSessionTaskStatus: () => updateSessionTaskStatus,
|
|
32578
32615
|
updateTaskStatus: () => updateTaskStatus,
|
|
@@ -32646,6 +32683,21 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
|
|
|
32646
32683
|
if (!Array.isArray(raw)) return void 0;
|
|
32647
32684
|
return raw.find((type) => typeof type === "string" && type.trim())?.trim();
|
|
32648
32685
|
}
|
|
32686
|
+
function roleCapabilityTags(policy, providerType) {
|
|
32687
|
+
const roles = policy && typeof policy === "object" && !Array.isArray(policy) ? policy.providerRoles : void 0;
|
|
32688
|
+
if (!Array.isArray(roles)) return [];
|
|
32689
|
+
const wantedProvider = typeof providerType === "string" && providerType.trim() ? providerType.trim().toLowerCase() : "";
|
|
32690
|
+
const out = [];
|
|
32691
|
+
for (const entry of roles) {
|
|
32692
|
+
if (!entry || typeof entry !== "object") continue;
|
|
32693
|
+
const type = typeof entry.providerType === "string" ? entry.providerType.trim().toLowerCase() : "";
|
|
32694
|
+
const role = typeof entry.role === "string" ? entry.role.trim().toLowerCase() : "";
|
|
32695
|
+
if (!role) continue;
|
|
32696
|
+
if (wantedProvider && type && type !== wantedProvider) continue;
|
|
32697
|
+
out.push(`role=${role}`);
|
|
32698
|
+
}
|
|
32699
|
+
return out;
|
|
32700
|
+
}
|
|
32649
32701
|
function buildMeshNodeCapabilityTags(node, providerType) {
|
|
32650
32702
|
const provider = typeof providerType === "string" && providerType.trim() ? providerType.trim() : firstProviderPriority(node?.policy);
|
|
32651
32703
|
const worktreeBranch = typeof node?.worktreeBranch === "string" && node.worktreeBranch.trim() ? node.worktreeBranch.trim() : null;
|
|
@@ -32657,7 +32709,25 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
|
|
|
32657
32709
|
// Worktree nodes automatically expose a "worktree=<branch>" tag so that
|
|
32658
32710
|
// mesh_enqueue_task with required_tags: ["worktree=<branch>"] routes
|
|
32659
32711
|
// only to the matching worktree node.
|
|
32660
|
-
...node?.isLocalWorktree === true && worktreeBranch ? [`worktree=${worktreeBranch}`] : []
|
|
32712
|
+
...node?.isLocalWorktree === true && worktreeBranch ? [`worktree=${worktreeBranch}`] : [],
|
|
32713
|
+
// Convergence routing: advertise how this node can land its work onto base.
|
|
32714
|
+
// - converge=refine: local worktree nodes (on ANY machine — refine_mesh_node
|
|
32715
|
+
// now forwards to the owning daemon) can run the Refinery merge → push →
|
|
32716
|
+
// cleanup against their own checkout, so they accept code_change tasks.
|
|
32717
|
+
// - converge=fast_forward: non-worktree nodes (the machine itself) can only
|
|
32718
|
+
// ff/push an already-converged branch; they are NOT a destination for
|
|
32719
|
+
// code_change work (a worktree is created first, and that worktree node
|
|
32720
|
+
// receives the task instead). Reuses the ordinary required-tags filter —
|
|
32721
|
+
// the load-balancing scheduler auto-injects converge=refine for code_change
|
|
32722
|
+
// so such work is hard-filtered onto refine-capable nodes.
|
|
32723
|
+
...node?.isLocalWorktree === true ? ["converge=refine"] : ["converge=fast_forward"],
|
|
32724
|
+
// Role-based routing: advertise role=<x> for each (node, provider) role
|
|
32725
|
+
// declared in policy.providerRoles. Narrowed to the selected provider when
|
|
32726
|
+
// one is given so the chosen provider must match a task's required role;
|
|
32727
|
+
// when no provider is selected, all declared roles are advertised for the
|
|
32728
|
+
// node-level eligibility scan. Reuses the ordinary required-tags filter —
|
|
32729
|
+
// no separate role field/gate.
|
|
32730
|
+
...roleCapabilityTags(node?.policy, providerType)
|
|
32661
32731
|
]);
|
|
32662
32732
|
}
|
|
32663
32733
|
function nodeSatisfiesRequiredTags(requiredTags, capabilityTags) {
|
|
@@ -32666,6 +32736,18 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
|
|
|
32666
32736
|
const available = new Set(normalizeMeshCapabilityTags(capabilityTags));
|
|
32667
32737
|
return required2.every((tag) => available.has(tag));
|
|
32668
32738
|
}
|
|
32739
|
+
function resolveConvergeRequiredTags(meshId, taskMode, explicitRequiredTags, opts) {
|
|
32740
|
+
if (taskMode !== "code_change") return explicitRequiredTags;
|
|
32741
|
+
if (typeof opts?.targetNodeId === "string" && opts.targetNodeId.trim()) return explicitRequiredTags;
|
|
32742
|
+
let optedIn = false;
|
|
32743
|
+
try {
|
|
32744
|
+
optedIn = resolveAutoConvergeCodeChange(getMesh(meshId)?.policy);
|
|
32745
|
+
} catch {
|
|
32746
|
+
optedIn = false;
|
|
32747
|
+
}
|
|
32748
|
+
if (!optedIn) return explicitRequiredTags;
|
|
32749
|
+
return normalizeMeshCapabilityTags([...explicitRequiredTags, MESH_CONVERGE_REFINE_TAG]);
|
|
32750
|
+
}
|
|
32669
32751
|
function withQueueLock(_meshId, fn) {
|
|
32670
32752
|
return MeshRuntimeStore.getInstance().transaction(fn);
|
|
32671
32753
|
}
|
|
@@ -32724,7 +32806,15 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
|
|
|
32724
32806
|
taskMode: modeValidation.taskMode,
|
|
32725
32807
|
targetNodeId: opts?.targetNodeId,
|
|
32726
32808
|
targetSessionId: opts?.targetSessionId,
|
|
32727
|
-
|
|
32809
|
+
// Convergence routing (opt-in): auto-inject converge=refine for code_change
|
|
32810
|
+
// tasks so they hard-filter onto refine-capable worktree nodes. No-op unless
|
|
32811
|
+
// the mesh opts in; explicit target_node_id / required_tags are preserved.
|
|
32812
|
+
requiredTags: resolveConvergeRequiredTags(
|
|
32813
|
+
meshId,
|
|
32814
|
+
modeValidation.taskMode,
|
|
32815
|
+
normalizeMeshCapabilityTags(opts?.requiredTags),
|
|
32816
|
+
{ targetNodeId: opts?.targetNodeId }
|
|
32817
|
+
),
|
|
32728
32818
|
...dependsOn.length > 0 ? { dependsOn } : {},
|
|
32729
32819
|
...typeof opts?.missionId === "string" && opts.missionId.trim() ? { missionId: opts.missionId.trim() } : {},
|
|
32730
32820
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -32992,6 +33082,7 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
|
|
|
32992
33082
|
"use strict";
|
|
32993
33083
|
import_crypto5 = require("crypto");
|
|
32994
33084
|
init_mesh_host_ownership();
|
|
33085
|
+
init_repo_mesh_types();
|
|
32995
33086
|
init_mesh_runtime_store();
|
|
32996
33087
|
init_mesh_config();
|
|
32997
33088
|
ACTIVE_MESH_QUEUE_STATUSES = ["pending", "assigned"];
|
|
@@ -33289,6 +33380,17 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
|
|
|
33289
33380
|
|
|
33290
33381
|
CREATE INDEX IF NOT EXISTS idx_mesh_missions_mesh_status
|
|
33291
33382
|
ON mesh_missions(mesh_id, status, updated_at);
|
|
33383
|
+
|
|
33384
|
+
-- Load-balancing scheduler: per-mesh round-robin rotation cursor. When
|
|
33385
|
+
-- the schedulingStrategy is 'round_robin', several eligible nodes tied at
|
|
33386
|
+
-- the least load are rotated by this cursor so the tie-break winner cycles
|
|
33387
|
+
-- across scheduling passes instead of always favouring the same array-order
|
|
33388
|
+
-- node. Persisted (not a module Map) so rotation survives daemon restarts
|
|
33389
|
+
-- and stays a single source of truth across scheduling entry points.
|
|
33390
|
+
CREATE TABLE IF NOT EXISTS mesh_scheduler_cursor (
|
|
33391
|
+
mesh_id TEXT PRIMARY KEY,
|
|
33392
|
+
cursor INTEGER NOT NULL DEFAULT 0
|
|
33393
|
+
);
|
|
33292
33394
|
`);
|
|
33293
33395
|
}
|
|
33294
33396
|
hasCompletionFingerprint(fingerprint) {
|
|
@@ -33466,6 +33568,44 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
|
|
|
33466
33568
|
`).get(meshId, nodeId);
|
|
33467
33569
|
return row !== void 0;
|
|
33468
33570
|
}
|
|
33571
|
+
/**
|
|
33572
|
+
* Count active (status='assigned') tasks on a node, regardless of provider or
|
|
33573
|
+
* task mode. This is the load metric for least-loaded / round-robin ranking:
|
|
33574
|
+
* the scheduler prefers the node with the fewest active assignments so
|
|
33575
|
+
* untargeted work spreads instead of piling onto whichever node asks first.
|
|
33576
|
+
*/
|
|
33577
|
+
nodeActiveAssignmentCount(meshId, nodeId) {
|
|
33578
|
+
const row = this.db.prepare(`
|
|
33579
|
+
SELECT COUNT(*) as count FROM mesh_queue
|
|
33580
|
+
WHERE mesh_id = ? AND status = 'assigned' AND assigned_node_id = ?
|
|
33581
|
+
`).get(meshId, nodeId);
|
|
33582
|
+
return row?.count ?? 0;
|
|
33583
|
+
}
|
|
33584
|
+
/**
|
|
33585
|
+
* Read the current per-mesh round-robin cursor (0 when unset). Used to rotate
|
|
33586
|
+
* the tie-break winner among nodes tied at the least load.
|
|
33587
|
+
*/
|
|
33588
|
+
getSchedulerCursor(meshId) {
|
|
33589
|
+
const row = this.db.prepare(
|
|
33590
|
+
"SELECT cursor FROM mesh_scheduler_cursor WHERE mesh_id = ?"
|
|
33591
|
+
).get(meshId);
|
|
33592
|
+
return row?.cursor ?? 0;
|
|
33593
|
+
}
|
|
33594
|
+
/**
|
|
33595
|
+
* Atomically advance the per-mesh round-robin cursor by one and return the
|
|
33596
|
+
* value that was current BEFORE the bump (the value the caller should rotate
|
|
33597
|
+
* by for this pass). UPSERT keeps it lock-free across concurrent passes.
|
|
33598
|
+
*/
|
|
33599
|
+
bumpSchedulerCursor(meshId) {
|
|
33600
|
+
return this.transaction(() => {
|
|
33601
|
+
const current = this.getSchedulerCursor(meshId);
|
|
33602
|
+
this.db.prepare(`
|
|
33603
|
+
INSERT INTO mesh_scheduler_cursor (mesh_id, cursor) VALUES (?, ?)
|
|
33604
|
+
ON CONFLICT(mesh_id) DO UPDATE SET cursor = excluded.cursor
|
|
33605
|
+
`).run(meshId, current + 1);
|
|
33606
|
+
return current;
|
|
33607
|
+
});
|
|
33608
|
+
}
|
|
33469
33609
|
/**
|
|
33470
33610
|
* Count active (status='assigned') tasks on a (node, provider) combination,
|
|
33471
33611
|
* matched by the assignedProviderType stamped on the payload at claim time.
|
|
@@ -38051,6 +38191,33 @@ Next step: ${nextStep}`;
|
|
|
38051
38191
|
function nodeHasActiveAssignment(meshId, nodeId) {
|
|
38052
38192
|
return getQueue(meshId, { status: ["assigned"] }).some((task) => task.assignedNodeId === nodeId);
|
|
38053
38193
|
}
|
|
38194
|
+
function nodeActiveLoad(meshId, nodeId) {
|
|
38195
|
+
return MeshRuntimeStore.getInstance().nodeActiveAssignmentCount(meshId, nodeId);
|
|
38196
|
+
}
|
|
38197
|
+
function resolveSchedulingStrategy(mesh) {
|
|
38198
|
+
return normalizeMeshSchedulingStrategy(mesh?.policy?.schedulingStrategy);
|
|
38199
|
+
}
|
|
38200
|
+
function orderEligibleNodes(meshId, strategy, nodes, opts) {
|
|
38201
|
+
if (strategy === "first_eligible" || nodes.length <= 1) {
|
|
38202
|
+
return nodes;
|
|
38203
|
+
}
|
|
38204
|
+
const priorityOf = (n) => resolveNodeSchedulingPriority(n.node?.policy);
|
|
38205
|
+
let rotation = 0;
|
|
38206
|
+
if (strategy === "round_robin") {
|
|
38207
|
+
const cursor = opts?.bumpCursor ? MeshRuntimeStore.getInstance().bumpSchedulerCursor(meshId) : MeshRuntimeStore.getInstance().getSchedulerCursor(meshId);
|
|
38208
|
+
rotation = (cursor % nodes.length + nodes.length) % nodes.length;
|
|
38209
|
+
}
|
|
38210
|
+
const rotationRank = (index) => (index - rotation + nodes.length) % nodes.length;
|
|
38211
|
+
return [...nodes].sort((a, b) => {
|
|
38212
|
+
const prioDelta = priorityOf(b) - priorityOf(a);
|
|
38213
|
+
if (prioDelta !== 0) return prioDelta;
|
|
38214
|
+
if (strategy === "least_loaded" || strategy === "round_robin") {
|
|
38215
|
+
const loadDelta = nodeActiveLoad(meshId, a.nodeId) - nodeActiveLoad(meshId, b.nodeId);
|
|
38216
|
+
if (loadDelta !== 0) return loadDelta;
|
|
38217
|
+
}
|
|
38218
|
+
return rotationRank(a.index) - rotationRank(b.index);
|
|
38219
|
+
});
|
|
38220
|
+
}
|
|
38054
38221
|
function activeProviderAssignedCount(meshId, nodeId, providerType) {
|
|
38055
38222
|
return getQueue(meshId, { status: ["assigned"] }).filter((task) => task.assignedNodeId === nodeId && task.assignedProviderType === providerType).length;
|
|
38056
38223
|
}
|
|
@@ -38187,7 +38354,14 @@ Next step: ${nextStep}`;
|
|
|
38187
38354
|
markAutoLaunch(meshId, task.id, { status: "skipped", reason: "no_node_satisfies_required_tags", nodeId: task.targetNodeId });
|
|
38188
38355
|
continue;
|
|
38189
38356
|
}
|
|
38190
|
-
|
|
38357
|
+
const strategy = resolveSchedulingStrategy(mesh);
|
|
38358
|
+
const orderedCandidateNodes = strategy === "first_eligible" ? candidateNodes : orderEligibleNodes(
|
|
38359
|
+
meshId,
|
|
38360
|
+
strategy,
|
|
38361
|
+
candidateNodes.map((node, index) => ({ nodeId: readMeshNodeId(node), node, index })).filter((c) => c.nodeId),
|
|
38362
|
+
{ bumpCursor: true }
|
|
38363
|
+
).map((c) => c.node);
|
|
38364
|
+
for (const node of orderedCandidateNodes) {
|
|
38191
38365
|
const nodeId = readMeshNodeId(node);
|
|
38192
38366
|
if (!nodeId) continue;
|
|
38193
38367
|
const launchKey = `${meshId}:${nodeId}`;
|
|
@@ -38314,6 +38488,8 @@ Next step: ${nextStep}`;
|
|
|
38314
38488
|
noIdleMeshSessionAvailable: true
|
|
38315
38489
|
};
|
|
38316
38490
|
}
|
|
38491
|
+
const strategy = resolveSchedulingStrategy(mesh);
|
|
38492
|
+
const localCandidates = [];
|
|
38317
38493
|
const cliInstances = components.instanceManager.getByCategory("cli");
|
|
38318
38494
|
for (const inst of cliInstances) {
|
|
38319
38495
|
const state = inst.getState();
|
|
@@ -38336,7 +38512,7 @@ Next step: ${nextStep}`;
|
|
|
38336
38512
|
const providerType = state.type || readNonEmptyString2(settings.providerType);
|
|
38337
38513
|
if (providerType) {
|
|
38338
38514
|
localIdleSessionsChecked += 1;
|
|
38339
|
-
|
|
38515
|
+
localCandidates.push({ nodeId, sessionId, providerType, origin: "local", node: mesh.nodes.find((n) => readMeshNodeId(n) === nodeId) });
|
|
38340
38516
|
} else {
|
|
38341
38517
|
skippedSessions.push({
|
|
38342
38518
|
nodeId,
|
|
@@ -38350,18 +38526,49 @@ Next step: ${nextStep}`;
|
|
|
38350
38526
|
remoteSessions = MeshRuntimeStore.getInstance().getRemoteIdleSessions();
|
|
38351
38527
|
} catch {
|
|
38352
38528
|
}
|
|
38529
|
+
const remoteCandidates = [];
|
|
38353
38530
|
for (const idle of remoteSessions) {
|
|
38354
38531
|
const node = mesh.nodes.find((n) => n.id === idle.nodeId);
|
|
38355
38532
|
if (node) {
|
|
38356
38533
|
remoteIdleSessionsChecked += 1;
|
|
38357
|
-
|
|
38358
|
-
|
|
38359
|
-
|
|
38360
|
-
|
|
38361
|
-
|
|
38362
|
-
|
|
38534
|
+
remoteCandidates.push({ nodeId: idle.nodeId, sessionId: idle.sessionId, providerType: idle.providerType, origin: "remote", node });
|
|
38535
|
+
}
|
|
38536
|
+
}
|
|
38537
|
+
const assignIdleCandidate = (candidate) => {
|
|
38538
|
+
const assigned = tryAssignQueueTask(components, meshId, candidate.nodeId, candidate.sessionId, candidate.providerType);
|
|
38539
|
+
if (assigned && candidate.origin === "remote") {
|
|
38540
|
+
try {
|
|
38541
|
+
MeshRuntimeStore.getInstance().deleteRemoteIdleSession(candidate.nodeId, candidate.sessionId);
|
|
38542
|
+
} catch {
|
|
38363
38543
|
}
|
|
38364
38544
|
}
|
|
38545
|
+
};
|
|
38546
|
+
if (strategy === "first_eligible") {
|
|
38547
|
+
for (const candidate of localCandidates) assignIdleCandidate(candidate);
|
|
38548
|
+
for (const candidate of remoteCandidates) assignIdleCandidate(candidate);
|
|
38549
|
+
} else {
|
|
38550
|
+
const pool = [...localCandidates, ...remoteCandidates];
|
|
38551
|
+
const baseIndex = /* @__PURE__ */ new Map();
|
|
38552
|
+
pool.forEach((c, i) => {
|
|
38553
|
+
if (!baseIndex.has(c.nodeId)) baseIndex.set(c.nodeId, i);
|
|
38554
|
+
});
|
|
38555
|
+
const uniqueNodes = [...new Set(pool.map((c) => c.nodeId))].map((nodeId, index) => ({ nodeId, node: pool.find((c) => c.nodeId === nodeId)?.node, index }));
|
|
38556
|
+
const ranked = orderEligibleNodes(meshId, strategy, uniqueNodes, { bumpCursor: true });
|
|
38557
|
+
const rankIndex = new Map(ranked.map((r, i) => [r.nodeId, i]));
|
|
38558
|
+
const remaining = [...pool];
|
|
38559
|
+
while (remaining.length > 0) {
|
|
38560
|
+
remaining.sort((a, b) => {
|
|
38561
|
+
const aPrio = resolveNodeSchedulingPriority(a.node?.policy);
|
|
38562
|
+
const bPrio = resolveNodeSchedulingPriority(b.node?.policy);
|
|
38563
|
+
if (aPrio !== bPrio) return bPrio - aPrio;
|
|
38564
|
+
if (strategy === "least_loaded" || strategy === "round_robin") {
|
|
38565
|
+
const loadDelta = nodeActiveLoad(meshId, a.nodeId) - nodeActiveLoad(meshId, b.nodeId);
|
|
38566
|
+
if (loadDelta !== 0) return loadDelta;
|
|
38567
|
+
}
|
|
38568
|
+
return (rankIndex.get(a.nodeId) ?? 0) - (rankIndex.get(b.nodeId) ?? 0);
|
|
38569
|
+
});
|
|
38570
|
+
assignIdleCandidate(remaining.shift());
|
|
38571
|
+
}
|
|
38365
38572
|
}
|
|
38366
38573
|
autoLaunchStarted = await maybeAutoLaunchOneQueueSession(components, meshId, mesh);
|
|
38367
38574
|
const afterQueue = getQueue(meshId);
|
|
@@ -41743,7 +41950,7 @@ ${cont}` : cont;
|
|
|
41743
41950
|
scheduleSettle() {
|
|
41744
41951
|
if (this.settleTimer) clearTimeout(this.settleTimer);
|
|
41745
41952
|
const epoch = this.responseEpoch;
|
|
41746
|
-
const
|
|
41953
|
+
const delay2 = Math.max(
|
|
41747
41954
|
this.timeouts.outputSettle,
|
|
41748
41955
|
this.submitPendingUntil > Date.now() ? this.submitPendingUntil - Date.now() + this.timeouts.outputSettle : 0
|
|
41749
41956
|
);
|
|
@@ -41751,7 +41958,7 @@ ${cont}` : cont;
|
|
|
41751
41958
|
this.settleTimer = null;
|
|
41752
41959
|
if (epoch !== this.responseEpoch) return;
|
|
41753
41960
|
this.evaluateSettled(this.transport.getSnapshot());
|
|
41754
|
-
},
|
|
41961
|
+
}, delay2);
|
|
41755
41962
|
}
|
|
41756
41963
|
/** Called from sendMessage in transport once a turn scope is established. */
|
|
41757
41964
|
onTurnStarted(turnScope) {
|
|
@@ -45262,6 +45469,7 @@ ${lastSnapshot}`;
|
|
|
45262
45469
|
DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS: () => DEFAULT_GIT_WORKSPACE_POLL_INTERVAL_MS,
|
|
45263
45470
|
DEFAULT_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS: () => DEFAULT_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS2,
|
|
45264
45471
|
DEFAULT_MESH_POLICY: () => DEFAULT_MESH_POLICY,
|
|
45472
|
+
DEFAULT_MESH_SCHEDULING_STRATEGY: () => DEFAULT_MESH_SCHEDULING_STRATEGY,
|
|
45265
45473
|
DEFAULT_SESSION_HOST_APP_NAME: () => DEFAULT_SESSION_HOST_APP_NAME,
|
|
45266
45474
|
DEFAULT_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS: () => DEFAULT_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS2,
|
|
45267
45475
|
DEFAULT_SESSION_HOST_READY_TIMEOUT_MS: () => DEFAULT_SESSION_HOST_READY_TIMEOUT_MS2,
|
|
@@ -45287,9 +45495,12 @@ ${lastSnapshot}`;
|
|
|
45287
45495
|
InMemoryGitSnapshotStore: () => InMemoryGitSnapshotStore,
|
|
45288
45496
|
LOG: () => LOG2,
|
|
45289
45497
|
MAX_LEDGER_SLICE_LIMIT: () => MAX_LEDGER_SLICE_LIMIT,
|
|
45498
|
+
MESH_CONVERGE_FAST_FORWARD_TAG: () => MESH_CONVERGE_FAST_FORWARD_TAG,
|
|
45499
|
+
MESH_CONVERGE_REFINE_TAG: () => MESH_CONVERGE_REFINE_TAG,
|
|
45290
45500
|
MESH_MISSION_STATUSES: () => MESH_MISSION_STATUSES,
|
|
45291
45501
|
MESH_REFINE_CONFIG_LOCATIONS: () => MESH_REFINE_CONFIG_LOCATIONS,
|
|
45292
45502
|
MESH_REFINE_CONFIG_SCHEMA: () => MESH_REFINE_CONFIG_SCHEMA,
|
|
45503
|
+
MESH_SCHEDULING_STRATEGIES: () => MESH_SCHEDULING_STRATEGIES,
|
|
45293
45504
|
MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS: () => MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS,
|
|
45294
45505
|
MESH_WORKTREE_BOOTSTRAP_CONFIG_SCHEMA: () => MESH_WORKTREE_BOOTSTRAP_CONFIG_SCHEMA,
|
|
45295
45506
|
MIN_GIT_WORKSPACE_POLL_INTERVAL_MS: () => MIN_GIT_WORKSPACE_POLL_INTERVAL_MS,
|
|
@@ -45490,6 +45701,7 @@ ${lastSnapshot}`;
|
|
|
45490
45701
|
normalizeManagedStatus: () => normalizeManagedStatus,
|
|
45491
45702
|
normalizeMeshCapabilityTags: () => normalizeMeshCapabilityTags,
|
|
45492
45703
|
normalizeMeshDaemonRole: () => normalizeMeshDaemonRole,
|
|
45704
|
+
normalizeMeshSchedulingStrategy: () => normalizeMeshSchedulingStrategy,
|
|
45493
45705
|
normalizeMeshTaskMode: () => normalizeMeshTaskMode,
|
|
45494
45706
|
normalizeMeshWorkerResult: () => normalizeMeshWorkerResult,
|
|
45495
45707
|
normalizeMessageParts: () => normalizeMessageParts,
|
|
@@ -45527,7 +45739,9 @@ ${lastSnapshot}`;
|
|
|
45527
45739
|
resetConfig: () => resetConfig,
|
|
45528
45740
|
resetDebugRuntimeConfig: () => resetDebugRuntimeConfig,
|
|
45529
45741
|
resetState: () => resetState,
|
|
45742
|
+
resolveAutoConvergeCodeChange: () => resolveAutoConvergeCodeChange,
|
|
45530
45743
|
resolveChatMessageKind: () => resolveChatMessageKind,
|
|
45744
|
+
resolveConvergeRequiredTags: () => resolveConvergeRequiredTags,
|
|
45531
45745
|
resolveCurrentGlobalInstallSurface: () => resolveCurrentGlobalInstallSurface,
|
|
45532
45746
|
resolveDebugRuntimeConfig: () => resolveDebugRuntimeConfig,
|
|
45533
45747
|
resolveDelegatedWorkerAutoApprove: () => resolveDelegatedWorkerAutoApprove,
|
|
@@ -45535,6 +45749,7 @@ ${lastSnapshot}`;
|
|
|
45535
45749
|
resolveGitRepository: () => resolveGitRepository,
|
|
45536
45750
|
resolveMeshHostStatus: () => resolveMeshHostStatus,
|
|
45537
45751
|
resolveMeshRefineValidationPlan: () => resolveMeshRefineValidationPlan,
|
|
45752
|
+
resolveNodeSchedulingPriority: () => resolveNodeSchedulingPriority,
|
|
45538
45753
|
resolveSessionHostAppName: () => resolveSessionHostAppName,
|
|
45539
45754
|
resolveSessionHostAppNameResolution: () => resolveSessionHostAppNameResolution2,
|
|
45540
45755
|
resolveWorktreePath: () => resolveWorktreePath,
|
|
@@ -47863,9 +48078,9 @@ ${lastSnapshot}`;
|
|
|
47863
48078
|
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
47864
48079
|
}
|
|
47865
48080
|
function summarizeMessage(message) {
|
|
47866
|
-
const
|
|
47867
|
-
const title =
|
|
47868
|
-
return { title: title || "(untitled task)", summary:
|
|
48081
|
+
const oneLine2 = message.replace(/\s+/g, " ").trim();
|
|
48082
|
+
const title = oneLine2.length > 96 ? `${oneLine2.slice(0, 93)}...` : oneLine2;
|
|
48083
|
+
return { title: title || "(untitled task)", summary: oneLine2 };
|
|
47869
48084
|
}
|
|
47870
48085
|
function elapsedSince(value, now) {
|
|
47871
48086
|
const started = value ? new Date(value).getTime() : Number.NaN;
|
|
@@ -50937,6 +51152,11 @@ ${cleanBody}`;
|
|
|
50937
51152
|
continue;
|
|
50938
51153
|
}
|
|
50939
51154
|
if (message.role === "assistant") {
|
|
51155
|
+
const isActivity = message.kind === "tool" || message.kind === "terminal" || message.kind === "thought";
|
|
51156
|
+
if (isActivity) {
|
|
51157
|
+
collapsed.push(message);
|
|
51158
|
+
continue;
|
|
51159
|
+
}
|
|
50940
51160
|
if (sawAssistantSinceLastUser) continue;
|
|
50941
51161
|
sawAssistantSinceLastUser = true;
|
|
50942
51162
|
collapsed.push(message);
|
|
@@ -56028,7 +56248,8 @@ ${effect.notification.body || ""}`.trim();
|
|
|
56028
56248
|
const sessionIdHint = typeof args?.targetSessionId === "string" ? args.targetSessionId : typeof args?.sessionId === "string" ? args.sessionId : "";
|
|
56029
56249
|
const providerHint = typeof args?.cliType === "string" ? args.cliType : typeof args?.providerType === "string" ? args.providerType : typeof args?.agentType === "string" ? args.agentType : "";
|
|
56030
56250
|
const filteredMessages = h ? maybeHideCoordinatorPromptMessage(h, providerHint, sessionIdHint, messages) : messages;
|
|
56031
|
-
const
|
|
56251
|
+
const includeActivity = args?.includeActivity === true || args?.includeActivity === "true";
|
|
56252
|
+
const visibleMessages = includeActivity ? filteredMessages.filter((m) => isUserFacingChatMessage(m) || isActivityChatMessage(m)) : filterUserFacingChatMessages(filteredMessages);
|
|
56032
56253
|
const sync = buildFullTail(visibleMessages, normalizeReadChatTailLimit(args));
|
|
56033
56254
|
const hiddenMsgCount = Math.max(0, messages.length - visibleMessages.length);
|
|
56034
56255
|
const preservedPayloadFields = Object.fromEntries(Object.entries(payload).filter(([key]) => shouldPreserveReadChatPayloadField(key)));
|
|
@@ -58387,6 +58608,9 @@ ${effect.notification.body || ""}`.trim();
|
|
|
58387
58608
|
}
|
|
58388
58609
|
function buildControlScriptResult(scriptName, payload) {
|
|
58389
58610
|
if (!payload || typeof payload !== "object") return {};
|
|
58611
|
+
if (payload.controlResult && typeof payload.controlResult === "object") {
|
|
58612
|
+
return { controlResult: payload.controlResult };
|
|
58613
|
+
}
|
|
58390
58614
|
const legacyListPayload = (() => {
|
|
58391
58615
|
if (Array.isArray(payload.options)) return payload;
|
|
58392
58616
|
if (/^listmodels$/i.test(scriptName) && Array.isArray(payload.models)) {
|
|
@@ -60217,12 +60441,12 @@ ${formatManifestValidationIssues2(validation.issues)}`,
|
|
|
60217
60441
|
scheduleSpawnPrime() {
|
|
60218
60442
|
const seqs = this.spec.send_on_spawn;
|
|
60219
60443
|
if (!Array.isArray(seqs) || seqs.length === 0) return;
|
|
60220
|
-
const
|
|
60444
|
+
const delay2 = Math.max(0, this.spec.send_on_spawn_delay_ms ?? 250);
|
|
60221
60445
|
setTimeout(() => {
|
|
60222
60446
|
for (const seq of seqs) {
|
|
60223
60447
|
if (typeof seq === "string" && seq.length > 0) this.adapter.send_keys(seq);
|
|
60224
60448
|
}
|
|
60225
|
-
},
|
|
60449
|
+
}, delay2);
|
|
60226
60450
|
}
|
|
60227
60451
|
dispatch(cmd) {
|
|
60228
60452
|
switch (cmd.kind) {
|
|
@@ -60588,11 +60812,11 @@ ${formatManifestValidationIssues2(validation.issues)}`,
|
|
|
60588
60812
|
const armed = this.delegateTimers.has(d.id);
|
|
60589
60813
|
const shouldFire = d.when_state === currentStateId;
|
|
60590
60814
|
if (shouldFire && !armed) {
|
|
60591
|
-
const
|
|
60815
|
+
const delay2 = d.after_duration_ms ?? 0;
|
|
60592
60816
|
const t = setTimeout(() => {
|
|
60593
60817
|
this.fireDelegate(d);
|
|
60594
60818
|
this.delegateTimers.delete(d.id);
|
|
60595
|
-
},
|
|
60819
|
+
}, delay2);
|
|
60596
60820
|
this.delegateTimers.set(d.id, t);
|
|
60597
60821
|
} else if (!shouldFire && armed) {
|
|
60598
60822
|
clearTimeout(this.delegateTimers.get(d.id));
|
|
@@ -60646,7 +60870,15 @@ ${formatManifestValidationIssues2(validation.issues)}`,
|
|
|
60646
60870
|
this.adapter.send_keys(a.keys);
|
|
60647
60871
|
return;
|
|
60648
60872
|
case "open_picker":
|
|
60649
|
-
|
|
60873
|
+
{
|
|
60874
|
+
const m = /^([\s\S]*?)([\r\n]+)$/.exec(a.trigger_keys);
|
|
60875
|
+
if (m && m[1]) {
|
|
60876
|
+
this.adapter.send_keys(m[1]);
|
|
60877
|
+
setTimeout(() => this.adapter.send_keys(m[2]), 200);
|
|
60878
|
+
} else {
|
|
60879
|
+
this.adapter.send_keys(a.trigger_keys);
|
|
60880
|
+
}
|
|
60881
|
+
}
|
|
60650
60882
|
this.pickerInProgress = { control_id: ctl.id, spec: ctl };
|
|
60651
60883
|
return;
|
|
60652
60884
|
case "attach_image": {
|
|
@@ -60829,8 +61061,7 @@ ${formatManifestValidationIssues2(validation.issues)}`,
|
|
|
60829
61061
|
for (let i = 0; i < lines.length; i += 1) {
|
|
60830
61062
|
const rec = lines[i];
|
|
60831
61063
|
if (filter && !filter(rec)) continue;
|
|
60832
|
-
const msg
|
|
60833
|
-
if (msg) {
|
|
61064
|
+
for (const msg of projectMessages(rec, src.message_map, i, lines.length, mtime)) {
|
|
60834
61065
|
if (transcriptWorkspace) msg.workspace = transcriptWorkspace;
|
|
60835
61066
|
messages.push(msg);
|
|
60836
61067
|
}
|
|
@@ -60910,8 +61141,9 @@ ${formatManifestValidationIssues2(validation.issues)}`,
|
|
|
60910
61141
|
const mtime = safeMtimeMs(resolved);
|
|
60911
61142
|
const messages = [];
|
|
60912
61143
|
for (let i = 0; i < messageRows.length; i += 1) {
|
|
60913
|
-
const msg
|
|
60914
|
-
|
|
61144
|
+
for (const msg of projectMessages(messageRows[i], src.message_map, i, messageRows.length, mtime)) {
|
|
61145
|
+
messages.push(msg);
|
|
61146
|
+
}
|
|
60915
61147
|
}
|
|
60916
61148
|
if (messages.length === 0) return null;
|
|
60917
61149
|
return {
|
|
@@ -61309,11 +61541,42 @@ ${formatManifestValidationIssues2(validation.issues)}`,
|
|
|
61309
61541
|
}
|
|
61310
61542
|
return cur;
|
|
61311
61543
|
}
|
|
61312
|
-
function
|
|
61544
|
+
function projectMessages(record2, map2, index, total, sourceMtimeMs) {
|
|
61313
61545
|
const roleRaw = jsonPathGet(record2, map2.role);
|
|
61314
|
-
const contentRaw = jsonPathGet(record2, map2.content);
|
|
61315
61546
|
const role = normalizeRole(roleRaw);
|
|
61316
|
-
let
|
|
61547
|
+
let receivedAt = sourceMtimeMs - (total - 1 - index) * 1e3;
|
|
61548
|
+
if (map2.timestamp_ms) {
|
|
61549
|
+
const tsRaw = jsonPathGet(record2, map2.timestamp_ms);
|
|
61550
|
+
const parsed = parseTimestamp(tsRaw);
|
|
61551
|
+
if (parsed != null) receivedAt = parsed;
|
|
61552
|
+
}
|
|
61553
|
+
const kindRaw = map2.kind ? jsonPathGet(record2, map2.kind) : void 0;
|
|
61554
|
+
const kind = typeof kindRaw === "string" && kindRaw ? kindRaw : "standard";
|
|
61555
|
+
const out = [];
|
|
61556
|
+
if (map2.tools) {
|
|
61557
|
+
const recordTool = projectToolBlock(record2, role, map2.tools);
|
|
61558
|
+
if (recordTool) {
|
|
61559
|
+
out.push({ ...recordTool, receivedAt });
|
|
61560
|
+
return out;
|
|
61561
|
+
}
|
|
61562
|
+
}
|
|
61563
|
+
const contentRaw = jsonPathGet(record2, map2.content);
|
|
61564
|
+
const content = cleanContent(stringifyContent(contentRaw), map2);
|
|
61565
|
+
if (content) out.push({ role, content, receivedAt, kind });
|
|
61566
|
+
if (map2.tools && Array.isArray(contentRaw)) {
|
|
61567
|
+
let nudge = 1;
|
|
61568
|
+
for (const block2 of contentRaw) {
|
|
61569
|
+
const tool = projectToolBlock(block2, role, map2.tools);
|
|
61570
|
+
if (tool) {
|
|
61571
|
+
out.push({ ...tool, receivedAt: receivedAt + nudge });
|
|
61572
|
+
nudge += 1;
|
|
61573
|
+
}
|
|
61574
|
+
}
|
|
61575
|
+
}
|
|
61576
|
+
return out;
|
|
61577
|
+
}
|
|
61578
|
+
function cleanContent(input, map2) {
|
|
61579
|
+
let content = input;
|
|
61317
61580
|
if (content && map2.content_strip) {
|
|
61318
61581
|
for (const tag of map2.content_strip) {
|
|
61319
61582
|
const safeTag = tag.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
@@ -61329,17 +61592,33 @@ ${formatManifestValidationIssues2(validation.issues)}`,
|
|
|
61329
61592
|
content = content.replace(open, "").replace(close, "");
|
|
61330
61593
|
}
|
|
61331
61594
|
}
|
|
61332
|
-
|
|
61333
|
-
|
|
61334
|
-
|
|
61335
|
-
|
|
61336
|
-
|
|
61337
|
-
|
|
61338
|
-
|
|
61595
|
+
return content ? content.trim() : "";
|
|
61596
|
+
}
|
|
61597
|
+
var DEFAULT_TOOL_CALL_TYPES = ["tool_use", "function_call", "custom_tool_call"];
|
|
61598
|
+
var DEFAULT_TOOL_RESULT_TYPES = ["tool_result", "function_call_output", "custom_tool_call_output"];
|
|
61599
|
+
function projectToolBlock(block2, role, tmap) {
|
|
61600
|
+
void role;
|
|
61601
|
+
if (block2 == null || typeof block2 !== "object") return null;
|
|
61602
|
+
const typeVal = String(jsonPathGet(block2, tmap.block_type || "$.type") ?? "");
|
|
61603
|
+
if (!typeVal) return null;
|
|
61604
|
+
const callTypes = tmap.call_types ?? DEFAULT_TOOL_CALL_TYPES;
|
|
61605
|
+
const resultTypes = tmap.result_types ?? DEFAULT_TOOL_RESULT_TYPES;
|
|
61606
|
+
if (callTypes.includes(typeVal)) {
|
|
61607
|
+
const name = String(jsonPathGet(block2, tmap.call_name || "$.name") ?? "tool").trim() || "tool";
|
|
61608
|
+
const args = oneLine(stringifyContent(jsonPathGet(block2, tmap.call_args || "$.input")), 240);
|
|
61609
|
+
const content = args ? `\u2197 ${name}: ${args}` : `\u2197 ${name}`;
|
|
61610
|
+
return { role: "assistant", content, receivedAt: 0, kind: "tool" };
|
|
61611
|
+
}
|
|
61612
|
+
if (resultTypes.includes(typeVal)) {
|
|
61613
|
+
const result = oneLine(stringifyContent(jsonPathGet(block2, tmap.result_content || "$.content")), 600);
|
|
61614
|
+
if (!result) return null;
|
|
61615
|
+
return { role: "assistant", content: `\u2198 ${result}`, receivedAt: 0, kind: "tool" };
|
|
61339
61616
|
}
|
|
61340
|
-
|
|
61341
|
-
|
|
61342
|
-
|
|
61617
|
+
return null;
|
|
61618
|
+
}
|
|
61619
|
+
function oneLine(s, max) {
|
|
61620
|
+
const flat = s.replace(/\s+/g, " ").trim();
|
|
61621
|
+
return flat.length > max ? flat.slice(0, max - 1) + "\u2026" : flat;
|
|
61343
61622
|
}
|
|
61344
61623
|
function parseTimestamp(v) {
|
|
61345
61624
|
if (v == null) return null;
|
|
@@ -61478,6 +61757,9 @@ ${formatManifestValidationIssues2(validation.issues)}`,
|
|
|
61478
61757
|
function stripAnsi3(text) {
|
|
61479
61758
|
return String(text || "").replace(/\x1B\][^\x07]*(?:\x07|\x1B\\)/g, "").replace(/\x1B[P^_X][\s\S]*?(?:\x07|\x1B\\)/g, "").replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "");
|
|
61480
61759
|
}
|
|
61760
|
+
function delay(ms) {
|
|
61761
|
+
return new Promise((resolve24) => setTimeout(resolve24, ms));
|
|
61762
|
+
}
|
|
61481
61763
|
var SpecCliAdapter = class _SpecCliAdapter {
|
|
61482
61764
|
cliType;
|
|
61483
61765
|
cliName;
|
|
@@ -61699,9 +61981,15 @@ ${formatManifestValidationIssues2(validation.issues)}`,
|
|
|
61699
61981
|
* drives the dispatch:
|
|
61700
61982
|
*
|
|
61701
61983
|
* send_keys → click_control (e.g. stop)
|
|
61702
|
-
* open_picker →
|
|
61703
|
-
*
|
|
61704
|
-
*
|
|
61984
|
+
* open_picker → two roles, driven by the screen, not a hardcoded list:
|
|
61985
|
+
* - LIST (no choice arg): open the picker, wait for it
|
|
61986
|
+
* to render, parse the on-screen options via
|
|
61987
|
+
* `extract_choices`, and return them as
|
|
61988
|
+
* `controlResult.options` (+ `currentValue`). This is
|
|
61989
|
+
* how the dashboard's Model/Mode controls learn what is
|
|
61990
|
+
* actually selectable in this CLI right now.
|
|
61991
|
+
* - SELECT (args.choiceIndex / args.choiceLabel): drive
|
|
61992
|
+
* the picker to that option using `submit_key`.
|
|
61705
61993
|
* attach_image → attach_image dispatch; expects args.blob (data url
|
|
61706
61994
|
* or base64) and args.mime
|
|
61707
61995
|
*
|
|
@@ -61726,11 +62014,128 @@ ${formatManifestValidationIssues2(validation.issues)}`,
|
|
|
61726
62014
|
this.driver.dispatch({ kind: "attach_image", blob, mime });
|
|
61727
62015
|
return Promise.resolve({ ok: true, effects: [{ type: "attached_image", controlId: ctl.id }] });
|
|
61728
62016
|
}
|
|
62017
|
+
if (action.type === "open_picker") {
|
|
62018
|
+
const choiceIndex = typeof flat.choiceIndex === "number" ? flat.choiceIndex : typeof flat.choiceIndex === "string" && flat.choiceIndex.trim() ? Number(flat.choiceIndex) : void 0;
|
|
62019
|
+
const choiceLabel = typeof flat.choiceLabel === "string" ? flat.choiceLabel : typeof flat.choice === "string" ? flat.choice : void 0;
|
|
62020
|
+
if (typeof choiceIndex === "number" && Number.isFinite(choiceIndex) || choiceLabel && choiceLabel.trim()) {
|
|
62021
|
+
return this.selectPickerChoice(ctl, action, choiceIndex, choiceLabel);
|
|
62022
|
+
}
|
|
62023
|
+
return this.openPickerAndListChoices(ctl, action);
|
|
62024
|
+
}
|
|
61729
62025
|
this.driver.dispatch({ kind: "click_control", control_id: ctl.id, payload: flat });
|
|
61730
|
-
|
|
61731
|
-
|
|
61732
|
-
|
|
61733
|
-
|
|
62026
|
+
return Promise.resolve({ ok: true, effects: [{ type: "sent_keys", controlId: ctl.id }] });
|
|
62027
|
+
}
|
|
62028
|
+
/**
|
|
62029
|
+
* Open an `open_picker` control and return the options the CLI is showing,
|
|
62030
|
+
* parsed live from the screen via `extract_choices`. Nothing is selected —
|
|
62031
|
+
* the picker is left open so a follow-up SELECT invoke can commit a choice.
|
|
62032
|
+
*/
|
|
62033
|
+
async openPickerAndListChoices(ctl, action) {
|
|
62034
|
+
this.driver.dispatch({ kind: "click_control", control_id: ctl.id });
|
|
62035
|
+
const ready = await this.waitForPickerRendered(action);
|
|
62036
|
+
const options = this.extractPickerChoices(action);
|
|
62037
|
+
const currentValue = options.find((o) => o.current)?.label;
|
|
62038
|
+
return {
|
|
62039
|
+
ok: true,
|
|
62040
|
+
effects: [{ type: "opened_picker", controlId: ctl.id }],
|
|
62041
|
+
controlResult: {
|
|
62042
|
+
options: options.map((o) => ({ value: o.label, label: o.label, current: o.current })),
|
|
62043
|
+
...currentValue ? { currentValue } : {},
|
|
62044
|
+
source: "screen-parse",
|
|
62045
|
+
...ready ? {} : { warning: "picker_render_timeout" }
|
|
62046
|
+
}
|
|
62047
|
+
};
|
|
62048
|
+
}
|
|
62049
|
+
/**
|
|
62050
|
+
* Drive an already-listable picker to a specific option. The option can be
|
|
62051
|
+
* named (choiceLabel — matched against the parsed on-screen labels) or
|
|
62052
|
+
* positional (choiceIndex — the on-screen number). The actual keystrokes
|
|
62053
|
+
* come from the spec's `submit_key` with `{index}` substituted, so the spec
|
|
62054
|
+
* — not this code — decides how a selection is keyed for each CLI.
|
|
62055
|
+
*/
|
|
62056
|
+
async selectPickerChoice(ctl, action, choiceIndex, choiceLabel) {
|
|
62057
|
+
this.driver.dispatch({ kind: "click_control", control_id: ctl.id });
|
|
62058
|
+
await this.waitForPickerRendered(action);
|
|
62059
|
+
const options = this.extractPickerChoices(action);
|
|
62060
|
+
let index = choiceIndex;
|
|
62061
|
+
if ((index == null || !Number.isFinite(index)) && choiceLabel) {
|
|
62062
|
+
const needle = choiceLabel.trim().toLowerCase();
|
|
62063
|
+
const match = options.find((o) => o.label.toLowerCase().includes(needle));
|
|
62064
|
+
if (!match) {
|
|
62065
|
+
return { ok: false, error: `choice not found on screen: ${choiceLabel}`, controlResult: { options: options.map((o) => ({ value: o.label, label: o.label })) } };
|
|
62066
|
+
}
|
|
62067
|
+
index = match.index;
|
|
62068
|
+
}
|
|
62069
|
+
if (index == null || !Number.isFinite(index)) {
|
|
62070
|
+
return { ok: false, error: "choiceIndex or choiceLabel required to select" };
|
|
62071
|
+
}
|
|
62072
|
+
const keys = (action.submit_key || "{index}\r").replace(/\{index\}/g, String(index));
|
|
62073
|
+
this.driver.dispatch({ kind: "pty_write", data: keys });
|
|
62074
|
+
const selected = options.find((o) => o.index === index);
|
|
62075
|
+
return {
|
|
62076
|
+
ok: true,
|
|
62077
|
+
effects: [{ type: "selected_choice", controlId: ctl.id }],
|
|
62078
|
+
controlResult: {
|
|
62079
|
+
ok: true,
|
|
62080
|
+
...selected ? { currentValue: selected.label } : {},
|
|
62081
|
+
selectedIndex: index
|
|
62082
|
+
}
|
|
62083
|
+
};
|
|
62084
|
+
}
|
|
62085
|
+
/** Poll the live screen until the picker's `wait_for` condition matches,
|
|
62086
|
+
* up to a short budget. Returns true if it rendered, false on timeout. */
|
|
62087
|
+
async waitForPickerRendered(action) {
|
|
62088
|
+
const wf = action.wait_for;
|
|
62089
|
+
if (!wf?.regex) {
|
|
62090
|
+
await delay(250);
|
|
62091
|
+
return true;
|
|
62092
|
+
}
|
|
62093
|
+
const re = new RegExp(wf.regex, wf.flags ?? "i");
|
|
62094
|
+
const deadline = Date.now() + 2500;
|
|
62095
|
+
while (Date.now() < deadline) {
|
|
62096
|
+
await delay(120);
|
|
62097
|
+
const hay = this.readScreenSectionText(wf.section);
|
|
62098
|
+
if (re.test(hay)) return true;
|
|
62099
|
+
}
|
|
62100
|
+
return false;
|
|
62101
|
+
}
|
|
62102
|
+
/** Parse the picker's `extract_choices` pattern against the live screen.
|
|
62103
|
+
* Each match yields { index, label, current }. `current` is true for the
|
|
62104
|
+
* line the CLI marks with its cursor glyph (❯ ›). Purely screen-driven —
|
|
62105
|
+
* no model/mode names are baked in. */
|
|
62106
|
+
extractPickerChoices(action) {
|
|
62107
|
+
const ec = action.extract_choices;
|
|
62108
|
+
if (!ec?.pattern) return [];
|
|
62109
|
+
const text = this.readScreenSectionText(ec.section);
|
|
62110
|
+
const out = [];
|
|
62111
|
+
const seen = /* @__PURE__ */ new Set();
|
|
62112
|
+
for (const rawLine of text.split("\n")) {
|
|
62113
|
+
const line = rawLine.replace(/\r$/, "");
|
|
62114
|
+
const m = new RegExp(ec.pattern, ec.flags ?? "").exec(line);
|
|
62115
|
+
if (!m) continue;
|
|
62116
|
+
const idx = Number(m[1]);
|
|
62117
|
+
if (!Number.isFinite(idx) || seen.has(idx)) continue;
|
|
62118
|
+
const label = (m[2] ?? "").replace(/\s+/g, " ").trim();
|
|
62119
|
+
if (!label) continue;
|
|
62120
|
+
const current = /^\s*[❯›>]/.test(line) || /[✔✓●]\s*$/.test(label);
|
|
62121
|
+
seen.add(idx);
|
|
62122
|
+
out.push({ index: idx, label, current });
|
|
62123
|
+
}
|
|
62124
|
+
return out;
|
|
62125
|
+
}
|
|
62126
|
+
/** Live text of a named screen section (or the whole screen when no
|
|
62127
|
+
* section is named), resolved from the driver's current sections. */
|
|
62128
|
+
readScreenSectionText(sectionId) {
|
|
62129
|
+
try {
|
|
62130
|
+
const sections = this.driver.getSections();
|
|
62131
|
+
if (sectionId && sections) {
|
|
62132
|
+
const hit = sections.find((s) => s.id === sectionId);
|
|
62133
|
+
if (hit) return hit.text;
|
|
62134
|
+
}
|
|
62135
|
+
return this.driver.getScreen();
|
|
62136
|
+
} catch {
|
|
62137
|
+
return "";
|
|
62138
|
+
}
|
|
61734
62139
|
}
|
|
61735
62140
|
getDebugSnapshot() {
|
|
61736
62141
|
let screen = "";
|
|
@@ -71882,6 +72287,43 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
|
|
|
71882
72287
|
}
|
|
71883
72288
|
var MESH_DIRECT_PROBE_TIMEOUT_MS = readMeshTimeoutEnvMs("MESH_DIRECT_PROBE_TIMEOUT_MS", 25e3);
|
|
71884
72289
|
var MESH_DIRECT_PROBE_RETRY_TIMEOUT_MS = readMeshTimeoutEnvMs("MESH_DIRECT_PROBE_RETRY_TIMEOUT_MS", 25e3);
|
|
72290
|
+
var MESH_DIRECT_PROBE_REUSE_MS = readMeshTimeoutEnvMs("MESH_DIRECT_PROBE_REUSE_MS", 12e3);
|
|
72291
|
+
var MeshGitProbeCache = class {
|
|
72292
|
+
constructor(reuseMs, now = Date.now) {
|
|
72293
|
+
this.reuseMs = reuseMs;
|
|
72294
|
+
this.now = now;
|
|
72295
|
+
}
|
|
72296
|
+
inflight = /* @__PURE__ */ new Map();
|
|
72297
|
+
recent = /* @__PURE__ */ new Map();
|
|
72298
|
+
key(daemonId, workspace) {
|
|
72299
|
+
return `${daemonId}::${workspace}`;
|
|
72300
|
+
}
|
|
72301
|
+
/**
|
|
72302
|
+
* Run `probe` for this peer, but reuse a fresh recent result or an in-flight
|
|
72303
|
+
* probe for the same key when one is available. `probe` is only invoked when
|
|
72304
|
+
* neither gate is satisfied.
|
|
72305
|
+
*/
|
|
72306
|
+
async probe(daemonId, workspace, probe) {
|
|
72307
|
+
const key = this.key(daemonId, workspace);
|
|
72308
|
+
const cached22 = this.recent.get(key);
|
|
72309
|
+
if (cached22 && this.now() - cached22.at < this.reuseMs) {
|
|
72310
|
+
return cached22.value;
|
|
72311
|
+
}
|
|
72312
|
+
const existing = this.inflight.get(key);
|
|
72313
|
+
if (existing) return existing;
|
|
72314
|
+
const pending = (async () => {
|
|
72315
|
+
const result = await probe();
|
|
72316
|
+
if (result) this.recent.set(key, { at: this.now(), value: result });
|
|
72317
|
+
return result;
|
|
72318
|
+
})();
|
|
72319
|
+
this.inflight.set(key, pending);
|
|
72320
|
+
try {
|
|
72321
|
+
return await pending;
|
|
72322
|
+
} finally {
|
|
72323
|
+
if (this.inflight.get(key) === pending) this.inflight.delete(key);
|
|
72324
|
+
}
|
|
72325
|
+
}
|
|
72326
|
+
};
|
|
71885
72327
|
async function probeRemoteMeshGitStatus(args) {
|
|
71886
72328
|
if (!args.dispatchMeshCommand) return null;
|
|
71887
72329
|
const remoteResult = await Promise.race([
|
|
@@ -71975,7 +72417,7 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
|
|
|
71975
72417
|
continue;
|
|
71976
72418
|
}
|
|
71977
72419
|
peerAttemptedCount += 1;
|
|
71978
|
-
const
|
|
72420
|
+
const runProbe = () => probeRemoteMeshGitStatusWithRetry({
|
|
71979
72421
|
dispatchMeshCommand: args.dispatchMeshCommand,
|
|
71980
72422
|
daemonId,
|
|
71981
72423
|
workspace,
|
|
@@ -71983,6 +72425,7 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
|
|
|
71983
72425
|
retryTimeoutMs: MESH_DIRECT_PROBE_RETRY_TIMEOUT_MS,
|
|
71984
72426
|
getConnection: args.getMeshPeerConnectionStatus
|
|
71985
72427
|
});
|
|
72428
|
+
const remoteGit = args.probeCache ? await args.probeCache.probe(daemonId, workspace, runProbe) : await runProbe();
|
|
71986
72429
|
if (remoteGit) {
|
|
71987
72430
|
recordInlineMeshDirectGitTruth(node, remoteGit, "selected_coordinator_mesh_p2p_git");
|
|
71988
72431
|
peerConfirmedCount += 1;
|
|
@@ -73305,6 +73748,10 @@ ${e?.stderr || ""}`
|
|
|
73305
73748
|
inlineMeshCache = /* @__PURE__ */ new Map();
|
|
73306
73749
|
/** Coordinator-owned whole-mesh aggregate status snapshots. Browser callers read this by default. */
|
|
73307
73750
|
aggregateMeshStatusCache = /* @__PURE__ */ new Map();
|
|
73751
|
+
/** Shared per-peer git_status probe dedup + recently-probed reuse gate.
|
|
73752
|
+
* Spans separate mesh_status/get_mesh calls so the dashboard auto-retry
|
|
73753
|
+
* loop cannot storm a slow peer with back-to-back refreshUpstream probes. */
|
|
73754
|
+
meshGitProbeCache = new MeshGitProbeCache(MESH_DIRECT_PROBE_REUSE_MS);
|
|
73308
73755
|
/** In-memory async Refinery jobs keyed by meshId:nodeId to reject/return duplicate in-flight requests. */
|
|
73309
73756
|
runningRefineJobs = /* @__PURE__ */ new Map();
|
|
73310
73757
|
/** Terminal async Refinery jobs preserve a clear answer after the worktree node has been removed. */
|
|
@@ -76207,7 +76654,8 @@ ${hintLines.join("\n")}` : "",
|
|
|
76207
76654
|
getMeshPeerConnectionStatus: this.deps.getMeshPeerConnectionStatus,
|
|
76208
76655
|
statusInstanceId: this.deps.statusInstanceId,
|
|
76209
76656
|
localMachineId: loadConfig2().machineId || "",
|
|
76210
|
-
probeRemotePeers
|
|
76657
|
+
probeRemotePeers,
|
|
76658
|
+
probeCache: this.meshGitProbeCache
|
|
76211
76659
|
});
|
|
76212
76660
|
const directTruthSatisfied = meshRecord.source !== "inline_bootstrap" || directTruth.directEvidenceCount > 0;
|
|
76213
76661
|
const sourceOfTruth = {
|
|
@@ -76811,6 +77259,22 @@ ${hintLines.join("\n")}` : "",
|
|
|
76811
77259
|
const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
|
|
76812
77260
|
const nodeId = typeof args?.nodeId === "string" ? args.nodeId.trim() : "";
|
|
76813
77261
|
if (!meshId || !nodeId) return { success: false, error: "meshId and nodeId required" };
|
|
77262
|
+
{
|
|
77263
|
+
const meshRecordForForward = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
|
|
77264
|
+
const forwardNode = meshRecordForForward?.mesh?.nodes?.find((n) => meshNodeIdMatches(n, nodeId));
|
|
77265
|
+
const nodeDaemonId = typeof forwardNode?.daemonId === "string" ? forwardNode.daemonId.trim() : void 0;
|
|
77266
|
+
const selfDaemonId = this.deps.statusInstanceId;
|
|
77267
|
+
const isRemote = nodeDaemonId && selfDaemonId && nodeDaemonId !== selfDaemonId;
|
|
77268
|
+
if (isRemote && this.deps.dispatchMeshCommand && !args?._meshDirectDispatch) {
|
|
77269
|
+
const callerCoordinatorDaemonId = typeof args?.coordinatorDaemonId === "string" && args.coordinatorDaemonId.trim() ? args.coordinatorDaemonId.trim() : void 0;
|
|
77270
|
+
const forwarded = await this.deps.dispatchMeshCommand(nodeDaemonId, "refine_mesh_node", {
|
|
77271
|
+
...typeof args === "object" && args !== null ? args : {},
|
|
77272
|
+
coordinatorDaemonId: callerCoordinatorDaemonId || selfDaemonId,
|
|
77273
|
+
_meshDirectDispatch: true
|
|
77274
|
+
});
|
|
77275
|
+
return forwarded ?? { success: false, error: "no response from remote node" };
|
|
77276
|
+
}
|
|
77277
|
+
}
|
|
76814
77278
|
const isDryRun = args?.dryRun !== false && args?.execute !== true;
|
|
76815
77279
|
if (isDryRun) {
|
|
76816
77280
|
const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
|
|
@@ -77783,6 +78247,7 @@ ${ptyResult.output.slice(-2e3)}`);
|
|
|
77783
78247
|
const liveMeshSessions = partitionSessionHostRecords(Array.isArray(sessionHostRecords) ? sessionHostRecords : []).liveRuntimes;
|
|
77784
78248
|
const localMachineId = loadConfig2().machineId || "";
|
|
77785
78249
|
const requireDirectPeerTruth = args?.requireDirectPeerTruth === true;
|
|
78250
|
+
const meshGitProbeCache = this.meshGitProbeCache;
|
|
77786
78251
|
const directTruth = requireDirectPeerTruth ? await hydrateInlineMeshDirectTruth({
|
|
77787
78252
|
mesh,
|
|
77788
78253
|
meshSource: meshRecord.source,
|
|
@@ -77793,7 +78258,8 @@ ${ptyResult.output.slice(-2e3)}`);
|
|
|
77793
78258
|
// Standing-state model: only an explicit refresh fans
|
|
77794
78259
|
// out a blocking peer git probe. Default loads return
|
|
77795
78260
|
// held truth so one slow peer can't block the graph.
|
|
77796
|
-
probeRemotePeers: refreshRequested
|
|
78261
|
+
probeRemotePeers: refreshRequested,
|
|
78262
|
+
probeCache: meshGitProbeCache
|
|
77797
78263
|
}) : {
|
|
77798
78264
|
directEvidenceCount: 0,
|
|
77799
78265
|
localConfirmedCount: 0,
|
|
@@ -77954,7 +78420,7 @@ ${ptyResult.output.slice(-2e3)}`);
|
|
|
77954
78420
|
}
|
|
77955
78421
|
remoteProbeApplied = true;
|
|
77956
78422
|
} else if (refreshRequested && !isSelfNode && daemonId && this.deps.dispatchMeshCommand && !directTruthUnavailableNodeIds.has(nodeId)) {
|
|
77957
|
-
const
|
|
78423
|
+
const runNodeProbe = () => probeRemoteMeshGitStatusWithRetry({
|
|
77958
78424
|
dispatchMeshCommand: this.deps.dispatchMeshCommand,
|
|
77959
78425
|
daemonId,
|
|
77960
78426
|
workspace,
|
|
@@ -77965,6 +78431,7 @@ ${ptyResult.output.slice(-2e3)}`);
|
|
|
77965
78431
|
status.connection = connection;
|
|
77966
78432
|
}
|
|
77967
78433
|
});
|
|
78434
|
+
const remoteGit = await meshGitProbeCache.probe(daemonId, workspace, runNodeProbe);
|
|
77968
78435
|
if (remoteGit) {
|
|
77969
78436
|
status.git = remoteGit;
|
|
77970
78437
|
status.health = remoteGit.isGitRepo ? deriveMeshNodeHealthFromGit(remoteGit) : "degraded";
|