squadrant 0.19.1 → 0.19.3
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 +507 -122
- package/dist/index.js.map +1 -1
- package/dist/squadrantd.js +533 -101
- 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,38 @@ 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
|
+
function ensureSocksDir(dir = CC_SOCKS_DIR) {
|
|
3441
|
+
fs9.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
3442
|
+
if ((fs9.statSync(dir).mode & 511) !== 448)
|
|
3443
|
+
fs9.chmodSync(dir, 448);
|
|
3444
|
+
}
|
|
3445
|
+
async function pollFirstTurnConfirmedAt(getTaskRecord, project, id) {
|
|
3446
|
+
const deadline = Date.now() + FIRST_TURN_HOOK_CONFIRM_WINDOW_MS;
|
|
3447
|
+
for (; ; ) {
|
|
3448
|
+
const rec = await getTaskRecord(project, id).catch(() => void 0);
|
|
3449
|
+
if (rec?.firstTurnConfirmedAt)
|
|
3450
|
+
return true;
|
|
3451
|
+
if (Date.now() >= deadline)
|
|
3452
|
+
return false;
|
|
3453
|
+
await new Promise((r) => setTimeout(r, FIRST_TURN_HOOK_POLL_INTERVAL_MS));
|
|
3454
|
+
}
|
|
3455
|
+
}
|
|
3456
|
+
function firstTrueOrBothFalse(a, b) {
|
|
3457
|
+
return new Promise((resolve4) => {
|
|
3458
|
+
let settledFalseCount = 0;
|
|
3459
|
+
const onSettle = (ok2) => {
|
|
3460
|
+
if (ok2) {
|
|
3461
|
+
resolve4(true);
|
|
3462
|
+
return;
|
|
3463
|
+
}
|
|
3464
|
+
settledFalseCount++;
|
|
3465
|
+
if (settledFalseCount === 2)
|
|
3466
|
+
resolve4(false);
|
|
3467
|
+
};
|
|
3468
|
+
a.then(onSettle, () => onSettle(false));
|
|
3469
|
+
b.then(onSettle, () => onSettle(false));
|
|
3470
|
+
});
|
|
3471
|
+
}
|
|
3428
3472
|
async function listCrewPanes(runtime, workspaceId, project) {
|
|
3429
3473
|
const surfaces = await runtime.listSurfaces(workspaceId);
|
|
3430
3474
|
return surfaces.filter((s) => s.title && isCrewTitle(project, s.title));
|
|
@@ -3535,7 +3579,7 @@ async function runCrewSpawn(input, config, deps) {
|
|
|
3535
3579
|
deps.onModelResolved?.({ agentName, model: crewModel });
|
|
3536
3580
|
}
|
|
3537
3581
|
if (agentName === "claude") {
|
|
3538
|
-
|
|
3582
|
+
ensureSocksDir();
|
|
3539
3583
|
const messagingSocketPath = path9.join(CC_SOCKS_DIR, `squadrant-${randomUUID3()}.sock`);
|
|
3540
3584
|
const rec = await deps.dispatchCrew({
|
|
3541
3585
|
provider: "claude",
|
|
@@ -3581,10 +3625,12 @@ async function runCrewSpawn(input, config, deps) {
|
|
|
3581
3625
|
fs9.writeFileSync(spillFile, claudeFirstTurn, "utf8");
|
|
3582
3626
|
claudeFirstTurn = `Full task is at ${spillFile} \u2014 cat it and follow it exactly.`;
|
|
3583
3627
|
}
|
|
3584
|
-
const
|
|
3628
|
+
const sendPromise = deps.sendFirstTurn(pane2, `${claudeFirstTurn}
|
|
3585
3629
|
|
|
3586
3630
|
${buildCompletionProtocol(rec.id, input.project)}`, preLaunchScreen);
|
|
3587
|
-
|
|
3631
|
+
const scrapeDelivered = sendPromise.then((r) => r.delivered).catch(() => false);
|
|
3632
|
+
const delivered = hooksInstalled && deps.getTaskRecord ? await firstTrueOrBothFalse(scrapeDelivered, pollFirstTurnConfirmedAt(deps.getTaskRecord, input.project, rec.id)) : await scrapeDelivered;
|
|
3633
|
+
if (!delivered) {
|
|
3588
3634
|
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
3635
|
`);
|
|
3590
3636
|
} else if (!hooksInstalled) {
|
|
@@ -3695,6 +3741,10 @@ async function runCrewSend(project, name, message, runtime, workspaceId, deps, o
|
|
|
3695
3741
|
const heldForMin = Math.round((Date.now() - task.operatorHold.since) / 6e4);
|
|
3696
3742
|
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
3743
|
}
|
|
3744
|
+
const isAttentionState = task?.state === "blocked" || task?.state === "awaiting-input" || task?.state === "review";
|
|
3745
|
+
if (task && !isAttentionState && task.firstTurnConfirmedAt && task.task === message) {
|
|
3746
|
+
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.`);
|
|
3747
|
+
}
|
|
3698
3748
|
let reopened = false;
|
|
3699
3749
|
try {
|
|
3700
3750
|
if (task) {
|
|
@@ -3861,7 +3911,7 @@ async function runCrewList(project, runtime, workspaceId) {
|
|
|
3861
3911
|
surfaceId: c.surfaceId
|
|
3862
3912
|
}));
|
|
3863
3913
|
}
|
|
3864
|
-
var CC_SOCKS_DIR, FIRST_TURN_INLINE_MAX_BYTES, TEMPLATES_DIR, STATE_ROOT, CLOSE_LOOKUP_RETRIES, CLOSE_LOOKUP_RETRY_DELAY_MS;
|
|
3914
|
+
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
3915
|
var init_crew_spawn = __esm({
|
|
3866
3916
|
"packages/core/dist/crew-spawn.js"() {
|
|
3867
3917
|
init_control_channel();
|
|
@@ -3873,6 +3923,8 @@ var init_crew_spawn = __esm({
|
|
|
3873
3923
|
FIRST_TURN_INLINE_MAX_BYTES = 1200;
|
|
3874
3924
|
TEMPLATES_DIR = path9.join(os4.homedir(), ".config", "squadrant", "templates");
|
|
3875
3925
|
STATE_ROOT = path9.join(os4.homedir(), ".config", "squadrant", "state");
|
|
3926
|
+
FIRST_TURN_HOOK_CONFIRM_WINDOW_MS = 1e5;
|
|
3927
|
+
FIRST_TURN_HOOK_POLL_INTERVAL_MS = 2e3;
|
|
3876
3928
|
CLOSE_LOOKUP_RETRIES = 3;
|
|
3877
3929
|
CLOSE_LOOKUP_RETRY_DELAY_MS = 150;
|
|
3878
3930
|
}
|
|
@@ -4042,9 +4094,9 @@ function createDelivery(ctx, daemonCmux, isSurfaceAlive) {
|
|
|
4042
4094
|
const lastDeferred = /* @__PURE__ */ new Map();
|
|
4043
4095
|
const inFlightDelivery = () => {
|
|
4044
4096
|
let worst = null;
|
|
4045
|
-
for (const [project,
|
|
4046
|
-
if (!worst ||
|
|
4047
|
-
worst = { project, ...
|
|
4097
|
+
for (const [project, v2] of lastDeferred) {
|
|
4098
|
+
if (!worst || v2.deferCount > worst.deferCount)
|
|
4099
|
+
worst = { project, ...v2 };
|
|
4048
4100
|
}
|
|
4049
4101
|
return worst;
|
|
4050
4102
|
};
|
|
@@ -6408,6 +6460,326 @@ var init_lifecycle_source = __esm({
|
|
|
6408
6460
|
}
|
|
6409
6461
|
});
|
|
6410
6462
|
|
|
6463
|
+
// packages/core/dist/events/fact.js
|
|
6464
|
+
function stampFact(raw, id) {
|
|
6465
|
+
return { ...raw, ...id };
|
|
6466
|
+
}
|
|
6467
|
+
var init_fact = __esm({
|
|
6468
|
+
"packages/core/dist/events/fact.js"() {
|
|
6469
|
+
}
|
|
6470
|
+
});
|
|
6471
|
+
|
|
6472
|
+
// packages/core/dist/events/log.js
|
|
6473
|
+
var FactLog;
|
|
6474
|
+
var init_log = __esm({
|
|
6475
|
+
"packages/core/dist/events/log.js"() {
|
|
6476
|
+
FactLog = class {
|
|
6477
|
+
capacity;
|
|
6478
|
+
buffers = /* @__PURE__ */ new Map();
|
|
6479
|
+
constructor(opts = {}) {
|
|
6480
|
+
this.capacity = opts.capacity ?? 256;
|
|
6481
|
+
}
|
|
6482
|
+
push(fact) {
|
|
6483
|
+
let buf = this.buffers.get(fact.taskId);
|
|
6484
|
+
if (!buf) {
|
|
6485
|
+
buf = [];
|
|
6486
|
+
this.buffers.set(fact.taskId, buf);
|
|
6487
|
+
}
|
|
6488
|
+
buf.push(fact);
|
|
6489
|
+
while (buf.length > this.capacity)
|
|
6490
|
+
buf.shift();
|
|
6491
|
+
}
|
|
6492
|
+
/** Oldest-first snapshot. A fresh array; later pushes never grow it. */
|
|
6493
|
+
recent(taskId) {
|
|
6494
|
+
return [...this.buffers.get(taskId) ?? []];
|
|
6495
|
+
}
|
|
6496
|
+
/** Newline-delimited JSON, one fact per line, oldest first. */
|
|
6497
|
+
serialize(taskId) {
|
|
6498
|
+
return this.recent(taskId).map((f) => JSON.stringify(f)).join("\n") + "\n";
|
|
6499
|
+
}
|
|
6500
|
+
/** Release a finished crew's buffer. */
|
|
6501
|
+
drop(taskId) {
|
|
6502
|
+
this.buffers.delete(taskId);
|
|
6503
|
+
}
|
|
6504
|
+
};
|
|
6505
|
+
}
|
|
6506
|
+
});
|
|
6507
|
+
|
|
6508
|
+
// packages/core/dist/events/invariant.js
|
|
6509
|
+
function freshTrace() {
|
|
6510
|
+
return {
|
|
6511
|
+
depth: 0,
|
|
6512
|
+
oldestOpenAt: null,
|
|
6513
|
+
stallReported: false,
|
|
6514
|
+
unknownSeen: 0,
|
|
6515
|
+
liveness: /* @__PURE__ */ new Map()
|
|
6516
|
+
};
|
|
6517
|
+
}
|
|
6518
|
+
function checkFact(trace, fact, opts) {
|
|
6519
|
+
const out = [];
|
|
6520
|
+
if (fact.origin === "inferred" && TERMINALISING.has(fact.kind)) {
|
|
6521
|
+
out.push(v("I4", `inferred fact "${fact.kind}" from ${fact.source} cannot terminalise alone`, fact));
|
|
6522
|
+
}
|
|
6523
|
+
switch (fact.kind) {
|
|
6524
|
+
case "tool.opened":
|
|
6525
|
+
if (trace.depth === 0) {
|
|
6526
|
+
trace.oldestOpenAt = fact.at;
|
|
6527
|
+
trace.stallReported = false;
|
|
6528
|
+
}
|
|
6529
|
+
trace.depth += 1;
|
|
6530
|
+
break;
|
|
6531
|
+
case "tool.closed":
|
|
6532
|
+
if (trace.depth === 0) {
|
|
6533
|
+
out.push(v("I1", `tool.closed from ${fact.source} with no open tool`, fact));
|
|
6534
|
+
} else {
|
|
6535
|
+
trace.depth -= 1;
|
|
6536
|
+
if (trace.depth === 0) {
|
|
6537
|
+
trace.oldestOpenAt = null;
|
|
6538
|
+
trace.stallReported = false;
|
|
6539
|
+
}
|
|
6540
|
+
}
|
|
6541
|
+
break;
|
|
6542
|
+
case "turn.ended":
|
|
6543
|
+
if (trace.depth > 0) {
|
|
6544
|
+
out.push(v("I2", `turn.ended with ${trace.depth} tool call(s) still open`, fact));
|
|
6545
|
+
trace.depth = 0;
|
|
6546
|
+
trace.oldestOpenAt = null;
|
|
6547
|
+
trace.stallReported = false;
|
|
6548
|
+
}
|
|
6549
|
+
break;
|
|
6550
|
+
case "unknown":
|
|
6551
|
+
trace.unknownSeen += 1;
|
|
6552
|
+
out.push(v("I5", `unrecognised frame "${fact.name}" from ${fact.source}`, fact));
|
|
6553
|
+
break;
|
|
6554
|
+
case "process.observed": {
|
|
6555
|
+
const prior = [...trace.liveness.entries()].find(([src, s]) => src !== fact.source && s.alive !== fact.alive && fact.at - s.at <= (opts.disagreeWindowMs ?? -1));
|
|
6556
|
+
if (prior) {
|
|
6557
|
+
out.push(v("I6", `liveness disagreement: ${prior[0]} said alive=${prior[1].alive}, ${fact.source} says alive=${fact.alive}`, fact));
|
|
6558
|
+
}
|
|
6559
|
+
trace.liveness.set(fact.source, { alive: fact.alive, at: fact.at });
|
|
6560
|
+
break;
|
|
6561
|
+
}
|
|
6562
|
+
default:
|
|
6563
|
+
break;
|
|
6564
|
+
}
|
|
6565
|
+
if (opts.stallBudgetMs !== void 0 && trace.depth > 0 && trace.oldestOpenAt !== null && !trace.stallReported && fact.at - trace.oldestOpenAt > opts.stallBudgetMs) {
|
|
6566
|
+
trace.stallReported = true;
|
|
6567
|
+
out.push(v("I3", `tool open for ${fact.at - trace.oldestOpenAt}ms, past the stall budget`, fact));
|
|
6568
|
+
}
|
|
6569
|
+
return out;
|
|
6570
|
+
}
|
|
6571
|
+
var v, TERMINALISING;
|
|
6572
|
+
var init_invariant = __esm({
|
|
6573
|
+
"packages/core/dist/events/invariant.js"() {
|
|
6574
|
+
v = (code, message, f) => ({ code, message, taskId: f.taskId, at: f.at });
|
|
6575
|
+
TERMINALISING = /* @__PURE__ */ new Set(["session.ended"]);
|
|
6576
|
+
}
|
|
6577
|
+
});
|
|
6578
|
+
|
|
6579
|
+
// packages/core/dist/events/to-control-event.js
|
|
6580
|
+
function toControlEvent(fact) {
|
|
6581
|
+
if (fact.origin === "inferred" && TERMINALISING2.has(fact.kind))
|
|
6582
|
+
return [];
|
|
6583
|
+
switch (fact.kind) {
|
|
6584
|
+
case "turn.ended":
|
|
6585
|
+
return [{ type: "task.turn.completed", id: fact.taskId, turnId: fact.turnId ?? fact.source }];
|
|
6586
|
+
case "permission.requested":
|
|
6587
|
+
return [{
|
|
6588
|
+
type: "task.approval.requested",
|
|
6589
|
+
id: fact.taskId,
|
|
6590
|
+
requestId: fact.requestId,
|
|
6591
|
+
question: fact.question,
|
|
6592
|
+
kind: fact.tool
|
|
6593
|
+
}];
|
|
6594
|
+
case "input.requested":
|
|
6595
|
+
return [{
|
|
6596
|
+
type: "task.input.requested",
|
|
6597
|
+
id: fact.taskId,
|
|
6598
|
+
requestId: fact.requestId,
|
|
6599
|
+
question: fact.question
|
|
6600
|
+
}];
|
|
6601
|
+
case "session.ended":
|
|
6602
|
+
return [{ type: "task.session.ended", id: fact.taskId }];
|
|
6603
|
+
case "session.started":
|
|
6604
|
+
return [{
|
|
6605
|
+
type: "task.started",
|
|
6606
|
+
id: fact.taskId,
|
|
6607
|
+
...fact.pid === void 0 ? {} : { pid: fact.pid },
|
|
6608
|
+
...fact.sessionId === void 0 ? {} : { sessionId: fact.sessionId }
|
|
6609
|
+
}];
|
|
6610
|
+
case "prompt.submitted":
|
|
6611
|
+
return [{ type: "task.first-turn.confirmed", id: fact.taskId }];
|
|
6612
|
+
// Liveness-only. The facade still feeds these to reduceLifecycle; they
|
|
6613
|
+
// simply carry no ControlEvent of their own.
|
|
6614
|
+
case "tool.opened":
|
|
6615
|
+
case "tool.closed":
|
|
6616
|
+
case "activity":
|
|
6617
|
+
case "process.observed":
|
|
6618
|
+
case "unknown":
|
|
6619
|
+
return [];
|
|
6620
|
+
}
|
|
6621
|
+
}
|
|
6622
|
+
var TERMINALISING2;
|
|
6623
|
+
var init_to_control_event = __esm({
|
|
6624
|
+
"packages/core/dist/events/to-control-event.js"() {
|
|
6625
|
+
TERMINALISING2 = /* @__PURE__ */ new Set(["session.ended"]);
|
|
6626
|
+
}
|
|
6627
|
+
});
|
|
6628
|
+
|
|
6629
|
+
// packages/core/dist/events/conformance.js
|
|
6630
|
+
function assert(cond, msg) {
|
|
6631
|
+
if (!cond)
|
|
6632
|
+
throw new Error(`conformance: ${msg}`);
|
|
6633
|
+
}
|
|
6634
|
+
function runAdapterConformance(adapter, samples) {
|
|
6635
|
+
const call = (raw) => adapter.translate(raw);
|
|
6636
|
+
return [
|
|
6637
|
+
{
|
|
6638
|
+
name: `${adapter.name}: never throws on garbage`,
|
|
6639
|
+
run: () => {
|
|
6640
|
+
for (const g of GARBAGE) {
|
|
6641
|
+
try {
|
|
6642
|
+
call(g);
|
|
6643
|
+
} catch (e) {
|
|
6644
|
+
throw new Error(`threw on ${JSON.stringify(g)}: ${String(e)}`);
|
|
6645
|
+
}
|
|
6646
|
+
}
|
|
6647
|
+
}
|
|
6648
|
+
},
|
|
6649
|
+
{
|
|
6650
|
+
name: `${adapter.name}: never returns null or undefined`,
|
|
6651
|
+
run: () => {
|
|
6652
|
+
for (const g of [...GARBAGE, ...samples]) {
|
|
6653
|
+
const out = call(g);
|
|
6654
|
+
assert(Array.isArray(out), `returned a non-array for ${JSON.stringify(g)}`);
|
|
6655
|
+
}
|
|
6656
|
+
}
|
|
6657
|
+
},
|
|
6658
|
+
{
|
|
6659
|
+
name: `${adapter.name}: an unrecognised frame yields unknown, not an empty array`,
|
|
6660
|
+
run: () => {
|
|
6661
|
+
const out = call({ type: "definitely-not-a-real-event-name" });
|
|
6662
|
+
assert(out.length > 0, "silently dropped an unrecognised frame (the #542 shape)");
|
|
6663
|
+
assert(out.every((f) => f.kind === "unknown"), "an unrecognised frame must translate to kind 'unknown'");
|
|
6664
|
+
}
|
|
6665
|
+
},
|
|
6666
|
+
{
|
|
6667
|
+
name: `${adapter.name}: recognises its own samples`,
|
|
6668
|
+
run: () => {
|
|
6669
|
+
for (const s of samples) {
|
|
6670
|
+
const out = call(s);
|
|
6671
|
+
assert(out.length > 0, `produced nothing for its own sample ${JSON.stringify(s)}`);
|
|
6672
|
+
assert(out.some((f) => f.kind !== "unknown"), `failed to recognise its own sample ${JSON.stringify(s)}`);
|
|
6673
|
+
}
|
|
6674
|
+
}
|
|
6675
|
+
},
|
|
6676
|
+
{
|
|
6677
|
+
name: `${adapter.name}: declares a constant origin`,
|
|
6678
|
+
run: () => {
|
|
6679
|
+
assert(adapter.origin === "agent" || adapter.origin === "scan" || adapter.origin === "inferred", `invalid origin "${String(adapter.origin)}"`);
|
|
6680
|
+
}
|
|
6681
|
+
}
|
|
6682
|
+
];
|
|
6683
|
+
}
|
|
6684
|
+
var GARBAGE;
|
|
6685
|
+
var init_conformance = __esm({
|
|
6686
|
+
"packages/core/dist/events/conformance.js"() {
|
|
6687
|
+
GARBAGE = [
|
|
6688
|
+
null,
|
|
6689
|
+
void 0,
|
|
6690
|
+
0,
|
|
6691
|
+
"",
|
|
6692
|
+
"not json",
|
|
6693
|
+
[],
|
|
6694
|
+
{},
|
|
6695
|
+
{ type: 42 },
|
|
6696
|
+
{ type: "definitely-not-a-real-event-name" }
|
|
6697
|
+
];
|
|
6698
|
+
}
|
|
6699
|
+
});
|
|
6700
|
+
|
|
6701
|
+
// packages/core/dist/events/source.js
|
|
6702
|
+
function createEventsSource(opts) {
|
|
6703
|
+
const now = opts.now ?? (() => Date.now());
|
|
6704
|
+
const log = new FactLog({ capacity: opts.capacity });
|
|
6705
|
+
const adapters = new Map(opts.adapters.map((a) => [a.name, a]));
|
|
6706
|
+
const traces = /* @__PURE__ */ new Map();
|
|
6707
|
+
const seqs = /* @__PURE__ */ new Map();
|
|
6708
|
+
let deps;
|
|
6709
|
+
const traceFor = (taskId) => {
|
|
6710
|
+
let t = traces.get(taskId);
|
|
6711
|
+
if (!t) {
|
|
6712
|
+
t = freshTrace();
|
|
6713
|
+
traces.set(taskId, t);
|
|
6714
|
+
}
|
|
6715
|
+
return t;
|
|
6716
|
+
};
|
|
6717
|
+
const nextSeq = (taskId) => {
|
|
6718
|
+
const n = seqs.get(taskId) ?? 0;
|
|
6719
|
+
seqs.set(taskId, n + 1);
|
|
6720
|
+
return n;
|
|
6721
|
+
};
|
|
6722
|
+
return {
|
|
6723
|
+
name: "events",
|
|
6724
|
+
start(d) {
|
|
6725
|
+
deps = d;
|
|
6726
|
+
},
|
|
6727
|
+
stop() {
|
|
6728
|
+
deps = void 0;
|
|
6729
|
+
},
|
|
6730
|
+
health() {
|
|
6731
|
+
return { active: deps !== void 0, error: null };
|
|
6732
|
+
},
|
|
6733
|
+
recent(taskId) {
|
|
6734
|
+
return log.recent(taskId);
|
|
6735
|
+
},
|
|
6736
|
+
dump(taskId) {
|
|
6737
|
+
return log.serialize(taskId);
|
|
6738
|
+
},
|
|
6739
|
+
ingest(source, raw, hint) {
|
|
6740
|
+
const adapter = adapters.get(source);
|
|
6741
|
+
if (!adapter || !deps)
|
|
6742
|
+
return;
|
|
6743
|
+
const rec = deps.resolve(hint);
|
|
6744
|
+
if (!rec)
|
|
6745
|
+
return;
|
|
6746
|
+
const taskId = rec.id;
|
|
6747
|
+
const at = now();
|
|
6748
|
+
let produced;
|
|
6749
|
+
try {
|
|
6750
|
+
const out = adapter.translate(raw);
|
|
6751
|
+
produced = Array.isArray(out) ? out : [{ kind: "unknown", name: `${source} returned non-array` }];
|
|
6752
|
+
} catch (e) {
|
|
6753
|
+
opts.log?.(`events: adapter ${source} threw: ${String(e)}`);
|
|
6754
|
+
produced = [{ kind: "unknown", name: `${source} threw` }];
|
|
6755
|
+
}
|
|
6756
|
+
for (const rawFact of produced) {
|
|
6757
|
+
const fact = stampFact(rawFact, {
|
|
6758
|
+
seq: nextSeq(taskId),
|
|
6759
|
+
taskId,
|
|
6760
|
+
at,
|
|
6761
|
+
source,
|
|
6762
|
+
origin: adapter.origin
|
|
6763
|
+
});
|
|
6764
|
+
log.push(fact);
|
|
6765
|
+
for (const v2 of checkFact(traceFor(taskId), fact, opts.check ?? {})) {
|
|
6766
|
+
opts.onViolation(v2);
|
|
6767
|
+
}
|
|
6768
|
+
for (const ev of toControlEvent(fact))
|
|
6769
|
+
opts.emit(ev);
|
|
6770
|
+
}
|
|
6771
|
+
}
|
|
6772
|
+
};
|
|
6773
|
+
}
|
|
6774
|
+
var init_source = __esm({
|
|
6775
|
+
"packages/core/dist/events/source.js"() {
|
|
6776
|
+
init_fact();
|
|
6777
|
+
init_log();
|
|
6778
|
+
init_invariant();
|
|
6779
|
+
init_to_control_event();
|
|
6780
|
+
}
|
|
6781
|
+
});
|
|
6782
|
+
|
|
6411
6783
|
// packages/core/dist/index.js
|
|
6412
6784
|
var dist_exports = {};
|
|
6413
6785
|
__export(dist_exports, {
|
|
@@ -6422,6 +6794,7 @@ __export(dist_exports, {
|
|
|
6422
6794
|
DEFAULT_TASK_TIMEOUT_MS: () => DEFAULT_TASK_TIMEOUT_MS,
|
|
6423
6795
|
DeferDelivery: () => DeferDelivery,
|
|
6424
6796
|
FIRST_TURN_INLINE_MAX_BYTES: () => FIRST_TURN_INLINE_MAX_BYTES,
|
|
6797
|
+
FactLog: () => FactLog,
|
|
6425
6798
|
GROUP_DISPATCH_WARMUP_POLL_MS: () => GROUP_DISPATCH_WARMUP_POLL_MS,
|
|
6426
6799
|
GROUP_DISPATCH_WARMUP_TIMEOUT_MS: () => GROUP_DISPATCH_WARMUP_TIMEOUT_MS,
|
|
6427
6800
|
IDLE_DEBOUNCE_MS: () => IDLE_DEBOUNCE_MS,
|
|
@@ -6451,6 +6824,7 @@ __export(dist_exports, {
|
|
|
6451
6824
|
capAllowed: () => capAllowed,
|
|
6452
6825
|
capOutput: () => capOutput,
|
|
6453
6826
|
captainSocketPath: () => captainSocketPath,
|
|
6827
|
+
checkFact: () => checkFact,
|
|
6454
6828
|
classifyHealth: () => classifyHealth,
|
|
6455
6829
|
closeWorkItem: () => closeWorkItem,
|
|
6456
6830
|
computeTemplateHash: () => computeTemplateHash,
|
|
@@ -6463,6 +6837,7 @@ __export(dist_exports, {
|
|
|
6463
6837
|
createDirectCrewPaneReader: () => createDirectCrewPaneReader,
|
|
6464
6838
|
createDirectSurfaceLivenessProbe: () => createDirectSurfaceLivenessProbe,
|
|
6465
6839
|
createEnsureCaptainAlive: () => createEnsureCaptainAlive,
|
|
6840
|
+
createEventsSource: () => createEventsSource,
|
|
6466
6841
|
createInteractiveProbe: () => createInteractiveProbe,
|
|
6467
6842
|
createIsCaptainAlive: () => createIsCaptainAlive,
|
|
6468
6843
|
createLaunch: () => createLaunch,
|
|
@@ -6494,6 +6869,7 @@ __export(dist_exports, {
|
|
|
6494
6869
|
encodeFrame: () => encodeFrame,
|
|
6495
6870
|
encodeMsg: () => encodeMsg,
|
|
6496
6871
|
ensureDaemon: () => ensureDaemon,
|
|
6872
|
+
ensureSocksDir: () => ensureSocksDir,
|
|
6497
6873
|
evaluateStall: () => evaluateStall,
|
|
6498
6874
|
exitMarkerPath: () => exitMarkerPath,
|
|
6499
6875
|
fallsBackToPane: () => fallsBackToPane,
|
|
@@ -6505,6 +6881,7 @@ __export(dist_exports, {
|
|
|
6505
6881
|
formatInbound: () => formatInbound,
|
|
6506
6882
|
formatInboundReceipt: () => formatInboundReceipt,
|
|
6507
6883
|
formatLifecycle: () => formatLifecycle,
|
|
6884
|
+
freshTrace: () => freshTrace,
|
|
6508
6885
|
getDaemonPid: () => getDaemonPid,
|
|
6509
6886
|
healCmdFor: () => healCmdFor,
|
|
6510
6887
|
isAuthorized: () => isAuthorized,
|
|
@@ -6561,6 +6938,7 @@ __export(dist_exports, {
|
|
|
6561
6938
|
resolveSetupUserId: () => resolveSetupUserId,
|
|
6562
6939
|
restartDaemonIfRunning: () => restartDaemonIfRunning,
|
|
6563
6940
|
rotateIfNeeded: () => rotateIfNeeded,
|
|
6941
|
+
runAdapterConformance: () => runAdapterConformance,
|
|
6564
6942
|
runCrewAnswer: () => runCrewAnswer,
|
|
6565
6943
|
runCrewClose: () => runCrewClose,
|
|
6566
6944
|
runCrewList: () => runCrewList,
|
|
@@ -6595,12 +6973,14 @@ __export(dist_exports, {
|
|
|
6595
6973
|
sideNameFromTitle: () => sideNameFromTitle,
|
|
6596
6974
|
sideNextAutoName: () => sideNextAutoName,
|
|
6597
6975
|
sideTitleFor: () => sideTitleFor,
|
|
6976
|
+
stampFact: () => stampFact,
|
|
6598
6977
|
startDaemon: () => startDaemon,
|
|
6599
6978
|
startServer: () => startServer,
|
|
6600
6979
|
stripBotMention: () => stripBotMention,
|
|
6601
6980
|
surfaceVerdict: () => surfaceVerdict,
|
|
6602
6981
|
timeoutGate: () => timeoutGate,
|
|
6603
6982
|
titleFor: () => titleFor,
|
|
6983
|
+
toControlEvent: () => toControlEvent,
|
|
6604
6984
|
topicKey: () => topicKey,
|
|
6605
6985
|
topicName: () => topicName,
|
|
6606
6986
|
tryAcquireDaemonLock: () => tryAcquireDaemonLock,
|
|
@@ -6647,6 +7027,12 @@ var init_dist2 = __esm({
|
|
|
6647
7027
|
init_crew_spawn();
|
|
6648
7028
|
init_crew_answer();
|
|
6649
7029
|
init_lifecycle_source();
|
|
7030
|
+
init_fact();
|
|
7031
|
+
init_log();
|
|
7032
|
+
init_invariant();
|
|
7033
|
+
init_to_control_event();
|
|
7034
|
+
init_conformance();
|
|
7035
|
+
init_source();
|
|
6650
7036
|
init_control_channel();
|
|
6651
7037
|
init_captain_channel();
|
|
6652
7038
|
}
|
|
@@ -6663,6 +7049,7 @@ init_dist2();
|
|
|
6663
7049
|
init_dist2();
|
|
6664
7050
|
init_dist2();
|
|
6665
7051
|
init_dist2();
|
|
7052
|
+
init_dist2();
|
|
6666
7053
|
import { join as join22, dirname as dirname4, resolve as resolve3 } from "path";
|
|
6667
7054
|
import { homedir as homedir13 } from "os";
|
|
6668
7055
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
@@ -7301,9 +7688,9 @@ ${directive}` : directive;
|
|
|
7301
7688
|
function withTimeout(p, ms, msg) {
|
|
7302
7689
|
return new Promise((resolve4, reject) => {
|
|
7303
7690
|
const t = setTimeout(() => reject(new Error(msg)), ms);
|
|
7304
|
-
p.then((
|
|
7691
|
+
p.then((v2) => {
|
|
7305
7692
|
clearTimeout(t);
|
|
7306
|
-
resolve4(
|
|
7693
|
+
resolve4(v2);
|
|
7307
7694
|
}, (e) => {
|
|
7308
7695
|
clearTimeout(t);
|
|
7309
7696
|
reject(e);
|
|
@@ -7312,6 +7699,7 @@ function withTimeout(p, ms, msg) {
|
|
|
7312
7699
|
}
|
|
7313
7700
|
|
|
7314
7701
|
// packages/agents/dist/opencode/sse-bridge.js
|
|
7702
|
+
var IGNORED_FRAME = /^(message|storage|file|lsp|installation)\./;
|
|
7315
7703
|
var OpencodeSseBridge = class {
|
|
7316
7704
|
controllers = /* @__PURE__ */ new Map();
|
|
7317
7705
|
/** taskId → the crew's opencode server port (for permission-reply POSTs). */
|
|
@@ -7448,6 +7836,10 @@ var OpencodeSseBridge = class {
|
|
|
7448
7836
|
return;
|
|
7449
7837
|
}
|
|
7450
7838
|
if (json?.type === "session.idle") {
|
|
7839
|
+
if (this.deps.ingest) {
|
|
7840
|
+
this.deps.ingest(json, taskId);
|
|
7841
|
+
return;
|
|
7842
|
+
}
|
|
7451
7843
|
this.deps.emit({
|
|
7452
7844
|
type: "task.turn.completed",
|
|
7453
7845
|
id: taskId,
|
|
@@ -7457,6 +7849,10 @@ var OpencodeSseBridge = class {
|
|
|
7457
7849
|
const p = json.properties;
|
|
7458
7850
|
if (p?.id && p?.sessionID) {
|
|
7459
7851
|
this.pendingPermByTask.set(taskId, { permID: p.id, sessionID: p.sessionID });
|
|
7852
|
+
if (this.deps.ingest) {
|
|
7853
|
+
this.deps.ingest(json, taskId);
|
|
7854
|
+
return;
|
|
7855
|
+
}
|
|
7460
7856
|
const tool = p.permission ?? "a tool";
|
|
7461
7857
|
const cmd = Array.isArray(p.patterns) && p.patterns.length ? `: ${p.patterns.join(" ")}` : "";
|
|
7462
7858
|
this.deps.emit({
|
|
@@ -7466,11 +7862,24 @@ var OpencodeSseBridge = class {
|
|
|
7466
7862
|
question: `opencode requests permission to run ${tool}${cmd}`,
|
|
7467
7863
|
kind: tool
|
|
7468
7864
|
});
|
|
7865
|
+
} else if (this.deps.ingest) {
|
|
7866
|
+
this.deps.ingest(json, taskId);
|
|
7469
7867
|
}
|
|
7470
7868
|
} else if (json?.type === "permission.replied") {
|
|
7471
7869
|
this.pendingPermByTask.delete(taskId);
|
|
7870
|
+
this.deps.ingest?.(json, taskId);
|
|
7871
|
+
} else if (!IGNORED_FRAME.test(json?.type ?? "")) {
|
|
7872
|
+
this.deps.ingest?.(json, taskId);
|
|
7472
7873
|
}
|
|
7473
7874
|
}
|
|
7875
|
+
/** Test seam: exercise handleLine without an SSE stream. */
|
|
7876
|
+
handleLineForTest(rawLine, taskId) {
|
|
7877
|
+
this.handleLine(taskId, rawLine);
|
|
7878
|
+
}
|
|
7879
|
+
/** Test seam: read pendingPermByTask without exposing it publicly. */
|
|
7880
|
+
pendingPermForTest(taskId) {
|
|
7881
|
+
return this.pendingPermByTask.get(taskId);
|
|
7882
|
+
}
|
|
7474
7883
|
};
|
|
7475
7884
|
|
|
7476
7885
|
// packages/agents/dist/interactive/claude.js
|
|
@@ -8038,70 +8447,41 @@ var ClaudeReceiptListener = class {
|
|
|
8038
8447
|
}
|
|
8039
8448
|
};
|
|
8040
8449
|
|
|
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
|
-
}
|
|
8450
|
+
// packages/agents/dist/opencode/fact-adapter.js
|
|
8451
|
+
function createOpencodeFactAdapter(deps) {
|
|
8452
|
+
return {
|
|
8453
|
+
name: "opencode-sse",
|
|
8454
|
+
origin: "agent",
|
|
8455
|
+
translate(raw) {
|
|
8456
|
+
const f = typeof raw === "object" && raw !== null ? raw : {};
|
|
8457
|
+
const type = typeof f.type === "string" ? f.type : void 0;
|
|
8458
|
+
if (type === void 0)
|
|
8459
|
+
return [{ kind: "unknown", name: "non-object" }];
|
|
8460
|
+
const p = f.properties ?? {};
|
|
8461
|
+
if (type === "session.idle") {
|
|
8462
|
+
return [{
|
|
8463
|
+
kind: "turn.ended",
|
|
8464
|
+
turnId: typeof p.sessionID === "string" ? p.sessionID : void 0
|
|
8465
|
+
}];
|
|
8466
|
+
}
|
|
8467
|
+
if (type === "permission.asked") {
|
|
8468
|
+
if (typeof p.id !== "string" || typeof p.sessionID !== "string") {
|
|
8469
|
+
return [{ kind: "unknown", name: "permission.asked:incomplete" }];
|
|
8470
|
+
}
|
|
8471
|
+
const tool = typeof p.permission === "string" ? p.permission : "a tool";
|
|
8472
|
+
const cmd = Array.isArray(p.patterns) && p.patterns.length ? `: ${p.patterns.join(" ")}` : "";
|
|
8473
|
+
return [{
|
|
8474
|
+
kind: "permission.requested",
|
|
8475
|
+
question: `opencode requests permission to run ${tool}${cmd}`,
|
|
8476
|
+
requestId: deps.nextRequestId(),
|
|
8477
|
+
tool
|
|
8478
|
+
}];
|
|
8479
|
+
}
|
|
8480
|
+
if (type === "permission.replied")
|
|
8481
|
+
return [{ kind: "activity" }];
|
|
8482
|
+
return [{ kind: "unknown", name: type }];
|
|
8483
|
+
}
|
|
8484
|
+
};
|
|
8105
8485
|
}
|
|
8106
8486
|
|
|
8107
8487
|
// packages/workspaces/dist/runtimes/cmux.js
|
|
@@ -9556,6 +9936,7 @@ function registerSenderIdentity(socketPath) {
|
|
|
9556
9936
|
}
|
|
9557
9937
|
async function sharedReceiptListener() {
|
|
9558
9938
|
if (shared) return shared;
|
|
9939
|
+
ensureSocksDir();
|
|
9559
9940
|
const socketPath = `${CC_SOCKS_DIR}/squadrantd-${process.pid}.sock`;
|
|
9560
9941
|
const listener = new ClaudeReceiptListener({
|
|
9561
9942
|
socketPath,
|
|
@@ -9716,7 +10097,37 @@ function startSquadrantd(opts = {}) {
|
|
|
9716
10097
|
codexAppServerSource.observe(ev);
|
|
9717
10098
|
}
|
|
9718
10099
|
});
|
|
9719
|
-
const
|
|
10100
|
+
const violationLogState = /* @__PURE__ */ new Map();
|
|
10101
|
+
const VIOLATION_LOG_SUMMARY_MS = 6e4;
|
|
10102
|
+
const onEventsViolation = (v2) => {
|
|
10103
|
+
const key = `${v2.taskId}:${v2.code}`;
|
|
10104
|
+
const prev = violationLogState.get(key);
|
|
10105
|
+
if (!prev) {
|
|
10106
|
+
violationLogState.set(key, { count: 1, loggedAt: Date.now() });
|
|
10107
|
+
log(`[events] ${v2.code} ${v2.taskId}: ${v2.message}`);
|
|
10108
|
+
return;
|
|
10109
|
+
}
|
|
10110
|
+
prev.count++;
|
|
10111
|
+
const now = Date.now();
|
|
10112
|
+
if (now - prev.loggedAt > VIOLATION_LOG_SUMMARY_MS) {
|
|
10113
|
+
log(`[events] ${v2.code} ${v2.taskId}: seen ${prev.count}x since last log (latest: ${v2.message})`);
|
|
10114
|
+
prev.count = 0;
|
|
10115
|
+
prev.loggedAt = now;
|
|
10116
|
+
}
|
|
10117
|
+
};
|
|
10118
|
+
const opencodeRequestIds = { n: 1 };
|
|
10119
|
+
const eventsSource = createEventsSource({
|
|
10120
|
+
adapters: [createOpencodeFactAdapter({ nextRequestId: () => opencodeRequestIds.n++ })],
|
|
10121
|
+
emit: (ev) => {
|
|
10122
|
+
const found = store.listAll().find((r) => r.id === ev.id);
|
|
10123
|
+
if (!found) return;
|
|
10124
|
+
void ctx.d.handle({ kind: "event", project: found.project, event: ev });
|
|
10125
|
+
if (ev.type === "task.approval.requested")
|
|
10126
|
+
ctx.schedulePromotion(ev.id, ev.requestId, "approval", ev.question);
|
|
10127
|
+
},
|
|
10128
|
+
onViolation: onEventsViolation,
|
|
10129
|
+
check: { stallBudgetMs: 6e4, disagreeWindowMs: 5e3 }
|
|
10130
|
+
});
|
|
9720
10131
|
const opencodeBridge = opts.opencodeBridge ?? new OpencodeSseBridge({
|
|
9721
10132
|
emit: (ev) => {
|
|
9722
10133
|
const found = store.listAll().find((r) => r.id === ev.id);
|
|
@@ -9724,8 +10135,8 @@ function startSquadrantd(opts = {}) {
|
|
|
9724
10135
|
void ctx.d.handle({ kind: "event", project: found.project, event: ev });
|
|
9725
10136
|
if (ev.type === "task.approval.requested")
|
|
9726
10137
|
ctx.schedulePromotion(ev.id, ev.requestId, "approval", ev.question);
|
|
9727
|
-
opencodeControlSource.observe(ev);
|
|
9728
10138
|
},
|
|
10139
|
+
ingest: (raw, taskId) => eventsSource.ingest("opencode-sse", raw, { taskId }),
|
|
9729
10140
|
log
|
|
9730
10141
|
});
|
|
9731
10142
|
const cmuxEventsBridge = opts.cmuxEventsBridge ?? new CmuxEventsBridge({
|
|
@@ -9750,11 +10161,11 @@ function startSquadrantd(opts = {}) {
|
|
|
9750
10161
|
ctx.cmuxEventsBridge = cmuxEventsBridge;
|
|
9751
10162
|
const claudePeerRegistrySource = new ClaudePeerRegistrySource({ log });
|
|
9752
10163
|
ctx.lifecycleSources = [
|
|
10164
|
+
eventsSource,
|
|
9753
10165
|
cmuxStoreSource,
|
|
9754
10166
|
nativeHookSource,
|
|
9755
10167
|
codexAppServerSource,
|
|
9756
|
-
claudePeerRegistrySource
|
|
9757
|
-
opencodeControlSource
|
|
10168
|
+
claudePeerRegistrySource
|
|
9758
10169
|
];
|
|
9759
10170
|
const tgCfg = loadConfig().telegram;
|
|
9760
10171
|
ctx.telegramBridge = opts.telegramBridge ?? (tgCfg && !process.env.VITEST ? buildTelegramBridge(
|
|
@@ -9810,6 +10221,37 @@ ${buildCompletionProtocol(rec.id, rec.project)}`;
|
|
|
9810
10221
|
}
|
|
9811
10222
|
});
|
|
9812
10223
|
const h = startDaemon(ctx, { ...opts, launchHeadless }, PKG_VERSION);
|
|
10224
|
+
const startEventsLifecycleSource = () => {
|
|
10225
|
+
let eventsTaskIndex;
|
|
10226
|
+
let eventsTaskIndexAt = 0;
|
|
10227
|
+
const EVENTS_TASK_INDEX_TTL_MS = 500;
|
|
10228
|
+
const eventsSourceDeps = {
|
|
10229
|
+
resolve: (hint) => {
|
|
10230
|
+
if (!hint.taskId) return void 0;
|
|
10231
|
+
const now = Date.now();
|
|
10232
|
+
if (!eventsTaskIndex || now - eventsTaskIndexAt > EVENTS_TASK_INDEX_TTL_MS) {
|
|
10233
|
+
eventsTaskIndex = /* @__PURE__ */ new Map();
|
|
10234
|
+
for (const r of store.listAll()) {
|
|
10235
|
+
if (!TERMINAL_STATES.has(r.state)) eventsTaskIndex.set(r.id, { id: r.id });
|
|
10236
|
+
}
|
|
10237
|
+
eventsTaskIndexAt = now;
|
|
10238
|
+
}
|
|
10239
|
+
const cached = eventsTaskIndex.get(hint.taskId);
|
|
10240
|
+
if (cached) return cached;
|
|
10241
|
+
return store.listAll().find(
|
|
10242
|
+
(r) => r.id === hint.taskId && !TERMINAL_STATES.has(r.state)
|
|
10243
|
+
);
|
|
10244
|
+
},
|
|
10245
|
+
report: () => {
|
|
10246
|
+
},
|
|
10247
|
+
log
|
|
10248
|
+
};
|
|
10249
|
+
try {
|
|
10250
|
+
eventsSource.start(eventsSourceDeps);
|
|
10251
|
+
} catch (e) {
|
|
10252
|
+
log(`events source start failed: ${e.message}`);
|
|
10253
|
+
}
|
|
10254
|
+
};
|
|
9813
10255
|
if (!process.env.VITEST) {
|
|
9814
10256
|
const prevSnaps = /* @__PURE__ */ new Map();
|
|
9815
10257
|
const storeDeps = {
|
|
@@ -9936,19 +10378,9 @@ ${buildCompletionProtocol(rec.id, rec.project)}`;
|
|
|
9936
10378
|
} catch (e) {
|
|
9937
10379
|
log(`claude peer registry source start failed: ${e.message}`);
|
|
9938
10380
|
}
|
|
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
|
-
}
|
|
10381
|
+
startEventsLifecycleSource();
|
|
9951
10382
|
}
|
|
10383
|
+
if (opts.forceStartEventsSource) startEventsLifecycleSource();
|
|
9952
10384
|
if (!process.env.VITEST) {
|
|
9953
10385
|
try {
|
|
9954
10386
|
const buildMtimeMs = statSync4(SELF_PATH2).mtimeMs;
|
|
@@ -9985,7 +10417,7 @@ ${buildCompletionProtocol(rec.id, rec.project)}`;
|
|
|
9985
10417
|
} catch {
|
|
9986
10418
|
}
|
|
9987
10419
|
try {
|
|
9988
|
-
|
|
10420
|
+
eventsSource.stop();
|
|
9989
10421
|
} catch {
|
|
9990
10422
|
}
|
|
9991
10423
|
return origStop(reason);
|