agentlas 1.0.45 → 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.
- package/CHANGELOG.md +30 -0
- package/README.md +7 -7
- package/engine/agentlas-capabilities.cjs +3 -2
- package/engine/agentlas-core-harness.cjs +18 -0
- package/engine/agentlas-i18n.cjs +8 -8
- package/engine/agentlas-input.cjs +2 -2
- package/engine/agentlas-native-host.cjs +124 -11
- package/engine/agentlas-onboard.cjs +8 -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 +1 -1
- 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/context.cjs +14 -3
- package/engine/commands/doctor.cjs +7 -4
- package/engine/commands/graph.cjs +62 -64
- package/engine/commands/search.cjs +8 -3
- package/engine/core/desktop-core.cjs +39 -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/auth-evidence.cjs +6 -0
- package/engine/runtimes/detect.cjs +1 -1
- package/engine/runtimes/resolve.cjs +27 -7
- package/engine/sessions/prompt.cjs +2 -2
- package/engine/ui/palette.cjs +1 -1
- package/engine/ui/repl.cjs +2 -2
- package/engine/ui/shell.cjs +108 -1
- package/engine/workforce/capture.cjs +52 -3
- package/engine/workforce/deps.cjs +2 -2
- package/engine/workforce/local-core-transport.cjs +13 -19
- package/package.json +1 -1
- package/engine/project/super-ontology-seed.json +0 -3288
|
@@ -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
|
-
|
|
65
|
-
|
|
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(
|
|
68
|
-
const p = whichSync(RUNTIME_BIN[
|
|
69
|
-
if (p) return {
|
|
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
|
-
"
|
|
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 {
|
|
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",
|
package/engine/ui/palette.cjs
CHANGED
|
@@ -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…)로 채운다.
|
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]);
|
package/engine/ui/shell.cjs
CHANGED
|
@@ -202,6 +202,8 @@ async function startShell(ctx, opts = {}) {
|
|
|
202
202
|
// ctx 초크포인트 재지정 — 55파일의 ctx.out 직출력이 전부 프레임 안으로 들어온다.
|
|
203
203
|
const shellCtx = {
|
|
204
204
|
...ctx,
|
|
205
|
+
// 명령이 "지금 사용자가 어디에 서 있는지"를 알아야 안내를 옳게 쓴다.
|
|
206
|
+
surface: "shell",
|
|
205
207
|
uiInstance: ui,
|
|
206
208
|
out: (s = "") => ui.line(String(s)),
|
|
207
209
|
err: (s = "") => ui.line(ui.c.amber(String(s))),
|
|
@@ -295,6 +297,38 @@ async function startShell(ctx, opts = {}) {
|
|
|
295
297
|
onMessage: (msg) => { ui.ensureNl(); ui.line(ui.c.dim(msg.text)); },
|
|
296
298
|
});
|
|
297
299
|
|
|
300
|
+
/*
|
|
301
|
+
* 목록 피커 — 슬러그를 손으로 받아치게 하지 않는다(오너 지적).
|
|
302
|
+
* SelectList 는 Focusable 이 아니라 포커스로는 키가 안 온다(Loader 와 같은 함정) —
|
|
303
|
+
* 전역 리스너에서 직접 forward 하고, 뜨는 동안 에디터 입력을 막는다.
|
|
304
|
+
*/
|
|
305
|
+
let activePicker = null;
|
|
306
|
+
function pick(items, opts = {}) {
|
|
307
|
+
return new Promise((resolve) => {
|
|
308
|
+
if (!items.length) { resolve(null); return; }
|
|
309
|
+
ui.ensureNl();
|
|
310
|
+
if (opts.title) ui.line(ui.c.bold(opts.title));
|
|
311
|
+
ui.line(ui.c.dim(en
|
|
312
|
+
? "↑/↓ choose · Enter confirm · Esc cancel"
|
|
313
|
+
: "↑/↓ 이동 · Enter 선택 · Esc 취소"));
|
|
314
|
+
const list = new pi.SelectList(items, Math.min(10, items.length), editorTheme.selectList, {});
|
|
315
|
+
const finish = (value) => {
|
|
316
|
+
if (activePicker !== list) return;
|
|
317
|
+
activePicker = null;
|
|
318
|
+
bottom.removeChild(list);
|
|
319
|
+
tui.setFocus(editor);
|
|
320
|
+
tui.requestRender();
|
|
321
|
+
resolve(value);
|
|
322
|
+
};
|
|
323
|
+
list.onSelect = (item) => finish(item);
|
|
324
|
+
list.onCancel = () => finish(null);
|
|
325
|
+
activePicker = list;
|
|
326
|
+
bottom.addChild(list);
|
|
327
|
+
tui.setFocus(null);
|
|
328
|
+
tui.requestRender();
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
298
332
|
const commands = require("../commands/index.cjs");
|
|
299
333
|
const handleSlash = async (cmdline) => {
|
|
300
334
|
const raw = cmdline.split(/\s+/)[0] || "";
|
|
@@ -339,6 +373,69 @@ async function startShell(ctx, opts = {}) {
|
|
|
339
373
|
} catch { /* 렌더 실패 → 아래 클래식 폴스루가 텍스트로 보여준다 */ }
|
|
340
374
|
}
|
|
341
375
|
}
|
|
376
|
+
/*
|
|
377
|
+
* /search — 결과를 목록으로 띄우고 방향키로 고른다. 슬러그를 손으로 받아치게
|
|
378
|
+
* 하지 않는다(오너 지적). 고르면 바로 설치까지 간다.
|
|
379
|
+
*
|
|
380
|
+
* kind 는 서버 열거값을 그대로 보여주지 않는다. "cloud-callable" 은 사용자에게
|
|
381
|
+
* "설치 안 해도 바로 부를 수 있음"이라는 뜻이지, 설치가 안 된다는 뜻이 아니다.
|
|
382
|
+
*/
|
|
383
|
+
if (cmd === "search" && rest.length) {
|
|
384
|
+
const query = rest.join(" ");
|
|
385
|
+
const { callHubTool, HubError } = require("../cloud/hub-client.cjs");
|
|
386
|
+
let result;
|
|
387
|
+
ui.updateSpinner(en ? "Searching the Hub…" : "Hub 검색 중…");
|
|
388
|
+
try {
|
|
389
|
+
result = await callHubTool("marketplace.search_agents", { q: query, limit: 12 });
|
|
390
|
+
} catch (e) {
|
|
391
|
+
ui.stopSpinner();
|
|
392
|
+
ui.error(Object.assign(new Error(e instanceof HubError ? e.message : String((e && e.message) || e)),
|
|
393
|
+
{ code: "hub_search_failed", honestStop: true }));
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
ui.stopSpinner();
|
|
397
|
+
const raw = (result && (result.results || result.agents || result.items)) || (Array.isArray(result) ? result : []);
|
|
398
|
+
const hidden = (slug) => /^researcher-\d+/.test(String(slug || "").toLowerCase())
|
|
399
|
+
|| String(slug || "").toLowerCase().startsWith("hephaestus-");
|
|
400
|
+
const rows = (Array.isArray(raw) ? raw : []).filter((it) => !hidden(it && (it.slug || it.id)));
|
|
401
|
+
if (!rows.length) { ui.line(ui.c.dim(en ? `No results for "${query}"` : `"${query}" 결과 없음`)); return; }
|
|
402
|
+
const callable = (k) => (String(k || "").includes("cloud")
|
|
403
|
+
? (en ? "callable without installing" : "설치 없이 호출 가능")
|
|
404
|
+
: (en ? "install to use" : "설치해야 사용"));
|
|
405
|
+
const chosen = await pick(rows.map((it) => ({
|
|
406
|
+
value: it.slug || it.id || "?",
|
|
407
|
+
label: `${it.slug || it.id}`,
|
|
408
|
+
description: `${it.name || it.title || ""} — ${callable(it.kind || it.entity_kind)}`,
|
|
409
|
+
})), { title: en ? `Hub results for "${query}"` : `"${query}" Hub 결과` });
|
|
410
|
+
if (!chosen) { ui.line(ui.c.dim(en ? "cancelled" : "취소됨")); return; }
|
|
411
|
+
ui.line(ui.c.dim(en ? `installing ${chosen.value}…` : `${chosen.value} 설치 중…`));
|
|
412
|
+
await commands.COMMANDS.install().run(shellCtx, [chosen.value]);
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/*
|
|
417
|
+
* /graph — 저장된 그래프를 목록으로 띄우고 고른 것을 실행한다.
|
|
418
|
+
* (저장 테이블 이름은 automations 이지만 이 화면이 다루는 건 그래프다.)
|
|
419
|
+
*/
|
|
420
|
+
if (cmd === "graph" && (!rest.length || rest[0] === "list")) {
|
|
421
|
+
const rowsOf = db.prepare("SELECT name, enabled, schedule, graph_json FROM automations ORDER BY name").all();
|
|
422
|
+
const graphs = rowsOf.filter((r) => r.graph_json);
|
|
423
|
+
if (!graphs.length) { ui.line(ui.c.dim(en ? "No saved graphs yet." : "저장된 그래프가 없습니다.")); return; }
|
|
424
|
+
const chosen = await pick(graphs.map((g) => {
|
|
425
|
+
let steps = 0;
|
|
426
|
+
try { steps = (JSON.parse(g.graph_json).nodes || []).length; } catch { steps = 0; }
|
|
427
|
+
return {
|
|
428
|
+
value: g.name,
|
|
429
|
+
label: g.name,
|
|
430
|
+
description: `${steps} ${en ? "steps" : "단계"} · ${g.enabled ? (en ? "on" : "켜짐") : (en ? "off" : "꺼짐")}`
|
|
431
|
+
+ (g.schedule ? ` · ${g.schedule}` : ""),
|
|
432
|
+
};
|
|
433
|
+
}), { title: en ? "Saved graphs" : "저장된 그래프" });
|
|
434
|
+
if (!chosen) { ui.line(ui.c.dim(en ? "cancelled" : "취소됨")); return; }
|
|
435
|
+
await commands.COMMANDS.graph().run(shellCtx, ["run", chosen.value]);
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
|
|
342
439
|
/*
|
|
343
440
|
* 세션 설정 4종. 자동완성은 되는데 처리 case 가 없어 "여기서는 아직 안 됩니다"만
|
|
344
441
|
* 답하던 죽은 광고였다(신설 게이트가 잡았다). 기본 REPL 과 같은 의미로 배선하고,
|
|
@@ -352,8 +449,11 @@ async function startShell(ctx, opts = {}) {
|
|
|
352
449
|
return;
|
|
353
450
|
}
|
|
354
451
|
if (cmd === "permission") {
|
|
452
|
+
if (!permissions.isLevel(value)) {
|
|
453
|
+
ui.line(ui.c.dim("Usage: /permission read|write|full"));
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
355
456
|
const next = permissions.normalize(value);
|
|
356
|
-
if (!next) { ui.line(ui.c.dim("Usage: /permission read|write|full")); return; }
|
|
357
457
|
permission = next;
|
|
358
458
|
ui.line(ui.c.dim(`permission: ${next} · ${en ? "persist: agentlas setup" : "영구 저장: agentlas setup"}`));
|
|
359
459
|
return;
|
|
@@ -474,6 +574,13 @@ async function startShell(ctx, opts = {}) {
|
|
|
474
574
|
};
|
|
475
575
|
|
|
476
576
|
tui.addInputListener((data) => {
|
|
577
|
+
// 피커가 떠 있으면 그 키는 피커 것이다 — 에디터로 새면 목록 위에서 글이 써진다.
|
|
578
|
+
if (activePicker) {
|
|
579
|
+
if (pi.matchesKey(data, "escape")) { activePicker.onCancel && activePicker.onCancel(); return { handled: true }; }
|
|
580
|
+
activePicker.handleInput(data);
|
|
581
|
+
tui.requestRender();
|
|
582
|
+
return { handled: true };
|
|
583
|
+
}
|
|
477
584
|
// Shift-Tab 권한 순환 — 렌더러가 raw mode 를 단독 소유하므로 readline 의
|
|
478
585
|
// swallowCompletion 우회 없이 여기서 직접 소비한다 (D2 위험 2의 해소 형태).
|
|
479
586
|
if (pi.matchesKey(data, "shift+tab")) {
|
|
@@ -29,6 +29,7 @@ const { dbPath, userDataDir } = require("../core/paths.cjs");
|
|
|
29
29
|
const RUNTIME_BIN = {
|
|
30
30
|
"claude-code": "claude",
|
|
31
31
|
codex: "codex",
|
|
32
|
+
agy: "agy",
|
|
32
33
|
gemini: "gemini",
|
|
33
34
|
};
|
|
34
35
|
|
|
@@ -198,6 +199,14 @@ function buildArgs(kind, systemPrompt, prompt, permission, runtimeOptions = {})
|
|
|
198
199
|
const mcp = native.geminiMcpIsolationArgs();
|
|
199
200
|
return ["--prompt", `[SYSTEM]\n${systemPrompt}\n\n${prompt}`, ...(model ? ["-m", model] : []), ...perm, ...noAuthorityArgs, ...mcp];
|
|
200
201
|
}
|
|
202
|
+
if (kind === "agy") {
|
|
203
|
+
return native.agyArgs({
|
|
204
|
+
prompt,
|
|
205
|
+
systemPrompt,
|
|
206
|
+
permission: noAuthority ? "read" : level,
|
|
207
|
+
model,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
201
210
|
return [prompt];
|
|
202
211
|
}
|
|
203
212
|
|
|
@@ -287,6 +296,7 @@ function capturedRuntimeUsage(kind, raw) {
|
|
|
287
296
|
}
|
|
288
297
|
const direct =
|
|
289
298
|
genericUsage(event?.usage) ||
|
|
299
|
+
genericUsage(event?.step_update?.usage) ||
|
|
290
300
|
genericUsage(event?.usageMetadata) ||
|
|
291
301
|
genericUsage(event?.stats);
|
|
292
302
|
if (direct) return direct;
|
|
@@ -346,6 +356,12 @@ function capturedRuntimeFailure(kind, raw, text) {
|
|
|
346
356
|
return { message: `gemini ${event.status}`, source: "marker" };
|
|
347
357
|
}
|
|
348
358
|
}
|
|
359
|
+
if (kind === "agy" && event.event === "result") {
|
|
360
|
+
const status = String(event.result?.status || "").toLowerCase();
|
|
361
|
+
if (status && !["success", "completed", "done"].includes(status)) {
|
|
362
|
+
return { message: `agy ${status}`, source: "marker" };
|
|
363
|
+
}
|
|
364
|
+
}
|
|
349
365
|
}
|
|
350
366
|
// 표식이 전혀 없는 케이스(codex 한도) — 휴리스틱 최후 그물, 출처 표기.
|
|
351
367
|
const refusal = detectRuntimeRefusal(text);
|
|
@@ -399,6 +415,21 @@ function capturedRuntimeAgentText(kind, raw) {
|
|
|
399
415
|
return final ? String(final.result ?? final.response) : "";
|
|
400
416
|
}
|
|
401
417
|
|
|
418
|
+
if (kind === "agy") {
|
|
419
|
+
const isProtocol = events.some((event) =>
|
|
420
|
+
event?.event === "step_update" || event?.event === "result",
|
|
421
|
+
);
|
|
422
|
+
if (!isProtocol) return text.trim();
|
|
423
|
+
const final = [...events].reverse().find((event) =>
|
|
424
|
+
event?.event === "result" && typeof event.result?.response === "string",
|
|
425
|
+
);
|
|
426
|
+
if (final) return final.result.response;
|
|
427
|
+
return events
|
|
428
|
+
.filter((event) => event?.event === "step_update" && event.step_update?.step_type === "agent_response")
|
|
429
|
+
.map((event) => typeof event.step_update?.text_delta === "string" ? event.step_update.text_delta : "")
|
|
430
|
+
.join("");
|
|
431
|
+
}
|
|
432
|
+
|
|
402
433
|
return text.trim();
|
|
403
434
|
}
|
|
404
435
|
|
|
@@ -423,6 +454,7 @@ function captureRuntime(kind, systemPrompt, prompt, opts) {
|
|
|
423
454
|
// 계약 테스트용 실행 파일 주입(가짜 CLI가 픽스처를 cat) — 프로덕션 경로에선 없음.
|
|
424
455
|
const bin = opts.binOverride || which(RUNTIME_BIN[kind]) || RUNTIME_BIN[kind];
|
|
425
456
|
let child;
|
|
457
|
+
let launchCleanup = () => {};
|
|
426
458
|
try {
|
|
427
459
|
const spawnImpl = opts.spawn || spawn;
|
|
428
460
|
const env = nativeHost.runtimeEnvForKind(kind, opts.env || process.env, {
|
|
@@ -431,13 +463,28 @@ function captureRuntime(kind, systemPrompt, prompt, opts) {
|
|
|
431
463
|
mcpAllowlistMode: kind === "gemini" ? "exact" : undefined,
|
|
432
464
|
});
|
|
433
465
|
const groupedChild = process.platform !== "win32" && spawnImpl === spawn;
|
|
434
|
-
|
|
466
|
+
const runtimeOptions = {
|
|
435
467
|
model: opts.model,
|
|
436
468
|
effort: opts.effort,
|
|
437
469
|
authorityMode: opts.authorityMode,
|
|
438
470
|
noToolsPolicyPath: opts.noToolsPolicyPath,
|
|
439
471
|
allowedNativeTools: opts.allowedNativeTools,
|
|
440
|
-
}
|
|
472
|
+
};
|
|
473
|
+
let childArgs = buildArgs(kind, systemPrompt, prompt, opts.permission, runtimeOptions);
|
|
474
|
+
if (kind === "agy") {
|
|
475
|
+
const prepared = nativeHost.prepareAgyLaunch({
|
|
476
|
+
prompt,
|
|
477
|
+
systemPrompt,
|
|
478
|
+
permission: opts.authorityMode === "no-authority" ? "read" : opts.permission,
|
|
479
|
+
model: opts.model,
|
|
480
|
+
}, {
|
|
481
|
+
platform: opts.platform,
|
|
482
|
+
promptLimit: opts.agyPromptLimit,
|
|
483
|
+
});
|
|
484
|
+
childArgs = prepared.args;
|
|
485
|
+
launchCleanup = prepared.cleanup;
|
|
486
|
+
}
|
|
487
|
+
child = spawnImpl(bin, childArgs, {
|
|
441
488
|
cwd,
|
|
442
489
|
stdio: ["ignore", "pipe", "pipe"],
|
|
443
490
|
env,
|
|
@@ -446,6 +493,7 @@ function captureRuntime(kind, systemPrompt, prompt, opts) {
|
|
|
446
493
|
});
|
|
447
494
|
child.__agentlasGroupedChild = groupedChild;
|
|
448
495
|
} catch (error) {
|
|
496
|
+
launchCleanup();
|
|
449
497
|
reject(error);
|
|
450
498
|
return;
|
|
451
499
|
}
|
|
@@ -479,6 +527,7 @@ function captureRuntime(kind, systemPrompt, prompt, opts) {
|
|
|
479
527
|
child.removeListener("error", onError);
|
|
480
528
|
child.removeListener("close", onClose);
|
|
481
529
|
if (opts.signal) opts.signal.removeEventListener?.("abort", onAbort);
|
|
530
|
+
launchCleanup();
|
|
482
531
|
};
|
|
483
532
|
const finishReject = (error) => {
|
|
484
533
|
if (settled) return;
|
|
@@ -562,7 +611,7 @@ function captureRuntime(kind, systemPrompt, prompt, opts) {
|
|
|
562
611
|
let text;
|
|
563
612
|
if (kind === "codex" && opts.authorityMode === "no-authority") {
|
|
564
613
|
text = codexCaptureAgentText(raw);
|
|
565
|
-
} else if (kind === "claude-code" || kind === "gemini") {
|
|
614
|
+
} else if (kind === "claude-code" || kind === "gemini" || kind === "agy") {
|
|
566
615
|
text = capturedRuntimeAgentText(kind, raw);
|
|
567
616
|
} else {
|
|
568
617
|
text = raw;
|
|
@@ -109,7 +109,7 @@ function legacyWorkforceRuntime(db, override) {
|
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
const error = new Error(
|
|
112
|
-
"no_runtime: no agent CLI or connected API runtime found (claude / codex / gemini / BYOK / Ollama).",
|
|
112
|
+
"no_runtime: no agent CLI or connected API runtime found (claude / codex / Antigravity agy / legacy gemini / BYOK / Ollama).",
|
|
113
113
|
);
|
|
114
114
|
error.code = "no_runtime";
|
|
115
115
|
throw error;
|
|
@@ -513,7 +513,7 @@ function projectContextSlice(projectPath, task) {
|
|
|
513
513
|
if (!projectPath || !String(task || "").trim()) return "";
|
|
514
514
|
try {
|
|
515
515
|
const core = coreHarness();
|
|
516
|
-
const coreRoot = core.
|
|
516
|
+
const coreRoot = core.resolveContextMapCoreRoot();
|
|
517
517
|
if (!coreRoot) return "";
|
|
518
518
|
const result = core.captureCoreJsonSync(
|
|
519
519
|
"agentlas_cloud",
|
|
@@ -7,16 +7,14 @@
|
|
|
7
7
|
* search_candidates 요청 {workOrder, sourceScope}
|
|
8
8
|
* 응답 agentlas.workforce-federation-result.v1 봉투
|
|
9
9
|
* → 루프에는 봉투를 벗긴 candidateSet만 준다.
|
|
10
|
-
* validate_selection 요청 {workOrder, selection
|
|
11
|
-
* (
|
|
12
|
-
* Core는 자기 선택 세션에서 연합을 이미 안다)
|
|
10
|
+
* validate_selection 요청 {workOrder, selection}
|
|
11
|
+
* (Core는 selectionSessionId로 자기 핀 세션을 복원한다)
|
|
13
12
|
* 응답 안의 selectionValidation이 정확히
|
|
14
13
|
* agentlas.workforce-selection-validation.v1 — 루프 검증기와
|
|
15
14
|
* 동일 계약이라 그대로 돌려준다. 원본 응답은 여기 상태로
|
|
16
15
|
* 붙잡아 둔다(prepare가 요구).
|
|
17
|
-
* prepare_execution 요청 {workOrder, selection,
|
|
18
|
-
* federatedSelection: <validate 원본 응답>,
|
|
19
|
-
* validationReceipt: <동일>, projectDir}
|
|
16
|
+
* prepare_execution 요청 {workOrder, selection,
|
|
17
|
+
* federatedSelection: <validate 원본 응답>, projectDir}
|
|
20
18
|
* 응답 안의 executionPlan이 정확히
|
|
21
19
|
* agentlas.workforce-execution-plan.v5 (roster에
|
|
22
20
|
* directiveBundle·permissionPolicy 동봉) — 그대로 돌려준다.
|
|
@@ -115,7 +113,7 @@ function createLocalCoreHubTool({ sourceScope, projectDir, cwd, client } = {}) {
|
|
|
115
113
|
const core = client || createLocalCoreClient({ cwd: cwd || projectDir });
|
|
116
114
|
// 편성 계보 상태 — 전부 "Core 어휘"(opaque id) 원본이다:
|
|
117
115
|
// maps: 마지막 search 의 id 지도. 재검색(refinement)마다 재구성된다.
|
|
118
|
-
// coreCandidateSet: Core 가 준
|
|
116
|
+
// coreCandidateSet: Core 가 준 요약 메뉴. 로컬 계보 확인에만 쓰며 반송하지 않는다.
|
|
119
117
|
// lastValidationEnvelope: validate 원본 응답. prepare 의 federatedSelection 은
|
|
120
118
|
// 이것이어야 한다 — 루프가 들고 있는 것은 벗겨낸 selectionValidation 뿐이다.
|
|
121
119
|
let maps = { forward: new Map(), reverse: new Map() };
|
|
@@ -137,12 +135,11 @@ function createLocalCoreHubTool({ sourceScope, projectDir, cwd, client } = {}) {
|
|
|
137
135
|
maps = buildIdMaps(args.workOrder);
|
|
138
136
|
coreCandidateSet = null;
|
|
139
137
|
lastValidationEnvelope = null;
|
|
140
|
-
//
|
|
141
|
-
//
|
|
142
|
-
// reference-first 메뉴는 candidate_set_invalid 로 거절된다).
|
|
138
|
+
// Current Core keeps the full dossier in its pinned session and returns a
|
|
139
|
+
// numbered decision menu. Do not request the legacy full-echo form.
|
|
143
140
|
let envelope;
|
|
144
141
|
try {
|
|
145
|
-
envelope = await core.call(name, { workOrder: mapDeep(args.workOrder, maps.forward), sourceScope
|
|
142
|
+
envelope = await core.call(name, { workOrder: mapDeep(args.workOrder, maps.forward), sourceScope });
|
|
146
143
|
} catch (error) {
|
|
147
144
|
// 반응형 정규화(1회): 경계가 지목한 finite 값만 opaque 로 바꿔 재시도.
|
|
148
145
|
const issues = error.code === "work_order_hub_boundary_rejected" ? boundaryIssues(error) : null;
|
|
@@ -161,7 +158,7 @@ function createLocalCoreHubTool({ sourceScope, projectDir, cwd, client } = {}) {
|
|
|
161
158
|
repaired += 1;
|
|
162
159
|
}
|
|
163
160
|
if (!repaired) throw error;
|
|
164
|
-
envelope = await core.call(name, { workOrder: mapDeep(args.workOrder, maps.forward), sourceScope
|
|
161
|
+
envelope = await core.call(name, { workOrder: mapDeep(args.workOrder, maps.forward), sourceScope });
|
|
165
162
|
}
|
|
166
163
|
const candidateSet = envelope && envelope.candidateSet;
|
|
167
164
|
if (!candidateSet || typeof candidateSet !== "object") throw invalid("local Core federation returned no candidateSet");
|
|
@@ -174,9 +171,10 @@ function createLocalCoreHubTool({ sourceScope, projectDir, cwd, client } = {}) {
|
|
|
174
171
|
error.code = "local_core_lineage_missing";
|
|
175
172
|
throw error;
|
|
176
173
|
}
|
|
177
|
-
//
|
|
178
|
-
|
|
179
|
-
|
|
174
|
+
// The host-authored selection must point at the exact summary menu just
|
|
175
|
+
// returned. Core independently reloads and verifies the full pinned set.
|
|
176
|
+
if (args.selection?.candidateSetDigest !== coreCandidateSet.candidateSetDigest) {
|
|
177
|
+
throw invalid("selection lineage mismatch between the loop and the local Core session");
|
|
180
178
|
}
|
|
181
179
|
let envelope;
|
|
182
180
|
let coreSelection = mapDeep(args.selection, maps.forward);
|
|
@@ -184,7 +182,6 @@ function createLocalCoreHubTool({ sourceScope, projectDir, cwd, client } = {}) {
|
|
|
184
182
|
envelope = await core.call(name, {
|
|
185
183
|
workOrder: mapDeep(args.workOrder, maps.forward),
|
|
186
184
|
selection: coreSelection,
|
|
187
|
-
candidateSet: coreCandidateSet,
|
|
188
185
|
});
|
|
189
186
|
} catch (error) {
|
|
190
187
|
/*
|
|
@@ -205,7 +202,6 @@ function createLocalCoreHubTool({ sourceScope, projectDir, cwd, client } = {}) {
|
|
|
205
202
|
envelope = await core.call(name, {
|
|
206
203
|
workOrder: mapDeep(args.workOrder, maps.forward),
|
|
207
204
|
selection: repairedSelection,
|
|
208
|
-
candidateSet: coreCandidateSet,
|
|
209
205
|
});
|
|
210
206
|
coreSelection = repairedSelection;
|
|
211
207
|
}
|
|
@@ -224,9 +220,7 @@ function createLocalCoreHubTool({ sourceScope, projectDir, cwd, client } = {}) {
|
|
|
224
220
|
workOrder: mapDeep(args.workOrder, maps.forward),
|
|
225
221
|
// validate 가 수락한 정확한 본 — 반응형 reasonCode 수리를 반영한다.
|
|
226
222
|
selection: lastCoreSelection || mapDeep(args.selection, maps.forward),
|
|
227
|
-
candidateSet: coreCandidateSet,
|
|
228
223
|
federatedSelection: lastValidationEnvelope,
|
|
229
|
-
validationReceipt: lastValidationEnvelope,
|
|
230
224
|
projectDir,
|
|
231
225
|
});
|
|
232
226
|
if (!envelope || typeof envelope.executionPlan !== "object") throw invalid("local Core preparation returned no executionPlan");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentlas",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.47",
|
|
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"
|