nexus-agentd 0.1.7 → 0.1.9

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/server.js CHANGED
@@ -1,21 +1,14 @@
1
1
  import { randomUUID, timingSafeEqual } from 'node:crypto';
2
2
  import http from 'node:http';
3
- import path from 'node:path';
4
- import { pipeline } from 'node:stream/promises';
5
- import { ArtifactStoreError, contentDisposition, GatewayArtifactStore, mediaTypeForName } from './artifact-store.js';
6
3
  import { AdminSessionStore } from './auth.js';
7
4
  import { ControlPlaneError } from './control-plane.js';
8
5
  import { RunNotFoundError, SessionNotFoundError, SessionRequestError } from './session.js';
9
6
  import { redirectToAgentdWebUi, writeAgentdWebUi } from './webui/index.js';
10
- import { WorkspaceFileError, WorkspaceFiles } from './workspace-files.js';
11
7
  const ADMIN_COOKIE = 'agent_nexus_admin';
12
8
  export function createAgentdServer(config, sessions, controlPlane) {
13
9
  const adminSessions = new AdminSessionStore(config.adminSessionTtlMs || 12 * 60 * 60 * 1000);
14
10
  const loginLimiter = new FailureRateLimiter(8, 60_000, 5 * 60_000);
15
11
  const apiLimiter = new FailureRateLimiter(30, 60_000, 60_000);
16
- const artifactStore = new GatewayArtifactStore(config.artifactStoragePath || path.resolve('.nexus-agentd-artifacts'), config.artifactTtlMs || 24 * 60 * 60 * 1000, config.maxArtifactBytes || 512 * 1024 * 1024, config.maxArtifactStorageBytes || 4 * 1024 * 1024 * 1024, config.maxPublishedArtifacts || 4096, config.maxConcurrentArtifactPublishes || 4);
17
- const workspaceFiles = new WorkspaceFiles(() => controlPlane?.snapshot().workspaceRoots || config.workspaceRoots, config.maxArtifactBytes || 512 * 1024 * 1024);
18
- artifactStore.startCleanup(config.cleanupIntervalMs);
19
12
  let sseConnections = 0;
20
13
  const server = http.createServer((request, response) => {
21
14
  const requestId = randomUUID();
@@ -27,8 +20,6 @@ export function createAgentdServer(config, sessions, controlPlane) {
27
20
  adminSessions,
28
21
  loginLimiter,
29
22
  apiLimiter,
30
- artifactStore,
31
- workspaceFiles,
32
23
  request,
33
24
  response,
34
25
  acquireSse() {
@@ -67,14 +58,11 @@ export function createAgentdServer(config, sessions, controlPlane) {
67
58
  server.headersTimeout = Math.min(server.requestTimeout, 20_000);
68
59
  server.keepAliveTimeout = 5_000;
69
60
  server.maxConnections = config.maxConnections || 256;
70
- server.once('close', () => {
71
- adminSessions.clear();
72
- artifactStore.stopCleanup();
73
- });
61
+ server.once('close', () => adminSessions.clear());
74
62
  return server;
75
63
  }
76
64
  async function handleRequest(context) {
77
- const { config, sessions, controlPlane, adminSessions, loginLimiter, apiLimiter, artifactStore, workspaceFiles, request, response } = context;
65
+ const { config, sessions, controlPlane, adminSessions, loginLimiter, apiLimiter, request, response } = context;
78
66
  const url = new URL(request.url || '/', 'http://localhost');
79
67
  if (url.pathname === '/' && request.method === 'GET') {
80
68
  redirectToAgentdWebUi(response);
@@ -84,12 +72,6 @@ async function handleRequest(context) {
84
72
  writeAgentdWebUi(response);
85
73
  return;
86
74
  }
87
- const artifactMatch = url.pathname.match(/^\/v1\/artifacts\/([a-f0-9]{64})\/(\d+)\/([^/]+)$/);
88
- if (artifactMatch && request.method === 'GET') {
89
- const artifact = await artifactStore.open(artifactMatch[1], artifactMatch[2], decodePathSegment(artifactMatch[3]));
90
- await streamDownload(response, artifact.handle, artifact.name, artifact.size, artifact.mediaType);
91
- return;
92
- }
93
75
  if (url.pathname === '/health' && request.method === 'GET') {
94
76
  writeJson(response, 200, {
95
77
  ok: true,
@@ -168,6 +150,10 @@ async function handleRequest(context) {
168
150
  return;
169
151
  }
170
152
  const principal = authenticateApiKey(request, config, controlPlane, apiLimiter);
153
+ if (url.pathname === '/v1/meta' && request.method === 'GET') {
154
+ writeJson(response, 200, { instanceId: sessions.instanceId });
155
+ return;
156
+ }
171
157
  if (url.pathname === '/v1/agents' && request.method === 'GET') {
172
158
  const ids = principal.scope.allAgents
173
159
  ? undefined
@@ -183,37 +169,9 @@ async function handleRequest(context) {
183
169
  assertOnlyKeys(body, ['agentId', 'workspace']);
184
170
  const agentId = requiredString(body.agentId, 'agentId');
185
171
  assertAgentScope(principal, agentId);
186
- writeJson(response, 201, await materializeSessionArtifacts(await sessions.create(agentId, optionalString(body.workspace), principal.id), artifactStore));
172
+ writeJson(response, 201, await sessions.create(agentId, optionalString(body.workspace), principal.id));
187
173
  return;
188
174
  }
189
- const filesMatch = url.pathname.match(/^\/v1\/sessions\/([^/]+)\/files(?:\/(content|publish))?$/);
190
- if (filesMatch) {
191
- const sessionId = decodePathSegment(filesMatch[1]);
192
- const session = ownedSession(sessions, sessionId, principal);
193
- const root = await workspaceFiles.sessionRoot(session.workspace);
194
- const action = filesMatch[2];
195
- if (!action && request.method === 'GET') {
196
- writeJson(response, 200, await workspaceFiles.list(root, url.searchParams.get('path') || ''));
197
- return;
198
- }
199
- if (action === 'content' && request.method === 'GET') {
200
- const file = await workspaceFiles.open(root, requiredQuery(url, 'path'));
201
- await streamDownload(response, file.handle, file.name, file.size, mediaTypeForName(file.name));
202
- return;
203
- }
204
- if (action === 'publish' && request.method === 'POST') {
205
- assertJsonContentType(request);
206
- const body = await readJsonBody(request, config.maxRequestBytes);
207
- assertOnlyKeys(body, ['paths']);
208
- const paths = requiredStringArray(body.paths, 'paths');
209
- if (paths.length > 32)
210
- throw new RequestError(400, 'paths must contain at most 32 values');
211
- const result = await publishWorkspaceFiles(workspaceFiles, artifactStore, root, paths);
212
- writeJson(response, result.errors.length ? 207 : 201, result);
213
- return;
214
- }
215
- throw new RequestError(405, 'Method not allowed');
216
- }
217
175
  const attachmentMatch = url.pathname.match(/^\/v1\/sessions\/([^/]+)\/attachments$/);
218
176
  if (attachmentMatch && request.method === 'POST') {
219
177
  const sessionId = decodeURIComponent(attachmentMatch[1]);
@@ -226,6 +184,46 @@ async function handleRequest(context) {
226
184
  writeJson(response, 201, sessions.addInputAttachment(sessionId, decodeHeader(request.headers['x-nexus-file-name']) || 'attachment', normalizedMediaType(request.headers['content-type']), bytes));
227
185
  return;
228
186
  }
187
+ const resolveMatch = url.pathname.match(/^\/v1\/sessions\/([^/]+)\/requests\/([^/]+)\/resolve$/);
188
+ if (resolveMatch && request.method === 'POST') {
189
+ const sessionId = decodeURIComponent(resolveMatch[1]);
190
+ const requestId = decodeURIComponent(resolveMatch[2]);
191
+ const session = sessions.get(sessionId);
192
+ if (!sessions.owns(sessionId, principal.id)) {
193
+ throw new RequestError(404, 'Session not found');
194
+ }
195
+ assertAgentScope(principal, session.agentId);
196
+ assertJsonContentType(request);
197
+ const body = await readJsonBody(request, config.maxRequestBytes);
198
+ assertOnlyKeys(body, ['message', 'optionId', 'action', 'attachments']);
199
+ const resolution = {
200
+ requestId,
201
+ message: optionalString(body.message),
202
+ optionId: optionalString(body.optionId),
203
+ action: optionalPendingAction(body.action)
204
+ };
205
+ if (!resolution.message &&
206
+ !resolution.optionId &&
207
+ !resolution.action) {
208
+ throw new RequestError(400, 'A pending response is required');
209
+ }
210
+ writeJson(response, 202, await sessions.resolvePending(sessionId, resolution, optionalAttachmentIds(body.attachments)));
211
+ return;
212
+ }
213
+ const publishMatch = url.pathname.match(/^\/v1\/sessions\/([^/]+)\/artifacts\/publish$/);
214
+ if (publishMatch && request.method === 'POST') {
215
+ const sessionId = decodeURIComponent(publishMatch[1]);
216
+ const session = sessions.get(sessionId);
217
+ if (!sessions.owns(sessionId, principal.id)) {
218
+ throw new RequestError(404, 'Session not found');
219
+ }
220
+ assertAgentScope(principal, session.agentId);
221
+ assertJsonContentType(request);
222
+ const body = await readJsonBody(request, config.maxRequestBytes);
223
+ assertOnlyKeys(body, ['path']);
224
+ writeJson(response, 201, await sessions.publishFile(sessionId, requiredString(body.path, 'path')));
225
+ return;
226
+ }
229
227
  const match = url.pathname.match(/^\/v1\/sessions\/([^/]+)(?:\/(message|cancel|events))?$/);
230
228
  if (!match)
231
229
  throw new RequestError(404, 'Route not found');
@@ -237,19 +235,23 @@ async function handleRequest(context) {
237
235
  }
238
236
  assertAgentScope(principal, session.agentId);
239
237
  if (!action && request.method === 'GET') {
240
- writeJson(response, 200, await materializeSessionArtifacts(session, artifactStore));
238
+ writeJson(response, 200, session);
239
+ return;
240
+ }
241
+ if (!action && request.method === 'DELETE') {
242
+ writeJson(response, 200, await sessions.close(sessionId));
241
243
  return;
242
244
  }
243
245
  if (action === 'message' && request.method === 'POST') {
244
246
  assertJsonContentType(request);
245
247
  const body = await readJsonBody(request, config.maxRequestBytes);
246
248
  assertOnlyKeys(body, ['message', 'attachments']);
247
- writeJson(response, 202, await materializeSessionArtifacts(await sessions.message(sessionId, requiredMessage(body.message), optionalAttachmentIds(body.attachments)), artifactStore));
249
+ writeJson(response, 202, await sessions.message(sessionId, requiredMessage(body.message), optionalAttachmentIds(body.attachments)));
248
250
  return;
249
251
  }
250
252
  if (action === 'cancel' && request.method === 'POST') {
251
253
  await assertEmptyJsonBody(request, config.maxRequestBytes);
252
- writeJson(response, 200, await materializeSessionArtifacts(await sessions.cancel(sessionId), artifactStore));
254
+ writeJson(response, 200, await sessions.cancel(sessionId));
253
255
  return;
254
256
  }
255
257
  if (action === 'events' && request.method === 'GET') {
@@ -262,66 +264,9 @@ async function handleRequest(context) {
262
264
  throw new RequestError(405, 'Method not allowed');
263
265
  }
264
266
  async function handleAdminRoute(context, url) {
265
- const { config, sessions, controlPlane, adminSessions, artifactStore, workspaceFiles, request, response } = context;
267
+ const { config, sessions, controlPlane, adminSessions, request, response } = context;
266
268
  if (!controlPlane)
267
269
  throw new RequestError(404, 'Control plane is unavailable');
268
- if (url.pathname === '/v1/admin/files/roots' && request.method === 'GET') {
269
- writeJson(response, 200, { roots: await workspaceFiles.roots() });
270
- return;
271
- }
272
- if (url.pathname === '/v1/admin/files' && request.method === 'GET') {
273
- const root = await workspaceFiles.adminRoot(requiredQuery(url, 'root'));
274
- writeJson(response, 200, await workspaceFiles.list(root, url.searchParams.get('path') || ''));
275
- return;
276
- }
277
- if (url.pathname === '/v1/admin/files/content') {
278
- const root = await workspaceFiles.adminRoot(requiredQuery(url, 'root'));
279
- const filePath = requiredQuery(url, 'path');
280
- if (request.method === 'GET') {
281
- const file = await workspaceFiles.open(root, filePath);
282
- await streamDownload(response, file.handle, file.name, file.size, mediaTypeForName(file.name));
283
- return;
284
- }
285
- if (request.method === 'PUT') {
286
- const declared = optionalContentLength(request);
287
- writeJson(response, 201, await workspaceFiles.write(root, filePath, request, declared));
288
- return;
289
- }
290
- if (request.method === 'DELETE') {
291
- await assertEmptyJsonBody(request, config.maxRequestBytes);
292
- writeJson(response, 200, await workspaceFiles.remove(root, filePath));
293
- return;
294
- }
295
- throw new RequestError(405, 'Method not allowed');
296
- }
297
- if (url.pathname === '/v1/admin/files/directory' && request.method === 'POST') {
298
- assertJsonContentType(request);
299
- const body = await readJsonBody(request, config.maxRequestBytes);
300
- assertOnlyKeys(body, ['root', 'path']);
301
- const root = await workspaceFiles.adminRoot(requiredString(body.root, 'root'));
302
- writeJson(response, 201, await workspaceFiles.createDirectory(root, requiredString(body.path, 'path')));
303
- return;
304
- }
305
- if (url.pathname === '/v1/admin/files/move' && request.method === 'POST') {
306
- assertJsonContentType(request);
307
- const body = await readJsonBody(request, config.maxRequestBytes);
308
- assertOnlyKeys(body, ['root', 'path', 'destination']);
309
- const root = await workspaceFiles.adminRoot(requiredString(body.root, 'root'));
310
- writeJson(response, 200, await workspaceFiles.move(root, requiredString(body.path, 'path'), requiredString(body.destination, 'destination')));
311
- return;
312
- }
313
- if (url.pathname === '/v1/admin/files/publish' && request.method === 'POST') {
314
- assertJsonContentType(request);
315
- const body = await readJsonBody(request, config.maxRequestBytes);
316
- assertOnlyKeys(body, ['root', 'paths']);
317
- const root = await workspaceFiles.adminRoot(requiredString(body.root, 'root'));
318
- const paths = requiredStringArray(body.paths, 'paths');
319
- if (paths.length > 32)
320
- throw new RequestError(400, 'paths must contain at most 32 values');
321
- const result = await publishWorkspaceFiles(workspaceFiles, artifactStore, root, paths);
322
- writeJson(response, result.errors.length ? 207 : 201, result);
323
- return;
324
- }
325
270
  if (url.pathname === '/v1/admin/overview' && request.method === 'GET') {
326
271
  const agents = await sessions.listAgents(undefined, url.searchParams.get('refresh') === '1');
327
272
  writeJson(response, 200, { agents, sessions: sessions.count() });
@@ -441,7 +386,6 @@ function readAgentUpdate(body) {
441
386
  'driver',
442
387
  'name',
443
388
  'description',
444
- 'instructions',
445
389
  'enabled',
446
390
  'workspace',
447
391
  'permissionPolicy',
@@ -459,7 +403,6 @@ function readAgentUpdate(body) {
459
403
  driver: optionalString(body.driver),
460
404
  name: optionalString(body.name),
461
405
  description: optionalString(body.description),
462
- instructions: optionalString(body.instructions),
463
406
  enabled: optionalBoolean(body.enabled, 'enabled'),
464
407
  workspace: optionalString(body.workspace),
465
408
  permissionPolicy: optionalString(body.permissionPolicy),
@@ -759,6 +702,14 @@ function optionalString(value) {
759
702
  throw new RequestError(400, 'Expected a string');
760
703
  return value.trim() || undefined;
761
704
  }
705
+ function optionalPendingAction(value) {
706
+ if (value === undefined || value === null || value === '')
707
+ return undefined;
708
+ if (value === 'accept' || value === 'decline' || value === 'cancel') {
709
+ return value;
710
+ }
711
+ throw new RequestError(400, 'action must be accept, decline, or cancel');
712
+ }
762
713
  function optionalBoolean(value, name) {
763
714
  if (value === undefined)
764
715
  return undefined;
@@ -788,22 +739,6 @@ function requiredStringArray(value, name, allowEmpty = false) {
788
739
  }
789
740
  return result;
790
741
  }
791
- function requiredQuery(url, name) {
792
- const value = url.searchParams.get(name)?.trim();
793
- if (!value)
794
- throw new RequestError(400, `${name} is required`);
795
- return value;
796
- }
797
- function optionalContentLength(request) {
798
- const raw = stringHeader(request.headers['content-length']);
799
- if (!raw)
800
- return undefined;
801
- const value = Number(raw);
802
- if (!Number.isSafeInteger(value) || value < 0) {
803
- throw new RequestError(400, 'Content-Length is invalid');
804
- }
805
- return value;
806
- }
807
742
  function cookieValue(request, name) {
808
743
  const cookie = stringHeader(request.headers.cookie);
809
744
  for (const part of cookie.split(';')) {
@@ -856,122 +791,6 @@ function safePath(value) {
856
791
  return '/invalid-url';
857
792
  }
858
793
  }
859
- function decodePathSegment(value) {
860
- try {
861
- return decodeURIComponent(value);
862
- }
863
- catch {
864
- throw new RequestError(400, 'URL path is invalid');
865
- }
866
- }
867
- function ownedSession(sessions, sessionId, principal) {
868
- const session = sessions.get(sessionId);
869
- if (!sessions.owns(sessionId, principal.id)) {
870
- throw new RequestError(404, 'Session not found');
871
- }
872
- assertAgentScope(principal, session.agentId);
873
- return session;
874
- }
875
- async function streamDownload(response, handle, name, size, mediaType) {
876
- try {
877
- response.writeHead(200, {
878
- 'Content-Type': mediaType,
879
- 'Content-Length': size,
880
- 'Content-Disposition': contentDisposition(name),
881
- 'Cache-Control': 'no-store',
882
- 'X-Content-Type-Options': 'nosniff'
883
- });
884
- await pipeline(handle.createReadStream({ autoClose: false }), response);
885
- }
886
- finally {
887
- await handle.close().catch(() => undefined);
888
- }
889
- }
890
- async function publishWorkspaceFiles(workspaceFiles, artifactStore, root, paths) {
891
- const files = [];
892
- const errors = [];
893
- let firstFailure;
894
- for (const value of paths) {
895
- let file;
896
- try {
897
- file = await workspaceFiles.publishable(root, value);
898
- files.push(await artifactStore.publishHandle(file.handle, file.name));
899
- }
900
- catch (error) {
901
- if (!(error instanceof WorkspaceFileError) && !(error instanceof ArtifactStoreError)) {
902
- throw error;
903
- }
904
- firstFailure ||= { status: error.status, message: error.message };
905
- errors.push({ path: value, error: error.message });
906
- }
907
- finally {
908
- await file?.handle.close().catch(() => undefined);
909
- }
910
- }
911
- if (!files.length && firstFailure) {
912
- throw new RequestError(firstFailure.status, firstFailure.message);
913
- }
914
- return { files, errors };
915
- }
916
- async function materializeSessionArtifacts(session, store) {
917
- const artifacts = await Promise.all(session.artifacts.map(async (artifact, index) => {
918
- if (!artifact.bytesBase64)
919
- return artifact;
920
- let bytes;
921
- try {
922
- bytes = decodeArtifactBase64(artifact.bytesBase64);
923
- }
924
- catch (error) {
925
- return omittedArtifact(artifact, errorMessage(error));
926
- }
927
- try {
928
- const published = await store.publishBytes(bytes, artifact.filename || artifact.name || `artifact-${artifact.id || randomUUID()}`, artifact.mediaType, `${session.id}:${artifact.id || index}`);
929
- return {
930
- ...artifact,
931
- name: artifact.name || published.name,
932
- filename: artifact.filename || published.name,
933
- url: published.url,
934
- bytesBase64: undefined,
935
- metadata: {
936
- ...artifact.metadata,
937
- publishedArtifactId: published.id,
938
- size: published.size,
939
- sha256: published.sha256,
940
- expiresAt: published.expiresAt
941
- }
942
- };
943
- }
944
- catch (error) {
945
- if (error instanceof ArtifactStoreError && error.status === 413) {
946
- return omittedArtifact(artifact, error.message);
947
- }
948
- throw error;
949
- }
950
- }));
951
- return { ...session, artifacts };
952
- }
953
- function decodeArtifactBase64(value) {
954
- const normalized = value.replace(/\s/g, '');
955
- if (!normalized ||
956
- normalized.length % 4 === 1 ||
957
- !/^[A-Za-z0-9+/]*={0,2}$/.test(normalized)) {
958
- throw new Error('Artifact contains invalid Base64 data');
959
- }
960
- const bytes = Buffer.from(normalized, 'base64');
961
- if (bytes.toString('base64').replace(/=+$/, '') !== normalized.replace(/=+$/, '')) {
962
- throw new Error('Artifact contains invalid Base64 data');
963
- }
964
- return bytes;
965
- }
966
- function omittedArtifact(artifact, reason) {
967
- return {
968
- ...artifact,
969
- bytesBase64: undefined,
970
- description: [artifact.description, `[binary artifact omitted: ${reason}]`]
971
- .filter(Boolean)
972
- .join('\n')
973
- };
974
- }
975
794
  function errorStatus(error) {
976
795
  if (error instanceof RunNotFoundError)
977
796
  return 404;
@@ -981,10 +800,6 @@ function errorStatus(error) {
981
800
  return error.status;
982
801
  if (error instanceof ControlPlaneError)
983
802
  return error.status;
984
- if (error instanceof ArtifactStoreError)
985
- return error.status;
986
- if (error instanceof WorkspaceFileError)
987
- return error.status;
988
803
  if (error instanceof RequestError)
989
804
  return error.status;
990
805
  return 500;
@@ -1,4 +1,4 @@
1
- import type { AgentdArtifact, AgentdEventType, AgentdInputAttachment, AgentdPendingRequest, AgentdSessionState } from './types.js';
1
+ import type { AgentdArtifact, AgentdEventType, AgentdInputAttachment, AgentdPendingRequest, AgentdPendingResponse, AgentdSessionState } from './types.js';
2
2
  export interface AgentSessionSink {
3
3
  readonly id: string;
4
4
  readonly state: AgentdSessionState;
@@ -18,7 +18,7 @@ export interface AcpSessionSink extends AgentSessionSink {
18
18
  export interface AgentSessionRuntime {
19
19
  start(workspace?: string): Promise<void>;
20
20
  prompt(message: string, attachments?: AgentdInputAttachment[]): Promise<void>;
21
- respondPending(message: string, attachments?: AgentdInputAttachment[]): Promise<void>;
21
+ respondPending(response: AgentdPendingResponse | string, attachments?: AgentdInputAttachment[]): Promise<void>;
22
22
  cancel(): Promise<void>;
23
23
  dispose(): Promise<void>;
24
24
  }
package/dist/session.d.ts CHANGED
@@ -2,7 +2,7 @@ import type { AgentDriver } from './drivers/index.js';
2
2
  import { SessionEventLog } from './events.js';
3
3
  import { RunStore, type RunListQuery } from './run-store.js';
4
4
  import type { AcpSessionSink, AgentSessionRuntime, AgentSessionSink } from './session-contract.js';
5
- import type { AgentdAgentView, AgentdArtifact, AgentdConfig, AgentdEvent, AgentdEventType, AgentdInputAttachmentView, AgentdPendingRequest, AgentdProtocol, AgentdSessionState, AgentdSessionView } from './types.js';
5
+ import type { AgentdAgentView, AgentdArtifact, AgentdConfig, AgentdEvent, AgentdEventType, AgentdInputAttachmentView, AgentdPendingRequest, AgentdPendingResponse, AgentdProtocol, AgentdSessionState, AgentdSessionView } from './types.js';
6
6
  import type { WorkspacePolicy } from './workspace.js';
7
7
  export declare class ManagedSession implements AgentSessionSink, AcpSessionSink {
8
8
  readonly agentId: string;
@@ -12,6 +12,7 @@ export declare class ManagedSession implements AgentSessionSink, AcpSessionSink
12
12
  private readonly maxOutputChars;
13
13
  private readonly agentName;
14
14
  private readonly runStore?;
15
+ readonly instanceId: string;
15
16
  readonly id: `${string}-${string}-${string}-${string}-${string}`;
16
17
  readonly createdAt: number;
17
18
  readonly events: SessionEventLog;
@@ -25,18 +26,20 @@ export declare class ManagedSession implements AgentSessionSink, AcpSessionSink
25
26
  private artifacts;
26
27
  private inputAttachments;
27
28
  private runtime?;
28
- constructor(agentId: string, protocol: AgentdProtocol, workspace: string | undefined, ownerKeyId: string, maxEvents: number, maxOutputChars: number, agentName: string, runStore?: RunStore | undefined);
29
+ constructor(agentId: string, protocol: AgentdProtocol, workspace: string | undefined, ownerKeyId: string, maxEvents: number, maxOutputChars: number, agentName: string, runStore?: RunStore | undefined, instanceId?: string);
29
30
  attach(runtime: AgentSessionRuntime): void;
30
31
  setProtocolSessionId(id: string): void;
31
32
  setAcpSessionId(id: string): void;
32
33
  setState(state: AgentdSessionState, error?: string): void;
33
34
  appendOutput(text: string): void;
34
35
  addArtifact(artifact: AgentdArtifact): void;
36
+ publishFile(filePath: string): Promise<AgentdArtifact>;
35
37
  setPending(request: AgentdPendingRequest): void;
36
38
  clearPending(): void;
37
39
  emit(type: AgentdEventType, data?: unknown): void;
38
40
  addInputAttachment(name: string, mediaType: string | undefined, bytes: Buffer): AgentdInputAttachmentView;
39
41
  message(message: string, attachmentIds?: string[]): Promise<void>;
42
+ resolvePending(response: AgentdPendingResponse, attachmentIds?: string[]): Promise<void>;
40
43
  cancel(): Promise<void>;
41
44
  dispose(): Promise<void>;
42
45
  snapshot(): AgentdSessionView;
@@ -50,6 +53,7 @@ export declare class SessionManager {
50
53
  private workspacePolicy;
51
54
  private drivers;
52
55
  private readonly runStore?;
56
+ readonly instanceId: `${string}-${string}-${string}-${string}-${string}`;
53
57
  private sessions;
54
58
  private cleanupTimer?;
55
59
  private readinessCache?;
@@ -67,8 +71,11 @@ export declare class SessionManager {
67
71
  };
68
72
  getRun(id: string): import("./types.js").AgentdRunDetail;
69
73
  message(id: string, message: string, attachmentIds?: string[]): Promise<AgentdSessionView>;
74
+ resolvePending(id: string, response: AgentdPendingResponse, attachmentIds?: string[]): Promise<AgentdSessionView>;
70
75
  addInputAttachment(id: string, name: string, mediaType: string | undefined, bytes: Buffer): AgentdInputAttachmentView;
76
+ publishFile(id: string, path: string): Promise<AgentdSessionView>;
71
77
  cancel(id: string): Promise<AgentdSessionView>;
78
+ close(id: string): Promise<AgentdSessionView>;
72
79
  eventsAfter(id: string, after?: string): AgentdEvent[];
73
80
  subscribe(id: string, listener: (event: AgentdEvent) => void): () => boolean;
74
81
  shutdown(): Promise<void>;