fuli-context 0.5.0 → 0.7.3
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.md +370 -202
- package/README.zh-CN.md +448 -0
- package/compose.graphiti.yml +6 -6
- package/dist/web/assets/AboutPage-DXh7Fwxt.css +1 -0
- package/dist/web/assets/AboutPage-qeO_vm0T.js +1 -0
- package/dist/web/assets/ConnectionsPage-CBHAdZXT.css +1 -0
- package/dist/web/assets/ConnectionsPage-DO0GR9Xg.js +1 -0
- package/dist/web/assets/KnowledgeInspector-Bjhdu2mf.css +1 -0
- package/dist/web/assets/KnowledgeInspector-DEW5lA1m.js +1 -0
- package/dist/web/assets/KnowledgeOrganizerPage-BkM568Ld.js +1 -0
- package/dist/web/assets/KnowledgeOrganizerPage-Cx1w1jb2.css +1 -0
- package/dist/web/assets/KnowledgePage-BAT4aZIm.js +1 -0
- package/dist/web/assets/KnowledgeWorkspace-DyGtBz--.js +4 -0
- package/dist/web/assets/OverviewPage-CmCcDCAs.js +1 -0
- package/dist/web/assets/PersonalProfilePage-C42mwKN8.js +1 -0
- package/dist/web/assets/{PersonalProfilePage-S8ZcG2LU.css → PersonalProfilePage-CdmEzyvZ.css} +1 -1
- package/dist/web/assets/PersonalProjectsPage-BwaXR62K.js +1 -0
- package/dist/web/assets/PublicProjectsPage-egKBulOq.js +1 -0
- package/dist/web/assets/ReviewPage-B3vnZ6gn.js +1 -0
- package/dist/web/assets/SearchableSelect-ih69qbcS.js +1 -0
- package/dist/web/assets/SettingsPage-C6ouU0sR.css +1 -0
- package/dist/web/assets/SettingsPage-oLE8mNs5.js +1 -0
- package/dist/web/assets/_plugin-vue_export-helper-BDNMzG2s.js +1 -0
- package/dist/web/assets/console-DE40hXGC.js +1 -0
- package/dist/web/assets/fuli-logo-Bu9WBuQe.js +1 -0
- package/dist/web/assets/fuli-logo-ER1wQ8Kk.png +0 -0
- package/dist/web/assets/i18n-CDxBs1sz.js +5 -0
- package/dist/web/assets/identity-BCxb1mcy.js +1 -0
- package/dist/web/assets/index-BzhNfzU_.js +2 -0
- package/dist/web/assets/index-CCOGfZzE.css +1 -0
- package/dist/web/assets/model-CCMdt10Q.js +1 -0
- package/dist/web/assets/paths-DwHeiGMT.js +1 -0
- package/dist/web/assets/useMinimumLoadingDisplay-BpCyBeko.css +1 -0
- package/dist/web/assets/useMinimumLoadingDisplay-HdI7XAmu.js +1 -0
- package/dist/web/assets/vue-router-Drfl1F8i.js +1 -0
- package/dist/web/index.html +9 -6
- package/docs/external-knowledge-architecture.md +279 -0
- package/examples/external-knowledge/markdown-folder.mjs +235 -0
- package/graph-provider/fuli_graph/app.py +57 -1
- package/graph-provider/fuli_graph/graph_models.py +3 -0
- package/graph-provider/fuli_graph/graph_query.py +62 -16
- package/graph-provider/fuli_graph/knowledge_review.py +657 -0
- package/graph-provider/fuli_graph/knowledge_review_models.py +133 -0
- package/graph-provider/fuli_graph/knowledge_search.py +51 -4
- package/graph-provider/fuli_graph/models.py +19 -0
- package/graph-provider/fuli_graph/runtime.py +15 -0
- package/graph-provider/fuli_graph/store_knowledge.py +2 -1
- package/graph-provider/pyproject.toml +1 -1
- package/npm-shrinkwrap.json +5 -407
- package/package.json +7 -5
- package/skills/capturing-session-knowledge/SKILL.md +24 -4
- package/skills/flreview/SKILL.md +100 -0
- package/skills/flreview/agents/openai.yaml +6 -0
- package/src/agent-tools/graph-definitions.js +81 -6
- package/src/agent-tools/graph-handlers.js +12 -0
- package/src/cli/command-registry.js +3 -3
- package/src/cli/local-runtime-command.js +51 -28
- package/src/cli/local-runtime-options.js +14 -8
- package/src/cli/setup-agent-selection.js +12 -12
- package/src/cli/setup-command.js +41 -18
- package/src/cli/shell-argument.js +10 -0
- package/src/cli/uninstall-command.js +17 -15
- package/src/cli/update-command.js +38 -30
- package/src/external-knowledge/catalog.js +61 -0
- package/src/external-knowledge/conflict-policy.js +58 -0
- package/src/external-knowledge/connected-search.js +166 -0
- package/src/external-knowledge/connectors/custom.js +133 -0
- package/src/external-knowledge/connectors/feishu.js +461 -0
- package/src/external-knowledge/connectors/mcp.js +438 -0
- package/src/external-knowledge/connectors/notion.js +307 -0
- package/src/external-knowledge/connectors/retrieval-api.js +222 -0
- package/src/external-knowledge/document-mapping.js +151 -0
- package/src/external-knowledge/graph-projection.js +17 -0
- package/src/external-knowledge/index.js +8 -0
- package/src/external-knowledge/registry.js +91 -0
- package/src/external-knowledge/runtime.js +68 -0
- package/src/external-knowledge/safe-diagnostic.js +14 -0
- package/src/external-knowledge/service.js +811 -0
- package/src/graphiti/agent-knowledge-workflows.js +7 -2
- package/src/graphiti/federated-application.js +68 -42
- package/src/graphiti/knowledge-item-role.js +21 -0
- package/src/graphiti/knowledge-review.js +43 -0
- package/src/graphiti/project-profile-mapping.js +32 -0
- package/src/graphiti/provider-client.js +22 -1
- package/src/graphiti/source-marker.js +54 -3
- package/src/graphiti/task-knowledge-recall.js +250 -0
- package/src/http/api-router.js +23 -108
- package/src/http/external-knowledge-api-router.js +104 -0
- package/src/http/graph-api-router.js +12 -1
- package/src/http/request-policy.js +1 -1
- package/src/local-runtime/lifecycle.js +26 -15
- package/src/mcp/create-mcp-server.js +4 -1
- package/src/mcp/instructions.js +7 -7
- package/src/mcp/tool-annotations.js +6 -0
- package/src/mcp/tool-result.js +1 -0
- package/src/server/application-lifecycle.js +2 -11
- package/src/server/blocked-ports.js +11 -0
- package/src/server.js +36 -17
- package/src/setup/agent-installation-status.js +2 -1
- package/src/setup/agents.js +2 -1
- package/src/setup/claude-code-config.js +2 -1
- package/src/setup/codex-bootstrap.js +12 -7
- package/src/setup/container-runtime.js +25 -21
- package/src/setup/graph-runtime.js +148 -56
- package/src/setup/node-runtime.js +3 -3
- package/src/setup/options.js +2 -4
- package/src/setup/paths.js +18 -3
- package/src/setup/review-skill-adapters.js +49 -0
- package/src/setup/setup.js +26 -2
- package/src/setup/uninstall.js +4 -2
- package/src/system/resource-monitor.js +317 -0
- package/src/system/runtime-settings.js +131 -0
- package/src/system/system-service.js +119 -0
- package/README.en.md +0 -336
- package/dist/web/assets/ConnectionsPage-CCKWm-6p.js +0 -1
- package/dist/web/assets/KnowledgeInspector-BZkB2wJn.js +0 -1
- package/dist/web/assets/KnowledgeInspector-CrGYxEob.css +0 -1
- package/dist/web/assets/KnowledgeOrganizerPage-CZAF_OlN.css +0 -1
- package/dist/web/assets/KnowledgeOrganizerPage-DlvcLwlL.js +0 -1
- package/dist/web/assets/KnowledgePage-CNQXd15y.js +0 -1
- package/dist/web/assets/KnowledgeWorkspace-CXPE4GCD.js +0 -4
- package/dist/web/assets/OverviewPage-ul7g7HXh.js +0 -1
- package/dist/web/assets/PersonalProfilePage-CC40Pw2L.js +0 -1
- package/dist/web/assets/PersonalProjectsPage-CPIEcShD.js +0 -1
- package/dist/web/assets/PublicProjectsPage-C700sR7z.js +0 -1
- package/dist/web/assets/ReviewPage-7MDDUDgA.js +0 -1
- package/dist/web/assets/console-B_Ena2DS.js +0 -5
- package/dist/web/assets/fuli-logo-DGuyqchQ.png +0 -0
- package/dist/web/assets/identity-CjBwYSMX.js +0 -1
- package/dist/web/assets/index-CpLDx3of.css +0 -1
- package/dist/web/assets/index-DAzeFJSc.js +0 -2
- package/dist/web/assets/model-C-5RVIJ2.js +0 -1
- package/dist/web/assets/paths-BkULqmGI.js +0 -1
- package/dist/web/assets/vue-router-DnD3hUm7.js +0 -1
- package/src/agent/candidate-read-service.js +0 -53
- package/src/agent/context-pack-service.js +0 -83
- package/src/agent/public-read-policy.js +0 -39
- package/src/agent/public-read-service.js +0 -69
- package/src/agent/read-projection.js +0 -107
- package/src/agent/search-service.js +0 -61
- package/src/agent/space-boundary.js +0 -33
- package/src/app/bootstrap.js +0 -8
- package/src/app/create-application.js +0 -143
- package/src/app/state-service.js +0 -38
- package/src/candidates.js +0 -78
- package/src/classifier.js +0 -63
- package/src/cli/command-arguments.js +0 -14
- package/src/context-pack.js +0 -206
- package/src/extractor.js +0 -77
- package/src/fact-history.js +0 -39
- package/src/facts/fact-service.js +0 -89
- package/src/http/bounded-json-array.js +0 -19
- package/src/http/lens-route.js +0 -50
- package/src/ingestion.js +0 -161
- package/src/lens/lens-correction-service.js +0 -81
- package/src/lens/lens-fact-writer.js +0 -85
- package/src/lens/lens-input.js +0 -62
- package/src/lens/lens-query-projection.js +0 -87
- package/src/lens/lens-query-ranking.js +0 -62
- package/src/lens/lens-query.js +0 -143
- package/src/lens/lens-resource-projection.js +0 -93
- package/src/lens/lens-resource-selection.js +0 -45
- package/src/lens/lens-resource-service.js +0 -103
- package/src/lens/lens-search-query.js +0 -122
- package/src/lens/lens-service.js +0 -183
- package/src/models.js +0 -44
- package/src/observer.js +0 -83
- package/src/project-rules.js +0 -33
- package/src/publication/outbox-service.js +0 -46
- package/src/publication/publication-envelope-schema.js +0 -75
- package/src/publication/publication-envelope.js +0 -37
- package/src/publication/publication-policy.js +0 -82
- package/src/publication/publication-service.js +0 -48
- package/src/router.js +0 -58
- package/src/runtime-options.js +0 -138
- package/src/security/episode-visibility.js +0 -14
- package/src/security/fact-visibility.js +0 -15
- package/src/security/source-metadata-policy.js +0 -8
- package/src/storage/episode-evidence-preview.js +0 -100
- package/src/storage/fact-search-order.js +0 -18
- package/src/storage/file/episode-evidence.js +0 -37
- package/src/storage/file/episode-index.js +0 -59
- package/src/storage/file/fact-search.js +0 -60
- package/src/storage/file-store.js +0 -437
- package/src/storage/migrate.js +0 -128
- package/src/storage/migrations/001-initial.sql +0 -83
- package/src/storage/migrations/002-storage-integrity.sql +0 -113
- package/src/storage/record-validation.js +0 -46
- package/src/storage/snapshot-defaults.js +0 -54
- package/src/storage/sqlite/candidates-repository.js +0 -84
- package/src/storage/sqlite/episode-evidence-repository.js +0 -154
- package/src/storage/sqlite/episode-privacy-sql.js +0 -33
- package/src/storage/sqlite/episodes-repository.js +0 -52
- package/src/storage/sqlite/fact-search-repository.js +0 -162
- package/src/storage/sqlite/facts-repository.js +0 -146
- package/src/storage/sqlite/imports-repository.js +0 -39
- package/src/storage/sqlite/mapper.js +0 -84
- package/src/storage/sqlite/open-database.js +0 -15
- package/src/storage/sqlite/outbox-repository.js +0 -103
- package/src/storage/sqlite/snapshot-repository.js +0 -69
- package/src/storage/sqlite/spaces-repository.js +0 -52
- package/src/storage/sqlite/subscriptions-repository.js +0 -54
- package/src/storage/sqlite-store.js +0 -159
- package/src/storage/stable-json.js +0 -24
- package/src/storage/store-port.js +0 -20
- package/src/store.js +0 -2
|
@@ -12,14 +12,17 @@ Accumulate reusable knowledge without interrupting the user's normal work. Keep
|
|
|
12
12
|
At the start of every user task, first use the lifecycle context supplied by the host when one
|
|
13
13
|
exists. A Claude Code `UserPromptSubmit` hook calls `begin_task_context`, which already loads
|
|
14
14
|
collaboration preferences and resolves the exact local personal project from the current working
|
|
15
|
-
directory.
|
|
15
|
+
directory. It also uses the submitted prompt transiently for bounded automatic project-memory
|
|
16
|
+
recall and never stores or returns that prompt. Apply the returned `task_knowledge_recall` and
|
|
17
|
+
preferences, then retain the opaque task token for the final checkpoint;
|
|
16
18
|
do not redundantly call `get_collaboration_preferences`.
|
|
17
19
|
|
|
18
20
|
When the host has not supplied lifecycle context, call exactly
|
|
19
21
|
`get_collaboration_preferences` before any other tool or answer. Pass `projectPath` as the current
|
|
20
|
-
working directory. This is the prompt-only fallback
|
|
21
|
-
hook. Fuli uses
|
|
22
|
-
exact local personal project itself. Do not infer
|
|
22
|
+
working directory and `taskPrompt` as the current user request. This is the prompt-only fallback
|
|
23
|
+
for Agents without an equivalent lifecycle hook. Fuli uses both only for this local MCP call,
|
|
24
|
+
never stores or returns them, and resolves the exact local personal project itself. Do not infer
|
|
25
|
+
or guess `personalProjectId` in the Agent.
|
|
23
26
|
|
|
24
27
|
Apply only `effective_preferences`. Personal-global preferences apply in every user task;
|
|
25
28
|
project-scoped preferences layer on only for the exact selected project. Do not apply items
|
|
@@ -42,6 +45,9 @@ current working directory. It resolves the exact active project without requirin
|
|
|
42
45
|
copy an ID, searches that child project first, and then includes only inheritable knowledge from
|
|
43
46
|
authorized parents or shared-source projects. Use `search_knowledge_graph` for personal-global
|
|
44
47
|
context, explicitly named extra projects, subscribed public projects, or advanced scoped queries.
|
|
48
|
+
Use `search_connected_knowledge` for a project-bound third-party source or explicitly selected
|
|
49
|
+
public project. Keep its source sets separate and obey the project's conflict policy;
|
|
50
|
+
`agent_decide` affects only the current response and never mutates or confirms a source.
|
|
45
51
|
|
|
46
52
|
Search when prior durable context can materially improve the task.
|
|
47
53
|
Before saying that a stable fact is unknown or asking the user to provide it again, search
|
|
@@ -50,6 +56,15 @@ requirements, terminology, architecture, prior decisions, runbooks, rationale, a
|
|
|
50
56
|
personal preferences. Do not search for a fully self-contained task or use Fuli as proof of live
|
|
51
57
|
external state.
|
|
52
58
|
|
|
59
|
+
First inspect `task_knowledge_recall` returned by the lifecycle or preference tool. Its automatic
|
|
60
|
+
query plan is deliberately bounded and based on implementation trigger categories, not on a
|
|
61
|
+
claim that every returned candidate supports the task. If it misses or its candidates are
|
|
62
|
+
irrelevant while durable context may still matter, call `search_current_project_knowledge`
|
|
63
|
+
yourself. Derive one to four focused queries from the action, artifact, target system, distinctive
|
|
64
|
+
identifier, and likely runbook category. Never pass the full conversational request as the sole
|
|
65
|
+
query. For example, a version-release request should search release/publish and push/commit
|
|
66
|
+
runbook concepts separately instead of searching the entire sentence.
|
|
67
|
+
|
|
53
68
|
Pending knowledge is eligible for on-demand retrieval and must remain visibly marked.
|
|
54
69
|
`agent_confirmed` knowledge is usable but ranks below user or authoritative-source
|
|
55
70
|
confirmation.
|
|
@@ -156,6 +171,11 @@ Capture confirmed, stable information:
|
|
|
156
171
|
- 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.
|
|
157
172
|
- Project: PRD facts, requirements, terminology, decisions, constraints, architecture, routes, APIs, metrics, and document-derived knowledge.
|
|
158
173
|
|
|
174
|
+
For a runbook, decision, route, or other item likely to be recalled from shorthand, add a bounded
|
|
175
|
+
`attributes.searchTerms` string array containing only neutral retrieval phrases supported by the
|
|
176
|
+
item and observed task vocabulary. Search terms are retrieval metadata, not new factual claims;
|
|
177
|
+
do not add speculative synonyms, credentials, or unrelated product names.
|
|
178
|
+
|
|
159
179
|
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.
|
|
160
180
|
|
|
161
181
|
For a confirmed project decision, prefer `record_decision_trace` over a flat note. Preserve the
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: flreview
|
|
3
|
+
description: Use when the user explicitly enters /flreview or explicitly asks to run the Fuli personal knowledge review command.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Fuli Knowledge Review
|
|
7
|
+
|
|
8
|
+
The invoking Agent owns each review session. FULI supplies durable knowledge, deterministic candidate ranking, and write tools; the Agent generates a fresh interactive review page for this invocation. There is no permanent **知识回顾** tab to open.
|
|
9
|
+
|
|
10
|
+
## Start The Session
|
|
11
|
+
|
|
12
|
+
For every exact `/flreview` invocation or explicit request to run the Fuli knowledge review:
|
|
13
|
+
|
|
14
|
+
1. Use `list_knowledge_spaces` to identify the active personal space, then call `list_personal_projects` so every project choice uses a real local personal project.
|
|
15
|
+
2. Use the `visualize` Skill to render an inline setup page in the current task. Collect scope and review depth in that page, not as serial chat questions. Ask patience first. If the user selects **完全没耐心**, hide mood, available-time, and token-budget fields and use a small highest-priority batch. Otherwise collect mood, available time, and token comfort together on the same setup page. These answers control only batch size and explanation depth, never Provider ranking. If the user's request already gives an exact scope and depth, skip setup.
|
|
16
|
+
3. On the setup page's explicit start action, call `start_knowledge_review` with the selected scope. Then call `list_knowledge_review_candidates` with a bounded page size matching the selected depth.
|
|
17
|
+
4. Generate all candidates from that batch on the same interactive page. Show all candidates at once; do not reveal them one at a time.
|
|
18
|
+
|
|
19
|
+
The setup scope tree is:
|
|
20
|
+
|
|
21
|
+
- **全部** — global preferences plus every local personal project's knowledge and project-scoped preferences.
|
|
22
|
+
- **个人偏好** — global preferences or one selected local personal project's preferences.
|
|
23
|
+
- **个人项目** — one selected local personal project or all local personal projects. A project includes its knowledge and project-scoped preferences.
|
|
24
|
+
|
|
25
|
+
Never infer the current directory as the selected project. Never include subscribed or public projects.
|
|
26
|
+
|
|
27
|
+
A bounded page limit is a batch size, not a fixed total-question cap.
|
|
28
|
+
|
|
29
|
+
## Generated Review Page
|
|
30
|
+
|
|
31
|
+
Follow the `visualize` Skill's inline-artifact rules. The artifact must not use `fetch`, XHR, WebSocket, or a direct FULI API. Every submitted action must call:
|
|
32
|
+
|
|
33
|
+
```js
|
|
34
|
+
await window.openai.sendFollowUpMessage({ prompt, title })
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The setup action envelope contains only `action`, `personal_space_id`, scope, optional project, and depth inputs. It must not invent a `review_id`, `candidate_key`, `item_id`, or `item_kind`. Every candidate action envelope contains `personal_space_id`, `review_id`, `candidate_key`, `item_id`, and `item_kind`, plus only the fields needed for that action. Treat a valid envelope as a continuation of the active review, not as a new `/flreview` invocation.
|
|
38
|
+
|
|
39
|
+
Each candidate card shows its content, current scope and source projects, confirmation state, `current_quadrant`, selection reasons, relevant evidence counts, utility, and confidence. Provider order is authoritative; do not invent a second score. Render these actions:
|
|
40
|
+
|
|
41
|
+
- **确认保留**
|
|
42
|
+
- **修改**
|
|
43
|
+
- **调整范围**
|
|
44
|
+
- **失效**
|
|
45
|
+
- **稍后处理**
|
|
46
|
+
- **交给 AI 判断**
|
|
47
|
+
|
|
48
|
+
Do not present the review as one candidate at a time in chat. Chat may contain only a concise status and the generated interactive artifact.
|
|
49
|
+
|
|
50
|
+
## Action Semantics
|
|
51
|
+
|
|
52
|
+
Do not mutate knowledge until the user submits an explicit visualization action. After any mutation, call `record_knowledge_review_progress` only after the write succeeds. If progress recording fails after a successful write, retry only progress; never repeat the mutation.
|
|
53
|
+
|
|
54
|
+
### 确认保留
|
|
55
|
+
|
|
56
|
+
If the candidate is already confirmed, record `confirmed`. If it is pending, first call `revise_personal_knowledge` with `action: confirm`, an auditable user confirmation basis, and no content or taxonomy changes; then record `confirmed`.
|
|
57
|
+
|
|
58
|
+
### 修改
|
|
59
|
+
|
|
60
|
+
Collect the exact replacement content and a reason in the card. Call `revise_personal_knowledge` with `action: update`, then record `updated`.
|
|
61
|
+
|
|
62
|
+
### 调整范围
|
|
63
|
+
|
|
64
|
+
- For a global preference with exactly one source project, offer a one-click move to that source project. With multiple source projects, require a project selection. Call `set_personal_preference_scope` with `scope: project` and the selected source project.
|
|
65
|
+
- For a project-scoped preference, offer promotion to global and call `set_personal_preference_scope` with `scope: global`.
|
|
66
|
+
- Ordinary project knowledge is not a personal preference. Never offer to turn ordinary project knowledge into a global preference.
|
|
67
|
+
|
|
68
|
+
After a successful scope write, record `updated`. Never change scope automatically.
|
|
69
|
+
|
|
70
|
+
### 失效
|
|
71
|
+
|
|
72
|
+
Offer these preset reasons and a custom reason field:
|
|
73
|
+
|
|
74
|
+
- 不应该沉淀为一条复利知识
|
|
75
|
+
- 不知所云
|
|
76
|
+
- 过期了
|
|
77
|
+
- 只在当时生效
|
|
78
|
+
|
|
79
|
+
Selecting a preset or entering a non-empty custom reason is sufficient authorization. There is no second confirmation and no confirmation checkbox. Call `revise_personal_knowledge` with `action: invalidate`, then record `invalidated` with the exact reason.
|
|
80
|
+
|
|
81
|
+
### 稍后处理
|
|
82
|
+
|
|
83
|
+
Do not mutate the knowledge. Record `deferred` and remove it from the current page. FULI carries it into the next review as `deferred_from_previous` even if no other ranking threshold still matches.
|
|
84
|
+
|
|
85
|
+
### 交给 AI 判断
|
|
86
|
+
|
|
87
|
+
This action means the user cannot responsibly classify the item now. Call `revise_personal_knowledge` with `action: update`, `currentQuadrant: unknown_unknown`, and a short `reasoningSummary` stating that the user delegated judgment during knowledge review. Never send `originQuadrant`: discovery classification is immutable evidence. The required order is `revise_personal_knowledge` before `record_knowledge_review_progress`. After that write succeeds, record `delegated_to_ai` and remove the item from the current page.
|
|
88
|
+
|
|
89
|
+
## Continue, Pause, Or Complete
|
|
90
|
+
|
|
91
|
+
After each successful action, refresh the current review's remaining candidates and regenerate the interactive page. Load the next bounded batch when the current batch is exhausted.
|
|
92
|
+
|
|
93
|
+
Call `finish_knowledge_review` with `paused` when the user explicitly pauses. Call it with `completed` only when no candidates remain or the user explicitly finishes the selected scope. Only completion advances the last-completed watermark.
|
|
94
|
+
|
|
95
|
+
## Guardrails
|
|
96
|
+
|
|
97
|
+
- FULI-saved knowledge is the only candidate source. Never fabricate, mock, or supplement candidates from ordinary search.
|
|
98
|
+
- Keep original evidence and revision history intact.
|
|
99
|
+
- Never silently resolve conflicts, invalidate knowledge, or change scope.
|
|
100
|
+
- If a required FULI or visualization capability is unavailable, report the exact missing capability and stop safely.
|
|
@@ -21,6 +21,13 @@ const sourceUri = {
|
|
|
21
21
|
pattern: '^[Hh][Tt][Tt][Pp][Ss]?://\\S+$'
|
|
22
22
|
};
|
|
23
23
|
const attributes = { type: 'object', additionalProperties: true };
|
|
24
|
+
const entityAttributes = {
|
|
25
|
+
type: 'object',
|
|
26
|
+
properties: {
|
|
27
|
+
searchTerms: arraySchema(boundedString(256), { minItems: 1, maxItems: 32 })
|
|
28
|
+
},
|
|
29
|
+
additionalProperties: true
|
|
30
|
+
};
|
|
24
31
|
const epistemicQuadrant = enumSchema([
|
|
25
32
|
'known_known', 'known_unknown', 'unknown_known', 'unknown_unknown'
|
|
26
33
|
]);
|
|
@@ -48,6 +55,9 @@ const confirmationBasis = objectSchema({
|
|
|
48
55
|
agentPolicyVersion: nullableStringSchema()
|
|
49
56
|
}, ['existenceReason', 'quadrantReason', 'proposedBy']);
|
|
50
57
|
const knowledgeItemKind = enumSchema(['entity', 'relationship']);
|
|
58
|
+
const knowledgeReviewScope = enumSchema([
|
|
59
|
+
'all', 'preferences_global', 'preferences_project', 'projects_all', 'project'
|
|
60
|
+
]);
|
|
51
61
|
const humanChangeStatus = enumSchema([
|
|
52
62
|
'all', 'human_changed', 'unseen', 'viewed', 'reviewed'
|
|
53
63
|
]);
|
|
@@ -84,7 +94,7 @@ const entity = objectSchema({
|
|
|
84
94
|
type: { ...boundedString(64), pattern: '^[A-Za-z][A-Za-z0-9_]*$' },
|
|
85
95
|
summary: boundedString(4096),
|
|
86
96
|
...epistemicFields,
|
|
87
|
-
attributes
|
|
97
|
+
attributes: entityAttributes
|
|
88
98
|
}, [
|
|
89
99
|
'key', 'name', 'type', 'originQuadrant',
|
|
90
100
|
'confirmationStatus', 'confirmationBasis'
|
|
@@ -202,10 +212,11 @@ export const GRAPH_TOOL_DEFINITIONS = [
|
|
|
202
212
|
{
|
|
203
213
|
name: 'begin_task_context',
|
|
204
214
|
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
|
|
215
|
+
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 uses taskPrompt transiently for bounded automatic recall when the request signals a stable prior fact, runbook, URL, decision, release, deployment, or authentication method. It never stores or returns projectPath or taskPrompt. Inspect task_knowledge_recall before asking the user to repeat stable context. Returns an opaque taskContextToken. Claude Code setup installs this as a deterministic UserPromptSubmit hook; manual Agent calls are a fallback.',
|
|
206
216
|
inputSchema: objectSchema({
|
|
207
217
|
sessionId: id,
|
|
208
|
-
projectPath: boundedString(4096)
|
|
218
|
+
projectPath: boundedString(4096),
|
|
219
|
+
taskPrompt: boundedString(8192)
|
|
209
220
|
}, ['sessionId', 'projectPath'])
|
|
210
221
|
},
|
|
211
222
|
{
|
|
@@ -228,9 +239,10 @@ export const GRAPH_TOOL_DEFINITIONS = [
|
|
|
228
239
|
{
|
|
229
240
|
name: 'get_collaboration_preferences',
|
|
230
241
|
title: 'READ FIRST · Load task collaboration preferences',
|
|
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
|
|
242
|
+
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 and taskPrompt as the current user request. Fuli uses both transiently, never stores or returns them, resolves one exact registered local project, and performs bounded automatic recall only when the request signals stable prior context. Inspect task_knowledge_recall before asking the user to repeat a project fact or method. On a miss, search_current_project_knowledge with one to four focused action, artifact, target-system, or identifier queries; never use the full conversational request as the only query. Apply effective_preferences before answering or constructing tool arguments. Resolve only relevant deferred_conflicts first. For write tools, enforce preferences in the actual payload; the final answer is not compliance. Personal-global preferences always apply; 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.',
|
|
232
243
|
inputSchema: objectSchema({
|
|
233
244
|
projectPath: boundedString(4096),
|
|
245
|
+
taskPrompt: boundedString(8192),
|
|
234
246
|
personalProjectId: nullableStringSchema(),
|
|
235
247
|
limit: integerSchema({ minimum: 1, maximum: 200 })
|
|
236
248
|
}, ['projectPath'])
|
|
@@ -323,6 +335,22 @@ export const GRAPH_TOOL_DEFINITIONS = [
|
|
|
323
335
|
includePending: booleanSchema()
|
|
324
336
|
}, ['personalSpaceId', 'query'])
|
|
325
337
|
},
|
|
338
|
+
{
|
|
339
|
+
name: 'search_connected_knowledge',
|
|
340
|
+
title: 'READ · Search personal, public, and third-party knowledge',
|
|
341
|
+
description: 'Search the exact personal project, explicitly selected subscribed public projects, and live read-only third-party bindings in one request. Use all_local_confirmed only after explicit user confirmation. Results remain separated by source and preserve provenance; public-space integration is Beta. The response includes the project conflict policy. Under ask_human, surface a material conflict in the Agent conversation and ask the user before selecting durable truth. Under agent_decide, the Agent may choose for the current response only, but must explain its basis and sources. This tool never rewrites, confirms, invalidates, or publishes underlying knowledge.',
|
|
342
|
+
inputSchema: objectSchema({
|
|
343
|
+
personalSpaceId: id,
|
|
344
|
+
personalProjectId: id,
|
|
345
|
+
query: shortText,
|
|
346
|
+
contextPersonalProjectIds: arraySchema(id, { maxItems: 15 }),
|
|
347
|
+
personalProjectScope: enumSchema(['bounded', 'all_local_confirmed']),
|
|
348
|
+
projectIds: arraySchema(id, { maxItems: 32 }),
|
|
349
|
+
limit: integerSchema({ minimum: 1, maximum: 100 }),
|
|
350
|
+
includeHistorical: booleanSchema(),
|
|
351
|
+
includePending: booleanSchema()
|
|
352
|
+
}, ['personalSpaceId', 'personalProjectId', 'query'])
|
|
353
|
+
},
|
|
326
354
|
{
|
|
327
355
|
name: 'record_knowledge_usage',
|
|
328
356
|
description: 'Record that personal knowledge materially affected the Agent final answer or completed action. Call only after the use actually occurs: cited means the answer explicitly relies on the item; applied means the item changed an implementation or decision. Retrieval, inspection, automatic preference injection, and merely appearing in context do not qualify. taskId must be the caller-stable identifier for the current user task and must be reused on retries; do not generate a fresh ID to recount the same task. Events are idempotent per knowledge item, task, use kind, and content generation. Repeated qualified use may promote pending knowledge to agent_confirmed, never to human-confirmed or public-eligible.',
|
|
@@ -463,15 +491,62 @@ export const GRAPH_TOOL_DEFINITIONS = [
|
|
|
463
491
|
description: 'List local personal project profiles and their current evidence-backed coverage summaries.',
|
|
464
492
|
inputSchema: objectSchema({ personalSpaceId: id })
|
|
465
493
|
},
|
|
494
|
+
{
|
|
495
|
+
name: 'start_knowledge_review',
|
|
496
|
+
title: 'REVIEW · Start or resume personal knowledge review',
|
|
497
|
+
description: 'Start or resume the exact selected /flreview scope. A first review has no watermark and scans all in-scope history. A later review uses only the last completed run as its watermark; paused runs never advance it. Project scopes accept only local personal project IDs.',
|
|
498
|
+
inputSchema: objectSchema({
|
|
499
|
+
personalSpaceId: id,
|
|
500
|
+
scope: knowledgeReviewScope,
|
|
501
|
+
personalProjectId: nullableStringSchema()
|
|
502
|
+
}, ['personalSpaceId', 'scope'])
|
|
503
|
+
},
|
|
504
|
+
{
|
|
505
|
+
name: 'list_knowledge_review_candidates',
|
|
506
|
+
title: 'REVIEW · List ranked personal knowledge candidates',
|
|
507
|
+
description: 'List one bounded page for an active or paused review. Provider policy ranks new/changed knowledge, conflicts or attention, low-weight knowledge, then repeated cross-session patterns. Already decided items stay out of the current run. The page limit is not a total-question cap.',
|
|
508
|
+
inputSchema: objectSchema({
|
|
509
|
+
personalSpaceId: id,
|
|
510
|
+
reviewId: id,
|
|
511
|
+
limit: integerSchema({ minimum: 1, maximum: 50 })
|
|
512
|
+
}, ['personalSpaceId', 'reviewId'])
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
name: 'record_knowledge_review_progress',
|
|
516
|
+
title: 'WRITE · Record one review outcome',
|
|
517
|
+
description: 'Record one user-authorized candidate outcome after any required knowledge mutation succeeds. confirmed keeps the item, updated/invalidated follow successful writes, deferred carries it into the next review, and delegated_to_ai follows a successful current-quadrant change to unknown_unknown.',
|
|
518
|
+
inputSchema: objectSchema({
|
|
519
|
+
personalSpaceId: id,
|
|
520
|
+
reviewId: id,
|
|
521
|
+
candidateKey: {
|
|
522
|
+
...boundedString(520),
|
|
523
|
+
pattern: '^(entity|relationship):.+'
|
|
524
|
+
},
|
|
525
|
+
outcome: enumSchema([
|
|
526
|
+
'confirmed', 'updated', 'invalidated', 'deferred', 'delegated_to_ai'
|
|
527
|
+
]),
|
|
528
|
+
note: nullableStringSchema()
|
|
529
|
+
}, ['personalSpaceId', 'reviewId', 'candidateKey', 'outcome'])
|
|
530
|
+
},
|
|
531
|
+
{
|
|
532
|
+
name: 'finish_knowledge_review',
|
|
533
|
+
title: 'WRITE · Pause or complete knowledge review',
|
|
534
|
+
description: 'Pause a review without moving its watermark, or complete it and establish the next run watermark. Use completed only when the selected scope is exhausted or the user explicitly finishes it.',
|
|
535
|
+
inputSchema: objectSchema({
|
|
536
|
+
personalSpaceId: id,
|
|
537
|
+
reviewId: id,
|
|
538
|
+
disposition: enumSchema(['paused', 'completed'])
|
|
539
|
+
}, ['personalSpaceId', 'reviewId', 'disposition'])
|
|
540
|
+
},
|
|
466
541
|
{
|
|
467
542
|
name: 'revise_personal_knowledge',
|
|
468
|
-
description: '
|
|
543
|
+
description: 'Confirm, correct, invalidate, or restore one personal entity or relationship while preserving revision history and original evidence. This is the same personal-only operation used by the management UI.',
|
|
469
544
|
inputSchema: objectSchema({
|
|
470
545
|
personalSpaceId: id,
|
|
471
546
|
personalProjectId: nullableStringSchema(),
|
|
472
547
|
itemKind: knowledgeItemKind,
|
|
473
548
|
itemId: id,
|
|
474
|
-
action: enumSchema(['update', 'invalidate', 'restore']),
|
|
549
|
+
action: enumSchema(['confirm', 'update', 'invalidate', 'restore']),
|
|
475
550
|
reason: shortText,
|
|
476
551
|
name: nullableStringSchema(),
|
|
477
552
|
summary: nullableStringSchema(),
|
|
@@ -22,6 +22,12 @@ const HANDLERS = Object.freeze({
|
|
|
22
22
|
agentInvocation: true,
|
|
23
23
|
agentToolName: 'search_knowledge_graph'
|
|
24
24
|
}),
|
|
25
|
+
search_connected_knowledge: (app, input) => {
|
|
26
|
+
if (!app.connectedKnowledge) {
|
|
27
|
+
throw new Error('Connected knowledge runtime is unavailable');
|
|
28
|
+
}
|
|
29
|
+
return app.connectedKnowledge.query(input);
|
|
30
|
+
},
|
|
25
31
|
record_knowledge_usage: (app, input) => app.recordKnowledgeUsage(input),
|
|
26
32
|
record_knowledge_feedback: (app, input) => app.recordKnowledgeFeedback(input),
|
|
27
33
|
search_current_project_knowledge: (app, input) =>
|
|
@@ -53,6 +59,12 @@ const HANDLERS = Object.freeze({
|
|
|
53
59
|
app.upsertPersonalProject(input)
|
|
54
60
|
),
|
|
55
61
|
list_personal_projects: (app, input) => app.listPersonalProjects(input),
|
|
62
|
+
start_knowledge_review: (app, input) => app.startKnowledgeReview(input),
|
|
63
|
+
list_knowledge_review_candidates: (app, input) =>
|
|
64
|
+
app.listKnowledgeReviewCandidates(input),
|
|
65
|
+
record_knowledge_review_progress: (app, input) =>
|
|
66
|
+
app.recordKnowledgeReviewProgress(input),
|
|
67
|
+
finish_knowledge_review: (app, input) => app.finishKnowledgeReview(input),
|
|
56
68
|
revise_personal_knowledge: (app, input) => app.reviseKnowledgeItem({
|
|
57
69
|
...input,
|
|
58
70
|
operationActor: 'agent'
|
|
@@ -12,10 +12,10 @@ General:
|
|
|
12
12
|
--version, -v
|
|
13
13
|
|
|
14
14
|
Local service:
|
|
15
|
-
start [--port
|
|
15
|
+
start [--port PORT] [--open] [--lan|--no-lan] [--rebuild] [--data-dir DIR] [--personal-space NAME]
|
|
16
16
|
stop [--data-dir DIR]
|
|
17
|
-
restart [--port
|
|
18
|
-
status [--json] [--data-dir DIR]
|
|
17
|
+
restart [--port PORT] [--open] [--lan|--no-lan] [--rebuild] [--data-dir DIR] [--personal-space NAME]
|
|
18
|
+
status [--json] [--data-dir DIR] [--port PORT]
|
|
19
19
|
open [--data-dir DIR]
|
|
20
20
|
|
|
21
21
|
Install and Agent connection:
|
|
@@ -6,6 +6,11 @@ import {
|
|
|
6
6
|
stopLocalRuntime
|
|
7
7
|
} from '../local-runtime/lifecycle.js';
|
|
8
8
|
import { resolveSetupPaths } from '../setup/paths.js';
|
|
9
|
+
import {
|
|
10
|
+
DEFAULT_RUNTIME_SETTINGS,
|
|
11
|
+
readRuntimeSettings,
|
|
12
|
+
runtimeSettingsWithOverrides
|
|
13
|
+
} from '../system/runtime-settings.js';
|
|
9
14
|
import { parseLocalRuntimeOptions } from './local-runtime-options.js';
|
|
10
15
|
|
|
11
16
|
export async function runLocalRuntimeCommand(command, args, dependencies = {}) {
|
|
@@ -13,8 +18,24 @@ export async function runLocalRuntimeCommand(command, args, dependencies = {}) {
|
|
|
13
18
|
const resolvePaths = dependencies.resolvePaths ?? resolveSetupPaths;
|
|
14
19
|
const env = dependencies.env ?? process.env;
|
|
15
20
|
const paths = resolvePaths({ dataDir: options.dataDir, env });
|
|
21
|
+
const readSettings = dependencies.readSettings ?? readRuntimeSettings;
|
|
22
|
+
const savedSettings = paths.runtimeSettingsPath
|
|
23
|
+
? readSettings(paths.runtimeSettingsPath)
|
|
24
|
+
: DEFAULT_RUNTIME_SETTINGS;
|
|
25
|
+
const runtimeSettings = runtimeSettingsWithOverrides(savedSettings, {
|
|
26
|
+
consolePort: options.port,
|
|
27
|
+
lanAccess: options.lan
|
|
28
|
+
});
|
|
16
29
|
const write = dependencies.write ?? writeLine;
|
|
17
|
-
const input = {
|
|
30
|
+
const input = {
|
|
31
|
+
...options,
|
|
32
|
+
port: runtimeSettings.ports.console,
|
|
33
|
+
lan: runtimeSettings.lanAccess,
|
|
34
|
+
runtimeSettings,
|
|
35
|
+
paths,
|
|
36
|
+
env,
|
|
37
|
+
onProgress: write
|
|
38
|
+
};
|
|
18
39
|
const handlers = {
|
|
19
40
|
start: dependencies.start ?? startLocalRuntime,
|
|
20
41
|
stop: dependencies.stop ?? stopLocalRuntime,
|
|
@@ -39,37 +60,39 @@ export async function runLocalRuntimeCommand(command, args, dependencies = {}) {
|
|
|
39
60
|
|
|
40
61
|
export function formatLocalRuntimeResult(command, result) {
|
|
41
62
|
if (command === 'start') {
|
|
42
|
-
const
|
|
43
|
-
|
|
63
|
+
const title = result.status === 'running'
|
|
64
|
+
? 'Fuli local services are already running.'
|
|
65
|
+
: 'Fuli local services started.';
|
|
66
|
+
return formatStartedRuntime(title, result);
|
|
44
67
|
}
|
|
45
68
|
if (command === 'restart') {
|
|
46
|
-
return formatStartedRuntime('Fuli
|
|
69
|
+
return formatStartedRuntime('Fuli local services restarted.', result);
|
|
47
70
|
}
|
|
48
71
|
if (command === 'stop') {
|
|
49
72
|
if (result.status === 'partial') {
|
|
50
73
|
return [
|
|
51
|
-
'Fuli
|
|
52
|
-
'
|
|
74
|
+
'The local Fuli Provider stopped, but the unverified UI process was left running.',
|
|
75
|
+
'Run fl status to inspect it without risking another process.'
|
|
53
76
|
].join('\n');
|
|
54
77
|
}
|
|
55
78
|
return result.console === 'not_running' && result.providers === 'not_initialized'
|
|
56
|
-
? 'Fuli
|
|
57
|
-
: 'Fuli
|
|
79
|
+
? 'Fuli local services have not been initialized.'
|
|
80
|
+
: 'Fuli local services stopped. Graph data was preserved.';
|
|
58
81
|
}
|
|
59
|
-
if (command === 'open') return
|
|
82
|
+
if (command === 'open') return `Opened: ${result.url}`;
|
|
60
83
|
if (command === 'status') return formatStatus(result);
|
|
61
84
|
throw new TypeError(`Unknown local runtime command: ${command}`);
|
|
62
85
|
}
|
|
63
86
|
|
|
64
87
|
function formatStartedRuntime(title, result) {
|
|
65
|
-
const lines = [title,
|
|
88
|
+
const lines = [title, `Management UI: ${result.url}`];
|
|
66
89
|
if (result.lan === true) {
|
|
67
90
|
lines.push(
|
|
68
|
-
'
|
|
91
|
+
'LAN URLs:',
|
|
69
92
|
...result.lanUrls.map((url) => ` ${url}`),
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
'
|
|
93
|
+
`Username: ${result.lanAccess.username}`,
|
|
94
|
+
`Temporary access code: ${result.lanAccess.accessCode}`,
|
|
95
|
+
'Use only on trusted Wi-Fi. Restarting LAN mode rotates the access code.'
|
|
73
96
|
);
|
|
74
97
|
}
|
|
75
98
|
return lines.join('\n');
|
|
@@ -77,24 +100,24 @@ function formatStartedRuntime(title, result) {
|
|
|
77
100
|
|
|
78
101
|
function formatStatus(result) {
|
|
79
102
|
const labels = {
|
|
80
|
-
running: '
|
|
81
|
-
degraded: '
|
|
82
|
-
stopped: '
|
|
83
|
-
not_configured: '
|
|
84
|
-
ready: '
|
|
85
|
-
unavailable: '
|
|
86
|
-
not_connected: '
|
|
87
|
-
not_configured_provider: '
|
|
88
|
-
unverified: '
|
|
103
|
+
running: 'running',
|
|
104
|
+
degraded: 'degraded',
|
|
105
|
+
stopped: 'stopped',
|
|
106
|
+
not_configured: 'not initialized',
|
|
107
|
+
ready: 'ready',
|
|
108
|
+
unavailable: 'unavailable',
|
|
109
|
+
not_connected: 'not connected',
|
|
110
|
+
not_configured_provider: 'not configured',
|
|
111
|
+
unverified: 'identity unverified'
|
|
89
112
|
};
|
|
90
113
|
const publicSummary = result.public.configured
|
|
91
|
-
? `${labels[result.public.status] ?? result.public.status}
|
|
114
|
+
? `${labels[result.public.status] ?? result.public.status} (${result.public.providers.length} Providers)`
|
|
92
115
|
: labels.not_connected;
|
|
93
116
|
return [
|
|
94
|
-
`Fuli
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
117
|
+
`Fuli local status: ${labels[result.status] ?? result.status}`,
|
|
118
|
+
`Management UI: ${labels[result.console.status] ?? result.console.status} · ${result.console.url}`,
|
|
119
|
+
`Personal graph: ${providerLabel(result.personal.status, labels)}`,
|
|
120
|
+
`Shared services: ${publicSummary}`
|
|
98
121
|
].join('\n');
|
|
99
122
|
}
|
|
100
123
|
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { DEFAULT_FULI_PORT } from '../defaults.js';
|
|
2
|
-
|
|
3
1
|
const VALUE_OPTIONS = Object.freeze({
|
|
4
2
|
'--data-dir': 'dataDir',
|
|
5
3
|
'--personal-space': 'personalSpaceName',
|
|
@@ -9,15 +7,16 @@ const VALUE_OPTIONS = Object.freeze({
|
|
|
9
7
|
const BOOLEAN_OPTIONS = Object.freeze({
|
|
10
8
|
'--open': 'open',
|
|
11
9
|
'--rebuild': 'rebuild',
|
|
12
|
-
'--lan': 'lan',
|
|
13
10
|
'--json': 'json'
|
|
14
11
|
});
|
|
15
12
|
|
|
16
13
|
const ALLOWED_BY_COMMAND = Object.freeze({
|
|
17
|
-
start: new Set([
|
|
14
|
+
start: new Set([
|
|
15
|
+
'--data-dir', '--personal-space', '--port', '--open', '--rebuild', '--lan', '--no-lan'
|
|
16
|
+
]),
|
|
18
17
|
stop: new Set(['--data-dir']),
|
|
19
18
|
restart: new Set([
|
|
20
|
-
'--data-dir', '--personal-space', '--port', '--open', '--rebuild', '--lan'
|
|
19
|
+
'--data-dir', '--personal-space', '--port', '--open', '--rebuild', '--lan', '--no-lan'
|
|
21
20
|
]),
|
|
22
21
|
status: new Set(['--data-dir', '--port', '--json']),
|
|
23
22
|
open: new Set(['--data-dir'])
|
|
@@ -28,11 +27,11 @@ export function parseLocalRuntimeOptions(command, args = []) {
|
|
|
28
27
|
if (!allowed) throw new TypeError(`Unknown local runtime command: ${command}`);
|
|
29
28
|
const result = {
|
|
30
29
|
dataDir: null,
|
|
31
|
-
personalSpaceName: '
|
|
32
|
-
port:
|
|
30
|
+
personalSpaceName: 'Personal',
|
|
31
|
+
port: null,
|
|
33
32
|
open: false,
|
|
34
33
|
rebuild: false,
|
|
35
|
-
lan:
|
|
34
|
+
lan: null,
|
|
36
35
|
json: false
|
|
37
36
|
};
|
|
38
37
|
const seen = new Set();
|
|
@@ -43,6 +42,13 @@ export function parseLocalRuntimeOptions(command, args = []) {
|
|
|
43
42
|
if (seen.has(flag)) throw new TypeError(`Duplicate ${flag}`);
|
|
44
43
|
seen.add(flag);
|
|
45
44
|
|
|
45
|
+
if (flag === '--lan' || flag === '--no-lan') {
|
|
46
|
+
const conflicting = flag === '--lan' ? '--no-lan' : '--lan';
|
|
47
|
+
if (seen.has(conflicting)) throw new TypeError('--lan and --no-lan cannot be combined');
|
|
48
|
+
result.lan = flag === '--lan';
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
|
|
46
52
|
const booleanKey = BOOLEAN_OPTIONS[flag];
|
|
47
53
|
if (booleanKey) {
|
|
48
54
|
result[booleanKey] = true;
|
|
@@ -27,15 +27,15 @@ export function parseAgentSelection(value, agents) {
|
|
|
27
27
|
|
|
28
28
|
const tokens = answer.split(/[\s,,]+/).filter(Boolean);
|
|
29
29
|
if (tokens.includes('0')) {
|
|
30
|
-
throw new TypeError('0
|
|
30
|
+
throw new TypeError('0 cannot be combined with another number');
|
|
31
31
|
}
|
|
32
32
|
if (tokens.some((token) => !/^\d+$/.test(token))) {
|
|
33
|
-
throw new TypeError('
|
|
33
|
+
throw new TypeError('Enter numbers, not Agent names');
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
const positions = new Set(tokens.map(Number));
|
|
37
37
|
if ([...positions].some((position) => position < 1 || position > agents.length)) {
|
|
38
|
-
throw new RangeError(
|
|
38
|
+
throw new RangeError(`Choose a number from 1-${agents.length}`);
|
|
39
39
|
}
|
|
40
40
|
return agents
|
|
41
41
|
.filter((_, index) => positions.has(index + 1))
|
|
@@ -86,21 +86,21 @@ export function selectedAgentIds(agents, state) {
|
|
|
86
86
|
|
|
87
87
|
export function formatAgentCheckboxSelection(agents, state) {
|
|
88
88
|
return [
|
|
89
|
-
'
|
|
89
|
+
'Multiple supported Agents were detected:',
|
|
90
90
|
...agents.map((agent, index) => {
|
|
91
91
|
const pointer = index === state.activeIndex ? '❯' : ' ';
|
|
92
92
|
const checkbox = state.selected[index] ? '[x]' : '[ ]';
|
|
93
93
|
const status = integrationStatusLabel(agent.integrationStatus);
|
|
94
94
|
return `${pointer} ${checkbox} ${agent.label}${status ? ` ${status}` : ''}`;
|
|
95
95
|
}),
|
|
96
|
-
'↑/↓
|
|
96
|
+
'↑/↓ Move · Space Toggle · A Select/Clear all · Enter Confirm'
|
|
97
97
|
].join('\n');
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
function integrationStatusLabel(status) {
|
|
101
|
-
if (status === 'connected') return '
|
|
102
|
-
if (status === 'update_available') return '
|
|
103
|
-
if (status === 'not_connected') return '
|
|
101
|
+
if (status === 'connected') return 'connected';
|
|
102
|
+
if (status === 'update_available') return 'update available';
|
|
103
|
+
if (status === 'not_connected') return 'not connected';
|
|
104
104
|
return '';
|
|
105
105
|
}
|
|
106
106
|
|
|
@@ -147,7 +147,7 @@ async function promptCheckboxAgentSelection(agents, { input, output }) {
|
|
|
147
147
|
return;
|
|
148
148
|
}
|
|
149
149
|
if (result.action === 'cancel') {
|
|
150
|
-
finish(() => reject(new Error('Setup
|
|
150
|
+
finish(() => reject(new Error('Setup cancelled')));
|
|
151
151
|
return;
|
|
152
152
|
}
|
|
153
153
|
if (result.action === 'update') {
|
|
@@ -161,7 +161,7 @@ async function promptCheckboxAgentSelection(agents, { input, output }) {
|
|
|
161
161
|
|
|
162
162
|
async function promptTextAgentSelection(agents, { input, output }) {
|
|
163
163
|
const terminal = createInterface({ input, output });
|
|
164
|
-
output.write('
|
|
164
|
+
output.write('Multiple supported Agents were detected:\n');
|
|
165
165
|
for (const [index, agent] of agents.entries()) {
|
|
166
166
|
output.write(` ${index + 1}. ${agent.label}\n`);
|
|
167
167
|
}
|
|
@@ -169,12 +169,12 @@ async function promptTextAgentSelection(agents, { input, output }) {
|
|
|
169
169
|
try {
|
|
170
170
|
while (true) {
|
|
171
171
|
const answer = await terminal.question(
|
|
172
|
-
'
|
|
172
|
+
'Select Agents (comma-separated numbers, Enter for all, 0 to skip) [all] '
|
|
173
173
|
);
|
|
174
174
|
try {
|
|
175
175
|
return parseAgentSelection(answer, agents);
|
|
176
176
|
} catch (error) {
|
|
177
|
-
output.write(
|
|
177
|
+
output.write(`Invalid selection: ${error.message}\n`);
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
180
|
} finally {
|