@agentworkforce/sage 1.1.0 → 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/dist/integrations/cloud-proxy-provider.d.ts.map +1 -1
- package/dist/integrations/cloud-proxy-provider.js +9 -1
- 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/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/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/openrouter.d.ts.map +1 -1
- package/dist/openrouter.js +2 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloud-proxy-provider.d.ts","sourceRoot":"","sources":["../../src/integrations/cloud-proxy-provider.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cloud-proxy-provider.d.ts","sourceRoot":"","sources":["../../src/integrations/cloud-proxy-provider.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,KAAK,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC;AACtE,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAClD,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEhD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB;AAED,MAAM,WAAW,aAAa,CAAC,CAAC,GAAG,OAAO;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,YAAY,CAAC;IACtB,IAAI,EAAE,CAAC,CAAC;CACT;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,CAAC,GAAG,OAAO,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;IACrE,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CACjC;AAsDD,qBAAa,eAAgB,SAAQ,KAAK;IACxC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;gBAEnB,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE;CAOjG;AAwBD,qBAAa,kBAAmB,YAAW,kBAAkB;IAC3D,QAAQ,CAAC,IAAI,iBAAiB;IAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;gBAE3B,MAAM,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE;IAK5D,KAAK,CAAC,CAAC,GAAG,OAAO,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;IA0DpE,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;CAWtC"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
const HEALTH_ENDPOINT = "/api/health";
|
|
2
2
|
const SLACK_PROXY_ENDPOINT = "/api/v1/proxy/slack";
|
|
3
3
|
const REQUEST_TIMEOUT_MS = 30_000;
|
|
4
|
+
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
5
|
+
const SLACK_TEAM_ID_PATTERN = /^[TE][A-Z0-9]{6,}$/;
|
|
4
6
|
function isRecord(value) {
|
|
5
7
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6
8
|
}
|
|
@@ -26,6 +28,12 @@ function readWorkspaceId(request) {
|
|
|
26
28
|
}
|
|
27
29
|
return workspaceId;
|
|
28
30
|
}
|
|
31
|
+
function toCloudWorkspaceIdentity(workspaceId) {
|
|
32
|
+
if (SLACK_TEAM_ID_PATTERN.test(workspaceId) && !UUID_PATTERN.test(workspaceId)) {
|
|
33
|
+
return { slackTeamId: workspaceId };
|
|
34
|
+
}
|
|
35
|
+
return { workspaceId };
|
|
36
|
+
}
|
|
29
37
|
export class CloudProxyError extends Error {
|
|
30
38
|
status;
|
|
31
39
|
code;
|
|
@@ -76,7 +84,7 @@ export class CloudProxyProvider {
|
|
|
76
84
|
"Content-Type": "application/json",
|
|
77
85
|
},
|
|
78
86
|
body: JSON.stringify({
|
|
79
|
-
workspaceId,
|
|
87
|
+
...toCloudWorkspaceIdentity(workspaceId),
|
|
80
88
|
endpoint: request.endpoint,
|
|
81
89
|
method: request.method,
|
|
82
90
|
...(request.data !== undefined ? { data: request.data } : {}),
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type FreshnessSource = "overlay" | "relayfile" | "proxy" | "cache" | "fallback";
|
|
2
|
+
export type EnvelopeStatus = "hit" | "miss" | "stale" | "error";
|
|
3
|
+
export interface FreshnessEnvelope<T> {
|
|
4
|
+
data: T | null;
|
|
5
|
+
status: EnvelopeStatus;
|
|
6
|
+
source: FreshnessSource;
|
|
7
|
+
asOf: number;
|
|
8
|
+
ageMs: number;
|
|
9
|
+
error?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare const DEFAULT_FRESHNESS_MAX_AGE_MS = 60000;
|
|
12
|
+
export declare function hit<T>(data: T, source: FreshnessSource): FreshnessEnvelope<T>;
|
|
13
|
+
export declare function miss<T>(source: FreshnessSource): FreshnessEnvelope<T>;
|
|
14
|
+
export declare function stale<T>(data: T, source: FreshnessSource, ageMs: number): FreshnessEnvelope<T>;
|
|
15
|
+
export declare function error<T>(message: string, source: FreshnessSource): FreshnessEnvelope<T>;
|
|
16
|
+
export declare function isFresh<T>(envelope: FreshnessEnvelope<T>, maxAgeMs?: number): boolean;
|
|
17
|
+
//# sourceMappingURL=freshness-envelope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"freshness-envelope.d.ts","sourceRoot":"","sources":["../../src/integrations/freshness-envelope.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,WAAW,GAAG,OAAO,GAAG,OAAO,GAAG,UAAU,CAAC;AAEvF,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAEhE,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAClC,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;IACf,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,eAAe,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,4BAA4B,QAAS,CAAC;AAEnD,wBAAgB,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,eAAe,GAAG,iBAAiB,CAAC,CAAC,CAAC,CAQ7E;AAED,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,eAAe,GAAG,iBAAiB,CAAC,CAAC,CAAC,CAQrE;AAED,wBAAgB,KAAK,CAAC,CAAC,EACrB,IAAI,EAAE,CAAC,EACP,MAAM,EAAE,eAAe,EACvB,KAAK,EAAE,MAAM,GACZ,iBAAiB,CAAC,CAAC,CAAC,CAQtB;AAED,wBAAgB,KAAK,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,iBAAiB,CAAC,CAAC,CAAC,CASvF;AAED,wBAAgB,OAAO,CAAC,CAAC,EACvB,QAAQ,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAC9B,QAAQ,SAA+B,GACtC,OAAO,CAET"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export const DEFAULT_FRESHNESS_MAX_AGE_MS = 60_000;
|
|
2
|
+
export function hit(data, source) {
|
|
3
|
+
return {
|
|
4
|
+
data,
|
|
5
|
+
status: "hit",
|
|
6
|
+
source,
|
|
7
|
+
asOf: Date.now(),
|
|
8
|
+
ageMs: 0,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export function miss(source) {
|
|
12
|
+
return {
|
|
13
|
+
data: null,
|
|
14
|
+
status: "miss",
|
|
15
|
+
source,
|
|
16
|
+
asOf: Date.now(),
|
|
17
|
+
ageMs: 0,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export function stale(data, source, ageMs) {
|
|
21
|
+
return {
|
|
22
|
+
data,
|
|
23
|
+
status: "stale",
|
|
24
|
+
source,
|
|
25
|
+
asOf: Date.now() - ageMs,
|
|
26
|
+
ageMs,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function error(message, source) {
|
|
30
|
+
return {
|
|
31
|
+
data: null,
|
|
32
|
+
status: "error",
|
|
33
|
+
source,
|
|
34
|
+
asOf: Date.now(),
|
|
35
|
+
ageMs: 0,
|
|
36
|
+
error: message,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export function isFresh(envelope, maxAgeMs = DEFAULT_FRESHNESS_MAX_AGE_MS) {
|
|
40
|
+
return envelope.status === "hit" && envelope.ageMs <= maxAgeMs;
|
|
41
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"freshness-envelope.test.d.ts","sourceRoot":"","sources":["../../src/integrations/freshness-envelope.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { error, hit, isFresh, miss, stale } from "./freshness-envelope.js";
|
|
3
|
+
describe("freshness envelope", () => {
|
|
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("creates a hit envelope with data and source", () => {
|
|
13
|
+
const data = { workspaceId: "rw_cloudws1" };
|
|
14
|
+
expect(hit(data, "overlay")).toEqual({
|
|
15
|
+
data,
|
|
16
|
+
status: "hit",
|
|
17
|
+
source: "overlay",
|
|
18
|
+
asOf: now.getTime(),
|
|
19
|
+
ageMs: 0,
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
it("creates a miss envelope with null data", () => {
|
|
23
|
+
expect(miss("relayfile")).toEqual({
|
|
24
|
+
data: null,
|
|
25
|
+
status: "miss",
|
|
26
|
+
source: "relayfile",
|
|
27
|
+
asOf: now.getTime(),
|
|
28
|
+
ageMs: 0,
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
it("creates a stale envelope carrying ageMs", () => {
|
|
32
|
+
expect(stale({ count: 2 }, "cache", 5_000)).toEqual({
|
|
33
|
+
data: { count: 2 },
|
|
34
|
+
status: "stale",
|
|
35
|
+
source: "cache",
|
|
36
|
+
asOf: now.getTime() - 5_000,
|
|
37
|
+
ageMs: 5_000,
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
it("creates an error envelope carrying the error string", () => {
|
|
41
|
+
expect(error("proxy unavailable", "proxy")).toEqual({
|
|
42
|
+
data: null,
|
|
43
|
+
status: "error",
|
|
44
|
+
source: "proxy",
|
|
45
|
+
asOf: now.getTime(),
|
|
46
|
+
ageMs: 0,
|
|
47
|
+
error: "proxy unavailable",
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
it("checks freshness against a strict max age", () => {
|
|
51
|
+
const envelope = hit({ id: "fresh-context" }, "cache");
|
|
52
|
+
expect(isFresh({ ...envelope, ageMs: 99 }, 100)).toBe(true);
|
|
53
|
+
expect(isFresh({ ...envelope, ageMs: 100 }, 100)).toBe(false);
|
|
54
|
+
expect(isFresh({ ...envelope, ageMs: 101 }, 100)).toBe(false);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export declare const DEFAULT_OVERLAY_TTL_MS = 120000;
|
|
2
|
+
export declare const DEFAULT_OVERLAY_MAX_ENTRIES = 500;
|
|
3
|
+
export interface OverlayEntry {
|
|
4
|
+
path: string;
|
|
5
|
+
data: Record<string, unknown>;
|
|
6
|
+
writtenAt: number;
|
|
7
|
+
provider: string;
|
|
8
|
+
action: string;
|
|
9
|
+
}
|
|
10
|
+
export interface RecentActionsOverlayOptions {
|
|
11
|
+
ttlMs?: number;
|
|
12
|
+
maxEntries?: number;
|
|
13
|
+
}
|
|
14
|
+
export declare class RecentActionsOverlay {
|
|
15
|
+
private readonly ttlMs;
|
|
16
|
+
private readonly maxEntries;
|
|
17
|
+
private readonly entries;
|
|
18
|
+
constructor(options?: RecentActionsOverlayOptions);
|
|
19
|
+
record(entry: Omit<OverlayEntry, "writtenAt">): void;
|
|
20
|
+
lookup(path: string): OverlayEntry | null;
|
|
21
|
+
search(query: {
|
|
22
|
+
provider?: string;
|
|
23
|
+
pathPrefix?: string;
|
|
24
|
+
since?: number;
|
|
25
|
+
}): OverlayEntry[];
|
|
26
|
+
clear(): void;
|
|
27
|
+
get size(): number;
|
|
28
|
+
private isExpired;
|
|
29
|
+
private evictOldestEntries;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=recent-actions-overlay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recent-actions-overlay.d.ts","sourceRoot":"","sources":["../../src/integrations/recent-actions-overlay.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,SAAU,CAAC;AAC9C,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAE/C,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,2BAA2B;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmC;gBAE/C,OAAO,CAAC,EAAE,2BAA2B;IAKjD,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,GAAG,IAAI;IAcpD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IAgBzC,MAAM,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,YAAY,EAAE;IA2BzF,KAAK,IAAI,IAAI;IAIb,IAAI,IAAI,IAAI,MAAM,CAWjB;IAED,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,kBAAkB;CAU3B"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
export const DEFAULT_OVERLAY_TTL_MS = 120_000;
|
|
2
|
+
export const DEFAULT_OVERLAY_MAX_ENTRIES = 500;
|
|
3
|
+
export class RecentActionsOverlay {
|
|
4
|
+
ttlMs;
|
|
5
|
+
maxEntries;
|
|
6
|
+
entries = new Map();
|
|
7
|
+
constructor(options) {
|
|
8
|
+
this.ttlMs = options?.ttlMs ?? DEFAULT_OVERLAY_TTL_MS;
|
|
9
|
+
this.maxEntries = options?.maxEntries ?? DEFAULT_OVERLAY_MAX_ENTRIES;
|
|
10
|
+
}
|
|
11
|
+
record(entry) {
|
|
12
|
+
const overlayEntry = {
|
|
13
|
+
...entry,
|
|
14
|
+
writtenAt: Date.now(),
|
|
15
|
+
};
|
|
16
|
+
if (this.entries.has(entry.path)) {
|
|
17
|
+
this.entries.delete(entry.path);
|
|
18
|
+
}
|
|
19
|
+
this.entries.set(entry.path, overlayEntry);
|
|
20
|
+
this.evictOldestEntries();
|
|
21
|
+
}
|
|
22
|
+
lookup(path) {
|
|
23
|
+
const entry = this.entries.get(path);
|
|
24
|
+
if (!entry) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
if (this.isExpired(entry, Date.now())) {
|
|
28
|
+
this.entries.delete(path);
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
this.entries.delete(path);
|
|
32
|
+
this.entries.set(path, entry);
|
|
33
|
+
return entry;
|
|
34
|
+
}
|
|
35
|
+
search(query) {
|
|
36
|
+
const now = Date.now();
|
|
37
|
+
const results = [];
|
|
38
|
+
for (const entry of this.entries.values()) {
|
|
39
|
+
if (this.isExpired(entry, now)) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (query.provider !== undefined && entry.provider !== query.provider) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (query.pathPrefix !== undefined && !entry.path.startsWith(query.pathPrefix)) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (query.since !== undefined && entry.writtenAt < query.since) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
results.push(entry);
|
|
52
|
+
}
|
|
53
|
+
return results.sort((left, right) => right.writtenAt - left.writtenAt);
|
|
54
|
+
}
|
|
55
|
+
clear() {
|
|
56
|
+
this.entries.clear();
|
|
57
|
+
}
|
|
58
|
+
get size() {
|
|
59
|
+
const now = Date.now();
|
|
60
|
+
let count = 0;
|
|
61
|
+
for (const entry of this.entries.values()) {
|
|
62
|
+
if (!this.isExpired(entry, now)) {
|
|
63
|
+
count += 1;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return count;
|
|
67
|
+
}
|
|
68
|
+
isExpired(entry, now) {
|
|
69
|
+
return now - entry.writtenAt >= this.ttlMs;
|
|
70
|
+
}
|
|
71
|
+
evictOldestEntries() {
|
|
72
|
+
while (this.entries.size > this.maxEntries) {
|
|
73
|
+
const oldestKey = this.entries.keys().next().value;
|
|
74
|
+
if (oldestKey === undefined) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
this.entries.delete(oldestKey);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recent-actions-overlay.test.d.ts","sourceRoot":"","sources":["../../src/integrations/recent-actions-overlay.test.ts"],"names":[],"mappings":""}
|
|
@@ -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
|
+
});
|
|
@@ -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/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;
|