@vellumai/assistant 0.8.11 → 0.8.12-staging.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +15 -17
- package/README.md +0 -6
- package/bun.lock +6 -122
- package/node_modules/@vellumai/gateway-client/bun.lock +1 -0
- package/node_modules/@vellumai/gateway-client/package.json +3 -1
- package/node_modules/@vellumai/gateway-client/src/__tests__/gateway-client.test.ts +1 -1
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +87 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +3 -5
- package/openapi.yaml +633 -4
- package/package.json +1 -3
- package/src/__tests__/adaptive-thinking-repair.test.ts +185 -0
- package/src/__tests__/agent-loop-compaction-events.test.ts +7 -6
- package/src/__tests__/anthropic-provider.test.ts +129 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +4 -1
- package/src/__tests__/btw-routes.test.ts +7 -34
- package/src/__tests__/checker.test.ts +6 -12
- package/src/__tests__/config-loader-backfill.test.ts +4 -2
- package/src/__tests__/config-loader-quarantine-notice.test.ts +167 -0
- package/src/__tests__/config-watcher.test.ts +2 -2
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -1
- package/src/__tests__/conversation-error.test.ts +2 -6
- package/src/__tests__/conversation-history-web-search.test.ts +8 -0
- package/src/__tests__/conversation-title-service.test.ts +2 -1
- package/src/__tests__/credential-security-invariants.test.ts +1 -1
- package/src/__tests__/disk-pressure-tools.test.ts +1 -1
- package/src/__tests__/exploration-drift-hook.test.ts +692 -0
- package/src/__tests__/filing-service.test.ts +8 -3
- package/src/__tests__/guardian-action-store.test.ts +0 -167
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +1 -1
- package/src/__tests__/heartbeat-disk-pressure.test.ts +4 -1
- package/src/__tests__/heartbeat-service.test.ts +5 -2
- package/src/__tests__/identity-intro-cache.test.ts +12 -5
- package/src/__tests__/identity-routes.test.ts +16 -57
- package/src/__tests__/injector-chain.test.ts +8 -3
- package/src/__tests__/injector-config-quarantine-notice.test.ts +115 -0
- package/src/__tests__/llm-catalog-parity.test.ts +16 -0
- package/src/__tests__/llm-usage-store.test.ts +11 -0
- package/src/__tests__/log-export-workspace.test.ts +468 -3
- package/src/__tests__/memory-v2-static-injector.test.ts +22 -0
- package/src/__tests__/model-intents.test.ts +1 -1
- package/src/__tests__/oauth-cli.test.ts +19 -8
- package/src/__tests__/openai-provider.test.ts +34 -0
- package/src/__tests__/prechat-onboarding-contract.test.ts +0 -1
- package/src/__tests__/recurrence-engine.test.ts +45 -0
- package/src/__tests__/schedule-routes.test.ts +34 -0
- package/src/__tests__/scheduler-disk-pressure.test.ts +1 -1
- package/src/__tests__/script-proxy-conversation-manager.test.ts +10 -5
- package/src/__tests__/secret-fixtures.ts +20 -0
- package/src/__tests__/skill-tool-factory.test.ts +49 -0
- package/src/__tests__/subagent-role-registry.test.ts +24 -1
- package/src/__tests__/subagent-tools.test.ts +1 -0
- package/src/__tests__/system-prompt.test.ts +109 -11
- package/src/__tests__/tool-approval-handler.test.ts +85 -0
- package/src/__tests__/tool-audit-listener.test.ts +86 -0
- package/src/__tests__/tool-error-hook.test.ts +1 -0
- package/src/__tests__/tool-result-spool.test.ts +337 -0
- package/src/__tests__/tool-result-truncate-hook.test.ts +1 -0
- package/src/__tests__/validate-input.test.ts +95 -1
- package/src/__tests__/workspace-migration-098-remove-stale-updates-bulletin-file.test.ts +65 -0
- package/src/__tests__/workspace-migration-099-disable-cache-one-shot-callsites.test.ts +139 -0
- package/src/__tests__/workspace-migration-100-upgrade-quality-profile-to-fable-5.test.ts +174 -0
- package/src/__tests__/workspace-migration-101-upgrade-balanced-economy-to-minimax-m3.test.ts +162 -0
- package/src/__tests__/workspace-release-notes-feature-flag-guard.test.ts +45 -95
- package/src/acp/__tests__/agent-process.test.ts +315 -2
- package/src/acp/__tests__/prepare-agent-env.test.ts +79 -5
- package/src/acp/agent-process.ts +163 -34
- package/src/acp/prepare-agent-env.ts +55 -15
- package/src/agent/loop.ts +81 -24
- package/src/api/events/usage-progress.ts +28 -0
- package/src/api/index.ts +6 -0
- package/src/background-wake/wake-intent-hooks.test.ts +2 -0
- package/src/bundler/app-bundler.ts +25 -42
- package/src/bundler/app-compiler.ts +8 -0
- package/src/calls/call-controller.ts +1 -1
- package/src/cli/commands/plugins.ts +248 -15
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +318 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +16 -9
- package/src/cli/lib/__tests__/plugin-artifact.test.ts +183 -0
- package/src/cli/lib/__tests__/plugin-details.test.ts +158 -0
- package/src/cli/lib/__tests__/plugin-fingerprint.test.ts +245 -0
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +307 -0
- package/src/cli/lib/inspect-plugin.ts +252 -0
- package/src/cli/lib/install-from-github.ts +214 -21
- package/src/cli/lib/list-installed-plugins.ts +17 -6
- package/src/cli/lib/plugin-artifact.ts +103 -0
- package/src/cli/lib/plugin-details.ts +18 -1
- package/src/cli/lib/plugin-fingerprint.ts +197 -0
- package/src/cli/lib/upgrade-plugin.ts +225 -0
- package/src/config/bundled-skills/subagent/SKILL.md +2 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +8 -2
- package/src/config/call-site-defaults.ts +13 -2
- package/src/config/feature-flag-registry.json +8 -16
- package/src/config/loader.ts +52 -59
- package/src/config/schema.ts +0 -2
- package/src/config/schemas/__tests__/memory-v2.test.ts +1 -0
- package/src/config/schemas/__tests__/memory-v3.test.ts +10 -0
- package/src/config/schemas/llm.ts +10 -0
- package/src/config/schemas/memory-v2.ts +13 -0
- package/src/config/schemas/memory-v3.ts +92 -0
- package/src/config/seed-inference-profiles.ts +4 -8
- package/src/context/post-turn-tool-result-truncation.ts +32 -18
- package/src/context/tool-result-spool.ts +104 -0
- package/src/credential-execution/feature-gates.ts +0 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +41 -16
- package/src/daemon/conversation-error.ts +6 -15
- package/src/daemon/conversation.ts +9 -0
- package/src/daemon/disk-pressure-policy.ts +0 -1
- package/src/daemon/lifecycle.ts +1 -20
- package/src/daemon/message-types/conversations.ts +2 -15
- package/src/daemon/trust-context.ts +1 -1
- package/src/events/tool-audit-listener.ts +40 -9
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +1 -1
- package/src/home/__tests__/home-content-refresh.test.ts +114 -0
- package/src/home/__tests__/suggested-prompts.test.ts +86 -5
- package/src/home/home-content-refresh.ts +43 -31
- package/src/home/home-greeting-cache.ts +8 -1
- package/src/home/home-greeting.ts +13 -9
- package/src/home/suggested-prompts.ts +77 -24
- package/src/ipc/routes/trust-rules.test.ts +66 -72
- package/src/media/image-credentials.ts +2 -2
- package/src/memory/__tests__/compaction-log-store-clickhouse.test.ts +432 -0
- package/src/memory/{compaction-log-writer-clickhouse.ts → compaction-log-store-clickhouse.ts} +264 -55
- package/src/memory/conversation-attention-store.ts +1 -0
- package/src/memory/conversation-bootstrap.ts +18 -9
- package/src/memory/conversation-crud.ts +12 -2
- package/src/memory/conversation-title-service.ts +53 -9
- package/src/memory/delivery-channels.ts +0 -69
- package/src/memory/graph/extraction-job.ts +0 -15
- package/src/memory/guardian-action-store.ts +1 -376
- package/src/memory/llm-usage-store.ts +5 -1
- package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +2 -2
- package/src/memory/v2/__tests__/consolidation-job.test.ts +183 -2
- package/src/memory/v2/__tests__/injection.test.ts +70 -0
- package/src/memory/v2/__tests__/static-context.test.ts +12 -0
- package/src/memory/v2/consolidation-job.ts +93 -9
- package/src/memory/v2/injection.ts +53 -0
- package/src/memory/v2/prompts/consolidation.ts +1 -0
- package/src/memory/v2/static-context.ts +13 -1
- package/src/memory/v2/sweep-job.ts +1 -1
- package/src/memory/v2/types.ts +5 -0
- package/src/plugin-api/types.ts +7 -0
- package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +300 -0
- package/src/plugins/defaults/exploration-drift/package.json +15 -0
- package/src/plugins/defaults/index.ts +25 -0
- package/src/plugins/defaults/memory-retrieval/injectors.ts +132 -4
- package/src/plugins/defaults/memory-v3-shadow/__tests__/card.test.ts +92 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +2 -1
- package/src/plugins/defaults/memory-v3-shadow/__tests__/fresh-set.test.ts +52 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/live-integration.test.ts +2 -1
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +136 -5
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +17 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +6 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +5 -1
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +68 -4
- package/src/plugins/defaults/memory-v3-shadow/card.ts +49 -5
- package/src/plugins/defaults/memory-v3-shadow/fresh-set.ts +59 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +4 -2
- package/src/plugins/defaults/memory-v3-shadow/learned-edges.test.ts +169 -0
- package/src/plugins/defaults/memory-v3-shadow/learned-edges.ts +178 -0
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +115 -26
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +13 -9
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +144 -22
- package/src/plugins/defaults/memory-v3-shadow/types.ts +24 -6
- package/src/plugins/defaults/title-generate/hooks/stop.ts +13 -0
- package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +16 -0
- package/src/prompts/cache-boundary.ts +17 -0
- package/src/prompts/sections.ts +50 -17
- package/src/prompts/system-prompt.ts +12 -4
- package/src/prompts/templates/system-sections.ts +22 -0
- package/src/providers/__tests__/unparseable-tool-args.test.ts +53 -0
- package/src/providers/anthropic/client.ts +74 -28
- package/src/providers/gemini/client.ts +5 -1
- package/src/providers/minimax/client.ts +9 -0
- package/src/providers/model-catalog.ts +28 -0
- package/src/providers/model-intents.ts +3 -3
- package/src/providers/openai/chat-completions-provider.ts +4 -2
- package/src/providers/openai/responses-provider.ts +7 -2
- package/src/providers/retry.ts +8 -0
- package/src/providers/types.ts +11 -0
- package/src/providers/unparseable-tool-args.ts +56 -0
- package/src/runtime/AGENTS.md +6 -0
- package/src/runtime/__tests__/agent-wake.test.ts +2 -2
- package/src/runtime/agent-wake.ts +5 -5
- package/src/runtime/background-job-runner.ts +2 -2
- package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +150 -3
- package/src/runtime/migrations/vbundle-import-analyzer.ts +29 -6
- package/src/runtime/migrations/vbundle-import-policy.ts +23 -0
- package/src/runtime/migrations/vbundle-importer.ts +9 -4
- package/src/runtime/migrations/vbundle-streaming-importer.ts +8 -3
- package/src/runtime/pre-first-message-gate.ts +1 -1
- package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +241 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +132 -0
- package/src/runtime/routes/__tests__/gateway-log-routes.test.ts +97 -185
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +17 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +348 -0
- package/src/runtime/routes/__tests__/task-routes.test.ts +3 -3
- package/src/runtime/routes/btw-routes.ts +0 -14
- package/src/runtime/routes/conversation-compaction-routes.ts +86 -19
- package/src/runtime/routes/conversation-list-routes.ts +77 -5
- package/src/runtime/routes/conversation-management-routes.ts +54 -0
- package/src/runtime/routes/conversation-query-routes.ts +79 -4
- package/src/runtime/routes/gateway-log-routes.ts +14 -64
- package/src/runtime/routes/home-feed-routes.ts +10 -0
- package/src/runtime/routes/identity-intro-cache.ts +1 -1
- package/src/runtime/routes/identity-routes.ts +76 -20
- package/src/runtime/routes/inbound-message-handler.ts +0 -36
- package/src/runtime/routes/log-export-routes.ts +143 -96
- package/src/runtime/routes/plugins-routes.ts +380 -0
- package/src/runtime/routes/redact-staged-export.ts +259 -0
- package/src/runtime/routes/schedule-routes.ts +19 -2
- package/src/runtime/routes/trust-rules-routes.ts +14 -67
- package/src/schedule/recurrence-engine.ts +34 -0
- package/src/schedule/scheduler.ts +1 -0
- package/src/security/redact-json.ts +61 -0
- package/src/skills/validate-input.ts +41 -1
- package/src/subagent/types.ts +26 -1
- package/src/telemetry/types.ts +15 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +6 -1
- package/src/telemetry/usage-telemetry-reporter.ts +1 -0
- package/src/tools/apps/executors.ts +1 -1
- package/src/tools/skills/skill-tool-factory.ts +19 -8
- package/src/tools/tool-approval-handler.ts +31 -0
- package/src/usage/types.ts +8 -1
- package/src/util/platform.ts +16 -0
- package/src/watcher/engine.ts +1 -0
- package/src/workspace/adaptive-thinking-repair.ts +113 -0
- package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +70 -67
- package/src/workspace/migrations/098-remove-stale-updates-bulletin-file.ts +31 -0
- package/src/workspace/migrations/099-disable-cache-one-shot-callsites.ts +81 -0
- package/src/workspace/migrations/100-upgrade-quality-profile-to-fable-5.ts +86 -0
- package/src/workspace/migrations/101-upgrade-balanced-economy-to-minimax-m3.ts +70 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/__tests__/config-loader-quarantine-bulletin.test.ts +0 -202
- package/src/__tests__/conversation-starters-cadence.test.ts +0 -161
- package/src/__tests__/guardian-action-followup-executor.test.ts +0 -322
- package/src/__tests__/guardian-action-followup-store.test.ts +0 -373
- package/src/__tests__/guardian-action-late-reply.test.ts +0 -1083
- package/src/__tests__/update-bulletin-job.test.ts +0 -292
- package/src/config/schemas/updates.ts +0 -14
- package/src/memory/__tests__/compaction-log-writer-clickhouse.test.ts +0 -227
- package/src/memory/conversation-starters-cadence.ts +0 -78
- package/src/prompts/update-bulletin-job.ts +0 -180
- package/src/runtime/guardian-action-followup-executor.ts +0 -306
|
@@ -48,10 +48,6 @@ import {
|
|
|
48
48
|
updateMessageContent,
|
|
49
49
|
updateMessageMetadata,
|
|
50
50
|
} from "../../memory/conversation-crud.js";
|
|
51
|
-
import {
|
|
52
|
-
clearPendingVerificationReply,
|
|
53
|
-
getPendingVerificationReply,
|
|
54
|
-
} from "../../memory/delivery-channels.js";
|
|
55
51
|
import {
|
|
56
52
|
clearPayload,
|
|
57
53
|
findMessageBySourceId,
|
|
@@ -629,38 +625,6 @@ export async function handleChannelInbound({
|
|
|
629
625
|
|
|
630
626
|
const replyCallbackUrl = body.replyCallbackUrl;
|
|
631
627
|
|
|
632
|
-
// ── Retry pending verification reply on duplicate ──
|
|
633
|
-
// If a previous verification delivery failed and stored a pending reply,
|
|
634
|
-
// gateway retries (duplicates) re-attempt delivery here. On success the
|
|
635
|
-
// pending marker is cleared so further duplicates short-circuit normally.
|
|
636
|
-
if (result.duplicate && replyCallbackUrl) {
|
|
637
|
-
const pendingReply = getPendingVerificationReply(result.eventId);
|
|
638
|
-
if (pendingReply) {
|
|
639
|
-
try {
|
|
640
|
-
await deliverChannelReply(replyCallbackUrl, {
|
|
641
|
-
chatId: pendingReply.chatId,
|
|
642
|
-
text: pendingReply.text,
|
|
643
|
-
assistantId: pendingReply.assistantId,
|
|
644
|
-
});
|
|
645
|
-
clearPendingVerificationReply(result.eventId);
|
|
646
|
-
log.info(
|
|
647
|
-
{ eventId: result.eventId },
|
|
648
|
-
"Retried pending verification reply: delivered",
|
|
649
|
-
);
|
|
650
|
-
} catch (retryErr) {
|
|
651
|
-
log.error(
|
|
652
|
-
{ err: retryErr, eventId: result.eventId },
|
|
653
|
-
"Retry of pending verification reply failed; will retry on next duplicate",
|
|
654
|
-
);
|
|
655
|
-
}
|
|
656
|
-
return {
|
|
657
|
-
accepted: true,
|
|
658
|
-
duplicate: true,
|
|
659
|
-
eventId: result.eventId,
|
|
660
|
-
};
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
|
|
664
628
|
// external_conversation_bindings is assistant-agnostic. Restrict writes to
|
|
665
629
|
// self so assistant-scoped legacy routes do not overwrite each other's
|
|
666
630
|
// channel binding metadata for the same chat.
|
|
@@ -41,6 +41,7 @@ import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
|
|
|
41
41
|
import { createTarGz } from "./archive-utils.js";
|
|
42
42
|
import { InternalError } from "./errors.js";
|
|
43
43
|
import { collectWorkspaceData } from "./log-export/workspace-allowlist.js";
|
|
44
|
+
import { redactStagedExportFiles } from "./redact-staged-export.js";
|
|
44
45
|
import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
|
|
45
46
|
|
|
46
47
|
const log = getLogger("log-export-routes");
|
|
@@ -48,6 +49,22 @@ const log = getLogger("log-export-routes");
|
|
|
48
49
|
/** Maximum total payload size for log file contents (10 MB). */
|
|
49
50
|
const MAX_LOG_PAYLOAD_BYTES = 10 * 1024 * 1024;
|
|
50
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Row caps for the conversation-data table dumps (`messages.json`,
|
|
54
|
+
* `llm-request-logs.json`, `llm-usage-events.json`). Without them, a
|
|
55
|
+
* `full: true` export dumps entire tables — for long-tenured users the only
|
|
56
|
+
* realistic way to blow past the sweep's `MAX_SWEEP_FILE_BYTES` cap, which
|
|
57
|
+
* fails closed by replacing the whole file with an omission note. Bounding
|
|
58
|
+
* at the source keeps the most recent (and most useful) rows instead,
|
|
59
|
+
* mirroring the `auditLimit` pattern: order by createdAt desc, newest rows
|
|
60
|
+
* first. Truncated sections are logged and surfaced in both the export log
|
|
61
|
+
* line and `export-manifest.json` (`truncatedSections`), so a capped bundle
|
|
62
|
+
* is distinguishable from a complete one.
|
|
63
|
+
*/
|
|
64
|
+
const MAX_EXPORT_MESSAGE_ROWS = 10_000;
|
|
65
|
+
export const MAX_EXPORT_LLM_REQUEST_LOG_ROWS = 2_000;
|
|
66
|
+
const MAX_EXPORT_LLM_USAGE_EVENT_ROWS = 10_000;
|
|
67
|
+
|
|
51
68
|
interface ExportRequestBody {
|
|
52
69
|
auditLimit?: number;
|
|
53
70
|
conversationId?: string;
|
|
@@ -102,23 +119,44 @@ async function handleExport({
|
|
|
102
119
|
);
|
|
103
120
|
|
|
104
121
|
// --- Conversation data tables ---
|
|
122
|
+
// Sections truncated by a row cap, surfaced in the export log line and
|
|
123
|
+
// the export manifest.
|
|
124
|
+
const truncatedSections: string[] = [];
|
|
125
|
+
/**
|
|
126
|
+
* Keep the newest `limit` rows of a section dump. Callers fetch
|
|
127
|
+
* `limit + 1` rows so truncation is detectable without a COUNT query.
|
|
128
|
+
*/
|
|
129
|
+
const capRows = <T>(rows: T[], limit: number, section: string): T[] => {
|
|
130
|
+
if (rows.length <= limit) return rows;
|
|
131
|
+
truncatedSections.push(section);
|
|
132
|
+
log.warn(
|
|
133
|
+
{ section, limit },
|
|
134
|
+
"Export section exceeded its row cap; keeping only the most recent rows",
|
|
135
|
+
);
|
|
136
|
+
return rows.slice(0, limit);
|
|
137
|
+
};
|
|
105
138
|
if (conversationId || full) {
|
|
106
139
|
const conversationFilter = conversationId
|
|
107
140
|
? [eq(messages.conversationId, conversationId)]
|
|
108
141
|
: [];
|
|
109
142
|
|
|
110
|
-
const messageRows =
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
143
|
+
const messageRows = capRows(
|
|
144
|
+
db
|
|
145
|
+
.select()
|
|
146
|
+
.from(messages)
|
|
147
|
+
.where(
|
|
148
|
+
and(
|
|
149
|
+
...conversationFilter,
|
|
150
|
+
startTime ? gte(messages.createdAt, startTime) : undefined,
|
|
151
|
+
endTime ? lte(messages.createdAt, endTime) : undefined,
|
|
152
|
+
),
|
|
153
|
+
)
|
|
154
|
+
.orderBy(desc(messages.createdAt))
|
|
155
|
+
.limit(MAX_EXPORT_MESSAGE_ROWS + 1)
|
|
156
|
+
.all(),
|
|
157
|
+
MAX_EXPORT_MESSAGE_ROWS,
|
|
158
|
+
"messages",
|
|
159
|
+
);
|
|
122
160
|
writeFileSync(
|
|
123
161
|
join(staging, "messages.json"),
|
|
124
162
|
JSON.stringify(messageRows, null, 2),
|
|
@@ -129,18 +167,23 @@ async function handleExport({
|
|
|
129
167
|
? [eq(llmRequestLogs.conversationId, conversationId)]
|
|
130
168
|
: [];
|
|
131
169
|
|
|
132
|
-
const llmLogRows =
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
170
|
+
const llmLogRows = capRows(
|
|
171
|
+
db
|
|
172
|
+
.select()
|
|
173
|
+
.from(llmRequestLogs)
|
|
174
|
+
.where(
|
|
175
|
+
and(
|
|
176
|
+
...llmConversationFilter,
|
|
177
|
+
startTime ? gte(llmRequestLogs.createdAt, startTime) : undefined,
|
|
178
|
+
endTime ? lte(llmRequestLogs.createdAt, endTime) : undefined,
|
|
179
|
+
),
|
|
180
|
+
)
|
|
181
|
+
.orderBy(desc(llmRequestLogs.createdAt))
|
|
182
|
+
.limit(MAX_EXPORT_LLM_REQUEST_LOG_ROWS + 1)
|
|
183
|
+
.all(),
|
|
184
|
+
MAX_EXPORT_LLM_REQUEST_LOG_ROWS,
|
|
185
|
+
"llm-request-logs",
|
|
186
|
+
);
|
|
144
187
|
writeFileSync(
|
|
145
188
|
join(staging, "llm-request-logs.json"),
|
|
146
189
|
JSON.stringify(llmLogRows, null, 2),
|
|
@@ -151,18 +194,23 @@ async function handleExport({
|
|
|
151
194
|
? [eq(llmUsageEvents.conversationId, conversationId)]
|
|
152
195
|
: [];
|
|
153
196
|
|
|
154
|
-
const usageRows =
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
197
|
+
const usageRows = capRows(
|
|
198
|
+
db
|
|
199
|
+
.select()
|
|
200
|
+
.from(llmUsageEvents)
|
|
201
|
+
.where(
|
|
202
|
+
and(
|
|
203
|
+
...usageConversationFilter,
|
|
204
|
+
startTime ? gte(llmUsageEvents.createdAt, startTime) : undefined,
|
|
205
|
+
endTime ? lte(llmUsageEvents.createdAt, endTime) : undefined,
|
|
206
|
+
),
|
|
207
|
+
)
|
|
208
|
+
.orderBy(desc(llmUsageEvents.createdAt))
|
|
209
|
+
.limit(MAX_EXPORT_LLM_USAGE_EVENT_ROWS + 1)
|
|
210
|
+
.all(),
|
|
211
|
+
MAX_EXPORT_LLM_USAGE_EVENT_ROWS,
|
|
212
|
+
"llm-usage-events",
|
|
213
|
+
);
|
|
166
214
|
writeFileSync(
|
|
167
215
|
join(staging, "llm-usage-events.json"),
|
|
168
216
|
JSON.stringify(usageRows, null, 2),
|
|
@@ -335,6 +383,7 @@ async function handleExport({
|
|
|
335
383
|
commitSha: COMMIT_SHA,
|
|
336
384
|
...(startTime !== undefined ? { startTime } : {}),
|
|
337
385
|
...(endTime !== undefined ? { endTime } : {}),
|
|
386
|
+
...(truncatedSections.length > 0 ? { truncatedSections } : {}),
|
|
338
387
|
exportedAt: new Date().toISOString(),
|
|
339
388
|
};
|
|
340
389
|
writeFileSync(
|
|
@@ -343,6 +392,13 @@ async function handleExport({
|
|
|
343
392
|
"utf-8",
|
|
344
393
|
);
|
|
345
394
|
|
|
395
|
+
// --- Secret-redaction sweep over every staged text file ---
|
|
396
|
+
// Belt-and-suspenders over the structural sanitizers above: catches
|
|
397
|
+
// legacy audit rows persisted with plaintext inputs (written before
|
|
398
|
+
// write-time redaction existed) and secrets sitting in copied workspace
|
|
399
|
+
// conversation files.
|
|
400
|
+
const redactionResult = redactStagedExportFiles(staging);
|
|
401
|
+
|
|
346
402
|
log.info(
|
|
347
403
|
{
|
|
348
404
|
auditCount: auditRows.length,
|
|
@@ -353,6 +409,10 @@ async function handleExport({
|
|
|
353
409
|
full: full ?? false,
|
|
354
410
|
workspaceEntries: workspaceResult.entries.length,
|
|
355
411
|
workspaceBytes: workspaceResult.totalBytes,
|
|
412
|
+
truncatedSections,
|
|
413
|
+
redactionScanned: redactionResult.filesScanned,
|
|
414
|
+
redactionRedacted: redactionResult.filesRedacted,
|
|
415
|
+
redactionOmitted: redactionResult.filesOmitted,
|
|
356
416
|
},
|
|
357
417
|
"Export collected, creating tar.gz archive",
|
|
358
418
|
);
|
|
@@ -386,6 +446,25 @@ function redactStringValue(val: unknown): string {
|
|
|
386
446
|
return val ? "(set)" : "(empty)";
|
|
387
447
|
}
|
|
388
448
|
|
|
449
|
+
/**
|
|
450
|
+
* Narrow an unknown value to a mutable record, or undefined if not an object.
|
|
451
|
+
* Deliberately looser than `isPlainObject` (`util/object.ts`): arrays are
|
|
452
|
+
* admitted on purpose so sanitization stays fail-closed — a malformed config
|
|
453
|
+
* shape must redact more, never less.
|
|
454
|
+
*/
|
|
455
|
+
function asRecord(v: unknown): Record<string, unknown> | undefined {
|
|
456
|
+
return v && typeof v === "object"
|
|
457
|
+
? (v as Record<string, unknown>)
|
|
458
|
+
: undefined;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/** Replace every value in a map with its (set)/(empty) presence flag. */
|
|
462
|
+
function redactValueMap(obj: Record<string, unknown>): Record<string, string> {
|
|
463
|
+
return Object.fromEntries(
|
|
464
|
+
Object.keys(obj).map((k) => [k, redactStringValue(obj[k])]),
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
|
|
389
468
|
function readSanitizedConfig(): Record<string, unknown> | undefined {
|
|
390
469
|
const configPath = getWorkspaceConfigPath();
|
|
391
470
|
if (!existsSync(configPath)) return undefined;
|
|
@@ -396,71 +475,39 @@ function readSanitizedConfig(): Record<string, unknown> | undefined {
|
|
|
396
475
|
|
|
397
476
|
delete config.apiKeys;
|
|
398
477
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
if (ingress.webhook && typeof ingress.webhook === "object") {
|
|
402
|
-
const webhook = ingress.webhook as Record<string, unknown>;
|
|
403
|
-
webhook.secret = redactStringValue(webhook.secret);
|
|
404
|
-
ingress.webhook = webhook;
|
|
405
|
-
}
|
|
406
|
-
config.ingress = ingress;
|
|
407
|
-
}
|
|
478
|
+
const webhook = asRecord(asRecord(config.ingress)?.webhook);
|
|
479
|
+
if (webhook) webhook.secret = redactStringValue(webhook.secret);
|
|
408
480
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
const e = entry as Record<string, unknown>;
|
|
417
|
-
if ("apiKey" in e) e.apiKey = redactStringValue(e.apiKey);
|
|
418
|
-
if (e.env && typeof e.env === "object") {
|
|
419
|
-
const env = e.env as Record<string, unknown>;
|
|
420
|
-
e.env = Object.fromEntries(
|
|
421
|
-
Object.keys(env).map((k) => [k, redactStringValue(env[k])]),
|
|
422
|
-
);
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
}
|
|
481
|
+
const skillEntries = asRecord(asRecord(config.skills)?.entries);
|
|
482
|
+
for (const entry of Object.values(skillEntries ?? {})) {
|
|
483
|
+
const e = asRecord(entry);
|
|
484
|
+
if (!e) continue;
|
|
485
|
+
if ("apiKey" in e) e.apiKey = redactStringValue(e.apiKey);
|
|
486
|
+
const env = asRecord(e.env);
|
|
487
|
+
if (env) e.env = redactValueMap(env);
|
|
427
488
|
}
|
|
428
489
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
490
|
+
const twilio = asRecord(config.twilio);
|
|
491
|
+
if (twilio) twilio.accountSid = redactStringValue(twilio.accountSid);
|
|
492
|
+
|
|
493
|
+
const acpAgents = asRecord(asRecord(config.acp)?.agents);
|
|
494
|
+
for (const agent of Object.values(acpAgents ?? {})) {
|
|
495
|
+
const a = asRecord(agent);
|
|
496
|
+
if (!a) continue;
|
|
497
|
+
// Agent env is an arbitrary user-supplied map (often API keys);
|
|
498
|
+
// redact every value and keep only the key names.
|
|
499
|
+
const env = asRecord(a.env);
|
|
500
|
+
if (env) a.env = redactValueMap(env);
|
|
433
501
|
}
|
|
434
502
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
if (s.transport && typeof s.transport === "object") {
|
|
444
|
-
const transport = s.transport as Record<string, unknown>;
|
|
445
|
-
if (transport.headers && typeof transport.headers === "object") {
|
|
446
|
-
const headers = transport.headers as Record<string, unknown>;
|
|
447
|
-
transport.headers = Object.fromEntries(
|
|
448
|
-
Object.keys(headers).map((k) => [
|
|
449
|
-
k,
|
|
450
|
-
redactStringValue(headers[k]),
|
|
451
|
-
]),
|
|
452
|
-
);
|
|
453
|
-
}
|
|
454
|
-
if (transport.env && typeof transport.env === "object") {
|
|
455
|
-
const env = transport.env as Record<string, unknown>;
|
|
456
|
-
transport.env = Object.fromEntries(
|
|
457
|
-
Object.keys(env).map((k) => [k, redactStringValue(env[k])]),
|
|
458
|
-
);
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
}
|
|
503
|
+
const mcpServers = asRecord(asRecord(config.mcp)?.servers);
|
|
504
|
+
for (const server of Object.values(mcpServers ?? {})) {
|
|
505
|
+
const transport = asRecord(asRecord(server)?.transport);
|
|
506
|
+
if (!transport) continue;
|
|
507
|
+
const headers = asRecord(transport.headers);
|
|
508
|
+
if (headers) transport.headers = redactValueMap(headers);
|
|
509
|
+
const env = asRecord(transport.env);
|
|
510
|
+
if (env) transport.env = redactValueMap(env);
|
|
464
511
|
}
|
|
465
512
|
|
|
466
513
|
return config;
|