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/LICENSE +22 -22
- package/README.md +215 -439
- package/dist/a2a/runtime.d.ts +2 -4
- package/dist/a2a/runtime.js +11 -10
- package/dist/acp/runtime.d.ts +3 -5
- package/dist/acp/runtime.js +40 -16
- package/dist/config.js +0 -12
- package/dist/control-plane.d.ts +0 -1
- package/dist/control-plane.js +0 -17
- package/dist/server.js +63 -248
- package/dist/session-contract.d.ts +2 -2
- package/dist/session.d.ts +9 -2
- package/dist/session.js +149 -7
- package/dist/types.d.ts +8 -44
- package/dist/webui/app.js +912 -1073
- package/dist/webui/icons.d.ts +0 -1
- package/dist/webui/icons.js +0 -1
- package/dist/webui/index.js +10 -10
- package/dist/webui/markup.js +65 -66
- package/dist/webui/styles.js +256 -268
- package/nexus-agentd.example.json +40 -47
- package/package.json +52 -52
- package/dist/agent-instructions.d.ts +0 -11
- package/dist/agent-instructions.js +0 -44
- package/dist/artifact-store.d.ts +0 -41
- package/dist/artifact-store.js +0 -340
- package/dist/workspace-files.d.ts +0 -48
- package/dist/workspace-files.js +0 -269
package/dist/a2a/runtime.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AgentSessionRuntime, AgentSessionSink } from '../session-contract.js';
|
|
2
|
-
import type { AgentdA2AConfig, AgentdAgentView, AgentdInputAttachment } from '../types.js';
|
|
2
|
+
import type { AgentdA2AConfig, AgentdAgentView, AgentdInputAttachment, AgentdPendingResponse } from '../types.js';
|
|
3
3
|
export declare function probeA2AAgent(id: string, config: AgentdA2AConfig): Promise<AgentdAgentView>;
|
|
4
4
|
export declare class A2AClientRuntime implements AgentSessionRuntime {
|
|
5
5
|
private readonly config;
|
|
@@ -10,8 +10,6 @@ export declare class A2AClientRuntime implements AgentSessionRuntime {
|
|
|
10
10
|
private taskId;
|
|
11
11
|
private contextId;
|
|
12
12
|
private prompting;
|
|
13
|
-
private firstPrompt;
|
|
14
|
-
private readonly instructions;
|
|
15
13
|
private disposed;
|
|
16
14
|
private activeController?;
|
|
17
15
|
private readonly artifactCache;
|
|
@@ -19,7 +17,7 @@ export declare class A2AClientRuntime implements AgentSessionRuntime {
|
|
|
19
17
|
constructor(config: AgentdA2AConfig, sink: AgentSessionSink, promptTimeoutMs: number);
|
|
20
18
|
start(): Promise<void>;
|
|
21
19
|
prompt(message: string, attachments?: AgentdInputAttachment[]): Promise<void>;
|
|
22
|
-
respondPending(
|
|
20
|
+
respondPending(response: AgentdPendingResponse | string, attachments?: AgentdInputAttachment[]): Promise<void>;
|
|
23
21
|
cancel(): Promise<void>;
|
|
24
22
|
dispose(): Promise<void>;
|
|
25
23
|
private messageRequest;
|
package/dist/a2a/runtime.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { Role, TaskState } from '@a2a-js/sdk';
|
|
3
3
|
import { ClientFactory, DefaultAgentCardResolver, JsonRpcTransportFactory, RestTransportFactory } from '@a2a-js/sdk/client';
|
|
4
|
-
import { buildAgentInstructions, composeInitialAgentPrompt } from '../agent-instructions.js';
|
|
5
4
|
export async function probeA2AAgent(id, config) {
|
|
6
5
|
const startedAt = Date.now();
|
|
7
6
|
if (config.enabled === false)
|
|
@@ -44,8 +43,6 @@ export class A2AClientRuntime {
|
|
|
44
43
|
taskId = '';
|
|
45
44
|
contextId = '';
|
|
46
45
|
prompting = false;
|
|
47
|
-
firstPrompt = true;
|
|
48
|
-
instructions;
|
|
49
46
|
disposed = false;
|
|
50
47
|
activeController;
|
|
51
48
|
artifactCache = new Map();
|
|
@@ -54,7 +51,6 @@ export class A2AClientRuntime {
|
|
|
54
51
|
this.config = config;
|
|
55
52
|
this.sink = sink;
|
|
56
53
|
this.promptTimeoutMs = promptTimeoutMs;
|
|
57
|
-
this.instructions = buildAgentInstructions(config.instructions);
|
|
58
54
|
}
|
|
59
55
|
async start() {
|
|
60
56
|
const resolver = createResolver(this.config);
|
|
@@ -82,16 +78,12 @@ export class A2AClientRuntime {
|
|
|
82
78
|
this.prompting = true;
|
|
83
79
|
this.sink.clearPending();
|
|
84
80
|
this.sink.setState('running');
|
|
85
|
-
const promptMessage = this.firstPrompt
|
|
86
|
-
? composeInitialAgentPrompt(this.instructions, message)
|
|
87
|
-
: message;
|
|
88
|
-
this.firstPrompt = false;
|
|
89
81
|
const controller = new AbortController();
|
|
90
82
|
this.activeController = controller;
|
|
91
83
|
const timer = setTimeout(() => controller.abort(new Error('A2A message timed out')), Math.min(this.promptTimeoutMs, this.config.timeoutMs || this.promptTimeoutMs));
|
|
92
84
|
timer.unref?.();
|
|
93
85
|
try {
|
|
94
|
-
const request = this.messageRequest(
|
|
86
|
+
const request = this.messageRequest(message, attachments);
|
|
95
87
|
for await (const response of this.client.sendMessageStream(request, {
|
|
96
88
|
signal: controller.signal
|
|
97
89
|
})) {
|
|
@@ -112,10 +104,19 @@ export class A2AClientRuntime {
|
|
|
112
104
|
this.prompting = false;
|
|
113
105
|
}
|
|
114
106
|
}
|
|
115
|
-
async respondPending(
|
|
107
|
+
async respondPending(response, attachments = []) {
|
|
116
108
|
if (this.sink.state !== 'input_required') {
|
|
117
109
|
throw new Error('A2A session is not waiting for input');
|
|
118
110
|
}
|
|
111
|
+
const message = typeof response === 'string'
|
|
112
|
+
? response
|
|
113
|
+
: response.action === 'cancel'
|
|
114
|
+
? 'cancel'
|
|
115
|
+
: response.action === 'decline'
|
|
116
|
+
? 'decline'
|
|
117
|
+
: response.message ?? response.optionId ?? '';
|
|
118
|
+
if (!message.trim())
|
|
119
|
+
throw new Error('A2A pending response is empty');
|
|
119
120
|
this.sink.clearPending();
|
|
120
121
|
await this.prompt(message, attachments);
|
|
121
122
|
}
|
package/dist/acp/runtime.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AgentDriver } from '../drivers/index.js';
|
|
2
2
|
import type { AcpSessionSink } from '../session-contract.js';
|
|
3
|
-
import type { AgentdInputAttachment } from '../types.js';
|
|
3
|
+
import type { AgentdInputAttachment, AgentdPendingResponse } from '../types.js';
|
|
4
4
|
export declare class AcpProcessRuntime {
|
|
5
5
|
private readonly driver;
|
|
6
6
|
private readonly sink;
|
|
@@ -12,14 +12,12 @@ export declare class AcpProcessRuntime {
|
|
|
12
12
|
private prompting;
|
|
13
13
|
private readonly maxStderrChunkChars;
|
|
14
14
|
private readonly promptTimeoutMs;
|
|
15
|
-
private readonly instructions;
|
|
16
|
-
private firstPrompt;
|
|
17
15
|
private promptCapabilities;
|
|
18
16
|
private inputDirectory?;
|
|
19
|
-
constructor(driver: AgentDriver, sink: AcpSessionSink, maxStderrChunkChars?: number, promptTimeoutMs?: number
|
|
17
|
+
constructor(driver: AgentDriver, sink: AcpSessionSink, maxStderrChunkChars?: number, promptTimeoutMs?: number);
|
|
20
18
|
start(workspace: string): Promise<void>;
|
|
21
19
|
prompt(message: string, attachments?: AgentdInputAttachment[]): Promise<void>;
|
|
22
|
-
respondPending(
|
|
20
|
+
respondPending(input: AgentdPendingResponse | string, _attachments?: AgentdInputAttachment[]): Promise<void>;
|
|
23
21
|
cancel(): Promise<void>;
|
|
24
22
|
dispose(): Promise<void>;
|
|
25
23
|
private requestPermission;
|
package/dist/acp/runtime.js
CHANGED
|
@@ -4,7 +4,6 @@ import path from 'node:path';
|
|
|
4
4
|
import { Readable, Writable } from 'node:stream';
|
|
5
5
|
import { pathToFileURL } from 'node:url';
|
|
6
6
|
import * as acp from '@agentclientprotocol/sdk';
|
|
7
|
-
import { buildAgentInstructions, composeInitialAgentPrompt } from '../agent-instructions.js';
|
|
8
7
|
export class AcpProcessRuntime {
|
|
9
8
|
driver;
|
|
10
9
|
sink;
|
|
@@ -16,11 +15,9 @@ export class AcpProcessRuntime {
|
|
|
16
15
|
prompting = false;
|
|
17
16
|
maxStderrChunkChars;
|
|
18
17
|
promptTimeoutMs;
|
|
19
|
-
instructions;
|
|
20
|
-
firstPrompt = true;
|
|
21
18
|
promptCapabilities = {};
|
|
22
19
|
inputDirectory;
|
|
23
|
-
constructor(driver, sink, maxStderrChunkChars = 16 * 1024, promptTimeoutMs = 30 * 60 * 1000
|
|
20
|
+
constructor(driver, sink, maxStderrChunkChars = 16 * 1024, promptTimeoutMs = 30 * 60 * 1000) {
|
|
24
21
|
this.driver = driver;
|
|
25
22
|
this.sink = sink;
|
|
26
23
|
const limit = Number.isFinite(maxStderrChunkChars)
|
|
@@ -28,7 +25,6 @@ export class AcpProcessRuntime {
|
|
|
28
25
|
: 16 * 1024;
|
|
29
26
|
this.maxStderrChunkChars = Math.max(1, Math.min(16 * 1024, limit));
|
|
30
27
|
this.promptTimeoutMs = Math.max(10_000, Math.trunc(promptTimeoutMs));
|
|
31
|
-
this.instructions = buildAgentInstructions(customInstructions);
|
|
32
28
|
}
|
|
33
29
|
async start(workspace) {
|
|
34
30
|
if (this.process)
|
|
@@ -90,14 +86,10 @@ export class AcpProcessRuntime {
|
|
|
90
86
|
this.prompting = true;
|
|
91
87
|
this.sink.clearPending();
|
|
92
88
|
this.sink.setState('running');
|
|
93
|
-
const promptMessage = this.firstPrompt
|
|
94
|
-
? composeInitialAgentPrompt(this.instructions, message)
|
|
95
|
-
: message;
|
|
96
|
-
this.firstPrompt = false;
|
|
97
89
|
try {
|
|
98
90
|
const response = await withTimeout(this.connection.agent.request(acp.methods.agent.session.prompt, {
|
|
99
91
|
sessionId,
|
|
100
|
-
prompt: await this.promptBlocks(
|
|
92
|
+
prompt: await this.promptBlocks(message, attachments)
|
|
101
93
|
}), this.promptTimeoutMs, 'ACP prompt timed out');
|
|
102
94
|
if (this.sink.state === 'canceled' || this.sink.state === 'failed') {
|
|
103
95
|
return;
|
|
@@ -123,16 +115,47 @@ export class AcpProcessRuntime {
|
|
|
123
115
|
this.prompting = false;
|
|
124
116
|
}
|
|
125
117
|
}
|
|
126
|
-
async respondPending(
|
|
118
|
+
async respondPending(input, _attachments = []) {
|
|
119
|
+
const response = typeof input === 'string'
|
|
120
|
+
? {
|
|
121
|
+
requestId: this.pendingInput?.request.id ??
|
|
122
|
+
this.pending?.request.id ??
|
|
123
|
+
'',
|
|
124
|
+
message: input
|
|
125
|
+
}
|
|
126
|
+
: input;
|
|
127
127
|
if (this.pendingInput) {
|
|
128
|
-
this.
|
|
128
|
+
if (response.requestId !== this.pendingInput.request.id) {
|
|
129
|
+
throw new Error(`ACP pending request no longer matches: ${response.requestId}`);
|
|
130
|
+
}
|
|
131
|
+
this.finishInput(response.message ?? response.optionId ?? '', response.action);
|
|
129
132
|
return;
|
|
130
133
|
}
|
|
131
134
|
const pending = this.pending;
|
|
132
135
|
if (!pending)
|
|
133
136
|
throw new Error('ACP session is not waiting for input');
|
|
134
|
-
|
|
137
|
+
if (response.requestId !== pending.request.id) {
|
|
138
|
+
throw new Error(`ACP pending request no longer matches: ${response.requestId}`);
|
|
139
|
+
}
|
|
135
140
|
const options = pending.request.options || [];
|
|
141
|
+
if (response.action === 'cancel' || response.action === 'decline') {
|
|
142
|
+
this.finishPermission({ outcome: { outcome: 'cancelled' } });
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (response.action === 'accept') {
|
|
146
|
+
const option = options.find((item) => item.kind?.startsWith('allow')) ||
|
|
147
|
+
options.find((item) => !(item.kind?.startsWith('reject') ?? false));
|
|
148
|
+
if (!option) {
|
|
149
|
+
throw new Error('Permission request does not provide an allow option');
|
|
150
|
+
}
|
|
151
|
+
this.finishPermission({
|
|
152
|
+
outcome: { outcome: 'selected', optionId: option.id }
|
|
153
|
+
});
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const normalized = (response.optionId ?? response.message ?? '')
|
|
157
|
+
.trim()
|
|
158
|
+
.toLowerCase();
|
|
136
159
|
if (['cancel', 'deny', 'reject', '拒绝', '取消', '不同意'].includes(normalized)) {
|
|
137
160
|
this.finishPermission({ outcome: { outcome: 'cancelled' } });
|
|
138
161
|
return;
|
|
@@ -277,16 +300,17 @@ export class AcpProcessRuntime {
|
|
|
277
300
|
this.pendingInput = { request, params, resolve, timer };
|
|
278
301
|
});
|
|
279
302
|
}
|
|
280
|
-
finishInput(message) {
|
|
303
|
+
finishInput(message, requestedAction) {
|
|
281
304
|
const pending = this.pendingInput;
|
|
282
305
|
if (!pending)
|
|
283
306
|
return;
|
|
284
307
|
const normalized = message.trim().toLowerCase();
|
|
285
308
|
let response;
|
|
286
|
-
if (['cancel', '取消'].includes(normalized)) {
|
|
309
|
+
if (requestedAction === 'cancel' || ['cancel', '取消'].includes(normalized)) {
|
|
287
310
|
response = { action: 'cancel' };
|
|
288
311
|
}
|
|
289
|
-
else if (
|
|
312
|
+
else if (requestedAction === 'decline' ||
|
|
313
|
+
['decline', '拒绝', '不同意'].includes(normalized)) {
|
|
290
314
|
response = { action: 'decline' };
|
|
291
315
|
}
|
|
292
316
|
else if (!isFormElicitation(pending.params)) {
|
package/dist/config.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { mkdir, open, readFile } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { normalizeAgentInstructions } from './agent-instructions.js';
|
|
4
3
|
import { agentdDriverKinds } from './types.js';
|
|
5
4
|
export async function ensureAgentdConfig(filePath, options = {}) {
|
|
6
5
|
const absolute = path.resolve(filePath);
|
|
@@ -98,12 +97,6 @@ export async function loadAgentdConfig(filePath) {
|
|
|
98
97
|
workspaceRoots,
|
|
99
98
|
maxRequestBytes: integerValue(value.maxRequestBytes, 'maxRequestBytes', 1024 * 1024, 1024, 16 * 1024 * 1024),
|
|
100
99
|
maxAttachmentBytes: integerValue(value.maxAttachmentBytes, 'maxAttachmentBytes', 32 * 1024 * 1024, 1024, 64 * 1024 * 1024),
|
|
101
|
-
artifactStoragePath: resolveConfigPath(absolute, optionalString(value.artifactStoragePath, 'artifactStoragePath') || './artifacts'),
|
|
102
|
-
maxArtifactBytes: integerValue(value.maxArtifactBytes, 'maxArtifactBytes', 512 * 1024 * 1024, 1024, 16 * 1024 * 1024 * 1024),
|
|
103
|
-
maxArtifactStorageBytes: integerValue(value.maxArtifactStorageBytes, 'maxArtifactStorageBytes', 4 * 1024 * 1024 * 1024, 1024 * 1024, Number.MAX_SAFE_INTEGER),
|
|
104
|
-
maxPublishedArtifacts: integerValue(value.maxPublishedArtifacts, 'maxPublishedArtifacts', 4096, 1, 1_000_000),
|
|
105
|
-
maxConcurrentArtifactPublishes: integerValue(value.maxConcurrentArtifactPublishes, 'maxConcurrentArtifactPublishes', 4, 1, 128),
|
|
106
|
-
artifactTtlMs: integerValue(value.artifactTtlMs, 'artifactTtlMs', 24 * 60 * 60 * 1000, 60_000, 30 * 24 * 60 * 60 * 1000),
|
|
107
100
|
maxEventsPerSession: integerValue(value.maxEventsPerSession, 'maxEventsPerSession', 2048, 64, 100_000),
|
|
108
101
|
maxOutputChars: integerValue(value.maxOutputChars, 'maxOutputChars', 512 * 1024, 16 * 1024, 16 * 1024 * 1024),
|
|
109
102
|
sessionTtlMs: integerValue(value.sessionTtlMs, 'sessionTtlMs', 24 * 60 * 60 * 1000, 60_000, 30 * 24 * 60 * 60 * 1000),
|
|
@@ -149,7 +142,6 @@ function parseAgent(id, value) {
|
|
|
149
142
|
driver,
|
|
150
143
|
name: optionalString(input.name, `agents.${id}.name`),
|
|
151
144
|
description: optionalString(input.description, `agents.${id}.description`),
|
|
152
|
-
instructions: normalizeAgentInstructions(input.instructions, `agents.${id}.instructions`),
|
|
153
145
|
enabled: optionalBoolean(input.enabled, `agents.${id}.enabled`) ?? true,
|
|
154
146
|
workspace: optionalString(input.workspace, `agents.${id}.workspace`),
|
|
155
147
|
command: optionalString(input.command, `agents.${id}.command`),
|
|
@@ -205,7 +197,6 @@ function parseA2AAgent(id, input) {
|
|
|
205
197
|
protocol: 'a2a',
|
|
206
198
|
name: optionalString(input.name, `agents.${id}.name`),
|
|
207
199
|
description: optionalString(input.description, `agents.${id}.description`),
|
|
208
|
-
instructions: normalizeAgentInstructions(input.instructions, `agents.${id}.instructions`),
|
|
209
200
|
enabled: optionalBoolean(input.enabled, `agents.${id}.enabled`) ?? true,
|
|
210
201
|
agentCardUrl,
|
|
211
202
|
agentUrl,
|
|
@@ -338,9 +329,6 @@ function validateHttpUrl(value, name) {
|
|
|
338
329
|
throw new Error(`nexus-agentd ${name} must be an http(s) URL without credentials or a fragment`);
|
|
339
330
|
}
|
|
340
331
|
}
|
|
341
|
-
function resolveConfigPath(configPath, value) {
|
|
342
|
-
return path.resolve(path.dirname(configPath), value);
|
|
343
|
-
}
|
|
344
332
|
function secretsEqual(left, right) {
|
|
345
333
|
return left.length === right.length && left === right;
|
|
346
334
|
}
|
package/dist/control-plane.d.ts
CHANGED
package/dist/control-plane.js
CHANGED
|
@@ -2,7 +2,6 @@ import { randomBytes, randomUUID, timingSafeEqual } from 'node:crypto';
|
|
|
2
2
|
import { chmod, open, readFile, rename, rm } from 'node:fs/promises';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { hashAdminPassword, validateAdminPassword, verifyAdminPassword } from './auth.js';
|
|
5
|
-
import { normalizeAgentInstructions } from './agent-instructions.js';
|
|
6
5
|
import { loadAgentdConfig } from './config.js';
|
|
7
6
|
import { createDriverRegistry } from './drivers/index.js';
|
|
8
7
|
import { agentdDriverKinds } from './types.js';
|
|
@@ -312,7 +311,6 @@ export class AgentdControlPlane {
|
|
|
312
311
|
next.name = cleanString(update.name) || cleanString(previous.name);
|
|
313
312
|
}
|
|
314
313
|
}
|
|
315
|
-
setOptionalInstructions(next, update.instructions ?? cleanString(previous.instructions));
|
|
316
314
|
setOptionalString(next, 'description', update.description ?? cleanString(previous.description));
|
|
317
315
|
agents[id] = next;
|
|
318
316
|
raw.agents = agents;
|
|
@@ -450,7 +448,6 @@ export class AgentdControlPlane {
|
|
|
450
448
|
protocol: 'a2a',
|
|
451
449
|
name: config.name || id,
|
|
452
450
|
description: config.description,
|
|
453
|
-
instructions: config.instructions,
|
|
454
451
|
enabled: config.enabled !== false,
|
|
455
452
|
agentCardUrl: config.agentCardUrl || defaultAgentCardUrl(config.agentUrl),
|
|
456
453
|
agentUrl: config.agentUrl,
|
|
@@ -469,7 +466,6 @@ export class AgentdControlPlane {
|
|
|
469
466
|
driver: config.driver,
|
|
470
467
|
name: config.name || id,
|
|
471
468
|
description: config.description,
|
|
472
|
-
instructions: config.instructions,
|
|
473
469
|
enabled: config.enabled !== false,
|
|
474
470
|
workspace: config.workspace || this.config.workspaceRoots[0] || '',
|
|
475
471
|
permissionPolicy: config.permissionPolicy || 'ask',
|
|
@@ -611,19 +607,6 @@ function setOptionalString(target, key, value) {
|
|
|
611
607
|
else
|
|
612
608
|
delete target[key];
|
|
613
609
|
}
|
|
614
|
-
function setOptionalInstructions(target, value) {
|
|
615
|
-
let text;
|
|
616
|
-
try {
|
|
617
|
-
text = normalizeAgentInstructions(value);
|
|
618
|
-
}
|
|
619
|
-
catch (error) {
|
|
620
|
-
throw new ControlPlaneError(400, errorMessage(error));
|
|
621
|
-
}
|
|
622
|
-
if (text)
|
|
623
|
-
target.instructions = text;
|
|
624
|
-
else
|
|
625
|
-
delete target.instructions;
|
|
626
|
-
}
|
|
627
610
|
function validHeaderName(value) {
|
|
628
611
|
return /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(value);
|
|
629
612
|
}
|