@engineeros/connector 0.11.2 → 0.12.2

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,41 @@
1
- {
2
- "name": "@engineeros/connector",
3
- "version": "0.11.2",
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
- "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
- },
1
+ {
2
+ "name": "@engineeros/connector",
3
+ "version": "0.12.2",
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
+ "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
37
  "dependencies": {
38
- "@agentclientprotocol/sdk": "1.3.0"
38
+ "@agentclientprotocol/sdk": "1.3.0",
39
+ "zod": "3.25.76"
39
40
  }
40
41
  }
@@ -58,50 +58,50 @@ export function launchAcpAgent(
58
58
  };
59
59
  }
60
60
 
61
- export async function disposeAcpRuntimes() {
61
+ export async function disposeAcpRuntimes() {
62
62
  const active = [...runtimes.values()];
63
63
  runtimes.clear();
64
64
  await Promise.all(active.map((runtime) => runtime.dispose()));
65
- }
66
-
67
- export async function inspectAcpExecutionProfiles(workspace, config) {
68
- const runtime = new AcpRuntime(workspace, config, () => {});
69
- try {
70
- await runtime.ready;
71
- const response = await runtime.context.request(acp.methods.agent.session.new, {
72
- cwd: workspace,
73
- mcpServers: [],
74
- });
75
- return executionProfilesFromConfigOptions(response.configOptions);
76
- } finally {
77
- await runtime.dispose();
78
- }
79
- }
80
-
81
- export function executionProfilesFromConfigOptions(configOptions = []) {
82
- const selectOption = (category) =>
83
- configOptions.find((candidate) => candidate.category === category && candidate.type === "select");
84
- const selectValues = (option) =>
85
- (option?.options || []).flatMap((candidate) =>
86
- Array.isArray(candidate.options) ? candidate.options : [candidate],
87
- );
88
- const modelOption = selectOption("model");
89
- const effortOption = selectOption("thought_level");
90
- const models = selectValues(modelOption);
91
- const efforts = selectValues(effortOption);
92
- return {
93
- model_selection: models.length > 0,
94
- model_profiles: models.map((model) => ({
95
- id: model.value,
96
- name: model.name || model.value,
97
- description: model.description || "",
98
- is_default: model.value === modelOption.currentValue,
99
- default_reasoning_effort: effortOption?.currentValue || null,
100
- reasoning_efforts: efforts.map((effort) => effort.value),
101
- })),
102
- reasoning_efforts: efforts.map((effort) => effort.value),
103
- };
104
- }
65
+ }
66
+
67
+ export async function inspectAcpExecutionProfiles(workspace, config) {
68
+ const runtime = new AcpRuntime(workspace, config, () => {});
69
+ try {
70
+ await runtime.ready;
71
+ const response = await runtime.context.request(acp.methods.agent.session.new, {
72
+ cwd: workspace,
73
+ mcpServers: [],
74
+ });
75
+ return executionProfilesFromConfigOptions(response.configOptions);
76
+ } finally {
77
+ await runtime.dispose();
78
+ }
79
+ }
80
+
81
+ export function executionProfilesFromConfigOptions(configOptions = []) {
82
+ const selectOption = (category) =>
83
+ configOptions.find((candidate) => candidate.category === category && candidate.type === "select");
84
+ const selectValues = (option) =>
85
+ (option?.options || []).flatMap((candidate) =>
86
+ Array.isArray(candidate.options) ? candidate.options : [candidate],
87
+ );
88
+ const modelOption = selectOption("model");
89
+ const effortOption = selectOption("thought_level");
90
+ const models = selectValues(modelOption);
91
+ const efforts = selectValues(effortOption);
92
+ return {
93
+ model_selection: models.length > 0,
94
+ model_profiles: models.map((model) => ({
95
+ id: model.value,
96
+ name: model.name || model.value,
97
+ description: model.description || "",
98
+ is_default: model.value === modelOption.currentValue,
99
+ default_reasoning_effort: effortOption?.currentValue || null,
100
+ reasoning_efforts: efforts.map((effort) => effort.value),
101
+ })),
102
+ reasoning_efforts: efforts.map((effort) => effort.value),
103
+ };
104
+ }
105
105
 
106
106
  export function activeAcpRuntimeCount() {
107
107
  return runtimes.size;
@@ -230,6 +230,7 @@ class AcpRuntime {
230
230
  finalMessage: turn.finalMessage,
231
231
  model: profile.model || this.config.agent_name || "acp-agent",
232
232
  sessionId: session.sessionId,
233
+ usage: normalizeTokenUsage(response.usage),
233
234
  };
234
235
  } catch (error) {
235
236
  throw this.failure("failed", error);
@@ -410,6 +411,27 @@ class AcpRuntime {
410
411
  }
411
412
  }
412
413
 
414
+ export function normalizeTokenUsage(value) {
415
+ if (!value || typeof value !== "object") return null;
416
+ const number = (...keys) => {
417
+ for (const key of keys) {
418
+ const candidate = value[key];
419
+ if (Number.isFinite(candidate) && candidate >= 0) return Math.trunc(candidate);
420
+ }
421
+ return 0;
422
+ };
423
+ const usage = {
424
+ input_tokens: number("inputTokens", "input_tokens"),
425
+ output_tokens: number("outputTokens", "output_tokens"),
426
+ cache_read_tokens: number("cachedReadTokens", "cachedInputTokens", "cache_read_input_tokens"),
427
+ cache_write_tokens: number("cachedWriteTokens", "cache_creation_input_tokens"),
428
+ reasoning_tokens: number("reasoningTokens", "reasoningOutputTokens", "reasoning_tokens"),
429
+ };
430
+ usage.total_tokens = number("totalTokens", "total_tokens") ||
431
+ Object.values(usage).reduce((total, tokens) => total + tokens, 0);
432
+ return usage.total_tokens > 0 ? usage : null;
433
+ }
434
+
413
435
  function acpRuntimeKey(workspace, config) {
414
436
  return JSON.stringify([
415
437
  workspace,
@@ -137,25 +137,25 @@ 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 firstSection = {
145
- "# Workspace Assessment": "## Executive Summary",
146
- "# Workspace Assessment Delta": "## Updated Executive Summary",
147
- }[outputHeading];
148
- if (
149
- firstSection &&
150
- (content.startsWith(firstSection) ||
151
- hasRequiredAssessmentSections(content, outputHeading))
152
- ) {
153
- return `${outputHeading}\n\n${content}`;
154
- }
155
- const lines = content.split(/\r?\n/);
156
- const headingIndex = lines.findIndex(
157
- (line) => line.trim() === outputHeading,
158
- );
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 (
149
+ firstSection &&
150
+ (content.startsWith(firstSection) ||
151
+ hasRequiredAssessmentSections(content, outputHeading))
152
+ ) {
153
+ return `${outputHeading}\n\n${content}`;
154
+ }
155
+ const lines = content.split(/\r?\n/);
156
+ const headingIndex = lines.findIndex(
157
+ (line) => line.trim() === outputHeading,
158
+ );
159
159
  const preamble =
160
160
  headingIndex >= 0 ? lines.slice(0, headingIndex).join("\n") : content;
161
161
  if (headingIndex < 0) {
@@ -174,38 +174,38 @@ export function normalizeAgentStructuredOutput(
174
174
  `Agent response must return '${outputHeading}' as plain Markdown, without a code fence.`,
175
175
  );
176
176
  }
177
- return normalized;
178
- }
179
-
180
- function hasRequiredAssessmentSections(content, outputHeading) {
181
- const sections =
182
- outputHeading === "# Workspace Assessment"
183
- ? [
184
- "## Executive Summary",
185
- "## Assessment Delta",
186
- "## Current System Map",
187
- "## Observed Capabilities",
188
- "## Findings",
189
- "## Implementation Scorecard",
190
- "## Highest-Return Actions",
191
- "## Commands Observed",
192
- ]
193
- : [
194
- "## Updated Executive Summary",
195
- "## Assessment Delta",
196
- "## Current System Map",
197
- "## Capability Changes",
198
- "## Finding Changes",
199
- "## Scorecard Changes",
200
- "## Highest-Return Actions",
201
- "## Commands Observed",
202
- ];
203
- return sections.every((section) =>
204
- new RegExp(`(?:^|\\n)${section.replace(/[.*+?^${}()|[\\]\\]/g, "\\$&")}\\s*$`, "m").test(
205
- content,
206
- ),
207
- );
208
- }
177
+ return normalized;
178
+ }
179
+
180
+ function hasRequiredAssessmentSections(content, outputHeading) {
181
+ const sections =
182
+ outputHeading === "# Workspace Assessment"
183
+ ? [
184
+ "## Executive Summary",
185
+ "## Assessment Delta",
186
+ "## Current System Map",
187
+ "## Observed Capabilities",
188
+ "## Findings",
189
+ "## Implementation Scorecard",
190
+ "## Highest-Return Actions",
191
+ "## Commands Observed",
192
+ ]
193
+ : [
194
+ "## Updated Executive Summary",
195
+ "## Assessment Delta",
196
+ "## Current System Map",
197
+ "## Capability Changes",
198
+ "## Finding Changes",
199
+ "## Scorecard Changes",
200
+ "## Highest-Return Actions",
201
+ "## Commands Observed",
202
+ ];
203
+ return sections.every((section) =>
204
+ new RegExp(`(?:^|\\n)${section.replace(/[.*+?^${}()|[\\]\\]/g, "\\$&")}\\s*$`, "m").test(
205
+ content,
206
+ ),
207
+ );
208
+ }
209
209
 
210
210
  function bundledSkill(skillName) {
211
211
  const cached = skillCache.get(skillName);
@@ -1,12 +1,12 @@
1
1
  import path from "node:path";
2
2
  import { agentHarnessCapabilities } from "./agent-harness.mjs";
3
3
 
4
- export function advertisedCapabilities(config, codingAgent) {
5
- const executionProfiles = codingAgent.executionProfiles || {
6
- model_selection: false,
7
- model_profiles: [],
8
- reasoning_efforts: [],
9
- };
4
+ export function advertisedCapabilities(config, codingAgent) {
5
+ const executionProfiles = codingAgent.executionProfiles || {
6
+ model_selection: false,
7
+ model_profiles: [],
8
+ reasoning_efforts: [],
9
+ };
10
10
  return {
11
11
  agent_protocols: [codingAgent.protocol],
12
12
  coding_agent: true,
@@ -16,9 +16,9 @@ export function advertisedCapabilities(config, codingAgent) {
16
16
  agent_name: codingAgent.name,
17
17
  agent_version: codingAgent.version,
18
18
  ...agentHarnessCapabilities(),
19
- execution_profiles: {
20
- ...executionProfiles,
21
- models: (executionProfiles.model_profiles || []).map((model) => model.id),
19
+ execution_profiles: {
20
+ ...executionProfiles,
21
+ models: (executionProfiles.model_profiles || []).map((model) => model.id),
22
22
  },
23
23
  };
24
24
  }