agentlas 1.0.4 → 1.0.6
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 +26 -0
- package/engine/agentlas-workforce.cjs +88 -11
- package/engine/ui/repl.cjs +25 -8
- package/engine/workforce/deps.cjs +3 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.6 — 2026-07-27
|
|
4
|
+
|
|
5
|
+
- **The startup banner is back.** The v2 REPL called `renderBanner` with the
|
|
6
|
+
v1 contract — no `ui`, and using the return value as a string — so it threw
|
|
7
|
+
a TypeError on every launch, and an argument-less `catch` disguised the
|
|
8
|
+
crash as a one-line `agentlas <version>` fallback. Nobody could see it,
|
|
9
|
+
including the tests. A banner contract test now guards it.
|
|
10
|
+
- **Per-stage model assignment.** One workforce run has stages with very
|
|
11
|
+
different demands: the leader must author a large exact schema (measured:
|
|
12
|
+
Haiku failed it twice in a row), while a worker writes one packet of prose
|
|
13
|
+
(measured: Haiku workers produced real code patches in the SWE run). The
|
|
14
|
+
engine used a single model for all of them. Stages now resolve their model
|
|
15
|
+
independently:
|
|
16
|
+
`AGENTLAS_WORKFORCE_MODEL_LEADER` (leader/selection/planner/refinement),
|
|
17
|
+
`AGENTLAS_WORKFORCE_MODEL_WORKER`, `AGENTLAS_WORKFORCE_MODEL_SYNTHESIS`,
|
|
18
|
+
`AGENTLAS_WORKFORCE_MODEL_VERIFIER`. Unset stages inherit the leader
|
|
19
|
+
setting, and with nothing set the behaviour is byte-identical to before.
|
|
20
|
+
|
|
21
|
+
## 1.0.5 — 2026-07-27
|
|
22
|
+
|
|
23
|
+
- A verifier that reports "no issues" as `[""]` instead of `[]` no longer
|
|
24
|
+
fails the run on a contract error. An empty string is a mis-spelling of
|
|
25
|
+
absence, not content, so it is normalized away before the issue contract
|
|
26
|
+
is checked — a passing verification stopped the last step of a real run
|
|
27
|
+
this way.
|
|
28
|
+
|
|
3
29
|
## 1.0.4 — 2026-07-27
|
|
4
30
|
|
|
5
31
|
**The hub boundary stops guessing.** Owner decision after live runs: rules
|
|
@@ -1487,7 +1487,12 @@ function validateVerifierResult(value) {
|
|
|
1487
1487
|
if (!["passed", "failed"].includes(check.status)) fail("verifier_invalid", "verifier check status is invalid");
|
|
1488
1488
|
assertString(check.evidence, "verifier.evidence", 2_000);
|
|
1489
1489
|
}
|
|
1490
|
-
|
|
1490
|
+
// 모델은 "지적 없음"을 []가 아니라 [""]로 쓰기도 한다(합격 판정 실측). 빈 문자열은
|
|
1491
|
+
// 내용이 아니라 부재의 오표기이므로 정규화해서 버린다 — 남은 항목만 계약 검사.
|
|
1492
|
+
const issues = assertArray(result.issues, "verifier.issues", 64)
|
|
1493
|
+
.filter((item) => !(typeof item === "string" && !item.trim()));
|
|
1494
|
+
issues.forEach((item, index) => assertString(item, `verifier.issues[${index}]`, 2_000));
|
|
1495
|
+
result.issues = issues;
|
|
1491
1496
|
return result;
|
|
1492
1497
|
}
|
|
1493
1498
|
|
|
@@ -1749,8 +1754,45 @@ function create(deps = {}) {
|
|
|
1749
1754
|
return new Ui({ lang: lang || (typeof D.prefsLang === "function" ? D.prefsLang() : "en") });
|
|
1750
1755
|
}
|
|
1751
1756
|
|
|
1757
|
+
/*
|
|
1758
|
+
* 스테이지별 모델 배정 (토큰 이코노미).
|
|
1759
|
+
*
|
|
1760
|
+
* 한 실행 안의 단계는 요구 난이도가 다르다: 리더/플래너는 거대한 스키마를 정확히
|
|
1761
|
+
* 작성해야 하고(실측 2026-07-27: Haiku는 워크오더 JSON에서 2회 연속 실패), 워커는
|
|
1762
|
+
* 자기 패킷 하나를 글로 쓰면 된다(같은 날 SWE 벤치에서 Haiku 워커가 실제 패치 생성).
|
|
1763
|
+
* 그런데 엔진은 전 단계에 모델 하나를 썼다 — 제일 어려운 단계에 맞추면 워커까지
|
|
1764
|
+
* 비싸고, 워커에 맞추면 리더가 죽는다.
|
|
1765
|
+
*
|
|
1766
|
+
* 설정은 명시적이며 기본값은 무변경이다(미설정 시 기존 동작 그대로):
|
|
1767
|
+
* AGENTLAS_WORKFORCE_MODEL_LEADER 리더/선발/플래너/워크오더 정제
|
|
1768
|
+
* AGENTLAS_WORKFORCE_MODEL_WORKER 워커(중첩 팀 워커 포함)
|
|
1769
|
+
* AGENTLAS_WORKFORCE_MODEL_SYNTHESIS 합성
|
|
1770
|
+
* AGENTLAS_WORKFORCE_MODEL_VERIFIER 검증
|
|
1771
|
+
* 미지정 스테이지는 리더 설정 → 명시 modelPin → 런타임 기본 순으로 내려간다.
|
|
1772
|
+
*/
|
|
1773
|
+
const STAGE_MODEL_ENV = Object.freeze({
|
|
1774
|
+
leader: "AGENTLAS_WORKFORCE_MODEL_LEADER",
|
|
1775
|
+
worker: "AGENTLAS_WORKFORCE_MODEL_WORKER",
|
|
1776
|
+
synthesis: "AGENTLAS_WORKFORCE_MODEL_SYNTHESIS",
|
|
1777
|
+
verifier: "AGENTLAS_WORKFORCE_MODEL_VERIFIER",
|
|
1778
|
+
});
|
|
1779
|
+
|
|
1780
|
+
function stageModelPin(stage, env = process.env) {
|
|
1781
|
+
const key = STAGE_MODEL_ENV[stage];
|
|
1782
|
+
const exact = key ? String(env[key] || "").trim() : "";
|
|
1783
|
+
if (exact) return exact;
|
|
1784
|
+
// 워커/합성/검증에 별도 지정이 없으면 리더 설정을 상속한다 — 리더만 올려도
|
|
1785
|
+
// 전 단계가 일관되게 동작하고, 아무것도 없으면 기존 경로와 완전히 동일하다.
|
|
1786
|
+
const leader = String(env[STAGE_MODEL_ENV.leader] || "").trim();
|
|
1787
|
+
return leader || null;
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1752
1790
|
async function runModel(runtime, system, prompt, context) {
|
|
1753
|
-
|
|
1791
|
+
// Core context slice는 리더 단계(작업 분석/선택/플래너/goal)의 프로젝트 접지다.
|
|
1792
|
+
// 핀 워커·합성·검증 호출의 계약 입력은 패킷/핸드오프뿐이므로(EXECUTION AUTHORITY
|
|
1793
|
+
// 고지와 동일 원칙) projectGrounding=false로 붙이지 않는다 — 2026-07-27 실측:
|
|
1794
|
+
// 무도구 콘텐츠 브리프에 프로젝트 파일 지도가 붙자 산출물이 디렉터리 나열로 샜다.
|
|
1795
|
+
const localContextSlice = context.projectGrounding !== false && typeof D.projectContextSlice === "function"
|
|
1754
1796
|
? D.projectContextSlice(context.cwd, context.task || "")
|
|
1755
1797
|
: "";
|
|
1756
1798
|
const effectiveSystem = localContextSlice
|
|
@@ -1775,12 +1817,12 @@ function create(deps = {}) {
|
|
|
1775
1817
|
cwd: context.cwd,
|
|
1776
1818
|
env: context.env,
|
|
1777
1819
|
permission: context.permission,
|
|
1778
|
-
model: context.modelPin || runtime.model || null,
|
|
1820
|
+
model: stageModelPin(context.stage) || context.modelPin || runtime.model || null,
|
|
1779
1821
|
effort: context.effortPin == null ? null : context.effortPin,
|
|
1780
1822
|
authorityMode,
|
|
1781
1823
|
}));
|
|
1782
1824
|
}
|
|
1783
|
-
return normalizeModelText(await D.runApi(runtime.backend, context.modelPin || runtime.model, effectiveSystem, prompt));
|
|
1825
|
+
return normalizeModelText(await D.runApi(runtime.backend, stageModelPin(context.stage) || context.modelPin || runtime.model, effectiveSystem, prompt));
|
|
1784
1826
|
}
|
|
1785
1827
|
|
|
1786
1828
|
async function callHubTool(name, args) {
|
|
@@ -1997,6 +2039,11 @@ function create(deps = {}) {
|
|
|
1997
2039
|
const runtime = ctx.runtime || D.resolveRuntime(db, ctx.runtimeOverride);
|
|
1998
2040
|
const identity = runtimeIdentity(runtime, ctx.modelPin || null);
|
|
1999
2041
|
const cwd = ctx.cwd || (typeof D.projectCwd === "function" ? D.projectCwd() : process.cwd());
|
|
2042
|
+
// 무도구(no-authority) 자식 CLI를 프로젝트 작업트리에서 실행하면 자식 CLI가
|
|
2043
|
+
// 프로젝트 설정·프로젝트 지시문·디렉터리 문맥을 스스로 삼킨다(2026-07-27 실측:
|
|
2044
|
+
// 프로젝트 설정 경고와 함께 워커 exit 1, 콘텐츠 브리프가 워크스페이스 코딩
|
|
2045
|
+
// 과제처럼 수행됨). 파일 권한이 없는 호출은 전용 중립 폴더에서 실행한다.
|
|
2046
|
+
const neutralCwd = typeof D.runCwd === "function" ? D.runCwd() : cwd;
|
|
2000
2047
|
const permission = ctx.permission || "write";
|
|
2001
2048
|
const env = typeof D.buildChildEnv === "function" ? await D.buildChildEnv(db, {
|
|
2002
2049
|
projectPath: ctx.projectPath || null, permission, cwd, lang: ui.lang,
|
|
@@ -2129,7 +2176,7 @@ function create(deps = {}) {
|
|
|
2129
2176
|
: system;
|
|
2130
2177
|
let raw;
|
|
2131
2178
|
try {
|
|
2132
|
-
raw = await runModel(runtime, attemptSystem, attemptPrompt, modelContext);
|
|
2179
|
+
raw = await runModel(runtime, attemptSystem, attemptPrompt, { ...modelContext, stage: "leader" });
|
|
2133
2180
|
} catch (error) {
|
|
2134
2181
|
receipt.structuredModelAttempts.push({
|
|
2135
2182
|
schemaVersion: "agentlas.workforce-structured-model-attempt.v1",
|
|
@@ -2971,6 +3018,10 @@ function create(deps = {}) {
|
|
|
2971
3018
|
: "EXECUTION AUTHORITY: zero tools are granted to this invocation — no file system, no shell, no web, no MCP, no subagents. Never emit tool-call syntax or XML-like invocation markup, and never explore or wait for a workspace. Author the complete deliverable directly in this reply as plain text or markdown, using only the packet inputs provided.";
|
|
2972
3019
|
const text = assertString(await runModel(runtime, [system, authorityDirective].join("\n\n"), prompt, {
|
|
2973
3020
|
...modelContext,
|
|
3021
|
+
// 무도구 호출은 패킷 입력만이 계약이다: 중립 cwd + 프로젝트 접지 차단.
|
|
3022
|
+
cwd: grantedToolIds.length ? modelContext.cwd : neutralCwd,
|
|
3023
|
+
projectGrounding: false,
|
|
3024
|
+
stage: "worker",
|
|
2974
3025
|
authorityMode: grantedToolIds.length ? "policy-filtered" : "no-authority",
|
|
2975
3026
|
grantedToolIds,
|
|
2976
3027
|
permissionPolicy: pinned.permissionPolicy,
|
|
@@ -3220,6 +3271,8 @@ function create(deps = {}) {
|
|
|
3220
3271
|
let verifierInvocationId = null;
|
|
3221
3272
|
let priorAttempt = null;
|
|
3222
3273
|
receipt.correctiveHistory = [];
|
|
3274
|
+
// 합성·검증도 무도구 핸드오프 파이프라인이다 — 워커와 동일한 격리 계약.
|
|
3275
|
+
const handoffModelContext = { ...modelContext, cwd: neutralCwd, projectGrounding: false };
|
|
3223
3276
|
if (!ctx.silent) ui.info(ui.lang === "ko" ? "합성 → 검증 단계" : "synthesis → verification");
|
|
3224
3277
|
for (let verifyAttempt = 1; verifyAttempt <= 2; verifyAttempt += 1) {
|
|
3225
3278
|
const synthesisStarted = nowIso(D.now);
|
|
@@ -3230,7 +3283,7 @@ function create(deps = {}) {
|
|
|
3230
3283
|
verifyAttempt > 1 ? "CORRECTIVE SYNTHESIS MODE: a pinned verifier rejected the prior synthesis. Repair the deliverable so every criterion is satisfied using only the existing worker handoffs. Never invent work that did not run." : "",
|
|
3231
3284
|
].filter(Boolean).join("\n\n"), stableJson(verifyAttempt > 1
|
|
3232
3285
|
? { workOrder, synthesis: delegationPlan.synthesis, handoffs: outputs, priorSynthesis: priorAttempt.text, verifierRejection: priorAttempt.verification }
|
|
3233
|
-
: { workOrder, synthesis: delegationPlan.synthesis, handoffs: outputs }),
|
|
3286
|
+
: { workOrder, synthesis: delegationPlan.synthesis, handoffs: outputs }), handoffModelContext), "synthesis output", 1_000_000);
|
|
3234
3287
|
receipt.synthesis = {
|
|
3235
3288
|
schemaVersion: "agentlas.workforce-synthesis-receipt.v1",
|
|
3236
3289
|
receiptId: synthesisInvocationId,
|
|
@@ -3249,12 +3302,35 @@ function create(deps = {}) {
|
|
|
3249
3302
|
|
|
3250
3303
|
const verifierStarted = nowIso(D.now);
|
|
3251
3304
|
verifierInvocationId = `workforce-invocation:${crypto.randomUUID()}`;
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3305
|
+
// 검증자 JSON도 다른 구조화 단계처럼 1회 유계 스키마 교정을 받는다. 2026-07-27
|
|
3306
|
+
// 실측: 정직한 불합격 판정이 2000자 초과 issues 문자열 하나 때문에
|
|
3307
|
+
// invalid_contract 크래시가 되어 판정·교정 재합성이 통째로 증발했다.
|
|
3308
|
+
// 교정 후에도 스키마가 깨지면 조용한 절단 없이 정직하게 던진다.
|
|
3309
|
+
const verifierSchemaRequirements = [
|
|
3310
|
+
'Return exactly one JSON object: {"schemaVersion":"agentlas.workforce-verification.v1","status":"passed|failed","checks":[{"checkId":"check:<id>","status":"passed|failed","evidence":"..."}],"issues":[]}.',
|
|
3255
3311
|
"Use double-quoted valid JSON. Passing requires evidence for every criterion; do not rubber-stamp.",
|
|
3256
|
-
|
|
3257
|
-
|
|
3312
|
+
"Every issues entry and every evidence value must be a plain string of at most 1900 characters; cite handoffs by slot id instead of quoting them at length.",
|
|
3313
|
+
].join("\n");
|
|
3314
|
+
let verifierPrompt = stableJson({ workOrder, criteria: delegationPlan.verifier.criteria, handoffs: outputs, synthesis: finalText });
|
|
3315
|
+
let verifierParseAttempts = 0;
|
|
3316
|
+
verification = null;
|
|
3317
|
+
while (verification === null) {
|
|
3318
|
+
verifierParseAttempts += 1;
|
|
3319
|
+
const verifierRaw = await runModel(runtime, [
|
|
3320
|
+
"You are the top-level host LLM verifier for this Agentlas workforce run.",
|
|
3321
|
+
"Evaluate the synthesis against every criterion and worker handoff.",
|
|
3322
|
+
verifierSchemaRequirements,
|
|
3323
|
+
verifierParseAttempts > 1 ? "STRUCTURED OUTPUT REPAIR MODE: repair the schema and field bounds only; keep your verdict and findings." : "",
|
|
3324
|
+
].filter(Boolean).join("\n\n"), verifierPrompt, handoffModelContext);
|
|
3325
|
+
try {
|
|
3326
|
+
verification = validateVerifierResult(parseModelObject(verifierRaw, "workforce verifier"));
|
|
3327
|
+
} catch (error) {
|
|
3328
|
+
if (!(error instanceof WorkforceContractError) || verifierParseAttempts >= MAX_STRUCTURED_MODEL_ATTEMPTS) throw error;
|
|
3329
|
+
const repair = buildSchemaRepairPrompt(error, verifierSchemaRequirements, verifierRaw);
|
|
3330
|
+
if (!repair.prompt) throw error;
|
|
3331
|
+
verifierPrompt = repair.prompt;
|
|
3332
|
+
}
|
|
3333
|
+
}
|
|
3258
3334
|
receipt.verifier = {
|
|
3259
3335
|
schemaVersion: "agentlas.workforce-verifier-receipt.v1",
|
|
3260
3336
|
receiptId: verifierInvocationId,
|
|
@@ -3271,6 +3347,7 @@ function create(deps = {}) {
|
|
|
3271
3347
|
result: verification,
|
|
3272
3348
|
verdict: verification.status === "passed" ? "pass" : "fail",
|
|
3273
3349
|
attempt: verifyAttempt,
|
|
3350
|
+
structuredAttemptCount: verifierParseAttempts,
|
|
3274
3351
|
};
|
|
3275
3352
|
if (verification.status === "passed") break;
|
|
3276
3353
|
if (verifyAttempt === 1) {
|
package/engine/ui/repl.cjs
CHANGED
|
@@ -38,13 +38,9 @@ async function startRepl(ctx, opts = {}) {
|
|
|
38
38
|
const ui = ctx.uiInstance;
|
|
39
39
|
const db = ctx.db();
|
|
40
40
|
|
|
41
|
-
try {
|
|
42
|
-
process.stdout.write(renderBanner({ version: readVersion(), lang: ctx.lang }) + "\n");
|
|
43
|
-
} catch {
|
|
44
|
-
ctx.out(`agentlas ${readVersion()}`);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
41
|
// 첫 실행 온보딩 (언어 → 런타임 → 권한). setup 명령으로 언제든 재실행 가능.
|
|
42
|
+
// 스플래시는 이 뒤에 그린다 — 배너가 광고하는 런타임·권한은 이번 세션에 실제로
|
|
43
|
+
// 적용될 값이어야 한다. 첫 실행 사용자는 마법사가 먼저 마스코트를 띄운다.
|
|
48
44
|
if (!ctx.prefs.onboarded && process.stdin.isTTY) {
|
|
49
45
|
const wizardRl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
50
46
|
try {
|
|
@@ -88,9 +84,30 @@ async function startRepl(ctx, opts = {}) {
|
|
|
88
84
|
return session;
|
|
89
85
|
};
|
|
90
86
|
|
|
87
|
+
/*
|
|
88
|
+
* renderBanner는 ui.line으로 직접 그리고 아무것도 반환하지 않는다(ctx는 {ui,...} 형태).
|
|
89
|
+
* v2 REPL이 이걸 "문자열을 반환하는 v1 배너"로 호출해 매 실행 TypeError로 죽었고,
|
|
90
|
+
* 인자 없는 catch가 그 크래시를 `agentlas <version>` 한 줄로 위장해 왔다 —
|
|
91
|
+
* 스플래시 전체가 사라진 걸 사람도 게이트도 못 봤다. 실패 사유는 이제 남긴다.
|
|
92
|
+
*/
|
|
93
|
+
try {
|
|
94
|
+
let runtimeLabel = "—";
|
|
95
|
+
try { runtimeLabel = resolveRt().kind; } catch { /* no_runtime: 첫 턴에서 정직 정지 */ }
|
|
96
|
+
let subjectLabel;
|
|
97
|
+
try {
|
|
98
|
+
const subject = opts.agent ? findAgent(db, opts.agent) : pickDefaultAgent(db);
|
|
99
|
+
if (subject) subjectLabel = subject.slug;
|
|
100
|
+
} catch { /* 표시용 — 못 정해도 배너는 그린다 */ }
|
|
101
|
+
renderBanner({ ui, version: readVersion(), runtimeLabel, subjectLabel, permission, cwd: process.cwd() });
|
|
102
|
+
} catch (e) {
|
|
103
|
+
ctx.out(`agentlas ${readVersion()}`);
|
|
104
|
+
ctx.err(ui.c.dim(`banner failed: ${(e && e.message) || e}`));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// 배너가 런타임·권한·작업 폴더를 이미 보여준다 — 여기서는 배너에 없는 것만.
|
|
91
108
|
ctx.out(ui.c.dim(en
|
|
92
|
-
? `v2 engine ·
|
|
93
|
-
: `v2 엔진 ·
|
|
109
|
+
? `v2 engine · parallel ≤${maxParallel()} — /help, /sessions, /quit`
|
|
110
|
+
: `v2 엔진 · 동시 ≤${maxParallel()} — /help, /sessions, /quit`));
|
|
94
111
|
|
|
95
112
|
if (opts.agent) {
|
|
96
113
|
try {
|
|
@@ -421,6 +421,9 @@ function buildWorkforceDeps(ctx = {}) {
|
|
|
421
421
|
prefsLang: () => ctx.lang || "en",
|
|
422
422
|
userDataDir,
|
|
423
423
|
projectCwd: capture.projectCwd,
|
|
424
|
+
// 무도구 핀 호출 전용 중립 작업 폴더 — 프로젝트 작업트리의 설정/지시문/디렉터리
|
|
425
|
+
// 문맥이 자식 CLI로 새는 것을 끊는다(agentlas-workforce.cjs neutralCwd 계약).
|
|
426
|
+
runCwd: capture.runCwd,
|
|
424
427
|
cloudSessionCookie: hubClient.cloudSessionCookie,
|
|
425
428
|
// v1과 동일: callHubTool은 주입하지 않는다. 워크포스 모듈 내부의 jsonrpc 경로가
|
|
426
429
|
// 거절 코드 원문 전파·retryClass 계약을 소유하며, fetchHub는 버퍼드
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentlas",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
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"
|