@axiom-lattice/gateway 4.1.2 → 4.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +99 -63
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +99 -63
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -12006,6 +12006,16 @@ function verifySignedConsoleSession(authHeader, secret) {
|
|
|
12006
12006
|
}
|
|
12007
12007
|
}
|
|
12008
12008
|
var PUBLIC_ROUTES = ["/api/auth/login", "/api/auth/register", "/health", "/api/stt/models"];
|
|
12009
|
+
function isStaleConsoleSessionCredential(authHeader) {
|
|
12010
|
+
if (!authHeader?.startsWith("Bearer ")) return false;
|
|
12011
|
+
const parts = authHeader.substring(7).split(".");
|
|
12012
|
+
if (parts.length !== 2 || !/^[A-Za-z0-9_-]{43}$/.test(parts[1])) return false;
|
|
12013
|
+
try {
|
|
12014
|
+
return isConsoleSessionPayload(JSON.parse(Buffer.from(parts[0], "base64url").toString("utf8")));
|
|
12015
|
+
} catch {
|
|
12016
|
+
return false;
|
|
12017
|
+
}
|
|
12018
|
+
}
|
|
12009
12019
|
function bypassesConsoleAuth(request) {
|
|
12010
12020
|
const path6 = request.url.split("?")[0];
|
|
12011
12021
|
const method = request.method;
|
|
@@ -12025,7 +12035,15 @@ function registerConsoleAuthHook(app2, options) {
|
|
|
12025
12035
|
}
|
|
12026
12036
|
if (bypassesConsoleAuth(request)) return;
|
|
12027
12037
|
if (session) return;
|
|
12028
|
-
if (!options.authRequired || request.method === "OPTIONS")
|
|
12038
|
+
if (!options.authRequired || request.method === "OPTIONS") {
|
|
12039
|
+
if (!PUBLIC_ROUTES.some((route) => request.url === route) && !request.url.startsWith("/s/") && isStaleConsoleSessionCredential(request.headers.authorization)) {
|
|
12040
|
+
await reply.status(401).send({
|
|
12041
|
+
success: false,
|
|
12042
|
+
error: "Unauthorized - Session token is invalid or expired. Please sign in again."
|
|
12043
|
+
});
|
|
12044
|
+
}
|
|
12045
|
+
return;
|
|
12046
|
+
}
|
|
12029
12047
|
if (PUBLIC_ROUTES.some((route) => request.url === route)) return;
|
|
12030
12048
|
if (request.url.startsWith("/s/")) return;
|
|
12031
12049
|
const path6 = request.url.split("?")[0];
|
|
@@ -14181,14 +14199,6 @@ function header(request, name) {
|
|
|
14181
14199
|
if (typeof value !== "string" || !value) throw new HttpTaskError(400, "INVALID_TASK_INPUT");
|
|
14182
14200
|
return value;
|
|
14183
14201
|
}
|
|
14184
|
-
function assertHeaderScope(request, projectId, workspaceId) {
|
|
14185
|
-
const requestedProject = header(request, "x-project-id");
|
|
14186
|
-
const requestedWorkspace = header(request, "x-workspace-id");
|
|
14187
|
-
const canonicalProject = projectId && projectId !== "default" ? projectId : void 0;
|
|
14188
|
-
if (requestedProject && requestedProject !== "default" && requestedProject !== canonicalProject || requestedWorkspace !== void 0 && requestedWorkspace !== workspaceId) {
|
|
14189
|
-
throw new HttpTaskError(409, "PROJECT_TASK_SCOPE_CONFLICT");
|
|
14190
|
-
}
|
|
14191
|
-
}
|
|
14192
14202
|
function integer(value, minimum = 0) {
|
|
14193
14203
|
if (value === void 0) return void 0;
|
|
14194
14204
|
const parsed = typeof value === "number" ? value : typeof value === "string" && value ? Number(value) : NaN;
|
|
@@ -14443,27 +14453,50 @@ function createTaskController(deps) {
|
|
|
14443
14453
|
}
|
|
14444
14454
|
const metadata = query.metadata === void 0 ? void 0 : parseMetadata(query.metadata);
|
|
14445
14455
|
if (!projectId || projectId === "default") {
|
|
14446
|
-
|
|
14447
|
-
const
|
|
14456
|
+
const requestedWorkspace = header(request, "x-workspace-id");
|
|
14457
|
+
const status = typeof query.status === "string" ? query.status : void 0;
|
|
14458
|
+
const priority = typeof query.priority === "string" ? query.priority : void 0;
|
|
14459
|
+
const parentId = typeof query.parentId === "string" ? query.parentId : void 0;
|
|
14460
|
+
const ownerType = query.ownerType === "user" || query.ownerType === "agent" ? query.ownerType : void 0;
|
|
14461
|
+
const ownerId = typeof query.ownerId === "string" ? query.ownerId : void 0;
|
|
14462
|
+
const effectiveLimit = limit ?? 50;
|
|
14463
|
+
const effectiveOffset = offset ?? 0;
|
|
14464
|
+
const perQueryLimit = effectiveLimit + effectiveOffset;
|
|
14465
|
+
const queries = [{
|
|
14448
14466
|
tenantId,
|
|
14449
14467
|
ownerType: "user",
|
|
14450
14468
|
ownerId: userId,
|
|
14451
14469
|
projectId: null,
|
|
14452
|
-
status
|
|
14453
|
-
priority
|
|
14454
|
-
parentId
|
|
14470
|
+
status,
|
|
14471
|
+
priority,
|
|
14472
|
+
parentId,
|
|
14455
14473
|
metadata,
|
|
14456
|
-
limit,
|
|
14457
|
-
offset
|
|
14458
|
-
}
|
|
14474
|
+
limit: perQueryLimit,
|
|
14475
|
+
offset: 0
|
|
14476
|
+
}];
|
|
14477
|
+
if (requestedWorkspace && deps.readableProjects) {
|
|
14478
|
+
const readable = await deps.readableProjects({ tenantId, userId, workspaceId: requestedWorkspace });
|
|
14479
|
+
for (const project2 of readable) {
|
|
14480
|
+
queries.push({
|
|
14481
|
+
tenantId,
|
|
14482
|
+
workspaceId: project2.workspaceId,
|
|
14483
|
+
projectId: project2.id,
|
|
14484
|
+
status,
|
|
14485
|
+
priority,
|
|
14486
|
+
parentId,
|
|
14487
|
+
metadata,
|
|
14488
|
+
ownerType,
|
|
14489
|
+
ownerId,
|
|
14490
|
+
limit: perQueryLimit,
|
|
14491
|
+
offset: 0
|
|
14492
|
+
});
|
|
14493
|
+
}
|
|
14494
|
+
}
|
|
14495
|
+
const merged = (await Promise.all(queries.map((q) => deps.tasks.list(q)))).flat().sort((left, right) => right.createdAt.getTime() - left.createdAt.getTime());
|
|
14496
|
+
const rows2 = merged.slice(effectiveOffset, effectiveOffset + effectiveLimit);
|
|
14459
14497
|
return { success: true, data: rows2, count: rows2.length };
|
|
14460
14498
|
}
|
|
14461
|
-
const requestedProject = header(request, "x-project-id");
|
|
14462
|
-
if (requestedProject && requestedProject !== "default" && requestedProject !== projectId) {
|
|
14463
|
-
throw new HttpTaskError(409, "PROJECT_TASK_SCOPE_CONFLICT");
|
|
14464
|
-
}
|
|
14465
14499
|
const { project } = await authorize(request, projectId, "read");
|
|
14466
|
-
assertHeaderScope(request, project.id, project.workspaceId);
|
|
14467
14500
|
const rows = await deps.tasks.list({
|
|
14468
14501
|
tenantId,
|
|
14469
14502
|
workspaceId: project.workspaceId,
|
|
@@ -14483,11 +14516,6 @@ function createTaskController(deps) {
|
|
|
14483
14516
|
await preauthorizeExplicitProject(request, authorize);
|
|
14484
14517
|
const current = await deps.tasks.getById(getTenantId17(request), request.params.id);
|
|
14485
14518
|
if (!current) return sendNotFound(reply);
|
|
14486
|
-
try {
|
|
14487
|
-
assertHeaderScope(request, current.projectId, current.workspaceId);
|
|
14488
|
-
} catch {
|
|
14489
|
-
throw notFound();
|
|
14490
|
-
}
|
|
14491
14519
|
await authorizeRead(request, current, authorize);
|
|
14492
14520
|
return { success: true, data: current };
|
|
14493
14521
|
});
|
|
@@ -14495,8 +14523,6 @@ function createTaskController(deps) {
|
|
|
14495
14523
|
const body = snapshotCreate(request.body);
|
|
14496
14524
|
const tenantId = getTenantId17(request);
|
|
14497
14525
|
const userId = getUserId4(request);
|
|
14498
|
-
const requestedProject = header(request, "x-project-id");
|
|
14499
|
-
if (requestedProject && requestedProject !== "default" && requestedProject !== body.projectId) throw new HttpTaskError(409, "PROJECT_TASK_SCOPE_CONFLICT");
|
|
14500
14526
|
const governed = governedCreate(body);
|
|
14501
14527
|
if (governed) return reply.code(409).send({ success: false, code: governed });
|
|
14502
14528
|
if (!body.projectId || body.projectId === "default") {
|
|
@@ -14506,11 +14532,8 @@ function createTaskController(deps) {
|
|
|
14506
14532
|
}
|
|
14507
14533
|
const capability2 = body.ownerType === "agent" ? "manage" : "send";
|
|
14508
14534
|
const { project } = await authorize(request, body.projectId, capability2);
|
|
14509
|
-
const requestedWorkspace = header(request, "x-workspace-id");
|
|
14510
|
-
if (body.workspaceId !== void 0 && body.workspaceId !== project.workspaceId || requestedWorkspace !== void 0 && requestedWorkspace !== project.workspaceId) throw new HttpTaskError(409, "PROJECT_TASK_SCOPE_CONFLICT");
|
|
14511
14535
|
return mutationMutex.runExclusive({ tenantId, projectId: project.id }, async () => {
|
|
14512
14536
|
const lockedAccess = await authorize(request, project.id, capability2);
|
|
14513
|
-
if (lockedAccess.project.workspaceId !== project.workspaceId) throw new HttpTaskError(409, "PROJECT_TASK_SCOPE_CONFLICT");
|
|
14514
14537
|
const candidate = {
|
|
14515
14538
|
id: body.id ?? "__new_task__",
|
|
14516
14539
|
tenantId,
|
|
@@ -14528,13 +14551,8 @@ function createTaskController(deps) {
|
|
|
14528
14551
|
let ownerType = "user";
|
|
14529
14552
|
let ownerId = userId;
|
|
14530
14553
|
if (body.ownerType === "agent") {
|
|
14531
|
-
const target = await deps.bots.findByAssistant(tenantId, project.id, body.ownerId ?? "");
|
|
14532
|
-
const room = await deps.rooms.getMainRoom(tenantId, project.id);
|
|
14533
|
-
if (!target || !room || target.status !== "active" || target.workspaceId !== project.workspaceId || target.roomId !== room.id) {
|
|
14534
|
-
return reply.code(409).send({ success: false, code: "PROJECT_TASK_OWNER_INACTIVE" });
|
|
14535
|
-
}
|
|
14536
14554
|
ownerType = "agent";
|
|
14537
|
-
ownerId =
|
|
14555
|
+
ownerId = body.ownerId ?? "";
|
|
14538
14556
|
}
|
|
14539
14557
|
const created = await deps.tasks.create({
|
|
14540
14558
|
...body,
|
|
@@ -14558,11 +14576,6 @@ function createTaskController(deps) {
|
|
|
14558
14576
|
await preauthorizeExplicitProject(request, authorize);
|
|
14559
14577
|
const current = await deps.tasks.getById(getTenantId17(request), request.params.id);
|
|
14560
14578
|
if (!current) return sendNotFound(reply);
|
|
14561
|
-
try {
|
|
14562
|
-
assertHeaderScope(request, current.projectId, current.workspaceId);
|
|
14563
|
-
} catch {
|
|
14564
|
-
throw notFound();
|
|
14565
|
-
}
|
|
14566
14579
|
await authorizeMutation(request, current, authorize);
|
|
14567
14580
|
return lockedTask(request, current, async (locked) => {
|
|
14568
14581
|
const governed = governedUpdate(locked, body);
|
|
@@ -14581,11 +14594,6 @@ function createTaskController(deps) {
|
|
|
14581
14594
|
await preauthorizeExplicitProject(request, authorize);
|
|
14582
14595
|
const current = await deps.tasks.getById(getTenantId17(request), request.params.id);
|
|
14583
14596
|
if (!current) return sendNotFound(reply);
|
|
14584
|
-
try {
|
|
14585
|
-
assertHeaderScope(request, current.projectId, current.workspaceId);
|
|
14586
|
-
} catch {
|
|
14587
|
-
throw notFound();
|
|
14588
|
-
}
|
|
14589
14597
|
await authorizeMutation(request, current, authorize);
|
|
14590
14598
|
return lockedTask(request, current, async (locked) => {
|
|
14591
14599
|
if (locked.ownerType === "agent") return reply.code(403).send({ success: false, code: "AGENT_RUNTIME_REQUIRED" });
|
|
@@ -14600,11 +14608,6 @@ function createTaskController(deps) {
|
|
|
14600
14608
|
await preauthorizeExplicitProject(request, authorize);
|
|
14601
14609
|
const current = await deps.tasks.getById(getTenantId17(request), request.params.id);
|
|
14602
14610
|
if (!current) return sendNotFound(reply);
|
|
14603
|
-
try {
|
|
14604
|
-
assertHeaderScope(request, current.projectId, current.workspaceId);
|
|
14605
|
-
} catch {
|
|
14606
|
-
throw notFound();
|
|
14607
|
-
}
|
|
14608
14611
|
await authorizeMutation(request, current, authorize);
|
|
14609
14612
|
return lockedTask(request, current, async (locked) => {
|
|
14610
14613
|
if (locked.ownerType === "agent") return reply.code(403).send({ success: false, code: "AGENT_RUNTIME_REQUIRED" });
|
|
@@ -14622,11 +14625,6 @@ function createTaskController(deps) {
|
|
|
14622
14625
|
await preauthorizeExplicitProject(request, authorize);
|
|
14623
14626
|
const current = await deps.tasks.getById(getTenantId17(request), request.params.id);
|
|
14624
14627
|
if (!current) return sendNotFound(reply);
|
|
14625
|
-
try {
|
|
14626
|
-
assertHeaderScope(request, current.projectId, current.workspaceId);
|
|
14627
|
-
} catch {
|
|
14628
|
-
throw notFound();
|
|
14629
|
-
}
|
|
14630
14628
|
await authorizeRead(request, current, authorize);
|
|
14631
14629
|
const query = exactObject(request.query, ["action", "limit", "offset"]);
|
|
14632
14630
|
const rows = await deps.workItems.list({
|
|
@@ -14644,8 +14642,6 @@ function createTaskController(deps) {
|
|
|
14644
14642
|
const reassign = withErrors(async (request, reply) => {
|
|
14645
14643
|
const body = snapshotReassign(request.body);
|
|
14646
14644
|
const pathProject = request.params.projectId;
|
|
14647
|
-
const requestedProject = header(request, "x-project-id");
|
|
14648
|
-
if (requestedProject && requestedProject !== "default" && requestedProject !== pathProject) throw new HttpTaskError(409, "PROJECT_TASK_SCOPE_CONFLICT");
|
|
14649
14645
|
const access = await authorize(request, pathProject, "manage");
|
|
14650
14646
|
const snapshot = await deps.tasks.getById(getTenantId17(request), request.params.id);
|
|
14651
14647
|
if (!snapshot || snapshot.projectId !== access.project.id || snapshot.workspaceId !== access.project.workspaceId || snapshot.ownerType !== "agent") throw notFound();
|
|
@@ -18691,6 +18687,7 @@ var import_node_crypto12 = require("crypto");
|
|
|
18691
18687
|
var import_core47 = require("@axiom-lattice/core");
|
|
18692
18688
|
var SYSTEM_ACTOR2 = "system";
|
|
18693
18689
|
var TERMINAL_ACTIONS = ["completed", "failed", "interrupted", "cancelled"];
|
|
18690
|
+
var MAX_LEARNING_ROUND_ANCESTORS = 8;
|
|
18694
18691
|
var ProjectTaskObserver = class {
|
|
18695
18692
|
/** @param deps Durable stores and Project Task runtime services. @param options Positive bounded scan limits. */
|
|
18696
18693
|
constructor(deps, options) {
|
|
@@ -18848,6 +18845,7 @@ var ProjectTaskObserver = class {
|
|
|
18848
18845
|
signal ?? (signal = this.deps.signal);
|
|
18849
18846
|
if (signal?.aborted || this.disposed) return;
|
|
18850
18847
|
if (!(0, import_core47.isProjectLifecycleTask)(task) || task.ownerType !== "agent") return;
|
|
18848
|
+
if (await this.belongsToLearningRound(task)) return;
|
|
18851
18849
|
if (task.status === "pending") {
|
|
18852
18850
|
if (await this.isReady(task) && !signal?.aborted && !this.disposed) await this.runStart(task, signal);
|
|
18853
18851
|
return;
|
|
@@ -18860,6 +18858,22 @@ var ProjectTaskObserver = class {
|
|
|
18860
18858
|
await this.resumeIfReady(task, signal);
|
|
18861
18859
|
}
|
|
18862
18860
|
}
|
|
18861
|
+
// SCOPE-COMPAT: learning rounds (metadata.trainingRound) and their subtrees execute in the
|
|
18862
|
+
// architect's builder thread (canvas CopilotPanel), not the Project Room runtime. Exempting
|
|
18863
|
+
// them here keeps admission hints, startup reconciliation, and dependent re-evaluation from
|
|
18864
|
+
// claiming these tasks — no roster warnings, no restart noise, and no double execution if
|
|
18865
|
+
// the architect later joins the room roster.
|
|
18866
|
+
async belongsToLearningRound(task) {
|
|
18867
|
+
let current = task;
|
|
18868
|
+
for (let depth = 0; current && depth < MAX_LEARNING_ROUND_ANCESTORS; depth += 1) {
|
|
18869
|
+
if (current.metadata?.trainingRound === true) return true;
|
|
18870
|
+
if (!current.parentId) return false;
|
|
18871
|
+
const parent = snapshotTaskItem(await this.deps.tasks.getById(current.tenantId, current.parentId));
|
|
18872
|
+
if (!parent || parent.id !== current.parentId || parent.tenantId !== current.tenantId) return false;
|
|
18873
|
+
current = parent;
|
|
18874
|
+
}
|
|
18875
|
+
return false;
|
|
18876
|
+
}
|
|
18863
18877
|
/** Collects every bounded dependent ID before re-reading or scheduling any dependent. */
|
|
18864
18878
|
async reevaluateDependents(task) {
|
|
18865
18879
|
const { ids } = await this.collectDependentIds(task);
|
|
@@ -18965,6 +18979,7 @@ var ProjectTaskObserver = class {
|
|
|
18965
18979
|
}
|
|
18966
18980
|
async wakeParent(child) {
|
|
18967
18981
|
if (!child.parentId) return;
|
|
18982
|
+
if (await this.belongsToLearningRound(child)) return;
|
|
18968
18983
|
const parent = snapshotTaskItem(await this.deps.tasks.getById(child.tenantId, child.parentId));
|
|
18969
18984
|
if (!parent || parent.id !== child.parentId || child.parentId !== parent.id || parent.ownerType !== "agent" || parent.workspaceId !== child.workspaceId || parent.projectId !== child.projectId || parent.status !== "interrupted" || !parent.dependencies?.includes(child.id)) return;
|
|
18970
18985
|
const interruption = (0, import_core47.parseExternalDependency)(parent.context?.interruption);
|
|
@@ -19428,7 +19443,7 @@ var RoomProjectionService = class {
|
|
|
19428
19443
|
* @returns Counts for inspected rows, projections, warnings, and bound truncation.
|
|
19429
19444
|
*/
|
|
19430
19445
|
async repairProject(input) {
|
|
19431
|
-
const result = { inspected: 0, projected: 0, warnings: 0, truncated: false };
|
|
19446
|
+
const result = { inspected: 0, projected: 0, warnings: 0, skipped: 0, truncated: false };
|
|
19432
19447
|
let before;
|
|
19433
19448
|
for (let page = 0; page < input.maxPages; page += 1) {
|
|
19434
19449
|
const rows = await this.deps.events.listProjectLifecycleEvents({
|
|
@@ -19454,10 +19469,15 @@ var RoomProjectionService = class {
|
|
|
19454
19469
|
if (safeIdentity) await this.warning("INVALID_PROJECTABLE_EVENT", safeIdentity, input);
|
|
19455
19470
|
continue;
|
|
19456
19471
|
}
|
|
19472
|
+
let stored;
|
|
19473
|
+
try {
|
|
19474
|
+
stored = await this.deps.tasks.getById(input.tenantId, row.taskId);
|
|
19475
|
+
} catch {
|
|
19476
|
+
stored = void 0;
|
|
19477
|
+
}
|
|
19457
19478
|
let task;
|
|
19458
19479
|
try {
|
|
19459
|
-
|
|
19460
|
-
task = stored === null ? void 0 : snapshotProjectTask(stored);
|
|
19480
|
+
task = stored === null || stored === void 0 ? void 0 : snapshotTaskItem(stored);
|
|
19461
19481
|
} catch {
|
|
19462
19482
|
task = void 0;
|
|
19463
19483
|
}
|
|
@@ -19466,6 +19486,10 @@ var RoomProjectionService = class {
|
|
|
19466
19486
|
await this.warning("INVALID_PROJECTABLE_EVENT", { taskId: row.taskId, eventKey: row.eventKey }, input);
|
|
19467
19487
|
continue;
|
|
19468
19488
|
}
|
|
19489
|
+
if (task.ownerType !== "agent" || !(0, import_core48.isProjectLifecycleTask)(task)) {
|
|
19490
|
+
result.skipped += 1;
|
|
19491
|
+
continue;
|
|
19492
|
+
}
|
|
19469
19493
|
const projected = await this.project({ task, room: input.room, workItem: row });
|
|
19470
19494
|
if (projected.kind === "projected") result.projected += 1;
|
|
19471
19495
|
else result.warnings += 1;
|
|
@@ -21259,6 +21283,18 @@ var registerLatticeRoutes = (app2, channelDeps, options = {}) => {
|
|
|
21259
21283
|
projectMutationMutex: import_core51.projectMutationMutex,
|
|
21260
21284
|
executionMutex: projectTaskComposition?.mutex ?? {
|
|
21261
21285
|
runExclusive: (_scope, operation) => operation()
|
|
21286
|
+
},
|
|
21287
|
+
readableProjects: async ({ tenantId, userId, workspaceId }) => {
|
|
21288
|
+
const projects = await projectStore.getProjectsByWorkspace(tenantId, workspaceId);
|
|
21289
|
+
const readable = [];
|
|
21290
|
+
for (const project of projects) {
|
|
21291
|
+
try {
|
|
21292
|
+
await accessService.requireExistingProjectAccess({ tenantId, userId, projectId: project.id, capability: "read" });
|
|
21293
|
+
readable.push({ id: project.id, workspaceId: project.workspaceId });
|
|
21294
|
+
} catch {
|
|
21295
|
+
}
|
|
21296
|
+
}
|
|
21297
|
+
return readable;
|
|
21262
21298
|
}
|
|
21263
21299
|
}));
|
|
21264
21300
|
app2.get(
|