@agentworkforce/sage 1.0.6 → 1.1.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.
- package/.env.example +5 -4
- package/dist/app.d.ts +0 -6
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +264 -217
- package/dist/integrations/cloud-proxy-provider.d.ts +42 -0
- package/dist/integrations/cloud-proxy-provider.d.ts.map +1 -0
- package/dist/integrations/cloud-proxy-provider.js +139 -0
- package/dist/integrations/freshness-envelope.d.ts +17 -0
- package/dist/integrations/freshness-envelope.d.ts.map +1 -0
- package/dist/integrations/freshness-envelope.js +41 -0
- package/dist/integrations/freshness-envelope.test.d.ts +2 -0
- package/dist/integrations/freshness-envelope.test.d.ts.map +1 -0
- package/dist/integrations/freshness-envelope.test.js +56 -0
- package/dist/integrations/github-context.d.ts +2 -1
- package/dist/integrations/github-context.d.ts.map +1 -1
- package/dist/integrations/github-context.js +4 -2
- package/dist/integrations/github.d.ts +4 -2
- package/dist/integrations/github.d.ts.map +1 -1
- package/dist/integrations/github.js +16 -11
- package/dist/integrations/recent-actions-overlay.d.ts +31 -0
- package/dist/integrations/recent-actions-overlay.d.ts.map +1 -0
- package/dist/integrations/recent-actions-overlay.js +80 -0
- package/dist/integrations/recent-actions-overlay.test.d.ts +2 -0
- package/dist/integrations/recent-actions-overlay.test.d.ts.map +1 -0
- package/dist/integrations/recent-actions-overlay.test.js +145 -0
- package/dist/integrations/slack-egress.d.ts +28 -0
- package/dist/integrations/slack-egress.d.ts.map +1 -0
- package/dist/integrations/slack-egress.js +181 -0
- package/dist/integrations/slack-ingress.d.ts +26 -0
- package/dist/integrations/slack-ingress.d.ts.map +1 -0
- package/dist/integrations/slack-ingress.js +31 -0
- package/dist/memory/org-memory.d.ts.map +1 -1
- package/dist/memory/org-memory.js +1 -0
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +1 -0
- package/dist/nango.d.ts +1 -6
- package/dist/nango.d.ts.map +1 -1
- package/dist/nango.js +9 -34
- package/dist/openrouter.d.ts.map +1 -1
- package/dist/openrouter.js +2 -1
- package/dist/proactive/context-watcher.d.ts +2 -2
- package/dist/proactive/context-watcher.d.ts.map +1 -1
- package/dist/proactive/context-watcher.js +5 -3
- package/dist/proactive/engine.d.ts +5 -9
- package/dist/proactive/engine.d.ts.map +1 -1
- package/dist/proactive/engine.js +25 -20
- package/dist/proactive/evidence-sources/affirmative-reply-source.d.ts.map +1 -1
- package/dist/proactive/evidence-sources/affirmative-reply-source.js +4 -2
- package/dist/proactive/evidence-sources/explicit-close-source.d.ts.map +1 -1
- package/dist/proactive/evidence-sources/explicit-close-source.js +4 -2
- package/dist/proactive/evidence-sources/pr-merge-source.d.ts.map +1 -1
- package/dist/proactive/evidence-sources/pr-merge-source.js +12 -5
- package/dist/proactive/evidence-sources/reaction-source.d.ts.map +1 -1
- package/dist/proactive/evidence-sources/reaction-source.js +6 -15
- package/dist/proactive/follow-up-checker.d.ts +4 -4
- package/dist/proactive/follow-up-checker.d.ts.map +1 -1
- package/dist/proactive/follow-up-checker.js +40 -21
- package/dist/proactive/integrations/slack-egress.d.ts +2 -0
- package/dist/proactive/integrations/slack-egress.d.ts.map +1 -0
- package/dist/proactive/integrations/slack-egress.js +1 -0
- package/dist/proactive/pr-matcher.d.ts +2 -2
- package/dist/proactive/pr-matcher.d.ts.map +1 -1
- package/dist/proactive/pr-matcher.js +8 -6
- package/dist/proactive/stale-thread-detector.d.ts +2 -2
- package/dist/proactive/stale-thread-detector.d.ts.map +1 -1
- package/dist/proactive/stale-thread-detector.js +16 -23
- package/dist/proactive/types.d.ts +8 -6
- package/dist/proactive/types.d.ts.map +1 -1
- package/dist/slack.d.ts +3 -13
- package/dist/slack.d.ts.map +1 -1
- package/dist/slack.js +7 -108
- package/dist/types.d.ts +1 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -1
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { RecentActionsOverlay } from "./recent-actions-overlay.js";
|
|
3
|
+
describe("RecentActionsOverlay", () => {
|
|
4
|
+
const now = new Date("2026-04-15T12:00:00.000Z");
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
vi.useFakeTimers();
|
|
7
|
+
vi.setSystemTime(now);
|
|
8
|
+
});
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
vi.useRealTimers();
|
|
11
|
+
});
|
|
12
|
+
it("records and looks up entries round-trip", () => {
|
|
13
|
+
const overlay = new RecentActionsOverlay();
|
|
14
|
+
const entry = {
|
|
15
|
+
path: "/messages/123",
|
|
16
|
+
data: { text: "hello" },
|
|
17
|
+
provider: "slack",
|
|
18
|
+
action: "message.create",
|
|
19
|
+
};
|
|
20
|
+
overlay.record(entry);
|
|
21
|
+
expect(overlay.lookup(entry.path)).toEqual({
|
|
22
|
+
...entry,
|
|
23
|
+
writtenAt: now.getTime(),
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
it("returns null for lookups after ttlMs expires", () => {
|
|
27
|
+
const overlay = new RecentActionsOverlay({ ttlMs: 1_000 });
|
|
28
|
+
overlay.record({
|
|
29
|
+
path: "/messages/expired",
|
|
30
|
+
data: { text: "old" },
|
|
31
|
+
provider: "slack",
|
|
32
|
+
action: "message.create",
|
|
33
|
+
});
|
|
34
|
+
vi.advanceTimersByTime(1_001);
|
|
35
|
+
expect(overlay.lookup("/messages/expired")).toBeNull();
|
|
36
|
+
});
|
|
37
|
+
it("evicts the least recently used entry when maxEntries is exceeded", () => {
|
|
38
|
+
const overlay = new RecentActionsOverlay({ maxEntries: 2 });
|
|
39
|
+
overlay.record({
|
|
40
|
+
path: "/messages/oldest",
|
|
41
|
+
data: { text: "oldest" },
|
|
42
|
+
provider: "slack",
|
|
43
|
+
action: "message.create",
|
|
44
|
+
});
|
|
45
|
+
overlay.record({
|
|
46
|
+
path: "/messages/evicted",
|
|
47
|
+
data: { text: "evicted" },
|
|
48
|
+
provider: "slack",
|
|
49
|
+
action: "message.create",
|
|
50
|
+
});
|
|
51
|
+
expect(overlay.lookup("/messages/oldest")).not.toBeNull();
|
|
52
|
+
overlay.record({
|
|
53
|
+
path: "/messages/newest",
|
|
54
|
+
data: { text: "newest" },
|
|
55
|
+
provider: "slack",
|
|
56
|
+
action: "message.create",
|
|
57
|
+
});
|
|
58
|
+
expect(overlay.lookup("/messages/evicted")).toBeNull();
|
|
59
|
+
expect(overlay.lookup("/messages/oldest")).toMatchObject({
|
|
60
|
+
path: "/messages/oldest",
|
|
61
|
+
});
|
|
62
|
+
expect(overlay.lookup("/messages/newest")).toMatchObject({
|
|
63
|
+
path: "/messages/newest",
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
it("searches matching entries", () => {
|
|
67
|
+
const overlay = new RecentActionsOverlay();
|
|
68
|
+
overlay.record({
|
|
69
|
+
path: "/channels/general/messages/1",
|
|
70
|
+
data: { text: "first" },
|
|
71
|
+
provider: "slack",
|
|
72
|
+
action: "message.create",
|
|
73
|
+
});
|
|
74
|
+
vi.advanceTimersByTime(10);
|
|
75
|
+
overlay.record({
|
|
76
|
+
path: "/repos/sage/issues/1",
|
|
77
|
+
data: { title: "bug" },
|
|
78
|
+
provider: "github",
|
|
79
|
+
action: "issue.create",
|
|
80
|
+
});
|
|
81
|
+
vi.advanceTimersByTime(10);
|
|
82
|
+
overlay.record({
|
|
83
|
+
path: "/channels/random/messages/2",
|
|
84
|
+
data: { text: "second" },
|
|
85
|
+
provider: "slack",
|
|
86
|
+
action: "message.create",
|
|
87
|
+
});
|
|
88
|
+
expect(overlay.search({
|
|
89
|
+
provider: "slack",
|
|
90
|
+
pathPrefix: "/channels/",
|
|
91
|
+
})).toEqual([
|
|
92
|
+
{
|
|
93
|
+
path: "/channels/random/messages/2",
|
|
94
|
+
data: { text: "second" },
|
|
95
|
+
provider: "slack",
|
|
96
|
+
action: "message.create",
|
|
97
|
+
writtenAt: now.getTime() + 20,
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
path: "/channels/general/messages/1",
|
|
101
|
+
data: { text: "first" },
|
|
102
|
+
provider: "slack",
|
|
103
|
+
action: "message.create",
|
|
104
|
+
writtenAt: now.getTime(),
|
|
105
|
+
},
|
|
106
|
+
]);
|
|
107
|
+
});
|
|
108
|
+
it("clears entries", () => {
|
|
109
|
+
const overlay = new RecentActionsOverlay();
|
|
110
|
+
overlay.record({
|
|
111
|
+
path: "/messages/1",
|
|
112
|
+
data: { text: "one" },
|
|
113
|
+
provider: "slack",
|
|
114
|
+
action: "message.create",
|
|
115
|
+
});
|
|
116
|
+
overlay.record({
|
|
117
|
+
path: "/messages/2",
|
|
118
|
+
data: { text: "two" },
|
|
119
|
+
provider: "slack",
|
|
120
|
+
action: "message.create",
|
|
121
|
+
});
|
|
122
|
+
overlay.clear();
|
|
123
|
+
expect(overlay.lookup("/messages/1")).toBeNull();
|
|
124
|
+
expect(overlay.lookup("/messages/2")).toBeNull();
|
|
125
|
+
expect(overlay.size).toBe(0);
|
|
126
|
+
});
|
|
127
|
+
it("reports the count of unexpired entries", () => {
|
|
128
|
+
const overlay = new RecentActionsOverlay({ ttlMs: 1_000 });
|
|
129
|
+
overlay.record({
|
|
130
|
+
path: "/messages/1",
|
|
131
|
+
data: { text: "one" },
|
|
132
|
+
provider: "slack",
|
|
133
|
+
action: "message.create",
|
|
134
|
+
});
|
|
135
|
+
overlay.record({
|
|
136
|
+
path: "/messages/2",
|
|
137
|
+
data: { text: "two" },
|
|
138
|
+
provider: "slack",
|
|
139
|
+
action: "message.create",
|
|
140
|
+
});
|
|
141
|
+
expect(overlay.size).toBe(2);
|
|
142
|
+
vi.advanceTimersByTime(1_001);
|
|
143
|
+
expect(overlay.size).toBe(0);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ConnectionProvider } from "./cloud-proxy-provider.js";
|
|
2
|
+
import { type ThreadMessage } from "../slack.js";
|
|
3
|
+
export interface SlackEgressResult {
|
|
4
|
+
ok: boolean;
|
|
5
|
+
ts?: string;
|
|
6
|
+
error?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class SlackEgressError extends Error {
|
|
9
|
+
readonly code: string;
|
|
10
|
+
readonly retryAfterMs?: number;
|
|
11
|
+
constructor(message: string, code?: string, retryAfterMs?: number);
|
|
12
|
+
}
|
|
13
|
+
export interface SlackReaction {
|
|
14
|
+
name: string;
|
|
15
|
+
count: number;
|
|
16
|
+
}
|
|
17
|
+
export interface SlackEgress {
|
|
18
|
+
postMessage(channel: string, text: string, threadTs?: string): Promise<SlackEgressResult>;
|
|
19
|
+
postMessageChunked(channel: string, text: string, threadTs?: string, maxChars?: number): Promise<SlackEgressResult>;
|
|
20
|
+
addReaction(channel: string, timestamp: string, emoji: string): Promise<void>;
|
|
21
|
+
fetchThreadHistory(channel: string, threadTs: string, botUserId?: string): Promise<ThreadMessage[]>;
|
|
22
|
+
getBotUserId(): Promise<string | undefined>;
|
|
23
|
+
getReactions(channel: string, timestamp: string): Promise<SlackReaction[]>;
|
|
24
|
+
}
|
|
25
|
+
export declare function createSlackEgress(provider: ConnectionProvider, workspaceId: string): SlackEgress;
|
|
26
|
+
export type { ConnectionProvider } from "./cloud-proxy-provider.js";
|
|
27
|
+
export type { ThreadMessage } from "../slack.js";
|
|
28
|
+
//# sourceMappingURL=slack-egress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slack-egress.d.ts","sourceRoot":"","sources":["../../src/integrations/slack-egress.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAgB,MAAM,2BAA2B,CAAC;AAElF,OAAO,EAIL,KAAK,aAAa,EACnB,MAAM,aAAa,CAAC;AA4BrB,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;gBAEnB,OAAO,EAAE,MAAM,EAAE,IAAI,SAAwB,EAAE,YAAY,CAAC,EAAE,MAAM;CAMjF;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC1F,kBAAkB,CAChB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,QAAQ,CAAC,EAAE,MAAM,EACjB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC9B,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9E,kBAAkB,CAChB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IAC5B,YAAY,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC5C,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;CAC5E;AA+ED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,GAAG,WAAW,CAkKhG;AAED,YAAY,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACpE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { SLACK_MESSAGE_MAX_CHARS, chunkSlackMessage, cleanSlackText, } from "../slack.js";
|
|
2
|
+
export class SlackEgressError extends Error {
|
|
3
|
+
code;
|
|
4
|
+
retryAfterMs;
|
|
5
|
+
constructor(message, code = "slack_egress_failed", retryAfterMs) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "SlackEgressError";
|
|
8
|
+
this.code = code;
|
|
9
|
+
this.retryAfterMs = retryAfterMs;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function isRecord(value) {
|
|
13
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14
|
+
}
|
|
15
|
+
function asString(value) {
|
|
16
|
+
return typeof value === "string" ? value : undefined;
|
|
17
|
+
}
|
|
18
|
+
function asNumber(value) {
|
|
19
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
20
|
+
}
|
|
21
|
+
function toSlackEgressError(error, fallbackCode = "slack_egress_failed") {
|
|
22
|
+
if (error instanceof SlackEgressError) {
|
|
23
|
+
return error;
|
|
24
|
+
}
|
|
25
|
+
if (isRecord(error)) {
|
|
26
|
+
const message = asString(error.message) ?? "Slack request failed";
|
|
27
|
+
const code = asString(error.code) ?? fallbackCode;
|
|
28
|
+
const retryAfterMs = asNumber(error.retryAfterMs);
|
|
29
|
+
return new SlackEgressError(message, code, retryAfterMs);
|
|
30
|
+
}
|
|
31
|
+
if (error instanceof Error) {
|
|
32
|
+
return new SlackEgressError(error.message, fallbackCode);
|
|
33
|
+
}
|
|
34
|
+
return new SlackEgressError("Slack request failed", fallbackCode);
|
|
35
|
+
}
|
|
36
|
+
function assertWorkspaceId(workspaceId) {
|
|
37
|
+
if (typeof workspaceId !== "string" || workspaceId.trim().length === 0) {
|
|
38
|
+
throw new Error("A workspaceId is required to create Slack egress");
|
|
39
|
+
}
|
|
40
|
+
return workspaceId.trim();
|
|
41
|
+
}
|
|
42
|
+
function createProxyRequest(workspaceId, endpoint, method, options) {
|
|
43
|
+
return {
|
|
44
|
+
method,
|
|
45
|
+
endpoint,
|
|
46
|
+
workspaceId,
|
|
47
|
+
...(options?.data !== undefined ? { data: options.data } : {}),
|
|
48
|
+
...(options?.params !== undefined ? { params: options.params } : {}),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
async function executeSlackRequest(provider, request) {
|
|
52
|
+
try {
|
|
53
|
+
const response = await provider.proxy(request);
|
|
54
|
+
if (isRecord(response.data) && response.data.ok === false) {
|
|
55
|
+
throw new SlackEgressError(asString(response.data.error) ?? "Slack request failed", asString(response.data.code) ?? "slack_error", asNumber(response.data.retryAfterMs));
|
|
56
|
+
}
|
|
57
|
+
if (response.status >= 400) {
|
|
58
|
+
throw new SlackEgressError(`Slack request failed with status ${response.status}`, `http_${response.status}`);
|
|
59
|
+
}
|
|
60
|
+
return response.data;
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
throw toSlackEgressError(error);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export function createSlackEgress(provider, workspaceId) {
|
|
67
|
+
const resolvedWorkspaceId = assertWorkspaceId(workspaceId);
|
|
68
|
+
let cachedBotUserId;
|
|
69
|
+
let botUserIdLoaded = false;
|
|
70
|
+
const postMessage = async (channel, text, threadTs) => {
|
|
71
|
+
const body = {
|
|
72
|
+
channel,
|
|
73
|
+
text,
|
|
74
|
+
...(threadTs ? { thread_ts: threadTs } : {}),
|
|
75
|
+
};
|
|
76
|
+
const response = await executeSlackRequest(provider, createProxyRequest(resolvedWorkspaceId, "/chat.postMessage", "POST", { data: body }));
|
|
77
|
+
return {
|
|
78
|
+
ok: response.ok === true,
|
|
79
|
+
...(response.ts ? { ts: response.ts } : {}),
|
|
80
|
+
...(response.error ? { error: response.error } : {}),
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
return {
|
|
84
|
+
postMessage,
|
|
85
|
+
async postMessageChunked(channel, text, threadTs, maxChars = SLACK_MESSAGE_MAX_CHARS) {
|
|
86
|
+
const effectiveMaxChars = Math.min(Math.max(1, maxChars), SLACK_MESSAGE_MAX_CHARS);
|
|
87
|
+
const chunks = chunkSlackMessage(text, effectiveMaxChars);
|
|
88
|
+
if (!chunks.length) {
|
|
89
|
+
return { ok: false, error: "Cannot post an empty Slack message" };
|
|
90
|
+
}
|
|
91
|
+
let firstTs;
|
|
92
|
+
let replyThreadTs = threadTs;
|
|
93
|
+
for (const chunk of chunks) {
|
|
94
|
+
const result = await postMessage(channel, chunk, replyThreadTs);
|
|
95
|
+
if (!result.ok) {
|
|
96
|
+
return {
|
|
97
|
+
ok: false,
|
|
98
|
+
...(firstTs ? { ts: firstTs } : {}),
|
|
99
|
+
...(result.error ? { error: result.error } : {}),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
firstTs ??= result.ts;
|
|
103
|
+
replyThreadTs ??= result.ts;
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
ok: true,
|
|
107
|
+
...(firstTs ? { ts: firstTs } : {}),
|
|
108
|
+
};
|
|
109
|
+
},
|
|
110
|
+
async addReaction(channel, timestamp, emoji) {
|
|
111
|
+
try {
|
|
112
|
+
await executeSlackRequest(provider, createProxyRequest(resolvedWorkspaceId, "/reactions.add", "POST", {
|
|
113
|
+
data: {
|
|
114
|
+
channel,
|
|
115
|
+
timestamp,
|
|
116
|
+
name: emoji,
|
|
117
|
+
},
|
|
118
|
+
}));
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
// Reactions are best-effort and should not block callers.
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
async fetchThreadHistory(channel, threadTs, botUserId) {
|
|
125
|
+
const response = await executeSlackRequest(provider, createProxyRequest(resolvedWorkspaceId, "/conversations.replies", "POST", {
|
|
126
|
+
data: {
|
|
127
|
+
channel,
|
|
128
|
+
ts: threadTs,
|
|
129
|
+
},
|
|
130
|
+
}));
|
|
131
|
+
if (!Array.isArray(response.messages)) {
|
|
132
|
+
return [];
|
|
133
|
+
}
|
|
134
|
+
return response.messages
|
|
135
|
+
.filter((message) => !message.subtype || message.subtype === "bot_message")
|
|
136
|
+
.sort((left, right) => Number(left.ts ?? "0") - Number(right.ts ?? "0"))
|
|
137
|
+
.map((message) => {
|
|
138
|
+
const content = cleanSlackText(message.text);
|
|
139
|
+
const isAssistant = Boolean(message.bot_id) || (!!botUserId && message.user === botUserId);
|
|
140
|
+
return {
|
|
141
|
+
role: isAssistant ? "assistant" : "user",
|
|
142
|
+
content,
|
|
143
|
+
...(message.ts ? { ts: message.ts } : {}),
|
|
144
|
+
};
|
|
145
|
+
})
|
|
146
|
+
.filter((message) => message.content.length > 0)
|
|
147
|
+
.slice(-20);
|
|
148
|
+
},
|
|
149
|
+
async getBotUserId() {
|
|
150
|
+
if (botUserIdLoaded) {
|
|
151
|
+
return cachedBotUserId;
|
|
152
|
+
}
|
|
153
|
+
const response = await executeSlackRequest(provider, createProxyRequest(resolvedWorkspaceId, "/auth.test", "POST"));
|
|
154
|
+
cachedBotUserId = asString(response.user_id);
|
|
155
|
+
botUserIdLoaded = true;
|
|
156
|
+
return cachedBotUserId;
|
|
157
|
+
},
|
|
158
|
+
async getReactions(channel, timestamp) {
|
|
159
|
+
const response = await executeSlackRequest(provider, createProxyRequest(resolvedWorkspaceId, "/reactions.get", "GET", {
|
|
160
|
+
params: {
|
|
161
|
+
channel,
|
|
162
|
+
timestamp,
|
|
163
|
+
},
|
|
164
|
+
}));
|
|
165
|
+
const reactions = response.message?.reactions;
|
|
166
|
+
if (!Array.isArray(reactions)) {
|
|
167
|
+
return [];
|
|
168
|
+
}
|
|
169
|
+
return reactions
|
|
170
|
+
.map((reaction) => {
|
|
171
|
+
const name = asString(reaction.name);
|
|
172
|
+
const count = asNumber(reaction.count);
|
|
173
|
+
if (!name || count === undefined) {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
return { name, count };
|
|
177
|
+
})
|
|
178
|
+
.filter((reaction) => reaction !== null);
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { normalizeSlackWebhook } from "@relayfile/adapter-slack";
|
|
2
|
+
import type { NormalizedWebhook, SlackEnvelope as AdapterSlackEventEnvelope, SlackEvent as AdapterSlackEvent } from "@relayfile/adapter-slack";
|
|
3
|
+
/**
|
|
4
|
+
* Parse a Slack webhook body that may have been forwarded through Nango.
|
|
5
|
+
*
|
|
6
|
+
* Real-world delivery path in production:
|
|
7
|
+
* Slack → Nango forward webhook → sage (this function)
|
|
8
|
+
*
|
|
9
|
+
* Nango wraps the underlying Slack event in its own envelope:
|
|
10
|
+
* { type: "forward", from: "slack", connectionId, providerConfigKey, payload: {...slack event...} }
|
|
11
|
+
*
|
|
12
|
+
* The raw Slack envelope (what the @relayfile/adapter-slack parser expects) is
|
|
13
|
+
* whatever is inside `.payload`. For direct webhook delivery paths (no Nango
|
|
14
|
+
* in front) the body IS the raw Slack envelope, no unwrap needed.
|
|
15
|
+
*
|
|
16
|
+
* This wrapper detects the envelope shape and hands the adapter parser the
|
|
17
|
+
* unwrapped inner payload in both cases.
|
|
18
|
+
*/
|
|
19
|
+
export declare function parseSlackWebhookEnvelope(rawBody: unknown): AdapterSlackEventEnvelope;
|
|
20
|
+
export { normalizeSlackWebhook };
|
|
21
|
+
export type SlackEvent = AdapterSlackEvent;
|
|
22
|
+
export type SlackEventEnvelope = AdapterSlackEventEnvelope;
|
|
23
|
+
export type NormalizedSlackWebhook = Omit<NormalizedWebhook, "payload"> & {
|
|
24
|
+
payload: SlackEventEnvelope;
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=slack-ingress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slack-ingress.d.ts","sourceRoot":"","sources":["../../src/integrations/slack-ingress.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,qBAAqB,EAEtB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EACV,iBAAiB,EACjB,aAAa,IAAI,yBAAyB,EAC1C,UAAU,IAAI,iBAAiB,EAChC,MAAM,0BAA0B,CAAC;AAElC;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,GAAG,yBAAyB,CAerF;AAED,OAAO,EAAE,qBAAqB,EAAE,CAAC;AAEjC,MAAM,MAAM,UAAU,GAAG,iBAAiB,CAAC;AAC3C,MAAM,MAAM,kBAAkB,GAAG,yBAAyB,CAAC;AAE3D,MAAM,MAAM,sBAAsB,GAAG,IAAI,CAAC,iBAAiB,EAAE,SAAS,CAAC,GAAG;IACxE,OAAO,EAAE,kBAAkB,CAAC;CAC7B,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { parseNangoWebhookPayload, } from "@relayfile/provider-nango";
|
|
2
|
+
import { normalizeSlackWebhook, parseSlackWebhookEnvelope as parseRawSlackWebhookEnvelope, } from "@relayfile/adapter-slack";
|
|
3
|
+
/**
|
|
4
|
+
* Parse a Slack webhook body that may have been forwarded through Nango.
|
|
5
|
+
*
|
|
6
|
+
* Real-world delivery path in production:
|
|
7
|
+
* Slack → Nango forward webhook → sage (this function)
|
|
8
|
+
*
|
|
9
|
+
* Nango wraps the underlying Slack event in its own envelope:
|
|
10
|
+
* { type: "forward", from: "slack", connectionId, providerConfigKey, payload: {...slack event...} }
|
|
11
|
+
*
|
|
12
|
+
* The raw Slack envelope (what the @relayfile/adapter-slack parser expects) is
|
|
13
|
+
* whatever is inside `.payload`. For direct webhook delivery paths (no Nango
|
|
14
|
+
* in front) the body IS the raw Slack envelope, no unwrap needed.
|
|
15
|
+
*
|
|
16
|
+
* This wrapper detects the envelope shape and hands the adapter parser the
|
|
17
|
+
* unwrapped inner payload in both cases.
|
|
18
|
+
*/
|
|
19
|
+
export function parseSlackWebhookEnvelope(rawBody) {
|
|
20
|
+
const envelope = parseNangoWebhookPayload(rawBody);
|
|
21
|
+
if (envelope.type === "forward") {
|
|
22
|
+
const inner = envelope.payload;
|
|
23
|
+
if (!inner || typeof inner !== "object") {
|
|
24
|
+
throw new TypeError("Nango forward webhook payload is missing its inner object — cannot extract Slack envelope");
|
|
25
|
+
}
|
|
26
|
+
return parseRawSlackWebhookEnvelope(inner);
|
|
27
|
+
}
|
|
28
|
+
// Direct webhook delivery — the body itself is already a raw Slack envelope.
|
|
29
|
+
return parseRawSlackWebhookEnvelope(envelope);
|
|
30
|
+
}
|
|
31
|
+
export { normalizeSlackWebhook };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"org-memory.d.ts","sourceRoot":"","sources":["../../src/memory/org-memory.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAyExD,qBAAa,SAAS;IAGR,OAAO,CAAC,QAAQ,CAAC,WAAW;IAFxC,OAAO,CAAC,OAAO,CAAuC;gBAEzB,WAAW,EAAE,MAAM;IAEhD,OAAO,CAAC,UAAU;
|
|
1
|
+
{"version":3,"file":"org-memory.d.ts","sourceRoot":"","sources":["../../src/memory/org-memory.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAyExD,qBAAa,SAAS;IAGR,OAAO,CAAC,QAAQ,CAAC,WAAW;IAFxC,OAAO,CAAC,OAAO,CAAuC;gBAEzB,WAAW,EAAE,MAAM;IAEhD,OAAO,CAAC,UAAU;IAUZ,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;IACjC,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAapD,aAAa,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IACrD,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAkD1E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAMd,gBAAgB;YAgDhB,WAAW;IAoDzB,OAAO,CAAC,aAAa;IAiBrB,OAAO,CAAC,QAAQ;CAGjB"}
|
|
@@ -31,6 +31,7 @@ export class OrgMemory {
|
|
|
31
31
|
return (this.adapter ??= createMemoryAdapter({
|
|
32
32
|
type: "supermemory",
|
|
33
33
|
apiKey: process.env.SUPERMEMORY_API_KEY,
|
|
34
|
+
endpoint: process.env.SUPERMEMORY_ENDPOINT,
|
|
34
35
|
defaultAgentId: AGENT_ID,
|
|
35
36
|
defaultProjectId: this.workspaceId,
|
|
36
37
|
}));
|
package/dist/memory.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../src/memory.ts"],"names":[],"mappings":"AAqCA,qBAAa,UAAU;IAGT,OAAO,CAAC,QAAQ,CAAC,WAAW;IAFxC,OAAO,CAAC,OAAO,CAAuC;gBAEzB,WAAW,EAAE,MAAM;IAEhD,OAAO,CAAC,UAAU;
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../src/memory.ts"],"names":[],"mappings":"AAqCA,qBAAa,UAAU;IAGT,OAAO,CAAC,QAAQ,CAAC,WAAW;IAFxC,OAAO,CAAC,OAAO,CAAuC;gBAEzB,WAAW,EAAE,MAAM;IAEhD,OAAO,CAAC,UAAU;IAUZ,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAgB9C,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,MAAM,EAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlF,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,MAAM,EAAO,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7F,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5B,OAAO,CAAC,MAAM;YAKA,QAAQ;YAeR,kBAAkB;YA2ClB,cAAc;IA0C5B,OAAO,CAAC,aAAa;IAgBrB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,eAAe;IAMvB,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,QAAQ;YAIF,GAAG;CAgBlB"}
|
package/dist/memory.js
CHANGED
|
@@ -15,6 +15,7 @@ export class SageMemory {
|
|
|
15
15
|
return (this.adapter ??= createMemoryAdapter({
|
|
16
16
|
type: 'supermemory',
|
|
17
17
|
apiKey: process.env.SUPERMEMORY_API_KEY,
|
|
18
|
+
endpoint: process.env.SUPERMEMORY_ENDPOINT,
|
|
18
19
|
defaultAgentId: AGENT_ID,
|
|
19
20
|
defaultProjectId: this.workspaceId,
|
|
20
21
|
}));
|
package/dist/nango.d.ts
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
export declare const NANGO_INTEGRATIONS: {
|
|
2
|
-
readonly GITHUB: "github-app-oauth";
|
|
3
|
-
};
|
|
4
1
|
export interface ProxyConfig {
|
|
5
2
|
connectionId: string;
|
|
6
3
|
providerConfigKey: string;
|
|
@@ -33,7 +30,6 @@ export declare class NangoError extends Error {
|
|
|
33
30
|
export declare class NangoClient {
|
|
34
31
|
private client;
|
|
35
32
|
private readonly githubOrgDiscoveryCache;
|
|
36
|
-
private readonly slackBotUserIdCache;
|
|
37
33
|
private readonly secretKey;
|
|
38
34
|
constructor({ secretKey }: {
|
|
39
35
|
secretKey: string;
|
|
@@ -41,8 +37,7 @@ export declare class NangoClient {
|
|
|
41
37
|
private getClient;
|
|
42
38
|
proxy<T>(config: ProxyConfig): Promise<T>;
|
|
43
39
|
getConnection(connectionId: string, providerConfigKey: string): Promise<NangoConnection | null>;
|
|
44
|
-
discoverGitHubOrgs(connectionId: string): Promise<GitHubOrgDiscovery>;
|
|
45
|
-
getSlackBotUserId(connectionId: string, providerConfigKey: string): Promise<string>;
|
|
40
|
+
discoverGitHubOrgs(connectionId: string, providerConfigKey: string): Promise<GitHubOrgDiscovery>;
|
|
46
41
|
private listGitHubInstallationRepos;
|
|
47
42
|
}
|
|
48
43
|
//# sourceMappingURL=nango.d.ts.map
|
package/dist/nango.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nango.d.ts","sourceRoot":"","sources":["../src/nango.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"nango.d.ts","sourceRoot":"","sources":["../src/nango.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3C,QAAQ,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAClE;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CAC9B;AA4BD,qBAAa,UAAW,SAAQ,KAAK;aAEjB,SAAS,EAAE,MAAM;aACjB,MAAM,EAAE,MAAM;aACd,KAAK,EAAE,OAAO;gBAFd,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO;CAKjC;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAqD;IAC7F,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;gBAEvB,EAAE,SAAS,EAAE,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE;IAIhD,OAAO,CAAC,SAAS;IAIX,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC;IAQzC,aAAa,CAAC,YAAY,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAQ/F,kBAAkB,CACtB,YAAY,EAAE,MAAM,EACpB,iBAAiB,EAAE,MAAM,GACxB,OAAO,CAAC,kBAAkB,CAAC;YA8DhB,2BAA2B;CAuD1C"}
|
package/dist/nango.js
CHANGED
|
@@ -1,12 +1,4 @@
|
|
|
1
1
|
import { Nango } from '@nangohq/node';
|
|
2
|
-
// Nango provider_config_key values. Only listed here when sage has no other
|
|
3
|
-
// way to discover the value at runtime — Slack keys are always sourced from
|
|
4
|
-
// the webhook envelope or the cloud workspace-connections resolver, never
|
|
5
|
-
// hardcoded, because different deployments register the Slack bot under
|
|
6
|
-
// different names (`slack-sage`, `slack-my-senior-dev`, etc).
|
|
7
|
-
export const NANGO_INTEGRATIONS = {
|
|
8
|
-
GITHUB: 'github-app-oauth',
|
|
9
|
-
};
|
|
10
2
|
const GITHUB_DISCOVERY_TTL_MS = 30 * 60_000;
|
|
11
3
|
export class NangoError extends Error {
|
|
12
4
|
operation;
|
|
@@ -23,7 +15,6 @@ export class NangoError extends Error {
|
|
|
23
15
|
export class NangoClient {
|
|
24
16
|
client = null;
|
|
25
17
|
githubOrgDiscoveryCache = new Map();
|
|
26
|
-
slackBotUserIdCache = new Map();
|
|
27
18
|
secretKey;
|
|
28
19
|
constructor({ secretKey }) {
|
|
29
20
|
this.secretKey = secretKey;
|
|
@@ -47,14 +38,16 @@ export class NangoClient {
|
|
|
47
38
|
throw new NangoError('getConnection', connectionId, error);
|
|
48
39
|
}
|
|
49
40
|
}
|
|
50
|
-
async discoverGitHubOrgs(connectionId) {
|
|
51
|
-
const
|
|
41
|
+
async discoverGitHubOrgs(connectionId, providerConfigKey) {
|
|
42
|
+
const resolvedProviderConfigKey = providerConfigKey.trim();
|
|
43
|
+
const cacheKey = `${connectionId}:${resolvedProviderConfigKey}`;
|
|
44
|
+
const cached = this.githubOrgDiscoveryCache.get(cacheKey);
|
|
52
45
|
if (cached && cached.expiresAt > Date.now()) {
|
|
53
46
|
return cloneGitHubOrgDiscovery(cached.value);
|
|
54
47
|
}
|
|
55
48
|
const response = await this.proxy({
|
|
56
49
|
connectionId,
|
|
57
|
-
providerConfigKey:
|
|
50
|
+
providerConfigKey: resolvedProviderConfigKey,
|
|
58
51
|
method: 'GET',
|
|
59
52
|
endpoint: '/user/installations',
|
|
60
53
|
});
|
|
@@ -66,7 +59,7 @@ export class NangoClient {
|
|
|
66
59
|
continue;
|
|
67
60
|
}
|
|
68
61
|
orgs.add(org);
|
|
69
|
-
const repoNames = await this.listGitHubInstallationRepos(connectionId, installation);
|
|
62
|
+
const repoNames = await this.listGitHubInstallationRepos(connectionId, resolvedProviderConfigKey, installation);
|
|
70
63
|
if (!reposByOrg.has(org)) {
|
|
71
64
|
reposByOrg.set(org, new Set());
|
|
72
65
|
}
|
|
@@ -84,31 +77,13 @@ export class NangoClient {
|
|
|
84
77
|
.sort(([left], [right]) => left.localeCompare(right))
|
|
85
78
|
.map(([org, repoNames]) => [org, [...repoNames].sort((left, right) => left.localeCompare(right))])),
|
|
86
79
|
};
|
|
87
|
-
this.githubOrgDiscoveryCache.set(
|
|
80
|
+
this.githubOrgDiscoveryCache.set(cacheKey, {
|
|
88
81
|
expiresAt: Date.now() + GITHUB_DISCOVERY_TTL_MS,
|
|
89
82
|
value: cloneGitHubOrgDiscovery(value),
|
|
90
83
|
});
|
|
91
84
|
return cloneGitHubOrgDiscovery(value);
|
|
92
85
|
}
|
|
93
|
-
async
|
|
94
|
-
const cached = this.slackBotUserIdCache.get(connectionId);
|
|
95
|
-
if (cached) {
|
|
96
|
-
return cached;
|
|
97
|
-
}
|
|
98
|
-
const response = await this.proxy({
|
|
99
|
-
connectionId,
|
|
100
|
-
providerConfigKey,
|
|
101
|
-
method: 'POST',
|
|
102
|
-
endpoint: '/auth.test',
|
|
103
|
-
});
|
|
104
|
-
const userId = response.user_id?.trim();
|
|
105
|
-
if (!userId) {
|
|
106
|
-
throw new NangoError('getSlackBotUserId', connectionId, new Error('Slack auth.test response missing user_id'));
|
|
107
|
-
}
|
|
108
|
-
this.slackBotUserIdCache.set(connectionId, userId);
|
|
109
|
-
return userId;
|
|
110
|
-
}
|
|
111
|
-
async listGitHubInstallationRepos(connectionId, installation) {
|
|
86
|
+
async listGitHubInstallationRepos(connectionId, providerConfigKey, installation) {
|
|
112
87
|
const endpoints = buildGitHubInstallationRepoEndpoints(installation);
|
|
113
88
|
if (endpoints.length === 0) {
|
|
114
89
|
return [];
|
|
@@ -125,7 +100,7 @@ export class NangoClient {
|
|
|
125
100
|
const pagedEndpoint = `${endpoint}${separator}per_page=${perPage}&page=${page}`;
|
|
126
101
|
const response = await this.proxy({
|
|
127
102
|
connectionId,
|
|
128
|
-
providerConfigKey
|
|
103
|
+
providerConfigKey,
|
|
129
104
|
method: 'GET',
|
|
130
105
|
endpoint: pagedEndpoint,
|
|
131
106
|
});
|
package/dist/openrouter.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openrouter.d.ts","sourceRoot":"","sources":["../src/openrouter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"openrouter.d.ts","sourceRoot":"","sources":["../src/openrouter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAQzD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,CAAC;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,QAAQ,EAAE,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACrC,CAAC;CACH;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/C;AAED,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAE7E,MAAM,WAAW,WAAW;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;CAC5C;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,2FAA2F;IAC3F,IAAI,CAAC,EAAE,cAAc,CAAC;CACvB;AAgDD,qBAAa,eAAgB,SAAQ,KAAK;aACK,MAAM,CAAC,EAAE,MAAM;aAAkB,IAAI,CAAC,EAAE,MAAM;gBAA/E,OAAO,EAAE,MAAM,EAAkB,MAAM,CAAC,EAAE,MAAM,YAAA,EAAkB,IAAI,CAAC,EAAE,MAAM,YAAA;CAI5F;AAuFD,wBAAsB,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,YAAY,CAAC,CAuHpG"}
|
package/dist/openrouter.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { env } from "node:process";
|
|
2
|
-
const
|
|
2
|
+
const DEFAULT_ENDPOINT = "https://openrouter.ai/api/v1/chat/completions";
|
|
3
|
+
const ENDPOINT = env.OPENROUTER_ENDPOINT?.trim() || DEFAULT_ENDPOINT;
|
|
3
4
|
const DEFAULT_MODEL = "anthropic/claude-sonnet-4-6";
|
|
4
5
|
const TIMEOUT_MS = 120_000;
|
|
5
6
|
const MAX_TOOL_ROUNDS = 5;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
+
import type { SlackEgress } from "../integrations/slack-egress.js";
|
|
1
2
|
import type { SageMemory } from "../memory.js";
|
|
2
|
-
|
|
3
|
-
export declare function watchContext(memory: SageMemory, nangoClient: NangoClient, slackConnectionId: string, slackProviderConfigKey: string, notifyChannel?: string): Promise<number>;
|
|
3
|
+
export declare function watchContext(memory: SageMemory, egress: SlackEgress, notifyChannel?: string): Promise<number>;
|
|
4
4
|
//# sourceMappingURL=context-watcher.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-watcher.d.ts","sourceRoot":"","sources":["../../src/proactive/context-watcher.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"context-watcher.d.ts","sourceRoot":"","sources":["../../src/proactive/context-watcher.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAEnE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AA6O/C,wBAAsB,YAAY,CAChC,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,WAAW,EACnB,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,MAAM,CAAC,CA2EjB"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { env } from "node:process";
|
|
2
2
|
import { chat } from "../openrouter.js";
|
|
3
|
-
import { postSlackMessageChunkedViaNango } from "../slack.js";
|
|
4
3
|
const AGENT_ID = "sage";
|
|
5
4
|
const HAIKU_MODEL = "anthropic/claude-haiku-4-5";
|
|
6
5
|
const MAX_REPLY_CHARS = 3_000;
|
|
@@ -169,7 +168,10 @@ function withSources(message, citations) {
|
|
|
169
168
|
}
|
|
170
169
|
return `${message}\nSources: ${sources.join(" | ")}`;
|
|
171
170
|
}
|
|
172
|
-
|
|
171
|
+
function postContextNotificationViaEgress(egress, channel, message) {
|
|
172
|
+
return egress.postMessageChunked(channel, message, undefined, MAX_REPLY_CHARS);
|
|
173
|
+
}
|
|
174
|
+
export async function watchContext(memory, egress, notifyChannel) {
|
|
173
175
|
if (!notifyChannel) {
|
|
174
176
|
console.warn("[proactive/context-watch] No notification channel configured");
|
|
175
177
|
return 0;
|
|
@@ -203,7 +205,7 @@ export async function watchContext(memory, nangoClient, slackConnectionId, slack
|
|
|
203
205
|
continue;
|
|
204
206
|
}
|
|
205
207
|
if (decision.shouldNotify && decision.message) {
|
|
206
|
-
const result = await
|
|
208
|
+
const result = await postContextNotificationViaEgress(egress, notifyChannel, withSources(decision.message, response.citations));
|
|
207
209
|
if (!result.ok) {
|
|
208
210
|
console.warn(`[proactive/context-watch] Failed to post topic update for "${topic.topic}": ${result.error}`);
|
|
209
211
|
continue;
|