@vellumai/assistant 0.7.3 → 0.8.0
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 +29 -28
- package/Dockerfile +1 -0
- package/__tests__/permissions/gateway-threshold-reader.test.ts +236 -9
- package/bun.lock +3 -0
- package/knip.json +1 -0
- package/node_modules/@vellumai/ipc-server-utils/bun.lock +24 -0
- package/node_modules/@vellumai/ipc-server-utils/package.json +18 -0
- package/node_modules/@vellumai/ipc-server-utils/src/index.ts +6 -0
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +430 -0
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +221 -0
- package/node_modules/@vellumai/ipc-server-utils/tsconfig.json +20 -0
- package/openapi.yaml +22 -4
- package/package.json +3 -1
- package/src/__tests__/annotate-risk-options.test.ts +291 -0
- package/src/__tests__/approval-cascade.test.ts +8 -16
- package/src/__tests__/approval-routes-http.test.ts +6 -0
- package/src/__tests__/auto-analysis-end-to-end.test.ts +12 -25
- package/src/__tests__/call-constants.test.ts +10 -1
- package/src/__tests__/call-controller.test.ts +127 -0
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +58 -28
- package/src/__tests__/config-loader-platform-defaults.test.ts +284 -1
- package/src/__tests__/context-search-memory-source.test.ts +3 -26
- package/src/__tests__/context-search-pkb-source.test.ts +12 -6
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -6
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop.test.ts +3 -3
- package/src/__tests__/conversation-confirmation-signals.test.ts +5 -13
- package/src/__tests__/conversation-init.benchmark.test.ts +1 -1
- package/src/__tests__/conversation-process-callsite.test.ts +1 -6
- package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -6
- package/src/__tests__/conversation-runtime-assembly.test.ts +15 -6
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -6
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +170 -9
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +73 -1
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +59 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +1 -7
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -7
- package/src/__tests__/filing-service.test.ts +2 -19
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +10 -26
- package/src/__tests__/injector-chain.test.ts +24 -16
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +10 -7
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +154 -67
- package/src/__tests__/notification-decision-fallback.test.ts +91 -0
- package/src/__tests__/notification-decision-strategy.test.ts +22 -0
- package/src/__tests__/oauth-cli.test.ts +121 -0
- package/src/__tests__/relay-server.test.ts +46 -2
- package/src/__tests__/secret-prompt-log-hygiene.test.ts +7 -5
- package/src/__tests__/secret-prompter-channel-fallback.test.ts +7 -5
- package/src/__tests__/secret-response-routing.test.ts +7 -5
- package/src/__tests__/server-history-render.test.ts +82 -0
- package/src/__tests__/skill-include-graph.test.ts +31 -0
- package/src/__tests__/skill-load-tool.test.ts +44 -16
- package/src/__tests__/skills.test.ts +39 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -42
- package/src/__tests__/tool-executor.test.ts +155 -0
- package/src/__tests__/voice-session-bridge.test.ts +3 -0
- package/src/__tests__/workspace-migration-069-seed-onboarding-threads.test.ts +120 -0
- package/src/__tests__/workspace-migration-071-remove-safe-storage-release-note.test.ts +206 -0
- package/src/__tests__/workspace-migration-safe-storage-limits-release.test.ts +15 -27
- package/src/agent/loop.ts +11 -0
- package/src/approvals/guardian-decision-primitive.ts +0 -13
- package/src/approvals/guardian-request-resolvers.ts +4 -32
- package/src/calls/call-constants.ts +5 -8
- package/src/calls/call-controller.ts +130 -67
- package/src/calls/relay-server.ts +7 -1
- package/src/calls/voice-session-bridge.ts +1 -1
- package/src/cli/commands/memory-v2.ts +7 -7
- package/src/cli/commands/oauth/__tests__/connect.test.ts +0 -254
- package/src/cli/commands/oauth/connect.ts +10 -52
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -3
- package/src/config/feature-flag-registry.json +1 -17
- package/src/config/loader.ts +72 -19
- package/src/config/schemas/memory-v2.ts +1 -1
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +32 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +32 -0
- package/src/daemon/conversation-agent-loop.ts +13 -10
- package/src/daemon/conversation-lifecycle.ts +22 -8
- package/src/daemon/conversation-surfaces.ts +16 -14
- package/src/daemon/conversation-tool-setup.ts +9 -5
- package/src/daemon/conversation.ts +1 -1
- package/src/daemon/handlers/shared.ts +26 -0
- package/src/daemon/host-bash-proxy.ts +1 -1
- package/src/daemon/host-browser-proxy.ts +1 -1
- package/src/daemon/host-cu-proxy.ts +1 -1
- package/src/daemon/host-file-proxy.ts +1 -1
- package/src/daemon/host-transfer-proxy.ts +2 -2
- package/src/daemon/lifecycle.ts +88 -73
- package/src/daemon/memory-v2-startup.ts +55 -14
- package/src/daemon/message-types/messages.ts +19 -1
- package/src/documents/document-store.ts +35 -1
- package/src/filing/filing-service.ts +2 -3
- package/src/heartbeat/heartbeat-service.ts +1 -1
- package/src/ipc/assistant-server.ts +93 -36
- package/src/ipc/skill-server.ts +99 -42
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +10 -57
- package/src/memory/context-search/sources/memory-v2.ts +1 -17
- package/src/memory/context-search/sources/memory.ts +2 -2
- package/src/memory/context-search/sources/pkb.ts +2 -3
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +104 -61
- package/src/memory/graph/__tests__/handle-remember-v2.test.ts +11 -26
- package/src/memory/graph/conversation-graph-memory.ts +32 -9
- package/src/memory/graph/graph-search.test.ts +6 -5
- package/src/memory/graph/graph-search.ts +3 -4
- package/src/memory/graph/retriever.test.ts +12 -7
- package/src/memory/graph/retriever.ts +4 -5
- package/src/memory/graph/tool-handlers.ts +3 -4
- package/src/memory/graph/tools.ts +4 -4
- package/src/memory/indexer.ts +1 -2
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +116 -0
- package/src/memory/jobs/embed-concept-page.ts +223 -87
- package/src/memory/jobs-worker.ts +8 -4
- package/src/memory/pkb/pkb-search.test.ts +6 -5
- package/src/memory/pkb/pkb-search.ts +4 -5
- package/src/memory/qdrant-client.ts +3 -0
- package/src/memory/search/semantic.ts +4 -5
- package/src/memory/v2/__tests__/activation.test.ts +35 -5
- package/src/memory/v2/__tests__/consolidation-job.test.ts +21 -32
- package/src/memory/v2/__tests__/injection.test.ts +140 -23
- package/src/memory/v2/__tests__/qdrant.test.ts +310 -9
- package/src/memory/v2/__tests__/sim.test.ts +118 -7
- package/src/memory/v2/__tests__/static-context.test.ts +1 -13
- package/src/memory/v2/__tests__/sweep-job.test.ts +19 -33
- package/src/memory/v2/consolidation-job.ts +7 -8
- package/src/memory/v2/injection.ts +32 -12
- package/src/memory/v2/page-store.ts +39 -0
- package/src/memory/v2/prompts/consolidation.ts +5 -0
- package/src/memory/v2/qdrant.ts +209 -48
- package/src/memory/v2/sim.ts +67 -26
- package/src/memory/v2/static-context.ts +4 -8
- package/src/memory/v2/sweep-job.ts +5 -6
- package/src/memory/v2/types.ts +7 -0
- package/src/notifications/copy-composer.ts +46 -12
- package/src/notifications/decision-engine.ts +46 -0
- package/src/permissions/gateway-threshold-reader.ts +116 -8
- package/src/permissions/prompter.ts +86 -96
- package/src/permissions/secret-prompter.ts +31 -31
- package/src/plugins/defaults/injectors.ts +1 -2
- package/src/proactive-artifact/job.test.ts +51 -4
- package/src/proactive-artifact/job.ts +16 -2
- package/src/proactive-artifact/message-copy.ts +18 -1
- package/src/prompts/templates/SOUL.md +13 -28
- package/src/runtime/auth/route-policy.ts +1 -0
- package/src/runtime/channel-approvals.ts +3 -2
- package/src/runtime/guardian-reply-router.ts +0 -10
- package/src/runtime/pending-interactions.ts +19 -15
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +147 -0
- package/src/runtime/routes/approval-routes.ts +7 -3
- package/src/runtime/routes/consolidation-routes.ts +8 -9
- package/src/runtime/routes/conversation-query-routes.ts +44 -1
- package/src/runtime/routes/debug-bash-routes.ts +2 -0
- package/src/runtime/routes/filing-routes.ts +2 -3
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +0 -3
- package/src/runtime/routes/memory-item-routes.test.ts +3 -9
- package/src/runtime/routes/memory-item-routes.ts +5 -6
- package/src/runtime/routes/memory-v2-routes.ts +103 -17
- package/src/skills/include-graph.ts +35 -13
- package/src/tools/document/document-tool.ts +20 -0
- package/src/tools/executor.ts +18 -2
- package/src/tools/memory/register.test.ts +7 -5
- package/src/tools/permission-checker.ts +15 -0
- package/src/tools/skills/load.ts +24 -20
- package/src/tools/tool-name-aliases.ts +19 -0
- package/src/tools/types.ts +19 -1
- package/src/workspace/migrations/067-release-notes-safe-storage-limits.ts +4 -62
- package/src/workspace/migrations/069-seed-onboarding-threads.ts +28 -0
- package/src/workspace/migrations/070-memory-v2-summary-schema-rebuild.ts +31 -0
- package/src/workspace/migrations/071-remove-safe-storage-release-note.ts +111 -0
- package/src/workspace/migrations/registry.ts +6 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tests for `assistant/src/memory/v2/sweep-job.ts`.
|
|
3
3
|
*
|
|
4
|
-
* Coverage matrix
|
|
5
|
-
* -
|
|
6
|
-
* -
|
|
7
|
-
* -
|
|
4
|
+
* Coverage matrix:
|
|
5
|
+
* - v2 disabled in config → no provider/DB calls, returns 0 (early bail).
|
|
6
|
+
* - sweep_enabled off → no provider call, returns 0.
|
|
7
|
+
* - v2 on + no recent messages → no provider call, returns 0.
|
|
8
|
+
* - v2 on + recent messages → provider invoked with rendered prompt;
|
|
8
9
|
* each entry is appended to `memory/buffer.md` AND today's archive.
|
|
9
10
|
* - Tool-call response shape mismatch → returns 0 without writes.
|
|
10
11
|
* - Empty entries are skipped (the model can't pad the buffer).
|
|
@@ -24,7 +25,6 @@ import { tmpdir } from "node:os";
|
|
|
24
25
|
import { join } from "node:path";
|
|
25
26
|
import {
|
|
26
27
|
afterAll,
|
|
27
|
-
afterEach,
|
|
28
28
|
beforeAll,
|
|
29
29
|
beforeEach,
|
|
30
30
|
describe,
|
|
@@ -85,23 +85,20 @@ afterAll(() => {
|
|
|
85
85
|
const { resetDb, getDb } = await import("../../db-connection.js");
|
|
86
86
|
const { initializeDb } = await import("../../db-init.js");
|
|
87
87
|
const { messages, conversations } = await import("../../schema.js");
|
|
88
|
-
const { _setOverridesForTesting } =
|
|
89
|
-
await import("../../../config/assistant-feature-flags.js");
|
|
90
88
|
const { memoryV2SweepJob } = await import("../sweep-job.js");
|
|
91
89
|
|
|
92
|
-
//
|
|
93
|
-
//
|
|
94
|
-
// the handler a minimal stand-in instead of materializing the full default
|
|
90
|
+
// The handler reads `config.memory.v2.enabled` and `sweep_enabled`, so we
|
|
91
|
+
// hand it a minimal stand-in instead of materializing the full default
|
|
95
92
|
// config — which would otherwise pull in heavy schemas this test doesn't
|
|
96
|
-
// exercise.
|
|
97
|
-
// `flag on` cases need the field set; the `flag off` case bails before
|
|
98
|
-
// the check and uses the bare empty stand-in.
|
|
93
|
+
// exercise.
|
|
99
94
|
const CONFIG = {
|
|
100
|
-
memory: { v2: { sweep_enabled: true } },
|
|
95
|
+
memory: { v2: { enabled: true, sweep_enabled: true } },
|
|
96
|
+
} as Parameters<typeof memoryV2SweepJob>[1];
|
|
97
|
+
const CONFIG_V2_OFF = {
|
|
98
|
+
memory: { v2: { enabled: false, sweep_enabled: true } },
|
|
101
99
|
} as Parameters<typeof memoryV2SweepJob>[1];
|
|
102
|
-
const CONFIG_FLAG_OFF = {} as Parameters<typeof memoryV2SweepJob>[1];
|
|
103
100
|
const CONFIG_SWEEP_OFF = {
|
|
104
|
-
memory: { v2: { sweep_enabled: false } },
|
|
101
|
+
memory: { v2: { enabled: true, sweep_enabled: false } },
|
|
105
102
|
} as Parameters<typeof memoryV2SweepJob>[1];
|
|
106
103
|
|
|
107
104
|
function makeJob(): Parameters<typeof memoryV2SweepJob>[0] {
|
|
@@ -205,18 +202,13 @@ beforeEach(() => {
|
|
|
205
202
|
providerStub = null;
|
|
206
203
|
});
|
|
207
204
|
|
|
208
|
-
afterEach(() => {
|
|
209
|
-
_setOverridesForTesting({});
|
|
210
|
-
});
|
|
211
|
-
|
|
212
205
|
// ---------------------------------------------------------------------------
|
|
213
206
|
|
|
214
|
-
describe("memoryV2SweepJob —
|
|
215
|
-
test("returns 0 without invoking the provider when
|
|
216
|
-
_setOverridesForTesting({ "memory-v2-enabled": false });
|
|
207
|
+
describe("memoryV2SweepJob — v2 disabled", () => {
|
|
208
|
+
test("returns 0 without invoking the provider when memory.v2.enabled is false", async () => {
|
|
217
209
|
providerStub = makeEntriesProvider(["should-not-be-written"]);
|
|
218
210
|
|
|
219
|
-
const written = await memoryV2SweepJob(makeJob(),
|
|
211
|
+
const written = await memoryV2SweepJob(makeJob(), CONFIG_V2_OFF);
|
|
220
212
|
|
|
221
213
|
expect(written).toBe(0);
|
|
222
214
|
expect(providerCalls).toHaveLength(0);
|
|
@@ -224,9 +216,8 @@ describe("memoryV2SweepJob — flag off", () => {
|
|
|
224
216
|
});
|
|
225
217
|
});
|
|
226
218
|
|
|
227
|
-
describe("memoryV2SweepJob —
|
|
219
|
+
describe("memoryV2SweepJob — sweep_enabled off", () => {
|
|
228
220
|
test("returns 0 without invoking the provider when sweep_enabled is false", async () => {
|
|
229
|
-
_setOverridesForTesting({ "memory-v2-enabled": true });
|
|
230
221
|
// No message seeding required — the sweep_enabled bail short-circuits
|
|
231
222
|
// before any DB or workspace reads.
|
|
232
223
|
providerStub = makeEntriesProvider(["should-not-be-written"]);
|
|
@@ -239,11 +230,7 @@ describe("memoryV2SweepJob — flag on, sweep_enabled off", () => {
|
|
|
239
230
|
});
|
|
240
231
|
});
|
|
241
232
|
|
|
242
|
-
describe("memoryV2SweepJob —
|
|
243
|
-
beforeEach(() => {
|
|
244
|
-
_setOverridesForTesting({ "memory-v2-enabled": true });
|
|
245
|
-
});
|
|
246
|
-
|
|
233
|
+
describe("memoryV2SweepJob — no recent messages", () => {
|
|
247
234
|
test("returns 0 when no messages exist in the recent window", async () => {
|
|
248
235
|
providerStub = makeEntriesProvider(["should-not-be-written"]);
|
|
249
236
|
|
|
@@ -273,9 +260,8 @@ describe("memoryV2SweepJob — flag on, no recent messages", () => {
|
|
|
273
260
|
// DB intact long enough for the SQL inserts here to clash.
|
|
274
261
|
let convCounter = 0;
|
|
275
262
|
|
|
276
|
-
describe("memoryV2SweepJob —
|
|
263
|
+
describe("memoryV2SweepJob — recent messages", () => {
|
|
277
264
|
beforeEach(() => {
|
|
278
|
-
_setOverridesForTesting({ "memory-v2-enabled": true });
|
|
279
265
|
seedMessages(`conv-${++convCounter}`, [
|
|
280
266
|
{
|
|
281
267
|
role: "user",
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
* substitute in.
|
|
16
16
|
*
|
|
17
17
|
* Lifecycle:
|
|
18
|
-
* 1. Bail if
|
|
19
|
-
*
|
|
18
|
+
* 1. Bail if `config.memory.v2.enabled` is false (the worker may have
|
|
19
|
+
* claimed a stale row from before v2 was disabled).
|
|
20
20
|
* 2. Acquire a single-process lock at `memory/.v2-state/consolidation.lock`
|
|
21
21
|
* so two overlapping schedule windows can't fight over the same files.
|
|
22
22
|
* The lock contains the holder's PID + timestamp so a crashed run leaves
|
|
@@ -53,7 +53,6 @@ import {
|
|
|
53
53
|
} from "node:fs";
|
|
54
54
|
import { dirname, join } from "node:path";
|
|
55
55
|
|
|
56
|
-
import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
|
|
57
56
|
import type { AssistantConfig } from "../../config/types.js";
|
|
58
57
|
import { INTERNAL_GUARDIAN_TRUST_CONTEXT } from "../../daemon/trust-context.js";
|
|
59
58
|
import { wakeAgentForOpportunity } from "../../runtime/agent-wake.js";
|
|
@@ -85,11 +84,11 @@ const FOLLOW_UP_JOB_TYPES: readonly MemoryJobType[] = [
|
|
|
85
84
|
|
|
86
85
|
/**
|
|
87
86
|
* Job handler. See file header for the full lifecycle. Returns a discriminated
|
|
88
|
-
* union so tests can assert on the path taken (
|
|
87
|
+
* union so tests can assert on the path taken (disabled / locked / empty /
|
|
89
88
|
* invoked) without having to spy on the filesystem.
|
|
90
89
|
*/
|
|
91
90
|
export type ConsolidationOutcome =
|
|
92
|
-
| { kind: "
|
|
91
|
+
| { kind: "disabled" }
|
|
93
92
|
| { kind: "locked"; holder: string }
|
|
94
93
|
| { kind: "empty_buffer" }
|
|
95
94
|
| { kind: "wake_failed"; reason?: string }
|
|
@@ -104,9 +103,9 @@ export async function memoryV2ConsolidateJob(
|
|
|
104
103
|
_job: MemoryJob,
|
|
105
104
|
config: AssistantConfig,
|
|
106
105
|
): Promise<ConsolidationOutcome> {
|
|
107
|
-
if (!
|
|
108
|
-
log.debug("memory
|
|
109
|
-
return { kind: "
|
|
106
|
+
if (!config.memory.v2.enabled) {
|
|
107
|
+
log.debug("memory.v2.enabled is false; consolidation skipped");
|
|
108
|
+
return { kind: "disabled" };
|
|
110
109
|
}
|
|
111
110
|
|
|
112
111
|
const memoryDir = join(getWorkspaceDir(), "memory");
|
|
@@ -360,6 +360,16 @@ interface RenderInjectionBlockResult {
|
|
|
360
360
|
missingSlugs: string[];
|
|
361
361
|
}
|
|
362
362
|
|
|
363
|
+
/**
|
|
364
|
+
* Leading instruction line emitted at the top of every non-empty injection
|
|
365
|
+
* block. Tells the agent that what follows are page summaries and that it
|
|
366
|
+
* should read the underlying file when a summary looks relevant. Pages
|
|
367
|
+
* without a `summary` field render in full instead — the agent treats
|
|
368
|
+
* those as inline content and doesn't need to follow up.
|
|
369
|
+
*/
|
|
370
|
+
const INJECTION_HEADER =
|
|
371
|
+
"**CRITICAL:** These are page summaries. Read the page file if it looks relevant.";
|
|
372
|
+
|
|
363
373
|
/**
|
|
364
374
|
* Render the inner content of the `<memory>` block for a list of slugs.
|
|
365
375
|
* The caller wraps the result in `<memory>...</memory>` exactly once at
|
|
@@ -383,23 +393,24 @@ interface RenderInjectionBlockResult {
|
|
|
383
393
|
* skill is an expected catalog-level outcome rather than a stale-index
|
|
384
394
|
* bug.
|
|
385
395
|
*
|
|
386
|
-
*
|
|
387
|
-
*
|
|
396
|
+
* Each concept-page section is rendered as a path header followed by either
|
|
397
|
+
* the page's `summary` (when present in frontmatter) or the full page (the
|
|
398
|
+
* fallback for pages predating the summary field). Skills sit at the end
|
|
399
|
+
* under `### Skills You Can Use`, unchanged. The leading `**CRITICAL:**`
|
|
400
|
+
* line tells the agent how to read the block.
|
|
401
|
+
*
|
|
402
|
+
* **CRITICAL:** These are page summaries. Read the page file if it looks relevant.
|
|
403
|
+
*
|
|
404
|
+
* # memory/concepts/<concept-slug-1>.md
|
|
405
|
+
* <summary-1>
|
|
388
406
|
*
|
|
389
|
-
*
|
|
407
|
+
* # memory/concepts/<concept-slug-2>.md
|
|
390
408
|
* ---
|
|
391
409
|
* edges:
|
|
392
410
|
* - <neighbor-slug>
|
|
393
411
|
* ref_files:
|
|
394
412
|
* - <path/to/asset>
|
|
395
413
|
* ---
|
|
396
|
-
* <body-1>
|
|
397
|
-
*
|
|
398
|
-
* ### <concept-slug-2>
|
|
399
|
-
* ---
|
|
400
|
-
* edges: []
|
|
401
|
-
* ref_files: []
|
|
402
|
-
* ---
|
|
403
414
|
* <body-2>
|
|
404
415
|
*
|
|
405
416
|
* ### Skills You Can Use
|
|
@@ -427,9 +438,18 @@ async function renderInjectionBlock(
|
|
|
427
438
|
missingSlugs.push(slug);
|
|
428
439
|
continue;
|
|
429
440
|
}
|
|
441
|
+
const summary = page.frontmatter.summary?.trim();
|
|
442
|
+
const path = `memory/concepts/${slug}.md`;
|
|
443
|
+
if (summary && summary.length > 0) {
|
|
444
|
+
sections.push(`# ${path}\n${summary}`);
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
// Fallback: page predates the `summary` field (or the field was set to
|
|
448
|
+
// empty). Render the full page — frontmatter + body — so retrieval
|
|
449
|
+
// still surfaces the same content the agent saw before this change.
|
|
430
450
|
const content = renderPageContent(page).trim();
|
|
431
451
|
if (content.length === 0) continue;
|
|
432
|
-
sections.push(
|
|
452
|
+
sections.push(`# ${path}\n${content}`);
|
|
433
453
|
}
|
|
434
454
|
|
|
435
455
|
const skillLines: string[] = [];
|
|
@@ -445,7 +465,7 @@ async function renderInjectionBlock(
|
|
|
445
465
|
if (sections.length === 0) return { block: null, missingSlugs };
|
|
446
466
|
|
|
447
467
|
return {
|
|
448
|
-
block: sections.join("\n\n")
|
|
468
|
+
block: `${INJECTION_HEADER}\n\n${sections.join("\n\n")}`,
|
|
449
469
|
missingSlugs,
|
|
450
470
|
};
|
|
451
471
|
}
|
|
@@ -338,6 +338,45 @@ export async function listPages(workspaceDir: string): Promise<string[]> {
|
|
|
338
338
|
return slugs;
|
|
339
339
|
}
|
|
340
340
|
|
|
341
|
+
/**
|
|
342
|
+
* Cheap "do any concept pages exist?" probe — walks the concepts/ tree only
|
|
343
|
+
* far enough to find one `.md` file and returns immediately. Used by the
|
|
344
|
+
* daemon-startup rebuild gate so the empty-after-create recovery path skips
|
|
345
|
+
* a full enumeration of all 1000+ pages just to ask a yes/no question.
|
|
346
|
+
*/
|
|
347
|
+
export async function hasConceptPages(workspaceDir: string): Promise<boolean> {
|
|
348
|
+
const root = getConceptsDir(workspaceDir);
|
|
349
|
+
const queue: string[] = [root];
|
|
350
|
+
|
|
351
|
+
while (queue.length > 0) {
|
|
352
|
+
const dir = queue.shift()!;
|
|
353
|
+
let entries;
|
|
354
|
+
try {
|
|
355
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
356
|
+
} catch (err) {
|
|
357
|
+
if ((err as NodeJS.ErrnoException).code === "ENOENT") {
|
|
358
|
+
if (dir === root) return false;
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
throw err;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
for (const entry of entries) {
|
|
365
|
+
if (entry.name.startsWith(".")) continue;
|
|
366
|
+
if (entry.isDirectory()) {
|
|
367
|
+
queue.push(join(dir, entry.name));
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
if (!entry.isFile()) continue;
|
|
371
|
+
if (!entry.name.endsWith(PAGE_EXTENSION)) continue;
|
|
372
|
+
if (entry.name.includes(".tmp.")) continue;
|
|
373
|
+
return true;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return false;
|
|
378
|
+
}
|
|
379
|
+
|
|
341
380
|
/**
|
|
342
381
|
* Delete a concept page. Idempotent — missing files are not an error.
|
|
343
382
|
*
|
|
@@ -131,6 +131,7 @@ edges:
|
|
|
131
131
|
- path/to/sister
|
|
132
132
|
- path/to/parent
|
|
133
133
|
ref_files: []
|
|
134
|
+
summary: 1-4 sentences describing what this article is. Plain prose only — no bullets, no newlines, no markdown lists. Lead with the most identifying detail.
|
|
134
135
|
---
|
|
135
136
|
# title
|
|
136
137
|
|
|
@@ -140,6 +141,8 @@ ref_files: []
|
|
|
140
141
|
- **bullet 2.** ...
|
|
141
142
|
\`\`\`
|
|
142
143
|
|
|
144
|
+
The \`summary\` field is required on every new or updated article. Retrieval injects \`path + summary\` into context — the agent reads the full file only when the summary looks relevant — so make the summary specific and terse. Keep it on a single YAML line (no \`|\` block scalars, no embedded newlines).
|
|
145
|
+
|
|
143
146
|
**Caps:** ~5-8 bullets per topic/concept article. ~10-12 per arc-node (which can use bold inline labels: \`**the open**: ...\`).
|
|
144
147
|
|
|
145
148
|
## One fact, one home
|
|
@@ -285,6 +288,7 @@ edges:
|
|
|
285
288
|
- some-named-phrase
|
|
286
289
|
- objects/some-artifact
|
|
287
290
|
ref_files: []
|
|
291
|
+
summary: A short prose description of the article — 1-4 sentences, single line.
|
|
288
292
|
---
|
|
289
293
|
\`\`\`
|
|
290
294
|
|
|
@@ -416,6 +420,7 @@ For each article you touched:
|
|
|
416
420
|
9. **Spawn check.** Did you ask "what's recognizable here?" not "what have I earned?" Did you catch any hedging — and spawn anyway? Any fold-into-parent / defer stealth-skips you almost did?
|
|
417
421
|
10. **Split-not-compress.** If anything went over cap, did you split? If you compressed, can you name the rationale in one sentence?
|
|
418
422
|
11. **Edges.** Outgoing within tiered caps (atomic ≤10, arc ≤15, gravity well ≤25, hard limit 20 on non-hubs)? No noise-edges to gravity wells from non-arc pages?
|
|
423
|
+
11a. **Summary present.** Every new or updated article has a \`summary:\` line — 1-4 sentences, single YAML line, lead with the identifying detail.
|
|
419
424
|
12. **Topic coherence.** Does each article answer ONE question? Gravity wells acting as hubs (pointing at topic articles), not absorbing body?
|
|
420
425
|
13. **\`recent.md\`** under 2000 chars, today=full / older=one-liners?
|
|
421
426
|
14. **\`[SOURCE NEEDED]\`** tags surfaced for human review?
|