agentlas 1.0.65 → 1.0.66

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 (163) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/README.md +6 -4
  3. package/bin/agentlas.cjs +341 -16
  4. package/engine/acp/server.cjs +27 -1
  5. package/engine/agentlas-api-agent.cjs +151 -120
  6. package/engine/agentlas-banner.cjs +6 -2
  7. package/engine/agentlas-capabilities.cjs +17 -10
  8. package/engine/agentlas-cloud-runtime.cjs +4 -5
  9. package/engine/agentlas-config.cjs +19 -54
  10. package/engine/agentlas-core-harness.cjs +59 -5
  11. package/engine/agentlas-desktop-loadout.cjs +17 -3
  12. package/engine/agentlas-evolution.cjs +75 -18
  13. package/engine/agentlas-experience-exchange.cjs +145 -14
  14. package/engine/agentlas-experience-intake.cjs +2 -2
  15. package/engine/agentlas-i18n.cjs +0 -2
  16. package/engine/agentlas-input.cjs +273 -20
  17. package/engine/agentlas-judgment.cjs +0 -0
  18. package/engine/agentlas-mcp-env.cjs +84 -23
  19. package/engine/agentlas-mcp-wrapper.cjs +36 -4
  20. package/engine/agentlas-memory-governance.cjs +2 -1
  21. package/engine/agentlas-memory-import.cjs +49 -12
  22. package/engine/agentlas-native-host.cjs +279 -44
  23. package/engine/agentlas-onboard.cjs +23 -1
  24. package/engine/agentlas-permissions.cjs +5 -1
  25. package/engine/agentlas-sqlite-policy.cjs +40 -2
  26. package/engine/agentlas-tasks.cjs +7 -0
  27. package/engine/agentlas-tools.cjs +37 -10
  28. package/engine/agentlas-ui.cjs +26 -4
  29. package/engine/agentlas-workforce.cjs +29 -5
  30. package/engine/agentlas-workload-routing.cjs +83 -10
  31. package/engine/agentlas.cjs +10 -3
  32. package/engine/agents/builder.cjs +35 -10
  33. package/engine/agents/files.cjs +81 -10
  34. package/engine/agents/import-local.cjs +10 -2
  35. package/engine/agents/registry.cjs +10 -3
  36. package/engine/agents/router.cjs +7 -7
  37. package/engine/agents/routes.cjs +31 -9
  38. package/engine/automation/daemon.cjs +154 -40
  39. package/engine/automation/launchd.cjs +395 -16
  40. package/engine/automation/schedule.cjs +90 -3
  41. package/engine/automation/store.cjs +163 -59
  42. package/engine/browser/cdp.cjs +190 -17
  43. package/engine/browser/vault.cjs +1 -0
  44. package/engine/cli-output.cjs +80 -28
  45. package/engine/cloud/auth.cjs +131 -13
  46. package/engine/cloud/hub-client.cjs +41 -16
  47. package/engine/cloud-assets/cas.cjs +18 -9
  48. package/engine/cloud-assets/commands.cjs +108 -30
  49. package/engine/cloud-assets/package.cjs +66 -3
  50. package/engine/cloud-assets/restore.cjs +12 -9
  51. package/engine/cloud-assets/state.cjs +308 -113
  52. package/engine/commands/acp.cjs +13 -2
  53. package/engine/commands/automation.cjs +75 -24
  54. package/engine/commands/billing.cjs +6 -1
  55. package/engine/commands/browser.cjs +61 -29
  56. package/engine/commands/build.cjs +42 -12
  57. package/engine/commands/call.cjs +1 -1
  58. package/engine/commands/cd.cjs +1 -1
  59. package/engine/commands/cloud.cjs +2 -1
  60. package/engine/commands/connect.cjs +38 -13
  61. package/engine/commands/creds.cjs +74 -29
  62. package/engine/commands/doctor.cjs +17 -1
  63. package/engine/commands/document.cjs +38 -10
  64. package/engine/commands/env.cjs +6 -1
  65. package/engine/commands/firm.cjs +42 -12
  66. package/engine/commands/graph.cjs +36 -32
  67. package/engine/commands/help.cjs +11 -1
  68. package/engine/commands/hep.cjs +1 -1
  69. package/engine/commands/import.cjs +5 -4
  70. package/engine/commands/index.cjs +1 -1
  71. package/engine/commands/install.cjs +6 -4
  72. package/engine/commands/list.cjs +56 -16
  73. package/engine/commands/login.cjs +5 -0
  74. package/engine/commands/logout.cjs +6 -1
  75. package/engine/commands/mcp.cjs +8 -2
  76. package/engine/commands/memory.cjs +3 -2
  77. package/engine/commands/multimodal.cjs +12 -0
  78. package/engine/commands/native.cjs +5 -1
  79. package/engine/commands/one.cjs +78 -33
  80. package/engine/commands/plugin.cjs +13 -2
  81. package/engine/commands/project.cjs +15 -12
  82. package/engine/commands/research.cjs +18 -7
  83. package/engine/commands/roles.cjs +104 -28
  84. package/engine/commands/route.cjs +2 -2
  85. package/engine/commands/run.cjs +42 -9
  86. package/engine/commands/search.cjs +23 -2
  87. package/engine/commands/setup.cjs +6 -1
  88. package/engine/commands/storm.cjs +44 -6
  89. package/engine/commands/swarm.cjs +47 -9
  90. package/engine/commands/telegram.cjs +6 -1
  91. package/engine/commands/uninstall.cjs +12 -6
  92. package/engine/commands/update.cjs +5 -0
  93. package/engine/commands/upload.cjs +2 -1
  94. package/engine/commands/usage.cjs +6 -1
  95. package/engine/commands/version.cjs +6 -1
  96. package/engine/commands/whoami.cjs +6 -1
  97. package/engine/commands/workforce.cjs +60 -16
  98. package/engine/core/capability-grants.cjs +7 -2
  99. package/engine/core/daemon-client.cjs +36 -7
  100. package/engine/core/db.cjs +10 -2
  101. package/engine/core/desktop-core-fetch.cjs +477 -77
  102. package/engine/core/schema-ensure.cjs +23 -11
  103. package/engine/experience/build.cjs +5 -1
  104. package/engine/experience/intents.cjs +29 -82
  105. package/engine/experience/runtime.cjs +7 -1
  106. package/engine/experience/variant.cjs +58 -4
  107. package/engine/firms/orchestrate.cjs +9 -6
  108. package/engine/graph/ask-model.cjs +22 -11
  109. package/engine/graph/interview.cjs +74 -23
  110. package/engine/graph/package.cjs +63 -8
  111. package/engine/hephaestus/local-core.cjs +18 -5
  112. package/engine/hephaestus/runtime.cjs +48 -43
  113. package/engine/hub/install.cjs +230 -61
  114. package/engine/hub/plugins.cjs +391 -29
  115. package/engine/mcp/consent.cjs +25 -17
  116. package/engine/mcp/contract.cjs +229 -24
  117. package/engine/mcp/inventory.cjs +15 -5
  118. package/engine/mcp/plan.cjs +5 -1
  119. package/engine/mcp/probe.cjs +24 -9
  120. package/engine/memory-cli/curate.cjs +74 -16
  121. package/engine/oberon/common.cjs +37 -12
  122. package/engine/oberon/manifest.cjs +25 -2
  123. package/engine/oberon/outputs.cjs +49 -19
  124. package/engine/oberon/render.cjs +665 -40
  125. package/engine/project/career-graph.cjs +15 -2
  126. package/engine/project/controller.cjs +133 -15
  127. package/engine/project/credentials.cjs +212 -34
  128. package/engine/project/env-file.cjs +93 -8
  129. package/engine/project/memory-context.cjs +11 -57
  130. package/engine/project/ontology.cjs +66 -25
  131. package/engine/project/paths.cjs +42 -8
  132. package/engine/project/seed.cjs +88 -51
  133. package/engine/project/state.cjs +41 -16
  134. package/engine/project/team.cjs +26 -1
  135. package/engine/runtime-refusal.cjs +4 -1
  136. package/engine/runtimes/acp-driver.cjs +56 -9
  137. package/engine/runtimes/auth-evidence.cjs +21 -2
  138. package/engine/runtimes/detect.cjs +28 -8
  139. package/engine/runtimes/kinds.cjs +12 -4
  140. package/engine/runtimes/overrides.cjs +4 -3
  141. package/engine/runtimes/resolve.cjs +8 -7
  142. package/engine/runtimes/roles.cjs +7 -5
  143. package/engine/sessions/apply-fences.cjs +396 -56
  144. package/engine/sessions/fences.cjs +111 -27
  145. package/engine/sessions/memory-turn.cjs +62 -3
  146. package/engine/sessions/orchestrator.cjs +11 -1
  147. package/engine/sessions/prompt.cjs +66 -9
  148. package/engine/sessions/session.cjs +70 -10
  149. package/engine/sessions/sink.cjs +78 -12
  150. package/engine/sessions/store.cjs +29 -7
  151. package/engine/storm/storm.cjs +26 -3
  152. package/engine/storm/swarm.cjs +31 -3
  153. package/engine/telegram/connect.cjs +185 -22
  154. package/engine/ui/commands-catalog.cjs +1 -1
  155. package/engine/ui/palette.cjs +12 -2
  156. package/engine/ui/repl.cjs +57 -9
  157. package/engine/ui/screens.cjs +2 -4
  158. package/engine/vendor/desktop-core.manifest.json +5 -5
  159. package/engine/workforce/capture.cjs +9 -27
  160. package/engine/workforce/deps.cjs +18 -6
  161. package/engine/workforce/local-core-transport.cjs +68 -5
  162. package/package.json +1 -1
  163. package/engine/commands/career-graph.cjs +0 -51
@@ -166,30 +166,80 @@ function writePurposeRepairCopy(root, projection) {
166
166
  return tempRoot;
167
167
  }
168
168
 
169
- function parseCloudFlags(args) {
169
+ const CLOUD_VALUE_FLAGS = ["limit", "name", "purpose", "scope", "slug", "visibility"];
170
+ const CLOUD_BOOLEAN_FLAGS = ["dry-run", "json", "llm-review", "overwrite", "strict"];
171
+
172
+ function cloudArgumentError(message) {
173
+ const error = new Error(message);
174
+ error.code = "INVALID_ARGUMENT";
175
+ return error;
176
+ }
177
+
178
+ function parseCloudFlags(args, spec = {}) {
179
+ const values = new Set(spec.values || CLOUD_VALUE_FLAGS);
180
+ const booleans = new Set(spec.booleans || CLOUD_BOOLEAN_FLAGS);
170
181
  const flags = { _: [] };
182
+ const seen = new Set();
183
+ let positionalOnly = false;
171
184
  for (let i = 0; i < args.length; i++) {
172
185
  const a = args[i];
173
- if (a && a.startsWith("--")) {
174
- const key = a.slice(2);
175
- const next = args[i + 1];
176
- if (next !== undefined && !String(next).startsWith("--")) {
177
- flags[key] = next;
178
- i++;
179
- } else {
180
- flags[key] = true;
181
- }
182
- } else {
186
+ if (positionalOnly) {
183
187
  flags._.push(a);
188
+ continue;
184
189
  }
190
+ if (a === "--") {
191
+ positionalOnly = true;
192
+ continue;
193
+ }
194
+ if (!String(a).startsWith("-")) {
195
+ flags._.push(a);
196
+ continue;
197
+ }
198
+ if (!String(a).startsWith("--")) throw cloudArgumentError(`unknown option: ${a}`);
199
+ const equal = String(a).indexOf("=");
200
+ const key = String(a).slice(2, equal === -1 ? undefined : equal);
201
+ if (!key || (!values.has(key) && !booleans.has(key))) throw cloudArgumentError(`unknown option: --${key || ""}`);
202
+ if (seen.has(key)) throw cloudArgumentError(`duplicate option: --${key}`);
203
+ seen.add(key);
204
+ if (booleans.has(key)) {
205
+ if (equal !== -1) throw cloudArgumentError(`--${key} does not take a value`);
206
+ flags[key] = true;
207
+ continue;
208
+ }
209
+ let value;
210
+ if (equal !== -1) {
211
+ value = String(a).slice(equal + 1);
212
+ } else {
213
+ const next = args[i + 1];
214
+ if (next === undefined || String(next).startsWith("--")) throw cloudArgumentError(`--${key} requires a value`);
215
+ value = String(next);
216
+ i++;
217
+ }
218
+ if (!value) throw cloudArgumentError(`--${key} requires a non-empty value`);
219
+ flags[key] = value;
185
220
  }
186
221
  return flags;
187
222
  }
188
223
 
224
+ function requireCloudPositionals(flags, min, max, usage) {
225
+ if (flags._.length < min || flags._.length > max) throw cloudArgumentError(usage);
226
+ return flags;
227
+ }
228
+
229
+ function positiveIntegerFlag(value, fallback, name, max) {
230
+ if (value == null) return fallback;
231
+ if (!/^\d+$/.test(String(value))) throw cloudArgumentError(`--${name} must be a positive integer`);
232
+ const parsed = Number(value);
233
+ if (!Number.isSafeInteger(parsed) || parsed < 1 || parsed > max) {
234
+ throw cloudArgumentError(`--${name} must be between 1 and ${max}`);
235
+ }
236
+ return parsed;
237
+ }
238
+
189
239
  function cloudVisibilityFlag(value) {
190
240
  if (value == null) return null;
191
241
  if (value === "private-link" || value === "marketplace") return value;
192
- throw new Error("--visibility must be private-link or marketplace");
242
+ throw cloudArgumentError("--visibility must be private-link or marketplace");
193
243
  }
194
244
 
195
245
  function cloudVisibilityForAction(sub, flags) {
@@ -213,7 +263,10 @@ function cloudVisibilityForAction(sub, flags) {
213
263
 
214
264
  /** 최상위 `upload`의 실제 동작 결정: 기본 save, 명시적 marketplace만 publish. */
215
265
  function cloudActionForTopLevelUpload(args) {
216
- const flags = parseCloudFlags(args);
266
+ const flags = requireCloudPositionals(parseCloudFlags(args, {
267
+ values: ["purpose", "slug", "visibility"],
268
+ booleans: ["dry-run", "json", "llm-review", "overwrite"],
269
+ }), 1, 1, "usage: agentlas upload <path> [--visibility marketplace]");
217
270
  return cloudVisibilityFlag(flags.visibility) === "marketplace" ? "publish" : "save";
218
271
  }
219
272
 
@@ -269,17 +322,20 @@ const CLOUD_HELP = [
269
322
  async function runCloud(ctx, args) {
270
323
  const sub = args[0] || "help";
271
324
  if (sub === "help" || sub === "--help" || sub === "-h") {
325
+ requireCloudPositionals(parseCloudFlags(args.slice(1), { values: [], booleans: ["json"] }), 0, 0, "usage: agentlas cloud help");
272
326
  ctx.out(CLOUD_HELP);
273
327
  return 0;
274
328
  }
275
329
  if (sub === "search") {
276
- const flags = parseCloudFlags(args.slice(1));
330
+ const flags = requireCloudPositionals(parseCloudFlags(args.slice(1), {
331
+ values: ["limit"], booleans: ["json"],
332
+ }), 1, Number.MAX_SAFE_INTEGER, 'usage: agentlas cloud search "<what you need>" [--limit 10]');
277
333
  const query = flags._.join(" ").trim();
278
334
  if (!query) {
279
335
  ctx.err('usage: agentlas cloud search "<what you need>" [--limit 10]');
280
336
  return 1;
281
337
  }
282
- const limit = Math.max(1, Math.min(30, Number(flags.limit) || 10));
338
+ const limit = positiveIntegerFlag(flags.limit, 10, "limit", 30);
283
339
  let result;
284
340
  try {
285
341
  result = await callHubTool("marketplace.search_agents", { q: query, limit });
@@ -287,6 +343,7 @@ async function runCloud(ctx, args) {
287
343
  ctx.err(e instanceof HubError ? e.message : `Marketplace connection failed: ${(e && e.message) || e}`);
288
344
  return 1;
289
345
  }
346
+ if (flags.json) { ctx.out(JSON.stringify(result, null, 2)); return 0; }
290
347
  const items = (result && (result.results || result.agents || result.items)) || [];
291
348
  if (!Array.isArray(items) || !items.length) {
292
349
  ctx.out(`No results for "${query}"`);
@@ -298,8 +355,10 @@ async function runCloud(ctx, args) {
298
355
  return 0;
299
356
  }
300
357
  if (sub === "list") {
301
- const flags = parseCloudFlags(args.slice(1));
302
- const result = await listOwnedCloudAgents(Number(flags.limit || 100));
358
+ const flags = requireCloudPositionals(parseCloudFlags(args.slice(1), {
359
+ values: ["limit"], booleans: ["json"],
360
+ }), 0, 0, "usage: agentlas cloud list [--limit 100] [--json]");
361
+ const result = await listOwnedCloudAgents(positiveIntegerFlag(flags.limit, 100, "limit", 1000));
303
362
  if (flags.json) { ctx.out(JSON.stringify(result, null, 2)); return 0; }
304
363
  const agents = Array.isArray(result.results) ? result.results : [];
305
364
  if (!agents.length) { ctx.out("No agents are stored in Private Agent Cloud."); return 0; }
@@ -332,7 +391,9 @@ async function runCloud(ctx, args) {
332
391
  return 0;
333
392
  }
334
393
  if (sub === "restore") {
335
- const flags = parseCloudFlags(args.slice(1));
394
+ const flags = requireCloudPositionals(parseCloudFlags(args.slice(1), {
395
+ values: [], booleans: ["json"],
396
+ }), 1, 1, "usage: agentlas cloud restore <slug> [--json]");
336
397
  const slug = flags._[0];
337
398
  if (!slug) { ctx.err("usage: agentlas cloud restore <slug> [--json]"); return 1; }
338
399
  const result = await restoreOwnedCloudAgent(ctx.db(), slug);
@@ -344,7 +405,9 @@ async function runCloud(ctx, args) {
344
405
  return 0;
345
406
  }
346
407
  if (sub === "delete" || sub === "unpublish") {
347
- const flags = parseCloudFlags(args.slice(1));
408
+ const flags = requireCloudPositionals(parseCloudFlags(args.slice(1), {
409
+ values: ["scope"], booleans: ["json"],
410
+ }), 1, 1, `usage: agentlas cloud ${sub} <slug> [--scope owner-private|hub-public] [--json]`);
348
411
  const slug = flags._[0];
349
412
  if (!slug) { ctx.err(`usage: agentlas cloud ${sub} <slug> [--json]`); return 1; }
350
413
  const result = await deleteCloudAgent(slug, { scope: flags.scope });
@@ -356,7 +419,9 @@ async function runCloud(ctx, args) {
356
419
  }
357
420
  // ── agentlas-cloud-runtime 소비 지점 (스캔/매니페스트/lazy-read/필드테스트) ──
358
421
  if (sub === "wizard") {
359
- const flags = parseCloudFlags(args.slice(1));
422
+ const flags = requireCloudPositionals(parseCloudFlags(args.slice(1), {
423
+ values: ["name"], booleans: ["json"],
424
+ }), 1, 1, "usage: agentlas cloud wizard <path> [--name name] [--json]");
360
425
  const root = flags._[0];
361
426
  if (!root) { ctx.err("usage: agentlas cloud wizard <path> [--name name]"); return 1; }
362
427
  const result = cloudRuntime.runWizard(root, { name: typeof flags.name === "string" ? flags.name : undefined });
@@ -365,7 +430,9 @@ async function runCloud(ctx, args) {
365
430
  }
366
431
  if (sub === "security") {
367
432
  if (args[1] !== "scan") { ctx.err("usage: agentlas cloud security scan <path> [--strict]"); return 1; }
368
- const flags = parseCloudFlags(args.slice(2));
433
+ const flags = requireCloudPositionals(parseCloudFlags(args.slice(2), {
434
+ values: [], booleans: ["json", "strict"],
435
+ }), 1, 1, "usage: agentlas cloud security scan <path> [--strict]");
369
436
  const root = flags._[0];
370
437
  if (!root) { ctx.err("usage: agentlas cloud security scan <path> [--strict]"); return 1; }
371
438
  const report = cloudRuntime.scanFolder(root);
@@ -375,15 +442,18 @@ async function runCloud(ctx, args) {
375
442
  if (sub === "runtime") {
376
443
  const action = args[1];
377
444
  if (action === "bundle") {
378
- const root = args[2];
379
- if (!root) { ctx.err("usage: agentlas cloud runtime bundle <path>"); return 1; }
445
+ const flags = requireCloudPositionals(parseCloudFlags(args.slice(2), {
446
+ values: [], booleans: ["json"],
447
+ }), 1, 1, "usage: agentlas cloud runtime bundle <path> [--json]");
448
+ const root = flags._[0];
380
449
  ctx.out(JSON.stringify(cloudRuntime.compileBundle(root), null, 2));
381
450
  return 0;
382
451
  }
383
452
  if (action === "read-agent-file") {
384
- const root = args[2];
385
- const targetPath = args[3];
386
- if (!root || !targetPath) { ctx.err("usage: agentlas cloud runtime read-agent-file <path> <file>"); return 1; }
453
+ const flags = requireCloudPositionals(parseCloudFlags(args.slice(2), {
454
+ values: [], booleans: ["json"],
455
+ }), 2, 2, "usage: agentlas cloud runtime read-agent-file <path> <file> [--json]");
456
+ const [root, targetPath] = flags._;
387
457
  ctx.out(JSON.stringify(cloudRuntime.readAgentFile(root, targetPath), null, 2));
388
458
  return 0;
389
459
  }
@@ -391,15 +461,20 @@ async function runCloud(ctx, args) {
391
461
  return 1;
392
462
  }
393
463
  if (sub === "field-test") {
394
- const flags = parseCloudFlags(args.slice(1));
464
+ const flags = requireCloudPositionals(parseCloudFlags(args.slice(1), {
465
+ values: [], booleans: ["json"],
466
+ }), 0, 0, "usage: agentlas cloud field-test [--json]");
395
467
  const result = cloudRuntime.runFieldTest();
396
468
  ctx.out(flags.json ? JSON.stringify(result, null, 2) : `${result.suite}: ${result.status}`);
397
469
  return result.status === "PASS" ? 0 : 1;
398
470
  }
399
471
  if (sub === "install") {
400
- const slug = args[1];
401
- if (!slug) { ctx.err("usage: agentlas cloud install <slug>"); return 1; }
472
+ const flags = requireCloudPositionals(parseCloudFlags(args.slice(1), {
473
+ values: [], booleans: ["json"],
474
+ }), 1, 1, "usage: agentlas cloud install <slug> [--json]");
475
+ const slug = flags._[0];
402
476
  const agent = await installHubAgent(ctx.db(), slug);
477
+ if (flags.json) { ctx.out(JSON.stringify(agent, null, 2)); return 0; }
403
478
  ctx.out(`${ctx.ui.green("✓")} Hub installed ${agent.slug} — ${agent.name}`);
404
479
  if (agent.localPath) ctx.out(` files: ${agent.localPath}`);
405
480
  return 0;
@@ -408,7 +483,10 @@ async function runCloud(ctx, args) {
408
483
  ctx.err("usage: agentlas cloud <save|publish|package|list|restore|install|delete|field-test> ...");
409
484
  return 1;
410
485
  }
411
- const flags = parseCloudFlags(args.slice(1));
486
+ const flags = requireCloudPositionals(parseCloudFlags(args.slice(1), {
487
+ values: ["purpose", "slug", "visibility"],
488
+ booleans: ["dry-run", "json", "llm-review", "overwrite"],
489
+ }), 1, 1, `usage: agentlas cloud ${sub} <path>`);
412
490
  const root = flags._[0];
413
491
  if (!root) { ctx.err(`usage: agentlas cloud ${sub} <path>`); return 1; }
414
492
  const visibility = cloudVisibilityForAction(sub, flags);
@@ -52,6 +52,7 @@ const cas = require("./cas.cjs");
52
52
  // 터미널 업로드가 그 파일들을 아예 열지 않았다.
53
53
  const CLOUD_TEXT_EXTS = new Set(SECRET_SCAN_TEXT_EXTENSIONS);
54
54
  const CLOUD_AGENT_FILES = new Set(UPLOAD_AGENT_DEFINITION_FILES);
55
+ const CLOUD_AGENT_FILE_KEYS = new Set([...CLOUD_AGENT_FILES].map((name) => name.toLowerCase()));
55
56
  const CLOUD_SKIP_DIRS = new Set(UPLOAD_SKIP_DIRECTORIES);
56
57
  const CLOUD_BLOCKED_FILE_RE = [/^\.env(?:\..*)?$/i, /^id_rsa(?:\.pub)?$/i, /^credentials(?:\..*)?$/i, /^secrets?(?:\..*)?$/i, /^cloud-asset-state\.v1\.json$/i, /(?:^|[._-])service-account(?:[._-]|$)/i, /\.(?:key|pem|p12|pfx|mobileprovision)$/i];
57
58
  const CLOUD_ROUTING_CARD_PATH = ".agentlas/routing-card.json";
@@ -385,7 +386,11 @@ const CLOUD_BULK_DATA_RE = /\.(log|jsonl|csv|tsv|lock)$/;
385
386
  function cloudTrimRank(rel, bytes) {
386
387
  const lower = String(rel || "").toLowerCase();
387
388
  const base = lower.split("/").pop() || lower;
388
- if (CLOUD_AGENT_FILES.has(base)) return 0;
389
+ // The generated catalog intentionally carries case-distinct root names
390
+ // (`AGENTS.md`, `AGENT.md`, `agent.md`). Ranking lower-cases paths, so the
391
+ // lookup must lower-case the catalog as well. Otherwise a package at the
392
+ // byte limit can evict AGENTS.md while retaining an unrelated cache file.
393
+ if (CLOUD_AGENT_FILE_KEYS.has(base)) return 0;
389
394
  if (lower.startsWith(".agentlas/")) return 0;
390
395
  if (base === "agentlas.json" || base === "manifest.json" || base === "package.json") return 0;
391
396
  if (CLOUD_CAPABILITY_DIR_RE.test(lower)) return 0;
@@ -427,13 +432,16 @@ function scanCloudFolder(rootPath) {
427
432
  let freed = 0;
428
433
  while (totalBytes - freed + needBytes > CLOUD_MAX_TOTAL_BYTES) {
429
434
  let victimIndex = -1;
430
- let victimRank = 0;
435
+ let victimRank = Number.POSITIVE_INFINITY;
431
436
  let victimBytes = -1;
432
437
  for (let index = 0; index < included.length; index += 1) {
433
438
  const candidate = included[index];
434
439
  const rank = cloudTrimRank(candidate.path, candidate.bytes);
435
440
  if (rank === 0) continue;
436
- if (rank > victimRank || (rank === victimRank && candidate.bytes > victimBytes)) {
441
+ // Rank 1 is disposable build/cache output; larger numbers are closer
442
+ // to ordinary authored content. Desktop's canonical trimmer removes
443
+ // the smallest positive rank first, then the largest file in that tier.
444
+ if (rank < victimRank || (rank === victimRank && candidate.bytes > victimBytes)) {
437
445
  victimIndex = index;
438
446
  victimRank = rank;
439
447
  victimBytes = candidate.bytes;
@@ -709,6 +717,60 @@ function scanCloudFolder(rootPath) {
709
717
  return { files, included, findings, omissions, totalBytes, localPackageMarker };
710
718
  }
711
719
 
720
+ /**
721
+ * Generated metadata is added after the stable source scan. Re-apply the
722
+ * public package limits at that exact boundary so injecting agentlas.json (or
723
+ * replacing a redacted public card) cannot turn a valid 400-file scan into a
724
+ * 401-file bundle that the server will reject.
725
+ */
726
+ function cloudFitGeneratedMetadataToLimits(scan) {
727
+ const removeVictim = () => {
728
+ let victimIndex = -1;
729
+ let victimRank = Number.POSITIVE_INFINITY;
730
+ let victimBytes = -1;
731
+ for (let index = 0; index < scan.included.length; index += 1) {
732
+ const candidate = scan.included[index];
733
+ const rank = cloudTrimRank(candidate.path, candidate.bytes);
734
+ if (rank === 0) continue;
735
+ if (rank < victimRank || (rank === victimRank && candidate.bytes > victimBytes)) {
736
+ victimIndex = index;
737
+ victimRank = rank;
738
+ victimBytes = candidate.bytes;
739
+ }
740
+ }
741
+ if (victimIndex < 0) return false;
742
+ const victim = scan.included.splice(victimIndex, 1)[0];
743
+ scan.omissions.push({
744
+ path: victim.path,
745
+ reason: "trimmed-for-package-limits",
746
+ sourceBytes: victim.bytes,
747
+ sourceSha256: victim.sha256,
748
+ sourceHashKind: "content",
749
+ });
750
+ const record = scan.files.find((item) => item.path === victim.path && item.included);
751
+ if (record) {
752
+ record.included = false;
753
+ record.reason = "trimmed-for-package-limits";
754
+ }
755
+ return true;
756
+ };
757
+
758
+ scan.totalBytes = scan.included.reduce((sum, file) => sum + file.bytes, 0);
759
+ while (scan.included.length > CLOUD_MAX_FILES || scan.totalBytes > CLOUD_MAX_TOTAL_BYTES) {
760
+ if (!removeVictim()) {
761
+ const error = new Error(
762
+ "Agent Cloud package cannot fit required agent metadata within the package limits. " +
763
+ "Reduce the number or size of capability files and retry.",
764
+ );
765
+ error.code = "AGENTLAS_CLOUD_PACKAGE_LIMITS";
766
+ throw error;
767
+ }
768
+ scan.totalBytes = scan.included.reduce((sum, file) => sum + file.bytes, 0);
769
+ }
770
+ scan.included.sort(cloudCodePointPathOrder);
771
+ scan.omissions.sort(cloudCodePointPathOrder);
772
+ }
773
+
712
774
  function cloudOmissionReceipt(omissions) {
713
775
  if (!Array.isArray(omissions) || omissions.length === 0) return undefined;
714
776
  const entries = omissions.map((entry) => ({
@@ -1074,6 +1136,7 @@ async function packageCloudAgent(db, root, opts = {}) {
1074
1136
  }
1075
1137
  // 신원은 해시 계산 전에 채운다 — 클라우드 해시는 agentlas.json 을 포함한다.
1076
1138
  cloudEnsureAgentIdentity(scan, snapshot, rootPath.split("/").pop());
1139
+ cloudFitGeneratedMetadataToLimits(scan);
1077
1140
  snapshot = cloudPackageSnapshot(scan.included);
1078
1141
  const routingCard = isPublicHubPublish ? readCloudRoutingCard(snapshot) : {};
1079
1142
  if (routingCard.finding) scan.findings.push(routingCard.finding);
@@ -30,15 +30,18 @@ async function listOwnedCloudAgents(limit = 100) {
30
30
  };
31
31
  if (!Array.isArray(result.results)) throw new Error("Agent Cloud list returned an invalid results contract.");
32
32
  if (result.results.length) {
33
- const stateValue = state.readCloudAssetState();
34
- for (const raw of result.results) {
35
- const descriptor = normalizeCloudAssetDescriptor(raw, "Agent Cloud list result");
36
- const key = state.cloudDescriptorKey(descriptor);
37
- const previous = stateValue.assets[key];
38
- const preserveRoots = previous && previous.descriptor.cloudId === descriptor.cloudId && previous.descriptor.revision === descriptor.revision;
39
- stateValue.assets[key] = { descriptor, sourceRoots: preserveRoots ? previous.sourceRoots : [] };
40
- }
41
- state.writeCloudAssetState(stateValue);
33
+ const descriptors = result.results.map((raw) => normalizeCloudAssetDescriptor(raw, "Agent Cloud list result"));
34
+ state.updateCloudAssetState((stateValue) => {
35
+ for (const descriptor of descriptors) {
36
+ const key = state.cloudDescriptorKey(descriptor);
37
+ const previous = stateValue.assets[key];
38
+ // A list response may race a save that completed after the server made
39
+ // the list snapshot. Never regress a newer locally observed receipt.
40
+ if (previous && Date.parse(previous.descriptor.updatedAt) > Date.parse(descriptor.updatedAt)) continue;
41
+ const preserveRoots = previous && previous.descriptor.cloudId === descriptor.cloudId && previous.descriptor.revision === descriptor.revision;
42
+ stateValue.assets[key] = { descriptor, sourceRoots: preserveRoots ? previous.sourceRoots : [] };
43
+ }
44
+ });
42
45
  }
43
46
  return result;
44
47
  }