@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
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* When loadConfig()/loadRawConfig() quarantines a corrupt config.json, it
|
|
3
|
+
* writes a small JSON sentinel to <workspace>/data/config-quarantine-notice.json
|
|
4
|
+
* recording the event. The per-turn `config-quarantine-notice` injector reads
|
|
5
|
+
* that sentinel and surfaces the reset to the agent — it's agent-visible
|
|
6
|
+
* context, not a push notification.
|
|
7
|
+
*
|
|
8
|
+
* The sentinel is overwritten on each quarantine (idempotent per event) and
|
|
9
|
+
* resolves from a workspace-derived path so it is safe to write during the
|
|
10
|
+
* very early config load, before the SQLite DB or getConfig().dataDir exist.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
existsSync,
|
|
15
|
+
mkdirSync,
|
|
16
|
+
readdirSync,
|
|
17
|
+
readFileSync,
|
|
18
|
+
rmSync,
|
|
19
|
+
writeFileSync,
|
|
20
|
+
} from "node:fs";
|
|
21
|
+
import { join } from "node:path";
|
|
22
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
23
|
+
|
|
24
|
+
import {
|
|
25
|
+
_writeQuarantineNotice,
|
|
26
|
+
invalidateConfigCache,
|
|
27
|
+
loadConfig,
|
|
28
|
+
} from "../config/loader.js";
|
|
29
|
+
import { getConfigQuarantineNoticePath } from "../util/platform.js";
|
|
30
|
+
import { setStorePathForTesting } from "./encrypted-store-test-helpers.js";
|
|
31
|
+
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// Helpers
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
const WORKSPACE_DIR = process.env.VELLUM_WORKSPACE_DIR!;
|
|
37
|
+
const CONFIG_PATH = join(WORKSPACE_DIR, "config.json");
|
|
38
|
+
const NOTICE_PATH = getConfigQuarantineNoticePath();
|
|
39
|
+
|
|
40
|
+
function ensureTestDir(): void {
|
|
41
|
+
const dirs = [
|
|
42
|
+
WORKSPACE_DIR,
|
|
43
|
+
join(WORKSPACE_DIR, "data"),
|
|
44
|
+
join(WORKSPACE_DIR, "data", "memory"),
|
|
45
|
+
join(WORKSPACE_DIR, "data", "memory", "knowledge"),
|
|
46
|
+
join(WORKSPACE_DIR, "data", "logs"),
|
|
47
|
+
];
|
|
48
|
+
for (const dir of dirs) {
|
|
49
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function resetWorkspace(): void {
|
|
54
|
+
for (const name of readdirSync(WORKSPACE_DIR)) {
|
|
55
|
+
rmSync(join(WORKSPACE_DIR, name), { recursive: true, force: true });
|
|
56
|
+
}
|
|
57
|
+
ensureTestDir();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function listQuarantinedFiles(): string[] {
|
|
61
|
+
return readdirSync(WORKSPACE_DIR).filter((name) =>
|
|
62
|
+
/^config\.json\.corrupt-.+\.json$/.test(name),
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function readNotice(): {
|
|
67
|
+
quarantinedAt: string;
|
|
68
|
+
quarantinePath: string;
|
|
69
|
+
originalPath: string;
|
|
70
|
+
} {
|
|
71
|
+
return JSON.parse(readFileSync(NOTICE_PATH, "utf-8"));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
// Tests
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
|
|
78
|
+
describe("config-quarantine notice sentinel", () => {
|
|
79
|
+
beforeEach(() => {
|
|
80
|
+
resetWorkspace();
|
|
81
|
+
setStorePathForTesting(join(WORKSPACE_DIR, "keys.enc"));
|
|
82
|
+
invalidateConfigCache();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
afterEach(() => {
|
|
86
|
+
setStorePathForTesting(null);
|
|
87
|
+
invalidateConfigCache();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("writes a sentinel recording the quarantine when config.json is corrupt", () => {
|
|
91
|
+
writeFileSync(CONFIG_PATH, '{"provider": "anthropic", "mo');
|
|
92
|
+
|
|
93
|
+
loadConfig();
|
|
94
|
+
|
|
95
|
+
const [quarantinedName] = listQuarantinedFiles();
|
|
96
|
+
expect(quarantinedName).toBeDefined();
|
|
97
|
+
const quarantinedPath = join(WORKSPACE_DIR, quarantinedName);
|
|
98
|
+
|
|
99
|
+
expect(existsSync(NOTICE_PATH)).toBe(true);
|
|
100
|
+
const notice = readNotice();
|
|
101
|
+
expect(notice.quarantinePath).toBe(quarantinedPath);
|
|
102
|
+
expect(notice.originalPath).toBe(CONFIG_PATH);
|
|
103
|
+
// quarantinedAt is a parseable ISO timestamp.
|
|
104
|
+
expect(Number.isNaN(Date.parse(notice.quarantinedAt))).toBe(false);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("a second quarantine overwrites the sentinel with the latest event", () => {
|
|
108
|
+
// First corruption round.
|
|
109
|
+
writeFileSync(CONFIG_PATH, '{"partial": ');
|
|
110
|
+
loadConfig();
|
|
111
|
+
invalidateConfigCache();
|
|
112
|
+
|
|
113
|
+
const firstNotice = readNotice();
|
|
114
|
+
expect(listQuarantinedFiles()).toHaveLength(1);
|
|
115
|
+
|
|
116
|
+
// Loader wrote a fresh default config.json after quarantine; corrupt it
|
|
117
|
+
// again. Spin briefly so the second quarantine gets a distinct
|
|
118
|
+
// millisecond-precision filename (and a later timestamp).
|
|
119
|
+
const untilDifferentMs = Date.now() + 5;
|
|
120
|
+
while (Date.now() < untilDifferentMs) {
|
|
121
|
+
/* spin */
|
|
122
|
+
}
|
|
123
|
+
writeFileSync(CONFIG_PATH, "still not json");
|
|
124
|
+
loadConfig();
|
|
125
|
+
|
|
126
|
+
const quarantined = listQuarantinedFiles().sort();
|
|
127
|
+
expect(quarantined).toHaveLength(2);
|
|
128
|
+
|
|
129
|
+
// Single sentinel, pointing at the most recent quarantine.
|
|
130
|
+
const secondNotice = readNotice();
|
|
131
|
+
expect(secondNotice.quarantinePath).not.toBe(firstNotice.quarantinePath);
|
|
132
|
+
expect(secondNotice.quarantinePath).toBe(
|
|
133
|
+
join(WORKSPACE_DIR, quarantined[quarantined.length - 1]),
|
|
134
|
+
);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("valid config.json does not create a sentinel (regression guard)", () => {
|
|
138
|
+
writeFileSync(
|
|
139
|
+
CONFIG_PATH,
|
|
140
|
+
JSON.stringify({ provider: "anthropic", model: "claude-opus-4-7" }),
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
loadConfig();
|
|
144
|
+
|
|
145
|
+
expect(listQuarantinedFiles()).toHaveLength(0);
|
|
146
|
+
expect(existsSync(NOTICE_PATH)).toBe(false);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test("sentinel write failure is swallowed (startup never blocks)", () => {
|
|
150
|
+
// Make the notice directory unwritable by replacing it with a file so the
|
|
151
|
+
// write/mkdir path throws. The helper must not propagate the error.
|
|
152
|
+
const noticeDir = join(WORKSPACE_DIR, "data");
|
|
153
|
+
rmSync(noticeDir, { recursive: true, force: true });
|
|
154
|
+
// A regular file where the `data` directory is expected makes mkdir/write
|
|
155
|
+
// fail with ENOTDIR/EEXIST.
|
|
156
|
+
writeFileSync(noticeDir, "not a directory", "utf-8");
|
|
157
|
+
|
|
158
|
+
const quarantinePath = join(
|
|
159
|
+
WORKSPACE_DIR,
|
|
160
|
+
"config.json.corrupt-2026-04-20T12-00-00.000Z.json",
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
expect(() =>
|
|
164
|
+
_writeQuarantineNotice(CONFIG_PATH, quarantinePath),
|
|
165
|
+
).not.toThrow();
|
|
166
|
+
});
|
|
167
|
+
});
|
|
@@ -266,12 +266,12 @@ describe("ConfigWatcher workspace file handlers", () => {
|
|
|
266
266
|
expect(identityCallCount).toBe(1);
|
|
267
267
|
});
|
|
268
268
|
|
|
269
|
-
test("
|
|
269
|
+
test("unregistered workspace files are not subscribed (only the registered handler set is)", () => {
|
|
270
270
|
watcher.start(onConversationEvict);
|
|
271
271
|
// Per-file watching only registers config.json, SOUL.md, IDENTITY.md.
|
|
272
272
|
// The whole workspace dir must not be watched either — that was the
|
|
273
273
|
// ENXIO-on-Unix-sockets bug.
|
|
274
|
-
expect(findFileWatch(join(WORKSPACE_DIR, "
|
|
274
|
+
expect(findFileWatch(join(WORKSPACE_DIR, "OTHER.md"))).toBeUndefined();
|
|
275
275
|
expect(findWatcher(WORKSPACE_DIR)).toBeUndefined();
|
|
276
276
|
});
|
|
277
277
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* `inferenceProfile` column at turn start and threads it through to
|
|
4
4
|
* `AgentLoop.run()` as the per-turn `overrideProfile`. Background
|
|
5
5
|
* conversations intentionally skip the column so background fan-out
|
|
6
|
-
* (subagents, scheduled tasks
|
|
6
|
+
* (subagents, scheduled tasks) runs on the workspace
|
|
7
7
|
* defaults rather than inheriting an interactive override.
|
|
8
8
|
*
|
|
9
9
|
* This is the "conversation-agent-loop integration" half of the
|
|
@@ -461,7 +461,7 @@ describe("classifyConversationError", () => {
|
|
|
461
461
|
const result = classifyConversationError(err, baseCtx);
|
|
462
462
|
expect(result.code).toBe("PROVIDER_NOT_CONFIGURED");
|
|
463
463
|
expect(result.userMessage).toBe(
|
|
464
|
-
"No API key configured for inference. Add one in Settings to start chatting.",
|
|
464
|
+
"No API key configured for inference. Add one in Settings → Models & Services to start chatting.",
|
|
465
465
|
);
|
|
466
466
|
expect(result.retryable).toBe(true);
|
|
467
467
|
expect(result.errorCategory).toBe("provider_not_configured");
|
|
@@ -636,11 +636,7 @@ describe("classifyConversationError", () => {
|
|
|
636
636
|
});
|
|
637
637
|
|
|
638
638
|
it("classifies ProviderError 403 with 'invalid api key' message as PROVIDER_INVALID_KEY", () => {
|
|
639
|
-
const err = new ProviderError(
|
|
640
|
-
"OpenAI: Invalid API key",
|
|
641
|
-
"openai",
|
|
642
|
-
403,
|
|
643
|
-
);
|
|
639
|
+
const err = new ProviderError("OpenAI: Invalid API key", "openai", 403);
|
|
644
640
|
const result = classifyConversationError(err, baseCtx);
|
|
645
641
|
expect(result.code).toBe("PROVIDER_INVALID_KEY");
|
|
646
642
|
expect(result.errorCategory).toBe("provider_invalid_key");
|
|
@@ -755,6 +755,7 @@ describe("web_search_tool_result structural guard", () => {
|
|
|
755
755
|
// (array of web_search_result objects) and is not truncated this way.
|
|
756
756
|
"context/tool-result-truncation.ts",
|
|
757
757
|
"context/post-turn-tool-result-truncation.ts",
|
|
758
|
+
"context/tool-result-spool.ts",
|
|
758
759
|
|
|
759
760
|
// Anthropic provider type guards define API-specific discriminants.
|
|
760
761
|
// It has a separate isWebSearchToolResultBlock for the other type.
|
|
@@ -788,6 +789,13 @@ describe("web_search_tool_result structural guard", () => {
|
|
|
788
789
|
// tool_result is relevant. Same reasoning as agent/loop.ts above.
|
|
789
790
|
"plugins/defaults/tool-error/hooks/post-tool-use.ts",
|
|
790
791
|
|
|
792
|
+
// Counts unbroken runs of locally-executed exploration tools (bash,
|
|
793
|
+
// file_read, file_list) to detect inline drift. Server-side web search
|
|
794
|
+
// results (web_search_tool_result) are not produced by the tool executor;
|
|
795
|
+
// any non-exploration block simply bounds the trailing run, which is the
|
|
796
|
+
// conservative direction (fewer nudges). Same reasoning as agent/loop.ts.
|
|
797
|
+
"plugins/defaults/exploration-drift/hooks/post-tool-use.ts",
|
|
798
|
+
|
|
791
799
|
// Reconciles synthesized cancellation tool_results for locally-executed
|
|
792
800
|
// tools only. Same reasoning as agent/loop.ts above.
|
|
793
801
|
"daemon/conversation-agent-loop.ts",
|
|
@@ -59,6 +59,7 @@ mock.module("../runtime/sync/resource-sync-events.js", () => ({
|
|
|
59
59
|
}));
|
|
60
60
|
|
|
61
61
|
import {
|
|
62
|
+
AUTO_TITLE_DETERMINISTIC,
|
|
62
63
|
generateAndPersistConversationTitle,
|
|
63
64
|
queueGenerateConversationTitle,
|
|
64
65
|
regenerateConversationTitle,
|
|
@@ -260,7 +261,7 @@ describe("conversation-title-service", () => {
|
|
|
260
261
|
expect(mockUpdateConversationTitle).toHaveBeenCalledWith(
|
|
261
262
|
"conv-1",
|
|
262
263
|
"Untitled Conversation",
|
|
263
|
-
|
|
264
|
+
AUTO_TITLE_DETERMINISTIC,
|
|
264
265
|
);
|
|
265
266
|
});
|
|
266
267
|
|
|
@@ -216,7 +216,7 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
|
|
|
216
216
|
"media/image-credentials.ts", // shared image-gen credential resolver (provider API key lookup)
|
|
217
217
|
"memory/embedding-backend.ts", // embedding backend API key lookup
|
|
218
218
|
"memory/llm-request-log-source-clickhouse.ts", // ClickHouse read source — lazy lookup of clickhouse:url + clickhouse:password + vellum:platform_assistant_id for self-scoped mirror reads
|
|
219
|
-
"memory/compaction-log-
|
|
219
|
+
"memory/compaction-log-store-clickhouse.ts", // ClickHouse compaction log writer — lazy lookup of clickhouse:url + clickhouse:password + vellum:platform_assistant_id for self-scoped event writes
|
|
220
220
|
"daemon/providers-setup.ts", // provider initialization API key lookup
|
|
221
221
|
"workspace/migrations/006-services-config.ts", // services config migration reads provider API keys
|
|
222
222
|
"workspace/migrations/018-rekey-compound-credential-keys.ts", // re-key compound credential storage keys
|
|
@@ -41,7 +41,7 @@ mock.module("../config/loader.js", () => ({
|
|
|
41
41
|
setNestedValue: () => undefined,
|
|
42
42
|
mergeDefaultWorkspaceConfig: (config: unknown) => config,
|
|
43
43
|
API_KEY_PROVIDERS: [] as const,
|
|
44
|
-
|
|
44
|
+
_writeQuarantineNotice: () => undefined,
|
|
45
45
|
}));
|
|
46
46
|
|
|
47
47
|
mock.module("../daemon/conversation-skill-tools.js", () => ({
|