@axiom-lattice/gateway 3.0.9 → 4.0.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.mjs CHANGED
@@ -6,6 +6,15 @@ import {
6
6
  getQrCode,
7
7
  getQrCodeStatus
8
8
  } from "./chunk-6CUQGDJI.mjs";
9
+ import {
10
+ PathOutsideRootError,
11
+ ProjectFileValidationError,
12
+ filesystemRootDirectory,
13
+ isBinaryContentType,
14
+ isPathWithinRoot,
15
+ resolvePathWithinRoot,
16
+ saveProjectFile
17
+ } from "./chunk-IDOEIBCF.mjs";
9
18
  import {
10
19
  getContentTypeFromFilename,
11
20
  getFilenameFromPath
@@ -59,8 +68,8 @@ function resolveLocalA2AWorkingDirectory(options) {
59
68
  }
60
69
  function validateWorkingDirectory(cwd) {
61
70
  try {
62
- const stat2 = statSync(cwd);
63
- if (!stat2.isDirectory()) {
71
+ const stat = statSync(cwd);
72
+ if (!stat.isDirectory()) {
64
73
  return `Working directory is not a directory: ${cwd}`;
65
74
  }
66
75
  return null;
@@ -739,17 +748,17 @@ async function getAssistantStats(request, reply) {
739
748
  try {
740
749
  const threads = await threadStore.getThreadsByAssistantId(tenantId, assistant.id);
741
750
  threadCount = threads.length;
742
- for (const t of threads) {
743
- const created = new Date(t.createdAt).getTime();
751
+ for (const t2 of threads) {
752
+ const created = new Date(t2.createdAt).getTime();
744
753
  if (created >= now - ms7d) last7d++;
745
754
  if (created >= now - ms30d) last30d++;
746
755
  const idx7 = Math.floor((now - created) / 864e5);
747
756
  if (idx7 >= 0 && idx7 < 7) daily7d[6 - idx7]++;
748
757
  const idx30 = Math.floor((now - created) / 864e5);
749
758
  if (idx30 >= 0 && idx30 < 30) daily30d[29 - idx30]++;
750
- const updated = new Date(t.updatedAt).getTime();
759
+ const updated = new Date(t2.updatedAt).getTime();
751
760
  if (lastActive === null || updated > lastActive.getTime()) {
752
- lastActive = new Date(t.updatedAt);
761
+ lastActive = new Date(t2.updatedAt);
753
762
  }
754
763
  }
755
764
  } catch {
@@ -895,7 +904,7 @@ var createRun = async (request, reply) => {
895
904
 
896
905
  `);
897
906
  if (!success) {
898
- await new Promise((resolve3) => reply.raw.once("drain", resolve3));
907
+ await new Promise((resolve2) => reply.raw.once("drain", resolve2));
899
908
  }
900
909
  }
901
910
  } catch (error) {
@@ -2183,11 +2192,11 @@ var CollectionService = class {
2183
2192
  async getAllCollections(request) {
2184
2193
  const tenantId = getTenantId5(request);
2185
2194
  const collections = await collectionLatticeManager.getAllCollections(tenantId);
2186
- for (const c of collections) {
2195
+ for (const c2 of collections) {
2187
2196
  try {
2188
- c._entryCount = await getCollectionEntryCount(c.name, c.embeddingKey, c.tenantId);
2197
+ c2._entryCount = await getCollectionEntryCount(c2.name, c2.embeddingKey, c2.tenantId);
2189
2198
  } catch {
2190
- c._entryCount = 0;
2199
+ c2._entryCount = 0;
2191
2200
  }
2192
2201
  }
2193
2202
  return collections;
@@ -3041,7 +3050,7 @@ async function getInboxItems(request, reply) {
3041
3050
  const runStatus = await agent.getRunStatus();
3042
3051
  if (runStatus !== "interrupted") return [];
3043
3052
  const state = await agent.getCurrentState();
3044
- const interrupts = state.tasks?.flatMap((t) => t.interrupts || []) || [];
3053
+ const interrupts = state.tasks?.flatMap((t2) => t2.interrupts || []) || [];
3045
3054
  return interrupts.map((i) => ({
3046
3055
  runId: r.id,
3047
3056
  assistantId: r.assistantId,
@@ -3398,7 +3407,8 @@ async function createPersonalAssistant(request, reply) {
3398
3407
  const wsId = getWorkspaceId2(request);
3399
3408
  await projectStore.createProject(tenantId, wsId, projectId, {
3400
3409
  name: `${name}'s Space`,
3401
- description: "Personal workspace for your assistant"
3410
+ description: "Personal workspace for your assistant",
3411
+ kind: "personal"
3402
3412
  });
3403
3413
  const config = PersonalAssistantConfig.get();
3404
3414
  config.key = assistantId;
@@ -3448,8 +3458,8 @@ async function deletePersonalAssistant(request, reply) {
3448
3458
  const threadStore = getStoreLattice5("default", "thread").store;
3449
3459
  try {
3450
3460
  const threads = await threadStore.getThreadsByAssistantId(tenantId, assistant.id);
3451
- for (const t of threads) {
3452
- await threadStore.deleteThread(tenantId, t.id);
3461
+ for (const t2 of threads) {
3462
+ await threadStore.deleteThread(tenantId, t2.id);
3453
3463
  }
3454
3464
  } catch {
3455
3465
  }
@@ -3469,170 +3479,19 @@ async function deletePersonalAssistant(request, reply) {
3469
3479
  return { success: true, message: "Personal assistant deleted" };
3470
3480
  }
3471
3481
 
3472
- // src/controllers/tasks.ts
3473
- import { getStoreLattice as getStoreLattice6 } from "@axiom-lattice/core";
3474
- function getTenantId9(request) {
3475
- const userTenantId = request.user?.tenantId;
3476
- if (userTenantId) return userTenantId;
3477
- return request.headers["x-tenant-id"] || "default";
3478
- }
3479
- function getUserId4(request) {
3480
- const userId = request.user?.id || request.headers["x-user-id"];
3481
- if (!userId) throw new Error("User ID is required");
3482
- return userId;
3483
- }
3484
- function getStore() {
3485
- return getStoreLattice6("default", "task").store;
3486
- }
3487
- async function listTasks(request, reply) {
3488
- try {
3489
- const tenantId = getTenantId9(request);
3490
- const query = request.query;
3491
- const store = getStore();
3492
- const tasks = await store.list({
3493
- tenantId,
3494
- status: query.status,
3495
- priority: query.priority,
3496
- ...query.ownerId && { ownerId: query.ownerId },
3497
- ...query.ownerType && { ownerType: query.ownerType },
3498
- ...query.parentId && { parentId: query.parentId },
3499
- metadata: query.metadata ? JSON.parse(query.metadata) : void 0,
3500
- projectId: query.projectId,
3501
- limit: query.limit ? parseInt(query.limit) : void 0,
3502
- offset: query.offset ? parseInt(query.offset) : void 0
3503
- });
3504
- return { success: true, data: tasks, count: tasks.length };
3505
- } catch (error) {
3506
- return { success: false, error: error.message };
3507
- }
3508
- }
3509
- async function getTask(request, reply) {
3510
- try {
3511
- const tenantId = getTenantId9(request);
3512
- const userId = getUserId4(request);
3513
- const { id } = request.params;
3514
- const store = getStore();
3515
- const task = await store.getById(tenantId, id);
3516
- if (!task) {
3517
- return reply.status(404).send({ success: false, error: "Task not found" });
3518
- }
3519
- if (task.ownerType === "user" && task.ownerId !== userId) {
3520
- return reply.status(403).send({ success: false, error: "Access denied" });
3521
- }
3522
- return { success: true, data: task };
3523
- } catch (error) {
3524
- return { success: false, error: error.message };
3525
- }
3526
- }
3527
- async function createTask(request, reply) {
3528
- try {
3529
- const tenantId = getTenantId9(request);
3530
- const userId = getUserId4(request);
3531
- const body = request.body;
3532
- if (!body.title) {
3533
- return reply.status(400).send({ success: false, error: "title is required" });
3534
- }
3535
- const store = getStore();
3536
- const task = await store.create({
3537
- ...body,
3538
- tenantId,
3539
- ownerType: body.ownerType || "user",
3540
- ownerId: body.ownerId || userId
3541
- });
3542
- return reply.status(201).send({ success: true, data: task });
3543
- } catch (error) {
3544
- return reply.status(500).send({ success: false, error: error.message });
3545
- }
3546
- }
3547
- async function updateTask(request, reply) {
3548
- try {
3549
- const tenantId = getTenantId9(request);
3550
- const userId = getUserId4(request);
3551
- const { id } = request.params;
3552
- const store = getStore();
3553
- const existing = await store.getById(tenantId, id);
3554
- if (!existing) {
3555
- return reply.status(404).send({ success: false, error: "Task not found" });
3556
- }
3557
- if (existing.ownerType === "user" && existing.ownerId !== userId) {
3558
- return reply.status(403).send({ success: false, error: "Access denied" });
3559
- }
3560
- const task = await store.update(tenantId, id, request.body);
3561
- if (!task) {
3562
- return reply.status(404).send({ success: false, error: "Task not found" });
3563
- }
3564
- return { success: true, data: task };
3565
- } catch (error) {
3566
- return reply.status(500).send({ success: false, error: error.message });
3567
- }
3568
- }
3569
- async function deleteTask(request, reply) {
3570
- try {
3571
- const tenantId = getTenantId9(request);
3572
- const userId = getUserId4(request);
3573
- const { id } = request.params;
3574
- const store = getStore();
3575
- const existing = await store.getById(tenantId, id);
3576
- if (!existing) {
3577
- return reply.status(404).send({ success: false, error: "Task not found" });
3578
- }
3579
- if (existing.ownerType === "user" && existing.ownerId !== userId) {
3580
- return reply.status(403).send({ success: false, error: "Access denied" });
3581
- }
3582
- const deleted = await store.delete(tenantId, id);
3583
- if (!deleted) {
3584
- return reply.status(404).send({ success: false, error: "Task not found" });
3585
- }
3586
- return { success: true, message: "Task deleted" };
3587
- } catch (error) {
3588
- return reply.status(500).send({ success: false, error: error.message });
3589
- }
3590
- }
3591
- async function completeTask(request, reply) {
3592
- try {
3593
- const tenantId = getTenantId9(request);
3594
- const userId = getUserId4(request);
3595
- const { id } = request.params;
3596
- const store = getStore();
3597
- const existing = await store.getById(tenantId, id);
3598
- if (!existing) {
3599
- return reply.status(404).send({ success: false, error: "Task not found" });
3600
- }
3601
- if (existing.ownerType === "user" && existing.ownerId !== userId) {
3602
- return reply.status(403).send({ success: false, error: "Access denied" });
3603
- }
3604
- const task = await store.update(tenantId, id, { status: "completed" });
3605
- if (!task) {
3606
- return reply.status(404).send({ success: false, error: "Task not found" });
3607
- }
3608
- return { success: true, data: task };
3609
- } catch (error) {
3610
- return reply.status(500).send({ success: false, error: error.message });
3611
- }
3612
- }
3613
- function getTaskWorkItemStore() {
3614
- return getStoreLattice6("default", "taskWorkItem").store;
3615
- }
3616
- async function listTaskWorkItems(request, reply) {
3617
- const tenantId = getTenantId9(request);
3618
- const { id } = request.params;
3619
- const { action, limit, offset } = request.query;
3620
- const store = getTaskWorkItemStore();
3621
- const items = await store.list({
3622
- tenantId,
3623
- taskId: id,
3624
- action,
3625
- limit: limit ? Number(limit) : void 0,
3626
- offset: offset ? Number(offset) : void 0
3627
- });
3628
- return reply.send({ success: true, data: items });
3629
- }
3630
-
3631
3482
  // src/services/ExportImportService.ts
3632
- import { ExportableEntityRegistry, DependencyResolver, IdRemapper } from "@axiom-lattice/core";
3483
+ import { ExportableEntityRegistry, DependencyResolver, IdRemapper, isBuiltInSkill } from "@axiom-lattice/core";
3633
3484
  import { randomUUID as randomUUID4 } from "crypto";
3485
+ import * as fs from "fs/promises";
3486
+ import * as os from "os";
3487
+ import * as path from "path";
3488
+ import * as tar from "tar";
3489
+ import * as zlib from "zlib";
3634
3490
  var exportJobs = /* @__PURE__ */ new Map();
3635
3491
  var JOB_TTL_MS = 60 * 60 * 1e3;
3492
+ var MAX_ARCHIVE_ENTRIES = 1e4;
3493
+ var MAX_ARCHIVE_FILE_BYTES = 100 * 1024 * 1024;
3494
+ var MAX_ARCHIVE_TOTAL_BYTES = 512 * 1024 * 1024;
3636
3495
  function cleanExpiredJobs() {
3637
3496
  const now = Date.now();
3638
3497
  for (const [id, entry] of exportJobs) {
@@ -3641,6 +3500,9 @@ function cleanExpiredJobs() {
3641
3500
  }
3642
3501
  }
3643
3502
  }
3503
+ function archiveFileKey(entityType, entityId, relPath) {
3504
+ return `files/${entityType}/${entityId}/${relPath}`;
3505
+ }
3644
3506
  var ExportImportService = class {
3645
3507
  constructor() {
3646
3508
  this.registry = ExportableEntityRegistry.getInstance();
@@ -3648,40 +3510,44 @@ var ExportImportService = class {
3648
3510
  /**
3649
3511
  * Preview an export: check which types would be auto-included, which dependencies are missing.
3650
3512
  */
3651
- async exportPreview(tenantId, selectedTypes) {
3513
+ async exportPreview(tenantId, selectedTypes, entityIds, context) {
3652
3514
  const allDefs = this.registry.getAll();
3653
3515
  const expanded = DependencyResolver.expandCascade(allDefs, selectedTypes);
3654
- const cascadeAdditions = expanded.filter((t) => !selectedTypes.includes(t));
3516
+ const cascadeAdditions = expanded.filter((t2) => !selectedTypes.includes(t2));
3655
3517
  const deps = DependencyResolver.computeDependencies(allDefs, expanded);
3656
3518
  const registeredTypes = new Set(allDefs.map((d) => d.entityType));
3657
- const missingDependencies = deps.missing.filter((t) => registeredTypes.has(t));
3519
+ const missingDependencies = deps.missing.filter((t2) => registeredTypes.has(t2));
3520
+ await this.resolveExportSelection(tenantId, selectedTypes, entityIds, context);
3658
3521
  return { missingDependencies, cascadeAdditions };
3659
3522
  }
3660
3523
  /**
3661
- * Generate a full ExportBundle for the given tenant and entity types.
3662
- * Stores the bundle in-memory and returns a jobId for download.
3524
+ * Generate a full export (bundle + file content) for the given tenant and entity types.
3525
+ * Stores the data in-memory and returns a jobId for download.
3663
3526
  */
3664
- async export(tenantId, entityTypes, entityIds) {
3665
- const allDefs = this.registry.getAll();
3666
- const expanded = DependencyResolver.expandCascade(allDefs, entityTypes);
3667
- const finalTypes = [.../* @__PURE__ */ new Set([...expanded, ...entityTypes])];
3668
- const dependencyOrder = DependencyResolver.resolveOrder(
3669
- finalTypes.map((t) => this.registry.get(t))
3527
+ async export(tenantId, entityTypes, entityIds, context) {
3528
+ const { dependencyOrder, entities: rawEntities } = await this.resolveExportSelection(
3529
+ tenantId,
3530
+ entityTypes,
3531
+ entityIds,
3532
+ context
3670
3533
  );
3671
3534
  const entities = {};
3535
+ const files = /* @__PURE__ */ new Map();
3672
3536
  let exportCounter = 0;
3673
3537
  for (const type of dependencyOrder) {
3674
3538
  const def = this.registry.get(type);
3675
- let raw = await def.listForExport(tenantId);
3676
- if (entityIds && entityIds[type] && entityIds[type].length > 0) {
3677
- const idSet = new Set(entityIds[type]);
3678
- raw = raw.filter((e) => idSet.has(e.data.id));
3679
- }
3539
+ const raw = rawEntities[type];
3680
3540
  entities[type] = raw.map((e) => ({
3681
3541
  ...e,
3682
3542
  _exportId: `exp-${type}-${String(++exportCounter).padStart(3, "0")}`,
3683
3543
  data: this.sanitizeEntityData(e.data)
3684
3544
  }));
3545
+ if (def.listFilesForExport && entities[type].length > 0) {
3546
+ const entityFiles = await def.listFilesForExport(tenantId, entities[type], context);
3547
+ for (const f of entityFiles) {
3548
+ files.set(archiveFileKey(type, f.entityId, f.path), f.content);
3549
+ }
3550
+ }
3685
3551
  }
3686
3552
  const rawIdToExportId = /* @__PURE__ */ new Map();
3687
3553
  for (const [, entityList] of Object.entries(entities)) {
@@ -3716,31 +3582,193 @@ var ExportImportService = class {
3716
3582
  _warning: "This file contains plaintext secrets (passwords, API keys, tokens). Store it securely and delete after import."
3717
3583
  };
3718
3584
  const jobId = randomUUID4();
3719
- exportJobs.set(jobId, { bundle, tenantId, createdAt: Date.now() });
3720
- return { jobId, bundle };
3585
+ exportJobs.set(jobId, { data: { bundle, files }, tenantId, createdAt: Date.now() });
3586
+ return { jobId, data: { bundle, files } };
3721
3587
  }
3722
3588
  /**
3723
- * Retrieve a previously generated export bundle by jobId with tenant validation.
3589
+ * Retrieve a previously generated export job data by jobId with tenant validation.
3724
3590
  */
3725
3591
  getExportJob(jobId, tenantId) {
3726
3592
  cleanExpiredJobs();
3727
3593
  const entry = exportJobs.get(jobId);
3728
3594
  if (!entry || entry.tenantId !== tenantId) return void 0;
3729
- return entry.bundle;
3595
+ return entry.data;
3596
+ }
3597
+ /**
3598
+ * Pack an ExportJobData into a tar.gz buffer.
3599
+ * Layout: bundle.json at root, files under files/{entityType}/{entityId}/{path}.
3600
+ */
3601
+ async packTarGz(data) {
3602
+ const { bundle, files } = data;
3603
+ const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "lattice-export-"));
3604
+ try {
3605
+ await fs.writeFile(path.join(tmpDir, "bundle.json"), JSON.stringify(bundle, null, 2), "utf-8");
3606
+ for (const [relPath, content] of files.entries()) {
3607
+ const fullPath = path.join(tmpDir, relPath);
3608
+ await fs.mkdir(path.dirname(fullPath), { recursive: true });
3609
+ await fs.writeFile(fullPath, content);
3610
+ }
3611
+ const stream = tar.c({ gzip: true, portable: true, cwd: tmpDir }, ["./bundle.json", ...Array.from(files.keys())]);
3612
+ const chunks = [];
3613
+ for await (const chunk of stream) {
3614
+ chunks.push(Buffer.from(chunk));
3615
+ }
3616
+ return Buffer.concat(chunks);
3617
+ } finally {
3618
+ await fs.rm(tmpDir, { recursive: true, force: true });
3619
+ }
3620
+ }
3621
+ /**
3622
+ * Unpack a tar.gz buffer into an ExportJobData (bundle + files).
3623
+ */
3624
+ async unpackTarGz(buffer) {
3625
+ const files = /* @__PURE__ */ new Map();
3626
+ let bundle;
3627
+ const gunzip = zlib.createGunzip();
3628
+ await new Promise((resolve2, reject) => {
3629
+ let settled = false;
3630
+ let entryCount = 0;
3631
+ let declaredTotalBytes = 0;
3632
+ let decompressedBytes = 0;
3633
+ let activeEntry;
3634
+ const paths = /* @__PURE__ */ new Set();
3635
+ const fail = (reason) => {
3636
+ if (settled) return;
3637
+ settled = true;
3638
+ const error = reason instanceof Error ? reason : new Error(String(reason));
3639
+ reject(error);
3640
+ try {
3641
+ activeEntry?.destroy(error);
3642
+ } catch {
3643
+ }
3644
+ try {
3645
+ parser.abort(error);
3646
+ } catch {
3647
+ }
3648
+ gunzip.destroy(error);
3649
+ };
3650
+ const countEntry = () => {
3651
+ entryCount += 1;
3652
+ if (entryCount <= MAX_ARCHIVE_ENTRIES) return true;
3653
+ fail(new Error(`Invalid export archive: exceeds ${MAX_ARCHIVE_ENTRIES.toLocaleString("en-US")} entries`));
3654
+ return false;
3655
+ };
3656
+ const parser = tar.t({
3657
+ gzip: false,
3658
+ onentry: (entry) => {
3659
+ activeEntry = entry;
3660
+ if (!countEntry()) return;
3661
+ entry.on("error", fail);
3662
+ entry.on("end", () => {
3663
+ if (activeEntry === entry) activeEntry = void 0;
3664
+ });
3665
+ const isDirectory = entry.type === "Directory";
3666
+ if (!isDirectory && entry.type !== "File" && entry.type !== "OldFile") {
3667
+ fail(new Error(`Invalid export archive: unsupported entry type "${entry.type}"`));
3668
+ return;
3669
+ }
3670
+ const rawPath = entry.path;
3671
+ if (rawPath.startsWith("/") || rawPath.includes("\\") || rawPath.includes("\0")) {
3672
+ fail(new Error(`Invalid export archive path: "${rawPath}"`));
3673
+ return;
3674
+ }
3675
+ const entryPath = rawPath.startsWith("./") ? rawPath.slice(2) : rawPath;
3676
+ const pathToValidate = isDirectory && entryPath.endsWith("/") ? entryPath.slice(0, -1) : entryPath;
3677
+ const segments = pathToValidate.split("/");
3678
+ if (segments.some((segment) => segment === "" || segment === "." || segment === "..") || (isDirectory ? !(pathToValidate === "files" || pathToValidate.startsWith("files/")) : entryPath !== "bundle.json" && !(entryPath.startsWith("files/") && segments.length > 1))) {
3679
+ fail(new Error(`Invalid export archive path: "${rawPath}"`));
3680
+ return;
3681
+ }
3682
+ if (isDirectory) {
3683
+ entry.resume();
3684
+ return;
3685
+ }
3686
+ if (paths.has(entryPath)) {
3687
+ fail(new Error(`Invalid export archive: duplicate path "${entryPath}"`));
3688
+ return;
3689
+ }
3690
+ paths.add(entryPath);
3691
+ if (!Number.isSafeInteger(entry.size) || entry.size < 0 || entry.size > MAX_ARCHIVE_FILE_BYTES) {
3692
+ fail(new Error("Invalid export archive: regular file size exceeds 100 MB"));
3693
+ return;
3694
+ }
3695
+ declaredTotalBytes += entry.size;
3696
+ if (declaredTotalBytes > MAX_ARCHIVE_TOTAL_BYTES) {
3697
+ fail(new Error("Invalid export archive: total uncompressed size exceeds 512 MB"));
3698
+ return;
3699
+ }
3700
+ const chunks = [];
3701
+ let streamedEntryBytes = 0;
3702
+ entry.on("data", (chunk) => {
3703
+ if (settled) return;
3704
+ streamedEntryBytes += chunk.length;
3705
+ if (streamedEntryBytes > MAX_ARCHIVE_FILE_BYTES) {
3706
+ fail(new Error("Invalid export archive: regular file size exceeds 100 MB"));
3707
+ return;
3708
+ }
3709
+ chunks.push(chunk);
3710
+ });
3711
+ entry.on("end", () => {
3712
+ if (settled) return;
3713
+ const content = Buffer.concat(chunks, streamedEntryBytes);
3714
+ if (entryPath === "bundle.json") {
3715
+ try {
3716
+ bundle = JSON.parse(content.toString("utf-8"));
3717
+ } catch (error) {
3718
+ fail(new Error(
3719
+ `Invalid export archive: malformed bundle.json: ${error instanceof Error ? error.message : String(error)}`
3720
+ ));
3721
+ }
3722
+ } else {
3723
+ files.set(entryPath, content);
3724
+ }
3725
+ });
3726
+ }
3727
+ });
3728
+ gunzip.on("data", (chunk) => {
3729
+ if (settled) return;
3730
+ decompressedBytes += chunk.length;
3731
+ if (decompressedBytes > MAX_ARCHIVE_TOTAL_BYTES) {
3732
+ fail(new Error("Invalid export archive: total uncompressed size exceeds 512 MB"));
3733
+ }
3734
+ });
3735
+ gunzip.on("error", fail);
3736
+ parser.on("ignoredEntry", (entry) => {
3737
+ if (!countEntry()) return;
3738
+ fail(new Error(`Invalid export archive: unsupported entry type "${entry.type}"`));
3739
+ });
3740
+ parser.on("meta", () => {
3741
+ if (!countEntry()) return;
3742
+ fail(new Error("Invalid export archive: unsupported metadata entry"));
3743
+ });
3744
+ parser.on("error", fail);
3745
+ parser.on("end", () => {
3746
+ if (settled) return;
3747
+ settled = true;
3748
+ resolve2();
3749
+ });
3750
+ gunzip.pipe(parser);
3751
+ gunzip.end(buffer);
3752
+ });
3753
+ if (!bundle) {
3754
+ throw new Error("Invalid export archive: missing bundle.json");
3755
+ }
3756
+ return { bundle, files };
3730
3757
  }
3731
3758
  /**
3732
3759
  * Preview import: validate bundle and detect conflicts.
3733
3760
  */
3734
- async previewImport(tenantId, bundle) {
3735
- this.validateBundle(bundle);
3761
+ async previewImport(tenantId, bundle, context) {
3762
+ const dependencyOrder = this.validateBundle(bundle);
3736
3763
  const allConflicts = [];
3737
3764
  const allInsertions = [];
3738
3765
  const allErrors = [];
3739
- for (const [entityType, entities] of Object.entries(bundle.entities)) {
3766
+ for (const entityType of dependencyOrder) {
3767
+ const entities = bundle.entities[entityType];
3740
3768
  if (entities.length === 0) continue;
3741
3769
  try {
3742
3770
  const def = this.registry.get(entityType);
3743
- const result = await def.previewImport(tenantId, entities);
3771
+ const result = await def.previewImport(tenantId, entities, context);
3744
3772
  allConflicts.push(...result.conflicts);
3745
3773
  allInsertions.push(...result.insertions);
3746
3774
  allErrors.push(...result.errors);
@@ -3758,25 +3786,132 @@ var ExportImportService = class {
3758
3786
  }
3759
3787
  /**
3760
3788
  * Apply import: create entities in dependency order with ID remapping.
3789
+ * File content (if any) is passed to registrations via the `files` map.
3761
3790
  */
3762
- async applyImport(tenantId, bundle, resolutions) {
3763
- this.validateBundle(bundle);
3791
+ async applyImport(tenantId, bundle, resolutions, files, context) {
3792
+ const dependencyOrder = this.validateBundle(bundle);
3764
3793
  const results = [];
3765
3794
  const idMap = {};
3795
+ const unresolvedExportIds = /* @__PURE__ */ new Set();
3766
3796
  const skillIdRemap = {};
3767
- for (const entityType of bundle.dependencyOrder) {
3797
+ const skillSourceToTarget = /* @__PURE__ */ new Map();
3798
+ const unresolvedSkillReferences = /* @__PURE__ */ new Map();
3799
+ const skillEntities = bundle.entities.skill ?? [];
3800
+ const skillConflicts = skillEntities.length > 0 ? await this.registry.get("skill").previewImport(tenantId, skillEntities, context) : { conflicts: [] };
3801
+ const skillConflictByExportId = new Map(
3802
+ skillConflicts.conflicts.map((conflict) => [conflict._exportId, conflict])
3803
+ );
3804
+ for (const entity of skillEntities) {
3805
+ const resolution = resolutions[entity._exportId];
3806
+ const sourceId = entity.data.id;
3807
+ const targetId = resolution?.action === "skip" ? skillConflictByExportId.get(entity._exportId)?.existingId : resolution?.action === "rename" && resolution.newId ? resolution.newId : sourceId;
3808
+ if (targetId) skillSourceToTarget.set(sourceId, targetId);
3809
+ }
3810
+ for (const entity of skillEntities) {
3811
+ const subSkills = entity.data.subSkills;
3812
+ if (!Array.isArray(subSkills)) continue;
3813
+ const unresolved = subSkills.find((id) => typeof id === "string" && !skillSourceToTarget.has(id) && !isBuiltInSkill(id));
3814
+ if (unresolved) unresolvedSkillReferences.set(entity._exportId, unresolved);
3815
+ }
3816
+ for (const entityType of dependencyOrder) {
3768
3817
  const entities = bundle.entities[entityType];
3769
3818
  if (!entities || entities.length === 0) continue;
3770
3819
  const def = this.registry.get(entityType);
3771
3820
  const remapper = new IdRemapper(idMap);
3821
+ let insertions = /* @__PURE__ */ new Set();
3822
+ let conflicts = /* @__PURE__ */ new Set();
3823
+ let conflictByExportId = /* @__PURE__ */ new Map();
3824
+ let previewError;
3825
+ try {
3826
+ const preview = await def.previewImport(tenantId, entities, context);
3827
+ insertions = new Set(preview.insertions.map((item) => item._exportId));
3828
+ conflicts = new Set(preview.conflicts.map((item) => item._exportId));
3829
+ conflictByExportId = new Map(preview.conflicts.map((item) => [item._exportId, item]));
3830
+ } catch (err) {
3831
+ previewError = err.message;
3832
+ }
3772
3833
  for (const entity of entities) {
3773
- const resolution = resolutions[entity._exportId];
3834
+ let resolution = resolutions[entity._exportId];
3774
3835
  if (resolution?.action === "skip") {
3775
- results.push({ _exportId: entity._exportId, entityType, status: "skipped" });
3836
+ const conflict = conflictByExportId.get(entity._exportId);
3837
+ if (conflict && !conflict.existingId) {
3838
+ unresolvedExportIds.add(entity._exportId);
3839
+ results.push({
3840
+ _exportId: entity._exportId,
3841
+ entityType,
3842
+ status: "failed",
3843
+ error: "Cannot skip conflict without an existing ID"
3844
+ });
3845
+ continue;
3846
+ }
3847
+ if (conflict?.existingId) {
3848
+ idMap[entity._exportId] = conflict.existingId;
3849
+ }
3850
+ results.push({
3851
+ _exportId: entity._exportId,
3852
+ entityType,
3853
+ status: "skipped",
3854
+ newId: conflict?.existingId
3855
+ });
3856
+ continue;
3857
+ }
3858
+ const pendingValues = [entity.data];
3859
+ let unresolvedReference;
3860
+ while (pendingValues.length > 0 && !unresolvedReference) {
3861
+ const value = pendingValues.pop();
3862
+ if (typeof value === "string") {
3863
+ const match = value.match(/^@\w+\/(.+)$/);
3864
+ if (match && unresolvedExportIds.has(match[1])) {
3865
+ unresolvedReference = match[1];
3866
+ }
3867
+ } else if (Array.isArray(value)) {
3868
+ pendingValues.push(...value);
3869
+ } else if (value && typeof value === "object") {
3870
+ pendingValues.push(...Object.values(value));
3871
+ }
3872
+ }
3873
+ if (unresolvedReference) {
3874
+ unresolvedExportIds.add(entity._exportId);
3875
+ results.push({
3876
+ _exportId: entity._exportId,
3877
+ entityType,
3878
+ status: "failed",
3879
+ error: `Unresolved reference to failed import entity "${unresolvedReference}"`
3880
+ });
3776
3881
  continue;
3777
3882
  }
3883
+ if (!resolution) {
3884
+ if (previewError || conflicts.has(entity._exportId) || !insertions.has(entity._exportId)) {
3885
+ results.push({
3886
+ _exportId: entity._exportId,
3887
+ entityType,
3888
+ status: "failed",
3889
+ error: previewError ? `Unable to verify missing conflict resolution: ${previewError}` : "Missing conflict resolution"
3890
+ });
3891
+ continue;
3892
+ }
3893
+ resolution = {
3894
+ _exportId: entity._exportId,
3895
+ action: "insert"
3896
+ };
3897
+ }
3778
3898
  try {
3779
3899
  const remappedData = remapper.remapReferences(entity.data);
3900
+ if (entityType === "skill") {
3901
+ const unresolvedSkill = unresolvedSkillReferences.get(entity._exportId);
3902
+ if (unresolvedSkill) {
3903
+ results.push({
3904
+ _exportId: entity._exportId,
3905
+ entityType,
3906
+ status: "failed",
3907
+ error: `Unresolved subSkill reference "${unresolvedSkill}"`
3908
+ });
3909
+ continue;
3910
+ }
3911
+ if (Array.isArray(remappedData.subSkills)) {
3912
+ remappedData.subSkills = remappedData.subSkills.map((id) => typeof id === "string" ? skillSourceToTarget.get(id) ?? id : id);
3913
+ }
3914
+ }
3780
3915
  if (entityType === "agent" && Object.keys(skillIdRemap).length > 0) {
3781
3916
  const skillRemapper = new IdRemapper({});
3782
3917
  remappedData.graphDefinition = skillRemapper.remapSkillIds(
@@ -3784,19 +3919,26 @@ var ExportImportService = class {
3784
3919
  skillIdRemap
3785
3920
  );
3786
3921
  }
3787
- if (resolution?.action === "rename" && resolution.newId) {
3922
+ if (resolution.action === "rename" && resolution.newId) {
3788
3923
  remappedData.id = resolution.newId;
3789
3924
  }
3925
+ const entityFiles = this.extractEntityFiles(
3926
+ entityType,
3927
+ entity.data.id,
3928
+ files
3929
+ );
3790
3930
  const entityWithRemappedData = { ...entity, data: remappedData };
3791
3931
  const { newId } = await def.applyImport(
3792
3932
  tenantId,
3793
3933
  entityWithRemappedData,
3794
- resolution ?? { _exportId: entity._exportId, action: "overwrite" }
3934
+ resolution,
3935
+ entityFiles,
3936
+ context
3795
3937
  );
3796
3938
  if (newId) {
3797
3939
  idMap[entity._exportId] = newId;
3798
3940
  if (entityType === "skill" && newId) {
3799
- const oldSkillId = remappedData.id;
3941
+ const oldSkillId = entity.data.id;
3800
3942
  if (oldSkillId !== newId) {
3801
3943
  skillIdRemap[oldSkillId] = newId;
3802
3944
  }
@@ -3804,7 +3946,7 @@ var ExportImportService = class {
3804
3946
  results.push({
3805
3947
  _exportId: entity._exportId,
3806
3948
  entityType,
3807
- status: resolution?.action === "overwrite" ? "updated" : "created",
3949
+ status: resolution.action === "overwrite" ? "updated" : "created",
3808
3950
  newId
3809
3951
  });
3810
3952
  }
@@ -3826,43 +3968,292 @@ var ExportImportService = class {
3826
3968
  listExportableTypes() {
3827
3969
  return this.registry.listTypes();
3828
3970
  }
3829
- validateBundle(bundle) {
3830
- if (bundle.version !== 1) {
3831
- throw new Error(`Unsupported bundle version: ${bundle.version}. Expected: 1`);
3832
- }
3833
- if (!bundle.entities || typeof bundle.entities !== "object") {
3834
- throw new Error("Invalid bundle: missing entities");
3835
- }
3836
- if (!Array.isArray(bundle.dependencyOrder)) {
3837
- throw new Error("Invalid bundle: missing dependencyOrder");
3971
+ /**
3972
+ * Extract file content for a single entity from the archive files map.
3973
+ * Keys are `files/{entityType}/{entityId}/{path}`; returns a Map of relative path → Buffer.
3974
+ */
3975
+ extractEntityFiles(entityType, entityId, files) {
3976
+ if (!files || files.size === 0) return void 0;
3977
+ const prefix = `files/${entityType}/${entityId}/`;
3978
+ const result = /* @__PURE__ */ new Map();
3979
+ for (const [path4, content] of files.entries()) {
3980
+ if (path4.startsWith(prefix)) {
3981
+ result.set(path4.slice(prefix.length), content);
3982
+ }
3838
3983
  }
3984
+ return result.size > 0 ? result : void 0;
3839
3985
  }
3840
- sanitizeEntityData(data) {
3841
- const sanitized = { ...data };
3842
- delete sanitized.tenantId;
3843
- delete sanitized.createdAt;
3844
- delete sanitized.updatedAt;
3845
- return sanitized;
3846
- }
3847
- };
3986
+ async resolveExportSelection(tenantId, entityTypes, entityIds, context) {
3987
+ const allDefs = this.registry.getAll();
3988
+ const registeredTypes = new Set(allDefs.map((def) => def.entityType));
3989
+ for (const type of entityTypes) {
3990
+ if (!registeredTypes.has(type)) {
3991
+ throw new Error(`Exportable entity type is not registered: "${type}"`);
3992
+ }
3993
+ }
3994
+ const expanded = DependencyResolver.expandCascade(allDefs, entityTypes);
3995
+ const dependencies = DependencyResolver.computeDependencies(allDefs, expanded).missing;
3996
+ const finalTypeSet = /* @__PURE__ */ new Set([...expanded, ...dependencies]);
3997
+ const dependencyOrder = DependencyResolver.resolveOrder(
3998
+ allDefs.filter((def) => finalTypeSet.has(def.entityType))
3999
+ );
4000
+ const availableByType = /* @__PURE__ */ new Map();
4001
+ const selectedIds = /* @__PURE__ */ new Map();
4002
+ const processedIds = /* @__PURE__ */ new Map();
4003
+ for (const type of dependencyOrder) {
4004
+ const raw = await this.registry.get(type).listForExport(tenantId, context);
4005
+ availableByType.set(type, new Map(raw.map((entity) => [entity.data.id, entity])));
4006
+ if (entityIds && Object.prototype.hasOwnProperty.call(entityIds, type)) {
4007
+ selectedIds.set(type, new Set(entityIds[type]));
4008
+ } else if (expanded.includes(type)) {
4009
+ selectedIds.set(type, new Set(raw.map((entity) => entity.data.id)));
4010
+ } else {
4011
+ selectedIds.set(type, /* @__PURE__ */ new Set());
4012
+ }
4013
+ processedIds.set(type, /* @__PURE__ */ new Set());
4014
+ }
4015
+ let referencesAdded = true;
4016
+ while (referencesAdded) {
4017
+ referencesAdded = false;
4018
+ for (const type of dependencyOrder) {
4019
+ const def = this.registry.get(type);
4020
+ if (!def.getReferencedEntityIds) continue;
4021
+ const processed = processedIds.get(type);
4022
+ const available = availableByType.get(type);
4023
+ const unprocessed = [...selectedIds.get(type)].filter((id) => !processed.has(id)).map((id) => available.get(id)).filter((entity) => entity !== void 0);
4024
+ if (unprocessed.length === 0) continue;
4025
+ for (const entity of unprocessed) processed.add(entity.data.id);
4026
+ const references = await def.getReferencedEntityIds(tenantId, unprocessed, context);
4027
+ for (const [referencedType, ids] of Object.entries(references)) {
4028
+ if (!finalTypeSet.has(referencedType)) {
4029
+ throw new Error(
4030
+ `Unresolved reference from ${type}: entity type "${referencedType}" is not in its registered dependency closure`
4031
+ );
4032
+ }
4033
+ const targetSelection = selectedIds.get(referencedType);
4034
+ const targetEntities = availableByType.get(referencedType);
4035
+ const explicitSelection = entityIds && Object.prototype.hasOwnProperty.call(entityIds, referencedType) ? new Set(entityIds[referencedType]) : void 0;
4036
+ for (const id of new Set(ids)) {
4037
+ if (referencedType === "skill" && isBuiltInSkill(id)) continue;
4038
+ if (explicitSelection && !explicitSelection.has(id)) {
4039
+ throw new Error(
4040
+ `Required reference from ${type} to ${referencedType} "${id}" is omitted by entityIds.${referencedType}`
4041
+ );
4042
+ }
4043
+ if (!targetEntities.has(id)) {
4044
+ throw new Error(`Unresolved reference from ${type} to ${referencedType} "${id}"`);
4045
+ }
4046
+ if (!targetSelection.has(id)) {
4047
+ targetSelection.add(id);
4048
+ referencesAdded = true;
4049
+ }
4050
+ }
4051
+ }
4052
+ }
4053
+ }
4054
+ const entities = {};
4055
+ for (const type of dependencyOrder) {
4056
+ const ids = selectedIds.get(type);
4057
+ entities[type] = [...availableByType.get(type).values()].filter((entity) => ids.has(entity.data.id));
4058
+ }
4059
+ return { dependencyOrder, entities };
4060
+ }
4061
+ validateBundle(bundle) {
4062
+ if (bundle.version !== 1) {
4063
+ throw new Error(`Unsupported bundle version: ${bundle.version}. Expected: 1`);
4064
+ }
4065
+ if (!bundle.entities || typeof bundle.entities !== "object" || Array.isArray(bundle.entities)) {
4066
+ throw new Error("Invalid bundle: missing entities");
4067
+ }
4068
+ if (!Array.isArray(bundle.dependencyOrder)) {
4069
+ throw new Error("Invalid bundle: missing dependencyOrder");
4070
+ }
4071
+ const orderSet = new Set(bundle.dependencyOrder);
4072
+ if (orderSet.size !== bundle.dependencyOrder.length) {
4073
+ throw new Error("Invalid bundle: duplicate type in dependencyOrder");
4074
+ }
4075
+ const entityTypes = Object.keys(bundle.entities);
4076
+ for (const entityType of entityTypes) {
4077
+ if (!Array.isArray(bundle.entities[entityType])) {
4078
+ throw new Error(`Invalid bundle: entities.${entityType} must be an array`);
4079
+ }
4080
+ if (!orderSet.has(entityType)) {
4081
+ throw new Error(`Invalid bundle: dependencyOrder omits entities type "${entityType}"`);
4082
+ }
4083
+ }
4084
+ for (const entityType of bundle.dependencyOrder) {
4085
+ if (!Object.prototype.hasOwnProperty.call(bundle.entities, entityType)) {
4086
+ throw new Error(`Invalid bundle: missing entities key for dependencyOrder type "${entityType}"`);
4087
+ }
4088
+ const def = this.registry.getAll().find((candidate) => candidate.entityType === entityType);
4089
+ if (!def) {
4090
+ throw new Error(`Invalid bundle: unregistered entity type "${entityType}"`);
4091
+ }
4092
+ for (const dependency of def.dependsOn) {
4093
+ const dependencyRegistered = this.registry.getAll().some((candidate) => candidate.entityType === dependency);
4094
+ if (dependencyRegistered && !orderSet.has(dependency)) {
4095
+ throw new Error(
4096
+ `Invalid bundle: missing registered dependency "${dependency}" required by "${entityType}"`
4097
+ );
4098
+ }
4099
+ }
4100
+ }
4101
+ const exportIds = /* @__PURE__ */ new Set();
4102
+ for (const entityType of entityTypes) {
4103
+ const ids = /* @__PURE__ */ new Set();
4104
+ for (const entity of bundle.entities[entityType]) {
4105
+ if (!entity || typeof entity !== "object" || typeof entity._exportId !== "string") {
4106
+ throw new Error(`Invalid bundle: malformed entity in "${entityType}"`);
4107
+ }
4108
+ if (exportIds.has(entity._exportId)) {
4109
+ throw new Error(`Invalid bundle: duplicate _exportId "${entity._exportId}"`);
4110
+ }
4111
+ exportIds.add(entity._exportId);
4112
+ const id = entity.data?.id;
4113
+ if (typeof id !== "string") {
4114
+ throw new Error(`Invalid bundle: entity ID in "${entityType}" must be a string`);
4115
+ }
4116
+ if (ids.has(id)) {
4117
+ throw new Error(`Invalid bundle: duplicate entity ID in "${entityType}": "${id}"`);
4118
+ }
4119
+ ids.add(id);
4120
+ }
4121
+ }
4122
+ const typeSet = new Set(entityTypes);
4123
+ const canonicalOrder = DependencyResolver.resolveOrder(
4124
+ this.registry.getAll().filter((def) => typeSet.has(def.entityType))
4125
+ );
4126
+ if (canonicalOrder.length !== bundle.dependencyOrder.length || canonicalOrder.some((type, index) => bundle.dependencyOrder[index] !== type)) {
4127
+ throw new Error(
4128
+ `Invalid bundle: dependencyOrder is not canonical; expected ${canonicalOrder.join(", ")}`
4129
+ );
4130
+ }
4131
+ return canonicalOrder;
4132
+ }
4133
+ sanitizeEntityData(data) {
4134
+ const sanitized = { ...data };
4135
+ delete sanitized.tenantId;
4136
+ delete sanitized.createdAt;
4137
+ delete sanitized.updatedAt;
4138
+ return sanitized;
4139
+ }
4140
+ };
3848
4141
 
3849
4142
  // src/controllers/export-import.ts
3850
4143
  import { ExportableEntityRegistry as ExportableEntityRegistry2 } from "@axiom-lattice/core";
3851
4144
  var service = new ExportImportService();
3852
- function getTenantId10(request) {
3853
- return request.user?.tenantId || request.headers["x-tenant-id"] || "default";
4145
+ var TenantScopeError = class extends Error {
4146
+ constructor() {
4147
+ super(...arguments);
4148
+ this.statusCode = 403;
4149
+ }
4150
+ };
4151
+ function resolveTenantId(request, routeTenantId) {
4152
+ const authenticatedTenantId = request.user?.tenantId;
4153
+ const header = request.headers["x-tenant-id"];
4154
+ const headerTenantId = Array.isArray(header) ? header[0] : header;
4155
+ if (authenticatedTenantId) {
4156
+ if (headerTenantId && headerTenantId !== authenticatedTenantId) {
4157
+ throw new TenantScopeError(
4158
+ `Tenant scope mismatch: header tenant "${headerTenantId}" does not match authenticated tenant "${authenticatedTenantId}"`
4159
+ );
4160
+ }
4161
+ if (routeTenantId && routeTenantId !== authenticatedTenantId) {
4162
+ throw new TenantScopeError(
4163
+ `Tenant scope mismatch: route tenant "${routeTenantId}" does not match authenticated tenant "${authenticatedTenantId}"`
4164
+ );
4165
+ }
4166
+ return authenticatedTenantId;
4167
+ }
4168
+ return headerTenantId || routeTenantId || "default";
4169
+ }
4170
+ function getContext(request) {
4171
+ return {
4172
+ workspaceId: request.headers["x-workspace-id"] || void 0,
4173
+ projectId: request.headers["x-project-id"] || void 0
4174
+ };
4175
+ }
4176
+ function safeArchiveFilename(sourceTenantId) {
4177
+ const safeTenantId = sourceTenantId.replace(/[^A-Za-z0-9._-]/g, "_");
4178
+ return `tenant-export-${safeTenantId}.tar.gz`;
4179
+ }
4180
+ function assertCompleteFile(file) {
4181
+ if (file.truncated) {
4182
+ throw new Error("Uploaded file was truncated because it exceeds the configured file size limit");
4183
+ }
4184
+ }
4185
+ function parseResolutions(value) {
4186
+ let parsed = value;
4187
+ if (typeof value === "string") {
4188
+ try {
4189
+ parsed = JSON.parse(value);
4190
+ } catch {
4191
+ throw new Error("resolutions must be valid JSON");
4192
+ }
4193
+ }
4194
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
4195
+ throw new Error("resolutions must be a JSON object");
4196
+ }
4197
+ return parsed;
4198
+ }
4199
+ function requireJsonBundle(request, body) {
4200
+ const contentType = request.headers["content-type"]?.split(";", 1)[0]?.trim().toLowerCase();
4201
+ if (contentType !== "application/json" || body.bundle === void 0) {
4202
+ throw new Error('Provide a multipart tar.gz file or an application/json body with "bundle"');
4203
+ }
4204
+ return body.bundle;
4205
+ }
4206
+ async function readPreviewArchive(request) {
4207
+ const part = await request.file();
4208
+ if (!part || part.fieldname !== "file") {
4209
+ throw new Error('Multipart import requires a tar.gz file field named "file"');
4210
+ }
4211
+ const archive = await part.toBuffer();
4212
+ assertCompleteFile(part.file);
4213
+ return archive;
4214
+ }
4215
+ async function readApplyMultipart(request) {
4216
+ let archive;
4217
+ let resolutionsValue;
4218
+ for await (const part of request.parts()) {
4219
+ if (part.type === "file") {
4220
+ if (part.fieldname !== "file" || archive !== void 0) {
4221
+ part.file.resume();
4222
+ throw new Error('Multipart import requires exactly one tar.gz file field named "file"');
4223
+ }
4224
+ archive = await part.toBuffer();
4225
+ assertCompleteFile(part.file);
4226
+ } else if (part.fieldname === "resolutions") {
4227
+ if (resolutionsValue !== void 0) {
4228
+ throw new Error('Multipart import requires exactly one "resolutions" field');
4229
+ }
4230
+ resolutionsValue = part.value;
4231
+ }
4232
+ }
4233
+ if (!archive) {
4234
+ throw new Error('Multipart import requires a tar.gz file field named "file"');
4235
+ }
4236
+ if (resolutionsValue === void 0) {
4237
+ throw new Error('Multipart import requires a "resolutions" JSON field');
4238
+ }
4239
+ return { archive, resolutions: parseResolutions(resolutionsValue) };
3854
4240
  }
3855
4241
  async function getExportableTypes(_request, reply) {
3856
4242
  const types = service.listExportableTypes();
3857
4243
  return reply.send({ success: true, data: types });
3858
4244
  }
3859
4245
  async function exportConfig(request, reply) {
3860
- const tenantId = getTenantId10(request) || request.params.tenantId;
4246
+ const tenantId = resolveTenantId(request, request.params.tenantId);
3861
4247
  const { entityTypes, entityIds } = request.body;
3862
4248
  if (!Array.isArray(entityTypes) || entityTypes.length === 0) {
3863
4249
  return reply.status(400).send({ success: false, message: "entityTypes must be a non-empty array" });
3864
4250
  }
3865
- const preview = await service.exportPreview(tenantId, entityTypes);
4251
+ const preview = await service.exportPreview(
4252
+ tenantId,
4253
+ entityTypes,
4254
+ entityIds,
4255
+ getContext(request)
4256
+ );
3866
4257
  if (preview.missingDependencies.length > 0 || preview.cascadeAdditions.length > 0) {
3867
4258
  return reply.send({
3868
4259
  success: true,
@@ -3873,40 +4264,41 @@ async function exportConfig(request, reply) {
3873
4264
  }
3874
4265
  });
3875
4266
  }
3876
- const { jobId, bundle } = await service.export(tenantId, entityTypes, entityIds);
4267
+ const { jobId, data } = await service.export(tenantId, entityTypes, entityIds, getContext(request));
3877
4268
  return reply.send({
3878
4269
  success: true,
3879
4270
  data: {
3880
4271
  jobId,
3881
4272
  summary: Object.fromEntries(
3882
- Object.entries(bundle.entities).map(([type, entities]) => [type, entities.length])
4273
+ Object.entries(data.bundle.entities).map(([type, entities]) => [type, entities.length])
3883
4274
  )
3884
4275
  }
3885
4276
  });
3886
4277
  }
3887
4278
  async function exportConfigConfirm(request, reply) {
3888
- const tenantId = getTenantId10(request) || request.params.tenantId;
4279
+ const tenantId = resolveTenantId(request, request.params.tenantId);
3889
4280
  const { entityTypes, entityIds } = request.body;
3890
- const { jobId, bundle } = await service.export(tenantId, entityTypes, entityIds);
4281
+ const { jobId, data } = await service.export(tenantId, entityTypes, entityIds, getContext(request));
3891
4282
  return reply.send({
3892
4283
  success: true,
3893
4284
  data: {
3894
4285
  jobId,
3895
4286
  summary: Object.fromEntries(
3896
- Object.entries(bundle.entities).map(([type, entities]) => [type, entities.length])
4287
+ Object.entries(data.bundle.entities).map(([type, entities]) => [type, entities.length])
3897
4288
  )
3898
4289
  }
3899
4290
  });
3900
4291
  }
3901
4292
  async function previewEntities(request, reply) {
3902
- const tenantId = getTenantId10(request) || request.params.tenantId;
4293
+ const tenantId = resolveTenantId(request, request.params.tenantId);
3903
4294
  const { entityTypes } = request.body;
4295
+ const context = getContext(request);
3904
4296
  try {
3905
4297
  const registry = ExportableEntityRegistry2.getInstance();
3906
4298
  const result = {};
3907
4299
  for (const type of entityTypes) {
3908
4300
  const def = registry.get(type);
3909
- const entities = await def.listForExport(tenantId);
4301
+ const entities = await def.listForExport(tenantId, context);
3910
4302
  result[type] = entities.map((e) => ({
3911
4303
  id: e.data.id,
3912
4304
  name: e.data.name || e.data.id,
@@ -3919,21 +4311,22 @@ async function previewEntities(request, reply) {
3919
4311
  }
3920
4312
  }
3921
4313
  async function downloadExport(request, reply) {
3922
- const tenantId = getTenantId10(request) || request.params.tenantId;
3923
- const bundle = service.getExportJob(request.params.jobId, tenantId);
3924
- if (!bundle) {
4314
+ const tenantId = resolveTenantId(request, request.params.tenantId);
4315
+ const data = service.getExportJob(request.params.jobId, tenantId);
4316
+ if (!data) {
3925
4317
  return reply.status(404).send({ success: false, message: "Export job not found or expired" });
3926
4318
  }
3927
- return reply.header("Content-Type", "application/json").header("Content-Disposition", `attachment; filename="tenant-export-${bundle.sourceTenantId}.json"`).send({ success: true, data: bundle });
4319
+ const tarGz = await service.packTarGz(data);
4320
+ const filename = safeArchiveFilename(data.bundle.sourceTenantId);
4321
+ reply.type("application/gzip");
4322
+ reply.header("Content-Disposition", `attachment; filename="${filename}"`);
4323
+ return reply.send(tarGz);
3928
4324
  }
3929
4325
  async function importPreview(request, reply) {
3930
- const tenantId = getTenantId10(request) || request.params.tenantId;
3931
- const { bundle } = request.body;
3932
- if (!bundle) {
3933
- return reply.status(400).send({ success: false, message: "No bundle provided in request body" });
3934
- }
4326
+ const tenantId = resolveTenantId(request, request.params.tenantId);
3935
4327
  try {
3936
- const preview = await service.previewImport(tenantId, bundle);
4328
+ const data = request.isMultipart() ? await service.unpackTarGz(await readPreviewArchive(request)) : { bundle: requireJsonBundle(request, request.body), files: void 0 };
4329
+ const preview = await service.previewImport(tenantId, data.bundle, getContext(request));
3937
4330
  return reply.send({ success: true, data: preview });
3938
4331
  } catch (err) {
3939
4332
  return reply.status(400).send({
@@ -3943,10 +4336,12 @@ async function importPreview(request, reply) {
3943
4336
  }
3944
4337
  }
3945
4338
  async function importApply(request, reply) {
3946
- const tenantId = getTenantId10(request) || request.params.tenantId;
4339
+ const tenantId = resolveTenantId(request, request.params.tenantId);
3947
4340
  try {
3948
- const { bundle, resolutions } = request.body;
3949
- const result = await service.applyImport(tenantId, bundle, resolutions);
4341
+ const multipart2 = request.isMultipart() ? await readApplyMultipart(request) : void 0;
4342
+ const data = multipart2 ? await service.unpackTarGz(multipart2.archive) : { bundle: requireJsonBundle(request, request.body), files: void 0 };
4343
+ const resolutions = multipart2?.resolutions ?? parseResolutions(request.body.resolutions);
4344
+ const result = await service.applyImport(tenantId, data.bundle, resolutions, data.files, getContext(request));
3950
4345
  return reply.send({ success: true, data: result });
3951
4346
  } catch (err) {
3952
4347
  return reply.status(400).send({
@@ -4632,43 +5027,15 @@ function registerSandboxProxyRoutes(app2) {
4632
5027
  // src/controllers/workspace.ts
4633
5028
  import * as fs2 from "fs/promises";
4634
5029
  import * as path2 from "path";
4635
- import * as os from "os";
4636
- import { getStoreLattice as getStoreLattice7 } from "@axiom-lattice/core";
5030
+ import * as os2 from "os";
5031
+ import { getStoreLattice as getStoreLattice6 } from "@axiom-lattice/core";
4637
5032
  import { SandboxFilesystem, FilesystemBackend } from "@axiom-lattice/core";
4638
5033
  import { getSandBoxManager as getSandBoxManager3 } from "@axiom-lattice/core";
4639
5034
  import { v4 as uuidv42 } from "uuid";
4640
-
4641
- // src/utils/path-containment.ts
4642
- import * as fs from "fs/promises";
4643
- import * as path from "path";
4644
- var PathOutsideRootError = class extends Error {
4645
- constructor(rootPath, targetPath) {
4646
- super(`Path is outside the allowed root: ${targetPath}`);
4647
- this.rootPath = rootPath;
4648
- this.targetPath = targetPath;
4649
- this.name = "PathOutsideRootError";
4650
- }
4651
- };
4652
- function isPathWithinRoot(rootPath, targetPath) {
4653
- const relative2 = path.relative(rootPath, targetPath);
4654
- return relative2 === "" || relative2 !== ".." && !relative2.startsWith(`..${path.sep}`) && !path.isAbsolute(relative2);
4655
- }
4656
- async function resolvePathWithinRoot(rootPath, targetPath) {
4657
- const [resolvedRoot, resolvedTarget] = await Promise.all([
4658
- fs.realpath(rootPath),
4659
- fs.realpath(targetPath)
4660
- ]);
4661
- if (!isPathWithinRoot(resolvedRoot, resolvedTarget)) {
4662
- throw new PathOutsideRootError(resolvedRoot, resolvedTarget);
4663
- }
4664
- return { rootPath: resolvedRoot, targetPath: resolvedTarget };
4665
- }
4666
-
4667
- // src/controllers/workspace.ts
4668
5035
  var WorkspaceController = class {
4669
5036
  constructor() {
4670
- this.workspaceStore = getStoreLattice7("default", "workspace").store;
4671
- this.projectStore = getStoreLattice7("default", "project").store;
5037
+ this.workspaceStore = getStoreLattice6("default", "workspace").store;
5038
+ this.projectStore = getStoreLattice6("default", "project").store;
4672
5039
  }
4673
5040
  getTenantId(request) {
4674
5041
  const userTenantId = request.user?.tenantId;
@@ -4740,11 +5107,33 @@ var WorkspaceController = class {
4740
5107
  async listProjects(request, reply) {
4741
5108
  const tenantId = this.getTenantId(request);
4742
5109
  const { workspaceId } = request.params;
4743
- const projects = await this.projectStore.getProjectsByWorkspace(
5110
+ const kindFilter = request.query.kind;
5111
+ await this.ensureTrainingProject(tenantId, workspaceId);
5112
+ const projects = kindFilter ? await this.projectStore.getProjectsByWorkspace(tenantId, workspaceId, {
5113
+ kind: kindFilter
5114
+ }) : await this.projectStore.getProjectsByWorkspace(tenantId, workspaceId);
5115
+ return { success: true, data: projects };
5116
+ }
5117
+ /**
5118
+ * Ensure at least one training project exists per tenant/workspace.
5119
+ *
5120
+ * Multiple training projects are allowed; this only creates the first one
5121
+ * when none exist so the frontend always has a default to select.
5122
+ */
5123
+ async ensureTrainingProject(tenantId, workspaceId) {
5124
+ const existing = await this.projectStore.getProjectsByWorkspace(
4744
5125
  tenantId,
4745
- workspaceId
5126
+ workspaceId,
5127
+ { kind: "training" }
4746
5128
  );
4747
- return { success: true, data: projects };
5129
+ if (existing.length > 0) {
5130
+ return;
5131
+ }
5132
+ await this.projectStore.createProject(tenantId, workspaceId, uuidv42(), {
5133
+ name: "Training",
5134
+ description: "Shared workspace for agent training",
5135
+ kind: "training"
5136
+ });
4748
5137
  }
4749
5138
  async createProject(request, reply) {
4750
5139
  const tenantId = this.getTenantId(request);
@@ -4784,7 +5173,24 @@ var WorkspaceController = class {
4784
5173
  }
4785
5174
  async deleteProject(request, reply) {
4786
5175
  const tenantId = this.getTenantId(request);
4787
- const { projectId } = request.params;
5176
+ const { workspaceId, projectId } = request.params;
5177
+ const project = await this.projectStore.getProjectById(tenantId, projectId);
5178
+ if (!project) {
5179
+ return reply.status(404).send({ success: false, error: "Project not found" });
5180
+ }
5181
+ if (project.kind === "training") {
5182
+ const training = await this.projectStore.getProjectsByWorkspace(
5183
+ tenantId,
5184
+ workspaceId,
5185
+ { kind: "training" }
5186
+ );
5187
+ if (training.length <= 1) {
5188
+ return reply.status(409).send({
5189
+ success: false,
5190
+ error: "The last training project cannot be deleted"
5191
+ });
5192
+ }
5193
+ }
4788
5194
  const deleted = await this.projectStore.deleteProject(tenantId, projectId);
4789
5195
  if (!deleted) {
4790
5196
  return reply.status(404).send({ success: false, error: "Project not found" });
@@ -4826,7 +5232,7 @@ var WorkspaceController = class {
4826
5232
  workspace
4827
5233
  };
4828
5234
  }
4829
- const rootDir = await this.getFilesystemRootDirectory(
5235
+ const rootDir = filesystemRootDirectory(
4830
5236
  tenantId,
4831
5237
  workspaceId,
4832
5238
  projectId
@@ -4840,41 +5246,6 @@ var WorkspaceController = class {
4840
5246
  workspace
4841
5247
  };
4842
5248
  }
4843
- async getFilesystemRootDirectory(tenantId, workspaceId, projectId) {
4844
- return `/lattice_store/tenants/${tenantId}/workspaces/${workspaceId}/${projectId}`;
4845
- }
4846
- isBinaryContentType(filename) {
4847
- const ext = filename.split(".").pop()?.toLowerCase() || "";
4848
- const binaryExtensions = /* @__PURE__ */ new Set([
4849
- "pdf",
4850
- "xlsx",
4851
- "xls",
4852
- "docx",
4853
- "doc",
4854
- "pptx",
4855
- "ppt",
4856
- "png",
4857
- "jpg",
4858
- "jpeg",
4859
- "gif",
4860
- "bmp",
4861
- "ico",
4862
- "webp",
4863
- "mp3",
4864
- "mp4",
4865
- "webm",
4866
- "ogg",
4867
- "wav",
4868
- "avi",
4869
- "mov",
4870
- "zip",
4871
- "tar",
4872
- "gz",
4873
- "rar",
4874
- "7z"
4875
- ]);
4876
- return binaryExtensions.has(ext);
4877
- }
4878
5249
  async downloadFile(request, reply) {
4879
5250
  const tenantId = this.getTenantId(request);
4880
5251
  const { workspaceId, projectId } = request.params;
@@ -4896,11 +5267,10 @@ var WorkspaceController = class {
4896
5267
  projectId
4897
5268
  };
4898
5269
  const filename2 = getFilenameFromPath(resolvedPath);
4899
- const isBinary = this.isBinaryContentType(filename2);
4900
5270
  const volumeBackend = await sandboxManager.getVolumeBackendForPath(volumeConfig, resolvedPath);
4901
5271
  let buf;
4902
5272
  if (volumeBackend) {
4903
- if (isBinary && volumeBackend.readBinary) {
5273
+ if (volumeBackend.readBinary) {
4904
5274
  buf = await volumeBackend.readBinary(resolvedPath);
4905
5275
  } else {
4906
5276
  const fileData = await volumeBackend.readRaw(resolvedPath);
@@ -4915,10 +5285,12 @@ var WorkspaceController = class {
4915
5285
  return reply.status(200).type(inferredContentType).header("Content-Disposition", contentDisposition).send(buf);
4916
5286
  }
4917
5287
  const { backend } = await this.getBackend(tenantId, workspaceId, projectId, assistantId, resolvedPath);
4918
- const content = await backend.read(resolvedPath, 0, Infinity);
5288
+ if (!backend.readBinary) {
5289
+ throw new Error("Filesystem backend does not support binary reads");
5290
+ }
5291
+ const buffer = await backend.readBinary(resolvedPath);
4919
5292
  const filename = getFilenameFromPath(resolvedPath);
4920
5293
  const mimeType = getContentTypeFromFilename(filename);
4921
- const buffer = Buffer.from(content, "utf-8");
4922
5294
  return reply.status(200).type(mimeType).header("Content-Disposition", `attachment; filename*=UTF-8''${encodeURIComponent(filename)}`).send(buffer);
4923
5295
  } catch (error) {
4924
5296
  const message = error instanceof Error ? error.message : String(error);
@@ -4950,7 +5322,7 @@ var WorkspaceController = class {
4950
5322
  projectId
4951
5323
  };
4952
5324
  const filename2 = getFilenameFromPath(resolvedPath);
4953
- const isBinary = this.isBinaryContentType(filename2);
5325
+ const isBinary = isBinaryContentType(filename2);
4954
5326
  const volumeBackend = await sandboxManager.getVolumeBackendForPath(volumeConfig, resolvedPath);
4955
5327
  let buf;
4956
5328
  if (volumeBackend) {
@@ -5081,80 +5453,29 @@ var WorkspaceController = class {
5081
5453
  if (pathValue && !/^[a-zA-Z0-9_./~-]+$/.test(pathValue)) {
5082
5454
  return reply.status(400).send({ success: false, error: "Invalid path parameter" });
5083
5455
  }
5084
- if (workspace.storageType === "sandbox") {
5085
- const sandboxManager = getSandBoxManager3();
5086
- const volumeConfig = {
5087
- assistant_id: assistantId || "",
5088
- thread_id: "",
5089
- tenantId,
5090
- workspaceId,
5091
- projectId
5092
- };
5093
- const realPath = pathValue ? path2.posix.join(pathValue, filename) : path2.posix.join("/project/uploads", filename);
5094
- const isBinary = this.isBinaryContentType(filename);
5095
- const volumeBackend = await sandboxManager.getVolumeBackendForPath(volumeConfig, realPath);
5096
- if (volumeBackend && !isBinary) {
5097
- await volumeBackend.write(realPath, buffer.toString("utf-8"));
5098
- } else if (volumeBackend && volumeBackend.writeBinary) {
5099
- await volumeBackend.writeBinary(realPath, buffer);
5100
- } else {
5101
- const sandbox = await sandboxManager.getSandboxFromConfig(volumeConfig);
5102
- await sandbox.file.uploadFile({ file: realPath, data: buffer });
5103
- }
5104
- const relativePath = pathValue ? path2.posix.join(pathValue, filename) : path2.posix.join("/project/uploads", filename);
5105
- const result2 = {
5106
- path: relativePath,
5107
- name: filename,
5108
- is_dir: false,
5109
- size: buffer.length,
5110
- modified_at: (/* @__PURE__ */ new Date()).toISOString()
5111
- };
5112
- return reply.status(200).send({ success: true, data: result2 });
5113
- }
5114
- const rootDir = await this.getFilesystemRootDirectory(
5456
+ const saved = await saveProjectFile({
5115
5457
  tenantId,
5116
5458
  workspaceId,
5117
- projectId
5118
- );
5119
- await fs2.mkdir(rootDir, { recursive: true });
5120
- const relativeDirectory = pathValue?.replace(/^[/\\]+/, "") || ".";
5121
- const targetDir = path2.resolve(rootDir, relativeDirectory);
5122
- if (!isPathWithinRoot(path2.resolve(rootDir), targetDir)) {
5123
- return reply.status(400).send({
5124
- success: false,
5125
- error: "Upload path must stay within the project directory"
5126
- });
5127
- }
5128
- await fs2.mkdir(targetDir, { recursive: true });
5129
- const [resolvedRoot, resolvedTargetDir] = await Promise.all([
5130
- fs2.realpath(rootDir),
5131
- fs2.realpath(targetDir)
5132
- ]);
5133
- if (!isPathWithinRoot(resolvedRoot, resolvedTargetDir)) {
5134
- return reply.status(400).send({
5135
- success: false,
5136
- error: "Upload path must stay within the project directory"
5137
- });
5138
- }
5139
- const safeFilename = path2.basename(filename);
5140
- if (safeFilename !== filename) {
5141
- return reply.status(400).send({
5142
- success: false,
5143
- error: "Invalid filename"
5144
- });
5145
- }
5146
- const targetPath = path2.join(resolvedTargetDir, safeFilename);
5147
- await fs2.writeFile(targetPath, buffer);
5148
- const stat2 = await fs2.stat(targetPath);
5149
- const result = {
5150
- path: path2.posix.join("/", relativeDirectory, safeFilename),
5459
+ projectId,
5151
5460
  name: filename,
5152
- is_dir: false,
5153
- size: stat2.size,
5154
- modified_at: stat2.mtime.toISOString()
5155
- };
5156
- return reply.status(200).send({ success: true, data: result });
5461
+ bytes: buffer,
5462
+ ...pathValue ? { path: pathValue } : {},
5463
+ assistantId
5464
+ });
5465
+ return reply.status(200).send({
5466
+ success: true,
5467
+ data: {
5468
+ path: saved.uri,
5469
+ name: saved.name,
5470
+ is_dir: false,
5471
+ size: saved.size,
5472
+ modified_at: (/* @__PURE__ */ new Date()).toISOString()
5473
+ }
5474
+ });
5157
5475
  } catch (error) {
5476
+ if (error instanceof ProjectFileValidationError) {
5477
+ return reply.status(400).send({ success: false, error: error.message });
5478
+ }
5158
5479
  const message = error instanceof Error ? error.message : String(error);
5159
5480
  return reply.status(500).send({
5160
5481
  success: false,
@@ -5171,7 +5492,7 @@ var WorkspaceController = class {
5171
5492
  * - Returns only directory names, no file contents or hidden files
5172
5493
  */
5173
5494
  async browseFilesystem(request, reply) {
5174
- const homeDir = os.homedir();
5495
+ const homeDir = os2.homedir();
5175
5496
  const requestedPath = request.query.path || homeDir;
5176
5497
  const SYSTEM_BLACKLIST = ["/etc", "/sys", "/proc", "/var", "/tmp", "/dev"];
5177
5498
  try {
@@ -5280,11 +5601,11 @@ function registerWorkspaceRoutes(app2) {
5280
5601
 
5281
5602
  // src/controllers/database-configs.ts
5282
5603
  import {
5283
- getStoreLattice as getStoreLattice8,
5604
+ getStoreLattice as getStoreLattice7,
5284
5605
  sqlDatabaseManager
5285
5606
  } from "@axiom-lattice/core";
5286
5607
  import { randomUUID as randomUUID5 } from "crypto";
5287
- function getTenantId11(request) {
5608
+ function getTenantId9(request) {
5288
5609
  const userTenantId = request.user?.tenantId;
5289
5610
  if (userTenantId) {
5290
5611
  return userTenantId;
@@ -5292,9 +5613,9 @@ function getTenantId11(request) {
5292
5613
  return request.headers["x-tenant-id"] || "default";
5293
5614
  }
5294
5615
  async function getDatabaseConfigList(request, reply) {
5295
- const tenantId = getTenantId11(request);
5616
+ const tenantId = getTenantId9(request);
5296
5617
  try {
5297
- const storeLattice = getStoreLattice8("default", "database");
5618
+ const storeLattice = getStoreLattice7("default", "database");
5298
5619
  const store = storeLattice.store;
5299
5620
  const configs = await store.getAllConfigs(tenantId);
5300
5621
  console.log("Backend: getAllConfigs returned:", configs);
@@ -5322,10 +5643,10 @@ async function getDatabaseConfigList(request, reply) {
5322
5643
  }
5323
5644
  }
5324
5645
  async function getDatabaseConfig(request, reply) {
5325
- const tenantId = getTenantId11(request);
5646
+ const tenantId = getTenantId9(request);
5326
5647
  const { key } = request.params;
5327
5648
  try {
5328
- const storeLattice = getStoreLattice8("default", "database");
5649
+ const storeLattice = getStoreLattice7("default", "database");
5329
5650
  const store = storeLattice.store;
5330
5651
  const config = await store.getConfigByKey(tenantId, key);
5331
5652
  if (!config) {
@@ -5348,10 +5669,10 @@ async function getDatabaseConfig(request, reply) {
5348
5669
  }
5349
5670
  }
5350
5671
  async function createDatabaseConfig(request, reply) {
5351
- const tenantId = getTenantId11(request);
5672
+ const tenantId = getTenantId9(request);
5352
5673
  const body = request.body;
5353
5674
  try {
5354
- const storeLattice = getStoreLattice8("default", "database");
5675
+ const storeLattice = getStoreLattice7("default", "database");
5355
5676
  const store = storeLattice.store;
5356
5677
  const existing = await store.getConfigByKey(tenantId, body.key);
5357
5678
  if (existing) {
@@ -5383,11 +5704,11 @@ async function createDatabaseConfig(request, reply) {
5383
5704
  }
5384
5705
  }
5385
5706
  async function updateDatabaseConfig(request, reply) {
5386
- const tenantId = getTenantId11(request);
5707
+ const tenantId = getTenantId9(request);
5387
5708
  const { key } = request.params;
5388
5709
  const updates = request.body;
5389
5710
  try {
5390
- const storeLattice = getStoreLattice8("default", "database");
5711
+ const storeLattice = getStoreLattice7("default", "database");
5391
5712
  const store = storeLattice.store;
5392
5713
  const existing = await store.getConfigByKey(tenantId, key);
5393
5714
  if (!existing) {
@@ -5425,10 +5746,10 @@ async function updateDatabaseConfig(request, reply) {
5425
5746
  }
5426
5747
  }
5427
5748
  async function deleteDatabaseConfig(request, reply) {
5428
- const tenantId = getTenantId11(request);
5749
+ const tenantId = getTenantId9(request);
5429
5750
  const { keyOrId } = request.params;
5430
5751
  try {
5431
- const storeLattice = getStoreLattice8("default", "database");
5752
+ const storeLattice = getStoreLattice7("default", "database");
5432
5753
  const store = storeLattice.store;
5433
5754
  console.log("Delete request - keyOrId:", keyOrId);
5434
5755
  let config = await store.getConfigByKey(tenantId, keyOrId);
@@ -5474,10 +5795,10 @@ async function deleteDatabaseConfig(request, reply) {
5474
5795
  }
5475
5796
  }
5476
5797
  async function testDatabaseConnection(request, reply) {
5477
- const tenantId = getTenantId11(request);
5798
+ const tenantId = getTenantId9(request);
5478
5799
  const { key } = request.params;
5479
5800
  try {
5480
- const storeLattice = getStoreLattice8("default", "database");
5801
+ const storeLattice = getStoreLattice7("default", "database");
5481
5802
  const store = storeLattice.store;
5482
5803
  const config = await store.getConfigByKey(tenantId, key);
5483
5804
  if (!config) {
@@ -5495,7 +5816,7 @@ async function testDatabaseConnection(request, reply) {
5495
5816
  await db.connect();
5496
5817
  await db.listTables();
5497
5818
  const latency = Date.now() - startTime;
5498
- await new Promise((resolve3) => setTimeout(resolve3, 100));
5819
+ await new Promise((resolve2) => setTimeout(resolve2, 100));
5499
5820
  await db.disconnect();
5500
5821
  await sqlDatabaseManager.removeDatabase(tenantId, testKey);
5501
5822
  return {
@@ -5562,12 +5883,12 @@ function registerDatabaseConfigRoutes(app2) {
5562
5883
 
5563
5884
  // src/controllers/metrics-configs.ts
5564
5885
  import {
5565
- getStoreLattice as getStoreLattice9,
5886
+ getStoreLattice as getStoreLattice8,
5566
5887
  metricsServerManager as metricsServerManager2,
5567
5888
  SemanticMetricsClient as SemanticMetricsClient2
5568
5889
  } from "@axiom-lattice/core";
5569
5890
  import { randomUUID as randomUUID6 } from "crypto";
5570
- function getTenantId12(request) {
5891
+ function getTenantId10(request) {
5571
5892
  const userTenantId = request.user?.tenantId;
5572
5893
  if (userTenantId) {
5573
5894
  return userTenantId;
@@ -5575,9 +5896,9 @@ function getTenantId12(request) {
5575
5896
  return request.headers["x-tenant-id"] || "default";
5576
5897
  }
5577
5898
  async function getMetricsServerConfigList(request, reply) {
5578
- const tenantId = getTenantId12(request);
5899
+ const tenantId = getTenantId10(request);
5579
5900
  try {
5580
- const storeLattice = getStoreLattice9("default", "metrics");
5901
+ const storeLattice = getStoreLattice8("default", "metrics");
5581
5902
  const store = storeLattice.store;
5582
5903
  const configs = await store.getAllConfigs(tenantId);
5583
5904
  return {
@@ -5601,10 +5922,10 @@ async function getMetricsServerConfigList(request, reply) {
5601
5922
  }
5602
5923
  }
5603
5924
  async function getMetricsServerConfig(request, reply) {
5604
- const tenantId = getTenantId12(request);
5925
+ const tenantId = getTenantId10(request);
5605
5926
  const { key } = request.params;
5606
5927
  try {
5607
- const storeLattice = getStoreLattice9("default", "metrics");
5928
+ const storeLattice = getStoreLattice8("default", "metrics");
5608
5929
  const store = storeLattice.store;
5609
5930
  const config = await store.getConfigByKey(tenantId, key);
5610
5931
  if (!config) {
@@ -5627,10 +5948,10 @@ async function getMetricsServerConfig(request, reply) {
5627
5948
  }
5628
5949
  }
5629
5950
  async function createMetricsServerConfig(request, reply) {
5630
- const tenantId = getTenantId12(request);
5951
+ const tenantId = getTenantId10(request);
5631
5952
  const body = request.body;
5632
5953
  try {
5633
- const storeLattice = getStoreLattice9("default", "metrics");
5954
+ const storeLattice = getStoreLattice8("default", "metrics");
5634
5955
  const store = storeLattice.store;
5635
5956
  const existing = await store.getConfigByKey(tenantId, body.key);
5636
5957
  if (existing) {
@@ -5678,11 +5999,11 @@ async function createMetricsServerConfig(request, reply) {
5678
5999
  }
5679
6000
  }
5680
6001
  async function updateMetricsServerConfig(request, reply) {
5681
- const tenantId = getTenantId12(request);
6002
+ const tenantId = getTenantId10(request);
5682
6003
  const { key } = request.params;
5683
6004
  const updates = request.body;
5684
6005
  try {
5685
- const storeLattice = getStoreLattice9("default", "metrics");
6006
+ const storeLattice = getStoreLattice8("default", "metrics");
5686
6007
  const store = storeLattice.store;
5687
6008
  const existing = await store.getConfigByKey(tenantId, key);
5688
6009
  if (!existing) {
@@ -5729,10 +6050,10 @@ async function updateMetricsServerConfig(request, reply) {
5729
6050
  }
5730
6051
  }
5731
6052
  async function deleteMetricsServerConfig(request, reply) {
5732
- const tenantId = getTenantId12(request);
6053
+ const tenantId = getTenantId10(request);
5733
6054
  const { keyOrId } = request.params;
5734
6055
  try {
5735
- const storeLattice = getStoreLattice9("default", "metrics");
6056
+ const storeLattice = getStoreLattice8("default", "metrics");
5736
6057
  const store = storeLattice.store;
5737
6058
  let config = await store.getConfigByKey(tenantId, keyOrId);
5738
6059
  let configKey = keyOrId;
@@ -5776,10 +6097,10 @@ async function deleteMetricsServerConfig(request, reply) {
5776
6097
  }
5777
6098
  }
5778
6099
  async function testMetricsServerConnection(request, reply) {
5779
- const tenantId = getTenantId12(request);
6100
+ const tenantId = getTenantId10(request);
5780
6101
  const { key } = request.params;
5781
6102
  try {
5782
- const storeLattice = getStoreLattice9("default", "metrics");
6103
+ const storeLattice = getStoreLattice8("default", "metrics");
5783
6104
  const store = storeLattice.store;
5784
6105
  const config = await store.getConfigByKey(tenantId, key);
5785
6106
  if (!config) {
@@ -5827,10 +6148,10 @@ async function testMetricsServerConnection(request, reply) {
5827
6148
  }
5828
6149
  }
5829
6150
  async function listAvailableMetrics(request, reply) {
5830
- const tenantId = getTenantId12(request);
6151
+ const tenantId = getTenantId10(request);
5831
6152
  const { key } = request.params;
5832
6153
  try {
5833
- const storeLattice = getStoreLattice9("default", "metrics");
6154
+ const storeLattice = getStoreLattice8("default", "metrics");
5834
6155
  const store = storeLattice.store;
5835
6156
  const config = await store.getConfigByKey(tenantId, key);
5836
6157
  if (!config) {
@@ -5865,11 +6186,11 @@ async function listAvailableMetrics(request, reply) {
5865
6186
  }
5866
6187
  }
5867
6188
  async function queryMetricsData(request, reply) {
5868
- const tenantId = getTenantId12(request);
6189
+ const tenantId = getTenantId10(request);
5869
6190
  const { key } = request.params;
5870
6191
  const { metricName, startTime, endTime, step, labels } = request.body;
5871
6192
  try {
5872
- const storeLattice = getStoreLattice9("default", "metrics");
6193
+ const storeLattice = getStoreLattice8("default", "metrics");
5873
6194
  const store = storeLattice.store;
5874
6195
  const config = await store.getConfigByKey(tenantId, key);
5875
6196
  if (!config) {
@@ -5913,10 +6234,10 @@ async function queryMetricsData(request, reply) {
5913
6234
  }
5914
6235
  }
5915
6236
  async function getDataSources(request, reply) {
5916
- const tenantId = getTenantId12(request);
6237
+ const tenantId = getTenantId10(request);
5917
6238
  const { key } = request.params;
5918
6239
  try {
5919
- const storeLattice = getStoreLattice9("default", "metrics");
6240
+ const storeLattice = getStoreLattice8("default", "metrics");
5920
6241
  const store = storeLattice.store;
5921
6242
  const config = await store.getConfigByKey(tenantId, key);
5922
6243
  if (!config) {
@@ -5954,10 +6275,10 @@ async function getDataSources(request, reply) {
5954
6275
  }
5955
6276
  }
5956
6277
  async function getDatasourceMetrics(request, reply) {
5957
- const tenantId = getTenantId12(request);
6278
+ const tenantId = getTenantId10(request);
5958
6279
  const { key, datasourceId } = request.params;
5959
6280
  try {
5960
- const storeLattice = getStoreLattice9("default", "metrics");
6281
+ const storeLattice = getStoreLattice8("default", "metrics");
5961
6282
  const store = storeLattice.store;
5962
6283
  const config = await store.getConfigByKey(tenantId, key);
5963
6284
  if (!config) {
@@ -5991,11 +6312,11 @@ async function getDatasourceMetrics(request, reply) {
5991
6312
  }
5992
6313
  }
5993
6314
  async function querySemanticMetrics(request, reply) {
5994
- const tenantId = getTenantId12(request);
6315
+ const tenantId = getTenantId10(request);
5995
6316
  const { key } = request.params;
5996
6317
  const body = request.body;
5997
6318
  try {
5998
- const storeLattice = getStoreLattice9("default", "metrics");
6319
+ const storeLattice = getStoreLattice8("default", "metrics");
5999
6320
  const store = storeLattice.store;
6000
6321
  const config = await store.getConfigByKey(tenantId, key);
6001
6322
  if (!config) {
@@ -6306,13 +6627,13 @@ function connectionRoutes(fastify2) {
6306
6627
  }
6307
6628
 
6308
6629
  // src/controllers/eval.ts
6309
- import { getStoreLattice as getStoreLattice11 } from "@axiom-lattice/core";
6630
+ import { getStoreLattice as getStoreLattice10 } from "@axiom-lattice/core";
6310
6631
  import { v4 as uuidv44 } from "uuid";
6311
6632
 
6312
6633
  // src/services/eval-runner.ts
6313
6634
  import { EventEmitter } from "events";
6314
6635
  import {
6315
- getStoreLattice as getStoreLattice10,
6636
+ getStoreLattice as getStoreLattice9,
6316
6637
  modelLatticeManager as modelLatticeManager2,
6317
6638
  LatticeEvalProject
6318
6639
  } from "@axiom-lattice/core";
@@ -6370,21 +6691,21 @@ var EvalRunner = class {
6370
6691
  for (const suite of suites) {
6371
6692
  let cases = await store.getCasesBySuite(tenantId, suite.id);
6372
6693
  if (caseIds && caseIds.length > 0) {
6373
- cases = cases.filter((c) => caseIds.includes(c.id));
6694
+ cases = cases.filter((c2) => caseIds.includes(c2.id));
6374
6695
  if (cases.length === 0) continue;
6375
6696
  }
6376
6697
  totalCases += cases.length;
6377
6698
  evalSuites.push({
6378
6699
  suiteName: suite.name,
6379
- cases: cases.map((c) => ({
6380
- caseId: c.id,
6381
- input: { message: c.inputMessage, files: c.inputFiles },
6382
- steps: c.steps,
6700
+ cases: cases.map((c2) => ({
6701
+ caseId: c2.id,
6702
+ input: { message: c2.inputMessage, files: c2.inputFiles },
6703
+ steps: c2.steps,
6383
6704
  output: { type: "message_content" },
6384
- interruptPolicy: c.interruptPolicy,
6705
+ interruptPolicy: c2.interruptPolicy,
6385
6706
  eval: {
6386
- content_assertion: c.contentAssertion,
6387
- eval_rubrics: c.rubrics?.map((r) => ({
6707
+ content_assertion: c2.contentAssertion,
6708
+ eval_rubrics: c2.rubrics?.map((r) => ({
6388
6709
  dimension: r.name,
6389
6710
  weight: r.weight,
6390
6711
  description: r.description
@@ -6556,13 +6877,13 @@ var EvalRunner = class {
6556
6877
  return this.runs.has(runId);
6557
6878
  }
6558
6879
  getEvalStore() {
6559
- return getStoreLattice10("default", "eval").store;
6880
+ return getStoreLattice9("default", "eval").store;
6560
6881
  }
6561
6882
  };
6562
6883
  var evalRunner = new EvalRunner();
6563
6884
 
6564
6885
  // src/controllers/eval.ts
6565
- function getTenantId13(request) {
6886
+ function getTenantId11(request) {
6566
6887
  const userTenantId = request.user?.tenantId;
6567
6888
  if (userTenantId) {
6568
6889
  return userTenantId;
@@ -6570,11 +6891,11 @@ function getTenantId13(request) {
6570
6891
  return request.headers["x-tenant-id"] || "default";
6571
6892
  }
6572
6893
  function getEvalStore() {
6573
- return getStoreLattice11("default", "eval").store;
6894
+ return getStoreLattice10("default", "eval").store;
6574
6895
  }
6575
6896
  async function createProject(request, reply) {
6576
6897
  try {
6577
- const tenantId = getTenantId13(request);
6898
+ const tenantId = getTenantId11(request);
6578
6899
  const store = getEvalStore();
6579
6900
  const id = uuidv44();
6580
6901
  const data = request.body;
@@ -6631,7 +6952,7 @@ async function createProject(request, reply) {
6631
6952
  }
6632
6953
  async function listProjects(request, reply) {
6633
6954
  try {
6634
- const tenantId = getTenantId13(request);
6955
+ const tenantId = getTenantId11(request);
6635
6956
  const store = getEvalStore();
6636
6957
  let projects = await store.getProjectsByTenant(tenantId);
6637
6958
  if (projects.length === 0) {
@@ -6665,7 +6986,7 @@ async function listProjects(request, reply) {
6665
6986
  }
6666
6987
  async function getProject(request, reply) {
6667
6988
  try {
6668
- const tenantId = getTenantId13(request);
6989
+ const tenantId = getTenantId11(request);
6669
6990
  const store = getEvalStore();
6670
6991
  const { pid } = request.params;
6671
6992
  const project = await store.getProjectById(tenantId, pid);
@@ -6685,7 +7006,7 @@ async function getProject(request, reply) {
6685
7006
  }
6686
7007
  async function updateProject(request, reply) {
6687
7008
  try {
6688
- const tenantId = getTenantId13(request);
7009
+ const tenantId = getTenantId11(request);
6689
7010
  const store = getEvalStore();
6690
7011
  const { pid } = request.params;
6691
7012
  const existing = await store.getProjectById(tenantId, pid);
@@ -6728,7 +7049,7 @@ async function updateProject(request, reply) {
6728
7049
  }
6729
7050
  async function deleteProject(request, reply) {
6730
7051
  try {
6731
- const tenantId = getTenantId13(request);
7052
+ const tenantId = getTenantId11(request);
6732
7053
  const store = getEvalStore();
6733
7054
  const { pid } = request.params;
6734
7055
  const existing = await store.getProjectById(tenantId, pid);
@@ -6747,7 +7068,7 @@ async function deleteProject(request, reply) {
6747
7068
  }
6748
7069
  async function createSuite(request, reply) {
6749
7070
  try {
6750
- const tenantId = getTenantId13(request);
7071
+ const tenantId = getTenantId11(request);
6751
7072
  const store = getEvalStore();
6752
7073
  const { pid } = request.params;
6753
7074
  const project = await store.getProjectById(tenantId, pid);
@@ -6769,7 +7090,7 @@ async function createSuite(request, reply) {
6769
7090
  }
6770
7091
  async function updateSuite(request, reply) {
6771
7092
  try {
6772
- const tenantId = getTenantId13(request);
7093
+ const tenantId = getTenantId11(request);
6773
7094
  const store = getEvalStore();
6774
7095
  const { sid } = request.params;
6775
7096
  const existing = await store.getSuiteById(tenantId, sid);
@@ -6789,7 +7110,7 @@ async function updateSuite(request, reply) {
6789
7110
  }
6790
7111
  async function deleteSuite(request, reply) {
6791
7112
  try {
6792
- const tenantId = getTenantId13(request);
7113
+ const tenantId = getTenantId11(request);
6793
7114
  const store = getEvalStore();
6794
7115
  const { sid } = request.params;
6795
7116
  const existing = await store.getSuiteById(tenantId, sid);
@@ -6808,7 +7129,7 @@ async function deleteSuite(request, reply) {
6808
7129
  }
6809
7130
  async function createCase(request, reply) {
6810
7131
  try {
6811
- const tenantId = getTenantId13(request);
7132
+ const tenantId = getTenantId11(request);
6812
7133
  const store = getEvalStore();
6813
7134
  const { sid } = request.params;
6814
7135
  const suite = await store.getSuiteById(tenantId, sid);
@@ -6838,7 +7159,7 @@ async function createCase(request, reply) {
6838
7159
  }
6839
7160
  async function listCasesForSuite(request, reply) {
6840
7161
  try {
6841
- const tenantId = getTenantId13(request);
7162
+ const tenantId = getTenantId11(request);
6842
7163
  const store = getEvalStore();
6843
7164
  const { sid } = request.params;
6844
7165
  const cases = await store.getCasesBySuite(tenantId, sid);
@@ -6854,7 +7175,7 @@ async function listCasesForSuite(request, reply) {
6854
7175
  }
6855
7176
  async function updateCase(request, reply) {
6856
7177
  try {
6857
- const tenantId = getTenantId13(request);
7178
+ const tenantId = getTenantId11(request);
6858
7179
  const store = getEvalStore();
6859
7180
  const { cid } = request.params;
6860
7181
  const existing = await store.getCaseById(tenantId, cid);
@@ -6874,7 +7195,7 @@ async function updateCase(request, reply) {
6874
7195
  }
6875
7196
  async function deleteCase(request, reply) {
6876
7197
  try {
6877
- const tenantId = getTenantId13(request);
7198
+ const tenantId = getTenantId11(request);
6878
7199
  const store = getEvalStore();
6879
7200
  const { cid } = request.params;
6880
7201
  const existing = await store.getCaseById(tenantId, cid);
@@ -6906,7 +7227,7 @@ function registerEvalRoutes(app2) {
6906
7227
  app2.delete("/api/eval/projects/:pid/suites/:sid/cases/:cid", deleteCase);
6907
7228
  app2.post("/api/eval/projects/:pid/runs", async (request, reply) => {
6908
7229
  try {
6909
- const tenantId = getTenantId13(request);
7230
+ const tenantId = getTenantId11(request);
6910
7231
  const { pid } = request.params;
6911
7232
  const body = request.body ?? {};
6912
7233
  const bodyWs = request.headers["x-workspace-id"] || "";
@@ -6925,7 +7246,7 @@ function registerEvalRoutes(app2) {
6925
7246
  });
6926
7247
  app2.get("/api/eval/runs", async (request, reply) => {
6927
7248
  try {
6928
- const tenantId = getTenantId13(request);
7249
+ const tenantId = getTenantId11(request);
6929
7250
  const query = request.query;
6930
7251
  const runs = await getEvalStore().getRunsByTenant(tenantId, { projectId: query.project_id, status: query.status });
6931
7252
  reply.send({ success: true, message: "Ok", data: { records: runs, total: runs.length } });
@@ -6935,7 +7256,7 @@ function registerEvalRoutes(app2) {
6935
7256
  });
6936
7257
  app2.get("/api/eval/runs/:rid", async (request, reply) => {
6937
7258
  try {
6938
- const tenantId = getTenantId13(request);
7259
+ const tenantId = getTenantId11(request);
6939
7260
  const { rid } = request.params;
6940
7261
  const run = await getEvalStore().getRunById(tenantId, rid);
6941
7262
  if (!run) return reply.status(404).send({ success: false, message: "Run not found" });
@@ -6957,7 +7278,7 @@ function registerEvalRoutes(app2) {
6957
7278
  const emitter = evalRunner.getEventEmitter();
6958
7279
  const eventKey = `run:${rid}`;
6959
7280
  if (!evalRunner.isRunning(rid)) {
6960
- const tenantId = getTenantId13(request);
7281
+ const tenantId = getTenantId11(request);
6961
7282
  const run = await getEvalStore().getRunById(tenantId, rid);
6962
7283
  if (run) {
6963
7284
  const eventType = run.status === "completed" ? "completed" : "error";
@@ -6996,7 +7317,7 @@ data: ${JSON.stringify(event.data)}
6996
7317
  });
6997
7318
  app2.delete("/api/eval/runs/:rid", async (request, reply) => {
6998
7319
  try {
6999
- const tenantId = getTenantId13(request);
7320
+ const tenantId = getTenantId11(request);
7000
7321
  const { rid } = request.params;
7001
7322
  const deleted = await getEvalStore().deleteRun(tenantId, rid);
7002
7323
  if (!deleted) return reply.status(404).send({ success: false, message: "Run not found" });
@@ -7007,7 +7328,7 @@ data: ${JSON.stringify(event.data)}
7007
7328
  });
7008
7329
  app2.get("/api/eval/reports/projects/:pid", async (request, reply) => {
7009
7330
  try {
7010
- const tenantId = getTenantId13(request);
7331
+ const tenantId = getTenantId11(request);
7011
7332
  const { pid } = request.params;
7012
7333
  const report = await getEvalStore().getProjectReport(tenantId, pid);
7013
7334
  if (!report) return reply.status(404).send({ success: false, message: "Project not found" });
@@ -7019,11 +7340,11 @@ data: ${JSON.stringify(event.data)}
7019
7340
  }
7020
7341
 
7021
7342
  // src/controllers/users.ts
7022
- import { getStoreLattice as getStoreLattice12 } from "@axiom-lattice/core";
7343
+ import { getStoreLattice as getStoreLattice11 } from "@axiom-lattice/core";
7023
7344
  import { v4 as uuidv45 } from "uuid";
7024
7345
  var UsersController = class {
7025
7346
  constructor() {
7026
- this.userStore = getStoreLattice12("default", "user").store;
7347
+ this.userStore = getStoreLattice11("default", "user").store;
7027
7348
  }
7028
7349
  async listUsers(request, reply) {
7029
7350
  const { email } = request.query;
@@ -7101,11 +7422,11 @@ function registerUserRoutes(app2) {
7101
7422
  }
7102
7423
 
7103
7424
  // src/controllers/tenants.ts
7104
- import { getStoreLattice as getStoreLattice13 } from "@axiom-lattice/core";
7425
+ import { getStoreLattice as getStoreLattice12 } from "@axiom-lattice/core";
7105
7426
  import { v4 as uuidv46 } from "uuid";
7106
7427
  var TenantsController = class {
7107
7428
  constructor() {
7108
- this.tenantStore = getStoreLattice13("default", "tenant").store;
7429
+ this.tenantStore = getStoreLattice12("default", "tenant").store;
7109
7430
  }
7110
7431
  // ==================== Tenant CRUD ====================
7111
7432
  async listTenants(request, reply) {
@@ -7169,8 +7490,9 @@ function registerTenantRoutes(app2) {
7169
7490
  }
7170
7491
 
7171
7492
  // src/controllers/auth.ts
7172
- import { getStoreLattice as getStoreLattice14 } from "@axiom-lattice/core";
7493
+ import { getStoreLattice as getStoreLattice13 } from "@axiom-lattice/core";
7173
7494
  import { v4 as uuidv47 } from "uuid";
7495
+ import { createHmac, randomBytes, timingSafeEqual } from "crypto";
7174
7496
  var defaultAuthConfig = {
7175
7497
  autoApproveUsers: true,
7176
7498
  allowTenantRegistration: true,
@@ -7178,9 +7500,9 @@ var defaultAuthConfig = {
7178
7500
  };
7179
7501
  var AuthController = class {
7180
7502
  constructor(config = {}) {
7181
- this.userStore = getStoreLattice14("default", "user").store;
7182
- this.tenantStore = getStoreLattice14("default", "tenant").store;
7183
- this.userTenantLinkStore = getStoreLattice14("default", "userTenantLink").store;
7503
+ this.userStore = getStoreLattice13("default", "user").store;
7504
+ this.tenantStore = getStoreLattice13("default", "tenant").store;
7505
+ this.userTenantLinkStore = getStoreLattice13("default", "userTenantLink").store;
7184
7506
  this.config = { ...defaultAuthConfig, ...config };
7185
7507
  }
7186
7508
  async register(request, reply) {
@@ -7251,7 +7573,8 @@ var AuthController = class {
7251
7573
  });
7252
7574
  }
7253
7575
  const tenantLinks = await this.userTenantLinkStore.getTenantsByUser(user.id);
7254
- const token = await this.generateToken(user.id);
7576
+ const singleTenantId = tenantLinks.length === 1 ? tenantLinks[0].tenantId : void 0;
7577
+ const token = await this.generateToken(user.id, singleTenantId);
7255
7578
  return {
7256
7579
  success: true,
7257
7580
  data: {
@@ -7338,7 +7661,7 @@ var AuthController = class {
7338
7661
  const linkMeta = link?.metadata || {};
7339
7662
  let personalAssistant = null;
7340
7663
  if (linkMeta.personalAssistantId) {
7341
- const assistantStore = getStoreLattice14("default", "assistant").store;
7664
+ const assistantStore = getStoreLattice13("default", "assistant").store;
7342
7665
  const exists = await assistantStore.hasAssistant(tenantId, linkMeta.personalAssistantId);
7343
7666
  if (exists) {
7344
7667
  personalAssistant = {
@@ -7457,6 +7780,9 @@ var AuthController = class {
7457
7780
  return hashedPassword === hash;
7458
7781
  }
7459
7782
  async generateToken(userId, tenantId) {
7783
+ if (!this.config.jwtSecret) {
7784
+ throw new Error("AUTH_TOKEN_SECRET is required to issue console sessions");
7785
+ }
7460
7786
  const payload = {
7461
7787
  userId,
7462
7788
  exp: Date.now() + this.config.tokenExpiration * 1e3
@@ -7464,7 +7790,7 @@ var AuthController = class {
7464
7790
  if (tenantId) {
7465
7791
  payload.tenantId = tenantId;
7466
7792
  }
7467
- return btoa(JSON.stringify(payload));
7793
+ return createConsoleSessionToken(payload, this.config.jwtSecret);
7468
7794
  }
7469
7795
  async changePassword(request, reply) {
7470
7796
  const userId = request.user?.id;
@@ -7518,12 +7844,46 @@ var AuthController = class {
7518
7844
  }
7519
7845
  }
7520
7846
  };
7521
- function extractUserFromAuthHeader(authHeader) {
7522
- if (!authHeader?.startsWith("Bearer ")) return void 0;
7847
+ function resolveConsoleTokenSecret(_authRequired) {
7848
+ const configured = process.env.AUTH_TOKEN_SECRET || process.env.JWT_SECRET;
7849
+ if (configured) return configured;
7850
+ devSessionSecret ?? (devSessionSecret = randomBytes(32).toString("hex"));
7851
+ console.warn(
7852
+ "[auth] AUTH_TOKEN_SECRET/JWT_SECRET is not set; using an in-process generated signing secret (sessions are invalidated on restart). Set AUTH_TOKEN_SECRET for production."
7853
+ );
7854
+ return devSessionSecret;
7855
+ }
7856
+ var devSessionSecret;
7857
+ function createConsoleSessionToken(payload, secret) {
7858
+ const encodedPayload = Buffer.from(JSON.stringify(payload)).toString("base64url");
7859
+ const signature = createHmac("sha256", secret).update(encodedPayload).digest("base64url");
7860
+ return `${encodedPayload}.${signature}`;
7861
+ }
7862
+ function isConsoleSessionPayload(value) {
7863
+ if (typeof value !== "object" || value === null) return false;
7864
+ const payload = value;
7865
+ return typeof payload.userId === "string" && payload.userId.length > 0 && typeof payload.exp === "number" && (payload.tenantId === void 0 || typeof payload.tenantId === "string");
7866
+ }
7867
+ function extractUserFromAuthHeader(authHeader, secret) {
7868
+ if (!secret || !authHeader?.startsWith("Bearer ")) return void 0;
7523
7869
  const token = authHeader.substring(7);
7524
- try {
7525
- const payload = JSON.parse(atob(token));
7526
- if (payload.exp && payload.exp < Date.now()) return void 0;
7870
+ const parts = token.split(".");
7871
+ if (parts.length !== 2) return void 0;
7872
+ const [encodedPayload, encodedSignature] = parts;
7873
+ if (!/^[A-Za-z0-9_-]{43}$/.test(encodedSignature)) return void 0;
7874
+ try {
7875
+ const actualSignature = Buffer.from(encodedSignature, "base64url");
7876
+ if (actualSignature.toString("base64url") !== encodedSignature) return void 0;
7877
+ const expectedSignature = createHmac("sha256", secret).update(encodedPayload).digest();
7878
+ if (actualSignature.length !== expectedSignature.length || !timingSafeEqual(actualSignature, expectedSignature)) {
7879
+ return void 0;
7880
+ }
7881
+ const payload = JSON.parse(
7882
+ Buffer.from(encodedPayload, "base64url").toString("utf8")
7883
+ );
7884
+ if (!isConsoleSessionPayload(payload) || payload.exp < Date.now()) {
7885
+ return void 0;
7886
+ }
7527
7887
  return {
7528
7888
  id: payload.userId,
7529
7889
  tenantId: payload.tenantId
@@ -7532,10 +7892,43 @@ function extractUserFromAuthHeader(authHeader) {
7532
7892
  return void 0;
7533
7893
  }
7534
7894
  }
7895
+ var PUBLIC_ROUTES = ["/api/auth/login", "/api/auth/register", "/health", "/api/stt/models"];
7896
+ function bypassesConsoleAuth(request) {
7897
+ const path4 = request.url.split("?")[0];
7898
+ if (request.method === "GET" && /^\/api\/a2a\/agents\/[^/]+\/\.well-known\/agent-card\.json$/.test(path4)) {
7899
+ return true;
7900
+ }
7901
+ return request.method === "POST" && /^\/api\/a2a\/agents\/[^/]+\/jsonrpc$/.test(path4);
7902
+ }
7903
+ function registerConsoleAuthHook(app2, options) {
7904
+ app2.addHook("preHandler", async (request, reply) => {
7905
+ if (bypassesConsoleAuth(request)) return;
7906
+ const secret = options.tokenSecret ?? resolveConsoleTokenSecret(options.authRequired);
7907
+ const user = extractUserFromAuthHeader(request.headers.authorization, secret);
7908
+ if (user) {
7909
+ request.user = user;
7910
+ return;
7911
+ }
7912
+ if (!options.authRequired || request.method === "OPTIONS") return;
7913
+ if (PUBLIC_ROUTES.some((route) => request.url === route)) return;
7914
+ if (request.url.startsWith("/s/")) return;
7915
+ const path4 = request.url.split("?")[0];
7916
+ if (path4.includes("/viewfile") || path4.includes("/downloadfile") || path4.includes("/uploadfile") || path4.startsWith("/f/") || path4 === "/f") {
7917
+ return;
7918
+ }
7919
+ await reply.status(401).send({
7920
+ success: false,
7921
+ error: "Unauthorized - Missing or invalid token"
7922
+ });
7923
+ });
7924
+ }
7535
7925
  function registerAuthRoutes(app2, config) {
7536
7926
  const controller = new AuthController(config);
7537
7927
  const authHook = async (request, reply) => {
7538
- const user = extractUserFromAuthHeader(request.headers.authorization);
7928
+ const user = extractUserFromAuthHeader(
7929
+ request.headers.authorization,
7930
+ config?.jwtSecret
7931
+ );
7539
7932
  if (!user) {
7540
7933
  return reply.status(401).send({
7541
7934
  success: false,
@@ -7888,20 +8281,36 @@ function setChannelControllerDeps(deps) {
7888
8281
  _adapterRegistry = deps.adapterRegistry;
7889
8282
  _router = deps.router;
7890
8283
  }
7891
- function getTenantId14(request) {
8284
+ function getTenantId12(request) {
7892
8285
  const userTenantId = request.user?.tenantId;
7893
8286
  if (userTenantId) {
7894
8287
  return userTenantId;
7895
8288
  }
7896
8289
  return request.headers["x-tenant-id"] || "default";
7897
8290
  }
7898
- async function getInstallationStore() {
7899
- const { getStoreLattice: getStoreLattice31 } = await import("@axiom-lattice/core");
7900
- const store = getStoreLattice31("default", "channelInstallation").store;
7901
- if (store) return store;
7902
- const { PostgreSQLChannelInstallationStore } = await import("@axiom-lattice/pg-stores");
7903
- const databaseUrl = process.env.DATABASE_URL;
7904
- if (!databaseUrl) {
8291
+ function snapshotPlainObject(value) {
8292
+ try {
8293
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return null;
8294
+ const prototype = Object.getPrototypeOf(value);
8295
+ if (prototype !== Object.prototype && prototype !== null) return null;
8296
+ const snapshot = {};
8297
+ for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(value))) {
8298
+ if (!descriptor.enumerable) continue;
8299
+ if (!("value" in descriptor)) return null;
8300
+ snapshot[key] = descriptor.value;
8301
+ }
8302
+ return snapshot;
8303
+ } catch {
8304
+ return null;
8305
+ }
8306
+ }
8307
+ async function getInstallationStore() {
8308
+ const { getStoreLattice: getStoreLattice27 } = await import("@axiom-lattice/core");
8309
+ const store = getStoreLattice27("default", "channelInstallation").store;
8310
+ if (store) return store;
8311
+ const { PostgreSQLChannelInstallationStore } = await import("@axiom-lattice/pg-stores");
8312
+ const databaseUrl = process.env.DATABASE_URL;
8313
+ if (!databaseUrl) {
7905
8314
  throw new Error("DATABASE_URL is required for channel installation store");
7906
8315
  }
7907
8316
  return new PostgreSQLChannelInstallationStore({
@@ -7909,7 +8318,7 @@ async function getInstallationStore() {
7909
8318
  });
7910
8319
  }
7911
8320
  async function getChannelInstallationList(request, reply) {
7912
- const tenantId = getTenantId14(request);
8321
+ const tenantId = getTenantId12(request);
7913
8322
  const { channel } = request.query;
7914
8323
  try {
7915
8324
  const store = await getInstallationStore();
@@ -7935,7 +8344,7 @@ async function getChannelInstallationList(request, reply) {
7935
8344
  }
7936
8345
  }
7937
8346
  async function getChannelInstallation(request, reply) {
7938
- const tenantId = getTenantId14(request);
8347
+ const tenantId = getTenantId12(request);
7939
8348
  const { installationId } = request.params;
7940
8349
  try {
7941
8350
  const store = await getInstallationStore();
@@ -7968,9 +8377,25 @@ async function getChannelInstallation(request, reply) {
7968
8377
  }
7969
8378
  }
7970
8379
  async function createChannelInstallation(request, reply) {
7971
- const tenantId = getTenantId14(request);
7972
- const body = request.body;
7973
- try {
8380
+ const tenantId = getTenantId12(request);
8381
+ const bodySnapshot = snapshotPlainObject(request.body);
8382
+ try {
8383
+ const allowedKeys = /* @__PURE__ */ new Set([
8384
+ "channel",
8385
+ "name",
8386
+ "config",
8387
+ "enabled",
8388
+ "fallbackAgentId",
8389
+ "rejectWhenNoBinding"
8390
+ ]);
8391
+ if (!bodySnapshot || Object.keys(bodySnapshot).some((key) => !allowedKeys.has(key))) {
8392
+ reply.code(400);
8393
+ return {
8394
+ success: false,
8395
+ message: "Invalid request body"
8396
+ };
8397
+ }
8398
+ const body = bodySnapshot;
7974
8399
  if (!body.channel) {
7975
8400
  reply.code(400);
7976
8401
  return {
@@ -8003,7 +8428,7 @@ async function createChannelInstallation(request, reply) {
8003
8428
  }
8004
8429
  }
8005
8430
  const store = await getInstallationStore();
8006
- const installationId = body.id || randomUUID7();
8431
+ const installationId = randomUUID7();
8007
8432
  const installation = await store.createInstallation(
8008
8433
  tenantId,
8009
8434
  installationId,
@@ -8027,7 +8452,8 @@ async function createChannelInstallation(request, reply) {
8027
8452
  };
8028
8453
  } catch (error) {
8029
8454
  console.error("Failed to create channel installation:", error);
8030
- if (error.message?.includes("duplicate") || error.code === "23505") {
8455
+ const errorMessage2 = error.message?.toLowerCase();
8456
+ if (errorMessage2?.includes("duplicate") || errorMessage2?.includes("unique constraint") || error.code === "23505") {
8031
8457
  reply.code(409);
8032
8458
  return {
8033
8459
  success: false,
@@ -8041,7 +8467,7 @@ async function createChannelInstallation(request, reply) {
8041
8467
  }
8042
8468
  }
8043
8469
  async function updateChannelInstallation(request, reply) {
8044
- const tenantId = getTenantId14(request);
8470
+ const tenantId = getTenantId12(request);
8045
8471
  const { installationId } = request.params;
8046
8472
  const body = request.body;
8047
8473
  try {
@@ -8095,7 +8521,7 @@ async function updateChannelInstallation(request, reply) {
8095
8521
  }
8096
8522
  }
8097
8523
  async function deleteChannelInstallation(request, reply) {
8098
- const tenantId = getTenantId14(request);
8524
+ const tenantId = getTenantId12(request);
8099
8525
  const { installationId } = request.params;
8100
8526
  try {
8101
8527
  const store = await getInstallationStore();
@@ -8156,13 +8582,13 @@ function registerChannelInstallationRoutes(app2) {
8156
8582
 
8157
8583
  // src/controllers/channel-bindings.ts
8158
8584
  import { getBindingRegistry } from "@axiom-lattice/core";
8159
- function getTenantId15(request) {
8585
+ function getTenantId13(request) {
8160
8586
  const userTenantId = request.user?.tenantId;
8161
8587
  if (userTenantId) return userTenantId;
8162
8588
  return request.headers["x-tenant-id"] || "default";
8163
8589
  }
8164
8590
  async function getBindingList(request, _reply) {
8165
- const tenantId = getTenantId15(request);
8591
+ const tenantId = getTenantId13(request);
8166
8592
  const { channel, agentId, channelInstallationId, limit, offset } = request.query;
8167
8593
  try {
8168
8594
  const registry = getBindingRegistry();
@@ -8174,7 +8600,7 @@ async function getBindingList(request, _reply) {
8174
8600
  }
8175
8601
  }
8176
8602
  async function getBinding(request, reply) {
8177
- const tenantId = getTenantId15(request);
8603
+ const tenantId = getTenantId13(request);
8178
8604
  try {
8179
8605
  const registry = getBindingRegistry();
8180
8606
  const bindings = await registry.list({ tenantId });
@@ -8190,7 +8616,7 @@ async function getBinding(request, reply) {
8190
8616
  }
8191
8617
  }
8192
8618
  async function createBinding(request, reply) {
8193
- const tenantId = getTenantId15(request);
8619
+ const tenantId = getTenantId13(request);
8194
8620
  try {
8195
8621
  const registry = getBindingRegistry();
8196
8622
  const binding = await registry.create({ ...request.body, tenantId });
@@ -8204,7 +8630,7 @@ async function createBinding(request, reply) {
8204
8630
  }
8205
8631
  async function updateBinding(request, reply) {
8206
8632
  try {
8207
- const tenantId = getTenantId15(request);
8633
+ const tenantId = getTenantId13(request);
8208
8634
  const registry = getBindingRegistry();
8209
8635
  const bindings = await registry.list({ tenantId });
8210
8636
  const existing = bindings.find((b) => b.id === request.params.id);
@@ -8222,7 +8648,7 @@ async function updateBinding(request, reply) {
8222
8648
  }
8223
8649
  async function deleteBinding(request, reply) {
8224
8650
  try {
8225
- const tenantId = getTenantId15(request);
8651
+ const tenantId = getTenantId13(request);
8226
8652
  const registry = getBindingRegistry();
8227
8653
  const bindings = await registry.list({ tenantId });
8228
8654
  const existing = bindings.find((b) => b.id === request.params.id);
@@ -8239,7 +8665,7 @@ async function deleteBinding(request, reply) {
8239
8665
  }
8240
8666
  }
8241
8667
  async function resolveBinding(request, _reply) {
8242
- const tenantId = getTenantId15(request);
8668
+ const tenantId = getTenantId13(request);
8243
8669
  const { channel, senderId, channelInstallationId } = request.query;
8244
8670
  try {
8245
8671
  const registry = getBindingRegistry();
@@ -8266,7 +8692,7 @@ function registerChannelBindingRoutes(app2) {
8266
8692
 
8267
8693
  // src/controllers/menu-items.ts
8268
8694
  import { getMenuRegistry } from "@axiom-lattice/core";
8269
- function getTenantId16(request) {
8695
+ function getTenantId14(request) {
8270
8696
  const userTenantId = request.user?.tenantId;
8271
8697
  if (userTenantId) return userTenantId;
8272
8698
  return request.headers["x-tenant-id"] || "default";
@@ -8275,7 +8701,7 @@ function errorMessage(err) {
8275
8701
  return err instanceof Error ? err.message : "Unexpected error";
8276
8702
  }
8277
8703
  async function getMenuItemList(request, _reply) {
8278
- const tenantId = getTenantId16(request);
8704
+ const tenantId = getTenantId14(request);
8279
8705
  try {
8280
8706
  const registry = getMenuRegistry();
8281
8707
  const items = await registry.list({ tenantId, menuTarget: request.query.menuTarget });
@@ -8287,7 +8713,7 @@ async function getMenuItemList(request, _reply) {
8287
8713
  }
8288
8714
  }
8289
8715
  async function getMenuItem(request, reply) {
8290
- const tenantId = getTenantId16(request);
8716
+ const tenantId = getTenantId14(request);
8291
8717
  try {
8292
8718
  const registry = getMenuRegistry();
8293
8719
  const item = await registry.getById(request.params.id);
@@ -8304,7 +8730,7 @@ async function getMenuItem(request, reply) {
8304
8730
  }
8305
8731
  }
8306
8732
  async function createMenuItem(request, reply) {
8307
- const tenantId = getTenantId16(request);
8733
+ const tenantId = getTenantId14(request);
8308
8734
  try {
8309
8735
  const registry = getMenuRegistry();
8310
8736
  const item = await registry.create({ ...request.body, tenantId });
@@ -8319,7 +8745,7 @@ async function createMenuItem(request, reply) {
8319
8745
  }
8320
8746
  async function updateMenuItem(request, reply) {
8321
8747
  try {
8322
- const tenantId = getTenantId16(request);
8748
+ const tenantId = getTenantId14(request);
8323
8749
  const registry = getMenuRegistry();
8324
8750
  const existing = await registry.getById(request.params.id);
8325
8751
  if (!existing || existing.tenantId !== tenantId) {
@@ -8337,7 +8763,7 @@ async function updateMenuItem(request, reply) {
8337
8763
  }
8338
8764
  async function deleteMenuItem(request, reply) {
8339
8765
  try {
8340
- const tenantId = getTenantId16(request);
8766
+ const tenantId = getTenantId14(request);
8341
8767
  const registry = getMenuRegistry();
8342
8768
  const existing = await registry.getById(request.params.id);
8343
8769
  if (!existing || existing.tenantId !== tenantId) {
@@ -8487,12 +8913,12 @@ async function handleBridgeProxy(request, reply) {
8487
8913
  };
8488
8914
  const timeout = 3e5;
8489
8915
  try {
8490
- const response = await new Promise((resolve3, reject) => {
8916
+ const response = await new Promise((resolve2, reject) => {
8491
8917
  const timer = setTimeout(() => {
8492
8918
  pending.delete(requestId);
8493
8919
  reject(new Error(`Bridge request timeout after ${timeout}ms`));
8494
8920
  }, timeout);
8495
- pending.set(requestId, { resolve: resolve3, reject, timer, agentId });
8921
+ pending.set(requestId, { resolve: resolve2, reject, timer, agentId });
8496
8922
  try {
8497
8923
  if (conn.ws.readyState !== conn.ws.OPEN) {
8498
8924
  pending.delete(requestId);
@@ -8616,11 +9042,11 @@ var registerSTTRoutes = (app2) => {
8616
9042
  };
8617
9043
 
8618
9044
  // src/controllers/local-a2a.ts
8619
- import { getStoreLattice as getStoreLattice15 } from "@axiom-lattice/core";
9045
+ import { getStoreLattice as getStoreLattice14 } from "@axiom-lattice/core";
8620
9046
  import {
8621
9047
  AgentType as AgentType2
8622
9048
  } from "@axiom-lattice/protocols";
8623
- function getTenantId17(request) {
9049
+ function getTenantId15(request) {
8624
9050
  const userTenantId = request.user?.tenantId;
8625
9051
  if (userTenantId) return userTenantId;
8626
9052
  return request.headers["x-tenant-id"] || "default";
@@ -8632,7 +9058,7 @@ function getLocalRuntime(assistant) {
8632
9058
  return graphDefinition.localRuntime;
8633
9059
  }
8634
9060
  async function getLocalA2AAssistants(tenantId) {
8635
- const storeLattice = getStoreLattice15("default", "assistant");
9061
+ const storeLattice = getStoreLattice14("default", "assistant");
8636
9062
  const assistants = await storeLattice.store.getAllAssistants(tenantId);
8637
9063
  const results = [];
8638
9064
  for (const assistant of assistants) {
@@ -8644,7 +9070,7 @@ async function getLocalA2AAssistants(tenantId) {
8644
9070
  return results;
8645
9071
  }
8646
9072
  async function getLocalA2AAssistant(tenantId, assistantId) {
8647
- const storeLattice = getStoreLattice15("default", "assistant");
9073
+ const storeLattice = getStoreLattice14("default", "assistant");
8648
9074
  const assistant = await storeLattice.store.getAssistantById(tenantId, assistantId);
8649
9075
  if (!assistant) return null;
8650
9076
  const runtime = getLocalRuntime(assistant);
@@ -8657,7 +9083,7 @@ async function requireManagedAssistant(request, reply) {
8657
9083
  reply.status(400).send({ success: false, error: "assistantId is required" });
8658
9084
  return null;
8659
9085
  }
8660
- const registered = await getLocalA2AAssistant(getTenantId17(request), assistantId);
9086
+ const registered = await getLocalA2AAssistant(getTenantId15(request), assistantId);
8661
9087
  if (!registered) {
8662
9088
  reply.status(404).send({
8663
9089
  success: false,
@@ -8668,7 +9094,7 @@ async function requireManagedAssistant(request, reply) {
8668
9094
  return { assistantId, localRuntime: registered.localRuntime };
8669
9095
  }
8670
9096
  async function getLocalA2AStatus(request, reply) {
8671
- const tenantId = getTenantId17(request);
9097
+ const tenantId = getTenantId15(request);
8672
9098
  const managed = await getLocalA2AAssistants(tenantId);
8673
9099
  const states = await Promise.all(
8674
9100
  managed.map(
@@ -8872,6 +9298,294 @@ function registerA2ATestConnectionRoute(app2) {
8872
9298
  });
8873
9299
  }
8874
9300
 
9301
+ // src/controllers/tasks.ts
9302
+ import {
9303
+ getStoreLattice as getStoreLattice15
9304
+ } from "@axiom-lattice/core";
9305
+ var AGENT_CREATE_STATUSES = /* @__PURE__ */ new Set(["pending"]);
9306
+ var FORBIDDEN_AGENT_CREATE_KEYS = /* @__PURE__ */ new Set([
9307
+ "beliefImpact",
9308
+ "beliefState",
9309
+ "cancelledAt",
9310
+ "completedAt",
9311
+ "completionEvidence",
9312
+ "evidenceId",
9313
+ "failureReason",
9314
+ "failedAt",
9315
+ "interruption",
9316
+ "lifecycle_completion_audit",
9317
+ "lifecycle_resume_audit",
9318
+ "requireReview",
9319
+ "result",
9320
+ "reviewedAt",
9321
+ "status",
9322
+ "interruptedAt"
9323
+ ]);
9324
+ function containsLifecycleField(value) {
9325
+ if (!value || typeof value !== "object") return false;
9326
+ if (Array.isArray(value)) return value.some(containsLifecycleField);
9327
+ return Object.entries(value).some(([key, nested]) => FORBIDDEN_AGENT_CREATE_KEYS.has(key) || key.startsWith("lifecycle_") || containsLifecycleField(nested));
9328
+ }
9329
+ function governedAgentCreateError(body) {
9330
+ if (body.ownerType !== "agent") return void 0;
9331
+ if (body.status && !AGENT_CREATE_STATUSES.has(body.status)) {
9332
+ return {
9333
+ code: "TASK_LIFECYCLE_TRANSITION_REQUIRED",
9334
+ error: "Public agent tasks can only be created pending."
9335
+ };
9336
+ }
9337
+ const raw = body;
9338
+ if (Object.prototype.hasOwnProperty.call(raw, "result") || Object.prototype.hasOwnProperty.call(raw, "failureReason") || Object.prototype.hasOwnProperty.call(raw, "requireReview") || Object.prototype.hasOwnProperty.call(raw, "createdAt") || Object.prototype.hasOwnProperty.call(raw, "updatedAt") || containsLifecycleField(body.context) || containsLifecycleField(body.metadata)) {
9339
+ return {
9340
+ code: "TASK_LIFECYCLE_CONTEXT_FORBIDDEN",
9341
+ error: "Agent lifecycle results, review policy, interruption state, and audit fields require a trusted lifecycle adapter."
9342
+ };
9343
+ }
9344
+ return void 0;
9345
+ }
9346
+ function getTenantId16(request) {
9347
+ const userTenantId = request.user?.tenantId;
9348
+ if (userTenantId) return userTenantId;
9349
+ return request.headers["x-tenant-id"] || "default";
9350
+ }
9351
+ function getUserId4(request) {
9352
+ const userId = request.user?.id || request.headers["x-user-id"];
9353
+ if (!userId) throw new Error("User ID is required");
9354
+ return userId;
9355
+ }
9356
+ function getStore() {
9357
+ return getStoreLattice15("default", "task").store;
9358
+ }
9359
+ function getAssistantStore2() {
9360
+ return getStoreLattice15("default", "assistant").store;
9361
+ }
9362
+ function governedAgentUpdateError(task, updates) {
9363
+ const has = (key) => Object.prototype.hasOwnProperty.call(updates, key);
9364
+ if (has("ownerType") && updates.ownerType !== task.ownerType || has("ownerId") && updates.ownerId !== task.ownerId) {
9365
+ return "TASK_OWNERSHIP_IMMUTABLE";
9366
+ }
9367
+ if (task.ownerType !== "agent") return void 0;
9368
+ if (has("parentId") && updates.parentId !== task.parentId || has("requireReview") && updates.requireReview !== task.requireReview || has("result") || has("failureReason") || has("status")) {
9369
+ return "TASK_LIFECYCLE_TRANSITION_REQUIRED";
9370
+ }
9371
+ if (has("context") || has("description")) {
9372
+ return "COMPLETION_CONTEXT_MUTATION";
9373
+ }
9374
+ return void 0;
9375
+ }
9376
+ async function listTasks(request) {
9377
+ try {
9378
+ const tenantId = getTenantId16(request);
9379
+ const userId = getUserId4(request);
9380
+ const query = request.query;
9381
+ const ownerType = query.ownerType;
9382
+ const mixedOwnership = ownerType === void 0;
9383
+ const limit = query.limit ? parseInt(query.limit) : void 0;
9384
+ const offset = query.offset ? parseInt(query.offset) : void 0;
9385
+ const store = getStore();
9386
+ const tasks = await store.list({
9387
+ tenantId,
9388
+ status: query.status,
9389
+ priority: query.priority,
9390
+ ...ownerType === "user" ? { ownerType, ownerId: userId } : {},
9391
+ ...ownerType === "agent" ? { ownerType, ...query.ownerId && { ownerId: query.ownerId } } : {},
9392
+ ...mixedOwnership && query.ownerId ? { ownerId: query.ownerId } : {},
9393
+ ...query.parentId && { parentId: query.parentId },
9394
+ metadata: query.metadata ? JSON.parse(query.metadata) : void 0,
9395
+ projectId: query.projectId,
9396
+ ...!mixedOwnership && limit !== void 0 ? { limit } : {},
9397
+ ...!mixedOwnership && offset !== void 0 ? { offset } : {}
9398
+ });
9399
+ const visibleTasks = mixedOwnership ? tasks.filter((task) => task.ownerType === "agent" || task.ownerType === "user" && task.ownerId === userId) : tasks;
9400
+ const pageOffset = mixedOwnership ? offset ?? 0 : 0;
9401
+ const page = mixedOwnership ? visibleTasks.slice(pageOffset, limit === void 0 ? void 0 : pageOffset + limit) : visibleTasks;
9402
+ return { success: true, data: page, count: page.length };
9403
+ } catch (error) {
9404
+ return { success: false, error: error.message };
9405
+ }
9406
+ }
9407
+ async function getTask(request, reply) {
9408
+ try {
9409
+ const tenantId = getTenantId16(request);
9410
+ const userId = getUserId4(request);
9411
+ const { id } = request.params;
9412
+ const store = getStore();
9413
+ const task = await store.getById(tenantId, id);
9414
+ if (!task) {
9415
+ return reply.status(404).send({ success: false, error: "Task not found" });
9416
+ }
9417
+ if (task.ownerType === "user" && task.ownerId !== userId) {
9418
+ return reply.status(403).send({ success: false, error: "Access denied" });
9419
+ }
9420
+ return { success: true, data: task };
9421
+ } catch (error) {
9422
+ return { success: false, error: error.message };
9423
+ }
9424
+ }
9425
+ async function createTask(request, reply) {
9426
+ try {
9427
+ const tenantId = getTenantId16(request);
9428
+ const userId = getUserId4(request);
9429
+ const body = request.body;
9430
+ if (!body.title) {
9431
+ return reply.status(400).send({ success: false, error: "title is required" });
9432
+ }
9433
+ const lifecycleError = governedAgentCreateError(body);
9434
+ if (lifecycleError) {
9435
+ return reply.status(409).send({ success: false, ...lifecycleError });
9436
+ }
9437
+ const ownerType = body.ownerType || "user";
9438
+ const ownerId = body.ownerId || userId;
9439
+ if (ownerType === "agent") {
9440
+ const assistant = await getAssistantStore2().getAssistantById(tenantId, ownerId);
9441
+ if (!assistant || assistant.tenantId !== tenantId) {
9442
+ return reply.status(404).send({
9443
+ success: false,
9444
+ code: "AGENT_OWNER_NOT_FOUND",
9445
+ error: "Agent owner was not found in the current tenant."
9446
+ });
9447
+ }
9448
+ }
9449
+ const store = getStore();
9450
+ const task = await store.create({
9451
+ ...body,
9452
+ tenantId,
9453
+ ownerType,
9454
+ ownerId
9455
+ });
9456
+ return reply.status(201).send({ success: true, data: task });
9457
+ } catch (error) {
9458
+ return reply.status(500).send({ success: false, error: error.message });
9459
+ }
9460
+ }
9461
+ async function updateTask(request, reply) {
9462
+ try {
9463
+ const tenantId = getTenantId16(request);
9464
+ const userId = getUserId4(request);
9465
+ const { id } = request.params;
9466
+ const store = getStore();
9467
+ const existing = await store.getById(tenantId, id);
9468
+ if (!existing) {
9469
+ return reply.status(404).send({ success: false, error: "Task not found" });
9470
+ }
9471
+ if (existing.ownerType === "user" && existing.ownerId !== userId) {
9472
+ return reply.status(403).send({ success: false, error: "Access denied" });
9473
+ }
9474
+ const lifecycleError = governedAgentUpdateError(existing, request.body);
9475
+ if (lifecycleError) {
9476
+ return reply.status(409).send({
9477
+ success: false,
9478
+ code: lifecycleError,
9479
+ error: lifecycleError === "COMPLETION_CONTEXT_MUTATION" ? "Agent completion context is owned by the task lifecycle service." : lifecycleError === "TASK_OWNERSHIP_IMMUTABLE" ? "Task ownership is immutable after creation." : "Agent ownership, hierarchy, and lifecycle transitions require a trusted lifecycle adapter."
9480
+ });
9481
+ }
9482
+ const task = await store.update(tenantId, id, request.body);
9483
+ if (!task) {
9484
+ return reply.status(404).send({ success: false, error: "Task not found" });
9485
+ }
9486
+ return { success: true, data: task };
9487
+ } catch (error) {
9488
+ return reply.status(500).send({ success: false, error: error.message });
9489
+ }
9490
+ }
9491
+ async function deleteTask(request, reply) {
9492
+ try {
9493
+ const tenantId = getTenantId16(request);
9494
+ const userId = getUserId4(request);
9495
+ const { id } = request.params;
9496
+ const store = getStore();
9497
+ const existing = await store.getById(tenantId, id);
9498
+ if (!existing) {
9499
+ return reply.status(404).send({ success: false, error: "Task not found" });
9500
+ }
9501
+ if (existing.ownerType === "user" && existing.ownerId !== userId) {
9502
+ return reply.status(403).send({ success: false, error: "Access denied" });
9503
+ }
9504
+ if (existing.ownerType === "agent") {
9505
+ return reply.status(403).send({
9506
+ success: false,
9507
+ code: "AGENT_RUNTIME_REQUIRED",
9508
+ error: "Agent-owned tasks can only be deleted by a trusted agent runtime."
9509
+ });
9510
+ }
9511
+ const deleted = await store.delete(tenantId, id);
9512
+ if (!deleted) {
9513
+ return reply.status(404).send({ success: false, error: "Task not found" });
9514
+ }
9515
+ return { success: true, message: "Task deleted" };
9516
+ } catch (error) {
9517
+ return reply.status(500).send({ success: false, error: error.message });
9518
+ }
9519
+ }
9520
+ async function completeTask(request, reply) {
9521
+ try {
9522
+ const tenantId = getTenantId16(request);
9523
+ const userId = getUserId4(request);
9524
+ const { id } = request.params;
9525
+ const store = getStore();
9526
+ const existing = await store.getById(tenantId, id);
9527
+ if (!existing) {
9528
+ return reply.status(404).send({ success: false, error: "Task not found" });
9529
+ }
9530
+ if (existing.ownerType === "user" && existing.ownerId !== userId) {
9531
+ return reply.status(403).send({ success: false, error: "Access denied" });
9532
+ }
9533
+ const body = request.body ?? {};
9534
+ if (existing.ownerType === "user") {
9535
+ const task = await store.update(tenantId, id, {
9536
+ status: "completed",
9537
+ ...body.result === void 0 ? {} : { result: body.result }
9538
+ });
9539
+ if (!task) return reply.status(404).send({ success: false, error: "Task not found" });
9540
+ return { success: true, data: task };
9541
+ }
9542
+ return reply.status(403).send({
9543
+ success: false,
9544
+ code: "AGENT_RUNTIME_REQUIRED",
9545
+ error: "Agent-owned tasks can only be completed by a trusted agent runtime."
9546
+ });
9547
+ } catch (error) {
9548
+ return reply.status(500).send({ success: false, error: error.message });
9549
+ }
9550
+ }
9551
+ function getTaskWorkItemStore() {
9552
+ return getStoreLattice15("default", "taskWorkItem").store;
9553
+ }
9554
+ async function listTaskWorkItems(request, reply) {
9555
+ const tenantId = getTenantId16(request);
9556
+ const userId = getUserId4(request);
9557
+ const { id } = request.params;
9558
+ const { action, limit, offset } = request.query;
9559
+ const task = await getStore().getById(tenantId, id);
9560
+ if (!task) {
9561
+ return reply.status(404).send({ success: false, error: "Task not found" });
9562
+ }
9563
+ if (task.ownerType === "user" && task.ownerId !== userId) {
9564
+ return reply.status(403).send({ success: false, error: "Access denied" });
9565
+ }
9566
+ const store = getTaskWorkItemStore();
9567
+ const items = await store.list({
9568
+ tenantId,
9569
+ taskId: id,
9570
+ action,
9571
+ limit: limit ? Number(limit) : void 0,
9572
+ offset: offset ? Number(offset) : void 0
9573
+ });
9574
+ return reply.send({ success: true, data: items });
9575
+ }
9576
+
9577
+ // src/routes/task-routes.ts
9578
+ function registerTaskRoutes(app2) {
9579
+ app2.get("/api/tasks", listTasks);
9580
+ app2.get("/api/tasks/:id", getTask);
9581
+ app2.post("/api/tasks", createTask);
9582
+ app2.put("/api/tasks/:id", updateTask);
9583
+ app2.patch("/api/tasks/:id", updateTask);
9584
+ app2.delete("/api/tasks/:id", deleteTask);
9585
+ app2.patch("/api/tasks/:id/complete", completeTask);
9586
+ app2.get("/api/tasks/:id/work-items", listTaskWorkItems);
9587
+ }
9588
+
8875
9589
  // src/routes/index.ts
8876
9590
  var registerLatticeRoutes = (app2, channelDeps) => {
8877
9591
  registerA2ABridgeRoutes(app2);
@@ -8926,13 +9640,7 @@ var registerLatticeRoutes = (app2, channelDeps) => {
8926
9640
  app2.post("/api/personal-assistant", createPersonalAssistant);
8927
9641
  app2.get("/api/personal-assistant", getPersonalAssistant);
8928
9642
  app2.delete("/api/personal-assistant", deletePersonalAssistant);
8929
- app2.get("/api/tasks", listTasks);
8930
- app2.get("/api/tasks/:id", getTask);
8931
- app2.post("/api/tasks", createTask);
8932
- app2.put("/api/tasks/:id", updateTask);
8933
- app2.delete("/api/tasks/:id", deleteTask);
8934
- app2.patch("/api/tasks/:id/complete", completeTask);
8935
- app2.get("/api/tasks/:id/work-items", listTaskWorkItems);
9643
+ registerTaskRoutes(app2);
8936
9644
  app2.get(
8937
9645
  "/api/assistants/:assistantId/graph",
8938
9646
  { schema: getAgentGraphSchema },
@@ -9041,7 +9749,8 @@ var registerLatticeRoutes = (app2, channelDeps) => {
9041
9749
  registerTenantRoutes(app2);
9042
9750
  registerAuthRoutes(app2, {
9043
9751
  autoApproveUsers: process.env.AUTO_APPROVE_USERS !== "false",
9044
- allowTenantRegistration: process.env.ALLOW_TENANT_REGISTRATION !== "false"
9752
+ allowTenantRegistration: process.env.ALLOW_TENANT_REGISTRATION !== "false",
9753
+ jwtSecret: resolveConsoleTokenSecret(process.env.AUTH_REQUIRED === "true")
9045
9754
  });
9046
9755
  registerChannelRoutes(app2, channelDeps);
9047
9756
  registerChannelInstallationRoutes(app2);
@@ -9178,9 +9887,16 @@ function registerResourceRoutes(app2, controller) {
9178
9887
  import { ExportableEntityRegistry as ExportableEntityRegistry3 } from "@axiom-lattice/core";
9179
9888
 
9180
9889
  // src/export_registrations/skill.registration.ts
9181
- import { getStoreLattice as getStoreLattice16 } from "@axiom-lattice/core";
9182
- function getStore2() {
9183
- return getStoreLattice16("default", "skill").store;
9890
+ import { SandboxSkillStore as SandboxSkillStore2, isBuiltInSkill as isBuiltInSkill2 } from "@axiom-lattice/core";
9891
+ function getStore2(context) {
9892
+ return new SandboxSkillStore2({
9893
+ workspaceId: context?.workspaceId,
9894
+ projectId: context?.projectId
9895
+ });
9896
+ }
9897
+ function toStoreContext(context) {
9898
+ if (!context) return void 0;
9899
+ return { workspaceId: context.workspaceId, projectId: context.projectId };
9184
9900
  }
9185
9901
  var skillRegistration = {
9186
9902
  entityType: "skill",
@@ -9188,10 +9904,10 @@ var skillRegistration = {
9188
9904
  category: "core",
9189
9905
  dependsOn: [],
9190
9906
  cascadeParents: [],
9191
- async listForExport(tenantId) {
9192
- const store = getStore2();
9193
- const skills = await store.getAllSkills(tenantId);
9194
- return skills.map((s) => ({
9907
+ async listForExport(tenantId, context) {
9908
+ const store = getStore2(context);
9909
+ const skills = await store.getAllSkills(tenantId, toStoreContext(context));
9910
+ return skills.filter((s) => !isBuiltInSkill2(s.id)).map((s) => ({
9195
9911
  _exportId: "",
9196
9912
  data: {
9197
9913
  id: s.id,
@@ -9205,14 +9921,58 @@ var skillRegistration = {
9205
9921
  }
9206
9922
  }));
9207
9923
  },
9208
- async previewImport(tenantId, entities) {
9209
- const store = getStore2();
9210
- const existing = await store.getAllSkills(tenantId);
9924
+ async getReferencedEntityIds(_tenantId, entities) {
9925
+ const subSkillIds = /* @__PURE__ */ new Set();
9926
+ for (const entity of entities) {
9927
+ const subSkills = entity.data.subSkills;
9928
+ if (!Array.isArray(subSkills)) continue;
9929
+ for (const id of subSkills) {
9930
+ if (typeof id === "string" && id) subSkillIds.add(id);
9931
+ }
9932
+ }
9933
+ return { skill: [...subSkillIds] };
9934
+ },
9935
+ async listFilesForExport(tenantId, entities, context) {
9936
+ const store = getStore2(context);
9937
+ const ctx = toStoreContext(context);
9938
+ const files = [];
9939
+ for (const entity of entities) {
9940
+ const skillId = entity.data.id;
9941
+ if (!skillId) continue;
9942
+ const content = entity.data.content;
9943
+ files.push({
9944
+ entityId: skillId,
9945
+ path: "SKILL.md",
9946
+ content: Buffer.from(content ?? "", "utf-8")
9947
+ });
9948
+ try {
9949
+ const resources = await store.listSkillResources?.(tenantId, skillId, ctx);
9950
+ if (resources) {
9951
+ for (const resourcePath of resources) {
9952
+ const resourceContent = typeof store.loadSkillResourceRaw === "function" ? await store.loadSkillResourceRaw(tenantId, skillId, resourcePath, ctx) : await store.loadSkillResource?.(tenantId, skillId, resourcePath, ctx);
9953
+ if (resourceContent !== null && resourceContent !== void 0) {
9954
+ files.push({
9955
+ entityId: skillId,
9956
+ path: `resources/${resourcePath}`,
9957
+ content: Buffer.isBuffer(resourceContent) ? resourceContent : Buffer.from(resourceContent, "utf-8")
9958
+ });
9959
+ }
9960
+ }
9961
+ }
9962
+ } catch {
9963
+ }
9964
+ }
9965
+ return files;
9966
+ },
9967
+ async previewImport(tenantId, entities, context) {
9968
+ const store = getStore2(context);
9969
+ const existing = await store.getAllSkills(tenantId, toStoreContext(context));
9211
9970
  const existingIds = new Set(existing.map((s) => s.id));
9212
9971
  const conflicts = [];
9213
9972
  const insertions = [];
9214
9973
  for (const entity of entities) {
9215
9974
  const id = entity.data.id;
9975
+ if (isBuiltInSkill2(id)) continue;
9216
9976
  if (existingIds.has(id)) {
9217
9977
  conflicts.push({
9218
9978
  _exportId: entity._exportId,
@@ -9231,33 +9991,49 @@ var skillRegistration = {
9231
9991
  }
9232
9992
  return { conflicts, insertions, errors: [] };
9233
9993
  },
9234
- async applyImport(tenantId, entity, resolution) {
9235
- const store = getStore2();
9994
+ async applyImport(tenantId, entity, resolution, files, context) {
9995
+ const store = getStore2(context);
9996
+ const ctx = toStoreContext(context);
9236
9997
  const data = entity.data;
9237
- const id = resolution.action === "rename" && resolution.newId ? resolution.newId : data.id;
9238
- if (resolution.action === "overwrite") {
9239
- try {
9240
- await store.deleteSkill(tenantId, id);
9241
- } catch {
9242
- }
9998
+ const originalName = data.name;
9999
+ const targetName = resolution.action === "rename" ? resolution.newId : originalName;
10000
+ if (typeof targetName !== "string" || targetName.trim().length === 0) {
10001
+ throw new Error("Skill import target name must not be empty");
9243
10002
  }
9244
- await store.createSkill(tenantId, id, {
9245
- name: data.name,
10003
+ await store.createSkill(tenantId, targetName, {
10004
+ name: targetName,
9246
10005
  description: data.description,
9247
10006
  license: data.license,
9248
10007
  compatibility: data.compatibility,
9249
10008
  metadata: data.metadata,
9250
10009
  content: data.content,
9251
10010
  subSkills: data.subSkills
9252
- });
9253
- return { newId: id };
10011
+ }, ctx);
10012
+ if (files && files.size > 0) {
10013
+ for (const [relPath, content] of files.entries()) {
10014
+ if (relPath === "SKILL.md") {
10015
+ const fileContent = content.toString("utf-8");
10016
+ if (fileContent !== data.content && typeof store.updateSkill === "function") {
10017
+ await store.updateSkill(tenantId, targetName, { content: fileContent }, ctx);
10018
+ }
10019
+ } else if (relPath.startsWith("resources/")) {
10020
+ const resourcePath = relPath.slice("resources/".length);
10021
+ if (typeof store.writeSkillResourceRaw === "function") {
10022
+ await store.writeSkillResourceRaw(tenantId, targetName, resourcePath, content, ctx);
10023
+ } else if (typeof store.writeSkillResource === "function") {
10024
+ await store.writeSkillResource(tenantId, targetName, resourcePath, content.toString("utf-8"), ctx);
10025
+ }
10026
+ }
10027
+ }
10028
+ }
10029
+ return { newId: targetName };
9254
10030
  }
9255
10031
  };
9256
10032
 
9257
10033
  // src/export_registrations/agent.registration.ts
9258
- import { getStoreLattice as getStoreLattice17 } from "@axiom-lattice/core";
10034
+ import { getStoreLattice as getStoreLattice16 } from "@axiom-lattice/core";
9259
10035
  function getStore3() {
9260
- return getStoreLattice17("default", "assistant").store;
10036
+ return getStoreLattice16("default", "assistant").store;
9261
10037
  }
9262
10038
  var agentRegistration = {
9263
10039
  entityType: "agent",
@@ -9278,145 +10054,68 @@ var agentRegistration = {
9278
10054
  }
9279
10055
  }));
9280
10056
  },
9281
- async previewImport(tenantId, entities) {
9282
- const store = getStore3();
9283
- const existing = await store.getAllAssistants(tenantId);
9284
- const existingIds = new Set(existing.map((a) => a.id));
9285
- const conflicts = [];
9286
- const insertions = [];
10057
+ async getReferencedEntityIds(_tenantId, entities) {
10058
+ const skillIds = /* @__PURE__ */ new Set();
9287
10059
  for (const entity of entities) {
9288
- const id = entity.data.id;
9289
- if (existingIds.has(id)) {
9290
- conflicts.push({
9291
- _exportId: entity._exportId,
9292
- entityType: "agent",
9293
- conflictType: "id_exists",
9294
- existingId: id,
9295
- existingName: entity.data.name || id
9296
- });
9297
- } else {
9298
- insertions.push({
9299
- _exportId: entity._exportId,
9300
- entityType: "agent",
9301
- name: entity.data.name || id
9302
- });
10060
+ const graphDefinition = entity.data.graphDefinition;
10061
+ if (!graphDefinition || typeof graphDefinition !== "object") continue;
10062
+ const ids = graphDefinition.skillIds;
10063
+ if (!Array.isArray(ids)) continue;
10064
+ for (const id of ids) {
10065
+ if (typeof id === "string" && id) skillIds.add(id);
9303
10066
  }
9304
10067
  }
9305
- return { conflicts, insertions, errors: [] };
9306
- },
9307
- async applyImport(tenantId, entity, resolution) {
9308
- const store = getStore3();
9309
- const data = entity.data;
9310
- const id = resolution.action === "rename" && resolution.newId ? resolution.newId : data.id;
9311
- if (resolution.action === "overwrite") {
9312
- try {
9313
- await store.deleteAssistant(tenantId, id);
9314
- } catch {
9315
- }
9316
- }
9317
- await store.createAssistant(tenantId, id, {
9318
- name: data.name,
9319
- description: data.description || "",
9320
- graphDefinition: data.graphDefinition
9321
- });
9322
- return { newId: id };
9323
- }
9324
- };
9325
-
9326
- // src/export_registrations/binding.registration.ts
9327
- import { getStoreLattice as getStoreLattice18 } from "@axiom-lattice/core";
9328
- function getStore4() {
9329
- return getStoreLattice18("default", "channelBinding").store;
9330
- }
9331
- var bindingRegistration = {
9332
- entityType: "binding",
9333
- label: "Binding(\u6E20\u9053\u7ED1\u5B9A)",
9334
- category: "core",
9335
- dependsOn: ["agent", "channel_installation"],
9336
- cascadeParents: [],
9337
- referenceFields: { agentId: "agent", channelInstallationId: "channel_installation" },
9338
- async listForExport(tenantId) {
9339
- const store = getStore4();
9340
- const bindings = await store.list({ tenantId, limit: 1e4, offset: 0 });
9341
- return bindings.map((b) => ({
9342
- _exportId: "",
9343
- data: {
9344
- channel: b.channel,
9345
- channelInstallationId: b.channelInstallationId,
9346
- senderId: b.senderId,
9347
- agentId: b.agentId,
9348
- threadId: b.threadId,
9349
- workspaceId: b.workspaceId,
9350
- projectId: b.projectId,
9351
- threadMode: b.threadMode,
9352
- senderDisplayName: b.senderDisplayName,
9353
- senderMetadata: b.senderMetadata,
9354
- enabled: b.enabled
9355
- }
9356
- }));
10068
+ return { skill: [...skillIds] };
9357
10069
  },
9358
10070
  async previewImport(tenantId, entities) {
9359
- const store = getStore4();
9360
- const existing = await store.list({ tenantId, limit: 1e4, offset: 0 });
9361
- const existingKeys = new Set(
9362
- existing.map((b) => `${b.channel}:${b.channelInstallationId}:${b.senderId}`)
9363
- );
10071
+ const store = getStore3();
10072
+ const existing = await store.getAllAssistants(tenantId);
10073
+ const existingIds = new Set(existing.map((a) => a.id));
9364
10074
  const conflicts = [];
9365
10075
  const insertions = [];
9366
10076
  for (const entity of entities) {
9367
- const d = entity.data;
9368
- const key = `${d.channel}:${d.channelInstallationId}:${d.senderId}`;
9369
- if (existingKeys.has(key)) {
10077
+ const id = entity.data.id;
10078
+ if (existingIds.has(id)) {
9370
10079
  conflicts.push({
9371
10080
  _exportId: entity._exportId,
9372
- entityType: "binding",
9373
- conflictType: "unique_constraint",
9374
- existingName: `${d.channel}:${d.senderId}`,
9375
- field: "channel+channelInstallationId+senderId"
10081
+ entityType: "agent",
10082
+ conflictType: "id_exists",
10083
+ existingId: id,
10084
+ existingName: entity.data.name || id
9376
10085
  });
9377
10086
  } else {
9378
10087
  insertions.push({
9379
10088
  _exportId: entity._exportId,
9380
- entityType: "binding",
9381
- name: `${d.channel}:${d.senderId}`
10089
+ entityType: "agent",
10090
+ name: entity.data.name || id
9382
10091
  });
9383
10092
  }
9384
10093
  }
9385
10094
  return { conflicts, insertions, errors: [] };
9386
10095
  },
9387
10096
  async applyImport(tenantId, entity, resolution) {
9388
- if (resolution.action === "skip") return {};
9389
- const store = getStore4();
9390
- const d = entity.data;
10097
+ const store = getStore3();
10098
+ const data = entity.data;
10099
+ const id = resolution.action === "rename" && resolution.newId ? resolution.newId : data.id;
9391
10100
  if (resolution.action === "overwrite") {
9392
- const existing = await store.list({ tenantId, limit: 1e4, offset: 0 });
9393
- const match = existing.find(
9394
- (b) => b.channel === d.channel && b.channelInstallationId === d.channelInstallationId && b.senderId === d.senderId
9395
- );
9396
- if (match) {
9397
- await store.delete(match.id);
10101
+ try {
10102
+ await store.deleteAssistant(tenantId, id);
10103
+ } catch {
9398
10104
  }
9399
10105
  }
9400
- await store.create({
9401
- channel: d.channel,
9402
- channelInstallationId: d.channelInstallationId,
9403
- tenantId,
9404
- senderId: d.senderId,
9405
- agentId: d.agentId,
9406
- threadMode: d.threadMode || "fixed",
9407
- senderDisplayName: d.senderDisplayName,
9408
- senderMetadata: d.senderMetadata,
9409
- workspaceId: d.workspaceId,
9410
- projectId: d.projectId
10106
+ await store.createAssistant(tenantId, id, {
10107
+ name: data.name,
10108
+ description: data.description || "",
10109
+ graphDefinition: data.graphDefinition
9411
10110
  });
9412
- return { newId: void 0 };
10111
+ return { newId: id };
9413
10112
  }
9414
10113
  };
9415
10114
 
9416
10115
  // src/export_registrations/database-config.registration.ts
9417
- import { getStoreLattice as getStoreLattice19 } from "@axiom-lattice/core";
9418
- function getStore5() {
9419
- return getStoreLattice19("default", "database").store;
10116
+ import { getStoreLattice as getStoreLattice17 } from "@axiom-lattice/core";
10117
+ function getStore4() {
10118
+ return getStoreLattice17("default", "database").store;
9420
10119
  }
9421
10120
  var databaseConfigRegistration = {
9422
10121
  entityType: "database_config",
@@ -9425,24 +10124,24 @@ var databaseConfigRegistration = {
9425
10124
  dependsOn: [],
9426
10125
  cascadeParents: [],
9427
10126
  async listForExport(tenantId) {
9428
- const store = getStore5();
10127
+ const store = getStore4();
9429
10128
  const configs = await store.getAllConfigs(tenantId);
9430
- return configs.map((c) => ({
10129
+ return configs.map((c2) => ({
9431
10130
  _exportId: "",
9432
10131
  data: {
9433
- id: c.id,
9434
- key: c.key,
9435
- name: c.name,
9436
- description: c.description,
9437
- config: c.config
10132
+ id: c2.id,
10133
+ key: c2.key,
10134
+ name: c2.name,
10135
+ description: c2.description,
10136
+ config: c2.config
9438
10137
  }
9439
10138
  }));
9440
10139
  },
9441
10140
  async previewImport(tenantId, entities) {
9442
- const store = getStore5();
10141
+ const store = getStore4();
9443
10142
  const existing = await store.getAllConfigs(tenantId);
9444
- const existingIds = new Set(existing.map((c) => c.id));
9445
- const existingKeys = new Set(existing.map((c) => c.key));
10143
+ const existingIds = new Set(existing.map((c2) => c2.id));
10144
+ const existingKeys = new Set(existing.map((c2) => c2.key));
9446
10145
  const conflicts = [];
9447
10146
  const insertions = [];
9448
10147
  for (const entity of entities) {
@@ -9476,7 +10175,7 @@ var databaseConfigRegistration = {
9476
10175
  return { conflicts, insertions, errors: [] };
9477
10176
  },
9478
10177
  async applyImport(tenantId, entity, resolution) {
9479
- const store = getStore5();
10178
+ const store = getStore4();
9480
10179
  const data = entity.data;
9481
10180
  const id = resolution.action === "rename" && resolution.newId ? resolution.newId : data.id;
9482
10181
  if (resolution.action === "overwrite") {
@@ -9496,9 +10195,9 @@ var databaseConfigRegistration = {
9496
10195
  };
9497
10196
 
9498
10197
  // src/export_registrations/metrics-config.registration.ts
9499
- import { getStoreLattice as getStoreLattice20 } from "@axiom-lattice/core";
9500
- function getStore6() {
9501
- return getStoreLattice20("default", "metrics").store;
10198
+ import { getStoreLattice as getStoreLattice18 } from "@axiom-lattice/core";
10199
+ function getStore5() {
10200
+ return getStoreLattice18("default", "metrics").store;
9502
10201
  }
9503
10202
  var metricsConfigRegistration = {
9504
10203
  entityType: "metrics_config",
@@ -9507,24 +10206,24 @@ var metricsConfigRegistration = {
9507
10206
  dependsOn: [],
9508
10207
  cascadeParents: [],
9509
10208
  async listForExport(tenantId) {
9510
- const store = getStore6();
10209
+ const store = getStore5();
9511
10210
  const configs = await store.getAllConfigs(tenantId);
9512
- return configs.map((c) => ({
10211
+ return configs.map((c2) => ({
9513
10212
  _exportId: "",
9514
10213
  data: {
9515
- id: c.id,
9516
- key: c.key,
9517
- name: c.name,
9518
- description: c.description,
9519
- config: c.config
10214
+ id: c2.id,
10215
+ key: c2.key,
10216
+ name: c2.name,
10217
+ description: c2.description,
10218
+ config: c2.config
9520
10219
  }
9521
10220
  }));
9522
10221
  },
9523
10222
  async previewImport(tenantId, entities) {
9524
- const store = getStore6();
10223
+ const store = getStore5();
9525
10224
  const existing = await store.getAllConfigs(tenantId);
9526
- const existingIds = new Set(existing.map((c) => c.id));
9527
- const existingKeys = new Set(existing.map((c) => c.key));
10225
+ const existingIds = new Set(existing.map((c2) => c2.id));
10226
+ const existingKeys = new Set(existing.map((c2) => c2.key));
9528
10227
  const conflicts = [];
9529
10228
  const insertions = [];
9530
10229
  for (const entity of entities) {
@@ -9558,7 +10257,7 @@ var metricsConfigRegistration = {
9558
10257
  return { conflicts, insertions, errors: [] };
9559
10258
  },
9560
10259
  async applyImport(tenantId, entity, resolution) {
9561
- const store = getStore6();
10260
+ const store = getStore5();
9562
10261
  const data = entity.data;
9563
10262
  const id = resolution.action === "rename" && resolution.newId ? resolution.newId : data.id;
9564
10263
  if (resolution.action === "overwrite") {
@@ -9578,9 +10277,9 @@ var metricsConfigRegistration = {
9578
10277
  };
9579
10278
 
9580
10279
  // src/export_registrations/mcp-config.registration.ts
9581
- import { getStoreLattice as getStoreLattice21 } from "@axiom-lattice/core";
9582
- function getStore7() {
9583
- return getStoreLattice21("default", "mcp").store;
10280
+ import { getStoreLattice as getStoreLattice19 } from "@axiom-lattice/core";
10281
+ function getStore6() {
10282
+ return getStoreLattice19("default", "mcp").store;
9584
10283
  }
9585
10284
  var mcpConfigRegistration = {
9586
10285
  entityType: "mcp_config",
@@ -9589,26 +10288,26 @@ var mcpConfigRegistration = {
9589
10288
  dependsOn: [],
9590
10289
  cascadeParents: [],
9591
10290
  async listForExport(tenantId) {
9592
- const store = getStore7();
10291
+ const store = getStore6();
9593
10292
  const configs = await store.getAllConfigs(tenantId);
9594
- return configs.map((c) => ({
10293
+ return configs.map((c2) => ({
9595
10294
  _exportId: "",
9596
10295
  data: {
9597
- id: c.id,
9598
- key: c.key,
9599
- name: c.name,
9600
- description: c.description,
9601
- config: c.config,
9602
- selectedTools: c.selectedTools,
9603
- status: c.status
10296
+ id: c2.id,
10297
+ key: c2.key,
10298
+ name: c2.name,
10299
+ description: c2.description,
10300
+ config: c2.config,
10301
+ selectedTools: c2.selectedTools,
10302
+ status: c2.status
9604
10303
  }
9605
10304
  }));
9606
10305
  },
9607
10306
  async previewImport(tenantId, entities) {
9608
- const store = getStore7();
10307
+ const store = getStore6();
9609
10308
  const existing = await store.getAllConfigs(tenantId);
9610
- const existingIds = new Set(existing.map((c) => c.id));
9611
- const existingKeys = new Set(existing.map((c) => c.key));
10309
+ const existingIds = new Set(existing.map((c2) => c2.id));
10310
+ const existingKeys = new Set(existing.map((c2) => c2.key));
9612
10311
  const conflicts = [];
9613
10312
  const insertions = [];
9614
10313
  for (const entity of entities) {
@@ -9642,7 +10341,7 @@ var mcpConfigRegistration = {
9642
10341
  return { conflicts, insertions, errors: [] };
9643
10342
  },
9644
10343
  async applyImport(tenantId, entity, resolution) {
9645
- const store = getStore7();
10344
+ const store = getStore6();
9646
10345
  const data = entity.data;
9647
10346
  const id = resolution.action === "rename" && resolution.newId ? resolution.newId : data.id;
9648
10347
  if (resolution.action === "overwrite") {
@@ -9663,12 +10362,12 @@ var mcpConfigRegistration = {
9663
10362
  };
9664
10363
 
9665
10364
  // src/export_registrations/connection.registration.ts
9666
- import { getStoreLattice as getStoreLattice22 } from "@axiom-lattice/core";
9667
- function getStore8() {
9668
- return getStoreLattice22("default", "connection").store;
10365
+ import { getStoreLattice as getStoreLattice20 } from "@axiom-lattice/core";
10366
+ function getStore7() {
10367
+ return getStoreLattice20("default", "connection").store;
9669
10368
  }
9670
10369
  async function listAllConnections(tenantId) {
9671
- const store = getStore8();
10370
+ const store = getStore7();
9672
10371
  if (typeof store.listByTenant !== "function") {
9673
10372
  throw new Error("ConnectionStore.listByTenant is not implemented. SQL bypass removed \u2014 upgrade your store adapter.");
9674
10373
  }
@@ -9682,22 +10381,22 @@ var connectionRegistration = {
9682
10381
  cascadeParents: [],
9683
10382
  async listForExport(tenantId) {
9684
10383
  const connections2 = await listAllConnections(tenantId);
9685
- return connections2.map((c) => ({
10384
+ return connections2.map((c2) => ({
9686
10385
  _exportId: "",
9687
10386
  data: {
9688
- id: c.id,
9689
- type: c.type,
9690
- key: c.key,
9691
- name: c.name,
9692
- description: c.description,
9693
- config: c.config
10387
+ id: c2.id,
10388
+ type: c2.type,
10389
+ key: c2.key,
10390
+ name: c2.name,
10391
+ description: c2.description,
10392
+ config: c2.config
9694
10393
  }
9695
10394
  }));
9696
10395
  },
9697
10396
  async previewImport(tenantId, entities) {
9698
10397
  const existing = await listAllConnections(tenantId);
9699
10398
  const existingCompositeKeys = new Set(
9700
- existing.map((c) => `${c.type}:${c.key}`)
10399
+ existing.map((c2) => `${c2.type}:${c2.key}`)
9701
10400
  );
9702
10401
  const conflicts = [];
9703
10402
  const insertions = [];
@@ -9724,7 +10423,7 @@ var connectionRegistration = {
9724
10423
  },
9725
10424
  async applyImport(tenantId, entity, resolution) {
9726
10425
  if (resolution.action === "skip") return {};
9727
- const store = getStore8();
10426
+ const store = getStore7();
9728
10427
  const d = entity.data;
9729
10428
  const type = d.type;
9730
10429
  const key = d.key;
@@ -9747,9 +10446,9 @@ var connectionRegistration = {
9747
10446
  };
9748
10447
 
9749
10448
  // src/export_registrations/collection.registration.ts
9750
- import { getStoreLattice as getStoreLattice23 } from "@axiom-lattice/core";
9751
- function getStore9() {
9752
- return getStoreLattice23("default", "collection").store;
10449
+ import { getStoreLattice as getStoreLattice21 } from "@axiom-lattice/core";
10450
+ function getStore8() {
10451
+ return getStoreLattice21("default", "collection").store;
9753
10452
  }
9754
10453
  var collectionRegistration = {
9755
10454
  entityType: "collection",
@@ -9758,23 +10457,23 @@ var collectionRegistration = {
9758
10457
  dependsOn: [],
9759
10458
  cascadeParents: [],
9760
10459
  async listForExport(tenantId) {
9761
- const store = getStore9();
10460
+ const store = getStore8();
9762
10461
  const collections = await store.getAllCollections(tenantId);
9763
- return collections.map((c) => ({
10462
+ return collections.map((c2) => ({
9764
10463
  _exportId: "",
9765
10464
  data: {
9766
- id: c.id,
9767
- name: c.name,
9768
- label: c.label,
9769
- embeddingKey: c.embeddingKey,
9770
- schema: c.schema
10465
+ id: c2.id,
10466
+ name: c2.name,
10467
+ label: c2.label,
10468
+ embeddingKey: c2.embeddingKey,
10469
+ schema: c2.schema
9771
10470
  }
9772
10471
  }));
9773
10472
  },
9774
10473
  async previewImport(tenantId, entities) {
9775
- const store = getStore9();
10474
+ const store = getStore8();
9776
10475
  const existing = await store.getAllCollections(tenantId);
9777
- const existingNames = new Set(existing.map((c) => c.name));
10476
+ const existingNames = new Set(existing.map((c2) => c2.name));
9778
10477
  const conflicts = [];
9779
10478
  const insertions = [];
9780
10479
  for (const entity of entities) {
@@ -9800,7 +10499,7 @@ var collectionRegistration = {
9800
10499
  },
9801
10500
  async applyImport(tenantId, entity, resolution) {
9802
10501
  if (resolution.action === "skip") return {};
9803
- const store = getStore9();
10502
+ const store = getStore8();
9804
10503
  const d = entity.data;
9805
10504
  const name = d.name;
9806
10505
  if (resolution.action === "overwrite") {
@@ -9820,9 +10519,11 @@ var collectionRegistration = {
9820
10519
  };
9821
10520
 
9822
10521
  // src/export_registrations/channel-installation.registration.ts
9823
- import { getStoreLattice as getStoreLattice24 } from "@axiom-lattice/core";
9824
- function getStore10() {
9825
- return getStoreLattice24("default", "channelInstallation").store;
10522
+ import { getStoreLattice as getStoreLattice22 } from "@axiom-lattice/core";
10523
+ import { randomUUID as randomUUID9 } from "crypto";
10524
+ var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
10525
+ function getStore9() {
10526
+ return getStoreLattice22("default", "channelInstallation").store;
9826
10527
  }
9827
10528
  var channelInstallationRegistration = {
9828
10529
  entityType: "channel_installation",
@@ -9832,7 +10533,7 @@ var channelInstallationRegistration = {
9832
10533
  cascadeParents: [],
9833
10534
  referenceFields: { fallbackAgentId: "agent" },
9834
10535
  async listForExport(tenantId) {
9835
- const store = getStore10();
10536
+ const store = getStore9();
9836
10537
  const installations = await store.getInstallationsByTenant(tenantId);
9837
10538
  return installations.map((i) => ({
9838
10539
  _exportId: "",
@@ -9848,21 +10549,20 @@ var channelInstallationRegistration = {
9848
10549
  }));
9849
10550
  },
9850
10551
  async previewImport(tenantId, entities) {
9851
- const store = getStore10();
9852
- const existing = await store.getInstallationsByTenant(tenantId);
9853
- const existingIds = new Set(existing.map((i) => i.id));
10552
+ const store = getStore9();
9854
10553
  const conflicts = [];
9855
10554
  const insertions = [];
9856
10555
  for (const entity of entities) {
9857
10556
  const d = entity.data;
9858
10557
  const id = d.id;
9859
- if (existingIds.has(id)) {
10558
+ const existing = UUID_PATTERN.test(id) ? await store.getInstallationById(id) : null;
10559
+ if (existing) {
9860
10560
  conflicts.push({
9861
10561
  _exportId: entity._exportId,
9862
10562
  entityType: "channel_installation",
9863
10563
  conflictType: "id_exists",
9864
10564
  existingId: id,
9865
- existingName: d.name || id
10565
+ existingName: existing.name || id
9866
10566
  });
9867
10567
  } else {
9868
10568
  insertions.push({
@@ -9875,31 +10575,56 @@ var channelInstallationRegistration = {
9875
10575
  return { conflicts, insertions, errors: [] };
9876
10576
  },
9877
10577
  async applyImport(tenantId, entity, resolution) {
9878
- const store = getStore10();
10578
+ const store = getStore9();
9879
10579
  const data = entity.data;
9880
- const id = resolution.action === "rename" && resolution.newId ? resolution.newId : data.id;
10580
+ const sourceId = data.id;
10581
+ const commonData = {
10582
+ name: data.name,
10583
+ enabled: data.enabled,
10584
+ fallbackAgentId: data.fallbackAgentId,
10585
+ rejectWhenNoBinding: data.rejectWhenNoBinding
10586
+ };
9881
10587
  if (resolution.action === "overwrite") {
9882
- try {
9883
- await store.deleteInstallation(tenantId, id);
9884
- } catch {
10588
+ const existing = await store.getInstallationById(sourceId);
10589
+ if (!existing || !UUID_PATTERN.test(existing.id)) {
10590
+ throw new Error("Channel installation overwrite target must be an existing UUID");
10591
+ }
10592
+ const updated = await store.updateInstallation(tenantId, existing.id, {
10593
+ ...commonData,
10594
+ config: data.config
10595
+ });
10596
+ if (!updated) {
10597
+ throw new Error("Channel installation overwrite target is not available in the target tenant");
9885
10598
  }
10599
+ return { newId: existing.id };
10600
+ }
10601
+ let id;
10602
+ if (resolution.action === "rename") {
10603
+ if (!resolution.newId || !UUID_PATTERN.test(resolution.newId)) {
10604
+ throw new Error("Channel installation rename ID must be a valid UUID");
10605
+ }
10606
+ id = resolution.newId;
10607
+ } else if (UUID_PATTERN.test(sourceId)) {
10608
+ if (await store.getInstallationById(sourceId)) {
10609
+ throw new Error(`Channel installation UUID already exists: ${sourceId}`);
10610
+ }
10611
+ id = sourceId;
10612
+ } else {
10613
+ id = randomUUID9();
9886
10614
  }
9887
10615
  await store.createInstallation(tenantId, id, {
9888
10616
  channel: data.channel,
9889
- name: data.name,
9890
- config: data.config,
9891
- enabled: data.enabled,
9892
- fallbackAgentId: data.fallbackAgentId,
9893
- rejectWhenNoBinding: data.rejectWhenNoBinding
10617
+ ...commonData,
10618
+ config: data.config
9894
10619
  });
9895
10620
  return { newId: id };
9896
10621
  }
9897
10622
  };
9898
10623
 
9899
10624
  // src/export_registrations/menu.registration.ts
9900
- import { getStoreLattice as getStoreLattice25 } from "@axiom-lattice/core";
9901
- function getStore11() {
9902
- return getStoreLattice25("default", "menu").store;
10625
+ import { getStoreLattice as getStoreLattice23 } from "@axiom-lattice/core";
10626
+ function getStore10() {
10627
+ return getStoreLattice23("default", "menu").store;
9903
10628
  }
9904
10629
  var menuRegistration = {
9905
10630
  entityType: "menu",
@@ -9908,7 +10633,7 @@ var menuRegistration = {
9908
10633
  dependsOn: ["agent"],
9909
10634
  cascadeParents: [],
9910
10635
  async listForExport(tenantId) {
9911
- const store = getStore11();
10636
+ const store = getStore10();
9912
10637
  const menus = await store.list({ tenantId });
9913
10638
  return menus.map((m) => ({
9914
10639
  _exportId: "",
@@ -9926,7 +10651,7 @@ var menuRegistration = {
9926
10651
  }));
9927
10652
  },
9928
10653
  async previewImport(tenantId, entities) {
9929
- const store = getStore11();
10654
+ const store = getStore10();
9930
10655
  const existing = await store.list({ tenantId });
9931
10656
  const existingIds = new Set(existing.map((m) => m.id));
9932
10657
  const conflicts = [];
@@ -9953,7 +10678,7 @@ var menuRegistration = {
9953
10678
  return { conflicts, insertions, errors: [] };
9954
10679
  },
9955
10680
  async applyImport(tenantId, entity, resolution) {
9956
- const store = getStore11();
10681
+ const store = getStore10();
9957
10682
  const d = entity.data;
9958
10683
  const id = resolution.action === "rename" && resolution.newId ? resolution.newId : d.id;
9959
10684
  if (resolution.action === "overwrite") {
@@ -9976,171 +10701,10 @@ var menuRegistration = {
9976
10701
  }
9977
10702
  };
9978
10703
 
9979
- // src/export_registrations/task.registration.ts
9980
- import { getStoreLattice as getStoreLattice26 } from "@axiom-lattice/core";
9981
- function getStore12() {
9982
- return getStoreLattice26("default", "task").store;
9983
- }
9984
- var taskRegistration = {
9985
- entityType: "task",
9986
- label: "Task(\u4EFB\u52A1)",
9987
- category: "core",
9988
- dependsOn: ["agent"],
9989
- cascadeParents: [],
9990
- referenceFields: { ownerId: "agent", parentId: "task" },
9991
- async listForExport(tenantId) {
9992
- const store = getStore12();
9993
- const tasks = await store.list({ tenantId, limit: 1e4, offset: 0 });
9994
- return tasks.map((t) => ({
9995
- _exportId: "",
9996
- data: {
9997
- id: t.id,
9998
- title: t.title,
9999
- description: t.description,
10000
- status: t.status,
10001
- priority: t.priority,
10002
- dueDate: t.dueDate,
10003
- ownerType: t.ownerType,
10004
- ownerId: t.ownerId,
10005
- metadata: t.metadata,
10006
- parentId: t.parentId,
10007
- sourceId: t.sourceId,
10008
- context: t.context
10009
- }
10010
- }));
10011
- },
10012
- async previewImport(tenantId, entities) {
10013
- const store = getStore12();
10014
- const existing = await store.list({ tenantId, limit: 1e4, offset: 0 });
10015
- const existingIds = new Set(existing.map((t) => t.id));
10016
- const conflicts = [];
10017
- const insertions = [];
10018
- for (const entity of entities) {
10019
- const d = entity.data;
10020
- const id = d.id;
10021
- if (existingIds.has(id)) {
10022
- conflicts.push({
10023
- _exportId: entity._exportId,
10024
- entityType: "task",
10025
- conflictType: "id_exists",
10026
- existingId: id,
10027
- existingName: d.title || id
10028
- });
10029
- } else {
10030
- insertions.push({
10031
- _exportId: entity._exportId,
10032
- entityType: "task",
10033
- name: d.title || id
10034
- });
10035
- }
10036
- }
10037
- return { conflicts, insertions, errors: [] };
10038
- },
10039
- async applyImport(tenantId, entity, resolution) {
10040
- const store = getStore12();
10041
- const data = entity.data;
10042
- const id = resolution.action === "rename" && resolution.newId ? resolution.newId : data.id;
10043
- if (resolution.action === "overwrite") {
10044
- try {
10045
- await store.delete(tenantId, id);
10046
- } catch {
10047
- }
10048
- }
10049
- await store.create({
10050
- tenantId,
10051
- ownerType: data.ownerType || "user",
10052
- ownerId: data.ownerId || "",
10053
- title: data.title,
10054
- description: data.description,
10055
- status: data.status,
10056
- priority: data.priority,
10057
- dueDate: data.dueDate,
10058
- metadata: data.metadata,
10059
- parentId: data.parentId,
10060
- sourceId: data.sourceId,
10061
- context: data.context
10062
- });
10063
- return { newId: id };
10064
- }
10065
- };
10066
-
10067
- // src/export_registrations/a2a-api-key.registration.ts
10068
- import { getStoreLattice as getStoreLattice27 } from "@axiom-lattice/core";
10069
- function getStore13() {
10070
- return getStoreLattice27("default", "a2aApiKey").store;
10071
- }
10072
- var a2aApiKeyRegistration = {
10073
- entityType: "a2a_api_key",
10074
- label: "A2A API Key(API\u5BC6\u94A5)",
10075
- category: "core",
10076
- dependsOn: [],
10077
- cascadeParents: [],
10078
- async listForExport(tenantId) {
10079
- const store = getStore13();
10080
- const keys = await store.list({ tenantId, limit: 1e4, offset: 0 });
10081
- return keys.map((k) => ({
10082
- _exportId: "",
10083
- data: {
10084
- id: k.id,
10085
- tenantId: k.tenantId,
10086
- projectId: k.projectId,
10087
- workspaceId: k.workspaceId,
10088
- label: k.label,
10089
- enabled: k.enabled
10090
- }
10091
- }));
10092
- },
10093
- async previewImport(tenantId, entities) {
10094
- const store = getStore13();
10095
- const existing = await store.list({ tenantId, limit: 1e4, offset: 0 });
10096
- const existingIds = new Set(existing.map((k) => k.id));
10097
- const conflicts = [];
10098
- const insertions = [];
10099
- for (const entity of entities) {
10100
- const d = entity.data;
10101
- const id = d.id;
10102
- if (existingIds.has(id)) {
10103
- conflicts.push({
10104
- _exportId: entity._exportId,
10105
- entityType: "a2a_api_key",
10106
- conflictType: "id_exists",
10107
- existingId: id,
10108
- existingName: d.label || id
10109
- });
10110
- } else {
10111
- insertions.push({
10112
- _exportId: entity._exportId,
10113
- entityType: "a2a_api_key",
10114
- name: d.label || id
10115
- });
10116
- }
10117
- }
10118
- return { conflicts, insertions, errors: [] };
10119
- },
10120
- async applyImport(tenantId, entity, resolution) {
10121
- if (resolution.action === "skip") return {};
10122
- const store = getStore13();
10123
- const d = entity.data;
10124
- if (resolution.action === "overwrite") {
10125
- try {
10126
- await store.delete(d.id);
10127
- } catch {
10128
- }
10129
- }
10130
- await store.create({
10131
- tenantId,
10132
- projectId: d.projectId,
10133
- workspaceId: d.workspaceId,
10134
- label: d.label
10135
- });
10136
- return { newId: void 0 };
10137
- }
10138
- };
10139
-
10140
10704
  // src/export_registrations/eval.registration.ts
10141
- import { getStoreLattice as getStoreLattice28 } from "@axiom-lattice/core";
10142
- function getStore14() {
10143
- return getStoreLattice28("default", "eval").store;
10705
+ import { getStoreLattice as getStoreLattice24 } from "@axiom-lattice/core";
10706
+ function getStore11() {
10707
+ return getStoreLattice24("default", "eval").store;
10144
10708
  }
10145
10709
  var evalRegistration = {
10146
10710
  entityType: "eval",
@@ -10150,7 +10714,7 @@ var evalRegistration = {
10150
10714
  cascadeParents: [],
10151
10715
  referenceFields: { suiteId: "eval_suite", projectId: "eval_project", runId: "eval_run", caseId: "eval_case" },
10152
10716
  async listForExport(tenantId) {
10153
- const store = getStore14();
10717
+ const store = getStore11();
10154
10718
  const projects = await store.getProjectsByTenant(tenantId);
10155
10719
  const entities = [];
10156
10720
  for (const project of projects) {
@@ -10180,19 +10744,19 @@ var evalRegistration = {
10180
10744
  }
10181
10745
  });
10182
10746
  const cases = await store.getCasesBySuite(tenantId, suite.id);
10183
- for (const c of cases) {
10747
+ for (const c2 of cases) {
10184
10748
  entities.push({
10185
10749
  _exportId: "",
10186
10750
  data: {
10187
10751
  _subType: "eval_case",
10188
- id: c.id,
10189
- suiteId: c.suiteId,
10190
- inputMessage: c.inputMessage,
10191
- inputFiles: c.inputFiles,
10192
- steps: c.steps,
10193
- outputType: c.outputType,
10194
- contentAssertion: c.contentAssertion,
10195
- rubrics: c.rubrics
10752
+ id: c2.id,
10753
+ suiteId: c2.suiteId,
10754
+ inputMessage: c2.inputMessage,
10755
+ inputFiles: c2.inputFiles,
10756
+ steps: c2.steps,
10757
+ outputType: c2.outputType,
10758
+ contentAssertion: c2.contentAssertion,
10759
+ rubrics: c2.rubrics
10196
10760
  }
10197
10761
  });
10198
10762
  }
@@ -10240,7 +10804,7 @@ var evalRegistration = {
10240
10804
  return entities;
10241
10805
  },
10242
10806
  async previewImport(tenantId, entities) {
10243
- const store = getStore14();
10807
+ const store = getStore11();
10244
10808
  const existingProjects = await store.getProjectsByTenant(tenantId);
10245
10809
  const existingProjectIds = new Set(existingProjects.map((p) => p.id));
10246
10810
  const existingProjectNames = new Set(existingProjects.map((p) => p.name));
@@ -10256,7 +10820,7 @@ var evalRegistration = {
10256
10820
  existingSuiteIds.add(s.id);
10257
10821
  try {
10258
10822
  const cases = await store.getCasesBySuite(tenantId, s.id);
10259
- for (const c of cases) existingCaseIds.add(c.id);
10823
+ for (const c2 of cases) existingCaseIds.add(c2.id);
10260
10824
  } catch {
10261
10825
  }
10262
10826
  }
@@ -10373,7 +10937,7 @@ var evalRegistration = {
10373
10937
  },
10374
10938
  async applyImport(tenantId, entity, resolution) {
10375
10939
  if (resolution.action === "skip") return {};
10376
- const store = getStore14();
10940
+ const store = getStore11();
10377
10941
  const d = entity.data;
10378
10942
  const subType = d._subType;
10379
10943
  const id = resolution.action === "rename" && resolution.newId ? resolution.newId : d.id;
@@ -10469,7 +11033,6 @@ function registerAllBuiltinEntities() {
10469
11033
  const registry = ExportableEntityRegistry3.getInstance();
10470
11034
  registry.register(skillRegistration);
10471
11035
  registry.register(agentRegistration);
10472
- registry.register(bindingRegistration);
10473
11036
  registry.register(databaseConfigRegistration);
10474
11037
  registry.register(metricsConfigRegistration);
10475
11038
  registry.register(mcpConfigRegistration);
@@ -10477,23 +11040,33 @@ function registerAllBuiltinEntities() {
10477
11040
  registry.register(collectionRegistration);
10478
11041
  registry.register(channelInstallationRegistration);
10479
11042
  registry.register(menuRegistration);
10480
- registry.register(taskRegistration);
10481
- registry.register(a2aApiKeyRegistration);
10482
11043
  registry.register(evalRegistration);
10483
11044
  }
10484
11045
 
10485
11046
  // src/router/MessageRouter.ts
10486
11047
  import {
10487
- getStoreLattice as getStoreLattice29,
11048
+ getStoreLattice as getStoreLattice25,
10488
11049
  agentInstanceManager as agentInstanceManager6
10489
11050
  } from "@axiom-lattice/core";
10490
- import { randomUUID as randomUUID9 } from "crypto";
11051
+ import { randomUUID as randomUUID10 } from "crypto";
10491
11052
  var BindingNotFoundError = class extends Error {
10492
11053
  constructor(message) {
10493
11054
  super(message);
10494
11055
  this.name = "BindingNotFoundError";
10495
11056
  }
10496
11057
  };
11058
+ var ChannelInstallationNotFoundError = class extends Error {
11059
+ constructor() {
11060
+ super("Channel installation is unavailable");
11061
+ this.name = "ChannelInstallationNotFoundError";
11062
+ }
11063
+ };
11064
+ var InvalidChannelInstallationError = class extends Error {
11065
+ constructor(message = "Channel installation is not valid for this message") {
11066
+ super(message);
11067
+ this.name = "InvalidChannelInstallationError";
11068
+ }
11069
+ };
10497
11070
  var MessageRouter = class {
10498
11071
  constructor(config) {
10499
11072
  /**
@@ -10538,20 +11111,20 @@ var MessageRouter = class {
10538
11111
  let threadId;
10539
11112
  let agentId;
10540
11113
  try {
11114
+ const installation = await this.installationStore.getInstallationById(
11115
+ message.channelInstallationId
11116
+ );
11117
+ if (!installation) {
11118
+ throw new ChannelInstallationNotFoundError();
11119
+ }
11120
+ if (installation.enabled !== true) {
11121
+ throw new InvalidChannelInstallationError("Channel installation is disabled");
11122
+ }
11123
+ if (installation.channel !== message.channel || message.tenantId !== void 0 && message.tenantId !== installation.tenantId) {
11124
+ throw new InvalidChannelInstallationError();
11125
+ }
11126
+ const tenantId = installation.tenantId;
10541
11127
  await this.runMiddlewares(ctx, async () => {
10542
- const tenantId = message.tenantId || (await this.installationStore.getInstallationById(message.channelInstallationId))?.tenantId;
10543
- if (!tenantId) {
10544
- console.error({
10545
- event: "dispatch:error",
10546
- error: "tenantId missing",
10547
- channel: message.channel,
10548
- channelInstallationId: message.channelInstallationId,
10549
- senderId: message.sender.id
10550
- }, "tenantId is required");
10551
- throw new Error(
10552
- "tenantId is required: provide it in the message or ensure the channelInstallation has a tenantId"
10553
- );
10554
- }
10555
11128
  console.log({ event: "dispatch:start", channel: message.channel, senderId: message.sender.id, tenantId }, "Message dispatch started");
10556
11129
  const adapter = this.adapterRegistry.get(message.channel);
10557
11130
  const hasAdapterThreadStrategy = !!adapter?.resolveThreadId;
@@ -10561,11 +11134,8 @@ var MessageRouter = class {
10561
11134
  channelInstallationId: message.channelInstallationId,
10562
11135
  tenantId
10563
11136
  });
10564
- const installation = await this.installationStore.getInstallationById(
10565
- message.channelInstallationId
10566
- );
10567
11137
  if (!binding) {
10568
- if (installation?.rejectWhenNoBinding) {
11138
+ if (installation.rejectWhenNoBinding) {
10569
11139
  console.warn({
10570
11140
  event: "dispatch:no_binding",
10571
11141
  channel: message.channel,
@@ -10577,7 +11147,7 @@ var MessageRouter = class {
10577
11147
  `No binding for sender "${message.sender.id}" on channel "${message.channel}"`
10578
11148
  );
10579
11149
  }
10580
- if (installation?.fallbackAgentId) {
11150
+ if (installation.fallbackAgentId) {
10581
11151
  console.warn({
10582
11152
  event: "dispatch:fallback",
10583
11153
  channel: message.channel,
@@ -10632,7 +11202,7 @@ var MessageRouter = class {
10632
11202
  );
10633
11203
  }
10634
11204
  }
10635
- agentId = binding?.agentId ?? installation?.fallbackAgentId;
11205
+ agentId = binding?.agentId ?? installation.fallbackAgentId;
10636
11206
  if (!agentId) {
10637
11207
  throw new BindingNotFoundError(
10638
11208
  `No agent configured for sender "${message.sender.id}"`
@@ -10647,7 +11217,7 @@ var MessageRouter = class {
10647
11217
  channel: message.channel,
10648
11218
  adapterChannel: adapter.channel
10649
11219
  }, "Thread resolved by adapter strategy");
10650
- const threadStore = getStoreLattice29("default", "thread").store;
11220
+ const threadStore = getStoreLattice25("default", "thread").store;
10651
11221
  try {
10652
11222
  await threadStore.createThread(
10653
11223
  tenantId,
@@ -10684,8 +11254,8 @@ var MessageRouter = class {
10684
11254
  }
10685
11255
  }
10686
11256
  if (!threadId) {
10687
- const threadStore = getStoreLattice29("default", "thread").store;
10688
- const newThreadId = randomUUID9();
11257
+ const threadStore = getStoreLattice25("default", "thread").store;
11258
+ const newThreadId = randomUUID10();
10689
11259
  console.log({
10690
11260
  event: "dispatch:thread:create",
10691
11261
  agentId,
@@ -10735,78 +11305,73 @@ var MessageRouter = class {
10735
11305
  const replySubKey = `${threadId}:${message.replyTarget.adapterChannel}:reply`;
10736
11306
  const adapter2 = this.adapterRegistry.get(message.replyTarget.adapterChannel);
10737
11307
  if (adapter2) {
10738
- const installation2 = await this.installationStore.getInstallationById(
10739
- message.channelInstallationId
10740
- );
10741
- if (installation2) {
10742
- const existing = this._replySubs.get(replySubKey);
10743
- if (!existing || existing.count === 0) {
10744
- const timer = setTimeout(() => {
10745
- const entry = this._replySubs.get(replySubKey);
10746
- if (entry) {
11308
+ const existing = this._replySubs.get(replySubKey);
11309
+ if (!existing || existing.count === 0) {
11310
+ const timer = setTimeout(() => {
11311
+ const entry = this._replySubs.get(replySubKey);
11312
+ if (entry) {
11313
+ this._replySubs.delete(replySubKey);
11314
+ console.warn({
11315
+ event: "dispatch:reply:timeout",
11316
+ threadId,
11317
+ channel: message.replyTarget.adapterChannel
11318
+ }, "Reply subscription timed out \u2014 no reply:ready fired within 1h");
11319
+ }
11320
+ }, 36e5);
11321
+ this._replySubs.set(replySubKey, { count: 1, timer });
11322
+ console.log({
11323
+ event: "dispatch:reply:subscribed",
11324
+ threadId,
11325
+ channel: message.replyTarget.adapterChannel,
11326
+ senderId: message.sender.id
11327
+ }, "Subscribed to reply:ready for thread");
11328
+ agent.subscribeOnce("reply:ready", (data) => {
11329
+ const messages = data.state?.values?.messages;
11330
+ const lastAI = messages?.filter((m) => m.type === "ai" || m.getType?.() === "ai").pop();
11331
+ const replyText = lastAI?.content ?? "";
11332
+ const entry = this._replySubs.get(replySubKey);
11333
+ if (entry) {
11334
+ entry.count--;
11335
+ if (entry.count <= 0) {
11336
+ clearTimeout(entry.timer);
10747
11337
  this._replySubs.delete(replySubKey);
10748
- console.warn({
10749
- event: "dispatch:reply:timeout",
10750
- threadId,
10751
- channel: message.replyTarget.adapterChannel
10752
- }, "Reply subscription timed out \u2014 no reply:ready fired within 1h");
10753
11338
  }
10754
- }, 36e5);
10755
- this._replySubs.set(replySubKey, { count: 1, timer });
10756
- console.log({
10757
- event: "dispatch:reply:subscribed",
10758
- threadId,
10759
- channel: message.replyTarget.adapterChannel,
10760
- senderId: message.sender.id
10761
- }, "Subscribed to reply:ready for thread");
10762
- agent.subscribeOnce("reply:ready", (data) => {
10763
- const messages = data.state?.values?.messages;
10764
- const lastAI = messages?.filter((m) => m.type === "ai" || m.getType?.() === "ai").pop();
10765
- const replyText = lastAI?.content ?? "";
10766
- const entry = this._replySubs.get(replySubKey);
10767
- if (entry) {
10768
- entry.count--;
10769
- if (entry.count <= 0) {
10770
- clearTimeout(entry.timer);
10771
- this._replySubs.delete(replySubKey);
10772
- }
10773
- }
10774
- if (replyText) {
11339
+ }
11340
+ if (replyText) {
11341
+ console.log({
11342
+ event: "dispatch:reply:sending",
11343
+ threadId,
11344
+ channel: message.replyTarget.adapterChannel,
11345
+ replyLength: replyText.length
11346
+ }, "Sending channel reply");
11347
+ adapter2.sendReply(message.replyTarget, { text: replyText }, installation).then(() => {
10775
11348
  console.log({
10776
- event: "dispatch:reply:sending",
10777
- threadId,
10778
- channel: message.replyTarget.adapterChannel,
10779
- replyLength: replyText.length
10780
- }, "Sending channel reply");
10781
- adapter2.sendReply(message.replyTarget, { text: replyText }, installation2).then(() => {
10782
- console.log({
10783
- event: "dispatch:reply:sent",
10784
- threadId,
10785
- channel: message.replyTarget.adapterChannel
10786
- }, "Channel reply sent successfully");
10787
- }).catch((err) => console.error({
10788
- event: "dispatch:reply:failed",
10789
- threadId,
10790
- channel: message.replyTarget.adapterChannel,
10791
- error: err instanceof Error ? err.message : String(err)
10792
- }, "Failed to send channel reply"));
10793
- } else {
10794
- console.warn({
10795
- event: "dispatch:reply:empty",
11349
+ event: "dispatch:reply:sent",
10796
11350
  threadId,
10797
11351
  channel: message.replyTarget.adapterChannel
10798
- }, "Agent produced no text output \u2014 skipping reply");
10799
- }
10800
- });
10801
- } else {
10802
- existing.count++;
10803
- console.log({
10804
- event: "dispatch:reply:incremented",
10805
- threadId,
10806
- channel: message.replyTarget.adapterChannel,
10807
- count: existing.count
10808
- }, "Incremented reply counter for thread (already subscribed)");
10809
- }
11352
+ }, "Channel reply sent successfully");
11353
+ }).catch((err) => console.error({
11354
+ event: "dispatch:reply:failed",
11355
+ threadId,
11356
+ channel: message.replyTarget.adapterChannel,
11357
+ error: err instanceof Error ? err.message : String(err)
11358
+ }, "Failed to send channel reply"));
11359
+ } else {
11360
+ console.warn({
11361
+ event: "dispatch:reply:empty",
11362
+ threadId,
11363
+ channel: message.replyTarget.adapterChannel
11364
+ }, "Agent produced no text output \u2014 skipping reply");
11365
+ }
11366
+ });
11367
+ } else {
11368
+ existing.count++;
11369
+ console.log({
11370
+ event: "dispatch:reply:incremented",
11371
+ threadId,
11372
+ channel: message.replyTarget.adapterChannel,
11373
+ count: existing.count
11374
+ }, "Incremented reply counter for thread (already subscribed)");
10810
11375
  }
10811
11376
  }
10812
11377
  }
@@ -11110,7 +11675,7 @@ ${summary}`
11110
11675
  console.log(
11111
11676
  `\u5C06\u5728 ${delayMs}ms \u540E\u91CD\u8BD5\u4EFB\u52A1 (${nextRetryCount}/${maxRetries})`
11112
11677
  );
11113
- await new Promise((resolve3) => setTimeout(resolve3, delayMs));
11678
+ await new Promise((resolve2) => setTimeout(resolve2, delayMs));
11114
11679
  return handleAgentTask(taskRequest, nextRetryCount);
11115
11680
  }
11116
11681
  if (callback_event) {
@@ -11290,7 +11855,7 @@ import {
11290
11855
  getLoggerLattice,
11291
11856
  loggerLatticeManager,
11292
11857
  sandboxLatticeManager as sandboxLatticeManager2,
11293
- getStoreLattice as getStoreLattice30,
11858
+ getStoreLattice as getStoreLattice26,
11294
11859
  agentInstanceManager as agentInstanceManager8,
11295
11860
  createSandboxProvider,
11296
11861
  TokenCache,
@@ -11340,26 +11905,8 @@ var getHeaderValue = (header) => {
11340
11905
  }
11341
11906
  return header;
11342
11907
  };
11343
- var PUBLIC_ROUTES = ["/api/auth/login", "/api/auth/register", "/health", "/api/stt/models"];
11344
- app.addHook("preHandler", async (request, reply) => {
11345
- const user = extractUserFromAuthHeader(request.headers.authorization);
11346
- if (user) {
11347
- request.user = user;
11348
- return;
11349
- }
11350
- const authRequired = process.env.AUTH_REQUIRED === "true";
11351
- if (!authRequired) return;
11352
- if (request.method === "OPTIONS") return;
11353
- if (PUBLIC_ROUTES.some((r) => request.url === r)) return;
11354
- if (request.url.startsWith("/s/")) return;
11355
- const urlPath = request.url.split("?")[0];
11356
- if (urlPath.includes("/viewfile") || urlPath.includes("/downloadfile") || urlPath.includes("/uploadfile")) return;
11357
- if (urlPath.startsWith("/f/") || urlPath === "/f") return;
11358
- return reply.status(401).send({
11359
- success: false,
11360
- error: "Unauthorized - Missing or invalid token"
11361
- });
11362
- });
11908
+ var authRequired = process.env.AUTH_REQUIRED === "true";
11909
+ registerConsoleAuthHook(app, { authRequired });
11363
11910
  app.addHook("onRequest", (request, reply, done) => {
11364
11911
  if (!request.headers["x-project-id"]) {
11365
11912
  request.headers["x-project-id"] = "default";
@@ -11389,7 +11936,7 @@ app.register(cors, {
11389
11936
  origin: true,
11390
11937
  methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"],
11391
11938
  allowedHeaders: "*",
11392
- exposedHeaders: ["Content-Type"],
11939
+ exposedHeaders: ["Content-Type", "Content-Disposition"],
11393
11940
  credentials: true
11394
11941
  });
11395
11942
  app.register(sensible);
@@ -11443,14 +11990,14 @@ function getConfiguredSandboxProvider() {
11443
11990
  }
11444
11991
  async function restoreMcpConnections() {
11445
11992
  try {
11446
- const storeLattice = getStoreLattice30("default", "mcp");
11993
+ const storeLattice = getStoreLattice26("default", "mcp");
11447
11994
  const store = storeLattice.store;
11448
11995
  if (!store) {
11449
11996
  logger4.info("MCP store not configured, skipping connection restoration");
11450
11997
  return;
11451
11998
  }
11452
11999
  const configs = await store.getAllConfigs("default");
11453
- const connectedConfigs = configs.filter((c) => c.status === "connected");
12000
+ const connectedConfigs = configs.filter((c2) => c2.status === "connected");
11454
12001
  if (connectedConfigs.length === 0) return;
11455
12002
  logger4.info(`Restoring ${connectedConfigs.length} MCP server connections...`);
11456
12003
  const { convertToConnection } = await import("./mcp-configs-SHRMQHIL.mjs");
@@ -11495,9 +12042,9 @@ var start = async (config) => {
11495
12042
  const { wechatChannelAdapter } = await import("./WechatChannelAdapter-WSDKR4OA.mjs");
11496
12043
  adapterRegistry.register(wechatChannelAdapter);
11497
12044
  try {
11498
- const { getStoreLattice: getStore15 } = await import("@axiom-lattice/core");
11499
- const bindingStore = getStore15("default", "channelBinding").store;
11500
- const installationStore = getStore15("default", "channelInstallation").store;
12045
+ const { getStoreLattice: getStore12 } = await import("@axiom-lattice/core");
12046
+ const bindingStore = getStore12("default", "channelBinding").store;
12047
+ const installationStore = getStore12("default", "channelInstallation").store;
11501
12048
  setBindingRegistry(bindingStore);
11502
12049
  const router = new MessageRouter({
11503
12050
  middlewares: [
@@ -11510,14 +12057,6 @@ var start = async (config) => {
11510
12057
  installationStore
11511
12058
  });
11512
12059
  channelDeps = { router, installationStore };
11513
- try {
11514
- const a2aKeyStore = getStore15("default", "a2aApiKey").store;
11515
- const a2a = await import("./a2a-QMHEH3ZT.mjs");
11516
- a2a.setA2AKeyStore(a2aKeyStore);
11517
- await a2a.refreshStoreKeyMap();
11518
- logger4.info("A2A key store initialized");
11519
- } catch {
11520
- }
11521
12060
  } catch (err) {
11522
12061
  logger4.warn("Channel infrastructure unavailable", {
11523
12062
  error: err instanceof Error ? err.message : String(err)
@@ -11525,7 +12064,7 @@ var start = async (config) => {
11525
12064
  }
11526
12065
  setEvalRunService(evalRunner);
11527
12066
  try {
11528
- const menuStore = getStoreLattice30("default", "menu").store;
12067
+ const menuStore = getStoreLattice26("default", "menu").store;
11529
12068
  setMenuRegistry(menuStore);
11530
12069
  logger4.info("Menu registry initialized");
11531
12070
  } catch {
@@ -11535,13 +12074,44 @@ var start = async (config) => {
11535
12074
  setChannelControllerDeps({ adapterRegistry, router: channelDeps.router });
11536
12075
  }
11537
12076
  registerLatticeRoutes(app, channelDeps);
12077
+ try {
12078
+ const { A2AAuthService, parseEnvKeys } = await import("./A2AAuthService-DK5X6VIL.mjs");
12079
+ const { registerA2AStandardRoutes } = await import("./a2a-standard-U2XYG45L.mjs");
12080
+ const a2a = await import("./a2a-7N3WDAOP.mjs");
12081
+ const a2aKeyStore = getStoreLattice26("default", "a2aApiKey").store;
12082
+ const assistantStore = getStoreLattice26("default", "assistant").store;
12083
+ const projectStore = getStoreLattice26("default", "project").store;
12084
+ const taskStore = getStoreLattice26("default", "task").store;
12085
+ const authService = new A2AAuthService({
12086
+ keyStore: a2aKeyStore,
12087
+ assistantStore,
12088
+ projectStore,
12089
+ envKeyMap: parseEnvKeys(process.env.A2A_API_KEYS || ""),
12090
+ allowUnauthenticated: process.env.A2A_ALLOW_UNAUTHENTICATED === "true"
12091
+ });
12092
+ await authService.refresh();
12093
+ a2a.setA2AKeyStore(a2aKeyStore);
12094
+ a2a.setA2AAuthService(authService);
12095
+ registerA2AStandardRoutes(app, {
12096
+ authService,
12097
+ getTaskStoreLattice: () => taskStore,
12098
+ getAssistantStore: () => assistantStore,
12099
+ getProjectStore: () => projectStore
12100
+ });
12101
+ a2a.registerA2ARoutes(app);
12102
+ logger4.info("A2A standard routes registered");
12103
+ } catch (err) {
12104
+ logger4.warn("A2A standard routes unavailable", {
12105
+ error: err instanceof Error ? err.message : String(err)
12106
+ });
12107
+ }
11538
12108
  if (!sandboxLatticeManager2.hasLattice("default")) {
11539
12109
  sandboxLatticeManager2.registerLattice("default", getConfiguredSandboxProvider());
11540
12110
  logger4.info("Registered sandbox manager from env configuration");
11541
12111
  }
11542
12112
  try {
11543
12113
  const { ResourceController } = await import("./resources-VA7LSDKN.mjs");
11544
- const sharedResourceStore = getStoreLattice30("default", "sharedResource").store;
12114
+ const sharedResourceStore = getStoreLattice26("default", "sharedResource").store;
11545
12115
  const cache = new TokenCache();
11546
12116
  const resourceController = new ResourceController({
11547
12117
  store: sharedResourceStore,