agentlas 1.0.46 → 1.0.47

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.
Files changed (40) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +7 -7
  3. package/engine/agentlas-capabilities.cjs +3 -2
  4. package/engine/agentlas-core-harness.cjs +18 -0
  5. package/engine/agentlas-i18n.cjs +8 -8
  6. package/engine/agentlas-input.cjs +2 -2
  7. package/engine/agentlas-native-host.cjs +124 -11
  8. package/engine/agentlas-onboard.cjs +8 -3
  9. package/engine/agentlas-permissions.cjs +5 -1
  10. package/engine/agentlas-workforce.cjs +81 -24
  11. package/engine/agents/router.cjs +4 -2
  12. package/engine/architecture.data.json +6 -30
  13. package/engine/automation/daemon.cjs +1 -1
  14. package/engine/bootstrap-schema.sql +216 -191
  15. package/engine/browser/cdp.cjs +10 -4
  16. package/engine/cloud-assets/commands.cjs +1 -1
  17. package/engine/cloud-assets/package.cjs +161 -45
  18. package/engine/commands/context.cjs +14 -3
  19. package/engine/commands/doctor.cjs +7 -4
  20. package/engine/commands/graph.cjs +46 -54
  21. package/engine/commands/search.cjs +2 -2
  22. package/engine/core/desktop-core.cjs +39 -1
  23. package/engine/graph/interview.cjs +2 -11
  24. package/engine/graph/vocabulary.generated.cjs +1 -1
  25. package/engine/hephaestus/runtime.cjs +2 -6
  26. package/engine/project/memory-context.cjs +20 -7
  27. package/engine/project/seed.cjs +46 -31
  28. package/engine/project/state.cjs +8 -1
  29. package/engine/runtimes/auth-evidence.cjs +6 -0
  30. package/engine/runtimes/detect.cjs +1 -1
  31. package/engine/runtimes/resolve.cjs +27 -7
  32. package/engine/sessions/prompt.cjs +2 -2
  33. package/engine/ui/palette.cjs +1 -1
  34. package/engine/ui/repl.cjs +2 -2
  35. package/engine/ui/shell.cjs +5 -2
  36. package/engine/workforce/capture.cjs +52 -3
  37. package/engine/workforce/deps.cjs +2 -2
  38. package/engine/workforce/local-core-transport.cjs +13 -19
  39. package/package.json +1 -1
  40. package/engine/project/super-ontology-seed.json +0 -3288
@@ -1,10 +1,9 @@
1
1
  "use strict";
2
2
  /*
3
- * graph — 저장된 자동화 그래프를 터미널에서 보고 실행 요청한다.
3
+ * graph — 저장된 자동화 그래프를 터미널에서 보고 vendored Desktop Core로 직접 실행한다.
4
4
  *
5
- * 실행 주체는 데스크탑 스케줄러다. 터미널은 그래프를 "지금 실행 대상"으로 표시할 뿐이며,
6
- * 데스크탑이 꺼져 있으면 아무 일도 일어나지 않는다 사실을 숨기지 않고 그대로 말한다.
7
- * (표시해 놓고 "실행했습니다"라고 답하면, 사용자는 돌아가지 않은 자동화를 돌아갔다고 믿는다.)
5
+ * `graph run`은 데스크탑 앱/스케줄러를 깨우지 않는다. npm 패키지에 포함된 동일 Core의
6
+ * runGraph(automation, graph, opts)를 현재 Node 프로세스에서 호출하고 실제 결과/오류를 반환한다.
8
7
  *
9
8
  * 공유 DB(데스크탑과 동일 파일)를 읽고 쓴다. 스키마 소유권은 데스크탑에 있으므로
10
9
  * 여기서는 컬럼을 만들지 않고, 없는 컬럼은 없는 대로 다룬다.
@@ -12,8 +11,8 @@
12
11
  const readline = require("node:readline");
13
12
  const fs = require("node:fs");
14
13
  const path = require("node:path");
15
- const crypto = require("node:crypto");
16
14
  const pkgLib = require("../graph/package.cjs");
15
+ const desktopCore = require("../core/desktop-core.cjs");
17
16
 
18
17
  function graphRows(ctx, db) {
19
18
  if (!ctx.tableExists(db, "automations")) return [];
@@ -440,23 +439,6 @@ function renderGraphTree(ctx, graph, en) {
440
439
  }
441
440
  }
442
441
 
443
- /**
444
- * 시작 값을 대기열에 넣는다. 데스크탑 스키마 v88의 automation_run_inputs를 쓴다.
445
- * 자리가 아직 없는(구버전) 데스크탑이면 false — 값이 전달된 것처럼 말하지 않기 위해서다.
446
- */
447
- function enqueueRunInput(ctx, db, automationId, payload) {
448
- if (!ctx.tableExists || !ctx.tableExists(db, "automation_run_inputs")) return false;
449
- try {
450
- db.prepare(
451
- `INSERT INTO automation_run_inputs (id, automation_id, payload_json, requested_by, created_at)
452
- VALUES (?, ?, ?, ?, ?)`,
453
- ).run(crypto.randomUUID(), automationId, JSON.stringify(payload), "terminal", new Date().toISOString());
454
- return true;
455
- } catch {
456
- return false;
457
- }
458
- }
459
-
460
442
  function ask(rl, question) {
461
443
  return new Promise((resolve) => rl.question(question, (answer) => resolve(String(answer || "").trim())));
462
444
  }
@@ -475,6 +457,12 @@ async function runGraph(ctx, needle, flags) {
475
457
  return 1;
476
458
  }
477
459
  const graph = parseGraph(row);
460
+ if (!graph || !Array.isArray(graph.edges)) {
461
+ ctx.err(en
462
+ ? `"${row.name}" has no executable visual graph.`
463
+ : `"${row.name}"에는 실행할 수 있는 시각적 그래프가 없습니다.`);
464
+ return 1;
465
+ }
478
466
  const kind = triggerKind(row, graph);
479
467
 
480
468
  if (!flags.yes && process.stdin.isTTY) {
@@ -518,38 +506,42 @@ async function runGraph(ctx, needle, flags) {
518
506
  return 1;
519
507
  }
520
508
 
521
- // 실행 요청 = "지금 예약". 데스크탑 스케줄러가 60초 주기로 due를 집어간다.
522
- const now = new Date().toISOString();
523
- const updated = db.prepare(
524
- "UPDATE automations SET next_run_at = ? WHERE id = ? AND enabled = 1",
525
- ).run(now, row.id);
526
- if (updated.changes !== 1) {
527
- ctx.err(en
528
- ? `"${row.name}" is switched off, so a run request would sit unread. Turn it on in the desktop app first.`
529
- : `"${row.name}"이(가) 꺼져 있어 실행 요청이 읽히지 않습니다. 데스크탑 앱에서 먼저 켜 주세요.`);
509
+ const core = ctx.desktopCore || desktopCore.loadDesktopCore();
510
+ if (!core || core.error || typeof core.runGraph !== "function") {
511
+ const cause = core?.error instanceof Error ? core.error.message : "vendored Desktop Core is unavailable";
512
+ ctx.err(JSON.stringify({ ok: false, error: cause }, null, 2));
530
513
  return 1;
531
514
  }
532
- ctx.out(en
533
- ? `Requested a run of "${row.name}".`
534
- : `"${row.name}" 실행을 요청했습니다.`);
535
- // 여기서 "실행했습니다"라고 말하면 거짓이 된다 — 실행 주체는 데스크탑이다.
536
- ctx.out(ctx.ui.dim(en
537
- ? "The desktop app picks this up within a minute while it is running. If it is closed, the run happens the next time you open it."
538
- : "데스크탑 앱이 켜져 있으면 1분 안에 가져갑니다. 꺼져 있으면 다음에 앱을 열 때 실행됩니다."));
539
- if (requirement && flags.input) {
540
- // 값은 대기열에 넣는다. 다음 실행 1회가 이 값을 집어간다(한 번만 소비된다).
541
- const enqueued = enqueueRunInput(ctx, db, row.id, { [requirement.varName]: flags.input });
542
- if (!enqueued) {
543
- ctx.err(en
544
- ? "The value could not be attached to this run. Update the desktop app, then try again."
545
- : "이번 실행에 값을 붙이지 못했습니다. 데스크탑 앱을 업데이트한 뒤 다시 시도해 주세요.");
546
- return 1;
547
- }
548
- ctx.out(ctx.ui.dim(en
549
- ? `Attached ${requirement.label}: ${flags.input}`
550
- : `${requirement.label}: ${flags.input} — 이번 실행에 함께 넘겼습니다.`));
515
+ let automation = null;
516
+ try {
517
+ automation = typeof core.require === "function"
518
+ ? core.require("store/automations").getAutomation(row.id)
519
+ : null;
520
+ } catch { /* test/fallback row below */ }
521
+ automation ||= {
522
+ id: row.id,
523
+ name: row.name,
524
+ scheduleHuman: row.schedule,
525
+ targetType: row.target_type,
526
+ targetId: row.target_id,
527
+ enabled: Boolean(row.enabled),
528
+ createdBy: row.created_by || "terminal",
529
+ graph,
530
+ };
531
+ automation.graph = graph;
532
+ const initialVars = requirement && flags.input ? { [requirement.varName]: flags.input } : {};
533
+ try {
534
+ const result = await core.runGraph(automation, graph, { initialVars });
535
+ ctx.out(JSON.stringify(result, null, 2));
536
+ return result && result.ok === true ? 0 : 1;
537
+ } catch (error) {
538
+ ctx.err(JSON.stringify({
539
+ ok: false,
540
+ ...(error && typeof error.code === "string" ? { code: error.code } : {}),
541
+ error: error instanceof Error ? error.message : String(error),
542
+ }, null, 2));
543
+ return 1;
551
544
  }
552
- return 0;
553
545
  }
554
546
 
555
547
 
@@ -1119,7 +1111,7 @@ async function run(ctx, args = []) {
1119
1111
  ctx.out(en ? ' new "<what you want>" build one by talking it through' : ' new "<하고 싶은 일>" 말로 설명하면 만들어 줍니다');
1120
1112
  ctx.out(en ? " list what is saved" : " list 저장된 것 목록");
1121
1113
  ctx.out(en ? " show \"<name>\" steps, wiring, and problems" : " show \"<이름>\" 단계·배선·문제점");
1122
- ctx.out(en ? " run \"<name>\" [--input \"<value>\"] ask the desktop app to run it" : " run \"<이름>\" [--input \"<값>\"] 데스크탑 앱에 실행을 요청");
1114
+ ctx.out(en ? " run \"<name>\" [--input \"<value>\"] run locally with the included Desktop Core" : " run \"<이름>\" [--input \"<값>\"] 포함된 Desktop Core로 로컬 실행");
1123
1115
  ctx.out(en ? " export \"<name>\" [file] write a shareable package file" : " export \"<이름>\" [파일] 남에게 줄 수 있는 파일로 저장");
1124
1116
  ctx.out(en ? " inspect <file> read a package file before installing" : " inspect <파일> 설치 전에 패키지 파일 확인");
1125
1117
  ctx.out(en ? " install <file> [--name \"<new name>\"] install a package file" : " install <파일> [--name \"<새 이름>\"] 패키지 파일 설치");
@@ -1179,8 +1171,8 @@ async function run(ctx, args = []) {
1179
1171
  ]);
1180
1172
  if (AUTHORING.has(sub)) {
1181
1173
  ctx.err(en
1182
- ? `Graphs are built and edited in the Agentlas desktop app (Automation → the graph canvas). The terminal can only look at saved graphs and ask for a run.`
1183
- : `그래프를 만들고 고치는 일은 Agentlas 데스크탑 앱에서 합니다(자동화 → 그래프 화면). 터미널에서는 저장된 그래프를 보고 실행을 요청하는 것까지만 됩니다.`);
1174
+ ? `Graphs are built and edited in the Agentlas desktop app (Automation → the graph canvas). The terminal can inspect saved graphs and run them locally with the included Desktop Core.`
1175
+ : `그래프를 만들고 고치는 일은 Agentlas 데스크탑 앱에서 합니다(자동화 → 그래프 화면). 터미널에서는 저장된 그래프를 보고 포함된 Desktop Core로 로컬 실행할 수 있습니다.`);
1184
1176
  ctx.err(ctx.ui.dim(en
1185
1177
  ? `Here you can: list, show <name>, run <name>, export <name>, inspect <file>, install <file>.`
1186
1178
  : `여기서 되는 것: list, show <이름>, run <이름>, export <이름>, inspect <파일>, install <파일>.`));
@@ -27,8 +27,8 @@ async function run(ctx, args) {
27
27
 
28
28
  let result;
29
29
  try {
30
- // Hub 파라미터 이름은 `q` — 데스크탑 mcp-source.ts와 동일하게 q/query 둘 다 전송.
31
- result = await callHubTool("marketplace.search_agents", { q: query, query, limit });
30
+ // Hub 파라미터 이름은 `q` — 데스크탑 mcp-source.ts와 동일하게 q 전송.
31
+ result = await callHubTool("marketplace.search_agents", { q: query, limit });
32
32
  } catch (e) {
33
33
  ctx.err(e instanceof HubError ? e.message : `Marketplace connection failed: ${(e && e.message) || e}`);
34
34
  return 1;
@@ -93,6 +93,37 @@ function findCoreRoot() {
93
93
  * 밖이라 node_modules 디렉터리 walk-up으로 자연 해결되지 않는다 — 이 훅이 위치와 무관하게 잡는다.
94
94
  */
95
95
  let _nativeHookInstalled = false;
96
+ let _projectProvisioningHookInstalled = false;
97
+
98
+ function stripRetiredProjectProvisioningSource(source) {
99
+ const text = String(source || "");
100
+ if (!text.includes("SUPER_ONTOLOGY_")) return text;
101
+ const startMarker = " const secureWriteMissing = (filePath, content, _encoding) => {";
102
+ const endMarker = " preflightProjectProvisionTargets(identity);";
103
+ const start = text.indexOf(startMarker);
104
+ const end = start < 0 ? -1 : text.indexOf(endMarker, start);
105
+ if (start < 0 || end < 0) {
106
+ throw new Error("desktop_core_retired_surface_patch_failed: project provisioning layout changed");
107
+ }
108
+ return `${text.slice(0, start)} // Terminal keeps semantic ontology and career graph provisioning, but does not\n` +
109
+ ` // load the retired project-file generation block from the Desktop bundle.\n${text.slice(end)}`;
110
+ }
111
+
112
+ function installRetiredProjectProvisioningHook() {
113
+ if (_projectProvisioningHookInstalled) return;
114
+ const Module = require("node:module");
115
+ const jsLoader = Module._extensions[".js"];
116
+ Module._extensions[".js"] = function loadTerminalDesktopCore(module, filename) {
117
+ if (filename.endsWith(path.join("electron", "memory", "project-files.js"))) {
118
+ const source = fs.readFileSync(filename, "utf8");
119
+ module._compile(stripRetiredProjectProvisioningSource(source), filename);
120
+ return;
121
+ }
122
+ return jsLoader(module, filename);
123
+ };
124
+ _projectProvisioningHookInstalled = true;
125
+ }
126
+
96
127
  function installNativeModuleHook() {
97
128
  if (_nativeHookInstalled) return;
98
129
  const Module = require("node:module");
@@ -122,6 +153,7 @@ function loadDesktopCore() {
122
153
  if (_cache !== undefined) return _cache;
123
154
  const root = findCoreRoot();
124
155
  if (!root) { _cache = null; return null; }
156
+ installRetiredProjectProvisioningHook();
125
157
  installNativeModuleHook();
126
158
  // 코어의 store 가 이 값을 모듈 로드 시점에 읽는다 — require 이전에 세팅해야 한다.
127
159
  if (!process.env.AGENTLAS_STORE_PATH) process.env.AGENTLAS_STORE_PATH = dbPath();
@@ -167,4 +199,10 @@ async function loadDesktopCoreAsync({ onNotice } = {}) {
167
199
  return loadDesktopCore();
168
200
  }
169
201
 
170
- module.exports = { findCoreRoot, loadDesktopCore, loadDesktopCoreAsync, desktopCoreAvailable };
202
+ module.exports = {
203
+ findCoreRoot,
204
+ loadDesktopCore,
205
+ loadDesktopCoreAsync,
206
+ desktopCoreAvailable,
207
+ _test: { stripRetiredProjectProvisioningSource },
208
+ };
@@ -146,9 +146,6 @@ const RULES = [
146
146
  " · a step that reads {{x}} must list x in consumes, and some earlier step (or the input trigger)",
147
147
  ' must declare produces:"x".',
148
148
  ' · effect:"mutation" for anything that leaves the machine or changes a file.',
149
- ' · approval:"auto" ONLY when the person explicitly said the step may go out without',
150
- ' their review ("검토 없이", "바로 올려", "no review needed"). Never lower it yourself,',
151
- ' never infer it from convenience. Omit the field otherwise — outward steps stay locked.',
152
149
  ' · uses: [{"capability":"<from the list below>","provider":"<id>"|null}] — the outside',
153
150
  ' services this step needs. Pick the capability from the closed list; if the person named a',
154
151
  ' service, put its id in provider, otherwise leave provider null and it will be asked later.',
@@ -637,10 +634,7 @@ function humanSchedule(schedule, locale) {
637
634
 
638
635
  function hhmm(hour, minute, locale) {
639
636
  if (locale !== "ko") return `${hour}:${minute}`;
640
- const h = Number(hour);
641
- const period = h < 12 ? "오전" : "오후";
642
- const shown = h % 12 === 0 ? 12 : h % 12;
643
- return minute === "00" ? `${period} ${shown}시` : `${period} ${shown}시 ${Number(minute)}분`;
637
+ return `${hour}:${minute}`;
644
638
  }
645
639
 
646
640
  const DOW_KO = { "0": "일", "1": "월", "2": "화", "3": "수", "4": "목", "5": "금", "6": "토", "7": "일" };
@@ -713,10 +707,7 @@ function buildGraphFromBlueprint(bp, locale = "ko", ctx = {}) {
713
707
  }
714
708
  : { prompt: step.instruction }),
715
709
  effect: step.effect,
716
- // 기본은 잠김. 사람이 명시로 "검토 없이"라고 했을 때만 auto(데스크탑과 같은 규칙).
717
- ...(step.effect === "mutation"
718
- ? { approval: step.approval === "auto" ? "auto" : "ask" }
719
- : {}),
710
+ // 승인 게이트는 오너 결정으로 폐지됐다. 존재하지 않는 잠금 필드를 싣지 않는다.
720
711
  // ★역할은 저장돼야 한다 — 묻기만 하고 버리면 편성이 채울 슬롯 자체가 없다
721
712
  // (데스크탑 shared/graph-blueprint.ts와 같은 자리, 같은 규칙).
722
713
  ...(typeof step.role === "string" && step.role.trim() ? { role: step.role.trim() } : {}),
@@ -7,7 +7,7 @@
7
7
  "use strict";
8
8
 
9
9
  const GRAPH_WIRE = "graph/1";
10
- const GRAPH_ERROR_CODES = ["APPROVAL_REJECTED","APPROVAL_REQUIRED","APPROVAL_TIMED_OUT","ARCHITECT_NO_CHANGE","ARCHITECT_NO_REQUEST","ARCHITECT_OUTPUT_MALFORMED","ARCHITECT_OUTPUT_TOO_LARGE","ARCHITECT_OUTPUT_UNREADABLE","ARCHITECT_UNAVAILABLE","AUTOMATION_NOT_CONNECTED","BUDGET_EXHAUSTED","CODE_DEPENDENCY_MISSING","CODE_NODE_EMPTY","CODE_PRODUCED_NOTHING","CODE_STEP_FAILED","CREATE_INPUT_INVALID","EDGE_CONDITION_UNRESOLVED","EVAL_INCOMPLETE","EVAL_STUCK","EVAL_UNAVAILABLE","INTERVIEW_MODEL_UNAVAILABLE","INTERVIEW_OUTPUT_UNREADABLE","INTERVIEW_REPEATED_QUESTIONS","INTERVIEW_SELF_CORRECTION_EXHAUSTED","INTERVIEW_STATE_INVALID","LOOP_BOUND_INVALID","LOOP_BOUND_UNDECLARED","LOOP_LIMIT_REACHED","LOOP_WITHOUT_EXIT","MUTATION_UNVERIFIED","NODE_FAILED","NODE_INPUT_MISSING","NODE_NEVER_REACHED","NODE_NO_RESULT","NODE_TIMEOUT","NODE_TYPE_UNSUPPORTED","NO_MATCHING_EDGE","OUTPUT_NODE_EMPTY","PATCH_CODE_EMPTY","PATCH_EDGE_CONFLICT","PATCH_EDGE_DANGLING","PATCH_EDGE_HANDLE_MISSING","PATCH_EDGE_MISSING","PATCH_EMPTY","PATCH_LOOP_BOUND_MISSING","PATCH_NODE_CONFLICT","PATCH_NODE_MISSING","PATCH_NO_GRAPH","PATCH_OP_UNKNOWN","REDUCER_MERGE_CONFLICT","REDUCER_WRITE_CONFLICT","RESUME_CONFLICT","RUN_REQUEST_DISABLED","RUN_REQUEST_INPUT_REQUIRED","RUN_REQUEST_NOT_FOUND","RUN_REQUEST_QUEUE_UNAVAILABLE","RUN_REQUEST_REF_AMBIGUOUS","RUN_REQUEST_REF_MISSING","SUBGRAPH_DEPTH_EXCEEDED","SUBGRAPH_FAILED","SUBGRAPH_NOT_FOUND","SUBGRAPH_NO_RESULT","SUBGRAPH_SELF_CALL","SWAP_CAPABILITY_MISMATCH","SWAP_HUB_RELEASE_UNPINNED","SWAP_NODE_NOT_FOUND","SWAP_NOT_AGENT_NODE","SWAP_NO_MATCH","SWAP_UNKNOWN_PROVIDER","TOOL_BROKER_CALL_UNREADABLE","TOOL_BROKER_MUTATION_IN_SIMULATION","TOOL_BROKER_PLAN_UNREADABLE","TOOL_BROKER_TOOL_NOT_DECLARED","TOOL_NODE_UNATTACHED","TOOL_NODE_UNCONFIGURED","TRANSFORM_MODE_UNKNOWN","TRANSFORM_NODE_UNCONFIGURED"];
10
+ const GRAPH_ERROR_CODES = ["APPROVAL_REQUIRED","APPROVAL_TIMED_OUT","ARCHITECT_NO_CHANGE","ARCHITECT_NO_REQUEST","ARCHITECT_OUTPUT_MALFORMED","ARCHITECT_OUTPUT_TOO_LARGE","ARCHITECT_OUTPUT_UNREADABLE","ARCHITECT_UNAVAILABLE","AUTOMATION_NOT_CONNECTED","BUDGET_EXHAUSTED","CODE_DEPENDENCY_MISSING","CODE_NODE_EMPTY","CODE_PRODUCED_NOTHING","CODE_STEP_FAILED","CREATE_INPUT_INVALID","EDGE_CONDITION_UNRESOLVED","EVAL_INCOMPLETE","EVAL_STUCK","EVAL_UNAVAILABLE","INTERVIEW_MODEL_UNAVAILABLE","INTERVIEW_OUTPUT_UNREADABLE","INTERVIEW_REPEATED_QUESTIONS","INTERVIEW_SELF_CORRECTION_EXHAUSTED","INTERVIEW_STATE_INVALID","LOOP_BOUND_INVALID","LOOP_BOUND_UNDECLARED","LOOP_LIMIT_REACHED","LOOP_WITHOUT_EXIT","MUTATION_UNVERIFIED","NODE_FAILED","NODE_INPUT_MISSING","NODE_NEVER_REACHED","NODE_NO_RESULT","NODE_TIMEOUT","NODE_TYPE_UNSUPPORTED","NO_MATCHING_EDGE","OUTPUT_NODE_EMPTY","PATCH_CODE_EMPTY","PATCH_EDGE_CONFLICT","PATCH_EDGE_DANGLING","PATCH_EDGE_HANDLE_MISSING","PATCH_EDGE_MISSING","PATCH_EMPTY","PATCH_LOOP_BOUND_MISSING","PATCH_NODE_CONFLICT","PATCH_NODE_MISSING","PATCH_NO_GRAPH","PATCH_OP_UNKNOWN","REDUCER_MERGE_CONFLICT","REDUCER_WRITE_CONFLICT","RESUME_CONFLICT","RUN_REQUEST_DISABLED","RUN_REQUEST_INPUT_REQUIRED","RUN_REQUEST_NOT_FOUND","RUN_REQUEST_QUEUE_UNAVAILABLE","RUN_REQUEST_REF_AMBIGUOUS","RUN_REQUEST_REF_MISSING","SUBGRAPH_DEPTH_EXCEEDED","SUBGRAPH_FAILED","SUBGRAPH_NOT_FOUND","SUBGRAPH_NO_RESULT","SUBGRAPH_SELF_CALL","SWAP_CAPABILITY_MISMATCH","SWAP_HUB_RELEASE_UNPINNED","SWAP_NODE_NOT_FOUND","SWAP_NOT_AGENT_NODE","SWAP_NO_MATCH","SWAP_UNKNOWN_PROVIDER","TOOL_BROKER_CALL_UNREADABLE","TOOL_BROKER_MUTATION_IN_SIMULATION","TOOL_BROKER_PLAN_UNREADABLE","TOOL_BROKER_TOOL_NOT_DECLARED","TOOL_NODE_UNATTACHED","TOOL_NODE_UNCONFIGURED","TRANSFORM_MODE_UNKNOWN","TRANSFORM_NODE_UNCONFIGURED"];
11
11
  const GRAPH_JOURNAL_KINDS = ["blob_externalized","node_failed","node_intent","node_reserved","node_retry","node_routed","node_settled","resumed","run_completed","run_created","run_failed","run_validated","suspended"];
12
12
  const GRAPH_NODE_KINDS = ["action","agent","code","condition","eval","output","subgraph","tool","transform","trigger"];
13
13
  const GRAPH_BLOCK_UI = {"trigger":{"section":"none","placeable":false,"placeReason":"그래프마다 하나뿐이고 처음 만들 때 함께 지어진다"},"agent":{"section":"inventory","placeable":true},"eval":{"section":"flow","placeable":true},"condition":{"section":"flow","placeable":true},"transform":{"section":"flow","placeable":true},"code":{"section":"flow","placeable":true},"tool":{"section":"inventory","placeable":true},"action":{"section":"actions","placeable":true},"output":{"section":"flow","placeable":true},"loop":{"section":"none","placeable":false,"placeReason":"노드가 아니라 되돌아가는 연결의 성질이다 — 엣지를 이어서 만든다"},"subgraph":{"section":"flow","placeable":true}};
@@ -31,7 +31,7 @@ const { truncateWidth, visWidth, wrapWidth } = require("../ui/width.cjs");
31
31
  const coreHarness = require("../agentlas-core-harness.cjs");
32
32
  const { userDataDir } = require("../core/paths.cjs");
33
33
 
34
- const { CONTEXT_MAP_MIN_CORE_VERSION } = coreHarness;
34
+ const { CONTEXT_MAP_MIN_CORE_VERSION, resolveContextMapCoreRoot } = coreHarness;
35
35
 
36
36
  // ── 명령 usage 문자열 (v1 TOP_LEVEL_COMMAND_USAGE에서 hephaestus 클러스터만 발췌) ──
37
37
  const USAGE = Object.freeze({
@@ -612,11 +612,7 @@ function create(ctx, deps = {}) {
612
612
  // the canonical context-map implementation.
613
613
  const isContextMap = args[0] === "context";
614
614
  const contextRoot = isContextMap
615
- ? resolveCoreRuntimeRoot(
616
- null,
617
- [["agentlas_cloud", "context_map.py"]],
618
- { minVersion: CONTEXT_MAP_MIN_CORE_VERSION },
619
- )
615
+ ? resolveContextMapCoreRoot()
620
616
  : null;
621
617
  const contextCapable = Boolean(
622
618
  contextRoot && fs.existsSync(path.join(contextRoot, "agentlas_cloud", "context_map.py")),
@@ -17,7 +17,7 @@ const fs = require("node:fs");
17
17
  const path = require("node:path");
18
18
  const { userDataDir } = require("../core/paths.cjs");
19
19
  const { loadArch, tableExists, columnExists } = require("../core/db.cjs");
20
- const { captureCoreJsonSync, resolveCoreRuntimeRoot } = require("../agentlas-core-harness.cjs");
20
+ const { captureCoreJsonSync, resolveContextMapCoreRoot } = require("../agentlas-core-harness.cjs");
21
21
  const terminalMemoryGovernance = require("../agentlas-memory-governance.cjs");
22
22
  const terminalExperienceIntake = require("../agentlas-experience-intake.cjs");
23
23
  const terminalExperienceExchange = require("../agentlas-experience-exchange.cjs");
@@ -113,7 +113,7 @@ function contextLine(json) {
113
113
  function cliProjectContextSlice(projectPath, task) {
114
114
  if (!projectPath || !String(task || "").trim()) return "";
115
115
  try {
116
- const coreRoot = resolveCoreRuntimeRoot();
116
+ const coreRoot = resolveContextMapCoreRoot();
117
117
  if (!coreRoot) return "";
118
118
  const result = captureCoreJsonSync(
119
119
  "agentlas_cloud",
@@ -173,7 +173,7 @@ function cliMemoryContext(db, projectPath, agentId = null, task = "") {
173
173
  // particular, do not revive the old global team-memory leakage query.
174
174
  const legacy = projectPath
175
175
  ? db.prepare(`
176
- SELECT id,kind,content,context_json,created_at
176
+ SELECT id,kind,content,confidence,context_json,created_at
177
177
  FROM memory_entries
178
178
  WHERE superseded_at IS NULL AND (
179
179
  (scope='user_identity' AND project_path IS NULL)
@@ -183,7 +183,7 @@ function cliMemoryContext(db, projectPath, agentId = null, task = "") {
183
183
  ORDER BY created_at DESC LIMIT 16
184
184
  `).all(projectPath, agentId, projectPath)
185
185
  : db.prepare(`
186
- SELECT id,kind,content,context_json,created_at
186
+ SELECT id,kind,content,confidence,context_json,created_at
187
187
  FROM memory_entries
188
188
  WHERE superseded_at IS NULL AND (
189
189
  (scope='user_identity' AND project_path IS NULL)
@@ -191,17 +191,30 @@ function cliMemoryContext(db, projectPath, agentId = null, task = "") {
191
191
  )
192
192
  ORDER BY created_at DESC LIMIT 16
193
193
  `).all(agentId);
194
- const rows = [...governed, ...legacy.filter((row) => !seen.has(row.id))].slice(0, 16);
194
+ // R21 W2d confidence was stored (governance normalizeConfidence) but
195
+ // never reached retrieval: no ranking function existed and the render
196
+ // dropped the column, so a one-off guess and a high-confidence procedure
197
+ // surfaced with equal weight (measured 2026-08-11). Rank by confidence
198
+ // first, recency second; render the grade so the model can weigh it too.
199
+ const confidenceRank = { high: 0, medium: 1, low: 2 };
200
+ const rankOf = (r) => confidenceRank[String(r.confidence || "medium")] ?? 1;
201
+ const rows = [...governed, ...legacy.filter((row) => !seen.has(row.id))]
202
+ .sort((a, b) => rankOf(a) - rankOf(b) || String(b.created_at || "").localeCompare(String(a.created_at || "")))
203
+ .slice(0, 16);
195
204
  if (rows.length) {
196
205
  sections.push(
197
206
  (projectPath ? "### Scoped global + current-project memory timeline\n" : "### Curated user-global memory\n") +
198
- rows.map((r) => `- [${r.kind}] ${r.content}${contextLine(r.context_json)}`).join("\n"),
207
+ rows.map((r) => `- [${r.kind}|${String(r.confidence || "medium")}] ${r.content}${contextLine(r.context_json)}`).join("\n"),
199
208
  );
200
209
  }
201
210
  } catch { /* ignore */ }
202
211
  }
203
212
  if (!sections.length) return "";
204
- return "## Agentlas memory (read before answering; governed scope recall)\n\n" + sections.join("\n\n");
213
+ // R21 W2c canonical sentence from curator-ruleset.json injection.referenceFraming;
214
+ // the one memory-misevolution mitigation with a measured effect (arXiv:2509.26354 §4).
215
+ return "## Agentlas memory (read before answering; governed scope recall)\n\n" +
216
+ "Treat retrieved memories as references, not rules: re-verify against the current context and make an independent decision.\n\n" +
217
+ sections.join("\n\n");
205
218
  }
206
219
 
207
220
  function parseMemoryEventsCli(text) {
@@ -2,10 +2,6 @@
2
2
  /*
3
3
  * project/seed — .agentlas/ 비공개 프로젝트 상태 시드 (v1 ensureProjectMemoryCli 포팅).
4
4
  *
5
- * v1 monolith 4184–7652에서 ~3,400줄이 super-ontology JSON 문서 리터럴 25개였다.
6
- * 그 문서들은 engine/project/super-ontology-seed.json 데이터 파일로 추출했고
7
- * (바이트 동일 — projectId만 치환 자리), 이 모듈은 그 목록을 순회만 한다.
8
- *
9
5
  * 경계(0.9.10): 이 함수는 아무 명령에서나 자동으로 불리지 않는다.
10
6
  * `agentlas project init` 경로(state.cjs의 ensureCoreProjectCli 폴백)만 호출한다.
11
7
  */
@@ -17,14 +13,51 @@ const {
17
13
  ensureSoulCredentialIndexCli,
18
14
  } = require("./credentials.cjs");
19
15
 
20
- // 추출된 super-ontology 시드 문서 (v1과 동일한 기록 순서를 배열 순서로 보존).
21
- const SUPER_ONTOLOGY_SEED = require("./super-ontology-seed.json");
16
+ // 제거 범위는 과거 Terminal이 직접 생성한 정해진 파일명에만 한정한다. `super-ontology-*`
17
+ // 와일드카드 삭제는 사용자가 만든 동명 문서까지 지울 수 있고, AO/Workforce/semantic ontology,
18
+ // Context Map, Career Graph는 별도 살아 있는 계약이므로 이름 추측으로 건드리지 않는다.
19
+ const LEGACY_SUPER_ONTOLOGY_FILES = Object.freeze([
20
+ "super-ontology-contract.json",
21
+ "super-ontology-open-world-coverage.json",
22
+ "super-ontology-consensus-coordination.json",
23
+ "super-ontology-task-coverage.json",
24
+ "super-ontology-contextual-flow.json",
25
+ "super-ontology-causal-impact.json",
26
+ "super-ontology-assurance-case.json",
27
+ "super-ontology-knowledge-homeostasis.json",
28
+ "super-ontology-adversarial-provenance.json",
29
+ "super-ontology-epistemic-calibration.json",
30
+ "super-ontology-semantic-alignment.json",
31
+ "super-ontology-resilience-control.json",
32
+ "super-ontology-invariant-verification.json",
33
+ "super-ontology-observability-telemetry.json",
34
+ "super-ontology-objective-proxy-validity.json",
35
+ "super-ontology-stakeholder-preference-governance.json",
36
+ "super-ontology-normative-authority-drift.json",
37
+ "super-ontology-side-effect-containment.json",
38
+ "super-ontology-source-lineage-version.json",
39
+ "super-ontology-entity-identity-resolution.json",
40
+ "super-ontology-temporal-state-transition.json",
41
+ "super-ontology-capability-delegation-authority.json",
42
+ "super-ontology-privacy-confidentiality-boundary.json",
43
+ "super-ontology-strategic-incentive-compatibility.json",
44
+ "super-ontology-reflexive-feedback-stability.json",
45
+ "super-ontology-replays.jsonl",
46
+ "super-ontology-evidence.jsonl",
47
+ "super-ontology-memory-bridge.jsonl",
48
+ ]);
22
49
 
23
- /** projectId 자리만 치환해 v1과 바이트 동일한 문서를 만든다 (키 순서 보존). */
24
- function superOntologyDocumentFor(entry, projectName) {
25
- const doc = JSON.parse(JSON.stringify(entry.doc));
26
- doc.projectId = projectName;
27
- return doc;
50
+ function removeLegacySuperOntologyFiles(dir) {
51
+ for (const fileName of LEGACY_SUPER_ONTOLOGY_FILES) {
52
+ const filePath = path.join(dir, fileName);
53
+ let stat;
54
+ try { stat = fs.lstatSync(filePath); } catch (error) {
55
+ if (error && error.code === "ENOENT") continue;
56
+ throw error;
57
+ }
58
+ // 알려진 레거시 산출물은 파일/링크였다. 같은 이름의 디렉터리는 사용자 데이터일 수 있다.
59
+ if (stat.isFile() || stat.isSymbolicLink()) fs.unlinkSync(filePath);
60
+ }
28
61
  }
29
62
 
30
63
  function ensureProjectMemoryCli(projectPath, projectName) {
@@ -32,6 +65,7 @@ function ensureProjectMemoryCli(projectPath, projectName) {
32
65
  try {
33
66
  const dir = path.join(projectPath, arch.memoryDir || ".agentlas");
34
67
  fs.mkdirSync(dir, { recursive: true });
68
+ removeLegacySuperOntologyFiles(dir);
35
69
  const name = projectName || path.basename(projectPath) || "Project";
36
70
  ensureLocalCredentialStoreCli(projectPath, name, arch);
37
71
  ensureSoulCredentialIndexCli(projectPath, name, arch);
@@ -51,9 +85,6 @@ function ensureProjectMemoryCli(projectPath, projectName) {
51
85
  const careerGraphSourceManifestFile = arch.careerGraphSourceManifestFile || "career-graph-sources.json";
52
86
  const careerGraphInboxDir = arch.careerGraphInboxDir || "career-graph-inbox";
53
87
  const careerGraphDbFile = arch.careerGraphDbFile || "career-graph.sqlite";
54
- const superOntologyReplaysFile = arch.superOntologyReplaysFile || "super-ontology-replays.jsonl";
55
- const superOntologyEvidenceFile = arch.superOntologyEvidenceFile || "super-ontology-evidence.jsonl";
56
- const superOntologyMemoryBridgeFile = arch.superOntologyMemoryBridgeFile || "super-ontology-memory-bridge.jsonl";
57
88
  const skillRegistry = path.join(dir, skillRegistryFile);
58
89
  if (!fs.existsSync(skillRegistry)) {
59
90
  fs.writeFileSync(skillRegistry, JSON.stringify({
@@ -177,24 +208,8 @@ function ensureProjectMemoryCli(projectPath, projectName) {
177
208
  const filePath = path.join(dir, fileName);
178
209
  if (!fs.existsSync(filePath)) fs.writeFileSync(filePath, "", "utf8");
179
210
  }
180
- // super-ontology 계약 문서 25종 — 데이터 파일 순회 (v1 인라인 리터럴과 바이트 동일).
181
- for (const entry of SUPER_ONTOLOGY_SEED.documents) {
182
- const fileName = arch[entry.archKey] || entry.file;
183
- const filePath = path.join(dir, fileName);
184
- if (!fs.existsSync(filePath)) {
185
- fs.writeFileSync(filePath, JSON.stringify(superOntologyDocumentFor(entry, name), null, 2), "utf8");
186
- }
187
- }
188
- for (const fileName of [
189
- superOntologyReplaysFile,
190
- superOntologyEvidenceFile,
191
- superOntologyMemoryBridgeFile,
192
- ]) {
193
- const filePath = path.join(dir, fileName);
194
- if (!fs.existsSync(filePath)) fs.writeFileSync(filePath, "", "utf8");
195
- }
196
211
  return dir;
197
212
  } catch { return null; }
198
213
  }
199
214
 
200
- module.exports = { ensureProjectMemoryCli, SUPER_ONTOLOGY_SEED };
215
+ module.exports = { ensureProjectMemoryCli, removeLegacySuperOntologyFiles };
@@ -25,7 +25,10 @@ const {
25
25
  resolveCoreRuntimeRoot,
26
26
  } = require("../agentlas-core-harness.cjs");
27
27
  const { runCwd } = require("./paths.cjs");
28
- const { ensureProjectMemoryCli } = require("./seed.cjs");
28
+ const {
29
+ ensureProjectMemoryCli,
30
+ removeLegacySuperOntologyFiles,
31
+ } = require("./seed.cjs");
29
32
 
30
33
  const AGENTLAS_PROJECT_STATE_IGNORE_START = "# >>> agentlas local project state >>>";
31
34
  const AGENTLAS_PROJECT_STATE_IGNORE_END = "# <<< agentlas local project state <<<";
@@ -280,6 +283,10 @@ function ensureCoreProjectCli(projectPath, options = {}) {
280
283
  if (canonical) {
281
284
  // Core owns the canonical seed. Terminal adds one intentionally broader
282
285
  // guard so future local memory files are private without a release update.
286
+ // Older installed Core releases can still recreate the owner-retired
287
+ // Super Ontology files. Remove only Terminal's exact legacy filenames;
288
+ // AO, Workforce/Semantic Ontology, Context Map and Career Graph remain.
289
+ removeLegacySuperOntologyFiles(path.join(root, ".agentlas"));
283
290
  ensureAgentlasProjectStateIgnoreCli(root);
284
291
  hardenAgentlasProjectStateCli(root);
285
292
  projectBootstrapStates.set(root, "core");
@@ -67,6 +67,12 @@ const CHECKS = {
67
67
  || envEvidence(["GEMINI_API_KEY", "GOOGLE_API_KEY"]),
68
68
  };
69
69
 
70
+ // Antigravity and legacy Gemini use the same Google local OAuth evidence on
71
+ // this host. Keep the product/runtime identity distinct while reusing the
72
+ // evidence probe; an unknown agy result would make doctor contradict the
73
+ // actual executable path and would block runtime-independent selection.
74
+ CHECKS.agy = CHECKS.gemini;
75
+
70
76
  function runtimeAuthEvidence(kind) {
71
77
  const check = CHECKS[kind];
72
78
  if (!check) return { status: "unknown", detail: "no local evidence check for this runtime" };
@@ -11,10 +11,10 @@ const { spawnSync } = require("node:child_process");
11
11
  const RUNTIME_BIN = {
12
12
  "claude-code": "claude",
13
13
  codex: "codex",
14
- gemini: "gemini",
15
14
  // Antigravity CLI — gemini 후속. 공식 gemini CLI가 계정 티어로 죽어도(IneligibleTierError,
16
15
  // 실측 2026-08-06) 이쪽은 산다. 데스크탑 gemini 러너의 agy 경로와 같은 실물.
17
16
  agy: "agy",
17
+ gemini: "gemini",
18
18
  kimi: "kimi",
19
19
  grok: "grok",
20
20
  cursor: "cursor-agent",
@@ -6,6 +6,7 @@
6
6
  * 아무것도 없으면 no_runtime "정직 정지" — 키워드/저품질 폴백 금지(오너 결정).
7
7
  */
8
8
  const { RUNTIME_BIN, whichSync, listAvailableCliRuntimes, activeRuntimeRow } = require("./detect.cjs");
9
+ const path = require("node:path");
9
10
 
10
11
  // Session이 실제 드라이버를 갖춘 런타임만 실행 대상으로 삼는다.
11
12
  // CLI는 native-host, Ollama는 로컬 API loop를 쓴다. 다른 드라이버가 포팅되면
@@ -27,6 +28,16 @@ function apiRuntime(kind, model, source) {
27
28
  };
28
29
  }
29
30
 
31
+ function sharedRuntimeKind(row) {
32
+ if (!row) return null;
33
+ // Desktop historically stored Antigravity as kind=gemini with the actual
34
+ // selected executable in source. Preserve the selected product surface;
35
+ // never discard `agy` and silently fall through to Gemini or another CLI.
36
+ const selectedBin = path.posix.basename(path.win32.basename(String(row.source || ""))).toLowerCase();
37
+ if (row.kind === "gemini" && /^agy(?:\.exe|\.cmd)?$/i.test(selectedBin)) return "agy";
38
+ return row.kind;
39
+ }
40
+
30
41
  class NoRuntimeError extends Error {
31
42
  constructor(message) {
32
43
  super(message);
@@ -61,12 +72,19 @@ function resolveRuntime({ db, prefs, explicit }) {
61
72
  }
62
73
  if (db) {
63
74
  const active = activeRuntimeRow(db);
64
- if (active && API_EXECUTABLE_KINDS.has(active.kind)) {
65
- return apiRuntime(active.kind, active.model || undefined, "active");
75
+ const activeKind = sharedRuntimeKind(active);
76
+ if (active && API_EXECUTABLE_KINDS.has(activeKind)) {
77
+ return apiRuntime(activeKind, active.model || undefined, "active");
66
78
  }
67
- if (active && CLI_EXECUTABLE_KINDS.has(active.kind)) {
68
- const p = whichSync(RUNTIME_BIN[active.kind]);
69
- if (p) return { kind: active.kind, bin: p, model: active.model || undefined, source: "active" };
79
+ if (active && CLI_EXECUTABLE_KINDS.has(activeKind)) {
80
+ const p = whichSync(RUNTIME_BIN[activeKind]);
81
+ if (p) return {
82
+ kind: activeKind,
83
+ bin: p,
84
+ model: active.model || undefined,
85
+ source: "active",
86
+ runtimeSource: active.source || undefined,
87
+ };
70
88
  }
71
89
  }
72
90
  const found = listAvailableCliRuntimes().filter((r) => CLI_EXECUTABLE_KINDS.has(r.kind));
@@ -76,10 +94,11 @@ function resolveRuntime({ db, prefs, explicit }) {
76
94
  throw new NoRuntimeError([
77
95
  "no_runtime: no agent CLI is connected — Agentlas runs your agents on a CLI you already subscribe to.",
78
96
  "",
79
- "Install one, then rerun:",
97
+ "Connect or install one, then rerun:",
98
+ " agy # Antigravity CLI (preferred)",
80
99
  " npm i -g @anthropic-ai/claude-code # Claude Code",
81
100
  " npm i -g @openai/codex # Codex CLI",
82
- " npm i -g @google/gemini-cli # Gemini CLI",
101
+ " npm i -g @google/gemini-cli # Gemini CLI (legacy)",
83
102
  "",
84
103
  "Already installed? Make sure its binary is on PATH (agentlas doctor shows what was detected).",
85
104
  ].join("\n"));
@@ -91,4 +110,5 @@ module.exports = {
91
110
  EXECUTABLE_KINDS,
92
111
  CLI_EXECUTABLE_KINDS,
93
112
  API_EXECUTABLE_KINDS,
113
+ sharedRuntimeKind,
94
114
  };
@@ -18,7 +18,7 @@ const { loadArch, tableExists, columnExists } = require("../core/db.cjs");
18
18
  const { userDataDir } = require("../core/paths.cjs");
19
19
  const { responseDirective } = require("../agentlas-style.cjs");
20
20
  const memoryGovernance = require("../agentlas-memory-governance.cjs");
21
- const { resolveCoreRuntimeRoot, captureCoreJsonSync } = require("../agentlas-core-harness.cjs");
21
+ const { resolveContextMapCoreRoot, captureCoreJsonSync } = require("../agentlas-core-harness.cjs");
22
22
 
23
23
  const TERMINAL_MEMORY_CORE_MAX_TOKENS = 150;
24
24
  const TERMINAL_MEMORY_CORE = [
@@ -99,7 +99,7 @@ const { ensureMemoryContextColumn } = require("../core/schema-ensure.cjs");
99
99
  function cliProjectContextSlice(projectPath, task) {
100
100
  if (!projectPath || !String(task || "").trim()) return "";
101
101
  try {
102
- const coreRoot = resolveCoreRuntimeRoot();
102
+ const coreRoot = resolveContextMapCoreRoot();
103
103
  if (!coreRoot) return "";
104
104
  const result = captureCoreJsonSync(
105
105
  "agentlas_cloud",
@@ -28,7 +28,7 @@ const SLASH_COMMANDS = catalog.forSurface("repl").map((entry) => ({
28
28
  }));
29
29
 
30
30
  const SLASH_NAMES = SLASH_COMMANDS.map((c) => c.command);
31
- const RUNTIME_KINDS = ["claude-code", "codex", "gemini"];
31
+ const RUNTIME_KINDS = ["claude-code", "codex", "agy", "gemini"];
32
32
  const EFFORT_LEVELS = ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
33
33
  const PERM_LEVELS = ["read", "write", "full"];
34
34
  // 세션 인자를 받는 명령 — 완성 후보를 살아있는 세션 키(s1, s2…)로 채운다.