opencode-courier 0.13.8 → 0.15.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 +7 -9
- package/dist/index.d.ts +0 -8
- package/dist/index.js +7 -54
- package/dist/kernel/protocol.d.ts +3 -17
- package/dist/kernel/protocol.js +3 -17
- package/dist/kernel/tool.d.ts +0 -2
- package/dist/kernel/tool.js +0 -1
- package/dist/memory/domain.d.ts +6 -85
- package/dist/memory/domain.js +7 -63
- package/dist/memory/events.d.ts +0 -11
- package/dist/memory/events.js +1 -12
- package/dist/memory/forget.d.ts +0 -5
- package/dist/memory/forget.js +0 -4
- package/dist/memory/index.js +0 -2
- package/dist/memory/lessons.d.ts +1 -9
- package/dist/memory/lessons.js +2 -11
- package/dist/memory/note.d.ts +2 -11
- package/dist/memory/note.js +5 -11
- package/dist/memory/pin.d.ts +0 -5
- package/dist/memory/pin.js +0 -4
- package/dist/memory/ports.d.ts +2 -22
- package/dist/memory/recall.d.ts +0 -5
- package/dist/memory/recall.js +0 -12
- package/dist/memory/record.d.ts +0 -4
- package/dist/memory/record.js +8 -31
- package/dist/memory/rpc-handlers.d.ts +3 -6
- package/dist/memory/rpc-handlers.js +0 -6
- package/dist/memory/rpc.d.ts +6 -19
- package/dist/memory/rpc.js +1 -16
- package/dist/memory/store.d.ts +0 -5
- package/dist/memory/store.js +1 -16
- package/dist/memory/sweep.d.ts +0 -4
- package/dist/memory/sweep.js +0 -4
- package/dist/memory/text.d.ts +1 -26
- package/dist/memory/text.js +8 -38
- package/dist/memory/tools.d.ts +0 -2
- package/dist/memory/tools.js +4 -6
- package/dist/memory/tui.d.ts +1 -35
- package/dist/person/events.d.ts +0 -16
- package/dist/person/events.js +3 -30
- package/dist/person/form.d.ts +0 -9
- package/dist/person/form.js +0 -10
- package/dist/person/index.d.ts +0 -2
- package/dist/person/permission.d.ts +0 -34
- package/dist/person/permission.js +2 -28
- package/dist/person/ports.d.ts +0 -16
- package/dist/person/ports.js +0 -1
- package/dist/person/question/answer.d.ts +0 -28
- package/dist/person/question/answer.js +0 -49
- package/dist/person/question/index.js +0 -2
- package/dist/person/question/lifecycle.d.ts +0 -26
- package/dist/person/question/lifecycle.js +1 -32
- package/dist/person/question/pure.d.ts +0 -11
- package/dist/person/question/pure.js +0 -11
- package/dist/person/question/relay.d.ts +0 -1
- package/dist/person/question/relay.js +5 -67
- package/dist/person/question/runtime.d.ts +0 -14
- package/dist/person/question/runtime.js +1 -26
- package/dist/person/question/shared.d.ts +0 -17
- package/dist/person/question/shared.js +0 -20
- package/dist/person/question/types.d.ts +0 -44
- package/dist/person/rpc-handlers.d.ts +0 -7
- package/dist/person/rpc-handlers.js +0 -6
- package/dist/person/rpc.d.ts +0 -20
- package/dist/person/rpc.js +0 -16
- package/dist/person/text.d.ts +0 -45
- package/dist/person/text.js +2 -54
- package/dist/person/tools.d.ts +0 -3
- package/dist/person/tools.js +3 -4
- package/dist/person/tui.d.ts +0 -21
- package/dist/platform/bounded.d.ts +0 -8
- package/dist/platform/bounded.js +0 -8
- package/dist/platform/events.d.ts +0 -18
- package/dist/platform/events.js +3 -22
- package/dist/platform/hub.d.ts +0 -113
- package/dist/platform/hub.js +1 -76
- package/dist/platform/json.d.ts +0 -3
- package/dist/platform/json.js +1 -3
- package/dist/platform/storage.d.ts +0 -7
- package/dist/platform/storage.js +0 -6
- package/dist/platform/version.d.ts +0 -9
- package/dist/platform/version.js +0 -8
- package/dist/rpc.js +0 -1
- package/dist/scheduling/index.d.ts +0 -1
- package/dist/scheduling/index.js +0 -1
- package/dist/scheduling/later.d.ts +0 -11
- package/dist/scheduling/later.js +0 -14
- package/dist/scheduling/text.d.ts +0 -1
- package/dist/scheduling/text.js +0 -1
- package/dist/scheduling/tools.d.ts +0 -1
- package/dist/scheduling/tools.js +6 -8
- package/dist/skill.d.ts +1 -9
- package/dist/skill.js +40 -97
- package/dist/tree/cleanup.d.ts +0 -20
- package/dist/tree/cleanup.js +0 -26
- package/dist/tree/context.d.ts +0 -5
- package/dist/tree/context.js +17 -13
- package/dist/tree/domain.d.ts +0 -64
- package/dist/tree/domain.js +7 -45
- package/dist/tree/events.d.ts +5 -36
- package/dist/tree/events.js +23 -55
- package/dist/tree/forget.d.ts +0 -15
- package/dist/tree/forget.js +0 -18
- package/dist/tree/group.d.ts +0 -22
- package/dist/tree/group.js +6 -39
- package/dist/tree/index.d.ts +2 -2
- package/dist/tree/index.js +1 -3
- package/dist/tree/limits.d.ts +1 -4
- package/dist/tree/limits.js +9 -13
- package/dist/tree/mascot.d.ts +0 -25
- package/dist/tree/mascot.js +1 -32
- package/dist/tree/model.d.ts +0 -5
- package/dist/tree/model.js +2 -15
- package/dist/tree/nudge.d.ts +0 -11
- package/dist/tree/nudge.js +3 -16
- package/dist/tree/panel.d.ts +0 -8
- package/dist/tree/panel.js +3 -16
- package/dist/tree/ports.d.ts +5 -139
- package/dist/tree/report.d.ts +1 -10
- package/dist/tree/report.js +4 -11
- package/dist/tree/roster.d.ts +0 -21
- package/dist/tree/roster.js +0 -21
- package/dist/tree/rpc-handlers.d.ts +6 -1
- package/dist/tree/rpc-handlers.js +0 -5
- package/dist/tree/rpc.d.ts +9 -109
- package/dist/tree/rpc.js +2 -38
- package/dist/tree/running.d.ts +0 -6
- package/dist/tree/running.js +2 -10
- package/dist/tree/send.d.ts +0 -4
- package/dist/tree/send.js +4 -31
- package/dist/tree/spawn.d.ts +1 -7
- package/dist/tree/spawn.js +15 -56
- package/dist/tree/standing.d.ts +0 -8
- package/dist/tree/standing.js +1 -11
- package/dist/tree/status.d.ts +0 -8
- package/dist/tree/status.js +1 -9
- package/dist/tree/stop.d.ts +2 -9
- package/dist/tree/stop.js +58 -25
- package/dist/tree/store.d.ts +0 -8
- package/dist/tree/store.js +4 -38
- package/dist/tree/subtree.d.ts +8 -26
- package/dist/tree/subtree.js +37 -35
- package/dist/tree/summary.d.ts +0 -4
- package/dist/tree/summary.js +0 -8
- package/dist/tree/text.d.ts +4 -46
- package/dist/tree/text.js +44 -83
- package/dist/tree/tools.d.ts +0 -6
- package/dist/tree/tools.js +15 -15
- package/dist/tree/tui.d.ts +0 -81
- package/dist/tree/wake.d.ts +0 -30
- package/dist/tree/wake.js +5 -39
- package/dist/tui.js +24 -10
- package/package.json +8 -9
- package/dist/webhook/index.d.ts +0 -5
- package/dist/webhook/index.js +0 -3
- package/dist/webhook/receiver.d.ts +0 -80
- package/dist/webhook/receiver.js +0 -339
- package/dist/webhook/subscriptions.d.ts +0 -29
- package/dist/webhook/subscriptions.js +0 -52
- package/dist/webhook/text.d.ts +0 -23
- package/dist/webhook/text.js +0 -82
- package/dist/webhook/tools.d.ts +0 -19
- package/dist/webhook/tools.js +0 -46
|
@@ -10,5 +10,4 @@ export declare const LaterInput: Schema.Struct<{
|
|
|
10
10
|
export declare const CancelInput: Schema.Struct<{
|
|
11
11
|
readonly id: Schema.String;
|
|
12
12
|
}>;
|
|
13
|
-
/** Adds `courier_later` and `courier_cancel`. */
|
|
14
13
|
export declare function addSchedulingTools(tools: ToolEditor, later: LaterPorts): void;
|
package/dist/scheduling/tools.js
CHANGED
|
@@ -4,10 +4,7 @@ import { cancel, schedule } from "./later.js";
|
|
|
4
4
|
import { cancelText, laterText } from "./text.js";
|
|
5
5
|
export const LaterInput = Schema.Struct({
|
|
6
6
|
message: Schema.String.annotate({ description: "The message to deliver." }),
|
|
7
|
-
delayMinutes: Schema.optional(
|
|
8
|
-
// Some models send the number as a string ("3", "3.0") whatever the schema says, and resend it
|
|
9
|
-
// unchanged after the error, so a string is accepted and schedule reads it as a number.
|
|
10
|
-
Schema.Union([Schema.Number, Schema.String]).annotate({
|
|
7
|
+
delayMinutes: Schema.optional(Schema.Union([Schema.Number, Schema.String]).annotate({
|
|
11
8
|
description: "Deliver this many minutes from now. Give this or at.",
|
|
12
9
|
})),
|
|
13
10
|
at: Schema.optional(Schema.String.annotate({ description: "Deliver at this ISO 8601 time. Give this or delayMinutes." })),
|
|
@@ -16,14 +13,14 @@ export const LaterInput = Schema.Struct({
|
|
|
16
13
|
export const CancelInput = Schema.Struct({
|
|
17
14
|
id: Schema.String.annotate({ description: "The id courier_later returned." }),
|
|
18
15
|
});
|
|
19
|
-
/** Adds `courier_later` and `courier_cancel`. */
|
|
20
16
|
export function addSchedulingTools(tools, later) {
|
|
21
17
|
tools.add({
|
|
22
18
|
name: "courier_later",
|
|
23
19
|
options: { codemode: false },
|
|
24
20
|
description: "Schedule a message for a session (this one by default), delivered when due and waking it if idle. " +
|
|
25
|
-
"Use it as a safety net when you start sessions: schedule one check-in
|
|
26
|
-
"calling more tools, and cancel it with courier_cancel
|
|
21
|
+
"Use it as a safety net when you start sessions: schedule one check-in per group (per session " +
|
|
22
|
+
"when ungrouped), end your turn by replying without calling more tools, and cancel it with courier_cancel " +
|
|
23
|
+
"when the reports arrive, as a report does not cancel it. Survives server restarts; " +
|
|
27
24
|
"may arrive up to ~15 seconds late.",
|
|
28
25
|
input: LaterInput,
|
|
29
26
|
execute: async (input, context) => {
|
|
@@ -38,7 +35,8 @@ export function addSchedulingTools(tools, later) {
|
|
|
38
35
|
tools.add({
|
|
39
36
|
name: "courier_cancel",
|
|
40
37
|
options: { codemode: false },
|
|
41
|
-
description: "Cancel a message scheduled with courier_later, e.g.
|
|
38
|
+
description: "Cancel a message scheduled with courier_later, e.g. a check-in whose reports arrived. Nothing else cancels " +
|
|
39
|
+
"it but courier_stop of the session it is for.",
|
|
42
40
|
input: CancelInput,
|
|
43
41
|
execute: async (input) => {
|
|
44
42
|
const cancelled = await cancel(later, input.id).catch(rethrow("courier_cancel"));
|
package/dist/skill.d.ts
CHANGED
|
@@ -1,20 +1,12 @@
|
|
|
1
1
|
import type { Plugin } from "@opencode/plugin";
|
|
2
|
-
/** Why a model loads the skill, listed with every session's available skills. */
|
|
3
2
|
export declare const SKILL_DESCRIPTION: string;
|
|
4
|
-
/**
|
|
5
|
-
* The skill's text: how the tools behave, not how to plan or review the parts, which is left to the person's own skills.
|
|
6
|
-
* It names no limits, which the role part and the brief give; it repeats the brief's split rule, since the root has no brief.
|
|
7
|
-
*/
|
|
8
3
|
export declare const SKILL_CONTENT: string;
|
|
9
|
-
/** The editor OpenCode hands a skill transform. */
|
|
10
4
|
export type SkillEditor = Parameters<Parameters<Plugin.Context["skill"]["transform"]>[0]>[0];
|
|
11
|
-
/** The courier-orchestrate skill as OpenCode lists it: its id, name and description, and the text the model loads. */
|
|
12
5
|
export declare const orchestrateSkill: () => {
|
|
13
6
|
readonly id: "courier-orchestrate";
|
|
14
7
|
readonly name: "courier-orchestrate";
|
|
15
8
|
readonly description: string;
|
|
16
|
-
readonly path: "
|
|
9
|
+
readonly path: "skill://courier-orchestrate/SKILL.md";
|
|
17
10
|
readonly content: string;
|
|
18
11
|
};
|
|
19
|
-
/** Adds the courier-orchestrate skill; every instance adds the same one, under the same id. */
|
|
20
12
|
export declare function addSkill(skills: SkillEditor): void;
|
package/dist/skill.js
CHANGED
|
@@ -1,122 +1,65 @@
|
|
|
1
1
|
import { ONE_PART_LINE, SKILL_ID, SPLIT_LINE } from "./kernel/protocol.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
"independent, substantial parts touching separate files or areas. Starting the parts, waiting without polling, " +
|
|
6
|
-
"relaying questions and permissions, reading reports, merging and reporting upward.";
|
|
7
|
-
/**
|
|
8
|
-
* The skill's text: how the tools behave, not how to plan or review the parts, which is left to the person's own skills.
|
|
9
|
-
* It names no limits, which the role part and the brief give; it repeats the brief's split rule, since the root has no brief.
|
|
10
|
-
*/
|
|
2
|
+
export const SKILL_DESCRIPTION = "How opencode-courier's session tools work. Load it when you decide whether to split a task across sessions with " +
|
|
3
|
+
"courier_spawn: one with 2 or more independent, substantial parts touching separate files or areas. Starting the " +
|
|
4
|
+
"parts, waiting without polling, permission requests and questions from below, verifying and merging.";
|
|
11
5
|
export const SKILL_CONTENT = [
|
|
12
6
|
"# How opencode-courier's session tools work",
|
|
13
7
|
"",
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"brief says if a session started you.",
|
|
8
|
+
"If courier_spawn is not among your tools, you cannot split: do the task yourself. Planning the parts, staging a " +
|
|
9
|
+
"tightly coupled core and reviewing the merge are not in this skill: if a skill for them is listed to you, load it too.",
|
|
17
10
|
"",
|
|
18
|
-
"
|
|
19
|
-
"is a playbook's: if a skill for that is listed to you, load it too.",
|
|
11
|
+
"## 1. Split or do",
|
|
20
12
|
"",
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"the root
|
|
26
|
-
|
|
27
|
-
"when it is small, sequential or tightly coupled. Several whole jobs in one prompt, such as 4 tickets, are that many " +
|
|
28
|
-
"parts: each session runs this skill for its job, so the tree below you repeats its shape once per job.",
|
|
29
|
-
"- Only the root, the session nobody started, may start exactly one session for the whole task, and only when the " +
|
|
30
|
-
"task is heavy (long reading, many edits, long runs) and its own context is worth keeping; it still verifies and " +
|
|
31
|
-
"integrates that session's report itself. Every other session never starts exactly one session for the whole task: " +
|
|
32
|
-
"it only adds a hop, and a chain of single hops uses up the tree's depth. One session for one stage is a part, not " +
|
|
33
|
-
"the whole, as your brief says: a core you stage before the rest, when 2 or more parts follow it, or a review of " +
|
|
34
|
-
"the parts once you have merged them.",
|
|
35
|
-
`- Say which in one line of your reply, before you start: ${SPLIT_LINE}; the root has a third form, ${ONE_PART_LINE}.`,
|
|
13
|
+
"Split or do by the rule in your brief, the text above your task. At the root, the top session of the tree, which has no brief: split a task with 2 " +
|
|
14
|
+
"or more independent, substantial parts touching separate files or areas, do it yourself when it is small, " +
|
|
15
|
+
"sequential or tightly coupled, or hand a heavy one (long reading, many edits, long runs) whole to one session to " +
|
|
16
|
+
`keep your context. Say which in one line of your reply: ${SPLIT_LINE}, or, at the root only, ${ONE_PART_LINE}. Several whole ` +
|
|
17
|
+
"jobs in one prompt, such as 4 tickets, are that many parts. Below the root, never start one session for the whole " +
|
|
18
|
+
"task: it only adds a hop, and a chain of single hops uses up the tree's depth.",
|
|
36
19
|
"",
|
|
37
20
|
"## 2. Start the parts",
|
|
38
21
|
"",
|
|
39
|
-
"- One courier_spawn per part that
|
|
40
|
-
"
|
|
41
|
-
|
|
42
|
-
"group of their own once the first group has reported.",
|
|
22
|
+
"- One courier_spawn per part, the parts that run at once in one group. When they do not all fit the tree's limits " +
|
|
23
|
+
"at once, start waves, each its own group, or leave them ungrouped and start the next as each report frees a slot.",
|
|
24
|
+
"- No two parts edit the same files; isolate: true for each part that edits files in parallel with another.",
|
|
43
25
|
"- Write each task so the session can do it without you, since it sees none of your conversation: what to produce " +
|
|
44
|
-
"and where, the context it needs, how to check it
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
"
|
|
48
|
-
"its branch is for you to merge.",
|
|
49
|
-
"- Leave model and variant unset, so a part runs on your own model, unless the part plainly calls for another: a " +
|
|
50
|
-
"cheaper one or a lower variant for a small, well-specified part, a stronger one or a higher variant for a design " +
|
|
51
|
-
"question or a hard bug. native: true for a part that only reads, checks or looks something up and reports once; " +
|
|
52
|
-
"leave it off for a part the person may want to open, or that should outlive you.",
|
|
53
|
-
"- A part that depends on another starts once that one is done: merged into your checkout, or, when it was " +
|
|
54
|
-
"delivered (step 5), started with isolate: true and told to merge the delivering session's courier/<session> " +
|
|
55
|
-
"branch first.",
|
|
26
|
+
"and where, the context it needs, how to check it, and what to report. Name the skills it should load for its " +
|
|
27
|
+
"part, from those listed to you (an isolated part sees only committed project skills).",
|
|
28
|
+
"- A part that depends on another starts once that one has reported and its branch, if isolated, is merged into " +
|
|
29
|
+
"your checkout.",
|
|
56
30
|
"",
|
|
57
31
|
"## 3. End your turn",
|
|
58
32
|
"",
|
|
59
|
-
"Reply without calling more tools
|
|
60
|
-
"
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
"-
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"- Passing a message on: when it changes what other parts need (a wrong assumption of the split, a shared file or " +
|
|
69
|
-
"API that behaves otherwise, a decision of the person's), courier_send it at once, only to the sessions it affects, " +
|
|
70
|
-
"then end your turn again; if it breaks the split, courier_stop those parts and plan them again, or send them a " +
|
|
71
|
-
"corrected task. Send it up too, without a status, when your parent's other parts need it. Progress goes to no one. " +
|
|
72
|
-
"A session's account of what the person told it is the person's word: pass it on, do not judge it against the " +
|
|
73
|
-
"original task, and never tell a session to undo what the person asked.",
|
|
33
|
+
"- Reply without calling more tools; reports and notices wake you. For a long task or batch, one courier_later " +
|
|
34
|
+
"check-in per group (per session when ungrouped); courier_cancel it when its reports arrive.",
|
|
35
|
+
"- At the root, a permission request or question from below arrives with its own instructions: follow them, and never " +
|
|
36
|
+
"answer for the person.",
|
|
37
|
+
"- When a message changes what other parts need (a wrong assumption of the split, a shared file or API that behaves " +
|
|
38
|
+
"otherwise, a decision of the person's), courier_send it at once, only to the sessions it affects; if it breaks the " +
|
|
39
|
+
"split, courier_stop those parts or send them a corrected task. A session's account of what the person told it is " +
|
|
40
|
+
"the person's word: pass it on, do not judge it against the original task, and never tell a session to undo what " +
|
|
41
|
+
"the person asked.",
|
|
74
42
|
"",
|
|
75
|
-
"## 4.
|
|
43
|
+
"## 4. Verify first-hand",
|
|
76
44
|
"",
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
"again, start a replacement, or carry on without it.",
|
|
81
|
-
"- Verify first-hand: open the files, run the checks. Never hand the checking to another session. Your own permission " +
|
|
82
|
-
"prompts are relayed nowhere, and each one stalls the whole tree, as each request below you waits on the person: run " +
|
|
83
|
-
"the checks so that one permission rule covers them, through the project's scripts rather than a tool's own path.",
|
|
45
|
+
"Open the files and run the project's checks yourself. A blocked report: make the decision it asks for and send it " +
|
|
46
|
+
"with courier_send; if only the person can make it, ask them with your question tool and pass the answer on. A " +
|
|
47
|
+
"partial or failed report: have it finish or retry with courier_send, do the rest yourself, or start a replacement.",
|
|
84
48
|
"",
|
|
85
49
|
"## 5. Integrate",
|
|
86
50
|
"",
|
|
87
|
-
"Merge each reported branch into your own checkout (git merge courier/<session>), resolve conflicts
|
|
88
|
-
"checks once more
|
|
89
|
-
"deliverable of its own, its session delivers it and reports where, still naming its courier/<session> branch when " +
|
|
90
|
-
"it is isolated; you verify each delivery and merge nothing of it into your checkout.",
|
|
91
|
-
"",
|
|
92
|
-
"## 6. Report upward",
|
|
93
|
-
"",
|
|
94
|
-
"- If a session started you: one courier_send to it, with one status for the whole (done only when every part is " +
|
|
95
|
-
"verified and integrated, or verified and delivered; partial, blocked or failed otherwise) and the artifacts.",
|
|
96
|
-
"- At the top, before you reply to the person: if the next tree in this project should know something about running " +
|
|
97
|
-
"one like this (how it was split, what a part got wrong and what caught it, what a brief should have said, what the " +
|
|
98
|
-
"person decided), one courier_note with that one line; none when there is nothing. Name the job it comes from and " +
|
|
99
|
-
"the reason, so a lesson for one kind of job is not taken for every job; the tool refuses a line over its limit " +
|
|
100
|
-
"rather than clipping it, so write it shorter. A rule that holds every time goes to AGENTS.md or a skill, through " +
|
|
101
|
-
"the person, not into a note.",
|
|
51
|
+
"Merge each isolated part's reported branch into your own checkout (git merge courier/<session>), resolve conflicts " +
|
|
52
|
+
"yourself and run the checks once more. Then courier_cleanup each session you are done with, isolated or not.",
|
|
102
53
|
"",
|
|
103
|
-
"##
|
|
54
|
+
"## 6. Done",
|
|
104
55
|
"",
|
|
105
|
-
|
|
106
|
-
"
|
|
107
|
-
"
|
|
108
|
-
"- A refusal from courier_spawn naming maxDepth, maxChildren or maxTotal: do that part yourself, or end your turn and " +
|
|
109
|
-
"start it once a report has arrived. Do not retry it in a loop.",
|
|
110
|
-
"- Lost track, after a compaction or a restart: one courier_tree shows every session under you with its depth, its " +
|
|
111
|
-
"parent and whether it still owes a report; one look, not a loop.",
|
|
112
|
-
"- A subtree that went wrong or is no longer needed: courier_stop its top session; everything under it is stopped " +
|
|
113
|
-
"too, and nothing in it reports back. cleanup: true removes their worktrees.",
|
|
56
|
+
"The task is done only when every part is verified and integrated. At the root, before you reply to the person: one " +
|
|
57
|
+
"courier_note if the next tree in this project should know something about running one like this; none when there " +
|
|
58
|
+
"is nothing.",
|
|
114
59
|
].join("\n");
|
|
115
|
-
|
|
116
|
-
const SKILL_PATH =
|
|
117
|
-
/** The courier-orchestrate skill as OpenCode lists it: its id, name and description, and the text the model loads. */
|
|
60
|
+
// A skill:// path: the read tool serves it from the registry (api-notes)
|
|
61
|
+
const SKILL_PATH = `skill://${SKILL_ID}/SKILL.md`;
|
|
118
62
|
export const orchestrateSkill = () => ({ id: SKILL_ID, name: SKILL_ID, description: SKILL_DESCRIPTION, path: SKILL_PATH, content: SKILL_CONTENT });
|
|
119
|
-
/** Adds the courier-orchestrate skill; every instance adds the same one, under the same id. */
|
|
120
63
|
export function addSkill(skills) {
|
|
121
64
|
skills.add(orchestrateSkill());
|
|
122
65
|
}
|
package/dist/tree/cleanup.d.ts
CHANGED
|
@@ -3,31 +3,11 @@ export interface CleanupInput {
|
|
|
3
3
|
readonly sessionID: string;
|
|
4
4
|
readonly force?: boolean;
|
|
5
5
|
}
|
|
6
|
-
/** At most this many changes and commits are returned; the reason still counts them all. */
|
|
7
6
|
export declare const MAX_LISTED = 50;
|
|
8
|
-
/**
|
|
9
|
-
* Forgets a child the parent started, removing its worktree if it is isolated. A worktree with uncommitted changes
|
|
10
|
-
* or commits found nowhere else is kept unless `force` is set; the result says so.
|
|
11
|
-
*/
|
|
12
7
|
export declare function cleanup(ports: CleanupPorts, parentID: string, input: CleanupInput): Promise<CleanupResult>;
|
|
13
|
-
/** Names the git to run, by absolute path, overriding the places `GIT_LOCATIONS` lists. */
|
|
14
8
|
export declare const GIT_ENV = "OPENCODE_COURIER_GIT";
|
|
15
|
-
/**
|
|
16
|
-
* Where git is looked for, in order: its usual install locations, never `PATH`, so a writable
|
|
17
|
-
* directory early in `PATH` cannot put another program in its place.
|
|
18
|
-
*/
|
|
19
9
|
export declare const GIT_LOCATIONS: Readonly<Record<"posix" | "win32", readonly string[]>>;
|
|
20
|
-
/**
|
|
21
|
-
* The git to run: `OPENCODE_COURIER_GIT` when it is an absolute path, otherwise the first of
|
|
22
|
-
* `GIT_LOCATIONS` that exists. Throws when there is none.
|
|
23
|
-
*/
|
|
24
10
|
export declare function findGit(env?: Readonly<Record<string, string | undefined>>, platform?: NodeJS.Platform, exists?: (path: string) => boolean): string;
|
|
25
|
-
/** The commit a worktree is on, or undefined when git cannot tell. */
|
|
26
11
|
export declare function headOf(directory: string): Promise<string | undefined>;
|
|
27
|
-
/** Whether a worktree has uncommitted changes, untracked files included; undefined when git cannot tell. */
|
|
28
12
|
export declare function dirtyOf(directory: string): Promise<boolean | undefined>;
|
|
29
|
-
/**
|
|
30
|
-
* Reads a worktree's state with git; undefined when the directory is gone. Commits reachable from
|
|
31
|
-
* `base`, the commit the worktree was made from, are not its own work and are not listed.
|
|
32
|
-
*/
|
|
33
13
|
export declare function inspectWorktree(directory: string, base?: string): Promise<WorktreeState | undefined>;
|
package/dist/tree/cleanup.js
CHANGED
|
@@ -5,18 +5,12 @@ import { forgetDropped } from "./forget.js";
|
|
|
5
5
|
import { syncStanding } from "./standing.js";
|
|
6
6
|
import { childStore } from "./store.js";
|
|
7
7
|
import { keepReason } from "./text.js";
|
|
8
|
-
/** At most this many changes and commits are returned; the reason still counts them all. */
|
|
9
8
|
export const MAX_LISTED = 50;
|
|
10
|
-
/**
|
|
11
|
-
* Forgets a child the parent started, removing its worktree if it is isolated. A worktree with uncommitted changes
|
|
12
|
-
* or commits found nowhere else is kept unless `force` is set; the result says so.
|
|
13
|
-
*/
|
|
14
9
|
export async function cleanup(ports, parentID, input) {
|
|
15
10
|
const entry = await childStore(ports.storage).entry(parentID, input.sessionID);
|
|
16
11
|
if (!entry)
|
|
17
12
|
throw new Error(`${input.sessionID} is not on the courier_children list of ${parentID}.`);
|
|
18
13
|
const { directory } = entry;
|
|
19
|
-
// A member still out is dropped from its group, so the group is released without it; the result names that group.
|
|
20
14
|
const forget = async () => {
|
|
21
15
|
const group = await forgetDropped(ports, entry);
|
|
22
16
|
await syncStanding(ports, parentID);
|
|
@@ -24,8 +18,6 @@ export async function cleanup(ports, parentID, input) {
|
|
|
24
18
|
};
|
|
25
19
|
if (!entry.isolated)
|
|
26
20
|
return { sessionID: input.sessionID, directory, outcome: "forgotten", ...(await forget()) };
|
|
27
|
-
// With force the state only decides whether there is anything left to remove, so a worktree git
|
|
28
|
-
// can no longer read is still removed.
|
|
29
21
|
const state = await ports
|
|
30
22
|
.inspect(directory, entry.base)
|
|
31
23
|
.catch((error) => (input.force ? { changes: [], commits: [] } : Promise.reject(error)));
|
|
@@ -48,26 +40,16 @@ export async function cleanup(ports, parentID, input) {
|
|
|
48
40
|
});
|
|
49
41
|
return { sessionID: input.sessionID, directory, outcome: "removed", ...(await forget()) };
|
|
50
42
|
}
|
|
51
|
-
/** Names the git to run, by absolute path, overriding the places `GIT_LOCATIONS` lists. */
|
|
52
43
|
export const GIT_ENV = "OPENCODE_COURIER_GIT";
|
|
53
|
-
/**
|
|
54
|
-
* Where git is looked for, in order: its usual install locations, never `PATH`, so a writable
|
|
55
|
-
* directory early in `PATH` cannot put another program in its place.
|
|
56
|
-
*/
|
|
57
44
|
export const GIT_LOCATIONS = {
|
|
58
45
|
posix: [
|
|
59
46
|
"/usr/bin/git",
|
|
60
47
|
"/usr/local/bin/git",
|
|
61
48
|
"/opt/homebrew/bin/git",
|
|
62
|
-
// NixOS, and nix-darwin.
|
|
63
49
|
"/run/current-system/sw/bin/git",
|
|
64
50
|
],
|
|
65
51
|
win32: [String.raw `C:\Program Files\Git\cmd\git.exe`, String.raw `C:\Program Files (x86)\Git\cmd\git.exe`],
|
|
66
52
|
};
|
|
67
|
-
/**
|
|
68
|
-
* The git to run: `OPENCODE_COURIER_GIT` when it is an absolute path, otherwise the first of
|
|
69
|
-
* `GIT_LOCATIONS` that exists. Throws when there is none.
|
|
70
|
-
*/
|
|
71
53
|
export function findGit(env = process.env, platform = process.platform, exists = existsSync) {
|
|
72
54
|
const named = env[GIT_ENV]?.trim();
|
|
73
55
|
if (named) {
|
|
@@ -84,29 +66,21 @@ export function findGit(env = process.env, platform = process.platform, exists =
|
|
|
84
66
|
function git(directory, args) {
|
|
85
67
|
return new Promise((resolve, reject) => execFile(findGit(), ["-C", directory, ...args], { maxBuffer: 16 * 1024 * 1024 }, (error, stdout, stderr) => error ? reject(new Error(`git ${args[0]} in ${directory}: ${stderr.trim() || error.message}`)) : resolve(stdout)));
|
|
86
68
|
}
|
|
87
|
-
/** The commit a worktree is on, or undefined when git cannot tell. */
|
|
88
69
|
export function headOf(directory) {
|
|
89
70
|
return git(directory, ["rev-parse", "HEAD"]).then((out) => out.trim() || undefined, () => undefined);
|
|
90
71
|
}
|
|
91
|
-
/** Whether a worktree has uncommitted changes, untracked files included; undefined when git cannot tell. */
|
|
92
72
|
export function dirtyOf(directory) {
|
|
93
73
|
return git(directory, ["status", "--porcelain"]).then((out) => out.trim().length > 0, () => undefined);
|
|
94
74
|
}
|
|
95
|
-
/**
|
|
96
|
-
* Reads a worktree's state with git; undefined when the directory is gone. Commits reachable from
|
|
97
|
-
* `base`, the commit the worktree was made from, are not its own work and are not listed.
|
|
98
|
-
*/
|
|
99
75
|
export async function inspectWorktree(directory, base) {
|
|
100
76
|
if (!existsSync(directory))
|
|
101
77
|
return undefined;
|
|
102
|
-
// An untracked directory is one entry, not every file in it.
|
|
103
78
|
const status = await git(directory, ["status", "--porcelain=v1", "-z", "--untracked-files=normal"]);
|
|
104
79
|
const changes = [];
|
|
105
80
|
const records = status.split("\0").filter(Boolean);
|
|
106
81
|
for (let i = 0; i < records.length; i++) {
|
|
107
82
|
const record = records[i];
|
|
108
83
|
changes.push(record.slice(3));
|
|
109
|
-
// A rename or copy is followed by its source path in a record of its own.
|
|
110
84
|
if ("RC".includes(record[0]) || "RC".includes(record[1]))
|
|
111
85
|
i++;
|
|
112
86
|
}
|
package/dist/tree/context.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { ContextPorts } from "./ports.js";
|
|
2
|
-
/** The part of a session context this hook changes. */
|
|
3
2
|
export interface Shaped {
|
|
4
3
|
readonly sessionID: string;
|
|
5
4
|
tools: Record<string, unknown>;
|
|
@@ -8,8 +7,4 @@ export interface Shaped {
|
|
|
8
7
|
text: string;
|
|
9
8
|
}>;
|
|
10
9
|
}
|
|
11
|
-
/**
|
|
12
|
-
* The `context` hook: hides courier_spawn and the tools named `hiddenFromLeaf` from a session at `maxDepth`, and names a
|
|
13
|
-
* session's role in a system part unless its `role` entry does; a session nobody spawned that has started none is pointed to the skill.
|
|
14
|
-
*/
|
|
15
10
|
export declare function shapeContext(ports: ContextPorts, event: Shaped): Promise<void>;
|
package/dist/tree/context.js
CHANGED
|
@@ -1,22 +1,17 @@
|
|
|
1
1
|
import { setBounded } from "../platform/bounded.js";
|
|
2
2
|
import { childStore } from "./store.js";
|
|
3
3
|
import { ROLE_KEY, ROLE_PREFIX, rolePart } from "./text.js";
|
|
4
|
-
/** How many sessions' roles an instance remembers. */
|
|
5
4
|
const ROLES_MAX = 1000;
|
|
6
|
-
/** A role part, or the role entry's block, already in the system prompt, wherever its part begins. */
|
|
7
5
|
const ROLE_LINE = new RegExp(`^${ROLE_PREFIX}`, "m");
|
|
8
|
-
/**
|
|
9
|
-
* The `context` hook: hides courier_spawn and the tools named `hiddenFromLeaf` from a session at `maxDepth`, and names a
|
|
10
|
-
* session's role in a system part unless its `role` entry does; a session nobody spawned that has started none is pointed to the skill.
|
|
11
|
-
*/
|
|
12
6
|
export async function shapeContext(ports, event) {
|
|
13
7
|
try {
|
|
14
|
-
const { depth, entry } = await roleOf(ports, event.sessionID);
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
if (depth !== null && depth >= ports.limits.maxDepth)
|
|
8
|
+
const { depth, entry, subagent } = await roleOf(ports, event.sessionID);
|
|
9
|
+
const leaf = subagent || (depth !== null && depth >= ports.limits.maxDepth);
|
|
10
|
+
if (leaf)
|
|
18
11
|
for (const tool of ["courier_spawn", ...(ports.hiddenFromLeaf ?? [])])
|
|
19
12
|
delete event.tools[tool];
|
|
13
|
+
if (subagent || (depth === null && !event.tools.courier_spawn))
|
|
14
|
+
return;
|
|
20
15
|
if (entry || event.system.some((part) => ROLE_LINE.test(part.text)))
|
|
21
16
|
return;
|
|
22
17
|
event.system.push({ type: "text", text: rolePart(depth, ports.limits) });
|
|
@@ -25,19 +20,28 @@ export async function shapeContext(ports, event) {
|
|
|
25
20
|
ports.log(`courier: cannot tell the role of session ${event.sessionID}: ${String(error)}`);
|
|
26
21
|
}
|
|
27
22
|
}
|
|
28
|
-
async function roleOf(ports, sessionID) {
|
|
23
|
+
async function roleOf(ports, sessionID, below = new Set()) {
|
|
29
24
|
const known = ports.roles.get(sessionID);
|
|
30
25
|
if (known !== undefined)
|
|
31
26
|
return known;
|
|
32
|
-
// The reverse index alone: a scan here would run for every session's first request, courier or not.
|
|
33
27
|
const children = childStore(ports.storage);
|
|
34
28
|
const chain = await children.indexedLineage(sessionID);
|
|
35
29
|
const depth = chain.length || ((await children.children(sessionID)).length ? 0 : null);
|
|
30
|
+
if (depth === null) {
|
|
31
|
+
const { parentID } = await ports.session.get({ sessionID });
|
|
32
|
+
if (parentID && !below.has(parentID)) {
|
|
33
|
+
const above = await roleOf(ports, parentID, new Set([...below, sessionID]));
|
|
34
|
+
if (above.depth === null && !above.subagent)
|
|
35
|
+
return { depth: null, entry: false };
|
|
36
|
+
const role = { depth: null, entry: false, subagent: true };
|
|
37
|
+
setBounded(ports.roles, sessionID, role, ROLES_MAX);
|
|
38
|
+
return role;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
36
41
|
const role = { depth, entry: depth !== null && (await hasEntry(ports, sessionID)) };
|
|
37
42
|
setBounded(ports.roles, sessionID, role, ROLES_MAX);
|
|
38
43
|
return role;
|
|
39
44
|
}
|
|
40
|
-
/** Whether the session carries the role entry, listed when its role is looked up; one that cannot be read gets the part. */
|
|
41
45
|
async function hasEntry(ports, sessionID) {
|
|
42
46
|
if (!ports.instructions)
|
|
43
47
|
return false;
|
package/dist/tree/domain.d.ts
CHANGED
|
@@ -1,37 +1,23 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The tree's pure rules, on plain data: the limits, a session's lineage, whether it runs, and a group
|
|
3
|
-
* member's standing. Imports nothing of the plugin's but `kernel/` and `platform/json.ts`.
|
|
4
|
-
*/
|
|
5
1
|
import type { Plugin } from "@opencode/plugin";
|
|
6
2
|
import type { Status } from "../kernel/protocol.js";
|
|
7
3
|
type Context = Plugin.Context;
|
|
8
|
-
/** The limits on the tree of sessions courier_spawn builds, from the plugin's options. */
|
|
9
4
|
export interface Limits {
|
|
10
5
|
readonly maxDepth: number;
|
|
11
6
|
readonly maxChildren: number;
|
|
12
7
|
readonly maxTotal: number;
|
|
13
8
|
}
|
|
14
9
|
export declare const DEFAULT_LIMITS: Limits;
|
|
15
|
-
/** The limits in the plugin's options; one that is not a positive integer keeps its default and is named in `problems`. */
|
|
16
10
|
export declare function readLimits(options: unknown): {
|
|
17
11
|
limits: Limits;
|
|
18
12
|
problems: string[];
|
|
19
13
|
};
|
|
20
|
-
/** The `nativeChildren` option: whether a child started without isolate or `native` gets OpenCode's own `parentID`; off unless `true`. */
|
|
21
14
|
export declare function readNativeChildren(options: unknown): {
|
|
22
15
|
nativeChildren: boolean;
|
|
23
16
|
problems: string[];
|
|
24
17
|
};
|
|
25
|
-
/** A session's times, as `session.get` gives them. */
|
|
26
18
|
type Times = Awaited<ReturnType<Context["session"]["get"]>>["time"];
|
|
27
|
-
/** A session's times as `busy` reads them; `busy`, when its latest execution started, is stamped since OpenCode 2.2.2. */
|
|
28
19
|
export type BusyTimes = Pick<Times, "updated" | "idle" | "busy">;
|
|
29
|
-
/**
|
|
30
|
-
* Whether a session may run by its times: it has never finished a turn, or its latest execution started after the last one ended
|
|
31
|
-
* (`busy`; without it `updated`, which a rename, an archive or a waiting inbox item also stamp); only its transcript then tells.
|
|
32
|
-
*/
|
|
33
20
|
export declare const busy: (time: BusyTimes) => boolean;
|
|
34
|
-
/** A stored message as `turnEnd` reads it: an assistant message's `error` says how its step ended. */
|
|
35
21
|
type Stored = {
|
|
36
22
|
readonly type: string;
|
|
37
23
|
readonly error?: {
|
|
@@ -42,50 +28,26 @@ type Stored = {
|
|
|
42
28
|
readonly completed?: unknown;
|
|
43
29
|
};
|
|
44
30
|
};
|
|
45
|
-
/**
|
|
46
|
-
* The message a transcript's last turn ended on, unless one runs, past asides made since: an `idle` message, or a step
|
|
47
|
-
* interrupted with none after it, as a declined permission or dismissed question ends a turn, recording no `idle` (#326).
|
|
48
|
-
*/
|
|
49
31
|
export declare function turnEnd<M extends Stored>(messages: ReadonlyArray<M>): M | undefined;
|
|
50
32
|
export declare const endsIdle: (messages: ReadonlyArray<Stored>) => boolean;
|
|
51
|
-
/** When the turn `end` ended: the session's idle time for an `idle` message, else when its interrupted step ended. */
|
|
52
33
|
export declare function endedAt(end: Stored, time: BusyTimes): number | undefined;
|
|
53
|
-
/**
|
|
54
|
-
* Whether a session of a tree read is live, for a cut listing's order: it runs (its turn not failed or interrupted since its last
|
|
55
|
-
* prompt), waits on a request or form, or owes a report, but not, unlike for the limits, for a message waiting in its inbox.
|
|
56
|
-
*/
|
|
57
34
|
export declare const isLive: (running: boolean | undefined, waits: number, report: {
|
|
58
35
|
readonly owes: boolean;
|
|
59
36
|
readonly ended: boolean;
|
|
60
37
|
} | undefined) => boolean;
|
|
61
|
-
/** A session of a tree read, as `cut` takes it: where it is in the tree, when it was started, and whether it is live. */
|
|
62
38
|
export interface Cuttable {
|
|
63
39
|
readonly sessionID: string;
|
|
64
40
|
readonly parentID: string;
|
|
65
41
|
readonly createdAt: number;
|
|
66
42
|
readonly live: boolean;
|
|
67
43
|
}
|
|
68
|
-
/**
|
|
69
|
-
* Which sessions a listing of at most `max` keeps, of a tree read in the order it is drawn: the live ones first, then
|
|
70
|
-
* the newest, each with the sessions above it so the tree still draws; `leftOut` counts the rest.
|
|
71
|
-
*/
|
|
72
44
|
export declare function cut<T extends Cuttable>(nodes: ReadonlyArray<T>, max: number): {
|
|
73
45
|
kept: T[];
|
|
74
46
|
leftOut: number;
|
|
75
47
|
};
|
|
76
|
-
/**
|
|
77
|
-
* Entries older than this are dropped when their parent's roster is read, and on plugin setup,
|
|
78
|
-
* except isolated children whose worktree is still there: those stay until courier_cleanup.
|
|
79
|
-
*/
|
|
80
48
|
export declare const RETENTION_MS: number;
|
|
81
|
-
/** Whether a directory still exists; tests pass a fake. */
|
|
82
49
|
export type Exists = (directory: string) => boolean;
|
|
83
|
-
/**
|
|
84
|
-
* Whether an entry is past retention at `now`: recorded more than `RETENTION_MS` before, unless it is an isolated
|
|
85
|
-
* child whose worktree exists, which is kept so it can still be cleaned up.
|
|
86
|
-
*/
|
|
87
50
|
export declare const isExpired: (entry: RosterEntry, now: number, exists: Exists) => boolean;
|
|
88
|
-
/** A session started with courier_spawn, recorded under the session that started it. */
|
|
89
51
|
export interface RosterEntry {
|
|
90
52
|
readonly sessionID: string;
|
|
91
53
|
readonly parentID: string;
|
|
@@ -93,49 +55,30 @@ export interface RosterEntry {
|
|
|
93
55
|
readonly directory: string;
|
|
94
56
|
readonly isolated: boolean;
|
|
95
57
|
readonly createdAt: number;
|
|
96
|
-
/** For an isolated child, the directory its worktree was made from; for the record, nothing acts on it. */
|
|
97
58
|
readonly source?: string;
|
|
98
|
-
/** For an isolated child, the project its worktree belongs to; courier_cleanup removes it from there. */
|
|
99
59
|
readonly project?: string;
|
|
100
|
-
/** For an isolated child, the commit its worktree was made from; its own work is what came after. */
|
|
101
60
|
readonly base?: string;
|
|
102
|
-
/** `"status"` when its brief asked for a report with a status; absent for a child an earlier release briefed. */
|
|
103
61
|
readonly reports?: "status";
|
|
104
|
-
/** The join group courier_spawn put it in, whose release holds its report; absent for a child in none. */
|
|
105
62
|
readonly group?: string;
|
|
106
|
-
/** Created with OpenCode's own `parentID` (courier_spawn's `native`, else the `nativeChildren` option), so OpenCode shows its asks in its parent's view. */
|
|
107
63
|
readonly native?: true;
|
|
108
|
-
/** The model it was started on, as `modelName` writes it, when that is not the model its parent was on. */
|
|
109
64
|
readonly model?: string;
|
|
110
65
|
}
|
|
111
|
-
/** How a child was started, as courier_children and the panel show it: isolated, native when it is, and its model when not its parent's. */
|
|
112
66
|
export declare function startedAs(entry: RosterEntry): {
|
|
113
67
|
model?: string | undefined;
|
|
114
68
|
native?: true | undefined;
|
|
115
69
|
isolated: boolean;
|
|
116
70
|
};
|
|
117
|
-
/** A model as a session names it: its provider, its id, and its variant, which `"default"` and none both leave to the provider. */
|
|
118
71
|
export interface ModelRef {
|
|
119
72
|
readonly providerID: string;
|
|
120
73
|
readonly id: string;
|
|
121
74
|
readonly variant?: string;
|
|
122
75
|
}
|
|
123
|
-
/** `provider/model` as `courier_spawn` takes it: one slash, nothing empty on either side. */
|
|
124
76
|
export declare function parseModelName(text: string): Pick<ModelRef, "providerID" | "id"> | undefined;
|
|
125
|
-
/** A model as the roster, the panel and the results name it: `provider/model`, with `#variant` for a variant that is not the default. */
|
|
126
77
|
export declare function modelName(model: ModelRef): string;
|
|
127
|
-
/** `lineage` over roster entries already read. */
|
|
128
78
|
export declare function lineageIn(entries: ReadonlyArray<RosterEntry>, sessionID: string): RosterEntry[];
|
|
129
|
-
/** Roster entries by their session, for `lineageBy`. */
|
|
130
79
|
export declare function bySession(entries: ReadonlyArray<RosterEntry>): Map<string, RosterEntry>;
|
|
131
|
-
/** `lineage` over roster entries already read and mapped by `bySession`. */
|
|
132
80
|
export declare function lineageBy(bySession: ReadonlyMap<string, RosterEntry>, sessionID: string): RosterEntry[];
|
|
133
|
-
/**
|
|
134
|
-
* The session whose view OpenCode shows the asks of a lineage's first session in: up its native parents, to the
|
|
135
|
-
* first session that is not a native child (an isolated one, or the top). The session itself when it is not native.
|
|
136
|
-
*/
|
|
137
81
|
export declare function nativeRootOf(chain: ReadonlyArray<RosterEntry>): string;
|
|
138
|
-
/** What a report lists besides its text; `checks` are the commands the session ran and what each gave. */
|
|
139
82
|
export interface Artifacts {
|
|
140
83
|
readonly branch?: string;
|
|
141
84
|
readonly commits?: ReadonlyArray<string>;
|
|
@@ -145,21 +88,18 @@ export interface Artifacts {
|
|
|
145
88
|
readonly result: string;
|
|
146
89
|
}>;
|
|
147
90
|
}
|
|
148
|
-
/** A report held with its group: its status, text and artifacts, as courier_send was given them, and when. */
|
|
149
91
|
export interface HeldReport {
|
|
150
92
|
readonly at: number;
|
|
151
93
|
readonly status: Status;
|
|
152
94
|
readonly message: string;
|
|
153
95
|
readonly artifacts?: Artifacts;
|
|
154
96
|
}
|
|
155
|
-
/** A member of a group that left it without a report, and why: its turn failed or was interrupted, it was deleted, or courier_cleanup dropped it. */
|
|
156
97
|
export interface LeftMember {
|
|
157
98
|
readonly sessionID: string;
|
|
158
99
|
readonly title: string;
|
|
159
100
|
readonly by: "failed" | "interrupted" | "deleted" | "dropped";
|
|
160
101
|
}
|
|
161
102
|
export declare const isGroupName: (value: unknown) => value is string;
|
|
162
|
-
/** A member of a group, as stored: its title for the release notice, and its report or how it left. */
|
|
163
103
|
export interface Member {
|
|
164
104
|
readonly title: string;
|
|
165
105
|
readonly joinedAt: number;
|
|
@@ -169,13 +109,9 @@ export interface Member {
|
|
|
169
109
|
readonly by: LeftMember["by"];
|
|
170
110
|
};
|
|
171
111
|
}
|
|
172
|
-
/** What is known of a member's standing: its report is held, still out, or it left, or it is no longer in an open group. */
|
|
173
112
|
export type Standing = "held" | "out" | LeftMember["by"] | "released";
|
|
174
|
-
/** A member's standing from its stored value, or `released` when there is none. */
|
|
175
113
|
export declare function standingOf(member: Member | undefined): Standing;
|
|
176
|
-
/** Whether every member of a group has a held report or has left: released once the parent's turn has ended. */
|
|
177
114
|
export declare const complete: (members: ReadonlyArray<Member>) => boolean;
|
|
178
|
-
/** How long the provider asked to wait after a failed turn, if it did: a positive number of milliseconds, at most `RETRY_MAX_MS`. */
|
|
179
115
|
export declare function retryDelay(error: {
|
|
180
116
|
readonly retryAfterMs?: unknown;
|
|
181
117
|
}): number | undefined;
|