@abgov/nx-adsp 12.7.0 → 12.8.0-beta.10
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/package.json +1 -1
- package/src/generators/angular-app/angular-app.js +24 -1
- package/src/generators/angular-app/angular-app.js.map +1 -1
- package/src/generators/angular-app/schema.d.ts +5 -0
- package/src/generators/angular-app/schema.json +10 -0
- package/src/generators/express-service/express-service.js +58 -10
- package/src/generators/express-service/express-service.js.map +1 -1
- package/src/generators/express-service/express-service.spec.ts +2 -0
- package/src/generators/express-service/schema.d.ts +6 -0
- package/src/generators/express-service/schema.json +11 -1
- package/src/generators/mean/mean.js +38 -5
- package/src/generators/mean/mean.js.map +1 -1
- package/src/generators/mean/mean.spec.ts +1 -0
- package/src/generators/mean/schema.d.ts +2 -0
- package/src/generators/mean/schema.json +10 -0
- package/src/generators/mern/mern.js +38 -5
- package/src/generators/mern/mern.js.map +1 -1
- package/src/generators/mern/mern.spec.ts +1 -0
- package/src/generators/mern/schema.d.ts +2 -0
- package/src/generators/mern/schema.json +10 -0
- package/src/generators/react-app/files/src/app/config.slice.ts__tmpl__ +2 -2
- package/src/generators/react-app/files/src/app/intake.slice.ts__tmpl__ +1 -1
- package/src/generators/react-app/files/src/app/start.slice.ts__tmpl__ +7 -7
- package/src/generators/react-app/files/src/app/user.slice.ts__tmpl__ +1 -1
- package/src/generators/react-app/react-app.js +24 -1
- package/src/generators/react-app/react-app.js.map +1 -1
- package/src/generators/react-app/schema.d.ts +4 -0
- package/src/generators/react-app/schema.json +10 -0
- package/src/utils/agent.d.ts +32 -2
- package/src/utils/agent.js +359 -93
- package/src/utils/agent.js.map +1 -1
- package/src/utils/agent.spec.ts +35 -3
- package/src/utils/plugin-version.d.ts +1 -0
- package/src/utils/plugin-version.js +7 -0
- package/src/utils/plugin-version.js.map +1 -0
package/src/utils/agent.js
CHANGED
|
@@ -1,139 +1,405 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.confirmAfterAgentInterrupt = confirmAfterAgentInterrupt;
|
|
3
4
|
exports.consultAgent = consultAgent;
|
|
4
5
|
const tslib_1 = require("tslib");
|
|
5
6
|
const readline_1 = require("readline");
|
|
6
7
|
const socket_io_client_1 = require("socket.io-client");
|
|
7
8
|
const nx_oc_1 = require("@abgov/nx-oc");
|
|
8
9
|
const AGENT_SERVICE_URN = 'urn:ads:platform:agent-service:v1';
|
|
9
|
-
const AGENT_ID = '
|
|
10
|
+
const AGENT_ID = 'nxAdspAgent';
|
|
11
|
+
/**
|
|
12
|
+
* After a consultAgent call, check whether the user interrupted before any
|
|
13
|
+
* files were generated and confirm they still want to proceed with the base
|
|
14
|
+
* scaffolding. Throws if the user declines, which aborts the Nx Tree commit.
|
|
15
|
+
*/
|
|
16
|
+
function confirmAfterAgentInterrupt(result) {
|
|
17
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
18
|
+
if ((result === null || result === void 0 ? void 0 : result.interrupted) && result.filesWritten === 0) {
|
|
19
|
+
const { prompt } = yield Promise.resolve().then(() => require('enquirer'));
|
|
20
|
+
const { proceed } = yield prompt({
|
|
21
|
+
type: 'confirm',
|
|
22
|
+
name: 'proceed',
|
|
23
|
+
message: 'Agent interaction ended without generating files. Continue with base scaffolding?',
|
|
24
|
+
initial: false,
|
|
25
|
+
});
|
|
26
|
+
if (!proceed) {
|
|
27
|
+
throw new Error('Generation aborted.');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
10
32
|
/**
|
|
11
33
|
* Connect to the ADSP agent-service and conduct a multi-turn conversation
|
|
12
34
|
* with the nx-adsp-agent. The agent uses its workspace tools to write
|
|
13
35
|
* generated and modified files; this function retrieves the workspace state
|
|
14
36
|
* after the conversation and applies all files to the Nx Tree.
|
|
15
37
|
*
|
|
38
|
+
* The socket connection and file upload start immediately. While the files
|
|
39
|
+
* are uploading, the developer is prompted for a brief project description.
|
|
40
|
+
* The initial message is sent as soon as both the upload and the description
|
|
41
|
+
* are ready — whichever finishes last.
|
|
42
|
+
*
|
|
16
43
|
* Returns null if agent-service is unavailable — callers should skip the
|
|
17
44
|
* agent step gracefully in that case.
|
|
18
45
|
*/
|
|
19
|
-
function consultAgent(directoryServiceUrl, accessToken, projectContext, host, projectRoot) {
|
|
46
|
+
function consultAgent(directoryServiceUrl, accessToken, projectContext, host, projectRoot, options) {
|
|
20
47
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
48
|
+
var _a, _b, _c;
|
|
49
|
+
if (!accessToken) {
|
|
50
|
+
process.stdout.write('\n[nx-adsp] No access token — skipping agent interaction.\n');
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
21
53
|
const agentServiceUrl = yield resolveAgentServiceUrl(directoryServiceUrl);
|
|
22
54
|
if (!agentServiceUrl) {
|
|
55
|
+
process.stdout.write('\n[nx-adsp] Agent-service not found in directory — skipping agent interaction.\n');
|
|
23
56
|
return null;
|
|
24
57
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
58
|
+
const isContinuation = (_a = options === null || options === void 0 ? void 0 : options.isContinuation) !== null && _a !== void 0 ? _a : false;
|
|
59
|
+
const threadId = (_b = options === null || options === void 0 ? void 0 : options.threadId) !== null && _b !== void 0 ? _b : crypto.randomUUID();
|
|
60
|
+
process.stdout.write(`\n[nx-adsp] Connecting to agent at ${agentServiceUrl}...\n`);
|
|
61
|
+
// Start socket connection immediately so file upload overlaps with the description prompt.
|
|
62
|
+
const socket = (0, socket_io_client_1.io)(agentServiceUrl, {
|
|
63
|
+
auth: { token: accessToken },
|
|
64
|
+
// Skip polling — go directly to WebSocket to avoid ARO ingress rejecting
|
|
65
|
+
// the polling POST with HTTP 400.
|
|
66
|
+
transports: ['websocket'],
|
|
67
|
+
timeout: 30000,
|
|
68
|
+
reconnection: false,
|
|
69
|
+
});
|
|
70
|
+
// rl is created after the enquirer prompt (below) so that readline gets a
|
|
71
|
+
// clean stdin — enquirer sets raw mode on stdin and restoring it before
|
|
72
|
+
// readline attaches avoids the two libraries leaving stdin in a bad state.
|
|
73
|
+
let rl = null;
|
|
74
|
+
// Coordination: sendInitialMessage is called once BOTH conditions are met:
|
|
75
|
+
// 1. description prompt has been answered (descriptionReady = true)
|
|
76
|
+
// 2. workspace files have been uploaded (workspaceReady = true)
|
|
77
|
+
// Whichever condition is satisfied last triggers the send.
|
|
78
|
+
let description;
|
|
79
|
+
let descriptionReady = false;
|
|
80
|
+
let workspaceReady = false;
|
|
81
|
+
let conversationStarted = false;
|
|
82
|
+
let buffer = '';
|
|
83
|
+
let conversationDone = false;
|
|
84
|
+
let agentHasResponded = false;
|
|
85
|
+
let thinkingInterval = null;
|
|
86
|
+
let interrupted = false;
|
|
87
|
+
let resolveConversation;
|
|
88
|
+
const conversationPromise = new Promise((r) => {
|
|
89
|
+
resolveConversation = r;
|
|
90
|
+
});
|
|
91
|
+
// ANSI helpers — no-op when stdout is not a TTY (e.g. CI, piped output).
|
|
92
|
+
const DIM = process.stdout.isTTY ? '\x1b[2m' : '';
|
|
93
|
+
const RESET = process.stdout.isTTY ? '\x1b[0m' : '';
|
|
94
|
+
const startThinking = () => {
|
|
95
|
+
// Dim colour is left open so the dots inherit it; stopThinking resets.
|
|
96
|
+
process.stdout.write(`${DIM}[nx-adsp] Agent is thinking`);
|
|
97
|
+
thinkingInterval = setInterval(() => {
|
|
98
|
+
if (!conversationDone)
|
|
99
|
+
process.stdout.write('.');
|
|
100
|
+
else
|
|
101
|
+
stopThinking();
|
|
102
|
+
}, 1000);
|
|
103
|
+
};
|
|
104
|
+
const stopThinking = () => {
|
|
105
|
+
if (thinkingInterval) {
|
|
106
|
+
clearInterval(thinkingInterval);
|
|
107
|
+
thinkingInterval = null;
|
|
108
|
+
process.stdout.write(`${RESET}\n`);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
const cleanup = (filesWritten) => {
|
|
112
|
+
conversationDone = true;
|
|
113
|
+
rl === null || rl === void 0 ? void 0 : rl.close();
|
|
114
|
+
socket.disconnect();
|
|
115
|
+
// Return null only for truly silent skips (no agent, no token, connection failed).
|
|
116
|
+
// Return a result whenever the user was actively engaged (agent responded)
|
|
117
|
+
// OR when they explicitly interrupted (Ctrl+C), so the caller always gets
|
|
118
|
+
// a chance to confirm before proceeding.
|
|
119
|
+
resolveConversation(agentHasResponded || interrupted
|
|
120
|
+
? { filesWritten, userInteracted: agentHasResponded, interrupted }
|
|
121
|
+
: null);
|
|
122
|
+
};
|
|
123
|
+
const buildInitialMessage = () => {
|
|
124
|
+
const { projectType, projectName, tenant, pluginVersion } = projectContext;
|
|
125
|
+
const descriptionLine = description ? `It is described as: "${description}". ` : '';
|
|
126
|
+
if (isContinuation) {
|
|
127
|
+
const stackDetail = projectType === 'react-app'
|
|
128
|
+
? 'It uses Redux Toolkit slices for state (store.ts, config.slice.ts, intake.slice.ts) and keycloak-js for authentication. '
|
|
129
|
+
: projectType === 'angular-app'
|
|
130
|
+
? 'It uses Angular standalone components, HttpClient with includeBearerTokenInterceptor for authenticated requests, and keycloak-angular for authentication. '
|
|
131
|
+
: '';
|
|
132
|
+
const fileNames = Object.keys(projectContext.existingFiles).join(', ');
|
|
133
|
+
return (`I've also scaffolded a ${projectType} called "${projectName}" for the same tenant. ` +
|
|
134
|
+
stackDetail +
|
|
135
|
+
`The files (${fileNames}) have been uploaded to your workspace — please read them to understand the current structure before suggesting capabilities. ` +
|
|
136
|
+
`Based on our service discussion, what ADSP frontend integrations would be most useful?`);
|
|
137
|
+
}
|
|
138
|
+
if (projectType === 'mern' || projectType === 'mean') {
|
|
139
|
+
const frontendStack = projectType === 'mern'
|
|
140
|
+
? 'React frontend using Redux Toolkit slices and keycloak-js'
|
|
141
|
+
: 'Angular frontend using standalone components and keycloak-angular';
|
|
142
|
+
const serviceFiles = Object.keys(projectContext.existingFiles)
|
|
143
|
+
.filter((f) => f.startsWith('service/'))
|
|
144
|
+
.join(', ');
|
|
145
|
+
const appFiles = Object.keys(projectContext.existingFiles)
|
|
146
|
+
.filter((f) => f.startsWith('app/'))
|
|
147
|
+
.join(', ');
|
|
148
|
+
return (`I am setting up a ${projectType.toUpperCase()} full-stack project called "${projectName}" ` +
|
|
149
|
+
`for ADSP tenant "${tenant}" (nx-adsp plugin version ${pluginVersion}). ` +
|
|
150
|
+
descriptionLine +
|
|
151
|
+
`It has an Express service ("${projectName}-service") and a ${frontendStack} ("${projectName}-app"). ` +
|
|
152
|
+
`Both have been uploaded to your workspace: service files (${serviceFiles}) and frontend files (${appFiles}). ` +
|
|
153
|
+
`When writing generated files, use the service/ prefix for backend files and the app/ prefix for frontend files. ` +
|
|
154
|
+
`What ADSP capabilities would be useful for this full-stack project?`);
|
|
155
|
+
}
|
|
156
|
+
const fileNames = Object.keys(projectContext.existingFiles).join(', ');
|
|
157
|
+
return (`I am setting up a new ${projectType} called "${projectName}" ` +
|
|
158
|
+
`for ADSP tenant "${tenant}" (nx-adsp plugin version ${pluginVersion}). ` +
|
|
159
|
+
descriptionLine +
|
|
160
|
+
`The project files (${fileNames}) have been uploaded to your workspace. ` +
|
|
161
|
+
`What ADSP capabilities would be useful to integrate into this service?`);
|
|
162
|
+
};
|
|
163
|
+
const sendInitialMessage = () => {
|
|
164
|
+
if (conversationStarted)
|
|
165
|
+
return;
|
|
166
|
+
conversationStarted = true;
|
|
167
|
+
process.stdout.write('[nx-adsp] Type your replies at the > prompt. Press Ctrl+D or leave blank to apply generated files.\n\n');
|
|
168
|
+
socket.emit('message', {
|
|
169
|
+
agent: AGENT_ID,
|
|
170
|
+
threadId,
|
|
171
|
+
content: buildInitialMessage(),
|
|
172
|
+
rawChunks: true,
|
|
30
173
|
});
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
174
|
+
startThinking();
|
|
175
|
+
setTimeout(() => {
|
|
176
|
+
stopThinking();
|
|
177
|
+
if (!conversationDone && !agentHasResponded) {
|
|
178
|
+
process.stdout.write('\n[nx-adsp] No response after 2 minutes. ' +
|
|
179
|
+
'The nxAdspAgent may still be deploying or the LLM is unresponsive. ' +
|
|
180
|
+
'Press Ctrl+C to skip and continue generation.\n');
|
|
181
|
+
}
|
|
182
|
+
}, 120000);
|
|
183
|
+
};
|
|
184
|
+
const requestWorkspaceState = () => {
|
|
185
|
+
socket.emit('workspace-read', { agent: AGENT_ID, threadId });
|
|
186
|
+
};
|
|
187
|
+
const promptUser = () => {
|
|
188
|
+
rl.question('\n> ', (input) => {
|
|
189
|
+
const trimmed = input.trim();
|
|
190
|
+
if (trimmed) {
|
|
191
|
+
socket.emit('message', {
|
|
192
|
+
agent: AGENT_ID,
|
|
193
|
+
threadId,
|
|
194
|
+
content: trimmed,
|
|
195
|
+
rawChunks: true,
|
|
196
|
+
});
|
|
197
|
+
startThinking();
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
// Empty input — apply whatever the agent has generated.
|
|
39
201
|
requestWorkspaceState();
|
|
40
202
|
}
|
|
41
203
|
});
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const promptUser = () => {
|
|
63
|
-
rl.question('\n> ', (input) => {
|
|
64
|
-
const trimmed = input.trim();
|
|
65
|
-
if (trimmed) {
|
|
66
|
-
sendMessage(trimmed);
|
|
204
|
+
};
|
|
205
|
+
const applyWorkspaceFiles = (files) => {
|
|
206
|
+
let count = 0;
|
|
207
|
+
for (const file of files) {
|
|
208
|
+
// Skip files we uploaded that the agent hasn't modified.
|
|
209
|
+
const original = projectContext.existingFiles[file.path];
|
|
210
|
+
if (original !== undefined && original === file.content) {
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
// Route to the correct project root. For composite generators (mern, mean)
|
|
214
|
+
// files are prefixed with 'service/' or 'app/' to distinguish the two parts.
|
|
215
|
+
let targetRoot = projectRoot;
|
|
216
|
+
let relativePath = file.path;
|
|
217
|
+
if (options === null || options === void 0 ? void 0 : options.additionalRoots) {
|
|
218
|
+
for (const [prefix, root] of Object.entries(options.additionalRoots)) {
|
|
219
|
+
if (file.path.startsWith(prefix + '/')) {
|
|
220
|
+
targetRoot = root;
|
|
221
|
+
relativePath = file.path.slice(prefix.length + 1);
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
67
224
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
225
|
+
// Also strip the default 'service/' prefix when it matches projectRoot
|
|
226
|
+
if (relativePath === file.path && file.path.startsWith('service/')) {
|
|
227
|
+
relativePath = file.path.slice('service/'.length);
|
|
71
228
|
}
|
|
72
|
-
});
|
|
73
|
-
};
|
|
74
|
-
const applyWorkspaceFiles = (files) => {
|
|
75
|
-
let count = 0;
|
|
76
|
-
for (const file of files) {
|
|
77
|
-
const fullPath = `${projectRoot}/${file.path}`;
|
|
78
|
-
host.write(fullPath, file.content);
|
|
79
|
-
count++;
|
|
80
229
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
230
|
+
host.write(`${targetRoot}/${relativePath}`, file.content);
|
|
231
|
+
count++;
|
|
232
|
+
}
|
|
233
|
+
return count;
|
|
234
|
+
};
|
|
235
|
+
// Register socket handlers upfront so they are active while the description
|
|
236
|
+
// prompt is being shown. readline handlers are registered after the prompt
|
|
237
|
+
// (see below) to avoid enquirer leaving stdin in a bad state.
|
|
238
|
+
socket.on('connect', () => {
|
|
239
|
+
// Connect and upload silently — stdout writes here would interleave with
|
|
240
|
+
// the description prompt that is active concurrently.
|
|
241
|
+
// The server signals handler readiness via socket.send() after its async
|
|
242
|
+
// getServiceConfiguration() resolves; wait for that before uploading.
|
|
243
|
+
socket.once('message', () => {
|
|
244
|
+
socket.emit('workspace-update', {
|
|
245
|
+
agent: AGENT_ID,
|
|
246
|
+
threadId,
|
|
247
|
+
writes: Object.entries(projectContext.existingFiles).map(([path, content]) => ({
|
|
248
|
+
path,
|
|
249
|
+
content,
|
|
250
|
+
})),
|
|
251
|
+
deletes: [],
|
|
252
|
+
});
|
|
91
253
|
});
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
254
|
+
});
|
|
255
|
+
socket.on('workspace-updated', () => {
|
|
256
|
+
workspaceReady = true;
|
|
257
|
+
if (descriptionReady) {
|
|
258
|
+
// Description was entered before upload finished — send now.
|
|
259
|
+
sendInitialMessage();
|
|
260
|
+
}
|
|
261
|
+
// else: description prompt is still open — post-prompt code will call sendInitialMessage.
|
|
262
|
+
});
|
|
263
|
+
const describeToolCall = (toolName, args) => {
|
|
264
|
+
switch (toolName) {
|
|
265
|
+
// Workspace tools (Mastra built-in names)
|
|
266
|
+
case 'mastra_workspace_write_file': return `Writing: ${args['path']}`;
|
|
267
|
+
case 'mastra_workspace_edit_file': return `Editing: ${args['path']}`;
|
|
268
|
+
case 'mastra_workspace_read_file': return `Reading: ${args['path']}`;
|
|
269
|
+
case 'mastra_workspace_list_files': return `Listing workspace files`;
|
|
270
|
+
// nx-adsp template tools (Mastra uses the agent registration key, not the tool id)
|
|
271
|
+
case 'listNxAdspTemplatesTool': return `Listing available ADSP templates`;
|
|
272
|
+
case 'getNxAdspTemplateTool': return `Getting template: ${args['templateId']}`;
|
|
273
|
+
default: return `Tool: ${toolName}`;
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
let firstDeltaOfTurn = true;
|
|
277
|
+
socket.on('stream', ({ chunk, done }) => {
|
|
278
|
+
var _a, _b, _c;
|
|
279
|
+
if ((chunk === null || chunk === void 0 ? void 0 : chunk.type) === 'tool-call') {
|
|
280
|
+
const p = chunk.payload;
|
|
281
|
+
if (p === null || p === void 0 ? void 0 : p.toolName) {
|
|
282
|
+
stopThinking();
|
|
283
|
+
process.stdout.write(`${DIM}[nx-adsp] ${describeToolCall(p.toolName, (_a = p.args) !== null && _a !== void 0 ? _a : {})}${RESET}\n`);
|
|
98
284
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
//
|
|
105
|
-
|
|
106
|
-
|
|
285
|
+
}
|
|
286
|
+
if ((chunk === null || chunk === void 0 ? void 0 : chunk.type) === 'text-delta') {
|
|
287
|
+
const text = (_c = (_b = chunk.payload) === null || _b === void 0 ? void 0 : _b.text) !== null && _c !== void 0 ? _c : '';
|
|
288
|
+
stopThinking();
|
|
289
|
+
if (firstDeltaOfTurn) {
|
|
290
|
+
// Blank line between the status/tool output and the agent's response text.
|
|
291
|
+
process.stdout.write('\n');
|
|
292
|
+
firstDeltaOfTurn = false;
|
|
107
293
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
294
|
+
buffer += text;
|
|
295
|
+
agentHasResponded = true;
|
|
296
|
+
process.stdout.write(text);
|
|
297
|
+
}
|
|
298
|
+
if (done) {
|
|
299
|
+
if (buffer.length > 0 && !buffer.endsWith('\n')) {
|
|
300
|
+
process.stdout.write('\n');
|
|
114
301
|
}
|
|
115
|
-
|
|
116
|
-
//
|
|
117
|
-
|
|
302
|
+
if (firstDeltaOfTurn) {
|
|
303
|
+
// Agent completed its turn (possibly doing tool work) without sending any
|
|
304
|
+
// text. Clear any active thinking indicator and show a dim hint so the
|
|
305
|
+
// user knows the turn ended and they can reply or press Enter to finish.
|
|
306
|
+
stopThinking();
|
|
307
|
+
process.stdout.write(`${DIM}[nx-adsp] Agent completed work without a response — reply or press Enter to apply files.${RESET}\n`);
|
|
118
308
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
309
|
+
buffer = '';
|
|
310
|
+
firstDeltaOfTurn = true; // reset for the next turn
|
|
311
|
+
promptUser();
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
socket.on('workspace-state', ({ files }) => {
|
|
315
|
+
// Only apply files the agent added or modified — not unchanged uploaded files.
|
|
316
|
+
const written = applyWorkspaceFiles(files !== null && files !== void 0 ? files : []);
|
|
317
|
+
if (written > 0) {
|
|
318
|
+
process.stdout.write(`\nApplied ${written} file(s) from agent workspace.\n`);
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
process.stdout.write('\nNo files generated by agent.\n');
|
|
322
|
+
}
|
|
323
|
+
cleanup(written);
|
|
324
|
+
});
|
|
325
|
+
socket.on('session-expired', () => {
|
|
326
|
+
process.stdout.write('\nAgent session expired.\n');
|
|
327
|
+
requestWorkspaceState();
|
|
328
|
+
});
|
|
329
|
+
socket.on('connect_error', (err) => {
|
|
330
|
+
var _a, _b, _c, _d;
|
|
331
|
+
const errAny = err;
|
|
332
|
+
process.stdout.write(`\n[nx-adsp] Connection failed: ${(_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : err}\n`);
|
|
333
|
+
process.stdout.write(`[nx-adsp] Error detail: ${JSON.stringify((_d = (_c = (_b = errAny === null || errAny === void 0 ? void 0 : errAny.description) !== null && _b !== void 0 ? _b : errAny === null || errAny === void 0 ? void 0 : errAny.context) !== null && _c !== void 0 ? _c : errAny === null || errAny === void 0 ? void 0 : errAny.cause) !== null && _d !== void 0 ? _d : 'none')}\n`);
|
|
334
|
+
cleanup(0);
|
|
335
|
+
});
|
|
336
|
+
socket.on('error', (err) => {
|
|
337
|
+
process.stdout.write(`\n[nx-adsp] Agent error: ${JSON.stringify(err)}\n`);
|
|
338
|
+
requestWorkspaceState();
|
|
339
|
+
});
|
|
340
|
+
// For continuation calls (shared thread from composite generator), skip the
|
|
341
|
+
// description prompt — the agent already has context from the prior service
|
|
342
|
+
// interaction on the same thread.
|
|
343
|
+
if (isContinuation) {
|
|
344
|
+
descriptionReady = true;
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
// Show description prompt while socket connects and uploads files in the background.
|
|
348
|
+
try {
|
|
349
|
+
const { prompt } = yield Promise.resolve().then(() => require('enquirer'));
|
|
350
|
+
const answer = yield prompt({
|
|
351
|
+
type: 'input',
|
|
352
|
+
name: 'description',
|
|
353
|
+
message: `Briefly describe what ${projectContext.projectName} does:`,
|
|
354
|
+
});
|
|
355
|
+
description = ((_c = answer.description) === null || _c === void 0 ? void 0 : _c.trim()) || undefined;
|
|
356
|
+
descriptionReady = true;
|
|
357
|
+
}
|
|
358
|
+
catch (_d) {
|
|
359
|
+
// Ctrl+C or cancellation during the description prompt.
|
|
360
|
+
interrupted = true;
|
|
361
|
+
cleanup(0);
|
|
362
|
+
return conversationPromise;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
// Create readline after enquirer has fully released stdin so the two
|
|
366
|
+
// libraries don't conflict over stdin state.
|
|
367
|
+
rl = (0, readline_1.createInterface)({ input: process.stdin, output: process.stdout });
|
|
368
|
+
rl.on('SIGINT', () => {
|
|
369
|
+
interrupted = true;
|
|
370
|
+
process.stdout.write('\n');
|
|
371
|
+
cleanup(0);
|
|
372
|
+
});
|
|
373
|
+
rl.on('close', () => {
|
|
374
|
+
if (!conversationDone && !interrupted) {
|
|
122
375
|
requestWorkspaceState();
|
|
123
|
-
}
|
|
124
|
-
socket.on('connect_error', () => cleanup(0));
|
|
125
|
-
socket.on('error', () => requestWorkspaceState());
|
|
376
|
+
}
|
|
126
377
|
});
|
|
378
|
+
if (workspaceReady) {
|
|
379
|
+
// Upload finished while the user was typing — send immediately.
|
|
380
|
+
sendInitialMessage();
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
// Rare: user typed faster than the upload completed. Show a brief status
|
|
384
|
+
// so the prompt doesn't appear to hang.
|
|
385
|
+
process.stdout.write('[nx-adsp] Uploading project files to workspace...\n');
|
|
386
|
+
// workspace-updated handler will call sendInitialMessage once upload completes.
|
|
387
|
+
}
|
|
388
|
+
return conversationPromise;
|
|
127
389
|
});
|
|
128
390
|
}
|
|
129
391
|
function resolveAgentServiceUrl(directoryServiceUrl) {
|
|
130
392
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
131
|
-
var _a;
|
|
132
393
|
try {
|
|
133
394
|
const urls = yield (0, nx_oc_1.getServiceUrls)(directoryServiceUrl);
|
|
134
|
-
|
|
395
|
+
const apiUrl = urls[AGENT_SERVICE_URN];
|
|
396
|
+
if (!apiUrl)
|
|
397
|
+
return null;
|
|
398
|
+
// The directory URL includes the REST API path (e.g. /agent/v1).
|
|
399
|
+
// Socket.io attaches at the server root, so use only the origin.
|
|
400
|
+
return new URL(apiUrl).origin;
|
|
135
401
|
}
|
|
136
|
-
catch (
|
|
402
|
+
catch (_a) {
|
|
137
403
|
return null;
|
|
138
404
|
}
|
|
139
405
|
});
|
package/src/utils/agent.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../../../../../packages/nx-adsp/src/utils/agent.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../../../../../packages/nx-adsp/src/utils/agent.ts"],"names":[],"mappings":"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"}
|
package/src/utils/agent.spec.ts
CHANGED
|
@@ -12,6 +12,12 @@ jest.mock('socket.io-client');
|
|
|
12
12
|
jest.mock('@abgov/nx-oc', () => ({ getServiceUrls: jest.fn() }));
|
|
13
13
|
jest.mock('@nx/devkit', () => ({ Tree: jest.fn() }));
|
|
14
14
|
|
|
15
|
+
// Enquirer is dynamically imported inside consultAgent; mock it here so the
|
|
16
|
+
// prompt resolves immediately with an empty description in all tests.
|
|
17
|
+
jest.mock('enquirer', () => ({
|
|
18
|
+
prompt: jest.fn().mockResolvedValue({ description: '' }),
|
|
19
|
+
}));
|
|
20
|
+
|
|
15
21
|
import { io } from 'socket.io-client';
|
|
16
22
|
import { getServiceUrls } from '@abgov/nx-oc';
|
|
17
23
|
|
|
@@ -37,6 +43,9 @@ function makeMockSocket() {
|
|
|
37
43
|
on: jest.fn((event: string, handler: (...args: unknown[]) => void) => {
|
|
38
44
|
handlers[event] = handler;
|
|
39
45
|
}),
|
|
46
|
+
once: jest.fn((event: string, handler: (...args: unknown[]) => void) => {
|
|
47
|
+
handlers[event] = handler;
|
|
48
|
+
}),
|
|
40
49
|
emit: jest.fn(),
|
|
41
50
|
disconnect: jest.fn(),
|
|
42
51
|
_handlers: handlers,
|
|
@@ -46,6 +55,9 @@ function makeMockSocket() {
|
|
|
46
55
|
return socket;
|
|
47
56
|
}
|
|
48
57
|
|
|
58
|
+
// Flush the microtask queue so async operations inside consultAgent settle.
|
|
59
|
+
// One tick is enough: the dynamic import and the enquirer mock both resolve
|
|
60
|
+
// as microtasks, so after this all handlers are registered and descriptionReady=true.
|
|
49
61
|
const flushPromises = () => new Promise(resolve => setTimeout(resolve, 0));
|
|
50
62
|
|
|
51
63
|
describe('consultAgent', () => {
|
|
@@ -66,6 +78,7 @@ describe('consultAgent', () => {
|
|
|
66
78
|
});
|
|
67
79
|
const socket = makeMockSocket();
|
|
68
80
|
const resultPromise = consultAgent('https://directory.example.com', 'token', PROJECT_CONTEXT, mockHost, 'apps/test-service');
|
|
81
|
+
// After one tick: enquirer mock resolves, descriptionReady=true, conversationPromise returned.
|
|
69
82
|
await flushPromises();
|
|
70
83
|
socket._handlers['connect_error']?.();
|
|
71
84
|
expect(await resultPromise).toBeNull();
|
|
@@ -79,7 +92,13 @@ describe('consultAgent', () => {
|
|
|
79
92
|
const resultPromise = consultAgent('https://directory.example.com', 'token', PROJECT_CONTEXT, mockHost, 'apps/test-service');
|
|
80
93
|
await flushPromises();
|
|
81
94
|
|
|
95
|
+
// descriptionReady=true at this point; workspace-updated fires last → triggers sendInitialMessage.
|
|
82
96
|
socket._handlers['connect']?.();
|
|
97
|
+
// Server signals readiness via 'message' before workspace-update is safe to send.
|
|
98
|
+
socket._handlers['message']?.('Connected as user...');
|
|
99
|
+
socket._handlers['workspace-updated']?.();
|
|
100
|
+
// Simulate agent responding with text (sets agentHasResponded = true)
|
|
101
|
+
socket._handlers['stream']?.({ chunk: { type: 'text-delta', payload: { text: 'I will add events.' } }, done: false });
|
|
83
102
|
socket._handlers['stream']?.({ chunk: null, done: true });
|
|
84
103
|
socket._handlers['workspace-state']?.({
|
|
85
104
|
files: [
|
|
@@ -89,12 +108,12 @@ describe('consultAgent', () => {
|
|
|
89
108
|
});
|
|
90
109
|
|
|
91
110
|
const result = await resultPromise;
|
|
92
|
-
expect(result).toEqual({ filesWritten: 2 });
|
|
111
|
+
expect(result).toEqual({ filesWritten: 2, userInteracted: true, interrupted: false });
|
|
93
112
|
expect(mockHost.write).toHaveBeenCalledWith('apps/test-service/src/roles.ts', 'export enum ServiceRoles {}');
|
|
94
113
|
expect(mockHost.write).toHaveBeenCalledWith('apps/test-service/src/main.ts', 'updated main.ts');
|
|
95
114
|
});
|
|
96
115
|
|
|
97
|
-
it('
|
|
116
|
+
it('uploads existing files to workspace before sending the initial message', async () => {
|
|
98
117
|
mockedGetServiceUrls.mockResolvedValue({
|
|
99
118
|
'urn:ads:platform:agent-service:v1': 'https://agent.example.com',
|
|
100
119
|
});
|
|
@@ -102,15 +121,28 @@ describe('consultAgent', () => {
|
|
|
102
121
|
const resultPromise = consultAgent('https://directory.example.com', 'token', PROJECT_CONTEXT, mockHost, 'apps/test-service');
|
|
103
122
|
await flushPromises();
|
|
104
123
|
|
|
124
|
+
// connect → server sends 'message' readiness signal → workspace-update emitted → workspace-updated → initial message sent
|
|
105
125
|
socket._handlers['connect']?.();
|
|
126
|
+
socket._handlers['message']?.('Connected as user...');
|
|
127
|
+
socket._handlers['workspace-updated']?.();
|
|
128
|
+
socket._handlers['stream']?.({ chunk: { type: 'text-delta', payload: { text: 'What does this service do?' } }, done: false });
|
|
106
129
|
socket._handlers['stream']?.({ chunk: null, done: true });
|
|
107
130
|
socket._handlers['workspace-state']?.({ files: [] });
|
|
108
131
|
await resultPromise;
|
|
109
132
|
|
|
133
|
+
expect(socket.emit).toHaveBeenCalledWith(
|
|
134
|
+
'workspace-update',
|
|
135
|
+
expect.objectContaining({
|
|
136
|
+
agent: 'nxAdspAgent',
|
|
137
|
+
writes: expect.arrayContaining([
|
|
138
|
+
expect.objectContaining({ path: 'src/main.ts' }),
|
|
139
|
+
]),
|
|
140
|
+
})
|
|
141
|
+
);
|
|
110
142
|
expect(socket.emit).toHaveBeenCalledWith(
|
|
111
143
|
'message',
|
|
112
144
|
expect.objectContaining({
|
|
113
|
-
agent: '
|
|
145
|
+
agent: 'nxAdspAgent',
|
|
114
146
|
content: expect.stringContaining('src/main.ts'),
|
|
115
147
|
})
|
|
116
148
|
);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const PLUGIN_VERSION = "12.x";
|