@cat-factory/agents 0.6.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/LICENSE +21 -0
- package/dist/agents/AiAgentExecutor.d.ts +72 -0
- package/dist/agents/AiAgentExecutor.d.ts.map +1 -0
- package/dist/agents/AiAgentExecutor.js +125 -0
- package/dist/agents/AiAgentExecutor.js.map +1 -0
- package/dist/agents/acceptance-prompts.d.ts +29 -0
- package/dist/agents/acceptance-prompts.d.ts.map +1 -0
- package/dist/agents/acceptance-prompts.js +112 -0
- package/dist/agents/acceptance-prompts.js.map +1 -0
- package/dist/agents/agent-catalog.d.ts +6 -0
- package/dist/agents/agent-catalog.d.ts.map +1 -0
- package/dist/agents/agent-catalog.js +197 -0
- package/dist/agents/agent-catalog.js.map +1 -0
- package/dist/agents/agent-configs.d.ts +17 -0
- package/dist/agents/agent-configs.d.ts.map +1 -0
- package/dist/agents/agent-configs.js +66 -0
- package/dist/agents/agent-configs.js.map +1 -0
- package/dist/agents/agent-routing.d.ts +57 -0
- package/dist/agents/agent-routing.d.ts.map +1 -0
- package/dist/agents/agent-routing.js +41 -0
- package/dist/agents/agent-routing.js.map +1 -0
- package/dist/agents/business-logic-prompts.d.ts +28 -0
- package/dist/agents/business-logic-prompts.d.ts.map +1 -0
- package/dist/agents/business-logic-prompts.js +96 -0
- package/dist/agents/business-logic-prompts.js.map +1 -0
- package/dist/agents/catalog.d.ts +6 -0
- package/dist/agents/catalog.d.ts.map +1 -0
- package/dist/agents/catalog.js +168 -0
- package/dist/agents/catalog.js.map +1 -0
- package/dist/agents/ci-gate.d.ts +2 -0
- package/dist/agents/ci-gate.d.ts.map +1 -0
- package/dist/agents/ci-gate.js +33 -0
- package/dist/agents/ci-gate.js.map +1 -0
- package/dist/agents/companion-prompts.d.ts +4 -0
- package/dist/agents/companion-prompts.d.ts.map +1 -0
- package/dist/agents/companion-prompts.js +27 -0
- package/dist/agents/companion-prompts.js.map +1 -0
- package/dist/agents/companions.d.ts +20 -0
- package/dist/agents/companions.d.ts.map +1 -0
- package/dist/agents/companions.js +38 -0
- package/dist/agents/companions.js.map +1 -0
- package/dist/agents/kinds/companions.d.ts +20 -0
- package/dist/agents/kinds/companions.d.ts.map +1 -0
- package/dist/agents/kinds/companions.js +39 -0
- package/dist/agents/kinds/companions.js.map +1 -0
- package/dist/agents/kinds/configs.d.ts +17 -0
- package/dist/agents/kinds/configs.d.ts.map +1 -0
- package/dist/agents/kinds/configs.js +66 -0
- package/dist/agents/kinds/configs.js.map +1 -0
- package/dist/agents/kinds/read-only.d.ts +13 -0
- package/dist/agents/kinds/read-only.d.ts.map +1 -0
- package/dist/agents/kinds/read-only.js +32 -0
- package/dist/agents/kinds/read-only.js.map +1 -0
- package/dist/agents/kinds/registry.d.ts +70 -0
- package/dist/agents/kinds/registry.d.ts.map +1 -0
- package/dist/agents/kinds/registry.js +51 -0
- package/dist/agents/kinds/registry.js.map +1 -0
- package/dist/agents/kinds/traits.d.ts +60 -0
- package/dist/agents/kinds/traits.d.ts.map +1 -0
- package/dist/agents/kinds/traits.js +123 -0
- package/dist/agents/kinds/traits.js.map +1 -0
- package/dist/agents/kinds/versions.d.ts +46 -0
- package/dist/agents/kinds/versions.d.ts.map +1 -0
- package/dist/agents/kinds/versions.js +22 -0
- package/dist/agents/kinds/versions.js.map +1 -0
- package/dist/agents/mock-prompts.d.ts +12 -0
- package/dist/agents/mock-prompts.d.ts.map +1 -0
- package/dist/agents/mock-prompts.js +61 -0
- package/dist/agents/mock-prompts.js.map +1 -0
- package/dist/agents/prompt-fragments.d.ts +17 -0
- package/dist/agents/prompt-fragments.d.ts.map +1 -0
- package/dist/agents/prompt-fragments.js +33 -0
- package/dist/agents/prompt-fragments.js.map +1 -0
- package/dist/agents/prompt-shared.d.ts +7 -0
- package/dist/agents/prompt-shared.d.ts.map +1 -0
- package/dist/agents/prompt-shared.js +10 -0
- package/dist/agents/prompt-shared.js.map +1 -0
- package/dist/agents/prompt-versions.d.ts +48 -0
- package/dist/agents/prompt-versions.d.ts.map +1 -0
- package/dist/agents/prompt-versions.js +55 -0
- package/dist/agents/prompt-versions.js.map +1 -0
- package/dist/agents/prompts/acceptance.d.ts +29 -0
- package/dist/agents/prompts/acceptance.d.ts.map +1 -0
- package/dist/agents/prompts/acceptance.js +112 -0
- package/dist/agents/prompts/acceptance.js.map +1 -0
- package/dist/agents/prompts/business-logic.d.ts +28 -0
- package/dist/agents/prompts/business-logic.d.ts.map +1 -0
- package/dist/agents/prompts/business-logic.js +98 -0
- package/dist/agents/prompts/business-logic.js.map +1 -0
- package/dist/agents/prompts/clarity.d.ts +10 -0
- package/dist/agents/prompts/clarity.d.ts.map +1 -0
- package/dist/agents/prompts/clarity.js +40 -0
- package/dist/agents/prompts/clarity.js.map +1 -0
- package/dist/agents/prompts/companion.d.ts +4 -0
- package/dist/agents/prompts/companion.d.ts.map +1 -0
- package/dist/agents/prompts/companion.js +61 -0
- package/dist/agents/prompts/companion.js.map +1 -0
- package/dist/agents/prompts/delivery-contract.d.ts +2 -0
- package/dist/agents/prompts/delivery-contract.d.ts.map +1 -0
- package/dist/agents/prompts/delivery-contract.js +33 -0
- package/dist/agents/prompts/delivery-contract.js.map +1 -0
- package/dist/agents/prompts/mock.d.ts +12 -0
- package/dist/agents/prompts/mock.d.ts.map +1 -0
- package/dist/agents/prompts/mock.js +61 -0
- package/dist/agents/prompts/mock.js.map +1 -0
- package/dist/agents/prompts/requirements.d.ts +13 -0
- package/dist/agents/prompts/requirements.d.ts.map +1 -0
- package/dist/agents/prompts/requirements.js +45 -0
- package/dist/agents/prompts/requirements.js.map +1 -0
- package/dist/agents/prompts/roles.d.ts +16 -0
- package/dist/agents/prompts/roles.d.ts.map +1 -0
- package/dist/agents/prompts/roles.js +74 -0
- package/dist/agents/prompts/roles.js.map +1 -0
- package/dist/agents/prompts/shared.d.ts +19 -0
- package/dist/agents/prompts/shared.d.ts.map +1 -0
- package/dist/agents/prompts/shared.js +25 -0
- package/dist/agents/prompts/shared.js.map +1 -0
- package/dist/agents/prompts/standard-templates.generated.d.ts +36 -0
- package/dist/agents/prompts/standard-templates.generated.d.ts.map +1 -0
- package/dist/agents/prompts/standard-templates.generated.js +122 -0
- package/dist/agents/prompts/standard-templates.generated.js.map +1 -0
- package/dist/agents/prompts/standard.d.ts +36 -0
- package/dist/agents/prompts/standard.d.ts.map +1 -0
- package/dist/agents/prompts/standard.js +208 -0
- package/dist/agents/prompts/standard.js.map +1 -0
- package/dist/agents/prompts/testing.d.ts +12 -0
- package/dist/agents/prompts/testing.d.ts.map +1 -0
- package/dist/agents/prompts/testing.js +94 -0
- package/dist/agents/prompts/testing.js.map +1 -0
- package/dist/agents/read-only.d.ts +13 -0
- package/dist/agents/read-only.d.ts.map +1 -0
- package/dist/agents/read-only.js +29 -0
- package/dist/agents/read-only.js.map +1 -0
- package/dist/agents/registry.d.ts +70 -0
- package/dist/agents/registry.d.ts.map +1 -0
- package/dist/agents/registry.js +51 -0
- package/dist/agents/registry.js.map +1 -0
- package/dist/agents/runtime/executor.d.ts +72 -0
- package/dist/agents/runtime/executor.d.ts.map +1 -0
- package/dist/agents/runtime/executor.js +125 -0
- package/dist/agents/runtime/executor.js.map +1 -0
- package/dist/agents/runtime/fragments.d.ts +17 -0
- package/dist/agents/runtime/fragments.d.ts.map +1 -0
- package/dist/agents/runtime/fragments.js +33 -0
- package/dist/agents/runtime/fragments.js.map +1 -0
- package/dist/agents/runtime/routing.d.ts +57 -0
- package/dist/agents/runtime/routing.d.ts.map +1 -0
- package/dist/agents/runtime/routing.js +41 -0
- package/dist/agents/runtime/routing.js.map +1 -0
- package/dist/agents/runtime/web-search.d.ts +43 -0
- package/dist/agents/runtime/web-search.d.ts.map +1 -0
- package/dist/agents/runtime/web-search.js +102 -0
- package/dist/agents/runtime/web-search.js.map +1 -0
- package/dist/agents/standard-prompt-templates.generated.d.ts +36 -0
- package/dist/agents/standard-prompt-templates.generated.d.ts.map +1 -0
- package/dist/agents/standard-prompt-templates.generated.js +122 -0
- package/dist/agents/standard-prompt-templates.generated.js.map +1 -0
- package/dist/agents/standard-prompts.d.ts +36 -0
- package/dist/agents/standard-prompts.d.ts.map +1 -0
- package/dist/agents/standard-prompts.js +202 -0
- package/dist/agents/standard-prompts.js.map +1 -0
- package/dist/agents/test-prompts.d.ts +12 -0
- package/dist/agents/test-prompts.d.ts.map +1 -0
- package/dist/agents/test-prompts.js +92 -0
- package/dist/agents/test-prompts.js.map +1 -0
- package/dist/agents/traits.d.ts +56 -0
- package/dist/agents/traits.d.ts.map +1 -0
- package/dist/agents/traits.js +100 -0
- package/dist/agents/traits.js.map +1 -0
- package/dist/agents/web-search.d.ts +43 -0
- package/dist/agents/web-search.d.ts.map +1 -0
- package/dist/agents/web-search.js +102 -0
- package/dist/agents/web-search.js.map +1 -0
- package/dist/fragmentLibrary/DeterministicFragmentSelector.d.ts +10 -0
- package/dist/fragmentLibrary/DeterministicFragmentSelector.d.ts.map +1 -0
- package/dist/fragmentLibrary/DeterministicFragmentSelector.js +12 -0
- package/dist/fragmentLibrary/DeterministicFragmentSelector.js.map +1 -0
- package/dist/fragmentLibrary/FragmentLibraryService.d.ts +55 -0
- package/dist/fragmentLibrary/FragmentLibraryService.d.ts.map +1 -0
- package/dist/fragmentLibrary/FragmentLibraryService.js +203 -0
- package/dist/fragmentLibrary/FragmentLibraryService.js.map +1 -0
- package/dist/fragmentLibrary/FragmentSourceService.d.ts +51 -0
- package/dist/fragmentLibrary/FragmentSourceService.d.ts.map +1 -0
- package/dist/fragmentLibrary/FragmentSourceService.js +181 -0
- package/dist/fragmentLibrary/FragmentSourceService.js.map +1 -0
- package/dist/fragmentLibrary/LlmFragmentSelector.d.ts +21 -0
- package/dist/fragmentLibrary/LlmFragmentSelector.d.ts.map +1 -0
- package/dist/fragmentLibrary/LlmFragmentSelector.js +87 -0
- package/dist/fragmentLibrary/LlmFragmentSelector.js.map +1 -0
- package/dist/fragmentLibrary/fragment-catalog.d.ts +43 -0
- package/dist/fragmentLibrary/fragment-catalog.d.ts.map +1 -0
- package/dist/fragmentLibrary/fragment-catalog.js +129 -0
- package/dist/fragmentLibrary/fragment-catalog.js.map +1 -0
- package/dist/fragmentLibrary/fragment-source.logic.d.ts +34 -0
- package/dist/fragmentLibrary/fragment-source.logic.d.ts.map +1 -0
- package/dist/fragmentLibrary/fragment-source.logic.js +172 -0
- package/dist/fragmentLibrary/fragment-source.logic.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/providers/cache.d.ts +22 -0
- package/dist/providers/cache.d.ts.map +1 -0
- package/dist/providers/cache.js +64 -0
- package/dist/providers/cache.js.map +1 -0
- package/dist/providers/endpoints.d.ts +8 -0
- package/dist/providers/endpoints.d.ts.map +1 -0
- package/dist/providers/endpoints.js +23 -0
- package/dist/providers/endpoints.js.map +1 -0
- package/dist/providers/index.d.ts +6 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +6 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/instrumented.d.ts +28 -0
- package/dist/providers/instrumented.d.ts.map +1 -0
- package/dist/providers/instrumented.js +147 -0
- package/dist/providers/instrumented.js.map +1 -0
- package/dist/providers/registry.d.ts +26 -0
- package/dist/providers/registry.d.ts.map +1 -0
- package/dist/providers/registry.js +32 -0
- package/dist/providers/registry.js.map +1 -0
- package/dist/providers/resolvers.d.ts +58 -0
- package/dist/providers/resolvers.d.ts.map +1 -0
- package/dist/providers/resolvers.js +78 -0
- package/dist/providers/resolvers.js.map +1 -0
- package/package.json +39 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { FRAGMENTS } from '@cat-factory/prompt-fragments';
|
|
2
|
+
import { ValidationError } from '@cat-factory/kernel';
|
|
3
|
+
import { DeterministicFragmentSelector } from './DeterministicFragmentSelector.js';
|
|
4
|
+
import { entryToFragment, mergeCatalog, toSelectable, } from './fragment-catalog.js';
|
|
5
|
+
import { slugFromPath } from './fragment-source.logic.js';
|
|
6
|
+
/**
|
|
7
|
+
* The fragment library's management service (ADR 0006). It owns the per-tier CRUD
|
|
8
|
+
* (account / workspace) and resolves the merged catalog a workspace sees (built-in ∪
|
|
9
|
+
* account ∪ workspace, override-by-id, tombstone-suppressed). It still implements
|
|
10
|
+
* {@link FragmentResolver} (`resolveForRun`), but the execution engine NO LONGER
|
|
11
|
+
* consumes it: fragment delivery is now the service-scoped `serviceFragmentIds` folded
|
|
12
|
+
* into `code-aware` agents, so this is a management surface only.
|
|
13
|
+
*/
|
|
14
|
+
export class FragmentLibraryService {
|
|
15
|
+
repo;
|
|
16
|
+
workspaces;
|
|
17
|
+
clock;
|
|
18
|
+
selector;
|
|
19
|
+
builtins;
|
|
20
|
+
constructor(deps) {
|
|
21
|
+
this.repo = deps.promptFragmentRepository;
|
|
22
|
+
this.workspaces = deps.workspaceRepository;
|
|
23
|
+
this.clock = deps.clock;
|
|
24
|
+
this.selector = deps.selector ?? new DeterministicFragmentSelector();
|
|
25
|
+
this.builtins = deps.builtins ?? FRAGMENTS;
|
|
26
|
+
}
|
|
27
|
+
/** This tier's hand-authored/sourced fragments (raw, not merged), newest first. */
|
|
28
|
+
async listTier(ownerKind, ownerId) {
|
|
29
|
+
const rows = await this.repo.listByOwner(ownerKind, ownerId);
|
|
30
|
+
return rows.sort((a, b) => b.updatedAt - a.updatedAt).map((row) => recordToWire(row));
|
|
31
|
+
}
|
|
32
|
+
/** Create a hand-authored fragment at a tier. */
|
|
33
|
+
async create(ownerKind, ownerId, input) {
|
|
34
|
+
const fragmentId = (input.id ?? slugFromPath(input.title)).trim();
|
|
35
|
+
if (!fragmentId)
|
|
36
|
+
throw new ValidationError('A fragment id (or a non-empty title) is required');
|
|
37
|
+
const now = this.clock.now();
|
|
38
|
+
const record = {
|
|
39
|
+
fragmentId,
|
|
40
|
+
ownerKind,
|
|
41
|
+
ownerId,
|
|
42
|
+
version: input.version ?? '1.0.0',
|
|
43
|
+
title: input.title.trim(),
|
|
44
|
+
category: input.category?.trim() || null,
|
|
45
|
+
summary: input.summary.trim(),
|
|
46
|
+
body: input.body.trim(),
|
|
47
|
+
appliesTo: input.appliesTo ?? null,
|
|
48
|
+
tags: input.tags && input.tags.length ? input.tags : null,
|
|
49
|
+
sourceId: null,
|
|
50
|
+
sourcePath: null,
|
|
51
|
+
sourceSha: null,
|
|
52
|
+
createdAt: now,
|
|
53
|
+
updatedAt: now,
|
|
54
|
+
deletedAt: null,
|
|
55
|
+
};
|
|
56
|
+
await this.repo.upsert(record);
|
|
57
|
+
return recordToWire(record);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Edit a fragment at a tier. If no row exists yet (e.g. shadowing a built-in or
|
|
61
|
+
* inherited account fragment), the patch must carry enough to stand alone
|
|
62
|
+
* (title, summary, body); otherwise it merges over the existing record.
|
|
63
|
+
*/
|
|
64
|
+
async update(ownerKind, ownerId, fragmentId, patch) {
|
|
65
|
+
const existing = await this.repo.get(ownerKind, ownerId, fragmentId);
|
|
66
|
+
const now = this.clock.now();
|
|
67
|
+
const base = existing ?? {
|
|
68
|
+
fragmentId,
|
|
69
|
+
ownerKind,
|
|
70
|
+
ownerId,
|
|
71
|
+
version: '1.0.0',
|
|
72
|
+
title: fragmentId,
|
|
73
|
+
category: null,
|
|
74
|
+
summary: '',
|
|
75
|
+
body: '',
|
|
76
|
+
appliesTo: null,
|
|
77
|
+
tags: null,
|
|
78
|
+
sourceId: null,
|
|
79
|
+
sourcePath: null,
|
|
80
|
+
sourceSha: null,
|
|
81
|
+
createdAt: now,
|
|
82
|
+
updatedAt: now,
|
|
83
|
+
deletedAt: null,
|
|
84
|
+
};
|
|
85
|
+
const next = {
|
|
86
|
+
...base,
|
|
87
|
+
version: patch.version?.trim() ?? base.version,
|
|
88
|
+
title: patch.title?.trim() ?? base.title,
|
|
89
|
+
category: patch.category !== undefined ? patch.category.trim() || null : base.category,
|
|
90
|
+
summary: patch.summary?.trim() ?? base.summary,
|
|
91
|
+
body: patch.body?.trim() ?? base.body,
|
|
92
|
+
appliesTo: patch.appliesTo !== undefined ? patch.appliesTo : base.appliesTo,
|
|
93
|
+
tags: patch.tags !== undefined ? (patch.tags.length ? patch.tags : null) : base.tags,
|
|
94
|
+
updatedAt: now,
|
|
95
|
+
deletedAt: null, // editing un-suppresses
|
|
96
|
+
};
|
|
97
|
+
if (!next.summary || !next.body) {
|
|
98
|
+
throw new ValidationError('A fragment needs a summary and a body');
|
|
99
|
+
}
|
|
100
|
+
await this.repo.upsert(next);
|
|
101
|
+
return recordToWire(next);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Tombstone a fragment at a tier. Suppresses an inherited built-in/account
|
|
105
|
+
* fragment, or removes a hand-authored one. Idempotent — writing a tombstone
|
|
106
|
+
* even when no row exists yet, so a tier can suppress something it inherits.
|
|
107
|
+
*/
|
|
108
|
+
async remove(ownerKind, ownerId, fragmentId) {
|
|
109
|
+
const now = this.clock.now();
|
|
110
|
+
const existing = await this.repo.get(ownerKind, ownerId, fragmentId);
|
|
111
|
+
if (existing) {
|
|
112
|
+
await this.repo.softDelete(ownerKind, ownerId, fragmentId, now);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
await this.repo.upsert({
|
|
116
|
+
fragmentId,
|
|
117
|
+
ownerKind,
|
|
118
|
+
ownerId,
|
|
119
|
+
version: '1.0.0',
|
|
120
|
+
title: fragmentId,
|
|
121
|
+
category: null,
|
|
122
|
+
summary: '',
|
|
123
|
+
body: '',
|
|
124
|
+
appliesTo: null,
|
|
125
|
+
tags: null,
|
|
126
|
+
sourceId: null,
|
|
127
|
+
sourcePath: null,
|
|
128
|
+
sourceSha: null,
|
|
129
|
+
createdAt: now,
|
|
130
|
+
updatedAt: now,
|
|
131
|
+
deletedAt: now,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
/** Resolve the merged catalog a workspace sees (built-in ∪ account ∪ workspace). */
|
|
135
|
+
async resolveCatalog(workspaceId) {
|
|
136
|
+
const accountId = await this.workspaces.accountOf(workspaceId);
|
|
137
|
+
const [accountRows, workspaceRows] = await Promise.all([
|
|
138
|
+
accountId ? this.repo.listByOwner('account', accountId, true) : Promise.resolve([]),
|
|
139
|
+
this.repo.listByOwner('workspace', workspaceId, true),
|
|
140
|
+
]);
|
|
141
|
+
return mergeCatalog(this.builtins, accountRows, workspaceRows);
|
|
142
|
+
}
|
|
143
|
+
/** The merged catalog as wire {@link ResolvedFragment}s for the management UI. */
|
|
144
|
+
async resolvedCatalog(workspaceId) {
|
|
145
|
+
const entries = await this.resolveCatalog(workspaceId);
|
|
146
|
+
return entries.map((entry) => ({ ...entryToFragment(entry), tier: entry.tier }));
|
|
147
|
+
}
|
|
148
|
+
/** {@link FragmentResolver}: pick + resolve the fragments to inject for a run. */
|
|
149
|
+
async resolveForRun(input) {
|
|
150
|
+
const catalog = await this.resolveCatalog(input.workspaceId);
|
|
151
|
+
const byId = new Map(catalog.map((e) => [e.id, e]));
|
|
152
|
+
let picked = [];
|
|
153
|
+
try {
|
|
154
|
+
picked = await this.selector.select(catalog.map(toSelectable), {
|
|
155
|
+
workspaceId: input.workspaceId,
|
|
156
|
+
agentKind: input.agentKind,
|
|
157
|
+
blockType: input.blockType,
|
|
158
|
+
blockTitle: input.blockTitle,
|
|
159
|
+
blockDescription: input.blockDescription,
|
|
160
|
+
signals: input.signals,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
picked = []; // selection never blocks a run; manual pins still apply
|
|
165
|
+
}
|
|
166
|
+
// Manual pins are authoritative and always included; the selector's picks
|
|
167
|
+
// union with them. Both are validated against the catalog so a stale id is
|
|
168
|
+
// simply dropped rather than breaking the run.
|
|
169
|
+
const selectedIds = [];
|
|
170
|
+
const seen = new Set();
|
|
171
|
+
for (const id of [...input.manualIds, ...picked]) {
|
|
172
|
+
if (seen.has(id) || !byId.has(id))
|
|
173
|
+
continue;
|
|
174
|
+
seen.add(id);
|
|
175
|
+
selectedIds.push(id);
|
|
176
|
+
}
|
|
177
|
+
// Emit in stable catalog order for replay-stable prompts.
|
|
178
|
+
const ordered = catalog.filter((e) => seen.has(e.id));
|
|
179
|
+
return {
|
|
180
|
+
fragments: ordered.map((e) => ({ id: e.id, body: e.body })),
|
|
181
|
+
selectedIds: ordered.map((e) => e.id),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function recordToWire(record) {
|
|
186
|
+
const fragment = {
|
|
187
|
+
id: record.fragmentId,
|
|
188
|
+
version: record.version,
|
|
189
|
+
title: record.title,
|
|
190
|
+
category: record.category ?? '',
|
|
191
|
+
summary: record.summary,
|
|
192
|
+
body: record.body,
|
|
193
|
+
};
|
|
194
|
+
if (record.appliesTo)
|
|
195
|
+
fragment.appliesTo = record.appliesTo;
|
|
196
|
+
if (record.tags && record.tags.length)
|
|
197
|
+
fragment.tags = record.tags;
|
|
198
|
+
if (record.sourceId && record.sourcePath !== null && record.sourceSha !== null) {
|
|
199
|
+
fragment.source = { sourceId: record.sourceId, path: record.sourcePath, sha: record.sourceSha };
|
|
200
|
+
}
|
|
201
|
+
return fragment;
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=FragmentLibraryService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FragmentLibraryService.js","sourceRoot":"","sources":["../../src/fragmentLibrary/FragmentLibraryService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { FragmentOwnerKind, FragmentSource, FragmentSourceStatus, FragmentSyncResult, LinkFragmentSourceInput } from '@cat-factory/kernel';
|
|
2
|
+
import type { Clock, IdGenerator } from '@cat-factory/kernel';
|
|
3
|
+
import type { GitHubClient } from '@cat-factory/kernel';
|
|
4
|
+
import type { FragmentSourceRepository, PromptFragmentRepository } from '@cat-factory/kernel';
|
|
5
|
+
/**
|
|
6
|
+
* Resolve the GitHub App installation id that can read a tier's repos. A
|
|
7
|
+
* workspace-owned source reads through the workspace's installation; an
|
|
8
|
+
* account-owned one through the account's. Returns null when no installation is
|
|
9
|
+
* available, so a sync fails with a clear error rather than a silent empty pull.
|
|
10
|
+
*/
|
|
11
|
+
export type ResolveFragmentInstallationId = (ownerKind: FragmentOwnerKind, ownerId: string) => Promise<number | null>;
|
|
12
|
+
export interface FragmentSourceServiceDependencies {
|
|
13
|
+
fragmentSourceRepository: FragmentSourceRepository;
|
|
14
|
+
promptFragmentRepository: PromptFragmentRepository;
|
|
15
|
+
githubClient: GitHubClient;
|
|
16
|
+
resolveInstallationId: ResolveFragmentInstallationId;
|
|
17
|
+
idGenerator: IdGenerator;
|
|
18
|
+
clock: Clock;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Repo-sourced fragments (ADR 0006 §3, §6): link a repo directory of Markdown
|
|
22
|
+
* guidelines to a tier, resync it (read the tree, upsert changed files, tombstone
|
|
23
|
+
* removed ones), and answer the cheap "check for changes" without writing. Reads
|
|
24
|
+
* go through the account's existing GitHub installation — no new credential store.
|
|
25
|
+
* Sync runs inline (bounded directory); the sha-keyed upsert is idempotent so it
|
|
26
|
+
* can later move behind a Workflow safely.
|
|
27
|
+
*/
|
|
28
|
+
export declare class FragmentSourceService {
|
|
29
|
+
private readonly deps;
|
|
30
|
+
constructor(deps: FragmentSourceServiceDependencies);
|
|
31
|
+
/** Linked sources for a tier + their last-synced state. */
|
|
32
|
+
list(ownerKind: FragmentOwnerKind, ownerId: string): Promise<FragmentSource[]>;
|
|
33
|
+
/** Link a repo directory as a fragment source. Does not sync (call {@link sync}). */
|
|
34
|
+
link(ownerKind: FragmentOwnerKind, ownerId: string, input: LinkFragmentSourceInput): Promise<FragmentSource>;
|
|
35
|
+
/** Unlink a source and tombstone every fragment it produced. */
|
|
36
|
+
unlink(sourceId: string): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Resync a source: list the directory, upsert every Markdown file whose blob
|
|
39
|
+
* sha changed, tombstone files removed upstream, and stamp the new tree digest.
|
|
40
|
+
* Idempotent — re-running with no upstream change touches nothing.
|
|
41
|
+
*/
|
|
42
|
+
sync(sourceId: string): Promise<FragmentSyncResult>;
|
|
43
|
+
/** Cheap "check for changes": compare the remote tree digest to the stored one. */
|
|
44
|
+
status(sourceId: string): Promise<FragmentSourceStatus>;
|
|
45
|
+
private require;
|
|
46
|
+
/** List the source directory and keep only Markdown files (with their shas). */
|
|
47
|
+
private readMarkdown;
|
|
48
|
+
/** Read, parse and upsert one file as a fragment owned by the source's tier. */
|
|
49
|
+
private syncEntry;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=FragmentSourceService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FragmentSourceService.d.ts","sourceRoot":"","sources":["../../src/fragmentLibrary/FragmentSourceService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,cAAc,EACd,oBAAoB,EACpB,kBAAkB,EAClB,uBAAuB,EACxB,MAAM,qBAAqB,CAAA;AAE5B,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAC7D,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,qBAAqB,CAAA;AACzE,OAAO,KAAK,EAEV,wBAAwB,EAExB,wBAAwB,EACzB,MAAM,qBAAqB,CAAA;AAQ5B;;;;;GAKG;AACH,MAAM,MAAM,6BAA6B,GAAG,CAC1C,SAAS,EAAE,iBAAiB,EAC5B,OAAO,EAAE,MAAM,KACZ,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;AAE3B,MAAM,WAAW,iCAAiC;IAChD,wBAAwB,EAAE,wBAAwB,CAAA;IAClD,wBAAwB,EAAE,wBAAwB,CAAA;IAClD,YAAY,EAAE,YAAY,CAAA;IAC1B,qBAAqB,EAAE,6BAA6B,CAAA;IACpD,WAAW,EAAE,WAAW,CAAA;IACxB,KAAK,EAAE,KAAK,CAAA;CACb;AAED;;;;;;;GAOG;AACH,qBAAa,qBAAqB;IACpB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAAjC,YAA6B,IAAI,EAAE,iCAAiC,EAAI;IAExE,2DAA2D;IACrD,IAAI,CAAC,SAAS,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAGnF;IAED,qFAAqF;IAC/E,IAAI,CACR,SAAS,EAAE,iBAAiB,EAC5B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,uBAAuB,GAC7B,OAAO,CAAC,cAAc,CAAC,CAiBzB;IAED,gEAAgE;IAC1D,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ5C;IAED;;;;OAIG;IACG,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAuCxD;IAED,mFAAmF;IAC7E,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAwB5D;YAIa,OAAO;IAUrB,gFAAgF;YAClE,YAAY;IAgB1B,gFAAgF;YAClE,SAAS;CA2CxB"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { NotFoundError, ValidationError, assertFound } from '@cat-factory/kernel';
|
|
2
|
+
import { digestListing, isMarkdownFile, parseFragmentMarkdown, slugFromPath, } from './fragment-source.logic.js';
|
|
3
|
+
/**
|
|
4
|
+
* Repo-sourced fragments (ADR 0006 §3, §6): link a repo directory of Markdown
|
|
5
|
+
* guidelines to a tier, resync it (read the tree, upsert changed files, tombstone
|
|
6
|
+
* removed ones), and answer the cheap "check for changes" without writing. Reads
|
|
7
|
+
* go through the account's existing GitHub installation — no new credential store.
|
|
8
|
+
* Sync runs inline (bounded directory); the sha-keyed upsert is idempotent so it
|
|
9
|
+
* can later move behind a Workflow safely.
|
|
10
|
+
*/
|
|
11
|
+
export class FragmentSourceService {
|
|
12
|
+
deps;
|
|
13
|
+
constructor(deps) {
|
|
14
|
+
this.deps = deps;
|
|
15
|
+
}
|
|
16
|
+
/** Linked sources for a tier + their last-synced state. */
|
|
17
|
+
async list(ownerKind, ownerId) {
|
|
18
|
+
const rows = await this.deps.fragmentSourceRepository.listByOwner(ownerKind, ownerId);
|
|
19
|
+
return rows.map(toWire);
|
|
20
|
+
}
|
|
21
|
+
/** Link a repo directory as a fragment source. Does not sync (call {@link sync}). */
|
|
22
|
+
async link(ownerKind, ownerId, input) {
|
|
23
|
+
const now = this.deps.clock.now();
|
|
24
|
+
const record = {
|
|
25
|
+
id: this.deps.idGenerator.next('frgsrc'),
|
|
26
|
+
ownerKind,
|
|
27
|
+
ownerId,
|
|
28
|
+
repoOwner: input.repoOwner.trim(),
|
|
29
|
+
repoName: input.repoName.trim(),
|
|
30
|
+
gitRef: input.gitRef?.trim() || 'HEAD',
|
|
31
|
+
dirPath: normalizeDir(input.dirPath),
|
|
32
|
+
lastSyncedSha: null,
|
|
33
|
+
lastSyncedAt: null,
|
|
34
|
+
createdAt: now,
|
|
35
|
+
deletedAt: null,
|
|
36
|
+
};
|
|
37
|
+
await this.deps.fragmentSourceRepository.upsert(record);
|
|
38
|
+
return toWire(record);
|
|
39
|
+
}
|
|
40
|
+
/** Unlink a source and tombstone every fragment it produced. */
|
|
41
|
+
async unlink(sourceId) {
|
|
42
|
+
const source = await this.require(sourceId);
|
|
43
|
+
const now = this.deps.clock.now();
|
|
44
|
+
const fragments = await this.deps.promptFragmentRepository.listBySource(sourceId);
|
|
45
|
+
for (const f of fragments) {
|
|
46
|
+
await this.deps.promptFragmentRepository.softDelete(f.ownerKind, f.ownerId, f.fragmentId, now);
|
|
47
|
+
}
|
|
48
|
+
await this.deps.fragmentSourceRepository.softDelete(source.id, now);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Resync a source: list the directory, upsert every Markdown file whose blob
|
|
52
|
+
* sha changed, tombstone files removed upstream, and stamp the new tree digest.
|
|
53
|
+
* Idempotent — re-running with no upstream change touches nothing.
|
|
54
|
+
*/
|
|
55
|
+
async sync(sourceId) {
|
|
56
|
+
const source = await this.require(sourceId);
|
|
57
|
+
const entries = await this.readMarkdown(source);
|
|
58
|
+
const existing = await this.deps.promptFragmentRepository.listBySource(sourceId);
|
|
59
|
+
const existingByPath = new Map(existing.map((f) => [f.sourcePath ?? '', f]));
|
|
60
|
+
const now = this.deps.clock.now();
|
|
61
|
+
let upserted = 0;
|
|
62
|
+
let unchanged = 0;
|
|
63
|
+
const seenPaths = new Set();
|
|
64
|
+
for (const entry of entries) {
|
|
65
|
+
seenPaths.add(entry.path);
|
|
66
|
+
const prior = existingByPath.get(entry.path);
|
|
67
|
+
if (prior && prior.sourceSha === entry.sha) {
|
|
68
|
+
unchanged++;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
await this.syncEntry(source, entry, prior, now);
|
|
72
|
+
upserted++;
|
|
73
|
+
}
|
|
74
|
+
// Tombstone fragments whose source file disappeared upstream.
|
|
75
|
+
let tombstoned = 0;
|
|
76
|
+
for (const f of existing) {
|
|
77
|
+
if (f.sourcePath && !seenPaths.has(f.sourcePath)) {
|
|
78
|
+
await this.deps.promptFragmentRepository.softDelete(f.ownerKind, f.ownerId, f.fragmentId, now);
|
|
79
|
+
tombstoned++;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const lastSyncedSha = digestListing(entries.map((e) => ({ path: e.path, sha: e.sha })));
|
|
83
|
+
await this.deps.fragmentSourceRepository.updateSyncState(source.id, lastSyncedSha, now);
|
|
84
|
+
return { upserted, tombstoned, unchanged, lastSyncedSha };
|
|
85
|
+
}
|
|
86
|
+
/** Cheap "check for changes": compare the remote tree digest to the stored one. */
|
|
87
|
+
async status(sourceId) {
|
|
88
|
+
const source = await this.require(sourceId);
|
|
89
|
+
const entries = await this.readMarkdown(source);
|
|
90
|
+
const existing = await this.deps.promptFragmentRepository.listBySource(sourceId);
|
|
91
|
+
const existingByPath = new Map(existing.map((f) => [f.sourcePath ?? '', f]));
|
|
92
|
+
let changedCount = 0;
|
|
93
|
+
const seen = new Set();
|
|
94
|
+
for (const entry of entries) {
|
|
95
|
+
seen.add(entry.path);
|
|
96
|
+
const prior = existingByPath.get(entry.path);
|
|
97
|
+
if (!prior || prior.sourceSha !== entry.sha)
|
|
98
|
+
changedCount++;
|
|
99
|
+
}
|
|
100
|
+
for (const f of existing) {
|
|
101
|
+
if (f.sourcePath && !seen.has(f.sourcePath))
|
|
102
|
+
changedCount++;
|
|
103
|
+
}
|
|
104
|
+
const remoteSha = digestListing(entries.map((e) => ({ path: e.path, sha: e.sha })));
|
|
105
|
+
return {
|
|
106
|
+
changed: remoteSha !== source.lastSyncedSha,
|
|
107
|
+
changedCount,
|
|
108
|
+
lastSyncedSha: source.lastSyncedSha,
|
|
109
|
+
remoteSha,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
// --- internals ----------------------------------------------------------
|
|
113
|
+
async require(sourceId) {
|
|
114
|
+
const source = assertFound(await this.deps.fragmentSourceRepository.get(sourceId), 'FragmentSource', sourceId);
|
|
115
|
+
if (source.deletedAt !== null)
|
|
116
|
+
throw new NotFoundError('FragmentSource', sourceId);
|
|
117
|
+
return source;
|
|
118
|
+
}
|
|
119
|
+
/** List the source directory and keep only Markdown files (with their shas). */
|
|
120
|
+
async readMarkdown(source) {
|
|
121
|
+
const installationId = await this.deps.resolveInstallationId(source.ownerKind, source.ownerId);
|
|
122
|
+
if (installationId === null) {
|
|
123
|
+
throw new ValidationError('No GitHub installation is available for this scope; connect GitHub before syncing a source');
|
|
124
|
+
}
|
|
125
|
+
const entries = await this.deps.githubClient.listDirectory(installationId, { owner: source.repoOwner, repo: source.repoName }, source.dirPath, source.gitRef);
|
|
126
|
+
return entries.filter((e) => e.type === 'file' && isMarkdownFile(e.name));
|
|
127
|
+
}
|
|
128
|
+
/** Read, parse and upsert one file as a fragment owned by the source's tier. */
|
|
129
|
+
async syncEntry(source, entry, prior, now) {
|
|
130
|
+
const installationId = await this.deps.resolveInstallationId(source.ownerKind, source.ownerId);
|
|
131
|
+
if (installationId === null) {
|
|
132
|
+
throw new ValidationError('No GitHub installation is available for this scope');
|
|
133
|
+
}
|
|
134
|
+
const file = await this.deps.githubClient.getFileContent(installationId, { owner: source.repoOwner, repo: source.repoName }, entry.path, source.gitRef);
|
|
135
|
+
if (!file)
|
|
136
|
+
return;
|
|
137
|
+
const parsed = parseFragmentMarkdown(entry.path, file.content);
|
|
138
|
+
if (!parsed)
|
|
139
|
+
return;
|
|
140
|
+
// Sourced ids are namespaced so two sources can't collide; an explicit
|
|
141
|
+
// frontmatter `id` instead *shadows* a built-in/inherited fragment (ADR 0006).
|
|
142
|
+
const fragmentId = parsed.id?.trim() || `src:${source.id}:${slugFromPath(entry.path)}`;
|
|
143
|
+
const record = {
|
|
144
|
+
fragmentId,
|
|
145
|
+
ownerKind: source.ownerKind,
|
|
146
|
+
ownerId: source.ownerId,
|
|
147
|
+
version: prior?.version ?? '1.0.0',
|
|
148
|
+
title: parsed.title,
|
|
149
|
+
category: parsed.category ?? null,
|
|
150
|
+
summary: parsed.summary,
|
|
151
|
+
body: parsed.body,
|
|
152
|
+
appliesTo: parsed.appliesTo ?? null,
|
|
153
|
+
tags: parsed.tags && parsed.tags.length ? parsed.tags : null,
|
|
154
|
+
sourceId: source.id,
|
|
155
|
+
sourcePath: entry.path,
|
|
156
|
+
sourceSha: file.sha,
|
|
157
|
+
createdAt: prior?.createdAt ?? now,
|
|
158
|
+
updatedAt: now,
|
|
159
|
+
deletedAt: null,
|
|
160
|
+
};
|
|
161
|
+
await this.deps.promptFragmentRepository.upsert(record);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function normalizeDir(dirPath) {
|
|
165
|
+
return (dirPath ?? '').replace(/^\/+|\/+$/g, '');
|
|
166
|
+
}
|
|
167
|
+
function toWire(record) {
|
|
168
|
+
return {
|
|
169
|
+
id: record.id,
|
|
170
|
+
ownerKind: record.ownerKind,
|
|
171
|
+
ownerId: record.ownerId,
|
|
172
|
+
repoOwner: record.repoOwner,
|
|
173
|
+
repoName: record.repoName,
|
|
174
|
+
gitRef: record.gitRef,
|
|
175
|
+
dirPath: record.dirPath,
|
|
176
|
+
lastSyncedSha: record.lastSyncedSha,
|
|
177
|
+
lastSyncedAt: record.lastSyncedAt,
|
|
178
|
+
createdAt: record.createdAt,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=FragmentSourceService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FragmentSourceService.js","sourceRoot":"","sources":["../../src/fragmentLibrary/FragmentSourceService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type FragmentSelectionContext, type FragmentSelector, type ModelProvider, type ModelProviderResolver, type ModelRef, type SelectableFragment } from '@cat-factory/kernel';
|
|
2
|
+
export interface LlmFragmentSelectorDependencies {
|
|
3
|
+
/** Resolve a {@link ModelProvider} for the run's workspace scope (DB key pool). */
|
|
4
|
+
modelProviderResolver?: ModelProviderResolver;
|
|
5
|
+
/** Static provider (e.g. a fake in tests); used when no resolver is set. */
|
|
6
|
+
modelProvider?: ModelProvider;
|
|
7
|
+
modelRef: ModelRef;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* LLM-backed {@link FragmentSelector} (ADR 0006 §5): asks the configured model to
|
|
11
|
+
* pick the relevant fragment ids from the candidates' summaries (bodies are never
|
|
12
|
+
* sent — summaries keep the call cheap). Degrades gracefully to the deterministic
|
|
13
|
+
* matcher if the model is unavailable or its response can't be parsed, so review
|
|
14
|
+
* never blocks on the selector and offline/test runs stay deterministic.
|
|
15
|
+
*/
|
|
16
|
+
export declare class LlmFragmentSelector implements FragmentSelector {
|
|
17
|
+
private readonly deps;
|
|
18
|
+
constructor(deps: LlmFragmentSelectorDependencies);
|
|
19
|
+
select(candidates: SelectableFragment[], context: FragmentSelectionContext): Promise<string[]>;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=LlmFragmentSelector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LlmFragmentSelector.d.ts","sourceRoot":"","sources":["../../src/fragmentLibrary/LlmFragmentSelector.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,wBAAwB,EAC7B,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,QAAQ,EACb,KAAK,kBAAkB,EACxB,MAAM,qBAAqB,CAAA;AAQ5B,MAAM,WAAW,+BAA+B;IAC9C,mFAAmF;IACnF,qBAAqB,CAAC,EAAE,qBAAqB,CAAA;IAC7C,4EAA4E;IAC5E,aAAa,CAAC,EAAE,aAAa,CAAA;IAC7B,QAAQ,EAAE,QAAQ,CAAA;CACnB;AA4CD;;;;;;GAMG;AACH,qBAAa,mBAAoB,YAAW,gBAAgB;IAC9C,OAAO,CAAC,QAAQ,CAAC,IAAI;IAAjC,YAA6B,IAAI,EAAE,+BAA+B,EAAI;IAEhE,MAAM,CACV,UAAU,EAAE,kBAAkB,EAAE,EAChC,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,MAAM,EAAE,CAAC,CAwBnB;CACF"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import {} from '@cat-factory/kernel';
|
|
2
|
+
import { generateText } from 'ai';
|
|
3
|
+
import { catFactoryObservability } from '../providers/instrumented.js';
|
|
4
|
+
import { selectDeterministic } from './fragment-catalog.js';
|
|
5
|
+
const SYSTEM_PROMPT = 'You select which best-practice guideline fragments are relevant to a coding task. ' +
|
|
6
|
+
'You are given the task context and a list of candidate fragments (id, title, summary, tags). ' +
|
|
7
|
+
'Return ONLY a JSON array of the ids that are relevant — no prose, no code fences. ' +
|
|
8
|
+
'Prefer precision: a frontend-only task should not pull database or backend guidelines.';
|
|
9
|
+
function buildPrompt(candidates, context) {
|
|
10
|
+
const list = candidates.map((c) => ({
|
|
11
|
+
id: c.id,
|
|
12
|
+
title: c.title,
|
|
13
|
+
summary: c.summary,
|
|
14
|
+
tags: c.tags ?? [],
|
|
15
|
+
}));
|
|
16
|
+
return [
|
|
17
|
+
`Agent role: ${context.agentKind}`,
|
|
18
|
+
`Block type: ${context.blockType}`,
|
|
19
|
+
`Block: ${context.blockTitle}`,
|
|
20
|
+
context.blockDescription ? `Description: ${context.blockDescription}` : '',
|
|
21
|
+
context.signals.length ? `Recent work / context:\n${context.signals.join('\n---\n')}` : '',
|
|
22
|
+
'',
|
|
23
|
+
'Candidate fragments:',
|
|
24
|
+
JSON.stringify(list, null, 2),
|
|
25
|
+
'',
|
|
26
|
+
'Respond with a JSON array of the relevant fragment ids, e.g. ["node.performance"].',
|
|
27
|
+
]
|
|
28
|
+
.filter(Boolean)
|
|
29
|
+
.join('\n');
|
|
30
|
+
}
|
|
31
|
+
function extractIds(text, valid) {
|
|
32
|
+
const start = text.indexOf('[');
|
|
33
|
+
const end = text.lastIndexOf(']');
|
|
34
|
+
if (start === -1 || end <= start)
|
|
35
|
+
return null;
|
|
36
|
+
try {
|
|
37
|
+
const parsed = JSON.parse(text.slice(start, end + 1));
|
|
38
|
+
if (!Array.isArray(parsed))
|
|
39
|
+
return null;
|
|
40
|
+
return parsed.filter((id) => typeof id === 'string' && valid.has(id));
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* LLM-backed {@link FragmentSelector} (ADR 0006 §5): asks the configured model to
|
|
48
|
+
* pick the relevant fragment ids from the candidates' summaries (bodies are never
|
|
49
|
+
* sent — summaries keep the call cheap). Degrades gracefully to the deterministic
|
|
50
|
+
* matcher if the model is unavailable or its response can't be parsed, so review
|
|
51
|
+
* never blocks on the selector and offline/test runs stay deterministic.
|
|
52
|
+
*/
|
|
53
|
+
export class LlmFragmentSelector {
|
|
54
|
+
deps;
|
|
55
|
+
constructor(deps) {
|
|
56
|
+
this.deps = deps;
|
|
57
|
+
}
|
|
58
|
+
async select(candidates, context) {
|
|
59
|
+
if (candidates.length === 0)
|
|
60
|
+
return [];
|
|
61
|
+
const fallback = () => selectDeterministic(candidates, context);
|
|
62
|
+
try {
|
|
63
|
+
const provider = this.deps.modelProviderResolver
|
|
64
|
+
? await this.deps.modelProviderResolver.forScope({ workspaceId: context.workspaceId })
|
|
65
|
+
: this.deps.modelProvider;
|
|
66
|
+
if (!provider)
|
|
67
|
+
return fallback();
|
|
68
|
+
const model = provider.resolve(this.deps.modelRef);
|
|
69
|
+
const { text } = await generateText({
|
|
70
|
+
model,
|
|
71
|
+
system: SYSTEM_PROMPT,
|
|
72
|
+
prompt: buildPrompt(candidates, context),
|
|
73
|
+
temperature: 0,
|
|
74
|
+
// Headroom for a reasoning model's `<think>` before the (small) id list —
|
|
75
|
+
// a tight cap truncates the output before any ids are emitted.
|
|
76
|
+
maxOutputTokens: 5000,
|
|
77
|
+
providerOptions: catFactoryObservability({ agentKind: 'fragment-selector' }),
|
|
78
|
+
});
|
|
79
|
+
const ids = extractIds(text, new Set(candidates.map((c) => c.id)));
|
|
80
|
+
return ids ?? fallback();
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return fallback();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=LlmFragmentSelector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LlmFragmentSelector.js","sourceRoot":"","sources":["../../src/fragmentLibrary/LlmFragmentSelector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { PromptFragment } from '@cat-factory/contracts';
|
|
2
|
+
import type { AgentKind, BlockType, FragmentTier } from '@cat-factory/kernel';
|
|
3
|
+
import type { FragmentSelectionContext, SelectableFragment } from '@cat-factory/kernel';
|
|
4
|
+
import type { PromptFragmentRecord } from '@cat-factory/kernel';
|
|
5
|
+
/** A fragment after the three tiers are merged, carrying its winning tier. */
|
|
6
|
+
export interface ResolvedCatalogEntry {
|
|
7
|
+
id: string;
|
|
8
|
+
version: string;
|
|
9
|
+
title: string;
|
|
10
|
+
category: string | null;
|
|
11
|
+
summary: string;
|
|
12
|
+
body: string;
|
|
13
|
+
appliesTo: {
|
|
14
|
+
blockTypes?: BlockType[];
|
|
15
|
+
agentKinds?: AgentKind[];
|
|
16
|
+
} | null;
|
|
17
|
+
tags: string[] | null;
|
|
18
|
+
source: {
|
|
19
|
+
sourceId: string;
|
|
20
|
+
path: string;
|
|
21
|
+
sha: string;
|
|
22
|
+
} | null;
|
|
23
|
+
tier: FragmentTier;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Merge the three tiers into one catalog. Later tiers override earlier ones by
|
|
27
|
+
* stable id (workspace > account > built-in); a tombstoned record at any tier
|
|
28
|
+
* *suppresses* the id outright. Returns live entries sorted by id.
|
|
29
|
+
*/
|
|
30
|
+
export declare function mergeCatalog(builtins: PromptFragment[], accountRows: PromptFragmentRecord[], workspaceRows: PromptFragmentRecord[]): ResolvedCatalogEntry[];
|
|
31
|
+
/** Reduce a resolved entry to the metadata the selector reasons over (no body). */
|
|
32
|
+
export declare function toSelectable(entry: ResolvedCatalogEntry): SelectableFragment;
|
|
33
|
+
/** A resolved entry → the wire {@link PromptFragment} (+ tier handled by caller). */
|
|
34
|
+
export declare function entryToFragment(entry: ResolvedCatalogEntry): PromptFragment;
|
|
35
|
+
/**
|
|
36
|
+
* The deterministic fallback selector (ADR 0006 §5): keep every candidate whose
|
|
37
|
+
* `appliesTo` gate admits the run, then — when any candidate carries tags —
|
|
38
|
+
* prefer those whose tags intersect the run's signals (block type, title,
|
|
39
|
+
* description, prior outputs). With no tags anywhere, the gate alone decides, so
|
|
40
|
+
* an offline/test run is fully deterministic and never blocks.
|
|
41
|
+
*/
|
|
42
|
+
export declare function selectDeterministic(candidates: SelectableFragment[], context: FragmentSelectionContext): string[];
|
|
43
|
+
//# sourceMappingURL=fragment-catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fragment-catalog.d.ts","sourceRoot":"","sources":["../../src/fragmentLibrary/fragment-catalog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAC5D,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAC7E,OAAO,KAAK,EAAE,wBAAwB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACvF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAM/D,8EAA8E;AAC9E,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE;QAAE,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;QAAC,UAAU,CAAC,EAAE,SAAS,EAAE,CAAA;KAAE,GAAG,IAAI,CAAA;IACxE,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;IACrB,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IAC9D,IAAI,EAAE,YAAY,CAAA;CACnB;AAqCD;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,cAAc,EAAE,EAC1B,WAAW,EAAE,oBAAoB,EAAE,EACnC,aAAa,EAAE,oBAAoB,EAAE,GACpC,oBAAoB,EAAE,CAMxB;AAgBD,mFAAmF;AACnF,wBAAgB,YAAY,CAAC,KAAK,EAAE,oBAAoB,GAAG,kBAAkB,CAU5E;AAED,qFAAqF;AACrF,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,cAAc,CAa3E;AAcD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,kBAAkB,EAAE,EAChC,OAAO,EAAE,wBAAwB,GAChC,MAAM,EAAE,CAqBV"}
|