@sjawhar/pi-legion-envoy 0.22.0 → 0.24.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/README.md +3 -1
- package/dist/envoy.js +33 -37
- package/dist/legion.js +33 -37
- package/dist/skills/dispatch/SKILL.md +12 -5
- package/dist/skills/legion-architect/SKILL.md +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -98,7 +98,9 @@ Configure the shared `envoy.json` with:
|
|
|
98
98
|
|
|
99
99
|
The user file is `~/.config/opencode/envoy.json`; a
|
|
100
100
|
`<cwd>/.opencode/envoy.json` file shallow-merges over it. `DISPATCH_URL` and
|
|
101
|
-
`DISPATCH_TOKEN` override the file values for one process.
|
|
101
|
+
`DISPATCH_TOKEN` override the file values for one process. Omitting
|
|
102
|
+
`dispatch.serverUrl` while `dispatch.enabled` is true targets
|
|
103
|
+
`http://localhost:8766`, the Go server's listen address. Invalid configuration,
|
|
102
104
|
an invalid URL, or an empty token leaves the nine tools unavailable and reports
|
|
103
105
|
the source of the error.
|
|
104
106
|
|
package/dist/envoy.js
CHANGED
|
@@ -29677,6 +29677,8 @@ var CommentEventPayloadSchema = object({
|
|
|
29677
29677
|
artifact_name: string2().optional(),
|
|
29678
29678
|
body: string2().optional(),
|
|
29679
29679
|
reply_to: string2().nullish(),
|
|
29680
|
+
ask_id: string2().nullish(),
|
|
29681
|
+
ask_question: string2().optional(),
|
|
29680
29682
|
anchor: object({ quote: string2().optional() }).nullish(),
|
|
29681
29683
|
suggestion: object({ replace_with: string2().optional() }).nullish()
|
|
29682
29684
|
});
|
|
@@ -29727,14 +29729,15 @@ var dispatchToolSpecs = [
|
|
|
29727
29729
|
},
|
|
29728
29730
|
{
|
|
29729
29731
|
name: "dispatch_comment",
|
|
29730
|
-
description: "Add review feedback to an issue or document quote. Do not use it for an exact replacement; use " + `dispatch_suggest instead. Body is at most 2,000 characters. ${ISSUE_REFERENCE}`,
|
|
29732
|
+
description: "Add review feedback to an issue or document quote, or reply to a question asked with dispatch_ask. " + "Do not use it for an exact replacement; use " + `dispatch_suggest instead. Body is at most 2,000 characters. ${ISSUE_REFERENCE}`,
|
|
29731
29733
|
arguments: (z) => ({
|
|
29732
29734
|
issue: z.string().describe(ISSUE_REFERENCE),
|
|
29733
29735
|
artifact: z.string().describe("Artifact slug or id required when quote is given.").optional(),
|
|
29734
29736
|
quote: z.string().describe("Optional exact quoted document text.").optional(),
|
|
29735
29737
|
occurrence: z.number({ int: true, min: 0 }).describe("Optional zero-based occurrence of quote.").optional(),
|
|
29736
29738
|
body: z.string({ max: 2000 }).describe("Review comment, at most 2,000 characters."),
|
|
29737
|
-
reply_to: z.string().describe("Optional comment id to reply to.").optional()
|
|
29739
|
+
reply_to: z.string().describe("Optional comment id to reply to.").optional(),
|
|
29740
|
+
reply_to_ask: z.string().describe("Optional ask id to reply to, threading this comment under that question. Mutually " + "exclusive with reply_to.").optional()
|
|
29738
29741
|
})
|
|
29739
29742
|
},
|
|
29740
29743
|
{
|
|
@@ -30065,25 +30068,10 @@ var LegionDaemonApi = {
|
|
|
30065
30068
|
request: architectCapability.extend({ issue: nonEmptyString, comment: string2().optional() }),
|
|
30066
30069
|
response: object({})
|
|
30067
30070
|
},
|
|
30068
|
-
SpawnToken: {
|
|
30069
|
-
request: architectCapability.extend({ issue: nonEmptyString, role: legionRole }),
|
|
30070
|
-
response: object({ spawnToken: nonEmptyString })
|
|
30071
|
-
},
|
|
30072
30071
|
ProvisioningCredential: {
|
|
30073
30072
|
request: architectCapability.extend({ issue: nonEmptyString }),
|
|
30074
30073
|
response: object({ token: nonEmptyString })
|
|
30075
30074
|
},
|
|
30076
|
-
RoleBacking: {
|
|
30077
|
-
request: strictObject({
|
|
30078
|
-
tree: nonEmptyString,
|
|
30079
|
-
issue: nonEmptyString,
|
|
30080
|
-
role: legionRole,
|
|
30081
|
-
sessionId: nonEmptyString,
|
|
30082
|
-
agentId: nonEmptyString,
|
|
30083
|
-
spawnToken: nonEmptyString
|
|
30084
|
-
}),
|
|
30085
|
-
response: object({})
|
|
30086
|
-
},
|
|
30087
30075
|
WorkerStarted: {
|
|
30088
30076
|
request: strictObject({
|
|
30089
30077
|
tree: nonEmptyString,
|
|
@@ -30130,20 +30118,6 @@ var LegionDaemonApi = {
|
|
|
30130
30118
|
roleToken: nonEmptyString
|
|
30131
30119
|
})
|
|
30132
30120
|
},
|
|
30133
|
-
Phase: {
|
|
30134
|
-
request: strictObject({
|
|
30135
|
-
tree: nonEmptyString,
|
|
30136
|
-
issue: nonEmptyString,
|
|
30137
|
-
phase: nonEmptyString,
|
|
30138
|
-
sessionId: nonEmptyString,
|
|
30139
|
-
spawnToken: nonEmptyString
|
|
30140
|
-
}),
|
|
30141
|
-
response: object({
|
|
30142
|
-
secret: nonEmptyString,
|
|
30143
|
-
gitName: nonEmptyString,
|
|
30144
|
-
gitEmail: nonEmptyString
|
|
30145
|
-
})
|
|
30146
|
-
},
|
|
30147
30121
|
WorkerSession: {
|
|
30148
30122
|
request: strictObject({
|
|
30149
30123
|
sessionId: nonEmptyString,
|
|
@@ -30838,6 +30812,12 @@ function dispatchPayload(event) {
|
|
|
30838
30812
|
const parsed = schema.safeParse(event.payload);
|
|
30839
30813
|
return parsed.success ? parsed.data : event.payload;
|
|
30840
30814
|
}
|
|
30815
|
+
function dispatchAskQuestion(event) {
|
|
30816
|
+
if (event.type !== "comment.created")
|
|
30817
|
+
return;
|
|
30818
|
+
const parsed = CommentEventPayloadSchema.safeParse(event.payload);
|
|
30819
|
+
return parsed.success && parsed.data.ask_question !== "" ? parsed.data.ask_question : undefined;
|
|
30820
|
+
}
|
|
30841
30821
|
function parseDispatchFrame(rawPayload) {
|
|
30842
30822
|
let value;
|
|
30843
30823
|
try {
|
|
@@ -30887,6 +30867,7 @@ function renderInbound(raw, sessionID, subject) {
|
|
|
30887
30867
|
}
|
|
30888
30868
|
let dispatchEvent;
|
|
30889
30869
|
let dispatchIssue;
|
|
30870
|
+
let askQuestion;
|
|
30890
30871
|
const dispatchRendered = envelope.source === "dispatch" && envelope.payload !== undefined;
|
|
30891
30872
|
if (envelope.source === "dispatch") {
|
|
30892
30873
|
if (envelope.payload === undefined) {
|
|
@@ -30900,6 +30881,7 @@ function renderInbound(raw, sessionID, subject) {
|
|
|
30900
30881
|
if (frame.event.actor.kind === "session" && frame.event.actor.id === sessionID) {
|
|
30901
30882
|
return { skip: true, content: "", envelope };
|
|
30902
30883
|
}
|
|
30884
|
+
askQuestion = dispatchAskQuestion(frame.event);
|
|
30903
30885
|
dispatchEvent = {
|
|
30904
30886
|
issue_key: frame.event.issue_key,
|
|
30905
30887
|
type: frame.event.type,
|
|
@@ -30947,7 +30929,7 @@ ${envelope.payload ?? ""}`;
|
|
|
30947
30929
|
...envelope.expires_at === undefined ? {} : { by: inboundTimestamp(envelope.expires_at) },
|
|
30948
30930
|
...envelope.urgency === undefined ? {} : { urgency: envelope.urgency },
|
|
30949
30931
|
...envelope.expects_reply === undefined ? {} : { expects_reply: envelope.expects_reply },
|
|
30950
|
-
...envelope.in_reply_to === undefined ? {} : { re: envelope.in_reply_to },
|
|
30932
|
+
...envelope.in_reply_to === undefined ? {} : { re: askQuestion ?? envelope.in_reply_to },
|
|
30951
30933
|
...envelope.supersedes === undefined ? {} : { supersedes: envelope.supersedes },
|
|
30952
30934
|
...reply === undefined ? {} : { reply_with: reply },
|
|
30953
30935
|
...role === undefined ? {} : {
|
|
@@ -31500,8 +31482,12 @@ function childrenSummary(issue) {
|
|
|
31500
31482
|
].join(`
|
|
31501
31483
|
`);
|
|
31502
31484
|
}
|
|
31503
|
-
function askSummary(ask) {
|
|
31485
|
+
function askSummary({ ask, replies }) {
|
|
31504
31486
|
const answer = ask.answer;
|
|
31487
|
+
const chain = replies.flatMap((reply) => [
|
|
31488
|
+
`${reply.id} \xB7 ${reply.author.kind} ${reply.author.id}`,
|
|
31489
|
+
`Body: ${reply.body}`
|
|
31490
|
+
]);
|
|
31505
31491
|
return [
|
|
31506
31492
|
`Question: ${ask.question}`,
|
|
31507
31493
|
"Options:",
|
|
@@ -31512,7 +31498,9 @@ function askSummary(ask) {
|
|
|
31512
31498
|
`- By: ${answer.user}`,
|
|
31513
31499
|
`- Selected: ${answer.selected.length === 0 ? "none" : answer.selected.join(", ")}`,
|
|
31514
31500
|
...answer.text === null ? [] : [`- Text: ${answer.text}`]
|
|
31515
|
-
]
|
|
31501
|
+
],
|
|
31502
|
+
"Replies:",
|
|
31503
|
+
...chain.length === 0 ? ["- none"] : chain
|
|
31516
31504
|
].join(`
|
|
31517
31505
|
`);
|
|
31518
31506
|
}
|
|
@@ -31611,10 +31599,15 @@ async function executeDispatchTool(input) {
|
|
|
31611
31599
|
const resolved = artifactReference ? await resolveArtifact(client, issue(), artifactReference) : undefined;
|
|
31612
31600
|
const anchored = resolved ? anchor(resolved.artifact, args) : undefined;
|
|
31613
31601
|
const replyTo = optionalString(args, "reply_to");
|
|
31602
|
+
const replyToAsk = optionalString(args, "reply_to_ask");
|
|
31603
|
+
if (replyTo !== undefined && replyToAsk !== undefined) {
|
|
31604
|
+
throw new Error("reply_to and reply_to_ask cannot both be set");
|
|
31605
|
+
}
|
|
31614
31606
|
const comment = await client.comment(issue(), {
|
|
31615
31607
|
body: stringArg(args, "body"),
|
|
31616
31608
|
...anchored === undefined ? {} : { anchor: anchored },
|
|
31617
31609
|
...replyTo === undefined ? {} : { reply_to: replyTo },
|
|
31610
|
+
...replyToAsk === undefined ? {} : { ask_id: replyToAsk },
|
|
31618
31611
|
actor
|
|
31619
31612
|
});
|
|
31620
31613
|
return {
|
|
@@ -31723,10 +31716,13 @@ Open anchored asks/comments: ${marks.join(", ")}`,
|
|
|
31723
31716
|
}
|
|
31724
31717
|
case "dispatch_read": {
|
|
31725
31718
|
if (issueArguments.ref?.kind === "ask") {
|
|
31726
|
-
const
|
|
31719
|
+
const askRead = await client.getAsk(issueArguments.ref.id);
|
|
31727
31720
|
return {
|
|
31728
|
-
text: askSummary(
|
|
31729
|
-
details: {
|
|
31721
|
+
text: askSummary(askRead),
|
|
31722
|
+
details: {
|
|
31723
|
+
issue: askRead.ask.issue_key,
|
|
31724
|
+
topic: dispatchIssueSubject(askRead.ask.issue_key, ">")
|
|
31725
|
+
}
|
|
31730
31726
|
};
|
|
31731
31727
|
}
|
|
31732
31728
|
if (issueArguments.ref?.kind === "comment") {
|
package/dist/legion.js
CHANGED
|
@@ -29675,6 +29675,8 @@ var CommentEventPayloadSchema = object({
|
|
|
29675
29675
|
artifact_name: string2().optional(),
|
|
29676
29676
|
body: string2().optional(),
|
|
29677
29677
|
reply_to: string2().nullish(),
|
|
29678
|
+
ask_id: string2().nullish(),
|
|
29679
|
+
ask_question: string2().optional(),
|
|
29678
29680
|
anchor: object({ quote: string2().optional() }).nullish(),
|
|
29679
29681
|
suggestion: object({ replace_with: string2().optional() }).nullish()
|
|
29680
29682
|
});
|
|
@@ -29725,14 +29727,15 @@ var dispatchToolSpecs = [
|
|
|
29725
29727
|
},
|
|
29726
29728
|
{
|
|
29727
29729
|
name: "dispatch_comment",
|
|
29728
|
-
description: "Add review feedback to an issue or document quote. Do not use it for an exact replacement; use " + `dispatch_suggest instead. Body is at most 2,000 characters. ${ISSUE_REFERENCE}`,
|
|
29730
|
+
description: "Add review feedback to an issue or document quote, or reply to a question asked with dispatch_ask. " + "Do not use it for an exact replacement; use " + `dispatch_suggest instead. Body is at most 2,000 characters. ${ISSUE_REFERENCE}`,
|
|
29729
29731
|
arguments: (z) => ({
|
|
29730
29732
|
issue: z.string().describe(ISSUE_REFERENCE),
|
|
29731
29733
|
artifact: z.string().describe("Artifact slug or id required when quote is given.").optional(),
|
|
29732
29734
|
quote: z.string().describe("Optional exact quoted document text.").optional(),
|
|
29733
29735
|
occurrence: z.number({ int: true, min: 0 }).describe("Optional zero-based occurrence of quote.").optional(),
|
|
29734
29736
|
body: z.string({ max: 2000 }).describe("Review comment, at most 2,000 characters."),
|
|
29735
|
-
reply_to: z.string().describe("Optional comment id to reply to.").optional()
|
|
29737
|
+
reply_to: z.string().describe("Optional comment id to reply to.").optional(),
|
|
29738
|
+
reply_to_ask: z.string().describe("Optional ask id to reply to, threading this comment under that question. Mutually " + "exclusive with reply_to.").optional()
|
|
29736
29739
|
})
|
|
29737
29740
|
},
|
|
29738
29741
|
{
|
|
@@ -30063,25 +30066,10 @@ var LegionDaemonApi = {
|
|
|
30063
30066
|
request: architectCapability.extend({ issue: nonEmptyString, comment: string2().optional() }),
|
|
30064
30067
|
response: object({})
|
|
30065
30068
|
},
|
|
30066
|
-
SpawnToken: {
|
|
30067
|
-
request: architectCapability.extend({ issue: nonEmptyString, role: legionRole }),
|
|
30068
|
-
response: object({ spawnToken: nonEmptyString })
|
|
30069
|
-
},
|
|
30070
30069
|
ProvisioningCredential: {
|
|
30071
30070
|
request: architectCapability.extend({ issue: nonEmptyString }),
|
|
30072
30071
|
response: object({ token: nonEmptyString })
|
|
30073
30072
|
},
|
|
30074
|
-
RoleBacking: {
|
|
30075
|
-
request: strictObject({
|
|
30076
|
-
tree: nonEmptyString,
|
|
30077
|
-
issue: nonEmptyString,
|
|
30078
|
-
role: legionRole,
|
|
30079
|
-
sessionId: nonEmptyString,
|
|
30080
|
-
agentId: nonEmptyString,
|
|
30081
|
-
spawnToken: nonEmptyString
|
|
30082
|
-
}),
|
|
30083
|
-
response: object({})
|
|
30084
|
-
},
|
|
30085
30073
|
WorkerStarted: {
|
|
30086
30074
|
request: strictObject({
|
|
30087
30075
|
tree: nonEmptyString,
|
|
@@ -30128,20 +30116,6 @@ var LegionDaemonApi = {
|
|
|
30128
30116
|
roleToken: nonEmptyString
|
|
30129
30117
|
})
|
|
30130
30118
|
},
|
|
30131
|
-
Phase: {
|
|
30132
|
-
request: strictObject({
|
|
30133
|
-
tree: nonEmptyString,
|
|
30134
|
-
issue: nonEmptyString,
|
|
30135
|
-
phase: nonEmptyString,
|
|
30136
|
-
sessionId: nonEmptyString,
|
|
30137
|
-
spawnToken: nonEmptyString
|
|
30138
|
-
}),
|
|
30139
|
-
response: object({
|
|
30140
|
-
secret: nonEmptyString,
|
|
30141
|
-
gitName: nonEmptyString,
|
|
30142
|
-
gitEmail: nonEmptyString
|
|
30143
|
-
})
|
|
30144
|
-
},
|
|
30145
30119
|
WorkerSession: {
|
|
30146
30120
|
request: strictObject({
|
|
30147
30121
|
sessionId: nonEmptyString,
|
|
@@ -31330,6 +31304,12 @@ function dispatchPayload(event) {
|
|
|
31330
31304
|
const parsed = schema.safeParse(event.payload);
|
|
31331
31305
|
return parsed.success ? parsed.data : event.payload;
|
|
31332
31306
|
}
|
|
31307
|
+
function dispatchAskQuestion(event) {
|
|
31308
|
+
if (event.type !== "comment.created")
|
|
31309
|
+
return;
|
|
31310
|
+
const parsed = CommentEventPayloadSchema.safeParse(event.payload);
|
|
31311
|
+
return parsed.success && parsed.data.ask_question !== "" ? parsed.data.ask_question : undefined;
|
|
31312
|
+
}
|
|
31333
31313
|
function parseDispatchFrame(rawPayload) {
|
|
31334
31314
|
let value;
|
|
31335
31315
|
try {
|
|
@@ -31379,6 +31359,7 @@ function renderInbound(raw, sessionID, subject) {
|
|
|
31379
31359
|
}
|
|
31380
31360
|
let dispatchEvent;
|
|
31381
31361
|
let dispatchIssue;
|
|
31362
|
+
let askQuestion;
|
|
31382
31363
|
const dispatchRendered = envelope.source === "dispatch" && envelope.payload !== undefined;
|
|
31383
31364
|
if (envelope.source === "dispatch") {
|
|
31384
31365
|
if (envelope.payload === undefined) {
|
|
@@ -31392,6 +31373,7 @@ function renderInbound(raw, sessionID, subject) {
|
|
|
31392
31373
|
if (frame.event.actor.kind === "session" && frame.event.actor.id === sessionID) {
|
|
31393
31374
|
return { skip: true, content: "", envelope };
|
|
31394
31375
|
}
|
|
31376
|
+
askQuestion = dispatchAskQuestion(frame.event);
|
|
31395
31377
|
dispatchEvent = {
|
|
31396
31378
|
issue_key: frame.event.issue_key,
|
|
31397
31379
|
type: frame.event.type,
|
|
@@ -31439,7 +31421,7 @@ ${envelope.payload ?? ""}`;
|
|
|
31439
31421
|
...envelope.expires_at === undefined ? {} : { by: inboundTimestamp(envelope.expires_at) },
|
|
31440
31422
|
...envelope.urgency === undefined ? {} : { urgency: envelope.urgency },
|
|
31441
31423
|
...envelope.expects_reply === undefined ? {} : { expects_reply: envelope.expects_reply },
|
|
31442
|
-
...envelope.in_reply_to === undefined ? {} : { re: envelope.in_reply_to },
|
|
31424
|
+
...envelope.in_reply_to === undefined ? {} : { re: askQuestion ?? envelope.in_reply_to },
|
|
31443
31425
|
...envelope.supersedes === undefined ? {} : { supersedes: envelope.supersedes },
|
|
31444
31426
|
...reply === undefined ? {} : { reply_with: reply },
|
|
31445
31427
|
...role === undefined ? {} : {
|
|
@@ -31985,8 +31967,12 @@ function childrenSummary(issue) {
|
|
|
31985
31967
|
].join(`
|
|
31986
31968
|
`);
|
|
31987
31969
|
}
|
|
31988
|
-
function askSummary(ask) {
|
|
31970
|
+
function askSummary({ ask, replies }) {
|
|
31989
31971
|
const answer = ask.answer;
|
|
31972
|
+
const chain = replies.flatMap((reply) => [
|
|
31973
|
+
`${reply.id} \xB7 ${reply.author.kind} ${reply.author.id}`,
|
|
31974
|
+
`Body: ${reply.body}`
|
|
31975
|
+
]);
|
|
31990
31976
|
return [
|
|
31991
31977
|
`Question: ${ask.question}`,
|
|
31992
31978
|
"Options:",
|
|
@@ -31997,7 +31983,9 @@ function askSummary(ask) {
|
|
|
31997
31983
|
`- By: ${answer.user}`,
|
|
31998
31984
|
`- Selected: ${answer.selected.length === 0 ? "none" : answer.selected.join(", ")}`,
|
|
31999
31985
|
...answer.text === null ? [] : [`- Text: ${answer.text}`]
|
|
32000
|
-
]
|
|
31986
|
+
],
|
|
31987
|
+
"Replies:",
|
|
31988
|
+
...chain.length === 0 ? ["- none"] : chain
|
|
32001
31989
|
].join(`
|
|
32002
31990
|
`);
|
|
32003
31991
|
}
|
|
@@ -32096,10 +32084,15 @@ async function executeDispatchTool(input) {
|
|
|
32096
32084
|
const resolved = artifactReference ? await resolveArtifact(client, issue(), artifactReference) : undefined;
|
|
32097
32085
|
const anchored = resolved ? anchor(resolved.artifact, args) : undefined;
|
|
32098
32086
|
const replyTo = optionalString(args, "reply_to");
|
|
32087
|
+
const replyToAsk = optionalString(args, "reply_to_ask");
|
|
32088
|
+
if (replyTo !== undefined && replyToAsk !== undefined) {
|
|
32089
|
+
throw new Error("reply_to and reply_to_ask cannot both be set");
|
|
32090
|
+
}
|
|
32099
32091
|
const comment = await client.comment(issue(), {
|
|
32100
32092
|
body: stringArg(args, "body"),
|
|
32101
32093
|
...anchored === undefined ? {} : { anchor: anchored },
|
|
32102
32094
|
...replyTo === undefined ? {} : { reply_to: replyTo },
|
|
32095
|
+
...replyToAsk === undefined ? {} : { ask_id: replyToAsk },
|
|
32103
32096
|
actor
|
|
32104
32097
|
});
|
|
32105
32098
|
return {
|
|
@@ -32208,10 +32201,13 @@ Open anchored asks/comments: ${marks.join(", ")}`,
|
|
|
32208
32201
|
}
|
|
32209
32202
|
case "dispatch_read": {
|
|
32210
32203
|
if (issueArguments.ref?.kind === "ask") {
|
|
32211
|
-
const
|
|
32204
|
+
const askRead = await client.getAsk(issueArguments.ref.id);
|
|
32212
32205
|
return {
|
|
32213
|
-
text: askSummary(
|
|
32214
|
-
details: {
|
|
32206
|
+
text: askSummary(askRead),
|
|
32207
|
+
details: {
|
|
32208
|
+
issue: askRead.ask.issue_key,
|
|
32209
|
+
topic: dispatchIssueSubject(askRead.ask.issue_key, ">")
|
|
32210
|
+
}
|
|
32215
32211
|
};
|
|
32216
32212
|
}
|
|
32217
32213
|
if (issueArguments.ref?.kind === "comment") {
|
|
@@ -46,6 +46,12 @@ Anchor a document question with `anchor: { artifact, quote, occurrence? }`; `occ
|
|
|
46
46
|
zero-based and selects a repeated quote. An anchor whose quote disappears becomes orphaned but
|
|
47
47
|
remains readable against its original document version. An ask stays open until a human answers;
|
|
48
48
|
you cannot withdraw it. Ask once and well. The host steers you when a human answers.
|
|
49
|
+
|
|
50
|
+
An ask is a thread, not a dead end: a human can reply to it before or after answering, and you
|
|
51
|
+
(the asker) can reply too — e.g. acknowledging a clarifying question, or following up after the
|
|
52
|
+
answer. Use `reply_to_ask` on `dispatch_comment` to reply under your own ask; it is mutually
|
|
53
|
+
exclusive with `reply_to`.
|
|
54
|
+
|
|
49
55
|
## The spec is where narrative goes
|
|
50
56
|
|
|
51
57
|
Read the current document before changing it:
|
|
@@ -85,11 +91,12 @@ message.
|
|
|
85
91
|
Add feedback with:
|
|
86
92
|
|
|
87
93
|
```ts
|
|
88
|
-
dispatch_comment({ issue, artifact?, quote?, occurrence?, body, reply_to? })
|
|
94
|
+
dispatch_comment({ issue, artifact?, quote?, occurrence?, body, reply_to?, reply_to_ask? })
|
|
89
95
|
```
|
|
90
96
|
|
|
91
97
|
It returns `details` `{ issue, topic, comment }`. `quote` requires `artifact`; omit both for a
|
|
92
|
-
floating issue comment. Use `reply_to` to continue a comment
|
|
98
|
+
floating issue comment. Use `reply_to` to continue a comment thread; use `reply_to_ask` to reply
|
|
99
|
+
directly under a question asked with `dispatch_ask`. The two are mutually exclusive.
|
|
93
100
|
|
|
94
101
|
Propose an exact replacement instead of describing it:
|
|
95
102
|
|
|
@@ -149,9 +156,9 @@ dispatch_read({ issue?, ref? })
|
|
|
149
156
|
```
|
|
150
157
|
|
|
151
158
|
With an issue ref, it returns the issue summary, open asks, and recent events with `details`
|
|
152
|
-
`{ issue }`. With an ask ref, it returns that ask's question, options, state,
|
|
153
|
-
comment ref, it returns that comment and its quoted reply chain. Use
|
|
154
|
-
document contents.
|
|
159
|
+
`{ issue }`. With an ask ref, it returns that ask's question, options, state, answer, and its
|
|
160
|
+
reply thread. With a comment ref, it returns that comment and its quoted reply chain. Use
|
|
161
|
+
`dispatch_doc_read` for document contents.
|
|
155
162
|
|
|
156
163
|
## References
|
|
157
164
|
|
|
@@ -213,7 +213,7 @@ corresponding lifecycle procedure.
|
|
|
213
213
|
| `pr-closed-unmerged` | Decide from current scope whether to reopen the work, send a fresh implementer, or cancel it with a reason. Delegate the repository action to the responsible phase worker and keep ownership. |
|
|
214
214
|
| `issue-comment` | Interpret the comment in the issue's design context. Answer it, adjust the plan, or relay it via `envoy_publish` to the responsible worker's role token; scope and product decisions remain with you. |
|
|
215
215
|
| `catchup-overseer` | Verify its gates, child counts, and PR verdicts against current artifacts, then resume the applicable numbered lifecycle step. It is a current-state snapshot, not a raw-event replay. For each entry in its `phaseCompletions` (`{issue, role, summary, at}`, phases that completed while you were not live), handle it exactly as a `phase-complete` wake. |
|
|
216
|
-
| `
|
|
216
|
+
| `worker-died` | Payload `{type:"worker-died", issue, role}`. The daemon probed and retried this role's worker through `MAX_LAUNCH_FAILURES` attempts and could not confirm a boot — never a raw-event replay or a silent revive. Reassess the work and `spawn_worker` again for the role (it resumes the same agent via `--resume` if a session file survived) or reassign it if the failure looks environmental, not agent-specific. |
|
|
217
217
|
| `reopened` | Reopen the root lifecycle: inspect the reason and current artifacts, reassess scope and children, and resume at the first applicable numbered step. |
|
|
218
218
|
|
|
219
219
|
## Escalation judgment
|