@hilbras/omninode 2.0.0-alpha.2 → 2.0.0-alpha.4
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 +26 -10
- package/dist/cli/index.js +618 -92
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +410 -51
- package/dist/index.js +651 -86
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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.
|
|
9
|
+
var OMNINODE_VERSION = "2.0.0-alpha.4";
|
|
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);
|
|
@@ -88,6 +83,11 @@ var agentConfigSchema = z.object({
|
|
|
88
83
|
input_mode: z.enum(["stdin", "arg", "protocol"]).default("stdin"),
|
|
89
84
|
timeout_ms: z.number().int().positive().optional(),
|
|
90
85
|
inherit_env: z.boolean().optional(),
|
|
86
|
+
env_policy: z.enum(["inherit", "allowlist", "denylist", "explicit"]).optional(),
|
|
87
|
+
env_allowlist: z.array(z.string()).optional(),
|
|
88
|
+
env_denylist: z.array(z.string()).optional(),
|
|
89
|
+
max_output_bytes: z.number().int().positive().optional(),
|
|
90
|
+
allow_external_cwd: z.boolean().optional(),
|
|
91
91
|
metadata: z.record(z.unknown()).optional()
|
|
92
92
|
}).strict();
|
|
93
93
|
var roleConfigSchema = z.object({
|
|
@@ -262,6 +262,11 @@ function toAgentConfig(raw) {
|
|
|
262
262
|
inputMode: raw.input_mode,
|
|
263
263
|
timeoutMs: raw.timeout_ms,
|
|
264
264
|
inheritEnv: raw.inherit_env,
|
|
265
|
+
envPolicy: raw.env_policy,
|
|
266
|
+
envAllowlist: raw.env_allowlist,
|
|
267
|
+
envDenylist: raw.env_denylist,
|
|
268
|
+
maxOutputBytes: raw.max_output_bytes,
|
|
269
|
+
allowExternalCwd: raw.allow_external_cwd,
|
|
265
270
|
metadata: raw.metadata
|
|
266
271
|
};
|
|
267
272
|
}
|
|
@@ -897,19 +902,416 @@ function registerModelsCommand(program) {
|
|
|
897
902
|
|
|
898
903
|
// src/agents/process/index.ts
|
|
899
904
|
import { spawn } from "child_process";
|
|
900
|
-
|
|
901
|
-
|
|
905
|
+
|
|
906
|
+
// src/agent-protocol/version.ts
|
|
907
|
+
var PROTOCOL_V2 = "omninode-agent-protocol/2";
|
|
908
|
+
var SUPPORTED_PROTOCOL_VERSIONS = [
|
|
909
|
+
"omninode-agent-protocol/1",
|
|
910
|
+
PROTOCOL_V2
|
|
911
|
+
];
|
|
912
|
+
var MAX_MESSAGE_BYTES = 1048576;
|
|
913
|
+
function isSupportedProtocol(protocol) {
|
|
914
|
+
return SUPPORTED_PROTOCOL_VERSIONS.includes(protocol);
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
// src/agent-protocol/types.ts
|
|
918
|
+
function newMessageId(prefix = "msg") {
|
|
919
|
+
const random = Math.random().toString(36).slice(2, 10);
|
|
920
|
+
return `${prefix}-${Date.now().toString(36)}-${random}`;
|
|
921
|
+
}
|
|
922
|
+
function envelope(type, payload, ids = {}) {
|
|
923
|
+
return {
|
|
924
|
+
protocol: PROTOCOL_V2,
|
|
925
|
+
messageId: newMessageId(),
|
|
926
|
+
...ids.requestId !== void 0 ? { requestId: ids.requestId } : {},
|
|
927
|
+
...ids.taskId !== void 0 ? { taskId: ids.taskId } : {},
|
|
928
|
+
...ids.agentId !== void 0 ? { agentId: ids.agentId } : {},
|
|
929
|
+
...ids.pipelineId !== void 0 ? { pipelineId: ids.pipelineId } : {},
|
|
930
|
+
...ids.executionId !== void 0 ? { executionId: ids.executionId } : {},
|
|
931
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
932
|
+
type,
|
|
933
|
+
payload,
|
|
934
|
+
...ids.metadata !== void 0 ? { metadata: ids.metadata } : {}
|
|
935
|
+
};
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
// src/agent-protocol/codec.ts
|
|
939
|
+
var KNOWN_TYPES = /* @__PURE__ */ new Set([
|
|
940
|
+
// node → agent
|
|
941
|
+
"TASK",
|
|
942
|
+
"HELLO_ACK",
|
|
943
|
+
"CONTEXT",
|
|
944
|
+
"INSTRUCTION",
|
|
945
|
+
"RESPONSE",
|
|
946
|
+
"CANCEL",
|
|
947
|
+
// agent → node
|
|
948
|
+
"HELLO",
|
|
949
|
+
"TASK_REQUEST",
|
|
950
|
+
"TASK_ACCEPTED",
|
|
951
|
+
"TASK_STARTED",
|
|
952
|
+
"STATUS",
|
|
953
|
+
"QUESTION",
|
|
954
|
+
"REPORT",
|
|
955
|
+
"ARTIFACT",
|
|
956
|
+
"COMPLETION",
|
|
957
|
+
"ERROR",
|
|
958
|
+
"CANCELLED"
|
|
959
|
+
]);
|
|
960
|
+
var PAYLOAD_REQUIRED_TYPES = /* @__PURE__ */ new Set(["TASK", "REPORT", "COMPLETION", "ERROR", "QUESTION", "ARTIFACT", "HELLO"]);
|
|
961
|
+
function validateEnvelope(value, line) {
|
|
962
|
+
const sample = line?.slice(0, 200);
|
|
963
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
964
|
+
return { ok: false, error: { code: "NOT_AN_OBJECT", message: "message must be a JSON object", ...sample ? { sample } : {} } };
|
|
965
|
+
}
|
|
966
|
+
const raw = value;
|
|
967
|
+
const protocol = raw.protocol;
|
|
968
|
+
if (protocol === void 0) {
|
|
969
|
+
const type = raw.type;
|
|
970
|
+
if (typeof type !== "string" || !KNOWN_TYPES.has(type)) {
|
|
971
|
+
return { ok: false, error: { code: "UNKNOWN_TYPE", message: `unknown message type ${String(type)}`, ...sample ? { sample } : {} } };
|
|
972
|
+
}
|
|
973
|
+
return {
|
|
974
|
+
ok: true,
|
|
975
|
+
legacy: true,
|
|
976
|
+
envelope: {
|
|
977
|
+
protocol: "omninode-agent-protocol/1",
|
|
978
|
+
messageId: typeof raw.messageId === "string" ? raw.messageId : "legacy",
|
|
979
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
980
|
+
type,
|
|
981
|
+
payload: raw.payload
|
|
982
|
+
}
|
|
983
|
+
};
|
|
984
|
+
}
|
|
985
|
+
if (typeof protocol !== "string" || !isSupportedProtocol(protocol)) {
|
|
986
|
+
return {
|
|
987
|
+
ok: false,
|
|
988
|
+
error: { code: "UNSUPPORTED_VERSION", message: `unsupported protocol "${String(protocol)}"`, ...sample ? { sample } : {} }
|
|
989
|
+
};
|
|
990
|
+
}
|
|
991
|
+
if (raw.type === void 0) {
|
|
992
|
+
return { ok: false, error: { code: "MISSING_FIELD", message: "message is missing 'type'", ...sample ? { sample } : {} } };
|
|
993
|
+
}
|
|
994
|
+
if (typeof raw.type !== "string" || !KNOWN_TYPES.has(raw.type)) {
|
|
995
|
+
return { ok: false, error: { code: "UNKNOWN_TYPE", message: `unknown message type ${String(raw.type)}`, ...sample ? { sample } : {} } };
|
|
996
|
+
}
|
|
997
|
+
if (raw.timestamp !== void 0 && typeof raw.timestamp !== "string") {
|
|
998
|
+
return { ok: false, error: { code: "MISSING_FIELD", message: "'timestamp' must be a string", ...sample ? { sample } : {} } };
|
|
999
|
+
}
|
|
1000
|
+
if (PAYLOAD_REQUIRED_TYPES.has(raw.type) && (raw.payload === void 0 || raw.payload === null)) {
|
|
1001
|
+
return { ok: false, error: { code: "INVALID_PAYLOAD", message: `${raw.type} requires a payload`, ...sample ? { sample } : {} } };
|
|
1002
|
+
}
|
|
1003
|
+
return {
|
|
1004
|
+
ok: true,
|
|
1005
|
+
legacy: protocol !== PROTOCOL_V2,
|
|
1006
|
+
envelope: {
|
|
1007
|
+
protocol,
|
|
1008
|
+
messageId: typeof raw.messageId === "string" ? raw.messageId : "unknown",
|
|
1009
|
+
...typeof raw.requestId === "string" ? { requestId: raw.requestId } : {},
|
|
1010
|
+
...typeof raw.taskId === "string" ? { taskId: raw.taskId } : {},
|
|
1011
|
+
...typeof raw.agentId === "string" ? { agentId: raw.agentId } : {},
|
|
1012
|
+
...typeof raw.pipelineId === "string" ? { pipelineId: raw.pipelineId } : {},
|
|
1013
|
+
...typeof raw.executionId === "string" ? { executionId: raw.executionId } : {},
|
|
1014
|
+
timestamp: typeof raw.timestamp === "string" ? raw.timestamp : (/* @__PURE__ */ new Date()).toISOString(),
|
|
1015
|
+
type: raw.type,
|
|
1016
|
+
payload: raw.payload,
|
|
1017
|
+
...raw.metadata !== null && typeof raw.metadata === "object" ? { metadata: raw.metadata } : {}
|
|
1018
|
+
}
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
function decodeMessage(line, maxBytes = MAX_MESSAGE_BYTES) {
|
|
1022
|
+
const trimmed = line.trim();
|
|
1023
|
+
if (trimmed.length === 0) {
|
|
1024
|
+
return { ok: false, error: { code: "INVALID_JSON", message: "empty line" } };
|
|
1025
|
+
}
|
|
1026
|
+
if (trimmed.length > maxBytes) {
|
|
1027
|
+
return {
|
|
1028
|
+
ok: false,
|
|
1029
|
+
error: { code: "OVERSIZED", message: `message of ${trimmed.length} bytes exceeds the ${maxBytes} byte limit`, sample: trimmed.slice(0, 200) }
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
1032
|
+
let value;
|
|
1033
|
+
try {
|
|
1034
|
+
value = JSON.parse(trimmed);
|
|
1035
|
+
} catch (error) {
|
|
1036
|
+
return {
|
|
1037
|
+
ok: false,
|
|
1038
|
+
error: { code: "INVALID_JSON", message: error instanceof Error ? error.message : "invalid JSON", sample: trimmed.slice(0, 200) }
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
1041
|
+
return validateEnvelope(value, trimmed);
|
|
1042
|
+
}
|
|
1043
|
+
function encodeMessage(envelope2, maxBytes = MAX_MESSAGE_BYTES) {
|
|
1044
|
+
const line = JSON.stringify(envelope2);
|
|
1045
|
+
if (line.length > maxBytes) {
|
|
1046
|
+
throw new Error(`outbound protocol message of ${line.length} bytes exceeds the ${maxBytes} byte limit`);
|
|
1047
|
+
}
|
|
1048
|
+
return line;
|
|
1049
|
+
}
|
|
1050
|
+
var ProtocolDecoder = class {
|
|
1051
|
+
buffer = "";
|
|
1052
|
+
maxBytes;
|
|
1053
|
+
constructor(maxBytes = MAX_MESSAGE_BYTES) {
|
|
1054
|
+
this.maxBytes = maxBytes;
|
|
1055
|
+
}
|
|
1056
|
+
push(chunk) {
|
|
1057
|
+
this.buffer += chunk;
|
|
1058
|
+
const results = [];
|
|
1059
|
+
let index = this.buffer.indexOf("\n");
|
|
1060
|
+
while (index >= 0) {
|
|
1061
|
+
const line = this.buffer.slice(0, index);
|
|
1062
|
+
this.buffer = this.buffer.slice(index + 1);
|
|
1063
|
+
if (line.trim().length > 0) results.push(decodeMessage(line, this.maxBytes));
|
|
1064
|
+
index = this.buffer.indexOf("\n");
|
|
1065
|
+
}
|
|
1066
|
+
if (this.buffer.length > this.maxBytes) {
|
|
1067
|
+
results.push({
|
|
1068
|
+
ok: false,
|
|
1069
|
+
error: { code: "OVERSIZED", message: `line exceeded ${this.maxBytes} bytes without a newline`, sample: this.buffer.slice(0, 200) }
|
|
1070
|
+
});
|
|
1071
|
+
this.buffer = "";
|
|
1072
|
+
}
|
|
1073
|
+
return results;
|
|
1074
|
+
}
|
|
1075
|
+
/** Flush any trailing line when the process exits. */
|
|
1076
|
+
flush() {
|
|
1077
|
+
if (this.buffer.trim().length === 0) {
|
|
1078
|
+
this.buffer = "";
|
|
1079
|
+
return [];
|
|
1080
|
+
}
|
|
1081
|
+
const result = decodeMessage(this.buffer, this.maxBytes);
|
|
1082
|
+
this.buffer = "";
|
|
1083
|
+
return [result];
|
|
1084
|
+
}
|
|
1085
|
+
};
|
|
1086
|
+
|
|
1087
|
+
// src/agent-protocol/handshake.ts
|
|
1088
|
+
function parseHello(envelopeValue) {
|
|
1089
|
+
if (envelopeValue.type !== "HELLO") {
|
|
1090
|
+
return { ok: false, reason: `expected HELLO, received ${envelopeValue.type}` };
|
|
1091
|
+
}
|
|
1092
|
+
const payload = envelopeValue.payload;
|
|
1093
|
+
if (payload === null || typeof payload !== "object") {
|
|
1094
|
+
return { ok: false, reason: "HELLO payload must be an object" };
|
|
1095
|
+
}
|
|
1096
|
+
const p = payload;
|
|
1097
|
+
if (typeof p.name !== "string" || p.name.length === 0) {
|
|
1098
|
+
return { ok: false, reason: "HELLO payload is missing 'name'" };
|
|
1099
|
+
}
|
|
1100
|
+
return {
|
|
1101
|
+
ok: true,
|
|
1102
|
+
descriptor: {
|
|
1103
|
+
protocol: envelopeValue.protocol,
|
|
1104
|
+
name: p.name,
|
|
1105
|
+
...typeof p.version === "string" ? { version: p.version } : {},
|
|
1106
|
+
capabilities: Array.isArray(p.capabilities) ? p.capabilities.filter((c) => typeof c === "string") : [],
|
|
1107
|
+
supports: Array.isArray(p.supports) ? p.supports.filter((s) => typeof s === "string") : []
|
|
1108
|
+
}
|
|
1109
|
+
};
|
|
1110
|
+
}
|
|
1111
|
+
function buildHelloAck(descriptor) {
|
|
1112
|
+
return envelope("HELLO_ACK", {
|
|
1113
|
+
ok: true,
|
|
1114
|
+
protocol: PROTOCOL_V2,
|
|
1115
|
+
agent: descriptor.name,
|
|
1116
|
+
...descriptor.version !== void 0 ? { agentVersion: descriptor.version } : {},
|
|
1117
|
+
supportedProtocols: [PROTOCOL_V2]
|
|
1118
|
+
}, { agentId: descriptor.name });
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
// src/agent-protocol/session.ts
|
|
1122
|
+
var ProtocolSession = class {
|
|
1123
|
+
decoder;
|
|
1124
|
+
ids;
|
|
1125
|
+
responder;
|
|
1126
|
+
violations = [];
|
|
1127
|
+
questions = [];
|
|
1128
|
+
artifacts = [];
|
|
1129
|
+
reports = [];
|
|
1130
|
+
descriptor;
|
|
1131
|
+
completionSummary;
|
|
1132
|
+
errorMessage;
|
|
1133
|
+
constructor(ids, options = {}) {
|
|
1134
|
+
this.ids = ids;
|
|
1135
|
+
this.responder = options.responder;
|
|
1136
|
+
this.decoder = new ProtocolDecoder(options.maxBytes);
|
|
1137
|
+
}
|
|
1138
|
+
/** The v2 messages OmniNode sends to open a task (written to the agent's stdin). */
|
|
1139
|
+
taskMessages(objective, role, context, instruction) {
|
|
1140
|
+
const ids = this.ids;
|
|
1141
|
+
const messages = [envelope("TASK", { taskId: ids.taskId, objective }, ids)];
|
|
1142
|
+
if (role !== void 0) messages.push(envelope("CONTEXT", { role }, ids));
|
|
1143
|
+
if (context !== void 0 && context.length > 0) messages.push(envelope("CONTEXT", { body: context }, ids));
|
|
1144
|
+
if (instruction !== void 0 && instruction.length > 0) messages.push(envelope("INSTRUCTION", { text: instruction }, ids));
|
|
1145
|
+
return messages.map((m) => encodeMessage(m));
|
|
1146
|
+
}
|
|
1147
|
+
helloAck(descriptor) {
|
|
1148
|
+
return encodeMessage(buildHelloAck(descriptor));
|
|
1149
|
+
}
|
|
1150
|
+
/** Consumes decoded stdout, answering questions as they arrive. */
|
|
1151
|
+
async handle(decoded) {
|
|
1152
|
+
if (!decoded.ok) {
|
|
1153
|
+
this.violations.push(decoded.error);
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
const message = decoded.envelope;
|
|
1157
|
+
if (message.protocol === PROTOCOL_V2) this.sawV2 = true;
|
|
1158
|
+
if (message.type === "HELLO") {
|
|
1159
|
+
const hello = parseHello(message);
|
|
1160
|
+
if (hello.ok) this.descriptor = hello.descriptor;
|
|
1161
|
+
else this.violations.push({ code: "INVALID_PAYLOAD", message: `bad HELLO: ${hello.reason}` });
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
if (message.type === "QUESTION") {
|
|
1165
|
+
await this.answerQuestion(message);
|
|
1166
|
+
return;
|
|
1167
|
+
}
|
|
1168
|
+
if (message.type === "REPORT") {
|
|
1169
|
+
this.reports.push(message.payload);
|
|
1170
|
+
return;
|
|
1171
|
+
}
|
|
1172
|
+
if (message.type === "ARTIFACT") {
|
|
1173
|
+
this.artifacts.push(message.payload);
|
|
1174
|
+
return;
|
|
1175
|
+
}
|
|
1176
|
+
if (message.type === "COMPLETION") {
|
|
1177
|
+
const payload = message.payload;
|
|
1178
|
+
this.completionSummary = typeof payload?.summary === "string" ? payload.summary : "";
|
|
1179
|
+
return;
|
|
1180
|
+
}
|
|
1181
|
+
if (message.type === "ERROR") {
|
|
1182
|
+
const payload = message.payload;
|
|
1183
|
+
const detail = typeof payload?.message === "string" ? payload.message : "agent reported an error";
|
|
1184
|
+
const code = typeof payload?.code === "string" ? ` [${payload.code}]` : "";
|
|
1185
|
+
this.errorMessage = `Agent error${code}: ${detail}`;
|
|
1186
|
+
return;
|
|
1187
|
+
}
|
|
1188
|
+
if (message.type === "TASK_ACCEPTED" || message.type === "TASK_STARTED" || message.type === "STATUS") {
|
|
1189
|
+
return;
|
|
1190
|
+
}
|
|
1191
|
+
this.violations.push({
|
|
1192
|
+
code: "UNKNOWN_TYPE",
|
|
1193
|
+
message: `unexpected message type ${message.type} from agent`
|
|
1194
|
+
});
|
|
1195
|
+
}
|
|
1196
|
+
async answerQuestion(message) {
|
|
1197
|
+
const payload = message.payload ?? {};
|
|
1198
|
+
const questionId = typeof payload.questionId === "string" ? payload.questionId : message.messageId;
|
|
1199
|
+
const question = typeof payload.question === "string" ? payload.question : "";
|
|
1200
|
+
const expected = typeof payload.expected === "string" ? payload.expected : "text";
|
|
1201
|
+
let answer;
|
|
1202
|
+
if (this.responder) {
|
|
1203
|
+
try {
|
|
1204
|
+
answer = await this.responder({ questionId, question, expected });
|
|
1205
|
+
} catch (error) {
|
|
1206
|
+
this.violations.push({
|
|
1207
|
+
code: "INVALID_PAYLOAD",
|
|
1208
|
+
message: `question responder failed: ${error instanceof Error ? error.message : String(error)}`
|
|
1209
|
+
});
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
this.questions.push({ questionId, question, ...answer !== void 0 ? { answer } : {} });
|
|
1213
|
+
this.pendingAnswers.push(
|
|
1214
|
+
encodeMessage(
|
|
1215
|
+
envelope("RESPONSE", { questionId, ...answer !== void 0 ? { body: answer } : {} }, {
|
|
1216
|
+
...this.ids,
|
|
1217
|
+
requestId: message.requestId ?? this.ids.requestId
|
|
1218
|
+
})
|
|
1219
|
+
)
|
|
1220
|
+
);
|
|
1221
|
+
}
|
|
1222
|
+
pendingAnswers = [];
|
|
1223
|
+
/** True once any inbound message carried the v2 protocol field. */
|
|
1224
|
+
sawV2 = false;
|
|
1225
|
+
};
|
|
1226
|
+
|
|
1227
|
+
// src/agents/cwd.ts
|
|
1228
|
+
import { statSync } from "fs";
|
|
1229
|
+
import { isAbsolute, relative, resolve } from "path";
|
|
1230
|
+
function validateWorkingDirectory(cwd, options = {}) {
|
|
1231
|
+
if (!cwd) return void 0;
|
|
1232
|
+
const normalized = isAbsolute(cwd) ? resolve(cwd) : resolve(process.cwd(), cwd);
|
|
1233
|
+
let stats;
|
|
1234
|
+
try {
|
|
1235
|
+
stats = statSync(normalized);
|
|
1236
|
+
} catch (error) {
|
|
1237
|
+
throw new AgentError(
|
|
1238
|
+
"AGENT_NOT_FOUND",
|
|
1239
|
+
`Working directory "${normalized}" does not exist or is not accessible.`,
|
|
1240
|
+
{ cause: error, details: { cwd: normalized } }
|
|
1241
|
+
);
|
|
1242
|
+
}
|
|
1243
|
+
if (!stats.isDirectory()) {
|
|
1244
|
+
throw new AgentError("AGENT_NOT_FOUND", `Working directory "${normalized}" is not a directory.`, {
|
|
1245
|
+
details: { cwd: normalized }
|
|
1246
|
+
});
|
|
1247
|
+
}
|
|
1248
|
+
try {
|
|
1249
|
+
statSync(resolve(normalized, "."));
|
|
1250
|
+
} catch (error) {
|
|
1251
|
+
throw new AgentError(
|
|
1252
|
+
"AGENT_NOT_FOUND",
|
|
1253
|
+
`Working directory "${normalized}" is not accessible.`,
|
|
1254
|
+
{ cause: error }
|
|
1255
|
+
);
|
|
1256
|
+
}
|
|
1257
|
+
const projectRoot = resolve(options.projectRoot ?? process.cwd());
|
|
1258
|
+
const rel = relative(projectRoot, normalized);
|
|
1259
|
+
const outsideProject = rel.startsWith("..") || isAbsolute(rel);
|
|
1260
|
+
if (outsideProject && options.allowExternal !== true) {
|
|
1261
|
+
throw new AgentError(
|
|
1262
|
+
"AGENT_NOT_FOUND",
|
|
1263
|
+
`Working directory "${normalized}" is outside the project root "${projectRoot}". Set allow_external_cwd: true on the agent to permit this.`,
|
|
1264
|
+
{ details: { cwd: normalized, projectRoot } }
|
|
1265
|
+
);
|
|
1266
|
+
}
|
|
1267
|
+
return { cwd: normalized, outsideProject };
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
// src/agents/env.ts
|
|
1271
|
+
var BASELINE_KEYS = ["PATH", "HOME"];
|
|
1272
|
+
function resolveEnvPolicy(config) {
|
|
1273
|
+
if (config.envPolicy !== void 0) return config.envPolicy;
|
|
1274
|
+
if (config.inheritEnv === false) return "explicit";
|
|
1275
|
+
return "inherit";
|
|
1276
|
+
}
|
|
902
1277
|
function buildChildEnv(config) {
|
|
903
1278
|
const configured = config.env ?? {};
|
|
904
|
-
|
|
1279
|
+
const policy = resolveEnvPolicy(config);
|
|
1280
|
+
if (policy === "inherit") {
|
|
905
1281
|
return { ...process.env, ...configured };
|
|
906
1282
|
}
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
}
|
|
1283
|
+
const base = {};
|
|
1284
|
+
for (const key of BASELINE_KEYS) {
|
|
1285
|
+
const value = process.env[key];
|
|
1286
|
+
if (value !== void 0) base[key] = value;
|
|
1287
|
+
}
|
|
1288
|
+
switch (policy) {
|
|
1289
|
+
case "explicit":
|
|
1290
|
+
return { ...base, ...configured };
|
|
1291
|
+
case "allowlist": {
|
|
1292
|
+
const allowed = { ...base };
|
|
1293
|
+
for (const key of config.envAllowlist ?? []) {
|
|
1294
|
+
const value = process.env[key];
|
|
1295
|
+
if (value !== void 0) allowed[key] = value;
|
|
1296
|
+
}
|
|
1297
|
+
return { ...allowed, ...configured };
|
|
1298
|
+
}
|
|
1299
|
+
case "denylist": {
|
|
1300
|
+
const denied = new Set((config.envDenylist ?? []).map((k) => k.toUpperCase()));
|
|
1301
|
+
const filtered = {};
|
|
1302
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
1303
|
+
if (denied.has(key.toUpperCase())) continue;
|
|
1304
|
+
filtered[key] = value;
|
|
1305
|
+
}
|
|
1306
|
+
return { ...filtered, ...configured };
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
912
1309
|
}
|
|
1310
|
+
|
|
1311
|
+
// src/agents/process/index.ts
|
|
1312
|
+
var DEFAULT_TIMEOUT_MS2 = 6e5;
|
|
1313
|
+
var KILL_GRACE_MS = 5e3;
|
|
1314
|
+
var DEFAULT_MAX_OUTPUT_BYTES = 5 * 1024 * 1024;
|
|
913
1315
|
function composeTaskText(input) {
|
|
914
1316
|
const sections = [`# Objective
|
|
915
1317
|
${input.objective}`];
|
|
@@ -930,18 +1332,19 @@ ${input.instruction}`);
|
|
|
930
1332
|
return sections.join("\n\n");
|
|
931
1333
|
}
|
|
932
1334
|
function buildProtocolMessages(input, agentName) {
|
|
933
|
-
const
|
|
1335
|
+
const session = new ProtocolSession({
|
|
1336
|
+
requestId: `req-${input.taskId}`,
|
|
934
1337
|
taskId: input.taskId,
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
1338
|
+
agentId: agentName,
|
|
1339
|
+
...input.pipelineId !== void 0 ? { pipelineId: input.pipelineId } : {},
|
|
1340
|
+
...input.executionId !== void 0 ? { executionId: input.executionId } : {}
|
|
1341
|
+
});
|
|
1342
|
+
return session.taskMessages(
|
|
1343
|
+
input.objective,
|
|
1344
|
+
input.role,
|
|
1345
|
+
input.context,
|
|
1346
|
+
input.instruction
|
|
1347
|
+
);
|
|
945
1348
|
}
|
|
946
1349
|
var ProcessAgent = class {
|
|
947
1350
|
config;
|
|
@@ -970,40 +1373,73 @@ var ProcessAgent = class {
|
|
|
970
1373
|
}
|
|
971
1374
|
const mode = this.config.inputMode ?? "stdin";
|
|
972
1375
|
const timeoutMs = this.config.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
|
|
973
|
-
return new Promise((
|
|
1376
|
+
return new Promise((resolve2, reject) => {
|
|
1377
|
+
let cwd;
|
|
1378
|
+
try {
|
|
1379
|
+
cwd = validateWorkingDirectory(this.config.cwd, {
|
|
1380
|
+
allowExternal: this.config.allowExternalCwd
|
|
1381
|
+
})?.cwd;
|
|
1382
|
+
} catch (error) {
|
|
1383
|
+
reject(error);
|
|
1384
|
+
return;
|
|
1385
|
+
}
|
|
974
1386
|
const child = spawn(
|
|
975
1387
|
command,
|
|
976
1388
|
[...this.config.args ?? [], ...mode === "arg" ? [composeTaskText(input)] : []],
|
|
977
1389
|
{
|
|
978
|
-
cwd:
|
|
1390
|
+
...cwd !== void 0 ? { cwd } : {},
|
|
979
1391
|
env: buildChildEnv(this.config),
|
|
980
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
1392
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1393
|
+
// Own process group so the whole tree can be terminated together
|
|
1394
|
+
// (roadmap §8: no orphaned children).
|
|
1395
|
+
...process.platform === "win32" ? {} : { detached: true }
|
|
981
1396
|
}
|
|
982
1397
|
);
|
|
983
1398
|
this.running.set(input.taskId, child);
|
|
1399
|
+
GLOBAL_RUNNING.add(child);
|
|
1400
|
+
installSignalForwarding();
|
|
984
1401
|
let stdout = "";
|
|
985
1402
|
let stderr = "";
|
|
986
1403
|
let timedOut = false;
|
|
1404
|
+
let outputLimitExceeded = false;
|
|
987
1405
|
let settled = false;
|
|
1406
|
+
const maxOutputBytes = this.config.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;
|
|
988
1407
|
const timer = setTimeout(() => {
|
|
989
1408
|
timedOut = true;
|
|
990
|
-
this.log.warn(`Task ${input.taskId} exceeded ${timeoutMs}ms \u2014 killing process.`);
|
|
991
|
-
child
|
|
992
|
-
setTimeout(() => child
|
|
1409
|
+
this.log.warn(`Task ${input.taskId} exceeded ${timeoutMs}ms \u2014 killing process tree.`);
|
|
1410
|
+
killTree(child, "SIGTERM");
|
|
1411
|
+
setTimeout(() => killTree(child, "SIGKILL"), KILL_GRACE_MS).unref();
|
|
993
1412
|
}, timeoutMs);
|
|
994
1413
|
const settle = (finish) => {
|
|
995
1414
|
if (settled) return;
|
|
996
1415
|
settled = true;
|
|
997
1416
|
clearTimeout(timer);
|
|
998
1417
|
this.running.delete(input.taskId);
|
|
1418
|
+
GLOBAL_RUNNING.delete(child);
|
|
999
1419
|
finish();
|
|
1000
1420
|
};
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1421
|
+
const collect = (into, chunk) => {
|
|
1422
|
+
if (outputLimitExceeded) return;
|
|
1423
|
+
const text = sanitizeChunk(chunk);
|
|
1424
|
+
if (into === "stdout") {
|
|
1425
|
+
if (stdout.length + text.length > maxOutputBytes) {
|
|
1426
|
+
stdout = (stdout + text).slice(0, maxOutputBytes);
|
|
1427
|
+
outputLimitExceeded = true;
|
|
1428
|
+
this.log.warn(`Agent "${this.config.name}" exceeded the ${maxOutputBytes} byte output limit \u2014 killing it.`);
|
|
1429
|
+
killTree(child, "SIGKILL");
|
|
1430
|
+
return;
|
|
1431
|
+
}
|
|
1432
|
+
stdout += text;
|
|
1433
|
+
} else {
|
|
1434
|
+
if (stderr.length + text.length > maxOutputBytes) {
|
|
1435
|
+
stderr = (stderr + text).slice(0, maxOutputBytes);
|
|
1436
|
+
return;
|
|
1437
|
+
}
|
|
1438
|
+
stderr += text;
|
|
1439
|
+
}
|
|
1440
|
+
};
|
|
1441
|
+
child.stdout?.on("data", (chunk) => collect("stdout", chunk));
|
|
1442
|
+
child.stderr?.on("data", (chunk) => collect("stderr", chunk));
|
|
1007
1443
|
child.stdin?.on("error", () => {
|
|
1008
1444
|
});
|
|
1009
1445
|
if (mode !== "arg") {
|
|
@@ -1057,7 +1493,7 @@ var ProcessAgent = class {
|
|
|
1057
1493
|
const logs = stderr.trim().length > 0 ? stderr.split("\n") : void 0;
|
|
1058
1494
|
if (mode === "protocol") {
|
|
1059
1495
|
try {
|
|
1060
|
-
|
|
1496
|
+
resolve2(
|
|
1061
1497
|
interpretProtocolRun({
|
|
1062
1498
|
input,
|
|
1063
1499
|
agentName: this.config.name,
|
|
@@ -1072,14 +1508,14 @@ var ProcessAgent = class {
|
|
|
1072
1508
|
return;
|
|
1073
1509
|
}
|
|
1074
1510
|
const trimmed = stdout.trim();
|
|
1075
|
-
|
|
1511
|
+
resolve2({
|
|
1076
1512
|
taskId: input.taskId,
|
|
1077
|
-
status: code === 0 ? "completed" : "failed",
|
|
1513
|
+
status: code === 0 && !outputLimitExceeded ? "completed" : "failed",
|
|
1078
1514
|
summary: trimmed.length > 0 ? firstLine(trimmed) : void 0,
|
|
1079
1515
|
rawOutput: trimmed.length > 0 ? trimmed : void 0,
|
|
1080
1516
|
exitCode: code ?? void 0,
|
|
1081
1517
|
logs,
|
|
1082
|
-
error:
|
|
1518
|
+
...outputLimitExceeded ? { error: `Agent exceeded the ${maxOutputBytes} byte output limit and was terminated.` } : code === 0 ? {} : { error: describeExit(code, signal) }
|
|
1083
1519
|
});
|
|
1084
1520
|
});
|
|
1085
1521
|
});
|
|
@@ -1088,67 +1524,117 @@ var ProcessAgent = class {
|
|
|
1088
1524
|
/** Ask the agent to stop current work (SIGTERM). */
|
|
1089
1525
|
async cancel(taskId) {
|
|
1090
1526
|
const child = this.running.get(taskId);
|
|
1091
|
-
if (child) child
|
|
1527
|
+
if (child) killTree(child, "SIGTERM");
|
|
1092
1528
|
}
|
|
1093
1529
|
runningTaskIds() {
|
|
1094
1530
|
return [...this.running.keys()];
|
|
1095
1531
|
}
|
|
1096
1532
|
};
|
|
1097
|
-
function interpretProtocolRun({
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
const code = typeof payload?.code === "string" ? ` [${payload.code}]` : "";
|
|
1118
|
-
error = `Agent error${code}: ${detail}`;
|
|
1119
|
-
}
|
|
1533
|
+
function interpretProtocolRun({
|
|
1534
|
+
input,
|
|
1535
|
+
agentName,
|
|
1536
|
+
stdout,
|
|
1537
|
+
exitCode,
|
|
1538
|
+
logs,
|
|
1539
|
+
responder
|
|
1540
|
+
}) {
|
|
1541
|
+
const session = new ProtocolSession(
|
|
1542
|
+
{
|
|
1543
|
+
requestId: `req-${input.taskId}`,
|
|
1544
|
+
taskId: input.taskId,
|
|
1545
|
+
agentId: agentName,
|
|
1546
|
+
...input.pipelineId !== void 0 ? { pipelineId: input.pipelineId } : {},
|
|
1547
|
+
...input.executionId !== void 0 ? { executionId: input.executionId } : {}
|
|
1548
|
+
},
|
|
1549
|
+
{ ...responder !== void 0 ? { responder } : {} }
|
|
1550
|
+
);
|
|
1551
|
+
for (const result of session.decoder.push(stdout)) {
|
|
1552
|
+
session.handle(result);
|
|
1120
1553
|
}
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
`Agent "${agentName}" produced no protocol messages on stdout (input mode "protocol").`,
|
|
1124
|
-
{ details: { taskId: input.taskId, stdout: stdout.slice(0, 2e3) } }
|
|
1125
|
-
);
|
|
1554
|
+
for (const result of session.decoder.flush()) {
|
|
1555
|
+
session.handle(result);
|
|
1126
1556
|
}
|
|
1557
|
+
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
1558
|
const trimmed = stdout.trim();
|
|
1559
|
+
const protocolBlock = {
|
|
1560
|
+
protocol: session.descriptor?.protocol ?? PROTOCOL_V2,
|
|
1561
|
+
legacy: !session.sawV2 && session.descriptor === void 0,
|
|
1562
|
+
...session.descriptor !== void 0 ? { descriptor: session.descriptor } : {},
|
|
1563
|
+
questions: session.questions,
|
|
1564
|
+
violations: session.violations.map((v) => `${v.code}: ${v.message}`)
|
|
1565
|
+
};
|
|
1566
|
+
if (session.errorMessage !== void 0) {
|
|
1567
|
+
return {
|
|
1568
|
+
taskId: input.taskId,
|
|
1569
|
+
status: "failed",
|
|
1570
|
+
error: session.errorMessage,
|
|
1571
|
+
rawOutput: trimmed.length > 0 ? trimmed : void 0,
|
|
1572
|
+
exitCode: exitCode ?? void 0,
|
|
1573
|
+
logs,
|
|
1574
|
+
protocol: protocolBlock,
|
|
1575
|
+
...reports.length > 0 ? { reports } : {}
|
|
1576
|
+
};
|
|
1577
|
+
}
|
|
1578
|
+
if (session.descriptor === void 0 && session.violations.length > 0 && session.reports.length === 0 && session.completionSummary === void 0) {
|
|
1579
|
+
return {
|
|
1580
|
+
taskId: input.taskId,
|
|
1581
|
+
status: "failed",
|
|
1582
|
+
error: `agent produced no valid protocol messages: ${protocolBlock.violations.join("; ")}`,
|
|
1583
|
+
rawOutput: trimmed.length > 0 ? trimmed : void 0,
|
|
1584
|
+
exitCode: exitCode ?? void 0,
|
|
1585
|
+
logs,
|
|
1586
|
+
protocol: protocolBlock
|
|
1587
|
+
};
|
|
1588
|
+
}
|
|
1589
|
+
const completed = exitCode === 0 && session.completionSummary !== void 0;
|
|
1128
1590
|
return {
|
|
1129
1591
|
taskId: input.taskId,
|
|
1130
|
-
status:
|
|
1131
|
-
summary:
|
|
1592
|
+
status: completed || exitCode === 0 ? completed ? "completed" : "completed" : "failed",
|
|
1593
|
+
summary: session.completionSummary ?? reports[0]?.summary,
|
|
1132
1594
|
rawOutput: trimmed.length > 0 ? trimmed : void 0,
|
|
1133
1595
|
exitCode: exitCode ?? void 0,
|
|
1134
1596
|
logs,
|
|
1135
|
-
|
|
1136
|
-
|
|
1597
|
+
protocol: protocolBlock,
|
|
1598
|
+
...reports.length > 0 ? { reports } : {},
|
|
1599
|
+
...exitCode !== 0 ? { error: `Process exited with code ${exitCode ?? "unknown"}` } : {}
|
|
1137
1600
|
};
|
|
1138
1601
|
}
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1602
|
+
var GLOBAL_RUNNING = /* @__PURE__ */ new Set();
|
|
1603
|
+
var signalForwardingInstalled = false;
|
|
1604
|
+
function installSignalForwarding() {
|
|
1605
|
+
if (signalForwardingInstalled) return;
|
|
1606
|
+
signalForwardingInstalled = true;
|
|
1607
|
+
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
1608
|
+
process.on(signal, () => {
|
|
1609
|
+
for (const child of GLOBAL_RUNNING) {
|
|
1610
|
+
killTree(child, "SIGTERM");
|
|
1611
|
+
}
|
|
1612
|
+
});
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
function killTree(child, signal) {
|
|
1616
|
+
if (child.pid === void 0) return;
|
|
1617
|
+
if (process.platform !== "win32") {
|
|
1618
|
+
try {
|
|
1619
|
+
process.kill(-child.pid, signal);
|
|
1620
|
+
return;
|
|
1621
|
+
} catch {
|
|
1148
1622
|
}
|
|
1623
|
+
}
|
|
1624
|
+
try {
|
|
1625
|
+
child.kill(signal);
|
|
1149
1626
|
} catch {
|
|
1150
1627
|
}
|
|
1151
|
-
|
|
1628
|
+
}
|
|
1629
|
+
function describeExit(code, signal) {
|
|
1630
|
+
if (signal !== null) {
|
|
1631
|
+
return signal === "SIGKILL" ? "Process was killed (SIGKILL) \u2014 usually an external kill or OOM." : `Process terminated by signal ${signal}.`;
|
|
1632
|
+
}
|
|
1633
|
+
if (code === null) return "Process terminated without an exit code.";
|
|
1634
|
+
return `Process exited with code ${code}.`;
|
|
1635
|
+
}
|
|
1636
|
+
function sanitizeChunk(chunk) {
|
|
1637
|
+
return chunk.toString("utf8").replace(/\u0000/g, "");
|
|
1152
1638
|
}
|
|
1153
1639
|
function normalizeReport(payload, taskId, agentName, index) {
|
|
1154
1640
|
if (payload === null || typeof payload !== "object") return void 0;
|
|
@@ -1169,6 +1655,40 @@ function firstLine(text, maxLength = 200) {
|
|
|
1169
1655
|
return line.length > maxLength ? `${line.slice(0, maxLength)}\u2026` : line;
|
|
1170
1656
|
}
|
|
1171
1657
|
|
|
1658
|
+
// src/agents/adapters.ts
|
|
1659
|
+
var AgentAdapterRegistry = class {
|
|
1660
|
+
adapters = /* @__PURE__ */ new Map();
|
|
1661
|
+
register(adapter) {
|
|
1662
|
+
this.adapters.set(adapter.integration, adapter);
|
|
1663
|
+
}
|
|
1664
|
+
get(integration) {
|
|
1665
|
+
return this.adapters.get(integration);
|
|
1666
|
+
}
|
|
1667
|
+
list() {
|
|
1668
|
+
return [...this.adapters.values()];
|
|
1669
|
+
}
|
|
1670
|
+
create(config) {
|
|
1671
|
+
const adapter = this.get(config.integration);
|
|
1672
|
+
if (!adapter) {
|
|
1673
|
+
throw new AgentError(
|
|
1674
|
+
"AGENT_NOT_FOUND",
|
|
1675
|
+
`No agent adapter registered for integration "${config.integration}". Registered: ${[...this.adapters.keys()].join(", ") || "(none)"}.`
|
|
1676
|
+
);
|
|
1677
|
+
}
|
|
1678
|
+
return adapter.create(config);
|
|
1679
|
+
}
|
|
1680
|
+
};
|
|
1681
|
+
var ProcessAgentAdapter = class {
|
|
1682
|
+
constructor(factory) {
|
|
1683
|
+
this.factory = factory;
|
|
1684
|
+
}
|
|
1685
|
+
factory;
|
|
1686
|
+
integration = "process";
|
|
1687
|
+
create(config) {
|
|
1688
|
+
return this.factory(config);
|
|
1689
|
+
}
|
|
1690
|
+
};
|
|
1691
|
+
|
|
1172
1692
|
// src/agents/registry.ts
|
|
1173
1693
|
var AgentRegistry = class {
|
|
1174
1694
|
agents = /* @__PURE__ */ new Map();
|
|
@@ -1184,14 +1704,15 @@ var AgentRegistry = class {
|
|
|
1184
1704
|
};
|
|
1185
1705
|
|
|
1186
1706
|
// src/agents/factory.ts
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1707
|
+
var defaultAgentAdapters = new AgentAdapterRegistry();
|
|
1708
|
+
defaultAgentAdapters.register(new ProcessAgentAdapter((config) => new ProcessAgent(config)));
|
|
1709
|
+
function createAgent(config, adapters = defaultAgentAdapters) {
|
|
1710
|
+
const registered = adapters.get(config.integration);
|
|
1711
|
+
if (registered) return registered.create(config);
|
|
1191
1712
|
if (config.integration === "native") {
|
|
1192
1713
|
throw new OmniNodeError(
|
|
1193
1714
|
"NOT_IMPLEMENTED",
|
|
1194
|
-
`
|
|
1715
|
+
`No native adapter is registered for "${config.name}". Register an IAgentAdapter (createAgent(config, adapters)) or use input over stdin/stdout (type "cli").`
|
|
1195
1716
|
);
|
|
1196
1717
|
}
|
|
1197
1718
|
throw new OmniNodeError("NOT_IMPLEMENTED", `Agent integration "${config.integration}" has no adapter yet.`);
|
|
@@ -1234,7 +1755,7 @@ function registerAgentCommands(program) {
|
|
|
1234
1755
|
"--input-mode <mode>",
|
|
1235
1756
|
"How the task is delivered: stdin (prompt on stdin), arg (prompt as last argument) or protocol (\xA721 JSON-lines task protocol).",
|
|
1236
1757
|
"stdin"
|
|
1237
|
-
).option(
|
|
1758
|
+
).option("--env-policy <policy>", "Environment policy: inherit, allowlist, denylist or explicit.").option("--allow-env <name>", "Variable to forward under the allowlist policy (repeatable).", collectArgs).option("--deny-env <name>", "Variable to remove under the denylist policy (repeatable).", collectArgs).option("--max-output-bytes <n>", "Per-stream output budget in bytes (default 5 MiB).", (value) => Number(value)).option("--allow-external-cwd", "Allow a working directory outside the project root.").option(
|
|
1238
1759
|
"--timeout-ms <ms>",
|
|
1239
1760
|
"Per-task timeout in milliseconds.",
|
|
1240
1761
|
(value) => Number(value)
|
|
@@ -1249,7 +1770,12 @@ async function addAgent(options) {
|
|
|
1249
1770
|
...options.arg !== void 0 && options.arg.length > 0 ? { args: options.arg } : {},
|
|
1250
1771
|
...options.cwd !== void 0 ? { cwd: options.cwd } : {},
|
|
1251
1772
|
input_mode: options.inputMode,
|
|
1252
|
-
...options.timeoutMs !== void 0 ? { timeout_ms: options.timeoutMs } : {}
|
|
1773
|
+
...options.timeoutMs !== void 0 ? { timeout_ms: options.timeoutMs } : {},
|
|
1774
|
+
...options.envPolicy !== void 0 ? { env_policy: options.envPolicy } : {},
|
|
1775
|
+
...options.allowEnv !== void 0 && options.allowEnv.length > 0 ? { env_allowlist: options.allowEnv } : {},
|
|
1776
|
+
...options.denyEnv !== void 0 && options.denyEnv.length > 0 ? { env_denylist: options.denyEnv } : {},
|
|
1777
|
+
...options.maxOutputBytes !== void 0 ? { max_output_bytes: options.maxOutputBytes } : {},
|
|
1778
|
+
...options.allowExternalCwd !== void 0 ? { allow_external_cwd: options.allowExternalCwd } : {}
|
|
1253
1779
|
});
|
|
1254
1780
|
if (!candidate.success) {
|
|
1255
1781
|
const issues = candidate.error.issues.map((issue) => `${issue.path.join(".") || "(root)"}: ${issue.message}`).join("; ");
|
|
@@ -1653,7 +2179,7 @@ function classifyThrown(error) {
|
|
|
1653
2179
|
return "failed";
|
|
1654
2180
|
}
|
|
1655
2181
|
function sleep(ms) {
|
|
1656
|
-
return new Promise((
|
|
2182
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
1657
2183
|
}
|
|
1658
2184
|
|
|
1659
2185
|
// src/audit/index.ts
|