@dougbots/avenor-opencode 0.4.1 → 0.5.1

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.
Files changed (3) hide show
  1. package/README.md +20 -5
  2. package/dist/index.js +42 -4
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -33,7 +33,7 @@ Add `@dougbots/avenor-opencode` to your OpenCode configuration. If you use a glo
33
33
 
34
34
  For a project-level config, add the same entry to `.opencode.json` in your repo root.
35
35
 
36
- Once registered, the plugin exposes six tools that your OpenCode agent can call directly — no extra configuration required.
36
+ Once registered, the plugin exposes eight tools that your OpenCode agent can call directly — no extra configuration required.
37
37
 
38
38
  ## Behaviour
39
39
 
@@ -68,13 +68,27 @@ The working directory (`dir`) is injected automatically from your OpenCode sessi
68
68
 
69
69
  ### `avenor_status`
70
70
 
71
- Get status of a specific run or all active runs. Surfaces any pending permission requests the sub-agent is blocked on.
71
+ 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.
72
72
 
73
73
  | Argument | Required | Description |
74
74
  |---|---|---|
75
75
  | `run_id` | no | Specific run ID to query (omit for all runs) |
76
+ | `view` | no | Response detail: `lifecycle` or `full` (default) |
76
77
  | `supervisor_id` | no | Reuse an existing supervisor by socket path |
77
78
 
79
+ ### `avenor_result`
80
+
81
+ Wait for a run to finish and return its bounded final output without transcript or event details. This is the output retrieval tool; `avenor_status` remains a lifecycle check.
82
+
83
+ | Argument | Required | Description |
84
+ |---|---|---|
85
+ | `run_id` | yes | Run ID to await |
86
+ | `wait` | no | Wait for a terminal result (default `true`) |
87
+ | `timeout` | no | Maximum time to wait (for example `30s` or `5m`) |
88
+ | `supervisor_id` | no | Reuse an existing supervisor by socket path |
89
+
90
+ A blocked run returns its pending permission instead of waiting forever. If this tool's own timeout expires, it returns the latest status with `ready: false` and `timed_out: true`.
91
+
78
92
  ### `avenor_answer_permission`
79
93
 
80
94
  Answer a pending permission request. The `option_id` comes from the `pending_permission.options` array returned by `avenor_status`.
@@ -123,8 +137,8 @@ Shut down the avenor supervisor process and clean up temp files. Call this when
123
137
  ```
124
138
  1. avenor_spawn → tool call shows live progress, blocks until done
125
139
  2. (avenor_answer_permission → if a permission is routed to this session mid-run)
126
- 3. tool call returns → structured result with status + session_id
127
- 4. avenor_events → inspect detailed output
140
+ 3. tool call returns → completion preview with status + session_id
141
+ 4. avenor_result → retrieve the bounded final output when needed
128
142
  5. avenor_follow_up → optionally iterate
129
143
  6. avenor_shutdown → clean up when finished
130
144
  ```
@@ -135,7 +149,8 @@ Shut down the avenor supervisor process and clean up temp files. Call this when
135
149
  2. plugin starts monitoring each run immediately
136
150
  3. (sub-agent finishes) → plugin re-prompts this session automatically
137
151
  4. avenor_answer_permission → if a permission was routed here mid-run
138
- 5. avenor_events → inspect output
152
+ 5. avenor_result → wait for and retrieve the final output
153
+ 6. avenor_events → inspect raw event history when needed
139
154
  ```
140
155
 
141
156
  ## Dependencies
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, followUpTool, inspectTool, observeRun, shutdownTool, spawnTool, statusTool } from "@dougbots/avenor-core";
3
+ import { Supervisor, answerPermissionTool, createRunSnapshot, dial, eventsTool, followUpTool, inspectTool, observeRun, resultTool, shutdownTool, spawnTool, statusTool } from "@dougbots/avenor-core";
4
4
  //#region src/plugin.ts
5
5
  const TERMINAL_STATUSES = new Set([
6
6
  "done",
@@ -206,6 +206,10 @@ const AvenorPlugin = async (ctx) => {
206
206
  kind: "aborted",
207
207
  snapshot: current
208
208
  };
209
+ if (handle.stopReason === "result") return {
210
+ kind: "stopped",
211
+ snapshot: current
212
+ };
209
213
  return {
210
214
  kind: "shutdown",
211
215
  snapshot: current
@@ -241,6 +245,10 @@ const AvenorPlugin = async (ctx) => {
241
245
  kind: "shutdown",
242
246
  snapshot: current
243
247
  };
248
+ if (handle.stopReason === "result") return {
249
+ kind: "stopped",
250
+ snapshot: current
251
+ };
244
252
  if (current.pending_permission || current.phase === "waiting") return {
245
253
  kind: "waiting",
246
254
  snapshot: current
@@ -471,7 +479,7 @@ const AvenorPlugin = async (ctx) => {
471
479
  monitorRun(run).catch(console.error);
472
480
  return {
473
481
  title: `${result.label} — dispatched`,
474
- output: `Dispatched "${result.label}" (run_id: ${result.run_id}). Call avenor_status with run_id to check progress, or wait for the completion notification.`
482
+ 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.`
475
483
  };
476
484
  }
477
485
  context.metadata({ title: `Starting ${result.label}…` });
@@ -526,19 +534,49 @@ const AvenorPlugin = async (ctx) => {
526
534
  }
527
535
  }),
528
536
  avenor_status: tool({
529
- description: "Get status of a run or all runs. Surfaces pending permission requests.",
537
+ description: "Get lifecycle status of a run or all runs. Use view=\"lifecycle\" for compact polling; full is the compatibility default.",
530
538
  args: {
531
539
  run_id: tool.schema.string().optional().describe("Specific run ID to query"),
540
+ view: tool.schema.enum(["lifecycle", "full"]).optional().describe("Response detail (default full for compatibility)"),
532
541
  supervisor_id: tool.schema.string().optional().describe("Reuse an existing supervisor by socket path")
533
542
  },
534
543
  async execute(args, _context) {
535
544
  const result = await statusTool({
536
545
  runId: args.run_id,
537
- supervisorId: args.supervisor_id
546
+ supervisorId: args.supervisor_id,
547
+ view: args.view
538
548
  });
539
549
  return JSON.stringify(result, null, 2);
540
550
  }
541
551
  }),
552
+ avenor_result: tool({
553
+ description: "Wait for a run to finish and return its bounded final output without transcript or event details.",
554
+ args: {
555
+ run_id: tool.schema.string().describe("Run ID to await"),
556
+ wait: tool.schema.boolean().optional().describe("Wait for a terminal result (default true)"),
557
+ timeout: tool.schema.string().optional().describe("Maximum time to wait (e.g. 30s, 5m)"),
558
+ supervisor_id: tool.schema.string().optional().describe("Reuse an existing supervisor by socket path")
559
+ },
560
+ async execute(args, context) {
561
+ const run = trackedRuns.get(args.run_id);
562
+ if (run) await stopMonitor(run.runId, "result");
563
+ try {
564
+ const result = await resultTool({
565
+ runId: args.run_id,
566
+ supervisorId: args.supervisor_id,
567
+ wait: args.wait,
568
+ timeout: args.timeout,
569
+ signal: context.abort
570
+ });
571
+ if (result.ready) unregisterRun(args.run_id);
572
+ else if (run) monitorRun(run).catch(console.error);
573
+ return JSON.stringify(result, null, 2);
574
+ } catch (error) {
575
+ if (run) monitorRun(run).catch(console.error);
576
+ throw error;
577
+ }
578
+ }
579
+ }),
542
580
  avenor_answer_permission: tool({
543
581
  description: "Answer a pending permission request for a sub-agent. Pass option_id from avenor_status pending_permission.options, or use \"allow_once\", \"allow_always\", \"deny\".",
544
582
  args: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dougbots/avenor-opencode",
3
- "version": "0.4.1",
3
+ "version": "0.5.1",
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.4.1"
30
+ "@dougbots/avenor-core": "^0.5.1"
31
31
  },
32
32
  "peerDependencies": {
33
33
  "@opencode-ai/plugin": "1.15.3"