@sjawhar/opencode-legion-envoy 3.2.3 → 3.2.4
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
|
@@ -14142,7 +14142,7 @@ var dispatchToolSpecs = [
|
|
|
14142
14142
|
ops: [{ op: "delete_column", block: "table-123", index: 1 }],
|
|
14143
14143
|
precondition: { blocks: [{ id: "table-123", token: "sha256:current-table-token" }] }
|
|
14144
14144
|
},
|
|
14145
|
-
description: "Apply deterministic document edits: replace or delete quoted text, insert markdown at an anchor, retype an identified paragraph or typed block into a schema-declared typed block, delete or move a whole block by its id, or delete a table row or column in place. " + "Do not use it for review feedback or for reading; use dispatch_comment, dispatch_suggest, or dispatch_doc_read instead. " + `For replace, delete, and quote anchors, find text as rendered: inline Markdown (**bold**, \`code\`) is tolerated and must be balanced; a leading '# ' matches a heading at any level. replace is inline: with is the new text of the matched span, so a marker of a different kind from the block's own stays literal text ('4. Design' written into a heading). A with that opens with a marker of the same kind as the matched block's own would write it twice and is INVALID_OP - including prose that merely looks like one ('1999. was a year' into an ordered item), which you write as text by escaping it ('1999\\. was a year'). The exception is a heading rename whose find carried a heading marker: replace(find="## Old", with="## New") gives '## New', and a different level applies only when find named the heading's actual level (find "## Old" with "### New" makes it an h3), since '# ' selects a heading without naming its level. Any non-empty with that renders to no text - a line indented four spaces or a tab, which markdown reads as a code block, or whitespace alone - is INVALID_OP rather than a silent deletion; pass an empty with to delete the matched text on purpose. ` + "with cannot open a new block: after a hard line break inside with (two trailing spaces, or a backslash, before the newline) a heading, bullet, '1.'/'1)' ordered, or '>' blockquote marker is INVALID_OP too, since that line would stay escaped text inside the matched block - use insert, plus delete for what it replaces, to add the block. " + "A delete whose find is a block's entire text removes the block (a list emptied of its items goes too); delete with block removes any block by id, and move with block relocates one. delete_row and delete_column take a table block and a zero-based index, preserving the table block id and refusing to remove cells with open asks or unresolved comments. " + 'Insert and move anchors also accept "start", "end", "heading:<exact heading text>", and "block:<id>"; block ids and their tokens come from GET /api/v1/artifacts/{artifact UUID}/blocks (the route takes the artifact UUID, not its slug). ' + "Optionally require the state just read: precondition selects exactly one of a document token from dispatch_doc_read, or block {id, token} values from /blocks. A block guard must include every block the batch changes; Dispatch resolves quote targets and rejects an uncovered batch rather than applying it. Use a document token for insert or move, which depend on document order. Prefer block tokens when the covered content blocks are independent sections. Tokens include inline marks, so a fresh human comment also makes a stale edit fail. PRECONDITION_FAILED means re-read; EDIT_QUEUE_FULL means back off before retrying. " + "The result carries the document token this edit produced, so a chain of guarded edits passes each result's token as the next edit's precondition with no dispatch_doc_read between them. " + "A batch that leaves the document exactly as it was mints no version, named or not, and the result says nothing changed and names each operation that did nothing. " + `The spec (or any document) holds requirements, design, and decisions - never progress, status, or timestamps. ${OWNER_REFERENCE} ${SPEC_WRITING_GUIDANCE}`,
|
|
14145
|
+
description: "Apply deterministic document edits: replace or delete quoted text, insert markdown at an anchor, retype an identified paragraph or typed block into a schema-declared typed block, delete or move a whole block by its id, or delete a table row or column in place. " + "Do not use it for review feedback or for reading; use dispatch_comment, dispatch_suggest, or dispatch_doc_read instead. " + `For replace, delete, and quote anchors, find text as rendered: inline Markdown (**bold**, \`code\`) is tolerated and must be balanced; a leading '# ' matches a heading at any level. replace is inline: with is the new text of the matched span, so a marker of a different kind from the block's own stays literal text ('4. Design' written into a heading). A with that opens with a marker of the same kind as the matched block's own would write it twice and is INVALID_OP - including prose that merely looks like one ('1999. was a year' into an ordered item), which you write as text by escaping it ('1999\\. was a year'). The exception is a heading rename whose find carried a heading marker: replace(find="## Old", with="## New") gives '## New', and a different level applies only when find named the heading's actual level (find "## Old" with "### New" makes it an h3), since '# ' selects a heading without naming its level. Any non-empty with that renders to no text - a line indented four spaces or a tab, which markdown reads as a code block, or whitespace alone - is INVALID_OP rather than a silent deletion; pass an empty with to delete the matched text on purpose - where the block holding it cannot be written without that paragraph, the replace is INVALID_OP and the refusal names the delete that removes it instead. ` + "with cannot open a new block: after a hard line break inside with (two trailing spaces, or a backslash, before the newline) a heading, bullet, '1.'/'1)' ordered, or '>' blockquote marker is INVALID_OP too, since that line would stay escaped text inside the matched block - use insert, plus delete for what it replaces, to add the block. A hard break in with is itself INVALID_OP when the matched text is in a heading or a table cell, which are written on one line. " + "A delete whose find is a block's entire text removes the block (a list emptied of its items goes too); delete with block removes any block by id, and move with block relocates one. delete_row and delete_column take a table block and a zero-based index, preserving the table block id and refusing to remove cells with open asks or unresolved comments. " + 'Insert and move anchors also accept "start", "end", "heading:<exact heading text>", and "block:<id>"; block ids and their tokens come from GET /api/v1/artifacts/{artifact UUID}/blocks (the route takes the artifact UUID, not its slug). ' + "Optionally require the state just read: precondition selects exactly one of a document token from dispatch_doc_read, or block {id, token} values from /blocks. A block guard must include every block the batch changes; Dispatch resolves quote targets and rejects an uncovered batch rather than applying it. Use a document token for insert or move, which depend on document order. Prefer block tokens when the covered content blocks are independent sections. Tokens include inline marks, so a fresh human comment also makes a stale edit fail. PRECONDITION_FAILED means re-read; EDIT_QUEUE_FULL means back off before retrying. " + "The result carries the document token this edit produced, so a chain of guarded edits passes each result's token as the next edit's precondition with no dispatch_doc_read between them. " + "A batch that leaves the document exactly as it was mints no version, named or not, and the result says nothing changed and names each operation that did nothing. " + `The spec (or any document) holds requirements, design, and decisions - never progress, status, or timestamps. ${OWNER_REFERENCE} ${SPEC_WRITING_GUIDANCE}`,
|
|
14146
14146
|
arguments: (z2) => ({
|
|
14147
14147
|
issue: z2.string().describe(ISSUE_REFERENCE).optional(),
|
|
14148
14148
|
project: z2.string().describe("Project key owning the document.").optional(),
|
|
@@ -14151,7 +14151,7 @@ var dispatchToolSpecs = [
|
|
|
14151
14151
|
ops: z2.array(z2.object({
|
|
14152
14152
|
op: z2.enum(DOC_EDIT_OPS).describe("Edit operation."),
|
|
14153
14153
|
find: z2.string().describe("Text of the target as rendered, for replace or delete; inline markdown (**bold**, `code`) is tolerated and must be balanced; a leading '# ' matches a heading. A delete of a block's entire text removes the block.").optional(),
|
|
14154
|
-
with: z2.string().describe("Replacement text for replace, parsed as inline markdown within the matched block; a marker of a different kind from the block's own is literal text, one of the same kind is refused unless it is a heading rename (where a level named by find is what lets with change it), a backslash escape keeps prose that merely looks like a marker, a block marker after a hard line break is refused because replace cannot open a new block, and any non-empty value that renders to no text is refused - only an empty value deletes the match.").optional(),
|
|
14154
|
+
with: z2.string().describe("Replacement text for replace: inside a code block, the code's literal text as sent (line breaks at its end do not survive a read, and a line of three or more colons in code inside a typed block, indented less than four columns from where the typed block's lines start, is refused, since the browser editor ends the typed block there: indent it four or more spaces, or move the code block out); text that would read as block syntax at a line start, such as '---' over a paragraph, is stored escaped and reads back as those characters, so a rule is added with insert beside the paragraph; elsewhere parsed as inline markdown within the matched block; a marker of a different kind from the block's own is literal text, one of the same kind is refused unless it is a heading rename (where a level named by find is what lets with change it), a backslash escape keeps prose that merely looks like a marker, a block marker after a hard line break is refused because replace cannot open a new block, and any non-empty value that renders to no text is refused - only an empty value deletes the match.").optional(),
|
|
14155
14155
|
occurrence: z2.number({ int: true, min: 0 }).describe("Optional zero-based match occurrence.").optional(),
|
|
14156
14156
|
markdown: z2.string().describe("Markdown to insert.").optional(),
|
|
14157
14157
|
after: z2.string().describe(`Insert or move after this anchor: a quote of the neighbouring block's text, or one of "start", "end", "heading:<exact heading text>", "block:<id>".`).optional(),
|
package/package.json
CHANGED
package/skills/dispatch/SKILL.md
CHANGED
|
@@ -580,7 +580,9 @@ a new typed block, omit `#block-id`; Dispatch mints it. When editing an existing
|
|
|
580
580
|
its id and every rendered attribute. Never copy an existing block's id into new markdown: an id
|
|
581
581
|
names one block, so an insert, upload or suggestion whose markdown names an id the document holds
|
|
582
582
|
outside the text it replaces is refused naming the id: `INVALID_OP` for an insert,
|
|
583
|
-
`INVALID_MARKDOWN` for any other write.
|
|
583
|
+
`INVALID_MARKDOWN` for any other write. To rewrite such a block whole, `delete` it and then
|
|
584
|
+
`insert` the new one carrying its id, anchored on the block before or after it, in that order and
|
|
585
|
+
in one batch: an insert carrying an id the document still holds is refused.
|
|
584
586
|
|
|
585
587
|
Use only the type names, content rule, attributes, and enum values returned by the schema. Values are
|
|
586
588
|
quoted: `:::callout{kind="warning" title="Risk"}`. Do not write Pandoc-style `::: {.callout}`, leaf
|
|
@@ -653,6 +655,14 @@ dispatch_suggest({ issue?, project?, artifact, ref?, quote, replace_with, body?,
|
|
|
653
655
|
It returns issue or project-document owner details plus `comment`. A human accepts or rejects a suggestion.
|
|
654
656
|
Errors: `TARGET_AMBIGUOUS` (add `occurrence`), `TARGET_NOT_FOUND` (re-read first), `INVALID_ANCHOR`/`ANCHOR_MISSING`/`ANCHOR_ORPHANED`
|
|
655
657
|
(bad, unwritten, or stale quote), `INVALID_MARKDOWN`/`DOC_SCHEMA` (malformed content), `CAP_EXCEEDED`, `ISSUE_CLOSED`.
|
|
658
|
+
Suggest only what the quoted block can hold. The accept, not the suggestion, checks that: an
|
|
659
|
+
accept whose `replace_with` would break an ask block that was readable before it is refused with
|
|
660
|
+
`INVALID_ASK_BLOCK` - a question given a code block, text after an ask's options, a second option
|
|
661
|
+
list, or an emptied question; one that names an id the document holds outside the text it
|
|
662
|
+
replaces, an ask under a held id included, with `INVALID_MARKDOWN`; and one no part of the
|
|
663
|
+
document can hold where it sits (a code block over a table cell's whole text), or whose quote runs
|
|
664
|
+
into an ask or callout from the text before it, with `INVALID_OP`. Either changes nothing: the human sees the reason with no Retry, and the suggestion
|
|
665
|
+
stays open until someone rejects or replaces it.
|
|
656
666
|
|
|
657
667
|
## Artifacts
|
|
658
668
|
|
|
@@ -66,12 +66,23 @@ heading's actual level — `find="## Old"`, `with="### New"` retitles and makes
|
|
|
66
66
|
so `find="# Old"` renames the text and keeps whatever level it selected. `with` that forms more than one
|
|
67
67
|
paragraph is rejected (`INVALID_OP` on `with`) — see the recipe for a multi-paragraph rewrite below; so is any non-empty `with` that
|
|
68
68
|
renders to no text, which a line indented four spaces or a tab does (markdown reads that as a code block), as does whitespace
|
|
69
|
-
alone. An empty `with`
|
|
69
|
+
alone. An empty `with` deletes the matched text on purpose; where the block holding it cannot be written without that
|
|
70
|
+
paragraph, the replace is `INVALID_OP`, and the refusal names the `delete` that removes it instead. Inside a code
|
|
71
|
+
block none of this applies: `with` is the code's literal text, written as sent, whitespace, markdown syntax and
|
|
72
|
+
references included, except that line breaks at the end of the code's text, and a line holding only whitespace in a
|
|
73
|
+
list item's code, do not survive the next read; and a line of three or more colons in code inside a typed block,
|
|
74
|
+
indented less than four columns from where the typed block's lines start, is `INVALID_OP`, since the browser editor
|
|
75
|
+
ends the typed block there - indent it four or more spaces (a tab reaches only the next tab stop, which inside a list
|
|
76
|
+
item or a blockquote can be two columns away), or move the code block out of the typed block. Text a `replace` writes
|
|
77
|
+
that would read as block syntax at a line start is stored escaped and reads back as the characters you sent: `---` over
|
|
78
|
+
a paragraph is stored `\---`, not a rule, so to add a rule, `insert` it beside the paragraph (`insert` with markdown
|
|
79
|
+
`***`). Use zero-based
|
|
80
|
+
`occurrence` for a
|
|
70
81
|
repeated target; re-read a missing or ambiguous target before retrying. Pass `summary` to name the version when recording a decision.
|
|
71
82
|
|
|
72
83
|
**Rewriting several paragraphs is one `replace` per paragraph, then a read-back.** `replace` is inline:
|
|
73
|
-
each `with` is the new text of one paragraph, and a `with` that forms two paragraphs is
|
|
74
|
-
text says. Give each paragraph you rewrite its own `replace`, which keeps that paragraph's block id and every
|
|
84
|
+
each `with` is the new text of one paragraph, and outside a code block a `with` that forms two paragraphs is
|
|
85
|
+
refused whatever the text says. Give each paragraph you rewrite its own `replace`, which keeps that paragraph's block id and every
|
|
75
86
|
anchor outside the text you rewrite. A comment or ask anchored to the text you rewrite loses its quote but keeps
|
|
76
87
|
its pin to the block, so the dashboard still shows it beside that paragraph; a delete (below) loses both. An
|
|
77
88
|
anchor that straddles the boundary keeps its mark over the words you left alone, with its quote shortened to
|
|
@@ -88,18 +99,26 @@ as literal text, except a backtick fence, which becomes an inline code span whos
|
|
|
88
99
|
info string and line breaks included (```` ```go\nx := 1``` ```` becomes the code span `go` + a line break +
|
|
89
100
|
`x := 1`), and a tilde fence, which stays literal.
|
|
90
101
|
|
|
91
|
-
HTML is not written as text at all
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
102
|
+
Outside a code block, HTML is not written as text at all, and a `replace` whose HTML would open a block where it lands is refused
|
|
103
|
+
(`INVALID_OP` on `with`), because the schema carries no block HTML. `<div>x</div>` and `<!-- note -->` open one
|
|
104
|
+
at the start of a paragraph or a list item and on the line after a hard break; a tag such as `<br>` opens one
|
|
105
|
+
only when it stands alone as a paragraph or a list item. The same HTML inside a line, in a table cell or in a
|
|
106
|
+
heading is inline HTML and is kept as written.
|
|
107
|
+
|
|
108
|
+
A hard line break in `with` (two trailing spaces or a backslash before a newline) is refused in a heading or a
|
|
109
|
+
table cell (`INVALID_OP` on `with`): both are written on one line, so the break would end the block there. A line
|
|
110
|
+
break inside a code span or inline HTML there is refused the same way. Write the text without the break, or
|
|
111
|
+
`insert` a new block after this one.
|
|
95
112
|
|
|
96
113
|
When the new text adds a block that is not a paragraph beside paragraphs, `insert` it beside the
|
|
97
114
|
paragraph you replaced, which keeps that paragraph's id; only when no paragraph of the new text is left to take
|
|
98
|
-
the old block's place is it
|
|
99
|
-
costs the id (below)
|
|
115
|
+
the old block's place is it a `delete` of the old block and then an `insert` of the new one, anchored on the
|
|
116
|
+
block before or after it. The delete is what costs the id (below); a typed block keeps its id when the insert
|
|
117
|
+
carries it, which works only in that order, because an insert carrying an id the document still holds is refused.
|
|
118
|
+
Then read the document back with
|
|
100
119
|
`dispatch_doc_read` and read the passage and its neighbours, not a grep for the words you added: an empty
|
|
101
|
-
`with` deletes the matched text on purpose, so a `replace` whose `with` you meant to fill
|
|
102
|
-
paragraph — the block and its id stay, holding nothing — and only a read shows what the document now says.
|
|
120
|
+
`with` deletes the matched text on purpose where the block allows it, so a `replace` whose `with` you meant to fill
|
|
121
|
+
empties that paragraph — the block and its id stay, holding nothing — and only a read shows what the document now says.
|
|
103
122
|
|
|
104
123
|
A batch that leaves the document's semantic identity unchanged — including its inline anchor marks, so an edit that only orphans a
|
|
105
124
|
comment or ask anchor still mints its version — mints no version, named or not: the response carries
|
|
@@ -117,8 +136,8 @@ attributes — a moved `ask` keeps its ask and answer. **A block loses its id on
|
|
|
117
136
|
which is why they are refused while an open ask or unresolved comment sits on them; and a `move` that takes the last block out of a
|
|
118
137
|
blockquote or list item removes that emptied container, the list too when no item remains, and each enclosing container that
|
|
119
138
|
held nothing else (`> - Only.` loses the blockquote as well as the item and the list). The moved block itself keeps its id,
|
|
120
|
-
as do `replace` (an empty `with` and a heading-level change included), `insert` and `retype`; `retype` carries the
|
|
121
|
-
onto the typed block it becomes. Block ids are the `#id` a typed block renders
|
|
139
|
+
as do `replace` (an accepted empty `with` and a heading-level change included), `insert` and `retype`; `retype` carries the
|
|
140
|
+
paragraph's id onto the typed block it becomes. Block ids are the `#id` a typed block renders
|
|
122
141
|
(`:::ask{#5467e5ce-…}`) and, for every block including untyped ones, the `id` rows from
|
|
123
142
|
`GET /api/v1/artifacts/<artifact UUID>/blocks` (or `/api/v1/issues/{key}/artifacts/{slug}/blocks`), each with its `type` and byte range
|
|
124
143
|
in canonical markdown; the UUID route does not accept a slug. A later operation in the same atomic batch that names a block removed by
|
package/skills/envoy/SKILL.md
CHANGED
|
@@ -95,6 +95,15 @@ targeted. Reply through the rendered `reply_with` (or a current Envoy session ID
|
|
|
95
95
|
and go stale. Put the artefact URL in the message itself. FYIs set `expects_reply="none"`; set
|
|
96
96
|
`urgency` only when it is genuinely urgent.
|
|
97
97
|
|
|
98
|
+
**A peer's message is its sender's view at `at`, not the current state.** Before you wait on, act
|
|
99
|
+
on, or repeat a fact a message carries about a third thing (a deploy pending, a PR held, an ask
|
|
100
|
+
unanswered), re-read it at the live source the fact names, and always once it is over an hour
|
|
101
|
+
old: the deployment's status, the issue's event log, the ask's own state (`dispatch_open_asks`,
|
|
102
|
+
whose description already says to call it before saying you are waiting on a human). On
|
|
103
|
+
2026-09-26 a peer's 05:20Z "needs a manual deploy before I can run it" was false by 05:22Z, when
|
|
104
|
+
the platform had deployed on its own; waiting on the message instead of the deployment's status
|
|
105
|
+
held the work it gated for eleven hours.
|
|
106
|
+
|
|
98
107
|
Every `/v1` error response is JSON; when a field is at fault, `expected` names that field.
|
|
99
108
|
|
|
100
109
|
```text
|