@nettee/beacon 0.2.5 → 0.2.6

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.
@@ -117,9 +117,20 @@ export function mergeOutcome(current, patch) {
117
117
  : {}),
118
118
  };
119
119
  }
120
+ /** Stable English summary when the Agent settles without closing reply/no_reply. */
121
+ export const MISSING_REPLY_OUTCOME_SUMMARY = "Expected exactly one of `reply` or `no_reply`, but the Agent Runtime settled without submitting either";
122
+ export function isMissingReplyOutcomeError(error) {
123
+ if (!(error instanceof Error))
124
+ return false;
125
+ if (error.message === MISSING_REPLY_OUTCOME_SUMMARY)
126
+ return true;
127
+ // Legacy summaries from earlier Beacon builds
128
+ return (/settled without submitting reply or no_reply/i.test(error.message) ||
129
+ /submitting a (Final Outcome|reply or no_reply)/i.test(error.message));
130
+ }
120
131
  export function completeOutcome(partial) {
121
132
  if (!partial.reply) {
122
- throw new Error("Agent Runtime settled without submitting reply or no_reply");
133
+ throw new Error(MISSING_REPLY_OUTCOME_SUMMARY);
123
134
  }
124
135
  return {
125
136
  reply: partial.reply,
@@ -1,5 +1,6 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { isAbsolute, join } from "node:path";
3
+ import { isMissingReplyOutcomeError, MISSING_REPLY_OUTCOME_SUMMARY, } from "../outcome/content.js";
3
4
  import { inboundOutOfRoleReply } from "../outcome/instructions.js";
4
5
  import { PiRuntimeError } from "../runtime/pi-rpc.js";
5
6
  import { agentSystemPromptTrigger, buildAgentSystemPrompt, profilePromptDigest, } from "../runtime/system-prompt.js";
@@ -39,11 +40,24 @@ function summary(error) {
39
40
  const value = error instanceof Error ? error.message : String(error);
40
41
  return value.slice(0, 4096) || "unknown failure";
41
42
  }
42
- function failureOutcome(runId) {
43
+ /** User-visible (Feishu/admin) failure text. Keep codes stable; explain missing outcomes. */
44
+ export function formatFailureReplyText(runId, failure) {
45
+ if (failure?.code === "outcome_missing") {
46
+ return [
47
+ `处理失败(run_id=${runId},code=outcome_missing):`,
48
+ "期望恰好调用一次 `reply` 或 `no_reply`,但 Agent 已结束且两者均未提交。",
49
+ ].join("");
50
+ }
51
+ if (failure) {
52
+ return `处理失败(run_id=${runId},code=${failure.code}),请查看 Beacon 本地记录。`;
53
+ }
54
+ return `处理失败(run_id=${runId}),请查看 Beacon 本地记录。`;
55
+ }
56
+ function failureOutcome(runId, failure) {
43
57
  return {
44
58
  reply: {
45
59
  kind: "text",
46
- text: `处理失败(run_id=${runId}),请查看 Beacon 本地记录。`,
60
+ text: formatFailureReplyText(runId, failure),
47
61
  },
48
62
  };
49
63
  }
@@ -169,7 +183,10 @@ export class RunOrchestrator {
169
183
  }
170
184
  }
171
185
  async fail(triggerKey, runId, code, error, submitted) {
172
- const detail = summary(error);
186
+ const detail = isMissingReplyOutcomeError(error)
187
+ ? MISSING_REPLY_OUTCOME_SUMMARY
188
+ : summary(error);
189
+ const failure = { code, summary: detail };
173
190
  await this.options.store.update(triggerKey, (current) => ({
174
191
  ...current,
175
192
  run: {
@@ -177,11 +194,11 @@ export class RunOrchestrator {
177
194
  this.newRun(runId, "queued", this.triggerFor(current))),
178
195
  state: "failed",
179
196
  finishedAt: this.timestamp(),
180
- failure: { code, summary: detail },
197
+ failure,
181
198
  },
182
199
  finalOutcome: {
183
200
  origin: "beacon_failure",
184
- content: failureOutcome(runId),
201
+ content: failureOutcome(runId, failure),
185
202
  submittedAt: this.timestamp(),
186
203
  },
187
204
  ...(submitted ? { feedback: submitted } : {}),
@@ -258,8 +275,7 @@ export class RunOrchestrator {
258
275
  submission.cancel();
259
276
  const code = error instanceof PiRuntimeError
260
277
  ? error.code
261
- : error instanceof Error &&
262
- /submitting a (Final Outcome|reply or no_reply)/.test(error.message)
278
+ : isMissingReplyOutcomeError(error)
263
279
  ? "outcome_missing"
264
280
  : "runtime_exit_failed";
265
281
  await this.fail(triggerKey, runId, code, error, submitted);
@@ -272,15 +288,16 @@ export class RunOrchestrator {
272
288
  input = await normalize();
273
289
  }
274
290
  catch (error) {
291
+ const failure = {
292
+ code: "trigger_normalization_failed",
293
+ summary: summary(error),
294
+ };
275
295
  await this.options.store.update(triggerKey, (current) => ({
276
296
  ...current,
277
- run: this.newRun(runId, "failed", this.triggerFor(current), {
278
- code: "trigger_normalization_failed",
279
- summary: summary(error),
280
- }),
297
+ run: this.newRun(runId, "failed", this.triggerFor(current), failure),
281
298
  finalOutcome: {
282
299
  origin: "beacon_failure",
283
- content: failureOutcome(runId),
300
+ content: failureOutcome(runId, failure),
284
301
  submittedAt: this.timestamp(),
285
302
  },
286
303
  }));
@@ -49,7 +49,8 @@ export async function triggerScheduleOnce(options) {
49
49
  if (record.run.state !== "succeeded" || !record.finalOutcome) {
50
50
  const failure = record.run.failure?.code ??
51
51
  (!record.finalOutcome ? "outcome_missing" : "unknown");
52
- throw new Error(`Schedule Trigger failed (run_id=${record.run.runId}): ${failure}`);
52
+ const detail = record.run.failure?.summary;
53
+ throw new Error(`Schedule Trigger failed (run_id=${record.run.runId}): ${failure}${detail ? ` — ${detail}` : ""}`);
53
54
  }
54
55
  return record;
55
56
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nettee/beacon",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "description": "A single-host bridge from messaging and schedules to isolated agent runs",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {