@sjawhar/pi-legion-envoy 1.51.1 → 1.52.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/envoy.js CHANGED
@@ -29726,6 +29726,24 @@ var AskEditedEventPayloadSchema = object({
29726
29726
  }),
29727
29727
  edited_by: object({ kind: string2(), id: string2() }).passthrough()
29728
29728
  });
29729
+ var CommentMentionSchema = object({
29730
+ target: string2(),
29731
+ delivery: _enum2(DELIVERY_CAPABILITIES),
29732
+ session_id: string2().nullable()
29733
+ });
29734
+ var CommentDeliverySchema = object({
29735
+ comment_id: string2(),
29736
+ target: string2(),
29737
+ attempt: number2().int().positive(),
29738
+ delivery: _enum2(DELIVERY_CAPABILITIES),
29739
+ session_id: string2().nullable(),
29740
+ envelope_id: string2().nullable(),
29741
+ state: _enum2(["pending", "sent", "failed"]),
29742
+ error: string2().nullable(),
29743
+ resolve_error: string2().nullable(),
29744
+ reply_id: string2().nullable(),
29745
+ created_at: string2()
29746
+ });
29729
29747
  var CommentEventPayloadSchema = object({
29730
29748
  id: string2().optional(),
29731
29749
  artifact_name: string2().optional(),
@@ -29741,7 +29759,9 @@ var CommentEventPayloadSchema = object({
29741
29759
  anchor: object({ block_id: string2().nullable().optional(), quote: string2().optional() }).nullish(),
29742
29760
  suggestion: object({ replace_with: string2().optional() }).nullish(),
29743
29761
  author: object({ kind: string2(), id: string2() }).optional(),
29744
- created_at: string2().optional()
29762
+ created_at: string2().optional(),
29763
+ mentions: array(CommentMentionSchema).optional(),
29764
+ deliveries: array(CommentDeliverySchema).optional()
29745
29765
  });
29746
29766
  var MessageEventPayloadSchema = object({
29747
29767
  id: string2().optional(),
@@ -29751,8 +29771,9 @@ var MessageEventPayloadSchema = object({
29751
29771
  reply_body: string2().optional(),
29752
29772
  author: object({ kind: string2(), id: string2() }).optional()
29753
29773
  });
29774
+ var DispatchTargetedResourceIDSchema = uuid2();
29754
29775
  var DispatchTargetedMessagePayloadSchema = MessageEventPayloadSchema.extend({
29755
- id: string2(),
29776
+ id: DispatchTargetedResourceIDSchema,
29756
29777
  issue_key: string2().nullable(),
29757
29778
  author: object({ kind: string2(), id: string2() }),
29758
29779
  body: string2(),
@@ -29761,6 +29782,31 @@ var DispatchTargetedMessagePayloadSchema = MessageEventPayloadSchema.extend({
29761
29782
  deliveries: array(unknown()),
29762
29783
  created_at: string2()
29763
29784
  });
29785
+ var DispatchTargetedCommentPayloadSchema = CommentEventPayloadSchema.extend({
29786
+ id: DispatchTargetedResourceIDSchema,
29787
+ issue_key: string2().nullable(),
29788
+ artifact_id: string2().nullable(),
29789
+ author: object({ kind: string2(), id: string2() }),
29790
+ body: string2(),
29791
+ reply_to: string2().nullable(),
29792
+ ask_id: string2().nullable(),
29793
+ mentions: array(CommentMentionSchema),
29794
+ deliveries: array(CommentDeliverySchema),
29795
+ created_at: string2(),
29796
+ artifact_name: string2()
29797
+ });
29798
+ var DispatchTargetedMessageDeliverySchema = object({
29799
+ attempt: number2().int().positive(),
29800
+ mode: _enum2(DELIVERY_CAPABILITIES)
29801
+ });
29802
+ var DispatchTargetedCommentDeliverySchema = DispatchTargetedMessageDeliverySchema.extend({
29803
+ comment_id: DispatchTargetedResourceIDSchema,
29804
+ target: string2()
29805
+ });
29806
+ var DispatchTargetedDeliverySchema = union([
29807
+ DispatchTargetedCommentDeliverySchema,
29808
+ DispatchTargetedMessageDeliverySchema
29809
+ ]);
29764
29810
  var MessageDeliveryEventPayloadSchema = object({
29765
29811
  message_id: string2().optional(),
29766
29812
  attempt: number2().int().positive().optional(),
@@ -29908,6 +29954,7 @@ var DOC_EDIT_OPS = ["replace", "delete", "insert", "retype", "move"];
29908
29954
  var dispatchToolSpecs = [
29909
29955
  {
29910
29956
  name: "dispatch_issue",
29957
+ example: { project: "DSP", title: "Native workspace" },
29911
29958
  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
29959
  arguments: (z) => ({
29913
29960
  project: z.string().describe("Project key for the new issue."),
@@ -29924,6 +29971,7 @@ var dispatchToolSpecs = [
29924
29971
  },
29925
29972
  {
29926
29973
  name: "dispatch_issue_update",
29974
+ example: { issue: "DSP-1", status: "in_progress" },
29927
29975
  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
29976
  arguments: (z) => ({
29929
29977
  issue: z.string().describe(ISSUE_REFERENCE),
@@ -29946,6 +29994,7 @@ var dispatchToolSpecs = [
29946
29994
  },
29947
29995
  {
29948
29996
  name: "dispatch_ask",
29997
+ example: { issue: "DSP-1", question: "Ship this?" },
29949
29998
  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
29999
  arguments: (z) => ({
29951
30000
  issue: z.string().describe(ISSUE_REFERENCE).optional(),
@@ -29969,6 +30018,10 @@ var dispatchToolSpecs = [
29969
30018
  },
29970
30019
  {
29971
30020
  name: "dispatch_edit_ask",
30021
+ example: {
30022
+ ask: "01234567-0000-4000-8000-000000000001",
30023
+ question: "Ship the revised plan?"
30024
+ },
29972
30025
  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
30026
  arguments: (z) => ({
29974
30027
  ask: z.string().describe("Ask id to edit."),
@@ -29990,6 +30043,11 @@ var dispatchToolSpecs = [
29990
30043
  },
29991
30044
  {
29992
30045
  name: "dispatch_resolve_ask",
30046
+ example: {
30047
+ ask: "01234567-0000-4000-8000-000000000001",
30048
+ kind: "retracted",
30049
+ reason: "A newer question supersedes this one."
30050
+ },
29993
30051
  description: "Retract an open question that is moot or resolve one after finding the answer. This closes the question without answering it.",
29994
30052
  arguments: (z) => ({
29995
30053
  ask: z.string().describe("Ask id to close."),
@@ -29999,6 +30057,7 @@ var dispatchToolSpecs = [
29999
30057
  },
30000
30058
  {
30001
30059
  name: "dispatch_resolve_comment",
30060
+ example: { comment: "01234567-0000-4000-8000-000000000001" },
30002
30061
  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
30062
  arguments: (z) => ({
30004
30063
  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 +30066,7 @@ var dispatchToolSpecs = [
30007
30066
  },
30008
30067
  {
30009
30068
  name: "dispatch_follow",
30069
+ example: { ask: "01234567-0000-4000-8000-000000000001", action: "follow" },
30010
30070
  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
30071
  arguments: (z) => ({
30012
30072
  ask: z.string().describe("Full ask id (uuid)."),
@@ -30016,6 +30076,7 @@ var dispatchToolSpecs = [
30016
30076
  },
30017
30077
  {
30018
30078
  name: "dispatch_comment",
30079
+ example: { issue: "DSP-1", body: "Looks good." },
30019
30080
  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
30081
  arguments: (z) => ({
30021
30082
  issue: z.string().describe(ISSUE_REFERENCE).optional(),
@@ -30033,6 +30094,12 @@ var dispatchToolSpecs = [
30033
30094
  },
30034
30095
  {
30035
30096
  name: "dispatch_suggest",
30097
+ example: {
30098
+ issue: "DSP-1",
30099
+ artifact: "spec",
30100
+ quote: "old wording",
30101
+ replace_with: "new wording"
30102
+ },
30036
30103
  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
30104
  arguments: (z) => ({
30038
30105
  issue: z.string().describe(ISSUE_REFERENCE).optional(),
@@ -30048,6 +30115,7 @@ var dispatchToolSpecs = [
30048
30115
  },
30049
30116
  {
30050
30117
  name: "dispatch_message",
30118
+ example: { issue: "DSP-1", body: "Implementation started." },
30051
30119
  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
30120
  arguments: (z) => ({
30053
30121
  issue: z.string().describe(ISSUE_REFERENCE),
@@ -30057,6 +30125,11 @@ var dispatchToolSpecs = [
30057
30125
  },
30058
30126
  {
30059
30127
  name: "dispatch_doc_edit",
30128
+ example: {
30129
+ issue: "DSP-1",
30130
+ artifact: "spec",
30131
+ ops: [{ op: "replace", find: "old", with: "new" }]
30132
+ },
30060
30133
  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
30134
  arguments: (z) => ({
30062
30135
  issue: z.string().describe(ISSUE_REFERENCE).optional(),
@@ -30081,6 +30154,7 @@ var dispatchToolSpecs = [
30081
30154
  },
30082
30155
  {
30083
30156
  name: "dispatch_doc_read",
30157
+ example: { issue: "DSP-1" },
30084
30158
  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
30159
  arguments: (z) => ({
30086
30160
  issue: z.string().describe(ISSUE_REFERENCE).optional(),
@@ -30093,6 +30167,7 @@ var dispatchToolSpecs = [
30093
30167
  },
30094
30168
  {
30095
30169
  name: "dispatch_request_approval",
30170
+ example: { issue: "DSP-1" },
30096
30171
  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
30172
  arguments: (z) => ({
30098
30173
  issue: z.string().describe(ISSUE_REFERENCE).optional(),
@@ -30103,6 +30178,8 @@ var dispatchToolSpecs = [
30103
30178
  },
30104
30179
  {
30105
30180
  name: "dispatch_artifact",
30181
+ example: { issue: "DSP-1", name: "design.md", content: `# Design
30182
+ ` },
30106
30183
  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
30184
  arguments: (z) => ({
30108
30185
  issue: z.string().describe(ISSUE_REFERENCE).optional(),
@@ -30122,6 +30199,7 @@ var dispatchToolSpecs = [
30122
30199
  },
30123
30200
  {
30124
30201
  name: "dispatch_read",
30202
+ example: { issue: "DSP-1" },
30125
30203
  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
30204
  arguments: (z) => ({
30127
30205
  issue: z.string().describe(ISSUE_REFERENCE).optional(),
@@ -30133,6 +30211,7 @@ var dispatchToolSpecs = [
30133
30211
  },
30134
30212
  {
30135
30213
  name: "dispatch_search",
30214
+ example: { query: "astrolabe" },
30136
30215
  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
30216
  arguments: (z) => ({
30138
30217
  query: z.string({ min: 2 }).describe("Keyword, phrase, or websearch expression; at least 2 characters."),
@@ -30142,6 +30221,7 @@ var dispatchToolSpecs = [
30142
30221
  },
30143
30222
  {
30144
30223
  name: "dispatch_issues",
30224
+ example: { project: "AGENTC" },
30145
30225
  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
30226
  arguments: (z) => ({
30147
30227
  project: z.string().describe("Project key to list issues from."),
@@ -30154,6 +30234,7 @@ var dispatchToolSpecs = [
30154
30234
  },
30155
30235
  {
30156
30236
  name: "dispatch_architecture_sync",
30237
+ example: { project: "CORE" },
30157
30238
  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
30239
  arguments: (z) => ({
30159
30240
  project: z.string().describe("Project key whose architecture source to sync, such as CORE.")
@@ -30162,6 +30243,7 @@ var dispatchToolSpecs = [
30162
30243
  },
30163
30244
  {
30164
30245
  name: "dispatch_open_asks",
30246
+ example: {},
30165
30247
  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
30248
  arguments: (z) => ({
30167
30249
  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 +30252,7 @@ var dispatchToolSpecs = [
30170
30252
  },
30171
30253
  {
30172
30254
  name: "dispatch_whoami",
30255
+ example: {},
30173
30256
  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
30257
  arguments: () => ({}),
30175
30258
  strict: true
@@ -31290,13 +31373,18 @@ var TolerantInboundEnvelopeSchema = object({
31290
31373
  source: InboundEnvelopeSchema.shape.source.optional().catch(undefined),
31291
31374
  sender: object(tolerantShape(InboundSenderSchema.shape)).optional().catch(undefined)
31292
31375
  }).passthrough();
31376
+ function dispatchReplyPath(resource, id) {
31377
+ const collection = resource === "message" ? "messages" : "comments";
31378
+ return `/api/v1/${collection}/${encodeURIComponent(id)}/reply`;
31379
+ }
31293
31380
  async function postDeliveryReply(config, sessionId, delivery, result) {
31294
- const response = await fetch(`${config.url}/api/v1/messages/${delivery.messageID}/reply`, {
31381
+ const response = await fetch(`${config.url}${dispatchReplyPath(delivery.resource, delivery.id)}`, {
31295
31382
  method: "POST",
31296
31383
  headers: { Authorization: `Bearer ${config.token}`, "Content-Type": "application/json" },
31297
31384
  body: JSON.stringify({
31298
31385
  actor: { kind: "session", id: sessionId },
31299
31386
  attempt: delivery.attempt,
31387
+ ...delivery.replyFields,
31300
31388
  ...result
31301
31389
  })
31302
31390
  });
@@ -31315,21 +31403,32 @@ function rememberBounded(seen, key, limit) {
31315
31403
  seen.delete(oldest.value);
31316
31404
  }
31317
31405
  }
31318
- var DispatchDeliveryRequestSchema = object({
31319
- attempt: number2().int().positive(),
31320
- mode: _enum2(DELIVERY_CAPABILITIES)
31321
- });
31406
+ function isCommentTargetedDelivery(delivery) {
31407
+ return "comment_id" in delivery;
31408
+ }
31322
31409
  var DispatchTargetedFrameSchema = object({
31323
31410
  event: DispatchEventSchema,
31324
- delivery: DispatchDeliveryRequestSchema
31325
- }).refine(({ event }) => event.type === "message.created" && DispatchTargetedMessagePayloadSchema.safeParse(event.payload).success, { message: "targeted delivery requires a message.created event" });
31411
+ delivery: DispatchTargetedDeliverySchema
31412
+ }).superRefine(({ event, delivery }, context) => {
31413
+ if (event.type === "message.created" && !isCommentTargetedDelivery(delivery) && DispatchTargetedMessagePayloadSchema.safeParse(event.payload).success) {
31414
+ return;
31415
+ }
31416
+ if (event.type === "comment.created" && isCommentTargetedDelivery(delivery) && DispatchTargetedCommentPayloadSchema.safeParse(event.payload).success) {
31417
+ return;
31418
+ }
31419
+ context.addIssue({
31420
+ code: "custom",
31421
+ message: event.type === "comment.created" ? "targeted delivery requires a complete comment.created event" : "targeted delivery requires a complete message.created event"
31422
+ });
31423
+ });
31326
31424
  var RecoverableDispatchDeliveryFailureSchema = object({
31327
31425
  event: object({
31426
+ type: _enum2(["message.created", "comment.created"]),
31328
31427
  issue_key: string2().nullable(),
31329
31428
  payload: object({ id: string2(), body: string2().optional() }).passthrough()
31330
31429
  }).passthrough(),
31331
- delivery: DispatchDeliveryRequestSchema
31332
- });
31430
+ delivery: DispatchTargetedDeliverySchema
31431
+ }).refine(({ event, delivery }) => event.type === "comment.created" ? isCommentTargetedDelivery(delivery) : !isCommentTargetedDelivery(delivery) && DispatchTargetedResourceIDSchema.safeParse(event.payload.id).success, { message: "delivery resource must match its event type" });
31333
31432
  function senderLabel(envelope) {
31334
31433
  const sender = envelope.source_session ?? envelope.source ?? "unknown";
31335
31434
  return envelope.sender?.title === undefined ? sender : `${sender} (${envelope.sender.title})`;
@@ -31474,12 +31573,19 @@ function parseDispatchFrame(rawPayload) {
31474
31573
  }
31475
31574
  const recoverable = RecoverableDispatchDeliveryFailureSchema.safeParse(value);
31476
31575
  if (recoverable.success) {
31576
+ const delivery = recoverable.data.delivery;
31577
+ const commentDelivery = isCommentTargetedDelivery(delivery);
31578
+ const resource = commentDelivery ? "comment" : "message";
31579
+ const id = commentDelivery ? delivery.comment_id : recoverable.data.event.payload.id;
31477
31580
  return {
31478
31581
  raw: value,
31479
31582
  rejectedDelivery: {
31480
- attempt: recoverable.data.delivery.attempt,
31481
- mode: recoverable.data.delivery.mode,
31482
- messageID: recoverable.data.event.payload.id,
31583
+ resource,
31584
+ id,
31585
+ attempt: delivery.attempt,
31586
+ mode: delivery.mode,
31587
+ replyPath: dispatchReplyPath(resource, id),
31588
+ replyFields: commentDelivery ? { target: delivery.target } : {},
31483
31589
  issueKey: recoverable.data.event.issue_key,
31484
31590
  body: recoverable.data.event.payload.body ?? ""
31485
31591
  },
@@ -31596,15 +31702,18 @@ function renderInbound(raw, sessionID, subject) {
31596
31702
  }
31597
31703
  dispatchReply = dispatchCommentReplyWith(frame.event, topic, commentPayload);
31598
31704
  if (frame.event.type === "message.created") {
31599
- const message = MessageEventPayloadSchema.safeParse(frame.event.payload);
31600
- const requested = DispatchDeliveryRequestSchema.safeParse(frame.delivery);
31601
- if (message.success && requested.success && message.data.id !== undefined) {
31705
+ const message = DispatchTargetedMessagePayloadSchema.safeParse(frame.event.payload);
31706
+ const requested = DispatchTargetedDeliverySchema.safeParse(frame.delivery);
31707
+ if (message.success && requested.success && !isCommentTargetedDelivery(requested.data)) {
31602
31708
  delivery = {
31709
+ resource: "message",
31710
+ id: message.data.id,
31603
31711
  attempt: requested.data.attempt,
31604
31712
  mode: requested.data.mode,
31605
- messageID: message.data.id,
31713
+ replyPath: dispatchReplyPath("message", message.data.id),
31714
+ replyFields: {},
31606
31715
  issueKey: frame.event.issue_key,
31607
- body: message.data.body ?? envelope.payload_summary ?? ""
31716
+ body: message.data.body
31608
31717
  };
31609
31718
  if (frame.event.issue_key !== null) {
31610
31719
  dispatchReply = {
@@ -31613,6 +31722,21 @@ function renderInbound(raw, sessionID, subject) {
31613
31722
  };
31614
31723
  }
31615
31724
  }
31725
+ } else if (frame.event.type === "comment.created") {
31726
+ const comment = DispatchTargetedCommentPayloadSchema.safeParse(frame.event.payload);
31727
+ const requested = DispatchTargetedDeliverySchema.safeParse(frame.delivery);
31728
+ if (comment.success && requested.success && isCommentTargetedDelivery(requested.data)) {
31729
+ delivery = {
31730
+ resource: "comment",
31731
+ id: requested.data.comment_id,
31732
+ attempt: requested.data.attempt,
31733
+ mode: requested.data.mode,
31734
+ replyPath: dispatchReplyPath("comment", requested.data.comment_id),
31735
+ replyFields: { target: requested.data.target },
31736
+ issueKey: frame.event.issue_key,
31737
+ body: comment.data.body
31738
+ };
31739
+ }
31616
31740
  }
31617
31741
  dispatchEvent = {
31618
31742
  owner: dispatchOwner(frame.event, topic),
@@ -32260,9 +32384,11 @@ class ToolInputError extends Error {
32260
32384
  problems;
32261
32385
  constructor(tool, problems) {
32262
32386
  const count = problems.length;
32387
+ const help = dispatchInputHelp(tool);
32263
32388
  super([
32264
32389
  `${tool} was not called: ${count} problem${count === 1 ? "" : "s"}`,
32265
- ...problems.map((problem) => `- ${problem}`)
32390
+ ...problems.map((problem) => `- ${problem}`),
32391
+ ...help === undefined ? [] : help.map((line) => `- ${line}`)
32266
32392
  ].join(`
32267
32393
  `));
32268
32394
  this.name = "ToolInputError";
@@ -32270,6 +32396,14 @@ class ToolInputError extends Error {
32270
32396
  this.problems = problems;
32271
32397
  }
32272
32398
  }
32399
+ function dispatchInputHelp(tool) {
32400
+ const spec = dispatchToolSpecs.find((candidate) => candidate.name === tool);
32401
+ if (spec === undefined)
32402
+ return;
32403
+ const schema = dispatchToolSchema(spec, zodSchemaApi(exports_external), { strict: true });
32404
+ const allowed = Object.keys(shapeOf(schema) ?? {}).join(", ") || "none";
32405
+ return [`Allowed keys: ${allowed}`, `Example: ${tool}(${JSON.stringify(spec.example)})`];
32406
+ }
32273
32407
  function unwrap(schema) {
32274
32408
  let current = schema;
32275
32409
  while (current !== undefined) {
@@ -32574,7 +32708,21 @@ function refTarget(ref, kind, id) {
32574
32708
  const ownerRef = ref.owner.kind === "issue" ? dispatchIssueRef(ref.owner.issue) : dispatchDocumentRef(ref.owner.project, `${ref.artifact}`);
32575
32709
  return dispatchChildRef(ownerRef, kind, id);
32576
32710
  }
32577
- var uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
32711
+ var canonicalUUIDPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
32712
+ var compactUUIDPattern = /^[0-9a-f]{32}$/i;
32713
+ function normalizeUUID(value) {
32714
+ let compact = value;
32715
+ if (value.slice(0, 9).toLowerCase() === "urn:uuid:") {
32716
+ compact = value.slice(9);
32717
+ } else if (value.length === 38 && value.startsWith("{") && value.endsWith("}")) {
32718
+ compact = value.slice(1, -1);
32719
+ }
32720
+ if (canonicalUUIDPattern.test(compact))
32721
+ compact = compact.replaceAll("-", "");
32722
+ if (!compactUUIDPattern.test(compact))
32723
+ return;
32724
+ return `${compact.slice(0, 8)}-${compact.slice(8, 12)}-${compact.slice(12, 16)}-${compact.slice(16, 20)}-${compact.slice(20)}`.toLowerCase();
32725
+ }
32578
32726
  var askIdProblem = "ask must be a bare ask id or a dispatch://.../ask/<id> reference";
32579
32727
  var commentIdProblem = "comment must be a bare comment id or a dispatch://.../comment/<id> reference";
32580
32728
  var messageIdProblem = "in_reply_to must be a full message id (uuid) or a dispatch://KEY/message/<id> reference";
@@ -32587,12 +32735,13 @@ function messageIdOf(value) {
32587
32735
  const reference = parseDispatchRef(value);
32588
32736
  return reference?.kind === "message" ? reference.id : undefined;
32589
32737
  })() : value;
32590
- return id !== undefined && uuidPattern.test(id) ? id : undefined;
32738
+ return id === undefined ? undefined : normalizeUUID(id);
32591
32739
  }
32592
32740
  var idPrefixPattern = /^[0-9a-f][0-9a-f-]{7,}$/i;
32593
32741
  async function resolveIdPrefix(tool, kind, ref, list) {
32594
- if (uuidPattern.test(ref.id))
32595
- return ref.id;
32742
+ const fullID = normalizeUUID(ref.id);
32743
+ if (fullID !== undefined)
32744
+ return fullID;
32596
32745
  const ownerName = ref.owner.kind === "issue" ? ref.owner.issue : `${ref.owner.project}/${ref.artifact}`;
32597
32746
  if (!idPrefixPattern.test(ref.id)) {
32598
32747
  throw new ToolInputError(tool, [
@@ -32824,21 +32973,53 @@ async function resolveArtifact(client, owner, artifactReference) {
32824
32973
  return { owner, artifact };
32825
32974
  const names = artifacts.filter((candidate) => candidate.name === artifactReference);
32826
32975
  if (names.length > 1) {
32827
- throw new Error(`artifact name ${artifactReference} is ambiguous in project ${owner.project}; ` + `${names.length} documents share it \u2014 use its slug instead`);
32976
+ throw new Error(documentReferenceProblem(artifactReference, names, "project", true));
32977
+ }
32978
+ if (names[0] === undefined) {
32979
+ throw new Error(documentReferenceProblem(artifactReference, artifacts, "project"));
32828
32980
  }
32829
- if (names[0] === undefined)
32830
- throw error;
32831
32981
  return { owner, artifact: names[0] };
32832
32982
  }
32833
32983
  }
32834
32984
  const issue = await client.getIssue(owner.issue);
32835
- const artifact = artifactReference === undefined || artifactReference === "spec" ? issue.artifacts.find((candidate) => candidate.primary || candidate.id === issue.primary_artifact_id) : issue.artifacts.find((candidate) => candidate.id === artifactReference || candidate.slug === artifactReference || candidate.name === artifactReference);
32985
+ let artifact;
32986
+ if (artifactReference === undefined || artifactReference === "spec") {
32987
+ artifact = issue.artifacts.find((candidate) => candidate.primary || candidate.id === issue.primary_artifact_id);
32988
+ } else {
32989
+ artifact = issue.artifacts.find((candidate) => candidate.id === artifactReference || candidate.slug === artifactReference);
32990
+ if (artifact === undefined) {
32991
+ const names = issue.artifacts.filter((candidate) => candidate.name === artifactReference);
32992
+ if (names.length > 1) {
32993
+ throw new Error(documentReferenceProblem(artifactReference, names, "issue", true));
32994
+ }
32995
+ artifact = names[0];
32996
+ }
32997
+ }
32836
32998
  if (!artifact) {
32837
- const slugs = issue.artifacts.map((candidate) => `${candidate.slug}${candidate.primary || candidate.id === issue.primary_artifact_id ? " (primary)" : ""}`);
32838
- throw new Error(`artifact "${artifactReference ?? "spec"}" was not found on issue ${issue.key}; artifacts: ${slugs.length === 0 ? "none" : slugs.join(", ")}`);
32999
+ throw new Error(documentReferenceProblem(artifactReference ?? "spec", issue.artifacts, "issue"));
32839
33000
  }
32840
33001
  return { owner, issue, artifact };
32841
33002
  }
33003
+ var documentHintLimit = 8;
33004
+ function documentReferenceProblem(reference, documents, owner, ambiguous = false) {
33005
+ const hints = documents.slice(0, documentHintLimit).map((document) => `${document.slug} (${document.name})`);
33006
+ const list = hints.length === 0 ? "none" : hints.join(", ");
33007
+ 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}`;
33008
+ }
33009
+ var askHintLimit = 8;
33010
+ function askIDInputProblem(asks, scope) {
33011
+ const hints = asks.slice(0, askHintLimit).map((ask) => `${ask.id.slice(0, 8)}\u2026 ${textHead(ask.question)}`);
33012
+ return `ask IDs are UUIDs; use the full ask ID; ${scope} open asks: ${hints.length === 0 ? "none" : hints.join(", ")}`;
33013
+ }
33014
+ async function invalidReplyToAskProblem(client, owner, resolved) {
33015
+ if (owner.kind === "issue") {
33016
+ const issue = resolved?.issue ?? await client.getIssue(owner.issue);
33017
+ return askIDInputProblem(issue.open_asks, "this issue's");
33018
+ }
33019
+ if (resolved === undefined)
33020
+ throw new Error("project document is missing its resolved artifact");
33021
+ return askIDInputProblem(await client.getArtifactAsks(resolved.artifact.id, "open"), "this document's");
33022
+ }
32842
33023
  function anchor(artifact, args) {
32843
33024
  const quote = optionalString(args, "quote");
32844
33025
  if (quote === undefined)
@@ -33442,7 +33623,13 @@ ${followsAsk(askOwner)}`,
33442
33623
  const resolved = owner.kind === "project" || artifactReference === undefined ? owner.kind === "project" ? await resolveArtifact(client, owner, artifactReference) : undefined : await resolveArtifact(client, owner, artifactReference);
33443
33624
  const anchored = resolved ? anchor(resolved.artifact, args) : undefined;
33444
33625
  const replyTo = optionalString(args, "reply_to");
33445
- const replyToAsk = optionalString(args, "reply_to_ask");
33626
+ const replyToAskReference = optionalString(args, "reply_to_ask");
33627
+ const replyToAsk = replyToAskReference === undefined ? undefined : normalizeUUID(replyToAskReference);
33628
+ if (replyToAskReference !== undefined && replyToAsk === undefined) {
33629
+ throw new ToolInputError(input.tool, [
33630
+ await invalidReplyToAskProblem(client, owner, resolved)
33631
+ ]);
33632
+ }
33446
33633
  const requestedTurn = optionalString(args, "turn");
33447
33634
  const commentInput = {
33448
33635
  body: stringArg(args, "body"),
@@ -34370,7 +34557,7 @@ function envoyExtension(pi) {
34370
34557
  }
34371
34558
  try {
34372
34559
  if (rendered.rejectedDelivery !== undefined) {
34373
- console.warn(`[envoy] rejecting malformed Dispatch targeted delivery ${rendered.rejectedDelivery.messageID}`);
34560
+ console.warn(`[envoy] rejecting malformed Dispatch targeted delivery ${rendered.rejectedDelivery.id}`);
34374
34561
  await postDispatchReply(rendered.rejectedDelivery, {
34375
34562
  error: "Invalid Dispatch targeted delivery frame"
34376
34563
  });
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.1",
16148
+ version: "1.52.0",
16149
16149
  type: "module",
16150
16150
  omp: {
16151
16151
  extensions: [
@@ -29045,6 +29045,24 @@ var AskEditedEventPayloadSchema = object({
29045
29045
  }),
29046
29046
  edited_by: object({ kind: string2(), id: string2() }).passthrough()
29047
29047
  });
29048
+ var CommentMentionSchema = object({
29049
+ target: string2(),
29050
+ delivery: _enum2(DELIVERY_CAPABILITIES),
29051
+ session_id: string2().nullable()
29052
+ });
29053
+ var CommentDeliverySchema = object({
29054
+ comment_id: string2(),
29055
+ target: string2(),
29056
+ attempt: number2().int().positive(),
29057
+ delivery: _enum2(DELIVERY_CAPABILITIES),
29058
+ session_id: string2().nullable(),
29059
+ envelope_id: string2().nullable(),
29060
+ state: _enum2(["pending", "sent", "failed"]),
29061
+ error: string2().nullable(),
29062
+ resolve_error: string2().nullable(),
29063
+ reply_id: string2().nullable(),
29064
+ created_at: string2()
29065
+ });
29048
29066
  var CommentEventPayloadSchema = object({
29049
29067
  id: string2().optional(),
29050
29068
  artifact_name: string2().optional(),
@@ -29060,7 +29078,9 @@ var CommentEventPayloadSchema = object({
29060
29078
  anchor: object({ block_id: string2().nullable().optional(), quote: string2().optional() }).nullish(),
29061
29079
  suggestion: object({ replace_with: string2().optional() }).nullish(),
29062
29080
  author: object({ kind: string2(), id: string2() }).optional(),
29063
- created_at: string2().optional()
29081
+ created_at: string2().optional(),
29082
+ mentions: array(CommentMentionSchema).optional(),
29083
+ deliveries: array(CommentDeliverySchema).optional()
29064
29084
  });
29065
29085
  var MessageEventPayloadSchema = object({
29066
29086
  id: string2().optional(),
@@ -29070,8 +29090,9 @@ var MessageEventPayloadSchema = object({
29070
29090
  reply_body: string2().optional(),
29071
29091
  author: object({ kind: string2(), id: string2() }).optional()
29072
29092
  });
29093
+ var DispatchTargetedResourceIDSchema = uuid2();
29073
29094
  var DispatchTargetedMessagePayloadSchema = MessageEventPayloadSchema.extend({
29074
- id: string2(),
29095
+ id: DispatchTargetedResourceIDSchema,
29075
29096
  issue_key: string2().nullable(),
29076
29097
  author: object({ kind: string2(), id: string2() }),
29077
29098
  body: string2(),
@@ -29080,6 +29101,31 @@ var DispatchTargetedMessagePayloadSchema = MessageEventPayloadSchema.extend({
29080
29101
  deliveries: array(unknown()),
29081
29102
  created_at: string2()
29082
29103
  });
29104
+ var DispatchTargetedCommentPayloadSchema = CommentEventPayloadSchema.extend({
29105
+ id: DispatchTargetedResourceIDSchema,
29106
+ issue_key: string2().nullable(),
29107
+ artifact_id: string2().nullable(),
29108
+ author: object({ kind: string2(), id: string2() }),
29109
+ body: string2(),
29110
+ reply_to: string2().nullable(),
29111
+ ask_id: string2().nullable(),
29112
+ mentions: array(CommentMentionSchema),
29113
+ deliveries: array(CommentDeliverySchema),
29114
+ created_at: string2(),
29115
+ artifact_name: string2()
29116
+ });
29117
+ var DispatchTargetedMessageDeliverySchema = object({
29118
+ attempt: number2().int().positive(),
29119
+ mode: _enum2(DELIVERY_CAPABILITIES)
29120
+ });
29121
+ var DispatchTargetedCommentDeliverySchema = DispatchTargetedMessageDeliverySchema.extend({
29122
+ comment_id: DispatchTargetedResourceIDSchema,
29123
+ target: string2()
29124
+ });
29125
+ var DispatchTargetedDeliverySchema = union([
29126
+ DispatchTargetedCommentDeliverySchema,
29127
+ DispatchTargetedMessageDeliverySchema
29128
+ ]);
29083
29129
  var MessageDeliveryEventPayloadSchema = object({
29084
29130
  message_id: string2().optional(),
29085
29131
  attempt: number2().int().positive().optional(),
@@ -29227,6 +29273,7 @@ var DOC_EDIT_OPS = ["replace", "delete", "insert", "retype", "move"];
29227
29273
  var dispatchToolSpecs = [
29228
29274
  {
29229
29275
  name: "dispatch_issue",
29276
+ example: { project: "DSP", title: "Native workspace" },
29230
29277
  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
29278
  arguments: (z) => ({
29232
29279
  project: z.string().describe("Project key for the new issue."),
@@ -29243,6 +29290,7 @@ var dispatchToolSpecs = [
29243
29290
  },
29244
29291
  {
29245
29292
  name: "dispatch_issue_update",
29293
+ example: { issue: "DSP-1", status: "in_progress" },
29246
29294
  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
29295
  arguments: (z) => ({
29248
29296
  issue: z.string().describe(ISSUE_REFERENCE),
@@ -29265,6 +29313,7 @@ var dispatchToolSpecs = [
29265
29313
  },
29266
29314
  {
29267
29315
  name: "dispatch_ask",
29316
+ example: { issue: "DSP-1", question: "Ship this?" },
29268
29317
  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
29318
  arguments: (z) => ({
29270
29319
  issue: z.string().describe(ISSUE_REFERENCE).optional(),
@@ -29288,6 +29337,10 @@ var dispatchToolSpecs = [
29288
29337
  },
29289
29338
  {
29290
29339
  name: "dispatch_edit_ask",
29340
+ example: {
29341
+ ask: "01234567-0000-4000-8000-000000000001",
29342
+ question: "Ship the revised plan?"
29343
+ },
29291
29344
  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
29345
  arguments: (z) => ({
29293
29346
  ask: z.string().describe("Ask id to edit."),
@@ -29309,6 +29362,11 @@ var dispatchToolSpecs = [
29309
29362
  },
29310
29363
  {
29311
29364
  name: "dispatch_resolve_ask",
29365
+ example: {
29366
+ ask: "01234567-0000-4000-8000-000000000001",
29367
+ kind: "retracted",
29368
+ reason: "A newer question supersedes this one."
29369
+ },
29312
29370
  description: "Retract an open question that is moot or resolve one after finding the answer. This closes the question without answering it.",
29313
29371
  arguments: (z) => ({
29314
29372
  ask: z.string().describe("Ask id to close."),
@@ -29318,6 +29376,7 @@ var dispatchToolSpecs = [
29318
29376
  },
29319
29377
  {
29320
29378
  name: "dispatch_resolve_comment",
29379
+ example: { comment: "01234567-0000-4000-8000-000000000001" },
29321
29380
  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
29381
  arguments: (z) => ({
29323
29382
  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 +29385,7 @@ var dispatchToolSpecs = [
29326
29385
  },
29327
29386
  {
29328
29387
  name: "dispatch_follow",
29388
+ example: { ask: "01234567-0000-4000-8000-000000000001", action: "follow" },
29329
29389
  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
29390
  arguments: (z) => ({
29331
29391
  ask: z.string().describe("Full ask id (uuid)."),
@@ -29335,6 +29395,7 @@ var dispatchToolSpecs = [
29335
29395
  },
29336
29396
  {
29337
29397
  name: "dispatch_comment",
29398
+ example: { issue: "DSP-1", body: "Looks good." },
29338
29399
  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
29400
  arguments: (z) => ({
29340
29401
  issue: z.string().describe(ISSUE_REFERENCE).optional(),
@@ -29352,6 +29413,12 @@ var dispatchToolSpecs = [
29352
29413
  },
29353
29414
  {
29354
29415
  name: "dispatch_suggest",
29416
+ example: {
29417
+ issue: "DSP-1",
29418
+ artifact: "spec",
29419
+ quote: "old wording",
29420
+ replace_with: "new wording"
29421
+ },
29355
29422
  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
29423
  arguments: (z) => ({
29357
29424
  issue: z.string().describe(ISSUE_REFERENCE).optional(),
@@ -29367,6 +29434,7 @@ var dispatchToolSpecs = [
29367
29434
  },
29368
29435
  {
29369
29436
  name: "dispatch_message",
29437
+ example: { issue: "DSP-1", body: "Implementation started." },
29370
29438
  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
29439
  arguments: (z) => ({
29372
29440
  issue: z.string().describe(ISSUE_REFERENCE),
@@ -29376,6 +29444,11 @@ var dispatchToolSpecs = [
29376
29444
  },
29377
29445
  {
29378
29446
  name: "dispatch_doc_edit",
29447
+ example: {
29448
+ issue: "DSP-1",
29449
+ artifact: "spec",
29450
+ ops: [{ op: "replace", find: "old", with: "new" }]
29451
+ },
29379
29452
  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
29453
  arguments: (z) => ({
29381
29454
  issue: z.string().describe(ISSUE_REFERENCE).optional(),
@@ -29400,6 +29473,7 @@ var dispatchToolSpecs = [
29400
29473
  },
29401
29474
  {
29402
29475
  name: "dispatch_doc_read",
29476
+ example: { issue: "DSP-1" },
29403
29477
  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
29478
  arguments: (z) => ({
29405
29479
  issue: z.string().describe(ISSUE_REFERENCE).optional(),
@@ -29412,6 +29486,7 @@ var dispatchToolSpecs = [
29412
29486
  },
29413
29487
  {
29414
29488
  name: "dispatch_request_approval",
29489
+ example: { issue: "DSP-1" },
29415
29490
  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
29491
  arguments: (z) => ({
29417
29492
  issue: z.string().describe(ISSUE_REFERENCE).optional(),
@@ -29422,6 +29497,8 @@ var dispatchToolSpecs = [
29422
29497
  },
29423
29498
  {
29424
29499
  name: "dispatch_artifact",
29500
+ example: { issue: "DSP-1", name: "design.md", content: `# Design
29501
+ ` },
29425
29502
  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
29503
  arguments: (z) => ({
29427
29504
  issue: z.string().describe(ISSUE_REFERENCE).optional(),
@@ -29441,6 +29518,7 @@ var dispatchToolSpecs = [
29441
29518
  },
29442
29519
  {
29443
29520
  name: "dispatch_read",
29521
+ example: { issue: "DSP-1" },
29444
29522
  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
29523
  arguments: (z) => ({
29446
29524
  issue: z.string().describe(ISSUE_REFERENCE).optional(),
@@ -29452,6 +29530,7 @@ var dispatchToolSpecs = [
29452
29530
  },
29453
29531
  {
29454
29532
  name: "dispatch_search",
29533
+ example: { query: "astrolabe" },
29455
29534
  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
29535
  arguments: (z) => ({
29457
29536
  query: z.string({ min: 2 }).describe("Keyword, phrase, or websearch expression; at least 2 characters."),
@@ -29461,6 +29540,7 @@ var dispatchToolSpecs = [
29461
29540
  },
29462
29541
  {
29463
29542
  name: "dispatch_issues",
29543
+ example: { project: "AGENTC" },
29464
29544
  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
29545
  arguments: (z) => ({
29466
29546
  project: z.string().describe("Project key to list issues from."),
@@ -29473,6 +29553,7 @@ var dispatchToolSpecs = [
29473
29553
  },
29474
29554
  {
29475
29555
  name: "dispatch_architecture_sync",
29556
+ example: { project: "CORE" },
29476
29557
  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
29558
  arguments: (z) => ({
29478
29559
  project: z.string().describe("Project key whose architecture source to sync, such as CORE.")
@@ -29481,6 +29562,7 @@ var dispatchToolSpecs = [
29481
29562
  },
29482
29563
  {
29483
29564
  name: "dispatch_open_asks",
29565
+ example: {},
29484
29566
  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
29567
  arguments: (z) => ({
29486
29568
  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 +29571,7 @@ var dispatchToolSpecs = [
29489
29571
  },
29490
29572
  {
29491
29573
  name: "dispatch_whoami",
29574
+ example: {},
29492
29575
  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
29576
  arguments: () => ({}),
29494
29577
  strict: true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjawhar/pi-legion-envoy",
3
- "version": "1.51.1",
3
+ "version": "1.52.0",
4
4
  "type": "module",
5
5
  "omp": {
6
6
  "extensions": [