@nettee/beacon 0.3.5 → 0.4.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.
Files changed (119) hide show
  1. package/README.md +25 -8
  2. package/deploy/io.nettee.beacon.plist.example +0 -2
  3. package/dist/cli-app.d.ts +31 -0
  4. package/dist/cli-app.js +6 -13
  5. package/dist/cli.d.ts +2 -0
  6. package/dist/cli.js +11 -8
  7. package/dist/daemon/config/channels.d.ts +11 -0
  8. package/dist/daemon/config/event-credentials.d.ts +6 -0
  9. package/dist/daemon/config/global.d.ts +40 -0
  10. package/dist/daemon/config/profile.d.ts +38 -0
  11. package/dist/daemon/config/registry.d.ts +3 -0
  12. package/dist/daemon/config/runtime-environment.d.ts +2 -0
  13. package/dist/daemon/config/secrets.d.ts +5 -0
  14. package/dist/daemon/config/yaml.d.ts +1 -0
  15. package/dist/daemon/dashboard/export.d.ts +11 -0
  16. package/dist/daemon/dashboard/profiles.d.ts +5 -0
  17. package/dist/daemon/dashboard/records.d.ts +25 -0
  18. package/dist/daemon/dashboard/server.d.ts +15 -0
  19. package/dist/daemon/dashboard/session-prompt.d.ts +4 -0
  20. package/dist/daemon/dashboard/static.d.ts +2 -0
  21. package/dist/daemon/doctor.d.ts +1 -0
  22. package/dist/daemon/domain/types.d.ts +137 -0
  23. package/dist/daemon/events/cloudevent.d.ts +25 -0
  24. package/dist/daemon/events/pipeline.d.ts +35 -0
  25. package/dist/daemon/events/server.d.ts +15 -0
  26. package/dist/daemon/events/store.d.ts +58 -0
  27. package/dist/daemon/feishu/doctor.d.ts +2 -0
  28. package/dist/daemon/feishu/final-outcome-card.d.ts +20 -0
  29. package/dist/daemon/feishu/gateway.d.ts +46 -0
  30. package/dist/daemon/feishu/intake.d.ts +37 -0
  31. package/dist/daemon/feishu/mentions.d.ts +37 -0
  32. package/dist/daemon/feishu/message-gateway.d.ts +4 -0
  33. package/dist/daemon/feishu/message-pipeline.d.ts +13 -0
  34. package/dist/daemon/feishu/reply-experiment.d.ts +3 -0
  35. package/dist/daemon/feishu/trigger-input.d.ts +36 -0
  36. package/dist/daemon/index.d.ts +6 -0
  37. package/dist/daemon/index.js +5 -0
  38. package/dist/daemon/manual-trigger.d.ts +2 -0
  39. package/dist/{manual-trigger.js → daemon/manual-trigger.js} +2 -1
  40. package/dist/daemon/message/gateway.d.ts +9 -0
  41. package/dist/daemon/outcome/content.d.ts +68 -0
  42. package/dist/daemon/outcome/doctor.d.ts +2 -0
  43. package/dist/{outcome → daemon/outcome}/doctor.js +2 -3
  44. package/dist/daemon/outcome/instructions.d.ts +3 -0
  45. package/dist/daemon/outcome/server.d.ts +20 -0
  46. package/dist/daemon/outcome/submit.d.ts +5 -0
  47. package/dist/daemon/run/create-pi-orchestrator.d.ts +17 -0
  48. package/dist/{run → daemon/run}/create-pi-orchestrator.js +1 -2
  49. package/dist/daemon/run/orchestrator.d.ts +61 -0
  50. package/dist/daemon/run/profile-runner.d.ts +9 -0
  51. package/dist/daemon/run/queue.d.ts +20 -0
  52. package/dist/daemon/runtime/pi-doctor.d.ts +1 -0
  53. package/dist/daemon/runtime/pi-outcome-extension.d.ts +43 -0
  54. package/dist/daemon/runtime/pi-rpc.d.ts +42 -0
  55. package/dist/{runtime → daemon/runtime}/pi-rpc.js +3 -1
  56. package/dist/daemon/runtime/system-prompt.d.ts +10 -0
  57. package/dist/daemon/runtime-options.d.ts +4 -0
  58. package/dist/daemon/runtime-options.js +1 -0
  59. package/dist/daemon/schedule/cron.d.ts +2 -0
  60. package/dist/daemon/schedule/cursor-store.d.ts +18 -0
  61. package/dist/daemon/schedule/loop.d.ts +17 -0
  62. package/dist/daemon/schedule/reconciler.d.ts +16 -0
  63. package/dist/daemon/schedule-trigger.d.ts +18 -0
  64. package/dist/{schedule-trigger.js → daemon/schedule-trigger.js} +2 -1
  65. package/dist/daemon/service.d.ts +4 -0
  66. package/dist/{service.js → daemon/service.js} +3 -1
  67. package/dist/daemon/state/trigger-store.d.ts +30 -0
  68. package/dist/version.d.ts +1 -0
  69. package/dist/web/assets/index-BP5xd2Iv.js +9 -0
  70. package/dist/web/assets/index-D3C6sw_J.css +2 -0
  71. package/dist/{dashboard/ui → web}/index.html +2 -2
  72. package/package.json +13 -25
  73. package/dist/dashboard/ui/assets/index-BNrf6SEW.css +0 -2
  74. package/dist/dashboard/ui/assets/index-DNeyL6DY.js +0 -9
  75. /package/dist/{config → daemon/config}/channels.js +0 -0
  76. /package/dist/{config → daemon/config}/event-credentials.js +0 -0
  77. /package/dist/{config → daemon/config}/global.js +0 -0
  78. /package/dist/{config → daemon/config}/profile.js +0 -0
  79. /package/dist/{config → daemon/config}/registry.js +0 -0
  80. /package/dist/{config → daemon/config}/runtime-environment.js +0 -0
  81. /package/dist/{config → daemon/config}/secrets.js +0 -0
  82. /package/dist/{config → daemon/config}/yaml.js +0 -0
  83. /package/dist/{dashboard → daemon/dashboard}/export.js +0 -0
  84. /package/dist/{dashboard → daemon/dashboard}/profiles.js +0 -0
  85. /package/dist/{dashboard → daemon/dashboard}/records.js +0 -0
  86. /package/dist/{dashboard → daemon/dashboard}/server.js +0 -0
  87. /package/dist/{dashboard → daemon/dashboard}/session-prompt.js +0 -0
  88. /package/dist/{dashboard → daemon/dashboard}/static.js +0 -0
  89. /package/dist/{doctor.js → daemon/doctor.js} +0 -0
  90. /package/dist/{domain → daemon/domain}/types.js +0 -0
  91. /package/dist/{events → daemon/events}/cloudevent.js +0 -0
  92. /package/dist/{events → daemon/events}/pipeline.js +0 -0
  93. /package/dist/{events → daemon/events}/server.js +0 -0
  94. /package/dist/{events → daemon/events}/store.js +0 -0
  95. /package/dist/{feishu → daemon/feishu}/doctor.js +0 -0
  96. /package/dist/{feishu → daemon/feishu}/final-outcome-card.js +0 -0
  97. /package/dist/{feishu → daemon/feishu}/gateway.js +0 -0
  98. /package/dist/{feishu → daemon/feishu}/intake.js +0 -0
  99. /package/dist/{feishu → daemon/feishu}/mentions.js +0 -0
  100. /package/dist/{feishu → daemon/feishu}/message-gateway.js +0 -0
  101. /package/dist/{feishu → daemon/feishu}/message-pipeline.js +0 -0
  102. /package/dist/{feishu → daemon/feishu}/reply-experiment.js +0 -0
  103. /package/dist/{feishu → daemon/feishu}/trigger-input.js +0 -0
  104. /package/dist/{message → daemon/message}/gateway.js +0 -0
  105. /package/dist/{outcome → daemon/outcome}/content.js +0 -0
  106. /package/dist/{outcome → daemon/outcome}/instructions.js +0 -0
  107. /package/dist/{outcome → daemon/outcome}/server.js +0 -0
  108. /package/dist/{outcome → daemon/outcome}/submit.js +0 -0
  109. /package/dist/{run → daemon/run}/orchestrator.js +0 -0
  110. /package/dist/{run → daemon/run}/profile-runner.js +0 -0
  111. /package/dist/{run → daemon/run}/queue.js +0 -0
  112. /package/dist/{runtime → daemon/runtime}/pi-doctor.js +0 -0
  113. /package/dist/{runtime → daemon/runtime}/pi-outcome-extension.js +0 -0
  114. /package/dist/{runtime → daemon/runtime}/system-prompt.js +0 -0
  115. /package/dist/{schedule → daemon/schedule}/cron.js +0 -0
  116. /package/dist/{schedule → daemon/schedule}/cursor-store.js +0 -0
  117. /package/dist/{schedule → daemon/schedule}/loop.js +0 -0
  118. /package/dist/{schedule → daemon/schedule}/reconciler.js +0 -0
  119. /package/dist/{state → daemon/state}/trigger-store.js +0 -0
@@ -0,0 +1,20 @@
1
+ import type { CardContent } from "../domain/types.js";
2
+ type CardOutcome = CardContent;
3
+ export type FeishuFinalOutcomeCard = {
4
+ config: {
5
+ wide_screen_mode: true;
6
+ };
7
+ header: {
8
+ template: "blue";
9
+ title: {
10
+ tag: "plain_text";
11
+ content: string;
12
+ };
13
+ };
14
+ elements: object[];
15
+ };
16
+ export declare function buildFeishuFinalOutcomeCard(outcome: CardOutcome, options?: {
17
+ generatedAt?: Date;
18
+ timeZone?: string;
19
+ }): FeishuFinalOutcomeCard;
20
+ export {};
@@ -0,0 +1,46 @@
1
+ import type { FeishuCredentials } from "../config/secrets.js";
2
+ import type { DeliveryContent } from "../domain/types.js";
3
+ import type { DeliveryAdapter } from "../run/orchestrator.js";
4
+ import type { FeishuMessageEvent } from "./intake.js";
5
+ import type { FeishuMessageGateway } from "./message-gateway.js";
6
+ import type { FetchedMessage } from "./trigger-input.js";
7
+ type ApiResult = {
8
+ code?: number | undefined;
9
+ msg?: string | undefined;
10
+ request_id?: string | undefined;
11
+ };
12
+ type MessageSendResult = ApiResult & {
13
+ data?: {
14
+ message_id?: string | undefined;
15
+ } | undefined;
16
+ };
17
+ /** Prefer Feishu's outbound `message_id` so quote-replies can resume that Run's Pi session. */
18
+ export declare function providerRequestIdFromMessageSend(result: MessageSendResult): {
19
+ providerRequestId?: string | undefined;
20
+ };
21
+ type ShutdownSignalSource = {
22
+ once(event: "SIGINT" | "SIGTERM", listener: () => void): unknown;
23
+ off(event: "SIGINT" | "SIGTERM", listener: () => void): unknown;
24
+ };
25
+ export declare function waitForShutdown(signals?: ShutdownSignalSource): Promise<void>;
26
+ export declare function shouldAcceptFeishuMessage(chatType: string, senderType: string, messageId: string, processedMessageIds: ReadonlySet<string>): boolean;
27
+ export declare function encodeFeishuFinalOutcome(outcome: DeliveryContent): {
28
+ msgType: "text" | "interactive";
29
+ content: string;
30
+ };
31
+ export declare class FeishuGateway implements FeishuMessageGateway {
32
+ private readonly credentials;
33
+ private readonly client;
34
+ constructor(credentials: FeishuCredentials);
35
+ checkReady(): Promise<void>;
36
+ run(handleEvent: (event: FeishuMessageEvent) => Promise<unknown>, shutdown?: Promise<void>): Promise<void>;
37
+ acknowledge(messageId: string): Promise<{
38
+ clear: () => Promise<void>;
39
+ }>;
40
+ fetchMessage(messageId: string): Promise<FetchedMessage>;
41
+ deliver(target: Parameters<DeliveryAdapter["deliver"]>[0], outcome: Parameters<DeliveryAdapter["deliver"]>[1]): Promise<{
42
+ providerRequestId?: string | undefined;
43
+ }>;
44
+ }
45
+ export declare function createFeishuGateway(credentials: FeishuCredentials): FeishuGateway;
46
+ export {};
@@ -0,0 +1,37 @@
1
+ import type { TriggerInput } from "../domain/types.js";
2
+ import type { Acknowledgement } from "../message/gateway.js";
3
+ import type { TriggerStore } from "../state/trigger-store.js";
4
+ import { type FeishuMention } from "./mentions.js";
5
+ import { type FetchedMessage } from "./trigger-input.js";
6
+ export type FeishuMessageEvent = {
7
+ event_id?: string | undefined;
8
+ sender: {
9
+ sender_type: string;
10
+ sender_id?: {
11
+ open_id?: string | undefined;
12
+ } | undefined;
13
+ };
14
+ message: {
15
+ message_id: string;
16
+ chat_id: string;
17
+ chat_type: string;
18
+ message_type: string;
19
+ content: string;
20
+ parent_id?: string | undefined;
21
+ mentions?: FeishuMention[] | undefined;
22
+ };
23
+ };
24
+ export type FeishuIntakeOptions = {
25
+ store: TriggerStore;
26
+ process(triggerKey: string, normalize: () => Promise<TriggerInput>): Promise<void>;
27
+ fetchMessage(messageId: string): Promise<FetchedMessage>;
28
+ acknowledge(messageId: string): Promise<Acknowledgement>;
29
+ onFatal(error: Error): void;
30
+ };
31
+ export declare class FeishuIntake {
32
+ private readonly options;
33
+ private readonly active;
34
+ constructor(options: FeishuIntakeOptions);
35
+ handle(event: FeishuMessageEvent): Promise<"accepted" | "duplicate" | "ignored">;
36
+ drain(): Promise<void>;
37
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Temporary hard cut: group @All must not start a Beacon run.
3
+ * Prefer Feishu `message.mentions`; fall back to content placeholders.
4
+ */
5
+ export type FeishuMention = {
6
+ key?: string | undefined;
7
+ id?: {
8
+ open_id?: string | undefined;
9
+ user_id?: string | undefined;
10
+ union_id?: string | undefined;
11
+ } | undefined;
12
+ name?: string | undefined;
13
+ mentioned_type?: string | undefined;
14
+ };
15
+ export type FeishuMentionDecisionInput = {
16
+ chatType: string;
17
+ mentions?: FeishuMention[] | undefined;
18
+ messageType?: string | undefined;
19
+ content?: string | undefined;
20
+ };
21
+ /** True when a Feishu mention entry is @All / @所有人 / @_all. */
22
+ export declare function isFeishuAtAllMention(mention: FeishuMention): boolean;
23
+ /** Detect @_all / at-all markers inside raw Feishu message content JSON. */
24
+ export declare function contentIndicatesFeishuAtAll(content: string | undefined): boolean;
25
+ /**
26
+ * A direct bot (or other entity) @mention — anything in `mentions` that is not @All.
27
+ * With mention-only Feishu scopes, non-@All mentions are the bot itself.
28
+ */
29
+ export declare function hasDirectFeishuMention(mentions: readonly FeishuMention[] | undefined): boolean;
30
+ /**
31
+ * Beacon-layer gate for inbound Feishu events.
32
+ * - DM (p2p): always process
33
+ * - Group: process only when a non-@All mention is present
34
+ * - Pure @All (with or without @_all content): ignore
35
+ * - @All + @bot together: process (direct mention wins)
36
+ */
37
+ export declare function shouldProcessFeishuInbound(input: FeishuMentionDecisionInput): boolean;
@@ -0,0 +1,4 @@
1
+ import type { MessageGateway } from "../message/gateway.js";
2
+ import type { FeishuMessageEvent } from "./intake.js";
3
+ import type { FetchedMessage } from "./trigger-input.js";
4
+ export type FeishuMessageGateway = MessageGateway<FeishuMessageEvent, FetchedMessage>;
@@ -0,0 +1,13 @@
1
+ import type { TriggerInput } from "../domain/types.js";
2
+ import type { TriggerStore } from "../state/trigger-store.js";
3
+ import type { FeishuMessageGateway } from "./message-gateway.js";
4
+ export type FeishuMessagePipelineOptions = {
5
+ gateway: FeishuMessageGateway;
6
+ store: TriggerStore;
7
+ process(triggerKey: string, normalize: () => Promise<TriggerInput>): Promise<void>;
8
+ onFatal(error: Error): void;
9
+ };
10
+ export declare function createFeishuMessagePipeline(options: FeishuMessagePipelineOptions): {
11
+ run: (shutdown: Promise<void>) => Promise<void>;
12
+ drain: () => Promise<void>;
13
+ };
@@ -0,0 +1,3 @@
1
+ import type { FeishuCredentials } from "../config/secrets.js";
2
+ export declare function shouldExerciseReplyExperiment(chatType: string, messageId: string, processedMessageIds: ReadonlySet<string>): boolean;
3
+ export declare function runFeishuReplyExperiment(credentials: FeishuCredentials): Promise<void>;
@@ -0,0 +1,36 @@
1
+ export type MessageSnapshot = {
2
+ messageId: string;
3
+ messageType: string;
4
+ senderType: string;
5
+ senderId?: string | undefined;
6
+ content: unknown;
7
+ };
8
+ export type FeishuTriggerInput = {
9
+ source: {
10
+ messageId: string;
11
+ chatId: string;
12
+ chatType: string;
13
+ senderId?: string | undefined;
14
+ };
15
+ message: MessageSnapshot;
16
+ quotedMessages: MessageSnapshot[];
17
+ };
18
+ export type InboundMessage = {
19
+ messageId: string;
20
+ chatId: string;
21
+ chatType: string;
22
+ senderId?: string | undefined;
23
+ senderType: string;
24
+ messageType: string;
25
+ content: string;
26
+ parentMessageId?: string | undefined;
27
+ };
28
+ export type FetchedMessage = {
29
+ messageId: string;
30
+ messageType: string;
31
+ senderType: string;
32
+ senderId?: string | undefined;
33
+ content: string;
34
+ parentMessageId?: string | undefined;
35
+ };
36
+ export declare function buildFeishuTriggerInput(inbound: InboundMessage, fetchMessage: (messageId: string) => Promise<FetchedMessage>): Promise<FeishuTriggerInput>;
@@ -0,0 +1,6 @@
1
+ export { runDoctor } from "./doctor.js";
2
+ export { runManualTrigger } from "./manual-trigger.js";
3
+ export { submitOutcomeFromCli } from "./outcome/submit.js";
4
+ export type { RuntimeOptions } from "./runtime-options.js";
5
+ export { runScheduleTrigger } from "./schedule-trigger.js";
6
+ export { runBeacon } from "./service.js";
@@ -0,0 +1,5 @@
1
+ export { runDoctor } from "./doctor.js";
2
+ export { runManualTrigger } from "./manual-trigger.js";
3
+ export { submitOutcomeFromCli } from "./outcome/submit.js";
4
+ export { runScheduleTrigger } from "./schedule-trigger.js";
5
+ export { runBeacon } from "./service.js";
@@ -0,0 +1,2 @@
1
+ import type { RuntimeOptions } from "./runtime-options.js";
2
+ export declare function runManualTrigger(configPath: string, profileId: string, input: string, options: RuntimeOptions, output?: (text: string) => Promise<void>): Promise<void>;
@@ -13,7 +13,7 @@ function writeStdout(text) {
13
13
  process.stdout.write(`${text}\n`, (error) => error ? reject(error) : resolve());
14
14
  });
15
15
  }
16
- export async function runManualTrigger(configPath, profileId, input, output = writeStdout) {
16
+ export async function runManualTrigger(configPath, profileId, input, options, output = writeStdout) {
17
17
  if (!input.trim())
18
18
  throw new Error("Manual Trigger input must not be empty");
19
19
  const global = await loadGlobalConfig(configPath);
@@ -41,6 +41,7 @@ export async function runManualTrigger(configPath, profileId, input, output = wr
41
41
  },
42
42
  };
43
43
  const orchestrator = createPiRunOrchestrator({
44
+ beaconCliPath: options.beaconCliPath,
44
45
  config: global,
45
46
  runtimeEnvironment,
46
47
  profile,
@@ -0,0 +1,9 @@
1
+ import type { DeliveryAdapter } from "../run/orchestrator.js";
2
+ export type Acknowledgement = {
3
+ clear(): Promise<void>;
4
+ };
5
+ export type MessageGateway<Event, ReferencedMessage> = DeliveryAdapter & {
6
+ run(handleEvent: (event: Event) => Promise<unknown>, shutdown: Promise<void>): Promise<void>;
7
+ acknowledge(messageId: string): Promise<Acknowledgement>;
8
+ fetchMessage(messageId: string): Promise<ReferencedMessage>;
9
+ };
@@ -0,0 +1,68 @@
1
+ import { z } from "zod";
2
+ import type { CardContent, DeliveryContent, FeedbackContent, FinalOutcomeContent, ReplyContent } from "../domain/types.js";
3
+ export declare const feedbackItemSchema: z.ZodObject<{
4
+ priority: z.ZodEnum<{
5
+ high: "high";
6
+ medium: "medium";
7
+ }>;
8
+ summary: z.ZodString;
9
+ }, z.core.$strict>;
10
+ export declare const feedbackContentSchema: z.ZodObject<{
11
+ items: z.ZodArray<z.ZodObject<{
12
+ priority: z.ZodEnum<{
13
+ high: "high";
14
+ medium: "medium";
15
+ }>;
16
+ summary: z.ZodString;
17
+ }, z.core.$strict>>;
18
+ }, z.core.$strict>;
19
+ export declare const feedbackRecordSchema: z.ZodObject<{
20
+ items: z.ZodArray<z.ZodObject<{
21
+ priority: z.ZodEnum<{
22
+ high: "high";
23
+ medium: "medium";
24
+ }>;
25
+ summary: z.ZodString;
26
+ }, z.core.$strict>>;
27
+ submittedAt: z.ZodString;
28
+ }, z.core.$strict>;
29
+ export declare const finalOutcomeContentSchema: z.ZodObject<{
30
+ reply: z.ZodDiscriminatedUnion<[z.ZodObject<{
31
+ kind: z.ZodLiteral<"text">;
32
+ text: z.ZodString;
33
+ }, z.core.$strict>, z.ZodObject<{
34
+ kind: z.ZodLiteral<"no_reply">;
35
+ reason: z.ZodString;
36
+ }, z.core.$strict>, z.ZodObject<{
37
+ kind: z.ZodLiteral<"card">;
38
+ title: z.ZodString;
39
+ content: z.ZodString;
40
+ buttons: z.ZodDefault<z.ZodArray<z.ZodObject<{
41
+ label: z.ZodString;
42
+ url: z.ZodURL;
43
+ }, z.core.$strict>>>;
44
+ }, z.core.$strict>], "kind">;
45
+ notify: z.ZodOptional<z.ZodObject<{
46
+ kind: z.ZodLiteral<"card">;
47
+ title: z.ZodString;
48
+ content: z.ZodString;
49
+ buttons: z.ZodDefault<z.ZodArray<z.ZodObject<{
50
+ label: z.ZodString;
51
+ url: z.ZodURL;
52
+ }, z.core.$strict>>>;
53
+ }, z.core.$strict>>;
54
+ }, z.core.$strict>;
55
+ export type OutcomePatch = {
56
+ reply?: ReplyContent | undefined;
57
+ notify?: CardContent | undefined;
58
+ feedback?: FeedbackContent | undefined;
59
+ };
60
+ export declare function parseFinalOutcomeContent(value: unknown): FinalOutcomeContent;
61
+ export declare function parseOutcomePatch(value: unknown): OutcomePatch;
62
+ export declare function mergeOutcome(current: OutcomePatch | undefined, patch: OutcomePatch): OutcomePatch;
63
+ /** Stable English summary when the Agent settles without closing reply/no_reply. */
64
+ export declare const MISSING_REPLY_OUTCOME_SUMMARY = "Expected exactly one of `reply_text`, `reply_card`, or `no_reply`, but the Agent Runtime settled without submitting any";
65
+ export declare function isMissingReplyOutcomeError(error: unknown): boolean;
66
+ export declare function completeOutcome(partial: OutcomePatch): FinalOutcomeContent;
67
+ export declare function renderFinalOutcomeAsText(outcome: FinalOutcomeContent): string;
68
+ export declare function renderDeliveryAsText(content: DeliveryContent): string;
@@ -0,0 +1,2 @@
1
+ import type { RuntimeOptions } from "../runtime-options.js";
2
+ export declare function runOutcomeDoctor(profileId: string, options: RuntimeOptions): Promise<void>;
@@ -1,4 +1,3 @@
1
- import { fileURLToPath } from "node:url";
2
1
  import { defaultChannelsPath, loadChannelRegistry, } from "../config/channels.js";
3
2
  import { loadProfile } from "../config/profile.js";
4
3
  import { createProfileRunner } from "../run/profile-runner.js";
@@ -13,7 +12,7 @@ const doctorTrigger = {
13
12
  content: { text: "Return exactly OUTCOME_CLI_OK." },
14
13
  },
15
14
  };
16
- export async function runOutcomeDoctor(profileId) {
15
+ export async function runOutcomeDoctor(profileId, options) {
17
16
  const channelsPath = defaultChannelsPath();
18
17
  const channels = await loadChannelRegistry(channelsPath);
19
18
  const profile = await loadProfile(profileId, undefined, {
@@ -21,7 +20,7 @@ export async function runOutcomeDoctor(profileId) {
21
20
  channelsPath,
22
21
  });
23
22
  const outcomes = await startOutcomeServer();
24
- const beaconCliPath = fileURLToPath(new URL("../../dist/cli.js", import.meta.url));
23
+ const beaconCliPath = options.beaconCliPath;
25
24
  try {
26
25
  const result = await createProfileRunner(profile, outcomes, beaconCliPath)(doctorTrigger);
27
26
  if (result.reply.kind !== "text" ||
@@ -0,0 +1,3 @@
1
+ export declare const inboundOutOfRoleReply = "\u8FD9\u6761\u6D88\u606F\u4E0D\u5728\u6211\u7684\u804C\u8D23\u8303\u56F4\u5185\u3002";
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."];
@@ -0,0 +1,20 @@
1
+ import type { FeedbackRecord, FinalOutcomeContent } from "../domain/types.js";
2
+ export type OutcomeBinding = {
3
+ socketPath: string;
4
+ runToken: string;
5
+ };
6
+ export type OutcomeSubmission = {
7
+ binding: OutcomeBinding;
8
+ take(): FinalOutcomeContent;
9
+ takeFeedback(): FeedbackRecord | undefined;
10
+ cancel(): void;
11
+ };
12
+ export type OutcomeSink = {
13
+ openRun(): OutcomeSubmission;
14
+ };
15
+ export type OutcomeServer = OutcomeSink & {
16
+ close(): Promise<void>;
17
+ };
18
+ export declare function startOutcomeServer(options?: {
19
+ maxRequestBytes?: number | undefined;
20
+ }): Promise<OutcomeServer>;
@@ -0,0 +1,5 @@
1
+ import type { FinalOutcomeContent } from "../domain/types.js";
2
+ import { completeOutcome, mergeOutcome, type OutcomePatch } from "./content.js";
3
+ export declare function submitOutcome(socketPath: string, runToken: string, outcome: OutcomePatch | FinalOutcomeContent): Promise<void>;
4
+ export declare function submitOutcomeFromCli(): Promise<void>;
5
+ export { completeOutcome, mergeOutcome };
@@ -0,0 +1,17 @@
1
+ import type { GlobalConfig } from "../config/global.js";
2
+ import type { Profile } from "../config/profile.js";
3
+ import type { OutcomeServer } from "../outcome/server.js";
4
+ import type { TriggerStore } from "../state/trigger-store.js";
5
+ import { type DeliveryAdapter, RunOrchestrator } from "./orchestrator.js";
6
+ import type { RunQueue } from "./queue.js";
7
+ export type PiOrchestratorConfig = Pick<GlobalConfig, "pi" | "runs">;
8
+ export declare function createPiRunOrchestrator(options: {
9
+ beaconCliPath: string;
10
+ config: PiOrchestratorConfig;
11
+ runtimeEnvironment: NodeJS.ProcessEnv;
12
+ profile: Profile;
13
+ store: TriggerStore;
14
+ queue: RunQueue;
15
+ outcomes: OutcomeServer;
16
+ delivery: DeliveryAdapter;
17
+ }): RunOrchestrator;
@@ -1,4 +1,3 @@
1
- import { fileURLToPath } from "node:url";
2
1
  import { runPiAgent } from "../runtime/pi-rpc.js";
3
2
  import { RunOrchestrator } from "./orchestrator.js";
4
3
  export function createPiRunOrchestrator(options) {
@@ -7,7 +6,7 @@ export function createPiRunOrchestrator(options) {
7
6
  store: options.store,
8
7
  queue: options.queue,
9
8
  outcomes: options.outcomes,
10
- beaconCliPath: fileURLToPath(new URL("../../dist/cli.js", import.meta.url)),
9
+ beaconCliPath: options.beaconCliPath,
11
10
  sessionDirectory: options.config.pi.sessionDirectory,
12
11
  runAgent: (request) => runPiAgent(request, {
13
12
  executable: options.config.pi.executable,
@@ -0,0 +1,61 @@
1
+ import type { Profile } from "../config/profile.js";
2
+ import type { DeliveryContent, DeliveryTarget, FailureCode, MessageSnapshot, TriggerInput, TriggerRecord } from "../domain/types.js";
3
+ import type { OutcomeServer } from "../outcome/server.js";
4
+ import type { TriggerStore } from "../state/trigger-store.js";
5
+ import type { AgentRuntimeRunner } from "./profile-runner.js";
6
+ import type { RunQueue } from "./queue.js";
7
+ export type DeliveryAdapter = {
8
+ deliver(target: DeliveryTarget, outcome: DeliveryContent, deliveryId: string): Promise<{
9
+ providerRequestId?: string | undefined;
10
+ }>;
11
+ };
12
+ export type RunOrchestratorOptions = {
13
+ profile: Profile;
14
+ store: TriggerStore;
15
+ queue: RunQueue;
16
+ outcomes: OutcomeServer;
17
+ beaconCliPath: string;
18
+ runAgent: AgentRuntimeRunner;
19
+ delivery: DeliveryAdapter;
20
+ sessionDirectory: string;
21
+ now?: (() => Date) | undefined;
22
+ id?: (() => string) | undefined;
23
+ };
24
+ /** Compact settle-side hint for outcome_missing (Feishu / admin). */
25
+ export declare function formatSettleFailureHint(parts: {
26
+ stopReason?: string | undefined;
27
+ thinking?: string | undefined;
28
+ text?: string | undefined;
29
+ }): string | undefined;
30
+ /** User-visible (Feishu/admin) failure text. Keep codes stable; prefer concrete reasons. */
31
+ export declare function formatFailureReplyText(runId: string, failure?: {
32
+ code: FailureCode;
33
+ summary: string;
34
+ }): string;
35
+ export type ContinuedPiSession = {
36
+ sessionId: string;
37
+ sessionPath: string;
38
+ systemPrompt?: string | undefined;
39
+ };
40
+ /**
41
+ * Walk a Feishu quote chain (oldest → newest) from the direct parent backward
42
+ * and reuse the first Beacon Delivery's Pi session.
43
+ */
44
+ export declare function resolveContinuedPiSession(quotedMessages: readonly MessageSnapshot[], findByDeliveredMessageId: (messageId: string) => Promise<TriggerRecord | undefined>): Promise<ContinuedPiSession | undefined>;
45
+ export declare class RunOrchestrator {
46
+ private readonly options;
47
+ private readonly now;
48
+ private readonly id;
49
+ constructor(options: RunOrchestratorOptions);
50
+ private timestamp;
51
+ private record;
52
+ private agentSystemPrompt;
53
+ private triggerFor;
54
+ private newRun;
55
+ private deliverField;
56
+ private deliverOutcome;
57
+ private fail;
58
+ private execute;
59
+ process(triggerKey: string, normalize: () => Promise<TriggerInput>): Promise<void>;
60
+ recover(): Promise<void>;
61
+ }
@@ -0,0 +1,9 @@
1
+ import type { Profile } from "../config/profile.js";
2
+ import type { FinalOutcomeContent } from "../domain/types.js";
3
+ import type { FeishuTriggerInput } from "../feishu/trigger-input.js";
4
+ import type { OutcomeSink } from "../outcome/server.js";
5
+ import { type PiRunRequest, type PiRunResult } from "../runtime/pi-rpc.js";
6
+ export type RunProfile = (trigger: FeishuTriggerInput) => Promise<FinalOutcomeContent>;
7
+ export type AgentRuntimeRunner = (request: PiRunRequest) => Promise<PiRunResult>;
8
+ export declare function formatFeishuTriggerPrompt(trigger: FeishuTriggerInput): string;
9
+ export declare function createProfileRunner(profile: Profile, outcomes: OutcomeSink, beaconCliPath: string, runAgent?: AgentRuntimeRunner): RunProfile;
@@ -0,0 +1,20 @@
1
+ export type QueueSubmission = {
2
+ accepted: false;
3
+ } | {
4
+ accepted: true;
5
+ completion: Promise<void>;
6
+ };
7
+ export declare class RunQueue {
8
+ readonly maxConcurrent: number;
9
+ readonly maxQueued: number;
10
+ private active;
11
+ private readonly pending;
12
+ private readonly waiting;
13
+ constructor(maxConcurrent: number, maxQueued: number);
14
+ enqueue(task: () => Promise<void>): QueueSubmission;
15
+ /** Wait for admission without dropping already accepted, persisted event work. */
16
+ enqueueWhenAvailable(task: () => Promise<void>): Promise<void>;
17
+ private promoteWaiting;
18
+ private execute;
19
+ private drain;
20
+ }
@@ -0,0 +1 @@
1
+ export declare function runPiDoctor(provider?: string, model?: string): Promise<void>;
@@ -0,0 +1,43 @@
1
+ import type { CardContent, FeedbackContent, FinalOutcomeContent } from "../domain/types.js";
2
+ type ToolResult = {
3
+ content: Array<{
4
+ type: "text";
5
+ text: string;
6
+ }>;
7
+ details: {
8
+ submitted: true;
9
+ };
10
+ };
11
+ export type TextOutcomeToolParams = {
12
+ text: string;
13
+ };
14
+ export type NoReplyOutcomeToolParams = {
15
+ reason: string;
16
+ };
17
+ export type CardOutcomeToolParams = {
18
+ title: string;
19
+ content: string;
20
+ buttons?: Array<{
21
+ label: string;
22
+ url: string;
23
+ }>;
24
+ };
25
+ export type FeedbackToolParams = {
26
+ items: FeedbackContent["items"];
27
+ };
28
+ type ExtensionApi = {
29
+ registerTool<TParams>(tool: {
30
+ name: string;
31
+ label: string;
32
+ description: string;
33
+ parameters: object;
34
+ execute(toolCallId: string, params: TParams, signal: AbortSignal): Promise<ToolResult>;
35
+ }): void;
36
+ };
37
+ export declare function replyTextFromToolParams(params: TextOutcomeToolParams): FinalOutcomeContent;
38
+ export declare function replyCardFromToolParams(params: CardOutcomeToolParams): FinalOutcomeContent;
39
+ export declare function noReplyFromToolParams(params: NoReplyOutcomeToolParams): FinalOutcomeContent;
40
+ export declare function notifyCardFromToolParams(params: CardOutcomeToolParams): CardContent;
41
+ export declare function feedbackFromToolParams(params: FeedbackToolParams): FeedbackContent;
42
+ export default function registerOutcomeTools(pi: ExtensionApi): void;
43
+ export {};
@@ -0,0 +1,42 @@
1
+ import type { OutcomeBinding } from "../outcome/server.js";
2
+ type PiOutcomeBinding = OutcomeBinding & {
3
+ cliPath: string;
4
+ };
5
+ export type PiRunRequest = {
6
+ prompt: string;
7
+ workspace: string;
8
+ provider?: string | undefined;
9
+ model?: string | undefined;
10
+ systemPrompt?: string | undefined;
11
+ outcome?: PiOutcomeBinding | undefined;
12
+ session?: {
13
+ id: string;
14
+ path: string;
15
+ name?: string | undefined;
16
+ } | undefined;
17
+ };
18
+ export type PiRunResult = {
19
+ text: string;
20
+ provider: string;
21
+ model: string;
22
+ /** Final assistant stopReason when Pi settles cleanly (usually `stop`). */
23
+ stopReason?: string | undefined;
24
+ /** Truncation-ready last thinking text from the final assistant message. */
25
+ thinking?: string | undefined;
26
+ };
27
+ export type PiRuntimeOptions = {
28
+ executable?: string | undefined;
29
+ timeoutMs?: number | undefined;
30
+ environment?: NodeJS.ProcessEnv | undefined;
31
+ runtimeEnvironment?: NodeJS.ProcessEnv | undefined;
32
+ terminateGraceMs?: number | undefined;
33
+ maxFrameBytes?: number | undefined;
34
+ };
35
+ export declare class PiRuntimeError extends Error {
36
+ readonly code: "runtime_spawn_failed" | "runtime_protocol_error" | "runtime_timeout" | "runtime_exit_failed";
37
+ constructor(code: "runtime_spawn_failed" | "runtime_protocol_error" | "runtime_timeout" | "runtime_exit_failed", message: string, options?: ErrorOptions);
38
+ }
39
+ /** Collect thinking / reasoning blocks from an assistant message content array. */
40
+ export declare function collectThinking(content: unknown[]): string;
41
+ export declare function runPiAgent(request: PiRunRequest, options?: PiRuntimeOptions): Promise<PiRunResult>;
42
+ export {};
@@ -83,7 +83,9 @@ function buildArguments(request) {
83
83
  if (request.systemPrompt)
84
84
  args.push("--system-prompt", request.systemPrompt);
85
85
  if (request.outcome) {
86
- args.push("--extension", fileURLToPath(new URL("../../dist/runtime/pi-outcome-extension.js", import.meta.url)));
86
+ args.push("--extension", fileURLToPath(new URL(import.meta.url.endsWith(".ts")
87
+ ? "../../dist/runtime/pi-outcome-extension.js"
88
+ : "./pi-outcome-extension.js", import.meta.url)));
87
89
  }
88
90
  return args;
89
91
  }
@@ -0,0 +1,10 @@
1
+ import type { Profile } from "../config/profile.js";
2
+ import type { DeliveryTarget, TriggerInput } from "../domain/types.js";
3
+ export type AgentSystemPromptTrigger = {
4
+ kind: TriggerInput["kind"];
5
+ scheduleId?: string | undefined;
6
+ notify: boolean;
7
+ };
8
+ export declare function agentSystemPromptTrigger(input: TriggerInput | undefined, notifyTarget: DeliveryTarget | undefined): AgentSystemPromptTrigger;
9
+ export declare function profilePromptDigest(profile: Pick<Profile, "persona" | "task">): string;
10
+ export declare function buildAgentSystemPrompt(profile: Pick<Profile, "persona" | "task" | "workspace">, trigger: AgentSystemPromptTrigger): string;
@@ -0,0 +1,4 @@
1
+ export type RuntimeOptions = {
2
+ /** Executable JavaScript CLI entry used by agent outcome submissions. */
3
+ beaconCliPath: string;
4
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export declare function occurrencesBetween(cron: string, timezone: string, afterExclusive: Date, throughInclusive: Date, limit: number): Date[];
2
+ export declare function nextOccurrence(cron: string, timezone: string, afterExclusive: Date): Date;