nexus-agentd 0.2.5 → 0.3.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.
- package/README.md +359 -250
- package/dist/a2a/runtime.d.ts +7 -1
- package/dist/a2a/runtime.js +290 -17
- package/dist/acp/runtime.d.ts +5 -1
- package/dist/acp/runtime.js +88 -39
- package/dist/artifact-store.d.ts +162 -0
- package/dist/artifact-store.js +1081 -0
- package/dist/cli.js +13 -14
- package/dist/completion-contract.js +1 -1
- package/dist/config.js +45 -5
- package/dist/control-plane-validation.d.ts +54 -0
- package/dist/control-plane-validation.js +152 -0
- package/dist/control-plane.d.ts +9 -32
- package/dist/control-plane.js +52 -158
- package/dist/drivers/index.js +12 -0
- package/dist/drivers/pi.js +1 -1
- package/dist/drivers/stdio.d.ts +4 -1
- package/dist/drivers/stdio.js +135 -19
- package/dist/events.d.ts +9 -0
- package/dist/events.js +17 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.js +33 -6
- package/dist/process-tree.js +15 -6
- package/dist/run-store.d.ts +57 -7
- package/dist/run-store.js +188 -23
- package/dist/server/auth-policy.d.ts +27 -0
- package/dist/server/auth-policy.js +143 -0
- package/dist/server/connection.d.ts +8 -0
- package/dist/server/connection.js +67 -0
- package/dist/server/diagnostics.d.ts +40 -0
- package/dist/server/diagnostics.js +130 -0
- package/dist/server/metrics.d.ts +51 -0
- package/dist/server/metrics.js +56 -0
- package/dist/server/quota.d.ts +59 -0
- package/dist/server/quota.js +176 -0
- package/dist/server/validation.d.ts +29 -0
- package/dist/server/validation.js +221 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.js +682 -418
- package/dist/session/admin-runs.d.ts +73 -0
- package/dist/session/admin-runs.js +297 -0
- package/dist/session/admission.d.ts +32 -0
- package/dist/session/admission.js +62 -0
- package/dist/session/readiness.d.ts +33 -0
- package/dist/session/readiness.js +174 -0
- package/dist/session-contract.d.ts +1 -0
- package/dist/session.d.ts +64 -7
- package/dist/session.js +321 -159
- package/dist/sse-writer.d.ts +7 -0
- package/dist/sse-writer.js +60 -0
- package/dist/types.d.ts +44 -2
- package/dist/types.js +1 -0
- package/dist/webui/app/sources.js +18 -10
- package/dist/webui/index.js +11 -11
- package/dist/webui/markup.js +69 -66
- package/dist/webui/styles.js +791 -616
- package/dist/workspace.d.ts +30 -0
- package/dist/workspace.js +67 -2
- package/examples/client.mjs +102 -0
- package/nexus-agentd.example.json +21 -2
- package/package.json +6 -3
package/dist/a2a/runtime.d.ts
CHANGED
|
@@ -10,6 +10,8 @@ export declare class A2AClientRuntime implements AgentSessionRuntime {
|
|
|
10
10
|
private taskId;
|
|
11
11
|
private contextId;
|
|
12
12
|
private prompting;
|
|
13
|
+
private promptFinished?;
|
|
14
|
+
private queuedResponse;
|
|
13
15
|
private disposed;
|
|
14
16
|
private activeController?;
|
|
15
17
|
private readonly artifactCache;
|
|
@@ -17,12 +19,16 @@ export declare class A2AClientRuntime implements AgentSessionRuntime {
|
|
|
17
19
|
private sawTaskStatus;
|
|
18
20
|
private sawAgentMessage;
|
|
19
21
|
private completionProof?;
|
|
22
|
+
private readonly taskTimeoutMs;
|
|
23
|
+
private readonly streamIdleTimeoutMs;
|
|
24
|
+
private taskDeadlineAt?;
|
|
20
25
|
constructor(config: AgentdA2AConfig, sink: AgentSessionSink, promptTimeoutMs: number);
|
|
21
26
|
start(): Promise<void>;
|
|
22
|
-
prompt(message: string, attachments?: AgentdInputAttachment[]): Promise<void>;
|
|
27
|
+
prompt(message: string, attachments?: AgentdInputAttachment[], continuation?: boolean): Promise<void>;
|
|
23
28
|
respondPending(response: AgentdPendingResponse | string, attachments?: AgentdInputAttachment[]): Promise<void>;
|
|
24
29
|
cancel(): Promise<void>;
|
|
25
30
|
dispose(): Promise<void>;
|
|
31
|
+
isAvailable(): boolean;
|
|
26
32
|
private messageRequest;
|
|
27
33
|
private processResponse;
|
|
28
34
|
private processTask;
|
package/dist/a2a/runtime.js
CHANGED
|
@@ -44,6 +44,8 @@ export class A2AClientRuntime {
|
|
|
44
44
|
taskId = '';
|
|
45
45
|
contextId = '';
|
|
46
46
|
prompting = false;
|
|
47
|
+
promptFinished;
|
|
48
|
+
queuedResponse = false;
|
|
47
49
|
disposed = false;
|
|
48
50
|
activeController;
|
|
49
51
|
artifactCache = new Map();
|
|
@@ -51,12 +53,23 @@ export class A2AClientRuntime {
|
|
|
51
53
|
sawTaskStatus = false;
|
|
52
54
|
sawAgentMessage = false;
|
|
53
55
|
completionProof;
|
|
56
|
+
taskTimeoutMs;
|
|
57
|
+
streamIdleTimeoutMs;
|
|
58
|
+
taskDeadlineAt;
|
|
54
59
|
constructor(config, sink, promptTimeoutMs) {
|
|
55
60
|
this.config = config;
|
|
56
61
|
this.sink = sink;
|
|
57
62
|
this.promptTimeoutMs = promptTimeoutMs;
|
|
63
|
+
// Keep inheritance at runtime so changing the gateway-wide prompt
|
|
64
|
+
// timeout continues to affect agents that did not opt into a local
|
|
65
|
+
// task deadline. The per-request timeout is deliberately excluded
|
|
66
|
+
// from this calculation: it only bounds connection/headers.
|
|
67
|
+
this.taskTimeoutMs = positiveTimeout(config.taskTimeoutMs ?? promptTimeoutMs, promptTimeoutMs);
|
|
68
|
+
this.streamIdleTimeoutMs = positiveTimeout(config.streamIdleTimeoutMs ?? config.timeoutMs, config.timeoutMs || 60_000);
|
|
58
69
|
}
|
|
59
70
|
async start() {
|
|
71
|
+
if (this.disposed)
|
|
72
|
+
throw new Error('A2A runtime is disposed');
|
|
60
73
|
const resolver = createResolver(this.config);
|
|
61
74
|
this.card = await resolveAgentCard(resolver, this.config);
|
|
62
75
|
const fetchImpl = authenticatedFetch(this.config);
|
|
@@ -70,9 +83,11 @@ export class A2AClientRuntime {
|
|
|
70
83
|
]
|
|
71
84
|
});
|
|
72
85
|
this.client = await factory.createFromAgentCard(this.card);
|
|
86
|
+
if (this.disposed)
|
|
87
|
+
throw new Error('A2A runtime is disposed');
|
|
73
88
|
this.sink.setState('created');
|
|
74
89
|
}
|
|
75
|
-
async prompt(message, attachments = []) {
|
|
90
|
+
async prompt(message, attachments = [], continuation = false) {
|
|
76
91
|
if (!this.client || !this.card)
|
|
77
92
|
throw new Error('A2A runtime is not connected');
|
|
78
93
|
if (this.prompting)
|
|
@@ -80,23 +95,36 @@ export class A2AClientRuntime {
|
|
|
80
95
|
if (this.disposed)
|
|
81
96
|
throw new Error('A2A runtime is disposed');
|
|
82
97
|
this.prompting = true;
|
|
98
|
+
let finish;
|
|
99
|
+
this.promptFinished = new Promise((resolve) => { finish = resolve; });
|
|
83
100
|
this.sawTaskStatus = false;
|
|
84
101
|
this.sawAgentMessage = false;
|
|
85
102
|
this.completionProof = undefined;
|
|
86
103
|
this.sink.clearPending();
|
|
87
104
|
this.sink.setState('running');
|
|
105
|
+
const taskDeadlineAt = continuation && this.taskDeadlineAt !== undefined
|
|
106
|
+
? this.taskDeadlineAt
|
|
107
|
+
: Date.now() + this.taskTimeoutMs;
|
|
108
|
+
this.taskDeadlineAt = taskDeadlineAt;
|
|
88
109
|
const controller = new AbortController();
|
|
89
110
|
this.activeController = controller;
|
|
90
|
-
const
|
|
111
|
+
const remainingTaskMs = Math.max(1, taskDeadlineAt - Date.now());
|
|
112
|
+
const timer = setTimeout(() => controller.abort(new A2ATaskTimeoutError(this.taskTimeoutMs)), remainingTaskMs);
|
|
91
113
|
timer.unref?.();
|
|
92
114
|
try {
|
|
93
115
|
const request = this.messageRequest(message, attachments);
|
|
94
116
|
for await (const response of this.client.sendMessageStream(request, {
|
|
95
117
|
signal: controller.signal
|
|
96
118
|
})) {
|
|
119
|
+
if (controller.signal.aborted || this.disposed)
|
|
120
|
+
break;
|
|
121
|
+
// An accepted answer supersedes the old input-required stream.
|
|
122
|
+
// Its trailing status must not reopen the answered request.
|
|
123
|
+
if (this.queuedResponse)
|
|
124
|
+
continue;
|
|
97
125
|
this.processResponse(response);
|
|
98
126
|
}
|
|
99
|
-
if (this.sink.state === 'running') {
|
|
127
|
+
if (this.sink.state === 'running' && !this.queuedResponse) {
|
|
100
128
|
if (this.completionProof) {
|
|
101
129
|
this.sink.completeTurn(this.completionProof);
|
|
102
130
|
}
|
|
@@ -114,7 +142,7 @@ export class A2AClientRuntime {
|
|
|
114
142
|
}
|
|
115
143
|
}
|
|
116
144
|
catch (error) {
|
|
117
|
-
if (this.sink.state !== 'canceled') {
|
|
145
|
+
if (this.sink.state !== 'canceled' && !this.queuedResponse) {
|
|
118
146
|
this.sink.setState('failed', abortMessage(error, controller.signal));
|
|
119
147
|
}
|
|
120
148
|
}
|
|
@@ -123,9 +151,16 @@ export class A2AClientRuntime {
|
|
|
123
151
|
if (this.activeController === controller)
|
|
124
152
|
this.activeController = undefined;
|
|
125
153
|
this.prompting = false;
|
|
154
|
+
if (this.sink.state !== 'input_required' && !this.queuedResponse) {
|
|
155
|
+
this.taskDeadlineAt = undefined;
|
|
156
|
+
}
|
|
157
|
+
finish();
|
|
158
|
+
this.promptFinished = undefined;
|
|
126
159
|
}
|
|
127
160
|
}
|
|
128
161
|
async respondPending(response, attachments = []) {
|
|
162
|
+
if (!this.isAvailable())
|
|
163
|
+
throw new Error('A2A runtime is unavailable');
|
|
129
164
|
if (this.sink.state !== 'input_required') {
|
|
130
165
|
throw new Error('A2A session is not waiting for input');
|
|
131
166
|
}
|
|
@@ -138,10 +173,27 @@ export class A2AClientRuntime {
|
|
|
138
173
|
: response.message ?? response.optionId ?? '';
|
|
139
174
|
if (!message.trim())
|
|
140
175
|
throw new Error('A2A pending response is empty');
|
|
176
|
+
const previous = this.promptFinished || Promise.resolve();
|
|
177
|
+
this.queuedResponse = true;
|
|
141
178
|
this.sink.clearPending();
|
|
142
|
-
|
|
179
|
+
this.sink.setState('running');
|
|
180
|
+
void previous.then(async () => {
|
|
181
|
+
this.queuedResponse = false;
|
|
182
|
+
if (this.disposed ||
|
|
183
|
+
this.sink.state === 'canceled' ||
|
|
184
|
+
this.sink.state === 'failed' ||
|
|
185
|
+
(this.taskDeadlineAt !== undefined && this.taskDeadlineAt <= Date.now()))
|
|
186
|
+
return;
|
|
187
|
+
await this.prompt(message, attachments, true);
|
|
188
|
+
}).catch((error) => {
|
|
189
|
+
if (!this.disposed && this.sink.state !== 'canceled')
|
|
190
|
+
this.sink.setState('failed', errorMessage(error));
|
|
191
|
+
});
|
|
143
192
|
}
|
|
144
193
|
async cancel() {
|
|
194
|
+
this.taskDeadlineAt = undefined;
|
|
195
|
+
this.sink.clearPending();
|
|
196
|
+
this.sink.setState('canceled');
|
|
145
197
|
this.activeController?.abort(new Error('A2A session canceled'));
|
|
146
198
|
if (this.client && this.taskId) {
|
|
147
199
|
try {
|
|
@@ -159,9 +211,13 @@ export class A2AClientRuntime {
|
|
|
159
211
|
}
|
|
160
212
|
async dispose() {
|
|
161
213
|
this.disposed = true;
|
|
214
|
+
this.taskDeadlineAt = undefined;
|
|
162
215
|
this.activeController?.abort(new Error('A2A runtime disposed'));
|
|
163
216
|
this.activeController = undefined;
|
|
164
217
|
}
|
|
218
|
+
isAvailable() {
|
|
219
|
+
return !this.disposed && Boolean(this.client && this.card);
|
|
220
|
+
}
|
|
165
221
|
messageRequest(text, attachments) {
|
|
166
222
|
return {
|
|
167
223
|
tenant: '',
|
|
@@ -338,12 +394,43 @@ function createResolver(config) {
|
|
|
338
394
|
legacyCompat: { enabled: true }
|
|
339
395
|
});
|
|
340
396
|
}
|
|
341
|
-
function resolveAgentCard(resolver, config) {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
397
|
+
async function resolveAgentCard(resolver, config) {
|
|
398
|
+
const origin = configuredOrigin(config);
|
|
399
|
+
const card = config.agentCardUrl
|
|
400
|
+
? await resolver.resolve(config.agentCardUrl, '')
|
|
401
|
+
: await resolver.resolve(config.agentUrl);
|
|
402
|
+
// The resolver normalizes v0.3 url/additionalInterfaces into this array.
|
|
403
|
+
// Validate every advertised endpoint before transport selection or probing.
|
|
404
|
+
if (!Array.isArray(card.supportedInterfaces) || !card.supportedInterfaces.length) {
|
|
405
|
+
throw new Error('A2A Agent Card must declare at least one interface URL');
|
|
406
|
+
}
|
|
407
|
+
for (const endpoint of card.supportedInterfaces)
|
|
408
|
+
assertSameOrigin(endpoint?.url, origin);
|
|
409
|
+
return card;
|
|
410
|
+
}
|
|
411
|
+
function a2aUrl(value) {
|
|
412
|
+
let url;
|
|
413
|
+
try {
|
|
414
|
+
url = new URL(value);
|
|
415
|
+
}
|
|
416
|
+
catch {
|
|
417
|
+
throw new Error('A2A URL is invalid');
|
|
418
|
+
}
|
|
419
|
+
if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password || url.hash) {
|
|
420
|
+
throw new Error('A2A URL must use http(s) without credentials or a fragment');
|
|
421
|
+
}
|
|
422
|
+
return url;
|
|
423
|
+
}
|
|
424
|
+
function configuredOrigin(config) {
|
|
425
|
+
const value = config.agentCardUrl || config.agentUrl;
|
|
426
|
+
if (!value)
|
|
427
|
+
throw new Error('A2A Agent Card URL is required');
|
|
428
|
+
return a2aUrl(value).origin;
|
|
429
|
+
}
|
|
430
|
+
function assertSameOrigin(value, origin) {
|
|
431
|
+
if (a2aUrl(value).origin !== origin) {
|
|
432
|
+
throw new Error('A2A URL must have the same origin as the configured Agent Card URL');
|
|
433
|
+
}
|
|
347
434
|
}
|
|
348
435
|
function preferredTransports(config) {
|
|
349
436
|
if (config.preferredTransport === 'jsonrpc')
|
|
@@ -353,8 +440,12 @@ function preferredTransports(config) {
|
|
|
353
440
|
return undefined;
|
|
354
441
|
}
|
|
355
442
|
function authenticatedFetch(config) {
|
|
443
|
+
const origin = configuredOrigin(config);
|
|
356
444
|
return async (input, init = {}) => {
|
|
357
|
-
|
|
445
|
+
// The final guard also covers SDK-derived paths and future card flows.
|
|
446
|
+
// Check before attaching credentials; never let fetch follow redirects.
|
|
447
|
+
assertSameOrigin(input instanceof Request ? input.url : String(input), origin);
|
|
448
|
+
const headers = new Headers(init.headers ?? (input instanceof Request ? input.headers : undefined));
|
|
358
449
|
if (config.auth?.type === 'bearer' && config.auth.value) {
|
|
359
450
|
headers.set('Authorization', `Bearer ${config.auth.value}`);
|
|
360
451
|
}
|
|
@@ -363,13 +454,195 @@ function authenticatedFetch(config) {
|
|
|
363
454
|
config.auth.value) {
|
|
364
455
|
headers.set(config.auth.headerName, config.auth.value);
|
|
365
456
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
457
|
+
const timeoutMs = config.timeoutMs || 60_000;
|
|
458
|
+
const timeoutController = new AbortController();
|
|
459
|
+
const signals = [timeoutController.signal];
|
|
460
|
+
if (init.signal)
|
|
461
|
+
signals.push(init.signal);
|
|
462
|
+
const signal = AbortSignal.any(signals);
|
|
463
|
+
const requestTimeout = new A2ARequestTimeoutError(timeoutMs);
|
|
464
|
+
let bodyAbort;
|
|
465
|
+
const timer = setTimeout(() => {
|
|
466
|
+
timeoutController.abort(requestTimeout);
|
|
467
|
+
bodyAbort?.();
|
|
468
|
+
}, timeoutMs);
|
|
469
|
+
timer.unref?.();
|
|
470
|
+
try {
|
|
471
|
+
const response = await fetch(input, {
|
|
472
|
+
...init,
|
|
473
|
+
headers,
|
|
474
|
+
redirect: 'error',
|
|
475
|
+
signal
|
|
476
|
+
});
|
|
477
|
+
// Only an SSE response is an intentionally long-lived request.
|
|
478
|
+
// Card and ordinary JSON calls retain timeoutMs through the body;
|
|
479
|
+
// otherwise a slow/trickling Card could evade the request bound.
|
|
480
|
+
if (isEventStream(response)) {
|
|
481
|
+
clearTimeout(timer);
|
|
482
|
+
return withStreamIdleTimeout(response, config.streamIdleTimeoutMs || timeoutMs);
|
|
483
|
+
}
|
|
484
|
+
if (!response.body) {
|
|
485
|
+
clearTimeout(timer);
|
|
486
|
+
return response;
|
|
487
|
+
}
|
|
488
|
+
const deadline = responseDeadlineBody(response.body, requestTimeout, () => clearTimeout(timer));
|
|
489
|
+
bodyAbort = deadline.abort;
|
|
490
|
+
return new Response(deadline.stream, {
|
|
491
|
+
status: response.status,
|
|
492
|
+
statusText: response.statusText,
|
|
493
|
+
headers: response.headers
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
catch (error) {
|
|
497
|
+
clearTimeout(timer);
|
|
498
|
+
if (timeoutController.signal.aborted && !init.signal?.aborted) {
|
|
499
|
+
throw requestTimeout;
|
|
500
|
+
}
|
|
501
|
+
throw error;
|
|
502
|
+
}
|
|
371
503
|
};
|
|
372
504
|
}
|
|
505
|
+
class A2ARequestTimeoutError extends Error {
|
|
506
|
+
constructor(timeoutMs) {
|
|
507
|
+
super(`A2A request timed out after ${timeoutMs}ms`);
|
|
508
|
+
this.name = 'A2ARequestTimeoutError';
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
class A2ATaskTimeoutError extends Error {
|
|
512
|
+
constructor(timeoutMs) {
|
|
513
|
+
super(`A2A task timed out after ${timeoutMs}ms`);
|
|
514
|
+
this.name = 'A2ATaskTimeoutError';
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
class A2AStreamIdleTimeoutError extends Error {
|
|
518
|
+
constructor(timeoutMs) {
|
|
519
|
+
super(`A2A stream made no progress for ${timeoutMs}ms`);
|
|
520
|
+
this.name = 'A2AStreamIdleTimeoutError';
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
function withStreamIdleTimeout(response, timeoutMs) {
|
|
524
|
+
if (!response.body || !Number.isFinite(timeoutMs) || timeoutMs <= 0)
|
|
525
|
+
return response;
|
|
526
|
+
const body = idleTimeoutBody(response.body, timeoutMs);
|
|
527
|
+
return new Response(body, {
|
|
528
|
+
status: response.status,
|
|
529
|
+
statusText: response.statusText,
|
|
530
|
+
headers: response.headers
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
function isEventStream(response) {
|
|
534
|
+
return response.headers.get('content-type')?.split(';', 1)[0].trim().toLowerCase() ===
|
|
535
|
+
'text/event-stream';
|
|
536
|
+
}
|
|
537
|
+
function responseDeadlineBody(body, timeoutError, cleanup) {
|
|
538
|
+
const reader = body.getReader();
|
|
539
|
+
let controllerRef;
|
|
540
|
+
let closed = false;
|
|
541
|
+
let timedOut = false;
|
|
542
|
+
const abort = () => {
|
|
543
|
+
if (closed || timedOut)
|
|
544
|
+
return;
|
|
545
|
+
timedOut = true;
|
|
546
|
+
void reader.cancel(timeoutError).catch(() => undefined);
|
|
547
|
+
controllerRef?.error(timeoutError);
|
|
548
|
+
};
|
|
549
|
+
const stream = new ReadableStream({
|
|
550
|
+
start(controller) {
|
|
551
|
+
controllerRef = controller;
|
|
552
|
+
},
|
|
553
|
+
async pull(controller) {
|
|
554
|
+
if (closed || timedOut)
|
|
555
|
+
return;
|
|
556
|
+
try {
|
|
557
|
+
const result = await reader.read();
|
|
558
|
+
if (timedOut)
|
|
559
|
+
return;
|
|
560
|
+
if (result.done) {
|
|
561
|
+
closed = true;
|
|
562
|
+
cleanup();
|
|
563
|
+
controller.close();
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
controller.enqueue(result.value);
|
|
567
|
+
}
|
|
568
|
+
catch (error) {
|
|
569
|
+
if (timedOut)
|
|
570
|
+
return;
|
|
571
|
+
closed = true;
|
|
572
|
+
cleanup();
|
|
573
|
+
controller.error(error);
|
|
574
|
+
}
|
|
575
|
+
},
|
|
576
|
+
async cancel(reason) {
|
|
577
|
+
closed = true;
|
|
578
|
+
cleanup();
|
|
579
|
+
await reader.cancel(reason);
|
|
580
|
+
}
|
|
581
|
+
});
|
|
582
|
+
return { stream, abort };
|
|
583
|
+
}
|
|
584
|
+
function idleTimeoutBody(body, timeoutMs) {
|
|
585
|
+
const reader = body.getReader();
|
|
586
|
+
let controllerRef;
|
|
587
|
+
let timer;
|
|
588
|
+
let closed = false;
|
|
589
|
+
let timedOut = false;
|
|
590
|
+
const clearIdleTimer = () => {
|
|
591
|
+
if (timer)
|
|
592
|
+
clearTimeout(timer);
|
|
593
|
+
timer = undefined;
|
|
594
|
+
};
|
|
595
|
+
const armIdleTimer = () => {
|
|
596
|
+
clearIdleTimer();
|
|
597
|
+
timer = setTimeout(() => {
|
|
598
|
+
if (closed || timedOut)
|
|
599
|
+
return;
|
|
600
|
+
timedOut = true;
|
|
601
|
+
const error = new A2AStreamIdleTimeoutError(timeoutMs);
|
|
602
|
+
void reader.cancel(error).catch(() => undefined);
|
|
603
|
+
controllerRef?.error(error);
|
|
604
|
+
}, timeoutMs);
|
|
605
|
+
timer.unref?.();
|
|
606
|
+
};
|
|
607
|
+
return new ReadableStream({
|
|
608
|
+
start(controller) {
|
|
609
|
+
controllerRef = controller;
|
|
610
|
+
armIdleTimer();
|
|
611
|
+
},
|
|
612
|
+
async pull(controller) {
|
|
613
|
+
if (closed || timedOut)
|
|
614
|
+
return;
|
|
615
|
+
try {
|
|
616
|
+
const result = await reader.read();
|
|
617
|
+
if (timedOut)
|
|
618
|
+
return;
|
|
619
|
+
if (result.done) {
|
|
620
|
+
closed = true;
|
|
621
|
+
clearIdleTimer();
|
|
622
|
+
controller.close();
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
armIdleTimer();
|
|
626
|
+
controller.enqueue(result.value);
|
|
627
|
+
}
|
|
628
|
+
catch (error) {
|
|
629
|
+
if (timedOut)
|
|
630
|
+
return;
|
|
631
|
+
closed = true;
|
|
632
|
+
clearIdleTimer();
|
|
633
|
+
controller.error(error);
|
|
634
|
+
}
|
|
635
|
+
},
|
|
636
|
+
async cancel(reason) {
|
|
637
|
+
closed = true;
|
|
638
|
+
clearIdleTimer();
|
|
639
|
+
await reader.cancel(reason);
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
function positiveTimeout(value, fallback) {
|
|
644
|
+
return Number.isFinite(value) && value > 0 ? Math.trunc(value) : fallback;
|
|
645
|
+
}
|
|
373
646
|
function artifactFromA2A(artifact) {
|
|
374
647
|
const text = textParts(artifact.parts).join('') || undefined;
|
|
375
648
|
const urlPart = artifact.parts.find((part) => part.content?.$case === 'url');
|
package/dist/acp/runtime.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ import type { AgentdInputAttachment, AgentdPendingResponse } from '../types.js';
|
|
|
4
4
|
export declare class AcpProcessRuntime {
|
|
5
5
|
private readonly driver;
|
|
6
6
|
private readonly sink;
|
|
7
|
+
private readonly allowedRoots;
|
|
8
|
+
private readonly startupTimeoutMs;
|
|
7
9
|
private process?;
|
|
8
10
|
private connection?;
|
|
9
11
|
private pending?;
|
|
@@ -16,12 +18,14 @@ export declare class AcpProcessRuntime {
|
|
|
16
18
|
private inputDirectory?;
|
|
17
19
|
private workspace?;
|
|
18
20
|
private promptOutput;
|
|
19
|
-
constructor(driver: AgentDriver, sink: AcpSessionSink, maxStderrChunkChars?: number, promptTimeoutMs?: number);
|
|
21
|
+
constructor(driver: AgentDriver, sink: AcpSessionSink, maxStderrChunkChars?: number, promptTimeoutMs?: number, allowedRoots?: readonly string[], startupTimeoutMs?: number);
|
|
20
22
|
start(workspace: string): Promise<void>;
|
|
23
|
+
private connect;
|
|
21
24
|
prompt(message: string, attachments?: AgentdInputAttachment[]): Promise<void>;
|
|
22
25
|
respondPending(input: AgentdPendingResponse | string, _attachments?: AgentdInputAttachment[]): Promise<void>;
|
|
23
26
|
cancel(): Promise<void>;
|
|
24
27
|
dispose(): Promise<void>;
|
|
28
|
+
isAvailable(): boolean;
|
|
25
29
|
private requestPermission;
|
|
26
30
|
private finishPermission;
|
|
27
31
|
private requestInput;
|