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.
- package/README.en.md +336 -0
- package/README.md +220 -174
- package/dist/web/assets/ConnectionsPage-CCKWm-6p.js +1 -0
- package/dist/web/assets/KnowledgeInspector-BZkB2wJn.js +1 -0
- package/dist/web/assets/KnowledgeInspector-CrGYxEob.css +1 -0
- package/dist/web/assets/KnowledgeOrganizerPage-CZAF_OlN.css +1 -0
- package/dist/web/assets/KnowledgeOrganizerPage-DlvcLwlL.js +1 -0
- package/dist/web/assets/KnowledgePage-CNQXd15y.js +1 -0
- package/dist/web/assets/KnowledgeWorkspace-CXPE4GCD.js +4 -0
- package/dist/web/assets/OverviewPage-ul7g7HXh.js +1 -0
- package/dist/web/assets/PersonalProfilePage-CC40Pw2L.js +1 -0
- package/dist/web/assets/PersonalProfilePage-S8ZcG2LU.css +1 -0
- package/dist/web/assets/PersonalProjectsPage-CPIEcShD.js +1 -0
- package/dist/web/assets/PublicProjectsPage-C700sR7z.js +1 -0
- package/dist/web/assets/ReviewPage-7MDDUDgA.js +1 -0
- package/dist/web/assets/console-B_Ena2DS.js +5 -0
- package/dist/web/assets/identity-CjBwYSMX.js +1 -0
- package/dist/web/assets/{index-Devf7glZ.css → index-CpLDx3of.css} +1 -1
- package/dist/web/assets/index-DAzeFJSc.js +2 -0
- package/dist/web/assets/model-C-5RVIJ2.js +1 -0
- package/dist/web/assets/{paths-B790iniO.js → paths-BkULqmGI.js} +1 -1
- package/dist/web/assets/{vue-router-BYTAoWT3.js → vue-router-DnD3hUm7.js} +1 -1
- package/dist/web/index.html +5 -6
- package/graph-provider/fuli_graph/app.py +48 -0
- package/graph-provider/fuli_graph/common_knowledge.py +443 -0
- package/graph-provider/fuli_graph/common_knowledge_models.py +54 -0
- package/graph-provider/fuli_graph/graph_models.py +9 -0
- package/graph-provider/fuli_graph/graph_query.py +25 -0
- package/graph-provider/fuli_graph/knowledge_audit.py +211 -0
- package/graph-provider/fuli_graph/knowledge_feedback_models.py +69 -0
- package/graph-provider/fuli_graph/knowledge_management.py +37 -0
- package/graph-provider/fuli_graph/knowledge_search.py +24 -2
- package/graph-provider/fuli_graph/models.py +69 -0
- package/graph-provider/fuli_graph/search_projection.py +59 -0
- package/graph-provider/fuli_graph/store_knowledge.py +2 -0
- package/graph-provider/pyproject.toml +1 -1
- package/npm-shrinkwrap.json +97 -2
- package/package.json +3 -1
- package/skills/capturing-session-knowledge/SKILL.md +83 -13
- package/src/agent-tools/graph-definitions.js +181 -13
- package/src/agent-tools/graph-handlers.js +19 -0
- package/src/cli/command-registry.js +3 -58
- package/src/cli/local-runtime-command.js +16 -2
- package/src/cli/local-runtime-options.js +6 -2
- package/src/cli.js +2 -37
- package/src/graphiti/agent-knowledge-workflows.js +543 -0
- package/src/graphiti/federated-application.js +85 -104
- package/src/graphiti/knowledge-provider-mapping.js +91 -0
- package/src/graphiti/provider-client.js +15 -0
- package/src/graphiti/source-uri.js +29 -0
- package/src/http/request-policy.js +37 -2
- package/src/local-runtime/lifecycle.js +11 -1
- package/src/mcp/common-knowledge-preview-tokens.js +67 -0
- package/src/mcp/create-mcp-server.js +43 -7
- package/src/mcp/instructions.js +6 -7
- package/src/mcp/lifecycle-audit.js +27 -0
- package/src/mcp/task-context-registry.js +93 -0
- package/src/mcp/tool-annotations.js +16 -1
- package/src/mcp/tool-result.js +24 -0
- package/src/runtime-options.js +7 -8
- package/src/server/lan-access.js +52 -0
- package/src/server.js +67 -8
- package/src/setup/agent-installation-status.js +14 -2
- package/src/setup/agents.js +12 -1
- package/src/setup/claude-code-config.js +194 -0
- package/src/setup/config-backup.js +20 -5
- package/src/setup/graph-runtime.js +78 -9
- package/dist/web/assets/ConnectionsPage-CY6H_mJJ.js +0 -1
- package/dist/web/assets/KnowledgeInspector-BSpz5Sfm.js +0 -1
- package/dist/web/assets/KnowledgeInspector-CHbB_0rC.css +0 -1
- package/dist/web/assets/KnowledgeOrganizerPage-BlEvhPrv.css +0 -1
- package/dist/web/assets/KnowledgeOrganizerPage-CvjGPlUc.js +0 -1
- package/dist/web/assets/KnowledgePage-Khuo3cNs.js +0 -1
- package/dist/web/assets/KnowledgeWorkspace-DVZusvem.js +0 -4
- package/dist/web/assets/OverviewPage-DC4C4Fhn.js +0 -1
- package/dist/web/assets/PersonalProfilePage-CSV9Mtgz.js +0 -1
- package/dist/web/assets/PersonalProfilePage-CVJ3TcRi.css +0 -1
- package/dist/web/assets/PersonalProjectsPage-A50EaseQ.js +0 -1
- package/dist/web/assets/PublicProjectsPage-BAdO_bae.js +0 -1
- package/dist/web/assets/ReviewPage-Dztk2jDG.js +0 -1
- package/dist/web/assets/console-tbvTUi6p.js +0 -1
- package/dist/web/assets/identity-B3s5CXhv.js +0 -1
- package/dist/web/assets/index-DwUDhQKc.js +0 -6
- package/dist/web/assets/model-DVT8sjy9.js +0 -1
- package/dist/web/assets/runtime-dom.esm-bundler-DttsK21F.js +0 -1
- package/src/cli/commands/candidates.js +0 -17
- package/src/cli/commands/ingestion.js +0 -32
- package/src/cli/commands/reads.js +0 -50
- package/src/cli/commands/spaces.js +0 -20
- package/src/cli/invocation.js +0 -47
- package/src/cli/migrate-command.js +0 -71
- package/src/cli/migration-destination.js +0 -117
- package/src/storage/import-json.js +0 -70
- package/src/storage/legacy-record-validation.js +0 -152
- 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: '
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
'
|
|
209
|
-
'
|
|
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
|
}
|
|
@@ -49,32 +12,14 @@ General:
|
|
|
49
12
|
--version, -v
|
|
50
13
|
|
|
51
14
|
Local service:
|
|
52
|
-
start [--port 2727] [--open] [--rebuild] [--data-dir DIR] [--personal-space NAME]
|
|
15
|
+
start [--port 2727] [--open] [--lan] [--rebuild] [--data-dir DIR] [--personal-space NAME]
|
|
53
16
|
stop [--data-dir DIR]
|
|
54
|
-
restart [--port 2727] [--open] [--rebuild] [--data-dir DIR] [--personal-space NAME]
|
|
17
|
+
restart [--port 2727] [--open] [--lan] [--rebuild] [--data-dir DIR] [--personal-space NAME]
|
|
55
18
|
status [--json] [--data-dir DIR]
|
|
56
19
|
open [--data-dir DIR]
|
|
57
20
|
|
|
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
|
}
|
|
@@ -40,10 +40,10 @@ export async function runLocalRuntimeCommand(command, args, dependencies = {}) {
|
|
|
40
40
|
export function formatLocalRuntimeResult(command, result) {
|
|
41
41
|
if (command === 'start') {
|
|
42
42
|
const verb = result.status === 'running' ? '已经在运行' : '已启动';
|
|
43
|
-
return
|
|
43
|
+
return formatStartedRuntime(`Fuli 本地服务${verb}。`, result);
|
|
44
44
|
}
|
|
45
45
|
if (command === 'restart') {
|
|
46
|
-
return
|
|
46
|
+
return formatStartedRuntime('Fuli 本地服务已重启。', result);
|
|
47
47
|
}
|
|
48
48
|
if (command === 'stop') {
|
|
49
49
|
if (result.status === 'partial') {
|
|
@@ -61,6 +61,20 @@ export function formatLocalRuntimeResult(command, result) {
|
|
|
61
61
|
throw new TypeError(`Unknown local runtime command: ${command}`);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
function formatStartedRuntime(title, result) {
|
|
65
|
+
const lines = [title, `管理界面:${result.url}`];
|
|
66
|
+
if (result.lan === true) {
|
|
67
|
+
lines.push(
|
|
68
|
+
'局域网界面:',
|
|
69
|
+
...result.lanUrls.map((url) => ` ${url}`),
|
|
70
|
+
`访问用户名:${result.lanAccess.username}`,
|
|
71
|
+
`临时访问口令:${result.lanAccess.accessCode}`,
|
|
72
|
+
'仅在可信 Wi-Fi 中使用;重新启动局域网模式会更换口令。'
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
return lines.join('\n');
|
|
76
|
+
}
|
|
77
|
+
|
|
64
78
|
function formatStatus(result) {
|
|
65
79
|
const labels = {
|
|
66
80
|
running: '运行中',
|
|
@@ -9,13 +9,16 @@ const VALUE_OPTIONS = Object.freeze({
|
|
|
9
9
|
const BOOLEAN_OPTIONS = Object.freeze({
|
|
10
10
|
'--open': 'open',
|
|
11
11
|
'--rebuild': 'rebuild',
|
|
12
|
+
'--lan': 'lan',
|
|
12
13
|
'--json': 'json'
|
|
13
14
|
});
|
|
14
15
|
|
|
15
16
|
const ALLOWED_BY_COMMAND = Object.freeze({
|
|
16
|
-
start: new Set(['--data-dir', '--personal-space', '--port', '--open', '--rebuild']),
|
|
17
|
+
start: new Set(['--data-dir', '--personal-space', '--port', '--open', '--rebuild', '--lan']),
|
|
17
18
|
stop: new Set(['--data-dir']),
|
|
18
|
-
restart: new Set([
|
|
19
|
+
restart: new Set([
|
|
20
|
+
'--data-dir', '--personal-space', '--port', '--open', '--rebuild', '--lan'
|
|
21
|
+
]),
|
|
19
22
|
status: new Set(['--data-dir', '--port', '--json']),
|
|
20
23
|
open: new Set(['--data-dir'])
|
|
21
24
|
});
|
|
@@ -29,6 +32,7 @@ export function parseLocalRuntimeOptions(command, args = []) {
|
|
|
29
32
|
port: DEFAULT_FULI_PORT,
|
|
30
33
|
open: false,
|
|
31
34
|
rebuild: false,
|
|
35
|
+
lan: false,
|
|
32
36
|
json: false
|
|
33
37
|
};
|
|
34
38
|
const seen = new Set();
|
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
|
|
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
|
-
|
|
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])) {
|