agentlas 1.0.51 → 1.0.53

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.
@@ -547,7 +547,29 @@ async function runGraph(ctx, needle, flags) {
547
547
  }
548
548
  }
549
549
 
550
- const core = ctx.desktopCore || desktopCore.loadDesktopCore();
550
+ /*
551
+ * ★엔진이 아직 없으면 **받아 온다**. npm 패키지는 실행 엔진을 담지 않고
552
+ * 매니페스트가 가리키는 자산에서 내려받는데(desktop-core-fetch), 그 길을
553
+ * 부르는 곳이 **한 군데도 없었다**. 만들어만 두고 배선하지 않은 것이다.
554
+ *
555
+ * 실측 2026-08-19: 갓 `npm i -g agentlas` 한 상태에서 `graph run` 이
556
+ * "vendored Desktop Core is unavailable" 로 즉시 죽었다. 새로 설치한 사람은
557
+ * 자동화를 한 번도 못 돌린다 — 캐시가 비어 있고, 채울 길을 아무도 안 밟는다.
558
+ *
559
+ * 받는 동안은 조용하지 않다(onNotice) — 12MB 를 말없이 끌어오지 않는다.
560
+ */
561
+ let core = ctx.desktopCore || desktopCore.loadDesktopCore();
562
+ if (!core || core.error || typeof core.runGraph !== "function") {
563
+ try {
564
+ core = await desktopCore.loadDesktopCoreAsync({ onNotice: (message) => ctx.err(message) });
565
+ } catch (fetchError) {
566
+ ctx.err(JSON.stringify({
567
+ ok: false,
568
+ error: `graph-execution engine could not be fetched: ${fetchError instanceof Error ? fetchError.message : String(fetchError)}`,
569
+ }, null, 2));
570
+ return 1;
571
+ }
572
+ }
551
573
  if (!core || core.error || typeof core.runGraph !== "function") {
552
574
  const cause = core?.error instanceof Error ? core.error.message : "vendored Desktop Core is unavailable";
553
575
  ctx.err(JSON.stringify({ ok: false, error: cause }, null, 2));
@@ -129,7 +129,16 @@ function installNativeModuleHook() {
129
129
  const Module = require("node:module");
130
130
  const terminalBetterSqlite3 = require("better-sqlite3"); // 터미널 ABI 로 빌드된 것
131
131
  let terminalKeytar = null;
132
- try { terminalKeytar = require("keytar"); } catch { /* optionalDependency — 없을 수 있다 */ }
132
+ try {
133
+ terminalKeytar = require("keytar");
134
+ // ★훅은 require 를 가로채지만 resolve 는 못 가로챈다. 코어의 키체인 호스트는 자식
135
+ // 프로세스를 띄우려고 **경로**가 필요한데, 벤더 트리에는 keytar 가 일부러 없다
136
+ // (아래 skip 목록). 그래서 우리 것의 실물 경로를 봉투에 담아 넘긴다.
137
+ // 실측 2026-08-20: 이게 없으면 `Cannot find module 'keytar'` 로 노드가 죽었다.
138
+ if (!process.env.AGENTLAS_KEYTAR_PATH) {
139
+ process.env.AGENTLAS_KEYTAR_PATH = require.resolve("keytar");
140
+ }
141
+ } catch { /* optionalDependency — 없을 수 있다 */ }
133
142
  const electronShim = makeElectronShim(); // electron 없이 코어를 돌리는 셰임
134
143
  const orig = Module._load;
135
144
  Module._load = function (request, parent, isMain) {
@@ -198,6 +198,12 @@ const RULES = [
198
198
  " the expected fields' / 'the row was appended with the submitted values'. If the outside",
199
199
  " result genuinely cannot be re-observed, say so in the step instruction rather than",
200
200
  " skipping the check.",
201
+ " · A value a branch tests for emptiness (op truthy/falsy) MUST NOT also carry a check",
202
+ " that demands it be non-empty. Threshold watchers are the common case: on a day the",
203
+ " threshold is not crossed the value is correctly empty, and a check demanding content",
204
+ " fails the run on exactly the days nothing was supposed to happen. Put that check",
205
+ " INSIDE the branch that has the value, or check the comparison step's own report",
206
+ " (both rates read, threshold applied) instead of the alert text.",
201
207
  " · repeatOn says which side loops. Write the condition the way the person said it and",
202
208
  " put the loop on the side they meant — do not flip either one to make it fit.",
203
209
  "",
@@ -557,6 +563,42 @@ function validateBlueprint(bp, ctx = {}) {
557
563
  }
558
564
  }
559
565
 
566
+ /*
567
+ * ★"비어 있을 수 있다"고 갈림길이 말한 값에 "비어 있으면 안 된다" 검증을 걸면 평상시마다
568
+ * 실패한다(실측 2026-08-19: 임계값 미달인 날 alertline 이 정당하게 비어 NODE_INPUT_MISSING).
569
+ * 정본은 데스크탑 shared/graph-blueprint.ts — 이 파일은 손복사본이고 패리티 게이트가 지킨다.
570
+ */
571
+ const emptinessTestedVars = new Set(
572
+ (bp.branches || [])
573
+ .filter((branch) => branch.op === "truthy" || branch.op === "falsy")
574
+ .map((branch) => String(branch.var || "").trim())
575
+ .filter(Boolean),
576
+ );
577
+ for (const check of bp.checks || []) {
578
+ const subject = String(check.subject || "").trim();
579
+ if (!subject || !emptinessTestedVars.has(subject)) continue;
580
+ const branch = (bp.branches || []).find((b) => String(b.var || "").trim() === subject);
581
+ if (!branch) continue;
582
+ // 위치를 본다 — 갈림길 뒤 값-있는 쪽의 검증은 비는 날 아예 안 돌므로 문제가 아니다.
583
+ const at = typeof check.afterStep === "number" ? check.afterStep : -1;
584
+ const runsBeforeTheBranchDecides = at <= (branch.afterStep ?? 0);
585
+ const sitsOnTheEmptySide = typeof branch.noStep === "number" && at === branch.noStep;
586
+ if (!runsBeforeTheBranchDecides && !sitsOnTheEmptySide) continue;
587
+ const yesStep = typeof branch.yesStep === "number" ? branch.yesStep : null;
588
+ // 사람에게 묻지 않는다 — 모양이 틀린 것이고 고치는 법이 하나로 정해진다.
589
+ push(
590
+ `"${subject}"은(는) 갈림길이 비어 있을 수 있다고 말하는 값인데, 그 앞의 검증이 비어 있지 않기를 요구합니다. `
591
+ + "임계값 감시처럼 '알릴 것이 없는 날'이 정상인 자동화는 그 날마다 실패합니다. "
592
+ + `검증을 지우지 말고 **값이 있는 쪽에서만 돌게** 옮기세요: 이 검증의 afterStep 을 `
593
+ + (yesStep !== null
594
+ ? `갈림길의 yes 쪽 단계(${yesStep})나 그 뒤 단계로 바꾸면 됩니다.`
595
+ : "갈림길의 yes 쪽 단계나 그 뒤 단계로 바꾸면 됩니다.")
596
+ + ` 값이 비었는지 자체를 확인하고 싶다면, "${subject}"을(를) 만든 비교 단계가 `
597
+ + "무엇을 읽고 어떤 임계값을 적용했는지 보고하게 하고 그 보고를 subject 로 삼으세요 "
598
+ + "— 그 보고는 알릴 것이 없는 날에도 비지 않습니다.",
599
+ );
600
+ }
601
+
560
602
  for (const branch of bp.branches || []) {
561
603
  const at = `${(branch.afterStep ?? 0) + 1}번째 단계 뒤의 갈림길`;
562
604
  if (!steps[branch.afterStep]) { push(`${at}가 없는 단계를 가리킵니다.`); continue; }
@@ -1,7 +1,7 @@
1
1
  {
2
- "version": "4",
3
- "url": "https://github.com/agentlas-ai/agentlas-terminal/releases/download/desktop-core-v4/desktop-core.tar.gz",
4
- "sha256": "105efb6c3daea6f40f5d4719533c8fd2fc75a1e151fbe524041e0edeb3f77458",
5
- "sizeBytes": 12530059,
6
- "writtenAt": "2026-08-19T13:04:26.175Z"
2
+ "version": "6",
3
+ "url": "https://github.com/agentlas-ai/agentlas-terminal/releases/download/desktop-core-v6/desktop-core.tar.gz",
4
+ "sha256": "e93020895d21a7c3484ee22fb7008b7ffcad65ff7c276de7f531c3dd016c7f0c",
5
+ "sizeBytes": 12534557,
6
+ "writtenAt": "2026-08-19T16:22:09.603Z"
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentlas",
3
- "version": "1.0.51",
3
+ "version": "1.0.53",
4
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"
@@ -11,7 +11,9 @@
11
11
  "test:release-contracts": "npm run smoke",
12
12
  "sync:architecture": "node scripts/sync-architecture-from-desktop.cjs",
13
13
  "vendor:core": "node scripts/vendor-desktop-core.cjs",
14
- "test:tool-access-notice-parity": "node test/tool-access-notice-parity.cjs"
14
+ "test:tool-access-notice-parity": "node test/tool-access-notice-parity.cjs",
15
+ "verify:engine-reachable": "node scripts/verify-engine-reachable.cjs",
16
+ "prepublishOnly": "npm run verify:engine-reachable"
15
17
  },
16
18
  "engines": {
17
19
  "node": ">=20.19"