fuli-context 0.3.0 → 0.4.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 (87) hide show
  1. package/README.md +0 -24
  2. package/dist/web/assets/ConnectionsPage-CCKWm-6p.js +1 -0
  3. package/dist/web/assets/KnowledgeInspector-BZkB2wJn.js +1 -0
  4. package/dist/web/assets/KnowledgeInspector-CrGYxEob.css +1 -0
  5. package/dist/web/assets/KnowledgeOrganizerPage-CJCNI2Wd.css +1 -0
  6. package/dist/web/assets/KnowledgeOrganizerPage-PXH-TlGt.js +1 -0
  7. package/dist/web/assets/KnowledgePage-CNQXd15y.js +1 -0
  8. package/dist/web/assets/KnowledgeWorkspace-CXPE4GCD.js +4 -0
  9. package/dist/web/assets/OverviewPage-ul7g7HXh.js +1 -0
  10. package/dist/web/assets/PersonalProfilePage-CC40Pw2L.js +1 -0
  11. package/dist/web/assets/PersonalProfilePage-S8ZcG2LU.css +1 -0
  12. package/dist/web/assets/PersonalProjectsPage-CPIEcShD.js +1 -0
  13. package/dist/web/assets/PublicProjectsPage-C700sR7z.js +1 -0
  14. package/dist/web/assets/ReviewPage-7MDDUDgA.js +1 -0
  15. package/dist/web/assets/console-B_Ena2DS.js +5 -0
  16. package/dist/web/assets/identity-CjBwYSMX.js +1 -0
  17. package/dist/web/assets/index-BQRkOwPx.js +2 -0
  18. package/dist/web/assets/{index-Devf7glZ.css → index-CpLDx3of.css} +1 -1
  19. package/dist/web/assets/model-C-5RVIJ2.js +1 -0
  20. package/dist/web/assets/{paths-B790iniO.js → paths-BkULqmGI.js} +1 -1
  21. package/dist/web/assets/{vue-router-BYTAoWT3.js → vue-router-DnD3hUm7.js} +1 -1
  22. package/dist/web/index.html +5 -6
  23. package/graph-provider/fuli_graph/app.py +48 -0
  24. package/graph-provider/fuli_graph/common_knowledge.py +443 -0
  25. package/graph-provider/fuli_graph/common_knowledge_models.py +54 -0
  26. package/graph-provider/fuli_graph/graph_models.py +9 -0
  27. package/graph-provider/fuli_graph/graph_query.py +25 -0
  28. package/graph-provider/fuli_graph/knowledge_audit.py +211 -0
  29. package/graph-provider/fuli_graph/knowledge_feedback_models.py +69 -0
  30. package/graph-provider/fuli_graph/knowledge_management.py +37 -0
  31. package/graph-provider/fuli_graph/knowledge_search.py +24 -2
  32. package/graph-provider/fuli_graph/models.py +69 -0
  33. package/graph-provider/fuli_graph/search_projection.py +59 -0
  34. package/graph-provider/fuli_graph/store_knowledge.py +2 -0
  35. package/graph-provider/pyproject.toml +1 -1
  36. package/npm-shrinkwrap.json +97 -2
  37. package/package.json +2 -1
  38. package/skills/capturing-session-knowledge/SKILL.md +83 -13
  39. package/src/agent-tools/graph-definitions.js +181 -13
  40. package/src/agent-tools/graph-handlers.js +19 -0
  41. package/src/cli/command-registry.js +1 -56
  42. package/src/cli.js +2 -37
  43. package/src/graphiti/agent-knowledge-workflows.js +543 -0
  44. package/src/graphiti/federated-application.js +85 -104
  45. package/src/graphiti/knowledge-provider-mapping.js +91 -0
  46. package/src/graphiti/provider-client.js +15 -0
  47. package/src/graphiti/source-uri.js +29 -0
  48. package/src/mcp/common-knowledge-preview-tokens.js +67 -0
  49. package/src/mcp/create-mcp-server.js +43 -7
  50. package/src/mcp/instructions.js +6 -7
  51. package/src/mcp/lifecycle-audit.js +27 -0
  52. package/src/mcp/task-context-registry.js +93 -0
  53. package/src/mcp/tool-annotations.js +16 -1
  54. package/src/mcp/tool-result.js +24 -0
  55. package/src/runtime-options.js +7 -8
  56. package/src/setup/agent-installation-status.js +14 -2
  57. package/src/setup/agents.js +12 -1
  58. package/src/setup/claude-code-config.js +194 -0
  59. package/src/setup/config-backup.js +20 -5
  60. package/dist/web/assets/ConnectionsPage-CY6H_mJJ.js +0 -1
  61. package/dist/web/assets/KnowledgeInspector-BSpz5Sfm.js +0 -1
  62. package/dist/web/assets/KnowledgeInspector-CHbB_0rC.css +0 -1
  63. package/dist/web/assets/KnowledgeOrganizerPage-BlEvhPrv.css +0 -1
  64. package/dist/web/assets/KnowledgeOrganizerPage-CvjGPlUc.js +0 -1
  65. package/dist/web/assets/KnowledgePage-Khuo3cNs.js +0 -1
  66. package/dist/web/assets/KnowledgeWorkspace-DVZusvem.js +0 -4
  67. package/dist/web/assets/OverviewPage-DC4C4Fhn.js +0 -1
  68. package/dist/web/assets/PersonalProfilePage-CSV9Mtgz.js +0 -1
  69. package/dist/web/assets/PersonalProfilePage-CVJ3TcRi.css +0 -1
  70. package/dist/web/assets/PersonalProjectsPage-A50EaseQ.js +0 -1
  71. package/dist/web/assets/PublicProjectsPage-BAdO_bae.js +0 -1
  72. package/dist/web/assets/ReviewPage-Dztk2jDG.js +0 -1
  73. package/dist/web/assets/console-tbvTUi6p.js +0 -1
  74. package/dist/web/assets/identity-B3s5CXhv.js +0 -1
  75. package/dist/web/assets/index-DwUDhQKc.js +0 -6
  76. package/dist/web/assets/model-DVT8sjy9.js +0 -1
  77. package/dist/web/assets/runtime-dom.esm-bundler-DttsK21F.js +0 -1
  78. package/src/cli/commands/candidates.js +0 -17
  79. package/src/cli/commands/ingestion.js +0 -32
  80. package/src/cli/commands/reads.js +0 -50
  81. package/src/cli/commands/spaces.js +0 -20
  82. package/src/cli/invocation.js +0 -47
  83. package/src/cli/migrate-command.js +0 -71
  84. package/src/cli/migration-destination.js +0 -117
  85. package/src/storage/import-json.js +0 -70
  86. package/src/storage/legacy-record-validation.js +0 -152
  87. package/src/storage/legacy-snapshot-validation.js +0 -32
@@ -14,6 +14,12 @@ const label = boundedString(512);
14
14
  const shortText = boundedString(2048);
15
15
  const longText = boundedString(8192);
16
16
  const dateTime = { ...boundedString(64), format: 'date-time' };
17
+ const sourceUri = {
18
+ type: ['string', 'null'],
19
+ minLength: 1,
20
+ maxLength: 2048,
21
+ pattern: '^[Hh][Tt][Tt][Pp][Ss]?://\\S+$'
22
+ };
17
23
  const attributes = { type: 'object', additionalProperties: true };
18
24
  const epistemicQuadrant = enumSchema([
19
25
  'known_known', 'known_unknown', 'unknown_known', 'unknown_unknown'
@@ -29,6 +35,10 @@ const confirmationActor = objectSchema({
29
35
  kind: enumSchema(['user', 'agent', 'authoritative_source', 'import']),
30
36
  label: nullableStringSchema()
31
37
  }, ['kind']);
38
+ const decisionActor = objectSchema({
39
+ kind: enumSchema(['user', 'agent', 'authoritative_source']),
40
+ label: nullableStringSchema()
41
+ }, ['kind']);
32
42
  const confirmationBasis = objectSchema({
33
43
  existenceReason: boundedString(4096),
34
44
  quadrantReason: boundedString(4096),
@@ -155,12 +165,70 @@ const personalProjectActionIntent = {
155
165
  const personalProjectActionRequired = [
156
166
  'personalSpaceId', 'itemKind', 'itemId', 'mode', 'reason'
157
167
  ];
168
+ const commonKnowledgePromotionIntent = {
169
+ personalSpaceId: id,
170
+ parentProjectId: id,
171
+ itemKind: knowledgeItemKind,
172
+ canonicalItemId: id,
173
+ duplicateItemIds: arraySchema(id, { minItems: 1, maxItems: 31 }),
174
+ reason: boundedString(2000),
175
+ humanConfirmationReason: boundedString(2000)
176
+ };
177
+ const commonKnowledgePromotionRequired = [
178
+ 'personalSpaceId', 'parentProjectId', 'itemKind', 'canonicalItemId',
179
+ 'duplicateItemIds', 'reason', 'humanConfirmationReason'
180
+ ];
181
+ const captureEpisodeFields = {
182
+ idempotencyKey: id,
183
+ name: label,
184
+ sourceKind: boundedString(128),
185
+ sourceDescription: boundedString(1024),
186
+ sourceUri,
187
+ sourceApplication,
188
+ sourceTurnId: { type: ['string', 'null'], minLength: 1, maxLength: 256 },
189
+ sourceExcerpt: { type: ['string', 'null'], minLength: 1, maxLength: 2048 },
190
+ referenceTime: dateTime,
191
+ summary: longText,
192
+ sensitivity: enumSchema(['normal', 'private', 'restricted']),
193
+ entities: arraySchema(entity, { minItems: 1, maxItems: 128 }),
194
+ relationships: arraySchema(relationship, { maxItems: 256 })
195
+ };
196
+ const captureEpisodeRequired = [
197
+ 'idempotencyKey', 'name', 'sourceKind', 'sourceDescription',
198
+ 'referenceTime', 'entities', 'relationships'
199
+ ];
158
200
 
159
201
  export const GRAPH_TOOL_DEFINITIONS = [
202
+ {
203
+ name: 'begin_task_context',
204
+ title: 'LIFECYCLE · Begin a Fuli-aware task',
205
+ description: 'Lifecycle entry used by supported Agent hooks before the model processes a user prompt. It resolves the exact local project from projectPath, loads effective collaboration preferences, and returns an opaque taskContextToken. It never stores projectPath. Claude Code setup installs this as a deterministic UserPromptSubmit hook; manual Agent calls are a fallback.',
206
+ inputSchema: objectSchema({
207
+ sessionId: id,
208
+ projectPath: boundedString(4096)
209
+ }, ['sessionId', 'projectPath'])
210
+ },
211
+ {
212
+ name: 'checkpoint_task_knowledge',
213
+ title: 'LIFECYCLE · Finish knowledge review',
214
+ description: 'Complete exactly one end-of-task knowledge review. Use capture_candidates only for a small durable batch supported by the task; captured Agent proposals remain pending unless the payload contains valid human or authoritative-source confirmation. Use retain_nothing when the turn produced no reusable knowledge. Never store raw transcripts, guesses, temporary logs, credentials, or disposable output.',
215
+ inputSchema: objectSchema({
216
+ taskContextToken: id,
217
+ disposition: enumSchema(['capture_candidates', 'retain_nothing']),
218
+ reason: boundedString(2000),
219
+ capture: objectSchema(captureEpisodeFields, captureEpisodeRequired)
220
+ }, ['taskContextToken', 'disposition', 'reason'])
221
+ },
222
+ {
223
+ name: 'verify_task_checkpoint',
224
+ title: 'LIFECYCLE · Verify end-of-task review',
225
+ description: 'Hook-facing read that returns a Claude Code Stop decision. It blocks stopping only when begin_task_context succeeded for the session and checkpoint_task_knowledge has not yet recorded capture_candidates or retain_nothing.',
226
+ inputSchema: objectSchema({ sessionId: id }, ['sessionId'])
227
+ },
160
228
  {
161
229
  name: 'get_collaboration_preferences',
162
230
  title: 'READ FIRST · Load task collaboration preferences',
163
- description: 'Call this exact tool name at the start of every user task before any other tool or answer; never substitute a project action tool. Pass projectPath as the current working directory; Fuli uses it transiently, never stores or returns it, and layers preferences only for one exact registered local project match. Returns effective_preferences plus deferred_conflicts intentionally queued for AI. Apply effective_preferences before answering or constructing tool arguments. Human-confirmed preferences outrank explicitly marked agent-confirmed preferences. If the current task would use a deferred conflict, call resolve_deferred_preference_conflict before applying either side; ignore unrelated deferred conflicts. For write tools, enforce preferences in the actual payload; mentioning them only in the final answer is not compliance. Personal-global preferences always apply; other conflicted, pending, invalid, ambiguous-project, and unrelated-project items never auto-apply. Automatic injection does not count as usage evidence. personalProjectId remains an explicit compatibility override.',
231
+ description: 'Fallback for Agents or tasks without begin_task_context hook context. When no hook context was supplied, call this exact tool name at the start of every user task before any other tool or answer; never substitute a project action tool. Do not call it redundantly when the entry hook already supplied preferences. Pass projectPath as the current working directory; Fuli uses it transiently, never stores or returns it, and layers preferences only for one exact registered local project match. Returns effective_preferences plus deferred_conflicts intentionally queued for AI. Apply effective_preferences before answering or constructing tool arguments. Human-confirmed preferences outrank explicitly marked agent-confirmed preferences. If the current task would use a deferred conflict, call resolve_deferred_preference_conflict before applying either side; ignore unrelated deferred conflicts. For write tools, enforce preferences in the actual payload; mentioning them only in the final answer is not compliance. Personal-global preferences always apply; other conflicted, pending, invalid, ambiguous-project, and unrelated-project items never auto-apply. Automatic injection does not count as usage evidence. personalProjectId remains an explicit compatibility override.',
164
232
  inputSchema: objectSchema({
165
233
  projectPath: boundedString(4096),
166
234
  personalProjectId: nullableStringSchema(),
@@ -185,34 +253,64 @@ export const GRAPH_TOOL_DEFINITIONS = [
185
253
  },
186
254
  {
187
255
  name: 'capture_session_knowledge',
188
- description: 'Silently batch durable session knowledge when the user-enabled capture policy allows it. Every item records its discovery quadrant, structured confirmation basis, and pending or confirmed state. A disabled policy returns capture_disabled without writing. Personal profile knowledge stays personal; only project knowledge with an auditable confirmation can enter public review.',
256
+ description: 'Silently batch durable session knowledge when the user-enabled capture policy allows it. When knowledge was read from an online source, sourceUri preserves the exact original HTTP(S) link so a capable Agent can re-read it later and refresh Fuli knowledge; it does not authorize source-document writeback. Every item records its discovery quadrant, structured confirmation basis, and pending or confirmed state. A disabled policy returns capture_disabled without writing. Personal profile knowledge stays personal; only project knowledge with an auditable confirmation can enter public review.',
189
257
  inputSchema: objectSchema({
190
258
  targetKind: enumSchema(['personal', 'project']),
191
259
  spaceId: id,
192
260
  personalProjectId: nullableStringSchema(),
193
261
  providerUrl: nullableStringSchema(),
194
- idempotencyKey: id,
195
262
  sessionId: id,
196
- name: label,
263
+ ...captureEpisodeFields
264
+ }, [
265
+ 'targetKind', 'spaceId', 'sessionId', ...captureEpisodeRequired
266
+ ])
267
+ },
268
+ {
269
+ name: 'record_decision_trace',
270
+ title: 'WRITE · Store a decision with its rationale',
271
+ description: 'Store one bounded, searchable project decision as a linked Decision, selected and rejected DecisionOption nodes, a DecisionRationale, and optional ValidationResult nodes. reason is mandatory: do not store a bare conclusion. User or authoritative-source decisions are confirmed with their audit basis; Agent-only proposals remain pending. Do not copy raw transcripts, credentials, private contact details, or disposable deliberation.',
272
+ inputSchema: objectSchema({
273
+ personalSpaceId: id,
274
+ personalProjectId: nullableStringSchema(),
275
+ sessionId: id,
276
+ idempotencyKey: id,
277
+ decisionKey: id,
278
+ title: label,
279
+ question: shortText,
280
+ selectedOption: objectSchema({
281
+ key: id,
282
+ label,
283
+ summary: nullableStringSchema()
284
+ }, ['key', 'label']),
285
+ rejectedOptions: arraySchema(objectSchema({
286
+ key: id,
287
+ label,
288
+ summary: nullableStringSchema()
289
+ }, ['key', 'label']), { maxItems: 16 }),
290
+ reason: boundedString(4096),
291
+ validationResults: arraySchema(objectSchema({
292
+ key: id,
293
+ outcome: enumSchema(['pass', 'fail', 'inconclusive']),
294
+ summary: shortText
295
+ }, ['key', 'outcome', 'summary']), { maxItems: 16 }),
296
+ decidedBy: decisionActor,
297
+ referenceTime: dateTime,
197
298
  sourceKind: boundedString(128),
198
299
  sourceDescription: boundedString(1024),
300
+ sourceUri,
199
301
  sourceApplication,
200
302
  sourceTurnId: { type: ['string', 'null'], minLength: 1, maxLength: 256 },
201
- sourceExcerpt: { type: ['string', 'null'], minLength: 1, maxLength: 2048 },
202
- referenceTime: dateTime,
203
- summary: longText,
204
- sensitivity: enumSchema(['normal', 'private', 'restricted']),
205
- entities: arraySchema(entity, { minItems: 1, maxItems: 128 }),
206
- relationships: arraySchema(relationship, { maxItems: 256 })
303
+ sensitivity: enumSchema(['normal', 'private', 'restricted'])
207
304
  }, [
208
- 'targetKind', 'spaceId', 'idempotencyKey', 'sessionId', 'name',
209
- 'sourceKind', 'sourceDescription', 'referenceTime', 'entities', 'relationships'
305
+ 'personalSpaceId', 'sessionId', 'idempotencyKey', 'decisionKey',
306
+ 'title', 'question', 'selectedOption', 'reason', 'decidedBy',
307
+ 'referenceTime', 'sourceKind', 'sourceDescription'
210
308
  ])
211
309
  },
212
310
  {
213
311
  name: 'search_knowledge_graph',
214
312
  title: 'READ · Search the scoped knowledge graph',
215
- description: 'Search durable context before saying you do not know when a task may depend on remembered URLs, routes, requirements, architecture, prior decisions, runbooks, rationale, or personal preferences. Pending knowledge is searchable and explicitly marked; agent-confirmed knowledge ranks below human-confirmed knowledge. The bounded scope includes the personal-global profile, exact active local project, selectively inheritable knowledge reached through PART_OF or USES_KNOWLEDGE_FROM, explicitly selected additional personal projects, and selected subscribed team projects. Generic RELATED_TO links never expand scope. Use all_local_confirmed only after explicit user confirmation; it searches registered local personal projects for this query and never expands public projects. If that still has no support, use read-only local file search in the current repository or workspace files within a safe root. The response includes sourceMarker for supporting results, noMatchSourceMarker when returned items do not support the answer, and retrievalGuidance with the required next action.',
313
+ description: 'Search durable context before saying you do not know when a task may depend on remembered URLs, routes, requirements, architecture, prior decisions, runbooks, rationale, or personal preferences. Supporting facts and entities expose bounded source_uris when their evidence came from online sources, allowing a capable Agent to re-read the original source before refreshing Fuli knowledge. Pending knowledge is searchable and explicitly marked; agent-confirmed knowledge ranks below human-confirmed knowledge. The bounded scope includes the personal-global profile, exact active local project, selectively inheritable knowledge reached through PART_OF or USES_KNOWLEDGE_FROM, explicitly selected additional personal projects, and selected subscribed team projects. Generic RELATED_TO links never expand scope. Use all_local_confirmed only after explicit user confirmation; it searches registered local personal projects for this query and never expands public projects. If that still has no support, use read-only local file search in the current repository or workspace files within a safe root. The response includes sourceMarker for supporting results, noMatchSourceMarker when returned items do not support the answer, and retrievalGuidance with the required next action.',
216
314
  inputSchema: objectSchema({
217
315
  personalSpaceId: id,
218
316
  query: shortText,
@@ -240,6 +338,76 @@ export const GRAPH_TOOL_DEFINITIONS = [
240
338
  }, ['itemId', 'itemKind', 'useKind']), { minItems: 1, maxItems: 200 })
241
339
  }, ['personalSpaceId', 'taskId', 'items'])
242
340
  },
341
+ {
342
+ name: 'record_knowledge_feedback',
343
+ title: 'WRITE · Record negative knowledge evidence',
344
+ description: 'Record bounded negative evidence when retained knowledge is rejected, fails validation, is contradicted, or becomes outdated. Each event requires a reason and evidence summary and is idempotent per task, item, feedback kind, and content generation. It lowers ranking signals and marks the item requires_attention. It never deletes evidence or silently overrides human-confirmed authority; trusted human/source contradiction can return agent-confirmed knowledge to pending.',
345
+ inputSchema: objectSchema({
346
+ personalSpaceId: id,
347
+ taskId: id,
348
+ sessionId: nullableStringSchema(),
349
+ toolName: nullableStringSchema(),
350
+ items: arraySchema(objectSchema({
351
+ itemId: id,
352
+ itemKind: knowledgeItemKind,
353
+ feedbackKind: enumSchema([
354
+ 'rejected', 'validation_failed', 'contradicted', 'outdated'
355
+ ]),
356
+ reason: boundedString(2000),
357
+ evidenceSummary: boundedString(4096),
358
+ reportedByKind: enumSchema([
359
+ 'user', 'agent', 'authoritative_source'
360
+ ]),
361
+ sourceUri
362
+ }, [
363
+ 'itemId', 'itemKind', 'feedbackKind', 'reason',
364
+ 'evidenceSummary', 'reportedByKind'
365
+ ]), { minItems: 1, maxItems: 200 })
366
+ }, ['personalSpaceId', 'taskId', 'items'])
367
+ },
368
+ {
369
+ name: 'search_current_project_knowledge',
370
+ title: 'READ · Search current project and its knowledge sources',
371
+ description: 'High-level project search for normal Agent work. Pass projectPath plus one or more focused queries; Fuli resolves the exact local project, searches its local knowledge first, then follows outgoing PART_OF or USES_KNOWLEDGE_FROM relations to authorized parent/source knowledge. An exact current-project item with the same stable key overrides an inherited item. The tool never guesses an ambiguous project and never traverses RELATED_TO.',
372
+ inputSchema: objectSchema({
373
+ projectPath: boundedString(4096),
374
+ queries: arraySchema(shortText, { minItems: 1, maxItems: 10 }),
375
+ limitPerQuery: integerSchema({ minimum: 1, maximum: 50 }),
376
+ includeHistorical: booleanSchema(),
377
+ includePending: booleanSchema()
378
+ }, ['projectPath', 'queries'])
379
+ },
380
+ {
381
+ name: 'discover_common_knowledge_candidates',
382
+ title: 'READ · Find possible parent-project knowledge',
383
+ description: 'Read-only governance aid for one parent personal project. It searches each direct PART_OF child independently, excludes inherited and personal-global results, and groups lexically similar knowledge found in multiple children. Similarity is only an inferred candidate signal: the tool never merges, promotes, rewrites, or invalidates knowledge, and every candidate requires explicit human confirmation before a separate promotion action.',
384
+ inputSchema: objectSchema({
385
+ personalSpaceId: id,
386
+ parentProjectId: id,
387
+ query: shortText,
388
+ minChildProjects: integerSchema({ minimum: 2, maximum: 32 }),
389
+ similarityThreshold: numberSchema({ minimum: 0, maximum: 1 }),
390
+ limitPerProject: integerSchema({ minimum: 1, maximum: 50 })
391
+ }, ['personalSpaceId', 'parentProjectId', 'query'])
392
+ },
393
+ {
394
+ name: 'preview_common_knowledge_promotion',
395
+ title: 'CONFIRM · Preview parent-project promotion',
396
+ description: 'Validate the exact common-knowledge promotion after a human has explicitly chosen the canonical item, duplicates, parent project, and rationale. The preview is read-only, requires items from distinct direct PART_OF children, and returns a short-lived token binding the exact intent. Do not treat inferred lexical similarity as human confirmation.',
397
+ inputSchema: objectSchema(
398
+ commonKnowledgePromotionIntent,
399
+ commonKnowledgePromotionRequired
400
+ )
401
+ },
402
+ {
403
+ name: 'apply_common_knowledge_promotion',
404
+ title: 'WRITE · Atomically promote parent knowledge',
405
+ description: 'Apply one human-confirmed preview as a single Provider mutation. It moves the canonical item to the parent, enables descendants inheritance, invalidates selected child duplicates with replacement links, and stores both the scope reason and human-confirmation reason in permanent revision history. A valid one-time previewToken is mandatory.',
406
+ inputSchema: objectSchema({
407
+ ...commonKnowledgePromotionIntent,
408
+ previewToken: id
409
+ }, [...commonKnowledgePromotionRequired, 'previewToken'])
410
+ },
243
411
  {
244
412
  name: 'get_knowledge_graph',
245
413
  description: 'Return a bounded node-edge graph for a personal space, one exact local personal project, or a team-shared project.',
@@ -1,6 +1,10 @@
1
1
  import { ApplicationError } from '../app/application-error.js';
2
2
 
3
3
  const HANDLERS = Object.freeze({
4
+ begin_task_context: (app, input) => app.beginTaskContext(input),
5
+ checkpoint_task_knowledge: (app, input) =>
6
+ app.checkpointTaskKnowledge(input),
7
+ verify_task_checkpoint: (app, input) => app.verifyTaskCheckpoint(input),
4
8
  get_collaboration_preferences: (app, input) => app.getCollaborationPreferences({
5
9
  ...input,
6
10
  agentInvocation: true,
@@ -12,12 +16,27 @@ const HANDLERS = Object.freeze({
12
16
  operationActor: 'agent'
13
17
  }),
14
18
  capture_session_knowledge: (app, input) => app.captureSessionKnowledge(input),
19
+ record_decision_trace: (app, input) => app.recordDecisionTrace(input),
15
20
  search_knowledge_graph: (app, input) => app.searchKnowledge({
16
21
  ...input,
17
22
  agentInvocation: true,
18
23
  agentToolName: 'search_knowledge_graph'
19
24
  }),
20
25
  record_knowledge_usage: (app, input) => app.recordKnowledgeUsage(input),
26
+ record_knowledge_feedback: (app, input) => app.recordKnowledgeFeedback(input),
27
+ search_current_project_knowledge: (app, input) =>
28
+ app.searchCurrentProjectKnowledge(input),
29
+ discover_common_knowledge_candidates: (app, input) =>
30
+ app.discoverCommonKnowledgeCandidates(input),
31
+ preview_common_knowledge_promotion: (app, input) =>
32
+ app.previewCommonKnowledgePromotion(input),
33
+ apply_common_knowledge_promotion: (app, input) => {
34
+ const { previewToken: _previewToken, ...promotion } = input;
35
+ return app.applyCommonKnowledgePromotion({
36
+ ...promotion,
37
+ operationActor: 'agent'
38
+ });
39
+ },
21
40
  get_knowledge_graph: (app, input) => app.getKnowledgeGraph({
22
41
  ...input,
23
42
  agentInvocation: true,
@@ -1,42 +1,5 @@
1
- import { candidate, candidates } from './commands/candidates.js';
2
- import { observe, remember } from './commands/ingestion.js';
3
- import { context, history, rules, search, timeline } from './commands/reads.js';
4
- import { createSpace, subscribe } from './commands/spaces.js';
5
-
6
- const COMMANDS = Object.freeze({
7
- space: createSpace,
8
- subscribe,
9
- remember,
10
- observe,
11
- search,
12
- timeline,
13
- rules,
14
- history,
15
- context,
16
- candidates,
17
- candidate
18
- });
19
-
20
1
  const LOCAL_RUNTIME_COMMANDS = new Set(['start', 'stop', 'restart', 'status', 'open']);
21
2
 
22
- export function dispatchCommand(app, command, args) {
23
- const handler = COMMANDS[command];
24
- if (!handler) throw new Error(`Unknown command: ${command}`);
25
- return handler(app, args);
26
- }
27
-
28
- export function isRegisteredCommand(command) {
29
- return command === '--help' || command === '-h' ||
30
- command === '--version' || command === '-v' ||
31
- Object.hasOwn(COMMANDS, command);
32
- }
33
-
34
- export function isCliCommand(command) {
35
- return command === 'setup' || command === 'update' ||
36
- command === 'uninstall' || command === 'migrate' ||
37
- LOCAL_RUNTIME_COMMANDS.has(command) || isRegisteredCommand(command);
38
- }
39
-
40
3
  export function isLocalRuntimeCommand(command) {
41
4
  return LOCAL_RUNTIME_COMMANDS.has(command);
42
5
  }
@@ -58,23 +21,5 @@ Local service:
58
21
  Install and Agent connection:
59
22
  setup [--yes] [--codex-only] [--data-dir DIR] [--personal-space NAME] [--port PORT] [--skip-agents] [--no-start] [--personal-only|--with-dev-public]
60
23
  update [setup options]
61
- uninstall [--yes] [--data-dir DIR]
62
-
63
- Legacy local knowledge commands:
64
- fuli [--db SQLITE_DB] [--personal-space 我] <command>
65
- Global options must appear before the command.
66
-
67
- Commands:
68
- space create NAME --kind personal|public
69
- subscribe PERSONAL_SPACE PUBLIC_SPACE
70
- remember PERSONAL_SPACE --target SPACE --source-kind prd --text TEXT
71
- observe PERSONAL_SPACE --target SPACE
72
- search PERSONAL_SPACE QUERY
73
- timeline SPACE SUBJECT
74
- rules SPACE
75
- history SPACE PREDICATE
76
- context PERSONAL_SPACE SPACE QUERY
77
- candidates PERSONAL_SPACE
78
- candidate CANDIDATE_ID sync|personal_only|ignore
79
- migrate --from LEGACY_JSON --to SQLITE_DB`);
24
+ uninstall [--yes] [--data-dir DIR]`);
80
25
  }
package/src/cli.js CHANGED
@@ -3,78 +3,43 @@ import { realpathSync } from 'node:fs';
3
3
  import { fileURLToPath } from 'node:url';
4
4
 
5
5
  import {
6
- dispatchCommand,
7
6
  isLocalRuntimeCommand,
8
- isRegisteredCommand,
9
7
  printHelp
10
8
  } from './cli/command-registry.js';
11
- import { parseCliInvocation } from './cli/invocation.js';
12
9
  import { runLocalRuntimeCommand } from './cli/local-runtime-command.js';
13
- import { migrateLegacyJson } from './cli/migrate-command.js';
14
10
  import { runSetupCommand } from './cli/setup-command.js';
15
11
  import { runUninstallCommand } from './cli/uninstall-command.js';
16
12
  import { runUpdateCommand } from './cli/update-command.js';
17
13
  import { FULI_VERSION } from './package-metadata.js';
18
14
  import { assertSupportedNodeVersion } from './setup/node-runtime.js';
19
- import {
20
- openLocalApplication,
21
- resolveRuntimeOptions
22
- } from './runtime-options.js';
23
15
 
24
16
  export async function main(argv = process.argv.slice(2), env = process.env) {
25
- const { runtimeArgs, command, args: commandArgs } = parseCliInvocation(argv);
17
+ const [command, ...commandArgs] = argv;
26
18
  if (!command || command === '--help' || command === '-h') {
27
- if (runtimeArgs.length) resolveRuntimeOptions(runtimeArgs, {});
28
19
  printHelp();
29
20
  return;
30
21
  }
31
22
  if (command === '--version' || command === '-v') {
32
- if (runtimeArgs.length) resolveRuntimeOptions(runtimeArgs, {});
33
23
  console.log(FULI_VERSION);
34
24
  return;
35
25
  }
36
- if (command === 'migrate') {
37
- if (runtimeArgs.length) resolveRuntimeOptions(runtimeArgs, {});
38
- console.log(JSON.stringify(migrateLegacyJson(commandArgs)));
39
- return;
40
- }
41
26
  if (command === 'setup') {
42
- if (runtimeArgs.length) {
43
- throw new TypeError('Setup options must appear after the setup command');
44
- }
45
27
  assertSupportedNodeVersion();
46
28
  await runSetupCommand(commandArgs, { env });
47
29
  return;
48
30
  }
49
31
  if (command === 'uninstall') {
50
- if (runtimeArgs.length) {
51
- throw new TypeError('Uninstall options must appear after the uninstall command');
52
- }
53
32
  await runUninstallCommand(commandArgs, { env });
54
33
  return;
55
34
  }
56
35
  if (command === 'update') {
57
- if (runtimeArgs.length) {
58
- throw new TypeError('Update options must appear after the update command');
59
- }
60
36
  assertSupportedNodeVersion();
61
37
  return runUpdateCommand(commandArgs, { env });
62
38
  }
63
39
  if (isLocalRuntimeCommand(command)) {
64
- if (runtimeArgs.length) {
65
- throw new TypeError(`${command} options must appear after the command`);
66
- }
67
40
  return runLocalRuntimeCommand(command, commandArgs, { env });
68
41
  }
69
- if (!isRegisteredCommand(command)) throw new Error(`Unknown command: ${command}`);
70
-
71
- const options = resolveRuntimeOptions(runtimeArgs, env);
72
- const app = openLocalApplication(options);
73
- try {
74
- dispatchCommand(app, command, commandArgs);
75
- } finally {
76
- app.close();
77
- }
42
+ throw new Error(`Unknown command: ${command}`);
78
43
  }
79
44
 
80
45
  if (isMainModule(import.meta.url, process.argv[1])) {