agentlas 1.0.11 → 1.0.12
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 +28 -0
- package/engine/agentlas-input.cjs +61 -7
- package/engine/agentlas-workforce.cjs +859 -101
- package/engine/agentlas-workload-routing.cjs +29 -3
- package/engine/automation/daemon.cjs +9 -0
- package/engine/automation/store.cjs +22 -0
- package/engine/bootstrap-schema.sql +24 -0
- package/engine/commands/doctor.cjs +23 -1
- package/engine/commands/firm.cjs +59 -4
- package/engine/commands/help.cjs +8 -5
- package/engine/commands/list.cjs +25 -1
- package/engine/commands/run.cjs +65 -11
- package/engine/firms/orchestrate.cjs +10 -3
- package/engine/runtimes/overrides.cjs +91 -22
- package/engine/runtimes/resolve.cjs +38 -8
- package/engine/runtimes/roles.cjs +162 -0
- package/engine/sessions/orchestrator.cjs +2 -1
- package/engine/sessions/session.cjs +56 -20
- package/engine/storm/swarm.cjs +28 -12
- package/engine/ui/palette.cjs +40 -0
- package/engine/ui/repl.cjs +71 -9
- package/engine/workforce/capture.cjs +199 -14
- package/engine/workforce/deps.cjs +145 -29
- package/package.json +1 -1
|
@@ -32,49 +32,116 @@ const path = require("node:path");
|
|
|
32
32
|
const { userDataDir } = require("../core/paths.cjs");
|
|
33
33
|
const hubClient = require("../cloud/hub-client.cjs");
|
|
34
34
|
const detect = require("../runtimes/detect.cjs");
|
|
35
|
+
const { resolvedModelRole } = require("../runtimes/roles.cjs");
|
|
35
36
|
const capture = require("./capture.cjs");
|
|
36
37
|
|
|
37
38
|
// ── 런타임 해석 (v1 resolveRuntime의 워크포스 어댑터) ─────────────────────
|
|
38
|
-
// 워크포스 모듈이 기대하는 형태:
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
function
|
|
43
|
-
if (!
|
|
39
|
+
// 워크포스 모듈이 기대하는 형태:
|
|
40
|
+
// {mode:"cli", kind, model?, effort?} | {mode:"api", backend, model, effort?}
|
|
41
|
+
// 반환 정본은 orchestrator 런타임이며 roleRuntimes에 두 역할의 실제 실행 런타임을
|
|
42
|
+
// 동봉한다. stage별 선택은 agentlas-workforce.cjs가 수행한다.
|
|
43
|
+
function runtimeFromSelection(selection) {
|
|
44
|
+
if (!selection || !selection.kind) return null;
|
|
45
|
+
const common = {
|
|
46
|
+
model: selection.model || null,
|
|
47
|
+
effort: selection.effort || null,
|
|
48
|
+
capabilities: ["code", "tools", ...(selection.longContext ? ["long-context"] : [])],
|
|
49
|
+
efforts: [],
|
|
50
|
+
source: selection.sourceLayer || selection.source || null,
|
|
51
|
+
role: selection.role || null,
|
|
52
|
+
};
|
|
53
|
+
if (capture.RUNTIME_BIN[selection.kind]) {
|
|
54
|
+
return { mode: "cli", kind: selection.kind, ...common };
|
|
55
|
+
}
|
|
56
|
+
if (selection.kind === "byok" && selection.backend) {
|
|
57
|
+
return { mode: "api", backend: selection.backend, ...common };
|
|
58
|
+
}
|
|
59
|
+
if (selection.kind === "ollama") {
|
|
60
|
+
return { mode: "api", backend: "ollama", ...common };
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function legacyWorkforceRuntime(db, override) {
|
|
66
|
+
let selectedOverride = override;
|
|
67
|
+
if (!selectedOverride) {
|
|
44
68
|
try {
|
|
45
69
|
const saved = require("../agentlas-config.cjs").loadPrefs(userDataDir()).runtime;
|
|
46
|
-
if (
|
|
70
|
+
if (
|
|
71
|
+
saved &&
|
|
72
|
+
saved !== "auto" &&
|
|
73
|
+
capture.RUNTIME_BIN[saved] &&
|
|
74
|
+
capture.which(capture.RUNTIME_BIN[saved])
|
|
75
|
+
) {
|
|
76
|
+
selectedOverride = saved;
|
|
77
|
+
}
|
|
47
78
|
} catch { /* prefs 없음 — 사다리 계속 */ }
|
|
48
79
|
}
|
|
49
80
|
const ar = db ? detect.activeRuntimeRow(db) : null;
|
|
50
|
-
const
|
|
51
|
-
? {
|
|
52
|
-
|
|
81
|
+
const active = ar
|
|
82
|
+
? runtimeFromSelection({
|
|
83
|
+
role: "orchestrator",
|
|
53
84
|
kind: ar.kind,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
85
|
+
backend: ar.backend,
|
|
86
|
+
source: ar.source,
|
|
87
|
+
model: ar.model,
|
|
88
|
+
effort: null,
|
|
89
|
+
longContext: Boolean(ar.long_context),
|
|
90
|
+
sourceLayer: "active-runtime",
|
|
91
|
+
})
|
|
58
92
|
: null;
|
|
59
|
-
if (
|
|
60
|
-
if (!capture.RUNTIME_BIN[
|
|
61
|
-
const error = new Error(
|
|
93
|
+
if (selectedOverride) {
|
|
94
|
+
if (!capture.RUNTIME_BIN[selectedOverride]) {
|
|
95
|
+
const error = new Error(
|
|
96
|
+
`unknown workforce runtime: ${selectedOverride} (capture drivers: ${Object.keys(capture.RUNTIME_BIN).join(", ")})`,
|
|
97
|
+
);
|
|
62
98
|
error.code = "no_runtime";
|
|
63
99
|
throw error;
|
|
64
100
|
}
|
|
65
|
-
return
|
|
101
|
+
return active && active.mode === "cli" && active.kind === selectedOverride
|
|
102
|
+
? active
|
|
103
|
+
: { mode: "cli", kind: selectedOverride, model: null, effort: null };
|
|
66
104
|
}
|
|
67
|
-
if (
|
|
68
|
-
if (ar && ar.kind === "byok" && ar.backend) return { mode: "api", backend: ar.backend, model: ar.model };
|
|
69
|
-
if (ar && ar.kind === "ollama") return { mode: "api", backend: "ollama", model: ar.model };
|
|
105
|
+
if (active) return active;
|
|
70
106
|
for (const kind of Object.keys(capture.RUNTIME_BIN)) {
|
|
71
|
-
if (capture.which(capture.RUNTIME_BIN[kind]))
|
|
107
|
+
if (capture.which(capture.RUNTIME_BIN[kind])) {
|
|
108
|
+
return { mode: "cli", kind, model: null, effort: null, source: "detected" };
|
|
109
|
+
}
|
|
72
110
|
}
|
|
73
|
-
const error = new Error(
|
|
111
|
+
const error = new Error(
|
|
112
|
+
"no_runtime: no agent CLI or connected API runtime found (claude / codex / gemini / BYOK / Ollama).",
|
|
113
|
+
);
|
|
74
114
|
error.code = "no_runtime";
|
|
75
115
|
throw error;
|
|
76
116
|
}
|
|
77
117
|
|
|
118
|
+
// 사다리: 명시 override > model_roles[role] > 레거시 prefs/active/PATH.
|
|
119
|
+
// worker가 비어 있거나 inherit=1이면 roles.cjs가 orchestrator로만 승격한다.
|
|
120
|
+
function resolveWorkforceRuntime(db, override) {
|
|
121
|
+
if (override) {
|
|
122
|
+
const exact = legacyWorkforceRuntime(db, override);
|
|
123
|
+
return {
|
|
124
|
+
...exact,
|
|
125
|
+
role: "orchestrator",
|
|
126
|
+
roleRuntimes: {
|
|
127
|
+
orchestrator: { ...exact, role: "orchestrator" },
|
|
128
|
+
worker: { ...exact, role: "worker" },
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
const fallback = legacyWorkforceRuntime(db, null);
|
|
133
|
+
const orchestrator = runtimeFromSelection(resolvedModelRole(db, "orchestrator")) || fallback;
|
|
134
|
+
const worker = runtimeFromSelection(resolvedModelRole(db, "worker")) || orchestrator;
|
|
135
|
+
return {
|
|
136
|
+
...orchestrator,
|
|
137
|
+
role: "orchestrator",
|
|
138
|
+
roleRuntimes: {
|
|
139
|
+
orchestrator: { ...orchestrator, role: "orchestrator" },
|
|
140
|
+
worker: { ...worker, role: "worker" },
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
78
145
|
// ── 영수증 (v1 모놀리스의 JSONL 계약 포팅: userData 하위, 0700/0600) ──────
|
|
79
146
|
function receiptFile() {
|
|
80
147
|
return path.join(userDataDir(), "workforce-execution-receipts.jsonl");
|
|
@@ -112,13 +179,49 @@ function persistBenchmarkArtifact(artifact, executionIdHint) {
|
|
|
112
179
|
// 보존하되 runtimeIds=[] / status="observed-not-executable"로 광고한다 —
|
|
113
180
|
// 필요 capability에 대해 워크포스 모듈이 플래너 전에 fail-closed 하게 된다.
|
|
114
181
|
// 권한을 제조하는 것보다 정직 정지가 계약이다.
|
|
115
|
-
|
|
182
|
+
// 워커가 실제로 부여받을 수 있는 유일한 네이티브 도구 집합 — 읽기 전용.
|
|
183
|
+
// claude-code는 plan 모드(쓰기·실행 거부) + --allowedTools 로 이 경계를 강제할 수 있어
|
|
184
|
+
// "정확 per-tool 부착"이 증명된다. 쓰기·셸·네트워크·MCP는 여전히 증명 불가라 잠긴 채다.
|
|
185
|
+
const READ_ONLY_NATIVE_TOOLS = ["Read", "Grep", "Glob"];
|
|
186
|
+
const READ_ONLY_BUILTIN_TOOL_ID = "builtin:file-read";
|
|
187
|
+
const READ_ONLY_CAPABILITY_IDS = ["tool:file-read"];
|
|
188
|
+
const READ_ONLY_RUNTIME_IDS = ["runtime:claude-code"];
|
|
189
|
+
|
|
190
|
+
function readOnlyBuiltinToolRows(roster, runtimeId) {
|
|
191
|
+
if (!READ_ONLY_RUNTIME_IDS.includes(String(runtimeId || ""))) return [];
|
|
192
|
+
const rows = [];
|
|
193
|
+
for (const pinned of roster || []) {
|
|
194
|
+
// 허브가 이 릴리스에 파일 읽기를 허용했을 때만. deny면 존중하고 부여하지 않는다.
|
|
195
|
+
if (pinned?.permissionPolicy?.fileRead?.mode !== "manifest-allowlist") continue;
|
|
196
|
+
rows.push({
|
|
197
|
+
slotId: pinned.slotId,
|
|
198
|
+
agentReleaseId: pinned.agentReleaseId,
|
|
199
|
+
permissionPolicyDigest: pinned.permissionPolicyDigest,
|
|
200
|
+
provider: "builtin",
|
|
201
|
+
toolId: READ_ONLY_BUILTIN_TOOL_ID,
|
|
202
|
+
// 내장 도구는 MCP 서버가 없다. validateToolInventory는 serverId가 정확히 null이
|
|
203
|
+
// 아니면 거절한다(2026-07-27 라이브: "builtin" 문자열을 넣어 prepare 직후 전량 폐기).
|
|
204
|
+
serverId: null,
|
|
205
|
+
description: "Read-only project file access (Read/Grep/Glob, no write, no shell)",
|
|
206
|
+
inputSchemaDigest: null,
|
|
207
|
+
runtimeIds: [...READ_ONLY_RUNTIME_IDS],
|
|
208
|
+
selectiveEnforcement: "exact-tool-allowlist",
|
|
209
|
+
capabilityIds: [...READ_ONLY_CAPABILITY_IDS],
|
|
210
|
+
status: "ready",
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
return rows;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async function listWorkforceTools({ db, roster, cwd, env, timeoutMs, signal, runtimeId }) {
|
|
116
217
|
const mcp = require("../mcp/index.cjs");
|
|
218
|
+
// 읽기 전용 내장 도구는 MCP 서버 유무와 무관하게 항상 제공한다.
|
|
219
|
+
const builtinRows = readOnlyBuiltinToolRows(roster, runtimeId);
|
|
117
220
|
const servers = mcp.readConsentedSystemMcpServers(db, {
|
|
118
221
|
userDataDir: userDataDir(),
|
|
119
222
|
createRuntimeHome: false,
|
|
120
223
|
}).slice(0, 8);
|
|
121
|
-
if (!servers.length) return
|
|
224
|
+
if (!servers.length) return builtinRows;
|
|
122
225
|
const deadline = Date.now() + Math.max(50, Math.min(12_000, Number(timeoutMs) || 12_000));
|
|
123
226
|
const outcomes = new Array(servers.length);
|
|
124
227
|
let cursor = 0;
|
|
@@ -140,7 +243,7 @@ async function listWorkforceTools({ db, roster, cwd, env, timeoutMs, signal }) {
|
|
|
140
243
|
await Promise.all(Array.from({ length: Math.min(3, servers.length) }, () => worker()));
|
|
141
244
|
|
|
142
245
|
const safeId = /^[A-Za-z0-9][A-Za-z0-9_.$:/@+~-]{0,127}$/;
|
|
143
|
-
const rows = [];
|
|
246
|
+
const rows = [...builtinRows];
|
|
144
247
|
for (let index = 0; index < servers.length; index += 1) {
|
|
145
248
|
const server = servers[index];
|
|
146
249
|
const listed = outcomes[index];
|
|
@@ -444,9 +547,19 @@ function buildWorkforceDeps(ctx = {}) {
|
|
|
444
547
|
appendAuditReceipt,
|
|
445
548
|
persistBenchmarkArtifact,
|
|
446
549
|
listWorkforceTools,
|
|
447
|
-
//
|
|
448
|
-
//
|
|
449
|
-
|
|
550
|
+
// 호스트가 자기 도구를 빌려주는 결정은 허브 후보 자격과 무관하다.
|
|
551
|
+
// requiredToolCapabilities는 "이 허브 에이전트가 그 도구를 선언했는가"라는
|
|
552
|
+
// 후보 필터라서, 선언한 에이전트가 사실상 0이라 그걸 쓰면 후보가 0건이 된다
|
|
553
|
+
// (2026-07-27 실측: 그래서 리더가 절대 선언하지 않았고 부여가 영영 발동 안 됨).
|
|
554
|
+
// 읽기 권한 대여는 허브가 그 릴리스에 파일 읽기를 허용했는지만 보면 된다.
|
|
555
|
+
hostReadOnlyGrants: (roster, runtimeId) => readOnlyBuiltinToolRows(roster, runtimeId),
|
|
556
|
+
// 읽기 전용 내장 도구는 claude-code plan 모드 + --allowedTools/--disallowedTools로
|
|
557
|
+
// 정확 경계가 증명된다 → 부여 허용. 그 밖(쓰기·셸·네트워크·MCP)은 여전히 증명
|
|
558
|
+
// 불가라 거부한다. 증명 없이 true를 돌려주면 권한 제조가 된다.
|
|
559
|
+
supportsWorkforceToolAuthority: async ({ grantedToolIds }) =>
|
|
560
|
+
Array.isArray(grantedToolIds)
|
|
561
|
+
&& grantedToolIds.length > 0
|
|
562
|
+
&& grantedToolIds.every((id) => id === READ_ONLY_BUILTIN_TOOL_ID),
|
|
450
563
|
bindWorkforceGoal,
|
|
451
564
|
loadWorkforceGoalRuntime,
|
|
452
565
|
recordWorkforceGoalTurn,
|
|
@@ -472,6 +585,9 @@ module.exports = {
|
|
|
472
585
|
appendAuditReceipt,
|
|
473
586
|
persistBenchmarkArtifact,
|
|
474
587
|
listWorkforceTools,
|
|
588
|
+
readOnlyBuiltinToolRows,
|
|
589
|
+
READ_ONLY_NATIVE_TOOLS,
|
|
590
|
+
READ_ONLY_BUILTIN_TOOL_ID,
|
|
475
591
|
bindWorkforceGoal,
|
|
476
592
|
loadWorkforceGoalRuntime,
|
|
477
593
|
completeWorkforceGoal,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentlas",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.12",
|
|
4
4
|
"description": "Agentlas agent terminal — chat with your installed AI agents and teams from the terminal, Claude Code style. Standalone: no desktop app required.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"agentlas": "bin/agentlas.cjs"
|