crewly 1.8.3 → 1.8.5
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/config/roles/_common/wiki-instructions.md +33 -0
- package/config/roles/orchestrator/prompt.md +121 -0
- package/config/roles/team-leader/prompt.md +38 -0
- package/config/skills/agent/core/wiki-query/SKILL.md +66 -0
- package/config/skills/agent/core/wiki-query/execute.sh +107 -0
- package/config/skills/orchestrator/wiki-bookkeep/SKILL.md +71 -0
- package/config/skills/orchestrator/wiki-bookkeep/execute.sh +72 -0
- package/config/skills/orchestrator/wiki-ingest/SKILL.md +63 -0
- package/config/skills/orchestrator/wiki-ingest/execute.sh +113 -0
- package/config/skills/orchestrator/wiki-process-queue/SKILL.md +71 -0
- package/config/skills/orchestrator/wiki-process-queue/execute.sh +93 -0
- package/config/skills/orchestrator/wiki-queue-add/SKILL.md +89 -0
- package/config/skills/orchestrator/wiki-queue-add/execute.sh +115 -0
- package/dist/backend/backend/src/controllers/wiki/wiki.controller.d.ts +134 -0
- package/dist/backend/backend/src/controllers/wiki/wiki.controller.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/wiki/wiki.controller.js +718 -0
- package/dist/backend/backend/src/controllers/wiki/wiki.controller.js.map +1 -0
- package/dist/backend/backend/src/controllers/wiki/wiki.routes.d.ts +23 -0
- package/dist/backend/backend/src/controllers/wiki/wiki.routes.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/wiki/wiki.routes.js +43 -0
- package/dist/backend/backend/src/controllers/wiki/wiki.routes.js.map +1 -0
- package/dist/backend/backend/src/index.d.ts.map +1 -1
- package/dist/backend/backend/src/index.js +39 -0
- package/dist/backend/backend/src/index.js.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.js +4 -0
- package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
- package/dist/backend/backend/src/services/core/system-health.util.d.ts +29 -4
- package/dist/backend/backend/src/services/core/system-health.util.d.ts.map +1 -1
- package/dist/backend/backend/src/services/core/system-health.util.js +105 -6
- package/dist/backend/backend/src/services/core/system-health.util.js.map +1 -1
- package/dist/backend/backend/src/services/session/pty/pty-session.d.ts +28 -0
- package/dist/backend/backend/src/services/session/pty/pty-session.d.ts.map +1 -1
- package/dist/backend/backend/src/services/session/pty/pty-session.js +162 -4
- package/dist/backend/backend/src/services/session/pty/pty-session.js.map +1 -1
- package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts +20 -0
- package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/task-pool/task-pool.service.js +61 -0
- package/dist/backend/backend/src/services/task-pool/task-pool.service.js.map +1 -1
- package/dist/backend/backend/src/services/v3/escalation-router.service.d.ts +38 -1
- package/dist/backend/backend/src/services/v3/escalation-router.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/escalation-router.service.js +124 -0
- package/dist/backend/backend/src/services/v3/escalation-router.service.js.map +1 -1
- package/dist/backend/backend/src/services/v3/v3-data.service.d.ts +19 -2
- package/dist/backend/backend/src/services/v3/v3-data.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/v3-data.service.js +64 -7
- package/dist/backend/backend/src/services/v3/v3-data.service.js.map +1 -1
- package/dist/backend/backend/src/services/wiki/referenced-by.resolver.d.ts +69 -0
- package/dist/backend/backend/src/services/wiki/referenced-by.resolver.d.ts.map +1 -0
- package/dist/backend/backend/src/services/wiki/referenced-by.resolver.js +174 -0
- package/dist/backend/backend/src/services/wiki/referenced-by.resolver.js.map +1 -0
- package/dist/backend/backend/src/services/wiki/schema-loader.service.d.ts +57 -0
- package/dist/backend/backend/src/services/wiki/schema-loader.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/wiki/schema-loader.service.js +183 -0
- package/dist/backend/backend/src/services/wiki/schema-loader.service.js.map +1 -0
- package/dist/backend/backend/src/services/wiki/wiki-bookkeep-trigger.service.d.ts +86 -0
- package/dist/backend/backend/src/services/wiki/wiki-bookkeep-trigger.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/wiki/wiki-bookkeep-trigger.service.js +187 -0
- package/dist/backend/backend/src/services/wiki/wiki-bookkeep-trigger.service.js.map +1 -0
- package/dist/backend/backend/src/services/wiki/wiki-bookkeep.service.d.ts +116 -0
- package/dist/backend/backend/src/services/wiki/wiki-bookkeep.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/wiki/wiki-bookkeep.service.js +299 -0
- package/dist/backend/backend/src/services/wiki/wiki-bookkeep.service.js.map +1 -0
- package/dist/backend/backend/src/services/wiki/wiki-chat-subscriber.service.d.ts +74 -0
- package/dist/backend/backend/src/services/wiki/wiki-chat-subscriber.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/wiki/wiki-chat-subscriber.service.js +154 -0
- package/dist/backend/backend/src/services/wiki/wiki-chat-subscriber.service.js.map +1 -0
- package/dist/backend/backend/src/services/wiki/wiki-ingest.service.d.ts +100 -0
- package/dist/backend/backend/src/services/wiki/wiki-ingest.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/wiki/wiki-ingest.service.js +212 -0
- package/dist/backend/backend/src/services/wiki/wiki-ingest.service.js.map +1 -0
- package/dist/backend/backend/src/services/wiki/wiki-process.service.d.ts +84 -0
- package/dist/backend/backend/src/services/wiki/wiki-process.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/wiki/wiki-process.service.js +138 -0
- package/dist/backend/backend/src/services/wiki/wiki-process.service.js.map +1 -0
- package/dist/backend/backend/src/services/wiki/wiki-query.service.d.ts +115 -0
- package/dist/backend/backend/src/services/wiki/wiki-query.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/wiki/wiki-query.service.js +291 -0
- package/dist/backend/backend/src/services/wiki/wiki-query.service.js.map +1 -0
- package/dist/backend/backend/src/services/wiki/wiki-queue.service.d.ts +115 -0
- package/dist/backend/backend/src/services/wiki/wiki-queue.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/wiki/wiki-queue.service.js +261 -0
- package/dist/backend/backend/src/services/wiki/wiki-queue.service.js.map +1 -0
- package/dist/backend/backend/src/services/wiki/wiki.types.d.ts +84 -0
- package/dist/backend/backend/src/services/wiki/wiki.types.d.ts.map +1 -0
- package/dist/backend/backend/src/services/wiki/wiki.types.js +10 -0
- package/dist/backend/backend/src/services/wiki/wiki.types.js.map +1 -0
- package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts +20 -0
- package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/task-pool/task-pool.service.js +61 -0
- package/dist/cli/backend/src/services/task-pool/task-pool.service.js.map +1 -1
- package/frontend/dist/assets/{index-b279da34.js → index-cc115bb4.js} +246 -246
- package/frontend/dist/assets/{index-c07e04c0.css → index-db3f5041.css} +1 -1
- package/frontend/dist/index.html +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WikiIngestService — write canonical pages into a vault's `llm-curated/`.
|
|
3
|
+
*
|
|
4
|
+
* Phase A scope per v2.1 spec: append entries to `llm-curated/log.md` for
|
|
5
|
+
* every chat/spec/learning source. Decisions/pattern pages (separate files
|
|
6
|
+
* under `llm-curated/decisions/`) are gated behind the LLM routing layer
|
|
7
|
+
* which lands in Phase B.
|
|
8
|
+
*
|
|
9
|
+
* Frozen-path contract (§2): refuses ANY write whose target lives under a
|
|
10
|
+
* `hardcoded:` folder. SchemaLoaderService.isFrozenPath() is the gate.
|
|
11
|
+
*
|
|
12
|
+
* @module services/wiki/wiki-ingest.service
|
|
13
|
+
*/
|
|
14
|
+
import * as path from 'path';
|
|
15
|
+
import * as fs from 'fs/promises';
|
|
16
|
+
import { existsSync } from 'fs';
|
|
17
|
+
import { LoggerService } from '../core/logger.service.js';
|
|
18
|
+
import { SchemaLoaderService } from './schema-loader.service.js';
|
|
19
|
+
const DEFAULT_LOG_RELATIVE_PATH = 'llm-curated/log.md';
|
|
20
|
+
const MAX_BODY_BYTES = 64 * 1024;
|
|
21
|
+
// Spec note (Steve, 2026-05-22): the earlier keyword-based
|
|
22
|
+
// `detectMessageShape` heuristic + `buildDecisionSlug` + `ingestDecision`
|
|
23
|
+
// dual-write were REMOVED. Routing into `llm-curated/<folder>/<page>.md`
|
|
24
|
+
// is now agent-driven via `wiki-process-queue` + the agent's own LLM gate
|
|
25
|
+
// — see WikiQueueService for the queue + the orchestrator system prompt
|
|
26
|
+
// for the rule that says "queue worth-saving content as you see it." This
|
|
27
|
+
// service stays low-level: it only writes the path the caller (skill,
|
|
28
|
+
// route, queue processor) specifies, after the frozen-path gate.
|
|
29
|
+
/**
|
|
30
|
+
* Writes ingest pages to a vault. Construct one per process; stateless.
|
|
31
|
+
*/
|
|
32
|
+
export class WikiIngestService {
|
|
33
|
+
static instance = null;
|
|
34
|
+
logger;
|
|
35
|
+
schemaLoader;
|
|
36
|
+
constructor(schemaLoader) {
|
|
37
|
+
this.logger = LoggerService.getInstance().createComponentLogger('WikiIngest');
|
|
38
|
+
this.schemaLoader = schemaLoader ?? new SchemaLoaderService();
|
|
39
|
+
}
|
|
40
|
+
static getInstance() {
|
|
41
|
+
if (!this.instance) {
|
|
42
|
+
this.instance = new WikiIngestService();
|
|
43
|
+
}
|
|
44
|
+
return this.instance;
|
|
45
|
+
}
|
|
46
|
+
/** Test-only reset. */
|
|
47
|
+
static _resetForTesting() {
|
|
48
|
+
this.instance = null;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Ingest a single source into the vault.
|
|
52
|
+
*
|
|
53
|
+
* Errors are returned as structured outcomes — this method never throws
|
|
54
|
+
* on legitimate refusals (frozen path, missing schema, empty body) so
|
|
55
|
+
* callers can fold the result into their normal control flow.
|
|
56
|
+
*/
|
|
57
|
+
async ingest(input) {
|
|
58
|
+
const validation = this.validateInput(input);
|
|
59
|
+
if (validation) {
|
|
60
|
+
return validation;
|
|
61
|
+
}
|
|
62
|
+
let schema;
|
|
63
|
+
try {
|
|
64
|
+
schema = await this.schemaLoader.load(input.vaultPath);
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
return {
|
|
68
|
+
ok: false,
|
|
69
|
+
reason: 'schema_missing',
|
|
70
|
+
message: err.message,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
const target = input.targetRelativePath ?? DEFAULT_LOG_RELATIVE_PATH;
|
|
74
|
+
if (this.schemaLoader.isFrozenPath(schema, target)) {
|
|
75
|
+
return {
|
|
76
|
+
ok: false,
|
|
77
|
+
reason: 'frozen_path',
|
|
78
|
+
attemptedPath: target,
|
|
79
|
+
frozenFolders: this.schemaLoader.getFrozenPaths(schema),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
const absoluteTarget = path.join(input.vaultPath, target);
|
|
83
|
+
await fs.mkdir(path.dirname(absoluteTarget), { recursive: true });
|
|
84
|
+
const logEntry = this.formatLogEntry(input);
|
|
85
|
+
await this.appendOrCreate(absoluteTarget, logEntry, input);
|
|
86
|
+
this.logger.info('WikiIngest wrote log entry', {
|
|
87
|
+
vault: input.vaultPath,
|
|
88
|
+
target,
|
|
89
|
+
sourceType: input.sourceType,
|
|
90
|
+
sourceRef: input.sourceRef,
|
|
91
|
+
bodyBytes: input.sourceBody.length,
|
|
92
|
+
});
|
|
93
|
+
return {
|
|
94
|
+
ok: true,
|
|
95
|
+
pagesWritten: [target],
|
|
96
|
+
logEntry,
|
|
97
|
+
frozenPathsTouched: [],
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
// Internals
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
103
|
+
validateInput(input) {
|
|
104
|
+
if (!input.vaultPath || !path.isAbsolute(input.vaultPath)) {
|
|
105
|
+
return {
|
|
106
|
+
ok: false,
|
|
107
|
+
reason: 'invalid_input',
|
|
108
|
+
message: `vaultPath must be an absolute path, got "${input.vaultPath ?? ''}"`,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
if (!input.sourceType) {
|
|
112
|
+
return { ok: false, reason: 'invalid_input', message: 'sourceType is required' };
|
|
113
|
+
}
|
|
114
|
+
if (!input.sourceRef) {
|
|
115
|
+
return { ok: false, reason: 'invalid_input', message: 'sourceRef is required' };
|
|
116
|
+
}
|
|
117
|
+
const body = input.sourceBody ?? '';
|
|
118
|
+
if (body.trim().length === 0) {
|
|
119
|
+
return {
|
|
120
|
+
ok: false,
|
|
121
|
+
reason: 'empty_body',
|
|
122
|
+
message: 'sourceBody is empty after trimming whitespace',
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
if (Buffer.byteLength(body, 'utf8') > MAX_BODY_BYTES) {
|
|
126
|
+
return {
|
|
127
|
+
ok: false,
|
|
128
|
+
reason: 'invalid_input',
|
|
129
|
+
message: `sourceBody exceeds ${MAX_BODY_BYTES} bytes`,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Build the markdown entry. Format is chosen to be safely append-only
|
|
136
|
+
* (no preceding section to rewrite) and grep-friendly:
|
|
137
|
+
*
|
|
138
|
+
* ## [<ISO>] <sourceType> | <callerSession or sourceRef>
|
|
139
|
+
*
|
|
140
|
+
* ref: <sourceRef>
|
|
141
|
+
*
|
|
142
|
+
* <body>
|
|
143
|
+
*/
|
|
144
|
+
formatLogEntry(input) {
|
|
145
|
+
const ts = new Date().toISOString();
|
|
146
|
+
const header = this.sanitizeOneLine(input.callerSession ?? input.sourceRef, 80);
|
|
147
|
+
const body = this.sanitizeBody(input.sourceBody);
|
|
148
|
+
const ref = this.sanitizeOneLine(input.sourceRef, 200);
|
|
149
|
+
return [
|
|
150
|
+
'',
|
|
151
|
+
`## [${ts}] ${input.sourceType} | ${header}`,
|
|
152
|
+
'',
|
|
153
|
+
`ref: ${ref}`,
|
|
154
|
+
'',
|
|
155
|
+
body,
|
|
156
|
+
'',
|
|
157
|
+
].join('\n');
|
|
158
|
+
}
|
|
159
|
+
async appendOrCreate(absolutePath, entry, input) {
|
|
160
|
+
if (!existsSync(absolutePath)) {
|
|
161
|
+
const header = this.buildPageHeader(absolutePath, input);
|
|
162
|
+
await fs.writeFile(absolutePath, header + entry, 'utf8');
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
await fs.appendFile(absolutePath, entry, 'utf8');
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Pick the right header for a freshly-created page:
|
|
169
|
+
* - `log.md` → audit-log preamble (append-only)
|
|
170
|
+
* - `decisions/*.md` → decision-page title + provenance block
|
|
171
|
+
* - anything else → minimal title block from the source ref
|
|
172
|
+
*/
|
|
173
|
+
buildPageHeader(absolutePath, input) {
|
|
174
|
+
const basename = absolutePath.split(/[/\\]/).pop() ?? '';
|
|
175
|
+
if (basename === 'log.md') {
|
|
176
|
+
return '# Activity log\n\nAppend-only log of ingested sources. Each entry: `## [<ISO>] <sourceType> | <caller>`.\n';
|
|
177
|
+
}
|
|
178
|
+
if (absolutePath.includes('/decisions/')) {
|
|
179
|
+
const title = this.sanitizeOneLine(input.sourceBody, 80);
|
|
180
|
+
const caller = input.callerSession ?? input.sourceRef;
|
|
181
|
+
return [
|
|
182
|
+
`# ${title}`,
|
|
183
|
+
'',
|
|
184
|
+
`> **source:** \`${this.sanitizeOneLine(input.sourceRef, 200)}\` `,
|
|
185
|
+
`> **caller:** \`${this.sanitizeOneLine(caller, 80)}\` `,
|
|
186
|
+
`> **recorded:** ${new Date().toISOString()}`,
|
|
187
|
+
'',
|
|
188
|
+
'---',
|
|
189
|
+
'',
|
|
190
|
+
].join('\n');
|
|
191
|
+
}
|
|
192
|
+
// Generic non-log target: minimal title block.
|
|
193
|
+
return `# ${this.sanitizeOneLine(input.sourceRef, 80)}\n\n`;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Defuse markers a future skill might mis-route on: `[CHAT]`, `[NOTIFY]`,
|
|
197
|
+
* `[EVENT]`, `[ESCALATION]` get a zero-width space inserted so they're
|
|
198
|
+
* still human-readable but don't trigger regex matchers downstream.
|
|
199
|
+
* (Mirrors the escalation-router sanitizer from PR #606.)
|
|
200
|
+
*/
|
|
201
|
+
sanitizeBody(body) {
|
|
202
|
+
const trimmed = body.replace(/\r\n/g, '\n').trim();
|
|
203
|
+
return trimmed.replace(/\[(CHAT|NOTIFY|EVENT|ESCALATION)\]/g, (_match, tag) => `[${tag}]`);
|
|
204
|
+
}
|
|
205
|
+
sanitizeOneLine(value, maxLen) {
|
|
206
|
+
const flat = value.replace(/[\r\n]+/g, ' ').replace(/\s+/g, ' ').trim();
|
|
207
|
+
if (flat.length <= maxLen)
|
|
208
|
+
return flat;
|
|
209
|
+
return flat.slice(0, maxLen - 1) + '…';
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=wiki-ingest.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wiki-ingest.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/wiki/wiki-ingest.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
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 { WikiQueueService, WikiQueueItem } from './wiki-queue.service.js';
|
|
23
|
+
import { WikiQueryService, WikiQuerySystemContext } from './wiki-query.service.js';
|
|
24
|
+
export interface WikiProcessClaimInput {
|
|
25
|
+
/** Agent session claiming the item. */
|
|
26
|
+
claimedBy: string;
|
|
27
|
+
/** Filter the queue to a specific vault (default: any). */
|
|
28
|
+
vaultPath?: string;
|
|
29
|
+
/** Skip this many leading pending items. Lets the agent re-roll after a skip. */
|
|
30
|
+
offset?: number;
|
|
31
|
+
/** For the vault context: how many candidate pages to include. */
|
|
32
|
+
topK?: number;
|
|
33
|
+
/** For the vault context: how many recent log entries. */
|
|
34
|
+
recentLogEntries?: number;
|
|
35
|
+
}
|
|
36
|
+
export interface WikiProcessClaimResult {
|
|
37
|
+
/** The claimed queue item. */
|
|
38
|
+
item: WikiQueueItem;
|
|
39
|
+
/**
|
|
40
|
+
* System-context the agent's LLM should classify against. Includes the
|
|
41
|
+
* vault SCHEMA, frozen-path list, recent log, and top-K candidate pages
|
|
42
|
+
* by keyword overlap with the queue item's content.
|
|
43
|
+
*
|
|
44
|
+
* `null` if the vault has no SCHEMA.md (rare — vault wasn't initialized).
|
|
45
|
+
*/
|
|
46
|
+
vaultContext: WikiQuerySystemContext | null;
|
|
47
|
+
/** Synthesis instructions for the agent's LLM. */
|
|
48
|
+
classifierNotes: string[];
|
|
49
|
+
}
|
|
50
|
+
export type WikiProcessClaimOutcome = {
|
|
51
|
+
ok: true;
|
|
52
|
+
result: WikiProcessClaimResult;
|
|
53
|
+
} | {
|
|
54
|
+
ok: false;
|
|
55
|
+
reason: 'no_pending_items' | 'vault_query_failed';
|
|
56
|
+
message: string;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Stateless façade — both backing services (queue, query) are singletons.
|
|
60
|
+
*/
|
|
61
|
+
export declare class WikiProcessService {
|
|
62
|
+
private readonly queue;
|
|
63
|
+
private readonly query;
|
|
64
|
+
private static instance;
|
|
65
|
+
private readonly logger;
|
|
66
|
+
constructor(queue?: WikiQueueService, query?: WikiQueryService);
|
|
67
|
+
static getInstance(): WikiProcessService;
|
|
68
|
+
/** Test-only reset. */
|
|
69
|
+
static _resetForTesting(): void;
|
|
70
|
+
/**
|
|
71
|
+
* Claim the next pending item + return the vault context the agent
|
|
72
|
+
* needs to classify it. The caller (agent runtime) then runs an LLM
|
|
73
|
+
* call against this context + the item's content/reason, picks a
|
|
74
|
+
* target page, and calls `wiki-ingest` + `/queue/:id/process` to
|
|
75
|
+
* commit.
|
|
76
|
+
*/
|
|
77
|
+
claimNext(input: WikiProcessClaimInput): Promise<WikiProcessClaimOutcome>;
|
|
78
|
+
/**
|
|
79
|
+
* The contract the agent's LLM must honor when classifying. Kept
|
|
80
|
+
* short so it can be inlined into the runtime's task-instruction.
|
|
81
|
+
*/
|
|
82
|
+
private classifierNotes;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=wiki-process.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wiki-process.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/wiki/wiki-process.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC1E,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACvB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,qBAAqB;IACpC,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,sBAAsB;IACrC,8BAA8B;IAC9B,IAAI,EAAE,aAAa,CAAC;IACpB;;;;;;OAMG;IACH,YAAY,EAAE,sBAAsB,GAAG,IAAI,CAAC;IAC5C,kDAAkD;IAClD,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,MAAM,uBAAuB,GAC/B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,sBAAsB,CAAA;CAAE,GAC5C;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,kBAAkB,GAAG,oBAAoB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtF;;GAEG;AACH,qBAAa,kBAAkB;IAK3B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,KAAK;IALxB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAmC;IAC1D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;gBAGtB,KAAK,GAAE,gBAAiD,EACxD,KAAK,GAAE,gBAAiD;IAK3E,MAAM,CAAC,WAAW,IAAI,kBAAkB;IAKxC,uBAAuB;IACvB,MAAM,CAAC,gBAAgB,IAAI,IAAI;IAI/B;;;;;;OAMG;IACG,SAAS,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,uBAAuB,CAAC;IA2E/E;;;OAGG;IACH,OAAO,CAAC,eAAe;CAUxB"}
|
|
@@ -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"}
|