@hilbras/omninode 2.0.0-alpha.2 → 2.0.0-alpha.3

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
@@ -18,7 +18,7 @@ existing intelligence; it does not try to become another model.
18
18
 
19
19
  ## Status
20
20
 
21
- **v2.0.0-alpha.2 — v2 Phase 2: Execution Reliability** (after Phase 1: architecture & API stabilization) (the v2
21
+ **v2.0.0-alpha.3 — v2 Phase 3: Agent Protocol v2** (after Phase 1: architecture & API stabilization, Phase 2: execution reliability) (the v2
22
22
  roadmap is [docs/ROADMAP_V2.md](docs/ROADMAP_V2.md); the v1 plan is complete:
23
23
  the
24
24
  full loop from §28 runs in one command —
@@ -296,7 +296,7 @@ required for the core engine.
296
296
  - [Architecture](docs/ARCHITECTURE.md) — module map, workflow, local state
297
297
  - [Security model](docs/SECURITY.md) — secrets, process execution, env isolation, audit log
298
298
  - [API stability policy](docs/API_STABILITY.md) and [public API inventory](docs/API.md)
299
- - [Deprecations](docs/DEPRECATIONS.md) · [Changelog](CHANGELOG.md) · [v2 roadmap](docs/ROADMAP_V2.md)
299
+ - [Agent Protocol v2 spec](docs/PROTOCOL.md) · [Deprecations](docs/DEPRECATIONS.md) · [Changelog](CHANGELOG.md) · [v2 roadmap](docs/ROADMAP_V2.md)
300
300
  - [Example project](examples/demo/) — a runnable end-to-end demo (`./demo.sh`)
301
301
 
302
302
  Prefer a container? `docker build -t omninode .` then
package/dist/cli/index.js CHANGED
@@ -6,7 +6,7 @@ import { realpathSync } from "fs";
6
6
  import { Command } from "commander";
7
7
 
8
8
  // src/version.ts
9
- var OMNINODE_VERSION = "2.0.0-alpha.2";
9
+ var OMNINODE_VERSION = "2.0.0-alpha.3";
10
10
 
11
11
  // src/errors/index.ts
12
12
  var OmniNodeError = class extends Error {
@@ -44,11 +44,6 @@ var PipelineError = class extends OmniNodeError {
44
44
  super(code, message, options);
45
45
  }
46
46
  };
47
- var ProtocolError = class extends OmniNodeError {
48
- constructor(message, options = {}) {
49
- super("PROTOCOL_VIOLATION", message, options);
50
- }
51
- };
52
47
  var PlannerError = class extends OmniNodeError {
53
48
  constructor(code, message, options = {}) {
54
49
  super(code, message, options);
@@ -897,6 +892,329 @@ function registerModelsCommand(program) {
897
892
 
898
893
  // src/agents/process/index.ts
899
894
  import { spawn } from "child_process";
895
+
896
+ // src/agent-protocol/version.ts
897
+ var PROTOCOL_V2 = "omninode-agent-protocol/2";
898
+ var SUPPORTED_PROTOCOL_VERSIONS = [
899
+ "omninode-agent-protocol/1",
900
+ PROTOCOL_V2
901
+ ];
902
+ var MAX_MESSAGE_BYTES = 1048576;
903
+ function isSupportedProtocol(protocol) {
904
+ return SUPPORTED_PROTOCOL_VERSIONS.includes(protocol);
905
+ }
906
+
907
+ // src/agent-protocol/types.ts
908
+ function newMessageId(prefix = "msg") {
909
+ const random = Math.random().toString(36).slice(2, 10);
910
+ return `${prefix}-${Date.now().toString(36)}-${random}`;
911
+ }
912
+ function envelope(type, payload, ids = {}) {
913
+ return {
914
+ protocol: PROTOCOL_V2,
915
+ messageId: newMessageId(),
916
+ ...ids.requestId !== void 0 ? { requestId: ids.requestId } : {},
917
+ ...ids.taskId !== void 0 ? { taskId: ids.taskId } : {},
918
+ ...ids.agentId !== void 0 ? { agentId: ids.agentId } : {},
919
+ ...ids.pipelineId !== void 0 ? { pipelineId: ids.pipelineId } : {},
920
+ ...ids.executionId !== void 0 ? { executionId: ids.executionId } : {},
921
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
922
+ type,
923
+ payload,
924
+ ...ids.metadata !== void 0 ? { metadata: ids.metadata } : {}
925
+ };
926
+ }
927
+
928
+ // src/agent-protocol/codec.ts
929
+ var KNOWN_TYPES = /* @__PURE__ */ new Set([
930
+ // node → agent
931
+ "TASK",
932
+ "HELLO_ACK",
933
+ "CONTEXT",
934
+ "INSTRUCTION",
935
+ "RESPONSE",
936
+ "CANCEL",
937
+ // agent → node
938
+ "HELLO",
939
+ "TASK_REQUEST",
940
+ "TASK_ACCEPTED",
941
+ "TASK_STARTED",
942
+ "STATUS",
943
+ "QUESTION",
944
+ "REPORT",
945
+ "ARTIFACT",
946
+ "COMPLETION",
947
+ "ERROR",
948
+ "CANCELLED"
949
+ ]);
950
+ var PAYLOAD_REQUIRED_TYPES = /* @__PURE__ */ new Set(["TASK", "REPORT", "COMPLETION", "ERROR", "QUESTION", "ARTIFACT", "HELLO"]);
951
+ function validateEnvelope(value, line) {
952
+ const sample = line?.slice(0, 200);
953
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
954
+ return { ok: false, error: { code: "NOT_AN_OBJECT", message: "message must be a JSON object", ...sample ? { sample } : {} } };
955
+ }
956
+ const raw = value;
957
+ const protocol = raw.protocol;
958
+ if (protocol === void 0) {
959
+ const type = raw.type;
960
+ if (typeof type !== "string" || !KNOWN_TYPES.has(type)) {
961
+ return { ok: false, error: { code: "UNKNOWN_TYPE", message: `unknown message type ${String(type)}`, ...sample ? { sample } : {} } };
962
+ }
963
+ return {
964
+ ok: true,
965
+ legacy: true,
966
+ envelope: {
967
+ protocol: "omninode-agent-protocol/1",
968
+ messageId: typeof raw.messageId === "string" ? raw.messageId : "legacy",
969
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
970
+ type,
971
+ payload: raw.payload
972
+ }
973
+ };
974
+ }
975
+ if (typeof protocol !== "string" || !isSupportedProtocol(protocol)) {
976
+ return {
977
+ ok: false,
978
+ error: { code: "UNSUPPORTED_VERSION", message: `unsupported protocol "${String(protocol)}"`, ...sample ? { sample } : {} }
979
+ };
980
+ }
981
+ if (raw.type === void 0) {
982
+ return { ok: false, error: { code: "MISSING_FIELD", message: "message is missing 'type'", ...sample ? { sample } : {} } };
983
+ }
984
+ if (typeof raw.type !== "string" || !KNOWN_TYPES.has(raw.type)) {
985
+ return { ok: false, error: { code: "UNKNOWN_TYPE", message: `unknown message type ${String(raw.type)}`, ...sample ? { sample } : {} } };
986
+ }
987
+ if (raw.timestamp !== void 0 && typeof raw.timestamp !== "string") {
988
+ return { ok: false, error: { code: "MISSING_FIELD", message: "'timestamp' must be a string", ...sample ? { sample } : {} } };
989
+ }
990
+ if (PAYLOAD_REQUIRED_TYPES.has(raw.type) && (raw.payload === void 0 || raw.payload === null)) {
991
+ return { ok: false, error: { code: "INVALID_PAYLOAD", message: `${raw.type} requires a payload`, ...sample ? { sample } : {} } };
992
+ }
993
+ return {
994
+ ok: true,
995
+ legacy: protocol !== PROTOCOL_V2,
996
+ envelope: {
997
+ protocol,
998
+ messageId: typeof raw.messageId === "string" ? raw.messageId : "unknown",
999
+ ...typeof raw.requestId === "string" ? { requestId: raw.requestId } : {},
1000
+ ...typeof raw.taskId === "string" ? { taskId: raw.taskId } : {},
1001
+ ...typeof raw.agentId === "string" ? { agentId: raw.agentId } : {},
1002
+ ...typeof raw.pipelineId === "string" ? { pipelineId: raw.pipelineId } : {},
1003
+ ...typeof raw.executionId === "string" ? { executionId: raw.executionId } : {},
1004
+ timestamp: typeof raw.timestamp === "string" ? raw.timestamp : (/* @__PURE__ */ new Date()).toISOString(),
1005
+ type: raw.type,
1006
+ payload: raw.payload,
1007
+ ...raw.metadata !== null && typeof raw.metadata === "object" ? { metadata: raw.metadata } : {}
1008
+ }
1009
+ };
1010
+ }
1011
+ function decodeMessage(line, maxBytes = MAX_MESSAGE_BYTES) {
1012
+ const trimmed = line.trim();
1013
+ if (trimmed.length === 0) {
1014
+ return { ok: false, error: { code: "INVALID_JSON", message: "empty line" } };
1015
+ }
1016
+ if (trimmed.length > maxBytes) {
1017
+ return {
1018
+ ok: false,
1019
+ error: { code: "OVERSIZED", message: `message of ${trimmed.length} bytes exceeds the ${maxBytes} byte limit`, sample: trimmed.slice(0, 200) }
1020
+ };
1021
+ }
1022
+ let value;
1023
+ try {
1024
+ value = JSON.parse(trimmed);
1025
+ } catch (error) {
1026
+ return {
1027
+ ok: false,
1028
+ error: { code: "INVALID_JSON", message: error instanceof Error ? error.message : "invalid JSON", sample: trimmed.slice(0, 200) }
1029
+ };
1030
+ }
1031
+ return validateEnvelope(value, trimmed);
1032
+ }
1033
+ function encodeMessage(envelope2, maxBytes = MAX_MESSAGE_BYTES) {
1034
+ const line = JSON.stringify(envelope2);
1035
+ if (line.length > maxBytes) {
1036
+ throw new Error(`outbound protocol message of ${line.length} bytes exceeds the ${maxBytes} byte limit`);
1037
+ }
1038
+ return line;
1039
+ }
1040
+ var ProtocolDecoder = class {
1041
+ buffer = "";
1042
+ maxBytes;
1043
+ constructor(maxBytes = MAX_MESSAGE_BYTES) {
1044
+ this.maxBytes = maxBytes;
1045
+ }
1046
+ push(chunk) {
1047
+ this.buffer += chunk;
1048
+ const results = [];
1049
+ let index = this.buffer.indexOf("\n");
1050
+ while (index >= 0) {
1051
+ const line = this.buffer.slice(0, index);
1052
+ this.buffer = this.buffer.slice(index + 1);
1053
+ if (line.trim().length > 0) results.push(decodeMessage(line, this.maxBytes));
1054
+ index = this.buffer.indexOf("\n");
1055
+ }
1056
+ if (this.buffer.length > this.maxBytes) {
1057
+ results.push({
1058
+ ok: false,
1059
+ error: { code: "OVERSIZED", message: `line exceeded ${this.maxBytes} bytes without a newline`, sample: this.buffer.slice(0, 200) }
1060
+ });
1061
+ this.buffer = "";
1062
+ }
1063
+ return results;
1064
+ }
1065
+ /** Flush any trailing line when the process exits. */
1066
+ flush() {
1067
+ if (this.buffer.trim().length === 0) {
1068
+ this.buffer = "";
1069
+ return [];
1070
+ }
1071
+ const result = decodeMessage(this.buffer, this.maxBytes);
1072
+ this.buffer = "";
1073
+ return [result];
1074
+ }
1075
+ };
1076
+
1077
+ // src/agent-protocol/handshake.ts
1078
+ function parseHello(envelopeValue) {
1079
+ if (envelopeValue.type !== "HELLO") {
1080
+ return { ok: false, reason: `expected HELLO, received ${envelopeValue.type}` };
1081
+ }
1082
+ const payload = envelopeValue.payload;
1083
+ if (payload === null || typeof payload !== "object") {
1084
+ return { ok: false, reason: "HELLO payload must be an object" };
1085
+ }
1086
+ const p = payload;
1087
+ if (typeof p.name !== "string" || p.name.length === 0) {
1088
+ return { ok: false, reason: "HELLO payload is missing 'name'" };
1089
+ }
1090
+ return {
1091
+ ok: true,
1092
+ descriptor: {
1093
+ protocol: envelopeValue.protocol,
1094
+ name: p.name,
1095
+ ...typeof p.version === "string" ? { version: p.version } : {},
1096
+ capabilities: Array.isArray(p.capabilities) ? p.capabilities.filter((c) => typeof c === "string") : [],
1097
+ supports: Array.isArray(p.supports) ? p.supports.filter((s) => typeof s === "string") : []
1098
+ }
1099
+ };
1100
+ }
1101
+ function buildHelloAck(descriptor) {
1102
+ return envelope("HELLO_ACK", {
1103
+ ok: true,
1104
+ protocol: PROTOCOL_V2,
1105
+ agent: descriptor.name,
1106
+ ...descriptor.version !== void 0 ? { agentVersion: descriptor.version } : {},
1107
+ supportedProtocols: [PROTOCOL_V2]
1108
+ }, { agentId: descriptor.name });
1109
+ }
1110
+
1111
+ // src/agent-protocol/session.ts
1112
+ var ProtocolSession = class {
1113
+ decoder;
1114
+ ids;
1115
+ responder;
1116
+ violations = [];
1117
+ questions = [];
1118
+ artifacts = [];
1119
+ reports = [];
1120
+ descriptor;
1121
+ completionSummary;
1122
+ errorMessage;
1123
+ constructor(ids, options = {}) {
1124
+ this.ids = ids;
1125
+ this.responder = options.responder;
1126
+ this.decoder = new ProtocolDecoder(options.maxBytes);
1127
+ }
1128
+ /** The v2 messages OmniNode sends to open a task (written to the agent's stdin). */
1129
+ taskMessages(objective, role, context, instruction) {
1130
+ const ids = this.ids;
1131
+ const messages = [envelope("TASK", { taskId: ids.taskId, objective }, ids)];
1132
+ if (role !== void 0) messages.push(envelope("CONTEXT", { role }, ids));
1133
+ if (context !== void 0 && context.length > 0) messages.push(envelope("CONTEXT", { body: context }, ids));
1134
+ if (instruction !== void 0 && instruction.length > 0) messages.push(envelope("INSTRUCTION", { text: instruction }, ids));
1135
+ return messages.map((m) => encodeMessage(m));
1136
+ }
1137
+ helloAck(descriptor) {
1138
+ return encodeMessage(buildHelloAck(descriptor));
1139
+ }
1140
+ /** Consumes decoded stdout, answering questions as they arrive. */
1141
+ async handle(decoded) {
1142
+ if (!decoded.ok) {
1143
+ this.violations.push(decoded.error);
1144
+ return;
1145
+ }
1146
+ const message = decoded.envelope;
1147
+ if (message.protocol === PROTOCOL_V2) this.sawV2 = true;
1148
+ if (message.type === "HELLO") {
1149
+ const hello = parseHello(message);
1150
+ if (hello.ok) this.descriptor = hello.descriptor;
1151
+ else this.violations.push({ code: "INVALID_PAYLOAD", message: `bad HELLO: ${hello.reason}` });
1152
+ return;
1153
+ }
1154
+ if (message.type === "QUESTION") {
1155
+ await this.answerQuestion(message);
1156
+ return;
1157
+ }
1158
+ if (message.type === "REPORT") {
1159
+ this.reports.push(message.payload);
1160
+ return;
1161
+ }
1162
+ if (message.type === "ARTIFACT") {
1163
+ this.artifacts.push(message.payload);
1164
+ return;
1165
+ }
1166
+ if (message.type === "COMPLETION") {
1167
+ const payload = message.payload;
1168
+ this.completionSummary = typeof payload?.summary === "string" ? payload.summary : "";
1169
+ return;
1170
+ }
1171
+ if (message.type === "ERROR") {
1172
+ const payload = message.payload;
1173
+ const detail = typeof payload?.message === "string" ? payload.message : "agent reported an error";
1174
+ const code = typeof payload?.code === "string" ? ` [${payload.code}]` : "";
1175
+ this.errorMessage = `Agent error${code}: ${detail}`;
1176
+ return;
1177
+ }
1178
+ if (message.type === "TASK_ACCEPTED" || message.type === "TASK_STARTED" || message.type === "STATUS") {
1179
+ return;
1180
+ }
1181
+ this.violations.push({
1182
+ code: "UNKNOWN_TYPE",
1183
+ message: `unexpected message type ${message.type} from agent`
1184
+ });
1185
+ }
1186
+ async answerQuestion(message) {
1187
+ const payload = message.payload ?? {};
1188
+ const questionId = typeof payload.questionId === "string" ? payload.questionId : message.messageId;
1189
+ const question = typeof payload.question === "string" ? payload.question : "";
1190
+ const expected = typeof payload.expected === "string" ? payload.expected : "text";
1191
+ let answer;
1192
+ if (this.responder) {
1193
+ try {
1194
+ answer = await this.responder({ questionId, question, expected });
1195
+ } catch (error) {
1196
+ this.violations.push({
1197
+ code: "INVALID_PAYLOAD",
1198
+ message: `question responder failed: ${error instanceof Error ? error.message : String(error)}`
1199
+ });
1200
+ }
1201
+ }
1202
+ this.questions.push({ questionId, question, ...answer !== void 0 ? { answer } : {} });
1203
+ this.pendingAnswers.push(
1204
+ encodeMessage(
1205
+ envelope("RESPONSE", { questionId, ...answer !== void 0 ? { body: answer } : {} }, {
1206
+ ...this.ids,
1207
+ requestId: message.requestId ?? this.ids.requestId
1208
+ })
1209
+ )
1210
+ );
1211
+ }
1212
+ pendingAnswers = [];
1213
+ /** True once any inbound message carried the v2 protocol field. */
1214
+ sawV2 = false;
1215
+ };
1216
+
1217
+ // src/agents/process/index.ts
900
1218
  var DEFAULT_TIMEOUT_MS2 = 6e5;
901
1219
  var KILL_GRACE_MS = 5e3;
902
1220
  function buildChildEnv(config) {
@@ -930,18 +1248,19 @@ ${input.instruction}`);
930
1248
  return sections.join("\n\n");
931
1249
  }
932
1250
  function buildProtocolMessages(input, agentName) {
933
- const meta = {
1251
+ const session = new ProtocolSession({
1252
+ requestId: `req-${input.taskId}`,
934
1253
  taskId: input.taskId,
935
- agent: agentName,
936
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
937
- };
938
- const messages = [
939
- { type: "TASK", payload: { taskId: input.taskId, objective: input.objective } }
940
- ];
941
- if (input.role) messages.push({ type: "ROLE", payload: input.role });
942
- if (input.context) messages.push({ type: "CONTEXT", payload: { body: input.context } });
943
- if (input.instruction) messages.push({ type: "INSTRUCTION", payload: { text: input.instruction } });
944
- return messages.map((message) => JSON.stringify({ meta, message }));
1254
+ agentId: agentName,
1255
+ ...input.pipelineId !== void 0 ? { pipelineId: input.pipelineId } : {},
1256
+ ...input.executionId !== void 0 ? { executionId: input.executionId } : {}
1257
+ });
1258
+ return session.taskMessages(
1259
+ input.objective,
1260
+ input.role,
1261
+ input.context,
1262
+ input.instruction
1263
+ );
945
1264
  }
946
1265
  var ProcessAgent = class {
947
1266
  config;
@@ -1094,62 +1413,75 @@ var ProcessAgent = class {
1094
1413
  return [...this.running.keys()];
1095
1414
  }
1096
1415
  };
1097
- function interpretProtocolRun({ input, agentName, stdout, exitCode, logs }) {
1098
- const reports = [];
1099
- let summary;
1100
- let error;
1101
- let sawProtocolMessage = false;
1102
- for (const line of stdout.split("\n")) {
1103
- const trimmed2 = line.trim();
1104
- if (trimmed2.length === 0) continue;
1105
- const message = parseAgentMessage(trimmed2);
1106
- if (!message) continue;
1107
- sawProtocolMessage = true;
1108
- if (message.type === "REPORT") {
1109
- const report = normalizeReport(message.payload, input.taskId, agentName, reports.length);
1110
- if (report) reports.push(report);
1111
- } else if (message.type === "COMPLETION") {
1112
- const payload = message.payload;
1113
- if (typeof payload?.summary === "string") summary = payload.summary;
1114
- } else if (message.type === "ERROR") {
1115
- const payload = message.payload;
1116
- const detail = typeof payload?.message === "string" ? payload.message : "unknown error";
1117
- const code = typeof payload?.code === "string" ? ` [${payload.code}]` : "";
1118
- error = `Agent error${code}: ${detail}`;
1119
- }
1416
+ function interpretProtocolRun({
1417
+ input,
1418
+ agentName,
1419
+ stdout,
1420
+ exitCode,
1421
+ logs,
1422
+ responder
1423
+ }) {
1424
+ const session = new ProtocolSession(
1425
+ {
1426
+ requestId: `req-${input.taskId}`,
1427
+ taskId: input.taskId,
1428
+ agentId: agentName,
1429
+ ...input.pipelineId !== void 0 ? { pipelineId: input.pipelineId } : {},
1430
+ ...input.executionId !== void 0 ? { executionId: input.executionId } : {}
1431
+ },
1432
+ { ...responder !== void 0 ? { responder } : {} }
1433
+ );
1434
+ for (const result of session.decoder.push(stdout)) {
1435
+ session.handle(result);
1120
1436
  }
1121
- if (!sawProtocolMessage && exitCode === 0) {
1122
- throw new ProtocolError(
1123
- `Agent "${agentName}" produced no protocol messages on stdout (input mode "protocol").`,
1124
- { details: { taskId: input.taskId, stdout: stdout.slice(0, 2e3) } }
1125
- );
1437
+ for (const result of session.decoder.flush()) {
1438
+ session.handle(result);
1126
1439
  }
1440
+ const reports = session.reports.flatMap((payload) => Array.isArray(payload) ? payload : [payload]).map((payload, index) => normalizeReport(payload, input.taskId, agentName, index)).filter((report) => report !== void 0);
1127
1441
  const trimmed = stdout.trim();
1442
+ const protocolBlock = {
1443
+ protocol: session.descriptor?.protocol ?? PROTOCOL_V2,
1444
+ legacy: !session.sawV2 && session.descriptor === void 0,
1445
+ ...session.descriptor !== void 0 ? { descriptor: session.descriptor } : {},
1446
+ questions: session.questions,
1447
+ violations: session.violations.map((v) => `${v.code}: ${v.message}`)
1448
+ };
1449
+ if (session.errorMessage !== void 0) {
1450
+ return {
1451
+ taskId: input.taskId,
1452
+ status: "failed",
1453
+ error: session.errorMessage,
1454
+ rawOutput: trimmed.length > 0 ? trimmed : void 0,
1455
+ exitCode: exitCode ?? void 0,
1456
+ logs,
1457
+ protocol: protocolBlock,
1458
+ ...reports.length > 0 ? { reports } : {}
1459
+ };
1460
+ }
1461
+ if (session.descriptor === void 0 && session.violations.length > 0 && session.reports.length === 0 && session.completionSummary === void 0) {
1462
+ return {
1463
+ taskId: input.taskId,
1464
+ status: "failed",
1465
+ error: `agent produced no valid protocol messages: ${protocolBlock.violations.join("; ")}`,
1466
+ rawOutput: trimmed.length > 0 ? trimmed : void 0,
1467
+ exitCode: exitCode ?? void 0,
1468
+ logs,
1469
+ protocol: protocolBlock
1470
+ };
1471
+ }
1472
+ const completed = exitCode === 0 && session.completionSummary !== void 0;
1128
1473
  return {
1129
1474
  taskId: input.taskId,
1130
- status: error !== void 0 || exitCode !== 0 ? "failed" : "completed",
1131
- summary: summary ?? reports[0]?.summary,
1475
+ status: completed || exitCode === 0 ? completed ? "completed" : "completed" : "failed",
1476
+ summary: session.completionSummary ?? reports[0]?.summary,
1132
1477
  rawOutput: trimmed.length > 0 ? trimmed : void 0,
1133
1478
  exitCode: exitCode ?? void 0,
1134
1479
  logs,
1135
- reports: reports.length > 0 ? reports : void 0,
1136
- error: error ?? (exitCode !== 0 ? `Process exited with code ${exitCode ?? "unknown"}` : void 0)
1480
+ protocol: protocolBlock,
1481
+ ...reports.length > 0 ? { reports } : {},
1482
+ ...exitCode !== 0 ? { error: `Process exited with code ${exitCode ?? "unknown"}` } : {}
1137
1483
  };
1138
1484
  }
1139
- function parseAgentMessage(line) {
1140
- try {
1141
- const parsed = JSON.parse(line);
1142
- if (typeof parsed.type === "string") {
1143
- return { type: parsed.type, payload: parsed.payload };
1144
- }
1145
- const inner = parsed.message;
1146
- if (inner && typeof inner.type === "string") {
1147
- return { type: inner.type, payload: inner.payload };
1148
- }
1149
- } catch {
1150
- }
1151
- return void 0;
1152
- }
1153
1485
  function normalizeReport(payload, taskId, agentName, index) {
1154
1486
  if (payload === null || typeof payload !== "object") return void 0;
1155
1487
  const raw = payload;