@axiom-lattice/gateway 3.0.10 → 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;
@@ -4951,7 +5322,7 @@ var WorkspaceController = class {
4951
5322
  projectId
4952
5323
  };
4953
5324
  const filename2 = getFilenameFromPath(resolvedPath);
4954
- const isBinary = this.isBinaryContentType(filename2);
5325
+ const isBinary = isBinaryContentType(filename2);
4955
5326
  const volumeBackend = await sandboxManager.getVolumeBackendForPath(volumeConfig, resolvedPath);
4956
5327
  let buf;
4957
5328
  if (volumeBackend) {
@@ -5082,80 +5453,29 @@ var WorkspaceController = class {
5082
5453
  if (pathValue && !/^[a-zA-Z0-9_./~-]+$/.test(pathValue)) {
5083
5454
  return reply.status(400).send({ success: false, error: "Invalid path parameter" });
5084
5455
  }
5085
- if (workspace.storageType === "sandbox") {
5086
- const sandboxManager = getSandBoxManager3();
5087
- const volumeConfig = {
5088
- assistant_id: assistantId || "",
5089
- thread_id: "",
5090
- tenantId,
5091
- workspaceId,
5092
- projectId
5093
- };
5094
- const realPath = pathValue ? path2.posix.join(pathValue, filename) : path2.posix.join("/project/uploads", filename);
5095
- const isBinary = this.isBinaryContentType(filename);
5096
- const volumeBackend = await sandboxManager.getVolumeBackendForPath(volumeConfig, realPath);
5097
- if (volumeBackend && !isBinary) {
5098
- await volumeBackend.write(realPath, buffer.toString("utf-8"));
5099
- } else if (volumeBackend && volumeBackend.writeBinary) {
5100
- await volumeBackend.writeBinary(realPath, buffer);
5101
- } else {
5102
- const sandbox = await sandboxManager.getSandboxFromConfig(volumeConfig);
5103
- await sandbox.file.uploadFile({ file: realPath, data: buffer });
5104
- }
5105
- const relativePath = pathValue ? path2.posix.join(pathValue, filename) : path2.posix.join("/project/uploads", filename);
5106
- const result2 = {
5107
- path: relativePath,
5108
- name: filename,
5109
- is_dir: false,
5110
- size: buffer.length,
5111
- modified_at: (/* @__PURE__ */ new Date()).toISOString()
5112
- };
5113
- return reply.status(200).send({ success: true, data: result2 });
5114
- }
5115
- const rootDir = await this.getFilesystemRootDirectory(
5456
+ const saved = await saveProjectFile({
5116
5457
  tenantId,
5117
5458
  workspaceId,
5118
- projectId
5119
- );
5120
- await fs2.mkdir(rootDir, { recursive: true });
5121
- const relativeDirectory = pathValue?.replace(/^[/\\]+/, "") || ".";
5122
- const targetDir = path2.resolve(rootDir, relativeDirectory);
5123
- if (!isPathWithinRoot(path2.resolve(rootDir), targetDir)) {
5124
- return reply.status(400).send({
5125
- success: false,
5126
- error: "Upload path must stay within the project directory"
5127
- });
5128
- }
5129
- await fs2.mkdir(targetDir, { recursive: true });
5130
- const [resolvedRoot, resolvedTargetDir] = await Promise.all([
5131
- fs2.realpath(rootDir),
5132
- fs2.realpath(targetDir)
5133
- ]);
5134
- if (!isPathWithinRoot(resolvedRoot, resolvedTargetDir)) {
5135
- return reply.status(400).send({
5136
- success: false,
5137
- error: "Upload path must stay within the project directory"
5138
- });
5139
- }
5140
- const safeFilename = path2.basename(filename);
5141
- if (safeFilename !== filename) {
5142
- return reply.status(400).send({
5143
- success: false,
5144
- error: "Invalid filename"
5145
- });
5146
- }
5147
- const targetPath = path2.join(resolvedTargetDir, safeFilename);
5148
- await fs2.writeFile(targetPath, buffer);
5149
- const stat2 = await fs2.stat(targetPath);
5150
- const result = {
5151
- path: path2.posix.join("/", relativeDirectory, safeFilename),
5459
+ projectId,
5152
5460
  name: filename,
5153
- is_dir: false,
5154
- size: stat2.size,
5155
- modified_at: stat2.mtime.toISOString()
5156
- };
5157
- 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
+ });
5158
5475
  } catch (error) {
5476
+ if (error instanceof ProjectFileValidationError) {
5477
+ return reply.status(400).send({ success: false, error: error.message });
5478
+ }
5159
5479
  const message = error instanceof Error ? error.message : String(error);
5160
5480
  return reply.status(500).send({
5161
5481
  success: false,
@@ -5172,7 +5492,7 @@ var WorkspaceController = class {
5172
5492
  * - Returns only directory names, no file contents or hidden files
5173
5493
  */
5174
5494
  async browseFilesystem(request, reply) {
5175
- const homeDir = os.homedir();
5495
+ const homeDir = os2.homedir();
5176
5496
  const requestedPath = request.query.path || homeDir;
5177
5497
  const SYSTEM_BLACKLIST = ["/etc", "/sys", "/proc", "/var", "/tmp", "/dev"];
5178
5498
  try {
@@ -5281,11 +5601,11 @@ function registerWorkspaceRoutes(app2) {
5281
5601
 
5282
5602
  // src/controllers/database-configs.ts
5283
5603
  import {
5284
- getStoreLattice as getStoreLattice8,
5604
+ getStoreLattice as getStoreLattice7,
5285
5605
  sqlDatabaseManager
5286
5606
  } from "@axiom-lattice/core";
5287
5607
  import { randomUUID as randomUUID5 } from "crypto";
5288
- function getTenantId11(request) {
5608
+ function getTenantId9(request) {
5289
5609
  const userTenantId = request.user?.tenantId;
5290
5610
  if (userTenantId) {
5291
5611
  return userTenantId;
@@ -5293,9 +5613,9 @@ function getTenantId11(request) {
5293
5613
  return request.headers["x-tenant-id"] || "default";
5294
5614
  }
5295
5615
  async function getDatabaseConfigList(request, reply) {
5296
- const tenantId = getTenantId11(request);
5616
+ const tenantId = getTenantId9(request);
5297
5617
  try {
5298
- const storeLattice = getStoreLattice8("default", "database");
5618
+ const storeLattice = getStoreLattice7("default", "database");
5299
5619
  const store = storeLattice.store;
5300
5620
  const configs = await store.getAllConfigs(tenantId);
5301
5621
  console.log("Backend: getAllConfigs returned:", configs);
@@ -5323,10 +5643,10 @@ async function getDatabaseConfigList(request, reply) {
5323
5643
  }
5324
5644
  }
5325
5645
  async function getDatabaseConfig(request, reply) {
5326
- const tenantId = getTenantId11(request);
5646
+ const tenantId = getTenantId9(request);
5327
5647
  const { key } = request.params;
5328
5648
  try {
5329
- const storeLattice = getStoreLattice8("default", "database");
5649
+ const storeLattice = getStoreLattice7("default", "database");
5330
5650
  const store = storeLattice.store;
5331
5651
  const config = await store.getConfigByKey(tenantId, key);
5332
5652
  if (!config) {
@@ -5349,10 +5669,10 @@ async function getDatabaseConfig(request, reply) {
5349
5669
  }
5350
5670
  }
5351
5671
  async function createDatabaseConfig(request, reply) {
5352
- const tenantId = getTenantId11(request);
5672
+ const tenantId = getTenantId9(request);
5353
5673
  const body = request.body;
5354
5674
  try {
5355
- const storeLattice = getStoreLattice8("default", "database");
5675
+ const storeLattice = getStoreLattice7("default", "database");
5356
5676
  const store = storeLattice.store;
5357
5677
  const existing = await store.getConfigByKey(tenantId, body.key);
5358
5678
  if (existing) {
@@ -5384,11 +5704,11 @@ async function createDatabaseConfig(request, reply) {
5384
5704
  }
5385
5705
  }
5386
5706
  async function updateDatabaseConfig(request, reply) {
5387
- const tenantId = getTenantId11(request);
5707
+ const tenantId = getTenantId9(request);
5388
5708
  const { key } = request.params;
5389
5709
  const updates = request.body;
5390
5710
  try {
5391
- const storeLattice = getStoreLattice8("default", "database");
5711
+ const storeLattice = getStoreLattice7("default", "database");
5392
5712
  const store = storeLattice.store;
5393
5713
  const existing = await store.getConfigByKey(tenantId, key);
5394
5714
  if (!existing) {
@@ -5426,10 +5746,10 @@ async function updateDatabaseConfig(request, reply) {
5426
5746
  }
5427
5747
  }
5428
5748
  async function deleteDatabaseConfig(request, reply) {
5429
- const tenantId = getTenantId11(request);
5749
+ const tenantId = getTenantId9(request);
5430
5750
  const { keyOrId } = request.params;
5431
5751
  try {
5432
- const storeLattice = getStoreLattice8("default", "database");
5752
+ const storeLattice = getStoreLattice7("default", "database");
5433
5753
  const store = storeLattice.store;
5434
5754
  console.log("Delete request - keyOrId:", keyOrId);
5435
5755
  let config = await store.getConfigByKey(tenantId, keyOrId);
@@ -5475,10 +5795,10 @@ async function deleteDatabaseConfig(request, reply) {
5475
5795
  }
5476
5796
  }
5477
5797
  async function testDatabaseConnection(request, reply) {
5478
- const tenantId = getTenantId11(request);
5798
+ const tenantId = getTenantId9(request);
5479
5799
  const { key } = request.params;
5480
5800
  try {
5481
- const storeLattice = getStoreLattice8("default", "database");
5801
+ const storeLattice = getStoreLattice7("default", "database");
5482
5802
  const store = storeLattice.store;
5483
5803
  const config = await store.getConfigByKey(tenantId, key);
5484
5804
  if (!config) {
@@ -5496,7 +5816,7 @@ async function testDatabaseConnection(request, reply) {
5496
5816
  await db.connect();
5497
5817
  await db.listTables();
5498
5818
  const latency = Date.now() - startTime;
5499
- await new Promise((resolve3) => setTimeout(resolve3, 100));
5819
+ await new Promise((resolve2) => setTimeout(resolve2, 100));
5500
5820
  await db.disconnect();
5501
5821
  await sqlDatabaseManager.removeDatabase(tenantId, testKey);
5502
5822
  return {
@@ -5563,12 +5883,12 @@ function registerDatabaseConfigRoutes(app2) {
5563
5883
 
5564
5884
  // src/controllers/metrics-configs.ts
5565
5885
  import {
5566
- getStoreLattice as getStoreLattice9,
5886
+ getStoreLattice as getStoreLattice8,
5567
5887
  metricsServerManager as metricsServerManager2,
5568
5888
  SemanticMetricsClient as SemanticMetricsClient2
5569
5889
  } from "@axiom-lattice/core";
5570
5890
  import { randomUUID as randomUUID6 } from "crypto";
5571
- function getTenantId12(request) {
5891
+ function getTenantId10(request) {
5572
5892
  const userTenantId = request.user?.tenantId;
5573
5893
  if (userTenantId) {
5574
5894
  return userTenantId;
@@ -5576,9 +5896,9 @@ function getTenantId12(request) {
5576
5896
  return request.headers["x-tenant-id"] || "default";
5577
5897
  }
5578
5898
  async function getMetricsServerConfigList(request, reply) {
5579
- const tenantId = getTenantId12(request);
5899
+ const tenantId = getTenantId10(request);
5580
5900
  try {
5581
- const storeLattice = getStoreLattice9("default", "metrics");
5901
+ const storeLattice = getStoreLattice8("default", "metrics");
5582
5902
  const store = storeLattice.store;
5583
5903
  const configs = await store.getAllConfigs(tenantId);
5584
5904
  return {
@@ -5602,10 +5922,10 @@ async function getMetricsServerConfigList(request, reply) {
5602
5922
  }
5603
5923
  }
5604
5924
  async function getMetricsServerConfig(request, reply) {
5605
- const tenantId = getTenantId12(request);
5925
+ const tenantId = getTenantId10(request);
5606
5926
  const { key } = request.params;
5607
5927
  try {
5608
- const storeLattice = getStoreLattice9("default", "metrics");
5928
+ const storeLattice = getStoreLattice8("default", "metrics");
5609
5929
  const store = storeLattice.store;
5610
5930
  const config = await store.getConfigByKey(tenantId, key);
5611
5931
  if (!config) {
@@ -5628,10 +5948,10 @@ async function getMetricsServerConfig(request, reply) {
5628
5948
  }
5629
5949
  }
5630
5950
  async function createMetricsServerConfig(request, reply) {
5631
- const tenantId = getTenantId12(request);
5951
+ const tenantId = getTenantId10(request);
5632
5952
  const body = request.body;
5633
5953
  try {
5634
- const storeLattice = getStoreLattice9("default", "metrics");
5954
+ const storeLattice = getStoreLattice8("default", "metrics");
5635
5955
  const store = storeLattice.store;
5636
5956
  const existing = await store.getConfigByKey(tenantId, body.key);
5637
5957
  if (existing) {
@@ -5679,11 +5999,11 @@ async function createMetricsServerConfig(request, reply) {
5679
5999
  }
5680
6000
  }
5681
6001
  async function updateMetricsServerConfig(request, reply) {
5682
- const tenantId = getTenantId12(request);
6002
+ const tenantId = getTenantId10(request);
5683
6003
  const { key } = request.params;
5684
6004
  const updates = request.body;
5685
6005
  try {
5686
- const storeLattice = getStoreLattice9("default", "metrics");
6006
+ const storeLattice = getStoreLattice8("default", "metrics");
5687
6007
  const store = storeLattice.store;
5688
6008
  const existing = await store.getConfigByKey(tenantId, key);
5689
6009
  if (!existing) {
@@ -5730,10 +6050,10 @@ async function updateMetricsServerConfig(request, reply) {
5730
6050
  }
5731
6051
  }
5732
6052
  async function deleteMetricsServerConfig(request, reply) {
5733
- const tenantId = getTenantId12(request);
6053
+ const tenantId = getTenantId10(request);
5734
6054
  const { keyOrId } = request.params;
5735
6055
  try {
5736
- const storeLattice = getStoreLattice9("default", "metrics");
6056
+ const storeLattice = getStoreLattice8("default", "metrics");
5737
6057
  const store = storeLattice.store;
5738
6058
  let config = await store.getConfigByKey(tenantId, keyOrId);
5739
6059
  let configKey = keyOrId;
@@ -5777,10 +6097,10 @@ async function deleteMetricsServerConfig(request, reply) {
5777
6097
  }
5778
6098
  }
5779
6099
  async function testMetricsServerConnection(request, reply) {
5780
- const tenantId = getTenantId12(request);
6100
+ const tenantId = getTenantId10(request);
5781
6101
  const { key } = request.params;
5782
6102
  try {
5783
- const storeLattice = getStoreLattice9("default", "metrics");
6103
+ const storeLattice = getStoreLattice8("default", "metrics");
5784
6104
  const store = storeLattice.store;
5785
6105
  const config = await store.getConfigByKey(tenantId, key);
5786
6106
  if (!config) {
@@ -5828,10 +6148,10 @@ async function testMetricsServerConnection(request, reply) {
5828
6148
  }
5829
6149
  }
5830
6150
  async function listAvailableMetrics(request, reply) {
5831
- const tenantId = getTenantId12(request);
6151
+ const tenantId = getTenantId10(request);
5832
6152
  const { key } = request.params;
5833
6153
  try {
5834
- const storeLattice = getStoreLattice9("default", "metrics");
6154
+ const storeLattice = getStoreLattice8("default", "metrics");
5835
6155
  const store = storeLattice.store;
5836
6156
  const config = await store.getConfigByKey(tenantId, key);
5837
6157
  if (!config) {
@@ -5866,11 +6186,11 @@ async function listAvailableMetrics(request, reply) {
5866
6186
  }
5867
6187
  }
5868
6188
  async function queryMetricsData(request, reply) {
5869
- const tenantId = getTenantId12(request);
6189
+ const tenantId = getTenantId10(request);
5870
6190
  const { key } = request.params;
5871
6191
  const { metricName, startTime, endTime, step, labels } = request.body;
5872
6192
  try {
5873
- const storeLattice = getStoreLattice9("default", "metrics");
6193
+ const storeLattice = getStoreLattice8("default", "metrics");
5874
6194
  const store = storeLattice.store;
5875
6195
  const config = await store.getConfigByKey(tenantId, key);
5876
6196
  if (!config) {
@@ -5914,10 +6234,10 @@ async function queryMetricsData(request, reply) {
5914
6234
  }
5915
6235
  }
5916
6236
  async function getDataSources(request, reply) {
5917
- const tenantId = getTenantId12(request);
6237
+ const tenantId = getTenantId10(request);
5918
6238
  const { key } = request.params;
5919
6239
  try {
5920
- const storeLattice = getStoreLattice9("default", "metrics");
6240
+ const storeLattice = getStoreLattice8("default", "metrics");
5921
6241
  const store = storeLattice.store;
5922
6242
  const config = await store.getConfigByKey(tenantId, key);
5923
6243
  if (!config) {
@@ -5955,10 +6275,10 @@ async function getDataSources(request, reply) {
5955
6275
  }
5956
6276
  }
5957
6277
  async function getDatasourceMetrics(request, reply) {
5958
- const tenantId = getTenantId12(request);
6278
+ const tenantId = getTenantId10(request);
5959
6279
  const { key, datasourceId } = request.params;
5960
6280
  try {
5961
- const storeLattice = getStoreLattice9("default", "metrics");
6281
+ const storeLattice = getStoreLattice8("default", "metrics");
5962
6282
  const store = storeLattice.store;
5963
6283
  const config = await store.getConfigByKey(tenantId, key);
5964
6284
  if (!config) {
@@ -5992,11 +6312,11 @@ async function getDatasourceMetrics(request, reply) {
5992
6312
  }
5993
6313
  }
5994
6314
  async function querySemanticMetrics(request, reply) {
5995
- const tenantId = getTenantId12(request);
6315
+ const tenantId = getTenantId10(request);
5996
6316
  const { key } = request.params;
5997
6317
  const body = request.body;
5998
6318
  try {
5999
- const storeLattice = getStoreLattice9("default", "metrics");
6319
+ const storeLattice = getStoreLattice8("default", "metrics");
6000
6320
  const store = storeLattice.store;
6001
6321
  const config = await store.getConfigByKey(tenantId, key);
6002
6322
  if (!config) {
@@ -6307,13 +6627,13 @@ function connectionRoutes(fastify2) {
6307
6627
  }
6308
6628
 
6309
6629
  // src/controllers/eval.ts
6310
- import { getStoreLattice as getStoreLattice11 } from "@axiom-lattice/core";
6630
+ import { getStoreLattice as getStoreLattice10 } from "@axiom-lattice/core";
6311
6631
  import { v4 as uuidv44 } from "uuid";
6312
6632
 
6313
6633
  // src/services/eval-runner.ts
6314
6634
  import { EventEmitter } from "events";
6315
6635
  import {
6316
- getStoreLattice as getStoreLattice10,
6636
+ getStoreLattice as getStoreLattice9,
6317
6637
  modelLatticeManager as modelLatticeManager2,
6318
6638
  LatticeEvalProject
6319
6639
  } from "@axiom-lattice/core";
@@ -6371,21 +6691,21 @@ var EvalRunner = class {
6371
6691
  for (const suite of suites) {
6372
6692
  let cases = await store.getCasesBySuite(tenantId, suite.id);
6373
6693
  if (caseIds && caseIds.length > 0) {
6374
- cases = cases.filter((c) => caseIds.includes(c.id));
6694
+ cases = cases.filter((c2) => caseIds.includes(c2.id));
6375
6695
  if (cases.length === 0) continue;
6376
6696
  }
6377
6697
  totalCases += cases.length;
6378
6698
  evalSuites.push({
6379
6699
  suiteName: suite.name,
6380
- cases: cases.map((c) => ({
6381
- caseId: c.id,
6382
- input: { message: c.inputMessage, files: c.inputFiles },
6383
- steps: c.steps,
6700
+ cases: cases.map((c2) => ({
6701
+ caseId: c2.id,
6702
+ input: { message: c2.inputMessage, files: c2.inputFiles },
6703
+ steps: c2.steps,
6384
6704
  output: { type: "message_content" },
6385
- interruptPolicy: c.interruptPolicy,
6705
+ interruptPolicy: c2.interruptPolicy,
6386
6706
  eval: {
6387
- content_assertion: c.contentAssertion,
6388
- eval_rubrics: c.rubrics?.map((r) => ({
6707
+ content_assertion: c2.contentAssertion,
6708
+ eval_rubrics: c2.rubrics?.map((r) => ({
6389
6709
  dimension: r.name,
6390
6710
  weight: r.weight,
6391
6711
  description: r.description
@@ -6557,13 +6877,13 @@ var EvalRunner = class {
6557
6877
  return this.runs.has(runId);
6558
6878
  }
6559
6879
  getEvalStore() {
6560
- return getStoreLattice10("default", "eval").store;
6880
+ return getStoreLattice9("default", "eval").store;
6561
6881
  }
6562
6882
  };
6563
6883
  var evalRunner = new EvalRunner();
6564
6884
 
6565
6885
  // src/controllers/eval.ts
6566
- function getTenantId13(request) {
6886
+ function getTenantId11(request) {
6567
6887
  const userTenantId = request.user?.tenantId;
6568
6888
  if (userTenantId) {
6569
6889
  return userTenantId;
@@ -6571,11 +6891,11 @@ function getTenantId13(request) {
6571
6891
  return request.headers["x-tenant-id"] || "default";
6572
6892
  }
6573
6893
  function getEvalStore() {
6574
- return getStoreLattice11("default", "eval").store;
6894
+ return getStoreLattice10("default", "eval").store;
6575
6895
  }
6576
6896
  async function createProject(request, reply) {
6577
6897
  try {
6578
- const tenantId = getTenantId13(request);
6898
+ const tenantId = getTenantId11(request);
6579
6899
  const store = getEvalStore();
6580
6900
  const id = uuidv44();
6581
6901
  const data = request.body;
@@ -6632,7 +6952,7 @@ async function createProject(request, reply) {
6632
6952
  }
6633
6953
  async function listProjects(request, reply) {
6634
6954
  try {
6635
- const tenantId = getTenantId13(request);
6955
+ const tenantId = getTenantId11(request);
6636
6956
  const store = getEvalStore();
6637
6957
  let projects = await store.getProjectsByTenant(tenantId);
6638
6958
  if (projects.length === 0) {
@@ -6666,7 +6986,7 @@ async function listProjects(request, reply) {
6666
6986
  }
6667
6987
  async function getProject(request, reply) {
6668
6988
  try {
6669
- const tenantId = getTenantId13(request);
6989
+ const tenantId = getTenantId11(request);
6670
6990
  const store = getEvalStore();
6671
6991
  const { pid } = request.params;
6672
6992
  const project = await store.getProjectById(tenantId, pid);
@@ -6686,7 +7006,7 @@ async function getProject(request, reply) {
6686
7006
  }
6687
7007
  async function updateProject(request, reply) {
6688
7008
  try {
6689
- const tenantId = getTenantId13(request);
7009
+ const tenantId = getTenantId11(request);
6690
7010
  const store = getEvalStore();
6691
7011
  const { pid } = request.params;
6692
7012
  const existing = await store.getProjectById(tenantId, pid);
@@ -6729,7 +7049,7 @@ async function updateProject(request, reply) {
6729
7049
  }
6730
7050
  async function deleteProject(request, reply) {
6731
7051
  try {
6732
- const tenantId = getTenantId13(request);
7052
+ const tenantId = getTenantId11(request);
6733
7053
  const store = getEvalStore();
6734
7054
  const { pid } = request.params;
6735
7055
  const existing = await store.getProjectById(tenantId, pid);
@@ -6748,7 +7068,7 @@ async function deleteProject(request, reply) {
6748
7068
  }
6749
7069
  async function createSuite(request, reply) {
6750
7070
  try {
6751
- const tenantId = getTenantId13(request);
7071
+ const tenantId = getTenantId11(request);
6752
7072
  const store = getEvalStore();
6753
7073
  const { pid } = request.params;
6754
7074
  const project = await store.getProjectById(tenantId, pid);
@@ -6770,7 +7090,7 @@ async function createSuite(request, reply) {
6770
7090
  }
6771
7091
  async function updateSuite(request, reply) {
6772
7092
  try {
6773
- const tenantId = getTenantId13(request);
7093
+ const tenantId = getTenantId11(request);
6774
7094
  const store = getEvalStore();
6775
7095
  const { sid } = request.params;
6776
7096
  const existing = await store.getSuiteById(tenantId, sid);
@@ -6790,7 +7110,7 @@ async function updateSuite(request, reply) {
6790
7110
  }
6791
7111
  async function deleteSuite(request, reply) {
6792
7112
  try {
6793
- const tenantId = getTenantId13(request);
7113
+ const tenantId = getTenantId11(request);
6794
7114
  const store = getEvalStore();
6795
7115
  const { sid } = request.params;
6796
7116
  const existing = await store.getSuiteById(tenantId, sid);
@@ -6809,7 +7129,7 @@ async function deleteSuite(request, reply) {
6809
7129
  }
6810
7130
  async function createCase(request, reply) {
6811
7131
  try {
6812
- const tenantId = getTenantId13(request);
7132
+ const tenantId = getTenantId11(request);
6813
7133
  const store = getEvalStore();
6814
7134
  const { sid } = request.params;
6815
7135
  const suite = await store.getSuiteById(tenantId, sid);
@@ -6839,7 +7159,7 @@ async function createCase(request, reply) {
6839
7159
  }
6840
7160
  async function listCasesForSuite(request, reply) {
6841
7161
  try {
6842
- const tenantId = getTenantId13(request);
7162
+ const tenantId = getTenantId11(request);
6843
7163
  const store = getEvalStore();
6844
7164
  const { sid } = request.params;
6845
7165
  const cases = await store.getCasesBySuite(tenantId, sid);
@@ -6855,7 +7175,7 @@ async function listCasesForSuite(request, reply) {
6855
7175
  }
6856
7176
  async function updateCase(request, reply) {
6857
7177
  try {
6858
- const tenantId = getTenantId13(request);
7178
+ const tenantId = getTenantId11(request);
6859
7179
  const store = getEvalStore();
6860
7180
  const { cid } = request.params;
6861
7181
  const existing = await store.getCaseById(tenantId, cid);
@@ -6875,7 +7195,7 @@ async function updateCase(request, reply) {
6875
7195
  }
6876
7196
  async function deleteCase(request, reply) {
6877
7197
  try {
6878
- const tenantId = getTenantId13(request);
7198
+ const tenantId = getTenantId11(request);
6879
7199
  const store = getEvalStore();
6880
7200
  const { cid } = request.params;
6881
7201
  const existing = await store.getCaseById(tenantId, cid);
@@ -6907,7 +7227,7 @@ function registerEvalRoutes(app2) {
6907
7227
  app2.delete("/api/eval/projects/:pid/suites/:sid/cases/:cid", deleteCase);
6908
7228
  app2.post("/api/eval/projects/:pid/runs", async (request, reply) => {
6909
7229
  try {
6910
- const tenantId = getTenantId13(request);
7230
+ const tenantId = getTenantId11(request);
6911
7231
  const { pid } = request.params;
6912
7232
  const body = request.body ?? {};
6913
7233
  const bodyWs = request.headers["x-workspace-id"] || "";
@@ -6926,7 +7246,7 @@ function registerEvalRoutes(app2) {
6926
7246
  });
6927
7247
  app2.get("/api/eval/runs", async (request, reply) => {
6928
7248
  try {
6929
- const tenantId = getTenantId13(request);
7249
+ const tenantId = getTenantId11(request);
6930
7250
  const query = request.query;
6931
7251
  const runs = await getEvalStore().getRunsByTenant(tenantId, { projectId: query.project_id, status: query.status });
6932
7252
  reply.send({ success: true, message: "Ok", data: { records: runs, total: runs.length } });
@@ -6936,7 +7256,7 @@ function registerEvalRoutes(app2) {
6936
7256
  });
6937
7257
  app2.get("/api/eval/runs/:rid", async (request, reply) => {
6938
7258
  try {
6939
- const tenantId = getTenantId13(request);
7259
+ const tenantId = getTenantId11(request);
6940
7260
  const { rid } = request.params;
6941
7261
  const run = await getEvalStore().getRunById(tenantId, rid);
6942
7262
  if (!run) return reply.status(404).send({ success: false, message: "Run not found" });
@@ -6958,7 +7278,7 @@ function registerEvalRoutes(app2) {
6958
7278
  const emitter = evalRunner.getEventEmitter();
6959
7279
  const eventKey = `run:${rid}`;
6960
7280
  if (!evalRunner.isRunning(rid)) {
6961
- const tenantId = getTenantId13(request);
7281
+ const tenantId = getTenantId11(request);
6962
7282
  const run = await getEvalStore().getRunById(tenantId, rid);
6963
7283
  if (run) {
6964
7284
  const eventType = run.status === "completed" ? "completed" : "error";
@@ -6997,7 +7317,7 @@ data: ${JSON.stringify(event.data)}
6997
7317
  });
6998
7318
  app2.delete("/api/eval/runs/:rid", async (request, reply) => {
6999
7319
  try {
7000
- const tenantId = getTenantId13(request);
7320
+ const tenantId = getTenantId11(request);
7001
7321
  const { rid } = request.params;
7002
7322
  const deleted = await getEvalStore().deleteRun(tenantId, rid);
7003
7323
  if (!deleted) return reply.status(404).send({ success: false, message: "Run not found" });
@@ -7008,7 +7328,7 @@ data: ${JSON.stringify(event.data)}
7008
7328
  });
7009
7329
  app2.get("/api/eval/reports/projects/:pid", async (request, reply) => {
7010
7330
  try {
7011
- const tenantId = getTenantId13(request);
7331
+ const tenantId = getTenantId11(request);
7012
7332
  const { pid } = request.params;
7013
7333
  const report = await getEvalStore().getProjectReport(tenantId, pid);
7014
7334
  if (!report) return reply.status(404).send({ success: false, message: "Project not found" });
@@ -7020,11 +7340,11 @@ data: ${JSON.stringify(event.data)}
7020
7340
  }
7021
7341
 
7022
7342
  // src/controllers/users.ts
7023
- import { getStoreLattice as getStoreLattice12 } from "@axiom-lattice/core";
7343
+ import { getStoreLattice as getStoreLattice11 } from "@axiom-lattice/core";
7024
7344
  import { v4 as uuidv45 } from "uuid";
7025
7345
  var UsersController = class {
7026
7346
  constructor() {
7027
- this.userStore = getStoreLattice12("default", "user").store;
7347
+ this.userStore = getStoreLattice11("default", "user").store;
7028
7348
  }
7029
7349
  async listUsers(request, reply) {
7030
7350
  const { email } = request.query;
@@ -7102,11 +7422,11 @@ function registerUserRoutes(app2) {
7102
7422
  }
7103
7423
 
7104
7424
  // src/controllers/tenants.ts
7105
- import { getStoreLattice as getStoreLattice13 } from "@axiom-lattice/core";
7425
+ import { getStoreLattice as getStoreLattice12 } from "@axiom-lattice/core";
7106
7426
  import { v4 as uuidv46 } from "uuid";
7107
7427
  var TenantsController = class {
7108
7428
  constructor() {
7109
- this.tenantStore = getStoreLattice13("default", "tenant").store;
7429
+ this.tenantStore = getStoreLattice12("default", "tenant").store;
7110
7430
  }
7111
7431
  // ==================== Tenant CRUD ====================
7112
7432
  async listTenants(request, reply) {
@@ -7170,8 +7490,9 @@ function registerTenantRoutes(app2) {
7170
7490
  }
7171
7491
 
7172
7492
  // src/controllers/auth.ts
7173
- import { getStoreLattice as getStoreLattice14 } from "@axiom-lattice/core";
7493
+ import { getStoreLattice as getStoreLattice13 } from "@axiom-lattice/core";
7174
7494
  import { v4 as uuidv47 } from "uuid";
7495
+ import { createHmac, randomBytes, timingSafeEqual } from "crypto";
7175
7496
  var defaultAuthConfig = {
7176
7497
  autoApproveUsers: true,
7177
7498
  allowTenantRegistration: true,
@@ -7179,9 +7500,9 @@ var defaultAuthConfig = {
7179
7500
  };
7180
7501
  var AuthController = class {
7181
7502
  constructor(config = {}) {
7182
- this.userStore = getStoreLattice14("default", "user").store;
7183
- this.tenantStore = getStoreLattice14("default", "tenant").store;
7184
- 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;
7185
7506
  this.config = { ...defaultAuthConfig, ...config };
7186
7507
  }
7187
7508
  async register(request, reply) {
@@ -7252,7 +7573,8 @@ var AuthController = class {
7252
7573
  });
7253
7574
  }
7254
7575
  const tenantLinks = await this.userTenantLinkStore.getTenantsByUser(user.id);
7255
- 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);
7256
7578
  return {
7257
7579
  success: true,
7258
7580
  data: {
@@ -7339,7 +7661,7 @@ var AuthController = class {
7339
7661
  const linkMeta = link?.metadata || {};
7340
7662
  let personalAssistant = null;
7341
7663
  if (linkMeta.personalAssistantId) {
7342
- const assistantStore = getStoreLattice14("default", "assistant").store;
7664
+ const assistantStore = getStoreLattice13("default", "assistant").store;
7343
7665
  const exists = await assistantStore.hasAssistant(tenantId, linkMeta.personalAssistantId);
7344
7666
  if (exists) {
7345
7667
  personalAssistant = {
@@ -7458,6 +7780,9 @@ var AuthController = class {
7458
7780
  return hashedPassword === hash;
7459
7781
  }
7460
7782
  async generateToken(userId, tenantId) {
7783
+ if (!this.config.jwtSecret) {
7784
+ throw new Error("AUTH_TOKEN_SECRET is required to issue console sessions");
7785
+ }
7461
7786
  const payload = {
7462
7787
  userId,
7463
7788
  exp: Date.now() + this.config.tokenExpiration * 1e3
@@ -7465,7 +7790,7 @@ var AuthController = class {
7465
7790
  if (tenantId) {
7466
7791
  payload.tenantId = tenantId;
7467
7792
  }
7468
- return btoa(JSON.stringify(payload));
7793
+ return createConsoleSessionToken(payload, this.config.jwtSecret);
7469
7794
  }
7470
7795
  async changePassword(request, reply) {
7471
7796
  const userId = request.user?.id;
@@ -7519,12 +7844,46 @@ var AuthController = class {
7519
7844
  }
7520
7845
  }
7521
7846
  };
7522
- function extractUserFromAuthHeader(authHeader) {
7523
- 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;
7524
7869
  const token = authHeader.substring(7);
7525
- try {
7526
- const payload = JSON.parse(atob(token));
7527
- 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
+ }
7528
7887
  return {
7529
7888
  id: payload.userId,
7530
7889
  tenantId: payload.tenantId
@@ -7533,10 +7892,43 @@ function extractUserFromAuthHeader(authHeader) {
7533
7892
  return void 0;
7534
7893
  }
7535
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
+ }
7536
7925
  function registerAuthRoutes(app2, config) {
7537
7926
  const controller = new AuthController(config);
7538
7927
  const authHook = async (request, reply) => {
7539
- const user = extractUserFromAuthHeader(request.headers.authorization);
7928
+ const user = extractUserFromAuthHeader(
7929
+ request.headers.authorization,
7930
+ config?.jwtSecret
7931
+ );
7540
7932
  if (!user) {
7541
7933
  return reply.status(401).send({
7542
7934
  success: false,
@@ -7889,16 +8281,32 @@ function setChannelControllerDeps(deps) {
7889
8281
  _adapterRegistry = deps.adapterRegistry;
7890
8282
  _router = deps.router;
7891
8283
  }
7892
- function getTenantId14(request) {
8284
+ function getTenantId12(request) {
7893
8285
  const userTenantId = request.user?.tenantId;
7894
8286
  if (userTenantId) {
7895
8287
  return userTenantId;
7896
8288
  }
7897
8289
  return request.headers["x-tenant-id"] || "default";
7898
8290
  }
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
+ }
7899
8307
  async function getInstallationStore() {
7900
- const { getStoreLattice: getStoreLattice31 } = await import("@axiom-lattice/core");
7901
- const store = getStoreLattice31("default", "channelInstallation").store;
8308
+ const { getStoreLattice: getStoreLattice27 } = await import("@axiom-lattice/core");
8309
+ const store = getStoreLattice27("default", "channelInstallation").store;
7902
8310
  if (store) return store;
7903
8311
  const { PostgreSQLChannelInstallationStore } = await import("@axiom-lattice/pg-stores");
7904
8312
  const databaseUrl = process.env.DATABASE_URL;
@@ -7910,7 +8318,7 @@ async function getInstallationStore() {
7910
8318
  });
7911
8319
  }
7912
8320
  async function getChannelInstallationList(request, reply) {
7913
- const tenantId = getTenantId14(request);
8321
+ const tenantId = getTenantId12(request);
7914
8322
  const { channel } = request.query;
7915
8323
  try {
7916
8324
  const store = await getInstallationStore();
@@ -7936,7 +8344,7 @@ async function getChannelInstallationList(request, reply) {
7936
8344
  }
7937
8345
  }
7938
8346
  async function getChannelInstallation(request, reply) {
7939
- const tenantId = getTenantId14(request);
8347
+ const tenantId = getTenantId12(request);
7940
8348
  const { installationId } = request.params;
7941
8349
  try {
7942
8350
  const store = await getInstallationStore();
@@ -7969,9 +8377,25 @@ async function getChannelInstallation(request, reply) {
7969
8377
  }
7970
8378
  }
7971
8379
  async function createChannelInstallation(request, reply) {
7972
- const tenantId = getTenantId14(request);
7973
- const body = request.body;
7974
- 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;
7975
8399
  if (!body.channel) {
7976
8400
  reply.code(400);
7977
8401
  return {
@@ -8004,7 +8428,7 @@ async function createChannelInstallation(request, reply) {
8004
8428
  }
8005
8429
  }
8006
8430
  const store = await getInstallationStore();
8007
- const installationId = body.id || randomUUID7();
8431
+ const installationId = randomUUID7();
8008
8432
  const installation = await store.createInstallation(
8009
8433
  tenantId,
8010
8434
  installationId,
@@ -8028,7 +8452,8 @@ async function createChannelInstallation(request, reply) {
8028
8452
  };
8029
8453
  } catch (error) {
8030
8454
  console.error("Failed to create channel installation:", error);
8031
- 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") {
8032
8457
  reply.code(409);
8033
8458
  return {
8034
8459
  success: false,
@@ -8042,7 +8467,7 @@ async function createChannelInstallation(request, reply) {
8042
8467
  }
8043
8468
  }
8044
8469
  async function updateChannelInstallation(request, reply) {
8045
- const tenantId = getTenantId14(request);
8470
+ const tenantId = getTenantId12(request);
8046
8471
  const { installationId } = request.params;
8047
8472
  const body = request.body;
8048
8473
  try {
@@ -8096,7 +8521,7 @@ async function updateChannelInstallation(request, reply) {
8096
8521
  }
8097
8522
  }
8098
8523
  async function deleteChannelInstallation(request, reply) {
8099
- const tenantId = getTenantId14(request);
8524
+ const tenantId = getTenantId12(request);
8100
8525
  const { installationId } = request.params;
8101
8526
  try {
8102
8527
  const store = await getInstallationStore();
@@ -8157,13 +8582,13 @@ function registerChannelInstallationRoutes(app2) {
8157
8582
 
8158
8583
  // src/controllers/channel-bindings.ts
8159
8584
  import { getBindingRegistry } from "@axiom-lattice/core";
8160
- function getTenantId15(request) {
8585
+ function getTenantId13(request) {
8161
8586
  const userTenantId = request.user?.tenantId;
8162
8587
  if (userTenantId) return userTenantId;
8163
8588
  return request.headers["x-tenant-id"] || "default";
8164
8589
  }
8165
8590
  async function getBindingList(request, _reply) {
8166
- const tenantId = getTenantId15(request);
8591
+ const tenantId = getTenantId13(request);
8167
8592
  const { channel, agentId, channelInstallationId, limit, offset } = request.query;
8168
8593
  try {
8169
8594
  const registry = getBindingRegistry();
@@ -8175,7 +8600,7 @@ async function getBindingList(request, _reply) {
8175
8600
  }
8176
8601
  }
8177
8602
  async function getBinding(request, reply) {
8178
- const tenantId = getTenantId15(request);
8603
+ const tenantId = getTenantId13(request);
8179
8604
  try {
8180
8605
  const registry = getBindingRegistry();
8181
8606
  const bindings = await registry.list({ tenantId });
@@ -8191,7 +8616,7 @@ async function getBinding(request, reply) {
8191
8616
  }
8192
8617
  }
8193
8618
  async function createBinding(request, reply) {
8194
- const tenantId = getTenantId15(request);
8619
+ const tenantId = getTenantId13(request);
8195
8620
  try {
8196
8621
  const registry = getBindingRegistry();
8197
8622
  const binding = await registry.create({ ...request.body, tenantId });
@@ -8205,7 +8630,7 @@ async function createBinding(request, reply) {
8205
8630
  }
8206
8631
  async function updateBinding(request, reply) {
8207
8632
  try {
8208
- const tenantId = getTenantId15(request);
8633
+ const tenantId = getTenantId13(request);
8209
8634
  const registry = getBindingRegistry();
8210
8635
  const bindings = await registry.list({ tenantId });
8211
8636
  const existing = bindings.find((b) => b.id === request.params.id);
@@ -8223,7 +8648,7 @@ async function updateBinding(request, reply) {
8223
8648
  }
8224
8649
  async function deleteBinding(request, reply) {
8225
8650
  try {
8226
- const tenantId = getTenantId15(request);
8651
+ const tenantId = getTenantId13(request);
8227
8652
  const registry = getBindingRegistry();
8228
8653
  const bindings = await registry.list({ tenantId });
8229
8654
  const existing = bindings.find((b) => b.id === request.params.id);
@@ -8240,7 +8665,7 @@ async function deleteBinding(request, reply) {
8240
8665
  }
8241
8666
  }
8242
8667
  async function resolveBinding(request, _reply) {
8243
- const tenantId = getTenantId15(request);
8668
+ const tenantId = getTenantId13(request);
8244
8669
  const { channel, senderId, channelInstallationId } = request.query;
8245
8670
  try {
8246
8671
  const registry = getBindingRegistry();
@@ -8267,7 +8692,7 @@ function registerChannelBindingRoutes(app2) {
8267
8692
 
8268
8693
  // src/controllers/menu-items.ts
8269
8694
  import { getMenuRegistry } from "@axiom-lattice/core";
8270
- function getTenantId16(request) {
8695
+ function getTenantId14(request) {
8271
8696
  const userTenantId = request.user?.tenantId;
8272
8697
  if (userTenantId) return userTenantId;
8273
8698
  return request.headers["x-tenant-id"] || "default";
@@ -8276,7 +8701,7 @@ function errorMessage(err) {
8276
8701
  return err instanceof Error ? err.message : "Unexpected error";
8277
8702
  }
8278
8703
  async function getMenuItemList(request, _reply) {
8279
- const tenantId = getTenantId16(request);
8704
+ const tenantId = getTenantId14(request);
8280
8705
  try {
8281
8706
  const registry = getMenuRegistry();
8282
8707
  const items = await registry.list({ tenantId, menuTarget: request.query.menuTarget });
@@ -8288,7 +8713,7 @@ async function getMenuItemList(request, _reply) {
8288
8713
  }
8289
8714
  }
8290
8715
  async function getMenuItem(request, reply) {
8291
- const tenantId = getTenantId16(request);
8716
+ const tenantId = getTenantId14(request);
8292
8717
  try {
8293
8718
  const registry = getMenuRegistry();
8294
8719
  const item = await registry.getById(request.params.id);
@@ -8305,7 +8730,7 @@ async function getMenuItem(request, reply) {
8305
8730
  }
8306
8731
  }
8307
8732
  async function createMenuItem(request, reply) {
8308
- const tenantId = getTenantId16(request);
8733
+ const tenantId = getTenantId14(request);
8309
8734
  try {
8310
8735
  const registry = getMenuRegistry();
8311
8736
  const item = await registry.create({ ...request.body, tenantId });
@@ -8320,7 +8745,7 @@ async function createMenuItem(request, reply) {
8320
8745
  }
8321
8746
  async function updateMenuItem(request, reply) {
8322
8747
  try {
8323
- const tenantId = getTenantId16(request);
8748
+ const tenantId = getTenantId14(request);
8324
8749
  const registry = getMenuRegistry();
8325
8750
  const existing = await registry.getById(request.params.id);
8326
8751
  if (!existing || existing.tenantId !== tenantId) {
@@ -8338,7 +8763,7 @@ async function updateMenuItem(request, reply) {
8338
8763
  }
8339
8764
  async function deleteMenuItem(request, reply) {
8340
8765
  try {
8341
- const tenantId = getTenantId16(request);
8766
+ const tenantId = getTenantId14(request);
8342
8767
  const registry = getMenuRegistry();
8343
8768
  const existing = await registry.getById(request.params.id);
8344
8769
  if (!existing || existing.tenantId !== tenantId) {
@@ -8488,12 +8913,12 @@ async function handleBridgeProxy(request, reply) {
8488
8913
  };
8489
8914
  const timeout = 3e5;
8490
8915
  try {
8491
- const response = await new Promise((resolve3, reject) => {
8916
+ const response = await new Promise((resolve2, reject) => {
8492
8917
  const timer = setTimeout(() => {
8493
8918
  pending.delete(requestId);
8494
8919
  reject(new Error(`Bridge request timeout after ${timeout}ms`));
8495
8920
  }, timeout);
8496
- pending.set(requestId, { resolve: resolve3, reject, timer, agentId });
8921
+ pending.set(requestId, { resolve: resolve2, reject, timer, agentId });
8497
8922
  try {
8498
8923
  if (conn.ws.readyState !== conn.ws.OPEN) {
8499
8924
  pending.delete(requestId);
@@ -8617,11 +9042,11 @@ var registerSTTRoutes = (app2) => {
8617
9042
  };
8618
9043
 
8619
9044
  // src/controllers/local-a2a.ts
8620
- import { getStoreLattice as getStoreLattice15 } from "@axiom-lattice/core";
9045
+ import { getStoreLattice as getStoreLattice14 } from "@axiom-lattice/core";
8621
9046
  import {
8622
9047
  AgentType as AgentType2
8623
9048
  } from "@axiom-lattice/protocols";
8624
- function getTenantId17(request) {
9049
+ function getTenantId15(request) {
8625
9050
  const userTenantId = request.user?.tenantId;
8626
9051
  if (userTenantId) return userTenantId;
8627
9052
  return request.headers["x-tenant-id"] || "default";
@@ -8633,7 +9058,7 @@ function getLocalRuntime(assistant) {
8633
9058
  return graphDefinition.localRuntime;
8634
9059
  }
8635
9060
  async function getLocalA2AAssistants(tenantId) {
8636
- const storeLattice = getStoreLattice15("default", "assistant");
9061
+ const storeLattice = getStoreLattice14("default", "assistant");
8637
9062
  const assistants = await storeLattice.store.getAllAssistants(tenantId);
8638
9063
  const results = [];
8639
9064
  for (const assistant of assistants) {
@@ -8645,7 +9070,7 @@ async function getLocalA2AAssistants(tenantId) {
8645
9070
  return results;
8646
9071
  }
8647
9072
  async function getLocalA2AAssistant(tenantId, assistantId) {
8648
- const storeLattice = getStoreLattice15("default", "assistant");
9073
+ const storeLattice = getStoreLattice14("default", "assistant");
8649
9074
  const assistant = await storeLattice.store.getAssistantById(tenantId, assistantId);
8650
9075
  if (!assistant) return null;
8651
9076
  const runtime = getLocalRuntime(assistant);
@@ -8658,7 +9083,7 @@ async function requireManagedAssistant(request, reply) {
8658
9083
  reply.status(400).send({ success: false, error: "assistantId is required" });
8659
9084
  return null;
8660
9085
  }
8661
- const registered = await getLocalA2AAssistant(getTenantId17(request), assistantId);
9086
+ const registered = await getLocalA2AAssistant(getTenantId15(request), assistantId);
8662
9087
  if (!registered) {
8663
9088
  reply.status(404).send({
8664
9089
  success: false,
@@ -8669,7 +9094,7 @@ async function requireManagedAssistant(request, reply) {
8669
9094
  return { assistantId, localRuntime: registered.localRuntime };
8670
9095
  }
8671
9096
  async function getLocalA2AStatus(request, reply) {
8672
- const tenantId = getTenantId17(request);
9097
+ const tenantId = getTenantId15(request);
8673
9098
  const managed = await getLocalA2AAssistants(tenantId);
8674
9099
  const states = await Promise.all(
8675
9100
  managed.map(
@@ -8873,6 +9298,294 @@ function registerA2ATestConnectionRoute(app2) {
8873
9298
  });
8874
9299
  }
8875
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
+
8876
9589
  // src/routes/index.ts
8877
9590
  var registerLatticeRoutes = (app2, channelDeps) => {
8878
9591
  registerA2ABridgeRoutes(app2);
@@ -8927,13 +9640,7 @@ var registerLatticeRoutes = (app2, channelDeps) => {
8927
9640
  app2.post("/api/personal-assistant", createPersonalAssistant);
8928
9641
  app2.get("/api/personal-assistant", getPersonalAssistant);
8929
9642
  app2.delete("/api/personal-assistant", deletePersonalAssistant);
8930
- app2.get("/api/tasks", listTasks);
8931
- app2.get("/api/tasks/:id", getTask);
8932
- app2.post("/api/tasks", createTask);
8933
- app2.put("/api/tasks/:id", updateTask);
8934
- app2.delete("/api/tasks/:id", deleteTask);
8935
- app2.patch("/api/tasks/:id/complete", completeTask);
8936
- app2.get("/api/tasks/:id/work-items", listTaskWorkItems);
9643
+ registerTaskRoutes(app2);
8937
9644
  app2.get(
8938
9645
  "/api/assistants/:assistantId/graph",
8939
9646
  { schema: getAgentGraphSchema },
@@ -9042,7 +9749,8 @@ var registerLatticeRoutes = (app2, channelDeps) => {
9042
9749
  registerTenantRoutes(app2);
9043
9750
  registerAuthRoutes(app2, {
9044
9751
  autoApproveUsers: process.env.AUTO_APPROVE_USERS !== "false",
9045
- allowTenantRegistration: process.env.ALLOW_TENANT_REGISTRATION !== "false"
9752
+ allowTenantRegistration: process.env.ALLOW_TENANT_REGISTRATION !== "false",
9753
+ jwtSecret: resolveConsoleTokenSecret(process.env.AUTH_REQUIRED === "true")
9046
9754
  });
9047
9755
  registerChannelRoutes(app2, channelDeps);
9048
9756
  registerChannelInstallationRoutes(app2);
@@ -9179,9 +9887,16 @@ function registerResourceRoutes(app2, controller) {
9179
9887
  import { ExportableEntityRegistry as ExportableEntityRegistry3 } from "@axiom-lattice/core";
9180
9888
 
9181
9889
  // src/export_registrations/skill.registration.ts
9182
- import { getStoreLattice as getStoreLattice16 } from "@axiom-lattice/core";
9183
- function getStore2() {
9184
- 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 };
9185
9900
  }
9186
9901
  var skillRegistration = {
9187
9902
  entityType: "skill",
@@ -9189,10 +9904,10 @@ var skillRegistration = {
9189
9904
  category: "core",
9190
9905
  dependsOn: [],
9191
9906
  cascadeParents: [],
9192
- async listForExport(tenantId) {
9193
- const store = getStore2();
9194
- const skills = await store.getAllSkills(tenantId);
9195
- 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) => ({
9196
9911
  _exportId: "",
9197
9912
  data: {
9198
9913
  id: s.id,
@@ -9206,14 +9921,58 @@ var skillRegistration = {
9206
9921
  }
9207
9922
  }));
9208
9923
  },
9209
- async previewImport(tenantId, entities) {
9210
- const store = getStore2();
9211
- 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));
9212
9970
  const existingIds = new Set(existing.map((s) => s.id));
9213
9971
  const conflicts = [];
9214
9972
  const insertions = [];
9215
9973
  for (const entity of entities) {
9216
9974
  const id = entity.data.id;
9975
+ if (isBuiltInSkill2(id)) continue;
9217
9976
  if (existingIds.has(id)) {
9218
9977
  conflicts.push({
9219
9978
  _exportId: entity._exportId,
@@ -9232,33 +9991,49 @@ var skillRegistration = {
9232
9991
  }
9233
9992
  return { conflicts, insertions, errors: [] };
9234
9993
  },
9235
- async applyImport(tenantId, entity, resolution) {
9236
- const store = getStore2();
9994
+ async applyImport(tenantId, entity, resolution, files, context) {
9995
+ const store = getStore2(context);
9996
+ const ctx = toStoreContext(context);
9237
9997
  const data = entity.data;
9238
- const id = resolution.action === "rename" && resolution.newId ? resolution.newId : data.id;
9239
- if (resolution.action === "overwrite") {
9240
- try {
9241
- await store.deleteSkill(tenantId, id);
9242
- } catch {
9243
- }
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");
9244
10002
  }
9245
- await store.createSkill(tenantId, id, {
9246
- name: data.name,
10003
+ await store.createSkill(tenantId, targetName, {
10004
+ name: targetName,
9247
10005
  description: data.description,
9248
10006
  license: data.license,
9249
10007
  compatibility: data.compatibility,
9250
10008
  metadata: data.metadata,
9251
10009
  content: data.content,
9252
10010
  subSkills: data.subSkills
9253
- });
9254
- 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 };
9255
10030
  }
9256
10031
  };
9257
10032
 
9258
10033
  // src/export_registrations/agent.registration.ts
9259
- import { getStoreLattice as getStoreLattice17 } from "@axiom-lattice/core";
10034
+ import { getStoreLattice as getStoreLattice16 } from "@axiom-lattice/core";
9260
10035
  function getStore3() {
9261
- return getStoreLattice17("default", "assistant").store;
10036
+ return getStoreLattice16("default", "assistant").store;
9262
10037
  }
9263
10038
  var agentRegistration = {
9264
10039
  entityType: "agent",
@@ -9279,145 +10054,68 @@ var agentRegistration = {
9279
10054
  }
9280
10055
  }));
9281
10056
  },
9282
- async previewImport(tenantId, entities) {
9283
- const store = getStore3();
9284
- const existing = await store.getAllAssistants(tenantId);
9285
- const existingIds = new Set(existing.map((a) => a.id));
9286
- const conflicts = [];
9287
- const insertions = [];
10057
+ async getReferencedEntityIds(_tenantId, entities) {
10058
+ const skillIds = /* @__PURE__ */ new Set();
9288
10059
  for (const entity of entities) {
9289
- const id = entity.data.id;
9290
- if (existingIds.has(id)) {
9291
- conflicts.push({
9292
- _exportId: entity._exportId,
9293
- entityType: "agent",
9294
- conflictType: "id_exists",
9295
- existingId: id,
9296
- existingName: entity.data.name || id
9297
- });
9298
- } else {
9299
- insertions.push({
9300
- _exportId: entity._exportId,
9301
- entityType: "agent",
9302
- name: entity.data.name || id
9303
- });
9304
- }
9305
- }
9306
- return { conflicts, insertions, errors: [] };
9307
- },
9308
- async applyImport(tenantId, entity, resolution) {
9309
- const store = getStore3();
9310
- const data = entity.data;
9311
- const id = resolution.action === "rename" && resolution.newId ? resolution.newId : data.id;
9312
- if (resolution.action === "overwrite") {
9313
- try {
9314
- await store.deleteAssistant(tenantId, id);
9315
- } catch {
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);
9316
10066
  }
9317
10067
  }
9318
- await store.createAssistant(tenantId, id, {
9319
- name: data.name,
9320
- description: data.description || "",
9321
- graphDefinition: data.graphDefinition
9322
- });
9323
- return { newId: id };
9324
- }
9325
- };
9326
-
9327
- // src/export_registrations/binding.registration.ts
9328
- import { getStoreLattice as getStoreLattice18 } from "@axiom-lattice/core";
9329
- function getStore4() {
9330
- return getStoreLattice18("default", "channelBinding").store;
9331
- }
9332
- var bindingRegistration = {
9333
- entityType: "binding",
9334
- label: "Binding(\u6E20\u9053\u7ED1\u5B9A)",
9335
- category: "core",
9336
- dependsOn: ["agent", "channel_installation"],
9337
- cascadeParents: [],
9338
- referenceFields: { agentId: "agent", channelInstallationId: "channel_installation" },
9339
- async listForExport(tenantId) {
9340
- const store = getStore4();
9341
- const bindings = await store.list({ tenantId, limit: 1e4, offset: 0 });
9342
- return bindings.map((b) => ({
9343
- _exportId: "",
9344
- data: {
9345
- channel: b.channel,
9346
- channelInstallationId: b.channelInstallationId,
9347
- senderId: b.senderId,
9348
- agentId: b.agentId,
9349
- threadId: b.threadId,
9350
- workspaceId: b.workspaceId,
9351
- projectId: b.projectId,
9352
- threadMode: b.threadMode,
9353
- senderDisplayName: b.senderDisplayName,
9354
- senderMetadata: b.senderMetadata,
9355
- enabled: b.enabled
9356
- }
9357
- }));
10068
+ return { skill: [...skillIds] };
9358
10069
  },
9359
10070
  async previewImport(tenantId, entities) {
9360
- const store = getStore4();
9361
- const existing = await store.list({ tenantId, limit: 1e4, offset: 0 });
9362
- const existingKeys = new Set(
9363
- existing.map((b) => `${b.channel}:${b.channelInstallationId}:${b.senderId}`)
9364
- );
10071
+ const store = getStore3();
10072
+ const existing = await store.getAllAssistants(tenantId);
10073
+ const existingIds = new Set(existing.map((a) => a.id));
9365
10074
  const conflicts = [];
9366
10075
  const insertions = [];
9367
10076
  for (const entity of entities) {
9368
- const d = entity.data;
9369
- const key = `${d.channel}:${d.channelInstallationId}:${d.senderId}`;
9370
- if (existingKeys.has(key)) {
10077
+ const id = entity.data.id;
10078
+ if (existingIds.has(id)) {
9371
10079
  conflicts.push({
9372
10080
  _exportId: entity._exportId,
9373
- entityType: "binding",
9374
- conflictType: "unique_constraint",
9375
- existingName: `${d.channel}:${d.senderId}`,
9376
- field: "channel+channelInstallationId+senderId"
10081
+ entityType: "agent",
10082
+ conflictType: "id_exists",
10083
+ existingId: id,
10084
+ existingName: entity.data.name || id
9377
10085
  });
9378
10086
  } else {
9379
10087
  insertions.push({
9380
10088
  _exportId: entity._exportId,
9381
- entityType: "binding",
9382
- name: `${d.channel}:${d.senderId}`
10089
+ entityType: "agent",
10090
+ name: entity.data.name || id
9383
10091
  });
9384
10092
  }
9385
10093
  }
9386
10094
  return { conflicts, insertions, errors: [] };
9387
10095
  },
9388
10096
  async applyImport(tenantId, entity, resolution) {
9389
- if (resolution.action === "skip") return {};
9390
- const store = getStore4();
9391
- 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;
9392
10100
  if (resolution.action === "overwrite") {
9393
- const existing = await store.list({ tenantId, limit: 1e4, offset: 0 });
9394
- const match = existing.find(
9395
- (b) => b.channel === d.channel && b.channelInstallationId === d.channelInstallationId && b.senderId === d.senderId
9396
- );
9397
- if (match) {
9398
- await store.delete(match.id);
10101
+ try {
10102
+ await store.deleteAssistant(tenantId, id);
10103
+ } catch {
9399
10104
  }
9400
10105
  }
9401
- await store.create({
9402
- channel: d.channel,
9403
- channelInstallationId: d.channelInstallationId,
9404
- tenantId,
9405
- senderId: d.senderId,
9406
- agentId: d.agentId,
9407
- threadMode: d.threadMode || "fixed",
9408
- senderDisplayName: d.senderDisplayName,
9409
- senderMetadata: d.senderMetadata,
9410
- workspaceId: d.workspaceId,
9411
- projectId: d.projectId
10106
+ await store.createAssistant(tenantId, id, {
10107
+ name: data.name,
10108
+ description: data.description || "",
10109
+ graphDefinition: data.graphDefinition
9412
10110
  });
9413
- return { newId: void 0 };
10111
+ return { newId: id };
9414
10112
  }
9415
10113
  };
9416
10114
 
9417
10115
  // src/export_registrations/database-config.registration.ts
9418
- import { getStoreLattice as getStoreLattice19 } from "@axiom-lattice/core";
9419
- function getStore5() {
9420
- return getStoreLattice19("default", "database").store;
10116
+ import { getStoreLattice as getStoreLattice17 } from "@axiom-lattice/core";
10117
+ function getStore4() {
10118
+ return getStoreLattice17("default", "database").store;
9421
10119
  }
9422
10120
  var databaseConfigRegistration = {
9423
10121
  entityType: "database_config",
@@ -9426,24 +10124,24 @@ var databaseConfigRegistration = {
9426
10124
  dependsOn: [],
9427
10125
  cascadeParents: [],
9428
10126
  async listForExport(tenantId) {
9429
- const store = getStore5();
10127
+ const store = getStore4();
9430
10128
  const configs = await store.getAllConfigs(tenantId);
9431
- return configs.map((c) => ({
10129
+ return configs.map((c2) => ({
9432
10130
  _exportId: "",
9433
10131
  data: {
9434
- id: c.id,
9435
- key: c.key,
9436
- name: c.name,
9437
- description: c.description,
9438
- config: c.config
10132
+ id: c2.id,
10133
+ key: c2.key,
10134
+ name: c2.name,
10135
+ description: c2.description,
10136
+ config: c2.config
9439
10137
  }
9440
10138
  }));
9441
10139
  },
9442
10140
  async previewImport(tenantId, entities) {
9443
- const store = getStore5();
10141
+ const store = getStore4();
9444
10142
  const existing = await store.getAllConfigs(tenantId);
9445
- const existingIds = new Set(existing.map((c) => c.id));
9446
- 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));
9447
10145
  const conflicts = [];
9448
10146
  const insertions = [];
9449
10147
  for (const entity of entities) {
@@ -9477,7 +10175,7 @@ var databaseConfigRegistration = {
9477
10175
  return { conflicts, insertions, errors: [] };
9478
10176
  },
9479
10177
  async applyImport(tenantId, entity, resolution) {
9480
- const store = getStore5();
10178
+ const store = getStore4();
9481
10179
  const data = entity.data;
9482
10180
  const id = resolution.action === "rename" && resolution.newId ? resolution.newId : data.id;
9483
10181
  if (resolution.action === "overwrite") {
@@ -9497,9 +10195,9 @@ var databaseConfigRegistration = {
9497
10195
  };
9498
10196
 
9499
10197
  // src/export_registrations/metrics-config.registration.ts
9500
- import { getStoreLattice as getStoreLattice20 } from "@axiom-lattice/core";
9501
- function getStore6() {
9502
- return getStoreLattice20("default", "metrics").store;
10198
+ import { getStoreLattice as getStoreLattice18 } from "@axiom-lattice/core";
10199
+ function getStore5() {
10200
+ return getStoreLattice18("default", "metrics").store;
9503
10201
  }
9504
10202
  var metricsConfigRegistration = {
9505
10203
  entityType: "metrics_config",
@@ -9508,24 +10206,24 @@ var metricsConfigRegistration = {
9508
10206
  dependsOn: [],
9509
10207
  cascadeParents: [],
9510
10208
  async listForExport(tenantId) {
9511
- const store = getStore6();
10209
+ const store = getStore5();
9512
10210
  const configs = await store.getAllConfigs(tenantId);
9513
- return configs.map((c) => ({
10211
+ return configs.map((c2) => ({
9514
10212
  _exportId: "",
9515
10213
  data: {
9516
- id: c.id,
9517
- key: c.key,
9518
- name: c.name,
9519
- description: c.description,
9520
- config: c.config
10214
+ id: c2.id,
10215
+ key: c2.key,
10216
+ name: c2.name,
10217
+ description: c2.description,
10218
+ config: c2.config
9521
10219
  }
9522
10220
  }));
9523
10221
  },
9524
10222
  async previewImport(tenantId, entities) {
9525
- const store = getStore6();
10223
+ const store = getStore5();
9526
10224
  const existing = await store.getAllConfigs(tenantId);
9527
- const existingIds = new Set(existing.map((c) => c.id));
9528
- 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));
9529
10227
  const conflicts = [];
9530
10228
  const insertions = [];
9531
10229
  for (const entity of entities) {
@@ -9559,7 +10257,7 @@ var metricsConfigRegistration = {
9559
10257
  return { conflicts, insertions, errors: [] };
9560
10258
  },
9561
10259
  async applyImport(tenantId, entity, resolution) {
9562
- const store = getStore6();
10260
+ const store = getStore5();
9563
10261
  const data = entity.data;
9564
10262
  const id = resolution.action === "rename" && resolution.newId ? resolution.newId : data.id;
9565
10263
  if (resolution.action === "overwrite") {
@@ -9579,9 +10277,9 @@ var metricsConfigRegistration = {
9579
10277
  };
9580
10278
 
9581
10279
  // src/export_registrations/mcp-config.registration.ts
9582
- import { getStoreLattice as getStoreLattice21 } from "@axiom-lattice/core";
9583
- function getStore7() {
9584
- return getStoreLattice21("default", "mcp").store;
10280
+ import { getStoreLattice as getStoreLattice19 } from "@axiom-lattice/core";
10281
+ function getStore6() {
10282
+ return getStoreLattice19("default", "mcp").store;
9585
10283
  }
9586
10284
  var mcpConfigRegistration = {
9587
10285
  entityType: "mcp_config",
@@ -9590,26 +10288,26 @@ var mcpConfigRegistration = {
9590
10288
  dependsOn: [],
9591
10289
  cascadeParents: [],
9592
10290
  async listForExport(tenantId) {
9593
- const store = getStore7();
10291
+ const store = getStore6();
9594
10292
  const configs = await store.getAllConfigs(tenantId);
9595
- return configs.map((c) => ({
10293
+ return configs.map((c2) => ({
9596
10294
  _exportId: "",
9597
10295
  data: {
9598
- id: c.id,
9599
- key: c.key,
9600
- name: c.name,
9601
- description: c.description,
9602
- config: c.config,
9603
- selectedTools: c.selectedTools,
9604
- 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
9605
10303
  }
9606
10304
  }));
9607
10305
  },
9608
10306
  async previewImport(tenantId, entities) {
9609
- const store = getStore7();
10307
+ const store = getStore6();
9610
10308
  const existing = await store.getAllConfigs(tenantId);
9611
- const existingIds = new Set(existing.map((c) => c.id));
9612
- 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));
9613
10311
  const conflicts = [];
9614
10312
  const insertions = [];
9615
10313
  for (const entity of entities) {
@@ -9643,7 +10341,7 @@ var mcpConfigRegistration = {
9643
10341
  return { conflicts, insertions, errors: [] };
9644
10342
  },
9645
10343
  async applyImport(tenantId, entity, resolution) {
9646
- const store = getStore7();
10344
+ const store = getStore6();
9647
10345
  const data = entity.data;
9648
10346
  const id = resolution.action === "rename" && resolution.newId ? resolution.newId : data.id;
9649
10347
  if (resolution.action === "overwrite") {
@@ -9664,12 +10362,12 @@ var mcpConfigRegistration = {
9664
10362
  };
9665
10363
 
9666
10364
  // src/export_registrations/connection.registration.ts
9667
- import { getStoreLattice as getStoreLattice22 } from "@axiom-lattice/core";
9668
- function getStore8() {
9669
- return getStoreLattice22("default", "connection").store;
10365
+ import { getStoreLattice as getStoreLattice20 } from "@axiom-lattice/core";
10366
+ function getStore7() {
10367
+ return getStoreLattice20("default", "connection").store;
9670
10368
  }
9671
10369
  async function listAllConnections(tenantId) {
9672
- const store = getStore8();
10370
+ const store = getStore7();
9673
10371
  if (typeof store.listByTenant !== "function") {
9674
10372
  throw new Error("ConnectionStore.listByTenant is not implemented. SQL bypass removed \u2014 upgrade your store adapter.");
9675
10373
  }
@@ -9683,22 +10381,22 @@ var connectionRegistration = {
9683
10381
  cascadeParents: [],
9684
10382
  async listForExport(tenantId) {
9685
10383
  const connections2 = await listAllConnections(tenantId);
9686
- return connections2.map((c) => ({
10384
+ return connections2.map((c2) => ({
9687
10385
  _exportId: "",
9688
10386
  data: {
9689
- id: c.id,
9690
- type: c.type,
9691
- key: c.key,
9692
- name: c.name,
9693
- description: c.description,
9694
- 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
9695
10393
  }
9696
10394
  }));
9697
10395
  },
9698
10396
  async previewImport(tenantId, entities) {
9699
10397
  const existing = await listAllConnections(tenantId);
9700
10398
  const existingCompositeKeys = new Set(
9701
- existing.map((c) => `${c.type}:${c.key}`)
10399
+ existing.map((c2) => `${c2.type}:${c2.key}`)
9702
10400
  );
9703
10401
  const conflicts = [];
9704
10402
  const insertions = [];
@@ -9725,7 +10423,7 @@ var connectionRegistration = {
9725
10423
  },
9726
10424
  async applyImport(tenantId, entity, resolution) {
9727
10425
  if (resolution.action === "skip") return {};
9728
- const store = getStore8();
10426
+ const store = getStore7();
9729
10427
  const d = entity.data;
9730
10428
  const type = d.type;
9731
10429
  const key = d.key;
@@ -9748,9 +10446,9 @@ var connectionRegistration = {
9748
10446
  };
9749
10447
 
9750
10448
  // src/export_registrations/collection.registration.ts
9751
- import { getStoreLattice as getStoreLattice23 } from "@axiom-lattice/core";
9752
- function getStore9() {
9753
- return getStoreLattice23("default", "collection").store;
10449
+ import { getStoreLattice as getStoreLattice21 } from "@axiom-lattice/core";
10450
+ function getStore8() {
10451
+ return getStoreLattice21("default", "collection").store;
9754
10452
  }
9755
10453
  var collectionRegistration = {
9756
10454
  entityType: "collection",
@@ -9759,23 +10457,23 @@ var collectionRegistration = {
9759
10457
  dependsOn: [],
9760
10458
  cascadeParents: [],
9761
10459
  async listForExport(tenantId) {
9762
- const store = getStore9();
10460
+ const store = getStore8();
9763
10461
  const collections = await store.getAllCollections(tenantId);
9764
- return collections.map((c) => ({
10462
+ return collections.map((c2) => ({
9765
10463
  _exportId: "",
9766
10464
  data: {
9767
- id: c.id,
9768
- name: c.name,
9769
- label: c.label,
9770
- embeddingKey: c.embeddingKey,
9771
- schema: c.schema
10465
+ id: c2.id,
10466
+ name: c2.name,
10467
+ label: c2.label,
10468
+ embeddingKey: c2.embeddingKey,
10469
+ schema: c2.schema
9772
10470
  }
9773
10471
  }));
9774
10472
  },
9775
10473
  async previewImport(tenantId, entities) {
9776
- const store = getStore9();
10474
+ const store = getStore8();
9777
10475
  const existing = await store.getAllCollections(tenantId);
9778
- const existingNames = new Set(existing.map((c) => c.name));
10476
+ const existingNames = new Set(existing.map((c2) => c2.name));
9779
10477
  const conflicts = [];
9780
10478
  const insertions = [];
9781
10479
  for (const entity of entities) {
@@ -9801,7 +10499,7 @@ var collectionRegistration = {
9801
10499
  },
9802
10500
  async applyImport(tenantId, entity, resolution) {
9803
10501
  if (resolution.action === "skip") return {};
9804
- const store = getStore9();
10502
+ const store = getStore8();
9805
10503
  const d = entity.data;
9806
10504
  const name = d.name;
9807
10505
  if (resolution.action === "overwrite") {
@@ -9821,9 +10519,11 @@ var collectionRegistration = {
9821
10519
  };
9822
10520
 
9823
10521
  // src/export_registrations/channel-installation.registration.ts
9824
- import { getStoreLattice as getStoreLattice24 } from "@axiom-lattice/core";
9825
- function getStore10() {
9826
- 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;
9827
10527
  }
9828
10528
  var channelInstallationRegistration = {
9829
10529
  entityType: "channel_installation",
@@ -9833,7 +10533,7 @@ var channelInstallationRegistration = {
9833
10533
  cascadeParents: [],
9834
10534
  referenceFields: { fallbackAgentId: "agent" },
9835
10535
  async listForExport(tenantId) {
9836
- const store = getStore10();
10536
+ const store = getStore9();
9837
10537
  const installations = await store.getInstallationsByTenant(tenantId);
9838
10538
  return installations.map((i) => ({
9839
10539
  _exportId: "",
@@ -9849,21 +10549,20 @@ var channelInstallationRegistration = {
9849
10549
  }));
9850
10550
  },
9851
10551
  async previewImport(tenantId, entities) {
9852
- const store = getStore10();
9853
- const existing = await store.getInstallationsByTenant(tenantId);
9854
- const existingIds = new Set(existing.map((i) => i.id));
10552
+ const store = getStore9();
9855
10553
  const conflicts = [];
9856
10554
  const insertions = [];
9857
10555
  for (const entity of entities) {
9858
10556
  const d = entity.data;
9859
10557
  const id = d.id;
9860
- if (existingIds.has(id)) {
10558
+ const existing = UUID_PATTERN.test(id) ? await store.getInstallationById(id) : null;
10559
+ if (existing) {
9861
10560
  conflicts.push({
9862
10561
  _exportId: entity._exportId,
9863
10562
  entityType: "channel_installation",
9864
10563
  conflictType: "id_exists",
9865
10564
  existingId: id,
9866
- existingName: d.name || id
10565
+ existingName: existing.name || id
9867
10566
  });
9868
10567
  } else {
9869
10568
  insertions.push({
@@ -9876,31 +10575,56 @@ var channelInstallationRegistration = {
9876
10575
  return { conflicts, insertions, errors: [] };
9877
10576
  },
9878
10577
  async applyImport(tenantId, entity, resolution) {
9879
- const store = getStore10();
10578
+ const store = getStore9();
9880
10579
  const data = entity.data;
9881
- 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
+ };
9882
10587
  if (resolution.action === "overwrite") {
9883
- try {
9884
- await store.deleteInstallation(tenantId, id);
9885
- } 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");
9886
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();
9887
10614
  }
9888
10615
  await store.createInstallation(tenantId, id, {
9889
10616
  channel: data.channel,
9890
- name: data.name,
9891
- config: data.config,
9892
- enabled: data.enabled,
9893
- fallbackAgentId: data.fallbackAgentId,
9894
- rejectWhenNoBinding: data.rejectWhenNoBinding
10617
+ ...commonData,
10618
+ config: data.config
9895
10619
  });
9896
10620
  return { newId: id };
9897
10621
  }
9898
10622
  };
9899
10623
 
9900
10624
  // src/export_registrations/menu.registration.ts
9901
- import { getStoreLattice as getStoreLattice25 } from "@axiom-lattice/core";
9902
- function getStore11() {
9903
- return getStoreLattice25("default", "menu").store;
10625
+ import { getStoreLattice as getStoreLattice23 } from "@axiom-lattice/core";
10626
+ function getStore10() {
10627
+ return getStoreLattice23("default", "menu").store;
9904
10628
  }
9905
10629
  var menuRegistration = {
9906
10630
  entityType: "menu",
@@ -9909,7 +10633,7 @@ var menuRegistration = {
9909
10633
  dependsOn: ["agent"],
9910
10634
  cascadeParents: [],
9911
10635
  async listForExport(tenantId) {
9912
- const store = getStore11();
10636
+ const store = getStore10();
9913
10637
  const menus = await store.list({ tenantId });
9914
10638
  return menus.map((m) => ({
9915
10639
  _exportId: "",
@@ -9927,7 +10651,7 @@ var menuRegistration = {
9927
10651
  }));
9928
10652
  },
9929
10653
  async previewImport(tenantId, entities) {
9930
- const store = getStore11();
10654
+ const store = getStore10();
9931
10655
  const existing = await store.list({ tenantId });
9932
10656
  const existingIds = new Set(existing.map((m) => m.id));
9933
10657
  const conflicts = [];
@@ -9954,7 +10678,7 @@ var menuRegistration = {
9954
10678
  return { conflicts, insertions, errors: [] };
9955
10679
  },
9956
10680
  async applyImport(tenantId, entity, resolution) {
9957
- const store = getStore11();
10681
+ const store = getStore10();
9958
10682
  const d = entity.data;
9959
10683
  const id = resolution.action === "rename" && resolution.newId ? resolution.newId : d.id;
9960
10684
  if (resolution.action === "overwrite") {
@@ -9977,171 +10701,10 @@ var menuRegistration = {
9977
10701
  }
9978
10702
  };
9979
10703
 
9980
- // src/export_registrations/task.registration.ts
9981
- import { getStoreLattice as getStoreLattice26 } from "@axiom-lattice/core";
9982
- function getStore12() {
9983
- return getStoreLattice26("default", "task").store;
9984
- }
9985
- var taskRegistration = {
9986
- entityType: "task",
9987
- label: "Task(\u4EFB\u52A1)",
9988
- category: "core",
9989
- dependsOn: ["agent"],
9990
- cascadeParents: [],
9991
- referenceFields: { ownerId: "agent", parentId: "task" },
9992
- async listForExport(tenantId) {
9993
- const store = getStore12();
9994
- const tasks = await store.list({ tenantId, limit: 1e4, offset: 0 });
9995
- return tasks.map((t) => ({
9996
- _exportId: "",
9997
- data: {
9998
- id: t.id,
9999
- title: t.title,
10000
- description: t.description,
10001
- status: t.status,
10002
- priority: t.priority,
10003
- dueDate: t.dueDate,
10004
- ownerType: t.ownerType,
10005
- ownerId: t.ownerId,
10006
- metadata: t.metadata,
10007
- parentId: t.parentId,
10008
- sourceId: t.sourceId,
10009
- context: t.context
10010
- }
10011
- }));
10012
- },
10013
- async previewImport(tenantId, entities) {
10014
- const store = getStore12();
10015
- const existing = await store.list({ tenantId, limit: 1e4, offset: 0 });
10016
- const existingIds = new Set(existing.map((t) => t.id));
10017
- const conflicts = [];
10018
- const insertions = [];
10019
- for (const entity of entities) {
10020
- const d = entity.data;
10021
- const id = d.id;
10022
- if (existingIds.has(id)) {
10023
- conflicts.push({
10024
- _exportId: entity._exportId,
10025
- entityType: "task",
10026
- conflictType: "id_exists",
10027
- existingId: id,
10028
- existingName: d.title || id
10029
- });
10030
- } else {
10031
- insertions.push({
10032
- _exportId: entity._exportId,
10033
- entityType: "task",
10034
- name: d.title || id
10035
- });
10036
- }
10037
- }
10038
- return { conflicts, insertions, errors: [] };
10039
- },
10040
- async applyImport(tenantId, entity, resolution) {
10041
- const store = getStore12();
10042
- const data = entity.data;
10043
- const id = resolution.action === "rename" && resolution.newId ? resolution.newId : data.id;
10044
- if (resolution.action === "overwrite") {
10045
- try {
10046
- await store.delete(tenantId, id);
10047
- } catch {
10048
- }
10049
- }
10050
- await store.create({
10051
- tenantId,
10052
- ownerType: data.ownerType || "user",
10053
- ownerId: data.ownerId || "",
10054
- title: data.title,
10055
- description: data.description,
10056
- status: data.status,
10057
- priority: data.priority,
10058
- dueDate: data.dueDate,
10059
- metadata: data.metadata,
10060
- parentId: data.parentId,
10061
- sourceId: data.sourceId,
10062
- context: data.context
10063
- });
10064
- return { newId: id };
10065
- }
10066
- };
10067
-
10068
- // src/export_registrations/a2a-api-key.registration.ts
10069
- import { getStoreLattice as getStoreLattice27 } from "@axiom-lattice/core";
10070
- function getStore13() {
10071
- return getStoreLattice27("default", "a2aApiKey").store;
10072
- }
10073
- var a2aApiKeyRegistration = {
10074
- entityType: "a2a_api_key",
10075
- label: "A2A API Key(API\u5BC6\u94A5)",
10076
- category: "core",
10077
- dependsOn: [],
10078
- cascadeParents: [],
10079
- async listForExport(tenantId) {
10080
- const store = getStore13();
10081
- const keys = await store.list({ tenantId, limit: 1e4, offset: 0 });
10082
- return keys.map((k) => ({
10083
- _exportId: "",
10084
- data: {
10085
- id: k.id,
10086
- tenantId: k.tenantId,
10087
- projectId: k.projectId,
10088
- workspaceId: k.workspaceId,
10089
- label: k.label,
10090
- enabled: k.enabled
10091
- }
10092
- }));
10093
- },
10094
- async previewImport(tenantId, entities) {
10095
- const store = getStore13();
10096
- const existing = await store.list({ tenantId, limit: 1e4, offset: 0 });
10097
- const existingIds = new Set(existing.map((k) => k.id));
10098
- const conflicts = [];
10099
- const insertions = [];
10100
- for (const entity of entities) {
10101
- const d = entity.data;
10102
- const id = d.id;
10103
- if (existingIds.has(id)) {
10104
- conflicts.push({
10105
- _exportId: entity._exportId,
10106
- entityType: "a2a_api_key",
10107
- conflictType: "id_exists",
10108
- existingId: id,
10109
- existingName: d.label || id
10110
- });
10111
- } else {
10112
- insertions.push({
10113
- _exportId: entity._exportId,
10114
- entityType: "a2a_api_key",
10115
- name: d.label || id
10116
- });
10117
- }
10118
- }
10119
- return { conflicts, insertions, errors: [] };
10120
- },
10121
- async applyImport(tenantId, entity, resolution) {
10122
- if (resolution.action === "skip") return {};
10123
- const store = getStore13();
10124
- const d = entity.data;
10125
- if (resolution.action === "overwrite") {
10126
- try {
10127
- await store.delete(d.id);
10128
- } catch {
10129
- }
10130
- }
10131
- await store.create({
10132
- tenantId,
10133
- projectId: d.projectId,
10134
- workspaceId: d.workspaceId,
10135
- label: d.label
10136
- });
10137
- return { newId: void 0 };
10138
- }
10139
- };
10140
-
10141
10704
  // src/export_registrations/eval.registration.ts
10142
- import { getStoreLattice as getStoreLattice28 } from "@axiom-lattice/core";
10143
- function getStore14() {
10144
- return getStoreLattice28("default", "eval").store;
10705
+ import { getStoreLattice as getStoreLattice24 } from "@axiom-lattice/core";
10706
+ function getStore11() {
10707
+ return getStoreLattice24("default", "eval").store;
10145
10708
  }
10146
10709
  var evalRegistration = {
10147
10710
  entityType: "eval",
@@ -10151,7 +10714,7 @@ var evalRegistration = {
10151
10714
  cascadeParents: [],
10152
10715
  referenceFields: { suiteId: "eval_suite", projectId: "eval_project", runId: "eval_run", caseId: "eval_case" },
10153
10716
  async listForExport(tenantId) {
10154
- const store = getStore14();
10717
+ const store = getStore11();
10155
10718
  const projects = await store.getProjectsByTenant(tenantId);
10156
10719
  const entities = [];
10157
10720
  for (const project of projects) {
@@ -10181,19 +10744,19 @@ var evalRegistration = {
10181
10744
  }
10182
10745
  });
10183
10746
  const cases = await store.getCasesBySuite(tenantId, suite.id);
10184
- for (const c of cases) {
10747
+ for (const c2 of cases) {
10185
10748
  entities.push({
10186
10749
  _exportId: "",
10187
10750
  data: {
10188
10751
  _subType: "eval_case",
10189
- id: c.id,
10190
- suiteId: c.suiteId,
10191
- inputMessage: c.inputMessage,
10192
- inputFiles: c.inputFiles,
10193
- steps: c.steps,
10194
- outputType: c.outputType,
10195
- contentAssertion: c.contentAssertion,
10196
- 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
10197
10760
  }
10198
10761
  });
10199
10762
  }
@@ -10241,7 +10804,7 @@ var evalRegistration = {
10241
10804
  return entities;
10242
10805
  },
10243
10806
  async previewImport(tenantId, entities) {
10244
- const store = getStore14();
10807
+ const store = getStore11();
10245
10808
  const existingProjects = await store.getProjectsByTenant(tenantId);
10246
10809
  const existingProjectIds = new Set(existingProjects.map((p) => p.id));
10247
10810
  const existingProjectNames = new Set(existingProjects.map((p) => p.name));
@@ -10257,7 +10820,7 @@ var evalRegistration = {
10257
10820
  existingSuiteIds.add(s.id);
10258
10821
  try {
10259
10822
  const cases = await store.getCasesBySuite(tenantId, s.id);
10260
- for (const c of cases) existingCaseIds.add(c.id);
10823
+ for (const c2 of cases) existingCaseIds.add(c2.id);
10261
10824
  } catch {
10262
10825
  }
10263
10826
  }
@@ -10374,7 +10937,7 @@ var evalRegistration = {
10374
10937
  },
10375
10938
  async applyImport(tenantId, entity, resolution) {
10376
10939
  if (resolution.action === "skip") return {};
10377
- const store = getStore14();
10940
+ const store = getStore11();
10378
10941
  const d = entity.data;
10379
10942
  const subType = d._subType;
10380
10943
  const id = resolution.action === "rename" && resolution.newId ? resolution.newId : d.id;
@@ -10470,7 +11033,6 @@ function registerAllBuiltinEntities() {
10470
11033
  const registry = ExportableEntityRegistry3.getInstance();
10471
11034
  registry.register(skillRegistration);
10472
11035
  registry.register(agentRegistration);
10473
- registry.register(bindingRegistration);
10474
11036
  registry.register(databaseConfigRegistration);
10475
11037
  registry.register(metricsConfigRegistration);
10476
11038
  registry.register(mcpConfigRegistration);
@@ -10478,23 +11040,33 @@ function registerAllBuiltinEntities() {
10478
11040
  registry.register(collectionRegistration);
10479
11041
  registry.register(channelInstallationRegistration);
10480
11042
  registry.register(menuRegistration);
10481
- registry.register(taskRegistration);
10482
- registry.register(a2aApiKeyRegistration);
10483
11043
  registry.register(evalRegistration);
10484
11044
  }
10485
11045
 
10486
11046
  // src/router/MessageRouter.ts
10487
11047
  import {
10488
- getStoreLattice as getStoreLattice29,
11048
+ getStoreLattice as getStoreLattice25,
10489
11049
  agentInstanceManager as agentInstanceManager6
10490
11050
  } from "@axiom-lattice/core";
10491
- import { randomUUID as randomUUID9 } from "crypto";
11051
+ import { randomUUID as randomUUID10 } from "crypto";
10492
11052
  var BindingNotFoundError = class extends Error {
10493
11053
  constructor(message) {
10494
11054
  super(message);
10495
11055
  this.name = "BindingNotFoundError";
10496
11056
  }
10497
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
+ };
10498
11070
  var MessageRouter = class {
10499
11071
  constructor(config) {
10500
11072
  /**
@@ -10539,20 +11111,20 @@ var MessageRouter = class {
10539
11111
  let threadId;
10540
11112
  let agentId;
10541
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;
10542
11127
  await this.runMiddlewares(ctx, async () => {
10543
- const tenantId = message.tenantId || (await this.installationStore.getInstallationById(message.channelInstallationId))?.tenantId;
10544
- if (!tenantId) {
10545
- console.error({
10546
- event: "dispatch:error",
10547
- error: "tenantId missing",
10548
- channel: message.channel,
10549
- channelInstallationId: message.channelInstallationId,
10550
- senderId: message.sender.id
10551
- }, "tenantId is required");
10552
- throw new Error(
10553
- "tenantId is required: provide it in the message or ensure the channelInstallation has a tenantId"
10554
- );
10555
- }
10556
11128
  console.log({ event: "dispatch:start", channel: message.channel, senderId: message.sender.id, tenantId }, "Message dispatch started");
10557
11129
  const adapter = this.adapterRegistry.get(message.channel);
10558
11130
  const hasAdapterThreadStrategy = !!adapter?.resolveThreadId;
@@ -10562,11 +11134,8 @@ var MessageRouter = class {
10562
11134
  channelInstallationId: message.channelInstallationId,
10563
11135
  tenantId
10564
11136
  });
10565
- const installation = await this.installationStore.getInstallationById(
10566
- message.channelInstallationId
10567
- );
10568
11137
  if (!binding) {
10569
- if (installation?.rejectWhenNoBinding) {
11138
+ if (installation.rejectWhenNoBinding) {
10570
11139
  console.warn({
10571
11140
  event: "dispatch:no_binding",
10572
11141
  channel: message.channel,
@@ -10578,7 +11147,7 @@ var MessageRouter = class {
10578
11147
  `No binding for sender "${message.sender.id}" on channel "${message.channel}"`
10579
11148
  );
10580
11149
  }
10581
- if (installation?.fallbackAgentId) {
11150
+ if (installation.fallbackAgentId) {
10582
11151
  console.warn({
10583
11152
  event: "dispatch:fallback",
10584
11153
  channel: message.channel,
@@ -10633,7 +11202,7 @@ var MessageRouter = class {
10633
11202
  );
10634
11203
  }
10635
11204
  }
10636
- agentId = binding?.agentId ?? installation?.fallbackAgentId;
11205
+ agentId = binding?.agentId ?? installation.fallbackAgentId;
10637
11206
  if (!agentId) {
10638
11207
  throw new BindingNotFoundError(
10639
11208
  `No agent configured for sender "${message.sender.id}"`
@@ -10648,7 +11217,7 @@ var MessageRouter = class {
10648
11217
  channel: message.channel,
10649
11218
  adapterChannel: adapter.channel
10650
11219
  }, "Thread resolved by adapter strategy");
10651
- const threadStore = getStoreLattice29("default", "thread").store;
11220
+ const threadStore = getStoreLattice25("default", "thread").store;
10652
11221
  try {
10653
11222
  await threadStore.createThread(
10654
11223
  tenantId,
@@ -10685,8 +11254,8 @@ var MessageRouter = class {
10685
11254
  }
10686
11255
  }
10687
11256
  if (!threadId) {
10688
- const threadStore = getStoreLattice29("default", "thread").store;
10689
- const newThreadId = randomUUID9();
11257
+ const threadStore = getStoreLattice25("default", "thread").store;
11258
+ const newThreadId = randomUUID10();
10690
11259
  console.log({
10691
11260
  event: "dispatch:thread:create",
10692
11261
  agentId,
@@ -10736,78 +11305,73 @@ var MessageRouter = class {
10736
11305
  const replySubKey = `${threadId}:${message.replyTarget.adapterChannel}:reply`;
10737
11306
  const adapter2 = this.adapterRegistry.get(message.replyTarget.adapterChannel);
10738
11307
  if (adapter2) {
10739
- const installation2 = await this.installationStore.getInstallationById(
10740
- message.channelInstallationId
10741
- );
10742
- if (installation2) {
10743
- const existing = this._replySubs.get(replySubKey);
10744
- if (!existing || existing.count === 0) {
10745
- const timer = setTimeout(() => {
10746
- const entry = this._replySubs.get(replySubKey);
10747
- 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);
10748
11337
  this._replySubs.delete(replySubKey);
10749
- console.warn({
10750
- event: "dispatch:reply:timeout",
10751
- threadId,
10752
- channel: message.replyTarget.adapterChannel
10753
- }, "Reply subscription timed out \u2014 no reply:ready fired within 1h");
10754
11338
  }
10755
- }, 36e5);
10756
- this._replySubs.set(replySubKey, { count: 1, timer });
10757
- console.log({
10758
- event: "dispatch:reply:subscribed",
10759
- threadId,
10760
- channel: message.replyTarget.adapterChannel,
10761
- senderId: message.sender.id
10762
- }, "Subscribed to reply:ready for thread");
10763
- agent.subscribeOnce("reply:ready", (data) => {
10764
- const messages = data.state?.values?.messages;
10765
- const lastAI = messages?.filter((m) => m.type === "ai" || m.getType?.() === "ai").pop();
10766
- const replyText = lastAI?.content ?? "";
10767
- const entry = this._replySubs.get(replySubKey);
10768
- if (entry) {
10769
- entry.count--;
10770
- if (entry.count <= 0) {
10771
- clearTimeout(entry.timer);
10772
- this._replySubs.delete(replySubKey);
10773
- }
10774
- }
10775
- 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(() => {
10776
11348
  console.log({
10777
- event: "dispatch:reply:sending",
10778
- threadId,
10779
- channel: message.replyTarget.adapterChannel,
10780
- replyLength: replyText.length
10781
- }, "Sending channel reply");
10782
- adapter2.sendReply(message.replyTarget, { text: replyText }, installation2).then(() => {
10783
- console.log({
10784
- event: "dispatch:reply:sent",
10785
- threadId,
10786
- channel: message.replyTarget.adapterChannel
10787
- }, "Channel reply sent successfully");
10788
- }).catch((err) => console.error({
10789
- event: "dispatch:reply:failed",
10790
- threadId,
10791
- channel: message.replyTarget.adapterChannel,
10792
- error: err instanceof Error ? err.message : String(err)
10793
- }, "Failed to send channel reply"));
10794
- } else {
10795
- console.warn({
10796
- event: "dispatch:reply:empty",
11349
+ event: "dispatch:reply:sent",
10797
11350
  threadId,
10798
11351
  channel: message.replyTarget.adapterChannel
10799
- }, "Agent produced no text output \u2014 skipping reply");
10800
- }
10801
- });
10802
- } else {
10803
- existing.count++;
10804
- console.log({
10805
- event: "dispatch:reply:incremented",
10806
- threadId,
10807
- channel: message.replyTarget.adapterChannel,
10808
- count: existing.count
10809
- }, "Incremented reply counter for thread (already subscribed)");
10810
- }
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)");
10811
11375
  }
10812
11376
  }
10813
11377
  }
@@ -11111,7 +11675,7 @@ ${summary}`
11111
11675
  console.log(
11112
11676
  `\u5C06\u5728 ${delayMs}ms \u540E\u91CD\u8BD5\u4EFB\u52A1 (${nextRetryCount}/${maxRetries})`
11113
11677
  );
11114
- await new Promise((resolve3) => setTimeout(resolve3, delayMs));
11678
+ await new Promise((resolve2) => setTimeout(resolve2, delayMs));
11115
11679
  return handleAgentTask(taskRequest, nextRetryCount);
11116
11680
  }
11117
11681
  if (callback_event) {
@@ -11291,7 +11855,7 @@ import {
11291
11855
  getLoggerLattice,
11292
11856
  loggerLatticeManager,
11293
11857
  sandboxLatticeManager as sandboxLatticeManager2,
11294
- getStoreLattice as getStoreLattice30,
11858
+ getStoreLattice as getStoreLattice26,
11295
11859
  agentInstanceManager as agentInstanceManager8,
11296
11860
  createSandboxProvider,
11297
11861
  TokenCache,
@@ -11341,26 +11905,8 @@ var getHeaderValue = (header) => {
11341
11905
  }
11342
11906
  return header;
11343
11907
  };
11344
- var PUBLIC_ROUTES = ["/api/auth/login", "/api/auth/register", "/health", "/api/stt/models"];
11345
- app.addHook("preHandler", async (request, reply) => {
11346
- const user = extractUserFromAuthHeader(request.headers.authorization);
11347
- if (user) {
11348
- request.user = user;
11349
- return;
11350
- }
11351
- const authRequired = process.env.AUTH_REQUIRED === "true";
11352
- if (!authRequired) return;
11353
- if (request.method === "OPTIONS") return;
11354
- if (PUBLIC_ROUTES.some((r) => request.url === r)) return;
11355
- if (request.url.startsWith("/s/")) return;
11356
- const urlPath = request.url.split("?")[0];
11357
- if (urlPath.includes("/viewfile") || urlPath.includes("/downloadfile") || urlPath.includes("/uploadfile")) return;
11358
- if (urlPath.startsWith("/f/") || urlPath === "/f") return;
11359
- return reply.status(401).send({
11360
- success: false,
11361
- error: "Unauthorized - Missing or invalid token"
11362
- });
11363
- });
11908
+ var authRequired = process.env.AUTH_REQUIRED === "true";
11909
+ registerConsoleAuthHook(app, { authRequired });
11364
11910
  app.addHook("onRequest", (request, reply, done) => {
11365
11911
  if (!request.headers["x-project-id"]) {
11366
11912
  request.headers["x-project-id"] = "default";
@@ -11390,7 +11936,7 @@ app.register(cors, {
11390
11936
  origin: true,
11391
11937
  methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"],
11392
11938
  allowedHeaders: "*",
11393
- exposedHeaders: ["Content-Type"],
11939
+ exposedHeaders: ["Content-Type", "Content-Disposition"],
11394
11940
  credentials: true
11395
11941
  });
11396
11942
  app.register(sensible);
@@ -11444,14 +11990,14 @@ function getConfiguredSandboxProvider() {
11444
11990
  }
11445
11991
  async function restoreMcpConnections() {
11446
11992
  try {
11447
- const storeLattice = getStoreLattice30("default", "mcp");
11993
+ const storeLattice = getStoreLattice26("default", "mcp");
11448
11994
  const store = storeLattice.store;
11449
11995
  if (!store) {
11450
11996
  logger4.info("MCP store not configured, skipping connection restoration");
11451
11997
  return;
11452
11998
  }
11453
11999
  const configs = await store.getAllConfigs("default");
11454
- const connectedConfigs = configs.filter((c) => c.status === "connected");
12000
+ const connectedConfigs = configs.filter((c2) => c2.status === "connected");
11455
12001
  if (connectedConfigs.length === 0) return;
11456
12002
  logger4.info(`Restoring ${connectedConfigs.length} MCP server connections...`);
11457
12003
  const { convertToConnection } = await import("./mcp-configs-SHRMQHIL.mjs");
@@ -11496,9 +12042,9 @@ var start = async (config) => {
11496
12042
  const { wechatChannelAdapter } = await import("./WechatChannelAdapter-WSDKR4OA.mjs");
11497
12043
  adapterRegistry.register(wechatChannelAdapter);
11498
12044
  try {
11499
- const { getStoreLattice: getStore15 } = await import("@axiom-lattice/core");
11500
- const bindingStore = getStore15("default", "channelBinding").store;
11501
- 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;
11502
12048
  setBindingRegistry(bindingStore);
11503
12049
  const router = new MessageRouter({
11504
12050
  middlewares: [
@@ -11511,14 +12057,6 @@ var start = async (config) => {
11511
12057
  installationStore
11512
12058
  });
11513
12059
  channelDeps = { router, installationStore };
11514
- try {
11515
- const a2aKeyStore = getStore15("default", "a2aApiKey").store;
11516
- const a2a = await import("./a2a-QMHEH3ZT.mjs");
11517
- a2a.setA2AKeyStore(a2aKeyStore);
11518
- await a2a.refreshStoreKeyMap();
11519
- logger4.info("A2A key store initialized");
11520
- } catch {
11521
- }
11522
12060
  } catch (err) {
11523
12061
  logger4.warn("Channel infrastructure unavailable", {
11524
12062
  error: err instanceof Error ? err.message : String(err)
@@ -11526,7 +12064,7 @@ var start = async (config) => {
11526
12064
  }
11527
12065
  setEvalRunService(evalRunner);
11528
12066
  try {
11529
- const menuStore = getStoreLattice30("default", "menu").store;
12067
+ const menuStore = getStoreLattice26("default", "menu").store;
11530
12068
  setMenuRegistry(menuStore);
11531
12069
  logger4.info("Menu registry initialized");
11532
12070
  } catch {
@@ -11536,13 +12074,44 @@ var start = async (config) => {
11536
12074
  setChannelControllerDeps({ adapterRegistry, router: channelDeps.router });
11537
12075
  }
11538
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
+ }
11539
12108
  if (!sandboxLatticeManager2.hasLattice("default")) {
11540
12109
  sandboxLatticeManager2.registerLattice("default", getConfiguredSandboxProvider());
11541
12110
  logger4.info("Registered sandbox manager from env configuration");
11542
12111
  }
11543
12112
  try {
11544
12113
  const { ResourceController } = await import("./resources-VA7LSDKN.mjs");
11545
- const sharedResourceStore = getStoreLattice30("default", "sharedResource").store;
12114
+ const sharedResourceStore = getStoreLattice26("default", "sharedResource").store;
11546
12115
  const cache = new TokenCache();
11547
12116
  const resourceController = new ResourceController({
11548
12117
  store: sharedResourceStore,