@engineeros/connector 0.8.7 → 0.8.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/README.md CHANGED
@@ -1,6 +1,18 @@
1
1
  # EngineerOS Connector
2
2
 
3
- The connector uses an authenticated EngineerOS WebSocket for pairing, workspace identity, run lifecycle, and evidence. Coding work can run through any ACP v1-compatible agent over stdio. Codex CLI remains available as the default built-in adapter.
3
+ The connector uses an authenticated EngineerOS WebSocket for pairing, workspace identity, run lifecycle, and evidence. Coding work can run through any agent in the official ACP registry or a custom ACP v1-compatible command over stdio. Direct Codex CLI remains available as the compatibility path.
4
+
5
+ ## Connect an official ACP agent
6
+
7
+ EngineerOS reads the curated [ACP agent registry](https://agentclientprotocol.com/registry), caches it for 24 hours, and uses the registry's pinned distribution for the current platform. The catalog includes Codex, Claude, Gemini, GitHub Copilot, Goose, OpenCode, Qwen Code, Cursor, and other compatible agents as they are published. List the current catalog, prepare the selected agent, then pair the workspace:
8
+
9
+ ```sh
10
+ npx --yes @engineeros/connector@latest agents
11
+ npx --yes @engineeros/connector@latest agent gemini install
12
+ npx --yes @engineeros/connector@latest pair PAIRING-CODE --url https://your-engineeros.example --workspace . --onboard --agent gemini
13
+ ```
14
+
15
+ Registry `npx` and `uvx` packages are prepared with their native package runner. Registry binaries are downloaded into `~/.engineeros/agents`, checked against the published SHA-256 digest when present, and launched from that managed location. The connector checks the selected distribution before pairing. Agent processes stay alive while the connector is active, project conversations reuse their ACP sessions, and response chunks reach Copilot as the agent produces them.
4
16
 
5
17
  ## Pair an ACP coding agent
6
18
 
@@ -10,7 +22,7 @@ Run the command from the repository the agent should work in:
10
22
  npx --yes @engineeros/connector@latest pair PAIRING-CODE --url http://localhost:8000 --workspace . --onboard --agent-name "My ACP agent" --agent-command my-agent --agent-args '["--acp"]'
11
23
  ```
12
24
 
13
- `--agent-command` must start an ACP v1 agent over NDJSON stdio. `--agent-args` is a JSON array so arguments are passed without invoking a shell. Omit both options to use the installed Codex CLI adapter.
25
+ `--agent-command` must start an ACP v1 agent over NDJSON stdio. `--agent-args` is a JSON array so arguments are passed without invoking a shell. Omit `--agent` and the custom command options to use the installed Codex CLI directly.
14
26
 
15
27
  Connect a local Codex CLI workspace to EngineerOS through an outbound WebSocket.
16
28
 
@@ -29,9 +41,9 @@ npx --yes @engineeros/connector@latest pair PAIRING-CODE --url https://your-engi
29
41
 
30
42
  The connector uploads a bounded ZIP snapshot for a safe file inventory, then stays online for deep assessments, rescans, and Goal Runs. Inventory never executes repository code. It excludes known secrets, dependency directories, build output, compiled binaries, files larger than 5 MB, agent-tool caches, Git metadata, and connector state before upload.
31
43
 
32
- From **Project steering -> Workspace**, choose **Assess with Codex** to run a read-only engineering assessment using the Codex CLI subscription already authenticated on that computer. EngineerOS stores the source-cited findings in System State and promotes the highest-return corrective action in Steering. Assessment cannot modify the workspace.
44
+ From **Project steering -> Workspace**, run the workspace assessment to use the connected agent subscription already authenticated on that computer. EngineerOS stores the source-cited findings in System State and promotes the highest-return corrective action in Steering. Assessment cannot modify the workspace.
33
45
 
34
- After onboarding, every project prompt is routed to this connection. Copilot, shaping, planning, architecture, and experience generation use the Codex CLI subscription and connected workspace context. Prompt runs are read-only; only an explicitly registered Goal Run receives workspace-write access. If the connector is offline, EngineerOS asks the user to reconnect instead of silently switching models.
46
+ After onboarding, every project prompt is routed to this connection. Copilot, shaping, planning, architecture, and experience generation use the connected agent subscription and workspace context. Interactive prompts run independently from assessments and Goal scheduling. Prompt runs are read-only; only an explicitly registered Goal Run receives workspace-write access. If the connector is offline, EngineerOS asks the user to reconnect instead of silently switching models.
35
47
 
36
48
  Project prompts use resumable, purpose-specific coding-agent sessions. The connector keeps the external session identifiers in its local configuration, so Copilot and artifact conversations survive connector restarts. Changing the purpose, model, or reasoning effort starts a separate session. Goal implementation and verification remain isolated runs.
37
49
 
@@ -40,18 +52,19 @@ An empty or document-only folder establishes a greenfield baseline. A code-beari
40
52
  ## Reconnect
41
53
 
42
54
  ```sh
43
- npx @engineeros/connector start --workspace .
55
+ npx --yes @engineeros/connector@latest start --workspace .
44
56
  ```
45
57
 
46
58
  Credentials are stored per workspace under `~/.engineeros/connectors` with owner-only permissions where supported.
59
+ If a new pairing command is accidentally run from the same folder against the same EngineerOS server, the connector reuses this saved identity instead of creating another baseline assessment.
47
60
 
48
61
  ## Run Goals
49
62
 
50
- Keep the connector online to receive Goals assigned from EngineerOS. Each Goal runs in an isolated worktree below `~/.engineeros/runs`. Cancellation stops Codex. The connector returns changed paths, a bounded diff, and the exact repository ZIP; a human still performs independent attestation.
63
+ Keep the connector online to receive Goals assigned from EngineerOS. Each Goal runs in an isolated worktree below `~/.engineeros/runs`. Cancellation stops the agent. The connector returns changed paths, a bounded diff, and the exact repository ZIP; a human still performs independent attestation.
51
64
 
52
65
  During the writable implementation phase, Codex receives an authenticated EngineerOS MCP server automatically. It can list, read, create, update, reclassify, soft-delete, and materialize project artifacts into canonical Product records through the same repository boundary used by Copilot. The backend accepts those calls only while the assigned Goal is running. Read-only project prompts and the independent verification phase do not receive mutation tools.
53
66
 
54
- Requirements: Node.js 22 or newer, Git, and an authenticated Codex CLI (`codex login`).
67
+ Requirements: Node.js 22 or newer, Git, and an authenticated agent. `npx` distributions use npm, `uvx` distributions require uv, and binary archives require `tar` (`unzip` for ZIP files on Linux). Registry agents report their own authentication prerequisites when they start.
55
68
 
56
69
  ## Execution profiles
57
70
 
@@ -3,7 +3,9 @@ import os from "node:os";
3
3
  import path from "node:path";
4
4
  import {
5
5
  assessmentResultUrl,
6
+ connectorResumeCredentials,
6
7
  loadConfig,
8
+ mergePairedConfig,
7
9
  resultUrl,
8
10
  saveConfig,
9
11
  socketUrl,
@@ -16,11 +18,17 @@ import {
16
18
  executeConnectedPrompt,
17
19
  executeWorkspaceAssessment,
18
20
  inspectCodingAgent,
19
- promptProgressMessage,
20
- promptQueueMessage,
21
+ promptStreamEvent,
21
22
  stopProcess,
22
23
  workspaceSnapshot,
23
24
  } from "../src/runner.mjs";
25
+ import { disposeAcpRuntimes } from "../src/acp-client.mjs";
26
+ import {
27
+ installRegisteredAgent,
28
+ inspectRegisteredAgent,
29
+ registeredAgentConfig,
30
+ registeredAgents,
31
+ } from "../src/agent-registry.mjs";
24
32
  import {
25
33
  describeWebSocketError,
26
34
  startConnectionWatchdog,
@@ -30,13 +38,60 @@ import { parseConnectorArgs } from "../src/cli-args.mjs";
30
38
  import { runMcpServer } from "../src/mcp-server.mjs";
31
39
  import packageJson from "../package.json" with { type: "json" };
32
40
 
33
- const { command, positional, flags } = parseConnectorArgs(process.argv.slice(2));
41
+ const { command, positional, flags } = parseConnectorArgs(
42
+ process.argv.slice(2),
43
+ );
44
+
45
+ if (command === "agents") {
46
+ let agents;
47
+ try {
48
+ agents = await registeredAgents();
49
+ } catch (error) {
50
+ fail(error instanceof Error ? error.message : String(error));
51
+ }
52
+ const statuses = await Promise.all(
53
+ agents.map(async (agent) => {
54
+ try {
55
+ await inspectRegisteredAgent(agent.id);
56
+ return `${agent.id}: ready (${agent.name} ${agent.version}, ${agent.distribution_type})`;
57
+ } catch (error) {
58
+ const detail = error instanceof Error ? error.message : String(error);
59
+ return `${agent.id}: setup needed (${agent.name} ${agent.version}, ${agent.distribution_type}) - ${detail}`;
60
+ }
61
+ }),
62
+ );
63
+ for (const status of statuses) {
64
+ console.log(status);
65
+ }
66
+ process.exit(0);
67
+ }
68
+
69
+ if (command === "agent") {
70
+ const agentId = positional[0];
71
+ const action = positional[1] || "check";
72
+ if (!agentId || !["check", "install"].includes(action)) {
73
+ fail("Usage: engineeros-connector agent AGENT_ID [check|install]");
74
+ }
75
+ try {
76
+ const installed =
77
+ action === "install"
78
+ ? await installRegisteredAgent(agentId)
79
+ : await inspectRegisteredAgent(agentId);
80
+ console.log(
81
+ `${installed.name} is ready (${installed.version}, ${installed.distribution}).`,
82
+ );
83
+ } catch (error) {
84
+ fail(error instanceof Error ? error.message : String(error));
85
+ }
86
+ process.exit(0);
87
+ }
34
88
 
35
89
  if (command === "mcp") {
36
90
  const config = await loadConfig(flags.workspace || process.cwd());
37
91
  if (!config) fail("This workspace is not paired with EngineerOS.");
38
92
  const runId = flags["run-id"] || positional[0];
39
- if (!runId) fail("Usage: engineeros-connector mcp --run-id RUN_ID [--workspace PATH]");
93
+ if (!runId)
94
+ fail("Usage: engineeros-connector mcp --run-id RUN_ID [--workspace PATH]");
40
95
  await runMcpServer({
41
96
  config,
42
97
  runId,
@@ -59,14 +114,27 @@ if (command === "status") {
59
114
 
60
115
  let config;
61
116
  let firstMessage;
117
+ let existingConfig;
62
118
  if (command === "pair") {
63
119
  const pairingCode = positional[0];
64
120
  if (!pairingCode)
65
121
  fail(
66
- "Usage: engineeros-connector pair CODE --url URL [--name NAME] [--workspace PATH] [--skip-git-repo-check]",
122
+ "Usage: engineeros-connector pair CODE --url URL [--agent AGENT_ID] [--name NAME] [--workspace PATH] [--skip-git-repo-check]",
67
123
  );
68
124
  const url = flags.url;
69
125
  if (!url) fail("Pairing requires --url with the EngineerOS backend address.");
126
+ if (flags.agent && flags["agent-command"]) {
127
+ fail("Use either --agent or --agent-command, not both.");
128
+ }
129
+ let agentConfig = {};
130
+ try {
131
+ if (flags.agent) {
132
+ await inspectRegisteredAgent(flags.agent);
133
+ agentConfig = await registeredAgentConfig(flags.agent);
134
+ }
135
+ } catch (error) {
136
+ fail(error instanceof Error ? error.message : String(error));
137
+ }
70
138
  config = {
71
139
  server_url: socketUrl(url),
72
140
  workspace: path.resolve(flags.workspace || process.cwd()),
@@ -76,16 +144,19 @@ if (command === "pair") {
76
144
  agent_command: flags["agent-command"] || null,
77
145
  agent_args: parseAgentArgs(flags["agent-args"]),
78
146
  agent_name: flags["agent-name"] || null,
147
+ ...agentConfig,
79
148
  skip_git_repo_check: flags["skip-git-repo-check"] === true,
80
149
  name:
81
150
  flags.name ||
82
151
  `${os.hostname()} - ${path.basename(path.resolve(flags.workspace || process.cwd()))}`,
83
152
  };
153
+ existingConfig = await loadConfig(config.workspace);
84
154
  firstMessage = {
85
155
  type: "pair",
86
156
  pairing_code: pairingCode,
87
157
  name: config.name,
88
158
  capabilities: {},
159
+ ...connectorResumeCredentials(existingConfig, config.server_url),
89
160
  };
90
161
  } else if (command === "start") {
91
162
  config = await loadConfig(flags.workspace || process.cwd());
@@ -99,7 +170,9 @@ if (command === "pair") {
99
170
  token: config.token,
100
171
  };
101
172
  } else {
102
- fail("Use `engineeros-connector pair`, `start`, `status`, or `mcp`.");
173
+ fail(
174
+ "Use `engineeros-connector pair`, `start`, `status`, `agents`, `agent`, or `mcp`.",
175
+ );
103
176
  }
104
177
 
105
178
  let codingAgent;
@@ -108,7 +181,9 @@ try {
108
181
  } catch (error) {
109
182
  fail(error instanceof Error ? error.message : String(error));
110
183
  }
111
- console.log(`Using ${codingAgent.name} through ${codingAgent.protocol} (${codingAgent.version}).`);
184
+ console.log(
185
+ `Using ${codingAgent.name} through ${codingAgent.protocol} (${codingAgent.version}).`,
186
+ );
112
187
  const capabilities = advertisedCapabilities(config, codingAgent);
113
188
  firstMessage.capabilities = capabilities;
114
189
 
@@ -116,7 +191,7 @@ let stopped = false;
116
191
  let active = null;
117
192
  const available = [];
118
193
  const assessments = [];
119
- const prompts = [];
194
+ const activePrompts = new Map();
120
195
  let socket;
121
196
  let pingTimer;
122
197
  let reconnectTimer;
@@ -132,6 +207,8 @@ process.on("SIGINT", async () => {
132
207
  clearTimeout(reconnectTimer);
133
208
  clearInterval(pingTimer);
134
209
  await stopProcess(active?.child);
210
+ await Promise.all([...activePrompts.values()].map(cancelPrompt));
211
+ await disposeAcpRuntimes();
135
212
  socket?.close();
136
213
  process.exit(0);
137
214
  });
@@ -157,11 +234,14 @@ async function connect() {
157
234
  const message = JSON.parse(String(event.data));
158
235
  if (message.type === "paired") {
159
236
  clearConnectionWatchdog?.();
160
- config = {
161
- ...config,
162
- connector_id: message.connector.id,
163
- token: message.token,
164
- };
237
+ const reusedConnector =
238
+ existingConfig?.connector_id === message.connector.id;
239
+ config = mergePairedConfig(
240
+ config,
241
+ existingConfig,
242
+ message.connector.id,
243
+ message.token,
244
+ );
165
245
  await saveConfig(config);
166
246
  firstMessage = {
167
247
  type: "authenticate",
@@ -169,7 +249,11 @@ async function connect() {
169
249
  token: config.token,
170
250
  capabilities,
171
251
  };
172
- console.log(`Paired. Connector ${config.connector_id} is online.`);
252
+ console.log(
253
+ reusedConnector
254
+ ? `Reconnected existing workspace connector ${config.connector_id}; assessment history is preserved.`
255
+ : `Paired. Connector ${config.connector_id} is online.`,
256
+ );
173
257
  reconnectDelay = 1_000;
174
258
  startPings();
175
259
  if (config.onboarding_pending) void submitWorkspaceSnapshot();
@@ -200,29 +284,12 @@ async function connect() {
200
284
  return;
201
285
  }
202
286
  if (message.type === "prompt.execute") {
203
- let added = false;
204
- if (
205
- active?.runId !== message.prompt_id &&
206
- !prompts.some((candidate) => candidate.prompt_id === message.prompt_id)
207
- ) {
208
- prompts.push(message);
209
- added = true;
210
- }
211
- if (added && active) {
212
- sendPromptProgress(message.prompt_id, promptQueueMessage(active.kind));
213
- }
214
- pump();
287
+ if (!activePrompts.has(message.prompt_id)) void executePrompt(message);
215
288
  return;
216
289
  }
217
290
  if (message.type === "prompt.cancel") {
218
- const queued = prompts.findIndex(
219
- (candidate) => candidate.prompt_id === message.prompt_id,
220
- );
221
- if (queued >= 0) prompts.splice(queued, 1);
222
- if (active?.kind === "prompt" && active.runId === message.prompt_id) {
223
- active.cancelled = true;
224
- await stopProcess(active.child);
225
- }
291
+ const promptState = activePrompts.get(message.prompt_id);
292
+ if (promptState) await cancelPrompt(promptState);
226
293
  return;
227
294
  }
228
295
  if (message.type === "run.available") {
@@ -259,11 +326,12 @@ async function connect() {
259
326
  console.error(`EngineerOS: ${message.message}`);
260
327
  }
261
328
  });
262
- socket.addEventListener("close", () => {
329
+ socket.addEventListener("close", (event) => {
263
330
  clearConnectionWatchdog?.();
264
331
  clearInterval(pingTimer);
265
- if (active?.kind === "prompt") {
266
- active.cancelled = true;
332
+ for (const promptState of activePrompts.values())
333
+ void cancelPrompt(promptState);
334
+ if (event.code === 4001 && active?.kind === "assessment") {
267
335
  void stopProcess(active.child);
268
336
  }
269
337
  if (connectionRejected) {
@@ -286,7 +354,9 @@ async function connect() {
286
354
 
287
355
  function scheduleReconnect() {
288
356
  if (stopped || connectionRejected || reconnectTimer) return;
289
- const detail = lastConnectionError ? ` Last error: ${lastConnectionError}` : "";
357
+ const detail = lastConnectionError
358
+ ? ` Last error: ${lastConnectionError}`
359
+ : "";
290
360
  console.error(
291
361
  `Connection unavailable.${detail} Retrying in ${Math.round(reconnectDelay / 1_000)}s.`,
292
362
  );
@@ -351,30 +421,21 @@ function startPings() {
351
421
  }, 10_000);
352
422
  }
353
423
 
354
- function sendPromptProgress(promptId, message) {
355
- if (!message || socket.readyState !== WebSocket.OPEN) return;
356
- socket.send(
357
- JSON.stringify({
358
- type: "prompt.progress",
359
- prompt_id: promptId,
360
- message: String(message).slice(0, 500),
361
- }),
362
- );
424
+ function sendPromptEvent(promptId, event) {
425
+ if (!event || socket.readyState !== WebSocket.OPEN) return;
426
+ const payload = {
427
+ type: "prompt.event",
428
+ prompt_id: promptId,
429
+ kind: event.kind,
430
+ };
431
+ if (event.message) payload.message = String(event.message).slice(0, 500);
432
+ if (event.delta) payload.delta = String(event.delta).slice(0, 50_000);
433
+ if (event.status) payload.status = String(event.status).slice(0, 100);
434
+ socket.send(JSON.stringify(payload));
363
435
  }
364
436
 
365
437
  function pump() {
366
438
  if (active || socket.readyState !== WebSocket.OPEN) return;
367
- const prompt = prompts.shift();
368
- if (prompt) {
369
- active = {
370
- kind: "prompt",
371
- runId: prompt.prompt_id,
372
- child: null,
373
- cancelled: false,
374
- };
375
- void executePrompt(prompt);
376
- return;
377
- }
378
439
  const assessment = assessments.shift();
379
440
  if (assessment) {
380
441
  active = {
@@ -403,36 +464,56 @@ function pump() {
403
464
 
404
465
  async function executePrompt(assignment) {
405
466
  const promptId = assignment.prompt_id;
406
- let lastProgressMessage;
407
- const reportProgress = (message) => {
467
+ const promptState = {
468
+ runId: promptId,
469
+ child: null,
470
+ controller: null,
471
+ cancelled: false,
472
+ };
473
+ activePrompts.set(promptId, promptState);
474
+ let lastEvent;
475
+ const reportEvent = (event) => {
476
+ const serialized = event ? JSON.stringify(event) : null;
408
477
  if (
409
- !message ||
410
- message === lastProgressMessage ||
478
+ !serialized ||
479
+ serialized === lastEvent ||
411
480
  socket.readyState !== WebSocket.OPEN ||
412
- active?.runId !== promptId
481
+ activePrompts.get(promptId) !== promptState
413
482
  ) {
414
483
  return;
415
484
  }
416
- lastProgressMessage = message;
417
- sendPromptProgress(promptId, message);
485
+ lastEvent = serialized;
486
+ sendPromptEvent(promptId, event);
418
487
  };
419
- sendPromptProgress(promptId, "Coding agent started this request");
488
+ sendPromptEvent(promptId, {
489
+ kind: "status",
490
+ message: "Agent started this request",
491
+ });
420
492
  console.log(
421
493
  `Answering ${assignment.purpose || "project"} prompt with ${codingAgent.name}.`,
422
494
  );
423
495
  try {
424
496
  const result = await executeConnectedPrompt(assignment, config, {
425
497
  onProcess: (child) => {
426
- if (active?.runId === promptId) active.child = child;
498
+ if (activePrompts.get(promptId) === promptState)
499
+ promptState.child = child;
427
500
  },
428
- onEvent: (event) => reportProgress(promptProgressMessage(event)),
501
+ onController: (controller) => {
502
+ if (activePrompts.get(promptId) === promptState) {
503
+ promptState.controller = controller;
504
+ }
505
+ },
506
+ onEvent: (event) => reportEvent(promptStreamEvent(event)),
429
507
  });
430
508
  config = {
431
509
  ...config,
432
- sessions: { ...(config.sessions || {}), [result.sessionKey]: result.sessionId },
510
+ sessions: {
511
+ ...(config.sessions || {}),
512
+ [result.sessionKey]: result.sessionId,
513
+ },
433
514
  };
434
515
  await saveConfig(config);
435
- if (active?.cancelled || socket.readyState !== WebSocket.OPEN) return;
516
+ if (promptState.cancelled || socket.readyState !== WebSocket.OPEN) return;
436
517
  socket.send(
437
518
  JSON.stringify({
438
519
  type: "prompt.completed",
@@ -443,7 +524,7 @@ async function executePrompt(assignment) {
443
524
  }),
444
525
  );
445
526
  } catch (error) {
446
- if (!active?.cancelled && socket.readyState === WebSocket.OPEN) {
527
+ if (!promptState.cancelled && socket.readyState === WebSocket.OPEN) {
447
528
  socket.send(
448
529
  JSON.stringify({
449
530
  type: "prompt.failed",
@@ -452,18 +533,29 @@ async function executePrompt(assignment) {
452
533
  }),
453
534
  );
454
535
  }
455
- if (!active?.cancelled) {
536
+ if (!promptState.cancelled) {
456
537
  console.error(error instanceof Error ? error.message : String(error));
457
538
  }
458
539
  } finally {
459
- active = null;
460
- pump();
540
+ if (activePrompts.get(promptId) === promptState)
541
+ activePrompts.delete(promptId);
461
542
  }
462
543
  }
463
544
 
545
+ async function cancelPrompt(promptState) {
546
+ promptState.cancelled = true;
547
+ if (typeof promptState.controller?.cancel === "function") {
548
+ await promptState.controller.cancel();
549
+ return;
550
+ }
551
+ await stopProcess(promptState.child);
552
+ }
553
+
464
554
  async function executeAssessment(assignment) {
465
555
  const assessmentId = assignment.assessment_id;
466
- console.log(`Assessing workspace with ${codingAgent.name} (${assessmentId}).`);
556
+ console.log(
557
+ `Assessing workspace with ${codingAgent.name} (${assessmentId}).`,
558
+ );
467
559
  let progress = 10;
468
560
  const reportedMilestones = new Set();
469
561
  const reportProgress = (message, { milestone = true } = {}) => {
@@ -515,7 +607,9 @@ async function executeAssessment(assignment) {
515
607
  `EngineerOS rejected the assessment (${response.status}): ${await response.text()}`,
516
608
  );
517
609
  }
518
- console.log(`${codingAgent.name} workspace assessment is current in EngineerOS.`);
610
+ console.log(
611
+ `${codingAgent.name} workspace assessment is current in EngineerOS.`,
612
+ );
519
613
  } catch (error) {
520
614
  if (socket.readyState === WebSocket.OPEN) {
521
615
  socket.send(
@@ -546,7 +640,7 @@ async function execute(assignment) {
546
640
  type: "run.progress",
547
641
  run_id: runId,
548
642
  progress_percent: progress,
549
- message: `${codingAgent.name} is working`,
643
+ message: "Agent is working",
550
644
  }),
551
645
  );
552
646
  }
@@ -558,7 +652,7 @@ async function execute(assignment) {
558
652
  },
559
653
  onEvent: (event) => {
560
654
  const message =
561
- event.message || event.item?.text || event.type || `${codingAgent.name} is working`;
655
+ event.message || event.item?.text || event.type || "Agent is working";
562
656
  if (socket.readyState === WebSocket.OPEN) {
563
657
  socket.send(
564
658
  JSON.stringify({
@@ -593,10 +687,16 @@ async function execute(assignment) {
593
687
  result.head_revision,
594
688
  );
595
689
  if (integration.applied) {
596
- console.log(`Run accepted and applied to the connected repository at ${integration.revision}.`);
690
+ console.log(
691
+ `Run accepted and applied to the connected repository at ${integration.revision}.`,
692
+ );
597
693
  } else {
598
- console.warn(`Run accepted but not applied locally. ${integration.reason}`);
599
- console.warn(`The verified run workspace remains at ${result.run_workspace}.`);
694
+ console.warn(
695
+ `Run accepted but not applied locally. ${integration.reason}`,
696
+ );
697
+ console.warn(
698
+ `The verified run workspace remains at ${result.run_workspace}.`,
699
+ );
600
700
  }
601
701
  } catch (error) {
602
702
  if (!active?.cancelled && socket.readyState === WebSocket.OPEN) {
@@ -621,12 +721,17 @@ function parseAgentArgs(value) {
621
721
  if (!value) return [];
622
722
  try {
623
723
  const parsed = JSON.parse(value);
624
- if (!Array.isArray(parsed) || parsed.some((item) => typeof item !== "string")) {
724
+ if (
725
+ !Array.isArray(parsed) ||
726
+ parsed.some((item) => typeof item !== "string")
727
+ ) {
625
728
  throw new Error();
626
729
  }
627
730
  return parsed;
628
731
  } catch {
629
- fail('--agent-args must be a JSON array, for example: --agent-args "[\\"acp\\"]"');
732
+ fail(
733
+ '--agent-args must be a JSON array, for example: --agent-args "[\\"acp\\"]"',
734
+ );
630
735
  }
631
736
  }
632
737
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@engineeros/connector",
3
- "version": "0.8.7",
3
+ "version": "0.8.9",
4
4
  "description": "Connect a local coding agent to EngineerOS, using ACP when supported.",
5
5
  "private": false,
6
6
  "type": "module",
@@ -16,7 +16,7 @@
16
16
  "scripts": {
17
17
  "start": "node ./bin/engineeros-connector.mjs",
18
18
  "test": "node --test --test-concurrency=1",
19
- "type-check": "node --check ./bin/engineeros-connector.mjs && node --check ./src/capabilities.mjs && node --check ./src/cli-args.mjs && node --check ./src/connection.mjs && node --check ./src/mcp-server.mjs && node --check ./src/runner.mjs"
19
+ "type-check": "node --check ./bin/engineeros-connector.mjs && node --check ./src/acp-client.mjs && node --check ./src/agent-registry.mjs && node --check ./src/capabilities.mjs && node --check ./src/cli-args.mjs && node --check ./src/config.mjs && node --check ./src/connection.mjs && node --check ./src/mcp-server.mjs && node --check ./src/runner.mjs"
20
20
  },
21
21
  "engines": {
22
22
  "node": ">=22"