squadrant 0.20.0 → 0.20.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 +87 -6
- package/dist/index.js.map +1 -1
- package/dist/squadrantd.js +22 -4
- package/dist/squadrantd.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11887,6 +11887,7 @@ var init_api_key_approval = __esm({
|
|
|
11887
11887
|
var OpencodeHttpChannel;
|
|
11888
11888
|
var init_http_channel = __esm({
|
|
11889
11889
|
"packages/agents/dist/opencode/http-channel.js"() {
|
|
11890
|
+
init_dist2();
|
|
11890
11891
|
OpencodeHttpChannel = class {
|
|
11891
11892
|
name = "opencode-http";
|
|
11892
11893
|
agent = "opencode";
|
|
@@ -11895,12 +11896,16 @@ var init_http_channel = __esm({
|
|
|
11895
11896
|
fetchImpl;
|
|
11896
11897
|
portFor;
|
|
11897
11898
|
sessionFor;
|
|
11899
|
+
directoryFor;
|
|
11900
|
+
createdAfterFor;
|
|
11898
11901
|
timeoutMs;
|
|
11899
11902
|
log;
|
|
11900
11903
|
constructor(deps) {
|
|
11901
11904
|
this.fetchImpl = deps.fetchImpl ?? fetch;
|
|
11902
11905
|
this.portFor = deps.portFor;
|
|
11903
11906
|
this.sessionFor = deps.sessionFor;
|
|
11907
|
+
this.directoryFor = deps.directoryFor;
|
|
11908
|
+
this.createdAfterFor = deps.createdAfterFor;
|
|
11904
11909
|
this.timeoutMs = deps.timeoutMs ?? 5e3;
|
|
11905
11910
|
this.log = deps.log;
|
|
11906
11911
|
}
|
|
@@ -11953,11 +11958,24 @@ var init_http_channel = __esm({
|
|
|
11953
11958
|
* are tried. This is a capability probe, NOT a version comparison — the honest
|
|
11954
11959
|
* check is "does this route answer", and neither path is a promised-stable
|
|
11955
11960
|
* contract. Re-run the smoke suite when opencode is upgraded.
|
|
11961
|
+
*
|
|
11962
|
+
* #787: when the crew's directory is known, resolve by EXACT directory
|
|
11963
|
+
* (realpath-normalized) and never fall back to the project-wide newest.
|
|
11964
|
+
* GET /session is project-scoped (shared by every worktree of the repo), so
|
|
11965
|
+
* "newest" can be a sibling crew's — prompt_async at that id would run the
|
|
11966
|
+
* message in the wrong session.
|
|
11967
|
+
*
|
|
11968
|
+
* #789: opencode creates a session lazily (nothing exists until the first turn),
|
|
11969
|
+
* so a reused worktree dir can hold a prior crew's session. When the crew's
|
|
11970
|
+
* createdAt is known, also require the candidate to have been created at/after
|
|
11971
|
+
* it — otherwise a send resolves a stale session the live TUI is not showing.
|
|
11956
11972
|
*/
|
|
11957
11973
|
async resolveSession(taskId, port) {
|
|
11958
11974
|
const cached = this.sessionByTask.get(taskId);
|
|
11959
11975
|
if (cached)
|
|
11960
11976
|
return cached;
|
|
11977
|
+
const directory = this.directoryFor?.(taskId);
|
|
11978
|
+
const createdAfterMs = this.createdAfterFor?.(taskId);
|
|
11961
11979
|
for (const path39 of ["/session?", "/api/session?"]) {
|
|
11962
11980
|
let res;
|
|
11963
11981
|
try {
|
|
@@ -11975,11 +11993,11 @@ var init_http_channel = __esm({
|
|
|
11975
11993
|
}
|
|
11976
11994
|
if (!Array.isArray(sessions) || sessions.length === 0)
|
|
11977
11995
|
continue;
|
|
11978
|
-
const
|
|
11979
|
-
if (!
|
|
11996
|
+
const resolved = directory ? newestSessionInDirectory(sessions, directory, createdAfterMs) : sessions.reduce((a, b) => (b.time?.updated ?? 0) > (a.time?.updated ?? 0) ? b : a).id;
|
|
11997
|
+
if (!resolved)
|
|
11980
11998
|
continue;
|
|
11981
|
-
this.sessionByTask.set(taskId,
|
|
11982
|
-
return
|
|
11999
|
+
this.sessionByTask.set(taskId, resolved);
|
|
12000
|
+
return resolved;
|
|
11983
12001
|
}
|
|
11984
12002
|
return void 0;
|
|
11985
12003
|
}
|
|
@@ -14659,7 +14677,57 @@ function writePerCrewOpencodeConfig(o) {
|
|
|
14659
14677
|
websearch: "allow",
|
|
14660
14678
|
task: "allow",
|
|
14661
14679
|
lsp: "allow",
|
|
14662
|
-
external_directory
|
|
14680
|
+
// unlike most permission keys, external_directory defaults to "ask" in
|
|
14681
|
+
// opencode. The catch-all is deliberately "ask" (not "allow") so unknown
|
|
14682
|
+
// external paths stay human-gated — least privilege. Trusted AI/config/
|
|
14683
|
+
// memory/tmp dirs are allow-listed explicitly below; each dir is listed
|
|
14684
|
+
// both as the bare path AND as `<dir>/**`, so the directory itself is
|
|
14685
|
+
// reachable, not only its contents.
|
|
14686
|
+
//
|
|
14687
|
+
// Ordering: opencode evaluates patterns last-match-wins, so the catch-all
|
|
14688
|
+
// goes FIRST and the specific allows follow. If a future version resolves
|
|
14689
|
+
// first-match instead, the failure mode is over-prompting (visible, safe)
|
|
14690
|
+
// rather than a silent allow-all (invisible, unsafe).
|
|
14691
|
+
external_directory: {
|
|
14692
|
+
"**": "ask",
|
|
14693
|
+
"/tmp": "allow",
|
|
14694
|
+
"/tmp/**": "allow",
|
|
14695
|
+
"/private/tmp": "allow",
|
|
14696
|
+
"/private/tmp/**": "allow",
|
|
14697
|
+
"/var/folders": "allow",
|
|
14698
|
+
"/var/folders/**": "allow",
|
|
14699
|
+
"~/.config/squadrant": "allow",
|
|
14700
|
+
"~/.config/squadrant/**": "allow",
|
|
14701
|
+
"~/.config/opencode": "allow",
|
|
14702
|
+
"~/.config/opencode/**": "allow",
|
|
14703
|
+
"~/.config/cmux": "allow",
|
|
14704
|
+
"~/.config/cmux/**": "allow",
|
|
14705
|
+
"~/.opencode": "allow",
|
|
14706
|
+
"~/.opencode/**": "allow",
|
|
14707
|
+
"~/.local/share/opencode": "allow",
|
|
14708
|
+
"~/.local/share/opencode/**": "allow",
|
|
14709
|
+
"~/.local/share/claude": "allow",
|
|
14710
|
+
"~/.local/share/claude/**": "allow",
|
|
14711
|
+
"~/.cache/opencode": "allow",
|
|
14712
|
+
"~/.cache/opencode/**": "allow",
|
|
14713
|
+
"~/.claude": "allow",
|
|
14714
|
+
"~/.claude/**": "allow",
|
|
14715
|
+
"~/.claude.json": "allow",
|
|
14716
|
+
"~/squadrant-hub": "allow",
|
|
14717
|
+
"~/squadrant-hub/**": "allow",
|
|
14718
|
+
"~/.claude-mem": "allow",
|
|
14719
|
+
"~/.claude-mem/**": "allow",
|
|
14720
|
+
"~/.agents": "allow",
|
|
14721
|
+
"~/.agents/**": "allow",
|
|
14722
|
+
"~/.cmuxterm": "allow",
|
|
14723
|
+
"~/.cmuxterm/**": "allow",
|
|
14724
|
+
"~/.codex": "allow",
|
|
14725
|
+
"~/.codex/**": "allow",
|
|
14726
|
+
"~/.cursor": "allow",
|
|
14727
|
+
"~/.cursor/**": "allow",
|
|
14728
|
+
"~/.gemini": "allow",
|
|
14729
|
+
"~/.gemini/**": "allow"
|
|
14730
|
+
}
|
|
14663
14731
|
}
|
|
14664
14732
|
};
|
|
14665
14733
|
writeFileSync11(file, JSON.stringify(config, null, 2));
|
|
@@ -15974,6 +16042,14 @@ async function runCrewSend2(project, name, message, opts) {
|
|
|
15974
16042
|
const controlChannels = [
|
|
15975
16043
|
new OpencodeHttpChannel({
|
|
15976
16044
|
portFor: (taskId) => tasks.find((t) => t.id === taskId)?.serverPort,
|
|
16045
|
+
// #787: scope resolution to the crew's own worktree (TaskRecord.cwd).
|
|
16046
|
+
// opencode's GET /session is project-scoped, so without this the channel
|
|
16047
|
+
// can target a newer sibling crew's / the captain's session.
|
|
16048
|
+
directoryFor: (taskId) => tasks.find((t) => t.id === taskId)?.cwd,
|
|
16049
|
+
// #789: opencode creates sessions lazily, so a reused worktree dir can hold
|
|
16050
|
+
// a prior crew's session. Gate on the crew's createdAt so a stale session
|
|
16051
|
+
// the live TUI is not showing is never selected.
|
|
16052
|
+
createdAfterFor: (taskId) => tasks.find((t) => t.id === taskId)?.createdAt,
|
|
15977
16053
|
log: (m) => console.error(chalk10.dim(m))
|
|
15978
16054
|
}),
|
|
15979
16055
|
new ClaudePeerChannel({
|
|
@@ -18016,6 +18092,10 @@ var launchCommand = new Command14("launch").description(
|
|
|
18016
18092
|
hadFailure = true;
|
|
18017
18093
|
return;
|
|
18018
18094
|
}
|
|
18095
|
+
let captainOpencodeConfigPath;
|
|
18096
|
+
if (isOpencodeCaptain && projectName) {
|
|
18097
|
+
captainOpencodeConfigPath = writePerCrewOpencodeConfig({ stateRoot, project: projectName, taskId: "captain" });
|
|
18098
|
+
}
|
|
18019
18099
|
const priorRecord = projectName ? readCaptainAddress(stateRoot, projectName) : null;
|
|
18020
18100
|
const captainPort = isOpencodeCaptain ? await getFreePort() : void 0;
|
|
18021
18101
|
let opencodeResumeSessionId;
|
|
@@ -18036,7 +18116,7 @@ var launchCommand = new Command14("launch").description(
|
|
|
18036
18116
|
}
|
|
18037
18117
|
const captainBoot = isOpencodeCaptain ? { port: captainPort, sessionId: pickResumeSessionId(priorRecord, "opencode", forceFresh) } : void 0;
|
|
18038
18118
|
opencodeResumeSessionId = captainBoot?.sessionId;
|
|
18039
|
-
|
|
18119
|
+
const baseCmd = buildAgentCmd(
|
|
18040
18120
|
agentName,
|
|
18041
18121
|
registry,
|
|
18042
18122
|
role,
|
|
@@ -18049,6 +18129,7 @@ var launchCommand = new Command14("launch").description(
|
|
|
18049
18129
|
thinking,
|
|
18050
18130
|
captainBoot
|
|
18051
18131
|
);
|
|
18132
|
+
return captainOpencodeConfigPath ? `OPENCODE_CONFIG=${captainOpencodeConfigPath} ${baseCmd}` : baseCmd;
|
|
18052
18133
|
},
|
|
18053
18134
|
initialPrompt,
|
|
18054
18135
|
runtime,
|