@mono-agent/agent-app 0.4.0 → 0.5.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.
- package/README.md +15 -5
- package/dist/adapter-send-tools-main.js +2 -2
- package/dist/adapter-send-tools-main.js.map +1 -1
- package/dist/adapter-send-tools.d.ts +72 -6
- package/dist/adapter-send-tools.d.ts.map +1 -1
- package/dist/adapter-send-tools.js +413 -38
- package/dist/adapter-send-tools.js.map +1 -1
- package/dist/app-config.d.ts +48 -11
- package/dist/app-config.d.ts.map +1 -1
- package/dist/app-config.js +98 -24
- package/dist/app-config.js.map +1 -1
- package/dist/app.d.ts +10 -0
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +659 -48
- package/dist/app.js.map +1 -1
- package/dist/artifact-retention.d.ts +32 -0
- package/dist/artifact-retention.d.ts.map +1 -0
- package/dist/artifact-retention.js +111 -0
- package/dist/artifact-retention.js.map +1 -0
- package/dist/audit-runs.d.ts +12 -0
- package/dist/audit-runs.d.ts.map +1 -0
- package/dist/audit-runs.js +68 -0
- package/dist/audit-runs.js.map +1 -0
- package/dist/backfill.d.ts +3 -7
- package/dist/backfill.d.ts.map +1 -1
- package/dist/backfill.js +115 -11
- package/dist/backfill.js.map +1 -1
- package/dist/background.d.ts +8 -1
- package/dist/background.d.ts.map +1 -1
- package/dist/background.js +202 -4
- package/dist/background.js.map +1 -1
- package/dist/broadcast-recorder.d.ts +27 -0
- package/dist/broadcast-recorder.d.ts.map +1 -0
- package/dist/broadcast-recorder.js +82 -0
- package/dist/broadcast-recorder.js.map +1 -0
- package/dist/channel-config-view.d.ts +28 -0
- package/dist/channel-config-view.d.ts.map +1 -0
- package/dist/channel-config-view.js +79 -0
- package/dist/channel-config-view.js.map +1 -0
- package/dist/channel-gate.d.ts +22 -0
- package/dist/channel-gate.d.ts.map +1 -0
- package/dist/channel-gate.js +35 -0
- package/dist/channel-gate.js.map +1 -0
- package/dist/channel-plugins.d.ts +39 -0
- package/dist/channel-plugins.d.ts.map +1 -0
- package/dist/channel-plugins.js +372 -0
- package/dist/channel-plugins.js.map +1 -0
- package/dist/channels.d.ts +62 -73
- package/dist/channels.d.ts.map +1 -1
- package/dist/channels.js +833 -126
- package/dist/channels.js.map +1 -1
- package/dist/cli.d.ts +65 -3
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +755 -30
- package/dist/cli.js.map +1 -1
- package/dist/config-reference.d.ts +24 -0
- package/dist/config-reference.d.ts.map +1 -0
- package/dist/config-reference.js +580 -0
- package/dist/config-reference.js.map +1 -0
- package/dist/configured-agent.d.ts +104 -0
- package/dist/configured-agent.d.ts.map +1 -0
- package/dist/configured-agent.js +578 -0
- package/dist/configured-agent.js.map +1 -0
- package/dist/consumer-contract.d.ts +36 -0
- package/dist/consumer-contract.d.ts.map +1 -0
- package/dist/consumer-contract.js +290 -0
- package/dist/consumer-contract.js.map +1 -0
- package/dist/doctor.d.ts +9 -0
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +352 -55
- package/dist/doctor.js.map +1 -1
- package/dist/index.d.ts +9 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +23 -7
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +91 -26
- package/dist/init.js.map +1 -1
- package/dist/install-skill.d.ts +1 -1
- package/dist/install-skill.js +2 -2
- package/dist/install-skill.js.map +1 -1
- package/dist/interaction-bridge.d.ts +72 -0
- package/dist/interaction-bridge.d.ts.map +1 -0
- package/dist/interaction-bridge.js +354 -0
- package/dist/interaction-bridge.js.map +1 -0
- package/dist/memory-command.d.ts +10 -0
- package/dist/memory-command.d.ts.map +1 -0
- package/dist/memory-command.js +649 -0
- package/dist/memory-command.js.map +1 -0
- package/dist/memory-recall-main.js +1 -1
- package/dist/memory-recall-main.js.map +1 -1
- package/dist/memory-recall.d.ts +41 -5
- package/dist/memory-recall.d.ts.map +1 -1
- package/dist/memory-recall.js +75 -5
- package/dist/memory-recall.js.map +1 -1
- package/dist/memory-rituals.d.ts +8 -11
- package/dist/memory-rituals.d.ts.map +1 -1
- package/dist/memory-rituals.js +26 -25
- package/dist/memory-rituals.js.map +1 -1
- package/dist/metrics.d.ts +13 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +105 -0
- package/dist/metrics.js.map +1 -0
- package/dist/notify-destinations.d.ts +23 -0
- package/dist/notify-destinations.d.ts.map +1 -0
- package/dist/notify-destinations.js +79 -0
- package/dist/notify-destinations.js.map +1 -0
- package/dist/posted-message-index.d.ts +61 -0
- package/dist/posted-message-index.d.ts.map +1 -0
- package/dist/posted-message-index.js +178 -0
- package/dist/posted-message-index.js.map +1 -0
- package/dist/proactive-notify.d.ts +32 -0
- package/dist/proactive-notify.d.ts.map +1 -0
- package/dist/proactive-notify.js +62 -0
- package/dist/proactive-notify.js.map +1 -0
- package/dist/recipes/base.d.ts +15 -0
- package/dist/recipes/base.d.ts.map +1 -0
- package/dist/recipes/base.js +51 -0
- package/dist/recipes/base.js.map +1 -0
- package/dist/recipes/catalog.d.ts +4 -0
- package/dist/recipes/catalog.d.ts.map +1 -0
- package/dist/recipes/catalog.js +525 -0
- package/dist/recipes/catalog.js.map +1 -0
- package/dist/recipes/index.d.ts +11 -0
- package/dist/recipes/index.d.ts.map +1 -0
- package/dist/recipes/index.js +14 -0
- package/dist/recipes/index.js.map +1 -0
- package/dist/recipes/types.d.ts +70 -0
- package/dist/recipes/types.d.ts.map +1 -0
- package/dist/recipes/types.js +15 -0
- package/dist/recipes/types.js.map +1 -0
- package/dist/request-model-override.d.ts +67 -0
- package/dist/request-model-override.d.ts.map +1 -0
- package/dist/request-model-override.js +96 -0
- package/dist/request-model-override.js.map +1 -0
- package/dist/runs-health.d.ts +22 -0
- package/dist/runs-health.d.ts.map +1 -0
- package/dist/runs-health.js +144 -0
- package/dist/runs-health.js.map +1 -0
- package/dist/seen-conversations.d.ts +22 -0
- package/dist/seen-conversations.d.ts.map +1 -0
- package/dist/seen-conversations.js +120 -0
- package/dist/seen-conversations.js.map +1 -0
- package/dist/setup.d.ts +29 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +97 -0
- package/dist/setup.js.map +1 -0
- package/dist/trigger-overrides.d.ts +17 -0
- package/dist/trigger-overrides.d.ts.map +1 -0
- package/dist/trigger-overrides.js +30 -0
- package/dist/trigger-overrides.js.map +1 -0
- package/dist/tui-command.d.ts +56 -0
- package/dist/tui-command.d.ts.map +1 -0
- package/dist/tui-command.js +160 -0
- package/dist/tui-command.js.map +1 -0
- package/dist/ui.d.ts.map +1 -1
- package/dist/ui.js +4 -0
- package/dist/ui.js.map +1 -1
- package/dist/web-command.d.ts +45 -0
- package/dist/web-command.d.ts.map +1 -0
- package/dist/web-command.js +152 -0
- package/dist/web-command.js.map +1 -0
- package/package.json +19 -18
- package/schema/mono-agent.config.schema.json +1686 -0
- package/skills/mono-agent-composer/SKILL.md +34 -9
- package/skills/mono-agent-composer/agents/openai.yaml +4 -0
- package/skills/mono-agent-composer/references/config-blueprint.md +92 -49
- package/skills/mono-agent-composer/references/discovery-questions.md +15 -17
- package/skills/mono-agent-composer/references/feature-coverage.md +22 -17
- package/skills/mono-agent-composer/references/package-map.md +19 -21
- package/skills/mono-agent-composer/references/playbooks.md +267 -0
- package/skills/mono-agent-composer/references/validation.md +9 -1
package/dist/metrics.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import process from "node:process";
|
|
3
|
+
import { summarizeRecordedRunMetrics } from "@mono-agent/observability";
|
|
4
|
+
import { resolveAppArtifactDir } from "./app-config.js";
|
|
5
|
+
export async function runMetrics(args) {
|
|
6
|
+
const cwd = process.cwd();
|
|
7
|
+
const input = {
|
|
8
|
+
env: process.env,
|
|
9
|
+
cwd,
|
|
10
|
+
configPath: resolve(cwd, args.configPath ?? "mono-agent.config.json"),
|
|
11
|
+
};
|
|
12
|
+
let report;
|
|
13
|
+
try {
|
|
14
|
+
const artifactDir = args.artifactDir === undefined
|
|
15
|
+
? await resolveAppArtifactDir(input)
|
|
16
|
+
: resolve(cwd, args.artifactDir);
|
|
17
|
+
report = await summarizeRecordedRunMetrics({
|
|
18
|
+
artifactDir,
|
|
19
|
+
...(args.since === undefined ? {} : { since: args.since }),
|
|
20
|
+
...(args.until === undefined ? {} : { until: args.until }),
|
|
21
|
+
...(args.groupBy === undefined ? {} : { groupBy: args.groupBy }),
|
|
22
|
+
scope: args.includeMemory === true ? "all" : "agent",
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
27
|
+
return 1;
|
|
28
|
+
}
|
|
29
|
+
process.stdout.write(args.json === true ? `${JSON.stringify(report, null, 2)}\n` : renderMetricsReport(report));
|
|
30
|
+
return 0;
|
|
31
|
+
}
|
|
32
|
+
export function renderMetricsReport(report) {
|
|
33
|
+
let out = `Artifact metrics: ${report.artifactDir}\n`;
|
|
34
|
+
out += `Summary files: ${report.totalSummaryFiles} (${report.parsedSummaryFiles} parsed, ${report.parseFailureCount} parse failed)\n`;
|
|
35
|
+
if (report.since !== undefined || report.until !== undefined) {
|
|
36
|
+
out += `Window: ${report.since ?? "-inf"} to ${report.until ?? "+inf"}\n`;
|
|
37
|
+
}
|
|
38
|
+
out += renderBucket(report.overall);
|
|
39
|
+
if (report.groups.length > 0 && report.groupBy !== undefined) {
|
|
40
|
+
out += `\nGroups by ${report.groupBy}\n`;
|
|
41
|
+
for (const group of report.groups) {
|
|
42
|
+
out += ` ${group.key}: ${group.totalRuns} run${group.totalRuns === 1 ? "" : "s"}`;
|
|
43
|
+
out += `, failed ${group.statusCounts.failed} (${formatPercent(group.statusRates.failed)})`;
|
|
44
|
+
out += `, p90 ${formatDuration(group.durationMs.p90)}`;
|
|
45
|
+
out += `, cost ${formatUsd(group.cost.totalUsd)}\n`;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (report.parseFailures.length > 0) {
|
|
49
|
+
out += "\nParse failures\n";
|
|
50
|
+
for (const issue of report.parseFailures) {
|
|
51
|
+
const value = issue.value === undefined ? "" : ` (${issue.value})`;
|
|
52
|
+
out += ` ${issue.fileName}: ${issue.reason}${value}\n`;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (report.warnings.length > 0) {
|
|
56
|
+
out += "\nWarnings\n";
|
|
57
|
+
for (const warning of report.warnings) {
|
|
58
|
+
out += ` ${warning}\n`;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
function renderBucket(bucket) {
|
|
64
|
+
let out = `Total runs: ${bucket.totalRuns}\n`;
|
|
65
|
+
out += "\nStatuses\n";
|
|
66
|
+
for (const [status, count] of Object.entries(bucket.statusCounts)) {
|
|
67
|
+
out += ` ${status}: ${count} (${formatPercent(bucket.statusRates[status])})\n`;
|
|
68
|
+
}
|
|
69
|
+
out += "\nDuration\n";
|
|
70
|
+
out += ` samples: ${bucket.durationMs.count}\n`;
|
|
71
|
+
out += ` p50: ${formatDuration(bucket.durationMs.p50)}\n`;
|
|
72
|
+
out += ` p90: ${formatDuration(bucket.durationMs.p90)}\n`;
|
|
73
|
+
out += ` p99: ${formatDuration(bucket.durationMs.p99)}\n`;
|
|
74
|
+
out += ` max: ${formatDuration(bucket.durationMs.max)}\n`;
|
|
75
|
+
out += "\nCost\n";
|
|
76
|
+
out += ` total: ${formatUsd(bucket.cost.totalUsd)}\n`;
|
|
77
|
+
out += ` average/run: ${formatUsd(bucket.cost.averageUsdPerRun)}\n`;
|
|
78
|
+
out += ` runs with cost: ${bucket.cost.runsWithCost}\n`;
|
|
79
|
+
out += "\nFailure kinds\n";
|
|
80
|
+
if (bucket.failureKindRates.length === 0) {
|
|
81
|
+
out += " none\n";
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
for (const failureKind of bucket.failureKindRates) {
|
|
85
|
+
out += ` ${failureKind.failureKind}: ${failureKind.count} (${formatPercent(failureKind.rate)})\n`;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return out;
|
|
89
|
+
}
|
|
90
|
+
function formatPercent(rate) {
|
|
91
|
+
const fixed = (rate * 100).toFixed(1);
|
|
92
|
+
return `${fixed.replace(/\.0$/u, "")}%`;
|
|
93
|
+
}
|
|
94
|
+
function formatDuration(value) {
|
|
95
|
+
return value === null ? "n/a" : `${formatNumber(value)} ms`;
|
|
96
|
+
}
|
|
97
|
+
function formatUsd(value) {
|
|
98
|
+
return `$${formatNumber(value, 6)}`;
|
|
99
|
+
}
|
|
100
|
+
function formatNumber(value, maxFractionDigits = 3) {
|
|
101
|
+
return new Intl.NumberFormat("en-US", {
|
|
102
|
+
maximumFractionDigits: maxFractionDigits,
|
|
103
|
+
}).format(value);
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=metrics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.js","sourceRoot":"","sources":["../src/metrics.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { NotifyDestination } from "@mono-agent/agent-contracts";
|
|
2
|
+
import type { MonoAgentAppConfigInput } from "./app-config.js";
|
|
3
|
+
import type { ChannelId, MonoAgentAppLogger } from "./channels.js";
|
|
4
|
+
export type { NotifyDestination } from "@mono-agent/agent-contracts";
|
|
5
|
+
export interface ResolveNotifyDestinationsInput {
|
|
6
|
+
readonly input: MonoAgentAppConfigInput;
|
|
7
|
+
readonly artifactDir: string;
|
|
8
|
+
/** Whether a given channel is currently running (only running channels can deliver). */
|
|
9
|
+
readonly isRunning: (id: ChannelId) => boolean;
|
|
10
|
+
readonly logger?: MonoAgentAppLogger | undefined;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The conversations a native cron/webhook notification may be delivered to, used by
|
|
14
|
+
* the app to infer a destination when a job/endpoint sets no explicit
|
|
15
|
+
* `notifyConversationId`. Combines two id-free sources so the operator never types a conversationId:
|
|
16
|
+
* - conversations the agent has actually handled (run-artifact summaries), filtered to
|
|
17
|
+
* notify-capable + running channels, newest-first;
|
|
18
|
+
* - the adapter allowlist entries (when not allow-all), surfaced as candidates the agent
|
|
19
|
+
* can reach even before it has conversed there (covers a fresh single-user agent).
|
|
20
|
+
* WhatsApp is excluded (no notify hook yet) so the agent never picks an undeliverable id.
|
|
21
|
+
*/
|
|
22
|
+
export declare function resolveNotifyDestinations(opts: ResolveNotifyDestinationsInput): Promise<readonly NotifyDestination[]>;
|
|
23
|
+
//# sourceMappingURL=notify-destinations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notify-destinations.d.ts","sourceRoot":"","sources":["../src/notify-destinations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAErE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,KAAK,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAKnE,YAAY,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAiBrE,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,KAAK,EAAE,uBAAuB,CAAC;IACxC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,wFAAwF;IACxF,QAAQ,CAAC,SAAS,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,OAAO,CAAC;IAC/C,QAAQ,CAAC,MAAM,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;CAClD;AAED;;;;;;;;;GASG;AACH,wBAAsB,yBAAyB,CAC7C,IAAI,EAAE,8BAA8B,GACnC,OAAO,CAAC,SAAS,iBAAiB,EAAE,CAAC,CA6BvC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { listSeenNotifyDestinations } from "./seen-conversations.js";
|
|
2
|
+
let slackModule;
|
|
3
|
+
let telegramModule;
|
|
4
|
+
const loadSlackModule = async () => (slackModule ??= await import("@mono-agent/slack-adapter"));
|
|
5
|
+
const loadTelegramModule = async () => (telegramModule ??= await import("@mono-agent/telegram-adapter"));
|
|
6
|
+
/** Channels whose conversations can receive a proactive notification turn. */
|
|
7
|
+
const NOTIFY_CAPABLE = new Set(["telegram", "slack"]);
|
|
8
|
+
/**
|
|
9
|
+
* The conversations a native cron/webhook notification may be delivered to, used by
|
|
10
|
+
* the app to infer a destination when a job/endpoint sets no explicit
|
|
11
|
+
* `notifyConversationId`. Combines two id-free sources so the operator never types a conversationId:
|
|
12
|
+
* - conversations the agent has actually handled (run-artifact summaries), filtered to
|
|
13
|
+
* notify-capable + running channels, newest-first;
|
|
14
|
+
* - the adapter allowlist entries (when not allow-all), surfaced as candidates the agent
|
|
15
|
+
* can reach even before it has conversed there (covers a fresh single-user agent).
|
|
16
|
+
* WhatsApp is excluded (no notify hook yet) so the agent never picks an undeliverable id.
|
|
17
|
+
*/
|
|
18
|
+
export async function resolveNotifyDestinations(opts) {
|
|
19
|
+
const out = [];
|
|
20
|
+
const present = new Set();
|
|
21
|
+
const seen = await listSeenNotifyDestinations(opts.artifactDir);
|
|
22
|
+
for (const sighting of seen) {
|
|
23
|
+
if (!NOTIFY_CAPABLE.has(sighting.channelId) || !opts.isRunning(sighting.channelId)) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
out.push({
|
|
27
|
+
conversationId: sighting.conversationId,
|
|
28
|
+
channelId: sighting.channelId,
|
|
29
|
+
...(sighting.lastSeen === undefined ? {} : { lastSeen: sighting.lastSeen }),
|
|
30
|
+
});
|
|
31
|
+
present.add(sighting.conversationId);
|
|
32
|
+
}
|
|
33
|
+
if (opts.isRunning("telegram")) {
|
|
34
|
+
for (const chatId of await telegramAllowlist(opts)) {
|
|
35
|
+
addAllowlisted(out, present, `telegram:${chatId}`, "telegram");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (opts.isRunning("slack")) {
|
|
39
|
+
for (const channelId of await slackAllowlist(opts)) {
|
|
40
|
+
addAllowlisted(out, present, `slack:${channelId}`, "slack");
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
function addAllowlisted(out, present, conversationId, channelId) {
|
|
46
|
+
if (present.has(conversationId)) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
present.add(conversationId);
|
|
50
|
+
out.push({ conversationId, channelId, fromAllowlist: true });
|
|
51
|
+
}
|
|
52
|
+
/** Allowlisted Telegram chat ids (empty when allow-all, disabled, or unavailable — no enumerable candidate). */
|
|
53
|
+
async function telegramAllowlist(opts) {
|
|
54
|
+
try {
|
|
55
|
+
const adapter = await loadTelegramModule();
|
|
56
|
+
const config = await adapter.loadTelegramAdapterConfig({ env: opts.input.env, jsonPath: opts.input.configPath });
|
|
57
|
+
return config.enabled && !config.allowAllChats ? config.allowedChatIds : [];
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
opts.logger?.warn?.("notify destinations: telegram allowlist unavailable.", { reason: reasonOf(error) });
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** Allowlisted Slack channel ids (empty when allow-all, disabled, or unavailable). */
|
|
65
|
+
async function slackAllowlist(opts) {
|
|
66
|
+
try {
|
|
67
|
+
const adapter = await loadSlackModule();
|
|
68
|
+
const config = await adapter.loadSlackAdapterConfig({ env: opts.input.env, jsonPath: opts.input.configPath });
|
|
69
|
+
return config.enabled && !config.allowAllChannels ? config.allowedChannelIds : [];
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
opts.logger?.warn?.("notify destinations: slack allowlist unavailable.", { reason: reasonOf(error) });
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function reasonOf(error) {
|
|
77
|
+
return error instanceof Error ? error.message : String(error);
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=notify-destinations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notify-destinations.js","sourceRoot":"","sources":["../src/notify-destinations.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AAWrE,IAAI,WAA2C,CAAC;AAChD,IAAI,cAAiD,CAAC;AACtD,MAAM,eAAe,GAAG,KAAK,IAAiC,EAAE,CAC9D,CAAC,WAAW,KAAK,MAAM,MAAM,CAAC,2BAA2B,CAAC,CAAC,CAAC;AAC9D,MAAM,kBAAkB,GAAG,KAAK,IAAoC,EAAE,CACpE,CAAC,cAAc,KAAK,MAAM,MAAM,CAAC,8BAA8B,CAAC,CAAC,CAAC;AAEpE,8EAA8E;AAC9E,MAAM,cAAc,GAA2B,IAAI,GAAG,CAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC;AAUzF;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,IAAoC;IAEpC,MAAM,GAAG,GAAwB,EAAE,CAAC;IACpC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAElC,MAAM,IAAI,GAAG,MAAM,0BAA0B,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAChE,KAAK,MAAM,QAAQ,IAAI,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YACnF,SAAS;QACX,CAAC;QACD,GAAG,CAAC,IAAI,CAAC;YACP,cAAc,EAAE,QAAQ,CAAC,cAAc;YACvC,SAAS,EAAE,QAAQ,CAAC,SAAS;YAC7B,GAAG,CAAC,QAAQ,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,CAAC;SAC5E,CAAC,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;IACvC,CAAC;IAED,IAAI,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,KAAK,MAAM,MAAM,IAAI,MAAM,iBAAiB,CAAC,IAAI,CAAC,EAAE,CAAC;YACnD,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,YAAY,MAAM,EAAE,EAAE,UAAU,CAAC,CAAC;QACjE,CAAC;IACH,CAAC;IACD,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5B,KAAK,MAAM,SAAS,IAAI,MAAM,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC;YACnD,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,SAAS,EAAE,EAAE,OAAO,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,cAAc,CACrB,GAAwB,EACxB,OAAoB,EACpB,cAAsB,EACtB,SAAoB;IAEpB,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,CAAC;QAChC,OAAO;IACT,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAC5B,GAAG,CAAC,IAAI,CAAC,EAAE,cAAc,EAAE,SAAS,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;AAC/D,CAAC;AAED,gHAAgH;AAChH,KAAK,UAAU,iBAAiB,CAAC,IAAoC;IACnE,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,kBAAkB,EAAE,CAAC;QAC3C,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,yBAAyB,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;QACjH,OAAO,MAAM,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9E,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,sDAAsD,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACzG,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,sFAAsF;AACtF,KAAK,UAAU,cAAc,CAAC,IAAoC;IAChE,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,eAAe,EAAE,CAAC;QACxC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,sBAAsB,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;QAC9G,OAAO,MAAM,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,CAAC;IACpF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,mDAAmD,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACtG,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAChE,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps a message the agent POSTED (`channelId` + Slack `ts`) back to the
|
|
3
|
+
* conversation that produced it, so a later in-thread reply can be resolved to
|
|
4
|
+
* that conversation and continue its history/session.
|
|
5
|
+
*
|
|
6
|
+
* Why this exists: a scheduled/proactive post (e.g. a daily digest) runs under a
|
|
7
|
+
* synthetic conversationId (e.g. `scheduled-scan`) and posts via
|
|
8
|
+
* `slack_send_message`, which registers no `slack:` conversation. When the user
|
|
9
|
+
* replies, the Slack adapter derives `slack:<channel>:<posted-ts>` — an id with no
|
|
10
|
+
* history. This index closes that gap: the producer records `(channel, ts) →
|
|
11
|
+
* producing conversationId`; the consumer (inbound dispatch) looks it up and
|
|
12
|
+
* aliases the reply onto the producing conversation.
|
|
13
|
+
*
|
|
14
|
+
* Storage is an append-only JSONL file inside the run-artifact dir. Append-only
|
|
15
|
+
* keeps cross-process writes safe: the `slack_send_message` child process and the
|
|
16
|
+
* adapter both only ever append (small `O_APPEND` writes are atomic), and only the
|
|
17
|
+
* single adapter process compacts. A `.jsonl` file is ignored by the
|
|
18
|
+
* `.summary.json` artifact scanners (see `seen-conversations.ts`), so it never
|
|
19
|
+
* collides with run-artifact tooling.
|
|
20
|
+
*/
|
|
21
|
+
export declare const POSTED_MESSAGE_INDEX_FILENAME = "posted-message-index.jsonl";
|
|
22
|
+
export interface PostedMessageEntry {
|
|
23
|
+
/** Slack channel/DM id the message was posted to. */
|
|
24
|
+
readonly channelId: string;
|
|
25
|
+
/** Slack message timestamp returned by `chat.postMessage`. */
|
|
26
|
+
readonly ts: string;
|
|
27
|
+
/** Producing conversationId, de-bucketed to its base form. */
|
|
28
|
+
readonly conversationId: string;
|
|
29
|
+
/** ISO timestamp of when the entry was written. */
|
|
30
|
+
readonly writtenAt: string;
|
|
31
|
+
}
|
|
32
|
+
/** The single index-file path both producer and consumer agree on. */
|
|
33
|
+
export declare function resolvePostedMessageIndexPath(artifactDir: string): string;
|
|
34
|
+
/** Strip a trailing daily-rollover bucket so the stored id is the base producing id. */
|
|
35
|
+
export declare function basePostedConversationId(conversationId: string): string;
|
|
36
|
+
/**
|
|
37
|
+
* Record that `conversationId` posted a message at `(channelId, ts)`. Best-effort:
|
|
38
|
+
* a failed index write must never fail the Slack post, so callers should not await
|
|
39
|
+
* this in a way that surfaces its errors — it swallows them. The stored
|
|
40
|
+
* conversationId is de-bucketed so the consumer can let the responder re-bucket to
|
|
41
|
+
* the reply's own day (consistent with daily session rollover).
|
|
42
|
+
*/
|
|
43
|
+
export declare function appendPostedMessage(indexPath: string, entry: {
|
|
44
|
+
channelId: string;
|
|
45
|
+
ts: string;
|
|
46
|
+
conversationId: string;
|
|
47
|
+
}, now?: () => Date): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Resolve the producing conversationId for a posted message, newest write wins.
|
|
50
|
+
* Returns `undefined` when the file is missing or has no matching entry, so the
|
|
51
|
+
* caller falls back to the default (a fresh `slack:` conversation) — no regression.
|
|
52
|
+
*/
|
|
53
|
+
export declare function lookupProducingConversation(indexPath: string, channelId: string, ts: string): Promise<string | undefined>;
|
|
54
|
+
/**
|
|
55
|
+
* Bound file growth by rewriting the index with only the newest `maxEntries`
|
|
56
|
+
* (by write time, de-duped to the newest entry per `channel+ts`). Consumer-only —
|
|
57
|
+
* a single process compacts, via temp-file + rename so a concurrent append is
|
|
58
|
+
* never interleaved with a partial rewrite. Best-effort.
|
|
59
|
+
*/
|
|
60
|
+
export declare function compactPostedMessageIndex(indexPath: string, maxEntries?: number): Promise<void>;
|
|
61
|
+
//# sourceMappingURL=posted-message-index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"posted-message-index.d.ts","sourceRoot":"","sources":["../src/posted-message-index.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,eAAO,MAAM,6BAA6B,+BAA+B,CAAC;AAO1E,MAAM,WAAW,kBAAkB;IACjC,qDAAqD;IACrD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,mDAAmD;IACnD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,sEAAsE;AACtE,wBAAgB,6BAA6B,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEzE;AAED,wFAAwF;AACxF,wBAAgB,wBAAwB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAEvE;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,EAChE,GAAG,GAAE,MAAM,IAAuB,GACjC,OAAO,CAAC,IAAI,CAAC,CA6Bf;AAED;;;;GAIG;AACH,wBAAsB,2BAA2B,CAC/C,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAgB7B;AAED;;;;;GAKG;AACH,wBAAsB,yBAAyB,CAC7C,SAAS,EAAE,MAAM,EACjB,UAAU,GAAE,MAAoC,GAC/C,OAAO,CAAC,IAAI,CAAC,CAyBf"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { appendFile, mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Maps a message the agent POSTED (`channelId` + Slack `ts`) back to the
|
|
5
|
+
* conversation that produced it, so a later in-thread reply can be resolved to
|
|
6
|
+
* that conversation and continue its history/session.
|
|
7
|
+
*
|
|
8
|
+
* Why this exists: a scheduled/proactive post (e.g. a daily digest) runs under a
|
|
9
|
+
* synthetic conversationId (e.g. `scheduled-scan`) and posts via
|
|
10
|
+
* `slack_send_message`, which registers no `slack:` conversation. When the user
|
|
11
|
+
* replies, the Slack adapter derives `slack:<channel>:<posted-ts>` — an id with no
|
|
12
|
+
* history. This index closes that gap: the producer records `(channel, ts) →
|
|
13
|
+
* producing conversationId`; the consumer (inbound dispatch) looks it up and
|
|
14
|
+
* aliases the reply onto the producing conversation.
|
|
15
|
+
*
|
|
16
|
+
* Storage is an append-only JSONL file inside the run-artifact dir. Append-only
|
|
17
|
+
* keeps cross-process writes safe: the `slack_send_message` child process and the
|
|
18
|
+
* adapter both only ever append (small `O_APPEND` writes are atomic), and only the
|
|
19
|
+
* single adapter process compacts. A `.jsonl` file is ignored by the
|
|
20
|
+
* `.summary.json` artifact scanners (see `seen-conversations.ts`), so it never
|
|
21
|
+
* collides with run-artifact tooling.
|
|
22
|
+
*/
|
|
23
|
+
export const POSTED_MESSAGE_INDEX_FILENAME = "posted-message-index.jsonl";
|
|
24
|
+
/** Daily-rollover bucket suffix the responder appends (`…#2026-06-22`). */
|
|
25
|
+
const ROLLOVER_BUCKET = /#\d{4}-\d{2}-\d{2}$/u;
|
|
26
|
+
const DEFAULT_COMPACT_MAX_ENTRIES = 5000;
|
|
27
|
+
/** The single index-file path both producer and consumer agree on. */
|
|
28
|
+
export function resolvePostedMessageIndexPath(artifactDir) {
|
|
29
|
+
return join(artifactDir, POSTED_MESSAGE_INDEX_FILENAME);
|
|
30
|
+
}
|
|
31
|
+
/** Strip a trailing daily-rollover bucket so the stored id is the base producing id. */
|
|
32
|
+
export function basePostedConversationId(conversationId) {
|
|
33
|
+
return conversationId.replace(ROLLOVER_BUCKET, "");
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Record that `conversationId` posted a message at `(channelId, ts)`. Best-effort:
|
|
37
|
+
* a failed index write must never fail the Slack post, so callers should not await
|
|
38
|
+
* this in a way that surfaces its errors — it swallows them. The stored
|
|
39
|
+
* conversationId is de-bucketed so the consumer can let the responder re-bucket to
|
|
40
|
+
* the reply's own day (consistent with daily session rollover).
|
|
41
|
+
*/
|
|
42
|
+
export async function appendPostedMessage(indexPath, entry, now = () => new Date()) {
|
|
43
|
+
const channelId = entry.channelId.trim();
|
|
44
|
+
const ts = entry.ts.trim();
|
|
45
|
+
const conversationId = basePostedConversationId(entry.conversationId.trim());
|
|
46
|
+
if (channelId.length === 0 || ts.length === 0 || conversationId.length === 0) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const record = {
|
|
50
|
+
channelId,
|
|
51
|
+
ts,
|
|
52
|
+
conversationId,
|
|
53
|
+
writtenAt: now().toISOString(),
|
|
54
|
+
};
|
|
55
|
+
const line = `${JSON.stringify(record)}\n`;
|
|
56
|
+
try {
|
|
57
|
+
await appendFile(indexPath, line, "utf8");
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
if (error?.code !== "ENOENT") {
|
|
61
|
+
return; // best-effort
|
|
62
|
+
}
|
|
63
|
+
// Artifact dir not created yet (agent never wrote a summary here). Create it
|
|
64
|
+
// and retry once; still best-effort.
|
|
65
|
+
try {
|
|
66
|
+
await mkdir(dirname(indexPath), { recursive: true });
|
|
67
|
+
await appendFile(indexPath, line, "utf8");
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// Give up silently.
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Resolve the producing conversationId for a posted message, newest write wins.
|
|
76
|
+
* Returns `undefined` when the file is missing or has no matching entry, so the
|
|
77
|
+
* caller falls back to the default (a fresh `slack:` conversation) — no regression.
|
|
78
|
+
*/
|
|
79
|
+
export async function lookupProducingConversation(indexPath, channelId, ts) {
|
|
80
|
+
const wantChannel = channelId.trim();
|
|
81
|
+
const wantTs = ts.trim();
|
|
82
|
+
if (wantChannel.length === 0 || wantTs.length === 0) {
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
let match;
|
|
86
|
+
for (const entry of await readEntries(indexPath)) {
|
|
87
|
+
if (entry.channelId !== wantChannel || entry.ts !== wantTs) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (match === undefined || entry.writtenAt >= match.writtenAt) {
|
|
91
|
+
match = entry;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return match?.conversationId;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Bound file growth by rewriting the index with only the newest `maxEntries`
|
|
98
|
+
* (by write time, de-duped to the newest entry per `channel+ts`). Consumer-only —
|
|
99
|
+
* a single process compacts, via temp-file + rename so a concurrent append is
|
|
100
|
+
* never interleaved with a partial rewrite. Best-effort.
|
|
101
|
+
*/
|
|
102
|
+
export async function compactPostedMessageIndex(indexPath, maxEntries = DEFAULT_COMPACT_MAX_ENTRIES) {
|
|
103
|
+
const entries = await readEntries(indexPath);
|
|
104
|
+
if (entries.length <= maxEntries) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
// Newest entry per (channel, ts), then newest-first, then cap.
|
|
108
|
+
const latest = new Map();
|
|
109
|
+
for (const entry of entries) {
|
|
110
|
+
const key = `${entry.channelId} ${entry.ts}`;
|
|
111
|
+
const prior = latest.get(key);
|
|
112
|
+
if (prior === undefined || entry.writtenAt >= prior.writtenAt) {
|
|
113
|
+
latest.set(key, entry);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const kept = [...latest.values()]
|
|
117
|
+
.sort((a, b) => (a.writtenAt < b.writtenAt ? 1 : a.writtenAt > b.writtenAt ? -1 : 0))
|
|
118
|
+
.slice(0, maxEntries);
|
|
119
|
+
const body = kept.map((entry) => JSON.stringify(entry)).join("\n");
|
|
120
|
+
const tmpPath = `${indexPath}.tmp-${String(kept.length)}`;
|
|
121
|
+
try {
|
|
122
|
+
await writeFile(tmpPath, kept.length === 0 ? "" : `${body}\n`, "utf8");
|
|
123
|
+
await rename(tmpPath, indexPath);
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// Best-effort; leave the original file in place on failure.
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async function readEntries(indexPath) {
|
|
130
|
+
let raw;
|
|
131
|
+
try {
|
|
132
|
+
raw = await readFile(indexPath, "utf8");
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
return []; // missing/unreadable → nothing recorded yet
|
|
136
|
+
}
|
|
137
|
+
const out = [];
|
|
138
|
+
for (const line of raw.split("\n")) {
|
|
139
|
+
const trimmed = line.trim();
|
|
140
|
+
if (trimmed.length === 0) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
const parsed = parseEntry(trimmed);
|
|
144
|
+
if (parsed !== undefined) {
|
|
145
|
+
out.push(parsed);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return out;
|
|
149
|
+
}
|
|
150
|
+
function parseEntry(line) {
|
|
151
|
+
let json;
|
|
152
|
+
try {
|
|
153
|
+
json = JSON.parse(line);
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
return undefined; // tolerate a torn/partial line
|
|
157
|
+
}
|
|
158
|
+
if (typeof json !== "object" || json === null) {
|
|
159
|
+
return undefined;
|
|
160
|
+
}
|
|
161
|
+
const record = json;
|
|
162
|
+
const channelId = stringField(record.channelId);
|
|
163
|
+
const ts = stringField(record.ts);
|
|
164
|
+
const conversationId = stringField(record.conversationId);
|
|
165
|
+
if (channelId === undefined || ts === undefined || conversationId === undefined) {
|
|
166
|
+
return undefined;
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
channelId,
|
|
170
|
+
ts,
|
|
171
|
+
conversationId,
|
|
172
|
+
writtenAt: stringField(record.writtenAt) ?? "",
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function stringField(value) {
|
|
176
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=posted-message-index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"posted-message-index.js","sourceRoot":"","sources":["../src/posted-message-index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { NotifyDeliveryResult } from "@mono-agent/agent-contracts";
|
|
2
|
+
import type { ChannelId, MonoAgentAppLogger, RunningChannel } from "./channels.js";
|
|
3
|
+
export type { NotifyDeliveryResult } from "@mono-agent/agent-contracts";
|
|
4
|
+
/** The push channel that owns a destination conversationId (requires a `<scheme>:<target>` form), or undefined. */
|
|
5
|
+
export declare function channelIdForConversation(conversationId: string): ChannelId | undefined;
|
|
6
|
+
export interface ProactiveNotifyInput {
|
|
7
|
+
/** Destination conversationId, e.g. `telegram:42` or `slack:C1:171.5`. */
|
|
8
|
+
readonly conversationId: string;
|
|
9
|
+
/** The message to deliver. With `verbatim`, posted as-is; otherwise run as a turn. */
|
|
10
|
+
readonly text: string;
|
|
11
|
+
/**
|
|
12
|
+
* Deliver `text` VERBATIM — post it to the destination unchanged with no model
|
|
13
|
+
* call, then record it to the conversation's history (native cron/webhook
|
|
14
|
+
* notification). Without it, `text` is a prompt run as a turn on the
|
|
15
|
+
* destination's harness (e.g. a Slack interactive trigger).
|
|
16
|
+
*/
|
|
17
|
+
readonly verbatim?: boolean;
|
|
18
|
+
/** Currently running channels, keyed by id (the app's live registry). */
|
|
19
|
+
readonly running: ReadonlyMap<ChannelId, Pick<RunningChannel, "notify">>;
|
|
20
|
+
readonly logger?: MonoAgentAppLogger;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Route a proactive notification to the channel that owns its destination
|
|
24
|
+
* conversation, so the message runs as a real turn on that channel's own harness
|
|
25
|
+
* (shared session/history) and is delivered through its normal stream. The owning
|
|
26
|
+
* channel's `notify` hook enforces its adapter allowlist before delivering, so a
|
|
27
|
+
* non-allowlisted (e.g. payload-supplied) destination is rejected here. Returns a
|
|
28
|
+
* structured {@link NotifyDeliveryResult}; never throws (the trigger run already
|
|
29
|
+
* succeeded), so the caller can report the outcome to the model.
|
|
30
|
+
*/
|
|
31
|
+
export declare function routeProactiveNotification(input: ProactiveNotifyInput): Promise<NotifyDeliveryResult>;
|
|
32
|
+
//# sourceMappingURL=proactive-notify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proactive-notify.d.ts","sourceRoot":"","sources":["../src/proactive-notify.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAExE,OAAO,KAAK,EAAE,SAAS,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAInF,YAAY,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAcxE,mHAAmH;AACnH,wBAAgB,wBAAwB,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAMtF;AAED,MAAM,WAAW,oBAAoB;IACnC,0EAA0E;IAC1E,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,sFAAsF;IACtF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,yEAAyE;IACzE,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC,CAAC;IACzE,QAAQ,CAAC,MAAM,CAAC,EAAE,kBAAkB,CAAC;CACtC;AAED;;;;;;;;GAQG;AACH,wBAAsB,0BAA0B,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA+B3G"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Push channels a proactive notification can be delivered to. The conversationId
|
|
3
|
+
* scheme (the part before the first `:`) identifies the owning channel; only
|
|
4
|
+
* these channels run a destination harness that can both produce and deliver a
|
|
5
|
+
* turn. cron/webhook/openai-api/a2a are request-driven, not push destinations.
|
|
6
|
+
*/
|
|
7
|
+
const PUSH_CHANNEL_BY_SCHEME = {
|
|
8
|
+
telegram: "telegram",
|
|
9
|
+
slack: "slack",
|
|
10
|
+
whatsapp: "whatsapp",
|
|
11
|
+
};
|
|
12
|
+
/** The push channel that owns a destination conversationId (requires a `<scheme>:<target>` form), or undefined. */
|
|
13
|
+
export function channelIdForConversation(conversationId) {
|
|
14
|
+
const colon = conversationId.indexOf(":");
|
|
15
|
+
if (colon <= 0) {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
return PUSH_CHANNEL_BY_SCHEME[conversationId.slice(0, colon)];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Route a proactive notification to the channel that owns its destination
|
|
22
|
+
* conversation, so the message runs as a real turn on that channel's own harness
|
|
23
|
+
* (shared session/history) and is delivered through its normal stream. The owning
|
|
24
|
+
* channel's `notify` hook enforces its adapter allowlist before delivering, so a
|
|
25
|
+
* non-allowlisted (e.g. payload-supplied) destination is rejected here. Returns a
|
|
26
|
+
* structured {@link NotifyDeliveryResult}; never throws (the trigger run already
|
|
27
|
+
* succeeded), so the caller can report the outcome to the model.
|
|
28
|
+
*/
|
|
29
|
+
export async function routeProactiveNotification(input) {
|
|
30
|
+
const channelId = channelIdForConversation(input.conversationId);
|
|
31
|
+
if (channelId === undefined) {
|
|
32
|
+
input.logger?.warn?.("Proactive notification skipped: unrecognized destination.", {
|
|
33
|
+
conversationId: input.conversationId,
|
|
34
|
+
});
|
|
35
|
+
return { delivered: false, reason: "unrecognized destination conversationId" };
|
|
36
|
+
}
|
|
37
|
+
const channel = input.running.get(channelId);
|
|
38
|
+
if (channel?.notify === undefined) {
|
|
39
|
+
input.logger?.warn?.("Proactive notification skipped: destination channel is not running or does not support delivery.", { conversationId: input.conversationId, channelId });
|
|
40
|
+
return { delivered: false, reason: `${channelId} channel is not running or does not support proactive delivery` };
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
return await channel.notify({
|
|
44
|
+
conversationId: input.conversationId,
|
|
45
|
+
text: input.text,
|
|
46
|
+
...(input.verbatim === undefined ? {} : { verbatim: input.verbatim }),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
const reason = reasonOf(error);
|
|
51
|
+
input.logger?.warn?.("Proactive notification failed: destination channel notify threw.", {
|
|
52
|
+
conversationId: input.conversationId,
|
|
53
|
+
channelId,
|
|
54
|
+
reason,
|
|
55
|
+
});
|
|
56
|
+
return { delivered: false, reason };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function reasonOf(error) {
|
|
60
|
+
return error instanceof Error ? error.message : String(error);
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=proactive-notify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proactive-notify.js","sourceRoot":"","sources":["../src/proactive-notify.ts"],"names":[],"mappings":"AAQA;;;;;GAKG;AACH,MAAM,sBAAsB,GAAuC;IACjE,QAAQ,EAAE,UAAU;IACpB,KAAK,EAAE,OAAO;IACd,QAAQ,EAAE,UAAU;CACrB,CAAC;AAEF,mHAAmH;AACnH,MAAM,UAAU,wBAAwB,CAAC,cAAsB;IAC7D,MAAM,KAAK,GAAG,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC1C,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;QACf,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,sBAAsB,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;AAChE,CAAC;AAmBD;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAAC,KAA2B;IAC1E,MAAM,SAAS,GAAG,wBAAwB,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IACjE,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,2DAA2D,EAAE;YAChF,cAAc,EAAE,KAAK,CAAC,cAAc;SACrC,CAAC,CAAC;QACH,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,yCAAyC,EAAE,CAAC;IACjF,CAAC;IACD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAC7C,IAAI,OAAO,EAAE,MAAM,KAAK,SAAS,EAAE,CAAC;QAClC,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,CAClB,kGAAkG,EAClG,EAAE,cAAc,EAAE,KAAK,CAAC,cAAc,EAAE,SAAS,EAAE,CACpD,CAAC;QACF,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,SAAS,gEAAgE,EAAE,CAAC;IACpH,CAAC;IACD,IAAI,CAAC;QACH,OAAO,MAAM,OAAO,CAAC,MAAM,CAAC;YAC1B,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,GAAG,CAAC,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC;SACtE,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;QAC/B,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,kEAAkE,EAAE;YACvF,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,SAAS;YACT,MAAM;SACP,CAAC,CAAC;QACH,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IACtC,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAChE,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { MonoAgentConfigJson } from "@mono-agent/config";
|
|
2
|
+
import type { RecipeInput, RecipeInputValues } from "./types.js";
|
|
3
|
+
export declare const DEFAULT_MODEL = "claude:claude-sonnet-4-6";
|
|
4
|
+
/** The model input every recipe shares; overridable from `--model`. */
|
|
5
|
+
export declare const MODEL_INPUT: RecipeInput;
|
|
6
|
+
/**
|
|
7
|
+
* The adapter-neutral skeleton every recipe extends: runtime model, identity,
|
|
8
|
+
* empty tool policy, local artifacts, and the trace registry. Recipes spread
|
|
9
|
+
* this and add their channel / memory / sandbox / observability blocks. Mirrors
|
|
10
|
+
* `init.ts`'s scaffold so a recipe-built folder matches a hand-init'd one.
|
|
11
|
+
*/
|
|
12
|
+
export declare function baseConfig(input: RecipeInputValues): MonoAgentConfigJson;
|
|
13
|
+
/** A `memory` block for the given tier, rooted at the standard memory path. */
|
|
14
|
+
export declare function memoryBlock(mode: "lite" | "journal" | "bujo"): NonNullable<MonoAgentConfigJson["memory"]>;
|
|
15
|
+
//# sourceMappingURL=base.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../src/recipes/base.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAG9D,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEjE,eAAO,MAAM,aAAa,6BAA6B,CAAC;AAExD,uEAAuE;AACvE,eAAO,MAAM,WAAW,EAAE,WAKzB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,iBAAiB,GAAG,mBAAmB,CA0BxE;AAED,+EAA+E;AAC/E,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAChC,WAAW,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC,CAM5C"}
|