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/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,38 @@ 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
|
+
function ensureSocksDir(dir = CC_SOCKS_DIR) {
|
|
4141
|
+
fs9.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
4142
|
+
if ((fs9.statSync(dir).mode & 511) !== 448)
|
|
4143
|
+
fs9.chmodSync(dir, 448);
|
|
4144
|
+
}
|
|
4145
|
+
async function pollFirstTurnConfirmedAt(getTaskRecord, project, id) {
|
|
4146
|
+
const deadline = Date.now() + FIRST_TURN_HOOK_CONFIRM_WINDOW_MS;
|
|
4147
|
+
for (; ; ) {
|
|
4148
|
+
const rec = await getTaskRecord(project, id).catch(() => void 0);
|
|
4149
|
+
if (rec?.firstTurnConfirmedAt)
|
|
4150
|
+
return true;
|
|
4151
|
+
if (Date.now() >= deadline)
|
|
4152
|
+
return false;
|
|
4153
|
+
await new Promise((r) => setTimeout(r, FIRST_TURN_HOOK_POLL_INTERVAL_MS));
|
|
4154
|
+
}
|
|
4155
|
+
}
|
|
4156
|
+
function firstTrueOrBothFalse(a, b) {
|
|
4157
|
+
return new Promise((resolve4) => {
|
|
4158
|
+
let settledFalseCount = 0;
|
|
4159
|
+
const onSettle = (ok2) => {
|
|
4160
|
+
if (ok2) {
|
|
4161
|
+
resolve4(true);
|
|
4162
|
+
return;
|
|
4163
|
+
}
|
|
4164
|
+
settledFalseCount++;
|
|
4165
|
+
if (settledFalseCount === 2)
|
|
4166
|
+
resolve4(false);
|
|
4167
|
+
};
|
|
4168
|
+
a.then(onSettle, () => onSettle(false));
|
|
4169
|
+
b.then(onSettle, () => onSettle(false));
|
|
4170
|
+
});
|
|
4171
|
+
}
|
|
4128
4172
|
async function listCrewPanes(runtime, workspaceId, project) {
|
|
4129
4173
|
const surfaces = await runtime.listSurfaces(workspaceId);
|
|
4130
4174
|
return surfaces.filter((s) => s.title && isCrewTitle(project, s.title));
|
|
@@ -4235,7 +4279,7 @@ async function runCrewSpawn(input, config, deps) {
|
|
|
4235
4279
|
deps.onModelResolved?.({ agentName, model: crewModel });
|
|
4236
4280
|
}
|
|
4237
4281
|
if (agentName === "claude") {
|
|
4238
|
-
|
|
4282
|
+
ensureSocksDir();
|
|
4239
4283
|
const messagingSocketPath = path9.join(CC_SOCKS_DIR, `squadrant-${randomUUID3()}.sock`);
|
|
4240
4284
|
const rec = await deps.dispatchCrew({
|
|
4241
4285
|
provider: "claude",
|
|
@@ -4281,10 +4325,12 @@ async function runCrewSpawn(input, config, deps) {
|
|
|
4281
4325
|
fs9.writeFileSync(spillFile, claudeFirstTurn, "utf8");
|
|
4282
4326
|
claudeFirstTurn = `Full task is at ${spillFile} \u2014 cat it and follow it exactly.`;
|
|
4283
4327
|
}
|
|
4284
|
-
const
|
|
4328
|
+
const sendPromise = deps.sendFirstTurn(pane2, `${claudeFirstTurn}
|
|
4285
4329
|
|
|
4286
4330
|
${buildCompletionProtocol(rec.id, input.project)}`, preLaunchScreen);
|
|
4287
|
-
|
|
4331
|
+
const scrapeDelivered = sendPromise.then((r) => r.delivered).catch(() => false);
|
|
4332
|
+
const delivered = hooksInstalled && deps.getTaskRecord ? await firstTrueOrBothFalse(scrapeDelivered, pollFirstTurnConfirmedAt(deps.getTaskRecord, input.project, rec.id)) : await scrapeDelivered;
|
|
4333
|
+
if (!delivered) {
|
|
4288
4334
|
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
4335
|
`);
|
|
4290
4336
|
} else if (!hooksInstalled) {
|
|
@@ -4395,6 +4441,10 @@ async function runCrewSend(project, name, message, runtime, workspaceId, deps, o
|
|
|
4395
4441
|
const heldForMin = Math.round((Date.now() - task.operatorHold.since) / 6e4);
|
|
4396
4442
|
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
4443
|
}
|
|
4444
|
+
const isAttentionState = task?.state === "blocked" || task?.state === "awaiting-input" || task?.state === "review";
|
|
4445
|
+
if (task && !isAttentionState && task.firstTurnConfirmedAt && task.task === message) {
|
|
4446
|
+
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.`);
|
|
4447
|
+
}
|
|
4398
4448
|
let reopened = false;
|
|
4399
4449
|
try {
|
|
4400
4450
|
if (task) {
|
|
@@ -4561,7 +4611,7 @@ async function runCrewList(project, runtime, workspaceId) {
|
|
|
4561
4611
|
surfaceId: c.surfaceId
|
|
4562
4612
|
}));
|
|
4563
4613
|
}
|
|
4564
|
-
var CC_SOCKS_DIR, FIRST_TURN_INLINE_MAX_BYTES, TEMPLATES_DIR, STATE_ROOT, CLOSE_LOOKUP_RETRIES, CLOSE_LOOKUP_RETRY_DELAY_MS;
|
|
4614
|
+
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
4615
|
var init_crew_spawn = __esm({
|
|
4566
4616
|
"packages/core/dist/crew-spawn.js"() {
|
|
4567
4617
|
init_control_channel();
|
|
@@ -4573,6 +4623,8 @@ var init_crew_spawn = __esm({
|
|
|
4573
4623
|
FIRST_TURN_INLINE_MAX_BYTES = 1200;
|
|
4574
4624
|
TEMPLATES_DIR = path9.join(os4.homedir(), ".config", "squadrant", "templates");
|
|
4575
4625
|
STATE_ROOT = path9.join(os4.homedir(), ".config", "squadrant", "state");
|
|
4626
|
+
FIRST_TURN_HOOK_CONFIRM_WINDOW_MS = 1e5;
|
|
4627
|
+
FIRST_TURN_HOOK_POLL_INTERVAL_MS = 2e3;
|
|
4576
4628
|
CLOSE_LOOKUP_RETRIES = 3;
|
|
4577
4629
|
CLOSE_LOOKUP_RETRY_DELAY_MS = 150;
|
|
4578
4630
|
}
|
|
@@ -4742,9 +4794,9 @@ function createDelivery(ctx, daemonCmux, isSurfaceAlive) {
|
|
|
4742
4794
|
const lastDeferred = /* @__PURE__ */ new Map();
|
|
4743
4795
|
const inFlightDelivery = () => {
|
|
4744
4796
|
let worst2 = null;
|
|
4745
|
-
for (const [project,
|
|
4746
|
-
if (!worst2 ||
|
|
4747
|
-
worst2 = { project, ...
|
|
4797
|
+
for (const [project, v2] of lastDeferred) {
|
|
4798
|
+
if (!worst2 || v2.deferCount > worst2.deferCount)
|
|
4799
|
+
worst2 = { project, ...v2 };
|
|
4748
4800
|
}
|
|
4749
4801
|
return worst2;
|
|
4750
4802
|
};
|
|
@@ -7108,6 +7160,326 @@ var init_lifecycle_source = __esm({
|
|
|
7108
7160
|
}
|
|
7109
7161
|
});
|
|
7110
7162
|
|
|
7163
|
+
// packages/core/dist/events/fact.js
|
|
7164
|
+
function stampFact(raw, id) {
|
|
7165
|
+
return { ...raw, ...id };
|
|
7166
|
+
}
|
|
7167
|
+
var init_fact = __esm({
|
|
7168
|
+
"packages/core/dist/events/fact.js"() {
|
|
7169
|
+
}
|
|
7170
|
+
});
|
|
7171
|
+
|
|
7172
|
+
// packages/core/dist/events/log.js
|
|
7173
|
+
var FactLog;
|
|
7174
|
+
var init_log = __esm({
|
|
7175
|
+
"packages/core/dist/events/log.js"() {
|
|
7176
|
+
FactLog = class {
|
|
7177
|
+
capacity;
|
|
7178
|
+
buffers = /* @__PURE__ */ new Map();
|
|
7179
|
+
constructor(opts = {}) {
|
|
7180
|
+
this.capacity = opts.capacity ?? 256;
|
|
7181
|
+
}
|
|
7182
|
+
push(fact) {
|
|
7183
|
+
let buf = this.buffers.get(fact.taskId);
|
|
7184
|
+
if (!buf) {
|
|
7185
|
+
buf = [];
|
|
7186
|
+
this.buffers.set(fact.taskId, buf);
|
|
7187
|
+
}
|
|
7188
|
+
buf.push(fact);
|
|
7189
|
+
while (buf.length > this.capacity)
|
|
7190
|
+
buf.shift();
|
|
7191
|
+
}
|
|
7192
|
+
/** Oldest-first snapshot. A fresh array; later pushes never grow it. */
|
|
7193
|
+
recent(taskId) {
|
|
7194
|
+
return [...this.buffers.get(taskId) ?? []];
|
|
7195
|
+
}
|
|
7196
|
+
/** Newline-delimited JSON, one fact per line, oldest first. */
|
|
7197
|
+
serialize(taskId) {
|
|
7198
|
+
return this.recent(taskId).map((f) => JSON.stringify(f)).join("\n") + "\n";
|
|
7199
|
+
}
|
|
7200
|
+
/** Release a finished crew's buffer. */
|
|
7201
|
+
drop(taskId) {
|
|
7202
|
+
this.buffers.delete(taskId);
|
|
7203
|
+
}
|
|
7204
|
+
};
|
|
7205
|
+
}
|
|
7206
|
+
});
|
|
7207
|
+
|
|
7208
|
+
// packages/core/dist/events/invariant.js
|
|
7209
|
+
function freshTrace() {
|
|
7210
|
+
return {
|
|
7211
|
+
depth: 0,
|
|
7212
|
+
oldestOpenAt: null,
|
|
7213
|
+
stallReported: false,
|
|
7214
|
+
unknownSeen: 0,
|
|
7215
|
+
liveness: /* @__PURE__ */ new Map()
|
|
7216
|
+
};
|
|
7217
|
+
}
|
|
7218
|
+
function checkFact(trace, fact, opts) {
|
|
7219
|
+
const out = [];
|
|
7220
|
+
if (fact.origin === "inferred" && TERMINALISING.has(fact.kind)) {
|
|
7221
|
+
out.push(v("I4", `inferred fact "${fact.kind}" from ${fact.source} cannot terminalise alone`, fact));
|
|
7222
|
+
}
|
|
7223
|
+
switch (fact.kind) {
|
|
7224
|
+
case "tool.opened":
|
|
7225
|
+
if (trace.depth === 0) {
|
|
7226
|
+
trace.oldestOpenAt = fact.at;
|
|
7227
|
+
trace.stallReported = false;
|
|
7228
|
+
}
|
|
7229
|
+
trace.depth += 1;
|
|
7230
|
+
break;
|
|
7231
|
+
case "tool.closed":
|
|
7232
|
+
if (trace.depth === 0) {
|
|
7233
|
+
out.push(v("I1", `tool.closed from ${fact.source} with no open tool`, fact));
|
|
7234
|
+
} else {
|
|
7235
|
+
trace.depth -= 1;
|
|
7236
|
+
if (trace.depth === 0) {
|
|
7237
|
+
trace.oldestOpenAt = null;
|
|
7238
|
+
trace.stallReported = false;
|
|
7239
|
+
}
|
|
7240
|
+
}
|
|
7241
|
+
break;
|
|
7242
|
+
case "turn.ended":
|
|
7243
|
+
if (trace.depth > 0) {
|
|
7244
|
+
out.push(v("I2", `turn.ended with ${trace.depth} tool call(s) still open`, fact));
|
|
7245
|
+
trace.depth = 0;
|
|
7246
|
+
trace.oldestOpenAt = null;
|
|
7247
|
+
trace.stallReported = false;
|
|
7248
|
+
}
|
|
7249
|
+
break;
|
|
7250
|
+
case "unknown":
|
|
7251
|
+
trace.unknownSeen += 1;
|
|
7252
|
+
out.push(v("I5", `unrecognised frame "${fact.name}" from ${fact.source}`, fact));
|
|
7253
|
+
break;
|
|
7254
|
+
case "process.observed": {
|
|
7255
|
+
const prior = [...trace.liveness.entries()].find(([src, s]) => src !== fact.source && s.alive !== fact.alive && fact.at - s.at <= (opts.disagreeWindowMs ?? -1));
|
|
7256
|
+
if (prior) {
|
|
7257
|
+
out.push(v("I6", `liveness disagreement: ${prior[0]} said alive=${prior[1].alive}, ${fact.source} says alive=${fact.alive}`, fact));
|
|
7258
|
+
}
|
|
7259
|
+
trace.liveness.set(fact.source, { alive: fact.alive, at: fact.at });
|
|
7260
|
+
break;
|
|
7261
|
+
}
|
|
7262
|
+
default:
|
|
7263
|
+
break;
|
|
7264
|
+
}
|
|
7265
|
+
if (opts.stallBudgetMs !== void 0 && trace.depth > 0 && trace.oldestOpenAt !== null && !trace.stallReported && fact.at - trace.oldestOpenAt > opts.stallBudgetMs) {
|
|
7266
|
+
trace.stallReported = true;
|
|
7267
|
+
out.push(v("I3", `tool open for ${fact.at - trace.oldestOpenAt}ms, past the stall budget`, fact));
|
|
7268
|
+
}
|
|
7269
|
+
return out;
|
|
7270
|
+
}
|
|
7271
|
+
var v, TERMINALISING;
|
|
7272
|
+
var init_invariant = __esm({
|
|
7273
|
+
"packages/core/dist/events/invariant.js"() {
|
|
7274
|
+
v = (code, message, f) => ({ code, message, taskId: f.taskId, at: f.at });
|
|
7275
|
+
TERMINALISING = /* @__PURE__ */ new Set(["session.ended"]);
|
|
7276
|
+
}
|
|
7277
|
+
});
|
|
7278
|
+
|
|
7279
|
+
// packages/core/dist/events/to-control-event.js
|
|
7280
|
+
function toControlEvent(fact) {
|
|
7281
|
+
if (fact.origin === "inferred" && TERMINALISING2.has(fact.kind))
|
|
7282
|
+
return [];
|
|
7283
|
+
switch (fact.kind) {
|
|
7284
|
+
case "turn.ended":
|
|
7285
|
+
return [{ type: "task.turn.completed", id: fact.taskId, turnId: fact.turnId ?? fact.source }];
|
|
7286
|
+
case "permission.requested":
|
|
7287
|
+
return [{
|
|
7288
|
+
type: "task.approval.requested",
|
|
7289
|
+
id: fact.taskId,
|
|
7290
|
+
requestId: fact.requestId,
|
|
7291
|
+
question: fact.question,
|
|
7292
|
+
kind: fact.tool
|
|
7293
|
+
}];
|
|
7294
|
+
case "input.requested":
|
|
7295
|
+
return [{
|
|
7296
|
+
type: "task.input.requested",
|
|
7297
|
+
id: fact.taskId,
|
|
7298
|
+
requestId: fact.requestId,
|
|
7299
|
+
question: fact.question
|
|
7300
|
+
}];
|
|
7301
|
+
case "session.ended":
|
|
7302
|
+
return [{ type: "task.session.ended", id: fact.taskId }];
|
|
7303
|
+
case "session.started":
|
|
7304
|
+
return [{
|
|
7305
|
+
type: "task.started",
|
|
7306
|
+
id: fact.taskId,
|
|
7307
|
+
...fact.pid === void 0 ? {} : { pid: fact.pid },
|
|
7308
|
+
...fact.sessionId === void 0 ? {} : { sessionId: fact.sessionId }
|
|
7309
|
+
}];
|
|
7310
|
+
case "prompt.submitted":
|
|
7311
|
+
return [{ type: "task.first-turn.confirmed", id: fact.taskId }];
|
|
7312
|
+
// Liveness-only. The facade still feeds these to reduceLifecycle; they
|
|
7313
|
+
// simply carry no ControlEvent of their own.
|
|
7314
|
+
case "tool.opened":
|
|
7315
|
+
case "tool.closed":
|
|
7316
|
+
case "activity":
|
|
7317
|
+
case "process.observed":
|
|
7318
|
+
case "unknown":
|
|
7319
|
+
return [];
|
|
7320
|
+
}
|
|
7321
|
+
}
|
|
7322
|
+
var TERMINALISING2;
|
|
7323
|
+
var init_to_control_event = __esm({
|
|
7324
|
+
"packages/core/dist/events/to-control-event.js"() {
|
|
7325
|
+
TERMINALISING2 = /* @__PURE__ */ new Set(["session.ended"]);
|
|
7326
|
+
}
|
|
7327
|
+
});
|
|
7328
|
+
|
|
7329
|
+
// packages/core/dist/events/conformance.js
|
|
7330
|
+
function assert(cond, msg) {
|
|
7331
|
+
if (!cond)
|
|
7332
|
+
throw new Error(`conformance: ${msg}`);
|
|
7333
|
+
}
|
|
7334
|
+
function runAdapterConformance(adapter, samples) {
|
|
7335
|
+
const call = (raw) => adapter.translate(raw);
|
|
7336
|
+
return [
|
|
7337
|
+
{
|
|
7338
|
+
name: `${adapter.name}: never throws on garbage`,
|
|
7339
|
+
run: () => {
|
|
7340
|
+
for (const g of GARBAGE) {
|
|
7341
|
+
try {
|
|
7342
|
+
call(g);
|
|
7343
|
+
} catch (e) {
|
|
7344
|
+
throw new Error(`threw on ${JSON.stringify(g)}: ${String(e)}`);
|
|
7345
|
+
}
|
|
7346
|
+
}
|
|
7347
|
+
}
|
|
7348
|
+
},
|
|
7349
|
+
{
|
|
7350
|
+
name: `${adapter.name}: never returns null or undefined`,
|
|
7351
|
+
run: () => {
|
|
7352
|
+
for (const g of [...GARBAGE, ...samples]) {
|
|
7353
|
+
const out = call(g);
|
|
7354
|
+
assert(Array.isArray(out), `returned a non-array for ${JSON.stringify(g)}`);
|
|
7355
|
+
}
|
|
7356
|
+
}
|
|
7357
|
+
},
|
|
7358
|
+
{
|
|
7359
|
+
name: `${adapter.name}: an unrecognised frame yields unknown, not an empty array`,
|
|
7360
|
+
run: () => {
|
|
7361
|
+
const out = call({ type: "definitely-not-a-real-event-name" });
|
|
7362
|
+
assert(out.length > 0, "silently dropped an unrecognised frame (the #542 shape)");
|
|
7363
|
+
assert(out.every((f) => f.kind === "unknown"), "an unrecognised frame must translate to kind 'unknown'");
|
|
7364
|
+
}
|
|
7365
|
+
},
|
|
7366
|
+
{
|
|
7367
|
+
name: `${adapter.name}: recognises its own samples`,
|
|
7368
|
+
run: () => {
|
|
7369
|
+
for (const s of samples) {
|
|
7370
|
+
const out = call(s);
|
|
7371
|
+
assert(out.length > 0, `produced nothing for its own sample ${JSON.stringify(s)}`);
|
|
7372
|
+
assert(out.some((f) => f.kind !== "unknown"), `failed to recognise its own sample ${JSON.stringify(s)}`);
|
|
7373
|
+
}
|
|
7374
|
+
}
|
|
7375
|
+
},
|
|
7376
|
+
{
|
|
7377
|
+
name: `${adapter.name}: declares a constant origin`,
|
|
7378
|
+
run: () => {
|
|
7379
|
+
assert(adapter.origin === "agent" || adapter.origin === "scan" || adapter.origin === "inferred", `invalid origin "${String(adapter.origin)}"`);
|
|
7380
|
+
}
|
|
7381
|
+
}
|
|
7382
|
+
];
|
|
7383
|
+
}
|
|
7384
|
+
var GARBAGE;
|
|
7385
|
+
var init_conformance = __esm({
|
|
7386
|
+
"packages/core/dist/events/conformance.js"() {
|
|
7387
|
+
GARBAGE = [
|
|
7388
|
+
null,
|
|
7389
|
+
void 0,
|
|
7390
|
+
0,
|
|
7391
|
+
"",
|
|
7392
|
+
"not json",
|
|
7393
|
+
[],
|
|
7394
|
+
{},
|
|
7395
|
+
{ type: 42 },
|
|
7396
|
+
{ type: "definitely-not-a-real-event-name" }
|
|
7397
|
+
];
|
|
7398
|
+
}
|
|
7399
|
+
});
|
|
7400
|
+
|
|
7401
|
+
// packages/core/dist/events/source.js
|
|
7402
|
+
function createEventsSource(opts) {
|
|
7403
|
+
const now = opts.now ?? (() => Date.now());
|
|
7404
|
+
const log = new FactLog({ capacity: opts.capacity });
|
|
7405
|
+
const adapters = new Map(opts.adapters.map((a) => [a.name, a]));
|
|
7406
|
+
const traces = /* @__PURE__ */ new Map();
|
|
7407
|
+
const seqs = /* @__PURE__ */ new Map();
|
|
7408
|
+
let deps;
|
|
7409
|
+
const traceFor = (taskId) => {
|
|
7410
|
+
let t = traces.get(taskId);
|
|
7411
|
+
if (!t) {
|
|
7412
|
+
t = freshTrace();
|
|
7413
|
+
traces.set(taskId, t);
|
|
7414
|
+
}
|
|
7415
|
+
return t;
|
|
7416
|
+
};
|
|
7417
|
+
const nextSeq = (taskId) => {
|
|
7418
|
+
const n = seqs.get(taskId) ?? 0;
|
|
7419
|
+
seqs.set(taskId, n + 1);
|
|
7420
|
+
return n;
|
|
7421
|
+
};
|
|
7422
|
+
return {
|
|
7423
|
+
name: "events",
|
|
7424
|
+
start(d) {
|
|
7425
|
+
deps = d;
|
|
7426
|
+
},
|
|
7427
|
+
stop() {
|
|
7428
|
+
deps = void 0;
|
|
7429
|
+
},
|
|
7430
|
+
health() {
|
|
7431
|
+
return { active: deps !== void 0, error: null };
|
|
7432
|
+
},
|
|
7433
|
+
recent(taskId) {
|
|
7434
|
+
return log.recent(taskId);
|
|
7435
|
+
},
|
|
7436
|
+
dump(taskId) {
|
|
7437
|
+
return log.serialize(taskId);
|
|
7438
|
+
},
|
|
7439
|
+
ingest(source, raw, hint) {
|
|
7440
|
+
const adapter = adapters.get(source);
|
|
7441
|
+
if (!adapter || !deps)
|
|
7442
|
+
return;
|
|
7443
|
+
const rec = deps.resolve(hint);
|
|
7444
|
+
if (!rec)
|
|
7445
|
+
return;
|
|
7446
|
+
const taskId = rec.id;
|
|
7447
|
+
const at = now();
|
|
7448
|
+
let produced;
|
|
7449
|
+
try {
|
|
7450
|
+
const out = adapter.translate(raw);
|
|
7451
|
+
produced = Array.isArray(out) ? out : [{ kind: "unknown", name: `${source} returned non-array` }];
|
|
7452
|
+
} catch (e) {
|
|
7453
|
+
opts.log?.(`events: adapter ${source} threw: ${String(e)}`);
|
|
7454
|
+
produced = [{ kind: "unknown", name: `${source} threw` }];
|
|
7455
|
+
}
|
|
7456
|
+
for (const rawFact of produced) {
|
|
7457
|
+
const fact = stampFact(rawFact, {
|
|
7458
|
+
seq: nextSeq(taskId),
|
|
7459
|
+
taskId,
|
|
7460
|
+
at,
|
|
7461
|
+
source,
|
|
7462
|
+
origin: adapter.origin
|
|
7463
|
+
});
|
|
7464
|
+
log.push(fact);
|
|
7465
|
+
for (const v2 of checkFact(traceFor(taskId), fact, opts.check ?? {})) {
|
|
7466
|
+
opts.onViolation(v2);
|
|
7467
|
+
}
|
|
7468
|
+
for (const ev of toControlEvent(fact))
|
|
7469
|
+
opts.emit(ev);
|
|
7470
|
+
}
|
|
7471
|
+
}
|
|
7472
|
+
};
|
|
7473
|
+
}
|
|
7474
|
+
var init_source = __esm({
|
|
7475
|
+
"packages/core/dist/events/source.js"() {
|
|
7476
|
+
init_fact();
|
|
7477
|
+
init_log();
|
|
7478
|
+
init_invariant();
|
|
7479
|
+
init_to_control_event();
|
|
7480
|
+
}
|
|
7481
|
+
});
|
|
7482
|
+
|
|
7111
7483
|
// packages/core/dist/index.js
|
|
7112
7484
|
var dist_exports2 = {};
|
|
7113
7485
|
__export(dist_exports2, {
|
|
@@ -7122,6 +7494,7 @@ __export(dist_exports2, {
|
|
|
7122
7494
|
DEFAULT_TASK_TIMEOUT_MS: () => DEFAULT_TASK_TIMEOUT_MS,
|
|
7123
7495
|
DeferDelivery: () => DeferDelivery,
|
|
7124
7496
|
FIRST_TURN_INLINE_MAX_BYTES: () => FIRST_TURN_INLINE_MAX_BYTES,
|
|
7497
|
+
FactLog: () => FactLog,
|
|
7125
7498
|
GROUP_DISPATCH_WARMUP_POLL_MS: () => GROUP_DISPATCH_WARMUP_POLL_MS,
|
|
7126
7499
|
GROUP_DISPATCH_WARMUP_TIMEOUT_MS: () => GROUP_DISPATCH_WARMUP_TIMEOUT_MS,
|
|
7127
7500
|
IDLE_DEBOUNCE_MS: () => IDLE_DEBOUNCE_MS,
|
|
@@ -7151,6 +7524,7 @@ __export(dist_exports2, {
|
|
|
7151
7524
|
capAllowed: () => capAllowed,
|
|
7152
7525
|
capOutput: () => capOutput,
|
|
7153
7526
|
captainSocketPath: () => captainSocketPath,
|
|
7527
|
+
checkFact: () => checkFact,
|
|
7154
7528
|
classifyHealth: () => classifyHealth,
|
|
7155
7529
|
closeWorkItem: () => closeWorkItem,
|
|
7156
7530
|
computeTemplateHash: () => computeTemplateHash,
|
|
@@ -7163,6 +7537,7 @@ __export(dist_exports2, {
|
|
|
7163
7537
|
createDirectCrewPaneReader: () => createDirectCrewPaneReader,
|
|
7164
7538
|
createDirectSurfaceLivenessProbe: () => createDirectSurfaceLivenessProbe,
|
|
7165
7539
|
createEnsureCaptainAlive: () => createEnsureCaptainAlive,
|
|
7540
|
+
createEventsSource: () => createEventsSource,
|
|
7166
7541
|
createInteractiveProbe: () => createInteractiveProbe,
|
|
7167
7542
|
createIsCaptainAlive: () => createIsCaptainAlive,
|
|
7168
7543
|
createLaunch: () => createLaunch,
|
|
@@ -7194,6 +7569,7 @@ __export(dist_exports2, {
|
|
|
7194
7569
|
encodeFrame: () => encodeFrame,
|
|
7195
7570
|
encodeMsg: () => encodeMsg,
|
|
7196
7571
|
ensureDaemon: () => ensureDaemon,
|
|
7572
|
+
ensureSocksDir: () => ensureSocksDir,
|
|
7197
7573
|
evaluateStall: () => evaluateStall,
|
|
7198
7574
|
exitMarkerPath: () => exitMarkerPath,
|
|
7199
7575
|
fallsBackToPane: () => fallsBackToPane,
|
|
@@ -7205,6 +7581,7 @@ __export(dist_exports2, {
|
|
|
7205
7581
|
formatInbound: () => formatInbound,
|
|
7206
7582
|
formatInboundReceipt: () => formatInboundReceipt,
|
|
7207
7583
|
formatLifecycle: () => formatLifecycle,
|
|
7584
|
+
freshTrace: () => freshTrace,
|
|
7208
7585
|
getDaemonPid: () => getDaemonPid,
|
|
7209
7586
|
healCmdFor: () => healCmdFor,
|
|
7210
7587
|
isAuthorized: () => isAuthorized,
|
|
@@ -7261,6 +7638,7 @@ __export(dist_exports2, {
|
|
|
7261
7638
|
resolveSetupUserId: () => resolveSetupUserId,
|
|
7262
7639
|
restartDaemonIfRunning: () => restartDaemonIfRunning,
|
|
7263
7640
|
rotateIfNeeded: () => rotateIfNeeded,
|
|
7641
|
+
runAdapterConformance: () => runAdapterConformance,
|
|
7264
7642
|
runCrewAnswer: () => runCrewAnswer,
|
|
7265
7643
|
runCrewClose: () => runCrewClose,
|
|
7266
7644
|
runCrewList: () => runCrewList,
|
|
@@ -7295,12 +7673,14 @@ __export(dist_exports2, {
|
|
|
7295
7673
|
sideNameFromTitle: () => sideNameFromTitle,
|
|
7296
7674
|
sideNextAutoName: () => sideNextAutoName,
|
|
7297
7675
|
sideTitleFor: () => sideTitleFor,
|
|
7676
|
+
stampFact: () => stampFact,
|
|
7298
7677
|
startDaemon: () => startDaemon,
|
|
7299
7678
|
startServer: () => startServer,
|
|
7300
7679
|
stripBotMention: () => stripBotMention,
|
|
7301
7680
|
surfaceVerdict: () => surfaceVerdict,
|
|
7302
7681
|
timeoutGate: () => timeoutGate,
|
|
7303
7682
|
titleFor: () => titleFor,
|
|
7683
|
+
toControlEvent: () => toControlEvent,
|
|
7304
7684
|
topicKey: () => topicKey,
|
|
7305
7685
|
topicName: () => topicName,
|
|
7306
7686
|
tryAcquireDaemonLock: () => tryAcquireDaemonLock,
|
|
@@ -7347,6 +7727,12 @@ var init_dist2 = __esm({
|
|
|
7347
7727
|
init_crew_spawn();
|
|
7348
7728
|
init_crew_answer();
|
|
7349
7729
|
init_lifecycle_source();
|
|
7730
|
+
init_fact();
|
|
7731
|
+
init_log();
|
|
7732
|
+
init_invariant();
|
|
7733
|
+
init_to_control_event();
|
|
7734
|
+
init_conformance();
|
|
7735
|
+
init_source();
|
|
7350
7736
|
init_control_channel();
|
|
7351
7737
|
init_captain_channel();
|
|
7352
7738
|
}
|
|
@@ -10308,9 +10694,9 @@ ${directive}` : directive;
|
|
|
10308
10694
|
function withTimeout(p, ms, msg) {
|
|
10309
10695
|
return new Promise((resolve4, reject) => {
|
|
10310
10696
|
const t = setTimeout(() => reject(new Error(msg)), ms);
|
|
10311
|
-
p.then((
|
|
10697
|
+
p.then((v2) => {
|
|
10312
10698
|
clearTimeout(t);
|
|
10313
|
-
resolve4(
|
|
10699
|
+
resolve4(v2);
|
|
10314
10700
|
}, (e) => {
|
|
10315
10701
|
clearTimeout(t);
|
|
10316
10702
|
reject(e);
|
|
@@ -10544,9 +10930,10 @@ var init_driver = __esm({
|
|
|
10544
10930
|
});
|
|
10545
10931
|
|
|
10546
10932
|
// packages/agents/dist/opencode/sse-bridge.js
|
|
10547
|
-
var OpencodeSseBridge;
|
|
10933
|
+
var IGNORED_FRAME, OpencodeSseBridge;
|
|
10548
10934
|
var init_sse_bridge = __esm({
|
|
10549
10935
|
"packages/agents/dist/opencode/sse-bridge.js"() {
|
|
10936
|
+
IGNORED_FRAME = /^(message|storage|file|lsp|installation)\./;
|
|
10550
10937
|
OpencodeSseBridge = class {
|
|
10551
10938
|
controllers = /* @__PURE__ */ new Map();
|
|
10552
10939
|
/** taskId → the crew's opencode server port (for permission-reply POSTs). */
|
|
@@ -10683,6 +11070,10 @@ var init_sse_bridge = __esm({
|
|
|
10683
11070
|
return;
|
|
10684
11071
|
}
|
|
10685
11072
|
if (json?.type === "session.idle") {
|
|
11073
|
+
if (this.deps.ingest) {
|
|
11074
|
+
this.deps.ingest(json, taskId);
|
|
11075
|
+
return;
|
|
11076
|
+
}
|
|
10686
11077
|
this.deps.emit({
|
|
10687
11078
|
type: "task.turn.completed",
|
|
10688
11079
|
id: taskId,
|
|
@@ -10692,6 +11083,10 @@ var init_sse_bridge = __esm({
|
|
|
10692
11083
|
const p = json.properties;
|
|
10693
11084
|
if (p?.id && p?.sessionID) {
|
|
10694
11085
|
this.pendingPermByTask.set(taskId, { permID: p.id, sessionID: p.sessionID });
|
|
11086
|
+
if (this.deps.ingest) {
|
|
11087
|
+
this.deps.ingest(json, taskId);
|
|
11088
|
+
return;
|
|
11089
|
+
}
|
|
10695
11090
|
const tool = p.permission ?? "a tool";
|
|
10696
11091
|
const cmd = Array.isArray(p.patterns) && p.patterns.length ? `: ${p.patterns.join(" ")}` : "";
|
|
10697
11092
|
this.deps.emit({
|
|
@@ -10701,11 +11096,24 @@ var init_sse_bridge = __esm({
|
|
|
10701
11096
|
question: `opencode requests permission to run ${tool}${cmd}`,
|
|
10702
11097
|
kind: tool
|
|
10703
11098
|
});
|
|
11099
|
+
} else if (this.deps.ingest) {
|
|
11100
|
+
this.deps.ingest(json, taskId);
|
|
10704
11101
|
}
|
|
10705
11102
|
} else if (json?.type === "permission.replied") {
|
|
10706
11103
|
this.pendingPermByTask.delete(taskId);
|
|
11104
|
+
this.deps.ingest?.(json, taskId);
|
|
11105
|
+
} else if (!IGNORED_FRAME.test(json?.type ?? "")) {
|
|
11106
|
+
this.deps.ingest?.(json, taskId);
|
|
10707
11107
|
}
|
|
10708
11108
|
}
|
|
11109
|
+
/** Test seam: exercise handleLine without an SSE stream. */
|
|
11110
|
+
handleLineForTest(rawLine, taskId) {
|
|
11111
|
+
this.handleLine(taskId, rawLine);
|
|
11112
|
+
}
|
|
11113
|
+
/** Test seam: read pendingPermByTask without exposing it publicly. */
|
|
11114
|
+
pendingPermForTest(taskId) {
|
|
11115
|
+
return this.pendingPermByTask.get(taskId);
|
|
11116
|
+
}
|
|
10709
11117
|
};
|
|
10710
11118
|
}
|
|
10711
11119
|
});
|
|
@@ -11705,77 +12113,6 @@ var init_receipt_listener = __esm({
|
|
|
11705
12113
|
}
|
|
11706
12114
|
});
|
|
11707
12115
|
|
|
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
12116
|
// packages/agents/dist/opencode/http-channel.js
|
|
11780
12117
|
var OpencodeHttpChannel;
|
|
11781
12118
|
var init_http_channel = __esm({
|
|
@@ -11887,6 +12224,47 @@ var init_http_channel = __esm({
|
|
|
11887
12224
|
}
|
|
11888
12225
|
});
|
|
11889
12226
|
|
|
12227
|
+
// packages/agents/dist/opencode/fact-adapter.js
|
|
12228
|
+
function createOpencodeFactAdapter(deps) {
|
|
12229
|
+
return {
|
|
12230
|
+
name: "opencode-sse",
|
|
12231
|
+
origin: "agent",
|
|
12232
|
+
translate(raw) {
|
|
12233
|
+
const f = typeof raw === "object" && raw !== null ? raw : {};
|
|
12234
|
+
const type = typeof f.type === "string" ? f.type : void 0;
|
|
12235
|
+
if (type === void 0)
|
|
12236
|
+
return [{ kind: "unknown", name: "non-object" }];
|
|
12237
|
+
const p = f.properties ?? {};
|
|
12238
|
+
if (type === "session.idle") {
|
|
12239
|
+
return [{
|
|
12240
|
+
kind: "turn.ended",
|
|
12241
|
+
turnId: typeof p.sessionID === "string" ? p.sessionID : void 0
|
|
12242
|
+
}];
|
|
12243
|
+
}
|
|
12244
|
+
if (type === "permission.asked") {
|
|
12245
|
+
if (typeof p.id !== "string" || typeof p.sessionID !== "string") {
|
|
12246
|
+
return [{ kind: "unknown", name: "permission.asked:incomplete" }];
|
|
12247
|
+
}
|
|
12248
|
+
const tool = typeof p.permission === "string" ? p.permission : "a tool";
|
|
12249
|
+
const cmd = Array.isArray(p.patterns) && p.patterns.length ? `: ${p.patterns.join(" ")}` : "";
|
|
12250
|
+
return [{
|
|
12251
|
+
kind: "permission.requested",
|
|
12252
|
+
question: `opencode requests permission to run ${tool}${cmd}`,
|
|
12253
|
+
requestId: deps.nextRequestId(),
|
|
12254
|
+
tool
|
|
12255
|
+
}];
|
|
12256
|
+
}
|
|
12257
|
+
if (type === "permission.replied")
|
|
12258
|
+
return [{ kind: "activity" }];
|
|
12259
|
+
return [{ kind: "unknown", name: type }];
|
|
12260
|
+
}
|
|
12261
|
+
};
|
|
12262
|
+
}
|
|
12263
|
+
var init_fact_adapter = __esm({
|
|
12264
|
+
"packages/agents/dist/opencode/fact-adapter.js"() {
|
|
12265
|
+
}
|
|
12266
|
+
});
|
|
12267
|
+
|
|
11890
12268
|
// packages/agents/dist/index.js
|
|
11891
12269
|
var dist_exports4 = {};
|
|
11892
12270
|
__export(dist_exports4, {
|
|
@@ -11901,7 +12279,6 @@ __export(dist_exports4, {
|
|
|
11901
12279
|
HEADLESS_ERROR_TAIL: () => HEADLESS_ERROR_TAIL,
|
|
11902
12280
|
MARKER_END: () => MARKER_END,
|
|
11903
12281
|
MARKER_START: () => MARKER_START,
|
|
11904
|
-
OpencodeControlSource: () => OpencodeControlSource,
|
|
11905
12282
|
OpencodeHttpChannel: () => OpencodeHttpChannel,
|
|
11906
12283
|
OpencodeSseBridge: () => OpencodeSseBridge,
|
|
11907
12284
|
ProjectionRegistry: () => ProjectionRegistry,
|
|
@@ -11921,6 +12298,7 @@ __export(dist_exports4, {
|
|
|
11921
12298
|
createGeminiEmitter: () => createGeminiEmitter,
|
|
11922
12299
|
createOpencodeDriver: () => createOpencodeDriver,
|
|
11923
12300
|
createOpencodeEmitter: () => createOpencodeEmitter,
|
|
12301
|
+
createOpencodeFactAdapter: () => createOpencodeFactAdapter,
|
|
11924
12302
|
decideCaptainMemoryWrite: () => decideCaptainMemoryWrite,
|
|
11925
12303
|
deriveTranscriptPath: () => deriveTranscriptPath,
|
|
11926
12304
|
detectTrailingQuestion: () => detectTrailingQuestion2,
|
|
@@ -11963,8 +12341,8 @@ var init_dist4 = __esm({
|
|
|
11963
12341
|
init_receipt_listener();
|
|
11964
12342
|
init_peer_wire();
|
|
11965
12343
|
init_registry8();
|
|
11966
|
-
init_control_source();
|
|
11967
12344
|
init_http_channel();
|
|
12345
|
+
init_fact_adapter();
|
|
11968
12346
|
}
|
|
11969
12347
|
});
|
|
11970
12348
|
|
|
@@ -13963,6 +14341,10 @@ async function runCrewSpawn2(input) {
|
|
|
13963
14341
|
emitEvent: async (p, event) => {
|
|
13964
14342
|
await squadrantdCall({ kind: "event", project: p, event });
|
|
13965
14343
|
},
|
|
14344
|
+
// #745: check the daemon's hook-confirmed state before reporting a false
|
|
14345
|
+
// "first turn not delivered" — swallow errors (offline/unreachable daemon)
|
|
14346
|
+
// so this optional check never itself breaks the spawn.
|
|
14347
|
+
getTaskRecord: async (p, id) => await squadrantdCall(buildStatusRequest(p, id)).catch(() => void 0),
|
|
13966
14348
|
onRouted: (route) => console.log(
|
|
13967
14349
|
chalk10.dim(
|
|
13968
14350
|
`routed: tier=${route.tier} \u2192 ${route.agent}${route.model ? `/${route.model}` : ""} (rule: "${route.matchedRule}")`
|
|
@@ -14941,12 +15323,12 @@ function donut(t) {
|
|
|
14941
15323
|
const C = 2 * Math.PI * r;
|
|
14942
15324
|
let acc = 0;
|
|
14943
15325
|
const segs = DONUT_ORDER.map((k) => {
|
|
14944
|
-
const
|
|
14945
|
-
if (
|
|
15326
|
+
const v2 = t[k];
|
|
15327
|
+
if (v2 <= 0)
|
|
14946
15328
|
return "";
|
|
14947
|
-
const len = t.total ?
|
|
15329
|
+
const len = t.total ? v2 / t.total * C : 0;
|
|
14948
15330
|
const rot = t.total ? acc / t.total * 360 : 0;
|
|
14949
|
-
acc +=
|
|
15331
|
+
acc += v2;
|
|
14950
15332
|
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
15333
|
}).join("");
|
|
14952
15334
|
const track = `<circle class="donut-track" cx="${cx}" cy="${cy}" r="${r}" fill="none" stroke-width="${w}"></circle>`;
|
|
@@ -15250,7 +15632,7 @@ function renderLiveGrid(snap, now) {
|
|
|
15250
15632
|
const headerLabels = STATE_ORDER.map((s) => `${STATE_LABEL[s]}`);
|
|
15251
15633
|
out.push(`<div class="live-header" data-live-header="">`);
|
|
15252
15634
|
out.push(headerLabels.map((l) => {
|
|
15253
|
-
const raw = Object.entries(STATE_LABEL).find(([,
|
|
15635
|
+
const raw = Object.entries(STATE_LABEL).find(([, v2]) => v2 === l)[0];
|
|
15254
15636
|
const c = stateCounts[raw];
|
|
15255
15637
|
const cls = c === 0 ? "zero" : "";
|
|
15256
15638
|
return `<span class="live-stat ${cls}"><span class="pdot ${STATE_CLS[raw]}"></span>${c} ${l}</span>`;
|
|
@@ -15802,7 +16184,7 @@ async function runDashboardWeb(input) {
|
|
|
15802
16184
|
console.log(chalk14.green(`\u2714 Squadrant system dashboard \u2192 http://127.0.0.1:${handle.port}`));
|
|
15803
16185
|
console.log(chalk14.dim(` polling the daemon every ${input.interval}s \xB7 localhost only \xB7 read-only \xB7 Ctrl-C to stop`));
|
|
15804
16186
|
}
|
|
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)", (
|
|
16187
|
+
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
16188
|
try {
|
|
15807
16189
|
if (opts.web) {
|
|
15808
16190
|
await runDashboardWeb({ port: opts.port, interval: opts.interval ?? 5 });
|
|
@@ -16022,7 +16404,7 @@ var launchCommand = new Command14("launch").description(
|
|
|
16022
16404
|
agentCmdFactory: (forceFresh) => {
|
|
16023
16405
|
const captainChannelEnabled = shouldWireCaptainChannel(agentName, config);
|
|
16024
16406
|
if (captainChannelEnabled) {
|
|
16025
|
-
|
|
16407
|
+
ensureSocksDir();
|
|
16026
16408
|
}
|
|
16027
16409
|
return buildAgentCmd(
|
|
16028
16410
|
agentName,
|
|
@@ -16733,11 +17115,11 @@ import chalk22 from "chalk";
|
|
|
16733
17115
|
import fs23 from "fs";
|
|
16734
17116
|
import path28 from "path";
|
|
16735
17117
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
16736
|
-
function parseScope(
|
|
16737
|
-
if (
|
|
17118
|
+
function parseScope(v2) {
|
|
17119
|
+
if (v2 !== "user" && v2 !== "project") {
|
|
16738
17120
|
throw new Error("--scope must be 'user' or 'project'");
|
|
16739
17121
|
}
|
|
16740
|
-
return
|
|
17122
|
+
return v2;
|
|
16741
17123
|
}
|
|
16742
17124
|
function findPackageRoot3() {
|
|
16743
17125
|
let dir = path28.dirname(fileURLToPath4(import.meta.url));
|
|
@@ -17185,8 +17567,10 @@ async function runHealDaemon(opts) {
|
|
|
17185
17567
|
const { stdout, stderr } = opts;
|
|
17186
17568
|
stdout.write("restarting squadrantd via launchd kickstart...\n");
|
|
17187
17569
|
try {
|
|
17188
|
-
opts.ensureDaemon();
|
|
17189
|
-
|
|
17570
|
+
const result = opts.ensureDaemon();
|
|
17571
|
+
const noteSuffix = result?.note ? ` (${result.note})` : "";
|
|
17572
|
+
stdout.write(chalk24.green(`\u2714 daemon kickstart complete${noteSuffix}
|
|
17573
|
+
`));
|
|
17190
17574
|
return 0;
|
|
17191
17575
|
} catch (e) {
|
|
17192
17576
|
stderr.write(`heal daemon failed: ${e.message}
|
|
@@ -17389,12 +17773,12 @@ async function dispatchAction(toProject, task, opts) {
|
|
|
17389
17773
|
process.exit(1);
|
|
17390
17774
|
}
|
|
17391
17775
|
}
|
|
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)", (
|
|
17776
|
+
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
17777
|
|
|
17394
17778
|
// packages/cli/src/commands/group.ts
|
|
17395
17779
|
init_dist2();
|
|
17396
17780
|
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)", (
|
|
17781
|
+
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
17782
|
console.error(chalk26.yellow(
|
|
17399
17783
|
`\u26A0 'squadrant group dispatch' is deprecated \u2014 use 'squadrant dispatch <project> "<task>"' instead.`
|
|
17400
17784
|
));
|
|
@@ -17448,6 +17832,7 @@ function registerSenderIdentity(socketPath2) {
|
|
|
17448
17832
|
}
|
|
17449
17833
|
async function sharedReceiptListener() {
|
|
17450
17834
|
if (shared) return shared;
|
|
17835
|
+
ensureSocksDir();
|
|
17451
17836
|
const socketPath2 = `${CC_SOCKS_DIR}/squadrantd-${process.pid}.sock`;
|
|
17452
17837
|
const listener = new ClaudeReceiptListener({
|
|
17453
17838
|
socketPath: socketPath2,
|
|
@@ -18070,7 +18455,7 @@ telegramCommand.command("link").argument("<project>", "project to bind to a Tele
|
|
|
18070
18455
|
const { topicId, created } = await runTelegramLink({ project, cfg, client, stateRoot: defaultStateRoot() });
|
|
18071
18456
|
console.log(chalk32.green(`${created ? "linked" : "already linked"}: ${project} \u2192 topic ${topicId}`));
|
|
18072
18457
|
});
|
|
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", (
|
|
18458
|
+
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
18459
|
if (!process.stdin.isTTY) {
|
|
18075
18460
|
console.error(chalk32.red("setup requires a TTY \u2014 pipe input is not supported"));
|
|
18076
18461
|
process.exit(1);
|
|
@@ -18580,7 +18965,7 @@ function printTree(items) {
|
|
|
18580
18965
|
function printFlat(items) {
|
|
18581
18966
|
for (const item of items) printItem(item, 0);
|
|
18582
18967
|
}
|
|
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)", (
|
|
18968
|
+
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
18969
|
const config = loadConfig();
|
|
18585
18970
|
const store = createWorkStore();
|
|
18586
18971
|
purgeExpiredWorkItems(store);
|
|
@@ -18902,7 +19287,7 @@ function queryClaudeMem(dbPath, project) {
|
|
|
18902
19287
|
createdAt: r.created_at
|
|
18903
19288
|
}));
|
|
18904
19289
|
const candidates = [summaryRow?.created_at, ...decisionRows.map((r) => r.created_at)].filter(
|
|
18905
|
-
(
|
|
19290
|
+
(v2) => !!v2
|
|
18906
19291
|
);
|
|
18907
19292
|
const oldestCreatedAt = candidates.length > 0 ? candidates.reduce((a, b) => a < b ? a : b) : null;
|
|
18908
19293
|
return {
|