@sjawhar/pi-legion-envoy 0.42.0 → 0.44.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 +81 -8
- package/dist/legion.js +81 -8
- package/dist/skills/dispatch/SKILL.md +20 -10
- package/package.json +1 -1
package/dist/envoy.js
CHANGED
|
@@ -29725,6 +29725,11 @@ var ChildStatusEventPayloadSchema = object({
|
|
|
29725
29725
|
from: string2().optional(),
|
|
29726
29726
|
to: string2().optional()
|
|
29727
29727
|
});
|
|
29728
|
+
var SubscriptionRemovedEventPayloadSchema = object({
|
|
29729
|
+
session_id: string2().optional(),
|
|
29730
|
+
by: object({ kind: string2(), id: string2().optional() }).passthrough().optional(),
|
|
29731
|
+
topics: array(string2()).optional()
|
|
29732
|
+
});
|
|
29728
29733
|
// ../contracts/src/dispatch-snippet.ts
|
|
29729
29734
|
var HTML_ENTITIES = [
|
|
29730
29735
|
["<", "<"],
|
|
@@ -29782,15 +29787,17 @@ function dispatchToolSchema(spec, z, opts) {
|
|
|
29782
29787
|
}
|
|
29783
29788
|
var ISSUE_REFERENCE = "An issue is a native KEY or external owner/repo#n reference; an external reference creates its native issue in the repository's dashboard-configured project or, failing that, the default project (DISPATCH_DEFAULT_PROJECT).";
|
|
29784
29789
|
var OWNER_REFERENCE = "Exactly one of issue and project is required. An issue is a native KEY or external owner/repo#n reference; a project is a project key such as CORE and addresses an unlinked project document named by artifact.";
|
|
29785
|
-
function documentOwnerValidation(requireArtifact) {
|
|
29790
|
+
function documentOwnerValidation(requireArtifact, alwaysRequireArtifact = false) {
|
|
29786
29791
|
return {
|
|
29787
29792
|
check: (value) => {
|
|
29788
29793
|
const input = value;
|
|
29789
29794
|
const hasIssue = typeof input.issue === "string";
|
|
29790
29795
|
const hasProject = typeof input.project === "string";
|
|
29791
|
-
|
|
29796
|
+
const hasArtifact = typeof input.artifact === "string";
|
|
29797
|
+
const hasRef = typeof input.ref === "string";
|
|
29798
|
+
return (hasIssue !== hasProject || !hasIssue && !hasProject && hasRef) && (!hasProject || !requireArtifact || hasArtifact) && (!alwaysRequireArtifact || hasArtifact || hasRef);
|
|
29792
29799
|
},
|
|
29793
|
-
message: "Exactly one of issue and project is required; with project, artifact names the document."
|
|
29800
|
+
message: alwaysRequireArtifact ? "Exactly one of issue and project is required; artifact or ref must name the document." : "Exactly one of issue and project is required; with project, artifact names the document."
|
|
29794
29801
|
};
|
|
29795
29802
|
}
|
|
29796
29803
|
var SPEC_SECTIONS = [
|
|
@@ -29825,6 +29832,7 @@ var dispatchToolSpecs = [
|
|
|
29825
29832
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
29826
29833
|
project: z.string().describe("Project key owning the document.").optional(),
|
|
29827
29834
|
artifact: z.string().describe("Project document slug or id.").optional(),
|
|
29835
|
+
ref: z.string().describe("Optional dispatch:// issue or document reference.").optional(),
|
|
29828
29836
|
question: z.string({ max: 800 }).describe("Decision question, at most 800 characters."),
|
|
29829
29837
|
options: z.array(z.object({
|
|
29830
29838
|
label: z.string().describe("Selectable option label."),
|
|
@@ -29877,6 +29885,7 @@ var dispatchToolSpecs = [
|
|
|
29877
29885
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
29878
29886
|
project: z.string().describe("Project key owning the document.").optional(),
|
|
29879
29887
|
artifact: z.string().describe("Artifact slug or id required when quote is given.").optional(),
|
|
29888
|
+
ref: z.string().describe("Optional dispatch:// issue or document reference.").optional(),
|
|
29880
29889
|
quote: z.string().describe("Optional exact quoted document text.").optional(),
|
|
29881
29890
|
occurrence: z.number({ int: true, min: 0 }).describe("Optional zero-based occurrence of quote.").optional(),
|
|
29882
29891
|
body: z.string({ max: 2000 }).describe("Review comment, at most 2,000 characters."),
|
|
@@ -29891,13 +29900,14 @@ var dispatchToolSpecs = [
|
|
|
29891
29900
|
arguments: (z) => ({
|
|
29892
29901
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
29893
29902
|
project: z.string().describe("Project key owning the document.").optional(),
|
|
29894
|
-
artifact: z.string().describe("Artifact slug or id containing the quoted text."),
|
|
29903
|
+
artifact: z.string().describe("Artifact slug or id containing the quoted text; optional when ref names it.").optional(),
|
|
29904
|
+
ref: z.string().describe("Optional dispatch:// issue or document reference.").optional(),
|
|
29895
29905
|
quote: z.string().describe("Exact document text to replace."),
|
|
29896
29906
|
replace_with: z.string().describe("Replacement text."),
|
|
29897
29907
|
body: z.string({ max: 2000 }).describe("Optional rationale, at most 2,000 characters.").optional(),
|
|
29898
29908
|
occurrence: z.number({ int: true, min: 0 }).describe("Optional zero-based occurrence of quote.").optional()
|
|
29899
29909
|
}),
|
|
29900
|
-
validation: documentOwnerValidation(true)
|
|
29910
|
+
validation: documentOwnerValidation(true, true)
|
|
29901
29911
|
},
|
|
29902
29912
|
{
|
|
29903
29913
|
name: "dispatch_message",
|
|
@@ -29914,7 +29924,8 @@ var dispatchToolSpecs = [
|
|
|
29914
29924
|
arguments: (z) => ({
|
|
29915
29925
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
29916
29926
|
project: z.string().describe("Project key owning the document.").optional(),
|
|
29917
|
-
artifact: z.string().describe("Artifact slug or id for the document."),
|
|
29927
|
+
artifact: z.string().describe("Artifact slug or id for the document; optional when ref names it.").optional(),
|
|
29928
|
+
ref: z.string().describe("Optional dispatch:// issue or document reference.").optional(),
|
|
29918
29929
|
ops: z.array(z.object({
|
|
29919
29930
|
op: z.enum(DOC_EDIT_OPS).describe("Edit operation."),
|
|
29920
29931
|
find: z.string().describe("Text to find for replace or delete.").optional(),
|
|
@@ -29926,7 +29937,7 @@ var dispatchToolSpecs = [
|
|
|
29926
29937
|
})).describe("Flat tagged edits; the server validates fields required for each operation."),
|
|
29927
29938
|
summary: z.string().describe("Optional named-version summary.").optional()
|
|
29928
29939
|
}),
|
|
29929
|
-
validation: documentOwnerValidation(true)
|
|
29940
|
+
validation: documentOwnerValidation(true, true)
|
|
29930
29941
|
},
|
|
29931
29942
|
{
|
|
29932
29943
|
name: "dispatch_doc_read",
|
|
@@ -31101,6 +31112,18 @@ function renderInbound(raw, sessionID, subject) {
|
|
|
31101
31112
|
if (frame.event.actor.kind === "session" && frame.event.actor.id === sessionID) {
|
|
31102
31113
|
return { skip: true, content: "", envelope };
|
|
31103
31114
|
}
|
|
31115
|
+
if (frame.event.type === "subscription.removed") {
|
|
31116
|
+
const removed = SubscriptionRemovedEventPayloadSchema.safeParse(frame.event.payload);
|
|
31117
|
+
if (!removed.success || removed.data.session_id !== sessionID) {
|
|
31118
|
+
return { skip: true, content: "", envelope };
|
|
31119
|
+
}
|
|
31120
|
+
const who = removed.data.by?.id ?? "someone";
|
|
31121
|
+
return {
|
|
31122
|
+
skip: false,
|
|
31123
|
+
content: `Unsubscribed from ${dispatchOwner(frame.event, subject ?? envelope.topic)} by ${who}`,
|
|
31124
|
+
envelope
|
|
31125
|
+
};
|
|
31126
|
+
}
|
|
31104
31127
|
askQuestion = dispatchAskQuestion(frame.event);
|
|
31105
31128
|
messageReplyPreview = dispatchMessageReplyPreview(frame.event);
|
|
31106
31129
|
dispatchEvent = {
|
|
@@ -32381,6 +32404,46 @@ function dispatchSubscriptionTopic(details) {
|
|
|
32381
32404
|
const { topic } = details;
|
|
32382
32405
|
return typeof topic === "string" && topic.startsWith("notifications.dispatch.") ? topic : null;
|
|
32383
32406
|
}
|
|
32407
|
+
function dispatchTopicLabel(topic) {
|
|
32408
|
+
if (topic.startsWith(DISPATCH_ISSUE_TOPIC_PREFIX)) {
|
|
32409
|
+
const [key] = topic.slice(DISPATCH_ISSUE_TOPIC_PREFIX.length).split(".", 2);
|
|
32410
|
+
if (key !== undefined && key !== "")
|
|
32411
|
+
return key;
|
|
32412
|
+
}
|
|
32413
|
+
if (topic.startsWith(DISPATCH_DOCUMENT_TOPIC_PREFIX)) {
|
|
32414
|
+
const [project, slug] = topic.slice(DISPATCH_DOCUMENT_TOPIC_PREFIX.length).split(".", 3);
|
|
32415
|
+
if (project !== undefined && project !== "" && slug !== undefined && slug !== "") {
|
|
32416
|
+
return `${project}/${slug}`;
|
|
32417
|
+
}
|
|
32418
|
+
}
|
|
32419
|
+
return topic;
|
|
32420
|
+
}
|
|
32421
|
+
function subscriptionRemovedTopics(raw, sessionID) {
|
|
32422
|
+
let envelope;
|
|
32423
|
+
try {
|
|
32424
|
+
envelope = JSON.parse(raw);
|
|
32425
|
+
} catch {
|
|
32426
|
+
return;
|
|
32427
|
+
}
|
|
32428
|
+
if (typeof envelope !== "object" || envelope === null)
|
|
32429
|
+
return;
|
|
32430
|
+
const { source, payload } = envelope;
|
|
32431
|
+
if (source !== "dispatch" || typeof payload !== "string")
|
|
32432
|
+
return;
|
|
32433
|
+
let event;
|
|
32434
|
+
try {
|
|
32435
|
+
event = JSON.parse(payload);
|
|
32436
|
+
} catch {
|
|
32437
|
+
return;
|
|
32438
|
+
}
|
|
32439
|
+
const parsedEvent = DispatchEventSchema.safeParse(event);
|
|
32440
|
+
if (!parsedEvent.success || parsedEvent.data.type !== "subscription.removed")
|
|
32441
|
+
return;
|
|
32442
|
+
const parsedPayload = SubscriptionRemovedEventPayloadSchema.safeParse(parsedEvent.data.payload);
|
|
32443
|
+
if (!parsedPayload.success || parsedPayload.data.session_id !== sessionID)
|
|
32444
|
+
return;
|
|
32445
|
+
return parsedPayload.data.topics ?? [];
|
|
32446
|
+
}
|
|
32384
32447
|
|
|
32385
32448
|
// ../envoy-client/src/tool-contract.ts
|
|
32386
32449
|
var DELIVERY_CONTRACT = "Delivery is at-least-once, possibly out of order across topics; use id for dedupe and at for freshness.";
|
|
@@ -32858,6 +32921,8 @@ function envoyExtension(pi) {
|
|
|
32858
32921
|
};
|
|
32859
32922
|
const deliver = async (subject, raw, reply) => {
|
|
32860
32923
|
const rendered = renderInbound(raw, sessionID, subject);
|
|
32924
|
+
for (const topic of subscriptionRemovedTopics(raw, sessionID) ?? [])
|
|
32925
|
+
closeIntentionally(topic);
|
|
32861
32926
|
const dedupeKey = rendered.envelope?.dedupe_key;
|
|
32862
32927
|
const duplicate = dedupeKey !== undefined && dedupeKeys.has(dedupeKey);
|
|
32863
32928
|
if (!duplicate && !rendered.skip) {
|
|
@@ -33249,8 +33314,16 @@ function envoyExtension(pi) {
|
|
|
33249
33314
|
if (topic === null)
|
|
33250
33315
|
return;
|
|
33251
33316
|
try {
|
|
33252
|
-
|
|
33317
|
+
const isNew = await subscribe(topic);
|
|
33318
|
+
if (isNew)
|
|
33253
33319
|
await registerSession();
|
|
33320
|
+
if (isNew) {
|
|
33321
|
+
pi.sendMessage({
|
|
33322
|
+
customType: "envoy-message",
|
|
33323
|
+
content: `Subscribed to ${dispatchTopicLabel(topic)} (every event on this issue reaches you; envoy_unsubscribe ${topic} to stop).`,
|
|
33324
|
+
display: true
|
|
33325
|
+
}, { deliverAs: "steer", triggerTurn: false });
|
|
33326
|
+
}
|
|
33254
33327
|
} catch (error) {
|
|
33255
33328
|
activeSessionContext?.ui.notify(`envoy: dispatch reply auto-subscribe failed (${messageFor(error)}); run envoy_subscribe ${topic}`, "warning");
|
|
33256
33329
|
}
|
package/dist/legion.js
CHANGED
|
@@ -29724,6 +29724,11 @@ var ChildStatusEventPayloadSchema = object({
|
|
|
29724
29724
|
from: string2().optional(),
|
|
29725
29725
|
to: string2().optional()
|
|
29726
29726
|
});
|
|
29727
|
+
var SubscriptionRemovedEventPayloadSchema = object({
|
|
29728
|
+
session_id: string2().optional(),
|
|
29729
|
+
by: object({ kind: string2(), id: string2().optional() }).passthrough().optional(),
|
|
29730
|
+
topics: array(string2()).optional()
|
|
29731
|
+
});
|
|
29727
29732
|
// ../contracts/src/dispatch-snippet.ts
|
|
29728
29733
|
var HTML_ENTITIES = [
|
|
29729
29734
|
["<", "<"],
|
|
@@ -29781,15 +29786,17 @@ function dispatchToolSchema(spec, z, opts) {
|
|
|
29781
29786
|
}
|
|
29782
29787
|
var ISSUE_REFERENCE = "An issue is a native KEY or external owner/repo#n reference; an external reference creates its native issue in the repository's dashboard-configured project or, failing that, the default project (DISPATCH_DEFAULT_PROJECT).";
|
|
29783
29788
|
var OWNER_REFERENCE = "Exactly one of issue and project is required. An issue is a native KEY or external owner/repo#n reference; a project is a project key such as CORE and addresses an unlinked project document named by artifact.";
|
|
29784
|
-
function documentOwnerValidation(requireArtifact) {
|
|
29789
|
+
function documentOwnerValidation(requireArtifact, alwaysRequireArtifact = false) {
|
|
29785
29790
|
return {
|
|
29786
29791
|
check: (value) => {
|
|
29787
29792
|
const input = value;
|
|
29788
29793
|
const hasIssue = typeof input.issue === "string";
|
|
29789
29794
|
const hasProject = typeof input.project === "string";
|
|
29790
|
-
|
|
29795
|
+
const hasArtifact = typeof input.artifact === "string";
|
|
29796
|
+
const hasRef = typeof input.ref === "string";
|
|
29797
|
+
return (hasIssue !== hasProject || !hasIssue && !hasProject && hasRef) && (!hasProject || !requireArtifact || hasArtifact) && (!alwaysRequireArtifact || hasArtifact || hasRef);
|
|
29791
29798
|
},
|
|
29792
|
-
message: "Exactly one of issue and project is required; with project, artifact names the document."
|
|
29799
|
+
message: alwaysRequireArtifact ? "Exactly one of issue and project is required; artifact or ref must name the document." : "Exactly one of issue and project is required; with project, artifact names the document."
|
|
29793
29800
|
};
|
|
29794
29801
|
}
|
|
29795
29802
|
var SPEC_SECTIONS = [
|
|
@@ -29824,6 +29831,7 @@ var dispatchToolSpecs = [
|
|
|
29824
29831
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
29825
29832
|
project: z.string().describe("Project key owning the document.").optional(),
|
|
29826
29833
|
artifact: z.string().describe("Project document slug or id.").optional(),
|
|
29834
|
+
ref: z.string().describe("Optional dispatch:// issue or document reference.").optional(),
|
|
29827
29835
|
question: z.string({ max: 800 }).describe("Decision question, at most 800 characters."),
|
|
29828
29836
|
options: z.array(z.object({
|
|
29829
29837
|
label: z.string().describe("Selectable option label."),
|
|
@@ -29876,6 +29884,7 @@ var dispatchToolSpecs = [
|
|
|
29876
29884
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
29877
29885
|
project: z.string().describe("Project key owning the document.").optional(),
|
|
29878
29886
|
artifact: z.string().describe("Artifact slug or id required when quote is given.").optional(),
|
|
29887
|
+
ref: z.string().describe("Optional dispatch:// issue or document reference.").optional(),
|
|
29879
29888
|
quote: z.string().describe("Optional exact quoted document text.").optional(),
|
|
29880
29889
|
occurrence: z.number({ int: true, min: 0 }).describe("Optional zero-based occurrence of quote.").optional(),
|
|
29881
29890
|
body: z.string({ max: 2000 }).describe("Review comment, at most 2,000 characters."),
|
|
@@ -29890,13 +29899,14 @@ var dispatchToolSpecs = [
|
|
|
29890
29899
|
arguments: (z) => ({
|
|
29891
29900
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
29892
29901
|
project: z.string().describe("Project key owning the document.").optional(),
|
|
29893
|
-
artifact: z.string().describe("Artifact slug or id containing the quoted text."),
|
|
29902
|
+
artifact: z.string().describe("Artifact slug or id containing the quoted text; optional when ref names it.").optional(),
|
|
29903
|
+
ref: z.string().describe("Optional dispatch:// issue or document reference.").optional(),
|
|
29894
29904
|
quote: z.string().describe("Exact document text to replace."),
|
|
29895
29905
|
replace_with: z.string().describe("Replacement text."),
|
|
29896
29906
|
body: z.string({ max: 2000 }).describe("Optional rationale, at most 2,000 characters.").optional(),
|
|
29897
29907
|
occurrence: z.number({ int: true, min: 0 }).describe("Optional zero-based occurrence of quote.").optional()
|
|
29898
29908
|
}),
|
|
29899
|
-
validation: documentOwnerValidation(true)
|
|
29909
|
+
validation: documentOwnerValidation(true, true)
|
|
29900
29910
|
},
|
|
29901
29911
|
{
|
|
29902
29912
|
name: "dispatch_message",
|
|
@@ -29913,7 +29923,8 @@ var dispatchToolSpecs = [
|
|
|
29913
29923
|
arguments: (z) => ({
|
|
29914
29924
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
29915
29925
|
project: z.string().describe("Project key owning the document.").optional(),
|
|
29916
|
-
artifact: z.string().describe("Artifact slug or id for the document."),
|
|
29926
|
+
artifact: z.string().describe("Artifact slug or id for the document; optional when ref names it.").optional(),
|
|
29927
|
+
ref: z.string().describe("Optional dispatch:// issue or document reference.").optional(),
|
|
29917
29928
|
ops: z.array(z.object({
|
|
29918
29929
|
op: z.enum(DOC_EDIT_OPS).describe("Edit operation."),
|
|
29919
29930
|
find: z.string().describe("Text to find for replace or delete.").optional(),
|
|
@@ -29925,7 +29936,7 @@ var dispatchToolSpecs = [
|
|
|
29925
29936
|
})).describe("Flat tagged edits; the server validates fields required for each operation."),
|
|
29926
29937
|
summary: z.string().describe("Optional named-version summary.").optional()
|
|
29927
29938
|
}),
|
|
29928
|
-
validation: documentOwnerValidation(true)
|
|
29939
|
+
validation: documentOwnerValidation(true, true)
|
|
29929
29940
|
},
|
|
29930
29941
|
{
|
|
29931
29942
|
name: "dispatch_doc_read",
|
|
@@ -31548,6 +31559,18 @@ function renderInbound(raw, sessionID, subject) {
|
|
|
31548
31559
|
if (frame.event.actor.kind === "session" && frame.event.actor.id === sessionID) {
|
|
31549
31560
|
return { skip: true, content: "", envelope };
|
|
31550
31561
|
}
|
|
31562
|
+
if (frame.event.type === "subscription.removed") {
|
|
31563
|
+
const removed = SubscriptionRemovedEventPayloadSchema.safeParse(frame.event.payload);
|
|
31564
|
+
if (!removed.success || removed.data.session_id !== sessionID) {
|
|
31565
|
+
return { skip: true, content: "", envelope };
|
|
31566
|
+
}
|
|
31567
|
+
const who = removed.data.by?.id ?? "someone";
|
|
31568
|
+
return {
|
|
31569
|
+
skip: false,
|
|
31570
|
+
content: `Unsubscribed from ${dispatchOwner(frame.event, subject ?? envelope.topic)} by ${who}`,
|
|
31571
|
+
envelope
|
|
31572
|
+
};
|
|
31573
|
+
}
|
|
31551
31574
|
askQuestion = dispatchAskQuestion(frame.event);
|
|
31552
31575
|
messageReplyPreview = dispatchMessageReplyPreview(frame.event);
|
|
31553
31576
|
dispatchEvent = {
|
|
@@ -32821,6 +32844,46 @@ function dispatchSubscriptionTopic(details) {
|
|
|
32821
32844
|
const { topic } = details;
|
|
32822
32845
|
return typeof topic === "string" && topic.startsWith("notifications.dispatch.") ? topic : null;
|
|
32823
32846
|
}
|
|
32847
|
+
function dispatchTopicLabel(topic) {
|
|
32848
|
+
if (topic.startsWith(DISPATCH_ISSUE_TOPIC_PREFIX)) {
|
|
32849
|
+
const [key] = topic.slice(DISPATCH_ISSUE_TOPIC_PREFIX.length).split(".", 2);
|
|
32850
|
+
if (key !== undefined && key !== "")
|
|
32851
|
+
return key;
|
|
32852
|
+
}
|
|
32853
|
+
if (topic.startsWith(DISPATCH_DOCUMENT_TOPIC_PREFIX)) {
|
|
32854
|
+
const [project, slug] = topic.slice(DISPATCH_DOCUMENT_TOPIC_PREFIX.length).split(".", 3);
|
|
32855
|
+
if (project !== undefined && project !== "" && slug !== undefined && slug !== "") {
|
|
32856
|
+
return `${project}/${slug}`;
|
|
32857
|
+
}
|
|
32858
|
+
}
|
|
32859
|
+
return topic;
|
|
32860
|
+
}
|
|
32861
|
+
function subscriptionRemovedTopics(raw, sessionID) {
|
|
32862
|
+
let envelope;
|
|
32863
|
+
try {
|
|
32864
|
+
envelope = JSON.parse(raw);
|
|
32865
|
+
} catch {
|
|
32866
|
+
return;
|
|
32867
|
+
}
|
|
32868
|
+
if (typeof envelope !== "object" || envelope === null)
|
|
32869
|
+
return;
|
|
32870
|
+
const { source, payload } = envelope;
|
|
32871
|
+
if (source !== "dispatch" || typeof payload !== "string")
|
|
32872
|
+
return;
|
|
32873
|
+
let event;
|
|
32874
|
+
try {
|
|
32875
|
+
event = JSON.parse(payload);
|
|
32876
|
+
} catch {
|
|
32877
|
+
return;
|
|
32878
|
+
}
|
|
32879
|
+
const parsedEvent = DispatchEventSchema.safeParse(event);
|
|
32880
|
+
if (!parsedEvent.success || parsedEvent.data.type !== "subscription.removed")
|
|
32881
|
+
return;
|
|
32882
|
+
const parsedPayload = SubscriptionRemovedEventPayloadSchema.safeParse(parsedEvent.data.payload);
|
|
32883
|
+
if (!parsedPayload.success || parsedPayload.data.session_id !== sessionID)
|
|
32884
|
+
return;
|
|
32885
|
+
return parsedPayload.data.topics ?? [];
|
|
32886
|
+
}
|
|
32824
32887
|
|
|
32825
32888
|
// ../envoy-client/src/tool-contract.ts
|
|
32826
32889
|
var DELIVERY_CONTRACT = "Delivery is at-least-once, possibly out of order across topics; use id for dedupe and at for freshness.";
|
|
@@ -33290,6 +33353,8 @@ function envoyExtension(pi) {
|
|
|
33290
33353
|
};
|
|
33291
33354
|
const deliver = async (subject, raw, reply) => {
|
|
33292
33355
|
const rendered = renderInbound(raw, sessionID, subject);
|
|
33356
|
+
for (const topic of subscriptionRemovedTopics(raw, sessionID) ?? [])
|
|
33357
|
+
closeIntentionally(topic);
|
|
33293
33358
|
const dedupeKey = rendered.envelope?.dedupe_key;
|
|
33294
33359
|
const duplicate = dedupeKey !== undefined && dedupeKeys.has(dedupeKey);
|
|
33295
33360
|
if (!duplicate && !rendered.skip) {
|
|
@@ -33681,8 +33746,16 @@ function envoyExtension(pi) {
|
|
|
33681
33746
|
if (topic === null)
|
|
33682
33747
|
return;
|
|
33683
33748
|
try {
|
|
33684
|
-
|
|
33749
|
+
const isNew = await subscribe(topic);
|
|
33750
|
+
if (isNew)
|
|
33685
33751
|
await registerSession();
|
|
33752
|
+
if (isNew) {
|
|
33753
|
+
pi.sendMessage({
|
|
33754
|
+
customType: "envoy-message",
|
|
33755
|
+
content: `Subscribed to ${dispatchTopicLabel(topic)} (every event on this issue reaches you; envoy_unsubscribe ${topic} to stop).`,
|
|
33756
|
+
display: true
|
|
33757
|
+
}, { deliverAs: "steer", triggerTurn: false });
|
|
33758
|
+
}
|
|
33686
33759
|
} catch (error) {
|
|
33687
33760
|
activeSessionContext?.ui.notify(`envoy: dispatch reply auto-subscribe failed (${messageFor(error)}); run envoy_subscribe ${topic}`, "warning");
|
|
33688
33761
|
}
|
|
@@ -169,19 +169,22 @@ retrying. Pass `summary` to name the version when recording a decision.
|
|
|
169
169
|
Add feedback with:
|
|
170
170
|
|
|
171
171
|
```ts
|
|
172
|
-
dispatch_comment({ issue?, project?, artifact?, quote?, occurrence?, body, reply_to?, reply_to_ask? })
|
|
172
|
+
dispatch_comment({ issue?, project?, artifact?, ref?, quote?, occurrence?, body, reply_to?, reply_to_ask? })
|
|
173
173
|
```
|
|
174
174
|
|
|
175
|
-
It returns issue or project-document owner details plus `comment` and, for writes, `topic`.
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
175
|
+
It returns issue or project-document owner details plus `comment` and, for writes, `topic`.
|
|
176
|
+
`ref` names the owner (an issue or project-document reference) in place of `issue`/`project`.
|
|
177
|
+
`quote` requires `artifact`; omit both for a floating issue comment. A reply (`reply_to`/`reply_to_ask`)
|
|
178
|
+
takes no `quote`; it belongs to its parent's anchor. Use `reply_to` to continue a comment thread at
|
|
179
|
+
its root; a reply to a resolved thread reopens it. Use `reply_to_ask` to reply directly under a
|
|
180
|
+
question asked with `dispatch_ask`. Comments are edited only by their author from the dashboard. A
|
|
181
|
+
delivered `comment.created` event carries the comment `id`; reply to it with
|
|
182
|
+
`dispatch_comment({ reply_to: <id> })`.
|
|
180
183
|
|
|
181
184
|
Propose an exact replacement instead of describing it:
|
|
182
185
|
|
|
183
186
|
```ts
|
|
184
|
-
dispatch_suggest({ issue?, project?, artifact, quote, replace_with, body?, occurrence? })
|
|
187
|
+
dispatch_suggest({ issue?, project?, artifact, ref?, quote, replace_with, body?, occurrence? })
|
|
185
188
|
```
|
|
186
189
|
|
|
187
190
|
It returns issue or project-document owner details plus `comment` and its write `topic`. A human accepts or rejects a suggestion.
|
|
@@ -223,9 +226,11 @@ It returns `details` `{ issue, topic, message }`. `body` is capped at 2,000 char
|
|
|
223
226
|
|
|
224
227
|
## What comes back
|
|
225
228
|
|
|
226
|
-
A write result's `details.topic` subscribes the host to its owner
|
|
229
|
+
A write result's `details.topic` subscribes the host to its owner, and the first such subscription on an issue or document also tells
|
|
230
|
+
you so (an already-subscribed write stays quiet — no repeat notice). Issue writes use `notifications.dispatch.issue.<KEY>.>`;
|
|
227
231
|
project-document writes use `notifications.dispatch.document.<PROJECT>.<SLUG>.>`. The owner topic carries every Dispatch event; `notify`
|
|
228
|
-
only controls agent wake and routed delivery.
|
|
232
|
+
only controls agent wake and routed delivery. A human may unsubscribe you from the issue or document header; you are told with a
|
|
233
|
+
`subscription.removed` notice when that happens. After a restart, catch up with:
|
|
229
234
|
|
|
230
235
|
```ts
|
|
231
236
|
dispatch_read({ issue?, project?, artifact?, ref? })
|
|
@@ -238,7 +243,12 @@ message ref, it returns that message and its reply chain. Reads do not subscribe
|
|
|
238
243
|
|
|
239
244
|
## References
|
|
240
245
|
|
|
241
|
-
Use these in document, ask, comment, and message bodies
|
|
246
|
+
Use these in document, ask, comment, and message bodies. In the dashboard, a reference renders
|
|
247
|
+
as an inline link whose text is the target's title (an issue's title, an ask's question, a
|
|
248
|
+
comment's first line, a document's name) once it resolves; a body that is only a bare reference
|
|
249
|
+
still gets an unfurl card instead. Every `ref` argument below (and `issue`/`project`) accepts
|
|
250
|
+
either form — an issue key or a project key is never ambiguous, since a project key never
|
|
251
|
+
contains a dash:
|
|
242
252
|
|
|
243
253
|
```text
|
|
244
254
|
dispatch://KEY
|