fuli-context 0.7.9 → 0.7.10-beta.7
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 +5 -2
- package/README.zh-CN.md +5 -3
- package/dist/web/assets/{AboutPage-Bp-Xz6yo.js → AboutPage-EtWPOSpz.js} +1 -1
- package/dist/web/assets/AboutPage-vYIBifKw.css +1 -0
- package/dist/web/assets/AgentHand-D5pOgVjJ.js +1 -0
- package/dist/web/assets/AgentHand-DvdNtJ3_.css +1 -0
- package/dist/web/assets/ConnectionsPage-DXkvMuyK.js +1 -0
- package/dist/web/assets/EmployeeRecruitDialog-B59X3rOi.css +1 -0
- package/dist/web/assets/EmployeeRecruitDialog-DONxXnd2.js +1 -0
- package/dist/web/assets/EmployeeWorkbenchPage-Cd4hd4bf.js +3 -0
- package/dist/web/assets/EmployeeWorkbenchPage-Cz7y-IFD.css +1 -0
- package/dist/web/assets/GrowthLoading-B_Hh0CRS.css +1 -0
- package/dist/web/assets/GrowthLoading-CCYPgE2f.js +1 -0
- package/dist/web/assets/KnowledgeEditDialog-BUqMfa0a.js +1 -0
- package/dist/web/assets/KnowledgeEditDialog-D9NuRHvp.css +1 -0
- package/dist/web/assets/KnowledgeInspector-DlVPDGP3.js +1 -0
- package/dist/web/assets/KnowledgeOrganizerPage-DhthZGKZ.js +1 -0
- package/dist/web/assets/KnowledgeOrganizerPage-h2iKvQpO.css +1 -0
- package/dist/web/assets/KnowledgePage-B6RDVxq-.js +1 -0
- package/dist/web/assets/KnowledgeWorkspace-mVFHQcxr.js +4 -0
- package/dist/web/assets/KnowledgeWorkspace-sb8OJ97I.css +1 -0
- package/dist/web/assets/NotFoundPage-CCeEdBy9.js +1 -0
- package/dist/web/assets/{OverviewPage-HwoSW-5z.js → OverviewPage-CnYWoZ3r.js} +1 -1
- package/dist/web/assets/PersonalProfilePage-Bz4zpTPC.js +1 -0
- package/dist/web/assets/PersonalProfilePage-yQRCBude.css +1 -0
- package/dist/web/assets/PersonalProjectsPage-2u1o6dvA.js +1 -0
- package/dist/web/assets/ProjectAgentsPage-8tci631q.js +11 -0
- package/dist/web/assets/ProjectAgentsPage-BN4k3ubu.css +1 -0
- package/dist/web/assets/ProjectScopePicker-BcPtIcv8.js +1 -0
- package/dist/web/assets/ProjectScopePicker-BvaGTMFx.css +1 -0
- package/dist/web/assets/PublicProjectsPage-BmBHfnud.js +1 -0
- package/dist/web/assets/ReviewPage-tvt2eqhw.js +1 -0
- package/dist/web/assets/{SearchableSelect-CFZNdtlY.js → SearchableSelect-BjBDBVuZ.js} +1 -1
- package/dist/web/assets/SettingsPage-Brl-SL33.css +1 -0
- package/dist/web/assets/SettingsPage-D9mNzBg-.js +1 -0
- package/dist/web/assets/SharedJefaReportPage-DXbj_JhO.css +1 -0
- package/dist/web/assets/SharedJefaReportPage-Dn_HBgaa.js +1 -0
- package/dist/web/assets/WritingTastePage-BCwimXC-.js +1 -0
- package/dist/web/assets/WritingTastePage-tb_pgude.css +1 -0
- package/dist/web/assets/bole-avatar-CVIr-bU_.webp +0 -0
- package/dist/web/assets/client-v8XKgQji.js +1 -0
- package/dist/web/assets/console-C9HS-zbd.js +1 -0
- package/dist/web/assets/conversation-launcher-settings-DMDCpf_N.js +1 -0
- package/dist/web/assets/employee-dialog-Bh5ccIbK.css +1 -0
- package/dist/web/assets/{i18n-DQH9D35M.js → i18n-BkJjUsz_.js} +3 -3
- package/dist/web/assets/{identity-JjZG6aS5.js → identity-74yGiMzO.js} +1 -1
- package/dist/web/assets/index-CwjxeHGk.css +1 -0
- package/dist/web/assets/index-Dioqmmvf.js +2 -0
- package/dist/web/assets/{model-zL97NJ-U.js → model-86tZgY0r.js} +1 -1
- package/dist/web/assets/shared-report-CLg82y4D.js +1 -0
- package/dist/web/assets/shared-report-ezeb8dW4.css +1 -0
- package/dist/web/assets/{useApi-CROJJdhE-CElDxEZI.js → useApi-CROJJdhE-BBUO_YEu.js} +1 -1
- package/dist/web/assets/useModalDialog-DrLhqtjR.js +1 -0
- package/dist/web/assets/{vue-router-F7MnkVlW.js → vue-router-Cw4QZxM1.js} +1 -1
- package/dist/web/index.html +14 -8
- package/docs/agent-interface-architecture.md +97 -0
- package/docs/employee-agents.md +58 -2
- package/graph-provider/fuli_graph/collaboration_context.py +2 -1
- package/graph-provider/fuli_graph/graph_models.py +5 -3
- package/graph-provider/fuli_graph/graph_query.py +4 -0
- package/graph-provider/fuli_graph/knowledge_search.py +2 -1
- package/graph-provider/fuli_graph/project_agent_access.py +2 -0
- package/graph-provider/fuli_graph/project_agent_attention_models.py +64 -0
- package/graph-provider/fuli_graph/project_agent_attention_routes.py +38 -0
- package/graph-provider/fuli_graph/project_agent_models.py +2 -0
- package/graph-provider/fuli_graph/project_agent_names.py +33 -0
- package/graph-provider/fuli_graph/project_agent_routes.py +12 -0
- package/graph-provider/fuli_graph/runtime.py +49 -20
- package/graph-provider/fuli_graph/store.py +2 -0
- package/graph-provider/fuli_graph/store_knowledge.py +4 -2
- package/graph-provider/fuli_graph/store_project_agent_attention.py +138 -0
- package/graph-provider/fuli_graph/store_project_agent_executor_learning.py +8 -0
- package/graph-provider/fuli_graph/store_project_agent_memory.py +40 -13
- package/graph-provider/fuli_graph/store_project_agent_task_recruitment.py +4 -1
- package/graph-provider/fuli_graph/store_project_agent_task_staffing.py +4 -1
- package/graph-provider/fuli_graph/store_project_agent_tasks.py +3 -0
- package/graph-provider/fuli_graph/store_project_agents.py +109 -0
- package/graph-provider/fuli_graph/store_task_contexts.py +1 -1
- package/graph-provider/fuli_graph/system_hr_identity.py +218 -0
- package/graph-provider/pyproject.toml +1 -1
- package/npm-shrinkwrap.json +52 -60
- package/package.json +2 -2
- package/skills/operating-fuli/SKILL.md +47 -0
- package/skills/operating-fuli/agents/openai.yaml +4 -0
- package/skills/operating-fuli/references/workflow-recipes.md +33 -0
- package/src/agent-tools/agent-attention-definitions.js +33 -0
- package/src/agent-tools/graph-definitions.js +8 -2
- package/src/agent-tools/graph-handlers.js +41 -0
- package/src/agent-tools/interface-catalog.js +101 -0
- package/src/agent-tools/interface-definitions.js +184 -0
- package/src/agent-tools/project-agent-definitions.js +1 -0
- package/src/cli/local-runtime-command.js +2 -1
- package/src/employees/catalog/bole.json +19 -0
- package/src/employees/install-package.js +3 -0
- package/src/employees/manifest.js +4 -0
- package/src/employees/package-registry.js +2 -0
- package/src/employees/recruitment.js +3 -0
- package/src/employees/service.js +10 -0
- package/src/external-knowledge/registry.js +24 -0
- package/src/external-knowledge/service.js +34 -1
- package/src/graphiti/agent-attention.js +26 -0
- package/src/graphiti/agent-knowledge-workflows.js +1 -0
- package/src/graphiti/project-agent-control-plane.js +21 -0
- package/src/graphiti/project-agent-mapping.js +6 -0
- package/src/graphiti/provider-client.js +13 -0
- package/src/graphiti/workspace-provider-client.js +9 -1
- package/src/http/agent-attention-api-router.js +28 -0
- package/src/http/error-mapping.js +3 -0
- package/src/http/graph-api-router.js +2 -0
- package/src/local-runtime/lifecycle.js +8 -2
- package/src/mcp/create-mcp-server.js +10 -1
- package/src/mcp/tool-annotations.js +31 -0
- package/src/mcp/tool-result.js +4 -4
- package/src/setup/agent-installation-status.js +2 -1
- package/src/setup/agents.js +4 -0
- package/src/setup/graph-runtime.js +4 -0
- package/src/setup/paths.js +2 -1
- package/src/setup/setup.js +4 -2
- package/src/setup/uninstall.js +4 -2
- package/src/system/resource-monitor.js +11 -1
- package/src/system/runtime-settings.js +7 -0
- package/dist/web/assets/AboutPage-DXh7Fwxt.css +0 -1
- package/dist/web/assets/ConnectionsPage-BMpkYeSE.js +0 -1
- package/dist/web/assets/EmployeeRecruitDialog-CnA7rOnB.css +0 -1
- package/dist/web/assets/EmployeeRecruitDialog-DbMVuLAI.js +0 -1
- package/dist/web/assets/EmployeeWorkbenchPage-BpkqpEs7.css +0 -1
- package/dist/web/assets/EmployeeWorkbenchPage-DtXSJOpq.js +0 -1
- package/dist/web/assets/KnowledgeEditDialog-DADXj_BT.js +0 -1
- package/dist/web/assets/KnowledgeEditDialog-iv-dgUeD.css +0 -1
- package/dist/web/assets/KnowledgeInspector-Xs4VCUg8.js +0 -1
- package/dist/web/assets/KnowledgeOrganizerPage-B_Ct4bB2.js +0 -1
- package/dist/web/assets/KnowledgeOrganizerPage-Cx1w1jb2.css +0 -1
- package/dist/web/assets/KnowledgePage-RG9Ttnrp.js +0 -1
- package/dist/web/assets/KnowledgeWorkspace-DI8YqTVI.js +0 -4
- package/dist/web/assets/PersonalProfilePage-B7T09bow.css +0 -1
- package/dist/web/assets/PersonalProfilePage-DCoG52SW.js +0 -1
- package/dist/web/assets/PersonalProjectsPage-CNb9mlbp.js +0 -1
- package/dist/web/assets/ProjectAgentsPage-C3vW6wmk.css +0 -1
- package/dist/web/assets/ProjectAgentsPage-zQ9BZW9v.js +0 -11
- package/dist/web/assets/PublicProjectsPage-BuLqkZ8g.js +0 -1
- package/dist/web/assets/ReviewPage-8A7N5pJs.js +0 -1
- package/dist/web/assets/SettingsPage-4ah1u906.js +0 -1
- package/dist/web/assets/SettingsPage-CwZp2ka4.css +0 -1
- package/dist/web/assets/WritingTastePage-DSgvTH6x.css +0 -1
- package/dist/web/assets/WritingTastePage-DztRj6MK.js +0 -1
- package/dist/web/assets/avatars-LvVKbDF7.js +0 -1
- package/dist/web/assets/console-B7dWYAoU.js +0 -1
- package/dist/web/assets/conversation-launcher-settings-D1xhW1S5.js +0 -1
- package/dist/web/assets/index-Ch1jmkyO.css +0 -1
- package/dist/web/assets/index-DRd-2Tii.js +0 -2
package/dist/web/index.html
CHANGED
|
@@ -9,16 +9,22 @@
|
|
|
9
9
|
/>
|
|
10
10
|
<link rel="icon" type="image/png" href="/assets/fuli-logo-ER1wQ8Kk.png" />
|
|
11
11
|
<title>复利 · Context Graph</title>
|
|
12
|
-
<script type="module" crossorigin src="/assets/index-
|
|
13
|
-
<link rel="modulepreload" crossorigin href="/assets/i18n-
|
|
14
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
15
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
16
|
-
<link rel="modulepreload" crossorigin href="/assets/vue-router-F7MnkVlW.js">
|
|
17
|
-
<link rel="modulepreload" crossorigin href="/assets/fuli-logo-Bu9WBuQe.js">
|
|
12
|
+
<script type="module" crossorigin src="/assets/index-Dioqmmvf.js"></script>
|
|
13
|
+
<link rel="modulepreload" crossorigin href="/assets/i18n-BkJjUsz_.js">
|
|
14
|
+
<link rel="modulepreload" crossorigin href="/assets/client-v8XKgQji.js">
|
|
15
|
+
<link rel="modulepreload" crossorigin href="/assets/console-C9HS-zbd.js">
|
|
18
16
|
<link rel="modulepreload" crossorigin href="/assets/_plugin-vue_export-helper-BDNMzG2s.js">
|
|
19
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
17
|
+
<link rel="modulepreload" crossorigin href="/assets/GrowthLoading-CCYPgE2f.js">
|
|
18
|
+
<link rel="modulepreload" crossorigin href="/assets/useApi-CROJJdhE-BBUO_YEu.js">
|
|
19
|
+
<link rel="modulepreload" crossorigin href="/assets/vue-router-Cw4QZxM1.js">
|
|
20
|
+
<link rel="modulepreload" crossorigin href="/assets/fuli-logo-Bu9WBuQe.js">
|
|
21
|
+
<link rel="modulepreload" crossorigin href="/assets/AgentHand-D5pOgVjJ.js">
|
|
22
|
+
<link rel="modulepreload" crossorigin href="/assets/useModalDialog-DrLhqtjR.js">
|
|
20
23
|
<link rel="modulepreload" crossorigin href="/assets/paths-CDREouhi.js">
|
|
21
|
-
<link rel="stylesheet" crossorigin href="/assets/
|
|
24
|
+
<link rel="stylesheet" crossorigin href="/assets/GrowthLoading-B_Hh0CRS.css">
|
|
25
|
+
<link rel="stylesheet" crossorigin href="/assets/AgentHand-DvdNtJ3_.css">
|
|
26
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CwjxeHGk.css">
|
|
27
|
+
<link rel="stylesheet" crossorigin href="/assets/employee-dialog-Bh5ccIbK.css">
|
|
22
28
|
</head>
|
|
23
29
|
<body>
|
|
24
30
|
<div id="app"></div>
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Agent interface architecture
|
|
2
|
+
|
|
3
|
+
FULI exposes product data operations through structured Agent tools so an AI Agent does not need browser automation to act on the user's behalf. The console and the Agent interface are peer clients of the same application services; neither client owns a second copy of the business rules.
|
|
4
|
+
|
|
5
|
+
## Contract
|
|
6
|
+
|
|
7
|
+
`list_agent_interfaces` returns the versioned `agent-interface-parity-v1` catalog. Every console data mutation is classified as one of:
|
|
8
|
+
|
|
9
|
+
- `agent`: the catalog names the structured Agent tool or tools that perform the same business operation.
|
|
10
|
+
- `local_user_only`: the catalog records why the operation requires local human presence.
|
|
11
|
+
|
|
12
|
+
The parity catalog is executable product metadata, not just documentation. Tests verify that every `agent` entry names a registered tool and that sensitive local-only routes remain explicit. A route with multiple actions, such as ignoring or resetting routing-learning evidence, lists every matching tool.
|
|
13
|
+
|
|
14
|
+
## Request path
|
|
15
|
+
|
|
16
|
+
```text
|
|
17
|
+
Console UI ───────┐
|
|
18
|
+
├─> application service ─> scoped Provider / local store
|
|
19
|
+
Agent tool / MCP ─┘
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Agent handlers delegate to existing application-service methods. They do not call the console over HTTP, render pages, write browser state, or bypass scope checks. Existing Host/Origin protection remains relevant to the console; Agent access is independently guarded by the Agent-access policy before any tool handler runs.
|
|
23
|
+
|
|
24
|
+
## Required operating sequence
|
|
25
|
+
|
|
26
|
+
### Agent names and human attention
|
|
27
|
+
|
|
28
|
+
Agent identity (`agentId`) and role (`profile.name`, `responsibility`) remain stable.
|
|
29
|
+
`profile.displayName` is the editable person-like nickname. Legacy role-style names
|
|
30
|
+
receive a deterministic nickname derived from their Agent ID; existing custom names
|
|
31
|
+
and explicit display names are preserved. These are product nicknames, not real people.
|
|
32
|
+
Reading a legacy profile does not rewrite its stored history.
|
|
33
|
+
|
|
34
|
+
Use `request_agent_attention` only for an explicit human action, with the exact space,
|
|
35
|
+
project and Agent, optional task ID, request kind, context and `requestedAction`.
|
|
36
|
+
The same idempotency key and payload returns the same request, including after closure;
|
|
37
|
+
reusing a key with another payload is a conflict. Running, waiting in a queue and
|
|
38
|
+
recoverable retries do not create attention requests automatically.
|
|
39
|
+
|
|
40
|
+
`list_agent_attention` supports project/Agent/status filters and pagination. Counts
|
|
41
|
+
cover the whole filtered set. Agents can withdraw obsolete requests with
|
|
42
|
+
`cancel_agent_attention`, using `expectedRevision` and a reason. Only the local-user
|
|
43
|
+
`POST /api/agent-attention/respond` workflow records a human reply. A reply closes the
|
|
44
|
+
attention item but does not itself grant permission, accept a task or run a command.
|
|
45
|
+
Agents read resolved replies and continue within the existing authority boundaries.
|
|
46
|
+
Original context, response, actor and timestamps remain in the private graph.
|
|
47
|
+
|
|
48
|
+
The console uses one shared attention store and dialog for the sidebar and personnel
|
|
49
|
+
views. Failed reads remain visible as errors, not a false empty state. Replies use
|
|
50
|
+
revision checks and preserve the user's draft on failure. Public reports never query
|
|
51
|
+
or expose this private attention queue.
|
|
52
|
+
|
|
53
|
+
An Agent should:
|
|
54
|
+
|
|
55
|
+
1. call `list_agent_interfaces`;
|
|
56
|
+
2. resolve exact space, project, item and version identifiers with read tools;
|
|
57
|
+
3. inspect the selected tool's current schema;
|
|
58
|
+
4. read current state before a write;
|
|
59
|
+
5. execute the narrowest scoped mutation with available compare-and-swap or idempotency fields;
|
|
60
|
+
6. verify through a structured read.
|
|
61
|
+
|
|
62
|
+
The bundled `operating-fuli` Skill teaches this sequence and is installed by `fuli setup` for supported Agents. Browser automation is permitted only as optional visual verification after a structured operation, never as a data-mutation fallback.
|
|
63
|
+
|
|
64
|
+
## Human-only boundaries
|
|
65
|
+
|
|
66
|
+
The following operations intentionally remain local-user-only:
|
|
67
|
+
|
|
68
|
+
- enabling or disabling Agent access;
|
|
69
|
+
- device-level runtime settings;
|
|
70
|
+
- completing a preference conflict as a human decision;
|
|
71
|
+
- confirming preference visibility scope;
|
|
72
|
+
- batch confirmation of knowledge.
|
|
73
|
+
- Jefa task completion that records explicit human acceptance (other task updates remain available through the employee tools).
|
|
74
|
+
|
|
75
|
+
These are trust boundaries, not missing interfaces. An Agent may explain where the user can perform the action, but must not substitute direct HTTP, storage, shell, or browser scripting.
|
|
76
|
+
|
|
77
|
+
## External knowledge
|
|
78
|
+
|
|
79
|
+
Connector discovery, binding creation, readiness checks, target updates, scoped synchronization, live retrieval, conflict policy and deletion have dedicated tools. Connector secrets are referenced by environment-variable name; tool results and catalog listings must not expose secret values.
|
|
80
|
+
|
|
81
|
+
Moving a connection between projects updates the binding targets. Deleting and recreating a binding is not the default because it breaks audit continuity and can discard connector state.
|
|
82
|
+
|
|
83
|
+
Target updates require the Agent's last-read `targetsVersion`. Registry mutations take a cross-process SQLite reservation, so sync/check/delete cannot overwrite a concurrent scope update. Busy or stale writes fail with a controlled conflict and must be re-read, not blindly retried. The legacy HTTP contract still accepts callers without a version for compatibility; current console and Agent clients supply one.
|
|
84
|
+
|
|
85
|
+
## Adding a feature
|
|
86
|
+
|
|
87
|
+
When a console feature adds a data mutation:
|
|
88
|
+
|
|
89
|
+
1. implement the business operation in an application service;
|
|
90
|
+
2. have the console route call that service;
|
|
91
|
+
3. add or reuse a typed Agent definition and a handler that calls the same service;
|
|
92
|
+
4. annotate read/write, destructive and open-world behavior for MCP clients;
|
|
93
|
+
5. add the route-to-tool mapping to `src/agent-tools/interface-catalog.js`, or document a narrowly justified `local_user_only` boundary;
|
|
94
|
+
6. add contract tests covering dispatch, scope, error behavior and the parity mapping;
|
|
95
|
+
7. update the `operating-fuli` workflow reference if the operation needs a non-obvious sequence.
|
|
96
|
+
|
|
97
|
+
A missing Agent interface must remain visible as a parity defect. Do not silently ship a browser-only write path.
|
package/docs/employee-agents.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Specialist Agents
|
|
2
2
|
|
|
3
|
-
An employee template is a reusable role with an optional FULI-hosted workbench. Recruitment reuses the existing durable Project Agent identity and assignment model. The
|
|
3
|
+
An employee template is a reusable role with an optional FULI-hosted or native workbench. Recruitment reuses the existing durable Project Agent identity and assignment model. The built-in specialists are **Jefa**, the project manager, and **Bole**, the fixed system HR Agent. Bole is created with every personal space, cannot be archived or replaced by another `hr` identity, and does not require a separate runtime package.
|
|
4
4
|
|
|
5
5
|
## User flow
|
|
6
6
|
|
|
@@ -16,7 +16,7 @@ The directory's **Filter projects** control supports multi-selection and inversi
|
|
|
16
16
|
|
|
17
17
|
Repeat recruitment is idempotent. Customized profile preferences and executor/model policies are preserved. One employee identity can have multiple project assignments. Reactivating an inactive identity is an explicit action; a conflicting unrelated identity is never overwritten. Recruitment does not start an executor or constitute model usage.
|
|
18
18
|
|
|
19
|
-
The catalog shows whether a workbench package is actually installed. Identity-only templates need no runtime and remain valid employees without a sidebar workbench.
|
|
19
|
+
The catalog shows whether a workbench package is actually installed. Identity-only templates need no runtime and remain valid employees without a sidebar workbench. A trusted built-in template may declare `workbench: { "kind": "native", "view": "people" }`; Bole uses this native panel to summarize Agent distribution, current tasks, and timestamped recruitment reasons from the same Project Agent APIs used elsewhere in the console.
|
|
20
20
|
|
|
21
21
|
## Local installation
|
|
22
22
|
|
|
@@ -71,8 +71,12 @@ Context includes `personalSpaceId`, `project`, `agentId`, and `basePath`; HTTP a
|
|
|
71
71
|
|
|
72
72
|
Optional manifest fields `defaultProjectScope: "all" | "selected"` and `taskEntry: { "boardTool": "read_board", "titleTool": "prepare_session_title" }` declare defaults and task-entry participation without a template-specific host branch. Context also carries the trusted `management` policy and native `session` identity when available. Task-entry board tools must declare `board.read`; the host returns bounded data and guidance, not an automatically started worker. A failed employee runtime degrades manager context without breaking collaboration preferences.
|
|
73
73
|
|
|
74
|
+
The optional built-in-only native workbench declaration is `workbench: { "kind": "native", "view": "people" }`. It renders host-owned read models and does not load third-party code. Installable employee packages continue to use the runtime contract above.
|
|
75
|
+
|
|
74
76
|
The reserved identity is `employee.<template-id>`, recognized by the generic `fuli.employee:<template-id>` capability. Preserve this marker when customizing employee profiles. The actual role, history, memory and project assignments remain in FULI's existing Agent model.
|
|
75
77
|
|
|
78
|
+
`employee.bole` is additionally reserved as the only system HR identity. Its Provider type is `hr`; other identities cannot claim that type. Recruitment history records the coordinating and HR Agent IDs, time, reason, proposed identity and final recruited identity, which powers Bole's people panel without inventing activity.
|
|
79
|
+
|
|
76
80
|
## HTTP and MCP
|
|
77
81
|
|
|
78
82
|
The host serves `/api/employee-templates`, recruitment/workspace/tool endpoints below it, and `/employee-workspaces/<template-id>/<project-id>/…`. Existing Host/Origin, JSON-body and LAN authorization checks apply. All workbench requests require an active matching identity and authorized responsibility policy, including assets and Agent discovery. Selected mode additionally requires an active assignment; all mode includes future active projects but enforces explicit exclusions and ended assignments before reaching the runtime.
|
|
@@ -87,6 +91,42 @@ Jefa supplies eight workbench tools for board/task reading, task creation, optim
|
|
|
87
91
|
|
|
88
92
|
## Verification and release boundary
|
|
89
93
|
|
|
94
|
+
Jefa uses one native `EmployeeTaskBoard` for every project scope. `/employees/jefa` and
|
|
95
|
+
`?project=all` show the current managed projects; legacy `__all__` is normalized. A single
|
|
96
|
+
project query or repeated `project` query parameters filter the same board, without changing
|
|
97
|
+
assignments. Task details and creation use the existing `get_task`, `create_tasks`, and
|
|
98
|
+
version-checked `update_tasks` interfaces. Explicit human confirmation of `done` continues
|
|
99
|
+
through the existing human HTTP endpoint. Other employee packages retain their generic iframe
|
|
100
|
+
workspaces; Jefa no longer switches to the package board when a card is opened.
|
|
101
|
+
|
|
102
|
+
The external-report action beside Jefa's identity has an independent project selection,
|
|
103
|
+
including all, one, several, and inverted selection. Generating a link explicitly enables
|
|
104
|
+
sharing for selected projects through `PATCH /employee-workspaces/jefa/<project>/api/projects/<project>/sharing`
|
|
105
|
+
with `{ publicShareEnabled: true, acceptsPublicRequests: false }`. Merely opening or filtering
|
|
106
|
+
the dialog performs no writes. Sharing status comes from the existing scoped snapshot API;
|
|
107
|
+
disabling uses the same endpoint with both flags false and invalidates that project's old slug.
|
|
108
|
+
|
|
109
|
+
One `/reports/jefa` link aggregates the selected project capabilities in its URL fragment,
|
|
110
|
+
keeping them out of HTTP access logs and referrer query strings. The report loads only
|
|
111
|
+
`GET /employee-workspaces/jefa/<project>/api/public/<slug>` and uses the same board in read-only
|
|
112
|
+
mode. It never loads the console shell or private board/task endpoints. The existing public
|
|
113
|
+
projection controls visibility: private tasks, details, evidence, and unrelated projects are
|
|
114
|
+
not included. Partial enablement is reported without claiming a complete link, and selection
|
|
115
|
+
changes clear the previously generated link. “All” is the current selection, not consent to
|
|
116
|
+
publish future projects automatically.
|
|
117
|
+
|
|
118
|
+
Before presenting a report link, the dialog verifies the selected public projections and
|
|
119
|
+
shows their task count. A zero-task selection is not presented as a successful report;
|
|
120
|
+
existing empty links explain that no tasks have been approved for public viewing. This
|
|
121
|
+
never changes task privacy or substitutes private board data for a public projection.
|
|
122
|
+
Refreshing the preview preserves the selected projects.
|
|
123
|
+
|
|
124
|
+
These structured HTTP operations remain subject to the host's existing scope, Origin and LAN
|
|
125
|
+
authentication checks. The link is not a separate authorization layer for the entire console.
|
|
126
|
+
Loopback links work only on the same computer; this UI does not enable LAN access, tunnels,
|
|
127
|
+
production hosting, or new public task visibility. Do not expose the local management service
|
|
128
|
+
as a public multi-user report server.
|
|
129
|
+
|
|
90
130
|
Run FULI's normal Node/web tests and typecheck. For an actual Jefa build:
|
|
91
131
|
|
|
92
132
|
```sh
|
|
@@ -97,3 +137,19 @@ node scripts/employee-package-smoke.js <built-package-directory> --serve
|
|
|
97
137
|
The smoke fixture uses a synthetic FULI directory but the real installed employee bundle, SQLite and MCP transport. The UI mode does not query a personal provider or external model. It deletes its temporary test data on shutdown.
|
|
98
138
|
|
|
99
139
|
Signing, remote catalog distribution, a runtime sandbox, database migration/uninstall UX, and production multi-user authorization remain separate release work. This feature does not authorize or publish a Feishu application.
|
|
140
|
+
|
|
141
|
+
### Bole identity upgrades
|
|
142
|
+
|
|
143
|
+
Local startup calls the idempotent system-HR endpoint for both fresh and existing installs.
|
|
144
|
+
It consolidates the reserved legacy `fuli-project-hr` identity into `employee.bole` in one
|
|
145
|
+
transaction. Names never identify merge targets. The legacy node remains an audit alias;
|
|
146
|
+
assignments, recruitment references, attention and task history follow the canonical Bole.
|
|
147
|
+
Old profile links resolve to Bole, and the obsolete ID cannot be recreated as another role.
|
|
148
|
+
Working-memory heads and immutable checkpoint JSON/hashes are retained; equivalent old/new-ID
|
|
149
|
+
memory retries address the same checkpoint. Knowledge visibility metadata is migrated only
|
|
150
|
+
inside the owning space, without changing historical evidence UUIDs.
|
|
151
|
+
|
|
152
|
+
Conflicting memory heads, locked executor policies, relationship properties or overlapping
|
|
153
|
+
learning buckets abort the complete migration. Nothing is silently selected or overwritten.
|
|
154
|
+
The Neo4j acceptance tests require an explicitly disposable loopback database; they never
|
|
155
|
+
clear or seed an existing user's graph.
|
|
@@ -42,7 +42,7 @@ async def read_collaboration_context(
|
|
|
42
42
|
status_code=422,
|
|
43
43
|
detail='project Agent preferences require a personal project',
|
|
44
44
|
)
|
|
45
|
-
await authorize_project_agent(
|
|
45
|
+
agent = await authorize_project_agent(
|
|
46
46
|
store,
|
|
47
47
|
actor,
|
|
48
48
|
space,
|
|
@@ -50,6 +50,7 @@ async def read_collaboration_context(
|
|
|
50
50
|
project_agent_id,
|
|
51
51
|
require_memory=True,
|
|
52
52
|
)
|
|
53
|
+
project_agent_id = agent['agent_id']
|
|
53
54
|
project_scopes = await _project_scopes(
|
|
54
55
|
store,
|
|
55
56
|
space,
|
|
@@ -17,7 +17,7 @@ from .models import (
|
|
|
17
17
|
KnowledgeItemKind,
|
|
18
18
|
KnowledgeRevisionRecord,
|
|
19
19
|
PersonalProfileAspect,
|
|
20
|
-
|
|
20
|
+
ScopedPreferenceScope,
|
|
21
21
|
StrictModel,
|
|
22
22
|
)
|
|
23
23
|
from .project_action_models import (
|
|
@@ -44,8 +44,9 @@ class GraphNode(StrictModel):
|
|
|
44
44
|
confirmation_basis: ConfirmationBasis | None = None
|
|
45
45
|
reasoning_summary: str | None = None
|
|
46
46
|
profile_aspect: PersonalProfileAspect | None = None
|
|
47
|
-
preference_scope:
|
|
47
|
+
preference_scope: ScopedPreferenceScope | None = None
|
|
48
48
|
preference_project_id: str | None = None
|
|
49
|
+
preference_agent_id: str | None = None
|
|
49
50
|
inheritance_mode: KnowledgeInheritanceMode = 'local_only'
|
|
50
51
|
inherited_project_ids: list[str] = Field(default_factory=list, max_length=32)
|
|
51
52
|
human_edited: bool = False
|
|
@@ -97,8 +98,9 @@ class GraphEdge(StrictModel):
|
|
|
97
98
|
confirmation_basis: ConfirmationBasis | None = None
|
|
98
99
|
reasoning_summary: str | None = None
|
|
99
100
|
profile_aspect: PersonalProfileAspect | None = None
|
|
100
|
-
preference_scope:
|
|
101
|
+
preference_scope: ScopedPreferenceScope | None = None
|
|
101
102
|
preference_project_id: str | None = None
|
|
103
|
+
preference_agent_id: str | None = None
|
|
102
104
|
inheritance_mode: KnowledgeInheritanceMode = 'local_only'
|
|
103
105
|
inherited_project_ids: list[str] = Field(default_factory=list, max_length=32)
|
|
104
106
|
human_edited: bool = False
|
|
@@ -216,6 +216,7 @@ def _node_query(project_scoped: bool, paginated: bool = False) -> str:
|
|
|
216
216
|
ELSE coalesce(node.fuli_preference_scope, 'global') END
|
|
217
217
|
AS preference_scope,
|
|
218
218
|
node.fuli_preference_project_id AS preference_project_id,
|
|
219
|
+
node.fuli_preference_agent_id AS preference_agent_id,
|
|
219
220
|
coalesce(node.fuli_inheritance_mode, 'local_only')
|
|
220
221
|
AS inheritance_mode,
|
|
221
222
|
coalesce(node.fuli_inherited_project_ids, [])
|
|
@@ -331,6 +332,7 @@ def _edge_query(project_scoped: bool, paginated: bool = False) -> str:
|
|
|
331
332
|
ELSE coalesce(edge.fuli_preference_scope, 'global') END
|
|
332
333
|
AS preference_scope,
|
|
333
334
|
edge.fuli_preference_project_id AS preference_project_id,
|
|
335
|
+
edge.fuli_preference_agent_id AS preference_agent_id,
|
|
334
336
|
coalesce(edge.fuli_inheritance_mode, 'local_only')
|
|
335
337
|
AS inheritance_mode,
|
|
336
338
|
coalesce(edge.fuli_inherited_project_ids, [])
|
|
@@ -499,6 +501,7 @@ def _graph_node(
|
|
|
499
501
|
profile_aspect=record.get('profile_aspect'),
|
|
500
502
|
preference_scope=record.get('preference_scope'),
|
|
501
503
|
preference_project_id=record.get('preference_project_id'),
|
|
504
|
+
preference_agent_id=record.get('preference_agent_id'),
|
|
502
505
|
inheritance_mode=record.get('inheritance_mode') or 'local_only',
|
|
503
506
|
inherited_project_ids=record.get('inherited_project_ids') or [],
|
|
504
507
|
human_edited=record.get('human_edited') is True,
|
|
@@ -580,6 +583,7 @@ def _graph_edge(
|
|
|
580
583
|
profile_aspect=record.get('profile_aspect'),
|
|
581
584
|
preference_scope=record.get('preference_scope'),
|
|
582
585
|
preference_project_id=record.get('preference_project_id'),
|
|
586
|
+
preference_agent_id=record.get('preference_agent_id'),
|
|
583
587
|
inheritance_mode=record.get('inheritance_mode') or 'local_only',
|
|
584
588
|
inherited_project_ids=record.get('inherited_project_ids') or [],
|
|
585
589
|
human_edited=record.get('human_edited') is True,
|
|
@@ -317,7 +317,7 @@ async def _personal_search_space(store, actor, spaces, request):
|
|
|
317
317
|
for project_id in request.personal_project_ids:
|
|
318
318
|
await authorize_personal_project(store, actor, space, project_id)
|
|
319
319
|
if request.project_agent_id:
|
|
320
|
-
await authorize_project_agent(
|
|
320
|
+
agent = await authorize_project_agent(
|
|
321
321
|
store,
|
|
322
322
|
actor,
|
|
323
323
|
space,
|
|
@@ -326,6 +326,7 @@ async def _personal_search_space(store, actor, spaces, request):
|
|
|
326
326
|
require_active=True,
|
|
327
327
|
require_memory=True,
|
|
328
328
|
)
|
|
329
|
+
request.project_agent_id = agent['agent_id']
|
|
329
330
|
return space
|
|
330
331
|
|
|
331
332
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from fastapi import HTTPException
|
|
2
2
|
|
|
3
3
|
from .personal_project_access import authorize_personal_project
|
|
4
|
+
from .system_hr_identity import resolve_hr_alias
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
async def authorize_project_agent(
|
|
@@ -20,6 +21,7 @@ async def authorize_project_agent(
|
|
|
20
21
|
detail='project Agent access requires a personal project',
|
|
21
22
|
)
|
|
22
23
|
await authorize_personal_project(store, actor, space, project_id)
|
|
24
|
+
agent_id = await resolve_hr_alias(store, space['id'], agent_id)
|
|
23
25
|
records, _, _ = await store.runtime.driver.execute_query(
|
|
24
26
|
'''
|
|
25
27
|
MATCH (space:FuliSpace {id: $space_id, kind: 'personal'})-
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Literal
|
|
3
|
+
|
|
4
|
+
from pydantic import Field, model_validator
|
|
5
|
+
|
|
6
|
+
from .model_base import StrictModel
|
|
7
|
+
from .model_validation import reject_credentials
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
AttentionStatus = Literal['open', 'resolved', 'cancelled']
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AgentAttentionCreate(StrictModel):
|
|
14
|
+
personal_space_id: str = Field(min_length=1, max_length=128)
|
|
15
|
+
personal_project_id: str = Field(min_length=1, max_length=128)
|
|
16
|
+
agent_id: str = Field(min_length=1, max_length=128)
|
|
17
|
+
task_id: str | None = Field(default=None, min_length=1, max_length=128)
|
|
18
|
+
idempotency_key: str = Field(min_length=8, max_length=256)
|
|
19
|
+
kind: Literal['question', 'approval', 'review', 'permission', 'blocked']
|
|
20
|
+
title: str = Field(min_length=1, max_length=160)
|
|
21
|
+
detail: str = Field(min_length=1, max_length=4096)
|
|
22
|
+
requested_action: str = Field(min_length=1, max_length=2048)
|
|
23
|
+
|
|
24
|
+
@model_validator(mode='after')
|
|
25
|
+
def reject_sensitive_values(self):
|
|
26
|
+
reject_credentials(self, 'Agent attention request')
|
|
27
|
+
return self
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class AgentAttentionDecision(StrictModel):
|
|
31
|
+
personal_space_id: str = Field(min_length=1, max_length=128)
|
|
32
|
+
personal_project_id: str = Field(min_length=1, max_length=128)
|
|
33
|
+
request_id: str = Field(min_length=1, max_length=128)
|
|
34
|
+
expected_revision: int = Field(ge=0)
|
|
35
|
+
response: str = Field(min_length=1, max_length=4096)
|
|
36
|
+
|
|
37
|
+
@model_validator(mode='after')
|
|
38
|
+
def reject_sensitive_values(self):
|
|
39
|
+
reject_credentials(self, 'Agent attention response')
|
|
40
|
+
return self
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class AgentAttentionRecord(StrictModel):
|
|
44
|
+
request_id: str
|
|
45
|
+
personal_space_id: str
|
|
46
|
+
personal_project_id: str
|
|
47
|
+
agent_id: str
|
|
48
|
+
task_id: str | None = None
|
|
49
|
+
kind: str
|
|
50
|
+
title: str
|
|
51
|
+
detail: str
|
|
52
|
+
requested_action: str
|
|
53
|
+
status: AttentionStatus = 'open'
|
|
54
|
+
revision: int = 0
|
|
55
|
+
response: str | None = None
|
|
56
|
+
responded_by: Literal['human', 'agent'] | None = None
|
|
57
|
+
created_at: datetime
|
|
58
|
+
updated_at: datetime
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class AgentAttentionList(StrictModel):
|
|
62
|
+
items: list[AgentAttentionRecord]
|
|
63
|
+
total: int
|
|
64
|
+
counts: dict[str, int]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from typing import Annotated, Any
|
|
2
|
+
|
|
3
|
+
from fastapi import Query
|
|
4
|
+
|
|
5
|
+
from .project_agent_attention_models import (
|
|
6
|
+
AgentAttentionCreate, AgentAttentionDecision, AgentAttentionList,
|
|
7
|
+
AgentAttentionRecord, AttentionStatus,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def register_project_agent_attention_routes(application, store, Actor: Any):
|
|
12
|
+
@application.post('/v1/agent-attention', response_model=AgentAttentionRecord)
|
|
13
|
+
async def create(request: AgentAttentionCreate, actor: Actor):
|
|
14
|
+
return await store.create_agent_attention(actor, request)
|
|
15
|
+
|
|
16
|
+
@application.get('/v1/agent-attention', response_model=AgentAttentionList)
|
|
17
|
+
async def list_requests(
|
|
18
|
+
actor: Actor,
|
|
19
|
+
personal_space_id: Annotated[str, Query(min_length=1, max_length=128)],
|
|
20
|
+
personal_project_id: Annotated[str | None, Query(min_length=1, max_length=128)] = None,
|
|
21
|
+
agent_id: Annotated[str | None, Query(min_length=1, max_length=128)] = None,
|
|
22
|
+
status: AttentionStatus | None = 'open',
|
|
23
|
+
limit: Annotated[int, Query(ge=1, le=200)] = 100,
|
|
24
|
+
offset: Annotated[int, Query(ge=0)] = 0,
|
|
25
|
+
):
|
|
26
|
+
return await store.list_agent_attention(
|
|
27
|
+
actor, personal_space_id, personal_project_id, agent_id, status, limit, offset,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
@application.post('/v1/agent-attention/cancel', response_model=AgentAttentionRecord)
|
|
31
|
+
async def cancel(request: AgentAttentionDecision, actor: Actor):
|
|
32
|
+
return await store.decide_agent_attention(actor, request, human=False)
|
|
33
|
+
|
|
34
|
+
@application.post('/v1/agent-attention/respond', response_model=AgentAttentionRecord)
|
|
35
|
+
async def respond(request: AgentAttentionDecision, actor: Actor):
|
|
36
|
+
# Only the trusted local-user HTTP workflow exposes this operation.
|
|
37
|
+
# A reply does not execute an approval, permission change or acceptance.
|
|
38
|
+
return await store.decide_agent_attention(actor, request, human=True)
|
|
@@ -90,6 +90,7 @@ class ProjectAgentExecutorPolicy(StrictModel):
|
|
|
90
90
|
|
|
91
91
|
class ProjectAgentProfile(StrictModel):
|
|
92
92
|
name: str = Field(min_length=1, max_length=160)
|
|
93
|
+
display_name: str | None = Field(default=None, min_length=1, max_length=160)
|
|
93
94
|
# Keep the visual occupation marker as first-class profile data. It is
|
|
94
95
|
# intentionally independent from ``name`` so clients can update either
|
|
95
96
|
# value without rewriting the other and old profiles can omit it.
|
|
@@ -266,6 +267,7 @@ class ProjectAgentAssignmentReplaceResult(StrictModel):
|
|
|
266
267
|
|
|
267
268
|
class ProjectAgentRecord(StrictModel):
|
|
268
269
|
agent_id: str
|
|
270
|
+
legacy_agent_ids: list[str] = Field(default_factory=list)
|
|
269
271
|
personal_space_id: str
|
|
270
272
|
personal_project_id: str | None = None
|
|
271
273
|
profile: ProjectAgentProfile
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Stable product nicknames, independent of identity, assignment and work status."""
|
|
2
|
+
import hashlib
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
FIRST_NAMES = (
|
|
7
|
+
'Milo', 'Nova', 'Arlo', 'Nora', 'Theo', 'Iris', 'Remy', 'Lena',
|
|
8
|
+
'Finn', 'Cleo', 'Ezra', 'Mira', 'Jude', 'Lyra', 'Owen', 'Cora',
|
|
9
|
+
'Alex', 'Robin', 'Jamie', 'Rowan', 'Casey', 'Avery', 'Morgan', 'Riley',
|
|
10
|
+
'Ellis', 'Quinn', 'Sage', 'Blair', 'Reese', 'Emery', 'Drew', 'Sky',
|
|
11
|
+
)
|
|
12
|
+
LAST_NAMES = (
|
|
13
|
+
'Reed', 'Lane', 'Vale', 'Brook', 'Wren', 'Stone', 'Park', 'Finch',
|
|
14
|
+
'Lake', 'Ash', 'Birch', 'Hill', 'West', 'Woods', 'Dale', 'Hart',
|
|
15
|
+
'Gray', 'Bell', 'Hayes', 'Blake', 'Lowe', 'Frost', 'Green', 'Page',
|
|
16
|
+
'Wells', 'Shaw', 'Fox', 'Snow', 'Field', 'North', 'Ray', 'Cole',
|
|
17
|
+
)
|
|
18
|
+
ROLE_NAME = re.compile(
|
|
19
|
+
r'(工程师|协调人|开发者|设计师|架构师|审计员|项目经理|负责人|专员|助手|智能体|验证员|测试员|记录员)'
|
|
20
|
+
r'|\b(agent|engineer|coordinator|specialist|developer|reviewer|tester|verifier|auditor|designer|architect|implementer)\b',
|
|
21
|
+
re.IGNORECASE,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def agent_display_name(agent_id: str, name: str, display_name: str | None = None):
|
|
26
|
+
if display_name:
|
|
27
|
+
return display_name
|
|
28
|
+
if agent_id == 'fuli-project-coordinator' and name == '项目协调人':
|
|
29
|
+
return 'Orion'
|
|
30
|
+
if not ROLE_NAME.search(name):
|
|
31
|
+
return name
|
|
32
|
+
digest = hashlib.sha256(agent_id.encode()).digest()
|
|
33
|
+
return f'{FIRST_NAMES[digest[0] % len(FIRST_NAMES)]} {LAST_NAMES[digest[1] % len(LAST_NAMES)]}'
|
|
@@ -60,6 +60,8 @@ def register_project_agent_routes(
|
|
|
60
60
|
"""Attach Project Agent routes while keeping ``create_app`` orchestration small."""
|
|
61
61
|
|
|
62
62
|
register_project_agent_memory_routes(application, store, Actor)
|
|
63
|
+
from .project_agent_attention_routes import register_project_agent_attention_routes
|
|
64
|
+
register_project_agent_attention_routes(application, store, Actor)
|
|
63
65
|
|
|
64
66
|
@application.put('/v1/project-agents', response_model=ProjectAgentRecord)
|
|
65
67
|
async def upsert_project_agent(
|
|
@@ -137,6 +139,16 @@ def register_project_agent_routes(
|
|
|
137
139
|
) -> ProjectAgentRecord:
|
|
138
140
|
return await store.ensure_system_project_coordinator(actor, personal_space_id)
|
|
139
141
|
|
|
142
|
+
@application.post(
|
|
143
|
+
'/v1/project-agents/system-hr',
|
|
144
|
+
response_model=ProjectAgentRecord,
|
|
145
|
+
)
|
|
146
|
+
async def ensure_project_agent_hr(
|
|
147
|
+
actor: Actor,
|
|
148
|
+
personal_space_id: Annotated[str, Query(min_length=1, max_length=128)],
|
|
149
|
+
) -> ProjectAgentRecord:
|
|
150
|
+
return await store.ensure_system_project_hr(actor, personal_space_id)
|
|
151
|
+
|
|
140
152
|
@application.post(
|
|
141
153
|
'/v1/project-agent-assignments',
|
|
142
154
|
response_model=ProjectAgentAssignmentRecord,
|
|
@@ -271,6 +271,8 @@ class GraphitiRuntime:
|
|
|
271
271
|
for query in (
|
|
272
272
|
'CREATE CONSTRAINT fuli_project_agent_id IF NOT EXISTS '
|
|
273
273
|
'FOR (n:FuliProjectAgent) REQUIRE n.id IS UNIQUE',
|
|
274
|
+
'CREATE CONSTRAINT fuli_agent_attention_id IF NOT EXISTS '
|
|
275
|
+
'FOR (n:FuliAgentAttention) REQUIRE n.id IS UNIQUE',
|
|
274
276
|
'CREATE CONSTRAINT fuli_project_agent_assignment_id IF NOT EXISTS '
|
|
275
277
|
'FOR (n:FuliProjectAgentAssignment) REQUIRE n.id IS UNIQUE',
|
|
276
278
|
'CREATE CONSTRAINT fuli_project_agent_memory_id IF NOT EXISTS '
|
|
@@ -516,7 +518,7 @@ class GraphitiRuntime:
|
|
|
516
518
|
from .project_agent_models import ProjectAgentProfile
|
|
517
519
|
from .provider_values import now_utc, stable_uuid
|
|
518
520
|
|
|
519
|
-
|
|
521
|
+
coordinator_profile = ProjectAgentProfile(
|
|
520
522
|
name='项目协调人',
|
|
521
523
|
responsibility='评估任务、应用用户配置并路由已有 Agent。',
|
|
522
524
|
agent_type='coordinator',
|
|
@@ -525,26 +527,50 @@ class GraphitiRuntime:
|
|
|
525
527
|
initial_preferences=['质量与可验收完成优先于成本和时间'],
|
|
526
528
|
status='active',
|
|
527
529
|
)
|
|
530
|
+
hr_profile = ProjectAgentProfile(
|
|
531
|
+
name='Bole',
|
|
532
|
+
occupation_emoji='🔎',
|
|
533
|
+
responsibility=(
|
|
534
|
+
'维护 Agent 人员分布、当前工作与可审计招募记录,'
|
|
535
|
+
'并在需要新角色时执行受策略约束的招募。'
|
|
536
|
+
),
|
|
537
|
+
agent_type='hr',
|
|
538
|
+
work_kinds=['agent-recruitment', 'staffing-review'],
|
|
539
|
+
capabilities=[
|
|
540
|
+
'Agent 招募', '人员分布', '工作状态', '招募审计',
|
|
541
|
+
'fuli.employee:bole',
|
|
542
|
+
],
|
|
543
|
+
initial_preferences=[
|
|
544
|
+
'每次招募保留任务、原因、触发来源与时间线。',
|
|
545
|
+
'只展示 Provider 已记录的人员状态,不推测未上报的工作。',
|
|
546
|
+
],
|
|
547
|
+
status='active',
|
|
548
|
+
)
|
|
528
549
|
updated_at = now_utc()
|
|
529
550
|
for record in records:
|
|
530
551
|
space_id = record['space_id']
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
552
|
+
for agent_id, profile in (
|
|
553
|
+
('fuli-project-coordinator', coordinator_profile),
|
|
554
|
+
('employee.bole', hr_profile),
|
|
555
|
+
):
|
|
556
|
+
node_id = stable_uuid(
|
|
557
|
+
self.settings.provider_id,
|
|
558
|
+
space_id,
|
|
559
|
+
'project-agent',
|
|
560
|
+
agent_id,
|
|
561
|
+
)
|
|
562
|
+
await self.driver.execute_query(
|
|
563
|
+
'''
|
|
539
564
|
MATCH (space:FuliSpace {id: $space_id, kind: 'personal'})
|
|
540
565
|
MERGE (agent:FuliProjectAgent {id: $id})
|
|
541
|
-
ON CREATE SET agent.agent_id =
|
|
566
|
+
ON CREATE SET agent.agent_id = $agent_id,
|
|
542
567
|
agent.profile_json = $profile_json,
|
|
543
568
|
agent.name = $name,
|
|
569
|
+
agent.occupation_emoji = $occupation_emoji,
|
|
544
570
|
agent.responsibility = $responsibility,
|
|
545
571
|
agent.capabilities = $capabilities,
|
|
546
572
|
agent.work_kinds = $work_kinds,
|
|
547
|
-
agent.agent_type =
|
|
573
|
+
agent.agent_type = $agent_type,
|
|
548
574
|
agent.memory_scope = 'reviewed_agent',
|
|
549
575
|
agent.status = 'active',
|
|
550
576
|
agent.system_managed = true,
|
|
@@ -552,15 +578,18 @@ class GraphitiRuntime:
|
|
|
552
578
|
agent.updated_at = $updated_at
|
|
553
579
|
MERGE (space)-[:HAS_PROJECT_AGENT_IDENTITY]->(agent)
|
|
554
580
|
''',
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
581
|
+
space_id=space_id,
|
|
582
|
+
id=node_id,
|
|
583
|
+
agent_id=agent_id,
|
|
584
|
+
profile_json=profile.model_dump_json(),
|
|
585
|
+
name=profile.name,
|
|
586
|
+
occupation_emoji=profile.occupation_emoji,
|
|
587
|
+
responsibility=profile.responsibility,
|
|
588
|
+
capabilities=profile.capabilities,
|
|
589
|
+
work_kinds=profile.work_kinds,
|
|
590
|
+
agent_type=profile.agent_type,
|
|
591
|
+
updated_at=updated_at,
|
|
592
|
+
)
|
|
564
593
|
|
|
565
594
|
async def _migrate_personal_project_relation_review_defaults(self) -> None:
|
|
566
595
|
await self.driver.execute_query(
|
|
@@ -7,6 +7,7 @@ from .store_project_agent_executors import StoreProjectAgentExecutors
|
|
|
7
7
|
from .store_project_agent_memory import StoreProjectAgentMemory
|
|
8
8
|
from .store_project_agent_context import StoreProjectAgentContext
|
|
9
9
|
from .store_project_agents import StoreProjectAgents
|
|
10
|
+
from .store_project_agent_attention import StoreProjectAgentAttention
|
|
10
11
|
from .store_project_agent_tasks import StoreProjectAgentTasks
|
|
11
12
|
from .store_projects import StoreProjects
|
|
12
13
|
from .store_publication import StorePublication
|
|
@@ -18,6 +19,7 @@ class GraphStore(
|
|
|
18
19
|
StoreIdentity,
|
|
19
20
|
StoreProjects,
|
|
20
21
|
StoreProjectAgents,
|
|
22
|
+
StoreProjectAgentAttention,
|
|
21
23
|
StoreProjectAgentMemory,
|
|
22
24
|
StoreProjectAgentContext,
|
|
23
25
|
StoreTaskContexts,
|