@cursor/july 0.1.6 → 0.1.8
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/dist/bin/agent-serve.js +73 -19
- package/dist/channels/slack/channel-watch.d.ts +6 -1
- package/dist/channels/slack/channel-watch.d.ts.map +1 -1
- package/dist/channels/slack/channel-watch.js +22 -1
- package/dist/channels/slack/cursor-account.d.ts +4 -0
- package/dist/channels/slack/cursor-account.d.ts.map +1 -1
- package/dist/channels/slack/cursor-account.js +12 -2
- package/dist/channels/slack/defaults.d.ts +10 -5
- package/dist/channels/slack/defaults.d.ts.map +1 -1
- package/dist/channels/slack/defaults.js +39 -17
- package/dist/channels/slack/dispatch.d.ts +18 -0
- package/dist/channels/slack/dispatch.d.ts.map +1 -1
- package/dist/channels/slack/dispatch.js +141 -38
- package/dist/channels/slack/inbound.d.ts +13 -3
- package/dist/channels/slack/inbound.d.ts.map +1 -1
- package/dist/channels/slack/inbound.js +17 -9
- package/dist/channels/slack/index.d.ts +4 -0
- package/dist/channels/slack/index.d.ts.map +1 -1
- package/dist/channels/slack/index.js +4 -0
- package/dist/channels/slack/live-delivery.d.ts.map +1 -1
- package/dist/channels/slack/live-delivery.js +1 -0
- package/dist/channels/slack/manifest.d.ts.map +1 -1
- package/dist/channels/slack/manifest.js +3 -0
- package/dist/channels/slack/nudge-carrier.d.ts +19 -0
- package/dist/channels/slack/nudge-carrier.d.ts.map +1 -0
- package/dist/channels/slack/nudge-carrier.js +166 -0
- package/dist/channels/slack/nudge-store.d.ts +61 -0
- package/dist/channels/slack/nudge-store.d.ts.map +1 -0
- package/dist/channels/slack/nudge-store.js +266 -0
- package/dist/channels/slack/slack-channel.d.ts.map +1 -1
- package/dist/channels/slack/slack-channel.js +74 -25
- package/dist/channels/slack/stop.d.ts +14 -0
- package/dist/channels/slack/stop.d.ts.map +1 -0
- package/dist/channels/slack/stop.js +22 -0
- package/dist/channels/slack/thread-context.d.ts +81 -0
- package/dist/channels/slack/thread-context.d.ts.map +1 -0
- package/dist/channels/slack/thread-context.js +470 -0
- package/dist/channels/slack/types.d.ts +27 -0
- package/dist/channels/slack/types.d.ts.map +1 -1
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +4 -4
- package/dist/docs/assets/{app.DMboGKUD.js → app.BP8mAVMW.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.DevOabKJ.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.DkEqEnJU.js → VPLocalSearchBox.1FXIZbz8.js} +1 -1
- package/dist/docs/assets/chunks/{theme.ASugdMNm.js → theme.Dm1R02v9.js} +2 -2
- package/dist/docs/assets/example-agents_index.md.CwtWu5ix.js +2 -0
- package/dist/docs/assets/{example-agents_index.md.CRqJlnIf.lean.js → example-agents_index.md.CwtWu5ix.lean.js} +1 -1
- package/dist/docs/assets/example-agents_oncall.md.BG_sUMly.js +10 -0
- package/dist/docs/assets/example-agents_oncall.md.BG_sUMly.lean.js +1 -0
- package/dist/docs/assets/{guides_slack.md.CCwqHvSV.js → guides_slack.md.FWale6Ip.js} +4 -3
- package/dist/docs/assets/{guides_slack.md.CCwqHvSV.lean.js → guides_slack.md.FWale6Ip.lean.js} +1 -1
- package/dist/docs/assets/{reference_sessions.md.DdjWabnN.js → reference_sessions.md.DLd6mvbv.js} +2 -2
- package/dist/docs/assets/{reference_sessions.md.DdjWabnN.lean.js → reference_sessions.md.DLd6mvbv.lean.js} +1 -1
- package/dist/docs/assets/{storage.md.CVnInNiN.js → storage.md.Cu3MwM2a.js} +3 -2
- package/dist/docs/assets/{storage.md.CVnInNiN.lean.js → storage.md.Cu3MwM2a.lean.js} +1 -1
- package/dist/docs/building-with-agents.html +4 -4
- package/dist/docs/concepts.html +4 -4
- package/dist/docs/deployment.html +4 -4
- package/dist/docs/evals.html +4 -4
- package/dist/docs/example-agents/approval-buddy.html +4 -4
- package/dist/docs/example-agents/benny.html +5 -5
- package/dist/docs/example-agents/bugbot.html +5 -5
- package/dist/docs/example-agents/codebase-wiki.html +4 -4
- package/dist/docs/example-agents/codeowners-review.html +4 -4
- package/dist/docs/example-agents/concierge.html +4 -4
- package/dist/docs/example-agents/fsd.html +4 -4
- package/dist/docs/example-agents/index.html +6 -6
- package/dist/docs/example-agents/knowledge-base.html +4 -4
- package/dist/docs/example-agents/oncall.html +34 -0
- package/dist/docs/example-agents/security-reviewer.html +4 -4
- package/dist/docs/example-agents/slack-agent.html +4 -4
- package/dist/docs/example-agents/weather-agent.html +4 -4
- package/dist/docs/guides/agent-to-agent.html +4 -4
- package/dist/docs/guides/cloud-runtime.html +4 -4
- package/dist/docs/guides/github.html +4 -4
- package/dist/docs/guides/human-in-the-loop.html +4 -4
- package/dist/docs/guides/slack.html +7 -6
- package/dist/docs/guides/webhooks.html +4 -4
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +4 -4
- package/dist/docs/index.html +4 -4
- package/dist/docs/quickstart.html +4 -4
- package/dist/docs/reference/agent-config.html +4 -4
- package/dist/docs/reference/channels.html +4 -4
- package/dist/docs/reference/cli.html +4 -4
- package/dist/docs/reference/connections.html +4 -4
- package/dist/docs/reference/hooks.html +4 -4
- package/dist/docs/reference/http-api.html +4 -4
- package/dist/docs/reference/instructions.html +4 -4
- package/dist/docs/reference/playground.html +4 -4
- package/dist/docs/reference/project-layout.html +4 -4
- package/dist/docs/reference/schedules.html +4 -4
- package/dist/docs/reference/sessions.html +5 -5
- package/dist/docs/reference/skills.html +4 -4
- package/dist/docs/reference/subagents.html +4 -4
- package/dist/docs/reference/tools.html +4 -4
- package/dist/docs/scaffolding-agents.html +4 -4
- package/dist/docs/storage.html +7 -6
- package/dist/docs/troubleshooting.html +4 -4
- package/dist/evals.d.ts +2 -2
- package/dist/internal/cli-ax.d.ts +23 -10
- package/dist/internal/cli-ax.d.ts.map +1 -1
- package/dist/internal/cli-ax.js +358 -55
- package/dist/internal/cli-mcp.d.ts +64 -0
- package/dist/internal/cli-mcp.d.ts.map +1 -0
- package/dist/internal/cli-mcp.js +570 -0
- package/dist/internal/distribution.d.ts.map +1 -1
- package/dist/internal/distribution.js +2 -0
- package/dist/internal/eval-run-store.d.ts +2 -0
- package/dist/internal/eval-run-store.d.ts.map +1 -1
- package/dist/internal/eval-run-store.js +4 -4
- package/dist/internal/evals-client.d.ts +96 -0
- package/dist/internal/evals-client.d.ts.map +1 -0
- package/dist/internal/evals-client.js +262 -0
- package/dist/internal/host-kv.d.ts +17 -0
- package/dist/internal/host-kv.d.ts.map +1 -0
- package/dist/internal/host-kv.js +77 -0
- package/dist/internal/init-project.js +3 -3
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +19 -2
- package/dist/internal/session-engine.d.ts +42 -7
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +247 -51
- package/dist/internal/storage-coordinator.d.ts +18 -1
- package/dist/internal/storage-coordinator.d.ts.map +1 -1
- package/dist/internal/storage-coordinator.js +43 -0
- package/dist/playground/assets/index-Bpr4Th4M.js +319 -0
- package/dist/playground/index.html +1 -1
- package/dist/storage-backends/file-kv.d.ts +42 -0
- package/dist/storage-backends/file-kv.d.ts.map +1 -0
- package/dist/storage-backends/file-kv.js +190 -0
- package/dist/storage-backends/postgres-kv.d.ts +77 -0
- package/dist/storage-backends/postgres-kv.d.ts.map +1 -0
- package/dist/storage-backends/postgres-kv.js +180 -0
- package/dist/storage.d.ts +10 -1
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +10 -1
- package/dist/types.d.ts +79 -0
- package/dist/types.d.ts.map +1 -1
- package/docs/example-agents/index.md +14 -9
- package/docs/example-agents/oncall.md +216 -0
- package/docs/guides/slack.md +11 -2
- package/docs/reference/sessions.md +13 -4
- package/docs/storage.md +27 -0
- package/package.json +15 -1
- package/skills/framework-map/SKILL.md +6 -4
- package/src/bin/agent-serve.ts +79 -18
- package/src/channels/slack/channel-watch.ts +28 -1
- package/src/channels/slack/cursor-account.ts +20 -0
- package/src/channels/slack/defaults.ts +51 -20
- package/src/channels/slack/dispatch.ts +215 -40
- package/src/channels/slack/inbound.ts +30 -10
- package/src/channels/slack/index.ts +4 -0
- package/src/channels/slack/live-delivery.ts +1 -0
- package/src/channels/slack/manifest.ts +3 -0
- package/src/channels/slack/nudge-carrier.ts +182 -0
- package/src/channels/slack/nudge-store.ts +308 -0
- package/src/channels/slack/slack-channel.ts +106 -31
- package/src/channels/slack/stop.ts +25 -0
- package/src/channels/slack/thread-context.ts +572 -0
- package/src/channels/slack/types.ts +27 -0
- package/src/evals.ts +2 -2
- package/src/internal/cli-ax.ts +466 -75
- package/src/internal/cli-mcp.ts +708 -0
- package/src/internal/distribution.ts +2 -0
- package/src/internal/eval-run-store.ts +5 -0
- package/src/internal/evals-client.ts +431 -0
- package/src/internal/host-kv.ts +85 -0
- package/src/internal/init-project.ts +3 -3
- package/src/internal/server.ts +43 -0
- package/src/internal/session-engine.ts +351 -50
- package/src/internal/storage-coordinator.ts +41 -0
- package/src/storage-backends/file-kv.ts +221 -0
- package/src/storage-backends/postgres-kv.ts +248 -0
- package/src/storage.ts +11 -1
- package/src/types.ts +83 -0
- package/dist/docs/assets/chunks/@localSearchIndexroot.O6eP7QZr.js +0 -1
- package/dist/docs/assets/example-agents_index.md.CRqJlnIf.js +0 -2
- package/dist/playground/assets/index-mux9aj5P.js +0 -319
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable Slack follow-up nudge store (Benny-style).
|
|
3
|
+
*
|
|
4
|
+
* Mid-turn inject and boundary resume share one exactly-once contract:
|
|
5
|
+
* pending nudges rename into delivered/ keyed by source message ts.
|
|
6
|
+
*/
|
|
7
|
+
export interface SlackNudgeRecord {
|
|
8
|
+
id: string;
|
|
9
|
+
continuationKey: string;
|
|
10
|
+
sourceTs: string;
|
|
11
|
+
text: string;
|
|
12
|
+
author?: string;
|
|
13
|
+
createdAt: string;
|
|
14
|
+
/** Content hash for pending dedupe. */
|
|
15
|
+
contentHash: string;
|
|
16
|
+
}
|
|
17
|
+
export interface SlackNudgeAppendInput {
|
|
18
|
+
continuationKey: string;
|
|
19
|
+
sourceTs: string;
|
|
20
|
+
text: string;
|
|
21
|
+
author?: string;
|
|
22
|
+
}
|
|
23
|
+
export declare function formatSlackFollowupNudgeBlock(nudges: readonly SlackNudgeRecord[]): string | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* Filesystem nudge store under `<root>/<continuation>/pending|delivered`.
|
|
26
|
+
*/
|
|
27
|
+
export declare class SlackNudgeStore {
|
|
28
|
+
private readonly rootDir;
|
|
29
|
+
constructor(rootDir: string);
|
|
30
|
+
private continuationDir;
|
|
31
|
+
private pendingDir;
|
|
32
|
+
private deliveredDir;
|
|
33
|
+
private deliveredPath;
|
|
34
|
+
append(input: SlackNudgeAppendInput): Promise<boolean>;
|
|
35
|
+
listPending(continuationKey: string): Promise<SlackNudgeRecord[]>;
|
|
36
|
+
isDelivered(continuationKey: string, sourceTs: string): Promise<boolean>;
|
|
37
|
+
hasUndelivered(continuationKey: string): Promise<boolean>;
|
|
38
|
+
/**
|
|
39
|
+
* Atomically move all pending nudges to delivered and return them.
|
|
40
|
+
* Already-delivered source ts values are dropped (exactly-once).
|
|
41
|
+
*/
|
|
42
|
+
drainForInject(continuationKey: string): Promise<SlackNudgeRecord[]>;
|
|
43
|
+
clear(continuationKey: string): Promise<void>;
|
|
44
|
+
/** Drain + format for tool/hook inject. */
|
|
45
|
+
drainFormatted(continuationKey: string): Promise<string | undefined>;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Durable ask claim store: one marker per `channelId:message.ts` so delayed
|
|
49
|
+
* Slack redelivery cannot start a second full answer.
|
|
50
|
+
*/
|
|
51
|
+
export declare class SlackAskDedupeStore {
|
|
52
|
+
private readonly rootDir;
|
|
53
|
+
constructor(rootDir: string);
|
|
54
|
+
private markerPath;
|
|
55
|
+
/** Returns true the first time this ask is claimed. */
|
|
56
|
+
tryClaim(channelId: string, messageTs: string): Promise<boolean>;
|
|
57
|
+
/** Drop a claim so a failed delivery can be retried. */
|
|
58
|
+
release(channelId: string, messageTs: string): Promise<void>;
|
|
59
|
+
wasClaimed(channelId: string, messageTs: string): Promise<boolean>;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=nudge-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nudge-store.d.ts","sourceRoot":"","sources":["../../../src/channels/slack/nudge-store.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAaH,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,qBAAqB;IACpC,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AA+BD,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,SAAS,gBAAgB,EAAE,GAClC,MAAM,GAAG,SAAS,CAoBpB;AAED;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IAEjC,YAAY,OAAO,EAAE,MAAM,EAE1B;IAED,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,aAAa;IAIf,MAAM,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,OAAO,CAAC,CAqC3D;IAEK,WAAW,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CA6BtE;IAEK,WAAW,CACf,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,CAAC,CAUlB;IAEK,cAAc,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAE9D;IAED;;;OAGG;IACG,cAAc,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CA8BzE;IAEK,KAAK,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKlD;IAED,2CAA2C;IACrC,cAAc,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAIzE;CACF;AAED;;;GAGG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IAEjC,YAAY,OAAO,EAAE,MAAM,EAE1B;IAED,OAAO,CAAC,UAAU;IAOlB,uDAAuD;IACjD,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAerE;IAED,wDAAwD;IAClD,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAKjE;IAEK,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOvE;CACF"}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable Slack follow-up nudge store (Benny-style).
|
|
3
|
+
*
|
|
4
|
+
* Mid-turn inject and boundary resume share one exactly-once contract:
|
|
5
|
+
* pending nudges rename into delivered/ keyed by source message ts.
|
|
6
|
+
*/
|
|
7
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
8
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
9
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
10
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
11
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
12
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
13
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
17
|
+
import { mkdir, readdir, readFile, rename, rm, writeFile, } from "node:fs/promises";
|
|
18
|
+
import { join } from "node:path";
|
|
19
|
+
/**
|
|
20
|
+
* Map an arbitrary key onto a single path segment under the store root.
|
|
21
|
+
* Rejects empty / `.` / `..` and strips remaining dots so path traversal
|
|
22
|
+
* cannot escape `<stateRoot>/slack-nudges` (or `slack-asks`).
|
|
23
|
+
*/
|
|
24
|
+
function safeKey(continuationKey) {
|
|
25
|
+
const trimmed = continuationKey.trim();
|
|
26
|
+
if (trimmed === "" || trimmed === "." || trimmed === "..") {
|
|
27
|
+
return "_empty";
|
|
28
|
+
}
|
|
29
|
+
const sanitized = trimmed
|
|
30
|
+
.replace(/[^a-zA-Z0-9._-]+/g, "_")
|
|
31
|
+
.replace(/^\.+/, "")
|
|
32
|
+
.replace(/\.+/g, "_")
|
|
33
|
+
.slice(0, 180);
|
|
34
|
+
if (sanitized === "" || sanitized === "." || sanitized === "..") {
|
|
35
|
+
return "_empty";
|
|
36
|
+
}
|
|
37
|
+
return sanitized;
|
|
38
|
+
}
|
|
39
|
+
function contentHash(text) {
|
|
40
|
+
return createHash("sha256").update(text).digest("hex").slice(0, 16);
|
|
41
|
+
}
|
|
42
|
+
function newNudgeId() {
|
|
43
|
+
return `nudge_${randomBytes(8).toString("hex")}`;
|
|
44
|
+
}
|
|
45
|
+
export function formatSlackFollowupNudgeBlock(nudges) {
|
|
46
|
+
if (nudges.length === 0) {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
const lines = [
|
|
50
|
+
"<slack_followup>",
|
|
51
|
+
"New Slack follow-up(s) arrived while you were working. Incorporate them before your final reply. If an ask was already answered in-thread, ack briefly instead of re-answering.",
|
|
52
|
+
"",
|
|
53
|
+
];
|
|
54
|
+
for (const nudge of nudges) {
|
|
55
|
+
const who = nudge.author === undefined || nudge.author === ""
|
|
56
|
+
? "someone"
|
|
57
|
+
: nudge.author;
|
|
58
|
+
lines.push(`- ${who} @ ${nudge.sourceTs}:`);
|
|
59
|
+
lines.push(nudge.text.trim() === "" ? "(empty)" : nudge.text.trim());
|
|
60
|
+
lines.push("");
|
|
61
|
+
}
|
|
62
|
+
lines.push("</slack_followup>");
|
|
63
|
+
return lines.join("\n").trimEnd();
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Filesystem nudge store under `<root>/<continuation>/pending|delivered`.
|
|
67
|
+
*/
|
|
68
|
+
export class SlackNudgeStore {
|
|
69
|
+
constructor(rootDir) {
|
|
70
|
+
this.rootDir = rootDir;
|
|
71
|
+
}
|
|
72
|
+
continuationDir(continuationKey) {
|
|
73
|
+
return join(this.rootDir, safeKey(continuationKey));
|
|
74
|
+
}
|
|
75
|
+
pendingDir(continuationKey) {
|
|
76
|
+
return join(this.continuationDir(continuationKey), "pending");
|
|
77
|
+
}
|
|
78
|
+
deliveredDir(continuationKey) {
|
|
79
|
+
return join(this.continuationDir(continuationKey), "delivered");
|
|
80
|
+
}
|
|
81
|
+
deliveredPath(continuationKey, sourceTs) {
|
|
82
|
+
return join(this.deliveredDir(continuationKey), safeKey(sourceTs));
|
|
83
|
+
}
|
|
84
|
+
append(input) {
|
|
85
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
86
|
+
const continuationKey = input.continuationKey.trim();
|
|
87
|
+
const sourceTs = input.sourceTs.trim();
|
|
88
|
+
const text = input.text.trim();
|
|
89
|
+
if (continuationKey === "" || sourceTs === "" || text === "") {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
if (yield this.isDelivered(continuationKey, sourceTs)) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
const pending = yield this.listPending(continuationKey);
|
|
96
|
+
const hash = contentHash(text);
|
|
97
|
+
if (pending.some((item) => item.sourceTs === sourceTs || item.contentHash === hash)) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
const record = Object.assign(Object.assign({ id: newNudgeId(), continuationKey,
|
|
101
|
+
sourceTs,
|
|
102
|
+
text }, (input.author === undefined || input.author === ""
|
|
103
|
+
? {}
|
|
104
|
+
: { author: input.author })), { createdAt: new Date().toISOString(), contentHash: hash });
|
|
105
|
+
const dir = this.pendingDir(continuationKey);
|
|
106
|
+
yield mkdir(dir, { recursive: true });
|
|
107
|
+
const target = join(dir, `${record.id}.json`);
|
|
108
|
+
const tmp = `${target}.${process.pid}.tmp`;
|
|
109
|
+
yield writeFile(tmp, `${JSON.stringify(record)}\n`, "utf8");
|
|
110
|
+
yield rename(tmp, target);
|
|
111
|
+
return true;
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
listPending(continuationKey) {
|
|
115
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
116
|
+
const dir = this.pendingDir(continuationKey);
|
|
117
|
+
let names;
|
|
118
|
+
try {
|
|
119
|
+
names = yield readdir(dir);
|
|
120
|
+
}
|
|
121
|
+
catch (_a) {
|
|
122
|
+
return [];
|
|
123
|
+
}
|
|
124
|
+
const records = [];
|
|
125
|
+
for (const name of names) {
|
|
126
|
+
if (!name.endsWith(".json")) {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
const raw = yield readFile(join(dir, name), "utf8");
|
|
131
|
+
const parsed = JSON.parse(raw);
|
|
132
|
+
if (typeof parsed.id === "string" &&
|
|
133
|
+
typeof parsed.sourceTs === "string" &&
|
|
134
|
+
typeof parsed.text === "string") {
|
|
135
|
+
records.push(parsed);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
catch (_b) {
|
|
139
|
+
// Skip corrupt pending files.
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
records.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
143
|
+
return records;
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
isDelivered(continuationKey, sourceTs) {
|
|
147
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
148
|
+
if (continuationKey.trim() === "" || sourceTs.trim() === "") {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
yield readFile(this.deliveredPath(continuationKey, sourceTs), "utf8");
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
catch (_a) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
hasUndelivered(continuationKey) {
|
|
161
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
162
|
+
return (yield this.listPending(continuationKey)).length > 0;
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Atomically move all pending nudges to delivered and return them.
|
|
167
|
+
* Already-delivered source ts values are dropped (exactly-once).
|
|
168
|
+
*/
|
|
169
|
+
drainForInject(continuationKey) {
|
|
170
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
171
|
+
const pending = yield this.listPending(continuationKey);
|
|
172
|
+
if (pending.length === 0) {
|
|
173
|
+
return [];
|
|
174
|
+
}
|
|
175
|
+
yield mkdir(this.deliveredDir(continuationKey), { recursive: true });
|
|
176
|
+
const delivered = [];
|
|
177
|
+
for (const record of pending) {
|
|
178
|
+
const pendingPath = join(this.pendingDir(continuationKey), `${record.id}.json`);
|
|
179
|
+
if (yield this.isDelivered(continuationKey, record.sourceTs)) {
|
|
180
|
+
yield rm(pendingPath, { force: true });
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
const dest = this.deliveredPath(continuationKey, record.sourceTs);
|
|
184
|
+
const tmp = `${dest}.${process.pid}.tmp`;
|
|
185
|
+
try {
|
|
186
|
+
yield writeFile(tmp, `${JSON.stringify(record)}\n`, "utf8");
|
|
187
|
+
yield rename(tmp, dest);
|
|
188
|
+
yield rm(pendingPath, { force: true });
|
|
189
|
+
delivered.push(record);
|
|
190
|
+
}
|
|
191
|
+
catch (_a) {
|
|
192
|
+
yield rm(tmp, { force: true }).catch(() => { });
|
|
193
|
+
// Race: another drain won — treat as delivered.
|
|
194
|
+
yield rm(pendingPath, { force: true }).catch(() => { });
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return delivered;
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
clear(continuationKey) {
|
|
201
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
202
|
+
yield rm(this.continuationDir(continuationKey), {
|
|
203
|
+
recursive: true,
|
|
204
|
+
force: true,
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
/** Drain + format for tool/hook inject. */
|
|
209
|
+
drainFormatted(continuationKey) {
|
|
210
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
211
|
+
return formatSlackFollowupNudgeBlock(yield this.drainForInject(continuationKey));
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Durable ask claim store: one marker per `channelId:message.ts` so delayed
|
|
217
|
+
* Slack redelivery cannot start a second full answer.
|
|
218
|
+
*/
|
|
219
|
+
export class SlackAskDedupeStore {
|
|
220
|
+
constructor(rootDir) {
|
|
221
|
+
this.rootDir = rootDir;
|
|
222
|
+
}
|
|
223
|
+
markerPath(channelId, messageTs) {
|
|
224
|
+
return join(this.rootDir, `${safeKey(channelId)}__${safeKey(messageTs)}.claimed`);
|
|
225
|
+
}
|
|
226
|
+
/** Returns true the first time this ask is claimed. */
|
|
227
|
+
tryClaim(channelId, messageTs) {
|
|
228
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
229
|
+
if (channelId.trim() === "" || messageTs.trim() === "") {
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
const path = this.markerPath(channelId, messageTs);
|
|
233
|
+
yield mkdir(this.rootDir, { recursive: true });
|
|
234
|
+
try {
|
|
235
|
+
yield writeFile(path, `${new Date().toISOString()}\n`, {
|
|
236
|
+
encoding: "utf8",
|
|
237
|
+
flag: "wx",
|
|
238
|
+
});
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
catch (_a) {
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
/** Drop a claim so a failed delivery can be retried. */
|
|
247
|
+
release(channelId, messageTs) {
|
|
248
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
249
|
+
if (channelId.trim() === "" || messageTs.trim() === "") {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
yield rm(this.markerPath(channelId, messageTs), { force: true });
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
wasClaimed(channelId, messageTs) {
|
|
256
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
257
|
+
try {
|
|
258
|
+
yield readFile(this.markerPath(channelId, messageTs), "utf8");
|
|
259
|
+
return true;
|
|
260
|
+
}
|
|
261
|
+
catch (_a) {
|
|
262
|
+
return false;
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slack-channel.d.ts","sourceRoot":"","sources":["../../../src/channels/slack/slack-channel.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EACV,iBAAiB,EAMlB,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"slack-channel.d.ts","sourceRoot":"","sources":["../../../src/channels/slack/slack-channel.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EACV,iBAAiB,EAMlB,MAAM,gBAAgB,CAAC;AAsCxB,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EAC1B,MAAM,YAAY,CAAC;AAEpB;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,EAC7D,OAAO,GAAE,IAAI,CAAC,mBAAmB,EAAE,WAAW,CAAC,GAAG;IAChD,SAAS,CAAC,EAAE,oBAAoB,CAAC,IAAI,CAAC,CAAC;CACnC,GACL,iBAAiB,CAAC,iBAAiB,CAAC,CAqYtC"}
|
|
@@ -20,7 +20,7 @@ import { slackContinuationKey } from "./api.js";
|
|
|
20
20
|
import { createChannelPostWatcher, resolveChannelNames, SlackChannelAllowlist, } from "./channel-watch.js";
|
|
21
21
|
import { credentialsFromEnv, slackEnvKeys, tryResolveAppToken, tryResolveBotToken, } from "./credentials.js";
|
|
22
22
|
import { createPendingSlackApiTransport, registerSlackCursorAccountRuntime, } from "./cursor-account.js";
|
|
23
|
-
import { buildDefaultEvents,
|
|
23
|
+
import { buildDefaultEvents, createDefaultOnChannelPost, createDefaultOnInbound, } from "./defaults.js";
|
|
24
24
|
import { dispatchInboundFromEventBody, dispatchInboundMessage, emptySlackChannelState, } from "./dispatch.js";
|
|
25
25
|
import { createEventIdDedupe } from "./event-id-dedupe.js";
|
|
26
26
|
import { dispatchInteractiveFromPayload } from "./interactive.js";
|
|
@@ -82,9 +82,11 @@ export function slackChannel(options = {}) {
|
|
|
82
82
|
? { botToken: accountTransport }
|
|
83
83
|
: ((_d = options.credentials) !== null && _d !== void 0 ? _d : credentialsFromEnv(options.envPrefix));
|
|
84
84
|
const envKeys = slackEnvKeys(options.envPrefix);
|
|
85
|
-
const
|
|
86
|
-
const
|
|
87
|
-
const
|
|
85
|
+
const loadingMessages = options.loadingMessages;
|
|
86
|
+
const defaultInbound = createDefaultOnInbound(loadingMessages);
|
|
87
|
+
const onAppMention = (_e = options.onAppMention) !== null && _e !== void 0 ? _e : defaultInbound;
|
|
88
|
+
const onDirectMessage = (_f = options.onDirectMessage) !== null && _f !== void 0 ? _f : defaultInbound;
|
|
89
|
+
const onChannelPost = (_g = options.onChannelPost) !== null && _g !== void 0 ? _g : createDefaultOnChannelPost(loadingMessages);
|
|
88
90
|
const engagement = (_h = options.engagement) !== null && _h !== void 0 ? _h : {};
|
|
89
91
|
const mentionsEnabled = engagement.mentions !== false;
|
|
90
92
|
const directMessagesEnabled = engagement.directMessages !== false;
|
|
@@ -113,6 +115,7 @@ export function slackChannel(options = {}) {
|
|
|
113
115
|
thinkingSteps: options.thinkingSteps,
|
|
114
116
|
suggestedPrompts: options.suggestedPrompts,
|
|
115
117
|
suggestedPromptsTitle: options.suggestedPromptsTitle,
|
|
118
|
+
loadingMessages,
|
|
116
119
|
getLog,
|
|
117
120
|
}));
|
|
118
121
|
}
|
|
@@ -123,11 +126,21 @@ export function slackChannel(options = {}) {
|
|
|
123
126
|
let socketHandle = null;
|
|
124
127
|
let channelWatch;
|
|
125
128
|
function inboundDeps(args) {
|
|
126
|
-
return Object.assign({ send: args.send, waitUntil: args.waitUntil, onAppMention,
|
|
129
|
+
return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ send: args.send, waitUntil: args.waitUntil, onAppMention,
|
|
127
130
|
onDirectMessage,
|
|
128
131
|
credentials,
|
|
129
132
|
eventIds, host: args.host, log: getLog(), mentionsEnabled,
|
|
130
|
-
directMessagesEnabled }, (
|
|
133
|
+
directMessagesEnabled }, (args.hasContinuationSession === undefined
|
|
134
|
+
? {}
|
|
135
|
+
: { hasContinuationSession: args.hasContinuationSession })), (args.isContinuationBusy === undefined
|
|
136
|
+
? {}
|
|
137
|
+
: { isContinuationBusy: args.isContinuationBusy })), (args.getContinuationLastBotMessageTs === undefined
|
|
138
|
+
? {}
|
|
139
|
+
: {
|
|
140
|
+
getContinuationLastBotMessageTs: args.getContinuationLastBotMessageTs,
|
|
141
|
+
})), (args.interruptContinuation === undefined
|
|
142
|
+
? {}
|
|
143
|
+
: { interruptContinuation: args.interruptContinuation })), (channelWatch === undefined ? {} : { channelWatch }));
|
|
131
144
|
}
|
|
132
145
|
const definition = defineChannel({
|
|
133
146
|
auth: [allowAll()],
|
|
@@ -156,6 +169,7 @@ export function slackChannel(options = {}) {
|
|
|
156
169
|
});
|
|
157
170
|
return send(input.message, {
|
|
158
171
|
auth: input.auth,
|
|
172
|
+
admission: "coalesce",
|
|
159
173
|
continuationToken: slackContinuationKey(channelId, threadTs),
|
|
160
174
|
state: emptySlackChannelState({
|
|
161
175
|
channelId,
|
|
@@ -190,12 +204,15 @@ export function slackChannel(options = {}) {
|
|
|
190
204
|
if (channelPosts !== undefined) {
|
|
191
205
|
const allowlist = new SlackChannelAllowlist(channelPosts.allow);
|
|
192
206
|
let botUserId;
|
|
207
|
+
let botId;
|
|
193
208
|
try {
|
|
194
209
|
const { WebClient } = yield import("@slack/web-api");
|
|
195
210
|
const client = new WebClient(botToken);
|
|
196
211
|
const identity = yield client.auth.test();
|
|
197
212
|
botUserId =
|
|
198
213
|
typeof identity.user_id === "string" ? identity.user_id : undefined;
|
|
214
|
+
botId =
|
|
215
|
+
typeof identity.bot_id === "string" ? identity.bot_id : undefined;
|
|
199
216
|
const names = allowlist.unresolvedNames();
|
|
200
217
|
if (names.length > 0) {
|
|
201
218
|
allowlist.applyResolvedNames(yield resolveChannelNames(client, names, getLog()));
|
|
@@ -206,30 +223,60 @@ export function slackChannel(options = {}) {
|
|
|
206
223
|
error: error instanceof Error ? error.message : String(error),
|
|
207
224
|
});
|
|
208
225
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
226
|
+
if (channelPosts.includeBotPosts === true &&
|
|
227
|
+
botUserId === undefined &&
|
|
228
|
+
botId === undefined) {
|
|
229
|
+
getLog().error("channel watch disabled", {
|
|
230
|
+
reason: "includeBotPosts requires bot identity from auth.test (loop safety)",
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
channelWatch = createChannelPostWatcher({
|
|
235
|
+
options: channelPosts,
|
|
236
|
+
allowlist,
|
|
237
|
+
getBotUserId: () => botUserId,
|
|
238
|
+
getBotId: () => botId,
|
|
239
|
+
log: getLog(),
|
|
240
|
+
dispatch: (message, eventId) => {
|
|
241
|
+
args.waitUntil(dispatchInboundMessage(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ kind: "channel_post", message, handler: onChannelPost, send: args.send, credentials, host: args.host, log: getLog() }, (args.hasContinuationSession === undefined
|
|
242
|
+
? {}
|
|
243
|
+
: {
|
|
244
|
+
hasContinuationSession: args.hasContinuationSession,
|
|
245
|
+
})), (args.isContinuationBusy === undefined
|
|
246
|
+
? {}
|
|
247
|
+
: { isContinuationBusy: args.isContinuationBusy })), (args.getContinuationLastBotMessageTs === undefined
|
|
248
|
+
? {}
|
|
249
|
+
: {
|
|
250
|
+
getContinuationLastBotMessageTs: args.getContinuationLastBotMessageTs,
|
|
251
|
+
})), (args.interruptContinuation === undefined
|
|
252
|
+
? {}
|
|
253
|
+
: {
|
|
254
|
+
interruptContinuation: args.interruptContinuation,
|
|
255
|
+
})), (eventId === undefined ? {} : { eventId }))));
|
|
256
|
+
},
|
|
257
|
+
});
|
|
258
|
+
getLog().info("channel watch enabled", {
|
|
259
|
+
channels: channelPosts.allow.join(","),
|
|
260
|
+
posts: (_b = channelPosts.posts) !== null && _b !== void 0 ? _b : "top-level",
|
|
261
|
+
debounce_ms: (_c = channelPosts.debounceMs) !== null && _c !== void 0 ? _c : 0,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
223
264
|
}
|
|
224
265
|
const socketOptions = {
|
|
225
266
|
appToken,
|
|
226
267
|
logger: (line) => getLog().write(line),
|
|
227
268
|
onEvent: (payload) => {
|
|
228
|
-
dispatchInboundFromEventBody(payload, inboundDeps({
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
269
|
+
dispatchInboundFromEventBody(payload, inboundDeps(Object.assign(Object.assign(Object.assign(Object.assign({ send: args.send, waitUntil: args.waitUntil, host: args.host }, (args.hasContinuationSession === undefined
|
|
270
|
+
? {}
|
|
271
|
+
: { hasContinuationSession: args.hasContinuationSession })), (args.isContinuationBusy === undefined
|
|
272
|
+
? {}
|
|
273
|
+
: { isContinuationBusy: args.isContinuationBusy })), (args.getContinuationLastBotMessageTs === undefined
|
|
274
|
+
? {}
|
|
275
|
+
: {
|
|
276
|
+
getContinuationLastBotMessageTs: args.getContinuationLastBotMessageTs,
|
|
277
|
+
})), (args.interruptContinuation === undefined
|
|
278
|
+
? {}
|
|
279
|
+
: { interruptContinuation: args.interruptContinuation }))));
|
|
233
280
|
},
|
|
234
281
|
};
|
|
235
282
|
if (routeInteractive) {
|
|
@@ -264,11 +311,13 @@ export function slackChannel(options = {}) {
|
|
|
264
311
|
});
|
|
265
312
|
},
|
|
266
313
|
onStop: () => __awaiter(this, void 0, void 0, function* () {
|
|
314
|
+
var _a;
|
|
267
315
|
getLog().info("channel stop");
|
|
268
316
|
channelWatch === null || channelWatch === void 0 ? void 0 : channelWatch.stop();
|
|
269
317
|
channelWatch = undefined;
|
|
270
318
|
yield socketHandle === null || socketHandle === void 0 ? void 0 : socketHandle.stop();
|
|
271
319
|
socketHandle = null;
|
|
320
|
+
yield (_a = options.onStop) === null || _a === void 0 ? void 0 : _a.call(options);
|
|
272
321
|
}),
|
|
273
322
|
});
|
|
274
323
|
if (accountTransport !== undefined) {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whole-message Slack "stop" directive (Benny parity).
|
|
3
|
+
*
|
|
4
|
+
* A message whose body is only `@bot stop` / `stop` cancels the active turn.
|
|
5
|
+
* The word "stop" embedded in a longer message is not a directive.
|
|
6
|
+
*/
|
|
7
|
+
/** Strip leading Slack user mentions and surrounding whitespace. */
|
|
8
|
+
export declare function stripLeadingSlackMentions(text: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* True when the message (after stripping @mentions) is exactly `stop`
|
|
11
|
+
* (case-insensitive).
|
|
12
|
+
*/
|
|
13
|
+
export declare function isWholeMessageStop(text: string): boolean;
|
|
14
|
+
//# sourceMappingURL=stop.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stop.d.ts","sourceRoot":"","sources":["../../../src/channels/slack/stop.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,oEAAoE;AACpE,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAM9D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAExD"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whole-message Slack "stop" directive (Benny parity).
|
|
3
|
+
*
|
|
4
|
+
* A message whose body is only `@bot stop` / `stop` cancels the active turn.
|
|
5
|
+
* The word "stop" embedded in a longer message is not a directive.
|
|
6
|
+
*/
|
|
7
|
+
const MENTION_RE = /^<@[A-Z0-9]+>\s*/i;
|
|
8
|
+
/** Strip leading Slack user mentions and surrounding whitespace. */
|
|
9
|
+
export function stripLeadingSlackMentions(text) {
|
|
10
|
+
let current = text.trim();
|
|
11
|
+
while (MENTION_RE.test(current)) {
|
|
12
|
+
current = current.replace(MENTION_RE, "").trim();
|
|
13
|
+
}
|
|
14
|
+
return current;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* True when the message (after stripping @mentions) is exactly `stop`
|
|
18
|
+
* (case-insensitive).
|
|
19
|
+
*/
|
|
20
|
+
export function isWholeMessageStop(text) {
|
|
21
|
+
return stripLeadingSlackMentions(text).toLowerCase() === "stop";
|
|
22
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch prior Slack thread messages and format them for the model prompt.
|
|
3
|
+
*
|
|
4
|
+
* Cold-start mentions (`@agent what happened here?`) only carry the mention
|
|
5
|
+
* text; the parent alert / earlier replies live in `conversations.replies`.
|
|
6
|
+
*/
|
|
7
|
+
import { type SlackLog } from "./log.js";
|
|
8
|
+
import type { SlackBotToken } from "./types.js";
|
|
9
|
+
/** Keep the thread root plus the newest replies when over the fetch cap. */
|
|
10
|
+
export declare function trimThreadMessagesForPrompt(messages: JsonRecord[], options: {
|
|
11
|
+
threadTs: string;
|
|
12
|
+
limit: number;
|
|
13
|
+
excludeTs?: string | readonly string[];
|
|
14
|
+
}): JsonRecord[];
|
|
15
|
+
type JsonRecord = Record<string, unknown>;
|
|
16
|
+
/** Pull readable text out of Slack attachment / Block Kit payloads. */
|
|
17
|
+
export declare function slackApiMessageBody(message: JsonRecord): string;
|
|
18
|
+
/** True when `ts` is strictly after `afterTs`. */
|
|
19
|
+
export declare function isSlackTsAfter(ts: string, afterTs: string): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Merge triggering messages (by ts) into a replies window when Slack lag
|
|
22
|
+
* omits them from `conversations.replies`.
|
|
23
|
+
*/
|
|
24
|
+
export declare function mergeThreadMessagesByTs(messages: JsonRecord[], extras: readonly JsonRecord[]): JsonRecord[];
|
|
25
|
+
/**
|
|
26
|
+
* Latest bot-authored message ts in a thread (own posts), used as the warm
|
|
27
|
+
* delta watermark when channel state has not recorded one yet.
|
|
28
|
+
*/
|
|
29
|
+
export declare function findLastBotMessageTs(messages: readonly JsonRecord[], options?: {
|
|
30
|
+
botUserId?: string;
|
|
31
|
+
botId?: string;
|
|
32
|
+
}): string | undefined;
|
|
33
|
+
/** Messages strictly after `afterTs`, excluding listed timestamps. */
|
|
34
|
+
export declare function selectThreadDeltaMessages(messages: JsonRecord[], options: {
|
|
35
|
+
afterTs?: string;
|
|
36
|
+
excludeTs?: string | readonly string[];
|
|
37
|
+
}): JsonRecord[];
|
|
38
|
+
export declare function formatSlackThreadContextBlock(messages: JsonRecord[], options?: {
|
|
39
|
+
threadTs?: string;
|
|
40
|
+
excludeTs?: string | readonly string[];
|
|
41
|
+
maxChars?: number;
|
|
42
|
+
}): string | undefined;
|
|
43
|
+
export declare function formatSlackThreadDeltaBlock(messages: JsonRecord[], options?: {
|
|
44
|
+
excludeTs?: string | readonly string[];
|
|
45
|
+
afterTs?: string;
|
|
46
|
+
maxChars?: number;
|
|
47
|
+
}): string | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* Load prior messages in a Slack thread and return a prompt context block.
|
|
50
|
+
* Soft-fails (returns undefined) when credentials are missing or the API errors.
|
|
51
|
+
*/
|
|
52
|
+
export declare function loadSlackThreadContextBlock(args: {
|
|
53
|
+
botToken: SlackBotToken | undefined;
|
|
54
|
+
channelId: string;
|
|
55
|
+
threadTs: string;
|
|
56
|
+
excludeTs?: string | readonly string[];
|
|
57
|
+
log?: SlackLog;
|
|
58
|
+
limit?: number;
|
|
59
|
+
maxChars?: number;
|
|
60
|
+
/** Extra messages merged by ts when Slack replies lag (Benny parity). */
|
|
61
|
+
mergeMessages?: readonly JsonRecord[];
|
|
62
|
+
}): Promise<string | undefined>;
|
|
63
|
+
/**
|
|
64
|
+
* Warm-turn delta: messages since the last bot post (or all new replies when
|
|
65
|
+
* the watermark is unknown), with optional merge-by-ts for the triggering ask.
|
|
66
|
+
*/
|
|
67
|
+
export declare function loadSlackThreadDeltaBlock(args: {
|
|
68
|
+
botToken: SlackBotToken | undefined;
|
|
69
|
+
channelId: string;
|
|
70
|
+
threadTs: string;
|
|
71
|
+
/** Watermark from channel state (`lastBotMessageTs`). */
|
|
72
|
+
afterTs?: string;
|
|
73
|
+
excludeTs?: string | readonly string[];
|
|
74
|
+
log?: SlackLog;
|
|
75
|
+
maxChars?: number;
|
|
76
|
+
mergeMessages?: readonly JsonRecord[];
|
|
77
|
+
botUserId?: string;
|
|
78
|
+
botId?: string;
|
|
79
|
+
}): Promise<string | undefined>;
|
|
80
|
+
export {};
|
|
81
|
+
//# sourceMappingURL=thread-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thread-context.d.ts","sourceRoot":"","sources":["../../../src/channels/slack/thread-context.ts"],"names":[],"mappings":"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"}
|