fuli-context 0.12.4 → 0.20.0-beta.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/dist/web/assets/{AboutPage-DKsgqteA.js → AboutPage-swHMgm8d.js} +1 -1
- package/dist/web/assets/{AgentHand-B5lezlek.js → AgentHand-aotYXirj.js} +1 -1
- package/dist/web/assets/{AgentName-C-V41xs0.js → AgentName-DaiSmFuk.js} +1 -1
- package/dist/web/assets/AgentProfilePage-C90Vo343.js +1 -0
- package/dist/web/assets/{AgentsDirectoryPage-IgN5QEdW.js → AgentsDirectoryPage-BiV0zVs4.js} +1 -1
- package/dist/web/assets/{ConnectionsPage-CXtNiTEF.js → ConnectionsPage-5HCXLXp3.js} +1 -1
- package/dist/web/assets/{EmployeeRecruitDialog-o6eyMw2g.js → EmployeeRecruitDialog-C7u31qkP.js} +1 -1
- package/dist/web/assets/{EmployeeWorkbenchPage-4EnGLauW.js → EmployeeWorkbenchPage-BHBWnrcC.js} +1 -1
- package/dist/web/assets/{KnowledgeEditDialog-BlBd6Xql.js → KnowledgeEditDialog-D-znf44H.js} +1 -1
- package/dist/web/assets/{KnowledgeInspector-Dl7SAgaY.js → KnowledgeInspector-CcU3PYaV.js} +1 -1
- package/dist/web/assets/{KnowledgeOrganizerPage-CpH4x6UA.js → KnowledgeOrganizerPage-C9hkFZ01.js} +1 -1
- package/dist/web/assets/KnowledgePage-ihfSfdh0.js +1 -0
- package/dist/web/assets/{KnowledgeWorkspace-4q_HfXas.js → KnowledgeWorkspace-WdcWXNSv.js} +1 -1
- package/dist/web/assets/{NotFoundPage-DtmfJSGx.js → NotFoundPage-CLtEdbfR.js} +1 -1
- package/dist/web/assets/{OverviewPage-C1zw_8_6.js → OverviewPage-CLKzau9g.js} +1 -1
- package/dist/web/assets/{PersonalProfilePage-tQyS7qsa.js → PersonalProfilePage-A02a3zQ1.js} +1 -1
- package/dist/web/assets/PersonalProjectsPage-5SDjPygh.js +1 -0
- package/dist/web/assets/{ProjectAgentFirstTask-Caf7c6F_.js → ProjectAgentFirstTask-KLVInwaa.js} +1 -1
- package/dist/web/assets/ProjectAgentsPage-BQj_XMd1.js +12 -0
- package/dist/web/assets/{ProjectAgentsPage-DWGkgIsr.css → ProjectAgentsPage-VYiEOpFJ.css} +1 -1
- package/dist/web/assets/{ProjectScopePicker-NyqcG6Iz.js → ProjectScopePicker-Bx4uycn7.js} +1 -1
- package/dist/web/assets/{PublicProjectsPage-CYwQLZmy.js → PublicProjectsPage-C7ci4IvJ.js} +1 -1
- package/dist/web/assets/{ReviewPage-EyuAtIOq.js → ReviewPage-CMAsStkm.js} +1 -1
- package/dist/web/assets/{RoundtablesPage-JVBXsJrs.js → RoundtablesPage-Q2wAZxkX.js} +1 -1
- package/dist/web/assets/SettingsPage-6FZ7_3-X.css +1 -0
- package/dist/web/assets/SettingsPage-CuQCjhJW.js +1 -0
- package/dist/web/assets/{SharedJefaReportPage-DFy5Ty7o.js → SharedJefaReportPage-DzNN_nGd.js} +1 -1
- package/dist/web/assets/TaskExecutionPlan-Bni76fd1.js +2 -0
- package/dist/web/assets/TaskExecutionPlan-CWRRp6u7.css +1 -0
- package/dist/web/assets/{UiButton-neeym_gA.js → UiButton-Dr16-h9x.js} +1 -1
- package/dist/web/assets/{UiDialog-BFVoqFnc.js → UiDialog-DBK46TV0.js} +1 -1
- package/dist/web/assets/{UiDisclosure-B-T7BmB5.js → UiDisclosure-DwG6hxCs.js} +1 -1
- package/dist/web/assets/{UiSegmented-D_kyRKDw.js → UiSegmented-DiQL22Tm.js} +1 -1
- package/dist/web/assets/{UiSelect-DDyct4mf.js → UiSelect-Dvbk5_6n.js} +1 -1
- package/dist/web/assets/{WritingTastePage-BPRq6kJ6.js → WritingTastePage-e8gY61zu.js} +1 -1
- package/dist/web/assets/{agent-pins-6LRHTMF6.js → agent-pins-CU7VfdIk.js} +1 -1
- package/dist/web/assets/{agent-profile-UgNiXgpU.css → agent-profile-DTn-2VAG.css} +1 -1
- package/dist/web/assets/{agent-work-summary-C-yW7x7A.js → agent-work-summary-DRJy9v48.js} +1 -1
- package/dist/web/assets/{attention-store-BGHyzWfv.js → attention-store-NDSr2coj.js} +1 -1
- package/dist/web/assets/{catalog-kb-cA8t6.js → catalog-C5DYqPHn.js} +1 -1
- package/dist/web/assets/{client-DodUFQDp.js → client-B9ebMaql.js} +1 -1
- package/dist/web/assets/{console-C2Lfifq-.js → console-C-3WEMad.js} +1 -1
- package/dist/web/assets/{conversation-launcher-settings-CowfVz7l.js → conversation-launcher-settings-jiI7_YEv.js} +1 -1
- package/dist/web/assets/{i18n-B4Fp9Oa5.js → i18n-x1XkVwhW.js} +3 -3
- package/dist/web/assets/{identity-DG_za-J7.js → identity-BrCgVI35.js} +1 -1
- package/dist/web/assets/{index-RxlM8aQV.js → index-DljXSFj8.js} +3 -3
- package/dist/web/assets/{judgment-types-DRqrjStd.js → judgment-types-DNdBT0an.js} +1 -1
- package/dist/web/assets/{model-C0eHDNsF.js → model-BTpIEeuF.js} +1 -1
- package/dist/web/assets/{personal-profile-graph-CHY3WYhi.js → personal-profile-graph-D1xokPXQ.js} +1 -1
- package/dist/web/assets/project-team-api-C6rXFu15.js +1 -0
- package/dist/web/assets/{shared-report-8JFvD9TZ.js → shared-report-00E2vwUk.js} +1 -1
- package/dist/web/assets/task-evidence-i9SDhd3E.js +1 -0
- package/dist/web/assets/{useAgentRoster-QDbOwtLj.js → useAgentRoster-BCx1Vft9.js} +1 -1
- package/dist/web/assets/{useApi-CROJJdhE-IuC5qwny.js → useApi-CROJJdhE-C53-85Eu.js} +1 -1
- package/dist/web/assets/{vue-router-nZMhR3dI.js → vue-router-DqHNZ3fU.js} +1 -1
- package/dist/web/index.html +15 -15
- package/docs/agent-conversations-and-collaboration.md +69 -1
- package/docs/agent-interface-architecture.md +11 -0
- package/docs/client-connections.md +42 -6
- package/docs/project-agent-memory.md +116 -1
- package/graph-provider/fuli_graph/agent_conversation_models.py +1 -1
- package/graph-provider/fuli_graph/app.py +6 -0
- package/graph-provider/fuli_graph/collaboration_context.py +38 -7
- package/graph-provider/fuli_graph/conversation_context.py +285 -25
- package/graph-provider/fuli_graph/default_project_lead.py +3 -2
- package/graph-provider/fuli_graph/models.py +3 -0
- package/graph-provider/fuli_graph/project_agent_access.py +6 -4
- package/graph-provider/fuli_graph/project_agent_context_models.py +4 -0
- package/graph-provider/fuli_graph/project_agent_memory_models.py +4 -4
- package/graph-provider/fuli_graph/project_agent_memory_routes.py +1 -1
- package/graph-provider/fuli_graph/project_agent_routes.py +6 -1
- package/graph-provider/fuli_graph/project_agent_task_models.py +41 -1
- package/graph-provider/fuli_graph/project_agent_work_log.py +3 -1
- package/graph-provider/fuli_graph/store_agent_conversations.py +26 -9
- package/graph-provider/fuli_graph/store_knowledge.py +6 -0
- package/graph-provider/fuli_graph/store_project_agent_context.py +28 -2
- package/graph-provider/fuli_graph/store_project_agent_memory.py +17 -7
- package/graph-provider/fuli_graph/store_project_agent_task_activity.py +2 -0
- package/graph-provider/fuli_graph/store_project_agent_task_reads.py +3 -0
- package/graph-provider/fuli_graph/store_project_agent_task_staffing.py +115 -31
- package/graph-provider/fuli_graph/store_project_agent_tasks.py +34 -8
- package/graph-provider/fuli_graph/store_task_contexts.py +3 -3
- package/graph-provider/fuli_graph/task_context_models.py +0 -6
- package/graph-provider/fuli_graph/task_notifications.py +54 -0
- package/npm-shrinkwrap.json +13 -0
- package/package.json +2 -1
- package/skills/capturing-session-knowledge/SKILL.md +8 -0
- package/src/agent-tools/agent-collaboration-definitions.js +8 -2
- package/src/agent-tools/agent-conversation-definitions.js +5 -5
- package/src/agent-tools/graph-definitions.js +4 -2
- package/src/agent-tools/graph-handlers.js +1 -0
- package/src/agent-tools/interface-catalog.js +1 -0
- package/src/agent-tools/interface-definitions.js +2 -2
- package/src/agent-tools/project-agent-definitions.js +13 -0
- package/src/agent-tools/project-agent-memory-definitions.js +13 -5
- package/src/agents/claude-code/host-notification.js +55 -0
- package/src/agents/claude-code/lifecycle-hook.js +31 -22
- package/src/agents/claude-code/session-anchors.js +128 -0
- package/src/agents/codex/conversation-transcript.js +2 -0
- package/src/agents/codex/lifecycle-hook.js +7 -9
- package/src/agents/codex/lifecycle-hooks.js +12 -9
- package/src/agents/cursor/lifecycle-hook.js +2 -3
- package/src/agents/directory-snapshots.js +303 -0
- package/src/agents/identity-receipt.js +7 -3
- package/src/cli/mcp-command.js +1 -2
- package/src/cli.js +3 -10
- package/src/conversations/current-task-lookup.js +2 -1
- package/src/conversations/guidance-file-store.js +63 -0
- package/src/conversations/hook-task-context.js +61 -0
- package/src/conversations/preference-file.js +50 -0
- package/src/conversations/session-delivery.js +70 -0
- package/src/conversations/task-context-guidance.js +57 -0
- package/src/conversations/task-context-view.js +70 -15
- package/src/conversations/task-entry-trim.js +85 -0
- package/src/conversations/transcript-reader.js +9 -1
- package/src/graphiti/agent-collaboration.js +125 -22
- package/src/graphiti/agent-conversations.js +7 -5
- package/src/graphiti/agent-knowledge-workflows.js +16 -3
- package/src/graphiti/collaboration-preference-projection.js +1 -0
- package/src/graphiti/collaboration-preference-workflow.js +15 -4
- package/src/graphiti/federated-application.js +14 -6
- package/src/graphiti/knowledge-item-view.js +61 -0
- package/src/graphiti/preference-task-scope.js +26 -0
- package/src/graphiti/project-agent-control-plane.js +21 -3
- package/src/graphiti/project-agent-mapping.js +2 -0
- package/src/graphiti/project-agent-memory.js +127 -7
- package/src/graphiti/project-agent-mention.js +2 -2
- package/src/graphiti/project-agent-task-entry.js +55 -19
- package/src/graphiti/project-agent-workflows.js +1 -0
- package/src/graphiti/provider-client.js +22 -7
- package/src/graphiti/setup.js +2 -2
- package/src/graphiti/task-entry-preferences.js +21 -2
- package/src/graphiti/task-entry-project-scope.js +10 -1
- package/src/graphiti/task-knowledge-recall.js +15 -2
- package/src/http/api-router.js +6 -0
- package/src/http/graph-api-router.js +11 -0
- package/src/http/static-handler.js +1 -1
- package/src/http/task-events-api-router.js +34 -0
- package/src/judgment/routing.js +1 -1
- package/src/mcp/agent-tool-catalog.js +14 -0
- package/src/mcp/create-mcp-server.js +43 -8
- package/src/mcp/knowledge-result-view.js +37 -0
- package/src/mcp/runtime.js +2 -1
- package/src/mcp/session-id.js +2 -0
- package/src/mcp/tool-annotations.js +1 -0
- package/src/mcp/tool-profiles.js +1 -0
- package/src/mcp/tool-result.js +4 -0
- package/src/server.js +4 -1
- package/src/setup/agent-installation-status.js +3 -0
- package/src/setup/claude-code-config.js +5 -1
- package/src/setup/client-connection.js +1 -1
- package/src/setup/installed-tool-profile.js +11 -0
- package/src/setup/paths.js +1 -0
- package/src/system/client-connection-status.js +163 -0
- package/src/system/main-module.js +16 -0
- package/src/system/system-service.js +19 -0
- package/dist/web/assets/AgentConversations-BnFsfRj3.js +0 -2
- package/dist/web/assets/AgentConversations-XqNEBY_2.css +0 -1
- package/dist/web/assets/AgentProfilePage-DjgqtOdz.js +0 -1
- package/dist/web/assets/KnowledgePage-C-gFr7ln.js +0 -1
- package/dist/web/assets/PersonalProjectsPage-Cgd_bjeK.js +0 -1
- package/dist/web/assets/ProjectAgentsPage-BOGkN5ZN.js +0 -12
- package/dist/web/assets/SettingsPage-BARo0Ds0.css +0 -1
- package/dist/web/assets/SettingsPage-Dfi7kl9c.js +0 -1
- package/dist/web/assets/project-team-api-Dfm8C4B8.js +0 -1
- package/dist/web/assets/task-evidence-DTiPCs7r.js +0 -1
|
@@ -10,12 +10,18 @@ Neo4j Provider 的独立日志中;工作记忆继续保存经过整理的决
|
|
|
10
10
|
| 压缩阈值 | 64 KB | 未折叠的消息原文超过阈值时,较早的消息折叠进摘要;可配 8–1024 |
|
|
11
11
|
| 自动恢复预算 | 4000 UTF-8 字节 | 保守内容上限,不是计费 token;可配 512–16000 |
|
|
12
12
|
| 自动恢复 | 摘要 + 最近消息 | 新 chat 恢复同一 Agent、同一项目的最近上下文 |
|
|
13
|
-
| 摘要内容 | 最近 12 个任务结果 +
|
|
13
|
+
| 摘要内容 | 最近 12 个任务结果 + 规则摘录 | 用户请求首行,以及用户与 Agent 消息中带决定、更正、未完成或方法标记的原句,附来源角色与序号;最多 6000 字节,省略数量可见 |
|
|
14
14
|
| 原始记录保存 | gzip 压缩 | 压缩不删除原文,也不调用模型生成摘要 |
|
|
15
15
|
|
|
16
16
|
压缩只看对话量,不看时间:闲置多久都不会丢失上下文。每个任务结束时的工作结果写入摘要;
|
|
17
17
|
工具输出不自动注入恢复上下文。折叠后的原文仍在数据库,需要细节时用
|
|
18
18
|
`read_agent_conversation` 分页补读,不生成虚构摘要。当前任务提示及宿主可见消息各有来源记录。
|
|
19
|
+
摘录按固定关键词规则抽取,不理解语义:未带标记的决定会漏掉,带关键词的普通句子可能被误分类;
|
|
20
|
+
它不是经核实的工作记忆,原文始终为准。细则见[项目 Agent 记忆](project-agent-memory.md)。
|
|
21
|
+
自动恢复和 `resume_agent_conversation` 都把最近一次任务结果单独放在 `context.latest_checkpoint`
|
|
22
|
+
(状态 + 结果摘要,从开头保留,超长时标 `truncated`),`context.summary` 标为 `summary_role: historical`,
|
|
23
|
+
只含更早的结果和摘录:旧摘录里的 TODO 不代表当前未完成工作,未完成或中断的检查点也不会因此显示为完成。
|
|
24
|
+
两者合计仍在恢复预算内(按 JSON 编码字节计,含转义)。交接只携带已保存的文本和引用,不转移本地文件、进程或原生工具状态。
|
|
19
25
|
敏感凭据命中检测时整条内容替换为省略标记,因此“原始记录”是已过滤的可见记录。
|
|
20
26
|
|
|
21
27
|
入口中的偏好保留完整指令,去掉重复证据包装。长期记忆只返回预览;更新前必须
|
|
@@ -49,6 +55,23 @@ Neo4j Provider 的独立日志中;工作记忆继续保存经过整理的决
|
|
|
49
55
|
保留完整指令、画像分类和写作品味域。协作职责同时带上 Agent 自己的判断、品味、个性和期望。
|
|
50
56
|
工作摘要、AR 配置的性格以及自动推断都不能伪装成人工确认的用户偏好。
|
|
51
57
|
|
|
58
|
+
## 可选执行框架
|
|
59
|
+
|
|
60
|
+
Agent 有明确计划时,通过 `record_project_agent_task_activity.executionPlan` 上报当前
|
|
61
|
+
步骤快照:`steps` 中每步包含稳定的 `id`、`title`、`status`,可补充 `detail`。
|
|
62
|
+
状态支持 `pending`、`in_progress`、`awaiting_review`、`completed`、`blocked`、`skipped`。
|
|
63
|
+
后续沿用步骤 ID 并上报完整计划;省略字段表示本次没有更新,`steps: []` 明确撤回计划。
|
|
64
|
+
没有计划时不强制生成,也不从模型内部思考推测步骤或百分比。
|
|
65
|
+
|
|
66
|
+
Agent 主页的当前工作卡及任务执行详情展示最新计划、步骤状态和上报时间。Provider
|
|
67
|
+
成功保存任务活动后,通过 SSE 通知页面读取变化任务;同一空间共用一条连接,不轮询。
|
|
68
|
+
首次连接及重连同步最新记录,任务结束、详情收起或页面隐藏时取消订阅。断线或读取
|
|
69
|
+
失败保留已显示内容并提示更新暂不可用。通知只携带任务 ID,沿用空间读取授权;任务
|
|
70
|
+
内容仍由既有接口读取。当前个人 Provider 以单进程运行,通知暂存于该进程,重启后
|
|
71
|
+
客户端重新同步数据库。保活心跳不读取任务库。调整保存在原有任务事件中,沿用授权、
|
|
72
|
+
版本和幂等检查。
|
|
73
|
+
步骤完成是 Agent 上报的进度,不会自动完成任务,也不替代测试或用户验收。
|
|
74
|
+
|
|
52
75
|
## 采集与失败恢复
|
|
53
76
|
|
|
54
77
|
Codex 和 Claude Code 的 `UserPromptSubmit`、`Stop` 命令适配器读取宿主提供的当前
|
|
@@ -88,6 +111,35 @@ JSONL transcript。只支持各适配器明确识别的可见文本、工具调
|
|
|
88
111
|
|
|
89
112
|
## 小组长与借调
|
|
90
113
|
|
|
114
|
+
组长负责拆分、统筹、集成和交付。`plan_agent_collaboration` 返回 `divisionPlan`,
|
|
115
|
+
复用现有任职与派工,不额外调用模型生成分工:
|
|
116
|
+
|
|
117
|
+
| 任务 | 默认工作包 | 开始条件 |
|
|
118
|
+
| --- | --- | --- |
|
|
119
|
+
| 小改动、只运行已有命令 | 延续一个工作包 | 当前上下文继续 |
|
|
120
|
+
| 通常的实质性改动 | 主线实施 + 另一位 FLA 独立验收 | 验收等待稳定产物 |
|
|
121
|
+
| 复杂且有明确独立边界 | 主线 + 一个独立工作包 + 独立验收 | 两个执行包可独立推进,验收等待两者产物 |
|
|
122
|
+
|
|
123
|
+
`taskSize` 默认 `standard`;`small` 只用于低影响小修改,安全敏感或明显改变行为的
|
|
124
|
+
工作仍需独立验收。`complex` 本身不制造第三个工作包,还需提供 `independentWork`
|
|
125
|
+
的任务类型、无写入冲突或未解决依赖的边界及最多 3 条必要查询。强耦合实现保持在一起,
|
|
126
|
+
独立验收可以顺序执行。验收请求不再为自己递归拆出验收者。
|
|
127
|
+
|
|
128
|
+
调用者传入全部已知 `implementationAgentIds`;验收候选排除这些作者、当前主线负责人
|
|
129
|
+
及本轮分配的执行者。实际作者变化后重新规划。先复用本项目合格人员,再走借调和已有
|
|
130
|
+
AR 招募流程;不会为填满人数而自动创建身份。每个委派工作包返回派工参数、等待条件
|
|
131
|
+
和理由,缺位仍遵守招聘确认政策,借调批准前不执行。
|
|
132
|
+
|
|
133
|
+
主线复用已有上下文,委派只携带目标、范围、授权产物引用、验收条件与必要查询,
|
|
134
|
+
不复制整段会话。派工从每个查询最多 3 个结果起步,缺什么再补什么。模型继续遵循
|
|
135
|
+
用户策略、执行器锁和真实可用能力,由 Tonborg 在满足质量要求后考虑成本,不写死
|
|
136
|
+
某个厂商或型号。组长将工作包与先后关系上报到已有执行框架,实际 worker、结果和
|
|
137
|
+
Token 用量进入任务活动;未报告的用量保持未知,不用估计值冒充节省。
|
|
138
|
+
|
|
139
|
+
新默认组长采用统筹职责;已有 Agent 的自定义职责不覆盖。普通任务入口与 MCP 指引
|
|
140
|
+
都会送达同一分工规则,因此既有组长也适用。FULI 控制面提供计划和记录,由宿主实际
|
|
141
|
+
派工,只有真实 worker 记录才表示已经分工执行。
|
|
142
|
+
|
|
91
143
|
`plan_agent_collaboration` 用职责、能力、任务类型和工作负载推荐成员,优先本项目。
|
|
92
144
|
目标组长通过 `request_agent_loan` 发起借调,来源组长在自己的项目上下文中通过
|
|
93
145
|
`decide_agent_loan` 确认。数据库创建目标项目中的独立任职;任务完成、失败或取消
|
|
@@ -96,6 +148,22 @@ JSONL transcript。只支持各适配器明确识别的可见文本、工具调
|
|
|
96
148
|
借调成功后仍需 `coordinate_project_agent_task` 明确参与者,再由已授权客户端实际
|
|
97
149
|
启动 worker。控制面只提供计划、权限和记录,模型推荐、借调批准都不代表已执行。
|
|
98
150
|
|
|
151
|
+
项目组长始终是对话负责人,但这不代表适合执行。路由先在固定小组内按能力、任务类型、
|
|
152
|
+
负载、同类任务历史和本项目执行连续性选执行者:组长合适就自己做,否则由最合适的成员执行(`project_team_member`),
|
|
153
|
+
组长写入任务的协调人并接收汇报。小组无人合格时(`project_team_gap`)走现有 AR 招聘,
|
|
154
|
+
遵循“招聘前确认”;合格成员只是当前客户端不可用时任务阻塞,不招聘替代者。
|
|
155
|
+
任务入口仅返回 `execution_reason`/`execution_agent_id` 与操作指引,不创建任务、不招聘、不启动 worker。
|
|
156
|
+
合格成员只是当前客户端不可用(`agent_unavailable`)时,任务仍由配置的组长协调并接收汇报,
|
|
157
|
+
并行计划也不会从小组外补人;显式指定的协调人优先。
|
|
158
|
+
|
|
159
|
+
`plan_agent_collaboration` 按与 Provider 相同的有效能力匹配专家:任务分配上的能力非空时取代档案能力,
|
|
160
|
+
而不是合并。结果排除当前客户端不可用、分配未激活、来源项目组长本人(组长不可借调)的 Agent,
|
|
161
|
+
只返回公开的角色与分配信息。每个候选附 `nextSteps`:`message_agent` 只是圆桌咨询(只读、advisory),
|
|
162
|
+
跨项目执行需目标组长 `request_agent_loan`、来源组长 `decide_agent_loan` 批准,再用
|
|
163
|
+
`coordinate_project_agent_task` 由授权运行时执行,结果回报组长。没有合格专家时返回 `recruitment`,
|
|
164
|
+
指向现有 `coordinate_project_agent_task`(`staffingIntent: new_durable`)与同一 `idempotencyKey`,
|
|
165
|
+
遵循“招聘前确认”;发现、消息与招聘都不算已执行的工作。
|
|
166
|
+
|
|
99
167
|
## 模型和验证
|
|
100
168
|
|
|
101
169
|
自适应策略按简单、普通、复杂任务映射到 fast、balanced、deep。显式用户策略、
|
|
@@ -78,6 +78,17 @@ currently project-assigned item globally accessible. Explicit project references
|
|
|
78
78
|
inheritance and genuine personal-global knowledge keep their existing scope rules; records
|
|
79
79
|
without scope metadata fail closed.
|
|
80
80
|
|
|
81
|
+
Agent working-memory export (`export_project_agent_memory`, catalog read workflow
|
|
82
|
+
`agent-memory`) returns generated Markdown for one scope from a fresh database read. It reuses
|
|
83
|
+
the `get_project_agent_memory` authority, including host-injected source clients and the strict
|
|
84
|
+
scope validator, takes no output path, writes no export file (existing authorized local
|
|
85
|
+
directory snapshots may refresh), and is not offered to remote connectors. Its MCP result
|
|
86
|
+
budget (512 KiB) holds the largest valid identity plus one memory layer; a result that would
|
|
87
|
+
still be truncated fails explicitly instead of returning a partial file. Memory reads,
|
|
88
|
+
checkpoints and task entry report directory status in the public `directorySnapshot` field;
|
|
89
|
+
the generic MCP omission of internal `snapshot` keys is unchanged.
|
|
90
|
+
The console route `GET /api/project-agents/:agentId/memory/export` uses owner inspection authority.
|
|
91
|
+
|
|
81
92
|
## Human-only boundaries
|
|
82
93
|
|
|
83
94
|
The following operations intentionally remain local-user-only:
|
|
@@ -25,18 +25,40 @@ directory, or `--runtime-config FILE` for an explicit runtime configuration.
|
|
|
25
25
|
`args` for clients with their own configuration editor. Paths with spaces remain
|
|
26
26
|
individual JSON arguments; do not paste them as an unquoted shell command.
|
|
27
27
|
|
|
28
|
+
### Client status
|
|
29
|
+
|
|
30
|
+
**Settings → Client connection** also lists the supported local clients that
|
|
31
|
+
FULI found and whether their FULI integration configuration is still in place:
|
|
32
|
+
connected, changed, skills left without MCP, removed after an earlier
|
|
33
|
+
connection, not connected, or unknown when a client's config, settings, hooks or
|
|
34
|
+
their folder cannot be read or parsed. Only a missing file counts as removed. The view re-reads configuration on open, on manual refresh and every 15
|
|
35
|
+
seconds while the card is open and the page is visible. It only describes
|
|
36
|
+
configuration; it does not show whether a client is running or reachable, and it
|
|
37
|
+
never reinstalls anything. FULI keeps only the latest status and the last five
|
|
38
|
+
changes per client in `client-connection-observations.json` in its data
|
|
39
|
+
directory; if that file is damaged, the history starts over. If it cannot be
|
|
40
|
+
saved, the console keeps the history until FULI restarts and says it is unsaved.
|
|
41
|
+
|
|
28
42
|
## Claude Desktop and other chat apps
|
|
29
43
|
|
|
30
44
|
`fuli setup` detects Claude Desktop (Windows and macOS) and adds a `fuli` entry to
|
|
31
45
|
`claude_desktop_config.json`, keeping its other settings and servers and backing up the
|
|
32
46
|
file first. Quit and reopen Claude Desktop afterwards; it only reads the file at launch.
|
|
33
47
|
|
|
34
|
-
Chat apps load
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
48
|
+
Chat apps load exposed tool schemas into each conversation, so Claude Desktop and
|
|
49
|
+
newly configured Claude Code connections get the **chat profile**
|
|
50
|
+
(`--tool-profile chat`): 21 initial tools for preferences, project knowledge,
|
|
51
|
+
recording, completion checkpoints, conversation recovery, the Roundtable and tool
|
|
52
|
+
discovery. `list_agent_interfaces` defaults to a schema-free directory. Request one
|
|
53
|
+
exact `toolName` to read its complete schema and expose that original named tool
|
|
54
|
+
through `tools/list_changed`, then call the original name under the client's normal
|
|
55
|
+
permissions. This also provides management, routing, publishing and recruitment on
|
|
56
|
+
demand; discovery grants no permission. If the client does not refresh its tool list,
|
|
57
|
+
reconnect using `fuli connect --profile full`. Generated full configuration keeps the
|
|
58
|
+
explicit `--tool-profile full` choice so managed upgrades preserve it. Legacy Claude
|
|
59
|
+
Code setup entries without an explicit profile migrate to chat; `alwaysLoad` remains
|
|
60
|
+
enabled for its initial tools. Claude Desktop conversations are attributed to `claude`.
|
|
61
|
+
For another chat-style client, print the same entry with
|
|
40
62
|
`fuli connect --profile chat --source-application claude` (or the matching source).
|
|
41
63
|
|
|
42
64
|
Claude Desktop has no lifecycle hooks, so task entry and the closing attribution line
|
|
@@ -52,6 +74,20 @@ Clients that only ask for a command can use `fuli mcp` if `fuli` is on their PAT
|
|
|
52
74
|
Configuration export is read-only: it neither modifies client settings nor
|
|
53
75
|
claims the client has connected successfully.
|
|
54
76
|
|
|
77
|
+
When a turn receives no valid Hook context, `get_collaboration_preferences` is
|
|
78
|
+
the task-entry fallback. Supply the actual native `sessionId` and current
|
|
79
|
+
`taskPrompt`; it returns a fresh `taskContextToken` for the final checkpoint and
|
|
80
|
+
recovers the selected role's latest saved result. Never guess a native ID or use
|
|
81
|
+
an MCP process ID. Without the native ID or prompt, preferences remain available
|
|
82
|
+
but `task_lifecycle.status=not_started` reports that checkpoint ownership was not
|
|
83
|
+
established. An unfinished Provider task is not evidence of Hook delivery.
|
|
84
|
+
|
|
85
|
+
Claude Code's blocked or unavailable entry output supplies the host's native ID
|
|
86
|
+
when available and directs the Agent to this fallback. Transcript ownership
|
|
87
|
+
guards stay intact: a fallback task-summary checkpoint does not prove that the
|
|
88
|
+
native transcript tail was captured or that the Hook delivered task context.
|
|
89
|
+
Hook execution, successful FULI entry and model receipt require separate evidence.
|
|
90
|
+
|
|
55
91
|
## What a compatible client needs
|
|
56
92
|
|
|
57
93
|
- MCP **tool discovery and invocation**, using stdio or the remote transport
|
|
@@ -49,7 +49,12 @@ Provider 读取已授权的员工和项目记录。切换客户端不会复制
|
|
|
49
49
|
1. 根据当前目录解析项目:先匹配精确 ID,再允许唯一的大小写等价 ID。工作树以原仓库
|
|
50
50
|
为准,不以任意工作树文件夹名覆盖原项目;多个候选或相近但不同的名字不猜测。
|
|
51
51
|
2. 选择具有有效任职、允许当前客户端的员工,并恢复职责、项目简介、近期任务、工作记录和记忆。
|
|
52
|
-
|
|
52
|
+
项目任务同时恢复同一员工的全局记忆:`memory` 是当前项目层,`global_memory` 是全局层
|
|
53
|
+
(项目为 `null`),两层各自校验空间、项目、员工和 scope,各自保留 revision;
|
|
54
|
+
任一层读取失败只把该层列入 `unavailable_components`,不隐藏另一层。全局任务只读取一次全局层;
|
|
55
|
+
`task_only` 员工两层都不读取。任务入口预览对每层分别限长并标注 `notesTruncated`。
|
|
56
|
+
3. 开始持久化的任务上下文,返回 `taskContextToken` 和当前记忆 revision(只绑定任务所在层)。
|
|
57
|
+
任务检查点只写该层;全局层须用 `personalProjectId: null` 单独、显式写入,不自动复制项目内容。
|
|
53
58
|
4. 如果任务入口尚无员工,协调时传入当前 token。新招的小组长可以接管同一任务;
|
|
54
59
|
即使执行器阻塞,也能保留阻塞原因与下一步。
|
|
55
60
|
|
|
@@ -136,3 +141,113 @@ MCP 上下文有长度上限。被截断时必须显式补读完整记忆后再
|
|
|
136
141
|
`begin_task_context` 和 `get_collaboration_preferences` 始终返回 `agent_receipt`,其中 `required: true` 和 `markdown` 构成统一的回复契约。宿主 Agent 必须在每次最终回复中原样显示一次这行身份与个人页面链接;问候、失败、没有可沉淀知识或关闭自动沉淀都不例外。无可用授权身份时返回 `FULI Agent:未选定`,不借用上次任务的身份。旧版本入口没有回执时应明确说明身份不可用。
|
|
137
142
|
|
|
138
143
|
MCP 与 Hook 交付会为完整回执保留空间,其他上下文超长时也不截断回执;预算不足以容纳回执时返回明确错误。该要求通过共享 MCP 指令、入口说明和操作 Skill 交付;外部宿主最终生成的文字仍由该宿主负责遵守,Fuli 不伪称能拦截所有客户端输出。身份回执不证明模型工作者已经启动,也不证明知识或会话已经保存。
|
|
144
|
+
|
|
145
|
+
## 全局 FLA 上下文
|
|
146
|
+
|
|
147
|
+
FLA 身份、模型策略和执行器偏好属于个人空间,可以没有项目任职。项目仍需负责人。
|
|
148
|
+
未匹配项目的会话显式选择 FLA(`projectAgentId` 或开头的 `@{agentId}`)时,直接恢复
|
|
149
|
+
该 FLA 的全局对话与工作记忆,不创建会话项目或临时任职;同一宿主会话后续沿用全局身份。
|
|
150
|
+
已明确解析出的项目仍走项目任职校验,无法访问时不会回退到全局。
|
|
151
|
+
|
|
152
|
+
对话与记忆工具省略 `personalProjectId` 或传 `null` 表示精确的全局范围,绝不是所有项目。
|
|
153
|
+
全局范围与每个项目分别保存对话、恢复策略、工作摘要及记忆 revision;后续任职不会迁移、
|
|
154
|
+
合并或继承这些数据。全局读取不会自动加载任何项目任务或知识。已有项目记录保持兼容。
|
|
155
|
+
全局写入仍检查个人空间权限、身份状态、客户端允许列表、任务 token 和记忆版本。
|
|
156
|
+
|
|
157
|
+
主页和高级管理页的“对话与记忆”都提供“全局”选项;项目选项只来自有效任职,
|
|
158
|
+
管理范围本身不会授予项目私有对话权限。无项目 FLA 的全局会话也支持跨客户端接续。
|
|
159
|
+
|
|
160
|
+
## 检查工作记忆与压缩摘录
|
|
161
|
+
|
|
162
|
+
“对话与记忆”展开后按所选范围(全局或某个项目)读取已有的
|
|
163
|
+
`GET /api/project-agents/:id/memory`,显示当前 revision、来源客户端、时间、摘要、决定、
|
|
164
|
+
未完成事项和下一步;没有检查点时明确显示为空,读取失败可重试,切换范围会丢弃旧响应。
|
|
165
|
+
工作记忆是任务检查点保存的工作上下文,不等于人工确认的知识。这里只读,不提供编辑入口。
|
|
166
|
+
|
|
167
|
+
对话超过压缩阈值后,较早消息按固定规则抽取原句,不调用模型,也不声称理解语义:
|
|
168
|
+
|
|
169
|
+
- 用户消息的首行作为请求标题;用户与 Agent 消息中带有更正、决定、未完成或方法标记
|
|
170
|
+
(如 `Decision:`、`TODO`、`instead`、`决定`、`待办`、`方法`)的行按此分类保留。代码块与工具
|
|
171
|
+
输出不抽取。
|
|
172
|
+
- 每条摘录记录来源角色与事件序号;去除首尾空白后完全相同的文本(区分大小写与内部空白)只保留一条并追加来源(保留首个与最近三个,
|
|
173
|
+
同一来源不重复),因此更新的说法与较早的说法并列出现,不会覆盖旧来源。被截断的摘录只是
|
|
174
|
+
前缀,不参与去重,避免把前缀相同的不同决定合并。
|
|
175
|
+
- 单条摘录最多 240 UTF-8 字节,整体最多 6000 字节、80 条;超出时先移除最旧的请求标题,
|
|
176
|
+
再移除最旧的方法,最后才移除决定、更正与未完成事项。未抽取的行(含代码块中的代码行)
|
|
177
|
+
和被移除的行计入 `omitted`。
|
|
178
|
+
- 恢复上下文时整段摘要(任务结果、旧请求、摘录)最多占预算的一半:任务结果单条按 240 字节
|
|
179
|
+
截断;超出时依次移除旧请求、请求标题与方法、最旧的任务结果,最后才移除决定、更正和未完成
|
|
180
|
+
事项。标题注明“不是经核实的工作记忆、原文仍保留”,并给出省略与截断数量。旧版纯文本摘要
|
|
181
|
+
按序列化后的 JSON 字节(含转义)裁去较早部分,保证整个恢复结果不超过预算。
|
|
182
|
+
- 旧摘要中的 `notes`(首行请求)继续可读,新摘录追加在其后,不改写历史数据。
|
|
183
|
+
|
|
184
|
+
阅读原文时首页返回同一份摘录,界面区分“自动摘录,未经核实”与工作记忆,显示来源序号、
|
|
185
|
+
截断与省略说明;原文消息带同样的序号,可在现有分页历史中找回。规则无法识别未带标记的
|
|
186
|
+
决定,也可能把带关键词的普通句子误分类;原文始终是依据。设计参考 Mem0 的抽取、合并去重和
|
|
187
|
+
保留来源,以及 LongMemEval 关注的时间更新与弃权维度,仅作为合成测试的验收方向,并未运行
|
|
188
|
+
这些公开基准。
|
|
189
|
+
|
|
190
|
+
## 员工目录快照(第一阶段)
|
|
191
|
+
|
|
192
|
+
每个长期员工(`memory_scope: reviewed_agent`)在 FULI 数据目录下有一个只为查看和交接生成的
|
|
193
|
+
目录:`agents/<Provider>/<个人空间>/<员工>/`。三级目录名都是稳定 ID 的哈希,不受显示名、改名
|
|
194
|
+
或不安全 ID 影响;不同 Provider、个人空间和同名员工互不重叠。只有通过 runtime config 打开的
|
|
195
|
+
应用会生成快照,没有全局回退位置。
|
|
196
|
+
|
|
197
|
+
- `AGENTS.md`:允许列出的身份与职责(名称、ID、工号、类型、状态、职责、能力、初始偏好、
|
|
198
|
+
性格与期望),不写分配列表、凭据、运行路径、会话或任务 token。
|
|
199
|
+
- `MEMORY.md`:全局工作记忆;`projects/<项目哈希>/MEMORY.md`:各项目工作记忆。每个文件注明
|
|
200
|
+
范围、数据库 revision、生成时间,以及“由数据库生成、不是依据、修改不会导入”。没有记忆时
|
|
201
|
+
如实写明尚无记录。疑似含凭据的旧内容不写入文件。
|
|
202
|
+
|
|
203
|
+
数据库始终是依据。快照在已授权的完整读取之后生成:`get_project_agent_memory` 写入所读范围;
|
|
204
|
+
控制台的“对话与记忆”(`inspectProjectAgentMemory`)以本机所有者身份读取,同样补齐或重建所读
|
|
205
|
+
范围,不套用员工的客户端白名单;任务入口写入本次已读取的全局层和项目层(在热路径预览裁剪之前),
|
|
206
|
+
不额外读取其他项目。写入前用任务入口相同的范围校验检查整个视图和当前记录:员工、项目、空间或
|
|
207
|
+
revision 不符时只刷新身份文件,不把内容写到所请求的范围下。
|
|
208
|
+
`checkpoint_project_agent_memory` 在数据库提交成功后用返回的记录刷新;任务检查点不返回记忆
|
|
209
|
+
内容,只把对应文件标记为 `stale`,下次授权读取时重新生成;较晚到达的旧标记不会撤销尚未
|
|
210
|
+
读取的新 revision。快照不属于数据库事务。
|
|
211
|
+
|
|
212
|
+
旧员工不需要迁移:首次授权读取时补齐目录,不写数据库、不改 revision、不改名或招聘。重复读取
|
|
213
|
+
不改写未变化的文件;删除或手动修改的生成文件会按数据库重建;目录里其他文件保持原样。旧
|
|
214
|
+
revision 不会覆盖已写入的新 revision(即使新文件已被删除)。
|
|
215
|
+
|
|
216
|
+
文件 0600、目录 0700,原子替换;任何受管理路径是符号链接或非常规文件时拒绝写入。并发写入用
|
|
217
|
+
目录内的短锁:已被占用时立即返回 `busy`,遗留超过 30 秒的锁会被清理。文件系统失败不会影响
|
|
218
|
+
已成功的数据库读写。工作记忆读取、记忆检查点、任务检查点(`agent_memory` 内)和任务入口
|
|
219
|
+
(`project_agent_context` 内)的结果,包括通过 MCP 返回时,都用公开字段 `directorySnapshot`
|
|
220
|
+
报告目录状态;MCP 对内部 `snapshot` 字段的通用屏蔽保持不变。`directorySnapshot` 只包含 `status`(`current`、`stale`、`newer_present`、
|
|
221
|
+
`busy`、`unsafe_path`、`unavailable`)、相对 `path` 和每个文件的范围、revision 与状态,不含
|
|
222
|
+
绝对路径或原始错误。
|
|
223
|
+
|
|
224
|
+
## 导出工作记忆(第二阶段)
|
|
225
|
+
|
|
226
|
+
“对话与记忆”的工作记忆标题旁有一个导出按钮,只下载当前所选范围(全局或一个已分配项目)
|
|
227
|
+
的 Markdown。Agent 使用只读工具 `export_project_agent_memory`,所有者控制台使用
|
|
228
|
+
`GET /api/project-agents/:agentId/memory/export`,二者共用同一个导出边界:
|
|
229
|
+
|
|
230
|
+
- 每次导出都重新从数据库读取,从不读取目录快照或用户修改过的文件;快照不存在也不代表
|
|
231
|
+
任何结论。读取结果先经过与任务入口相同的严格范围校验(外层、当前记录、每条历史的员工、
|
|
232
|
+
项目、空间、范围和 revision),任一不符即拒绝,不生成 Markdown。
|
|
233
|
+
- 内容只有允许列出的身份字段和所选范围的一层记忆,注明范围、数据库 revision、记录时间、
|
|
234
|
+
生成时间,以及“由数据库生成、不是依据、修改不会导入”。不含原始对话、token、任务或会话 ID、
|
|
235
|
+
完整分配列表或运行路径;疑似凭据的内容被隐去。结果只返回建议文件名和 Markdown 文本,
|
|
236
|
+
不接受输出路径参数,也不写导出文件;记忆内容按纯文本返回。MCP 为该工具单独预留 512 KiB,
|
|
237
|
+
足以容纳最大的合法身份资料(含 UTF-8 与 JSON 转义)和一层记忆;万一仍超出,会明确报错,
|
|
238
|
+
而不是返回被截断的半个文件。
|
|
239
|
+
- Agent 工具沿用 `get_project_agent_memory` 的权限:活动的长期员工、可信宿主注入的来源客户端
|
|
240
|
+
必须在白名单内,调用方不能自报来源;项目必须由 Provider 校验分配,不会隐式读取其他项目。
|
|
241
|
+
该工具不在远程连接器的允许列表中。控制台按现有所有者检查权限导出,只要求长期员工。
|
|
242
|
+
- 导出时会顺带刷新可选的目录快照;快照目录不可写不影响导出,结果也不声称已在本机保存。
|
|
243
|
+
- 界面在请求期间禁用按钮并显示进度;切换范围、员工或收起面板后才返回的结果会被丢弃,
|
|
244
|
+
不会下载错误范围;失败时说明并可重试。
|
|
245
|
+
|
|
246
|
+
### 升级说明
|
|
247
|
+
|
|
248
|
+
从 0.12.4 升级不需要迁移,现有数据库保持不变:员工 ID、工号、资料、各项目工作记忆的历史与
|
|
249
|
+
revision 以及访问 token 都沿用。目录在首次授权读取时才补齐。0.12.4 没有全局工作记忆,升级后
|
|
250
|
+
全局层为空,直到第一次检查点。删除生成目录或文件后,下次读取会按数据库重新生成;手动修改的
|
|
251
|
+
Markdown 永远不会导入。目录权限出错时不影响读写和导出,修复权限后下次读取即可恢复。以上
|
|
252
|
+
升级结果目前只在一条路径上验证过(已发布的 0.12.4 → 新 Provider,同一隔离 Neo4j),并非
|
|
253
|
+
覆盖所有旧版本。
|
|
@@ -22,7 +22,7 @@ class ConversationPolicy(StrictModel):
|
|
|
22
22
|
|
|
23
23
|
class ConversationScope(StrictModel):
|
|
24
24
|
personal_space_id: str = Field(min_length=1, max_length=128)
|
|
25
|
-
personal_project_id: str = Field(min_length=1, max_length=128)
|
|
25
|
+
personal_project_id: str | None = Field(default=None, min_length=1, max_length=128)
|
|
26
26
|
agent_id: str = Field(min_length=1, max_length=128)
|
|
27
27
|
source_application: SourceApplication | None = None
|
|
28
28
|
|
|
@@ -879,6 +879,9 @@ def create_app(settings: Settings | None = None) -> FastAPI:
|
|
|
879
879
|
Query(min_length=1, max_length=128),
|
|
880
880
|
] = None,
|
|
881
881
|
limit: Annotated[int, Query(ge=1, le=200)] = 100,
|
|
882
|
+
session_id: Annotated[str | None, Query(min_length=1, max_length=256)] = None,
|
|
883
|
+
work_kind: Annotated[str | None, Query(min_length=1, max_length=128)] = None,
|
|
884
|
+
workflow: Annotated[str | None, Query(min_length=1, max_length=128)] = None,
|
|
882
885
|
) -> CollaborationContextResult:
|
|
883
886
|
return await store.collaboration_context(
|
|
884
887
|
actor,
|
|
@@ -886,6 +889,9 @@ def create_app(settings: Settings | None = None) -> FastAPI:
|
|
|
886
889
|
personal_project_id,
|
|
887
890
|
limit,
|
|
888
891
|
project_agent_id,
|
|
892
|
+
session_id,
|
|
893
|
+
work_kind,
|
|
894
|
+
workflow,
|
|
889
895
|
)
|
|
890
896
|
|
|
891
897
|
@application.get('/v1/spaces/{space_id}/graph', response_model=GraphResult)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from collections import defaultdict
|
|
2
2
|
from math import isfinite
|
|
3
|
+
import re
|
|
3
4
|
|
|
4
5
|
from fastapi import HTTPException
|
|
5
6
|
|
|
@@ -12,7 +13,7 @@ from .models import (
|
|
|
12
13
|
)
|
|
13
14
|
from .personal_project_access import authorize_personal_project
|
|
14
15
|
from .project_agent_access import authorize_project_agent
|
|
15
|
-
from .provider_values import json_object, native_datetime as _native_datetime
|
|
16
|
+
from .provider_values import json_object, native_datetime as _native_datetime, preference_qualifiers
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
async def read_collaboration_context(
|
|
@@ -22,6 +23,9 @@ async def read_collaboration_context(
|
|
|
22
23
|
personal_project_id: str | None = None,
|
|
23
24
|
limit: int = 100,
|
|
24
25
|
project_agent_id: str | None = None,
|
|
26
|
+
session_id: str | None = None,
|
|
27
|
+
work_kind: str | None = None,
|
|
28
|
+
workflow: str | None = None,
|
|
25
29
|
) -> CollaborationContextResult:
|
|
26
30
|
space = await store.authorize(actor, space_id, 'reader')
|
|
27
31
|
if space['kind'] != 'personal':
|
|
@@ -37,18 +41,13 @@ async def read_collaboration_context(
|
|
|
37
41
|
personal_project_id,
|
|
38
42
|
)
|
|
39
43
|
if project_agent_id:
|
|
40
|
-
if not personal_project_id:
|
|
41
|
-
raise HTTPException(
|
|
42
|
-
status_code=422,
|
|
43
|
-
detail='project Agent preferences require a personal project',
|
|
44
|
-
)
|
|
45
44
|
agent = await authorize_project_agent(
|
|
46
45
|
store,
|
|
47
46
|
actor,
|
|
48
47
|
space,
|
|
49
48
|
personal_project_id,
|
|
50
49
|
project_agent_id,
|
|
51
|
-
require_memory=True,
|
|
50
|
+
require_memory=True, allow_global=True,
|
|
52
51
|
)
|
|
53
52
|
project_agent_id = agent['agent_id']
|
|
54
53
|
project_scopes = await _project_scopes(
|
|
@@ -105,6 +104,9 @@ async def read_collaboration_context(
|
|
|
105
104
|
*edges,
|
|
106
105
|
*(item for item in nodes if item.id not in edge_endpoint_ids),
|
|
107
106
|
]
|
|
107
|
+
scoped_items = [item for item in items if _applies_to_task(item, session_id, work_kind, workflow)]
|
|
108
|
+
task_scope_excluded_count = len(items) - len(scoped_items)
|
|
109
|
+
items = scoped_items
|
|
108
110
|
items.sort(key=_sort_key)
|
|
109
111
|
if len(items) > limit:
|
|
110
112
|
truncated = True
|
|
@@ -178,6 +180,8 @@ async def read_collaboration_context(
|
|
|
178
180
|
project_preferences=project_preferences,
|
|
179
181
|
agent_preferences=agent_preferences,
|
|
180
182
|
effective_preferences=effective_preferences,
|
|
183
|
+
task_scope_excluded_count=task_scope_excluded_count,
|
|
184
|
+
task_scope_filtered=True,
|
|
181
185
|
conflicts=[*global_conflicts, *project_conflicts, *agent_conflicts],
|
|
182
186
|
overridden_global_ids=overridden_global_ids,
|
|
183
187
|
overridden_inherited_ids=overridden_inherited_ids,
|
|
@@ -187,6 +191,32 @@ async def read_collaboration_context(
|
|
|
187
191
|
)
|
|
188
192
|
|
|
189
193
|
|
|
194
|
+
def _applies_to_task(item, session_id, work_kind, workflow):
|
|
195
|
+
qualifiers = {**item.attributes, **json_object(item.attributes.get('preference_qualifiers')),
|
|
196
|
+
**item.preference_qualifiers}
|
|
197
|
+
conditions = [
|
|
198
|
+
(qualifiers.get('sessionId', qualifiers.get('session_id', qualifiers.get('sessionIds', qualifiers.get('session_ids', _legacy_session(item.instruction))))), session_id),
|
|
199
|
+
(qualifiers.get('workKind', qualifiers.get('work_kind', qualifiers.get('workKinds', qualifiers.get('work_kinds')))), work_kind),
|
|
200
|
+
(qualifiers.get('workflow', qualifiers.get('workflows')), workflow),
|
|
201
|
+
]
|
|
202
|
+
for condition, actual in conditions:
|
|
203
|
+
values = [condition] if isinstance(condition, str) and condition.strip() else (
|
|
204
|
+
condition if isinstance(condition, list) and condition and
|
|
205
|
+
all(isinstance(value, str) and value.strip() for value in condition) else None
|
|
206
|
+
)
|
|
207
|
+
if values and actual and actual not in values:
|
|
208
|
+
return False
|
|
209
|
+
return True
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def _legacy_session(instruction):
|
|
213
|
+
# Read compatibility only; provenance/ambiguous business prose never scopes.
|
|
214
|
+
match = re.match(r'^仅(?:在|适用于)\s*(?:Codex|Claude Code|Cursor)\s*会话\s+'
|
|
215
|
+
r'([a-fA-F0-9]{8}(?:-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12})'
|
|
216
|
+
r'\s*(?:中|的|后续|[,,::])', instruction or '')
|
|
217
|
+
return match[1] if match else None
|
|
218
|
+
|
|
219
|
+
|
|
190
220
|
async def _project_scopes(store, space, personal_project_id):
|
|
191
221
|
if not personal_project_id:
|
|
192
222
|
return {}
|
|
@@ -436,6 +466,7 @@ def _preference_item(
|
|
|
436
466
|
preference_project_id=project_id,
|
|
437
467
|
preference_agent_id=agent_id,
|
|
438
468
|
attributes=attributes,
|
|
469
|
+
preference_qualifiers=preference_qualifiers(attributes),
|
|
439
470
|
weight=weight,
|
|
440
471
|
reason=reason,
|
|
441
472
|
confirmation_basis=basis,
|