agentlas 1.0.12 → 1.0.14
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 +39 -0
- package/README.md +4 -2
- package/bin/agentlas.cjs +17 -3
- package/engine/agentlas-config.cjs +25 -18
- package/engine/agentlas-core-harness.cjs +14 -1
- package/engine/agentlas-i18n.cjs +2 -0
- package/engine/agentlas-input.cjs +1 -1
- package/engine/agentlas-memory-governance.cjs +10 -0
- package/engine/agentlas-onboard.cjs +22 -5
- package/engine/agentlas-sqlite-policy.cjs +18 -7
- package/engine/agentlas-workforce.cjs +136 -19
- package/engine/agentlas-workload-routing.cjs +32 -4
- package/engine/agentlas.cjs +8 -0
- package/engine/automation/daemon.cjs +67 -30
- package/engine/automation/schedule.cjs +16 -0
- package/engine/automation/store.cjs +70 -12
- package/engine/bootstrap-schema.sql +788 -53
- package/engine/commands/automation.cjs +8 -0
- package/engine/commands/chats.cjs +6 -1
- package/engine/commands/firm.cjs +7 -0
- package/engine/commands/help.cjs +23 -2
- package/engine/commands/hep-cloud.cjs +31 -0
- package/engine/commands/hep-hub.cjs +30 -0
- package/engine/commands/hep-local.cjs +32 -0
- package/engine/commands/hep-network.cjs +43 -0
- package/engine/commands/index.cjs +38 -7
- package/engine/commands/open.cjs +5 -1
- package/engine/commands/run.cjs +12 -2
- package/engine/commands/setup.cjs +12 -11
- package/engine/commands/storm.cjs +5 -9
- package/engine/commands/swarm.cjs +4 -4
- package/engine/commands/uninstall.cjs +36 -2
- package/engine/commands/version.cjs +29 -0
- package/engine/commands/workforce.cjs +10 -10
- package/engine/core/schema-ensure.cjs +75 -0
- package/engine/experience/variant.cjs +46 -2
- package/engine/hephaestus/runtime.cjs +7 -0
- package/engine/memory-cli/curate.cjs +3 -7
- package/engine/project/memory-context.cjs +3 -7
- package/engine/project/state.cjs +7 -6
- package/engine/sessions/orchestrator.cjs +41 -3
- package/engine/sessions/prompt.cjs +4 -7
- package/engine/sessions/session.cjs +32 -0
- package/engine/storm/swarm.cjs +12 -0
- package/engine/ui/palette.cjs +14 -2
- package/engine/ui/renderer.cjs +37 -0
- package/engine/ui/repl.cjs +7 -1
- package/engine/workforce/concurrency.cjs +41 -0
- package/package.json +1 -1
|
@@ -92,6 +92,14 @@ async function run(ctx, args) {
|
|
|
92
92
|
targetId = f.id;
|
|
93
93
|
targetLabel = f.name;
|
|
94
94
|
}
|
|
95
|
+
// 잘못된 IANA 존이면 nextCronRun 이 cron 파싱 실패와 똑같이 null 을 돌려준다.
|
|
96
|
+
// 그대로 두면 멀쩡한 cron 을 범인으로 지목해서, 사용자가 cron 만 계속 고쳐 쓰며
|
|
97
|
+
// 매번 같은 실패를 본다. 존을 먼저 검증해 틀린 필드를 정확히 지목한다.
|
|
98
|
+
if (flags.tz && !schedule.isValidTimezone(flags.tz)) {
|
|
99
|
+
return fail(ko
|
|
100
|
+
? `타임존을 해석하지 못했습니다: "${flags.tz}" (IANA 형식이 필요합니다 — 예: Asia/Seoul, UTC, America/New_York)`
|
|
101
|
+
: `Could not parse timezone: "${flags.tz}" (needs IANA format — e.g. Asia/Seoul, UTC, America/New_York)`);
|
|
102
|
+
}
|
|
95
103
|
const next = schedule.nextCronRun(flags.cron, new Date(), flags.tz || null);
|
|
96
104
|
if (!next) {
|
|
97
105
|
return fail(ko
|
|
@@ -20,8 +20,13 @@ function run(ctx, args) {
|
|
|
20
20
|
}
|
|
21
21
|
for (const r of rows) {
|
|
22
22
|
const when = String(r.updated_at || "").replace("T", " ").slice(0, 16);
|
|
23
|
-
|
|
23
|
+
// id 앞 8자를 먼저 찍는다 — `open <id 앞부분>`이 유일한 재개 경로인데
|
|
24
|
+
// 목록에 id가 없으면 SQLite를 직접 열지 않는 한 재개가 불가능했다.
|
|
25
|
+
// 8자는 open.cjs의 모호성 안내(id.slice(0,8))와 같은 규약.
|
|
26
|
+
const id = String(r.id || "").slice(0, 8);
|
|
27
|
+
ctx.out(` ${ctx.ui.bold(id)} ${ctx.ui.dim(when)} ${ctx.ui.accent((r.agent_slug || "?").padEnd(20))} ${r.title}`);
|
|
24
28
|
}
|
|
29
|
+
ctx.out(ctx.ui.dim(ctx.lang === "en" ? " resume: agentlas open <id>" : " 재개: agentlas open <id>"));
|
|
25
30
|
return 0;
|
|
26
31
|
}
|
|
27
32
|
|
package/engine/commands/firm.cjs
CHANGED
|
@@ -73,6 +73,13 @@ async function run(ctx, args) {
|
|
|
73
73
|
const { EFFORTS, TIERS } = require("../agentlas-workload-routing.cjs");
|
|
74
74
|
const { Orchestrator } = require("../sessions/orchestrator.cjs");
|
|
75
75
|
const permissions = require("../agentlas-permissions.cjs");
|
|
76
|
+
// 플래그 검증은 CEO 계획 턴을 부르기 전에. normalize는 모르는 값을 read로 fail-closed
|
|
77
|
+
// 강등하는데, firm은 그 권한을 CEO·본부 전 세션·종합 턴까지 그대로 물려주므로 조용히
|
|
78
|
+
// 강등하면 full을 요청한 사용자가 쓰기 차단된 채 3-tier 전체를 돌린 줄 모른다 (run.cjs 동일 가드).
|
|
79
|
+
if (flags.permission && !permissions.LEVELS.includes(String(flags.permission))) {
|
|
80
|
+
ctx.err(`unknown --permission ${flags.permission} (use: ${permissions.LEVELS.join(" | ")})`);
|
|
81
|
+
return 1;
|
|
82
|
+
}
|
|
76
83
|
if (flags.effort && !EFFORTS.includes(String(flags.effort))) {
|
|
77
84
|
ctx.err(`unknown --effort ${flags.effort} (use: ${EFFORTS.join(" | ")})`);
|
|
78
85
|
return 1;
|
package/engine/commands/help.cjs
CHANGED
|
@@ -22,12 +22,13 @@ AGENTS & HUB
|
|
|
22
22
|
import <path> · cd · native prepare local folder agents
|
|
23
23
|
list installed agents/companies + orchestrator/worker runtimes
|
|
24
24
|
experience <sub> portable Experience: list|inspect|validate|save|publish|status|export|unpublish
|
|
25
|
-
variant resolve
|
|
25
|
+
variant resolve --base-release <id> local variant selection (variant help)
|
|
26
26
|
|
|
27
27
|
EXECUTE
|
|
28
28
|
storm <goal> Goal+UltraCode harness: plan → allocate → execute → verify [--research]
|
|
29
29
|
swarm <goal> emergent agent swarm [--parallel N]
|
|
30
30
|
workforce | network <request> Agent Workforce Ontology route
|
|
31
|
+
hep-local | hep-cloud | hep-hub "<request>" same, restricted to one source scope
|
|
31
32
|
call "a,b" "<ctx>" · browser · route "<req>" [--json] · research <sub>
|
|
32
33
|
|
|
33
34
|
KNOWLEDGE
|
|
@@ -65,4 +66,24 @@ function run(ctx) {
|
|
|
65
66
|
return 0;
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
|
|
69
|
+
function runForCommand(ctx, command) {
|
|
70
|
+
const name = String(command || "").trim();
|
|
71
|
+
const rows = HELP.split("\n")
|
|
72
|
+
.map((line) => line.trim())
|
|
73
|
+
.filter((line) => line && !line.endsWith(":"))
|
|
74
|
+
.filter((line) => {
|
|
75
|
+
const commandColumn = line.split(/\s{2,}/, 1)[0];
|
|
76
|
+
return commandColumn
|
|
77
|
+
.split(/\s*·\s*|\s*\|\s*/)
|
|
78
|
+
.some((entry) => entry === name || entry.startsWith(`${name} `));
|
|
79
|
+
});
|
|
80
|
+
ctx.out(`Usage: agentlas ${name} [options]`);
|
|
81
|
+
if (rows.length > 0) {
|
|
82
|
+
for (const row of rows) ctx.out(` ${row}`);
|
|
83
|
+
} else {
|
|
84
|
+
ctx.out(` See "agentlas help" for the full command list.`);
|
|
85
|
+
}
|
|
86
|
+
return 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
module.exports = { run, runForCommand, HELP };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* hep-cloud — 로그인한 오너의 Agent Cloud 에이전트만으로 임시 태스크포스 편성.
|
|
4
|
+
*
|
|
5
|
+
* WHY 이 파일이 따로 있는가 (2026-07-28 수리):
|
|
6
|
+
* COMMAND_ALIASES 가 `hep-cloud → cloud` 로 접혀 있었다. `cloud` 는 이름만 같은
|
|
7
|
+
* 전혀 다른 명령 — 클라우드 자산 보관함(save|publish|package|list|restore|
|
|
8
|
+
* install|delete|field-test)이다. 그래서 `agentlas hep-cloud "<과제>"` 는
|
|
9
|
+
* 과제를 서브커맨드로 읽고 `usage: agentlas cloud <save|…>` 를 뱉으며 exit 1 —
|
|
10
|
+
* hep-cloud 를 한 글자도 언급하지 않는 에러라 사용자에게 다음 수가 없었다.
|
|
11
|
+
* 스코프(오너 Cloud 한정)를 실제로 지키는 표면은 Hephaestus 네이티브 런타임
|
|
12
|
+
* (`hephaestus hep-cloud`)뿐이므로 build/call/legacy-network 와 동일한
|
|
13
|
+
* 패스스루 계약으로 그쪽에 넘긴다. 자산 보관함은 계속 `agentlas cloud …` 다.
|
|
14
|
+
*
|
|
15
|
+
* v1 인자 가드 그대로: help 토큰 → usage 0, 무인자 → usage 실패 exit 1.
|
|
16
|
+
*/
|
|
17
|
+
const { create, usageFor, isHelpToken } = require("../hephaestus/runtime.cjs");
|
|
18
|
+
|
|
19
|
+
async function run(ctx, args) {
|
|
20
|
+
if (args.some(isHelpToken)) {
|
|
21
|
+
ctx.out(usageFor("hep-cloud", ctx.lang));
|
|
22
|
+
return 0;
|
|
23
|
+
}
|
|
24
|
+
if (!args.length) {
|
|
25
|
+
ctx.err("✖ " + usageFor("hep-cloud", ctx.lang));
|
|
26
|
+
return 1;
|
|
27
|
+
}
|
|
28
|
+
return create(ctx).cmdHep(["hep-cloud", ...args]);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = { run };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* hep-hub — 공개 Agentlas Hub 에이전트만으로 임시 태스크포스 편성.
|
|
4
|
+
*
|
|
5
|
+
* WHY 이 파일이 따로 있는가 (2026-07-28 수리):
|
|
6
|
+
* COMMAND_ALIASES 가 `hep-hub → search` 로 접혀 있었다. search 는 스태핑이 아니라
|
|
7
|
+
* 마켓플레이스 디렉터리 나열이고(그마저 Cloud+Hub 혼합 표면이다), 실행은 하지
|
|
8
|
+
* 않는다. 그래서 `agentlas hep-hub "<과제>"` 는 과제를 검색어로 읽고 슬러그
|
|
9
|
+
* 목록만 찍은 뒤 exit 0 — 아무것도 실행하지 않고 성공한 척했다(가짜 성공 금지).
|
|
10
|
+
* 공개 Hub 한정 스태핑을 실제로 수행하는 표면은 Hephaestus 네이티브 런타임
|
|
11
|
+
* (`hephaestus hep-hub`)이므로 그쪽에 그대로 넘긴다. 후보만 보고 싶으면
|
|
12
|
+
* 기존 `agentlas search`(=hep-search)가 그대로 남아 있다.
|
|
13
|
+
*
|
|
14
|
+
* v1 인자 가드 그대로: help 토큰 → usage 0, 무인자 → usage 실패 exit 1.
|
|
15
|
+
*/
|
|
16
|
+
const { create, usageFor, isHelpToken } = require("../hephaestus/runtime.cjs");
|
|
17
|
+
|
|
18
|
+
async function run(ctx, args) {
|
|
19
|
+
if (args.some(isHelpToken)) {
|
|
20
|
+
ctx.out(usageFor("hep-hub", ctx.lang));
|
|
21
|
+
return 0;
|
|
22
|
+
}
|
|
23
|
+
if (!args.length) {
|
|
24
|
+
ctx.err("✖ " + usageFor("hep-hub", ctx.lang));
|
|
25
|
+
return 1;
|
|
26
|
+
}
|
|
27
|
+
return create(ctx).cmdHep(["hep-hub", ...args]);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = { run };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* hep-local — 이 머신에 등록된 Local 에이전트만으로 임시 태스크포스 편성.
|
|
4
|
+
*
|
|
5
|
+
* WHY 이 파일이 따로 있는가 (2026-07-28 수리):
|
|
6
|
+
* COMMAND_ALIASES 가 `hep-local → workforce` 로 접혀 있었다. 터미널 workforce 는
|
|
7
|
+
* 공개 Hub 메뉴로 스태핑하는 Agent Workforce Ontology 경로이고(엔드포인트:
|
|
8
|
+
* AGENTLAS_MCP_BASE_URL, search_candidates 스키마에 sourceScope 자체가 없음),
|
|
9
|
+
* cmdWorkforce 는 스코프 플래그를 받지 않는다. 즉 "등록된 로컬만" 이라고
|
|
10
|
+
* 문서화된 명령이 조용히 Local+Cloud+공개 Hub 전체로 넓혀 실행됐다 —
|
|
11
|
+
* 사용자가 의도하지 않은 Hub 크레딧 소모까지 포함해서. 스코프는 이름의 전부다.
|
|
12
|
+
* 스코프를 지킬 수 있는 표면은 Hephaestus 네이티브 런타임(`hephaestus hep-local`)
|
|
13
|
+
* 뿐이므로, build/call/legacy-network 와 동일한 패스스루 계약으로 그쪽에 넘긴다.
|
|
14
|
+
*
|
|
15
|
+
* v1 인자 가드 그대로: help 토큰 → usage 0, 무인자 → usage 실패 exit 1
|
|
16
|
+
* (요청 문자열 없는 호출이 자연어 라우팅으로 새는 것 방지).
|
|
17
|
+
*/
|
|
18
|
+
const { create, usageFor, isHelpToken } = require("../hephaestus/runtime.cjs");
|
|
19
|
+
|
|
20
|
+
async function run(ctx, args) {
|
|
21
|
+
if (args.some(isHelpToken)) {
|
|
22
|
+
ctx.out(usageFor("hep-local", ctx.lang));
|
|
23
|
+
return 0;
|
|
24
|
+
}
|
|
25
|
+
if (!args.length) {
|
|
26
|
+
ctx.err("✖ " + usageFor("hep-local", ctx.lang));
|
|
27
|
+
return 1;
|
|
28
|
+
}
|
|
29
|
+
return create(ctx).cmdHep(["hep-local", ...args]);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = { run };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* hep-network — 등록된 Local + 오너 Cloud + 공개 Hub 를 연합해 임시 태스크포스 편성.
|
|
4
|
+
*
|
|
5
|
+
* WHY 이 파일이 따로 있는가 (2026-07-28 수리):
|
|
6
|
+
* COMMAND_ALIASES 가 `hep-network → workforce` 로 접혀 있었다. hep-local/hep-cloud/
|
|
7
|
+
* hep-hub 와 정확히 같은 결함이다 — 별칭은 이름만 바꿔주고 스코프는 어디에도
|
|
8
|
+
* 전달하지 않는다. 터미널 workforce(cmdWorkforce)는 callHubTool 로
|
|
9
|
+
* AGENTLAS_MCP_BASE_URL(기본 https://agentlas.cloud/api/mcp/v1)을 직접 치고
|
|
10
|
+
* `{workOrder}` 만 보낸다(engine/agentlas-workforce.cjs:3043). 그 서버는
|
|
11
|
+
* sourceScope 가 없으면 "hub" 로 기본값을 잡는다
|
|
12
|
+
* (agentlas/AgentsAtlas/app/src/lib/mcp/workforce.ts:228). 결과:
|
|
13
|
+
*
|
|
14
|
+
* - 로컬 에이전트와 오너 Cloud 에이전트는 후보 집합에 들어간 적이 없다.
|
|
15
|
+
* - 그런데 영수증의 로스터 핀은 전부 source:"hub" 로 기록된다
|
|
16
|
+
* (engine/workforce/deps.cjs). "네트워크 전량을 봤다" 로 읽힌다.
|
|
17
|
+
*
|
|
18
|
+
* 연합(federation)은 Core 가 소유한다: 세 소스 메뉴를 각각 받아 출처·계보를
|
|
19
|
+
* 증명하고 하나의 CandidateSet 으로 합친 뒤, 선택은 호스트 LLM 이 한다. 터미널이
|
|
20
|
+
* Hub 를 직접 치는 경로에는 그 계층이 통째로 없다. 그래서 build/call/hep-local
|
|
21
|
+
* 과 동일한 패스스루 계약으로 Hephaestus 네이티브 표면에 넘긴다.
|
|
22
|
+
*
|
|
23
|
+
* `agentlas workforce` 자체는 남겨둔다 — 공개 Hub 스태핑을 그 이름으로 명시해
|
|
24
|
+
* 부르는 것은 정직한 사용이다. 이름이 소스 스코프를 약속하는 hep-* 만 옮긴다.
|
|
25
|
+
*
|
|
26
|
+
* v1 인자 가드 그대로: help 토큰 → usage 0, 무인자 → usage 실패 exit 1
|
|
27
|
+
* (요청 문자열 없는 호출이 자연어 라우팅으로 새는 것 방지).
|
|
28
|
+
*/
|
|
29
|
+
const { create, usageFor, isHelpToken } = require("../hephaestus/runtime.cjs");
|
|
30
|
+
|
|
31
|
+
async function run(ctx, args) {
|
|
32
|
+
if (args.some(isHelpToken)) {
|
|
33
|
+
ctx.out(usageFor("hep-network", ctx.lang));
|
|
34
|
+
return 0;
|
|
35
|
+
}
|
|
36
|
+
if (!args.length) {
|
|
37
|
+
ctx.err("✖ " + usageFor("hep-network", ctx.lang));
|
|
38
|
+
return 1;
|
|
39
|
+
}
|
|
40
|
+
return create(ctx).cmdHep(["hep-network", ...args]);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = { run };
|
|
@@ -47,6 +47,12 @@ const COMMANDS = {
|
|
|
47
47
|
evolve: () => require("./evolve.cjs"),
|
|
48
48
|
variant: () => require("./variant.cjs"),
|
|
49
49
|
hep: () => require("./hep.cjs"),
|
|
50
|
+
// 소스 스코프 스태핑 3종은 1급 명령이다 — 별칭으로 접으면 스코프가 사라진다.
|
|
51
|
+
// (아래 COMMAND_ALIASES 주석의 2026-07-28 수리 참조.)
|
|
52
|
+
"hep-network": () => require("./hep-network.cjs"),
|
|
53
|
+
"hep-local": () => require("./hep-local.cjs"),
|
|
54
|
+
"hep-cloud": () => require("./hep-cloud.cjs"),
|
|
55
|
+
"hep-hub": () => require("./hep-hub.cjs"),
|
|
50
56
|
build: () => require("./build.cjs"),
|
|
51
57
|
connect: () => require("./connect.cjs"),
|
|
52
58
|
call: () => require("./call.cjs"),
|
|
@@ -106,10 +112,25 @@ const GUARDED_NO_ARG = new Set(["search", "install", "upload"]);
|
|
|
106
112
|
// 플랫폼 간 이름 통일(오너 결정 2026-07-27): 클로드코드/코덱스에서 부르는 hep-*
|
|
107
113
|
// 스킬명과 터미널 명령이 서로 다르면 사용자가 어느 표면에 있는지에 따라 이름을
|
|
108
114
|
// 바꿔 써야 한다. 같은 기능은 어디서든 같은 이름으로 부른다.
|
|
115
|
+
//
|
|
116
|
+
// 불변식(2026-07-28 수리): 별칭은 "같은 기능"에만 건다. 소스 스코프를 이름에
|
|
117
|
+
// 달고 있는 hep-local / hep-cloud / hep-hub 는 여기 넣으면 안 된다 — 별칭은
|
|
118
|
+
// 이름만 바꿔주고 스코프는 어디에도 전달되지 않기 때문이다. 실제로 그랬다:
|
|
119
|
+
// hep-cloud → cloud : 자산 보관함 명령. 과제 문자열을 서브커맨드로 읽어
|
|
120
|
+
// `usage: agentlas cloud <save|…>` + exit 1.
|
|
121
|
+
// hep-local → workforce : cmdWorkforce 는 스코프 플래그를 받지 않는다(전량
|
|
122
|
+
// 공개 Hub 메뉴 스태핑). "로컬 전용"이 조용히 넓어짐.
|
|
123
|
+
// hep-hub → search : 디렉터리 나열만 하고 아무것도 실행하지 않음.
|
|
124
|
+
// 세 명령은 스코프를 실제로 지키는 Hephaestus 네이티브 표면으로 가는 1급 명령
|
|
125
|
+
// (COMMANDS 의 hep-local/hep-cloud/hep-hub)으로 승격했다.
|
|
126
|
+
//
|
|
127
|
+
// 같은 이유로 hep-network 도 별칭에서 뺐다(2026-07-28). 이름은 "Local + owner
|
|
128
|
+
// Cloud + public Hub"인데 cmdWorkforce 는 스코프를 어디에도 싣지 않고
|
|
129
|
+
// agentlas.cloud/api/mcp/v1 을 직접 친다. 그 서버는 sourceScope 가 없으면 "hub"
|
|
130
|
+
// 로 기본값을 잡으므로(agentlas/.../lib/mcp/workforce.ts:228) 로컬·클라우드
|
|
131
|
+
// 에이전트는 후보에 들어간 적이 없는데 결과는 네트워크 전량을 본 것처럼 남았다.
|
|
132
|
+
// 연합은 Core 가 소유한다 — 네이티브 표면으로 넘긴다.
|
|
109
133
|
const COMMAND_ALIASES = {
|
|
110
|
-
"hep-network": "workforce",
|
|
111
|
-
network: "workforce",
|
|
112
|
-
"hep-cloud": "cloud",
|
|
113
134
|
"hep-build": "build",
|
|
114
135
|
"hep-call": "call",
|
|
115
136
|
"hep-search": "search",
|
|
@@ -117,8 +138,6 @@ const COMMAND_ALIASES = {
|
|
|
117
138
|
"hep-storm": "storm",
|
|
118
139
|
"hep-browser": "browser",
|
|
119
140
|
"hep-connect": "connect",
|
|
120
|
-
"hep-local": "workforce",
|
|
121
|
-
"hep-hub": "search",
|
|
122
141
|
};
|
|
123
142
|
|
|
124
143
|
function resolveCommandName(cmd) {
|
|
@@ -139,8 +158,20 @@ function dispatch(ctx, argv) {
|
|
|
139
158
|
return 1;
|
|
140
159
|
}
|
|
141
160
|
|
|
142
|
-
|
|
143
|
-
|
|
161
|
+
/*
|
|
162
|
+
* 인자 유무와 무관하게 막는다. `rest.length === 0` 조건이 붙어 있던 동안은
|
|
163
|
+
* 단어 하나만 더 붙이면(`agentlas settings theme`, `marketplace browse`)
|
|
164
|
+
* 가드를 그냥 지나쳐 dispatch가 undefined를 반환했고, 엔진은 그걸 프롬프트로
|
|
165
|
+
* 보고 실제 에이전트를 띄웠다 — 제품이 "데스크탑 전용"이라고 선언한 화면
|
|
166
|
+
* 이름에 토큰이 청구되고 에이전트가 사용자 저장소에서 셸까지 돌렸다(실사용
|
|
167
|
+
* 실증: settings theme → System Optimizer가 Bash 실행, marketplace browse →
|
|
168
|
+
* 20883 토큰 소진). agentlas.cjs의 오타 가드는 `normalized.length === 1`
|
|
169
|
+
* 이라 이 경로를 못 받는다. 여기가 유일한 차단 지점이므로 arity를 보지 않는다.
|
|
170
|
+
* 진짜 작업이면 안내대로 따옴표로 묶어 하나의 프롬프트로 넘긴다.
|
|
171
|
+
*/
|
|
172
|
+
if (DESKTOP_ONLY_SURFACES[cmd]) {
|
|
173
|
+
const asTask = [rawCmd, ...rest].join(" ");
|
|
174
|
+
ctx.err(`${DESKTOP_ONLY_SURFACES[cmd]}\nIt was not run as a prompt — rerun with quotes if you meant a task: agentlas "${asTask}"`);
|
|
144
175
|
return 1;
|
|
145
176
|
}
|
|
146
177
|
|
package/engine/commands/open.cjs
CHANGED
|
@@ -25,7 +25,11 @@ function run(ctx, args) {
|
|
|
25
25
|
return 1;
|
|
26
26
|
}
|
|
27
27
|
if (rows.length > 1) {
|
|
28
|
-
|
|
28
|
+
// 안내 접두사는 사용자가 입력한 것보다 반드시 길어야 한다 — chats 목록이
|
|
29
|
+
// 8자를 찍으므로, 8자로 고정하면 8자 충돌 시 같은 문자열 두 개를 돌려주는
|
|
30
|
+
// 막다른 길이 된다("더 긴 접두사"를 만들 방법이 없음).
|
|
31
|
+
const hintLen = Math.max(8, prefix.length + 4);
|
|
32
|
+
ctx.err(ko ? `모호합니다 — 더 긴 접두사를 쓰세요 (${rows.map((r) => r.id.slice(0, hintLen)).join(", ")})` : `Ambiguous — use a longer prefix (${rows.map((r) => r.id.slice(0, hintLen)).join(", ")})`);
|
|
29
33
|
return 1;
|
|
30
34
|
}
|
|
31
35
|
const chat = rows[0];
|
package/engine/commands/run.cjs
CHANGED
|
@@ -26,6 +26,8 @@ const { Orchestrator } = require("../sessions/orchestrator.cjs");
|
|
|
26
26
|
const { Renderer } = require("../ui/renderer.cjs");
|
|
27
27
|
const permissions = require("../agentlas-permissions.cjs");
|
|
28
28
|
const { EFFORTS, TIERS } = require("../agentlas-workload-routing.cjs");
|
|
29
|
+
const { projectCwd } = require("../project/paths.cjs");
|
|
30
|
+
const { ensureTerminalProjectForExecutionCli } = require("../project/state.cjs");
|
|
29
31
|
|
|
30
32
|
function readStdin() {
|
|
31
33
|
return new Promise((resolve) => {
|
|
@@ -164,12 +166,20 @@ async function runOnce(ctx, args) {
|
|
|
164
166
|
}
|
|
165
167
|
}
|
|
166
168
|
|
|
169
|
+
const permission = permissions.normalize(parsed.permission || (ctx.prefs && ctx.prefs.permission) || "write");
|
|
170
|
+
const cwd = projectCwd();
|
|
171
|
+
try {
|
|
172
|
+
ensureTerminalProjectForExecutionCli(db, cwd, permission, "terminal-run");
|
|
173
|
+
} catch (e) {
|
|
174
|
+
ctx.err(String((e && e.message) || e));
|
|
175
|
+
return 1;
|
|
176
|
+
}
|
|
167
177
|
const orch = new Orchestrator({ db, lang: ctx.lang });
|
|
168
178
|
const session = orch.spawn({
|
|
169
179
|
agent,
|
|
170
180
|
runtime,
|
|
171
|
-
permission
|
|
172
|
-
cwd
|
|
181
|
+
permission,
|
|
182
|
+
cwd,
|
|
173
183
|
title: prompt.slice(0, 60),
|
|
174
184
|
});
|
|
175
185
|
|
|
@@ -10,20 +10,20 @@ const { userDataDir } = require("../core/paths.cjs");
|
|
|
10
10
|
const { RUNTIME_BIN, whichSync } = require("../runtimes/detect.cjs");
|
|
11
11
|
|
|
12
12
|
async function runWizard(ctx, rl) {
|
|
13
|
-
|
|
13
|
+
// 저장을 마법사 "밖"에서 하면 이미 "완료" 문구가 찍힌 뒤다. 저장 함수를 넘겨
|
|
14
|
+
// 성공 문구보다 먼저 쓰게 하고, updatePrefs가 던지는 실패를 그대로 올려보낸다
|
|
15
|
+
// (예전엔 반환값을 버려서 잠금 경합·읽기전용 디렉터리에서도 exit 0으로 거짓말).
|
|
16
|
+
return runOnboard({
|
|
14
17
|
ui: ctx.uiInstance,
|
|
15
18
|
rl,
|
|
16
19
|
helpers: { RUNTIME_BIN, which: whichSync },
|
|
17
|
-
|
|
18
|
-
if (result && result.onboarded) {
|
|
19
|
-
updatePrefs(userDataDir(), {
|
|
20
|
+
persist: (choices) => updatePrefs(userDataDir(), {
|
|
20
21
|
onboarded: true,
|
|
21
|
-
...(
|
|
22
|
-
...(
|
|
23
|
-
...(
|
|
24
|
-
})
|
|
25
|
-
}
|
|
26
|
-
return result;
|
|
22
|
+
...(choices.lang ? { language: choices.lang } : {}),
|
|
23
|
+
...(choices.runtime ? { runtime: choices.runtime } : {}),
|
|
24
|
+
...(choices.permission ? { permission: choices.permission } : {}),
|
|
25
|
+
}),
|
|
26
|
+
});
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
async function run(ctx) {
|
|
@@ -36,7 +36,8 @@ async function run(ctx) {
|
|
|
36
36
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
37
37
|
try {
|
|
38
38
|
const result = await runWizard(ctx, rl);
|
|
39
|
-
|
|
39
|
+
// 저장까지 성공해야 0 — 설정이 디스크에 없으면 사용자에겐 아무것도 안 한 것과 같다.
|
|
40
|
+
return result && result.onboarded && result.saved ? 0 : 1;
|
|
40
41
|
} finally {
|
|
41
42
|
rl.close();
|
|
42
43
|
}
|
|
@@ -10,16 +10,12 @@
|
|
|
10
10
|
* stormbreaker-core-harness-unavailable 로 멈춘다 — 하네스의 로컬 모조 실행 금지.
|
|
11
11
|
* 런타임 부재는 resolveRuntime의 code="no_runtime" throw가 그대로 전파돼 exit 1.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* 경로를 만들었다. 그 프로젝트-상태 기계는 아직 v2로 포팅되지 않았다. storm/swarm 이
|
|
16
|
-
* projectPath 를 쓰는 곳은 buildChildEnv 의 프로젝트 dotenv 스코핑뿐이고 부재 시
|
|
17
|
-
* fail-closed 의존이 없다 — 그래서 여기서는 cwd 를 projectPath 로 넘긴다
|
|
18
|
-
* (commands/workforce.cjs 와 동일한 결정·동일한 근거). 프로젝트-상태 모듈이 v2에
|
|
19
|
-
* 착륙하면 이 자리를 그 결과로 교체한다.
|
|
13
|
+
* Write-capable first contact now runs the canonical Core project bootstrap
|
|
14
|
+
* for the exact current folder. Read-only execution remains passive.
|
|
20
15
|
*/
|
|
21
16
|
const { buildStormDeps } = require("../storm/deps.cjs");
|
|
22
17
|
const { projectCwd } = require("../workforce/capture.cjs");
|
|
18
|
+
const { ensureTerminalProjectForExecutionCli } = require("../project/state.cjs");
|
|
23
19
|
|
|
24
20
|
function assertPermissionFlag(value) {
|
|
25
21
|
if (value && !["read", "write", "full"].includes(String(value))) {
|
|
@@ -62,11 +58,11 @@ async function run(ctx, args) {
|
|
|
62
58
|
const db = ctx.db();
|
|
63
59
|
const cwd = projectCwd();
|
|
64
60
|
const permission = resolvePermission(ctx);
|
|
61
|
+
const projectPath = ensureTerminalProjectForExecutionCli(db, cwd, permission, "terminal-storm") || cwd;
|
|
65
62
|
const storm = require("../storm/storm.cjs").create(buildStormDeps(ctx));
|
|
66
63
|
const r = await storm.cmdStorm(db, rest, runtimeOverride, {
|
|
67
64
|
cwd,
|
|
68
|
-
|
|
69
|
-
projectPath: cwd,
|
|
65
|
+
projectPath,
|
|
70
66
|
permission,
|
|
71
67
|
});
|
|
72
68
|
return r && r.ok ? 0 : 1;
|
|
@@ -11,11 +11,11 @@
|
|
|
11
11
|
* agentlas-workload-routing 이 검증만 하고, 거부 시 fail-closed 다.
|
|
12
12
|
* 런타임 부재는 resolveRuntime의 code="no_runtime" throw가 그대로 전파돼 exit 1.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
* commands/storm.cjs 파일 머리 주석과 동일한 결정·동일한 근거로 cwd 를 넘긴다.
|
|
14
|
+
* Write-capable first contact bootstraps the exact current project through Core.
|
|
16
15
|
*/
|
|
17
16
|
const { buildStormDeps } = require("../storm/deps.cjs");
|
|
18
17
|
const { projectCwd } = require("../workforce/capture.cjs");
|
|
18
|
+
const { ensureTerminalProjectForExecutionCli } = require("../project/state.cjs");
|
|
19
19
|
|
|
20
20
|
function assertPermissionFlag(value) {
|
|
21
21
|
if (value && !["read", "write", "full"].includes(String(value))) {
|
|
@@ -62,11 +62,11 @@ async function run(ctx, args) {
|
|
|
62
62
|
const db = ctx.db();
|
|
63
63
|
const cwd = projectCwd();
|
|
64
64
|
const permission = resolvePermission(ctx);
|
|
65
|
+
const projectPath = ensureTerminalProjectForExecutionCli(db, cwd, permission, "terminal-swarm") || cwd;
|
|
65
66
|
const swarm = require("../storm/swarm.cjs").create(buildStormDeps(ctx));
|
|
66
67
|
const r = await swarm.cmdSwarm(db, rest, runtimeOverride, {
|
|
67
68
|
cwd,
|
|
68
|
-
|
|
69
|
-
projectPath: cwd,
|
|
69
|
+
projectPath,
|
|
70
70
|
permission,
|
|
71
71
|
});
|
|
72
72
|
return r && r.ok ? 0 : 1;
|
|
@@ -11,7 +11,8 @@ const { runWriteTransaction } = require("../agentlas-sqlite-policy.cjs");
|
|
|
11
11
|
|
|
12
12
|
function run(ctx, args) {
|
|
13
13
|
const ko = ctx.lang === "ko";
|
|
14
|
-
|
|
14
|
+
// 동의 플래그를 슬러그보다 앞에 써도 되도록 첫 번째 비플래그 인자를 대상으로 본다.
|
|
15
|
+
const token = args.find((a) => !String(a).startsWith("-"));
|
|
15
16
|
if (!token) {
|
|
16
17
|
ctx.err(ko ? "사용법: agentlas uninstall <agent>" : "Usage: agentlas uninstall <agent>");
|
|
17
18
|
return 1;
|
|
@@ -45,6 +46,35 @@ function run(ctx, args) {
|
|
|
45
46
|
}
|
|
46
47
|
}
|
|
47
48
|
|
|
49
|
+
/*
|
|
50
|
+
* 대화 파괴 게이트 — bootstrap-schema.sql:50 chats.agent_id 는
|
|
51
|
+
* `ON DELETE CASCADE`, :61 chat_messages.chat_id 도 CASCADE 다. 즉
|
|
52
|
+
* installed_agents 행 하나를 지우면 그 에이전트의 모든 챗과 메시지가 같이
|
|
53
|
+
* 사라진다(데스크탑과 공유하는 SQLite라 데스크탑 쪽 이력도 함께 증발한다).
|
|
54
|
+
* 그런데 이 명령은 그 사실을 한 줄도 알리지 않고 `✓ 제거됨`만 찍었다 —
|
|
55
|
+
* 되돌릴 수 없는 삭제가 무고지·무확인으로 일어나던 자리다.
|
|
56
|
+
* 그래서 지우기 "전에" 파급 건수를 세고, 대화가 있으면 명시적 동의
|
|
57
|
+
* (--yes/-y/--force) 없이는 아무것도 지우지 않는다. 대화가 0건이면
|
|
58
|
+
* 파괴할 이력이 없으므로 종전대로 그냥 진행한다(불필요한 마찰 금지).
|
|
59
|
+
*/
|
|
60
|
+
const consented = args.some((a) => a === "--yes" || a === "-y" || a === "--force");
|
|
61
|
+
let chatCount = 0;
|
|
62
|
+
let messageCount = 0;
|
|
63
|
+
if (ctx.tableExists(db, "chats")) {
|
|
64
|
+
chatCount = db.prepare("SELECT COUNT(*) AS n FROM chats WHERE agent_id=?").get(agent.id).n;
|
|
65
|
+
if (chatCount && ctx.tableExists(db, "chat_messages")) {
|
|
66
|
+
messageCount = db.prepare(
|
|
67
|
+
"SELECT COUNT(*) AS n FROM chat_messages WHERE chat_id IN (SELECT id FROM chats WHERE agent_id=?)",
|
|
68
|
+
).get(agent.id).n;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (chatCount && !consented) {
|
|
72
|
+
ctx.err(ko
|
|
73
|
+
? `"${agent.slug}" 제거는 대화 ${chatCount}개와 메시지 ${messageCount}개를 함께 영구 삭제합니다 (데스크탑 앱과 공유하는 DB이며 되돌릴 수 없습니다).\n계속하려면: agentlas uninstall ${agent.slug} --yes`
|
|
74
|
+
: `Uninstalling "${agent.slug}" will also permanently delete ${chatCount} chat(s) and ${messageCount} message(s) (shared with the Desktop app; this cannot be undone).\nRe-run to confirm: agentlas uninstall ${agent.slug} --yes`);
|
|
75
|
+
return 1;
|
|
76
|
+
}
|
|
77
|
+
|
|
48
78
|
let deleted = false;
|
|
49
79
|
runWriteTransaction(db, () => {
|
|
50
80
|
deleted = db.prepare("DELETE FROM installed_agents WHERE id=?").run(agent.id).changes > 0;
|
|
@@ -58,7 +88,11 @@ function run(ctx, args) {
|
|
|
58
88
|
saveRoutes(routes);
|
|
59
89
|
}
|
|
60
90
|
} catch { /* 라우트 정리는 best-effort */ }
|
|
61
|
-
|
|
91
|
+
// 실제로 무엇이 사라졌는지 성공 줄에 남긴다 — 조용한 파괴 금지.
|
|
92
|
+
const cascade = chatCount
|
|
93
|
+
? (ko ? ` (대화 ${chatCount}개 · 메시지 ${messageCount}개 삭제됨)` : ` (deleted ${chatCount} chat(s), ${messageCount} message(s))`)
|
|
94
|
+
: "";
|
|
95
|
+
ctx.out(`${ctx.ui.green("✓")} ${ko ? "제거됨" : "Uninstalled"}: ${agent.slug}${cascade}`);
|
|
62
96
|
return 0;
|
|
63
97
|
}
|
|
64
98
|
ctx.err(ko ? "제거하지 못했습니다." : "Nothing was uninstalled.");
|
|
@@ -1,8 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* version — 터미널 버전과 함께 **지금 붙어 있는 Agentlas-OS Core** 를 보고한다.
|
|
4
|
+
*
|
|
5
|
+
* 왜 Core 도 같이 찍나 (2026-07-28):
|
|
6
|
+
* 터미널의 계약 표면(build/upload/workforce/context/…)은 Core 릴리스가 소유한다.
|
|
7
|
+
* Core 는 스스로 갱신되고(agentlas_cloud/update.py), 셸은 그때그때 해석된 루트에
|
|
8
|
+
* 붙는다. 그런데 `version`·`--where`·`doctor` 어디에서도 Core 를 찍지 않아서,
|
|
9
|
+
* 사용자도 지원 담당도 **어떤 Core 위에서 돌고 있는지 알 방법이 없었다**.
|
|
10
|
+
* "최신인데 안 맞는다" 류의 오진이 그 공백에서 나온다. 붙은 대상을 보여주는 것이
|
|
11
|
+
* 진단의 출발점이다.
|
|
12
|
+
*
|
|
13
|
+
* Core 가 없어도 실패하지 않는다 — 56개 명령은 Core 없이 동작한다. 없으면 없다고
|
|
14
|
+
* 정직하게 적는다(조용한 생략 금지).
|
|
15
|
+
*/
|
|
2
16
|
const { readVersion } = require("../agentlas-banner.cjs");
|
|
3
17
|
|
|
18
|
+
function coreLine() {
|
|
19
|
+
try {
|
|
20
|
+
const core = require("../agentlas-core-harness.cjs");
|
|
21
|
+
const root = core.resolveCoreRuntimeRoot();
|
|
22
|
+
if (!root) return "agentlas-os not attached (install: hephaestus update)";
|
|
23
|
+
const version = core.readCoreRuntimeVersion(root);
|
|
24
|
+
// 버전을 못 읽는 루트는 "낡음"이 아니라 "모름"이다. 두 상태를 구분해 적는다 —
|
|
25
|
+
// 모름을 그럴듯한 값으로 메꾸면 그 순간 진단이 거짓이 된다.
|
|
26
|
+
return `agentlas-os ${version || "unknown version"} (${root})`;
|
|
27
|
+
} catch (error) {
|
|
28
|
+
return `agentlas-os unavailable (${(error && error.message) || error})`;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
4
32
|
function run(ctx) {
|
|
5
33
|
ctx.out(`agentlas ${readVersion()}`);
|
|
34
|
+
ctx.out(coreLine());
|
|
6
35
|
return 0;
|
|
7
36
|
}
|
|
8
37
|
|
|
@@ -11,17 +11,12 @@
|
|
|
11
11
|
* parity().cmdHep(["hep-network", ...]) 호환 탈출구였는데, 그 parity 모듈이 아직
|
|
12
12
|
* v2에 없으므로 정직 정지로 안내만 한다(가짜 성공 금지).
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* 경로를 만들었다. 그 프로젝트-상태 기계(폴더 방문 기록/활성화 판정)는 아직 v2로
|
|
17
|
-
* 포팅되지 않았다. cmdWorkforce 가 projectPath 를 쓰는 곳은 buildChildEnv 의
|
|
18
|
-
* 프로젝트 dotenv 스코핑뿐이고(agentlas-workforce.cjs 1926행) 부재 시 fail-closed
|
|
19
|
-
* 의존이 없음을 확인했다 — 그래서 여기서는 cwd 를 projectPath 로 넘긴다(같은 폴더
|
|
20
|
-
* 의 .env/.env.local 이 비신뢰 소스로 한 번 더 스캔될 뿐, 권한이 넓어지지 않는다).
|
|
21
|
-
* 프로젝트-상태 모듈이 v2에 착륙하면 이 자리를 그 결과로 교체한다.
|
|
14
|
+
* Write-capable first contact bootstraps the exact current project through the
|
|
15
|
+
* canonical Core merge-only boundary. Read-only inspection remains passive.
|
|
22
16
|
*/
|
|
23
17
|
const { workforceRuntime } = require("../workforce/deps.cjs");
|
|
24
18
|
const { projectCwd } = require("../workforce/capture.cjs");
|
|
19
|
+
const { ensureTerminalProjectForExecutionCli } = require("../project/state.cjs");
|
|
25
20
|
|
|
26
21
|
function resolvePermission(ctx) {
|
|
27
22
|
// v1 resolveTerminalPermission 포팅: 저장된 `full` 은 세션 한정 계약이라
|
|
@@ -70,11 +65,16 @@ async function dispatch(ctx, command, args) {
|
|
|
70
65
|
const db = ctx.db();
|
|
71
66
|
const cwd = projectCwd();
|
|
72
67
|
const permission = resolvePermission(ctx);
|
|
68
|
+
const projectPath = ensureTerminalProjectForExecutionCli(
|
|
69
|
+
db,
|
|
70
|
+
cwd,
|
|
71
|
+
permission,
|
|
72
|
+
`terminal-${command}`,
|
|
73
|
+
) || cwd;
|
|
73
74
|
const runtime = workforceRuntime({ lang: ctx.lang, out: ctx.out });
|
|
74
75
|
const result = await runtime.cmdWorkforce(db, rest, runtimeOverride, {
|
|
75
76
|
cwd,
|
|
76
|
-
|
|
77
|
-
projectPath: cwd,
|
|
77
|
+
projectPath,
|
|
78
78
|
permission,
|
|
79
79
|
});
|
|
80
80
|
return result && result.ok ? 0 : 1;
|