@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
|
@@ -1364,6 +1364,40 @@ describe("custom baseURL initialization", () => {
|
|
|
1364
1364
|
timeout: DEFAULT_SDK_TIMEOUT_MS,
|
|
1365
1365
|
});
|
|
1366
1366
|
});
|
|
1367
|
+
|
|
1368
|
+
test("MinimaxProvider sends reasoning_split so reasoning never leaks into content as <think> tags", async () => {
|
|
1369
|
+
fakeChunks = [textChunk("hello", "stop"), usageChunk(10, 5)];
|
|
1370
|
+
const provider = new MinimaxProvider("mm-user-key", "MiniMax-M3");
|
|
1371
|
+
|
|
1372
|
+
await provider.sendMessage([userMsg("hi")]);
|
|
1373
|
+
|
|
1374
|
+
expect(lastCreateParams!.reasoning_split).toBe(true);
|
|
1375
|
+
});
|
|
1376
|
+
|
|
1377
|
+
test("MinimaxProvider replays prior assistant thinking via reasoning_content", async () => {
|
|
1378
|
+
fakeChunks = [textChunk("done", "stop"), usageChunk(10, 5)];
|
|
1379
|
+
const provider = new MinimaxProvider("mm-user-key", "MiniMax-M3");
|
|
1380
|
+
|
|
1381
|
+
await provider.sendMessage([
|
|
1382
|
+
userMsg("question"),
|
|
1383
|
+
{
|
|
1384
|
+
role: "assistant",
|
|
1385
|
+
content: [
|
|
1386
|
+
{ type: "thinking", thinking: "prior reasoning", signature: "" },
|
|
1387
|
+
{ type: "text", text: "prior answer" },
|
|
1388
|
+
],
|
|
1389
|
+
},
|
|
1390
|
+
userMsg("follow-up"),
|
|
1391
|
+
]);
|
|
1392
|
+
|
|
1393
|
+
const sent = lastCreateParams!.messages as Array<Record<string, unknown>>;
|
|
1394
|
+
const assistantTurn = sent.find((m) => m.role === "assistant");
|
|
1395
|
+
expect(assistantTurn).toEqual({
|
|
1396
|
+
role: "assistant",
|
|
1397
|
+
content: "prior answer",
|
|
1398
|
+
reasoning_content: "prior reasoning",
|
|
1399
|
+
});
|
|
1400
|
+
});
|
|
1367
1401
|
});
|
|
1368
1402
|
|
|
1369
1403
|
// ---------------------------------------------------------------------------
|
|
@@ -2,6 +2,8 @@ import { describe, expect, test } from "bun:test";
|
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
computeNextRunAt,
|
|
5
|
+
describeRRuleExpression,
|
|
6
|
+
isSingleFireRRule,
|
|
5
7
|
isValidScheduleExpression,
|
|
6
8
|
} from "../schedule/recurrence-engine.js";
|
|
7
9
|
|
|
@@ -117,3 +119,46 @@ describe("recurrence engine — rrule", () => {
|
|
|
117
119
|
expect(next).toBe(jan2);
|
|
118
120
|
});
|
|
119
121
|
});
|
|
122
|
+
|
|
123
|
+
describe("recurrence engine — rrule display helpers", () => {
|
|
124
|
+
const SINGLE_FIRE = "DTSTART:20990612T080000\nRRULE:FREQ=DAILY;COUNT=1";
|
|
125
|
+
const WEEKLY = "DTSTART:20990612T080000\nRRULE:FREQ=WEEKLY;BYDAY=MO,WE";
|
|
126
|
+
|
|
127
|
+
test("isSingleFireRRule detects COUNT=1 rules", () => {
|
|
128
|
+
expect(isSingleFireRRule(SINGLE_FIRE)).toBe(true);
|
|
129
|
+
expect(isSingleFireRRule(WEEKLY)).toBe(false);
|
|
130
|
+
expect(
|
|
131
|
+
isSingleFireRRule("DTSTART:20990612T080000\nRRULE:FREQ=DAILY;COUNT=2"),
|
|
132
|
+
).toBe(false);
|
|
133
|
+
expect(isSingleFireRRule("not an rrule")).toBe(false);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test("isSingleFireRRule is false for set constructs", () => {
|
|
137
|
+
expect(
|
|
138
|
+
isSingleFireRRule(
|
|
139
|
+
"DTSTART:20990612T080000\nRRULE:FREQ=DAILY;COUNT=1\nRDATE:20990613T080000",
|
|
140
|
+
),
|
|
141
|
+
).toBe(false);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("describeRRuleExpression humanizes rules", () => {
|
|
145
|
+
expect(describeRRuleExpression(SINGLE_FIRE)).toBe("One-time");
|
|
146
|
+
expect(describeRRuleExpression(WEEKLY)).toBe(
|
|
147
|
+
"Every week on Monday, Wednesday",
|
|
148
|
+
);
|
|
149
|
+
expect(
|
|
150
|
+
describeRRuleExpression(
|
|
151
|
+
"DTSTART:20990612T080000\nRRULE:FREQ=DAILY;COUNT=3",
|
|
152
|
+
),
|
|
153
|
+
).toBe("Every day for 3 times");
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test("describeRRuleExpression falls back instead of leaking raw text", () => {
|
|
157
|
+
expect(
|
|
158
|
+
describeRRuleExpression(
|
|
159
|
+
"DTSTART:20990612T080000\nRRULE:FREQ=DAILY\nEXDATE:20990613T080000",
|
|
160
|
+
),
|
|
161
|
+
).toBe("Custom recurrence");
|
|
162
|
+
expect(describeRRuleExpression("garbage")).toBe("Custom recurrence");
|
|
163
|
+
});
|
|
164
|
+
});
|
|
@@ -492,6 +492,40 @@ describe("GET /schedules — default defer exclusion", () => {
|
|
|
492
492
|
});
|
|
493
493
|
});
|
|
494
494
|
|
|
495
|
+
test("humanizes rrule cadences and flags single-fire rrules as one-shot", () => {
|
|
496
|
+
createSchedule({
|
|
497
|
+
name: "RRule single fire",
|
|
498
|
+
cronExpression: "DTSTART:20990612T080000\nRRULE:FREQ=DAILY;COUNT=1",
|
|
499
|
+
syntax: "rrule",
|
|
500
|
+
message: "brush teeth",
|
|
501
|
+
});
|
|
502
|
+
createSchedule({
|
|
503
|
+
name: "RRule weekly",
|
|
504
|
+
cronExpression: "DTSTART:20990612T080000\nRRULE:FREQ=WEEKLY;BYDAY=MO,WE",
|
|
505
|
+
syntax: "rrule",
|
|
506
|
+
message: "standup",
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
const route = findRoute("schedules", "GET");
|
|
510
|
+
const result = route.handler({}) as {
|
|
511
|
+
schedules: Array<{
|
|
512
|
+
name: string;
|
|
513
|
+
cadenceDescription: string;
|
|
514
|
+
isOneShot: boolean;
|
|
515
|
+
}>;
|
|
516
|
+
};
|
|
517
|
+
const byName = new Map(result.schedules.map((s) => [s.name, s]));
|
|
518
|
+
|
|
519
|
+
expect(byName.get("RRule single fire")).toMatchObject({
|
|
520
|
+
cadenceDescription: "One-time",
|
|
521
|
+
isOneShot: true,
|
|
522
|
+
});
|
|
523
|
+
expect(byName.get("RRule weekly")).toMatchObject({
|
|
524
|
+
cadenceDescription: "Every week on Monday, Wednesday",
|
|
525
|
+
isOneShot: false,
|
|
526
|
+
});
|
|
527
|
+
});
|
|
528
|
+
|
|
495
529
|
test("mutation responses also exclude deferred wakes", () => {
|
|
496
530
|
createSchedule({
|
|
497
531
|
name: "Agent schedule",
|
|
@@ -247,13 +247,18 @@ describe("session-manager", () => {
|
|
|
247
247
|
await startSession(session.id);
|
|
248
248
|
expect(getActiveSession(CONV_ID)).toBeDefined();
|
|
249
249
|
|
|
250
|
-
// Wait for the idle timeout to fire
|
|
251
|
-
|
|
250
|
+
// Wait for the idle timeout to fire and the async stop to finish.
|
|
251
|
+
// The session passes through "stopping" while the server closes, so
|
|
252
|
+
// poll for the terminal state rather than sleeping a fixed interval.
|
|
253
|
+
const deadline = Date.now() + 5000;
|
|
254
|
+
const findSession = () =>
|
|
255
|
+
getSessionsForConversation(CONV_ID).find((x) => x.id === session.id);
|
|
256
|
+
while (findSession()?.status !== "stopped" && Date.now() < deadline) {
|
|
257
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
258
|
+
}
|
|
252
259
|
|
|
253
260
|
expect(getActiveSession(CONV_ID)).toBeUndefined();
|
|
254
|
-
|
|
255
|
-
const s = all.find((x) => x.id === session.id);
|
|
256
|
-
expect(s?.status).toBe("stopped");
|
|
261
|
+
expect(findSession()?.status).toBe("stopped");
|
|
257
262
|
});
|
|
258
263
|
});
|
|
259
264
|
|
|
@@ -347,6 +347,55 @@ describe("createSkillTool — required/type/enum validation", () => {
|
|
|
347
347
|
expect(result.content).toContain('mode must be one of "a", "b"');
|
|
348
348
|
});
|
|
349
349
|
|
|
350
|
+
test("coerces string booleans before validation and passes the coerced value to the executor", async () => {
|
|
351
|
+
const hash = computeSkillVersionHash(tempDir);
|
|
352
|
+
const tool = createSkillTool(
|
|
353
|
+
makeEntry({
|
|
354
|
+
executor: "echo.ts",
|
|
355
|
+
input_schema: {
|
|
356
|
+
type: "object",
|
|
357
|
+
properties: {
|
|
358
|
+
auto_open: { type: "boolean" },
|
|
359
|
+
name: { type: "string" },
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
}),
|
|
363
|
+
tempDir,
|
|
364
|
+
hash,
|
|
365
|
+
);
|
|
366
|
+
|
|
367
|
+
const result = await tool.execute(
|
|
368
|
+
{ auto_open: "false", name: "x" },
|
|
369
|
+
makeContext(),
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
expect(result.isError).toBe(false);
|
|
373
|
+
const parsed = JSON.parse(result.content);
|
|
374
|
+
expect(parsed.input).toEqual({ auto_open: false, name: "x" });
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
test("rejects non-coercible boolean strings with a self-correcting message", async () => {
|
|
378
|
+
const hash = computeSkillVersionHash(tempDir);
|
|
379
|
+
const tool = createSkillTool(
|
|
380
|
+
makeEntry({
|
|
381
|
+
executor: "echo.ts",
|
|
382
|
+
input_schema: {
|
|
383
|
+
type: "object",
|
|
384
|
+
properties: { auto_open: { type: "boolean" } },
|
|
385
|
+
},
|
|
386
|
+
}),
|
|
387
|
+
tempDir,
|
|
388
|
+
hash,
|
|
389
|
+
);
|
|
390
|
+
|
|
391
|
+
const result = await tool.execute({ auto_open: "yes" }, makeContext());
|
|
392
|
+
|
|
393
|
+
expect(result.isError).toBe(true);
|
|
394
|
+
expect(result.content).toContain(
|
|
395
|
+
"auto_open must be a boolean — pass true or false as a JSON boolean, not a string",
|
|
396
|
+
);
|
|
397
|
+
});
|
|
398
|
+
|
|
350
399
|
test("passes valid input through to the executor unchanged", async () => {
|
|
351
400
|
const hash = computeSkillVersionHash(tempDir);
|
|
352
401
|
const tool = createSkillTool(
|
|
@@ -7,7 +7,13 @@ import {
|
|
|
7
7
|
} from "../subagent/index.js";
|
|
8
8
|
|
|
9
9
|
/** All roles defined in the SubagentRole union. */
|
|
10
|
-
const ALL_ROLES: SubagentRole[] = [
|
|
10
|
+
const ALL_ROLES: SubagentRole[] = [
|
|
11
|
+
"general",
|
|
12
|
+
"researcher",
|
|
13
|
+
"coder",
|
|
14
|
+
"planner",
|
|
15
|
+
"investigator",
|
|
16
|
+
];
|
|
11
17
|
|
|
12
18
|
describe("SUBAGENT_ROLE_REGISTRY", () => {
|
|
13
19
|
test("covers all values in the SubagentRole union", () => {
|
|
@@ -64,6 +70,23 @@ describe("SUBAGENT_ROLE_REGISTRY", () => {
|
|
|
64
70
|
expect(SUBAGENT_ROLE_REGISTRY.planner.allowedTools!).toContain("recall");
|
|
65
71
|
});
|
|
66
72
|
|
|
73
|
+
test('investigator includes "bash" for grep/find-based code and log investigation', () => {
|
|
74
|
+
expect(SUBAGENT_ROLE_REGISTRY.investigator.allowedTools!).toContain("bash");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("investigator excludes file write tools (read-only investigation)", () => {
|
|
78
|
+
const tools = SUBAGENT_ROLE_REGISTRY.investigator.allowedTools!;
|
|
79
|
+
expect(tools).not.toContain("file_write");
|
|
80
|
+
expect(tools).not.toContain("file_edit");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("investigator preamble defines the root-cause report contract", () => {
|
|
84
|
+
const preamble = SUBAGENT_ROLE_REGISTRY.investigator.systemPromptPreamble;
|
|
85
|
+
expect(preamble).toContain("Root cause");
|
|
86
|
+
expect(preamble).toContain("Evidence");
|
|
87
|
+
expect(preamble).toContain("notify_parent");
|
|
88
|
+
});
|
|
89
|
+
|
|
67
90
|
test("no role references the old memory_recall tool name", () => {
|
|
68
91
|
for (const [_role, config] of Object.entries(SUBAGENT_ROLE_REGISTRY)) {
|
|
69
92
|
if (config.allowedTools !== undefined) {
|
|
@@ -110,6 +110,8 @@ mock.module("../credential-execution/managed-catalog.js", () => ({
|
|
|
110
110
|
// Import after mock
|
|
111
111
|
const { buildSystemPrompt, ensurePromptFiles, stripCommentLines } =
|
|
112
112
|
await import("../prompts/system-prompt.js");
|
|
113
|
+
const { SYSTEM_PROMPT_CACHE_BOUNDARY } =
|
|
114
|
+
await import("../prompts/cache-boundary.js");
|
|
113
115
|
|
|
114
116
|
describe("buildSystemPrompt", () => {
|
|
115
117
|
beforeEach(() => {
|
|
@@ -128,7 +130,6 @@ describe("buildSystemPrompt", () => {
|
|
|
128
130
|
"SOUL.md",
|
|
129
131
|
"USER.md",
|
|
130
132
|
"BOOTSTRAP.md",
|
|
131
|
-
"UPDATES.md",
|
|
132
133
|
"VOICE.md",
|
|
133
134
|
"skills",
|
|
134
135
|
"users",
|
|
@@ -603,16 +604,6 @@ describe("buildSystemPrompt", () => {
|
|
|
603
604
|
});
|
|
604
605
|
});
|
|
605
606
|
|
|
606
|
-
test("never includes UPDATES.md content in system prompt", () => {
|
|
607
|
-
const updatesBody = "# v1.2\n\nNew feature added. UNIQUE_UPDATES_MARKER.";
|
|
608
|
-
writeFileSync(join(TEST_DIR, "UPDATES.md"), updatesBody);
|
|
609
|
-
const result = buildSystemPrompt();
|
|
610
|
-
expect(result).not.toContain("## Recent Updates");
|
|
611
|
-
expect(result).not.toContain(updatesBody);
|
|
612
|
-
expect(result).not.toContain("UNIQUE_UPDATES_MARKER");
|
|
613
|
-
expect(result).not.toContain("Update Handling");
|
|
614
|
-
});
|
|
615
|
-
|
|
616
607
|
test("strips comment lines starting with _ from prompt files", () => {
|
|
617
608
|
writeFileSync(
|
|
618
609
|
join(TEST_DIR, "IDENTITY.md"),
|
|
@@ -879,6 +870,113 @@ describe("buildSystemPrompt", () => {
|
|
|
879
870
|
expect(orgIdx).toBeGreaterThan(parallelIdx);
|
|
880
871
|
});
|
|
881
872
|
|
|
873
|
+
describe("cache breakpoints", () => {
|
|
874
|
+
test("default breakpoint splits the prompt after 11-channel-persona", () => {
|
|
875
|
+
/**
|
|
876
|
+
* Tests that the bundled `cacheBreakpoint` on 11-channel-persona
|
|
877
|
+
* places the boundary marker between the stable sections and the
|
|
878
|
+
* volatile 12-voice section.
|
|
879
|
+
*/
|
|
880
|
+
// GIVEN a workspace where a volatile section (12-voice) renders
|
|
881
|
+
writeFileSync(join(TEST_DIR, "VOICE.md"), "- Prefers lowercase.");
|
|
882
|
+
|
|
883
|
+
// WHEN the system prompt is built
|
|
884
|
+
const result = buildSystemPrompt();
|
|
885
|
+
|
|
886
|
+
// THEN the boundary marker sits before the voice content and
|
|
887
|
+
// after the stable instruction sections
|
|
888
|
+
const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
|
|
889
|
+
expect(boundaryIdx).toBeGreaterThan(-1);
|
|
890
|
+
expect(result.indexOf("## Communication")).toBeLessThan(boundaryIdx);
|
|
891
|
+
expect(result.indexOf("# Voice Profile")).toBeGreaterThan(boundaryIdx);
|
|
892
|
+
});
|
|
893
|
+
|
|
894
|
+
test("breakpoint splits even when the declaring section gates off", () => {
|
|
895
|
+
/**
|
|
896
|
+
* Tests that the split is resolved against the section ordering,
|
|
897
|
+
* not the rendered output — an absent channel persona must not
|
|
898
|
+
* merge the blocks around it.
|
|
899
|
+
*/
|
|
900
|
+
// GIVEN no channel persona file exists but a volatile section renders
|
|
901
|
+
writeFileSync(join(TEST_DIR, "VOICE.md"), "- Prefers lowercase.");
|
|
902
|
+
|
|
903
|
+
// WHEN the system prompt is built
|
|
904
|
+
const result = buildSystemPrompt();
|
|
905
|
+
|
|
906
|
+
// THEN the boundary marker is still present
|
|
907
|
+
expect(result).toContain(SYSTEM_PROMPT_CACHE_BOUNDARY);
|
|
908
|
+
});
|
|
909
|
+
|
|
910
|
+
test("no boundary marker when nothing renders after the breakpoint", () => {
|
|
911
|
+
/**
|
|
912
|
+
* Tests that empty trailing blocks are dropped so the marker
|
|
913
|
+
* never dangles at the end of the prompt.
|
|
914
|
+
*/
|
|
915
|
+
// GIVEN a fresh workspace with no volatile sections (no VOICE.md,
|
|
916
|
+
// no BOOTSTRAP.md, no connections)
|
|
917
|
+
// WHEN the system prompt is built
|
|
918
|
+
const result = buildSystemPrompt();
|
|
919
|
+
|
|
920
|
+
// THEN no boundary marker appears
|
|
921
|
+
expect(result).not.toContain(SYSTEM_PROMPT_CACHE_BOUNDARY);
|
|
922
|
+
});
|
|
923
|
+
|
|
924
|
+
test("workspace override of 11-channel-persona without cache_breakpoint clears the default", () => {
|
|
925
|
+
/**
|
|
926
|
+
* Tests that a workspace override takes full control of the
|
|
927
|
+
* section, including its breakpoint declaration.
|
|
928
|
+
*/
|
|
929
|
+
// GIVEN a workspace override of 11-channel-persona with no
|
|
930
|
+
// cache_breakpoint frontmatter, and a volatile section that renders
|
|
931
|
+
mkdirSync(SYSTEM_PROMPTS_DIR, { recursive: true });
|
|
932
|
+
writeFileSync(
|
|
933
|
+
join(SYSTEM_PROMPTS_DIR, "11-channel-persona.md"),
|
|
934
|
+
"Override channel persona.\n",
|
|
935
|
+
);
|
|
936
|
+
writeFileSync(join(TEST_DIR, "VOICE.md"), "- Prefers lowercase.");
|
|
937
|
+
|
|
938
|
+
// WHEN the system prompt is built
|
|
939
|
+
const result = buildSystemPrompt();
|
|
940
|
+
|
|
941
|
+
// THEN the override body renders but no boundary marker appears
|
|
942
|
+
expect(result).toContain("Override channel persona.");
|
|
943
|
+
expect(result).not.toContain(SYSTEM_PROMPT_CACHE_BOUNDARY);
|
|
944
|
+
});
|
|
945
|
+
|
|
946
|
+
test("frontmatter cache_breakpoint on a workspace section places the boundary there", () => {
|
|
947
|
+
/**
|
|
948
|
+
* Tests that users can declare the breakpoint position via
|
|
949
|
+
* `cache_breakpoint: true` frontmatter, and that only the first
|
|
950
|
+
* declaration (in id-sort order) is honored.
|
|
951
|
+
*/
|
|
952
|
+
// GIVEN a custom workspace section declaring a cache breakpoint
|
|
953
|
+
// ahead of the bundled default on 11-channel-persona
|
|
954
|
+
mkdirSync(SYSTEM_PROMPTS_DIR, { recursive: true });
|
|
955
|
+
writeFileSync(
|
|
956
|
+
join(SYSTEM_PROMPTS_DIR, "00-aaa-custom.md"),
|
|
957
|
+
"---\ncache_breakpoint: true\n---\nCustom head section.\n",
|
|
958
|
+
);
|
|
959
|
+
writeFileSync(join(TEST_DIR, "VOICE.md"), "- Prefers lowercase.");
|
|
960
|
+
|
|
961
|
+
// WHEN the system prompt is built
|
|
962
|
+
const result = buildSystemPrompt();
|
|
963
|
+
|
|
964
|
+
// THEN exactly one boundary appears, right after the custom section
|
|
965
|
+
const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
|
|
966
|
+
expect(boundaryIdx).toBeGreaterThan(-1);
|
|
967
|
+
expect(result.indexOf("Custom head section.")).toBeLessThan(
|
|
968
|
+
boundaryIdx,
|
|
969
|
+
);
|
|
970
|
+
expect(result.indexOf("## Communication")).toBeGreaterThan(boundaryIdx);
|
|
971
|
+
|
|
972
|
+
// AND the bundled default declaration on 11-channel-persona is
|
|
973
|
+
// ignored — no second marker
|
|
974
|
+
expect(
|
|
975
|
+
result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY, boundaryIdx + 1),
|
|
976
|
+
).toBe(-1);
|
|
977
|
+
});
|
|
978
|
+
});
|
|
979
|
+
|
|
882
980
|
describe("containerized section (slot 02)", () => {
|
|
883
981
|
const CONTAINERIZED_FILE = join(
|
|
884
982
|
SYSTEM_PROMPTS_DIR,
|