@engineeros/connector 0.12.3 → 0.12.5

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.
@@ -31,7 +31,9 @@ import {
31
31
  registeredAgents,
32
32
  } from "../src/agent-registry.mjs";
33
33
  import {
34
+ describeRejectedResponse,
34
35
  describeWebSocketError,
36
+ protocolFailureMessage,
35
37
  startConnectionWatchdog,
36
38
  } from "../src/connection.mjs";
37
39
  import { advertisedCapabilities } from "../src/capabilities.mjs";
@@ -387,7 +389,7 @@ async function submitWorkspaceSnapshot() {
387
389
  );
388
390
  if (!response.ok) {
389
391
  throw new Error(
390
- `EngineerOS rejected the workspace (${response.status}): ${await response.text()}`,
392
+ await describeRejectedResponse(response, "the workspace"),
391
393
  );
392
394
  }
393
395
  const result = await response.json();
@@ -422,7 +424,7 @@ async function submitWorkspaceSnapshot() {
422
424
  } catch (error) {
423
425
  snapshotInFlight = false;
424
426
  console.error(
425
- `Workspace assessment failed: ${error instanceof Error ? error.message : String(error)}`,
427
+ `Workspace assessment failed: ${protocolFailureMessage(error)}`,
426
428
  );
427
429
  }
428
430
  }
@@ -545,17 +547,18 @@ async function executePrompt(assignment) {
545
547
  }),
546
548
  );
547
549
  } catch (error) {
550
+ const message = protocolFailureMessage(error);
548
551
  if (!promptState.cancelled && socket.readyState === WebSocket.OPEN) {
549
552
  socket.send(
550
553
  JSON.stringify({
551
554
  type: "prompt.failed",
552
555
  prompt_id: promptId,
553
- message: error instanceof Error ? error.message : String(error),
556
+ message,
554
557
  }),
555
558
  );
556
559
  }
557
560
  if (!promptState.cancelled) {
558
- console.error(error instanceof Error ? error.message : String(error));
561
+ console.error(message);
559
562
  }
560
563
  } finally {
561
564
  if (activePrompts.get(promptId) === promptState)
@@ -662,22 +665,23 @@ async function executeAssessment(assignment) {
662
665
  );
663
666
  if (!response.ok) {
664
667
  throw new Error(
665
- `EngineerOS rejected the assessment (${response.status}): ${await response.text()}`,
668
+ await describeRejectedResponse(response, "the assessment"),
666
669
  );
667
670
  }
668
671
  console.log("Workspace assessment is current in EngineerOS.");
669
672
  } catch (error) {
670
673
  flushOutput();
674
+ const message = protocolFailureMessage(error);
671
675
  if (socket.readyState === WebSocket.OPEN) {
672
676
  socket.send(
673
677
  JSON.stringify({
674
678
  type: "workspace.assessment.failed",
675
679
  assessment_id: assessmentId,
676
- message: error instanceof Error ? error.message : String(error),
680
+ message,
677
681
  }),
678
682
  );
679
683
  }
680
- console.error(error instanceof Error ? error.message : String(error));
684
+ console.error(message);
681
685
  } finally {
682
686
  if (outputTimer) clearTimeout(outputTimer);
683
687
  clearInterval(heartbeat);
@@ -735,10 +739,9 @@ async function execute(assignment) {
735
739
  body: JSON.stringify(result),
736
740
  },
737
741
  );
738
- if (!response.ok)
739
- throw new Error(
740
- `EngineerOS rejected the result (${response.status}): ${await response.text()}`,
741
- );
742
+ if (!response.ok) {
743
+ throw new Error(await describeRejectedResponse(response, "the result"));
744
+ }
742
745
  const integration = await applyAcceptedChange(
743
746
  config.workspace,
744
747
  assignment.base_revision,
@@ -757,17 +760,17 @@ async function execute(assignment) {
757
760
  );
758
761
  }
759
762
  } catch (error) {
763
+ const message = protocolFailureMessage(error);
760
764
  if (!active?.cancelled && socket.readyState === WebSocket.OPEN) {
761
765
  socket.send(
762
766
  JSON.stringify({
763
767
  type: "run.failed",
764
768
  run_id: runId,
765
- message: error instanceof Error ? error.message : String(error),
769
+ message,
766
770
  }),
767
771
  );
768
772
  }
769
- if (!active?.cancelled)
770
- console.error(error instanceof Error ? error.message : String(error));
773
+ if (!active?.cancelled) console.error(message);
771
774
  } finally {
772
775
  clearInterval(heartbeat);
773
776
  active = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@engineeros/connector",
3
- "version": "0.12.3",
3
+ "version": "0.12.5",
4
4
  "description": "Connect a local coding agent to EngineerOS, using ACP when supported.",
5
5
  "private": false,
6
6
  "type": "module",
@@ -424,7 +424,11 @@ export function normalizeTokenUsage(value) {
424
424
  input_tokens: number("inputTokens", "input_tokens"),
425
425
  output_tokens: number("outputTokens", "output_tokens"),
426
426
  cache_read_tokens: number("cachedReadTokens", "cachedInputTokens", "cache_read_input_tokens"),
427
- cache_write_tokens: number("cachedWriteTokens", "cache_creation_input_tokens"),
427
+ cache_write_tokens: number(
428
+ "cachedWriteTokens",
429
+ "cacheWriteInputTokens",
430
+ "cache_creation_input_tokens",
431
+ ),
428
432
  reasoning_tokens: number("reasoningTokens", "reasoningOutputTokens", "reasoning_tokens"),
429
433
  };
430
434
  usage.total_tokens = number("totalTokens", "total_tokens") ||
@@ -157,11 +157,14 @@ export function launchCodexAppServer({
157
157
  if (message.method === "item/agentMessage/delta" && typeof params.delta === "string") {
158
158
  finalMessage += params.delta;
159
159
  callbacks.onEvent?.({ type: "codex.agent_message_delta", delta: params.delta });
160
- } else if (message.method === "item/started" || message.method === "item/completed") {
161
- callbacks.onEvent?.({ type: `codex.${message.method}`, item: params.item });
160
+ } else if (message.method === "item/started" || message.method === "item/completed") {
161
+ callbacks.onEvent?.({ type: `codex.${message.method}`, item: params.item });
162
162
  } else if (message.method === "thread/tokenUsage/updated") {
163
+ const tokenUsage = params.tokenUsage || params.usage || params;
163
164
  usage = normalizeTokenUsage(
164
- params.last || params.usage || params.tokenUsage?.last || params.tokenUsage || params,
165
+ previousSessionId
166
+ ? tokenUsage.last || params.last || tokenUsage
167
+ : tokenUsage.total || params.total || tokenUsage.last || params.last || tokenUsage,
165
168
  );
166
169
  callbacks.onEvent?.({ type: "codex.usage", update: params });
167
170
  if (usage && completionTimer) {
@@ -1,3 +1,6 @@
1
+ const PROTOCOL_FAILURE_MESSAGE_MAX_LENGTH = 2_000;
2
+ const TRUNCATED_FAILURE_SUFFIX = "\n… [truncated by EngineerOS connector]";
3
+
1
4
  export function describeWebSocketError(event) {
2
5
  return (
3
6
  event?.error?.message ||
@@ -6,6 +9,26 @@ export function describeWebSocketError(event) {
6
9
  );
7
10
  }
8
11
 
12
+ export function protocolFailureMessage(error) {
13
+ const raw = error instanceof Error ? error.message : String(error ?? "");
14
+ const message =
15
+ raw.trim() || "The connector failed without providing an error detail.";
16
+ const characters = [...message];
17
+ if (characters.length <= PROTOCOL_FAILURE_MESSAGE_MAX_LENGTH) return message;
18
+ const suffix = [...TRUNCATED_FAILURE_SUFFIX];
19
+ return `${characters
20
+ .slice(0, PROTOCOL_FAILURE_MESSAGE_MAX_LENGTH - suffix.length)
21
+ .join("")}${TRUNCATED_FAILURE_SUFFIX}`;
22
+ }
23
+
24
+ export async function describeRejectedResponse(response, subject) {
25
+ const responseBody = await response.text();
26
+ const detail = responseDetail(responseBody);
27
+ return protocolFailureMessage(
28
+ `EngineerOS rejected ${subject} (${response.status})${detail ? `: ${detail}` : "."}`,
29
+ );
30
+ }
31
+
9
32
  export function startConnectionWatchdog(
10
33
  socket,
11
34
  url,
@@ -24,3 +47,28 @@ export function startConnectionWatchdog(
24
47
 
25
48
  return () => clearTimeout(timer);
26
49
  }
50
+
51
+ function responseDetail(responseBody) {
52
+ const body = String(responseBody || "").trim();
53
+ if (!body) return "";
54
+ try {
55
+ const parsed = JSON.parse(body);
56
+ if (typeof parsed?.detail === "string") return parsed.detail;
57
+ if (Array.isArray(parsed?.detail)) {
58
+ return parsed.detail.map(validationIssue).filter(Boolean).join("; ");
59
+ }
60
+ } catch {
61
+ // Plain-text server errors are already useful to the operator.
62
+ }
63
+ return body;
64
+ }
65
+
66
+ function validationIssue(issue) {
67
+ if (typeof issue === "string") return issue;
68
+ if (!issue || typeof issue !== "object") return "";
69
+ const location = Array.isArray(issue.loc) ? issue.loc.join(".") : "";
70
+ const message = typeof issue.msg === "string" ? issue.msg : "";
71
+ const type = typeof issue.type === "string" ? ` (${issue.type})` : "";
72
+ if (!location && !message) return "";
73
+ return `${location ? `${location}: ` : ""}${message}${type}`;
74
+ }