squadrant 0.13.1 → 0.13.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 +71 -16
- package/dist/index.js.map +1 -1
- package/dist/squadrantd.js +27 -4
- package/dist/squadrantd.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2487,8 +2487,9 @@ async function runCodexInteractiveSpawn(o) {
|
|
|
2487
2487
|
title
|
|
2488
2488
|
});
|
|
2489
2489
|
await o.runtime.sendToPane(pane, `squadrant crew attach ${rec.id}`);
|
|
2490
|
-
|
|
2491
|
-
|
|
2490
|
+
const firstTurnText = o.firstTurn ?? o.task;
|
|
2491
|
+
if (firstTurnText && firstTurnText !== "(interactive)") {
|
|
2492
|
+
void o.sendCodexFirstTurn(rec.id, firstTurnText).catch((e) => {
|
|
2492
2493
|
process.stderr.write(`(first-turn delivery failed: ${e.message})
|
|
2493
2494
|
`);
|
|
2494
2495
|
});
|
|
@@ -2520,6 +2521,13 @@ async function runCrewSpawn(input, config, deps) {
|
|
|
2520
2521
|
name,
|
|
2521
2522
|
base: resolveWorktreeBase(proj.path)
|
|
2522
2523
|
}) : proj.path;
|
|
2524
|
+
let firstTurnTask = input.task;
|
|
2525
|
+
if (input.taskFile && input.taskFile !== "-" && !input.shared) {
|
|
2526
|
+
const absTaskFile = path10.resolve(input.taskFile);
|
|
2527
|
+
const basename = path10.basename(absTaskFile);
|
|
2528
|
+
fs12.copyFileSync(absTaskFile, path10.join(spawnCwd, basename));
|
|
2529
|
+
firstTurnTask = `Read ./${basename} to get your task brief, then execute it.`;
|
|
2530
|
+
}
|
|
2523
2531
|
const route = !input.agentExplicit && !input.model ? resolveCrewRoute(input.task, config) : null;
|
|
2524
2532
|
if (route) {
|
|
2525
2533
|
deps.onRouted?.(route);
|
|
@@ -2535,6 +2543,7 @@ async function runCrewSpawn(input, config, deps) {
|
|
|
2535
2543
|
return runCodexInteractiveSpawn({
|
|
2536
2544
|
project: input.project,
|
|
2537
2545
|
task: input.task,
|
|
2546
|
+
firstTurn: firstTurnTask !== input.task ? firstTurnTask : void 0,
|
|
2538
2547
|
cwd: spawnCwd,
|
|
2539
2548
|
runtime: deps.runtime,
|
|
2540
2549
|
workspaceId: captain.id,
|
|
@@ -2578,9 +2587,15 @@ async function runCrewSpawn(input, config, deps) {
|
|
|
2578
2587
|
const envPrefix = `SQUADRANT_CREW_TASK_ID=${rec.id} SQUADRANT_CREW_PROJECT=${input.project}`;
|
|
2579
2588
|
await deps.runtime.sendToPane(pane2, `cd ${shellQuote(spawnCwd)} && ${envPrefix} ${cliCommand2}`);
|
|
2580
2589
|
const preLaunchScreen = await deps.runtime.readPaneScreen(pane2) ?? "";
|
|
2581
|
-
await deps.sendFirstTurn(pane2, `${
|
|
2590
|
+
const claudeResult = await deps.sendFirstTurn(pane2, `${firstTurnTask}
|
|
2582
2591
|
|
|
2583
2592
|
${buildCompletionProtocol(rec.id, input.project)}`, preLaunchScreen);
|
|
2593
|
+
if (!claudeResult.delivered) {
|
|
2594
|
+
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.
|
|
2595
|
+
`);
|
|
2596
|
+
} else {
|
|
2597
|
+
await deps.emitEvent?.(input.project, { type: "task.first-turn.confirmed", id: rec.id });
|
|
2598
|
+
}
|
|
2584
2599
|
return { ...pane2, title: title2 };
|
|
2585
2600
|
}
|
|
2586
2601
|
if (agentName === "opencode") {
|
|
@@ -2620,7 +2635,7 @@ ${buildCompletionProtocol(rec.id, input.project)}`, preLaunchScreen);
|
|
|
2620
2635
|
const envPrefix = `SQUADRANT_CREW_TASK_ID=${rec.id} SQUADRANT_CREW_PROJECT=${input.project}`;
|
|
2621
2636
|
await deps.runtime.sendToPane(pane2, `cd ${shellQuote(spawnCwd)} && ${envPrefix} OPENCODE_CONFIG=${opencodeConfigPath} ${cliCommand2}`);
|
|
2622
2637
|
const preLaunchScreen = await deps.runtime.readPaneScreen(pane2) ?? "";
|
|
2623
|
-
await deps.sendFirstTurn(pane2, `${
|
|
2638
|
+
const opencodeResult = await deps.sendFirstTurn(pane2, `${firstTurnTask}
|
|
2624
2639
|
|
|
2625
2640
|
${buildCompletionProtocol(rec.id, input.project)}`, preLaunchScreen, {
|
|
2626
2641
|
// #235: confirm-on-delivery — sendFirstTurnWhenReady polls until "Ask
|
|
@@ -2628,6 +2643,12 @@ ${buildCompletionProtocol(rec.id, input.project)}`, preLaunchScreen, {
|
|
|
2628
2643
|
// without duplicating the task. See crew-pane.ts SPLASH_MAX_CHECKS/EVERY_N.
|
|
2629
2644
|
splashMarker: "Ask anything\u2026"
|
|
2630
2645
|
});
|
|
2646
|
+
if (!opencodeResult.delivered) {
|
|
2647
|
+
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.
|
|
2648
|
+
`);
|
|
2649
|
+
} else {
|
|
2650
|
+
await deps.emitEvent?.(input.project, { type: "task.first-turn.confirmed", id: rec.id });
|
|
2651
|
+
}
|
|
2631
2652
|
return { ...pane2, title: title2 };
|
|
2632
2653
|
}
|
|
2633
2654
|
const cliCommand = agent.buildCommand({
|
|
@@ -2644,7 +2665,11 @@ ${buildCompletionProtocol(rec.id, input.project)}`, preLaunchScreen, {
|
|
|
2644
2665
|
await deps.runtime.sendToPane(pane, cliCommand);
|
|
2645
2666
|
if (interactive) {
|
|
2646
2667
|
const preLaunchScreen = await deps.runtime.readPaneScreen(pane) ?? "";
|
|
2647
|
-
await deps.sendFirstTurn(pane,
|
|
2668
|
+
const genericResult = await deps.sendFirstTurn(pane, firstTurnTask, preLaunchScreen);
|
|
2669
|
+
if (!genericResult.delivered) {
|
|
2670
|
+
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.
|
|
2671
|
+
`);
|
|
2672
|
+
}
|
|
2648
2673
|
}
|
|
2649
2674
|
return { ...pane, title };
|
|
2650
2675
|
}
|
|
@@ -2665,8 +2690,12 @@ async function runCrewSend(project, name, message, runtime, workspaceId, deps) {
|
|
|
2665
2690
|
}
|
|
2666
2691
|
} catch {
|
|
2667
2692
|
}
|
|
2668
|
-
const deliver = deps.sendToPane ?? ((pane, msg) => runtime.sendToPane(pane, msg));
|
|
2669
|
-
await deliver(crew, message);
|
|
2693
|
+
const deliver = deps.sendToPane ?? ((pane, msg) => runtime.sendToPane(pane, msg).then(() => ({ delivered: true })));
|
|
2694
|
+
const { delivered } = await deliver(crew, message);
|
|
2695
|
+
if (!delivered) {
|
|
2696
|
+
process.stderr.write(`\u26A0\uFE0F Message not delivered to crew '${name}' \u2014 use 'squadrant crew send ${project} ${name}' to re-send.
|
|
2697
|
+
`);
|
|
2698
|
+
}
|
|
2670
2699
|
}
|
|
2671
2700
|
async function runCrewRead(project, name, runtime, workspaceId) {
|
|
2672
2701
|
const crew = await findCrewPane(runtime, workspaceId, project, name);
|
|
@@ -4003,9 +4032,9 @@ async function confirmedSendToPane(runtime, pane, message) {
|
|
|
4003
4032
|
if (draft !== "" && draft !== null)
|
|
4004
4033
|
sawDraft = true;
|
|
4005
4034
|
if (draft === "" && sawDraft)
|
|
4006
|
-
return;
|
|
4035
|
+
return { delivered: true };
|
|
4007
4036
|
if (draft === null && afterScreen !== preSendScreen)
|
|
4008
|
-
return;
|
|
4037
|
+
return { delivered: true };
|
|
4009
4038
|
const settled = await settleInputBox(runtime, pane);
|
|
4010
4039
|
if (settled)
|
|
4011
4040
|
sawDraft = true;
|
|
@@ -4015,6 +4044,7 @@ async function confirmedSendToPane(runtime, pane, message) {
|
|
|
4015
4044
|
}
|
|
4016
4045
|
await runtime.sendKeyToPane(pane, "Enter");
|
|
4017
4046
|
}
|
|
4047
|
+
return { delivered: false };
|
|
4018
4048
|
}
|
|
4019
4049
|
async function sendFirstTurnWhenReady(runtime, pane, task, preLaunchScreen, acceptanceConfig) {
|
|
4020
4050
|
await new Promise((r) => setTimeout(r, SEND_FIRST_TURN_FLOOR_MS));
|
|
@@ -4023,7 +4053,8 @@ async function sendFirstTurnWhenReady(runtime, pane, task, preLaunchScreen, acce
|
|
|
4023
4053
|
let stable = false;
|
|
4024
4054
|
for (let i = 0; i < maxPolls && !stable; i++) {
|
|
4025
4055
|
const screen = await runtime.readPaneScreen(pane) ?? "";
|
|
4026
|
-
|
|
4056
|
+
const hasInputBox = !!acceptanceConfig?.splashMarker || parseDraftFromScreen(screen) !== null;
|
|
4057
|
+
if (screen.length > 0 && screen === previousScreen && screen !== preLaunchScreen && hasInputBox) {
|
|
4027
4058
|
stable = true;
|
|
4028
4059
|
} else {
|
|
4029
4060
|
previousScreen = screen;
|
|
@@ -4037,13 +4068,16 @@ async function sendFirstTurnWhenReady(runtime, pane, task, preLaunchScreen, acce
|
|
|
4037
4068
|
await new Promise((r) => setTimeout(r, POST_SEND_CHECK_MS));
|
|
4038
4069
|
const afterScreen = await runtime.readPaneScreen(pane) ?? "";
|
|
4039
4070
|
if (isTurnAccepted(preSendScreen, afterScreen, acceptanceConfig)) {
|
|
4040
|
-
return;
|
|
4071
|
+
return { delivered: true };
|
|
4041
4072
|
}
|
|
4042
4073
|
if ((check2 + 1) % SPLASH_RESEND_EVERY_N === 0 && check2 < SPLASH_MAX_CHECKS - 1) {
|
|
4043
4074
|
await runtime.sendToPane(pane, task);
|
|
4044
4075
|
}
|
|
4045
4076
|
}
|
|
4046
|
-
return;
|
|
4077
|
+
return { delivered: false };
|
|
4078
|
+
}
|
|
4079
|
+
if (!stable) {
|
|
4080
|
+
return confirmedSendToPane(runtime, pane, task);
|
|
4047
4081
|
}
|
|
4048
4082
|
await runtime.pasteToPane(pane, task);
|
|
4049
4083
|
let sawDraft = await settleInputBox(runtime, pane);
|
|
@@ -4057,9 +4091,9 @@ async function sendFirstTurnWhenReady(runtime, pane, task, preLaunchScreen, acce
|
|
|
4057
4091
|
if (draft !== "" && draft !== null)
|
|
4058
4092
|
sawDraft = true;
|
|
4059
4093
|
if (draft === "" && sawDraft)
|
|
4060
|
-
return;
|
|
4094
|
+
return { delivered: true };
|
|
4061
4095
|
if (draft === null && afterScreen !== preSendScreen)
|
|
4062
|
-
return;
|
|
4096
|
+
return { delivered: true };
|
|
4063
4097
|
const settled = await settleInputBox(runtime, pane);
|
|
4064
4098
|
if (settled)
|
|
4065
4099
|
sawDraft = true;
|
|
@@ -4069,6 +4103,10 @@ async function sendFirstTurnWhenReady(runtime, pane, task, preLaunchScreen, acce
|
|
|
4069
4103
|
}
|
|
4070
4104
|
await runtime.sendKeyToPane(pane, "Enter");
|
|
4071
4105
|
}
|
|
4106
|
+
if (!sawDraft) {
|
|
4107
|
+
return confirmedSendToPane(runtime, pane, task);
|
|
4108
|
+
}
|
|
4109
|
+
return { delivered: false };
|
|
4072
4110
|
}
|
|
4073
4111
|
var SEND_FIRST_TURN_FLOOR_MS, POLL_INTERVAL_MS, SEND_FIRST_TURN_TIMEOUT_MS, POST_SEND_CHECK_MS, SPLASH_MAX_CHECKS, SPLASH_RESEND_EVERY_N, SETTLE_POLL_MS, SETTLE_MAX_POLLS, SUBMIT_RETRY_LIMIT;
|
|
4074
4112
|
var init_crew_pane = __esm({
|
|
@@ -7042,6 +7080,7 @@ import chalk9 from "chalk";
|
|
|
7042
7080
|
// packages/cli/src/commands/crew-control.ts
|
|
7043
7081
|
init_dist2();
|
|
7044
7082
|
init_dist2();
|
|
7083
|
+
init_dist();
|
|
7045
7084
|
init_dist4();
|
|
7046
7085
|
import { Command as Command8 } from "commander";
|
|
7047
7086
|
import { createConnection as createConnection3 } from "net";
|
|
@@ -7467,7 +7506,16 @@ function addControlPlaneCrewCommands(crew) {
|
|
|
7467
7506
|
const r = await squadrantdCall(buildStatusRequest(project, id));
|
|
7468
7507
|
process.stdout.write(JSON.stringify(r) + "\n");
|
|
7469
7508
|
});
|
|
7470
|
-
crew.command("tasks <project>").description("List control-plane tasks for a project (control-plane analogue of legacy `list`), or purge a task with --purge").option("--json", "Full JSON output (one or more records)").option("--id <taskId>", "Show only tasks matching this id prefix").option("--state <state>", "Filter by task state").option("--state-only <taskId>", "Print just the state string for a single task").option("--purge <taskId>", "Purge a task record from the store (default: only terminal records)").option("--force", "Force-purge a non-terminal record (use with --purge)").action(async (project, opts) => {
|
|
7509
|
+
crew.command("tasks <project>").description("List control-plane tasks for a project (control-plane analogue of legacy `list`), or purge a task with --purge").option("--json", "Full JSON output (one or more records)").option("--id <taskId>", "Show only tasks matching this id prefix").option("--state <state>", "Filter by task state").option("--state-only <taskId>", "Print just the state string for a single task").option("--purge <taskId>", "Purge a task record from the store (default: only terminal records)").option("--force", "Force-purge a non-terminal record (use with --purge)").option("--all-terminal", "Purge all terminal (done/cancelled/failed) records for the project").action(async (project, opts) => {
|
|
7510
|
+
if (opts.allTerminal) {
|
|
7511
|
+
const tasks = await squadrantdCall({ kind: "list", project });
|
|
7512
|
+
const terminal = tasks.filter((t) => TERMINAL_STATES.has(t.state));
|
|
7513
|
+
for (const t of terminal) {
|
|
7514
|
+
await squadrantdCall({ kind: "purge", project, id: t.id, force: false });
|
|
7515
|
+
}
|
|
7516
|
+
console.log(`purged ${terminal.length} terminal record(s)`);
|
|
7517
|
+
return;
|
|
7518
|
+
}
|
|
7471
7519
|
if (opts.purge) {
|
|
7472
7520
|
const r = await squadrantdCall({ kind: "purge", project, id: opts.purge, force: opts.force ?? false });
|
|
7473
7521
|
console.log(`purged ${r.provider}/${r.id} (was ${r.state})`);
|
|
@@ -7705,6 +7753,10 @@ async function runCrewSpawn2(input) {
|
|
|
7705
7753
|
sendFirstTurn: (pane, firstTurn, preLaunchScreen, opts) => sendFirstTurnWhenReady(runtime, pane, firstTurn, preLaunchScreen, opts),
|
|
7706
7754
|
getFreePort,
|
|
7707
7755
|
sendCodexFirstTurn,
|
|
7756
|
+
// #466: wire delivery confirmation so the daemon stamps firstTurnConfirmedAt.
|
|
7757
|
+
emitEvent: async (p, event) => {
|
|
7758
|
+
await squadrantdCall({ kind: "event", project: p, event });
|
|
7759
|
+
},
|
|
7708
7760
|
onRouted: (route) => console.log(
|
|
7709
7761
|
chalk9.dim(
|
|
7710
7762
|
`routed: tier=${route.tier} \u2192 ${route.agent}${route.model ? `/${route.model}` : ""} (rule: "${route.matchedRule}")`
|
|
@@ -7765,7 +7817,10 @@ crewCommand.command("spawn").description(
|
|
|
7765
7817
|
// opencode consumes the `approval` flag (→ bash:"ask" per-crew config).
|
|
7766
7818
|
...opts.approval ? { approvalPolicy: "untrusted", approval: true } : {},
|
|
7767
7819
|
...opts.shared ? { shared: true } : {},
|
|
7768
|
-
...opts.model ? { model: opts.model } : {}
|
|
7820
|
+
...opts.model ? { model: opts.model } : {},
|
|
7821
|
+
// #458: pass the raw file path (not stdin) so runCrewSpawn can copy it
|
|
7822
|
+
// into the isolated worktree root for relative-path access.
|
|
7823
|
+
...opts.taskFile && opts.taskFile !== "-" ? { taskFile: opts.taskFile } : {}
|
|
7769
7824
|
});
|
|
7770
7825
|
console.log(chalk9.green(`\u2714 Crew '${pane.title}' spawned (${pane.surfaceId})`));
|
|
7771
7826
|
} catch (err) {
|