@sjawhar/pi-legion-envoy 1.51.0 → 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 +118 -12
- package/dist/legion.js +44 -1
- package/package.json +1 -1
package/dist/envoy.js
CHANGED
|
@@ -29696,6 +29696,12 @@ var askEventPayloadFields = {
|
|
|
29696
29696
|
mark_id: string2().optional(),
|
|
29697
29697
|
block_id: string2().nullable().optional()
|
|
29698
29698
|
}).passthrough().nullish(),
|
|
29699
|
+
anchor_artifact: object({
|
|
29700
|
+
project: string2().optional(),
|
|
29701
|
+
slug: string2().optional(),
|
|
29702
|
+
name: string2().optional(),
|
|
29703
|
+
primary: boolean2().optional()
|
|
29704
|
+
}).optional(),
|
|
29699
29705
|
resolution: object({
|
|
29700
29706
|
kind: _enum2(["retracted", "resolved"]).optional(),
|
|
29701
29707
|
reason: string2().optional(),
|
|
@@ -29902,6 +29908,7 @@ var DOC_EDIT_OPS = ["replace", "delete", "insert", "retype", "move"];
|
|
|
29902
29908
|
var dispatchToolSpecs = [
|
|
29903
29909
|
{
|
|
29904
29910
|
name: "dispatch_issue",
|
|
29911
|
+
example: { project: "DSP", title: "Native workspace" },
|
|
29905
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}`,
|
|
29906
29913
|
arguments: (z) => ({
|
|
29907
29914
|
project: z.string().describe("Project key for the new issue."),
|
|
@@ -29918,6 +29925,7 @@ var dispatchToolSpecs = [
|
|
|
29918
29925
|
},
|
|
29919
29926
|
{
|
|
29920
29927
|
name: "dispatch_issue_update",
|
|
29928
|
+
example: { issue: "DSP-1", status: "in_progress" },
|
|
29921
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}`,
|
|
29922
29930
|
arguments: (z) => ({
|
|
29923
29931
|
issue: z.string().describe(ISSUE_REFERENCE),
|
|
@@ -29940,6 +29948,7 @@ var dispatchToolSpecs = [
|
|
|
29940
29948
|
},
|
|
29941
29949
|
{
|
|
29942
29950
|
name: "dispatch_ask",
|
|
29951
|
+
example: { issue: "DSP-1", question: "Ship this?" },
|
|
29943
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}`,
|
|
29944
29953
|
arguments: (z) => ({
|
|
29945
29954
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
@@ -29963,6 +29972,10 @@ var dispatchToolSpecs = [
|
|
|
29963
29972
|
},
|
|
29964
29973
|
{
|
|
29965
29974
|
name: "dispatch_edit_ask",
|
|
29975
|
+
example: {
|
|
29976
|
+
ask: "01234567-0000-4000-8000-000000000001",
|
|
29977
|
+
question: "Ship the revised plan?"
|
|
29978
|
+
},
|
|
29966
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.",
|
|
29967
29980
|
arguments: (z) => ({
|
|
29968
29981
|
ask: z.string().describe("Ask id to edit."),
|
|
@@ -29984,6 +29997,11 @@ var dispatchToolSpecs = [
|
|
|
29984
29997
|
},
|
|
29985
29998
|
{
|
|
29986
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
|
+
},
|
|
29987
30005
|
description: "Retract an open question that is moot or resolve one after finding the answer. This closes the question without answering it.",
|
|
29988
30006
|
arguments: (z) => ({
|
|
29989
30007
|
ask: z.string().describe("Ask id to close."),
|
|
@@ -29993,6 +30011,7 @@ var dispatchToolSpecs = [
|
|
|
29993
30011
|
},
|
|
29994
30012
|
{
|
|
29995
30013
|
name: "dispatch_resolve_comment",
|
|
30014
|
+
example: { comment: "01234567-0000-4000-8000-000000000001" },
|
|
29996
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.",
|
|
29997
30016
|
arguments: (z) => ({
|
|
29998
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.")
|
|
@@ -30001,6 +30020,7 @@ var dispatchToolSpecs = [
|
|
|
30001
30020
|
},
|
|
30002
30021
|
{
|
|
30003
30022
|
name: "dispatch_follow",
|
|
30023
|
+
example: { ask: "01234567-0000-4000-8000-000000000001", action: "follow" },
|
|
30004
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>.>",
|
|
30005
30025
|
arguments: (z) => ({
|
|
30006
30026
|
ask: z.string().describe("Full ask id (uuid)."),
|
|
@@ -30010,6 +30030,7 @@ var dispatchToolSpecs = [
|
|
|
30010
30030
|
},
|
|
30011
30031
|
{
|
|
30012
30032
|
name: "dispatch_comment",
|
|
30033
|
+
example: { issue: "DSP-1", body: "Looks good." },
|
|
30013
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}`,
|
|
30014
30035
|
arguments: (z) => ({
|
|
30015
30036
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
@@ -30027,6 +30048,12 @@ var dispatchToolSpecs = [
|
|
|
30027
30048
|
},
|
|
30028
30049
|
{
|
|
30029
30050
|
name: "dispatch_suggest",
|
|
30051
|
+
example: {
|
|
30052
|
+
issue: "DSP-1",
|
|
30053
|
+
artifact: "spec",
|
|
30054
|
+
quote: "old wording",
|
|
30055
|
+
replace_with: "new wording"
|
|
30056
|
+
},
|
|
30030
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}`,
|
|
30031
30058
|
arguments: (z) => ({
|
|
30032
30059
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
@@ -30042,6 +30069,7 @@ var dispatchToolSpecs = [
|
|
|
30042
30069
|
},
|
|
30043
30070
|
{
|
|
30044
30071
|
name: "dispatch_message",
|
|
30072
|
+
example: { issue: "DSP-1", body: "Implementation started." },
|
|
30045
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}`,
|
|
30046
30074
|
arguments: (z) => ({
|
|
30047
30075
|
issue: z.string().describe(ISSUE_REFERENCE),
|
|
@@ -30051,6 +30079,11 @@ var dispatchToolSpecs = [
|
|
|
30051
30079
|
},
|
|
30052
30080
|
{
|
|
30053
30081
|
name: "dispatch_doc_edit",
|
|
30082
|
+
example: {
|
|
30083
|
+
issue: "DSP-1",
|
|
30084
|
+
artifact: "spec",
|
|
30085
|
+
ops: [{ op: "replace", find: "old", with: "new" }]
|
|
30086
|
+
},
|
|
30054
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}`,
|
|
30055
30088
|
arguments: (z) => ({
|
|
30056
30089
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
@@ -30075,6 +30108,7 @@ var dispatchToolSpecs = [
|
|
|
30075
30108
|
},
|
|
30076
30109
|
{
|
|
30077
30110
|
name: "dispatch_doc_read",
|
|
30111
|
+
example: { issue: "DSP-1" },
|
|
30078
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,
|
|
30079
30113
|
arguments: (z) => ({
|
|
30080
30114
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
@@ -30087,6 +30121,7 @@ var dispatchToolSpecs = [
|
|
|
30087
30121
|
},
|
|
30088
30122
|
{
|
|
30089
30123
|
name: "dispatch_request_approval",
|
|
30124
|
+
example: { issue: "DSP-1" },
|
|
30090
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,
|
|
30091
30126
|
arguments: (z) => ({
|
|
30092
30127
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
@@ -30097,6 +30132,8 @@ var dispatchToolSpecs = [
|
|
|
30097
30132
|
},
|
|
30098
30133
|
{
|
|
30099
30134
|
name: "dispatch_artifact",
|
|
30135
|
+
example: { issue: "DSP-1", name: "design.md", content: `# Design
|
|
30136
|
+
` },
|
|
30100
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}`,
|
|
30101
30138
|
arguments: (z) => ({
|
|
30102
30139
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
@@ -30116,6 +30153,7 @@ var dispatchToolSpecs = [
|
|
|
30116
30153
|
},
|
|
30117
30154
|
{
|
|
30118
30155
|
name: "dispatch_read",
|
|
30156
|
+
example: { issue: "DSP-1" },
|
|
30119
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,
|
|
30120
30158
|
arguments: (z) => ({
|
|
30121
30159
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
@@ -30127,6 +30165,7 @@ var dispatchToolSpecs = [
|
|
|
30127
30165
|
},
|
|
30128
30166
|
{
|
|
30129
30167
|
name: "dispatch_search",
|
|
30168
|
+
example: { query: "astrolabe" },
|
|
30130
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.',
|
|
30131
30170
|
arguments: (z) => ({
|
|
30132
30171
|
query: z.string({ min: 2 }).describe("Keyword, phrase, or websearch expression; at least 2 characters."),
|
|
@@ -30136,6 +30175,7 @@ var dispatchToolSpecs = [
|
|
|
30136
30175
|
},
|
|
30137
30176
|
{
|
|
30138
30177
|
name: "dispatch_issues",
|
|
30178
|
+
example: { project: "AGENTC" },
|
|
30139
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.",
|
|
30140
30180
|
arguments: (z) => ({
|
|
30141
30181
|
project: z.string().describe("Project key to list issues from."),
|
|
@@ -30148,6 +30188,7 @@ var dispatchToolSpecs = [
|
|
|
30148
30188
|
},
|
|
30149
30189
|
{
|
|
30150
30190
|
name: "dispatch_architecture_sync",
|
|
30191
|
+
example: { project: "CORE" },
|
|
30151
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.",
|
|
30152
30193
|
arguments: (z) => ({
|
|
30153
30194
|
project: z.string().describe("Project key whose architecture source to sync, such as CORE.")
|
|
@@ -30156,6 +30197,7 @@ var dispatchToolSpecs = [
|
|
|
30156
30197
|
},
|
|
30157
30198
|
{
|
|
30158
30199
|
name: "dispatch_open_asks",
|
|
30200
|
+
example: {},
|
|
30159
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.",
|
|
30160
30202
|
arguments: (z) => ({
|
|
30161
30203
|
project: z.string().describe("Project key; when supplied, lists every open ask in the project instead of only this session's own.").optional()
|
|
@@ -30164,6 +30206,7 @@ var dispatchToolSpecs = [
|
|
|
30164
30206
|
},
|
|
30165
30207
|
{
|
|
30166
30208
|
name: "dispatch_whoami",
|
|
30209
|
+
example: {},
|
|
30167
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.",
|
|
30168
30211
|
arguments: () => ({}),
|
|
30169
30212
|
strict: true
|
|
@@ -32254,9 +32297,11 @@ class ToolInputError extends Error {
|
|
|
32254
32297
|
problems;
|
|
32255
32298
|
constructor(tool, problems) {
|
|
32256
32299
|
const count = problems.length;
|
|
32300
|
+
const help = dispatchInputHelp(tool);
|
|
32257
32301
|
super([
|
|
32258
32302
|
`${tool} was not called: ${count} problem${count === 1 ? "" : "s"}`,
|
|
32259
|
-
...problems.map((problem) => `- ${problem}`)
|
|
32303
|
+
...problems.map((problem) => `- ${problem}`),
|
|
32304
|
+
...help === undefined ? [] : help.map((line) => `- ${line}`)
|
|
32260
32305
|
].join(`
|
|
32261
32306
|
`));
|
|
32262
32307
|
this.name = "ToolInputError";
|
|
@@ -32264,6 +32309,14 @@ class ToolInputError extends Error {
|
|
|
32264
32309
|
this.problems = problems;
|
|
32265
32310
|
}
|
|
32266
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
|
+
}
|
|
32267
32320
|
function unwrap(schema) {
|
|
32268
32321
|
let current = schema;
|
|
32269
32322
|
while (current !== undefined) {
|
|
@@ -32568,7 +32621,21 @@ function refTarget(ref, kind, id) {
|
|
|
32568
32621
|
const ownerRef = ref.owner.kind === "issue" ? dispatchIssueRef(ref.owner.issue) : dispatchDocumentRef(ref.owner.project, `${ref.artifact}`);
|
|
32569
32622
|
return dispatchChildRef(ownerRef, kind, id);
|
|
32570
32623
|
}
|
|
32571
|
-
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
|
+
}
|
|
32572
32639
|
var askIdProblem = "ask must be a bare ask id or a dispatch://.../ask/<id> reference";
|
|
32573
32640
|
var commentIdProblem = "comment must be a bare comment id or a dispatch://.../comment/<id> reference";
|
|
32574
32641
|
var messageIdProblem = "in_reply_to must be a full message id (uuid) or a dispatch://KEY/message/<id> reference";
|
|
@@ -32581,12 +32648,13 @@ function messageIdOf(value) {
|
|
|
32581
32648
|
const reference = parseDispatchRef(value);
|
|
32582
32649
|
return reference?.kind === "message" ? reference.id : undefined;
|
|
32583
32650
|
})() : value;
|
|
32584
|
-
return id
|
|
32651
|
+
return id === undefined ? undefined : normalizeUUID(id);
|
|
32585
32652
|
}
|
|
32586
32653
|
var idPrefixPattern = /^[0-9a-f][0-9a-f-]{7,}$/i;
|
|
32587
32654
|
async function resolveIdPrefix(tool, kind, ref, list) {
|
|
32588
|
-
|
|
32589
|
-
|
|
32655
|
+
const fullID = normalizeUUID(ref.id);
|
|
32656
|
+
if (fullID !== undefined)
|
|
32657
|
+
return fullID;
|
|
32590
32658
|
const ownerName = ref.owner.kind === "issue" ? ref.owner.issue : `${ref.owner.project}/${ref.artifact}`;
|
|
32591
32659
|
if (!idPrefixPattern.test(ref.id)) {
|
|
32592
32660
|
throw new ToolInputError(tool, [
|
|
@@ -32818,21 +32886,53 @@ async function resolveArtifact(client, owner, artifactReference) {
|
|
|
32818
32886
|
return { owner, artifact };
|
|
32819
32887
|
const names = artifacts.filter((candidate) => candidate.name === artifactReference);
|
|
32820
32888
|
if (names.length > 1) {
|
|
32821
|
-
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"));
|
|
32822
32893
|
}
|
|
32823
|
-
if (names[0] === undefined)
|
|
32824
|
-
throw error;
|
|
32825
32894
|
return { owner, artifact: names[0] };
|
|
32826
32895
|
}
|
|
32827
32896
|
}
|
|
32828
32897
|
const issue = await client.getIssue(owner.issue);
|
|
32829
|
-
|
|
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
|
+
}
|
|
32830
32911
|
if (!artifact) {
|
|
32831
|
-
|
|
32832
|
-
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"));
|
|
32833
32913
|
}
|
|
32834
32914
|
return { owner, issue, artifact };
|
|
32835
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
|
+
}
|
|
32836
32936
|
function anchor(artifact, args) {
|
|
32837
32937
|
const quote = optionalString(args, "quote");
|
|
32838
32938
|
if (quote === undefined)
|
|
@@ -33436,7 +33536,13 @@ ${followsAsk(askOwner)}`,
|
|
|
33436
33536
|
const resolved = owner.kind === "project" || artifactReference === undefined ? owner.kind === "project" ? await resolveArtifact(client, owner, artifactReference) : undefined : await resolveArtifact(client, owner, artifactReference);
|
|
33437
33537
|
const anchored = resolved ? anchor(resolved.artifact, args) : undefined;
|
|
33438
33538
|
const replyTo = optionalString(args, "reply_to");
|
|
33439
|
-
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
|
+
}
|
|
33440
33546
|
const requestedTurn = optionalString(args, "turn");
|
|
33441
33547
|
const commentInput = {
|
|
33442
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: [
|
|
@@ -29015,6 +29015,12 @@ var askEventPayloadFields = {
|
|
|
29015
29015
|
mark_id: string2().optional(),
|
|
29016
29016
|
block_id: string2().nullable().optional()
|
|
29017
29017
|
}).passthrough().nullish(),
|
|
29018
|
+
anchor_artifact: object({
|
|
29019
|
+
project: string2().optional(),
|
|
29020
|
+
slug: string2().optional(),
|
|
29021
|
+
name: string2().optional(),
|
|
29022
|
+
primary: boolean2().optional()
|
|
29023
|
+
}).optional(),
|
|
29018
29024
|
resolution: object({
|
|
29019
29025
|
kind: _enum2(["retracted", "resolved"]).optional(),
|
|
29020
29026
|
reason: string2().optional(),
|
|
@@ -29221,6 +29227,7 @@ var DOC_EDIT_OPS = ["replace", "delete", "insert", "retype", "move"];
|
|
|
29221
29227
|
var dispatchToolSpecs = [
|
|
29222
29228
|
{
|
|
29223
29229
|
name: "dispatch_issue",
|
|
29230
|
+
example: { project: "DSP", title: "Native workspace" },
|
|
29224
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}`,
|
|
29225
29232
|
arguments: (z) => ({
|
|
29226
29233
|
project: z.string().describe("Project key for the new issue."),
|
|
@@ -29237,6 +29244,7 @@ var dispatchToolSpecs = [
|
|
|
29237
29244
|
},
|
|
29238
29245
|
{
|
|
29239
29246
|
name: "dispatch_issue_update",
|
|
29247
|
+
example: { issue: "DSP-1", status: "in_progress" },
|
|
29240
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}`,
|
|
29241
29249
|
arguments: (z) => ({
|
|
29242
29250
|
issue: z.string().describe(ISSUE_REFERENCE),
|
|
@@ -29259,6 +29267,7 @@ var dispatchToolSpecs = [
|
|
|
29259
29267
|
},
|
|
29260
29268
|
{
|
|
29261
29269
|
name: "dispatch_ask",
|
|
29270
|
+
example: { issue: "DSP-1", question: "Ship this?" },
|
|
29262
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}`,
|
|
29263
29272
|
arguments: (z) => ({
|
|
29264
29273
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
@@ -29282,6 +29291,10 @@ var dispatchToolSpecs = [
|
|
|
29282
29291
|
},
|
|
29283
29292
|
{
|
|
29284
29293
|
name: "dispatch_edit_ask",
|
|
29294
|
+
example: {
|
|
29295
|
+
ask: "01234567-0000-4000-8000-000000000001",
|
|
29296
|
+
question: "Ship the revised plan?"
|
|
29297
|
+
},
|
|
29285
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.",
|
|
29286
29299
|
arguments: (z) => ({
|
|
29287
29300
|
ask: z.string().describe("Ask id to edit."),
|
|
@@ -29303,6 +29316,11 @@ var dispatchToolSpecs = [
|
|
|
29303
29316
|
},
|
|
29304
29317
|
{
|
|
29305
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
|
+
},
|
|
29306
29324
|
description: "Retract an open question that is moot or resolve one after finding the answer. This closes the question without answering it.",
|
|
29307
29325
|
arguments: (z) => ({
|
|
29308
29326
|
ask: z.string().describe("Ask id to close."),
|
|
@@ -29312,6 +29330,7 @@ var dispatchToolSpecs = [
|
|
|
29312
29330
|
},
|
|
29313
29331
|
{
|
|
29314
29332
|
name: "dispatch_resolve_comment",
|
|
29333
|
+
example: { comment: "01234567-0000-4000-8000-000000000001" },
|
|
29315
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.",
|
|
29316
29335
|
arguments: (z) => ({
|
|
29317
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.")
|
|
@@ -29320,6 +29339,7 @@ var dispatchToolSpecs = [
|
|
|
29320
29339
|
},
|
|
29321
29340
|
{
|
|
29322
29341
|
name: "dispatch_follow",
|
|
29342
|
+
example: { ask: "01234567-0000-4000-8000-000000000001", action: "follow" },
|
|
29323
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>.>",
|
|
29324
29344
|
arguments: (z) => ({
|
|
29325
29345
|
ask: z.string().describe("Full ask id (uuid)."),
|
|
@@ -29329,6 +29349,7 @@ var dispatchToolSpecs = [
|
|
|
29329
29349
|
},
|
|
29330
29350
|
{
|
|
29331
29351
|
name: "dispatch_comment",
|
|
29352
|
+
example: { issue: "DSP-1", body: "Looks good." },
|
|
29332
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}`,
|
|
29333
29354
|
arguments: (z) => ({
|
|
29334
29355
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
@@ -29346,6 +29367,12 @@ var dispatchToolSpecs = [
|
|
|
29346
29367
|
},
|
|
29347
29368
|
{
|
|
29348
29369
|
name: "dispatch_suggest",
|
|
29370
|
+
example: {
|
|
29371
|
+
issue: "DSP-1",
|
|
29372
|
+
artifact: "spec",
|
|
29373
|
+
quote: "old wording",
|
|
29374
|
+
replace_with: "new wording"
|
|
29375
|
+
},
|
|
29349
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}`,
|
|
29350
29377
|
arguments: (z) => ({
|
|
29351
29378
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
@@ -29361,6 +29388,7 @@ var dispatchToolSpecs = [
|
|
|
29361
29388
|
},
|
|
29362
29389
|
{
|
|
29363
29390
|
name: "dispatch_message",
|
|
29391
|
+
example: { issue: "DSP-1", body: "Implementation started." },
|
|
29364
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}`,
|
|
29365
29393
|
arguments: (z) => ({
|
|
29366
29394
|
issue: z.string().describe(ISSUE_REFERENCE),
|
|
@@ -29370,6 +29398,11 @@ var dispatchToolSpecs = [
|
|
|
29370
29398
|
},
|
|
29371
29399
|
{
|
|
29372
29400
|
name: "dispatch_doc_edit",
|
|
29401
|
+
example: {
|
|
29402
|
+
issue: "DSP-1",
|
|
29403
|
+
artifact: "spec",
|
|
29404
|
+
ops: [{ op: "replace", find: "old", with: "new" }]
|
|
29405
|
+
},
|
|
29373
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}`,
|
|
29374
29407
|
arguments: (z) => ({
|
|
29375
29408
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
@@ -29394,6 +29427,7 @@ var dispatchToolSpecs = [
|
|
|
29394
29427
|
},
|
|
29395
29428
|
{
|
|
29396
29429
|
name: "dispatch_doc_read",
|
|
29430
|
+
example: { issue: "DSP-1" },
|
|
29397
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,
|
|
29398
29432
|
arguments: (z) => ({
|
|
29399
29433
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
@@ -29406,6 +29440,7 @@ var dispatchToolSpecs = [
|
|
|
29406
29440
|
},
|
|
29407
29441
|
{
|
|
29408
29442
|
name: "dispatch_request_approval",
|
|
29443
|
+
example: { issue: "DSP-1" },
|
|
29409
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,
|
|
29410
29445
|
arguments: (z) => ({
|
|
29411
29446
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
@@ -29416,6 +29451,8 @@ var dispatchToolSpecs = [
|
|
|
29416
29451
|
},
|
|
29417
29452
|
{
|
|
29418
29453
|
name: "dispatch_artifact",
|
|
29454
|
+
example: { issue: "DSP-1", name: "design.md", content: `# Design
|
|
29455
|
+
` },
|
|
29419
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}`,
|
|
29420
29457
|
arguments: (z) => ({
|
|
29421
29458
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
@@ -29435,6 +29472,7 @@ var dispatchToolSpecs = [
|
|
|
29435
29472
|
},
|
|
29436
29473
|
{
|
|
29437
29474
|
name: "dispatch_read",
|
|
29475
|
+
example: { issue: "DSP-1" },
|
|
29438
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,
|
|
29439
29477
|
arguments: (z) => ({
|
|
29440
29478
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
@@ -29446,6 +29484,7 @@ var dispatchToolSpecs = [
|
|
|
29446
29484
|
},
|
|
29447
29485
|
{
|
|
29448
29486
|
name: "dispatch_search",
|
|
29487
|
+
example: { query: "astrolabe" },
|
|
29449
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.',
|
|
29450
29489
|
arguments: (z) => ({
|
|
29451
29490
|
query: z.string({ min: 2 }).describe("Keyword, phrase, or websearch expression; at least 2 characters."),
|
|
@@ -29455,6 +29494,7 @@ var dispatchToolSpecs = [
|
|
|
29455
29494
|
},
|
|
29456
29495
|
{
|
|
29457
29496
|
name: "dispatch_issues",
|
|
29497
|
+
example: { project: "AGENTC" },
|
|
29458
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.",
|
|
29459
29499
|
arguments: (z) => ({
|
|
29460
29500
|
project: z.string().describe("Project key to list issues from."),
|
|
@@ -29467,6 +29507,7 @@ var dispatchToolSpecs = [
|
|
|
29467
29507
|
},
|
|
29468
29508
|
{
|
|
29469
29509
|
name: "dispatch_architecture_sync",
|
|
29510
|
+
example: { project: "CORE" },
|
|
29470
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.",
|
|
29471
29512
|
arguments: (z) => ({
|
|
29472
29513
|
project: z.string().describe("Project key whose architecture source to sync, such as CORE.")
|
|
@@ -29475,6 +29516,7 @@ var dispatchToolSpecs = [
|
|
|
29475
29516
|
},
|
|
29476
29517
|
{
|
|
29477
29518
|
name: "dispatch_open_asks",
|
|
29519
|
+
example: {},
|
|
29478
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.",
|
|
29479
29521
|
arguments: (z) => ({
|
|
29480
29522
|
project: z.string().describe("Project key; when supplied, lists every open ask in the project instead of only this session's own.").optional()
|
|
@@ -29483,6 +29525,7 @@ var dispatchToolSpecs = [
|
|
|
29483
29525
|
},
|
|
29484
29526
|
{
|
|
29485
29527
|
name: "dispatch_whoami",
|
|
29528
|
+
example: {},
|
|
29486
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.",
|
|
29487
29530
|
arguments: () => ({}),
|
|
29488
29531
|
strict: true
|