@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
|
@@ -40,75 +40,6 @@ function normalizePositiveIntegerIndexes(value: unknown): number[] {
|
|
|
40
40
|
return [...new Set(indexes)].sort((a, b) => a - b);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
// ── Pending verification reply helpers ───────────────────────────────
|
|
44
|
-
//
|
|
45
|
-
// When a guardian verification succeeds but the confirmation reply fails
|
|
46
|
-
// to deliver, we persist the reply details on the inbound event so that
|
|
47
|
-
// gateway retries (which arrive as duplicates) can re-attempt delivery.
|
|
48
|
-
|
|
49
|
-
export interface PendingVerificationReply {
|
|
50
|
-
__pendingVerificationReply: true;
|
|
51
|
-
chatId: string;
|
|
52
|
-
text: string;
|
|
53
|
-
assistantId: string;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Store a pending verification reply on an inbound event. Called when
|
|
58
|
-
* `deliverChannelReply` fails after challenge consumption so the reply
|
|
59
|
-
* can be retried on subsequent duplicate deliveries.
|
|
60
|
-
*/
|
|
61
|
-
export function storePendingVerificationReply(
|
|
62
|
-
eventId: string,
|
|
63
|
-
reply: Omit<PendingVerificationReply, "__pendingVerificationReply">,
|
|
64
|
-
): void {
|
|
65
|
-
const db = getDb();
|
|
66
|
-
const payload: PendingVerificationReply = {
|
|
67
|
-
__pendingVerificationReply: true,
|
|
68
|
-
...reply,
|
|
69
|
-
};
|
|
70
|
-
db.update(channelInboundEvents)
|
|
71
|
-
.set({ rawPayload: JSON.stringify(payload), updatedAt: Date.now() })
|
|
72
|
-
.where(eq(channelInboundEvents.id, eventId))
|
|
73
|
-
.run();
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Retrieve a pending verification reply for a given event, if one exists.
|
|
78
|
-
*/
|
|
79
|
-
export function getPendingVerificationReply(
|
|
80
|
-
eventId: string,
|
|
81
|
-
): PendingVerificationReply | null {
|
|
82
|
-
const db = getDb();
|
|
83
|
-
const row = db
|
|
84
|
-
.select({ rawPayload: channelInboundEvents.rawPayload })
|
|
85
|
-
.from(channelInboundEvents)
|
|
86
|
-
.where(eq(channelInboundEvents.id, eventId))
|
|
87
|
-
.get();
|
|
88
|
-
|
|
89
|
-
if (!row?.rawPayload) return null;
|
|
90
|
-
try {
|
|
91
|
-
const parsed = JSON.parse(row.rawPayload);
|
|
92
|
-
if (parsed && parsed.__pendingVerificationReply === true) {
|
|
93
|
-
return parsed as PendingVerificationReply;
|
|
94
|
-
}
|
|
95
|
-
return null;
|
|
96
|
-
} catch {
|
|
97
|
-
return null;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Clear a pending verification reply after successful delivery.
|
|
103
|
-
*/
|
|
104
|
-
export function clearPendingVerificationReply(eventId: string): void {
|
|
105
|
-
const db = getDb();
|
|
106
|
-
db.update(channelInboundEvents)
|
|
107
|
-
.set({ rawPayload: null, updatedAt: Date.now() })
|
|
108
|
-
.where(eq(channelInboundEvents.id, eventId))
|
|
109
|
-
.run();
|
|
110
|
-
}
|
|
111
|
-
|
|
112
43
|
// ── Per-segment delivery progress ──────────────────────────────────
|
|
113
44
|
//
|
|
114
45
|
// When a split reply (multiple text segments from tool boundaries) fails
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
import type { AssistantConfig } from "../../config/types.js";
|
|
10
10
|
import { getLogger } from "../../util/logger.js";
|
|
11
11
|
import { getMemoryCheckpoint, setMemoryCheckpoint } from "../checkpoints.js";
|
|
12
|
-
import { maybeEnqueueConversationStartersJob } from "../conversation-starters-cadence.js";
|
|
13
12
|
import { asString } from "../job-utils.js";
|
|
14
13
|
import type { MemoryJob } from "../jobs-store.js";
|
|
15
14
|
import { runGraphExtraction } from "./extraction.js";
|
|
@@ -65,20 +64,6 @@ export async function graphExtractJob(
|
|
|
65
64
|
},
|
|
66
65
|
"Graph extraction job complete",
|
|
67
66
|
);
|
|
68
|
-
|
|
69
|
-
try {
|
|
70
|
-
maybeEnqueueConversationStartersJob(scopeId);
|
|
71
|
-
} catch (cadenceErr) {
|
|
72
|
-
log.warn(
|
|
73
|
-
{
|
|
74
|
-
err:
|
|
75
|
-
cadenceErr instanceof Error
|
|
76
|
-
? cadenceErr.message
|
|
77
|
-
: String(cadenceErr),
|
|
78
|
-
},
|
|
79
|
-
"Conversation starters cadence check failed (non-fatal)",
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
67
|
} catch (err) {
|
|
83
68
|
log.error(
|
|
84
69
|
{ conversationId, err: err instanceof Error ? err.message : String(err) },
|
|
@@ -10,13 +10,10 @@
|
|
|
10
10
|
import { and, desc, eq, inArray, lt } from "drizzle-orm";
|
|
11
11
|
import { v4 as uuid } from "uuid";
|
|
12
12
|
|
|
13
|
-
import { getLogger } from "../util/logger.js";
|
|
14
13
|
import { getDb } from "./db-connection.js";
|
|
15
14
|
import { rawChanges } from "./raw-query.js";
|
|
16
15
|
import { guardianActionDeliveries, guardianActionRequests } from "./schema.js";
|
|
17
16
|
|
|
18
|
-
const log = getLogger("guardian-action-store");
|
|
19
|
-
|
|
20
17
|
// ---------------------------------------------------------------------------
|
|
21
18
|
// Types
|
|
22
19
|
// ---------------------------------------------------------------------------
|
|
@@ -45,6 +42,7 @@ export type FollowupState =
|
|
|
45
42
|
| "completed"
|
|
46
43
|
| "declined"
|
|
47
44
|
| "failed";
|
|
45
|
+
/** Follow-up action recorded on a request row (column retained). */
|
|
48
46
|
export type FollowupAction = "call_back" | "decline";
|
|
49
47
|
|
|
50
48
|
export interface GuardianActionRequest {
|
|
@@ -432,180 +430,6 @@ export function cancelGuardianActionRequest(id: string): void {
|
|
|
432
430
|
.run();
|
|
433
431
|
}
|
|
434
432
|
|
|
435
|
-
// ---------------------------------------------------------------------------
|
|
436
|
-
// Follow-up lifecycle helpers
|
|
437
|
-
// ---------------------------------------------------------------------------
|
|
438
|
-
|
|
439
|
-
/** Valid non-terminal followup_state transitions for progressFollowupState.
|
|
440
|
-
* Terminal states (completed, declined, failed) are only reachable via
|
|
441
|
-
* finalizeFollowup, which properly sets followupCompletedAt. */
|
|
442
|
-
const FOLLOWUP_TRANSITIONS: Record<FollowupState, FollowupState[]> = {
|
|
443
|
-
none: [],
|
|
444
|
-
awaiting_guardian_choice: ["dispatching"],
|
|
445
|
-
dispatching: [],
|
|
446
|
-
completed: [],
|
|
447
|
-
declined: [],
|
|
448
|
-
failed: [],
|
|
449
|
-
};
|
|
450
|
-
|
|
451
|
-
/** Valid terminal transitions for finalizeFollowup. Maps from current
|
|
452
|
-
* followup_state to the terminal states reachable from it. */
|
|
453
|
-
const FOLLOWUP_FINALIZE_TRANSITIONS: Partial<
|
|
454
|
-
Record<FollowupState, FollowupState[]>
|
|
455
|
-
> = {
|
|
456
|
-
awaiting_guardian_choice: ["declined"],
|
|
457
|
-
dispatching: ["completed", "failed"],
|
|
458
|
-
};
|
|
459
|
-
|
|
460
|
-
/**
|
|
461
|
-
* Atomically set status='expired' and expired_reason on a pending request.
|
|
462
|
-
* Returns the updated request on success, or null if the request was not
|
|
463
|
-
* in 'pending' status (first-writer-wins).
|
|
464
|
-
*/
|
|
465
|
-
export function markTimedOutWithReason(
|
|
466
|
-
id: string,
|
|
467
|
-
reason: ExpiredReason,
|
|
468
|
-
): GuardianActionRequest | null {
|
|
469
|
-
const db = getDb();
|
|
470
|
-
const now = Date.now();
|
|
471
|
-
|
|
472
|
-
db.update(guardianActionRequests)
|
|
473
|
-
.set({ status: "expired", expiredReason: reason, updatedAt: now })
|
|
474
|
-
.where(
|
|
475
|
-
and(
|
|
476
|
-
eq(guardianActionRequests.id, id),
|
|
477
|
-
eq(guardianActionRequests.status, "pending"),
|
|
478
|
-
),
|
|
479
|
-
)
|
|
480
|
-
.run();
|
|
481
|
-
|
|
482
|
-
if (rawChanges() === 0) return null;
|
|
483
|
-
|
|
484
|
-
// Also expire active deliveries
|
|
485
|
-
db.update(guardianActionDeliveries)
|
|
486
|
-
.set({ status: "expired", updatedAt: now })
|
|
487
|
-
.where(
|
|
488
|
-
and(
|
|
489
|
-
eq(guardianActionDeliveries.requestId, id),
|
|
490
|
-
inArray(guardianActionDeliveries.status, ["pending", "sent"]),
|
|
491
|
-
),
|
|
492
|
-
)
|
|
493
|
-
.run();
|
|
494
|
-
|
|
495
|
-
return getGuardianActionRequest(id);
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
/**
|
|
499
|
-
* Atomically transition an expired request into the follow-up flow.
|
|
500
|
-
* Sets followup_state='awaiting_guardian_choice', records the late answer
|
|
501
|
-
* text and timestamp. Only succeeds if status='expired' and followup_state='none'.
|
|
502
|
-
* Returns the updated request on success, or null on conflict.
|
|
503
|
-
*/
|
|
504
|
-
export function startFollowupFromExpiredRequest(
|
|
505
|
-
id: string,
|
|
506
|
-
lateAnswerText: string,
|
|
507
|
-
): GuardianActionRequest | null {
|
|
508
|
-
const db = getDb();
|
|
509
|
-
const now = Date.now();
|
|
510
|
-
|
|
511
|
-
db.update(guardianActionRequests)
|
|
512
|
-
.set({
|
|
513
|
-
followupState: "awaiting_guardian_choice",
|
|
514
|
-
lateAnswerText,
|
|
515
|
-
lateAnsweredAt: now,
|
|
516
|
-
updatedAt: now,
|
|
517
|
-
})
|
|
518
|
-
.where(
|
|
519
|
-
and(
|
|
520
|
-
eq(guardianActionRequests.id, id),
|
|
521
|
-
eq(guardianActionRequests.status, "expired"),
|
|
522
|
-
eq(guardianActionRequests.followupState, "none"),
|
|
523
|
-
),
|
|
524
|
-
)
|
|
525
|
-
.run();
|
|
526
|
-
|
|
527
|
-
if (rawChanges() === 0) return null;
|
|
528
|
-
return getGuardianActionRequest(id);
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
/**
|
|
532
|
-
* Atomically progress the followup_state. Validates that the transition
|
|
533
|
-
* is allowed (see FOLLOWUP_TRANSITIONS). Optionally sets the followup_action.
|
|
534
|
-
* Returns the updated request on success, or null if the transition was
|
|
535
|
-
* invalid or the prior state didn't match.
|
|
536
|
-
*/
|
|
537
|
-
export function progressFollowupState(
|
|
538
|
-
id: string,
|
|
539
|
-
newState: FollowupState,
|
|
540
|
-
action?: FollowupAction,
|
|
541
|
-
): GuardianActionRequest | null {
|
|
542
|
-
const request = getGuardianActionRequest(id);
|
|
543
|
-
if (!request) return null;
|
|
544
|
-
|
|
545
|
-
const allowed = FOLLOWUP_TRANSITIONS[request.followupState];
|
|
546
|
-
if (!allowed.includes(newState)) return null;
|
|
547
|
-
|
|
548
|
-
const db = getDb();
|
|
549
|
-
const now = Date.now();
|
|
550
|
-
|
|
551
|
-
const updates: Record<string, unknown> = {
|
|
552
|
-
followupState: newState,
|
|
553
|
-
updatedAt: now,
|
|
554
|
-
};
|
|
555
|
-
if (action !== undefined) updates.followupAction = action;
|
|
556
|
-
|
|
557
|
-
db.update(guardianActionRequests)
|
|
558
|
-
.set(updates)
|
|
559
|
-
.where(
|
|
560
|
-
and(
|
|
561
|
-
eq(guardianActionRequests.id, id),
|
|
562
|
-
eq(guardianActionRequests.status, "expired"),
|
|
563
|
-
eq(guardianActionRequests.followupState, request.followupState),
|
|
564
|
-
),
|
|
565
|
-
)
|
|
566
|
-
.run();
|
|
567
|
-
|
|
568
|
-
if (rawChanges() === 0) return null;
|
|
569
|
-
return getGuardianActionRequest(id);
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
/**
|
|
573
|
-
* Finalize a follow-up by setting the terminal followup_state and
|
|
574
|
-
* recording followup_completed_at. Only succeeds from a non-terminal state
|
|
575
|
-
* and only on expired requests.
|
|
576
|
-
*/
|
|
577
|
-
export function finalizeFollowup(
|
|
578
|
-
id: string,
|
|
579
|
-
finalState: "completed" | "declined" | "failed",
|
|
580
|
-
): GuardianActionRequest | null {
|
|
581
|
-
const request = getGuardianActionRequest(id);
|
|
582
|
-
if (!request) return null;
|
|
583
|
-
|
|
584
|
-
const allowed = FOLLOWUP_FINALIZE_TRANSITIONS[request.followupState];
|
|
585
|
-
if (!allowed?.includes(finalState)) return null;
|
|
586
|
-
|
|
587
|
-
const db = getDb();
|
|
588
|
-
const now = Date.now();
|
|
589
|
-
|
|
590
|
-
db.update(guardianActionRequests)
|
|
591
|
-
.set({
|
|
592
|
-
followupState: finalState,
|
|
593
|
-
followupCompletedAt: now,
|
|
594
|
-
updatedAt: now,
|
|
595
|
-
})
|
|
596
|
-
.where(
|
|
597
|
-
and(
|
|
598
|
-
eq(guardianActionRequests.id, id),
|
|
599
|
-
eq(guardianActionRequests.status, "expired"),
|
|
600
|
-
eq(guardianActionRequests.followupState, request.followupState),
|
|
601
|
-
),
|
|
602
|
-
)
|
|
603
|
-
.run();
|
|
604
|
-
|
|
605
|
-
if (rawChanges() === 0) return null;
|
|
606
|
-
return getGuardianActionRequest(id);
|
|
607
|
-
}
|
|
608
|
-
|
|
609
433
|
// ---------------------------------------------------------------------------
|
|
610
434
|
// Guardian Action Deliveries
|
|
611
435
|
// ---------------------------------------------------------------------------
|
|
@@ -640,205 +464,6 @@ export function createGuardianActionDelivery(params: {
|
|
|
640
464
|
return rowToDelivery(row);
|
|
641
465
|
}
|
|
642
466
|
|
|
643
|
-
/**
|
|
644
|
-
* Look up all pending deliveries by destination conversation ID.
|
|
645
|
-
* Used for disambiguation when a reused vellum conversation has multiple active
|
|
646
|
-
* guardian requests.
|
|
647
|
-
*/
|
|
648
|
-
export function getPendingDeliveriesByConversation(
|
|
649
|
-
conversationId: string,
|
|
650
|
-
): GuardianActionDelivery[] {
|
|
651
|
-
try {
|
|
652
|
-
const db = getDb();
|
|
653
|
-
const rows = db
|
|
654
|
-
.select({ delivery: guardianActionDeliveries })
|
|
655
|
-
.from(guardianActionDeliveries)
|
|
656
|
-
.innerJoin(
|
|
657
|
-
guardianActionRequests,
|
|
658
|
-
eq(guardianActionDeliveries.requestId, guardianActionRequests.id),
|
|
659
|
-
)
|
|
660
|
-
.where(
|
|
661
|
-
and(
|
|
662
|
-
eq(
|
|
663
|
-
guardianActionDeliveries.destinationConversationId,
|
|
664
|
-
conversationId,
|
|
665
|
-
),
|
|
666
|
-
eq(guardianActionDeliveries.status, "sent"),
|
|
667
|
-
eq(guardianActionRequests.status, "pending"),
|
|
668
|
-
),
|
|
669
|
-
)
|
|
670
|
-
.all();
|
|
671
|
-
return rows.map((r) => rowToDelivery(r.delivery));
|
|
672
|
-
} catch (err) {
|
|
673
|
-
if (err instanceof Error && err.message.includes("no such table")) {
|
|
674
|
-
log.warn({ err }, "guardian tables not yet created");
|
|
675
|
-
return [];
|
|
676
|
-
}
|
|
677
|
-
throw err;
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
/**
|
|
682
|
-
* Look up sent deliveries for expired requests eligible for follow-up.
|
|
683
|
-
* Used by inbound message routing to match late guardian answers to expired requests.
|
|
684
|
-
*/
|
|
685
|
-
export function getExpiredDeliveriesByDestination(
|
|
686
|
-
channel: string,
|
|
687
|
-
chatId: string,
|
|
688
|
-
): GuardianActionDelivery[] {
|
|
689
|
-
try {
|
|
690
|
-
const db = getDb();
|
|
691
|
-
|
|
692
|
-
const rows = db
|
|
693
|
-
.select({
|
|
694
|
-
delivery: guardianActionDeliveries,
|
|
695
|
-
})
|
|
696
|
-
.from(guardianActionDeliveries)
|
|
697
|
-
.innerJoin(
|
|
698
|
-
guardianActionRequests,
|
|
699
|
-
eq(guardianActionDeliveries.requestId, guardianActionRequests.id),
|
|
700
|
-
)
|
|
701
|
-
.where(
|
|
702
|
-
and(
|
|
703
|
-
eq(guardianActionRequests.status, "expired"),
|
|
704
|
-
eq(guardianActionRequests.followupState, "none"),
|
|
705
|
-
eq(guardianActionDeliveries.destinationChannel, channel),
|
|
706
|
-
eq(guardianActionDeliveries.destinationChatId, chatId),
|
|
707
|
-
eq(guardianActionDeliveries.status, "expired"),
|
|
708
|
-
),
|
|
709
|
-
)
|
|
710
|
-
.all();
|
|
711
|
-
|
|
712
|
-
return rows.map((r) => rowToDelivery(r.delivery));
|
|
713
|
-
} catch (err) {
|
|
714
|
-
if (err instanceof Error && err.message.includes("no such table")) {
|
|
715
|
-
log.warn({ err }, "guardian tables not yet created");
|
|
716
|
-
return [];
|
|
717
|
-
}
|
|
718
|
-
throw err;
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
/**
|
|
723
|
-
* Look up all expired deliveries by destination conversation ID.
|
|
724
|
-
* Used for disambiguation when a reused vellum conversation has multiple expired
|
|
725
|
-
* guardian requests eligible for follow-up.
|
|
726
|
-
*/
|
|
727
|
-
export function getExpiredDeliveriesByConversation(
|
|
728
|
-
conversationId: string,
|
|
729
|
-
): GuardianActionDelivery[] {
|
|
730
|
-
try {
|
|
731
|
-
const db = getDb();
|
|
732
|
-
const rows = db
|
|
733
|
-
.select({ delivery: guardianActionDeliveries })
|
|
734
|
-
.from(guardianActionDeliveries)
|
|
735
|
-
.innerJoin(
|
|
736
|
-
guardianActionRequests,
|
|
737
|
-
eq(guardianActionDeliveries.requestId, guardianActionRequests.id),
|
|
738
|
-
)
|
|
739
|
-
.where(
|
|
740
|
-
and(
|
|
741
|
-
eq(
|
|
742
|
-
guardianActionDeliveries.destinationConversationId,
|
|
743
|
-
conversationId,
|
|
744
|
-
),
|
|
745
|
-
eq(guardianActionDeliveries.status, "expired"),
|
|
746
|
-
eq(guardianActionRequests.status, "expired"),
|
|
747
|
-
eq(guardianActionRequests.followupState, "none"),
|
|
748
|
-
),
|
|
749
|
-
)
|
|
750
|
-
.all();
|
|
751
|
-
return rows.map((r) => rowToDelivery(r.delivery));
|
|
752
|
-
} catch (err) {
|
|
753
|
-
if (err instanceof Error && err.message.includes("no such table")) {
|
|
754
|
-
log.warn({ err }, "guardian tables not yet created");
|
|
755
|
-
return [];
|
|
756
|
-
}
|
|
757
|
-
throw err;
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
/**
|
|
762
|
-
* Look up deliveries for requests in `awaiting_guardian_choice` follow-up state.
|
|
763
|
-
* Used by inbound message routing to intercept guardian follow-up replies
|
|
764
|
-
* on channel paths (Telegram, WhatsApp).
|
|
765
|
-
*/
|
|
766
|
-
export function getFollowupDeliveriesByDestination(
|
|
767
|
-
channel: string,
|
|
768
|
-
chatId: string,
|
|
769
|
-
): GuardianActionDelivery[] {
|
|
770
|
-
try {
|
|
771
|
-
const db = getDb();
|
|
772
|
-
|
|
773
|
-
const rows = db
|
|
774
|
-
.select({
|
|
775
|
-
delivery: guardianActionDeliveries,
|
|
776
|
-
})
|
|
777
|
-
.from(guardianActionDeliveries)
|
|
778
|
-
.innerJoin(
|
|
779
|
-
guardianActionRequests,
|
|
780
|
-
eq(guardianActionDeliveries.requestId, guardianActionRequests.id),
|
|
781
|
-
)
|
|
782
|
-
.where(
|
|
783
|
-
and(
|
|
784
|
-
eq(guardianActionRequests.status, "expired"),
|
|
785
|
-
eq(guardianActionRequests.followupState, "awaiting_guardian_choice"),
|
|
786
|
-
eq(guardianActionDeliveries.destinationChannel, channel),
|
|
787
|
-
eq(guardianActionDeliveries.destinationChatId, chatId),
|
|
788
|
-
eq(guardianActionDeliveries.status, "expired"),
|
|
789
|
-
),
|
|
790
|
-
)
|
|
791
|
-
.all();
|
|
792
|
-
|
|
793
|
-
return rows.map((r) => rowToDelivery(r.delivery));
|
|
794
|
-
} catch (err) {
|
|
795
|
-
if (err instanceof Error && err.message.includes("no such table")) {
|
|
796
|
-
log.warn({ err }, "guardian tables not yet created");
|
|
797
|
-
return [];
|
|
798
|
-
}
|
|
799
|
-
throw err;
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
/**
|
|
804
|
-
* Look up all deliveries for requests in `awaiting_guardian_choice` follow-up
|
|
805
|
-
* state by destination conversation ID. Used for disambiguation when a reused
|
|
806
|
-
* vellum conversation has multiple follow-up guardian requests.
|
|
807
|
-
*/
|
|
808
|
-
export function getFollowupDeliveriesByConversation(
|
|
809
|
-
conversationId: string,
|
|
810
|
-
): GuardianActionDelivery[] {
|
|
811
|
-
try {
|
|
812
|
-
const db = getDb();
|
|
813
|
-
const rows = db
|
|
814
|
-
.select({ delivery: guardianActionDeliveries })
|
|
815
|
-
.from(guardianActionDeliveries)
|
|
816
|
-
.innerJoin(
|
|
817
|
-
guardianActionRequests,
|
|
818
|
-
eq(guardianActionDeliveries.requestId, guardianActionRequests.id),
|
|
819
|
-
)
|
|
820
|
-
.where(
|
|
821
|
-
and(
|
|
822
|
-
eq(
|
|
823
|
-
guardianActionDeliveries.destinationConversationId,
|
|
824
|
-
conversationId,
|
|
825
|
-
),
|
|
826
|
-
eq(guardianActionDeliveries.status, "expired"),
|
|
827
|
-
eq(guardianActionRequests.status, "expired"),
|
|
828
|
-
eq(guardianActionRequests.followupState, "awaiting_guardian_choice"),
|
|
829
|
-
),
|
|
830
|
-
)
|
|
831
|
-
.all();
|
|
832
|
-
return rows.map((r) => rowToDelivery(r.delivery));
|
|
833
|
-
} catch (err) {
|
|
834
|
-
if (err instanceof Error && err.message.includes("no such table")) {
|
|
835
|
-
log.warn({ err }, "guardian tables not yet created");
|
|
836
|
-
return [];
|
|
837
|
-
}
|
|
838
|
-
throw err;
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
|
|
842
467
|
export function updateDeliveryStatus(
|
|
843
468
|
deliveryId: string,
|
|
844
469
|
status: GuardianActionDeliveryStatus,
|
|
@@ -43,6 +43,7 @@ export function recordUsageEvent(
|
|
|
43
43
|
createdAt: Date.now(),
|
|
44
44
|
...input,
|
|
45
45
|
callSite: input.callSite ?? null,
|
|
46
|
+
llmCallCount: input.llmCallCount ?? 1,
|
|
46
47
|
inferenceProfile: input.inferenceProfile ?? null,
|
|
47
48
|
inferenceProfileSource: input.inferenceProfileSource ?? null,
|
|
48
49
|
estimatedCostUsd: pricing.estimatedCostUsd,
|
|
@@ -69,7 +70,7 @@ export function recordUsageEvent(
|
|
|
69
70
|
rawUsage: event.rawUsage === null ? null : JSON.stringify(event.rawUsage),
|
|
70
71
|
estimatedCostUsd: event.estimatedCostUsd,
|
|
71
72
|
pricingStatus: event.pricingStatus,
|
|
72
|
-
llmCallCount: event.llmCallCount
|
|
73
|
+
llmCallCount: event.llmCallCount,
|
|
73
74
|
metadataJson: null,
|
|
74
75
|
// Capture the assistant's version at RECORD time so a batch flush
|
|
75
76
|
// days later doesn't mis-attribute this row to whatever version
|
|
@@ -105,6 +106,7 @@ function rowToUsageEvent(row: {
|
|
|
105
106
|
rawUsage: string | null;
|
|
106
107
|
estimatedCostUsd: number | null;
|
|
107
108
|
pricingStatus: string;
|
|
109
|
+
llmCallCount: number | null;
|
|
108
110
|
assistantVersion: string | null;
|
|
109
111
|
}): UsageEvent {
|
|
110
112
|
return {
|
|
@@ -127,6 +129,7 @@ function rowToUsageEvent(row: {
|
|
|
127
129
|
rawUsage: parseRawUsage(row.rawUsage),
|
|
128
130
|
estimatedCostUsd: row.estimatedCostUsd,
|
|
129
131
|
pricingStatus: row.pricingStatus as "priced" | "unpriced",
|
|
132
|
+
llmCallCount: row.llmCallCount,
|
|
130
133
|
assistantVersion: row.assistantVersion,
|
|
131
134
|
};
|
|
132
135
|
}
|
|
@@ -230,6 +233,7 @@ export function queryUnreportedUsageEvents(
|
|
|
230
233
|
estimatedCostUsd: llmUsageEvents.estimatedCostUsd,
|
|
231
234
|
pricingStatus: llmUsageEvents.pricingStatus,
|
|
232
235
|
assistantVersion: llmUsageEvents.assistantVersion,
|
|
236
|
+
llmCallCount: llmUsageEvents.llmCallCount,
|
|
233
237
|
conversationType: conversations.conversationType,
|
|
234
238
|
// Null when conversationId is null (no parent conversation).
|
|
235
239
|
// Otherwise the count of eligible user turns up to and including
|
|
@@ -4,8 +4,8 @@ import { withCrashRecovery } from "./validate-migration-state.js";
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Rename checkpoint keys from the old `thread_starters:` prefix to
|
|
7
|
-
* `conversation_starters:` so that the renamed code paths
|
|
8
|
-
* `conversation-
|
|
7
|
+
* `conversation_starters:` so that the renamed code paths (see
|
|
8
|
+
* `conversation-starter-checkpoints.ts` and `conversation-starters.ts`)
|
|
9
9
|
* find existing generation state and avoid unnecessary re-generation.
|
|
10
10
|
*
|
|
11
11
|
* This was originally appended to migration 174, but that migration
|