fuli-context 0.8.0 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/README.zh-CN.md +4 -1
- package/dist/web/assets/{AboutPage-BjMmpwJB.js → AboutPage-fBq5VFoQ.js} +1 -1
- package/dist/web/assets/AgentHand-CMZk5GjY.js +1 -0
- package/dist/web/assets/AgentName-Cydd0UI4.css +1 -0
- package/dist/web/assets/AgentName-DRZHAIB2.js +1 -0
- package/dist/web/assets/AgentProfilePage-BPQEfMhB.js +1 -0
- package/dist/web/assets/AgentsDirectoryPage-CKZfrcuD.js +1 -0
- package/dist/web/assets/{ConnectionsPage-BKGxFDos.js → ConnectionsPage-COb56qnk.js} +1 -1
- package/dist/web/assets/EmployeeRecruitDialog-BLTpjCcK.js +1 -0
- package/dist/web/assets/EmployeeWorkbenchPage-CQwZnTA0.css +1 -0
- package/dist/web/assets/EmployeeWorkbenchPage-HUd-gne2.js +3 -0
- package/dist/web/assets/{GrowthLoading-WXCj8b0r.js → GrowthLoading-Bu_kH-I8.js} +1 -1
- package/dist/web/assets/{KnowledgeEditDialog-Bkie08ZA.js → KnowledgeEditDialog-BTwXXRCD.js} +1 -1
- package/dist/web/assets/{KnowledgeInspector-Cez-Z_95.js → KnowledgeInspector-Ct5hl2--.js} +1 -1
- package/dist/web/assets/{KnowledgeOrganizerPage-BYmTPA_8.js → KnowledgeOrganizerPage-DMC8Hh43.js} +1 -1
- package/dist/web/assets/KnowledgePage-uXW1Eovz.js +1 -0
- package/dist/web/assets/{KnowledgeWorkspace-DKij2_B4.js → KnowledgeWorkspace-DmHIc0jb.js} +1 -1
- package/dist/web/assets/{NotFoundPage-HGV-q-bh.js → NotFoundPage-C0yOR9T9.js} +1 -1
- package/dist/web/assets/{OverviewPage-DE29_F_Y.js → OverviewPage-BreX_kb4.js} +1 -1
- package/dist/web/assets/{PersonalProfilePage-B95DFc7x.js → PersonalProfilePage-CX1a6_E3.js} +1 -1
- package/dist/web/assets/PersonalProjectsPage-Br1S8ETM.js +1 -0
- package/dist/web/assets/ProjectAgentsPage-CA04h1Uf.js +13 -0
- package/dist/web/assets/ProjectAgentsPage-CZWWV4Kw.css +1 -0
- package/dist/web/assets/{ProjectScopePicker-Cu5PdxF6.js → ProjectScopePicker-DVHfXX1f.js} +1 -1
- package/dist/web/assets/{PublicProjectsPage-BY4B3_t3.js → PublicProjectsPage-CtctKml1.js} +1 -1
- package/dist/web/assets/{ReviewPage-C9vCesNu.js → ReviewPage-DUqaK-do.js} +1 -1
- package/dist/web/assets/{SearchableSelect-214TvfYg.js → SearchableSelect-BZtyYmZN.js} +1 -1
- package/dist/web/assets/{SettingsPage-D9TnzcFc.js → SettingsPage-CB4ymLr8.js} +1 -1
- package/dist/web/assets/{SharedJefaReportPage-CdSGI_OE.js → SharedJefaReportPage-CbFtrDCY.js} +1 -1
- package/dist/web/assets/{WritingTastePage-CUE1dU8f.js → WritingTastePage-B7lSzs2z.js} +1 -1
- package/dist/web/assets/agent-profile-CdKOh5xw.css +1 -0
- package/dist/web/assets/agent-profile.css_vue_type_style_index_0_src_true_lang-C22UdfU0.js +1 -0
- package/dist/web/assets/catalog-DU4Kfd-N.js +1 -0
- package/dist/web/assets/{client-Bl4oiW3E.js → client-CEHkJJ-5.js} +1 -1
- package/dist/web/assets/{console-BAIVNC-N.js → console--CZxc3PW.js} +1 -1
- package/dist/web/assets/{conversation-launcher-settings-DpRI_Jp2.js → conversation-launcher-settings-Co1RlTFH.js} +1 -1
- package/dist/web/assets/{i18n-CVFADb_M.js → i18n-BtOOoyJd.js} +3 -3
- package/dist/web/assets/{identity-Dt0WZgqo.js → identity-CsowvtIm.js} +1 -1
- package/dist/web/assets/index-7X3LGByg.js +2 -0
- package/dist/web/assets/index-CWMqDq8g.css +1 -0
- package/dist/web/assets/{model-B8sOxih0.js → model-CGpllrds.js} +1 -1
- package/dist/web/assets/{personal-profile-graph-aAqex7ty.js → personal-profile-graph-BNTG4PSY.js} +1 -1
- package/dist/web/assets/profile-model-BQO4zNs9.js +1 -0
- package/dist/web/assets/{shared-report-BQD6l_cS.js → shared-report-BFUJqqQe.js} +1 -1
- package/dist/web/assets/task-evidence-BUu7yYIn.js +1 -0
- package/dist/web/assets/{useApi-CROJJdhE-CuJ16v2S.js → useApi-CROJJdhE-raNlHRDl.js} +1 -1
- package/dist/web/assets/{useModalDialog-Ddv2XJQi.js → useModalDialog-CroGk2Cs.js} +1 -1
- package/dist/web/assets/{vue-router-C8KuEZ6a.js → vue-router-JFYBLXB1.js} +1 -1
- package/dist/web/index.html +12 -10
- package/docs/agent-conversations-and-collaboration.md +115 -0
- package/docs/agent-interface-architecture.md +17 -0
- package/docs/employee-agents.md +14 -1
- package/docs/project-agent-memory.md +34 -1
- package/graph-provider/fuli_graph/agent_conversation_models.py +72 -0
- package/graph-provider/fuli_graph/agent_conversation_routes.py +47 -0
- package/graph-provider/fuli_graph/agent_loan_models.py +28 -0
- package/graph-provider/fuli_graph/agent_quality_policy.py +151 -0
- package/graph-provider/fuli_graph/agent_verification_models.py +22 -0
- package/graph-provider/fuli_graph/conversation_context.py +30 -0
- package/graph-provider/fuli_graph/knowledge_search.py +8 -16
- package/graph-provider/fuli_graph/project_agent_executor_models.py +3 -0
- package/graph-provider/fuli_graph/project_agent_memory_routes.py +2 -0
- package/graph-provider/fuli_graph/project_agent_models.py +10 -0
- package/graph-provider/fuli_graph/project_agent_routes.py +5 -1
- package/graph-provider/fuli_graph/project_agent_task_models.py +4 -0
- package/graph-provider/fuli_graph/runtime.py +14 -0
- package/graph-provider/fuli_graph/store.py +6 -0
- package/graph-provider/fuli_graph/store_agent_conversations.py +206 -0
- package/graph-provider/fuli_graph/store_agent_loans.py +146 -0
- package/graph-provider/fuli_graph/store_agent_verification.py +111 -0
- package/graph-provider/fuli_graph/store_project_agent_executors.py +2 -0
- package/graph-provider/fuli_graph/store_project_agent_task_activity.py +6 -0
- package/graph-provider/fuli_graph/store_project_agent_tasks.py +20 -8
- package/graph-provider/fuli_graph/store_task_contexts.py +9 -0
- package/graph-provider/fuli_graph/task_context_routes.py +7 -0
- package/graph-provider/pyproject.toml +1 -1
- package/npm-shrinkwrap.json +15 -15
- package/package.json +4 -3
- package/skills/capturing-session-knowledge/SKILL.md +20 -2
- package/skills/operating-fuli/SKILL.md +4 -0
- package/src/agent-tools/agent-collaboration-definitions.js +30 -0
- package/src/agent-tools/agent-conversation-definitions.js +21 -0
- package/src/agent-tools/employee-definitions.js +6 -5
- package/src/agent-tools/graph-definitions.js +19 -35
- package/src/agent-tools/graph-handlers.js +17 -1
- package/src/agent-tools/interface-catalog.js +2 -0
- package/src/agent-tools/project-agent-definitions.js +21 -0
- package/src/agent-tools/project-relation-definitions.js +36 -0
- package/src/agents/claude-code/conversation-transcript.js +20 -0
- package/src/agents/claude-code/lifecycle-hook.js +154 -0
- package/src/agents/claude-code/lifecycle-hooks.js +20 -0
- package/src/agents/codex/conversation-transcript.js +22 -0
- package/src/agents/codex/lifecycle-hook.js +47 -8
- package/src/agents/codex/lifecycle-hooks.js +6 -6
- package/src/agents/cursor/lifecycle-hook.js +5 -4
- package/src/app/agent-request-context.js +6 -1
- package/src/app/test-tools.js +26 -0
- package/src/conversations/sync-transcript.js +144 -0
- package/src/conversations/task-context-view.js +28 -0
- package/src/conversations/transcript-guard.js +54 -0
- package/src/conversations/transcript-reader.js +113 -0
- package/src/employees/service.js +8 -1
- package/src/graphiti/agent-collaboration.js +62 -0
- package/src/graphiti/agent-conversations.js +75 -0
- package/src/graphiti/agent-knowledge-workflows.js +8 -2
- package/src/graphiti/project-agent-control-plane.js +22 -2
- package/src/graphiti/project-agent-mapping.js +19 -1
- package/src/graphiti/project-agent-task-entry.js +11 -1
- package/src/graphiti/project-agent-workflows.js +11 -1
- package/src/graphiti/project-path-context.js +17 -10
- package/src/graphiti/provider-client.js +17 -0
- package/src/graphiti/verification-preference.js +24 -0
- package/src/http/graph-api-router.js +17 -0
- package/src/mcp/create-mcp-server.js +19 -5
- package/src/mcp/employee-tool-catalog.js +22 -0
- package/src/mcp/session-id.js +4 -0
- package/src/mcp/tool-annotations.js +8 -1
- package/src/mcp/tool-result.js +3 -1
- package/src/setup/agent-installation-status.js +1 -0
- package/src/setup/claude-code-config.js +13 -19
- package/dist/web/assets/AgentHand-BfLYPGjg.js +0 -1
- package/dist/web/assets/EmployeeRecruitDialog-D-W043v4.js +0 -1
- package/dist/web/assets/EmployeeWorkbenchPage-Cz7y-IFD.css +0 -1
- package/dist/web/assets/EmployeeWorkbenchPage-HHQPxdqI.js +0 -3
- package/dist/web/assets/KnowledgePage-Ck7tIBDn.js +0 -1
- package/dist/web/assets/PersonalProjectsPage-Bt7tFF5Z.js +0 -1
- package/dist/web/assets/ProjectAgentsPage-BioDR9OW.css +0 -1
- package/dist/web/assets/ProjectAgentsPage-CiiPNe_G.js +0 -12
- package/dist/web/assets/index-BUMq5zNh.js +0 -2
- package/dist/web/assets/index-C0ci8dnU.css +0 -1
- package/src/mcp/task-context-registry.js +0 -165
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { planAgentCollaboration, agentLoan, agentVerification } from './agent-collaboration.js';
|
|
2
|
+
import { queryConversations, resumeConversation, updateConversationPolicy } from './agent-conversations.js';
|
|
1
3
|
import { agentProjectResolution } from './agent-knowledge-workflows.js';
|
|
2
4
|
import { listAgentAttention, changeAgentAttention } from './agent-attention.js';
|
|
3
5
|
import {
|
|
@@ -54,6 +56,24 @@ export const projectAgentControlPlaneHooks = Object.freeze({
|
|
|
54
56
|
});
|
|
55
57
|
|
|
56
58
|
export class ProjectAgentControlPlaneApplication {
|
|
59
|
+
async agentVerification(operation, input) {
|
|
60
|
+
this.#assertSpace(input.personalSpaceId);
|
|
61
|
+
return agentVerification(this, operation, input);
|
|
62
|
+
}
|
|
63
|
+
async planAgentCollaboration(input) { return planAgentCollaboration(this, input); }
|
|
64
|
+
async agentLoan(operation, input) {
|
|
65
|
+
if (input.personalSpaceId) this.#assertSpace(input.personalSpaceId);
|
|
66
|
+
return agentLoan(this, operation, input);
|
|
67
|
+
}
|
|
68
|
+
async queryAgentConversations(input) {
|
|
69
|
+
this.#assertSpace(input.personalSpaceId);
|
|
70
|
+
return queryConversations(this, input);
|
|
71
|
+
}
|
|
72
|
+
async resumeAgentConversation(input) { return resumeConversation(this, input); }
|
|
73
|
+
async updateAgentConversationPolicy(input) {
|
|
74
|
+
this.#assertSpace(input.personalSpaceId);
|
|
75
|
+
return updateConversationPolicy(this, input);
|
|
76
|
+
}
|
|
57
77
|
async listAgentAttention(input) {
|
|
58
78
|
this.#assertSpace(input.personalSpaceId);
|
|
59
79
|
return listAgentAttention(this, input);
|
|
@@ -103,7 +123,7 @@ export class ProjectAgentControlPlaneApplication {
|
|
|
103
123
|
}
|
|
104
124
|
|
|
105
125
|
async listCurrentProjectAgents(input) {
|
|
106
|
-
const resolution = await this.#resolveProject(input.projectPath);
|
|
126
|
+
const resolution = await this.#resolveProject(input.projectPath, input.personalProjectId ?? null);
|
|
107
127
|
return listCurrentProjectAgentsWorkflow(
|
|
108
128
|
this,
|
|
109
129
|
agentProjectResolution(resolution),
|
|
@@ -152,7 +172,7 @@ export class ProjectAgentControlPlaneApplication {
|
|
|
152
172
|
}
|
|
153
173
|
|
|
154
174
|
async coordinateProjectAgentTask(input) {
|
|
155
|
-
const resolution = await this.#resolveProject(input.projectPath);
|
|
175
|
+
const resolution = await this.#resolveProject(input.projectPath, input.personalProjectId ?? null);
|
|
156
176
|
return coordinateProjectAgentTaskWorkflow(
|
|
157
177
|
this,
|
|
158
178
|
agentProjectResolution(resolution),
|
|
@@ -9,6 +9,8 @@ export function providerProjectAgentProfile(profile) {
|
|
|
9
9
|
initial_preferences: profile.initialPreferences ?? profile.initial_preferences ?? [],
|
|
10
10
|
status: profile.status ?? 'active'
|
|
11
11
|
};
|
|
12
|
+
copyOptional(result, 'character', profile.character, Object.hasOwn(profile, 'character'));
|
|
13
|
+
copyOptional(result, 'expectations', profile.expectations, Object.hasOwn(profile, 'expectations'));
|
|
12
14
|
copyOptional(result, 'occupation_emoji', profile.occupationEmoji !== undefined
|
|
13
15
|
? profile.occupationEmoji : profile.occupation_emoji,
|
|
14
16
|
hasAny(profile, ['occupationEmoji', 'occupation_emoji']));
|
|
@@ -55,6 +57,8 @@ export function projectAgentRecord(value) {
|
|
|
55
57
|
createdAt: value.created_at,
|
|
56
58
|
updatedAt: value.updated_at
|
|
57
59
|
};
|
|
60
|
+
copyOptional(result.profile, 'character', profile.character, Object.hasOwn(profile, 'character'));
|
|
61
|
+
copyOptional(result.profile, 'expectations', profile.expectations, Object.hasOwn(profile, 'expectations'));
|
|
58
62
|
copyOptional(
|
|
59
63
|
result.profile,
|
|
60
64
|
'occupationEmoji',
|
|
@@ -254,6 +258,9 @@ export function providerProjectAgentTaskSubmit(input) {
|
|
|
254
258
|
required_capabilities: input.requiredCapabilities ?? input.required_capabilities ?? [],
|
|
255
259
|
executor_capability_hints:
|
|
256
260
|
input.executorCapabilityHints ?? input.executor_capability_hints ?? [],
|
|
261
|
+
// Preserve an explicit opt-out so Provider validation rejects it instead
|
|
262
|
+
// of silently accepting a different contract from the caller's request.
|
|
263
|
+
verification_required: input.verificationRequired ?? input.verification_required ?? true,
|
|
257
264
|
duration: input.duration ?? 'ongoing',
|
|
258
265
|
staffing_intent: input.staffingIntent ?? input.staffing_intent ?? 'reuse_preferred',
|
|
259
266
|
lead_agent_id: input.leadAgentId ?? input.lead_agent_id ?? null,
|
|
@@ -333,6 +340,7 @@ export function projectAgentTokenUsage(value) {
|
|
|
333
340
|
|
|
334
341
|
export function providerProjectAgentTaskActivity(input) {
|
|
335
342
|
const result = withoutUndefined({
|
|
343
|
+
artifact_revision: input.artifactRevision ?? input.artifact_revision,
|
|
336
344
|
personal_space_id: input.personalSpaceId ?? input.personal_space_id,
|
|
337
345
|
personal_project_id: input.personalProjectId ?? input.personal_project_id,
|
|
338
346
|
task_id: input.taskId ?? input.task_id,
|
|
@@ -612,7 +620,7 @@ export function projectAgentRecruitmentRecord(value = {}) {
|
|
|
612
620
|
}
|
|
613
621
|
|
|
614
622
|
export function projectAgentProfileRecord(profile = {}) {
|
|
615
|
-
|
|
623
|
+
const result = {
|
|
616
624
|
name: profile.name,
|
|
617
625
|
responsibility: profile.responsibility,
|
|
618
626
|
occupationEmoji: profile.occupation_emoji !== undefined
|
|
@@ -628,6 +636,11 @@ export function projectAgentProfileRecord(profile = {}) {
|
|
|
628
636
|
cleanupEligible: profile.cleanup_eligible ?? false,
|
|
629
637
|
status: profile.status ?? 'active'
|
|
630
638
|
};
|
|
639
|
+
copyOptional(result, 'displayName', profile.display_name ?? profile.displayName,
|
|
640
|
+
hasAny(profile, ['display_name', 'displayName']));
|
|
641
|
+
copyOptional(result, 'character', profile.character, Object.hasOwn(profile, 'character'));
|
|
642
|
+
copyOptional(result, 'expectations', profile.expectations, Object.hasOwn(profile, 'expectations'));
|
|
643
|
+
return result;
|
|
631
644
|
}
|
|
632
645
|
|
|
633
646
|
export function projectAgentExecutionSummary(value) {
|
|
@@ -812,6 +825,8 @@ export function providerProjectAgentRecruitmentDecision(input) {
|
|
|
812
825
|
|
|
813
826
|
function providerExecutorModel(value = {}) {
|
|
814
827
|
return withoutUndefined({
|
|
828
|
+
capability_tier: value.capabilityTier ?? value.capability_tier,
|
|
829
|
+
cost_rank: value.costRank ?? value.cost_rank,
|
|
815
830
|
provider: value.provider,
|
|
816
831
|
model: value.model,
|
|
817
832
|
capabilities: value.capabilities ?? [],
|
|
@@ -826,6 +841,8 @@ function providerExecutorModel(value = {}) {
|
|
|
826
841
|
|
|
827
842
|
function executorModelRecord(value = {}) {
|
|
828
843
|
return withoutUndefined({
|
|
844
|
+
capabilityTier: value.capability_tier ?? value.capabilityTier,
|
|
845
|
+
costRank: value.cost_rank ?? value.costRank,
|
|
829
846
|
provider: value.provider,
|
|
830
847
|
model: value.model,
|
|
831
848
|
capabilities: value.capabilities ?? [],
|
|
@@ -1034,6 +1051,7 @@ export function executorPreflightRecord(value = {}) {
|
|
|
1034
1051
|
|
|
1035
1052
|
export function providerExecutorActualReport(input) {
|
|
1036
1053
|
return withoutUndefined({
|
|
1054
|
+
artifact_revision: input.artifactRevision ?? input.artifact_revision,
|
|
1037
1055
|
personal_space_id: input.personalSpaceId ?? input.personal_space_id,
|
|
1038
1056
|
personal_project_id: input.personalProjectId ?? input.personal_project_id,
|
|
1039
1057
|
task_id: input.taskId ?? input.task_id,
|
|
@@ -89,7 +89,13 @@ export async function loadProjectAgentContinuity(application, {
|
|
|
89
89
|
selection_reason: selectionReason,
|
|
90
90
|
match_basis: [...matchBasis],
|
|
91
91
|
role: { name: agent.profile.displayName || agent.profile.name, responsibility: agent.profile.responsibility,
|
|
92
|
-
initial_preferences: [...(agent.profile.initialPreferences ?? [])]
|
|
92
|
+
initial_preferences: [...(agent.profile.initialPreferences ?? [])],
|
|
93
|
+
character: {
|
|
94
|
+
judgment: boundedRoleText(agent.profile.character?.judgment, 2048),
|
|
95
|
+
taste: boundedRoleText(agent.profile.character?.taste, 2048),
|
|
96
|
+
personality: boundedRoleText(agent.profile.character?.personality, 2048)
|
|
97
|
+
},
|
|
98
|
+
expectations: boundedRoleText(agent.profile.expectations, 4096) },
|
|
93
99
|
project_brief: profile ? { name: profile.name, purpose: profile.purpose,
|
|
94
100
|
scope: profile.scope, technical_summary: profile.technical_summary,
|
|
95
101
|
boundaries: profile.boundaries ?? [] } : null,
|
|
@@ -108,6 +114,10 @@ export async function loadProjectAgentContinuity(application, {
|
|
|
108
114
|
};
|
|
109
115
|
}
|
|
110
116
|
|
|
117
|
+
function boundedRoleText(value, limit) {
|
|
118
|
+
return typeof value === 'string' ? value.slice(0, limit) : '';
|
|
119
|
+
}
|
|
120
|
+
|
|
111
121
|
function scopedItems(items, agentId) {
|
|
112
122
|
return (items ?? []).filter(item => !item.project_agent_id || item.project_agent_id === agentId)
|
|
113
123
|
.slice(0, 4).map(item => ({
|
|
@@ -178,6 +178,7 @@ export async function coordinateProjectAgentTask(
|
|
|
178
178
|
personalProjectId: projectId,
|
|
179
179
|
idempotencyKey: input.idempotencyKey,
|
|
180
180
|
title: input.title,
|
|
181
|
+
verificationRequired: true,
|
|
181
182
|
objective: input.objective,
|
|
182
183
|
workKind: input.workKind,
|
|
183
184
|
requiredCapabilities: input.requiredCapabilities,
|
|
@@ -213,7 +214,14 @@ export async function coordinateProjectAgentTask(
|
|
|
213
214
|
assignment_summary: participant.assignmentSummary ?? null,
|
|
214
215
|
workstream_boundary: boundaries[index] ?? null,
|
|
215
216
|
context_status: contexts[index]?.status ?? 'unavailable',
|
|
216
|
-
context:
|
|
217
|
+
context: {
|
|
218
|
+
...contexts[index],
|
|
219
|
+
lifecycle: {
|
|
220
|
+
task_checkpoint_owner: 'coordinator',
|
|
221
|
+
completion_tool: 'record_project_agent_task_activity',
|
|
222
|
+
instruction: 'Return the bounded result and evidence to the coordinator. Report only worker activity. Do not begin or checkpoint an inherited parent task context, or write its Agent memory. An independently created host task/session owns its own lifecycle.'
|
|
223
|
+
}
|
|
224
|
+
}
|
|
217
225
|
}));
|
|
218
226
|
const contextsReady = workerPlan.every(({ context_status: status }) => status === 'ready');
|
|
219
227
|
const hostExecutionRequired = route.task.status === 'queued' &&
|
|
@@ -248,6 +256,8 @@ export async function coordinateProjectAgentTask(
|
|
|
248
256
|
start_only_returned_workers: true,
|
|
249
257
|
report_worker_start_and_terminal_events: true,
|
|
250
258
|
report_actual_executor_only_after_real_use: true,
|
|
259
|
+
task_checkpoint_owner: 'coordinator',
|
|
260
|
+
forward_parent_task_context_token: false,
|
|
251
261
|
release_runtime_lease_in_finally: true
|
|
252
262
|
},
|
|
253
263
|
worker_plan: workerPlan,
|
|
@@ -47,23 +47,22 @@ export function resolvePersonalProjectPath(projectPath, projects, {
|
|
|
47
47
|
pathApi
|
|
48
48
|
});
|
|
49
49
|
if (repositoryRoot) {
|
|
50
|
-
const repositoryId = pathApi.basename(repositoryRoot);
|
|
51
|
-
if (projectIds.has(repositoryId)) {
|
|
52
|
-
return matched(repositoryId, 'repository_root');
|
|
53
|
-
}
|
|
54
50
|
const worktreeId = worktreeOriginProjectId(
|
|
55
51
|
pathApi.join(repositoryRoot, '.git'),
|
|
56
52
|
{ readText, pathApi }
|
|
57
53
|
);
|
|
58
|
-
if (worktreeId
|
|
59
|
-
|
|
54
|
+
if (worktreeId) {
|
|
55
|
+
// A worktree's folder name is arbitrary; only its original repository
|
|
56
|
+
// identifies the project, even when the folder names another project.
|
|
57
|
+
return resolveProjectId(worktreeId, projectIds, 'git_worktree_origin')
|
|
58
|
+
?? unresolved('unmatched');
|
|
60
59
|
}
|
|
60
|
+
const repository = resolveProjectId(pathApi.basename(repositoryRoot), projectIds, 'repository_root');
|
|
61
|
+
if (repository) return repository;
|
|
61
62
|
}
|
|
62
63
|
|
|
63
|
-
const
|
|
64
|
-
if (
|
|
65
|
-
return matched(directoryId, 'directory_name');
|
|
66
|
-
}
|
|
64
|
+
const directory = resolveProjectId(pathApi.basename(currentPath), projectIds, 'directory_name');
|
|
65
|
+
if (directory) return directory;
|
|
67
66
|
|
|
68
67
|
const childMatches = [...projectIds].filter((projectId) => (
|
|
69
68
|
CHILD_PROJECT_MARKERS.some((marker) =>
|
|
@@ -79,6 +78,14 @@ export function resolvePersonalProjectPath(projectPath, projects, {
|
|
|
79
78
|
return unresolved('unmatched');
|
|
80
79
|
}
|
|
81
80
|
|
|
81
|
+
function resolveProjectId(candidate, projectIds, basis) {
|
|
82
|
+
if (projectIds.has(candidate)) return matched(candidate, basis);
|
|
83
|
+
const matches = [...projectIds].filter((id) => id.toLowerCase() === candidate.toLowerCase());
|
|
84
|
+
if (matches.length === 1) return matched(matches[0], `${basis}_case_insensitive`);
|
|
85
|
+
if (matches.length > 1) return unresolved('ambiguous', { candidateCount: matches.length });
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
|
|
82
89
|
export function findRepositoryRoot(projectPath, {
|
|
83
90
|
fileExists = existsSync,
|
|
84
91
|
pathApi = path
|
|
@@ -58,6 +58,23 @@ export class GraphitiProviderClient {
|
|
|
58
58
|
this.requestTimeoutMs = requestTimeoutMs;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
agentVerification(operation, input) {
|
|
62
|
+
if (!['query', 'record'].includes(operation)) throw new TypeError('Unknown verification operation');
|
|
63
|
+
return this.#request(`/v1/agent-verification/${operation}`, { method: 'POST', body: input });
|
|
64
|
+
}
|
|
65
|
+
agentLoan(operation, input) {
|
|
66
|
+
if (!['query', 'request', 'decide'].includes(operation)) throw new TypeError('Unknown loan operation');
|
|
67
|
+
return this.#request(`/v1/agent-loans/${operation}`, { method: 'POST', body: input });
|
|
68
|
+
}
|
|
69
|
+
conversation(operation, input) {
|
|
70
|
+
if (!['query', 'append', 'resume', 'policy', 'boundary'].includes(operation)) throw new TypeError('Unknown conversation operation');
|
|
71
|
+
return this.#request(`/v1/agent-conversations/${operation}`, {
|
|
72
|
+
method: operation === 'policy' ? 'PUT' : 'POST', body: input
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
currentTaskContext(input) {
|
|
76
|
+
return this.#request(`/v1/task-context-sessions/current?${new URLSearchParams(input)}`);
|
|
77
|
+
}
|
|
61
78
|
health() { return this.#request('/health', { authenticated: false }); }
|
|
62
79
|
listSpaces() { return this.#request('/v1/spaces'); }
|
|
63
80
|
createSpace(input) { return this.#request('/v1/spaces', { method: 'POST', body: input }); }
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Preference selection never authorizes an executor. Existing user routing rules
|
|
2
|
+
// win; otherwise the default applies only to the current task.
|
|
3
|
+
export function verificationPreference({ executors, rules = [], projectId, lockedExecutorIds = null, now = Date.now() }) {
|
|
4
|
+
const eligible = executors.filter(executor => executor.registrationStatus === 'registered'
|
|
5
|
+
&& executor.permissionStatus === 'authorized' && executor.preflightStatus === 'passed'
|
|
6
|
+
&& executor.workspacePermission && executor.healthStatus !== 'unhealthy'
|
|
7
|
+
&& (!executor.healthRequired || executor.healthStatus === 'healthy')
|
|
8
|
+
&& executor.availableModels?.some(model => model.available)
|
|
9
|
+
&& (!lockedExecutorIds || lockedExecutorIds.includes(executor.executorId)));
|
|
10
|
+
const applicable = rules.filter(rule => rule.status === 'active' && rule.workKind === 'test_validation'
|
|
11
|
+
&& (rule.scope === 'global' || rule.scope === 'space' || rule.scope === 'project' && rule.personalProjectId === projectId))
|
|
12
|
+
.sort((a, b) => ({ project: 3, space: 2, global: 1 }[b.scope] - { project: 3, space: 2, global: 1 }[a.scope])
|
|
13
|
+
|| (a.priority ?? 100) - (b.priority ?? 100));
|
|
14
|
+
const preference = applicable[0];
|
|
15
|
+
const preferred = (preference?.executorIds ?? []).map(id => eligible.find(item => item.executorId === id)).find(Boolean);
|
|
16
|
+
eligible.sort((a, b) => (a.globalPriority ?? 100) - (b.globalPriority ?? 100) || a.executorId.localeCompare(b.executorId));
|
|
17
|
+
return { status: eligible.length ? 'available' : 'blocked', recommendedExecutorId: preferred?.executorId ?? eligible[0]?.executorId ?? null,
|
|
18
|
+
choiceSource: preferred ? 'user_rule' : 'task_default', scope: preferred ? preference.scope : 'task',
|
|
19
|
+
shouldAsk: !preference && eligible.length > 1,
|
|
20
|
+
expiresAt: new Date(now + 15000).toISOString(), timeoutSeconds: 15,
|
|
21
|
+
eligibleExecutors: eligible.map(item => ({ executorId: item.executorId, name: item.displayName })),
|
|
22
|
+
persistChoiceWith: 'upsert_executor_routing_rule', workKind: 'test_validation',
|
|
23
|
+
guidance: 'If shouldAsk, offer the optional choice once. If unanswered at expiresAt, use recommendedExecutorId for this task only. Ask project vs global scope only for a user-selected preference; silence creates no durable rule. Recheck availability before dispatch. Never grant permissions, authenticate or spend newly authorized money from a timeout.' };
|
|
24
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { readJson, sendJson } from './response.js';
|
|
2
|
+
import { testToolsEnabled } from '../app/test-tools.js';
|
|
2
3
|
import { handleAgentAttentionRequest } from './agent-attention-api-router.js';
|
|
3
4
|
|
|
4
5
|
export async function handleGraphApiRequest({
|
|
@@ -8,6 +9,14 @@ export async function handleGraphApiRequest({
|
|
|
8
9
|
app
|
|
9
10
|
}) {
|
|
10
11
|
if (await handleAgentAttentionRequest({ request, response, url, app })) return true;
|
|
12
|
+
if (url.pathname === '/api/agent-conversations/query' && request.method === 'POST') {
|
|
13
|
+
sendJson(response, 200, await app.queryAgentConversations({ ...(await readJson(request)), ownerInspection: true }));
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
if (url.pathname === '/api/agent-conversations/policy' && request.method === 'PUT') {
|
|
17
|
+
sendJson(response, 200, await app.updateAgentConversationPolicy({ ...(await readJson(request)), ownerInspection: true }));
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
11
20
|
if (url.pathname === '/api/state' && request.method === 'GET') {
|
|
12
21
|
sendJson(response, 200, await app.state());
|
|
13
22
|
return true;
|
|
@@ -224,6 +233,14 @@ export async function handleGraphApiRequest({
|
|
|
224
233
|
return true;
|
|
225
234
|
}
|
|
226
235
|
if (url.pathname === '/api/project-agents/test-cleanup' && request.method === 'POST') {
|
|
236
|
+
if (!testToolsEnabled()) {
|
|
237
|
+
sendJson(response, 404, {
|
|
238
|
+
error: 'Not found',
|
|
239
|
+
code: 'not_found',
|
|
240
|
+
detail: 'Test cleanup requires FULI_ENABLE_TEST_TOOLS=1'
|
|
241
|
+
});
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
227
244
|
sendJson(response, 200, await app.cleanupProjectAgentTestRoles({
|
|
228
245
|
personalSpaceId: url.searchParams.get('personalSpaceId'),
|
|
229
246
|
testSource: url.searchParams.get('testSource')
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { compactTaskContext } from '../conversations/task-context-view.js';
|
|
2
|
+
import { employeeToolCatalogResult } from './employee-tool-catalog.js';
|
|
1
3
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
4
|
import {
|
|
3
5
|
CallToolRequestSchema,
|
|
@@ -13,6 +15,7 @@ import { createCommonKnowledgePreviewTokens } from './common-knowledge-preview-t
|
|
|
13
15
|
import { auditLifecycleTool } from './lifecycle-audit.js';
|
|
14
16
|
import { registerFuliContextResources } from './context-resources.js';
|
|
15
17
|
import { annotationsFor } from './tool-annotations.js';
|
|
18
|
+
import { isTestOnlyToolName, testToolsEnabled } from '../app/test-tools.js';
|
|
16
19
|
import {
|
|
17
20
|
errorToolResult,
|
|
18
21
|
hookAdditionalContextToolResult,
|
|
@@ -28,6 +31,9 @@ import {
|
|
|
28
31
|
} from './session-id.js';
|
|
29
32
|
|
|
30
33
|
const TOOL_RESULT_LIMIT_BYTES = Object.freeze({
|
|
34
|
+
read_agent_conversation: 600 * 1024,
|
|
35
|
+
list_agent_conversations: 32 * 1024,
|
|
36
|
+
resume_agent_conversation: 24 * 1024,
|
|
31
37
|
list_project_agent_tasks: 128 * 1024,
|
|
32
38
|
list_preference_conflicts: 128 * 1024,
|
|
33
39
|
list_agent_interfaces: 64 * 1024,
|
|
@@ -107,7 +113,8 @@ export function createMcpServer(
|
|
|
107
113
|
authoritativeSourceApplication,
|
|
108
114
|
withRuntimeLease,
|
|
109
115
|
toolNames,
|
|
110
|
-
prepareToolInput
|
|
116
|
+
prepareToolInput,
|
|
117
|
+
env
|
|
111
118
|
);
|
|
112
119
|
for (const tool of tools.values()) registerTool(server, tool);
|
|
113
120
|
if (!tools.size) registerEmptyToolList(server);
|
|
@@ -130,7 +137,8 @@ function createToolMap(
|
|
|
130
137
|
sourceApplication,
|
|
131
138
|
withRuntimeLease,
|
|
132
139
|
toolNames,
|
|
133
|
-
prepareToolInput
|
|
140
|
+
prepareToolInput,
|
|
141
|
+
env = process.env
|
|
134
142
|
) {
|
|
135
143
|
const commonKnowledgePreviews = createCommonKnowledgePreviewTokens();
|
|
136
144
|
if (toolNames !== null && !Array.isArray(toolNames)) {
|
|
@@ -147,8 +155,10 @@ function createToolMap(
|
|
|
147
155
|
);
|
|
148
156
|
}
|
|
149
157
|
}
|
|
158
|
+
const includeTestTools = testToolsEnabled(env);
|
|
150
159
|
return new Map(definitions
|
|
151
160
|
.filter((definition) => !allowedTools || allowedTools.has(definition.name))
|
|
161
|
+
.filter((definition) => includeTestTools || !isTestOnlyToolName(definition.name))
|
|
152
162
|
.map((definition) => [definition.name, {
|
|
153
163
|
definition,
|
|
154
164
|
schema: jsonSchemaToZod(definition.inputSchema),
|
|
@@ -169,7 +179,7 @@ function createToolMap(
|
|
|
169
179
|
),
|
|
170
180
|
projectActionPreviews,
|
|
171
181
|
commonKnowledgePreviews,
|
|
172
|
-
requestContext
|
|
182
|
+
{ ...requestContext, testToolsEnabled: includeTestTools }
|
|
173
183
|
),
|
|
174
184
|
requestContext
|
|
175
185
|
);
|
|
@@ -216,8 +226,11 @@ async function invokeTool(tool, input, requestContext = null) {
|
|
|
216
226
|
const value = await tool.invoke(input, requestContext);
|
|
217
227
|
auditLifecycleTool(tool.definition.name);
|
|
218
228
|
const limitBytes = TOOL_RESULT_LIMIT_BYTES[tool.definition.name];
|
|
229
|
+
if (tool.definition.name === 'list_employee_tools') {
|
|
230
|
+
return employeeToolCatalogResult(value, { limitBytes });
|
|
231
|
+
}
|
|
219
232
|
if (tool.definition.name === 'begin_task_context') {
|
|
220
|
-
return hookAdditionalContextToolResult(value, {
|
|
233
|
+
return hookAdditionalContextToolResult(compactTaskContext(value), {
|
|
221
234
|
hookEventName: 'UserPromptSubmit',
|
|
222
235
|
label: 'Fuli task context. Apply effective_preferences and use taskContextToken for the final checkpoint.',
|
|
223
236
|
limitBytes,
|
|
@@ -227,7 +240,8 @@ async function invokeTool(tool, input, requestContext = null) {
|
|
|
227
240
|
const itemLimit = ({ list_agent_interfaces: 200, list_project_agent_tasks: 200,
|
|
228
241
|
list_preference_conflicts: 1000, list_external_knowledge_bindings: 200,
|
|
229
242
|
get_collaboration_preferences: 1000 })[tool.definition.name];
|
|
230
|
-
return successToolResult(
|
|
243
|
+
return successToolResult(tool.definition.name === 'get_collaboration_preferences'
|
|
244
|
+
? compactTaskContext(value) : value, { limitBytes, itemLimit });
|
|
231
245
|
} catch (error) {
|
|
232
246
|
return errorToolResult(error);
|
|
233
247
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ApplicationError } from '../app/application-error.js';
|
|
2
|
+
|
|
3
|
+
// Schemas are executable contracts. Summary truncation can change their types,
|
|
4
|
+
// required fields or enum values, so return a complete bounded catalog or fail.
|
|
5
|
+
export function employeeToolCatalogResult(value, { limitBytes = 64 * 1024 } = {}) {
|
|
6
|
+
const catalog = pick(value, ['personalSpaceId', 'agentId', 'templateId', 'name',
|
|
7
|
+
'role', 'runtimeStatus', 'basePath', 'workbenchUrl', 'agentCardUrl']);
|
|
8
|
+
if (value.project) catalog.project = pick(value.project, ['id', 'name', 'description']);
|
|
9
|
+
catalog.tools = (value.tools ?? []).map(tool => pick(tool,
|
|
10
|
+
['name', 'title', 'description', 'permission', 'inputSchema']));
|
|
11
|
+
const json = JSON.stringify(catalog);
|
|
12
|
+
if (Buffer.byteLength(json, 'utf8') > limitBytes) {
|
|
13
|
+
throw new ApplicationError('tool_catalog_too_large',
|
|
14
|
+
'Employee tool catalog is too large. Use includeSchemas=false to list names, then toolName to read one complete schema.');
|
|
15
|
+
}
|
|
16
|
+
return { content: [{ type: 'text', text: json }], structuredContent: JSON.parse(json) };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function pick(value, keys) {
|
|
20
|
+
return Object.fromEntries(keys.filter(key => value[key] !== undefined)
|
|
21
|
+
.map(key => [key, value[key]]));
|
|
22
|
+
}
|
package/src/mcp/session-id.js
CHANGED
|
@@ -3,6 +3,10 @@ import { randomUUID } from 'node:crypto';
|
|
|
3
3
|
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
4
4
|
const WORKFLOW_OBSERVATION_TOOL = 'record_workflow_transition_observation';
|
|
5
5
|
const PROJECT_AGENT_SOURCE_TOOLS = new Set([
|
|
6
|
+
'get_agent_quality_gate', 'record_agent_verification',
|
|
7
|
+
'plan_agent_collaboration', 'request_agent_loan', 'decide_agent_loan', 'list_agent_loans',
|
|
8
|
+
'list_agent_conversations', 'read_agent_conversation', 'resume_agent_conversation',
|
|
9
|
+
'get_agent_conversation_policy', 'update_agent_conversation_policy',
|
|
6
10
|
'call_employee_tool',
|
|
7
11
|
'begin_task_context',
|
|
8
12
|
'verify_task_checkpoint',
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
const READ_TOOLS = new Set([
|
|
2
|
+
'get_agent_quality_gate',
|
|
3
|
+
'plan_agent_collaboration', 'list_agent_loans',
|
|
4
|
+
'list_agent_conversations', 'read_agent_conversation', 'get_agent_conversation_policy',
|
|
2
5
|
'list_agent_attention',
|
|
3
6
|
'list_project_agent_tasks',
|
|
4
7
|
'list_preference_conflicts',
|
|
@@ -46,6 +49,9 @@ const READ_TOOLS = new Set([
|
|
|
46
49
|
]);
|
|
47
50
|
|
|
48
51
|
const WRITE_TOOLS = new Set([
|
|
52
|
+
'record_agent_verification',
|
|
53
|
+
'request_agent_loan', 'decide_agent_loan',
|
|
54
|
+
'resume_agent_conversation', 'update_agent_conversation_policy',
|
|
49
55
|
'request_agent_attention',
|
|
50
56
|
'cancel_agent_attention',
|
|
51
57
|
'update_capture_policy',
|
|
@@ -128,7 +134,8 @@ const DESTRUCTIVE_TOOLS = new Set([
|
|
|
128
134
|
'review_project_proposal',
|
|
129
135
|
'review_project_relation',
|
|
130
136
|
'review_personal_draft',
|
|
131
|
-
'unsubscribe_public_project'
|
|
137
|
+
'unsubscribe_public_project',
|
|
138
|
+
'cleanup_test_project_agents'
|
|
132
139
|
]);
|
|
133
140
|
|
|
134
141
|
export function annotationsFor(name) {
|
package/src/mcp/tool-result.js
CHANGED
|
@@ -178,7 +178,9 @@ function projectString(value, budget) {
|
|
|
178
178
|
function projectArray(value, budget, depth) {
|
|
179
179
|
const projected = [];
|
|
180
180
|
let used = 2;
|
|
181
|
-
|
|
181
|
+
// sanitize already applied the caller's item limit. A second default limit
|
|
182
|
+
// here silently drops catalog entries and preferences when bytes are capped.
|
|
183
|
+
for (const item of value) {
|
|
182
184
|
const separatorBytes = projected.length ? 1 : 0;
|
|
183
185
|
const child = projectValue(item, budget - used - separatorBytes, depth + 1);
|
|
184
186
|
if (child === undefined) continue;
|
|
@@ -39,6 +39,7 @@ export function inspectAgentInstallations(agents, context, {
|
|
|
39
39
|
);
|
|
40
40
|
const claudeLifecycleCurrent = agent.id !== 'claude-code' ||
|
|
41
41
|
hasCurrentClaudeCodeHooks(readJson(agent.settingsPath), {
|
|
42
|
+
...context,
|
|
42
43
|
hookTimeoutSec: context.hookTimeoutSec ?? 30
|
|
43
44
|
});
|
|
44
45
|
const codexConfig = agent.id === 'codex' && agent.hooksPath
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { isDeepStrictEqual } from 'node:util';
|
|
3
|
+
import { claudeLifecycleCommand, isClaudeLifecycleCommand } from '../agents/claude-code/lifecycle-hooks.js';
|
|
3
4
|
|
|
4
5
|
import {
|
|
5
6
|
readJsonFile,
|
|
@@ -48,9 +49,7 @@ export function connectClaudeCode(agent, context, {
|
|
|
48
49
|
}
|
|
49
50
|
}
|
|
50
51
|
};
|
|
51
|
-
const nextSettings = withManagedHooks(settings,
|
|
52
|
-
hookTimeoutSec: context.hookTimeoutSec
|
|
53
|
-
});
|
|
52
|
+
const nextSettings = withManagedHooks(settings, context);
|
|
54
53
|
const registrationChanged = !sameJson(current, next);
|
|
55
54
|
const hooksChanged = !sameJson(settings, nextSettings);
|
|
56
55
|
if (registrationChanged) writeConfig(agent.configPath, next);
|
|
@@ -104,13 +103,13 @@ export function disconnectClaudeCode(agent, {
|
|
|
104
103
|
};
|
|
105
104
|
}
|
|
106
105
|
|
|
107
|
-
export function hasCurrentClaudeCodeHooks(settings,
|
|
106
|
+
export function hasCurrentClaudeCodeHooks(settings, context = {}) {
|
|
108
107
|
try {
|
|
109
108
|
assertObject(settings, 'Claude Code settings');
|
|
110
109
|
assertHookSettings(settings.hooks ?? {});
|
|
111
110
|
assertPermissionSettings(settings.permissions ?? {});
|
|
112
111
|
return sameJson(
|
|
113
|
-
withManagedHooks(settings,
|
|
112
|
+
withManagedHooks(settings, context),
|
|
114
113
|
settings
|
|
115
114
|
);
|
|
116
115
|
} catch {
|
|
@@ -118,23 +117,20 @@ export function hasCurrentClaudeCodeHooks(settings, { hookTimeoutSec = 30 } = {}
|
|
|
118
117
|
}
|
|
119
118
|
}
|
|
120
119
|
|
|
121
|
-
function withManagedHooks(settings,
|
|
120
|
+
function withManagedHooks(settings, context) {
|
|
121
|
+
const { hookTimeoutSec = 30 } = context;
|
|
122
122
|
const normalizedHookTimeoutSec = positiveHookTimeout(hookTimeoutSec);
|
|
123
123
|
const clean = withoutManagedSettings(settings);
|
|
124
124
|
const hooks = { ...(clean.hooks ?? {}) };
|
|
125
125
|
hooks.UserPromptSubmit = [
|
|
126
126
|
...(hooks.UserPromptSubmit ?? []),
|
|
127
|
-
managedHookGroup('
|
|
128
|
-
sessionId: '${session_id}',
|
|
129
|
-
projectPath: '${cwd}',
|
|
130
|
-
taskPrompt: '${prompt}'
|
|
131
|
-
}, 'Loading Fuli task context', normalizedHookTimeoutSec)
|
|
127
|
+
managedHookGroup(context, 'UserPromptSubmit', 'Loading Fuli task context', normalizedHookTimeoutSec)
|
|
132
128
|
];
|
|
133
129
|
hooks.Stop = [
|
|
134
130
|
...(hooks.Stop ?? []),
|
|
135
131
|
managedHookGroup(
|
|
136
|
-
|
|
137
|
-
|
|
132
|
+
context,
|
|
133
|
+
'Stop',
|
|
138
134
|
'Checking Fuli task checkpoint',
|
|
139
135
|
normalizedHookTimeoutSec
|
|
140
136
|
)
|
|
@@ -186,13 +182,11 @@ function withoutManagedHooks(settings) {
|
|
|
186
182
|
return { ...settings, hooks };
|
|
187
183
|
}
|
|
188
184
|
|
|
189
|
-
function managedHookGroup(
|
|
185
|
+
function managedHookGroup(context, event, statusMessage, timeout) {
|
|
190
186
|
return {
|
|
191
187
|
hooks: [{
|
|
192
|
-
type: '
|
|
193
|
-
|
|
194
|
-
tool,
|
|
195
|
-
input,
|
|
188
|
+
type: 'command',
|
|
189
|
+
command: claudeLifecycleCommand(context, event, timeout),
|
|
196
190
|
timeout,
|
|
197
191
|
statusMessage
|
|
198
192
|
}]
|
|
@@ -207,7 +201,7 @@ function positiveHookTimeout(value) {
|
|
|
207
201
|
}
|
|
208
202
|
|
|
209
203
|
function isManagedHook(hook, tool = null) {
|
|
210
|
-
return hook?.type === 'mcp_tool'
|
|
204
|
+
return isClaudeLifecycleCommand(hook) || hook?.type === 'mcp_tool'
|
|
211
205
|
&& hook.server === FULI_SERVER
|
|
212
206
|
&& MANAGED_HOOK_TOOLS.has(hook.tool)
|
|
213
207
|
&& (tool === null || hook.tool === tool);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{L as e,N as t,T as n,V as r,Z as i,_ as a,a as o,at as s,b as c,nt as l,v as u,y as d}from"./i18n-CVFADb_M.js";import{r as f}from"./console-BAIVNC-N.js";import{t as p}from"./_plugin-vue_export-helper-BDNMzG2s.js";import{i as m,n as h}from"./client-Bl4oiW3E.js";function g(e){let t=/^employee\.([a-z][a-z0-9-]{0,63})$/.exec(e.agentId)?.[1];return t&&e.profile.capabilities.includes(`fuli.employee:${t}`)?t:null}var _=i([]),v=i(!1),y=i(``),b=``,x=null,S=0;async function C(e){if(!e)return[];let t=await h(`/api/employee-templates?${new URLSearchParams({personalSpaceId:e})}`);return Array.isArray(t.templates)?t.templates:[]}function w(e){if(e===b&&x)return x;let t=++S;return e!==b&&(_.value=[]),b=e,y.value=``,e?(v.value=!0,x=C(e).then(e=>{t===S&&(_.value=e)}).catch(e=>{t===S&&(_.value=[],y.value=T(e))}).finally(()=>{t===S&&(v.value=!1,x=null)}),x):(_.value=[],v.value=!1,Promise.resolve())}function T(e){let t=e instanceof Error?e.message:``;try{let e=JSON.parse(t);return e.code&&[`runtime_unavailable`,`assignment_required`,`project_not_found`,`identity_conflict`,`reactivation_required`,`assignment_scope_conflict`,`assignment_update_incomplete`].includes(e.code)?o(`employees.errors.${e.code}`):e.error||o(`employees.loadError`)}catch{return t||o(`employees.loadError`)}}var E={jefa:`/assets/jefa-avatar-Dew42td5.webp`,bole:`/assets/bole-avatar-CVIr-bU_.webp`};function D(e){return E[e]}var O=f(`agent-attention`,()=>{let e=i(``),t=i({}),n=i(0),r=i([]),a=i(0),o=i(``),s=i(!1),c=i(!1),l=i(``),u=0;async function d(){let i=++u;if(e.value){c.value=!0;try{let c=new URLSearchParams({personalSpaceId:e.value,status:`open`}),d=await h(`/api/agent-attention?${c}&limit=1`);if(i!==u)return;if(t.value=d.counts,n.value=d.total,s.value){o.value&&c.set(`agentId`,o.value);let e=await h(`/api/agent-attention?${c}&limit=100`);if(i!==u)return;r.value=e.items,a.value=e.total}l.value=``}catch(e){i===u&&(l.value=e instanceof Error?e.message:String(e))}finally{i===u&&(c.value=!1)}}}function f(i){e.value!==i&&(u++,e.value=i,t.value={},r.value=[],n.value=0,a.value=0,l.value=``,s.value=!1,c.value=!1,o.value=``,i&&d())}function p(e=``){o.value=e,r.value=[],a.value=0,s.value=!0,d()}async function g(){if(c.value)return;let t=++u;c.value=!0;try{let n=new URLSearchParams({personalSpaceId:e.value,status:`open`,limit:`100`,offset:String(r.value.length)});o.value&&n.set(`agentId`,o.value);let i=await h(`/api/agent-attention?${n}`);if(t!==u)return;let s=new Set(r.value.map(e=>e.requestId));r.value.push(...i.items.filter(e=>!s.has(e.requestId))),a.value=i.total}catch(e){t===u&&(l.value=String(e))}finally{t===u&&(c.value=!1)}}async function _(t,n){let r=e.value;if(!r||t.personalSpaceId!==r)throw Error(`This request belongs to a different space. Reopen it before replying.`);await m(`/api/agent-attention/respond`,{personalSpaceId:r,personalProjectId:t.personalProjectId,requestId:t.requestId,expectedRevision:t.revision,response:n}),e.value===r&&await d()}return{spaceId:e,counts:t,total:n,items:r,filteredTotal:a,agentId:o,open:s,loading:c,error:l,setSpace:f,show:p,refresh:d,more:g,respond:_}});function k(e){let t=new URLSearchParams({agent:e.agentId,project:e.personalProjectId});return e.taskId&&t.set(`task`,e.taskId),`/project-agents?${t}${e.taskId?`#task-${encodeURIComponent(e.taskId)}`:``}`}var A={"aria-hidden":`true`},j=p(n({__name:`AgentHand`,props:{agentId:{},passive:{type:Boolean}},setup(n){let i=n,f=O(),p=a(()=>i.agentId?f.counts[i.agentId]??0:f.total);function m(e){i.passive||(e.preventDefault(),e.stopPropagation(),f.show(i.agentId))}return(i,a)=>p.value?(t(),d(e(n.passive?`span`:`button`),{key:0,type:n.passive?void 0:`button`,class:`agent-hand`,"aria-label":l(o)(`attention.count`,{count:p.value}),title:l(o)(`attention.count`,{count:p.value}),onClick:m},{default:r(()=>[a[0]||=u(`svg`,{viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`1.7`,"stroke-linecap":`round`,"stroke-linejoin":`round`,"aria-hidden":`true`},[u(`path`,{d:`M8 13V5a1.5 1.5 0 0 1 3 0v6-8a1.5 1.5 0 0 1 3 0v8-6a1.5 1.5 0 0 1 3 0v7-3a1.5 1.5 0 0 1 3 0v6c0 4-2 7-6 7h-1c-3 0-4-1-6-4l-3-4a1.6 1.6 0 0 1 2.5-2z`})],-1),u(`span`,A,s(p.value),1)]),_:1},8,[`type`,`aria-label`,`title`])):c(``,!0)}}),[[`__scopeId`,`data-v-0ed1321f`]]);export{y as a,g as c,w as d,D as i,_ as l,k as n,v as o,O as r,T as s,j as t,C as u};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{C as e,F as t,N as n,T as r,V as i,Z as a,_ as o,a as s,at as c,b as l,h as u,m as ee,nt as d,rt as te,v as f,w as p,x as m,y as h,z as g}from"./i18n-CVFADb_M.js";import{t as _}from"./GrowthLoading-WXCj8b0r.js";import{t as v}from"./_plugin-vue_export-helper-BDNMzG2s.js";import{t as y}from"./vue-router-C8KuEZ6a.js";import{i as ne}from"./client-Bl4oiW3E.js";import{d as re,i as b,l as ie,s as ae,u as oe}from"./AgentHand-BfLYPGjg.js";import{t as se}from"./useModalDialog-Ddv2XJQi.js";import{n as ce}from"./paths-CDREouhi.js";import{t as x}from"./SearchableSelect-214TvfYg.js";import{t as le}from"./ProjectScopePicker-Cu5PdxF6.js";var ue={class:`employee-recruit-heading`},de={id:`employee-recruit-title`},fe=[`disabled`],pe={key:1,role:`alert`,class:`employee-message`},me={key:2},he={class:`employee-recruit-body`},ge={key:0,class:`employee-picker-field`},_e={class:`employee-profile`},ve={class:`employee-avatar`,"aria-hidden":`true`},ye=[`src`],be={class:`employee-specialties`},xe=[`aria-label`],Se=[`data-scope`,`aria-checked`,`tabindex`,`disabled`,`onClick`],Ce={key:3,class:`employee-scope-rule`,role:`status`},we={key:4,class:`employee-muted`},Te={key:5,class:`employee-muted`},Ee={key:6,class:`employee-muted`},De=[`aria-label`],Oe={class:`employee-title-controls`},ke={class:`employee-picker-field`},Ae={class:`employee-picker-field`},je={class:`employee-title-preview`},Me={class:`employee-muted`},Ne=[`aria-label`],Pe={key:9,class:`employee-error`,role:`alert`},Fe=[`disabled`],Ie={key:11,class:`employee-success`,role:`status`},Le={class:`employee-recruit-actions`},Re=[`disabled`],ze=[`disabled`],S=v(r({__name:`EmployeeRecruitDialog`,props:{open:{type:Boolean},personalSpaceId:{},projects:{},defaultProjectId:{},defaultProjectIds:{},templateId:{}},emits:[`close`,`recruited`],setup(r,{emit:v}){let S=r,C=v,w=a([]),T=a([]),E=a(`selected`),D=a([]),O=a(`auto`),k=a(`emoji`),A=a(``),j=a(``),M=a(!1),N=a(!1),P=a(``),F=a(!1),I=a(``),L=a(null),R=a([]),z=a(``),{dialogRef:Be,initialFocusRef:Ve,onCancel:He,onKeydown:Ue}=se(()=>S.open,Q),B=o(()=>R.value),V=o(()=>S.templateId?B.value.find(e=>e.id===S.templateId):B.value.find(e=>e.id===P.value)??B.value[0]),We=o(()=>V.value?.agentId?s(`employees.manageProjectsTitle`,{name:V.value.name}):s(`employees.recruit`)),H=o(()=>S.projects.filter(e=>e.profile.lifecycle!==`archived`).map(e=>({id:e.project_id,name:e.profile.name}))),U=o(()=>V.value?.management!==void 0),W=o({get:()=>E.value===`all`?H.value.filter(e=>!D.value.includes(e.id)).map(e=>e.id):w.value,set:e=>{if(E.value===`all`){let t=new Set(H.value.map(e=>e.id));D.value=[...D.value.filter(e=>!t.has(e)),...H.value.filter(t=>!e.includes(t.id)).map(e=>e.id)]}else w.value=e}}),G=o(()=>({mode:E.value,projectIds:E.value===`selected`?[...w.value].sort():[],excludedProjectIds:E.value===`all`?[...D.value].sort():[],titleMode:O.value,titleStyle:k.value})),Ge=o(()=>[`auto`,`suggest`,`off`].map(e=>({value:e,label:s(`employees.titles.${e}`)}))),Ke=o(()=>[`emoji`,`text`].map(e=>({value:e,label:s(`employees.titles.${e}`)}))),K=o(()=>new Set(T.value)),qe=o(()=>[...K.value].filter(e=>!W.value.includes(e))),q=o(()=>!!(V.value?.agentStatus&&V.value.agentStatus!==`active`)),J=o(()=>V.value?.agentStatus===`active`&&(U.value?JSON.stringify(G.value)===A.value&&(E.value===`all`||w.value.length===K.value.size&&w.value.every(e=>K.value.has(e))):w.value.length===K.value.size&&w.value.every(e=>K.value.has(e)))),Je=o(()=>F.value?s(`employees.recruiting`):q.value?s(`employees.rehire`,{name:V.value?.name??``}):J.value||V.value?.agentId?s(`employees.assign`):s(`employees.recruitName`,{name:V.value?.name??``})),Ye=o(()=>V.value&&W.value.length?`/employees/${encodeURIComponent(V.value.id)}?project=${encodeURIComponent(W.value[0])}`:``),Y=0;g([()=>S.open,()=>S.personalSpaceId,()=>S.templateId],([e])=>{Y+=1,e&&Z(!0)},{immediate:!0}),g(P,()=>{S.open&&!M.value&&X(!0)}),g(G,()=>{I.value=N.value?I.value:``,L.value=null},{flush:`sync`});function X(e=!1){let t=new Set(H.value.map(e=>e.id));T.value=[...new Set((V.value?.assignments??[]).filter(e=>e.status===`active`&&t.has(e.personalProjectId)).map(e=>e.personalProjectId))];let n=e&&!V.value?.agentId?S.defaultProjectIds??(S.defaultProjectId?[S.defaultProjectId]:[]):[];w.value=[...new Set([...T.value,...n.filter(e=>t.has(e))])];let r=V.value?.management;E.value=r?.mode??`selected`,D.value=[...r?.excludedProjectIds??[]],O.value=r?.titleMode??`auto`,k.value=r?.titleStyle??`emoji`,r?.mode===`selected`&&V.value?.agentId&&(w.value=r.projectIds.filter(e=>t.has(e))),r?.mode===`all`&&V.value?.agentId&&(T.value=[...W.value]),A.value=JSON.stringify(G.value),j.value=V.value?.assignmentsVersion??``,N.value=!1,I.value=``,L.value=null}async function Z(e=!1){let t=++Y;M.value=!0,R.value=[],z.value=``;try{let n=await oe(S.personalSpaceId);if(t!==Y||!S.open)return;R.value=n,X(e)}catch(e){t===Y&&S.open&&(R.value=[],z.value=ae(e))}finally{t===Y&&(M.value=!1)}}function Q(){F.value||C(`close`)}async function Xe(){if(!V.value||V.value.identityConflict||!S.personalSpaceId||F.value||M.value||N.value||!j.value||J.value)return;F.value=!0,I.value=``;let e=V.value.id;try{let t=await ne(`/api/employee-templates/${encodeURIComponent(e)}/recruit`,{personalSpaceId:S.personalSpaceId,...U.value?{management:G.value}:{personalProjectIds:[...w.value]},replaceAssignments:!0,expectedAssignmentsVersion:j.value,...q.value?{reactivate:!0}:{}});await re(S.personalSpaceId),R.value=[...ie.value],X(),L.value=t,C(`recruited`,t)}catch(e){I.value=ae(e);try{let t=JSON.parse(e instanceof Error?e.message:``);N.value=t.code===`assignment_scope_conflict`||t.code===`assignment_update_incomplete`}catch{}}finally{F.value=!1}}function $(e){e===`selected`&&(w.value=[...W.value]),E.value=e}function Ze(e){if(!(F.value||M.value||N.value)&&[`ArrowLeft`,`ArrowRight`,`Home`,`End`].includes(e.key)){e.preventDefault();let t=e.key===`Home`?`all`:e.key===`End`||E.value===`all`?`selected`:`all`;$(t),e.currentTarget.querySelector(`[data-scope="${t}"]`)?.focus()}}return(a,o)=>r.open?(n(),m(`dialog`,{key:0,ref_key:`dialogRef`,ref:Be,class:`employee-recruit-dialog`,"aria-modal":`true`,"aria-labelledby":`employee-recruit-title`,onCancel:o[6]||=(...e)=>d(He)&&d(He)(...e),onKeydown:o[7]||=(...e)=>d(Ue)&&d(Ue)(...e)},[f(`header`,ue,[f(`h2`,de,c(We.value),1),f(`button`,{ref_key:`initialFocusRef`,ref:Ve,type:`button`,class:`quiet-button`,disabled:F.value,onClick:Q},c(d(s)(`employees.close`)),9,fe)]),M.value&&!B.value.length?(n(),h(_,{key:0,variant:`compact`,label:d(s)(`employees.loading`)},null,8,[`label`])):z.value?(n(),m(`div`,pe,[f(`p`,null,c(z.value),1),f(`button`,{class:`quiet-button`,type:`button`,onClick:o[0]||=e=>Z(!0)},c(d(s)(`employees.retry`)),1)])):V.value?(n(),m(`form`,{key:3,onSubmit:ee(Xe,[`prevent`])},[f(`div`,he,[B.value.length>1&&!S.templateId?(n(),m(`div`,ge,[f(`span`,null,c(d(s)(`employees.choose`)),1),p(x,{modelValue:P.value,"onUpdate:modelValue":o[1]||=e=>P.value=e,"control-id":`employee-template`,label:d(s)(`employees.choose`),options:B.value.map(e=>({value:e.id,label:e.name,meta:e.role})),disabled:F.value||M.value},null,8,[`modelValue`,`label`,`options`,`disabled`])])):l(``,!0),f(`div`,_e,[f(`span`,ve,[d(b)(V.value.id)?(n(),m(`img`,{key:0,src:d(b)(V.value.id),alt:``},null,8,ye)):(n(),m(u,{key:1},[e(c(V.value.name.slice(0,1)),1)],64))]),f(`div`,null,[f(`h3`,null,[e(c(V.value.name)+` `,1),f(`span`,null,c(V.value.role),1)]),f(`p`,null,c(V.value.description),1)])]),f(`p`,be,c(V.value.capabilities.join(` · `)),1),M.value?(n(),h(_,{key:1,variant:`compact`,label:d(s)(`employees.loadingScope`)},null,8,[`label`])):l(``,!0),U.value?(n(),m(`div`,{key:2,class:`employee-scope-mode`,role:`radiogroup`,"aria-label":d(s)(`employees.scope.rule`),onKeydown:Ze},[(n(),m(u,null,t([`all`,`selected`],e=>f(`button`,{key:e,type:`button`,role:`radio`,"data-scope":e,"aria-checked":E.value===e,tabindex:E.value===e?0:-1,disabled:F.value||M.value||N.value,onClick:t=>$(e)},c(d(s)(`employees.scope.${e===`all`?`continuousAll`:`onlySelected`}`)),9,Se)),64))],40,xe)):l(``,!0),U.value?(n(),m(`p`,Ce,c(E.value===`all`?d(s)(`employees.scope.allHint`,{name:V.value.name}):d(s)(`employees.scope.selectedHint`)),1)):l(``,!0),p(le,{modelValue:W.value,"onUpdate:modelValue":o[2]||=e=>W.value=e,projects:H.value,inline:``,hint:E.value===`all`?d(s)(`employees.scope.excludeHint`):void 0,disabled:F.value||M.value||N.value},null,8,[`modelValue`,`projects`,`hint`,`disabled`]),E.value===`all`&&D.value.length?(n(),m(`p`,we,c(d(s)(`employees.scope.excludedCount`,{count:D.value.length})),1)):l(``,!0),H.value.length?l(``,!0):(n(),m(`p`,Te,[e(c(d(s)(`employees.noProjects`))+` `,1),p(d(y),{to:d(ce)(r.personalSpaceId,`directory`),onClick:Q},{default:i(()=>[e(c(d(s)(`employees.createProject`)),1)]),_:1},8,[`to`])])),qe.value.length&&!L.value?(n(),m(`p`,Ee,c(d(s)(`employees.scopeRemoved`,{count:qe.value.length})),1)):l(``,!0),U.value&&V.value.permissions.includes(`session.title`)?(n(),m(`section`,{key:7,class:`employee-title-settings`,"aria-label":d(s)(`employees.titles.heading`)},[f(`h3`,null,c(d(s)(`employees.titles.heading`)),1),f(`div`,Oe,[f(`div`,ke,[f(`span`,null,c(d(s)(`employees.titles.mode`)),1),p(x,{modelValue:O.value,"onUpdate:modelValue":o[3]||=e=>O.value=e,"control-id":`employee-title-mode`,label:d(s)(`employees.titles.mode`),options:Ge.value,disabled:F.value||M.value||N.value},null,8,[`modelValue`,`label`,`options`,`disabled`])]),f(`div`,Ae,[f(`span`,null,c(d(s)(`employees.titles.style`)),1),p(x,{modelValue:k.value,"onUpdate:modelValue":o[4]||=e=>k.value=e,"control-id":`employee-title-style`,label:d(s)(`employees.titles.style`),options:Ke.value,disabled:F.value||M.value||N.value||O.value===`off`},null,8,[`modelValue`,`label`,`options`,`disabled`])])]),f(`p`,je,[e(c(d(s)(`employees.titles.example`))+` `,1),f(`span`,null,`【P1|`+c(k.value===`emoji`?`🔧 `:``)+`FIX|`+c(d(s)(`employees.titles.exampleTask`))+`】`,1)]),f(`p`,Me,c(d(s)(`employees.titles.boundary`)),1)],8,De)):l(``,!0),f(`section`,{class:`employee-permissions`,"aria-label":d(s)(`employees.permissions`)},[f(`strong`,null,c(d(s)(`employees.permissions`)),1),f(`ul`,null,[(n(!0),m(u,null,t(V.value.permissions,e=>(n(),m(`li`,{key:e},c(e===`board.read`?d(s)(`employees.boardRead`):e===`board.write`?d(s)(`employees.boardWrite`):e===`session.title`?d(s)(`employees.titles.heading`):e),1))),128))]),f(`p`,null,c(d(s)(`employees.noExecutor`)),1)],8,Ne),V.value.runtime?(n(),m(`p`,{key:8,class:te([`employee-runtime`,{"is-warning":V.value.runtimeStatus!==`ready`}])},c(V.value.runtimeStatus===`ready`?d(s)(`employees.hostReady`):d(s)(`employees.installRequired`)),3)):l(``,!0),I.value||V.value.identityConflict?(n(),m(`p`,Pe,c(I.value||d(s)(`employees.errors.identity_conflict`)),1)):l(``,!0),N.value?(n(),m(`button`,{key:10,type:`button`,class:`quiet-button`,disabled:M.value,onClick:o[5]||=e=>Z()},c(d(s)(`employees.reloadScope`)),9,Fe)):l(``,!0),L.value?(n(),m(`div`,Ie,[f(`strong`,null,c(d(s)(`employees.scopeSaved`)),1),f(`p`,null,c(E.value===`all`?d(s)(`employees.scope.successAll`,{count:W.value.length}):W.value.length?d(s)(`employees.successMultiple`,{count:W.value.length}):d(s)(`employees.successUnassigned`)),1)])):l(``,!0)]),f(`footer`,Le,[f(`button`,{class:`quiet-button`,type:`button`,disabled:F.value,onClick:Q},c(d(s)(`employees.cancel`)),9,Re),J.value&&Ye.value&&!F.value&&!M.value&&!N.value&&V.value.runtimeStatus===`ready`?(n(),h(d(y),{key:0,class:`employee-primary`,to:Ye.value,onClick:Q},{default:i(()=>[e(c(d(s)(`employees.open`)),1)]),_:1},8,[`to`])):(n(),m(`button`,{key:1,class:`employee-primary`,type:`submit`,disabled:F.value||M.value||N.value||!j.value||J.value||V.value.identityConflict||!r.personalSpaceId},[F.value?(n(),h(_,{key:0,variant:`inline`,label:Je.value},null,8,[`label`])):(n(),m(u,{key:1},[e(c(Je.value),1)],64))],8,ze))])],32)):(n(),m(`p`,me,c(d(s)(`employees.noTemplates`)),1))],544)):l(``,!0)}}),[[`__scopeId`,`data-v-53eaa264`]]);export{S as t};
|