agentlas 1.0.5 → 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 +18 -0
- package/engine/agentlas-workforce.cjs +82 -10
- 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,23 @@
|
|
|
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
|
+
|
|
3
21
|
## 1.0.5 — 2026-07-27
|
|
4
22
|
|
|
5
23
|
- A verifier that reports "no issues" as `[""]` instead of `[]` no longer
|
|
@@ -1754,8 +1754,45 @@ function create(deps = {}) {
|
|
|
1754
1754
|
return new Ui({ lang: lang || (typeof D.prefsLang === "function" ? D.prefsLang() : "en") });
|
|
1755
1755
|
}
|
|
1756
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
|
+
|
|
1757
1790
|
async function runModel(runtime, system, prompt, context) {
|
|
1758
|
-
|
|
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"
|
|
1759
1796
|
? D.projectContextSlice(context.cwd, context.task || "")
|
|
1760
1797
|
: "";
|
|
1761
1798
|
const effectiveSystem = localContextSlice
|
|
@@ -1780,12 +1817,12 @@ function create(deps = {}) {
|
|
|
1780
1817
|
cwd: context.cwd,
|
|
1781
1818
|
env: context.env,
|
|
1782
1819
|
permission: context.permission,
|
|
1783
|
-
model: context.modelPin || runtime.model || null,
|
|
1820
|
+
model: stageModelPin(context.stage) || context.modelPin || runtime.model || null,
|
|
1784
1821
|
effort: context.effortPin == null ? null : context.effortPin,
|
|
1785
1822
|
authorityMode,
|
|
1786
1823
|
}));
|
|
1787
1824
|
}
|
|
1788
|
-
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));
|
|
1789
1826
|
}
|
|
1790
1827
|
|
|
1791
1828
|
async function callHubTool(name, args) {
|
|
@@ -2002,6 +2039,11 @@ function create(deps = {}) {
|
|
|
2002
2039
|
const runtime = ctx.runtime || D.resolveRuntime(db, ctx.runtimeOverride);
|
|
2003
2040
|
const identity = runtimeIdentity(runtime, ctx.modelPin || null);
|
|
2004
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;
|
|
2005
2047
|
const permission = ctx.permission || "write";
|
|
2006
2048
|
const env = typeof D.buildChildEnv === "function" ? await D.buildChildEnv(db, {
|
|
2007
2049
|
projectPath: ctx.projectPath || null, permission, cwd, lang: ui.lang,
|
|
@@ -2134,7 +2176,7 @@ function create(deps = {}) {
|
|
|
2134
2176
|
: system;
|
|
2135
2177
|
let raw;
|
|
2136
2178
|
try {
|
|
2137
|
-
raw = await runModel(runtime, attemptSystem, attemptPrompt, modelContext);
|
|
2179
|
+
raw = await runModel(runtime, attemptSystem, attemptPrompt, { ...modelContext, stage: "leader" });
|
|
2138
2180
|
} catch (error) {
|
|
2139
2181
|
receipt.structuredModelAttempts.push({
|
|
2140
2182
|
schemaVersion: "agentlas.workforce-structured-model-attempt.v1",
|
|
@@ -2976,6 +3018,10 @@ function create(deps = {}) {
|
|
|
2976
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.";
|
|
2977
3019
|
const text = assertString(await runModel(runtime, [system, authorityDirective].join("\n\n"), prompt, {
|
|
2978
3020
|
...modelContext,
|
|
3021
|
+
// 무도구 호출은 패킷 입력만이 계약이다: 중립 cwd + 프로젝트 접지 차단.
|
|
3022
|
+
cwd: grantedToolIds.length ? modelContext.cwd : neutralCwd,
|
|
3023
|
+
projectGrounding: false,
|
|
3024
|
+
stage: "worker",
|
|
2979
3025
|
authorityMode: grantedToolIds.length ? "policy-filtered" : "no-authority",
|
|
2980
3026
|
grantedToolIds,
|
|
2981
3027
|
permissionPolicy: pinned.permissionPolicy,
|
|
@@ -3225,6 +3271,8 @@ function create(deps = {}) {
|
|
|
3225
3271
|
let verifierInvocationId = null;
|
|
3226
3272
|
let priorAttempt = null;
|
|
3227
3273
|
receipt.correctiveHistory = [];
|
|
3274
|
+
// 합성·검증도 무도구 핸드오프 파이프라인이다 — 워커와 동일한 격리 계약.
|
|
3275
|
+
const handoffModelContext = { ...modelContext, cwd: neutralCwd, projectGrounding: false };
|
|
3228
3276
|
if (!ctx.silent) ui.info(ui.lang === "ko" ? "합성 → 검증 단계" : "synthesis → verification");
|
|
3229
3277
|
for (let verifyAttempt = 1; verifyAttempt <= 2; verifyAttempt += 1) {
|
|
3230
3278
|
const synthesisStarted = nowIso(D.now);
|
|
@@ -3235,7 +3283,7 @@ function create(deps = {}) {
|
|
|
3235
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." : "",
|
|
3236
3284
|
].filter(Boolean).join("\n\n"), stableJson(verifyAttempt > 1
|
|
3237
3285
|
? { workOrder, synthesis: delegationPlan.synthesis, handoffs: outputs, priorSynthesis: priorAttempt.text, verifierRejection: priorAttempt.verification }
|
|
3238
|
-
: { workOrder, synthesis: delegationPlan.synthesis, handoffs: outputs }),
|
|
3286
|
+
: { workOrder, synthesis: delegationPlan.synthesis, handoffs: outputs }), handoffModelContext), "synthesis output", 1_000_000);
|
|
3239
3287
|
receipt.synthesis = {
|
|
3240
3288
|
schemaVersion: "agentlas.workforce-synthesis-receipt.v1",
|
|
3241
3289
|
receiptId: synthesisInvocationId,
|
|
@@ -3254,12 +3302,35 @@ function create(deps = {}) {
|
|
|
3254
3302
|
|
|
3255
3303
|
const verifierStarted = nowIso(D.now);
|
|
3256
3304
|
verifierInvocationId = `workforce-invocation:${crypto.randomUUID()}`;
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
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":[]}.',
|
|
3260
3311
|
"Use double-quoted valid JSON. Passing requires evidence for every criterion; do not rubber-stamp.",
|
|
3261
|
-
|
|
3262
|
-
|
|
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
|
+
}
|
|
3263
3334
|
receipt.verifier = {
|
|
3264
3335
|
schemaVersion: "agentlas.workforce-verifier-receipt.v1",
|
|
3265
3336
|
receiptId: verifierInvocationId,
|
|
@@ -3276,6 +3347,7 @@ function create(deps = {}) {
|
|
|
3276
3347
|
result: verification,
|
|
3277
3348
|
verdict: verification.status === "passed" ? "pass" : "fail",
|
|
3278
3349
|
attempt: verifyAttempt,
|
|
3350
|
+
structuredAttemptCount: verifierParseAttempts,
|
|
3279
3351
|
};
|
|
3280
3352
|
if (verification.status === "passed") break;
|
|
3281
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"
|