@nettee/beacon 0.4.2 → 0.4.3
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 +2 -1
- package/dist/daemon/dashboard/records.d.ts +2 -1
- package/dist/daemon/dashboard/records.js +12 -1
- package/dist/daemon/domain/types.d.ts +16 -0
- package/dist/daemon/domain/types.js +6 -0
- package/dist/daemon/outcome/content.d.ts +31 -1
- package/dist/daemon/outcome/content.js +29 -2
- package/dist/daemon/outcome/instructions.d.ts +2 -1
- package/dist/daemon/outcome/instructions.js +6 -0
- package/dist/daemon/outcome/server.d.ts +2 -1
- package/dist/daemon/outcome/server.js +14 -0
- package/dist/daemon/run/orchestrator.js +10 -2
- package/dist/daemon/runtime/pi-outcome-extension.d.ts +5 -1
- package/dist/daemon/runtime/pi-outcome-extension.js +58 -1
- package/dist/daemon/runtime/system-prompt.js +3 -1
- package/dist/daemon/state/trigger-store.js +2 -1
- package/dist/web/assets/{index-Dd_jruNx.js → index-BjhnvHaZ.js} +1 -1
- package/dist/web/assets/index-v4gFn6EO.css +2 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/assets/index-D3C6sw_J.css +0 -2
package/README.md
CHANGED
|
@@ -68,7 +68,8 @@ and lists the missing paths. Both pairs may exist; the workspace pair wins.
|
|
|
68
68
|
|
|
69
69
|
Beacon prepends an English platform template for this Run: workspace limit,
|
|
70
70
|
inbound vs Schedule vs event vs manual capabilities, the `reply_text` / `reply_card` /
|
|
71
|
-
`no_reply` / `notify_card` contract,
|
|
71
|
+
`no_reply` / `notify_card` contract, optional `submit_feedback` (instruction gaps),
|
|
72
|
+
and optional `submit_observability_feedback` (RCA observability gaps).
|
|
72
73
|
|
|
73
74
|
Protect the home and secret file before running Beacon:
|
|
74
75
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { RunRow } from "@nettee/beacon-shared";
|
|
2
|
-
import type { FeedbackRecord } from "../domain/types.js";
|
|
2
|
+
import type { FeedbackRecord, ObservabilityFeedbackRecord } from "../domain/types.js";
|
|
3
3
|
export declare const runIdPattern: RegExp;
|
|
4
4
|
export type RunSummary = {
|
|
5
5
|
profileId: string;
|
|
@@ -18,6 +18,7 @@ export type RunSummary = {
|
|
|
18
18
|
hasSessionFile: boolean;
|
|
19
19
|
systemPrompt: string | undefined;
|
|
20
20
|
feedback: FeedbackRecord | undefined;
|
|
21
|
+
observabilityFeedback: ObservabilityFeedbackRecord | undefined;
|
|
21
22
|
};
|
|
22
23
|
export declare function listRunSummaries(profilesDirectory: string): Promise<RunSummary[]>;
|
|
23
24
|
export declare function kindLabel(row: Pick<RunSummary, "kind" | "scheduleId" | "eventSource" | "eventType">): string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readdir, readFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { feedbackRecordSchema } from "../outcome/content.js";
|
|
3
|
+
import { feedbackRecordSchema, observabilityFeedbackRecordSchema, } from "../outcome/content.js";
|
|
4
4
|
export const runIdPattern = /^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/;
|
|
5
5
|
function asRecord(value) {
|
|
6
6
|
return typeof value === "object" && value !== null
|
|
@@ -14,6 +14,10 @@ function asFeedback(value) {
|
|
|
14
14
|
const parsed = feedbackRecordSchema.safeParse(value);
|
|
15
15
|
return parsed.success ? parsed.data : undefined;
|
|
16
16
|
}
|
|
17
|
+
function asObservabilityFeedback(value) {
|
|
18
|
+
const parsed = observabilityFeedbackRecordSchema.safeParse(value);
|
|
19
|
+
return parsed.success ? parsed.data : undefined;
|
|
20
|
+
}
|
|
17
21
|
async function readDirents(path) {
|
|
18
22
|
try {
|
|
19
23
|
return await readdir(path, { withFileTypes: true });
|
|
@@ -61,6 +65,7 @@ function summarize(value, hasSessionFile) {
|
|
|
61
65
|
hasSessionFile,
|
|
62
66
|
systemPrompt: asString(run?.systemPrompt),
|
|
63
67
|
feedback: asFeedback(record.feedback),
|
|
68
|
+
observabilityFeedback: asObservabilityFeedback(record.observabilityFeedback),
|
|
64
69
|
};
|
|
65
70
|
}
|
|
66
71
|
async function loadSummary(path) {
|
|
@@ -126,6 +131,12 @@ export function toRunListItem(row) {
|
|
|
126
131
|
summary: item.summary,
|
|
127
132
|
}))
|
|
128
133
|
: null,
|
|
134
|
+
observabilityFeedback: row.observabilityFeedback && row.observabilityFeedback.items.length > 0
|
|
135
|
+
? row.observabilityFeedback.items.map((item) => ({
|
|
136
|
+
priority: item.priority,
|
|
137
|
+
summary: item.summary,
|
|
138
|
+
}))
|
|
139
|
+
: null,
|
|
129
140
|
};
|
|
130
141
|
}
|
|
131
142
|
export async function findRunSummary(profilesDirectory, runId) {
|
|
@@ -84,6 +84,20 @@ export type FeedbackRecord = {
|
|
|
84
84
|
items: FeedbackItem[];
|
|
85
85
|
submittedAt: string;
|
|
86
86
|
};
|
|
87
|
+
/** Observability-gap feedback may include low; instruction feedback may not. */
|
|
88
|
+
export declare const observabilityFeedbackPriorities: readonly ["high", "medium", "low"];
|
|
89
|
+
export type ObservabilityFeedbackPriority = (typeof observabilityFeedbackPriorities)[number];
|
|
90
|
+
export type ObservabilityFeedbackItem = {
|
|
91
|
+
priority: ObservabilityFeedbackPriority;
|
|
92
|
+
summary: string;
|
|
93
|
+
};
|
|
94
|
+
export type ObservabilityFeedbackContent = {
|
|
95
|
+
items: ObservabilityFeedbackItem[];
|
|
96
|
+
};
|
|
97
|
+
export type ObservabilityFeedbackRecord = {
|
|
98
|
+
items: ObservabilityFeedbackItem[];
|
|
99
|
+
submittedAt: string;
|
|
100
|
+
};
|
|
87
101
|
export type DeliveryContent = TextReplyContent | CardContent;
|
|
88
102
|
export type RunRecord = {
|
|
89
103
|
runId: string;
|
|
@@ -132,6 +146,8 @@ export type TriggerRecord = {
|
|
|
132
146
|
finalOutcome?: FinalOutcomeRecord | undefined;
|
|
133
147
|
/** Present only when the Agent called `submit_feedback`. */
|
|
134
148
|
feedback?: FeedbackRecord | undefined;
|
|
149
|
+
/** Present only when the Agent called `submit_observability_feedback`. */
|
|
150
|
+
observabilityFeedback?: ObservabilityFeedbackRecord | undefined;
|
|
135
151
|
delivery?: DeliveryRecord | undefined;
|
|
136
152
|
notifyDelivery?: DeliveryRecord | undefined;
|
|
137
153
|
};
|
|
@@ -17,3 +17,9 @@ export const failureCodes = [
|
|
|
17
17
|
"delivery_interrupted",
|
|
18
18
|
];
|
|
19
19
|
export const feedbackPriorities = ["high", "medium"];
|
|
20
|
+
/** Observability-gap feedback may include low; instruction feedback may not. */
|
|
21
|
+
export const observabilityFeedbackPriorities = [
|
|
22
|
+
"high",
|
|
23
|
+
"medium",
|
|
24
|
+
"low",
|
|
25
|
+
];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import type { CardContent, DeliveryContent, FeedbackContent, FinalOutcomeContent, ReplyContent } from "../domain/types.js";
|
|
2
|
+
import type { CardContent, DeliveryContent, FeedbackContent, FinalOutcomeContent, ObservabilityFeedbackContent, ReplyContent } from "../domain/types.js";
|
|
3
3
|
export declare const feedbackItemSchema: z.ZodObject<{
|
|
4
4
|
priority: z.ZodEnum<{
|
|
5
5
|
high: "high";
|
|
@@ -26,6 +26,35 @@ export declare const feedbackRecordSchema: z.ZodObject<{
|
|
|
26
26
|
}, z.core.$strict>>;
|
|
27
27
|
submittedAt: z.ZodString;
|
|
28
28
|
}, z.core.$strict>;
|
|
29
|
+
export declare const observabilityFeedbackItemSchema: z.ZodObject<{
|
|
30
|
+
priority: z.ZodEnum<{
|
|
31
|
+
high: "high";
|
|
32
|
+
medium: "medium";
|
|
33
|
+
low: "low";
|
|
34
|
+
}>;
|
|
35
|
+
summary: z.ZodString;
|
|
36
|
+
}, z.core.$strict>;
|
|
37
|
+
export declare const observabilityFeedbackContentSchema: z.ZodObject<{
|
|
38
|
+
items: z.ZodArray<z.ZodObject<{
|
|
39
|
+
priority: z.ZodEnum<{
|
|
40
|
+
high: "high";
|
|
41
|
+
medium: "medium";
|
|
42
|
+
low: "low";
|
|
43
|
+
}>;
|
|
44
|
+
summary: z.ZodString;
|
|
45
|
+
}, z.core.$strict>>;
|
|
46
|
+
}, z.core.$strict>;
|
|
47
|
+
export declare const observabilityFeedbackRecordSchema: z.ZodObject<{
|
|
48
|
+
items: z.ZodArray<z.ZodObject<{
|
|
49
|
+
priority: z.ZodEnum<{
|
|
50
|
+
high: "high";
|
|
51
|
+
medium: "medium";
|
|
52
|
+
low: "low";
|
|
53
|
+
}>;
|
|
54
|
+
summary: z.ZodString;
|
|
55
|
+
}, z.core.$strict>>;
|
|
56
|
+
submittedAt: z.ZodString;
|
|
57
|
+
}, z.core.$strict>;
|
|
29
58
|
export declare const finalOutcomeContentSchema: z.ZodObject<{
|
|
30
59
|
reply: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
31
60
|
kind: z.ZodLiteral<"text">;
|
|
@@ -56,6 +85,7 @@ export type OutcomePatch = {
|
|
|
56
85
|
reply?: ReplyContent | undefined;
|
|
57
86
|
notify?: CardContent | undefined;
|
|
58
87
|
feedback?: FeedbackContent | undefined;
|
|
88
|
+
observabilityFeedback?: ObservabilityFeedbackContent | undefined;
|
|
59
89
|
};
|
|
60
90
|
export declare function parseFinalOutcomeContent(value: unknown): FinalOutcomeContent;
|
|
61
91
|
export declare function parseOutcomePatch(value: unknown): OutcomePatch;
|
|
@@ -46,6 +46,23 @@ export const feedbackRecordSchema = z
|
|
|
46
46
|
submittedAt: z.string().datetime({ offset: true }),
|
|
47
47
|
})
|
|
48
48
|
.strict();
|
|
49
|
+
export const observabilityFeedbackItemSchema = z
|
|
50
|
+
.object({
|
|
51
|
+
priority: z.enum(["high", "medium", "low"]),
|
|
52
|
+
summary: nonBlank.max(1024),
|
|
53
|
+
})
|
|
54
|
+
.strict();
|
|
55
|
+
export const observabilityFeedbackContentSchema = z
|
|
56
|
+
.object({
|
|
57
|
+
items: z.array(observabilityFeedbackItemSchema).min(1).max(3),
|
|
58
|
+
})
|
|
59
|
+
.strict();
|
|
60
|
+
export const observabilityFeedbackRecordSchema = z
|
|
61
|
+
.object({
|
|
62
|
+
items: z.array(observabilityFeedbackItemSchema).min(1).max(3),
|
|
63
|
+
submittedAt: z.string().datetime({ offset: true }),
|
|
64
|
+
})
|
|
65
|
+
.strict();
|
|
49
66
|
export const finalOutcomeContentSchema = z
|
|
50
67
|
.object({
|
|
51
68
|
reply: replySchema,
|
|
@@ -57,12 +74,14 @@ const outcomePatchSchema = z
|
|
|
57
74
|
reply: replySchema.optional(),
|
|
58
75
|
notify: cardSchema.optional(),
|
|
59
76
|
feedback: feedbackContentSchema.optional(),
|
|
77
|
+
observabilityFeedback: observabilityFeedbackContentSchema.optional(),
|
|
60
78
|
})
|
|
61
79
|
.strict()
|
|
62
80
|
.refine((value) => value.reply !== undefined ||
|
|
63
81
|
value.notify !== undefined ||
|
|
64
|
-
value.feedback !== undefined
|
|
65
|
-
|
|
82
|
+
value.feedback !== undefined ||
|
|
83
|
+
value.observabilityFeedback !== undefined, {
|
|
84
|
+
message: "Outcome submission must include reply, notify, feedback, or observabilityFeedback",
|
|
66
85
|
});
|
|
67
86
|
const legacyOutcomeSchema = z.discriminatedUnion("kind", [
|
|
68
87
|
textReplySchema,
|
|
@@ -106,6 +125,9 @@ export function mergeOutcome(current, patch) {
|
|
|
106
125
|
if (patch.feedback && current?.feedback) {
|
|
107
126
|
throw new Error("Feedback already submitted");
|
|
108
127
|
}
|
|
128
|
+
if (patch.observabilityFeedback && current?.observabilityFeedback) {
|
|
129
|
+
throw new Error("Observability feedback already submitted");
|
|
130
|
+
}
|
|
109
131
|
return {
|
|
110
132
|
...(current?.reply || patch.reply
|
|
111
133
|
? { reply: patch.reply ?? current?.reply }
|
|
@@ -116,6 +138,11 @@ export function mergeOutcome(current, patch) {
|
|
|
116
138
|
...(current?.feedback || patch.feedback
|
|
117
139
|
? { feedback: patch.feedback ?? current?.feedback }
|
|
118
140
|
: {}),
|
|
141
|
+
...(current?.observabilityFeedback || patch.observabilityFeedback
|
|
142
|
+
? {
|
|
143
|
+
observabilityFeedback: patch.observabilityFeedback ?? current?.observabilityFeedback,
|
|
144
|
+
}
|
|
145
|
+
: {}),
|
|
119
146
|
};
|
|
120
147
|
}
|
|
121
148
|
/** Stable English summary when the Agent settles without closing reply/no_reply. */
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export declare const inboundOutOfRoleReply = "\u8FD9\u6761\u6D88\u606F\u4E0D\u5728\u6211\u7684\u804C\u8D23\u8303\u56F4\u5185\u3002";
|
|
2
2
|
export declare const finalOutcomeContractInstructions: readonly ["`reply_text` sends plain text. For an inbound Feishu message, quote-reply that message. For a Schedule, message the Profile admin. For a manual Run, print to the operator. Do not fill `chat_id`.", "`reply_card` sends a titled Markdown card on the conversational channel (inbound quote-reply, or Schedule admin chat). It closes that channel by itself. Do not also call `reply_text` or `no_reply` on the same Run. Do not fill `chat_id`.", "`no_reply` finishes a Run without messaging the conversational channel. Its reason is stored for audit and is never sent. Do not use it on inbound Feishu messages.", "`notify_card` posts a titled Markdown card to this Run's configured notify group. Call it only when this Run has a notify target. Do not fill `chat_id`.", "Which tool to use for a given business result is decided by the Profile persona/task (or the situation), not by these platform lines alone.", "Beacon ignores ordinary assistant final text for Delivery."];
|
|
3
|
-
export declare const feedbackContractInstructions: readonly ["If this Run had a real high or medium problem with instructions, Skills, dependencies, or tools, you may call `submit_feedback` once with one to three items.", "If there are none, do not call it. Do not invent items. Do not report low-priority issues, taste, or problems you already worked around without needing a platform change."];
|
|
3
|
+
export declare const feedbackContractInstructions: readonly ["If this Run had a real high or medium problem with instructions, Skills, dependencies, or tools, you may call `submit_feedback` once with one to three items.", "If there are none, do not call it. Do not invent items. Do not report low-priority issues, taste, or problems you already worked around without needing a platform change.", "Do not put observability gaps in `submit_feedback`. Use `submit_observability_feedback` for those."];
|
|
4
|
+
export declare const observabilityFeedbackContractInstructions: readonly ["If root-cause analysis on this Run was held back by missing observability (logs, metrics, traces, event correlation, or similar signals), you may call `submit_observability_feedback` once with one to three items at high, medium, or low priority.", "Say what signal is missing and how adding it would make RCA more useful or more certain. If there are none, do not call it. Do not invent items.", "Do not put instruction, Skill, dependency, or tool problems in `submit_observability_feedback`. Use `submit_feedback` for those."];
|
|
@@ -10,4 +10,10 @@ export const finalOutcomeContractInstructions = [
|
|
|
10
10
|
export const feedbackContractInstructions = [
|
|
11
11
|
"If this Run had a real high or medium problem with instructions, Skills, dependencies, or tools, you may call `submit_feedback` once with one to three items.",
|
|
12
12
|
"If there are none, do not call it. Do not invent items. Do not report low-priority issues, taste, or problems you already worked around without needing a platform change.",
|
|
13
|
+
"Do not put observability gaps in `submit_feedback`. Use `submit_observability_feedback` for those.",
|
|
14
|
+
];
|
|
15
|
+
export const observabilityFeedbackContractInstructions = [
|
|
16
|
+
"If root-cause analysis on this Run was held back by missing observability (logs, metrics, traces, event correlation, or similar signals), you may call `submit_observability_feedback` once with one to three items at high, medium, or low priority.",
|
|
17
|
+
"Say what signal is missing and how adding it would make RCA more useful or more certain. If there are none, do not call it. Do not invent items.",
|
|
18
|
+
"Do not put instruction, Skill, dependency, or tool problems in `submit_observability_feedback`. Use `submit_feedback` for those.",
|
|
13
19
|
];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FeedbackRecord, FinalOutcomeContent } from "../domain/types.js";
|
|
1
|
+
import type { FeedbackRecord, FinalOutcomeContent, ObservabilityFeedbackRecord } from "../domain/types.js";
|
|
2
2
|
export type OutcomeBinding = {
|
|
3
3
|
socketPath: string;
|
|
4
4
|
runToken: string;
|
|
@@ -7,6 +7,7 @@ export type OutcomeSubmission = {
|
|
|
7
7
|
binding: OutcomeBinding;
|
|
8
8
|
take(): FinalOutcomeContent;
|
|
9
9
|
takeFeedback(): FeedbackRecord | undefined;
|
|
10
|
+
takeObservabilityFeedback(): ObservabilityFeedbackRecord | undefined;
|
|
10
11
|
cancel(): void;
|
|
11
12
|
};
|
|
12
13
|
export type OutcomeSink = {
|
|
@@ -76,6 +76,10 @@ export async function startOutcomeServer(options = {}) {
|
|
|
76
76
|
if (patch.feedback && !previous?.feedback) {
|
|
77
77
|
submission.feedbackSubmittedAt = new Date().toISOString();
|
|
78
78
|
}
|
|
79
|
+
if (patch.observabilityFeedback && !previous?.observabilityFeedback) {
|
|
80
|
+
submission.observabilityFeedbackSubmittedAt =
|
|
81
|
+
new Date().toISOString();
|
|
82
|
+
}
|
|
79
83
|
}
|
|
80
84
|
catch (error) {
|
|
81
85
|
respond(socket, {
|
|
@@ -113,6 +117,16 @@ export async function startOutcomeServer(options = {}) {
|
|
|
113
117
|
}
|
|
114
118
|
return { items, submittedAt: record.feedbackSubmittedAt };
|
|
115
119
|
},
|
|
120
|
+
takeObservabilityFeedback() {
|
|
121
|
+
const items = record.outcome?.observabilityFeedback?.items;
|
|
122
|
+
if (!items || record.observabilityFeedbackSubmittedAt === undefined) {
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
items,
|
|
127
|
+
submittedAt: record.observabilityFeedbackSubmittedAt,
|
|
128
|
+
};
|
|
129
|
+
},
|
|
116
130
|
cancel() {
|
|
117
131
|
submissions.delete(runToken);
|
|
118
132
|
},
|
|
@@ -253,7 +253,7 @@ export class RunOrchestrator {
|
|
|
253
253
|
await this.deliverField(triggerKey, "notifyDelivery", current.notifyTarget, outcome.notify, create && current.notifyDelivery === undefined);
|
|
254
254
|
}
|
|
255
255
|
}
|
|
256
|
-
async fail(triggerKey, runId, code, error, submitted, settle) {
|
|
256
|
+
async fail(triggerKey, runId, code, error, submitted, submittedObservability, settle) {
|
|
257
257
|
let detail = isMissingReplyOutcomeError(error)
|
|
258
258
|
? MISSING_REPLY_OUTCOME_SUMMARY
|
|
259
259
|
: summary(error);
|
|
@@ -278,6 +278,9 @@ export class RunOrchestrator {
|
|
|
278
278
|
submittedAt: this.timestamp(),
|
|
279
279
|
},
|
|
280
280
|
...(submitted ? { feedback: submitted } : {}),
|
|
281
|
+
...(submittedObservability
|
|
282
|
+
? { observabilityFeedback: submittedObservability }
|
|
283
|
+
: {}),
|
|
281
284
|
}));
|
|
282
285
|
await this.deliverOutcome(triggerKey);
|
|
283
286
|
}
|
|
@@ -336,6 +339,7 @@ export class RunOrchestrator {
|
|
|
336
339
|
text: completion.text,
|
|
337
340
|
};
|
|
338
341
|
const submitted = submission.takeFeedback();
|
|
342
|
+
const submittedObservability = submission.takeObservabilityFeedback();
|
|
339
343
|
const outcome = normalizeAgentOutcome(input, submission.take(), record.notifyTarget);
|
|
340
344
|
await this.options.store.update(triggerKey, (current) => ({
|
|
341
345
|
...current,
|
|
@@ -352,18 +356,22 @@ export class RunOrchestrator {
|
|
|
352
356
|
submittedAt: this.timestamp(),
|
|
353
357
|
},
|
|
354
358
|
...(submitted ? { feedback: submitted } : {}),
|
|
359
|
+
...(submittedObservability
|
|
360
|
+
? { observabilityFeedback: submittedObservability }
|
|
361
|
+
: {}),
|
|
355
362
|
}));
|
|
356
363
|
await this.deliverOutcome(triggerKey);
|
|
357
364
|
}
|
|
358
365
|
catch (error) {
|
|
359
366
|
const submitted = submission.takeFeedback();
|
|
367
|
+
const submittedObservability = submission.takeObservabilityFeedback();
|
|
360
368
|
submission.cancel();
|
|
361
369
|
const code = error instanceof PiRuntimeError
|
|
362
370
|
? error.code
|
|
363
371
|
: isMissingReplyOutcomeError(error)
|
|
364
372
|
? "outcome_missing"
|
|
365
373
|
: "runtime_exit_failed";
|
|
366
|
-
await this.fail(triggerKey, runId, code, error, submitted, settle);
|
|
374
|
+
await this.fail(triggerKey, runId, code, error, submitted, submittedObservability, settle);
|
|
367
375
|
}
|
|
368
376
|
}
|
|
369
377
|
async process(triggerKey, normalize) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CardContent, FeedbackContent, FinalOutcomeContent } from "../domain/types.js";
|
|
1
|
+
import type { CardContent, FeedbackContent, FinalOutcomeContent, ObservabilityFeedbackContent } from "../domain/types.js";
|
|
2
2
|
type ToolResult = {
|
|
3
3
|
content: Array<{
|
|
4
4
|
type: "text";
|
|
@@ -25,6 +25,9 @@ export type CardOutcomeToolParams = {
|
|
|
25
25
|
export type FeedbackToolParams = {
|
|
26
26
|
items: FeedbackContent["items"];
|
|
27
27
|
};
|
|
28
|
+
export type ObservabilityFeedbackToolParams = {
|
|
29
|
+
items: ObservabilityFeedbackContent["items"];
|
|
30
|
+
};
|
|
28
31
|
type ExtensionApi = {
|
|
29
32
|
registerTool<TParams>(tool: {
|
|
30
33
|
name: string;
|
|
@@ -39,5 +42,6 @@ export declare function replyCardFromToolParams(params: CardOutcomeToolParams):
|
|
|
39
42
|
export declare function noReplyFromToolParams(params: NoReplyOutcomeToolParams): FinalOutcomeContent;
|
|
40
43
|
export declare function notifyCardFromToolParams(params: CardOutcomeToolParams): CardContent;
|
|
41
44
|
export declare function feedbackFromToolParams(params: FeedbackToolParams): FeedbackContent;
|
|
45
|
+
export declare function observabilityFeedbackFromToolParams(params: ObservabilityFeedbackToolParams): ObservabilityFeedbackContent;
|
|
42
46
|
export default function registerOutcomeTools(pi: ExtensionApi): void;
|
|
43
47
|
export {};
|
|
@@ -39,6 +39,15 @@ export function feedbackFromToolParams(params) {
|
|
|
39
39
|
throw new Error("submit_feedback produced no items");
|
|
40
40
|
return patch.feedback;
|
|
41
41
|
}
|
|
42
|
+
export function observabilityFeedbackFromToolParams(params) {
|
|
43
|
+
const patch = parseOutcomePatch({
|
|
44
|
+
observabilityFeedback: { items: params.items },
|
|
45
|
+
});
|
|
46
|
+
if (!patch.observabilityFeedback) {
|
|
47
|
+
throw new Error("submit_observability_feedback produced no items");
|
|
48
|
+
}
|
|
49
|
+
return patch.observabilityFeedback;
|
|
50
|
+
}
|
|
42
51
|
async function invokeBeaconCli(outcome, signal) {
|
|
43
52
|
const cliPath = process.env.BEACON_CLI_PATH;
|
|
44
53
|
if (!cliPath)
|
|
@@ -209,7 +218,7 @@ export default function registerOutcomeTools(pi) {
|
|
|
209
218
|
pi.registerTool({
|
|
210
219
|
name: "submit_feedback",
|
|
211
220
|
label: "Submit Feedback",
|
|
212
|
-
description: "Optionally report one to three high or medium problems with instructions, Skills, dependencies, or tools on this Run. Call at most once, and only when such a problem actually existed. Do not call this tool when there is nothing to report.",
|
|
221
|
+
description: "Optionally report one to three high or medium problems with instructions, Skills, dependencies, or tools on this Run. Call at most once, and only when such a problem actually existed. Do not use this for missing observability — use submit_observability_feedback instead. Do not call this tool when there is nothing to report.",
|
|
213
222
|
parameters: {
|
|
214
223
|
type: "object",
|
|
215
224
|
properties: {
|
|
@@ -249,4 +258,52 @@ export default function registerOutcomeTools(pi) {
|
|
|
249
258
|
};
|
|
250
259
|
},
|
|
251
260
|
});
|
|
261
|
+
pi.registerTool({
|
|
262
|
+
name: "submit_observability_feedback",
|
|
263
|
+
label: "Submit Observability Feedback",
|
|
264
|
+
description: "Optionally report one to three observability gaps (missing logs, metrics, traces, or correlation signals) that held back root-cause analysis on this Run. Priority may be high, medium, or low. Call at most once, and only when such a gap actually existed. Do not use this for instruction/Skill/dependency/tool problems — use submit_feedback instead. Do not call this tool when there is nothing to report.",
|
|
265
|
+
parameters: {
|
|
266
|
+
type: "object",
|
|
267
|
+
properties: {
|
|
268
|
+
items: {
|
|
269
|
+
type: "array",
|
|
270
|
+
minItems: 1,
|
|
271
|
+
maxItems: 3,
|
|
272
|
+
description: "One to three observability findings. Omit the tool call entirely when there are none.",
|
|
273
|
+
items: {
|
|
274
|
+
type: "object",
|
|
275
|
+
properties: {
|
|
276
|
+
priority: {
|
|
277
|
+
type: "string",
|
|
278
|
+
enum: ["high", "medium", "low"],
|
|
279
|
+
description: "high, medium, or low — how much the missing signal limited RCA confidence.",
|
|
280
|
+
},
|
|
281
|
+
summary: {
|
|
282
|
+
type: "string",
|
|
283
|
+
minLength: 1,
|
|
284
|
+
maxLength: 1024,
|
|
285
|
+
description: "One or two sentences naming the missing signal and how adding it would help RCA.",
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
required: ["priority", "summary"],
|
|
289
|
+
additionalProperties: false,
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
required: ["items"],
|
|
294
|
+
additionalProperties: false,
|
|
295
|
+
},
|
|
296
|
+
async execute(_toolCallId, params, signal) {
|
|
297
|
+
await invokeBeaconCli({ observabilityFeedback: { items: params.items } }, signal);
|
|
298
|
+
return {
|
|
299
|
+
content: [
|
|
300
|
+
{
|
|
301
|
+
type: "text",
|
|
302
|
+
text: "Observability feedback accepted by Beacon.",
|
|
303
|
+
},
|
|
304
|
+
],
|
|
305
|
+
details: { submitted: true },
|
|
306
|
+
};
|
|
307
|
+
},
|
|
308
|
+
});
|
|
252
309
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { feedbackContractInstructions, finalOutcomeContractInstructions, } from "../outcome/instructions.js";
|
|
2
|
+
import { feedbackContractInstructions, finalOutcomeContractInstructions, observabilityFeedbackContractInstructions, } from "../outcome/instructions.js";
|
|
3
3
|
export function agentSystemPromptTrigger(input, notifyTarget) {
|
|
4
4
|
const notify = notifyTarget !== undefined;
|
|
5
5
|
if (input?.kind === "event") {
|
|
@@ -30,6 +30,8 @@ export function buildAgentSystemPrompt(profile, trigger) {
|
|
|
30
30
|
"",
|
|
31
31
|
...feedbackContractInstructions,
|
|
32
32
|
"",
|
|
33
|
+
...observabilityFeedbackContractInstructions,
|
|
34
|
+
"",
|
|
33
35
|
profile.persona,
|
|
34
36
|
"",
|
|
35
37
|
profile.task,
|
|
@@ -4,7 +4,7 @@ import { basename, dirname, isAbsolute, join } from "node:path";
|
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { failureCodes, } from "../domain/types.js";
|
|
6
6
|
import { cloudEventSchema } from "../events/cloudevent.js";
|
|
7
|
-
import { feedbackRecordSchema, finalOutcomeContentSchema, parseFinalOutcomeContent, } from "../outcome/content.js";
|
|
7
|
+
import { feedbackRecordSchema, finalOutcomeContentSchema, observabilityFeedbackRecordSchema, parseFinalOutcomeContent, } from "../outcome/content.js";
|
|
8
8
|
const timestamp = z.string().datetime({ offset: true });
|
|
9
9
|
const failureSchema = z
|
|
10
10
|
.object({ code: z.enum(failureCodes), summary: z.string().min(1).max(4096) })
|
|
@@ -122,6 +122,7 @@ const triggerRecordSchema = z
|
|
|
122
122
|
run: runSchema.optional(),
|
|
123
123
|
finalOutcome: outcomeSchema.optional(),
|
|
124
124
|
feedback: feedbackRecordSchema.optional(),
|
|
125
|
+
observabilityFeedback: observabilityFeedbackRecordSchema.optional(),
|
|
125
126
|
delivery: deliverySchema.optional(),
|
|
126
127
|
notifyDelivery: deliverySchema.optional(),
|
|
127
128
|
})
|