@agent-native/core 0.70.3 → 0.72.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +51 -0
- package/corpus/core/docs/content/actions.md +23 -0
- package/corpus/core/docs/content/audit-log.md +111 -0
- package/corpus/core/docs/content/multi-app-workspace.md +2 -2
- package/corpus/core/docs/design/durable-agent-runs.md +458 -4
- package/corpus/core/package.json +2 -1
- package/corpus/core/src/action.ts +80 -1
- package/corpus/core/src/agent/durable-background.ts +192 -0
- package/corpus/core/src/agent/production-agent.ts +357 -13
- package/corpus/core/src/agent/run-manager.ts +66 -3
- package/corpus/core/src/agent/run-store.ts +143 -23
- package/corpus/core/src/agent/types.ts +20 -0
- package/corpus/core/src/application-state/store.ts +5 -0
- package/corpus/core/src/audit/actions/get-audit-event.ts +23 -0
- package/corpus/core/src/audit/actions/list-audit-events.ts +65 -0
- package/corpus/core/src/audit/cleanup-job.ts +100 -0
- package/corpus/core/src/audit/config.ts +91 -0
- package/corpus/core/src/audit/index.ts +43 -0
- package/corpus/core/src/audit/record.ts +143 -0
- package/corpus/core/src/audit/redact.ts +109 -0
- package/corpus/core/src/audit/store.ts +244 -0
- package/corpus/core/src/audit/types.ts +125 -0
- package/corpus/core/src/chat-threads/store.ts +10 -0
- package/corpus/core/src/client/AgentPanel.tsx +12 -9
- package/corpus/core/src/client/AssistantChat.tsx +6 -0
- package/corpus/core/src/client/blocks/library/FileTreeBlock.tsx +72 -14
- package/corpus/core/src/client/chat/repo-helpers.ts +38 -0
- package/corpus/core/src/db/client.ts +4 -0
- package/corpus/core/src/db/widen-columns.ts +75 -0
- package/corpus/core/src/deploy/build.ts +96 -0
- package/corpus/core/src/deploy/workspace-deploy.ts +121 -0
- package/corpus/core/src/mcp/build-server.ts +23 -13
- package/corpus/core/src/oauth-tokens/store.ts +6 -0
- package/corpus/core/src/provider-api/custom-registry.ts +8 -0
- package/corpus/core/src/resources/store.ts +10 -0
- package/corpus/core/src/scripts/runner.ts +6 -3
- package/corpus/core/src/server/action-discovery.ts +6 -0
- package/corpus/core/src/server/action-routes.ts +1 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +133 -66
- package/corpus/core/src/server/auth.ts +5 -0
- package/corpus/core/src/server/core-routes-plugin.ts +14 -0
- package/corpus/core/src/server/security-headers.ts +9 -6
- package/corpus/core/src/settings/store.ts +6 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/audit-log/SKILL.md +93 -0
- package/corpus/core/src/usage/store.ts +6 -0
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +19 -0
- package/corpus/templates/analytics/AGENTS.md +8 -0
- package/corpus/templates/analytics/actions/compose-dashboard.ts +317 -0
- package/corpus/templates/analytics/changelog/2026-06-23-build-large-first-party-analytics-dashboards-in-one-fast-cal.md +6 -0
- package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +574 -0
- package/corpus/templates/calendar/app/components/calendar/GoogleConnectBanner.tsx +60 -6
- package/corpus/templates/calendar/changelog/2026-06-23-added-a-heads-up-explaining-google-s-app-not-verified-screen.md +6 -0
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +20 -43
- package/corpus/templates/clips/app/components/recorder/recording-toolbar.tsx +5 -1
- package/corpus/templates/clips/app/lib/countdown-audio-cue.ts +3 -51
- package/corpus/templates/clips/changelog/2026-06-23-dragging-the-desktop-camera-bubble-now-glides-to-a-stop-at-t.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-public-clips-now-play-inline-in-slack-connect-a-workspac.md +6 -0
- package/corpus/templates/clips/chrome-extension/PERMISSIONS.md +72 -0
- package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -10
- package/corpus/templates/clips/chrome-extension/src/background.ts +163 -32
- package/corpus/templates/clips/chrome-extension/src/content-script.ts +301 -24
- package/corpus/templates/clips/chrome-extension/src/offscreen.ts +245 -67
- package/corpus/templates/clips/chrome-extension/src/overlay.css +77 -43
- package/corpus/templates/clips/chrome-extension/src/overlay.html +7 -1
- package/corpus/templates/clips/chrome-extension/src/overlay.ts +102 -38
- package/corpus/templates/clips/chrome-extension/src/popup.html +62 -5
- package/corpus/templates/clips/chrome-extension/src/popup.ts +275 -4
- package/corpus/templates/clips/chrome-extension/src/styles.css +34 -0
- package/corpus/templates/clips/chrome-extension/vite.config.ts +5 -0
- package/corpus/templates/clips/desktop/src/lib/audio-cue.ts +21 -16
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +10 -17
- package/corpus/templates/clips/desktop/src/overlays/bubble.tsx +73 -23
- package/corpus/templates/clips/desktop/src/overlays/countdown.tsx +0 -6
- package/corpus/templates/clips/desktop/src/overlays/toolbar.tsx +56 -46
- package/corpus/templates/clips/desktop/src/styles.css +33 -5
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +113 -0
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +3 -0
- package/corpus/templates/clips/server/lib/media-permissions.ts +5 -1
- package/corpus/templates/clips/shared/recording-audio.ts +62 -0
- package/corpus/templates/clips/shared/recording-core.ts +94 -0
- package/dist/action.d.ts +31 -0
- package/dist/action.d.ts.map +1 -1
- package/dist/action.js +45 -1
- package/dist/action.js.map +1 -1
- package/dist/agent/context-xray/schema.d.ts +1 -1
- package/dist/agent/durable-background.d.ts +60 -0
- package/dist/agent/durable-background.d.ts.map +1 -0
- package/dist/agent/durable-background.js +144 -0
- package/dist/agent/durable-background.js.map +1 -0
- package/dist/agent/observational-memory/schema.d.ts +1 -1
- package/dist/agent/production-agent.d.ts +20 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +320 -14
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts +48 -0
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +45 -3
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/run-store.d.ts +30 -1
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +138 -24
- package/dist/agent/run-store.js.map +1 -1
- package/dist/agent/types.d.ts +20 -0
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/application-state/store.d.ts.map +1 -1
- package/dist/application-state/store.js +5 -0
- package/dist/application-state/store.js.map +1 -1
- package/dist/audit/actions/get-audit-event.d.ts +11 -0
- package/dist/audit/actions/get-audit-event.d.ts.map +1 -0
- package/dist/audit/actions/get-audit-event.js +22 -0
- package/dist/audit/actions/get-audit-event.js.map +1 -0
- package/dist/audit/actions/list-audit-events.d.ts +22 -0
- package/dist/audit/actions/list-audit-events.d.ts.map +1 -0
- package/dist/audit/actions/list-audit-events.js +61 -0
- package/dist/audit/actions/list-audit-events.js.map +1 -0
- package/dist/audit/cleanup-job.d.ts +12 -0
- package/dist/audit/cleanup-job.d.ts.map +1 -0
- package/dist/audit/cleanup-job.js +93 -0
- package/dist/audit/cleanup-job.js.map +1 -0
- package/dist/audit/config.d.ts +30 -0
- package/dist/audit/config.d.ts.map +1 -0
- package/dist/audit/config.js +65 -0
- package/dist/audit/config.js.map +1 -0
- package/dist/audit/index.d.ts +13 -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/audit/record.d.ts +24 -0
- package/dist/audit/record.d.ts.map +1 -0
- package/dist/audit/record.js +106 -0
- package/dist/audit/record.js.map +1 -0
- package/dist/audit/redact.d.ts +28 -0
- package/dist/audit/redact.d.ts.map +1 -0
- package/dist/audit/redact.js +109 -0
- package/dist/audit/redact.js.map +1 -0
- package/dist/audit/store.d.ts +14 -0
- package/dist/audit/store.d.ts.map +1 -0
- package/dist/audit/store.js +219 -0
- package/dist/audit/store.js.map +1 -0
- package/dist/audit/types.d.ts +114 -0
- package/dist/audit/types.d.ts.map +1 -0
- package/dist/audit/types.js +15 -0
- package/dist/audit/types.js.map +1 -0
- package/dist/chat-threads/store.d.ts.map +1 -1
- package/dist/chat-threads/store.js +10 -0
- package/dist/chat-threads/store.js.map +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +2 -2
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +6 -1
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/blocks/library/FileTreeBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/FileTreeBlock.js +37 -4
- package/dist/client/blocks/library/FileTreeBlock.js.map +1 -1
- package/dist/client/chat/repo-helpers.d.ts +16 -0
- package/dist/client/chat/repo-helpers.d.ts.map +1 -1
- package/dist/client/chat/repo-helpers.js +40 -0
- package/dist/client/chat/repo-helpers.js.map +1 -1
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +3 -0
- package/dist/db/client.js.map +1 -1
- package/dist/db/widen-columns.d.ts +39 -0
- package/dist/db/widen-columns.d.ts.map +1 -0
- package/dist/db/widen-columns.js +73 -0
- package/dist/db/widen-columns.js.map +1 -0
- package/dist/deploy/build.d.ts +29 -0
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +85 -0
- package/dist/deploy/build.js.map +1 -1
- package/dist/deploy/workspace-deploy.d.ts.map +1 -1
- package/dist/deploy/workspace-deploy.js +108 -0
- package/dist/deploy/workspace-deploy.js.map +1 -1
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js +24 -9
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/oauth-tokens/store.d.ts.map +1 -1
- package/dist/oauth-tokens/store.js +6 -0
- package/dist/oauth-tokens/store.js.map +1 -1
- package/dist/provider-api/custom-registry.d.ts.map +1 -1
- package/dist/provider-api/custom-registry.js +8 -0
- package/dist/provider-api/custom-registry.js.map +1 -1
- package/dist/resources/store.d.ts.map +1 -1
- package/dist/resources/store.js +9 -0
- package/dist/resources/store.js.map +1 -1
- package/dist/scripts/runner.js +4 -3
- package/dist/scripts/runner.js.map +1 -1
- package/dist/server/action-discovery.d.ts.map +1 -1
- package/dist/server/action-discovery.js +6 -0
- package/dist/server/action-discovery.js.map +1 -1
- package/dist/server/action-routes.d.ts.map +1 -1
- package/dist/server/action-routes.js +1 -0
- package/dist/server/action-routes.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +68 -13
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +5 -0
- package/dist/server/auth.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +13 -0
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/security-headers.d.ts +8 -5
- package/dist/server/security-headers.d.ts.map +1 -1
- package/dist/server/security-headers.js +9 -6
- package/dist/server/security-headers.js.map +1 -1
- package/dist/settings/store.d.ts.map +1 -1
- package/dist/settings/store.js +6 -0
- package/dist/settings/store.js.map +1 -1
- package/dist/templates/workspace-core/.agents/skills/audit-log/SKILL.md +93 -0
- package/dist/usage/store.d.ts.map +1 -1
- package/dist/usage/store.js +5 -0
- package/dist/usage/store.js.map +1 -1
- package/docs/content/actions.md +23 -0
- package/docs/content/audit-log.md +111 -0
- package/docs/content/multi-app-workspace.md +2 -2
- package/docs/design/durable-agent-runs.md +458 -4
- package/package.json +2 -1
- package/src/templates/workspace-core/.agents/skills/audit-log/SKILL.md +93 -0
package/dist/agent/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/agent/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ReasoningEffort } from \"../shared/reasoning-effort.js\";\nimport type { AgentMcpAppPayload } from \"../mcp-client/app-result.js\";\nimport type { ActionChatUIConfig } from \"../action-ui.js\";\n\nexport interface AgentNativeJsonSchema {\n type?: string | string[];\n description?: string;\n enum?: unknown[];\n const?: unknown;\n properties?: Record<string, AgentNativeJsonSchema>;\n required?: string[];\n additionalProperties?: boolean | AgentNativeJsonSchema;\n items?: AgentNativeJsonSchema;\n oneOf?: AgentNativeJsonSchema[];\n anyOf?: AgentNativeJsonSchema[];\n allOf?: AgentNativeJsonSchema[];\n not?: AgentNativeJsonSchema;\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n minimum?: number;\n maximum?: number;\n minItems?: number;\n maxItems?: number;\n}\n\nexport interface ActionTool {\n description: string;\n parameters?: AgentNativeJsonSchema & {\n type: \"object\";\n properties: Record<string, AgentNativeJsonSchema>;\n required?: string[];\n };\n}\n\n/** @deprecated Use `ActionTool` instead */\nexport type ScriptTool = ActionTool;\n\nexport interface AgentMessage {\n role: \"user\" | \"assistant\";\n content: string;\n}\n\nexport type AgentChatStructuredContentPart =\n | { type: \"text\"; text: string }\n | {\n type: \"tool-call\";\n id?: string;\n toolCallId?: string;\n name?: string;\n toolName?: string;\n input?: unknown;\n args?: unknown;\n }\n | {\n type: \"tool-result\";\n toolCallId: string;\n /** Persisted for replay; omitted in older rows is backfilled server-side. */\n toolName?: string;\n toolInput?: string;\n content: string;\n isError?: boolean;\n };\n\nexport interface AgentChatStructuredMessage {\n role: \"user\" | \"assistant\";\n content: AgentChatStructuredContentPart[];\n}\n\nexport interface AgentChatReference {\n type: \"file\" | \"skill\" | \"mention\" | \"agent\" | \"custom-agent\";\n path: string;\n name: string;\n source: string;\n refType?: string;\n refId?: string;\n}\n\nexport interface MentionProviderItem {\n id: string;\n label: string;\n description?: string;\n icon?: string;\n refType: string;\n refId?: string;\n refPath?: string;\n}\n\nexport interface MentionProvider {\n label: string;\n icon?: string;\n search: (\n query: string,\n /** The H3 event for the current request — use to make internal API calls */\n event?: any,\n ) => MentionProviderItem[] | Promise<MentionProviderItem[]>;\n}\n\nexport interface AgentChatAttachment {\n type: string;\n name: string;\n data?: string;\n contentType?: string;\n text?: string;\n}\n\nexport interface AgentChatScope {\n type: string;\n id: string;\n label?: string;\n}\n\nexport interface AgentChatRequest {\n message: string;\n /**\n * User-visible text to persist in chat history. `message` may be normalized\n * for the model (for example mention markup or internal continuation text).\n */\n displayMessage?: string;\n history?: AgentMessage[];\n /**\n * Provider-neutral transcript used for run recovery. Unlike `history`,\n * this preserves assistant tool calls and matching tool results so\n * continuation turns do not re-run completed read-only tools.\n */\n structuredHistory?: AgentChatStructuredMessage[];\n references?: AgentChatReference[];\n threadId?: string;\n attachments?: AgentChatAttachment[];\n /** Internal retry/continuation requests should not create visible user turns. */\n internalContinuation?: boolean;\n /**\n * Stable identity for the logical assistant turn this request belongs to.\n * The client sends the SAME turnId for the initial POST and every\n * auto-continuation re-POST of one turn, so the server can fold each\n * continuation run's output onto a single durable assistant message instead\n * of dropping the earlier chunks. Defaults to the run id when absent.\n */\n turnId?: string;\n /** Execution mode for this turn. Plan mode is read-only and proposes before acting. */\n mode?: \"act\" | \"plan\";\n /** Per-request model override (ephemeral, from the composer model picker). */\n model?: string;\n /** Per-request engine override (sent alongside model for cross-provider switches). */\n engine?: string;\n /** Per-request reasoning effort override (ephemeral, from the composer picker). */\n effort?: ReasoningEffort;\n /** Usage-tracking label for this call (e.g. \"chat\", \"summarize\"). Default: \"chat\". */\n usageLabel?: string;\n /** Stable browser tab id so screen/url context and navigation commands are tab-scoped. */\n browserTabId?: string;\n /** Resource scope for this chat thread, e.g. the deck currently bound to the tab. */\n scope?: AgentChatScope | null;\n /** When true, expose this chat turn as a user-visible run in RunsTray. */\n trackInRunsTray?: boolean;\n /**\n * Approval grants for human-in-the-loop actions. Each entry is a stable\n * approval key (see the `approval_required` event's `approvalKey`). When the\n * agent calls an action declared `needsApproval`, the loop pauses and emits\n * `approval_required`; the client re-issues the turn (typically an empty\n * continuation) with the approved call's key here so the gate lets it run.\n * Keys not present here keep the action paused. The model never sees or sets\n * this — it is supplied by the human's approve affordance.\n */\n approvedToolCalls?: string[];\n}\n\nexport type AgentChatEvent =\n | { type: \"text\"; text: string }\n | { type: \"thinking\"; text: string }\n | { type: \"activity\"; label: string; tool?: string }\n | { type: \"stream_keepalive\" }\n | { type: \"tool_start\"; tool: string; input: Record<string, string> }\n | {\n type: \"tool_done\";\n tool: string;\n result: string;\n mcpApp?: AgentMcpAppPayload;\n chatUI?: ActionChatUIConfig;\n }\n | {\n /**\n * The agent tried to call an action declared `needsApproval` and the loop\n * paused instead of executing it. The client should surface an\n * approve/deny affordance; on approve, re-issue the turn with\n * `approvedToolCalls: [approvalKey]` so the gate lets this call run.\n */\n type: \"approval_required\";\n tool: string;\n input: Record<string, string>;\n /** Stable key the client echoes back in `approvedToolCalls` to approve. */\n approvalKey: string;\n /** The model-side tool-call id for this paused call, when available. */\n toolCallId?: string;\n }\n | {\n type: \"agent_call\";\n agent: string;\n status: \"start\" | \"done\" | \"error\";\n }\n | { type: \"agent_call_text\"; agent: string; text: string }\n | {\n type: \"agent_task\";\n taskId: string;\n threadId: string;\n description: string;\n status: \"running\" | \"completed\" | \"errored\";\n }\n | {\n type: \"agent_task_update\";\n taskId: string;\n preview: string;\n currentStep?: string;\n }\n | {\n type: \"agent_task_complete\";\n taskId: string;\n summary: string;\n }\n | { type: \"done\" }\n | {\n type: \"error\";\n error: string;\n /**\n * Optional machine-readable error code. Builder gateway uses codes\n * like \"credits-limit-monthly\" / \"unauthorized\" / \"gateway_not_enabled\"\n * so the chat UI can render a structured CTA (e.g. upgrade button).\n */\n errorCode?: string;\n /** Optional link paired with errorCode — e.g. Builder billing page. */\n upgradeUrl?: string;\n /** Optional details for expandable UI/debugging. */\n details?: string;\n /** True when the user can reasonably continue/retry from partial work. */\n recoverable?: boolean;\n }\n /**\n * Legacy SSE terminal event. New streams emit\n * `{ type: \"error\", errorCode: \"missing_credentials\" }` instead.\n */\n | { type: \"missing_api_key\" }\n | { type: \"loop_limit\"; maxIterations?: number }\n | {\n /**\n * An in-loop `Processor` aborted the run via `abort()` (which throws a\n * `TripWire`). The loop catches it, emits this event, stops cleanly, and\n * surfaces the reason as a final assistant message. Structural hook for\n * real-time guardrails and a proof-of-done / coverage gate.\n */\n type: \"tripwire\";\n reason: string;\n /** Name of the processor that aborted, when it declared one. */\n processor?: string;\n }\n | {\n type: \"auto_continue\";\n reason:\n | \"run_timeout\"\n | \"loop_limit\"\n | \"no_progress\"\n | \"stream_ended\"\n | \"gateway_timeout\"\n | \"network_interrupted\";\n maxIterations?: number;\n }\n | { type: \"clear\" };\n\nexport interface RunEvent {\n seq: number;\n event: AgentChatEvent;\n}\n\nexport type RunStatus = \"running\" | \"completed\" | \"errored\" | \"aborted\";\n"]}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/agent/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ReasoningEffort } from \"../shared/reasoning-effort.js\";\nimport type { AgentMcpAppPayload } from \"../mcp-client/app-result.js\";\nimport type { ActionChatUIConfig } from \"../action-ui.js\";\n\nexport interface AgentNativeJsonSchema {\n type?: string | string[];\n description?: string;\n enum?: unknown[];\n const?: unknown;\n properties?: Record<string, AgentNativeJsonSchema>;\n required?: string[];\n additionalProperties?: boolean | AgentNativeJsonSchema;\n items?: AgentNativeJsonSchema;\n oneOf?: AgentNativeJsonSchema[];\n anyOf?: AgentNativeJsonSchema[];\n allOf?: AgentNativeJsonSchema[];\n not?: AgentNativeJsonSchema;\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n minimum?: number;\n maximum?: number;\n minItems?: number;\n maxItems?: number;\n}\n\nexport interface ActionTool {\n description: string;\n parameters?: AgentNativeJsonSchema & {\n type: \"object\";\n properties: Record<string, AgentNativeJsonSchema>;\n required?: string[];\n };\n}\n\n/** @deprecated Use `ActionTool` instead */\nexport type ScriptTool = ActionTool;\n\nexport interface AgentMessage {\n role: \"user\" | \"assistant\";\n content: string;\n}\n\nexport type AgentChatStructuredContentPart =\n | { type: \"text\"; text: string }\n | {\n type: \"tool-call\";\n id?: string;\n toolCallId?: string;\n name?: string;\n toolName?: string;\n input?: unknown;\n args?: unknown;\n }\n | {\n type: \"tool-result\";\n toolCallId: string;\n /** Persisted for replay; omitted in older rows is backfilled server-side. */\n toolName?: string;\n toolInput?: string;\n content: string;\n isError?: boolean;\n };\n\nexport interface AgentChatStructuredMessage {\n role: \"user\" | \"assistant\";\n content: AgentChatStructuredContentPart[];\n}\n\nexport interface AgentChatReference {\n type: \"file\" | \"skill\" | \"mention\" | \"agent\" | \"custom-agent\";\n path: string;\n name: string;\n source: string;\n refType?: string;\n refId?: string;\n}\n\nexport interface MentionProviderItem {\n id: string;\n label: string;\n description?: string;\n icon?: string;\n refType: string;\n refId?: string;\n refPath?: string;\n}\n\nexport interface MentionProvider {\n label: string;\n icon?: string;\n search: (\n query: string,\n /** The H3 event for the current request — use to make internal API calls */\n event?: any,\n ) => MentionProviderItem[] | Promise<MentionProviderItem[]>;\n}\n\nexport interface AgentChatAttachment {\n type: string;\n name: string;\n data?: string;\n contentType?: string;\n text?: string;\n}\n\nexport interface AgentChatScope {\n type: string;\n id: string;\n label?: string;\n}\n\nexport interface AgentChatRequest {\n message: string;\n /**\n * User-visible text to persist in chat history. `message` may be normalized\n * for the model (for example mention markup or internal continuation text).\n */\n displayMessage?: string;\n history?: AgentMessage[];\n /**\n * Provider-neutral transcript used for run recovery. Unlike `history`,\n * this preserves assistant tool calls and matching tool results so\n * continuation turns do not re-run completed read-only tools.\n */\n structuredHistory?: AgentChatStructuredMessage[];\n references?: AgentChatReference[];\n threadId?: string;\n attachments?: AgentChatAttachment[];\n /** Internal retry/continuation requests should not create visible user turns. */\n internalContinuation?: boolean;\n /**\n * Internal marker set ONLY by the durable-background self-dispatch (see\n * `AGENT_CHAT_BACKGROUND_RUN_FIELD`). Present when the agent-chat handler is\n * re-entered as the background worker: it carries the pre-claimed `runId` and\n * logical `turnId` so the worker runs the loop inline with the background\n * soft-timeout instead of re-claiming the slot or re-dispatching. Untrusted\n * on its own — the `_process-run` route HMAC-verifies the dispatch before\n * invoking the handler. Absent on every normal client request.\n */\n __backgroundRun?: {\n runId: string;\n turnId?: string;\n /**\n * Number of server-driven background→background continuations already\n * chained into this logical turn (0 on the first chunk). The worker\n * increments this when it re-fires `_process-run` at a soft-timeout\n * boundary and refuses to chain past `MAX_BACKGROUND_RUN_CONTINUATIONS`.\n */\n continuationCount?: number;\n };\n /**\n * Stable identity for the logical assistant turn this request belongs to.\n * The client sends the SAME turnId for the initial POST and every\n * auto-continuation re-POST of one turn, so the server can fold each\n * continuation run's output onto a single durable assistant message instead\n * of dropping the earlier chunks. Defaults to the run id when absent.\n */\n turnId?: string;\n /** Execution mode for this turn. Plan mode is read-only and proposes before acting. */\n mode?: \"act\" | \"plan\";\n /** Per-request model override (ephemeral, from the composer model picker). */\n model?: string;\n /** Per-request engine override (sent alongside model for cross-provider switches). */\n engine?: string;\n /** Per-request reasoning effort override (ephemeral, from the composer picker). */\n effort?: ReasoningEffort;\n /** Usage-tracking label for this call (e.g. \"chat\", \"summarize\"). Default: \"chat\". */\n usageLabel?: string;\n /** Stable browser tab id so screen/url context and navigation commands are tab-scoped. */\n browserTabId?: string;\n /** Resource scope for this chat thread, e.g. the deck currently bound to the tab. */\n scope?: AgentChatScope | null;\n /** When true, expose this chat turn as a user-visible run in RunsTray. */\n trackInRunsTray?: boolean;\n /**\n * Approval grants for human-in-the-loop actions. Each entry is a stable\n * approval key (see the `approval_required` event's `approvalKey`). When the\n * agent calls an action declared `needsApproval`, the loop pauses and emits\n * `approval_required`; the client re-issues the turn (typically an empty\n * continuation) with the approved call's key here so the gate lets it run.\n * Keys not present here keep the action paused. The model never sees or sets\n * this — it is supplied by the human's approve affordance.\n */\n approvedToolCalls?: string[];\n}\n\nexport type AgentChatEvent =\n | { type: \"text\"; text: string }\n | { type: \"thinking\"; text: string }\n | { type: \"activity\"; label: string; tool?: string }\n | { type: \"stream_keepalive\" }\n | { type: \"tool_start\"; tool: string; input: Record<string, string> }\n | {\n type: \"tool_done\";\n tool: string;\n result: string;\n mcpApp?: AgentMcpAppPayload;\n chatUI?: ActionChatUIConfig;\n }\n | {\n /**\n * The agent tried to call an action declared `needsApproval` and the loop\n * paused instead of executing it. The client should surface an\n * approve/deny affordance; on approve, re-issue the turn with\n * `approvedToolCalls: [approvalKey]` so the gate lets this call run.\n */\n type: \"approval_required\";\n tool: string;\n input: Record<string, string>;\n /** Stable key the client echoes back in `approvedToolCalls` to approve. */\n approvalKey: string;\n /** The model-side tool-call id for this paused call, when available. */\n toolCallId?: string;\n }\n | {\n type: \"agent_call\";\n agent: string;\n status: \"start\" | \"done\" | \"error\";\n }\n | { type: \"agent_call_text\"; agent: string; text: string }\n | {\n type: \"agent_task\";\n taskId: string;\n threadId: string;\n description: string;\n status: \"running\" | \"completed\" | \"errored\";\n }\n | {\n type: \"agent_task_update\";\n taskId: string;\n preview: string;\n currentStep?: string;\n }\n | {\n type: \"agent_task_complete\";\n taskId: string;\n summary: string;\n }\n | { type: \"done\" }\n | {\n type: \"error\";\n error: string;\n /**\n * Optional machine-readable error code. Builder gateway uses codes\n * like \"credits-limit-monthly\" / \"unauthorized\" / \"gateway_not_enabled\"\n * so the chat UI can render a structured CTA (e.g. upgrade button).\n */\n errorCode?: string;\n /** Optional link paired with errorCode — e.g. Builder billing page. */\n upgradeUrl?: string;\n /** Optional details for expandable UI/debugging. */\n details?: string;\n /** True when the user can reasonably continue/retry from partial work. */\n recoverable?: boolean;\n }\n /**\n * Legacy SSE terminal event. New streams emit\n * `{ type: \"error\", errorCode: \"missing_credentials\" }` instead.\n */\n | { type: \"missing_api_key\" }\n | { type: \"loop_limit\"; maxIterations?: number }\n | {\n /**\n * An in-loop `Processor` aborted the run via `abort()` (which throws a\n * `TripWire`). The loop catches it, emits this event, stops cleanly, and\n * surfaces the reason as a final assistant message. Structural hook for\n * real-time guardrails and a proof-of-done / coverage gate.\n */\n type: \"tripwire\";\n reason: string;\n /** Name of the processor that aborted, when it declared one. */\n processor?: string;\n }\n | {\n type: \"auto_continue\";\n reason:\n | \"run_timeout\"\n | \"loop_limit\"\n | \"no_progress\"\n | \"stream_ended\"\n | \"gateway_timeout\"\n | \"network_interrupted\";\n maxIterations?: number;\n }\n | { type: \"clear\" };\n\nexport interface RunEvent {\n seq: number;\n event: AgentChatEvent;\n}\n\nexport type RunStatus = \"running\" | \"completed\" | \"errored\" | \"aborted\";\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/application-state/store.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/application-state/store.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAsD9D,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAgBzC;AAED,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAUf;AAED,wBAAsB,cAAc,CAClC,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,OAAO,CAAC,CAUlB;AAED,wBAAsB,YAAY,CAChC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,CAAC,CAWjE;AAED,wBAAsB,sBAAsB,CAC1C,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,MAAM,CAAC,CAsBjB"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getDbExec, isConnectionError, isPostgres, intType, } from "../db/client.js";
|
|
2
|
+
import { widenIntColumnsToBigInt } from "../db/widen-columns.js";
|
|
2
3
|
import { emitAppStateChange, emitAppStateDelete } from "./emitter.js";
|
|
3
4
|
let _initPromise;
|
|
4
5
|
// Escapes LIKE wildcards (`%`, `_`) and the escape char itself so a caller's
|
|
@@ -21,6 +22,10 @@ async function ensureTable() {
|
|
|
21
22
|
PRIMARY KEY (session_id, key)
|
|
22
23
|
)
|
|
23
24
|
`);
|
|
25
|
+
// Older deployments created `updated_at` as 32-bit `INTEGER`; on Postgres
|
|
26
|
+
// the `Date.now()` written by appStatePut() on every turn overflows int4.
|
|
27
|
+
// Widen it in place (no-op once done / on fresh BIGINT databases).
|
|
28
|
+
await widenIntColumnsToBigInt("application_state", ["updated_at"]);
|
|
24
29
|
// Indexes for the two hot poll paths:
|
|
25
30
|
// - `SELECT … WHERE updated_at > ?` (watermark scan, every poll cycle)
|
|
26
31
|
// - `SELECT … WHERE key = ? … ORDER BY updated_at ASC` (marker lookups)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/application-state/store.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n getDbExec,\n isConnectionError,\n isPostgres,\n intType,\n} from \"../db/client.js\";\nimport { emitAppStateChange, emitAppStateDelete } from \"./emitter.js\";\nimport type { StoreWriteOptions } from \"../settings/store.js\";\n\nlet _initPromise: Promise<void> | undefined;\n\n// Escapes LIKE wildcards (`%`, `_`) and the escape char itself so a caller's\n// literal prefix is matched verbatim. Used with `ESCAPE '\\'` in prefix queries\n// below; without this, a prefix such as `user_settings` would treat `_` as a\n// single-char wildcard and over-match (e.g. delete `userXsettings`).\nfunction escapeLike(s: string): string {\n return s.replace(/([\\\\%_])/g, \"\\\\$1\");\n}\n\nasync function ensureTable(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n await client.execute(`\n CREATE TABLE IF NOT EXISTS application_state (\n session_id TEXT NOT NULL,\n key TEXT NOT NULL,\n value TEXT NOT NULL,\n updated_at ${intType()} NOT NULL,\n PRIMARY KEY (session_id, key)\n )\n `);\n // Indexes for the two hot poll paths:\n // - `SELECT … WHERE updated_at > ?` (watermark scan, every poll cycle)\n // - `SELECT … WHERE key = ? … ORDER BY updated_at ASC` (marker lookups)\n // Both are dialect-agnostic (no DESC/partial/PG-only syntax) so they\n // apply identically on SQLite and Postgres. IF NOT EXISTS makes them\n // idempotent across restarts on existing databases.\n for (const ddl of [\n `CREATE INDEX IF NOT EXISTS app_state_updated_at_idx ON application_state (updated_at)`,\n `CREATE INDEX IF NOT EXISTS app_state_key_updated_idx ON application_state (key, updated_at)`,\n ]) {\n try {\n await client.execute(ddl);\n } catch {\n // Index already exists or the dialect rejected a duplicate.\n }\n }\n })().catch((err) => {\n // Retry init on the next call after a failed startup.\n _initPromise = undefined;\n throw err;\n });\n }\n return _initPromise;\n}\n\nexport async function appStateGet(\n sessionId: string,\n key: string,\n): Promise<Record<string, unknown> | null> {\n try {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT value FROM application_state WHERE session_id = ? AND key = ?`,\n args: [sessionId, key],\n });\n if (rows.length === 0) return null;\n return JSON.parse(rows[0].value as string);\n } catch (err) {\n // Transient WS / connection drops (Neon serverless) — caller polls every\n // 2s and will see the value on the next tick. Swallow rather than 500.\n if (isConnectionError(err)) return null;\n throw err;\n }\n}\n\nexport async function appStatePut(\n sessionId: string,\n key: string,\n value: Record<string, unknown>,\n options?: StoreWriteOptions,\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n await client.execute({\n sql: isPostgres()\n ? `INSERT INTO application_state (session_id, key, value, updated_at) VALUES (?, ?, ?, ?) ON CONFLICT (session_id, key) DO UPDATE SET value=EXCLUDED.value, updated_at=EXCLUDED.updated_at`\n : `INSERT OR REPLACE INTO application_state (session_id, key, value, updated_at) VALUES (?, ?, ?, ?)`,\n args: [sessionId, key, JSON.stringify(value), Date.now()],\n });\n emitAppStateChange(key, options?.requestSource, sessionId);\n}\n\nexport async function appStateDelete(\n sessionId: string,\n key: string,\n options?: StoreWriteOptions,\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const result = await client.execute({\n sql: `DELETE FROM application_state WHERE session_id = ? AND key = ?`,\n args: [sessionId, key],\n });\n const deleted = result.rowsAffected > 0;\n if (deleted) emitAppStateDelete(key, options?.requestSource, sessionId);\n return deleted;\n}\n\nexport async function appStateList(\n sessionId: string,\n keyPrefix: string,\n): Promise<Array<{ key: string; value: Record<string, unknown> }>> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT key, value FROM application_state WHERE session_id = ? AND key LIKE ? ESCAPE '\\\\'`,\n args: [sessionId, escapeLike(keyPrefix) + \"%\"],\n });\n return rows.map((row) => ({\n key: row.key as string,\n value: JSON.parse(row.value as string),\n }));\n}\n\nexport async function appStateDeleteByPrefix(\n sessionId: string,\n keyPrefix: string,\n options?: StoreWriteOptions,\n): Promise<number> {\n await ensureTable();\n const client = getDbExec();\n\n // Get keys first so we can emit events\n const { rows } = await client.execute({\n sql: `SELECT key FROM application_state WHERE session_id = ? AND key LIKE ? ESCAPE '\\\\'`,\n args: [sessionId, escapeLike(keyPrefix) + \"%\"],\n });\n\n if (rows.length === 0) return 0;\n\n const result = await client.execute({\n sql: `DELETE FROM application_state WHERE session_id = ? AND key LIKE ? ESCAPE '\\\\'`,\n args: [sessionId, escapeLike(keyPrefix) + \"%\"],\n });\n\n for (const row of rows) {\n emitAppStateDelete(row.key as string, options?.requestSource, sessionId);\n }\n\n return result.rowsAffected;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/application-state/store.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n getDbExec,\n isConnectionError,\n isPostgres,\n intType,\n} from \"../db/client.js\";\nimport { widenIntColumnsToBigInt } from \"../db/widen-columns.js\";\nimport { emitAppStateChange, emitAppStateDelete } from \"./emitter.js\";\nimport type { StoreWriteOptions } from \"../settings/store.js\";\n\nlet _initPromise: Promise<void> | undefined;\n\n// Escapes LIKE wildcards (`%`, `_`) and the escape char itself so a caller's\n// literal prefix is matched verbatim. Used with `ESCAPE '\\'` in prefix queries\n// below; without this, a prefix such as `user_settings` would treat `_` as a\n// single-char wildcard and over-match (e.g. delete `userXsettings`).\nfunction escapeLike(s: string): string {\n return s.replace(/([\\\\%_])/g, \"\\\\$1\");\n}\n\nasync function ensureTable(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n await client.execute(`\n CREATE TABLE IF NOT EXISTS application_state (\n session_id TEXT NOT NULL,\n key TEXT NOT NULL,\n value TEXT NOT NULL,\n updated_at ${intType()} NOT NULL,\n PRIMARY KEY (session_id, key)\n )\n `);\n // Older deployments created `updated_at` as 32-bit `INTEGER`; on Postgres\n // the `Date.now()` written by appStatePut() on every turn overflows int4.\n // Widen it in place (no-op once done / on fresh BIGINT databases).\n await widenIntColumnsToBigInt(\"application_state\", [\"updated_at\"]);\n // Indexes for the two hot poll paths:\n // - `SELECT … WHERE updated_at > ?` (watermark scan, every poll cycle)\n // - `SELECT … WHERE key = ? … ORDER BY updated_at ASC` (marker lookups)\n // Both are dialect-agnostic (no DESC/partial/PG-only syntax) so they\n // apply identically on SQLite and Postgres. IF NOT EXISTS makes them\n // idempotent across restarts on existing databases.\n for (const ddl of [\n `CREATE INDEX IF NOT EXISTS app_state_updated_at_idx ON application_state (updated_at)`,\n `CREATE INDEX IF NOT EXISTS app_state_key_updated_idx ON application_state (key, updated_at)`,\n ]) {\n try {\n await client.execute(ddl);\n } catch {\n // Index already exists or the dialect rejected a duplicate.\n }\n }\n })().catch((err) => {\n // Retry init on the next call after a failed startup.\n _initPromise = undefined;\n throw err;\n });\n }\n return _initPromise;\n}\n\nexport async function appStateGet(\n sessionId: string,\n key: string,\n): Promise<Record<string, unknown> | null> {\n try {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT value FROM application_state WHERE session_id = ? AND key = ?`,\n args: [sessionId, key],\n });\n if (rows.length === 0) return null;\n return JSON.parse(rows[0].value as string);\n } catch (err) {\n // Transient WS / connection drops (Neon serverless) — caller polls every\n // 2s and will see the value on the next tick. Swallow rather than 500.\n if (isConnectionError(err)) return null;\n throw err;\n }\n}\n\nexport async function appStatePut(\n sessionId: string,\n key: string,\n value: Record<string, unknown>,\n options?: StoreWriteOptions,\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n await client.execute({\n sql: isPostgres()\n ? `INSERT INTO application_state (session_id, key, value, updated_at) VALUES (?, ?, ?, ?) ON CONFLICT (session_id, key) DO UPDATE SET value=EXCLUDED.value, updated_at=EXCLUDED.updated_at`\n : `INSERT OR REPLACE INTO application_state (session_id, key, value, updated_at) VALUES (?, ?, ?, ?)`,\n args: [sessionId, key, JSON.stringify(value), Date.now()],\n });\n emitAppStateChange(key, options?.requestSource, sessionId);\n}\n\nexport async function appStateDelete(\n sessionId: string,\n key: string,\n options?: StoreWriteOptions,\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const result = await client.execute({\n sql: `DELETE FROM application_state WHERE session_id = ? AND key = ?`,\n args: [sessionId, key],\n });\n const deleted = result.rowsAffected > 0;\n if (deleted) emitAppStateDelete(key, options?.requestSource, sessionId);\n return deleted;\n}\n\nexport async function appStateList(\n sessionId: string,\n keyPrefix: string,\n): Promise<Array<{ key: string; value: Record<string, unknown> }>> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT key, value FROM application_state WHERE session_id = ? AND key LIKE ? ESCAPE '\\\\'`,\n args: [sessionId, escapeLike(keyPrefix) + \"%\"],\n });\n return rows.map((row) => ({\n key: row.key as string,\n value: JSON.parse(row.value as string),\n }));\n}\n\nexport async function appStateDeleteByPrefix(\n sessionId: string,\n keyPrefix: string,\n options?: StoreWriteOptions,\n): Promise<number> {\n await ensureTable();\n const client = getDbExec();\n\n // Get keys first so we can emit events\n const { rows } = await client.execute({\n sql: `SELECT key FROM application_state WHERE session_id = ? AND key LIKE ? ESCAPE '\\\\'`,\n args: [sessionId, escapeLike(keyPrefix) + \"%\"],\n });\n\n if (rows.length === 0) return 0;\n\n const result = await client.execute({\n sql: `DELETE FROM application_state WHERE session_id = ? AND key LIKE ? ESCAPE '\\\\'`,\n args: [sessionId, escapeLike(keyPrefix) + \"%\"],\n });\n\n for (const row of rows) {\n emitAppStateDelete(row.key as string, options?.requestSource, sessionId);\n }\n\n return result.rowsAffected;\n}\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch a single audit event by id, including its redacted input payload.
|
|
3
|
+
* Scoped to the caller's identity — returns null if they can't access it.
|
|
4
|
+
*/
|
|
5
|
+
declare const _default: import("../../action.js").ActionDefinition<{
|
|
6
|
+
id: string;
|
|
7
|
+
}, {
|
|
8
|
+
event: import("../types.js").AuditEvent;
|
|
9
|
+
}>;
|
|
10
|
+
export default _default;
|
|
11
|
+
//# sourceMappingURL=get-audit-event.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-audit-event.d.ts","sourceRoot":"","sources":["../../../src/audit/actions/get-audit-event.ts"],"names":[],"mappings":"AAIA;;;GAGG;;;;;;AACH,wBAcG"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { defineAction } from "../../action.js";
|
|
3
|
+
import { getAuditEventById } from "../store.js";
|
|
4
|
+
/**
|
|
5
|
+
* Fetch a single audit event by id, including its redacted input payload.
|
|
6
|
+
* Scoped to the caller's identity — returns null if they can't access it.
|
|
7
|
+
*/
|
|
8
|
+
export default defineAction({
|
|
9
|
+
description: "Get one audit-log event by id, with its full redacted input payload. Returns null if you don't have access to it.",
|
|
10
|
+
schema: z.object({
|
|
11
|
+
id: z.string().describe("The audit event id."),
|
|
12
|
+
}),
|
|
13
|
+
http: { method: "GET" },
|
|
14
|
+
run: async (args, ctx) => {
|
|
15
|
+
const event = await getAuditEventById(args.id, {
|
|
16
|
+
userEmail: ctx?.userEmail,
|
|
17
|
+
orgId: ctx?.orgId ?? null,
|
|
18
|
+
});
|
|
19
|
+
return { event };
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
//# sourceMappingURL=get-audit-event.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-audit-event.js","sourceRoot":"","sources":["../../../src/audit/actions/get-audit-event.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD;;;GAGG;AACH,eAAe,YAAY,CAAC;IAC1B,WAAW,EACT,mHAAmH;IACrH,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,qBAAqB,CAAC;KAC/C,CAAC;IACF,IAAI,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;IACvB,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;QACvB,MAAM,KAAK,GAAG,MAAM,iBAAiB,CAAC,IAAI,CAAC,EAAE,EAAE;YAC7C,SAAS,EAAE,GAAG,EAAE,SAAS;YACzB,KAAK,EAAE,GAAG,EAAE,KAAK,IAAI,IAAI;SAC1B,CAAC,CAAC;QACH,OAAO,EAAE,KAAK,EAAE,CAAC;IACnB,CAAC;CACF,CAAC,CAAC","sourcesContent":["import { z } from \"zod\";\nimport { defineAction } from \"../../action.js\";\nimport { getAuditEventById } from \"../store.js\";\n\n/**\n * Fetch a single audit event by id, including its redacted input payload.\n * Scoped to the caller's identity — returns null if they can't access it.\n */\nexport default defineAction({\n description:\n \"Get one audit-log event by id, with its full redacted input payload. Returns null if you don't have access to it.\",\n schema: z.object({\n id: z.string().describe(\"The audit event id.\"),\n }),\n http: { method: \"GET\" },\n run: async (args, ctx) => {\n const event = await getAuditEventById(args.id, {\n userEmail: ctx?.userEmail,\n orgId: ctx?.orgId ?? null,\n });\n return { event };\n },\n});\n"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* List audit-log events the current user can see — their own actions plus the
|
|
3
|
+
* agent's actions on their behalf, scoped in SQL to the caller's identity and
|
|
4
|
+
* org. Read-only; never exposes other tenants' rows.
|
|
5
|
+
*/
|
|
6
|
+
declare const _default: import("../../action.js").ActionDefinition<{
|
|
7
|
+
targetType?: string;
|
|
8
|
+
targetId?: string;
|
|
9
|
+
actorKind?: "agent" | "system" | "human";
|
|
10
|
+
actorEmail?: string;
|
|
11
|
+
status?: "error" | "success" | "denied";
|
|
12
|
+
threadId?: string;
|
|
13
|
+
turnId?: string;
|
|
14
|
+
action?: string;
|
|
15
|
+
sinceMs?: number;
|
|
16
|
+
limit?: number;
|
|
17
|
+
}, {
|
|
18
|
+
events: import("../types.js").AuditEvent[];
|
|
19
|
+
count: number;
|
|
20
|
+
}>;
|
|
21
|
+
export default _default;
|
|
22
|
+
//# sourceMappingURL=list-audit-events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-audit-events.d.ts","sourceRoot":"","sources":["../../../src/audit/actions/list-audit-events.ts"],"names":[],"mappings":"AAIA;;;;GAIG;;;;;;;;;;;;;;;;AACH,wBAuDG"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { defineAction } from "../../action.js";
|
|
3
|
+
import { queryAuditEvents } from "../store.js";
|
|
4
|
+
/**
|
|
5
|
+
* List audit-log events the current user can see — their own actions plus the
|
|
6
|
+
* agent's actions on their behalf, scoped in SQL to the caller's identity and
|
|
7
|
+
* org. Read-only; never exposes other tenants' rows.
|
|
8
|
+
*/
|
|
9
|
+
export default defineAction({
|
|
10
|
+
description: "List audit-log events (who changed what, when, and whether it was you or the agent) for resources you can access. Supports filtering by target resource, actor (agent vs human), status, agent thread/turn, and time. Use this to answer 'what did the agent change', 'who edited this record', or 'show recent changes'.",
|
|
11
|
+
schema: z.object({
|
|
12
|
+
targetType: z
|
|
13
|
+
.string()
|
|
14
|
+
.optional()
|
|
15
|
+
.describe("Filter to one resource type, e.g. 'recording'."),
|
|
16
|
+
targetId: z
|
|
17
|
+
.string()
|
|
18
|
+
.optional()
|
|
19
|
+
.describe("Filter to one resource id (pair with targetType)."),
|
|
20
|
+
actorKind: z
|
|
21
|
+
.enum(["agent", "human", "system"])
|
|
22
|
+
.optional()
|
|
23
|
+
.describe("Filter to changes made by the agent, a human, or the system."),
|
|
24
|
+
actorEmail: z.string().optional().describe("Filter to one actor's email."),
|
|
25
|
+
status: z
|
|
26
|
+
.enum(["success", "error", "denied"])
|
|
27
|
+
.optional()
|
|
28
|
+
.describe("Filter by outcome."),
|
|
29
|
+
threadId: z.string().optional().describe("Filter to one agent thread."),
|
|
30
|
+
turnId: z
|
|
31
|
+
.string()
|
|
32
|
+
.optional()
|
|
33
|
+
.describe("Filter to one agent turn (a single agent response)."),
|
|
34
|
+
action: z.string().optional().describe("Filter to one action name."),
|
|
35
|
+
sinceMs: z
|
|
36
|
+
.number()
|
|
37
|
+
.optional()
|
|
38
|
+
.describe("Only events at or after this Unix epoch (ms)."),
|
|
39
|
+
limit: z
|
|
40
|
+
.number()
|
|
41
|
+
.optional()
|
|
42
|
+
.describe("Max events to return (default 100, max 500)."),
|
|
43
|
+
}),
|
|
44
|
+
http: { method: "GET" },
|
|
45
|
+
run: async (args, ctx) => {
|
|
46
|
+
const events = await queryAuditEvents({ userEmail: ctx?.userEmail, orgId: ctx?.orgId ?? null }, {
|
|
47
|
+
...(args.targetType ? { targetType: args.targetType } : {}),
|
|
48
|
+
...(args.targetId ? { targetId: args.targetId } : {}),
|
|
49
|
+
...(args.actorKind ? { actorKind: args.actorKind } : {}),
|
|
50
|
+
...(args.actorEmail ? { actorEmail: args.actorEmail } : {}),
|
|
51
|
+
...(args.status ? { status: args.status } : {}),
|
|
52
|
+
...(args.threadId ? { threadId: args.threadId } : {}),
|
|
53
|
+
...(args.turnId ? { turnId: args.turnId } : {}),
|
|
54
|
+
...(args.action ? { action: args.action } : {}),
|
|
55
|
+
...(typeof args.sinceMs === "number" ? { sinceMs: args.sinceMs } : {}),
|
|
56
|
+
...(typeof args.limit === "number" ? { limit: args.limit } : {}),
|
|
57
|
+
});
|
|
58
|
+
return { events, count: events.length };
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
//# sourceMappingURL=list-audit-events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-audit-events.js","sourceRoot":"","sources":["../../../src/audit/actions/list-audit-events.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from \"zod\";\nimport { defineAction } from \"../../action.js\";\nimport { queryAuditEvents } from \"../store.js\";\n\n/**\n * List audit-log events the current user can see — their own actions plus the\n * agent's actions on their behalf, scoped in SQL to the caller's identity and\n * org. Read-only; never exposes other tenants' rows.\n */\nexport default defineAction({\n description:\n \"List audit-log events (who changed what, when, and whether it was you or the agent) for resources you can access. Supports filtering by target resource, actor (agent vs human), status, agent thread/turn, and time. Use this to answer 'what did the agent change', 'who edited this record', or 'show recent changes'.\",\n schema: z.object({\n targetType: z\n .string()\n .optional()\n .describe(\"Filter to one resource type, e.g. 'recording'.\"),\n targetId: z\n .string()\n .optional()\n .describe(\"Filter to one resource id (pair with targetType).\"),\n actorKind: z\n .enum([\"agent\", \"human\", \"system\"])\n .optional()\n .describe(\"Filter to changes made by the agent, a human, or the system.\"),\n actorEmail: z.string().optional().describe(\"Filter to one actor's email.\"),\n status: z\n .enum([\"success\", \"error\", \"denied\"])\n .optional()\n .describe(\"Filter by outcome.\"),\n threadId: z.string().optional().describe(\"Filter to one agent thread.\"),\n turnId: z\n .string()\n .optional()\n .describe(\"Filter to one agent turn (a single agent response).\"),\n action: z.string().optional().describe(\"Filter to one action name.\"),\n sinceMs: z\n .number()\n .optional()\n .describe(\"Only events at or after this Unix epoch (ms).\"),\n limit: z\n .number()\n .optional()\n .describe(\"Max events to return (default 100, max 500).\"),\n }),\n http: { method: \"GET\" },\n run: async (args, ctx) => {\n const events = await queryAuditEvents(\n { userEmail: ctx?.userEmail, orgId: ctx?.orgId ?? null },\n {\n ...(args.targetType ? { targetType: args.targetType } : {}),\n ...(args.targetId ? { targetId: args.targetId } : {}),\n ...(args.actorKind ? { actorKind: args.actorKind } : {}),\n ...(args.actorEmail ? { actorEmail: args.actorEmail } : {}),\n ...(args.status ? { status: args.status } : {}),\n ...(args.threadId ? { threadId: args.threadId } : {}),\n ...(args.turnId ? { turnId: args.turnId } : {}),\n ...(args.action ? { action: args.action } : {}),\n ...(typeof args.sinceMs === \"number\" ? { sinceMs: args.sinceMs } : {}),\n ...(typeof args.limit === \"number\" ? { limit: args.limit } : {}),\n },\n );\n return { events, count: events.length };\n },\n});\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run the audit cleanup once. Returns the deleted row count, or null when
|
|
3
|
+
* retention is disabled (`AGENT_NATIVE_AUDIT_RETENTION_DAYS=0`).
|
|
4
|
+
*/
|
|
5
|
+
export declare function runAuditCleanupOnce(): Promise<number | null>;
|
|
6
|
+
/**
|
|
7
|
+
* Start the recurring audit-cleanup job. Idempotent — calling more than once is
|
|
8
|
+
* a no-op while a previous schedule is active. Returns a stop function.
|
|
9
|
+
*/
|
|
10
|
+
export declare function startAuditCleanupJob(): () => void;
|
|
11
|
+
export declare function stopAuditCleanupJob(): void;
|
|
12
|
+
//# sourceMappingURL=cleanup-job.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cleanup-job.d.ts","sourceRoot":"","sources":["../../src/audit/cleanup-job.ts"],"names":[],"mappings":"AA+BA;;;GAGG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAKlE;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,IAAI,CA0CjD;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAS1C"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit-log retention job.
|
|
3
|
+
*
|
|
4
|
+
* Periodically purges `agent_audit_log` rows older than the configured horizon
|
|
5
|
+
* so the log doesn't grow unbounded. Retention is configurable via
|
|
6
|
+
* `AGENT_NATIVE_AUDIT_RETENTION_DAYS` (default: 365 days — audit trails are kept
|
|
7
|
+
* far longer than sampled traces). Setting it to `0` disables the purge (keep
|
|
8
|
+
* forever). Mirrors observability/cleanup-job.ts.
|
|
9
|
+
*
|
|
10
|
+
* Runs once on startup after a short delay, then on a 24-hour interval. Timers
|
|
11
|
+
* are unref'd so they never keep the process alive on their own.
|
|
12
|
+
*/
|
|
13
|
+
import { deleteOldAuditEvents } from "./store.js";
|
|
14
|
+
const DEFAULT_RETENTION_DAYS = 365;
|
|
15
|
+
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
|
|
16
|
+
const STARTUP_DELAY_MS = 5 * 60 * 1000;
|
|
17
|
+
let _cleanupTimer = null;
|
|
18
|
+
let _intervalTimer = null;
|
|
19
|
+
function resolveRetentionDays() {
|
|
20
|
+
const raw = process.env.AGENT_NATIVE_AUDIT_RETENTION_DAYS;
|
|
21
|
+
if (raw === undefined || raw === null || raw === "") {
|
|
22
|
+
return DEFAULT_RETENTION_DAYS;
|
|
23
|
+
}
|
|
24
|
+
const parsed = Number.parseInt(raw, 10);
|
|
25
|
+
if (!Number.isFinite(parsed) || parsed < 0)
|
|
26
|
+
return DEFAULT_RETENTION_DAYS;
|
|
27
|
+
return parsed;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Run the audit cleanup once. Returns the deleted row count, or null when
|
|
31
|
+
* retention is disabled (`AGENT_NATIVE_AUDIT_RETENTION_DAYS=0`).
|
|
32
|
+
*/
|
|
33
|
+
export async function runAuditCleanupOnce() {
|
|
34
|
+
const days = resolveRetentionDays();
|
|
35
|
+
if (days === 0)
|
|
36
|
+
return null;
|
|
37
|
+
const cutoff = Date.now() - days * ONE_DAY_MS;
|
|
38
|
+
return deleteOldAuditEvents(cutoff);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Start the recurring audit-cleanup job. Idempotent — calling more than once is
|
|
42
|
+
* a no-op while a previous schedule is active. Returns a stop function.
|
|
43
|
+
*/
|
|
44
|
+
export function startAuditCleanupJob() {
|
|
45
|
+
if (_cleanupTimer || _intervalTimer)
|
|
46
|
+
return stopAuditCleanupJob;
|
|
47
|
+
const days = resolveRetentionDays();
|
|
48
|
+
if (days === 0) {
|
|
49
|
+
if (process.env.DEBUG)
|
|
50
|
+
// eslint-disable-next-line no-console
|
|
51
|
+
console.log("[audit] Audit cleanup disabled (AGENT_NATIVE_AUDIT_RETENTION_DAYS=0)");
|
|
52
|
+
return () => { };
|
|
53
|
+
}
|
|
54
|
+
const tick = () => {
|
|
55
|
+
runAuditCleanupOnce()
|
|
56
|
+
.then((deleted) => {
|
|
57
|
+
if (deleted == null)
|
|
58
|
+
return;
|
|
59
|
+
if (process.env.DEBUG) {
|
|
60
|
+
// eslint-disable-next-line no-console
|
|
61
|
+
console.log(`[audit] Audit cleanup purged ${deleted} rows (retention=${days}d)`);
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
.catch((err) => {
|
|
65
|
+
// eslint-disable-next-line no-console
|
|
66
|
+
console.error("[audit] Audit cleanup failed:", err?.message ?? err);
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
_cleanupTimer = setTimeout(() => {
|
|
70
|
+
_cleanupTimer = null;
|
|
71
|
+
tick();
|
|
72
|
+
_intervalTimer = setInterval(tick, ONE_DAY_MS);
|
|
73
|
+
if (typeof _intervalTimer.unref === "function")
|
|
74
|
+
_intervalTimer.unref();
|
|
75
|
+
}, STARTUP_DELAY_MS);
|
|
76
|
+
if (typeof _cleanupTimer.unref === "function")
|
|
77
|
+
_cleanupTimer.unref();
|
|
78
|
+
if (process.env.DEBUG)
|
|
79
|
+
// eslint-disable-next-line no-console
|
|
80
|
+
console.log(`[audit] Audit cleanup scheduled (retention=${days}d, daily)`);
|
|
81
|
+
return stopAuditCleanupJob;
|
|
82
|
+
}
|
|
83
|
+
export function stopAuditCleanupJob() {
|
|
84
|
+
if (_cleanupTimer) {
|
|
85
|
+
clearTimeout(_cleanupTimer);
|
|
86
|
+
_cleanupTimer = null;
|
|
87
|
+
}
|
|
88
|
+
if (_intervalTimer) {
|
|
89
|
+
clearInterval(_intervalTimer);
|
|
90
|
+
_intervalTimer = null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=cleanup-job.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cleanup-job.js","sourceRoot":"","sources":["../../src/audit/cleanup-job.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Audit-log retention job.\n *\n * Periodically purges `agent_audit_log` rows older than the configured horizon\n * so the log doesn't grow unbounded. Retention is configurable via\n * `AGENT_NATIVE_AUDIT_RETENTION_DAYS` (default: 365 days — audit trails are kept\n * far longer than sampled traces). Setting it to `0` disables the purge (keep\n * forever). Mirrors observability/cleanup-job.ts.\n *\n * Runs once on startup after a short delay, then on a 24-hour interval. Timers\n * are unref'd so they never keep the process alive on their own.\n */\nimport { deleteOldAuditEvents } from \"./store.js\";\n\nconst DEFAULT_RETENTION_DAYS = 365;\nconst ONE_DAY_MS = 24 * 60 * 60 * 1000;\nconst STARTUP_DELAY_MS = 5 * 60 * 1000;\n\nlet _cleanupTimer: NodeJS.Timeout | null = null;\nlet _intervalTimer: NodeJS.Timeout | null = null;\n\nfunction resolveRetentionDays(): number {\n const raw = process.env.AGENT_NATIVE_AUDIT_RETENTION_DAYS;\n if (raw === undefined || raw === null || raw === \"\") {\n return DEFAULT_RETENTION_DAYS;\n }\n const parsed = Number.parseInt(raw, 10);\n if (!Number.isFinite(parsed) || parsed < 0) return DEFAULT_RETENTION_DAYS;\n return parsed;\n}\n\n/**\n * Run the audit cleanup once. Returns the deleted row count, or null when\n * retention is disabled (`AGENT_NATIVE_AUDIT_RETENTION_DAYS=0`).\n */\nexport async function runAuditCleanupOnce(): Promise<number | null> {\n const days = resolveRetentionDays();\n if (days === 0) return null;\n const cutoff = Date.now() - days * ONE_DAY_MS;\n return deleteOldAuditEvents(cutoff);\n}\n\n/**\n * Start the recurring audit-cleanup job. Idempotent — calling more than once is\n * a no-op while a previous schedule is active. Returns a stop function.\n */\nexport function startAuditCleanupJob(): () => void {\n if (_cleanupTimer || _intervalTimer) return stopAuditCleanupJob;\n const days = resolveRetentionDays();\n if (days === 0) {\n if (process.env.DEBUG)\n // eslint-disable-next-line no-console\n console.log(\n \"[audit] Audit cleanup disabled (AGENT_NATIVE_AUDIT_RETENTION_DAYS=0)\",\n );\n return () => {};\n }\n\n const tick = () => {\n runAuditCleanupOnce()\n .then((deleted) => {\n if (deleted == null) return;\n if (process.env.DEBUG) {\n // eslint-disable-next-line no-console\n console.log(\n `[audit] Audit cleanup purged ${deleted} rows (retention=${days}d)`,\n );\n }\n })\n .catch((err) => {\n // eslint-disable-next-line no-console\n console.error(\"[audit] Audit cleanup failed:\", err?.message ?? err);\n });\n };\n\n _cleanupTimer = setTimeout(() => {\n _cleanupTimer = null;\n tick();\n _intervalTimer = setInterval(tick, ONE_DAY_MS);\n if (typeof _intervalTimer.unref === \"function\") _intervalTimer.unref();\n }, STARTUP_DELAY_MS);\n if (typeof _cleanupTimer.unref === \"function\") _cleanupTimer.unref();\n\n if (process.env.DEBUG)\n // eslint-disable-next-line no-console\n console.log(`[audit] Audit cleanup scheduled (retention=${days}d, daily)`);\n\n return stopAuditCleanupJob;\n}\n\nexport function stopAuditCleanupJob(): void {\n if (_cleanupTimer) {\n clearTimeout(_cleanupTimer);\n _cleanupTimer = null;\n }\n if (_intervalTimer) {\n clearInterval(_intervalTimer);\n _intervalTimer = null;\n }\n}\n"]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure (no-DB) audit configuration helpers.
|
|
3
|
+
*
|
|
4
|
+
* Kept free of any database / store import so `action.ts` can statically
|
|
5
|
+
* import them without pulling the DB client into every bundle that touches an
|
|
6
|
+
* action. The DB-touching recorder lives in `record.ts`, which `action.ts`
|
|
7
|
+
* loads lazily via dynamic import on first audited call.
|
|
8
|
+
*/
|
|
9
|
+
import type { ActionAuditConfig, AuditActorKind, AuditStatus } from "./types.js";
|
|
10
|
+
/** Normalize a raw `audit` option into a config object (or undefined). */
|
|
11
|
+
export declare function normalizeAuditConfig(raw: unknown): ActionAuditConfig | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Whether to attach the audit wrapper to an action at definition time. Decided
|
|
14
|
+
* from the action's resolved `readOnly` flag plus its config — the action's
|
|
15
|
+
* *name* isn't known here (it's the registry key), so name-based skipping
|
|
16
|
+
* happens later in `shouldRecordAudit`.
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolveAuditAttach(config: ActionAuditConfig | undefined, readOnly: boolean | undefined): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Final, name-aware decision made at record time. Explicit `enabled: true`
|
|
21
|
+
* overrides the high-frequency denylist; otherwise denylisted names are
|
|
22
|
+
* dropped.
|
|
23
|
+
*/
|
|
24
|
+
export declare function shouldRecordAudit(config: ActionAuditConfig | undefined, actionName: string): boolean;
|
|
25
|
+
/** Derive the actor kind from the invocation surface + resolved identity. */
|
|
26
|
+
export declare function deriveActorKind(caller: string | undefined, actorEmail: string | undefined | null): AuditActorKind;
|
|
27
|
+
/** Whether the whole subsystem is disabled via env. */
|
|
28
|
+
export declare function isAuditDisabled(): boolean;
|
|
29
|
+
export type { AuditStatus };
|
|
30
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/audit/config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EACV,iBAAiB,EACjB,cAAc,EACd,WAAW,EACZ,MAAM,YAAY,CAAC;AAwBpB,0EAA0E;AAC1E,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,OAAO,GACX,iBAAiB,GAAG,SAAS,CAG/B;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,iBAAiB,GAAG,SAAS,EACrC,QAAQ,EAAE,OAAO,GAAG,SAAS,GAC5B,OAAO,CAMT;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,iBAAiB,GAAG,SAAS,EACrC,UAAU,EAAE,MAAM,GACjB,OAAO,CAKT;AAED,6EAA6E;AAC7E,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GACpC,cAAc,CAGhB;AAED,uDAAuD;AACvD,wBAAgB,eAAe,IAAI,OAAO,CAEzC;AAED,YAAY,EAAE,WAAW,EAAE,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* High-frequency / ephemeral framework actions that are not meaningful audit
|
|
3
|
+
* events. They mutate UI/agent-context state many times per session; auditing
|
|
4
|
+
* each one would flood the log without recording a real user-facing change.
|
|
5
|
+
*
|
|
6
|
+
* An action can still force itself on with `audit: { enabled: true }` —
|
|
7
|
+
* explicit config always wins over this denylist (see `shouldRecordAudit`).
|
|
8
|
+
*/
|
|
9
|
+
const DEFAULT_SKIP_ACTIONS = new Set([
|
|
10
|
+
"context-pin",
|
|
11
|
+
"context-evict",
|
|
12
|
+
"context-restore",
|
|
13
|
+
"context-report",
|
|
14
|
+
"context-manifest-get",
|
|
15
|
+
"change-appearance",
|
|
16
|
+
"toggle-demo-mode",
|
|
17
|
+
]);
|
|
18
|
+
/** Name patterns for high-frequency state-sync actions, skipped by default. */
|
|
19
|
+
const DEFAULT_SKIP_PATTERN = /(application-state|app-state|set-state|view-screen|navigate|poll)/i;
|
|
20
|
+
/** Normalize a raw `audit` option into a config object (or undefined). */
|
|
21
|
+
export function normalizeAuditConfig(raw) {
|
|
22
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
23
|
+
return undefined;
|
|
24
|
+
return raw;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Whether to attach the audit wrapper to an action at definition time. Decided
|
|
28
|
+
* from the action's resolved `readOnly` flag plus its config — the action's
|
|
29
|
+
* *name* isn't known here (it's the registry key), so name-based skipping
|
|
30
|
+
* happens later in `shouldRecordAudit`.
|
|
31
|
+
*/
|
|
32
|
+
export function resolveAuditAttach(config, readOnly) {
|
|
33
|
+
if (config && typeof config.enabled === "boolean")
|
|
34
|
+
return config.enabled;
|
|
35
|
+
// Read-only actions are not audited unless they opt in via `onRead`.
|
|
36
|
+
if (readOnly === true)
|
|
37
|
+
return config?.onRead === true;
|
|
38
|
+
// Everything else mutates — audit by default.
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Final, name-aware decision made at record time. Explicit `enabled: true`
|
|
43
|
+
* overrides the high-frequency denylist; otherwise denylisted names are
|
|
44
|
+
* dropped.
|
|
45
|
+
*/
|
|
46
|
+
export function shouldRecordAudit(config, actionName) {
|
|
47
|
+
if (config && config.enabled === true)
|
|
48
|
+
return true;
|
|
49
|
+
if (DEFAULT_SKIP_ACTIONS.has(actionName))
|
|
50
|
+
return false;
|
|
51
|
+
if (DEFAULT_SKIP_PATTERN.test(actionName))
|
|
52
|
+
return false;
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
/** Derive the actor kind from the invocation surface + resolved identity. */
|
|
56
|
+
export function deriveActorKind(caller, actorEmail) {
|
|
57
|
+
if (caller === "tool")
|
|
58
|
+
return "agent";
|
|
59
|
+
return actorEmail ? "human" : "system";
|
|
60
|
+
}
|
|
61
|
+
/** Whether the whole subsystem is disabled via env. */
|
|
62
|
+
export function isAuditDisabled() {
|
|
63
|
+
return process.env.AGENT_NATIVE_AUDIT_ENABLED === "false";
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/audit/config.ts"],"names":[],"mappings":"AAcA;;;;;;;GAOG;AACH,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAS;IAC3C,aAAa;IACb,eAAe;IACf,iBAAiB;IACjB,gBAAgB;IAChB,sBAAsB;IACtB,mBAAmB;IACnB,kBAAkB;CACnB,CAAC,CAAC;AAEH,+EAA+E;AAC/E,MAAM,oBAAoB,GACxB,oEAAoE,CAAC;AAEvE,0EAA0E;AAC1E,MAAM,UAAU,oBAAoB,CAClC,GAAY;IAEZ,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAC5E,OAAO,GAAwB,CAAC;AAClC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAChC,MAAqC,EACrC,QAA6B;IAE7B,IAAI,MAAM,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC,OAAO,CAAC;IACzE,qEAAqE;IACrE,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACtD,8CAA8C;IAC9C,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAAqC,EACrC,UAAkB;IAElB,IAAI,MAAM,IAAI,MAAM,CAAC,OAAO,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACnD,IAAI,oBAAoB,CAAC,GAAG,CAAC,UAAU,CAAC;QAAE,OAAO,KAAK,CAAC;IACvD,IAAI,oBAAoB,CAAC,IAAI,CAAC,UAAU,CAAC;QAAE,OAAO,KAAK,CAAC;IACxD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,eAAe,CAC7B,MAA0B,EAC1B,UAAqC;IAErC,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO,OAAO,CAAC;IACtC,OAAO,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC;AACzC,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,eAAe;IAC7B,OAAO,OAAO,CAAC,GAAG,CAAC,0BAA0B,KAAK,OAAO,CAAC;AAC5D,CAAC","sourcesContent":["/**\n * Pure (no-DB) audit configuration helpers.\n *\n * Kept free of any database / store import so `action.ts` can statically\n * import them without pulling the DB client into every bundle that touches an\n * action. The DB-touching recorder lives in `record.ts`, which `action.ts`\n * loads lazily via dynamic import on first audited call.\n */\nimport type {\n ActionAuditConfig,\n AuditActorKind,\n AuditStatus,\n} from \"./types.js\";\n\n/**\n * High-frequency / ephemeral framework actions that are not meaningful audit\n * events. They mutate UI/agent-context state many times per session; auditing\n * each one would flood the log without recording a real user-facing change.\n *\n * An action can still force itself on with `audit: { enabled: true }` —\n * explicit config always wins over this denylist (see `shouldRecordAudit`).\n */\nconst DEFAULT_SKIP_ACTIONS = new Set<string>([\n \"context-pin\",\n \"context-evict\",\n \"context-restore\",\n \"context-report\",\n \"context-manifest-get\",\n \"change-appearance\",\n \"toggle-demo-mode\",\n]);\n\n/** Name patterns for high-frequency state-sync actions, skipped by default. */\nconst DEFAULT_SKIP_PATTERN =\n /(application-state|app-state|set-state|view-screen|navigate|poll)/i;\n\n/** Normalize a raw `audit` option into a config object (or undefined). */\nexport function normalizeAuditConfig(\n raw: unknown,\n): ActionAuditConfig | undefined {\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) return undefined;\n return raw as ActionAuditConfig;\n}\n\n/**\n * Whether to attach the audit wrapper to an action at definition time. Decided\n * from the action's resolved `readOnly` flag plus its config — the action's\n * *name* isn't known here (it's the registry key), so name-based skipping\n * happens later in `shouldRecordAudit`.\n */\nexport function resolveAuditAttach(\n config: ActionAuditConfig | undefined,\n readOnly: boolean | undefined,\n): boolean {\n if (config && typeof config.enabled === \"boolean\") return config.enabled;\n // Read-only actions are not audited unless they opt in via `onRead`.\n if (readOnly === true) return config?.onRead === true;\n // Everything else mutates — audit by default.\n return true;\n}\n\n/**\n * Final, name-aware decision made at record time. Explicit `enabled: true`\n * overrides the high-frequency denylist; otherwise denylisted names are\n * dropped.\n */\nexport function shouldRecordAudit(\n config: ActionAuditConfig | undefined,\n actionName: string,\n): boolean {\n if (config && config.enabled === true) return true;\n if (DEFAULT_SKIP_ACTIONS.has(actionName)) return false;\n if (DEFAULT_SKIP_PATTERN.test(actionName)) return false;\n return true;\n}\n\n/** Derive the actor kind from the invocation surface + resolved identity. */\nexport function deriveActorKind(\n caller: string | undefined,\n actorEmail: string | undefined | null,\n): AuditActorKind {\n if (caller === \"tool\") return \"agent\";\n return actorEmail ? \"human\" : \"system\";\n}\n\n/** Whether the whole subsystem is disabled via env. */\nexport function isAuditDisabled(): boolean {\n return process.env.AGENT_NATIVE_AUDIT_ENABLED === \"false\";\n}\n\nexport type { AuditStatus };\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework audit log — a durable, complete, access-scoped, append-only record
|
|
3
|
+
* of who mutated what app data, when, from where, and (for the agent) in which
|
|
4
|
+
* run. Distinct from observability (sampled telemetry) and tracking
|
|
5
|
+
* (fire-and-forget analytics). Capture is automatic at the `defineAction` seam.
|
|
6
|
+
*/
|
|
7
|
+
export type { ActionAuditConfig, AuditActorKind, AuditCallMeta, AuditEvent, AuditQueryFilters, AuditStatus, AuditTarget, AuditVisibility, } from "./types.js";
|
|
8
|
+
export { deriveActorKind, isAuditDisabled, normalizeAuditConfig, resolveAuditAttach, shouldRecordAudit, } from "./config.js";
|
|
9
|
+
export { redactArgsToJson } from "./redact.js";
|
|
10
|
+
export { ensureAuditTables, insertAuditEvent, queryAuditEvents, getAuditEventById, deleteOldAuditEvents, type AuditReadScope, } from "./store.js";
|
|
11
|
+
export { recordActionAudit } from "./record.js";
|
|
12
|
+
export { runAuditCleanupOnce, startAuditCleanupJob, stopAuditCleanupJob, } from "./cleanup-job.js";
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/audit/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,YAAY,EACV,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,UAAU,EACV,iBAAiB,EACjB,WAAW,EACX,WAAW,EACX,eAAe,GAChB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,eAAe,EACf,eAAe,EACf,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,KAAK,cAAc,GACpB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { deriveActorKind, isAuditDisabled, normalizeAuditConfig, resolveAuditAttach, shouldRecordAudit, } from "./config.js";
|
|
2
|
+
export { redactArgsToJson } from "./redact.js";
|
|
3
|
+
export { ensureAuditTables, insertAuditEvent, queryAuditEvents, getAuditEventById, deleteOldAuditEvents, } from "./store.js";
|
|
4
|
+
export { recordActionAudit } from "./record.js";
|
|
5
|
+
export { runAuditCleanupOnce, startAuditCleanupJob, stopAuditCleanupJob, } from "./cleanup-job.js";
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/audit/index.ts"],"names":[],"mappings":"AAiBA,OAAO,EACL,eAAe,EACf,eAAe,EACf,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,GAErB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC","sourcesContent":["/**\n * Framework audit log — a durable, complete, access-scoped, append-only record\n * of who mutated what app data, when, from where, and (for the agent) in which\n * run. Distinct from observability (sampled telemetry) and tracking\n * (fire-and-forget analytics). Capture is automatic at the `defineAction` seam.\n */\nexport type {\n ActionAuditConfig,\n AuditActorKind,\n AuditCallMeta,\n AuditEvent,\n AuditQueryFilters,\n AuditStatus,\n AuditTarget,\n AuditVisibility,\n} from \"./types.js\";\n\nexport {\n deriveActorKind,\n isAuditDisabled,\n normalizeAuditConfig,\n resolveAuditAttach,\n shouldRecordAudit,\n} from \"./config.js\";\n\nexport { redactArgsToJson } from \"./redact.js\";\n\nexport {\n ensureAuditTables,\n insertAuditEvent,\n queryAuditEvents,\n getAuditEventById,\n deleteOldAuditEvents,\n type AuditReadScope,\n} from \"./store.js\";\n\nexport { recordActionAudit } from \"./record.js\";\n\nexport {\n runAuditCleanupOnce,\n startAuditCleanupJob,\n stopAuditCleanupJob,\n} from \"./cleanup-job.js\";\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ActionAuditConfig, AuditStatus } from "./types.js";
|
|
2
|
+
/** Minimal view of the action run context the recorder needs. */
|
|
3
|
+
export interface AuditRunContextLike {
|
|
4
|
+
actionName?: string;
|
|
5
|
+
caller?: string;
|
|
6
|
+
userEmail?: string;
|
|
7
|
+
orgId?: string | null;
|
|
8
|
+
threadId?: string;
|
|
9
|
+
turnId?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface RecordActionAuditInput {
|
|
12
|
+
config: ActionAuditConfig | undefined;
|
|
13
|
+
args: unknown;
|
|
14
|
+
ctx: AuditRunContextLike | undefined;
|
|
15
|
+
status: AuditStatus;
|
|
16
|
+
result?: unknown;
|
|
17
|
+
error?: unknown;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Record one audit event. Resolves the actor, target, ownership (for scoped
|
|
21
|
+
* reads), and redacted inputs, then appends a row. Never throws.
|
|
22
|
+
*/
|
|
23
|
+
export declare function recordActionAudit(input: RecordActionAuditInput): Promise<void>;
|
|
24
|
+
//# sourceMappingURL=record.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../../src/audit/record.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EACV,iBAAiB,EAGjB,WAAW,EAEZ,MAAM,YAAY,CAAC;AAEpB,iEAAiE;AACjE,MAAM,WAAW,mBAAmB;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,iBAAiB,GAAG,SAAS,CAAC;IACtC,IAAI,EAAE,OAAO,CAAC;IACd,GAAG,EAAE,mBAAmB,GAAG,SAAS,CAAC;IACrC,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AA0CD;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,sBAAsB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAqDf"}
|