@sjawhar/pi-envoy 9.3.0 → 9.5.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/dispatch.js
CHANGED
|
@@ -14243,7 +14243,7 @@ var dispatchToolSpecs = [
|
|
|
14243
14243
|
ops: [{ op: "delete_column", block: "table-123", index: 1 }],
|
|
14244
14244
|
precondition: { blocks: [{ id: "table-123", token: "sha256:current-table-token" }] }
|
|
14245
14245
|
},
|
|
14246
|
-
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. A task item (\`- [ ]\` / \`- [x]\`) is ticked the same way: when find matches from the start of the item's text and with opens with '[x] ' or '[ ] ', the opening sets the item's box and the rest is its new text (replace(find="Write it", with="[x] Write it") ticks it); the same opening over a plain list item or a paragraph stays literal text. 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 - a list item, quote, typed block or footnote definition left holding only the emptied paragraph keeps it. ` + "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. A delete or retype that would take an ask block out of the document while its ask is open is refused, with nothing sent. 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. " +
|
|
14246
|
+
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. A task item (\`- [ ]\` / \`- [x]\`) is ticked the same way: when find matches from the start of the item's text and with opens with '[x] ' or '[ ] ', the opening sets the item's box and the rest is its new text (replace(find="Write it", with="[x] Write it") ticks it); the same opening over a plain list item or a paragraph stays literal text. 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 - a list item, quote, typed block or footnote definition left holding only the emptied paragraph keeps it. ` + "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. A delete or retype that would take an ask block out of the document while its ask is open is refused, with nothing sent. 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>"; dispatch doc-read --blocks lists every block's id, type, opening words and token, and each table's rows and cells with the indexes delete_row and delete_column take (GET /api/v1/artifacts/{artifact UUID}/blocks serves the same list). ` + "Optionally require the state just read: precondition selects exactly one of a document token from dispatch doc-read, or block {id, token} values from dispatch doc-read --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. " + "A change a browser removes while the edit is in flight is never reported as applied: EDIT_LOST_TO_CONCURRENT_CHANGE means the write was refused and nothing was written, so re-read the document and decide again, as with PRECONDITION_FAILED; lost_ops on a successful result names operations whose text the live document no longer has, because the deletion landed after the version was written. " + `The spec (or any document) holds requirements, design, and decisions - never progress, status, or timestamps. ${OWNER_REFERENCE} ${SPEC_WRITING_POINTER}`,
|
|
14247
14247
|
arguments: (z2) => ({
|
|
14248
14248
|
issue: z2.string().describe(ISSUE_REFERENCE).optional(),
|
|
14249
14249
|
project: z2.string().describe("Project key owning the document.").optional(),
|
|
@@ -14257,7 +14257,7 @@ var dispatchToolSpecs = [
|
|
|
14257
14257
|
markdown: z2.string().describe("Markdown to insert; a CR LF or a lone carriage return in it is written as a line feed.").optional(),
|
|
14258
14258
|
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(),
|
|
14259
14259
|
before: z2.string().describe(`Insert or move before this anchor: a quote of the neighbouring block's text, or one of "start", "end", "heading:<exact heading text>", "block:<id>".`).optional(),
|
|
14260
|
-
block: z2.string().describe("Block id for retype, delete, move, delete_row, or delete_column: the #id of a typed block, or an id
|
|
14260
|
+
block: z2.string().describe("Block id for retype, delete, move, delete_row, or delete_column: the #id of a typed block, or an id dispatch doc-read --blocks lists.").optional(),
|
|
14261
14261
|
index: z2.number({ int: true, min: 0 }).describe("Zero-based row or column index for delete_row or delete_column.").optional(),
|
|
14262
14262
|
type: z2.string().describe("Typed block name for retype.").optional(),
|
|
14263
14263
|
attributes: z2.unknown().describe("Typed block attributes for retype; a CR LF or a lone carriage return in a string value is written as a line feed.").optional()
|
|
@@ -14265,9 +14265,9 @@ var dispatchToolSpecs = [
|
|
|
14265
14265
|
precondition: z2.object({
|
|
14266
14266
|
document: z2.string({ min: 1 }).describe("Token for the exact canonical document returned by dispatch doc-read.").optional(),
|
|
14267
14267
|
blocks: z2.array(z2.object({
|
|
14268
|
-
id: z2.string({ min: 1 }).describe("Stable block id
|
|
14268
|
+
id: z2.string({ min: 1 }).describe("Stable block id dispatch doc-read --blocks lists."),
|
|
14269
14269
|
token: z2.string({ min: 1 }).describe("That block's full-state token, including inline marks.")
|
|
14270
|
-
}), { min: 1 }).describe("Every content block this batch changes, each with the token
|
|
14270
|
+
}), { min: 1 }).describe("Every content block this batch changes, each with the token dispatch doc-read --blocks lists.").optional()
|
|
14271
14271
|
}).describe("Optional optimistic-concurrency guard; select exactly one of document or blocks.").optional(),
|
|
14272
14272
|
summary: z2.string().describe("Optional named-version summary.").optional()
|
|
14273
14273
|
}),
|
|
@@ -14276,12 +14276,13 @@ var dispatchToolSpecs = [
|
|
|
14276
14276
|
{
|
|
14277
14277
|
name: "dispatch_doc_read",
|
|
14278
14278
|
example: { issue: "DSP-1" },
|
|
14279
|
-
description: "Read a live document or a named document version, or the text of an uploaded file at its latest or named version. " + "Do not use it for issue status, asks, or events; " + "use dispatch read instead. Supply ref, issue, or project plus artifact; issue plus an omitted artifact reads the primary document. " + "A live read returns its document token for an optional dispatch doc-edit precondition
|
|
14279
|
+
description: "Read a live document or a named document version, or the text of an uploaded file at its latest or named version. " + "Do not use it for issue status, asks, or events; " + "use dispatch read instead. Supply ref, issue, or project plus artifact; issue plus an omitted artifact reads the primary document. " + "A live read returns its document token for an optional dispatch doc-edit precondition. With blocks it lists the live document's blocks instead of its markdown, in document order: each block's id, type, token, anchored comments and asks, and opening words, and under a table each row by the index delete_row takes and each cell by its row and the column delete_column takes; block ids, block tokens and those indexes are what dispatch doc-edit takes. " + "A picture (an uploaded PNG, JPEG, GIF or WebP of at most 3,750,000 bytes, 5 MB once base64-encoded) comes back as an image you see, with its name, type, size and version, every time you ask, including one a read already showed this session; " + "any other file that is not UTF-8 text is described, with the route that serves its bytes. " + OWNER_REFERENCE,
|
|
14280
14280
|
arguments: (z2) => ({
|
|
14281
14281
|
issue: z2.string().describe(ISSUE_REFERENCE).optional(),
|
|
14282
14282
|
project: z2.string().describe("Project key owning the document.").optional(),
|
|
14283
14283
|
artifact: z2.string().describe("Optional project document artifact id, slug, or filename; primary document by default for an issue.").optional(),
|
|
14284
14284
|
version: z2.number({ int: true, min: 1 }).describe("Optional version number.").optional(),
|
|
14285
|
+
blocks: z2.boolean().describe("List the live document's blocks, with each table's rows and cells, instead of its markdown; refused with a version, or for an uploaded file.").optional(),
|
|
14285
14286
|
ref: z2.string().describe("Optional dispatch:// document reference; a picture in a conversation on the Agents page is dispatch://agent/<session id>/artifact/<slug>@vN.").optional()
|
|
14286
14287
|
}),
|
|
14287
14288
|
validation: documentOwnerValidation(true)
|
|
@@ -16637,6 +16638,12 @@ function argumentProblems(tool, args) {
|
|
|
16637
16638
|
}
|
|
16638
16639
|
break;
|
|
16639
16640
|
}
|
|
16641
|
+
case "dispatch_doc_read": {
|
|
16642
|
+
if (optionalBoolean(args, "blocks") === true && optionalNumber(args, "version") !== undefined) {
|
|
16643
|
+
problems.push("blocks lists the live document, the one dispatch doc-edit changes; name no version");
|
|
16644
|
+
}
|
|
16645
|
+
break;
|
|
16646
|
+
}
|
|
16640
16647
|
}
|
|
16641
16648
|
return problems;
|
|
16642
16649
|
}
|
|
@@ -17315,6 +17322,47 @@ ${text}`,
|
|
|
17315
17322
|
details
|
|
17316
17323
|
};
|
|
17317
17324
|
}
|
|
17325
|
+
var BLOCK_OPENING_CHARS = 60;
|
|
17326
|
+
async function listDocumentBlocks(client, artifact) {
|
|
17327
|
+
const before = await client.docRead(artifact.id);
|
|
17328
|
+
const blocks = await client.artifactBlocks(artifact.id);
|
|
17329
|
+
const after = await client.docRead(artifact.id);
|
|
17330
|
+
if (before.token !== after.token || before.markdown !== after.markdown) {
|
|
17331
|
+
throw new Error(`${artifact.name} changed while dispatch doc-read --blocks read it, so the opening words could name the wrong text; nothing is listed. Run it again.`);
|
|
17332
|
+
}
|
|
17333
|
+
const bytes = new TextEncoder().encode(before.markdown);
|
|
17334
|
+
const decoder = new TextDecoder;
|
|
17335
|
+
const lines = [
|
|
17336
|
+
`Blocks of ${artifact.name}, in document order, each indented under the block holding it: id, type, (token; anchored comments and asks), opening words. A table lists its rows by the index delete_row takes and its cells by the row and the column delete_column takes.`
|
|
17337
|
+
];
|
|
17338
|
+
const holders = [];
|
|
17339
|
+
for (const block of blocks) {
|
|
17340
|
+
while (holders.length > 0 && block.from >= (holders.at(-1)?.to ?? 0))
|
|
17341
|
+
holders.pop();
|
|
17342
|
+
const indent = " ".repeat(holders.length);
|
|
17343
|
+
const flat = decoder.decode(bytes.subarray(block.from, block.to)).replace(/\s+/g, " ").trim();
|
|
17344
|
+
const points = Array.from(flat);
|
|
17345
|
+
const opening = points.length > BLOCK_OPENING_CHARS ? `${points.slice(0, BLOCK_OPENING_CHARS).join("")}\u2026` : flat;
|
|
17346
|
+
const { comments, asks } = block.references;
|
|
17347
|
+
lines.push(`${indent}${block.id} ${block.type} (${block.token ?? "no token"}; ${comments} comment${comments === 1 ? "" : "s"}, ${asks} ask${asks === 1 ? "" : "s"}): ${opening}`);
|
|
17348
|
+
if (block.type === "table") {
|
|
17349
|
+
if (block.table === undefined) {
|
|
17350
|
+
lines.push(`${indent} This Dispatch does not report table rows yet, so no row or column index is listed.`);
|
|
17351
|
+
} else {
|
|
17352
|
+
for (const row of block.table.rows) {
|
|
17353
|
+
lines.push(`${indent} row ${row.index}, ${row.id === "" ? "no id yet" : `id ${row.id}`}`);
|
|
17354
|
+
for (const cell of row.cells) {
|
|
17355
|
+
const under = cell.header === "" ? "" : `, under ${JSON.stringify(cell.header)}`;
|
|
17356
|
+
lines.push(`${indent} row ${cell.row} column ${cell.column}, ${cell.id === "" ? "no id yet" : `id ${cell.id}`}${under}: ${cell.text === "" ? "(empty)" : cell.text}`);
|
|
17357
|
+
}
|
|
17358
|
+
}
|
|
17359
|
+
}
|
|
17360
|
+
}
|
|
17361
|
+
holders.push(block);
|
|
17362
|
+
}
|
|
17363
|
+
return { text: lines.join(`
|
|
17364
|
+
`), token: before.token };
|
|
17365
|
+
}
|
|
17318
17366
|
async function refuseOpenDecisionBlocks(client, tool, resolved) {
|
|
17319
17367
|
const artifact = resolved.artifact;
|
|
17320
17368
|
const latest = artifact.approval?.latest_version;
|
|
@@ -18089,11 +18137,29 @@ ${followsAsk(askOwner)}`,
|
|
|
18089
18137
|
const { agentArtifact } = ownerArguments;
|
|
18090
18138
|
if (agentArtifact !== undefined) {
|
|
18091
18139
|
const artifact = await client.getAgentArtifact(agentArtifact.session, agentArtifact.slug);
|
|
18140
|
+
if (optionalBoolean(args, "blocks") === true) {
|
|
18141
|
+
throw new Error(`${artifact.name} is an uploaded ${artifact.kind}, not a document, so it has no blocks. Read it with dispatch doc-read without --blocks.`);
|
|
18142
|
+
}
|
|
18092
18143
|
return readUploadedFile(client, artifact, { session: agentArtifact.session, artifact: artifact.slug }, optionalNumber(args, "version") ?? agentArtifact.version, `agent/${agentArtifact.session}`, shown);
|
|
18093
18144
|
}
|
|
18094
18145
|
const artifactReference = optionalString(args, "artifact") ?? (ownerArguments.ref?.kind === "spec" || ownerArguments.ref?.kind === "artifact" ? ownerArguments.ref.id : undefined);
|
|
18095
18146
|
const resolved = await resolveDocument(documentOwner(), artifactReference);
|
|
18096
18147
|
const version2 = optionalNumber(args, "version") ?? ownerArguments.ref?.version;
|
|
18148
|
+
if (optionalBoolean(args, "blocks") === true) {
|
|
18149
|
+
if (resolved.artifact.kind === "file" || resolved.artifact.kind === "image") {
|
|
18150
|
+
throw new Error(`${resolved.artifact.name} is an uploaded ${resolved.artifact.kind}, not a document, so it has no blocks. Read it with dispatch doc-read without --blocks.`);
|
|
18151
|
+
}
|
|
18152
|
+
const listed = await listDocumentBlocks(client, resolved.artifact);
|
|
18153
|
+
return {
|
|
18154
|
+
text: listed.token === undefined ? listed.text : `${listed.text}
|
|
18155
|
+
|
|
18156
|
+
Document token: ${listed.token}`,
|
|
18157
|
+
details: resolved.owner.kind === "project" ? {
|
|
18158
|
+
project: resolved.artifact.project,
|
|
18159
|
+
document: documentLabel(resolved.artifact.project, resolved.artifact.slug)
|
|
18160
|
+
} : { issue: resolved.issue?.key }
|
|
18161
|
+
};
|
|
18162
|
+
}
|
|
18097
18163
|
if (resolved.artifact.kind === "file" || resolved.artifact.kind === "image") {
|
|
18098
18164
|
const { artifact } = resolved;
|
|
18099
18165
|
return readUploadedFile(client, artifact, resolved.owner.kind === "project" ? {
|
package/dist/envoy.js
CHANGED
|
@@ -30230,7 +30230,7 @@ var dispatchToolSpecs = [
|
|
|
30230
30230
|
ops: [{ op: "delete_column", block: "table-123", index: 1 }],
|
|
30231
30231
|
precondition: { blocks: [{ id: "table-123", token: "sha256:current-table-token" }] }
|
|
30232
30232
|
},
|
|
30233
|
-
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. A task item (\`- [ ]\` / \`- [x]\`) is ticked the same way: when find matches from the start of the item's text and with opens with '[x] ' or '[ ] ', the opening sets the item's box and the rest is its new text (replace(find="Write it", with="[x] Write it") ticks it); the same opening over a plain list item or a paragraph stays literal text. 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 - a list item, quote, typed block or footnote definition left holding only the emptied paragraph keeps it. ` + "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. A delete or retype that would take an ask block out of the document while its ask is open is refused, with nothing sent. 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. " +
|
|
30233
|
+
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. A task item (\`- [ ]\` / \`- [x]\`) is ticked the same way: when find matches from the start of the item's text and with opens with '[x] ' or '[ ] ', the opening sets the item's box and the rest is its new text (replace(find="Write it", with="[x] Write it") ticks it); the same opening over a plain list item or a paragraph stays literal text. 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 - a list item, quote, typed block or footnote definition left holding only the emptied paragraph keeps it. ` + "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. A delete or retype that would take an ask block out of the document while its ask is open is refused, with nothing sent. 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>"; dispatch doc-read --blocks lists every block's id, type, opening words and token, and each table's rows and cells with the indexes delete_row and delete_column take (GET /api/v1/artifacts/{artifact UUID}/blocks serves the same list). ` + "Optionally require the state just read: precondition selects exactly one of a document token from dispatch doc-read, or block {id, token} values from dispatch doc-read --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. " + "A change a browser removes while the edit is in flight is never reported as applied: EDIT_LOST_TO_CONCURRENT_CHANGE means the write was refused and nothing was written, so re-read the document and decide again, as with PRECONDITION_FAILED; lost_ops on a successful result names operations whose text the live document no longer has, because the deletion landed after the version was written. " + `The spec (or any document) holds requirements, design, and decisions - never progress, status, or timestamps. ${OWNER_REFERENCE} ${SPEC_WRITING_POINTER}`,
|
|
30234
30234
|
arguments: (z2) => ({
|
|
30235
30235
|
issue: z2.string().describe(ISSUE_REFERENCE).optional(),
|
|
30236
30236
|
project: z2.string().describe("Project key owning the document.").optional(),
|
|
@@ -30244,7 +30244,7 @@ var dispatchToolSpecs = [
|
|
|
30244
30244
|
markdown: z2.string().describe("Markdown to insert; a CR LF or a lone carriage return in it is written as a line feed.").optional(),
|
|
30245
30245
|
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(),
|
|
30246
30246
|
before: z2.string().describe(`Insert or move before this anchor: a quote of the neighbouring block's text, or one of "start", "end", "heading:<exact heading text>", "block:<id>".`).optional(),
|
|
30247
|
-
block: z2.string().describe("Block id for retype, delete, move, delete_row, or delete_column: the #id of a typed block, or an id
|
|
30247
|
+
block: z2.string().describe("Block id for retype, delete, move, delete_row, or delete_column: the #id of a typed block, or an id dispatch doc-read --blocks lists.").optional(),
|
|
30248
30248
|
index: z2.number({ int: true, min: 0 }).describe("Zero-based row or column index for delete_row or delete_column.").optional(),
|
|
30249
30249
|
type: z2.string().describe("Typed block name for retype.").optional(),
|
|
30250
30250
|
attributes: z2.unknown().describe("Typed block attributes for retype; a CR LF or a lone carriage return in a string value is written as a line feed.").optional()
|
|
@@ -30252,9 +30252,9 @@ var dispatchToolSpecs = [
|
|
|
30252
30252
|
precondition: z2.object({
|
|
30253
30253
|
document: z2.string({ min: 1 }).describe("Token for the exact canonical document returned by dispatch doc-read.").optional(),
|
|
30254
30254
|
blocks: z2.array(z2.object({
|
|
30255
|
-
id: z2.string({ min: 1 }).describe("Stable block id
|
|
30255
|
+
id: z2.string({ min: 1 }).describe("Stable block id dispatch doc-read --blocks lists."),
|
|
30256
30256
|
token: z2.string({ min: 1 }).describe("That block's full-state token, including inline marks.")
|
|
30257
|
-
}), { min: 1 }).describe("Every content block this batch changes, each with the token
|
|
30257
|
+
}), { min: 1 }).describe("Every content block this batch changes, each with the token dispatch doc-read --blocks lists.").optional()
|
|
30258
30258
|
}).describe("Optional optimistic-concurrency guard; select exactly one of document or blocks.").optional(),
|
|
30259
30259
|
summary: z2.string().describe("Optional named-version summary.").optional()
|
|
30260
30260
|
}),
|
|
@@ -30263,12 +30263,13 @@ var dispatchToolSpecs = [
|
|
|
30263
30263
|
{
|
|
30264
30264
|
name: "dispatch_doc_read",
|
|
30265
30265
|
example: { issue: "DSP-1" },
|
|
30266
|
-
description: "Read a live document or a named document version, or the text of an uploaded file at its latest or named version. " + "Do not use it for issue status, asks, or events; " + "use dispatch read instead. Supply ref, issue, or project plus artifact; issue plus an omitted artifact reads the primary document. " + "A live read returns its document token for an optional dispatch doc-edit precondition
|
|
30266
|
+
description: "Read a live document or a named document version, or the text of an uploaded file at its latest or named version. " + "Do not use it for issue status, asks, or events; " + "use dispatch read instead. Supply ref, issue, or project plus artifact; issue plus an omitted artifact reads the primary document. " + "A live read returns its document token for an optional dispatch doc-edit precondition. With blocks it lists the live document's blocks instead of its markdown, in document order: each block's id, type, token, anchored comments and asks, and opening words, and under a table each row by the index delete_row takes and each cell by its row and the column delete_column takes; block ids, block tokens and those indexes are what dispatch doc-edit takes. " + "A picture (an uploaded PNG, JPEG, GIF or WebP of at most 3,750,000 bytes, 5 MB once base64-encoded) comes back as an image you see, with its name, type, size and version, every time you ask, including one a read already showed this session; " + "any other file that is not UTF-8 text is described, with the route that serves its bytes. " + OWNER_REFERENCE,
|
|
30267
30267
|
arguments: (z2) => ({
|
|
30268
30268
|
issue: z2.string().describe(ISSUE_REFERENCE).optional(),
|
|
30269
30269
|
project: z2.string().describe("Project key owning the document.").optional(),
|
|
30270
30270
|
artifact: z2.string().describe("Optional project document artifact id, slug, or filename; primary document by default for an issue.").optional(),
|
|
30271
30271
|
version: z2.number({ int: true, min: 1 }).describe("Optional version number.").optional(),
|
|
30272
|
+
blocks: z2.boolean().describe("List the live document's blocks, with each table's rows and cells, instead of its markdown; refused with a version, or for an uploaded file.").optional(),
|
|
30272
30273
|
ref: z2.string().describe("Optional dispatch:// document reference; a picture in a conversation on the Agents page is dispatch://agent/<session id>/artifact/<slug>@vN.").optional()
|
|
30273
30274
|
}),
|
|
30274
30275
|
validation: documentOwnerValidation(true)
|
|
@@ -406,7 +406,9 @@ every version that records a decision is named with `dispatch doc-edit --summary
|
|
|
406
406
|
|
|
407
407
|
Read the current document before changing it with `dispatch doc-read` (`--issue`, or `--project`
|
|
408
408
|
and `--artifact`, an optional `--version`, or `--ref`).
|
|
409
|
-
It prints live or versioned markdown with open marks. A live read ends with a document token; `--
|
|
409
|
+
It prints live or versioned markdown with open marks. A live read ends with a document token; `--blocks` lists the live
|
|
410
|
+
document's block ids, tokens and table rows instead, which `dispatch doc-edit` takes to address a block that is not a typed
|
|
411
|
+
block. `--issue` with no
|
|
410
412
|
`--artifact` reads the issue specification; a project needs `--artifact`; and a `dispatch://PROJECT/artifact/<document-ref>`
|
|
411
413
|
ref supplies both, where `document-ref` is the slug (an id or a filename resolves when no document has that slug). A file
|
|
412
414
|
`dispatch artifact` uploaded reads its text at the latest or named version, or a description when it is not UTF-8 text.
|
|
@@ -60,10 +60,15 @@ table's block id. A row index includes the header: row `0` is the header and its
|
|
|
60
60
|
row and any row's last column cannot be deleted. An index is required. A missing, non-integer, negative, or out-of-range index is
|
|
61
61
|
`INVALID_OP` on `index`, naming the supplied value and the table's actual dimensions before making any change. Markdown parsing
|
|
62
62
|
canonicalizes short ragged rows by padding missing cells, so column deletion preserves every non-selected cell in the canonical table.
|
|
63
|
-
`
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
`dispatch doc-read --issue <KEY> --blocks` (or `--project` and `--artifact`) lists every block of the live document in place of its
|
|
64
|
+
markdown, one line each, in document order and indented under the block holding it: its id, its type, its token, how many comments
|
|
65
|
+
and asks anchor to it (a table counts its cells' anchors) and its opening words. Under a table it lists each row by its index, what
|
|
66
|
+
`delete_row` takes (0 is the header), and each cell by its row and its column, what `delete_column` takes, with the opening words
|
|
67
|
+
of the header drawn above it. A cell spanning columns or rows counts once in its row, so its column is that index and not where
|
|
68
|
+
the markdown writes it. A Dispatch that does not list table rows yet says so instead of an index. It takes no `--version`, since
|
|
69
|
+
only the live document's ids and tokens address an edit, and an uploaded file has no blocks.
|
|
70
|
+
`GET /api/v1/artifacts/<artifact UUID>/blocks` and `GET /api/v1/artifacts/<artifact UUID>/blocks/<block id>` answer the same
|
|
71
|
+
facts for a reader outside the command. A row or column
|
|
67
72
|
deletion that would remove an open ask or unresolved comment anchor is `INVALID_OP` on `index`, naming the axis and anchor ids;
|
|
68
73
|
answered asks and resolved comments are history and do not block it. A `find` or quote anchor tolerates inline Markdown
|
|
69
74
|
(`**bold**`, `` `code` ``) and a leading `# ` selects a heading by its text; a miss names the quote and the three nearest blocks so
|
|
@@ -160,14 +165,12 @@ blockquote or list item removes that emptied container, the list too when no ite
|
|
|
160
165
|
held nothing else (`> - Only.` loses the blockquote as well as the item and the list). The moved block itself keeps its id,
|
|
161
166
|
as do `replace` (an accepted empty `with` and a heading-level change included), `insert` and `retype`; `retype` carries the
|
|
162
167
|
paragraph's id onto the typed block it becomes. Block ids are the `#id` a typed block renders
|
|
163
|
-
(`:::ask{#5467e5ce-…}`) and, for every block including untyped ones, the `
|
|
164
|
-
`GET /api/v1/artifacts/<artifact UUID>/blocks` (or `/api/v1/issues/{key}/artifacts/{slug}/blocks`), each with its `type` and byte range
|
|
165
|
-
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
|
|
168
|
+
(`:::ask{#5467e5ce-…}`) and, for every block including untyped ones, the ids `dispatch doc-read --blocks` lists. A later operation in the same atomic batch that names a block removed by
|
|
166
169
|
an earlier `delete {block}` fails as `INVALID_OP` naming the earlier operation and the parent block that cascaded the removal. A move
|
|
167
170
|
whose anchor lies inside the moved block, or a delete that would leave a typed block without the body its content rule requires, is
|
|
168
171
|
`INVALID_OP` naming the field and the rule.
|
|
169
172
|
|
|
170
|
-
`
|
|
173
|
+
`dispatch doc-read --blocks` lists a full-state `token` on every block, including
|
|
171
174
|
inline marks. To reject a stale edit, pass `--precondition-json` with exactly one of
|
|
172
175
|
`{"document": "<document token>"}` or
|
|
173
176
|
`{"blocks": [{"id": "<block id>", "token": "<block token>"}]}`. A document token comes from
|