@sjawhar/pi-legion-envoy 1.51.1 → 1.51.2
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/envoy.js +112 -12
- package/dist/legion.js +38 -1
- package/package.json +1 -1
package/dist/envoy.js
CHANGED
|
@@ -29908,6 +29908,7 @@ var DOC_EDIT_OPS = ["replace", "delete", "insert", "retype", "move"];
|
|
|
29908
29908
|
var dispatchToolSpecs = [
|
|
29909
29909
|
{
|
|
29910
29910
|
name: "dispatch_issue",
|
|
29911
|
+
example: { project: "DSP", title: "Native workspace" },
|
|
29911
29912
|
description: "Create a native Dispatch issue for newly tracked work. Search first with dispatch_search; if potentially duplicate issues exist, this returns 409 POSSIBLE_DUPLICATE unless force is true after reading them. " + `Do not use it when an existing issue already covers the work; read or update that issue instead. ${ISSUE_REFERENCE}`,
|
|
29912
29913
|
arguments: (z) => ({
|
|
29913
29914
|
project: z.string().describe("Project key for the new issue."),
|
|
@@ -29924,6 +29925,7 @@ var dispatchToolSpecs = [
|
|
|
29924
29925
|
},
|
|
29925
29926
|
{
|
|
29926
29927
|
name: "dispatch_issue_update",
|
|
29928
|
+
example: { issue: "DSP-1", status: "in_progress" },
|
|
29927
29929
|
description: "Update an existing issue: move its lifecycle status, retitle it, replace its labels, link a URL " + "(the pull request that delivers it, a run, a document), set its route, set or clear its parent, " + "or attach it to architecture components. Status is one of " + `${ISSUE_STATUSES.join(", ")}; outside Legion, move it yourself as the work advances; inside ` + "Legion the daemon moves it. external_links are " + "merged into the issue's existing links by URL, so linking the pull request you just opened " + "keeps every earlier link. components replaces the issue's own attachment and is allowed on a " + "closed issue. Priority is the human's and is not settable here. At least one " + `field besides issue is required. ${ISSUE_REFERENCE}`,
|
|
29928
29930
|
arguments: (z) => ({
|
|
29929
29931
|
issue: z.string().describe(ISSUE_REFERENCE),
|
|
@@ -29946,6 +29948,7 @@ var dispatchToolSpecs = [
|
|
|
29946
29948
|
},
|
|
29947
29949
|
{
|
|
29948
29950
|
name: "dispatch_ask",
|
|
29951
|
+
example: { issue: "DSP-1", question: "Ship this?" },
|
|
29949
29952
|
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. A to-do a human must complete is a question phrased as that to-do, with the options you want (for example Done / Can't). " + "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". A quote anchor is pinned to its block. Question is at most ${ASK_QUESTION_MAX} ` + `characters and has at most 8 options. ${OWNER_REFERENCE}`,
|
|
29950
29953
|
arguments: (z) => ({
|
|
29951
29954
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
@@ -29969,6 +29972,10 @@ var dispatchToolSpecs = [
|
|
|
29969
29972
|
},
|
|
29970
29973
|
{
|
|
29971
29974
|
name: "dispatch_edit_ask",
|
|
29975
|
+
example: {
|
|
29976
|
+
ask: "01234567-0000-4000-8000-000000000001",
|
|
29977
|
+
question: "Ship the revised plan?"
|
|
29978
|
+
},
|
|
29972
29979
|
description: "Edit an open question in place. Use it to correct or refine the same decision; retract the " + "old ask and open a new one when the decision itself changes. Previous text remains in the " + "event log. Only the asking session can edit it; answered or resolved asks cannot be edited.",
|
|
29973
29980
|
arguments: (z) => ({
|
|
29974
29981
|
ask: z.string().describe("Ask id to edit."),
|
|
@@ -29990,6 +29997,11 @@ var dispatchToolSpecs = [
|
|
|
29990
29997
|
},
|
|
29991
29998
|
{
|
|
29992
29999
|
name: "dispatch_resolve_ask",
|
|
30000
|
+
example: {
|
|
30001
|
+
ask: "01234567-0000-4000-8000-000000000001",
|
|
30002
|
+
kind: "retracted",
|
|
30003
|
+
reason: "A newer question supersedes this one."
|
|
30004
|
+
},
|
|
29993
30005
|
description: "Retract an open question that is moot or resolve one after finding the answer. This closes the question without answering it.",
|
|
29994
30006
|
arguments: (z) => ({
|
|
29995
30007
|
ask: z.string().describe("Ask id to close."),
|
|
@@ -29999,6 +30011,7 @@ var dispatchToolSpecs = [
|
|
|
29999
30011
|
},
|
|
30000
30012
|
{
|
|
30001
30013
|
name: "dispatch_resolve_comment",
|
|
30014
|
+
example: { comment: "01234567-0000-4000-8000-000000000001" },
|
|
30002
30015
|
description: "Resolve a review comment thread once it has been addressed - typically your own comment " + "after the document was fixed. Any session or human may resolve any open comment on an " + "open issue or project document; reopening a resolved comment is human-only (the dashboard). " + "Not for asks: use dispatch_resolve_ask.",
|
|
30003
30016
|
arguments: (z) => ({
|
|
30004
30017
|
comment: z.string().describe("Comment id (uuid), or a dispatch://KEY/comment/<id> or " + "dispatch://PROJECT/artifact/<slug>/comment/<id> reference; a reference accepts an " + "8+ character id prefix that is unique on its owner.")
|
|
@@ -30007,6 +30020,7 @@ var dispatchToolSpecs = [
|
|
|
30007
30020
|
},
|
|
30008
30021
|
{
|
|
30009
30022
|
name: "dispatch_follow",
|
|
30023
|
+
example: { ask: "01234567-0000-4000-8000-000000000001", action: "follow" },
|
|
30010
30024
|
description: "Follow or unfollow an ask. Every session that opens or replies to an ask follows it: its answer, " + "edits, resolution, and replies reach that session directly. Unfollow to stop; follow to rejoin or " + "to hear an ask you never wrote to. Whole-issue subscription is separate: envoy_subscribe " + "notifications.dispatch.issue.<KEY>.>",
|
|
30011
30025
|
arguments: (z) => ({
|
|
30012
30026
|
ask: z.string().describe("Full ask id (uuid)."),
|
|
@@ -30016,6 +30030,7 @@ var dispatchToolSpecs = [
|
|
|
30016
30030
|
},
|
|
30017
30031
|
{
|
|
30018
30032
|
name: "dispatch_comment",
|
|
30033
|
+
example: { issue: "DSP-1", body: "Looks good." },
|
|
30019
30034
|
description: "Add review feedback to an issue or project document quote, or reply to a question asked with dispatch_ask. " + "Do not use it for an exact replacement; use " + `dispatch_suggest instead. A quote anchor is pinned to its block. Body is at most 2,000 characters. ${OWNER_REFERENCE}`,
|
|
30020
30035
|
arguments: (z) => ({
|
|
30021
30036
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
@@ -30033,6 +30048,12 @@ var dispatchToolSpecs = [
|
|
|
30033
30048
|
},
|
|
30034
30049
|
{
|
|
30035
30050
|
name: "dispatch_suggest",
|
|
30051
|
+
example: {
|
|
30052
|
+
issue: "DSP-1",
|
|
30053
|
+
artifact: "spec",
|
|
30054
|
+
quote: "old wording",
|
|
30055
|
+
replace_with: "new wording"
|
|
30056
|
+
},
|
|
30036
30057
|
description: "Propose an exact replacement for quoted document text. Do not use it for general feedback; use " + `dispatch_comment instead. Optional explanation is at most 2,000 characters. ${OWNER_REFERENCE}`,
|
|
30037
30058
|
arguments: (z) => ({
|
|
30038
30059
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
@@ -30048,6 +30069,7 @@ var dispatchToolSpecs = [
|
|
|
30048
30069
|
},
|
|
30049
30070
|
{
|
|
30050
30071
|
name: "dispatch_message",
|
|
30072
|
+
example: { issue: "DSP-1", body: "Implementation started." },
|
|
30051
30073
|
description: "Post a note humans must read now: a reply to a human's message, a deliverable that landed, or a blocker only " + "they can clear. Never progress or status updates - Dispatch is a high-signal record, not a log. Not a decision " + `(dispatch_ask) or document feedback (dispatch_comment). Body is at most 2,000 characters. ${ISSUE_REFERENCE}`,
|
|
30052
30074
|
arguments: (z) => ({
|
|
30053
30075
|
issue: z.string().describe(ISSUE_REFERENCE),
|
|
@@ -30057,6 +30079,11 @@ var dispatchToolSpecs = [
|
|
|
30057
30079
|
},
|
|
30058
30080
|
{
|
|
30059
30081
|
name: "dispatch_doc_edit",
|
|
30082
|
+
example: {
|
|
30083
|
+
issue: "DSP-1",
|
|
30084
|
+
artifact: "spec",
|
|
30085
|
+
ops: [{ op: "replace", find: "old", with: "new" }]
|
|
30086
|
+
},
|
|
30060
30087
|
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, and delete or move a whole block by its id. " + "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; a leading '# ' matches a heading. replace is inline: with is the new text of the matched span, so a leading list or heading marker stays literal text. " + "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. " + 'Insert and move anchors also accept "start", "end", "heading:<exact heading text>", and "block:<id>"; block ids are the #id of a typed block or a row of GET /api/v1/artifacts/{id}/blocks. ' + `The spec (or any document) holds requirements, design, and decisions - never progress, status, or timestamps. ${OWNER_REFERENCE} ${SPEC_WRITING_GUIDANCE}`,
|
|
30061
30088
|
arguments: (z) => ({
|
|
30062
30089
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
@@ -30081,6 +30108,7 @@ var dispatchToolSpecs = [
|
|
|
30081
30108
|
},
|
|
30082
30109
|
{
|
|
30083
30110
|
name: "dispatch_doc_read",
|
|
30111
|
+
example: { issue: "DSP-1" },
|
|
30084
30112
|
description: "Read a live document or a named document 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. " + OWNER_REFERENCE,
|
|
30085
30113
|
arguments: (z) => ({
|
|
30086
30114
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
@@ -30093,6 +30121,7 @@ var dispatchToolSpecs = [
|
|
|
30093
30121
|
},
|
|
30094
30122
|
{
|
|
30095
30123
|
name: "dispatch_request_approval",
|
|
30124
|
+
example: { issue: "DSP-1" },
|
|
30096
30125
|
description: "Ask a human to approve a document at its current version - the exception path for a spec " + "that departs from what was settled or proposes children, not a step for every issue. Opens an " + "approval ask (Approve / Request changes) in the human's Inbox; the answer pins a review to the " + "document version and arrives as artifact.approved or artifact.changes_requested. A later edit " + "makes an approval stale; request again for the new version. Idempotent while a request is open. " + OWNER_REFERENCE,
|
|
30097
30126
|
arguments: (z) => ({
|
|
30098
30127
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
@@ -30103,6 +30132,8 @@ var dispatchToolSpecs = [
|
|
|
30103
30132
|
},
|
|
30104
30133
|
{
|
|
30105
30134
|
name: "dispatch_artifact",
|
|
30135
|
+
example: { issue: "DSP-1", name: "design.md", content: `# Design
|
|
30136
|
+
` },
|
|
30106
30137
|
description: "Attach a local file or inline text as an issue artifact or project document. Do not use it to edit a live document; use " + `dispatch_doc_edit instead. Exactly one of path or content is required; artifacts are limited to 25 MiB. ${OWNER_REFERENCE}`,
|
|
30107
30138
|
arguments: (z) => ({
|
|
30108
30139
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
@@ -30122,6 +30153,7 @@ var dispatchToolSpecs = [
|
|
|
30122
30153
|
},
|
|
30123
30154
|
{
|
|
30124
30155
|
name: "dispatch_read",
|
|
30156
|
+
example: { issue: "DSP-1" },
|
|
30125
30157
|
description: "Read an issue or project-document summary, targeted ask, or targeted comment reply chain. Do not use it for document " + "contents; use dispatch_doc_read instead. Supply ref, issue, or project plus artifact. " + "Every read ends with `Referenced by:` (what cites or hangs off this node, each with its dispatch:// address, " + "an excerpt, and when) and `Links:` (what it cites), so tracing provenance is one call. " + OWNER_REFERENCE,
|
|
30126
30158
|
arguments: (z) => ({
|
|
30127
30159
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
@@ -30133,6 +30165,7 @@ var dispatchToolSpecs = [
|
|
|
30133
30165
|
},
|
|
30134
30166
|
{
|
|
30135
30167
|
name: "dispatch_search",
|
|
30168
|
+
example: { query: "astrolabe" },
|
|
30136
30169
|
description: "Search every issue, document, comment, ask, and message for a keyword or phrase and get deep links. " + "Use it before creating an issue or a design document, and to find where a word was written. " + 'Websearch syntax: "quoted phrase", -excluded, OR.',
|
|
30137
30170
|
arguments: (z) => ({
|
|
30138
30171
|
query: z.string({ min: 2 }).describe("Keyword, phrase, or websearch expression; at least 2 characters."),
|
|
@@ -30142,6 +30175,7 @@ var dispatchToolSpecs = [
|
|
|
30142
30175
|
},
|
|
30143
30176
|
{
|
|
30144
30177
|
name: "dispatch_issues",
|
|
30178
|
+
example: { project: "AGENTC" },
|
|
30145
30179
|
description: "List a project's issues for a roadmap or backlog pass: every issue in one project, each carrying " + "its status, priority, parent, labels, and open-ask count, so you can see backlog shape without " + "opening every issue. Optionally filter by status, parent, label, or how recently it changed. Do " + "not use it to search by keyword or phrase; dispatch_search remains the keyword surface. Rows are " + "capped at limit (default 50, max 250), applied to the response here, not by the server.",
|
|
30146
30180
|
arguments: (z) => ({
|
|
30147
30181
|
project: z.string().describe("Project key to list issues from."),
|
|
@@ -30154,6 +30188,7 @@ var dispatchToolSpecs = [
|
|
|
30154
30188
|
},
|
|
30155
30189
|
{
|
|
30156
30190
|
name: "dispatch_architecture_sync",
|
|
30191
|
+
example: { project: "CORE" },
|
|
30157
30192
|
description: "Import a project's architecture model from its configured source repository now, instead of " + "waiting for the server's five-minute schedule. Returns the imported commit and component " + "count, or the recorded error when the model was rejected (the previous model stays up). " + "The source itself is configured by a human in Settings; 404 SOURCE_NOT_FOUND without one.",
|
|
30158
30193
|
arguments: (z) => ({
|
|
30159
30194
|
project: z.string().describe("Project key whose architecture source to sync, such as CORE.")
|
|
@@ -30162,6 +30197,7 @@ var dispatchToolSpecs = [
|
|
|
30162
30197
|
},
|
|
30163
30198
|
{
|
|
30164
30199
|
name: "dispatch_open_asks",
|
|
30200
|
+
example: {},
|
|
30165
30201
|
description: "List active unanswered asks, oldest first, with age and whose reply is needed. Omit project to " + "see only this session's own authored asks (call before saying you are waiting for human input); " + "supply project to see every open ask across that project's issues and documents, whoever authored " + "them.",
|
|
30166
30202
|
arguments: (z) => ({
|
|
30167
30203
|
project: z.string().describe("Project key; when supplied, lists every open ask in the project instead of only this session's own.").optional()
|
|
@@ -30170,6 +30206,7 @@ var dispatchToolSpecs = [
|
|
|
30170
30206
|
},
|
|
30171
30207
|
{
|
|
30172
30208
|
name: "dispatch_whoami",
|
|
30209
|
+
example: {},
|
|
30173
30210
|
description: "Who Dispatch takes this session for: {session, owner}. owner is the lowercase GitHub login of the human whose personal token you run under (the default assignee of issues you create), or null under the shared token.",
|
|
30174
30211
|
arguments: () => ({}),
|
|
30175
30212
|
strict: true
|
|
@@ -32260,9 +32297,11 @@ class ToolInputError extends Error {
|
|
|
32260
32297
|
problems;
|
|
32261
32298
|
constructor(tool, problems) {
|
|
32262
32299
|
const count = problems.length;
|
|
32300
|
+
const help = dispatchInputHelp(tool);
|
|
32263
32301
|
super([
|
|
32264
32302
|
`${tool} was not called: ${count} problem${count === 1 ? "" : "s"}`,
|
|
32265
|
-
...problems.map((problem) => `- ${problem}`)
|
|
32303
|
+
...problems.map((problem) => `- ${problem}`),
|
|
32304
|
+
...help === undefined ? [] : help.map((line) => `- ${line}`)
|
|
32266
32305
|
].join(`
|
|
32267
32306
|
`));
|
|
32268
32307
|
this.name = "ToolInputError";
|
|
@@ -32270,6 +32309,14 @@ class ToolInputError extends Error {
|
|
|
32270
32309
|
this.problems = problems;
|
|
32271
32310
|
}
|
|
32272
32311
|
}
|
|
32312
|
+
function dispatchInputHelp(tool) {
|
|
32313
|
+
const spec = dispatchToolSpecs.find((candidate) => candidate.name === tool);
|
|
32314
|
+
if (spec === undefined)
|
|
32315
|
+
return;
|
|
32316
|
+
const schema = dispatchToolSchema(spec, zodSchemaApi(exports_external), { strict: true });
|
|
32317
|
+
const allowed = Object.keys(shapeOf(schema) ?? {}).join(", ") || "none";
|
|
32318
|
+
return [`Allowed keys: ${allowed}`, `Example: ${tool}(${JSON.stringify(spec.example)})`];
|
|
32319
|
+
}
|
|
32273
32320
|
function unwrap(schema) {
|
|
32274
32321
|
let current = schema;
|
|
32275
32322
|
while (current !== undefined) {
|
|
@@ -32574,7 +32621,21 @@ function refTarget(ref, kind, id) {
|
|
|
32574
32621
|
const ownerRef = ref.owner.kind === "issue" ? dispatchIssueRef(ref.owner.issue) : dispatchDocumentRef(ref.owner.project, `${ref.artifact}`);
|
|
32575
32622
|
return dispatchChildRef(ownerRef, kind, id);
|
|
32576
32623
|
}
|
|
32577
|
-
var
|
|
32624
|
+
var canonicalUUIDPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
32625
|
+
var compactUUIDPattern = /^[0-9a-f]{32}$/i;
|
|
32626
|
+
function normalizeUUID(value) {
|
|
32627
|
+
let compact = value;
|
|
32628
|
+
if (value.slice(0, 9).toLowerCase() === "urn:uuid:") {
|
|
32629
|
+
compact = value.slice(9);
|
|
32630
|
+
} else if (value.length === 38 && value.startsWith("{") && value.endsWith("}")) {
|
|
32631
|
+
compact = value.slice(1, -1);
|
|
32632
|
+
}
|
|
32633
|
+
if (canonicalUUIDPattern.test(compact))
|
|
32634
|
+
compact = compact.replaceAll("-", "");
|
|
32635
|
+
if (!compactUUIDPattern.test(compact))
|
|
32636
|
+
return;
|
|
32637
|
+
return `${compact.slice(0, 8)}-${compact.slice(8, 12)}-${compact.slice(12, 16)}-${compact.slice(16, 20)}-${compact.slice(20)}`.toLowerCase();
|
|
32638
|
+
}
|
|
32578
32639
|
var askIdProblem = "ask must be a bare ask id or a dispatch://.../ask/<id> reference";
|
|
32579
32640
|
var commentIdProblem = "comment must be a bare comment id or a dispatch://.../comment/<id> reference";
|
|
32580
32641
|
var messageIdProblem = "in_reply_to must be a full message id (uuid) or a dispatch://KEY/message/<id> reference";
|
|
@@ -32587,12 +32648,13 @@ function messageIdOf(value) {
|
|
|
32587
32648
|
const reference = parseDispatchRef(value);
|
|
32588
32649
|
return reference?.kind === "message" ? reference.id : undefined;
|
|
32589
32650
|
})() : value;
|
|
32590
|
-
return id
|
|
32651
|
+
return id === undefined ? undefined : normalizeUUID(id);
|
|
32591
32652
|
}
|
|
32592
32653
|
var idPrefixPattern = /^[0-9a-f][0-9a-f-]{7,}$/i;
|
|
32593
32654
|
async function resolveIdPrefix(tool, kind, ref, list) {
|
|
32594
|
-
|
|
32595
|
-
|
|
32655
|
+
const fullID = normalizeUUID(ref.id);
|
|
32656
|
+
if (fullID !== undefined)
|
|
32657
|
+
return fullID;
|
|
32596
32658
|
const ownerName = ref.owner.kind === "issue" ? ref.owner.issue : `${ref.owner.project}/${ref.artifact}`;
|
|
32597
32659
|
if (!idPrefixPattern.test(ref.id)) {
|
|
32598
32660
|
throw new ToolInputError(tool, [
|
|
@@ -32824,21 +32886,53 @@ async function resolveArtifact(client, owner, artifactReference) {
|
|
|
32824
32886
|
return { owner, artifact };
|
|
32825
32887
|
const names = artifacts.filter((candidate) => candidate.name === artifactReference);
|
|
32826
32888
|
if (names.length > 1) {
|
|
32827
|
-
throw new Error(
|
|
32889
|
+
throw new Error(documentReferenceProblem(artifactReference, names, "project", true));
|
|
32890
|
+
}
|
|
32891
|
+
if (names[0] === undefined) {
|
|
32892
|
+
throw new Error(documentReferenceProblem(artifactReference, artifacts, "project"));
|
|
32828
32893
|
}
|
|
32829
|
-
if (names[0] === undefined)
|
|
32830
|
-
throw error;
|
|
32831
32894
|
return { owner, artifact: names[0] };
|
|
32832
32895
|
}
|
|
32833
32896
|
}
|
|
32834
32897
|
const issue = await client.getIssue(owner.issue);
|
|
32835
|
-
|
|
32898
|
+
let artifact;
|
|
32899
|
+
if (artifactReference === undefined || artifactReference === "spec") {
|
|
32900
|
+
artifact = issue.artifacts.find((candidate) => candidate.primary || candidate.id === issue.primary_artifact_id);
|
|
32901
|
+
} else {
|
|
32902
|
+
artifact = issue.artifacts.find((candidate) => candidate.id === artifactReference || candidate.slug === artifactReference);
|
|
32903
|
+
if (artifact === undefined) {
|
|
32904
|
+
const names = issue.artifacts.filter((candidate) => candidate.name === artifactReference);
|
|
32905
|
+
if (names.length > 1) {
|
|
32906
|
+
throw new Error(documentReferenceProblem(artifactReference, names, "issue", true));
|
|
32907
|
+
}
|
|
32908
|
+
artifact = names[0];
|
|
32909
|
+
}
|
|
32910
|
+
}
|
|
32836
32911
|
if (!artifact) {
|
|
32837
|
-
|
|
32838
|
-
throw new Error(`artifact "${artifactReference ?? "spec"}" was not found on issue ${issue.key}; artifacts: ${slugs.length === 0 ? "none" : slugs.join(", ")}`);
|
|
32912
|
+
throw new Error(documentReferenceProblem(artifactReference ?? "spec", issue.artifacts, "issue"));
|
|
32839
32913
|
}
|
|
32840
32914
|
return { owner, issue, artifact };
|
|
32841
32915
|
}
|
|
32916
|
+
var documentHintLimit = 8;
|
|
32917
|
+
function documentReferenceProblem(reference, documents, owner, ambiguous = false) {
|
|
32918
|
+
const hints = documents.slice(0, documentHintLimit).map((document) => `${document.slug} (${document.name})`);
|
|
32919
|
+
const list = hints.length === 0 ? "none" : hints.join(", ");
|
|
32920
|
+
return ambiguous ? `"${reference}" names ${documents.length} documents on this ${owner}; use a slug: ${list}` : `document "${reference}" not found by slug; this ${owner}'s documents: ${list}`;
|
|
32921
|
+
}
|
|
32922
|
+
var askHintLimit = 8;
|
|
32923
|
+
function askIDInputProblem(asks, scope) {
|
|
32924
|
+
const hints = asks.slice(0, askHintLimit).map((ask) => `${ask.id.slice(0, 8)}\u2026 ${textHead(ask.question)}`);
|
|
32925
|
+
return `ask IDs are UUIDs; use the full ask ID; ${scope} open asks: ${hints.length === 0 ? "none" : hints.join(", ")}`;
|
|
32926
|
+
}
|
|
32927
|
+
async function invalidReplyToAskProblem(client, owner, resolved) {
|
|
32928
|
+
if (owner.kind === "issue") {
|
|
32929
|
+
const issue = resolved?.issue ?? await client.getIssue(owner.issue);
|
|
32930
|
+
return askIDInputProblem(issue.open_asks, "this issue's");
|
|
32931
|
+
}
|
|
32932
|
+
if (resolved === undefined)
|
|
32933
|
+
throw new Error("project document is missing its resolved artifact");
|
|
32934
|
+
return askIDInputProblem(await client.getArtifactAsks(resolved.artifact.id, "open"), "this document's");
|
|
32935
|
+
}
|
|
32842
32936
|
function anchor(artifact, args) {
|
|
32843
32937
|
const quote = optionalString(args, "quote");
|
|
32844
32938
|
if (quote === undefined)
|
|
@@ -33442,7 +33536,13 @@ ${followsAsk(askOwner)}`,
|
|
|
33442
33536
|
const resolved = owner.kind === "project" || artifactReference === undefined ? owner.kind === "project" ? await resolveArtifact(client, owner, artifactReference) : undefined : await resolveArtifact(client, owner, artifactReference);
|
|
33443
33537
|
const anchored = resolved ? anchor(resolved.artifact, args) : undefined;
|
|
33444
33538
|
const replyTo = optionalString(args, "reply_to");
|
|
33445
|
-
const
|
|
33539
|
+
const replyToAskReference = optionalString(args, "reply_to_ask");
|
|
33540
|
+
const replyToAsk = replyToAskReference === undefined ? undefined : normalizeUUID(replyToAskReference);
|
|
33541
|
+
if (replyToAskReference !== undefined && replyToAsk === undefined) {
|
|
33542
|
+
throw new ToolInputError(input.tool, [
|
|
33543
|
+
await invalidReplyToAskProblem(client, owner, resolved)
|
|
33544
|
+
]);
|
|
33545
|
+
}
|
|
33446
33546
|
const requestedTurn = optionalString(args, "turn");
|
|
33447
33547
|
const commentInput = {
|
|
33448
33548
|
body: stringArg(args, "body"),
|
package/dist/legion.js
CHANGED
|
@@ -16145,7 +16145,7 @@ import { logger } from "@oh-my-pi/pi-utils";
|
|
|
16145
16145
|
// package.json
|
|
16146
16146
|
var package_default = {
|
|
16147
16147
|
name: "@sjawhar/pi-legion-envoy",
|
|
16148
|
-
version: "1.51.
|
|
16148
|
+
version: "1.51.2",
|
|
16149
16149
|
type: "module",
|
|
16150
16150
|
omp: {
|
|
16151
16151
|
extensions: [
|
|
@@ -29227,6 +29227,7 @@ var DOC_EDIT_OPS = ["replace", "delete", "insert", "retype", "move"];
|
|
|
29227
29227
|
var dispatchToolSpecs = [
|
|
29228
29228
|
{
|
|
29229
29229
|
name: "dispatch_issue",
|
|
29230
|
+
example: { project: "DSP", title: "Native workspace" },
|
|
29230
29231
|
description: "Create a native Dispatch issue for newly tracked work. Search first with dispatch_search; if potentially duplicate issues exist, this returns 409 POSSIBLE_DUPLICATE unless force is true after reading them. " + `Do not use it when an existing issue already covers the work; read or update that issue instead. ${ISSUE_REFERENCE}`,
|
|
29231
29232
|
arguments: (z) => ({
|
|
29232
29233
|
project: z.string().describe("Project key for the new issue."),
|
|
@@ -29243,6 +29244,7 @@ var dispatchToolSpecs = [
|
|
|
29243
29244
|
},
|
|
29244
29245
|
{
|
|
29245
29246
|
name: "dispatch_issue_update",
|
|
29247
|
+
example: { issue: "DSP-1", status: "in_progress" },
|
|
29246
29248
|
description: "Update an existing issue: move its lifecycle status, retitle it, replace its labels, link a URL " + "(the pull request that delivers it, a run, a document), set its route, set or clear its parent, " + "or attach it to architecture components. Status is one of " + `${ISSUE_STATUSES.join(", ")}; outside Legion, move it yourself as the work advances; inside ` + "Legion the daemon moves it. external_links are " + "merged into the issue's existing links by URL, so linking the pull request you just opened " + "keeps every earlier link. components replaces the issue's own attachment and is allowed on a " + "closed issue. Priority is the human's and is not settable here. At least one " + `field besides issue is required. ${ISSUE_REFERENCE}`,
|
|
29247
29249
|
arguments: (z) => ({
|
|
29248
29250
|
issue: z.string().describe(ISSUE_REFERENCE),
|
|
@@ -29265,6 +29267,7 @@ var dispatchToolSpecs = [
|
|
|
29265
29267
|
},
|
|
29266
29268
|
{
|
|
29267
29269
|
name: "dispatch_ask",
|
|
29270
|
+
example: { issue: "DSP-1", question: "Ship this?" },
|
|
29268
29271
|
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. A to-do a human must complete is a question phrased as that to-do, with the options you want (for example Done / Can't). " + "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". A quote anchor is pinned to its block. Question is at most ${ASK_QUESTION_MAX} ` + `characters and has at most 8 options. ${OWNER_REFERENCE}`,
|
|
29269
29272
|
arguments: (z) => ({
|
|
29270
29273
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
@@ -29288,6 +29291,10 @@ var dispatchToolSpecs = [
|
|
|
29288
29291
|
},
|
|
29289
29292
|
{
|
|
29290
29293
|
name: "dispatch_edit_ask",
|
|
29294
|
+
example: {
|
|
29295
|
+
ask: "01234567-0000-4000-8000-000000000001",
|
|
29296
|
+
question: "Ship the revised plan?"
|
|
29297
|
+
},
|
|
29291
29298
|
description: "Edit an open question in place. Use it to correct or refine the same decision; retract the " + "old ask and open a new one when the decision itself changes. Previous text remains in the " + "event log. Only the asking session can edit it; answered or resolved asks cannot be edited.",
|
|
29292
29299
|
arguments: (z) => ({
|
|
29293
29300
|
ask: z.string().describe("Ask id to edit."),
|
|
@@ -29309,6 +29316,11 @@ var dispatchToolSpecs = [
|
|
|
29309
29316
|
},
|
|
29310
29317
|
{
|
|
29311
29318
|
name: "dispatch_resolve_ask",
|
|
29319
|
+
example: {
|
|
29320
|
+
ask: "01234567-0000-4000-8000-000000000001",
|
|
29321
|
+
kind: "retracted",
|
|
29322
|
+
reason: "A newer question supersedes this one."
|
|
29323
|
+
},
|
|
29312
29324
|
description: "Retract an open question that is moot or resolve one after finding the answer. This closes the question without answering it.",
|
|
29313
29325
|
arguments: (z) => ({
|
|
29314
29326
|
ask: z.string().describe("Ask id to close."),
|
|
@@ -29318,6 +29330,7 @@ var dispatchToolSpecs = [
|
|
|
29318
29330
|
},
|
|
29319
29331
|
{
|
|
29320
29332
|
name: "dispatch_resolve_comment",
|
|
29333
|
+
example: { comment: "01234567-0000-4000-8000-000000000001" },
|
|
29321
29334
|
description: "Resolve a review comment thread once it has been addressed - typically your own comment " + "after the document was fixed. Any session or human may resolve any open comment on an " + "open issue or project document; reopening a resolved comment is human-only (the dashboard). " + "Not for asks: use dispatch_resolve_ask.",
|
|
29322
29335
|
arguments: (z) => ({
|
|
29323
29336
|
comment: z.string().describe("Comment id (uuid), or a dispatch://KEY/comment/<id> or " + "dispatch://PROJECT/artifact/<slug>/comment/<id> reference; a reference accepts an " + "8+ character id prefix that is unique on its owner.")
|
|
@@ -29326,6 +29339,7 @@ var dispatchToolSpecs = [
|
|
|
29326
29339
|
},
|
|
29327
29340
|
{
|
|
29328
29341
|
name: "dispatch_follow",
|
|
29342
|
+
example: { ask: "01234567-0000-4000-8000-000000000001", action: "follow" },
|
|
29329
29343
|
description: "Follow or unfollow an ask. Every session that opens or replies to an ask follows it: its answer, " + "edits, resolution, and replies reach that session directly. Unfollow to stop; follow to rejoin or " + "to hear an ask you never wrote to. Whole-issue subscription is separate: envoy_subscribe " + "notifications.dispatch.issue.<KEY>.>",
|
|
29330
29344
|
arguments: (z) => ({
|
|
29331
29345
|
ask: z.string().describe("Full ask id (uuid)."),
|
|
@@ -29335,6 +29349,7 @@ var dispatchToolSpecs = [
|
|
|
29335
29349
|
},
|
|
29336
29350
|
{
|
|
29337
29351
|
name: "dispatch_comment",
|
|
29352
|
+
example: { issue: "DSP-1", body: "Looks good." },
|
|
29338
29353
|
description: "Add review feedback to an issue or project document quote, or reply to a question asked with dispatch_ask. " + "Do not use it for an exact replacement; use " + `dispatch_suggest instead. A quote anchor is pinned to its block. Body is at most 2,000 characters. ${OWNER_REFERENCE}`,
|
|
29339
29354
|
arguments: (z) => ({
|
|
29340
29355
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
@@ -29352,6 +29367,12 @@ var dispatchToolSpecs = [
|
|
|
29352
29367
|
},
|
|
29353
29368
|
{
|
|
29354
29369
|
name: "dispatch_suggest",
|
|
29370
|
+
example: {
|
|
29371
|
+
issue: "DSP-1",
|
|
29372
|
+
artifact: "spec",
|
|
29373
|
+
quote: "old wording",
|
|
29374
|
+
replace_with: "new wording"
|
|
29375
|
+
},
|
|
29355
29376
|
description: "Propose an exact replacement for quoted document text. Do not use it for general feedback; use " + `dispatch_comment instead. Optional explanation is at most 2,000 characters. ${OWNER_REFERENCE}`,
|
|
29356
29377
|
arguments: (z) => ({
|
|
29357
29378
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
@@ -29367,6 +29388,7 @@ var dispatchToolSpecs = [
|
|
|
29367
29388
|
},
|
|
29368
29389
|
{
|
|
29369
29390
|
name: "dispatch_message",
|
|
29391
|
+
example: { issue: "DSP-1", body: "Implementation started." },
|
|
29370
29392
|
description: "Post a note humans must read now: a reply to a human's message, a deliverable that landed, or a blocker only " + "they can clear. Never progress or status updates - Dispatch is a high-signal record, not a log. Not a decision " + `(dispatch_ask) or document feedback (dispatch_comment). Body is at most 2,000 characters. ${ISSUE_REFERENCE}`,
|
|
29371
29393
|
arguments: (z) => ({
|
|
29372
29394
|
issue: z.string().describe(ISSUE_REFERENCE),
|
|
@@ -29376,6 +29398,11 @@ var dispatchToolSpecs = [
|
|
|
29376
29398
|
},
|
|
29377
29399
|
{
|
|
29378
29400
|
name: "dispatch_doc_edit",
|
|
29401
|
+
example: {
|
|
29402
|
+
issue: "DSP-1",
|
|
29403
|
+
artifact: "spec",
|
|
29404
|
+
ops: [{ op: "replace", find: "old", with: "new" }]
|
|
29405
|
+
},
|
|
29379
29406
|
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, and delete or move a whole block by its id. " + "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; a leading '# ' matches a heading. replace is inline: with is the new text of the matched span, so a leading list or heading marker stays literal text. " + "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. " + 'Insert and move anchors also accept "start", "end", "heading:<exact heading text>", and "block:<id>"; block ids are the #id of a typed block or a row of GET /api/v1/artifacts/{id}/blocks. ' + `The spec (or any document) holds requirements, design, and decisions - never progress, status, or timestamps. ${OWNER_REFERENCE} ${SPEC_WRITING_GUIDANCE}`,
|
|
29380
29407
|
arguments: (z) => ({
|
|
29381
29408
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
@@ -29400,6 +29427,7 @@ var dispatchToolSpecs = [
|
|
|
29400
29427
|
},
|
|
29401
29428
|
{
|
|
29402
29429
|
name: "dispatch_doc_read",
|
|
29430
|
+
example: { issue: "DSP-1" },
|
|
29403
29431
|
description: "Read a live document or a named document 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. " + OWNER_REFERENCE,
|
|
29404
29432
|
arguments: (z) => ({
|
|
29405
29433
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
@@ -29412,6 +29440,7 @@ var dispatchToolSpecs = [
|
|
|
29412
29440
|
},
|
|
29413
29441
|
{
|
|
29414
29442
|
name: "dispatch_request_approval",
|
|
29443
|
+
example: { issue: "DSP-1" },
|
|
29415
29444
|
description: "Ask a human to approve a document at its current version - the exception path for a spec " + "that departs from what was settled or proposes children, not a step for every issue. Opens an " + "approval ask (Approve / Request changes) in the human's Inbox; the answer pins a review to the " + "document version and arrives as artifact.approved or artifact.changes_requested. A later edit " + "makes an approval stale; request again for the new version. Idempotent while a request is open. " + OWNER_REFERENCE,
|
|
29416
29445
|
arguments: (z) => ({
|
|
29417
29446
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
@@ -29422,6 +29451,8 @@ var dispatchToolSpecs = [
|
|
|
29422
29451
|
},
|
|
29423
29452
|
{
|
|
29424
29453
|
name: "dispatch_artifact",
|
|
29454
|
+
example: { issue: "DSP-1", name: "design.md", content: `# Design
|
|
29455
|
+
` },
|
|
29425
29456
|
description: "Attach a local file or inline text as an issue artifact or project document. Do not use it to edit a live document; use " + `dispatch_doc_edit instead. Exactly one of path or content is required; artifacts are limited to 25 MiB. ${OWNER_REFERENCE}`,
|
|
29426
29457
|
arguments: (z) => ({
|
|
29427
29458
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
@@ -29441,6 +29472,7 @@ var dispatchToolSpecs = [
|
|
|
29441
29472
|
},
|
|
29442
29473
|
{
|
|
29443
29474
|
name: "dispatch_read",
|
|
29475
|
+
example: { issue: "DSP-1" },
|
|
29444
29476
|
description: "Read an issue or project-document summary, targeted ask, or targeted comment reply chain. Do not use it for document " + "contents; use dispatch_doc_read instead. Supply ref, issue, or project plus artifact. " + "Every read ends with `Referenced by:` (what cites or hangs off this node, each with its dispatch:// address, " + "an excerpt, and when) and `Links:` (what it cites), so tracing provenance is one call. " + OWNER_REFERENCE,
|
|
29445
29477
|
arguments: (z) => ({
|
|
29446
29478
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
@@ -29452,6 +29484,7 @@ var dispatchToolSpecs = [
|
|
|
29452
29484
|
},
|
|
29453
29485
|
{
|
|
29454
29486
|
name: "dispatch_search",
|
|
29487
|
+
example: { query: "astrolabe" },
|
|
29455
29488
|
description: "Search every issue, document, comment, ask, and message for a keyword or phrase and get deep links. " + "Use it before creating an issue or a design document, and to find where a word was written. " + 'Websearch syntax: "quoted phrase", -excluded, OR.',
|
|
29456
29489
|
arguments: (z) => ({
|
|
29457
29490
|
query: z.string({ min: 2 }).describe("Keyword, phrase, or websearch expression; at least 2 characters."),
|
|
@@ -29461,6 +29494,7 @@ var dispatchToolSpecs = [
|
|
|
29461
29494
|
},
|
|
29462
29495
|
{
|
|
29463
29496
|
name: "dispatch_issues",
|
|
29497
|
+
example: { project: "AGENTC" },
|
|
29464
29498
|
description: "List a project's issues for a roadmap or backlog pass: every issue in one project, each carrying " + "its status, priority, parent, labels, and open-ask count, so you can see backlog shape without " + "opening every issue. Optionally filter by status, parent, label, or how recently it changed. Do " + "not use it to search by keyword or phrase; dispatch_search remains the keyword surface. Rows are " + "capped at limit (default 50, max 250), applied to the response here, not by the server.",
|
|
29465
29499
|
arguments: (z) => ({
|
|
29466
29500
|
project: z.string().describe("Project key to list issues from."),
|
|
@@ -29473,6 +29507,7 @@ var dispatchToolSpecs = [
|
|
|
29473
29507
|
},
|
|
29474
29508
|
{
|
|
29475
29509
|
name: "dispatch_architecture_sync",
|
|
29510
|
+
example: { project: "CORE" },
|
|
29476
29511
|
description: "Import a project's architecture model from its configured source repository now, instead of " + "waiting for the server's five-minute schedule. Returns the imported commit and component " + "count, or the recorded error when the model was rejected (the previous model stays up). " + "The source itself is configured by a human in Settings; 404 SOURCE_NOT_FOUND without one.",
|
|
29477
29512
|
arguments: (z) => ({
|
|
29478
29513
|
project: z.string().describe("Project key whose architecture source to sync, such as CORE.")
|
|
@@ -29481,6 +29516,7 @@ var dispatchToolSpecs = [
|
|
|
29481
29516
|
},
|
|
29482
29517
|
{
|
|
29483
29518
|
name: "dispatch_open_asks",
|
|
29519
|
+
example: {},
|
|
29484
29520
|
description: "List active unanswered asks, oldest first, with age and whose reply is needed. Omit project to " + "see only this session's own authored asks (call before saying you are waiting for human input); " + "supply project to see every open ask across that project's issues and documents, whoever authored " + "them.",
|
|
29485
29521
|
arguments: (z) => ({
|
|
29486
29522
|
project: z.string().describe("Project key; when supplied, lists every open ask in the project instead of only this session's own.").optional()
|
|
@@ -29489,6 +29525,7 @@ var dispatchToolSpecs = [
|
|
|
29489
29525
|
},
|
|
29490
29526
|
{
|
|
29491
29527
|
name: "dispatch_whoami",
|
|
29528
|
+
example: {},
|
|
29492
29529
|
description: "Who Dispatch takes this session for: {session, owner}. owner is the lowercase GitHub login of the human whose personal token you run under (the default assignee of issues you create), or null under the shared token.",
|
|
29493
29530
|
arguments: () => ({}),
|
|
29494
29531
|
strict: true
|