@mono-agent/agent-app 0.3.0 → 0.4.1

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 (186) hide show
  1. package/README.md +24 -10
  2. package/dist/adapter-send-tools-main.d.ts +3 -0
  3. package/dist/adapter-send-tools-main.d.ts.map +1 -0
  4. package/dist/adapter-send-tools-main.js +20 -0
  5. package/dist/adapter-send-tools-main.js.map +1 -0
  6. package/dist/adapter-send-tools.d.ts +125 -0
  7. package/dist/adapter-send-tools.d.ts.map +1 -0
  8. package/dist/adapter-send-tools.js +629 -0
  9. package/dist/adapter-send-tools.js.map +1 -0
  10. package/dist/app-config.d.ts +86 -26
  11. package/dist/app-config.d.ts.map +1 -1
  12. package/dist/app-config.js +284 -90
  13. package/dist/app-config.js.map +1 -1
  14. package/dist/app.d.ts +51 -20
  15. package/dist/app.d.ts.map +1 -1
  16. package/dist/app.js +889 -119
  17. package/dist/app.js.map +1 -1
  18. package/dist/artifact-retention.d.ts +32 -0
  19. package/dist/artifact-retention.d.ts.map +1 -0
  20. package/dist/artifact-retention.js +111 -0
  21. package/dist/artifact-retention.js.map +1 -0
  22. package/dist/audit-runs.d.ts +12 -0
  23. package/dist/audit-runs.d.ts.map +1 -0
  24. package/dist/audit-runs.js +68 -0
  25. package/dist/audit-runs.js.map +1 -0
  26. package/dist/backfill.d.ts +75 -0
  27. package/dist/backfill.d.ts.map +1 -0
  28. package/dist/backfill.js +350 -0
  29. package/dist/backfill.js.map +1 -0
  30. package/dist/background.d.ts +8 -5
  31. package/dist/background.d.ts.map +1 -1
  32. package/dist/background.js +279 -53
  33. package/dist/background.js.map +1 -1
  34. package/dist/broadcast-recorder.d.ts +27 -0
  35. package/dist/broadcast-recorder.d.ts.map +1 -0
  36. package/dist/broadcast-recorder.js +82 -0
  37. package/dist/broadcast-recorder.js.map +1 -0
  38. package/dist/channel-config-view.d.ts +28 -0
  39. package/dist/channel-config-view.d.ts.map +1 -0
  40. package/dist/channel-config-view.js +79 -0
  41. package/dist/channel-config-view.js.map +1 -0
  42. package/dist/channel-gate.d.ts +22 -0
  43. package/dist/channel-gate.d.ts.map +1 -0
  44. package/dist/channel-gate.js +35 -0
  45. package/dist/channel-gate.js.map +1 -0
  46. package/dist/channel-plugins.d.ts +39 -0
  47. package/dist/channel-plugins.d.ts.map +1 -0
  48. package/dist/channel-plugins.js +346 -0
  49. package/dist/channel-plugins.js.map +1 -0
  50. package/dist/channels.d.ts +62 -66
  51. package/dist/channels.d.ts.map +1 -1
  52. package/dist/channels.js +850 -120
  53. package/dist/channels.js.map +1 -1
  54. package/dist/cli.d.ts +103 -4
  55. package/dist/cli.d.ts.map +1 -1
  56. package/dist/cli.js +984 -112
  57. package/dist/cli.js.map +1 -1
  58. package/dist/config-reference.d.ts +24 -0
  59. package/dist/config-reference.d.ts.map +1 -0
  60. package/dist/config-reference.js +580 -0
  61. package/dist/config-reference.js.map +1 -0
  62. package/dist/configured-agent.d.ts +104 -0
  63. package/dist/configured-agent.d.ts.map +1 -0
  64. package/dist/configured-agent.js +578 -0
  65. package/dist/configured-agent.js.map +1 -0
  66. package/dist/consumer-contract.d.ts +36 -0
  67. package/dist/consumer-contract.d.ts.map +1 -0
  68. package/dist/consumer-contract.js +290 -0
  69. package/dist/consumer-contract.js.map +1 -0
  70. package/dist/doctor.d.ts +17 -0
  71. package/dist/doctor.d.ts.map +1 -1
  72. package/dist/doctor.js +392 -95
  73. package/dist/doctor.js.map +1 -1
  74. package/dist/index.d.ts +14 -6
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +7 -3
  77. package/dist/index.js.map +1 -1
  78. package/dist/init.d.ts +23 -7
  79. package/dist/init.d.ts.map +1 -1
  80. package/dist/init.js +91 -26
  81. package/dist/init.js.map +1 -1
  82. package/dist/install-skill.d.ts +1 -1
  83. package/dist/install-skill.js +2 -2
  84. package/dist/install-skill.js.map +1 -1
  85. package/dist/interaction-bridge.d.ts +72 -0
  86. package/dist/interaction-bridge.d.ts.map +1 -0
  87. package/dist/interaction-bridge.js +354 -0
  88. package/dist/interaction-bridge.js.map +1 -0
  89. package/dist/launchd.d.ts +0 -2
  90. package/dist/launchd.d.ts.map +1 -1
  91. package/dist/launchd.js +0 -2
  92. package/dist/launchd.js.map +1 -1
  93. package/dist/memory-command.d.ts +10 -0
  94. package/dist/memory-command.d.ts.map +1 -0
  95. package/dist/memory-command.js +649 -0
  96. package/dist/memory-command.js.map +1 -0
  97. package/dist/memory-recall-main.d.ts +3 -0
  98. package/dist/memory-recall-main.d.ts.map +1 -0
  99. package/dist/memory-recall-main.js +30 -0
  100. package/dist/memory-recall-main.js.map +1 -0
  101. package/dist/memory-recall.d.ts +147 -0
  102. package/dist/memory-recall.d.ts.map +1 -0
  103. package/dist/memory-recall.js +345 -0
  104. package/dist/memory-recall.js.map +1 -0
  105. package/dist/memory-rituals.d.ts +8 -11
  106. package/dist/memory-rituals.d.ts.map +1 -1
  107. package/dist/memory-rituals.js +26 -25
  108. package/dist/memory-rituals.js.map +1 -1
  109. package/dist/metrics.d.ts +13 -0
  110. package/dist/metrics.d.ts.map +1 -0
  111. package/dist/metrics.js +105 -0
  112. package/dist/metrics.js.map +1 -0
  113. package/dist/notify-destinations.d.ts +23 -0
  114. package/dist/notify-destinations.d.ts.map +1 -0
  115. package/dist/notify-destinations.js +79 -0
  116. package/dist/notify-destinations.js.map +1 -0
  117. package/dist/posted-message-index.d.ts +61 -0
  118. package/dist/posted-message-index.d.ts.map +1 -0
  119. package/dist/posted-message-index.js +178 -0
  120. package/dist/posted-message-index.js.map +1 -0
  121. package/dist/proactive-notify.d.ts +32 -0
  122. package/dist/proactive-notify.d.ts.map +1 -0
  123. package/dist/proactive-notify.js +62 -0
  124. package/dist/proactive-notify.js.map +1 -0
  125. package/dist/recipes/base.d.ts +15 -0
  126. package/dist/recipes/base.d.ts.map +1 -0
  127. package/dist/recipes/base.js +51 -0
  128. package/dist/recipes/base.js.map +1 -0
  129. package/dist/recipes/catalog.d.ts +4 -0
  130. package/dist/recipes/catalog.d.ts.map +1 -0
  131. package/dist/recipes/catalog.js +509 -0
  132. package/dist/recipes/catalog.js.map +1 -0
  133. package/dist/recipes/index.d.ts +11 -0
  134. package/dist/recipes/index.d.ts.map +1 -0
  135. package/dist/recipes/index.js +14 -0
  136. package/dist/recipes/index.js.map +1 -0
  137. package/dist/recipes/types.d.ts +70 -0
  138. package/dist/recipes/types.d.ts.map +1 -0
  139. package/dist/recipes/types.js +15 -0
  140. package/dist/recipes/types.js.map +1 -0
  141. package/dist/request-model-override.d.ts +67 -0
  142. package/dist/request-model-override.d.ts.map +1 -0
  143. package/dist/request-model-override.js +96 -0
  144. package/dist/request-model-override.js.map +1 -0
  145. package/dist/runs-health.d.ts +22 -0
  146. package/dist/runs-health.d.ts.map +1 -0
  147. package/dist/runs-health.js +144 -0
  148. package/dist/runs-health.js.map +1 -0
  149. package/dist/seen-conversations.d.ts +22 -0
  150. package/dist/seen-conversations.d.ts.map +1 -0
  151. package/dist/seen-conversations.js +120 -0
  152. package/dist/seen-conversations.js.map +1 -0
  153. package/dist/sessions.d.ts +21 -0
  154. package/dist/sessions.d.ts.map +1 -0
  155. package/dist/sessions.js +45 -0
  156. package/dist/sessions.js.map +1 -0
  157. package/dist/setup.d.ts +29 -0
  158. package/dist/setup.d.ts.map +1 -0
  159. package/dist/setup.js +97 -0
  160. package/dist/setup.js.map +1 -0
  161. package/dist/trigger-overrides.d.ts +17 -0
  162. package/dist/trigger-overrides.d.ts.map +1 -0
  163. package/dist/trigger-overrides.js +30 -0
  164. package/dist/trigger-overrides.js.map +1 -0
  165. package/dist/tui-command.d.ts +56 -0
  166. package/dist/tui-command.d.ts.map +1 -0
  167. package/dist/tui-command.js +160 -0
  168. package/dist/tui-command.js.map +1 -0
  169. package/dist/ui.d.ts +54 -0
  170. package/dist/ui.d.ts.map +1 -0
  171. package/dist/ui.js +157 -0
  172. package/dist/ui.js.map +1 -0
  173. package/dist/web-command.d.ts +39 -0
  174. package/dist/web-command.d.ts.map +1 -0
  175. package/dist/web-command.js +151 -0
  176. package/dist/web-command.js.map +1 -0
  177. package/package.json +22 -17
  178. package/schema/mono-agent.config.schema.json +1686 -0
  179. package/skills/mono-agent-composer/SKILL.md +38 -12
  180. package/skills/mono-agent-composer/agents/openai.yaml +4 -0
  181. package/skills/mono-agent-composer/references/config-blueprint.md +84 -31
  182. package/skills/mono-agent-composer/references/discovery-questions.md +49 -36
  183. package/skills/mono-agent-composer/references/feature-coverage.md +26 -19
  184. package/skills/mono-agent-composer/references/package-map.md +20 -22
  185. package/skills/mono-agent-composer/references/playbooks.md +209 -0
  186. package/skills/mono-agent-composer/references/validation.md +14 -5
@@ -0,0 +1,82 @@
1
+ import { LIVE_EVENT_SCHEMA } from "@mono-agent/agent-contracts";
2
+ import { redactJsonValue } from "@mono-agent/observability";
3
+ /**
4
+ * Wrap a {@link RunRecorder} so every run start/event/finish is ALSO published to
5
+ * a {@link RunEventSink} (the in-process live bus), giving operator surfaces
6
+ * sub-run visibility the on-disk recorder can't (it flushes only at start/finish).
7
+ *
8
+ * Broadcast is best-effort and additive, exactly like the Phoenix exporter path:
9
+ * the inner recorder is the source of truth for the returned summary, and a
10
+ * publish failure never changes the run outcome (publish is wrapped and the bus's
11
+ * own `publish` is contracted never to throw). `seq` is left 0 here — the bus
12
+ * stamps a process-wide monotonic value on publish.
13
+ */
14
+ export function createBroadcastRunRecorder(inner, sink, ctx) {
15
+ let eventIndex = 0;
16
+ const publish = (frame) => {
17
+ try {
18
+ sink.publish(frame);
19
+ }
20
+ catch {
21
+ // Broadcast is best-effort; a sink failure must never break the run.
22
+ }
23
+ };
24
+ const finished = async (summary) => {
25
+ const redactedSummary = redactJsonValue(summary);
26
+ publish({
27
+ t: "run_finished",
28
+ schema: LIVE_EVENT_SCHEMA,
29
+ sourceId: ctx.sourceId,
30
+ runId: ctx.runId,
31
+ status: redactedSummary.status,
32
+ summary: redactedSummary,
33
+ seq: 0,
34
+ });
35
+ return summary;
36
+ };
37
+ const recorder = {
38
+ onEvent(event) {
39
+ inner.onEvent(event);
40
+ const redactedEvent = redactJsonValue(event);
41
+ publish({
42
+ t: "event",
43
+ schema: LIVE_EVENT_SCHEMA,
44
+ sourceId: ctx.sourceId,
45
+ runId: ctx.runId,
46
+ eventIndex: eventIndex++,
47
+ event: redactedEvent,
48
+ seq: 0,
49
+ });
50
+ },
51
+ async finish(result) {
52
+ return await finished(await inner.finish(result));
53
+ },
54
+ async fail(error) {
55
+ return await finished(await inner.fail(error));
56
+ },
57
+ };
58
+ // `start` is optional on RunRecorder — only expose (and emit run_started) when
59
+ // the inner recorder has one, so callers that probe `recorder.start?.()` behave
60
+ // identically to the unwrapped recorder.
61
+ if (inner.start !== undefined) {
62
+ const innerStart = inner.start.bind(inner);
63
+ recorder.start = async () => {
64
+ const summary = await innerStart();
65
+ publish({
66
+ t: "run_started",
67
+ schema: LIVE_EVENT_SCHEMA,
68
+ sourceId: ctx.sourceId,
69
+ ...(ctx.sourceLabel === undefined ? {} : { sourceLabel: ctx.sourceLabel }),
70
+ runId: ctx.runId,
71
+ conversationId: ctx.conversationId,
72
+ ...(ctx.source === undefined ? {} : { source: ctx.source }),
73
+ ...(ctx.sourceDetail === undefined ? {} : { sourceDetail: ctx.sourceDetail }),
74
+ startedAt: summary.startedAt ?? new Date().toISOString(),
75
+ seq: 0,
76
+ });
77
+ return summary;
78
+ };
79
+ }
80
+ return recorder;
81
+ }
82
+ //# sourceMappingURL=broadcast-recorder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"broadcast-recorder.js","sourceRoot":"","sources":["../src/broadcast-recorder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { ConfigViewSection } from "@mono-agent/config";
2
+ import type { JsonEnvFieldSpec } from "@mono-agent/agent-contracts";
3
+ import type { MonoAgentAppConfigInput } from "./app-config.js";
4
+ import type { ChannelDriver } from "./channels.js";
5
+ /**
6
+ * The driver facts a channel config view needs: enough to resolve the section
7
+ * status through the channel's own loader without starting anything.
8
+ */
9
+ type ChannelViewDriver = Pick<ChannelDriver, "id" | "label" | "loadConfig" | "isConfigError" | "disabledReason">;
10
+ /**
11
+ * Compose one channel's source-annotated config section from the adapter's
12
+ * exported field registry — the channel counterpart of the core
13
+ * `buildMonoAgentConfigView`. Field sources resolve with the loader's own
14
+ * layering semantics (a set env var wins, then a JSON value the layer function
15
+ * would encode, else the default), so the view can never disagree with what the
16
+ * adapter actually reads. Secret fields are never printed — only set/unset.
17
+ */
18
+ export declare function buildChannelConfigView(driver: ChannelViewDriver, fields: readonly JsonEnvFieldSpec[], input: MonoAgentAppConfigInput, options?: {
19
+ readonly jsonKey?: string;
20
+ }): Promise<ConfigViewSection>;
21
+ /**
22
+ * Collect every driver's config view section. Advisory surface: a channel
23
+ * whose view fails to compose is skipped — its validate section reports the
24
+ * load problem loudly instead.
25
+ */
26
+ export declare function collectChannelConfigViews(drivers: readonly ChannelDriver[], input: MonoAgentAppConfigInput): Promise<readonly ConfigViewSection[]>;
27
+ export {};
28
+ //# sourceMappingURL=channel-config-view.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel-config-view.d.ts","sourceRoot":"","sources":["../src/channel-config-view.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAmB,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE7E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAEpE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAInD;;;GAGG;AACH,KAAK,iBAAiB,GAAG,IAAI,CAAC,aAAa,EAAE,IAAI,GAAG,OAAO,GAAG,YAAY,GAAG,eAAe,GAAG,gBAAgB,CAAC,CAAC;AAEjH;;;;;;;GAOG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,iBAAiB,EACzB,MAAM,EAAE,SAAS,gBAAgB,EAAE,EACnC,KAAK,EAAE,uBAAuB,EAC9B,OAAO,GAAE;IAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GAC1C,OAAO,CAAC,iBAAiB,CAAC,CAwB5B;AAqBD;;;;GAIG;AACH,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,SAAS,aAAa,EAAE,EACjC,KAAK,EAAE,uBAAuB,GAC7B,OAAO,CAAC,SAAS,iBAAiB,EAAE,CAAC,CAavC"}
@@ -0,0 +1,79 @@
1
+ import { encodeJsonEnvValue, normalizeOptionalString, readJsonSection, readSettingsJson } from "@mono-agent/agent-contracts";
2
+ const PLACEHOLDER = "—";
3
+ /**
4
+ * Compose one channel's source-annotated config section from the adapter's
5
+ * exported field registry — the channel counterpart of the core
6
+ * `buildMonoAgentConfigView`. Field sources resolve with the loader's own
7
+ * layering semantics (a set env var wins, then a JSON value the layer function
8
+ * would encode, else the default), so the view can never disagree with what the
9
+ * adapter actually reads. Secret fields are never printed — only set/unset.
10
+ */
11
+ export async function buildChannelConfigView(driver, fields, input, options = {}) {
12
+ const { json } = await readSettingsJson(input.configPath);
13
+ const section = readJsonSection(json, options.jsonKey ?? driver.id);
14
+ // An explicitly disabled channel reports `disabled`; an enabled channel —
15
+ // even one still waiting for credentials (typed config error) — is `active`.
16
+ let status = "active";
17
+ try {
18
+ const config = await driver.loadConfig(input);
19
+ if (driver.disabledReason?.(config) !== undefined) {
20
+ status = "disabled";
21
+ }
22
+ }
23
+ catch (error) {
24
+ if (!driver.isConfigError(error)) {
25
+ throw error;
26
+ }
27
+ }
28
+ return {
29
+ id: driver.id,
30
+ label: driver.label,
31
+ status,
32
+ fields: fields.map((field) => toChannelViewField(field, section, input.env)),
33
+ };
34
+ }
35
+ function toChannelViewField(field, section, env) {
36
+ const envValue = normalizeOptionalString(env[field.env]);
37
+ const jsonValue = encodeJsonEnvValue(field.fromJson(section), field.kind ?? "string");
38
+ const resolved = envValue ?? jsonValue;
39
+ const source = envValue !== undefined ? "env" : jsonValue !== undefined ? "json" : "default";
40
+ return {
41
+ id: field.id,
42
+ label: labelForFieldId(field.id),
43
+ value: field.secret === true ? (resolved === undefined ? "unset" : "set") : resolved ?? PLACEHOLDER,
44
+ source,
45
+ ...(field.secret === true ? { redacted: true } : {}),
46
+ envKey: field.env,
47
+ };
48
+ }
49
+ /**
50
+ * Collect every driver's config view section. Advisory surface: a channel
51
+ * whose view fails to compose is skipped — its validate section reports the
52
+ * load problem loudly instead.
53
+ */
54
+ export async function collectChannelConfigViews(drivers, input) {
55
+ const sections = [];
56
+ for (const driver of drivers) {
57
+ if (driver.configView === undefined) {
58
+ continue;
59
+ }
60
+ try {
61
+ sections.push(await driver.configView(input));
62
+ }
63
+ catch {
64
+ // Skip: the channel's own validate section owns error reporting.
65
+ }
66
+ }
67
+ return sections;
68
+ }
69
+ /** `a2a.provider.bearerToken` → "Provider bearer token". */
70
+ function labelForFieldId(id) {
71
+ const words = id
72
+ .split(".")
73
+ .slice(1)
74
+ .join(" ")
75
+ .replace(/([a-z0-9])([A-Z])/gu, "$1 $2")
76
+ .toLowerCase();
77
+ return words.charAt(0).toUpperCase() + words.slice(1);
78
+ }
79
+ //# sourceMappingURL=channel-config-view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel-config-view.js","sourceRoot":"","sources":["../src/channel-config-view.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { MonoAgentAppConfigInput } from "./app-config.js";
2
+ /** What marks a channel as "the operator expressed intent" for the lazy-load gate. */
3
+ export interface ChannelGateSpec {
4
+ /** Top-level key of the channel's section in mono-agent.config.json. */
5
+ readonly jsonKey: string;
6
+ /** Env prefix: any set `MONO_AGENT_<X>_*` var marks the channel configured. */
7
+ readonly envPrefix: string;
8
+ /** Folder (relative to cwd) whose existence marks the channel configured (cron/, webhook/). */
9
+ readonly dir?: string;
10
+ }
11
+ /**
12
+ * True when the operator expressed ANY intent for the channel: its JSON section
13
+ * exists (even with `enabled: false` — the real loader must still parse it so a
14
+ * malformed disabled section keeps erroring exactly as before), any env var
15
+ * with the channel's prefix is set, or its jobs/endpoints folder exists.
16
+ *
17
+ * False is the lazy-load fast path: the adapter module is never imported and
18
+ * the driver answers with the loader's own empty-input output (drift-guarded
19
+ * by tests) — so a webhook-only agent never loads the chat SDKs.
20
+ */
21
+ export declare function isChannelConfigured(input: MonoAgentAppConfigInput, spec: ChannelGateSpec): Promise<boolean>;
22
+ //# sourceMappingURL=channel-gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel-gate.d.ts","sourceRoot":"","sources":["../src/channel-gate.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAE/D,sFAAsF;AACtF,MAAM,WAAW,eAAe;IAC9B,wEAAwE;IACxE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,+EAA+E;IAC/E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,+FAA+F;IAC/F,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;GASG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,uBAAuB,EAC9B,IAAI,EAAE,eAAe,GACpB,OAAO,CAAC,OAAO,CAAC,CAmBlB"}
@@ -0,0 +1,35 @@
1
+ import { access } from "node:fs/promises";
2
+ import { resolve } from "node:path";
3
+ import { readSettingsJson } from "@mono-agent/agent-contracts";
4
+ /**
5
+ * True when the operator expressed ANY intent for the channel: its JSON section
6
+ * exists (even with `enabled: false` — the real loader must still parse it so a
7
+ * malformed disabled section keeps erroring exactly as before), any env var
8
+ * with the channel's prefix is set, or its jobs/endpoints folder exists.
9
+ *
10
+ * False is the lazy-load fast path: the adapter module is never imported and
11
+ * the driver answers with the loader's own empty-input output (drift-guarded
12
+ * by tests) — so a webhook-only agent never loads the chat SDKs.
13
+ */
14
+ export async function isChannelConfigured(input, spec) {
15
+ for (const [key, value] of Object.entries(input.env)) {
16
+ if (value !== undefined && key.startsWith(spec.envPrefix)) {
17
+ return true;
18
+ }
19
+ }
20
+ const { json } = await readSettingsJson(input.configPath);
21
+ if (json[spec.jsonKey] !== undefined) {
22
+ return true;
23
+ }
24
+ if (spec.dir !== undefined) {
25
+ try {
26
+ await access(resolve(input.cwd, spec.dir));
27
+ return true;
28
+ }
29
+ catch {
30
+ // folder absent → no intent from this signal
31
+ }
32
+ }
33
+ return false;
34
+ }
35
+ //# sourceMappingURL=channel-gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel-gate.js","sourceRoot":"","sources":["../src/channel-gate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAc/D;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,KAA8B,EAC9B,IAAqB;IAErB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACrD,IAAI,KAAK,KAAK,SAAS,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YAC1D,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,gBAAgB,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC1D,IAAK,IAAgC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,SAAS,EAAE,CAAC;QAClE,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,IAAI,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;YAC3C,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,6CAA6C;QAC/C,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,39 @@
1
+ import type { SettingsJsonValue } from "@mono-agent/agent-contracts";
2
+ import type { MonoAgentAppConfigInput } from "./app-config.js";
3
+ import type { ChannelGateSpec } from "./channel-gate.js";
4
+ import type { ChannelDriver } from "./channels.js";
5
+ export type ChannelPluginConfigErrorCode = "invalid_plugin_config" | "plugin_import_failed" | "malformed_plugin_export";
6
+ export interface ChannelPluginConfigErrorDetails {
7
+ readonly code?: ChannelPluginConfigErrorCode;
8
+ readonly packageName?: string;
9
+ readonly pluginId?: string;
10
+ readonly reason?: string;
11
+ }
12
+ export declare class ChannelPluginConfigError extends Error {
13
+ readonly code: ChannelPluginConfigErrorCode;
14
+ readonly details: ChannelPluginConfigErrorDetails;
15
+ constructor(code: ChannelPluginConfigErrorCode, message: string, details?: ChannelPluginConfigErrorDetails);
16
+ }
17
+ export interface ChannelPluginEntry {
18
+ readonly packageName: string;
19
+ readonly id?: string;
20
+ readonly label?: string;
21
+ readonly config?: RawPluginConfig;
22
+ }
23
+ type RawPluginConfig = Readonly<Record<string, SettingsJsonValue>>;
24
+ export interface ResolveConfiguredChannelPluginsOptions {
25
+ readonly reservedIds?: Iterable<string>;
26
+ }
27
+ interface ExternalPackageChannelDriverOptions {
28
+ readonly packageName: string;
29
+ readonly id: string;
30
+ readonly label: string;
31
+ readonly gate: ChannelGateSpec;
32
+ readonly unconfiguredConfig: unknown;
33
+ readonly unconfiguredDisabledReason: string;
34
+ readonly factoryOptions?: Record<string, unknown>;
35
+ }
36
+ export declare function resolveConfiguredChannelPlugins(input: MonoAgentAppConfigInput, options?: ResolveConfiguredChannelPluginsOptions): Promise<readonly ChannelDriver[]>;
37
+ export declare function createExternalPackageChannelDriver(options: ExternalPackageChannelDriverOptions): ChannelDriver;
38
+ export {};
39
+ //# sourceMappingURL=channel-plugins.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel-plugins.d.ts","sourceRoot":"","sources":["../src/channel-plugins.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAA0C,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAE7G,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAE/D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,MAAM,MAAM,4BAA4B,GACpC,uBAAuB,GACvB,sBAAsB,GACtB,yBAAyB,CAAC;AAE9B,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,IAAI,CAAC,EAAE,4BAA4B,CAAC;IAC7C,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,QAAQ,CAAC,IAAI,EAAE,4BAA4B,CAAC;IAC5C,QAAQ,CAAC,OAAO,EAAE,+BAA+B,CAAC;gBAGhD,IAAI,EAAE,4BAA4B,EAClC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,+BAAoC;CAOhD;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,eAAe,CAAC;CACnC;AAED,KAAK,eAAe,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC;AAanE,MAAM,WAAW,sCAAsC;IACrD,QAAQ,CAAC,WAAW,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;CACzC;AAWD,UAAU,mCAAmC;IAC3C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,QAAQ,CAAC,kBAAkB,EAAE,OAAO,CAAC;IACrC,QAAQ,CAAC,0BAA0B,EAAE,MAAM,CAAC;IAC5C,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnD;AAED,wBAAsB,+BAA+B,CACnD,KAAK,EAAE,uBAAuB,EAC9B,OAAO,GAAE,sCAA2C,GACnD,OAAO,CAAC,SAAS,aAAa,EAAE,CAAC,CAQnC;AAED,wBAAgB,kCAAkC,CAChD,OAAO,EAAE,mCAAmC,GAC3C,aAAa,CAuDf"}
@@ -0,0 +1,346 @@
1
+ import { readSettingsJson } from "@mono-agent/agent-contracts";
2
+ import { isChannelConfigured } from "./channel-gate.js";
3
+ export class ChannelPluginConfigError extends Error {
4
+ code;
5
+ details;
6
+ constructor(code, message, details = {}) {
7
+ super(message);
8
+ this.name = "ChannelPluginConfigError";
9
+ this.code = code;
10
+ this.details = { ...details, code };
11
+ }
12
+ }
13
+ export async function resolveConfiguredChannelPlugins(input, options = {}) {
14
+ const entries = await readConfiguredChannelPluginEntries(input.configPath);
15
+ const checkedEntries = rejectChannelPluginIdCollisions(entries, options.reservedIds ?? []);
16
+ const drivers = [];
17
+ for (const entry of checkedEntries) {
18
+ drivers.push(await resolveChannelPlugin(entry));
19
+ }
20
+ return drivers;
21
+ }
22
+ export function createExternalPackageChannelDriver(options) {
23
+ let driver;
24
+ let loading;
25
+ const loadDriver = async () => {
26
+ loading ??= loadChannelPluginDriver({
27
+ packageName: options.packageName,
28
+ id: options.id,
29
+ label: options.label,
30
+ ...(options.factoryOptions === undefined ? {} : { factoryOptions: options.factoryOptions }),
31
+ });
32
+ try {
33
+ driver = await loading;
34
+ return driver;
35
+ }
36
+ catch (error) {
37
+ loading = undefined;
38
+ throw error;
39
+ }
40
+ };
41
+ return {
42
+ id: options.id,
43
+ label: options.label,
44
+ async configView(input) {
45
+ if (!(await isChannelConfigured(input, options.gate))) {
46
+ return { id: options.id, label: options.label, status: "disabled", fields: [] };
47
+ }
48
+ const resolved = await loadDriver();
49
+ return await fallbackConfigView(resolved, input);
50
+ },
51
+ async loadConfig(input) {
52
+ if (!(await isChannelConfigured(input, options.gate))) {
53
+ return options.unconfiguredConfig;
54
+ }
55
+ return await (await loadDriver()).loadConfig(input);
56
+ },
57
+ isConfigError(error) {
58
+ return error instanceof ChannelPluginConfigError || driver?.isConfigError(error) === true;
59
+ },
60
+ disabledReason(config) {
61
+ if (config === options.unconfiguredConfig) {
62
+ return options.unconfiguredDisabledReason;
63
+ }
64
+ return driver?.disabledReason?.(config);
65
+ },
66
+ waitingReason(config) {
67
+ return driver?.waitingReason?.(config);
68
+ },
69
+ configIssues(config) {
70
+ return driver?.configIssues?.(config) ?? [];
71
+ },
72
+ async start(input) {
73
+ return await (await loadDriver()).start(input);
74
+ },
75
+ };
76
+ }
77
+ async function readConfiguredChannelPluginEntries(configPath) {
78
+ let json;
79
+ try {
80
+ json = (await readSettingsJson(configPath)).json;
81
+ }
82
+ catch {
83
+ return [];
84
+ }
85
+ const channels = json.channels;
86
+ if (channels === undefined) {
87
+ return [];
88
+ }
89
+ if (!isSettingsRecord(channels)) {
90
+ return [
91
+ {
92
+ id: "channel-plugin",
93
+ label: "Channel plugin",
94
+ code: "invalid_plugin_config",
95
+ message: "channels must be an object when channel plugins are configured.",
96
+ },
97
+ ];
98
+ }
99
+ const plugins = channels.plugins;
100
+ if (plugins === undefined) {
101
+ return [];
102
+ }
103
+ if (!Array.isArray(plugins)) {
104
+ return [
105
+ {
106
+ id: "channel-plugin",
107
+ label: "Channel plugin",
108
+ code: "invalid_plugin_config",
109
+ message: "channels.plugins must be an array.",
110
+ },
111
+ ];
112
+ }
113
+ return plugins.map((entry, index) => parseChannelPluginEntry(entry, index));
114
+ }
115
+ function parseChannelPluginEntry(entry, index) {
116
+ const fallbackId = `channel-plugin-${index + 1}`;
117
+ if (!isSettingsRecord(entry)) {
118
+ return {
119
+ id: fallbackId,
120
+ label: "Channel plugin",
121
+ code: "invalid_plugin_config",
122
+ message: `channels.plugins[${index}] must be an object.`,
123
+ };
124
+ }
125
+ const explicitId = readOptionalNonEmptyString(entry.id);
126
+ const explicitLabel = readOptionalNonEmptyString(entry.label);
127
+ const packageName = readOptionalNonEmptyString(entry.package);
128
+ const id = explicitId ?? (packageName === undefined ? fallbackId : channelIdFromPackageName(packageName));
129
+ const label = explicitLabel ?? labelFromChannelId(id);
130
+ if (packageName === undefined) {
131
+ return {
132
+ id,
133
+ label,
134
+ code: "invalid_plugin_config",
135
+ message: `channels.plugins[${index}].package must be a non-empty package name.`,
136
+ };
137
+ }
138
+ let config;
139
+ if (entry.config !== undefined) {
140
+ if (!isSettingsRecord(entry.config)) {
141
+ return {
142
+ id,
143
+ label,
144
+ packageName,
145
+ pluginId: id,
146
+ code: "invalid_plugin_config",
147
+ message: `channels.plugins[${index}].config must be an object.`,
148
+ };
149
+ }
150
+ config = entry.config;
151
+ }
152
+ return {
153
+ packageName,
154
+ ...(explicitId === undefined ? {} : { id: explicitId }),
155
+ ...(explicitLabel === undefined ? {} : { label: explicitLabel }),
156
+ ...(config === undefined ? {} : { config }),
157
+ };
158
+ }
159
+ function rejectChannelPluginIdCollisions(entries, reservedIds) {
160
+ const reserved = new Set(reservedIds);
161
+ const seen = new Set(reserved);
162
+ return entries.map((entry, index) => {
163
+ const id = pluginEntryId(entry);
164
+ if (!seen.has(id)) {
165
+ seen.add(id);
166
+ return entry;
167
+ }
168
+ const collisionTarget = reserved.has(id) ? "a built-in channel" : "an earlier channel plugin";
169
+ const sectionId = uniqueInvalidPluginId(index, seen);
170
+ const label = `${pluginEntryLabel(entry)} plugin`;
171
+ const packageName = pluginEntryPackageName(entry);
172
+ return {
173
+ id: sectionId,
174
+ label,
175
+ ...(packageName === undefined ? {} : { packageName }),
176
+ pluginId: id,
177
+ code: "invalid_plugin_config",
178
+ message: `channels.plugins[${index}] resolves to channel id "${id}", which collides with ${collisionTarget}. Choose a unique plugin id.`,
179
+ };
180
+ });
181
+ }
182
+ function uniqueInvalidPluginId(index, seen) {
183
+ const base = `channel-plugin-${index + 1}`;
184
+ let candidate = base;
185
+ let suffix = 2;
186
+ while (seen.has(candidate)) {
187
+ candidate = `${base}-${suffix}`;
188
+ suffix += 1;
189
+ }
190
+ seen.add(candidate);
191
+ return candidate;
192
+ }
193
+ function pluginEntryId(entry) {
194
+ if ("message" in entry) {
195
+ return entry.pluginId ?? entry.id;
196
+ }
197
+ return entry.id ?? channelIdFromPackageName(entry.packageName);
198
+ }
199
+ function pluginEntryLabel(entry) {
200
+ if ("message" in entry) {
201
+ return entry.label;
202
+ }
203
+ return entry.label ?? labelFromChannelId(pluginEntryId(entry));
204
+ }
205
+ function pluginEntryPackageName(entry) {
206
+ return "packageName" in entry ? entry.packageName : undefined;
207
+ }
208
+ async function resolveChannelPlugin(entry) {
209
+ if ("message" in entry) {
210
+ return createUnavailablePluginDriver(entry.id, entry.label, entry.message, {
211
+ ...(entry.packageName === undefined ? {} : { packageName: entry.packageName }),
212
+ pluginId: entry.pluginId ?? entry.id,
213
+ code: entry.code,
214
+ });
215
+ }
216
+ try {
217
+ return await loadChannelPluginDriver(entry);
218
+ }
219
+ catch (error) {
220
+ const id = entry.id ?? channelIdFromPackageName(entry.packageName);
221
+ const label = entry.label ?? labelFromChannelId(id);
222
+ const message = error instanceof ChannelPluginConfigError
223
+ ? error.message
224
+ : `Cannot load channel plugin ${entry.packageName}: ${reasonOf(error)}. Install the package or remove it from channels.plugins.`;
225
+ const code = error instanceof ChannelPluginConfigError ? error.code : "plugin_import_failed";
226
+ return createUnavailablePluginDriver(id, label, message, {
227
+ packageName: entry.packageName,
228
+ pluginId: id,
229
+ code,
230
+ });
231
+ }
232
+ }
233
+ async function loadChannelPluginDriver(input) {
234
+ let mod;
235
+ try {
236
+ mod = await import(input.packageName);
237
+ }
238
+ catch (error) {
239
+ throw new ChannelPluginConfigError("plugin_import_failed", `Cannot load channel plugin ${input.packageName}: ${reasonOf(error)}. Install the package or remove it from channels.plugins.`, { packageName: input.packageName, ...(input.id === undefined ? {} : { pluginId: input.id }), reason: reasonOf(error) });
240
+ }
241
+ const factory = channelDriverFactory(mod);
242
+ if (factory === undefined) {
243
+ throw malformedPluginExport(input.packageName, input.id);
244
+ }
245
+ let driver;
246
+ try {
247
+ driver = await factory({
248
+ ...(input.factoryOptions ?? {}),
249
+ ...(input.id === undefined ? {} : { id: input.id }),
250
+ ...(input.label === undefined ? {} : { label: input.label }),
251
+ ...(input.config === undefined ? {} : { config: input.config }),
252
+ });
253
+ }
254
+ catch (error) {
255
+ throw new ChannelPluginConfigError("malformed_plugin_export", `Channel plugin ${input.packageName} failed while creating its driver: ${reasonOf(error)}`, { packageName: input.packageName, ...(input.id === undefined ? {} : { pluginId: input.id }), reason: reasonOf(error) });
256
+ }
257
+ if (!isChannelDriver(driver)) {
258
+ throw malformedPluginExport(input.packageName, input.id);
259
+ }
260
+ return driver;
261
+ }
262
+ function channelDriverFactory(mod) {
263
+ if (!isUnknownRecord(mod)) {
264
+ return undefined;
265
+ }
266
+ const factory = mod.createChannelDriver ?? mod.default;
267
+ return typeof factory === "function" ? factory : undefined;
268
+ }
269
+ function createUnavailablePluginDriver(id, label, message, options = {}) {
270
+ const code = options.code ?? (options.packageName === undefined ? "invalid_plugin_config" : "plugin_import_failed");
271
+ const error = new ChannelPluginConfigError(code, message, {
272
+ ...(options.packageName === undefined ? {} : { packageName: options.packageName }),
273
+ pluginId: options.pluginId ?? id,
274
+ });
275
+ return {
276
+ id,
277
+ label,
278
+ async configView() {
279
+ return { id, label, status: "active", fields: [] };
280
+ },
281
+ async loadConfig() {
282
+ throw error;
283
+ },
284
+ isConfigError(candidate) {
285
+ return candidate instanceof ChannelPluginConfigError;
286
+ },
287
+ async start() {
288
+ throw error;
289
+ },
290
+ };
291
+ }
292
+ async function fallbackConfigView(driver, input) {
293
+ if (driver.configView !== undefined) {
294
+ return await driver.configView(input);
295
+ }
296
+ let status = "active";
297
+ try {
298
+ const config = await driver.loadConfig(input);
299
+ if (driver.disabledReason?.(config) !== undefined) {
300
+ status = "disabled";
301
+ }
302
+ }
303
+ catch (error) {
304
+ if (!driver.isConfigError(error)) {
305
+ throw error;
306
+ }
307
+ }
308
+ return { id: driver.id, label: driver.label, status, fields: [] };
309
+ }
310
+ function malformedPluginExport(packageName, pluginId) {
311
+ return new ChannelPluginConfigError("malformed_plugin_export", `Channel plugin ${packageName} must export createChannelDriver(options) returning a ChannelDriver.`, { packageName, ...(pluginId === undefined ? {} : { pluginId }) });
312
+ }
313
+ function isChannelDriver(value) {
314
+ return (isUnknownRecord(value) &&
315
+ typeof value.id === "string" &&
316
+ value.id.trim().length > 0 &&
317
+ typeof value.label === "string" &&
318
+ value.label.trim().length > 0 &&
319
+ typeof value.loadConfig === "function" &&
320
+ typeof value.isConfigError === "function" &&
321
+ typeof value.start === "function");
322
+ }
323
+ function isSettingsRecord(value) {
324
+ return typeof value === "object" && value !== null && !Array.isArray(value);
325
+ }
326
+ function isUnknownRecord(value) {
327
+ return typeof value === "object" && value !== null && !Array.isArray(value);
328
+ }
329
+ function readOptionalNonEmptyString(value) {
330
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
331
+ }
332
+ function channelIdFromPackageName(packageName) {
333
+ const segment = packageName.split("/").pop() ?? packageName;
334
+ return segment.endsWith("-adapter") ? segment.slice(0, -"-adapter".length) : segment;
335
+ }
336
+ function labelFromChannelId(id) {
337
+ return id
338
+ .split(/[-_]+/u)
339
+ .filter((part) => part.length > 0)
340
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
341
+ .join(" ") || "Channel plugin";
342
+ }
343
+ function reasonOf(error) {
344
+ return error instanceof Error ? error.message : String(error);
345
+ }
346
+ //# sourceMappingURL=channel-plugins.js.map