@digimuza/system-core 0.2.0 → 0.3.0-rc.1
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/INTEGRATION.md +209 -3
- package/README.md +23 -0
- package/dist/ai/api.d.ts +120 -0
- package/dist/ai/api.d.ts.map +1 -0
- package/dist/ai/api.js +446 -0
- package/dist/ai/api.js.map +1 -0
- package/dist/ai/card.d.ts +27 -0
- package/dist/ai/card.d.ts.map +1 -0
- package/dist/ai/card.js +84 -0
- package/dist/ai/card.js.map +1 -0
- package/dist/ai/contract.d.ts +364 -0
- package/dist/ai/contract.d.ts.map +1 -0
- package/dist/ai/contract.js +101 -0
- package/dist/ai/contract.js.map +1 -0
- package/dist/ai/display.d.ts +20 -0
- package/dist/ai/display.d.ts.map +1 -0
- package/dist/ai/display.js +35 -0
- package/dist/ai/display.js.map +1 -0
- package/dist/ai/index.d.ts +7 -0
- package/dist/ai/index.d.ts.map +1 -0
- package/dist/ai/index.js +10 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/ai/manifest.d.ts +25 -0
- package/dist/ai/manifest.d.ts.map +1 -0
- package/dist/ai/manifest.js +15 -0
- package/dist/ai/manifest.js.map +1 -0
- package/dist/ai/runner.d.ts +71 -0
- package/dist/ai/runner.d.ts.map +1 -0
- package/dist/ai/runner.js +246 -0
- package/dist/ai/runner.js.map +1 -0
- package/dist/ai/tools.d.ts +42 -0
- package/dist/ai/tools.d.ts.map +1 -0
- package/dist/ai/tools.js +117 -0
- package/dist/ai/tools.js.map +1 -0
- package/dist/audit/csv.d.ts +31 -0
- package/dist/audit/csv.d.ts.map +1 -0
- package/dist/audit/csv.js +39 -0
- package/dist/audit/csv.js.map +1 -0
- package/dist/audit/index.d.ts +5 -0
- package/dist/audit/index.d.ts.map +1 -0
- package/dist/audit/index.js +6 -0
- package/dist/audit/index.js.map +1 -0
- package/dist/convex/functions.d.ts +39 -0
- package/dist/convex/functions.d.ts.map +1 -1
- package/dist/convex/functions.js +38 -6
- package/dist/convex/functions.js.map +1 -1
- package/dist/convex/index.d.ts +1 -1
- package/dist/convex/index.d.ts.map +1 -1
- package/dist/convex/index.js.map +1 -1
- package/dist/core/access.d.ts +14 -0
- package/dist/core/access.d.ts.map +1 -1
- package/dist/core/access.js +62 -0
- package/dist/core/access.js.map +1 -1
- package/dist/core/audit-module.d.ts +5 -0
- package/dist/core/audit-module.d.ts.map +1 -0
- package/dist/core/audit-module.js +25 -0
- package/dist/core/audit-module.js.map +1 -0
- package/dist/core/audit.d.ts +31 -0
- package/dist/core/audit.d.ts.map +1 -1
- package/dist/core/audit.js +46 -2
- package/dist/core/audit.js.map +1 -1
- package/dist/core/catalog.d.ts +41 -0
- package/dist/core/catalog.d.ts.map +1 -1
- package/dist/core/catalog.js +45 -0
- package/dist/core/catalog.js.map +1 -1
- package/dist/core/context.d.ts +8 -2
- package/dist/core/context.d.ts.map +1 -1
- package/dist/core/context.js +4 -1
- package/dist/core/context.js.map +1 -1
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +2 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/notify.d.ts +37 -0
- package/dist/core/notify.d.ts.map +1 -0
- package/dist/core/notify.js +16 -0
- package/dist/core/notify.js.map +1 -0
- package/dist/core/roles.d.ts.map +1 -1
- package/dist/core/roles.js +5 -1
- package/dist/core/roles.js.map +1 -1
- package/dist/core/system-modules.d.ts +4 -0
- package/dist/core/system-modules.d.ts.map +1 -1
- package/dist/core/system-modules.js +11 -0
- package/dist/core/system-modules.js.map +1 -1
- package/dist/finance/ai-tools.d.ts +55 -0
- package/dist/finance/ai-tools.d.ts.map +1 -0
- package/dist/finance/ai-tools.js +65 -0
- package/dist/finance/ai-tools.js.map +1 -0
- package/dist/finance/index.d.ts +87 -1
- package/dist/finance/index.d.ts.map +1 -1
- package/dist/finance/index.js +92 -38
- package/dist/finance/index.js.map +1 -1
- package/dist/finance/search.d.ts +15 -0
- package/dist/finance/search.d.ts.map +1 -0
- package/dist/finance/search.js +51 -0
- package/dist/finance/search.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/modules/index.d.ts +1 -1
- package/dist/modules/index.d.ts.map +1 -1
- package/dist/modules/index.js +1 -1
- package/dist/modules/index.js.map +1 -1
- package/dist/notifications/api.d.ts +130 -0
- package/dist/notifications/api.d.ts.map +1 -0
- package/dist/notifications/api.js +384 -0
- package/dist/notifications/api.js.map +1 -0
- package/dist/notifications/contract.d.ts +132 -0
- package/dist/notifications/contract.d.ts.map +1 -0
- package/dist/notifications/contract.js +59 -0
- package/dist/notifications/contract.js.map +1 -0
- package/dist/notifications/index.d.ts +6 -0
- package/dist/notifications/index.d.ts.map +1 -0
- package/dist/notifications/index.js +9 -0
- package/dist/notifications/index.js.map +1 -0
- package/dist/search/index.d.ts +95 -0
- package/dist/search/index.d.ts.map +1 -0
- package/dist/search/index.js +151 -0
- package/dist/search/index.js.map +1 -0
- package/package.json +41 -2
- package/src/ai/api.ts +535 -0
- package/src/ai/card.ts +91 -0
- package/src/ai/contract.ts +245 -0
- package/src/ai/display.ts +38 -0
- package/src/ai/index.ts +9 -0
- package/src/ai/manifest.ts +16 -0
- package/src/ai/runner.ts +275 -0
- package/src/ai/tools.ts +128 -0
- package/src/audit/csv.ts +60 -0
- package/src/audit/index.ts +7 -0
- package/src/convex/functions.ts +54 -7
- package/src/convex/index.ts +1 -1
- package/src/core/access.ts +55 -1
- package/src/core/audit-module.ts +26 -0
- package/src/core/audit.ts +68 -2
- package/src/core/catalog.ts +63 -0
- package/src/core/context.ts +13 -2
- package/src/core/index.ts +2 -0
- package/src/core/notify.ts +52 -0
- package/src/core/roles.ts +5 -1
- package/src/core/system-modules.ts +12 -0
- package/src/finance/ai-tools.ts +68 -0
- package/src/finance/index.ts +119 -42
- package/src/finance/search.ts +58 -0
- package/src/index.ts +2 -0
- package/src/modules/index.ts +3 -0
- package/src/notifications/api.ts +477 -0
- package/src/notifications/contract.ts +142 -0
- package/src/notifications/index.ts +8 -0
- package/src/search/index.ts +198 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/ai/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAqB,MAAM,qBAAqB,CAAA;AAErE;;;;GAIG;AACH,MAAM,CAAC,MAAM,EAAE,GAAG,YAAY,CAAC;IAC7B,EAAE,EAAE,IAAI;IACR,IAAI,EAAE,UAAU;IAChB,WAAW,EAAE,4BAA4B;IACzC,WAAW,EAAE,CAAC,EAAE,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,mBAAmB,EAAE,WAAW,EAAE,mDAAmD,EAAE,CAAC;IACjI,eAAe,EAAE,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,sBAAsB,EAAE,WAAW,EAAE,+BAA+B,EAAE,WAAW,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC;CAC/I,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG,EAAE,GAAG,EAAE,aAAa,EAA6D,CAAA"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { AiOptions, MessageView } from "./contract.js";
|
|
2
|
+
/** Max model steps (calls) per run turn; guards against tool loops. */
|
|
3
|
+
export declare const AI_MAX_STEPS = 8;
|
|
4
|
+
/** Text deltas are written with `_appendText` at most this often … */
|
|
5
|
+
export declare const AI_FLUSH_MS = 150;
|
|
6
|
+
/** … or as soon as this many characters are buffered. */
|
|
7
|
+
export declare const AI_FLUSH_CHARS = 200;
|
|
8
|
+
export type AiLanguageModel = any;
|
|
9
|
+
export interface AiRunnerOptions extends AiOptions {
|
|
10
|
+
/** OpenAI API key; default `process.env.OPENAI_API_KEY`. Never logged or returned. */
|
|
11
|
+
openAiApiKey?: () => string | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Base URL of an OpenAI-compatible API, e.g. a proxy like 9Router
|
|
14
|
+
* (`https://router.digimuza.ai/v1`). Default `process.env.OPENAI_BASE_URL`,
|
|
15
|
+
* else api.openai.com. Requests go to `<baseURL>/chat/completions`.
|
|
16
|
+
*/
|
|
17
|
+
baseURL?: string;
|
|
18
|
+
/** Custom fetch for the provider (tests, request middleware). Default: global fetch. */
|
|
19
|
+
fetch?: typeof globalThis.fetch;
|
|
20
|
+
/**
|
|
21
|
+
* Test hook: builds the language model for a model id instead of OpenAI (e.g.
|
|
22
|
+
* `MockLanguageModelV4` from `ai/test`). When set, no API key is needed.
|
|
23
|
+
*/
|
|
24
|
+
model?: (modelId: string) => AiLanguageModel;
|
|
25
|
+
}
|
|
26
|
+
type ModelMessage = {
|
|
27
|
+
role: "user";
|
|
28
|
+
content: string;
|
|
29
|
+
} | {
|
|
30
|
+
role: "assistant";
|
|
31
|
+
content: Array<{
|
|
32
|
+
type: "text";
|
|
33
|
+
text: string;
|
|
34
|
+
} | {
|
|
35
|
+
type: "tool-call";
|
|
36
|
+
toolCallId: string;
|
|
37
|
+
toolName: string;
|
|
38
|
+
input: unknown;
|
|
39
|
+
}>;
|
|
40
|
+
} | {
|
|
41
|
+
role: "tool";
|
|
42
|
+
content: Array<{
|
|
43
|
+
type: "tool-result";
|
|
44
|
+
toolCallId: string;
|
|
45
|
+
toolName: string;
|
|
46
|
+
output: {
|
|
47
|
+
type: "json";
|
|
48
|
+
value: unknown;
|
|
49
|
+
};
|
|
50
|
+
}>;
|
|
51
|
+
};
|
|
52
|
+
/** OpenAI function names allow only `[a-zA-Z0-9_-]`; tool ids are `module.verb_object`. */
|
|
53
|
+
export declare const modelToolName: (toolId: string) => string;
|
|
54
|
+
/**
|
|
55
|
+
* History → model messages. User text → user messages; assistant parts in order as
|
|
56
|
+
* text + tool calls, each finished call (done/error/rejected) followed by its result.
|
|
57
|
+
* Calls without an outcome yet are left out (a call without a result is invalid).
|
|
58
|
+
*/
|
|
59
|
+
export declare function toModelMessages(history: readonly MessageView[]): ModelMessage[];
|
|
60
|
+
/**
|
|
61
|
+
* // convex/aiRunner.ts
|
|
62
|
+
* "use node"
|
|
63
|
+
* export const { runAgent } = defineAiRunner(aiOptions)
|
|
64
|
+
*/
|
|
65
|
+
export declare function defineAiRunner(options: AiRunnerOptions): {
|
|
66
|
+
runAgent: import("convex/server").RegisteredAction<"internal", {
|
|
67
|
+
runId: import("convex/values").GenericId<"aiRuns">;
|
|
68
|
+
}, Promise<void>>;
|
|
69
|
+
};
|
|
70
|
+
export {};
|
|
71
|
+
//# sourceMappingURL=runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/ai/runner.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,SAAS,EAAgC,WAAW,EAA6D,MAAM,eAAe,CAAA;AAEpJ,uEAAuE;AACvE,eAAO,MAAM,YAAY,IAAI,CAAA;AAC7B,sEAAsE;AACtE,eAAO,MAAM,WAAW,MAAM,CAAA;AAC9B,yDAAyD;AACzD,eAAO,MAAM,cAAc,MAAM,CAAA;AAKjC,MAAM,MAAM,eAAe,GAAG,GAAG,CAAA;AAEjC,MAAM,WAAW,eAAgB,SAAQ,SAAS;IAChD,sFAAsF;IACtF,YAAY,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,CAAA;IACvC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,wFAAwF;IACxF,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;IAC/B;;;OAGG;IACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,eAAe,CAAA;CAC7C;AAGD,KAAK,YAAY,GACb;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,WAAW,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;CAAE,GACnJ;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,aAAa,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,OAAO,CAAA;SAAE,CAAA;KAAE,CAAC,CAAA;CAAE,CAAA;AAO7I,2FAA2F;AAC3F,eAAO,MAAM,aAAa,WAAY,MAAM,WAA2C,CAAA;AAKvF;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,GAAG,YAAY,EAAE,CA8B/E;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,eAAe;;;;EA6KtD"}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
// Node-only Mastra runner of the AI module (DIG-71). Entry `@digimuza/system-core/ai/runner`.
|
|
2
|
+
// All Mastra / AI SDK imports live here, never in contract/api/tools: the app file that
|
|
3
|
+
// re-exports `runAgent` must start with "use node".
|
|
4
|
+
import { createOpenAI } from "@ai-sdk/openai";
|
|
5
|
+
import { Agent } from "@mastra/core/agent";
|
|
6
|
+
import { noopLogger } from "@mastra/core/logger";
|
|
7
|
+
import { createTool } from "@mastra/core/tools";
|
|
8
|
+
import { internalActionGeneric, makeFunctionReference } from "convex/server";
|
|
9
|
+
import { v } from "convex/values";
|
|
10
|
+
import { createLogger } from "../diagnostics/logging.js";
|
|
11
|
+
/** Max model steps (calls) per run turn; guards against tool loops. */
|
|
12
|
+
export const AI_MAX_STEPS = 8;
|
|
13
|
+
/** Text deltas are written with `_appendText` at most this often … */
|
|
14
|
+
export const AI_FLUSH_MS = 150;
|
|
15
|
+
/** … or as soon as this many characters are buffered. */
|
|
16
|
+
export const AI_FLUSH_CHARS = 200;
|
|
17
|
+
class StopTurn extends Error {
|
|
18
|
+
}
|
|
19
|
+
const UNAVAILABLE = "UNAVAILABLE";
|
|
20
|
+
const FAILED = { code: UNAVAILABLE, message: "The assistant failed. Try again." };
|
|
21
|
+
/** OpenAI function names allow only `[a-zA-Z0-9_-]`; tool ids are `module.verb_object`. */
|
|
22
|
+
export const modelToolName = (toolId) => toolId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
23
|
+
const toolOutput = (p) => p.state === "done" ? { ok: true, result: p.result ?? null } : p.state === "rejected" ? { ok: false, error: "REJECTED", message: "rejected by user" } : { ok: false, error: p.error ?? UNAVAILABLE };
|
|
24
|
+
/**
|
|
25
|
+
* History → model messages. User text → user messages; assistant parts in order as
|
|
26
|
+
* text + tool calls, each finished call (done/error/rejected) followed by its result.
|
|
27
|
+
* Calls without an outcome yet are left out (a call without a result is invalid).
|
|
28
|
+
*/
|
|
29
|
+
export function toModelMessages(history) {
|
|
30
|
+
const out = [];
|
|
31
|
+
for (const m of history) {
|
|
32
|
+
if (m.role === "user") {
|
|
33
|
+
const text = m.parts.flatMap((p) => (p.type === "text" ? [p.text] : [])).join("\n");
|
|
34
|
+
if (text.trim())
|
|
35
|
+
out.push({ role: "user", content: text });
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
let assistant = [];
|
|
39
|
+
let results = [];
|
|
40
|
+
const flush = () => {
|
|
41
|
+
if (assistant.length)
|
|
42
|
+
out.push({ role: "assistant", content: assistant });
|
|
43
|
+
if (results.length)
|
|
44
|
+
out.push({ role: "tool", content: results });
|
|
45
|
+
assistant = [];
|
|
46
|
+
results = [];
|
|
47
|
+
};
|
|
48
|
+
for (const p of m.parts) {
|
|
49
|
+
if (p.type === "text") {
|
|
50
|
+
if (!p.text)
|
|
51
|
+
continue;
|
|
52
|
+
if (results.length)
|
|
53
|
+
flush();
|
|
54
|
+
assistant.push({ type: "text", text: p.text });
|
|
55
|
+
}
|
|
56
|
+
else if (p.state === "done" || p.state === "error" || p.state === "rejected") {
|
|
57
|
+
const toolName = modelToolName(p.toolName);
|
|
58
|
+
assistant.push({ type: "tool-call", toolCallId: p.toolCallId, toolName, input: p.args ?? {} });
|
|
59
|
+
results.push({ type: "tool-result", toolCallId: p.toolCallId, toolName, output: { type: "json", value: toolOutput(p) } });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
flush();
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* // convex/aiRunner.ts
|
|
68
|
+
* "use node"
|
|
69
|
+
* export const { runAgent } = defineAiRunner(aiOptions)
|
|
70
|
+
*/
|
|
71
|
+
export function defineAiRunner(options) {
|
|
72
|
+
const fnModule = options.functionsModule ?? "ai";
|
|
73
|
+
const mutation = (name) => makeFunctionReference(`${fnModule}:${name}`);
|
|
74
|
+
const query = (name) => makeFunctionReference(`${fnModule}:${name}`);
|
|
75
|
+
const fn = {
|
|
76
|
+
startRun: mutation("_startRun"),
|
|
77
|
+
appendText: mutation("_appendText"),
|
|
78
|
+
addToolCall: mutation("_addToolCall"),
|
|
79
|
+
executeTool: mutation("_executeTool"),
|
|
80
|
+
setToolCallState: mutation("_setToolCallState"),
|
|
81
|
+
finishRun: mutation("_finishRun"),
|
|
82
|
+
isStopRequested: query("_isStopRequested"),
|
|
83
|
+
};
|
|
84
|
+
const logger = createLogger({ moduleId: "ai.runner" });
|
|
85
|
+
async function run(ctx, runId) {
|
|
86
|
+
const rc = (await ctx.runMutation(fn.startRun, { runId }));
|
|
87
|
+
if (!rc)
|
|
88
|
+
return;
|
|
89
|
+
let finished = null;
|
|
90
|
+
const finish = async (status, error) => {
|
|
91
|
+
if (finished)
|
|
92
|
+
return;
|
|
93
|
+
finished = status;
|
|
94
|
+
await ctx.runMutation(fn.finishRun, error ? { runId, status, error } : { runId, status });
|
|
95
|
+
};
|
|
96
|
+
try {
|
|
97
|
+
const agentDef = options.agents.find((a) => a.id === rc.agentId);
|
|
98
|
+
if (!agentDef)
|
|
99
|
+
return await finish("error", { code: UNAVAILABLE, message: "Unknown assistant" });
|
|
100
|
+
let modelFor = options.model;
|
|
101
|
+
if (!modelFor) {
|
|
102
|
+
const apiKey = options.openAiApiKey?.() ?? process.env.OPENAI_API_KEY;
|
|
103
|
+
if (!apiKey)
|
|
104
|
+
return await finish("error", { code: UNAVAILABLE, message: "AI model is not configured" });
|
|
105
|
+
const baseURL = options.baseURL ?? (process.env.OPENAI_BASE_URL || undefined);
|
|
106
|
+
const openai = createOpenAI({ apiKey, ...(baseURL ? { baseURL } : {}), ...(options.fetch ? { fetch: options.fetch } : {}) });
|
|
107
|
+
// Chat Completions, not the Responses API default: OpenAI-compatible
|
|
108
|
+
// proxies (9Router, LiteLLM, …) implement /v1/chat/completions. Model ids
|
|
109
|
+
// are plain strings (router names like `cx/gpt-5` are not in the SDK's union).
|
|
110
|
+
modelFor = (id) => openai.chat(id);
|
|
111
|
+
}
|
|
112
|
+
const messageId = rc.messageId;
|
|
113
|
+
const executeTool = async (toolCallId) => {
|
|
114
|
+
let res;
|
|
115
|
+
try {
|
|
116
|
+
res = (await ctx.runMutation(fn.executeTool, { runId, toolCallId }));
|
|
117
|
+
}
|
|
118
|
+
catch (e) {
|
|
119
|
+
logger.warn("ai_tool_execute_failed", { errorName: e instanceof Error ? e.name : typeof e });
|
|
120
|
+
res = { ok: false, code: UNAVAILABLE, message: "Tool failed" };
|
|
121
|
+
}
|
|
122
|
+
await ctx.runMutation(fn.setToolCallState, res.ok ? { messageId, toolCallId, state: "done", result: res.result ?? null } : { messageId, toolCallId, state: "error", error: res.code });
|
|
123
|
+
return res;
|
|
124
|
+
};
|
|
125
|
+
// 5. Resume: run the tools the user approved since the last turn.
|
|
126
|
+
const history = rc.history.map((m) => (m.id === messageId ? { ...m, parts: m.parts.map((p) => ({ ...p })) } : m));
|
|
127
|
+
const current = history.find((m) => m.id === messageId);
|
|
128
|
+
for (const p of current?.parts ?? []) {
|
|
129
|
+
if (p.type !== "tool-call" || p.state !== "approved")
|
|
130
|
+
continue;
|
|
131
|
+
await ctx.runMutation(fn.setToolCallState, { messageId, toolCallId: p.toolCallId, state: "running" });
|
|
132
|
+
const res = await executeTool(p.toolCallId);
|
|
133
|
+
Object.assign(p, res.ok ? { state: "done", result: res.result ?? null } : { state: "error", error: res.code });
|
|
134
|
+
}
|
|
135
|
+
// Still waiting on the user for another call: nothing to do until they answer.
|
|
136
|
+
if (current?.parts.some((p) => p.type === "tool-call" && p.state === "awaiting-approval"))
|
|
137
|
+
return await finish("waiting-approval");
|
|
138
|
+
// 7. Text batching + stop checks.
|
|
139
|
+
const controller = new AbortController();
|
|
140
|
+
let buffer = "";
|
|
141
|
+
let lastFlush = Date.now();
|
|
142
|
+
let chain = Promise.resolve();
|
|
143
|
+
const flush = () => (chain = chain.then(async () => {
|
|
144
|
+
lastFlush = Date.now();
|
|
145
|
+
if (buffer && !finished) {
|
|
146
|
+
const text = buffer;
|
|
147
|
+
buffer = "";
|
|
148
|
+
await ctx.runMutation(fn.appendText, { messageId, text });
|
|
149
|
+
}
|
|
150
|
+
if (!finished && (await ctx.runQuery(fn.isStopRequested, { runId }))) {
|
|
151
|
+
controller.abort();
|
|
152
|
+
await finish("stopped");
|
|
153
|
+
}
|
|
154
|
+
}));
|
|
155
|
+
// 4. Tools: only those the actor may use right now.
|
|
156
|
+
let awaitingApproval = false;
|
|
157
|
+
const allowed = new Set(rc.allowedToolIds);
|
|
158
|
+
const tools = {};
|
|
159
|
+
for (const tool of agentDef.tools) {
|
|
160
|
+
if (!allowed.has(tool.id))
|
|
161
|
+
continue;
|
|
162
|
+
const name = modelToolName(tool.id);
|
|
163
|
+
tools[name] = createTool({
|
|
164
|
+
id: name,
|
|
165
|
+
description: tool.description,
|
|
166
|
+
inputSchema: tool.input,
|
|
167
|
+
execute: async (input, toolCtx) => {
|
|
168
|
+
const toolCallId = toolCtx?.agent?.toolCallId || `call_${crypto.randomUUID()}`;
|
|
169
|
+
await flush();
|
|
170
|
+
if (finished)
|
|
171
|
+
throw new StopTurn("stopped");
|
|
172
|
+
const args = input ?? {};
|
|
173
|
+
if (tool.requiresApproval) {
|
|
174
|
+
awaitingApproval = true;
|
|
175
|
+
await ctx.runMutation(fn.addToolCall, { messageId, toolCallId, toolName: tool.id, args, state: "awaiting-approval" });
|
|
176
|
+
return { ok: false, status: "awaiting-approval", message: "The user must approve this action first. Stop and wait for their answer." };
|
|
177
|
+
}
|
|
178
|
+
await ctx.runMutation(fn.addToolCall, { messageId, toolCallId, toolName: tool.id, args, state: "running" });
|
|
179
|
+
const res = await executeTool(toolCallId);
|
|
180
|
+
return res.ok ? { ok: true, result: res.result ?? null } : { ok: false, error: res.code, message: res.message };
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
const agent = new Agent({
|
|
185
|
+
id: agentDef.id,
|
|
186
|
+
name: agentDef.name,
|
|
187
|
+
instructions: agentDef.instructions,
|
|
188
|
+
model: modelFor(rc.model),
|
|
189
|
+
tools,
|
|
190
|
+
});
|
|
191
|
+
// Mastra logs stream errors with the full error object; upstream API errors carry the
|
|
192
|
+
// request body (prompts, tool results). Silence it; we log only the error name.
|
|
193
|
+
agent.__setLogger(noopLogger);
|
|
194
|
+
const messages = toModelMessages(history);
|
|
195
|
+
// An approval call ends the turn after its step (other calls of the same step still
|
|
196
|
+
// finish and are recorded); the step cap stops tool loops.
|
|
197
|
+
const stopWhen = [({ steps }) => steps.length >= AI_MAX_STEPS, () => awaitingApproval];
|
|
198
|
+
try {
|
|
199
|
+
const output = await agent.stream(messages, {
|
|
200
|
+
maxSteps: AI_MAX_STEPS,
|
|
201
|
+
stopWhen: stopWhen,
|
|
202
|
+
abortSignal: controller.signal,
|
|
203
|
+
});
|
|
204
|
+
const reader = output.fullStream.getReader();
|
|
205
|
+
for (;;) {
|
|
206
|
+
const { done, value } = await reader.read();
|
|
207
|
+
if (done || finished)
|
|
208
|
+
break;
|
|
209
|
+
const chunk = value;
|
|
210
|
+
if (chunk.type === "text-delta" && chunk.payload?.text) {
|
|
211
|
+
buffer += chunk.payload.text;
|
|
212
|
+
if (buffer.length >= AI_FLUSH_CHARS || Date.now() - lastFlush >= AI_FLUSH_MS)
|
|
213
|
+
await flush();
|
|
214
|
+
}
|
|
215
|
+
else if (chunk.type === "error") {
|
|
216
|
+
throw chunk.payload?.error instanceof Error ? chunk.payload.error : new Error("stream error");
|
|
217
|
+
}
|
|
218
|
+
if (finished)
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
if (finished)
|
|
222
|
+
await reader.cancel().catch(() => { });
|
|
223
|
+
}
|
|
224
|
+
catch (e) {
|
|
225
|
+
if (!finished)
|
|
226
|
+
throw e;
|
|
227
|
+
}
|
|
228
|
+
await flush();
|
|
229
|
+
await chain;
|
|
230
|
+
// 8. End of turn.
|
|
231
|
+
await finish(awaitingApproval ? "waiting-approval" : "done");
|
|
232
|
+
}
|
|
233
|
+
catch (e) {
|
|
234
|
+
logger.error("ai_run_failed", { errorName: e instanceof Error ? e.name : typeof e });
|
|
235
|
+
await finish("error", FAILED);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
const runAgent = internalActionGeneric({
|
|
239
|
+
args: { runId: v.id("aiRuns") },
|
|
240
|
+
handler: async (ctx, { runId }) => {
|
|
241
|
+
await run(ctx, runId);
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
return { runAgent };
|
|
245
|
+
}
|
|
246
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/ai/runner.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,wFAAwF;AACxF,oDAAoD;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAC/C,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAgD,MAAM,eAAe,CAAA;AAC1H,OAAO,EAAE,CAAC,EAAE,MAAM,eAAe,CAAA;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAGxD,uEAAuE;AACvE,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAA;AAC7B,sEAAsE;AACtE,MAAM,CAAC,MAAM,WAAW,GAAG,GAAG,CAAA;AAC9B,yDAAyD;AACzD,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,CAAA;AA+BjC,MAAM,QAAS,SAAQ,KAAK;CAAG;AAE/B,MAAM,WAAW,GAAG,aAAa,CAAA;AACjC,MAAM,MAAM,GAAG,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,kCAAkC,EAAE,CAAA;AAEjF,2FAA2F;AAC3F,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,MAAc,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAA;AAEvF,MAAM,UAAU,GAAG,CAAC,CAAW,EAAW,EAAE,CAC1C,CAAC,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,IAAI,WAAW,EAAE,CAAA;AAErM;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,OAA+B;IAC7D,MAAM,GAAG,GAAmB,EAAE,CAAA;IAC9B,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YACtB,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YACnF,IAAI,IAAI,CAAC,IAAI,EAAE;gBAAE,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAA;YAC1D,SAAQ;QACV,CAAC;QACD,IAAI,SAAS,GAA4D,EAAE,CAAA;QAC3E,IAAI,OAAO,GAAuD,EAAE,CAAA;QACpE,MAAM,KAAK,GAAG,GAAG,EAAE;YACjB,IAAI,SAAS,CAAC,MAAM;gBAAE,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAA;YACzE,IAAI,OAAO,CAAC,MAAM;gBAAE,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAA;YAChE,SAAS,GAAG,EAAE,CAAA;YACd,OAAO,GAAG,EAAE,CAAA;QACd,CAAC,CAAA;QACD,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;YACxB,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBACtB,IAAI,CAAC,CAAC,CAAC,IAAI;oBAAE,SAAQ;gBACrB,IAAI,OAAO,CAAC,MAAM;oBAAE,KAAK,EAAE,CAAA;gBAC3B,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAA;YAChD,CAAC;iBAAM,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,IAAI,CAAC,CAAC,KAAK,KAAK,OAAO,IAAI,CAAC,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;gBAC/E,MAAM,QAAQ,GAAG,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAA;gBAC1C,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC,CAAA;gBAC9F,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;YAC3H,CAAC;QACH,CAAC;QACD,KAAK,EAAE,CAAA;IACT,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,OAAwB;IACrD,MAAM,QAAQ,GAAG,OAAO,CAAC,eAAe,IAAI,IAAI,CAAA;IAChD,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,qBAAqB,CAAa,GAAG,QAAQ,IAAI,IAAI,EAAE,CAAC,CAAA;IAC3F,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,qBAAqB,CAAU,GAAG,QAAQ,IAAI,IAAI,EAAE,CAAC,CAAA;IACrF,MAAM,EAAE,GAAG;QACT,QAAQ,EAAE,QAAQ,CAAC,WAAW,CAAC;QAC/B,UAAU,EAAE,QAAQ,CAAC,aAAa,CAAC;QACnC,WAAW,EAAE,QAAQ,CAAC,cAAc,CAAC;QACrC,WAAW,EAAE,QAAQ,CAAC,cAAc,CAAC;QACrC,gBAAgB,EAAE,QAAQ,CAAC,mBAAmB,CAAC;QAC/C,SAAS,EAAE,QAAQ,CAAC,YAAY,CAAC;QACjC,eAAe,EAAE,KAAK,CAAC,kBAAkB,CAAC;KAC3C,CAAA;IACD,MAAM,MAAM,GAAG,YAAY,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC,CAAA;IAEtD,KAAK,UAAU,GAAG,CAAC,GAAuC,EAAE,KAAc;QACxE,MAAM,EAAE,GAAG,CAAC,MAAM,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,CAAC,CAAsB,CAAA;QAC/E,IAAI,CAAC,EAAE;YAAE,OAAM;QAEf,IAAI,QAAQ,GAAqB,IAAI,CAAA;QACrC,MAAM,MAAM,GAAG,KAAK,EAAE,MAAiB,EAAE,KAAyC,EAAE,EAAE;YACpF,IAAI,QAAQ;gBAAE,OAAM;YACpB,QAAQ,GAAG,MAAM,CAAA;YACjB,MAAM,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAA;QAC3F,CAAC,CAAA;QAED,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,OAAO,CAAC,CAAA;YAChE,IAAI,CAAC,QAAQ;gBAAE,OAAO,MAAM,MAAM,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,mBAAmB,EAAE,CAAC,CAAA;YAEhG,IAAI,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAA;YAC5B,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,MAAM,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,EAAE,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,CAAA;gBACrE,IAAI,CAAC,MAAM;oBAAE,OAAO,MAAM,MAAM,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,4BAA4B,EAAE,CAAC,CAAA;gBACvG,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,SAAS,CAAC,CAAA;gBAC7E,MAAM,MAAM,GAAG,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAA;gBAC5H,qEAAqE;gBACrE,0EAA0E;gBAC1E,+EAA+E;gBAC/E,QAAQ,GAAG,CAAC,EAAU,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;YAC5C,CAAC;YAED,MAAM,SAAS,GAAG,EAAE,CAAC,SAAS,CAAA;YAC9B,MAAM,WAAW,GAAG,KAAK,EAAE,UAAkB,EAAgC,EAAE;gBAC7E,IAAI,GAAwB,CAAA;gBAC5B,IAAI,CAAC;oBACH,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAwB,CAAA;gBAC7F,CAAC;gBAAC,OAAO,CAAC,EAAE,CAAC;oBACX,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE,EAAE,SAAS,EAAE,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;oBAC5F,GAAG,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,aAAa,EAAE,CAAA;gBAChE,CAAC;gBACD,MAAM,GAAG,CAAC,WAAW,CACnB,EAAE,CAAC,gBAAgB,EACnB,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,CAC3I,CAAA;gBACD,OAAO,GAAG,CAAA;YACZ,CAAC,CAAA;YAED,kEAAkE;YAClE,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;YACjH,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAA;YACvD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,KAAK,IAAI,EAAE,EAAE,CAAC;gBACrC,IAAI,CAAC,CAAC,IAAI,KAAK,WAAW,IAAI,CAAC,CAAC,KAAK,KAAK,UAAU;oBAAE,SAAQ;gBAC9D,MAAM,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC,gBAAgB,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAA;gBACrG,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC,CAAA;gBAC3C,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAuB,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAwB,EAAE,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAA;YAClJ,CAAC;YACD,+EAA+E;YAC/E,IAAI,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,IAAI,CAAC,CAAC,KAAK,KAAK,mBAAmB,CAAC;gBAAE,OAAO,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAA;YAElI,kCAAkC;YAClC,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;YACxC,IAAI,MAAM,GAAG,EAAE,CAAA;YACf,IAAI,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;YAC1B,IAAI,KAAK,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAA;YAC5C,MAAM,KAAK,GAAG,GAAkB,EAAE,CAChC,CAAC,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE;gBAC7B,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;gBACtB,IAAI,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACxB,MAAM,IAAI,GAAG,MAAM,CAAA;oBACnB,MAAM,GAAG,EAAE,CAAA;oBACX,MAAM,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;gBAC3D,CAAC;gBACD,IAAI,CAAC,QAAQ,IAAI,CAAC,MAAM,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,eAAe,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC;oBACrE,UAAU,CAAC,KAAK,EAAE,CAAA;oBAClB,MAAM,MAAM,CAAC,SAAS,CAAC,CAAA;gBACzB,CAAC;YACH,CAAC,CAAC,CAAC,CAAA;YAEL,oDAAoD;YACpD,IAAI,gBAAgB,GAAG,KAAK,CAAA;YAC5B,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,EAAE,CAAC,cAAc,CAAC,CAAA;YAC1C,MAAM,KAAK,GAAkD,EAAE,CAAA;YAC/D,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,KAA0B,EAAE,CAAC;gBACvD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;oBAAE,SAAQ;gBACnC,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;gBACnC,KAAK,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC;oBACvB,EAAE,EAAE,IAAI;oBACR,WAAW,EAAE,IAAI,CAAC,WAAW;oBAC7B,WAAW,EAAE,IAAI,CAAC,KAAK;oBACvB,OAAO,EAAE,KAAK,EAAE,KAAc,EAAE,OAAwD,EAAE,EAAE;wBAC1F,MAAM,UAAU,GAAG,OAAO,EAAE,KAAK,EAAE,UAAU,IAAI,QAAQ,MAAM,CAAC,UAAU,EAAE,EAAE,CAAA;wBAC9E,MAAM,KAAK,EAAE,CAAA;wBACb,IAAI,QAAQ;4BAAE,MAAM,IAAI,QAAQ,CAAC,SAAS,CAAC,CAAA;wBAC3C,MAAM,IAAI,GAAG,KAAK,IAAI,EAAE,CAAA;wBACxB,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;4BAC1B,gBAAgB,GAAG,IAAI,CAAA;4BACvB,MAAM,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,CAAA;4BACrH,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,mBAAmB,EAAE,OAAO,EAAE,0EAA0E,EAAE,CAAA;wBACxI,CAAC;wBACD,MAAM,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAA;wBAC3G,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,UAAU,CAAC,CAAA;wBACzC,OAAO,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAA;oBACjH,CAAC;iBACkC,CAAkC,CAAA;YACzE,CAAC;YAED,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC;gBACtB,EAAE,EAAE,QAAQ,CAAC,EAAE;gBACf,IAAI,EAAE,QAAQ,CAAC,IAAI;gBACnB,YAAY,EAAE,QAAQ,CAAC,YAAY;gBACnC,KAAK,EAAE,QAAQ,CAAC,EAAE,CAAC,KAAK,CAAC;gBACzB,KAAK;aACN,CAAC,CAAA;YACF,sFAAsF;YACtF,gFAAgF;YAChF,KAAK,CAAC,WAAW,CAAC,UAAU,CAAC,CAAA;YAE7B,MAAM,QAAQ,GAAG,eAAe,CAAC,OAAO,CAAC,CAAA;YACzC,oFAAoF;YACpF,2DAA2D;YAC3D,MAAM,QAAQ,GAAG,CAAC,CAAC,EAAE,KAAK,EAAwB,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,IAAI,YAAY,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAAC,CAAA;YAC5G,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,QAAiB,EAAE;oBACnD,QAAQ,EAAE,YAAY;oBACtB,QAAQ,EAAE,QAAiB;oBAC3B,WAAW,EAAE,UAAU,CAAC,MAAM;iBAC/B,CAAC,CAAA;gBACF,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,SAAS,EAAE,CAAA;gBAC5C,SAAS,CAAC;oBACR,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAA;oBAC3C,IAAI,IAAI,IAAI,QAAQ;wBAAE,MAAK;oBAC3B,MAAM,KAAK,GAAG,KAAuE,CAAA;oBACrF,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,IAAI,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC;wBACvD,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAA;wBAC5B,IAAI,MAAM,CAAC,MAAM,IAAI,cAAc,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,IAAI,WAAW;4BAAE,MAAM,KAAK,EAAE,CAAA;oBAC7F,CAAC;yBAAM,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;wBAClC,MAAM,KAAK,CAAC,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,cAAc,CAAC,CAAA;oBAC/F,CAAC;oBACD,IAAI,QAAQ;wBAAE,MAAK;gBACrB,CAAC;gBACD,IAAI,QAAQ;oBAAE,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;YACrD,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,IAAI,CAAC,QAAQ;oBAAE,MAAM,CAAC,CAAA;YACxB,CAAC;YACD,MAAM,KAAK,EAAE,CAAA;YACb,MAAM,KAAK,CAAA;YACX,kBAAkB;YAClB,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;QAC9D,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,CAAC,KAAK,CAAC,eAAe,EAAE,EAAE,SAAS,EAAE,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;YACpF,MAAM,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;QAC/B,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,qBAAqB,CAAC;QACrC,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE;QAC/B,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;YAChC,MAAM,GAAG,CAAC,GAAG,EAAE,KAAgB,CAAC,CAAA;QAClC,CAAC;KACF,CAAC,CAAA;IAEF,OAAO,EAAE,QAAQ,EAAE,CAAA;AACrB,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
import { type ActorRef } from "../core/access.js";
|
|
3
|
+
import type { SystemContext } from "../core/context.js";
|
|
4
|
+
import { type AiAgentDefinition, type AiTool, type ToolExecutionResult } from "./contract.js";
|
|
5
|
+
/** `module.verb_object`, e.g. `finance.list_invoices`. */
|
|
6
|
+
export declare const AI_TOOL_ID: RegExp;
|
|
7
|
+
export declare const AI_AGENT_ID: RegExp;
|
|
8
|
+
/**
|
|
9
|
+
* Declares a tool. Identity at runtime; validates the definition (id format, permission format).
|
|
10
|
+
*
|
|
11
|
+
* Handler rule: check everything (existence, state, uniqueness, limits) BEFORE the
|
|
12
|
+
* first write. A failed handler's earlier writes are committed, because
|
|
13
|
+
* `executeAiTool` reports the failure as a result instead of throwing, so the
|
|
14
|
+
* mutation (and its `ai.tool.run` audit event) commits. The finance tools follow
|
|
15
|
+
* this: their shared invoice operations validate first, then insert/patch once.
|
|
16
|
+
*
|
|
17
|
+
* Conventions: `permission` is the owning module's own permission; anything that
|
|
18
|
+
* writes sets `requiresApproval: true`.
|
|
19
|
+
*/
|
|
20
|
+
export declare function defineAiTool<I extends z.ZodType, O>(tool: AiTool<I, O>): AiTool<I, O>;
|
|
21
|
+
/** Declares an agent. Identity at runtime; validates ids and that tool ids are unique. */
|
|
22
|
+
export declare function defineAiAgent<const A extends AiAgentDefinition>(agent: A): A;
|
|
23
|
+
/**
|
|
24
|
+
* Runs one tool call as the run's actor, inside a mutation:
|
|
25
|
+
* validates `args` with the tool's zod schema (VALIDATION), re-authorizes the
|
|
26
|
+
* actor for `tool.permission` with system-core `authorizeJob` (FORBIDDEN when
|
|
27
|
+
* null: revoked key, suspended user, role view, disabled module…), runs the
|
|
28
|
+
* handler, maps SystemError to its code, and audits `ai.tool.run`.
|
|
29
|
+
*
|
|
30
|
+
* Handlers must validate before they write: the failure is returned (not
|
|
31
|
+
* thrown) so the audit is kept, which means the surrounding mutation also
|
|
32
|
+
* commits whatever the handler wrote before it failed.
|
|
33
|
+
*/
|
|
34
|
+
export declare function executeAiTool(input: {
|
|
35
|
+
db: any;
|
|
36
|
+
sys: SystemContext;
|
|
37
|
+
actorRef: ActorRef;
|
|
38
|
+
tool: AiTool<any, any>;
|
|
39
|
+
args: unknown;
|
|
40
|
+
toolCallId: string;
|
|
41
|
+
}): Promise<ToolExecutionResult>;
|
|
42
|
+
//# sourceMappingURL=tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/ai/tools.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAC5B,OAAO,EAA8B,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAG7E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAGvD,OAAO,EAAY,KAAK,iBAAiB,EAAE,KAAK,MAAM,EAAE,KAAK,mBAAmB,EAAE,MAAM,eAAe,CAAA;AAEvG,0DAA0D;AAC1D,eAAO,MAAM,UAAU,QAAuC,CAAA;AAC9D,eAAO,MAAM,WAAW,QAA2B,CAAA;AAInD;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAUrF;AAED,0FAA0F;AAC1F,wBAAgB,aAAa,CAAC,KAAK,CAAC,CAAC,SAAS,iBAAiB,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAW5E;AAQD;;;;;;;;;;GAUG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE;IAEzC,EAAE,EAAE,GAAG,CAAA;IACP,GAAG,EAAE,aAAa,CAAA;IAClB,QAAQ,EAAE,QAAQ,CAAA;IAElB,IAAI,EAAE,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;IACtB,IAAI,EAAE,OAAO,CAAA;IACb,UAAU,EAAE,MAAM,CAAA;CACnB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA6C/B"}
|
package/dist/ai/tools.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { rehydrateActor } from "../core/access.js";
|
|
2
|
+
import { recordAudit } from "../core/audit.js";
|
|
3
|
+
import { PERMISSION_ID } from "../core/catalog.js";
|
|
4
|
+
import { toSystemError } from "../core/errors.js";
|
|
5
|
+
import { authorizeJob } from "../core/jobs.js";
|
|
6
|
+
import { AI_AUDIT } from "./contract.js";
|
|
7
|
+
/** `module.verb_object`, e.g. `finance.list_invoices`. */
|
|
8
|
+
export const AI_TOOL_ID = /^[a-z][a-z0-9-]*\.[a-z][a-z0-9_]*$/;
|
|
9
|
+
export const AI_AGENT_ID = /^[a-z][a-z0-9-]{1,47}$/;
|
|
10
|
+
const isZodSchema = (s) => !!s && typeof s === "object" && typeof s.safeParse === "function";
|
|
11
|
+
/**
|
|
12
|
+
* Declares a tool. Identity at runtime; validates the definition (id format, permission format).
|
|
13
|
+
*
|
|
14
|
+
* Handler rule: check everything (existence, state, uniqueness, limits) BEFORE the
|
|
15
|
+
* first write. A failed handler's earlier writes are committed, because
|
|
16
|
+
* `executeAiTool` reports the failure as a result instead of throwing, so the
|
|
17
|
+
* mutation (and its `ai.tool.run` audit event) commits. The finance tools follow
|
|
18
|
+
* this: their shared invoice operations validate first, then insert/patch once.
|
|
19
|
+
*
|
|
20
|
+
* Conventions: `permission` is the owning module's own permission; anything that
|
|
21
|
+
* writes sets `requiresApproval: true`.
|
|
22
|
+
*/
|
|
23
|
+
export function defineAiTool(tool) {
|
|
24
|
+
const id = typeof tool?.id === "string" ? tool.id : "";
|
|
25
|
+
if (!AI_TOOL_ID.test(id))
|
|
26
|
+
throw new Error(`defineAiTool: invalid tool id "${id}" (expected module.verb_object, e.g. finance.list_invoices)`);
|
|
27
|
+
if (typeof tool.permission !== "string" || !PERMISSION_ID.test(tool.permission)) {
|
|
28
|
+
throw new Error(`defineAiTool(${id}): invalid permission "${String(tool.permission)}" (expected module:resource:action)`);
|
|
29
|
+
}
|
|
30
|
+
if (typeof tool.description !== "string" || !tool.description.trim())
|
|
31
|
+
throw new Error(`defineAiTool(${id}): description is required`);
|
|
32
|
+
if (!isZodSchema(tool.input))
|
|
33
|
+
throw new Error(`defineAiTool(${id}): input must be a zod schema`);
|
|
34
|
+
if (typeof tool.handler !== "function")
|
|
35
|
+
throw new Error(`defineAiTool(${id}): handler must be a function`);
|
|
36
|
+
return tool;
|
|
37
|
+
}
|
|
38
|
+
/** Declares an agent. Identity at runtime; validates ids and that tool ids are unique. */
|
|
39
|
+
export function defineAiAgent(agent) {
|
|
40
|
+
const id = typeof agent?.id === "string" ? agent.id : "";
|
|
41
|
+
if (!AI_AGENT_ID.test(id))
|
|
42
|
+
throw new Error(`defineAiAgent: invalid agent id "${id}" (expected ${AI_AGENT_ID.source})`);
|
|
43
|
+
if (typeof agent.instructions !== "string" || !agent.instructions.trim())
|
|
44
|
+
throw new Error(`defineAiAgent(${id}): instructions are required`);
|
|
45
|
+
if (!Array.isArray(agent.tools))
|
|
46
|
+
throw new Error(`defineAiAgent(${id}): tools must be an array`);
|
|
47
|
+
const seen = new Set();
|
|
48
|
+
for (const tool of agent.tools) {
|
|
49
|
+
if (seen.has(tool.id))
|
|
50
|
+
throw new Error(`defineAiAgent(${id}): duplicate tool id "${tool.id}"`);
|
|
51
|
+
seen.add(tool.id);
|
|
52
|
+
}
|
|
53
|
+
return agent;
|
|
54
|
+
}
|
|
55
|
+
/** Field paths of the failed issues, never their values. */
|
|
56
|
+
function invalidFields(error) {
|
|
57
|
+
const paths = error.issues.map((i) => (i.path.length ? i.path.map((p) => String(p)).join(".") : "(root)"));
|
|
58
|
+
return [...new Set(paths)];
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Runs one tool call as the run's actor, inside a mutation:
|
|
62
|
+
* validates `args` with the tool's zod schema (VALIDATION), re-authorizes the
|
|
63
|
+
* actor for `tool.permission` with system-core `authorizeJob` (FORBIDDEN when
|
|
64
|
+
* null: revoked key, suspended user, role view, disabled module…), runs the
|
|
65
|
+
* handler, maps SystemError to its code, and audits `ai.tool.run`.
|
|
66
|
+
*
|
|
67
|
+
* Handlers must validate before they write: the failure is returned (not
|
|
68
|
+
* thrown) so the audit is kept, which means the surrounding mutation also
|
|
69
|
+
* commits whatever the handler wrote before it failed.
|
|
70
|
+
*/
|
|
71
|
+
export async function executeAiTool(input) {
|
|
72
|
+
const { db, sys, actorRef, tool, args, toolCallId } = input;
|
|
73
|
+
// Who the audit event names: the rehydrated actor if still valid, else "system".
|
|
74
|
+
let auditActor = (await rehydrateActor(sys, actorRef)) ?? "system";
|
|
75
|
+
const audit = (result, reason) => {
|
|
76
|
+
const event = { action: AI_AUDIT.toolRun, targetType: "ai_tool", targetId: tool.id, result, changes: { toolCallId } };
|
|
77
|
+
if (reason)
|
|
78
|
+
event.reason = reason;
|
|
79
|
+
return recordAudit(sys, auditActor, event);
|
|
80
|
+
};
|
|
81
|
+
const failure = async (code, message) => {
|
|
82
|
+
await audit(code === "FORBIDDEN" || code === "UNAUTHENTICATED" ? "denied" : "failed", code);
|
|
83
|
+
return { ok: false, code, message };
|
|
84
|
+
};
|
|
85
|
+
const parsed = tool.input.safeParse(args);
|
|
86
|
+
if (!parsed.success) {
|
|
87
|
+
const fields = invalidFields(parsed.error);
|
|
88
|
+
sys.logger.info("ai_tool_invalid_args", { toolId: tool.id, fields });
|
|
89
|
+
return failure("VALIDATION", `Invalid arguments for ${tool.id}: ${fields.join(", ")}`);
|
|
90
|
+
}
|
|
91
|
+
const auth = await authorizeJob(sys, actorRef, tool.permission);
|
|
92
|
+
if (!auth)
|
|
93
|
+
return failure("FORBIDDEN", `Not allowed to use ${tool.id}.`);
|
|
94
|
+
auditActor = auth.actor;
|
|
95
|
+
let result;
|
|
96
|
+
try {
|
|
97
|
+
result = await tool.handler({ db, sys, actor: auth.actor }, parsed.data);
|
|
98
|
+
}
|
|
99
|
+
catch (e) {
|
|
100
|
+
const se = toSystemError(e);
|
|
101
|
+
if (se)
|
|
102
|
+
return failure(se.code, se.toPublic().message);
|
|
103
|
+
sys.logger.error("ai_tool_failed", { toolId: tool.id, error: e });
|
|
104
|
+
return failure("UNAVAILABLE", "The tool failed.");
|
|
105
|
+
}
|
|
106
|
+
let json;
|
|
107
|
+
try {
|
|
108
|
+
json = result === undefined ? null : JSON.parse(JSON.stringify(result));
|
|
109
|
+
}
|
|
110
|
+
catch (e) {
|
|
111
|
+
sys.logger.error("ai_tool_result_not_json", { toolId: tool.id, error: e });
|
|
112
|
+
return failure("UNAVAILABLE", "The tool returned an invalid result.");
|
|
113
|
+
}
|
|
114
|
+
await audit("success");
|
|
115
|
+
return { ok: true, result: json };
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/ai/tools.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** The fields of an audit event (`listAudit` items) the export reads. */
|
|
2
|
+
export interface AuditCsvEvent {
|
|
3
|
+
at: number;
|
|
4
|
+
action: string;
|
|
5
|
+
result: string;
|
|
6
|
+
actorKind?: string;
|
|
7
|
+
actorUserId?: string;
|
|
8
|
+
apiKeyId?: string;
|
|
9
|
+
targetType: string;
|
|
10
|
+
targetId: string;
|
|
11
|
+
operationId: string;
|
|
12
|
+
changes?: Record<string, unknown>;
|
|
13
|
+
reason?: string;
|
|
14
|
+
}
|
|
15
|
+
/** Stable column order. */
|
|
16
|
+
export declare const AUDIT_CSV_COLUMNS: readonly ["at", "action", "result", "actor", "apiKey", "target", "operationId", "details"];
|
|
17
|
+
export interface AuditCsvOptions {
|
|
18
|
+
/** Prepend a UTF-8 BOM so Excel reads Lithuanian letters correctly. Default false. */
|
|
19
|
+
bom?: boolean;
|
|
20
|
+
}
|
|
21
|
+
/** One CSV cell: neutralizes formula injection (leading `'`), then RFC 4180 quoting. */
|
|
22
|
+
export declare function csvCell(value: unknown): string;
|
|
23
|
+
/**
|
|
24
|
+
* Audit events as CSV (RFC 4180: CRLF line ends, quoted cells where needed),
|
|
25
|
+
* header row first. Columns: at (ISO 8601 UTC), action, result, actor (user ID,
|
|
26
|
+
* or `system` / `anonymous`), apiKey (key ID for agents), target
|
|
27
|
+
* (`type:id`), operationId, details (JSON of `changes` and `reason`).
|
|
28
|
+
* Cells starting with = + - @ tab or CR get a leading `'` (CSV formula injection).
|
|
29
|
+
*/
|
|
30
|
+
export declare function auditToCsv(events: readonly AuditCsvEvent[], options?: AuditCsvOptions): string;
|
|
31
|
+
//# sourceMappingURL=csv.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csv.d.ts","sourceRoot":"","sources":["../../src/audit/csv.ts"],"names":[],"mappings":"AAIA,yEAAyE;AACzE,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACjC,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,2BAA2B;AAC3B,eAAO,MAAM,iBAAiB,YAAI,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,SAAS,CAAU,CAAA;AAE3H,MAAM,WAAW,eAAe;IAC9B,sFAAsF;IACtF,GAAG,CAAC,EAAE,OAAO,CAAA;CACd;AAKD,wFAAwF;AACxF,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAI9C;AASD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,EAAE,OAAO,GAAE,eAAoB,GAAG,MAAM,CAQlG"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// CSV export of audit events (DIG-73). Pure and browser-safe: no Convex, no env.
|
|
2
|
+
// The UI pages through `listAudit`, calls `auditToCsv`, offers the download and
|
|
3
|
+
// records the export with `recordAuditExport` (audit action `audit.export`).
|
|
4
|
+
/** Stable column order. */
|
|
5
|
+
export const AUDIT_CSV_COLUMNS = ["at", "action", "result", "actor", "apiKey", "target", "operationId", "details"];
|
|
6
|
+
/** Characters that make spreadsheet apps evaluate a cell as a formula. */
|
|
7
|
+
const FORMULA_START = /^[=+\-@\t\r]/;
|
|
8
|
+
/** One CSV cell: neutralizes formula injection (leading `'`), then RFC 4180 quoting. */
|
|
9
|
+
export function csvCell(value) {
|
|
10
|
+
let s = value === undefined || value === null ? "" : String(value);
|
|
11
|
+
if (FORMULA_START.test(s))
|
|
12
|
+
s = `'${s}`;
|
|
13
|
+
return /[",\r\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
|
|
14
|
+
}
|
|
15
|
+
function details(e) {
|
|
16
|
+
const out = {};
|
|
17
|
+
if (e.changes !== undefined)
|
|
18
|
+
out.changes = e.changes;
|
|
19
|
+
if (e.reason !== undefined)
|
|
20
|
+
out.reason = e.reason;
|
|
21
|
+
return Object.keys(out).length ? JSON.stringify(out) : "";
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Audit events as CSV (RFC 4180: CRLF line ends, quoted cells where needed),
|
|
25
|
+
* header row first. Columns: at (ISO 8601 UTC), action, result, actor (user ID,
|
|
26
|
+
* or `system` / `anonymous`), apiKey (key ID for agents), target
|
|
27
|
+
* (`type:id`), operationId, details (JSON of `changes` and `reason`).
|
|
28
|
+
* Cells starting with = + - @ tab or CR get a leading `'` (CSV formula injection).
|
|
29
|
+
*/
|
|
30
|
+
export function auditToCsv(events, options = {}) {
|
|
31
|
+
const rows = [AUDIT_CSV_COLUMNS.join(",")];
|
|
32
|
+
for (const e of events) {
|
|
33
|
+
const at = Number.isFinite(e.at) ? new Date(e.at).toISOString() : "";
|
|
34
|
+
const actor = e.actorUserId ?? e.actorKind ?? "";
|
|
35
|
+
rows.push([at, e.action, e.result, actor, e.apiKeyId ?? "", `${e.targetType}:${e.targetId}`, e.operationId, details(e)].map(csvCell).join(","));
|
|
36
|
+
}
|
|
37
|
+
return `${options.bom ? "" : ""}${rows.join("\r\n")}\r\n`;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=csv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csv.js","sourceRoot":"","sources":["../../src/audit/csv.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,gFAAgF;AAChF,6EAA6E;AAiB7E,2BAA2B;AAC3B,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,SAAS,CAAU,CAAA;AAO3H,0EAA0E;AAC1E,MAAM,aAAa,GAAG,cAAc,CAAA;AAEpC,wFAAwF;AACxF,MAAM,UAAU,OAAO,CAAC,KAAc;IACpC,IAAI,CAAC,GAAG,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IAClE,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,CAAC,GAAG,IAAI,CAAC,EAAE,CAAA;IACtC,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;AAC9D,CAAC;AAED,SAAS,OAAO,CAAC,CAAgB;IAC/B,MAAM,GAAG,GAA4B,EAAE,CAAA;IACvC,IAAI,CAAC,CAAC,OAAO,KAAK,SAAS;QAAE,GAAG,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,CAAA;IACpD,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS;QAAE,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAA;IACjD,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;AAC3D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CAAC,MAAgC,EAAE,OAAO,GAAoB,EAAE;IACxF,MAAM,IAAI,GAAa,CAAC,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;IACpD,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;QACpE,MAAM,KAAK,GAAG,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,SAAS,IAAI,EAAE,CAAA;QAChD,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,QAAQ,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;IACjJ,CAAC;IACD,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAA;AAC5D,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { auditToCsv, csvCell, AUDIT_CSV_COLUMNS, type AuditCsvEvent, type AuditCsvOptions } from "./csv.js";
|
|
2
|
+
export { auditModuleOf, CORE_AUDIT_PREFIXES } from "../core/audit-module.js";
|
|
3
|
+
export type { AuditEventRow } from "../core/store.js";
|
|
4
|
+
export type { AuditFacets, AuditFilter, AuditExportFilter } from "../core/audit.js";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/audit/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,iBAAiB,EAAE,KAAK,aAAa,EAAE,KAAK,eAAe,EAAE,MAAM,UAAU,CAAA;AAC3G,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAA;AAC5E,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AACrD,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// `@digimuza/system-core/audit`: browser-safe audit helpers (DIG-73).
|
|
2
|
+
// The audit functions themselves (`listAudit`, `listAuditFacets`,
|
|
3
|
+
// `recordAuditExport`) are part of `system.functions` (`@digimuza/system-core/convex`).
|
|
4
|
+
export { auditToCsv, csvCell, AUDIT_CSV_COLUMNS } from "./csv.js";
|
|
5
|
+
export { auditModuleOf, CORE_AUDIT_PREFIXES } from "../core/audit-module.js";
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/audit/index.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,kEAAkE;AAClE,wFAAwF;AACxF,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,iBAAiB,EAA4C,MAAM,UAAU,CAAA;AAC3G,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAA"}
|