fuli-context 0.7.6 → 0.7.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.
Files changed (146) hide show
  1. package/README.md +69 -13
  2. package/README.zh-CN.md +60 -14
  3. package/dist/web/assets/{AboutPage-sLV9WChi.js → AboutPage-BcZeFcHe.js} +1 -1
  4. package/dist/web/assets/{ConnectionsPage-BWn4ywGJ.js → ConnectionsPage-xAQYl1SQ.js} +1 -1
  5. package/dist/web/assets/EmployeeRecruitDialog-CbPAmhy7.js +1 -0
  6. package/dist/web/assets/EmployeeRecruitDialog-CfFlyKSh.css +1 -0
  7. package/dist/web/assets/EmployeeWorkbenchPage-BQvTLoc8.css +1 -0
  8. package/dist/web/assets/EmployeeWorkbenchPage-DF3EL0Ui.js +1 -0
  9. package/dist/web/assets/KnowledgeEditDialog-UnnBDt0I.js +1 -0
  10. package/dist/web/assets/{KnowledgeEditDialog-DWUV_NPp.css → KnowledgeEditDialog-iv-dgUeD.css} +1 -1
  11. package/dist/web/assets/{KnowledgeInspector-CRQWOaos.js → KnowledgeInspector-BqPJH5Rr.js} +1 -1
  12. package/dist/web/assets/{KnowledgeOrganizerPage-Dfj3vIpU.js → KnowledgeOrganizerPage-CyvRflXM.js} +1 -1
  13. package/dist/web/assets/KnowledgePage-59eczTqe.js +1 -0
  14. package/dist/web/assets/{KnowledgeWorkspace-ChEB5Bwk.js → KnowledgeWorkspace-CfIjhOog.js} +1 -1
  15. package/dist/web/assets/{OverviewPage-Cd66M0RT.js → OverviewPage-Xyfm5xkZ.js} +1 -1
  16. package/dist/web/assets/{PersonalProfilePage-RCFHL0Wz.js → PersonalProfilePage-DB9mByB3.js} +1 -1
  17. package/dist/web/assets/PersonalProjectsPage-_-YXVi5O.js +1 -0
  18. package/dist/web/assets/ProjectAgentsPage-C3vW6wmk.css +1 -0
  19. package/dist/web/assets/ProjectAgentsPage-DeeQByPX.js +11 -0
  20. package/dist/web/assets/{PublicProjectsPage-mstPEjHe.js → PublicProjectsPage-BEZ1YPs3.js} +1 -1
  21. package/dist/web/assets/{ReviewPage-6Y1SmfZy.js → ReviewPage-CQYGwFBH.js} +1 -1
  22. package/dist/web/assets/SearchableSelect-D27H0MoP.js +1 -0
  23. package/dist/web/assets/{SettingsPage--LdTMUm7.js → SettingsPage-BlEZ03Yi.js} +1 -1
  24. package/dist/web/assets/{WritingTastePage-CIpKswvs.js → WritingTastePage-DvRt_nmP.js} +1 -1
  25. package/dist/web/assets/catalog-DFvIaHVS.js +1 -0
  26. package/dist/web/assets/{console-BT1P2Db4.js → console-tOxOl8Vq.js} +1 -1
  27. package/dist/web/assets/conversation-launcher-settings-DuFSpPAo.js +1 -0
  28. package/dist/web/assets/{i18n-js6gfc7j.js → i18n-CgBHZthI.js} +3 -3
  29. package/dist/web/assets/{identity-CfDfanpa.js → identity-CFo_Uw1H.js} +1 -1
  30. package/dist/web/assets/index-CJc5ppXH.css +1 -0
  31. package/dist/web/assets/index-yCi3cz2l.js +2 -0
  32. package/dist/web/assets/{model-DJkLAJsg.js → model-BtnVr7fX.js} +1 -1
  33. package/dist/web/assets/{useApi-CROJJdhE-BdSti7Y2.js → useApi-CROJJdhE-B6MHm_Av.js} +1 -1
  34. package/dist/web/assets/{vue-router-Kk67EaCP.js → vue-router-CzHm-y-x.js} +1 -1
  35. package/dist/web/index.html +7 -6
  36. package/docs/employee-agents.md +99 -0
  37. package/docs/project-agent-memory.md +479 -0
  38. package/graph-provider/fuli_graph/knowledge_audit_models.py +171 -0
  39. package/graph-provider/fuli_graph/model_base.py +5 -0
  40. package/graph-provider/fuli_graph/model_validation.py +32 -0
  41. package/graph-provider/fuli_graph/models.py +26 -170
  42. package/graph-provider/fuli_graph/project_agent_context_models.py +29 -0
  43. package/graph-provider/fuli_graph/project_agent_executor_models.py +35 -7
  44. package/graph-provider/fuli_graph/project_agent_memory_models.py +79 -0
  45. package/graph-provider/fuli_graph/project_agent_memory_routes.py +46 -0
  46. package/graph-provider/fuli_graph/project_agent_models.py +9 -0
  47. package/graph-provider/fuli_graph/project_agent_recruitment_values.py +48 -0
  48. package/graph-provider/fuli_graph/project_agent_routes.py +21 -1
  49. package/graph-provider/fuli_graph/project_agent_task_models.py +217 -29
  50. package/graph-provider/fuli_graph/runtime.py +37 -1
  51. package/graph-provider/fuli_graph/store.py +6 -0
  52. package/graph-provider/fuli_graph/store_project_agent_context.py +65 -0
  53. package/graph-provider/fuli_graph/store_project_agent_executor_learning.py +175 -11
  54. package/graph-provider/fuli_graph/store_project_agent_executor_routing.py +33 -10
  55. package/graph-provider/fuli_graph/store_project_agent_executors.py +181 -14
  56. package/graph-provider/fuli_graph/store_project_agent_memory.py +174 -0
  57. package/graph-provider/fuli_graph/store_project_agent_task_activity.py +423 -0
  58. package/graph-provider/fuli_graph/store_project_agent_task_persistence.py +152 -0
  59. package/graph-provider/fuli_graph/store_project_agent_task_reads.py +187 -21
  60. package/graph-provider/fuli_graph/store_project_agent_task_recruitment.py +748 -162
  61. package/graph-provider/fuli_graph/store_project_agent_task_staffing.py +230 -18
  62. package/graph-provider/fuli_graph/store_project_agent_tasks.py +261 -468
  63. package/graph-provider/fuli_graph/store_project_agents.py +214 -46
  64. package/graph-provider/fuli_graph/store_task_contexts.py +217 -0
  65. package/graph-provider/fuli_graph/store_transactions.py +40 -0
  66. package/graph-provider/fuli_graph/task_context_models.py +50 -0
  67. package/graph-provider/fuli_graph/task_context_routes.py +31 -0
  68. package/graph-provider/fuli_graph/workflow_observation_models.py +1 -1
  69. package/graph-provider/pyproject.toml +1 -1
  70. package/npm-shrinkwrap.json +8 -8
  71. package/package.json +5 -2
  72. package/skills/capturing-session-knowledge/SKILL.md +18 -6
  73. package/src/adaptive-runtime/executor-pool.js +46 -5
  74. package/src/adaptive-runtime/graph-services.js +10 -3
  75. package/src/adaptive-runtime/lease-client.js +66 -14
  76. package/src/adaptive-runtime/runtime-broker.js +24 -9
  77. package/src/agent-tools/employee-definitions.js +43 -0
  78. package/src/agent-tools/graph-definitions.js +25 -6
  79. package/src/agent-tools/graph-handlers.js +11 -0
  80. package/src/agent-tools/project-agent-definitions.js +35 -4
  81. package/src/agent-tools/project-agent-memory-definitions.js +36 -0
  82. package/src/agent-tools.js +6 -2
  83. package/src/agents/codex/lifecycle-hook.js +107 -0
  84. package/src/agents/codex/lifecycle-hooks.js +231 -0
  85. package/src/agents/cursor/lifecycle-hook.js +133 -0
  86. package/src/agents/cursor/lifecycle-hooks.js +50 -0
  87. package/src/agents/lifecycle-message.js +19 -0
  88. package/src/app/agent-request-context.js +23 -0
  89. package/src/cli/command-registry.js +2 -0
  90. package/src/cli/employee-command.js +17 -0
  91. package/src/cli/remote-mcp-command.js +282 -0
  92. package/src/cli/setup-command.js +5 -0
  93. package/src/cli.js +10 -0
  94. package/src/employees/catalog/jefa.json +21 -0
  95. package/src/employees/install-package.js +77 -0
  96. package/src/employees/management-policy.js +88 -0
  97. package/src/employees/manifest.js +53 -0
  98. package/src/employees/package-registry.js +107 -0
  99. package/src/employees/project-selection.js +50 -0
  100. package/src/employees/recruitment.js +199 -0
  101. package/src/employees/service.js +83 -0
  102. package/src/employees/task-entry.js +39 -0
  103. package/src/graphiti/agent-knowledge-workflows.js +135 -25
  104. package/src/graphiti/federated-application.js +78 -35
  105. package/src/graphiti/project-agent-control-plane.js +20 -3
  106. package/src/graphiti/project-agent-mapping.js +155 -5
  107. package/src/graphiti/project-agent-memory.js +115 -0
  108. package/src/graphiti/project-agent-task-entry.js +126 -0
  109. package/src/graphiti/project-agent-workflows.js +30 -14
  110. package/src/graphiti/provider-client.js +66 -4
  111. package/src/http/api-router.js +25 -5
  112. package/src/http/employee-api-router.js +64 -0
  113. package/src/http/error-mapping.js +4 -0
  114. package/src/http/graph-api-router.js +2 -0
  115. package/src/http/request-policy.js +6 -3
  116. package/src/http/static-handler.js +4 -3
  117. package/src/mcp/create-mcp-server.js +98 -37
  118. package/src/mcp/direct-call.js +38 -0
  119. package/src/mcp/instructions.js +8 -7
  120. package/src/mcp/provider-task-context-registry.js +74 -0
  121. package/src/mcp/remote-http-server.js +761 -0
  122. package/src/mcp/session-id.js +29 -3
  123. package/src/mcp/task-context-registry.js +88 -29
  124. package/src/mcp/tool-annotations.js +10 -2
  125. package/src/mcp/tool-result.js +8 -3
  126. package/src/mcp-server.js +20 -8
  127. package/src/native-runtime/runtime.js +58 -8
  128. package/src/server.js +76 -28
  129. package/src/setup/agent-installation-status.js +38 -6
  130. package/src/setup/agents.js +20 -8
  131. package/src/setup/claude-code-config.js +55 -13
  132. package/src/setup/codex-bootstrap.js +11 -10
  133. package/src/setup/codex-config.js +74 -7
  134. package/src/setup/config-backup.js +18 -2
  135. package/src/setup/cursor-config.js +22 -5
  136. package/src/setup/setup.js +30 -22
  137. package/src/storage/json-file.js +11 -3
  138. package/dist/web/assets/KnowledgeEditDialog-CZ5ziPrD.js +0 -1
  139. package/dist/web/assets/KnowledgePage-BRYC9S5Y.js +0 -1
  140. package/dist/web/assets/PersonalProjectsPage-DEh8XWby.js +0 -1
  141. package/dist/web/assets/ProjectAgentsPage-Crh7PrXa.css +0 -1
  142. package/dist/web/assets/ProjectAgentsPage-DPnaH-TR.js +0 -11
  143. package/dist/web/assets/SearchableSelect-D3HM8YF8.js +0 -1
  144. package/dist/web/assets/conversation-launcher-settings-DrJEotdf.js +0 -1
  145. package/dist/web/assets/index-BnH5mcZx.css +0 -1
  146. package/dist/web/assets/index-GqJf1xqg.js +0 -2
@@ -1,4 +1,5 @@
1
1
  import { dirname } from 'node:path';
2
+ import { createEmployeeService } from '../employees/service.js';
2
3
 
3
4
  import { GraphitiProviderClient } from './provider-client.js';
4
5
  import {
@@ -30,7 +31,7 @@ import {
30
31
  } from './source-marker.js';
31
32
  import { resolvePersonalProjectPath } from './project-path-context.js';
32
33
  import { mergeExternalKnowledgeProjection } from '../external-knowledge/graph-projection.js';
33
- import { TaskContextRegistry } from '../mcp/task-context-registry.js';
34
+ import { ProviderTaskContextRegistry } from '../mcp/provider-task-context-registry.js';
34
35
  import { attachExternalKnowledgeRuntime } from '../external-knowledge/runtime.js';
35
36
  import { resolveSetupPaths } from '../setup/paths.js';
36
37
  import {
@@ -79,6 +80,7 @@ import {
79
80
  getCollaborationPreferences as getCollaborationPreferencesWorkflow
80
81
  } from './collaboration-preference-workflow.js';
81
82
  import { buildUserTasteSkill } from './user-taste-skill.js';
83
+ import { resolveTaskEntryAgent, loadProjectAgentContinuity } from './project-agent-task-entry.js';
82
84
  import { getWritingTasteProfile as getWritingTasteProfileWorkflow } from './writing-taste-profile-workflow.js';
83
85
  import {
84
86
  groupSubscriptions,
@@ -114,6 +116,7 @@ export function openFederatedGraphApplication({
114
116
  env,
115
117
  fetchImpl
116
118
  });
119
+ app.employees = createEmployeeService({ app, runtimeConfigPath });
117
120
  }
118
121
  return app;
119
122
  }
@@ -137,7 +140,7 @@ export class FederatedGraphApplication extends ProjectAgentControlPlaneApplicati
137
140
  agentAccessPolicyStore = new AgentAccessPolicyStore(),
138
141
  consoleUrl = sourceConsoleUrl(null),
139
142
  projectPathResolver = resolvePersonalProjectPath,
140
- taskContextRegistry = new TaskContextRegistry(),
143
+ taskContextRegistry = null,
141
144
  providerRequestTimeoutMs = undefined
142
145
  } = {}) {
143
146
  super();
@@ -147,7 +150,6 @@ export class FederatedGraphApplication extends ProjectAgentControlPlaneApplicati
147
150
  this.agentAccessPolicyStore = agentAccessPolicyStore;
148
151
  this.consoleUrl = consoleUrl;
149
152
  this.projectPathResolver = projectPathResolver;
150
- this.taskContextRegistry = taskContextRegistry;
151
153
  this.personal = new GraphitiProviderClient({
152
154
  baseUrl: config.personal.providerUrl,
153
155
  accessToken: config.personal.accessToken,
@@ -155,6 +157,9 @@ export class FederatedGraphApplication extends ProjectAgentControlPlaneApplicati
155
157
  fetchImpl,
156
158
  requestTimeoutMs: providerRequestTimeoutMs
157
159
  });
160
+ this.taskContextRegistry = taskContextRegistry ?? new ProviderTaskContextRegistry(
161
+ this.personal, config.personal.spaceId
162
+ );
158
163
  this.workspaces = new Map(config.workspaces.map((workspace) => {
159
164
  const provider = createWorkspaceProvider(workspace, {
160
165
  fetchImpl, requestTimeoutMs: providerRequestTimeoutMs
@@ -171,8 +176,8 @@ export class FederatedGraphApplication extends ProjectAgentControlPlaneApplicati
171
176
  return checkpointTaskKnowledgeWorkflow(this, input);
172
177
  }
173
178
 
174
- verifyTaskCheckpoint({ sessionId }) {
175
- return this.taskContextRegistry.verify(sessionId);
179
+ verifyTaskCheckpoint({ sessionId, sourceApplication = 'other' }) {
180
+ return this.taskContextRegistry.verify(sessionId, sourceApplication);
176
181
  }
177
182
 
178
183
  async recordDecisionTrace(input) {
@@ -180,6 +185,10 @@ export class FederatedGraphApplication extends ProjectAgentControlPlaneApplicati
180
185
  return recordDecisionTraceWorkflow(this, input);
181
186
  }
182
187
 
188
+ validateCaptureSessionKnowledge(input) {
189
+ if (this.getCapturePolicy().enabled) this.#prepareKnowledgeCapture(input);
190
+ }
191
+
183
192
  async captureSessionKnowledge(input) {
184
193
  const capturePolicy = this.getCapturePolicy();
185
194
  if (!capturePolicy.enabled) {
@@ -189,15 +198,8 @@ export class FederatedGraphApplication extends ProjectAgentControlPlaneApplicati
189
198
  capturePolicy
190
199
  };
191
200
  }
192
- assertNoCredentials(input);
193
- const episode = providerEpisode(input);
201
+ const { episode, workspace } = this.#prepareKnowledgeCapture(input);
194
202
  if (input.targetKind === 'personal') {
195
- if (input.spaceId !== this.config.personal.spaceId) {
196
- throw new ApplicationError(
197
- ApplicationErrorCode.VALIDATION,
198
- 'Personal capture target must be the active personal space'
199
- );
200
- }
201
203
  const result = await this.personal.commit({
202
204
  space_id: input.spaceId,
203
205
  personal_project_id: input.personalProjectId ?? null,
@@ -206,20 +208,6 @@ export class FederatedGraphApplication extends ProjectAgentControlPlaneApplicati
206
208
  });
207
209
  return { route: 'personal', ...result };
208
210
  }
209
- if (input.projectAgentId) {
210
- throw new ApplicationError(
211
- ApplicationErrorCode.VALIDATION,
212
- 'Project Agent knowledge can only be captured in the personal graph'
213
- );
214
- }
215
- if (episode.sensitivity !== 'normal') {
216
- throw new ApplicationError(
217
- ApplicationErrorCode.VALIDATION,
218
- 'Private or restricted knowledge cannot enter a team-shared project queue. Use targetKind "personal" with personalProjectId to keep it in the personal graph'
219
- );
220
- }
221
- assertPublicKnowledgeEligible(episode);
222
- const workspace = this.#workspace(input.providerUrl);
223
211
  const draft = await this.personal.createPublicationDraft({
224
212
  personal_space_id: this.config.personal.spaceId,
225
213
  target_project_id: input.spaceId,
@@ -257,19 +245,33 @@ export class FederatedGraphApplication extends ProjectAgentControlPlaneApplicati
257
245
  projectAgentId = null,
258
246
  projectPath = null,
259
247
  taskPrompt = null,
248
+ sourceApplication = 'other',
249
+ sourceSessionId = null,
250
+ sessionId = null,
251
+ turnId = null,
252
+ workKind = null,
253
+ requiredCapabilities = [],
260
254
  limit = 100,
261
255
  agentInvocation = false,
262
256
  agentToolName = 'get_collaboration_preferences'
263
257
  } = {}) {
264
- const projectResolution = await this.#resolvePreferenceProject({
265
- personalProjectId,
266
- projectPath
267
- });
268
- return getCollaborationPreferencesWorkflow(
258
+ const projectResolution = await this.#resolvePreferenceProject({ personalProjectId, projectPath });
259
+ const selection = projectPath === null && !personalProjectId && !projectAgentId
260
+ ? null
261
+ : await resolveTaskEntryAgent(
262
+ this, projectResolution, { projectAgentId, taskPrompt, sourceApplication,
263
+ sessionId: agentToolName === 'get_collaboration_preferences'
264
+ ? sessionId ?? sourceSessionId : turnId ? sessionId : null,
265
+ turnId: agentToolName === 'begin_task_context' ? turnId : null,
266
+ workKind, requiredCapabilities, agentInvocation, agentToolName }
267
+ );
268
+ // A rejected explicit role must not leak its private preferences via fallback.
269
+ const selectedAgentId = selection ? selection.agent?.agentId ?? null : projectAgentId;
270
+ const preferences = await getCollaborationPreferencesWorkflow(
269
271
  this,
270
272
  projectResolution,
271
273
  {
272
- projectAgentId,
274
+ projectAgentId: selectedAgentId,
273
275
  taskPrompt,
274
276
  limit,
275
277
  agentInvocation,
@@ -277,6 +279,19 @@ export class FederatedGraphApplication extends ProjectAgentControlPlaneApplicati
277
279
  },
278
280
  (items, toolName) => this.#recordAgentViews(items, toolName)
279
281
  );
282
+ const management = this.employees && agentInvocation &&
283
+ ['begin_task_context', 'get_collaboration_preferences'].includes(agentToolName)
284
+ ? await this.employees.taskEntry({ personalProjectId: projectResolution.personalProjectId,
285
+ sourceApplication, sourceSessionId, sessionId })
286
+ : null;
287
+ const managedPreferences = management ? { ...preferences, project_management_context: management } : preferences;
288
+ if (!selection) return managedPreferences;
289
+ const context = selection.agent ? await loadProjectAgentContinuity(this, {
290
+ projectId: projectResolution.personalProjectId, agent: selection.agent,
291
+ sourceApplication, taskPrompt, selectionReason: selection.reason,
292
+ matchBasis: selection.match_basis
293
+ }) : selection;
294
+ return { ...managedPreferences, project_agent_context: context };
280
295
  }
281
296
 
282
297
  async getUserTasteSkill({
@@ -518,7 +533,7 @@ export class FederatedGraphApplication extends ProjectAgentControlPlaneApplicati
518
533
 
519
534
  async searchCurrentProjectKnowledge(input) {
520
535
  const resolution = await this.#resolvePreferenceProject({
521
- personalProjectId: null,
536
+ personalProjectId: input.personalProjectId ?? null,
522
537
  projectPath: input.projectPath
523
538
  });
524
539
  return searchCurrentProjectKnowledgeWorkflow(this, resolution, input);
@@ -1164,7 +1179,7 @@ export class FederatedGraphApplication extends ProjectAgentControlPlaneApplicati
1164
1179
  };
1165
1180
  }
1166
1181
 
1167
- close() {}
1182
+ close() { return this.employees?.close(); }
1168
1183
 
1169
1184
  async #recordAgentViews(items, toolName) {
1170
1185
  const unique = new Map(items.map((item) => [
@@ -1195,6 +1210,34 @@ export class FederatedGraphApplication extends ProjectAgentControlPlaneApplicati
1195
1210
  return this.#resolvePreferenceProject(input);
1196
1211
  }
1197
1212
 
1213
+ #prepareKnowledgeCapture(input) {
1214
+ assertNoCredentials(input);
1215
+ const episode = providerEpisode(input);
1216
+ if (input.targetKind === 'personal') {
1217
+ if (input.spaceId !== this.config.personal.spaceId) {
1218
+ throw new ApplicationError(
1219
+ ApplicationErrorCode.VALIDATION,
1220
+ 'Personal capture target must be the active personal space'
1221
+ );
1222
+ }
1223
+ return { episode };
1224
+ }
1225
+ if (input.projectAgentId) {
1226
+ throw new ApplicationError(
1227
+ ApplicationErrorCode.VALIDATION,
1228
+ 'Project Agent knowledge can only be captured in the personal graph'
1229
+ );
1230
+ }
1231
+ if (episode.sensitivity !== 'normal') {
1232
+ throw new ApplicationError(
1233
+ ApplicationErrorCode.VALIDATION,
1234
+ 'Private or restricted knowledge cannot enter a team-shared project queue. Use targetKind "personal" with personalProjectId to keep it in the personal graph'
1235
+ );
1236
+ }
1237
+ assertPublicKnowledgeEligible(episode);
1238
+ return { episode, workspace: this.#workspace(input.providerUrl) };
1239
+ }
1240
+
1198
1241
  #workspace(providerUrl) {
1199
1242
  const requested = providerUrl
1200
1243
  ? canonicalProviderUrl(providerUrl)
@@ -1,4 +1,8 @@
1
1
  import { agentProjectResolution } from './agent-knowledge-workflows.js';
2
+ import {
3
+ getProjectAgentMemory,
4
+ checkpointProjectAgentMemory
5
+ } from './project-agent-memory.js';
2
6
  import {
3
7
  authorizeExecutor as authorizeExecutorWorkflow,
4
8
  cleanupProjectAgentTestRoles as cleanupProjectAgentTestRolesWorkflow,
@@ -48,6 +52,16 @@ export const projectAgentControlPlaneHooks = Object.freeze({
48
52
  });
49
53
 
50
54
  export class ProjectAgentControlPlaneApplication {
55
+ async getProjectAgentMemory(input) {
56
+ this.#assertSpace(input.personalSpaceId);
57
+ return getProjectAgentMemory(this, input);
58
+ }
59
+
60
+ async checkpointProjectAgentMemory(input) {
61
+ this.#assertSpace(input.personalSpaceId);
62
+ return checkpointProjectAgentMemory(this, input);
63
+ }
64
+
51
65
  async upsertProjectAgent(input) {
52
66
  this.#assertSpace(input.personalSpaceId);
53
67
  return upsertProjectAgentWorkflow(this, input);
@@ -252,7 +266,10 @@ export class ProjectAgentControlPlaneApplication {
252
266
  }
253
267
 
254
268
  async getProjectAgentContext(input) {
255
- const resolution = await this.#resolveProject(input.projectPath);
269
+ const resolution = await this.#resolveProject(
270
+ input.projectPath,
271
+ input.personalProjectId ?? null
272
+ );
256
273
  return getProjectAgentContextWorkflow(
257
274
  this,
258
275
  agentProjectResolution(resolution),
@@ -266,9 +283,9 @@ export class ProjectAgentControlPlaneApplication {
266
283
  );
267
284
  }
268
285
 
269
- #resolveProject(projectPath) {
286
+ #resolveProject(projectPath, personalProjectId = null) {
270
287
  return this[projectAgentControlPlaneHooks.resolvePreferenceProject]({
271
- personalProjectId: null,
288
+ personalProjectId,
272
289
  projectPath
273
290
  });
274
291
  }
@@ -246,6 +246,8 @@ export function providerProjectAgentTaskSubmit(input) {
246
246
  objective: input.objective,
247
247
  work_kind: input.workKind ?? input.work_kind,
248
248
  required_capabilities: input.requiredCapabilities ?? input.required_capabilities ?? [],
249
+ executor_capability_hints:
250
+ input.executorCapabilityHints ?? input.executor_capability_hints ?? [],
249
251
  duration: input.duration ?? 'ongoing',
250
252
  staffing_intent: input.staffingIntent ?? input.staffing_intent ?? 'reuse_preferred',
251
253
  lead_agent_id: input.leadAgentId ?? input.lead_agent_id ?? null,
@@ -293,6 +295,36 @@ export function projectAgentParallelPlan(value = {}) {
293
295
  };
294
296
  }
295
297
 
298
+ export function providerProjectAgentTokenUsage(value) {
299
+ if (value === undefined || value === null) return value ?? null;
300
+ return {
301
+ source: value.source,
302
+ total_tokens: value.totalTokens ?? value.total_tokens,
303
+ input_tokens: value.inputTokens ?? value.input_tokens ?? null,
304
+ output_tokens: value.outputTokens ?? value.output_tokens ?? null,
305
+ cached_input_tokens: value.cachedInputTokens ?? value.cached_input_tokens ?? null,
306
+ cache_write_input_tokens: value.cacheWriteInputTokens ??
307
+ value.cache_write_input_tokens ?? null,
308
+ reasoning_output_tokens: value.reasoningOutputTokens ??
309
+ value.reasoning_output_tokens ?? null
310
+ };
311
+ }
312
+
313
+ export function projectAgentTokenUsage(value) {
314
+ if (value === undefined || value === null) return value ?? null;
315
+ return {
316
+ source: value.source,
317
+ totalTokens: value.total_tokens ?? value.totalTokens,
318
+ inputTokens: value.input_tokens ?? value.inputTokens ?? null,
319
+ outputTokens: value.output_tokens ?? value.outputTokens ?? null,
320
+ cachedInputTokens: value.cached_input_tokens ?? value.cachedInputTokens ?? null,
321
+ cacheWriteInputTokens: value.cache_write_input_tokens ??
322
+ value.cacheWriteInputTokens ?? null,
323
+ reasoningOutputTokens: value.reasoning_output_tokens ??
324
+ value.reasoningOutputTokens ?? null
325
+ };
326
+ }
327
+
296
328
  export function providerProjectAgentTaskActivity(input) {
297
329
  const result = withoutUndefined({
298
330
  personal_space_id: input.personalSpaceId ?? input.personal_space_id,
@@ -332,6 +364,31 @@ export function providerProjectAgentTaskActivity(input) {
332
364
  worker_status: input.workerStatus ?? input.worker_status ?? null
333
365
  });
334
366
  }
367
+ if (hasAny(input, ['tokenUsage', 'token_usage'])) {
368
+ result.token_usage = providerProjectAgentTokenUsage(
369
+ input.tokenUsage ?? input.token_usage
370
+ );
371
+ }
372
+ if (hasAny(input, ['workerRuntime', 'worker_runtime'])) {
373
+ const runtime = input.workerRuntime ?? input.worker_runtime;
374
+ result.worker_runtime = runtime ? withoutUndefined({
375
+ application: runtime.application,
376
+ session_id: runtime.sessionId ?? runtime.session_id ?? null,
377
+ session_url: runtime.sessionUrl ?? runtime.session_url ?? null
378
+ }) : null;
379
+ }
380
+ copyOptional(
381
+ result,
382
+ 'source_session_url',
383
+ input.sourceSessionUrl ?? input.source_session_url ?? null,
384
+ hasAny(input, ['sourceSessionUrl', 'source_session_url'])
385
+ );
386
+ copyOptional(
387
+ result,
388
+ 'tools_used',
389
+ input.toolsUsed ?? input.tools_used ?? null,
390
+ hasAny(input, ['toolsUsed', 'tools_used'])
391
+ );
335
392
  return result;
336
393
  }
337
394
 
@@ -344,6 +401,7 @@ export function projectAgentTaskRecord(value = {}) {
344
401
  objective: value.objective,
345
402
  workKind: value.work_kind,
346
403
  requiredCapabilities: value.required_capabilities ?? [],
404
+ executorCapabilityHints: value.executor_capability_hints ?? [],
347
405
  duration: value.duration,
348
406
  staffingIntent: value.staffing_intent,
349
407
  status: value.status,
@@ -411,7 +469,7 @@ export function projectAgentTaskParticipantRecord(value = {}) {
411
469
  }
412
470
 
413
471
  export function projectAgentTaskEventRecord(value = {}) {
414
- return {
472
+ const result = {
415
473
  eventId: value.event_id,
416
474
  taskId: value.task_id,
417
475
  agentId: value.agent_id ?? null,
@@ -439,6 +497,31 @@ export function projectAgentTaskEventRecord(value = {}) {
439
497
  fallbackUsed: value.fallback_used ?? false,
440
498
  createdAt: value.created_at
441
499
  };
500
+ copyOptional(
501
+ result,
502
+ 'workerRuntime',
503
+ projectAgentWorkerRuntime(value.worker_runtime ?? value.workerRuntime),
504
+ hasAny(value, ['worker_runtime', 'workerRuntime'])
505
+ );
506
+ copyOptional(
507
+ result,
508
+ 'sourceSessionUrl',
509
+ value.source_session_url ?? value.sourceSessionUrl ?? null,
510
+ hasAny(value, ['source_session_url', 'sourceSessionUrl'])
511
+ );
512
+ copyOptional(
513
+ result,
514
+ 'toolsUsed',
515
+ value.tools_used ?? value.toolsUsed ?? null,
516
+ hasAny(value, ['tools_used', 'toolsUsed'])
517
+ );
518
+ copyOptional(
519
+ result,
520
+ 'tokenUsage',
521
+ projectAgentTokenUsage(value.token_usage ?? value.tokenUsage),
522
+ hasAny(value, ['token_usage', 'tokenUsage'])
523
+ );
524
+ return result;
442
525
  }
443
526
 
444
527
  export function projectAgentRoutingDecisionRecord(value = {}) {
@@ -483,6 +566,7 @@ export function projectAgentTaskRouteResult(value = {}) {
483
566
  task: projectAgentTaskRecord(value.task ?? {}),
484
567
  assignedAgent: value.assigned_agent ? projectAgentRecord(value.assigned_agent) : null,
485
568
  recruitment: value.recruitment ? projectAgentRecruitmentRecord(value.recruitment) : null,
569
+ recruitments: (value.recruitments ?? []).map(projectAgentRecruitmentRecord),
486
570
  decision: value.decision,
487
571
  mustDiscloseRecruitment: value.must_disclose_recruitment ?? false,
488
572
  clientNotice: value.client_notice ?? null
@@ -505,6 +589,8 @@ export function projectAgentRecruitmentRecord(value = {}) {
505
589
  status: value.status,
506
590
  confirmationMode: value.confirmation_mode,
507
591
  proposedAgentId: value.proposed_agent_id,
592
+ participantRole: value.participant_role ?? 'lead',
593
+ recruitmentSlot: value.recruitment_slot ?? 'lead',
508
594
  proposedProfile: value.proposed_profile
509
595
  ? projectAgentProfileRecord(value.proposed_profile) : null,
510
596
  triggerSourceApplication: value.trigger_source_application ?? null,
@@ -542,10 +628,10 @@ export function projectAgentExecutionSummary(value) {
542
628
  if (!Array.isArray(value)) return [];
543
629
  return value.map((entry = {}) => {
544
630
  const actualExecutor = entry.actual_executor_id ?? entry.actual_executor ??
545
- entry.actualExecutor ?? entry.executor_id ?? entry.executor ?? null;
631
+ entry.actualExecutor ?? null;
546
632
  const workSummary = entry.work_summary ?? entry.workSummary ?? entry.summary ?? null;
547
633
  const status = entry.status ?? entry.worker_status ?? entry.workerStatus ?? null;
548
- return {
634
+ const result = {
549
635
  agentId: entry.agent_id ?? entry.agentId ?? null,
550
636
  agentName: entry.agent_name ?? entry.agentName ?? null,
551
637
  occupationEmoji: entry.occupation_emoji ?? entry.occupationEmoji ?? null,
@@ -563,8 +649,48 @@ export function projectAgentExecutionSummary(value) {
563
649
  workSummary,
564
650
  summary: workSummary,
565
651
  status,
566
- workerStatus: entry.worker_status ?? entry.workerStatus ?? status
652
+ workerStatus: entry.worker_status ?? entry.workerStatus ?? null
567
653
  };
654
+ copyOptional(
655
+ result,
656
+ 'workerRuntime',
657
+ projectAgentWorkerRuntime(entry.worker_runtime ?? entry.workerRuntime),
658
+ hasAny(entry, ['worker_runtime', 'workerRuntime'])
659
+ );
660
+ copyOptional(
661
+ result,
662
+ 'sourceSessionId',
663
+ entry.source_session_id ?? entry.sourceSessionId ?? null,
664
+ hasAny(entry, ['source_session_id', 'sourceSessionId'])
665
+ );
666
+ copyOptional(
667
+ result,
668
+ 'sourceSessionUrl',
669
+ entry.source_session_url ?? entry.sourceSessionUrl ?? null,
670
+ hasAny(entry, ['source_session_url', 'sourceSessionUrl'])
671
+ );
672
+ copyOptional(
673
+ result,
674
+ 'toolsUsed',
675
+ entry.tools_used ?? entry.toolsUsed ?? null,
676
+ hasAny(entry, ['tools_used', 'toolsUsed'])
677
+ );
678
+ copyOptional(
679
+ result,
680
+ 'tokenUsage',
681
+ projectAgentTokenUsage(entry.token_usage ?? entry.tokenUsage),
682
+ hasAny(entry, ['token_usage', 'tokenUsage'])
683
+ );
684
+ return result;
685
+ });
686
+ }
687
+
688
+ export function projectAgentWorkerRuntime(value) {
689
+ if (!value || typeof value !== 'object') return null;
690
+ return withoutUndefined({
691
+ application: value.application,
692
+ sessionId: value.session_id ?? value.sessionId ?? null,
693
+ sessionUrl: value.session_url ?? value.sessionUrl ?? null
568
694
  });
569
695
  }
570
696
 
@@ -587,12 +713,30 @@ export function projectAgentActivityRecord(value = {}) {
587
713
  summary: task.summary,
588
714
  occurredAt: task.occurred_at,
589
715
  sourceApplication: task.source_application ?? null,
716
+ sourceSessionId: task.source_session_id ?? null,
717
+ sourceSessionUrl: task.source_session_url ?? task.sourceSessionUrl ?? null,
718
+ toolsUsed: task.tools_used ?? task.toolsUsed ?? null,
590
719
  actualExecutor: task.actual_executor_id ?? task.actual_executor ?? null,
720
+ selectedExecutorId: task.selected_executor_id ?? null,
721
+ executorRuleId: task.executor_rule_id ?? task.rule_id ?? null,
591
722
  matchedExecutorRuleId: task.matched_executor_rule_id ?? task.routing_rule_id ?? null,
592
723
  executorSelectionReason: task.executor_selection_reason ?? null,
724
+ executorFallbackReason: task.executor_fallback_reason ?? null,
725
+ executorFallbackOutcome: task.executor_fallback_outcome ?? null,
726
+ executorBlockedReason: task.executor_blocked_reason ?? null,
727
+ executorDecision: task.executor_decision ?? null,
593
728
  auditId: task.audit_id ?? null,
594
729
  actualModelProvider: task.actual_model_provider ?? null,
595
730
  actualModel: task.actual_model ?? null,
731
+ workerId: task.worker_id ?? task.workerId ?? null,
732
+ workerLabel: task.worker_label ?? task.workerLabel ?? null,
733
+ workerOccupationEmoji: task.worker_occupation_emoji ??
734
+ task.workerOccupationEmoji ?? null,
735
+ workerStatus: task.worker_status ?? task.workerStatus ?? null,
736
+ tokenUsage: projectAgentTokenUsage(task.token_usage ?? task.tokenUsage),
737
+ ...(hasAny(task, ['worker_runtime', 'workerRuntime']) ? {
738
+ workerRuntime: projectAgentWorkerRuntime(task.worker_runtime ?? task.workerRuntime)
739
+ } : {}),
596
740
  executionSummary: projectAgentExecutionSummary(
597
741
  task.execution_summary ?? task.executionSummary
598
742
  ),
@@ -608,7 +752,12 @@ export function projectAgentRecruitmentPolicyRecord(value = {}) {
608
752
  return {
609
753
  personalSpaceId: value.personal_space_id,
610
754
  confirmationMode: value.confirmation_mode ?? 'automatic',
611
- updatedAt: value.updated_at ?? null
755
+ updatedAt: value.updated_at ?? null,
756
+ ...(value.policy_status ? {
757
+ policyStatus: value.policy_status,
758
+ appliesToRecruitment: value.applies_to_recruitment,
759
+ warning: value.warning
760
+ } : {})
612
761
  };
613
762
  }
614
763
 
@@ -991,6 +1140,7 @@ export function routingLearningRecord(value = {}) {
991
1140
  ratingCount: value.rating_count,
992
1141
  averageRating: value.average_rating,
993
1142
  neutralDueToInsufficientEvidence: value.neutral_due_to_insufficient_evidence,
1143
+ validationWarnings: value.validation_warnings,
994
1144
  weightedSuccess: value.weighted_success,
995
1145
  weightedFailure: value.weighted_failure,
996
1146
  evidenceRefs: value.evidence_refs,
@@ -0,0 +1,115 @@
1
+ import { ApplicationError, ApplicationErrorCode } from '../app/application-error.js';
2
+ import { detectSensitiveContent } from '../security/sensitive-content.js';
3
+
4
+ export function agentMemoryRecord(value) {
5
+ if (!value || typeof value !== 'object' || Array.isArray(value) ||
6
+ !Number.isInteger(value.revision) || value.revision < 1 ||
7
+ !value.memory || typeof value.memory !== 'object' || Array.isArray(value.memory) ||
8
+ typeof value.memory.summary !== 'string') {
9
+ throw invalidAgentMemoryResponse();
10
+ }
11
+ return {
12
+ checkpointId: value.checkpoint_id,
13
+ personalSpaceId: value.personal_space_id,
14
+ personalProjectId: value.personal_project_id,
15
+ agentId: value.agent_id,
16
+ revision: value.revision,
17
+ memory: {
18
+ summary: value.memory.summary,
19
+ decisions: memoryNotes(value.memory.decisions),
20
+ openThreads: memoryNotes(value.memory.open_threads),
21
+ nextActions: memoryNotes(value.memory.next_actions)
22
+ },
23
+ sourceApplication: value.source_application,
24
+ sourceSessionId: value.source_session_id ?? null,
25
+ taskId: value.task_id ?? null,
26
+ createdAt: value.created_at
27
+ };
28
+ }
29
+
30
+ export function agentMemoryView(value) {
31
+ if (!value || typeof value !== 'object' || Array.isArray(value) ||
32
+ !Number.isInteger(value.revision) || value.revision < 0 ||
33
+ (value.history !== undefined && value.history !== null && !Array.isArray(value.history))) {
34
+ throw invalidAgentMemoryResponse();
35
+ }
36
+ const current = value.current === undefined || value.current === null
37
+ ? null : agentMemoryRecord(value.current);
38
+ const history = (value.history ?? []).map(agentMemoryRecord)
39
+ .filter(record => record.revision !== current?.revision);
40
+ return {
41
+ personalSpaceId: value.personal_space_id,
42
+ personalProjectId: value.personal_project_id,
43
+ agentId: value.agent_id,
44
+ scope: value.scope,
45
+ storage: value.storage,
46
+ authority: value.authority,
47
+ revision: value.revision ?? 0,
48
+ current,
49
+ history
50
+ };
51
+ }
52
+
53
+ export async function getProjectAgentMemory(application, input) {
54
+ await requireAgentMemoryAccess(application, input);
55
+ return agentMemoryView(await application.personal.getProjectAgentMemory(input));
56
+ }
57
+
58
+ export async function checkpointProjectAgentMemory(application, input) {
59
+ const prepared = await prepareProjectAgentMemory(application, input);
60
+ if (prepared.status === 'capture_disabled') return prepared;
61
+ const value = await application.personal.writeProjectAgentMemory(prepared.request);
62
+ return { status: 'checkpointed', ...agentMemoryRecord(value) };
63
+ }
64
+
65
+ // Validate and normalize through the same boundary without performing a write.
66
+ // Task checkpoints send this payload to the Provider's atomic prepare operation.
67
+ export async function prepareProjectAgentMemory(application, input) {
68
+ if (detectSensitiveContent(JSON.stringify(input.memory)).restricted) {
69
+ throw new ApplicationError(ApplicationErrorCode.VALIDATION, 'Agent working memory contains credentials');
70
+ }
71
+ await requireAgentMemoryAccess(application, input);
72
+ const policy = application.getCapturePolicy?.() ?? { enabled: true };
73
+ if (!policy.enabled) return { status: 'capture_disabled', capturePolicy: policy };
74
+ return { request: {
75
+ personal_space_id: input.personalSpaceId,
76
+ personal_project_id: input.personalProjectId,
77
+ agent_id: input.agentId,
78
+ expected_revision: input.expectedRevision,
79
+ idempotency_key: input.idempotencyKey,
80
+ memory: {
81
+ summary: input.memory.summary,
82
+ decisions: input.memory.decisions ?? [],
83
+ open_threads: input.memory.openThreads ?? [],
84
+ next_actions: input.memory.nextActions ?? []
85
+ },
86
+ source_application: input.sourceApplication ?? 'other',
87
+ source_session_id: input.sourceSessionId ?? null,
88
+ task_id: input.taskId ?? null
89
+ } };
90
+ }
91
+
92
+ async function requireAgentMemoryAccess(application, input) {
93
+ const agent = await application.getProjectAgent(input);
94
+ if (agent.profile.status !== 'active' || agent.memoryScope !== 'reviewed_agent') {
95
+ throw new ApplicationError(ApplicationErrorCode.VALIDATION, 'An active durable Agent assignment is required for working memory');
96
+ }
97
+ if (!(agent.profile.allowedClients ?? []).includes(input.sourceApplication ?? 'other')) {
98
+ throw new ApplicationError(ApplicationErrorCode.VALIDATION, 'This client is not allowed to load the Agent memory');
99
+ }
100
+ }
101
+
102
+ function memoryNotes(value) {
103
+ const notes = value ?? [];
104
+ if (!Array.isArray(notes) || notes.some(note => typeof note !== 'string')) {
105
+ throw invalidAgentMemoryResponse();
106
+ }
107
+ return [...notes];
108
+ }
109
+
110
+ function invalidAgentMemoryResponse() {
111
+ return new ApplicationError(
112
+ ApplicationErrorCode.VALIDATION,
113
+ 'Provider returned an invalid Agent memory record'
114
+ );
115
+ }