opencode-courier 0.13.6 → 0.13.7
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 +8 -7
- package/dist/index.js +6 -1
- package/dist/tree/events.js +7 -19
- package/dist/tree/index.d.ts +1 -0
- package/dist/tree/index.js +1 -0
- package/dist/tree/nudge.d.ts +28 -0
- package/dist/tree/nudge.js +75 -0
- package/dist/tree/ports.d.ts +8 -1
- package/dist/tree/store.d.ts +1 -0
- package/dist/tree/store.js +12 -2
- package/dist/tree/text.d.ts +2 -0
- package/dist/tree/text.js +9 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ A narrated tour of the terminal UI: the tree panel, the asks inbox, failing chil
|
|
|
23
23
|
## Quickstart
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
|
-
npm install -g @opencode/cli@2.2.
|
|
26
|
+
npm install -g @opencode/cli@2.2.5 # OpenCode V2, the version courier is tested on
|
|
27
27
|
opencode plugin add opencode-courier
|
|
28
28
|
opencode
|
|
29
29
|
```
|
|
@@ -58,8 +58,9 @@ or stuck, the child calls `courier_send` with a status (`done`, `partial`, `bloc
|
|
|
58
58
|
The message lands in the parent's inbox and starts a new turn if the parent is idle.
|
|
59
59
|
|
|
60
60
|
Children started with the same `group` report together, in one message. A child that fails (a
|
|
61
|
-
rate or quota limit with when it lifts)
|
|
62
|
-
|
|
61
|
+
rate or quota limit with when it lifts) is reported to its parent. One about to end its turn
|
|
62
|
+
without reporting is reminded once, in the same turn, and its parent is told only if it still does
|
|
63
|
+
not report. A message that does not wake its session is sent once more, then shown as `stalled`. On
|
|
63
64
|
OpenCode 2.2.0 a parent's system prompt carries one line per child with its state, kept current,
|
|
64
65
|
so it need not look. Children, held reports and scheduled messages survive a restart. The `courier-orchestrate` skill, listed in every session, tells a model when to
|
|
65
66
|
split a task and how to check and merge the parts. Every detail: [the reference](docs/reference.md).
|
|
@@ -67,10 +68,10 @@ split a task and how to check and merge the parts. Every detail: [the reference]
|
|
|
67
68
|
## Install
|
|
68
69
|
|
|
69
70
|
Requires OpenCode V2: the `opencode` command from `@opencode/cli`. Each release is tested against
|
|
70
|
-
one OpenCode version; this one is tested on **OpenCode 2.2.
|
|
71
|
+
one OpenCode version; this one is tested on **OpenCode 2.2.5** (check with `opencode --version`).
|
|
71
72
|
|
|
72
73
|
```bash
|
|
73
|
-
npm install -g @opencode/cli@2.2.
|
|
74
|
+
npm install -g @opencode/cli@2.2.5
|
|
74
75
|
opencode plugin add opencode-courier
|
|
75
76
|
```
|
|
76
77
|
|
|
@@ -83,13 +84,13 @@ name, without a version: OpenCode offers updates only for an entry without an ex
|
|
|
83
84
|
<!-- compat-table:start rows=3: generated by script/compat-table.ts from the release tags -->
|
|
84
85
|
| opencode-courier | OpenCode V2 (`opencode` and `@opencode/plugin`) |
|
|
85
86
|
|---|---|
|
|
87
|
+
| 0.13.7 | 2.2.5 |
|
|
86
88
|
| 0.13.6 | 2.2.4 |
|
|
87
89
|
| 0.13.5 | 2.2.3 |
|
|
88
|
-
| 0.13.2, 0.13.3, 0.13.4 | 2.2.2 |
|
|
89
90
|
<!-- compat-table:end -->
|
|
90
91
|
|
|
91
92
|
Every release: [the full table](docs/reference.md#the-opencode-version). The server part loads on
|
|
92
|
-
any V2 host from 2.0.4 on. The terminal part is tested on OpenCode 2.2.
|
|
93
|
+
any V2 host from 2.0.4 on. The terminal part is tested on OpenCode 2.2.5 only.
|
|
93
94
|
|
|
94
95
|
## Options
|
|
95
96
|
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import { addSchedulingTools, cancelScheduled, dueJob, isScheduled } from "./sche
|
|
|
10
10
|
import { addPersonTools, asksPermission, asksQuestion, formsShown, joinRelay, noticeCutOff, pendingOf, pendingQuestions, personEvents, portsAt, registerPersonRpc, relayQuestions, } from "./person/index.js";
|
|
11
11
|
import { addMemoryTools, lessonsOf, memoryEvents, NOTE_TOOL, readMemory, RECALL_TOOL, registerMemoryRpc, sweepRecords } from "./memory/index.js";
|
|
12
12
|
import { addSkill } from "./skill.js";
|
|
13
|
-
import { addTreeTools, deliverReleased, wakeMissed, dirtyOf, headOf, inspectWorktree, pruneExpired, readLimits, readNativeChildren, registerTreeRpc, shapeContext, together, treeEvents, waitsOn, } from "./tree/index.js";
|
|
13
|
+
import { addTreeTools, deliverReleased, wakeMissed, dirtyOf, headOf, inspectWorktree, onIdle, pruneExpired, readLimits, readNativeChildren, registerTreeRpc, shapeContext, together, treeEvents, waitsOn, } from "./tree/index.js";
|
|
14
14
|
import { addWebhookTools, joinReceiver, readConfig, receiving, subscribedSince, } from "./webhook/index.js";
|
|
15
15
|
/** The spawns under way in the process, outside the hub, so spawns in other locations, copies and hub versions count them. */
|
|
16
16
|
const gate = sharedState("spawning", () => ({ reserved: new Set(), turn: Promise.resolve() }));
|
|
@@ -171,6 +171,11 @@ export const courier = () => Plugin.define({
|
|
|
171
171
|
log: later.log,
|
|
172
172
|
nudge,
|
|
173
173
|
};
|
|
174
|
+
// A spawned session about to go idle owing a report is reminded once, in the same execution. The hook is
|
|
175
|
+
// experimental, and a host before 2.2.4 lacks it: the rest loads all the same, and its parents are told as before.
|
|
176
|
+
await Promise.resolve()
|
|
177
|
+
.then(() => ctx.session.hook("experimental.idle", (event) => onIdle(watchPorts, watchState.seen, outside, event).then(() => undefined)))
|
|
178
|
+
.catch((error) => log(`courier idle: not registered, a child that ends without a report is not reminded: ${String(error)}`));
|
|
174
179
|
// The hub's subscriptions to OpenCode's events, one and a standby, start with this copy's modules.
|
|
175
180
|
watchFromHub(hub, eventModules);
|
|
176
181
|
const member = {
|
package/dist/tree/events.js
CHANGED
|
@@ -3,7 +3,8 @@ import { claim, SEEN_MAX, setBounded } from "../platform/bounded.js";
|
|
|
3
3
|
import { endsIdle, retryDelay } from "./domain.js";
|
|
4
4
|
import { forgetChild, forgetDeleted } from "./forget.js";
|
|
5
5
|
import { hasCompleteGroup, leave, outIn } from "./group.js";
|
|
6
|
-
import {
|
|
6
|
+
import { owedUnwaited } from "./nudge.js";
|
|
7
|
+
import { stoppedSincePrompt } from "./report.js";
|
|
7
8
|
import { syncStanding } from "./standing.js";
|
|
8
9
|
import { childStore, groupStore } from "./store.js";
|
|
9
10
|
import { lastReply } from "./status.js";
|
|
@@ -71,6 +72,9 @@ export async function notePrompt(ports, event, inbox) {
|
|
|
71
72
|
const parentID = await children.indexedParent(sessionID);
|
|
72
73
|
if (parentID === undefined)
|
|
73
74
|
return false;
|
|
75
|
+
// The reminder to report is not a prompt: it asks for the report the last one owes.
|
|
76
|
+
if (inboxID !== undefined && (await children.nudgeOf(sessionID)).inboxID === inboxID)
|
|
77
|
+
return false;
|
|
74
78
|
await children.prompted(sessionID, event.created ?? ports.now());
|
|
75
79
|
await syncStanding(ports, parentID);
|
|
76
80
|
return true;
|
|
@@ -122,20 +126,6 @@ export const waitsOn = (...suppliers) => async (ports, sessionID, since) => {
|
|
|
122
126
|
};
|
|
123
127
|
/** A `Waits` asking `suppliers` at once, as cheap as each other: it waits when one does, and fails when one fails. */
|
|
124
128
|
export const together = (...suppliers) => async (ports, sessionID, since) => (await Promise.all(suppliers.map((supplier) => supplier(ports, sessionID, since)))).includes(true);
|
|
125
|
-
/**
|
|
126
|
-
* Whether a session prompted at `prompt` waits: on an untold report of a session it started, a complete group of
|
|
127
|
-
* its about to be delivered, or what `outside` says it waits on. Cheapest first.
|
|
128
|
-
*/
|
|
129
|
-
async function waits(ports, outside, sessionID, prompt) {
|
|
130
|
-
const children = childStore(ports.storage);
|
|
131
|
-
const started = await children.children(sessionID);
|
|
132
|
-
if ((await Promise.all(started.map((entry) => awaited(children, entry.sessionID)))).includes(true))
|
|
133
|
-
return true;
|
|
134
|
-
// A group with a member still out is waited on through that member, above; one whose every out member was told of is not.
|
|
135
|
-
if (started.some((entry) => entry.group) && (await hasCompleteGroup(groupStore(ports.storage), sessionID)))
|
|
136
|
-
return true;
|
|
137
|
-
return outside(ports, sessionID, prompt);
|
|
138
|
-
}
|
|
139
129
|
/**
|
|
140
130
|
* Tells the parent of a spawned session whose turn ended without reporting to it since its last
|
|
141
131
|
* prompt, unless it waits; finds the session by the reverse index alone. Returns the parents told.
|
|
@@ -147,10 +137,8 @@ export async function reportSilent(ports, seen, event, outside) {
|
|
|
147
137
|
// Claimed after the lookup, so other sessions' turns do not crowd `seen`, and before anything is told.
|
|
148
138
|
if (!entry || !claim(seen, event.id))
|
|
149
139
|
return [];
|
|
150
|
-
const report = await
|
|
151
|
-
if (!report
|
|
152
|
-
return [];
|
|
153
|
-
if (await stoppedSincePrompt(children, sessionID).catch(() => false))
|
|
140
|
+
const report = await owedUnwaited(ports, children, outside, sessionID);
|
|
141
|
+
if (!report)
|
|
154
142
|
return [];
|
|
155
143
|
// The notice goes out without the reply rather than not at all.
|
|
156
144
|
const lastText = await ports.session.context({ sessionID }).then(lastReply, () => undefined);
|
package/dist/tree/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export { shapeContext } from "./context.js";
|
|
|
3
3
|
export { endsIdle, lineageIn, nativeRootOf, readLimits, readNativeChildren } from "./domain.js";
|
|
4
4
|
export { together, treeEvents, waitsOn } from "./events.js";
|
|
5
5
|
export { deliverReleased } from "./group.js";
|
|
6
|
+
export { onIdle } from "./nudge.js";
|
|
6
7
|
export { contextOf } from "./panel.js";
|
|
7
8
|
export type { CleanupPorts, CourierPorts, Entries, Entry, Instructions, ModelWindow, Pending, PendingPrompt, Role, RosterEntry, RosterStorage, ShownForm, SpawnGate, Waits, WatchPorts, WatchState, } from "./ports.js";
|
|
8
9
|
export { allEntries, answeringTop, childrenOf, indexedLineage, lineage, pruneExpired, reportState, RETENTION_MS, type ReportState } from "./roster.js";
|
package/dist/tree/index.js
CHANGED
|
@@ -5,6 +5,7 @@ export { shapeContext } from "./context.js";
|
|
|
5
5
|
export { endsIdle, lineageIn, nativeRootOf, readLimits, readNativeChildren } from "./domain.js";
|
|
6
6
|
export { together, treeEvents, waitsOn } from "./events.js";
|
|
7
7
|
export { deliverReleased } from "./group.js";
|
|
8
|
+
export { onIdle } from "./nudge.js";
|
|
8
9
|
export { contextOf } from "./panel.js";
|
|
9
10
|
export { allEntries, answeringTop, childrenOf, indexedLineage, lineage, pruneExpired, reportState, RETENTION_MS } from "./roster.js";
|
|
10
11
|
export { runs, turnIn, turnOf } from "./running.js";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ChildStore, Waits, WatchPorts } from "./ports.js";
|
|
2
|
+
/** OpenCode's `experimental.idle` hook event: a session about to go idle, and the background work still running for it or below it. */
|
|
3
|
+
export interface Idle {
|
|
4
|
+
readonly sessionID: string;
|
|
5
|
+
readonly background: ReadonlyArray<{
|
|
6
|
+
readonly sessionID: string;
|
|
7
|
+
}>;
|
|
8
|
+
}
|
|
9
|
+
/** The ports the reminder is sent through: the watcher's. */
|
|
10
|
+
export type NudgePorts = Pick<WatchPorts, "storage" | "session" | "permissions" | "now" | "log">;
|
|
11
|
+
/**
|
|
12
|
+
* The report a spawned session owes since its last prompt, unless it waits or courier_stop stopped it since: what the
|
|
13
|
+
* silent-turn notice tells the parent of, and the reminder reminds the session of.
|
|
14
|
+
*/
|
|
15
|
+
export declare function owedUnwaited(ports: NudgePorts, children: ChildStore, outside: Waits, sessionID: string): Promise<{
|
|
16
|
+
resumesAt?: number | undefined;
|
|
17
|
+
prompt: number;
|
|
18
|
+
owes: boolean;
|
|
19
|
+
progressed: boolean;
|
|
20
|
+
ended: boolean;
|
|
21
|
+
} | undefined>;
|
|
22
|
+
/**
|
|
23
|
+
* Reminds a spawned session about to go idle owing a report it does not wait on, once per prompt, so it reports in the
|
|
24
|
+
* same execution; one with background work of its own is woken again by it. Returns whether it was reminded.
|
|
25
|
+
*/
|
|
26
|
+
export declare function nudgeSilent(ports: NudgePorts, seen: Set<string>, outside: Waits, idle: Idle): Promise<boolean>;
|
|
27
|
+
/** `nudgeSilent` as the hook runs it: a hook that fails fails the execution, so a failure is logged, never thrown. */
|
|
28
|
+
export declare function onIdle(ports: NudgePorts, seen: Set<string>, outside: Waits, idle: Idle): Promise<boolean>;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { envelope } from "../kernel/protocol.js";
|
|
2
|
+
import { claim } from "../platform/bounded.js";
|
|
3
|
+
import { hasCompleteGroup } from "./group.js";
|
|
4
|
+
import { awaited, reportOf, stoppedSincePrompt } from "./report.js";
|
|
5
|
+
import { childStore, groupStore } from "./store.js";
|
|
6
|
+
import { reportReminder } from "./text.js";
|
|
7
|
+
/**
|
|
8
|
+
* Whether a session prompted at `prompt` waits: on an untold report of a session it started, a complete group of
|
|
9
|
+
* its about to be delivered, or what `outside` says it waits on. Cheapest first.
|
|
10
|
+
*/
|
|
11
|
+
async function waits(ports, outside, sessionID, prompt) {
|
|
12
|
+
const children = childStore(ports.storage);
|
|
13
|
+
const started = await children.children(sessionID);
|
|
14
|
+
if ((await Promise.all(started.map((entry) => awaited(children, entry.sessionID)))).includes(true))
|
|
15
|
+
return true;
|
|
16
|
+
// A group with a member still out is waited on through that member, above; one whose every out member was told of is not.
|
|
17
|
+
if (started.some((entry) => entry.group) && (await hasCompleteGroup(groupStore(ports.storage), sessionID)))
|
|
18
|
+
return true;
|
|
19
|
+
return outside(ports, sessionID, prompt);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The report a spawned session owes since its last prompt, unless it waits or courier_stop stopped it since: what the
|
|
23
|
+
* silent-turn notice tells the parent of, and the reminder reminds the session of.
|
|
24
|
+
*/
|
|
25
|
+
export async function owedUnwaited(ports, children, outside, sessionID) {
|
|
26
|
+
const report = await reportOf(children, sessionID);
|
|
27
|
+
if (!report?.owes || (await waits(ports, outside, sessionID, report.prompt)))
|
|
28
|
+
return undefined;
|
|
29
|
+
if (await stoppedSincePrompt(children, sessionID).catch(() => false))
|
|
30
|
+
return undefined;
|
|
31
|
+
return report;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Reminds a spawned session about to go idle owing a report it does not wait on, once per prompt, so it reports in the
|
|
35
|
+
* same execution; one with background work of its own is woken again by it. Returns whether it was reminded.
|
|
36
|
+
*/
|
|
37
|
+
export async function nudgeSilent(ports, seen, outside, idle) {
|
|
38
|
+
const { sessionID } = idle;
|
|
39
|
+
if (idle.background.some((work) => work.sessionID === sessionID))
|
|
40
|
+
return false;
|
|
41
|
+
const children = childStore(ports.storage);
|
|
42
|
+
const parentID = await children.indexedParent(sessionID);
|
|
43
|
+
if (parentID === undefined)
|
|
44
|
+
return false;
|
|
45
|
+
const report = await owedUnwaited(ports, children, outside, sessionID);
|
|
46
|
+
if (!report)
|
|
47
|
+
return false;
|
|
48
|
+
const { at: nudgedAt } = await children.nudgeOf(sessionID);
|
|
49
|
+
if ((nudgedAt !== undefined && nudgedAt >= report.prompt) || !claim(seen, `nudge/${sessionID}/${report.prompt}`))
|
|
50
|
+
return false;
|
|
51
|
+
const at = Math.max(ports.now(), report.prompt);
|
|
52
|
+
// Noted before it is sent, so a second hook never sends another, and with its id after, before the turn takes it.
|
|
53
|
+
await children.nudged(sessionID, at);
|
|
54
|
+
const { id } = await ports.session.synthetic({
|
|
55
|
+
sessionID,
|
|
56
|
+
text: envelope("courier", reportReminder(parentID), { reminder: "report" }),
|
|
57
|
+
description: "Reminded to report",
|
|
58
|
+
metadata: { source: "courier", reminder: "report", resume: false },
|
|
59
|
+
delivery: "steer",
|
|
60
|
+
// The hook's own execution takes it: no wake, and the missed-wake watch leaves it be.
|
|
61
|
+
resume: false,
|
|
62
|
+
});
|
|
63
|
+
await children.nudged(sessionID, at, id);
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
/** `nudgeSilent` as the hook runs it: a hook that fails fails the execution, so a failure is logged, never thrown. */
|
|
67
|
+
export async function onIdle(ports, seen, outside, idle) {
|
|
68
|
+
try {
|
|
69
|
+
return await nudgeSilent(ports, seen, outside, idle);
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
ports.log(`courier idle: could not remind ${idle.sessionID} to report: ${String(error)}`);
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
package/dist/tree/ports.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export type RosterStorage = Storage;
|
|
|
12
12
|
/** How a session's wait for a report ended: it reported to its parent, or its turn failed or was interrupted. */
|
|
13
13
|
export type Settled = "report" | "failed" | "interrupted";
|
|
14
14
|
/** The notes kept of a spawned session's report state; docs/maintainers/architecture.md has each. */
|
|
15
|
-
export type ReportField = "prompt" | "settled" | "progress" | "told" | "stopped";
|
|
15
|
+
export type ReportField = "prompt" | "settled" | "progress" | "told" | "stopped" | "nudged";
|
|
16
16
|
/** A report note as read: when it was made, and for `settled` how the wait ended, the report's status or a failure's `resumesAt`, as stored. */
|
|
17
17
|
export interface ReportNote {
|
|
18
18
|
readonly at: number | undefined;
|
|
@@ -75,6 +75,13 @@ export interface ChildStore {
|
|
|
75
75
|
untold(sessionID: string, at: number): Promise<void>;
|
|
76
76
|
/** Notes that courier_stop stopped a spawned session at `at`. */
|
|
77
77
|
stopped(sessionID: string, at: number): Promise<void>;
|
|
78
|
+
/** Notes that a spawned session was reminded at `at` to report, with the reminder's inbox id once it is admitted. */
|
|
79
|
+
nudged(sessionID: string, at: number, inboxID?: string): Promise<void>;
|
|
80
|
+
/** When a spawned session was last reminded to report, and the reminder's inbox id. */
|
|
81
|
+
nudgeOf(sessionID: string): Promise<{
|
|
82
|
+
readonly at: number | undefined;
|
|
83
|
+
readonly inboxID?: string;
|
|
84
|
+
}>;
|
|
78
85
|
/** A `settled` or `progress` note as stored, for `restore` to put back. */
|
|
79
86
|
peek(sessionID: string, field: "settled" | "progress"): Promise<Stored | undefined>;
|
|
80
87
|
/** Puts back a note `peek` read; one that was not there is removed. */
|
package/dist/tree/store.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export declare const settledKey: (sessionID: string) => string;
|
|
|
14
14
|
export declare const progressKey: (sessionID: string) => string;
|
|
15
15
|
export declare const toldKey: (sessionID: string) => string;
|
|
16
16
|
export declare const stoppedKey: (sessionID: string) => string;
|
|
17
|
+
export declare const nudgedKey: (sessionID: string) => string;
|
|
17
18
|
export declare const memberKey: (parentID: string, group: string, sessionID: string) => string;
|
|
18
19
|
/** The key prefix of a parent's groups, and of one group's members. */
|
|
19
20
|
export declare const groupsPrefix: (parentID: string) => string;
|
package/dist/tree/store.js
CHANGED
|
@@ -17,8 +17,8 @@ export function reverseKey(sessionID) {
|
|
|
17
17
|
return `${BY_CHILD}${sessionID}`;
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
|
-
* Whether a spawned session has reported since its last prompt, under `report/<sessionID>/`: `prompt`, `settled`
|
|
21
|
-
*
|
|
20
|
+
* Whether a spawned session has reported since its last prompt, under `report/<sessionID>/`: `prompt`, `settled`, `progress`,
|
|
21
|
+
* `told`, `stopped` and `nudged` (the reminder to report); docs/maintainers/architecture.md has each.
|
|
22
22
|
*/
|
|
23
23
|
const REPORT_PREFIX = "report/";
|
|
24
24
|
export const promptKey = (sessionID) => `${REPORT_PREFIX}${sessionID}/prompt`;
|
|
@@ -26,12 +26,14 @@ export const settledKey = (sessionID) => `${REPORT_PREFIX}${sessionID}/settled`;
|
|
|
26
26
|
export const progressKey = (sessionID) => `${REPORT_PREFIX}${sessionID}/progress`;
|
|
27
27
|
export const toldKey = (sessionID) => `${REPORT_PREFIX}${sessionID}/told`;
|
|
28
28
|
export const stoppedKey = (sessionID) => `${REPORT_PREFIX}${sessionID}/stopped`;
|
|
29
|
+
export const nudgedKey = (sessionID) => `${REPORT_PREFIX}${sessionID}/nudged`;
|
|
29
30
|
const REPORT_KEYS = {
|
|
30
31
|
prompt: promptKey,
|
|
31
32
|
settled: settledKey,
|
|
32
33
|
progress: progressKey,
|
|
33
34
|
told: toldKey,
|
|
34
35
|
stopped: stoppedKey,
|
|
36
|
+
nudged: nudgedKey,
|
|
35
37
|
};
|
|
36
38
|
/**
|
|
37
39
|
* Join groups: each member of a parent's group under `group/<parentID>/<name>/<sessionID>`, with its
|
|
@@ -195,6 +197,14 @@ export function childStore(storage) {
|
|
|
195
197
|
async stopped(sessionID, at) {
|
|
196
198
|
await storage.set(stoppedKey(sessionID), { at });
|
|
197
199
|
},
|
|
200
|
+
async nudged(sessionID, at, inboxID) {
|
|
201
|
+
await storage.set(nudgedKey(sessionID), { at, ...(inboxID === undefined ? {} : { inboxID }) });
|
|
202
|
+
},
|
|
203
|
+
async nudgeOf(sessionID) {
|
|
204
|
+
const value = await storage.get(nudgedKey(sessionID));
|
|
205
|
+
const inboxID = str(obj(value).inboxID);
|
|
206
|
+
return { at: timeOf(value), ...(inboxID === undefined ? {} : { inboxID }) };
|
|
207
|
+
},
|
|
198
208
|
async peek(sessionID, field) {
|
|
199
209
|
const value = await storage.get(REPORT_KEYS[field](sessionID));
|
|
200
210
|
return value;
|
package/dist/tree/text.d.ts
CHANGED
|
@@ -108,6 +108,8 @@ export declare function keepReason(state: {
|
|
|
108
108
|
* retry time (`retryAfterMs`) counts from. Message the child before that and it fails again, so the notice says to wait.
|
|
109
109
|
*/
|
|
110
110
|
export declare function failureNotice(title: string, error: ExecutionError, group?: string, at?: number): string;
|
|
111
|
+
/** The one reminder a spawned session gets as it is about to end its turn owing its parent a report; it carries on in the same turn. */
|
|
112
|
+
export declare function reportReminder(parentID: string): string;
|
|
111
113
|
/**
|
|
112
114
|
* What the parent is told when a session it started ends its turn without a report to it;
|
|
113
115
|
* `progressed` when it messaged the parent without a status since its last prompt.
|
package/dist/tree/text.js
CHANGED
|
@@ -364,6 +364,15 @@ export function failureNotice(title, error, group, at) {
|
|
|
364
364
|
: []),
|
|
365
365
|
].join("\n");
|
|
366
366
|
}
|
|
367
|
+
/** The one reminder a spawned session gets as it is about to end its turn owing its parent a report; it carries on in the same turn. */
|
|
368
|
+
export function reportReminder(parentID) {
|
|
369
|
+
return [
|
|
370
|
+
`You are about to end your turn without reporting to session ${parentID}, which started you.`,
|
|
371
|
+
`If you are finished, send your report now: call courier_send with sessionID "${parentID}", a short account of what you did and ` +
|
|
372
|
+
`status ${STATUS_LIST}. If work is left, carry on and report when you finish.`,
|
|
373
|
+
"This reminder comes once: if you end your turn without a report, the session that started you is told.",
|
|
374
|
+
].join("\n");
|
|
375
|
+
}
|
|
367
376
|
/** Longest last reply a notice that a session ended its turn without reporting quotes. */
|
|
368
377
|
const MAX_REPLY = 2000;
|
|
369
378
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-courier",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.7",
|
|
4
4
|
"description": "Multi-session orchestration for OpenCode V2: spawn child sessions, be woken by them. No polling.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Ivo Pogace <71701666+ivopogace@users.noreply.github.com>",
|
|
@@ -56,12 +56,12 @@
|
|
|
56
56
|
"test:e2e": "e2e/run.sh"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
|
-
"@opencode/plugin": "2.2.
|
|
59
|
+
"@opencode/plugin": "2.2.5",
|
|
60
60
|
"effect": "4.0.6"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
|
-
"@opencode/plugin": "2.2.
|
|
64
|
-
"@opencode/theme": "2.2.
|
|
63
|
+
"@opencode/plugin": "2.2.5",
|
|
64
|
+
"@opencode/theme": "2.2.5",
|
|
65
65
|
"@opentui/core": "0.8.0",
|
|
66
66
|
"@opentui/solid": "0.8.0",
|
|
67
67
|
"@types/bun": "1.4.2",
|