agentlas 1.0.29 → 1.0.35

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.
Files changed (50) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/bin/agentlas.cjs +15 -0
  3. package/engine/agentlas-cloud-runtime.cjs +54 -4
  4. package/engine/agentlas-input.cjs +10 -1
  5. package/engine/agentlas-judgment.cjs +0 -0
  6. package/engine/agentlas-native-host.cjs +69 -4
  7. package/engine/agentlas-ui.cjs +2 -0
  8. package/engine/agentlas-workforce.cjs +28 -5
  9. package/engine/agentlas.cjs +64 -2
  10. package/engine/agents/builder.cjs +84 -0
  11. package/engine/agents/router.cjs +8 -2
  12. package/engine/automation/launchd.cjs +131 -0
  13. package/engine/bootstrap-schema.sql +6 -44
  14. package/engine/browser/cdp.cjs +188 -0
  15. package/engine/browser/vault.cjs +124 -0
  16. package/engine/cli-output.cjs +262 -0
  17. package/engine/cloud-assets/package.cjs +5 -1
  18. package/engine/commands/automation.cjs +37 -1
  19. package/engine/commands/browser.cjs +166 -8
  20. package/engine/commands/build.cjs +101 -20
  21. package/engine/commands/connect.cjs +162 -9
  22. package/engine/commands/creds.cjs +16 -2
  23. package/engine/commands/doctor.cjs +20 -1
  24. package/engine/commands/document.cjs +79 -0
  25. package/engine/commands/graph.cjs +50 -10
  26. package/engine/commands/help.cjs +8 -6
  27. package/engine/commands/index.cjs +1 -0
  28. package/engine/commands/list.cjs +10 -1
  29. package/engine/commands/project.cjs +79 -16
  30. package/engine/commands/roles.cjs +10 -2
  31. package/engine/commands/telegram.cjs +23 -16
  32. package/engine/core/desktop-core-fetch.cjs +98 -0
  33. package/engine/core/desktop-core.cjs +170 -0
  34. package/engine/graph/ask-model.cjs +29 -1
  35. package/engine/graph/interview.cjs +105 -20
  36. package/engine/graph/layout.cjs +36 -34
  37. package/engine/graph/vocabulary.generated.cjs +1 -1
  38. package/engine/hephaestus/runtime.cjs +10 -2
  39. package/engine/project/controller.cjs +8 -8
  40. package/engine/project/team.cjs +99 -0
  41. package/engine/runtime-refusal.cjs +71 -0
  42. package/engine/runtimes/detect.cjs +3 -0
  43. package/engine/runtimes/resolve.cjs +1 -1
  44. package/engine/sessions/session.cjs +15 -2
  45. package/engine/telegram/connect.cjs +202 -0
  46. package/engine/ui/palette.cjs +1 -0
  47. package/engine/ui/repl.cjs +112 -1
  48. package/engine/vendor/desktop-core.manifest.json +7 -0
  49. package/engine/workforce/capture.cjs +51 -1
  50. package/package.json +3 -2
@@ -132,6 +132,36 @@ async function startRepl(ctx, opts = {}) {
132
132
  let effortOverride = opts.effort || null;
133
133
 
134
134
  const recoverPresentation = (operation, error) => {
135
+ /*
136
+ * 정직 정지(honestStop)는 복구 대상이 아니다 (2026-08-06 실사용 실측).
137
+ * 실제 REPL에 자연어 지시를 하면 "이 프로젝트에 팀이 없다"는 정직한 안내가
138
+ * "One이 복구 중" 한 줄로 삼켜졌다 — 사용자는 무엇이 왜 안 되는지, 무엇을
139
+ * 하면 되는지 전혀 못 봤다(빈 답보다 나쁜 침묵). 정직 정지는 그 사유를
140
+ * 그대로 보여주고, 어떻게 실행하는지까지 덧붙인다. One 복구는 예상 못 한
141
+ * 실패에만 쓴다. (사람용 문장과 기계 판단은 다른 필드 — 스케줄러와 같은 원칙.)
142
+ */
143
+ if (error && (error.honestStop || error.code)) {
144
+ // 공유 controller 모듈은 lang을 모른다 — 기계 code로 여기서 현지화한다.
145
+ const KO_REASON = {
146
+ project_team_empty: "이 프로젝트에 에이전트 팀이 없습니다. Agentlas Desktop의 Work에서 에이전트를 넣고 다시 시도하세요.",
147
+ project_not_connected: "이 폴더는 Agentlas 프로젝트에 연결돼 있지 않습니다. Desktop Work에서 연결하거나, 특정 에이전트를 지정해 바로 실행하세요.",
148
+ project_ambiguous: "이 폴더에 Agentlas 프로젝트가 둘 이상 연결돼 있습니다. 소스 연결을 하나만 남기고 다시 시도하세요.",
149
+ project_team_unreadable: "이 프로젝트의 에이전트 팀을 읽을 수 없습니다. Agentlas Desktop에서 프로젝트를 열어 팀을 다시 저장하세요.",
150
+ project_teams_unsupported: "이 Agentlas 데이터 저장소는 아직 프로젝트 팀을 지원하지 않습니다. 최신 Agentlas Desktop을 한 번 실행한 뒤 다시 시도하세요.",
151
+ };
152
+ const reason = (!en && error.code && KO_REASON[error.code]) || String(error.message || error);
153
+ ui.line(ui.c.amber("✖ ") + reason);
154
+ // 데스크탑을 강제하지 않는다 — 터미널에서 바로 프로젝트를 연결한다(오너 원칙).
155
+ const connectHint = (error.code === "project_not_connected" || error.code === "project_team_empty");
156
+ ui.line(ui.c.dim(en
157
+ ? (connectHint
158
+ ? "Connect this folder standalone: agentlas project use <agent> · list agents: /list · or run one directly: agentlas run <agent> \"<task>\"."
159
+ : "Run a specific agent directly: agentlas run <agent> \"<task>\" · list agents: /list.")
160
+ : (connectHint
161
+ ? "이 폴더를 바로 연결: agentlas project use <에이전트> · 에이전트 목록: /list · 또는 하나만 바로 실행: agentlas run <에이전트> \"<할 일>\"."
162
+ : "특정 에이전트로 바로 실행: agentlas run <에이전트> \"<할 일>\" · 에이전트 목록: /list.")));
163
+ return;
164
+ }
135
165
  const active = orch.active();
136
166
  const evidence = String((error && error.message) || error || "").slice(0, 8000);
137
167
  if (active && !active.isBusy()) {
@@ -252,10 +282,37 @@ async function startRepl(ctx, opts = {}) {
252
282
  },
253
283
  });
254
284
  const PROMPT = "› ";
285
+ /*
286
+ * 빈 입력 고스트 힌트 (2026-08-06, 레퍼런스 REPL 대조에서 나온 유도 격차).
287
+ * claude-code류 REPL은 빈 입력줄에 무엇을 칠 수 있는지 흐리게 보여준다 —
288
+ * 우리는 `› ` 뿐이라 첫 사용자가 / 팔레트·@ 멘션·? 도움의 존재를 알 길이
289
+ * 없었다. 커서 "뒤"에 dim 힌트를 쓰고 커서를 되돌린다. 어떤 키든 처음
290
+ * 눌리는 순간 커서-뒤 지우기(CSI K)로 걷어내므로 readline 의 echo·팔레트
291
+ * 오버레이와 겹치지 않는다. 비TTY·바쁜 세션에서는 그리지 않는다.
292
+ */
293
+ const { visWidth } = require("../agentlas-composer.cjs");
294
+ const GHOST_HINT = en
295
+ ? "type a task · / commands · @ files · ? shortcuts"
296
+ : "할 일을 문장으로 · / 명령 · @ 파일 · ? 단축키";
297
+ let ghostVisible = false;
298
+ const drawGhost = () => {
299
+ if (!process.stdout.isTTY || rl.line) return;
300
+ const width = visWidth(GHOST_HINT);
301
+ const room = (process.stdout.columns || 80) - visWidth(PROMPT) - 2;
302
+ if (width > room) return; // 좁은 터미널에서는 유도보다 입력이 우선이다
303
+ ui.write(ui.c.faint(GHOST_HINT) + `\x1b[${width}D`);
304
+ ghostVisible = true;
305
+ };
306
+ const clearGhost = () => {
307
+ if (!ghostVisible) return;
308
+ ghostVisible = false;
309
+ if (process.stdout.isTTY) ui.write("\x1b[K");
310
+ };
255
311
  const prompt = () => {
256
312
  if (!renderer.session || !renderer.session.isBusy()) {
257
313
  rl.setPrompt(PROMPT);
258
314
  rl.prompt();
315
+ drawGhost();
259
316
  }
260
317
  };
261
318
 
@@ -289,6 +346,8 @@ async function startRepl(ctx, opts = {}) {
289
346
  },
290
347
  });
291
348
  const onShortcutKey = (str, key) => {
349
+ // 고스트 힌트는 어떤 키가 와도 readline 이 echo 하기 전에 걷어낸다.
350
+ clearGhost();
292
351
  if (!permissionShortcut.handleKey(str, key)) return;
293
352
  swallowCompletion = true;
294
353
  setImmediate(() => { swallowCompletion = false; });
@@ -329,10 +388,62 @@ async function startRepl(ctx, opts = {}) {
329
388
  }
330
389
  };
331
390
 
391
+ let emptyEnters = 0;
332
392
  rl.on("line", (line) => {
333
393
  sigints = 0;
334
394
  const input = line.trim();
335
- if (!input) { prompt(); return; }
395
+ if (!input) {
396
+ /*
397
+ * 빈 Enter 유도 (2026-08-06 레퍼런스 대조): 1회는 조용히 — 실수로 친
398
+ * Enter 마다 잔소리하면 소음이다. 연달아 두 번이면 길을 잃은 것이므로
399
+ * 다음 행선지를 한 줄로 보여준다.
400
+ */
401
+ emptyEnters += 1;
402
+ if (emptyEnters >= 2) {
403
+ emptyEnters = 0;
404
+ ui.line(ui.c.dim(en
405
+ ? "Type a task in plain words, / for commands, ? for shortcuts."
406
+ : "할 일을 그냥 문장으로 치세요. / 는 명령, ? 는 단축키입니다."));
407
+ }
408
+ prompt();
409
+ return;
410
+ }
411
+ emptyEnters = 0;
412
+
413
+ /*
414
+ * `?` 단축키 도움 (2026-08-06): 첫 화면 어디에도 키 조작법이 없었다 —
415
+ * Shift-Tab 권한 순환·조종 큐·@멘션은 아는 사람만 썼다. 한 글자로 요약을
416
+ * 준다(claude-code 의 ? 관례).
417
+ */
418
+ if (input === "?") {
419
+ const rowsHelp = en ? [
420
+ ["/", "command palette (type to filter, ↑↓ pick, Tab complete, Esc close)"],
421
+ ["Tab", "complete commands, agent names, @file paths, session keys"],
422
+ ["@path", "mention a file anywhere in your sentence"],
423
+ ["↑ / ↓", "input history (kept across sessions)"],
424
+ ["Shift-Tab", "cycle permission read → write (twice for full)"],
425
+ ["Ctrl-C", "interrupt the running turn · press twice to quit"],
426
+ ["typing while running", "queues steering for the current turn"],
427
+ ["/sessions · /s <n>", "list · switch parallel sessions"],
428
+ ] : [
429
+ ["/", "명령 팔레트 (입력=검색, ↑↓ 선택, Tab 완성, Esc 닫기)"],
430
+ ["Tab", "명령·에이전트 이름·@파일 경로·세션 키 완성"],
431
+ ["@경로", "문장 어디서든 파일 멘션"],
432
+ ["↑ / ↓", "입력 히스토리 (세션 넘어 유지)"],
433
+ ["Shift-Tab", "권한 순환 read → write (두 번이면 full)"],
434
+ ["Ctrl-C", "실행 중 턴 중단 · 두 번이면 종료"],
435
+ ["실행 중 타이핑", "현재 턴에 조종 지시로 쌓임"],
436
+ ["/sessions · /s <n>", "병렬 세션 목록 · 전환"],
437
+ ];
438
+ // 한글 키 라벨은 전각 폭 — padEnd(코드포인트 수)로 맞추면 열이 어긋난다.
439
+ const keyWidth = Math.max(...rowsHelp.map(([k]) => visWidth(k)));
440
+ ui.line(ui.c.bold(en ? "Shortcuts" : "단축키"));
441
+ for (const [k, desc] of rowsHelp) {
442
+ ui.line(" " + ui.c.blue(k) + " ".repeat(keyWidth - visWidth(k)) + " " + ui.c.dim(desc));
443
+ }
444
+ prompt();
445
+ return;
446
+ }
336
447
 
337
448
  if (input.startsWith("/")) {
338
449
  try {
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": "2",
3
+ "url": "https://github.com/agentlas-ai/agentlas-terminal/releases/download/desktop-core-v2/desktop-core.tar.gz",
4
+ "sha256": "782760c34af1d06b9efd212d48ba032fe46907bdc8bc836025c44fc658968d6f",
5
+ "sizeBytes": 12295321,
6
+ "writtenAt": "2026-08-09T21:43:34.707Z"
7
+ }
@@ -18,6 +18,7 @@
18
18
  * 프로젝트 .env가 보호 키(타임아웃/캡처 상한 포함)를 덮지 못한다
19
19
  */
20
20
  const fs = require("node:fs");
21
+ const { detectRuntimeRefusal } = require("../runtime-refusal.cjs");
21
22
  const os = require("node:os");
22
23
  const path = require("node:path");
23
24
  const { spawn } = require("node:child_process");
@@ -309,6 +310,48 @@ function capturedRuntimeUsage(kind, raw) {
309
310
  return null;
310
311
  }
311
312
 
313
+
314
+ /**
315
+ * ★캡처된 스트림에서 **실패 표식**을 걷는다 — 종료코드만 보면 exit 0 거절이 산출물이 된다.
316
+ *
317
+ * 실측(2026-08-06): claude 한도는 rate_limit_event(status:rejected)·result(is_error:true)를
318
+ * 보내고, codex 한도는 표식 없이 거절문을 agent_message로 싣고 turn.completed로 끝난다.
319
+ * 이 파일은 이벤트 종류 목록에 rate_limit_event를 적어 두고도 한 번도 읽지 않았다 —
320
+ * 거절문이 그대로 워커 핸드오프가 되어 종합(synthesis)에 섞였다.
321
+ */
322
+ function capturedRuntimeFailure(kind, raw, text) {
323
+ const events = raw.split(/\r?\n/).map((line) => {
324
+ try { return JSON.parse(line); } catch { return null; }
325
+ }).filter(Boolean);
326
+ for (const event of events) {
327
+ if (!event || typeof event !== "object") continue;
328
+ if (kind === "claude-code") {
329
+ if (event.type === "result" && event.is_error) {
330
+ return { message: typeof event.result === "string" ? event.result : "claude error", source: "marker" };
331
+ }
332
+ if (event.type === "rate_limit_event" && event.rate_limit_info && event.rate_limit_info.status === "rejected") {
333
+ return { message: "claude rate limit rejected", source: "marker" };
334
+ }
335
+ }
336
+ if (kind === "codex" && (event.type === "turn.failed" || event.type === "error")) {
337
+ const msg = (event.error && (event.error.message || event.error)) || "codex error";
338
+ return { message: String(msg), source: "marker" };
339
+ }
340
+ if (kind === "gemini") {
341
+ if (event.type === "error") {
342
+ const msg = (event.error && (event.error.message || event.error)) || "gemini error";
343
+ return { message: String(msg), source: "marker" };
344
+ }
345
+ if (event.type === "result" && event.status && event.status !== "success") {
346
+ return { message: `gemini ${event.status}`, source: "marker" };
347
+ }
348
+ }
349
+ }
350
+ // 표식이 전혀 없는 케이스(codex 한도) — 휴리스틱 최후 그물, 출처 표기.
351
+ const refusal = detectRuntimeRefusal(text);
352
+ return refusal ? { message: refusal.message, source: "heuristic" } : null;
353
+ }
354
+
312
355
  function capturedRuntimeAgentText(kind, raw) {
313
356
  const text = String(raw || "");
314
357
  const events = [];
@@ -377,7 +420,8 @@ function captureRuntime(kind, systemPrompt, prompt, opts) {
377
420
  ? captureOutputLimit(opts.env || process.env)
378
421
  : directCaptureOutputLimit(opts.outputLimitBytes);
379
422
  return new Promise((resolve, reject) => {
380
- const bin = which(RUNTIME_BIN[kind]) || RUNTIME_BIN[kind];
423
+ // 계약 테스트용 실행 파일 주입(가짜 CLI가 픽스처를 cat) 프로덕션 경로에선 없음.
424
+ const bin = opts.binOverride || which(RUNTIME_BIN[kind]) || RUNTIME_BIN[kind];
381
425
  let child;
382
426
  try {
383
427
  const spawnImpl = opts.spawn || spawn;
@@ -523,6 +567,12 @@ function captureRuntime(kind, systemPrompt, prompt, opts) {
523
567
  } else {
524
568
  text = raw;
525
569
  }
570
+ // ★거절 고지문은 산출물이 아니다 — 성공으로 돌려주면 워커 핸드오프가 오염된다.
571
+ const failure = capturedRuntimeFailure(kind, raw, text);
572
+ if (failure) {
573
+ finishReject(new Error(`${kind} runtime refused (${failure.source}): ${failure.message}`));
574
+ return;
575
+ }
526
576
  finishResolve(opts.envelope
527
577
  ? { text, usage: capturedRuntimeUsage(kind, raw) }
528
578
  : text);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "agentlas",
3
- "version": "1.0.29",
4
- "description": "Agentlas project terminal \u2014 run project controllers and task-scoped agent teams from the terminal. Standalone: no desktop app process required.",
3
+ "version": "1.0.35",
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"
7
7
  },
@@ -9,6 +9,7 @@
9
9
  "smoke": "sh test/smoke.sh",
10
10
  "test:release-contracts": "npm run smoke",
11
11
  "sync:architecture": "node scripts/sync-architecture-from-desktop.cjs",
12
+ "vendor:core": "node scripts/vendor-desktop-core.cjs",
12
13
  "test:tool-access-notice-parity": "node test/tool-access-notice-parity.cjs"
13
14
  },
14
15
  "engines": {