crewly 1.8.4 → 1.8.6

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.
Files changed (85) hide show
  1. package/config/roles/_common/wiki-instructions.md +33 -0
  2. package/config/roles/orchestrator/prompt.md +66 -4
  3. package/config/roles/team-leader/prompt.md +38 -0
  4. package/config/skills/agent/core/wiki-query/SKILL.md +66 -0
  5. package/config/skills/agent/core/wiki-query/execute.sh +107 -0
  6. package/config/skills/orchestrator/wiki-bookkeep/SKILL.md +71 -0
  7. package/config/skills/orchestrator/wiki-bookkeep/execute.sh +72 -0
  8. package/config/skills/orchestrator/wiki-ingest/SKILL.md +63 -0
  9. package/config/skills/orchestrator/wiki-ingest/execute.sh +113 -0
  10. package/config/skills/orchestrator/wiki-process-queue/SKILL.md +71 -0
  11. package/config/skills/orchestrator/wiki-process-queue/execute.sh +93 -0
  12. package/config/skills/orchestrator/wiki-queue-add/SKILL.md +89 -0
  13. package/config/skills/orchestrator/wiki-queue-add/execute.sh +115 -0
  14. package/dist/backend/backend/src/controllers/chat/chat.controller.d.ts.map +1 -1
  15. package/dist/backend/backend/src/controllers/chat/chat.controller.js +20 -0
  16. package/dist/backend/backend/src/controllers/chat/chat.controller.js.map +1 -1
  17. package/dist/backend/backend/src/controllers/slack/slack.controller.d.ts.map +1 -1
  18. package/dist/backend/backend/src/controllers/slack/slack.controller.js +15 -0
  19. package/dist/backend/backend/src/controllers/slack/slack.controller.js.map +1 -1
  20. package/dist/backend/backend/src/controllers/wiki/wiki.controller.d.ts +134 -0
  21. package/dist/backend/backend/src/controllers/wiki/wiki.controller.d.ts.map +1 -0
  22. package/dist/backend/backend/src/controllers/wiki/wiki.controller.js +718 -0
  23. package/dist/backend/backend/src/controllers/wiki/wiki.controller.js.map +1 -0
  24. package/dist/backend/backend/src/controllers/wiki/wiki.routes.d.ts +23 -0
  25. package/dist/backend/backend/src/controllers/wiki/wiki.routes.d.ts.map +1 -0
  26. package/dist/backend/backend/src/controllers/wiki/wiki.routes.js +43 -0
  27. package/dist/backend/backend/src/controllers/wiki/wiki.routes.js.map +1 -0
  28. package/dist/backend/backend/src/index.d.ts.map +1 -1
  29. package/dist/backend/backend/src/index.js +65 -0
  30. package/dist/backend/backend/src/index.js.map +1 -1
  31. package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
  32. package/dist/backend/backend/src/routes/api.routes.js +4 -0
  33. package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
  34. package/dist/backend/backend/src/services/orc/orc-delivery-enforcer.service.d.ts +142 -0
  35. package/dist/backend/backend/src/services/orc/orc-delivery-enforcer.service.d.ts.map +1 -0
  36. package/dist/backend/backend/src/services/orc/orc-delivery-enforcer.service.js +265 -0
  37. package/dist/backend/backend/src/services/orc/orc-delivery-enforcer.service.js.map +1 -0
  38. package/dist/backend/backend/src/services/session/pty/pty-session.d.ts +28 -0
  39. package/dist/backend/backend/src/services/session/pty/pty-session.d.ts.map +1 -1
  40. package/dist/backend/backend/src/services/session/pty/pty-session.js +162 -4
  41. package/dist/backend/backend/src/services/session/pty/pty-session.js.map +1 -1
  42. package/dist/backend/backend/src/services/wiki/referenced-by.resolver.d.ts +69 -0
  43. package/dist/backend/backend/src/services/wiki/referenced-by.resolver.d.ts.map +1 -0
  44. package/dist/backend/backend/src/services/wiki/referenced-by.resolver.js +174 -0
  45. package/dist/backend/backend/src/services/wiki/referenced-by.resolver.js.map +1 -0
  46. package/dist/backend/backend/src/services/wiki/schema-loader.service.d.ts +57 -0
  47. package/dist/backend/backend/src/services/wiki/schema-loader.service.d.ts.map +1 -0
  48. package/dist/backend/backend/src/services/wiki/schema-loader.service.js +183 -0
  49. package/dist/backend/backend/src/services/wiki/schema-loader.service.js.map +1 -0
  50. package/dist/backend/backend/src/services/wiki/wiki-bookkeep-trigger.service.d.ts +86 -0
  51. package/dist/backend/backend/src/services/wiki/wiki-bookkeep-trigger.service.d.ts.map +1 -0
  52. package/dist/backend/backend/src/services/wiki/wiki-bookkeep-trigger.service.js +187 -0
  53. package/dist/backend/backend/src/services/wiki/wiki-bookkeep-trigger.service.js.map +1 -0
  54. package/dist/backend/backend/src/services/wiki/wiki-bookkeep.service.d.ts +116 -0
  55. package/dist/backend/backend/src/services/wiki/wiki-bookkeep.service.d.ts.map +1 -0
  56. package/dist/backend/backend/src/services/wiki/wiki-bookkeep.service.js +299 -0
  57. package/dist/backend/backend/src/services/wiki/wiki-bookkeep.service.js.map +1 -0
  58. package/dist/backend/backend/src/services/wiki/wiki-chat-subscriber.service.d.ts +74 -0
  59. package/dist/backend/backend/src/services/wiki/wiki-chat-subscriber.service.d.ts.map +1 -0
  60. package/dist/backend/backend/src/services/wiki/wiki-chat-subscriber.service.js +154 -0
  61. package/dist/backend/backend/src/services/wiki/wiki-chat-subscriber.service.js.map +1 -0
  62. package/dist/backend/backend/src/services/wiki/wiki-ingest.service.d.ts +100 -0
  63. package/dist/backend/backend/src/services/wiki/wiki-ingest.service.d.ts.map +1 -0
  64. package/dist/backend/backend/src/services/wiki/wiki-ingest.service.js +212 -0
  65. package/dist/backend/backend/src/services/wiki/wiki-ingest.service.js.map +1 -0
  66. package/dist/backend/backend/src/services/wiki/wiki-process.service.d.ts +84 -0
  67. package/dist/backend/backend/src/services/wiki/wiki-process.service.d.ts.map +1 -0
  68. package/dist/backend/backend/src/services/wiki/wiki-process.service.js +138 -0
  69. package/dist/backend/backend/src/services/wiki/wiki-process.service.js.map +1 -0
  70. package/dist/backend/backend/src/services/wiki/wiki-query.service.d.ts +115 -0
  71. package/dist/backend/backend/src/services/wiki/wiki-query.service.d.ts.map +1 -0
  72. package/dist/backend/backend/src/services/wiki/wiki-query.service.js +291 -0
  73. package/dist/backend/backend/src/services/wiki/wiki-query.service.js.map +1 -0
  74. package/dist/backend/backend/src/services/wiki/wiki-queue.service.d.ts +115 -0
  75. package/dist/backend/backend/src/services/wiki/wiki-queue.service.d.ts.map +1 -0
  76. package/dist/backend/backend/src/services/wiki/wiki-queue.service.js +261 -0
  77. package/dist/backend/backend/src/services/wiki/wiki-queue.service.js.map +1 -0
  78. package/dist/backend/backend/src/services/wiki/wiki.types.d.ts +84 -0
  79. package/dist/backend/backend/src/services/wiki/wiki.types.d.ts.map +1 -0
  80. package/dist/backend/backend/src/services/wiki/wiki.types.js +10 -0
  81. package/dist/backend/backend/src/services/wiki/wiki.types.js.map +1 -0
  82. package/frontend/dist/assets/{index-b279da34.js → index-cc115bb4.js} +246 -246
  83. package/frontend/dist/assets/{index-c07e04c0.css → index-db3f5041.css} +1 -1
  84. package/frontend/dist/index.html +2 -2
  85. package/package.json +1 -1
@@ -0,0 +1,138 @@
1
+ /**
2
+ * WikiProcessService — bundles "claim a pending item + build the
3
+ * system-context the agent's LLM needs to classify it."
4
+ *
5
+ * Per Steve's 2026-05-22 redesign:
6
+ * - the skill DOES NOT pick a target folder. The agent picks.
7
+ * - there is NO preset taxonomy beyond the frozen folders (sop/,
8
+ * team-norm/, memory/, sop-overrides/). Everything under
9
+ * llm-curated/* is fair game; the agent invents folder names.
10
+ * - the agent reads the queue item's `reason` (which we forced it to
11
+ * write at queue-add time) PLUS the existing vault state (so it
12
+ * doesn't re-classify into a duplicate of an existing page).
13
+ *
14
+ * Lifecycle this service participates in:
15
+ * pending → (this service) → claimed + context payload returned
16
+ * → (agent's LLM, outside the skill) picks target path
17
+ * → (agent calls wiki-ingest) → page written
18
+ * → (agent calls queue/:id/process) → status=processed
19
+ *
20
+ * @module services/wiki/wiki-process.service
21
+ */
22
+ import { LoggerService } from '../core/logger.service.js';
23
+ import { WikiQueueService } from './wiki-queue.service.js';
24
+ import { WikiQueryService, } from './wiki-query.service.js';
25
+ /**
26
+ * Stateless façade — both backing services (queue, query) are singletons.
27
+ */
28
+ export class WikiProcessService {
29
+ queue;
30
+ query;
31
+ static instance = null;
32
+ logger;
33
+ constructor(queue = WikiQueueService.getInstance(), query = WikiQueryService.getInstance()) {
34
+ this.queue = queue;
35
+ this.query = query;
36
+ this.logger = LoggerService.getInstance().createComponentLogger('WikiProcess');
37
+ }
38
+ static getInstance() {
39
+ if (!this.instance)
40
+ this.instance = new WikiProcessService();
41
+ return this.instance;
42
+ }
43
+ /** Test-only reset. */
44
+ static _resetForTesting() {
45
+ this.instance = null;
46
+ }
47
+ /**
48
+ * Claim the next pending item + return the vault context the agent
49
+ * needs to classify it. The caller (agent runtime) then runs an LLM
50
+ * call against this context + the item's content/reason, picks a
51
+ * target page, and calls `wiki-ingest` + `/queue/:id/process` to
52
+ * commit.
53
+ */
54
+ async claimNext(input) {
55
+ if (!input.claimedBy) {
56
+ return {
57
+ ok: false,
58
+ reason: 'no_pending_items',
59
+ message: 'claimedBy is required',
60
+ };
61
+ }
62
+ const pending = await this.queue.list({
63
+ status: 'pending',
64
+ vaultPath: input.vaultPath,
65
+ limit: 1 + (input.offset ?? 0),
66
+ });
67
+ const offset = input.offset ?? 0;
68
+ if (pending.length <= offset) {
69
+ return {
70
+ ok: false,
71
+ reason: 'no_pending_items',
72
+ message: input.vaultPath
73
+ ? `No pending items in queue for vault ${input.vaultPath}`
74
+ : 'No pending items in queue',
75
+ };
76
+ }
77
+ // Items are newest-first; oldest pending is the last one.
78
+ // Phase 1 picks newest after offset; future heuristics can swap order.
79
+ const target = pending[offset];
80
+ const claimed = await this.queue.claim(target.id, input.claimedBy);
81
+ // Build vault context for the agent's LLM. We use the queue item's
82
+ // content as the query so the candidate-page search surfaces the
83
+ // most-likely existing pages to merge into / dedupe against.
84
+ const vaultQuery = await this.query.query({
85
+ vaultPath: claimed.vaultPath,
86
+ query: claimed.content,
87
+ topK: input.topK ?? 5,
88
+ recentLogEntries: input.recentLogEntries ?? 10,
89
+ });
90
+ if (!vaultQuery.ok) {
91
+ this.logger.warn('WikiProcess vault query failed', {
92
+ itemId: claimed.id,
93
+ reason: vaultQuery.reason,
94
+ message: vaultQuery.message,
95
+ });
96
+ // We've already claimed; return what we have. Caller's runtime can
97
+ // still classify with just the item — the vault context is a help,
98
+ // not a hard requirement.
99
+ return {
100
+ ok: true,
101
+ result: {
102
+ item: claimed,
103
+ vaultContext: null,
104
+ classifierNotes: this.classifierNotes(),
105
+ },
106
+ };
107
+ }
108
+ this.logger.info('WikiProcess claimed', {
109
+ itemId: claimed.id,
110
+ vaultPath: claimed.vaultPath,
111
+ claimedBy: input.claimedBy,
112
+ candidatePages: vaultQuery.context.candidatePages.length,
113
+ });
114
+ return {
115
+ ok: true,
116
+ result: {
117
+ item: claimed,
118
+ vaultContext: vaultQuery.context,
119
+ classifierNotes: this.classifierNotes(),
120
+ },
121
+ };
122
+ }
123
+ /**
124
+ * The contract the agent's LLM must honor when classifying. Kept
125
+ * short so it can be inlined into the runtime's task-instruction.
126
+ */
127
+ classifierNotes() {
128
+ return [
129
+ 'You have ALREADY decided this item is wiki-worthy at queue-add time (see item.reason). Do NOT re-justify; classify.',
130
+ 'Pick the target page path under llm-curated/. You may invent sub-folder names — there is NO preset taxonomy. Examples: llm-curated/customers/anthropic.md, llm-curated/decisions/2026-05-22-pricing-lock.md, llm-curated/patterns/embedding-fallback.md.',
131
+ 'NEVER target a frozen folder (vaultContext.schemaSummary.frozenPaths). The ingest call will reject these with HTTP 422.',
132
+ 'PREFER merging into an existing relevant page over creating a new one. Use vaultContext.candidatePages to check first; only create a new page when nothing fits.',
133
+ 'If after reading the context you decide the item is actually NOT wiki-worthy after all (duplicate of existing content, low signal), mark it skipped via POST /queue/:id/skip with a `skipReason`.',
134
+ 'When committing: 1) call wiki-ingest with the target path; 2) call POST /queue/:id/process with { ingested, pagesWritten, targetPath, summary }.',
135
+ ];
136
+ }
137
+ }
138
+ //# sourceMappingURL=wiki-process.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wiki-process.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/wiki/wiki-process.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,115 @@
1
+ /**
2
+ * WikiQueryService — read a vault and build the `system-context` payload
3
+ * that the caller's runtime feeds to its LLM for synthesis.
4
+ *
5
+ * Per v2.1 spec §3 the skill is split into two halves:
6
+ * - System-context (LLM-agnostic) → what this service produces
7
+ * - Task-instruction (per-LLM) → lives on disk under
8
+ * `config/skills/<role>/wiki-<verb>/prompts/<runtime>.md` and is
9
+ * concatenated by the caller's runtime, NOT by us.
10
+ *
11
+ * Phase 1 scope (this file): single-vault read. Cross-vault synthesis is
12
+ * Phase 2 work (`wiki-recall-cross-scope` reducer, §1 amended v2.1).
13
+ *
14
+ * @module services/wiki/wiki-query.service
15
+ */
16
+ import { SchemaLoaderService } from './schema-loader.service.js';
17
+ import { VaultSchema } from './wiki.types.js';
18
+ export interface WikiQueryInput {
19
+ /** Absolute path to the vault root. */
20
+ vaultPath: string;
21
+ /** Natural-language query. Used for naive keyword ranking. */
22
+ query: string;
23
+ /** How many candidate pages to return (default 5). */
24
+ topK?: number;
25
+ /** How many tail log.md entries to include (default 20). */
26
+ recentLogEntries?: number;
27
+ }
28
+ export interface WikiLogEntry {
29
+ timestamp: string;
30
+ sourceType: string;
31
+ caller: string;
32
+ ref: string;
33
+ body: string;
34
+ }
35
+ export interface WikiCandidatePage {
36
+ /** Path relative to vault root. */
37
+ path: string;
38
+ /** First ~MAX_PAGE_BYTES bytes of the file (UTF-8). */
39
+ excerpt: string;
40
+ /** Simple keyword overlap score; higher = more relevant. */
41
+ score: number;
42
+ }
43
+ export interface WikiQuerySystemContext {
44
+ vault: {
45
+ scope: VaultSchema['vault_scope'];
46
+ id: string;
47
+ path: string;
48
+ };
49
+ schemaSummary: {
50
+ hardcoded: Array<{
51
+ path: string;
52
+ description: string;
53
+ }>;
54
+ llmCurated: Array<{
55
+ path: string;
56
+ seedSubdirs: string[];
57
+ }>;
58
+ frozenPaths: string[];
59
+ writePolicy: VaultSchema['write_policy'];
60
+ };
61
+ query: string;
62
+ recentLog: WikiLogEntry[];
63
+ candidatePages: WikiCandidatePage[];
64
+ /** Synthesis instructions the caller's LLM should honor. */
65
+ callerNotes: string[];
66
+ }
67
+ export interface WikiQueryFailure {
68
+ ok: false;
69
+ reason: 'invalid_input' | 'schema_missing' | 'vault_missing';
70
+ message: string;
71
+ }
72
+ export type WikiQueryResult = {
73
+ ok: true;
74
+ context: WikiQuerySystemContext;
75
+ } | WikiQueryFailure;
76
+ /**
77
+ * Reads a vault and builds the system-context payload the caller's LLM
78
+ * synthesizes against. Stateless. No LLM calls inside this service.
79
+ */
80
+ export declare class WikiQueryService {
81
+ private static instance;
82
+ private readonly logger;
83
+ private readonly schemaLoader;
84
+ constructor(schemaLoader?: SchemaLoaderService);
85
+ static getInstance(): WikiQueryService;
86
+ /** Test-only reset. */
87
+ static _resetForTesting(): void;
88
+ /**
89
+ * Build a system-context payload for `query` against the vault at
90
+ * `vaultPath`. Caller's LLM consumes this + its task-instruction prompt.
91
+ */
92
+ query(input: WikiQueryInput): Promise<WikiQueryResult>;
93
+ private validate;
94
+ /**
95
+ * Parse the last N entries from `llm-curated/log.md`. Entries follow the
96
+ * format written by `WikiIngestService.formatLogEntry`:
97
+ *
98
+ * ## [<ISO>] <sourceType> | <caller>
99
+ *
100
+ * ref: <ref>
101
+ *
102
+ * <body lines>
103
+ */
104
+ private readRecentLog;
105
+ /**
106
+ * Walk the vault's NON-frozen subtrees and rank pages by keyword overlap
107
+ * with the query. Phase 1 is intentionally simple — Phase 2 will swap in
108
+ * embedding-based scoring.
109
+ */
110
+ private findCandidatePages;
111
+ private tokenize;
112
+ private scorePage;
113
+ private readExcerpt;
114
+ }
115
+ //# sourceMappingURL=wiki-query.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wiki-query.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/wiki/wiki-query.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAO9C,MAAM,WAAW,cAAc;IAC7B,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,8DAA8D;IAC9D,KAAK,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4DAA4D;IAC5D,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,iBAAiB;IAChC,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE;QAAE,KAAK,EAAE,WAAW,CAAC,aAAa,CAAC,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACvE,aAAa,EAAE;QACb,SAAS,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACxD,UAAU,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,MAAM,EAAE,CAAA;SAAE,CAAC,CAAC;QAC3D,WAAW,EAAE,MAAM,EAAE,CAAC;QACtB,WAAW,EAAE,WAAW,CAAC,cAAc,CAAC,CAAC;KAC1C,CAAC;IACF,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,cAAc,EAAE,iBAAiB,EAAE,CAAC;IACpC,4DAA4D;IAC5D,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,eAAe,GAAG,gBAAgB,GAAG,eAAe,CAAC;IAC7D,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,eAAe,GACvB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,sBAAsB,CAAA;CAAE,GAC7C,gBAAgB,CAAC;AAErB;;;GAGG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAiC;IACxD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAsB;gBAEvC,YAAY,CAAC,EAAE,mBAAmB;IAK9C,MAAM,CAAC,WAAW,IAAI,gBAAgB;IAOtC,uBAAuB;IACvB,MAAM,CAAC,gBAAgB,IAAI,IAAI;IAI/B;;;OAGG;IACG,KAAK,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC;IAqE5D,OAAO,CAAC,QAAQ;IAmChB;;;;;;;;;OASG;YACW,aAAa;IA8C3B;;;;OAIG;YACW,kBAAkB;IA2DhC,OAAO,CAAC,QAAQ;YAOF,SAAS;YAuBT,WAAW;CAS1B"}
@@ -0,0 +1,291 @@
1
+ /**
2
+ * WikiQueryService — read a vault and build the `system-context` payload
3
+ * that the caller's runtime feeds to its LLM for synthesis.
4
+ *
5
+ * Per v2.1 spec §3 the skill is split into two halves:
6
+ * - System-context (LLM-agnostic) → what this service produces
7
+ * - Task-instruction (per-LLM) → lives on disk under
8
+ * `config/skills/<role>/wiki-<verb>/prompts/<runtime>.md` and is
9
+ * concatenated by the caller's runtime, NOT by us.
10
+ *
11
+ * Phase 1 scope (this file): single-vault read. Cross-vault synthesis is
12
+ * Phase 2 work (`wiki-recall-cross-scope` reducer, §1 amended v2.1).
13
+ *
14
+ * @module services/wiki/wiki-query.service
15
+ */
16
+ import * as path from 'path';
17
+ import * as fs from 'fs/promises';
18
+ import { LoggerService } from '../core/logger.service.js';
19
+ import { SchemaLoaderService } from './schema-loader.service.js';
20
+ const DEFAULT_RECENT_LOG_ENTRIES = 20;
21
+ const DEFAULT_TOP_K_PAGES = 5;
22
+ const MAX_PAGE_BYTES = 8 * 1024; // truncate per-page excerpt
23
+ const MAX_TOTAL_PAGES_SCANNED = 200; // safety cap on filesystem walk
24
+ /**
25
+ * Reads a vault and builds the system-context payload the caller's LLM
26
+ * synthesizes against. Stateless. No LLM calls inside this service.
27
+ */
28
+ export class WikiQueryService {
29
+ static instance = null;
30
+ logger;
31
+ schemaLoader;
32
+ constructor(schemaLoader) {
33
+ this.logger = LoggerService.getInstance().createComponentLogger('WikiQuery');
34
+ this.schemaLoader = schemaLoader ?? new SchemaLoaderService();
35
+ }
36
+ static getInstance() {
37
+ if (!this.instance) {
38
+ this.instance = new WikiQueryService();
39
+ }
40
+ return this.instance;
41
+ }
42
+ /** Test-only reset. */
43
+ static _resetForTesting() {
44
+ this.instance = null;
45
+ }
46
+ /**
47
+ * Build a system-context payload for `query` against the vault at
48
+ * `vaultPath`. Caller's LLM consumes this + its task-instruction prompt.
49
+ */
50
+ async query(input) {
51
+ const validation = this.validate(input);
52
+ if (validation)
53
+ return validation;
54
+ let schema;
55
+ try {
56
+ schema = await this.schemaLoader.load(input.vaultPath);
57
+ }
58
+ catch (err) {
59
+ return {
60
+ ok: false,
61
+ reason: 'schema_missing',
62
+ message: err.message,
63
+ };
64
+ }
65
+ const topK = input.topK ?? DEFAULT_TOP_K_PAGES;
66
+ const recentN = input.recentLogEntries ?? DEFAULT_RECENT_LOG_ENTRIES;
67
+ const recentLog = await this.readRecentLog(input.vaultPath, recentN);
68
+ const candidatePages = await this.findCandidatePages(input.vaultPath, schema, input.query, topK);
69
+ const context = {
70
+ vault: {
71
+ scope: schema.vault_scope,
72
+ id: schema.vault_id,
73
+ path: input.vaultPath,
74
+ },
75
+ schemaSummary: {
76
+ hardcoded: schema.hardcoded.map((h) => ({
77
+ path: h.path,
78
+ description: h.description,
79
+ })),
80
+ llmCurated: schema.llm_curated.map((l) => ({
81
+ path: l.path,
82
+ seedSubdirs: l.seed_subdirs,
83
+ })),
84
+ frozenPaths: this.schemaLoader.getFrozenPaths(schema),
85
+ writePolicy: schema.write_policy,
86
+ },
87
+ query: input.query,
88
+ recentLog,
89
+ candidatePages,
90
+ callerNotes: [
91
+ 'Cite pages by their relative path inside the vault.',
92
+ 'Do not propose writes into any frozenPaths — refuse or redirect to llm-curated/.',
93
+ 'Treat candidatePages.excerpt as truncated; ask wiki-query again with a refined query if you need full content.',
94
+ 'The recentLog is append-only audit; never propose rewrites to its content.',
95
+ ],
96
+ };
97
+ this.logger.debug('WikiQuery built system-context', {
98
+ vault: input.vaultPath,
99
+ queryLen: input.query.length,
100
+ logEntries: recentLog.length,
101
+ candidatePages: candidatePages.length,
102
+ });
103
+ return { ok: true, context };
104
+ }
105
+ // ---------------------------------------------------------------------------
106
+ // Internals
107
+ // ---------------------------------------------------------------------------
108
+ validate(input) {
109
+ if (!input.vaultPath || !path.isAbsolute(input.vaultPath)) {
110
+ return {
111
+ ok: false,
112
+ reason: 'invalid_input',
113
+ message: `vaultPath must be an absolute path, got "${input.vaultPath ?? ''}"`,
114
+ };
115
+ }
116
+ if (typeof input.query !== 'string' || input.query.trim().length === 0) {
117
+ return {
118
+ ok: false,
119
+ reason: 'invalid_input',
120
+ message: 'query must be a non-empty string',
121
+ };
122
+ }
123
+ if (input.topK !== undefined && (!Number.isInteger(input.topK) || input.topK <= 0)) {
124
+ return {
125
+ ok: false,
126
+ reason: 'invalid_input',
127
+ message: 'topK must be a positive integer when provided',
128
+ };
129
+ }
130
+ if (input.recentLogEntries !== undefined &&
131
+ (!Number.isInteger(input.recentLogEntries) || input.recentLogEntries < 0)) {
132
+ return {
133
+ ok: false,
134
+ reason: 'invalid_input',
135
+ message: 'recentLogEntries must be a non-negative integer when provided',
136
+ };
137
+ }
138
+ return null;
139
+ }
140
+ /**
141
+ * Parse the last N entries from `llm-curated/log.md`. Entries follow the
142
+ * format written by `WikiIngestService.formatLogEntry`:
143
+ *
144
+ * ## [<ISO>] <sourceType> | <caller>
145
+ *
146
+ * ref: <ref>
147
+ *
148
+ * <body lines>
149
+ */
150
+ async readRecentLog(vaultPath, limit) {
151
+ const logPath = path.join(vaultPath, 'llm-curated', 'log.md');
152
+ let raw;
153
+ try {
154
+ raw = await fs.readFile(logPath, 'utf8');
155
+ }
156
+ catch {
157
+ return [];
158
+ }
159
+ const headerRe = /^## \[([^\]]+)\]\s+(\S+)\s+\|\s+(.+)$/gm;
160
+ const entries = [];
161
+ const matches = [];
162
+ let m;
163
+ while ((m = headerRe.exec(raw))) {
164
+ matches.push({
165
+ idx: m.index,
166
+ ts: m[1],
167
+ sourceType: m[2],
168
+ caller: m[3].trim(),
169
+ });
170
+ }
171
+ for (let i = 0; i < matches.length; i++) {
172
+ const start = matches[i].idx;
173
+ const end = i + 1 < matches.length ? matches[i + 1].idx : raw.length;
174
+ const block = raw.slice(start, end);
175
+ const refMatch = block.match(/^ref:\s*(.+)$/m);
176
+ // Body is everything after the `ref:` line (skipping the blank line).
177
+ let body = '';
178
+ if (refMatch) {
179
+ const refLineEnd = block.indexOf(refMatch[0]) + refMatch[0].length;
180
+ body = block.slice(refLineEnd).replace(/^\n+/, '').trim();
181
+ }
182
+ entries.push({
183
+ timestamp: matches[i].ts,
184
+ sourceType: matches[i].sourceType,
185
+ caller: matches[i].caller,
186
+ ref: refMatch ? refMatch[1].trim() : '',
187
+ body,
188
+ });
189
+ }
190
+ // Most-recent-first; cap to requested limit.
191
+ return entries.slice(-limit).reverse();
192
+ }
193
+ /**
194
+ * Walk the vault's NON-frozen subtrees and rank pages by keyword overlap
195
+ * with the query. Phase 1 is intentionally simple — Phase 2 will swap in
196
+ * embedding-based scoring.
197
+ */
198
+ async findCandidatePages(vaultPath, schema, query, topK) {
199
+ const queryTerms = this.tokenize(query);
200
+ if (queryTerms.length === 0)
201
+ return [];
202
+ const llmCuratedRoots = schema.llm_curated.map((l) => path.join(vaultPath, l.path.replace(/[/\\]+$/, '')));
203
+ const candidates = [];
204
+ let scanned = 0;
205
+ for (const root of llmCuratedRoots) {
206
+ const stack = [root];
207
+ while (stack.length > 0 && scanned < MAX_TOTAL_PAGES_SCANNED) {
208
+ const current = stack.pop();
209
+ let entries;
210
+ try {
211
+ entries = await fs.readdir(current, { withFileTypes: true });
212
+ }
213
+ catch {
214
+ continue;
215
+ }
216
+ for (const entry of entries) {
217
+ const full = path.join(current, entry.name);
218
+ if (entry.isDirectory()) {
219
+ if (entry.name.startsWith('.') || entry.name === 'node_modules')
220
+ continue;
221
+ stack.push(full);
222
+ continue;
223
+ }
224
+ if (!entry.isFile())
225
+ continue;
226
+ if (!entry.name.endsWith('.md'))
227
+ continue;
228
+ // Skip log.md — it's surfaced separately as recentLog.
229
+ if (path.relative(vaultPath, full).replace(/\\/g, '/') ===
230
+ 'llm-curated/log.md') {
231
+ continue;
232
+ }
233
+ scanned++;
234
+ const score = await this.scorePage(full, queryTerms);
235
+ if (score > 0) {
236
+ const excerpt = await this.readExcerpt(full);
237
+ candidates.push({
238
+ path: path.relative(vaultPath, full).replace(/\\/g, '/'),
239
+ excerpt,
240
+ score,
241
+ });
242
+ }
243
+ }
244
+ }
245
+ }
246
+ candidates.sort((a, b) => b.score - a.score);
247
+ return candidates.slice(0, topK);
248
+ }
249
+ tokenize(text) {
250
+ return text
251
+ .toLowerCase()
252
+ .split(/[\s\p{P}]+/u)
253
+ .filter((t) => t.length >= 2);
254
+ }
255
+ async scorePage(absolutePath, queryTerms) {
256
+ let content;
257
+ try {
258
+ content = (await fs.readFile(absolutePath, 'utf8')).toLowerCase();
259
+ }
260
+ catch {
261
+ return 0;
262
+ }
263
+ let score = 0;
264
+ for (const term of queryTerms) {
265
+ // Bounded count: don't reward spammy repetition above 10.
266
+ let count = 0;
267
+ let idx = 0;
268
+ while (count < 10) {
269
+ const found = content.indexOf(term, idx);
270
+ if (found < 0)
271
+ break;
272
+ count++;
273
+ idx = found + term.length;
274
+ }
275
+ score += count;
276
+ }
277
+ return score;
278
+ }
279
+ async readExcerpt(absolutePath) {
280
+ try {
281
+ const raw = await fs.readFile(absolutePath, 'utf8');
282
+ if (Buffer.byteLength(raw, 'utf8') <= MAX_PAGE_BYTES)
283
+ return raw;
284
+ return raw.slice(0, MAX_PAGE_BYTES) + '\n\n…[truncated]';
285
+ }
286
+ catch {
287
+ return '';
288
+ }
289
+ }
290
+ }
291
+ //# sourceMappingURL=wiki-query.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wiki-query.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/wiki/wiki-query.service.ts"],"names":[],"mappings":"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"}