@sjawhar/pi-legion-envoy 1.2.0 → 1.4.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 +69 -3
- package/dist/legion.js +69 -3
- package/dist/skills/dispatch/SKILL.md +16 -0
- package/package.json +1 -1
package/dist/envoy.js
CHANGED
|
@@ -29671,6 +29671,14 @@ var ArtifactVersionEventPayloadSchema = object({
|
|
|
29671
29671
|
version: object({ number: number2().optional(), summary: string2().nullish() }).optional(),
|
|
29672
29672
|
diff: string2().optional()
|
|
29673
29673
|
});
|
|
29674
|
+
var ArtifactReviewEventPayloadSchema = object({
|
|
29675
|
+
artifact_id: string2().optional(),
|
|
29676
|
+
name: string2().optional(),
|
|
29677
|
+
version: number2().int().optional(),
|
|
29678
|
+
actor: object({ kind: string2(), id: string2() }).passthrough().optional(),
|
|
29679
|
+
reason: string2().nullish(),
|
|
29680
|
+
ask_id: string2().nullish()
|
|
29681
|
+
});
|
|
29674
29682
|
var askEventPayloadFields = {
|
|
29675
29683
|
id: string2().optional(),
|
|
29676
29684
|
opened_event_id: number2().int().positive(),
|
|
@@ -29952,6 +29960,16 @@ var dispatchToolSpecs = [
|
|
|
29952
29960
|
}),
|
|
29953
29961
|
validation: documentOwnerValidation(true)
|
|
29954
29962
|
},
|
|
29963
|
+
{
|
|
29964
|
+
name: "dispatch_request_approval",
|
|
29965
|
+
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,
|
|
29966
|
+
arguments: (z) => ({
|
|
29967
|
+
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
29968
|
+
project: z.string().describe("Project key owning the document.").optional(),
|
|
29969
|
+
artifact: z.string().describe("Artifact slug or id; primary document by default for an issue.").optional()
|
|
29970
|
+
}),
|
|
29971
|
+
validation: documentOwnerValidation(true)
|
|
29972
|
+
},
|
|
29955
29973
|
{
|
|
29956
29974
|
name: "dispatch_artifact",
|
|
29957
29975
|
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}`,
|
|
@@ -31476,6 +31494,9 @@ class DispatchClient {
|
|
|
31476
31494
|
async resolveAsk(id, input) {
|
|
31477
31495
|
return this.#json("POST", ["api", "v1", "asks", id, "resolve"], input);
|
|
31478
31496
|
}
|
|
31497
|
+
async requestApproval(artifactID, input) {
|
|
31498
|
+
return this.#json("POST", ["api", "v1", "artifacts", artifactID, "approval-requests"], input);
|
|
31499
|
+
}
|
|
31479
31500
|
async editAsk(id, input) {
|
|
31480
31501
|
return this.#json("PATCH", ["api", "v1", "asks", id], input);
|
|
31481
31502
|
}
|
|
@@ -31961,13 +31982,31 @@ function toolActor(origin, input) {
|
|
|
31961
31982
|
}
|
|
31962
31983
|
};
|
|
31963
31984
|
}
|
|
31985
|
+
function approvalLine(artifact) {
|
|
31986
|
+
const approval = artifact.approval;
|
|
31987
|
+
if (approval === undefined || approval.state === "draft")
|
|
31988
|
+
return;
|
|
31989
|
+
switch (approval.state) {
|
|
31990
|
+
case "awaiting":
|
|
31991
|
+
return `Approval: awaiting (requested by ${approval.requested_by?.id ?? "unknown"}, ask ${approval.ask_id ?? "?"})`;
|
|
31992
|
+
case "approved":
|
|
31993
|
+
return `Approval: approved v${approval.version} by ${approval.by?.id ?? "unknown"}`;
|
|
31994
|
+
case "stale":
|
|
31995
|
+
return `Approval: approved v${approval.version} by ${approval.by?.id ?? "unknown"}, edited since (now v${approval.latest_version}) - request approval again`;
|
|
31996
|
+
case "changes_requested":
|
|
31997
|
+
return `Approval: changes requested on v${approval.version} by ${approval.by?.id ?? "unknown"}: ${approval.reason ?? ""}`;
|
|
31998
|
+
}
|
|
31999
|
+
}
|
|
31964
32000
|
function issueSummary(issue, events, references) {
|
|
31965
32001
|
const asks = issue.open_asks;
|
|
32002
|
+
const spec = issue.artifacts?.find((artifact) => artifact.primary);
|
|
32003
|
+
const specApproval = spec === undefined ? undefined : approvalLine(spec);
|
|
31966
32004
|
return [
|
|
31967
32005
|
`Title: ${issue.title}`,
|
|
31968
32006
|
`Key: ${issue.key}`,
|
|
31969
32007
|
`Status: ${issue.status}`,
|
|
31970
32008
|
`Route: ${issue.route ?? "none"}`,
|
|
32009
|
+
...specApproval === undefined ? [] : [`Spec ${specApproval.replace(/^Approval/, "approval")}`],
|
|
31971
32010
|
"Open asks:",
|
|
31972
32011
|
...asks.length === 0 ? ["- none"] : asks.map((ask) => `- ${ask.id}: ${ask.question}`),
|
|
31973
32012
|
"References:",
|
|
@@ -32294,16 +32333,42 @@ async function executeDispatchTool(input) {
|
|
|
32294
32333
|
const version = optionalNumber(args, "version") ?? ownerArguments.ref?.version;
|
|
32295
32334
|
const document = await client.docRead(resolved.artifact.id, version);
|
|
32296
32335
|
const marks = await openArtifactMarks(client, resolved);
|
|
32336
|
+
const approval = approvalLine(resolved.artifact);
|
|
32337
|
+
const trailer = [
|
|
32338
|
+
...marks.length === 0 ? [] : [`Open anchored asks/comments: ${marks.join(", ")}`],
|
|
32339
|
+
...approval === undefined ? [] : [approval]
|
|
32340
|
+
];
|
|
32297
32341
|
return {
|
|
32298
|
-
text:
|
|
32342
|
+
text: trailer.length === 0 ? document.markdown : `${document.markdown}
|
|
32299
32343
|
|
|
32300
|
-
|
|
32344
|
+
${trailer.join(`
|
|
32345
|
+
`)}`,
|
|
32301
32346
|
details: resolved.owner.kind === "project" ? {
|
|
32302
32347
|
project: resolved.artifact.project,
|
|
32303
32348
|
document: `${resolved.artifact.project}/${resolved.artifact.slug}`
|
|
32304
32349
|
} : { issue: resolved.issue?.key }
|
|
32305
32350
|
};
|
|
32306
32351
|
}
|
|
32352
|
+
case "dispatch_request_approval": {
|
|
32353
|
+
const artifactReference = optionalString(args, "artifact") ?? (ownerArguments.ref?.kind === "spec" || ownerArguments.ref?.kind === "artifact" ? ownerArguments.ref.id : undefined);
|
|
32354
|
+
const resolved = await resolveArtifact(client, documentOwner(), artifactReference);
|
|
32355
|
+
const result = await client.requestApproval(resolved.artifact.id, { actor });
|
|
32356
|
+
if (result.ask === null) {
|
|
32357
|
+
return {
|
|
32358
|
+
text: `${resolved.artifact.name} is already approved at version ${result.version} by ${result.approval.by?.id ?? "unknown"}; no new request was opened. An edit after approval makes it stale, so request again only for a new version.`,
|
|
32359
|
+
details: {
|
|
32360
|
+
...resolved.owner.kind === "project" ? documentResultDetails(resolved.artifact) : { issue: resolved.issue?.key },
|
|
32361
|
+
artifact: resolved.artifact.id,
|
|
32362
|
+
version: result.version
|
|
32363
|
+
}
|
|
32364
|
+
};
|
|
32365
|
+
}
|
|
32366
|
+
const details = await askResultDetails(client, result.ask, resolved);
|
|
32367
|
+
return {
|
|
32368
|
+
text: `Approval requested for ${resolved.artifact.name} at version ${result.version} (ask ${result.ask.id}). The answer arrives as artifact.approved or artifact.changes_requested; an edit after approval makes it stale, so request again for the new version.`,
|
|
32369
|
+
details: { ...details, artifact: resolved.artifact.id, version: result.version }
|
|
32370
|
+
};
|
|
32371
|
+
}
|
|
32307
32372
|
case "dispatch_artifact": {
|
|
32308
32373
|
const summary = optionalString(args, "summary");
|
|
32309
32374
|
const name = stringArg(args, "name");
|
|
@@ -32366,7 +32431,8 @@ Open anchored asks/comments: ${marks.join(", ")}`,
|
|
|
32366
32431
|
text: [
|
|
32367
32432
|
`Document: ${resolved.artifact.project} / ${resolved.artifact.name}`,
|
|
32368
32433
|
`Reference: dispatch://${resolved.artifact.project}/artifact/${resolved.artifact.slug}`,
|
|
32369
|
-
`Versions: ${resolved.artifact.versions.length}
|
|
32434
|
+
`Versions: ${resolved.artifact.versions.length}`,
|
|
32435
|
+
...approvalLine(resolved.artifact) === undefined ? [] : [approvalLine(resolved.artifact)]
|
|
32370
32436
|
].join(`
|
|
32371
32437
|
`),
|
|
32372
32438
|
details: {
|
package/dist/legion.js
CHANGED
|
@@ -29670,6 +29670,14 @@ var ArtifactVersionEventPayloadSchema = object({
|
|
|
29670
29670
|
version: object({ number: number2().optional(), summary: string2().nullish() }).optional(),
|
|
29671
29671
|
diff: string2().optional()
|
|
29672
29672
|
});
|
|
29673
|
+
var ArtifactReviewEventPayloadSchema = object({
|
|
29674
|
+
artifact_id: string2().optional(),
|
|
29675
|
+
name: string2().optional(),
|
|
29676
|
+
version: number2().int().optional(),
|
|
29677
|
+
actor: object({ kind: string2(), id: string2() }).passthrough().optional(),
|
|
29678
|
+
reason: string2().nullish(),
|
|
29679
|
+
ask_id: string2().nullish()
|
|
29680
|
+
});
|
|
29673
29681
|
var askEventPayloadFields = {
|
|
29674
29682
|
id: string2().optional(),
|
|
29675
29683
|
opened_event_id: number2().int().positive(),
|
|
@@ -29951,6 +29959,16 @@ var dispatchToolSpecs = [
|
|
|
29951
29959
|
}),
|
|
29952
29960
|
validation: documentOwnerValidation(true)
|
|
29953
29961
|
},
|
|
29962
|
+
{
|
|
29963
|
+
name: "dispatch_request_approval",
|
|
29964
|
+
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,
|
|
29965
|
+
arguments: (z) => ({
|
|
29966
|
+
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
29967
|
+
project: z.string().describe("Project key owning the document.").optional(),
|
|
29968
|
+
artifact: z.string().describe("Artifact slug or id; primary document by default for an issue.").optional()
|
|
29969
|
+
}),
|
|
29970
|
+
validation: documentOwnerValidation(true)
|
|
29971
|
+
},
|
|
29954
29972
|
{
|
|
29955
29973
|
name: "dispatch_artifact",
|
|
29956
29974
|
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}`,
|
|
@@ -31902,6 +31920,9 @@ class DispatchClient {
|
|
|
31902
31920
|
async resolveAsk(id, input) {
|
|
31903
31921
|
return this.#json("POST", ["api", "v1", "asks", id, "resolve"], input);
|
|
31904
31922
|
}
|
|
31923
|
+
async requestApproval(artifactID, input) {
|
|
31924
|
+
return this.#json("POST", ["api", "v1", "artifacts", artifactID, "approval-requests"], input);
|
|
31925
|
+
}
|
|
31905
31926
|
async editAsk(id, input) {
|
|
31906
31927
|
return this.#json("PATCH", ["api", "v1", "asks", id], input);
|
|
31907
31928
|
}
|
|
@@ -32387,13 +32408,31 @@ function toolActor(origin, input) {
|
|
|
32387
32408
|
}
|
|
32388
32409
|
};
|
|
32389
32410
|
}
|
|
32411
|
+
function approvalLine(artifact) {
|
|
32412
|
+
const approval = artifact.approval;
|
|
32413
|
+
if (approval === undefined || approval.state === "draft")
|
|
32414
|
+
return;
|
|
32415
|
+
switch (approval.state) {
|
|
32416
|
+
case "awaiting":
|
|
32417
|
+
return `Approval: awaiting (requested by ${approval.requested_by?.id ?? "unknown"}, ask ${approval.ask_id ?? "?"})`;
|
|
32418
|
+
case "approved":
|
|
32419
|
+
return `Approval: approved v${approval.version} by ${approval.by?.id ?? "unknown"}`;
|
|
32420
|
+
case "stale":
|
|
32421
|
+
return `Approval: approved v${approval.version} by ${approval.by?.id ?? "unknown"}, edited since (now v${approval.latest_version}) - request approval again`;
|
|
32422
|
+
case "changes_requested":
|
|
32423
|
+
return `Approval: changes requested on v${approval.version} by ${approval.by?.id ?? "unknown"}: ${approval.reason ?? ""}`;
|
|
32424
|
+
}
|
|
32425
|
+
}
|
|
32390
32426
|
function issueSummary(issue, events, references) {
|
|
32391
32427
|
const asks = issue.open_asks;
|
|
32428
|
+
const spec = issue.artifacts?.find((artifact) => artifact.primary);
|
|
32429
|
+
const specApproval = spec === undefined ? undefined : approvalLine(spec);
|
|
32392
32430
|
return [
|
|
32393
32431
|
`Title: ${issue.title}`,
|
|
32394
32432
|
`Key: ${issue.key}`,
|
|
32395
32433
|
`Status: ${issue.status}`,
|
|
32396
32434
|
`Route: ${issue.route ?? "none"}`,
|
|
32435
|
+
...specApproval === undefined ? [] : [`Spec ${specApproval.replace(/^Approval/, "approval")}`],
|
|
32397
32436
|
"Open asks:",
|
|
32398
32437
|
...asks.length === 0 ? ["- none"] : asks.map((ask) => `- ${ask.id}: ${ask.question}`),
|
|
32399
32438
|
"References:",
|
|
@@ -32720,16 +32759,42 @@ async function executeDispatchTool(input) {
|
|
|
32720
32759
|
const version = optionalNumber(args, "version") ?? ownerArguments.ref?.version;
|
|
32721
32760
|
const document = await client.docRead(resolved.artifact.id, version);
|
|
32722
32761
|
const marks = await openArtifactMarks(client, resolved);
|
|
32762
|
+
const approval = approvalLine(resolved.artifact);
|
|
32763
|
+
const trailer = [
|
|
32764
|
+
...marks.length === 0 ? [] : [`Open anchored asks/comments: ${marks.join(", ")}`],
|
|
32765
|
+
...approval === undefined ? [] : [approval]
|
|
32766
|
+
];
|
|
32723
32767
|
return {
|
|
32724
|
-
text:
|
|
32768
|
+
text: trailer.length === 0 ? document.markdown : `${document.markdown}
|
|
32725
32769
|
|
|
32726
|
-
|
|
32770
|
+
${trailer.join(`
|
|
32771
|
+
`)}`,
|
|
32727
32772
|
details: resolved.owner.kind === "project" ? {
|
|
32728
32773
|
project: resolved.artifact.project,
|
|
32729
32774
|
document: `${resolved.artifact.project}/${resolved.artifact.slug}`
|
|
32730
32775
|
} : { issue: resolved.issue?.key }
|
|
32731
32776
|
};
|
|
32732
32777
|
}
|
|
32778
|
+
case "dispatch_request_approval": {
|
|
32779
|
+
const artifactReference = optionalString(args, "artifact") ?? (ownerArguments.ref?.kind === "spec" || ownerArguments.ref?.kind === "artifact" ? ownerArguments.ref.id : undefined);
|
|
32780
|
+
const resolved = await resolveArtifact(client, documentOwner(), artifactReference);
|
|
32781
|
+
const result = await client.requestApproval(resolved.artifact.id, { actor });
|
|
32782
|
+
if (result.ask === null) {
|
|
32783
|
+
return {
|
|
32784
|
+
text: `${resolved.artifact.name} is already approved at version ${result.version} by ${result.approval.by?.id ?? "unknown"}; no new request was opened. An edit after approval makes it stale, so request again only for a new version.`,
|
|
32785
|
+
details: {
|
|
32786
|
+
...resolved.owner.kind === "project" ? documentResultDetails(resolved.artifact) : { issue: resolved.issue?.key },
|
|
32787
|
+
artifact: resolved.artifact.id,
|
|
32788
|
+
version: result.version
|
|
32789
|
+
}
|
|
32790
|
+
};
|
|
32791
|
+
}
|
|
32792
|
+
const details = await askResultDetails(client, result.ask, resolved);
|
|
32793
|
+
return {
|
|
32794
|
+
text: `Approval requested for ${resolved.artifact.name} at version ${result.version} (ask ${result.ask.id}). The answer arrives as artifact.approved or artifact.changes_requested; an edit after approval makes it stale, so request again for the new version.`,
|
|
32795
|
+
details: { ...details, artifact: resolved.artifact.id, version: result.version }
|
|
32796
|
+
};
|
|
32797
|
+
}
|
|
32733
32798
|
case "dispatch_artifact": {
|
|
32734
32799
|
const summary = optionalString(args, "summary");
|
|
32735
32800
|
const name = stringArg(args, "name");
|
|
@@ -32792,7 +32857,8 @@ Open anchored asks/comments: ${marks.join(", ")}`,
|
|
|
32792
32857
|
text: [
|
|
32793
32858
|
`Document: ${resolved.artifact.project} / ${resolved.artifact.name}`,
|
|
32794
32859
|
`Reference: dispatch://${resolved.artifact.project}/artifact/${resolved.artifact.slug}`,
|
|
32795
|
-
`Versions: ${resolved.artifact.versions.length}
|
|
32860
|
+
`Versions: ${resolved.artifact.versions.length}`,
|
|
32861
|
+
...approvalLine(resolved.artifact) === undefined ? [] : [approvalLine(resolved.artifact)]
|
|
32796
32862
|
].join(`
|
|
32797
32863
|
`),
|
|
32798
32864
|
details: {
|
|
@@ -123,6 +123,22 @@ clarification, not an answer: the human did not understand the question or needs
|
|
|
123
123
|
in their Inbox. Answer in the same thread with `dispatch_comment({ reply_to_ask })`, or reword the question itself with
|
|
124
124
|
`dispatch_edit_ask` when the wording was the problem; either puts the ask back in front of them. Do not open a second ask.
|
|
125
125
|
|
|
126
|
+
## Approval of a spec
|
|
127
|
+
|
|
128
|
+
Approval is a property of a document, not a question you phrase: a human approves a specific version, the way a pull-request
|
|
129
|
+
review approves a commit, and any later edit makes that approval stale. It is the exception, not a step for every issue - reach
|
|
130
|
+
for it when a spec departs from what the human already settled, proposes children, or when the project has armed a design gate.
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
dispatch_request_approval({ issue?, project?, artifact? })
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Opens (or returns the open) approval ask for the document at its latest version - options `Approve` and `Request changes`, in
|
|
137
|
+
the human's Inbox like any ask. The answer reaches you as `artifact.approved` or `artifact.changes_requested` with the pinned
|
|
138
|
+
`version`; `changes_requested` carries the reason, which is your next piece of work. `dispatch_read` and `dispatch_doc_read` show
|
|
139
|
+
the document's approval state; `stale` means it was approved and then edited - request again for the new version. Never write
|
|
140
|
+
"Approve" options into an ordinary `dispatch_ask`, and never approve anything yourself: only humans review.
|
|
141
|
+
|
|
126
142
|
## The Spec
|
|
127
143
|
|
|
128
144
|
The spec holds requirements, design, acceptance, decisions, and rejected alternatives, structured per [Writing a spec](#writing-a-spec).
|