fuli-context 0.5.0 → 0.7.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 (206) hide show
  1. package/README.en.md +2 -333
  2. package/README.md +370 -202
  3. package/README.zh-CN.md +448 -0
  4. package/compose.graphiti.yml +6 -6
  5. package/dist/web/assets/AboutPage-DXh7Fwxt.css +1 -0
  6. package/dist/web/assets/AboutPage-DfnZo7Lo.js +1 -0
  7. package/dist/web/assets/ConnectionsPage-BloaSKmt.js +1 -0
  8. package/dist/web/assets/ConnectionsPage-CBHAdZXT.css +1 -0
  9. package/dist/web/assets/KnowledgeInspector-Bjhdu2mf.css +1 -0
  10. package/dist/web/assets/KnowledgeInspector-QvNpv0_k.js +1 -0
  11. package/dist/web/assets/KnowledgeOrganizerPage-Cx1w1jb2.css +1 -0
  12. package/dist/web/assets/KnowledgeOrganizerPage-DKq1jVD5.js +1 -0
  13. package/dist/web/assets/KnowledgePage-BWL0jT01.js +1 -0
  14. package/dist/web/assets/KnowledgeWorkspace-Czq6aZ7b.js +4 -0
  15. package/dist/web/assets/OverviewPage-5sJoO1Gh.js +1 -0
  16. package/dist/web/assets/{PersonalProfilePage-S8ZcG2LU.css → PersonalProfilePage-CdmEzyvZ.css} +1 -1
  17. package/dist/web/assets/PersonalProfilePage-tVChM9ik.js +1 -0
  18. package/dist/web/assets/PersonalProjectsPage-BPFrup4V.js +1 -0
  19. package/dist/web/assets/PublicProjectsPage-BPJSeREN.js +1 -0
  20. package/dist/web/assets/ReviewPage-B7EN817i.js +1 -0
  21. package/dist/web/assets/SearchableSelect-ClLN0rIb.js +1 -0
  22. package/dist/web/assets/SettingsPage-C6ouU0sR.css +1 -0
  23. package/dist/web/assets/SettingsPage-Chd_B3eH.js +1 -0
  24. package/dist/web/assets/_plugin-vue_export-helper-BDNMzG2s.js +1 -0
  25. package/dist/web/assets/console-CbDsKrsO.js +1 -0
  26. package/dist/web/assets/fuli-logo-Bu9WBuQe.js +1 -0
  27. package/dist/web/assets/fuli-logo-ER1wQ8Kk.png +0 -0
  28. package/dist/web/assets/i18n-BQcns1PH.js +5 -0
  29. package/dist/web/assets/identity-B8K4VYg6.js +1 -0
  30. package/dist/web/assets/index-CB8Xmsc2.js +2 -0
  31. package/dist/web/assets/index-CZeuPh-U.css +1 -0
  32. package/dist/web/assets/model-eF52GgPF.js +1 -0
  33. package/dist/web/assets/paths-hwz-Ac1g.js +1 -0
  34. package/dist/web/assets/useMinimumLoadingDisplay-BpCyBeko.css +1 -0
  35. package/dist/web/assets/useMinimumLoadingDisplay-wCgGDmHg.js +1 -0
  36. package/dist/web/assets/vue-router-BsEp_XwI.js +1 -0
  37. package/dist/web/index.html +9 -6
  38. package/docs/external-knowledge-architecture.md +279 -0
  39. package/examples/external-knowledge/markdown-folder.mjs +235 -0
  40. package/graph-provider/fuli_graph/app.py +57 -1
  41. package/graph-provider/fuli_graph/graph_models.py +3 -0
  42. package/graph-provider/fuli_graph/graph_query.py +62 -16
  43. package/graph-provider/fuli_graph/knowledge_review.py +612 -0
  44. package/graph-provider/fuli_graph/knowledge_review_models.py +127 -0
  45. package/graph-provider/fuli_graph/knowledge_search.py +51 -4
  46. package/graph-provider/fuli_graph/models.py +19 -0
  47. package/graph-provider/fuli_graph/runtime.py +15 -0
  48. package/graph-provider/fuli_graph/store_knowledge.py +2 -1
  49. package/graph-provider/pyproject.toml +1 -1
  50. package/npm-shrinkwrap.json +5 -407
  51. package/package.json +7 -5
  52. package/skills/capturing-session-knowledge/SKILL.md +24 -4
  53. package/skills/flreview/SKILL.md +73 -0
  54. package/skills/flreview/agents/openai.yaml +6 -0
  55. package/src/agent-tools/graph-definitions.js +79 -4
  56. package/src/agent-tools/graph-handlers.js +12 -0
  57. package/src/cli/command-registry.js +3 -3
  58. package/src/cli/local-runtime-command.js +51 -28
  59. package/src/cli/local-runtime-options.js +14 -8
  60. package/src/cli/setup-agent-selection.js +12 -12
  61. package/src/cli/setup-command.js +41 -18
  62. package/src/cli/shell-argument.js +10 -0
  63. package/src/cli/uninstall-command.js +17 -15
  64. package/src/cli/update-command.js +38 -30
  65. package/src/external-knowledge/catalog.js +61 -0
  66. package/src/external-knowledge/conflict-policy.js +58 -0
  67. package/src/external-knowledge/connected-search.js +166 -0
  68. package/src/external-knowledge/connectors/custom.js +133 -0
  69. package/src/external-knowledge/connectors/feishu.js +461 -0
  70. package/src/external-knowledge/connectors/mcp.js +438 -0
  71. package/src/external-knowledge/connectors/notion.js +307 -0
  72. package/src/external-knowledge/connectors/retrieval-api.js +222 -0
  73. package/src/external-knowledge/document-mapping.js +151 -0
  74. package/src/external-knowledge/graph-projection.js +17 -0
  75. package/src/external-knowledge/index.js +8 -0
  76. package/src/external-knowledge/registry.js +91 -0
  77. package/src/external-knowledge/runtime.js +68 -0
  78. package/src/external-knowledge/safe-diagnostic.js +14 -0
  79. package/src/external-knowledge/service.js +811 -0
  80. package/src/graphiti/agent-knowledge-workflows.js +7 -2
  81. package/src/graphiti/federated-application.js +66 -41
  82. package/src/graphiti/knowledge-item-role.js +21 -0
  83. package/src/graphiti/knowledge-review.js +43 -0
  84. package/src/graphiti/project-profile-mapping.js +32 -0
  85. package/src/graphiti/provider-client.js +22 -1
  86. package/src/graphiti/source-marker.js +54 -3
  87. package/src/graphiti/task-knowledge-recall.js +250 -0
  88. package/src/http/api-router.js +23 -108
  89. package/src/http/external-knowledge-api-router.js +104 -0
  90. package/src/http/graph-api-router.js +12 -1
  91. package/src/http/request-policy.js +1 -1
  92. package/src/local-runtime/lifecycle.js +26 -15
  93. package/src/mcp/create-mcp-server.js +1 -0
  94. package/src/mcp/instructions.js +7 -7
  95. package/src/mcp/tool-annotations.js +6 -0
  96. package/src/mcp/tool-result.js +1 -0
  97. package/src/server/application-lifecycle.js +2 -11
  98. package/src/server/blocked-ports.js +11 -0
  99. package/src/server.js +36 -17
  100. package/src/setup/agent-installation-status.js +2 -1
  101. package/src/setup/agents.js +2 -1
  102. package/src/setup/claude-code-config.js +2 -1
  103. package/src/setup/codex-bootstrap.js +12 -7
  104. package/src/setup/container-runtime.js +25 -21
  105. package/src/setup/graph-runtime.js +148 -56
  106. package/src/setup/node-runtime.js +3 -3
  107. package/src/setup/options.js +2 -4
  108. package/src/setup/paths.js +18 -3
  109. package/src/setup/review-skill-adapters.js +49 -0
  110. package/src/setup/setup.js +26 -2
  111. package/src/setup/uninstall.js +4 -2
  112. package/src/system/resource-monitor.js +317 -0
  113. package/src/system/runtime-settings.js +131 -0
  114. package/src/system/system-service.js +119 -0
  115. package/dist/web/assets/ConnectionsPage-CCKWm-6p.js +0 -1
  116. package/dist/web/assets/KnowledgeInspector-BZkB2wJn.js +0 -1
  117. package/dist/web/assets/KnowledgeInspector-CrGYxEob.css +0 -1
  118. package/dist/web/assets/KnowledgeOrganizerPage-CZAF_OlN.css +0 -1
  119. package/dist/web/assets/KnowledgeOrganizerPage-DlvcLwlL.js +0 -1
  120. package/dist/web/assets/KnowledgePage-CNQXd15y.js +0 -1
  121. package/dist/web/assets/KnowledgeWorkspace-CXPE4GCD.js +0 -4
  122. package/dist/web/assets/OverviewPage-ul7g7HXh.js +0 -1
  123. package/dist/web/assets/PersonalProfilePage-CC40Pw2L.js +0 -1
  124. package/dist/web/assets/PersonalProjectsPage-CPIEcShD.js +0 -1
  125. package/dist/web/assets/PublicProjectsPage-C700sR7z.js +0 -1
  126. package/dist/web/assets/ReviewPage-7MDDUDgA.js +0 -1
  127. package/dist/web/assets/console-B_Ena2DS.js +0 -5
  128. package/dist/web/assets/fuli-logo-DGuyqchQ.png +0 -0
  129. package/dist/web/assets/identity-CjBwYSMX.js +0 -1
  130. package/dist/web/assets/index-CpLDx3of.css +0 -1
  131. package/dist/web/assets/index-DAzeFJSc.js +0 -2
  132. package/dist/web/assets/model-C-5RVIJ2.js +0 -1
  133. package/dist/web/assets/paths-BkULqmGI.js +0 -1
  134. package/dist/web/assets/vue-router-DnD3hUm7.js +0 -1
  135. package/src/agent/candidate-read-service.js +0 -53
  136. package/src/agent/context-pack-service.js +0 -83
  137. package/src/agent/public-read-policy.js +0 -39
  138. package/src/agent/public-read-service.js +0 -69
  139. package/src/agent/read-projection.js +0 -107
  140. package/src/agent/search-service.js +0 -61
  141. package/src/agent/space-boundary.js +0 -33
  142. package/src/app/bootstrap.js +0 -8
  143. package/src/app/create-application.js +0 -143
  144. package/src/app/state-service.js +0 -38
  145. package/src/candidates.js +0 -78
  146. package/src/classifier.js +0 -63
  147. package/src/cli/command-arguments.js +0 -14
  148. package/src/context-pack.js +0 -206
  149. package/src/extractor.js +0 -77
  150. package/src/fact-history.js +0 -39
  151. package/src/facts/fact-service.js +0 -89
  152. package/src/http/bounded-json-array.js +0 -19
  153. package/src/http/lens-route.js +0 -50
  154. package/src/ingestion.js +0 -161
  155. package/src/lens/lens-correction-service.js +0 -81
  156. package/src/lens/lens-fact-writer.js +0 -85
  157. package/src/lens/lens-input.js +0 -62
  158. package/src/lens/lens-query-projection.js +0 -87
  159. package/src/lens/lens-query-ranking.js +0 -62
  160. package/src/lens/lens-query.js +0 -143
  161. package/src/lens/lens-resource-projection.js +0 -93
  162. package/src/lens/lens-resource-selection.js +0 -45
  163. package/src/lens/lens-resource-service.js +0 -103
  164. package/src/lens/lens-search-query.js +0 -122
  165. package/src/lens/lens-service.js +0 -183
  166. package/src/models.js +0 -44
  167. package/src/observer.js +0 -83
  168. package/src/project-rules.js +0 -33
  169. package/src/publication/outbox-service.js +0 -46
  170. package/src/publication/publication-envelope-schema.js +0 -75
  171. package/src/publication/publication-envelope.js +0 -37
  172. package/src/publication/publication-policy.js +0 -82
  173. package/src/publication/publication-service.js +0 -48
  174. package/src/router.js +0 -58
  175. package/src/runtime-options.js +0 -138
  176. package/src/security/episode-visibility.js +0 -14
  177. package/src/security/fact-visibility.js +0 -15
  178. package/src/security/source-metadata-policy.js +0 -8
  179. package/src/storage/episode-evidence-preview.js +0 -100
  180. package/src/storage/fact-search-order.js +0 -18
  181. package/src/storage/file/episode-evidence.js +0 -37
  182. package/src/storage/file/episode-index.js +0 -59
  183. package/src/storage/file/fact-search.js +0 -60
  184. package/src/storage/file-store.js +0 -437
  185. package/src/storage/migrate.js +0 -128
  186. package/src/storage/migrations/001-initial.sql +0 -83
  187. package/src/storage/migrations/002-storage-integrity.sql +0 -113
  188. package/src/storage/record-validation.js +0 -46
  189. package/src/storage/snapshot-defaults.js +0 -54
  190. package/src/storage/sqlite/candidates-repository.js +0 -84
  191. package/src/storage/sqlite/episode-evidence-repository.js +0 -154
  192. package/src/storage/sqlite/episode-privacy-sql.js +0 -33
  193. package/src/storage/sqlite/episodes-repository.js +0 -52
  194. package/src/storage/sqlite/fact-search-repository.js +0 -162
  195. package/src/storage/sqlite/facts-repository.js +0 -146
  196. package/src/storage/sqlite/imports-repository.js +0 -39
  197. package/src/storage/sqlite/mapper.js +0 -84
  198. package/src/storage/sqlite/open-database.js +0 -15
  199. package/src/storage/sqlite/outbox-repository.js +0 -103
  200. package/src/storage/sqlite/snapshot-repository.js +0 -69
  201. package/src/storage/sqlite/spaces-repository.js +0 -52
  202. package/src/storage/sqlite/subscriptions-repository.js +0 -54
  203. package/src/storage/sqlite-store.js +0 -159
  204. package/src/storage/stable-json.js +0 -24
  205. package/src/storage/store-port.js +0 -20
  206. package/src/store.js +0 -2
@@ -1,9 +1,9 @@
1
1
  export const MCP_INSTRUCTIONS = [
2
- "Each user task: if begin_task_context supplied hook-provided task context, apply. Otherwise call exactly get_collaboration_preferences with projectPath=cwd before other tools/answer as the fallback; never substitute a project action or Fuli tool. Apply all effective_preferences: personal-global everywhere plus only Fuli's matched project. Resolve relevant deferred_conflict first. Never guess personalProjectId. Writes enforce preferences in the actual payload; final text is not compliance.",
3
- 'For current-project facts use search_current_project_knowledge: exact active child first, then inheritable parent knowledge via outgoing PART_OF/USES_KNOWLEDGE_FROM within two hops. Child stable keys override. Never inherit project preferences or traverse RELATED_TO.',
4
- 'Before claiming facts unknown, use search_knowledge_graph. Use exact IDs for extra projects; mark pending. Widen a location miss to all_local_confirmed only after consent, then use rg only in the safe current repo/workspace. Never scan outside it. Live URLs need deployment evidence; changing state needs monitoring/Git MCP.',
5
- 'Use record_decision_trace for confirmed choices/rationale/validation. Use record_knowledge_feedback when retained knowledge is rejected, contradicted, outdated, or fails validation; preserve history.',
6
- 'Batch durable confirmed knowledge via capture_session_knowledge in exact scope. Never store secrets, transcripts, or guesses. Only human/source confirmation permits public review. Finish a hook-managed task with checkpoint_task_knowledge once: capture_candidates or retain_nothing; fallback Agents likewise flush or retain nothing.',
7
- 'When personal knowledge materially affects the answer/action, call record_knowledge_usage. Retrieval/injection never counts; it creates at most agent_confirmed and is never public-eligible.',
8
- 'Terminal-safe Markdown. Final MUST begin with sourceMarker.leadMarkdown; append sourceMarker.markdown unchanged only on match. On miss use noMatchSourceMarker leadMarkdown only (empty markdown). Never wrap markers in HTML or cite writes.'
2
+ "Each user task: apply hook-provided task context from begin_task_context. Otherwise, before other tools/answer, call exactly get_collaboration_preferences with projectPath=cwd and taskPrompt=current user request as fallback; never substitute a project action/Fuli tool. Apply all effective_preferences: personal-global everywhere plus only Fuli's matched project. Resolve relevant deferred_conflict first. Never guess personalProjectId. Writes enforce preferences in the actual payload; final text is not compliance.",
3
+ 'Before asking the user to repeat a stable project fact or method, inspect task_knowledge_recall. On miss use search_current_project_knowledge with 1-4 focused action, artifact, target-system, or identifier queries; never use the whole conversational request as the only query. Search the exact active child first, then inheritable parent PART_OF/USES_KNOWLEDGE_FROM knowledge within two hops; child stable keys override. Never inherit project preferences or traverse RELATED_TO.',
4
+ 'Before claiming facts unknown, use search_knowledge_graph. Use exact IDs for extra projects. Widen a miss to all_local_confirmed only after consent, then rg only in the safe current repo/workspace. Never scan outside it. Live state needs deployment/monitoring/Git MCP evidence. External/public evidence: search_connected_knowledge; agent_decide is response-only.',
5
+ 'Use record_decision_trace for confirmed choices and record_knowledge_feedback when knowledge is rejected, contradicted, outdated, or fails validation.',
6
+ 'Batch durable confirmed knowledge via capture_session_knowledge in exact scope; never secrets, transcripts, or guesses. Finish once with checkpoint_task_knowledge: capture_candidates or retain_nothing.',
7
+ 'After material personal-knowledge use call record_knowledge_usage; retrieval alone never counts or permits public use.',
8
+ 'Terminal-safe Markdown. Final MUST begin with sourceMarker.leadMarkdown; append sourceMarker.markdown unchanged on match. On miss use noMatchSourceMarker leadMarkdown only (empty markdown). Never wrap markers in HTML.'
9
9
  ].join(' ');
@@ -5,6 +5,7 @@ const READ_TOOLS = new Set([
5
5
  'preview_common_knowledge_promotion',
6
6
  'list_knowledge_spaces',
7
7
  'list_personal_projects',
8
+ 'list_knowledge_review_candidates',
8
9
  'preview_personal_project_action',
9
10
  'list_project_releases',
10
11
  'list_project_relations',
@@ -21,6 +22,7 @@ const WRITE_TOOLS = new Set([
21
22
  'capture_session_knowledge',
22
23
  'record_decision_trace',
23
24
  'search_knowledge_graph',
25
+ 'search_connected_knowledge',
24
26
  'record_knowledge_usage',
25
27
  'record_knowledge_feedback',
26
28
  'apply_common_knowledge_promotion',
@@ -28,6 +30,9 @@ const WRITE_TOOLS = new Set([
28
30
  'search_human_knowledge_changes',
29
31
  'review_human_knowledge_change',
30
32
  'upsert_personal_project',
33
+ 'start_knowledge_review',
34
+ 'record_knowledge_review_progress',
35
+ 'finish_knowledge_review',
31
36
  'revise_personal_knowledge',
32
37
  'reassign_personal_knowledge',
33
38
  'set_personal_preference_scope',
@@ -64,6 +69,7 @@ export function annotationsFor(name) {
64
69
  name === 'record_knowledge_feedback' ||
65
70
  name === 'checkpoint_task_knowledge',
66
71
  openWorldHint: name === 'capture_session_knowledge' ||
72
+ name === 'search_connected_knowledge' ||
67
73
  name === 'record_decision_trace' ||
68
74
  name === 'record_knowledge_feedback' ||
69
75
  name === 'checkpoint_task_knowledge' ||
@@ -4,6 +4,7 @@ const OMITTED_KEYS = new Set([
4
4
  'store',
5
5
  'dbpath',
6
6
  'databasepath',
7
+ 'privatepath',
7
8
  'snapshot',
8
9
  'fullsnapshot'
9
10
  ]);
@@ -1,21 +1,12 @@
1
- import { createApplication } from '../app/create-application.js';
2
1
  import { openFederatedGraphApplication } from '../graphiti/federated-application.js';
3
- import { openLocalApplication } from '../runtime-options.js';
4
2
 
5
3
  export function createServerApplication({
6
4
  app,
7
- store,
8
- dbPath,
9
5
  runtimeConfigPath,
10
- personalSpaceName,
11
6
  closeApplicationOnShutdown
12
7
  }) {
13
- const application = app ?? (store
14
- ? createApplication({ store, activePersonalSpaceName: personalSpaceName })
15
- : dbPath
16
- ? openLocalApplication({ dbPath, personalSpaceName })
17
- : openFederatedGraphApplication({ runtimeConfigPath }));
18
- const owned = closeApplicationOnShutdown ?? (!app && !store);
8
+ const application = app ?? openFederatedGraphApplication({ runtimeConfigPath });
9
+ const owned = closeApplicationOnShutdown ?? !app;
19
10
  return {
20
11
  application,
21
12
  close: closeOnce(() => {
@@ -0,0 +1,11 @@
1
+ const FETCH_BLOCKED_PORTS = new Set([
2
+ 1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42, 43, 53, 69, 77, 79, 87, 95,
3
+ 101, 102, 103, 104, 109, 110, 111, 113, 115, 117, 119, 123, 135, 137, 139, 143, 161, 179, 389,
4
+ 427, 465, 512, 513, 514, 515, 526, 530, 531, 532, 540, 548, 554, 556, 563, 587, 601, 636,
5
+ 989, 990, 993, 995, 1719, 1720, 1723, 2049, 3659, 4045, 4190, 5060, 5061, 6000, 6566,
6
+ 6665, 6666, 6667, 6668, 6669, 6697, 10080
7
+ ]);
8
+
9
+ export function isFetchBlockedPort(port) {
10
+ return FETCH_BLOCKED_PORTS.has(Number(port));
11
+ }
package/src/server.js CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createServer as createHttpServer } from 'node:http';
3
+ import { dirname } from 'node:path';
3
4
  import { fileURLToPath } from 'node:url';
4
5
 
5
6
  import { handleApiRequest } from './http/api-router.js';
@@ -9,6 +10,7 @@ import { sendJson } from './http/response.js';
9
10
  import { serveStatic } from './http/static-handler.js';
10
11
  import { DEFAULT_FULI_PORT } from './defaults.js';
11
12
  import { resolveGraphRuntimeOptions } from './graphiti/runtime-config.js';
13
+ import { isFetchBlockedPort } from './server/blocked-ports.js';
12
14
  import {
13
15
  discoverLanAddresses,
14
16
  lanConsoleUrls,
@@ -16,19 +18,14 @@ import {
16
18
  } from './server/lan-access.js';
17
19
  import { createServerApplication } from './server/application-lifecycle.js';
18
20
  import { listenServer } from './server/listen.js';
21
+ import { resolveSetupPaths } from './setup/paths.js';
22
+ import { createSystemService } from './system/system-service.js';
19
23
 
20
- const FETCH_BLOCKED_PORTS = new Set([
21
- 1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42, 43, 53, 69, 77, 79, 87, 95, 101, 102,
22
- 103, 104, 109, 110, 111, 113, 115, 117, 119, 123, 135, 137, 139, 143, 161, 179, 389, 427, 465,
23
- 512, 513, 514, 515, 526, 530, 531, 532, 540, 548, 554, 556, 563, 587, 601, 636, 989, 990, 993,
24
- 995, 1719, 1720, 1723, 2049, 3659, 4045, 4190, 5060, 5061, 6000, 6566, 6665, 6666, 6667, 6668,
25
- 6669, 6697, 10080
26
- ]);
24
+ const PACKAGE_ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
27
25
 
28
26
  export async function createServer(options = {}) {
29
27
  const {
30
28
  port = DEFAULT_FULI_PORT,
31
- store,
32
29
  app,
33
30
  closeApplicationOnShutdown,
34
31
  isBlockedPort = isFetchBlockedPort,
@@ -47,17 +44,28 @@ export async function createServer(options = {}) {
47
44
  if (Number(port) !== 0 && isBlockedPort(port)) {
48
45
  throw new Error(`Port ${port} is blocked for local fetch`);
49
46
  }
50
- const legacyRuntime = app || store || Object.hasOwn(options, 'dbPath');
51
- const localOptions = legacyRuntime
52
- ? { dbPath: options.dbPath, personalSpaceName: options.personalSpaceName ?? '我' }
53
- : resolveServerGraphOptions(options);
47
+ const localOptions = app ? {} : resolveServerGraphOptions(options);
54
48
  const runtime = createServerApplication({
55
49
  app,
56
- store,
57
50
  ...localOptions,
58
51
  closeApplicationOnShutdown
59
52
  });
60
53
  const application = runtime.application;
54
+ const externalKnowledge = options.externalKnowledge ??
55
+ application.externalKnowledge ?? null;
56
+ const connectedKnowledge = options.connectedKnowledge ??
57
+ application.connectedKnowledge ?? null;
58
+ const system = options.system ?? (!app
59
+ ? createSystemService({
60
+ paths: resolveSetupPaths({
61
+ dataDir: dirname(localOptions.runtimeConfigPath),
62
+ packageRoot: PACKAGE_ROOT
63
+ }),
64
+ packageRoot: PACKAGE_ROOT,
65
+ activePort: port,
66
+ activeLan: lanEnabled
67
+ })
68
+ : null);
61
69
 
62
70
  for (let attempt = 0; attempt < 20; attempt += 1) {
63
71
  let authority = null;
@@ -67,6 +75,9 @@ export async function createServer(options = {}) {
67
75
  request,
68
76
  response,
69
77
  app: application,
78
+ system,
79
+ externalKnowledge,
80
+ connectedKnowledge,
70
81
  authority,
71
82
  lanAuthorities: allowedLanAuthorities,
72
83
  lanAccessToken: lanToken
@@ -122,14 +133,15 @@ function resolveServerGraphOptions(options) {
122
133
  });
123
134
  }
124
135
 
125
- export function isFetchBlockedPort(port) {
126
- return FETCH_BLOCKED_PORTS.has(Number(port));
127
- }
136
+ export { isFetchBlockedPort } from './server/blocked-ports.js';
128
137
 
129
138
  async function handleRequest({
130
139
  request,
131
140
  response,
132
141
  app,
142
+ system,
143
+ externalKnowledge,
144
+ connectedKnowledge,
133
145
  authority,
134
146
  lanAuthorities,
135
147
  lanAccessToken
@@ -141,7 +153,14 @@ async function handleRequest({
141
153
  lanAuthorities,
142
154
  lanAccessToken
143
155
  })) return;
144
- if (await handleApiRequest({ request, response, app })) return;
156
+ if (await handleApiRequest({
157
+ request,
158
+ response,
159
+ app,
160
+ system,
161
+ externalKnowledge,
162
+ connectedKnowledge
163
+ })) return;
145
164
  serveStatic(new URL(request.url, 'http://127.0.0.1').pathname, response);
146
165
  }
147
166
 
@@ -17,7 +17,8 @@ export function inspectAgentInstallations(agents, context, {
17
17
  const registration = inspectRegistration(agent, context, { readText, readJson });
18
18
  const skillPairs = [
19
19
  [context.sessionSkillPath, agent.skillPath],
20
- [context.projectSkillPath, agent.projectSkillPath]
20
+ [context.projectSkillPath, agent.projectSkillPath],
21
+ [context.reviewSkillPath, agent.reviewSkillPath]
21
22
  ];
22
23
  const skillsPresent = skillPairs.some(([, targetPath]) => fileExists(targetPath));
23
24
  const skillsCurrent = skillPairs.every(
@@ -9,6 +9,7 @@ import {
9
9
  } from './claude-code-config.js';
10
10
  import { connectCursor, disconnectCursor } from './cursor-config.js';
11
11
  import { connectCodex, disconnectCodex } from './codex-config.js';
12
+ import { adaptAgentForReviewSkill } from './review-skill-adapters.js';
12
13
 
13
14
  export function discoverAgents({
14
15
  platform = process.platform,
@@ -52,7 +53,7 @@ export function discoverAgents({
52
53
  projectSkillPath: pathApi.join(homeDir, '.cursor', 'skills', projectSkillName),
53
54
  available: Boolean(commandExists('cursor'))
54
55
  }
55
- ];
56
+ ].map((agent) => adaptAgentForReviewSkill(agent, { homeDir, pathApi }));
56
57
  }
57
58
 
58
59
  export function buildAgentCommands(agent, context) {
@@ -115,7 +115,8 @@ function withManagedHooks(settings) {
115
115
  ...(hooks.UserPromptSubmit ?? []),
116
116
  managedHookGroup('begin_task_context', {
117
117
  sessionId: '${session_id}',
118
- projectPath: '${cwd}'
118
+ projectPath: '${cwd}',
119
+ taskPrompt: '${prompt}'
119
120
  }, 'Loading Fuli task context')
120
121
  ];
121
122
  hooks.Stop = [
@@ -9,18 +9,23 @@ import {
9
9
  } from 'node:fs';
10
10
  import { basename, dirname, join } from 'node:path';
11
11
 
12
+ import { codexReviewCommandBridge } from './review-skill-adapters.js';
13
+
12
14
  const BLOCK_START = '<!-- BEGIN FULI MANAGED INSTRUCTIONS -->';
13
15
  const BLOCK_END = '<!-- END FULI MANAGED INSTRUCTIONS -->';
14
16
 
15
17
  const GLOBAL_INSTRUCTIONS = `${BLOCK_START}
16
18
  At the start of every user task, before any other tool or answer, call exactly
17
- \`get_collaboration_preferences\` with \`projectPath\`=current working directory. Never substitute
18
- project action or Fuli tool.
19
- Apply all returned \`effective_preferences\` before answers or tools. For write tools, enforce
20
- them in the actual payload; mentioning them later is not compliance.
21
- Before using a relevant \`deferred_conflict\`, call
22
- \`resolve_deferred_preference_conflict\`; ignore unrelated ones.
23
- The current request wins on conflict.
19
+ \`get_collaboration_preferences\` with \`projectPath\`=current working directory and
20
+ \`taskPrompt\`=current user request; never substitute a project action/Fuli tool. Fuli uses both
21
+ transiently and never stores or returns them. Apply all returned \`effective_preferences\`.
22
+ Before asking the user to repeat a stable project fact or method, inspect
23
+ \`task_knowledge_recall\`. On miss call \`search_current_project_knowledge\` with 1-4 focused
24
+ action/artifact/target/identifier queries; never use the full request as the only query.
25
+ For write tools enforce them in the actual payload; mentioning them later is not compliance.
26
+ Relevant \`deferred_conflict\`: call \`resolve_deferred_preference_conflict\` first; ignore others.
27
+ Current request wins.
28
+ ${codexReviewCommandBridge()}
24
29
  ${BLOCK_END}`;
25
30
 
26
31
  export function installCodexBootstrap(agent, _context, {
@@ -86,7 +86,7 @@ export async function ensureContainerRuntime(options = {}, dependencies = {}) {
86
86
  throw containerRuntimeError(runtime, options.platform ?? process.platform);
87
87
  }
88
88
 
89
- onProgress(`正在启动 ${runtime.desktop.label},首次启动可能需要几分钟…`);
89
+ onProgress(`Starting ${runtime.desktop.label}; the first launch may take a few minutes...`);
90
90
  await launch(runtime.desktop, dependencies);
91
91
 
92
92
  const attempts = Math.max(1, Math.ceil(maxWaitMs / pollIntervalMs));
@@ -100,9 +100,9 @@ export async function ensureContainerRuntime(options = {}, dependencies = {}) {
100
100
  }
101
101
 
102
102
  throw new Error(
103
- `${runtime.desktop?.label ?? runtime.label} 已启动,但容器引擎未在 ` +
104
- `${Math.ceil(maxWaitMs / 1000)} 秒内就绪。请打开该应用查看错误,确认 docker info ` +
105
- `可以成功后重新运行 fl setup。${detailSuffix(runtime.detail)}`
103
+ `${runtime.desktop?.label ?? runtime.label} started, but the container engine was not ready ` +
104
+ `within ${Math.ceil(maxWaitMs / 1000)} seconds. Open the application to inspect errors, ` +
105
+ `confirm that docker info succeeds, then run fl setup again.${detailSuffix(runtime.detail)}`
106
106
  );
107
107
  }
108
108
 
@@ -124,12 +124,14 @@ export function runDockerCompose(args, runtime = null, {
124
124
 
125
125
  const detail = safeProcessDetail(result, homeDir);
126
126
  if (/port is already allocated|address already in use|bind.+failed/i.test(detail)) {
127
- throw new Error(`Fuli 需要的本地端口已被占用。${detailSuffix(detail)}`);
127
+ throw new Error(`A local port required by Fuli is already in use.${detailSuffix(detail)}`);
128
128
  }
129
129
  if (/cannot connect|connection refused|error during connect|docker daemon|\bEOF\b/i.test(detail)) {
130
- throw new Error(`容器运行时在启动 Fuli Provider 时失去连接。${detailSuffix(detail)}`);
130
+ throw new Error(
131
+ `The container runtime disconnected while starting the Fuli Provider.${detailSuffix(detail)}`
132
+ );
131
133
  }
132
- throw new Error(`Docker Compose 无法启动 Fuli Provider。${detailSuffix(detail)}`);
134
+ throw new Error(`Docker Compose could not start the Fuli Provider.${detailSuffix(detail)}`);
133
135
  }
134
136
 
135
137
  export function selectDockerEnvironment({
@@ -161,19 +163,21 @@ export function containerRuntimeError(runtime, platform = process.platform) {
161
163
  }
162
164
  if (runtime.status === 'missing-compose') {
163
165
  return new Error(
164
- `已找到 Docker,但缺少 Docker Compose v2。请安装 Compose v2 后重新运行 fl setup。` +
166
+ 'Docker was found, but Docker Compose v2 is missing. Install Compose v2, then run ' +
167
+ 'fl setup again.' +
165
168
  detailSuffix(runtime.detail)
166
169
  );
167
170
  }
168
171
  if (runtime.explicitTarget) {
169
172
  return new Error(
170
- `DOCKER_HOST 或 DOCKER_CONTEXT 指向的容器引擎不可用。请修复该配置并确认 ` +
171
- `docker info 可以成功后重新运行 fl setup。${detailSuffix(runtime.detail)}`
173
+ 'The container engine selected by DOCKER_HOST or DOCKER_CONTEXT is unavailable. Fix the ' +
174
+ `configuration, confirm that docker info succeeds, then run fl setup again.${detailSuffix(runtime.detail)}`
172
175
  );
173
176
  }
174
177
  return new Error(
175
- `已找到 Docker 和 Compose,但容器引擎没有运行。请启动 Docker Desktop、` +
176
- `Rancher Desktop 或当前 Docker 服务,确认 docker info 可以成功后重新运行 fl setup。` +
178
+ 'Docker and Compose were found, but the container engine is not running. Start Docker ' +
179
+ 'Desktop, Rancher Desktop, or the current Docker service; confirm that docker info succeeds; ' +
180
+ 'then run fl setup again.' +
177
181
  detailSuffix(runtime.detail)
178
182
  );
179
183
  }
@@ -330,17 +334,17 @@ async function launchDesktopRuntime(desktop, { spawnProcess = spawn } = {}) {
330
334
 
331
335
  function missingRuntimeMessage(platform) {
332
336
  if (platform === 'darwin') {
333
- return `未检测到可用的 Docker Compose 容器运行时。请安装并首次打开 Rancher Desktop ` +
334
- `(${RANCHER_DOWNLOAD_URL}) 或 Docker Desktop (${DOCKER_DOWNLOAD_URL}),` +
335
- `然后重新运行 fl setup。`;
337
+ return 'No usable Docker Compose container runtime was detected. Install and open Rancher ' +
338
+ `Desktop (${RANCHER_DOWNLOAD_URL}) or Docker Desktop (${DOCKER_DOWNLOAD_URL}), then run ` +
339
+ 'fl setup again.';
336
340
  }
337
341
  if (platform === 'win32') {
338
- return `未检测到可用的 Docker Compose 容器运行时。请先启用 WSL 2,再安装并首次打开 ` +
339
- `Rancher Desktop (${RANCHER_DOWNLOAD_URL}) 或 Docker Desktop ` +
340
- `(${DOCKER_DOWNLOAD_URL}),然后重新运行 fl setup。`;
342
+ return 'No usable Docker Compose container runtime was detected. Enable WSL 2, then install ' +
343
+ `and open Rancher Desktop (${RANCHER_DOWNLOAD_URL}) or Docker Desktop ` +
344
+ `(${DOCKER_DOWNLOAD_URL}), and run fl setup again.`;
341
345
  }
342
- return `未检测到可用的 Docker Compose 容器运行时。请安装 Docker Engine + Compose v2,` +
343
- `或 Rancher Desktop (${RANCHER_DOWNLOAD_URL}),然后重新运行 fl setup。`;
346
+ return 'No usable Docker Compose container runtime was detected. Install Docker Engine with ' +
347
+ `Compose v2, or Rancher Desktop (${RANCHER_DOWNLOAD_URL}), then run fl setup again.`;
344
348
  }
345
349
 
346
350
  function defaultDockerAvailable(env) {
@@ -372,7 +376,7 @@ function safeProcessDetail(result, homeDir) {
372
376
  }
373
377
 
374
378
  function detailSuffix(detail) {
375
- return nonEmpty(detail) ? ` Docker 返回:${detail}` : '';
379
+ return nonEmpty(detail) ? ` Docker returned: ${detail}` : '';
376
380
  }
377
381
 
378
382
  function processSucceeded(result) {