@vellumai/assistant 0.8.11 → 0.8.12-staging.1
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 +126 -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-usage-store.test.ts +11 -0
- 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__/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-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-release-notes-feature-flag-guard.test.ts +45 -95
- 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/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 +301 -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 +219 -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/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/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/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-intents.ts +2 -2
- package/src/providers/openai/chat-completions-provider.ts +2 -1
- package/src/providers/openai/responses-provider.ts +5 -1
- package/src/providers/retry.ts +8 -0
- package/src/providers/types.ts +11 -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__/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 +1 -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/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/plugins-routes.ts +21 -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/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/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/registry.ts +4 -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,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Upgrade a single installed plugin to the marketplace's current pin.
|
|
3
|
+
*
|
|
4
|
+
* The marketplace pins every plugin to a full, immutable commit SHA (see
|
|
5
|
+
* {@link ./plugin-marketplace}); an upgrade re-materializes the install at
|
|
6
|
+
* whatever SHA the catalog currently advertises. Drift is detected with the
|
|
7
|
+
* same exact commit-SHA comparison {@link ./inspect-plugin} uses, so an
|
|
8
|
+
* upgrade is a no-op when the installed copy already matches the pin.
|
|
9
|
+
*
|
|
10
|
+
* This is deliberately a distinct operation from install: `install` is
|
|
11
|
+
* first-time materialization (and errors on an existing install unless
|
|
12
|
+
* `--force` is passed), whereas `upgrade` moves an existing install forward.
|
|
13
|
+
* Mechanically the move is a forced re-install at the current pin, which the
|
|
14
|
+
* underlying {@link ./install-from-github.installPlugin} performs atomically —
|
|
15
|
+
* the previously installed copy is preserved until the fetch succeeds.
|
|
16
|
+
*
|
|
17
|
+
* Conflict resolution for locally-modified plugins is intentionally out of
|
|
18
|
+
* scope here: this overwrites the install with the pinned tree. A future
|
|
19
|
+
* iteration will detect local edits (installed SHA = merge base) and resolve
|
|
20
|
+
* them before the swap.
|
|
21
|
+
*
|
|
22
|
+
* Designed for direct programmatic use with injected dependencies, mirroring
|
|
23
|
+
* the sibling plugin libraries. The CLI command `assistant plugins upgrade
|
|
24
|
+
* <name>` is a thin wrapper that supplies production deps and formats the
|
|
25
|
+
* result.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { join } from "node:path";
|
|
29
|
+
|
|
30
|
+
import { getWorkspacePluginsDir } from "../../util/platform.js";
|
|
31
|
+
import {
|
|
32
|
+
inspectPlugin,
|
|
33
|
+
type PluginInspection,
|
|
34
|
+
PluginInspectNotFoundError,
|
|
35
|
+
} from "./inspect-plugin.js";
|
|
36
|
+
import {
|
|
37
|
+
type FetchLike,
|
|
38
|
+
type GitRunner,
|
|
39
|
+
installPlugin,
|
|
40
|
+
type PostinstallRunner,
|
|
41
|
+
sanitizePluginName,
|
|
42
|
+
} from "./install-from-github.js";
|
|
43
|
+
import { PluginNotInstalledError } from "./uninstall-plugin.js";
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Outcome of an upgrade attempt.
|
|
47
|
+
*
|
|
48
|
+
* - `upgraded` — the install was moved to the current marketplace pin.
|
|
49
|
+
* - `already-up-to-date` — the installed commit already equals the pin; no-op.
|
|
50
|
+
* - `would-upgrade` — a `--dry-run` that found drift but made no changes.
|
|
51
|
+
*/
|
|
52
|
+
export type PluginUpgradeOutcome =
|
|
53
|
+
| "upgraded"
|
|
54
|
+
| "already-up-to-date"
|
|
55
|
+
| "would-upgrade";
|
|
56
|
+
|
|
57
|
+
/** Options that control which plugin to upgrade and how. */
|
|
58
|
+
export interface UpgradePluginOptions {
|
|
59
|
+
/** Install name (kebab-case directory name). */
|
|
60
|
+
readonly name: string;
|
|
61
|
+
/** Report what would change without modifying the install. */
|
|
62
|
+
readonly dryRun?: boolean;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Dependencies injected by the caller. */
|
|
66
|
+
export interface UpgradePluginDeps {
|
|
67
|
+
/** HTTP client. Production callers pass `globalThis.fetch.bind(globalThis)`. */
|
|
68
|
+
readonly fetch: FetchLike;
|
|
69
|
+
/** Override the workspace plugins directory. Falls back to the live workspace. */
|
|
70
|
+
readonly workspacePluginsDir?: string;
|
|
71
|
+
/** Override the git runner used to clone the source. Forwarded to {@link installPlugin}. */
|
|
72
|
+
readonly runGit?: GitRunner;
|
|
73
|
+
/** Override the postinstall adapter runner. Forwarded to {@link installPlugin}. */
|
|
74
|
+
readonly runPostinstall?: PostinstallRunner;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Result of an upgrade attempt. */
|
|
78
|
+
export interface PluginUpgradeResult {
|
|
79
|
+
readonly name: string;
|
|
80
|
+
readonly outcome: PluginUpgradeOutcome;
|
|
81
|
+
/** Installed commit before the upgrade; `null` when no provenance was recorded. */
|
|
82
|
+
readonly fromCommit: string | null;
|
|
83
|
+
/** Marketplace-pinned commit the install was (or would be) moved to. */
|
|
84
|
+
readonly toCommit: string;
|
|
85
|
+
/** Absolute path to the installed plugin directory. */
|
|
86
|
+
readonly target: string;
|
|
87
|
+
/** Files materialized by the upgrade; `null` for a no-op or dry run. */
|
|
88
|
+
readonly fileCount: number | null;
|
|
89
|
+
/** Whether this was a dry run (no changes made). */
|
|
90
|
+
readonly dryRun: boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Whether the installed copy lacked resolvable provenance before the
|
|
93
|
+
* upgrade. Such installs are re-pinned to the current SHA, which also
|
|
94
|
+
* records provenance going forward.
|
|
95
|
+
*/
|
|
96
|
+
readonly provenanceWasUnknown: boolean;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** An installed plugin has no marketplace pin to upgrade to. */
|
|
100
|
+
export class PluginNotUpgradableError extends Error {
|
|
101
|
+
constructor(
|
|
102
|
+
readonly pluginName: string,
|
|
103
|
+
reason: string,
|
|
104
|
+
) {
|
|
105
|
+
super(`Plugin "${pluginName}" cannot be upgraded: ${reason}.`);
|
|
106
|
+
this.name = "PluginNotUpgradableError";
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function pluginTarget(name: string, deps: UpgradePluginDeps): string {
|
|
111
|
+
const dir = deps.workspacePluginsDir ?? getWorkspacePluginsDir();
|
|
112
|
+
return join(dir, name);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Move an installed plugin to the marketplace's current pin.
|
|
117
|
+
*
|
|
118
|
+
* Throws {@link PluginNotInstalledError} when no copy is installed,
|
|
119
|
+
* {@link PluginNotUpgradableError} when the install has no marketplace pin to
|
|
120
|
+
* advance to (no catalog entry, or the catalog was unreachable), and
|
|
121
|
+
* propagates {@link installPlugin}'s errors (e.g. source unavailable,
|
|
122
|
+
* postinstall failure) when the re-install itself fails.
|
|
123
|
+
*/
|
|
124
|
+
export async function upgradePlugin(
|
|
125
|
+
opts: UpgradePluginOptions,
|
|
126
|
+
deps: UpgradePluginDeps,
|
|
127
|
+
): Promise<PluginUpgradeResult> {
|
|
128
|
+
const name = sanitizePluginName(opts.name);
|
|
129
|
+
const dryRun = opts.dryRun ?? false;
|
|
130
|
+
|
|
131
|
+
let inspection: PluginInspection;
|
|
132
|
+
try {
|
|
133
|
+
inspection = await inspectPlugin(
|
|
134
|
+
{ name },
|
|
135
|
+
{ fetch: deps.fetch, workspacePluginsDir: deps.workspacePluginsDir },
|
|
136
|
+
);
|
|
137
|
+
} catch (err) {
|
|
138
|
+
if (err instanceof PluginInspectNotFoundError) {
|
|
139
|
+
throw new PluginNotInstalledError(name, pluginTarget(name, deps));
|
|
140
|
+
}
|
|
141
|
+
throw err;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
switch (inspection.status) {
|
|
145
|
+
case "not-installed":
|
|
146
|
+
throw new PluginNotInstalledError(name, pluginTarget(name, deps));
|
|
147
|
+
case "not-in-marketplace":
|
|
148
|
+
throw new PluginNotUpgradableError(
|
|
149
|
+
name,
|
|
150
|
+
"it has no marketplace entry to upgrade from",
|
|
151
|
+
);
|
|
152
|
+
case "remote-unavailable":
|
|
153
|
+
throw new PluginNotUpgradableError(
|
|
154
|
+
name,
|
|
155
|
+
`the marketplace could not be reached (${inspection.remoteError ?? "unknown error"})`,
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// The remaining statuses (up-to-date, update-available, unknown-provenance)
|
|
160
|
+
// all imply an installed copy and a resolvable marketplace pin.
|
|
161
|
+
const { local, remote } = inspection;
|
|
162
|
+
if (!local || !remote) {
|
|
163
|
+
throw new PluginNotUpgradableError(
|
|
164
|
+
name,
|
|
165
|
+
"its install or marketplace metadata could not be resolved",
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const fromCommit = local.commit;
|
|
170
|
+
const toCommit = remote.commit;
|
|
171
|
+
const provenanceWasUnknown = inspection.status === "unknown-provenance";
|
|
172
|
+
|
|
173
|
+
if (inspection.status === "up-to-date") {
|
|
174
|
+
return {
|
|
175
|
+
name,
|
|
176
|
+
outcome: "already-up-to-date",
|
|
177
|
+
fromCommit,
|
|
178
|
+
toCommit,
|
|
179
|
+
target: local.target,
|
|
180
|
+
fileCount: null,
|
|
181
|
+
dryRun,
|
|
182
|
+
provenanceWasUnknown: false,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (dryRun) {
|
|
187
|
+
return {
|
|
188
|
+
name,
|
|
189
|
+
outcome: "would-upgrade",
|
|
190
|
+
fromCommit,
|
|
191
|
+
toCommit,
|
|
192
|
+
target: local.target,
|
|
193
|
+
fileCount: null,
|
|
194
|
+
dryRun: true,
|
|
195
|
+
provenanceWasUnknown,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const result = await installPlugin(
|
|
200
|
+
{ name, force: true },
|
|
201
|
+
{
|
|
202
|
+
fetch: deps.fetch,
|
|
203
|
+
workspacePluginsDir: deps.workspacePluginsDir,
|
|
204
|
+
runGit: deps.runGit,
|
|
205
|
+
runPostinstall: deps.runPostinstall,
|
|
206
|
+
},
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
name,
|
|
211
|
+
outcome: "upgraded",
|
|
212
|
+
fromCommit,
|
|
213
|
+
toCommit: result.commit ?? toCommit,
|
|
214
|
+
target: result.target,
|
|
215
|
+
fileCount: result.fileCount,
|
|
216
|
+
dryRun: false,
|
|
217
|
+
provenanceWasUnknown,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
@@ -39,6 +39,7 @@ Each subagent is spawned with a role that determines its tool access. Choose the
|
|
|
39
39
|
| `researcher` | `web_search`, `web_fetch`, `file_read`, `file_list`, `recall`, `notify_parent` | Information gathering, web research, codebase exploration, reading documentation |
|
|
40
40
|
| `coder` | `bash`, `file_read`, `file_write`, `file_edit`, `web_search`, `recall`, `notify_parent` | Code changes, file editing, running commands, build/test tasks |
|
|
41
41
|
| `planner` | `file_read`, `file_list`, `web_search`, `web_fetch`, `recall`, `notify_parent` | Analysis, planning, synthesizing information, reviewing approaches |
|
|
42
|
+
| `investigator` | `bash`, `file_read`, `file_list`, `web_search`, `web_fetch`, `recall`, `notify_parent` | Root-cause analysis: debugging, log forensics, tracing behavior across many files. Shell access is for read-only investigation (grep/find/reading logs); returns a compact root-cause report |
|
|
42
43
|
|
|
43
44
|
All specialized roles (`researcher`, `coder`, `planner`) include `notify_parent` for mid-run communication with the parent.
|
|
44
45
|
|
|
@@ -97,5 +98,6 @@ Rule of thumb: "Does this task need to know what we've been talking about?" If y
|
|
|
97
98
|
- Use `subagent_message` to send follow-up instructions to a running subagent.
|
|
98
99
|
- Use `subagent_abort` to cancel a subagent that is no longer needed.
|
|
99
100
|
- Default to spawning subagents for any task that involves web research, multi-file exploration, or independent coding work. Serial execution should be the exception, not the rule.
|
|
101
|
+
- Delegate root-cause investigations ("why is X happening?", debugging, log forensics) to an `investigator` instead of grepping inline. A long investigation done inline floods your own context with file slices and grep output, crowding out the conversation; the investigator does the digging in its own context and returns a compact root-cause report.
|
|
100
102
|
- When a user request has both an information-gathering component and an action component, spawn a researcher immediately rather than doing the research inline yourself.
|
|
101
103
|
- Prefer spawning 2-3 focused subagents over one large general-purpose subagent. Smaller scopes finish faster and fail more gracefully.
|
|
@@ -31,8 +31,14 @@
|
|
|
31
31
|
},
|
|
32
32
|
"role": {
|
|
33
33
|
"type": "string",
|
|
34
|
-
"enum": [
|
|
35
|
-
|
|
34
|
+
"enum": [
|
|
35
|
+
"general",
|
|
36
|
+
"researcher",
|
|
37
|
+
"coder",
|
|
38
|
+
"planner",
|
|
39
|
+
"investigator"
|
|
40
|
+
],
|
|
41
|
+
"description": "Agent specialization that controls tool access. 'researcher': read-only (web, files, memory). 'coder': file and bash access. 'planner': read-only analysis. 'investigator': root-cause analysis — debugging, log forensics, tracing behavior across many files; has bash for read-only investigation (grep/find/log inspection) and returns a compact root-cause report. 'general': full access (default). Ignored when fork: true (forks always use general)."
|
|
36
42
|
}
|
|
37
43
|
},
|
|
38
44
|
"required": ["label", "objective"]
|
|
@@ -7,6 +7,13 @@ type CallSiteDefaultConfig = {
|
|
|
7
7
|
temperature?: number | null;
|
|
8
8
|
thinking?: { enabled?: boolean; streamThinking?: boolean };
|
|
9
9
|
contextWindow?: { maxInputTokens?: number };
|
|
10
|
+
/**
|
|
11
|
+
* Opt the call site out of prompt caching. Set for one-shot call sites
|
|
12
|
+
* whose prompts never repeat — or repeat slower than the cache TTL — so
|
|
13
|
+
* each call would pay the cache-write premium without a future read.
|
|
14
|
+
* Telemetry confirms ~0–5% cache hit rates on these sites.
|
|
15
|
+
*/
|
|
16
|
+
disableCache?: boolean;
|
|
10
17
|
};
|
|
11
18
|
|
|
12
19
|
export const CALL_SITE_DEFAULTS: Record<LLMCallSite, CallSiteDefaultConfig> = {
|
|
@@ -17,7 +24,7 @@ export const CALL_SITE_DEFAULTS: Record<LLMCallSite, CallSiteDefaultConfig> = {
|
|
|
17
24
|
patternScan: { profile: "balanced" },
|
|
18
25
|
narrativeRefinement: { profile: "balanced" },
|
|
19
26
|
callAgent: { profile: "balanced" },
|
|
20
|
-
memoryConsolidation: { profile: "balanced" },
|
|
27
|
+
memoryConsolidation: { profile: "balanced", disableCache: true },
|
|
21
28
|
identityIntro: { profile: "balanced" },
|
|
22
29
|
emptyStateGreeting: { profile: "balanced" },
|
|
23
30
|
|
|
@@ -32,6 +39,7 @@ export const CALL_SITE_DEFAULTS: Record<LLMCallSite, CallSiteDefaultConfig> = {
|
|
|
32
39
|
effort: "low",
|
|
33
40
|
thinking: { enabled: false, streamThinking: false },
|
|
34
41
|
temperature: 0,
|
|
42
|
+
disableCache: true,
|
|
35
43
|
},
|
|
36
44
|
conversationStarters: {
|
|
37
45
|
profile: "balanced",
|
|
@@ -47,7 +55,7 @@ export const CALL_SITE_DEFAULTS: Record<LLMCallSite, CallSiteDefaultConfig> = {
|
|
|
47
55
|
memoryV2Sweep: { profile: "cost-optimized" },
|
|
48
56
|
memoryV2Consolidation: { profile: "balanced" },
|
|
49
57
|
conversationSummarization: { profile: "cost-optimized" },
|
|
50
|
-
conversationTitle: { profile: "cost-optimized" },
|
|
58
|
+
conversationTitle: { profile: "cost-optimized", disableCache: true },
|
|
51
59
|
approvalCopy: { profile: "cost-optimized" },
|
|
52
60
|
approvalConversation: { profile: "cost-optimized" },
|
|
53
61
|
trustRuleSuggestion: { profile: "cost-optimized" },
|
|
@@ -70,6 +78,7 @@ export const CALL_SITE_DEFAULTS: Record<LLMCallSite, CallSiteDefaultConfig> = {
|
|
|
70
78
|
profile: "cost-optimized",
|
|
71
79
|
effort: "low",
|
|
72
80
|
thinking: { enabled: false },
|
|
81
|
+
disableCache: true,
|
|
73
82
|
},
|
|
74
83
|
guardianQuestionCopy: {
|
|
75
84
|
profile: "cost-optimized",
|
|
@@ -107,11 +116,13 @@ export const CALL_SITE_DEFAULTS: Record<LLMCallSite, CallSiteDefaultConfig> = {
|
|
|
107
116
|
effort: "low",
|
|
108
117
|
thinking: { enabled: false },
|
|
109
118
|
temperature: 0.7,
|
|
119
|
+
disableCache: true,
|
|
110
120
|
},
|
|
111
121
|
homeSuggestedPrompts: {
|
|
112
122
|
profile: "cost-optimized",
|
|
113
123
|
maxTokens: 512,
|
|
114
124
|
effort: "low",
|
|
115
125
|
thinking: { enabled: false },
|
|
126
|
+
disableCache: true,
|
|
116
127
|
},
|
|
117
128
|
};
|
|
@@ -75,14 +75,6 @@
|
|
|
75
75
|
"description": "Control Developer nav visibility in macOS settings",
|
|
76
76
|
"defaultEnabled": false
|
|
77
77
|
},
|
|
78
|
-
{
|
|
79
|
-
"id": "system-schedule-toggles",
|
|
80
|
-
"scope": "assistant",
|
|
81
|
-
"key": "system-schedule-toggles",
|
|
82
|
-
"label": "System Schedule Toggles",
|
|
83
|
-
"description": "Show the Heartbeat pause toggle on the schedules settings page. The toggle only pauses automatic runs; manual Run now remains available.",
|
|
84
|
-
"defaultEnabled": false
|
|
85
|
-
},
|
|
86
78
|
{
|
|
87
79
|
"id": "developer-menu-items",
|
|
88
80
|
"scope": "client",
|
|
@@ -363,6 +355,14 @@
|
|
|
363
355
|
"description": "Enable self-hosted assistant configuration.",
|
|
364
356
|
"defaultEnabled": false
|
|
365
357
|
},
|
|
358
|
+
{
|
|
359
|
+
"id": "web-remote-ingress",
|
|
360
|
+
"scope": "assistant",
|
|
361
|
+
"key": "web-remote-ingress",
|
|
362
|
+
"label": "Web Remote Ingress",
|
|
363
|
+
"description": "Serve the web client over a public tunnel and enable browser pairing for self-hosted assistants.",
|
|
364
|
+
"defaultEnabled": false
|
|
365
|
+
},
|
|
366
366
|
{
|
|
367
367
|
"id": "settings-sleep-policy",
|
|
368
368
|
"scope": "assistant",
|
|
@@ -403,14 +403,6 @@
|
|
|
403
403
|
"description": "When on, memory-v3 topic-tree retrieval becomes the live injected memory source (suppressing v2 injection). Off by default; only enable after shadow telemetry validates the design.",
|
|
404
404
|
"defaultEnabled": false
|
|
405
405
|
},
|
|
406
|
-
{
|
|
407
|
-
"id": "empty-state-dynamic-greetings",
|
|
408
|
-
"scope": "assistant",
|
|
409
|
-
"key": "empty-state-dynamic-greetings",
|
|
410
|
-
"label": "Dynamic Empty-State Greetings",
|
|
411
|
-
"description": "Enable the empty new-chat screen to refresh cached greeting options via the empty-state greeting LLM call site. When off, the assistant serves workspace-authored greetings or static fallbacks only.",
|
|
412
|
-
"defaultEnabled": false
|
|
413
|
-
},
|
|
414
406
|
{
|
|
415
407
|
"id": "self-intro-greeting",
|
|
416
408
|
"scope": "both",
|
package/src/config/loader.ts
CHANGED
|
@@ -5,14 +5,14 @@ import {
|
|
|
5
5
|
renameSync,
|
|
6
6
|
writeFileSync,
|
|
7
7
|
} from "node:fs";
|
|
8
|
-
import {
|
|
8
|
+
import { dirname, join } from "node:path";
|
|
9
9
|
|
|
10
10
|
import { safeStatSync } from "../util/fs.js";
|
|
11
11
|
import { getLogger } from "../util/logger.js";
|
|
12
12
|
import {
|
|
13
13
|
ensureDataDir,
|
|
14
|
+
getConfigQuarantineNoticePath,
|
|
14
15
|
getWorkspaceConfigPath,
|
|
15
|
-
getWorkspaceDir,
|
|
16
16
|
} from "../util/platform.js";
|
|
17
17
|
import { AssistantConfigSchema } from "./schema.js";
|
|
18
18
|
import type { AssistantConfig } from "./types.js";
|
|
@@ -201,10 +201,9 @@ function filesystemSafeTimestamp(date: Date = new Date()): string {
|
|
|
201
201
|
* in `.json` so editors syntax-highlight the preserved content:
|
|
202
202
|
* `<path>.corrupt-<ISO-timestamp>.json`
|
|
203
203
|
*
|
|
204
|
-
* On a successful rename, also
|
|
205
|
-
* so the
|
|
206
|
-
*
|
|
207
|
-
* to users).
|
|
204
|
+
* On a successful rename, also writes a small JSON sentinel recording the
|
|
205
|
+
* event so the per-turn `config-quarantine-notice` injector can surface it to
|
|
206
|
+
* the agent (log-level errors alone are invisible to users).
|
|
208
207
|
*/
|
|
209
208
|
function quarantineCorruptConfig(configPath: string, err: unknown): string {
|
|
210
209
|
const quarantinePath = `${configPath}.corrupt-${filesystemSafeTimestamp()}.json`;
|
|
@@ -215,7 +214,7 @@ function quarantineCorruptConfig(configPath: string, err: unknown): string {
|
|
|
215
214
|
`quarantined to ${quarantinePath} and loaded defaults. ` +
|
|
216
215
|
`Inspect the quarantined file to recover any hand-edited settings.`,
|
|
217
216
|
);
|
|
218
|
-
|
|
217
|
+
writeQuarantineNotice(configPath, quarantinePath);
|
|
219
218
|
} catch (renameErr) {
|
|
220
219
|
log.error(
|
|
221
220
|
{ renameErr },
|
|
@@ -227,66 +226,55 @@ function quarantineCorruptConfig(configPath: string, err: unknown): string {
|
|
|
227
226
|
}
|
|
228
227
|
|
|
229
228
|
/**
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
* cases are the user asking why their settings changed or noticing missing
|
|
235
|
-
* API keys. The bulletin is agent-visible context, not a push notification.
|
|
229
|
+
* Write a small JSON sentinel recording that the config file was quarantined.
|
|
230
|
+
* The per-turn `config-quarantine-notice` injector reads this sentinel and, if
|
|
231
|
+
* it is recent, injects a system block so the agent can explain the reset when
|
|
232
|
+
* the user asks why their settings changed or notices missing API keys.
|
|
236
233
|
*
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
234
|
+
* Writes with pure `node:fs` and a workspace-derived path
|
|
235
|
+
* ({@link getConfigQuarantineNoticePath}) deliberately: config load happens
|
|
236
|
+
* extremely early at daemon startup — before the SQLite DB is initialized and
|
|
237
|
+
* before `getConfig().dataDir` is available — so neither a DB checkpoint nor a
|
|
238
|
+
* config-dependent path can be used here without risking import-time DB init.
|
|
239
|
+
*
|
|
240
|
+
* Idempotent per quarantine event: each call overwrites the sentinel with the
|
|
241
|
+
* latest event, so a crash-then-retry re-records the same (or newer) event
|
|
242
|
+
* rather than accumulating duplicates.
|
|
243
243
|
*
|
|
244
244
|
* Best-effort: any write failure is logged at `warn` and swallowed. The
|
|
245
245
|
* quarantine path must never block startup, and the error log from
|
|
246
246
|
* `quarantineCorruptConfig` remains the authoritative record.
|
|
247
247
|
*
|
|
248
|
-
* Exported with an underscore-prefixed alias (`
|
|
249
|
-
* tests can exercise the
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
* point.
|
|
248
|
+
* Exported with an underscore-prefixed alias (`_writeQuarantineNotice`) so
|
|
249
|
+
* tests can exercise the write directly. Non-test callers should never import
|
|
250
|
+
* the underscore alias — the wiring into `quarantineCorruptConfig` is the
|
|
251
|
+
* production entry point.
|
|
253
252
|
*/
|
|
254
|
-
function
|
|
253
|
+
function writeQuarantineNotice(
|
|
255
254
|
originalPath: string,
|
|
256
255
|
quarantinePath: string,
|
|
257
256
|
): void {
|
|
258
257
|
try {
|
|
259
|
-
const
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
:
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
`## Config was reset to defaults\n\n` +
|
|
271
|
-
`Your \`config.json\` was unreadable at ${timestamp} and couldn't be parsed ` +
|
|
272
|
-
`as JSON. The assistant preserved the original file at \`${quarantinePath}\` ` +
|
|
273
|
-
`and loaded defaults so the app stays working.\n\n` +
|
|
274
|
-
`If you had custom settings (API keys, model choices, voice preferences), ` +
|
|
275
|
-
`they are still in the quarantined file — \`cat ${quarantinePath}\` to ` +
|
|
276
|
-
`recover them, then re-enter through Settings or the CLI.\n\n` +
|
|
277
|
-
`${marker}\n`;
|
|
278
|
-
|
|
279
|
-
const toWrite = existing.length === 0 ? block : `${existing}\n${block}`;
|
|
280
|
-
writeFileSync(updatesPath, toWrite, "utf-8");
|
|
258
|
+
const noticePath = getConfigQuarantineNoticePath();
|
|
259
|
+
const dir = dirname(noticePath);
|
|
260
|
+
if (!existsSync(dir)) {
|
|
261
|
+
mkdirSync(dir, { recursive: true });
|
|
262
|
+
}
|
|
263
|
+
const notice = {
|
|
264
|
+
quarantinedAt: new Date().toISOString(),
|
|
265
|
+
quarantinePath,
|
|
266
|
+
originalPath,
|
|
267
|
+
};
|
|
268
|
+
writeFileSync(noticePath, JSON.stringify(notice, null, 2) + "\n", "utf-8");
|
|
281
269
|
log.info(
|
|
282
|
-
`
|
|
283
|
-
`(quarantined as ${
|
|
270
|
+
`Wrote config-quarantine notice to ${noticePath} for ${originalPath} ` +
|
|
271
|
+
`(quarantined as ${quarantinePath}).`,
|
|
284
272
|
);
|
|
285
|
-
} catch (
|
|
273
|
+
} catch (noticeErr) {
|
|
286
274
|
log.warn(
|
|
287
|
-
{
|
|
288
|
-
`Failed to
|
|
289
|
-
`
|
|
275
|
+
{ noticeErr },
|
|
276
|
+
`Failed to write config-quarantine notice; the quarantine event is ` +
|
|
277
|
+
`still recorded in the assistant logs.`,
|
|
290
278
|
);
|
|
291
279
|
}
|
|
292
280
|
}
|
|
@@ -660,7 +648,9 @@ export function loadConfig(): AssistantConfig {
|
|
|
660
648
|
quarantineCorruptConfig(
|
|
661
649
|
configPath,
|
|
662
650
|
new Error(
|
|
663
|
-
`config.json must contain a JSON object at the top level; got ${describeJsonShape(
|
|
651
|
+
`config.json must contain a JSON object at the top level; got ${describeJsonShape(
|
|
652
|
+
parsed,
|
|
653
|
+
)}`,
|
|
664
654
|
),
|
|
665
655
|
);
|
|
666
656
|
fileConfig = {};
|
|
@@ -889,7 +879,9 @@ export function loadRawConfig(): Record<string, unknown> {
|
|
|
889
879
|
quarantineCorruptConfig(
|
|
890
880
|
configPath,
|
|
891
881
|
new Error(
|
|
892
|
-
`config.json must contain a JSON object at the top level; got ${describeJsonShape(
|
|
882
|
+
`config.json must contain a JSON object at the top level; got ${describeJsonShape(
|
|
883
|
+
parsed,
|
|
884
|
+
)}`,
|
|
893
885
|
),
|
|
894
886
|
);
|
|
895
887
|
return {};
|
|
@@ -963,8 +955,9 @@ export function setNestedValue(
|
|
|
963
955
|
}
|
|
964
956
|
|
|
965
957
|
/**
|
|
966
|
-
* Test-only alias for `
|
|
967
|
-
*
|
|
968
|
-
*
|
|
958
|
+
* Test-only alias for `writeQuarantineNotice`. Exists so the sentinel write
|
|
959
|
+
* (and its overwrite/idempotency semantics) can be exercised directly with a
|
|
960
|
+
* deterministic quarantine path without widening the runtime surface. Not for
|
|
961
|
+
* production use.
|
|
969
962
|
*/
|
|
970
|
-
export const
|
|
963
|
+
export const _writeQuarantineNotice = writeQuarantineNotice;
|
package/src/config/schema.ts
CHANGED
|
@@ -56,7 +56,6 @@ import {
|
|
|
56
56
|
TimeoutConfigSchema,
|
|
57
57
|
} from "./schemas/timeouts.js";
|
|
58
58
|
import { ToolsConfigSchema } from "./schemas/tools.js";
|
|
59
|
-
import { UpdatesConfigSchema } from "./schemas/updates.js";
|
|
60
59
|
import { WorkspaceGitConfigSchema } from "./schemas/workspace-git.js";
|
|
61
60
|
|
|
62
61
|
export const AssistantConfigSchema = z
|
|
@@ -89,7 +88,6 @@ export const AssistantConfigSchema = z
|
|
|
89
88
|
llmRequestLogs: LlmRequestLogsConfigSchema,
|
|
90
89
|
filing: FilingConfigSchema.default(FilingConfigSchema.parse({})),
|
|
91
90
|
heartbeat: HeartbeatConfigSchema.default(HeartbeatConfigSchema.parse({})),
|
|
92
|
-
updates: UpdatesConfigSchema.default(UpdatesConfigSchema.parse({})),
|
|
93
91
|
hostBrowser: HostBrowserConfigSchema.default(
|
|
94
92
|
HostBrowserConfigSchema.parse({}),
|
|
95
93
|
),
|
|
@@ -8,9 +8,19 @@ describe("MemoryV3ConfigSchema", () => {
|
|
|
8
8
|
expect(parsed).toEqual({
|
|
9
9
|
prune: { maxResidentBytes: 393216, targetResidentBytes: 262144 },
|
|
10
10
|
hotSet: { k: 40, halfLifeDays: 14 },
|
|
11
|
+
freshSet: { k: 100 },
|
|
12
|
+
learnedEdges: {
|
|
13
|
+
halfLifeDays: 30,
|
|
14
|
+
minCount: 3,
|
|
15
|
+
npmiFloor: 0.2,
|
|
16
|
+
maxPerPage: 6,
|
|
17
|
+
perSeed: 3,
|
|
18
|
+
cap: 20,
|
|
19
|
+
},
|
|
11
20
|
spotlight: { n: 6, windowTurns: 2 },
|
|
12
21
|
needleK: 100,
|
|
13
22
|
denseK: 100,
|
|
23
|
+
replyQueryK: 12,
|
|
14
24
|
edge: { hubDegree: 30, seedCount: 18, perSeed: 6, cap: 45 },
|
|
15
25
|
});
|
|
16
26
|
});
|
|
@@ -331,6 +331,15 @@ export const LLMConfigBase = z.object({
|
|
|
331
331
|
// `profileConfigFragment`, which strips it from the merge), so a preset
|
|
332
332
|
// can't bleed from a lower-precedence profile into one that didn't opt in.
|
|
333
333
|
logitBias: LogitBiasPresetSchema.optional(),
|
|
334
|
+
/**
|
|
335
|
+
* Opt this config out of prompt caching. Providers send no cache
|
|
336
|
+
* breakpoints and strip caller-stamped `cache_control` markers. Intended
|
|
337
|
+
* for one-shot call sites whose prompts never repeat (or repeat slower
|
|
338
|
+
* than the cache TTL), where every breakpoint is a paid cache write with
|
|
339
|
+
* no future read. Optional (no schema default) so it only appears in
|
|
340
|
+
* resolved configs when a layer sets it.
|
|
341
|
+
*/
|
|
342
|
+
disableCache: z.boolean().optional(),
|
|
334
343
|
});
|
|
335
344
|
export type LLMConfigBase = z.infer<typeof LLMConfigBase>;
|
|
336
345
|
|
|
@@ -352,6 +361,7 @@ const LLMConfigFragment = z.object({
|
|
|
352
361
|
contextWindow: ContextWindowDeepPartialSchema.optional(),
|
|
353
362
|
openrouter: OpenRouterDeepPartialSchema.optional(),
|
|
354
363
|
logitBias: LogitBiasPresetSchema.optional(),
|
|
364
|
+
disableCache: z.boolean().optional(),
|
|
355
365
|
});
|
|
356
366
|
type LLMConfigFragment = z.infer<typeof LLMConfigFragment>;
|
|
357
367
|
|
|
@@ -207,6 +207,19 @@ export const MemoryV2ConfigSchema = z
|
|
|
207
207
|
.describe(
|
|
208
208
|
"Size-based trigger for consolidation. When `memory/buffer.md` reaches this many non-empty lines, consolidation runs even if the time-based interval hasn't elapsed. Defaults to 100. Set to `null` to disable the size trigger and rely solely on `consolidation_interval_hours`.",
|
|
209
209
|
),
|
|
210
|
+
consolidation_max_entries_per_run: z
|
|
211
|
+
.number({
|
|
212
|
+
error: "memory.v2.consolidation_max_entries_per_run must be a number",
|
|
213
|
+
})
|
|
214
|
+
.int("memory.v2.consolidation_max_entries_per_run must be an integer")
|
|
215
|
+
.positive(
|
|
216
|
+
"memory.v2.consolidation_max_entries_per_run must be a positive integer",
|
|
217
|
+
)
|
|
218
|
+
.nullable()
|
|
219
|
+
.default(150)
|
|
220
|
+
.describe(
|
|
221
|
+
"Upper bound on buffer entries one consolidation run may process. When the buffer holds more, the run's cutoff is moved back to the first over-cap entry's timestamp so the overflow is deferred to a follow-up pass (the `consolidation_max_buffer_lines` size trigger re-fires while the remainder stays over threshold). Bounds the context a single agentic run must read after a backlog. Set to `null` to always process the full buffer.",
|
|
222
|
+
),
|
|
210
223
|
max_page_chars: z
|
|
211
224
|
.number({ error: "memory.v2.max_page_chars must be a number" })
|
|
212
225
|
.int("memory.v2.max_page_chars must be an integer")
|