@dougbots/avenor-opencode 0.9.0 → 0.10.0
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/README.md +20 -1
- package/dist/index.d.ts +14 -2
- package/dist/index.js +70 -21
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -58,17 +58,36 @@ Dispatch an agent run in the current working directory.
|
|
|
58
58
|
|
|
59
59
|
| Argument | Required | Description |
|
|
60
60
|
|---|---|---|
|
|
61
|
-
| `agent` |
|
|
61
|
+
| `agent` | no | Agent name; omission uses the supplied model or runtime defaults |
|
|
62
62
|
| `prompt` | no | Prompt text |
|
|
63
63
|
| `prompt_file` | no | Path to a prompt file |
|
|
64
64
|
| `label` | no | Human-readable label for the run |
|
|
65
65
|
| `timeout` | no | Timeout duration (e.g. `3600s`, `30m`) |
|
|
66
66
|
| `model` | no | Model override |
|
|
67
|
+
| `thinking` | no | Run-level reasoning control; unsupported effective backends reject explicit values |
|
|
68
|
+
| `backend` | no | Backend override; direct mode defaults to Avenor's `opencode-acp` backend when omitted |
|
|
69
|
+
| `roster_file` | no | Path to a top-level roster map |
|
|
70
|
+
| `roster_entry` | no | Key within `roster_file` |
|
|
67
71
|
| `wait` | no | Block until complete (default `true`) |
|
|
68
72
|
| `supervisor_id` | no | Reuse an existing supervisor by socket path |
|
|
69
73
|
|
|
70
74
|
The working directory (`dir`) is injected automatically from your OpenCode session context — it is not a user-facing argument.
|
|
71
75
|
|
|
76
|
+
#### Direct and roster modes
|
|
77
|
+
|
|
78
|
+
Use `roster_file` plus `roster_entry` to select a complete identity. Every roster entry requires `backend` and at least one of `agent` or `model`; roster mode rejects direct `agent`, `model`, and `backend` overrides. Direct mode allows all three identity fields to be omitted independently, including backend-only selection. Roster mode does not apply the direct `opencode-acp` default.
|
|
79
|
+
|
|
80
|
+
```json
|
|
81
|
+
{
|
|
82
|
+
"prompt": "Review the repository",
|
|
83
|
+
"roster_file": "/repo/roster.json",
|
|
84
|
+
"roster_entry": "reviewer",
|
|
85
|
+
"wait": true
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Roster entries currently support only `backend`, `agent`, and `model`. `system` and `thinking` are rejected; `thinking` remains a run-level value and is validated against the effective backend. A resumed session cannot change backend. The plugin passes `roster_file` as supplied, so an absolute path avoids ambiguity when the OpenCode process directory differs from the project directory.
|
|
90
|
+
|
|
72
91
|
### `avenor_status`
|
|
73
92
|
|
|
74
93
|
Get status of a specific run or all active runs. Use the compact `lifecycle` view for progress and pending permission checks; `full` remains the compatibility default.
|
package/dist/index.d.ts
CHANGED
|
@@ -6,7 +6,14 @@ type TrackedRun = {
|
|
|
6
6
|
runId: string;
|
|
7
7
|
runtimeId?: string;
|
|
8
8
|
sessionId?: string;
|
|
9
|
-
agent
|
|
9
|
+
agent?: string;
|
|
10
|
+
model?: string;
|
|
11
|
+
backend?: string;
|
|
12
|
+
rosterFile?: string;
|
|
13
|
+
rosterEntry?: string;
|
|
14
|
+
effectiveAgent?: string;
|
|
15
|
+
effectiveModel?: string;
|
|
16
|
+
effectiveBackend?: string;
|
|
10
17
|
orchestratorSessionId: string;
|
|
11
18
|
label: string;
|
|
12
19
|
supervisorId?: string;
|
|
@@ -15,7 +22,12 @@ type TrackedRun = {
|
|
|
15
22
|
lastSnapshot?: RunSnapshot;
|
|
16
23
|
};
|
|
17
24
|
declare function terminalStatus(snapshot: RunSnapshot, fallback?: StatusResult): string;
|
|
25
|
+
type HostIdentityMetadataInput = Pick<TrackedRun, 'agent' | 'model' | 'backend' | 'rosterFile' | 'rosterEntry' | 'effectiveAgent' | 'effectiveModel' | 'effectiveBackend'>;
|
|
26
|
+
type HostIdentityStatus = Pick<StatusResult, 'roster_file' | 'roster_entry' | 'agent' | 'model' | 'backend' | 'effective_agent' | 'effective_model' | 'effective_backend'>;
|
|
27
|
+
/** Project shared spawn identity into OpenCode metadata without resolving rosters here. */
|
|
28
|
+
declare function hostIdentityMetadata(run?: HostIdentityMetadataInput, status?: HostIdentityStatus): Record<string, unknown>;
|
|
29
|
+
declare function buildSnapshotMetadata(snapshot: RunSnapshot, run?: HostIdentityMetadataInput, status?: HostIdentityStatus): Record<string, unknown>;
|
|
18
30
|
declare function buildWaitingText(run: TrackedRun, snapshot: RunSnapshot): string;
|
|
19
31
|
declare const AvenorPlugin: Plugin;
|
|
20
32
|
//#endregion
|
|
21
|
-
export { AvenorPlugin, buildWaitingText, terminalStatus };
|
|
33
|
+
export { AvenorPlugin, buildSnapshotMetadata, buildWaitingText, hostIdentityMetadata, terminalStatus };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { tool } from "@opencode-ai/plugin";
|
|
2
2
|
import * as crypto from "node:crypto";
|
|
3
|
-
import { Supervisor, answerPermissionTool, createRunSnapshot, dial, eventsTool, extractEventText, followUpTool, inspectTool, observeRun, resultTool, shutdownTool, spawnTool, statusTool } from "@dougbots/avenor-core";
|
|
3
|
+
import { Supervisor, answerPermissionTool, createRunSnapshot, dial, eventsTool, extractEventText, followUpTool, inspectTool, observeRun, resultTool, shutdownTool, spawnTool, statusTool, validateSpawnSelection } from "@dougbots/avenor-core";
|
|
4
4
|
//#region src/render.ts
|
|
5
5
|
const FINAL_OUTPUT_CHARS$1 = 1500;
|
|
6
6
|
const SCALAR_CHARS = 240;
|
|
@@ -462,7 +462,20 @@ function formatTranscriptPreview(snapshot) {
|
|
|
462
462
|
if (lines.length === 0) return void 0;
|
|
463
463
|
return clipTail(lines.join("\n"), METADATA_TEXT_CHARS);
|
|
464
464
|
}
|
|
465
|
-
|
|
465
|
+
/** Project shared spawn identity into OpenCode metadata without resolving rosters here. */
|
|
466
|
+
function hostIdentityMetadata(run = {}, status) {
|
|
467
|
+
const metadata = {};
|
|
468
|
+
const add = (key, value) => {
|
|
469
|
+
if (typeof value === "string" && value.length > 0) metadata[key] = value;
|
|
470
|
+
};
|
|
471
|
+
add("roster_file", status?.roster_file ?? run.rosterFile);
|
|
472
|
+
add("roster_entry", status?.roster_entry ?? run.rosterEntry);
|
|
473
|
+
add("effective_agent", status?.effective_agent ?? status?.agent ?? run.effectiveAgent);
|
|
474
|
+
add("effective_model", status?.effective_model ?? status?.model ?? run.effectiveModel);
|
|
475
|
+
add("effective_backend", status?.effective_backend ?? status?.backend ?? run.effectiveBackend);
|
|
476
|
+
return metadata;
|
|
477
|
+
}
|
|
478
|
+
function buildSnapshotMetadata(snapshot, run, status) {
|
|
466
479
|
const metadata = {};
|
|
467
480
|
if (snapshot.phase) metadata.phase = snapshot.phase;
|
|
468
481
|
if (snapshot.latest_seq !== void 0) metadata.latest_seq = snapshot.latest_seq;
|
|
@@ -472,6 +485,7 @@ function buildSnapshotMetadata(snapshot) {
|
|
|
472
485
|
if (transcriptPreview) metadata.transcript_preview = transcriptPreview;
|
|
473
486
|
const pendingPermission = clipTail(snapshot.pending_permission?.description ?? snapshot.pending_permission?.question ?? snapshot.phase_label, METADATA_TEXT_CHARS);
|
|
474
487
|
if (pendingPermission) metadata.pending_permission = pendingPermission;
|
|
488
|
+
Object.assign(metadata, hostIdentityMetadata(run, status));
|
|
475
489
|
return metadata;
|
|
476
490
|
}
|
|
477
491
|
function buildWaitingText(run, snapshot) {
|
|
@@ -544,6 +558,16 @@ const AvenorPlugin = async (ctx) => {
|
|
|
544
558
|
if (snapshot.latest_seq !== void 0) run.latestSeq = snapshot.latest_seq;
|
|
545
559
|
registerSessionId(snapshot.identity.session_id, snapshot.identity.runtime_id, run.runId);
|
|
546
560
|
}
|
|
561
|
+
function updateRunIdentity(run, status) {
|
|
562
|
+
run.rosterFile = status.roster_file ?? run.rosterFile;
|
|
563
|
+
run.rosterEntry = status.roster_entry ?? run.rosterEntry;
|
|
564
|
+
run.effectiveAgent = status.effective_agent ?? status.agent ?? run.effectiveAgent;
|
|
565
|
+
run.effectiveModel = status.effective_model ?? status.model ?? run.effectiveModel;
|
|
566
|
+
run.effectiveBackend = status.effective_backend ?? status.backend ?? run.effectiveBackend;
|
|
567
|
+
run.agent = run.effectiveAgent ?? run.agent;
|
|
568
|
+
run.model = run.effectiveModel ?? run.model;
|
|
569
|
+
run.backend = run.effectiveBackend ?? run.backend;
|
|
570
|
+
}
|
|
547
571
|
async function primeTrackedRun(run, options = {}) {
|
|
548
572
|
try {
|
|
549
573
|
const raw = await statusTool({
|
|
@@ -553,6 +577,7 @@ const AvenorPlugin = async (ctx) => {
|
|
|
553
577
|
const status = Array.isArray(raw) ? raw[0] : raw;
|
|
554
578
|
if (!status) return void 0;
|
|
555
579
|
registerSessionId(status.session_id, status.runtime_id, run.runId);
|
|
580
|
+
updateRunIdentity(run, status);
|
|
556
581
|
if (status.latest_seq !== void 0 && options.captureLatestSeq) run.latestSeq = status.latest_seq;
|
|
557
582
|
return status;
|
|
558
583
|
} catch {
|
|
@@ -578,10 +603,12 @@ const AvenorPlugin = async (ctx) => {
|
|
|
578
603
|
}
|
|
579
604
|
async function loadInspection(run) {
|
|
580
605
|
try {
|
|
581
|
-
|
|
606
|
+
const inspection = await inspectTool({
|
|
582
607
|
runId: run.runId,
|
|
583
608
|
supervisorId: run.supervisorId
|
|
584
609
|
});
|
|
610
|
+
updateRunIdentity(run, inspection.status);
|
|
611
|
+
return inspection;
|
|
585
612
|
} catch {
|
|
586
613
|
return;
|
|
587
614
|
}
|
|
@@ -842,7 +869,7 @@ const AvenorPlugin = async (ctx) => {
|
|
|
842
869
|
avenor_spawn: tool({
|
|
843
870
|
description: "Dispatch an agent run via avenor. Optional thinking accepts off, minimal, low, medium, high, xhigh, or max; unsupported backends reject explicit values. Blocks by default, showing live progress as an updating tool call. Set wait=false for fire-and-forget — you will be re-prompted automatically on completion.",
|
|
844
871
|
args: {
|
|
845
|
-
agent: tool.schema.string().describe("
|
|
872
|
+
agent: tool.schema.string().optional().describe("Optional agent name; omission uses the supplied model or runtime defaults"),
|
|
846
873
|
prompt: tool.schema.string().optional().describe("Prompt text"),
|
|
847
874
|
prompt_file: tool.schema.string().optional().describe("Path to prompt file"),
|
|
848
875
|
dir: tool.schema.string().optional().describe("Working directory for the run (defaults to the session project directory)"),
|
|
@@ -859,24 +886,35 @@ const AvenorPlugin = async (ctx) => {
|
|
|
859
886
|
"max"
|
|
860
887
|
]).optional().describe("Thinking level; omission uses the backend default"),
|
|
861
888
|
backend: tool.schema.string().optional().describe("Backend override"),
|
|
889
|
+
roster_file: tool.schema.string().optional().describe("Path to the roster map"),
|
|
890
|
+
roster_entry: tool.schema.string().optional().describe("Roster entry to select"),
|
|
862
891
|
server_url: tool.schema.string().optional().describe("Backend server URL"),
|
|
863
892
|
supervisor_id: tool.schema.string().optional().describe("Reuse an existing supervisor by socket path"),
|
|
864
893
|
wait: tool.schema.boolean().default(true).describe("Block until complete with live status updates. False = fire-and-forget.")
|
|
865
894
|
},
|
|
866
895
|
async execute(args, context) {
|
|
867
|
-
|
|
868
|
-
const result = await spawnTool({
|
|
896
|
+
validateSpawnSelection({
|
|
869
897
|
agent: args.agent,
|
|
870
|
-
prompt: args.prompt,
|
|
871
|
-
promptFile: args.prompt_file,
|
|
872
|
-
label: args.label,
|
|
873
|
-
dir: args.dir ?? context.directory,
|
|
874
|
-
timeout: args.timeout,
|
|
875
898
|
model: args.model,
|
|
876
|
-
thinking: args.thinking,
|
|
877
899
|
backend: args.backend,
|
|
878
|
-
|
|
879
|
-
|
|
900
|
+
roster_file: args.roster_file,
|
|
901
|
+
roster_entry: args.roster_entry
|
|
902
|
+
});
|
|
903
|
+
const ownerRunId = ensureParentRunId();
|
|
904
|
+
const result = await spawnTool({
|
|
905
|
+
...args.agent !== void 0 ? { agent: args.agent } : {},
|
|
906
|
+
...args.prompt !== void 0 ? { prompt: args.prompt } : {},
|
|
907
|
+
...args.prompt_file !== void 0 ? { promptFile: args.prompt_file } : {},
|
|
908
|
+
...args.label !== void 0 ? { label: args.label } : {},
|
|
909
|
+
dir: args.dir ?? context.directory,
|
|
910
|
+
...args.timeout !== void 0 ? { timeout: args.timeout } : {},
|
|
911
|
+
...args.model !== void 0 ? { model: args.model } : {},
|
|
912
|
+
...args.thinking !== void 0 ? { thinking: args.thinking } : {},
|
|
913
|
+
...args.backend !== void 0 ? { backend: args.backend } : {},
|
|
914
|
+
...args.roster_file !== void 0 ? { rosterFile: args.roster_file } : {},
|
|
915
|
+
...args.roster_entry !== void 0 ? { rosterEntry: args.roster_entry } : {},
|
|
916
|
+
...args.server_url !== void 0 ? { serverUrl: args.server_url } : {},
|
|
917
|
+
...args.supervisor_id !== void 0 ? { supervisorId: args.supervisor_id } : {},
|
|
880
918
|
parent_run_id: ownerRunId
|
|
881
919
|
});
|
|
882
920
|
if (result.broker_url) brokerUrl = result.broker_url;
|
|
@@ -885,6 +923,10 @@ const AvenorPlugin = async (ctx) => {
|
|
|
885
923
|
runId: result.run_id,
|
|
886
924
|
runtimeId: result.runtime_id,
|
|
887
925
|
agent: args.agent,
|
|
926
|
+
model: args.model,
|
|
927
|
+
backend: args.backend,
|
|
928
|
+
rosterFile: args.roster_file,
|
|
929
|
+
rosterEntry: args.roster_entry,
|
|
888
930
|
orchestratorSessionId: context.sessionID,
|
|
889
931
|
label: result.label,
|
|
890
932
|
supervisorId: result.supervisor_id || args.supervisor_id,
|
|
@@ -897,7 +939,8 @@ const AvenorPlugin = async (ctx) => {
|
|
|
897
939
|
monitorRun(run).catch(console.error);
|
|
898
940
|
return {
|
|
899
941
|
title: `${result.label} — dispatched`,
|
|
900
|
-
output: `Dispatched "${result.label}" (run_id: ${result.run_id}). Completion will be delivered automatically; use avenor_status with view "lifecycle" for progress or avenor_result to wait for the final result
|
|
942
|
+
output: `Dispatched "${result.label}" (run_id: ${result.run_id}). Completion will be delivered automatically; use avenor_status with view "lifecycle" for progress or avenor_result to wait for the final result.`,
|
|
943
|
+
metadata: hostIdentityMetadata(run)
|
|
901
944
|
};
|
|
902
945
|
}
|
|
903
946
|
context.metadata({ title: `Starting ${result.label}…` });
|
|
@@ -906,7 +949,7 @@ const AvenorPlugin = async (ctx) => {
|
|
|
906
949
|
onSnapshot: (snapshot) => {
|
|
907
950
|
context.metadata({
|
|
908
951
|
title: snapshot.pending_permission ? `[blocked] ${result.label}` : result.label,
|
|
909
|
-
metadata: buildSnapshotMetadata(snapshot)
|
|
952
|
+
metadata: buildSnapshotMetadata(snapshot, run)
|
|
910
953
|
});
|
|
911
954
|
}
|
|
912
955
|
});
|
|
@@ -921,7 +964,7 @@ const AvenorPlugin = async (ctx) => {
|
|
|
921
964
|
run_id: result.run_id,
|
|
922
965
|
session_id: inspection?.status.session_id ?? snapshot.identity.session_id,
|
|
923
966
|
final_output: clipTail(inspection?.final_output ?? snapshot.final_output, FINAL_OUTPUT_CHARS),
|
|
924
|
-
...buildSnapshotMetadata(snapshot)
|
|
967
|
+
...buildSnapshotMetadata(snapshot, run, inspection?.status)
|
|
925
968
|
}
|
|
926
969
|
};
|
|
927
970
|
}
|
|
@@ -934,20 +977,26 @@ const AvenorPlugin = async (ctx) => {
|
|
|
934
977
|
metadata: {
|
|
935
978
|
run_id: result.run_id,
|
|
936
979
|
session_id: inspection?.status.session_id ?? snapshot.identity.session_id,
|
|
937
|
-
...buildSnapshotMetadata(snapshot)
|
|
980
|
+
...buildSnapshotMetadata(snapshot, run, inspection?.status)
|
|
938
981
|
}
|
|
939
982
|
};
|
|
940
983
|
}
|
|
941
984
|
if (outcome.kind === "error") return {
|
|
942
985
|
title: `${result.label} — monitoring error`,
|
|
943
986
|
output: `Monitoring failed: ${outcome.error ?? "unknown error"}. The run may still be active — use avenor_status to check.`,
|
|
944
|
-
metadata: {
|
|
987
|
+
metadata: {
|
|
988
|
+
run_id: result.run_id,
|
|
989
|
+
...hostIdentityMetadata(run)
|
|
990
|
+
}
|
|
945
991
|
};
|
|
946
992
|
unregisterRun(result.run_id);
|
|
947
993
|
return {
|
|
948
994
|
title: `${result.label} — aborted`,
|
|
949
995
|
output: `Monitoring of "${result.label}" (run_id: ${result.run_id}) was interrupted. The run may still be active — use avenor_status to check.`,
|
|
950
|
-
metadata: {
|
|
996
|
+
metadata: {
|
|
997
|
+
run_id: result.run_id,
|
|
998
|
+
...hostIdentityMetadata(run)
|
|
999
|
+
}
|
|
951
1000
|
};
|
|
952
1001
|
}
|
|
953
1002
|
}),
|
|
@@ -1095,4 +1144,4 @@ const AvenorPlugin = async (ctx) => {
|
|
|
1095
1144
|
};
|
|
1096
1145
|
};
|
|
1097
1146
|
//#endregion
|
|
1098
|
-
export { AvenorPlugin, buildWaitingText, terminalStatus };
|
|
1147
|
+
export { AvenorPlugin, buildSnapshotMetadata, buildWaitingText, hostIdentityMetadata, terminalStatus };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dougbots/avenor-opencode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"dev": "tsdown --watch"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@dougbots/avenor-core": "^0.
|
|
30
|
+
"@dougbots/avenor-core": "^0.10.0"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"@opencode-ai/plugin": "1.15.3"
|