@interactive-inc/claude-funnel 0.59.1 → 0.60.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 (85) hide show
  1. package/README.md +9 -3
  2. package/dist/bin.js +549 -487
  3. package/dist/channels-2g_BU1N0.d.ts +174 -0
  4. package/dist/claude.d.ts +9 -5
  5. package/dist/claude.js +54 -17
  6. package/dist/{diagnostic-log-Cb3v8P7p.d.ts → connector-descriptor-6SXJoszo.d.ts} +158 -2
  7. package/dist/connectors/discord.d.ts +30 -4
  8. package/dist/connectors/discord.js +2 -2
  9. package/dist/connectors/gh.d.ts +21 -5
  10. package/dist/connectors/gh.js +3 -3
  11. package/dist/connectors/schedule.d.ts +124 -2
  12. package/dist/connectors/schedule.js +3 -3
  13. package/dist/connectors/slack.d.ts +149 -5
  14. package/dist/connectors/slack.js +2 -2
  15. package/dist/{diagnostic-sql-reader-CzYgZpq2.js → diagnostic-sql-reader-C9zR-Csp.js} +5 -5
  16. package/dist/diagnostics.d.ts +1 -1
  17. package/dist/diagnostics.js +1 -1
  18. package/dist/{discord-listener-CKsZGTnH.js → discord-connector-BL36yvbL.js} +60 -37
  19. package/dist/docs.d.ts +1 -1
  20. package/dist/docs.js +1 -1
  21. package/dist/doctor.d.ts +1 -1
  22. package/dist/doctor.js +1 -1
  23. package/dist/error-message-of-Byi4y0Uf.js +9 -0
  24. package/dist/{file-process-guard-JhFpmHYo.d.ts → file-process-guard-DOlCr4GF.d.ts} +4 -5
  25. package/dist/{funnel-diagnostics-BpKYrMSu.js → funnel-diagnostics-CSiJmPlZ.js} +19 -2
  26. package/dist/{funnel-diagnostics-K-wON25Y.d.ts → funnel-diagnostics-DpXOsCty.d.ts} +3 -3
  27. package/dist/{funnel-docs-ng5K8w4j.js → funnel-docs-BxXZ9Ksx.js} +76 -3
  28. package/dist/{funnel-docs-DYBs1-H_.d.ts → funnel-docs-CNklHvbt.d.ts} +1 -1
  29. package/dist/{funnel-doctor-vxO96TCA.d.ts → funnel-doctor-CZf_0Luq.d.ts} +2 -2
  30. package/dist/{funnel-recovery-COExL9MD.d.ts → funnel-recovery-DnLrdWO9.d.ts} +1 -1
  31. package/dist/gateway/daemon.js +326 -266
  32. package/dist/gateway-base-url-Dy4Ykuoh.js +14 -0
  33. package/dist/gateway.d.ts +2 -2
  34. package/dist/gateway.js +2 -2
  35. package/dist/{gh-listener-B2I4s8qh.js → gh-connector-DpiixfQZ.js} +53 -5
  36. package/dist/gh-connector-schema-Rzwc1c1N.js +12 -0
  37. package/dist/http-client-oICicjuO.d.ts +18 -0
  38. package/dist/index-CgY8NdMz.d.ts +1057 -0
  39. package/dist/index.d.ts +1558 -17
  40. package/dist/index.js +374 -342
  41. package/dist/{local-config-json-schema-DE1zkMcb.js → local-config-json-schema-JyLqOQNX.js} +9 -5
  42. package/dist/local-config-sync-Dh1Croqe.d.ts +169 -0
  43. package/dist/local-config.d.ts +2 -2
  44. package/dist/local-config.js +2 -2
  45. package/dist/logger.js +1 -1
  46. package/dist/{memory-diagnostic-log-B9Us7X05.js → memory-diagnostic-log-CI60kNfB.js} +33 -18
  47. package/dist/{memory-token-prompter-CcShtF8B.d.ts → memory-token-prompter-B4sjyaAq.d.ts} +2 -2
  48. package/dist/{memory-token-prompter-C7vREzCL.js → memory-token-prompter-CZde7e6y.js} +1 -1
  49. package/dist/{node-file-system-BcrmWN9I.js → node-file-system-Blr8pAir.js} +1 -1
  50. package/dist/node-http-client-lowp60Oa.js +25 -0
  51. package/dist/{gh-connector-schema-ClPLSYD9.js → node-process-runner-DxTvycoK.js} +1 -12
  52. package/dist/{profiles-g2qGVOWv.d.ts → profiles-Cy5wXQ0L.d.ts} +3 -3
  53. package/dist/{profiles-MnXvYfZF.js → profiles-DSzTeKQw.js} +1 -1
  54. package/dist/profiles.d.ts +1 -1
  55. package/dist/profiles.js +1 -1
  56. package/dist/recovery.d.ts +1 -1
  57. package/dist/recovery.js +1 -1
  58. package/dist/{schedule-listener-DP9Jhc6U.js → schedule-connector-L4uzg5M8.js} +109 -9
  59. package/dist/{settings-reader-DPwqOVUm.d.ts → settings-reader-BIFB_j2f.d.ts} +1 -1
  60. package/dist/settings-schema-D1xcOqRu.d.ts +78 -0
  61. package/dist/{gateway-base-url-DxVjjDoW.js → settings-store-CUKSeTXC.js} +27 -29
  62. package/dist/{slack-listener-C4wlZaOq.js → slack-connector-DQIFPdBF.js} +67 -12
  63. package/dist/slot-fields-CMoRpwuy.js +45 -0
  64. package/dist/{yaml-render-cZu6CxkE.js → yaml-render-qW34NlYz.js} +4 -4
  65. package/package.json +1 -1
  66. package/dist/connector-adapter-DGacCppE.d.ts +0 -25
  67. package/dist/discord-connector-schema-CQyfDkLD.d.ts +0 -39
  68. package/dist/gh-connector-schema-CZzwzvqY.d.ts +0 -14
  69. package/dist/index-D7mjirUL.d.ts +0 -3602
  70. package/dist/local-config-sync-BGPAS9Be.d.ts +0 -401
  71. package/dist/process-runner-DIm1cy95.d.ts +0 -52
  72. package/dist/resolve-connector-token-CczqG_Ig.js +0 -22
  73. package/dist/schedule-listener-DoMPjHZj.d.ts +0 -112
  74. package/dist/settings-schema-1hh11jnN.d.ts +0 -152
  75. package/dist/slack-listener-Dj9NFbAJ.d.ts +0 -136
  76. /package/dist/{connector-adapter-qwXLjQId.js → connector-adapter-DU9Rvyec.js} +0 -0
  77. /package/dist/{connector-listener-CpHBecCj.js → connector-listener-DR3aKOuK.js} +0 -0
  78. /package/dist/{file-system-PWKKU7lA.js → file-system-Wvzc2ePY.js} +0 -0
  79. /package/dist/{file-system-DxpnnUVb.d.ts → file-system-o51IsM0W.d.ts} +0 -0
  80. /package/dist/{funnel-doctor-CApCezTq.js → funnel-doctor-DiJCjHsg.js} +0 -0
  81. /package/dist/{funnel-log-sqlite-sink-B_5_4ybn.js → funnel-log-sqlite-sink-kqJbx2H7.js} +0 -0
  82. /package/dist/{funnel-recovery-D9CxD5Zs.js → funnel-recovery-BFdPjL6Z.js} +0 -0
  83. /package/dist/{logger-BP6SisKt.js → logger-B6iyNbxM.js} +0 -0
  84. /package/dist/{schedule-connector-schema-B_xO5z5B.js → schedule-connector-schema-CfyuMCMh.js} +0 -0
  85. /package/dist/{settings-reader-DPqrpV7s.js → settings-reader-CtQ-Ix8_.js} +0 -0
@@ -1,4 +1,6 @@
1
- import { a as ScheduleEntry, c as scheduleEntrySchema, i as ScheduleConnectorConfig, l as ScheduleStateStore, n as ScheduleOnFired, o as scheduleCatchupPolicySchema, r as ScheduleCatchupPolicy, s as scheduleConnectorSchema, t as FunnelScheduleListener } from "../schedule-listener-DoMPjHZj.js";
1
+ import { B as FunnelLogger, D as FunnelConnectorListener, O as NotifyFn, p as ConnectorDiagnosticLog, r as ConnectorDescriptor } from "../connector-descriptor-6SXJoszo.js";
2
+ import { n as FunnelFileSystem } from "../file-system-o51IsM0W.js";
3
+ import { z } from "zod";
2
4
 
3
5
  //#region lib/engine/connectors/match-cron.d.ts
4
6
  /**
@@ -13,4 +15,124 @@ import { a as ScheduleEntry, c as scheduleEntrySchema, i as ScheduleConnectorCon
13
15
  */
14
16
  declare const matchCron: (expr: string, date: Date) => boolean;
15
17
  //#endregion
16
- export { FunnelScheduleListener, ScheduleCatchupPolicy, ScheduleConnectorConfig, ScheduleEntry, ScheduleOnFired, ScheduleStateStore, matchCron, scheduleCatchupPolicySchema, scheduleConnectorSchema, scheduleEntrySchema };
18
+ //#region lib/engine/connectors/schedule-state-store.d.ts
19
+ type Deps$1 = {
20
+ path: string;
21
+ fs?: FunnelFileSystem;
22
+ };
23
+ /**
24
+ * Per-connector lastFiredAt persistence for the schedule listener. The path is
25
+ * passed in by the schedule connector descriptor (via the registry's
26
+ * connectorDir) so this store does not know about the funnel directory layout
27
+ * (`channels/<id>/connectors/<id>/state.json` lives outside this class).
28
+ */
29
+ declare class ScheduleStateStore {
30
+ private readonly path;
31
+ private readonly fs;
32
+ constructor(deps: Deps$1);
33
+ load(): Map<string, Date>;
34
+ save(state: Map<string, Date>): void;
35
+ }
36
+ //#endregion
37
+ //#region lib/engine/connectors/schedule-connector-schema.d.ts
38
+ /**
39
+ * Catch-up behavior when the daemon was down past one or more matching minutes.
40
+ *
41
+ * - `latest`: fire once with the most recent missed match (default; preserves prior behavior).
42
+ * - `all`: fire once per missed minute, oldest first (capped at 24 h).
43
+ * - `skip`: never fire missed matches; only fire when the current minute matches.
44
+ */
45
+ declare const scheduleCatchupPolicySchema: z.ZodEnum<{
46
+ latest: "latest";
47
+ all: "all";
48
+ skip: "skip";
49
+ }>;
50
+ type ScheduleCatchupPolicy = z.infer<typeof scheduleCatchupPolicySchema>;
51
+ declare const scheduleEntrySchema: z.ZodObject<{
52
+ id: z.ZodString;
53
+ cron: z.ZodString;
54
+ prompt: z.ZodString;
55
+ enabled: z.ZodDefault<z.ZodBoolean>;
56
+ catchupPolicy: z.ZodDefault<z.ZodEnum<{
57
+ latest: "latest";
58
+ all: "all";
59
+ skip: "skip";
60
+ }>>;
61
+ }, z.core.$strip>;
62
+ type ScheduleEntry = z.infer<typeof scheduleEntrySchema>;
63
+ declare const scheduleConnectorSchema: z.ZodObject<{
64
+ id: z.ZodString;
65
+ name: z.ZodString;
66
+ type: z.ZodLiteral<"schedule">;
67
+ entries: z.ZodDefault<z.ZodArray<z.ZodObject<{
68
+ id: z.ZodString;
69
+ cron: z.ZodString;
70
+ prompt: z.ZodString;
71
+ enabled: z.ZodDefault<z.ZodBoolean>;
72
+ catchupPolicy: z.ZodDefault<z.ZodEnum<{
73
+ latest: "latest";
74
+ all: "all";
75
+ skip: "skip";
76
+ }>>;
77
+ }, z.core.$strip>>>;
78
+ createdAt: z.ZodOptional<z.ZodString>;
79
+ updatedAt: z.ZodOptional<z.ZodString>;
80
+ }, z.core.$strip>;
81
+ type ScheduleConnectorConfig = z.infer<typeof scheduleConnectorSchema>;
82
+ //#endregion
83
+ //#region lib/engine/connectors/schedule-listener.d.ts
84
+ type ScheduleOnFired = (entry: ScheduleEntry, firedAt: Date) => void | Promise<void>;
85
+ type Deps = {
86
+ config: ScheduleConnectorConfig;
87
+ lastFiredStore: ScheduleStateStore; /** Funnel channel uuid this connector lives under; stamped onto diagnostic-log rows. */
88
+ channelId?: string;
89
+ logger?: FunnelLogger; /** Diagnostic log of fired entries and lifecycle. No-op when absent. */
90
+ diagnosticLog?: ConnectorDiagnosticLog;
91
+ now?: () => Date;
92
+ /**
93
+ * Invoked after a schedule entry fires successfully. Use to remove one-shot
94
+ * entries from the connector config, or to log per-fire side effects.
95
+ * Errors from this callback are caught and logged; they do not abort the tick.
96
+ */
97
+ onFired?: ScheduleOnFired;
98
+ };
99
+ declare class FunnelScheduleListener extends FunnelConnectorListener {
100
+ private readonly config;
101
+ private readonly lastFiredStore;
102
+ private readonly channelId;
103
+ private readonly logger;
104
+ private readonly diagnosticLog;
105
+ private readonly now;
106
+ private readonly onFired;
107
+ private timer;
108
+ private stopped;
109
+ constructor(deps: Deps);
110
+ start(notify: NotifyFn): Promise<void>;
111
+ stop(): Promise<void>;
112
+ isAlive(): boolean;
113
+ tick(notify: NotifyFn): Promise<void>;
114
+ private fireEntry;
115
+ private notifyOne;
116
+ private findMostRecentMatch;
117
+ private findAllMatches;
118
+ private logInvalidCron;
119
+ private truncateToMinute;
120
+ private recordRaw;
121
+ private recordProcessed;
122
+ private recordConnection;
123
+ }
124
+ //#endregion
125
+ //#region lib/engine/connectors/schedule-connector.d.ts
126
+ type ScheduleConnectorOptions = {
127
+ /** Invoked after a schedule entry fires successfully — e.g. to drop one-shot entries. */onFired?: ScheduleOnFired;
128
+ };
129
+ /**
130
+ * Schedule connector descriptor. Pass `scheduleConnector()` to
131
+ * `new Funnel({ connectors: [...] })` to enable the type. Schedule has no
132
+ * outbound adapter; its per-entry CRUD is exposed via `operations`
133
+ * (listEntries / addEntry / removeEntry) and reached through
134
+ * `funnel.channels.connectorOp(...)`.
135
+ */
136
+ declare const scheduleConnector: (options?: ScheduleConnectorOptions) => ConnectorDescriptor;
137
+ //#endregion
138
+ export { FunnelScheduleListener, ScheduleCatchupPolicy, ScheduleConnectorConfig, ScheduleConnectorOptions, ScheduleEntry, ScheduleOnFired, ScheduleStateStore, matchCron, scheduleCatchupPolicySchema, scheduleConnector, scheduleConnectorSchema, scheduleEntrySchema };
@@ -1,3 +1,3 @@
1
- import { n as ScheduleStateStore, r as matchCron, t as FunnelScheduleListener } from "../schedule-listener-DP9Jhc6U.js";
2
- import { n as scheduleConnectorSchema, r as scheduleEntrySchema, t as scheduleCatchupPolicySchema } from "../schedule-connector-schema-B_xO5z5B.js";
3
- export { FunnelScheduleListener, ScheduleStateStore, matchCron, scheduleCatchupPolicySchema, scheduleConnectorSchema, scheduleEntrySchema };
1
+ import { n as scheduleConnectorSchema, r as scheduleEntrySchema, t as scheduleCatchupPolicySchema } from "../schedule-connector-schema-CfyuMCMh.js";
2
+ import { i as matchCron, n as FunnelScheduleListener, r as ScheduleStateStore, t as scheduleConnector } from "../schedule-connector-L4uzg5M8.js";
3
+ export { FunnelScheduleListener, ScheduleStateStore, matchCron, scheduleCatchupPolicySchema, scheduleConnector, scheduleConnectorSchema, scheduleEntrySchema };
@@ -1,18 +1,48 @@
1
- import { n as FunnelConnectorAdapter, t as CallInput } from "../connector-adapter-DGacCppE.js";
2
- import { a as slackConnectorSchema, c as SlackProcessedEmit, d as SlackSkipReason, f as SlackEvent, i as SlackConnectorConfig, l as SlackProcessedSkip, m as SlackReactionEvent, n as SlackOnAppCreated, o as FunnelSlackEventProcessor, p as SlackMessageEvent, r as SlackPreprocessEvent, s as SlackProcessed, t as FunnelSlackListener, u as SlackRawEvent } from "../slack-listener-Dj9NFbAJ.js";
1
+ import { A as FunnelConnectorAdapter, B as FunnelLogger, D as FunnelConnectorListener, O as NotifyFn, k as CallInput, p as ConnectorDiagnosticLog, r as ConnectorDescriptor } from "../connector-descriptor-6SXJoszo.js";
2
+ import { z } from "zod";
3
+ import { App } from "@slack/bolt";
3
4
 
5
+ //#region lib/engine/connectors/slack-connector-schema.d.ts
6
+ /**
7
+ * A slack connector resolves its tokens one of two ways, set at sync time:
8
+ *
9
+ * - literal: `botToken` / `appToken` hold the real `xoxb-`/`xapp-` secret
10
+ * (set by a `fnl channels` command or a TTY prompt at launch).
11
+ * - by reference: `botTokenEnv` / `appTokenEnv` hold the *name* of an env var.
12
+ * The secret never lands in settings.json; the listener resolves it from
13
+ * `process.env` at start. This form is only set through the engine API
14
+ * (`new Funnel(...)`) — funnel.json and the `fnl` CLI produce literals.
15
+ *
16
+ * Both are optional at the schema level (a discriminated-union member can't
17
+ * carry a cross-field refine); the listener requires exactly one resolved
18
+ * token per slot and errors loudly otherwise.
19
+ */
20
+ declare const slackConnectorSchema: z.ZodObject<{
21
+ id: z.ZodString;
22
+ name: z.ZodString;
23
+ type: z.ZodLiteral<"slack">;
24
+ botToken: z.ZodOptional<z.ZodString>;
25
+ appToken: z.ZodOptional<z.ZodString>;
26
+ botTokenEnv: z.ZodOptional<z.ZodString>;
27
+ appTokenEnv: z.ZodOptional<z.ZodString>;
28
+ minify: z.ZodDefault<z.ZodBoolean>;
29
+ createdAt: z.ZodOptional<z.ZodString>;
30
+ updatedAt: z.ZodOptional<z.ZodString>;
31
+ }, z.core.$strip>;
32
+ type SlackConnectorConfig = z.infer<typeof slackConnectorSchema>;
33
+ //#endregion
4
34
  //#region lib/engine/connectors/slack-adapter.d.ts
5
35
  type SlackWebClientLike = {
6
36
  apiCall: (method: string, options?: Record<string, unknown>) => Promise<unknown>;
7
37
  };
8
- type Deps = {
38
+ type Deps$1 = {
9
39
  config: SlackConnectorConfig; /** Environment used to resolve a `botTokenEnv` reference. Defaults to process.env. */
10
40
  env?: NodeJS.ProcessEnv;
11
41
  client?: SlackWebClientLike;
12
42
  };
13
43
  declare class FunnelSlackAdapter extends FunnelConnectorAdapter {
14
44
  private readonly client;
15
- constructor(deps: Deps);
45
+ constructor(deps: Deps$1);
16
46
  call(input: CallInput): Promise<unknown>;
17
47
  postMessage(props: {
18
48
  channel: string;
@@ -31,4 +61,118 @@ declare class FunnelSlackAdapter extends FunnelConnectorAdapter {
31
61
  }): Promise<unknown>;
32
62
  }
33
63
  //#endregion
34
- export { FunnelSlackAdapter, FunnelSlackEventProcessor, FunnelSlackListener, SlackConnectorConfig, SlackEvent, SlackMessageEvent, SlackOnAppCreated, SlackPreprocessEvent, SlackProcessed, SlackProcessedEmit, SlackProcessedSkip, SlackRawEvent, SlackReactionEvent, SlackSkipReason, SlackWebClientLike, slackConnectorSchema };
64
+ //#region lib/engine/connectors/slack-event-types.d.ts
65
+ type SlackMessageEvent = {
66
+ kind: "message";
67
+ channel: string;
68
+ user: string;
69
+ rawText: string;
70
+ text: string;
71
+ threadTs: string;
72
+ ts: string;
73
+ isThreadRoot: boolean;
74
+ mentioned: boolean;
75
+ source: "app_mention" | "message";
76
+ };
77
+ type SlackReactionEvent = {
78
+ kind: "reaction_added" | "reaction_removed";
79
+ channel: string;
80
+ user: string;
81
+ emoji: string;
82
+ targetTs: string;
83
+ targetUser: string | null;
84
+ };
85
+ type SlackEvent = SlackMessageEvent | SlackReactionEvent;
86
+ //#endregion
87
+ //#region lib/engine/connectors/slack-event-processor.d.ts
88
+ type SlackRawEvent = Record<string, unknown>;
89
+ /**
90
+ * Why the processor dropped an event. Mirrored verbatim into the diagnostic
91
+ * log's processed `outcome` column so "Slack delivered it but no notification arrived" is
92
+ * traceable to the exact gate that dropped it. The listener may additionally
93
+ * record `skip:preprocess` for events a host preprocessor dropped before the
94
+ * processor ran — that gate is outside this type.
95
+ */
96
+ type SlackSkipReason = "skip:type" | "skip:subtype" | "skip:dedup" | "skip:self-user" | "skip:self-bot";
97
+ type SlackProcessedSkip = {
98
+ skip: true;
99
+ reason: SlackSkipReason;
100
+ };
101
+ type SlackProcessedEmit = {
102
+ skip: false;
103
+ event: SlackEvent;
104
+ content: string;
105
+ meta: Record<string, string>;
106
+ shouldReact: boolean;
107
+ channel: string;
108
+ timestamp: string;
109
+ };
110
+ type SlackProcessed = SlackProcessedSkip | SlackProcessedEmit;
111
+ type Props = {
112
+ ownBotUserId: string;
113
+ ownBotId: string;
114
+ minify?: boolean;
115
+ now?: () => number;
116
+ };
117
+ declare class FunnelSlackEventProcessor {
118
+ private readonly ownBotUserId;
119
+ private readonly ownBotId;
120
+ private readonly minify;
121
+ private readonly now;
122
+ private readonly dedup;
123
+ constructor(props: Props);
124
+ process(event: SlackRawEvent): SlackProcessed;
125
+ }
126
+ //#endregion
127
+ //#region lib/engine/connectors/slack-listener.d.ts
128
+ type SlackOnAppCreated = (app: App) => void | Promise<void>;
129
+ type SlackPreprocessEvent = (event: SlackRawEvent) => SlackRawEvent | null;
130
+ type Deps = {
131
+ config: SlackConnectorConfig; /** Funnel channel uuid this connector lives under; stamped onto diagnostic-log rows. */
132
+ channelId?: string; /** Environment used to resolve `botTokenEnv`/`appTokenEnv` references. Defaults to process.env. */
133
+ env?: NodeJS.ProcessEnv;
134
+ logger?: FunnelLogger; /** Diagnostic log of inbound events, before and after processing. No-op when absent. */
135
+ diagnosticLog?: ConnectorDiagnosticLog;
136
+ /**
137
+ * Invoked after the Bolt App is constructed, before it starts.
138
+ * Use to attach app.action handlers, custom middleware, etc.
139
+ */
140
+ onAppCreated?: SlackOnAppCreated;
141
+ /**
142
+ * Transform or drop the raw Slack event before the built-in processor sees it.
143
+ * Return null to drop the event entirely.
144
+ */
145
+ preprocessEvent?: SlackPreprocessEvent;
146
+ };
147
+ declare class FunnelSlackListener extends FunnelConnectorListener {
148
+ private readonly config;
149
+ private readonly channelId;
150
+ private readonly env;
151
+ private readonly logger;
152
+ private readonly diagnosticLog;
153
+ private readonly onAppCreated;
154
+ private readonly preprocessEvent;
155
+ private app;
156
+ private connected;
157
+ constructor(deps: Deps);
158
+ start(notify: NotifyFn): Promise<void>;
159
+ stop(): Promise<void>;
160
+ isAlive(): boolean;
161
+ private recordRaw;
162
+ private recordProcessed;
163
+ private recordConnection;
164
+ }
165
+ //#endregion
166
+ //#region lib/engine/connectors/slack-connector.d.ts
167
+ type SlackConnectorOptions = {
168
+ /** Invoked after the Bolt App is constructed, before start — attach app.action handlers etc. */onAppCreated?: SlackOnAppCreated; /** Transform or drop a raw Slack event before the built-in processor sees it. */
169
+ preprocessEvent?: SlackPreprocessEvent;
170
+ };
171
+ /**
172
+ * Slack connector descriptor. Pass `slackConnector()` to
173
+ * `new Funnel({ connectors: [...] })` to enable the type. Host launch hooks are
174
+ * closed over here, so they need no Funnel-level option plumbing.
175
+ */
176
+ declare const slackConnector: (options?: SlackConnectorOptions) => ConnectorDescriptor;
177
+ //#endregion
178
+ export { FunnelSlackAdapter, FunnelSlackEventProcessor, FunnelSlackListener, SlackConnectorConfig, SlackConnectorOptions, SlackEvent, SlackMessageEvent, SlackOnAppCreated, SlackPreprocessEvent, SlackProcessed, SlackProcessedEmit, SlackProcessedSkip, SlackRawEvent, SlackReactionEvent, SlackSkipReason, SlackWebClientLike, slackConnector, slackConnectorSchema };
@@ -1,3 +1,3 @@
1
- import { n as FunnelSlackEventProcessor, r as FunnelSlackAdapter, t as FunnelSlackListener } from "../slack-listener-C4wlZaOq.js";
2
1
  import { t as slackConnectorSchema } from "../slack-connector-schema-C1zEf4TG.js";
3
- export { FunnelSlackAdapter, FunnelSlackEventProcessor, FunnelSlackListener, slackConnectorSchema };
2
+ import { i as FunnelSlackAdapter, n as FunnelSlackListener, r as FunnelSlackEventProcessor, t as slackConnector } from "../slack-connector-DQIFPdBF.js";
3
+ export { FunnelSlackAdapter, FunnelSlackEventProcessor, FunnelSlackListener, slackConnector, slackConnectorSchema };
@@ -1,10 +1,10 @@
1
1
  import { Database } from "bun:sqlite";
2
- //#region lib/gateway/diagnostic-log/diagnostic-sql-reader.ts
2
+ //#region lib/engine/diagnostic-log/diagnostic-sql-reader.ts
3
3
  /**
4
4
  * Read-only SQL surface over the three diagnostic tables, for Claude to query
5
5
  * the log with arbitrary `SELECT`s. It opens all files read-only and exposes
6
6
  * three views — `raw`, `processed`, `connection` — that hide the storage
7
- * details (the physical table is `leuco_log` and each row's columns live
7
+ * details (the physical table is `logs` and each row's columns live
8
8
  * inside a JSON `event` blob): the views surface the columns as plain fields,
9
9
  * with `payload` already pulled out of the nested JSON.
10
10
  *
@@ -59,7 +59,7 @@ const rawViewSql = `CREATE TEMP VIEW raw AS SELECT
59
59
  json_extract(event, '$.connector_id') AS connector_id,
60
60
  json_extract(event, '$.channel_id') AS channel_id,
61
61
  json_extract(event, '$.payload') AS payload
62
- FROM main.leuco_log`;
62
+ FROM main.logs`;
63
63
  const processedViewSql = `CREATE TEMP VIEW processed AS SELECT
64
64
  seq,
65
65
  ts,
@@ -69,7 +69,7 @@ const processedViewSql = `CREATE TEMP VIEW processed AS SELECT
69
69
  json_extract(event, '$.channel_id') AS channel_id,
70
70
  json_extract(event, '$.outcome') AS outcome,
71
71
  json_extract(event, '$.payload') AS payload
72
- FROM processeddb.leuco_log`;
72
+ FROM processeddb.logs`;
73
73
  const connectionViewSql = `CREATE TEMP VIEW connection AS SELECT
74
74
  seq,
75
75
  ts,
@@ -78,6 +78,6 @@ const connectionViewSql = `CREATE TEMP VIEW connection AS SELECT
78
78
  json_extract(event, '$.channel_id') AS channel_id,
79
79
  json_extract(event, '$.status') AS status,
80
80
  json_extract(event, '$.detail') AS detail
81
- FROM connectiondb.leuco_log`;
81
+ FROM connectiondb.logs`;
82
82
  //#endregion
83
83
  export { ConnectorDiagnosticSqlReader as t };
@@ -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-K-wON25Y.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-DpXOsCty.js";
2
2
  export { ChannelDiagnosis, DiagnoseAllReport, DiagnosisStatus, DiagnosticConnectionError, DiagnosticEvent, DiagnosticsChannelSource, DiagnosticsGatewayProbe, DiagnosticsPublisher, DiagnosticsTokenReader, FunnelDiagnostics, ReplayResult, previewOf, queryRows, toDiagnosticConnectionError, toDiagnosticEvent };
@@ -1,2 +1,2 @@
1
- import { a as toDiagnosticEvent, i as toDiagnosticConnectionError, n as previewOf, r as queryRows, t as FunnelDiagnostics } from "./funnel-diagnostics-BpKYrMSu.js";
1
+ import { a as toDiagnosticEvent, i as toDiagnosticConnectionError, n as previewOf, r as queryRows, t as FunnelDiagnostics } from "./funnel-diagnostics-CSiJmPlZ.js";
2
2
  export { FunnelDiagnostics, previewOf, queryRows, toDiagnosticConnectionError, toDiagnosticEvent };
@@ -1,31 +1,10 @@
1
- import { t as FunnelConnectorAdapter } from "./connector-adapter-qwXLjQId.js";
2
- import { t as resolveConnectorToken } from "./resolve-connector-token-CczqG_Ig.js";
3
- import { t as FunnelConnectorListener } from "./connector-listener-CpHBecCj.js";
1
+ import { t as discordConnectorSchema } from "./discord-connector-schema-B_N6IXLz.js";
2
+ import { t as NodeFunnelHttpClient } from "./node-http-client-lowp60Oa.js";
3
+ import { t as FunnelConnectorAdapter } from "./connector-adapter-DU9Rvyec.js";
4
+ import { t as FunnelConnectorListener } from "./connector-listener-DR3aKOuK.js";
5
+ import { t as errorMessageOf } from "./error-message-of-Byi4y0Uf.js";
6
+ import { n as resolveConnectorToken, t as slotFields } from "./slot-fields-CMoRpwuy.js";
4
7
  import { Client, GatewayIntentBits, Partials } from "discord.js";
5
- //#region lib/engine/http/http-client.ts
6
- var FunnelHttpClient = class {};
7
- //#endregion
8
- //#region lib/engine/http/node-http-client.ts
9
- var NodeFunnelHttpClient = class extends FunnelHttpClient {
10
- constructor() {
11
- super();
12
- Object.freeze(this);
13
- }
14
- async fetch(request) {
15
- const res = await globalThis.fetch(request.url, {
16
- method: request.method,
17
- headers: request.headers,
18
- body: request.body
19
- });
20
- return {
21
- status: res.status,
22
- ok: res.ok,
23
- text: () => res.text(),
24
- json: () => res.json()
25
- };
26
- }
27
- };
28
- //#endregion
29
8
  //#region lib/engine/connectors/discord-adapter.ts
30
9
  const DISCORD_API_BASE = "https://discord.com/api/v10";
31
10
  const defaultHttp = new NodeFunnelHttpClient();
@@ -145,7 +124,7 @@ var FunnelDiscordListener = class extends FunnelConnectorListener {
145
124
  await notify(result.content, result.meta);
146
125
  } catch (error) {
147
126
  this.recordProcessed(eventId, rawEvent, "emitted:delivery-failed", result.content);
148
- this.logger?.error("discord notify error", { error: messageOf(error) });
127
+ this.logger?.error("discord notify error", { error: errorMessageOf(error) });
149
128
  return;
150
129
  }
151
130
  this.recordProcessed(eventId, rawEvent, "emitted", result.content);
@@ -158,8 +137,8 @@ var FunnelDiscordListener = class extends FunnelConnectorListener {
158
137
  });
159
138
  });
160
139
  client.on("error", (error) => {
161
- this.recordConnection("error", messageOf(error));
162
- this.logger?.error("discord client error", { error: messageOf(error) });
140
+ this.recordConnection("error", errorMessageOf(error));
141
+ this.logger?.error("discord client error", { error: errorMessageOf(error) });
163
142
  });
164
143
  try {
165
144
  await client.login(resolveConnectorToken({
@@ -169,7 +148,7 @@ var FunnelDiscordListener = class extends FunnelConnectorListener {
169
148
  label: `${this.config.name}.botToken`
170
149
  }));
171
150
  } catch (error) {
172
- this.recordConnection("error", messageOf(error));
151
+ this.recordConnection("error", errorMessageOf(error));
173
152
  throw error;
174
153
  }
175
154
  this.client = client;
@@ -181,8 +160,8 @@ var FunnelDiscordListener = class extends FunnelConnectorListener {
181
160
  await this.client.destroy();
182
161
  this.recordConnection("disconnected", "");
183
162
  } catch (error) {
184
- this.recordConnection("error", messageOf(error));
185
- this.logger?.error("discord stop error", { error: messageOf(error) });
163
+ this.recordConnection("error", errorMessageOf(error));
164
+ this.logger?.error("discord stop error", { error: errorMessageOf(error) });
186
165
  } finally {
187
166
  this.client = null;
188
167
  this.recordConnection("stopped", "");
@@ -220,8 +199,52 @@ var FunnelDiscordListener = class extends FunnelConnectorListener {
220
199
  });
221
200
  }
222
201
  };
223
- const messageOf = (error) => {
224
- return error instanceof Error ? error.message : String(error);
225
- };
226
202
  //#endregion
227
- export { FunnelHttpClient as a, NodeFunnelHttpClient as i, FunnelDiscordEventProcessor as n, FunnelDiscordAdapter as r, FunnelDiscordListener as t };
203
+ //#region lib/engine/connectors/discord-connector.ts
204
+ /**
205
+ * Discord connector descriptor. Pass `discordConnector()` to
206
+ * `new Funnel({ connectors: [...] })` to enable the type.
207
+ */
208
+ const discordConnector = () => ({
209
+ type: "discord",
210
+ toolExposed: true,
211
+ createListener(config, deps) {
212
+ return new FunnelDiscordListener({
213
+ config: discordConnectorSchema.parse(config),
214
+ channelId: deps.channelId,
215
+ logger: deps.logger,
216
+ diagnosticLog: deps.diagnosticLog
217
+ });
218
+ },
219
+ createAdapter(config) {
220
+ return new FunnelDiscordAdapter({ config: discordConnectorSchema.parse(config) });
221
+ },
222
+ secretTokens(config) {
223
+ return [discordConnectorSchema.parse(config).botToken].filter((token) => token !== void 0);
224
+ },
225
+ buildConfig(input, context) {
226
+ return discordConnectorSchema.parse({
227
+ id: context.id,
228
+ type: "discord",
229
+ name: input.name,
230
+ ...typeof input.botToken === "string" ? { botToken: input.botToken } : {},
231
+ ...typeof input.botTokenEnv === "string" ? { botTokenEnv: input.botTokenEnv } : {},
232
+ createdAt: context.now,
233
+ updatedAt: context.now
234
+ });
235
+ },
236
+ applyUpdate(config, fields, context) {
237
+ const current = discordConnectorSchema.parse(config);
238
+ return discordConnectorSchema.parse({
239
+ id: current.id,
240
+ name: current.name,
241
+ type: "discord",
242
+ createdAt: current.createdAt,
243
+ updatedAt: context.now,
244
+ ...slotFields("botToken", "botTokenEnv", fields, current)
245
+ });
246
+ },
247
+ operations: {}
248
+ });
249
+ //#endregion
250
+ export { FunnelDiscordAdapter as i, FunnelDiscordListener as n, FunnelDiscordEventProcessor as r, discordConnector as t };
package/dist/docs.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { n as FunnelDocs, t as DocsTopicListing } from "./funnel-docs-DYBs1-H_.js";
1
+ import { n as FunnelDocs, t as DocsTopicListing } from "./funnel-docs-CNklHvbt.js";
2
2
  export { DocsTopicListing, FunnelDocs };
package/dist/docs.js CHANGED
@@ -1,2 +1,2 @@
1
- import { t as FunnelDocs } from "./funnel-docs-ng5K8w4j.js";
1
+ import { t as FunnelDocs } from "./funnel-docs-BxXZ9Ksx.js";
2
2
  export { 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-vxO96TCA.js";
1
+ import { n as DoctorReport, r as FunnelDoctor, t as DoctorFixMode } from "./funnel-doctor-CZf_0Luq.js";
2
2
  export { DoctorFixMode, DoctorReport, FunnelDoctor };
package/dist/doctor.js CHANGED
@@ -1,2 +1,2 @@
1
- import { t as FunnelDoctor } from "./funnel-doctor-CApCezTq.js";
1
+ import { t as FunnelDoctor } from "./funnel-doctor-DiJCjHsg.js";
2
2
  export { FunnelDoctor };
@@ -0,0 +1,9 @@
1
+ //#region lib/engine/error/error-message-of.ts
2
+ /**
3
+ * Normalize an unknown thrown value into a loggable message string.
4
+ */
5
+ const errorMessageOf = (error) => {
6
+ return error instanceof Error ? error.message : String(error);
7
+ };
8
+ //#endregion
9
+ export { errorMessageOf as t };
@@ -1,8 +1,7 @@
1
- import { t as ChannelConfig } from "./settings-schema-1hh11jnN.js";
2
- import { n as FunnelIdGenerator } from "./settings-reader-DPwqOVUm.js";
3
- import { S as FunnelLogger } from "./diagnostic-log-Cb3v8P7p.js";
4
- import { r as FunnelProcessRunner } from "./process-runner-DIm1cy95.js";
5
- import { n as FunnelFileSystem } from "./file-system-DxpnnUVb.js";
1
+ import { t as ChannelConfig } from "./settings-schema-D1xcOqRu.js";
2
+ import { n as FunnelIdGenerator } from "./settings-reader-BIFB_j2f.js";
3
+ import { B as FunnelLogger, I as FunnelProcessRunner } from "./connector-descriptor-6SXJoszo.js";
4
+ import { n as FunnelFileSystem } from "./file-system-o51IsM0W.js";
6
5
 
7
6
  //#region lib/engine/claude/channel-resolver.d.ts
8
7
  type ChannelResolver = {
@@ -1,4 +1,5 @@
1
- import { t as ConnectorDiagnosticSqlReader } from "./diagnostic-sql-reader-CzYgZpq2.js";
1
+ import { t as gatewayLoopbackUrl } from "./gateway-base-url-Dy4Ykuoh.js";
2
+ import { t as ConnectorDiagnosticSqlReader } from "./diagnostic-sql-reader-C9zR-Csp.js";
2
3
  import { join } from "node:path";
3
4
  import { existsSync } from "node:fs";
4
5
  //#region lib/services/diagnostics/diagnostic-event.ts
@@ -103,6 +104,8 @@ const buildDiagnosis = (report) => {
103
104
  nextActions: ["fnl gateway logs"],
104
105
  rootCause
105
106
  };
107
+ const slackEventGap = diagnoseSlackEventSubscriptionGap(report);
108
+ if (slackEventGap !== null) return slackEventGap;
106
109
  return {
107
110
  status: "ok",
108
111
  message: "everything looks healthy",
@@ -110,6 +113,20 @@ const buildDiagnosis = (report) => {
110
113
  rootCause: null
111
114
  };
112
115
  };
116
+ const diagnoseSlackEventSubscriptionGap = (report) => {
117
+ if (!report.listeners.some((listener) => listener.type === "slack") || report.recentEvents.length === 0) return null;
118
+ const slackEvents = report.recentEvents.filter((event) => event.type === "slack").map((event) => event.payloadParsed?.type);
119
+ if (slackEvents.length === 0) return null;
120
+ const sawAppMention = slackEvents.includes("app_mention");
121
+ const sawMessage = slackEvents.includes("message");
122
+ if (!sawAppMention || sawMessage) return null;
123
+ return {
124
+ status: "warn",
125
+ message: "Slack is only delivering app_mention events; unmentioned thread replies may not arrive",
126
+ nextActions: ["Add Slack bot events: message.channels, message.groups, message.im, message.mpim; reinstall the app; then restart the gateway"],
127
+ rootCause: "Slack Event Subscriptions likely omit message.* events"
128
+ };
129
+ };
113
130
  /**
114
131
  * Programmable diagnostics surface — used by both the CLI (fnl debug …) and
115
132
  * the MCP tools (fnl_debug, fnl_recent_events, …). Pure read-side, no
@@ -247,7 +264,7 @@ var FunnelDiagnostics = class {
247
264
  if (!gatewayStatus.running) return null;
248
265
  const token = this.props.gatewayToken.read();
249
266
  const headers = token ? { Authorization: `Bearer ${token}` } : {};
250
- const res = await fetch(`http://127.0.0.1:${gatewayStatus.port}/status`, { headers }).catch(() => null);
267
+ const res = await fetch(`${gatewayLoopbackUrl(gatewayStatus.port)}/status`, { headers }).catch(() => null);
251
268
  if (!res || !res.ok) return null;
252
269
  const body = await res.json();
253
270
  return isGatewayStatusResponse(body) ? body : null;
@@ -1,6 +1,6 @@
1
- import { t as ChannelConfig } from "./settings-schema-1hh11jnN.js";
1
+ import { t as ChannelConfig } from "./settings-schema-D1xcOqRu.js";
2
2
 
3
- //#region lib/gateway/diagnostic-log/diagnostic-sql-reader.d.ts
3
+ //#region lib/engine/diagnostic-log/diagnostic-sql-reader.d.ts
4
4
  type Props$1 = {
5
5
  /** SQLite file holding the raw (pre-filter) table. */rawPath: string; /** SQLite file holding the processed (verdict) table. */
6
6
  processedPath: string; /** SQLite file holding the connection (lifecycle) table. */
@@ -11,7 +11,7 @@ type Row = Record<string, unknown>;
11
11
  * Read-only SQL surface over the three diagnostic tables, for Claude to query
12
12
  * the log with arbitrary `SELECT`s. It opens all files read-only and exposes
13
13
  * three views — `raw`, `processed`, `connection` — that hide the storage
14
- * details (the physical table is `leuco_log` and each row's columns live
14
+ * details (the physical table is `logs` and each row's columns live
15
15
  * inside a JSON `event` blob): the views surface the columns as plain fields,
16
16
  * with `payload` already pulled out of the nested JSON.
17
17
  *