@sjawhar/pi-legion-envoy 1.9.0 → 1.11.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 +170 -19
- package/dist/legion.js +170 -19
- package/dist/skills/dispatch/SKILL.md +38 -1
- package/package.json +1 -1
package/dist/envoy.js
CHANGED
|
@@ -29730,10 +29730,20 @@ var CommentEventPayloadSchema = object({
|
|
|
29730
29730
|
var MessageEventPayloadSchema = object({
|
|
29731
29731
|
id: string2().optional(),
|
|
29732
29732
|
body: string2().optional(),
|
|
29733
|
-
|
|
29733
|
+
target: string2().nullish(),
|
|
29734
|
+
in_reply_to: string2().nullish(),
|
|
29734
29735
|
reply_body: string2().optional(),
|
|
29735
29736
|
author: object({ kind: string2(), id: string2() }).optional()
|
|
29736
29737
|
});
|
|
29738
|
+
var MessageDeliveryEventPayloadSchema = object({
|
|
29739
|
+
message_id: string2().optional(),
|
|
29740
|
+
attempt: number2().int().positive().optional(),
|
|
29741
|
+
delivery: _enum2(["btw", "aside", "steer"]).optional(),
|
|
29742
|
+
session_id: string2().optional(),
|
|
29743
|
+
title: string2().optional(),
|
|
29744
|
+
state: _enum2(["sent", "failed"]).optional(),
|
|
29745
|
+
error: string2().optional()
|
|
29746
|
+
});
|
|
29737
29747
|
var ChildStatusEventPayloadSchema = object({
|
|
29738
29748
|
child_key: string2().optional(),
|
|
29739
29749
|
from: string2().optional(),
|
|
@@ -29827,7 +29837,7 @@ var SPEC_SECTIONS = [
|
|
|
29827
29837
|
];
|
|
29828
29838
|
var SPEC_WRITING_GUIDANCE = `When writing a spec, use these sections in order: ${SPEC_SECTIONS.join(", ")}. ` + "Write for a reader who has not seen the code: plain sentences, every identifier expanded on " + "first use, no coined shorthand; see skills/dispatch Writing for the human and Writing a spec.";
|
|
29829
29839
|
var ASK_URGENCIES = ["low", "med", "high", "blocking"];
|
|
29830
|
-
var DOC_EDIT_OPS = ["replace", "delete", "insert"];
|
|
29840
|
+
var DOC_EDIT_OPS = ["replace", "delete", "insert", "retype"];
|
|
29831
29841
|
var dispatchToolSpecs = [
|
|
29832
29842
|
{
|
|
29833
29843
|
name: "dispatch_issue",
|
|
@@ -29932,12 +29942,12 @@ var dispatchToolSpecs = [
|
|
|
29932
29942
|
arguments: (z) => ({
|
|
29933
29943
|
issue: z.string().describe(ISSUE_REFERENCE),
|
|
29934
29944
|
body: z.string({ max: 2000 }).describe("Update text, at most 2,000 characters."),
|
|
29935
|
-
|
|
29945
|
+
in_reply_to: z.string().describe("Optional message id or dispatch://KEY/message/<id> reference to reply to, threading " + "this message under it so the reply stays with the original in the Conversation.").optional()
|
|
29936
29946
|
})
|
|
29937
29947
|
},
|
|
29938
29948
|
{
|
|
29939
29949
|
name: "dispatch_doc_edit",
|
|
29940
|
-
description: "Apply deterministic
|
|
29950
|
+
description: "Apply deterministic document edits, including retyping an identified paragraph into a schema-declared typed block. " + "Do not use it for review feedback or for reading; use dispatch_comment, dispatch_suggest, or dispatch_doc_read instead. " + `The spec (or any document) holds requirements, design, and decisions - never progress, status, or timestamps. ${OWNER_REFERENCE} ${SPEC_WRITING_GUIDANCE}`,
|
|
29941
29951
|
arguments: (z) => ({
|
|
29942
29952
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
29943
29953
|
project: z.string().describe("Project key owning the document.").optional(),
|
|
@@ -29950,7 +29960,10 @@ var dispatchToolSpecs = [
|
|
|
29950
29960
|
occurrence: z.number({ int: true, min: 0 }).describe("Optional zero-based match occurrence.").optional(),
|
|
29951
29961
|
markdown: z.string().describe("Markdown to insert.").optional(),
|
|
29952
29962
|
after: z.string().describe("Anchor after which to insert.").optional(),
|
|
29953
|
-
before: z.string().describe("Anchor before which to insert.").optional()
|
|
29963
|
+
before: z.string().describe("Anchor before which to insert.").optional(),
|
|
29964
|
+
block: z.string().describe("Block id to retype.").optional(),
|
|
29965
|
+
type: z.string().describe("Typed block name for retype.").optional(),
|
|
29966
|
+
attributes: z.unknown().describe("Typed block attributes for retype.").optional()
|
|
29954
29967
|
})).describe("Flat tagged edits; the server validates fields required for each operation."),
|
|
29955
29968
|
summary: z.string().describe("Optional named-version summary.").optional()
|
|
29956
29969
|
}),
|
|
@@ -30411,6 +30424,7 @@ function zodSchemaApi(zod) {
|
|
|
30411
30424
|
},
|
|
30412
30425
|
boolean: () => api.boolean(),
|
|
30413
30426
|
enum: (values) => api.enum(values),
|
|
30427
|
+
unknown: () => api.unknown(),
|
|
30414
30428
|
array: (item, opts = {}) => {
|
|
30415
30429
|
let schema = api.array(item);
|
|
30416
30430
|
if (opts.min !== undefined)
|
|
@@ -31008,6 +31022,31 @@ var TolerantInboundEnvelopeSchema = object({
|
|
|
31008
31022
|
source: InboundEnvelopeSchema.shape.source.optional().catch(undefined),
|
|
31009
31023
|
sender: object(tolerantShape(InboundSenderSchema.shape)).optional().catch(undefined)
|
|
31010
31024
|
}).passthrough();
|
|
31025
|
+
var DispatchDeliveryRequestSchema = object({
|
|
31026
|
+
attempt: number2().int().positive(),
|
|
31027
|
+
mode: _enum2(["btw", "aside", "steer"])
|
|
31028
|
+
});
|
|
31029
|
+
var DispatchTargetedMessagePayloadSchema = object({
|
|
31030
|
+
id: string2(),
|
|
31031
|
+
issue_key: string2(),
|
|
31032
|
+
author: object({ kind: string2(), id: string2() }),
|
|
31033
|
+
body: string2(),
|
|
31034
|
+
target: string2(),
|
|
31035
|
+
in_reply_to: string2().nullable(),
|
|
31036
|
+
deliveries: array(unknown()),
|
|
31037
|
+
created_at: string2()
|
|
31038
|
+
});
|
|
31039
|
+
var DispatchTargetedFrameSchema = object({
|
|
31040
|
+
event: DispatchEventSchema,
|
|
31041
|
+
delivery: DispatchDeliveryRequestSchema
|
|
31042
|
+
}).refine(({ event }) => event.type === "message.created" && DispatchTargetedMessagePayloadSchema.safeParse(event.payload).success, { message: "targeted delivery requires a message.created event" });
|
|
31043
|
+
var RecoverableDispatchDeliveryFailureSchema = object({
|
|
31044
|
+
event: object({
|
|
31045
|
+
issue_key: string2(),
|
|
31046
|
+
payload: object({ id: string2(), body: string2().optional() }).passthrough()
|
|
31047
|
+
}).passthrough(),
|
|
31048
|
+
delivery: DispatchDeliveryRequestSchema
|
|
31049
|
+
});
|
|
31011
31050
|
function senderLabel(envelope) {
|
|
31012
31051
|
const sender = envelope.source_session ?? envelope.source ?? "unknown";
|
|
31013
31052
|
return envelope.sender?.title === undefined ? sender : `${sender} (${envelope.sender.title})`;
|
|
@@ -31034,6 +31073,8 @@ var DISPATCH_PAYLOAD_SCHEMAS = {
|
|
|
31034
31073
|
"suggestion.accepted": CommentEventPayloadSchema,
|
|
31035
31074
|
"suggestion.rejected": CommentEventPayloadSchema,
|
|
31036
31075
|
"message.created": MessageEventPayloadSchema,
|
|
31076
|
+
"message.delivery": MessageDeliveryEventPayloadSchema,
|
|
31077
|
+
"message.answered": MessageEventPayloadSchema,
|
|
31037
31078
|
"child.status": ChildStatusEventPayloadSchema
|
|
31038
31079
|
};
|
|
31039
31080
|
function dispatchOwner(event, topic) {
|
|
@@ -31066,6 +31107,9 @@ function dispatchMessageReplyPreview(event) {
|
|
|
31066
31107
|
const parsed = MessageEventPayloadSchema.safeParse(event.payload);
|
|
31067
31108
|
return parsed.success && parsed.data.reply_body !== undefined && parsed.data.reply_body !== "" ? parsed.data.reply_body : undefined;
|
|
31068
31109
|
}
|
|
31110
|
+
function isObject2(value) {
|
|
31111
|
+
return typeof value === "object" && value !== null;
|
|
31112
|
+
}
|
|
31069
31113
|
function parseDispatchFrame(rawPayload) {
|
|
31070
31114
|
let value;
|
|
31071
31115
|
try {
|
|
@@ -31073,11 +31117,41 @@ function parseDispatchFrame(rawPayload) {
|
|
|
31073
31117
|
} catch {
|
|
31074
31118
|
return { raw: rawPayload };
|
|
31075
31119
|
}
|
|
31120
|
+
const targeted = DispatchTargetedFrameSchema.safeParse(value);
|
|
31121
|
+
if (targeted.success) {
|
|
31122
|
+
const wireFrame = value;
|
|
31123
|
+
return {
|
|
31124
|
+
event: targeted.data.event,
|
|
31125
|
+
notify: wireFrame.event.notify,
|
|
31126
|
+
delivery: targeted.data.delivery
|
|
31127
|
+
};
|
|
31128
|
+
}
|
|
31129
|
+
if (isObject2(value) && "delivery" in value) {
|
|
31130
|
+
const recoverable = RecoverableDispatchDeliveryFailureSchema.safeParse(value);
|
|
31131
|
+
if (recoverable.success) {
|
|
31132
|
+
return {
|
|
31133
|
+
raw: value,
|
|
31134
|
+
rejectedDelivery: {
|
|
31135
|
+
attempt: recoverable.data.delivery.attempt,
|
|
31136
|
+
mode: recoverable.data.delivery.mode,
|
|
31137
|
+
messageID: recoverable.data.event.payload.id,
|
|
31138
|
+
issueKey: recoverable.data.event.issue_key,
|
|
31139
|
+
body: recoverable.data.event.payload.body ?? ""
|
|
31140
|
+
},
|
|
31141
|
+
malformedDelivery: true
|
|
31142
|
+
};
|
|
31143
|
+
}
|
|
31144
|
+
return { raw: value, malformedDelivery: true };
|
|
31145
|
+
}
|
|
31076
31146
|
const parsed = DispatchEventSchema.safeParse(value);
|
|
31077
31147
|
if (!parsed.success)
|
|
31078
31148
|
return { raw: value };
|
|
31079
31149
|
const wireEvent = value;
|
|
31080
|
-
return {
|
|
31150
|
+
return {
|
|
31151
|
+
event: parsed.data,
|
|
31152
|
+
notify: wireEvent.notify,
|
|
31153
|
+
delivery: undefined
|
|
31154
|
+
};
|
|
31081
31155
|
}
|
|
31082
31156
|
function inboundTimestamp(milliseconds) {
|
|
31083
31157
|
if (milliseconds === undefined)
|
|
@@ -31115,6 +31189,10 @@ function renderInbound(raw, sessionID, subject) {
|
|
|
31115
31189
|
}
|
|
31116
31190
|
let dispatchEvent;
|
|
31117
31191
|
let dispatchIssue;
|
|
31192
|
+
let dispatchReply;
|
|
31193
|
+
let delivery;
|
|
31194
|
+
let rejectedDelivery;
|
|
31195
|
+
let malformedDelivery = false;
|
|
31118
31196
|
let askQuestion;
|
|
31119
31197
|
let messageReplyPreview;
|
|
31120
31198
|
const dispatchRendered = envelope.source === "dispatch" && envelope.payload !== undefined;
|
|
@@ -31144,6 +31222,20 @@ function renderInbound(raw, sessionID, subject) {
|
|
|
31144
31222
|
}
|
|
31145
31223
|
askQuestion = dispatchAskQuestion(frame.event);
|
|
31146
31224
|
messageReplyPreview = dispatchMessageReplyPreview(frame.event);
|
|
31225
|
+
if (frame.event.type === "message.created" && frame.event.issue_key !== null) {
|
|
31226
|
+
const message = MessageEventPayloadSchema.safeParse(frame.event.payload);
|
|
31227
|
+
const requested = DispatchDeliveryRequestSchema.safeParse(frame.delivery);
|
|
31228
|
+
if (message.success && requested.success && message.data.id !== undefined) {
|
|
31229
|
+
delivery = {
|
|
31230
|
+
attempt: requested.data.attempt,
|
|
31231
|
+
mode: requested.data.mode,
|
|
31232
|
+
messageID: message.data.id,
|
|
31233
|
+
issueKey: frame.event.issue_key,
|
|
31234
|
+
body: message.data.body ?? envelope.payload_summary ?? ""
|
|
31235
|
+
};
|
|
31236
|
+
dispatchReply = `dispatch_message(issue="${frame.event.issue_key}", in_reply_to="${message.data.id}", body="...")`;
|
|
31237
|
+
}
|
|
31238
|
+
}
|
|
31147
31239
|
dispatchEvent = {
|
|
31148
31240
|
owner: dispatchOwner(frame.event, subject ?? envelope.topic),
|
|
31149
31241
|
...frame.event.issue_key === null ? {
|
|
@@ -31158,6 +31250,8 @@ function renderInbound(raw, sessionID, subject) {
|
|
|
31158
31250
|
};
|
|
31159
31251
|
} else {
|
|
31160
31252
|
dispatchEvent = frame.raw;
|
|
31253
|
+
rejectedDelivery = frame.rejectedDelivery;
|
|
31254
|
+
malformedDelivery = frame.malformedDelivery === true;
|
|
31161
31255
|
}
|
|
31162
31256
|
}
|
|
31163
31257
|
}
|
|
@@ -31182,7 +31276,7 @@ ${envelope.payload ?? ""}`;
|
|
|
31182
31276
|
}
|
|
31183
31277
|
}
|
|
31184
31278
|
const role = envelope.sender?.roles?.[0];
|
|
31185
|
-
const reply = replyWith(envelope);
|
|
31279
|
+
const reply = dispatchReply ?? replyWith(envelope);
|
|
31186
31280
|
const sourceIssue = parseIssues.includes("source") || KNOWN_SOURCES[envelope.source] !== undefined ? undefined : `source=${envelope.source}`;
|
|
31187
31281
|
const unrecognised = [
|
|
31188
31282
|
...parseIssues,
|
|
@@ -31212,7 +31306,14 @@ ${envelope.payload ?? ""}`;
|
|
|
31212
31306
|
},
|
|
31213
31307
|
...unrecognised === undefined ? {} : { unrecognised }
|
|
31214
31308
|
};
|
|
31215
|
-
return {
|
|
31309
|
+
return {
|
|
31310
|
+
skip: false,
|
|
31311
|
+
content: encode3({ envoy: rendered }),
|
|
31312
|
+
envelope,
|
|
31313
|
+
...delivery === undefined ? {} : { delivery },
|
|
31314
|
+
...rejectedDelivery === undefined ? {} : { rejectedDelivery },
|
|
31315
|
+
...malformedDelivery ? { malformedDelivery: true } : {}
|
|
31316
|
+
};
|
|
31216
31317
|
}
|
|
31217
31318
|
|
|
31218
31319
|
// ../envoy-client/src/dispatch-config.ts
|
|
@@ -31876,13 +31977,13 @@ function askId(args) {
|
|
|
31876
31977
|
}
|
|
31877
31978
|
return reference.id;
|
|
31878
31979
|
}
|
|
31879
|
-
function
|
|
31880
|
-
const
|
|
31881
|
-
if (
|
|
31882
|
-
return
|
|
31883
|
-
const reference = parseDispatchRef(
|
|
31980
|
+
function messageInReplyTo(args) {
|
|
31981
|
+
const inReplyTo = optionalString(args, "in_reply_to");
|
|
31982
|
+
if (inReplyTo === undefined || !inReplyTo.startsWith("dispatch://"))
|
|
31983
|
+
return inReplyTo;
|
|
31984
|
+
const reference = parseDispatchRef(inReplyTo);
|
|
31884
31985
|
if (reference?.kind !== "message") {
|
|
31885
|
-
throw new Error("
|
|
31986
|
+
throw new Error("in_reply_to must be a bare message id or a dispatch://.../message/<id> reference");
|
|
31886
31987
|
}
|
|
31887
31988
|
return reference.id;
|
|
31888
31989
|
}
|
|
@@ -32330,10 +32431,10 @@ async function executeDispatchTool(input) {
|
|
|
32330
32431
|
};
|
|
32331
32432
|
}
|
|
32332
32433
|
case "dispatch_message": {
|
|
32333
|
-
const
|
|
32434
|
+
const inReplyTo = messageInReplyTo(args);
|
|
32334
32435
|
const message = await client.message(issue(), {
|
|
32335
32436
|
body: stringArg(args, "body"),
|
|
32336
|
-
...
|
|
32437
|
+
...inReplyTo === undefined ? {} : { in_reply_to: inReplyTo },
|
|
32337
32438
|
actor
|
|
32338
32439
|
});
|
|
32339
32440
|
const messageRef = `dispatch://${message.issue_key}/message/${message.id}`;
|
|
@@ -32355,8 +32456,10 @@ async function executeDispatchTool(input) {
|
|
|
32355
32456
|
...summary === undefined ? {} : { summary },
|
|
32356
32457
|
actor
|
|
32357
32458
|
});
|
|
32459
|
+
const retyped = ops.filter((operation) => operation.op === "retype").length;
|
|
32460
|
+
const versionText = edited.version === null ? "no new version" : `version ${edited.version.number}`;
|
|
32358
32461
|
return {
|
|
32359
|
-
text:
|
|
32462
|
+
text: retyped === 0 ? `Applied ${edited.applied} ops (${versionText})` : `Applied ${edited.applied} ops; retyped ${retyped} block${retyped === 1 ? "" : "s"} (${versionText})`,
|
|
32360
32463
|
details: writeResultDetails(resolved, {
|
|
32361
32464
|
applied: edited.applied,
|
|
32362
32465
|
...edited.version === null ? {} : { version: edited.version.number }
|
|
@@ -32708,6 +32811,7 @@ var SessionWireSchema = object({
|
|
|
32708
32811
|
title: string2(),
|
|
32709
32812
|
topics: array(string2()),
|
|
32710
32813
|
roles: array(string2()).optional(),
|
|
32814
|
+
capabilities: array(string2()).optional(),
|
|
32711
32815
|
self_subscribed: boolean2(),
|
|
32712
32816
|
last_seen: number2().int().optional()
|
|
32713
32817
|
});
|
|
@@ -32816,6 +32920,7 @@ function createEnvoyClient(config) {
|
|
|
32816
32920
|
topics: expandSubscriptionTopics(input.topics),
|
|
32817
32921
|
port: input.port,
|
|
32818
32922
|
title: input.title,
|
|
32923
|
+
...input.capabilities === undefined ? {} : { capabilities: input.capabilities },
|
|
32819
32924
|
driving: input.driving,
|
|
32820
32925
|
...input.selfSubscribed === undefined ? {} : { self_subscribed: input.selfSubscribed }
|
|
32821
32926
|
}))),
|
|
@@ -33050,6 +33155,26 @@ function envoyExtension(pi) {
|
|
|
33050
33155
|
});
|
|
33051
33156
|
return connection;
|
|
33052
33157
|
};
|
|
33158
|
+
const postDispatchReply = async (delivery, result) => {
|
|
33159
|
+
if (!dispatchConfig.enabled || dispatchConfig.url === null || dispatchConfig.token === null) {
|
|
33160
|
+
throw new Error("Dispatch reply endpoint is not configured");
|
|
33161
|
+
}
|
|
33162
|
+
const response = await fetch(`${dispatchConfig.url}/api/v1/messages/${delivery.messageID}/reply`, {
|
|
33163
|
+
method: "POST",
|
|
33164
|
+
headers: {
|
|
33165
|
+
Authorization: `Bearer ${dispatchConfig.token}`,
|
|
33166
|
+
"Content-Type": "application/json"
|
|
33167
|
+
},
|
|
33168
|
+
body: JSON.stringify({
|
|
33169
|
+
actor: { kind: "session", id: sessionID },
|
|
33170
|
+
attempt: delivery.attempt,
|
|
33171
|
+
...result
|
|
33172
|
+
})
|
|
33173
|
+
});
|
|
33174
|
+
if (!response.ok) {
|
|
33175
|
+
throw new Error(`Dispatch reply failed: ${response.status} ${await response.text()}`);
|
|
33176
|
+
}
|
|
33177
|
+
};
|
|
33053
33178
|
const deliver = async (subject, raw, reply) => {
|
|
33054
33179
|
const rendered = renderInbound(raw, sessionID, subject);
|
|
33055
33180
|
for (const topic of subscriptionRemovedTopics(raw, sessionID) ?? [])
|
|
@@ -33069,9 +33194,34 @@ function envoyExtension(pi) {
|
|
|
33069
33194
|
inbox.pop();
|
|
33070
33195
|
}
|
|
33071
33196
|
try {
|
|
33072
|
-
|
|
33197
|
+
if (rendered.rejectedDelivery !== undefined) {
|
|
33198
|
+
console.warn(`[envoy] rejecting malformed Dispatch targeted delivery ${rendered.rejectedDelivery.messageID}`);
|
|
33199
|
+
await postDispatchReply(rendered.rejectedDelivery, {
|
|
33200
|
+
error: "Invalid Dispatch targeted delivery frame"
|
|
33201
|
+
});
|
|
33202
|
+
} else if (rendered.malformedDelivery === true) {
|
|
33203
|
+
console.warn("[envoy] dropping malformed Dispatch targeted delivery without a reply address");
|
|
33204
|
+
} else if (rendered.delivery?.mode === "btw") {
|
|
33205
|
+
if (pi.askEphemeral === undefined) {
|
|
33206
|
+
await postDispatchReply(rendered.delivery, {
|
|
33207
|
+
error: "This OMP host does not support BTW delivery"
|
|
33208
|
+
});
|
|
33209
|
+
} else {
|
|
33210
|
+
try {
|
|
33211
|
+
const reply = await pi.askEphemeral({ prompt: rendered.delivery.body });
|
|
33212
|
+
await postDispatchReply(rendered.delivery, { body: reply.replyText });
|
|
33213
|
+
} catch (error) {
|
|
33214
|
+
await postDispatchReply(rendered.delivery, { error: messageFor(error) });
|
|
33215
|
+
}
|
|
33216
|
+
}
|
|
33217
|
+
} else {
|
|
33218
|
+
pi.sendMessage({ customType: "envoy-message", content: rendered.content, display: true }, {
|
|
33219
|
+
deliverAs: rendered.delivery?.mode === "aside" ? "aside" : "steer",
|
|
33220
|
+
triggerTurn: true
|
|
33221
|
+
});
|
|
33222
|
+
}
|
|
33073
33223
|
} catch (error) {
|
|
33074
|
-
console.warn(`[envoy] failed to
|
|
33224
|
+
console.warn(`[envoy] failed to deliver envelope ${envelope?.event_id ?? "unknown"}`, error);
|
|
33075
33225
|
throw error;
|
|
33076
33226
|
}
|
|
33077
33227
|
if (dedupeKey !== undefined) {
|
|
@@ -33150,6 +33300,7 @@ function envoyExtension(pi) {
|
|
|
33150
33300
|
topics: [...new Set([agentSubject(sessionID), ...subscriptions.keys()])],
|
|
33151
33301
|
port: 0,
|
|
33152
33302
|
title: activeSessionContext?.sessionManager.getSessionName?.() ?? "",
|
|
33303
|
+
capabilities: typeof pi.askEphemeral === "function" ? ["aside", "btw"] : ["aside"],
|
|
33153
33304
|
driving: false,
|
|
33154
33305
|
selfSubscribed: true
|
|
33155
33306
|
});
|
package/dist/legion.js
CHANGED
|
@@ -29729,10 +29729,20 @@ var CommentEventPayloadSchema = object({
|
|
|
29729
29729
|
var MessageEventPayloadSchema = object({
|
|
29730
29730
|
id: string2().optional(),
|
|
29731
29731
|
body: string2().optional(),
|
|
29732
|
-
|
|
29732
|
+
target: string2().nullish(),
|
|
29733
|
+
in_reply_to: string2().nullish(),
|
|
29733
29734
|
reply_body: string2().optional(),
|
|
29734
29735
|
author: object({ kind: string2(), id: string2() }).optional()
|
|
29735
29736
|
});
|
|
29737
|
+
var MessageDeliveryEventPayloadSchema = object({
|
|
29738
|
+
message_id: string2().optional(),
|
|
29739
|
+
attempt: number2().int().positive().optional(),
|
|
29740
|
+
delivery: _enum2(["btw", "aside", "steer"]).optional(),
|
|
29741
|
+
session_id: string2().optional(),
|
|
29742
|
+
title: string2().optional(),
|
|
29743
|
+
state: _enum2(["sent", "failed"]).optional(),
|
|
29744
|
+
error: string2().optional()
|
|
29745
|
+
});
|
|
29736
29746
|
var ChildStatusEventPayloadSchema = object({
|
|
29737
29747
|
child_key: string2().optional(),
|
|
29738
29748
|
from: string2().optional(),
|
|
@@ -29826,7 +29836,7 @@ var SPEC_SECTIONS = [
|
|
|
29826
29836
|
];
|
|
29827
29837
|
var SPEC_WRITING_GUIDANCE = `When writing a spec, use these sections in order: ${SPEC_SECTIONS.join(", ")}. ` + "Write for a reader who has not seen the code: plain sentences, every identifier expanded on " + "first use, no coined shorthand; see skills/dispatch Writing for the human and Writing a spec.";
|
|
29828
29838
|
var ASK_URGENCIES = ["low", "med", "high", "blocking"];
|
|
29829
|
-
var DOC_EDIT_OPS = ["replace", "delete", "insert"];
|
|
29839
|
+
var DOC_EDIT_OPS = ["replace", "delete", "insert", "retype"];
|
|
29830
29840
|
var dispatchToolSpecs = [
|
|
29831
29841
|
{
|
|
29832
29842
|
name: "dispatch_issue",
|
|
@@ -29931,12 +29941,12 @@ var dispatchToolSpecs = [
|
|
|
29931
29941
|
arguments: (z) => ({
|
|
29932
29942
|
issue: z.string().describe(ISSUE_REFERENCE),
|
|
29933
29943
|
body: z.string({ max: 2000 }).describe("Update text, at most 2,000 characters."),
|
|
29934
|
-
|
|
29944
|
+
in_reply_to: z.string().describe("Optional message id or dispatch://KEY/message/<id> reference to reply to, threading " + "this message under it so the reply stays with the original in the Conversation.").optional()
|
|
29935
29945
|
})
|
|
29936
29946
|
},
|
|
29937
29947
|
{
|
|
29938
29948
|
name: "dispatch_doc_edit",
|
|
29939
|
-
description: "Apply deterministic
|
|
29949
|
+
description: "Apply deterministic document edits, including retyping an identified paragraph into a schema-declared typed block. " + "Do not use it for review feedback or for reading; use dispatch_comment, dispatch_suggest, or dispatch_doc_read instead. " + `The spec (or any document) holds requirements, design, and decisions - never progress, status, or timestamps. ${OWNER_REFERENCE} ${SPEC_WRITING_GUIDANCE}`,
|
|
29940
29950
|
arguments: (z) => ({
|
|
29941
29951
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
29942
29952
|
project: z.string().describe("Project key owning the document.").optional(),
|
|
@@ -29949,7 +29959,10 @@ var dispatchToolSpecs = [
|
|
|
29949
29959
|
occurrence: z.number({ int: true, min: 0 }).describe("Optional zero-based match occurrence.").optional(),
|
|
29950
29960
|
markdown: z.string().describe("Markdown to insert.").optional(),
|
|
29951
29961
|
after: z.string().describe("Anchor after which to insert.").optional(),
|
|
29952
|
-
before: z.string().describe("Anchor before which to insert.").optional()
|
|
29962
|
+
before: z.string().describe("Anchor before which to insert.").optional(),
|
|
29963
|
+
block: z.string().describe("Block id to retype.").optional(),
|
|
29964
|
+
type: z.string().describe("Typed block name for retype.").optional(),
|
|
29965
|
+
attributes: z.unknown().describe("Typed block attributes for retype.").optional()
|
|
29953
29966
|
})).describe("Flat tagged edits; the server validates fields required for each operation."),
|
|
29954
29967
|
summary: z.string().describe("Optional named-version summary.").optional()
|
|
29955
29968
|
}),
|
|
@@ -30410,6 +30423,7 @@ function zodSchemaApi(zod) {
|
|
|
30410
30423
|
},
|
|
30411
30424
|
boolean: () => api.boolean(),
|
|
30412
30425
|
enum: (values) => api.enum(values),
|
|
30426
|
+
unknown: () => api.unknown(),
|
|
30413
30427
|
array: (item, opts = {}) => {
|
|
30414
30428
|
let schema = api.array(item);
|
|
30415
30429
|
if (opts.min !== undefined)
|
|
@@ -31456,6 +31470,31 @@ var TolerantInboundEnvelopeSchema = object({
|
|
|
31456
31470
|
source: InboundEnvelopeSchema.shape.source.optional().catch(undefined),
|
|
31457
31471
|
sender: object(tolerantShape(InboundSenderSchema.shape)).optional().catch(undefined)
|
|
31458
31472
|
}).passthrough();
|
|
31473
|
+
var DispatchDeliveryRequestSchema = object({
|
|
31474
|
+
attempt: number2().int().positive(),
|
|
31475
|
+
mode: _enum2(["btw", "aside", "steer"])
|
|
31476
|
+
});
|
|
31477
|
+
var DispatchTargetedMessagePayloadSchema = object({
|
|
31478
|
+
id: string2(),
|
|
31479
|
+
issue_key: string2(),
|
|
31480
|
+
author: object({ kind: string2(), id: string2() }),
|
|
31481
|
+
body: string2(),
|
|
31482
|
+
target: string2(),
|
|
31483
|
+
in_reply_to: string2().nullable(),
|
|
31484
|
+
deliveries: array(unknown()),
|
|
31485
|
+
created_at: string2()
|
|
31486
|
+
});
|
|
31487
|
+
var DispatchTargetedFrameSchema = object({
|
|
31488
|
+
event: DispatchEventSchema,
|
|
31489
|
+
delivery: DispatchDeliveryRequestSchema
|
|
31490
|
+
}).refine(({ event }) => event.type === "message.created" && DispatchTargetedMessagePayloadSchema.safeParse(event.payload).success, { message: "targeted delivery requires a message.created event" });
|
|
31491
|
+
var RecoverableDispatchDeliveryFailureSchema = object({
|
|
31492
|
+
event: object({
|
|
31493
|
+
issue_key: string2(),
|
|
31494
|
+
payload: object({ id: string2(), body: string2().optional() }).passthrough()
|
|
31495
|
+
}).passthrough(),
|
|
31496
|
+
delivery: DispatchDeliveryRequestSchema
|
|
31497
|
+
});
|
|
31459
31498
|
function senderLabel(envelope) {
|
|
31460
31499
|
const sender = envelope.source_session ?? envelope.source ?? "unknown";
|
|
31461
31500
|
return envelope.sender?.title === undefined ? sender : `${sender} (${envelope.sender.title})`;
|
|
@@ -31482,6 +31521,8 @@ var DISPATCH_PAYLOAD_SCHEMAS = {
|
|
|
31482
31521
|
"suggestion.accepted": CommentEventPayloadSchema,
|
|
31483
31522
|
"suggestion.rejected": CommentEventPayloadSchema,
|
|
31484
31523
|
"message.created": MessageEventPayloadSchema,
|
|
31524
|
+
"message.delivery": MessageDeliveryEventPayloadSchema,
|
|
31525
|
+
"message.answered": MessageEventPayloadSchema,
|
|
31485
31526
|
"child.status": ChildStatusEventPayloadSchema
|
|
31486
31527
|
};
|
|
31487
31528
|
function dispatchOwner(event, topic) {
|
|
@@ -31514,6 +31555,9 @@ function dispatchMessageReplyPreview(event) {
|
|
|
31514
31555
|
const parsed = MessageEventPayloadSchema.safeParse(event.payload);
|
|
31515
31556
|
return parsed.success && parsed.data.reply_body !== undefined && parsed.data.reply_body !== "" ? parsed.data.reply_body : undefined;
|
|
31516
31557
|
}
|
|
31558
|
+
function isObject2(value) {
|
|
31559
|
+
return typeof value === "object" && value !== null;
|
|
31560
|
+
}
|
|
31517
31561
|
function parseDispatchFrame(rawPayload) {
|
|
31518
31562
|
let value;
|
|
31519
31563
|
try {
|
|
@@ -31521,11 +31565,41 @@ function parseDispatchFrame(rawPayload) {
|
|
|
31521
31565
|
} catch {
|
|
31522
31566
|
return { raw: rawPayload };
|
|
31523
31567
|
}
|
|
31568
|
+
const targeted = DispatchTargetedFrameSchema.safeParse(value);
|
|
31569
|
+
if (targeted.success) {
|
|
31570
|
+
const wireFrame = value;
|
|
31571
|
+
return {
|
|
31572
|
+
event: targeted.data.event,
|
|
31573
|
+
notify: wireFrame.event.notify,
|
|
31574
|
+
delivery: targeted.data.delivery
|
|
31575
|
+
};
|
|
31576
|
+
}
|
|
31577
|
+
if (isObject2(value) && "delivery" in value) {
|
|
31578
|
+
const recoverable = RecoverableDispatchDeliveryFailureSchema.safeParse(value);
|
|
31579
|
+
if (recoverable.success) {
|
|
31580
|
+
return {
|
|
31581
|
+
raw: value,
|
|
31582
|
+
rejectedDelivery: {
|
|
31583
|
+
attempt: recoverable.data.delivery.attempt,
|
|
31584
|
+
mode: recoverable.data.delivery.mode,
|
|
31585
|
+
messageID: recoverable.data.event.payload.id,
|
|
31586
|
+
issueKey: recoverable.data.event.issue_key,
|
|
31587
|
+
body: recoverable.data.event.payload.body ?? ""
|
|
31588
|
+
},
|
|
31589
|
+
malformedDelivery: true
|
|
31590
|
+
};
|
|
31591
|
+
}
|
|
31592
|
+
return { raw: value, malformedDelivery: true };
|
|
31593
|
+
}
|
|
31524
31594
|
const parsed = DispatchEventSchema.safeParse(value);
|
|
31525
31595
|
if (!parsed.success)
|
|
31526
31596
|
return { raw: value };
|
|
31527
31597
|
const wireEvent = value;
|
|
31528
|
-
return {
|
|
31598
|
+
return {
|
|
31599
|
+
event: parsed.data,
|
|
31600
|
+
notify: wireEvent.notify,
|
|
31601
|
+
delivery: undefined
|
|
31602
|
+
};
|
|
31529
31603
|
}
|
|
31530
31604
|
function inboundTimestamp(milliseconds) {
|
|
31531
31605
|
if (milliseconds === undefined)
|
|
@@ -31563,6 +31637,10 @@ function renderInbound(raw, sessionID, subject) {
|
|
|
31563
31637
|
}
|
|
31564
31638
|
let dispatchEvent;
|
|
31565
31639
|
let dispatchIssue;
|
|
31640
|
+
let dispatchReply;
|
|
31641
|
+
let delivery;
|
|
31642
|
+
let rejectedDelivery;
|
|
31643
|
+
let malformedDelivery = false;
|
|
31566
31644
|
let askQuestion;
|
|
31567
31645
|
let messageReplyPreview;
|
|
31568
31646
|
const dispatchRendered = envelope.source === "dispatch" && envelope.payload !== undefined;
|
|
@@ -31592,6 +31670,20 @@ function renderInbound(raw, sessionID, subject) {
|
|
|
31592
31670
|
}
|
|
31593
31671
|
askQuestion = dispatchAskQuestion(frame.event);
|
|
31594
31672
|
messageReplyPreview = dispatchMessageReplyPreview(frame.event);
|
|
31673
|
+
if (frame.event.type === "message.created" && frame.event.issue_key !== null) {
|
|
31674
|
+
const message = MessageEventPayloadSchema.safeParse(frame.event.payload);
|
|
31675
|
+
const requested = DispatchDeliveryRequestSchema.safeParse(frame.delivery);
|
|
31676
|
+
if (message.success && requested.success && message.data.id !== undefined) {
|
|
31677
|
+
delivery = {
|
|
31678
|
+
attempt: requested.data.attempt,
|
|
31679
|
+
mode: requested.data.mode,
|
|
31680
|
+
messageID: message.data.id,
|
|
31681
|
+
issueKey: frame.event.issue_key,
|
|
31682
|
+
body: message.data.body ?? envelope.payload_summary ?? ""
|
|
31683
|
+
};
|
|
31684
|
+
dispatchReply = `dispatch_message(issue="${frame.event.issue_key}", in_reply_to="${message.data.id}", body="...")`;
|
|
31685
|
+
}
|
|
31686
|
+
}
|
|
31595
31687
|
dispatchEvent = {
|
|
31596
31688
|
owner: dispatchOwner(frame.event, subject ?? envelope.topic),
|
|
31597
31689
|
...frame.event.issue_key === null ? {
|
|
@@ -31606,6 +31698,8 @@ function renderInbound(raw, sessionID, subject) {
|
|
|
31606
31698
|
};
|
|
31607
31699
|
} else {
|
|
31608
31700
|
dispatchEvent = frame.raw;
|
|
31701
|
+
rejectedDelivery = frame.rejectedDelivery;
|
|
31702
|
+
malformedDelivery = frame.malformedDelivery === true;
|
|
31609
31703
|
}
|
|
31610
31704
|
}
|
|
31611
31705
|
}
|
|
@@ -31630,7 +31724,7 @@ ${envelope.payload ?? ""}`;
|
|
|
31630
31724
|
}
|
|
31631
31725
|
}
|
|
31632
31726
|
const role = envelope.sender?.roles?.[0];
|
|
31633
|
-
const reply = replyWith(envelope);
|
|
31727
|
+
const reply = dispatchReply ?? replyWith(envelope);
|
|
31634
31728
|
const sourceIssue = parseIssues.includes("source") || KNOWN_SOURCES[envelope.source] !== undefined ? undefined : `source=${envelope.source}`;
|
|
31635
31729
|
const unrecognised = [
|
|
31636
31730
|
...parseIssues,
|
|
@@ -31660,7 +31754,14 @@ ${envelope.payload ?? ""}`;
|
|
|
31660
31754
|
},
|
|
31661
31755
|
...unrecognised === undefined ? {} : { unrecognised }
|
|
31662
31756
|
};
|
|
31663
|
-
return {
|
|
31757
|
+
return {
|
|
31758
|
+
skip: false,
|
|
31759
|
+
content: encode3({ envoy: rendered }),
|
|
31760
|
+
envelope,
|
|
31761
|
+
...delivery === undefined ? {} : { delivery },
|
|
31762
|
+
...rejectedDelivery === undefined ? {} : { rejectedDelivery },
|
|
31763
|
+
...malformedDelivery ? { malformedDelivery: true } : {}
|
|
31764
|
+
};
|
|
31664
31765
|
}
|
|
31665
31766
|
|
|
31666
31767
|
// ../envoy-client/src/dispatch-config.ts
|
|
@@ -32302,13 +32403,13 @@ function askId(args) {
|
|
|
32302
32403
|
}
|
|
32303
32404
|
return reference.id;
|
|
32304
32405
|
}
|
|
32305
|
-
function
|
|
32306
|
-
const
|
|
32307
|
-
if (
|
|
32308
|
-
return
|
|
32309
|
-
const reference = parseDispatchRef(
|
|
32406
|
+
function messageInReplyTo(args) {
|
|
32407
|
+
const inReplyTo = optionalString(args, "in_reply_to");
|
|
32408
|
+
if (inReplyTo === undefined || !inReplyTo.startsWith("dispatch://"))
|
|
32409
|
+
return inReplyTo;
|
|
32410
|
+
const reference = parseDispatchRef(inReplyTo);
|
|
32310
32411
|
if (reference?.kind !== "message") {
|
|
32311
|
-
throw new Error("
|
|
32412
|
+
throw new Error("in_reply_to must be a bare message id or a dispatch://.../message/<id> reference");
|
|
32312
32413
|
}
|
|
32313
32414
|
return reference.id;
|
|
32314
32415
|
}
|
|
@@ -32756,10 +32857,10 @@ async function executeDispatchTool(input) {
|
|
|
32756
32857
|
};
|
|
32757
32858
|
}
|
|
32758
32859
|
case "dispatch_message": {
|
|
32759
|
-
const
|
|
32860
|
+
const inReplyTo = messageInReplyTo(args);
|
|
32760
32861
|
const message = await client.message(issue(), {
|
|
32761
32862
|
body: stringArg(args, "body"),
|
|
32762
|
-
...
|
|
32863
|
+
...inReplyTo === undefined ? {} : { in_reply_to: inReplyTo },
|
|
32763
32864
|
actor
|
|
32764
32865
|
});
|
|
32765
32866
|
const messageRef = `dispatch://${message.issue_key}/message/${message.id}`;
|
|
@@ -32781,8 +32882,10 @@ async function executeDispatchTool(input) {
|
|
|
32781
32882
|
...summary === undefined ? {} : { summary },
|
|
32782
32883
|
actor
|
|
32783
32884
|
});
|
|
32885
|
+
const retyped = ops.filter((operation) => operation.op === "retype").length;
|
|
32886
|
+
const versionText = edited.version === null ? "no new version" : `version ${edited.version.number}`;
|
|
32784
32887
|
return {
|
|
32785
|
-
text:
|
|
32888
|
+
text: retyped === 0 ? `Applied ${edited.applied} ops (${versionText})` : `Applied ${edited.applied} ops; retyped ${retyped} block${retyped === 1 ? "" : "s"} (${versionText})`,
|
|
32786
32889
|
details: writeResultDetails(resolved, {
|
|
32787
32890
|
applied: edited.applied,
|
|
32788
32891
|
...edited.version === null ? {} : { version: edited.version.number }
|
|
@@ -33134,6 +33237,7 @@ var SessionWireSchema = object({
|
|
|
33134
33237
|
title: string2(),
|
|
33135
33238
|
topics: array(string2()),
|
|
33136
33239
|
roles: array(string2()).optional(),
|
|
33240
|
+
capabilities: array(string2()).optional(),
|
|
33137
33241
|
self_subscribed: boolean2(),
|
|
33138
33242
|
last_seen: number2().int().optional()
|
|
33139
33243
|
});
|
|
@@ -33242,6 +33346,7 @@ function createEnvoyClient(config) {
|
|
|
33242
33346
|
topics: expandSubscriptionTopics(input.topics),
|
|
33243
33347
|
port: input.port,
|
|
33244
33348
|
title: input.title,
|
|
33349
|
+
...input.capabilities === undefined ? {} : { capabilities: input.capabilities },
|
|
33245
33350
|
driving: input.driving,
|
|
33246
33351
|
...input.selfSubscribed === undefined ? {} : { self_subscribed: input.selfSubscribed }
|
|
33247
33352
|
}))),
|
|
@@ -33468,6 +33573,26 @@ function envoyExtension(pi) {
|
|
|
33468
33573
|
});
|
|
33469
33574
|
return connection;
|
|
33470
33575
|
};
|
|
33576
|
+
const postDispatchReply = async (delivery, result) => {
|
|
33577
|
+
if (!dispatchConfig.enabled || dispatchConfig.url === null || dispatchConfig.token === null) {
|
|
33578
|
+
throw new Error("Dispatch reply endpoint is not configured");
|
|
33579
|
+
}
|
|
33580
|
+
const response = await fetch(`${dispatchConfig.url}/api/v1/messages/${delivery.messageID}/reply`, {
|
|
33581
|
+
method: "POST",
|
|
33582
|
+
headers: {
|
|
33583
|
+
Authorization: `Bearer ${dispatchConfig.token}`,
|
|
33584
|
+
"Content-Type": "application/json"
|
|
33585
|
+
},
|
|
33586
|
+
body: JSON.stringify({
|
|
33587
|
+
actor: { kind: "session", id: sessionID },
|
|
33588
|
+
attempt: delivery.attempt,
|
|
33589
|
+
...result
|
|
33590
|
+
})
|
|
33591
|
+
});
|
|
33592
|
+
if (!response.ok) {
|
|
33593
|
+
throw new Error(`Dispatch reply failed: ${response.status} ${await response.text()}`);
|
|
33594
|
+
}
|
|
33595
|
+
};
|
|
33471
33596
|
const deliver = async (subject, raw, reply) => {
|
|
33472
33597
|
const rendered = renderInbound(raw, sessionID, subject);
|
|
33473
33598
|
for (const topic of subscriptionRemovedTopics(raw, sessionID) ?? [])
|
|
@@ -33487,9 +33612,34 @@ function envoyExtension(pi) {
|
|
|
33487
33612
|
inbox.pop();
|
|
33488
33613
|
}
|
|
33489
33614
|
try {
|
|
33490
|
-
|
|
33615
|
+
if (rendered.rejectedDelivery !== undefined) {
|
|
33616
|
+
console.warn(`[envoy] rejecting malformed Dispatch targeted delivery ${rendered.rejectedDelivery.messageID}`);
|
|
33617
|
+
await postDispatchReply(rendered.rejectedDelivery, {
|
|
33618
|
+
error: "Invalid Dispatch targeted delivery frame"
|
|
33619
|
+
});
|
|
33620
|
+
} else if (rendered.malformedDelivery === true) {
|
|
33621
|
+
console.warn("[envoy] dropping malformed Dispatch targeted delivery without a reply address");
|
|
33622
|
+
} else if (rendered.delivery?.mode === "btw") {
|
|
33623
|
+
if (pi.askEphemeral === undefined) {
|
|
33624
|
+
await postDispatchReply(rendered.delivery, {
|
|
33625
|
+
error: "This OMP host does not support BTW delivery"
|
|
33626
|
+
});
|
|
33627
|
+
} else {
|
|
33628
|
+
try {
|
|
33629
|
+
const reply = await pi.askEphemeral({ prompt: rendered.delivery.body });
|
|
33630
|
+
await postDispatchReply(rendered.delivery, { body: reply.replyText });
|
|
33631
|
+
} catch (error) {
|
|
33632
|
+
await postDispatchReply(rendered.delivery, { error: messageFor(error) });
|
|
33633
|
+
}
|
|
33634
|
+
}
|
|
33635
|
+
} else {
|
|
33636
|
+
pi.sendMessage({ customType: "envoy-message", content: rendered.content, display: true }, {
|
|
33637
|
+
deliverAs: rendered.delivery?.mode === "aside" ? "aside" : "steer",
|
|
33638
|
+
triggerTurn: true
|
|
33639
|
+
});
|
|
33640
|
+
}
|
|
33491
33641
|
} catch (error) {
|
|
33492
|
-
console.warn(`[envoy] failed to
|
|
33642
|
+
console.warn(`[envoy] failed to deliver envelope ${envelope?.event_id ?? "unknown"}`, error);
|
|
33493
33643
|
throw error;
|
|
33494
33644
|
}
|
|
33495
33645
|
if (dedupeKey !== undefined) {
|
|
@@ -33568,6 +33718,7 @@ function envoyExtension(pi) {
|
|
|
33568
33718
|
topics: [...new Set([agentSubject(sessionID), ...subscriptions.keys()])],
|
|
33569
33719
|
port: 0,
|
|
33570
33720
|
title: activeSessionContext?.sessionManager.getSessionName?.() ?? "",
|
|
33721
|
+
capabilities: typeof pi.askEphemeral === "function" ? ["aside", "btw"] : ["aside"],
|
|
33571
33722
|
driving: false,
|
|
33572
33723
|
selfSubscribed: true
|
|
33573
33724
|
});
|
|
@@ -199,13 +199,16 @@ exact `EditOp` shape:
|
|
|
199
199
|
|
|
200
200
|
```ts
|
|
201
201
|
type EditOp = {
|
|
202
|
-
op: "replace" | "delete" | "insert";
|
|
202
|
+
op: "replace" | "delete" | "insert" | "retype";
|
|
203
203
|
find?: string;
|
|
204
204
|
with?: string;
|
|
205
205
|
occurrence?: number;
|
|
206
206
|
markdown?: string;
|
|
207
207
|
after?: string;
|
|
208
208
|
before?: string;
|
|
209
|
+
block?: string;
|
|
210
|
+
type?: string;
|
|
211
|
+
attributes?: Record<string, unknown>;
|
|
209
212
|
};
|
|
210
213
|
```
|
|
211
214
|
|
|
@@ -221,6 +224,9 @@ and deleting a cell's quoted text removes only that text.
|
|
|
221
224
|
|
|
222
225
|
Use `replace` for inline continuation. Use zero-based `occurrence` for a repeated target; re-read a missing or ambiguous target before
|
|
223
226
|
retrying. Pass `summary` to name the version when recording a decision.
|
|
227
|
+
`retype` turns the paragraph with `block` into the named typed `type` in place. It keeps the
|
|
228
|
+
block id and uses `attributes` for client-owned typed attributes. Use it when an existing
|
|
229
|
+
paragraph is the question that should become a decision.
|
|
224
230
|
|
|
225
231
|
## Typed blocks
|
|
226
232
|
|
|
@@ -236,6 +242,19 @@ quoted: `:::callout{kind="warning" title="Risk"}`. Do not write Pandoc-style `::
|
|
|
236
242
|
block. Do not set attributes the schema marks `server: true`; the server ignores them and reasserts
|
|
237
243
|
its authoritative value at settlement.
|
|
238
244
|
|
|
245
|
+
Questions about a document must be `ask` blocks, never an `Open questions` prose section. An ask
|
|
246
|
+
body is one or more question paragraphs followed by an optional bullet list of options, where each
|
|
247
|
+
item is `Label: description`. For example:
|
|
248
|
+
|
|
249
|
+
```md
|
|
250
|
+
:::ask{urgency="high" multiple="false"}
|
|
251
|
+
Should we ship the migration?
|
|
252
|
+
|
|
253
|
+
- Ship: Release the verified change.
|
|
254
|
+
- Hold: Wait for another review.
|
|
255
|
+
:::
|
|
256
|
+
```
|
|
257
|
+
|
|
239
258
|
## Comments and suggestions
|
|
240
259
|
|
|
241
260
|
Add feedback with:
|
|
@@ -298,6 +317,24 @@ dispatch_message({ issue, body })
|
|
|
298
317
|
It returns `details` `{ issue, topic, message }`. `body` is capped at 2,000 characters. A message is not a decision
|
|
299
318
|
(`dispatch_ask`) or document feedback (`dispatch_comment`), and it does not wake anyone unless the issue is routed.
|
|
300
319
|
|
|
320
|
+
### Targeted agent messages
|
|
321
|
+
|
|
322
|
+
A human can target the issue message at a live Envoy session or role as **BTW**, **Aside**, or
|
|
323
|
+
**Steer**. The incoming Dispatch frame names the issue and includes a `reply_with` instruction;
|
|
324
|
+
reply on the same open issue with the existing tool, never a new targeted send:
|
|
325
|
+
|
|
326
|
+
```ts
|
|
327
|
+
dispatch_message({
|
|
328
|
+
issue: "CORE-1",
|
|
329
|
+
in_reply_to: "<targeted-message-id>",
|
|
330
|
+
body: "The requested answer.",
|
|
331
|
+
})
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
`in_reply_to` correlates the answer under the human's message in its Conversation card. A BTW
|
|
335
|
+
delivery can post its answer automatically; use this call when the frame asks the primary agent to
|
|
336
|
+
reply. Agent bearers cannot set `target` or `delivery`.
|
|
337
|
+
|
|
301
338
|
## What comes back
|
|
302
339
|
|
|
303
340
|
A write result's `details.topic` subscribes the host to its owner, and the first such subscription on an issue or document also tells
|