@aiwg/cli 2026.8.0 → 2026.8.2

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 (70) hide show
  1. package/README.md +33 -0
  2. package/agentic/code/providers/capability-matrix.yaml +511 -0
  3. package/agentic/code/providers/model-capabilities.v1.json +120 -0
  4. package/agentic/code/providers/model-catalog.v1.json +96 -0
  5. package/agentic/code/providers/model-policy-evaluations.v1.json +50 -0
  6. package/agentic/code/providers/premium-model-allowlist.v1.json +36 -0
  7. package/bin/aiwg.mjs +14 -10
  8. package/dist/src/api/index.d.ts +1 -0
  9. package/dist/src/api/index.js +1 -0
  10. package/dist/src/artifacts/cli.js +2 -0
  11. package/dist/src/artifacts/types.js +4 -0
  12. package/dist/src/auth/client.js +209 -0
  13. package/dist/src/auth/config.js +38 -0
  14. package/dist/src/auth/credential-store.js +141 -0
  15. package/dist/src/auth/resource-credentials.js +25 -0
  16. package/dist/src/auth/types.js +2 -0
  17. package/dist/src/channel/manager.mjs +5 -5
  18. package/dist/src/cli/handlers/auth.js +125 -0
  19. package/dist/src/cli/handlers/help.js +1 -0
  20. package/dist/src/cli/handlers/index.js +3 -1
  21. package/dist/src/cli/handlers/install.js +42 -4
  22. package/dist/src/cli/handlers/marketplace.js +375 -122
  23. package/dist/src/cli/handlers/resource-versions.js +2 -0
  24. package/dist/src/cli/handlers/sessions.js +23 -5
  25. package/dist/src/cli/handlers/subcommands.js +10 -1
  26. package/dist/src/cli/handlers/use.js +342 -43
  27. package/dist/src/config/gitignore.js +1 -0
  28. package/dist/src/extensions/commands/definitions.js +19 -0
  29. package/dist/src/marketplace/exchange.js +602 -0
  30. package/dist/src/marketplace/provenance-types.js +19 -0
  31. package/dist/src/marketplace/provenance.js +834 -0
  32. package/dist/src/memory/canonical-context.js +342 -0
  33. package/dist/src/memory/context-pack.js +282 -0
  34. package/dist/src/memory/index.js +4 -0
  35. package/dist/src/memory/intake.js +118 -0
  36. package/dist/src/packages/adapters/git.js +79 -29
  37. package/dist/src/packages/package-discovery.js +81 -0
  38. package/dist/src/packages/package-registry.js +2 -0
  39. package/dist/src/packages/registry.js +119 -20
  40. package/dist/src/resources/resolver.js +1 -0
  41. package/dist/src/resources/web-release.d.ts +3 -1
  42. package/dist/src/resources/web-release.js +14 -6
  43. package/dist/src/serve/agentic-sandbox-fleet-client.js +213 -0
  44. package/dist/src/serve/fleet-mission-conductor.js +293 -0
  45. package/dist/src/sessions/index.js +1 -0
  46. package/dist/src/sessions/output-registration.js +338 -0
  47. package/dist/src/sessions/promotion.js +73 -2
  48. package/dist/src/sessions/repository.js +2 -1
  49. package/dist/src/update/notifier.mjs +13 -2
  50. package/package.json +8 -1
  51. package/tools/_resolve-impl.mjs +74 -0
  52. package/tools/agents/deploy-agents.mjs +962 -0
  53. package/tools/agents/providers/base.mjs +2954 -0
  54. package/tools/agents/providers/claude.mjs +711 -0
  55. package/tools/agents/providers/codex.mjs +699 -0
  56. package/tools/agents/providers/copilot.mjs +659 -0
  57. package/tools/agents/providers/cursor.mjs +714 -0
  58. package/tools/agents/providers/factory.mjs +1130 -0
  59. package/tools/agents/providers/hermes.mjs +663 -0
  60. package/tools/agents/providers/hook-capabilities.mjs +85 -0
  61. package/tools/agents/providers/model-role.mjs +56 -0
  62. package/tools/agents/providers/openclaw-translator.mjs +348 -0
  63. package/tools/agents/providers/openclaw.mjs +680 -0
  64. package/tools/agents/providers/opencode.mjs +675 -0
  65. package/tools/agents/providers/openhuman.mjs +292 -0
  66. package/tools/agents/providers/warp.mjs +413 -0
  67. package/tools/agents/providers/windsurf.mjs +748 -0
  68. package/tools/commands/deploy-prompts-codex.mjs +336 -0
  69. package/tools/plugin/package-plugins.mjs +1013 -0
  70. package/tools/skills/deploy-skills-codex.mjs +571 -0
@@ -0,0 +1,342 @@
1
+ import { closeSync, existsSync, mkdirSync, openSync, readFileSync, realpathSync, renameSync, unlinkSync, writeFileSync, } from 'node:fs';
2
+ import { createHash } from 'node:crypto';
3
+ import { dirname, relative, resolve, sep } from 'node:path';
4
+ import { z } from 'zod';
5
+ const DigestSchema = z.string().regex(/^sha256:[0-9a-f]{64}$/);
6
+ const LocatorSchema = z.string().min(1).max(512).refine(value => !/[\r\n\0]/.test(value));
7
+ export const CanonicalContextTargetSchema = z.enum([
8
+ 'project-fact', 'preference', 'goal', 'project', 'person', 'decision', 'operating-rule',
9
+ ]);
10
+ export const CanonicalContextClassificationSchema = z.enum(['public', 'internal']);
11
+ export const CanonicalContextProposalSchema = z.object({
12
+ target: CanonicalContextTargetSchema,
13
+ key: z.string().min(1).max(128).regex(/^[a-z0-9][a-z0-9._-]*$/),
14
+ value: z.string().min(1).max(8192),
15
+ sourceRef: LocatorSchema,
16
+ sourceDigest: DigestSchema.nullable().default(null),
17
+ reviewer: z.string().min(1).max(128),
18
+ reason: z.string().min(1).max(1024),
19
+ scope: z.string().min(1).max(128),
20
+ classification: CanonicalContextClassificationSchema,
21
+ reviewAt: z.string().datetime().nullable().default(null),
22
+ expiresAt: z.string().datetime().nullable().default(null),
23
+ }).strict();
24
+ function sha256(value) {
25
+ return `sha256:${createHash('sha256').update(value).digest('hex')}`;
26
+ }
27
+ function canonicalLocator(value) {
28
+ try {
29
+ const parsed = new URL(value);
30
+ if (parsed.protocol === 'http:' || parsed.protocol === 'https:') {
31
+ parsed.username = '';
32
+ parsed.password = '';
33
+ parsed.search = '';
34
+ parsed.hash = '';
35
+ return parsed.toString();
36
+ }
37
+ }
38
+ catch {
39
+ // Opaque locator.
40
+ }
41
+ return value;
42
+ }
43
+ function assertSafeProposal(proposal) {
44
+ const content = `${proposal.key}\n${proposal.value}\n${proposal.sourceRef}`;
45
+ if (/(?:ignore|disregard) (?:all |the )?(?:previous|prior) instructions|system prompt|developer message|(?:run|execute) (?:a )?(?:shell|command)/i.test(content)) {
46
+ throw new Error('canonical context proposal contains instruction-like material');
47
+ }
48
+ if (/(?:^|[?&;:\s])(?:api[_-]?key|access[_-]?token|token|secret|password|passwd|authorization)\s*[:=]\s*[^\s&;]+/i.test(content)
49
+ || /-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/.test(content)) {
50
+ throw new Error('canonical context proposal contains unsafe material');
51
+ }
52
+ if (/^(?:system|developer|provider|agent)(?:\.|$)/.test(proposal.key)) {
53
+ throw new Error('canonical context cannot override a higher-authority namespace');
54
+ }
55
+ }
56
+ function ensureStorageInsideProject(projectRoot, target) {
57
+ const root = realpathSync(projectRoot);
58
+ const lexicalRoot = resolve(projectRoot);
59
+ const candidate = resolve(target);
60
+ if (candidate !== lexicalRoot && !candidate.startsWith(`${lexicalRoot}${sep}`)) {
61
+ throw new Error('canonical context storage must remain inside the project');
62
+ }
63
+ let ancestor = candidate;
64
+ while (!existsSync(ancestor))
65
+ ancestor = dirname(ancestor);
66
+ const actual = realpathSync(ancestor);
67
+ if (actual !== root && !actual.startsWith(`${root}${sep}`)) {
68
+ throw new Error('canonical context storage cannot traverse a link outside the project');
69
+ }
70
+ }
71
+ function writeJsonAtomic(filePath, value) {
72
+ mkdirSync(dirname(filePath), { recursive: true });
73
+ const temporary = `${filePath}.${process.pid}.${Date.now()}.tmp`;
74
+ writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\n`, { flag: 'wx', mode: 0o600 });
75
+ renameSync(temporary, filePath);
76
+ }
77
+ function workspaceIdentity(projectRoot) {
78
+ const root = realpathSync(projectRoot);
79
+ return sha256(relative(dirname(root), root));
80
+ }
81
+ function stableEntryId(proposal) {
82
+ return sha256(JSON.stringify({
83
+ target: proposal.target,
84
+ key: proposal.key,
85
+ value: proposal.value,
86
+ sourceRef: proposal.sourceRef,
87
+ sourceDigest: proposal.sourceDigest,
88
+ scope: proposal.scope,
89
+ classification: proposal.classification,
90
+ }));
91
+ }
92
+ function proposalFromEntry(entry) {
93
+ return CanonicalContextProposalSchema.parse({
94
+ target: entry.target,
95
+ key: entry.key,
96
+ value: entry.value,
97
+ sourceRef: entry.sourceRef,
98
+ sourceDigest: entry.sourceDigest,
99
+ reviewer: entry.reviewer,
100
+ reason: entry.reason,
101
+ scope: entry.scope,
102
+ classification: entry.classification,
103
+ reviewAt: entry.reviewAt,
104
+ expiresAt: entry.expiresAt,
105
+ });
106
+ }
107
+ function storeDigest(store) {
108
+ return sha256(JSON.stringify(store));
109
+ }
110
+ export class CanonicalContextRepository {
111
+ root;
112
+ statePath;
113
+ receiptRoot;
114
+ lockPath;
115
+ workspaceId;
116
+ constructor(projectRoot) {
117
+ this.root = resolve(projectRoot, '.aiwg/context/compound-memory');
118
+ this.statePath = resolve(this.root, 'context.json');
119
+ this.receiptRoot = resolve(this.root, 'receipts');
120
+ this.lockPath = resolve(this.root, '.lock');
121
+ ensureStorageInsideProject(projectRoot, this.root);
122
+ this.workspaceId = workspaceIdentity(projectRoot);
123
+ }
124
+ read() {
125
+ if (!existsSync(this.statePath)) {
126
+ return {
127
+ schemaVersion: 'aiwg.canonical-context.v1',
128
+ workspaceId: this.workspaceId,
129
+ revision: 0,
130
+ entries: {},
131
+ };
132
+ }
133
+ const store = JSON.parse(readFileSync(this.statePath, 'utf8'));
134
+ if (store.schemaVersion !== 'aiwg.canonical-context.v1'
135
+ || store.workspaceId !== this.workspaceId
136
+ || !Number.isSafeInteger(store.revision)
137
+ || typeof store.entries !== 'object'
138
+ || Array.isArray(store.entries)) {
139
+ throw new Error('canonical context store identity or schema is invalid');
140
+ }
141
+ return store;
142
+ }
143
+ previewUpsert(raw) {
144
+ const proposal = CanonicalContextProposalSchema.parse({
145
+ ...raw,
146
+ sourceRef: canonicalLocator(raw.sourceRef),
147
+ });
148
+ assertSafeProposal(proposal);
149
+ const store = this.read();
150
+ const entryId = stableEntryId(proposal);
151
+ const active = Object.values(store.entries).find(entry => (entry.status === 'active' && entry.target === proposal.target && entry.key === proposal.key));
152
+ const duplicate = active?.entryId === entryId;
153
+ const conflicts = active && !duplicate
154
+ ? [{ entryId: active.entryId, valueDigest: sha256(active.value) }]
155
+ : [];
156
+ const diff = duplicate ? [] : [
157
+ { field: `${proposal.target}.${proposal.key}`, before: active?.value ?? null, after: proposal.value },
158
+ ];
159
+ const snapshot = storeDigest(store);
160
+ return {
161
+ schemaVersion: 'aiwg.canonical-context-preview.v1',
162
+ operationId: sha256(JSON.stringify({ operation: 'upsert', proposal, snapshot })),
163
+ operation: 'upsert',
164
+ workspaceId: this.workspaceId,
165
+ storeDigest: snapshot,
166
+ duplicate,
167
+ confirmationRequired: true,
168
+ diff,
169
+ conflicts,
170
+ proposedEntryId: entryId,
171
+ importCount: 0,
172
+ };
173
+ }
174
+ previewRevoke(entryId, reviewer, reason) {
175
+ const store = this.read();
176
+ const entry = store.entries[entryId];
177
+ if (!entry)
178
+ throw new Error('canonical context entry was not found');
179
+ const duplicate = entry.status === 'revoked';
180
+ const snapshot = storeDigest(store);
181
+ return {
182
+ schemaVersion: 'aiwg.canonical-context-preview.v1',
183
+ operationId: sha256(JSON.stringify({ operation: 'revoke', entryId, reviewer, reason, snapshot })),
184
+ operation: 'revoke',
185
+ workspaceId: this.workspaceId,
186
+ storeDigest: snapshot,
187
+ duplicate,
188
+ confirmationRequired: true,
189
+ diff: duplicate ? [] : [{ field: `${entry.target}.${entry.key}`, before: entry.value, after: null }],
190
+ conflicts: [],
191
+ proposedEntryId: entryId,
192
+ importCount: 0,
193
+ };
194
+ }
195
+ export() {
196
+ const store = this.read();
197
+ return {
198
+ schemaVersion: 'aiwg.canonical-context-export.v1',
199
+ sourceWorkspaceId: store.workspaceId,
200
+ exportedAt: new Date().toISOString(),
201
+ entries: Object.values(store.entries).sort((left, right) => left.entryId.localeCompare(right.entryId)),
202
+ };
203
+ }
204
+ previewImport(bundle, allowCrossWorkspace = false) {
205
+ if (bundle.schemaVersion !== 'aiwg.canonical-context-export.v1' || !Array.isArray(bundle.entries)) {
206
+ throw new Error('canonical context import schema is invalid');
207
+ }
208
+ if (bundle.sourceWorkspaceId !== this.workspaceId && !allowCrossWorkspace) {
209
+ throw new Error('cross-workspace context import requires explicit authorization');
210
+ }
211
+ for (const entry of bundle.entries)
212
+ assertSafeProposal(proposalFromEntry(entry));
213
+ const store = this.read();
214
+ const conflicts = bundle.entries.flatMap(imported => Object.values(store.entries)
215
+ .filter(entry => entry.status === 'active'
216
+ && imported.status === 'active'
217
+ && entry.target === imported.target
218
+ && entry.key === imported.key
219
+ && entry.value !== imported.value)
220
+ .map(entry => ({ entryId: entry.entryId, valueDigest: sha256(entry.value) })));
221
+ const snapshot = storeDigest(store);
222
+ return {
223
+ schemaVersion: 'aiwg.canonical-context-preview.v1',
224
+ operationId: sha256(JSON.stringify({
225
+ operation: 'import',
226
+ bundleDigest: sha256(JSON.stringify(bundle)),
227
+ allowCrossWorkspace,
228
+ snapshot,
229
+ })),
230
+ operation: 'import',
231
+ workspaceId: this.workspaceId,
232
+ storeDigest: snapshot,
233
+ duplicate: bundle.entries.every(entry => Boolean(store.entries[entry.entryId])),
234
+ confirmationRequired: true,
235
+ diff: bundle.entries
236
+ .filter(entry => !store.entries[entry.entryId])
237
+ .map(entry => ({ field: `${entry.target}.${entry.key}`, before: null, after: entry.value })),
238
+ conflicts,
239
+ proposedEntryId: null,
240
+ importCount: bundle.entries.length,
241
+ };
242
+ }
243
+ confirm(input) {
244
+ mkdirSync(this.root, { recursive: true });
245
+ const lock = openSync(this.lockPath, 'wx', 0o600);
246
+ try {
247
+ const requestedReceiptPath = resolve(this.receiptRoot, `${input.preview.operationId.replace(':', '_')}.json`);
248
+ if (existsSync(requestedReceiptPath)) {
249
+ return {
250
+ ...JSON.parse(readFileSync(requestedReceiptPath, 'utf8')),
251
+ duplicate: true,
252
+ };
253
+ }
254
+ const current = input.preview.operation === 'upsert' && input.proposal
255
+ ? this.previewUpsert(input.proposal)
256
+ : input.preview.operation === 'revoke' && input.revoke
257
+ ? this.previewRevoke(input.revoke.entryId, input.revoke.reviewer, input.revoke.reason)
258
+ : input.preview.operation === 'import' && input.bundle
259
+ ? this.previewImport(input.bundle, input.allowCrossWorkspace)
260
+ : null;
261
+ if (!current || current.operationId !== input.preview.operationId) {
262
+ throw new Error('confirmation requires the exact current canonical-context preview');
263
+ }
264
+ const receiptPath = requestedReceiptPath;
265
+ const store = this.read();
266
+ const now = new Date().toISOString();
267
+ const changed = [];
268
+ if (current.operation === 'upsert' && input.proposal && current.proposedEntryId) {
269
+ const proposal = CanonicalContextProposalSchema.parse({
270
+ ...input.proposal,
271
+ sourceRef: canonicalLocator(input.proposal.sourceRef),
272
+ });
273
+ if (!current.duplicate) {
274
+ const active = Object.values(store.entries).find(entry => (entry.status === 'active' && entry.target === proposal.target && entry.key === proposal.key));
275
+ if (active) {
276
+ active.status = 'superseded';
277
+ active.updatedAt = now;
278
+ active.disposition = { reviewer: proposal.reviewer, reason: proposal.reason, recordedAt: now };
279
+ changed.push(active.entryId);
280
+ }
281
+ store.entries[current.proposedEntryId] = {
282
+ ...proposal,
283
+ entryId: current.proposedEntryId,
284
+ status: 'active',
285
+ createdAt: now,
286
+ updatedAt: now,
287
+ supersedes: active?.entryId ?? null,
288
+ disposition: null,
289
+ importedFromWorkspace: null,
290
+ };
291
+ changed.push(current.proposedEntryId);
292
+ }
293
+ }
294
+ else if (current.operation === 'revoke' && input.revoke) {
295
+ const entry = store.entries[input.revoke.entryId];
296
+ if (!current.duplicate) {
297
+ entry.status = 'revoked';
298
+ entry.updatedAt = now;
299
+ entry.disposition = {
300
+ reviewer: input.revoke.reviewer,
301
+ reason: input.revoke.reason,
302
+ recordedAt: now,
303
+ };
304
+ changed.push(entry.entryId);
305
+ }
306
+ }
307
+ else if (current.operation === 'import' && input.bundle) {
308
+ for (const imported of input.bundle.entries) {
309
+ if (store.entries[imported.entryId])
310
+ continue;
311
+ store.entries[imported.entryId] = {
312
+ ...imported,
313
+ importedFromWorkspace: input.bundle.sourceWorkspaceId,
314
+ };
315
+ changed.push(imported.entryId);
316
+ }
317
+ }
318
+ if (changed.length > 0) {
319
+ store.revision += 1;
320
+ writeJsonAtomic(this.statePath, store);
321
+ }
322
+ const receipt = {
323
+ schemaVersion: 'aiwg.canonical-context-receipt.v1',
324
+ receiptId: sha256(`${current.operationId}\0${store.revision}`),
325
+ operationId: current.operationId,
326
+ operation: current.operation,
327
+ workspaceId: this.workspaceId,
328
+ entryIds: changed,
329
+ revision: store.revision,
330
+ duplicate: current.duplicate,
331
+ completedAt: now,
332
+ };
333
+ writeJsonAtomic(receiptPath, receipt);
334
+ return receipt;
335
+ }
336
+ finally {
337
+ closeSync(lock);
338
+ unlinkSync(this.lockPath);
339
+ }
340
+ }
341
+ }
342
+ //# sourceMappingURL=canonical-context.js.map
@@ -0,0 +1,282 @@
1
+ import { existsSync, readFileSync, readdirSync, realpathSync, statSync, } from 'node:fs';
2
+ import { createHash } from 'node:crypto';
3
+ import { relative, resolve, sep } from 'node:path';
4
+ import { loadFortemiCoreMetadataEntries } from '../artifacts/fortemi-core-query-adapter.js';
5
+ const DEFAULT_BUDGET = {
6
+ totalCharacters: 8000,
7
+ lineCharacters: 2000,
8
+ wikiCharacters: 4000,
9
+ citationCharacters: 1500,
10
+ instructionCharacters: 500,
11
+ };
12
+ const EXCLUDED_STATES = new Set([
13
+ 'contradicted', 'superseded', 'revoked', 'origin-unavailable',
14
+ ]);
15
+ function sha256(value) {
16
+ return `sha256:${createHash('sha256').update(value).digest('hex')}`;
17
+ }
18
+ function projectLocator(root, target) {
19
+ const actual = realpathSync(target);
20
+ if (actual !== root && !actual.startsWith(`${root}${sep}`)) {
21
+ throw new Error('context source cannot traverse a link outside the project');
22
+ }
23
+ return relative(root, actual).split(sep).join('/');
24
+ }
25
+ function terms(value) {
26
+ return [...new Set(value.toLocaleLowerCase().match(/[a-z0-9][a-z0-9_-]{1,}/g) ?? [])];
27
+ }
28
+ function lexicalScore(taskTerms, text) {
29
+ if (taskTerms.length === 0)
30
+ return 0;
31
+ const normalized = text.toLocaleLowerCase();
32
+ const hits = taskTerms.filter(term => normalized.includes(term)).length;
33
+ return hits / taskTerms.length;
34
+ }
35
+ function normalizedClaim(value) {
36
+ return value.toLocaleLowerCase().replace(/[^a-z0-9]+/g, ' ').trim();
37
+ }
38
+ function boundedInteger(value, minimum, maximum, name) {
39
+ if (!Number.isSafeInteger(value) || value < minimum || value > maximum) {
40
+ throw new Error(`${name} must be an integer from ${minimum} through ${maximum}`);
41
+ }
42
+ return value;
43
+ }
44
+ function resolvedBudget(input = {}) {
45
+ const budget = { ...DEFAULT_BUDGET, ...input };
46
+ boundedInteger(budget.totalCharacters, 256, 65536, 'totalCharacters');
47
+ boundedInteger(budget.lineCharacters, 0, 65536, 'lineCharacters');
48
+ boundedInteger(budget.wikiCharacters, 0, 65536, 'wikiCharacters');
49
+ boundedInteger(budget.citationCharacters, 0, 16384, 'citationCharacters');
50
+ boundedInteger(budget.instructionCharacters, 0, 8192, 'instructionCharacters');
51
+ return budget;
52
+ }
53
+ function lineCandidates(root, taskTerms) {
54
+ const memoryPath = resolve(root, '.aiwg/memory/line-memory.txt');
55
+ const metadataPath = resolve(root, '.aiwg/memory/line-memory.meta.json');
56
+ if (!existsSync(memoryPath))
57
+ return [];
58
+ projectLocator(root, memoryPath);
59
+ const values = readFileSync(memoryPath, 'utf8').split(/\r?\n/).filter(value => value.trim());
60
+ let entries = {};
61
+ if (existsSync(metadataPath)) {
62
+ projectLocator(root, metadataPath);
63
+ const parsed = JSON.parse(readFileSync(metadataPath, 'utf8'));
64
+ entries = parsed.entries ?? {};
65
+ }
66
+ return values.map((text, index) => {
67
+ const entry = Object.values(entries).find(candidate => candidate.value === text && candidate.status === 'active');
68
+ const sources = Array.isArray(entry?.sources) ? entry.sources : [];
69
+ const score = lexicalScore(taskTerms, text);
70
+ return {
71
+ tier: 'line',
72
+ text,
73
+ locator: typeof entry?.id === 'string' ? `line-memory:${entry.id}` : `.aiwg/memory/line-memory.txt#line-${index + 1}`,
74
+ digest: typeof entry?.digest === 'string' ? entry.digest : sha256(text),
75
+ score,
76
+ backend: 'line-memory-lexical',
77
+ verified: sources.length > 0,
78
+ state: 'active',
79
+ freshness: typeof entry?.updatedAt === 'string' ? entry.updatedAt : null,
80
+ };
81
+ }).filter(candidate => candidate.score > 0);
82
+ }
83
+ function markdownText(raw) {
84
+ return raw
85
+ .replace(/^---\s*[\s\S]*?\n---\s*/m, '')
86
+ .replace(/```[\s\S]*?```/g, ' ')
87
+ .replace(/!\?\[\[[^\]]+\]\]/g, ' ')
88
+ .replace(/\[\[([^\]|]+)(?:\|[^\]]+)?\]\]/g, '$1')
89
+ .replace(/[#>*_`~-]+/g, ' ')
90
+ .replace(/\s+/g, ' ')
91
+ .trim();
92
+ }
93
+ function wikiCandidates(root, taskTerms, maxFiles) {
94
+ const wikiRoot = resolve(root, '.aiwg/wiki');
95
+ if (!existsSync(wikiRoot))
96
+ return [];
97
+ projectLocator(root, wikiRoot);
98
+ const pending = [wikiRoot];
99
+ const results = [];
100
+ let visited = 0;
101
+ while (pending.length > 0 && visited < maxFiles) {
102
+ const directory = pending.shift();
103
+ for (const entry of readdirSync(directory, { withFileTypes: true })
104
+ .sort((left, right) => left.name.localeCompare(right.name))) {
105
+ if (visited >= maxFiles)
106
+ break;
107
+ const target = resolve(directory, entry.name);
108
+ if (entry.isSymbolicLink())
109
+ continue;
110
+ if (entry.isDirectory()) {
111
+ pending.push(target);
112
+ continue;
113
+ }
114
+ if (!entry.isFile() || !entry.name.endsWith('.md') || entry.name === 'index.md')
115
+ continue;
116
+ visited += 1;
117
+ const locator = projectLocator(root, target);
118
+ const raw = readFileSync(target, 'utf8').slice(0, 65536);
119
+ const text = markdownText(raw).slice(0, 1200);
120
+ const score = lexicalScore(taskTerms, text);
121
+ if (score <= 0)
122
+ continue;
123
+ const stat = statSync(target);
124
+ results.push({
125
+ tier: 'wiki',
126
+ text,
127
+ locator,
128
+ digest: sha256(raw),
129
+ score,
130
+ backend: 'wiki-lexical-fallback',
131
+ verified: /(?:^|\n)(?:source|sources|provenance):/i.test(raw),
132
+ state: 'active',
133
+ freshness: stat.mtime.toISOString(),
134
+ });
135
+ }
136
+ }
137
+ return results;
138
+ }
139
+ function fortemiWikiCandidates(root, taskTerms, maxFiles) {
140
+ const loaded = loadFortemiCoreMetadataEntries(root, 'project');
141
+ if (loaded.reason || loaded.entries.length === 0)
142
+ return [];
143
+ const results = [];
144
+ for (const entry of loaded.entries
145
+ .filter(candidate => candidate.path.startsWith('.aiwg/wiki/') && candidate.path.endsWith('.md'))
146
+ .sort((left, right) => left.path.localeCompare(right.path))
147
+ .slice(0, maxFiles)) {
148
+ const target = resolve(root, entry.path);
149
+ if (!existsSync(target))
150
+ continue;
151
+ const locator = projectLocator(root, target);
152
+ const raw = readFileSync(target, 'utf8').slice(0, 65536);
153
+ const text = markdownText(raw).slice(0, 1200);
154
+ const score = lexicalScore(taskTerms, [entry.title, entry.summary, text].join(' '));
155
+ if (score <= 0)
156
+ continue;
157
+ results.push({
158
+ tier: 'wiki',
159
+ text,
160
+ locator,
161
+ digest: /^(?:sha256:)?[0-9a-f]{64}$/i.test(entry.checksum)
162
+ ? `sha256:${entry.checksum.replace(/^sha256:/, '').toLocaleLowerCase()}`
163
+ : sha256(raw),
164
+ score,
165
+ backend: 'fortemi-core',
166
+ verified: /(?:^|\n)(?:source|sources|provenance):/i.test(raw),
167
+ state: 'active',
168
+ freshness: entry.updated || statSync(target).mtime.toISOString(),
169
+ });
170
+ }
171
+ return results;
172
+ }
173
+ function rank(candidates) {
174
+ const effectiveScore = (candidate) => (candidate.state === 'stale' ? candidate.score * 0.5 : candidate.score);
175
+ return [...candidates].sort((left, right) => (effectiveScore(right) - effectiveScore(left)
176
+ || (left.tier === right.tier ? 0 : left.tier === 'line' ? -1 : 1)
177
+ || left.locator.localeCompare(right.locator)));
178
+ }
179
+ export function buildContextPack(task, candidates, options = {}) {
180
+ const started = performance.now();
181
+ const budget = resolvedBudget(options.budget);
182
+ const maxFiles = boundedInteger(options.maxFiles ?? 1000, 1, 10000, 'maxFiles');
183
+ const excluded = [];
184
+ const deduplicated = new Map();
185
+ for (const candidate of rank(candidates)) {
186
+ if (EXCLUDED_STATES.has(candidate.state)) {
187
+ excluded.push({ locator: candidate.locator, reason: candidate.state });
188
+ continue;
189
+ }
190
+ const key = normalizedClaim(candidate.text);
191
+ if (!key)
192
+ continue;
193
+ if (deduplicated.has(key)) {
194
+ excluded.push({ locator: candidate.locator, reason: 'duplicate-claim' });
195
+ continue;
196
+ }
197
+ deduplicated.set(key, candidate);
198
+ }
199
+ const used = {
200
+ totalCharacters: 0,
201
+ lineCharacters: 0,
202
+ wikiCharacters: 0,
203
+ citationCharacters: 0,
204
+ instructionCharacters: 0,
205
+ };
206
+ const instructions = [];
207
+ for (const instruction of options.instructions ?? []) {
208
+ const cost = instruction.text.length;
209
+ if (used.instructionCharacters + cost > budget.instructionCharacters
210
+ || used.totalCharacters + cost > budget.totalCharacters) {
211
+ excluded.push({ locator: instruction.locator, reason: 'instruction-budget' });
212
+ continue;
213
+ }
214
+ instructions.push(instruction);
215
+ used.instructionCharacters += cost;
216
+ used.totalCharacters += cost;
217
+ }
218
+ const items = [];
219
+ for (const candidate of deduplicated.values()) {
220
+ const section = candidate.tier === 'line' ? 'lineCharacters' : 'wikiCharacters';
221
+ const sectionLimit = candidate.tier === 'line' ? budget.lineCharacters : budget.wikiCharacters;
222
+ const citation = `${candidate.locator}${candidate.digest ? ` ${candidate.digest}` : ''}`;
223
+ const contentCost = candidate.text.length;
224
+ const citationCost = citation.length;
225
+ if (used[section] + contentCost > sectionLimit) {
226
+ excluded.push({ locator: candidate.locator, reason: `${candidate.tier}-budget` });
227
+ continue;
228
+ }
229
+ if (used.citationCharacters + citationCost > budget.citationCharacters) {
230
+ excluded.push({ locator: candidate.locator, reason: 'citation-budget' });
231
+ continue;
232
+ }
233
+ if (used.totalCharacters + contentCost + citationCost > budget.totalCharacters) {
234
+ excluded.push({ locator: candidate.locator, reason: 'total-budget' });
235
+ continue;
236
+ }
237
+ items.push({ ...candidate, trust: 'quoted-data', citation });
238
+ used[section] += contentCost;
239
+ used.citationCharacters += citationCost;
240
+ used.totalCharacters += contentCost + citationCost;
241
+ }
242
+ const identity = {
243
+ taskDigest: sha256(task),
244
+ budget,
245
+ items: items.map(item => ({ locator: item.locator, digest: item.digest, text: item.text })),
246
+ instructions,
247
+ };
248
+ return {
249
+ schemaVersion: 'aiwg.compound-memory.context-pack.v1',
250
+ id: sha256(JSON.stringify(identity)),
251
+ taskDigest: identity.taskDigest,
252
+ backend: [...new Set(items.map(item => item.backend))].sort(),
253
+ budget,
254
+ used,
255
+ items,
256
+ instructions,
257
+ excluded,
258
+ truncated: excluded.some(item => item.reason.endsWith('budget')),
259
+ metrics: {
260
+ candidates: candidates.length,
261
+ selected: items.length,
262
+ elapsedMs: Number((performance.now() - started).toFixed(3)),
263
+ maxFiles,
264
+ },
265
+ };
266
+ }
267
+ export function buildWorkspaceContextPack(projectRoot, task, options = {}) {
268
+ if (!task.trim())
269
+ throw new Error('context task must be nonblank');
270
+ const root = realpathSync(projectRoot);
271
+ const taskTerms = terms(task);
272
+ const maxFiles = boundedInteger(options.maxFiles ?? 1000, 1, 10000, 'maxFiles');
273
+ const candidates = [
274
+ ...lineCandidates(root, taskTerms),
275
+ ...(() => {
276
+ const indexed = fortemiWikiCandidates(root, taskTerms, maxFiles);
277
+ return indexed.length > 0 ? indexed : wikiCandidates(root, taskTerms, maxFiles);
278
+ })(),
279
+ ];
280
+ return buildContextPack(task, candidates, { ...options, maxFiles });
281
+ }
282
+ //# sourceMappingURL=context-pack.js.map
@@ -0,0 +1,4 @@
1
+ export * from './context-pack.js';
2
+ export * from './canonical-context.js';
3
+ export * from './intake.js';
4
+ //# sourceMappingURL=index.js.map