agentlas 1.0.46 → 1.0.48
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 +33 -0
- package/README.md +7 -7
- package/engine/acp/server.cjs +279 -0
- package/engine/agentlas-capabilities.cjs +4 -2
- package/engine/agentlas-core-harness.cjs +18 -0
- package/engine/agentlas-i18n.cjs +8 -8
- package/engine/agentlas-input.cjs +3 -2
- package/engine/agentlas-native-host.cjs +130 -11
- package/engine/agentlas-onboard.cjs +9 -3
- package/engine/agentlas-permissions.cjs +5 -1
- package/engine/agentlas-workforce.cjs +81 -24
- package/engine/agents/router.cjs +4 -2
- package/engine/architecture.data.json +6 -30
- package/engine/automation/daemon.cjs +3 -7
- package/engine/bootstrap-schema.sql +216 -191
- package/engine/browser/cdp.cjs +10 -4
- package/engine/cloud-assets/commands.cjs +1 -1
- package/engine/cloud-assets/package.cjs +161 -45
- package/engine/commands/acp.cjs +45 -0
- package/engine/commands/billing.cjs +2 -2
- package/engine/commands/call.cjs +4 -0
- package/engine/commands/context.cjs +14 -3
- package/engine/commands/doctor.cjs +8 -4
- package/engine/commands/graph.cjs +46 -54
- package/engine/commands/index.cjs +2 -0
- package/engine/commands/search.cjs +2 -2
- package/engine/commands/workforce.cjs +11 -0
- package/engine/core/desktop-core.cjs +93 -1
- package/engine/firms/orchestrate.cjs +32 -1
- package/engine/graph/interview.cjs +2 -11
- package/engine/graph/vocabulary.generated.cjs +1 -1
- package/engine/hephaestus/runtime.cjs +2 -6
- package/engine/project/memory-context.cjs +20 -7
- package/engine/project/seed.cjs +46 -31
- package/engine/project/state.cjs +8 -1
- package/engine/runtimes/acp-driver.cjs +96 -0
- package/engine/runtimes/auth-evidence.cjs +6 -0
- package/engine/runtimes/detect.cjs +3 -13
- package/engine/runtimes/kinds.cjs +84 -0
- package/engine/runtimes/resolve.cjs +67 -14
- package/engine/sessions/prompt.cjs +2 -2
- package/engine/ui/commands-catalog.cjs +2 -0
- package/engine/ui/palette.cjs +2 -1
- package/engine/ui/repl.cjs +4 -3
- package/engine/ui/shell.cjs +43 -5
- package/engine/vendor/desktop-core.manifest.json +5 -5
- package/engine/workforce/capture.cjs +55 -10
- package/engine/workforce/deps.cjs +2 -2
- package/engine/workforce/local-core-transport.cjs +13 -19
- package/package.json +2 -1
- package/engine/project/super-ontology-seed.json +0 -3288
package/engine/project/state.cjs
CHANGED
|
@@ -25,7 +25,10 @@ const {
|
|
|
25
25
|
resolveCoreRuntimeRoot,
|
|
26
26
|
} = require("../agentlas-core-harness.cjs");
|
|
27
27
|
const { runCwd } = require("./paths.cjs");
|
|
28
|
-
const {
|
|
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");
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* runtimes/acp-driver — kimi · grok · cursor 를 위한 ACP 드라이버 (PRD 2026-08-15 T-2).
|
|
4
|
+
*
|
|
5
|
+
* 세 번째 손코딩이 아니다. Desktop 이 만든 공용 ACP 러너(electron/runtime/acp.js)를 벤더 코어에서
|
|
6
|
+
* 그대로 로드해 쓴다 — 데스크탑·터미널이 같은 파일 하나로 같은 런타임을 같은 품질로 돈다.
|
|
7
|
+
* (터미널이 손으로 미러링해 온 native-host 4종과 달리 드리프트가 구조적으로 없다.)
|
|
8
|
+
*
|
|
9
|
+
* 벤더 코어에 acp.js 가 없으면(옛 코어) — 종전 그대로 "드라이버 없음"으로 정직하게 거부한다.
|
|
10
|
+
* 조용히 다른 런타임으로 넘어가지 않는다.
|
|
11
|
+
*/
|
|
12
|
+
const { loadCoreAcpRuntime } = require("../core/desktop-core.cjs");
|
|
13
|
+
|
|
14
|
+
// 정본(runtimes/kinds.cjs)의 ACP 3종에서 파생 — resolve.cjs의 ACP_CLI_KINDS와 같은 원소.
|
|
15
|
+
const ACP_KINDS = new Set(require("./kinds.cjs").ACP_CLI_KINDS);
|
|
16
|
+
|
|
17
|
+
/** 이 머신에서 ACP 드라이버를 쓸 수 있는가. { ok, reason?, module? } */
|
|
18
|
+
function acpDriverAvailability() {
|
|
19
|
+
const loaded = loadCoreAcpRuntime();
|
|
20
|
+
if (!loaded) return { ok: false, reason: "desktop core not available (run `agentlas doctor`)" };
|
|
21
|
+
if (loaded.error) return { ok: false, reason: loaded.error.message };
|
|
22
|
+
if (!loaded.module || typeof loaded.module.createAcpRunner !== "function") {
|
|
23
|
+
return { ok: false, reason: "desktop core exposes no createAcpRunner" };
|
|
24
|
+
}
|
|
25
|
+
return { ok: true, module: loaded.module };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function acpSpecFor(kind, mod) {
|
|
29
|
+
const spec = mod.ACP_AGENTS && mod.ACP_AGENTS[kind];
|
|
30
|
+
return spec || null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* native-host 계약으로 ACP 턴을 돈다.
|
|
35
|
+
* req = { kind, bin, prompt, systemPrompt, cwd, permission, ui, env, signal, model, locale }
|
|
36
|
+
* 반환: { text, session, usage, error, errorKind, errorSource }
|
|
37
|
+
*/
|
|
38
|
+
async function runAcpTurn(req) {
|
|
39
|
+
const { kind, bin, ui } = req;
|
|
40
|
+
const avail = acpDriverAvailability();
|
|
41
|
+
if (!avail.ok) {
|
|
42
|
+
return { text: "", session: req.session || {}, error: `runtime '${kind}' has no ACP driver here: ${avail.reason}`, errorKind: "unsupported", errorSource: "marker" };
|
|
43
|
+
}
|
|
44
|
+
const mod = avail.module;
|
|
45
|
+
const spec = acpSpecFor(kind, mod);
|
|
46
|
+
if (!spec) {
|
|
47
|
+
return { text: "", session: req.session || {}, error: `runtime '${kind}' is not an ACP agent in this core`, errorKind: "unsupported", errorSource: "marker" };
|
|
48
|
+
}
|
|
49
|
+
const runner = mod.createAcpRunner(spec);
|
|
50
|
+
const locale = req.locale === "ko" ? "ko" : "en";
|
|
51
|
+
let streaming = false;
|
|
52
|
+
let lastText = "";
|
|
53
|
+
const events = {
|
|
54
|
+
onPartial: (full) => {
|
|
55
|
+
const text = String(full || "");
|
|
56
|
+
if (!streaming) { ui.streamStart(); streaming = true; }
|
|
57
|
+
const delta = text.startsWith(lastText) ? text.slice(lastText.length) : text;
|
|
58
|
+
lastText = text;
|
|
59
|
+
if (delta) ui.streamDelta(delta);
|
|
60
|
+
},
|
|
61
|
+
onStatus: (status) => ui.status(status),
|
|
62
|
+
onTool: (name, args, result, id, isError) => {
|
|
63
|
+
ui.tool(name, args || "");
|
|
64
|
+
if (result) ui.toolResult(result, !isError);
|
|
65
|
+
},
|
|
66
|
+
onThinking: (phase) => { if (phase === "start") ui.status(locale === "ko" ? "생각 중..." : "thinking..."); },
|
|
67
|
+
onNotice: (notice) => { if (notice && notice.message) ui.status(notice.message); },
|
|
68
|
+
};
|
|
69
|
+
try {
|
|
70
|
+
const result = await runner({
|
|
71
|
+
systemPrompt: req.systemPrompt || "",
|
|
72
|
+
history: [],
|
|
73
|
+
userPrompt: req.prompt || "",
|
|
74
|
+
backendLabel: spec.label,
|
|
75
|
+
locale,
|
|
76
|
+
permission: req.permission,
|
|
77
|
+
runtimeSource: bin,
|
|
78
|
+
cwd: req.cwd,
|
|
79
|
+
env: req.env || process.env,
|
|
80
|
+
signal: req.signal,
|
|
81
|
+
...(req.model ? { model: req.model } : {}),
|
|
82
|
+
}, events);
|
|
83
|
+
if (streaming) ui.streamEnd();
|
|
84
|
+
const session = { ...(req.session || {}), ...(result.sessionId ? { acpSessionId: result.sessionId } : {}) };
|
|
85
|
+
if (result.failure) {
|
|
86
|
+
return { text: result.text || "", session, usage: null, error: result.failure.message, errorKind: result.failure.kind, errorSource: result.failure.source };
|
|
87
|
+
}
|
|
88
|
+
return { text: result.text || "", session, usage: null, error: null };
|
|
89
|
+
} catch (e) {
|
|
90
|
+
if (streaming) ui.streamEnd();
|
|
91
|
+
const message = e && e.message ? e.message : String(e);
|
|
92
|
+
return { text: "", session: req.session || {}, usage: null, error: message, errorKind: /abort/i.test(message) ? "cancelled" : "exit", errorSource: "marker" };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
module.exports = { ACP_KINDS, acpDriverAvailability, acpSpecFor, runAcpTurn };
|
|
@@ -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" };
|
|
@@ -7,20 +7,10 @@
|
|
|
7
7
|
* (없으면 no_runtime 정직 정지 — 폴백 금지는 상위 계층의 계약).
|
|
8
8
|
*/
|
|
9
9
|
const { spawnSync } = require("node:child_process");
|
|
10
|
+
// kind 목록/실행 파일 이름의 정본은 runtimes/kinds.cjs 하나다 — 여기서 다시 적지 않는다.
|
|
11
|
+
const { RUNTIME_BIN, CLI_KINDS } = require("./kinds.cjs");
|
|
10
12
|
|
|
11
|
-
const
|
|
12
|
-
"claude-code": "claude",
|
|
13
|
-
codex: "codex",
|
|
14
|
-
gemini: "gemini",
|
|
15
|
-
// Antigravity CLI — gemini 후속. 공식 gemini CLI가 계정 티어로 죽어도(IneligibleTierError,
|
|
16
|
-
// 실측 2026-08-06) 이쪽은 산다. 데스크탑 gemini 러너의 agy 경로와 같은 실물.
|
|
17
|
-
agy: "agy",
|
|
18
|
-
kimi: "kimi",
|
|
19
|
-
grok: "grok",
|
|
20
|
-
cursor: "cursor-agent",
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const CLI_RUNTIMES = Object.keys(RUNTIME_BIN);
|
|
13
|
+
const CLI_RUNTIMES = CLI_KINDS;
|
|
24
14
|
|
|
25
15
|
function whichSync(bin) {
|
|
26
16
|
const cmd = process.platform === "win32" ? "where" : "which";
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* runtimes/kinds — 런타임 종류(RuntimeKind)의 단일 정본 (2026-08-18).
|
|
4
|
+
*
|
|
5
|
+
* 배경: 같은 어휘가 8곳(detect·capture·resolve·acp-driver·capabilities·onboard·
|
|
6
|
+
* palette·input)에 손 목록으로 흩어져 있었다 — 새 kind 를 추가하면 한두 곳이
|
|
7
|
+
* 반드시 빠진다. 여기 한 벌만 두고, 다른 표면은 이 상수를 **가져다 쓴다**.
|
|
8
|
+
* 좁은 목록(캡처 4종 등)도 정본에서 파생해야 새 kind 가 모든 표면에 보인다.
|
|
9
|
+
*
|
|
10
|
+
* 순서가 곧 계약이다:
|
|
11
|
+
* - detect.listAvailableCliRuntimes 는 이 순서로 PATH 를 훑고, resolve 의
|
|
12
|
+
* "detected" 폴백은 그 첫 항목을 쓴다.
|
|
13
|
+
* - 온보딩 위저드 선택지·팔레트/입력 완성 후보도 이 순서로 표시된다.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const RUNTIME_KIND_SPECS = [
|
|
17
|
+
{ kind: "claude-code", bin: "claude", driver: "native", capture: true },
|
|
18
|
+
{ kind: "codex", bin: "codex", driver: "native", capture: true },
|
|
19
|
+
// Antigravity CLI — gemini 후속. 공식 gemini CLI가 계정 티어로 죽어도(IneligibleTierError,
|
|
20
|
+
// 실측 2026-08-06) 이쪽은 산다. 데스크탑 gemini 러너의 agy 경로와 같은 실물.
|
|
21
|
+
{ kind: "agy", bin: "agy", driver: "native", capture: true },
|
|
22
|
+
{ kind: "gemini", bin: "gemini", driver: "native", capture: true },
|
|
23
|
+
// kimi/grok/cursor 는 ACP 드라이버(runtimes/acp-driver.cjs → 벤더 코어의 공용
|
|
24
|
+
// ACP 러너)로 돈다 (PRD 2026-08-15 T-2). 캡처(buildArgs/텍스트 추출) 계약은 없다.
|
|
25
|
+
{ kind: "kimi", bin: "kimi", driver: "acp" },
|
|
26
|
+
{ kind: "grok", bin: "grok", driver: "acp" },
|
|
27
|
+
{ kind: "cursor", bin: "cursor-agent", driver: "acp" },
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
/** kind → 실행 파일 이름. CLI 런타임 전체(네이티브 + ACP). */
|
|
31
|
+
const RUNTIME_BIN = Object.fromEntries(RUNTIME_KIND_SPECS.map((s) => [s.kind, s.bin]));
|
|
32
|
+
|
|
33
|
+
/** CLI 런타임 kind 전체(탐지 순서). */
|
|
34
|
+
const CLI_KINDS = RUNTIME_KIND_SPECS.map((s) => s.kind);
|
|
35
|
+
|
|
36
|
+
/** native-host 드라이버(스폰 러너)를 갖춘 CLI 4종. */
|
|
37
|
+
const NATIVE_CLI_KINDS = RUNTIME_KIND_SPECS.filter((s) => s.driver === "native").map((s) => s.kind);
|
|
38
|
+
|
|
39
|
+
/** 벤더 코어의 공용 ACP 러너로 도는 3종. */
|
|
40
|
+
const ACP_CLI_KINDS = RUNTIME_KIND_SPECS.filter((s) => s.driver === "acp").map((s) => s.kind);
|
|
41
|
+
|
|
42
|
+
/** 캡처(no-authority headless) 드라이버가 검증된 kind — buildArgs/텍스트 추출 계약 보유. */
|
|
43
|
+
const CAPTURE_CLI_KINDS = RUNTIME_KIND_SPECS.filter((s) => s.capture).map((s) => s.kind);
|
|
44
|
+
|
|
45
|
+
/** kind → bin, 캡처 검증본만. workforce/capture 가 쓴다. */
|
|
46
|
+
const CAPTURE_RUNTIME_BIN = Object.fromEntries(
|
|
47
|
+
RUNTIME_KIND_SPECS.filter((s) => s.capture).map((s) => [s.kind, s.bin]),
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
/** CLI 가 아니라 로컬 API loop 로 실행되는 kind. */
|
|
51
|
+
const API_EXECUTABLE_KINDS = ["ollama"];
|
|
52
|
+
|
|
53
|
+
/** BYOK/API 백엔드 spec 문자열(/runtime 완성 후보의 API 절반). */
|
|
54
|
+
const API_BACKEND_SPECS = ["anthropic", "openai", "google", "ollama", "upstage"];
|
|
55
|
+
|
|
56
|
+
/** /runtime 이 받는 spec 전체: 네이티브 CLI kind + API 백엔드. */
|
|
57
|
+
const RUNTIME_SPECS = [...NATIVE_CLI_KINDS, ...API_BACKEND_SPECS];
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 저장 계약(automation 등)이 허용하는 kind 전체 — CLI + 로컬/BYOK 실행 kind.
|
|
61
|
+
* 데스크탑 shared/runtime-kinds.ts 와 동형(터미널 표기: antigravity→agy, acp 미지원).
|
|
62
|
+
*/
|
|
63
|
+
const CONTRACT_RUNTIME_KINDS = [...CLI_KINDS, "byok", "ollama", "lmstudio", "mlx"];
|
|
64
|
+
|
|
65
|
+
/** 저장 계약이 허용하는 LLM 백엔드 — 데스크탑 shared/runtime-backends.ts 와 동일 15종. */
|
|
66
|
+
const CONTRACT_RUNTIME_BACKENDS = [
|
|
67
|
+
"anthropic", "openai", "google", "ollama", "lmstudio", "mlx", "upstage", "custom", "glm",
|
|
68
|
+
"kimi", "deepseek", "minimax", "xai", "openrouter", "cursor",
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
module.exports = {
|
|
72
|
+
RUNTIME_KIND_SPECS,
|
|
73
|
+
RUNTIME_BIN,
|
|
74
|
+
CLI_KINDS,
|
|
75
|
+
NATIVE_CLI_KINDS,
|
|
76
|
+
ACP_CLI_KINDS,
|
|
77
|
+
CAPTURE_CLI_KINDS,
|
|
78
|
+
CAPTURE_RUNTIME_BIN,
|
|
79
|
+
API_EXECUTABLE_KINDS,
|
|
80
|
+
API_BACKEND_SPECS,
|
|
81
|
+
RUNTIME_SPECS,
|
|
82
|
+
CONTRACT_RUNTIME_KINDS,
|
|
83
|
+
CONTRACT_RUNTIME_BACKENDS,
|
|
84
|
+
};
|
|
@@ -6,16 +6,45 @@
|
|
|
6
6
|
* 아무것도 없으면 no_runtime "정직 정지" — 키워드/저품질 폴백 금지(오너 결정).
|
|
7
7
|
*/
|
|
8
8
|
const { RUNTIME_BIN, whichSync, listAvailableCliRuntimes, activeRuntimeRow } = require("./detect.cjs");
|
|
9
|
+
const KINDS = require("./kinds.cjs");
|
|
10
|
+
const path = require("node:path");
|
|
9
11
|
|
|
10
12
|
// Session이 실제 드라이버를 갖춘 런타임만 실행 대상으로 삼는다.
|
|
11
13
|
// CLI는 native-host, Ollama는 로컬 API loop를 쓴다. 다른 드라이버가 포팅되면
|
|
12
14
|
// 해당 집합에 추가한다(조용한 오폭 방지).
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
//
|
|
16
|
+
// kimi/grok/cursor 는 ACP 드라이버(runtimes/acp-driver.cjs → 벤더 코어의 공용 ACP 러너)로 돈다
|
|
17
|
+
// (PRD 2026-08-15 T-2). 벤더 코어가 그 러너를 갖고 있을 때만 실행 대상에 든다 — 옛 코어면
|
|
18
|
+
// 종전과 같은 "드라이버 없음" 정직 거부.
|
|
19
|
+
// 집합의 원소는 정본(runtimes/kinds.cjs)에서 파생한다 — 여기서 다시 적지 않는다.
|
|
20
|
+
const NATIVE_CLI_KINDS = new Set(KINDS.NATIVE_CLI_KINDS);
|
|
21
|
+
const ACP_CLI_KINDS = new Set(KINDS.ACP_CLI_KINDS);
|
|
22
|
+
const API_EXECUTABLE_KINDS = new Set(KINDS.API_EXECUTABLE_KINDS);
|
|
23
|
+
|
|
24
|
+
function acpKindsAvailable() {
|
|
25
|
+
try {
|
|
26
|
+
const { acpDriverAvailability } = require("./acp-driver.cjs");
|
|
27
|
+
return acpDriverAvailability().ok ? ACP_CLI_KINDS : new Set();
|
|
28
|
+
} catch {
|
|
29
|
+
return new Set();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 라이브 집합: 네이티브 4종 + (코어가 ACP 러너를 갖고 있으면) ACP 3종.
|
|
34
|
+
const CLI_EXECUTABLE_KINDS = new Proxy(NATIVE_CLI_KINDS, {
|
|
35
|
+
get(target, prop) {
|
|
36
|
+
const live = new Set([...target, ...acpKindsAvailable()]);
|
|
37
|
+
const value = live[prop];
|
|
38
|
+
return typeof value === "function" ? value.bind(live) : value;
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
const EXECUTABLE_KINDS = new Proxy(NATIVE_CLI_KINDS, {
|
|
42
|
+
get(target, prop) {
|
|
43
|
+
const live = new Set([...target, ...acpKindsAvailable(), ...API_EXECUTABLE_KINDS]);
|
|
44
|
+
const value = live[prop];
|
|
45
|
+
return typeof value === "function" ? value.bind(live) : value;
|
|
46
|
+
},
|
|
47
|
+
});
|
|
19
48
|
|
|
20
49
|
function apiRuntime(kind, model, source) {
|
|
21
50
|
if (!API_EXECUTABLE_KINDS.has(kind)) return null;
|
|
@@ -27,6 +56,16 @@ function apiRuntime(kind, model, source) {
|
|
|
27
56
|
};
|
|
28
57
|
}
|
|
29
58
|
|
|
59
|
+
function sharedRuntimeKind(row) {
|
|
60
|
+
if (!row) return null;
|
|
61
|
+
// Desktop historically stored Antigravity as kind=gemini with the actual
|
|
62
|
+
// selected executable in source. Preserve the selected product surface;
|
|
63
|
+
// never discard `agy` and silently fall through to Gemini or another CLI.
|
|
64
|
+
const selectedBin = path.posix.basename(path.win32.basename(String(row.source || ""))).toLowerCase();
|
|
65
|
+
if (row.kind === "gemini" && /^agy(?:\.exe|\.cmd)?$/i.test(selectedBin)) return "agy";
|
|
66
|
+
return row.kind;
|
|
67
|
+
}
|
|
68
|
+
|
|
30
69
|
class NoRuntimeError extends Error {
|
|
31
70
|
constructor(message) {
|
|
32
71
|
super(message);
|
|
@@ -45,7 +84,10 @@ function resolveRuntime({ db, prefs, explicit }) {
|
|
|
45
84
|
const bin = RUNTIME_BIN[explicit];
|
|
46
85
|
if (!bin) throw new NoRuntimeError(`unknown runtime: ${explicit}`);
|
|
47
86
|
if (!CLI_EXECUTABLE_KINDS.has(explicit)) {
|
|
48
|
-
|
|
87
|
+
const acpHint = ACP_CLI_KINDS.has(explicit)
|
|
88
|
+
? ` — its ACP driver needs the desktop core with electron/runtime/acp.js (npm run vendor:core / agentlas doctor)`
|
|
89
|
+
: "";
|
|
90
|
+
throw new NoRuntimeError(`runtime '${explicit}' has no v2 streaming driver yet (available: ${[...EXECUTABLE_KINDS].join(", ")})${acpHint}`);
|
|
49
91
|
}
|
|
50
92
|
const p = whichSync(bin);
|
|
51
93
|
if (!p) throw new NoRuntimeError(`runtime '${explicit}' requested but '${bin}' is not on PATH`);
|
|
@@ -61,12 +103,19 @@ function resolveRuntime({ db, prefs, explicit }) {
|
|
|
61
103
|
}
|
|
62
104
|
if (db) {
|
|
63
105
|
const active = activeRuntimeRow(db);
|
|
64
|
-
|
|
65
|
-
|
|
106
|
+
const activeKind = sharedRuntimeKind(active);
|
|
107
|
+
if (active && API_EXECUTABLE_KINDS.has(activeKind)) {
|
|
108
|
+
return apiRuntime(activeKind, active.model || undefined, "active");
|
|
66
109
|
}
|
|
67
|
-
if (active && CLI_EXECUTABLE_KINDS.has(
|
|
68
|
-
const p = whichSync(RUNTIME_BIN[
|
|
69
|
-
if (p) return {
|
|
110
|
+
if (active && CLI_EXECUTABLE_KINDS.has(activeKind)) {
|
|
111
|
+
const p = whichSync(RUNTIME_BIN[activeKind]);
|
|
112
|
+
if (p) return {
|
|
113
|
+
kind: activeKind,
|
|
114
|
+
bin: p,
|
|
115
|
+
model: active.model || undefined,
|
|
116
|
+
source: "active",
|
|
117
|
+
runtimeSource: active.source || undefined,
|
|
118
|
+
};
|
|
70
119
|
}
|
|
71
120
|
}
|
|
72
121
|
const found = listAvailableCliRuntimes().filter((r) => CLI_EXECUTABLE_KINDS.has(r.kind));
|
|
@@ -76,10 +125,11 @@ function resolveRuntime({ db, prefs, explicit }) {
|
|
|
76
125
|
throw new NoRuntimeError([
|
|
77
126
|
"no_runtime: no agent CLI is connected — Agentlas runs your agents on a CLI you already subscribe to.",
|
|
78
127
|
"",
|
|
79
|
-
"
|
|
128
|
+
"Connect or install one, then rerun:",
|
|
129
|
+
" agy # Antigravity CLI (preferred)",
|
|
80
130
|
" npm i -g @anthropic-ai/claude-code # Claude Code",
|
|
81
131
|
" npm i -g @openai/codex # Codex CLI",
|
|
82
|
-
" npm i -g @google/gemini-cli # Gemini CLI",
|
|
132
|
+
" npm i -g @google/gemini-cli # Gemini CLI (legacy)",
|
|
83
133
|
"",
|
|
84
134
|
"Already installed? Make sure its binary is on PATH (agentlas doctor shows what was detected).",
|
|
85
135
|
].join("\n"));
|
|
@@ -90,5 +140,8 @@ module.exports = {
|
|
|
90
140
|
NoRuntimeError,
|
|
91
141
|
EXECUTABLE_KINDS,
|
|
92
142
|
CLI_EXECUTABLE_KINDS,
|
|
143
|
+
NATIVE_CLI_KINDS,
|
|
144
|
+
ACP_CLI_KINDS,
|
|
93
145
|
API_EXECUTABLE_KINDS,
|
|
146
|
+
sharedRuntimeKind,
|
|
94
147
|
};
|
|
@@ -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 {
|
|
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 =
|
|
102
|
+
const coreRoot = resolveContextMapCoreRoot();
|
|
103
103
|
if (!coreRoot) return "";
|
|
104
104
|
const result = captureCoreJsonSync(
|
|
105
105
|
"agentlas_cloud",
|
|
@@ -107,6 +107,8 @@ const CATALOG = [
|
|
|
107
107
|
{ name: "import", group: "advanced", tier: "more", surfaces: BOTH, args: "<path>", argsKo: "<경로>", ko: "로컬 폴더 에이전트 가져오기", en: "Import a local folder agent" },
|
|
108
108
|
{ name: "cd", group: "advanced", tier: "more", surfaces: BOTH, args: "<agent>", argsKo: "<에이전트>", ko: "그 에이전트의 폴더 경로를 출력", en: "Print that agent's folder path" },
|
|
109
109
|
{ name: "native", group: "advanced", tier: "more", surfaces: BOTH, args: "prepare <agent>", argsKo: "prepare <에이전트>", ko: "네이티브 CLI 컨텍스트 생성", en: "Prepare native CLI context" },
|
|
110
|
+
// CLI only: stdout is the protocol wire, so it cannot run inside the REPL.
|
|
111
|
+
{ name: "acp", group: "advanced", tier: "more", surfaces: CLI, args: "[--info]", ko: "에디터(Zed·JetBrains)용 ACP 에이전트로 실행", en: "Serve Agentlas as an ACP agent for editors (Zed, JetBrains)" },
|
|
110
112
|
{ name: "mcp", group: "advanced", tier: "more", surfaces: BOTH, args: "[list|probe <id>]", ko: "MCP 서버", en: "MCP servers" },
|
|
111
113
|
{ name: "plugin", group: "advanced", tier: "more", surfaces: BOTH, args: "<add <slug>|list|remove>", ko: "Hub 플러그인 (MCP 서버)", en: "Hub plugins (MCP servers)" },
|
|
112
114
|
{ name: "creds", group: "advanced", tier: "more", surfaces: BOTH, args: "<list|save|file>", ko: "API 키 보관 (값은 절대 표시 안 함)", en: "API keys (values are never printed)" },
|
package/engine/ui/palette.cjs
CHANGED
|
@@ -28,7 +28,8 @@ const SLASH_COMMANDS = catalog.forSurface("repl").map((entry) => ({
|
|
|
28
28
|
}));
|
|
29
29
|
|
|
30
30
|
const SLASH_NAMES = SLASH_COMMANDS.map((c) => c.command);
|
|
31
|
-
|
|
31
|
+
// /runtime 완성 후보 — 정본(runtimes/kinds.cjs)의 네이티브 스폰 러너 4종.
|
|
32
|
+
const RUNTIME_KINDS = require("../runtimes/kinds.cjs").NATIVE_CLI_KINDS;
|
|
32
33
|
const EFFORT_LEVELS = ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
33
34
|
const PERM_LEVELS = ["read", "write", "full"];
|
|
34
35
|
// 세션 인자를 받는 명령 — 완성 후보를 살아있는 세션 키(s1, s2…)로 채운다.
|
package/engine/ui/repl.cjs
CHANGED
|
@@ -741,7 +741,7 @@ function handleSlash(ctx, cmdline, api) {
|
|
|
741
741
|
}
|
|
742
742
|
|
|
743
743
|
case "runtime": {
|
|
744
|
-
if (!rest[0]) throw usageError("Usage: /runtime claude-code|codex|gemini");
|
|
744
|
+
if (!rest[0]) throw usageError("Usage: /runtime claude-code|codex|agy|gemini");
|
|
745
745
|
// 세션 오버라이드는 저장되지 않는다 — 고지 없이는 사용자가 영구 설정으로
|
|
746
746
|
// 믿는다(2026-08-05 감사 결함 C). 영구 경로를 같은 줄에서 알려준다.
|
|
747
747
|
api.setRuntime(rest[0]);
|
|
@@ -774,7 +774,7 @@ function handleSlash(ctx, cmdline, api) {
|
|
|
774
774
|
return;
|
|
775
775
|
}
|
|
776
776
|
case "permission": {
|
|
777
|
-
if (!
|
|
777
|
+
if (!permissions.isLevel(rest[0])) {
|
|
778
778
|
throw usageError("Usage: /permission read|write|full");
|
|
779
779
|
}
|
|
780
780
|
const level = permissions.normalize(rest[0]);
|
|
@@ -791,7 +791,8 @@ function handleSlash(ctx, cmdline, api) {
|
|
|
791
791
|
* (REPL의 평문 입력이 곧 run이다).
|
|
792
792
|
*/
|
|
793
793
|
// help/agents/list/mcp/doctor 등은 위 케이스에서 이미 처리된다.
|
|
794
|
-
|
|
794
|
+
// acp: stdout becomes the protocol wire — meaningless (and destructive) inside the REPL.
|
|
795
|
+
const REPL_EXCLUDED = new Set(["firm", "setup", "run", "acp"]);
|
|
795
796
|
if (!REPL_EXCLUDED.has(cmd) && commands.COMMANDS[cmd]) {
|
|
796
797
|
const result = commands.COMMANDS[cmd]().run(ctx, rest);
|
|
797
798
|
if (result && typeof result.then === "function") {
|
package/engine/ui/shell.cjs
CHANGED
|
@@ -45,6 +45,35 @@ function loadRenderer() {
|
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
/*
|
|
49
|
+
* 제어 블록 스트리퍼 — 정본은 벤더 코어의 shared/agent-control-blocks
|
|
50
|
+
* (Desktop·Mobile 과 같은 규칙: Memory Events/Delegate/Automation 헤딩,
|
|
51
|
+
* <<agentlas-ask>>·<<agentlas-surface>>·<<agentlas-one-followups>>·goal-complete
|
|
52
|
+
* 마커, 스트리밍 미완성 꼬리까지). 손 regex 는 Memory Events 만 알아 나머지
|
|
53
|
+
* 마커가 화면에 원문으로 샜다. 옛 벤더 번들이라 정본이 없으면 종전 regex 로
|
|
54
|
+
* fail-open — 스트리퍼 부재가 TUI 를 죽여선 안 된다.
|
|
55
|
+
*/
|
|
56
|
+
let _stripCanonical; // undefined=미시도 · null=정본 없음 · function=정본
|
|
57
|
+
function stripControlBlocksForDisplay(text, streaming) {
|
|
58
|
+
if (_stripCanonical === undefined) {
|
|
59
|
+
try {
|
|
60
|
+
const loaded = require("../core/desktop-core.cjs").loadCoreShared("agent-control-blocks");
|
|
61
|
+
_stripCanonical = loaded && loaded.module && typeof loaded.module.stripAgentControlBlocks === "function"
|
|
62
|
+
? loaded.module.stripAgentControlBlocks
|
|
63
|
+
: null;
|
|
64
|
+
} catch {
|
|
65
|
+
_stripCanonical = null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const value = String(text);
|
|
69
|
+
if (_stripCanonical) {
|
|
70
|
+
try {
|
|
71
|
+
return _stripCanonical(value, { streaming: !!streaming });
|
|
72
|
+
} catch { /* 정본 실패 → 아래 종전 regex 로 fail-open */ }
|
|
73
|
+
}
|
|
74
|
+
return value.replace(/\n#{1,3} Memory Events\b[\s\S]*$/, "\n");
|
|
75
|
+
}
|
|
76
|
+
|
|
48
77
|
/* Ui 를 상속해 write 초크포인트만 렌더러로 돌린다. */
|
|
49
78
|
class ShellUi extends Ui {
|
|
50
79
|
/*
|
|
@@ -127,16 +156,22 @@ class ShellUi extends Ui {
|
|
|
127
156
|
if (!this._md) this.streamStart();
|
|
128
157
|
this._mdText += String(text);
|
|
129
158
|
/*
|
|
130
|
-
*
|
|
159
|
+
* 제어 블록은 런타임 계약(펜스 파이프라인이 수확)이지 사용자용이 아니다.
|
|
131
160
|
* append-only 기본 REPL은 이미 찍힌 봉투를 지울 수 없지만, 누적 재렌더는
|
|
132
161
|
* 표시만 잘라낼 수 있다 — 수확 경로(st.text/fences)는 건드리지 않는다.
|
|
162
|
+
* 정본 스트리퍼의 streaming 모드가 미완성 마커 꼬리도 한 프레임 감춘다.
|
|
133
163
|
*/
|
|
134
|
-
const visible = this._mdText
|
|
164
|
+
const visible = stripControlBlocksForDisplay(this._mdText, true);
|
|
135
165
|
this._md.setText(visible);
|
|
136
166
|
this._tui.requestRender();
|
|
137
167
|
this._streaming = true;
|
|
138
168
|
}
|
|
139
169
|
streamEnd() {
|
|
170
|
+
// 확정 렌더 — streaming 모드가 감춰 두던 꼬리를 settled 규칙으로 최종 판정한다.
|
|
171
|
+
if (this._md && this._mdText) {
|
|
172
|
+
this._md.setText(stripControlBlocksForDisplay(this._mdText, false));
|
|
173
|
+
this._tui.requestRender();
|
|
174
|
+
}
|
|
140
175
|
this._md = null;
|
|
141
176
|
this._mdText = "";
|
|
142
177
|
this._streaming = false;
|
|
@@ -386,7 +421,7 @@ async function startShell(ctx, opts = {}) {
|
|
|
386
421
|
let result;
|
|
387
422
|
ui.updateSpinner(en ? "Searching the Hub…" : "Hub 검색 중…");
|
|
388
423
|
try {
|
|
389
|
-
result = await callHubTool("marketplace.search_agents", { q: query,
|
|
424
|
+
result = await callHubTool("marketplace.search_agents", { q: query, limit: 12 });
|
|
390
425
|
} catch (e) {
|
|
391
426
|
ui.stopSpinner();
|
|
392
427
|
ui.error(Object.assign(new Error(e instanceof HubError ? e.message : String((e && e.message) || e)),
|
|
@@ -449,8 +484,11 @@ async function startShell(ctx, opts = {}) {
|
|
|
449
484
|
return;
|
|
450
485
|
}
|
|
451
486
|
if (cmd === "permission") {
|
|
487
|
+
if (!permissions.isLevel(value)) {
|
|
488
|
+
ui.line(ui.c.dim("Usage: /permission read|write|full"));
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
452
491
|
const next = permissions.normalize(value);
|
|
453
|
-
if (!next) { ui.line(ui.c.dim("Usage: /permission read|write|full")); return; }
|
|
454
492
|
permission = next;
|
|
455
493
|
ui.line(ui.c.dim(`permission: ${next} · ${en ? "persist: agentlas setup" : "영구 저장: agentlas setup"}`));
|
|
456
494
|
return;
|
|
@@ -612,4 +650,4 @@ async function startShell(ctx, opts = {}) {
|
|
|
612
650
|
return new Promise(() => {});
|
|
613
651
|
}
|
|
614
652
|
|
|
615
|
-
module.exports = { startShell };
|
|
653
|
+
module.exports = { startShell, stripControlBlocksForDisplay };
|
|
@@ -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": "3",
|
|
3
|
+
"url": "https://github.com/agentlas-ai/agentlas-terminal/releases/download/desktop-core-v3/desktop-core.tar.gz",
|
|
4
|
+
"sha256": "a72cce0ccfe718e2a9255b1b4722d330ef9887e22be92c654a0435f17f5ee178",
|
|
5
|
+
"sizeBytes": 12437404,
|
|
6
|
+
"writtenAt": "2026-08-18T04:09:04.540Z"
|
|
7
7
|
}
|