agentlas 1.0.64 → 1.0.66
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 +87 -0
- package/README.md +8 -6
- package/bin/agentlas.cjs +341 -16
- package/engine/acp/server.cjs +27 -1
- 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 +29 -5
- package/engine/agentlas-workload-routing.cjs +83 -10
- package/engine/agentlas.cjs +10 -3
- 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/bootstrap-schema.sql +4 -4
- package/engine/browser/cdp.cjs +315 -32
- package/engine/browser/vault.cjs +1 -0
- package/engine/cli-output.cjs +80 -28
- 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 +308 -113
- 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 +67 -34
- package/engine/commands/build.cjs +42 -12
- 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 +40 -12
- 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 +8 -2
- 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 +29 -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 +230 -61
- 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 +15 -5
- package/engine/mcp/plan.cjs +5 -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 +212 -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 +187 -24
- 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
package/engine/ui/repl.cjs
CHANGED
|
@@ -267,6 +267,7 @@ async function startRepl(ctx, opts = {}) {
|
|
|
267
267
|
});
|
|
268
268
|
// 진행 중인 비동기 슬래시 명령 — 종료가 이걸 잘라먹지 않도록 close 핸들러가 기다린다.
|
|
269
269
|
const pendingCommands = new Set();
|
|
270
|
+
const pendingShells = new Set();
|
|
270
271
|
const trackCommand = (promise) => {
|
|
271
272
|
pendingCommands.add(promise);
|
|
272
273
|
promise.then(() => pendingCommands.delete(promise), () => pendingCommands.delete(promise));
|
|
@@ -394,6 +395,15 @@ async function startRepl(ctx, opts = {}) {
|
|
|
394
395
|
prompt();
|
|
395
396
|
return;
|
|
396
397
|
}
|
|
398
|
+
if (pendingShells.size) {
|
|
399
|
+
const controller = [...pendingShells].at(-1);
|
|
400
|
+
controller.abort();
|
|
401
|
+
ui.ensureNl();
|
|
402
|
+
ui.line(ui.c.dim(en ? "(shell command interrupted)" : "(셸 명령 중단됨)"));
|
|
403
|
+
sigints = 0;
|
|
404
|
+
prompt();
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
397
407
|
sigints += 1;
|
|
398
408
|
if (sigints >= 2) { rl.close(); return; }
|
|
399
409
|
ui.line(ui.c.dim(en ? "(ctrl-c again to quit)" : "(한 번 더 ctrl-c 하면 종료)"));
|
|
@@ -487,7 +497,12 @@ async function startRepl(ctx, opts = {}) {
|
|
|
487
497
|
}
|
|
488
498
|
|
|
489
499
|
if (input.startsWith("!")) {
|
|
490
|
-
|
|
500
|
+
const controller = new AbortController();
|
|
501
|
+
pendingShells.add(controller);
|
|
502
|
+
const command = runShell(ctx, input.slice(1).trim(), permission, { signal: controller.signal })
|
|
503
|
+
.then(prompt, (e) => { recoverPresentation("shell action", e); prompt(); })
|
|
504
|
+
.finally(() => pendingShells.delete(controller));
|
|
505
|
+
trackCommand(command);
|
|
491
506
|
return;
|
|
492
507
|
}
|
|
493
508
|
|
|
@@ -519,6 +534,7 @@ async function startRepl(ctx, opts = {}) {
|
|
|
519
534
|
process.stdin.removeListener("keypress", onShortcutKey);
|
|
520
535
|
slashPalette.detach();
|
|
521
536
|
renderer.detach();
|
|
537
|
+
for (const controller of pendingShells) controller.abort();
|
|
522
538
|
/*
|
|
523
539
|
* 슬래시 명령은 비동기다. 예전에는 close 가 곧바로 resolve 해서 프로세스가 끝나 버렸고,
|
|
524
540
|
* `/search …` 직후 `/quit` 을 치면 그 명령이 출력 한 줄 없이 사라졌다(실측: 같은 입력을
|
|
@@ -554,7 +570,7 @@ async function startRepl(ctx, opts = {}) {
|
|
|
554
570
|
* env 객체는 절대 변형하지 않는다 (credential-env-regression 계약).
|
|
555
571
|
* - 출력 8MB 캡, 표시 전 시크릿 마스킹, 프로세스 그룹 종료.
|
|
556
572
|
*/
|
|
557
|
-
async function runShell(ctx, cmd, permission) {
|
|
573
|
+
async function runShell(ctx, cmd, permission, options = {}) {
|
|
558
574
|
const ui = ctx.uiInstance;
|
|
559
575
|
const en = ctx.lang === "en";
|
|
560
576
|
if (!cmd) return;
|
|
@@ -588,13 +604,49 @@ async function runShell(ctx, cmd, permission) {
|
|
|
588
604
|
});
|
|
589
605
|
let bytes = 0;
|
|
590
606
|
let capped = false;
|
|
607
|
+
let settled = false;
|
|
608
|
+
let forceKillTimer = null;
|
|
609
|
+
const terminate = (signal) => {
|
|
610
|
+
const pid = Number(child.pid);
|
|
611
|
+
if (grouped && Number.isInteger(pid) && pid > 1) {
|
|
612
|
+
try { process.kill(-pid, signal); return; } catch { /* fall through */ }
|
|
613
|
+
}
|
|
614
|
+
try { child.kill(signal); } catch { /* already gone */ }
|
|
615
|
+
};
|
|
616
|
+
const forceKillSoon = () => {
|
|
617
|
+
if (forceKillTimer) return;
|
|
618
|
+
forceKillTimer = setTimeout(() => terminate("SIGKILL"), 250);
|
|
619
|
+
forceKillTimer.unref?.();
|
|
620
|
+
};
|
|
621
|
+
const reapOnExit = () => terminate("SIGKILL");
|
|
622
|
+
process.once("exit", reapOnExit);
|
|
623
|
+
const abortHandler = () => {
|
|
624
|
+
terminate("SIGTERM");
|
|
625
|
+
forceKillSoon();
|
|
626
|
+
};
|
|
627
|
+
if (options.signal) {
|
|
628
|
+
if (options.signal.aborted) abortHandler();
|
|
629
|
+
else options.signal.addEventListener?.("abort", abortHandler, { once: true });
|
|
630
|
+
}
|
|
631
|
+
const finish = (code, failed = false) => {
|
|
632
|
+
if (settled) return;
|
|
633
|
+
settled = true;
|
|
634
|
+
if (forceKillTimer) clearTimeout(forceKillTimer);
|
|
635
|
+
process.removeListener("exit", reapOnExit);
|
|
636
|
+
if (options.signal) options.signal.removeEventListener?.("abort", abortHandler);
|
|
637
|
+
ui.streamEnd();
|
|
638
|
+
if (failed) ui.error();
|
|
639
|
+
else if (code !== 0 && !capped && !options.signal?.aborted) ui.line(ui.c.dim(`exit ${code}`));
|
|
640
|
+
resolve();
|
|
641
|
+
};
|
|
591
642
|
const onData = (d) => {
|
|
592
643
|
bytes += d.length;
|
|
593
644
|
if (bytes > maxBytes) {
|
|
594
645
|
if (!capped) {
|
|
595
646
|
capped = true;
|
|
596
647
|
ui.warn(en ? "(output capped at 8MB — command stopped)" : "(출력 8MB 초과 — 명령 중단)");
|
|
597
|
-
|
|
648
|
+
terminate("SIGTERM");
|
|
649
|
+
forceKillSoon();
|
|
598
650
|
}
|
|
599
651
|
return;
|
|
600
652
|
}
|
|
@@ -603,12 +655,8 @@ async function runShell(ctx, cmd, permission) {
|
|
|
603
655
|
ui.streamStart(true);
|
|
604
656
|
child.stdout.on("data", onData);
|
|
605
657
|
child.stderr.on("data", onData);
|
|
606
|
-
child.on("error", () =>
|
|
607
|
-
child.on("close", (code) =>
|
|
608
|
-
ui.streamEnd();
|
|
609
|
-
if (code !== 0 && !capped) ui.line(ui.c.dim(`exit ${code}`));
|
|
610
|
-
resolve();
|
|
611
|
-
});
|
|
658
|
+
child.on("error", () => finish(1, true));
|
|
659
|
+
child.on("close", (code) => finish(code));
|
|
612
660
|
});
|
|
613
661
|
}
|
|
614
662
|
|
package/engine/ui/screens.cjs
CHANGED
|
@@ -249,8 +249,8 @@ function settings(ui, db, en, ctx) {
|
|
|
249
249
|
? "change: /setup (wizard) · /runtime · /model · /effort · /permission · roles set · creds · env"
|
|
250
250
|
: "변경: /setup (마법사) · /runtime · /model · /effort · /permission · roles set · creds · env"));
|
|
251
251
|
ui.line(ui.c.dim(en
|
|
252
|
-
? "Desktop-only
|
|
253
|
-
: "
|
|
252
|
+
? "also available: agentlas multimodal · Desktop-only: theme, mobile pairing QR, auto-update"
|
|
253
|
+
: "추가 설정: agentlas multimodal · 데스크탑 전용: 테마, 모바일 페어링 QR, 자동 업데이트"));
|
|
254
254
|
}
|
|
255
255
|
|
|
256
256
|
/* ── /projects — 데스크탑 workspace + project/detail ── */
|
|
@@ -355,8 +355,6 @@ function firms(ui, db, en, ctx, arg) {
|
|
|
355
355
|
} else if (org) {
|
|
356
356
|
ui.line(ui.c.dim(en ? " roster is declared in the org chart only" : " 로스터가 조직도 선언에만 있습니다"));
|
|
357
357
|
}
|
|
358
|
-
const chats = count(db, "SELECT COUNT(*) n FROM chats WHERE firm_id = ? AND archived_at IS NULL", );
|
|
359
|
-
void chats;
|
|
360
358
|
ui.line("");
|
|
361
359
|
ui.line(ui.c.dim(en ? `run: /firm ${f.slug} "<task>"` : `실행: /firm ${f.slug} "<할 일>"`));
|
|
362
360
|
return;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "
|
|
3
|
-
"url": "https://github.com/agentlas-ai/agentlas-terminal/releases/download/desktop-core-
|
|
4
|
-
"sha256": "
|
|
5
|
-
"sizeBytes":
|
|
6
|
-
"writtenAt": "2026-08-
|
|
2
|
+
"version": "16",
|
|
3
|
+
"url": "https://github.com/agentlas-ai/agentlas-terminal/releases/download/desktop-core-v16/desktop-core.tar.gz",
|
|
4
|
+
"sha256": "563370a5cb53afd0e6688e7b5fe30e5bc0e9022f70a9239f5e95fb1da6309868",
|
|
5
|
+
"sizeBytes": 18173415,
|
|
6
|
+
"writtenAt": "2026-08-30T02:34:26.615Z"
|
|
7
7
|
}
|
|
@@ -23,6 +23,7 @@ const os = require("node:os");
|
|
|
23
23
|
const path = require("node:path");
|
|
24
24
|
const { spawn } = require("node:child_process");
|
|
25
25
|
const { dbPath, userDataDir } = require("../core/paths.cjs");
|
|
26
|
+
const { runCwd, projectCwd } = require("../project/paths.cjs");
|
|
26
27
|
|
|
27
28
|
// 캡처 드라이버가 검증된 런타임만. 정본(runtimes/kinds.cjs)의 RUNTIME_BIN에는 kimi/grok/cursor도
|
|
28
29
|
// 있지만 buildArgs/텍스트 추출 계약이 없으므로 캡처 검증 파생본만 쓴다 — 새 kind 를
|
|
@@ -726,8 +727,12 @@ async function runApi(backend, model, system, prompt, options) {
|
|
|
726
727
|
model = model || DEFAULT_API_MODEL[backend];
|
|
727
728
|
const fetchImpl = options.fetch || globalThis.fetch;
|
|
728
729
|
if (typeof fetchImpl !== "function") throw new Error("fetch is unavailable in this runtime (run through the app runtime).");
|
|
730
|
+
const request = (url, init) => fetchImpl(
|
|
731
|
+
url,
|
|
732
|
+
options.signal ? { ...init, signal: options.signal } : init,
|
|
733
|
+
);
|
|
729
734
|
if (backend === "ollama") {
|
|
730
|
-
const resp = await
|
|
735
|
+
const resp = await request("http://127.0.0.1:11434/api/chat", {
|
|
731
736
|
method: "POST",
|
|
732
737
|
headers: { "content-type": "application/json" },
|
|
733
738
|
body: JSON.stringify({ model, stream: false, messages: [{ role: "system", content: system }, { role: "user", content: prompt }] }),
|
|
@@ -760,7 +765,7 @@ async function runApi(backend, model, system, prompt, options) {
|
|
|
760
765
|
const systemField = backend === "anthropic"
|
|
761
766
|
? [{ type: "text", text: system, cache_control: { type: "ephemeral" } }]
|
|
762
767
|
: system;
|
|
763
|
-
const resp = await
|
|
768
|
+
const resp = await request(`${base}/v1/messages`, {
|
|
764
769
|
method: "POST",
|
|
765
770
|
headers: { "content-type": "application/json", ...authHeaders, "anthropic-version": "2023-06-01" },
|
|
766
771
|
body: JSON.stringify({ model, max_tokens: 4096, system: systemField, messages: [{ role: "user", content: prompt }] }),
|
|
@@ -781,7 +786,7 @@ async function runApi(backend, model, system, prompt, options) {
|
|
|
781
786
|
: readCustomApiBaseUrl())
|
|
782
787
|
: "https://api.openai.com/v1";
|
|
783
788
|
const label = backend === "custom" ? "Custom API" : backend === "upstage" ? "Upstage" : "OpenAI";
|
|
784
|
-
const resp = await
|
|
789
|
+
const resp = await request(`${base}/chat/completions`, {
|
|
785
790
|
method: "POST",
|
|
786
791
|
headers: { "content-type": "application/json", authorization: "Bearer " + key },
|
|
787
792
|
body: JSON.stringify({ model, messages: [{ role: "system", content: system }, { role: "user", content: prompt }] }),
|
|
@@ -795,7 +800,7 @@ async function runApi(backend, model, system, prompt, options) {
|
|
|
795
800
|
}
|
|
796
801
|
if (backend === "google") {
|
|
797
802
|
const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${encodeURIComponent(key)}`;
|
|
798
|
-
const resp = await
|
|
803
|
+
const resp = await request(url, {
|
|
799
804
|
method: "POST",
|
|
800
805
|
headers: { "content-type": "application/json" },
|
|
801
806
|
body: JSON.stringify({ systemInstruction: { parts: [{ text: system }] }, contents: [{ role: "user", parts: [{ text: prompt }] }] }),
|
|
@@ -811,29 +816,6 @@ async function runApi(backend, model, system, prompt, options) {
|
|
|
811
816
|
throw new Error("Unsupported backend: " + backend);
|
|
812
817
|
}
|
|
813
818
|
|
|
814
|
-
// ── 작업 폴더 규칙 (v1 runCwd/projectCwd 포팅) ─────────────────────────
|
|
815
|
-
function runCwd() {
|
|
816
|
-
const dir = path.join(userDataDir(), "agent-cwd");
|
|
817
|
-
try {
|
|
818
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
819
|
-
return dir;
|
|
820
|
-
} catch {
|
|
821
|
-
return os.homedir();
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
// 에이전트가 실행될 작업 폴더 = 사용자가 명령을 친 현재 디렉터리(= 대상 프로젝트).
|
|
826
|
-
// 단, home/userData/agent-cwd 같은 "프로젝트 아님" 위치면 안전한 전용 폴더로 폴백.
|
|
827
|
-
function projectCwd() {
|
|
828
|
-
try {
|
|
829
|
-
const cwd = process.cwd();
|
|
830
|
-
if (!cwd || cwd === os.homedir() || cwd === userDataDir() || cwd === runCwd()) return runCwd();
|
|
831
|
-
return cwd;
|
|
832
|
-
} catch {
|
|
833
|
-
return runCwd();
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
|
|
837
819
|
// ── 자식 env 빌더 (v1 buildChildEnvCli 포팅) ───────────────────────────
|
|
838
820
|
function parseDotEnv(text) {
|
|
839
821
|
const out = {};
|
|
@@ -32,7 +32,8 @@ const path = require("node:path");
|
|
|
32
32
|
const { userDataDir } = require("../core/paths.cjs");
|
|
33
33
|
const hubClient = require("../cloud/hub-client.cjs");
|
|
34
34
|
const detect = require("../runtimes/detect.cjs");
|
|
35
|
-
const {
|
|
35
|
+
const { canonicalRuntimeKind } = require("../runtimes/kinds.cjs");
|
|
36
|
+
const { pickRoleFromPool } = require("../runtimes/roles.cjs");
|
|
36
37
|
const capture = require("./capture.cjs");
|
|
37
38
|
|
|
38
39
|
// ── 런타임 해석 (v1 resolveRuntime의 워크포스 어댑터) ─────────────────────
|
|
@@ -49,6 +50,8 @@ function runtimeFromSelection(selection) {
|
|
|
49
50
|
efforts: [],
|
|
50
51
|
source: selection.sourceLayer || selection.source || null,
|
|
51
52
|
role: selection.role || null,
|
|
53
|
+
inherit: Boolean(selection.inherit),
|
|
54
|
+
skipped: Array.isArray(selection.skipped) ? selection.skipped : [],
|
|
52
55
|
};
|
|
53
56
|
if (capture.RUNTIME_BIN[selection.kind]) {
|
|
54
57
|
return { mode: "cli", kind: selection.kind, ...common };
|
|
@@ -63,10 +66,10 @@ function runtimeFromSelection(selection) {
|
|
|
63
66
|
}
|
|
64
67
|
|
|
65
68
|
function legacyWorkforceRuntime(db, override) {
|
|
66
|
-
let selectedOverride = override;
|
|
69
|
+
let selectedOverride = canonicalRuntimeKind(override);
|
|
67
70
|
if (!selectedOverride) {
|
|
68
71
|
try {
|
|
69
|
-
const saved = require("../agentlas-config.cjs").loadPrefs(userDataDir()).runtime;
|
|
72
|
+
const saved = canonicalRuntimeKind(require("../agentlas-config.cjs").loadPrefs(userDataDir()).runtime);
|
|
70
73
|
if (
|
|
71
74
|
saved &&
|
|
72
75
|
saved !== "auto" &&
|
|
@@ -81,7 +84,7 @@ function legacyWorkforceRuntime(db, override) {
|
|
|
81
84
|
const active = ar
|
|
82
85
|
? runtimeFromSelection({
|
|
83
86
|
role: "orchestrator",
|
|
84
|
-
kind: ar.kind,
|
|
87
|
+
kind: canonicalRuntimeKind(ar.kind),
|
|
85
88
|
backend: ar.backend,
|
|
86
89
|
source: ar.source,
|
|
87
90
|
model: ar.model,
|
|
@@ -128,8 +131,17 @@ function resolveWorkforceRuntime(db, override) {
|
|
|
128
131
|
},
|
|
129
132
|
};
|
|
130
133
|
}
|
|
131
|
-
const
|
|
132
|
-
|
|
134
|
+
const isAvailable = (selection) => {
|
|
135
|
+
const kind = canonicalRuntimeKind(selection && selection.kind);
|
|
136
|
+
if (capture.RUNTIME_BIN[kind]) return Boolean(capture.which(capture.RUNTIME_BIN[kind]));
|
|
137
|
+
return kind === "byok" && Boolean(selection.backend) || kind === "ollama";
|
|
138
|
+
};
|
|
139
|
+
const pickAvailableRole = (role) => {
|
|
140
|
+
const selected = pickRoleFromPool(db, role, isAvailable);
|
|
141
|
+
return selected && isAvailable(selected) ? runtimeFromSelection(selected) : null;
|
|
142
|
+
};
|
|
143
|
+
const orchestrator = pickAvailableRole("orchestrator");
|
|
144
|
+
const worker = pickAvailableRole("worker");
|
|
133
145
|
if (!orchestrator || !worker) {
|
|
134
146
|
const error = new Error("Dashboard orchestrator and worker priorities are required for Workforce execution.");
|
|
135
147
|
error.code = "no_runtime_role_priority";
|
|
@@ -26,6 +26,10 @@ const crypto = require("node:crypto");
|
|
|
26
26
|
const { createLocalCoreClient } = require("../hephaestus/local-core.cjs");
|
|
27
27
|
|
|
28
28
|
const SOURCE_SCOPES = new Set(["network", "local", "cloud", "hub"]);
|
|
29
|
+
const MAX_MAPPED_NODES = 100_000;
|
|
30
|
+
const MAX_MAPPED_DEPTH = 64;
|
|
31
|
+
const MAX_MAPPED_STRING_BYTES = 16 * 1024 * 1024;
|
|
32
|
+
const FORBIDDEN_OBJECT_KEYS = new Set(["__proto__", "prototype", "constructor"]);
|
|
29
33
|
|
|
30
34
|
/*
|
|
31
35
|
* ── id 정규화 (실측 2026-08-05 라이브 런 실패의 수리) ──
|
|
@@ -87,18 +91,58 @@ function boundaryIssues(error) {
|
|
|
87
91
|
}
|
|
88
92
|
|
|
89
93
|
/** 깊은 순회로 문자열 값을 정확 일치 치환한 사본을 만든다. */
|
|
90
|
-
function mapDeep(value, map) {
|
|
91
|
-
|
|
92
|
-
if (
|
|
93
|
-
|
|
94
|
+
function mapDeep(value, map, state = { nodes: 0, stringBytes: 0 }, depth = 0) {
|
|
95
|
+
state.nodes += 1;
|
|
96
|
+
if (state.nodes > MAX_MAPPED_NODES || depth > MAX_MAPPED_DEPTH) {
|
|
97
|
+
const error = new Error("local Core payload exceeds the safe structure limit");
|
|
98
|
+
error.code = "local_core_payload_invalid";
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
if (typeof value === "string") {
|
|
102
|
+
state.stringBytes += Buffer.byteLength(value, "utf8");
|
|
103
|
+
if (state.stringBytes > MAX_MAPPED_STRING_BYTES) {
|
|
104
|
+
const error = new Error("local Core payload exceeds the safe text limit");
|
|
105
|
+
error.code = "local_core_payload_invalid";
|
|
106
|
+
throw error;
|
|
107
|
+
}
|
|
108
|
+
return map.get(value) || value;
|
|
109
|
+
}
|
|
110
|
+
if (Array.isArray(value)) return value.map((item) => mapDeep(item, map, state, depth + 1));
|
|
94
111
|
if (value && typeof value === "object") {
|
|
112
|
+
const proto = Object.getPrototypeOf(value);
|
|
113
|
+
if (proto !== Object.prototype && proto !== null) {
|
|
114
|
+
const error = new Error("local Core payload contains a non-plain object");
|
|
115
|
+
error.code = "local_core_payload_invalid";
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
95
118
|
const out = {};
|
|
96
|
-
for (const [key, item] of Object.entries(value))
|
|
119
|
+
for (const [key, item] of Object.entries(value)) {
|
|
120
|
+
if (FORBIDDEN_OBJECT_KEYS.has(key)) {
|
|
121
|
+
const error = new Error(`local Core payload contains a forbidden key: ${key}`);
|
|
122
|
+
error.code = "local_core_payload_invalid";
|
|
123
|
+
throw error;
|
|
124
|
+
}
|
|
125
|
+
out[key] = mapDeep(item, map, state, depth + 1);
|
|
126
|
+
}
|
|
97
127
|
return out;
|
|
98
128
|
}
|
|
99
129
|
return value;
|
|
100
130
|
}
|
|
101
131
|
|
|
132
|
+
function stablePayloadDigest(value) {
|
|
133
|
+
const mapped = mapDeep(value, new Map());
|
|
134
|
+
const canonical = (item) => {
|
|
135
|
+
if (Array.isArray(item)) return `[${item.map(canonical).join(",")}]`;
|
|
136
|
+
if (item && typeof item === "object") {
|
|
137
|
+
return `{${Object.keys(item).sort().filter((key) => item[key] !== undefined)
|
|
138
|
+
.map((key) => `${JSON.stringify(key)}:${canonical(item[key])}`).join(",")}}`;
|
|
139
|
+
}
|
|
140
|
+
const encoded = JSON.stringify(item);
|
|
141
|
+
return encoded === undefined ? "null" : encoded;
|
|
142
|
+
};
|
|
143
|
+
return sha256hex(canonical(mapped));
|
|
144
|
+
}
|
|
145
|
+
|
|
102
146
|
function createLocalCoreHubTool({ sourceScope, projectDir, cwd, client } = {}) {
|
|
103
147
|
if (!SOURCE_SCOPES.has(sourceScope)) {
|
|
104
148
|
const error = new Error(`sourceScope must be network|local|cloud|hub, got: ${String(sourceScope)}`);
|
|
@@ -123,6 +167,8 @@ function createLocalCoreHubTool({ sourceScope, projectDir, cwd, client } = {}) {
|
|
|
123
167
|
// 반송해야 한다 — 반응형 reasonCode 수리가 있었으면 루프의 selection 을 다시
|
|
124
168
|
// 변환한 것과 다르다(exact binding).
|
|
125
169
|
let lastCoreSelection = null;
|
|
170
|
+
let lastWorkOrderDigest = null;
|
|
171
|
+
let lastSelectionDigest = null;
|
|
126
172
|
|
|
127
173
|
const invalid = (message) => {
|
|
128
174
|
const error = new Error(message);
|
|
@@ -135,6 +181,9 @@ function createLocalCoreHubTool({ sourceScope, projectDir, cwd, client } = {}) {
|
|
|
135
181
|
maps = buildIdMaps(args.workOrder);
|
|
136
182
|
coreCandidateSet = null;
|
|
137
183
|
lastValidationEnvelope = null;
|
|
184
|
+
lastCoreSelection = null;
|
|
185
|
+
lastSelectionDigest = null;
|
|
186
|
+
lastWorkOrderDigest = stablePayloadDigest(args.workOrder);
|
|
138
187
|
// Current Core keeps the full dossier in its pinned session and returns a
|
|
139
188
|
// numbered decision menu. Do not request the legacy full-echo form.
|
|
140
189
|
let envelope;
|
|
@@ -171,6 +220,11 @@ function createLocalCoreHubTool({ sourceScope, projectDir, cwd, client } = {}) {
|
|
|
171
220
|
error.code = "local_core_lineage_missing";
|
|
172
221
|
throw error;
|
|
173
222
|
}
|
|
223
|
+
if (stablePayloadDigest(args.workOrder) !== lastWorkOrderDigest) {
|
|
224
|
+
const error = new Error("validate_selection workOrder differs from the searched local Core lineage");
|
|
225
|
+
error.code = "local_core_lineage_mismatch";
|
|
226
|
+
throw error;
|
|
227
|
+
}
|
|
174
228
|
// The host-authored selection must point at the exact summary menu just
|
|
175
229
|
// returned. Core independently reloads and verifies the full pinned set.
|
|
176
230
|
if (args.selection?.candidateSetDigest !== coreCandidateSet.candidateSetDigest) {
|
|
@@ -208,6 +262,7 @@ function createLocalCoreHubTool({ sourceScope, projectDir, cwd, client } = {}) {
|
|
|
208
262
|
if (!envelope || typeof envelope.selectionValidation !== "object") throw invalid("local Core validation returned no selectionValidation receipt");
|
|
209
263
|
lastValidationEnvelope = envelope;
|
|
210
264
|
lastCoreSelection = coreSelection;
|
|
265
|
+
lastSelectionDigest = stablePayloadDigest(args.selection);
|
|
211
266
|
return mapDeep(envelope.selectionValidation, maps.reverse);
|
|
212
267
|
}
|
|
213
268
|
if (name === "workforce.prepare_execution") {
|
|
@@ -216,6 +271,14 @@ function createLocalCoreHubTool({ sourceScope, projectDir, cwd, client } = {}) {
|
|
|
216
271
|
error.code = "local_core_lineage_missing";
|
|
217
272
|
throw error;
|
|
218
273
|
}
|
|
274
|
+
if (
|
|
275
|
+
stablePayloadDigest(args.workOrder) !== lastWorkOrderDigest ||
|
|
276
|
+
stablePayloadDigest(args.selection) !== lastSelectionDigest
|
|
277
|
+
) {
|
|
278
|
+
const error = new Error("prepare_execution input differs from the validated local Core lineage");
|
|
279
|
+
error.code = "local_core_lineage_mismatch";
|
|
280
|
+
throw error;
|
|
281
|
+
}
|
|
219
282
|
const envelope = await core.call(name, {
|
|
220
283
|
workOrder: mapDeep(args.workOrder, maps.forward),
|
|
221
284
|
// validate 가 수락한 정확한 본 — 반응형 reasonCode 수리를 반영한다.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentlas",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.66",
|
|
4
4
|
"description": "Agentlas project terminal — run project controllers and task-scoped agent teams from the terminal. Standalone: no desktop app process required.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"agentlas": "bin/agentlas.cjs"
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/*
|
|
3
|
-
* career-graph — 커리어 그래프 상태/소스 등록 + 인덱스 실행 위임 (v1 cmdCareerGraph 포팅).
|
|
4
|
-
*
|
|
5
|
-
* ingest|query|verify|trace|public-card 는 파생 인덱스 실행이며 Agentlas OS /
|
|
6
|
-
* Hephaestus 런타임(career_graph 파이썬 모듈)이 소유한다 — 런타임이 있으면
|
|
7
|
-
* 그대로 위임 실행하고, 없으면 정직 정지(대신 계산하는 척 금지).
|
|
8
|
-
*/
|
|
9
|
-
const { runCareerGraphCli } = require("../project/career-graph.cjs");
|
|
10
|
-
const { resolveCoreRuntimeRoot, resolvePython, spawnCoreModule } = require("../agentlas-core-harness.cjs");
|
|
11
|
-
|
|
12
|
-
const INDEX_SUBCOMMANDS = new Set(["ingest", "query", "verify", "trace", "public-card"]);
|
|
13
|
-
|
|
14
|
-
async function run(ctx, args) {
|
|
15
|
-
const sub = String((args && args[0]) || "status");
|
|
16
|
-
if (INDEX_SUBCOMMANDS.has(sub)) {
|
|
17
|
-
const coreRoot = resolveCoreRuntimeRoot();
|
|
18
|
-
if (!coreRoot || !resolvePython()) {
|
|
19
|
-
ctx.err(ctx.lang === "ko"
|
|
20
|
-
? "Career Graph 인덱스 실행은 Agentlas OS / Hephaestus 런타임이 필요합니다 (~/.agentlas/runtime/current). 런타임 없이 결과를 지어내지 않습니다."
|
|
21
|
-
: "Career Graph index execution requires the Agentlas OS / Hephaestus runtime (~/.agentlas/runtime/current). No runtime — no fabricated result.");
|
|
22
|
-
return 1;
|
|
23
|
-
}
|
|
24
|
-
const graphArgs = args.slice();
|
|
25
|
-
if (!graphArgs.includes("--project")) graphArgs.push("--project", process.cwd());
|
|
26
|
-
const child = spawnCoreModule("career_graph", graphArgs, { cwd: process.cwd(), stdio: "inherit" }, coreRoot);
|
|
27
|
-
if (!child) {
|
|
28
|
-
ctx.err("Agentlas Core runtime or Python 3.9+ is unavailable.");
|
|
29
|
-
return 1;
|
|
30
|
-
}
|
|
31
|
-
return await new Promise((resolve) => {
|
|
32
|
-
child.on("error", (e) => { ctx.err(String((e && e.message) || e)); resolve(1); });
|
|
33
|
-
child.on("close", (code) => resolve(code == null ? 1 : code));
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
try {
|
|
37
|
-
const lines = await runCareerGraphCli(args, {
|
|
38
|
-
cwd: process.cwd(),
|
|
39
|
-
projectPath: process.cwd(),
|
|
40
|
-
lang: ctx.lang,
|
|
41
|
-
notify: (line) => ctx.out(line),
|
|
42
|
-
});
|
|
43
|
-
for (const line of lines) ctx.out(line);
|
|
44
|
-
return 0;
|
|
45
|
-
} catch (e) {
|
|
46
|
-
ctx.err(String((e && e.message) || e));
|
|
47
|
-
return 1;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
module.exports = { run };
|