Skip to content

Commit b75ff28

Browse files
committed
Gate primary evidence archive to sessions that request it
Workers omit the holder, so assemble keeps plain storage and skips admission and authorize recording wraps.
1 parent b936a4b commit b75ff28

3 files changed

Lines changed: 203 additions & 139 deletions

File tree

‎src/session/assemble-runtime.test.ts‎

Lines changed: 117 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,7 @@ import { mkdtemp } from "node:fs/promises";
33
import { tmpdir } from "node:os";
44
import { join } from "node:path";
55
import type { Agent } from "@intx/agent";
6-
import type {
7-
AuditStore,
8-
Compactor,
9-
ContextStore,
10-
ToolDefinition,
11-
} from "@intx/types/runtime";
6+
import type { Compactor, ContextStore, ToolDefinition } from "@intx/types/runtime";
127

138
import { withMockedModuleDuring } from "../../tests/helpers/mock-module.js";
149
import {
@@ -18,16 +13,10 @@ import {
1813
} from "./assemble-runtime.js";
1914

2015
function def(name: string): ToolDefinition {
21-
return {
22-
name,
23-
description: `${name} tool`,
24-
inputSchema: { type: "object", properties: {} },
25-
};
16+
return { name, description: `${name} tool`, inputSchema: { type: "object", properties: {} } };
2617
}
2718

28-
function wiring(
29-
overrides: Partial<Parameters<typeof createAdvertisedToolset>[0]> = {},
30-
) {
19+
function wiring(overrides: Partial<Parameters<typeof createAdvertisedToolset>[0]> = {}) {
3120
return {
3221
sessionMode: "orchestrator" as const,
3322
toolAvailability: { languageServerAvailable: false },
@@ -39,34 +28,24 @@ function wiring(
3928
describe("createAdvertisedToolset", () => {
4029
test("drops names outside the built-in prefix", () => {
4130
const { computeAdvertised } = createAdvertisedToolset(wiring());
42-
const names = computeAdvertised([
43-
def("write_file"),
44-
def("mystery_tool"),
45-
]).map((d) => d.name);
31+
const names = computeAdvertised([def("write_file"), def("mystery_tool")]).map((d) => d.name);
4632
expect(names).toContain("write_file");
4733
expect(names).not.toContain("mystery_tool");
4834
});
4935

5036
test("appends activated tools after the prefix, in activation order", () => {
5137
const { activated, computeAdvertised } = createAdvertisedToolset(wiring());
5238
expect(activated.activate(["mystery_tool"])).toBe(true);
53-
const names = computeAdvertised([
54-
def("read_file"),
55-
def("mystery_tool"),
56-
]).map((d) => d.name);
39+
const names = computeAdvertised([def("read_file"), def("mystery_tool")]).map((d) => d.name);
5740
expect(names[names.length - 1]).toBe("mystery_tool");
5841
expect(names.slice(0, -1)).not.toContain("mystery_tool");
5942
});
6043

6144
test("honors an explicit built-in prefix", () => {
62-
const { computeAdvertised } = createAdvertisedToolset(
63-
wiring({ builtInPrefix: ["read_file"] }),
64-
);
65-
expect(
66-
computeAdvertised([def("read_file"), def("write_file")]).map(
67-
(d) => d.name,
68-
),
69-
).toEqual(["read_file"]);
45+
const { computeAdvertised } = createAdvertisedToolset(wiring({ builtInPrefix: ["read_file"] }));
46+
expect(computeAdvertised([def("read_file"), def("write_file")]).map((d) => d.name)).toEqual([
47+
"read_file",
48+
]);
7049
});
7150

7251
test("advertises nothing from an empty registry", () => {
@@ -129,26 +108,60 @@ function stubInferenceDeps(): ChatAgentWiring["inferenceDeps"] {
129108
};
130109
}
131110

111+
function stubAuthorize(): ChatAgentWiring["authorize"] {
112+
return async () => ({ effect: "allow", matchingGrants: [], resolvedBy: null });
113+
}
114+
115+
function stubChatAgentWiring(overrides: Partial<ChatAgentWiring> = {}): ChatAgentWiring {
116+
return {
117+
toolsId: "test/tools",
118+
agentId: "test/agent",
119+
systemPrompt: "prompt",
120+
authorize: stubAuthorize(),
121+
getDynamicRunner: () => {
122+
throw new Error("getDynamicRunner should not run at assemble or mocked build");
123+
},
124+
computeAdvertised: () => [],
125+
activateTools: () => false,
126+
inactivityTimeoutMs: 1_000,
127+
onTasksChange: () => {},
128+
requestContinuation: () => {},
129+
getProvider: () => ({ providerName: "test", model: "m" }),
130+
getWorkdir: () => "/build-dir",
131+
inferenceDeps: stubInferenceDeps(),
132+
getSources: () => [
133+
{
134+
id: "s",
135+
provider: "test",
136+
baseURL: "http://localhost",
137+
apiKey: "k",
138+
model: "m",
139+
},
140+
],
141+
getDefaultSource: () => "s",
142+
getCompactor: () => stubCompactor("build"),
143+
onBuilt: () => {},
144+
...overrides,
145+
};
146+
}
147+
132148
describe("assembleChatAgent", () => {
133-
test("getWorkdir, getSessionId, and getCompactor run at buildAgent time", async () => {
149+
test("getWorkdir and getCompactor run at buildAgent time, not assemble time", async () => {
134150
const storeDirs: string[] = [];
135151
const agentWorkdirs: string[] = [];
136-
const agentSessionIds: string[] = [];
137-
const agentAudits: AuditStore[] = [];
138-
const agentStorages: ContextStore[] = [];
139152
const agentCompactors: Compactor[] = [];
140153
const fakeStorage = {
141154
readBlob: async () => new Uint8Array(),
142-
} as unknown as ContextStore & AuditStore;
143-
const fakeAgent = { close: async () => undefined } as unknown as Agent;
155+
} as unknown as ContextStore;
156+
const fakeAgent = { close: async () => {} } as unknown as Agent;
144157

145158
await withMockedModuleDuring(
146159
import.meta.resolve("./optimized-context-store.js"),
147160
(real: typeof import("./optimized-context-store.js")) => ({
148161
...real,
149-
createSessionStores: async (dir: string) => {
162+
createOptimizedContextStore: async (dir: string) => {
150163
storeDirs.push(dir);
151-
return { storage: fakeStorage, audit: fakeStorage };
164+
return fakeStorage;
152165
},
153166
}),
154167
async () => {
@@ -158,104 +171,106 @@ describe("assembleChatAgent", () => {
158171
...real,
159172
createAgentWithLiveToolDispatch: async (
160173
_def: unknown,
161-
env: {
162-
workdir: string;
163-
sessionId?: string;
164-
storage: ContextStore;
165-
audit: AuditStore;
166-
compactors: { "pruning-compactor": Compactor };
167-
},
174+
env: { workdir: string; compactors: { "pruning-compactor": Compactor } },
168175
) => {
169176
agentWorkdirs.push(env.workdir);
170-
if (env.sessionId !== undefined)
171-
agentSessionIds.push(env.sessionId);
172-
agentStorages.push(env.storage);
173-
agentAudits.push(env.audit);
174177
agentCompactors.push(env.compactors["pruning-compactor"]);
175178
return fakeAgent;
176179
},
177180
}),
178181
async () => {
179182
const { assembleChatAgent } = await import("./assemble-runtime.js");
180183
const workdirCalls: string[] = [];
181-
const sessionIdCalls: string[] = [];
182184
const compactorCalls: string[] = [];
183185
let liveDir = "/assemble-dir";
184-
let liveSessionId = "assemble-session";
185186
let liveCompactor = stubCompactor("assemble");
186187

187-
const { buildAgent } = assembleChatAgent({
188-
toolsId: "test/tools",
189-
agentId: "test/agent",
190-
systemPrompt: "prompt",
191-
authorize: async () => ({
192-
effect: "allow",
193-
matchingGrants: [],
194-
resolvedBy: null,
195-
}),
196-
getDynamicRunner: () => {
197-
throw new Error(
198-
"getDynamicRunner should not run at assemble or mocked build",
199-
);
200-
},
201-
computeAdvertised: () => [],
202-
activateTools: () => false,
203-
inactivityTimeoutMs: 1_000,
204-
onTasksChange: () => undefined,
205-
requestContinuation: () => undefined,
206-
getProvider: () => ({ providerName: "test", model: "m" }),
207-
getWorkdir: () => {
208-
workdirCalls.push(liveDir);
209-
return liveDir;
210-
},
211-
getSessionId: () => {
212-
sessionIdCalls.push(liveSessionId);
213-
return liveSessionId;
214-
},
215-
inferenceDeps: stubInferenceDeps(),
216-
getSources: () => [
217-
{
218-
id: "s",
219-
provider: "test",
220-
baseURL: "http://localhost",
221-
apiKey: "k",
222-
model: "m",
188+
const { buildAgent } = assembleChatAgent(
189+
stubChatAgentWiring({
190+
getWorkdir: () => {
191+
workdirCalls.push(liveDir);
192+
return liveDir;
193+
},
194+
getCompactor: () => {
195+
compactorCalls.push(liveCompactor.name);
196+
return liveCompactor;
223197
},
224-
],
225-
getDefaultSource: () => "s",
226-
getCompactor: () => {
227-
compactorCalls.push(liveCompactor.name);
228-
return liveCompactor;
229-
},
230-
onBuilt: () => undefined,
231-
});
198+
}),
199+
);
232200

233201
expect(workdirCalls).toEqual([]);
234-
expect(sessionIdCalls).toEqual([]);
235202
expect(compactorCalls).toEqual([]);
236203
expect(storeDirs).toEqual([]);
237204
expect(agentWorkdirs).toEqual([]);
238205

239206
liveDir = "/build-dir";
240-
liveSessionId = "build-session";
241207
liveCompactor = stubCompactor("build");
242208
const builtCompactor = liveCompactor;
243209

244210
await buildAgent();
245211

246212
expect(workdirCalls).toEqual(["/build-dir"]);
247-
expect(sessionIdCalls).toEqual(["build-session"]);
248213
expect(compactorCalls).toEqual(["build"]);
249214
expect(storeDirs).toEqual(["/build-dir"]);
250215
expect(agentWorkdirs).toEqual(["/build-dir"]);
251-
expect(agentSessionIds).toEqual(["build-session"]);
252-
expect(agentStorages).toEqual([fakeStorage]);
253-
expect(agentAudits).toEqual([fakeStorage]);
254-
expect(Object.is(agentAudits[0], agentStorages[0])).toBe(true);
255216
expect(agentCompactors).toEqual([builtCompactor]);
256217
},
257218
);
258219
},
259220
);
260221
});
222+
223+
test("omits evidence archive when no holder is provided", async () => {
224+
const fakeStorage = {
225+
readBlob: async () => new Uint8Array(),
226+
} as unknown as ContextStore;
227+
const fakeAgent = { close: async () => {} } as unknown as Agent;
228+
const authorize = stubAuthorize();
229+
let capturedStorage: ContextStore | undefined;
230+
let capturedAuthorize: unknown;
231+
let builtAgent: Agent | undefined;
232+
let builtStorage: ContextStore | undefined;
233+
234+
await withMockedModuleDuring(
235+
import.meta.resolve("./optimized-context-store.js"),
236+
(real: typeof import("./optimized-context-store.js")) => ({
237+
...real,
238+
createOptimizedContextStore: async () => fakeStorage,
239+
}),
240+
async () => {
241+
await withMockedModuleDuring(
242+
import.meta.resolve("../agent/live-tool-dispatch.js"),
243+
(real: typeof import("../agent/live-tool-dispatch.js")) => ({
244+
...real,
245+
createAgentWithLiveToolDispatch: async (
246+
_def: unknown,
247+
env: { storage: ContextStore; authorize: unknown },
248+
) => {
249+
capturedStorage = env.storage;
250+
capturedAuthorize = env.authorize;
251+
return fakeAgent;
252+
},
253+
}),
254+
async () => {
255+
const { assembleChatAgent } = await import("./assemble-runtime.js");
256+
const { buildAgent } = assembleChatAgent(
257+
stubChatAgentWiring({
258+
authorize,
259+
onBuilt: (agent, storage) => {
260+
builtAgent = agent;
261+
builtStorage = storage;
262+
},
263+
}),
264+
);
265+
await buildAgent();
266+
},
267+
);
268+
},
269+
);
270+
271+
expect(capturedStorage).toBe(fakeStorage);
272+
expect(capturedAuthorize).toBe(authorize);
273+
expect(builtAgent).toBe(fakeAgent);
274+
expect(builtStorage).toBe(fakeStorage);
275+
});
261276
});

0 commit comments

Comments
 (0)