@sjawhar/opencode-legion-envoy 1.11.0 → 1.13.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/dist/src/server.js
CHANGED
|
@@ -13590,6 +13590,7 @@ var ArtifactReviewEventPayloadSchema = object({
|
|
|
13590
13590
|
var askEventPayloadFields = {
|
|
13591
13591
|
id: string2().optional(),
|
|
13592
13592
|
opened_event_id: number2().int().positive(),
|
|
13593
|
+
kind: _enum2(["question", "approval", "action"]).optional(),
|
|
13593
13594
|
question: string2().optional(),
|
|
13594
13595
|
options: array(object({ label: string2().optional() })).nullish(),
|
|
13595
13596
|
answer: object({ selected: array(string2()).nullish(), text: string2().nullish() }).nullish(),
|
|
@@ -13757,13 +13758,14 @@ var dispatchToolSpecs = [
|
|
|
13757
13758
|
},
|
|
13758
13759
|
{
|
|
13759
13760
|
name: "dispatch_ask",
|
|
13760
|
-
description: "Open a durable, answerable decision on an issue or project document. Do not use it for a status update or discussion; " + "use dispatch_message instead. Anchor a document question, thread reply_to/reply_to_ask, or cite a dispatch:// " + `reference \u2014 it must be answerable from its own text and anchor alone, never "see above". Question is at most 800 ` + `characters and has at most 8 options. ${OWNER_REFERENCE}`,
|
|
13761
|
+
description: "Open a durable, answerable decision or human to-do on an issue or project document. Do not use it for a status update or discussion; " + "use dispatch_message instead. Use kind: action for a to-do a human must complete; it has fixed Done / Can't answers. " + "Anchor a document question, thread reply_to/reply_to_ask, or cite a dispatch:// " + `reference \u2014 it must be answerable from its own text and anchor alone, never "see above". Question is at most 800 ` + `characters and has at most 8 options. ${OWNER_REFERENCE}`,
|
|
13761
13762
|
arguments: (z) => ({
|
|
13762
13763
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
13763
13764
|
project: z.string().describe("Project key owning the document.").optional(),
|
|
13764
13765
|
artifact: z.string().describe("Project document artifact id, slug, or filename.").optional(),
|
|
13765
13766
|
ref: z.string().describe("Optional dispatch:// issue or document reference.").optional(),
|
|
13766
13767
|
question: z.string({ max: 800 }).describe("Decision question, at most 800 characters."),
|
|
13768
|
+
kind: z.enum(["action"]).describe("Optional human to-do ask kind.").optional(),
|
|
13767
13769
|
options: z.array(z.object({
|
|
13768
13770
|
label: z.string().describe("Selectable option label."),
|
|
13769
13771
|
description: z.string().describe("Optional option context.").optional()
|
|
@@ -14100,12 +14102,24 @@ var controllerIssue = strictObject({
|
|
|
14100
14102
|
issue: nonEmptyString
|
|
14101
14103
|
});
|
|
14102
14104
|
var TREE_STATUSES = ["queued", "active", "lingering", "dead", "launch-failed", "closed"];
|
|
14103
|
-
var
|
|
14105
|
+
var stateTmuxLocator = strictObject({
|
|
14106
|
+
runtime: literal("tmux"),
|
|
14104
14107
|
tmuxSession: nonEmptyString,
|
|
14105
14108
|
tmuxWindowId: nonEmptyString,
|
|
14106
14109
|
tmuxPaneId: nonEmptyString.optional()
|
|
14107
14110
|
});
|
|
14108
|
-
var
|
|
14111
|
+
var stateK8sLocator = strictObject({
|
|
14112
|
+
runtime: literal("kubernetes"),
|
|
14113
|
+
namespace: nonEmptyString,
|
|
14114
|
+
podName: nonEmptyString,
|
|
14115
|
+
podUid: nonEmptyString,
|
|
14116
|
+
pvcName: nonEmptyString
|
|
14117
|
+
});
|
|
14118
|
+
var stateLocator = discriminatedUnion("runtime", [stateTmuxLocator, stateK8sLocator]);
|
|
14119
|
+
var stateTreeLocator = discriminatedUnion("runtime", [
|
|
14120
|
+
stateTmuxLocator.extend({ ompSessionFile: nonEmptyString.optional() }),
|
|
14121
|
+
stateK8sLocator.extend({ ompSessionFile: nonEmptyString.optional() })
|
|
14122
|
+
]);
|
|
14109
14123
|
var stateIssue = strictObject({
|
|
14110
14124
|
key: nonEmptyString,
|
|
14111
14125
|
title: string2(),
|
|
@@ -14132,7 +14146,7 @@ var stateRole = strictObject({
|
|
|
14132
14146
|
sessionId: nonEmptyString.optional(),
|
|
14133
14147
|
readyConfirmedAt: number2().optional(),
|
|
14134
14148
|
launchFailures: number2().int().nonnegative().optional(),
|
|
14135
|
-
locator:
|
|
14149
|
+
locator: stateLocator.optional()
|
|
14136
14150
|
});
|
|
14137
14151
|
var LegionDaemonApi = {
|
|
14138
14152
|
State: {
|
|
@@ -14147,7 +14161,7 @@ var LegionDaemonApi = {
|
|
|
14147
14161
|
queue: array(nonEmptyString)
|
|
14148
14162
|
}),
|
|
14149
14163
|
gates: record(string2(), stateGate),
|
|
14150
|
-
controllerLocator:
|
|
14164
|
+
controllerLocator: stateLocator.optional(),
|
|
14151
14165
|
roles: record(string2(), stateRole),
|
|
14152
14166
|
controllerPendingNotices: number2().int().nonnegative(),
|
|
14153
14167
|
pendingStatusWrites: array(nonEmptyString)
|
|
@@ -14950,6 +14964,12 @@ function askUrgency(args) {
|
|
|
14950
14964
|
const value = args.urgency;
|
|
14951
14965
|
return ASK_URGENCIES.find((urgency) => urgency === value);
|
|
14952
14966
|
}
|
|
14967
|
+
function askKind(args) {
|
|
14968
|
+
const value = optionalString(args, "kind");
|
|
14969
|
+
if (value === undefined || value === "action")
|
|
14970
|
+
return value;
|
|
14971
|
+
throw new Error("kind must be action");
|
|
14972
|
+
}
|
|
14953
14973
|
function parseDispatchRef(ref) {
|
|
14954
14974
|
const projectDocument = ref.match(/^dispatch:\/\/([A-Z][A-Z0-9]{1,9})\/artifact\/([^/@]+)(?:@v(\d+))?(?:\/(ask|comment)\/([^/]+))?$/);
|
|
14955
14975
|
if (projectDocument) {
|
|
@@ -15385,9 +15405,11 @@ async function executeDispatchTool(input) {
|
|
|
15385
15405
|
const options = args.options;
|
|
15386
15406
|
const multiple = optionalBoolean(args, "multiple");
|
|
15387
15407
|
const urgency = askUrgency(args);
|
|
15408
|
+
const kind = askKind(args);
|
|
15388
15409
|
const anchored = anchorArgs && resolved ? anchor(resolved.artifact, anchorArgs) : undefined;
|
|
15389
15410
|
const askInput = {
|
|
15390
15411
|
question: stringArg(args, "question"),
|
|
15412
|
+
...kind === undefined ? {} : { kind },
|
|
15391
15413
|
...Array.isArray(options) ? { options } : {},
|
|
15392
15414
|
...multiple === undefined ? {} : { multiple },
|
|
15393
15415
|
...urgency === undefined ? {} : { urgency },
|
package/package.json
CHANGED
package/skills/dispatch/SKILL.md
CHANGED
|
@@ -36,6 +36,12 @@ it to the agent through `dispatch.token` in `~/.config/opencode/envoy.json` or `
|
|
|
36
36
|
The server records the minting human as the owner of that session's writes. `DISPATCH_AGENT_TOKEN`
|
|
37
37
|
is the shared devbox fallback; do not configure it for an individual agent.
|
|
38
38
|
|
|
39
|
+
The deployed Dispatch server's browser origin is configured separately with
|
|
40
|
+
`DISPATCH_SERVER_URL` in the deployment `compose/.env`. Do not change an
|
|
41
|
+
agent's `envoy.json` to set the GitHub OAuth callback origin: the value must
|
|
42
|
+
be the exact URL humans type in their browser, and the GitHub App callback is
|
|
43
|
+
`<DISPATCH_SERVER_URL>/auth/callback`.
|
|
44
|
+
|
|
39
45
|
## Writing a spec
|
|
40
46
|
|
|
41
47
|
A spec has two readers: the human who decides reads the top; the implementer who builds reads the
|
|
@@ -128,6 +134,24 @@ An ask must be answerable from its own text and its anchor alone. Anchor a quest
|
|
|
128
134
|
about a comment with `reply_to`; thread a follow-up on your own ask with `reply_to_ask`; cite anything else with a `dispatch://`
|
|
129
135
|
reference (see [References](#references)). Never write "see above", "the message above", or "as attached".
|
|
130
136
|
|
|
137
|
+
**Anything that needs the human is an ask, or it does not exist.** An approval, a credential,
|
|
138
|
+
a setting only they can change, a review click, a conflict between two of their own rules - if
|
|
139
|
+
your work waits on it, open a `dispatch_ask` with `kind: "action"` the moment you know, the
|
|
140
|
+
action as the question (the server supplies the fixed `Done` / `Can't` options; `Can't` requires
|
|
141
|
+
an explanation). Never write it into a spec, a comment reply, a message, or a
|
|
142
|
+
pull-request body: nothing in those paths reaches the human's Inbox, and a human who is not
|
|
143
|
+
reading your document does not know they are the blocker. Before asking, try to remove the
|
|
144
|
+
step: a value already on the machine, a permission you already hold, an API that replaces the
|
|
145
|
+
click. One ask per item, `urgency: "high"` when work is stopped on it; while it is open, keep
|
|
146
|
+
working on everything that is not.
|
|
147
|
+
|
|
148
|
+
Use `kind: "action"` for a to-do handed to a human. It has fixed `Done` / `Can't` options;
|
|
149
|
+
`Can't` requires an explanation, while the asker can still correct the action's wording:
|
|
150
|
+
```ts
|
|
151
|
+
dispatch_ask({ issue: "DSP-42", kind: "action",
|
|
152
|
+
question: "Confirm the deployment is complete." })
|
|
153
|
+
```
|
|
154
|
+
|
|
131
155
|
Correct or refine an open ask in place instead of opening a second question:
|
|
132
156
|
```ts
|
|
133
157
|
dispatch_edit_ask({
|
|
@@ -25,7 +25,10 @@ environment, then run:
|
|
|
25
25
|
The command resolves the project from daemon state, claims the Envoy role for the current
|
|
26
26
|
session, and posts readiness before controller commands can act. It retains the environment
|
|
27
27
|
capability for `legion({ op: "set_status", issue, status })`. Never pass a secret as a command argument
|
|
28
|
-
or copy it into a transcript.
|
|
28
|
+
or copy it into a transcript. The claim is kept alive automatically afterwards: the Envoy
|
|
29
|
+
registration heartbeat re-asserts it and re-posts readiness whenever the listener loses sight of
|
|
30
|
+
this session, so `/legion-claim-controller` is the manual override, not a routine step after a
|
|
31
|
+
listener restart.
|
|
29
32
|
|
|
30
33
|
This handshake lets the daemon redeliver held controller work. It does not turn the controller
|
|
31
34
|
into a state holder: daemon state and the Dispatch project remain authoritative.
|