fuli-context 0.3.0 → 0.5.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 (95) hide show
  1. package/README.en.md +336 -0
  2. package/README.md +220 -174
  3. package/dist/web/assets/ConnectionsPage-CCKWm-6p.js +1 -0
  4. package/dist/web/assets/KnowledgeInspector-BZkB2wJn.js +1 -0
  5. package/dist/web/assets/KnowledgeInspector-CrGYxEob.css +1 -0
  6. package/dist/web/assets/KnowledgeOrganizerPage-CZAF_OlN.css +1 -0
  7. package/dist/web/assets/KnowledgeOrganizerPage-DlvcLwlL.js +1 -0
  8. package/dist/web/assets/KnowledgePage-CNQXd15y.js +1 -0
  9. package/dist/web/assets/KnowledgeWorkspace-CXPE4GCD.js +4 -0
  10. package/dist/web/assets/OverviewPage-ul7g7HXh.js +1 -0
  11. package/dist/web/assets/PersonalProfilePage-CC40Pw2L.js +1 -0
  12. package/dist/web/assets/PersonalProfilePage-S8ZcG2LU.css +1 -0
  13. package/dist/web/assets/PersonalProjectsPage-CPIEcShD.js +1 -0
  14. package/dist/web/assets/PublicProjectsPage-C700sR7z.js +1 -0
  15. package/dist/web/assets/ReviewPage-7MDDUDgA.js +1 -0
  16. package/dist/web/assets/console-B_Ena2DS.js +5 -0
  17. package/dist/web/assets/identity-CjBwYSMX.js +1 -0
  18. package/dist/web/assets/{index-Devf7glZ.css → index-CpLDx3of.css} +1 -1
  19. package/dist/web/assets/index-DAzeFJSc.js +2 -0
  20. package/dist/web/assets/model-C-5RVIJ2.js +1 -0
  21. package/dist/web/assets/{paths-B790iniO.js → paths-BkULqmGI.js} +1 -1
  22. package/dist/web/assets/{vue-router-BYTAoWT3.js → vue-router-DnD3hUm7.js} +1 -1
  23. package/dist/web/index.html +5 -6
  24. package/graph-provider/fuli_graph/app.py +48 -0
  25. package/graph-provider/fuli_graph/common_knowledge.py +443 -0
  26. package/graph-provider/fuli_graph/common_knowledge_models.py +54 -0
  27. package/graph-provider/fuli_graph/graph_models.py +9 -0
  28. package/graph-provider/fuli_graph/graph_query.py +25 -0
  29. package/graph-provider/fuli_graph/knowledge_audit.py +211 -0
  30. package/graph-provider/fuli_graph/knowledge_feedback_models.py +69 -0
  31. package/graph-provider/fuli_graph/knowledge_management.py +37 -0
  32. package/graph-provider/fuli_graph/knowledge_search.py +24 -2
  33. package/graph-provider/fuli_graph/models.py +69 -0
  34. package/graph-provider/fuli_graph/search_projection.py +59 -0
  35. package/graph-provider/fuli_graph/store_knowledge.py +2 -0
  36. package/graph-provider/pyproject.toml +1 -1
  37. package/npm-shrinkwrap.json +97 -2
  38. package/package.json +3 -1
  39. package/skills/capturing-session-knowledge/SKILL.md +83 -13
  40. package/src/agent-tools/graph-definitions.js +181 -13
  41. package/src/agent-tools/graph-handlers.js +19 -0
  42. package/src/cli/command-registry.js +3 -58
  43. package/src/cli/local-runtime-command.js +16 -2
  44. package/src/cli/local-runtime-options.js +6 -2
  45. package/src/cli.js +2 -37
  46. package/src/graphiti/agent-knowledge-workflows.js +543 -0
  47. package/src/graphiti/federated-application.js +85 -104
  48. package/src/graphiti/knowledge-provider-mapping.js +91 -0
  49. package/src/graphiti/provider-client.js +15 -0
  50. package/src/graphiti/source-uri.js +29 -0
  51. package/src/http/request-policy.js +37 -2
  52. package/src/local-runtime/lifecycle.js +11 -1
  53. package/src/mcp/common-knowledge-preview-tokens.js +67 -0
  54. package/src/mcp/create-mcp-server.js +43 -7
  55. package/src/mcp/instructions.js +6 -7
  56. package/src/mcp/lifecycle-audit.js +27 -0
  57. package/src/mcp/task-context-registry.js +93 -0
  58. package/src/mcp/tool-annotations.js +16 -1
  59. package/src/mcp/tool-result.js +24 -0
  60. package/src/runtime-options.js +7 -8
  61. package/src/server/lan-access.js +52 -0
  62. package/src/server.js +67 -8
  63. package/src/setup/agent-installation-status.js +14 -2
  64. package/src/setup/agents.js +12 -1
  65. package/src/setup/claude-code-config.js +194 -0
  66. package/src/setup/config-backup.js +20 -5
  67. package/src/setup/graph-runtime.js +78 -9
  68. package/dist/web/assets/ConnectionsPage-CY6H_mJJ.js +0 -1
  69. package/dist/web/assets/KnowledgeInspector-BSpz5Sfm.js +0 -1
  70. package/dist/web/assets/KnowledgeInspector-CHbB_0rC.css +0 -1
  71. package/dist/web/assets/KnowledgeOrganizerPage-BlEvhPrv.css +0 -1
  72. package/dist/web/assets/KnowledgeOrganizerPage-CvjGPlUc.js +0 -1
  73. package/dist/web/assets/KnowledgePage-Khuo3cNs.js +0 -1
  74. package/dist/web/assets/KnowledgeWorkspace-DVZusvem.js +0 -4
  75. package/dist/web/assets/OverviewPage-DC4C4Fhn.js +0 -1
  76. package/dist/web/assets/PersonalProfilePage-CSV9Mtgz.js +0 -1
  77. package/dist/web/assets/PersonalProfilePage-CVJ3TcRi.css +0 -1
  78. package/dist/web/assets/PersonalProjectsPage-A50EaseQ.js +0 -1
  79. package/dist/web/assets/PublicProjectsPage-BAdO_bae.js +0 -1
  80. package/dist/web/assets/ReviewPage-Dztk2jDG.js +0 -1
  81. package/dist/web/assets/console-tbvTUi6p.js +0 -1
  82. package/dist/web/assets/identity-B3s5CXhv.js +0 -1
  83. package/dist/web/assets/index-DwUDhQKc.js +0 -6
  84. package/dist/web/assets/model-DVT8sjy9.js +0 -1
  85. package/dist/web/assets/runtime-dom.esm-bundler-DttsK21F.js +0 -1
  86. package/src/cli/commands/candidates.js +0 -17
  87. package/src/cli/commands/ingestion.js +0 -32
  88. package/src/cli/commands/reads.js +0 -50
  89. package/src/cli/commands/spaces.js +0 -20
  90. package/src/cli/invocation.js +0 -47
  91. package/src/cli/migrate-command.js +0 -71
  92. package/src/cli/migration-destination.js +0 -117
  93. package/src/storage/import-json.js +0 -70
  94. package/src/storage/legacy-record-validation.js +0 -152
  95. package/src/storage/legacy-snapshot-validation.js +0 -32
@@ -1,3 +1,5 @@
1
+ from datetime import datetime
2
+
1
3
  from graphiti_core.edges import EntityEdge
2
4
 
3
5
  from .models import (
@@ -35,6 +37,9 @@ async def read_edge_epistemic_metadata(store, edge_ids: list[str]) -> dict[str,
35
37
  '''
36
38
  MATCH ()-[edge:RELATES_TO]->()
37
39
  WHERE edge.uuid IN $edge_ids
40
+ OPTIONAL MATCH (episode:Episodic)
41
+ WHERE episode.uuid IN coalesce(edge.episodes, [])
42
+ WITH edge, collect(DISTINCT episode) AS episodes
38
43
  RETURN edge.uuid AS id,
39
44
  coalesce(edge.fuli_origin_quadrant, 'known_known') AS origin_quadrant,
40
45
  coalesce(edge.fuli_current_quadrant, 'known_known') AS current_quadrant,
@@ -68,6 +73,19 @@ async def read_edge_epistemic_metadata(store, edge_ids: list[str]) -> dict[str,
68
73
  ,coalesce(edge.fuli_distinct_task_count, 0)
69
74
  AS distinct_task_count
70
75
  ,edge.fuli_last_used_at AS last_used_at
76
+ ,coalesce(edge.fuli_negative_evidence_count, 0)
77
+ AS negative_evidence_count
78
+ ,coalesce(edge.fuli_requires_attention, false)
79
+ AS requires_attention
80
+ ,edge.fuli_last_feedback_kind AS last_feedback_kind
81
+ ,edge.fuli_last_feedback_at AS last_feedback_at
82
+ ,[episode IN episodes
83
+ WHERE episode.fuli_source_uri IS NOT NULL |
84
+ {
85
+ uri: episode.fuli_source_uri,
86
+ reference_time: episode.valid_at,
87
+ created_at: episode.created_at
88
+ }] AS source_references
71
89
  ''',
72
90
  edge_ids=edge_ids,
73
91
  routing_='r',
@@ -140,12 +158,53 @@ def fact_result(
140
158
  qualified_use_count=int(metadata.get('qualified_use_count') or 0),
141
159
  distinct_task_count=int(metadata.get('distinct_task_count') or 0),
142
160
  last_used_at=_native_datetime(metadata.get('last_used_at')),
161
+ negative_evidence_count=int(
162
+ metadata.get('negative_evidence_count') or 0
163
+ ),
164
+ requires_attention=metadata.get('requires_attention') is True,
165
+ last_feedback_kind=metadata.get('last_feedback_kind'),
166
+ last_feedback_at=_native_datetime(metadata.get('last_feedback_at')),
143
167
  scope_distance=int(metadata.get('scope_distance') or 0),
144
168
  inherited_from_project_id=metadata.get('inherited_from_project_id'),
145
169
  scope_path=metadata.get('scope_path') or [],
170
+ source_uris=source_uris_from_references(
171
+ metadata.get('source_references')
172
+ ),
146
173
  )
147
174
 
148
175
 
176
+ def source_uris_from_references(references, limit: int = 20) -> list[str]:
177
+ ranked = sorted(
178
+ (
179
+ (reference, index)
180
+ for index, reference in enumerate(references or [])
181
+ if isinstance(reference, dict) and reference.get('uri')
182
+ ),
183
+ key=lambda item: (
184
+ _source_reference_timestamp(item[0]),
185
+ -item[1],
186
+ ),
187
+ reverse=True,
188
+ )
189
+ result = []
190
+ for reference, _ in ranked:
191
+ uri = reference['uri']
192
+ if uri in result:
193
+ continue
194
+ result.append(uri)
195
+ if len(result) == limit:
196
+ break
197
+ return result
198
+
199
+
200
+ def _source_reference_timestamp(reference: dict) -> float:
201
+ for field in ('reference_time', 'created_at'):
202
+ value = _native_datetime(reference.get(field))
203
+ if isinstance(value, datetime):
204
+ return value.timestamp()
205
+ return 0.0
206
+
207
+
149
208
  def _confirmation_basis(value) -> ConfirmationBasis | None:
150
209
  data = json_object(value)
151
210
  if not data:
@@ -268,6 +268,7 @@ class StoreKnowledge:
268
268
  entity_edges: $relationship_ids,
269
269
  fuli_session_id: $session_id,
270
270
  fuli_source_kind: $source_kind,
271
+ fuli_source_uri: $source_uri,
271
272
  fuli_source_application: $source_application,
272
273
  fuli_source_turn_id: $source_turn_id,
273
274
  fuli_source_excerpt: $source_excerpt,
@@ -391,6 +392,7 @@ class StoreKnowledge:
391
392
  relationship_ids=relationship_ids,
392
393
  session_id=episode.session_id,
393
394
  source_kind=episode.source_kind,
395
+ source_uri=episode.source_uri,
394
396
  source_application=episode.source_application,
395
397
  source_turn_id=episode.source_turn_id,
396
398
  source_excerpt=episode.source_excerpt,
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fuli-graph-provider"
3
- version = "0.3.0"
3
+ version = "0.5.0"
4
4
  description = "Graphiti-backed personal and shared project provider for Fuli."
5
5
  requires-python = ">=3.12"
6
6
  dependencies = [
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "fuli-context",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "fuli-context",
9
- "version": "0.3.0",
9
+ "version": "0.5.0",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "@hono/node-server": "2.0.12",
@@ -34,6 +34,7 @@
34
34
  "vite": "^8.1.5",
35
35
  "vitest": "^4.1.10",
36
36
  "vue": "^3.5.40",
37
+ "vue-i18n": "^11.4.8",
37
38
  "vue-router": "^5.2.0",
38
39
  "vue-tsc": "^3.3.8"
39
40
  },
@@ -427,6 +428,71 @@
427
428
  "hono": "^4"
428
429
  }
429
430
  },
431
+ "node_modules/@intlify/core-base": {
432
+ "version": "11.4.8",
433
+ "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-11.4.8.tgz",
434
+ "integrity": "sha512-A+Q7SKm5oEcy1E/cghqd7n/St4XjTqLhiiyDuieNcMrJcrHlkY5n0jp7Q9dD3txvVHzvsmBVV5M9wD5/s1zfzw==",
435
+ "dev": true,
436
+ "license": "MIT",
437
+ "dependencies": {
438
+ "@intlify/devtools-types": "11.4.8",
439
+ "@intlify/message-compiler": "11.4.8",
440
+ "@intlify/shared": "11.4.8"
441
+ },
442
+ "engines": {
443
+ "node": ">= 22"
444
+ },
445
+ "funding": {
446
+ "url": "https://github.com/sponsors/kazupon"
447
+ }
448
+ },
449
+ "node_modules/@intlify/devtools-types": {
450
+ "version": "11.4.8",
451
+ "resolved": "https://registry.npmjs.org/@intlify/devtools-types/-/devtools-types-11.4.8.tgz",
452
+ "integrity": "sha512-MGpID+rlfzGUbNcnC20bm5NMSBHPrvx0atLTfv9dftn3kjXw1hGKDcIcwrO99tSrZEc2i+hczRL7ks8qXsHPkQ==",
453
+ "dev": true,
454
+ "license": "MIT",
455
+ "dependencies": {
456
+ "@intlify/core-base": "11.4.8",
457
+ "@intlify/shared": "11.4.8"
458
+ },
459
+ "engines": {
460
+ "node": ">= 22"
461
+ },
462
+ "funding": {
463
+ "url": "https://github.com/sponsors/kazupon"
464
+ }
465
+ },
466
+ "node_modules/@intlify/message-compiler": {
467
+ "version": "11.4.8",
468
+ "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-11.4.8.tgz",
469
+ "integrity": "sha512-vbzk17dYwduYiv52EK61+FDCyhfVg1uPUtPmiD/d45W99uJIcXywrweOBcHv7n9/iEqmXiMGT52bgJbZDQqK3w==",
470
+ "dev": true,
471
+ "license": "MIT",
472
+ "dependencies": {
473
+ "@intlify/shared": "11.4.8",
474
+ "source-map-js": "^1.0.2"
475
+ },
476
+ "engines": {
477
+ "node": ">= 22"
478
+ },
479
+ "funding": {
480
+ "url": "https://github.com/sponsors/kazupon"
481
+ }
482
+ },
483
+ "node_modules/@intlify/shared": {
484
+ "version": "11.4.8",
485
+ "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-11.4.8.tgz",
486
+ "integrity": "sha512-XbRgrv+XEuvDr7UCY55oibVrh+o4u+A0VB6nSL0F5Z8LcZxE/8j573LYG6bCrOigIcHdGpSNI7Rh5UpC5/B/eg==",
487
+ "dev": true,
488
+ "license": "MIT",
489
+ "engines": {
490
+ "node": ">= 22"
491
+ },
492
+ "funding": {
493
+ "url": "https://github.com/sponsors/kazupon"
494
+ }
495
+ },
430
496
  "node_modules/@isaacs/cliui": {
431
497
  "version": "8.0.2",
432
498
  "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@@ -5731,6 +5797,35 @@
5731
5797
  "dev": true,
5732
5798
  "license": "MIT"
5733
5799
  },
5800
+ "node_modules/vue-i18n": {
5801
+ "version": "11.4.8",
5802
+ "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-11.4.8.tgz",
5803
+ "integrity": "sha512-0ULeHP6Z9CGvAm67S77ZEp41cfGXIREGL8qfhos2BMgcQQewtQcDKuojt6jjasAD/S8GwfTp2ySPmDSpwvrCMQ==",
5804
+ "dev": true,
5805
+ "license": "MIT",
5806
+ "dependencies": {
5807
+ "@intlify/core-base": "11.4.8",
5808
+ "@intlify/devtools-types": "11.4.8",
5809
+ "@intlify/shared": "11.4.8",
5810
+ "@vue/devtools-api": "^6.5.0"
5811
+ },
5812
+ "engines": {
5813
+ "node": ">= 22"
5814
+ },
5815
+ "funding": {
5816
+ "url": "https://github.com/sponsors/kazupon"
5817
+ },
5818
+ "peerDependencies": {
5819
+ "vue": "^3.0.0"
5820
+ }
5821
+ },
5822
+ "node_modules/vue-i18n/node_modules/@vue/devtools-api": {
5823
+ "version": "6.6.4",
5824
+ "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
5825
+ "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
5826
+ "dev": true,
5827
+ "license": "MIT"
5828
+ },
5734
5829
  "node_modules/vue-router": {
5735
5830
  "version": "5.2.0",
5736
5831
  "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-5.2.0.tgz",
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "fuli-context",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "Local-first context graph for AI agents.",
5
5
  "type": "module",
6
6
  "files": [
7
+ "README.en.md",
7
8
  "npm-shrinkwrap.json",
8
9
  "src/",
9
10
  "dist/web/",
@@ -86,6 +87,7 @@
86
87
  "vite": "^8.1.5",
87
88
  "vitest": "^4.1.10",
88
89
  "vue": "^3.5.40",
90
+ "vue-i18n": "^11.4.8",
89
91
  "vue-router": "^5.2.0",
90
92
  "vue-tsc": "^3.3.8"
91
93
  },
@@ -7,12 +7,19 @@ description: Use when Fuli tools are available and a task may depend on remember
7
7
 
8
8
  Accumulate reusable knowledge without interrupting the user's normal work. Keep an internal session buffer and flush focused batches at a natural task boundary or before the session ends.
9
9
 
10
- ## Load Collaboration Preferences
10
+ ## Establish Task Context
11
11
 
12
- At the start of every user task, before any other tool or answer, call
13
- `get_collaboration_preferences`. Pass `projectPath` as the current working directory. Fuli uses
14
- that path only for this local MCP call, never stores or returns it, and resolves the exact local
15
- personal project itself. Do not infer or guess `personalProjectId` in the Agent.
12
+ At the start of every user task, first use the lifecycle context supplied by the host when one
13
+ exists. A Claude Code `UserPromptSubmit` hook calls `begin_task_context`, which already loads
14
+ collaboration preferences and resolves the exact local personal project from the current working
15
+ directory. Apply that returned context and retain its opaque task token for the final checkpoint;
16
+ do not redundantly call `get_collaboration_preferences`.
17
+
18
+ When the host has not supplied lifecycle context, call exactly
19
+ `get_collaboration_preferences` before any other tool or answer. Pass `projectPath` as the current
20
+ working directory. This is the prompt-only fallback for Agents without an equivalent lifecycle
21
+ hook. Fuli uses the path only for this local MCP call, never stores or returns it, and resolves the
22
+ exact local personal project itself. Do not infer or guess `personalProjectId` in the Agent.
16
23
 
17
24
  Apply only `effective_preferences`. Personal-global preferences apply in every user task;
18
25
  project-scoped preferences layer on only for the exact selected project. Do not apply items
@@ -30,7 +37,13 @@ Mentioning a preference only in the final answer is not compliance.
30
37
 
31
38
  ## Retrieve Context
32
39
 
33
- Call `search_knowledge_graph` when prior durable context can materially improve the task.
40
+ For facts about the current local project, call `search_current_project_knowledge` with the
41
+ current working directory. It resolves the exact active project without requiring the Agent to
42
+ copy an ID, searches that child project first, and then includes only inheritable knowledge from
43
+ authorized parents or shared-source projects. Use `search_knowledge_graph` for personal-global
44
+ context, explicitly named extra projects, subscribed public projects, or advanced scoped queries.
45
+
46
+ Search when prior durable context can materially improve the task.
34
47
  Before saying that a stable fact is unknown or asking the user to provide it again, search
35
48
  Fuli if it may have been learned earlier. Strong triggers include URLs, deployment routes,
36
49
  requirements, terminology, architecture, prior decisions, runbooks, rationale, and remembered
@@ -75,11 +88,13 @@ IDs; never search every personal project, every subscription, or the whole graph
75
88
  Use the current repository, the user's wording, and `list_knowledge_spaces` to resolve scope.
76
89
  Ask if the active project remains ambiguous.
77
90
 
78
- The active project may also retrieve only knowledge explicitly marked inheritable from projects
79
- reached through outgoing `PART_OF` or `USES_KNOWLEDGE_FROM` relations. Never inherit
80
- project-scoped personal preferences, never traverse `RELATED_TO`, stop after two hops, and retain
81
- the returned scope path. Exact active-project knowledge with the same stable key overrides an
82
- inherited item.
91
+ The active child project may also retrieve only knowledge explicitly marked inheritable from
92
+ projects reached through its outgoing `PART_OF` or `USES_KNOWLEDGE_FROM` relations. This means
93
+ project-specific PRDs, configuration IDs, and debugging notes remain in the child, while a parent
94
+ may own shared run, validation, and test runbooks. Search the child first, then authorized parents.
95
+ Never inherit project-scoped personal preferences, never traverse `RELATED_TO`, stop after two
96
+ hops, and retain the returned scope path. Exact active-project knowledge with the same stable key
97
+ overrides an inherited item.
83
98
 
84
99
  After a retrieved personal item materially affects the final answer or a completed action, call
85
100
  `record_knowledge_usage` once for that task and item with `cited` or `applied`. Do not record
@@ -96,6 +111,27 @@ do not calculate, override, or claim the promotion themselves.
96
111
 
97
112
  For “why was this code written?” questions, use stored evidence relationships and a separately configured Git MCP together. Never invent a commit, PRD link, or missing history. For “what errors happened today?” questions, obtain current data from a monitoring/log MCP; use Fuli only for runbooks, architecture, prior causes, and other durable context.
98
113
 
114
+ ## Preserve Online Sources For Knowledge Refresh
115
+
116
+ When the user provides an online project or document link and the current Agent has a connector
117
+ or tool that can read it:
118
+
119
+ 1. Read the current source with the Agent's available capability. Fuli does not implement or
120
+ pretend to provide source-specific reading.
121
+ 2. Distill only durable knowledge, then pass the exact stable original HTTP(S) link as
122
+ `sourceUri` to `capture_session_knowledge`. Do not substitute a temporary download URL,
123
+ fabricate a link, or put credentials in the URI.
124
+ 3. Route confidential or internal sources to personal knowledge with `private` or `restricted`
125
+ sensitivity. The runtime may retain the source URI; never copy a real internal URI into Git
126
+ project files, fixtures, examples, logs, or public project evidence.
127
+ 4. When the user later asks to refresh the knowledge, search the relevant scope, use the returned
128
+ `source_uris` from supporting facts or entities, re-read the source with the current Agent,
129
+ and revise or supersede the corresponding Fuli knowledge while preserving history.
130
+
131
+ This workflow refreshes knowledge stored in Fuli. It does not imply or require writing back to
132
+ the original online document. If the current Agent cannot read the source, report that limitation
133
+ and never invent source-derived knowledge.
134
+
99
135
  Every `search_knowledge_graph` response provides two terminal-safe Markdown choices:
100
136
  `sourceMarker` for returned items that support the answer, and `noMatchSourceMarker`
101
137
  for a search with no supporting result. A tool-labelled match that is irrelevant to
@@ -111,6 +147,10 @@ omit, or wrap markers in HTML. A capture or write result is not a read citation.
111
147
 
112
148
  ## Decide What Persists
113
149
 
150
+ Evaluate every completed task for reusable value, but do not force every collaboration to become
151
+ knowledge. `retain_nothing` is the correct result for temporary output, already-known material,
152
+ unsupported inference, or a task with no durable value.
153
+
114
154
  Capture confirmed, stable information:
115
155
 
116
156
  - Personal: the user's enduring preferences, boundaries, habits, and working methods. Project-specific personal rules must explicitly link to that project instead of becoming global defaults.
@@ -118,6 +158,17 @@ Capture confirmed, stable information:
118
158
 
119
159
  Do not capture credentials, tokens, cookies, private keys, raw transcripts, temporary logs, command output, speculative conclusions, or disposable implementation details. Treat a correction as a replacement, not an additional conflicting fact.
120
160
 
161
+ For a confirmed project decision, prefer `record_decision_trace` over a flat note. Preserve the
162
+ selected option, materially considered rejected options, the human/source-confirmed rationale,
163
+ and any validation evidence. The rationale must explain the tradeoff rather than merely repeat
164
+ the selected option.
165
+
166
+ When retained knowledge is later rejected, contradicted, found outdated, or fails validation,
167
+ call `record_knowledge_feedback` with a concise reason and available evidence. Negative evidence
168
+ lowers ranking and marks the item for attention without deleting its history. Agent-only feedback
169
+ must not demote human/source-confirmed knowledge; authoritative contradiction may require review
170
+ or demotion under the Provider policy.
171
+
121
172
  ## Classify Knowledge State
122
173
 
123
174
  Every entity and relationship needs an `originQuadrant`, `confirmationBasis`, and
@@ -195,6 +246,21 @@ global decision without hiding unrelated preferences.
195
246
  - Never auto-approve a project Proposal.
196
247
  - If ownership is genuinely ambiguous and changes who can see the knowledge, ask one concise question instead of guessing.
197
248
 
249
+ ## Converge Shared Child Knowledge
250
+
251
+ When multiple direct children of one parent independently contain substantially equivalent
252
+ runbooks, conventions, or architecture facts, call `discover_common_knowledge_candidates`.
253
+ Discovery is read-only and must compare child-local knowledge with inheritance disabled so a
254
+ parent item cannot manufacture its own promotion candidate.
255
+
256
+ Never promote automatically. Preview a candidate with
257
+ `preview_common_knowledge_promotion`, show the proposed parent ownership, descendants inheritance
258
+ mode, selected duplicates, and scope rationale, then require a human confirmation reason. Apply
259
+ with `apply_common_knowledge_promotion` only using the one-time intent-bound preview token. The
260
+ Provider must atomically create or move the canonical parent item, mark descendants inheritance,
261
+ supersede child duplicates, and record the promotion audit. Child-specific exceptions stay in
262
+ their child projects.
263
+
198
264
  ## Resolve Or Create The Local Project
199
265
 
200
266
  Before the first project-scoped capture in a repository:
@@ -214,7 +280,7 @@ Use `capture_session_knowledge` with a small, coherent batch:
214
280
  1. List spaces when the target IDs are not already known.
215
281
  2. Summarize the durable facts; do not submit the entire source document or conversation.
216
282
  3. Create stable entity keys, typed entities, and explicit uppercase relationships. When relevant, model `GitCommit`, `CodeSymbol`, `PRDSection`, `Runbook`, and `Incident` entities and connect them with relationships such as `IMPLEMENTED_IN`, `DEFINED_BY`, `MOTIVATED_BY`, `APPLIES_TO`, and `INVESTIGATED_WITH`.
217
- 4. Include source kind, source application, a non-sensitive source description, reference time, session ID, and a retry-safe idempotency key. When available, include a stable source turn ID and only the smallest relevant excerpt needed to understand the evidence; never include a full transcript.
283
+ 4. Include source kind, source application, a non-sensitive source description, reference time, session ID, and a retry-safe idempotency key. When an online source was actually read, also include its exact stable `sourceUri`. When available, include a stable source turn ID and only the smallest relevant excerpt needed to understand the evidence; never include a full transcript.
218
284
  5. Use `supersedes` when replacing a known relationship and set confidence below `1` only when the source itself expresses uncertainty.
219
285
  6. Flush silently at the boundary. Continue the user's task without narrating routine capture.
220
286
 
@@ -222,6 +288,10 @@ If `capture_session_knowledge` or automatic project creation returns `capture_di
222
288
  user has disabled automatic capture. Do not retry, do not create a fallback record elsewhere,
223
289
  and do not describe the skipped batch as stored. Existing knowledge may still be queried.
224
290
 
225
- Before sending the final response, perform one checkpoint: either flush the durable batch or determine that this turn produced no reusable knowledge. Do not finish a reusable project decision with an unflushed batch.
291
+ Before sending the final response, perform one checkpoint. For a hook-managed task, call
292
+ `checkpoint_task_knowledge` exactly once with either `capture_candidates` and the bounded durable
293
+ batch, or `retain_nothing`. For a prompt-only Agent, either flush the durable batch directly or
294
+ determine that the turn produced no reusable knowledge. Do not finish a reusable project decision
295
+ with an unflushed batch.
226
296
 
227
297
  Only notify the user when capture fails, conflicts with existing knowledge, or requires an ownership decision. If project knowledge was proposed successfully, do not interrupt merely to announce the pending review item.