@agentworkforce/sage 1.1.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/app.d.ts.map +1 -1
  2. package/dist/app.js +56 -23
  3. package/dist/e2e/e2e-harness.d.ts +36 -0
  4. package/dist/e2e/e2e-harness.d.ts.map +1 -0
  5. package/dist/e2e/e2e-harness.js +278 -0
  6. package/dist/e2e/mock-cloud-proxy-server.d.ts +25 -0
  7. package/dist/e2e/mock-cloud-proxy-server.d.ts.map +1 -0
  8. package/dist/e2e/mock-cloud-proxy-server.js +149 -0
  9. package/dist/e2e/mock-relayfile-server.d.ts +35 -0
  10. package/dist/e2e/mock-relayfile-server.d.ts.map +1 -0
  11. package/dist/e2e/mock-relayfile-server.js +488 -0
  12. package/dist/integrations/cloud-proxy-provider.js +1 -1
  13. package/dist/integrations/freshness-envelope.js +1 -1
  14. package/dist/integrations/github.d.ts +24 -1
  15. package/dist/integrations/github.d.ts.map +1 -1
  16. package/dist/integrations/github.js +116 -1
  17. package/dist/integrations/linear-ingress.d.ts +30 -0
  18. package/dist/integrations/linear-ingress.d.ts.map +1 -0
  19. package/dist/integrations/linear-ingress.js +58 -0
  20. package/dist/integrations/notion-ingress.d.ts +26 -0
  21. package/dist/integrations/notion-ingress.d.ts.map +1 -0
  22. package/dist/integrations/notion-ingress.js +70 -0
  23. package/dist/integrations/provider-ingress-dedup.d.ts +14 -0
  24. package/dist/integrations/provider-ingress-dedup.d.ts.map +1 -0
  25. package/dist/integrations/provider-ingress-dedup.js +35 -0
  26. package/dist/integrations/provider-ingress-dedup.test.d.ts +2 -0
  27. package/dist/integrations/provider-ingress-dedup.test.d.ts.map +1 -0
  28. package/dist/integrations/provider-ingress-dedup.test.js +55 -0
  29. package/dist/integrations/provider-write-facade.d.ts +80 -0
  30. package/dist/integrations/provider-write-facade.d.ts.map +1 -0
  31. package/dist/integrations/provider-write-facade.js +417 -0
  32. package/dist/integrations/provider-write-facade.test.d.ts +2 -0
  33. package/dist/integrations/provider-write-facade.test.d.ts.map +1 -0
  34. package/dist/integrations/provider-write-facade.test.js +247 -0
  35. package/dist/integrations/read-your-writes.test.d.ts +2 -0
  36. package/dist/integrations/read-your-writes.test.d.ts.map +1 -0
  37. package/dist/integrations/read-your-writes.test.js +170 -0
  38. package/dist/integrations/recent-actions-overlay.d.ts +1 -0
  39. package/dist/integrations/recent-actions-overlay.d.ts.map +1 -1
  40. package/dist/integrations/recent-actions-overlay.js +3 -0
  41. package/dist/integrations/relayfile-reader-envelope.test.d.ts +2 -0
  42. package/dist/integrations/relayfile-reader-envelope.test.d.ts.map +1 -0
  43. package/dist/integrations/relayfile-reader-envelope.test.js +198 -0
  44. package/dist/integrations/relayfile-reader.d.ts +20 -1
  45. package/dist/integrations/relayfile-reader.d.ts.map +1 -1
  46. package/dist/integrations/relayfile-reader.js +334 -48
  47. package/dist/integrations/slack-egress.d.ts +2 -1
  48. package/dist/integrations/slack-egress.d.ts.map +1 -1
  49. package/dist/integrations/slack-egress.js +28 -1
  50. package/dist/observability.e2e.test.d.ts +2 -0
  51. package/dist/observability.e2e.test.d.ts.map +1 -0
  52. package/dist/observability.e2e.test.js +411 -0
  53. package/package.json +9 -4
@@ -0,0 +1,170 @@
1
+ import { RelayFileApiError } from "@relayfile/sdk";
2
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
+ import { ProviderWriteFacade, } from "./provider-write-facade.js";
4
+ import { RecentActionsOverlay } from "./recent-actions-overlay.js";
5
+ import { SageRelayFileReader } from "./relayfile-reader.js";
6
+ const WORKSPACE_ID = "rw_cloudws_read_your_writes";
7
+ const NOW = new Date("2026-04-15T12:00:00.000Z");
8
+ const OVERLAY_TTL_MS = 5_000;
9
+ function notIngestedYet(path) {
10
+ return new RelayFileApiError(404, {
11
+ code: "not_found",
12
+ message: `RelayFile has not ingested ${path} yet`,
13
+ correlationId: "corr_read_your_writes_404",
14
+ });
15
+ }
16
+ function createRelayFile404Client() {
17
+ return {
18
+ readFile: vi.fn(async (_workspaceId, filePath) => {
19
+ throw notIngestedYet(filePath);
20
+ }),
21
+ queryFiles: vi.fn(async (_workspaceId, options) => {
22
+ throw notIngestedYet(options?.path ?? "/");
23
+ }),
24
+ listTree: vi.fn(async (_workspaceId, options) => {
25
+ throw notIngestedYet(options?.path ?? "/");
26
+ }),
27
+ };
28
+ }
29
+ function createReader(client, overlay) {
30
+ return new SageRelayFileReader({
31
+ client: client,
32
+ workspaceId: WORKSPACE_ID,
33
+ overlay,
34
+ logger: {
35
+ warn: vi.fn(),
36
+ error: vi.fn(),
37
+ },
38
+ });
39
+ }
40
+ describe("read-your-writes overlay integration", () => {
41
+ beforeEach(() => {
42
+ vi.useFakeTimers();
43
+ vi.setSystemTime(NOW);
44
+ });
45
+ afterEach(() => {
46
+ vi.useRealTimers();
47
+ });
48
+ it("serves just-written Slack and GitHub PR comments from overlay before RelayFile has ingested them", async () => {
49
+ const overlay = new RecentActionsOverlay({ ttlMs: OVERLAY_TTL_MS });
50
+ const relayFileClient = createRelayFile404Client();
51
+ const slackEgress = {
52
+ postMessage: vi.fn(async () => ({
53
+ ok: true,
54
+ ts: "1776254400.000100",
55
+ })),
56
+ addReaction: vi.fn(async () => undefined),
57
+ };
58
+ const githubIntegration = {
59
+ createIssueComment: vi.fn(async () => ({
60
+ data: {
61
+ id: 314159,
62
+ url: "https://github.example/AgentWorkforce/sage/pull/42#issuecomment-314159",
63
+ },
64
+ })),
65
+ createPRReview: vi.fn(async () => ({
66
+ data: {
67
+ id: 271828,
68
+ url: "https://github.example/AgentWorkforce/sage/pull/42#pullrequestreview-271828",
69
+ },
70
+ })),
71
+ };
72
+ const facade = new ProviderWriteFacade({
73
+ slackEgress,
74
+ githubIntegration,
75
+ overlay,
76
+ });
77
+ const reader = createReader(relayFileClient, overlay);
78
+ const slackChannel = "CREADWRITES";
79
+ const slackText = "Read your writes Slack smoke message";
80
+ const slackWrite = await facade.write({
81
+ kind: "slack:postMessage",
82
+ channel: slackChannel,
83
+ text: slackText,
84
+ }, "sync");
85
+ expect(slackWrite).toMatchObject({
86
+ mode: "sync",
87
+ ok: true,
88
+ providerPath: `/slack/channels/${slackChannel}/messages/1776254400.000100.json`,
89
+ });
90
+ const slackEnvelope = await reader.searchSlackHistoryEnveloped(slackText, slackChannel, 5);
91
+ expect(slackEnvelope).toMatchObject({
92
+ status: "hit",
93
+ source: "overlay",
94
+ asOf: NOW.getTime(),
95
+ ageMs: 0,
96
+ });
97
+ expect(slackEnvelope.data).toEqual([
98
+ expect.objectContaining({
99
+ path: slackWrite.providerPath,
100
+ provider: "slack",
101
+ properties: expect.objectContaining({
102
+ channel: slackChannel,
103
+ text: slackText,
104
+ ts: "1776254400.000100",
105
+ }),
106
+ }),
107
+ ]);
108
+ expect(relayFileClient.readFile).not.toHaveBeenCalled();
109
+ expect(relayFileClient.queryFiles).not.toHaveBeenCalled();
110
+ expect(relayFileClient.listTree).not.toHaveBeenCalled();
111
+ const githubOwner = "AgentWorkforce";
112
+ const githubRepo = "sage";
113
+ const pullNumber = 42;
114
+ const githubBody = "Read your writes GitHub PR comment";
115
+ const githubWrite = await facade.write({
116
+ kind: "github:createComment",
117
+ owner: githubOwner,
118
+ repo: githubRepo,
119
+ number: pullNumber,
120
+ body: githubBody,
121
+ }, "sync");
122
+ expect(githubWrite).toMatchObject({
123
+ mode: "sync",
124
+ ok: true,
125
+ providerPath: "/github/repos/AgentWorkforce/sage/issues/42/comments/314159.json",
126
+ });
127
+ const githubEnvelope = await reader.readGitHubPREnveloped(githubOwner, githubRepo, pullNumber);
128
+ expect(githubEnvelope).toMatchObject({
129
+ status: "hit",
130
+ source: "overlay",
131
+ asOf: NOW.getTime(),
132
+ ageMs: 0,
133
+ });
134
+ expect(githubEnvelope.data).toContain("Recent GitHub PR #42 overlay entries");
135
+ expect(githubEnvelope.data).toContain(githubWrite.providerPath);
136
+ expect(githubEnvelope.data).toContain(githubBody);
137
+ expect(relayFileClient.readFile).not.toHaveBeenCalled();
138
+ expect(relayFileClient.queryFiles).not.toHaveBeenCalled();
139
+ expect(relayFileClient.listTree).not.toHaveBeenCalled();
140
+ vi.advanceTimersByTime(OVERLAY_TTL_MS + 1);
141
+ await expect(reader.searchSlackHistoryEnveloped(slackText, slackChannel, 5)).resolves.toMatchObject({
142
+ data: null,
143
+ status: "miss",
144
+ source: "relayfile",
145
+ asOf: NOW.getTime() + OVERLAY_TTL_MS + 1,
146
+ ageMs: 0,
147
+ });
148
+ expect(relayFileClient.queryFiles).toHaveBeenCalledWith(WORKSPACE_ID, {
149
+ provider: "slack",
150
+ path: `/slack/channels/${slackChannel}`,
151
+ cursor: undefined,
152
+ limit: 100,
153
+ });
154
+ expect(relayFileClient.listTree).toHaveBeenCalledWith(WORKSPACE_ID, {
155
+ path: `/slack/channels/${slackChannel}`,
156
+ depth: 8,
157
+ cursor: undefined,
158
+ });
159
+ await expect(reader.readGitHubPREnveloped(githubOwner, githubRepo, pullNumber)).resolves.toMatchObject({
160
+ data: null,
161
+ status: "miss",
162
+ source: "relayfile",
163
+ asOf: NOW.getTime() + OVERLAY_TTL_MS + 1,
164
+ ageMs: 0,
165
+ });
166
+ expect(relayFileClient.readFile).toHaveBeenCalledWith(WORKSPACE_ID, "/github/repos/AgentWorkforce/sage/pulls/42/meta.json");
167
+ expect(relayFileClient.readFile).toHaveBeenCalledWith(WORKSPACE_ID, "/github/repos/AgentWorkforce/sage/pulls/42/metadata.json");
168
+ expect(relayFileClient.readFile).toHaveBeenCalledWith(WORKSPACE_ID, "/github/repos/AgentWorkforce/sage/pulls/42/diff.patch");
169
+ });
170
+ });
@@ -23,6 +23,7 @@ export declare class RecentActionsOverlay {
23
23
  pathPrefix?: string;
24
24
  since?: number;
25
25
  }): OverlayEntry[];
26
+ remove(path: string): boolean;
26
27
  clear(): void;
27
28
  get size(): number;
28
29
  private isExpired;
@@ -1 +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"}
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,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI7B,KAAK,IAAI,IAAI;IAIb,IAAI,IAAI,IAAI,MAAM,CAWjB;IAED,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,kBAAkB;CAU3B"}
@@ -52,6 +52,9 @@ export class RecentActionsOverlay {
52
52
  }
53
53
  return results.sort((left, right) => right.writtenAt - left.writtenAt);
54
54
  }
55
+ remove(path) {
56
+ return this.entries.delete(path);
57
+ }
55
58
  clear() {
56
59
  this.entries.clear();
57
60
  }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=relayfile-reader-envelope.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relayfile-reader-envelope.test.d.ts","sourceRoot":"","sources":["../../src/integrations/relayfile-reader-envelope.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,198 @@
1
+ import { RelayFileApiError } from "@relayfile/sdk";
2
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
+ import { RecentActionsOverlay } from "./recent-actions-overlay.js";
4
+ import { SageRelayFileReader } from "./relayfile-reader.js";
5
+ const WORKSPACE_ID = "rw_cloudws1";
6
+ const NOW = new Date("2026-04-15T12:00:00.000Z");
7
+ function fileResponse(filePath, content) {
8
+ return {
9
+ path: filePath,
10
+ revision: `rev:${filePath}`,
11
+ contentType: "text/plain",
12
+ content,
13
+ encoding: "utf-8",
14
+ };
15
+ }
16
+ function notFoundError(filePath) {
17
+ return new RelayFileApiError(404, {
18
+ code: "not_found",
19
+ message: `No relayfile entry for ${filePath}`,
20
+ correlationId: "corr_404",
21
+ });
22
+ }
23
+ function createStubRelayFileClient() {
24
+ return {
25
+ readFile: vi.fn(async (_workspaceId, filePath) => fileResponse(filePath, "relayfile body")),
26
+ queryFiles: vi.fn(async () => ({
27
+ items: [],
28
+ nextCursor: null,
29
+ })),
30
+ listTree: vi.fn(async (_workspaceId, options) => ({
31
+ path: options?.path ?? "/",
32
+ entries: [],
33
+ nextCursor: null,
34
+ })),
35
+ };
36
+ }
37
+ function createReader(client, overlay) {
38
+ return new SageRelayFileReader({
39
+ client: client,
40
+ workspaceId: WORKSPACE_ID,
41
+ overlay,
42
+ logger: {
43
+ warn: vi.fn(),
44
+ error: vi.fn(),
45
+ },
46
+ });
47
+ }
48
+ describe("SageRelayFileReader envelopes", () => {
49
+ beforeEach(() => {
50
+ vi.useFakeTimers();
51
+ vi.setSystemTime(NOW);
52
+ });
53
+ afterEach(() => {
54
+ vi.useRealTimers();
55
+ });
56
+ it('returns an overlay envelope and does not call the relayfile client', async () => {
57
+ const overlay = new RecentActionsOverlay();
58
+ overlay.record({
59
+ path: "/notes/overlay.md",
60
+ data: { content: "overlay body" },
61
+ provider: "github",
62
+ action: "file.write",
63
+ });
64
+ const client = createStubRelayFileClient();
65
+ const reader = createReader(client, overlay);
66
+ await expect(reader.readFileEnveloped("/notes/overlay.md")).resolves.toEqual({
67
+ data: "overlay body",
68
+ status: "hit",
69
+ source: "overlay",
70
+ asOf: NOW.getTime(),
71
+ ageMs: 0,
72
+ });
73
+ expect(client.readFile).not.toHaveBeenCalled();
74
+ expect(client.queryFiles).not.toHaveBeenCalled();
75
+ expect(client.listTree).not.toHaveBeenCalled();
76
+ });
77
+ it('returns a relayfile hit envelope with source="relayfile" and asOf', async () => {
78
+ const client = createStubRelayFileClient();
79
+ client.readFile.mockResolvedValueOnce(fileResponse("/notes/relay.md", "relayfile hit"));
80
+ const reader = createReader(client);
81
+ await expect(reader.readFileEnveloped("notes/relay.md")).resolves.toEqual({
82
+ data: "relayfile hit",
83
+ status: "hit",
84
+ source: "relayfile",
85
+ asOf: NOW.getTime(),
86
+ ageMs: 0,
87
+ });
88
+ expect(client.readFile).toHaveBeenCalledWith(WORKSPACE_ID, "/notes/relay.md");
89
+ });
90
+ it("returns a miss envelope for relayfile 404s", async () => {
91
+ const client = createStubRelayFileClient();
92
+ client.readFile.mockRejectedValueOnce(notFoundError("/notes/missing.md"));
93
+ const reader = createReader(client);
94
+ await expect(reader.readFileEnveloped("/notes/missing.md")).resolves.toEqual({
95
+ data: null,
96
+ status: "miss",
97
+ source: "relayfile",
98
+ asOf: NOW.getTime(),
99
+ ageMs: 0,
100
+ });
101
+ });
102
+ it("returns an error envelope with the thrown error string", async () => {
103
+ const client = createStubRelayFileClient();
104
+ client.readFile.mockRejectedValueOnce(new Error("relayfile unavailable"));
105
+ const reader = createReader(client);
106
+ await expect(reader.readFileEnveloped("/notes/error.md")).resolves.toEqual({
107
+ data: null,
108
+ status: "error",
109
+ source: "relayfile",
110
+ asOf: NOW.getTime(),
111
+ ageMs: 0,
112
+ error: "relayfile unavailable",
113
+ });
114
+ });
115
+ it("keeps backwards-compatible non-enveloped methods unwrapped", async () => {
116
+ const plainFilePath = "/notes/plain.md";
117
+ const searchItem = {
118
+ path: "/docs/needle.md",
119
+ revision: "rev-query",
120
+ contentType: "text/markdown",
121
+ provider: "github",
122
+ size: 24,
123
+ properties: { title: "Needle Doc" },
124
+ };
125
+ const slackItem = {
126
+ path: "/slack/channels/C123/messages/171234.json",
127
+ revision: "rev-slack",
128
+ contentType: "application/json",
129
+ provider: "slack",
130
+ size: 38,
131
+ properties: { title: "Slack Message" },
132
+ };
133
+ const contentsByPath = new Map([
134
+ [plainFilePath, "plain relayfile text"],
135
+ [searchItem.path, "needle content"],
136
+ [
137
+ "/github/repos/agent/sage/contents/README.md@main.json",
138
+ JSON.stringify({
139
+ path: "README.md",
140
+ sha: "abc123",
141
+ content: "hello from github",
142
+ encoding: "utf-8",
143
+ }),
144
+ ],
145
+ [
146
+ "/github/repos/agent/sage/pulls/42/meta.json",
147
+ JSON.stringify({
148
+ title: "Add envelopes",
149
+ body: "Envelope support",
150
+ html_url: "https://github.com/agent/sage/pull/42",
151
+ }),
152
+ ],
153
+ ["/github/repos/agent/sage/pulls/42/diff.patch", "diff --git a/file b/file"],
154
+ [slackItem.path, JSON.stringify({ text: "deployment shipped" })],
155
+ ]);
156
+ const client = createStubRelayFileClient();
157
+ client.readFile.mockImplementation(async (_workspaceId, filePath) => {
158
+ const content = contentsByPath.get(filePath);
159
+ if (content === undefined) {
160
+ throw notFoundError(filePath);
161
+ }
162
+ return fileResponse(filePath, content);
163
+ });
164
+ client.queryFiles.mockImplementation(async (_workspaceId, options) => ({
165
+ items: options?.provider === "slack" ? [slackItem] : [searchItem],
166
+ nextCursor: null,
167
+ }));
168
+ const reader = createReader(client);
169
+ await expect(reader.readFile(plainFilePath)).resolves.toBe("plain relayfile text");
170
+ await expect(reader.readFile("/notes/missing.md")).resolves.toBeNull();
171
+ const searchResults = await reader.searchFiles("needle", undefined, 1);
172
+ expect(searchResults).toEqual([
173
+ expect.objectContaining({
174
+ path: searchItem.path,
175
+ provider: "github",
176
+ title: "Needle Doc",
177
+ revision: "rev-query",
178
+ }),
179
+ ]);
180
+ expect(searchResults).not.toHaveProperty("status");
181
+ expect(searchResults).not.toHaveProperty("asOf");
182
+ await expect(reader.readGitHubFile("agent", "sage", "README.md", "main")).resolves.toBe("Path: README.md\nSHA: abc123\nContent:\nhello from github");
183
+ const pullRequest = await reader.readGitHubPR("agent", "sage", 42);
184
+ expect(pullRequest).toContain("Title: Add envelopes");
185
+ expect(pullRequest).toContain("Diff:\ndiff --git a/file b/file");
186
+ const slackResults = await reader.searchSlackHistory("deployment", "C123", 1);
187
+ expect(slackResults).toEqual([
188
+ expect.objectContaining({
189
+ path: slackItem.path,
190
+ provider: "slack",
191
+ title: "Slack Message",
192
+ revision: "rev-slack",
193
+ }),
194
+ ]);
195
+ expect(slackResults).not.toHaveProperty("status");
196
+ expect(slackResults).not.toHaveProperty("asOf");
197
+ });
198
+ });
@@ -1,9 +1,12 @@
1
1
  import type { RelayFileClient } from '@relayfile/sdk';
2
+ import { type FreshnessEnvelope } from './freshness-envelope.js';
3
+ import type { RecentActionsOverlay } from './recent-actions-overlay.js';
2
4
  export interface SageRelayFileReaderOptions {
3
5
  client: RelayFileClient | null;
4
6
  workspaceId: string;
5
7
  cacheTtlMs?: number;
6
8
  logger?: Pick<Console, 'warn' | 'error'>;
9
+ overlay?: RecentActionsOverlay;
7
10
  }
8
11
  export interface VfsSearchResult {
9
12
  path: string;
@@ -18,14 +21,17 @@ export declare class SageRelayFileReader {
18
21
  private readonly workspaceId;
19
22
  private readonly cacheTtlMs;
20
23
  private readonly logger;
24
+ private readonly overlay;
21
25
  private readonly searchCache;
22
26
  private readonly fileCache;
23
27
  private readonly logTimestamps;
24
28
  constructor(options: SageRelayFileReaderOptions);
25
- static disabled(workspaceId?: string): SageRelayFileReader;
29
+ static disabled(workspaceId?: string, overlay?: RecentActionsOverlay): SageRelayFileReader;
26
30
  isEnabled(): boolean;
27
31
  searchFiles(query: string, provider?: string, limit?: number): Promise<VfsSearchResult[]>;
32
+ searchFilesEnveloped(query: string, provider?: string, limit?: number): Promise<FreshnessEnvelope<VfsSearchResult[]>>;
28
33
  readFile(filePath: string): Promise<string | null>;
34
+ readFileEnveloped(filePath: string): Promise<FreshnessEnvelope<string>>;
29
35
  listDir(dirPath: string, limit?: number): Promise<VfsSearchResult[]>;
30
36
  queryByProperties(properties: Record<string, string>, limit?: number): Promise<VfsSearchResult[]>;
31
37
  listGitHubRepos(): Promise<Array<{
@@ -34,7 +40,9 @@ export declare class SageRelayFileReader {
34
40
  defaultBranch?: string;
35
41
  }>>;
36
42
  readGitHubFile(owner: string, repo: string, filePath: string, ref?: string): Promise<string | null>;
43
+ readGitHubFileEnveloped(owner: string, repo: string, filePath: string, ref?: string): Promise<FreshnessEnvelope<string>>;
37
44
  readGitHubPR(owner: string, repo: string, number: number): Promise<string | null>;
45
+ readGitHubPREnveloped(owner: string, repo: string, number: number): Promise<FreshnessEnvelope<string>>;
38
46
  readGitHubIssue(owner: string, repo: string, number: number): Promise<string | null>;
39
47
  listGitHubRepoFiles(owner: string, repo: string, dirPath?: string): Promise<VfsSearchResult[]>;
40
48
  searchGitHubCode(query: string, repo?: string): Promise<VfsSearchResult[]>;
@@ -42,11 +50,22 @@ export declare class SageRelayFileReader {
42
50
  readNotionPage(pageId: string): Promise<string | null>;
43
51
  readLinearIssue(issueId: string): Promise<string | null>;
44
52
  searchSlackHistory(query: string, channel?: string, limit?: number): Promise<VfsSearchResult[]>;
53
+ searchSlackHistoryEnveloped(query: string, channel?: string, limit?: number): Promise<FreshnessEnvelope<VfsSearchResult[]>>;
45
54
  private run;
55
+ private relayfileEnvelope;
56
+ private searchOverlay;
57
+ private readOverlayFile;
58
+ private readGitHubFileOverlay;
59
+ private readGitHubPROverlay;
46
60
  private getCache;
47
61
  private setCache;
48
62
  private logError;
49
63
  private isNotFoundError;
64
+ private searchFilesFromRelayFile;
65
+ private readFileFromRelayFile;
66
+ private readGitHubFileFromRelayFile;
67
+ private readGitHubPRFromRelayFile;
68
+ private searchSlackHistoryFromRelayFile;
50
69
  private readFileResponse;
51
70
  private tryReadFile;
52
71
  private readFirstAvailable;
@@ -1 +1 @@
1
- {"version":3,"file":"relayfile-reader.d.ts","sourceRoot":"","sources":["../../src/integrations/relayfile-reader.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"relayfile-reader.d.ts","sourceRoot":"","sources":["../../src/integrations/relayfile-reader.ts"],"names":[],"mappings":"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"}