@axiom-lattice/gateway 4.0.0 → 4.0.2
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/{a2a-standard-U2XYG45L.mjs → a2a-standard-VYQCHMBV.mjs} +3 -10
- package/dist/a2a-standard-VYQCHMBV.mjs.map +1 -0
- package/dist/{chunk-IDOEIBCF.mjs → chunk-J4BK77PA.mjs} +13 -2
- package/dist/chunk-J4BK77PA.mjs.map +1 -0
- package/dist/index.js +1272 -148
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1212 -93
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -6
- package/dist/a2a-standard-U2XYG45L.mjs.map +0 -1
- package/dist/chunk-IDOEIBCF.mjs.map +0 -1
package/dist/index.js
CHANGED
|
@@ -958,6 +958,21 @@ var init_wechat_client = __esm({
|
|
|
958
958
|
}
|
|
959
959
|
});
|
|
960
960
|
|
|
961
|
+
// src/services/files/task-file-refs.ts
|
|
962
|
+
function formatFileRefsSection(refs) {
|
|
963
|
+
const lines = refs.map((ref) => {
|
|
964
|
+
const label = ref.name ?? ref.uri;
|
|
965
|
+
const mime = ref.mimeType ? ` (${ref.mimeType})` : "";
|
|
966
|
+
return `- ${label}${mime}: ${ref.uri}`;
|
|
967
|
+
});
|
|
968
|
+
return ["[\u9644\u4EF6]", ...lines].join("\n");
|
|
969
|
+
}
|
|
970
|
+
var init_task_file_refs = __esm({
|
|
971
|
+
"src/services/files/task-file-refs.ts"() {
|
|
972
|
+
"use strict";
|
|
973
|
+
}
|
|
974
|
+
});
|
|
975
|
+
|
|
961
976
|
// src/channels/wechat/context-store.ts
|
|
962
977
|
function makeKey(installationId, senderId) {
|
|
963
978
|
return `${installationId}::${senderId}`;
|
|
@@ -1025,18 +1040,18 @@ function extractText(msg) {
|
|
|
1025
1040
|
}
|
|
1026
1041
|
return null;
|
|
1027
1042
|
}
|
|
1028
|
-
var
|
|
1043
|
+
var import_zod6, logger4, wechatConfigSchema, MAX_RECONNECT_DELAY_MS, BASE_RECONNECT_DELAY_MS, HEARTBEAT_INTERVAL_MS, MSG_TYPE_USER, MSG_ITEM_TEXT, activeConnections2, seenClientIds, wechatChannelAdapter;
|
|
1029
1044
|
var init_WechatChannelAdapter = __esm({
|
|
1030
1045
|
"src/channels/wechat/WechatChannelAdapter.ts"() {
|
|
1031
1046
|
"use strict";
|
|
1032
|
-
|
|
1047
|
+
import_zod6 = require("zod");
|
|
1033
1048
|
init_wechat_client();
|
|
1034
1049
|
init_context_store();
|
|
1035
1050
|
init_Logger();
|
|
1036
1051
|
logger4 = new Logger({ serviceName: "lattice/gateway/wechat" });
|
|
1037
|
-
wechatConfigSchema =
|
|
1038
|
-
botToken:
|
|
1039
|
-
uin:
|
|
1052
|
+
wechatConfigSchema = import_zod6.z.object({
|
|
1053
|
+
botToken: import_zod6.z.string(),
|
|
1054
|
+
uin: import_zod6.z.string().optional()
|
|
1040
1055
|
});
|
|
1041
1056
|
MAX_RECONNECT_DELAY_MS = 3e4;
|
|
1042
1057
|
BASE_RECONNECT_DELAY_MS = 1e3;
|
|
@@ -1497,7 +1512,7 @@ function extractFailureReason(task) {
|
|
|
1497
1512
|
return extractText2(task.status.message.parts);
|
|
1498
1513
|
}
|
|
1499
1514
|
function observationEventKey(task, result, failureReason) {
|
|
1500
|
-
const digest = (0,
|
|
1515
|
+
const digest = (0, import_node_crypto5.createHash)("sha256").update(JSON.stringify({
|
|
1501
1516
|
state: task.status.state,
|
|
1502
1517
|
timestamp: task.status.timestamp,
|
|
1503
1518
|
message: task.status.message,
|
|
@@ -1507,7 +1522,7 @@ function observationEventKey(task, result, failureReason) {
|
|
|
1507
1522
|
return `a2a-status-observation:${task.id}:${digest}`;
|
|
1508
1523
|
}
|
|
1509
1524
|
function snapshotEventKey(task) {
|
|
1510
|
-
const digest = (0,
|
|
1525
|
+
const digest = (0, import_node_crypto5.createHash)("sha256").update(JSON.stringify(task)).digest("hex");
|
|
1511
1526
|
return `a2a-task-snapshot:${task.id}:${digest}`;
|
|
1512
1527
|
}
|
|
1513
1528
|
function isGovernedObservation(state) {
|
|
@@ -1519,11 +1534,11 @@ function isA2ATaskSnapshot(value) {
|
|
|
1519
1534
|
function snapshotSequence(value) {
|
|
1520
1535
|
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
|
|
1521
1536
|
}
|
|
1522
|
-
var
|
|
1537
|
+
var import_node_crypto5, import_node_util, TASK_STATUS_TO_A2A_STATE, TITLE_MAX_LENGTH, A2ATaskScopeConflictError, A2ATaskStoreAdapter;
|
|
1523
1538
|
var init_A2ATaskStoreAdapter = __esm({
|
|
1524
1539
|
"src/services/a2a/A2ATaskStoreAdapter.ts"() {
|
|
1525
1540
|
"use strict";
|
|
1526
|
-
|
|
1541
|
+
import_node_crypto5 = require("crypto");
|
|
1527
1542
|
import_node_util = require("util");
|
|
1528
1543
|
TASK_STATUS_TO_A2A_STATE = {
|
|
1529
1544
|
pending: "submitted",
|
|
@@ -1842,14 +1857,6 @@ function filePartsToRefs(parts, addedBy) {
|
|
|
1842
1857
|
}
|
|
1843
1858
|
return refs;
|
|
1844
1859
|
}
|
|
1845
|
-
function formatFileRefsSection(refs) {
|
|
1846
|
-
const lines = refs.map((ref) => {
|
|
1847
|
-
const label = ref.name ?? ref.uri;
|
|
1848
|
-
const mime = ref.mimeType ? ` (${ref.mimeType})` : "";
|
|
1849
|
-
return `- ${label}${mime}: ${ref.uri}`;
|
|
1850
|
-
});
|
|
1851
|
-
return ["[\u9644\u4EF6]", ...lines].join("\n");
|
|
1852
|
-
}
|
|
1853
1860
|
async function filePartsToRefsWithIngest(parts, addedBy, ingestor, dedupeKeyBase, projectId) {
|
|
1854
1861
|
if (!ingestor) {
|
|
1855
1862
|
return filePartsToRefs(parts, addedBy);
|
|
@@ -1866,6 +1873,7 @@ async function filePartsToRefsWithIngest(parts, addedBy, ingestor, dedupeKeyBase
|
|
|
1866
1873
|
var init_a2aFileRefs = __esm({
|
|
1867
1874
|
"src/services/a2a/a2aFileRefs.ts"() {
|
|
1868
1875
|
"use strict";
|
|
1876
|
+
init_task_file_refs();
|
|
1869
1877
|
}
|
|
1870
1878
|
});
|
|
1871
1879
|
|
|
@@ -1887,14 +1895,15 @@ function agentMessage(text) {
|
|
|
1887
1895
|
parts: [{ kind: "text", text }]
|
|
1888
1896
|
};
|
|
1889
1897
|
}
|
|
1890
|
-
var import_uuid10,
|
|
1898
|
+
var import_uuid10, import_core55, import_protocols8, _AxiomAgentExecutor, AxiomAgentExecutor;
|
|
1891
1899
|
var init_AxiomAgentExecutor = __esm({
|
|
1892
1900
|
"src/services/a2a/AxiomAgentExecutor.ts"() {
|
|
1893
1901
|
"use strict";
|
|
1894
1902
|
import_uuid10 = require("uuid");
|
|
1895
|
-
|
|
1896
|
-
|
|
1903
|
+
import_core55 = require("@axiom-lattice/core");
|
|
1904
|
+
import_protocols8 = require("@axiom-lattice/protocols");
|
|
1897
1905
|
init_a2aFileRefs();
|
|
1906
|
+
init_task_file_refs();
|
|
1898
1907
|
_AxiomAgentExecutor = class _AxiomAgentExecutor {
|
|
1899
1908
|
constructor(options) {
|
|
1900
1909
|
this.scope = options.scope;
|
|
@@ -1978,7 +1987,7 @@ var init_AxiomAgentExecutor = __esm({
|
|
|
1978
1987
|
await this.taskStore.save(initialTask);
|
|
1979
1988
|
bus.publish(initialTask);
|
|
1980
1989
|
try {
|
|
1981
|
-
const agent =
|
|
1990
|
+
const agent = import_core55.agentInstanceManager.getAgent({
|
|
1982
1991
|
assistant_id: this.scope.assistantId,
|
|
1983
1992
|
thread_id: threadId,
|
|
1984
1993
|
tenant_id: this.scope.tenantId,
|
|
@@ -2001,16 +2010,16 @@ ${formatFileRefsSection(inputFiles)}` : text;
|
|
|
2001
2010
|
custom_run_config: { taskId: ctx.taskId }
|
|
2002
2011
|
});
|
|
2003
2012
|
const stream = agent.chunkStream(messageId, [
|
|
2004
|
-
|
|
2013
|
+
import_protocols8.MessageChunkTypes.MESSAGE_COMPLETED
|
|
2005
2014
|
]);
|
|
2006
2015
|
for await (const chunk of stream) {
|
|
2007
2016
|
if (execution.canceled) return;
|
|
2008
2017
|
const chunkType = chunk.type;
|
|
2009
2018
|
const chunkText = chunk.data?.content ?? "";
|
|
2010
|
-
if (chunkType ===
|
|
2019
|
+
if (chunkType === import_protocols8.MessageChunkTypes.INTERRUPT) {
|
|
2011
2020
|
break;
|
|
2012
2021
|
}
|
|
2013
|
-
if (chunkType ===
|
|
2022
|
+
if (chunkType === import_protocols8.MessageChunkTypes.AI || chunkType === import_protocols8.MessageChunkTypes.TOOL) {
|
|
2014
2023
|
if (chunkText) {
|
|
2015
2024
|
bus.publish({
|
|
2016
2025
|
kind: "status-update",
|
|
@@ -2128,7 +2137,7 @@ ${formatFileRefsSection(inputFiles)}` : text;
|
|
|
2128
2137
|
const threadId = task ? asString2(task.metadata?.threadId) : void 0;
|
|
2129
2138
|
if (threadId) {
|
|
2130
2139
|
try {
|
|
2131
|
-
const agent =
|
|
2140
|
+
const agent = import_core55.agentInstanceManager.getAgent({
|
|
2132
2141
|
assistant_id: this.scope.assistantId,
|
|
2133
2142
|
thread_id: threadId,
|
|
2134
2143
|
tenant_id: this.scope.tenantId,
|
|
@@ -2490,13 +2499,13 @@ function isAsyncGenerator(value) {
|
|
|
2490
2499
|
}
|
|
2491
2500
|
function registerA2AStandardRoutes(app2, deps) {
|
|
2492
2501
|
const authService = deps.authService;
|
|
2493
|
-
const getTaskStore = deps.getTaskStoreLattice ?? (() => (0,
|
|
2494
|
-
const getTaskWorkItemStore2 = deps.getTaskWorkItemStore ?? (() => (0,
|
|
2495
|
-
const getAssistantStore3 = deps.getAssistantStore ?? (() => (0,
|
|
2496
|
-
const getProjectStore2 = deps.getProjectStore ?? (() => (0,
|
|
2502
|
+
const getTaskStore = deps.getTaskStoreLattice ?? (() => (0, import_core56.getStoreLattice)("default", "task").store);
|
|
2503
|
+
const getTaskWorkItemStore2 = deps.getTaskWorkItemStore ?? (() => (0, import_core56.getStoreLattice)("default", "taskWorkItem").store);
|
|
2504
|
+
const getAssistantStore3 = deps.getAssistantStore ?? (() => (0, import_core56.getStoreLattice)("default", "assistant").store);
|
|
2505
|
+
const getProjectStore2 = deps.getProjectStore ?? (() => (0, import_core56.getStoreLattice)("default", "project").store);
|
|
2497
2506
|
const getTenantStore = deps.getTenantStore ?? (() => {
|
|
2498
2507
|
try {
|
|
2499
|
-
return (0,
|
|
2508
|
+
return (0, import_core56.getStoreLattice)("default", "tenant").store;
|
|
2500
2509
|
} catch {
|
|
2501
2510
|
return void 0;
|
|
2502
2511
|
}
|
|
@@ -2588,7 +2597,7 @@ function registerA2AStandardRoutes(app2, deps) {
|
|
|
2588
2597
|
const taskStore = new A2ATaskStoreAdapter({
|
|
2589
2598
|
taskStore: internalTaskStore,
|
|
2590
2599
|
workItemStore,
|
|
2591
|
-
lifecycle: (0,
|
|
2600
|
+
lifecycle: (0, import_core56.createTaskLifecycleService)({
|
|
2592
2601
|
taskStore: internalTaskStore,
|
|
2593
2602
|
workItemStore
|
|
2594
2603
|
}),
|
|
@@ -2707,7 +2716,7 @@ data: ${JSON.stringify(errorResponse)}
|
|
|
2707
2716
|
const taskStore = new A2ATaskStoreAdapter({
|
|
2708
2717
|
taskStore: internalTaskStore,
|
|
2709
2718
|
workItemStore,
|
|
2710
|
-
lifecycle: (0,
|
|
2719
|
+
lifecycle: (0, import_core56.createTaskLifecycleService)({
|
|
2711
2720
|
taskStore: internalTaskStore,
|
|
2712
2721
|
workItemStore
|
|
2713
2722
|
}),
|
|
@@ -2752,13 +2761,13 @@ data: ${JSON.stringify(data)}
|
|
|
2752
2761
|
}
|
|
2753
2762
|
);
|
|
2754
2763
|
}
|
|
2755
|
-
var import_uuid11, import_server2,
|
|
2764
|
+
var import_uuid11, import_server2, import_core56, SSE_HEADERS;
|
|
2756
2765
|
var init_a2a_standard = __esm({
|
|
2757
2766
|
"src/routes/a2a-standard.ts"() {
|
|
2758
2767
|
"use strict";
|
|
2759
2768
|
import_uuid11 = require("uuid");
|
|
2760
2769
|
import_server2 = require("@a2a-js/sdk/server");
|
|
2761
|
-
|
|
2770
|
+
import_core56 = require("@axiom-lattice/core");
|
|
2762
2771
|
init_A2AAuthService();
|
|
2763
2772
|
init_A2AAgentCardBuilder();
|
|
2764
2773
|
init_A2ATaskStoreAdapter();
|
|
@@ -2932,12 +2941,12 @@ var resources_exports = {};
|
|
|
2932
2941
|
__export(resources_exports, {
|
|
2933
2942
|
ResourceController: () => ResourceController
|
|
2934
2943
|
});
|
|
2935
|
-
var import_bcryptjs,
|
|
2944
|
+
var import_bcryptjs, import_core57, ResourceController;
|
|
2936
2945
|
var init_resources = __esm({
|
|
2937
2946
|
"src/controllers/resources.ts"() {
|
|
2938
2947
|
"use strict";
|
|
2939
2948
|
import_bcryptjs = __toESM(require("bcryptjs"));
|
|
2940
|
-
|
|
2949
|
+
import_core57 = require("@axiom-lattice/core");
|
|
2941
2950
|
init_mime();
|
|
2942
2951
|
ResourceController = class {
|
|
2943
2952
|
constructor(deps) {
|
|
@@ -2952,14 +2961,14 @@ var init_resources = __esm({
|
|
|
2952
2961
|
if (!workspaceId || !projectId) {
|
|
2953
2962
|
return reply.status(400).send({ error: "x-workspace-id and x-project-id headers required" });
|
|
2954
2963
|
}
|
|
2955
|
-
const payload = (0,
|
|
2964
|
+
const payload = (0, import_core57.createSharePayload)(
|
|
2956
2965
|
tenantId,
|
|
2957
2966
|
workspaceId,
|
|
2958
2967
|
projectId,
|
|
2959
2968
|
userId,
|
|
2960
2969
|
body
|
|
2961
2970
|
);
|
|
2962
|
-
const token = (0,
|
|
2971
|
+
const token = (0, import_core57.generateToken)();
|
|
2963
2972
|
try {
|
|
2964
2973
|
await this.deps.store.create({ ...payload, token });
|
|
2965
2974
|
this.deps.logger.info(
|
|
@@ -3111,7 +3120,7 @@ var init_resources = __esm({
|
|
|
3111
3120
|
resourcePath: subPath
|
|
3112
3121
|
}, subPath, ext, request, reply);
|
|
3113
3122
|
}
|
|
3114
|
-
const volume = (0,
|
|
3123
|
+
const volume = (0, import_core57.buildNamedVolumeName)("p", "project", tenantId, workspaceId, projectId);
|
|
3115
3124
|
return this._resolveAndServe(
|
|
3116
3125
|
volume,
|
|
3117
3126
|
subPath,
|
|
@@ -4098,6 +4107,90 @@ async function getAssistantStats(request, reply) {
|
|
|
4098
4107
|
var import_uuid = require("uuid");
|
|
4099
4108
|
var import_core3 = require("@axiom-lattice/core");
|
|
4100
4109
|
var import_protocols2 = require("@axiom-lattice/protocols");
|
|
4110
|
+
|
|
4111
|
+
// src/services/agents/ScopedAgentExecution.ts
|
|
4112
|
+
async function executeScopedAgentMessage(agent, request, stopTypes, options) {
|
|
4113
|
+
const { messageId } = options === void 0 ? await agent.addMessage(request) : await agent.addMessage(request, options.queueMode);
|
|
4114
|
+
return { messageId, stream: agent.chunkStream(messageId, stopTypes) };
|
|
4115
|
+
}
|
|
4116
|
+
|
|
4117
|
+
// src/services/streaming/pipeAsyncIterableToSse.ts
|
|
4118
|
+
async function pipeAsyncIterableToSse(raw, stream, options = {}) {
|
|
4119
|
+
const iterator = stream[Symbol.asyncIterator]();
|
|
4120
|
+
const project = options.project ?? ((value) => [value]);
|
|
4121
|
+
const serialize = options.serialize ?? ((event) => `data: ${JSON.stringify(event)}
|
|
4122
|
+
|
|
4123
|
+
`);
|
|
4124
|
+
const closeSources = options.closeSource && options.closeSource !== raw ? [raw, options.closeSource] : [raw];
|
|
4125
|
+
let closed = raw.destroyed || raw.writableEnded;
|
|
4126
|
+
let returned = false;
|
|
4127
|
+
let resolveClosed;
|
|
4128
|
+
const closedPromise = new Promise((resolve3) => {
|
|
4129
|
+
resolveClosed = resolve3;
|
|
4130
|
+
});
|
|
4131
|
+
const returnConsumer = () => {
|
|
4132
|
+
if (returned) return;
|
|
4133
|
+
returned = true;
|
|
4134
|
+
void Promise.resolve(iterator.return?.()).catch(() => void 0);
|
|
4135
|
+
};
|
|
4136
|
+
const onClose = () => {
|
|
4137
|
+
closed = true;
|
|
4138
|
+
returnConsumer();
|
|
4139
|
+
resolveClosed?.();
|
|
4140
|
+
};
|
|
4141
|
+
for (const source of closeSources) {
|
|
4142
|
+
source.on("close", onClose);
|
|
4143
|
+
source.on("error", onClose);
|
|
4144
|
+
}
|
|
4145
|
+
try {
|
|
4146
|
+
while (!closed) {
|
|
4147
|
+
const next = await Promise.race([
|
|
4148
|
+
iterator.next().then((result) => ({ kind: "next", result })),
|
|
4149
|
+
closedPromise.then(() => ({ kind: "closed" }))
|
|
4150
|
+
]);
|
|
4151
|
+
if (next.kind === "closed" || next.result.done) break;
|
|
4152
|
+
for (const event of project(next.result.value)) {
|
|
4153
|
+
if (!await writeSse(raw, serialize(event), closedPromise)) {
|
|
4154
|
+
closed = true;
|
|
4155
|
+
returnConsumer();
|
|
4156
|
+
break;
|
|
4157
|
+
}
|
|
4158
|
+
}
|
|
4159
|
+
}
|
|
4160
|
+
} catch (error) {
|
|
4161
|
+
if (!options.onError) throw error;
|
|
4162
|
+
if (!closed && !raw.destroyed && !raw.writableEnded) {
|
|
4163
|
+
for (const event of options.onError(error)) {
|
|
4164
|
+
if (!await writeSse(raw, serialize(event), closedPromise)) break;
|
|
4165
|
+
}
|
|
4166
|
+
}
|
|
4167
|
+
returnConsumer();
|
|
4168
|
+
} finally {
|
|
4169
|
+
for (const source of closeSources) {
|
|
4170
|
+
source.removeListener("close", onClose);
|
|
4171
|
+
source.removeListener("error", onClose);
|
|
4172
|
+
}
|
|
4173
|
+
if (!closed && !raw.destroyed && !raw.writableEnded) raw.end();
|
|
4174
|
+
}
|
|
4175
|
+
}
|
|
4176
|
+
async function writeSse(raw, value, closed) {
|
|
4177
|
+
if (raw.destroyed || raw.writableEnded) return false;
|
|
4178
|
+
if (raw.write(value)) return true;
|
|
4179
|
+
return new Promise((resolve3) => {
|
|
4180
|
+
let settled = false;
|
|
4181
|
+
const onDrain = () => finish(!raw.destroyed && !raw.writableEnded);
|
|
4182
|
+
const finish = (writable) => {
|
|
4183
|
+
if (settled) return;
|
|
4184
|
+
settled = true;
|
|
4185
|
+
raw.removeListener("drain", onDrain);
|
|
4186
|
+
resolve3(writable);
|
|
4187
|
+
};
|
|
4188
|
+
raw.once("drain", onDrain);
|
|
4189
|
+
void closed.then(() => finish(false));
|
|
4190
|
+
});
|
|
4191
|
+
}
|
|
4192
|
+
|
|
4193
|
+
// src/controllers/run.ts
|
|
4101
4194
|
function getUserId2(request) {
|
|
4102
4195
|
const authUser = request.user;
|
|
4103
4196
|
if (authUser?.id) return authUser.id;
|
|
@@ -4164,20 +4257,22 @@ var createRun = async (request, reply) => {
|
|
|
4164
4257
|
});
|
|
4165
4258
|
try {
|
|
4166
4259
|
const messageInput = message_id ? { ...input, id: message_id } : input;
|
|
4167
|
-
const result = await agent
|
|
4260
|
+
const result = await executeScopedAgentMessage(agent, {
|
|
4168
4261
|
input: messageInput,
|
|
4169
4262
|
command,
|
|
4170
4263
|
custom_run_config: mergedConfig
|
|
4171
|
-
},
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4264
|
+
}, [import_protocols2.MessageChunkTypes.MESSAGE_COMPLETED, import_protocols2.MessageChunkTypes.INTERRUPT, import_protocols2.MessageChunkTypes.MESSAGE_FAILED], {
|
|
4265
|
+
queueMode: mode
|
|
4266
|
+
});
|
|
4267
|
+
await pipeAsyncIterableToSse(reply.raw, result.stream, {
|
|
4268
|
+
onError: (error) => [{
|
|
4269
|
+
type: "error",
|
|
4270
|
+
data: {
|
|
4271
|
+
id: (0, import_uuid.v4)(),
|
|
4272
|
+
content: error instanceof Error ? error.message : "Stream processing error"
|
|
4273
|
+
}
|
|
4274
|
+
}]
|
|
4275
|
+
});
|
|
4181
4276
|
} catch (error) {
|
|
4182
4277
|
const errorEvent = {
|
|
4183
4278
|
type: "error",
|
|
@@ -4190,7 +4285,7 @@ var createRun = async (request, reply) => {
|
|
|
4190
4285
|
|
|
4191
4286
|
`);
|
|
4192
4287
|
} finally {
|
|
4193
|
-
reply.raw.end();
|
|
4288
|
+
if (!reply.raw.writableEnded && !reply.raw.destroyed) reply.raw.end();
|
|
4194
4289
|
}
|
|
4195
4290
|
} else {
|
|
4196
4291
|
const { message: msg, ...restInputNonStream } = input;
|
|
@@ -4239,17 +4334,16 @@ var resumeStream = async (request, reply) => {
|
|
|
4239
4334
|
workspace_id,
|
|
4240
4335
|
project_id
|
|
4241
4336
|
});
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4337
|
+
await pipeAsyncIterableToSse(reply.raw, agent.chunkStream(message_id, []), {
|
|
4338
|
+
closeSource: request.raw,
|
|
4339
|
+
onError: (error) => [{
|
|
4340
|
+
type: "error",
|
|
4341
|
+
data: {
|
|
4342
|
+
id: (0, import_uuid.v4)(),
|
|
4343
|
+
content: error instanceof Error ? error.message : "Resume stream processing error"
|
|
4344
|
+
}
|
|
4345
|
+
}]
|
|
4246
4346
|
});
|
|
4247
|
-
for await (const chunk of stream) {
|
|
4248
|
-
if (closed || reply.raw.destroyed) break;
|
|
4249
|
-
reply.raw.write(`data: ${JSON.stringify(chunk)}
|
|
4250
|
-
|
|
4251
|
-
`);
|
|
4252
|
-
}
|
|
4253
4347
|
} catch (error) {
|
|
4254
4348
|
const errorEvent = {
|
|
4255
4349
|
type: "error",
|
|
@@ -4262,7 +4356,7 @@ var resumeStream = async (request, reply) => {
|
|
|
4262
4356
|
|
|
4263
4357
|
`);
|
|
4264
4358
|
} finally {
|
|
4265
|
-
reply.raw.end();
|
|
4359
|
+
if (!reply.raw.writableEnded && !reply.raw.destroyed) reply.raw.end();
|
|
4266
4360
|
}
|
|
4267
4361
|
} catch (error) {
|
|
4268
4362
|
reply.status(500).send({
|
|
@@ -6712,6 +6806,7 @@ async function deletePersonalAssistant(request, reply) {
|
|
|
6712
6806
|
if (!deleted) {
|
|
6713
6807
|
return reply.status(500).send({ success: false, message: "Failed to delete personal assistant" });
|
|
6714
6808
|
}
|
|
6809
|
+
import_core16.eventBus.publish("assistant:deleted", { id: assistant.id, tenantId });
|
|
6715
6810
|
const threadStore = (0, import_core16.getStoreLattice)("default", "thread").store;
|
|
6716
6811
|
try {
|
|
6717
6812
|
const threads = await threadStore.getThreadsByAssistantId(tenantId, assistant.id);
|
|
@@ -10161,11 +10256,11 @@ async function createProject(request, reply) {
|
|
|
10161
10256
|
}
|
|
10162
10257
|
const serverCfg = data.targetServerConfig || {};
|
|
10163
10258
|
const judgeCfg = data.judgeModelConfig || {};
|
|
10164
|
-
const
|
|
10165
|
-
if (
|
|
10166
|
-
const { modelLatticeManager:
|
|
10167
|
-
if (!
|
|
10168
|
-
return reply.status(400).send({ success: false, message: `Judge model "${
|
|
10259
|
+
const modelKey2 = judgeCfg.modelKey || "";
|
|
10260
|
+
if (modelKey2) {
|
|
10261
|
+
const { modelLatticeManager: modelLatticeManager4 } = await import("@axiom-lattice/core");
|
|
10262
|
+
if (!modelLatticeManager4.hasLattice(modelKey2)) {
|
|
10263
|
+
return reply.status(400).send({ success: false, message: `Judge model "${modelKey2}" is not registered` });
|
|
10169
10264
|
}
|
|
10170
10265
|
}
|
|
10171
10266
|
const project = await store2.createProject(tenantId, id, {
|
|
@@ -10173,7 +10268,7 @@ async function createProject(request, reply) {
|
|
|
10173
10268
|
description: data.description,
|
|
10174
10269
|
version: data.version,
|
|
10175
10270
|
judgeModelConfig: {
|
|
10176
|
-
modelKey,
|
|
10271
|
+
modelKey: modelKey2,
|
|
10177
10272
|
displayName: judgeCfg.displayName || ""
|
|
10178
10273
|
},
|
|
10179
10274
|
targetServerConfig: {
|
|
@@ -10210,8 +10305,8 @@ async function listProjects(request, reply) {
|
|
|
10210
10305
|
let projects = await store2.getProjectsByTenant(tenantId);
|
|
10211
10306
|
if (projects.length === 0) {
|
|
10212
10307
|
try {
|
|
10213
|
-
const { modelLatticeManager:
|
|
10214
|
-
const models =
|
|
10308
|
+
const { modelLatticeManager: modelLatticeManager4 } = await import("@axiom-lattice/core");
|
|
10309
|
+
const models = modelLatticeManager4.getAllLattices();
|
|
10215
10310
|
const first = models[0];
|
|
10216
10311
|
const judgeModel = first ? { modelKey: first.key } : {};
|
|
10217
10312
|
const host = request.hostname || "localhost";
|
|
@@ -10277,15 +10372,15 @@ async function updateProject(request, reply) {
|
|
|
10277
10372
|
}
|
|
10278
10373
|
if (body.judgeModelConfig !== void 0) {
|
|
10279
10374
|
const judgeCfg = body.judgeModelConfig || {};
|
|
10280
|
-
const
|
|
10281
|
-
if (
|
|
10282
|
-
const { modelLatticeManager:
|
|
10283
|
-
if (!
|
|
10284
|
-
return reply.status(400).send({ success: false, message: `Judge model "${
|
|
10375
|
+
const modelKey2 = judgeCfg.modelKey || "";
|
|
10376
|
+
if (modelKey2) {
|
|
10377
|
+
const { modelLatticeManager: modelLatticeManager4 } = await import("@axiom-lattice/core");
|
|
10378
|
+
if (!modelLatticeManager4.hasLattice(modelKey2)) {
|
|
10379
|
+
return reply.status(400).send({ success: false, message: `Judge model "${modelKey2}" is not registered` });
|
|
10285
10380
|
}
|
|
10286
10381
|
}
|
|
10287
10382
|
updateData.judgeModelConfig = {
|
|
10288
|
-
modelKey,
|
|
10383
|
+
modelKey: modelKey2,
|
|
10289
10384
|
displayName: judgeCfg.displayName || ""
|
|
10290
10385
|
};
|
|
10291
10386
|
}
|
|
@@ -11148,6 +11243,8 @@ function extractUserFromAuthHeader(authHeader, secret) {
|
|
|
11148
11243
|
var PUBLIC_ROUTES = ["/api/auth/login", "/api/auth/register", "/health", "/api/stt/models"];
|
|
11149
11244
|
function bypassesConsoleAuth(request) {
|
|
11150
11245
|
const path6 = request.url.split("?")[0];
|
|
11246
|
+
const method = request.method;
|
|
11247
|
+
if (method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime\/bootstrap$/.test(path6) || (method === "GET" || method === "POST") && /^\/api\/web-apps\/[^/]+\/runtime\/threads$/.test(path6) || (method === "PATCH" || method === "DELETE") && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+$/.test(path6) || method === "GET" && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+\/messages$/.test(path6) || method === "POST" && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+\/(messages\/stream|abort|attachments)$/.test(path6) || method === "POST" && /^\/api\/web-apps\/[^/]+\/runtime\/threads\/[^/]+\/interrupts\/[^/]+\/resume$/.test(path6)) return true;
|
|
11151
11248
|
if (request.method === "GET" && /^\/api\/a2a\/agents\/[^/]+\/\.well-known\/agent-card\.json$/.test(path6)) {
|
|
11152
11249
|
return true;
|
|
11153
11250
|
}
|
|
@@ -11155,13 +11252,13 @@ function bypassesConsoleAuth(request) {
|
|
|
11155
11252
|
}
|
|
11156
11253
|
function registerConsoleAuthHook(app2, options) {
|
|
11157
11254
|
app2.addHook("preHandler", async (request, reply) => {
|
|
11158
|
-
if (bypassesConsoleAuth(request)) return;
|
|
11159
11255
|
const secret = options.tokenSecret ?? resolveConsoleTokenSecret(options.authRequired);
|
|
11160
11256
|
const user = extractUserFromAuthHeader(request.headers.authorization, secret);
|
|
11161
11257
|
if (user) {
|
|
11162
11258
|
request.user = user;
|
|
11163
|
-
return;
|
|
11164
11259
|
}
|
|
11260
|
+
if (bypassesConsoleAuth(request)) return;
|
|
11261
|
+
if (user) return;
|
|
11165
11262
|
if (!options.authRequired || request.method === "OPTIONS") return;
|
|
11166
11263
|
if (PUBLIC_ROUTES.some((route) => request.url === route)) return;
|
|
11167
11264
|
if (request.url.startsWith("/s/")) return;
|
|
@@ -11561,8 +11658,8 @@ function snapshotPlainObject(value) {
|
|
|
11561
11658
|
}
|
|
11562
11659
|
}
|
|
11563
11660
|
async function getInstallationStore() {
|
|
11564
|
-
const { getStoreLattice:
|
|
11565
|
-
const store2 =
|
|
11661
|
+
const { getStoreLattice: getStoreLattice31 } = await import("@axiom-lattice/core");
|
|
11662
|
+
const store2 = getStoreLattice31("default", "channelInstallation").store;
|
|
11566
11663
|
if (store2) return store2;
|
|
11567
11664
|
const { PostgreSQLChannelInstallationStore } = await import("@axiom-lattice/pg-stores");
|
|
11568
11665
|
const databaseUrl = process.env.DATABASE_URL;
|
|
@@ -12258,7 +12355,7 @@ var registerSTTRoutes = (app2) => {
|
|
|
12258
12355
|
}
|
|
12259
12356
|
},
|
|
12260
12357
|
async (request, reply) => {
|
|
12261
|
-
const
|
|
12358
|
+
const modelKey2 = request.query.model || "default";
|
|
12262
12359
|
const data = await request.file({
|
|
12263
12360
|
limits: { fileSize: MAX_AUDIO_FILE_SIZE }
|
|
12264
12361
|
});
|
|
@@ -12276,9 +12373,9 @@ var registerSTTRoutes = (app2) => {
|
|
|
12276
12373
|
try {
|
|
12277
12374
|
let client;
|
|
12278
12375
|
try {
|
|
12279
|
-
client = (0, import_core38.getSTTClient)(
|
|
12376
|
+
client = (0, import_core38.getSTTClient)(modelKey2);
|
|
12280
12377
|
} catch {
|
|
12281
|
-
return reply.status(404).send({ error: `STT model "${
|
|
12378
|
+
return reply.status(404).send({ error: `STT model "${modelKey2}" not found` });
|
|
12282
12379
|
}
|
|
12283
12380
|
const result = await client.transcribe(buffer, format);
|
|
12284
12381
|
return reply.send({
|
|
@@ -12689,8 +12786,10 @@ async function createTask(request, reply) {
|
|
|
12689
12786
|
const ownerType = body.ownerType || "user";
|
|
12690
12787
|
const ownerId = body.ownerId || userId;
|
|
12691
12788
|
if (ownerType === "agent") {
|
|
12789
|
+
(0, import_core40.ensureBuiltinAgentsForTenant)(tenantId);
|
|
12692
12790
|
const assistant = await getAssistantStore2().getAssistantById(tenantId, ownerId);
|
|
12693
|
-
|
|
12791
|
+
const configuredAgent = import_core40.agentLatticeManager.getAgentConfigWithTenant(tenantId, ownerId);
|
|
12792
|
+
if ((!assistant || assistant.tenantId !== tenantId) && !configuredAgent) {
|
|
12694
12793
|
return reply.status(404).send({
|
|
12695
12794
|
success: false,
|
|
12696
12795
|
code: "AGENT_OWNER_NOT_FOUND",
|
|
@@ -12839,7 +12938,1032 @@ function registerTaskRoutes(app2) {
|
|
|
12839
12938
|
}
|
|
12840
12939
|
|
|
12841
12940
|
// src/routes/index.ts
|
|
12941
|
+
var import_core41 = require("@axiom-lattice/core");
|
|
12942
|
+
|
|
12943
|
+
// src/services/agent-web-app/AgentWebAppService.ts
|
|
12944
|
+
function createConfiguredAgentResolver(registry) {
|
|
12945
|
+
return {
|
|
12946
|
+
async exists(tenantId, assistantId) {
|
|
12947
|
+
await registry.hydrate(tenantId);
|
|
12948
|
+
return registry.has(tenantId, assistantId);
|
|
12949
|
+
}
|
|
12950
|
+
};
|
|
12951
|
+
}
|
|
12952
|
+
var AgentWebAppError = class extends Error {
|
|
12953
|
+
constructor(code, statusCode, message) {
|
|
12954
|
+
super(message);
|
|
12955
|
+
this.code = code;
|
|
12956
|
+
this.statusCode = statusCode;
|
|
12957
|
+
this.name = "AgentWebAppError";
|
|
12958
|
+
}
|
|
12959
|
+
};
|
|
12960
|
+
var AgentWebAppService = class {
|
|
12961
|
+
constructor(deps) {
|
|
12962
|
+
this.deps = deps;
|
|
12963
|
+
}
|
|
12964
|
+
async create(context, input) {
|
|
12965
|
+
await this.validateAssistant(context, input.assistantId);
|
|
12966
|
+
const normalized = await this.validateConfig(context.tenantId, input);
|
|
12967
|
+
return this.deps.webAppStore.create(context.tenantId, normalized);
|
|
12968
|
+
}
|
|
12969
|
+
async list(context, assistantId) {
|
|
12970
|
+
const records = await this.deps.webAppStore.list(context.tenantId, assistantId);
|
|
12971
|
+
const visible = [];
|
|
12972
|
+
for (const record of records) {
|
|
12973
|
+
if (await this.canAccessAssistant(context, record.assistantId)) visible.push(record);
|
|
12974
|
+
}
|
|
12975
|
+
return visible;
|
|
12976
|
+
}
|
|
12977
|
+
async get(context, webAppId) {
|
|
12978
|
+
return this.requireAuthorizedWebApp(context, webAppId);
|
|
12979
|
+
}
|
|
12980
|
+
async update(context, webAppId, patch) {
|
|
12981
|
+
const current = await this.requireAuthorizedWebApp(context, webAppId);
|
|
12982
|
+
const effective = {
|
|
12983
|
+
assistantId: current.assistantId,
|
|
12984
|
+
name: patch.name ?? current.name,
|
|
12985
|
+
description: Object.prototype.hasOwnProperty.call(patch, "description") ? patch.description : current.description,
|
|
12986
|
+
integration: current.integration,
|
|
12987
|
+
scope: { ...current.scope, ...patch.scope },
|
|
12988
|
+
features: { ...current.features, ...patch.features },
|
|
12989
|
+
appearance: { ...current.appearance, ...patch.appearance }
|
|
12990
|
+
};
|
|
12991
|
+
await this.validateAssistant(context, current.assistantId);
|
|
12992
|
+
const validated = await this.validateConfig(context.tenantId, effective);
|
|
12993
|
+
const update = {
|
|
12994
|
+
...patch.name !== void 0 ? { name: validated.name } : {},
|
|
12995
|
+
...Object.prototype.hasOwnProperty.call(patch, "description") ? { description: validated.description } : {},
|
|
12996
|
+
...patch.scope ? { scope: validated.scope } : {},
|
|
12997
|
+
...patch.features ? { features: validated.features } : {},
|
|
12998
|
+
...patch.appearance ? { appearance: validated.appearance } : {}
|
|
12999
|
+
};
|
|
13000
|
+
return this.writeSnapshot(context, current, update);
|
|
13001
|
+
}
|
|
13002
|
+
async enable(context, webAppId) {
|
|
13003
|
+
const current = await this.requireAuthorizedWebApp(context, webAppId);
|
|
13004
|
+
await this.validateConfig(context.tenantId, current);
|
|
13005
|
+
return this.writeSnapshot(context, current, { status: "active" });
|
|
13006
|
+
}
|
|
13007
|
+
async disable(context, webAppId) {
|
|
13008
|
+
const current = await this.requireAuthorizedWebApp(context, webAppId);
|
|
13009
|
+
return this.writeSnapshot(context, current, { status: "disabled" });
|
|
13010
|
+
}
|
|
13011
|
+
async delete(context, webAppId) {
|
|
13012
|
+
await this.requireAuthorizedWebApp(context, webAppId);
|
|
13013
|
+
if (!await this.deps.webAppStore.delete(context.tenantId, webAppId)) this.notFound();
|
|
13014
|
+
}
|
|
13015
|
+
async validateAssistant(context, assistantId) {
|
|
13016
|
+
const assistant = await this.deps.assistantStore.getAssistantById(context.tenantId, assistantId);
|
|
13017
|
+
if (assistant) {
|
|
13018
|
+
if (assistant.ownerUserId && assistant.ownerUserId !== context.userId) {
|
|
13019
|
+
throw new AgentWebAppError("ASSISTANT_FORBIDDEN", 403, "Assistant is not available to this user");
|
|
13020
|
+
}
|
|
13021
|
+
return;
|
|
13022
|
+
}
|
|
13023
|
+
if (await this.deps.configuredAgentResolver.exists(context.tenantId, assistantId)) return;
|
|
13024
|
+
throw new AgentWebAppError("ASSISTANT_NOT_FOUND", 404, "Assistant not found");
|
|
13025
|
+
}
|
|
13026
|
+
async validateConfig(tenantId, input) {
|
|
13027
|
+
const allowedProjectIds = [...new Set(input.scope.allowedProjectIds)];
|
|
13028
|
+
if (!allowedProjectIds.length || !allowedProjectIds.includes(input.scope.defaultProjectId)) {
|
|
13029
|
+
throw new AgentWebAppError("PROJECT_SCOPE_INVALID", 400, "Default project must be in the project allowlist");
|
|
13030
|
+
}
|
|
13031
|
+
if (!input.features.projectSelector && allowedProjectIds.length !== 1) {
|
|
13032
|
+
throw new AgentWebAppError("PROJECT_SCOPE_INVALID", 400, "Disabled project selection requires exactly one project");
|
|
13033
|
+
}
|
|
13034
|
+
for (const projectId of allowedProjectIds) {
|
|
13035
|
+
if (!await this.deps.projectStore.getProjectById(tenantId, projectId)) {
|
|
13036
|
+
throw new AgentWebAppError("PROJECT_NOT_FOUND", 404, "Project not found");
|
|
13037
|
+
}
|
|
13038
|
+
}
|
|
13039
|
+
const scope = this.validateModels(input.scope, input.features.modelSelector);
|
|
13040
|
+
return { ...input, scope: { ...scope, allowedProjectIds } };
|
|
13041
|
+
}
|
|
13042
|
+
validateModels(scope, modelSelector) {
|
|
13043
|
+
const allowedModelKeys = scope.allowedModelKeys ? [...new Set(scope.allowedModelKeys)] : void 0;
|
|
13044
|
+
if (!allowedModelKeys?.length) {
|
|
13045
|
+
if (scope.defaultModelKey || modelSelector) {
|
|
13046
|
+
throw new AgentWebAppError("MODEL_SCOPE_INVALID", 400, "Agent-default models cannot enable model selection or set a default");
|
|
13047
|
+
}
|
|
13048
|
+
return { ...scope, defaultModelKey: void 0, allowedModelKeys: void 0 };
|
|
13049
|
+
}
|
|
13050
|
+
if (!modelSelector && allowedModelKeys.length > 1) {
|
|
13051
|
+
throw new AgentWebAppError("MODEL_SCOPE_INVALID", 400, "Disabled model selection allows at most one model");
|
|
13052
|
+
}
|
|
13053
|
+
if (scope.defaultModelKey && !allowedModelKeys.includes(scope.defaultModelKey)) {
|
|
13054
|
+
throw new AgentWebAppError("MODEL_SCOPE_INVALID", 400, "Default model must be in the model allowlist");
|
|
13055
|
+
}
|
|
13056
|
+
for (const key of allowedModelKeys) {
|
|
13057
|
+
if (!this.deps.modelRegistry.has(key)) {
|
|
13058
|
+
throw new AgentWebAppError("MODEL_NOT_FOUND", 400, "Configured model is not registered");
|
|
13059
|
+
}
|
|
13060
|
+
}
|
|
13061
|
+
return { ...scope, allowedModelKeys };
|
|
13062
|
+
}
|
|
13063
|
+
async requireWebApp(tenantId, webAppId) {
|
|
13064
|
+
return await this.deps.webAppStore.getById(tenantId, webAppId) ?? this.notFound();
|
|
13065
|
+
}
|
|
13066
|
+
async requireAuthorizedWebApp(context, webAppId) {
|
|
13067
|
+
const webApp = await this.requireWebApp(context.tenantId, webAppId);
|
|
13068
|
+
if (!await this.canAccessAssistant(context, webApp.assistantId)) this.notFound();
|
|
13069
|
+
return webApp;
|
|
13070
|
+
}
|
|
13071
|
+
async canAccessAssistant(context, assistantId) {
|
|
13072
|
+
const assistant = await this.deps.assistantStore.getAssistantById(context.tenantId, assistantId);
|
|
13073
|
+
if (assistant) return !assistant.ownerUserId || assistant.ownerUserId === context.userId;
|
|
13074
|
+
return this.deps.configuredAgentResolver.exists(context.tenantId, assistantId);
|
|
13075
|
+
}
|
|
13076
|
+
async writeSnapshot(context, current, patch) {
|
|
13077
|
+
const updated = await this.deps.webAppStore.update(
|
|
13078
|
+
context.tenantId,
|
|
13079
|
+
current.id,
|
|
13080
|
+
patch,
|
|
13081
|
+
{ expectedUpdatedAt: current.updatedAt }
|
|
13082
|
+
);
|
|
13083
|
+
if (updated) return updated;
|
|
13084
|
+
await this.requireAuthorizedWebApp(context, current.id);
|
|
13085
|
+
throw new AgentWebAppError("WEB_APP_CONFLICT", 409, "Agent Web App was modified concurrently");
|
|
13086
|
+
}
|
|
13087
|
+
notFound() {
|
|
13088
|
+
throw new AgentWebAppError("WEB_APP_NOT_FOUND", 404, "Agent Web App not found");
|
|
13089
|
+
}
|
|
13090
|
+
};
|
|
13091
|
+
|
|
13092
|
+
// src/routes/agent-web-apps.ts
|
|
13093
|
+
var import_zod4 = require("zod");
|
|
13094
|
+
|
|
13095
|
+
// src/schemas/agent-web-app.ts
|
|
13096
|
+
var import_zod3 = require("zod");
|
|
13097
|
+
var externalId = import_zod3.z.string().min(1).max(128).regex(/^[A-Za-z0-9._:@-]+$/);
|
|
13098
|
+
var modelKey = import_zod3.z.string().min(1).max(128);
|
|
13099
|
+
var agentWebAppScopeSchema = import_zod3.z.object({
|
|
13100
|
+
defaultProjectId: externalId,
|
|
13101
|
+
allowedProjectIds: import_zod3.z.array(externalId).min(1).max(100),
|
|
13102
|
+
defaultModelKey: modelKey.optional(),
|
|
13103
|
+
allowedModelKeys: import_zod3.z.array(modelKey).max(100).optional()
|
|
13104
|
+
}).strict();
|
|
13105
|
+
var agentWebAppFeaturesSchema = import_zod3.z.object({
|
|
13106
|
+
projectSelector: import_zod3.z.boolean(),
|
|
13107
|
+
modelSelector: import_zod3.z.boolean(),
|
|
13108
|
+
threadManagement: import_zod3.z.boolean(),
|
|
13109
|
+
attachments: import_zod3.z.boolean(),
|
|
13110
|
+
hitl: import_zod3.z.boolean(),
|
|
13111
|
+
genUI: import_zod3.z.boolean()
|
|
13112
|
+
}).strict();
|
|
13113
|
+
var agentWebAppAppearanceSchema = import_zod3.z.object({
|
|
13114
|
+
title: import_zod3.z.string().max(120).optional(),
|
|
13115
|
+
welcomeMessage: import_zod3.z.string().max(1e3).optional(),
|
|
13116
|
+
primaryColor: import_zod3.z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional()
|
|
13117
|
+
}).strict();
|
|
13118
|
+
var createAgentWebAppSchema = import_zod3.z.object({
|
|
13119
|
+
assistantId: externalId,
|
|
13120
|
+
name: import_zod3.z.string().min(1).max(120),
|
|
13121
|
+
description: import_zod3.z.string().max(1e3).optional(),
|
|
13122
|
+
integration: import_zod3.z.object({ type: import_zod3.z.literal("react_sdk") }).strict(),
|
|
13123
|
+
scope: agentWebAppScopeSchema,
|
|
13124
|
+
features: agentWebAppFeaturesSchema,
|
|
13125
|
+
appearance: agentWebAppAppearanceSchema
|
|
13126
|
+
}).strict();
|
|
13127
|
+
var updateAgentWebAppSchema = import_zod3.z.object({
|
|
13128
|
+
name: import_zod3.z.string().min(1).max(120).optional(),
|
|
13129
|
+
description: import_zod3.z.string().max(1e3).optional(),
|
|
13130
|
+
scope: agentWebAppScopeSchema.partial().strict().optional(),
|
|
13131
|
+
features: agentWebAppFeaturesSchema.partial().strict().optional(),
|
|
13132
|
+
appearance: agentWebAppAppearanceSchema.partial().strict().optional()
|
|
13133
|
+
}).strict().refine((value) => Object.keys(value).length > 0, "Patch must not be empty");
|
|
13134
|
+
var listAgentWebAppsQuerySchema = import_zod3.z.object({
|
|
13135
|
+
assistantId: externalId.optional()
|
|
13136
|
+
}).strict();
|
|
13137
|
+
|
|
13138
|
+
// src/routes/agent-web-apps.ts
|
|
13139
|
+
function registerAgentWebAppRoutes(app2, deps) {
|
|
13140
|
+
app2.register(async (routes) => {
|
|
13141
|
+
routes.setErrorHandler((error, _request, reply) => normalizeError(reply, error));
|
|
13142
|
+
registerScopedRoutes(routes, deps.service);
|
|
13143
|
+
});
|
|
13144
|
+
}
|
|
13145
|
+
function registerScopedRoutes(app2, service2) {
|
|
13146
|
+
app2.post("/api/web-apps", async (request, reply) => handle(reply, async () => ({
|
|
13147
|
+
statusCode: 201,
|
|
13148
|
+
data: await service2.create(requireContext(request), createAgentWebAppSchema.parse(request.body))
|
|
13149
|
+
})));
|
|
13150
|
+
app2.get("/api/web-apps", async (request, reply) => handle(reply, async () => {
|
|
13151
|
+
const context = requireContext(request);
|
|
13152
|
+
const query = listAgentWebAppsQuerySchema.parse(request.query);
|
|
13153
|
+
const records = await service2.list(context, query.assistantId);
|
|
13154
|
+
return { data: { records, total: records.length } };
|
|
13155
|
+
}));
|
|
13156
|
+
app2.get("/api/web-apps/:webAppId", async (request, reply) => handle(reply, async () => ({ data: await service2.get(requireContext(request), request.params.webAppId) })));
|
|
13157
|
+
app2.patch("/api/web-apps/:webAppId", async (request, reply) => handle(reply, async () => ({
|
|
13158
|
+
data: await service2.update(
|
|
13159
|
+
requireContext(request),
|
|
13160
|
+
request.params.webAppId,
|
|
13161
|
+
updateAgentWebAppSchema.parse(request.body)
|
|
13162
|
+
)
|
|
13163
|
+
})));
|
|
13164
|
+
app2.post("/api/web-apps/:webAppId/enable", async (request, reply) => handle(reply, async () => ({ data: await service2.enable(requireContext(request), request.params.webAppId) })));
|
|
13165
|
+
app2.post("/api/web-apps/:webAppId/disable", async (request, reply) => handle(reply, async () => ({ data: await service2.disable(requireContext(request), request.params.webAppId) })));
|
|
13166
|
+
app2.delete("/api/web-apps/:webAppId", async (request, reply) => handle(reply, async () => {
|
|
13167
|
+
await service2.delete(requireContext(request), request.params.webAppId);
|
|
13168
|
+
return { data: { id: request.params.webAppId } };
|
|
13169
|
+
}));
|
|
13170
|
+
}
|
|
13171
|
+
function requireContext(request) {
|
|
13172
|
+
const user = request.user;
|
|
13173
|
+
const userId = user?.id ?? user?.userId;
|
|
13174
|
+
if (!userId || !user?.tenantId) {
|
|
13175
|
+
throw new AgentWebAppError("UNAUTHORIZED", 401, "Console session required");
|
|
13176
|
+
}
|
|
13177
|
+
return { tenantId: user.tenantId, userId };
|
|
13178
|
+
}
|
|
13179
|
+
async function handle(reply, operation) {
|
|
13180
|
+
try {
|
|
13181
|
+
const result = await operation();
|
|
13182
|
+
return reply.code(result.statusCode ?? 200).send({ success: true, data: result.data });
|
|
13183
|
+
} catch (error) {
|
|
13184
|
+
if (error instanceof import_zod4.ZodError) {
|
|
13185
|
+
return invalidRequest(reply, 400);
|
|
13186
|
+
}
|
|
13187
|
+
if (error instanceof AgentWebAppError) {
|
|
13188
|
+
return reply.code(error.statusCode).send({
|
|
13189
|
+
success: false,
|
|
13190
|
+
error: { code: error.code, message: error.message, retryable: false }
|
|
13191
|
+
});
|
|
13192
|
+
}
|
|
13193
|
+
throw error;
|
|
13194
|
+
}
|
|
13195
|
+
}
|
|
13196
|
+
function normalizeError(reply, error) {
|
|
13197
|
+
if (error instanceof AgentWebAppError) {
|
|
13198
|
+
return reply.code(error.statusCode).send({
|
|
13199
|
+
success: false,
|
|
13200
|
+
error: { code: error.code, message: error.message, retryable: false }
|
|
13201
|
+
});
|
|
13202
|
+
}
|
|
13203
|
+
if (error.statusCode && error.statusCode >= 400 && error.statusCode < 500) {
|
|
13204
|
+
return invalidRequest(reply, error.statusCode);
|
|
13205
|
+
}
|
|
13206
|
+
return reply.code(500).send({
|
|
13207
|
+
success: false,
|
|
13208
|
+
error: { code: "INTERNAL_ERROR", message: "Internal server error", retryable: false }
|
|
13209
|
+
});
|
|
13210
|
+
}
|
|
13211
|
+
function invalidRequest(reply, statusCode) {
|
|
13212
|
+
return reply.code(statusCode).send({
|
|
13213
|
+
success: false,
|
|
13214
|
+
error: { code: "INVALID_REQUEST", message: "Invalid request", retryable: false }
|
|
13215
|
+
});
|
|
13216
|
+
}
|
|
13217
|
+
|
|
13218
|
+
// src/routes/agent-web-app-runtime.ts
|
|
13219
|
+
var import_zod5 = require("zod");
|
|
13220
|
+
|
|
13221
|
+
// src/services/agent-web-app/AgentWebAppRuntimeService.ts
|
|
13222
|
+
var import_node_crypto3 = require("crypto");
|
|
13223
|
+
var import_protocols6 = require("@axiom-lattice/protocols");
|
|
13224
|
+
init_task_file_refs();
|
|
13225
|
+
function isRecord(value) {
|
|
13226
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13227
|
+
}
|
|
13228
|
+
var AgentWebAppRuntimeError = class extends Error {
|
|
13229
|
+
constructor(code, statusCode, message) {
|
|
13230
|
+
super(message);
|
|
13231
|
+
this.code = code;
|
|
13232
|
+
this.statusCode = statusCode;
|
|
13233
|
+
this.name = "AgentWebAppRuntimeError";
|
|
13234
|
+
}
|
|
13235
|
+
};
|
|
13236
|
+
var AgentWebAppRuntimeService = class {
|
|
13237
|
+
constructor(deps) {
|
|
13238
|
+
this.deps = deps;
|
|
13239
|
+
}
|
|
13240
|
+
async bootstrap(webAppId, userId, selection) {
|
|
13241
|
+
const runtime = await this.resolveRuntime(webAppId, selection);
|
|
13242
|
+
const assistant = await this.deps.assistantResolver.resolve(
|
|
13243
|
+
runtime.webApp.tenantId,
|
|
13244
|
+
runtime.webApp.assistantId
|
|
13245
|
+
);
|
|
13246
|
+
if (!assistant) this.fail("WEB_APP_DISABLED", 403, "Agent Web App is unavailable");
|
|
13247
|
+
const publicProjectIds = runtime.webApp.features.projectSelector ? runtime.webApp.scope.allowedProjectIds : [runtime.webApp.scope.defaultProjectId];
|
|
13248
|
+
const projects = await Promise.all(publicProjectIds.map(async (id) => {
|
|
13249
|
+
const project = await this.deps.projectStore.getProjectById(runtime.webApp.tenantId, id);
|
|
13250
|
+
if (!project) this.fail("PROJECT_NOT_ALLOWED", 400, "Project is not available");
|
|
13251
|
+
return { id: project.id, name: project.name };
|
|
13252
|
+
}));
|
|
13253
|
+
const defaultModelKey = runtime.webApp.scope.defaultModelKey;
|
|
13254
|
+
if (defaultModelKey && !this.deps.modelRegistry.has(defaultModelKey)) {
|
|
13255
|
+
this.fail("MODEL_NOT_ALLOWED", 400, "Default model is not available");
|
|
13256
|
+
}
|
|
13257
|
+
const configuredPublicModelKeys = runtime.webApp.features.modelSelector ? runtime.webApp.scope.allowedModelKeys ?? [] : defaultModelKey ? [defaultModelKey] : [];
|
|
13258
|
+
const publicModelKeys = [...new Set(configuredPublicModelKeys)].filter((key) => this.deps.modelRegistry.has(key));
|
|
13259
|
+
const models = publicModelKeys.map((key) => ({
|
|
13260
|
+
key,
|
|
13261
|
+
label: this.deps.modelRegistry.label(key)
|
|
13262
|
+
}));
|
|
13263
|
+
const result = {
|
|
13264
|
+
webApp: {
|
|
13265
|
+
id: runtime.webApp.id,
|
|
13266
|
+
name: runtime.webApp.name,
|
|
13267
|
+
description: runtime.webApp.description,
|
|
13268
|
+
assistant,
|
|
13269
|
+
defaultProjectId: runtime.webApp.scope.defaultProjectId,
|
|
13270
|
+
defaultModelKey: runtime.webApp.scope.defaultModelKey,
|
|
13271
|
+
features: runtime.webApp.features,
|
|
13272
|
+
appearance: runtime.webApp.appearance,
|
|
13273
|
+
identityAssurance: "unverified"
|
|
13274
|
+
},
|
|
13275
|
+
projects,
|
|
13276
|
+
models
|
|
13277
|
+
};
|
|
13278
|
+
if (!runtime.webApp.features.threadManagement) {
|
|
13279
|
+
result.thread = await this.resolveImplicitThread(runtime, userId);
|
|
13280
|
+
}
|
|
13281
|
+
return result;
|
|
13282
|
+
}
|
|
13283
|
+
async listThreads(webAppId, userId, selection) {
|
|
13284
|
+
const runtime = await this.resolveRuntime(webAppId, selection);
|
|
13285
|
+
this.requireFeature(runtime.webApp.features.threadManagement);
|
|
13286
|
+
const threads = await this.ownedThreads(runtime, userId);
|
|
13287
|
+
return threads.sort(compareThreads).map(toRuntimeThread);
|
|
13288
|
+
}
|
|
13289
|
+
async createThread(webAppId, userId, input) {
|
|
13290
|
+
const runtime = await this.resolveRuntime(webAppId, input);
|
|
13291
|
+
this.requireFeature(runtime.webApp.features.threadManagement);
|
|
13292
|
+
return this.createOwnedThread(runtime, userId, input.label);
|
|
13293
|
+
}
|
|
13294
|
+
/** Update a thread label; omitted or blank labels preserve the existing label. */
|
|
13295
|
+
async updateThread(webAppId, userId, threadId, input) {
|
|
13296
|
+
const runtime = await this.resolveRuntime(webAppId, input);
|
|
13297
|
+
this.requireFeature(runtime.webApp.features.threadManagement);
|
|
13298
|
+
const thread = await this.requireOwnedThread(runtime, userId, threadId);
|
|
13299
|
+
const label = normalizeLabel(input.label);
|
|
13300
|
+
const updated = await this.deps.threadStore.updateThread(runtime.webApp.tenantId, thread.id, {
|
|
13301
|
+
metadata: this.metadata(runtime, userId, label)
|
|
13302
|
+
});
|
|
13303
|
+
if (!updated) this.threadNotFound();
|
|
13304
|
+
return toRuntimeThread(updated);
|
|
13305
|
+
}
|
|
13306
|
+
async deleteThread(webAppId, userId, threadId, selection) {
|
|
13307
|
+
const runtime = await this.resolveRuntime(webAppId, selection);
|
|
13308
|
+
this.requireFeature(runtime.webApp.features.threadManagement);
|
|
13309
|
+
await this.requireOwnedThread(runtime, userId, threadId);
|
|
13310
|
+
if (!await this.deps.threadStore.deleteThread(runtime.webApp.tenantId, threadId)) this.threadNotFound();
|
|
13311
|
+
}
|
|
13312
|
+
async getMessages(webAppId, userId, threadId, selection) {
|
|
13313
|
+
const runtime = await this.resolveRuntime(webAppId, selection);
|
|
13314
|
+
await this.requireOwnedThread(runtime, userId, threadId);
|
|
13315
|
+
const messages = await this.deps.messageReader.getCurrentMessages({
|
|
13316
|
+
tenantId: runtime.webApp.tenantId,
|
|
13317
|
+
assistantId: runtime.webApp.assistantId,
|
|
13318
|
+
threadId,
|
|
13319
|
+
workspaceId: runtime.project.workspaceId,
|
|
13320
|
+
projectId: runtime.project.id
|
|
13321
|
+
});
|
|
13322
|
+
return messages.flatMap((message) => {
|
|
13323
|
+
if (!isRecord(message) || typeof message.id !== "string" || message.role !== "human" && message.role !== "ai") return [];
|
|
13324
|
+
const content = typeof message.content === "string" ? message.content : runtime.webApp.features.genUI && Array.isArray(message.content) ? message.content.map(import_protocols6.parseAgentWebAppGenUIBlock).filter((block) => block !== void 0) : void 0;
|
|
13325
|
+
return [{ id: message.id, role: message.role, ...content && (typeof content === "string" || content.length > 0) ? { content } : {} }];
|
|
13326
|
+
});
|
|
13327
|
+
}
|
|
13328
|
+
async streamMessage(webAppId, userId, threadId, input) {
|
|
13329
|
+
const runtime = await this.resolveOwnedRuntime(webAppId, userId, threadId, input);
|
|
13330
|
+
const refs = this.resolveAttachmentRefs(runtime, userId, threadId, input.attachmentRefs ?? []);
|
|
13331
|
+
const agent = this.resolveAgent(runtime, threadId);
|
|
13332
|
+
const content = refs.length ? `${input.content}
|
|
13333
|
+
|
|
13334
|
+
${formatFileRefsSection(refs)}` : input.content;
|
|
13335
|
+
const execution = await this.execute(agent, runtime, { input: { message: content } });
|
|
13336
|
+
return { ...execution, allowInterrupts: runtime.webApp.features.hitl, allowGenUI: runtime.webApp.features.genUI };
|
|
13337
|
+
}
|
|
13338
|
+
async abort(webAppId, userId, threadId, selection) {
|
|
13339
|
+
const { agent } = await this.resolveOwnedAgent(webAppId, userId, threadId, selection);
|
|
13340
|
+
await agent.abort();
|
|
13341
|
+
}
|
|
13342
|
+
async uploadAttachment(webAppId, userId, threadId, selection, file) {
|
|
13343
|
+
const runtime = await this.resolveOwnedRuntime(webAppId, userId, threadId, selection);
|
|
13344
|
+
this.requireNamedFeature(runtime.webApp.features.attachments, "Attachments are disabled");
|
|
13345
|
+
if (!this.deps.saveProjectFile || !this.deps.attachmentRefs) this.fail("INTERNAL_ERROR", 500, "Attachment service is unavailable");
|
|
13346
|
+
const userDigest = (0, import_node_crypto3.createHash)("sha256").update(userId, "utf8").digest("hex");
|
|
13347
|
+
const contentDigest = (0, import_node_crypto3.createHash)("sha256").update(file.bytes).digest("hex");
|
|
13348
|
+
const displayName = file.name;
|
|
13349
|
+
const physicalName = `${(0, import_node_crypto3.randomUUID)()}-${sanitizePhysicalFilename(file.name)}`;
|
|
13350
|
+
const saved = await this.deps.saveProjectFile({
|
|
13351
|
+
tenantId: runtime.webApp.tenantId,
|
|
13352
|
+
workspaceId: runtime.project.workspaceId,
|
|
13353
|
+
projectId: runtime.project.id,
|
|
13354
|
+
assistantId: runtime.webApp.assistantId,
|
|
13355
|
+
name: physicalName,
|
|
13356
|
+
mimeType: file.mimeType,
|
|
13357
|
+
bytes: file.bytes,
|
|
13358
|
+
path: `/project/web-apps/${runtime.webApp.id}/${userDigest}/${threadId}`
|
|
13359
|
+
});
|
|
13360
|
+
const ref = this.deps.attachmentRefs.sign({
|
|
13361
|
+
webAppId: runtime.webApp.id,
|
|
13362
|
+
userId,
|
|
13363
|
+
threadId,
|
|
13364
|
+
projectId: runtime.project.id,
|
|
13365
|
+
uri: saved.uri,
|
|
13366
|
+
name: displayName,
|
|
13367
|
+
mimeType: file.mimeType,
|
|
13368
|
+
size: saved.size,
|
|
13369
|
+
contentDigest
|
|
13370
|
+
});
|
|
13371
|
+
return { ref, name: displayName, mimeType: file.mimeType, size: saved.size };
|
|
13372
|
+
}
|
|
13373
|
+
async resumeInterrupt(webAppId, userId, threadId, interruptId, selection, input) {
|
|
13374
|
+
const { runtime, agent } = await this.resolveOwnedAgent(webAppId, userId, threadId, selection);
|
|
13375
|
+
this.requireNamedFeature(runtime.webApp.features.hitl, "Human review is disabled");
|
|
13376
|
+
const state = await agent.getCurrentState();
|
|
13377
|
+
if (!hasInterrupt(state, interruptId)) this.fail("INVALID_REQUEST", 400, "Interrupt not found");
|
|
13378
|
+
const execution = await this.execute(agent, runtime, {
|
|
13379
|
+
input: { message: input.message },
|
|
13380
|
+
command: { resume: input.response }
|
|
13381
|
+
});
|
|
13382
|
+
return { ...execution, allowInterrupts: true, allowGenUI: runtime.webApp.features.genUI };
|
|
13383
|
+
}
|
|
13384
|
+
execute(agent, runtime, request) {
|
|
13385
|
+
return executeScopedAgentMessage(agent, {
|
|
13386
|
+
...request,
|
|
13387
|
+
...runtime.modelKey ? { custom_run_config: { modelConfig: { modelKey: runtime.modelKey } } } : {}
|
|
13388
|
+
}, [import_protocols6.MessageChunkTypes.MESSAGE_COMPLETED, import_protocols6.MessageChunkTypes.INTERRUPT, import_protocols6.MessageChunkTypes.MESSAGE_FAILED]);
|
|
13389
|
+
}
|
|
13390
|
+
async resolveRuntime(webAppId, selection) {
|
|
13391
|
+
const webApp = await this.deps.webAppStore.findById(webAppId);
|
|
13392
|
+
if (!webApp) this.fail("WEB_APP_NOT_FOUND", 404, "Agent Web App not found");
|
|
13393
|
+
const previewAllowed = webApp.status === "draft" && selection.preview?.tenantId === webApp.tenantId && await this.deps.authorizePreview?.(webApp, selection.preview) === true;
|
|
13394
|
+
if (webApp.status !== "active" && !previewAllowed) this.fail("WEB_APP_DISABLED", 403, "Agent Web App is disabled");
|
|
13395
|
+
if (!webApp.features.projectSelector && selection.projectId !== void 0) {
|
|
13396
|
+
this.fail("PROJECT_SELECTOR_DISABLED", 400, "Project selection is disabled");
|
|
13397
|
+
}
|
|
13398
|
+
const projectId = selection.projectId ?? webApp.scope.defaultProjectId;
|
|
13399
|
+
if (!webApp.scope.allowedProjectIds.includes(projectId)) {
|
|
13400
|
+
this.fail("PROJECT_NOT_ALLOWED", 400, "Project is not allowed");
|
|
13401
|
+
}
|
|
13402
|
+
const project = await this.deps.projectStore.getProjectById(webApp.tenantId, projectId);
|
|
13403
|
+
if (!project) this.fail("PROJECT_NOT_ALLOWED", 400, "Project is not available");
|
|
13404
|
+
if (!webApp.features.modelSelector && selection.modelKey !== void 0) {
|
|
13405
|
+
this.fail("MODEL_NOT_ALLOWED", 400, "Model selection is disabled");
|
|
13406
|
+
}
|
|
13407
|
+
const modelKey2 = selection.modelKey ?? webApp.scope.defaultModelKey;
|
|
13408
|
+
const allowedModels = webApp.scope.allowedModelKeys;
|
|
13409
|
+
if (modelKey2 && (!allowedModels?.includes(modelKey2) || !this.deps.modelRegistry.has(modelKey2))) {
|
|
13410
|
+
this.fail("MODEL_NOT_ALLOWED", 400, "Model is not allowed");
|
|
13411
|
+
}
|
|
13412
|
+
return { webApp, project, modelKey: modelKey2 };
|
|
13413
|
+
}
|
|
13414
|
+
async resolveOwnedAgent(webAppId, userId, threadId, selection) {
|
|
13415
|
+
const runtime = await this.resolveOwnedRuntime(webAppId, userId, threadId, selection);
|
|
13416
|
+
return { runtime, agent: this.resolveAgent(runtime, threadId) };
|
|
13417
|
+
}
|
|
13418
|
+
async resolveOwnedRuntime(webAppId, userId, threadId, selection) {
|
|
13419
|
+
const runtime = await this.resolveRuntime(webAppId, selection);
|
|
13420
|
+
await this.requireOwnedThread(runtime, userId, threadId);
|
|
13421
|
+
return runtime;
|
|
13422
|
+
}
|
|
13423
|
+
resolveAgent(runtime, threadId) {
|
|
13424
|
+
if (!this.deps.agentResolver) this.fail("INTERNAL_ERROR", 500, "Agent runtime is unavailable");
|
|
13425
|
+
return this.deps.agentResolver.getAgent({
|
|
13426
|
+
tenantId: runtime.webApp.tenantId,
|
|
13427
|
+
assistantId: runtime.webApp.assistantId,
|
|
13428
|
+
threadId,
|
|
13429
|
+
workspaceId: runtime.project.workspaceId,
|
|
13430
|
+
projectId: runtime.project.id,
|
|
13431
|
+
...runtime.modelKey ? { modelKey: runtime.modelKey } : {}
|
|
13432
|
+
});
|
|
13433
|
+
}
|
|
13434
|
+
resolveAttachmentRefs(runtime, userId, threadId, refs) {
|
|
13435
|
+
if (refs.length === 0) return [];
|
|
13436
|
+
this.requireNamedFeature(runtime.webApp.features.attachments, "Attachments are disabled");
|
|
13437
|
+
if (!this.deps.attachmentRefs) this.fail("INTERNAL_ERROR", 500, "Attachment service is unavailable");
|
|
13438
|
+
try {
|
|
13439
|
+
return refs.map((ref) => ({
|
|
13440
|
+
...this.deps.attachmentRefs.verify(ref, {
|
|
13441
|
+
webAppId: runtime.webApp.id,
|
|
13442
|
+
userId,
|
|
13443
|
+
threadId,
|
|
13444
|
+
projectId: runtime.project.id
|
|
13445
|
+
}),
|
|
13446
|
+
addedBy: "user"
|
|
13447
|
+
}));
|
|
13448
|
+
} catch {
|
|
13449
|
+
return this.fail("INVALID_REQUEST", 400, "Invalid attachment reference");
|
|
13450
|
+
}
|
|
13451
|
+
}
|
|
13452
|
+
async ownedThreads(runtime, userId) {
|
|
13453
|
+
return this.deps.threadStore.getThreadsByAssistantId(
|
|
13454
|
+
runtime.webApp.tenantId,
|
|
13455
|
+
runtime.webApp.assistantId,
|
|
13456
|
+
{
|
|
13457
|
+
source: "web_app",
|
|
13458
|
+
webAppId: runtime.webApp.id,
|
|
13459
|
+
userId,
|
|
13460
|
+
projectId: runtime.project.id
|
|
13461
|
+
}
|
|
13462
|
+
);
|
|
13463
|
+
}
|
|
13464
|
+
async requireOwnedThread(runtime, userId, threadId) {
|
|
13465
|
+
const thread = await this.deps.threadStore.getThreadById(runtime.webApp.tenantId, threadId);
|
|
13466
|
+
const metadata = thread?.metadata;
|
|
13467
|
+
if (!thread || thread.assistantId !== runtime.webApp.assistantId || metadata?.source !== "web_app" || metadata.webAppId !== runtime.webApp.id || metadata.userId !== userId || metadata.projectId !== runtime.project.id) this.threadNotFound();
|
|
13468
|
+
return thread;
|
|
13469
|
+
}
|
|
13470
|
+
async resolveImplicitThread(runtime, userId) {
|
|
13471
|
+
const latest = (await this.ownedThreads(runtime, userId)).sort(compareThreads)[0];
|
|
13472
|
+
if (latest) return toRuntimeThread(latest);
|
|
13473
|
+
const threadId = implicitThreadId(runtime, userId);
|
|
13474
|
+
const existing = await this.deps.threadStore.getThreadById(
|
|
13475
|
+
runtime.webApp.tenantId,
|
|
13476
|
+
threadId
|
|
13477
|
+
);
|
|
13478
|
+
if (existing) return toRuntimeThread(this.requireImplicitOwnership(runtime, userId, existing));
|
|
13479
|
+
await this.deps.threadStore.createThread(
|
|
13480
|
+
runtime.webApp.tenantId,
|
|
13481
|
+
runtime.webApp.assistantId,
|
|
13482
|
+
threadId,
|
|
13483
|
+
{ metadata: this.metadata(runtime, userId) }
|
|
13484
|
+
);
|
|
13485
|
+
const persisted = await this.deps.threadStore.getThreadById(
|
|
13486
|
+
runtime.webApp.tenantId,
|
|
13487
|
+
threadId
|
|
13488
|
+
);
|
|
13489
|
+
if (!persisted) this.fail("INTERNAL_ERROR", 500, "Implicit thread could not be resolved");
|
|
13490
|
+
return toRuntimeThread(this.requireImplicitOwnership(runtime, userId, persisted));
|
|
13491
|
+
}
|
|
13492
|
+
requireImplicitOwnership(runtime, userId, thread) {
|
|
13493
|
+
const metadata = thread.metadata;
|
|
13494
|
+
if (thread.assistantId !== runtime.webApp.assistantId || metadata?.source !== "web_app" || metadata.webAppId !== runtime.webApp.id || metadata.userId !== userId || metadata.projectId !== runtime.project.id) this.fail("INTERNAL_ERROR", 500, "Implicit thread identity conflict");
|
|
13495
|
+
return thread;
|
|
13496
|
+
}
|
|
13497
|
+
async createOwnedThread(runtime, userId, rawLabel) {
|
|
13498
|
+
const thread = await this.deps.threadStore.createThread(
|
|
13499
|
+
runtime.webApp.tenantId,
|
|
13500
|
+
runtime.webApp.assistantId,
|
|
13501
|
+
(0, import_node_crypto3.randomUUID)(),
|
|
13502
|
+
{ metadata: this.metadata(runtime, userId, normalizeLabel(rawLabel)) }
|
|
13503
|
+
);
|
|
13504
|
+
return toRuntimeThread(thread);
|
|
13505
|
+
}
|
|
13506
|
+
metadata(runtime, userId, label) {
|
|
13507
|
+
return {
|
|
13508
|
+
source: "web_app",
|
|
13509
|
+
webAppId: runtime.webApp.id,
|
|
13510
|
+
userId,
|
|
13511
|
+
projectId: runtime.project.id,
|
|
13512
|
+
...label ? { label } : {}
|
|
13513
|
+
};
|
|
13514
|
+
}
|
|
13515
|
+
requireFeature(enabled) {
|
|
13516
|
+
if (!enabled) this.fail("FEATURE_DISABLED", 403, "Thread management is disabled");
|
|
13517
|
+
}
|
|
13518
|
+
requireNamedFeature(enabled, message) {
|
|
13519
|
+
if (!enabled) this.fail("FEATURE_DISABLED", 403, message);
|
|
13520
|
+
}
|
|
13521
|
+
threadNotFound() {
|
|
13522
|
+
return this.fail("THREAD_NOT_FOUND", 404, "Thread not found");
|
|
13523
|
+
}
|
|
13524
|
+
fail(code, statusCode, message) {
|
|
13525
|
+
throw new AgentWebAppRuntimeError(code, statusCode, message);
|
|
13526
|
+
}
|
|
13527
|
+
};
|
|
13528
|
+
function hasInterrupt(state, interruptId) {
|
|
13529
|
+
if (typeof state !== "object" || state === null || !("tasks" in state) || !Array.isArray(state.tasks)) return false;
|
|
13530
|
+
return state.tasks.some((task) => {
|
|
13531
|
+
if (typeof task !== "object" || task === null || !("interrupts" in task) || !Array.isArray(task.interrupts)) return false;
|
|
13532
|
+
return task.interrupts.some((interrupt) => typeof interrupt === "object" && interrupt !== null && "id" in interrupt && interrupt.id === interruptId);
|
|
13533
|
+
});
|
|
13534
|
+
}
|
|
13535
|
+
function sanitizePhysicalFilename(name) {
|
|
13536
|
+
const sanitized = name.replace(/[^A-Za-z0-9._-]/g, "_").replace(/^\.+/, "");
|
|
13537
|
+
return sanitized || "file";
|
|
13538
|
+
}
|
|
13539
|
+
function normalizeLabel(label) {
|
|
13540
|
+
const normalized = label?.trim();
|
|
13541
|
+
return normalized || void 0;
|
|
13542
|
+
}
|
|
13543
|
+
function implicitThreadId(runtime, userId) {
|
|
13544
|
+
const canonicalTuple = JSON.stringify([
|
|
13545
|
+
"axiom.agent-web-app.implicit-thread",
|
|
13546
|
+
1,
|
|
13547
|
+
runtime.webApp.tenantId,
|
|
13548
|
+
runtime.webApp.assistantId,
|
|
13549
|
+
runtime.webApp.id,
|
|
13550
|
+
userId,
|
|
13551
|
+
runtime.project.id
|
|
13552
|
+
]);
|
|
13553
|
+
const bytes = (0, import_node_crypto3.createHash)("sha256").update(canonicalTuple, "utf8").digest().subarray(0, 16);
|
|
13554
|
+
bytes[6] = bytes[6] & 15 | 128;
|
|
13555
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
13556
|
+
const hex = bytes.toString("hex");
|
|
13557
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
13558
|
+
}
|
|
13559
|
+
function compareThreads(left, right) {
|
|
13560
|
+
return right.updatedAt.getTime() - left.updatedAt.getTime() || right.id.localeCompare(left.id);
|
|
13561
|
+
}
|
|
13562
|
+
function toRuntimeThread(thread) {
|
|
13563
|
+
return {
|
|
13564
|
+
id: thread.id,
|
|
13565
|
+
projectId: String(thread.metadata?.projectId),
|
|
13566
|
+
...typeof thread.metadata?.label === "string" ? { label: thread.metadata.label } : {},
|
|
13567
|
+
createdAt: thread.createdAt,
|
|
13568
|
+
updatedAt: thread.updatedAt
|
|
13569
|
+
};
|
|
13570
|
+
}
|
|
13571
|
+
|
|
13572
|
+
// src/services/agent-web-app/AgentWebAppStreamProjector.ts
|
|
13573
|
+
var import_protocols7 = require("@axiom-lattice/protocols");
|
|
13574
|
+
function createAgentWebAppStreamProjectionContext(options = {}) {
|
|
13575
|
+
return { startedToolIds: /* @__PURE__ */ new Set(), allowInterrupts: options.allowInterrupts ?? true, allowGenUI: options.allowGenUI ?? false };
|
|
13576
|
+
}
|
|
13577
|
+
function projectAgentWebAppChunk(chunk, context) {
|
|
13578
|
+
if (chunk.type === import_protocols7.MessageChunkTypes.AI) {
|
|
13579
|
+
const events = [];
|
|
13580
|
+
const startedToolIds = context?.startedToolIds ?? /* @__PURE__ */ new Set();
|
|
13581
|
+
if (typeof chunk.data.content === "string" && chunk.data.content) events.push({ type: "message.delta", text: chunk.data.content });
|
|
13582
|
+
if (context?.allowGenUI && Array.isArray(chunk.data.content)) {
|
|
13583
|
+
for (const value of chunk.data.content) {
|
|
13584
|
+
const block = (0, import_protocols7.parseAgentWebAppGenUIBlock)(value);
|
|
13585
|
+
if (block) events.push({ type: "genui.render", block });
|
|
13586
|
+
}
|
|
13587
|
+
}
|
|
13588
|
+
for (const call of chunk.data.tool_call_chunks ?? []) {
|
|
13589
|
+
if (call.id && call.name && !startedToolIds.has(call.id)) {
|
|
13590
|
+
startedToolIds.add(call.id);
|
|
13591
|
+
events.push({ type: "tool.started", id: call.id, name: call.name });
|
|
13592
|
+
}
|
|
13593
|
+
}
|
|
13594
|
+
for (const call of chunk.data.tool_calls ?? []) {
|
|
13595
|
+
if (!startedToolIds.has(call.id)) {
|
|
13596
|
+
startedToolIds.add(call.id);
|
|
13597
|
+
events.push({ type: "tool.started", id: call.id, name: call.name });
|
|
13598
|
+
}
|
|
13599
|
+
}
|
|
13600
|
+
return events;
|
|
13601
|
+
}
|
|
13602
|
+
if (chunk.type === import_protocols7.MessageChunkTypes.TOOL && chunk.data.tool_call_id) {
|
|
13603
|
+
return [{ type: "tool.completed", id: chunk.data.tool_call_id }];
|
|
13604
|
+
}
|
|
13605
|
+
if (chunk.type === import_protocols7.MessageChunkTypes.INTERRUPT) {
|
|
13606
|
+
if (context && !context.allowInterrupts) return [{ type: "stream.completed" }];
|
|
13607
|
+
const interrupt = publicInterrupt(chunk);
|
|
13608
|
+
return interrupt ? [{ type: "interrupt.created", interrupt }, { type: "stream.completed" }] : [{ type: "stream.completed" }];
|
|
13609
|
+
}
|
|
13610
|
+
if (chunk.type === import_protocols7.MessageChunkTypes.MESSAGE_COMPLETED) {
|
|
13611
|
+
return [
|
|
13612
|
+
{ type: "message.completed", messageId: chunk.data.id },
|
|
13613
|
+
{ type: "stream.completed" }
|
|
13614
|
+
];
|
|
13615
|
+
}
|
|
13616
|
+
if (chunk.type === import_protocols7.MessageChunkTypes.MESSAGE_FAILED) {
|
|
13617
|
+
return [
|
|
13618
|
+
{ type: "error", error: { code: "STREAM_FAILED", message: "Stream failed", retryable: true } },
|
|
13619
|
+
{ type: "stream.completed" }
|
|
13620
|
+
];
|
|
13621
|
+
}
|
|
13622
|
+
return [];
|
|
13623
|
+
}
|
|
13624
|
+
function publicInterrupt(chunk) {
|
|
13625
|
+
const value = parseInterruptContent(chunk.data.content);
|
|
13626
|
+
if (!value) return void 0;
|
|
13627
|
+
const topLevelId = chunk.id;
|
|
13628
|
+
const id = typeof topLevelId === "string" ? topLevelId : chunk.data.id;
|
|
13629
|
+
if (!id) return void 0;
|
|
13630
|
+
return {
|
|
13631
|
+
id,
|
|
13632
|
+
type: typeof value.type === "string" ? value.type : "input",
|
|
13633
|
+
prompt: typeof value.prompt === "string" ? value.prompt : typeof value.message === "string" ? value.message : "Input required",
|
|
13634
|
+
...isRecord2(value.data) ? { data: value.data } : {}
|
|
13635
|
+
};
|
|
13636
|
+
}
|
|
13637
|
+
function parseInterruptContent(content) {
|
|
13638
|
+
if (!content) return {};
|
|
13639
|
+
if (isRecord2(content)) return content;
|
|
13640
|
+
if (typeof content !== "string") return void 0;
|
|
13641
|
+
try {
|
|
13642
|
+
const parsed = JSON.parse(content);
|
|
13643
|
+
return isRecord2(parsed) ? parsed : { prompt: content };
|
|
13644
|
+
} catch {
|
|
13645
|
+
return { prompt: content };
|
|
13646
|
+
}
|
|
13647
|
+
}
|
|
13648
|
+
function isRecord2(value) {
|
|
13649
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13650
|
+
}
|
|
13651
|
+
|
|
13652
|
+
// src/routes/agent-web-app-runtime.ts
|
|
13653
|
+
var idSchema = import_zod5.z.string().min(1).max(128);
|
|
13654
|
+
var paramsSchema = import_zod5.z.object({ webAppId: idSchema }).strict();
|
|
13655
|
+
var threadParamsSchema = import_zod5.z.object({ webAppId: idSchema, threadId: idSchema }).strict();
|
|
13656
|
+
var interruptParamsSchema = import_zod5.z.object({ webAppId: idSchema, threadId: idSchema, interruptId: idSchema }).strict();
|
|
13657
|
+
var bootstrapQuerySchema = import_zod5.z.object({ projectId: idSchema.optional(), modelKey: idSchema.optional() }).strict();
|
|
13658
|
+
var projectQuerySchema = import_zod5.z.object({ projectId: idSchema.optional() }).strict();
|
|
13659
|
+
var threadBodySchema = import_zod5.z.object({ projectId: idSchema.optional(), label: import_zod5.z.string().max(128).optional() }).strict();
|
|
13660
|
+
var streamBodySchema = import_zod5.z.object({
|
|
13661
|
+
content: import_zod5.z.string().trim().min(1).max(32768),
|
|
13662
|
+
projectId: idSchema.optional(),
|
|
13663
|
+
modelKey: idSchema.optional(),
|
|
13664
|
+
attachmentRefs: import_zod5.z.array(import_zod5.z.string().min(1).max(8192)).max(10).optional()
|
|
13665
|
+
}).strict();
|
|
13666
|
+
var resumeBodySchema = import_zod5.z.object({
|
|
13667
|
+
projectId: idSchema.optional(),
|
|
13668
|
+
modelKey: idSchema.optional(),
|
|
13669
|
+
message: import_zod5.z.string().trim().min(1).max(4096),
|
|
13670
|
+
response: import_zod5.z.union([
|
|
13671
|
+
import_zod5.z.boolean(),
|
|
13672
|
+
import_zod5.z.string().max(4096),
|
|
13673
|
+
import_zod5.z.object({ approved: import_zod5.z.boolean(), feedback: import_zod5.z.string().max(4096).optional() }).strict()
|
|
13674
|
+
])
|
|
13675
|
+
}).strict();
|
|
13676
|
+
var MAX_ATTACHMENT_BYTES = 10 * 1024 * 1024;
|
|
13677
|
+
function createExternalHeaderIdentityResolver() {
|
|
13678
|
+
return { resolve(request) {
|
|
13679
|
+
const raw = request.headers["x-axiom-external-user-id"];
|
|
13680
|
+
if (raw === void 0) throw new AgentWebAppRuntimeError("USER_ID_REQUIRED", 401, "External user id is required");
|
|
13681
|
+
if (Array.isArray(raw) || typeof raw !== "string") throw new AgentWebAppRuntimeError("INVALID_USER_ID", 400, "External user id is invalid");
|
|
13682
|
+
const userId = raw.trim();
|
|
13683
|
+
if (!/^[A-Za-z0-9._:@-]+$/.test(userId) || userId.length > 128) throw new AgentWebAppRuntimeError("INVALID_USER_ID", 400, "External user id is invalid");
|
|
13684
|
+
return { userId, assurance: "unverified" };
|
|
13685
|
+
} };
|
|
13686
|
+
}
|
|
13687
|
+
function registerAgentWebAppRuntimeRoutes(app2, deps) {
|
|
13688
|
+
const identities = deps.identityResolver ?? createExternalHeaderIdentityResolver();
|
|
13689
|
+
app2.register(async (routes) => {
|
|
13690
|
+
routes.setErrorHandler((error, _request, reply) => normalizeError2(reply, error));
|
|
13691
|
+
routes.get("/api/web-apps/:webAppId/runtime/bootstrap", async (request, reply) => handle2(reply, async () => {
|
|
13692
|
+
const { webAppId } = paramsSchema.parse(request.params);
|
|
13693
|
+
const identity = resolveRuntimeIdentity(request, webAppId, identities);
|
|
13694
|
+
return { data: await deps.service.bootstrap(webAppId, identity.userId, withPreview(bootstrapQuerySchema.parse(request.query), identity.preview)) };
|
|
13695
|
+
}));
|
|
13696
|
+
routes.get("/api/web-apps/:webAppId/runtime/threads", async (request, reply) => handle2(reply, async () => {
|
|
13697
|
+
const { webAppId } = paramsSchema.parse(request.params);
|
|
13698
|
+
const identity = resolveRuntimeIdentity(request, webAppId, identities);
|
|
13699
|
+
const records = await deps.service.listThreads(webAppId, identity.userId, withPreview(projectQuerySchema.parse(request.query), identity.preview));
|
|
13700
|
+
return { data: { records, total: records.length } };
|
|
13701
|
+
}));
|
|
13702
|
+
routes.post("/api/web-apps/:webAppId/runtime/threads", async (request, reply) => handle2(reply, async () => {
|
|
13703
|
+
const { webAppId } = paramsSchema.parse(request.params);
|
|
13704
|
+
const identity = resolveRuntimeIdentity(request, webAppId, identities);
|
|
13705
|
+
return { statusCode: 201, data: await deps.service.createThread(webAppId, identity.userId, withPreview(threadBodySchema.parse(request.body), identity.preview)) };
|
|
13706
|
+
}));
|
|
13707
|
+
routes.patch("/api/web-apps/:webAppId/runtime/threads/:threadId", async (request, reply) => handle2(reply, async () => {
|
|
13708
|
+
const { webAppId, threadId } = threadParamsSchema.parse(request.params);
|
|
13709
|
+
const identity = resolveRuntimeIdentity(request, webAppId, identities);
|
|
13710
|
+
return { data: await deps.service.updateThread(webAppId, identity.userId, threadId, withPreview(threadBodySchema.parse(request.body), identity.preview)) };
|
|
13711
|
+
}));
|
|
13712
|
+
routes.delete("/api/web-apps/:webAppId/runtime/threads/:threadId", async (request, reply) => handle2(reply, async () => {
|
|
13713
|
+
const { webAppId, threadId } = threadParamsSchema.parse(request.params);
|
|
13714
|
+
const identity = resolveRuntimeIdentity(request, webAppId, identities);
|
|
13715
|
+
await deps.service.deleteThread(webAppId, identity.userId, threadId, withPreview(projectQuerySchema.parse(request.query), identity.preview));
|
|
13716
|
+
return { data: { id: threadId } };
|
|
13717
|
+
}));
|
|
13718
|
+
routes.get("/api/web-apps/:webAppId/runtime/threads/:threadId/messages", async (request, reply) => handle2(reply, async () => {
|
|
13719
|
+
const { webAppId, threadId } = threadParamsSchema.parse(request.params);
|
|
13720
|
+
const identity = resolveRuntimeIdentity(request, webAppId, identities);
|
|
13721
|
+
const messages = await deps.service.getMessages(webAppId, identity.userId, threadId, withPreview(projectQuerySchema.parse(request.query), identity.preview));
|
|
13722
|
+
return { data: { messages } };
|
|
13723
|
+
}));
|
|
13724
|
+
routes.post("/api/web-apps/:webAppId/runtime/threads/:threadId/messages/stream", async (request, reply) => {
|
|
13725
|
+
try {
|
|
13726
|
+
const { webAppId, threadId } = threadParamsSchema.parse(request.params);
|
|
13727
|
+
const identity = resolveRuntimeIdentity(request, webAppId, identities);
|
|
13728
|
+
const input = streamBodySchema.parse(request.body);
|
|
13729
|
+
const result = await deps.service.streamMessage(webAppId, identity.userId, threadId, withPreview(input, identity.preview));
|
|
13730
|
+
beginSse(reply);
|
|
13731
|
+
await pipeAgentWebAppStream(reply.raw, result.stream, result);
|
|
13732
|
+
} catch (error) {
|
|
13733
|
+
return normalizeError2(reply, error);
|
|
13734
|
+
}
|
|
13735
|
+
});
|
|
13736
|
+
routes.post("/api/web-apps/:webAppId/runtime/threads/:threadId/abort", async (request, reply) => handle2(reply, async () => {
|
|
13737
|
+
const { webAppId, threadId } = threadParamsSchema.parse(request.params);
|
|
13738
|
+
const identity = resolveRuntimeIdentity(request, webAppId, identities);
|
|
13739
|
+
await deps.service.abort(webAppId, identity.userId, threadId, withPreview(projectQuerySchema.parse(request.query), identity.preview));
|
|
13740
|
+
return { data: { aborted: true } };
|
|
13741
|
+
}));
|
|
13742
|
+
routes.post("/api/web-apps/:webAppId/runtime/threads/:threadId/attachments", async (request, reply) => handle2(reply, async () => {
|
|
13743
|
+
const { webAppId, threadId } = threadParamsSchema.parse(request.params);
|
|
13744
|
+
const identity = resolveRuntimeIdentity(request, webAppId, identities);
|
|
13745
|
+
if (!request.isMultipart()) throw new import_zod5.ZodError([]);
|
|
13746
|
+
const part = await request.file({ limits: { files: 1, fileSize: MAX_ATTACHMENT_BYTES } });
|
|
13747
|
+
if (!part || !part.filename || part.filename.length > 255) throw new import_zod5.ZodError([]);
|
|
13748
|
+
const bytes = await part.toBuffer();
|
|
13749
|
+
if (bytes.length === 0 || part.file.truncated) throw new import_zod5.ZodError([]);
|
|
13750
|
+
const data = await deps.service.uploadAttachment(webAppId, identity.userId, threadId, withPreview(projectQuerySchema.parse(request.query), identity.preview), {
|
|
13751
|
+
name: part.filename,
|
|
13752
|
+
mimeType: part.mimetype || "application/octet-stream",
|
|
13753
|
+
bytes
|
|
13754
|
+
});
|
|
13755
|
+
return { statusCode: 201, data };
|
|
13756
|
+
}));
|
|
13757
|
+
routes.post("/api/web-apps/:webAppId/runtime/threads/:threadId/interrupts/:interruptId/resume", async (request, reply) => {
|
|
13758
|
+
try {
|
|
13759
|
+
const { webAppId, threadId, interruptId } = interruptParamsSchema.parse(request.params);
|
|
13760
|
+
const identity = resolveRuntimeIdentity(request, webAppId, identities);
|
|
13761
|
+
const { projectId, modelKey: modelKey2, message, response } = resumeBodySchema.parse(request.body);
|
|
13762
|
+
const result = await deps.service.resumeInterrupt(webAppId, identity.userId, threadId, interruptId, withPreview({ projectId, modelKey: modelKey2 }, identity.preview), { message, response });
|
|
13763
|
+
beginSse(reply);
|
|
13764
|
+
await pipeAgentWebAppStream(reply.raw, result.stream, result);
|
|
13765
|
+
} catch (error) {
|
|
13766
|
+
return normalizeError2(reply, error);
|
|
13767
|
+
}
|
|
13768
|
+
});
|
|
13769
|
+
});
|
|
13770
|
+
}
|
|
13771
|
+
function resolveRuntimeIdentity(request, webAppId, identities) {
|
|
13772
|
+
if (request.headers["x-axiom-web-app-preview"] !== "true") return identities.resolve(request);
|
|
13773
|
+
const user = request.user;
|
|
13774
|
+
if (!user?.id || !user.tenantId) throw new AgentWebAppRuntimeError("USER_ID_REQUIRED", 401, "Authenticated Console preview is required");
|
|
13775
|
+
return { userId: `console-preview:${user.id}:${webAppId}`, preview: { tenantId: user.tenantId, userId: user.id } };
|
|
13776
|
+
}
|
|
13777
|
+
function withPreview(selection, preview) {
|
|
13778
|
+
return { ...selection, ...preview ? { preview } : {} };
|
|
13779
|
+
}
|
|
13780
|
+
async function pipeAgentWebAppStream(raw, stream, options = {}) {
|
|
13781
|
+
const projection = createAgentWebAppStreamProjectionContext(options);
|
|
13782
|
+
await pipeAsyncIterableToSse(raw, stream, {
|
|
13783
|
+
project: (chunk) => projectAgentWebAppChunk(chunk, projection),
|
|
13784
|
+
onError: () => [
|
|
13785
|
+
{ type: "error", error: { code: "STREAM_FAILED", message: "Stream failed", retryable: true } },
|
|
13786
|
+
{ type: "stream.completed" }
|
|
13787
|
+
]
|
|
13788
|
+
});
|
|
13789
|
+
}
|
|
13790
|
+
function beginSse(reply) {
|
|
13791
|
+
reply.hijack();
|
|
13792
|
+
reply.raw.writeHead(200, {
|
|
13793
|
+
"Content-Type": "text/event-stream; charset=utf-8",
|
|
13794
|
+
"Cache-Control": "no-cache, no-transform",
|
|
13795
|
+
Connection: "keep-alive",
|
|
13796
|
+
"X-Accel-Buffering": "no"
|
|
13797
|
+
});
|
|
13798
|
+
}
|
|
13799
|
+
async function handle2(reply, operation) {
|
|
13800
|
+
try {
|
|
13801
|
+
const result = await operation();
|
|
13802
|
+
return reply.code(result.statusCode ?? 200).send({ success: true, data: result.data });
|
|
13803
|
+
} catch (error) {
|
|
13804
|
+
return normalizeError2(reply, error);
|
|
13805
|
+
}
|
|
13806
|
+
}
|
|
13807
|
+
function normalizeError2(reply, error) {
|
|
13808
|
+
if (error instanceof AgentWebAppRuntimeError) return reply.code(error.statusCode).send({ success: false, error: { code: error.code, message: error.message, retryable: false } });
|
|
13809
|
+
if (error instanceof import_zod5.ZodError) return reply.code(400).send({ success: false, error: { code: "INVALID_REQUEST", message: "Invalid request", retryable: false } });
|
|
13810
|
+
if (isMultipartRequestError(error)) return reply.code(400).send({ success: false, error: { code: "INVALID_REQUEST", message: "Invalid request", retryable: false } });
|
|
13811
|
+
return reply.code(500).send({ success: false, error: { code: "INTERNAL_ERROR", message: "Internal server error", retryable: false } });
|
|
13812
|
+
}
|
|
13813
|
+
function isMultipartRequestError(error) {
|
|
13814
|
+
return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" && error.code.startsWith("FST_");
|
|
13815
|
+
}
|
|
13816
|
+
|
|
13817
|
+
// src/services/agent-web-app/AgentWebAppAttachmentRef.ts
|
|
13818
|
+
var import_node_crypto4 = require("crypto");
|
|
13819
|
+
var TTL_MS = 15 * 6e4;
|
|
13820
|
+
function resolveAgentWebAppAttachmentSecret(consoleSecret) {
|
|
13821
|
+
return process.env.WEB_APP_ATTACHMENT_SECRET || process.env.AUTH_TOKEN_SECRET || process.env.JWT_SECRET || consoleSecret;
|
|
13822
|
+
}
|
|
13823
|
+
var AgentWebAppAttachmentRefService = class {
|
|
13824
|
+
constructor(secret, now = Date.now) {
|
|
13825
|
+
this.now = now;
|
|
13826
|
+
if (!secret) throw new Error("Attachment signing secret is required");
|
|
13827
|
+
this.key = (0, import_node_crypto4.createHash)("sha256").update(secret, "utf8").digest();
|
|
13828
|
+
}
|
|
13829
|
+
sign(input) {
|
|
13830
|
+
const payload = {
|
|
13831
|
+
webAppId: input.webAppId,
|
|
13832
|
+
userDigest: digestUser(input.userId),
|
|
13833
|
+
threadId: input.threadId,
|
|
13834
|
+
projectId: input.projectId,
|
|
13835
|
+
uri: input.uri,
|
|
13836
|
+
name: input.name,
|
|
13837
|
+
mimeType: input.mimeType,
|
|
13838
|
+
size: input.size,
|
|
13839
|
+
contentDigest: input.contentDigest,
|
|
13840
|
+
exp: this.now() + TTL_MS
|
|
13841
|
+
};
|
|
13842
|
+
const iv = (0, import_node_crypto4.randomBytes)(12);
|
|
13843
|
+
const cipher = (0, import_node_crypto4.createCipheriv)("aes-256-gcm", this.key, iv);
|
|
13844
|
+
const ciphertext = Buffer.concat([
|
|
13845
|
+
cipher.update(JSON.stringify(payload), "utf8"),
|
|
13846
|
+
cipher.final()
|
|
13847
|
+
]);
|
|
13848
|
+
const tag = cipher.getAuthTag();
|
|
13849
|
+
return `v1.${iv.toString("base64url")}.${ciphertext.toString("base64url")}.${tag.toString("base64url")}`;
|
|
13850
|
+
}
|
|
13851
|
+
verify(ref, scope) {
|
|
13852
|
+
const [version, encodedIv, encodedCiphertext, encodedTag, extra] = ref.split(".");
|
|
13853
|
+
if (version !== "v1" || !encodedIv || !encodedCiphertext || !encodedTag || extra !== void 0 || !/^[A-Za-z0-9_-]{16}$/.test(encodedIv) || !/^[A-Za-z0-9_-]+$/.test(encodedCiphertext) || !/^[A-Za-z0-9_-]{22}$/.test(encodedTag)) this.invalid();
|
|
13854
|
+
try {
|
|
13855
|
+
const iv = decodeCanonical(encodedIv, 12);
|
|
13856
|
+
const ciphertext = decodeCanonical(encodedCiphertext);
|
|
13857
|
+
const tag = decodeCanonical(encodedTag, 16);
|
|
13858
|
+
const decipher = (0, import_node_crypto4.createDecipheriv)("aes-256-gcm", this.key, iv);
|
|
13859
|
+
decipher.setAuthTag(tag);
|
|
13860
|
+
const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
13861
|
+
const value = JSON.parse(plaintext.toString("utf8"));
|
|
13862
|
+
if (!isAttachmentPayload(value)) this.invalid();
|
|
13863
|
+
if (value.exp <= this.now()) throw new Error("Attachment reference expired");
|
|
13864
|
+
if (value.webAppId !== scope.webAppId || value.userDigest !== digestUser(scope.userId) || value.threadId !== scope.threadId || value.projectId !== scope.projectId) this.invalid();
|
|
13865
|
+
return {
|
|
13866
|
+
uri: value.uri,
|
|
13867
|
+
name: value.name,
|
|
13868
|
+
mimeType: value.mimeType,
|
|
13869
|
+
size: value.size,
|
|
13870
|
+
contentDigest: value.contentDigest
|
|
13871
|
+
};
|
|
13872
|
+
} catch (error) {
|
|
13873
|
+
if (error instanceof Error && error.message === "Attachment reference expired") throw error;
|
|
13874
|
+
this.invalid();
|
|
13875
|
+
}
|
|
13876
|
+
}
|
|
13877
|
+
invalid() {
|
|
13878
|
+
throw new Error("Invalid attachment reference");
|
|
13879
|
+
}
|
|
13880
|
+
};
|
|
13881
|
+
function decodeCanonical(value, expectedBytes) {
|
|
13882
|
+
const decoded = Buffer.from(value, "base64url");
|
|
13883
|
+
if (decoded.toString("base64url") !== value || expectedBytes !== void 0 && decoded.length !== expectedBytes) {
|
|
13884
|
+
throw new Error("Invalid attachment reference");
|
|
13885
|
+
}
|
|
13886
|
+
return decoded;
|
|
13887
|
+
}
|
|
13888
|
+
function digestUser(userId) {
|
|
13889
|
+
return (0, import_node_crypto4.createHash)("sha256").update(userId, "utf8").digest("hex");
|
|
13890
|
+
}
|
|
13891
|
+
function isAttachmentPayload(value) {
|
|
13892
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
13893
|
+
const record = value;
|
|
13894
|
+
if (Object.keys(record).sort().join(",") !== "contentDigest,exp,mimeType,name,projectId,size,threadId,uri,userDigest,webAppId") return false;
|
|
13895
|
+
return typeof record.webAppId === "string" && typeof record.userDigest === "string" && /^[a-f0-9]{64}$/.test(record.userDigest) && typeof record.threadId === "string" && typeof record.projectId === "string" && typeof record.uri === "string" && record.uri.startsWith("/") && typeof record.name === "string" && record.name.length > 0 && typeof record.mimeType === "string" && record.mimeType.length > 0 && typeof record.size === "number" && Number.isSafeInteger(record.size) && record.size >= 0 && typeof record.contentDigest === "string" && /^[a-f0-9]{64}$/.test(record.contentDigest) && typeof record.exp === "number" && Number.isSafeInteger(record.exp);
|
|
13896
|
+
}
|
|
13897
|
+
|
|
13898
|
+
// src/routes/index.ts
|
|
13899
|
+
init_workspace_file_service();
|
|
12842
13900
|
var registerLatticeRoutes = (app2, channelDeps) => {
|
|
13901
|
+
const webAppStore = (0, import_core41.getStoreLattice)("default", "agentWebApp").store;
|
|
13902
|
+
const assistantStore = (0, import_core41.getStoreLattice)("default", "assistant").store;
|
|
13903
|
+
const projectStore = (0, import_core41.getStoreLattice)("default", "project").store;
|
|
13904
|
+
registerAgentWebAppRoutes(app2, {
|
|
13905
|
+
service: new AgentWebAppService({
|
|
13906
|
+
webAppStore,
|
|
13907
|
+
assistantStore,
|
|
13908
|
+
projectStore,
|
|
13909
|
+
modelRegistry: { has: (key) => import_core41.modelLatticeManager.hasLattice(key) },
|
|
13910
|
+
configuredAgentResolver: createConfiguredAgentResolver({
|
|
13911
|
+
hydrate: (tenantId) => import_core41.agentLatticeManager.initializeStoredAssistantsForTenant(tenantId),
|
|
13912
|
+
has: (tenantId, assistantId) => import_core41.agentLatticeManager.hasAgentLatticeWithTenant(tenantId, assistantId)
|
|
13913
|
+
})
|
|
13914
|
+
})
|
|
13915
|
+
});
|
|
13916
|
+
registerAgentWebAppRuntimeRoutes(app2, {
|
|
13917
|
+
service: new AgentWebAppRuntimeService({
|
|
13918
|
+
webAppStore,
|
|
13919
|
+
threadStore: (0, import_core41.getStoreLattice)("default", "thread").store,
|
|
13920
|
+
projectStore,
|
|
13921
|
+
assistantResolver: {
|
|
13922
|
+
resolve: async (tenantId, assistantId) => {
|
|
13923
|
+
const stored = await assistantStore.getAssistantById(tenantId, assistantId);
|
|
13924
|
+
if (stored) return stored;
|
|
13925
|
+
await import_core41.agentLatticeManager.initializeStoredAssistantsForTenant(tenantId);
|
|
13926
|
+
const config = import_core41.agentLatticeManager.getAgentConfigWithTenant(tenantId, assistantId);
|
|
13927
|
+
return config ? { id: assistantId, name: config.name, description: config.description } : null;
|
|
13928
|
+
}
|
|
13929
|
+
},
|
|
13930
|
+
modelRegistry: {
|
|
13931
|
+
has: (key) => import_core41.modelLatticeManager.hasLattice(key),
|
|
13932
|
+
label: (key) => key
|
|
13933
|
+
},
|
|
13934
|
+
messageReader: {
|
|
13935
|
+
getCurrentMessages: ({ tenantId, assistantId, threadId, workspaceId, projectId }) => import_core41.agentInstanceManager.getAgent({
|
|
13936
|
+
tenant_id: tenantId,
|
|
13937
|
+
assistant_id: assistantId,
|
|
13938
|
+
thread_id: threadId,
|
|
13939
|
+
workspace_id: workspaceId,
|
|
13940
|
+
project_id: projectId
|
|
13941
|
+
}).getCurrentMessages()
|
|
13942
|
+
},
|
|
13943
|
+
agentResolver: {
|
|
13944
|
+
getAgent: ({ tenantId, assistantId, threadId, workspaceId, projectId }) => import_core41.agentInstanceManager.getAgent({
|
|
13945
|
+
tenant_id: tenantId,
|
|
13946
|
+
assistant_id: assistantId,
|
|
13947
|
+
thread_id: threadId,
|
|
13948
|
+
workspace_id: workspaceId,
|
|
13949
|
+
project_id: projectId
|
|
13950
|
+
})
|
|
13951
|
+
},
|
|
13952
|
+
attachmentRefs: new AgentWebAppAttachmentRefService(
|
|
13953
|
+
resolveAgentWebAppAttachmentSecret(
|
|
13954
|
+
resolveConsoleTokenSecret(process.env.AUTH_REQUIRED === "true")
|
|
13955
|
+
)
|
|
13956
|
+
),
|
|
13957
|
+
saveProjectFile,
|
|
13958
|
+
authorizePreview: async (webApp, preview) => {
|
|
13959
|
+
if (webApp.tenantId !== preview.tenantId) return false;
|
|
13960
|
+
const assistant = await assistantStore.getAssistantById(preview.tenantId, webApp.assistantId);
|
|
13961
|
+
if (assistant) return !assistant.ownerUserId || assistant.ownerUserId === preview.userId;
|
|
13962
|
+
await import_core41.agentLatticeManager.initializeStoredAssistantsForTenant(preview.tenantId);
|
|
13963
|
+
return import_core41.agentLatticeManager.hasAgentLatticeWithTenant(preview.tenantId, webApp.assistantId);
|
|
13964
|
+
}
|
|
13965
|
+
})
|
|
13966
|
+
});
|
|
12843
13967
|
registerA2ABridgeRoutes(app2);
|
|
12844
13968
|
registerSTTRoutes(app2);
|
|
12845
13969
|
app2.get("/api/local-a2a", getLocalA2AStatus);
|
|
@@ -13136,12 +14260,12 @@ function registerResourceRoutes(app2, controller) {
|
|
|
13136
14260
|
}
|
|
13137
14261
|
|
|
13138
14262
|
// src/export_registrations/index.ts
|
|
13139
|
-
var
|
|
14263
|
+
var import_core52 = require("@axiom-lattice/core");
|
|
13140
14264
|
|
|
13141
14265
|
// src/export_registrations/skill.registration.ts
|
|
13142
|
-
var
|
|
14266
|
+
var import_core42 = require("@axiom-lattice/core");
|
|
13143
14267
|
function getStore2(context) {
|
|
13144
|
-
return new
|
|
14268
|
+
return new import_core42.SandboxSkillStore({
|
|
13145
14269
|
workspaceId: context?.workspaceId,
|
|
13146
14270
|
projectId: context?.projectId
|
|
13147
14271
|
});
|
|
@@ -13159,7 +14283,7 @@ var skillRegistration = {
|
|
|
13159
14283
|
async listForExport(tenantId, context) {
|
|
13160
14284
|
const store2 = getStore2(context);
|
|
13161
14285
|
const skills = await store2.getAllSkills(tenantId, toStoreContext(context));
|
|
13162
|
-
return skills.filter((s) => !(0,
|
|
14286
|
+
return skills.filter((s) => !(0, import_core42.isBuiltInSkill)(s.id)).map((s) => ({
|
|
13163
14287
|
_exportId: "",
|
|
13164
14288
|
data: {
|
|
13165
14289
|
id: s.id,
|
|
@@ -13224,7 +14348,7 @@ var skillRegistration = {
|
|
|
13224
14348
|
const insertions = [];
|
|
13225
14349
|
for (const entity of entities) {
|
|
13226
14350
|
const id = entity.data.id;
|
|
13227
|
-
if ((0,
|
|
14351
|
+
if ((0, import_core42.isBuiltInSkill)(id)) continue;
|
|
13228
14352
|
if (existingIds.has(id)) {
|
|
13229
14353
|
conflicts.push({
|
|
13230
14354
|
_exportId: entity._exportId,
|
|
@@ -13283,9 +14407,9 @@ var skillRegistration = {
|
|
|
13283
14407
|
};
|
|
13284
14408
|
|
|
13285
14409
|
// src/export_registrations/agent.registration.ts
|
|
13286
|
-
var
|
|
14410
|
+
var import_core43 = require("@axiom-lattice/core");
|
|
13287
14411
|
function getStore3() {
|
|
13288
|
-
return (0,
|
|
14412
|
+
return (0, import_core43.getStoreLattice)("default", "assistant").store;
|
|
13289
14413
|
}
|
|
13290
14414
|
var agentRegistration = {
|
|
13291
14415
|
entityType: "agent",
|
|
@@ -13365,9 +14489,9 @@ var agentRegistration = {
|
|
|
13365
14489
|
};
|
|
13366
14490
|
|
|
13367
14491
|
// src/export_registrations/database-config.registration.ts
|
|
13368
|
-
var
|
|
14492
|
+
var import_core44 = require("@axiom-lattice/core");
|
|
13369
14493
|
function getStore4() {
|
|
13370
|
-
return (0,
|
|
14494
|
+
return (0, import_core44.getStoreLattice)("default", "database").store;
|
|
13371
14495
|
}
|
|
13372
14496
|
var databaseConfigRegistration = {
|
|
13373
14497
|
entityType: "database_config",
|
|
@@ -13447,9 +14571,9 @@ var databaseConfigRegistration = {
|
|
|
13447
14571
|
};
|
|
13448
14572
|
|
|
13449
14573
|
// src/export_registrations/metrics-config.registration.ts
|
|
13450
|
-
var
|
|
14574
|
+
var import_core45 = require("@axiom-lattice/core");
|
|
13451
14575
|
function getStore5() {
|
|
13452
|
-
return (0,
|
|
14576
|
+
return (0, import_core45.getStoreLattice)("default", "metrics").store;
|
|
13453
14577
|
}
|
|
13454
14578
|
var metricsConfigRegistration = {
|
|
13455
14579
|
entityType: "metrics_config",
|
|
@@ -13529,9 +14653,9 @@ var metricsConfigRegistration = {
|
|
|
13529
14653
|
};
|
|
13530
14654
|
|
|
13531
14655
|
// src/export_registrations/mcp-config.registration.ts
|
|
13532
|
-
var
|
|
14656
|
+
var import_core46 = require("@axiom-lattice/core");
|
|
13533
14657
|
function getStore6() {
|
|
13534
|
-
return (0,
|
|
14658
|
+
return (0, import_core46.getStoreLattice)("default", "mcp").store;
|
|
13535
14659
|
}
|
|
13536
14660
|
var mcpConfigRegistration = {
|
|
13537
14661
|
entityType: "mcp_config",
|
|
@@ -13614,9 +14738,9 @@ var mcpConfigRegistration = {
|
|
|
13614
14738
|
};
|
|
13615
14739
|
|
|
13616
14740
|
// src/export_registrations/connection.registration.ts
|
|
13617
|
-
var
|
|
14741
|
+
var import_core47 = require("@axiom-lattice/core");
|
|
13618
14742
|
function getStore7() {
|
|
13619
|
-
return (0,
|
|
14743
|
+
return (0, import_core47.getStoreLattice)("default", "connection").store;
|
|
13620
14744
|
}
|
|
13621
14745
|
async function listAllConnections(tenantId) {
|
|
13622
14746
|
const store2 = getStore7();
|
|
@@ -13698,9 +14822,9 @@ var connectionRegistration = {
|
|
|
13698
14822
|
};
|
|
13699
14823
|
|
|
13700
14824
|
// src/export_registrations/collection.registration.ts
|
|
13701
|
-
var
|
|
14825
|
+
var import_core48 = require("@axiom-lattice/core");
|
|
13702
14826
|
function getStore8() {
|
|
13703
|
-
return (0,
|
|
14827
|
+
return (0, import_core48.getStoreLattice)("default", "collection").store;
|
|
13704
14828
|
}
|
|
13705
14829
|
var collectionRegistration = {
|
|
13706
14830
|
entityType: "collection",
|
|
@@ -13771,11 +14895,11 @@ var collectionRegistration = {
|
|
|
13771
14895
|
};
|
|
13772
14896
|
|
|
13773
14897
|
// src/export_registrations/channel-installation.registration.ts
|
|
13774
|
-
var
|
|
14898
|
+
var import_core49 = require("@axiom-lattice/core");
|
|
13775
14899
|
var import_crypto10 = require("crypto");
|
|
13776
14900
|
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;
|
|
13777
14901
|
function getStore9() {
|
|
13778
|
-
return (0,
|
|
14902
|
+
return (0, import_core49.getStoreLattice)("default", "channelInstallation").store;
|
|
13779
14903
|
}
|
|
13780
14904
|
var channelInstallationRegistration = {
|
|
13781
14905
|
entityType: "channel_installation",
|
|
@@ -13874,9 +14998,9 @@ var channelInstallationRegistration = {
|
|
|
13874
14998
|
};
|
|
13875
14999
|
|
|
13876
15000
|
// src/export_registrations/menu.registration.ts
|
|
13877
|
-
var
|
|
15001
|
+
var import_core50 = require("@axiom-lattice/core");
|
|
13878
15002
|
function getStore10() {
|
|
13879
|
-
return (0,
|
|
15003
|
+
return (0, import_core50.getStoreLattice)("default", "menu").store;
|
|
13880
15004
|
}
|
|
13881
15005
|
var menuRegistration = {
|
|
13882
15006
|
entityType: "menu",
|
|
@@ -13954,9 +15078,9 @@ var menuRegistration = {
|
|
|
13954
15078
|
};
|
|
13955
15079
|
|
|
13956
15080
|
// src/export_registrations/eval.registration.ts
|
|
13957
|
-
var
|
|
15081
|
+
var import_core51 = require("@axiom-lattice/core");
|
|
13958
15082
|
function getStore11() {
|
|
13959
|
-
return (0,
|
|
15083
|
+
return (0, import_core51.getStoreLattice)("default", "eval").store;
|
|
13960
15084
|
}
|
|
13961
15085
|
var evalRegistration = {
|
|
13962
15086
|
entityType: "eval",
|
|
@@ -14282,7 +15406,7 @@ var evalRegistration = {
|
|
|
14282
15406
|
|
|
14283
15407
|
// src/export_registrations/index.ts
|
|
14284
15408
|
function registerAllBuiltinEntities() {
|
|
14285
|
-
const registry =
|
|
15409
|
+
const registry = import_core52.ExportableEntityRegistry.getInstance();
|
|
14286
15410
|
registry.register(skillRegistration);
|
|
14287
15411
|
registry.register(agentRegistration);
|
|
14288
15412
|
registry.register(databaseConfigRegistration);
|
|
@@ -14296,7 +15420,7 @@ function registerAllBuiltinEntities() {
|
|
|
14296
15420
|
}
|
|
14297
15421
|
|
|
14298
15422
|
// src/router/MessageRouter.ts
|
|
14299
|
-
var
|
|
15423
|
+
var import_core53 = require("@axiom-lattice/core");
|
|
14300
15424
|
var import_crypto11 = require("crypto");
|
|
14301
15425
|
var BindingNotFoundError = class extends Error {
|
|
14302
15426
|
constructor(message) {
|
|
@@ -14466,7 +15590,7 @@ var MessageRouter = class {
|
|
|
14466
15590
|
channel: message.channel,
|
|
14467
15591
|
adapterChannel: adapter.channel
|
|
14468
15592
|
}, "Thread resolved by adapter strategy");
|
|
14469
|
-
const threadStore = (0,
|
|
15593
|
+
const threadStore = (0, import_core53.getStoreLattice)("default", "thread").store;
|
|
14470
15594
|
try {
|
|
14471
15595
|
await threadStore.createThread(
|
|
14472
15596
|
tenantId,
|
|
@@ -14503,7 +15627,7 @@ var MessageRouter = class {
|
|
|
14503
15627
|
}
|
|
14504
15628
|
}
|
|
14505
15629
|
if (!threadId) {
|
|
14506
|
-
const threadStore = (0,
|
|
15630
|
+
const threadStore = (0, import_core53.getStoreLattice)("default", "thread").store;
|
|
14507
15631
|
const newThreadId = (0, import_crypto11.randomUUID)();
|
|
14508
15632
|
console.log({
|
|
14509
15633
|
event: "dispatch:thread:create",
|
|
@@ -14543,7 +15667,7 @@ var MessageRouter = class {
|
|
|
14543
15667
|
senderId: message.sender.id,
|
|
14544
15668
|
contentLength: message.content.text.length
|
|
14545
15669
|
}, "Dispatching to agent");
|
|
14546
|
-
const agent =
|
|
15670
|
+
const agent = import_core53.agentInstanceManager.getAgent({
|
|
14547
15671
|
tenant_id: tenantId,
|
|
14548
15672
|
assistant_id: agentId,
|
|
14549
15673
|
thread_id: threadId,
|
|
@@ -14783,7 +15907,7 @@ function createAuditLoggerMiddleware() {
|
|
|
14783
15907
|
}
|
|
14784
15908
|
|
|
14785
15909
|
// src/index.ts
|
|
14786
|
-
var
|
|
15910
|
+
var import_core58 = require("@axiom-lattice/core");
|
|
14787
15911
|
|
|
14788
15912
|
// src/swagger.ts
|
|
14789
15913
|
var import_swagger = __toESM(require("@fastify/swagger"));
|
|
@@ -14848,7 +15972,7 @@ var configureSwagger = async (app2, customSwaggerConfig, customSwaggerUiConfig)
|
|
|
14848
15972
|
};
|
|
14849
15973
|
|
|
14850
15974
|
// src/services/agent_task_consumer.ts
|
|
14851
|
-
var
|
|
15975
|
+
var import_core54 = require("@axiom-lattice/core");
|
|
14852
15976
|
var handleAgentTask = async (taskRequest, retryCount = 0) => {
|
|
14853
15977
|
const {
|
|
14854
15978
|
assistant_id,
|
|
@@ -14866,17 +15990,17 @@ var handleAgentTask = async (taskRequest, retryCount = 0) => {
|
|
|
14866
15990
|
console.log(
|
|
14867
15991
|
`\u5F00\u59CB\u5904\u7406\u4EFB\u52A1 [assistant_id: ${assistant_id}, thread_id: ${thread_id}]`
|
|
14868
15992
|
);
|
|
14869
|
-
const agent =
|
|
14870
|
-
await agent.addMessage({ input, command, custom_run_config: runConfig },
|
|
15993
|
+
const agent = import_core54.agentInstanceManager.getAgent({ assistant_id, thread_id, tenant_id, workspace_id: runConfig?.workspaceId, project_id: runConfig?.projectId, custom_run_config: runConfig });
|
|
15994
|
+
await agent.addMessage({ input, command, custom_run_config: runConfig }, import_core54.QueueMode.STEER);
|
|
14871
15995
|
if (callback_event) {
|
|
14872
15996
|
agent.subscribeOnce("message:completed", (evt) => {
|
|
14873
|
-
|
|
15997
|
+
import_core54.eventBus.publish(callback_event, {
|
|
14874
15998
|
success: true,
|
|
14875
15999
|
state: evt.state
|
|
14876
16000
|
});
|
|
14877
16001
|
if (main_thread_id && main_tenant_id) {
|
|
14878
16002
|
try {
|
|
14879
|
-
const mainAgent =
|
|
16003
|
+
const mainAgent = import_core54.agentInstanceManager.getAgent({
|
|
14880
16004
|
assistant_id: main_assistant_id ?? assistant_id,
|
|
14881
16005
|
thread_id: main_thread_id,
|
|
14882
16006
|
tenant_id: main_tenant_id,
|
|
@@ -14906,7 +16030,7 @@ ${summary}`
|
|
|
14906
16030
|
}
|
|
14907
16031
|
});
|
|
14908
16032
|
agent.subscribeOnce("message:interrupted", (evt) => {
|
|
14909
|
-
|
|
16033
|
+
import_core54.eventBus.publish(callback_event, {
|
|
14910
16034
|
success: true,
|
|
14911
16035
|
state: evt.state
|
|
14912
16036
|
});
|
|
@@ -14929,7 +16053,7 @@ ${summary}`
|
|
|
14929
16053
|
return handleAgentTask(taskRequest, nextRetryCount);
|
|
14930
16054
|
}
|
|
14931
16055
|
if (callback_event) {
|
|
14932
|
-
|
|
16056
|
+
import_core54.eventBus.publish(callback_event, {
|
|
14933
16057
|
success: false,
|
|
14934
16058
|
error: error instanceof Error ? error.message : String(error)
|
|
14935
16059
|
});
|
|
@@ -14966,7 +16090,7 @@ var _AgentTaskConsumer = class _AgentTaskConsumer {
|
|
|
14966
16090
|
* 初始化事件监听和队列轮询
|
|
14967
16091
|
*/
|
|
14968
16092
|
initialize() {
|
|
14969
|
-
|
|
16093
|
+
import_core54.eventBus.subscribe(import_core54.AGENT_TASK_EVENT, this.trigger_agent_task.bind(this));
|
|
14970
16094
|
this.startPollingQueue();
|
|
14971
16095
|
console.log("Agent\u4EFB\u52A1\u6D88\u8D39\u8005\u5DF2\u542F\u52A8\u5E76\u76D1\u542C\u4EFB\u52A1\u4E8B\u4EF6\u548C\u961F\u5217");
|
|
14972
16096
|
}
|
|
@@ -15085,7 +16209,7 @@ var _AgentTaskConsumer = class _AgentTaskConsumer {
|
|
|
15085
16209
|
handleAgentTask(taskRequest).catch((error) => {
|
|
15086
16210
|
console.error("\u5904\u7406Agent\u4EFB\u52A1\u65F6\u53D1\u751F\u672A\u6355\u83B7\u7684\u9519\u8BEF:", error);
|
|
15087
16211
|
if (taskRequest.callback_event) {
|
|
15088
|
-
|
|
16212
|
+
import_core54.eventBus.publish(taskRequest.callback_event, {
|
|
15089
16213
|
success: false,
|
|
15090
16214
|
error: error instanceof Error ? error.message : String(error)
|
|
15091
16215
|
});
|
|
@@ -15100,8 +16224,8 @@ _AgentTaskConsumer.agent_run_endpoint = "http://localhost:4001/api/runs";
|
|
|
15100
16224
|
var AgentTaskConsumer = _AgentTaskConsumer;
|
|
15101
16225
|
|
|
15102
16226
|
// src/index.ts
|
|
15103
|
-
var
|
|
15104
|
-
var
|
|
16227
|
+
var import_core59 = require("@axiom-lattice/core");
|
|
16228
|
+
var import_protocols9 = require("@axiom-lattice/protocols");
|
|
15105
16229
|
var import_meta = {};
|
|
15106
16230
|
process.on("unhandledRejection", (reason, promise) => {
|
|
15107
16231
|
console.error("\u672A\u5904\u7406\u7684Promise\u62D2\u7EDD:", reason);
|
|
@@ -15109,18 +16233,18 @@ process.on("unhandledRejection", (reason, promise) => {
|
|
|
15109
16233
|
var DEFAULT_LOGGER_CONFIG = {
|
|
15110
16234
|
name: "default",
|
|
15111
16235
|
description: "Default logger for lattice-gateway service",
|
|
15112
|
-
type:
|
|
16236
|
+
type: import_protocols9.LoggerType.PINO,
|
|
15113
16237
|
serviceName: "lattice/gateway",
|
|
15114
16238
|
loggerName: "lattice/gateway"
|
|
15115
16239
|
};
|
|
15116
16240
|
var loggerLattice = initializeLogger(DEFAULT_LOGGER_CONFIG);
|
|
15117
16241
|
var logger5 = loggerLattice.client;
|
|
15118
16242
|
function initializeLogger(config) {
|
|
15119
|
-
if (
|
|
15120
|
-
|
|
16243
|
+
if (import_core59.loggerLatticeManager.hasLattice("default")) {
|
|
16244
|
+
import_core59.loggerLatticeManager.removeLattice("default");
|
|
15121
16245
|
}
|
|
15122
|
-
(0,
|
|
15123
|
-
return (0,
|
|
16246
|
+
(0, import_core59.registerLoggerLattice)("default", config);
|
|
16247
|
+
return (0, import_core59.getLoggerLattice)("default");
|
|
15124
16248
|
}
|
|
15125
16249
|
var app = (0, import_fastify.default)({
|
|
15126
16250
|
logger: false,
|
|
@@ -15213,7 +16337,7 @@ app.setErrorHandler((error, request, reply) => {
|
|
|
15213
16337
|
});
|
|
15214
16338
|
function getConfiguredSandboxProvider() {
|
|
15215
16339
|
const sandboxProviderType = process.env.SANDBOX_PROVIDER_TYPE || "microsandbox-remote";
|
|
15216
|
-
return (0,
|
|
16340
|
+
return (0, import_core59.createSandboxProvider)({
|
|
15217
16341
|
type: sandboxProviderType,
|
|
15218
16342
|
remoteBaseURL: process.env.SANDBOX_BASE_URL,
|
|
15219
16343
|
microsandboxServiceBaseURL: process.env.MICROSANDBOX_SERVICE_BASE_URL,
|
|
@@ -15229,7 +16353,7 @@ function getConfiguredSandboxProvider() {
|
|
|
15229
16353
|
}
|
|
15230
16354
|
async function restoreMcpConnections() {
|
|
15231
16355
|
try {
|
|
15232
|
-
const storeLattice = (0,
|
|
16356
|
+
const storeLattice = (0, import_core59.getStoreLattice)("default", "mcp");
|
|
15233
16357
|
const store2 = storeLattice.store;
|
|
15234
16358
|
if (!store2) {
|
|
15235
16359
|
logger5.info("MCP store not configured, skipping connection restoration");
|
|
@@ -15243,15 +16367,15 @@ async function restoreMcpConnections() {
|
|
|
15243
16367
|
for (const config of connectedConfigs) {
|
|
15244
16368
|
try {
|
|
15245
16369
|
const connection = convertToConnection2(config.config);
|
|
15246
|
-
|
|
16370
|
+
import_core59.mcpManager.addServer(config.key, connection);
|
|
15247
16371
|
} catch (err) {
|
|
15248
16372
|
logger5.warn(`Failed to prepare MCP server "${config.key}" for restoration`, { error: err });
|
|
15249
16373
|
}
|
|
15250
16374
|
}
|
|
15251
|
-
await
|
|
16375
|
+
await import_core59.mcpManager.connect();
|
|
15252
16376
|
for (const config of connectedConfigs) {
|
|
15253
16377
|
try {
|
|
15254
|
-
await
|
|
16378
|
+
await import_core59.mcpManager.registerToolsToToolLattice(config.key, config.selectedTools);
|
|
15255
16379
|
logger5.info(`MCP server "${config.key}" restored with ${config.selectedTools.length} tools`);
|
|
15256
16380
|
} catch (err) {
|
|
15257
16381
|
logger5.warn(`Failed to register tools for MCP server "${config.key}"`, { error: err });
|
|
@@ -15284,7 +16408,7 @@ var start = async (config) => {
|
|
|
15284
16408
|
const { getStoreLattice: getStore12 } = await import("@axiom-lattice/core");
|
|
15285
16409
|
const bindingStore = getStore12("default", "channelBinding").store;
|
|
15286
16410
|
const installationStore = getStore12("default", "channelInstallation").store;
|
|
15287
|
-
(0,
|
|
16411
|
+
(0, import_core58.setBindingRegistry)(bindingStore);
|
|
15288
16412
|
const router = new MessageRouter({
|
|
15289
16413
|
middlewares: [
|
|
15290
16414
|
createDeduplicationMiddleware(),
|
|
@@ -15301,10 +16425,10 @@ var start = async (config) => {
|
|
|
15301
16425
|
error: err instanceof Error ? err.message : String(err)
|
|
15302
16426
|
});
|
|
15303
16427
|
}
|
|
15304
|
-
(0,
|
|
16428
|
+
(0, import_core58.setEvalRunService)(evalRunner);
|
|
15305
16429
|
try {
|
|
15306
|
-
const menuStore = (0,
|
|
15307
|
-
(0,
|
|
16430
|
+
const menuStore = (0, import_core59.getStoreLattice)("default", "menu").store;
|
|
16431
|
+
(0, import_core58.setMenuRegistry)(menuStore);
|
|
15308
16432
|
logger5.info("Menu registry initialized");
|
|
15309
16433
|
} catch {
|
|
15310
16434
|
}
|
|
@@ -15317,10 +16441,10 @@ var start = async (config) => {
|
|
|
15317
16441
|
const { A2AAuthService: A2AAuthService2, parseEnvKeys: parseEnvKeys2 } = await Promise.resolve().then(() => (init_A2AAuthService(), A2AAuthService_exports));
|
|
15318
16442
|
const { registerA2AStandardRoutes: registerA2AStandardRoutes2 } = await Promise.resolve().then(() => (init_a2a_standard(), a2a_standard_exports));
|
|
15319
16443
|
const a2a = await Promise.resolve().then(() => (init_a2a(), a2a_exports));
|
|
15320
|
-
const a2aKeyStore = (0,
|
|
15321
|
-
const assistantStore = (0,
|
|
15322
|
-
const projectStore = (0,
|
|
15323
|
-
const taskStore = (0,
|
|
16444
|
+
const a2aKeyStore = (0, import_core59.getStoreLattice)("default", "a2aApiKey").store;
|
|
16445
|
+
const assistantStore = (0, import_core59.getStoreLattice)("default", "assistant").store;
|
|
16446
|
+
const projectStore = (0, import_core59.getStoreLattice)("default", "project").store;
|
|
16447
|
+
const taskStore = (0, import_core59.getStoreLattice)("default", "task").store;
|
|
15324
16448
|
const authService = new A2AAuthService2({
|
|
15325
16449
|
keyStore: a2aKeyStore,
|
|
15326
16450
|
assistantStore,
|
|
@@ -15344,17 +16468,17 @@ var start = async (config) => {
|
|
|
15344
16468
|
error: err instanceof Error ? err.message : String(err)
|
|
15345
16469
|
});
|
|
15346
16470
|
}
|
|
15347
|
-
if (!
|
|
15348
|
-
|
|
16471
|
+
if (!import_core59.sandboxLatticeManager.hasLattice("default")) {
|
|
16472
|
+
import_core59.sandboxLatticeManager.registerLattice("default", getConfiguredSandboxProvider());
|
|
15349
16473
|
logger5.info("Registered sandbox manager from env configuration");
|
|
15350
16474
|
}
|
|
15351
16475
|
try {
|
|
15352
16476
|
const { ResourceController: ResourceController2 } = await Promise.resolve().then(() => (init_resources(), resources_exports));
|
|
15353
|
-
const sharedResourceStore = (0,
|
|
15354
|
-
const cache = new
|
|
16477
|
+
const sharedResourceStore = (0, import_core59.getStoreLattice)("default", "sharedResource").store;
|
|
16478
|
+
const cache = new import_core59.TokenCache();
|
|
15355
16479
|
const resourceController = new ResourceController2({
|
|
15356
16480
|
store: sharedResourceStore,
|
|
15357
|
-
sandboxManager:
|
|
16481
|
+
sandboxManager: import_core59.sandboxLatticeManager,
|
|
15358
16482
|
cache,
|
|
15359
16483
|
logger: logger5
|
|
15360
16484
|
});
|
|
@@ -15390,7 +16514,7 @@ var start = async (config) => {
|
|
|
15390
16514
|
}
|
|
15391
16515
|
}
|
|
15392
16516
|
if (process.env.AXIOM_RESTORE_PENDING_ON_STARTUP === "true") {
|
|
15393
|
-
|
|
16517
|
+
import_core59.agentInstanceManager.restore().then((stats) => {
|
|
15394
16518
|
logger5.info(`Agent recovery complete: ${stats.restored} threads restored, ${stats.errors} errors`);
|
|
15395
16519
|
}).catch((error) => {
|
|
15396
16520
|
logger5.error("Agent recovery failed", { error });
|