@interactive-inc/claude-funnel 0.65.0 → 0.66.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 (38) hide show
  1. package/dist/bin.js +127 -127
  2. package/dist/broadcaster-DVNcw00K.d.ts +138 -0
  3. package/dist/channel.d.ts +161 -0
  4. package/dist/channel.js +203 -0
  5. package/dist/{channels-CRGb6B5_.d.ts → channels-DR90RmUP.d.ts} +5 -4
  6. package/dist/claude.d.ts +4 -4
  7. package/dist/{connector-descriptor-BFIhyTfa.d.ts → connector-descriptor-CmyHlEiT.d.ts} +3 -26
  8. package/dist/connectors/discord.d.ts +4 -3
  9. package/dist/connectors/gh.d.ts +4 -3
  10. package/dist/connectors/schedule.d.ts +4 -3
  11. package/dist/connectors/slack.d.ts +4 -3
  12. package/dist/diagnostics.d.ts +1 -1
  13. package/dist/docs.d.ts +1 -1
  14. package/dist/doctor.d.ts +1 -1
  15. package/dist/{file-process-guard-tVcgckH6.d.ts → file-process-guard-CiBwXjzm.d.ts} +5 -4
  16. package/dist/{flume-source-listener-BNyAII7N.d.ts → flume-source-listener-FzR5lGpL.d.ts} +2 -1
  17. package/dist/{funnel-diagnostics-b9ar0Ing.d.ts → funnel-diagnostics-kbd27d7I.d.ts} +1 -1
  18. package/dist/{funnel-doctor-CnRQi4kM.d.ts → funnel-doctor-CVTNUtDn.d.ts} +2 -2
  19. package/dist/{funnel-recovery-CMhY8Jfk.d.ts → funnel-recovery-D8MkiTpk.d.ts} +1 -1
  20. package/dist/gateway/daemon.js +16 -16
  21. package/dist/gateway.d.ts +3 -2
  22. package/dist/{index-Ds6sHhA-.d.ts → index-BU1TtuFy.d.ts} +6 -139
  23. package/dist/index.d.ts +19 -17
  24. package/dist/local-config.d.ts +2 -2
  25. package/dist/logger-Bc_Ooypb.d.ts +25 -0
  26. package/dist/{memory-token-prompter-BoV8Hf-n.d.ts → memory-token-prompter-D0kIdhar.d.ts} +2 -2
  27. package/dist/{profiles-cVZQkM69.d.ts → profiles-M24JrcOh.d.ts} +3 -3
  28. package/dist/profiles.d.ts +1 -1
  29. package/dist/recovery.d.ts +1 -1
  30. package/dist/{settings-reader-BNxjsxCB.d.ts → settings-reader-BnKIbmsD.d.ts} +1 -1
  31. package/package.json +7 -2
  32. /package/dist/{discord-connector-schema-D-bOVAKt.d.ts → discord-connector-schema-D1dyxhXk.d.ts} +0 -0
  33. /package/dist/{file-system-VhwwXZbm.d.ts → file-system-DHgbuTFb.d.ts} +0 -0
  34. /package/dist/{funnel-docs-CNklHvbt.d.ts → funnel-docs-Cd66sKyV.d.ts} +0 -0
  35. /package/dist/{gh-connector-schema-DWQaB6gX.d.ts → gh-connector-schema-BCHEqpUg.d.ts} +0 -0
  36. /package/dist/{schedule-connector-schema-Z0RXLgPI.d.ts → schedule-connector-schema-D7ZEKvit.d.ts} +0 -0
  37. /package/dist/{settings-schema-BL_c2Udm.d.ts → settings-schema-IShSMm5G.d.ts} +0 -0
  38. /package/dist/{slack-event-processor-BhCf5Wiy.d.ts → slack-event-processor-BDTr62cN.d.ts} +0 -0
@@ -0,0 +1,138 @@
1
+ import { t as FunnelLogger } from "./logger-Bc_Ooypb.js";
2
+ import { ServerWebSocket } from "bun";
3
+
4
+ //#region lib/engine/error/on-funnel-error.d.ts
5
+ /**
6
+ * Host integration hook called when Funnel catches an exception that would
7
+ * otherwise be silently swallowed (subscriber throw, listener start failure,
8
+ * MCP forward failure, etc.). Pass `Sentry.captureException` from the host to
9
+ * pipe these into your error reporter. Defaults to a no-op when omitted.
10
+ *
11
+ * `context` carries the component name and any extra metadata the caller had
12
+ * at the catch site (channel / connector / subscriber id when available).
13
+ */
14
+ type OnFunnelError = (error: Error, context?: Record<string, unknown>) => void;
15
+ //#endregion
16
+ //#region lib/gateway/broadcaster.d.ts
17
+ type ClientData = {
18
+ /** Stable channel id (uuid) that the WS client subscribed to. */channel: string; /** Human-facing channel name resolved at upgrade time, kept for log readability. */
19
+ channelName?: string | null; /** Connector names belonging to that channel. */
20
+ connectors: string[]; /** Routing mode resolved from channel config at upgrade time. Defaults to fanout. */
21
+ delivery?: "fanout" | "exclusive";
22
+ /**
23
+ * Opaque per-client id declared at upgrade time (`?id=<subscriberId>`). When an
24
+ * event carries `meta.target`, only the client whose `subscriberId` equals it
25
+ * receives the event among that channel's regular subscribers. Targeted delivery
26
+ * is how a publisher addresses one specific instance (e.g. a single agent
27
+ * session) without every subscriber having to receive and discard it.
28
+ */
29
+ subscriberId?: string;
30
+ };
31
+ type BroadcastEvent = {
32
+ content: string;
33
+ meta?: Record<string, string>;
34
+ };
35
+ type ReplayableEvent = BroadcastEvent & {
36
+ offset: number;
37
+ };
38
+ type BroadcastSubscriber = (event: ReplayableEvent) => void;
39
+ /**
40
+ * Optional persistent replay source. Wired in by the gateway-server with a
41
+ * `FunnelEventLog` (SQLite-backed by default) so reconnects across daemon
42
+ * restarts can recover events older than the in-memory buffer via an indexed
43
+ * `seq > since` range scan.
44
+ */
45
+ type ReplaySource = {
46
+ loadSince(since: number): ReplayableEvent[];
47
+ };
48
+ type Deps = {
49
+ logger?: FunnelLogger; /** Host hook for surfacing subscriber-throw exceptions. Defaults to no-op. */
50
+ onError?: OnFunnelError;
51
+ maxBufferedBytes?: number;
52
+ now?: () => number; /** Number of recent events kept in the in-memory replay buffer. */
53
+ replayBufferSize?: number; /** Hard byte cap on replay buffer payloads. Older events are evicted FIFO until under this cap. */
54
+ replayBufferMaxBytes?: number; /** Persistent replay source consulted when the in-memory buffer cannot satisfy `since`. */
55
+ persistentReplay?: ReplaySource;
56
+ };
57
+ type BroadcasterMetrics = {
58
+ clients: number;
59
+ subscribers: number;
60
+ eventsBroadcast: number;
61
+ droppedSlowClients: number;
62
+ lastBroadcastAt: string | null; /** Latest emitted offset. Clients can `?since=<offset>` to ask for events strictly after this point. */
63
+ latestOffset: number; /** Oldest offset still held in the replay buffer. Older values cannot be replayed and trigger a full resync. */
64
+ oldestReplayableOffset: number | null;
65
+ };
66
+ /**
67
+ * In-process pub/sub for connector events.
68
+ *
69
+ * Two outbound paths:
70
+ * - WS clients connected via the gateway's `/ws` endpoint, scoped per channel
71
+ * - In-process subscribers registered via `subscribe()` (programmable API)
72
+ *
73
+ * Backpressure: if a WS client's `bufferedAmount` exceeds `maxBufferedBytes`
74
+ * (default 1 MiB), the client is closed with code 1009 and dropped from the
75
+ * registry to keep one slow consumer from blocking the daemon.
76
+ *
77
+ * Replay: every emitted event gets a strictly increasing `offset`. The latest
78
+ * `replayBufferSize` events are kept in memory; reconnecting WS clients can
79
+ * pass `?since=<offset>` and the broadcaster resends matching events before
80
+ * resuming the live stream. The in-memory ring covers short reconnects;
81
+ * older history is served from the event log wired in as `persistentReplay`.
82
+ */
83
+ declare class FunnelBroadcaster {
84
+ private readonly clients;
85
+ private readonly subscribers;
86
+ private readonly logger;
87
+ private readonly onError;
88
+ private readonly maxBufferedBytes;
89
+ private readonly now;
90
+ private readonly replayBufferSize;
91
+ private readonly replayBufferMaxBytes;
92
+ private readonly replayBuffer;
93
+ private readonly persistentReplay;
94
+ private readonly exclusiveCursor;
95
+ private replayBufferBytes;
96
+ private eventsBroadcast;
97
+ private droppedSlowClients;
98
+ private lastBroadcastAt;
99
+ private latestOffset;
100
+ constructor(deps?: Deps);
101
+ getMetrics(): BroadcasterMetrics;
102
+ /**
103
+ * Returns events with offset > since, filtered by the connector subscription
104
+ * rules of `data`. Used at WS upgrade time when the client passes `?since=<offset>`.
105
+ *
106
+ * Two-tier lookup:
107
+ * 1. The in-memory ring buffer (covers short reconnects, last `replayBufferSize` events).
108
+ * 2. If `since` predates the oldest in-memory entry and a persistent replay source
109
+ * is wired in (SQLite by default), the gap is filled from it. This covers reconnects
110
+ * across daemon restarts where the in-memory buffer was lost.
111
+ *
112
+ * Result is sorted ascending by offset and de-duplicated against the in-memory buffer.
113
+ */
114
+ replaySince(since: number, data: ClientData): ReplayableEvent[];
115
+ private matchesClient;
116
+ /**
117
+ * Returns the list of WS clients that should receive `event`. For each per-channel group:
118
+ * - fanout → every matching client receives
119
+ * - exclusive → exactly one client receives, picked round-robin per channel
120
+ *
121
+ * `meta.target` narrows the recipient set via `matchesClient`: only the subscriber
122
+ * whose `subscriberId` equals `target` receives a targeted event.
123
+ */
124
+ private pickRecipients;
125
+ addClient(ws: ServerWebSocket<unknown>, data: ClientData): void;
126
+ removeClient(ws: ServerWebSocket<unknown>): void;
127
+ getClientCount(): number;
128
+ listChannels(): {
129
+ channel: string;
130
+ connectors: string[];
131
+ }[];
132
+ subscribe(handler: BroadcastSubscriber): () => void;
133
+ broadcast(content: string, meta?: Record<string, string>): ReplayableEvent;
134
+ /** Forward-seed the offset counter (used at startup from the persisted event store). */
135
+ seedLatestOffset(offset: number): void;
136
+ }
137
+ //#endregion
138
+ export { OnFunnelError as a, ReplayableEvent as i, BroadcastSubscriber as n, FunnelBroadcaster as r, BroadcastEvent as t };
@@ -0,0 +1,161 @@
1
+ import { n as FunnelClock, t as FunnelLogger } from "./logger-Bc_Ooypb.js";
2
+ import { n as FunnelFileSystem } from "./file-system-DHgbuTFb.js";
3
+ import { r as FunnelBroadcaster } from "./broadcaster-DVNcw00K.js";
4
+ import { FlumeCatchupPolicy, FlumeErrorHandler, FlumeEvent, FlumeRuntimeDeps, FlumeSource, FlumeStatePersister } from "@interactive-inc/flume";
5
+
6
+ //#region lib/engine/channel/channel.d.ts
7
+ /**
8
+ * 1 tick / 1 event を `broadcaster.broadcast(content, meta)` に流すための整形済みペイロード。
9
+ * `null` を返すと「drop」(broadcast しない) を意味する
10
+ */
11
+ type ChannelBroadcastPayload = {
12
+ readonly content: string;
13
+ readonly meta?: Record<string, string>;
14
+ };
15
+ type ChannelTransform = (event: FlumeEvent) => ChannelBroadcastPayload | null;
16
+ /**
17
+ * Channel が `build(ctx)` で返す実体。flume sources の集合と任意の transform を 1 まとまりとして
18
+ * supervisor に渡す
19
+ */
20
+ type ChannelRuntime = {
21
+ readonly sources: ReadonlyArray<FlumeSource>;
22
+ /**
23
+ * 各 source の FlumeEvent を broadcast payload に変換する関数。
24
+ * 省略時は `{ content: JSON.stringify(event.data), meta: event.meta }` でフォールバック
25
+ */
26
+ readonly transform?: ChannelTransform;
27
+ };
28
+ /**
29
+ * Channel.build に渡される環境。FunnelClock / FunnelLogger / FunnelFileSystem を経由して
30
+ * IO 境界を抽象化する。statePersister は funnel-fs に紐づいた `FlumeStatePersister<S>` を
31
+ * channel id-scoped なファイル名で生成するヘルパー
32
+ */
33
+ type ChannelBuildContext = {
34
+ readonly channelId: string;
35
+ readonly channelName: string;
36
+ readonly signal: AbortSignal;
37
+ readonly logger: FunnelLogger;
38
+ readonly clock: FunnelClock;
39
+ readonly fs: FunnelFileSystem;
40
+ /**
41
+ * `<funnelDir>/channels/<channelId>/<filename>.json` に Read/Write する純粋 DI 用 helper。
42
+ * flume が要求する `FlumeStatePersister<S>` 形に合わせる
43
+ */
44
+ readonly statePersister: <S>(filename: string) => FlumeStatePersister<S>;
45
+ };
46
+ /**
47
+ * 1 channel = 1 inbound 受信単位。`build` が confluence に挿す sources を返す。
48
+ * `build` は host abort / token rotation 等で何度でも再呼び出しされうる「純粋なファクトリ」
49
+ */
50
+ type Channel = {
51
+ readonly id: string;
52
+ readonly name?: string;
53
+ readonly build: (ctx: ChannelBuildContext) => ChannelRuntime | Promise<ChannelRuntime>;
54
+ };
55
+ /** `Channel` を frozen で返す identity helper。consumer の型推論を整える */
56
+ declare function defineChannel(channel: Channel): Channel;
57
+ //#endregion
58
+ //#region lib/engine/channel/channel-supervisor.d.ts
59
+ type Props$1 = {
60
+ readonly broadcaster: FunnelBroadcaster;
61
+ readonly logger: FunnelLogger;
62
+ readonly clock: FunnelClock;
63
+ readonly fs: FunnelFileSystem;
64
+ /**
65
+ * Channel ごとの state / 永続化ファイルの起点。
66
+ * `<root>/channels/<channelId>/` 配下に書き込む
67
+ */
68
+ readonly dir: string;
69
+ readonly deps?: FlumeRuntimeDeps;
70
+ readonly onError?: FlumeErrorHandler; /** Host abort. fired すると全 channel を close する */
71
+ readonly signal?: AbortSignal;
72
+ };
73
+ /**
74
+ * Channel-manifest を `FlumeConfluence` 1 つにマッピングする supervisor。
75
+ *
76
+ * - `register(channel)` で受け入れた channel は `start()` 時に `build(ctx)` され
77
+ * sources が confluence に `add(channelId, sources)` で挿される
78
+ * - confluence の onItem (groupId 付き) を 1 本受けて、対応する channel の `transform`
79
+ * を通してから `broadcaster.broadcast(content, meta)` に流す
80
+ * - `unregister(id)` で個別停止、`stop()` で全停止
81
+ *
82
+ * 既存の `FunnelListenerRegistry` (ConnectorDescriptor 系) とは独立。
83
+ * 並走させて段階的に移行する設計
84
+ */
85
+ declare class FunnelChannelSupervisor {
86
+ private readonly props;
87
+ private readonly confluence;
88
+ private readonly registered;
89
+ private readonly pending;
90
+ private started;
91
+ constructor(props: Props$1);
92
+ /** start 前 / 後どちらでも受け付ける。start 後は即座に build + add する */
93
+ register(channel: Channel): void;
94
+ unregister(id: string): Promise<void>;
95
+ start(): Promise<void>;
96
+ stop(): Promise<void>;
97
+ ids(): ReadonlyArray<string>;
98
+ has(id: string): boolean;
99
+ private openChannel;
100
+ private handleItem;
101
+ private transformToPayload;
102
+ }
103
+ //#endregion
104
+ //#region lib/engine/channel/time-channel.d.ts
105
+ type Options = {
106
+ readonly id: string;
107
+ readonly name?: string;
108
+ readonly cron: string; /** 既定: `{ mode: "lastOnly" }` (起動時に直近 1 件だけ取り戻す) */
109
+ readonly catchupPolicy?: FlumeCatchupPolicy;
110
+ /**
111
+ * 各 tick を broadcast payload に変換する関数。省略時は
112
+ * `{ content: "tick", meta: { cron, firedAt } }` 相当
113
+ */
114
+ readonly transform?: ChannelTransform;
115
+ /**
116
+ * `statePersister` を有効にするかどうか (既定 true)。`false` にすると catchup も無効
117
+ */
118
+ readonly persist?: boolean;
119
+ };
120
+ /**
121
+ * 1 cron entry の time channel を作る factory。
122
+ *
123
+ * 使用例 (inta jiho):
124
+ * ```ts
125
+ * const jiho = timeChannel({
126
+ * id: "inta-jiho",
127
+ * cron: "0 * * * *",
128
+ * transform: (event) => ({
129
+ * content: `⏰ ${new Date(event.data.firedAt as number).getHours()}時をお知らせします。`,
130
+ * meta: { event_type: "schedule", source: "inta-jiho", target_role: "primary" },
131
+ * }),
132
+ * })
133
+ * ```
134
+ *
135
+ * `persist: true` (既定) の場合、`<channelDir>/time.json` に `lastFiredAt` を残し、
136
+ * 起動時に `catchupPolicy` (既定 lastOnly) に従って過去 tick を取り戻す
137
+ */
138
+ declare function timeChannel(options: Options): Channel;
139
+ //#endregion
140
+ //#region lib/engine/channel/file-state-persister.d.ts
141
+ type Props = {
142
+ readonly fs: FunnelFileSystem;
143
+ readonly path: string;
144
+ };
145
+ /**
146
+ * `FunnelFileSystem` 上に JSON で 1 行 / 1 状態を保存する `FlumeStatePersister`。
147
+ * 保存先ディレクトリは load/save の前に再帰作成する。
148
+ * load は不在 / parse 失敗時に `null` を返す (flume 側で素直に「初回扱い」になる)
149
+ */
150
+ declare function createFileStatePersister<S>(props: Props): FlumeStatePersister<S>;
151
+ type DirProps = {
152
+ readonly fs: FunnelFileSystem; /** `<funnelDir>/channels/<channelId>` の絶対パス */
153
+ readonly channelDir: string;
154
+ };
155
+ /**
156
+ * channel 単位の state persister ファクトリ。`statePersister<S>("time")` を呼ぶと
157
+ * `<channelDir>/time.json` 用の persister を返す
158
+ */
159
+ declare function createChannelStatePersisterFactory(props: DirProps): <S>(filename: string) => FlumeStatePersister<S>;
160
+ //#endregion
161
+ export { type Channel, type ChannelBroadcastPayload, type ChannelBuildContext, type ChannelRuntime, type ChannelTransform, FunnelChannelSupervisor, createChannelStatePersisterFactory, createFileStatePersister, defineChannel, timeChannel };
@@ -0,0 +1,203 @@
1
+ import { dirname, join } from "node:path";
2
+ import { FlumeConfluence, FlumeTimeSource } from "@interactive-inc/flume";
3
+ //#region lib/engine/channel/channel.ts
4
+ /** `Channel` を frozen で返す identity helper。consumer の型推論を整える */
5
+ function defineChannel(channel) {
6
+ return Object.freeze({ ...channel });
7
+ }
8
+ //#endregion
9
+ //#region lib/engine/channel/file-state-persister.ts
10
+ /**
11
+ * `FunnelFileSystem` 上に JSON で 1 行 / 1 状態を保存する `FlumeStatePersister`。
12
+ * 保存先ディレクトリは load/save の前に再帰作成する。
13
+ * load は不在 / parse 失敗時に `null` を返す (flume 側で素直に「初回扱い」になる)
14
+ */
15
+ function createFileStatePersister(props) {
16
+ return {
17
+ async load() {
18
+ if (!props.fs.existsSync(props.path)) return null;
19
+ const raw = props.fs.readFileSync(props.path);
20
+ if (raw === "") return null;
21
+ try {
22
+ return JSON.parse(raw);
23
+ } catch {
24
+ return null;
25
+ }
26
+ },
27
+ async save(state) {
28
+ const dir = dirname(props.path);
29
+ if (!props.fs.existsSync(dir)) props.fs.mkdirSync(dir, { recursive: true });
30
+ props.fs.writeFileSync(props.path, JSON.stringify(state));
31
+ }
32
+ };
33
+ }
34
+ /**
35
+ * channel 単位の state persister ファクトリ。`statePersister<S>("time")` を呼ぶと
36
+ * `<channelDir>/time.json` 用の persister を返す
37
+ */
38
+ function createChannelStatePersisterFactory(props) {
39
+ return (filename) => createFileStatePersister({
40
+ fs: props.fs,
41
+ path: join(props.channelDir, `${filename}.json`)
42
+ });
43
+ }
44
+ //#endregion
45
+ //#region lib/engine/channel/channel-supervisor.ts
46
+ /**
47
+ * Channel-manifest を `FlumeConfluence` 1 つにマッピングする supervisor。
48
+ *
49
+ * - `register(channel)` で受け入れた channel は `start()` 時に `build(ctx)` され
50
+ * sources が confluence に `add(channelId, sources)` で挿される
51
+ * - confluence の onItem (groupId 付き) を 1 本受けて、対応する channel の `transform`
52
+ * を通してから `broadcaster.broadcast(content, meta)` に流す
53
+ * - `unregister(id)` で個別停止、`stop()` で全停止
54
+ *
55
+ * 既存の `FunnelListenerRegistry` (ConnectorDescriptor 系) とは独立。
56
+ * 並走させて段階的に移行する設計
57
+ */
58
+ var FunnelChannelSupervisor = class {
59
+ confluence;
60
+ registered = /* @__PURE__ */ new Map();
61
+ pending = /* @__PURE__ */ new Map();
62
+ started = false;
63
+ constructor(props) {
64
+ this.props = props;
65
+ this.confluence = new FlumeConfluence({
66
+ onEvent: (item) => this.handleItem(item),
67
+ onError: props.onError,
68
+ deps: props.deps
69
+ });
70
+ if (props.signal) {
71
+ const onAbort = () => {
72
+ this.stop().catch(() => {});
73
+ };
74
+ if (props.signal.aborted) this.started = true;
75
+ else props.signal.addEventListener("abort", onAbort, { once: true });
76
+ }
77
+ }
78
+ /** start 前 / 後どちらでも受け付ける。start 後は即座に build + add する */
79
+ register(channel) {
80
+ if (this.registered.has(channel.id) || this.pending.has(channel.id)) throw new Error(`FunnelChannelSupervisor: channel id already registered: ${channel.id}`);
81
+ if (!this.started) {
82
+ this.pending.set(channel.id, channel);
83
+ return;
84
+ }
85
+ this.openChannel(channel);
86
+ }
87
+ async unregister(id) {
88
+ this.pending.delete(id);
89
+ const entry = this.registered.get(id);
90
+ if (!entry) return;
91
+ this.registered.delete(id);
92
+ entry.abortController.abort();
93
+ await this.confluence.remove(id);
94
+ }
95
+ async start() {
96
+ if (this.started) return;
97
+ this.started = true;
98
+ const channels = [...this.pending.values()];
99
+ this.pending.clear();
100
+ for (const channel of channels) await this.openChannel(channel);
101
+ }
102
+ async stop() {
103
+ this.started = false;
104
+ for (const entry of this.registered.values()) entry.abortController.abort();
105
+ this.registered.clear();
106
+ this.pending.clear();
107
+ await this.confluence.closeAll();
108
+ }
109
+ ids() {
110
+ return [...this.registered.keys()];
111
+ }
112
+ has(id) {
113
+ return this.registered.has(id) || this.pending.has(id);
114
+ }
115
+ async openChannel(channel) {
116
+ const channelDir = join(this.props.dir, "channels", channel.id);
117
+ const abortController = new AbortController();
118
+ const runtime = await channel.build({
119
+ channelId: channel.id,
120
+ channelName: channel.name ?? channel.id,
121
+ signal: abortController.signal,
122
+ logger: this.props.logger,
123
+ clock: this.props.clock,
124
+ fs: this.props.fs,
125
+ statePersister: createChannelStatePersisterFactory({
126
+ fs: this.props.fs,
127
+ channelDir
128
+ })
129
+ });
130
+ const result = await this.confluence.add(channel.id, runtime.sources);
131
+ if (result instanceof Error) {
132
+ this.props.logger.error(`channel "${channel.id}" failed to start`, { error: result.message });
133
+ abortController.abort();
134
+ return;
135
+ }
136
+ this.registered.set(channel.id, {
137
+ channel,
138
+ runtime,
139
+ abortController
140
+ });
141
+ }
142
+ handleItem(item) {
143
+ if (item.kind !== "event") return;
144
+ const entry = this.registered.get(item.groupId);
145
+ if (!entry) return;
146
+ const payload = this.transformToPayload(entry, item.event);
147
+ if (payload === null) return;
148
+ this.props.broadcaster.broadcast(payload.content, payload.meta);
149
+ }
150
+ transformToPayload(entry, event) {
151
+ const transform = entry.runtime.transform;
152
+ if (transform === void 0) return {
153
+ content: JSON.stringify(event.data),
154
+ meta: event.meta
155
+ };
156
+ const out = transform(event);
157
+ if (out === null) return null;
158
+ return {
159
+ content: out.content,
160
+ meta: out.meta
161
+ };
162
+ }
163
+ };
164
+ //#endregion
165
+ //#region lib/engine/channel/time-channel.ts
166
+ /**
167
+ * 1 cron entry の time channel を作る factory。
168
+ *
169
+ * 使用例 (inta jiho):
170
+ * ```ts
171
+ * const jiho = timeChannel({
172
+ * id: "inta-jiho",
173
+ * cron: "0 * * * *",
174
+ * transform: (event) => ({
175
+ * content: `⏰ ${new Date(event.data.firedAt as number).getHours()}時をお知らせします。`,
176
+ * meta: { event_type: "schedule", source: "inta-jiho", target_role: "primary" },
177
+ * }),
178
+ * })
179
+ * ```
180
+ *
181
+ * `persist: true` (既定) の場合、`<channelDir>/time.json` に `lastFiredAt` を残し、
182
+ * 起動時に `catchupPolicy` (既定 lastOnly) に従って過去 tick を取り戻す
183
+ */
184
+ function timeChannel(options) {
185
+ const persist = options.persist ?? true;
186
+ const catchupPolicy = options.catchupPolicy ?? { mode: "lastOnly" };
187
+ return defineChannel({
188
+ id: options.id,
189
+ name: options.name ?? options.id,
190
+ build: (ctx) => {
191
+ return {
192
+ sources: [new FlumeTimeSource({
193
+ cron: options.cron,
194
+ statePersister: persist ? ctx.statePersister("time") : void 0,
195
+ catchupPolicy: persist ? catchupPolicy : { mode: "off" }
196
+ })],
197
+ transform: options.transform
198
+ };
199
+ }
200
+ });
201
+ }
202
+ //#endregion
203
+ export { FunnelChannelSupervisor, createChannelStatePersisterFactory, createFileStatePersister, defineChannel, timeChannel };
@@ -1,7 +1,8 @@
1
- import { n as ChannelDeliveryMode, t as ChannelConfig } from "./settings-schema-BL_c2Udm.js";
2
- import { n as FunnelIdGenerator, t as FunnelSettingsReader } from "./settings-reader-BNxjsxCB.js";
3
- import { B as FunnelProcessRunner, I as BaseConnectorConfig, N as CallInput, P as FunnelConnectorAdapter, W as FunnelLogger, _ as ConnectorDiagnosticLog, c as FunnelClock, j as FunnelConnectorListener, l as FunnelHttpClient, n as ConnectorBuildContext, o as ConnectorOperationContext, r as ConnectorDescriptor, s as ConnectorUpdateContext } from "./connector-descriptor-BFIhyTfa.js";
4
- import { n as FunnelFileSystem } from "./file-system-VhwwXZbm.js";
1
+ import { n as FunnelClock, t as FunnelLogger } from "./logger-Bc_Ooypb.js";
2
+ import { n as FunnelFileSystem } from "./file-system-DHgbuTFb.js";
3
+ import { n as ChannelDeliveryMode, t as ChannelConfig } from "./settings-schema-IShSMm5G.js";
4
+ import { n as FunnelIdGenerator, t as FunnelSettingsReader } from "./settings-reader-BnKIbmsD.js";
5
+ import { A as FunnelConnectorListener, F as BaseConnectorConfig, M as CallInput, N as FunnelConnectorAdapter, c as FunnelHttpClient, g as ConnectorDiagnosticLog, n as ConnectorBuildContext, o as ConnectorOperationContext, r as ConnectorDescriptor, s as ConnectorUpdateContext, z as FunnelProcessRunner } from "./connector-descriptor-CmyHlEiT.js";
5
6
 
6
7
  //#region lib/engine/connectors/connector-registry.d.ts
7
8
  type Deps$1 = {
package/dist/claude.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { a as ProcessGuard, c as ChannelResolver, i as SessionStore, n as FunnelClaude, o as McpInstaller, r as LaunchOptions, s as GatewayController, t as FunnelFileProcessGuard } from "./file-process-guard-tVcgckH6.js";
2
- import { r as ConnectorDescriptor } from "./connector-descriptor-BFIhyTfa.js";
3
- import { n as FunnelFileSystem } from "./file-system-VhwwXZbm.js";
4
- import { t as FunnelProfiles } from "./profiles-cVZQkM69.js";
1
+ import { n as FunnelFileSystem } from "./file-system-DHgbuTFb.js";
2
+ import { a as ProcessGuard, c as ChannelResolver, i as SessionStore, n as FunnelClaude, o as McpInstaller, r as LaunchOptions, s as GatewayController, t as FunnelFileProcessGuard } from "./file-process-guard-CiBwXjzm.js";
3
+ import { r as ConnectorDescriptor } from "./connector-descriptor-CmyHlEiT.js";
4
+ import { t as FunnelProfiles } from "./profiles-M24JrcOh.js";
5
5
 
6
6
  //#region lib/engine/mcp/mcp.d.ts
7
7
  declare const FUNNEL_MCP_COMMAND = "bun";
@@ -1,19 +1,7 @@
1
- import { n as FunnelFileSystem } from "./file-system-VhwwXZbm.js";
1
+ import { n as FunnelClock, t as FunnelLogger } from "./logger-Bc_Ooypb.js";
2
+ import { n as FunnelFileSystem } from "./file-system-DHgbuTFb.js";
2
3
  import { z } from "zod";
3
4
 
4
- //#region lib/engine/logger/logger.d.ts
5
- /**
6
- * Structured logger with three levels and an optional log-file path.
7
- * Defaults to NodeFunnelLogger (appends to `<os.tmpdir()>/funnel/funnel.log`);
8
- * MemoryFunnelLogger captures entries in memory and NoopFunnelLogger silences output.
9
- */
10
- declare abstract class FunnelLogger {
11
- abstract info(message: string, meta?: Record<string, unknown>): void;
12
- abstract warn(message: string, meta?: Record<string, unknown>): void;
13
- abstract error(message: string, meta?: Record<string, unknown>): void;
14
- abstract readonly file: string | null;
15
- }
16
- //#endregion
17
5
  //#region lib/engine/process/process-runner.d.ts
18
6
  type RunOptions = {
19
7
  cwd?: string;
@@ -315,17 +303,6 @@ declare abstract class FunnelHttpClient {
315
303
  abstract fetch(request: HttpRequest): Promise<HttpResponse>;
316
304
  }
317
305
  //#endregion
318
- //#region lib/engine/time/clock.d.ts
319
- /**
320
- * Time boundary. Default NodeFunnelClock returns `new Date()`; MemoryFunnelClock
321
- * is settable and `advance(ms)`-able for deterministic schedule / timeout tests.
322
- */
323
- declare abstract class FunnelClock {
324
- abstract now(): Date;
325
- millis(): number;
326
- iso(): string;
327
- }
328
- //#endregion
329
306
  //#region lib/engine/connectors/connector-descriptor.d.ts
330
307
  /** Boundaries a listener needs, supplied by the registry at build time. */
331
308
  type ConnectorListenerDeps = {
@@ -400,4 +377,4 @@ type ConnectorDescriptor = {
400
377
  operations: Record<string, ConnectorOperation>;
401
378
  };
402
379
  //#endregion
403
- export { connectorRawEventSchema as A, FunnelProcessRunner as B, ConnectorRawQuery as C, StoredRawEvent as D, StoredProcessedEvent as E, JsonValue as F, RunOptions as H, BaseConnectorConfig as I, baseConnectorConfigSchema as L, NotifyFn as M, CallInput as N, connectorConnectionEventSchema as O, FunnelConnectorAdapter as P, AttachOptions as R, ConnectorRawEvent as S, StoredConnectionEvent as T, RunResult as U, ProcessSnapshot as V, FunnelLogger as W, ConnectorDiagnosticLog as _, ConnectorOperation as a, ConnectorProcessedRecord as b, FunnelClock as c, HttpResponse as d, CONNECTOR_CONNECTION_STATUSES as f, ConnectorConnectionStatus as g, ConnectorConnectionRecord as h, ConnectorListenerDeps as i, FunnelConnectorListener as j, connectorProcessedEventSchema as k, FunnelHttpClient as l, ConnectorConnectionQuery as m, ConnectorBuildContext as n, ConnectorOperationContext as o, ConnectorConnectionEvent as p, ConnectorDescriptor as r, ConnectorUpdateContext as s, ConnectorAdapterDeps as t, HttpRequest as u, ConnectorProcessedEvent as v, ConnectorRawRecord as w, ConnectorQuery as x, ConnectorProcessedQuery as y, DetachOptions as z };
380
+ export { FunnelConnectorListener as A, ProcessSnapshot as B, ConnectorRawRecord as C, connectorConnectionEventSchema as D, StoredRawEvent as E, BaseConnectorConfig as F, RunResult as H, baseConnectorConfigSchema as I, AttachOptions as L, CallInput as M, FunnelConnectorAdapter as N, connectorProcessedEventSchema as O, JsonValue as P, DetachOptions as R, ConnectorRawQuery as S, StoredProcessedEvent as T, RunOptions as V, ConnectorProcessedEvent as _, ConnectorOperation as a, ConnectorQuery as b, FunnelHttpClient as c, CONNECTOR_CONNECTION_STATUSES as d, ConnectorConnectionEvent as f, ConnectorDiagnosticLog as g, ConnectorConnectionStatus as h, ConnectorListenerDeps as i, NotifyFn as j, connectorRawEventSchema as k, HttpRequest as l, ConnectorConnectionRecord as m, ConnectorBuildContext as n, ConnectorOperationContext as o, ConnectorConnectionQuery as p, ConnectorDescriptor as r, ConnectorUpdateContext as s, ConnectorAdapterDeps as t, HttpResponse as u, ConnectorProcessedQuery as v, StoredConnectionEvent as w, ConnectorRawEvent as x, ConnectorProcessedRecord as y, FunnelProcessRunner as z };
@@ -1,6 +1,7 @@
1
- import { M as NotifyFn, N as CallInput, P as FunnelConnectorAdapter, W as FunnelLogger, _ as ConnectorDiagnosticLog, l as FunnelHttpClient, r as ConnectorDescriptor } from "../connector-descriptor-BFIhyTfa.js";
2
- import { n as discordConnectorSchema, t as DiscordConnectorConfig } from "../discord-connector-schema-D-bOVAKt.js";
3
- import { t as FunnelFlumeSourceListener } from "../flume-source-listener-BNyAII7N.js";
1
+ import { t as FunnelLogger } from "../logger-Bc_Ooypb.js";
2
+ import { M as CallInput, N as FunnelConnectorAdapter, c as FunnelHttpClient, g as ConnectorDiagnosticLog, j as NotifyFn, r as ConnectorDescriptor } from "../connector-descriptor-CmyHlEiT.js";
3
+ import { n as discordConnectorSchema, t as DiscordConnectorConfig } from "../discord-connector-schema-D1dyxhXk.js";
4
+ import { t as FunnelFlumeSourceListener } from "../flume-source-listener-FzR5lGpL.js";
4
5
  import { FlumeRuntimeDeps } from "@interactive-inc/flume";
5
6
 
6
7
  //#region lib/engine/connectors/discord-adapter.d.ts
@@ -1,6 +1,7 @@
1
- import { B as FunnelProcessRunner, M as NotifyFn, N as CallInput, P as FunnelConnectorAdapter, W as FunnelLogger, _ as ConnectorDiagnosticLog, r as ConnectorDescriptor } from "../connector-descriptor-BFIhyTfa.js";
2
- import { t as FunnelFlumeSourceListener } from "../flume-source-listener-BNyAII7N.js";
3
- import { n as ghConnectorSchema, t as GhConnectorConfig } from "../gh-connector-schema-DWQaB6gX.js";
1
+ import { t as FunnelLogger } from "../logger-Bc_Ooypb.js";
2
+ import { M as CallInput, N as FunnelConnectorAdapter, g as ConnectorDiagnosticLog, j as NotifyFn, r as ConnectorDescriptor, z as FunnelProcessRunner } from "../connector-descriptor-CmyHlEiT.js";
3
+ import { t as FunnelFlumeSourceListener } from "../flume-source-listener-FzR5lGpL.js";
4
+ import { n as ghConnectorSchema, t as GhConnectorConfig } from "../gh-connector-schema-BCHEqpUg.js";
4
5
  import { FlumeRuntimeDeps } from "@interactive-inc/flume";
5
6
 
6
7
  //#region lib/engine/connectors/gh-adapter.d.ts
@@ -1,6 +1,7 @@
1
- import { M as NotifyFn, W as FunnelLogger, _ as ConnectorDiagnosticLog, j as FunnelConnectorListener, r as ConnectorDescriptor } from "../connector-descriptor-BFIhyTfa.js";
2
- import { n as FunnelFileSystem } from "../file-system-VhwwXZbm.js";
3
- import { a as scheduleConnectorSchema, i as scheduleCatchupPolicySchema, n as ScheduleConnectorConfig, o as scheduleEntrySchema, r as ScheduleEntry, t as ScheduleCatchupPolicy } from "../schedule-connector-schema-Z0RXLgPI.js";
1
+ import { t as FunnelLogger } from "../logger-Bc_Ooypb.js";
2
+ import { n as FunnelFileSystem } from "../file-system-DHgbuTFb.js";
3
+ import { A as FunnelConnectorListener, g as ConnectorDiagnosticLog, j as NotifyFn, r as ConnectorDescriptor } from "../connector-descriptor-CmyHlEiT.js";
4
+ import { a as scheduleConnectorSchema, i as scheduleCatchupPolicySchema, n as ScheduleConnectorConfig, o as scheduleEntrySchema, r as ScheduleEntry, t as ScheduleCatchupPolicy } from "../schedule-connector-schema-D7ZEKvit.js";
4
5
 
5
6
  //#region lib/engine/connectors/match-cron.d.ts
6
7
  /**
@@ -1,6 +1,7 @@
1
- import { M as NotifyFn, N as CallInput, P as FunnelConnectorAdapter, W as FunnelLogger, _ as ConnectorDiagnosticLog, l as FunnelHttpClient, r as ConnectorDescriptor } from "../connector-descriptor-BFIhyTfa.js";
2
- import { t as FunnelFlumeSourceListener } from "../flume-source-listener-BNyAII7N.js";
3
- import { a as SlackRawEvent, c as SlackMessageEvent, d as slackConnectorSchema, i as SlackProcessedSkip, l as SlackReactionEvent, n as SlackProcessed, o as SlackSkipReason, r as SlackProcessedEmit, s as SlackEvent, t as FunnelSlackEventProcessor, u as SlackConnectorConfig } from "../slack-event-processor-BhCf5Wiy.js";
1
+ import { t as FunnelLogger } from "../logger-Bc_Ooypb.js";
2
+ import { M as CallInput, N as FunnelConnectorAdapter, c as FunnelHttpClient, g as ConnectorDiagnosticLog, j as NotifyFn, r as ConnectorDescriptor } from "../connector-descriptor-CmyHlEiT.js";
3
+ import { t as FunnelFlumeSourceListener } from "../flume-source-listener-FzR5lGpL.js";
4
+ import { a as SlackRawEvent, c as SlackMessageEvent, d as slackConnectorSchema, i as SlackProcessedSkip, l as SlackReactionEvent, n as SlackProcessed, o as SlackSkipReason, r as SlackProcessedEmit, s as SlackEvent, t as FunnelSlackEventProcessor, u as SlackConnectorConfig } from "../slack-event-processor-BDTr62cN.js";
4
5
  import { FlumeRuntimeDeps } from "@interactive-inc/flume";
5
6
 
6
7
  //#region lib/engine/connectors/slack-adapter.d.ts
@@ -1,2 +1,2 @@
1
- import { a as DiagnosticsGatewayProbe, c as FunnelDiagnostics, d as DiagnosticEvent, f as previewOf, h as toDiagnosticEvent, i as DiagnosticsChannelSource, l as ReplayResult, m as toDiagnosticConnectionError, n as DiagnoseAllReport, o as DiagnosticsPublisher, p as queryRows, r as DiagnosisStatus, s as DiagnosticsTokenReader, t as ChannelDiagnosis, u as DiagnosticConnectionError } from "./funnel-diagnostics-b9ar0Ing.js";
1
+ import { a as DiagnosticsGatewayProbe, c as FunnelDiagnostics, d as DiagnosticEvent, f as previewOf, h as toDiagnosticEvent, i as DiagnosticsChannelSource, l as ReplayResult, m as toDiagnosticConnectionError, n as DiagnoseAllReport, o as DiagnosticsPublisher, p as queryRows, r as DiagnosisStatus, s as DiagnosticsTokenReader, t as ChannelDiagnosis, u as DiagnosticConnectionError } from "./funnel-diagnostics-kbd27d7I.js";
2
2
  export { ChannelDiagnosis, DiagnoseAllReport, DiagnosisStatus, DiagnosticConnectionError, DiagnosticEvent, DiagnosticsChannelSource, DiagnosticsGatewayProbe, DiagnosticsPublisher, DiagnosticsTokenReader, FunnelDiagnostics, ReplayResult, previewOf, queryRows, toDiagnosticConnectionError, toDiagnosticEvent };
package/dist/docs.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { n as FunnelDocs, t as DocsTopicListing } from "./funnel-docs-CNklHvbt.js";
1
+ import { n as FunnelDocs, t as DocsTopicListing } from "./funnel-docs-Cd66sKyV.js";
2
2
  export { DocsTopicListing, FunnelDocs };
package/dist/doctor.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { n as DoctorReport, r as FunnelDoctor, t as DoctorFixMode } from "./funnel-doctor-CnRQi4kM.js";
1
+ import { n as DoctorReport, r as FunnelDoctor, t as DoctorFixMode } from "./funnel-doctor-CVTNUtDn.js";
2
2
  export { DoctorFixMode, DoctorReport, FunnelDoctor };