fluffy-context 0.7.6 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +115 -36
  2. package/dist/src/agent/api.d.ts +49 -4
  3. package/dist/src/agent/api.js +183 -131
  4. package/dist/src/agent/index.d.ts +7 -1
  5. package/dist/src/agent/index.js +1 -0
  6. package/dist/src/cli/main.js +301 -15
  7. package/dist/src/cognition/evidence-graph.d.ts +4 -0
  8. package/dist/src/cognition/evidence-graph.js +187 -0
  9. package/dist/src/cognition/evolution.d.ts +36 -0
  10. package/dist/src/cognition/evolution.js +221 -0
  11. package/dist/src/cognition/journal.js +143 -2
  12. package/dist/src/cognition/projections.js +124 -2
  13. package/dist/src/cognition/recipes.d.ts +25 -0
  14. package/dist/src/cognition/recipes.js +149 -0
  15. package/dist/src/cognition/skills.d.ts +45 -0
  16. package/dist/src/cognition/skills.js +184 -0
  17. package/dist/src/cognition/types.d.ts +196 -2
  18. package/dist/src/cognition/usage-report.d.ts +40 -0
  19. package/dist/src/cognition/usage-report.js +232 -0
  20. package/dist/src/cognition/usage.d.ts +5 -0
  21. package/dist/src/cognition/usage.js +48 -0
  22. package/dist/src/compiler/compile.js +73 -4
  23. package/dist/src/compiler/graph.d.ts +7 -0
  24. package/dist/src/compiler/graph.js +117 -0
  25. package/dist/src/compiler/identity.d.ts +1 -1
  26. package/dist/src/compiler/identity.js +3 -1
  27. package/dist/src/compiler/sources.d.ts +3 -1
  28. package/dist/src/compiler/sources.js +11 -2
  29. package/dist/src/compiler/types.d.ts +23 -2
  30. package/dist/src/mcp/server.js +228 -6
  31. package/dist/src/storage/layout.d.ts +5 -0
  32. package/dist/src/storage/layout.js +15 -0
  33. package/dist/src/version.d.ts +1 -1
  34. package/dist/src/version.js +1 -1
  35. package/package.json +1 -1
  36. package/skills/fluffy-context/SKILL.md +53 -5
@@ -4,11 +4,17 @@ import { contextsRoot, contextMetadataPath } from '../storage/layout.js';
4
4
  import { isRecord, readJson } from '../storage/json-store.js';
5
5
  import { checkpoint, rebuildSnapshot, resume, resumeSnapshot } from '../runtime/runtime.js';
6
6
  import { recordUse } from '../cognition/feedback.js';
7
+ import { inspectRecipe, listRecipes, materializeRecipe, transitionRecipe } from '../cognition/recipes.js';
8
+ import { inspectSkill, listSkills, materializeSkill, recordSkillInvocation, recordSkillOutcome, transitionSkill } from '../cognition/skills.js';
9
+ import { acceptEvolutionCandidate, inspectEvolutionProposal, listEvolutionProposals, proposeEvolution, transitionEvolutionProposal } from '../cognition/evolution.js';
7
10
  import { selectV2Orientation } from '../cognition/orient.js';
8
11
  import { discoverV2Deadends, discoverV2Knowledge } from '../cognition/retrieval.js';
9
12
  import { discoverDeadends, discoverKnowledge, listDeadends } from '../runtime/knowledge.js';
10
13
  import { listNotes } from '../runtime/notes.js';
11
14
  import { expandContext } from '../compiler/expand.js';
15
+ import { compileContext } from '../compiler/compile.js';
16
+ import { withUsageTelemetry } from '../cognition/usage.js';
17
+ import { buildUsageReport } from '../cognition/usage-report.js';
12
18
  function normalized(value) {
13
19
  return value.trim().toLocaleLowerCase();
14
20
  }
@@ -139,155 +145,201 @@ export async function saveContext(startPath, input, options = {}) {
139
145
  const projectRoot = await resolveProjectRoot(startPath);
140
146
  return { projectRoot, result: await checkpoint(projectRoot, input, options) };
141
147
  }
142
- export async function contextExpand(startPath, input) {
143
- const projectRoot = await resolveProjectRoot(startPath);
144
- return expandContext(projectRoot, input);
148
+ export async function contextExpand(startPath, input, interfaceName = 'agent') {
149
+ return withUsageTelemetry(startPath, 'expand', 'agent', input, () => resolveProjectRoot(startPath).then((projectRoot) => expandContext(projectRoot, input)), interfaceName);
145
150
  }
146
- export async function loadContext(startPath, options = {}) {
147
- const projectRoot = await resolveProjectRoot(startPath);
148
- const result = await resume(projectRoot, options.contextId, options.maxChars ?? 4000);
149
- const knowledge = options.query === undefined ? null : await discoverKnowledge(projectRoot, options.query, { scope: options.scope, maxChars: options.maxChars ?? 4000 });
150
- const verifiedDeadends = await listDeadends(projectRoot);
151
- return {
152
- projectRoot,
153
- context: result.context,
154
- snapshot: result.snapshot,
155
- resumeSummary: result.resumeSummary,
156
- ...(options.includeDetails ? { details: result.details } : {}),
157
- knowledge,
158
- verifiedDeadendIds: verifiedDeadends.map((item) => item.deadendId),
159
- };
151
+ export async function contextCompile(startPath, options = {}, interfaceName = 'agent') {
152
+ return withUsageTelemetry(startPath, 'compile', 'agent', options, () => compileContext(startPath, options), interfaceName);
160
153
  }
161
- export async function contextOrient(startPath, options = {}) {
162
- const projectRoot = await resolveProjectRoot(startPath);
163
- const query = options.query === undefined ? null : options.query.trim();
164
- if (query === '')
165
- throw new Error('context orient query must not be empty');
166
- let loaded;
167
- try {
168
- const v2 = await selectV2Orientation(projectRoot, options.contextId);
169
- loaded = v2
170
- ? await resumeSnapshot(projectRoot, v2.contextId, v2.snapshotId, options.maxChars ?? 4000)
171
- : await resume(projectRoot, options.contextId, options.maxChars ?? 4000, { touchLastUsedAt: false });
172
- }
173
- catch (error) {
174
- if (error instanceof Error && error.message === 'no active context found') {
175
- return {
176
- status: 'no_context',
177
- projectRoot,
178
- query,
179
- context: null,
180
- snapshot: null,
181
- resumeSummary: null,
182
- knowledge: emptyKnowledge(query ?? ''),
183
- deadends: emptyDeadends(query ?? ''),
184
- notes: [],
185
- truncated: false,
186
- explanation: {
187
- mode: 'v1-fallback',
188
- selection: 'v1',
189
- retrieval: { knowledge: {}, deadends: {} },
190
- truncation: { summary: false, knowledge: false, deadends: false, notes: false },
191
- },
192
- };
154
+ export async function loadContext(startPath, options = {}, interfaceName = 'agent') {
155
+ return withUsageTelemetry(startPath, 'resume', 'agent', options, async () => {
156
+ const projectRoot = await resolveProjectRoot(startPath);
157
+ const result = await resume(projectRoot, options.contextId, options.maxChars ?? 4000);
158
+ const knowledge = options.query === undefined ? null : await discoverKnowledge(projectRoot, options.query, { scope: options.scope, maxChars: options.maxChars ?? 4000 });
159
+ const verifiedDeadends = await listDeadends(projectRoot);
160
+ return {
161
+ projectRoot,
162
+ context: result.context,
163
+ snapshot: result.snapshot,
164
+ resumeSummary: result.resumeSummary,
165
+ ...(options.includeDetails ? { details: result.details } : {}),
166
+ knowledge,
167
+ verifiedDeadendIds: verifiedDeadends.map((item) => item.deadendId),
168
+ };
169
+ }, interfaceName);
170
+ }
171
+ export async function contextOrient(startPath, options = {}, interfaceName = 'agent') {
172
+ const action = async () => {
173
+ const projectRoot = await resolveProjectRoot(startPath);
174
+ const query = options.query === undefined ? null : options.query.trim();
175
+ if (query === '')
176
+ throw new Error('context orient query must not be empty');
177
+ let loaded;
178
+ try {
179
+ const v2 = await selectV2Orientation(projectRoot, options.contextId);
180
+ loaded = v2
181
+ ? await resumeSnapshot(projectRoot, v2.contextId, v2.snapshotId, options.maxChars ?? 4000)
182
+ : await resume(projectRoot, options.contextId, options.maxChars ?? 4000, { touchLastUsedAt: false });
193
183
  }
194
- throw error;
195
- }
196
- const v2 = await selectV2Orientation(projectRoot, options.contextId);
197
- const notes = (await listNotes(projectRoot, {
198
- contextId: loaded.context.id,
199
- openOnly: true,
200
- limit: v2 ? Number.MAX_SAFE_INTEGER : options.noteLimit ?? 20,
201
- maxChars: options.maxChars ?? 4000,
202
- })).filter((note) => !v2 || v2.noteIds.has(note.noteId)).slice(0, options.noteLimit ?? 20);
203
- const v2Knowledge = query === null || !v2 ? null : discoverV2Knowledge(v2.retrieval, query, v2.currentGit, {
204
- scope: options.scope,
205
- limit: options.knowledgeLimit ?? 10,
206
- maxChars: options.maxChars ?? 4000,
207
- });
208
- const v2Deadends = query === null || !v2 ? null : discoverV2Deadends(v2.retrieval, query, v2.currentGit, {
209
- scope: options.scope,
210
- limit: options.deadendLimit ?? 10,
211
- maxChars: options.maxChars ?? 4000,
212
- });
213
- const applicableKnowledge = query === null
214
- ? emptyKnowledge('')
215
- : v2Knowledge?.result ?? await discoverKnowledge(projectRoot, query, {
184
+ catch (error) {
185
+ if (error instanceof Error && error.message === 'no active context found') {
186
+ return {
187
+ status: 'no_context',
188
+ projectRoot,
189
+ query,
190
+ context: null,
191
+ snapshot: null,
192
+ resumeSummary: null,
193
+ knowledge: emptyKnowledge(query ?? ''),
194
+ deadends: emptyDeadends(query ?? ''),
195
+ notes: [],
196
+ truncated: false,
197
+ explanation: {
198
+ mode: 'v1-fallback',
199
+ selection: 'v1',
200
+ retrieval: { knowledge: {}, deadends: {} },
201
+ truncation: { summary: false, knowledge: false, deadends: false, notes: false },
202
+ },
203
+ };
204
+ }
205
+ throw error;
206
+ }
207
+ const v2 = await selectV2Orientation(projectRoot, options.contextId);
208
+ const notes = (await listNotes(projectRoot, {
209
+ contextId: loaded.context.id,
210
+ openOnly: true,
211
+ limit: v2 ? Number.MAX_SAFE_INTEGER : options.noteLimit ?? 20,
212
+ maxChars: options.maxChars ?? 4000,
213
+ })).filter((note) => !v2 || v2.noteIds.has(note.noteId)).slice(0, options.noteLimit ?? 20);
214
+ const v2Knowledge = query === null || !v2 ? null : discoverV2Knowledge(v2.retrieval, query, v2.currentGit, {
216
215
  scope: options.scope,
217
216
  limit: options.knowledgeLimit ?? 10,
218
217
  maxChars: options.maxChars ?? 4000,
219
218
  });
220
- const applicableDeadends = query === null
221
- ? emptyDeadends('')
222
- : v2Deadends?.result ?? await discoverDeadends(projectRoot, query, {
219
+ const v2Deadends = query === null || !v2 ? null : discoverV2Deadends(v2.retrieval, query, v2.currentGit, {
223
220
  scope: options.scope,
224
221
  limit: options.deadendLimit ?? 10,
225
222
  maxChars: options.maxChars ?? 4000,
226
223
  });
227
- const budget = { remaining: Math.max(0, options.maxChars ?? 4000) };
228
- const resumeSummary = {
229
- progressSummary: limited(loaded.resumeSummary.progressSummary, budget),
230
- lastError: loaded.resumeSummary.lastError === null ? null : limited(loaded.resumeSummary.lastError, budget),
231
- completed: limitedList(loaded.resumeSummary.completed, budget),
232
- pendingTasks: limitedList(loaded.resumeSummary.pendingTasks, budget),
233
- decisions: limitedList(loaded.resumeSummary.decisions, budget),
234
- risks: limitedList(loaded.resumeSummary.risks, budget),
235
- relatedFiles: limitedList(loaded.resumeSummary.relatedFiles, budget),
236
- branchOrCommitDrift: loaded.resumeSummary.branchOrCommitDrift,
237
- };
238
- const resumeTruncated = textLength(resumeSummary.progressSummary) < textLength(loaded.resumeSummary.progressSummary)
239
- || textLength(resumeSummary.lastError) < textLength(loaded.resumeSummary.lastError)
240
- || textLength(resumeSummary.completed) < textLength(loaded.resumeSummary.completed)
241
- || textLength(resumeSummary.pendingTasks) < textLength(loaded.resumeSummary.pendingTasks)
242
- || textLength(resumeSummary.decisions) < textLength(loaded.resumeSummary.decisions)
243
- || textLength(resumeSummary.risks) < textLength(loaded.resumeSummary.risks)
244
- || textLength(resumeSummary.relatedFiles) < textLength(loaded.resumeSummary.relatedFiles);
245
- const limitedKnowledgeResult = limitKnowledge(applicableKnowledge, budget);
246
- const limitedDeadendResult = limitDeadends(applicableDeadends, budget);
247
- const limitedNotesResult = limitNotes(notes, budget);
248
- return {
249
- status: 'ready',
250
- projectRoot,
251
- query,
252
- context: {
253
- id: loaded.context.id,
254
- title: loaded.context.title,
255
- status: loaded.context.status,
256
- branch: loaded.context.branch,
257
- commit: loaded.context.commit,
258
- updatedAt: loaded.context.updatedAt,
259
- },
260
- snapshot: {
261
- snapshotId: loaded.snapshot.snapshotId,
262
- mode: loaded.snapshot.mode,
263
- createdAt: loaded.snapshot.createdAt,
264
- branch: loaded.snapshot.branch,
265
- commit: loaded.snapshot.commit,
266
- },
267
- resumeSummary,
268
- knowledge: limitedKnowledgeResult.result,
269
- deadends: limitedDeadendResult.result,
270
- notes: limitedNotesResult.notes,
271
- truncated: resumeTruncated || limitedKnowledgeResult.truncated || limitedDeadendResult.truncated || limitedNotesResult.truncated,
272
- explanation: {
273
- mode: v2 ? 'v2' : 'v1-fallback',
274
- selection: v2?.selection ?? 'v1',
275
- retrieval: {
276
- knowledge: v2Knowledge?.exclusions ?? {},
277
- deadends: v2Deadends?.exclusions ?? {},
224
+ const applicableKnowledge = query === null
225
+ ? emptyKnowledge('')
226
+ : v2Knowledge?.result ?? await discoverKnowledge(projectRoot, query, {
227
+ scope: options.scope,
228
+ limit: options.knowledgeLimit ?? 10,
229
+ maxChars: options.maxChars ?? 4000,
230
+ });
231
+ const applicableDeadends = query === null
232
+ ? emptyDeadends('')
233
+ : v2Deadends?.result ?? await discoverDeadends(projectRoot, query, {
234
+ scope: options.scope,
235
+ limit: options.deadendLimit ?? 10,
236
+ maxChars: options.maxChars ?? 4000,
237
+ });
238
+ const budget = { remaining: Math.max(0, options.maxChars ?? 4000) };
239
+ const resumeSummary = {
240
+ progressSummary: limited(loaded.resumeSummary.progressSummary, budget),
241
+ lastError: loaded.resumeSummary.lastError === null ? null : limited(loaded.resumeSummary.lastError, budget),
242
+ completed: limitedList(loaded.resumeSummary.completed, budget),
243
+ pendingTasks: limitedList(loaded.resumeSummary.pendingTasks, budget),
244
+ decisions: limitedList(loaded.resumeSummary.decisions, budget),
245
+ risks: limitedList(loaded.resumeSummary.risks, budget),
246
+ relatedFiles: limitedList(loaded.resumeSummary.relatedFiles, budget),
247
+ branchOrCommitDrift: loaded.resumeSummary.branchOrCommitDrift,
248
+ };
249
+ const resumeTruncated = textLength(resumeSummary.progressSummary) < textLength(loaded.resumeSummary.progressSummary)
250
+ || textLength(resumeSummary.lastError) < textLength(loaded.resumeSummary.lastError)
251
+ || textLength(resumeSummary.completed) < textLength(loaded.resumeSummary.completed)
252
+ || textLength(resumeSummary.pendingTasks) < textLength(loaded.resumeSummary.pendingTasks)
253
+ || textLength(resumeSummary.decisions) < textLength(loaded.resumeSummary.decisions)
254
+ || textLength(resumeSummary.risks) < textLength(loaded.resumeSummary.risks)
255
+ || textLength(resumeSummary.relatedFiles) < textLength(loaded.resumeSummary.relatedFiles);
256
+ const limitedKnowledgeResult = limitKnowledge(applicableKnowledge, budget);
257
+ const limitedDeadendResult = limitDeadends(applicableDeadends, budget);
258
+ const limitedNotesResult = limitNotes(notes, budget);
259
+ return {
260
+ status: 'ready',
261
+ projectRoot,
262
+ query,
263
+ context: {
264
+ id: loaded.context.id,
265
+ title: loaded.context.title,
266
+ status: loaded.context.status,
267
+ branch: loaded.context.branch,
268
+ commit: loaded.context.commit,
269
+ updatedAt: loaded.context.updatedAt,
278
270
  },
279
- truncation: {
280
- summary: resumeTruncated,
281
- knowledge: limitedKnowledgeResult.truncated,
282
- deadends: limitedDeadendResult.truncated,
283
- notes: limitedNotesResult.truncated,
271
+ snapshot: {
272
+ snapshotId: loaded.snapshot.snapshotId,
273
+ mode: loaded.snapshot.mode,
274
+ createdAt: loaded.snapshot.createdAt,
275
+ branch: loaded.snapshot.branch,
276
+ commit: loaded.snapshot.commit,
284
277
  },
285
- },
278
+ resumeSummary,
279
+ knowledge: limitedKnowledgeResult.result,
280
+ deadends: limitedDeadendResult.result,
281
+ notes: limitedNotesResult.notes,
282
+ truncated: resumeTruncated || limitedKnowledgeResult.truncated || limitedDeadendResult.truncated || limitedNotesResult.truncated,
283
+ explanation: {
284
+ mode: v2 ? 'v2' : 'v1-fallback',
285
+ selection: v2?.selection ?? 'v1',
286
+ retrieval: {
287
+ knowledge: v2Knowledge?.exclusions ?? {},
288
+ deadends: v2Deadends?.exclusions ?? {},
289
+ },
290
+ truncation: {
291
+ summary: resumeTruncated,
292
+ knowledge: limitedKnowledgeResult.truncated,
293
+ deadends: limitedDeadendResult.truncated,
294
+ notes: limitedNotesResult.truncated,
295
+ },
296
+ },
297
+ };
286
298
  };
299
+ return interfaceName === null
300
+ ? action()
301
+ : withUsageTelemetry(startPath, 'orient', 'agent', options, action, interfaceName);
302
+ }
303
+ export async function contextUsageReport(startPath, options = {}) {
304
+ return buildUsageReport(startPath, options);
287
305
  }
288
306
  export async function recordContextUse(startPath, input) {
289
307
  return recordUse(startPath, input);
290
308
  }
309
+ export { inspectRecipe, inspectSkill, listRecipes, listSkills };
310
+ export async function proposeContextEvolution(startPath, input = {}) {
311
+ return proposeEvolution(startPath, input);
312
+ }
313
+ export async function listContextEvolutionProposals(startPath) {
314
+ return listEvolutionProposals(startPath);
315
+ }
316
+ export async function inspectContextEvolutionProposal(startPath, proposalId) {
317
+ return inspectEvolutionProposal(startPath, proposalId);
318
+ }
319
+ export async function acceptContextEvolutionCandidate(startPath, input) {
320
+ return acceptEvolutionCandidate(startPath, input);
321
+ }
322
+ export async function transitionContextEvolutionProposal(startPath, input) {
323
+ return transitionEvolutionProposal(startPath, input);
324
+ }
325
+ export async function materializeContextRecipe(startPath, input) {
326
+ return materializeRecipe(startPath, input);
327
+ }
328
+ export async function transitionContextRecipe(startPath, input) {
329
+ return transitionRecipe(startPath, input);
330
+ }
331
+ export async function materializeContextSkill(startPath, input) {
332
+ return materializeSkill(startPath, input);
333
+ }
334
+ export async function transitionContextSkill(startPath, input) {
335
+ return transitionSkill(startPath, input);
336
+ }
337
+ export async function recordContextSkillInvocation(startPath, input) {
338
+ return recordSkillInvocation(startPath, input);
339
+ }
340
+ export async function recordContextSkillOutcome(startPath, input) {
341
+ return recordSkillOutcome(startPath, input);
342
+ }
291
343
  export async function searchContext(startPath, query, options = {}) {
292
344
  const projectRoot = await resolveProjectRoot(startPath);
293
345
  const cleanQuery = query.trim();
@@ -1,6 +1,12 @@
1
1
  export * from './api.js';
2
+ export type { MaterializeRecipeInput, TransitionRecipeInput, } from '../cognition/recipes.js';
3
+ export type { MaterializeSkillInput, RecordSkillInvocationInput, RecordSkillOutcomeInput, TransitionSkillInput, } from '../cognition/skills.js';
4
+ export type { AcceptEvolutionCandidateInput, ProposeEvolutionInput, ProposeEvolutionResult, TransitionEvolutionProposalInput, } from '../cognition/evolution.js';
5
+ export type { EvolutionCandidateArtifact, EvolutionLineageReference, EvolutionProposal, EvolutionProposalState, Experience, } from '../cognition/types.js';
2
6
  export type { ContextOrientContext, ContextUseInput, ContextOrientExplanation, ContextOrientNoContextResult, ContextOrientOptions, ContextOrientReadyResult, ContextOrientResult, ContextOrientSnapshot, } from './types.js';
3
7
  export type { AgentLoadOptions, AgentLoadResult, AgentSaveResult, ContextSearchHit, ContextSearchOptions, ContextSearchResult, } from '../runtime/types.js';
8
+ export { contextCompile } from './api.js';
4
9
  export { compileContext } from '../compiler/compile.js';
5
10
  export { canonicalJson, contentHash, estimateTokens, rebuildManifestHash, validateManifestIdentity } from '../compiler/identity.js';
6
- export type { ContextActivityRecord, ContextAnchor, ContextBudget, ContextBudgetReport, ContextCandidate, ContextCandidateSection, ContextCompileOptions, ContextExpandInput, ContextExpandLevel, ContextExpandResult, ContextExclusion, ContextExclusionReason, ContextIntent, ContextIntentSignal, ContextLevel, ContextManifest, ContextProvenance, ContextScenario, ContextSourceDescriptor, ContextTruncation, } from '../compiler/types.js';
11
+ export type { ContextActivityRecord, ContextAnchor, ContextBudget, ContextBudgetReport, ContextCandidate, ContextCandidateSection, ContextCompileOptions, ContextRecipeParticipation, ContextExpandInput, ContextExpandLevel, ContextExpandResult, ContextExclusion, ContextExclusionReason, ContextIntent, ContextIntentSignal, ContextLevel, ContextManifest, ContextProvenance, ContextScenario, ContextSourceDescriptor, ContextTruncation, } from '../compiler/types.js';
12
+ export type { CompilerRecipe, CompilerRecipeState } from '../cognition/types.js';
@@ -1,3 +1,4 @@
1
1
  export * from './api.js';
2
+ export { contextCompile } from './api.js';
2
3
  export { compileContext } from '../compiler/compile.js';
3
4
  export { canonicalJson, contentHash, estimateTokens, rebuildManifestHash, validateManifestIdentity } from '../compiler/identity.js';