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/index.js
CHANGED
|
@@ -86,22 +86,22 @@ var init_config_io = __esm({
|
|
|
86
86
|
import path2 from "path";
|
|
87
87
|
import os from "os";
|
|
88
88
|
import chalk from "chalk";
|
|
89
|
-
function isThinkingLevel(
|
|
90
|
-
return THINKING_LEVELS.includes(
|
|
89
|
+
function isThinkingLevel(v2) {
|
|
90
|
+
return THINKING_LEVELS.includes(v2);
|
|
91
91
|
}
|
|
92
|
-
function parseThinkingLevel(
|
|
93
|
-
if (!isThinkingLevel(
|
|
94
|
-
throw new Error(`Invalid --thinking value '${
|
|
92
|
+
function parseThinkingLevel(v2) {
|
|
93
|
+
if (!isThinkingLevel(v2)) {
|
|
94
|
+
throw new Error(`Invalid --thinking value '${v2}'. Valid values: ${THINKING_LEVELS.join(", ")}`);
|
|
95
95
|
}
|
|
96
|
-
return
|
|
96
|
+
return v2;
|
|
97
97
|
}
|
|
98
98
|
function resolveControlChannelMode(cfg, agent) {
|
|
99
|
-
const
|
|
100
|
-
return
|
|
99
|
+
const v2 = cfg?.[agent];
|
|
100
|
+
return v2 && CONTROL_CHANNEL_MODES.has(v2) ? v2 : "off";
|
|
101
101
|
}
|
|
102
102
|
function resolveCaptainChannelMode(defaults) {
|
|
103
|
-
const
|
|
104
|
-
return
|
|
103
|
+
const v2 = defaults?.captainChannel;
|
|
104
|
+
return v2 && CONTROL_CHANNEL_MODES.has(v2) ? v2 : "off";
|
|
105
105
|
}
|
|
106
106
|
function getDefaultConfig() {
|
|
107
107
|
return {
|
|
@@ -219,8 +219,8 @@ function deepMerge(base, patch) {
|
|
|
219
219
|
if (patch === null || typeof patch !== "object" || Array.isArray(patch))
|
|
220
220
|
return patch ?? base;
|
|
221
221
|
const out = { ...base };
|
|
222
|
-
for (const [k,
|
|
223
|
-
out[k] = deepMerge(out[k],
|
|
222
|
+
for (const [k, v2] of Object.entries(patch)) {
|
|
223
|
+
out[k] = deepMerge(out[k], v2);
|
|
224
224
|
}
|
|
225
225
|
return out;
|
|
226
226
|
}
|
|
@@ -730,7 +730,7 @@ function isCacheStale(state, now, intervalMs = CHECK_INTERVAL_MS, failureInterva
|
|
|
730
730
|
return now - state.lastChecked >= (state.lastCheckFailed ? failureIntervalMs : intervalMs);
|
|
731
731
|
}
|
|
732
732
|
function isNewerVersion(latest, current) {
|
|
733
|
-
const parse2 = (
|
|
733
|
+
const parse2 = (v2) => v2.trim().replace(/^v/, "").split("-")[0].split(".").map((n) => Number(n) || 0);
|
|
734
734
|
const [la = 0, lb = 0, lc = 0] = parse2(latest);
|
|
735
735
|
const [ca = 0, cb = 0, cc = 0] = parse2(current);
|
|
736
736
|
if (la !== ca)
|
|
@@ -1111,8 +1111,8 @@ var init_runtime_sync = __esm({
|
|
|
1111
1111
|
});
|
|
1112
1112
|
|
|
1113
1113
|
// packages/shared/dist/lib/tool-compat.js
|
|
1114
|
-
function parseSemVer(
|
|
1115
|
-
const m =
|
|
1114
|
+
function parseSemVer(v2) {
|
|
1115
|
+
const m = v2.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
1116
1116
|
if (!m)
|
|
1117
1117
|
return null;
|
|
1118
1118
|
return [parseInt(m[1], 10), parseInt(m[2], 10), parseInt(m[3], 10)];
|
|
@@ -2523,12 +2523,12 @@ function isDaemonSocketLive(sockPath, timeoutMs = 500) {
|
|
|
2523
2523
|
return;
|
|
2524
2524
|
}
|
|
2525
2525
|
const conn = createConnection(sockPath);
|
|
2526
|
-
const finish = (
|
|
2526
|
+
const finish = (v2) => {
|
|
2527
2527
|
try {
|
|
2528
2528
|
conn.destroy();
|
|
2529
2529
|
} catch {
|
|
2530
2530
|
}
|
|
2531
|
-
resolve4(
|
|
2531
|
+
resolve4(v2);
|
|
2532
2532
|
};
|
|
2533
2533
|
const timer = setTimeout(() => finish(false), timeoutMs);
|
|
2534
2534
|
conn.on("connect", () => {
|
|
@@ -3194,17 +3194,20 @@ function getDaemonPid(target) {
|
|
|
3194
3194
|
function forceKickstartAndVerify(target, opts = {}) {
|
|
3195
3195
|
const pollAttempts = opts.pollAttempts ?? 15;
|
|
3196
3196
|
const pollDelayMs = opts.pollDelayMs ?? 300;
|
|
3197
|
-
const kickstartRetries = opts.kickstartRetries ??
|
|
3197
|
+
const kickstartRetries = opts.kickstartRetries ?? 10;
|
|
3198
3198
|
const kickstartRetryDelayMs = opts.kickstartRetryDelayMs ?? 300;
|
|
3199
3199
|
const pidBefore = getDaemonPid(target);
|
|
3200
|
+
let kickstartError = null;
|
|
3200
3201
|
for (let i = 0; i < kickstartRetries; i++) {
|
|
3201
3202
|
try {
|
|
3202
3203
|
execFileSync3("launchctl", ["kickstart", "-k", target], { stdio: "ignore" });
|
|
3204
|
+
kickstartError = null;
|
|
3203
3205
|
break;
|
|
3204
3206
|
} catch (e) {
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3207
|
+
kickstartError = e;
|
|
3208
|
+
if (i < kickstartRetries - 1) {
|
|
3209
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, kickstartRetryDelayMs);
|
|
3210
|
+
}
|
|
3208
3211
|
}
|
|
3209
3212
|
}
|
|
3210
3213
|
let pidAfter = null;
|
|
@@ -3216,7 +3219,16 @@ function forceKickstartAndVerify(target, opts = {}) {
|
|
|
3216
3219
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, pollDelayMs);
|
|
3217
3220
|
}
|
|
3218
3221
|
}
|
|
3219
|
-
|
|
3222
|
+
const restarted = pidAfter !== null && pidAfter !== pidBefore;
|
|
3223
|
+
if (kickstartError && !restarted)
|
|
3224
|
+
throw kickstartError;
|
|
3225
|
+
return {
|
|
3226
|
+
target,
|
|
3227
|
+
pidBefore,
|
|
3228
|
+
pidAfter,
|
|
3229
|
+
restarted,
|
|
3230
|
+
...kickstartError ? { note: "kickstart -k refused; daemon restarted by bootstrap" } : {}
|
|
3231
|
+
};
|
|
3220
3232
|
}
|
|
3221
3233
|
function isOperatorInitiatedCommand(topLevelArg) {
|
|
3222
3234
|
return topLevelArg !== void 0 && OPERATOR_INITIATED_COMMANDS.has(topLevelArg);
|
|
@@ -4125,6 +4137,33 @@ import fs9 from "fs";
|
|
|
4125
4137
|
import os4 from "os";
|
|
4126
4138
|
import path9 from "path";
|
|
4127
4139
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
4140
|
+
async function pollFirstTurnConfirmedAt(getTaskRecord, project, id) {
|
|
4141
|
+
const deadline = Date.now() + FIRST_TURN_HOOK_CONFIRM_WINDOW_MS;
|
|
4142
|
+
for (; ; ) {
|
|
4143
|
+
const rec = await getTaskRecord(project, id).catch(() => void 0);
|
|
4144
|
+
if (rec?.firstTurnConfirmedAt)
|
|
4145
|
+
return true;
|
|
4146
|
+
if (Date.now() >= deadline)
|
|
4147
|
+
return false;
|
|
4148
|
+
await new Promise((r) => setTimeout(r, FIRST_TURN_HOOK_POLL_INTERVAL_MS));
|
|
4149
|
+
}
|
|
4150
|
+
}
|
|
4151
|
+
function firstTrueOrBothFalse(a, b) {
|
|
4152
|
+
return new Promise((resolve4) => {
|
|
4153
|
+
let settledFalseCount = 0;
|
|
4154
|
+
const onSettle = (ok2) => {
|
|
4155
|
+
if (ok2) {
|
|
4156
|
+
resolve4(true);
|
|
4157
|
+
return;
|
|
4158
|
+
}
|
|
4159
|
+
settledFalseCount++;
|
|
4160
|
+
if (settledFalseCount === 2)
|
|
4161
|
+
resolve4(false);
|
|
4162
|
+
};
|
|
4163
|
+
a.then(onSettle, () => onSettle(false));
|
|
4164
|
+
b.then(onSettle, () => onSettle(false));
|
|
4165
|
+
});
|
|
4166
|
+
}
|
|
4128
4167
|
async function listCrewPanes(runtime, workspaceId, project) {
|
|
4129
4168
|
const surfaces = await runtime.listSurfaces(workspaceId);
|
|
4130
4169
|
return surfaces.filter((s) => s.title && isCrewTitle(project, s.title));
|
|
@@ -4281,10 +4320,12 @@ async function runCrewSpawn(input, config, deps) {
|
|
|
4281
4320
|
fs9.writeFileSync(spillFile, claudeFirstTurn, "utf8");
|
|
4282
4321
|
claudeFirstTurn = `Full task is at ${spillFile} \u2014 cat it and follow it exactly.`;
|
|
4283
4322
|
}
|
|
4284
|
-
const
|
|
4323
|
+
const sendPromise = deps.sendFirstTurn(pane2, `${claudeFirstTurn}
|
|
4285
4324
|
|
|
4286
4325
|
${buildCompletionProtocol(rec.id, input.project)}`, preLaunchScreen);
|
|
4287
|
-
|
|
4326
|
+
const scrapeDelivered = sendPromise.then((r) => r.delivered).catch(() => false);
|
|
4327
|
+
const delivered = hooksInstalled && deps.getTaskRecord ? await firstTrueOrBothFalse(scrapeDelivered, pollFirstTurnConfirmedAt(deps.getTaskRecord, input.project, rec.id)) : await scrapeDelivered;
|
|
4328
|
+
if (!delivered) {
|
|
4288
4329
|
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.
|
|
4289
4330
|
`);
|
|
4290
4331
|
} else if (!hooksInstalled) {
|
|
@@ -4395,6 +4436,10 @@ async function runCrewSend(project, name, message, runtime, workspaceId, deps, o
|
|
|
4395
4436
|
const heldForMin = Math.round((Date.now() - task.operatorHold.since) / 6e4);
|
|
4396
4437
|
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.`);
|
|
4397
4438
|
}
|
|
4439
|
+
const isAttentionState = task?.state === "blocked" || task?.state === "awaiting-input" || task?.state === "review";
|
|
4440
|
+
if (task && !isAttentionState && task.firstTurnConfirmedAt && task.task === message) {
|
|
4441
|
+
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.`);
|
|
4442
|
+
}
|
|
4398
4443
|
let reopened = false;
|
|
4399
4444
|
try {
|
|
4400
4445
|
if (task) {
|
|
@@ -4561,7 +4606,7 @@ async function runCrewList(project, runtime, workspaceId) {
|
|
|
4561
4606
|
surfaceId: c.surfaceId
|
|
4562
4607
|
}));
|
|
4563
4608
|
}
|
|
4564
|
-
var CC_SOCKS_DIR, FIRST_TURN_INLINE_MAX_BYTES, TEMPLATES_DIR, STATE_ROOT, CLOSE_LOOKUP_RETRIES, CLOSE_LOOKUP_RETRY_DELAY_MS;
|
|
4609
|
+
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;
|
|
4565
4610
|
var init_crew_spawn = __esm({
|
|
4566
4611
|
"packages/core/dist/crew-spawn.js"() {
|
|
4567
4612
|
init_control_channel();
|
|
@@ -4573,6 +4618,8 @@ var init_crew_spawn = __esm({
|
|
|
4573
4618
|
FIRST_TURN_INLINE_MAX_BYTES = 1200;
|
|
4574
4619
|
TEMPLATES_DIR = path9.join(os4.homedir(), ".config", "squadrant", "templates");
|
|
4575
4620
|
STATE_ROOT = path9.join(os4.homedir(), ".config", "squadrant", "state");
|
|
4621
|
+
FIRST_TURN_HOOK_CONFIRM_WINDOW_MS = 1e5;
|
|
4622
|
+
FIRST_TURN_HOOK_POLL_INTERVAL_MS = 2e3;
|
|
4576
4623
|
CLOSE_LOOKUP_RETRIES = 3;
|
|
4577
4624
|
CLOSE_LOOKUP_RETRY_DELAY_MS = 150;
|
|
4578
4625
|
}
|
|
@@ -4742,9 +4789,9 @@ function createDelivery(ctx, daemonCmux, isSurfaceAlive) {
|
|
|
4742
4789
|
const lastDeferred = /* @__PURE__ */ new Map();
|
|
4743
4790
|
const inFlightDelivery = () => {
|
|
4744
4791
|
let worst2 = null;
|
|
4745
|
-
for (const [project,
|
|
4746
|
-
if (!worst2 ||
|
|
4747
|
-
worst2 = { project, ...
|
|
4792
|
+
for (const [project, v2] of lastDeferred) {
|
|
4793
|
+
if (!worst2 || v2.deferCount > worst2.deferCount)
|
|
4794
|
+
worst2 = { project, ...v2 };
|
|
4748
4795
|
}
|
|
4749
4796
|
return worst2;
|
|
4750
4797
|
};
|
|
@@ -7108,6 +7155,326 @@ var init_lifecycle_source = __esm({
|
|
|
7108
7155
|
}
|
|
7109
7156
|
});
|
|
7110
7157
|
|
|
7158
|
+
// packages/core/dist/events/fact.js
|
|
7159
|
+
function stampFact(raw, id) {
|
|
7160
|
+
return { ...raw, ...id };
|
|
7161
|
+
}
|
|
7162
|
+
var init_fact = __esm({
|
|
7163
|
+
"packages/core/dist/events/fact.js"() {
|
|
7164
|
+
}
|
|
7165
|
+
});
|
|
7166
|
+
|
|
7167
|
+
// packages/core/dist/events/log.js
|
|
7168
|
+
var FactLog;
|
|
7169
|
+
var init_log = __esm({
|
|
7170
|
+
"packages/core/dist/events/log.js"() {
|
|
7171
|
+
FactLog = class {
|
|
7172
|
+
capacity;
|
|
7173
|
+
buffers = /* @__PURE__ */ new Map();
|
|
7174
|
+
constructor(opts = {}) {
|
|
7175
|
+
this.capacity = opts.capacity ?? 256;
|
|
7176
|
+
}
|
|
7177
|
+
push(fact) {
|
|
7178
|
+
let buf = this.buffers.get(fact.taskId);
|
|
7179
|
+
if (!buf) {
|
|
7180
|
+
buf = [];
|
|
7181
|
+
this.buffers.set(fact.taskId, buf);
|
|
7182
|
+
}
|
|
7183
|
+
buf.push(fact);
|
|
7184
|
+
while (buf.length > this.capacity)
|
|
7185
|
+
buf.shift();
|
|
7186
|
+
}
|
|
7187
|
+
/** Oldest-first snapshot. A fresh array; later pushes never grow it. */
|
|
7188
|
+
recent(taskId) {
|
|
7189
|
+
return [...this.buffers.get(taskId) ?? []];
|
|
7190
|
+
}
|
|
7191
|
+
/** Newline-delimited JSON, one fact per line, oldest first. */
|
|
7192
|
+
serialize(taskId) {
|
|
7193
|
+
return this.recent(taskId).map((f) => JSON.stringify(f)).join("\n") + "\n";
|
|
7194
|
+
}
|
|
7195
|
+
/** Release a finished crew's buffer. */
|
|
7196
|
+
drop(taskId) {
|
|
7197
|
+
this.buffers.delete(taskId);
|
|
7198
|
+
}
|
|
7199
|
+
};
|
|
7200
|
+
}
|
|
7201
|
+
});
|
|
7202
|
+
|
|
7203
|
+
// packages/core/dist/events/invariant.js
|
|
7204
|
+
function freshTrace() {
|
|
7205
|
+
return {
|
|
7206
|
+
depth: 0,
|
|
7207
|
+
oldestOpenAt: null,
|
|
7208
|
+
stallReported: false,
|
|
7209
|
+
unknownSeen: 0,
|
|
7210
|
+
liveness: /* @__PURE__ */ new Map()
|
|
7211
|
+
};
|
|
7212
|
+
}
|
|
7213
|
+
function checkFact(trace, fact, opts) {
|
|
7214
|
+
const out = [];
|
|
7215
|
+
if (fact.origin === "inferred" && TERMINALISING.has(fact.kind)) {
|
|
7216
|
+
out.push(v("I4", `inferred fact "${fact.kind}" from ${fact.source} cannot terminalise alone`, fact));
|
|
7217
|
+
}
|
|
7218
|
+
switch (fact.kind) {
|
|
7219
|
+
case "tool.opened":
|
|
7220
|
+
if (trace.depth === 0) {
|
|
7221
|
+
trace.oldestOpenAt = fact.at;
|
|
7222
|
+
trace.stallReported = false;
|
|
7223
|
+
}
|
|
7224
|
+
trace.depth += 1;
|
|
7225
|
+
break;
|
|
7226
|
+
case "tool.closed":
|
|
7227
|
+
if (trace.depth === 0) {
|
|
7228
|
+
out.push(v("I1", `tool.closed from ${fact.source} with no open tool`, fact));
|
|
7229
|
+
} else {
|
|
7230
|
+
trace.depth -= 1;
|
|
7231
|
+
if (trace.depth === 0) {
|
|
7232
|
+
trace.oldestOpenAt = null;
|
|
7233
|
+
trace.stallReported = false;
|
|
7234
|
+
}
|
|
7235
|
+
}
|
|
7236
|
+
break;
|
|
7237
|
+
case "turn.ended":
|
|
7238
|
+
if (trace.depth > 0) {
|
|
7239
|
+
out.push(v("I2", `turn.ended with ${trace.depth} tool call(s) still open`, fact));
|
|
7240
|
+
trace.depth = 0;
|
|
7241
|
+
trace.oldestOpenAt = null;
|
|
7242
|
+
trace.stallReported = false;
|
|
7243
|
+
}
|
|
7244
|
+
break;
|
|
7245
|
+
case "unknown":
|
|
7246
|
+
trace.unknownSeen += 1;
|
|
7247
|
+
out.push(v("I5", `unrecognised frame "${fact.name}" from ${fact.source}`, fact));
|
|
7248
|
+
break;
|
|
7249
|
+
case "process.observed": {
|
|
7250
|
+
const prior = [...trace.liveness.entries()].find(([src, s]) => src !== fact.source && s.alive !== fact.alive && fact.at - s.at <= (opts.disagreeWindowMs ?? -1));
|
|
7251
|
+
if (prior) {
|
|
7252
|
+
out.push(v("I6", `liveness disagreement: ${prior[0]} said alive=${prior[1].alive}, ${fact.source} says alive=${fact.alive}`, fact));
|
|
7253
|
+
}
|
|
7254
|
+
trace.liveness.set(fact.source, { alive: fact.alive, at: fact.at });
|
|
7255
|
+
break;
|
|
7256
|
+
}
|
|
7257
|
+
default:
|
|
7258
|
+
break;
|
|
7259
|
+
}
|
|
7260
|
+
if (opts.stallBudgetMs !== void 0 && trace.depth > 0 && trace.oldestOpenAt !== null && !trace.stallReported && fact.at - trace.oldestOpenAt > opts.stallBudgetMs) {
|
|
7261
|
+
trace.stallReported = true;
|
|
7262
|
+
out.push(v("I3", `tool open for ${fact.at - trace.oldestOpenAt}ms, past the stall budget`, fact));
|
|
7263
|
+
}
|
|
7264
|
+
return out;
|
|
7265
|
+
}
|
|
7266
|
+
var v, TERMINALISING;
|
|
7267
|
+
var init_invariant = __esm({
|
|
7268
|
+
"packages/core/dist/events/invariant.js"() {
|
|
7269
|
+
v = (code, message, f) => ({ code, message, taskId: f.taskId, at: f.at });
|
|
7270
|
+
TERMINALISING = /* @__PURE__ */ new Set(["session.ended"]);
|
|
7271
|
+
}
|
|
7272
|
+
});
|
|
7273
|
+
|
|
7274
|
+
// packages/core/dist/events/to-control-event.js
|
|
7275
|
+
function toControlEvent(fact) {
|
|
7276
|
+
if (fact.origin === "inferred" && TERMINALISING2.has(fact.kind))
|
|
7277
|
+
return [];
|
|
7278
|
+
switch (fact.kind) {
|
|
7279
|
+
case "turn.ended":
|
|
7280
|
+
return [{ type: "task.turn.completed", id: fact.taskId, turnId: fact.turnId ?? fact.source }];
|
|
7281
|
+
case "permission.requested":
|
|
7282
|
+
return [{
|
|
7283
|
+
type: "task.approval.requested",
|
|
7284
|
+
id: fact.taskId,
|
|
7285
|
+
requestId: fact.requestId,
|
|
7286
|
+
question: fact.question,
|
|
7287
|
+
kind: fact.tool
|
|
7288
|
+
}];
|
|
7289
|
+
case "input.requested":
|
|
7290
|
+
return [{
|
|
7291
|
+
type: "task.input.requested",
|
|
7292
|
+
id: fact.taskId,
|
|
7293
|
+
requestId: fact.requestId,
|
|
7294
|
+
question: fact.question
|
|
7295
|
+
}];
|
|
7296
|
+
case "session.ended":
|
|
7297
|
+
return [{ type: "task.session.ended", id: fact.taskId }];
|
|
7298
|
+
case "session.started":
|
|
7299
|
+
return [{
|
|
7300
|
+
type: "task.started",
|
|
7301
|
+
id: fact.taskId,
|
|
7302
|
+
...fact.pid === void 0 ? {} : { pid: fact.pid },
|
|
7303
|
+
...fact.sessionId === void 0 ? {} : { sessionId: fact.sessionId }
|
|
7304
|
+
}];
|
|
7305
|
+
case "prompt.submitted":
|
|
7306
|
+
return [{ type: "task.first-turn.confirmed", id: fact.taskId }];
|
|
7307
|
+
// Liveness-only. The facade still feeds these to reduceLifecycle; they
|
|
7308
|
+
// simply carry no ControlEvent of their own.
|
|
7309
|
+
case "tool.opened":
|
|
7310
|
+
case "tool.closed":
|
|
7311
|
+
case "activity":
|
|
7312
|
+
case "process.observed":
|
|
7313
|
+
case "unknown":
|
|
7314
|
+
return [];
|
|
7315
|
+
}
|
|
7316
|
+
}
|
|
7317
|
+
var TERMINALISING2;
|
|
7318
|
+
var init_to_control_event = __esm({
|
|
7319
|
+
"packages/core/dist/events/to-control-event.js"() {
|
|
7320
|
+
TERMINALISING2 = /* @__PURE__ */ new Set(["session.ended"]);
|
|
7321
|
+
}
|
|
7322
|
+
});
|
|
7323
|
+
|
|
7324
|
+
// packages/core/dist/events/conformance.js
|
|
7325
|
+
function assert(cond, msg) {
|
|
7326
|
+
if (!cond)
|
|
7327
|
+
throw new Error(`conformance: ${msg}`);
|
|
7328
|
+
}
|
|
7329
|
+
function runAdapterConformance(adapter, samples) {
|
|
7330
|
+
const call = (raw) => adapter.translate(raw);
|
|
7331
|
+
return [
|
|
7332
|
+
{
|
|
7333
|
+
name: `${adapter.name}: never throws on garbage`,
|
|
7334
|
+
run: () => {
|
|
7335
|
+
for (const g of GARBAGE) {
|
|
7336
|
+
try {
|
|
7337
|
+
call(g);
|
|
7338
|
+
} catch (e) {
|
|
7339
|
+
throw new Error(`threw on ${JSON.stringify(g)}: ${String(e)}`);
|
|
7340
|
+
}
|
|
7341
|
+
}
|
|
7342
|
+
}
|
|
7343
|
+
},
|
|
7344
|
+
{
|
|
7345
|
+
name: `${adapter.name}: never returns null or undefined`,
|
|
7346
|
+
run: () => {
|
|
7347
|
+
for (const g of [...GARBAGE, ...samples]) {
|
|
7348
|
+
const out = call(g);
|
|
7349
|
+
assert(Array.isArray(out), `returned a non-array for ${JSON.stringify(g)}`);
|
|
7350
|
+
}
|
|
7351
|
+
}
|
|
7352
|
+
},
|
|
7353
|
+
{
|
|
7354
|
+
name: `${adapter.name}: an unrecognised frame yields unknown, not an empty array`,
|
|
7355
|
+
run: () => {
|
|
7356
|
+
const out = call({ type: "definitely-not-a-real-event-name" });
|
|
7357
|
+
assert(out.length > 0, "silently dropped an unrecognised frame (the #542 shape)");
|
|
7358
|
+
assert(out.every((f) => f.kind === "unknown"), "an unrecognised frame must translate to kind 'unknown'");
|
|
7359
|
+
}
|
|
7360
|
+
},
|
|
7361
|
+
{
|
|
7362
|
+
name: `${adapter.name}: recognises its own samples`,
|
|
7363
|
+
run: () => {
|
|
7364
|
+
for (const s of samples) {
|
|
7365
|
+
const out = call(s);
|
|
7366
|
+
assert(out.length > 0, `produced nothing for its own sample ${JSON.stringify(s)}`);
|
|
7367
|
+
assert(out.some((f) => f.kind !== "unknown"), `failed to recognise its own sample ${JSON.stringify(s)}`);
|
|
7368
|
+
}
|
|
7369
|
+
}
|
|
7370
|
+
},
|
|
7371
|
+
{
|
|
7372
|
+
name: `${adapter.name}: declares a constant origin`,
|
|
7373
|
+
run: () => {
|
|
7374
|
+
assert(adapter.origin === "agent" || adapter.origin === "scan" || adapter.origin === "inferred", `invalid origin "${String(adapter.origin)}"`);
|
|
7375
|
+
}
|
|
7376
|
+
}
|
|
7377
|
+
];
|
|
7378
|
+
}
|
|
7379
|
+
var GARBAGE;
|
|
7380
|
+
var init_conformance = __esm({
|
|
7381
|
+
"packages/core/dist/events/conformance.js"() {
|
|
7382
|
+
GARBAGE = [
|
|
7383
|
+
null,
|
|
7384
|
+
void 0,
|
|
7385
|
+
0,
|
|
7386
|
+
"",
|
|
7387
|
+
"not json",
|
|
7388
|
+
[],
|
|
7389
|
+
{},
|
|
7390
|
+
{ type: 42 },
|
|
7391
|
+
{ type: "definitely-not-a-real-event-name" }
|
|
7392
|
+
];
|
|
7393
|
+
}
|
|
7394
|
+
});
|
|
7395
|
+
|
|
7396
|
+
// packages/core/dist/events/source.js
|
|
7397
|
+
function createEventsSource(opts) {
|
|
7398
|
+
const now = opts.now ?? (() => Date.now());
|
|
7399
|
+
const log = new FactLog({ capacity: opts.capacity });
|
|
7400
|
+
const adapters = new Map(opts.adapters.map((a) => [a.name, a]));
|
|
7401
|
+
const traces = /* @__PURE__ */ new Map();
|
|
7402
|
+
const seqs = /* @__PURE__ */ new Map();
|
|
7403
|
+
let deps;
|
|
7404
|
+
const traceFor = (taskId) => {
|
|
7405
|
+
let t = traces.get(taskId);
|
|
7406
|
+
if (!t) {
|
|
7407
|
+
t = freshTrace();
|
|
7408
|
+
traces.set(taskId, t);
|
|
7409
|
+
}
|
|
7410
|
+
return t;
|
|
7411
|
+
};
|
|
7412
|
+
const nextSeq = (taskId) => {
|
|
7413
|
+
const n = seqs.get(taskId) ?? 0;
|
|
7414
|
+
seqs.set(taskId, n + 1);
|
|
7415
|
+
return n;
|
|
7416
|
+
};
|
|
7417
|
+
return {
|
|
7418
|
+
name: "events",
|
|
7419
|
+
start(d) {
|
|
7420
|
+
deps = d;
|
|
7421
|
+
},
|
|
7422
|
+
stop() {
|
|
7423
|
+
deps = void 0;
|
|
7424
|
+
},
|
|
7425
|
+
health() {
|
|
7426
|
+
return { active: deps !== void 0, error: null };
|
|
7427
|
+
},
|
|
7428
|
+
recent(taskId) {
|
|
7429
|
+
return log.recent(taskId);
|
|
7430
|
+
},
|
|
7431
|
+
dump(taskId) {
|
|
7432
|
+
return log.serialize(taskId);
|
|
7433
|
+
},
|
|
7434
|
+
ingest(source, raw, hint) {
|
|
7435
|
+
const adapter = adapters.get(source);
|
|
7436
|
+
if (!adapter || !deps)
|
|
7437
|
+
return;
|
|
7438
|
+
const rec = deps.resolve(hint);
|
|
7439
|
+
if (!rec)
|
|
7440
|
+
return;
|
|
7441
|
+
const taskId = rec.id;
|
|
7442
|
+
const at = now();
|
|
7443
|
+
let produced;
|
|
7444
|
+
try {
|
|
7445
|
+
const out = adapter.translate(raw);
|
|
7446
|
+
produced = Array.isArray(out) ? out : [{ kind: "unknown", name: `${source} returned non-array` }];
|
|
7447
|
+
} catch (e) {
|
|
7448
|
+
opts.log?.(`events: adapter ${source} threw: ${String(e)}`);
|
|
7449
|
+
produced = [{ kind: "unknown", name: `${source} threw` }];
|
|
7450
|
+
}
|
|
7451
|
+
for (const rawFact of produced) {
|
|
7452
|
+
const fact = stampFact(rawFact, {
|
|
7453
|
+
seq: nextSeq(taskId),
|
|
7454
|
+
taskId,
|
|
7455
|
+
at,
|
|
7456
|
+
source,
|
|
7457
|
+
origin: adapter.origin
|
|
7458
|
+
});
|
|
7459
|
+
log.push(fact);
|
|
7460
|
+
for (const v2 of checkFact(traceFor(taskId), fact, opts.check ?? {})) {
|
|
7461
|
+
opts.onViolation(v2);
|
|
7462
|
+
}
|
|
7463
|
+
for (const ev of toControlEvent(fact))
|
|
7464
|
+
opts.emit(ev);
|
|
7465
|
+
}
|
|
7466
|
+
}
|
|
7467
|
+
};
|
|
7468
|
+
}
|
|
7469
|
+
var init_source = __esm({
|
|
7470
|
+
"packages/core/dist/events/source.js"() {
|
|
7471
|
+
init_fact();
|
|
7472
|
+
init_log();
|
|
7473
|
+
init_invariant();
|
|
7474
|
+
init_to_control_event();
|
|
7475
|
+
}
|
|
7476
|
+
});
|
|
7477
|
+
|
|
7111
7478
|
// packages/core/dist/index.js
|
|
7112
7479
|
var dist_exports2 = {};
|
|
7113
7480
|
__export(dist_exports2, {
|
|
@@ -7122,6 +7489,7 @@ __export(dist_exports2, {
|
|
|
7122
7489
|
DEFAULT_TASK_TIMEOUT_MS: () => DEFAULT_TASK_TIMEOUT_MS,
|
|
7123
7490
|
DeferDelivery: () => DeferDelivery,
|
|
7124
7491
|
FIRST_TURN_INLINE_MAX_BYTES: () => FIRST_TURN_INLINE_MAX_BYTES,
|
|
7492
|
+
FactLog: () => FactLog,
|
|
7125
7493
|
GROUP_DISPATCH_WARMUP_POLL_MS: () => GROUP_DISPATCH_WARMUP_POLL_MS,
|
|
7126
7494
|
GROUP_DISPATCH_WARMUP_TIMEOUT_MS: () => GROUP_DISPATCH_WARMUP_TIMEOUT_MS,
|
|
7127
7495
|
IDLE_DEBOUNCE_MS: () => IDLE_DEBOUNCE_MS,
|
|
@@ -7151,6 +7519,7 @@ __export(dist_exports2, {
|
|
|
7151
7519
|
capAllowed: () => capAllowed,
|
|
7152
7520
|
capOutput: () => capOutput,
|
|
7153
7521
|
captainSocketPath: () => captainSocketPath,
|
|
7522
|
+
checkFact: () => checkFact,
|
|
7154
7523
|
classifyHealth: () => classifyHealth,
|
|
7155
7524
|
closeWorkItem: () => closeWorkItem,
|
|
7156
7525
|
computeTemplateHash: () => computeTemplateHash,
|
|
@@ -7163,6 +7532,7 @@ __export(dist_exports2, {
|
|
|
7163
7532
|
createDirectCrewPaneReader: () => createDirectCrewPaneReader,
|
|
7164
7533
|
createDirectSurfaceLivenessProbe: () => createDirectSurfaceLivenessProbe,
|
|
7165
7534
|
createEnsureCaptainAlive: () => createEnsureCaptainAlive,
|
|
7535
|
+
createEventsSource: () => createEventsSource,
|
|
7166
7536
|
createInteractiveProbe: () => createInteractiveProbe,
|
|
7167
7537
|
createIsCaptainAlive: () => createIsCaptainAlive,
|
|
7168
7538
|
createLaunch: () => createLaunch,
|
|
@@ -7205,6 +7575,7 @@ __export(dist_exports2, {
|
|
|
7205
7575
|
formatInbound: () => formatInbound,
|
|
7206
7576
|
formatInboundReceipt: () => formatInboundReceipt,
|
|
7207
7577
|
formatLifecycle: () => formatLifecycle,
|
|
7578
|
+
freshTrace: () => freshTrace,
|
|
7208
7579
|
getDaemonPid: () => getDaemonPid,
|
|
7209
7580
|
healCmdFor: () => healCmdFor,
|
|
7210
7581
|
isAuthorized: () => isAuthorized,
|
|
@@ -7261,6 +7632,7 @@ __export(dist_exports2, {
|
|
|
7261
7632
|
resolveSetupUserId: () => resolveSetupUserId,
|
|
7262
7633
|
restartDaemonIfRunning: () => restartDaemonIfRunning,
|
|
7263
7634
|
rotateIfNeeded: () => rotateIfNeeded,
|
|
7635
|
+
runAdapterConformance: () => runAdapterConformance,
|
|
7264
7636
|
runCrewAnswer: () => runCrewAnswer,
|
|
7265
7637
|
runCrewClose: () => runCrewClose,
|
|
7266
7638
|
runCrewList: () => runCrewList,
|
|
@@ -7295,12 +7667,14 @@ __export(dist_exports2, {
|
|
|
7295
7667
|
sideNameFromTitle: () => sideNameFromTitle,
|
|
7296
7668
|
sideNextAutoName: () => sideNextAutoName,
|
|
7297
7669
|
sideTitleFor: () => sideTitleFor,
|
|
7670
|
+
stampFact: () => stampFact,
|
|
7298
7671
|
startDaemon: () => startDaemon,
|
|
7299
7672
|
startServer: () => startServer,
|
|
7300
7673
|
stripBotMention: () => stripBotMention,
|
|
7301
7674
|
surfaceVerdict: () => surfaceVerdict,
|
|
7302
7675
|
timeoutGate: () => timeoutGate,
|
|
7303
7676
|
titleFor: () => titleFor,
|
|
7677
|
+
toControlEvent: () => toControlEvent,
|
|
7304
7678
|
topicKey: () => topicKey,
|
|
7305
7679
|
topicName: () => topicName,
|
|
7306
7680
|
tryAcquireDaemonLock: () => tryAcquireDaemonLock,
|
|
@@ -7347,6 +7721,12 @@ var init_dist2 = __esm({
|
|
|
7347
7721
|
init_crew_spawn();
|
|
7348
7722
|
init_crew_answer();
|
|
7349
7723
|
init_lifecycle_source();
|
|
7724
|
+
init_fact();
|
|
7725
|
+
init_log();
|
|
7726
|
+
init_invariant();
|
|
7727
|
+
init_to_control_event();
|
|
7728
|
+
init_conformance();
|
|
7729
|
+
init_source();
|
|
7350
7730
|
init_control_channel();
|
|
7351
7731
|
init_captain_channel();
|
|
7352
7732
|
}
|
|
@@ -10308,9 +10688,9 @@ ${directive}` : directive;
|
|
|
10308
10688
|
function withTimeout(p, ms, msg) {
|
|
10309
10689
|
return new Promise((resolve4, reject) => {
|
|
10310
10690
|
const t = setTimeout(() => reject(new Error(msg)), ms);
|
|
10311
|
-
p.then((
|
|
10691
|
+
p.then((v2) => {
|
|
10312
10692
|
clearTimeout(t);
|
|
10313
|
-
resolve4(
|
|
10693
|
+
resolve4(v2);
|
|
10314
10694
|
}, (e) => {
|
|
10315
10695
|
clearTimeout(t);
|
|
10316
10696
|
reject(e);
|
|
@@ -10544,9 +10924,10 @@ var init_driver = __esm({
|
|
|
10544
10924
|
});
|
|
10545
10925
|
|
|
10546
10926
|
// packages/agents/dist/opencode/sse-bridge.js
|
|
10547
|
-
var OpencodeSseBridge;
|
|
10927
|
+
var IGNORED_FRAME, OpencodeSseBridge;
|
|
10548
10928
|
var init_sse_bridge = __esm({
|
|
10549
10929
|
"packages/agents/dist/opencode/sse-bridge.js"() {
|
|
10930
|
+
IGNORED_FRAME = /^(message|storage|file|lsp|installation)\./;
|
|
10550
10931
|
OpencodeSseBridge = class {
|
|
10551
10932
|
controllers = /* @__PURE__ */ new Map();
|
|
10552
10933
|
/** taskId → the crew's opencode server port (for permission-reply POSTs). */
|
|
@@ -10683,6 +11064,10 @@ var init_sse_bridge = __esm({
|
|
|
10683
11064
|
return;
|
|
10684
11065
|
}
|
|
10685
11066
|
if (json?.type === "session.idle") {
|
|
11067
|
+
if (this.deps.ingest) {
|
|
11068
|
+
this.deps.ingest(json, taskId);
|
|
11069
|
+
return;
|
|
11070
|
+
}
|
|
10686
11071
|
this.deps.emit({
|
|
10687
11072
|
type: "task.turn.completed",
|
|
10688
11073
|
id: taskId,
|
|
@@ -10692,6 +11077,10 @@ var init_sse_bridge = __esm({
|
|
|
10692
11077
|
const p = json.properties;
|
|
10693
11078
|
if (p?.id && p?.sessionID) {
|
|
10694
11079
|
this.pendingPermByTask.set(taskId, { permID: p.id, sessionID: p.sessionID });
|
|
11080
|
+
if (this.deps.ingest) {
|
|
11081
|
+
this.deps.ingest(json, taskId);
|
|
11082
|
+
return;
|
|
11083
|
+
}
|
|
10695
11084
|
const tool = p.permission ?? "a tool";
|
|
10696
11085
|
const cmd = Array.isArray(p.patterns) && p.patterns.length ? `: ${p.patterns.join(" ")}` : "";
|
|
10697
11086
|
this.deps.emit({
|
|
@@ -10701,11 +11090,24 @@ var init_sse_bridge = __esm({
|
|
|
10701
11090
|
question: `opencode requests permission to run ${tool}${cmd}`,
|
|
10702
11091
|
kind: tool
|
|
10703
11092
|
});
|
|
11093
|
+
} else if (this.deps.ingest) {
|
|
11094
|
+
this.deps.ingest(json, taskId);
|
|
10704
11095
|
}
|
|
10705
11096
|
} else if (json?.type === "permission.replied") {
|
|
10706
11097
|
this.pendingPermByTask.delete(taskId);
|
|
11098
|
+
this.deps.ingest?.(json, taskId);
|
|
11099
|
+
} else if (!IGNORED_FRAME.test(json?.type ?? "")) {
|
|
11100
|
+
this.deps.ingest?.(json, taskId);
|
|
10707
11101
|
}
|
|
10708
11102
|
}
|
|
11103
|
+
/** Test seam: exercise handleLine without an SSE stream. */
|
|
11104
|
+
handleLineForTest(rawLine, taskId) {
|
|
11105
|
+
this.handleLine(taskId, rawLine);
|
|
11106
|
+
}
|
|
11107
|
+
/** Test seam: read pendingPermByTask without exposing it publicly. */
|
|
11108
|
+
pendingPermForTest(taskId) {
|
|
11109
|
+
return this.pendingPermByTask.get(taskId);
|
|
11110
|
+
}
|
|
10709
11111
|
};
|
|
10710
11112
|
}
|
|
10711
11113
|
});
|
|
@@ -11705,77 +12107,6 @@ var init_receipt_listener = __esm({
|
|
|
11705
12107
|
}
|
|
11706
12108
|
});
|
|
11707
12109
|
|
|
11708
|
-
// packages/agents/dist/opencode/control-source.js
|
|
11709
|
-
function toSnapshot2(ev) {
|
|
11710
|
-
const now = Date.now();
|
|
11711
|
-
switch (ev.type) {
|
|
11712
|
-
// A permission was answered on the bus and the turn resumed.
|
|
11713
|
-
case "task.started":
|
|
11714
|
-
return { taskId: ev.id, state: "running", alive: true, origin: "agent", at: now };
|
|
11715
|
-
// session.idle — the turn finished. Liveness, NOT completion (anti-#2576).
|
|
11716
|
-
case "task.turn.completed":
|
|
11717
|
-
return { taskId: ev.id, state: "idle", alive: true, origin: "agent", at: now };
|
|
11718
|
-
// permission.asked — opencode STATES it is gated. No guessing from pixels.
|
|
11719
|
-
case "task.approval.requested":
|
|
11720
|
-
return {
|
|
11721
|
-
taskId: ev.id,
|
|
11722
|
-
state: "needsInput",
|
|
11723
|
-
alive: true,
|
|
11724
|
-
origin: "agent",
|
|
11725
|
-
at: now,
|
|
11726
|
-
detail: { note: ev.question, reason: ev.kind }
|
|
11727
|
-
};
|
|
11728
|
-
// Terminal (task.done/blocked/cancelled) and notify-only events are ignored:
|
|
11729
|
-
// terminal state comes exclusively from `squadrant crew signal`.
|
|
11730
|
-
default:
|
|
11731
|
-
return null;
|
|
11732
|
-
}
|
|
11733
|
-
}
|
|
11734
|
-
var OpencodeControlSource;
|
|
11735
|
-
var init_control_source = __esm({
|
|
11736
|
-
"packages/agents/dist/opencode/control-source.js"() {
|
|
11737
|
-
OpencodeControlSource = class {
|
|
11738
|
-
name = "opencode-control";
|
|
11739
|
-
deps;
|
|
11740
|
-
active = false;
|
|
11741
|
-
cache = /* @__PURE__ */ new Map();
|
|
11742
|
-
start(deps) {
|
|
11743
|
-
this.deps = deps;
|
|
11744
|
-
this.active = true;
|
|
11745
|
-
}
|
|
11746
|
-
stop() {
|
|
11747
|
-
this.deps = void 0;
|
|
11748
|
-
this.active = false;
|
|
11749
|
-
this.cache.clear();
|
|
11750
|
-
}
|
|
11751
|
-
/** Push-only source — no fallible startup of its own. */
|
|
11752
|
-
health() {
|
|
11753
|
-
return { active: this.active, error: null };
|
|
11754
|
-
}
|
|
11755
|
-
/** Liveness floor: origin must be "scan" and must not assert needsInput. */
|
|
11756
|
-
snapshot(taskId) {
|
|
11757
|
-
const s = this.cache.get(taskId);
|
|
11758
|
-
if (!s)
|
|
11759
|
-
return void 0;
|
|
11760
|
-
return { ...s, origin: "scan", state: s.state === "needsInput" ? "running" : s.state };
|
|
11761
|
-
}
|
|
11762
|
-
/**
|
|
11763
|
-
* Feed one ControlEvent from OpencodeSseBridge into the port.
|
|
11764
|
-
* Wired in squadrantd.ts as: emit = (ev) => { source.observe(ev); …existing… }
|
|
11765
|
-
*/
|
|
11766
|
-
observe(ev) {
|
|
11767
|
-
if (!this.deps)
|
|
11768
|
-
return;
|
|
11769
|
-
const snap = toSnapshot2(ev);
|
|
11770
|
-
if (!snap)
|
|
11771
|
-
return;
|
|
11772
|
-
this.cache.set(snap.taskId, snap);
|
|
11773
|
-
this.deps.report(snap);
|
|
11774
|
-
}
|
|
11775
|
-
};
|
|
11776
|
-
}
|
|
11777
|
-
});
|
|
11778
|
-
|
|
11779
12110
|
// packages/agents/dist/opencode/http-channel.js
|
|
11780
12111
|
var OpencodeHttpChannel;
|
|
11781
12112
|
var init_http_channel = __esm({
|
|
@@ -11887,6 +12218,47 @@ var init_http_channel = __esm({
|
|
|
11887
12218
|
}
|
|
11888
12219
|
});
|
|
11889
12220
|
|
|
12221
|
+
// packages/agents/dist/opencode/fact-adapter.js
|
|
12222
|
+
function createOpencodeFactAdapter(deps) {
|
|
12223
|
+
return {
|
|
12224
|
+
name: "opencode-sse",
|
|
12225
|
+
origin: "agent",
|
|
12226
|
+
translate(raw) {
|
|
12227
|
+
const f = typeof raw === "object" && raw !== null ? raw : {};
|
|
12228
|
+
const type = typeof f.type === "string" ? f.type : void 0;
|
|
12229
|
+
if (type === void 0)
|
|
12230
|
+
return [{ kind: "unknown", name: "non-object" }];
|
|
12231
|
+
const p = f.properties ?? {};
|
|
12232
|
+
if (type === "session.idle") {
|
|
12233
|
+
return [{
|
|
12234
|
+
kind: "turn.ended",
|
|
12235
|
+
turnId: typeof p.sessionID === "string" ? p.sessionID : void 0
|
|
12236
|
+
}];
|
|
12237
|
+
}
|
|
12238
|
+
if (type === "permission.asked") {
|
|
12239
|
+
if (typeof p.id !== "string" || typeof p.sessionID !== "string") {
|
|
12240
|
+
return [{ kind: "unknown", name: "permission.asked:incomplete" }];
|
|
12241
|
+
}
|
|
12242
|
+
const tool = typeof p.permission === "string" ? p.permission : "a tool";
|
|
12243
|
+
const cmd = Array.isArray(p.patterns) && p.patterns.length ? `: ${p.patterns.join(" ")}` : "";
|
|
12244
|
+
return [{
|
|
12245
|
+
kind: "permission.requested",
|
|
12246
|
+
question: `opencode requests permission to run ${tool}${cmd}`,
|
|
12247
|
+
requestId: deps.nextRequestId(),
|
|
12248
|
+
tool
|
|
12249
|
+
}];
|
|
12250
|
+
}
|
|
12251
|
+
if (type === "permission.replied")
|
|
12252
|
+
return [{ kind: "activity" }];
|
|
12253
|
+
return [{ kind: "unknown", name: type }];
|
|
12254
|
+
}
|
|
12255
|
+
};
|
|
12256
|
+
}
|
|
12257
|
+
var init_fact_adapter = __esm({
|
|
12258
|
+
"packages/agents/dist/opencode/fact-adapter.js"() {
|
|
12259
|
+
}
|
|
12260
|
+
});
|
|
12261
|
+
|
|
11890
12262
|
// packages/agents/dist/index.js
|
|
11891
12263
|
var dist_exports4 = {};
|
|
11892
12264
|
__export(dist_exports4, {
|
|
@@ -11901,7 +12273,6 @@ __export(dist_exports4, {
|
|
|
11901
12273
|
HEADLESS_ERROR_TAIL: () => HEADLESS_ERROR_TAIL,
|
|
11902
12274
|
MARKER_END: () => MARKER_END,
|
|
11903
12275
|
MARKER_START: () => MARKER_START,
|
|
11904
|
-
OpencodeControlSource: () => OpencodeControlSource,
|
|
11905
12276
|
OpencodeHttpChannel: () => OpencodeHttpChannel,
|
|
11906
12277
|
OpencodeSseBridge: () => OpencodeSseBridge,
|
|
11907
12278
|
ProjectionRegistry: () => ProjectionRegistry,
|
|
@@ -11921,6 +12292,7 @@ __export(dist_exports4, {
|
|
|
11921
12292
|
createGeminiEmitter: () => createGeminiEmitter,
|
|
11922
12293
|
createOpencodeDriver: () => createOpencodeDriver,
|
|
11923
12294
|
createOpencodeEmitter: () => createOpencodeEmitter,
|
|
12295
|
+
createOpencodeFactAdapter: () => createOpencodeFactAdapter,
|
|
11924
12296
|
decideCaptainMemoryWrite: () => decideCaptainMemoryWrite,
|
|
11925
12297
|
deriveTranscriptPath: () => deriveTranscriptPath,
|
|
11926
12298
|
detectTrailingQuestion: () => detectTrailingQuestion2,
|
|
@@ -11963,8 +12335,8 @@ var init_dist4 = __esm({
|
|
|
11963
12335
|
init_receipt_listener();
|
|
11964
12336
|
init_peer_wire();
|
|
11965
12337
|
init_registry8();
|
|
11966
|
-
init_control_source();
|
|
11967
12338
|
init_http_channel();
|
|
12339
|
+
init_fact_adapter();
|
|
11968
12340
|
}
|
|
11969
12341
|
});
|
|
11970
12342
|
|
|
@@ -13963,6 +14335,10 @@ async function runCrewSpawn2(input) {
|
|
|
13963
14335
|
emitEvent: async (p, event) => {
|
|
13964
14336
|
await squadrantdCall({ kind: "event", project: p, event });
|
|
13965
14337
|
},
|
|
14338
|
+
// #745: check the daemon's hook-confirmed state before reporting a false
|
|
14339
|
+
// "first turn not delivered" — swallow errors (offline/unreachable daemon)
|
|
14340
|
+
// so this optional check never itself breaks the spawn.
|
|
14341
|
+
getTaskRecord: async (p, id) => await squadrantdCall(buildStatusRequest(p, id)).catch(() => void 0),
|
|
13966
14342
|
onRouted: (route) => console.log(
|
|
13967
14343
|
chalk10.dim(
|
|
13968
14344
|
`routed: tier=${route.tier} \u2192 ${route.agent}${route.model ? `/${route.model}` : ""} (rule: "${route.matchedRule}")`
|
|
@@ -14941,12 +15317,12 @@ function donut(t) {
|
|
|
14941
15317
|
const C = 2 * Math.PI * r;
|
|
14942
15318
|
let acc = 0;
|
|
14943
15319
|
const segs = DONUT_ORDER.map((k) => {
|
|
14944
|
-
const
|
|
14945
|
-
if (
|
|
15320
|
+
const v2 = t[k];
|
|
15321
|
+
if (v2 <= 0)
|
|
14946
15322
|
return "";
|
|
14947
|
-
const len = t.total ?
|
|
15323
|
+
const len = t.total ? v2 / t.total * C : 0;
|
|
14948
15324
|
const rot = t.total ? acc / t.total * 360 : 0;
|
|
14949
|
-
acc +=
|
|
15325
|
+
acc += v2;
|
|
14950
15326
|
return `<circle class="seg s-${k}" cx="${cx}" cy="${cy}" r="${r}" fill="none" stroke-width="${w}" stroke-dasharray="${len.toFixed(2)} ${(C - len).toFixed(2)}" transform="rotate(${(rot - 90).toFixed(2)} ${cx} ${cy})"></circle>`;
|
|
14951
15327
|
}).join("");
|
|
14952
15328
|
const track = `<circle class="donut-track" cx="${cx}" cy="${cy}" r="${r}" fill="none" stroke-width="${w}"></circle>`;
|
|
@@ -15250,7 +15626,7 @@ function renderLiveGrid(snap, now) {
|
|
|
15250
15626
|
const headerLabels = STATE_ORDER.map((s) => `${STATE_LABEL[s]}`);
|
|
15251
15627
|
out.push(`<div class="live-header" data-live-header="">`);
|
|
15252
15628
|
out.push(headerLabels.map((l) => {
|
|
15253
|
-
const raw = Object.entries(STATE_LABEL).find(([,
|
|
15629
|
+
const raw = Object.entries(STATE_LABEL).find(([, v2]) => v2 === l)[0];
|
|
15254
15630
|
const c = stateCounts[raw];
|
|
15255
15631
|
const cls = c === 0 ? "zero" : "";
|
|
15256
15632
|
return `<span class="live-stat ${cls}"><span class="pdot ${STATE_CLS[raw]}"></span>${c} ${l}</span>`;
|
|
@@ -15802,7 +16178,7 @@ async function runDashboardWeb(input) {
|
|
|
15802
16178
|
console.log(chalk14.green(`\u2714 Squadrant system dashboard \u2192 http://127.0.0.1:${handle.port}`));
|
|
15803
16179
|
console.log(chalk14.dim(` polling the daemon every ${input.interval}s \xB7 localhost only \xB7 read-only \xB7 Ctrl-C to stop`));
|
|
15804
16180
|
}
|
|
15805
|
-
var dashboardCommand = new Command13("dashboard").description("Live status grid of all projects (derived from daemon task state)").option("--once", "Print one snapshot and exit (used by --pane's refresh loop)").option("--pane", "Open a refreshing sidebar pane in the current cmux workspace").option("--web", "Serve the live system-health web dashboard on 127.0.0.1 (HTTP + SSE)").option("--port <port>", "Port for --web (default 7878)", (
|
|
16181
|
+
var dashboardCommand = new Command13("dashboard").description("Live status grid of all projects (derived from daemon task state)").option("--once", "Print one snapshot and exit (used by --pane's refresh loop)").option("--pane", "Open a refreshing sidebar pane in the current cmux workspace").option("--web", "Serve the live system-health web dashboard on 127.0.0.1 (HTTP + SSE)").option("--port <port>", "Port for --web (default 7878)", (v2) => parseInt(v2, 10), 7878).option("--direction <dir>", "Pane split direction (right|left|up|down)", "right").option("--interval <seconds>", "Daemon poll interval for --web (default 5); refresh interval for --pane (default 10)", (v2) => parseInt(v2, 10)).action(async (opts) => {
|
|
15806
16182
|
try {
|
|
15807
16183
|
if (opts.web) {
|
|
15808
16184
|
await runDashboardWeb({ port: opts.port, interval: opts.interval ?? 5 });
|
|
@@ -16733,11 +17109,11 @@ import chalk22 from "chalk";
|
|
|
16733
17109
|
import fs23 from "fs";
|
|
16734
17110
|
import path28 from "path";
|
|
16735
17111
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
16736
|
-
function parseScope(
|
|
16737
|
-
if (
|
|
17112
|
+
function parseScope(v2) {
|
|
17113
|
+
if (v2 !== "user" && v2 !== "project") {
|
|
16738
17114
|
throw new Error("--scope must be 'user' or 'project'");
|
|
16739
17115
|
}
|
|
16740
|
-
return
|
|
17116
|
+
return v2;
|
|
16741
17117
|
}
|
|
16742
17118
|
function findPackageRoot3() {
|
|
16743
17119
|
let dir = path28.dirname(fileURLToPath4(import.meta.url));
|
|
@@ -17185,8 +17561,10 @@ async function runHealDaemon(opts) {
|
|
|
17185
17561
|
const { stdout, stderr } = opts;
|
|
17186
17562
|
stdout.write("restarting squadrantd via launchd kickstart...\n");
|
|
17187
17563
|
try {
|
|
17188
|
-
opts.ensureDaemon();
|
|
17189
|
-
|
|
17564
|
+
const result = opts.ensureDaemon();
|
|
17565
|
+
const noteSuffix = result?.note ? ` (${result.note})` : "";
|
|
17566
|
+
stdout.write(chalk24.green(`\u2714 daemon kickstart complete${noteSuffix}
|
|
17567
|
+
`));
|
|
17190
17568
|
return 0;
|
|
17191
17569
|
} catch (e) {
|
|
17192
17570
|
stderr.write(`heal daemon failed: ${e.message}
|
|
@@ -17389,12 +17767,12 @@ async function dispatchAction(toProject, task, opts) {
|
|
|
17389
17767
|
process.exit(1);
|
|
17390
17768
|
}
|
|
17391
17769
|
}
|
|
17392
|
-
var dispatchCommand = new Command25("dispatch").description("Dispatch a task to any registered project (tracked, reports back on settle)").argument("<project>", "Target project name").argument("<task>", "Task description to dispatch").option("--provider <p>", "claude|opencode|codex", "claude").option("--mode <m>", "headless|interactive", "headless").option("--warmup-timeout <s>", "seconds to wait for target captain to boot (same-group only; default: 120)", (
|
|
17770
|
+
var dispatchCommand = new Command25("dispatch").description("Dispatch a task to any registered project (tracked, reports back on settle)").argument("<project>", "Target project name").argument("<task>", "Task description to dispatch").option("--provider <p>", "claude|opencode|codex", "claude").option("--mode <m>", "headless|interactive", "headless").option("--warmup-timeout <s>", "seconds to wait for target captain to boot (same-group only; default: 120)", (v2) => parseInt(v2, 10) * 1e3).action(dispatchAction);
|
|
17393
17771
|
|
|
17394
17772
|
// packages/cli/src/commands/group.ts
|
|
17395
17773
|
init_dist2();
|
|
17396
17774
|
var groupCommand = new Command26("group").description("Cross-project intra-group operations (Phase 1: dispatch)").addCommand(
|
|
17397
|
-
new Command26("dispatch").description("[DEPRECATED \u2014 use 'squadrant dispatch'] Dispatch a task to a sibling project in the same group").argument("<to-project>", "Target project name").argument("<task>", "Task description to dispatch").option("--provider <p>", "claude|opencode|codex", "claude").option("--mode <m>", "headless|interactive", "headless").option("--warmup-timeout <s>", "seconds to wait for target captain to boot (default: 120)", (
|
|
17775
|
+
new Command26("dispatch").description("[DEPRECATED \u2014 use 'squadrant dispatch'] Dispatch a task to a sibling project in the same group").argument("<to-project>", "Target project name").argument("<task>", "Task description to dispatch").option("--provider <p>", "claude|opencode|codex", "claude").option("--mode <m>", "headless|interactive", "headless").option("--warmup-timeout <s>", "seconds to wait for target captain to boot (default: 120)", (v2) => parseInt(v2, 10) * 1e3).action(async (toProject, task, opts) => {
|
|
17398
17776
|
console.error(chalk26.yellow(
|
|
17399
17777
|
`\u26A0 'squadrant group dispatch' is deprecated \u2014 use 'squadrant dispatch <project> "<task>"' instead.`
|
|
17400
17778
|
));
|
|
@@ -18070,7 +18448,7 @@ telegramCommand.command("link").argument("<project>", "project to bind to a Tele
|
|
|
18070
18448
|
const { topicId, created } = await runTelegramLink({ project, cfg, client, stateRoot: defaultStateRoot() });
|
|
18071
18449
|
console.log(chalk32.green(`${created ? "linked" : "already linked"}: ${project} \u2192 topic ${topicId}`));
|
|
18072
18450
|
});
|
|
18073
|
-
telegramCommand.command("setup").description("Interactive wizard \u2014 bot token, validate, auto-detect supergroup, write config").option("--reset-token", "force re-entry of the bot token even if one already exists").option("--redetect", "force group re-detection even when a supergroup is already configured").option("--user-id <id>", "allowlist user-id \u2014 enables remote control on a re-run without getUpdates detection", (
|
|
18451
|
+
telegramCommand.command("setup").description("Interactive wizard \u2014 bot token, validate, auto-detect supergroup, write config").option("--reset-token", "force re-entry of the bot token even if one already exists").option("--redetect", "force group re-detection even when a supergroup is already configured").option("--user-id <id>", "allowlist user-id \u2014 enables remote control on a re-run without getUpdates detection", (v2) => parseInt(v2, 10)).action(async (opts) => {
|
|
18074
18452
|
if (!process.stdin.isTTY) {
|
|
18075
18453
|
console.error(chalk32.red("setup requires a TTY \u2014 pipe input is not supported"));
|
|
18076
18454
|
process.exit(1);
|
|
@@ -18580,7 +18958,7 @@ function printTree(items) {
|
|
|
18580
18958
|
function printFlat(items) {
|
|
18581
18959
|
for (const item of items) printItem(item, 0);
|
|
18582
18960
|
}
|
|
18583
|
-
var startCmd = new Command33("start").description("Start a new work item").argument("<title>", "what you're doing").option("--project <name>", "project this work belongs to (defaults to the current registered project)").option("--parent <id>", "id of the wave/parent item this nests under").option("--tag <tag>", "attach a tag (repeatable)", (
|
|
18961
|
+
var startCmd = new Command33("start").description("Start a new work item").argument("<title>", "what you're doing").option("--project <name>", "project this work belongs to (defaults to the current registered project)").option("--parent <id>", "id of the wave/parent item this nests under").option("--tag <tag>", "attach a tag (repeatable)", (v2, prev) => [...prev, v2], []).action((title, opts) => {
|
|
18584
18962
|
const config = loadConfig();
|
|
18585
18963
|
const store = createWorkStore();
|
|
18586
18964
|
purgeExpiredWorkItems(store);
|
|
@@ -18902,7 +19280,7 @@ function queryClaudeMem(dbPath, project) {
|
|
|
18902
19280
|
createdAt: r.created_at
|
|
18903
19281
|
}));
|
|
18904
19282
|
const candidates = [summaryRow?.created_at, ...decisionRows.map((r) => r.created_at)].filter(
|
|
18905
|
-
(
|
|
19283
|
+
(v2) => !!v2
|
|
18906
19284
|
);
|
|
18907
19285
|
const oldestCreatedAt = candidates.length > 0 ? candidates.reduce((a, b) => a < b ? a : b) : null;
|
|
18908
19286
|
return {
|