@engineeros/connector 0.10.4 → 0.11.1

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 CHANGED
@@ -1,40 +1,40 @@
1
- {
2
- "name": "@engineeros/connector",
3
- "version": "0.10.4",
4
- "description": "Connect a local coding agent to EngineerOS, using ACP when supported.",
5
- "private": false,
6
- "type": "module",
7
- "license": "UNLICENSED",
8
- "files": [
9
- "bin",
10
- "src",
11
- "README.md"
12
- ],
13
- "bin": {
14
- "engineeros-connector": "bin/engineeros-connector.mjs"
15
- },
16
- "scripts": {
17
- "start": "node ./bin/engineeros-connector.mjs",
18
- "prepublishOnly": "node ./scripts/release.mjs check",
19
- "release:patch": "node ./scripts/release.mjs patch",
20
- "test": "node --test --test-concurrency=1",
1
+ {
2
+ "name": "@engineeros/connector",
3
+ "version": "0.11.1",
4
+ "description": "Connect a local coding agent to EngineerOS, using ACP when supported.",
5
+ "private": false,
6
+ "type": "module",
7
+ "license": "UNLICENSED",
8
+ "files": [
9
+ "bin",
10
+ "src",
11
+ "README.md"
12
+ ],
13
+ "bin": {
14
+ "engineeros-connector": "bin/engineeros-connector.mjs"
15
+ },
16
+ "scripts": {
17
+ "start": "node ./bin/engineeros-connector.mjs",
18
+ "prepublishOnly": "node ./scripts/release.mjs check",
19
+ "release:patch": "node ./scripts/release.mjs patch",
20
+ "test": "node --test --test-concurrency=1",
21
21
  "type-check": "node --check ./bin/engineeros-connector.mjs && node --check ./scripts/release.mjs && node --check ./src/acp-client.mjs && node --check ./src/agent-harness.mjs && node --check ./src/agent-registry.mjs && node --check ./src/capabilities.mjs && node --check ./src/cli-args.mjs && node --check ./src/codex-app-server.mjs && node --check ./src/config.mjs && node --check ./src/connection.mjs && node --check ./src/mcp-server.mjs && node --check ./src/runner.mjs"
22
- },
23
- "engines": {
24
- "node": ">=22"
25
- },
26
- "repository": {
27
- "type": "git",
28
- "url": "git+https://github.com/vinpuli/engineerosv2.git",
29
- "directory": "packages/engineeros-connector"
30
- },
31
- "bugs": {
32
- "url": "https://github.com/vinpuli/engineerosv2/issues"
33
- },
34
- "publishConfig": {
35
- "access": "public"
36
- },
37
- "dependencies": {
38
- "@agentclientprotocol/sdk": "1.3.0"
39
- }
40
- }
22
+ },
23
+ "engines": {
24
+ "node": ">=22"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/vinpuli/engineerosv2.git",
29
+ "directory": "packages/engineeros-connector"
30
+ },
31
+ "bugs": {
32
+ "url": "https://github.com/vinpuli/engineerosv2/issues"
33
+ },
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
37
+ "dependencies": {
38
+ "@agentclientprotocol/sdk": "1.3.0"
39
+ }
40
+ }
@@ -137,14 +137,21 @@ export function normalizeAgentStructuredOutput(
137
137
  "EngineerOS structured output requires a heading contract.",
138
138
  );
139
139
  }
140
- const content = String(response || "").trim();
141
- if (!content) {
142
- throw new Error("Agent completed without returning a response.");
143
- }
144
- const lines = content.split(/\r?\n/);
145
- const headingIndex = lines.findIndex(
146
- (line, index) => index < 20 && line.trim() === outputHeading,
147
- );
140
+ const content = String(response || "").trim();
141
+ if (!content) {
142
+ throw new Error("Agent completed without returning a response.");
143
+ }
144
+ const firstSection = {
145
+ "# Workspace Assessment": "## Executive Summary",
146
+ "# Workspace Assessment Delta": "## Updated Executive Summary",
147
+ }[outputHeading];
148
+ if (firstSection && content.startsWith(firstSection)) {
149
+ return `${outputHeading}\n\n${content}`;
150
+ }
151
+ const lines = content.split(/\r?\n/);
152
+ const headingIndex = lines.findIndex(
153
+ (line) => line.trim() === outputHeading,
154
+ );
148
155
  const preamble =
149
156
  headingIndex >= 0 ? lines.slice(0, headingIndex).join("\n") : content;
150
157
  if (headingIndex < 0) {
@@ -1,4 +1,4 @@
1
- import { spawn } from "node:child_process";
1
+ import { spawn } from "node:child_process";
2
2
  import readline from "node:readline";
3
3
 
4
4
  export async function inspectCodexExecutionProfiles({
@@ -51,7 +51,7 @@ export async function inspectCodexExecutionProfiles({
51
51
  }, timeoutMs);
52
52
  try {
53
53
  await request("initialize", {
54
- clientInfo: { name: "engineeros-connector", title: "EngineerOS Connector", version: "0.10.4" },
54
+ clientInfo: { name: "engineeros-connector", title: "EngineerOS Connector", version: "0.11.0" },
55
55
  });
56
56
  child.stdin.write(`${JSON.stringify({ method: "initialized" })}\n`);
57
57
  const models = [];
@@ -79,140 +79,140 @@ export async function inspectCodexExecutionProfiles({
79
79
  child.kill();
80
80
  }
81
81
  }
82
-
83
- export function launchCodexAppServer({
84
- workspace,
85
- prompt,
86
- sandbox,
87
- profile = {},
88
- previousSessionId,
89
- callbacks = {},
90
- command = process.env.CODEX_BIN || (process.platform === "win32" ? "codex.cmd" : "codex"),
91
- spawnProcess = spawn,
92
- }) {
93
- const child = spawnProcess(command, ["app-server", "--stdio"], {
94
- cwd: workspace,
95
- env: process.env,
96
- shell: process.platform === "win32",
97
- windowsHide: true,
98
- stdio: ["pipe", "pipe", "pipe"],
99
- });
100
- const pending = new Map();
101
- let requestId = 0;
102
- let threadId = previousSessionId || "";
103
- let turnId = "";
104
- let finalMessage = "";
105
- let stderr = "";
106
- let settled = false;
107
-
108
- const request = (method, params) =>
109
- new Promise((resolve, reject) => {
110
- const id = ++requestId;
111
- pending.set(id, { resolve, reject });
112
- child.stdin.write(`${JSON.stringify({ id, method, params })}\n`);
113
- });
114
-
115
- const completed = new Promise((resolve, reject) => {
116
- const finish = (error) => {
117
- if (settled) return;
118
- settled = true;
119
- for (const waiter of pending.values()) waiter.reject(error || new Error("Codex app-server stopped."));
120
- pending.clear();
121
- if (error) reject(error);
122
- else resolve({ finalMessage, output: stderr.slice(-20_000), sessionId: threadId });
123
- };
124
-
125
- child.once("error", finish);
126
- child.once("close", (code) => {
127
- if (!settled) finish(new Error(`Codex app-server exited before completing the turn (code ${code ?? 1}). ${stderr}`));
128
- });
129
-
130
- const lines = readline.createInterface({ input: child.stdout });
131
- lines.on("line", (line) => {
132
- let message;
133
- try {
134
- message = JSON.parse(line);
135
- } catch {
136
- return;
137
- }
138
- if (message.id !== undefined) {
139
- const waiter = pending.get(message.id);
140
- if (!waiter) return;
141
- pending.delete(message.id);
142
- if (message.error) waiter.reject(new Error(message.error.message || "Codex app-server request failed."));
143
- else waiter.resolve(message.result);
144
- return;
145
- }
146
- const params = message.params || {};
147
- if (message.method === "item/agentMessage/delta" && typeof params.delta === "string") {
148
- finalMessage += params.delta;
149
- callbacks.onEvent?.({ type: "codex.agent_message_delta", delta: params.delta });
150
- } else if (message.method === "item/started" || message.method === "item/completed") {
151
- callbacks.onEvent?.({ type: `codex.${message.method}`, item: params.item });
152
- } else if (message.method === "thread/tokenUsage/updated") {
153
- callbacks.onEvent?.({ type: "codex.usage", update: params });
154
- } else if (message.method === "turn/completed" && params.threadId === threadId) {
155
- const status = params.turn?.status;
156
- if (status === "failed") finish(new Error(params.turn?.error?.message || "Codex turn failed."));
157
- else if (!finalMessage.trim()) finish(new Error("Codex completed without returning a response."));
158
- else finish();
159
- child.kill();
160
- }
161
- });
162
-
163
- child.stderr.setEncoding("utf8");
164
- child.stderr.on("data", (chunk) => {
165
- stderr = `${stderr}${chunk}`.slice(-20_000);
166
- });
167
-
168
- void (async () => {
169
- try {
170
- await request("initialize", {
171
- clientInfo: { name: "engineeros-connector", title: "EngineerOS Connector", version: "0.10.4" },
172
- });
173
- child.stdin.write(`${JSON.stringify({ method: "initialized" })}\n`);
174
- const threadResult = previousSessionId
175
- ? await request("thread/resume", {
176
- threadId: previousSessionId,
177
- cwd: workspace,
178
- sandbox,
179
- approvalPolicy: "never",
180
- model: profile.model || null,
181
- })
182
- : await request("thread/start", {
183
- cwd: workspace,
184
- sandbox,
185
- approvalPolicy: "never",
186
- model: profile.model || null,
187
- ephemeral: false,
188
- });
189
- threadId = threadResult.thread.id;
190
- callbacks.onEvent?.({ type: "thread.started", thread_id: threadId });
191
- const turnResult = await request("turn/start", {
192
- threadId,
193
- input: [{ type: "text", text: prompt }],
194
- effort: profile.reasoning_effort || null,
195
- });
196
- turnId = turnResult.turn.id;
197
- } catch (error) {
198
- child.kill();
199
- finish(error instanceof Error ? error : new Error(String(error)));
200
- }
201
- })();
202
- });
203
-
204
- return {
205
- child,
206
- completed,
207
- cancel: async () => {
208
- if (threadId && turnId && !settled) {
209
- try {
210
- await request("turn/interrupt", { threadId, turnId });
211
- } catch {
212
- // Process termination below is the final cancellation boundary.
213
- }
214
- }
215
- child.kill();
216
- },
217
- };
218
- }
82
+
83
+ export function launchCodexAppServer({
84
+ workspace,
85
+ prompt,
86
+ sandbox,
87
+ profile = {},
88
+ previousSessionId,
89
+ callbacks = {},
90
+ command = process.env.CODEX_BIN || (process.platform === "win32" ? "codex.cmd" : "codex"),
91
+ spawnProcess = spawn,
92
+ }) {
93
+ const child = spawnProcess(command, ["app-server", "--stdio"], {
94
+ cwd: workspace,
95
+ env: process.env,
96
+ shell: process.platform === "win32",
97
+ windowsHide: true,
98
+ stdio: ["pipe", "pipe", "pipe"],
99
+ });
100
+ const pending = new Map();
101
+ let requestId = 0;
102
+ let threadId = previousSessionId || "";
103
+ let turnId = "";
104
+ let finalMessage = "";
105
+ let stderr = "";
106
+ let settled = false;
107
+
108
+ const request = (method, params) =>
109
+ new Promise((resolve, reject) => {
110
+ const id = ++requestId;
111
+ pending.set(id, { resolve, reject });
112
+ child.stdin.write(`${JSON.stringify({ id, method, params })}\n`);
113
+ });
114
+
115
+ const completed = new Promise((resolve, reject) => {
116
+ const finish = (error) => {
117
+ if (settled) return;
118
+ settled = true;
119
+ for (const waiter of pending.values()) waiter.reject(error || new Error("Codex app-server stopped."));
120
+ pending.clear();
121
+ if (error) reject(error);
122
+ else resolve({ finalMessage, output: stderr.slice(-20_000), sessionId: threadId });
123
+ };
124
+
125
+ child.once("error", finish);
126
+ child.once("close", (code) => {
127
+ if (!settled) finish(new Error(`Codex app-server exited before completing the turn (code ${code ?? 1}). ${stderr}`));
128
+ });
129
+
130
+ const lines = readline.createInterface({ input: child.stdout });
131
+ lines.on("line", (line) => {
132
+ let message;
133
+ try {
134
+ message = JSON.parse(line);
135
+ } catch {
136
+ return;
137
+ }
138
+ if (message.id !== undefined) {
139
+ const waiter = pending.get(message.id);
140
+ if (!waiter) return;
141
+ pending.delete(message.id);
142
+ if (message.error) waiter.reject(new Error(message.error.message || "Codex app-server request failed."));
143
+ else waiter.resolve(message.result);
144
+ return;
145
+ }
146
+ const params = message.params || {};
147
+ if (message.method === "item/agentMessage/delta" && typeof params.delta === "string") {
148
+ finalMessage += params.delta;
149
+ callbacks.onEvent?.({ type: "codex.agent_message_delta", delta: params.delta });
150
+ } else if (message.method === "item/started" || message.method === "item/completed") {
151
+ callbacks.onEvent?.({ type: `codex.${message.method}`, item: params.item });
152
+ } else if (message.method === "thread/tokenUsage/updated") {
153
+ callbacks.onEvent?.({ type: "codex.usage", update: params });
154
+ } else if (message.method === "turn/completed" && params.threadId === threadId) {
155
+ const status = params.turn?.status;
156
+ if (status === "failed") finish(new Error(params.turn?.error?.message || "Codex turn failed."));
157
+ else if (!finalMessage.trim()) finish(new Error("Codex completed without returning a response."));
158
+ else finish();
159
+ child.kill();
160
+ }
161
+ });
162
+
163
+ child.stderr.setEncoding("utf8");
164
+ child.stderr.on("data", (chunk) => {
165
+ stderr = `${stderr}${chunk}`.slice(-20_000);
166
+ });
167
+
168
+ void (async () => {
169
+ try {
170
+ await request("initialize", {
171
+ clientInfo: { name: "engineeros-connector", title: "EngineerOS Connector", version: "0.11.0" },
172
+ });
173
+ child.stdin.write(`${JSON.stringify({ method: "initialized" })}\n`);
174
+ const threadResult = previousSessionId
175
+ ? await request("thread/resume", {
176
+ threadId: previousSessionId,
177
+ cwd: workspace,
178
+ sandbox,
179
+ approvalPolicy: "never",
180
+ model: profile.model || null,
181
+ })
182
+ : await request("thread/start", {
183
+ cwd: workspace,
184
+ sandbox,
185
+ approvalPolicy: "never",
186
+ model: profile.model || null,
187
+ ephemeral: false,
188
+ });
189
+ threadId = threadResult.thread.id;
190
+ callbacks.onEvent?.({ type: "thread.started", thread_id: threadId });
191
+ const turnResult = await request("turn/start", {
192
+ threadId,
193
+ input: [{ type: "text", text: prompt }],
194
+ effort: profile.reasoning_effort || null,
195
+ });
196
+ turnId = turnResult.turn.id;
197
+ } catch (error) {
198
+ child.kill();
199
+ finish(error instanceof Error ? error : new Error(String(error)));
200
+ }
201
+ })();
202
+ });
203
+
204
+ return {
205
+ child,
206
+ completed,
207
+ cancel: async () => {
208
+ if (threadId && turnId && !settled) {
209
+ try {
210
+ await request("turn/interrupt", { threadId, turnId });
211
+ } catch {
212
+ // Process termination below is the final cancellation boundary.
213
+ }
214
+ }
215
+ child.kill();
216
+ },
217
+ };
218
+ }