agentlas 1.0.65 → 1.0.67
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/CHANGELOG.md +94 -0
- package/README.md +12 -4
- package/bin/agentlas.cjs +354 -16
- package/engine/acp/server.cjs +34 -3
- package/engine/agentlas-api-agent.cjs +151 -120
- package/engine/agentlas-banner.cjs +6 -2
- package/engine/agentlas-capabilities.cjs +17 -10
- package/engine/agentlas-cloud-runtime.cjs +4 -5
- package/engine/agentlas-config.cjs +19 -54
- package/engine/agentlas-core-harness.cjs +59 -5
- package/engine/agentlas-desktop-loadout.cjs +17 -3
- package/engine/agentlas-evolution.cjs +75 -18
- package/engine/agentlas-experience-exchange.cjs +145 -14
- package/engine/agentlas-experience-intake.cjs +2 -2
- package/engine/agentlas-i18n.cjs +0 -2
- package/engine/agentlas-input.cjs +273 -20
- package/engine/agentlas-judgment.cjs +0 -0
- package/engine/agentlas-mcp-env.cjs +84 -23
- package/engine/agentlas-mcp-wrapper.cjs +36 -4
- package/engine/agentlas-memory-governance.cjs +2 -1
- package/engine/agentlas-memory-import.cjs +49 -12
- package/engine/agentlas-native-host.cjs +279 -44
- package/engine/agentlas-onboard.cjs +23 -1
- package/engine/agentlas-permissions.cjs +5 -1
- package/engine/agentlas-sqlite-policy.cjs +40 -2
- package/engine/agentlas-tasks.cjs +7 -0
- package/engine/agentlas-tools.cjs +37 -10
- package/engine/agentlas-ui.cjs +26 -4
- package/engine/agentlas-workforce.cjs +30 -5
- package/engine/agentlas-workload-routing.cjs +83 -10
- package/engine/agentlas.cjs +21 -4
- package/engine/agents/builder.cjs +35 -10
- package/engine/agents/files.cjs +81 -10
- package/engine/agents/import-local.cjs +10 -2
- package/engine/agents/registry.cjs +10 -3
- package/engine/agents/router.cjs +7 -7
- package/engine/agents/routes.cjs +31 -9
- package/engine/automation/daemon.cjs +154 -40
- package/engine/automation/launchd.cjs +395 -16
- package/engine/automation/schedule.cjs +90 -3
- package/engine/automation/store.cjs +163 -59
- package/engine/browser/cdp.cjs +190 -17
- package/engine/browser/vault.cjs +1 -0
- package/engine/cli-output.cjs +96 -30
- package/engine/cloud/auth.cjs +131 -13
- package/engine/cloud/hub-client.cjs +41 -16
- package/engine/cloud-assets/cas.cjs +18 -9
- package/engine/cloud-assets/commands.cjs +108 -30
- package/engine/cloud-assets/package.cjs +66 -3
- package/engine/cloud-assets/restore.cjs +12 -9
- package/engine/cloud-assets/state.cjs +609 -116
- package/engine/commands/acp.cjs +13 -2
- package/engine/commands/automation.cjs +75 -24
- package/engine/commands/billing.cjs +6 -1
- package/engine/commands/browser.cjs +61 -29
- package/engine/commands/build.cjs +72 -14
- package/engine/commands/call.cjs +1 -1
- package/engine/commands/cd.cjs +1 -1
- package/engine/commands/cloud.cjs +2 -1
- package/engine/commands/connect.cjs +38 -13
- package/engine/commands/creds.cjs +74 -29
- package/engine/commands/doctor.cjs +17 -1
- package/engine/commands/document.cjs +38 -10
- package/engine/commands/env.cjs +6 -1
- package/engine/commands/firm.cjs +42 -12
- package/engine/commands/graph.cjs +36 -32
- package/engine/commands/help.cjs +11 -1
- package/engine/commands/hep.cjs +1 -1
- package/engine/commands/import.cjs +5 -4
- package/engine/commands/index.cjs +1 -1
- package/engine/commands/install.cjs +6 -4
- package/engine/commands/list.cjs +56 -16
- package/engine/commands/login.cjs +5 -0
- package/engine/commands/logout.cjs +6 -1
- package/engine/commands/mcp.cjs +150 -37
- package/engine/commands/memory.cjs +3 -2
- package/engine/commands/multimodal.cjs +12 -0
- package/engine/commands/native.cjs +5 -1
- package/engine/commands/one.cjs +78 -33
- package/engine/commands/plugin.cjs +13 -2
- package/engine/commands/project.cjs +15 -12
- package/engine/commands/research.cjs +18 -7
- package/engine/commands/roles.cjs +104 -28
- package/engine/commands/route.cjs +2 -2
- package/engine/commands/run.cjs +42 -9
- package/engine/commands/search.cjs +23 -2
- package/engine/commands/setup.cjs +6 -1
- package/engine/commands/storm.cjs +44 -6
- package/engine/commands/swarm.cjs +47 -9
- package/engine/commands/telegram.cjs +6 -1
- package/engine/commands/uninstall.cjs +12 -6
- package/engine/commands/update.cjs +5 -0
- package/engine/commands/upload.cjs +2 -1
- package/engine/commands/usage.cjs +6 -1
- package/engine/commands/version.cjs +6 -1
- package/engine/commands/whoami.cjs +6 -1
- package/engine/commands/workforce.cjs +60 -16
- package/engine/core/capability-grants.cjs +7 -2
- package/engine/core/daemon-client.cjs +36 -7
- package/engine/core/db.cjs +10 -2
- package/engine/core/desktop-core-fetch.cjs +477 -77
- package/engine/core/schema-ensure.cjs +23 -11
- package/engine/experience/build.cjs +5 -1
- package/engine/experience/intents.cjs +35 -82
- package/engine/experience/runtime.cjs +7 -1
- package/engine/experience/variant.cjs +58 -4
- package/engine/firms/orchestrate.cjs +9 -6
- package/engine/graph/ask-model.cjs +22 -11
- package/engine/graph/interview.cjs +74 -23
- package/engine/graph/package.cjs +63 -8
- package/engine/hephaestus/local-core.cjs +18 -5
- package/engine/hephaestus/runtime.cjs +48 -43
- package/engine/hub/install.cjs +484 -63
- package/engine/hub/plugins.cjs +391 -29
- package/engine/mcp/consent.cjs +25 -17
- package/engine/mcp/contract.cjs +229 -24
- package/engine/mcp/inventory.cjs +46 -10
- package/engine/mcp/plan.cjs +10 -1
- package/engine/mcp/probe.cjs +24 -9
- package/engine/memory-cli/curate.cjs +74 -16
- package/engine/oberon/common.cjs +37 -12
- package/engine/oberon/manifest.cjs +25 -2
- package/engine/oberon/outputs.cjs +49 -19
- package/engine/oberon/render.cjs +665 -40
- package/engine/project/career-graph.cjs +15 -2
- package/engine/project/controller.cjs +133 -15
- package/engine/project/credentials.cjs +382 -34
- package/engine/project/env-file.cjs +93 -8
- package/engine/project/memory-context.cjs +11 -57
- package/engine/project/ontology.cjs +66 -25
- package/engine/project/paths.cjs +42 -8
- package/engine/project/seed.cjs +88 -51
- package/engine/project/state.cjs +41 -16
- package/engine/project/team.cjs +26 -1
- package/engine/runtime-refusal.cjs +4 -1
- package/engine/runtimes/acp-driver.cjs +56 -9
- package/engine/runtimes/auth-evidence.cjs +21 -2
- package/engine/runtimes/detect.cjs +28 -8
- package/engine/runtimes/kinds.cjs +12 -4
- package/engine/runtimes/overrides.cjs +4 -3
- package/engine/runtimes/resolve.cjs +8 -7
- package/engine/runtimes/roles.cjs +7 -5
- package/engine/sessions/apply-fences.cjs +396 -56
- package/engine/sessions/fences.cjs +111 -27
- package/engine/sessions/memory-turn.cjs +62 -3
- package/engine/sessions/orchestrator.cjs +11 -1
- package/engine/sessions/prompt.cjs +66 -9
- package/engine/sessions/session.cjs +70 -10
- package/engine/sessions/sink.cjs +78 -12
- package/engine/sessions/store.cjs +29 -7
- package/engine/storm/storm.cjs +26 -3
- package/engine/storm/swarm.cjs +31 -3
- package/engine/telegram/connect.cjs +185 -22
- package/engine/ui/commands-catalog.cjs +1 -1
- package/engine/ui/palette.cjs +12 -2
- package/engine/ui/repl.cjs +57 -9
- package/engine/ui/screens.cjs +2 -4
- package/engine/vendor/desktop-core.manifest.json +5 -5
- package/engine/workforce/capture.cjs +9 -27
- package/engine/workforce/deps.cjs +18 -6
- package/engine/workforce/local-core-transport.cjs +68 -5
- package/package.json +1 -1
- package/engine/commands/career-graph.cjs +0 -51
|
@@ -74,12 +74,18 @@ async function streamAnthropic({ apiKey, model, system, messages, permission, ui
|
|
|
74
74
|
if (toolDefs.length) body.tools = toolDefs;
|
|
75
75
|
|
|
76
76
|
const idle = idleAbort(signal, IDLE_MS);
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
77
|
+
let resp;
|
|
78
|
+
try {
|
|
79
|
+
resp = await fetch("https://api.anthropic.com/v1/messages", {
|
|
80
|
+
method: "POST",
|
|
81
|
+
headers: { "content-type": "application/json", "x-api-key": apiKey, "anthropic-version": "2023-06-01" },
|
|
82
|
+
signal: idle.signal,
|
|
83
|
+
body: JSON.stringify(body),
|
|
84
|
+
});
|
|
85
|
+
} catch (error) {
|
|
86
|
+
idle.clear();
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
83
89
|
if (!resp.ok) {
|
|
84
90
|
idle.clear();
|
|
85
91
|
throw new Error(`Anthropic ${resp.status}: ${(await resp.text().catch(() => "")).slice(0, 300)}`);
|
|
@@ -88,45 +94,49 @@ async function streamAnthropic({ apiKey, model, system, messages, permission, ui
|
|
|
88
94
|
const blocks = {}; // index → {type, text, name, id, inputJson}
|
|
89
95
|
let stopReason = null;
|
|
90
96
|
let usage = null;
|
|
91
|
-
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (ev.type === "content_block_start") {
|
|
101
|
-
const cb = ev.content_block || {};
|
|
102
|
-
blocks[ev.index] = { type: cb.type, text: "", name: cb.name, id: cb.id, inputJson: "" };
|
|
103
|
-
if (cb.type === "tool_use") ui.tool(cb.name, "");
|
|
104
|
-
else ui.streamStart();
|
|
105
|
-
} else if (ev.type === "content_block_delta") {
|
|
106
|
-
const b = blocks[ev.index] || (blocks[ev.index] = { type: "text", text: "", inputJson: "" });
|
|
107
|
-
if (ev.delta.type === "text_delta") {
|
|
108
|
-
b.text += ev.delta.text;
|
|
109
|
-
ui.streamDelta(ev.delta.text);
|
|
110
|
-
} else if (ev.delta.type === "input_json_delta") {
|
|
111
|
-
b.inputJson += ev.delta.partial_json || "";
|
|
97
|
+
try {
|
|
98
|
+
for await (const line of iterSse(resp, idle)) {
|
|
99
|
+
const data = sseData(line);
|
|
100
|
+
if (!data || data === "[DONE]") continue;
|
|
101
|
+
let ev;
|
|
102
|
+
try {
|
|
103
|
+
ev = JSON.parse(data);
|
|
104
|
+
} catch {
|
|
105
|
+
continue;
|
|
112
106
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
107
|
+
if (ev.type === "content_block_start") {
|
|
108
|
+
const cb = ev.content_block || {};
|
|
109
|
+
blocks[ev.index] = { type: cb.type, text: "", name: cb.name, id: cb.id, inputJson: "" };
|
|
110
|
+
if (cb.type === "tool_use") ui.tool(cb.name, "");
|
|
111
|
+
else ui.streamStart();
|
|
112
|
+
} else if (ev.type === "content_block_delta") {
|
|
113
|
+
const b = blocks[ev.index] || (blocks[ev.index] = { type: "text", text: "", inputJson: "" });
|
|
114
|
+
if (ev.delta.type === "text_delta") {
|
|
115
|
+
b.text += ev.delta.text;
|
|
116
|
+
ui.streamDelta(ev.delta.text);
|
|
117
|
+
} else if (ev.delta.type === "input_json_delta") {
|
|
118
|
+
b.inputJson += ev.delta.partial_json || "";
|
|
119
|
+
}
|
|
120
|
+
} else if (ev.type === "content_block_stop") {
|
|
121
|
+
const b = blocks[ev.index];
|
|
122
|
+
if (b && b.type === "tool_use") {
|
|
123
|
+
const arg = safeArg(b.inputJson);
|
|
124
|
+
if (arg) ui.info(ui.c.dim(" " + arg));
|
|
125
|
+
}
|
|
126
|
+
// 텍스트 블록 stop에서는 streamEnd를 호출하지 않는다 — 메모리 가드 중간 flush 방지(턴 끝에서만 flush).
|
|
127
|
+
} else if (ev.type === "message_start") {
|
|
128
|
+
const u = ev.message && ev.message.usage;
|
|
129
|
+
if (u) usage = { input_tokens: u.input_tokens || 0, output_tokens: u.output_tokens || 0 };
|
|
130
|
+
} else if (ev.type === "message_delta") {
|
|
131
|
+
if (ev.delta && ev.delta.stop_reason) stopReason = ev.delta.stop_reason;
|
|
132
|
+
if (ev.usage) usage = { input_tokens: (usage && usage.input_tokens) || 0, output_tokens: ev.usage.output_tokens };
|
|
118
133
|
}
|
|
119
|
-
// 텍스트 블록 stop에서는 streamEnd를 호출하지 않는다 — 메모리 가드 중간 flush 방지(턴 끝에서만 flush).
|
|
120
|
-
} else if (ev.type === "message_start") {
|
|
121
|
-
const u = ev.message && ev.message.usage;
|
|
122
|
-
if (u) usage = { input_tokens: u.input_tokens || 0, output_tokens: u.output_tokens || 0 };
|
|
123
|
-
} else if (ev.type === "message_delta") {
|
|
124
|
-
if (ev.delta && ev.delta.stop_reason) stopReason = ev.delta.stop_reason;
|
|
125
|
-
if (ev.usage) usage = { input_tokens: (usage && usage.input_tokens) || 0, output_tokens: ev.usage.output_tokens };
|
|
126
134
|
}
|
|
135
|
+
} finally {
|
|
136
|
+
// 본문 스트림이 중간에 끊겨도 Renderer의 열린 스트림 상태와 취소 리스너를 남기지 않는다.
|
|
137
|
+
ui.streamEnd();
|
|
138
|
+
idle.clear();
|
|
127
139
|
}
|
|
128
|
-
ui.streamEnd();
|
|
129
|
-
idle.clear();
|
|
130
140
|
|
|
131
141
|
// 어셈블
|
|
132
142
|
const ordered = Object.keys(blocks)
|
|
@@ -192,12 +202,18 @@ async function streamOpenAI({ apiKey, model, messages, permission, ui, signal, b
|
|
|
192
202
|
if (toolDefs.length) body.tools = toolDefs;
|
|
193
203
|
|
|
194
204
|
const idle = idleAbort(signal, IDLE_MS);
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
205
|
+
let resp;
|
|
206
|
+
try {
|
|
207
|
+
resp = await fetch(`${baseUrl || "https://api.openai.com/v1"}/chat/completions`, {
|
|
208
|
+
method: "POST",
|
|
209
|
+
headers: { "content-type": "application/json", authorization: `Bearer ${apiKey}` },
|
|
210
|
+
signal: idle.signal,
|
|
211
|
+
body: JSON.stringify(body),
|
|
212
|
+
});
|
|
213
|
+
} catch (error) {
|
|
214
|
+
idle.clear();
|
|
215
|
+
throw error;
|
|
216
|
+
}
|
|
201
217
|
if (!resp.ok) {
|
|
202
218
|
idle.clear();
|
|
203
219
|
throw new Error(`OpenAI ${resp.status}: ${(await resp.text().catch(() => "")).slice(0, 300)}`);
|
|
@@ -208,43 +224,46 @@ async function streamOpenAI({ apiKey, model, messages, permission, ui, signal, b
|
|
|
208
224
|
const toolCalls = {}; // index → {id, name, args}
|
|
209
225
|
let finish = null;
|
|
210
226
|
let usage = null;
|
|
211
|
-
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
if (ev.usage) usage = { input_tokens: ev.usage.prompt_tokens || 0, output_tokens: ev.usage.completion_tokens || 0 };
|
|
221
|
-
const choice = ev.choices && ev.choices[0];
|
|
222
|
-
if (!choice) continue;
|
|
223
|
-
const delta = choice.delta || {};
|
|
224
|
-
if (delta.content) {
|
|
225
|
-
if (!started) {
|
|
226
|
-
ui.streamStart();
|
|
227
|
-
started = true;
|
|
227
|
+
try {
|
|
228
|
+
for await (const line of iterSse(resp, idle)) {
|
|
229
|
+
const data = sseData(line);
|
|
230
|
+
if (!data || data === "[DONE]") continue;
|
|
231
|
+
let ev;
|
|
232
|
+
try {
|
|
233
|
+
ev = JSON.parse(data);
|
|
234
|
+
} catch {
|
|
235
|
+
continue;
|
|
228
236
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
237
|
+
if (ev.usage) usage = { input_tokens: ev.usage.prompt_tokens || 0, output_tokens: ev.usage.completion_tokens || 0 };
|
|
238
|
+
const choice = ev.choices && ev.choices[0];
|
|
239
|
+
if (!choice) continue;
|
|
240
|
+
const delta = choice.delta || {};
|
|
241
|
+
if (delta.content) {
|
|
242
|
+
if (!started) {
|
|
243
|
+
ui.streamStart();
|
|
244
|
+
started = true;
|
|
245
|
+
}
|
|
246
|
+
text += delta.content;
|
|
247
|
+
ui.streamDelta(delta.content);
|
|
248
|
+
}
|
|
249
|
+
if (Array.isArray(delta.tool_calls)) {
|
|
250
|
+
for (const tc of delta.tool_calls) {
|
|
251
|
+
const idx = tc.index ?? 0;
|
|
252
|
+
const cur = toolCalls[idx] || (toolCalls[idx] = { id: "", name: "", args: "" });
|
|
253
|
+
if (tc.id) cur.id = tc.id;
|
|
254
|
+
if (tc.function && tc.function.name) {
|
|
255
|
+
if (!cur.name) ui.tool(tc.function.name, "");
|
|
256
|
+
cur.name = tc.function.name;
|
|
257
|
+
}
|
|
258
|
+
if (tc.function && tc.function.arguments) cur.args += tc.function.arguments;
|
|
240
259
|
}
|
|
241
|
-
if (tc.function && tc.function.arguments) cur.args += tc.function.arguments;
|
|
242
260
|
}
|
|
261
|
+
if (choice.finish_reason) finish = choice.finish_reason;
|
|
243
262
|
}
|
|
244
|
-
|
|
263
|
+
} finally {
|
|
264
|
+
if (started) ui.streamEnd();
|
|
265
|
+
idle.clear();
|
|
245
266
|
}
|
|
246
|
-
if (started) ui.streamEnd();
|
|
247
|
-
idle.clear();
|
|
248
267
|
|
|
249
268
|
const calls = Object.keys(toolCalls)
|
|
250
269
|
.sort((a, b) => a - b)
|
|
@@ -327,26 +346,29 @@ async function runOllamaLoop(req) {
|
|
|
327
346
|
let text = "";
|
|
328
347
|
let started = false;
|
|
329
348
|
let toolCalls = [];
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
const msg = ev.message || {};
|
|
338
|
-
if (msg.content) {
|
|
339
|
-
if (!started) {
|
|
340
|
-
ui.streamStart();
|
|
341
|
-
started = true;
|
|
349
|
+
try {
|
|
350
|
+
for await (const line of iterSse(resp, idle)) {
|
|
351
|
+
let ev;
|
|
352
|
+
try {
|
|
353
|
+
ev = JSON.parse(line);
|
|
354
|
+
} catch {
|
|
355
|
+
continue;
|
|
342
356
|
}
|
|
343
|
-
|
|
344
|
-
|
|
357
|
+
const msg = ev.message || {};
|
|
358
|
+
if (msg.content) {
|
|
359
|
+
if (!started) {
|
|
360
|
+
ui.streamStart();
|
|
361
|
+
started = true;
|
|
362
|
+
}
|
|
363
|
+
text += msg.content;
|
|
364
|
+
ui.streamDelta(msg.content);
|
|
365
|
+
}
|
|
366
|
+
if (Array.isArray(msg.tool_calls)) toolCalls = toolCalls.concat(msg.tool_calls);
|
|
345
367
|
}
|
|
346
|
-
|
|
368
|
+
} finally {
|
|
369
|
+
if (started) ui.streamEnd();
|
|
370
|
+
idle.clear();
|
|
347
371
|
}
|
|
348
|
-
if (started) ui.streamEnd();
|
|
349
|
-
idle.clear();
|
|
350
372
|
finalText = text || finalText;
|
|
351
373
|
if (!toolCalls.length) break;
|
|
352
374
|
messages.push({ role: "assistant", content: text, tool_calls: toolCalls });
|
|
@@ -373,39 +395,48 @@ async function runGoogleChat(req) {
|
|
|
373
395
|
}
|
|
374
396
|
const url = `https://generativelanguage.googleapis.com/v1beta/models/${req.model}:streamGenerateContent?alt=sse&key=${encodeURIComponent(req.apiKey)}`;
|
|
375
397
|
const idle = idleAbort(req.signal, IDLE_MS);
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
398
|
+
let resp;
|
|
399
|
+
try {
|
|
400
|
+
resp = await fetch(url, {
|
|
401
|
+
method: "POST",
|
|
402
|
+
headers: { "content-type": "application/json" },
|
|
403
|
+
signal: idle.signal,
|
|
404
|
+
body: JSON.stringify({ systemInstruction: { parts: [{ text: req.system }] }, contents }),
|
|
405
|
+
});
|
|
406
|
+
} catch (error) {
|
|
407
|
+
idle.clear();
|
|
408
|
+
throw error;
|
|
409
|
+
}
|
|
382
410
|
if (!resp.ok) {
|
|
383
411
|
idle.clear();
|
|
384
412
|
throw new Error(`Google ${resp.status}: ${(await resp.text().catch(() => "")).slice(0, 300)}`);
|
|
385
413
|
}
|
|
386
414
|
let text = "";
|
|
387
415
|
let started = false;
|
|
388
|
-
|
|
389
|
-
const
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
if (
|
|
400
|
-
|
|
401
|
-
|
|
416
|
+
try {
|
|
417
|
+
for await (const line of iterSse(resp, idle)) {
|
|
418
|
+
const data = sseData(line);
|
|
419
|
+
if (!data || data === "[DONE]") continue;
|
|
420
|
+
let ev;
|
|
421
|
+
try {
|
|
422
|
+
ev = JSON.parse(data);
|
|
423
|
+
} catch {
|
|
424
|
+
continue;
|
|
425
|
+
}
|
|
426
|
+
const t = ev.candidates && ev.candidates[0] && ev.candidates[0].content && ev.candidates[0].content.parts && ev.candidates[0].content.parts[0] && ev.candidates[0].content.parts[0].text;
|
|
427
|
+
if (t) {
|
|
428
|
+
if (!started) {
|
|
429
|
+
ui.streamStart();
|
|
430
|
+
started = true;
|
|
431
|
+
}
|
|
432
|
+
text += t;
|
|
433
|
+
ui.streamDelta(t);
|
|
402
434
|
}
|
|
403
|
-
text += t;
|
|
404
|
-
ui.streamDelta(t);
|
|
405
435
|
}
|
|
436
|
+
} finally {
|
|
437
|
+
if (started) ui.streamEnd();
|
|
438
|
+
idle.clear();
|
|
406
439
|
}
|
|
407
|
-
if (started) ui.streamEnd();
|
|
408
|
-
idle.clear();
|
|
409
440
|
return { text };
|
|
410
441
|
}
|
|
411
442
|
|
|
@@ -163,8 +163,12 @@ function cardBottom(label, inner, c) {
|
|
|
163
163
|
function renderCompactSplash(ui, value, room) {
|
|
164
164
|
const c = ui.c;
|
|
165
165
|
const version = value.version ? " v" + value.version : "";
|
|
166
|
+
const title = truncateWidth(
|
|
167
|
+
WORDMARK_COMPACT + version + " · " + ui.t("banner.product"),
|
|
168
|
+
Math.max(1, room),
|
|
169
|
+
);
|
|
166
170
|
ui.line("");
|
|
167
|
-
ui.line(CARD_MARGIN + c.bold(c.emerald(
|
|
171
|
+
ui.line(CARD_MARGIN + c.bold(c.emerald(title)));
|
|
168
172
|
ui.line(CARD_MARGIN + c.text(truncateWidth(ui.t("banner.session", value.runtime, value.subject, value.permission), room)));
|
|
169
173
|
ui.line(CARD_MARGIN + c.faint(truncateWidth(ui.t("banner.location", value.cwd), room)));
|
|
170
174
|
ui.line("");
|
|
@@ -186,7 +190,7 @@ function renderBanner(ctx) {
|
|
|
186
190
|
const columns = ui.out.columns || 80;
|
|
187
191
|
const inner = Math.min(CARD_MAX_INNER, columns - visWidth(CARD_MARGIN) * 2 - 2);
|
|
188
192
|
if (inner < CARD_MIN_INNER) {
|
|
189
|
-
renderCompactSplash(ui, value, Math.max(
|
|
193
|
+
renderCompactSplash(ui, value, Math.max(1, columns - visWidth(CARD_MARGIN)));
|
|
190
194
|
return;
|
|
191
195
|
}
|
|
192
196
|
|
|
@@ -24,22 +24,27 @@ const RUNTIME_CAPS = {
|
|
|
24
24
|
upstage: { code: true, image: false, label: "solar" }, // Upstage Solar — Korean sovereign LLM (OpenAI-compatible)
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
const CLI_KINDS =
|
|
27
|
+
// 목록·별칭의 정본은 runtimes/kinds.cjs다. ACP 공용 러너가 추가된 뒤에도 이 파일이
|
|
28
|
+
// 옛 native 4종만 보아 grok/kimi/cursor를 API backend로 잘못 만들지 않게 한다.
|
|
29
|
+
const KINDS = require("./runtimes/kinds.cjs");
|
|
30
|
+
const CLI_KINDS = KINDS.CLI_KINDS;
|
|
31
|
+
const API_SPECS = new Set(KINDS.API_BACKEND_SPECS);
|
|
31
32
|
|
|
32
33
|
function capsFor(spec) {
|
|
33
|
-
|
|
34
|
+
const canonical = KINDS.canonicalRuntimeKind(spec);
|
|
35
|
+
return RUNTIME_CAPS[canonical] || { code: true, image: false, label: canonical || "?" };
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
// runtime object ⇄ spec string
|
|
37
39
|
function specOf(rt) {
|
|
38
40
|
if (!rt) return "";
|
|
39
|
-
return rt.mode === "cli" ? rt.kind : rt.backend;
|
|
41
|
+
return rt.mode === "cli" ? KINDS.canonicalRuntimeKind(rt.kind) : rt.backend;
|
|
40
42
|
}
|
|
41
43
|
function runtimeFromSpec(spec) {
|
|
42
|
-
|
|
44
|
+
const canonical = KINDS.canonicalRuntimeKind(spec);
|
|
45
|
+
if (CLI_KINDS.includes(canonical)) return { mode: "cli", kind: canonical };
|
|
46
|
+
if (API_SPECS.has(canonical)) return { mode: "api", backend: canonical, model: null };
|
|
47
|
+
return null;
|
|
43
48
|
}
|
|
44
49
|
|
|
45
50
|
// Image capability is judged only by the connected model from the complete
|
|
@@ -116,10 +121,12 @@ function clearImageJudgments() {
|
|
|
116
121
|
// Image agents route to an installed image-capable runtime; otherwise keep the session default.
|
|
117
122
|
function autoRuntimeFor(agent, { installedKinds, activeSpec }) {
|
|
118
123
|
if (needsImage(agent)) {
|
|
119
|
-
|
|
120
|
-
|
|
124
|
+
const canonicalActive = KINDS.canonicalRuntimeKind(activeSpec);
|
|
125
|
+
if (capsFor(canonicalActive).image) return canonicalActive;
|
|
126
|
+
const installed = new Set((installedKinds || []).map(KINDS.canonicalRuntimeKind));
|
|
127
|
+
for (const k of ["agy", "gemini", "codex"]) if (installed.has(k)) return k;
|
|
121
128
|
}
|
|
122
|
-
return activeSpec;
|
|
129
|
+
return KINDS.canonicalRuntimeKind(activeSpec);
|
|
123
130
|
}
|
|
124
131
|
|
|
125
132
|
// short capability badge for display
|
|
@@ -4,10 +4,9 @@ const fs = require("node:fs");
|
|
|
4
4
|
const path = require("node:path");
|
|
5
5
|
const os = require("node:os");
|
|
6
6
|
const crypto = require("node:crypto");
|
|
7
|
+
const { looksSecret, redactSecrets } = require("./agentlas-secret-patterns.cjs");
|
|
7
8
|
|
|
8
9
|
const TEXT_EXTS = new Set([".md", ".txt", ".json", ".jsonl", ".yaml", ".yml", ".toml", ".js", ".ts", ".tsx", ".cjs", ".mjs", ".sh"]);
|
|
9
|
-
const SECRET_RE = /(sk-(?:ant-)?[A-Za-z0-9_-]{20,}|gh[opsu]_[A-Za-z0-9_]{20,}|AKIA[0-9A-Z]{16}|AIza[0-9A-Za-z_-]{35}|-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----)/;
|
|
10
|
-
const SECRET_ASSIGN_RE = /\b(?:api[_-]?key|secret|password|token)\s*[:=]\s*["']?([A-Za-z0-9+/=_-]{20,})["']?/i;
|
|
11
10
|
const PROMPT_INJECTION_RE = /\b(ignore (?:all |previous |prior )?instructions|reveal (?:your )?system prompt|print hidden instructions)\b/i;
|
|
12
11
|
const DESTRUCTIVE_RE = /\b(rm\s+-rf\s+(?:\/|~)|curl\b[^\n]{0,240}\|\s*(?:sudo\s+)?(?:sh|bash|zsh)|mkfs\.|dd\s+if=\/dev\/)\b/i;
|
|
13
12
|
|
|
@@ -133,7 +132,7 @@ function buildManifest(root, options = {}) {
|
|
|
133
132
|
}
|
|
134
133
|
|
|
135
134
|
function redact(text) {
|
|
136
|
-
return text
|
|
135
|
+
return redactSecrets(text, "[REDACTED_SECRET]");
|
|
137
136
|
}
|
|
138
137
|
|
|
139
138
|
function scanFiles(files) {
|
|
@@ -146,13 +145,13 @@ function scanFiles(files) {
|
|
|
146
145
|
// `/token|secret/i` test blocked ordinary vocabulary — measured 2026-07-29,
|
|
147
146
|
// it gave the live `web-master` package a BLOCK verdict for its own design
|
|
148
147
|
// system (`token-architecture.md`, `reference-token-db.json`). Dropping it
|
|
149
|
-
// costs no coverage: the per-line
|
|
148
|
+
// costs no coverage: the shared per-line credential scan below reads every
|
|
150
149
|
// packaged file and is the check that actually looks at values.
|
|
151
150
|
if (/^\.env(\.|$)/.test(file.path.split("/").pop() || "") || /(?:^|\/)(secrets|credentials|cookies)\//i.test(file.path)) {
|
|
152
151
|
add("BLOCK", "credential-path", file, null, "Credential-like file path is excluded from Cloud package and public publish.");
|
|
153
152
|
}
|
|
154
153
|
file.content.split(/\r?\n/).forEach((line, index) => {
|
|
155
|
-
if (
|
|
154
|
+
if (looksSecret(line)) add("BLOCK", "secret-like-value", file, index + 1, "Secret-like value detected and redacted.");
|
|
156
155
|
if (PROMPT_INJECTION_RE.test(line)) add("WARN", "prompt-injection", file, index + 1, "Prompt-injection style instruction needs review.");
|
|
157
156
|
if (DESTRUCTIVE_RE.test(line)) add("WARN", "destructive-command", file, index + 1, "Destructive or remote shell command needs review before execution.");
|
|
158
157
|
});
|
|
@@ -8,8 +8,11 @@
|
|
|
8
8
|
*/
|
|
9
9
|
const fs = require("node:fs");
|
|
10
10
|
const path = require("node:path");
|
|
11
|
-
const
|
|
12
|
-
|
|
11
|
+
const {
|
|
12
|
+
readJsonFile,
|
|
13
|
+
writePrivateJsonAtomic,
|
|
14
|
+
withPrivateStateLock,
|
|
15
|
+
} = require("./mcp/contract.cjs");
|
|
13
16
|
|
|
14
17
|
function prefsPath(userDataDir) {
|
|
15
18
|
return path.join(userDataDir, "cli-prefs.json");
|
|
@@ -19,7 +22,7 @@ function backupPath(userDataDir) {
|
|
|
19
22
|
}
|
|
20
23
|
function readPrefsFile(file) {
|
|
21
24
|
try {
|
|
22
|
-
const value =
|
|
25
|
+
const { value } = readJsonFile(file, "CLI preferences");
|
|
23
26
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
24
27
|
} catch {
|
|
25
28
|
return null;
|
|
@@ -29,60 +32,15 @@ function loadPrefs(userDataDir) {
|
|
|
29
32
|
return readPrefsFile(prefsPath(userDataDir)) || readPrefsFile(backupPath(userDataDir)) || {};
|
|
30
33
|
}
|
|
31
34
|
|
|
32
|
-
function sleepSync(ms) {
|
|
33
|
-
try {
|
|
34
|
-
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
35
|
-
} catch {
|
|
36
|
-
const until = Date.now() + ms;
|
|
37
|
-
while (Date.now() < until) { /* old Node fallback */ }
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
35
|
function withPrefsLock(userDataDir, callback) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
fs.mkdirSync(lock, { mode: 0o700 });
|
|
47
|
-
break;
|
|
48
|
-
} catch (error) {
|
|
49
|
-
if (!error || error.code !== "EEXIST") throw error;
|
|
50
|
-
try {
|
|
51
|
-
if (Date.now() - fs.statSync(lock).mtimeMs > LOCK_STALE_MS) {
|
|
52
|
-
fs.rmdirSync(lock);
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
} catch {
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
58
|
-
if (Date.now() >= deadline) throw new Error("preferences lock timeout");
|
|
59
|
-
sleepSync(10);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
try {
|
|
63
|
-
return callback();
|
|
64
|
-
} finally {
|
|
65
|
-
try { fs.rmdirSync(lock); } catch { /* stale lock recovery handles interrupted writers */ }
|
|
66
|
-
}
|
|
36
|
+
return withPrivateStateLock(prefsPath(userDataDir), {
|
|
37
|
+
unsafe: "preferences lock is unsafe",
|
|
38
|
+
busy: "preferences lock timeout",
|
|
39
|
+
}, callback);
|
|
67
40
|
}
|
|
68
41
|
|
|
69
42
|
function atomicWrite(file, value) {
|
|
70
|
-
|
|
71
|
-
let fd;
|
|
72
|
-
try {
|
|
73
|
-
fd = fs.openSync(tmp, "wx", 0o600);
|
|
74
|
-
fs.writeFileSync(fd, JSON.stringify(value, null, 2), "utf8");
|
|
75
|
-
fs.fsyncSync(fd);
|
|
76
|
-
fs.closeSync(fd);
|
|
77
|
-
fd = null;
|
|
78
|
-
fs.renameSync(tmp, file);
|
|
79
|
-
try { fs.chmodSync(file, 0o600); } catch { /* win32 */ }
|
|
80
|
-
} finally {
|
|
81
|
-
if (fd != null) {
|
|
82
|
-
try { fs.closeSync(fd); } catch { /* ignore */ }
|
|
83
|
-
}
|
|
84
|
-
try { fs.unlinkSync(tmp); } catch { /* already renamed or never created */ }
|
|
85
|
-
}
|
|
43
|
+
writePrivateJsonAtomic(file, value);
|
|
86
44
|
}
|
|
87
45
|
|
|
88
46
|
function mergePrefs(base, patch) {
|
|
@@ -111,9 +69,16 @@ function updatePrefs(userDataDir, patch) {
|
|
|
111
69
|
fs.mkdirSync(userDataDir, { recursive: true });
|
|
112
70
|
return withPrefsLock(userDataDir, () => {
|
|
113
71
|
const file = prefsPath(userDataDir);
|
|
72
|
+
let currentStat = null;
|
|
73
|
+
try { currentStat = fs.lstatSync(file); } catch (error) {
|
|
74
|
+
if (!error || error.code !== "ENOENT") throw error;
|
|
75
|
+
}
|
|
76
|
+
if (currentStat && (!currentStat.isFile() || currentStat.isSymbolicLink() || currentStat.nlink !== 1)) {
|
|
77
|
+
throw new Error("CLI preferences path is unsafe");
|
|
78
|
+
}
|
|
114
79
|
const currentWasValid = Boolean(readPrefsFile(file));
|
|
115
80
|
const current = loadPrefs(userDataDir);
|
|
116
|
-
if (
|
|
81
|
+
if (currentStat && !currentWasValid) {
|
|
117
82
|
try { fs.renameSync(file, `${file}.corrupt-${Date.now()}-${process.pid}`); } catch { /* recover from backup */ }
|
|
118
83
|
}
|
|
119
84
|
const next = mergePrefs(current, patch);
|
|
@@ -12,6 +12,8 @@ const HARNESS_ID = "agentlas-core/stormbreaker-goal-ultracode";
|
|
|
12
12
|
const HARNESS_MODE = "stormbreaker-goal-ultracode";
|
|
13
13
|
const CONTEXT_MAP_MIN_CORE_VERSION = "1.1.86";
|
|
14
14
|
const CORE_MANIFEST_MAX_BYTES = 64 * 1024;
|
|
15
|
+
const CORE_CAPTURE_MAX_BYTES = 16 * 1024 * 1024;
|
|
16
|
+
const CORE_CAPTURE_TIMEOUT_MS = 120_000;
|
|
15
17
|
const CORE_RUNTIME_MARKERS = [
|
|
16
18
|
["agentlas_cloud", "__main__.py"],
|
|
17
19
|
["schemas", "workforce-work-order.schema.json"],
|
|
@@ -222,13 +224,55 @@ async function captureCoreJson(moduleName, args, opts = {}, explicitRoot) {
|
|
|
222
224
|
const child = spawnCoreModule(moduleName, args, { ...opts, stdio: ["ignore", "pipe", "pipe"] }, explicitRoot);
|
|
223
225
|
if (!child) throw new Error("Agentlas Core runtime or Python 3.9+ is unavailable.");
|
|
224
226
|
const result = await new Promise((resolve) => {
|
|
225
|
-
|
|
227
|
+
const stdoutChunks = [];
|
|
228
|
+
let stdoutBytes = 0;
|
|
226
229
|
let stderr = "";
|
|
227
|
-
|
|
230
|
+
let outputExceeded = false;
|
|
231
|
+
let timedOut = false;
|
|
232
|
+
let settled = false;
|
|
233
|
+
let forceKill = null;
|
|
234
|
+
const finish = (value) => {
|
|
235
|
+
if (settled) return;
|
|
236
|
+
settled = true;
|
|
237
|
+
clearTimeout(timeout);
|
|
238
|
+
if (forceKill) clearTimeout(forceKill);
|
|
239
|
+
resolve(value);
|
|
240
|
+
};
|
|
241
|
+
const terminate = () => {
|
|
242
|
+
try { child.kill("SIGTERM"); } catch { /* already gone */ }
|
|
243
|
+
if (!forceKill) {
|
|
244
|
+
forceKill = setTimeout(() => { try { child.kill("SIGKILL"); } catch { /* already gone */ } }, 250);
|
|
245
|
+
forceKill.unref?.();
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
const timeoutMs = Number.isFinite(Number(opts.timeout)) && Number(opts.timeout) > 0
|
|
249
|
+
? Number(opts.timeout)
|
|
250
|
+
: CORE_CAPTURE_TIMEOUT_MS;
|
|
251
|
+
const timeout = setTimeout(() => { timedOut = true; terminate(); }, timeoutMs);
|
|
252
|
+
timeout.unref?.();
|
|
253
|
+
child.stdout.on("data", (chunk) => {
|
|
254
|
+
if (outputExceeded) return;
|
|
255
|
+
const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
256
|
+
stdoutBytes += bytes.length;
|
|
257
|
+
if (stdoutBytes > CORE_CAPTURE_MAX_BYTES) {
|
|
258
|
+
outputExceeded = true;
|
|
259
|
+
terminate();
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
stdoutChunks.push(bytes);
|
|
263
|
+
});
|
|
228
264
|
child.stderr.on("data", (chunk) => { stderr = (stderr + chunk.toString()).slice(-4000); });
|
|
229
|
-
child.on("error", (error) =>
|
|
230
|
-
child.on("close", (code) =>
|
|
265
|
+
child.on("error", (error) => finish({ code: 1, stdout: "", stderr: String(error.message), outputExceeded, timedOut }));
|
|
266
|
+
child.on("close", (code) => finish({
|
|
267
|
+
code: code ?? 0,
|
|
268
|
+
stdout: outputExceeded ? "" : Buffer.concat(stdoutChunks, stdoutBytes).toString("utf8"),
|
|
269
|
+
stderr,
|
|
270
|
+
outputExceeded,
|
|
271
|
+
timedOut,
|
|
272
|
+
}));
|
|
231
273
|
});
|
|
274
|
+
if (result.outputExceeded) throw new Error(`Agentlas Core ${moduleName} response exceeded ${CORE_CAPTURE_MAX_BYTES} bytes.`);
|
|
275
|
+
if (result.timedOut) throw new Error(`Agentlas Core ${moduleName} timed out after ${Number(opts.timeout) > 0 ? Number(opts.timeout) : CORE_CAPTURE_TIMEOUT_MS}ms.`);
|
|
232
276
|
const json = parseJsonOutput(result.stdout);
|
|
233
277
|
if (result.code !== 0 || !json) {
|
|
234
278
|
throw new Error(result.stderr.trim() || `Agentlas Core ${moduleName} did not return JSON.`);
|
|
@@ -247,7 +291,8 @@ function captureCoreJsonSync(moduleName, args, opts = {}, explicitRoot) {
|
|
|
247
291
|
...opts,
|
|
248
292
|
encoding: "utf8",
|
|
249
293
|
windowsHide: true,
|
|
250
|
-
|
|
294
|
+
maxBuffer: CORE_CAPTURE_MAX_BYTES,
|
|
295
|
+
timeout: opts.timeout || CORE_CAPTURE_TIMEOUT_MS,
|
|
251
296
|
env: {
|
|
252
297
|
...(opts.env || process.env),
|
|
253
298
|
HEPHAESTUS_RUNTIME_ROOT: root,
|
|
@@ -256,6 +301,13 @@ function captureCoreJsonSync(moduleName, args, opts = {}, explicitRoot) {
|
|
|
256
301
|
},
|
|
257
302
|
},
|
|
258
303
|
);
|
|
304
|
+
if (result.error && result.error.code === "ENOBUFS") {
|
|
305
|
+
throw new Error(`Agentlas Core ${moduleName} response exceeded ${CORE_CAPTURE_MAX_BYTES} bytes.`);
|
|
306
|
+
}
|
|
307
|
+
if (result.error && result.error.code === "ETIMEDOUT") {
|
|
308
|
+
const timeoutMs = Number(opts.timeout) > 0 ? Number(opts.timeout) : CORE_CAPTURE_TIMEOUT_MS;
|
|
309
|
+
throw new Error(`Agentlas Core ${moduleName} timed out after ${timeoutMs}ms.`);
|
|
310
|
+
}
|
|
259
311
|
const json = parseJsonOutput(result.stdout);
|
|
260
312
|
if (result.status !== 0 || !json) {
|
|
261
313
|
throw new Error(String(result.stderr || result.error?.message || `Agentlas Core ${moduleName} did not return JSON.`).trim());
|
|
@@ -279,6 +331,8 @@ module.exports = {
|
|
|
279
331
|
HARNESS_MODE,
|
|
280
332
|
CONTEXT_MAP_MIN_CORE_VERSION,
|
|
281
333
|
CONTEXT_MAP_V3_RUNTIME_MARKERS,
|
|
334
|
+
CORE_CAPTURE_MAX_BYTES,
|
|
335
|
+
CORE_CAPTURE_TIMEOUT_MS,
|
|
282
336
|
PY_BOOTSTRAP,
|
|
283
337
|
readCoreRuntimeVersion,
|
|
284
338
|
resolveCoreRuntimeRootFromCandidates,
|