@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
|
@@ -12,6 +12,10 @@ import { INTERNAL_GUARDIAN_TRUST_CONTEXT } from "../../daemon/trust-context.js";
|
|
|
12
12
|
import { bootstrapConversation } from "../../memory/conversation-bootstrap.js";
|
|
13
13
|
import { getConversation } from "../../memory/conversation-crud.js";
|
|
14
14
|
import { getUsageCostForConversationWindow } from "../../memory/llm-usage-store.js";
|
|
15
|
+
import {
|
|
16
|
+
describeRRuleExpression,
|
|
17
|
+
isSingleFireRRule,
|
|
18
|
+
} from "../../schedule/recurrence-engine.js";
|
|
15
19
|
import { normalizeScheduleSyntax } from "../../schedule/recurrence-types.js";
|
|
16
20
|
import {
|
|
17
21
|
runScript,
|
|
@@ -136,7 +140,20 @@ function getCadenceDescription(
|
|
|
136
140
|
if (job.syntax === "cron") {
|
|
137
141
|
return describeCronExpression(job.cronExpression);
|
|
138
142
|
}
|
|
139
|
-
return job.
|
|
143
|
+
return describeRRuleExpression(job.cronExpression);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Presentation-layer one-shot flag. A COUNT=1 rrule fires exactly once and
|
|
148
|
+
* should read as one-time in clients, even though the scheduler internally
|
|
149
|
+
* treats expression-backed jobs as recurring (retry policy, conversation
|
|
150
|
+
* reuse). Do not feed this back into scheduler logic.
|
|
151
|
+
*/
|
|
152
|
+
function isOneShotForDisplay(
|
|
153
|
+
job: Pick<ScheduleJob, "syntax" | "cronExpression">,
|
|
154
|
+
): boolean {
|
|
155
|
+
if (job.cronExpression == null) return true;
|
|
156
|
+
return job.syntax === "rrule" && isSingleFireRRule(job.cronExpression);
|
|
140
157
|
}
|
|
141
158
|
|
|
142
159
|
function handleListSchedules(queryParams: Record<string, string>) {
|
|
@@ -182,7 +199,7 @@ function handleListSchedules(queryParams: Record<string, string>) {
|
|
|
182
199
|
routingIntent: j.routingIntent,
|
|
183
200
|
reuseConversation: j.reuseConversation,
|
|
184
201
|
wakeConversationId: j.wakeConversationId,
|
|
185
|
-
isOneShot: j
|
|
202
|
+
isOneShot: isOneShotForDisplay(j),
|
|
186
203
|
};
|
|
187
204
|
}),
|
|
188
205
|
};
|
|
@@ -1,83 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Trust rule listing route — gateway
|
|
2
|
+
* Trust rule listing route — gateway IPC proxy.
|
|
3
3
|
*
|
|
4
|
-
* The handler
|
|
5
|
-
*
|
|
4
|
+
* The handler calls the gateway over the local IPC socket because trust rule
|
|
5
|
+
* storage is gateway-owned in Docker mode.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
TrustRulesListIpcParamsSchema,
|
|
9
|
+
type TrustRulesListIpcResponse,
|
|
10
|
+
TrustRulesListIpcResponseSchema,
|
|
11
|
+
} from "@vellumai/gateway-client/gateway-ipc-contracts";
|
|
8
12
|
|
|
9
|
-
import {
|
|
13
|
+
import { ipcCallPersistent } from "../../ipc/gateway-client.js";
|
|
10
14
|
import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
|
|
11
15
|
import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
|
|
12
16
|
|
|
13
|
-
// ── Shared helper ───────────────────────────────────────────────────────
|
|
14
|
-
|
|
15
|
-
async function gatewayFetch(
|
|
16
|
-
path: string,
|
|
17
|
-
init?: RequestInit,
|
|
18
|
-
): Promise<unknown> {
|
|
19
|
-
const base = getGatewayInternalBaseUrl();
|
|
20
|
-
const res = await fetch(`${base}${path}`, init);
|
|
21
|
-
if (!res.ok) {
|
|
22
|
-
let message = `Gateway request failed (${res.status})`;
|
|
23
|
-
try {
|
|
24
|
-
const body = (await res.json()) as { error?: unknown };
|
|
25
|
-
if (typeof body.error === "string") {
|
|
26
|
-
message = body.error;
|
|
27
|
-
}
|
|
28
|
-
} catch {
|
|
29
|
-
// ignore JSON parse failures
|
|
30
|
-
}
|
|
31
|
-
throw new Error(message);
|
|
32
|
-
}
|
|
33
|
-
return res.json();
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// ── Schemas ─────────────────────────────────────────────────────────────
|
|
37
|
-
|
|
38
|
-
const TrustRulesListParams = z
|
|
39
|
-
.object({
|
|
40
|
-
tool: z.string().optional(),
|
|
41
|
-
origin: z.string().optional(),
|
|
42
|
-
include_all: z.boolean().optional(),
|
|
43
|
-
})
|
|
44
|
-
.strict();
|
|
45
|
-
|
|
46
|
-
const TrustRuleSchema = z.object({
|
|
47
|
-
id: z.string(),
|
|
48
|
-
tool: z.string(),
|
|
49
|
-
pattern: z.string(),
|
|
50
|
-
risk: z.enum(["low", "medium", "high"]),
|
|
51
|
-
description: z.string(),
|
|
52
|
-
origin: z.enum(["default", "user_defined"]),
|
|
53
|
-
userModified: z.boolean(),
|
|
54
|
-
deleted: z.boolean(),
|
|
55
|
-
createdAt: z.string(),
|
|
56
|
-
updatedAt: z.string(),
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
const TrustRulesListResponseSchema = z.object({
|
|
60
|
-
rules: z.array(TrustRuleSchema),
|
|
61
|
-
});
|
|
62
|
-
type TrustRulesListResponse = z.infer<typeof TrustRulesListResponseSchema>;
|
|
63
|
-
|
|
64
17
|
// ── Handlers ────────────────────────────────────────────────────────────
|
|
65
18
|
|
|
66
19
|
async function handleList({
|
|
67
20
|
queryParams = {},
|
|
68
21
|
body = {},
|
|
69
|
-
}: RouteHandlerArgs): Promise<
|
|
22
|
+
}: RouteHandlerArgs): Promise<TrustRulesListIpcResponse> {
|
|
70
23
|
// HTTP GET delivers filters via queryParams; CLI IPC puts them in body.
|
|
71
24
|
const source = Object.keys(queryParams).length > 0 ? queryParams : body;
|
|
72
|
-
const p =
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
if (p.origin) qs.set("origin", p.origin);
|
|
76
|
-
if (p.include_all) qs.set("include_all", "true");
|
|
77
|
-
const query = qs.toString();
|
|
78
|
-
return gatewayFetch(
|
|
79
|
-
`/v1/trust-rules${query ? `?${query}` : ""}`,
|
|
80
|
-
) as Promise<TrustRulesListResponse>;
|
|
25
|
+
const p = TrustRulesListIpcParamsSchema.parse(source);
|
|
26
|
+
const result = await ipcCallPersistent("trust_rules_list", p);
|
|
27
|
+
return TrustRulesListIpcResponseSchema.parse(result);
|
|
81
28
|
}
|
|
82
29
|
|
|
83
30
|
// ── Route definitions ───────────────────────────────────────────────────
|
|
@@ -96,7 +43,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
96
43
|
description:
|
|
97
44
|
"List trust rules, optionally filtered by tool, origin, or include_all.",
|
|
98
45
|
tags: ["trust-rules"],
|
|
99
|
-
responseBody:
|
|
46
|
+
responseBody: TrustRulesListIpcResponseSchema,
|
|
100
47
|
queryParams: [
|
|
101
48
|
{ name: "tool", description: "Filter by tool name" },
|
|
102
49
|
{ name: "origin", description: "Filter by origin" },
|
|
@@ -135,6 +135,40 @@ export function isValidScheduleExpression(spec: ScheduleSpec): boolean {
|
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
/**
|
|
139
|
+
* Detect whether an RRULE expression fires exactly once — a single RRULE
|
|
140
|
+
* with COUNT=1 and no set constructs. Such schedules are semantically
|
|
141
|
+
* one-shots even though they carry a recurrence expression.
|
|
142
|
+
*/
|
|
143
|
+
export function isSingleFireRRule(expression: string): boolean {
|
|
144
|
+
try {
|
|
145
|
+
const normalized = normalizeRruleExpression(expression);
|
|
146
|
+
if (hasSetConstructs(normalized)) return false;
|
|
147
|
+
const rule = rrulestr(normalized);
|
|
148
|
+
return !(rule instanceof RRuleSet) && rule.options.count === 1;
|
|
149
|
+
} catch {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Human-readable description of an RRULE expression for display surfaces.
|
|
156
|
+
* Single-fire rules read as "One-time"; rules the library cannot express
|
|
157
|
+
* fall back to "Custom recurrence" rather than leaking raw iCalendar text.
|
|
158
|
+
*/
|
|
159
|
+
export function describeRRuleExpression(expression: string): string {
|
|
160
|
+
if (isSingleFireRRule(expression)) return "One-time";
|
|
161
|
+
try {
|
|
162
|
+
const normalized = normalizeRruleExpression(expression);
|
|
163
|
+
if (hasSetConstructs(normalized)) return "Custom recurrence";
|
|
164
|
+
const text = rrulestr(normalized).toText();
|
|
165
|
+
if (!text) return "Custom recurrence";
|
|
166
|
+
return text.charAt(0).toUpperCase() + text.slice(1);
|
|
167
|
+
} catch {
|
|
168
|
+
return "Custom recurrence";
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
138
172
|
/**
|
|
139
173
|
* Compute the next run timestamp (epoch ms) for a schedule expression.
|
|
140
174
|
* Throws if no future runs exist.
|
|
@@ -619,6 +619,7 @@ export async function runScheduleDueWorkOnce(
|
|
|
619
619
|
jobName: `schedule:${job.id}`,
|
|
620
620
|
source: "schedule",
|
|
621
621
|
prompt: job.message,
|
|
622
|
+
systemHint: `Schedule: ${job.name}`,
|
|
622
623
|
trustContext: { sourceChannel: "vellum", trustClass: "guardian" },
|
|
623
624
|
callSite: "mainAgent",
|
|
624
625
|
timeoutMs: SCHEDULE_TALK_TIMEOUT_MS,
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON-aware secret redaction: walk a parsed JSON value and run
|
|
3
|
+
* `redactSecrets()` over every string leaf (object keys included).
|
|
4
|
+
*
|
|
5
|
+
* The redaction marker (`<redacted type="..." />`) contains double quotes,
|
|
6
|
+
* so splicing it into an already-serialized JSON document corrupts any
|
|
7
|
+
* string it lands inside. Callers that need to keep serialized JSON valid
|
|
8
|
+
* must redact the string leaves of the parsed value and re-stringify,
|
|
9
|
+
* rather than redacting the serialized text.
|
|
10
|
+
*
|
|
11
|
+
* Shared by the export-time staged-file sweep (`redact-staged-export.ts`)
|
|
12
|
+
* and the tool-audit listener (`tool-audit-listener.ts`). The hot-path pino
|
|
13
|
+
* log serializer (`util/log-redact.ts`) intentionally does not use this
|
|
14
|
+
* module.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { redactSecrets } from "./secret-scanner.js";
|
|
18
|
+
|
|
19
|
+
export interface RedactedJsonValue {
|
|
20
|
+
/** Redacted copy of the input (the input itself is never mutated). */
|
|
21
|
+
value: unknown;
|
|
22
|
+
/** True when at least one string leaf was changed by redaction. */
|
|
23
|
+
changed: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface RedactionState {
|
|
27
|
+
changed: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Redact secrets in every string leaf (keys included) of a parsed JSON
|
|
32
|
+
* value. Returns the redacted copy plus a `changed` flag so callers can
|
|
33
|
+
* keep unchanged content byte-identical (no gratuitous re-serialization).
|
|
34
|
+
*/
|
|
35
|
+
export function redactJsonStringLeaves(value: unknown): RedactedJsonValue {
|
|
36
|
+
const state: RedactionState = { changed: false };
|
|
37
|
+
return { value: walk(value, state), changed: state.changed };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function walk(value: unknown, state: RedactionState): unknown {
|
|
41
|
+
if (typeof value === "string") {
|
|
42
|
+
return redactString(value, state);
|
|
43
|
+
}
|
|
44
|
+
if (Array.isArray(value)) {
|
|
45
|
+
return value.map((item) => walk(item, state));
|
|
46
|
+
}
|
|
47
|
+
if (value !== null && typeof value === "object") {
|
|
48
|
+
return Object.fromEntries(
|
|
49
|
+
Object.entries(value).map(
|
|
50
|
+
([key, val]) => [redactString(key, state), walk(val, state)] as const,
|
|
51
|
+
),
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function redactString(value: string, state: RedactionState): string {
|
|
58
|
+
const redacted = redactSecrets(value);
|
|
59
|
+
if (redacted !== value) state.changed = true;
|
|
60
|
+
return redacted;
|
|
61
|
+
}
|
|
@@ -66,6 +66,42 @@ function quoteList(values: readonly string[]): string {
|
|
|
66
66
|
return values.map((v) => `"${v}"`).join(", ");
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
/**
|
|
70
|
+
* Coerce string-encoded booleans (`"true"`/`"false"`) to real booleans for
|
|
71
|
+
* properties the schema declares as `type: "boolean"`.
|
|
72
|
+
*
|
|
73
|
+
* Some providers' models serialize booleans as JSON strings. Rejecting those
|
|
74
|
+
* loses the caller's intent: the model's typical recovery is to drop the field
|
|
75
|
+
* and retry, at which point the field's default silently inverts what it asked
|
|
76
|
+
* for (e.g. `app_create` with `auto_open: "false"` → retry omits the field →
|
|
77
|
+
* default `true` opens a half-built app). Accepting the unambiguous string
|
|
78
|
+
* forms preserves intent.
|
|
79
|
+
*
|
|
80
|
+
* Pure: returns a new object when a coercion applies, otherwise returns
|
|
81
|
+
* `input` unchanged. Never mutates `input` or `schema`.
|
|
82
|
+
*/
|
|
83
|
+
export function coerceStringBooleans(
|
|
84
|
+
input: Record<string, unknown>,
|
|
85
|
+
schema: Record<string, unknown> | undefined,
|
|
86
|
+
): Record<string, unknown> {
|
|
87
|
+
if (!schema) return input;
|
|
88
|
+
const properties = schema.properties;
|
|
89
|
+
if (!isPlainObject(properties)) return input;
|
|
90
|
+
|
|
91
|
+
let coerced: Record<string, unknown> | undefined;
|
|
92
|
+
for (const [key, rawSubSchema] of Object.entries(properties)) {
|
|
93
|
+
if (!isPlainObject(rawSubSchema)) continue;
|
|
94
|
+
if (rawSubSchema.type !== "boolean") continue;
|
|
95
|
+
const value = input[key];
|
|
96
|
+
if (typeof value !== "string") continue;
|
|
97
|
+
const normalized = value.trim().toLowerCase();
|
|
98
|
+
if (normalized !== "true" && normalized !== "false") continue;
|
|
99
|
+
coerced ??= { ...input };
|
|
100
|
+
coerced[key] = normalized === "true";
|
|
101
|
+
}
|
|
102
|
+
return coerced ?? input;
|
|
103
|
+
}
|
|
104
|
+
|
|
69
105
|
/**
|
|
70
106
|
* Validate a tool input object against the (optional) JSON-schema definition
|
|
71
107
|
* declared on the tool entry. Returns `{ ok: true }` if the input is valid (or
|
|
@@ -121,7 +157,11 @@ export function validateInputAgainstSchema(
|
|
|
121
157
|
if (typeof declaredType === "string" && SUPPORTED_TYPES.has(declaredType)) {
|
|
122
158
|
const type = declaredType as SupportedType;
|
|
123
159
|
if (!matchesType(value, type)) {
|
|
124
|
-
errors.push(
|
|
160
|
+
errors.push(
|
|
161
|
+
type === "boolean" && typeof value === "string"
|
|
162
|
+
? `${key} must be a boolean — pass true or false as a JSON boolean, not a string`
|
|
163
|
+
: `${key} must be ${typeArticle(type)} ${type}`,
|
|
164
|
+
);
|
|
125
165
|
// No point checking enum/items if the base type is wrong.
|
|
126
166
|
continue;
|
|
127
167
|
}
|
package/src/subagent/types.ts
CHANGED
|
@@ -105,7 +105,12 @@ export const SUBAGENT_LIMITS = {
|
|
|
105
105
|
|
|
106
106
|
// ── Roles ───────────────────────────────────────────────────────────────
|
|
107
107
|
|
|
108
|
-
export type SubagentRole =
|
|
108
|
+
export type SubagentRole =
|
|
109
|
+
| "general"
|
|
110
|
+
| "researcher"
|
|
111
|
+
| "coder"
|
|
112
|
+
| "planner"
|
|
113
|
+
| "investigator";
|
|
109
114
|
|
|
110
115
|
export interface SubagentRoleConfig {
|
|
111
116
|
/**
|
|
@@ -167,4 +172,24 @@ export const SUBAGENT_ROLE_REGISTRY: Record<SubagentRole, SubagentRoleConfig> =
|
|
|
167
172
|
systemPromptPreamble:
|
|
168
173
|
"You are an analysis-focused subagent with read-only access. Read files, search the web, and synthesize findings. You cannot write files or run shell commands.",
|
|
169
174
|
},
|
|
175
|
+
investigator: {
|
|
176
|
+
allowedTools: [
|
|
177
|
+
"bash",
|
|
178
|
+
"file_read",
|
|
179
|
+
"file_list",
|
|
180
|
+
"web_search",
|
|
181
|
+
"web_fetch",
|
|
182
|
+
"recall",
|
|
183
|
+
"notify_parent",
|
|
184
|
+
],
|
|
185
|
+
skillIds: [],
|
|
186
|
+
systemPromptPreamble: [
|
|
187
|
+
"You are an investigation-focused subagent for root-cause analysis: debugging, log forensics, and tracing behavior across code.",
|
|
188
|
+
"Your shell access is for read-only investigation (grep, find, reading files and logs) — do not modify files or system state.",
|
|
189
|
+
"Working method: read whole files instead of many small line-range slices; prefer broad searches (e.g. grep -rn across a directory) over one-symbol-at-a-time queries.",
|
|
190
|
+
"Send notify_parent (urgency 'important') as soon as each finding is confirmed, so progress survives interruption.",
|
|
191
|
+
"Your final message must be a compact root-cause report with these sections: Symptom, Root cause, Evidence (file:line references), Suggested fix, Open questions.",
|
|
192
|
+
"If you approach context limits, stop investigating and produce the report from what you have — a partial report delivered is worth more than a complete investigation lost.",
|
|
193
|
+
].join(" "),
|
|
194
|
+
},
|
|
170
195
|
};
|
package/src/telemetry/types.ts
CHANGED
|
@@ -55,7 +55,12 @@ export interface ModelTelemetryEventBase extends TelemetryEventBase {
|
|
|
55
55
|
inference_profile_source: UsageAttributionProfileSource | null;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
/**
|
|
58
|
+
/**
|
|
59
|
+
* LLM usage event — one per persisted usage row. The main agent loop
|
|
60
|
+
* persists a single row per turn with token totals summed across every
|
|
61
|
+
* provider API call in the loop (`llm_call_count` carries the call count);
|
|
62
|
+
* auxiliary call sites persist one row per call.
|
|
63
|
+
*/
|
|
59
64
|
export interface LlmUsageTelemetryEvent extends TelemetryEventBase {
|
|
60
65
|
type: "llm_usage";
|
|
61
66
|
/**
|
|
@@ -85,6 +90,15 @@ export interface LlmUsageTelemetryEvent extends TelemetryEventBase {
|
|
|
85
90
|
output_tokens: number;
|
|
86
91
|
cache_creation_input_tokens: number | null;
|
|
87
92
|
cache_read_input_tokens: number | null;
|
|
93
|
+
/**
|
|
94
|
+
* Number of provider API calls aggregated into this event. The main
|
|
95
|
+
* agent loop persists one usage row per turn with token totals summed
|
|
96
|
+
* across every call in the loop, so this is how downstream consumers
|
|
97
|
+
* recover per-call averages (effective tokens ÷ calls). Auxiliary call
|
|
98
|
+
* sites record exactly 1. Null for rows persisted before daemon
|
|
99
|
+
* migration `200-usage-llm-call-count`; consumers treat null as 1.
|
|
100
|
+
*/
|
|
101
|
+
llm_call_count: number | null;
|
|
88
102
|
/**
|
|
89
103
|
* The provider's untouched `usage` block. Anthropic surfaces a TTL
|
|
90
104
|
* breakdown under `cache_creation.ephemeral_{5m,1h}_input_tokens`;
|
|
@@ -183,7 +183,7 @@ initializeDb();
|
|
|
183
183
|
|
|
184
184
|
let eventIdCounter = 0;
|
|
185
185
|
|
|
186
|
-
// The reporter consumes `UnreportedUsageEvent` (UsageEvent + the
|
|
186
|
+
// The reporter consumes `UnreportedUsageEvent` (UsageEvent + the
|
|
187
187
|
// JOIN-computed fields `conversationType` and `turnIndex`). Build that
|
|
188
188
|
// shape directly so the mock matches `queryUnreportedUsageEvents`'
|
|
189
189
|
// return type exactly.
|
|
@@ -218,6 +218,7 @@ function makeUsageEvent(
|
|
|
218
218
|
assistantVersion: "test-app-version",
|
|
219
219
|
conversationType: "standard",
|
|
220
220
|
turnIndex: 1,
|
|
221
|
+
llmCallCount: 1,
|
|
221
222
|
...overrides,
|
|
222
223
|
};
|
|
223
224
|
}
|
|
@@ -507,6 +508,7 @@ describe("UsageTelemetryReporter", () => {
|
|
|
507
508
|
callSite: "compactionAgent",
|
|
508
509
|
inferenceProfile: "quality-optimized",
|
|
509
510
|
inferenceProfileSource: "conversation",
|
|
511
|
+
llmCallCount: 3,
|
|
510
512
|
createdAt: 1700000099000,
|
|
511
513
|
});
|
|
512
514
|
mockQueryUnreportedUsageEvents.mockReturnValue([event]);
|
|
@@ -538,6 +540,7 @@ describe("UsageTelemetryReporter", () => {
|
|
|
538
540
|
expect(e.output_tokens).toBe(100);
|
|
539
541
|
expect(e.cache_creation_input_tokens).toBe(20);
|
|
540
542
|
expect(e.cache_read_input_tokens).toBe(15);
|
|
543
|
+
expect(e.llm_call_count).toBe(3);
|
|
541
544
|
expect(e.actor).toBe("context_compactor");
|
|
542
545
|
expect(e.llm_call_site).toBe("compactionAgent");
|
|
543
546
|
expect(e.inference_profile).toBe("quality-optimized");
|
|
@@ -599,6 +602,7 @@ describe("UsageTelemetryReporter", () => {
|
|
|
599
602
|
callSite: null,
|
|
600
603
|
inferenceProfile: null,
|
|
601
604
|
inferenceProfileSource: null,
|
|
605
|
+
llmCallCount: null,
|
|
602
606
|
});
|
|
603
607
|
mockQueryUnreportedUsageEvents.mockReturnValue([event]);
|
|
604
608
|
mockFetch.mockImplementation(() =>
|
|
@@ -617,6 +621,7 @@ describe("UsageTelemetryReporter", () => {
|
|
|
617
621
|
llm_call_site: null,
|
|
618
622
|
inference_profile: null,
|
|
619
623
|
inference_profile_source: null,
|
|
624
|
+
llm_call_count: null,
|
|
620
625
|
});
|
|
621
626
|
});
|
|
622
627
|
|
|
@@ -379,6 +379,7 @@ export class UsageTelemetryReporter {
|
|
|
379
379
|
output_tokens: e.outputTokens,
|
|
380
380
|
cache_creation_input_tokens: e.cacheCreationInputTokens ?? null,
|
|
381
381
|
cache_read_input_tokens: e.cacheReadInputTokens ?? null,
|
|
382
|
+
llm_call_count: e.llmCallCount,
|
|
382
383
|
raw_usage: e.rawUsage,
|
|
383
384
|
actor: e.actor,
|
|
384
385
|
llm_call_site: e.callSite,
|
|
@@ -548,7 +548,7 @@ export async function executeAppGenerateIcon(
|
|
|
548
548
|
return {
|
|
549
549
|
content: JSON.stringify({
|
|
550
550
|
error:
|
|
551
|
-
"Icon generation failed. Make sure a Gemini API key is configured in Settings.",
|
|
551
|
+
"Icon generation failed. Make sure a Gemini API key is configured in Settings → Models & Services.",
|
|
552
552
|
}),
|
|
553
553
|
isError: true,
|
|
554
554
|
};
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { SkillToolEntry } from "../../config/skills.js";
|
|
2
2
|
import { RiskLevel } from "../../permissions/types.js";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
coerceStringBooleans,
|
|
5
|
+
validateInputAgainstSchema,
|
|
6
|
+
} from "../../skills/validate-input.js";
|
|
4
7
|
import type {
|
|
5
8
|
ExecutionTarget,
|
|
6
9
|
Tool,
|
|
@@ -42,10 +45,12 @@ export function createSkillTool(
|
|
|
42
45
|
input: Record<string, unknown>,
|
|
43
46
|
context: ToolContext,
|
|
44
47
|
): Promise<ToolExecutionResult> {
|
|
48
|
+
const schema = entry.input_schema as Record<string, unknown> | undefined;
|
|
49
|
+
const coercedInput = coerceStringBooleans(input, schema);
|
|
45
50
|
const validation = validateInputAgainstSchema(
|
|
46
51
|
entry.name,
|
|
47
|
-
|
|
48
|
-
|
|
52
|
+
coercedInput,
|
|
53
|
+
schema,
|
|
49
54
|
);
|
|
50
55
|
if (!validation.ok) {
|
|
51
56
|
return {
|
|
@@ -54,11 +59,17 @@ export function createSkillTool(
|
|
|
54
59
|
};
|
|
55
60
|
}
|
|
56
61
|
|
|
57
|
-
return runSkillToolScript(
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
return runSkillToolScript(
|
|
63
|
+
skillDir,
|
|
64
|
+
entry.executor,
|
|
65
|
+
coercedInput,
|
|
66
|
+
context,
|
|
67
|
+
{
|
|
68
|
+
target: entry.execution_target,
|
|
69
|
+
expectedSkillVersionHash: versionHash,
|
|
70
|
+
bundled,
|
|
71
|
+
},
|
|
72
|
+
);
|
|
62
73
|
},
|
|
63
74
|
};
|
|
64
75
|
}
|
|
@@ -5,6 +5,10 @@ import {
|
|
|
5
5
|
getCanonicalGuardianRequest,
|
|
6
6
|
updateCanonicalGuardianRequest,
|
|
7
7
|
} from "../memory/canonical-guardian-store.js";
|
|
8
|
+
import {
|
|
9
|
+
isUnparseableToolArgs,
|
|
10
|
+
unparseableToolArgsMessage,
|
|
11
|
+
} from "../providers/unparseable-tool-args.js";
|
|
8
12
|
import { isUntrustedTrustClass } from "../runtime/actor-trust-resolver.js";
|
|
9
13
|
import { createOrReuseToolGrantRequest } from "../runtime/tool-grant-request-helper.js";
|
|
10
14
|
import { redactSecrets } from "../security/secret-scanner.js";
|
|
@@ -241,6 +245,33 @@ export class ToolApprovalHandler {
|
|
|
241
245
|
};
|
|
242
246
|
}
|
|
243
247
|
|
|
248
|
+
// Reject tool calls whose arguments failed JSON parsing in the provider
|
|
249
|
+
// layer (wrapped under the `_raw` marker). Executing with the marker
|
|
250
|
+
// object would feed garbage input to the tool — and worse, a tool that
|
|
251
|
+
// tolerates missing fields can "succeed" (e.g. ui_show creating a
|
|
252
|
+
// typeless surface), so the model never learns its arguments were
|
|
253
|
+
// mangled. Fail loudly instead so the model retries.
|
|
254
|
+
if (isUnparseableToolArgs(input)) {
|
|
255
|
+
const msg = unparseableToolArgsMessage(name, input._raw);
|
|
256
|
+
const durationMs = Date.now() - startTime;
|
|
257
|
+
emitLifecycleEvent({
|
|
258
|
+
type: "error",
|
|
259
|
+
toolName: name,
|
|
260
|
+
executionTarget,
|
|
261
|
+
input,
|
|
262
|
+
workingDir: context.workingDir,
|
|
263
|
+
conversationId: context.conversationId,
|
|
264
|
+
requestId: context.requestId,
|
|
265
|
+
riskLevel,
|
|
266
|
+
decision: "error",
|
|
267
|
+
durationMs,
|
|
268
|
+
errorMessage: msg,
|
|
269
|
+
isExpected: true,
|
|
270
|
+
errorCategory: "tool_failure",
|
|
271
|
+
});
|
|
272
|
+
return { allowed: false, result: { content: msg, isError: true } };
|
|
273
|
+
}
|
|
274
|
+
|
|
244
275
|
// Reject tool invocations targeting guardian control-plane endpoints from non-guardian actors.
|
|
245
276
|
const guardianCheck = enforceVerificationControlPlanePolicy(
|
|
246
277
|
name,
|
package/src/usage/types.ts
CHANGED
|
@@ -62,7 +62,7 @@ export interface UsageEventInput {
|
|
|
62
62
|
inferenceProfile?: string | null;
|
|
63
63
|
inferenceProfileSource?: UsageAttributionProfileSource | null;
|
|
64
64
|
/** Number of actual LLM API calls represented by this event (defaults to 1). */
|
|
65
|
-
llmCallCount?: number;
|
|
65
|
+
llmCallCount?: number | null;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
/**
|
|
@@ -85,6 +85,13 @@ export interface UsageEvent extends UsageEventInput {
|
|
|
85
85
|
inferenceProfileSource: UsageAttributionProfileSource | null;
|
|
86
86
|
estimatedCostUsd: number | null;
|
|
87
87
|
pricingStatus: "priced" | "unpriced";
|
|
88
|
+
/**
|
|
89
|
+
* Number of provider API calls aggregated into this event. The main agent
|
|
90
|
+
* loop persists one row per turn with this set to the number of calls in
|
|
91
|
+
* the loop; auxiliary call sites persist 1. `null` only for rows persisted
|
|
92
|
+
* before migration `200-usage-llm-call-count` ran.
|
|
93
|
+
*/
|
|
94
|
+
llmCallCount: number | null;
|
|
88
95
|
/**
|
|
89
96
|
* Version of the assistant binary at the moment this event was
|
|
90
97
|
* RECORDED, captured by `recordUsageEvent` and persisted with the
|
package/src/util/platform.ts
CHANGED
|
@@ -79,6 +79,22 @@ export function getDataDir(): string {
|
|
|
79
79
|
return join(getWorkspaceDir(), "data");
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Returns the path to the config-quarantine notice sentinel
|
|
84
|
+
* (`<workspace>/data/config-quarantine-notice.json`).
|
|
85
|
+
*
|
|
86
|
+
* Written by the config loader when a corrupt `config.json` is quarantined and
|
|
87
|
+
* read by the per-turn `config-quarantine-notice` injector. Lives under the
|
|
88
|
+
* internal data dir (runtime state, config-free to resolve) rather than the
|
|
89
|
+
* user-facing workspace root because it is daemon-written bookkeeping, not a
|
|
90
|
+
* file the user edits. The path resolves without loading config, so it is safe
|
|
91
|
+
* to call during early-boot config load before the DB or `getConfig().dataDir`
|
|
92
|
+
* exist.
|
|
93
|
+
*/
|
|
94
|
+
export function getConfigQuarantineNoticePath(): string {
|
|
95
|
+
return join(getDataDir(), "config-quarantine-notice.json");
|
|
96
|
+
}
|
|
97
|
+
|
|
82
98
|
/**
|
|
83
99
|
* Returns the embedding models directory (~/.vellum/workspace/embedding-models).
|
|
84
100
|
* Downloaded embedding runtime (onnxruntime-node, transformers bundle, model weights)
|
package/src/watcher/engine.ts
CHANGED
|
@@ -251,6 +251,7 @@ export async function runWatchersOnce(
|
|
|
251
251
|
// The seed lives in the sandwich messages; processMessage runs
|
|
252
252
|
// with an empty prompt so we don't double-inject the action prompt.
|
|
253
253
|
prompt: "",
|
|
254
|
+
systemHint: `Watcher: ${watcher.name}`,
|
|
254
255
|
trustContext: { sourceChannel: "vellum", trustClass: "guardian" },
|
|
255
256
|
callSite: "mainAgent",
|
|
256
257
|
timeoutMs: WATCHER_JOB_TIMEOUT_MS,
|