lua-cli 3.16.0 → 3.17.0

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/dist/index.js CHANGED
@@ -132,13 +132,14 @@ var init_semver = __esm({
132
132
  // src/config/constants.ts
133
133
  import { join } from "path";
134
134
  import { homedir } from "os";
135
- var CLI_CONFIG_DIR, VERSION_CHECK_FILE, TELEMETRY_FILE, BASE_URLS, CREDENTIALS_FILE, PREFERRED_AGENT_TYPES, SANDBOX_STORAGE_FILE, POSTHOG_API_KEY, POSTHOG_HOST;
135
+ var CLI_CONFIG_DIR, VERSION_CHECK_FILE, TELEMETRY_FILE, CLI_CACHE_FILE, BASE_URLS, CREDENTIALS_FILE, PREFERRED_AGENT_TYPES, SANDBOX_STORAGE_FILE, POSTHOG_API_KEY, POSTHOG_HOST;
136
136
  var init_constants = __esm({
137
137
  "src/config/constants.ts"() {
138
138
  "use strict";
139
139
  CLI_CONFIG_DIR = join(homedir(), ".lua-cli");
140
140
  VERSION_CHECK_FILE = join(CLI_CONFIG_DIR, "version-check.json");
141
141
  TELEMETRY_FILE = join(CLI_CONFIG_DIR, "telemetry.json");
142
+ CLI_CACHE_FILE = join(CLI_CONFIG_DIR, "cache.json");
142
143
  BASE_URLS = {
143
144
  API: process.env.LUA_API_URL || "https://api.heylua.ai",
144
145
  AUTH: process.env.LUA_AUTH_URL || "https://auth.heylua.ai",
@@ -1526,7 +1527,7 @@ var init_artifact_loader = __esm({
1526
1527
  });
1527
1528
 
1528
1529
  // src/api/backup.api.service.ts
1529
- var BackupApi;
1530
+ var BackupApi, backup_api_service_default;
1530
1531
  var init_backup_api_service = __esm({
1531
1532
  "src/api/backup.api.service.ts"() {
1532
1533
  "use strict";
@@ -1645,7 +1646,20 @@ var init_backup_api_service = __esm({
1645
1646
  Authorization: `Bearer ${this.apiKey}`
1646
1647
  });
1647
1648
  }
1649
+ /**
1650
+ * Get the list of backup versions for this agent.
1651
+ *
1652
+ * @param all - If true, lifts the server-side 50-version cap
1653
+ * @returns Promise resolving to the list of version summaries (newest first)
1654
+ */
1655
+ async getBackupVersions(all = false) {
1656
+ const qs = all ? "?all=true" : "";
1657
+ return this.httpGet(`/developer/agents/${this.agentId}/backup/versions${qs}`, {
1658
+ Authorization: `Bearer ${this.apiKey}`
1659
+ });
1660
+ }
1648
1661
  };
1662
+ backup_api_service_default = BackupApi;
1649
1663
  }
1650
1664
  });
1651
1665
 
@@ -1662,7 +1676,7 @@ async function ensureBundlesUploaded(apiKey, agentId, bundles) {
1662
1676
  uploaded: 0
1663
1677
  };
1664
1678
  }
1665
- const api = new BackupApi(BASE_URLS.API, apiKey, agentId);
1679
+ const api = new backup_api_service_default(BASE_URLS.API, apiKey, agentId);
1666
1680
  const hashes = Array.from(bundles.keys());
1667
1681
  const urlResponse = await api.getBlobUploadUrls(hashes);
1668
1682
  if (!urlResponse.success) {
@@ -4122,8 +4136,12 @@ var init_tool_plugin = __esm({
4122
4136
  displayName = "Tool";
4123
4137
  defineFunction = "defineTool";
4124
4138
  legacyClassNames = [
4125
- "LuaTool"
4139
+ "LuaTool",
4140
+ "LuaVoiceTool"
4126
4141
  ];
4142
+ crossFileRewrite = {
4143
+ copyAllFields: true
4144
+ };
4127
4145
  supportsClassDefinition = true;
4128
4146
  /**
4129
4147
  * Extract metadata from a `defineTool({...})` / `new LuaTool({...})`
@@ -4298,6 +4316,9 @@ var init_job_plugin = __esm({
4298
4316
  legacyClassNames = [
4299
4317
  "LuaJob"
4300
4318
  ];
4319
+ crossFileRewrite = {
4320
+ copyAllFields: true
4321
+ };
4301
4322
  supportsClassDefinition = true;
4302
4323
  /**
4303
4324
  * Class-definition extraction. Reads `name`/`description`/`schedule`/
@@ -4479,6 +4500,9 @@ var init_webhook_plugin = __esm({
4479
4500
  legacyClassNames = [
4480
4501
  "LuaWebhook"
4481
4502
  ];
4503
+ crossFileRewrite = {
4504
+ copyAllFields: true
4505
+ };
4482
4506
  supportsClassDefinition = true;
4483
4507
  /**
4484
4508
  * Class-definition shape: read fields off the inheritance chain
@@ -4705,6 +4729,9 @@ var init_preprocessor_plugin = __esm({
4705
4729
  "PreProcessor",
4706
4730
  "LuaPreprocessor"
4707
4731
  ];
4732
+ crossFileRewrite = {
4733
+ copyAllFields: true
4734
+ };
4708
4735
  /**
4709
4736
  * Class-definition extraction. Reads `name`/`description`/`async`/
4710
4737
  * `priority`/`execute` off the inheritance chain (most-derived wins).
@@ -4788,6 +4815,9 @@ var init_postprocessor_plugin = __esm({
4788
4815
  "PostProcessor",
4789
4816
  "LuaPostprocessor"
4790
4817
  ];
4818
+ crossFileRewrite = {
4819
+ copyAllFields: true
4820
+ };
4791
4821
  /**
4792
4822
  * Class-definition extraction. Reads `name`/`description`/`priority`/
4793
4823
  * `execute` off the inheritance chain.
@@ -4863,6 +4893,9 @@ var init_mcp_server_plugin = __esm({
4863
4893
  legacyClassNames = [
4864
4894
  "LuaMCPServer"
4865
4895
  ];
4896
+ crossFileRewrite = {
4897
+ copyAllFields: true
4898
+ };
4866
4899
  supportsClassDefinition = true;
4867
4900
  /**
4868
4901
  * Class-definition extraction. Reads `name`/`description`/`transport`/
@@ -5034,11 +5067,11 @@ var init_mcp_server_plugin = __esm({
5034
5067
  // ../shared-source-sync/dist/index.mjs
5035
5068
  import { createHash } from "crypto";
5036
5069
  import { extname } from "path";
5037
- import { readdirSync as readdirSync2, readFileSync as readFileSync6, statSync as statSync2 } from "fs";
5070
+ import { readdirSync as readdirSync2, readFileSync as readFileSync7, statSync as statSync2 } from "fs";
5038
5071
  import { join as join4, sep } from "path";
5039
5072
  import { gunzipSync, gzipSync } from "zlib";
5040
- import { existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync22, statSync as statSync22, writeFileSync as writeFileSync5 } from "fs";
5041
- import { dirname as dirname3, join as join22, resolve, sep as sep2 } from "path";
5073
+ import { existsSync as existsSync5, mkdirSync as mkdirSync6, readFileSync as readFileSync22, statSync as statSync22, writeFileSync as writeFileSync6 } from "fs";
5074
+ import { dirname as dirname4, join as join22, resolve, sep as sep2 } from "path";
5042
5075
  import { mkdirSync as mkdirSync22, readdirSync as readdirSync22, readFileSync as readFileSync32, statSync as statSync3, writeFileSync as writeFileSync22 } from "fs";
5043
5076
  import { dirname as dirname22, join as join32, sep as sep3 } from "path";
5044
5077
  import { gunzipSync as gunzipSync2, gzipSync as gzipSync2 } from "zlib";
@@ -5098,7 +5131,7 @@ function walkWorkspace(rootDir, opts = {}) {
5098
5131
  if (stats.size > maxBytes) continue;
5099
5132
  let content;
5100
5133
  try {
5101
- content = readFileSync6(abs);
5134
+ content = readFileSync7(abs);
5102
5135
  } catch {
5103
5136
  continue;
5104
5137
  }
@@ -5215,10 +5248,10 @@ function restoreFromBlobs(manifest, blobs, targetDir, opts = {}) {
5215
5248
  continue;
5216
5249
  }
5217
5250
  }
5218
- mkdirSync5(dirname3(targetPath), {
5251
+ mkdirSync6(dirname4(targetPath), {
5219
5252
  recursive: true
5220
5253
  });
5221
- writeFileSync5(targetPath, content);
5254
+ writeFileSync6(targetPath, content);
5222
5255
  filesWritten++;
5223
5256
  }
5224
5257
  return {
@@ -5413,12 +5446,12 @@ var init_dist2 = __esm({
5413
5446
  this.options = options;
5414
5447
  this.fetchFn = options.fetch ?? fetch;
5415
5448
  }
5416
- url(path18) {
5449
+ url(path19) {
5417
5450
  const base = this.options.baseUrl.replace(/\/$/, "");
5418
- return `${base}/developer/agents/${encodeURIComponent(this.options.agentId)}${path18}`;
5451
+ return `${base}/developer/agents/${encodeURIComponent(this.options.agentId)}${path19}`;
5419
5452
  }
5420
- async json(method, path18, body) {
5421
- const endpoint = this.url(path18);
5453
+ async json(method, path19, body) {
5454
+ const endpoint = this.url(path19);
5422
5455
  const res = await this.fetchFn(endpoint, {
5423
5456
  method,
5424
5457
  headers: {
@@ -5437,7 +5470,7 @@ var init_dist2 = __esm({
5437
5470
  } catch {
5438
5471
  text2 = void 0;
5439
5472
  }
5440
- throw new BackupHttpError(`Backup request failed: ${method} ${path18} \u2192 ${res.status} ${res.statusText}`, res.status, endpoint, text2);
5473
+ throw new BackupHttpError(`Backup request failed: ${method} ${path19} \u2192 ${res.status} ${res.statusText}`, res.status, endpoint, text2);
5441
5474
  }
5442
5475
  const text = await res.text();
5443
5476
  if (!text) return void 0;
@@ -5504,16 +5537,16 @@ var init_dist2 = __esm({
5504
5537
  });
5505
5538
 
5506
5539
  // src/compiler/utils/common.ts
5507
- import path3 from "path";
5540
+ import path4 from "path";
5508
5541
  function hashContent(content) {
5509
5542
  return hashContentTruncated(content);
5510
5543
  }
5511
5544
  function isInside(child, parent) {
5512
- const resolvedChild = path3.resolve(child);
5513
- const resolvedParent = path3.resolve(parent);
5545
+ const resolvedChild = path4.resolve(child);
5546
+ const resolvedParent = path4.resolve(parent);
5514
5547
  if (resolvedChild === resolvedParent) return true;
5515
- const rel = path3.relative(resolvedParent, resolvedChild);
5516
- return !!rel && !rel.startsWith("..") && !path3.isAbsolute(rel);
5548
+ const rel = path4.relative(resolvedParent, resolvedChild);
5549
+ return !!rel && !rel.startsWith("..") && !path4.isAbsolute(rel);
5517
5550
  }
5518
5551
  function classifyProjectFile(name) {
5519
5552
  if (name.endsWith(".ts") || name.endsWith(".tsx") || name.endsWith(".js")) {
@@ -5542,20 +5575,20 @@ var init_common = __esm({
5542
5575
  });
5543
5576
 
5544
5577
  // src/compiler/utils/path-resolver.ts
5545
- import * as fs3 from "fs";
5546
- import * as path4 from "path";
5578
+ import * as fs4 from "fs";
5579
+ import * as path5 from "path";
5547
5580
  function getTsConfigPathMappings(rootDir) {
5548
- const resolvedRoot = path4.resolve(rootDir);
5581
+ const resolvedRoot = path5.resolve(rootDir);
5549
5582
  if (mappingsCache.has(resolvedRoot)) {
5550
5583
  return mappingsCache.get(resolvedRoot) ?? null;
5551
5584
  }
5552
- const tsconfigPath = path4.join(resolvedRoot, "tsconfig.json");
5553
- if (!fs3.existsSync(tsconfigPath)) {
5585
+ const tsconfigPath = path5.join(resolvedRoot, "tsconfig.json");
5586
+ if (!fs4.existsSync(tsconfigPath)) {
5554
5587
  mappingsCache.set(resolvedRoot, null);
5555
5588
  return null;
5556
5589
  }
5557
5590
  try {
5558
- const tsconfigContent = fs3.readFileSync(tsconfigPath, "utf-8");
5591
+ const tsconfigContent = fs4.readFileSync(tsconfigPath, "utf-8");
5559
5592
  const tsconfig = JSON.parse(tsconfigContent);
5560
5593
  const compilerOptions = tsconfig.compilerOptions || {};
5561
5594
  if (compilerOptions.paths) {
@@ -5582,7 +5615,7 @@ function resolvePathAlias(specifier, rootDir) {
5582
5615
  if (match) {
5583
5616
  const captured = match[1] || "";
5584
5617
  const targetPath = targets[0].replace("*", captured);
5585
- return path4.join(mappings.rootDir, mappings.baseUrl, targetPath);
5618
+ return path5.join(mappings.rootDir, mappings.baseUrl, targetPath);
5586
5619
  }
5587
5620
  }
5588
5621
  return null;
@@ -5594,20 +5627,20 @@ function resolveModuleSpecifier(specifier, fromFile, rootDir) {
5594
5627
  return tryExtensions(aliasResolved);
5595
5628
  }
5596
5629
  if (normalized.startsWith("./") || normalized.startsWith("../")) {
5597
- const basePath = path4.resolve(path4.dirname(fromFile), normalized);
5630
+ const basePath = path5.resolve(path5.dirname(fromFile), normalized);
5598
5631
  return tryExtensions(basePath);
5599
5632
  }
5600
5633
  return void 0;
5601
5634
  }
5602
5635
  function tryExtensions(basePath) {
5603
5636
  for (const ext of MODULE_EXTENSIONS) {
5604
- const full = ext.startsWith("/") ? path4.join(basePath, ext) : basePath + ext;
5605
- if (fs3.existsSync(full)) return full;
5637
+ const full = ext.startsWith("/") ? path5.join(basePath, ext) : basePath + ext;
5638
+ if (fs4.existsSync(full)) return full;
5606
5639
  }
5607
5640
  return void 0;
5608
5641
  }
5609
5642
  function registerProjectRootDir(project, rootDir) {
5610
- projectRootDirs.set(project, path4.resolve(rootDir));
5643
+ projectRootDirs.set(project, path5.resolve(rootDir));
5611
5644
  }
5612
5645
  function getProjectRootDir(project) {
5613
5646
  return projectRootDirs.get(project);
@@ -6207,7 +6240,7 @@ var init_reference_resolver = __esm({
6207
6240
  });
6208
6241
 
6209
6242
  // src/compiler/plugins/skill.plugin.ts
6210
- import fs4 from "fs/promises";
6243
+ import fs5 from "fs/promises";
6211
6244
  import { Node as Node11 } from "ts-morph";
6212
6245
  function findEnclosingVariableDeclaration(config) {
6213
6246
  let current = config.getParent();
@@ -6256,6 +6289,22 @@ var init_skill_plugin = __esm({
6256
6289
  legacyClassNames = [
6257
6290
  "LuaSkill"
6258
6291
  ];
6292
+ // Whitelist drops `tools` for the same reasons agent drops skills/webhooks/jobs:
6293
+ // 1. Bundle size — preserving class references drags tool source files into
6294
+ // the parent bundle.
6295
+ // 2. Residual super-edge cases — `stripSuperCallsInConstructors` covers
6296
+ // top-level `super(...)` only; `super.method()` mid-constructor and
6297
+ // `field = super.x` survive a strip and explode at runtime.
6298
+ // MAINTENANCE: this whitelist also assumes skills are JSON-only artifacts (via
6299
+ // `buildArtifact`). If skills ever migrate to bundled JS, this drop will silently
6300
+ // lose `tools` from skill bundles — re-evaluate.
6301
+ crossFileRewrite = {
6302
+ fields: [
6303
+ "name",
6304
+ "description",
6305
+ "context"
6306
+ ]
6307
+ };
6259
6308
  supportsClassDefinition = true;
6260
6309
  /**
6261
6310
  * Class-definition extraction. Reads `name`/`description`/`context`/
@@ -6524,7 +6573,7 @@ var init_skill_plugin = __esm({
6524
6573
  description: metadata.description,
6525
6574
  metadata: this.sanitizeForPersistence(metadata.metadata)
6526
6575
  }, null, 2);
6527
- const originalSource = await fs4.readFile(metadata.sourcePath, "utf-8");
6576
+ const originalSource = await fs5.readFile(metadata.sourcePath, "utf-8");
6528
6577
  return {
6529
6578
  code,
6530
6579
  sourceMap: "",
@@ -6647,6 +6696,22 @@ var init_agent_plugin = __esm({
6647
6696
  legacyClassNames = [
6648
6697
  "LuaAgent"
6649
6698
  ];
6699
+ // Whitelist (not copyAllFields). Two reasons, both real:
6700
+ // 1. Bundle size — `skills`/`webhooks`/`jobs`/etc. hold CLASS references;
6701
+ // preserving them drags every subclass source file into the agent bundle.
6702
+ // 2. Residual super-edge cases — `stripSuperCallsInConstructors` handles
6703
+ // top-level `super(...)`, but NOT `super.method()` mid-constructor or
6704
+ // `field = super.x` initializers, both of which still orphan after
6705
+ // `stripSdkExtendsClauses` runs on dragged-in files.
6706
+ // Agent runtime only needs name/persona/model; references are resolved by `resolveArrayRefs`.
6707
+ crossFileRewrite = {
6708
+ fields: [
6709
+ "name",
6710
+ "description",
6711
+ "persona",
6712
+ "model"
6713
+ ]
6714
+ };
6650
6715
  supportsClassDefinition = true;
6651
6716
  /**
6652
6717
  * Class-definition extraction. Reads the agent's own metadata
@@ -6993,6 +7058,9 @@ var init_device_plugin = __esm({
6993
7058
  legacyClassNames = [
6994
7059
  "LuaDevice"
6995
7060
  ];
7061
+ crossFileRewrite = {
7062
+ copyAllFields: true
7063
+ };
6996
7064
  supportsClassDefinition = true;
6997
7065
  /**
6998
7066
  * Class-definition extraction. Reads `name`/`description`/`group`
@@ -7115,6 +7183,9 @@ var init_device_trigger_plugin = __esm({
7115
7183
  legacyClassNames = [
7116
7184
  "LuaDeviceTrigger"
7117
7185
  ];
7186
+ crossFileRewrite = {
7187
+ copyAllFields: true
7188
+ };
7118
7189
  supportsClassDefinition = true;
7119
7190
  /**
7120
7191
  * Class-definition extraction. Reads `name`/`description`/has-flags
@@ -7255,13 +7326,355 @@ var init_device_trigger_plugin = __esm({
7255
7326
  }
7256
7327
  });
7257
7328
 
7329
+ // src/compiler/utils/primitive-rewrite.ts
7330
+ import { readFileSync as readFileSync9 } from "fs";
7331
+ import { Node as Node14, Project, ts as ts3 } from "ts-morph";
7332
+ function rewritePrimitiveSource(metadata, opts) {
7333
+ const sourceCode = readFileSync9(metadata.sourcePath, "utf-8");
7334
+ const localProject = new Project({
7335
+ useInMemoryFileSystem: true,
7336
+ compilerOptions: {
7337
+ allowJs: true,
7338
+ target: 99,
7339
+ module: 99
7340
+ }
7341
+ });
7342
+ const sf = localProject.createSourceFile(`__rewrite_${metadata.kind}_${metadata.name}.ts`, sourceCode);
7343
+ const isPrimitiveCall = /* @__PURE__ */ __name((n) => {
7344
+ if (Node14.isNewExpression(n)) {
7345
+ const callee = n.getExpression();
7346
+ return Node14.isIdentifier(callee) && opts.constructorNames.includes(callee.getText());
7347
+ }
7348
+ if (Node14.isCallExpression(n)) {
7349
+ const callee = n.getExpression();
7350
+ return Node14.isIdentifier(callee) && opts.defineFunctionName !== void 0 && callee.getText() === opts.defineFunctionName;
7351
+ }
7352
+ return false;
7353
+ }, "isPrimitiveCall");
7354
+ const callNode = findCallByExportName(sf, metadata, isPrimitiveCall);
7355
+ if (!callNode) {
7356
+ throw new Error(`Primitive call for export "${metadata.exportName}" (kind=${metadata.kind}) not found in ${metadata.sourcePath}`);
7357
+ }
7358
+ const arg = callNode.getArguments()[0];
7359
+ if (!arg || !Node14.isObjectLiteralExpression(arg)) {
7360
+ throw new Error(`Primitive config arg is not an object literal in ${metadata.sourcePath}`);
7361
+ }
7362
+ const synthesized = opts.buildLiteral(arg, {
7363
+ metadata,
7364
+ compilerVersion: COMPILER_VERSION
7365
+ });
7366
+ callNode.replaceWithText(synthesized);
7367
+ sf.forEachDescendant((n) => {
7368
+ if (n === callNode) return;
7369
+ if (isPrimitiveCall(n)) n.replaceWithText("undefined");
7370
+ });
7371
+ rewriteCrossFileCallsInSourceFile(sf, opts.crossFileSpecs, opts.sdkBaseClassNames);
7372
+ stripLuaCliImports(sf);
7373
+ ensureDefaultExport(sf, metadata);
7374
+ return sf.getText();
7375
+ }
7376
+ function rewriteCrossFileCallsInSourceFile(sf, specs, sdkBaseClassNames) {
7377
+ warnUnsupportedNamespaceImports(sf);
7378
+ const sdkIdentifiers = new Set(sdkBaseClassNames);
7379
+ for (const spec of specs) {
7380
+ if (spec.defineFunction) sdkIdentifiers.add(spec.defineFunction);
7381
+ }
7382
+ const aliasMap = buildSdkAliasMap(sf, sdkIdentifiers);
7383
+ let progressed = true;
7384
+ while (progressed) {
7385
+ progressed = false;
7386
+ const callNode = findFirstCrossFileSdkCall(sf, specs, aliasMap);
7387
+ if (!callNode) break;
7388
+ const spec = callNode.spec;
7389
+ const node = callNode.node;
7390
+ const arg = node.getArguments()[0];
7391
+ const objArg = arg && Node14.isObjectLiteralExpression(arg) ? arg : void 0;
7392
+ node.replaceWithText(buildBareObjectLiteral(spec, objArg));
7393
+ progressed = true;
7394
+ }
7395
+ stripSdkExtendsClauses(sf, sdkBaseClassNames, aliasMap);
7396
+ }
7397
+ function warnUnsupportedNamespaceImports(sf) {
7398
+ for (const imp of sf.getImportDeclarations()) {
7399
+ const spec = imp.getModuleSpecifierValue();
7400
+ if (spec !== "lua-cli" && spec !== "lua-cli/skill" && spec !== "lua-cli/voice") continue;
7401
+ const ns = imp.getNamespaceImport();
7402
+ if (!ns) continue;
7403
+ const localName = ns.getText();
7404
+ const pos = sf.getLineAndColumnAtPos(imp.getStart());
7405
+ console.warn(formatSarifWarning({
7406
+ ruleId: "lua/unsupported-namespace-import",
7407
+ filePath: sf.getFilePath(),
7408
+ line: pos.line,
7409
+ column: pos.column,
7410
+ message: `namespace import \`import * as ${localName} from '${spec}'\` is not supported by the cross-file rewrite. \`new ${localName}.X({...})\` calls will leak \`${localName}\` into the bundle as an undefined identifier.`,
7411
+ hint: `Use named imports instead: \`import { LuaTool, LuaWebhook, ... } from '${spec}'\`.`
7412
+ }));
7413
+ }
7414
+ }
7415
+ function stripSdkExtendsClauses(sf, sdkBaseClassNames, aliasMap) {
7416
+ const matchesSdkClass = /* @__PURE__ */ __name((text) => {
7417
+ const canonical = aliasMap.get(text) ?? text;
7418
+ return sdkBaseClassNames.has(canonical);
7419
+ }, "matchesSdkClass");
7420
+ for (const classDecl of sf.getClasses()) {
7421
+ const ext = classDecl.getExtends();
7422
+ if (!ext) continue;
7423
+ const expr = ext.getExpression();
7424
+ if (!Node14.isIdentifier(expr)) continue;
7425
+ if (matchesSdkClass(expr.getText())) {
7426
+ classDecl.removeExtends();
7427
+ stripSuperCallsInConstructors(classDecl);
7428
+ }
7429
+ }
7430
+ sf.forEachDescendant((n) => {
7431
+ if (!Node14.isClassExpression(n)) return;
7432
+ const ext = n.getExtends();
7433
+ if (!ext) return;
7434
+ const expr = ext.getExpression();
7435
+ if (!Node14.isIdentifier(expr)) return;
7436
+ if (matchesSdkClass(expr.getText())) {
7437
+ n.removeExtends();
7438
+ stripSuperCallsInConstructors(n);
7439
+ }
7440
+ });
7441
+ }
7442
+ function stripSuperCallsInConstructors(classNode) {
7443
+ for (const ctor of classNode.getConstructors()) {
7444
+ const body = ctor.getBody();
7445
+ if (!body || !Node14.isBlock(body)) continue;
7446
+ const toRemove = [];
7447
+ for (const stmt of body.getStatements()) {
7448
+ if (!Node14.isExpressionStatement(stmt)) continue;
7449
+ const expr = stmt.getExpression();
7450
+ if (!Node14.isCallExpression(expr)) continue;
7451
+ if (expr.getExpression().getKind() !== ts3.SyntaxKind.SuperKeyword) continue;
7452
+ toRemove.push(stmt);
7453
+ }
7454
+ for (const stmt of toRemove) stmt.remove();
7455
+ }
7456
+ warnRemainingSuperReferences(classNode);
7457
+ }
7458
+ function warnRemainingSuperReferences(classNode) {
7459
+ const sf = classNode.getSourceFile();
7460
+ classNode.forEachDescendant((n) => {
7461
+ if (n.getKind() !== ts3.SyntaxKind.SuperKeyword) return;
7462
+ const parent = n.getParent();
7463
+ if (parent && Node14.isCallExpression(parent) && parent.getExpression() === n && Node14.isExpressionStatement(parent.getParent())) {
7464
+ return;
7465
+ }
7466
+ const pos = sf.getLineAndColumnAtPos(n.getStart());
7467
+ console.warn(formatSarifWarning({
7468
+ ruleId: "lua/orphan-super-reference",
7469
+ filePath: sf.getFilePath(),
7470
+ line: pos.line,
7471
+ column: pos.column,
7472
+ message: `\`super\` reference will become orphan after the SDK base class is stripped from the bundle. The compiled artifact will throw \`SyntaxError: 'super' keyword is only allowed in a derived class\` at sandbox eval.`,
7473
+ hint: `Move the override into a class field initializer (the canonical subclass-with-field-override pattern), or stop subclassing the SDK base and use the \`defineX({...})\` / \`new LuaX({...})\` form.`
7474
+ }));
7475
+ });
7476
+ }
7477
+ function buildSdkAliasMap(sf, sdkIdentifiers) {
7478
+ const map = /* @__PURE__ */ new Map();
7479
+ for (const imp of sf.getImportDeclarations()) {
7480
+ const spec = imp.getModuleSpecifierValue();
7481
+ if (spec !== "lua-cli" && spec !== "lua-cli/skill" && spec !== "lua-cli/voice") continue;
7482
+ for (const named of imp.getNamedImports()) {
7483
+ const exported = named.getName();
7484
+ const alias = named.getAliasNode()?.getText();
7485
+ if (!sdkIdentifiers.has(exported)) continue;
7486
+ const local = alias ?? exported;
7487
+ map.set(local, exported);
7488
+ }
7489
+ }
7490
+ let progressed = true;
7491
+ while (progressed) {
7492
+ progressed = false;
7493
+ for (const vd of sf.getVariableDeclarations()) {
7494
+ const init = vd.getInitializer();
7495
+ if (!init || !Node14.isIdentifier(init)) continue;
7496
+ const name = vd.getName();
7497
+ if (map.has(name)) continue;
7498
+ const initText = init.getText();
7499
+ const canonical = map.get(initText) ?? (sdkIdentifiers.has(initText) ? initText : void 0);
7500
+ if (canonical) {
7501
+ map.set(name, canonical);
7502
+ progressed = true;
7503
+ }
7504
+ }
7505
+ }
7506
+ return map;
7507
+ }
7508
+ function findFirstCrossFileSdkCall(sf, specs, aliasMap) {
7509
+ let found;
7510
+ sf.forEachDescendant((n) => {
7511
+ if (found) return;
7512
+ if (Node14.isNewExpression(n)) {
7513
+ const callee = n.getExpression();
7514
+ if (!Node14.isIdentifier(callee)) return;
7515
+ const canonical = aliasMap.get(callee.getText()) ?? callee.getText();
7516
+ const spec = specs.find((s) => s.classNames.includes(canonical));
7517
+ if (spec) found = {
7518
+ node: n,
7519
+ spec
7520
+ };
7521
+ } else if (Node14.isCallExpression(n)) {
7522
+ const callee = n.getExpression();
7523
+ if (!Node14.isIdentifier(callee)) return;
7524
+ const canonical = aliasMap.get(callee.getText()) ?? callee.getText();
7525
+ const spec = specs.find((s) => s.defineFunction !== void 0 && s.defineFunction === canonical);
7526
+ if (spec) found = {
7527
+ node: n,
7528
+ spec
7529
+ };
7530
+ }
7531
+ });
7532
+ return found;
7533
+ }
7534
+ function buildBareObjectLiteral(spec, arg) {
7535
+ if (!arg) return "{}";
7536
+ if (spec.mode === "copyAllFields") {
7537
+ return arg.getText();
7538
+ }
7539
+ const parts = [];
7540
+ for (const f of spec.fields) {
7541
+ const prop = arg.getProperty(f);
7542
+ if (prop && Node14.isPropertyAssignment(prop)) {
7543
+ const init = prop.getInitializer();
7544
+ if (init) parts.push(`${f}: ${init.getText()}`);
7545
+ } else if (prop && Node14.isShorthandPropertyAssignment(prop)) {
7546
+ parts.push(`${f}: ${prop.getName()}`);
7547
+ }
7548
+ }
7549
+ return `{ ${parts.join(", ")} }`;
7550
+ }
7551
+ function findCallByExportName(sf, metadata, isPrimitiveCall) {
7552
+ if (metadata.isDefaultExport) {
7553
+ for (const stmt of sf.getStatements()) {
7554
+ if (!Node14.isExportAssignment(stmt)) continue;
7555
+ const expr = stmt.getExpression();
7556
+ if (isPrimitiveCall(expr)) return expr;
7557
+ if (Node14.isIdentifier(expr)) {
7558
+ const varDecl2 = sf.getVariableDeclaration(expr.getText());
7559
+ const init2 = varDecl2?.getInitializer();
7560
+ if (init2 && isPrimitiveCall(init2)) return init2;
7561
+ }
7562
+ }
7563
+ return void 0;
7564
+ }
7565
+ const varDecl = sf.getVariableDeclaration(metadata.exportName);
7566
+ const init = varDecl?.getInitializer();
7567
+ if (init && isPrimitiveCall(init)) return init;
7568
+ return void 0;
7569
+ }
7570
+ function stripLuaCliImports(sf) {
7571
+ for (const imp of sf.getImportDeclarations()) {
7572
+ const spec = imp.getModuleSpecifierValue();
7573
+ if (spec === "lua-cli" || spec === "lua-cli/skill" || spec === "lua-cli/voice") {
7574
+ imp.remove();
7575
+ }
7576
+ }
7577
+ }
7578
+ function ensureDefaultExport(sf, metadata) {
7579
+ if (metadata.isDefaultExport) return;
7580
+ const existingDefault = sf.getStatements().find(Node14.isExportAssignment);
7581
+ if (!existingDefault) {
7582
+ sf.addStatements(`
7583
+ export default ${metadata.exportName};`);
7584
+ return;
7585
+ }
7586
+ const expr = existingDefault.getExpression();
7587
+ const alreadyCorrect = Node14.isIdentifier(expr) && expr.getText() === metadata.exportName;
7588
+ if (!alreadyCorrect) {
7589
+ existingDefault.remove();
7590
+ sf.addStatements(`
7591
+ export default ${metadata.exportName};`);
7592
+ }
7593
+ }
7594
+ function getPropertyText(arg, name) {
7595
+ const prop = arg.getProperty(name);
7596
+ if (prop && Node14.isPropertyAssignment(prop)) {
7597
+ return prop.getInitializer()?.getText();
7598
+ }
7599
+ if (prop && Node14.isShorthandPropertyAssignment(prop)) {
7600
+ return prop.getName();
7601
+ }
7602
+ return void 0;
7603
+ }
7604
+ function buildLuaPrimitiveMarker(metadata, kind) {
7605
+ return `__lua_primitive__: { version: ${JSON.stringify(COMPILER_VERSION)}, kind: '${kind}', name: ${JSON.stringify(metadata.name)}, description: ${JSON.stringify(metadata.description)}, exportName: ${JSON.stringify(metadata.exportName)} }`;
7606
+ }
7607
+ var init_primitive_rewrite = __esm({
7608
+ "src/compiler/utils/primitive-rewrite.ts"() {
7609
+ "use strict";
7610
+ init_types();
7611
+ init_reference_resolver();
7612
+ __name(rewritePrimitiveSource, "rewritePrimitiveSource");
7613
+ __name(rewriteCrossFileCallsInSourceFile, "rewriteCrossFileCallsInSourceFile");
7614
+ __name(warnUnsupportedNamespaceImports, "warnUnsupportedNamespaceImports");
7615
+ __name(stripSdkExtendsClauses, "stripSdkExtendsClauses");
7616
+ __name(stripSuperCallsInConstructors, "stripSuperCallsInConstructors");
7617
+ __name(warnRemainingSuperReferences, "warnRemainingSuperReferences");
7618
+ __name(buildSdkAliasMap, "buildSdkAliasMap");
7619
+ __name(findFirstCrossFileSdkCall, "findFirstCrossFileSdkCall");
7620
+ __name(buildBareObjectLiteral, "buildBareObjectLiteral");
7621
+ __name(findCallByExportName, "findCallByExportName");
7622
+ __name(stripLuaCliImports, "stripLuaCliImports");
7623
+ __name(ensureDefaultExport, "ensureDefaultExport");
7624
+ __name(getPropertyText, "getPropertyText");
7625
+ __name(buildLuaPrimitiveMarker, "buildLuaPrimitiveMarker");
7626
+ }
7627
+ });
7628
+
7629
+ // src/compiler/utils/cross-file-specs.ts
7630
+ function buildCrossFileSpecs(plugins = pluginRegistry.getAll()) {
7631
+ const specs = [];
7632
+ for (const plugin of plugins) {
7633
+ if (!plugin.crossFileRewrite) continue;
7634
+ const classNames = [
7635
+ ...plugin.legacyClassNames
7636
+ ];
7637
+ const defineFunction = plugin.defineFunction || void 0;
7638
+ if ("copyAllFields" in plugin.crossFileRewrite && plugin.crossFileRewrite.copyAllFields) {
7639
+ specs.push({
7640
+ classNames,
7641
+ defineFunction,
7642
+ mode: "copyAllFields"
7643
+ });
7644
+ } else if ("fields" in plugin.crossFileRewrite) {
7645
+ specs.push({
7646
+ classNames,
7647
+ defineFunction,
7648
+ mode: "whitelist",
7649
+ fields: plugin.crossFileRewrite.fields
7650
+ });
7651
+ }
7652
+ }
7653
+ return specs;
7654
+ }
7655
+ function buildSdkBaseClassNames(plugins = pluginRegistry.getAll()) {
7656
+ const names = /* @__PURE__ */ new Set();
7657
+ for (const plugin of plugins) {
7658
+ for (const cls of plugin.legacyClassNames) names.add(cls);
7659
+ }
7660
+ return names;
7661
+ }
7662
+ var init_cross_file_specs = __esm({
7663
+ "src/compiler/utils/cross-file-specs.ts"() {
7664
+ "use strict";
7665
+ init_registry();
7666
+ __name(buildCrossFileSpecs, "buildCrossFileSpecs");
7667
+ __name(buildSdkBaseClassNames, "buildSdkBaseClassNames");
7668
+ }
7669
+ });
7670
+
7258
7671
  // src/compiler/plugins/voice.plugin.ts
7259
- import fs5 from "fs/promises";
7260
- import { Node as Node14 } from "ts-morph";
7672
+ import fs6 from "fs/promises";
7673
+ import { Node as Node15 } from "ts-morph";
7261
7674
  function astObjectToPlain(obj) {
7262
7675
  const result = {};
7263
7676
  for (const prop of obj.getProperties()) {
7264
- if (Node14.isSpreadAssignment(prop)) {
7677
+ if (Node15.isSpreadAssignment(prop)) {
7265
7678
  const expr = prop.getExpression();
7266
7679
  const evaluated = astValueToPlain(expr);
7267
7680
  if (evaluated && typeof evaluated === "object" && !Array.isArray(evaluated)) {
@@ -7269,14 +7682,14 @@ function astObjectToPlain(obj) {
7269
7682
  }
7270
7683
  continue;
7271
7684
  }
7272
- if (Node14.isShorthandPropertyAssignment(prop)) {
7685
+ if (Node15.isShorthandPropertyAssignment(prop)) {
7273
7686
  const key2 = prop.getName();
7274
7687
  if (!key2) continue;
7275
7688
  const v2 = astValueToPlain(prop.getNameNode());
7276
7689
  if (v2 !== void 0) result[key2] = v2;
7277
7690
  continue;
7278
7691
  }
7279
- if (!Node14.isPropertyAssignment(prop)) continue;
7692
+ if (!Node15.isPropertyAssignment(prop)) continue;
7280
7693
  const key = prop.getName();
7281
7694
  if (!key) continue;
7282
7695
  const value = prop.getInitializer();
@@ -7287,16 +7700,16 @@ function astObjectToPlain(obj) {
7287
7700
  return result;
7288
7701
  }
7289
7702
  function astValueToPlain(value) {
7290
- if (Node14.isStringLiteral(value) || Node14.isNoSubstitutionTemplateLiteral(value)) {
7703
+ if (Node15.isStringLiteral(value) || Node15.isNoSubstitutionTemplateLiteral(value)) {
7291
7704
  return value.getLiteralText();
7292
7705
  }
7293
- if (Node14.isNumericLiteral(value)) {
7706
+ if (Node15.isNumericLiteral(value)) {
7294
7707
  return Number(value.getLiteralText());
7295
7708
  }
7296
- if (Node14.isTrueLiteral(value)) return true;
7297
- if (Node14.isFalseLiteral(value)) return false;
7298
- if (Node14.isObjectLiteralExpression(value)) return astObjectToPlain(value);
7299
- if (Node14.isArrayLiteralExpression(value)) {
7709
+ if (Node15.isTrueLiteral(value)) return true;
7710
+ if (Node15.isFalseLiteral(value)) return false;
7711
+ if (Node15.isObjectLiteralExpression(value)) return astObjectToPlain(value);
7712
+ if (Node15.isArrayLiteralExpression(value)) {
7300
7713
  return value.getElements().map((el) => astValueToPlain(el)).filter((x) => x !== void 0);
7301
7714
  }
7302
7715
  return void 0;
@@ -7355,15 +7768,36 @@ function stripKeys(obj, keys) {
7355
7768
  }
7356
7769
  function leftmostIdentifierName(node) {
7357
7770
  let current = node;
7358
- while (Node14.isPropertyAccessExpression(current)) {
7771
+ while (Node15.isPropertyAccessExpression(current)) {
7359
7772
  current = current.getExpression();
7360
7773
  }
7361
- return Node14.isIdentifier(current) ? current.getText() : void 0;
7774
+ return Node15.isIdentifier(current) ? current.getText() : void 0;
7775
+ }
7776
+ function resolveProviderAlias(node, localName) {
7777
+ const sourceFile = node.getSourceFile();
7778
+ for (const imp of sourceFile.getImportDeclarations()) {
7779
+ const moduleSpec = imp.getModuleSpecifierValue();
7780
+ const livekitPlugin = moduleSpec.startsWith("@livekit/agents-plugin-");
7781
+ const isLuaCliVoice = moduleSpec === "lua-cli/voice";
7782
+ const isLivekitAgents = moduleSpec === "@livekit/agents";
7783
+ if (!isLuaCliVoice && !livekitPlugin && !isLivekitAgents) continue;
7784
+ for (const named of imp.getNamedImports()) {
7785
+ const alias = named.getAliasNode()?.getText();
7786
+ const exported = named.getName();
7787
+ const bound = alias ?? exported;
7788
+ if (bound === localName) return exported;
7789
+ }
7790
+ const ns = imp.getNamespaceImport();
7791
+ if (ns && ns.getText() === localName && livekitPlugin) {
7792
+ return moduleSpec.replace("@livekit/agents-plugin-", "");
7793
+ }
7794
+ }
7795
+ return localName;
7362
7796
  }
7363
7797
  function extractRealtimeFromNewExpression(newExpr, classExpr, field) {
7364
7798
  if (classExpr.getName() !== "RealtimeModel") return void 0;
7365
7799
  const realtimeNode = classExpr.getExpression();
7366
- if (!Node14.isPropertyAccessExpression(realtimeNode) || realtimeNode.getName() !== "realtime") {
7800
+ if (!Node15.isPropertyAccessExpression(realtimeNode) || realtimeNode.getName() !== "realtime") {
7367
7801
  return void 0;
7368
7802
  }
7369
7803
  if (field !== "llm") {
@@ -7374,7 +7808,8 @@ function extractRealtimeFromNewExpression(newExpr, classExpr, field) {
7374
7808
  }
7375
7809
  };
7376
7810
  }
7377
- const provider = leftmostIdentifierName(realtimeNode.getExpression());
7811
+ const providerLocal = leftmostIdentifierName(realtimeNode.getExpression());
7812
+ const provider = providerLocal ? resolveProviderAlias(realtimeNode, providerLocal) : void 0;
7378
7813
  if (!provider) {
7379
7814
  return {
7380
7815
  error: {
@@ -7393,7 +7828,7 @@ function extractRealtimeFromNewExpression(newExpr, classExpr, field) {
7393
7828
  }
7394
7829
  const optsArg = newExpr.getArguments()[0];
7395
7830
  let options = {};
7396
- if (optsArg && Node14.isObjectLiteralExpression(optsArg)) {
7831
+ if (optsArg && Node15.isObjectLiteralExpression(optsArg)) {
7397
7832
  options = astObjectToPlain(optsArg);
7398
7833
  }
7399
7834
  return {
@@ -7406,7 +7841,7 @@ function extractRealtimeFromNewExpression(newExpr, classExpr, field) {
7406
7841
  }
7407
7842
  function extractFromNewExpression(newExpr, field) {
7408
7843
  const expr = newExpr.getExpression();
7409
- if (!Node14.isPropertyAccessExpression(expr)) {
7844
+ if (!Node15.isPropertyAccessExpression(expr)) {
7410
7845
  return {
7411
7846
  error: {
7412
7847
  field,
@@ -7416,7 +7851,7 @@ function extractFromNewExpression(newExpr, field) {
7416
7851
  }
7417
7852
  const realtimeResult = extractRealtimeFromNewExpression(newExpr, expr, field);
7418
7853
  if (realtimeResult) return realtimeResult;
7419
- const moduleName = expr.getExpression().getText();
7854
+ const moduleName = resolveProviderAlias(expr, expr.getExpression().getText());
7420
7855
  const className = expr.getName();
7421
7856
  if (!PLUGIN_CLASSES.includes(className)) {
7422
7857
  return {
@@ -7436,7 +7871,7 @@ function extractFromNewExpression(newExpr, field) {
7436
7871
  }
7437
7872
  const optsArg = newExpr.getArguments()[0];
7438
7873
  let options = {};
7439
- if (optsArg && Node14.isObjectLiteralExpression(optsArg)) {
7874
+ if (optsArg && Node15.isObjectLiteralExpression(optsArg)) {
7440
7875
  options = astObjectToPlain(optsArg);
7441
7876
  }
7442
7877
  if (moduleName === "inference") {
@@ -7484,7 +7919,7 @@ function extractFromNewExpression(newExpr, field) {
7484
7919
  }
7485
7920
  function unwrapType(node) {
7486
7921
  let current = node;
7487
- while (Node14.isAsExpression(current) || Node14.isSatisfiesExpression(current) || Node14.isParenthesizedExpression(current)) {
7922
+ while (Node15.isAsExpression(current) || Node15.isSatisfiesExpression(current) || Node15.isParenthesizedExpression(current)) {
7488
7923
  const inner = current.getExpression?.();
7489
7924
  if (!inner) break;
7490
7925
  current = inner;
@@ -7493,11 +7928,11 @@ function unwrapType(node) {
7493
7928
  }
7494
7929
  function extractModelField(config, field) {
7495
7930
  const prop = config.getProperty(field);
7496
- if (!prop || !Node14.isPropertyAssignment(prop)) return {};
7931
+ if (!prop || !Node15.isPropertyAssignment(prop)) return {};
7497
7932
  const initializer = prop.getInitializer();
7498
7933
  if (!initializer) return {};
7499
7934
  const value = unwrapType(initializer);
7500
- if (Node14.isStringLiteral(value) || Node14.isNoSubstitutionTemplateLiteral(value)) {
7935
+ if (Node15.isStringLiteral(value) || Node15.isNoSubstitutionTemplateLiteral(value)) {
7501
7936
  const text = value.getLiteralText();
7502
7937
  if (!text) {
7503
7938
  return {
@@ -7509,7 +7944,7 @@ function extractModelField(config, field) {
7509
7944
  }
7510
7945
  return parseDescriptor(text, field);
7511
7946
  }
7512
- if (Node14.isObjectLiteralExpression(value)) {
7947
+ if (Node15.isObjectLiteralExpression(value)) {
7513
7948
  if (field !== "tts") {
7514
7949
  return {
7515
7950
  error: {
@@ -7536,7 +7971,7 @@ function extractModelField(config, field) {
7536
7971
  model: out
7537
7972
  };
7538
7973
  }
7539
- if (Node14.isNewExpression(value)) {
7974
+ if (Node15.isNewExpression(value)) {
7540
7975
  return extractFromNewExpression(value, field);
7541
7976
  }
7542
7977
  return {
@@ -7549,20 +7984,23 @@ function extractModelField(config, field) {
7549
7984
  function hasFunctionProperty(config, propertyName) {
7550
7985
  const prop = config.getProperty(propertyName);
7551
7986
  if (!prop) return false;
7552
- if (Node14.isMethodDeclaration(prop)) return true;
7553
- if (!Node14.isPropertyAssignment(prop)) return false;
7987
+ if (Node15.isMethodDeclaration(prop)) return true;
7988
+ if (!Node15.isPropertyAssignment(prop)) return false;
7554
7989
  const value = prop.getInitializer();
7555
7990
  if (!value) return false;
7556
- if (Node14.isArrowFunction(value) || Node14.isFunctionExpression(value)) return true;
7557
- if (Node14.isIdentifier(value)) return true;
7991
+ if (Node15.isArrowFunction(value) || Node15.isFunctionExpression(value)) return true;
7992
+ if (Node15.isIdentifier(value)) return true;
7558
7993
  return false;
7559
7994
  }
7560
7995
  function hasArrayProperty(config, propertyName) {
7561
7996
  const prop = config.getProperty(propertyName);
7562
- if (!prop || !Node14.isPropertyAssignment(prop)) return false;
7997
+ if (!prop || !Node15.isPropertyAssignment(prop)) return false;
7563
7998
  const value = prop.getInitializer();
7564
7999
  if (!value) return false;
7565
- return Node14.isArrayLiteralExpression(value);
8000
+ if (Node15.isArrayLiteralExpression(value)) return value.getElements().length > 0;
8001
+ if (Node15.isNullLiteral(value)) return false;
8002
+ if (Node15.isIdentifier(value) && value.getText() === "undefined") return false;
8003
+ return true;
7566
8004
  }
7567
8005
  function normalizePronunciations(raw) {
7568
8006
  if (!raw) return {
@@ -7588,6 +8026,8 @@ var init_voice_plugin = __esm({
7588
8026
  "src/compiler/plugins/voice.plugin.ts"() {
7589
8027
  "use strict";
7590
8028
  init_dist();
8029
+ init_primitive_rewrite();
8030
+ init_cross_file_specs();
7591
8031
  init_types();
7592
8032
  init_ast_helpers();
7593
8033
  init_common();
@@ -7618,6 +8058,7 @@ var init_voice_plugin = __esm({
7618
8058
  __name(parseDescriptor, "parseDescriptor");
7619
8059
  __name(stripKeys, "stripKeys");
7620
8060
  __name(leftmostIdentifierName, "leftmostIdentifierName");
8061
+ __name(resolveProviderAlias, "resolveProviderAlias");
7621
8062
  __name(extractRealtimeFromNewExpression, "extractRealtimeFromNewExpression");
7622
8063
  __name(extractFromNewExpression, "extractFromNewExpression");
7623
8064
  __name(unwrapType, "unwrapType");
@@ -7635,6 +8076,14 @@ var init_voice_plugin = __esm({
7635
8076
  legacyClassNames = [
7636
8077
  "LuaVoice"
7637
8078
  ];
8079
+ // Voice's cross-file rewrite uses a narrow whitelist because configs can
8080
+ // have nested provider SDK calls (new deepgram.STT, new elevenlabs.TTS)
8081
+ // that the top-level rewrite (in generateEntryPoint) handles separately.
8082
+ crossFileRewrite = {
8083
+ fields: [
8084
+ "name"
8085
+ ]
8086
+ };
7638
8087
  extractFromConfig(config, exportName, sourcePath, position, pattern) {
7639
8088
  const common = this.extractCommonFields(config, exportName, sourcePath, position);
7640
8089
  if (!common) return null;
@@ -7761,8 +8210,8 @@ var init_voice_plugin = __esm({
7761
8210
  }, "isMissingTopLevelRequired");
7762
8211
  for (const issue of parsed.error.issues) {
7763
8212
  if (isMissingTopLevelRequired(issue)) continue;
7764
- const path18 = issue.path.length > 0 ? issue.path.join(".") : "<root>";
7765
- errors.push(validationError(`Voice config invalid at \`${path18}\`: ${issue.message}`, {
8213
+ const path19 = issue.path.length > 0 ? issue.path.join(".") : "<root>";
8214
+ errors.push(validationError(`Voice config invalid at \`${path19}\`: ${issue.message}`, {
7766
8215
  line
7767
8216
  }));
7768
8217
  }
@@ -7775,43 +8224,45 @@ var init_voice_plugin = __esm({
7775
8224
  };
7776
8225
  }
7777
8226
  /**
7778
- * Entry-point emitter for the bundled JS path. Imports the dev's
7779
- * LuaVoice instance and re-exports its hooks + voice-only tools in
7780
- * the flat shape the worker sandbox expects (sandbox.ts:extractExports).
8227
+ * Entry-point emitter for hook-bearing voices.
8228
+ *
8229
+ * Architecturally, the bundle must NOT re-evaluate `new LuaVoice({...})` in
8230
+ * the worker sandbox — that call's only purpose is compile-time metadata
8231
+ * extraction (provider/class/options for stt/tts/llm), and re-running it
8232
+ * with stripped SDK imports requires runtime stub injection (a Proxy that
8233
+ * covers only a fraction of the dev's possible introspection patterns).
8234
+ *
8235
+ * Instead, this method does a source-level AST rewrite:
8236
+ * 1. Parse the dev's source file
8237
+ * 2. Find the LuaVoice / defineVoice call site
8238
+ * 3. Splice the hook function-expression texts + tool entries directly
8239
+ * into a synthesized `{ __lua_primitive__, onEnter, ..., tools }`
8240
+ * object literal at the same module-scope position
8241
+ * 4. Strip the stripped lua-cli / lua-cli/voice imports
7781
8242
  *
7782
- * Tools land as a name-keyed map; sandbox.normalizeTools also accepts
7783
- * arrays, but pre-normalizing here avoids one walk per session start
7784
- * and keeps the bundle's wire shape obvious to anyone inspecting it.
8243
+ * Closure references (module-level helpers, consts, types) survive
8244
+ * because the splice happens at the same lexical position as the
8245
+ * original call site.
7785
8246
  *
7786
8247
  * Only invoked by the compiler when `buildArtifact` returns undefined
7787
- * (i.e. when the voice declares hooks or voice-only tools) — see
7788
- * the conditional in `buildArtifact` below.
8248
+ * (i.e. when the voice declares hooks or voice-only tools).
7789
8249
  */
7790
8250
  generateEntryPoint(metadata) {
7791
- const importStatement = metadata.isDefaultExport ? `import __voice__ from ${JSON.stringify(metadata.sourcePath)};` : `import { __lua_target__ as __voice__ } from ${JSON.stringify(metadata.sourcePath)};`;
7792
- return `// AUTO-GENERATED ENTRY POINT \u2014 Voice: ${metadata.name}
7793
- ${importStatement}
7794
-
7795
- const __luaTools = {};
7796
- if (__voice__ && Array.isArray(__voice__.tools)) {
7797
- for (const t of __voice__.tools) {
7798
- if (t && typeof t === 'object' && typeof t.name === 'string') {
7799
- __luaTools[t.name] = {
7800
- name: t.name,
7801
- description: t.description,
7802
- execute: t.execute,
7803
- condition: t.condition,
7804
- voice: t.voice,
7805
- };
7806
- }
7807
- }
7808
- }
7809
-
7810
- export const onEnter = __voice__ && __voice__.onEnter;
7811
- export const onUserTurnCompleted = __voice__ && __voice__.onUserTurnCompleted;
7812
- export const onExit = __voice__ && __voice__.onExit;
7813
- export const tools = __luaTools;
7814
- `;
8251
+ return rewritePrimitiveSource(metadata, {
8252
+ constructorNames: [
8253
+ "LuaVoice"
8254
+ ],
8255
+ defineFunctionName: this.defineFunction,
8256
+ buildLiteral: /* @__PURE__ */ __name((arg, ctx) => `{
8257
+ ${buildLuaPrimitiveMarker(ctx.metadata, "voice")},
8258
+ onEnter: ${getPropertyText(arg, "onEnter") ?? "undefined"},
8259
+ onUserTurnCompleted: ${getPropertyText(arg, "onUserTurnCompleted") ?? "undefined"},
8260
+ onExit: ${getPropertyText(arg, "onExit") ?? "undefined"},
8261
+ tools: ${getPropertyText(arg, "tools") ?? "[]"}
8262
+ }`, "buildLiteral"),
8263
+ crossFileSpecs: buildCrossFileSpecs(),
8264
+ sdkBaseClassNames: buildSdkBaseClassNames()
8265
+ });
7815
8266
  }
7816
8267
  /**
7817
8268
  * Emit a JSON metadata sidecar OR fall through to esbuild bundling.
@@ -7841,7 +8292,7 @@ export const tools = __luaTools;
7841
8292
  description: metadata.description,
7842
8293
  metadata: sanitized
7843
8294
  }, null, 2);
7844
- const originalSource = await fs5.readFile(metadata.sourcePath, "utf-8");
8295
+ const originalSource = await fs6.readFile(metadata.sourcePath, "utf-8");
7845
8296
  return {
7846
8297
  code,
7847
8298
  sourceMap: "",
@@ -7994,8 +8445,8 @@ var init_registry = __esm({
7994
8445
  });
7995
8446
 
7996
8447
  // src/compiler/bundler.ts
7997
- import fs6 from "fs/promises";
7998
- import path5 from "path";
8448
+ import fs7 from "fs/promises";
8449
+ import path6 from "path";
7999
8450
  import crypto4 from "crypto";
8000
8451
  import { build } from "esbuild";
8001
8452
  var Bundler;
@@ -8003,6 +8454,8 @@ var init_bundler = __esm({
8003
8454
  "src/compiler/bundler.ts"() {
8004
8455
  "use strict";
8005
8456
  init_types();
8457
+ init_primitive_rewrite();
8458
+ init_cross_file_specs();
8006
8459
  Bundler = class {
8007
8460
  static {
8008
8461
  __name(this, "Bundler");
@@ -8010,24 +8463,43 @@ var init_bundler = __esm({
8010
8463
  options;
8011
8464
  project;
8012
8465
  verbose;
8466
+ // Snapshotted at construction. Plugins registered after this Bundler
8467
+ // is instantiated (test fixtures, "lua-cli as a library" usage) won't appear.
8468
+ // Today the registry is module-load-time only, so this is fine.
8469
+ crossFileSpecs = buildCrossFileSpecs();
8470
+ sdkBaseClassNames = buildSdkBaseClassNames();
8013
8471
  constructor(options, project, verbose) {
8014
8472
  this.options = options;
8015
8473
  this.project = project;
8016
8474
  this.verbose = verbose;
8017
8475
  }
8018
8476
  /**
8019
- * Strip lua-cli and api-exports import statements from source code using TypeScript AST.
8020
- * This is more robust than regex and handles all edge cases (multiline, comments, etc.).
8477
+ * Strip lua-cli / lua-cli/skill / lua-cli/voice / api-exports imports from
8478
+ * a source file, then rewrite every SDK class constructor / define-function
8479
+ * call in the file to a bare object literal (`{ name, execute, ... }`).
8480
+ *
8481
+ * After this transform:
8482
+ * - SDK class identifiers (LuaTool, LuaJob, defineWebhook, ...) no longer
8483
+ * appear in the source. The call sites that used them are replaced with
8484
+ * inline object literals carrying the runtime fields. By construction,
8485
+ * the bundle has zero references to stripped SDK identifiers, so the
8486
+ * sandbox doesn't need any runtime stub for them.
8487
+ * - Platform-API identifiers (Data, User, AI, Agents, Jobs, Lua, env, ...)
8488
+ * remain as free identifiers in the dev's execute / hook bodies. esbuild
8489
+ * leaves them alone (they're not imported, not local), and at runtime
8490
+ * they resolve to the sandbox's injected globals.
8491
+ *
8492
+ * Replaces the prior `buildLuaCliStubBlock` runtime-stub approach (BAC-234).
8021
8493
  */
8022
8494
  stripLuaCliImportsAST(code, filename = "temp.ts") {
8023
8495
  try {
8024
8496
  const tempFile = this.project.createSourceFile(`__strip_${filename}`, code, {
8025
8497
  overwrite: true
8026
8498
  });
8027
- const imports = tempFile.getImportDeclarations();
8028
- imports.forEach((imp) => {
8029
- const moduleSpecifier = imp.getModuleSpecifierValue();
8030
- if (moduleSpecifier === "lua-cli" || moduleSpecifier === "lua-cli/skill" || moduleSpecifier.includes("api-exports")) {
8499
+ rewriteCrossFileCallsInSourceFile(tempFile, this.crossFileSpecs, this.sdkBaseClassNames);
8500
+ tempFile.getImportDeclarations().forEach((imp) => {
8501
+ const spec = imp.getModuleSpecifierValue();
8502
+ if (spec === "lua-cli" || spec === "lua-cli/skill" || spec === "lua-cli/voice" || spec.includes("api-exports")) {
8031
8503
  imp.remove();
8032
8504
  }
8033
8505
  });
@@ -8035,16 +8507,17 @@ var init_bundler = __esm({
8035
8507
  this.project.removeSourceFile(tempFile);
8036
8508
  return result;
8037
8509
  } catch (error) {
8038
- return code;
8510
+ const msg = error instanceof Error ? error.message : String(error);
8511
+ throw new Error(`[lua-cli] AST rewrite failed for ${filename}: ${msg}. Source: bare SDK identifiers would survive into the bundle and fail at runtime; aborting compile.`);
8039
8512
  }
8040
8513
  }
8041
8514
  /**
8042
8515
  * Bundle a primitive's entry point using esbuild.
8043
8516
  */
8044
8517
  async bundle(metadata, entryPointCode) {
8045
- const tempDir = path5.join(this.options.outDir, ".temp");
8046
- const outfile = path5.join(tempDir, `${metadata.kind}-${metadata.name}.bundle.js`);
8047
- const sourceDir = path5.dirname(metadata.sourcePath);
8518
+ const tempDir = path6.join(this.options.outDir, ".temp");
8519
+ const outfile = path6.join(tempDir, `${metadata.kind}-${metadata.name}.bundle.js`);
8520
+ const sourceDir = path6.dirname(metadata.sourcePath);
8048
8521
  if (this.options.debug) {
8049
8522
  this.verbose(` Source: ${metadata.sourcePath}`);
8050
8523
  this.verbose(` Output: ${outfile}`);
@@ -8074,15 +8547,15 @@ var init_bundler = __esm({
8074
8547
  ],
8075
8548
  logLevel: this.options.debug ? "debug" : "silent"
8076
8549
  });
8077
- const code = await fs6.readFile(outfile, "utf-8");
8550
+ const code = await fs7.readFile(outfile, "utf-8");
8078
8551
  let sourceMap = "";
8079
8552
  try {
8080
- sourceMap = await fs6.readFile(outfile + ".map", "utf-8");
8553
+ sourceMap = await fs7.readFile(outfile + ".map", "utf-8");
8081
8554
  } catch {
8082
8555
  }
8083
- const originalSource = await fs6.readFile(metadata.sourcePath, "utf-8");
8556
+ const originalSource = await fs7.readFile(metadata.sourcePath, "utf-8");
8084
8557
  const hash = crypto4.createHash("sha256").update(code).digest("hex").slice(0, 16);
8085
- const stats = await fs6.stat(outfile);
8558
+ const stats = await fs7.stat(outfile);
8086
8559
  return {
8087
8560
  code,
8088
8561
  sourceMap,
@@ -8099,14 +8572,14 @@ var init_bundler = __esm({
8099
8572
  const bundler = this;
8100
8573
  const sourcePath = metadata.sourcePath;
8101
8574
  const exportName = metadata.exportName;
8102
- const sourceDir = path5.dirname(sourcePath);
8575
+ const sourceDir = path6.dirname(sourcePath);
8103
8576
  return {
8104
8577
  name: "lua-virtual-source",
8105
8578
  setup(build2) {
8106
8579
  build2.onResolve({
8107
8580
  filter: /.*/
8108
8581
  }, (args2) => {
8109
- const resolved = args2.path.startsWith(".") ? path5.resolve(args2.resolveDir, args2.path) : args2.path;
8582
+ const resolved = args2.path.startsWith(".") ? path6.resolve(args2.resolveDir, args2.path) : args2.path;
8110
8583
  const normalizedResolved = resolved.replace(/\.(ts|tsx|js|jsx)$/, "");
8111
8584
  const normalizedSource = sourcePath.replace(/\.(ts|tsx|js|jsx)$/, "");
8112
8585
  if (normalizedResolved === normalizedSource) {
@@ -8122,8 +8595,8 @@ var init_bundler = __esm({
8122
8595
  namespace: "lua-virtual-source"
8123
8596
  }, async () => {
8124
8597
  const sourceFile = bundler.project.getSourceFile(sourcePath);
8125
- let content = sourceFile?.getText() ?? await fs6.readFile(sourcePath, "utf-8");
8126
- content = bundler.stripLuaCliImportsAST(content, path5.basename(sourcePath));
8598
+ let content = sourceFile?.getText() ?? await fs7.readFile(sourcePath, "utf-8");
8599
+ content = bundler.stripLuaCliImportsAST(content, path6.basename(sourcePath));
8127
8600
  const virtualContent = metadata.isDefaultExport ? content : `${content}
8128
8601
  export { ${exportName} as __lua_target__ };
8129
8602
  `;
@@ -8143,8 +8616,6 @@ export { ${exportName} as __lua_target__ };
8143
8616
  */
8144
8617
  createLuaCliShimPlugin() {
8145
8618
  const stripImports = this.stripLuaCliImportsAST.bind(this);
8146
- const debug = this.options.debug;
8147
- const verbose = this.verbose.bind(this);
8148
8619
  return {
8149
8620
  name: "lua-cli-import-stripper",
8150
8621
  setup(build2) {
@@ -8153,29 +8624,27 @@ export { ${exportName} as __lua_target__ };
8153
8624
  namespace: "file"
8154
8625
  }, async (args2) => {
8155
8626
  if (args2.path.includes("node_modules")) return null;
8156
- const contents = await fs6.readFile(args2.path, "utf-8");
8627
+ const contents = await fs7.readFile(args2.path, "utf-8");
8157
8628
  if (!contents.includes("lua-cli") && !contents.includes("api-exports")) {
8158
8629
  return null;
8159
8630
  }
8160
8631
  try {
8161
- const modifiedContents = stripImports(contents, path5.basename(args2.path));
8632
+ const modifiedContents = stripImports(contents, path6.basename(args2.path));
8162
8633
  if (modifiedContents !== contents) {
8163
8634
  return {
8164
8635
  contents: modifiedContents,
8165
8636
  loader: args2.path.endsWith(".ts") || args2.path.endsWith(".tsx") ? "ts" : "js",
8166
- resolveDir: path5.dirname(args2.path)
8637
+ resolveDir: path6.dirname(args2.path)
8167
8638
  };
8168
8639
  }
8169
8640
  return null;
8170
8641
  } catch (error) {
8171
- if (debug) {
8172
- verbose(` Warning: Failed to parse ${args2.path} with ts-morph, using fallback`);
8173
- }
8174
- return null;
8642
+ const msg = error instanceof Error ? error.message : String(error);
8643
+ throw new Error(`[lua-cli] AST rewrite failed for ${args2.path}: ${msg}. Aborting compile (silent fallback would corrupt the bundle).`);
8175
8644
  }
8176
8645
  });
8177
8646
  build2.onResolve({
8178
- filter: /^lua-cli(\/skill)?$/
8647
+ filter: /^lua-cli(\/(skill|voice))?$/
8179
8648
  }, () => ({
8180
8649
  path: "lua-cli-empty",
8181
8650
  namespace: "lua-cli-empty"
@@ -8201,9 +8670,9 @@ export { ${exportName} as __lua_target__ };
8201
8670
  });
8202
8671
 
8203
8672
  // src/compiler/agent-traverser.ts
8204
- import { Project, Node as Node15 } from "ts-morph";
8205
- import path6 from "path";
8206
- import fs7 from "fs";
8673
+ import { Project as Project2, Node as Node16 } from "ts-morph";
8674
+ import path7 from "path";
8675
+ import fs8 from "fs";
8207
8676
  var PRIMITIVE_TYPES, AgentTraverser;
8208
8677
  var init_agent_traverser = __esm({
8209
8678
  "src/compiler/agent-traverser.ts"() {
@@ -8275,8 +8744,8 @@ var init_agent_traverser = __esm({
8275
8744
  constructor(rootDir, debug = false) {
8276
8745
  this.rootDir = rootDir;
8277
8746
  this.debug = debug;
8278
- this.project = new Project({
8279
- tsConfigFilePath: path6.join(rootDir, "tsconfig.json"),
8747
+ this.project = new Project2({
8748
+ tsConfigFilePath: path7.join(rootDir, "tsconfig.json"),
8280
8749
  skipAddingFilesFromTsConfig: true
8281
8750
  });
8282
8751
  registerProjectRootDir(this.project, this.rootDir);
@@ -8360,15 +8829,15 @@ var init_agent_traverser = __esm({
8360
8829
  */
8361
8830
  detectAgent() {
8362
8831
  const priorityFiles = [
8363
- path6.join(this.rootDir, "index.ts"),
8364
- path6.join(this.rootDir, "src", "index.ts"),
8365
- path6.join(this.rootDir, "agent.ts"),
8366
- path6.join(this.rootDir, "src", "agent.ts"),
8367
- path6.join(this.rootDir, "main.ts"),
8368
- path6.join(this.rootDir, "src", "main.ts")
8832
+ path7.join(this.rootDir, "index.ts"),
8833
+ path7.join(this.rootDir, "src", "index.ts"),
8834
+ path7.join(this.rootDir, "agent.ts"),
8835
+ path7.join(this.rootDir, "src", "agent.ts"),
8836
+ path7.join(this.rootDir, "main.ts"),
8837
+ path7.join(this.rootDir, "src", "main.ts")
8369
8838
  ];
8370
8839
  for (const filePath of priorityFiles) {
8371
- if (fs7.existsSync(filePath)) {
8840
+ if (fs8.existsSync(filePath)) {
8372
8841
  try {
8373
8842
  const sourceFile = this.project.addSourceFileAtPath(filePath);
8374
8843
  const agent = this.detectAgentInFile(sourceFile);
@@ -8424,7 +8893,7 @@ var init_agent_traverser = __esm({
8424
8893
  */
8425
8894
  extractArrayRefs(config, propName) {
8426
8895
  const prop = config.getProperty(propName);
8427
- if (!prop || !Node15.isPropertyAssignment(prop)) return [];
8896
+ if (!prop || !Node16.isPropertyAssignment(prop)) return [];
8428
8897
  const value = prop.getInitializer();
8429
8898
  if (!value) return [];
8430
8899
  const ctx = {
@@ -8509,7 +8978,7 @@ var init_agent_traverser = __esm({
8509
8978
  for (const assign of file.getExportAssignments()) {
8510
8979
  if (assign.isExportEquals()) continue;
8511
8980
  const expr = assign.getExpression();
8512
- if (!Node15.isIdentifier(expr)) continue;
8981
+ if (!Node16.isIdentifier(expr)) continue;
8513
8982
  const targetName = expr.getText();
8514
8983
  defaultMatch = detected.find((d) => d.exportName === targetName);
8515
8984
  if (defaultMatch) break;
@@ -8520,7 +8989,7 @@ var init_agent_traverser = __esm({
8520
8989
  console.warn(formatSarifWarning({
8521
8990
  ruleId: "lua/missing-primitive-declaration",
8522
8991
  filePath: sourcePath,
8523
- message: `agent references ${kind} "${refName}" but ${path6.basename(sourcePath)} does not define a matching primitive`,
8992
+ message: `agent references ${kind} "${refName}" but ${path7.basename(sourcePath)} does not define a matching primitive`,
8524
8993
  hint: `export a named ${plugin.displayName.toLowerCase()} declaration as "${refName}", or adjust the import so it points to the file that does`
8525
8994
  }));
8526
8995
  return null;
@@ -8540,20 +9009,20 @@ var init_agent_traverser = __esm({
8540
9009
  });
8541
9010
 
8542
9011
  // src/compiler/utils/workspace.ts
8543
- import fs8 from "fs";
8544
- import path7 from "path";
9012
+ import fs9 from "fs";
9013
+ import path8 from "path";
8545
9014
  function findWorkspaceRoot(rootDir) {
8546
- const resolvedInput = path7.resolve(rootDir);
9015
+ const resolvedInput = path8.resolve(rootDir);
8547
9016
  const cached = workspaceRootCache.get(resolvedInput);
8548
9017
  if (cached !== void 0) return cached;
8549
9018
  let current = resolvedInput;
8550
- const { root } = path7.parse(current);
9019
+ const { root } = path8.parse(current);
8551
9020
  while (true) {
8552
9021
  if (isWorkspaceRoot(current)) {
8553
9022
  workspaceRootCache.set(resolvedInput, current);
8554
9023
  return current;
8555
9024
  }
8556
- const parent = path7.dirname(current);
9025
+ const parent = path8.dirname(current);
8557
9026
  if (parent === current || current === root) break;
8558
9027
  current = parent;
8559
9028
  }
@@ -8561,22 +9030,22 @@ function findWorkspaceRoot(rootDir) {
8561
9030
  return resolvedInput;
8562
9031
  }
8563
9032
  function isWorkspaceRoot(dir) {
8564
- if (fileExists(path7.join(dir, "pnpm-workspace.yaml"))) return true;
8565
- if (fileExists(path7.join(dir, "lerna.json"))) return true;
8566
- if (packageJsonHasWorkspaces(path7.join(dir, "package.json"))) return true;
8567
- if (dirExists(path7.join(dir, ".git"))) return true;
9033
+ if (fileExists(path8.join(dir, "pnpm-workspace.yaml"))) return true;
9034
+ if (fileExists(path8.join(dir, "lerna.json"))) return true;
9035
+ if (packageJsonHasWorkspaces(path8.join(dir, "package.json"))) return true;
9036
+ if (dirExists(path8.join(dir, ".git"))) return true;
8568
9037
  return false;
8569
9038
  }
8570
9039
  function fileExists(p) {
8571
9040
  try {
8572
- return fs8.statSync(p).isFile();
9041
+ return fs9.statSync(p).isFile();
8573
9042
  } catch {
8574
9043
  return false;
8575
9044
  }
8576
9045
  }
8577
9046
  function dirExists(p) {
8578
9047
  try {
8579
- return fs8.statSync(p).isDirectory();
9048
+ return fs9.statSync(p).isDirectory();
8580
9049
  } catch {
8581
9050
  return false;
8582
9051
  }
@@ -8584,7 +9053,7 @@ function dirExists(p) {
8584
9053
  function packageJsonHasWorkspaces(pkgPath) {
8585
9054
  if (!fileExists(pkgPath)) return false;
8586
9055
  try {
8587
- const raw = fs8.readFileSync(pkgPath, "utf-8");
9056
+ const raw = fs9.readFileSync(pkgPath, "utf-8");
8588
9057
  const json = JSON.parse(raw);
8589
9058
  if (!json.workspaces) return false;
8590
9059
  return Array.isArray(json.workspaces) || typeof json.workspaces === "object";
@@ -8606,9 +9075,9 @@ var init_workspace = __esm({
8606
9075
  });
8607
9076
 
8608
9077
  // src/compiler/compiler.ts
8609
- import fs9 from "fs/promises";
8610
- import path8 from "path";
8611
- import { Project as Project2 } from "ts-morph";
9078
+ import fs10 from "fs/promises";
9079
+ import path9 from "path";
9080
+ import { Project as Project3 } from "ts-morph";
8612
9081
  async function compile(options) {
8613
9082
  const compiler = new Compiler(options);
8614
9083
  return compiler.compile();
@@ -8659,8 +9128,8 @@ var init_compiler = __esm({
8659
9128
  runtimeValidation: true,
8660
9129
  ...options
8661
9130
  };
8662
- this.project = new Project2({
8663
- tsConfigFilePath: path8.join(options.rootDir, "tsconfig.json")
9131
+ this.project = new Project3({
9132
+ tsConfigFilePath: path9.join(options.rootDir, "tsconfig.json")
8664
9133
  });
8665
9134
  registerProjectRootDir(this.project, this.options.rootDir);
8666
9135
  this.bundler = new Bundler({
@@ -8737,10 +9206,10 @@ var init_compiler = __esm({
8737
9206
  return await this.createResult(false, [], errors, warnings, startTime);
8738
9207
  }
8739
9208
  this.verbose("\u{1F4E6} Bundling primitives...");
8740
- await fs9.mkdir(this.options.outDir, {
9209
+ await fs10.mkdir(this.options.outDir, {
8741
9210
  recursive: true
8742
9211
  });
8743
- await fs9.mkdir(path8.join(this.options.outDir, ".temp"), {
9212
+ await fs10.mkdir(path9.join(this.options.outDir, ".temp"), {
8744
9213
  recursive: true
8745
9214
  });
8746
9215
  const CONCURRENCY = 4;
@@ -8760,7 +9229,7 @@ var init_compiler = __esm({
8760
9229
  this.verbose("\u{1F4BE} Writing artifacts...");
8761
9230
  await this.writeArtifacts(compiledPrimitives);
8762
9231
  if (!this.options.debug) {
8763
- await fs9.rm(path8.join(this.options.outDir, ".temp"), {
9232
+ await fs10.rm(path9.join(this.options.outDir, ".temp"), {
8764
9233
  recursive: true,
8765
9234
  force: true
8766
9235
  });
@@ -8803,7 +9272,7 @@ var init_compiler = __esm({
8803
9272
  */
8804
9273
  getWorkspaceRoot() {
8805
9274
  if (this._workspaceRoot === void 0) {
8806
- this._workspaceRoot = findWorkspaceRoot(path8.resolve(this.options.rootDir));
9275
+ this._workspaceRoot = findWorkspaceRoot(path9.resolve(this.options.rootDir));
8807
9276
  }
8808
9277
  return this._workspaceRoot;
8809
9278
  }
@@ -8817,18 +9286,18 @@ var init_compiler = __esm({
8817
9286
  * - Files outside the detected workspace root (not our code)
8818
9287
  */
8819
9288
  collectExternalWorkspaceFiles(traverser) {
8820
- const rootDir = path8.resolve(this.options.rootDir);
9289
+ const rootDir = path9.resolve(this.options.rootDir);
8821
9290
  const workspaceRoot = this.getWorkspaceRoot();
8822
9291
  if (workspaceRoot === rootDir) return [];
8823
9292
  const loaded = traverser.getAllLoadedSourceFilePaths();
8824
9293
  const result = [];
8825
9294
  const seen = /* @__PURE__ */ new Set();
8826
9295
  for (const raw of loaded) {
8827
- const abs = path8.resolve(raw);
9296
+ const abs = path9.resolve(raw);
8828
9297
  if (seen.has(abs)) continue;
8829
9298
  seen.add(abs);
8830
9299
  if (isInside(abs, rootDir)) continue;
8831
- if (abs.includes(`${path8.sep}node_modules${path8.sep}`)) continue;
9300
+ if (abs.includes(`${path9.sep}node_modules${path9.sep}`)) continue;
8832
9301
  if (!isInside(abs, workspaceRoot)) continue;
8833
9302
  result.push(abs);
8834
9303
  }
@@ -8961,9 +9430,9 @@ var init_compiler = __esm({
8961
9430
  * @param primitives - All compiled primitives to write
8962
9431
  */
8963
9432
  async writeArtifacts(primitives) {
8964
- const artifactsDir = path8.join(this.options.outDir, "artifacts");
8965
- const sourcesDir = path8.join(this.options.outDir, "sources");
8966
- await fs9.mkdir(sourcesDir, {
9433
+ const artifactsDir = path9.join(this.options.outDir, "artifacts");
9434
+ const sourcesDir = path9.join(this.options.outDir, "sources");
9435
+ await fs10.mkdir(sourcesDir, {
8967
9436
  recursive: true
8968
9437
  });
8969
9438
  const writtenSources = /* @__PURE__ */ new Map();
@@ -8971,7 +9440,7 @@ var init_compiler = __esm({
8971
9440
  this.verbose(" \u{1F4C2} Collecting project files...");
8972
9441
  const projectFiles = await this.storeProjectFiles(sourcesDir, writtenSources);
8973
9442
  const manifest = await this.createManifest(primitives, projectFiles);
8974
- await fs9.writeFile(path8.join(this.options.outDir, "manifest.json"), JSON.stringify(manifest, null, 2));
9443
+ await fs10.writeFile(path9.join(this.options.outDir, "manifest.json"), JSON.stringify(manifest, null, 2));
8975
9444
  this.verbose(` \u{1F4E6} Total files stored: ${writtenSources.size} (deduplicated)`);
8976
9445
  this.verbose(` \u{1F4C1} Project files: ${projectFiles.length}`);
8977
9446
  if (this.options.verbose || this.options.debug) {
@@ -8983,25 +9452,25 @@ var init_compiler = __esm({
8983
9452
  */
8984
9453
  async writePrimitiveArtifacts(primitives, artifactsDir, sourcesDir, writtenSources) {
8985
9454
  for (const primitive of primitives) {
8986
- const primitiveDir = path8.join(artifactsDir, primitive.kind);
8987
- await fs9.mkdir(primitiveDir, {
9455
+ const primitiveDir = path9.join(artifactsDir, primitive.kind);
9456
+ await fs10.mkdir(primitiveDir, {
8988
9457
  recursive: true
8989
9458
  });
8990
9459
  const baseName = primitive.name;
8991
- await fs9.writeFile(path8.join(primitiveDir, `${baseName}.js`), primitive.artifact.code);
9460
+ await fs10.writeFile(path9.join(primitiveDir, `${baseName}.js`), primitive.artifact.code);
8992
9461
  if (primitive.artifact.sourceMap) {
8993
- await fs9.writeFile(path8.join(primitiveDir, `${baseName}.js.map`), primitive.artifact.sourceMap);
9462
+ await fs10.writeFile(path9.join(primitiveDir, `${baseName}.js.map`), primitive.artifact.sourceMap);
8994
9463
  }
8995
9464
  const sourceHash = hashContent(primitive.artifact.originalSource);
8996
9465
  if (!writtenSources.has(sourceHash)) {
8997
9466
  const ext = primitive.sourcePath.endsWith(".tsx") ? ".tsx" : ".ts";
8998
- await fs9.writeFile(path8.join(sourcesDir, `${sourceHash}${ext}`), primitive.artifact.originalSource);
8999
- const relativePath = path8.relative(this.options.rootDir, primitive.sourcePath);
9467
+ await fs10.writeFile(path9.join(sourcesDir, `${sourceHash}${ext}`), primitive.artifact.originalSource);
9468
+ const relativePath = path9.relative(this.options.rootDir, primitive.sourcePath);
9000
9469
  writtenSources.set(sourceHash, relativePath);
9001
9470
  }
9002
9471
  const sidecarPlugin = pluginRegistry.get(primitive.kind);
9003
9472
  const sidecarMetadata = sidecarPlugin?.sanitizeForPersistence ? sidecarPlugin.sanitizeForPersistence(primitive.metadata) : primitive.metadata;
9004
- await fs9.writeFile(path8.join(primitiveDir, `${baseName}.json`), JSON.stringify({
9473
+ await fs10.writeFile(path9.join(primitiveDir, `${baseName}.json`), JSON.stringify({
9005
9474
  kind: primitive.kind,
9006
9475
  name: primitive.name,
9007
9476
  description: primitive.description,
@@ -9023,12 +9492,12 @@ var init_compiler = __esm({
9023
9492
  const allFiles = await this.collectProjectFiles();
9024
9493
  const workspaceRoot = this.getWorkspaceRoot();
9025
9494
  for (const file of allFiles) {
9026
- const absPath = file.external ? path8.join(workspaceRoot, file.relativePath) : path8.join(this.options.rootDir, file.relativePath);
9027
- const content = await fs9.readFile(absPath, "utf-8");
9495
+ const absPath = file.external ? path9.join(workspaceRoot, file.relativePath) : path9.join(this.options.rootDir, file.relativePath);
9496
+ const content = await fs10.readFile(absPath, "utf-8");
9028
9497
  const hash = hashContent(content);
9029
9498
  if (!writtenSources.has(hash)) {
9030
- const ext = path8.extname(file.relativePath) || ".txt";
9031
- await fs9.writeFile(path8.join(sourcesDir, `${hash}${ext}`), content);
9499
+ const ext = path9.extname(file.relativePath) || ".txt";
9500
+ await fs10.writeFile(path9.join(sourcesDir, `${hash}${ext}`), content);
9032
9501
  writtenSources.set(hash, file.relativePath);
9033
9502
  }
9034
9503
  projectFiles.push({
@@ -9104,12 +9573,12 @@ var init_compiler = __esm({
9104
9573
  async collectProjectFiles() {
9105
9574
  const files = [];
9106
9575
  const scanDir = /* @__PURE__ */ __name(async (dir, relativeBase = "") => {
9107
- const entries = await fs9.readdir(dir, {
9576
+ const entries = await fs10.readdir(dir, {
9108
9577
  withFileTypes: true
9109
9578
  });
9110
9579
  for (const entry of entries) {
9111
9580
  const relativePath = relativeBase ? `${relativeBase}/${entry.name}` : entry.name;
9112
- const fullPath = path8.join(dir, entry.name);
9581
+ const fullPath = path9.join(dir, entry.name);
9113
9582
  if (entry.isDirectory()) {
9114
9583
  if (shouldSkipDirectory(entry.name)) continue;
9115
9584
  await scanDir(fullPath, relativePath);
@@ -9126,10 +9595,10 @@ var init_compiler = __esm({
9126
9595
  const workspaceRoot = this.getWorkspaceRoot();
9127
9596
  const externalSeen = /* @__PURE__ */ new Set();
9128
9597
  for (const abs of this.externalWorkspaceFiles) {
9129
- const rel = path8.relative(workspaceRoot, abs).split(path8.sep).join("/");
9598
+ const rel = path9.relative(workspaceRoot, abs).split(path9.sep).join("/");
9130
9599
  if (!rel || rel.startsWith("..")) continue;
9131
9600
  if (externalSeen.has(rel)) continue;
9132
- const name = path8.basename(abs);
9601
+ const name = path9.basename(abs);
9133
9602
  if (shouldSkipFile(rel) || shouldSkipFile(name)) continue;
9134
9603
  externalSeen.add(rel);
9135
9604
  files.push({
@@ -9230,13 +9699,13 @@ var init_compiler = __esm({
9230
9699
  });
9231
9700
 
9232
9701
  // src/compiler/utils/file-discovery.ts
9233
- import path9 from "path";
9234
- import fs10 from "fs";
9702
+ import path10 from "path";
9703
+ import fs11 from "fs";
9235
9704
  function findEntryPoint(projectDir) {
9236
9705
  const baseDir = projectDir || process.cwd();
9237
9706
  for (const relativePath of ENTRY_POINT_PRIORITY) {
9238
- const fullPath = path9.join(baseDir, relativePath);
9239
- if (fs10.existsSync(fullPath)) {
9707
+ const fullPath = path10.join(baseDir, relativePath);
9708
+ if (fs11.existsSync(fullPath)) {
9240
9709
  return fullPath;
9241
9710
  }
9242
9711
  }
@@ -9272,9 +9741,9 @@ var init_file_discovery = __esm({
9272
9741
  });
9273
9742
 
9274
9743
  // src/compiler/source-writer.ts
9275
- import { Project as Project3, Node as Node16 } from "ts-morph";
9276
- import fs11 from "fs";
9277
- import path10 from "path";
9744
+ import { Project as Project4, Node as Node17 } from "ts-morph";
9745
+ import fs12 from "fs";
9746
+ import path11 from "path";
9278
9747
  function resolveEntryPath(options) {
9279
9748
  let indexPath;
9280
9749
  try {
@@ -9286,14 +9755,14 @@ function resolveEntryPath(options) {
9286
9755
  indexPath = entryPoint;
9287
9756
  } else {
9288
9757
  const baseDir = options?.projectDir || process.cwd();
9289
- indexPath = path10.join(baseDir, "src", "index.ts");
9758
+ indexPath = path11.join(baseDir, "src", "index.ts");
9290
9759
  }
9291
9760
  }
9292
9761
  } catch {
9293
9762
  const baseDir = options?.projectDir || process.cwd();
9294
- indexPath = path10.join(baseDir, "src", "index.ts");
9763
+ indexPath = path11.join(baseDir, "src", "index.ts");
9295
9764
  }
9296
- if (!fs11.existsSync(indexPath)) {
9765
+ if (!fs12.existsSync(indexPath)) {
9297
9766
  console.warn(`Warning: Entry file not found at ${indexPath}`);
9298
9767
  return null;
9299
9768
  }
@@ -9315,20 +9784,20 @@ function updateAgentConfig(updates, options) {
9315
9784
  const indexPath = resolveEntryPath(options);
9316
9785
  if (!indexPath) return false;
9317
9786
  try {
9318
- const project = new Project3({
9787
+ const project = new Project4({
9319
9788
  skipAddingFilesFromTsConfig: true
9320
9789
  });
9321
9790
  const sourceFile = project.addSourceFileAtPath(indexPath);
9322
9791
  let updated = false;
9323
9792
  sourceFile.forEachDescendant((node) => {
9324
- if (Node16.isNewExpression(node) && node.getExpression().getText() === "LuaAgent") {
9793
+ if (Node17.isNewExpression(node) && node.getExpression().getText() === "LuaAgent") {
9325
9794
  const args2 = node.getArguments();
9326
- if (args2.length > 0 && Node16.isObjectLiteralExpression(args2[0])) {
9795
+ if (args2.length > 0 && Node17.isObjectLiteralExpression(args2[0])) {
9327
9796
  const configObj = args2[0];
9328
9797
  const replacedKeys = /* @__PURE__ */ new Set();
9329
9798
  let lastReplacedIndex = -1;
9330
9799
  configObj.getProperties().forEach((prop, index) => {
9331
- if (Node16.isPropertyAssignment(prop)) {
9800
+ if (Node17.isPropertyAssignment(prop)) {
9332
9801
  const propName = prop.getName();
9333
9802
  if (Object.prototype.hasOwnProperty.call(filteredUpdates, propName)) {
9334
9803
  const newValue = filteredUpdates[propName];
@@ -9384,21 +9853,21 @@ function removeAgentConfigProperty(propertyName, options) {
9384
9853
  const indexPath = resolveEntryPath(options);
9385
9854
  if (!indexPath) return false;
9386
9855
  try {
9387
- const project = new Project3({
9856
+ const project = new Project4({
9388
9857
  skipAddingFilesFromTsConfig: true
9389
9858
  });
9390
9859
  const sourceFile = project.addSourceFileAtPath(indexPath);
9391
9860
  let foundConstructor = false;
9392
9861
  let removed = false;
9393
9862
  sourceFile.forEachDescendant((node) => {
9394
- if (Node16.isNewExpression(node) && node.getExpression().getText() === "LuaAgent") {
9863
+ if (Node17.isNewExpression(node) && node.getExpression().getText() === "LuaAgent") {
9395
9864
  const args2 = node.getArguments();
9396
- if (args2.length > 0 && Node16.isObjectLiteralExpression(args2[0])) {
9865
+ if (args2.length > 0 && Node17.isObjectLiteralExpression(args2[0])) {
9397
9866
  foundConstructor = true;
9398
9867
  const configObj = args2[0];
9399
9868
  const props = configObj.getProperties();
9400
9869
  for (const prop of props) {
9401
- if (Node16.isPropertyAssignment(prop) && prop.getName() === propertyName) {
9870
+ if (Node17.isPropertyAssignment(prop) && prop.getName() === propertyName) {
9402
9871
  prop.remove();
9403
9872
  removed = true;
9404
9873
  break;
@@ -13283,6 +13752,164 @@ __name(promptAuthMethod, "promptAuthMethod");
13283
13752
 
13284
13753
  // src/commands/configure.ts
13285
13754
  init_analytics();
13755
+
13756
+ // src/utils/versioning-mode-cache.ts
13757
+ import * as fs3 from "fs";
13758
+ import * as path3 from "path";
13759
+
13760
+ // src/api/agent-version.api.service.ts
13761
+ init_http_client();
13762
+ var AgentVersionApi = class extends HttpClient {
13763
+ static {
13764
+ __name(this, "AgentVersionApi");
13765
+ }
13766
+ apiKey;
13767
+ agentId;
13768
+ constructor(baseUrl, apiKey, agentId) {
13769
+ super(baseUrl);
13770
+ this.apiKey = apiKey;
13771
+ this.agentId = agentId;
13772
+ }
13773
+ get basePath() {
13774
+ return `/developer/agents/${encodeURIComponent(this.agentId)}`;
13775
+ }
13776
+ get authHeader() {
13777
+ return {
13778
+ Authorization: `Bearer ${this.apiKey}`
13779
+ };
13780
+ }
13781
+ // ---------------------------------------------------------------------------
13782
+ // Versioning mode
13783
+ // ---------------------------------------------------------------------------
13784
+ async getVersioningMode() {
13785
+ return this.httpGet(`${this.basePath}/versioning/mode`, this.authHeader);
13786
+ }
13787
+ // ---------------------------------------------------------------------------
13788
+ // Version CRUD
13789
+ // ---------------------------------------------------------------------------
13790
+ async createVersion(body) {
13791
+ return this.httpPost(`${this.basePath}/versions`, body, this.authHeader);
13792
+ }
13793
+ async listVersions(query) {
13794
+ const params = new URLSearchParams();
13795
+ if (query?.all !== void 0) params.append("all", String(query.all));
13796
+ if (query?.limit !== void 0) params.append("limit", String(query.limit));
13797
+ if (query?.status !== void 0) params.append("status", query.status);
13798
+ const qs = params.toString();
13799
+ const url = qs ? `${this.basePath}/versions?${qs}` : `${this.basePath}/versions`;
13800
+ return this.httpGet(url, this.authHeader);
13801
+ }
13802
+ async getVersion(version) {
13803
+ return this.httpGet(`${this.basePath}/versions/${version}`, this.authHeader);
13804
+ }
13805
+ async deleteVersion(version) {
13806
+ return this.httpDelete(`${this.basePath}/versions/${version}`, this.authHeader);
13807
+ }
13808
+ // ---------------------------------------------------------------------------
13809
+ // Diff
13810
+ // ---------------------------------------------------------------------------
13811
+ async diffVersions(from, to) {
13812
+ const url = `${this.basePath}/versions/diff?from=${from}&to=${to}`;
13813
+ return this.httpGet(url, this.authHeader);
13814
+ }
13815
+ // ---------------------------------------------------------------------------
13816
+ // Promote
13817
+ // ---------------------------------------------------------------------------
13818
+ async promoteVersion(version) {
13819
+ return this.httpPost(`${this.basePath}/versions/${version}/promote`, {}, this.authHeader);
13820
+ }
13821
+ // ---------------------------------------------------------------------------
13822
+ // Patch commit hash — called by `lua version create` after a successful git
13823
+ // commit + tag to propagate the SHA to the backend AgentVersion record.
13824
+ // Failure is non-fatal; the version snapshot is valid whether or not this
13825
+ // PATCH lands.
13826
+ // ---------------------------------------------------------------------------
13827
+ async patchCommitHash(version, commitHash) {
13828
+ return this.httpPatch(`${this.basePath}/versions/${version}/commit-hash`, {
13829
+ commitHash
13830
+ }, this.authHeader);
13831
+ }
13832
+ };
13833
+
13834
+ // src/utils/versioning-mode-cache.ts
13835
+ init_constants();
13836
+ init_auth_error();
13837
+ init_constants();
13838
+ var CACHE_TTL_MS = 60 * 60 * 1e3;
13839
+ function readCacheFile() {
13840
+ try {
13841
+ const raw = fs3.readFileSync(CLI_CACHE_FILE, "utf-8");
13842
+ return JSON.parse(raw);
13843
+ } catch {
13844
+ return {};
13845
+ }
13846
+ }
13847
+ __name(readCacheFile, "readCacheFile");
13848
+ function writeCacheFile(data) {
13849
+ try {
13850
+ fs3.mkdirSync(path3.dirname(CLI_CACHE_FILE), {
13851
+ recursive: true
13852
+ });
13853
+ fs3.writeFileSync(CLI_CACHE_FILE, JSON.stringify(data, null, 2));
13854
+ } catch {
13855
+ }
13856
+ }
13857
+ __name(writeCacheFile, "writeCacheFile");
13858
+ function isFresh(entry) {
13859
+ const age = Date.now() - new Date(entry.cachedAt).getTime();
13860
+ return age >= 0 && age < CACHE_TTL_MS;
13861
+ }
13862
+ __name(isFresh, "isFresh");
13863
+ async function getVersioningModeCached(apiKey, agentId) {
13864
+ const file = readCacheFile();
13865
+ const existing = file.versioningMode?.[agentId];
13866
+ if (existing && isFresh(existing)) {
13867
+ return {
13868
+ enabled: existing.enabled
13869
+ };
13870
+ }
13871
+ try {
13872
+ const api = new AgentVersionApi(BASE_URLS.API, apiKey, agentId);
13873
+ const response = await api.getVersioningMode();
13874
+ if (!response.success || !response.data) {
13875
+ if (existing) return {
13876
+ enabled: existing.enabled
13877
+ };
13878
+ return {
13879
+ enabled: false
13880
+ };
13881
+ }
13882
+ const enabled = response.data.enabled === true;
13883
+ file.versioningMode = file.versioningMode ?? {};
13884
+ file.versioningMode[agentId] = {
13885
+ enabled,
13886
+ cachedAt: (/* @__PURE__ */ new Date()).toISOString()
13887
+ };
13888
+ writeCacheFile(file);
13889
+ return {
13890
+ enabled
13891
+ };
13892
+ } catch (err) {
13893
+ if (err instanceof AuthenticationError) throw err;
13894
+ if (err instanceof Error && /access denied|forbidden|403|401/i.test(err.message)) throw err;
13895
+ if (existing) return {
13896
+ enabled: existing.enabled
13897
+ };
13898
+ return {
13899
+ enabled: false
13900
+ };
13901
+ }
13902
+ }
13903
+ __name(getVersioningModeCached, "getVersioningModeCached");
13904
+ function invalidateVersioningModeCache() {
13905
+ const file = readCacheFile();
13906
+ if (!file.versioningMode) return;
13907
+ delete file.versioningMode;
13908
+ writeCacheFile(file);
13909
+ }
13910
+ __name(invalidateVersioningModeCache, "invalidateVersioningModeCache");
13911
+
13912
+ // src/commands/configure.ts
13286
13913
  async function configureCommand(options = {}) {
13287
13914
  return withErrorHandling(async () => {
13288
13915
  const { apiKey, email, otp } = options;
@@ -13292,11 +13919,13 @@ async function configureCommand(options = {}) {
13292
13919
  authMethod = "api-key";
13293
13920
  nonInteractive = true;
13294
13921
  await handleApiKeyAuthNonInteractive(apiKey);
13922
+ invalidateVersioningModeCache();
13295
13923
  } else if (email) {
13296
13924
  authMethod = "email";
13297
13925
  nonInteractive = true;
13298
13926
  if (otp) {
13299
13927
  await handleEmailOtpVerify(email, otp);
13928
+ invalidateVersioningModeCache();
13300
13929
  } else {
13301
13930
  await handleEmailOtpRequest(email);
13302
13931
  }
@@ -13305,8 +13934,10 @@ async function configureCommand(options = {}) {
13305
13934
  clearPromptLines(2);
13306
13935
  if (authMethod === "api-key") {
13307
13936
  await handleApiKeyAuth();
13937
+ invalidateVersioningModeCache();
13308
13938
  } else if (authMethod === "email") {
13309
13939
  await handleEmailAuth();
13940
+ invalidateVersioningModeCache();
13310
13941
  }
13311
13942
  }
13312
13943
  trackEvent("cli_auth_completed", {
@@ -13978,8 +14609,8 @@ init_compile_constants();
13978
14609
  init_artifact_loader();
13979
14610
  init_backup_api_service();
13980
14611
  init_constants();
13981
- import fs12 from "fs";
13982
- import path11 from "path";
14612
+ import fs13 from "fs";
14613
+ import path12 from "path";
13983
14614
  var BACKUP_CACHE_FILENAME = "backup-manifest.json";
13984
14615
  function calculateProjectHash(projectFiles) {
13985
14616
  return combineFileHashes(projectFiles);
@@ -13992,11 +14623,11 @@ function getCurrentProjectHash(projectPath = process.cwd()) {
13992
14623
  }
13993
14624
  __name(getCurrentProjectHash, "getCurrentProjectHash");
13994
14625
  function loadSourceByHash(hash, relativePath, projectPath = process.cwd()) {
13995
- const sourcesDir = path11.join(projectPath, COMPILE_DIRS.DIST_V2, "sources");
13996
- const ext = path11.extname(relativePath) || ".txt";
13997
- const sourcePath = path11.join(sourcesDir, `${hash}${ext}`);
13998
- if (fs12.existsSync(sourcePath)) {
13999
- return fs12.readFileSync(sourcePath, "utf-8");
14626
+ const sourcesDir = path12.join(projectPath, COMPILE_DIRS.DIST_V2, "sources");
14627
+ const ext = path12.extname(relativePath) || ".txt";
14628
+ const sourcePath = path12.join(sourcesDir, `${hash}${ext}`);
14629
+ if (fs13.existsSync(sourcePath)) {
14630
+ return fs13.readFileSync(sourcePath, "utf-8");
14000
14631
  }
14001
14632
  return null;
14002
14633
  }
@@ -14015,30 +14646,30 @@ function prepareFileRefs(projectPath = process.cwd()) {
14015
14646
  }
14016
14647
  __name(prepareFileRefs, "prepareFileRefs");
14017
14648
  function reconcileManifestWithDisk(projectPath = process.cwd()) {
14018
- const manifestPath = path11.join(projectPath, COMPILE_DIRS.DIST_V2, "manifest.json");
14019
- if (!fs12.existsSync(manifestPath)) return;
14649
+ const manifestPath = path12.join(projectPath, COMPILE_DIRS.DIST_V2, "manifest.json");
14650
+ if (!fs13.existsSync(manifestPath)) return;
14020
14651
  const manifest = loadManifest(projectPath);
14021
- const sourcesDir = path11.join(projectPath, COMPILE_DIRS.DIST_V2, "sources");
14022
- fs12.mkdirSync(sourcesDir, {
14652
+ const sourcesDir = path12.join(projectPath, COMPILE_DIRS.DIST_V2, "sources");
14653
+ fs13.mkdirSync(sourcesDir, {
14023
14654
  recursive: true
14024
14655
  });
14025
14656
  let changed = false;
14026
14657
  for (const file of manifest.projectFiles) {
14027
14658
  if (file.external) continue;
14028
- const absPath = path11.join(projectPath, file.relativePath);
14029
- if (!fs12.existsSync(absPath)) continue;
14659
+ const absPath = path12.join(projectPath, file.relativePath);
14660
+ if (!fs13.existsSync(absPath)) continue;
14030
14661
  let content;
14031
14662
  try {
14032
- content = fs12.readFileSync(absPath, "utf-8");
14663
+ content = fs13.readFileSync(absPath, "utf-8");
14033
14664
  } catch {
14034
14665
  continue;
14035
14666
  }
14036
14667
  const freshHash = hashContent(content);
14037
14668
  if (freshHash === file.hash) continue;
14038
- const ext = path11.extname(file.relativePath) || ".txt";
14039
- const sourceTargetPath = path11.join(sourcesDir, `${freshHash}${ext}`);
14669
+ const ext = path12.extname(file.relativePath) || ".txt";
14670
+ const sourceTargetPath = path12.join(sourcesDir, `${freshHash}${ext}`);
14040
14671
  try {
14041
- fs12.writeFileSync(sourceTargetPath, content, "utf-8");
14672
+ fs13.writeFileSync(sourceTargetPath, content, "utf-8");
14042
14673
  } catch {
14043
14674
  continue;
14044
14675
  }
@@ -14047,7 +14678,7 @@ function reconcileManifestWithDisk(projectPath = process.cwd()) {
14047
14678
  changed = true;
14048
14679
  }
14049
14680
  if (changed) {
14050
- fs12.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
14681
+ fs13.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
14051
14682
  }
14052
14683
  }
14053
14684
  __name(reconcileManifestWithDisk, "reconcileManifestWithDisk");
@@ -14120,7 +14751,7 @@ function checkRestoreConflicts(manifest, targetDir) {
14120
14751
  const existingFiles = [];
14121
14752
  for (const file of manifest.files) {
14122
14753
  const filePath = resolveBackupFileTarget2(file, targetDir);
14123
- if (fs12.existsSync(filePath)) {
14754
+ if (fs13.existsSync(filePath)) {
14124
14755
  existingFiles.push(file.relativePath);
14125
14756
  } else {
14126
14757
  newFiles.push(file.relativePath);
@@ -14146,16 +14777,16 @@ function createBackupTracking(projectHash) {
14146
14777
  }
14147
14778
  __name(createBackupTracking, "createBackupTracking");
14148
14779
  function getBackupCachePath(projectPath = process.cwd()) {
14149
- return path11.join(projectPath, ".lua", BACKUP_CACHE_FILENAME);
14780
+ return path12.join(projectPath, ".lua", BACKUP_CACHE_FILENAME);
14150
14781
  }
14151
14782
  __name(getBackupCachePath, "getBackupCachePath");
14152
14783
  function writeBackupManifestCache(cache, projectPath = process.cwd()) {
14153
14784
  const cachePath = getBackupCachePath(projectPath);
14154
14785
  try {
14155
- fs12.mkdirSync(path11.dirname(cachePath), {
14786
+ fs13.mkdirSync(path12.dirname(cachePath), {
14156
14787
  recursive: true
14157
14788
  });
14158
- fs12.writeFileSync(cachePath, JSON.stringify(cache, null, 2), "utf-8");
14789
+ fs13.writeFileSync(cachePath, JSON.stringify(cache, null, 2), "utf-8");
14159
14790
  } catch (error) {
14160
14791
  const message = error instanceof Error ? error.message : String(error);
14161
14792
  console.warn(`Could not write backup manifest cache: ${message}`);
@@ -14164,9 +14795,9 @@ function writeBackupManifestCache(cache, projectPath = process.cwd()) {
14164
14795
  __name(writeBackupManifestCache, "writeBackupManifestCache");
14165
14796
  function readBackupManifestCache(projectPath = process.cwd()) {
14166
14797
  const cachePath = getBackupCachePath(projectPath);
14167
- if (!fs12.existsSync(cachePath)) return null;
14798
+ if (!fs13.existsSync(cachePath)) return null;
14168
14799
  try {
14169
- const raw = fs12.readFileSync(cachePath, "utf-8");
14800
+ const raw = fs13.readFileSync(cachePath, "utf-8");
14170
14801
  const parsed = JSON.parse(raw);
14171
14802
  if (typeof parsed.lastHash !== "string" || typeof parsed.lastPushedAt !== "string" || !Array.isArray(parsed.files) || !parsed.files.every((f) => f && typeof f.relativePath === "string" && typeof f.hash === "string")) {
14172
14803
  return null;
@@ -14193,18 +14824,18 @@ function detectLocalConflictsForPull(incomingFiles, projectPath = process.cwd())
14193
14824
  f.hash
14194
14825
  ]));
14195
14826
  const conflicts = /* @__PURE__ */ new Set();
14196
- const projectRoot = path11.resolve(projectPath);
14827
+ const projectRoot = path12.resolve(projectPath);
14197
14828
  for (const incoming of incomingFiles) {
14198
14829
  if (COMPILE_MANAGED_FILES.has(incoming.relativePath)) continue;
14199
14830
  if (incoming.external) continue;
14200
- const absPath = path11.resolve(projectRoot, incoming.relativePath);
14201
- if (absPath !== projectRoot && !absPath.startsWith(projectRoot + path11.sep)) {
14831
+ const absPath = path12.resolve(projectRoot, incoming.relativePath);
14832
+ if (absPath !== projectRoot && !absPath.startsWith(projectRoot + path12.sep)) {
14202
14833
  continue;
14203
14834
  }
14204
- if (!fs12.existsSync(absPath)) continue;
14835
+ if (!fs13.existsSync(absPath)) continue;
14205
14836
  let diskHash;
14206
14837
  try {
14207
- const bytes = fs12.readFileSync(absPath, "utf-8");
14838
+ const bytes = fs13.readFileSync(absPath, "utf-8");
14208
14839
  diskHash = hashContent(bytes);
14209
14840
  } catch {
14210
14841
  conflicts.add(incoming.relativePath);
@@ -14231,7 +14862,7 @@ function detectLocalConflictsForPull(incomingFiles, projectPath = process.cwd())
14231
14862
  __name(detectLocalConflictsForPull, "detectLocalConflictsForPull");
14232
14863
  async function fetchBackupManifestForPull(apiKey, agentId) {
14233
14864
  try {
14234
- const backupApi = new BackupApi(BASE_URLS.API, apiKey, agentId);
14865
+ const backupApi = new backup_api_service_default(BASE_URLS.API, apiKey, agentId);
14235
14866
  const response = await backupApi.getBackupManifest();
14236
14867
  return {
14237
14868
  fetched: response.data ?? null
@@ -14262,7 +14893,7 @@ __name(decideBackupFreshness, "decideBackupFreshness");
14262
14893
  async function checkServerBackupNewerThanLocal(apiKey, agentId, projectPath = process.cwd()) {
14263
14894
  const local = readBackupManifestCache(projectPath);
14264
14895
  try {
14265
- const api = new BackupApi(BASE_URLS.API, apiKey, agentId);
14896
+ const api = new backup_api_service_default(BASE_URLS.API, apiKey, agentId);
14266
14897
  const resp = await api.getBackupMetadata();
14267
14898
  const metadata = resp.success && resp.data ? {
14268
14899
  projectHash: resp.data.projectHash
@@ -14275,7 +14906,7 @@ async function checkServerBackupNewerThanLocal(apiKey, agentId, projectPath = pr
14275
14906
  __name(checkServerBackupNewerThanLocal, "checkServerBackupNewerThanLocal");
14276
14907
  async function checkBackupAvailability(apiKey, agentId) {
14277
14908
  try {
14278
- const backupApi = new BackupApi(BASE_URLS.API, apiKey, agentId);
14909
+ const backupApi = new backup_api_service_default(BASE_URLS.API, apiKey, agentId);
14279
14910
  const metadata = await backupApi.getBackupMetadata();
14280
14911
  if (metadata.data && metadata.data.fileCount > 0) {
14281
14912
  return {
@@ -14297,7 +14928,7 @@ __name(checkBackupAvailability, "checkBackupAvailability");
14297
14928
  async function restoreFromBackupForSync(apiKey, agentId, expectedPrimitives = [], projectPath, preFetchedManifest) {
14298
14929
  const targetDir = projectPath || process.cwd();
14299
14930
  try {
14300
- const backupApi = new BackupApi(BASE_URLS.API, apiKey, agentId);
14931
+ const backupApi = new backup_api_service_default(BASE_URLS.API, apiKey, agentId);
14301
14932
  const manifest = preFetchedManifest ?? (await backupApi.getBackupManifest()).data ?? null;
14302
14933
  if (!manifest || !manifest.files?.length) {
14303
14934
  return {
@@ -14383,17 +15014,17 @@ function writeBackupFilesWithByteCompare(manifest, blobs, targetDir) {
14383
15014
  }
14384
15015
  restoredContents.set(makeRestoredContentsKey(file), content);
14385
15016
  const filePath = resolveBackupFileTarget2(file, targetDir);
14386
- if (fs12.existsSync(filePath)) {
14387
- const existing = fs12.readFileSync(filePath);
15017
+ if (fs13.existsSync(filePath)) {
15018
+ const existing = fs13.readFileSync(filePath);
14388
15019
  if (existing.equals(content)) {
14389
15020
  filesUnchanged++;
14390
15021
  continue;
14391
15022
  }
14392
15023
  }
14393
- fs12.mkdirSync(path11.dirname(filePath), {
15024
+ fs13.mkdirSync(path12.dirname(filePath), {
14394
15025
  recursive: true
14395
15026
  });
14396
- fs12.writeFileSync(filePath, content);
15027
+ fs13.writeFileSync(filePath, content);
14397
15028
  filesWritten++;
14398
15029
  }
14399
15030
  return {
@@ -16119,7 +16750,7 @@ async function checkAndRestoreBackup(apiKey, agentId, options) {
16119
16750
  if (!confirm) return false;
16120
16751
  }
16121
16752
  try {
16122
- const backupApi = new BackupApi(BASE_URLS.API, apiKey, agentId);
16753
+ const backupApi = new backup_api_service_default(BASE_URLS.API, apiKey, agentId);
16123
16754
  const urlsResponse = await backupApi.getBlobUrls(manifest.files.map((f) => f.hash));
16124
16755
  if (!urlsResponse.success || !urlsResponse.data) {
16125
16756
  writeError(`Failed to fetch download URLs: ${urlsResponse.error?.message ?? "unknown error"}`);
@@ -16173,6 +16804,7 @@ async function destroyCommand(options) {
16173
16804
  if (options?.force) {
16174
16805
  const deleted2 = deleteApiKey();
16175
16806
  if (deleted2) {
16807
+ invalidateVersioningModeCache();
16176
16808
  writeSuccess("\u2705 API key deleted successfully.");
16177
16809
  } else {
16178
16810
  writeProgress("\u274C Failed to delete API key.");
@@ -16198,6 +16830,7 @@ async function destroyCommand(options) {
16198
16830
  if (confirm) {
16199
16831
  deleted = deleteApiKey();
16200
16832
  if (deleted) {
16833
+ invalidateVersioningModeCache();
16201
16834
  writeSuccess("\u2705 API key deleted successfully.");
16202
16835
  } else {
16203
16836
  writeProgress("\u274C Failed to delete API key.");
@@ -16268,13 +16901,13 @@ __name(apiKeyCommand, "apiKeyCommand");
16268
16901
  init_cli();
16269
16902
  init_files();
16270
16903
  init_auth();
16271
- import path13 from "path";
16904
+ import path14 from "path";
16272
16905
 
16273
16906
  // src/commands/sync.ts
16274
16907
  init_dist();
16275
16908
  init_cli();
16276
- import fs13 from "fs";
16277
- import path12 from "path";
16909
+ import fs14 from "fs";
16910
+ import path13 from "path";
16278
16911
 
16279
16912
  // src/utils/prompt-handler.ts
16280
16913
  init_cli();
@@ -16485,8 +17118,8 @@ init_mcp_server_handler();
16485
17118
  async function syncCommand(options) {
16486
17119
  return withErrorHandling(async () => {
16487
17120
  const preCompileYaml = readYamlConfig();
16488
- const yamlPath = path12.resolve(process.cwd(), "lua.skill.yaml");
16489
- const preCompileYamlBytes = fs13.existsSync(yamlPath) ? fs13.readFileSync(yamlPath, "utf-8") : null;
17121
+ const yamlPath = path13.resolve(process.cwd(), "lua.skill.yaml");
17122
+ const preCompileYamlBytes = fs14.existsSync(yamlPath) ? fs14.readFileSync(yamlPath, "utf-8") : null;
16490
17123
  let syncCompletedSuccessfully = false;
16491
17124
  try {
16492
17125
  writeProgress("\u{1F504} Compiling to get latest local state...");
@@ -16547,7 +17180,7 @@ async function syncCommand(options) {
16547
17180
  } finally {
16548
17181
  if (!syncCompletedSuccessfully && preCompileYamlBytes !== null) {
16549
17182
  try {
16550
- fs13.writeFileSync(yamlPath, preCompileYamlBytes, "utf-8");
17183
+ fs14.writeFileSync(yamlPath, preCompileYamlBytes, "utf-8");
16551
17184
  } catch {
16552
17185
  }
16553
17186
  }
@@ -18104,7 +18737,7 @@ async function compileCommand(options) {
18104
18737
  }
18105
18738
  writeProgress("\u{1F528} Compiling...");
18106
18739
  const rootDir = process.cwd();
18107
- const outDir = path13.join(rootDir, "dist-v2");
18740
+ const outDir = path14.join(rootDir, "dist-v2");
18108
18741
  const result = await compile({
18109
18742
  rootDir,
18110
18743
  outDir,
@@ -18267,17 +18900,18 @@ init_cli();
18267
18900
  init_command_utils();
18268
18901
 
18269
18902
  // src/utils/sandbox.ts
18270
- import vm2 from "vm";
18271
- import path16 from "path";
18903
+ import vm3 from "vm";
18904
+ import path17 from "path";
18272
18905
 
18273
18906
  // ../shared-sandbox/dist/index.mjs
18274
18907
  import vm from "vm";
18275
18908
  import { createRequire } from "module";
18276
- import path14 from "path";
18909
+ import path15 from "path";
18277
18910
  import dns from "dns";
18278
18911
  import net from "net";
18279
18912
  import { promisify } from "util";
18280
18913
  import { Agent as UndiciAgent } from "undici";
18914
+ import vm2 from "vm";
18281
18915
  var __defProp4 = Object.defineProperty;
18282
18916
  var __name4 = /* @__PURE__ */ __name((target, value) => __defProp4(target, "name", { value, configurable: true }), "__name");
18283
18917
  function buildSandboxProcess(opts) {
@@ -18442,7 +19076,7 @@ var REQUIRE_BLOCKLIST = /* @__PURE__ */ new Set([
18442
19076
  var SANDBOX_FAKE_DIRNAME = "/";
18443
19077
  var SANDBOX_FAKE_FILENAME = "/index.ts";
18444
19078
  function buildSandboxRequire(opts) {
18445
- const realRequire = createRequire(path14.join(opts.cwd, "package.json"));
19079
+ const realRequire = createRequire(path15.join(opts.cwd, "package.json"));
18446
19080
  const wrapped = /* @__PURE__ */ __name4((id) => {
18447
19081
  if (REQUIRE_BLOCKLIST.has(id)) {
18448
19082
  const evt = {
@@ -19088,16 +19722,38 @@ function applyGlobalSelfReference(context) {
19088
19722
  }
19089
19723
  __name(applyGlobalSelfReference, "applyGlobalSelfReference");
19090
19724
  __name4(applyGlobalSelfReference, "applyGlobalSelfReference");
19725
+ function resolveExtraAllowedCidrs() {
19726
+ const csv = process.env.LUA_ALLOWED_EGRESS_CIDRS;
19727
+ if (!csv) return [];
19728
+ return csv.split(",").map((s) => s.trim()).filter(Boolean);
19729
+ }
19730
+ __name(resolveExtraAllowedCidrs, "resolveExtraAllowedCidrs");
19731
+ __name4(resolveExtraAllowedCidrs, "resolveExtraAllowedCidrs");
19732
+ function wrapBundleAsCjsModule(source) {
19733
+ return "(function(module, exports, require) {\n" + source + "\n})(module, module.exports, require);";
19734
+ }
19735
+ __name(wrapBundleAsCjsModule, "wrapBundleAsCjsModule");
19736
+ __name4(wrapBundleAsCjsModule, "wrapBundleAsCjsModule");
19737
+ function runBundleInContext(context, source, options) {
19738
+ const wrapped = wrapBundleAsCjsModule(source);
19739
+ vm2.runInContext(wrapped, context, {
19740
+ filename: options.filename,
19741
+ timeout: options.timeoutMs ?? 1e3,
19742
+ lineOffset: -1
19743
+ });
19744
+ }
19745
+ __name(runBundleInContext, "runBundleInContext");
19746
+ __name4(runBundleInContext, "runBundleInContext");
19091
19747
 
19092
19748
  // src/utils/env-loader.utils.ts
19093
- import path15 from "path";
19094
- import fs14 from "fs";
19749
+ import path16 from "path";
19750
+ import fs15 from "fs";
19095
19751
  function parseEnvFile(filePath) {
19096
- if (!fs14.existsSync(filePath)) {
19752
+ if (!fs15.existsSync(filePath)) {
19097
19753
  return {};
19098
19754
  }
19099
19755
  try {
19100
- const content = fs14.readFileSync(filePath, "utf8");
19756
+ const content = fs15.readFileSync(filePath, "utf8");
19101
19757
  if (!content.trim()) return {};
19102
19758
  const envVars = {};
19103
19759
  content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).forEach((line) => {
@@ -19123,7 +19779,7 @@ async function loadEnvironmentVariables(context) {
19123
19779
  }
19124
19780
  __name(loadEnvironmentVariables, "loadEnvironmentVariables");
19125
19781
  function loadSandboxEnvVariables() {
19126
- const envFilePath = path15.join(process.cwd(), ".env");
19782
+ const envFilePath = path16.join(process.cwd(), ".env");
19127
19783
  const envMap = parseEnvFile(envFilePath);
19128
19784
  return Object.entries(envMap).map(([key, value]) => ({
19129
19785
  key,
@@ -19200,12 +19856,12 @@ function resolveEgressMode() {
19200
19856
  return "off";
19201
19857
  }
19202
19858
  __name(resolveEgressMode, "resolveEgressMode");
19203
- function resolveExtraAllowedCidrs() {
19859
+ function resolveExtraAllowedCidrs2() {
19204
19860
  const csv = process.env.LUA_ALLOWED_EGRESS_CIDRS;
19205
19861
  if (!csv) return [];
19206
19862
  return csv.split(",").map((s) => s.trim()).filter(Boolean);
19207
19863
  }
19208
- __name(resolveExtraAllowedCidrs, "resolveExtraAllowedCidrs");
19864
+ __name(resolveExtraAllowedCidrs2, "resolveExtraAllowedCidrs");
19209
19865
  function defaultSecurityEventLogger(evt) {
19210
19866
  console.warn("[sandbox]", JSON.stringify(evt));
19211
19867
  }
@@ -19218,7 +19874,7 @@ function loadEnvironmentVariables2() {
19218
19874
  envVars[key] = value;
19219
19875
  }
19220
19876
  }
19221
- const envFilePath = path16.join(process.cwd(), ".env");
19877
+ const envFilePath = path17.join(process.cwd(), ".env");
19222
19878
  const fileEnvVars = parseEnvFile(envFilePath);
19223
19879
  Object.assign(envVars, fileEnvVars);
19224
19880
  return envVars;
@@ -19353,7 +20009,7 @@ function createSandbox(options) {
19353
20009
  extraGlobals,
19354
20010
  requireMode: resolveRequireMode(),
19355
20011
  egressMode: resolveEgressMode(),
19356
- egressExtraAllowedCidrs: resolveExtraAllowedCidrs(),
20012
+ egressExtraAllowedCidrs: resolveExtraAllowedCidrs2(),
19357
20013
  onSecurityEvent: defaultSecurityEventLogger
19358
20014
  });
19359
20015
  }
@@ -19361,11 +20017,14 @@ __name(createSandbox, "createSandbox");
19361
20017
  async function executeTool(options) {
19362
20018
  const { toolCode, inputs } = options;
19363
20019
  const sandbox = createSandbox(options);
19364
- const context = vm2.createContext(sandbox);
20020
+ const context = vm3.createContext(sandbox);
19365
20021
  applyGlobalSelfReference(context);
19366
20022
  const isBundledArtifact = toolCode.includes("__lua_primitive__");
19367
20023
  if (isBundledArtifact) {
19368
- vm2.runInContext(toolCode, context);
20024
+ runBundleInContext(context, toolCode, {
20025
+ filename: "tool.bundle.js",
20026
+ timeoutMs: 18e4
20027
+ });
19369
20028
  const moduleExports = context.module.exports;
19370
20029
  const primitive = moduleExports.default?.primitive || moduleExports.primitive;
19371
20030
  if (!primitive || !primitive.execute) {
@@ -19393,7 +20052,7 @@ module.exports = async (input) => {
19393
20052
  }
19394
20053
  };
19395
20054
  `;
19396
- vm2.runInContext(commonJsWrapper, context);
20055
+ vm3.runInContext(commonJsWrapper, context);
19397
20056
  const executeFunction = context.module.exports;
19398
20057
  return await executeFunction(inputs);
19399
20058
  }
@@ -19402,7 +20061,7 @@ __name(executeTool, "executeTool");
19402
20061
  async function executeWebhook(options) {
19403
20062
  const { webhookCode, query, headers, body } = options;
19404
20063
  const sandbox = createSandbox(options);
19405
- const context = vm2.createContext(sandbox);
20064
+ const context = vm3.createContext(sandbox);
19406
20065
  applyGlobalSelfReference(context);
19407
20066
  const event = {
19408
20067
  query: query ?? {},
@@ -19412,7 +20071,10 @@ async function executeWebhook(options) {
19412
20071
  };
19413
20072
  const isBundledArtifact = webhookCode.includes("__lua_primitive__");
19414
20073
  if (isBundledArtifact) {
19415
- vm2.runInContext(webhookCode, context);
20074
+ runBundleInContext(context, webhookCode, {
20075
+ filename: "webhook.bundle.js",
20076
+ timeoutMs: 18e4
20077
+ });
19416
20078
  const moduleExports = context.module.exports;
19417
20079
  const primitive = moduleExports.default?.primitive || moduleExports.primitive;
19418
20080
  if (!primitive || !primitive.execute) {
@@ -19440,7 +20102,7 @@ module.exports = async (event) => {
19440
20102
  }
19441
20103
  };
19442
20104
  `;
19443
- vm2.runInContext(commonJsWrapper, context);
20105
+ vm3.runInContext(commonJsWrapper, context);
19444
20106
  const executeFunction = context.module.exports;
19445
20107
  return await executeFunction(event);
19446
20108
  }
@@ -19449,7 +20111,7 @@ __name(executeWebhook, "executeWebhook");
19449
20111
  async function executeJob(options) {
19450
20112
  const { jobCode, jobData, apiKey, agentId } = options;
19451
20113
  const sandbox = createSandbox(options);
19452
- const context = vm2.createContext(sandbox);
20114
+ const context = vm3.createContext(sandbox);
19453
20115
  applyGlobalSelfReference(context);
19454
20116
  let jobInstance = void 0;
19455
20117
  if (jobData) {
@@ -19475,7 +20137,10 @@ async function executeJob(options) {
19475
20137
  }
19476
20138
  const isBundledArtifact = jobCode.includes("__lua_primitive__");
19477
20139
  if (isBundledArtifact) {
19478
- vm2.runInContext(jobCode, context);
20140
+ runBundleInContext(context, jobCode, {
20141
+ filename: "job.bundle.js",
20142
+ timeoutMs: 18e4
20143
+ });
19479
20144
  const moduleExports = context.module.exports;
19480
20145
  const primitive = moduleExports.default?.primitive || moduleExports.primitive;
19481
20146
  if (!primitive || !primitive.execute) {
@@ -19503,7 +20168,7 @@ module.exports = async (job) => {
19503
20168
  }
19504
20169
  };
19505
20170
  `;
19506
- vm2.runInContext(commonJsWrapper, context);
20171
+ vm3.runInContext(commonJsWrapper, context);
19507
20172
  const executeFunction = context.module.exports;
19508
20173
  return await executeFunction(jobInstance);
19509
20174
  }
@@ -19515,11 +20180,14 @@ async function executePreProcessor(options) {
19515
20180
  const UserDataApi2 = (await Promise.resolve().then(() => (init_user_data_api_service(), user_data_api_service_exports))).default;
19516
20181
  const userService = new UserDataApi2(BASE_URLS.API, apiKey, agentId);
19517
20182
  const userInstance = await userService.get();
19518
- const context = vm2.createContext(sandbox);
20183
+ const context = vm3.createContext(sandbox);
19519
20184
  applyGlobalSelfReference(context);
19520
20185
  const isBundledArtifact = processorCode.includes("__lua_primitive__");
19521
20186
  if (isBundledArtifact) {
19522
- vm2.runInContext(processorCode, context);
20187
+ runBundleInContext(context, processorCode, {
20188
+ filename: "preprocessor.bundle.js",
20189
+ timeoutMs: 18e4
20190
+ });
19523
20191
  const moduleExports = context.module.exports;
19524
20192
  const primitive = moduleExports.default?.primitive || moduleExports.primitive;
19525
20193
  if (!primitive || !primitive.execute) {
@@ -19547,7 +20215,7 @@ module.exports = async (input) => {
19547
20215
  }
19548
20216
  };
19549
20217
  `;
19550
- vm2.runInContext(commonJsWrapper, context);
20218
+ vm3.runInContext(commonJsWrapper, context);
19551
20219
  const executeFunction = context.module.exports;
19552
20220
  return await executeFunction({
19553
20221
  user: userInstance,
@@ -19563,11 +20231,14 @@ async function executePostProcessor(options) {
19563
20231
  const UserDataApi2 = (await Promise.resolve().then(() => (init_user_data_api_service(), user_data_api_service_exports))).default;
19564
20232
  const userService = new UserDataApi2(BASE_URLS.API, apiKey, agentId);
19565
20233
  const userInstance = await userService.get();
19566
- const context = vm2.createContext(sandbox);
20234
+ const context = vm3.createContext(sandbox);
19567
20235
  applyGlobalSelfReference(context);
19568
20236
  const isBundledArtifact = processorCode.includes("__lua_primitive__");
19569
20237
  if (isBundledArtifact) {
19570
- vm2.runInContext(processorCode, context);
20238
+ runBundleInContext(context, processorCode, {
20239
+ filename: "postprocessor.bundle.js",
20240
+ timeoutMs: 18e4
20241
+ });
19571
20242
  const moduleExports = context.module.exports;
19572
20243
  const primitive = moduleExports.default?.primitive || moduleExports.primitive;
19573
20244
  if (!primitive || !primitive.execute) {
@@ -19592,7 +20263,7 @@ module.exports = async (input) => {
19592
20263
  }
19593
20264
  };
19594
20265
  `;
19595
- vm2.runInContext(commonJsWrapper, context);
20266
+ vm3.runInContext(commonJsWrapper, context);
19596
20267
  const executeFunction = context.module.exports;
19597
20268
  return await executeFunction({
19598
20269
  user: userInstance,
@@ -21089,6 +21760,7 @@ init_dist();
21089
21760
  init_files();
21090
21761
  init_cli();
21091
21762
  init_command_utils();
21763
+ init_auth();
21092
21764
  init_semver();
21093
21765
  init_auth_error();
21094
21766
  init_constants();
@@ -21155,7 +21827,7 @@ async function runBackupPush(opts) {
21155
21827
  projectHash: manifestProjectHash
21156
21828
  };
21157
21829
  }
21158
- const backupApi = new BackupApi(BASE_URLS.API, opts.apiKey, opts.agentId);
21830
+ const backupApi = new backup_api_service_default(BASE_URLS.API, opts.apiKey, opts.agentId);
21159
21831
  const allHashes = opts.fresh ? [
21160
21832
  ...new Set(fileRefs.map((f) => f.hash))
21161
21833
  ] : getAllFileHashes(projectPath);
@@ -21216,8 +21888,8 @@ async function runBackupPush(opts) {
21216
21888
  }
21217
21889
  __name(runBackupPush, "runBackupPush");
21218
21890
  async function uploadBlobsFromDisk(uploadUrls, files, projectPath, concurrency = 10) {
21219
- const fs16 = await import("fs");
21220
- const path18 = await import("path");
21891
+ const fs17 = await import("fs");
21892
+ const path19 = await import("path");
21221
21893
  const zlib2 = await import("zlib");
21222
21894
  const hashToPath = /* @__PURE__ */ new Map();
21223
21895
  for (const f of files) {
@@ -21231,8 +21903,8 @@ async function uploadBlobsFromDisk(uploadUrls, files, projectPath, concurrency =
21231
21903
  if (!rel) {
21232
21904
  throw new Error(`No file found for hash: ${hash}`);
21233
21905
  }
21234
- const abs = path18.join(projectPath, rel);
21235
- const content = fs16.readFileSync(abs);
21906
+ const abs = path19.join(projectPath, rel);
21907
+ const content = fs17.readFileSync(abs);
21236
21908
  const compressed = zlib2.gzipSync(content);
21237
21909
  const response = await fetch(uploadUrls[hash], {
21238
21910
  method: "PUT",
@@ -21323,6 +21995,262 @@ init_analytics();
21323
21995
  init_dist2();
21324
21996
  init_skills_api_service();
21325
21997
 
21998
+ // src/utils/try-git-commit.ts
21999
+ init_cli();
22000
+ init_files();
22001
+
22002
+ // src/utils/git.ts
22003
+ import { spawn } from "child_process";
22004
+ var DEFAULT_TIMEOUT_MS = 1e4;
22005
+ async function runGit(args2, opts = {}) {
22006
+ return new Promise((resolve6, reject) => {
22007
+ const child = spawn("git", args2, {
22008
+ cwd: opts.cwd,
22009
+ shell: false,
22010
+ stdio: [
22011
+ "ignore",
22012
+ "pipe",
22013
+ "pipe"
22014
+ ]
22015
+ });
22016
+ let stdout = "";
22017
+ let stderr = "";
22018
+ let settled = false;
22019
+ const timer = setTimeout(() => {
22020
+ settled = true;
22021
+ child.kill("SIGKILL");
22022
+ reject(new Error(`git ${args2.join(" ")} timed out after ${opts.timeout ?? DEFAULT_TIMEOUT_MS}ms`));
22023
+ }, opts.timeout ?? DEFAULT_TIMEOUT_MS);
22024
+ child.stdout?.on("data", (chunk) => {
22025
+ stdout += chunk.toString();
22026
+ });
22027
+ child.stdout?.on("error", (err) => {
22028
+ if (settled) return;
22029
+ settled = true;
22030
+ clearTimeout(timer);
22031
+ reject(err);
22032
+ });
22033
+ child.stderr?.on("data", (chunk) => {
22034
+ stderr += chunk.toString();
22035
+ });
22036
+ child.stderr?.on("error", () => {
22037
+ });
22038
+ child.on("error", (err) => {
22039
+ if (settled) return;
22040
+ settled = true;
22041
+ clearTimeout(timer);
22042
+ reject(err);
22043
+ });
22044
+ child.on("close", (code) => {
22045
+ if (settled) return;
22046
+ settled = true;
22047
+ clearTimeout(timer);
22048
+ resolve6({
22049
+ stdout,
22050
+ stderr,
22051
+ code: code ?? 0
22052
+ });
22053
+ });
22054
+ });
22055
+ }
22056
+ __name(runGit, "runGit");
22057
+ function classifyGitError(err, stderr = "") {
22058
+ if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") {
22059
+ return "no-git";
22060
+ }
22061
+ const s = stderr.toLowerCase();
22062
+ if (s.includes("not a git repository")) return "no-repo";
22063
+ if (s.includes("head detached") || s.includes("does not point to a branch") || s.includes("src refspec head does not match")) return "detached-head";
22064
+ if (s.includes("pre-commit hook") || s.includes("hook failed")) return "hook-failed";
22065
+ if (s.includes("already exists")) return "tag-exists";
22066
+ if (s.includes("nothing to commit")) return "nothing-to-commit";
22067
+ if (s.includes("authentication failed") || s.includes("could not read username")) return "auth";
22068
+ return "unknown";
22069
+ }
22070
+ __name(classifyGitError, "classifyGitError");
22071
+ async function isGitAvailable() {
22072
+ try {
22073
+ const { code } = await runGit([
22074
+ "--version"
22075
+ ]);
22076
+ return code === 0;
22077
+ } catch {
22078
+ return false;
22079
+ }
22080
+ }
22081
+ __name(isGitAvailable, "isGitAvailable");
22082
+ async function isInsideGitRepo(cwd) {
22083
+ try {
22084
+ const { code } = await runGit([
22085
+ "rev-parse",
22086
+ "--is-inside-work-tree"
22087
+ ], {
22088
+ cwd
22089
+ });
22090
+ return code === 0;
22091
+ } catch {
22092
+ return false;
22093
+ }
22094
+ }
22095
+ __name(isInsideGitRepo, "isInsideGitRepo");
22096
+ async function gitUserConfigured(cwd) {
22097
+ const readOne = /* @__PURE__ */ __name(async (key) => {
22098
+ try {
22099
+ const { stdout, code } = await runGit([
22100
+ "config",
22101
+ "--get",
22102
+ key
22103
+ ], {
22104
+ cwd
22105
+ });
22106
+ if (code !== 0) return void 0;
22107
+ const trimmed = stdout.trim();
22108
+ return trimmed.length > 0 ? trimmed : void 0;
22109
+ } catch {
22110
+ return void 0;
22111
+ }
22112
+ }, "readOne");
22113
+ const [name, email] = await Promise.all([
22114
+ readOne("user.name"),
22115
+ readOne("user.email")
22116
+ ]);
22117
+ return {
22118
+ name,
22119
+ email
22120
+ };
22121
+ }
22122
+ __name(gitUserConfigured, "gitUserConfigured");
22123
+ async function getCommitSha(cwd) {
22124
+ try {
22125
+ const { stdout, code } = await runGit([
22126
+ "rev-parse",
22127
+ "HEAD"
22128
+ ], {
22129
+ cwd
22130
+ });
22131
+ if (code !== 0) return null;
22132
+ const sha = stdout.trim();
22133
+ return sha.length > 0 ? sha : null;
22134
+ } catch {
22135
+ return null;
22136
+ }
22137
+ }
22138
+ __name(getCommitSha, "getCommitSha");
22139
+
22140
+ // src/utils/try-git-commit.ts
22141
+ init_analytics();
22142
+ async function tryGitCommit(opts) {
22143
+ const config = readYamlConfig();
22144
+ if (!config?.git?.enabled) {
22145
+ return {
22146
+ committed: false
22147
+ };
22148
+ }
22149
+ const runOpts = {
22150
+ cwd: opts.cwd
22151
+ };
22152
+ if (!opts.allowEmpty) {
22153
+ try {
22154
+ const addResult = await runGit([
22155
+ "add",
22156
+ "-A"
22157
+ ], runOpts);
22158
+ if (addResult.code !== 0) {
22159
+ const reason = classifyGitError(null, addResult.stderr);
22160
+ reportFailure("add", reason);
22161
+ return {
22162
+ committed: false
22163
+ };
22164
+ }
22165
+ } catch (err) {
22166
+ const reason = classifyGitError(err);
22167
+ reportFailure("add", reason);
22168
+ return {
22169
+ committed: false
22170
+ };
22171
+ }
22172
+ }
22173
+ const commitArgs = [
22174
+ "commit"
22175
+ ];
22176
+ if (opts.allowEmpty) commitArgs.push("--allow-empty");
22177
+ commitArgs.push("-m", opts.message);
22178
+ try {
22179
+ const commitResult = await runGit(commitArgs, runOpts);
22180
+ if (commitResult.code !== 0) {
22181
+ const reason = classifyGitError(null, commitResult.stderr);
22182
+ if (reason !== "nothing-to-commit") {
22183
+ reportFailure("commit", reason);
22184
+ }
22185
+ return {
22186
+ committed: false
22187
+ };
22188
+ }
22189
+ } catch (err) {
22190
+ const reason = classifyGitError(err);
22191
+ if (reason !== "nothing-to-commit") {
22192
+ reportFailure("commit", reason);
22193
+ }
22194
+ return {
22195
+ committed: false
22196
+ };
22197
+ }
22198
+ const sha = await getCommitSha(opts.cwd) ?? void 0;
22199
+ let tagged;
22200
+ if (opts.tag) {
22201
+ try {
22202
+ const tagResult = await runGit([
22203
+ "tag",
22204
+ opts.tag
22205
+ ], runOpts);
22206
+ if (tagResult.code !== 0) {
22207
+ const reason = classifyGitError(null, tagResult.stderr);
22208
+ reportFailure("tag", reason);
22209
+ tagged = false;
22210
+ } else {
22211
+ tagged = true;
22212
+ }
22213
+ } catch (err) {
22214
+ const reason = classifyGitError(err);
22215
+ reportFailure("tag", reason);
22216
+ tagged = false;
22217
+ }
22218
+ }
22219
+ trackEvent("cli_git_sideeffect_succeeded", {
22220
+ action: opts.action,
22221
+ has_sha: Boolean(sha),
22222
+ has_tag: tagged === true
22223
+ });
22224
+ return {
22225
+ committed: true,
22226
+ sha,
22227
+ tagged
22228
+ };
22229
+ }
22230
+ __name(tryGitCommit, "tryGitCommit");
22231
+ function reportFailure(action, reason) {
22232
+ writeInfo(`\u26A0\uFE0F Git ${action} skipped: ${reason}`);
22233
+ trackEvent("cli_git_sideeffect_failed", {
22234
+ action,
22235
+ reason
22236
+ });
22237
+ }
22238
+ __name(reportFailure, "reportFailure");
22239
+
22240
+ // src/utils/git-messages.ts
22241
+ var GIT_MESSAGES = {
22242
+ pushStaged: "lua: push staged code",
22243
+ createVersion: /* @__PURE__ */ __name((version, message) => {
22244
+ const trimmed = message?.trim();
22245
+ return trimmed ? `lua: create version v${version}: ${trimmed}` : `lua: create version v${version}`;
22246
+ }, "createVersion"),
22247
+ promote: /* @__PURE__ */ __name((version) => `lua: promote v${version} to production`, "promote"),
22248
+ delete: /* @__PURE__ */ __name((version) => `lua: delete version v${version}`, "delete"),
22249
+ pullLatest: "lua: pull latest backup",
22250
+ pullVersion: /* @__PURE__ */ __name((version) => `lua: pull version v${version}`, "pullVersion")
22251
+ };
22252
+ var tagName = /* @__PURE__ */ __name((version) => `lua/v${version}`, "tagName");
22253
+
21326
22254
  // src/commands/push-helpers.ts
21327
22255
  function pickPushAllNextStepVariant(opts) {
21328
22256
  if (opts.pushedSomething && opts.failedCount === 0) return "success";
@@ -21637,6 +22565,34 @@ async function pushCommand(type, cmdObj) {
21637
22565
  allowAll: true
21638
22566
  });
21639
22567
  }
22568
+ const earlyApiKey = loadApiKey();
22569
+ const earlyConfig = readYamlConfig();
22570
+ const earlyAgentId = earlyConfig?.agent?.agentId;
22571
+ const versioning = earlyApiKey && earlyAgentId ? await getVersioningModeCached(earlyApiKey, earlyAgentId) : {
22572
+ enabled: false
22573
+ };
22574
+ const isStageAll = !type && versioning.enabled;
22575
+ const isGranular = Boolean(type && type !== "all" && versioning.enabled);
22576
+ const isAutoDeployNoOp = versioning.enabled && (type === "all" || !type) && Boolean(options.autoDeploy);
22577
+ if (isAutoDeployNoOp) {
22578
+ writeInfo("\u26A0\uFE0F --auto-deploy is ignored when agent versioning is on. Use `lua version promote <version>` after `lua version create`.");
22579
+ options.autoDeploy = false;
22580
+ }
22581
+ if (options.entityName && !type) {
22582
+ console.log("\nUsage:");
22583
+ console.log(" lua push skill --name mySkill --set-version 1.0.5 Push specific skill");
22584
+ console.log(" lua push webhook --name myWebhook --set-version 2.0.0 Push specific webhook");
22585
+ throw new Error("Type must be specified when using the --name option.");
22586
+ }
22587
+ if (isStageAll) {
22588
+ return await pushAllCommand({
22589
+ ...options,
22590
+ autoDeployNoopWarned: isAutoDeployNoOp
22591
+ });
22592
+ }
22593
+ if (isGranular) {
22594
+ writeInfo("\u26A0\uFE0F Granular push is deprecated when agent versioning is on. Use `lua push` (no args) to stage all changes, then `lua version create`.");
22595
+ }
21640
22596
  if (type === "all") {
21641
22597
  if (!options.force) {
21642
22598
  console.log("\nUsage:");
@@ -21644,13 +22600,10 @@ async function pushCommand(type, cmdObj) {
21644
22600
  console.log(" lua push all --force --auto-deploy Push and deploy all to production");
21645
22601
  throw new Error('The "all" type requires the --force flag');
21646
22602
  }
21647
- return await pushAllCommand(options);
21648
- }
21649
- if (options.entityName && !type) {
21650
- console.log("\nUsage:");
21651
- console.log(" lua push skill --name mySkill --set-version 1.0.5 Push specific skill");
21652
- console.log(" lua push webhook --name myWebhook --set-version 2.0.0 Push specific webhook");
21653
- throw new Error("Type must be specified when using the --name option.");
22603
+ return await pushAllCommand({
22604
+ ...options,
22605
+ autoDeployNoopWarned: isAutoDeployNoOp
22606
+ });
21654
22607
  }
21655
22608
  if (type) {
21656
22609
  selectedType = type;
@@ -21791,13 +22744,17 @@ Until the backup catches up, \`lua init\` for this agent will restore an older s
21791
22744
  bundles_total: versionedPushDeployResult?.bundleStats?.total ?? 0,
21792
22745
  bundles_uploaded: versionedPushDeployResult?.bundleStats?.uploaded ?? 0,
21793
22746
  bundles_existed: versionedPushDeployResult?.bundleStats?.alreadyExisted ?? 0,
21794
- // Phase 2 `--include-source` telemetry. Tracks adoption of the flag
21795
- // and whether the per-skill source attach actually lands cleanly —
21796
- // useful for the eventual "flip default to ON" decision in Slice 5.
22747
+ // `--include-source` telemetry. Tracks adoption of the flag and whether
22748
+ // the per-skill source attach actually lands cleanly — useful for the
22749
+ // eventual "flip default to ON" decision.
21797
22750
  include_source_requested: options.includeSource || false,
21798
22751
  include_source_attempted: trackedIncludeSource?.attempted ?? 0,
21799
22752
  include_source_attached: trackedIncludeSource?.attached ?? 0,
21800
- include_source_failed: trackedIncludeSource?.failed ?? 0
22753
+ include_source_failed: trackedIncludeSource?.failed ?? 0,
22754
+ // Agent-versioning telemetry. Driven by isStageAll / isGranular.
22755
+ versioning_mode: versioning.enabled ? isGranular ? "granular" : "all" : "flag-off",
22756
+ granular_deprecation_warned: isGranular,
22757
+ auto_deploy_noop_warned: isAutoDeployNoOp
21801
22758
  });
21802
22759
  const productionDeployedActually = versionedPushDeployResult?.productionDeploySucceeded === true || !!agentOutcome && !agentOutcome.cancelled && agentOutcome.productionDeployedWithAutoDeploy;
21803
22760
  const personaAutoDeployRanAndFailed = selectedType === "agent" && options.autoDeploy && !!agentOutcome && !agentOutcome.cancelled && "personaAutoDeployFailed" in agentOutcome && agentOutcome.personaAutoDeployFailed;
@@ -22090,6 +23047,7 @@ async function pushAllCommand(options) {
22090
23047
  }
22091
23048
  const apiKey = await requireAuthOrExit(false);
22092
23049
  const agentId = config.agent.agentId;
23050
+ const pushAllVersioning = await getVersioningModeCached(apiKey, agentId);
22093
23051
  let manifest;
22094
23052
  try {
22095
23053
  manifest = loadManifest();
@@ -22386,15 +23344,26 @@ Until the backup catches up, \`lua init\` for this agent will restore an older s
22386
23344
  bundles_total: bundleStatsAggregate.total,
22387
23345
  bundles_uploaded: bundleStatsAggregate.uploaded,
22388
23346
  bundles_existed: bundleStatsAggregate.existed,
22389
- // Phase 2 `--include-source` telemetry, parallel to the single-push
23347
+ // `--include-source` telemetry, parallel to the single-push
22390
23348
  // `cli_push_completed` shape. Without these the `lua push all` path —
22391
- // arguably the highest-volume one for multi-skill agents — would be
22392
- // invisible to adoption monitoring.
23349
+ // the highest-volume one for multi-skill agents — would be invisible
23350
+ // to adoption monitoring.
22393
23351
  include_source_requested: options.includeSource || false,
22394
23352
  include_source_attempted: includeSourceStats?.attempted ?? 0,
22395
23353
  include_source_attached: includeSourceStats?.attached ?? 0,
22396
- include_source_failed: includeSourceStats?.failed ?? 0
23354
+ include_source_failed: includeSourceStats?.failed ?? 0,
23355
+ // Agent-versioning telemetry.
23356
+ versioning_mode: pushAllVersioning.enabled ? "stage-all" : "flag-off",
23357
+ granular_deprecation_warned: false,
23358
+ auto_deploy_noop_warned: options.autoDeployNoopWarned || false
22397
23359
  });
23360
+ try {
23361
+ await tryGitCommit({
23362
+ message: GIT_MESSAGES.pushStaged,
23363
+ action: "push-staged"
23364
+ });
23365
+ } catch {
23366
+ }
22398
23367
  const pushedSomething = allResults.length > 0 || mcpPushedCount > 0 || !!personaPushResult || backupSuccess;
22399
23368
  const variant = pickPushAllNextStepVariant({
22400
23369
  pushedSomething,
@@ -22402,7 +23371,11 @@ Until the backup catches up, \`lua init\` for this agent will restore an older s
22402
23371
  });
22403
23372
  if (variant === "success") {
22404
23373
  console.log("");
22405
- if (options.autoDeploy) {
23374
+ if (pushAllVersioning.enabled) {
23375
+ if (!options.suppressVersionCreateHint) {
23376
+ writeInfo(`Run \`lua version create [-m "<message>"]\` to snapshot the staged state into a named version, then \`lua version promote <version>\` to deploy.`);
23377
+ }
23378
+ } else if (options.autoDeploy) {
22406
23379
  writeHintBlock({
22407
23380
  headline: "All deployed. Generate test traffic first, then inspect:",
22408
23381
  lines: [
@@ -22670,6 +23643,10 @@ async function deployCommand(type, cmdObj) {
22670
23643
  selectedType = answer.type;
22671
23644
  }
22672
23645
  const apiKey = await requireAuthOrExit();
23646
+ const versioning = await getVersioningModeCached(apiKey, agentId);
23647
+ if (versioning.enabled) {
23648
+ writeInfo("\u26A0\uFE0F `lua deploy` is deprecated when agent versioning is on. Use `lua version promote <version>` for instant, atomic promotion.");
23649
+ }
22673
23650
  let personaDeployed = false;
22674
23651
  let versionedOutcome = null;
22675
23652
  if (selectedType === "persona") {
@@ -22682,7 +23659,8 @@ async function deployCommand(type, cmdObj) {
22682
23659
  primitive_type: selectedType,
22683
23660
  force_mode: options.force || false,
22684
23661
  entity_selected_by_name: !!options.name,
22685
- version_selected_by_flag: !!options.version
23662
+ version_selected_by_flag: !!options.version,
23663
+ granular_deprecation_warned: versioning.enabled
22686
23664
  });
22687
23665
  const deployed = selectedType === "persona" ? personaDeployed : versionedOutcome?.deployed ?? false;
22688
23666
  const hintPrintedAlready = selectedType !== "persona" && !!versionedOutcome?.hintPrinted;
@@ -23176,11 +24154,11 @@ init_cli();
23176
24154
 
23177
24155
  // src/utils/sandbox-storage.ts
23178
24156
  init_constants();
23179
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6 } from "fs";
23180
- import { dirname as dirname5 } from "path";
24157
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7 } from "fs";
24158
+ import { dirname as dirname6 } from "path";
23181
24159
  function readStore() {
23182
24160
  try {
23183
- const raw = readFileSync8(SANDBOX_STORAGE_FILE, "utf8");
24161
+ const raw = readFileSync10(SANDBOX_STORAGE_FILE, "utf8");
23184
24162
  const parsed = JSON.parse(raw);
23185
24163
  return {
23186
24164
  skills: parsed.skills ?? {},
@@ -23200,10 +24178,10 @@ function readStore() {
23200
24178
  __name(readStore, "readStore");
23201
24179
  function writeStore(store) {
23202
24180
  try {
23203
- mkdirSync6(dirname5(SANDBOX_STORAGE_FILE), {
24181
+ mkdirSync7(dirname6(SANDBOX_STORAGE_FILE), {
23204
24182
  recursive: true
23205
24183
  });
23206
- writeFileSync6(SANDBOX_STORAGE_FILE, JSON.stringify(store, null, 2), "utf8");
24184
+ writeFileSync7(SANDBOX_STORAGE_FILE, JSON.stringify(store, null, 2), "utf8");
23207
24185
  } catch {
23208
24186
  }
23209
24187
  }
@@ -23707,7 +24685,15 @@ var MIME_TYPES = {
23707
24685
  ".json": "application/json",
23708
24686
  // Email
23709
24687
  ".eml": "message/rfc822",
23710
- ".msg": "application/vnd.ms-outlook"
24688
+ ".msg": "application/vnd.ms-outlook",
24689
+ // Audio. lua-core picks the path per provider: types in the provider's
24690
+ // native allowlist go straight to the model, the rest are transcribed by
24691
+ // Deepgram via FileConversionService.
24692
+ ".m4a": "audio/mp4",
24693
+ ".mp3": "audio/mpeg",
24694
+ ".wav": "audio/wav",
24695
+ ".ogg": "audio/ogg",
24696
+ ".opus": "audio/opus"
23711
24697
  };
23712
24698
  var IMAGE_MIME_TYPES = /* @__PURE__ */ new Set([
23713
24699
  "image/png",
@@ -24517,8 +25503,8 @@ init_cli();
24517
25503
  init_constants();
24518
25504
  init_command_utils();
24519
25505
  init_developer_api_service();
24520
- import fs15 from "fs";
24521
- import path17 from "path";
25506
+ import fs16 from "fs";
25507
+ import path18 from "path";
24522
25508
  import inquirer10 from "inquirer";
24523
25509
  init_analytics();
24524
25510
  function resolveEnvironment(env, hasNonInteractiveFlags) {
@@ -24877,10 +25863,10 @@ function variablesToEnvContent(variables) {
24877
25863
  }
24878
25864
  __name(variablesToEnvContent, "variablesToEnvContent");
24879
25865
  function saveSandboxEnvVariables(variables) {
24880
- const envFilePath = path17.join(process.cwd(), ".env");
25866
+ const envFilePath = path18.join(process.cwd(), ".env");
24881
25867
  try {
24882
25868
  const content = variablesToEnvContent(variables);
24883
- fs15.writeFileSync(envFilePath, content, "utf8");
25869
+ fs16.writeFileSync(envFilePath, content, "utf8");
24884
25870
  return true;
24885
25871
  } catch (error) {
24886
25872
  console.error("\u274C Error saving .env file:", error);
@@ -29372,7 +30358,7 @@ init_auth();
29372
30358
  init_auth_api_service();
29373
30359
  init_files();
29374
30360
  init_artifact_loader();
29375
- import { existsSync as existsSync7, readFileSync as readFileSync9 } from "fs";
30361
+ import { existsSync as existsSync7, readFileSync as readFileSync11 } from "fs";
29376
30362
  import { join as join6 } from "path";
29377
30363
  import { performance } from "perf_hooks";
29378
30364
  import * as os from "os";
@@ -29723,7 +30709,7 @@ function gatherTelemetry() {
29723
30709
  } else {
29724
30710
  try {
29725
30711
  if (existsSync7(TELEMETRY_FILE)) {
29726
- const raw = readFileSync9(TELEMETRY_FILE, "utf8");
30712
+ const raw = readFileSync11(TELEMETRY_FILE, "utf8");
29727
30713
  const cfg = JSON.parse(raw);
29728
30714
  if (typeof cfg.enabled === "boolean") enabled = cfg.enabled;
29729
30715
  }
@@ -38904,7 +39890,7 @@ __name(telemetryCommand, "telemetryCommand");
38904
39890
  init_cli();
38905
39891
  init_command_utils();
38906
39892
  init_analytics();
38907
- import { writeFileSync as writeFileSync7, existsSync as existsSync8, unlinkSync as unlinkSync2 } from "fs";
39893
+ import { writeFileSync as writeFileSync8, existsSync as existsSync8, unlinkSync as unlinkSync2 } from "fs";
38908
39894
  import { resolve as resolve4, join as join7 } from "path";
38909
39895
  init_artifact_loader();
38910
39896
  init_types();
@@ -39065,7 +40051,7 @@ async function governanceCommand(action) {
39065
40051
  }
39066
40052
  }
39067
40053
  const content = generateFile(setup);
39068
- writeFileSync7(filePath, content, "utf-8");
40054
+ writeFileSync8(filePath, content, "utf-8");
39069
40055
  const relativePath = filePath.replace(process.cwd() + "/", "");
39070
40056
  writeSuccess(`Created ${relativePath}`);
39071
40057
  console.log("");
@@ -39329,14 +40315,14 @@ init_analytics();
39329
40315
  init_command_utils();
39330
40316
  init_artifact_loader();
39331
40317
  init_types();
39332
- import { spawn as spawn2 } from "child_process";
40318
+ import { spawn as spawn3 } from "child_process";
39333
40319
  import { existsSync as existsSync9, readdirSync as readdirSync3, statSync as statSync4 } from "fs";
39334
40320
  import { join as join8, relative, resolve as resolve5 } from "path";
39335
40321
  init_voice_api_service();
39336
40322
  init_constants();
39337
40323
 
39338
40324
  // src/commands/voice-terminal.ts
39339
- import { spawn, spawnSync } from "child_process";
40325
+ import { spawn as spawn2, spawnSync } from "child_process";
39340
40326
  import { platform as platform3 } from "os";
39341
40327
  import { AudioFrame, AudioSource, AudioStream, LocalAudioTrack, Room, RoomEvent, TrackKind, TrackPublishOptions, TrackSource } from "@livekit/rtc-node";
39342
40328
 
@@ -39599,7 +40585,7 @@ function spawnCapture() {
39599
40585
  String(FRAME_SAMPLES * 2),
39600
40586
  "-"
39601
40587
  ];
39602
- const proc = spawn("sox", args2, {
40588
+ const proc = spawn2("sox", args2, {
39603
40589
  stdio: [
39604
40590
  "ignore",
39605
40591
  "pipe",
@@ -39630,7 +40616,7 @@ function spawnPlayback() {
39630
40616
  "-",
39631
40617
  "-d"
39632
40618
  ];
39633
- const proc = spawn("sox", args2, {
40619
+ const proc = spawn2("sox", args2, {
39634
40620
  stdio: [
39635
40621
  "pipe",
39636
40622
  "ignore",
@@ -39960,7 +40946,7 @@ function buildRunnerArgs(runner, opts) {
39960
40946
  __name(buildRunnerArgs, "buildRunnerArgs");
39961
40947
  async function spawnRunner(runner, args2, cwd) {
39962
40948
  return new Promise((resolveExit) => {
39963
- const proc = spawn2("npx", [
40949
+ const proc = spawn3("npx", [
39964
40950
  runner,
39965
40951
  ...args2
39966
40952
  ], {
@@ -40353,6 +41339,12 @@ async function sourceRollbackCommand(opts) {
40353
41339
  const config = readYamlConfig();
40354
41340
  const agentId = config?.agent?.agentId;
40355
41341
  if (!agentId) throw new Error("No agentId in lua.skill.yaml. Run `lua init` first.");
41342
+ if (!opts.silent) {
41343
+ const versioning = await getVersioningModeCached(apiKey, agentId);
41344
+ if (versioning.enabled) {
41345
+ writeInfo("\u26A0\uFE0F `lua source rollback` is deprecated when agent versioning is on. Use `lua version promote <version>` \u2014 instant, no re-upload required.");
41346
+ }
41347
+ }
40356
41348
  const fetchM = opts.fetchManifest ?? defaultFetchManifest;
40357
41349
  const fetchU = opts.fetchUrls ?? defaultFetchUrls;
40358
41350
  const dlBlobs = opts.downloadBlobs ?? downloadBlobsParallel;
@@ -40423,6 +41415,560 @@ function defaultRestore(manifest, blobs, targetDir) {
40423
41415
  }
40424
41416
  __name(defaultRestore, "defaultRestore");
40425
41417
 
41418
+ // src/commands/version.ts
41419
+ init_cli();
41420
+ init_command_utils();
41421
+ init_analytics();
41422
+ init_files();
41423
+ init_constants();
41424
+
41425
+ // src/utils/parse-version.ts
41426
+ function parseVersion2(arg) {
41427
+ if (!arg) throw new Error(`Invalid version "${arg}": must be a positive integer like "3" or "v3".`);
41428
+ const stripped = arg.replace(/^v/i, "");
41429
+ const n = Number.parseInt(stripped, 10);
41430
+ if (!Number.isFinite(n) || n <= 0 || String(n) !== stripped) {
41431
+ throw new Error(`Invalid version "${arg}": must be a positive integer like "3" or "v3".`);
41432
+ }
41433
+ return n;
41434
+ }
41435
+ __name(parseVersion2, "parseVersion");
41436
+
41437
+ // src/commands/version.ts
41438
+ async function assertVersioningEnabled(apiKey, agentId) {
41439
+ const { enabled } = await getVersioningModeCached(apiKey, agentId);
41440
+ if (!enabled) {
41441
+ throw new Error("Agent versioning is not enabled for your organization. Contact your admin to enable it, or use `lua push <primitive>` for the legacy flow.");
41442
+ }
41443
+ }
41444
+ __name(assertVersioningEnabled, "assertVersioningEnabled");
41445
+ async function versionCreateCommand(options = {}) {
41446
+ return withErrorHandling(async () => {
41447
+ const { apiKey, agentId } = await initializeCommand();
41448
+ await assertVersioningEnabled(apiKey, agentId);
41449
+ let config = readYamlConfig();
41450
+ let backupVersion = config?.backup?.activeVersion;
41451
+ if (options.autoPush) {
41452
+ await pushAllCommand({
41453
+ force: true,
41454
+ includeSource: true,
41455
+ suppressVersionCreateHint: true
41456
+ });
41457
+ config = readYamlConfig();
41458
+ backupVersion = config?.backup?.activeVersion;
41459
+ if (backupVersion == null) {
41460
+ throw new Error("Backup tracking lost after push. Aborting.");
41461
+ }
41462
+ }
41463
+ if (backupVersion == null) {
41464
+ throw new Error("No backup exists for this agent. Run `lua push` first, or pass `--auto-push` to push and snapshot in one step.");
41465
+ }
41466
+ let message = options.message;
41467
+ const isTTY = Boolean(process.stdin.isTTY && process.stdout.isTTY);
41468
+ const isCi = isCiModeEnabled();
41469
+ if (!message && isTTY && !isCi) {
41470
+ const answers = await safePrompt([
41471
+ {
41472
+ type: "input",
41473
+ name: "input",
41474
+ message: "Optional description (blank to skip):"
41475
+ }
41476
+ ]);
41477
+ message = answers?.input?.trim() || void 0;
41478
+ }
41479
+ const api = new AgentVersionApi(BASE_URLS.API, apiKey, agentId);
41480
+ const result = await api.createVersion({
41481
+ message,
41482
+ sourceManifestVersion: backupVersion,
41483
+ commitHash: options.commitHash
41484
+ });
41485
+ if (!result.success || !result.data) {
41486
+ throw new Error(result.error?.message ?? "Failed to create version");
41487
+ }
41488
+ writeInfo(`\u2713 Created v${result.data.version} (staged). Run \`lua version promote v${result.data.version}\` to deploy.`);
41489
+ try {
41490
+ const gitResult = await tryGitCommit({
41491
+ message: GIT_MESSAGES.createVersion(result.data.version, message),
41492
+ action: "version-create",
41493
+ tag: tagName(result.data.version),
41494
+ allowEmpty: true
41495
+ });
41496
+ if (gitResult.committed && gitResult.sha) {
41497
+ await api.patchCommitHash(result.data.version, gitResult.sha);
41498
+ }
41499
+ } catch {
41500
+ }
41501
+ trackEvent("cli_version_create_completed", {
41502
+ auto_push: Boolean(options.autoPush),
41503
+ has_message: Boolean(message),
41504
+ non_interactive: !isTTY || isCi
41505
+ });
41506
+ }, "version create");
41507
+ }
41508
+ __name(versionCreateCommand, "versionCreateCommand");
41509
+ async function versionListCommand(options = {}) {
41510
+ return withErrorHandling(async () => {
41511
+ const { apiKey, agentId } = await initializeCommand();
41512
+ await assertVersioningEnabled(apiKey, agentId);
41513
+ const query = {};
41514
+ if (options.all) query.all = true;
41515
+ if (options.limit != null) query.limit = options.limit;
41516
+ if (options.status) query.status = options.status;
41517
+ const api = new AgentVersionApi(BASE_URLS.API, apiKey, agentId);
41518
+ const response = await api.listVersions(query);
41519
+ if (!response.success || !response.data) {
41520
+ throw new Error(response.error?.message ?? "Failed to list versions");
41521
+ }
41522
+ const versions = response.data;
41523
+ if (options.json) {
41524
+ console.log(JSON.stringify(versions, null, 2));
41525
+ } else if (versions.length === 0) {
41526
+ writeInfo("(no versions yet \u2014 run `lua version create` to make one)");
41527
+ } else {
41528
+ console.log("VERSION STATUS CREATED BY MESSAGE");
41529
+ for (const v of versions) {
41530
+ const star = v.status === "active" ? "*" : " ";
41531
+ const date = new Date(v.createdAt).toISOString().slice(0, 16).replace("T", " ");
41532
+ const status = v.status.padEnd(11);
41533
+ const by = (v.createdBy || "").slice(0, 10).padEnd(10);
41534
+ const msg = (v.message || "").slice(0, 60);
41535
+ console.log(`v${v.version}${star} ${status} ${date} ${by}${msg}`);
41536
+ }
41537
+ }
41538
+ trackEvent("cli_version_list_completed", {
41539
+ count: versions.length,
41540
+ status_filter: options.status ?? "all"
41541
+ });
41542
+ }, "version list");
41543
+ }
41544
+ __name(versionListCommand, "versionListCommand");
41545
+ async function versionShowCommand(versionArg, options = {}) {
41546
+ return withErrorHandling(async () => {
41547
+ const { apiKey, agentId } = await initializeCommand();
41548
+ await assertVersioningEnabled(apiKey, agentId);
41549
+ const version = parseVersion2(versionArg);
41550
+ const api = new AgentVersionApi(BASE_URLS.API, apiKey, agentId);
41551
+ const response = await api.getVersion(version);
41552
+ if (!response.success || !response.data) {
41553
+ throw new Error(response.error?.message ?? `Version v${version} not found`);
41554
+ }
41555
+ const v = response.data;
41556
+ if (options.json) {
41557
+ console.log(JSON.stringify(v, null, 2));
41558
+ } else {
41559
+ console.log(`Version v${v.version} (${v.status})`);
41560
+ console.log(` Created: ${v.createdAt} by ${v.createdBy}`);
41561
+ if (v.message) console.log(` Message: ${v.message}`);
41562
+ if (v.commitHash) console.log(` Commit: ${v.commitHash}`);
41563
+ console.log(` Model: ${v.snapshot.model}`);
41564
+ console.log(` Snapshot:`);
41565
+ console.log(` Skills: ${v.snapshot.skills.length}`);
41566
+ console.log(` Webhooks: ${v.snapshot.webhooks.length}`);
41567
+ console.log(` Jobs: ${v.snapshot.jobs.length}`);
41568
+ console.log(` Preprocessors: ${v.snapshot.preprocessors.length}`);
41569
+ console.log(` Postprocessors: ${v.snapshot.postprocessors.length}`);
41570
+ console.log(` MCP servers: ${v.snapshot.mcpServers.length}`);
41571
+ console.log(` Persona: ${v.snapshot.persona.versionId || "(none)"}`);
41572
+ }
41573
+ trackEvent("cli_version_show_completed", {
41574
+ version,
41575
+ json: Boolean(options.json)
41576
+ });
41577
+ }, "version show");
41578
+ }
41579
+ __name(versionShowCommand, "versionShowCommand");
41580
+ async function versionDiffCommand(fromArg, toArg, options = {}) {
41581
+ return withErrorHandling(async () => {
41582
+ const { apiKey, agentId } = await initializeCommand();
41583
+ await assertVersioningEnabled(apiKey, agentId);
41584
+ const from = parseVersion2(fromArg);
41585
+ const to = parseVersion2(toArg);
41586
+ const api = new AgentVersionApi(BASE_URLS.API, apiKey, agentId);
41587
+ const response = await api.diffVersions(from, to);
41588
+ if (!response.success || !response.data) {
41589
+ throw new Error(response.error?.message ?? "Failed to diff versions");
41590
+ }
41591
+ const diff = response.data;
41592
+ if (options.json) {
41593
+ console.log(JSON.stringify(diff, null, 2));
41594
+ } else {
41595
+ console.log(`Diff v${from} \u2192 v${to}`);
41596
+ const sections = [
41597
+ {
41598
+ name: "Skills",
41599
+ entry: diff.skills,
41600
+ key: "skillId"
41601
+ },
41602
+ {
41603
+ name: "Webhooks",
41604
+ entry: diff.webhooks,
41605
+ key: "webhookId"
41606
+ },
41607
+ {
41608
+ name: "Jobs",
41609
+ entry: diff.jobs,
41610
+ key: "jobId"
41611
+ },
41612
+ {
41613
+ name: "Preprocessors",
41614
+ entry: diff.preprocessors,
41615
+ key: "id"
41616
+ },
41617
+ {
41618
+ name: "Postprocessors",
41619
+ entry: diff.postprocessors,
41620
+ key: "id"
41621
+ }
41622
+ ];
41623
+ for (const { name, entry, key } of sections) {
41624
+ const total = entry.added.length + entry.removed.length + entry.changed.length;
41625
+ if (total === 0) {
41626
+ console.log(`${name}: (no changes)`);
41627
+ continue;
41628
+ }
41629
+ console.log(`${name}:`);
41630
+ for (const a of entry.added) {
41631
+ console.log(` + ${a[key]}@${a.version} (added)`);
41632
+ }
41633
+ for (const r of entry.removed) {
41634
+ console.log(` - ${r[key]}@${r.version} (removed)`);
41635
+ }
41636
+ for (const c of entry.changed) {
41637
+ console.log(` ~ ${c[key]} ${c.from.version} \u2192 ${c.to.version}`);
41638
+ }
41639
+ }
41640
+ const mcp = diff.mcpServers;
41641
+ if (mcp.added.length || mcp.removed.length || mcp.changed.length) {
41642
+ console.log("MCP servers:");
41643
+ mcp.added.forEach((m) => console.log(` + ${m.id} (added)`));
41644
+ mcp.removed.forEach((m) => console.log(` - ${m.id} (removed)`));
41645
+ mcp.changed.forEach((m) => console.log(` ~ ${m.id} (config changed)`));
41646
+ } else {
41647
+ console.log("MCP servers: (no changes)");
41648
+ }
41649
+ console.log(`Persona: ${diff.persona ? `${diff.persona.from} \u2192 ${diff.persona.to}` : "(unchanged)"}`);
41650
+ console.log(`Model: ${diff.model ? `${diff.model.from} \u2192 ${diff.model.to}` : "(unchanged)"}`);
41651
+ }
41652
+ trackEvent("cli_version_diff_completed", {
41653
+ from_to_distance: Math.abs(to - from)
41654
+ });
41655
+ }, "version diff");
41656
+ }
41657
+ __name(versionDiffCommand, "versionDiffCommand");
41658
+ async function versionPromoteCommand(versionArg) {
41659
+ return withErrorHandling(async () => {
41660
+ const { apiKey, agentId } = await initializeCommand();
41661
+ await assertVersioningEnabled(apiKey, agentId);
41662
+ const version = parseVersion2(versionArg);
41663
+ const api = new AgentVersionApi(BASE_URLS.API, apiKey, agentId);
41664
+ const response = await api.promoteVersion(version);
41665
+ if (!response.success) {
41666
+ const errCode = response.error?.code;
41667
+ if (errCode === "VERSION_ALREADY_ACTIVE") {
41668
+ writeInfo(`v${version} is already active. No change.`);
41669
+ return;
41670
+ }
41671
+ throw new Error(response.error?.message ?? `Failed to promote v${version}`);
41672
+ }
41673
+ if (!response.data) {
41674
+ throw new Error(`Promote returned no data`);
41675
+ }
41676
+ const { promoted, previousActive } = response.data;
41677
+ const rollback = previousActive != null && previousActive > promoted;
41678
+ if (previousActive == null) {
41679
+ writeInfo(`\u2713 Promoted v${promoted}. (No previous active version.)`);
41680
+ } else {
41681
+ writeInfo(`\u2713 Promoted v${promoted}. Previous active: v${previousActive}.`);
41682
+ }
41683
+ try {
41684
+ await tryGitCommit({
41685
+ message: GIT_MESSAGES.promote(promoted),
41686
+ action: "version-promote",
41687
+ allowEmpty: true
41688
+ });
41689
+ } catch {
41690
+ }
41691
+ trackEvent("cli_version_promote_completed", {
41692
+ from_version: previousActive,
41693
+ to_version: promoted,
41694
+ rollback
41695
+ });
41696
+ }, "version promote");
41697
+ }
41698
+ __name(versionPromoteCommand, "versionPromoteCommand");
41699
+ async function versionDeleteCommand(versionArg, options = {}) {
41700
+ return withErrorHandling(async () => {
41701
+ const { apiKey, agentId } = await initializeCommand();
41702
+ await assertVersioningEnabled(apiKey, agentId);
41703
+ if (versionArg === "current" || versionArg === "active") {
41704
+ throw new Error(`Cannot delete the active version directly. Use \`lua version promote <other>\` to roll back first, then delete.`);
41705
+ }
41706
+ const version = parseVersion2(versionArg);
41707
+ const isTTY = Boolean(process.stdin.isTTY && process.stdout.isTTY);
41708
+ const isCi = isCiModeEnabled();
41709
+ if (!options.force && isTTY && !isCi) {
41710
+ const answer = await safePrompt([
41711
+ {
41712
+ type: "confirm",
41713
+ name: "confirm",
41714
+ message: `Delete v${version}?`,
41715
+ default: false
41716
+ }
41717
+ ]);
41718
+ if (!answer?.confirm) {
41719
+ writeInfo("Aborted.");
41720
+ return;
41721
+ }
41722
+ }
41723
+ const api = new AgentVersionApi(BASE_URLS.API, apiKey, agentId);
41724
+ const response = await api.deleteVersion(version);
41725
+ if (!response.success) {
41726
+ const errCode = response.error?.code;
41727
+ if (errCode === "CANNOT_DELETE_ACTIVE_VERSION") {
41728
+ throw new Error(`Cannot delete v${version}: it is the active version. Promote a different version first.`);
41729
+ }
41730
+ if (errCode === "CANNOT_DELETE_LAST_VERSION") {
41731
+ throw new Error(`Cannot delete v${version}: it is the only remaining version.`);
41732
+ }
41733
+ throw new Error(response.error?.message ?? `Failed to delete v${version}`);
41734
+ }
41735
+ writeInfo(`\u2713 Deleted v${version}.`);
41736
+ try {
41737
+ await tryGitCommit({
41738
+ message: GIT_MESSAGES.delete(version),
41739
+ action: "version-delete",
41740
+ allowEmpty: true
41741
+ });
41742
+ } catch {
41743
+ }
41744
+ trackEvent("cli_version_delete_completed", {
41745
+ version,
41746
+ force: Boolean(options.force)
41747
+ });
41748
+ }, "version delete");
41749
+ }
41750
+ __name(versionDeleteCommand, "versionDeleteCommand");
41751
+
41752
+ // src/commands/git.ts
41753
+ init_cli();
41754
+ init_files();
41755
+ init_analytics();
41756
+ var NO_YAML_MESSAGE = "No lua.skill.yaml found. Please run this command from a skill directory.";
41757
+ function failConnect(status, banner, errorMessage) {
41758
+ if (banner) writeError(banner);
41759
+ trackEvent("cli_git_connect_completed", {
41760
+ ...status,
41761
+ succeeded: false
41762
+ });
41763
+ throw new Error(errorMessage);
41764
+ }
41765
+ __name(failConnect, "failConnect");
41766
+ async function gitConnectCommand() {
41767
+ return withErrorHandling(async () => {
41768
+ const config = readYamlConfig();
41769
+ if (!config) {
41770
+ failConnect({
41771
+ git_available: false,
41772
+ in_repo: false,
41773
+ user_configured: false
41774
+ }, null, NO_YAML_MESSAGE);
41775
+ }
41776
+ if (!await isGitAvailable()) {
41777
+ failConnect({
41778
+ git_available: false,
41779
+ in_repo: false,
41780
+ user_configured: false
41781
+ }, "\u2717 Git binary not found on PATH.\n Fix: install git from https://git-scm.com/downloads, then re-run `lua git connect`.", "git is not installed");
41782
+ }
41783
+ if (!await isInsideGitRepo()) {
41784
+ failConnect({
41785
+ git_available: true,
41786
+ in_repo: false,
41787
+ user_configured: false
41788
+ }, "\u2717 This directory is not inside a git repository.\n Fix: run `git init` in your project root, then re-run `lua git connect`.", "not inside a git repository");
41789
+ }
41790
+ const identity = await gitUserConfigured();
41791
+ if (!identity.email) {
41792
+ failConnect({
41793
+ git_available: true,
41794
+ in_repo: true,
41795
+ user_configured: false
41796
+ }, '\u2717 Git user.email is not configured.\n Fix: git config --global user.email "you@example.com"', "git user.email is not configured");
41797
+ }
41798
+ if (!identity.name) {
41799
+ failConnect({
41800
+ git_available: true,
41801
+ in_repo: true,
41802
+ user_configured: false
41803
+ }, '\u2717 Git user.name is not configured.\n Fix: git config --global user.name "Your Name"', "git user.name is not configured");
41804
+ }
41805
+ config.git = {
41806
+ enabled: true
41807
+ };
41808
+ writeYamlConfig(config);
41809
+ writeSuccess("\u2713 Git integration enabled. Future `lua push`, `lua version *`, and `lua pull` runs will auto-commit.");
41810
+ trackEvent("cli_git_connect_completed", {
41811
+ git_available: true,
41812
+ in_repo: true,
41813
+ user_configured: true,
41814
+ succeeded: true
41815
+ });
41816
+ }, "git connect");
41817
+ }
41818
+ __name(gitConnectCommand, "gitConnectCommand");
41819
+ async function gitDisconnectCommand() {
41820
+ return withErrorHandling(async () => {
41821
+ const config = readYamlConfig();
41822
+ if (!config) {
41823
+ throw new Error(NO_YAML_MESSAGE);
41824
+ }
41825
+ config.git = {
41826
+ enabled: false
41827
+ };
41828
+ writeYamlConfig(config);
41829
+ writeSuccess("\u2713 Git integration disabled. Existing commits and tags are untouched.");
41830
+ trackEvent("cli_git_disconnect_completed", {});
41831
+ }, "git disconnect");
41832
+ }
41833
+ __name(gitDisconnectCommand, "gitDisconnectCommand");
41834
+ async function gitStatusCommand() {
41835
+ return withErrorHandling(async () => {
41836
+ const config = readYamlConfig();
41837
+ const enabled = Boolean(config?.git?.enabled);
41838
+ if (!enabled) {
41839
+ writeInfo("Git integration: disabled (run `lua git connect` to enable)");
41840
+ trackEvent("cli_git_status_completed", {
41841
+ enabled: false
41842
+ });
41843
+ return;
41844
+ }
41845
+ const settled = await Promise.allSettled([
41846
+ runGit([
41847
+ "--version"
41848
+ ]),
41849
+ gitUserConfigured(),
41850
+ runGit([
41851
+ "log",
41852
+ "--grep",
41853
+ "^lua: ",
41854
+ "-n",
41855
+ "1",
41856
+ "--format=%h %s"
41857
+ ]),
41858
+ // tryGitCommit creates lightweight tags (`git tag <name>`), which have
41859
+ // no taggerdate field. creatordate works for both flavors and returns
41860
+ // the committerdate of the referenced commit — chronological order
41861
+ // matches our pattern of tagging immediately after the commit.
41862
+ runGit([
41863
+ "for-each-ref",
41864
+ "--sort=-creatordate",
41865
+ "refs/tags/lua/",
41866
+ "--count=1",
41867
+ "--format=%(refname:short)"
41868
+ ])
41869
+ ]);
41870
+ const versionResult = settled[0].status === "fulfilled" ? settled[0].value : {
41871
+ stdout: "",
41872
+ stderr: "",
41873
+ code: 1
41874
+ };
41875
+ const identity = settled[1].status === "fulfilled" ? settled[1].value : {
41876
+ name: void 0,
41877
+ email: void 0
41878
+ };
41879
+ const lastCommitResult = settled[2].status === "fulfilled" ? settled[2].value : {
41880
+ stdout: "",
41881
+ stderr: "",
41882
+ code: 1
41883
+ };
41884
+ const lastTagResult = settled[3].status === "fulfilled" ? settled[3].value : {
41885
+ stdout: "",
41886
+ stderr: "",
41887
+ code: 1
41888
+ };
41889
+ const versionLine = versionResult.code === 0 ? `${versionResult.stdout.trim()} (\u2713)` : "(not detected)";
41890
+ const identityLine = identity.name && identity.email ? `${identity.name} <${identity.email}> (\u2713)` : "(not configured)";
41891
+ const lastCommit = lastCommitResult.code === 0 && lastCommitResult.stdout.trim() ? lastCommitResult.stdout.trim() : "(none yet)";
41892
+ const lastTag = lastTagResult.code === 0 && lastTagResult.stdout.trim() ? lastTagResult.stdout.trim() : "(none yet)";
41893
+ writeInfo(`Git integration: enabled`);
41894
+ writeInfo(`Project repo: ${process.cwd()}`);
41895
+ writeInfo(`Git binary: ${versionLine}`);
41896
+ writeInfo(`User identity: ${identityLine}`);
41897
+ writeInfo(`Last lua commit: ${lastCommit}`);
41898
+ writeInfo(`Last lua tag: ${lastTag}`);
41899
+ trackEvent("cli_git_status_completed", {
41900
+ enabled: true
41901
+ });
41902
+ }, "git status");
41903
+ }
41904
+ __name(gitStatusCommand, "gitStatusCommand");
41905
+
41906
+ // src/commands/pull.ts
41907
+ init_cli();
41908
+ init_command_utils();
41909
+ init_analytics();
41910
+ init_backup_api_service();
41911
+ init_constants();
41912
+ async function pullCommand(options = {}) {
41913
+ return withErrorHandling(async () => {
41914
+ const { apiKey, agentId } = await initializeCommand();
41915
+ const force = Boolean(options.force);
41916
+ if (options.version) {
41917
+ await assertVersioningEnabled(apiKey, agentId);
41918
+ const version = parseVersion2(options.version);
41919
+ const api = new AgentVersionApi(BASE_URLS.API, apiKey, agentId);
41920
+ const response = await api.getVersion(version);
41921
+ if (!response.success || !response.data) {
41922
+ throw new Error(response.error?.message ?? `Version v${version} not found`);
41923
+ }
41924
+ const sourceManifestVersion = response.data.sourceManifestVersion;
41925
+ if (sourceManifestVersion == null) {
41926
+ throw new Error(`Version v${version} has no source backup linked. Cannot pull source for this version.`);
41927
+ }
41928
+ writeInfo(`Pulling source for agent version v${version} (backup v${sourceManifestVersion})\u2026`);
41929
+ await sourceRollbackCommand({
41930
+ version: sourceManifestVersion,
41931
+ force,
41932
+ silent: true
41933
+ });
41934
+ try {
41935
+ await tryGitCommit({
41936
+ message: GIT_MESSAGES.pullVersion(version),
41937
+ action: "pull-version"
41938
+ });
41939
+ } catch {
41940
+ }
41941
+ trackEvent("cli_pull_completed", {
41942
+ has_version_arg: true
41943
+ });
41944
+ return;
41945
+ }
41946
+ const backupApi = new BackupApi(BASE_URLS.API, apiKey, agentId);
41947
+ const backupResp = await backupApi.getBackupVersions();
41948
+ if (!backupResp.success || !backupResp.data?.versions?.length) {
41949
+ throw new Error("No backup versions available to pull.");
41950
+ }
41951
+ const latest = backupResp.data.versions[0].version;
41952
+ writeInfo(`Pulling latest source backup (v${latest})\u2026`);
41953
+ await sourceRollbackCommand({
41954
+ version: latest,
41955
+ force,
41956
+ silent: true
41957
+ });
41958
+ try {
41959
+ await tryGitCommit({
41960
+ message: GIT_MESSAGES.pullLatest,
41961
+ action: "pull-latest"
41962
+ });
41963
+ } catch {
41964
+ }
41965
+ trackEvent("cli_pull_completed", {
41966
+ has_version_arg: false
41967
+ });
41968
+ }, "pull");
41969
+ }
41970
+ __name(pullCommand, "pullCommand");
41971
+
40426
41972
  // src/cli/command-definitions.ts
40427
41973
  init_cli();
40428
41974
  function setupAuthCommands(program2) {
@@ -41051,6 +42597,50 @@ Examples:
41051
42597
  $ lua voice test --runner vitest Force vitest
41052
42598
  `).action(voiceTestCommand);
41053
42599
  voice.command("list").description("List LuaVoice primitives in the compiled manifest").option("--json", "Output as JSON").action(voiceListCommand);
42600
+ const versionGroup = program2.command("version").description("\u{1F3F7}\uFE0F Manage agent versions (atomic snapshots of agent state)");
42601
+ versionGroup.command("create").description("Snapshot current staged state into a new version").option("-m, --message <message>", "Optional description for this version").option("--auto-push", "Push first if local changes are not yet staged").option("--commit-hash <hash>", "Optional git commit hash to associate with this version").addHelpText("after", `
42602
+ Examples:
42603
+ $ lua version create Snapshot current state
42604
+ $ lua version create -m "Add FAQ skill" Include a description
42605
+ $ lua version create --auto-push Push then snapshot in one step
42606
+ `).action((opts) => versionCreateCommand(opts));
42607
+ versionGroup.command("list").description("List versions of the current agent").option("--all", "Show all versions (no server-side cap)").option("--limit <n>", "Cap output to this many entries", (v) => parseInt(v, 10)).option("--status <status>", "Filter by status: active | staged | all", "all").option("--json", "Output as JSON").addHelpText("after", `
42608
+ Examples:
42609
+ $ lua version list List all versions (default)
42610
+ $ lua version list --status active Show only active versions
42611
+ $ lua version list --limit 10 Show the 10 most recent
42612
+ $ lua version list --json Machine-readable output
42613
+ `).action((opts) => versionListCommand(opts));
42614
+ versionGroup.command("show <version>").description("Show full snapshot of a specific version").option("--json", "Output as JSON").addHelpText("after", `
42615
+ Examples:
42616
+ $ lua version show 3 Show version 3 details
42617
+ $ lua version show v3 --json JSON output
42618
+ `).action((version, opts) => versionShowCommand(version, opts));
42619
+ versionGroup.command("diff <from> <to>").description("Compare two versions side-by-side").option("--json", "Output diff as JSON").addHelpText("after", `
42620
+ Examples:
42621
+ $ lua version diff 2 3 Compare v2 \u2192 v3
42622
+ $ lua version diff v2 v3 --json Machine-readable diff
42623
+ `).action((from, to, opts) => versionDiffCommand(from, to, opts));
42624
+ versionGroup.command("promote <version>").description("Promote a version to active (atomic, instant)").addHelpText("after", `
42625
+ Examples:
42626
+ $ lua version promote 3 Promote v3 to the active version
42627
+ $ lua version promote v3 Same (v-prefix accepted)
42628
+ `).action((version) => versionPromoteCommand(version));
42629
+ versionGroup.command("delete <version>").description("Soft-delete a version").option("--force", "Skip confirmation prompt").addHelpText("after", `
42630
+ Examples:
42631
+ $ lua version delete 3 Delete v3 (confirms first)
42632
+ $ lua version delete v3 --force Skip confirmation
42633
+ `).action((version, opts) => versionDeleteCommand(version, opts));
42634
+ const gitGroup = program2.command("git").description("Manage opt-in git auto-commits for this project");
42635
+ gitGroup.command("connect").description("Enable git auto-commits (runs sanity checks, writes git.enabled: true)").action(() => gitConnectCommand());
42636
+ gitGroup.command("disconnect").description("Disable git auto-commits (existing commits/tags untouched)").action(() => gitDisconnectCommand());
42637
+ gitGroup.command("status").description("Show git integration config + last lua-issued commit/tag").action(() => gitStatusCommand());
42638
+ program2.command("pull").description("\u{1F4E5} Pull the agent's source code locally").option("--version <version>", "Pull source linked to a specific agent version (requires versioning)").option("--force", "Skip confirmation prompt").addHelpText("after", `
42639
+ Examples:
42640
+ $ lua pull Restore the latest source backup
42641
+ $ lua pull --version 3 Restore the source snapshot from agent version 3
42642
+ $ lua pull --version v3 --force Skip the confirmation prompt
42643
+ `).action((opts) => pullCommand(opts));
41054
42644
  const sourceCmd = program2.command("source").description("\u{1F5C2}\uFE0F Manage workspace source versions");
41055
42645
  sourceCmd.command("list").description("List source versions for the current agent").option("--all", "Show all versions instead of the most recent 50", false).option("--limit <n>", "Number of versions to show", "50").addHelpText("after", `
41056
42646
  Examples: