@sjawhar/opencode-legion-envoy 1.10.0 → 1.12.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(),
|
|
@@ -13740,7 +13741,7 @@ var SPEC_SECTIONS = [
|
|
|
13740
13741
|
];
|
|
13741
13742
|
var SPEC_WRITING_GUIDANCE = `When writing a spec, use these sections in order: ${SPEC_SECTIONS.join(", ")}. ` + "Write for a reader who has not seen the code: plain sentences, every identifier expanded on " + "first use, no coined shorthand; see skills/dispatch Writing for the human and Writing a spec.";
|
|
13742
13743
|
var ASK_URGENCIES = ["low", "med", "high", "blocking"];
|
|
13743
|
-
var DOC_EDIT_OPS = ["replace", "delete", "insert"];
|
|
13744
|
+
var DOC_EDIT_OPS = ["replace", "delete", "insert", "retype"];
|
|
13744
13745
|
var dispatchToolSpecs = [
|
|
13745
13746
|
{
|
|
13746
13747
|
name: "dispatch_issue",
|
|
@@ -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()
|
|
@@ -13850,7 +13852,7 @@ var dispatchToolSpecs = [
|
|
|
13850
13852
|
},
|
|
13851
13853
|
{
|
|
13852
13854
|
name: "dispatch_doc_edit",
|
|
13853
|
-
description: "Apply deterministic
|
|
13855
|
+
description: "Apply deterministic document edits, including retyping an identified paragraph into a schema-declared typed block. " + "Do not use it for review feedback or for reading; use dispatch_comment, dispatch_suggest, or dispatch_doc_read instead. " + `The spec (or any document) holds requirements, design, and decisions - never progress, status, or timestamps. ${OWNER_REFERENCE} ${SPEC_WRITING_GUIDANCE}`,
|
|
13854
13856
|
arguments: (z) => ({
|
|
13855
13857
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
13856
13858
|
project: z.string().describe("Project key owning the document.").optional(),
|
|
@@ -13863,7 +13865,10 @@ var dispatchToolSpecs = [
|
|
|
13863
13865
|
occurrence: z.number({ int: true, min: 0 }).describe("Optional zero-based match occurrence.").optional(),
|
|
13864
13866
|
markdown: z.string().describe("Markdown to insert.").optional(),
|
|
13865
13867
|
after: z.string().describe("Anchor after which to insert.").optional(),
|
|
13866
|
-
before: z.string().describe("Anchor before which to insert.").optional()
|
|
13868
|
+
before: z.string().describe("Anchor before which to insert.").optional(),
|
|
13869
|
+
block: z.string().describe("Block id to retype.").optional(),
|
|
13870
|
+
type: z.string().describe("Typed block name for retype.").optional(),
|
|
13871
|
+
attributes: z.unknown().describe("Typed block attributes for retype.").optional()
|
|
13867
13872
|
})).describe("Flat tagged edits; the server validates fields required for each operation."),
|
|
13868
13873
|
summary: z.string().describe("Optional named-version summary.").optional()
|
|
13869
13874
|
}),
|
|
@@ -14307,6 +14312,7 @@ function zodSchemaApi(zod) {
|
|
|
14307
14312
|
},
|
|
14308
14313
|
boolean: () => api.boolean(),
|
|
14309
14314
|
enum: (values) => api.enum(values),
|
|
14315
|
+
unknown: () => api.unknown(),
|
|
14310
14316
|
array: (item, opts = {}) => {
|
|
14311
14317
|
let schema = api.array(item);
|
|
14312
14318
|
if (opts.min !== undefined)
|
|
@@ -14946,6 +14952,12 @@ function askUrgency(args) {
|
|
|
14946
14952
|
const value = args.urgency;
|
|
14947
14953
|
return ASK_URGENCIES.find((urgency) => urgency === value);
|
|
14948
14954
|
}
|
|
14955
|
+
function askKind(args) {
|
|
14956
|
+
const value = optionalString(args, "kind");
|
|
14957
|
+
if (value === undefined || value === "action")
|
|
14958
|
+
return value;
|
|
14959
|
+
throw new Error("kind must be action");
|
|
14960
|
+
}
|
|
14949
14961
|
function parseDispatchRef(ref) {
|
|
14950
14962
|
const projectDocument = ref.match(/^dispatch:\/\/([A-Z][A-Z0-9]{1,9})\/artifact\/([^/@]+)(?:@v(\d+))?(?:\/(ask|comment)\/([^/]+))?$/);
|
|
14951
14963
|
if (projectDocument) {
|
|
@@ -15381,9 +15393,11 @@ async function executeDispatchTool(input) {
|
|
|
15381
15393
|
const options = args.options;
|
|
15382
15394
|
const multiple = optionalBoolean(args, "multiple");
|
|
15383
15395
|
const urgency = askUrgency(args);
|
|
15396
|
+
const kind = askKind(args);
|
|
15384
15397
|
const anchored = anchorArgs && resolved ? anchor(resolved.artifact, anchorArgs) : undefined;
|
|
15385
15398
|
const askInput = {
|
|
15386
15399
|
question: stringArg(args, "question"),
|
|
15400
|
+
...kind === undefined ? {} : { kind },
|
|
15387
15401
|
...Array.isArray(options) ? { options } : {},
|
|
15388
15402
|
...multiple === undefined ? {} : { multiple },
|
|
15389
15403
|
...urgency === undefined ? {} : { urgency },
|
|
@@ -15487,8 +15501,10 @@ async function executeDispatchTool(input) {
|
|
|
15487
15501
|
...summary === undefined ? {} : { summary },
|
|
15488
15502
|
actor
|
|
15489
15503
|
});
|
|
15504
|
+
const retyped = ops.filter((operation) => operation.op === "retype").length;
|
|
15505
|
+
const versionText = edited.version === null ? "no new version" : `version ${edited.version.number}`;
|
|
15490
15506
|
return {
|
|
15491
|
-
text:
|
|
15507
|
+
text: retyped === 0 ? `Applied ${edited.applied} ops (${versionText})` : `Applied ${edited.applied} ops; retyped ${retyped} block${retyped === 1 ? "" : "s"} (${versionText})`,
|
|
15492
15508
|
details: writeResultDetails(resolved, {
|
|
15493
15509
|
applied: edited.applied,
|
|
15494
15510
|
...edited.version === null ? {} : { version: edited.version.number }
|
package/package.json
CHANGED
package/skills/dispatch/SKILL.md
CHANGED
|
@@ -128,6 +128,24 @@ An ask must be answerable from its own text and its anchor alone. Anchor a quest
|
|
|
128
128
|
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
129
|
reference (see [References](#references)). Never write "see above", "the message above", or "as attached".
|
|
130
130
|
|
|
131
|
+
**Anything that needs the human is an ask, or it does not exist.** An approval, a credential,
|
|
132
|
+
a setting only they can change, a review click, a conflict between two of their own rules - if
|
|
133
|
+
your work waits on it, open a `dispatch_ask` with `kind: "action"` the moment you know, the
|
|
134
|
+
action as the question (the server supplies the fixed `Done` / `Can't` options; `Can't` requires
|
|
135
|
+
an explanation). Never write it into a spec, a comment reply, a message, or a
|
|
136
|
+
pull-request body: nothing in those paths reaches the human's Inbox, and a human who is not
|
|
137
|
+
reading your document does not know they are the blocker. Before asking, try to remove the
|
|
138
|
+
step: a value already on the machine, a permission you already hold, an API that replaces the
|
|
139
|
+
click. One ask per item, `urgency: "high"` when work is stopped on it; while it is open, keep
|
|
140
|
+
working on everything that is not.
|
|
141
|
+
|
|
142
|
+
Use `kind: "action"` for a to-do handed to a human. It has fixed `Done` / `Can't` options;
|
|
143
|
+
`Can't` requires an explanation, while the asker can still correct the action's wording:
|
|
144
|
+
```ts
|
|
145
|
+
dispatch_ask({ issue: "DSP-42", kind: "action",
|
|
146
|
+
question: "Confirm the deployment is complete." })
|
|
147
|
+
```
|
|
148
|
+
|
|
131
149
|
Correct or refine an open ask in place instead of opening a second question:
|
|
132
150
|
```ts
|
|
133
151
|
dispatch_edit_ask({
|
|
@@ -199,13 +217,16 @@ exact `EditOp` shape:
|
|
|
199
217
|
|
|
200
218
|
```ts
|
|
201
219
|
type EditOp = {
|
|
202
|
-
op: "replace" | "delete" | "insert";
|
|
220
|
+
op: "replace" | "delete" | "insert" | "retype";
|
|
203
221
|
find?: string;
|
|
204
222
|
with?: string;
|
|
205
223
|
occurrence?: number;
|
|
206
224
|
markdown?: string;
|
|
207
225
|
after?: string;
|
|
208
226
|
before?: string;
|
|
227
|
+
block?: string;
|
|
228
|
+
type?: string;
|
|
229
|
+
attributes?: Record<string, unknown>;
|
|
209
230
|
};
|
|
210
231
|
```
|
|
211
232
|
|
|
@@ -221,6 +242,9 @@ and deleting a cell's quoted text removes only that text.
|
|
|
221
242
|
|
|
222
243
|
Use `replace` for inline continuation. Use zero-based `occurrence` for a repeated target; re-read a missing or ambiguous target before
|
|
223
244
|
retrying. Pass `summary` to name the version when recording a decision.
|
|
245
|
+
`retype` turns the paragraph with `block` into the named typed `type` in place. It keeps the
|
|
246
|
+
block id and uses `attributes` for client-owned typed attributes. Use it when an existing
|
|
247
|
+
paragraph is the question that should become a decision.
|
|
224
248
|
|
|
225
249
|
## Typed blocks
|
|
226
250
|
|
|
@@ -236,6 +260,19 @@ quoted: `:::callout{kind="warning" title="Risk"}`. Do not write Pandoc-style `::
|
|
|
236
260
|
block. Do not set attributes the schema marks `server: true`; the server ignores them and reasserts
|
|
237
261
|
its authoritative value at settlement.
|
|
238
262
|
|
|
263
|
+
Questions about a document must be `ask` blocks, never an `Open questions` prose section. An ask
|
|
264
|
+
body is one or more question paragraphs followed by an optional bullet list of options, where each
|
|
265
|
+
item is `Label: description`. For example:
|
|
266
|
+
|
|
267
|
+
```md
|
|
268
|
+
:::ask{urgency="high" multiple="false"}
|
|
269
|
+
Should we ship the migration?
|
|
270
|
+
|
|
271
|
+
- Ship: Release the verified change.
|
|
272
|
+
- Hold: Wait for another review.
|
|
273
|
+
:::
|
|
274
|
+
```
|
|
275
|
+
|
|
239
276
|
## Comments and suggestions
|
|
240
277
|
|
|
241
278
|
Add feedback with:
|
|
@@ -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.
|