@sjawhar/opencode-legion-envoy 0.24.0 → 0.26.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 +2 -1
- package/dist/src/server.js +24 -36
- package/package.json +1 -1
- package/skills/dispatch/SKILL.md +12 -5
- package/skills/legion-architect/SKILL.md +1 -1
package/README.md
CHANGED
|
@@ -25,7 +25,8 @@ This package exposes:
|
|
|
25
25
|
The native `dispatch_*` tools create and read Dispatch issues, asks, comments, documents, and
|
|
26
26
|
artifacts. They are present when `dispatch.enabled` resolves a server URL and bearer token from
|
|
27
27
|
envoy.json (`~/.config/opencode/envoy.json`, merged with `<repo>/.opencode/envoy.json`) or the
|
|
28
|
-
`DISPATCH_URL` and `DISPATCH_TOKEN` environment variables.
|
|
28
|
+
`DISPATCH_URL` and `DISPATCH_TOKEN` environment variables; `dispatch.enabled: true` without
|
|
29
|
+
`dispatch.serverUrl` targets `http://localhost:8766`. Each call fills the target issue from
|
|
29
30
|
the session working directory, stamps it with the OpenCode session id and title, and stores
|
|
30
31
|
`details.topic` as tool metadata so a successful mutation subscribes to that exact Dispatch topic.
|
|
31
32
|
|
package/dist/src/server.js
CHANGED
|
@@ -13580,6 +13580,8 @@ var CommentEventPayloadSchema = object({
|
|
|
13580
13580
|
artifact_name: string2().optional(),
|
|
13581
13581
|
body: string2().optional(),
|
|
13582
13582
|
reply_to: string2().nullish(),
|
|
13583
|
+
ask_id: string2().nullish(),
|
|
13584
|
+
ask_question: string2().optional(),
|
|
13583
13585
|
anchor: object({ quote: string2().optional() }).nullish(),
|
|
13584
13586
|
suggestion: object({ replace_with: string2().optional() }).nullish()
|
|
13585
13587
|
});
|
|
@@ -13630,14 +13632,15 @@ var dispatchToolSpecs = [
|
|
|
13630
13632
|
},
|
|
13631
13633
|
{
|
|
13632
13634
|
name: "dispatch_comment",
|
|
13633
|
-
description: "Add review feedback to an issue or document quote. Do not use it for an exact replacement; use " + `dispatch_suggest instead. Body is at most 2,000 characters. ${ISSUE_REFERENCE}`,
|
|
13635
|
+
description: "Add review feedback to an issue or document quote, or reply to a question asked with dispatch_ask. " + "Do not use it for an exact replacement; use " + `dispatch_suggest instead. Body is at most 2,000 characters. ${ISSUE_REFERENCE}`,
|
|
13634
13636
|
arguments: (z) => ({
|
|
13635
13637
|
issue: z.string().describe(ISSUE_REFERENCE),
|
|
13636
13638
|
artifact: z.string().describe("Artifact slug or id required when quote is given.").optional(),
|
|
13637
13639
|
quote: z.string().describe("Optional exact quoted document text.").optional(),
|
|
13638
13640
|
occurrence: z.number({ int: true, min: 0 }).describe("Optional zero-based occurrence of quote.").optional(),
|
|
13639
13641
|
body: z.string({ max: 2000 }).describe("Review comment, at most 2,000 characters."),
|
|
13640
|
-
reply_to: z.string().describe("Optional comment id to reply to.").optional()
|
|
13642
|
+
reply_to: z.string().describe("Optional comment id to reply to.").optional(),
|
|
13643
|
+
reply_to_ask: z.string().describe("Optional ask id to reply to, threading this comment under that question. Mutually " + "exclusive with reply_to.").optional()
|
|
13641
13644
|
})
|
|
13642
13645
|
},
|
|
13643
13646
|
{
|
|
@@ -13948,25 +13951,10 @@ var LegionDaemonApi = {
|
|
|
13948
13951
|
request: architectCapability.extend({ issue: nonEmptyString, comment: string2().optional() }),
|
|
13949
13952
|
response: object({})
|
|
13950
13953
|
},
|
|
13951
|
-
SpawnToken: {
|
|
13952
|
-
request: architectCapability.extend({ issue: nonEmptyString, role: legionRole }),
|
|
13953
|
-
response: object({ spawnToken: nonEmptyString })
|
|
13954
|
-
},
|
|
13955
13954
|
ProvisioningCredential: {
|
|
13956
13955
|
request: architectCapability.extend({ issue: nonEmptyString }),
|
|
13957
13956
|
response: object({ token: nonEmptyString })
|
|
13958
13957
|
},
|
|
13959
|
-
RoleBacking: {
|
|
13960
|
-
request: strictObject({
|
|
13961
|
-
tree: nonEmptyString,
|
|
13962
|
-
issue: nonEmptyString,
|
|
13963
|
-
role: legionRole,
|
|
13964
|
-
sessionId: nonEmptyString,
|
|
13965
|
-
agentId: nonEmptyString,
|
|
13966
|
-
spawnToken: nonEmptyString
|
|
13967
|
-
}),
|
|
13968
|
-
response: object({})
|
|
13969
|
-
},
|
|
13970
13958
|
WorkerStarted: {
|
|
13971
13959
|
request: strictObject({
|
|
13972
13960
|
tree: nonEmptyString,
|
|
@@ -14013,20 +14001,6 @@ var LegionDaemonApi = {
|
|
|
14013
14001
|
roleToken: nonEmptyString
|
|
14014
14002
|
})
|
|
14015
14003
|
},
|
|
14016
|
-
Phase: {
|
|
14017
|
-
request: strictObject({
|
|
14018
|
-
tree: nonEmptyString,
|
|
14019
|
-
issue: nonEmptyString,
|
|
14020
|
-
phase: nonEmptyString,
|
|
14021
|
-
sessionId: nonEmptyString,
|
|
14022
|
-
spawnToken: nonEmptyString
|
|
14023
|
-
}),
|
|
14024
|
-
response: object({
|
|
14025
|
-
secret: nonEmptyString,
|
|
14026
|
-
gitName: nonEmptyString,
|
|
14027
|
-
gitEmail: nonEmptyString
|
|
14028
|
-
})
|
|
14029
|
-
},
|
|
14030
14004
|
WorkerSession: {
|
|
14031
14005
|
request: strictObject({
|
|
14032
14006
|
sessionId: nonEmptyString,
|
|
@@ -14668,8 +14642,12 @@ function childrenSummary(issue) {
|
|
|
14668
14642
|
].join(`
|
|
14669
14643
|
`);
|
|
14670
14644
|
}
|
|
14671
|
-
function askSummary(ask) {
|
|
14645
|
+
function askSummary({ ask, replies }) {
|
|
14672
14646
|
const answer = ask.answer;
|
|
14647
|
+
const chain = replies.flatMap((reply) => [
|
|
14648
|
+
`${reply.id} \xB7 ${reply.author.kind} ${reply.author.id}`,
|
|
14649
|
+
`Body: ${reply.body}`
|
|
14650
|
+
]);
|
|
14673
14651
|
return [
|
|
14674
14652
|
`Question: ${ask.question}`,
|
|
14675
14653
|
"Options:",
|
|
@@ -14680,7 +14658,9 @@ function askSummary(ask) {
|
|
|
14680
14658
|
`- By: ${answer.user}`,
|
|
14681
14659
|
`- Selected: ${answer.selected.length === 0 ? "none" : answer.selected.join(", ")}`,
|
|
14682
14660
|
...answer.text === null ? [] : [`- Text: ${answer.text}`]
|
|
14683
|
-
]
|
|
14661
|
+
],
|
|
14662
|
+
"Replies:",
|
|
14663
|
+
...chain.length === 0 ? ["- none"] : chain
|
|
14684
14664
|
].join(`
|
|
14685
14665
|
`);
|
|
14686
14666
|
}
|
|
@@ -14779,10 +14759,15 @@ async function executeDispatchTool(input) {
|
|
|
14779
14759
|
const resolved = artifactReference ? await resolveArtifact(client, issue(), artifactReference) : undefined;
|
|
14780
14760
|
const anchored = resolved ? anchor(resolved.artifact, args) : undefined;
|
|
14781
14761
|
const replyTo = optionalString(args, "reply_to");
|
|
14762
|
+
const replyToAsk = optionalString(args, "reply_to_ask");
|
|
14763
|
+
if (replyTo !== undefined && replyToAsk !== undefined) {
|
|
14764
|
+
throw new Error("reply_to and reply_to_ask cannot both be set");
|
|
14765
|
+
}
|
|
14782
14766
|
const comment = await client.comment(issue(), {
|
|
14783
14767
|
body: stringArg(args, "body"),
|
|
14784
14768
|
...anchored === undefined ? {} : { anchor: anchored },
|
|
14785
14769
|
...replyTo === undefined ? {} : { reply_to: replyTo },
|
|
14770
|
+
...replyToAsk === undefined ? {} : { ask_id: replyToAsk },
|
|
14786
14771
|
actor
|
|
14787
14772
|
});
|
|
14788
14773
|
return {
|
|
@@ -14891,10 +14876,13 @@ Open anchored asks/comments: ${marks.join(", ")}`,
|
|
|
14891
14876
|
}
|
|
14892
14877
|
case "dispatch_read": {
|
|
14893
14878
|
if (issueArguments.ref?.kind === "ask") {
|
|
14894
|
-
const
|
|
14879
|
+
const askRead = await client.getAsk(issueArguments.ref.id);
|
|
14895
14880
|
return {
|
|
14896
|
-
text: askSummary(
|
|
14897
|
-
details: {
|
|
14881
|
+
text: askSummary(askRead),
|
|
14882
|
+
details: {
|
|
14883
|
+
issue: askRead.ask.issue_key,
|
|
14884
|
+
topic: dispatchIssueSubject(askRead.ask.issue_key, ">")
|
|
14885
|
+
}
|
|
14898
14886
|
};
|
|
14899
14887
|
}
|
|
14900
14888
|
if (issueArguments.ref?.kind === "comment") {
|
package/package.json
CHANGED
package/skills/dispatch/SKILL.md
CHANGED
|
@@ -46,6 +46,12 @@ Anchor a document question with `anchor: { artifact, quote, occurrence? }`; `occ
|
|
|
46
46
|
zero-based and selects a repeated quote. An anchor whose quote disappears becomes orphaned but
|
|
47
47
|
remains readable against its original document version. An ask stays open until a human answers;
|
|
48
48
|
you cannot withdraw it. Ask once and well. The host steers you when a human answers.
|
|
49
|
+
|
|
50
|
+
An ask is a thread, not a dead end: a human can reply to it before or after answering, and you
|
|
51
|
+
(the asker) can reply too — e.g. acknowledging a clarifying question, or following up after the
|
|
52
|
+
answer. Use `reply_to_ask` on `dispatch_comment` to reply under your own ask; it is mutually
|
|
53
|
+
exclusive with `reply_to`.
|
|
54
|
+
|
|
49
55
|
## The spec is where narrative goes
|
|
50
56
|
|
|
51
57
|
Read the current document before changing it:
|
|
@@ -85,11 +91,12 @@ message.
|
|
|
85
91
|
Add feedback with:
|
|
86
92
|
|
|
87
93
|
```ts
|
|
88
|
-
dispatch_comment({ issue, artifact?, quote?, occurrence?, body, reply_to? })
|
|
94
|
+
dispatch_comment({ issue, artifact?, quote?, occurrence?, body, reply_to?, reply_to_ask? })
|
|
89
95
|
```
|
|
90
96
|
|
|
91
97
|
It returns `details` `{ issue, topic, comment }`. `quote` requires `artifact`; omit both for a
|
|
92
|
-
floating issue comment. Use `reply_to` to continue a comment
|
|
98
|
+
floating issue comment. Use `reply_to` to continue a comment thread; use `reply_to_ask` to reply
|
|
99
|
+
directly under a question asked with `dispatch_ask`. The two are mutually exclusive.
|
|
93
100
|
|
|
94
101
|
Propose an exact replacement instead of describing it:
|
|
95
102
|
|
|
@@ -149,9 +156,9 @@ dispatch_read({ issue?, ref? })
|
|
|
149
156
|
```
|
|
150
157
|
|
|
151
158
|
With an issue ref, it returns the issue summary, open asks, and recent events with `details`
|
|
152
|
-
`{ issue }`. With an ask ref, it returns that ask's question, options, state,
|
|
153
|
-
comment ref, it returns that comment and its quoted reply chain. Use
|
|
154
|
-
document contents.
|
|
159
|
+
`{ issue }`. With an ask ref, it returns that ask's question, options, state, answer, and its
|
|
160
|
+
reply thread. With a comment ref, it returns that comment and its quoted reply chain. Use
|
|
161
|
+
`dispatch_doc_read` for document contents.
|
|
155
162
|
|
|
156
163
|
## References
|
|
157
164
|
|
|
@@ -213,7 +213,7 @@ corresponding lifecycle procedure.
|
|
|
213
213
|
| `pr-closed-unmerged` | Decide from current scope whether to reopen the work, send a fresh implementer, or cancel it with a reason. Delegate the repository action to the responsible phase worker and keep ownership. |
|
|
214
214
|
| `issue-comment` | Interpret the comment in the issue's design context. Answer it, adjust the plan, or relay it via `envoy_publish` to the responsible worker's role token; scope and product decisions remain with you. |
|
|
215
215
|
| `catchup-overseer` | Verify its gates, child counts, and PR verdicts against current artifacts, then resume the applicable numbered lifecycle step. It is a current-state snapshot, not a raw-event replay. For each entry in its `phaseCompletions` (`{issue, role, summary, at}`, phases that completed while you were not live), handle it exactly as a `phase-complete` wake. |
|
|
216
|
-
| `
|
|
216
|
+
| `worker-died` | Payload `{type:"worker-died", issue, role}`. The daemon probed and retried this role's worker through `MAX_LAUNCH_FAILURES` attempts and could not confirm a boot — never a raw-event replay or a silent revive. Reassess the work and `spawn_worker` again for the role (it resumes the same agent via `--resume` if a session file survived) or reassign it if the failure looks environmental, not agent-specific. |
|
|
217
217
|
| `reopened` | Reopen the root lifecycle: inspect the reason and current artifacts, reassess scope and children, and resume at the first applicable numbered step. |
|
|
218
218
|
|
|
219
219
|
## Escalation judgment
|