squadrant 0.19.1 → 0.19.2
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 +498 -120
- package/dist/index.js.map +1 -1
- package/dist/squadrantd.js +525 -100
- package/dist/squadrantd.js.map +1 -1
- package/package.json +1 -1
- package/scripts/control-event-table.mjs +227 -0
package/dist/squadrantd.js
CHANGED
|
@@ -199,8 +199,8 @@ function deepMerge(base, patch) {
|
|
|
199
199
|
if (patch === null || typeof patch !== "object" || Array.isArray(patch))
|
|
200
200
|
return patch ?? base;
|
|
201
201
|
const out = { ...base };
|
|
202
|
-
for (const [k,
|
|
203
|
-
out[k] = deepMerge(out[k],
|
|
202
|
+
for (const [k, v2] of Object.entries(patch)) {
|
|
203
|
+
out[k] = deepMerge(out[k], v2);
|
|
204
204
|
}
|
|
205
205
|
return out;
|
|
206
206
|
}
|
|
@@ -676,8 +676,8 @@ var init_runtime_sync = __esm({
|
|
|
676
676
|
});
|
|
677
677
|
|
|
678
678
|
// packages/shared/dist/lib/tool-compat.js
|
|
679
|
-
function parseSemVer(
|
|
680
|
-
const m =
|
|
679
|
+
function parseSemVer(v2) {
|
|
680
|
+
const m = v2.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
681
681
|
if (!m)
|
|
682
682
|
return null;
|
|
683
683
|
return [parseInt(m[1], 10), parseInt(m[2], 10), parseInt(m[3], 10)];
|
|
@@ -1823,12 +1823,12 @@ function isDaemonSocketLive(sockPath, timeoutMs = 500) {
|
|
|
1823
1823
|
return;
|
|
1824
1824
|
}
|
|
1825
1825
|
const conn = createConnection(sockPath);
|
|
1826
|
-
const finish = (
|
|
1826
|
+
const finish = (v2) => {
|
|
1827
1827
|
try {
|
|
1828
1828
|
conn.destroy();
|
|
1829
1829
|
} catch {
|
|
1830
1830
|
}
|
|
1831
|
-
resolve4(
|
|
1831
|
+
resolve4(v2);
|
|
1832
1832
|
};
|
|
1833
1833
|
const timer = setTimeout(() => finish(false), timeoutMs);
|
|
1834
1834
|
conn.on("connect", () => {
|
|
@@ -2494,17 +2494,20 @@ function getDaemonPid(target) {
|
|
|
2494
2494
|
function forceKickstartAndVerify(target, opts = {}) {
|
|
2495
2495
|
const pollAttempts = opts.pollAttempts ?? 15;
|
|
2496
2496
|
const pollDelayMs = opts.pollDelayMs ?? 300;
|
|
2497
|
-
const kickstartRetries = opts.kickstartRetries ??
|
|
2497
|
+
const kickstartRetries = opts.kickstartRetries ?? 10;
|
|
2498
2498
|
const kickstartRetryDelayMs = opts.kickstartRetryDelayMs ?? 300;
|
|
2499
2499
|
const pidBefore = getDaemonPid(target);
|
|
2500
|
+
let kickstartError = null;
|
|
2500
2501
|
for (let i = 0; i < kickstartRetries; i++) {
|
|
2501
2502
|
try {
|
|
2502
2503
|
execFileSync3("launchctl", ["kickstart", "-k", target], { stdio: "ignore" });
|
|
2504
|
+
kickstartError = null;
|
|
2503
2505
|
break;
|
|
2504
2506
|
} catch (e) {
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2507
|
+
kickstartError = e;
|
|
2508
|
+
if (i < kickstartRetries - 1) {
|
|
2509
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, kickstartRetryDelayMs);
|
|
2510
|
+
}
|
|
2508
2511
|
}
|
|
2509
2512
|
}
|
|
2510
2513
|
let pidAfter = null;
|
|
@@ -2516,7 +2519,16 @@ function forceKickstartAndVerify(target, opts = {}) {
|
|
|
2516
2519
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, pollDelayMs);
|
|
2517
2520
|
}
|
|
2518
2521
|
}
|
|
2519
|
-
|
|
2522
|
+
const restarted = pidAfter !== null && pidAfter !== pidBefore;
|
|
2523
|
+
if (kickstartError && !restarted)
|
|
2524
|
+
throw kickstartError;
|
|
2525
|
+
return {
|
|
2526
|
+
target,
|
|
2527
|
+
pidBefore,
|
|
2528
|
+
pidAfter,
|
|
2529
|
+
restarted,
|
|
2530
|
+
...kickstartError ? { note: "kickstart -k refused; daemon restarted by bootstrap" } : {}
|
|
2531
|
+
};
|
|
2520
2532
|
}
|
|
2521
2533
|
function isOperatorInitiatedCommand(topLevelArg) {
|
|
2522
2534
|
return topLevelArg !== void 0 && OPERATOR_INITIATED_COMMANDS.has(topLevelArg);
|
|
@@ -3425,6 +3437,33 @@ import fs9 from "fs";
|
|
|
3425
3437
|
import os4 from "os";
|
|
3426
3438
|
import path9 from "path";
|
|
3427
3439
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
3440
|
+
async function pollFirstTurnConfirmedAt(getTaskRecord, project, id) {
|
|
3441
|
+
const deadline = Date.now() + FIRST_TURN_HOOK_CONFIRM_WINDOW_MS;
|
|
3442
|
+
for (; ; ) {
|
|
3443
|
+
const rec = await getTaskRecord(project, id).catch(() => void 0);
|
|
3444
|
+
if (rec?.firstTurnConfirmedAt)
|
|
3445
|
+
return true;
|
|
3446
|
+
if (Date.now() >= deadline)
|
|
3447
|
+
return false;
|
|
3448
|
+
await new Promise((r) => setTimeout(r, FIRST_TURN_HOOK_POLL_INTERVAL_MS));
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
3451
|
+
function firstTrueOrBothFalse(a, b) {
|
|
3452
|
+
return new Promise((resolve4) => {
|
|
3453
|
+
let settledFalseCount = 0;
|
|
3454
|
+
const onSettle = (ok2) => {
|
|
3455
|
+
if (ok2) {
|
|
3456
|
+
resolve4(true);
|
|
3457
|
+
return;
|
|
3458
|
+
}
|
|
3459
|
+
settledFalseCount++;
|
|
3460
|
+
if (settledFalseCount === 2)
|
|
3461
|
+
resolve4(false);
|
|
3462
|
+
};
|
|
3463
|
+
a.then(onSettle, () => onSettle(false));
|
|
3464
|
+
b.then(onSettle, () => onSettle(false));
|
|
3465
|
+
});
|
|
3466
|
+
}
|
|
3428
3467
|
async function listCrewPanes(runtime, workspaceId, project) {
|
|
3429
3468
|
const surfaces = await runtime.listSurfaces(workspaceId);
|
|
3430
3469
|
return surfaces.filter((s) => s.title && isCrewTitle(project, s.title));
|
|
@@ -3581,10 +3620,12 @@ async function runCrewSpawn(input, config, deps) {
|
|
|
3581
3620
|
fs9.writeFileSync(spillFile, claudeFirstTurn, "utf8");
|
|
3582
3621
|
claudeFirstTurn = `Full task is at ${spillFile} \u2014 cat it and follow it exactly.`;
|
|
3583
3622
|
}
|
|
3584
|
-
const
|
|
3623
|
+
const sendPromise = deps.sendFirstTurn(pane2, `${claudeFirstTurn}
|
|
3585
3624
|
|
|
3586
3625
|
${buildCompletionProtocol(rec.id, input.project)}`, preLaunchScreen);
|
|
3587
|
-
|
|
3626
|
+
const scrapeDelivered = sendPromise.then((r) => r.delivered).catch(() => false);
|
|
3627
|
+
const delivered = hooksInstalled && deps.getTaskRecord ? await firstTrueOrBothFalse(scrapeDelivered, pollFirstTurnConfirmedAt(deps.getTaskRecord, input.project, rec.id)) : await scrapeDelivered;
|
|
3628
|
+
if (!delivered) {
|
|
3588
3629
|
process.stderr.write(`\u26A0\uFE0F First turn not delivered for crew '${name}' \u2014 use 'squadrant crew send ${input.project} ${name}' to re-send the task.
|
|
3589
3630
|
`);
|
|
3590
3631
|
} else if (!hooksInstalled) {
|
|
@@ -3695,6 +3736,10 @@ async function runCrewSend(project, name, message, runtime, workspaceId, deps, o
|
|
|
3695
3736
|
const heldForMin = Math.round((Date.now() - task.operatorHold.since) / 6e4);
|
|
3696
3737
|
throw new Error(`Crew '${name}' is under operator takeover (held ${heldForMin}m${task.operatorHold.note ? `: ${task.operatorHold.note}` : ""}). The operator is working in that tab \u2014 sending a message disrupts their conversation. Ask them to run 'squadrant crew handback ${project} ${name}', or pass --force if they told you to.`);
|
|
3697
3738
|
}
|
|
3739
|
+
const isAttentionState = task?.state === "blocked" || task?.state === "awaiting-input" || task?.state === "review";
|
|
3740
|
+
if (task && !isAttentionState && task.firstTurnConfirmedAt && task.task === message) {
|
|
3741
|
+
throw new Error(`Crew '${name}' already confirmed receipt of this task \u2014 its first turn was delivered and is not being re-sent to avoid running it twice. If you have new instructions, send different text.`);
|
|
3742
|
+
}
|
|
3698
3743
|
let reopened = false;
|
|
3699
3744
|
try {
|
|
3700
3745
|
if (task) {
|
|
@@ -3861,7 +3906,7 @@ async function runCrewList(project, runtime, workspaceId) {
|
|
|
3861
3906
|
surfaceId: c.surfaceId
|
|
3862
3907
|
}));
|
|
3863
3908
|
}
|
|
3864
|
-
var CC_SOCKS_DIR, FIRST_TURN_INLINE_MAX_BYTES, TEMPLATES_DIR, STATE_ROOT, CLOSE_LOOKUP_RETRIES, CLOSE_LOOKUP_RETRY_DELAY_MS;
|
|
3909
|
+
var CC_SOCKS_DIR, FIRST_TURN_INLINE_MAX_BYTES, TEMPLATES_DIR, STATE_ROOT, FIRST_TURN_HOOK_CONFIRM_WINDOW_MS, FIRST_TURN_HOOK_POLL_INTERVAL_MS, CLOSE_LOOKUP_RETRIES, CLOSE_LOOKUP_RETRY_DELAY_MS;
|
|
3865
3910
|
var init_crew_spawn = __esm({
|
|
3866
3911
|
"packages/core/dist/crew-spawn.js"() {
|
|
3867
3912
|
init_control_channel();
|
|
@@ -3873,6 +3918,8 @@ var init_crew_spawn = __esm({
|
|
|
3873
3918
|
FIRST_TURN_INLINE_MAX_BYTES = 1200;
|
|
3874
3919
|
TEMPLATES_DIR = path9.join(os4.homedir(), ".config", "squadrant", "templates");
|
|
3875
3920
|
STATE_ROOT = path9.join(os4.homedir(), ".config", "squadrant", "state");
|
|
3921
|
+
FIRST_TURN_HOOK_CONFIRM_WINDOW_MS = 1e5;
|
|
3922
|
+
FIRST_TURN_HOOK_POLL_INTERVAL_MS = 2e3;
|
|
3876
3923
|
CLOSE_LOOKUP_RETRIES = 3;
|
|
3877
3924
|
CLOSE_LOOKUP_RETRY_DELAY_MS = 150;
|
|
3878
3925
|
}
|
|
@@ -4042,9 +4089,9 @@ function createDelivery(ctx, daemonCmux, isSurfaceAlive) {
|
|
|
4042
4089
|
const lastDeferred = /* @__PURE__ */ new Map();
|
|
4043
4090
|
const inFlightDelivery = () => {
|
|
4044
4091
|
let worst = null;
|
|
4045
|
-
for (const [project,
|
|
4046
|
-
if (!worst ||
|
|
4047
|
-
worst = { project, ...
|
|
4092
|
+
for (const [project, v2] of lastDeferred) {
|
|
4093
|
+
if (!worst || v2.deferCount > worst.deferCount)
|
|
4094
|
+
worst = { project, ...v2 };
|
|
4048
4095
|
}
|
|
4049
4096
|
return worst;
|
|
4050
4097
|
};
|
|
@@ -6408,6 +6455,326 @@ var init_lifecycle_source = __esm({
|
|
|
6408
6455
|
}
|
|
6409
6456
|
});
|
|
6410
6457
|
|
|
6458
|
+
// packages/core/dist/events/fact.js
|
|
6459
|
+
function stampFact(raw, id) {
|
|
6460
|
+
return { ...raw, ...id };
|
|
6461
|
+
}
|
|
6462
|
+
var init_fact = __esm({
|
|
6463
|
+
"packages/core/dist/events/fact.js"() {
|
|
6464
|
+
}
|
|
6465
|
+
});
|
|
6466
|
+
|
|
6467
|
+
// packages/core/dist/events/log.js
|
|
6468
|
+
var FactLog;
|
|
6469
|
+
var init_log = __esm({
|
|
6470
|
+
"packages/core/dist/events/log.js"() {
|
|
6471
|
+
FactLog = class {
|
|
6472
|
+
capacity;
|
|
6473
|
+
buffers = /* @__PURE__ */ new Map();
|
|
6474
|
+
constructor(opts = {}) {
|
|
6475
|
+
this.capacity = opts.capacity ?? 256;
|
|
6476
|
+
}
|
|
6477
|
+
push(fact) {
|
|
6478
|
+
let buf = this.buffers.get(fact.taskId);
|
|
6479
|
+
if (!buf) {
|
|
6480
|
+
buf = [];
|
|
6481
|
+
this.buffers.set(fact.taskId, buf);
|
|
6482
|
+
}
|
|
6483
|
+
buf.push(fact);
|
|
6484
|
+
while (buf.length > this.capacity)
|
|
6485
|
+
buf.shift();
|
|
6486
|
+
}
|
|
6487
|
+
/** Oldest-first snapshot. A fresh array; later pushes never grow it. */
|
|
6488
|
+
recent(taskId) {
|
|
6489
|
+
return [...this.buffers.get(taskId) ?? []];
|
|
6490
|
+
}
|
|
6491
|
+
/** Newline-delimited JSON, one fact per line, oldest first. */
|
|
6492
|
+
serialize(taskId) {
|
|
6493
|
+
return this.recent(taskId).map((f) => JSON.stringify(f)).join("\n") + "\n";
|
|
6494
|
+
}
|
|
6495
|
+
/** Release a finished crew's buffer. */
|
|
6496
|
+
drop(taskId) {
|
|
6497
|
+
this.buffers.delete(taskId);
|
|
6498
|
+
}
|
|
6499
|
+
};
|
|
6500
|
+
}
|
|
6501
|
+
});
|
|
6502
|
+
|
|
6503
|
+
// packages/core/dist/events/invariant.js
|
|
6504
|
+
function freshTrace() {
|
|
6505
|
+
return {
|
|
6506
|
+
depth: 0,
|
|
6507
|
+
oldestOpenAt: null,
|
|
6508
|
+
stallReported: false,
|
|
6509
|
+
unknownSeen: 0,
|
|
6510
|
+
liveness: /* @__PURE__ */ new Map()
|
|
6511
|
+
};
|
|
6512
|
+
}
|
|
6513
|
+
function checkFact(trace, fact, opts) {
|
|
6514
|
+
const out = [];
|
|
6515
|
+
if (fact.origin === "inferred" && TERMINALISING.has(fact.kind)) {
|
|
6516
|
+
out.push(v("I4", `inferred fact "${fact.kind}" from ${fact.source} cannot terminalise alone`, fact));
|
|
6517
|
+
}
|
|
6518
|
+
switch (fact.kind) {
|
|
6519
|
+
case "tool.opened":
|
|
6520
|
+
if (trace.depth === 0) {
|
|
6521
|
+
trace.oldestOpenAt = fact.at;
|
|
6522
|
+
trace.stallReported = false;
|
|
6523
|
+
}
|
|
6524
|
+
trace.depth += 1;
|
|
6525
|
+
break;
|
|
6526
|
+
case "tool.closed":
|
|
6527
|
+
if (trace.depth === 0) {
|
|
6528
|
+
out.push(v("I1", `tool.closed from ${fact.source} with no open tool`, fact));
|
|
6529
|
+
} else {
|
|
6530
|
+
trace.depth -= 1;
|
|
6531
|
+
if (trace.depth === 0) {
|
|
6532
|
+
trace.oldestOpenAt = null;
|
|
6533
|
+
trace.stallReported = false;
|
|
6534
|
+
}
|
|
6535
|
+
}
|
|
6536
|
+
break;
|
|
6537
|
+
case "turn.ended":
|
|
6538
|
+
if (trace.depth > 0) {
|
|
6539
|
+
out.push(v("I2", `turn.ended with ${trace.depth} tool call(s) still open`, fact));
|
|
6540
|
+
trace.depth = 0;
|
|
6541
|
+
trace.oldestOpenAt = null;
|
|
6542
|
+
trace.stallReported = false;
|
|
6543
|
+
}
|
|
6544
|
+
break;
|
|
6545
|
+
case "unknown":
|
|
6546
|
+
trace.unknownSeen += 1;
|
|
6547
|
+
out.push(v("I5", `unrecognised frame "${fact.name}" from ${fact.source}`, fact));
|
|
6548
|
+
break;
|
|
6549
|
+
case "process.observed": {
|
|
6550
|
+
const prior = [...trace.liveness.entries()].find(([src, s]) => src !== fact.source && s.alive !== fact.alive && fact.at - s.at <= (opts.disagreeWindowMs ?? -1));
|
|
6551
|
+
if (prior) {
|
|
6552
|
+
out.push(v("I6", `liveness disagreement: ${prior[0]} said alive=${prior[1].alive}, ${fact.source} says alive=${fact.alive}`, fact));
|
|
6553
|
+
}
|
|
6554
|
+
trace.liveness.set(fact.source, { alive: fact.alive, at: fact.at });
|
|
6555
|
+
break;
|
|
6556
|
+
}
|
|
6557
|
+
default:
|
|
6558
|
+
break;
|
|
6559
|
+
}
|
|
6560
|
+
if (opts.stallBudgetMs !== void 0 && trace.depth > 0 && trace.oldestOpenAt !== null && !trace.stallReported && fact.at - trace.oldestOpenAt > opts.stallBudgetMs) {
|
|
6561
|
+
trace.stallReported = true;
|
|
6562
|
+
out.push(v("I3", `tool open for ${fact.at - trace.oldestOpenAt}ms, past the stall budget`, fact));
|
|
6563
|
+
}
|
|
6564
|
+
return out;
|
|
6565
|
+
}
|
|
6566
|
+
var v, TERMINALISING;
|
|
6567
|
+
var init_invariant = __esm({
|
|
6568
|
+
"packages/core/dist/events/invariant.js"() {
|
|
6569
|
+
v = (code, message, f) => ({ code, message, taskId: f.taskId, at: f.at });
|
|
6570
|
+
TERMINALISING = /* @__PURE__ */ new Set(["session.ended"]);
|
|
6571
|
+
}
|
|
6572
|
+
});
|
|
6573
|
+
|
|
6574
|
+
// packages/core/dist/events/to-control-event.js
|
|
6575
|
+
function toControlEvent(fact) {
|
|
6576
|
+
if (fact.origin === "inferred" && TERMINALISING2.has(fact.kind))
|
|
6577
|
+
return [];
|
|
6578
|
+
switch (fact.kind) {
|
|
6579
|
+
case "turn.ended":
|
|
6580
|
+
return [{ type: "task.turn.completed", id: fact.taskId, turnId: fact.turnId ?? fact.source }];
|
|
6581
|
+
case "permission.requested":
|
|
6582
|
+
return [{
|
|
6583
|
+
type: "task.approval.requested",
|
|
6584
|
+
id: fact.taskId,
|
|
6585
|
+
requestId: fact.requestId,
|
|
6586
|
+
question: fact.question,
|
|
6587
|
+
kind: fact.tool
|
|
6588
|
+
}];
|
|
6589
|
+
case "input.requested":
|
|
6590
|
+
return [{
|
|
6591
|
+
type: "task.input.requested",
|
|
6592
|
+
id: fact.taskId,
|
|
6593
|
+
requestId: fact.requestId,
|
|
6594
|
+
question: fact.question
|
|
6595
|
+
}];
|
|
6596
|
+
case "session.ended":
|
|
6597
|
+
return [{ type: "task.session.ended", id: fact.taskId }];
|
|
6598
|
+
case "session.started":
|
|
6599
|
+
return [{
|
|
6600
|
+
type: "task.started",
|
|
6601
|
+
id: fact.taskId,
|
|
6602
|
+
...fact.pid === void 0 ? {} : { pid: fact.pid },
|
|
6603
|
+
...fact.sessionId === void 0 ? {} : { sessionId: fact.sessionId }
|
|
6604
|
+
}];
|
|
6605
|
+
case "prompt.submitted":
|
|
6606
|
+
return [{ type: "task.first-turn.confirmed", id: fact.taskId }];
|
|
6607
|
+
// Liveness-only. The facade still feeds these to reduceLifecycle; they
|
|
6608
|
+
// simply carry no ControlEvent of their own.
|
|
6609
|
+
case "tool.opened":
|
|
6610
|
+
case "tool.closed":
|
|
6611
|
+
case "activity":
|
|
6612
|
+
case "process.observed":
|
|
6613
|
+
case "unknown":
|
|
6614
|
+
return [];
|
|
6615
|
+
}
|
|
6616
|
+
}
|
|
6617
|
+
var TERMINALISING2;
|
|
6618
|
+
var init_to_control_event = __esm({
|
|
6619
|
+
"packages/core/dist/events/to-control-event.js"() {
|
|
6620
|
+
TERMINALISING2 = /* @__PURE__ */ new Set(["session.ended"]);
|
|
6621
|
+
}
|
|
6622
|
+
});
|
|
6623
|
+
|
|
6624
|
+
// packages/core/dist/events/conformance.js
|
|
6625
|
+
function assert(cond, msg) {
|
|
6626
|
+
if (!cond)
|
|
6627
|
+
throw new Error(`conformance: ${msg}`);
|
|
6628
|
+
}
|
|
6629
|
+
function runAdapterConformance(adapter, samples) {
|
|
6630
|
+
const call = (raw) => adapter.translate(raw);
|
|
6631
|
+
return [
|
|
6632
|
+
{
|
|
6633
|
+
name: `${adapter.name}: never throws on garbage`,
|
|
6634
|
+
run: () => {
|
|
6635
|
+
for (const g of GARBAGE) {
|
|
6636
|
+
try {
|
|
6637
|
+
call(g);
|
|
6638
|
+
} catch (e) {
|
|
6639
|
+
throw new Error(`threw on ${JSON.stringify(g)}: ${String(e)}`);
|
|
6640
|
+
}
|
|
6641
|
+
}
|
|
6642
|
+
}
|
|
6643
|
+
},
|
|
6644
|
+
{
|
|
6645
|
+
name: `${adapter.name}: never returns null or undefined`,
|
|
6646
|
+
run: () => {
|
|
6647
|
+
for (const g of [...GARBAGE, ...samples]) {
|
|
6648
|
+
const out = call(g);
|
|
6649
|
+
assert(Array.isArray(out), `returned a non-array for ${JSON.stringify(g)}`);
|
|
6650
|
+
}
|
|
6651
|
+
}
|
|
6652
|
+
},
|
|
6653
|
+
{
|
|
6654
|
+
name: `${adapter.name}: an unrecognised frame yields unknown, not an empty array`,
|
|
6655
|
+
run: () => {
|
|
6656
|
+
const out = call({ type: "definitely-not-a-real-event-name" });
|
|
6657
|
+
assert(out.length > 0, "silently dropped an unrecognised frame (the #542 shape)");
|
|
6658
|
+
assert(out.every((f) => f.kind === "unknown"), "an unrecognised frame must translate to kind 'unknown'");
|
|
6659
|
+
}
|
|
6660
|
+
},
|
|
6661
|
+
{
|
|
6662
|
+
name: `${adapter.name}: recognises its own samples`,
|
|
6663
|
+
run: () => {
|
|
6664
|
+
for (const s of samples) {
|
|
6665
|
+
const out = call(s);
|
|
6666
|
+
assert(out.length > 0, `produced nothing for its own sample ${JSON.stringify(s)}`);
|
|
6667
|
+
assert(out.some((f) => f.kind !== "unknown"), `failed to recognise its own sample ${JSON.stringify(s)}`);
|
|
6668
|
+
}
|
|
6669
|
+
}
|
|
6670
|
+
},
|
|
6671
|
+
{
|
|
6672
|
+
name: `${adapter.name}: declares a constant origin`,
|
|
6673
|
+
run: () => {
|
|
6674
|
+
assert(adapter.origin === "agent" || adapter.origin === "scan" || adapter.origin === "inferred", `invalid origin "${String(adapter.origin)}"`);
|
|
6675
|
+
}
|
|
6676
|
+
}
|
|
6677
|
+
];
|
|
6678
|
+
}
|
|
6679
|
+
var GARBAGE;
|
|
6680
|
+
var init_conformance = __esm({
|
|
6681
|
+
"packages/core/dist/events/conformance.js"() {
|
|
6682
|
+
GARBAGE = [
|
|
6683
|
+
null,
|
|
6684
|
+
void 0,
|
|
6685
|
+
0,
|
|
6686
|
+
"",
|
|
6687
|
+
"not json",
|
|
6688
|
+
[],
|
|
6689
|
+
{},
|
|
6690
|
+
{ type: 42 },
|
|
6691
|
+
{ type: "definitely-not-a-real-event-name" }
|
|
6692
|
+
];
|
|
6693
|
+
}
|
|
6694
|
+
});
|
|
6695
|
+
|
|
6696
|
+
// packages/core/dist/events/source.js
|
|
6697
|
+
function createEventsSource(opts) {
|
|
6698
|
+
const now = opts.now ?? (() => Date.now());
|
|
6699
|
+
const log = new FactLog({ capacity: opts.capacity });
|
|
6700
|
+
const adapters = new Map(opts.adapters.map((a) => [a.name, a]));
|
|
6701
|
+
const traces = /* @__PURE__ */ new Map();
|
|
6702
|
+
const seqs = /* @__PURE__ */ new Map();
|
|
6703
|
+
let deps;
|
|
6704
|
+
const traceFor = (taskId) => {
|
|
6705
|
+
let t = traces.get(taskId);
|
|
6706
|
+
if (!t) {
|
|
6707
|
+
t = freshTrace();
|
|
6708
|
+
traces.set(taskId, t);
|
|
6709
|
+
}
|
|
6710
|
+
return t;
|
|
6711
|
+
};
|
|
6712
|
+
const nextSeq = (taskId) => {
|
|
6713
|
+
const n = seqs.get(taskId) ?? 0;
|
|
6714
|
+
seqs.set(taskId, n + 1);
|
|
6715
|
+
return n;
|
|
6716
|
+
};
|
|
6717
|
+
return {
|
|
6718
|
+
name: "events",
|
|
6719
|
+
start(d) {
|
|
6720
|
+
deps = d;
|
|
6721
|
+
},
|
|
6722
|
+
stop() {
|
|
6723
|
+
deps = void 0;
|
|
6724
|
+
},
|
|
6725
|
+
health() {
|
|
6726
|
+
return { active: deps !== void 0, error: null };
|
|
6727
|
+
},
|
|
6728
|
+
recent(taskId) {
|
|
6729
|
+
return log.recent(taskId);
|
|
6730
|
+
},
|
|
6731
|
+
dump(taskId) {
|
|
6732
|
+
return log.serialize(taskId);
|
|
6733
|
+
},
|
|
6734
|
+
ingest(source, raw, hint) {
|
|
6735
|
+
const adapter = adapters.get(source);
|
|
6736
|
+
if (!adapter || !deps)
|
|
6737
|
+
return;
|
|
6738
|
+
const rec = deps.resolve(hint);
|
|
6739
|
+
if (!rec)
|
|
6740
|
+
return;
|
|
6741
|
+
const taskId = rec.id;
|
|
6742
|
+
const at = now();
|
|
6743
|
+
let produced;
|
|
6744
|
+
try {
|
|
6745
|
+
const out = adapter.translate(raw);
|
|
6746
|
+
produced = Array.isArray(out) ? out : [{ kind: "unknown", name: `${source} returned non-array` }];
|
|
6747
|
+
} catch (e) {
|
|
6748
|
+
opts.log?.(`events: adapter ${source} threw: ${String(e)}`);
|
|
6749
|
+
produced = [{ kind: "unknown", name: `${source} threw` }];
|
|
6750
|
+
}
|
|
6751
|
+
for (const rawFact of produced) {
|
|
6752
|
+
const fact = stampFact(rawFact, {
|
|
6753
|
+
seq: nextSeq(taskId),
|
|
6754
|
+
taskId,
|
|
6755
|
+
at,
|
|
6756
|
+
source,
|
|
6757
|
+
origin: adapter.origin
|
|
6758
|
+
});
|
|
6759
|
+
log.push(fact);
|
|
6760
|
+
for (const v2 of checkFact(traceFor(taskId), fact, opts.check ?? {})) {
|
|
6761
|
+
opts.onViolation(v2);
|
|
6762
|
+
}
|
|
6763
|
+
for (const ev of toControlEvent(fact))
|
|
6764
|
+
opts.emit(ev);
|
|
6765
|
+
}
|
|
6766
|
+
}
|
|
6767
|
+
};
|
|
6768
|
+
}
|
|
6769
|
+
var init_source = __esm({
|
|
6770
|
+
"packages/core/dist/events/source.js"() {
|
|
6771
|
+
init_fact();
|
|
6772
|
+
init_log();
|
|
6773
|
+
init_invariant();
|
|
6774
|
+
init_to_control_event();
|
|
6775
|
+
}
|
|
6776
|
+
});
|
|
6777
|
+
|
|
6411
6778
|
// packages/core/dist/index.js
|
|
6412
6779
|
var dist_exports = {};
|
|
6413
6780
|
__export(dist_exports, {
|
|
@@ -6422,6 +6789,7 @@ __export(dist_exports, {
|
|
|
6422
6789
|
DEFAULT_TASK_TIMEOUT_MS: () => DEFAULT_TASK_TIMEOUT_MS,
|
|
6423
6790
|
DeferDelivery: () => DeferDelivery,
|
|
6424
6791
|
FIRST_TURN_INLINE_MAX_BYTES: () => FIRST_TURN_INLINE_MAX_BYTES,
|
|
6792
|
+
FactLog: () => FactLog,
|
|
6425
6793
|
GROUP_DISPATCH_WARMUP_POLL_MS: () => GROUP_DISPATCH_WARMUP_POLL_MS,
|
|
6426
6794
|
GROUP_DISPATCH_WARMUP_TIMEOUT_MS: () => GROUP_DISPATCH_WARMUP_TIMEOUT_MS,
|
|
6427
6795
|
IDLE_DEBOUNCE_MS: () => IDLE_DEBOUNCE_MS,
|
|
@@ -6451,6 +6819,7 @@ __export(dist_exports, {
|
|
|
6451
6819
|
capAllowed: () => capAllowed,
|
|
6452
6820
|
capOutput: () => capOutput,
|
|
6453
6821
|
captainSocketPath: () => captainSocketPath,
|
|
6822
|
+
checkFact: () => checkFact,
|
|
6454
6823
|
classifyHealth: () => classifyHealth,
|
|
6455
6824
|
closeWorkItem: () => closeWorkItem,
|
|
6456
6825
|
computeTemplateHash: () => computeTemplateHash,
|
|
@@ -6463,6 +6832,7 @@ __export(dist_exports, {
|
|
|
6463
6832
|
createDirectCrewPaneReader: () => createDirectCrewPaneReader,
|
|
6464
6833
|
createDirectSurfaceLivenessProbe: () => createDirectSurfaceLivenessProbe,
|
|
6465
6834
|
createEnsureCaptainAlive: () => createEnsureCaptainAlive,
|
|
6835
|
+
createEventsSource: () => createEventsSource,
|
|
6466
6836
|
createInteractiveProbe: () => createInteractiveProbe,
|
|
6467
6837
|
createIsCaptainAlive: () => createIsCaptainAlive,
|
|
6468
6838
|
createLaunch: () => createLaunch,
|
|
@@ -6505,6 +6875,7 @@ __export(dist_exports, {
|
|
|
6505
6875
|
formatInbound: () => formatInbound,
|
|
6506
6876
|
formatInboundReceipt: () => formatInboundReceipt,
|
|
6507
6877
|
formatLifecycle: () => formatLifecycle,
|
|
6878
|
+
freshTrace: () => freshTrace,
|
|
6508
6879
|
getDaemonPid: () => getDaemonPid,
|
|
6509
6880
|
healCmdFor: () => healCmdFor,
|
|
6510
6881
|
isAuthorized: () => isAuthorized,
|
|
@@ -6561,6 +6932,7 @@ __export(dist_exports, {
|
|
|
6561
6932
|
resolveSetupUserId: () => resolveSetupUserId,
|
|
6562
6933
|
restartDaemonIfRunning: () => restartDaemonIfRunning,
|
|
6563
6934
|
rotateIfNeeded: () => rotateIfNeeded,
|
|
6935
|
+
runAdapterConformance: () => runAdapterConformance,
|
|
6564
6936
|
runCrewAnswer: () => runCrewAnswer,
|
|
6565
6937
|
runCrewClose: () => runCrewClose,
|
|
6566
6938
|
runCrewList: () => runCrewList,
|
|
@@ -6595,12 +6967,14 @@ __export(dist_exports, {
|
|
|
6595
6967
|
sideNameFromTitle: () => sideNameFromTitle,
|
|
6596
6968
|
sideNextAutoName: () => sideNextAutoName,
|
|
6597
6969
|
sideTitleFor: () => sideTitleFor,
|
|
6970
|
+
stampFact: () => stampFact,
|
|
6598
6971
|
startDaemon: () => startDaemon,
|
|
6599
6972
|
startServer: () => startServer,
|
|
6600
6973
|
stripBotMention: () => stripBotMention,
|
|
6601
6974
|
surfaceVerdict: () => surfaceVerdict,
|
|
6602
6975
|
timeoutGate: () => timeoutGate,
|
|
6603
6976
|
titleFor: () => titleFor,
|
|
6977
|
+
toControlEvent: () => toControlEvent,
|
|
6604
6978
|
topicKey: () => topicKey,
|
|
6605
6979
|
topicName: () => topicName,
|
|
6606
6980
|
tryAcquireDaemonLock: () => tryAcquireDaemonLock,
|
|
@@ -6647,6 +7021,12 @@ var init_dist2 = __esm({
|
|
|
6647
7021
|
init_crew_spawn();
|
|
6648
7022
|
init_crew_answer();
|
|
6649
7023
|
init_lifecycle_source();
|
|
7024
|
+
init_fact();
|
|
7025
|
+
init_log();
|
|
7026
|
+
init_invariant();
|
|
7027
|
+
init_to_control_event();
|
|
7028
|
+
init_conformance();
|
|
7029
|
+
init_source();
|
|
6650
7030
|
init_control_channel();
|
|
6651
7031
|
init_captain_channel();
|
|
6652
7032
|
}
|
|
@@ -6663,6 +7043,7 @@ init_dist2();
|
|
|
6663
7043
|
init_dist2();
|
|
6664
7044
|
init_dist2();
|
|
6665
7045
|
init_dist2();
|
|
7046
|
+
init_dist2();
|
|
6666
7047
|
import { join as join22, dirname as dirname4, resolve as resolve3 } from "path";
|
|
6667
7048
|
import { homedir as homedir13 } from "os";
|
|
6668
7049
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
@@ -7301,9 +7682,9 @@ ${directive}` : directive;
|
|
|
7301
7682
|
function withTimeout(p, ms, msg) {
|
|
7302
7683
|
return new Promise((resolve4, reject) => {
|
|
7303
7684
|
const t = setTimeout(() => reject(new Error(msg)), ms);
|
|
7304
|
-
p.then((
|
|
7685
|
+
p.then((v2) => {
|
|
7305
7686
|
clearTimeout(t);
|
|
7306
|
-
resolve4(
|
|
7687
|
+
resolve4(v2);
|
|
7307
7688
|
}, (e) => {
|
|
7308
7689
|
clearTimeout(t);
|
|
7309
7690
|
reject(e);
|
|
@@ -7312,6 +7693,7 @@ function withTimeout(p, ms, msg) {
|
|
|
7312
7693
|
}
|
|
7313
7694
|
|
|
7314
7695
|
// packages/agents/dist/opencode/sse-bridge.js
|
|
7696
|
+
var IGNORED_FRAME = /^(message|storage|file|lsp|installation)\./;
|
|
7315
7697
|
var OpencodeSseBridge = class {
|
|
7316
7698
|
controllers = /* @__PURE__ */ new Map();
|
|
7317
7699
|
/** taskId → the crew's opencode server port (for permission-reply POSTs). */
|
|
@@ -7448,6 +7830,10 @@ var OpencodeSseBridge = class {
|
|
|
7448
7830
|
return;
|
|
7449
7831
|
}
|
|
7450
7832
|
if (json?.type === "session.idle") {
|
|
7833
|
+
if (this.deps.ingest) {
|
|
7834
|
+
this.deps.ingest(json, taskId);
|
|
7835
|
+
return;
|
|
7836
|
+
}
|
|
7451
7837
|
this.deps.emit({
|
|
7452
7838
|
type: "task.turn.completed",
|
|
7453
7839
|
id: taskId,
|
|
@@ -7457,6 +7843,10 @@ var OpencodeSseBridge = class {
|
|
|
7457
7843
|
const p = json.properties;
|
|
7458
7844
|
if (p?.id && p?.sessionID) {
|
|
7459
7845
|
this.pendingPermByTask.set(taskId, { permID: p.id, sessionID: p.sessionID });
|
|
7846
|
+
if (this.deps.ingest) {
|
|
7847
|
+
this.deps.ingest(json, taskId);
|
|
7848
|
+
return;
|
|
7849
|
+
}
|
|
7460
7850
|
const tool = p.permission ?? "a tool";
|
|
7461
7851
|
const cmd = Array.isArray(p.patterns) && p.patterns.length ? `: ${p.patterns.join(" ")}` : "";
|
|
7462
7852
|
this.deps.emit({
|
|
@@ -7466,11 +7856,24 @@ var OpencodeSseBridge = class {
|
|
|
7466
7856
|
question: `opencode requests permission to run ${tool}${cmd}`,
|
|
7467
7857
|
kind: tool
|
|
7468
7858
|
});
|
|
7859
|
+
} else if (this.deps.ingest) {
|
|
7860
|
+
this.deps.ingest(json, taskId);
|
|
7469
7861
|
}
|
|
7470
7862
|
} else if (json?.type === "permission.replied") {
|
|
7471
7863
|
this.pendingPermByTask.delete(taskId);
|
|
7864
|
+
this.deps.ingest?.(json, taskId);
|
|
7865
|
+
} else if (!IGNORED_FRAME.test(json?.type ?? "")) {
|
|
7866
|
+
this.deps.ingest?.(json, taskId);
|
|
7472
7867
|
}
|
|
7473
7868
|
}
|
|
7869
|
+
/** Test seam: exercise handleLine without an SSE stream. */
|
|
7870
|
+
handleLineForTest(rawLine, taskId) {
|
|
7871
|
+
this.handleLine(taskId, rawLine);
|
|
7872
|
+
}
|
|
7873
|
+
/** Test seam: read pendingPermByTask without exposing it publicly. */
|
|
7874
|
+
pendingPermForTest(taskId) {
|
|
7875
|
+
return this.pendingPermByTask.get(taskId);
|
|
7876
|
+
}
|
|
7474
7877
|
};
|
|
7475
7878
|
|
|
7476
7879
|
// packages/agents/dist/interactive/claude.js
|
|
@@ -8038,70 +8441,41 @@ var ClaudeReceiptListener = class {
|
|
|
8038
8441
|
}
|
|
8039
8442
|
};
|
|
8040
8443
|
|
|
8041
|
-
// packages/agents/dist/opencode/
|
|
8042
|
-
|
|
8043
|
-
|
|
8044
|
-
|
|
8045
|
-
|
|
8046
|
-
|
|
8047
|
-
|
|
8048
|
-
|
|
8049
|
-
|
|
8050
|
-
|
|
8051
|
-
|
|
8052
|
-
|
|
8053
|
-
|
|
8054
|
-
|
|
8055
|
-
|
|
8056
|
-
|
|
8057
|
-
|
|
8058
|
-
|
|
8059
|
-
|
|
8060
|
-
|
|
8061
|
-
|
|
8062
|
-
|
|
8063
|
-
|
|
8064
|
-
|
|
8065
|
-
|
|
8066
|
-
|
|
8067
|
-
|
|
8068
|
-
|
|
8069
|
-
|
|
8070
|
-
|
|
8071
|
-
|
|
8072
|
-
|
|
8073
|
-
return;
|
|
8074
|
-
|
|
8075
|
-
|
|
8076
|
-
return;
|
|
8077
|
-
this.cache.set(snap.taskId, snap);
|
|
8078
|
-
this.deps.report(snap);
|
|
8079
|
-
}
|
|
8080
|
-
};
|
|
8081
|
-
function toSnapshot2(ev) {
|
|
8082
|
-
const now = Date.now();
|
|
8083
|
-
switch (ev.type) {
|
|
8084
|
-
// A permission was answered on the bus and the turn resumed.
|
|
8085
|
-
case "task.started":
|
|
8086
|
-
return { taskId: ev.id, state: "running", alive: true, origin: "agent", at: now };
|
|
8087
|
-
// session.idle — the turn finished. Liveness, NOT completion (anti-#2576).
|
|
8088
|
-
case "task.turn.completed":
|
|
8089
|
-
return { taskId: ev.id, state: "idle", alive: true, origin: "agent", at: now };
|
|
8090
|
-
// permission.asked — opencode STATES it is gated. No guessing from pixels.
|
|
8091
|
-
case "task.approval.requested":
|
|
8092
|
-
return {
|
|
8093
|
-
taskId: ev.id,
|
|
8094
|
-
state: "needsInput",
|
|
8095
|
-
alive: true,
|
|
8096
|
-
origin: "agent",
|
|
8097
|
-
at: now,
|
|
8098
|
-
detail: { note: ev.question, reason: ev.kind }
|
|
8099
|
-
};
|
|
8100
|
-
// Terminal (task.done/blocked/cancelled) and notify-only events are ignored:
|
|
8101
|
-
// terminal state comes exclusively from `squadrant crew signal`.
|
|
8102
|
-
default:
|
|
8103
|
-
return null;
|
|
8104
|
-
}
|
|
8444
|
+
// packages/agents/dist/opencode/fact-adapter.js
|
|
8445
|
+
function createOpencodeFactAdapter(deps) {
|
|
8446
|
+
return {
|
|
8447
|
+
name: "opencode-sse",
|
|
8448
|
+
origin: "agent",
|
|
8449
|
+
translate(raw) {
|
|
8450
|
+
const f = typeof raw === "object" && raw !== null ? raw : {};
|
|
8451
|
+
const type = typeof f.type === "string" ? f.type : void 0;
|
|
8452
|
+
if (type === void 0)
|
|
8453
|
+
return [{ kind: "unknown", name: "non-object" }];
|
|
8454
|
+
const p = f.properties ?? {};
|
|
8455
|
+
if (type === "session.idle") {
|
|
8456
|
+
return [{
|
|
8457
|
+
kind: "turn.ended",
|
|
8458
|
+
turnId: typeof p.sessionID === "string" ? p.sessionID : void 0
|
|
8459
|
+
}];
|
|
8460
|
+
}
|
|
8461
|
+
if (type === "permission.asked") {
|
|
8462
|
+
if (typeof p.id !== "string" || typeof p.sessionID !== "string") {
|
|
8463
|
+
return [{ kind: "unknown", name: "permission.asked:incomplete" }];
|
|
8464
|
+
}
|
|
8465
|
+
const tool = typeof p.permission === "string" ? p.permission : "a tool";
|
|
8466
|
+
const cmd = Array.isArray(p.patterns) && p.patterns.length ? `: ${p.patterns.join(" ")}` : "";
|
|
8467
|
+
return [{
|
|
8468
|
+
kind: "permission.requested",
|
|
8469
|
+
question: `opencode requests permission to run ${tool}${cmd}`,
|
|
8470
|
+
requestId: deps.nextRequestId(),
|
|
8471
|
+
tool
|
|
8472
|
+
}];
|
|
8473
|
+
}
|
|
8474
|
+
if (type === "permission.replied")
|
|
8475
|
+
return [{ kind: "activity" }];
|
|
8476
|
+
return [{ kind: "unknown", name: type }];
|
|
8477
|
+
}
|
|
8478
|
+
};
|
|
8105
8479
|
}
|
|
8106
8480
|
|
|
8107
8481
|
// packages/workspaces/dist/runtimes/cmux.js
|
|
@@ -9716,7 +10090,37 @@ function startSquadrantd(opts = {}) {
|
|
|
9716
10090
|
codexAppServerSource.observe(ev);
|
|
9717
10091
|
}
|
|
9718
10092
|
});
|
|
9719
|
-
const
|
|
10093
|
+
const violationLogState = /* @__PURE__ */ new Map();
|
|
10094
|
+
const VIOLATION_LOG_SUMMARY_MS = 6e4;
|
|
10095
|
+
const onEventsViolation = (v2) => {
|
|
10096
|
+
const key = `${v2.taskId}:${v2.code}`;
|
|
10097
|
+
const prev = violationLogState.get(key);
|
|
10098
|
+
if (!prev) {
|
|
10099
|
+
violationLogState.set(key, { count: 1, loggedAt: Date.now() });
|
|
10100
|
+
log(`[events] ${v2.code} ${v2.taskId}: ${v2.message}`);
|
|
10101
|
+
return;
|
|
10102
|
+
}
|
|
10103
|
+
prev.count++;
|
|
10104
|
+
const now = Date.now();
|
|
10105
|
+
if (now - prev.loggedAt > VIOLATION_LOG_SUMMARY_MS) {
|
|
10106
|
+
log(`[events] ${v2.code} ${v2.taskId}: seen ${prev.count}x since last log (latest: ${v2.message})`);
|
|
10107
|
+
prev.count = 0;
|
|
10108
|
+
prev.loggedAt = now;
|
|
10109
|
+
}
|
|
10110
|
+
};
|
|
10111
|
+
const opencodeRequestIds = { n: 1 };
|
|
10112
|
+
const eventsSource = createEventsSource({
|
|
10113
|
+
adapters: [createOpencodeFactAdapter({ nextRequestId: () => opencodeRequestIds.n++ })],
|
|
10114
|
+
emit: (ev) => {
|
|
10115
|
+
const found = store.listAll().find((r) => r.id === ev.id);
|
|
10116
|
+
if (!found) return;
|
|
10117
|
+
void ctx.d.handle({ kind: "event", project: found.project, event: ev });
|
|
10118
|
+
if (ev.type === "task.approval.requested")
|
|
10119
|
+
ctx.schedulePromotion(ev.id, ev.requestId, "approval", ev.question);
|
|
10120
|
+
},
|
|
10121
|
+
onViolation: onEventsViolation,
|
|
10122
|
+
check: { stallBudgetMs: 6e4, disagreeWindowMs: 5e3 }
|
|
10123
|
+
});
|
|
9720
10124
|
const opencodeBridge = opts.opencodeBridge ?? new OpencodeSseBridge({
|
|
9721
10125
|
emit: (ev) => {
|
|
9722
10126
|
const found = store.listAll().find((r) => r.id === ev.id);
|
|
@@ -9724,8 +10128,8 @@ function startSquadrantd(opts = {}) {
|
|
|
9724
10128
|
void ctx.d.handle({ kind: "event", project: found.project, event: ev });
|
|
9725
10129
|
if (ev.type === "task.approval.requested")
|
|
9726
10130
|
ctx.schedulePromotion(ev.id, ev.requestId, "approval", ev.question);
|
|
9727
|
-
opencodeControlSource.observe(ev);
|
|
9728
10131
|
},
|
|
10132
|
+
ingest: (raw, taskId) => eventsSource.ingest("opencode-sse", raw, { taskId }),
|
|
9729
10133
|
log
|
|
9730
10134
|
});
|
|
9731
10135
|
const cmuxEventsBridge = opts.cmuxEventsBridge ?? new CmuxEventsBridge({
|
|
@@ -9750,11 +10154,11 @@ function startSquadrantd(opts = {}) {
|
|
|
9750
10154
|
ctx.cmuxEventsBridge = cmuxEventsBridge;
|
|
9751
10155
|
const claudePeerRegistrySource = new ClaudePeerRegistrySource({ log });
|
|
9752
10156
|
ctx.lifecycleSources = [
|
|
10157
|
+
eventsSource,
|
|
9753
10158
|
cmuxStoreSource,
|
|
9754
10159
|
nativeHookSource,
|
|
9755
10160
|
codexAppServerSource,
|
|
9756
|
-
claudePeerRegistrySource
|
|
9757
|
-
opencodeControlSource
|
|
10161
|
+
claudePeerRegistrySource
|
|
9758
10162
|
];
|
|
9759
10163
|
const tgCfg = loadConfig().telegram;
|
|
9760
10164
|
ctx.telegramBridge = opts.telegramBridge ?? (tgCfg && !process.env.VITEST ? buildTelegramBridge(
|
|
@@ -9810,6 +10214,37 @@ ${buildCompletionProtocol(rec.id, rec.project)}`;
|
|
|
9810
10214
|
}
|
|
9811
10215
|
});
|
|
9812
10216
|
const h = startDaemon(ctx, { ...opts, launchHeadless }, PKG_VERSION);
|
|
10217
|
+
const startEventsLifecycleSource = () => {
|
|
10218
|
+
let eventsTaskIndex;
|
|
10219
|
+
let eventsTaskIndexAt = 0;
|
|
10220
|
+
const EVENTS_TASK_INDEX_TTL_MS = 500;
|
|
10221
|
+
const eventsSourceDeps = {
|
|
10222
|
+
resolve: (hint) => {
|
|
10223
|
+
if (!hint.taskId) return void 0;
|
|
10224
|
+
const now = Date.now();
|
|
10225
|
+
if (!eventsTaskIndex || now - eventsTaskIndexAt > EVENTS_TASK_INDEX_TTL_MS) {
|
|
10226
|
+
eventsTaskIndex = /* @__PURE__ */ new Map();
|
|
10227
|
+
for (const r of store.listAll()) {
|
|
10228
|
+
if (!TERMINAL_STATES.has(r.state)) eventsTaskIndex.set(r.id, { id: r.id });
|
|
10229
|
+
}
|
|
10230
|
+
eventsTaskIndexAt = now;
|
|
10231
|
+
}
|
|
10232
|
+
const cached = eventsTaskIndex.get(hint.taskId);
|
|
10233
|
+
if (cached) return cached;
|
|
10234
|
+
return store.listAll().find(
|
|
10235
|
+
(r) => r.id === hint.taskId && !TERMINAL_STATES.has(r.state)
|
|
10236
|
+
);
|
|
10237
|
+
},
|
|
10238
|
+
report: () => {
|
|
10239
|
+
},
|
|
10240
|
+
log
|
|
10241
|
+
};
|
|
10242
|
+
try {
|
|
10243
|
+
eventsSource.start(eventsSourceDeps);
|
|
10244
|
+
} catch (e) {
|
|
10245
|
+
log(`events source start failed: ${e.message}`);
|
|
10246
|
+
}
|
|
10247
|
+
};
|
|
9813
10248
|
if (!process.env.VITEST) {
|
|
9814
10249
|
const prevSnaps = /* @__PURE__ */ new Map();
|
|
9815
10250
|
const storeDeps = {
|
|
@@ -9936,19 +10371,9 @@ ${buildCompletionProtocol(rec.id, rec.project)}`;
|
|
|
9936
10371
|
} catch (e) {
|
|
9937
10372
|
log(`claude peer registry source start failed: ${e.message}`);
|
|
9938
10373
|
}
|
|
9939
|
-
|
|
9940
|
-
resolve: () => void 0,
|
|
9941
|
-
report: () => {
|
|
9942
|
-
},
|
|
9943
|
-
// read-only slice: caching is internal to the source
|
|
9944
|
-
log
|
|
9945
|
-
};
|
|
9946
|
-
try {
|
|
9947
|
-
opencodeControlSource.start(opencodeSourceDeps);
|
|
9948
|
-
} catch (e) {
|
|
9949
|
-
log(`opencode control source start failed: ${e.message}`);
|
|
9950
|
-
}
|
|
10374
|
+
startEventsLifecycleSource();
|
|
9951
10375
|
}
|
|
10376
|
+
if (opts.forceStartEventsSource) startEventsLifecycleSource();
|
|
9952
10377
|
if (!process.env.VITEST) {
|
|
9953
10378
|
try {
|
|
9954
10379
|
const buildMtimeMs = statSync4(SELF_PATH2).mtimeMs;
|
|
@@ -9985,7 +10410,7 @@ ${buildCompletionProtocol(rec.id, rec.project)}`;
|
|
|
9985
10410
|
} catch {
|
|
9986
10411
|
}
|
|
9987
10412
|
try {
|
|
9988
|
-
|
|
10413
|
+
eventsSource.stop();
|
|
9989
10414
|
} catch {
|
|
9990
10415
|
}
|
|
9991
10416
|
return origStop(reason);
|