@axiom-lattice/core 4.3.0 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1747,6 +1747,7 @@ __export(index_exports, {
1747
1747
  TaskLifecycleNotifier: () => TaskLifecycleNotifier,
1748
1748
  TaskLifecycleService: () => TaskLifecycleService,
1749
1749
  TaskRunBridgeStoresRequiredError: () => TaskRunBridgeStoresRequiredError,
1750
+ TaskRunRegistry: () => TaskRunRegistry,
1750
1751
  TaskStatus: () => TaskStatus,
1751
1752
  TeamAgentGraphBuilder: () => TeamAgentGraphBuilder,
1752
1753
  TenantPluginCatalog: () => TenantPluginCatalog,
@@ -1758,7 +1759,6 @@ __export(index_exports, {
1758
1759
  VectorStoreProviderManager: () => VectorStoreProviderManager,
1759
1760
  VolumeFilesystem: () => VolumeFilesystem,
1760
1761
  abortWorkflowRun: () => abortWorkflowRun,
1761
- agentHasManagedTaskCapability: () => agentHasManagedTaskCapability,
1762
1762
  agentInstanceManager: () => agentInstanceManager,
1763
1763
  agentLatticeManager: () => agentLatticeManager,
1764
1764
  appendReconciliationWarning: () => appendReconciliationWarning,
@@ -1933,6 +1933,9 @@ __export(index_exports, {
1933
1933
  projectTaskAdmissionNotifier: () => projectTaskAdmissionNotifier,
1934
1934
  queueLatticeManager: () => queueLatticeManager,
1935
1935
  readLoopBreakerConfig: () => readLoopBreakerConfig,
1936
+ reconcileAllTenantTaskRuns: () => reconcileAllTenantTaskRuns,
1937
+ reconcileStaleTaskRuns: () => reconcileStaleTaskRuns,
1938
+ reconcileTenantTaskRuns: () => reconcileTenantTaskRuns,
1936
1939
  registerAgentLattice: () => registerAgentLattice,
1937
1940
  registerAgentLatticeWithTenant: () => registerAgentLatticeWithTenant,
1938
1941
  registerAgentLattices: () => registerAgentLattices,
@@ -1981,6 +1984,7 @@ __export(index_exports, {
1981
1984
  storeLatticeManager: () => storeLatticeManager,
1982
1985
  sttModelLatticeManager: () => sttModelLatticeManager,
1983
1986
  taskLifecycleNotifier: () => taskLifecycleNotifier,
1987
+ taskRunRegistry: () => taskRunRegistry,
1984
1988
  toJsonSchema: () => toJsonSchema,
1985
1989
  toSafeStateExpr: () => toSafeStateExpr,
1986
1990
  toolLatticeManager: () => toolLatticeManager,
@@ -13205,7 +13209,7 @@ function readLoopBreakerConfig(env = process.env) {
13205
13209
  return {
13206
13210
  repeatWarn: parse(env.AGENT_REPEATED_TOOL_CALL_WARN, 3),
13207
13211
  repeatStop: parse(env.AGENT_REPEATED_TOOL_CALL_STOP, 5),
13208
- runLimit: parse(env.AGENT_TOOL_CALL_RUN_LIMIT, 120),
13212
+ runLimit: parse(env.AGENT_TOOL_CALL_RUN_LIMIT, 300),
13209
13213
  skillRunLimit: parse(env.AGENT_SKILL_CALL_RUN_LIMIT, 8)
13210
13214
  };
13211
13215
  }
@@ -16889,6 +16893,41 @@ var AgentManager = class _AgentManager {
16889
16893
  }
16890
16894
  };
16891
16895
 
16896
+ // src/services/TaskExecutionContext.ts
16897
+ var DELEGATED_WORKER_SCOPE_TOKEN = Symbol("delegated-worker-scope");
16898
+ var delegatedWorkerScopeSecret = Object.freeze({});
16899
+ function createDelegatedWorkerRunConfig(taskId) {
16900
+ return {
16901
+ taskId,
16902
+ delegatedTaskId: taskId,
16903
+ taskExecutionRole: "worker",
16904
+ execution: { taskId },
16905
+ [DELEGATED_WORKER_SCOPE_TOKEN]: delegatedWorkerScopeSecret
16906
+ };
16907
+ }
16908
+ function getDelegatedTaskId(runConfig) {
16909
+ return runConfig[DELEGATED_WORKER_SCOPE_TOKEN] === delegatedWorkerScopeSecret && runConfig.taskExecutionRole === "worker" && typeof runConfig.taskId === "string" && runConfig.taskId.trim() !== "" && typeof runConfig.delegatedTaskId === "string" && runConfig.taskId === runConfig.delegatedTaskId ? runConfig.taskId : void 0;
16910
+ }
16911
+ function hasDelegatedWorkerToken(runConfig) {
16912
+ return Object.prototype.hasOwnProperty.call(runConfig, DELEGATED_WORKER_SCOPE_TOKEN);
16913
+ }
16914
+ function preserveDelegatedWorkerContext(source, target) {
16915
+ const delegatedTaskId = getDelegatedTaskId(source);
16916
+ if (!delegatedTaskId) return;
16917
+ target.taskId = delegatedTaskId;
16918
+ target.delegatedTaskId = delegatedTaskId;
16919
+ target.taskExecutionRole = "worker";
16920
+ target.execution = { taskId: delegatedTaskId };
16921
+ target[DELEGATED_WORKER_SCOPE_TOKEN] = delegatedWorkerScopeSecret;
16922
+ }
16923
+ function stripDelegatedWorkerContext(target) {
16924
+ delete target.taskId;
16925
+ delete target.delegatedTaskId;
16926
+ delete target.taskExecutionRole;
16927
+ delete target.execution;
16928
+ delete target[DELEGATED_WORKER_SCOPE_TOKEN];
16929
+ }
16930
+
16892
16931
  // src/agent_worker/agent_worker_graph.ts
16893
16932
  var AgentWorkerState = import_langgraph5.Annotation.Root({
16894
16933
  // Input parameters
@@ -16913,6 +16952,13 @@ var AgentWorkerState = import_langgraph5.Annotation.Root({
16913
16952
  reducer: (x, y) => y ?? x,
16914
16953
  default: () => void 0
16915
16954
  }),
16955
+ // Optional persistent TaskItem binding. Rebuilt into an authentic delegated
16956
+ // worker scope on every execute (the in-process scope symbol cannot survive
16957
+ // LangGraph checkpointing, so it is re-derived from this plain task id).
16958
+ taskId: (0, import_langgraph5.Annotation)({
16959
+ reducer: (x, y) => y ?? x,
16960
+ default: () => ""
16961
+ }),
16916
16962
  // Execution result
16917
16963
  result: (0, import_langgraph5.Annotation)({
16918
16964
  reducer: (x, y) => y ?? x,
@@ -16930,14 +16976,15 @@ var AgentWorkerState = import_langgraph5.Annotation.Root({
16930
16976
  })
16931
16977
  });
16932
16978
  async function executeNode(state) {
16933
- const { assistant_id, thread_id, input, command2, runConfig } = state;
16979
+ const { assistant_id, thread_id, input, command2, runConfig, taskId } = state;
16980
+ const effectiveRunConfig = taskId ? { ...runConfig ?? {}, ...createDelegatedWorkerRunConfig(taskId) } : runConfig;
16934
16981
  const callParams = {
16935
16982
  assistant_id,
16936
16983
  "x-tenant-id": runConfig?.tenantId,
16937
16984
  thread_id,
16938
16985
  input,
16939
16986
  command: command2,
16940
- runConfig
16987
+ runConfig: effectiveRunConfig
16941
16988
  };
16942
16989
  const result = await AgentManager.getInstance().callAgentInQueue(
16943
16990
  callParams,
@@ -18288,38 +18335,6 @@ var CapabilityRuntimeResolver = class {
18288
18335
  }
18289
18336
  };
18290
18337
 
18291
- // src/services/TaskExecutionContext.ts
18292
- var DELEGATED_WORKER_SCOPE_TOKEN = Symbol("delegated-worker-scope");
18293
- var delegatedWorkerScopeSecret = Object.freeze({});
18294
- function createDelegatedWorkerRunConfig(taskId) {
18295
- return {
18296
- taskId,
18297
- delegatedTaskId: taskId,
18298
- taskExecutionRole: "worker",
18299
- [DELEGATED_WORKER_SCOPE_TOKEN]: delegatedWorkerScopeSecret
18300
- };
18301
- }
18302
- function getDelegatedTaskId(runConfig) {
18303
- return runConfig[DELEGATED_WORKER_SCOPE_TOKEN] === delegatedWorkerScopeSecret && runConfig.taskExecutionRole === "worker" && typeof runConfig.taskId === "string" && runConfig.taskId.trim() !== "" && typeof runConfig.delegatedTaskId === "string" && runConfig.taskId === runConfig.delegatedTaskId ? runConfig.taskId : void 0;
18304
- }
18305
- function hasDelegatedWorkerToken(runConfig) {
18306
- return Object.prototype.hasOwnProperty.call(runConfig, DELEGATED_WORKER_SCOPE_TOKEN);
18307
- }
18308
- function preserveDelegatedWorkerContext(source, target) {
18309
- const delegatedTaskId = getDelegatedTaskId(source);
18310
- if (!delegatedTaskId) return;
18311
- target.taskId = delegatedTaskId;
18312
- target.delegatedTaskId = delegatedTaskId;
18313
- target.taskExecutionRole = "worker";
18314
- target[DELEGATED_WORKER_SCOPE_TOKEN] = delegatedWorkerScopeSecret;
18315
- }
18316
- function stripDelegatedWorkerContext(target) {
18317
- delete target.taskId;
18318
- delete target.delegatedTaskId;
18319
- delete target.taskExecutionRole;
18320
- delete target[DELEGATED_WORKER_SCOPE_TOKEN];
18321
- }
18322
-
18323
18338
  // src/services/TrustedAgentDispatchCapability.ts
18324
18339
  var TRUSTED_AGENT_DISPATCH = Symbol("axiom.trustedAgentDispatch");
18325
18340
 
@@ -18483,8 +18498,22 @@ function sanitizeCallerRunConfig(value, preserveDelegatedContext = true) {
18483
18498
  } else {
18484
18499
  stripDelegatedWorkerContext(callerRunConfig);
18485
18500
  }
18501
+ preserveExecutionBinding(value, callerRunConfig);
18486
18502
  return callerRunConfig;
18487
18503
  }
18504
+ function preserveExecutionBinding(source, target) {
18505
+ if (typeof source !== "object" || source === null || Array.isArray(source)) return;
18506
+ const execution = (0, import_protocols10.descriptorDataValue)(Object.getOwnPropertyDescriptor(source, "execution"))?.value;
18507
+ if (typeof execution !== "object" || execution === null || Array.isArray(execution)) return;
18508
+ const taskId = (0, import_protocols10.descriptorDataValue)(Object.getOwnPropertyDescriptor(execution, "taskId"))?.value;
18509
+ if (typeof taskId !== "string" || taskId.trim() === "") return;
18510
+ Object.defineProperty(target, "execution", {
18511
+ value: { taskId },
18512
+ enumerable: true,
18513
+ configurable: true,
18514
+ writable: true
18515
+ });
18516
+ }
18488
18517
  function sanitizeExecutionRunConfig(value) {
18489
18518
  return cloneRunConfig(value, false);
18490
18519
  }
@@ -18604,6 +18633,7 @@ function checkpointCapabilityRevision(state) {
18604
18633
  }
18605
18634
  var QUEUE_PROCESSOR_RETRY_BASE_MS = 100;
18606
18635
  var QUEUE_PROCESSOR_MAX_RETRIES = 3;
18636
+ var AGENT_RECURSION_LIMIT = 1e3;
18607
18637
  function hasExactKeys(record, required, optional = []) {
18608
18638
  const allowed = /* @__PURE__ */ new Set([...required, ...optional]);
18609
18639
  return required.every((key5) => Object.prototype.hasOwnProperty.call(record, key5)) && Reflect.ownKeys(record).every((key5) => typeof key5 === "string" && allowed.has(key5));
@@ -18656,7 +18686,7 @@ var Agent = class {
18656
18686
  ...executionRunConfig,
18657
18687
  runConfig: executionRunConfig
18658
18688
  },
18659
- recursionLimit: 200,
18689
+ recursionLimit: AGENT_RECURSION_LIMIT,
18660
18690
  signal
18661
18691
  }
18662
18692
  );
@@ -18731,7 +18761,7 @@ var Agent = class {
18731
18761
  },
18732
18762
  streamMode: ["updates", "messages"],
18733
18763
  subgraphs: false,
18734
- recursionLimit: 200,
18764
+ recursionLimit: AGENT_RECURSION_LIMIT,
18735
18765
  signal
18736
18766
  }
18737
18767
  );
@@ -19229,6 +19259,15 @@ var Agent = class {
19229
19259
  configurable: true,
19230
19260
  writable: true
19231
19261
  });
19262
+ const trustedTaskId = parsed.projectTask?.taskId;
19263
+ if (trustedTaskId) {
19264
+ Object.defineProperty(base, "execution", {
19265
+ value: { taskId: trustedTaskId },
19266
+ enumerable: true,
19267
+ configurable: true,
19268
+ writable: true
19269
+ });
19270
+ }
19232
19271
  return base;
19233
19272
  }
19234
19273
  getHumanPendingContent(message) {
@@ -19432,7 +19471,7 @@ var Agent = class {
19432
19471
  ...executionRunConfig,
19433
19472
  runConfig: executionRunConfig
19434
19473
  },
19435
- recursionLimit: 200,
19474
+ recursionLimit: AGENT_RECURSION_LIMIT,
19436
19475
  signal
19437
19476
  }
19438
19477
  );
@@ -19543,7 +19582,7 @@ var Agent = class {
19543
19582
  },
19544
19583
  streamMode: ["updates", "messages"],
19545
19584
  subgraphs: false,
19546
- recursionLimit: 200,
19585
+ recursionLimit: AGENT_RECURSION_LIMIT,
19547
19586
  signal
19548
19587
  }
19549
19588
  );
@@ -20300,6 +20339,20 @@ var agentInstanceManager = AgentInstanceManager.getInstance();
20300
20339
  var DEFAULT_SUBAGENT_PROMPT = "In order to complete the objective that the user asks of you, you have access to a number of standard tools.";
20301
20340
  var EXCLUDED_STATE_KEYS = ["messages", "todos", "jumpTo"];
20302
20341
  var DEFAULT_GENERAL_PURPOSE_DESCRIPTION = "General-purpose agent for researching complex questions, searching for files and content, and executing multi-step tasks. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries use this agent to perform the search for you. This agent has access to all tools as the main agent.";
20342
+ function createGeneralPurposeAgent(options) {
20343
+ const middleware = [...options.middleware];
20344
+ if (options.interruptOn) {
20345
+ middleware.push((0, import_langchain53.humanInTheLoopMiddleware)({ interruptOn: options.interruptOn }));
20346
+ }
20347
+ return (0, import_langchain53.createAgent)({
20348
+ model: options.model,
20349
+ systemPrompt: DEFAULT_SUBAGENT_PROMPT,
20350
+ tools: options.tools,
20351
+ middleware,
20352
+ checkpointer: getCheckpointSaver("default"),
20353
+ name: "general-purpose"
20354
+ });
20355
+ }
20303
20356
  function getTaskToolDescription(subagentDescriptions) {
20304
20357
  return subagentDescriptions.length > 0 ? `
20305
20358
  Launch an ephemeral subagent to handle complex, multi-step independent tasks with isolated context windows.
@@ -20479,21 +20532,12 @@ function getSubagents(options) {
20479
20532
  const agents = {};
20480
20533
  const subagentDescriptions = [];
20481
20534
  if (generalPurposeAgent) {
20482
- const generalPurposeMiddleware = [...defaultSubagentMiddleware];
20483
- if (defaultInterruptOn) {
20484
- generalPurposeMiddleware.push(
20485
- (0, import_langchain53.humanInTheLoopMiddleware)({ interruptOn: defaultInterruptOn })
20486
- );
20487
- }
20488
- const generalPurposeSubagent = (0, import_langchain53.createAgent)({
20535
+ agents["general-purpose"] = createGeneralPurposeAgent({
20489
20536
  model: defaultModel,
20490
- systemPrompt: DEFAULT_SUBAGENT_PROMPT,
20491
20537
  tools: defaultTools,
20492
- middleware: generalPurposeMiddleware,
20493
- checkpointer: getCheckpointSaver("default"),
20494
- name: "general-purpose"
20538
+ middleware: defaultSubagentMiddleware,
20539
+ interruptOn: defaultInterruptOn
20495
20540
  });
20496
- agents["general-purpose"] = generalPurposeSubagent;
20497
20541
  subagentDescriptions.push(
20498
20542
  `- general-purpose: ${DEFAULT_GENERAL_PURPOSE_DESCRIPTION}`
20499
20543
  );
@@ -23932,6 +23976,7 @@ ${BASE_PROMPT}` : BASE_PROMPT;
23932
23976
  }
23933
23977
 
23934
23978
  // src/agent_lattice/builders/DeepAgentGraphBuilder.ts
23979
+ var import_protocols15 = require("@axiom-lattice/protocols");
23935
23980
  init_MemoryLatticeManager();
23936
23981
  var DeepAgentGraphBuilder = class {
23937
23982
  /**
@@ -23981,6 +24026,28 @@ var DeepAgentGraphBuilder = class {
23981
24026
  backend: filesystemBackend,
23982
24027
  middleware: middlewares
23983
24028
  });
24029
+ if (middlewares.some((candidate) => candidate.name === "TaskMiddleware") && agentLattice.config.key) {
24030
+ const tenantId2 = params.tenantId ?? "default";
24031
+ const generalPurposeKey = `${agentLattice.config.key}-general-purpose`;
24032
+ if (!agentLatticeManager.hasAgentLatticeWithTenant(tenantId2, generalPurposeKey)) {
24033
+ const generalPurposeAgent = createGeneralPurposeAgent({
24034
+ model: params.model,
24035
+ tools,
24036
+ middleware: middlewares
24037
+ });
24038
+ agentLatticeManager.registerWithTenant(tenantId2, generalPurposeKey, {
24039
+ config: {
24040
+ key: generalPurposeKey,
24041
+ name: "general-purpose",
24042
+ description: DEFAULT_GENERAL_PURPOSE_DESCRIPTION,
24043
+ type: import_protocols15.AgentType.REACT,
24044
+ prompt: DEFAULT_SUBAGENT_PROMPT,
24045
+ tenantId: tenantId2
24046
+ },
24047
+ client: generalPurposeAgent
24048
+ });
24049
+ }
24050
+ }
23984
24051
  return deepAgent;
23985
24052
  }
23986
24053
  };
@@ -25932,11 +25999,11 @@ _A2ARemoteClient.MAX_CACHED_TASKS = 100;
25932
25999
  var A2ARemoteClient = _A2ARemoteClient;
25933
26000
 
25934
26001
  // src/agent_lattice/builders/RemoteAgentGraphBuilder.ts
25935
- var import_protocols15 = require("@axiom-lattice/protocols");
26002
+ var import_protocols16 = require("@axiom-lattice/protocols");
25936
26003
  init_MemoryLatticeManager();
25937
26004
  var RemoteAgentGraphBuilder = class {
25938
26005
  async build(agentLattice, params) {
25939
- if (agentLattice.config.type !== import_protocols15.AgentType.A2A_REMOTE) {
26006
+ if (agentLattice.config.type !== import_protocols16.AgentType.A2A_REMOTE) {
25940
26007
  throw new Error(
25941
26008
  `RemoteAgentGraphBuilder received wrong agent type: ${agentLattice.config.type}`
25942
26009
  );
@@ -26001,7 +26068,7 @@ function extractLastHumanMessage(messages) {
26001
26068
  // src/agent_lattice/builders/WorkflowAgentGraphBuilder.ts
26002
26069
  var import_langchain61 = require("langchain");
26003
26070
  init_MemoryLatticeManager();
26004
- var import_protocols16 = require("@axiom-lattice/protocols");
26071
+ var import_protocols17 = require("@axiom-lattice/protocols");
26005
26072
  init_compile();
26006
26073
 
26007
26074
  // src/agent_lattice/builders/middlewareSanitize.ts
@@ -26039,7 +26106,7 @@ ${configPrompt}` : base;
26039
26106
  }
26040
26107
  var WorkflowAgentGraphBuilder = class {
26041
26108
  async build(agentLattice, params) {
26042
- if (!(0, import_protocols16.isWorkflowAgentConfig)(agentLattice.config)) {
26109
+ if (!(0, import_protocols17.isWorkflowAgentConfig)(agentLattice.config)) {
26043
26110
  throw new Error(
26044
26111
  `WorkflowAgentGraphBuilder received wrong agent type: ${agentLattice.config.type}`
26045
26112
  );
@@ -26177,7 +26244,7 @@ var AgentGraphBuilderFactory = class _AgentGraphBuilderFactory {
26177
26244
  };
26178
26245
 
26179
26246
  // src/agent_lattice/builders/AgentParamsBuilder.ts
26180
- var import_protocols17 = require("@axiom-lattice/protocols");
26247
+ var import_protocols18 = require("@axiom-lattice/protocols");
26181
26248
  var AgentParamsBuilder = class {
26182
26249
  /**
26183
26250
  * constructor
@@ -26195,8 +26262,8 @@ var AgentParamsBuilder = class {
26195
26262
  * @returns Agent build parameters
26196
26263
  */
26197
26264
  async buildParams(agentLattice, options) {
26198
- const skills = (0, import_protocols17.isDeepAgentConfig)(agentLattice.config) || (0, import_protocols17.isProcessingAgentConfig)(agentLattice.config) ? agentLattice.config.skillCategories : void 0;
26199
- const toolKeys = options?.overrideTools || (0, import_protocols17.getToolsFromConfig)(agentLattice.config);
26265
+ const skills = (0, import_protocols18.isDeepAgentConfig)(agentLattice.config) || (0, import_protocols18.isProcessingAgentConfig)(agentLattice.config) ? agentLattice.config.skillCategories : void 0;
26266
+ const toolKeys = options?.overrideTools || (0, import_protocols18.getToolsFromConfig)(agentLattice.config);
26200
26267
  const tenantId2 = agentLattice.config.tenantId || "default";
26201
26268
  const tools = toolKeys.map((toolKey) => {
26202
26269
  const toolLattice = toolLatticeManager.getToolLatticeWithTenant(tenantId2, toolKey) ?? toolLatticeManager.getToolLatticeWithTenant("default", toolKey);
@@ -26215,7 +26282,7 @@ var AgentParamsBuilder = class {
26215
26282
  if (!model) {
26216
26283
  throw new Error(`Model "${modelKey}" does not exist`);
26217
26284
  }
26218
- const subAgentKeys = (0, import_protocols17.getSubAgentsFromConfig)(agentLattice.config);
26285
+ const subAgentKeys = (0, import_protocols18.getSubAgentsFromConfig)(agentLattice.config);
26219
26286
  const subAgents = await Promise.all(subAgentKeys.map(async (agentKey) => {
26220
26287
  const subAgentLattice = await this.getAgentLatticeFunc(agentKey);
26221
26288
  if (!subAgentLattice) {
@@ -26232,7 +26299,7 @@ var AgentParamsBuilder = class {
26232
26299
  }));
26233
26300
  const resolvedSubAgents = subAgents.filter((s) => s !== null);
26234
26301
  let internalSubAgents = [];
26235
- if ((0, import_protocols17.isDeepAgentConfig)(agentLattice.config) || (0, import_protocols17.isProcessingAgentConfig)(agentLattice.config)) {
26302
+ if ((0, import_protocols18.isDeepAgentConfig)(agentLattice.config) || (0, import_protocols18.isProcessingAgentConfig)(agentLattice.config)) {
26236
26303
  internalSubAgents = agentLattice.config.internalSubAgents?.map((i) => ({
26237
26304
  key: i.key,
26238
26305
  config: i
@@ -26710,9 +26777,15 @@ For human judgment, use the actual HITL payload \`status: "interrupted"\` plus
26710
26777
  handled by the configured HITL lifecycle; \`review_required\` is not a task status.
26711
26778
 
26712
26779
  Create the active parent and each started subtask as records: \`manage_task
26713
- create\` always persists \`pending\`. Execution starts only via
26714
- \`task(taskId, agentId)\` dispatch, or the agent works inline and keeps the status
26715
- truthful with \`set_status\`. Use \`pending\` only for future or
26780
+ create\` always persists \`pending\`. **Process the task you were assigned in this
26781
+ run: set it \`in_progress\` with \`manage_task set_status\` when you start (a
26782
+ dispatched task is claimed automatically by the engine).** To bring
26783
+ in a more suitable expert, create a subtask (\`pending\`) and dispatch THAT with
26784
+ \`task { taskId, agentId }\` (a specialist, or agentId "general-purpose" to run your
26785
+ own capabilities in an isolated run). **Never re-dispatch the task you are
26786
+ currently processing** \u2014 delegating to an expert means giving them a child task,
26787
+ not handing over your own. Report the outcome with \`set_status completed\` (result
26788
+ + beliefImpact), \`failed\`, or \`interrupted\`. Use \`pending\` only for future or
26716
26789
  dependency-blocked work.
26717
26790
  Before marking the parent completed, call \`list(parentId)\`: every child must
26718
26791
  be completed or cancelled, while a failed or interrupted child blocks completion
@@ -28003,34 +28076,24 @@ Renew HITL approval when a foundational assumption or authority boundary changes
28003
28076
 
28004
28077
  ## Skill Loading Policy
28005
28078
 
28006
- Visible in <available_skills> does not mean loaded. Follow these three tiers.
28007
-
28008
- ### Tier 1 \u2014 Session start (mandatory, unconditional)
28009
- Load before any other work; the bootstrap prompt enforces this:
28010
- - [[agent-architecture]] (this skill) \u2014 shared policy and routing.
28011
- - [[task-tracking]] \u2014 before your first \`manage_task\` call; Task management is
28012
- the planning surface and is gated again under Architect Task Policy.
28013
-
28014
- ### Tier 2 \u2014 Scenario gates (mandatory before entering the scenario)
28015
- Load the owning skill with the \`skill\` tool BEFORE the first action of that
28016
- scenario. Never execute a scenario without its owning skill loaded:
28017
- - Creating or modifying a Capability (\`react\`) or Orchestra (\`deep_agent\`)
28018
- Agent \u2192 [[agent-build]]
28019
- - Creating or modifying a Workflow (stable enumerable pipelines) \u2192 [[design-workflow]]
28020
- - Learning a capability from source material \u2192 [[document-learning-learn-capability]]
28021
- - Designing evals, running evals, or fixing eval failures \u2192 [[eval-verify]]
28022
- - Claiming work is complete or delivered \u2192 [[completion-gate]]
28023
- - Final configuration pre-check before delivery \u2192 [[review-agent]]
28024
- - Placing or authoring knowledge files (company vs project level) \u2192 [[knowledge-management]]
28025
-
28026
- If a Tier 2 load fails, retry once, then stop and tell the user which
28027
- capability is unavailable. Do not improvise the scenario from partial knowledge.
28028
-
28029
- ### Tier 3 \u2014 Reference (load on demand, never preload)
28030
- Deep details reachable through wiki-links inside loaded skills, and plugin
28031
- skills outside this chain (for example task-definition for non-architect
28032
- agents, or sandbox-plugins-authoring for the \`plugin-builder\` agent). Load one
28033
- only when the current step actually needs it.
28079
+ Visible in <available_skills> does not mean loaded. Load a skill only when the
28080
+ current step actually requires its procedure \u2014 one at a time, never preloaded.
28081
+ Do not load skills speculatively or to "see what exists": inspect existing
28082
+ assets with a bounded read-only survey, not by loading skill content.
28083
+
28084
+ - [[agent-architecture]] (this skill) \u2014 loaded at session start by the bootstrap
28085
+ prompt; it is the shared policy and router.
28086
+ - [[task-tracking]] \u2014 load right before your first \`manage_task\` call; Task
28087
+ management is the planning surface (gated again under Architect Task Policy).
28088
+ - Procedure skills \u2014 [[agent-build]], [[design-workflow]],
28089
+ [[document-learning-learn-capability]], [[eval-verify]], [[completion-gate]],
28090
+ [[review-agent]], [[knowledge-management]]: load the ONE that owns the step you
28091
+ are about to perform, immediately before performing it. Never preload the chain.
28092
+ - Reference and plugin skills (deep wiki-links, task-definition, sandbox authoring,
28093
+ and any plugin skills): load only when the current step needs them; never preload.
28094
+
28095
+ Loading a skill is a tool call, not a plan. If a required load fails, retry once,
28096
+ then stop and tell the user which capability is unavailable.
28034
28097
 
28035
28098
  ## Agent Forms and Routing
28036
28099
  - **Capability** (\`react\`) for bounded direct work: [[agent-build]]
@@ -28043,9 +28106,19 @@ only when the current step actually needs it.
28043
28106
  - **Tenant plugin** (sandbox-authored tool + optional HTML UI) \u2192 use the \`plugin-builder\` agent, or load [[sandbox-plugins-authoring]].
28044
28107
 
28045
28108
  ## Architect Task Policy
28046
- The Agent Architect uses persistent TaskItems as its planning surface. Architect must not use
28047
- \`write_todos\`. Create a persistent child only for an independently verifiable
28109
+ The Agent Architect MUST use persistent TaskItems as its single working surface;
28110
+ Architect must not use \`write_todos\` for independently verifiable outcomes, and
28111
+ working without a corresponding TaskItem is a policy violation.
28112
+ Create a persistent child only for an independently verifiable
28048
28113
  outcome; tool calls and file reads are actions inside the active Task.
28114
+ **Each task is your evidence ledger**: its Acceptance Criteria + Belief State define
28115
+ what counts as evidence, the work runs inside your own run (eval / \`invoke_agent\` for
28116
+ subject agents) and produces that evidence, and the parent completes only when the
28117
+ evidence is sufficient. Process the task you were assigned in this run; set it
28118
+ \`in_progress\` with \`manage_task set_status\` when you start. Delegate a subtask only
28119
+ when it belongs to another agent \u2014 create it (\`pending\`) and dispatch it with
28120
+ \`task { taskId, agentId }\`. To exercise a designed/subject agent, use eval /
28121
+ \`invoke_agent\` inside your own run instead of \`task\` dispatch.
28049
28122
  **MANDATORY: before your first \`manage_task\` call in this session, load
28050
28123
  [[task-tracking]] with the \`skill\` tool.** Do not create, update, or transition
28051
28124
  any Task until it is loaded. If the load fails, retry once, then tell the user
@@ -28116,11 +28189,11 @@ When a room request is actionable work, decide between two paths:
28116
28189
  \`assistantId\`, then dispatch it with
28117
28190
  \`task { taskId, agentId: <bot assistantId> }\` so the specialist actually runs
28118
28191
  (creating the task alone starts nothing). \`task\` transfers the task owner to
28119
- \`agentId\` and requires a **task-capable** bot (enabled \`task\` middleware that
28120
- exposes \`manage_task\`); a non-capable target is rejected with
28121
- \`TASK_DELEGATE_TARGET_NOT_CAPABLE\`. State the expected output and
28122
- acceptance criteria in the task description so the assigned bot can verify its
28123
- own work.
28192
+ \`agentId\` and runs it in an isolated thread; the target does not need task
28193
+ tools \u2014 if it has \`manage_task\` it records status itself, otherwise reconcile
28194
+ its returned \`executionResultId\` with \`manage_task\` update. State the expected output
28195
+ and acceptance criteria in the task description so the assigned bot can
28196
+ verify its own work.
28124
28197
 
28125
28198
  ## Discovering bots
28126
28199
  Before delegating, call \`list_room_roster\` to see the active room bots and their
@@ -28271,9 +28344,15 @@ criteria are truly met \u2014 never as a workaround.
28271
28344
  uncertainties, with no required minimum and a maximum of seven:
28272
28345
  \`manage_task create(title: <goal>, description: <contract + Belief State>, ownerType: "agent")\`
28273
28346
  \`create\` always yields a \`pending\` record \u2014 it never starts execution. Record
28274
- the returned parent task id, then run it by dispatching
28275
- \`task { taskId, agentId }\` (a fresh isolated thread); otherwise work inline and
28276
- \`set_status\` as you go.
28347
+ the returned parent task id, then **process it in this run**, setting it
28348
+ \`in_progress\` with \`manage_task set_status\` when you start. To decompose, create each subtask
28349
+ with \`manage_task create\` (\`pending\`) and delegate it with \`task { taskId,
28350
+ agentId }\` to the agent that should execute it (a specialist, or
28351
+ "general-purpose" for your own capabilities). The executor does not need
28352
+ \`manage_task\`: it returns an observation and \`executionResultId\`, and the caller
28353
+ records the outcome with \`manage_task\`. To exercise a subject application agent,
28354
+ use eval cases / \`invoke_agent\` inside your own run \u2014 \`task\` dispatch is
28355
+ delegation of persistent work, not an evaluation mechanism.
28277
28356
  - **Create a subtask for a verifiable result**, not per phase: identify its
28278
28357
  deliverable, define observable acceptance criteria and required evidence, and
28279
28358
  state positive and negative result branches under Targets / Expected Impact
@@ -28298,10 +28377,15 @@ ${TASK_BELIEF_REFERENCE_EXAMPLE}
28298
28377
 
28299
28378
  ## Status discipline \u2014 MANDATORY
28300
28379
 
28380
+ - Execution ownership: process the task you were assigned in this run; set it
28381
+ \`in_progress\` with \`manage_task set_status\` when you start. To involve a more
28382
+ suitable expert, create a subtask and dispatch it with \`task { taskId, agentId }\`
28383
+ \u2014 never re-dispatch the task you are processing. Report the outcome with
28384
+ \`set_status completed\` (result + beliefImpact), \`failed\`, or \`interrupted\`.
28301
28385
  - All status transitions go through \`manage_task set_status\` (with
28302
28386
  \`result\` on completed, \`failureReason\` on failed). \`update\` is for
28303
28387
  content fields only and rejects a status field.
28304
- - A subtask is \`completed\` ONLY when its work is actually finished.
28388
+ - A task is \`completed\` ONLY when its work is actually finished.
28305
28389
  While fixing eval failures, keep it \`in_progress\` \u2014 never mark
28306
28390
  \`completed\` as a workaround (the state machine rejects illegal
28307
28391
  transitions).
@@ -28901,6 +28985,11 @@ its evidence projection, not an independent source of goals or expectations.
28901
28985
  touching any eval project. An eval project created without a real, confirmed
28902
28986
  subject is a hard error. If the agent is only planned (not yet materialized),
28903
28987
  evaluation must wait until materialization returns its id.
28988
+ - **Exercise the subject through eval \u2014 never through \`task\` dispatch.** The
28989
+ subject agent is invoked by eval cases / \`invoke_agent\`; do NOT create a
28990
+ TaskItem assigned to the subject and dispatch it. \`task\` is for delegated
28991
+ execution of persistent work, not an evaluation mechanism. Use eval cases to
28992
+ measure the subject; keep \`task\` dispatch for executors running real work.
28904
28993
  - **Check existence before creating; reuse is the rule, creation is the exception.**
28905
28994
  Project names are unique per tenant. 1) Name it exactly \`eval-{agent-id}\`.
28906
28995
  2) \`read_eval list_projects({ name: "eval-{agent-id}" })\` \u2014 a name-scoped lookup,
@@ -29888,7 +29977,7 @@ var InMemoryMenuStore = class {
29888
29977
  // src/agent_lattice/agentArchitectTools.ts
29889
29978
  var import_zod50 = __toESM(require("zod"));
29890
29979
  var import_uuid9 = require("uuid");
29891
- var import_protocols18 = require("@axiom-lattice/protocols");
29980
+ var import_protocols19 = require("@axiom-lattice/protocols");
29892
29981
  function getTenantId2(exeConfig) {
29893
29982
  const runConfig = exeConfig?.configurable?.runConfig || {};
29894
29983
  return runConfig.tenantId || "default";
@@ -29938,7 +30027,7 @@ function getStringMetadata(config) {
29938
30027
  }
29939
30028
  function isUndecidedLearningPlaceholder(config) {
29940
30029
  const metadata = getStringMetadata(config);
29941
- return config.type === import_protocols18.AgentType.REACT && metadata.learningPlaceholder === "true" && metadata.architectureStatus === "undecided";
30030
+ return config.type === import_protocols19.AgentType.REACT && metadata.learningPlaceholder === "true" && metadata.architectureStatus === "undecided";
29942
30031
  }
29943
30032
  function requireSkillDeclaration(skillLoaded) {
29944
30033
  if (skillLoaded !== true) {
@@ -30091,7 +30180,7 @@ registerToolLattice(
30091
30180
  key: id,
30092
30181
  name: input.name,
30093
30182
  description: input.description || "",
30094
- type: input.type === "deep_agent" ? import_protocols18.AgentType.DEEP_AGENT : import_protocols18.AgentType.REACT,
30183
+ type: input.type === "deep_agent" ? import_protocols19.AgentType.DEEP_AGENT : import_protocols19.AgentType.REACT,
30095
30184
  prompt: input.prompt,
30096
30185
  ...input.tools && input.tools.length > 0 ? { tools: input.tools } : {},
30097
30186
  ...input.middleware && input.middleware.length > 0 ? { middleware: input.middleware } : {},
@@ -30148,7 +30237,7 @@ registerToolLattice(
30148
30237
  key: id,
30149
30238
  name: input.name,
30150
30239
  description: input.description || "",
30151
- type: import_protocols18.AgentType.WORKFLOW,
30240
+ type: import_protocols19.AgentType.WORKFLOW,
30152
30241
  prompt: input.description || `Workflow: ${input.name}`,
30153
30242
  workflowYaml: input.yaml,
30154
30243
  ...input.tools && input.tools.length > 0 ? { tools: input.tools } : {},
@@ -30185,7 +30274,7 @@ registerToolLattice(
30185
30274
  return JSON.stringify({ error: `Agent '${input.id}' not found` });
30186
30275
  }
30187
30276
  const graphDef = agent.graphDefinition;
30188
- if (!graphDef || graphDef.type !== import_protocols18.AgentType.WORKFLOW) {
30277
+ if (!graphDef || graphDef.type !== import_protocols19.AgentType.WORKFLOW) {
30189
30278
  return JSON.stringify({ error: `Agent '${input.id}' is not a workflow agent` });
30190
30279
  }
30191
30280
  const yaml2 = graphDef.workflowYaml;
@@ -30300,7 +30389,7 @@ registerToolLattice(
30300
30389
  return JSON.stringify({ error: `Agent '${input.id}' not found` });
30301
30390
  }
30302
30391
  const existingConfig = existing.graphDefinition || {};
30303
- const isWorkflow = existingConfig.type === import_protocols18.AgentType.WORKFLOW;
30392
+ const isWorkflow = existingConfig.type === import_protocols19.AgentType.WORKFLOW;
30304
30393
  const isPlaceholder = isUndecidedLearningPlaceholder(existingConfig);
30305
30394
  if (!isWorkflow && !isPlaceholder) {
30306
30395
  console.log(`[update_workflow] ERROR: not a workflow agent: ${input.id}`);
@@ -30316,7 +30405,7 @@ registerToolLattice(
30316
30405
  const mergedConfig = {
30317
30406
  ...existingConfig,
30318
30407
  ...isPlaceholder ? {
30319
- type: import_protocols18.AgentType.WORKFLOW,
30408
+ type: import_protocols19.AgentType.WORKFLOW,
30320
30409
  workflowYaml: input.yaml,
30321
30410
  metadata: {
30322
30411
  ...getStringMetadata(existingConfig),
@@ -30422,14 +30511,14 @@ registerToolLattice(
30422
30511
  const mergedConfig = { ...existingConfig, ...input.config };
30423
30512
  const isPlaceholder = isUndecidedLearningPlaceholder(existingConfigRecord);
30424
30513
  const materializedType = input.config.type;
30425
- const isMaterializingPlaceholder = isPlaceholder && (materializedType === import_protocols18.AgentType.REACT || materializedType === import_protocols18.AgentType.DEEP_AGENT);
30514
+ const isMaterializingPlaceholder = isPlaceholder && (materializedType === import_protocols19.AgentType.REACT || materializedType === import_protocols19.AgentType.DEEP_AGENT);
30426
30515
  if (isMaterializingPlaceholder) {
30427
30516
  mergedConfig.metadata = {
30428
30517
  ...getStringMetadata(existingConfigRecord),
30429
30518
  ...input.config.metadata ?? {},
30430
30519
  learningPlaceholder: "false",
30431
30520
  architectureStatus: "materialized",
30432
- architectureForm: materializedType === import_protocols18.AgentType.DEEP_AGENT ? "orchestra" : "capability"
30521
+ architectureForm: materializedType === import_protocols19.AgentType.DEEP_AGENT ? "orchestra" : "capability"
30433
30522
  };
30434
30523
  } else if (isPlaceholder && input.config.metadata !== void 0) {
30435
30524
  const metadata = {
@@ -30679,100 +30768,20 @@ var import_protocols20 = require("@axiom-lattice/protocols");
30679
30768
  // src/agent_lattice/agentArchitectPrompt.ts
30680
30769
  var AGENT_ARCHITECT_PROMPT = `You are the Agent Architect.
30681
30770
 
30682
- CRITICAL FIRST ACTION: Before any response or other action, call the \`skill\`
30683
- tool with skill_name: "agent-architecture" to load agent-architecture and follow it.
30684
- Immediately after it loads, call the \`skill\` tool again with skill_name:
30685
- "task-tracking" \u2014 persistent Task management is your core planning surface and
30686
- you must have its operation reference before creating or updating any Task.
30687
- Never announce skill loads. If a load fails, retry once, then stop and
30688
- explicitly report that the required skill could not be loaded.`;
30689
-
30690
- // src/agent_lattice/agentReviewerConfig.ts
30691
- var import_protocols19 = require("@axiom-lattice/protocols");
30692
-
30693
- // src/agent_lattice/agentReviewerPrompt.ts
30694
- var AGENT_REVIEWER_PROMPT = `# Agent Reviewer
30695
-
30696
- You are an **Agent Reviewer** \u2014 a quality assurance specialist for AI agents. Your job is to review agent configurations for correctness and test them by invoking them with realistic messages.
30697
-
30698
- ## Your Tools
30699
-
30700
- - **get_agent** \u2014 Fetch an agent's full configuration
30701
- - **list_agents** \u2014 List all agents in the workspace
30702
- - **list_tools** \u2014 List all available tools that can be assigned to agents
30703
- - **invoke_agent** \u2014 Test an agent by sending a message and getting its response. This spawns a completely fresh agent context \u2014 clean, isolated, and realistic.
30704
-
30705
- ## Your Workflow
30706
-
30707
- ### When asked to review an agent:
30708
-
30709
- 1. Call **get_agent** to fetch the agent's config
30710
- 2. Review the configuration for:
30711
- - **Completeness** \u2014 Are name, description, prompt present? Is the prompt clear and actionable?
30712
- - **Tool validity** \u2014 Do the referenced tools exist? Call **list_tools** to verify.
30713
- - **Middleware correctness** \u2014 Is every middleware entry complete (id, type, name, description, enabled, config)?
30714
- - **Sub-agent references** \u2014 Do referenced sub-agent IDs exist? Call **list_agents** to verify.
30715
- - **Type consistency** \u2014 Does the agent type match its configuration shape? (e.g., PROCESSING must have edges, DEEP_AGENT may have subAgents)
30716
- 3. Report your findings clearly. For each issue, state:
30717
- - Severity (ERROR / WARNING / INFO)
30718
- - What the problem is
30719
- - How to fix it
30720
-
30721
- ### When asked to test an agent:
30722
-
30723
- 1. Call **get_agent** to understand the agent's purpose and expected behavior
30724
- 2. Craft a realistic test message that exercises the agent's core responsibility \u2014 what would a real user say?
30725
- 3. Call **invoke_agent(id, message)** to send the test message
30726
- 4. Analyze the response:
30727
- - **If result.__interrupt__ is present** (an array of interrupt objects): The agent hit a human-in-the-loop interrupt (e.g., ask_user_to_clarify or interrupt() in a workflow). This is NOT an error \u2014 it means the agent paused execution waiting for user feedback. Each interrupt has a value (the question/request) and optionally an id. Report this as: "The agent successfully paused and is waiting for user input: <describe the interrupt value>." The messages array may contain the agent's output up to the point of interruption.
30728
- - **Otherwise, result.messages contains the agent's output**: The agent completed without interruption. Check:
30729
- - Did the agent understand the request?
30730
- - Was the response relevant and accurate?
30731
- - Did the agent use the right tools?
30732
- - Were there any errors or unexpected behaviors?
30733
- - **If result.error is present**: The invocation itself failed (agent not found, compilation error, etc.). Report the error clearly.
30734
- 5. Report your findings with the agent's actual response
30771
+ Your working surface is persistent **TaskItems**. Every independently verifiable
30772
+ outcome MUST be a TaskItem created and kept truthful through \`manage_task\`; do NOT
30773
+ keep a separate todo list \u2014 the TaskItem is your only planning and evidence surface.
30774
+ Gather evidence through tasks, and treat the parent as done only when its
30775
+ acceptance evidence is sufficient. If the \`task\` tools are unavailable, stop and
30776
+ report that task management is unavailable instead of working around it.
30735
30777
 
30736
- ### When results are wrong:
30737
-
30738
- If the agent's response is incorrect or unexpected:
30739
- 1. Point out specifically what went wrong
30740
- 2. Suggest what might need to change in the prompt or middleware config
30741
- 3. Offer to re-test after fixes are applied
30742
-
30743
- ## Important Rules
30744
-
30745
- - **Always use invoke_agent for testing.** This is the ONLY way to test \u2014 it spawns a fresh agent context isolated from all other conversations.
30746
- - **Be specific in your feedback.** Say "the agent failed to call the sql tool because the middleware config is missing databaseKeys" not "the agent didn't work."
30747
- - **One agent at a time.** Focus on one agent per review or test request. Don't try to review multiple agents simultaneously.
30748
- - **Be concise but thorough.** Cover all the checks, but present findings clearly without fluff.
30749
- `;
30750
-
30751
- // src/agent_lattice/agentReviewerConfig.ts
30752
- var AGENT_REVIEWER_KEY = "agent-reviewer";
30753
- var agentReviewerConfig = {
30754
- key: AGENT_REVIEWER_KEY,
30755
- name: "Agent Reviewer",
30756
- description: "Review and test AI agents. Use this agent to check agent configurations for correctness and to test agents by invoking them with realistic messages.",
30757
- type: import_protocols19.AgentType.REACT,
30758
- prompt: AGENT_REVIEWER_PROMPT,
30759
- tools: [
30760
- "invoke_agent",
30761
- "get_agent",
30762
- "list_agents",
30763
- "list_tools"
30764
- ],
30765
- middleware: [
30766
- {
30767
- id: "widget",
30768
- type: "widget",
30769
- name: "Widget",
30770
- description: "Render interactive HTML widgets and SVG diagrams",
30771
- enabled: true,
30772
- config: {}
30773
- }
30774
- ]
30775
- };
30778
+ CRITICAL FIRST ACTION: Before any response or other action, call the \`skill\`
30779
+ tool with skill_name: "agent-architecture" to load the shared policy and router,
30780
+ and follow it. Load every OTHER skill only when the current step actually
30781
+ requires its procedure \u2014 one at a time, never preloaded. Load "task-tracking"
30782
+ right before your first \`manage_task\` call. Never announce skill loads. If a
30783
+ required load fails, retry once, then stop and explicitly report that the
30784
+ required skill could not be loaded.`;
30776
30785
 
30777
30786
  // src/agent_lattice/agentArchitectConfig.ts
30778
30787
  var AGENT_ARCHITECT_KEY = "agent-architect";
@@ -30798,7 +30807,6 @@ var agentArchitectConfig = {
30798
30807
  "manage_binding"
30799
30808
  ],
30800
30809
  subAgents: ["document-parser-benchmark"],
30801
- internalSubAgents: [agentReviewerConfig],
30802
30810
  middleware: [
30803
30811
  {
30804
30812
  id: "filesystem",
@@ -30883,9 +30891,96 @@ var agentArchitectConfig = {
30883
30891
  ]
30884
30892
  };
30885
30893
 
30886
- // src/agent_lattice/roomCoordinatorConfig.ts
30894
+ // src/agent_lattice/agentReviewerConfig.ts
30887
30895
  var import_protocols21 = require("@axiom-lattice/protocols");
30888
30896
 
30897
+ // src/agent_lattice/agentReviewerPrompt.ts
30898
+ var AGENT_REVIEWER_PROMPT = `# Agent Reviewer
30899
+
30900
+ You are an **Agent Reviewer** \u2014 a quality assurance specialist for AI agents. Your job is to review agent configurations for correctness and test them by invoking them with realistic messages.
30901
+
30902
+ ## Your Tools
30903
+
30904
+ - **get_agent** \u2014 Fetch an agent's full configuration
30905
+ - **list_agents** \u2014 List all agents in the workspace
30906
+ - **list_tools** \u2014 List all available tools that can be assigned to agents
30907
+ - **invoke_agent** \u2014 Test an agent by sending a message and getting its response. This spawns a completely fresh agent context \u2014 clean, isolated, and realistic.
30908
+
30909
+ ## Your Workflow
30910
+
30911
+ ### When asked to review an agent:
30912
+
30913
+ 1. Call **get_agent** to fetch the agent's config
30914
+ 2. Review the configuration for:
30915
+ - **Completeness** \u2014 Are name, description, prompt present? Is the prompt clear and actionable?
30916
+ - **Tool validity** \u2014 Do the referenced tools exist? Call **list_tools** to verify.
30917
+ - **Middleware correctness** \u2014 Is every middleware entry complete (id, type, name, description, enabled, config)?
30918
+ - **Sub-agent references** \u2014 Do referenced sub-agent IDs exist? Call **list_agents** to verify.
30919
+ - **Type consistency** \u2014 Does the agent type match its configuration shape? (e.g., PROCESSING must have edges, DEEP_AGENT may have subAgents)
30920
+ 3. Report your findings clearly. For each issue, state:
30921
+ - Severity (ERROR / WARNING / INFO)
30922
+ - What the problem is
30923
+ - How to fix it
30924
+
30925
+ ### When asked to test an agent:
30926
+
30927
+ 1. Call **get_agent** to understand the agent's purpose and expected behavior
30928
+ 2. Craft a realistic test message that exercises the agent's core responsibility \u2014 what would a real user say?
30929
+ 3. Call **invoke_agent(id, message)** to send the test message
30930
+ 4. Analyze the response:
30931
+ - **If result.__interrupt__ is present** (an array of interrupt objects): The agent hit a human-in-the-loop interrupt (e.g., ask_user_to_clarify or interrupt() in a workflow). This is NOT an error \u2014 it means the agent paused execution waiting for user feedback. Each interrupt has a value (the question/request) and optionally an id. Report this as: "The agent successfully paused and is waiting for user input: <describe the interrupt value>." The messages array may contain the agent's output up to the point of interruption.
30932
+ - **Otherwise, result.messages contains the agent's output**: The agent completed without interruption. Check:
30933
+ - Did the agent understand the request?
30934
+ - Was the response relevant and accurate?
30935
+ - Did the agent use the right tools?
30936
+ - Were there any errors or unexpected behaviors?
30937
+ - **If result.error is present**: The invocation itself failed (agent not found, compilation error, etc.). Report the error clearly.
30938
+ 5. Report your findings with the agent's actual response
30939
+
30940
+ ### When results are wrong:
30941
+
30942
+ If the agent's response is incorrect or unexpected:
30943
+ 1. Point out specifically what went wrong
30944
+ 2. Suggest what might need to change in the prompt or middleware config
30945
+ 3. Offer to re-test after fixes are applied
30946
+
30947
+ ## Important Rules
30948
+
30949
+ - **Always use invoke_agent for testing.** This is the ONLY way to test \u2014 it spawns a fresh agent context isolated from all other conversations.
30950
+ - **Be specific in your feedback.** Say "the agent failed to call the sql tool because the middleware config is missing databaseKeys" not "the agent didn't work."
30951
+ - **One agent at a time.** Focus on one agent per review or test request. Don't try to review multiple agents simultaneously.
30952
+ - **Be concise but thorough.** Cover all the checks, but present findings clearly without fluff.
30953
+ `;
30954
+
30955
+ // src/agent_lattice/agentReviewerConfig.ts
30956
+ var AGENT_REVIEWER_KEY = "agent-reviewer";
30957
+ var agentReviewerConfig = {
30958
+ key: AGENT_REVIEWER_KEY,
30959
+ name: "Agent Reviewer",
30960
+ description: "Review and test AI agents. Use this agent to check agent configurations for correctness and to test agents by invoking them with realistic messages.",
30961
+ type: import_protocols21.AgentType.REACT,
30962
+ prompt: AGENT_REVIEWER_PROMPT,
30963
+ tools: [
30964
+ "invoke_agent",
30965
+ "get_agent",
30966
+ "list_agents",
30967
+ "list_tools"
30968
+ ],
30969
+ middleware: [
30970
+ {
30971
+ id: "widget",
30972
+ type: "widget",
30973
+ name: "Widget",
30974
+ description: "Render interactive HTML widgets and SVG diagrams",
30975
+ enabled: true,
30976
+ config: {}
30977
+ }
30978
+ ]
30979
+ };
30980
+
30981
+ // src/agent_lattice/roomCoordinatorConfig.ts
30982
+ var import_protocols22 = require("@axiom-lattice/protocols");
30983
+
30889
30984
  // src/agent_lattice/roomCoordinatorPrompt.ts
30890
30985
  var ROOM_COORDINATOR_PROMPT = `You are the Room Coordinator. CRITICAL FIRST ACTION: Before any response or other action, call the \`skill\` tool with skill_name: "room-coordination" to load room-coordination and follow it. Never announce the skill load. If it fails, retry once, then stop and explicitly report that the required room-coordination skill could not be loaded.`;
30891
30986
 
@@ -30895,7 +30990,7 @@ var roomCoordinatorConfig = {
30895
30990
  key: ROOM_COORDINATOR_KEY,
30896
30991
  name: "Room Coordinator",
30897
30992
  description: "Manages project room conversations: replies when no bot is mentioned, coordinates room members, and organizes project work.",
30898
- type: import_protocols21.AgentType.DEEP_AGENT,
30993
+ type: import_protocols22.AgentType.DEEP_AGENT,
30899
30994
  prompt: ROOM_COORDINATOR_PROMPT,
30900
30995
  tools: [],
30901
30996
  middleware: [
@@ -31530,17 +31625,6 @@ var getAgentClient = (tenantId2, key5, options) => agentLatticeManager.initializ
31530
31625
  var getAgentClientAsync = async (tenantId2, key5, options) => agentLatticeManager.initializeClientAsync(tenantId2, key5, options);
31531
31626
  var createAgentClientFromAgentLattice = async (agentLattice, options) => await agentLatticeManager.createAgentClientFromConfig(agentLattice, options);
31532
31627
 
31533
- // src/agent_lattice/taskCapability.ts
31534
- function agentHasManagedTaskCapability(config) {
31535
- const middleware = config?.middleware;
31536
- const taskMiddleware = Array.isArray(middleware) ? middleware.find((entry) => entry?.type === "task" && entry?.enabled === true) : void 0;
31537
- if (!taskMiddleware) {
31538
- return false;
31539
- }
31540
- const allowedTools = taskMiddleware.allowedTools;
31541
- return allowedTools === void 0 || Array.isArray(allowedTools) && (allowedTools.length === 0 || allowedTools.includes("manage_task"));
31542
- }
31543
-
31544
31628
  // src/index.ts
31545
31629
  init_memory_lattice();
31546
31630
 
@@ -31639,7 +31723,7 @@ var getVectorStoreClient = (key5) => vectorStoreLatticeManager.getVectorStoreCli
31639
31723
 
31640
31724
  // src/logger_lattice/LoggerLatticeManager.ts
31641
31725
  init_BaseLatticeManager();
31642
- var import_protocols22 = require("@axiom-lattice/protocols");
31726
+ var import_protocols23 = require("@axiom-lattice/protocols");
31643
31727
 
31644
31728
  // src/logger_lattice/PinoLoggerClient.ts
31645
31729
  var import_pino = __toESM(require("pino"));
@@ -31861,11 +31945,11 @@ var LoggerLatticeManager = class _LoggerLatticeManager extends BaseLatticeManage
31861
31945
  if (client) {
31862
31946
  loggerClient = client;
31863
31947
  } else {
31864
- if (config.type === import_protocols22.LoggerType.PINO) {
31948
+ if (config.type === import_protocols23.LoggerType.PINO) {
31865
31949
  loggerClient = new PinoLoggerClient(config);
31866
- } else if (config.type === import_protocols22.LoggerType.CONSOLE) {
31950
+ } else if (config.type === import_protocols23.LoggerType.CONSOLE) {
31867
31951
  loggerClient = new ConsoleLoggerClient(config);
31868
- } else if (config.type === import_protocols22.LoggerType.CUSTOM) {
31952
+ } else if (config.type === import_protocols23.LoggerType.CUSTOM) {
31869
31953
  throw new Error(
31870
31954
  `Custom logger client must be provided. Please pass the client to registerLattice.`
31871
31955
  );
@@ -36127,7 +36211,7 @@ var LatticeEvalSuite = class {
36127
36211
  };
36128
36212
 
36129
36213
  // src/eval_lattice/LatticeEvalProject.ts
36130
- var import_protocols23 = require("@axiom-lattice/protocols");
36214
+ var import_protocols24 = require("@axiom-lattice/protocols");
36131
36215
  var import_messages8 = require("@langchain/core/messages");
36132
36216
  var import_uuid11 = require("uuid");
36133
36217
  var DEFAULT_CALIBRATION_PROBES = [
@@ -36183,7 +36267,7 @@ var LatticeEvalProject = class {
36183
36267
  key: this.judgeAgentKey,
36184
36268
  name: `Judge agent for ${this.project.projectName}`,
36185
36269
  description: "Judge agent for evaluating Lattice test cases",
36186
- type: import_protocols23.AgentType.REACT,
36270
+ type: import_protocols24.AgentType.REACT,
36187
36271
  prompt: "",
36188
36272
  modelKey: judgeModelKey
36189
36273
  };
@@ -36409,7 +36493,7 @@ function getMenuRegistry() {
36409
36493
  }
36410
36494
 
36411
36495
  // src/services/ProjectTaskReassignmentAuthorization.ts
36412
- var import_protocols24 = require("@axiom-lattice/protocols");
36496
+ var import_protocols25 = require("@axiom-lattice/protocols");
36413
36497
  var issued = /* @__PURE__ */ new WeakMap();
36414
36498
  function createProjectTaskReassignmentAuthorizer(policy, threadValidator) {
36415
36499
  return {
@@ -36442,7 +36526,7 @@ function inspectProjectTaskReassignmentAuthorization(authorization) {
36442
36526
  return snapshot ? { ...snapshot } : void 0;
36443
36527
  }
36444
36528
  function snapshotInput(input) {
36445
- const row = (0, import_protocols24.snapshotExactRecord)(input, [
36529
+ const row = (0, import_protocols25.snapshotExactRecord)(input, [
36446
36530
  "tenantId",
36447
36531
  "workspaceId",
36448
36532
  "projectId",
@@ -36485,7 +36569,7 @@ function snapshotInput(input) {
36485
36569
  }
36486
36570
 
36487
36571
  // src/services/TaskLifecycleNotifier.ts
36488
- var import_protocols25 = require("@axiom-lattice/protocols");
36572
+ var import_protocols26 = require("@axiom-lattice/protocols");
36489
36573
  var TaskLifecycleNotifier = class {
36490
36574
  constructor() {
36491
36575
  this.listeners = /* @__PURE__ */ new Set();
@@ -36529,7 +36613,7 @@ var TaskLifecycleNotifier = class {
36529
36613
  };
36530
36614
  var taskLifecycleNotifier = new TaskLifecycleNotifier();
36531
36615
  function snapshotNotification(value) {
36532
- const row = (0, import_protocols25.snapshotExactRecord)(value, [
36616
+ const row = (0, import_protocols26.snapshotExactRecord)(value, [
36533
36617
  "tenantId",
36534
36618
  "workspaceId",
36535
36619
  "projectId",
@@ -36561,7 +36645,7 @@ function nonempty(value) {
36561
36645
  }
36562
36646
 
36563
36647
  // src/services/ProjectTaskAdmissionNotifier.ts
36564
- var import_protocols26 = require("@axiom-lattice/protocols");
36648
+ var import_protocols27 = require("@axiom-lattice/protocols");
36565
36649
  var ProjectTaskAdmissionNotifier = class {
36566
36650
  /**
36567
36651
  * Creates a bounded Project task admission notifier.
@@ -36634,7 +36718,7 @@ var ProjectTaskAdmissionNotifier = class {
36634
36718
  };
36635
36719
  var projectTaskAdmissionNotifier = new ProjectTaskAdmissionNotifier();
36636
36720
  function snapshotNotification2(value) {
36637
- const row = (0, import_protocols26.snapshotExactRecord)(value, [
36721
+ const row = (0, import_protocols27.snapshotExactRecord)(value, [
36638
36722
  "tenantId",
36639
36723
  "workspaceId",
36640
36724
  "projectId",
@@ -36664,9 +36748,9 @@ function canonicalTimestamp(value) {
36664
36748
  }
36665
36749
 
36666
36750
  // src/services/ProjectMutationMutex.ts
36667
- var import_protocols27 = require("@axiom-lattice/protocols");
36751
+ var import_protocols28 = require("@axiom-lattice/protocols");
36668
36752
  function projectKey(scope) {
36669
- const row = (0, import_protocols27.snapshotExactRecord)(scope, ["tenantId", "projectId"]);
36753
+ const row = (0, import_protocols28.snapshotExactRecord)(scope, ["tenantId", "projectId"]);
36670
36754
  if (!row || typeof row.tenantId !== "string" || !row.tenantId || row.tenantId.includes("\0") || typeof row.projectId !== "string" || !row.projectId || row.projectId.includes("\0")) {
36671
36755
  throw new Error("Invalid Project mutation scope");
36672
36756
  }
@@ -36741,7 +36825,7 @@ function createTrustedAgentMessageDispatcher(resolve4) {
36741
36825
 
36742
36826
  // src/services/RoomAgentMessageService.ts
36743
36827
  var import_crypto8 = require("crypto");
36744
- var import_protocols28 = require("@axiom-lattice/protocols");
36828
+ var import_protocols29 = require("@axiom-lattice/protocols");
36745
36829
  var INPUT_KEYS = [
36746
36830
  "tenantId",
36747
36831
  "workspaceId",
@@ -36861,7 +36945,7 @@ var RoomAgentMessageService = class {
36861
36945
  function exactValues(value, required, optional = []) {
36862
36946
  try {
36863
36947
  if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
36864
- return (0, import_protocols28.snapshotExactRecord)(value, required, optional);
36948
+ return (0, import_protocols29.snapshotExactRecord)(value, required, optional);
36865
36949
  } catch {
36866
36950
  return void 0;
36867
36951
  }
@@ -36913,7 +36997,7 @@ function sourceOriginMatches(author, source) {
36913
36997
  return false;
36914
36998
  }
36915
36999
  function snapshotMentions(value) {
36916
- const rows = (0, import_protocols28.snapshotExactArray)(value);
37000
+ const rows = (0, import_protocols29.snapshotExactArray)(value);
36917
37001
  if (!rows) return void 0;
36918
37002
  const mentions = [];
36919
37003
  for (const row of rows) {
@@ -36941,7 +37025,7 @@ function snapshotCanonical(value, expected) {
36941
37025
  const author = exactValues(values?.author, ["type", "membershipId", "assistantId"]);
36942
37026
  const content = exactValues(values?.content, ["type", "text"]);
36943
37027
  const createdAt = date(values?.createdAt);
36944
- if (!values || !createdAt || typeof values.id !== "string" || values.id.length === 0 || (0, import_protocols28.snapshotExactArray)(values.mentions)?.length !== 0 || author?.type !== "bot" || expected.author.type !== "bot" || author.membershipId !== expected.author.membershipId || author.assistantId !== expected.author.assistantId || content?.type !== "text" || content.text !== expected.content.text || values.tenantId !== expected.tenantId || values.workspaceId !== expected.workspaceId || values.projectId !== expected.projectId || values.roomId !== expected.roomId || values.replyToMessageId !== expected.replyToMessageId || values.source !== "agent" || values.sourceId !== expected.sourceId || values.idempotencyKey !== expected.idempotencyKey) return void 0;
37028
+ if (!values || !createdAt || typeof values.id !== "string" || values.id.length === 0 || (0, import_protocols29.snapshotExactArray)(values.mentions)?.length !== 0 || author?.type !== "bot" || expected.author.type !== "bot" || author.membershipId !== expected.author.membershipId || author.assistantId !== expected.author.assistantId || content?.type !== "text" || content.text !== expected.content.text || values.tenantId !== expected.tenantId || values.workspaceId !== expected.workspaceId || values.projectId !== expected.projectId || values.roomId !== expected.roomId || values.replyToMessageId !== expected.replyToMessageId || values.source !== "agent" || values.sourceId !== expected.sourceId || values.idempotencyKey !== expected.idempotencyKey) return void 0;
36945
37029
  return {
36946
37030
  id: values.id,
36947
37031
  tenantId: expected.tenantId,
@@ -36960,7 +37044,7 @@ function snapshotCanonical(value, expected) {
36960
37044
  }
36961
37045
 
36962
37046
  // src/services/ProjectTaskOwnerPolicy.ts
36963
- var import_protocols29 = require("@axiom-lattice/protocols");
37047
+ var import_protocols30 = require("@axiom-lattice/protocols");
36964
37048
  var MEMBERSHIP_REQUIRED_KEYS = [
36965
37049
  "id",
36966
37050
  "tenantId",
@@ -37069,7 +37153,7 @@ function sameRosterScope(membership, scope) {
37069
37153
  return membership.tenantId === scope.tenantId && membership.workspaceId === scope.workspaceId && membership.projectId === scope.projectId && membership.roomId === scope.roomId;
37070
37154
  }
37071
37155
  function snapshotScope(value, keys) {
37072
- const values = (0, import_protocols29.snapshotExactRecord)(value, keys);
37156
+ const values = (0, import_protocols30.snapshotExactRecord)(value, keys);
37073
37157
  if (!values || keys.some((key5) => !isNonemptyString(values[key5]))) return void 0;
37074
37158
  return values;
37075
37159
  }
@@ -37081,7 +37165,7 @@ function projectScope(scope, keys) {
37081
37165
  function snapshotMembership2(value) {
37082
37166
  try {
37083
37167
  if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
37084
- const values = (0, import_protocols29.snapshotExactRecord)(value, MEMBERSHIP_REQUIRED_KEYS, ["responsibility"]);
37168
+ const values = (0, import_protocols30.snapshotExactRecord)(value, MEMBERSHIP_REQUIRED_KEYS, ["responsibility"]);
37085
37169
  if (!values) return void 0;
37086
37170
  const joinedAt = snapshotDate(values.joinedAt);
37087
37171
  const updatedAt = snapshotDate(values.updatedAt);
@@ -37120,7 +37204,7 @@ function snapshotDate(value) {
37120
37204
 
37121
37205
  // src/services/TaskLifecycleService.ts
37122
37206
  var import_node_crypto8 = require("crypto");
37123
- var import_protocols30 = require("@axiom-lattice/protocols");
37207
+ var import_protocols31 = require("@axiom-lattice/protocols");
37124
37208
 
37125
37209
  // src/middlewares/taskBelief.ts
37126
37210
  var import_node_crypto7 = require("crypto");
@@ -37191,7 +37275,7 @@ async function applyBeliefImpacts(taskStore, owner, impacts) {
37191
37275
  if (impacts.length === 0) return { description: owner.description ?? "" };
37192
37276
  let current = owner;
37193
37277
  for (let attempt = 0; attempt < BELIEF_OWNER_WRITE_ATTEMPTS; attempt += 1) {
37194
- const parsed = (0, import_protocols30.parseTaskBeliefState)(current.description ?? "");
37278
+ const parsed = (0, import_protocols31.parseTaskBeliefState)(current.description ?? "");
37195
37279
  const existingTable = parsed.success;
37196
37280
  if (!existingTable && parsed.code !== "MISSING_BELIEF_STATE") {
37197
37281
  return { description: current.description ?? "", task: current };
@@ -37208,7 +37292,7 @@ async function applyBeliefImpacts(taskStore, owner, impacts) {
37208
37292
  changed = true;
37209
37293
  }
37210
37294
  if (!changed) return { description: current.description ?? "", task: current };
37211
- const description = (0, import_protocols30.replaceTaskBeliefState)(current.description ?? "", { entries });
37295
+ const description = (0, import_protocols31.replaceTaskBeliefState)(current.description ?? "", { entries });
37212
37296
  try {
37213
37297
  const updated = await taskStore.updateIfStatusAndUpdatedAt(
37214
37298
  current.tenantId,
@@ -37233,8 +37317,8 @@ async function applyBeliefImpacts(taskStore, owner, impacts) {
37233
37317
  return { description: current.description ?? "", task: current };
37234
37318
  }
37235
37319
  function parseExternalDependency(value) {
37236
- const row = (0, import_protocols30.snapshotExactRecord)(value, ["type", "summary", "requestedAt", "dependencyTaskIds"]);
37237
- const ids = (0, import_protocols30.snapshotExactArray)(row?.dependencyTaskIds);
37320
+ const row = (0, import_protocols31.snapshotExactRecord)(value, ["type", "summary", "requestedAt", "dependencyTaskIds"]);
37321
+ const ids = (0, import_protocols31.snapshotExactArray)(row?.dependencyTaskIds);
37238
37322
  if (!row || row.type !== "external_dependency" || typeof row.summary !== "string" || !row.summary.trim() || typeof row.requestedAt !== "string" || !Number.isFinite(Date.parse(row.requestedAt)) || !ids || ids.length === 0 || ids.some((id) => typeof id !== "string" || !id.trim())) return void 0;
37239
37323
  const trimmed = ids.map((id) => id.trim());
37240
37324
  if (new Set(trimmed).size !== trimmed.length) return void 0;
@@ -37321,7 +37405,7 @@ function beliefStateOf(value) {
37321
37405
  function sameBeliefOwner(left, right) {
37322
37406
  const leftIdentity = { ...left, beliefState: void 0 };
37323
37407
  const rightIdentity = { ...right, beliefState: void 0 };
37324
- return canonicalJson(leftIdentity) === canonicalJson(rightIdentity) && (0, import_protocols30.taskBeliefStatesEqual)(left.beliefState, right.beliefState);
37408
+ return canonicalJson(leftIdentity) === canonicalJson(rightIdentity) && (0, import_protocols31.taskBeliefStatesEqual)(left.beliefState, right.beliefState);
37325
37409
  }
37326
37410
  function sameBeliefOwnerIdentity(left, right) {
37327
37411
  return canonicalJson({ ...left, beliefState: void 0 }) === canonicalJson({ ...right, beliefState: void 0 });
@@ -37469,7 +37553,7 @@ function changedBeliefKeys(left, right) {
37469
37553
  return [...keys].filter((key5) => {
37470
37554
  const oldEntry = leftByKey.get(key5);
37471
37555
  const newEntry = rightByKey.get(key5);
37472
- return !oldEntry || !newEntry || !(0, import_protocols30.taskBeliefStatesEqual)(
37556
+ return !oldEntry || !newEntry || !(0, import_protocols31.taskBeliefStatesEqual)(
37473
37557
  { entries: [oldEntry] },
37474
37558
  { entries: [newEntry] }
37475
37559
  );
@@ -38490,10 +38574,10 @@ var TaskLifecycleService = class {
38490
38574
  const description = preserveTargetContract(existing.description ?? "", input.description);
38491
38575
  const pending = existing.context?.belief_reconciliation_audit;
38492
38576
  const pendingAudit = pending && typeof pending === "object" ? pending : void 0;
38493
- const oldBelief = (0, import_protocols30.parseTaskBeliefState)(existing.description ?? "");
38494
- const newBelief = (0, import_protocols30.parseTaskBeliefState)(description);
38577
+ const oldBelief = (0, import_protocols31.parseTaskBeliefState)(existing.description ?? "");
38578
+ const newBelief = (0, import_protocols31.parseTaskBeliefState)(description);
38495
38579
  if (!newBelief.success) return failure2("INVALID_BELIEF_TABLE", newBelief.message);
38496
- if (!pendingAudit && description === (existing.description ?? "") && oldBelief.success && (0, import_protocols30.taskBeliefStatesEqual)(oldBelief.state, newBelief.state)) {
38580
+ if (!pendingAudit && description === (existing.description ?? "") && oldBelief.success && (0, import_protocols31.taskBeliefStatesEqual)(oldBelief.state, newBelief.state)) {
38497
38581
  return { success: true, task: existing, mutated: false };
38498
38582
  }
38499
38583
  const canonicalNew = newBelief.state.entries;
@@ -38624,7 +38708,7 @@ var TaskLifecycleService = class {
38624
38708
  }
38625
38709
  beliefOwnerSnapshot(task, parent) {
38626
38710
  const owner = parent?.ownerType === "agent" ? parent : task;
38627
- const parsed = (0, import_protocols30.parseTaskBeliefState)(owner.description ?? "");
38711
+ const parsed = (0, import_protocols31.parseTaskBeliefState)(owner.description ?? "");
38628
38712
  const beliefState = parsed.success ? parsed.state : { entries: [] };
38629
38713
  if (parent?.ownerType === "agent") {
38630
38714
  return {
@@ -39396,7 +39480,7 @@ function createTaskLifecycleService(deps) {
39396
39480
  // src/services/TaskExecutionService.ts
39397
39481
  var import_node_crypto9 = require("crypto");
39398
39482
  var import_node_util3 = require("util");
39399
- var import_protocols31 = require("@axiom-lattice/protocols");
39483
+ var import_protocols32 = require("@axiom-lattice/protocols");
39400
39484
 
39401
39485
  // src/services/taskErrorMessage.ts
39402
39486
  var MAX_EXECUTION_ERROR_MESSAGE_LENGTH = 1024;
@@ -39538,6 +39622,33 @@ function createTaskRunBridge(deps) {
39538
39622
  transitioned: true
39539
39623
  };
39540
39624
  }
39625
+ if (task.status === "interrupted" || task.status === "failed") {
39626
+ if (task.ownerType !== "agent") {
39627
+ return {
39628
+ success: false,
39629
+ code: "AGENT_TASK_REQUIRED",
39630
+ error: "Lifecycle transitions only support agent tasks."
39631
+ };
39632
+ }
39633
+ const result = task.status === "interrupted" ? await deps.lifecycle.resumeInterruption({ tenantId: tenantId2, taskId: task.id, actor, threadId }) : await deps.lifecycle.retryTask({ tenantId: tenantId2, taskId: task.id, actor, threadId });
39634
+ if (!result.success) {
39635
+ return {
39636
+ success: false,
39637
+ code: result.code,
39638
+ error: result.error,
39639
+ ...result.hint !== void 0 ? { hint: result.hint } : {},
39640
+ ...result.retryable !== void 0 ? { retryable: result.retryable } : {},
39641
+ ...result.warnings !== void 0 ? { warnings: result.warnings } : {}
39642
+ };
39643
+ }
39644
+ return {
39645
+ success: true,
39646
+ task: result.task,
39647
+ warnings: result.warnings ?? [],
39648
+ mutated: true,
39649
+ transitioned: true
39650
+ };
39651
+ }
39541
39652
  return { success: true, task, warnings, mutated: false, transitioned: false };
39542
39653
  },
39543
39654
  async settleRun(input) {
@@ -39691,48 +39802,18 @@ async function prepareTaskDispatch(deps, input) {
39691
39802
  }
39692
39803
  };
39693
39804
  }
39694
- if (task.status !== "pending") {
39805
+ if (task.status !== "pending" && task.status !== "interrupted" && task.status !== "failed") {
39695
39806
  return {
39696
39807
  kind: "failure",
39697
39808
  failure: {
39698
39809
  success: false,
39699
39810
  code: "TASK_NOT_DISPATCHABLE",
39700
- error: "Only a `pending` task can be dispatched.",
39701
- hint: `This task is '${task.status}'. Only a \`pending\` task dispatches: resolve it (resume an interruption / cancel a stuck run) or create a new task.`,
39811
+ error: "Only a `pending`, `interrupted`, or `failed` task can be dispatched.",
39812
+ hint: `This task is '${task.status}'. A \`pending\` task starts, an \`interrupted\` task resumes, and a \`failed\` task retries. A task already \`in_progress\` is held by a live run; terminal tasks cannot be dispatched.`,
39702
39813
  taskId: task.id
39703
39814
  }
39704
39815
  };
39705
39816
  }
39706
- if (deps.resolveTaskCapability) {
39707
- let targetCapable;
39708
- try {
39709
- targetCapable = await deps.resolveTaskCapability(input.tenantId, input.agentId);
39710
- } catch (error) {
39711
- return {
39712
- kind: "failure",
39713
- failure: {
39714
- success: false,
39715
- code: "AGENT_RESOLUTION_FAILED",
39716
- error: errorMessage(error),
39717
- taskId: task.id,
39718
- agentId: input.agentId
39719
- }
39720
- };
39721
- }
39722
- if (!targetCapable) {
39723
- return {
39724
- kind: "failure",
39725
- failure: {
39726
- success: false,
39727
- code: "TASK_DELEGATE_TARGET_NOT_CAPABLE",
39728
- taskId: task.id,
39729
- agentId: input.agentId,
39730
- error: `Agent '${input.agentId}' cannot manage tasks, so a dispatched TaskItem could never be completed.`,
39731
- hint: "Delegate to an agent with the `task` middleware (exposes `manage_task`), or do the work inline and update the task with `manage_task set_status`."
39732
- }
39733
- };
39734
- }
39735
- }
39736
39817
  const managedAuthority = executionAuthorityRegistry.resolve(task);
39737
39818
  if (managedAuthority) {
39738
39819
  if (managedAuthority.onManagedStart) {
@@ -40144,6 +40225,7 @@ function toJsonSafeValue(value) {
40144
40225
  }
40145
40226
 
40146
40227
  // src/services/TaskExecutionService.ts
40228
+ var import_langgraph14 = require("@langchain/langgraph");
40147
40229
  function persistedExecutionObservation(item, attempted) {
40148
40230
  const detail = item.detail;
40149
40231
  const persistedObservation = toJsonSafeValue(detail?.observation);
@@ -40190,9 +40272,10 @@ function createTaskExecutionService(deps) {
40190
40272
  const run = async (plan) => {
40191
40273
  const { tenantId: tenantId2, agentId, actor, parent, completedDependencies } = plan;
40192
40274
  let task = plan.task;
40275
+ const persistedThreadId = (task.status === "interrupted" || task.status === "failed") && typeof task.context?.thread_id === "string" && task.context.thread_id.trim() !== "" ? task.context.thread_id : void 0;
40193
40276
  let threadId;
40194
40277
  try {
40195
- threadId = (deps.createThreadId ?? import_node_crypto9.randomUUID)();
40278
+ threadId = persistedThreadId ?? (deps.createThreadId ?? import_node_crypto9.randomUUID)();
40196
40279
  } catch (error) {
40197
40280
  return {
40198
40281
  success: false,
@@ -40203,7 +40286,7 @@ function createTaskExecutionService(deps) {
40203
40286
  };
40204
40287
  }
40205
40288
  const eventKey = `execution-result:${threadId}`;
40206
- if (!(0, import_protocols31.isExecutionResultEventKey)(eventKey)) {
40289
+ if (!(0, import_protocols32.isExecutionResultEventKey)(eventKey)) {
40207
40290
  return {
40208
40291
  success: false,
40209
40292
  code: "INVALID_EXECUTION_RESULT_ID",
@@ -40231,12 +40314,12 @@ function createTaskExecutionService(deps) {
40231
40314
  task = transfer.task;
40232
40315
  }
40233
40316
  const warnings = [];
40234
- if (task.status === "pending") {
40317
+ if (task.status === "pending" || task.status === "interrupted" || task.status === "failed") {
40235
40318
  const begun = await bridge.beginRun({
40236
40319
  tenantId: tenantId2,
40237
40320
  task,
40238
40321
  actor,
40239
- threadId: plan.callerThreadId
40322
+ threadId
40240
40323
  });
40241
40324
  if (!begun.success) return { ...begun, taskId: task.id };
40242
40325
  if (!begun.mutated || !begun.transitioned) {
@@ -40265,6 +40348,19 @@ function createTaskExecutionService(deps) {
40265
40348
  try {
40266
40349
  observation = toJsonSafeValue(await deps.invokeAgent(invocation));
40267
40350
  } catch (error) {
40351
+ if (error instanceof import_langgraph14.GraphInterrupt) {
40352
+ try {
40353
+ await bridge.settleRun({
40354
+ tenantId: tenantId2,
40355
+ task,
40356
+ actor,
40357
+ threadId,
40358
+ outcome: { kind: "interrupted", summary: "Awaiting user input" }
40359
+ });
40360
+ } catch {
40361
+ }
40362
+ throw error;
40363
+ }
40268
40364
  const normalizationError = error instanceof JsonSafeValueError;
40269
40365
  const message = normalizationError ? `${error.code}: ${error.message}` : errorMessage(error);
40270
40366
  const failureCode = normalizationError ? "EXECUTION_RESULT_NORMALIZATION_FAILED" : "AGENT_INVOCATION_FAILED";
@@ -40395,6 +40491,76 @@ function createTaskExecutionService(deps) {
40395
40491
  };
40396
40492
  }
40397
40493
 
40494
+ // src/services/TaskRunRegistry.ts
40495
+ var TaskRunRegistry = class {
40496
+ constructor() {
40497
+ this.live = /* @__PURE__ */ new Set();
40498
+ }
40499
+ /** Marks a task as currently held by a live run. */
40500
+ register(taskId) {
40501
+ this.live.add(taskId);
40502
+ }
40503
+ /** Removes a task once its run has ended. */
40504
+ unregister(taskId) {
40505
+ this.live.delete(taskId);
40506
+ }
40507
+ /** Reports whether a task is currently held by a live run. */
40508
+ isLive(taskId) {
40509
+ return this.live.has(taskId);
40510
+ }
40511
+ /** Returns a snapshot of the live task ids (diagnostics/tests). */
40512
+ snapshot() {
40513
+ return [...this.live];
40514
+ }
40515
+ /** Clears all live entries (tests / process teardown). */
40516
+ clear() {
40517
+ this.live.clear();
40518
+ }
40519
+ };
40520
+ var taskRunRegistry = new TaskRunRegistry();
40521
+ async function reconcileStaleTaskRuns(input) {
40522
+ const registry4 = input.registry ?? taskRunRegistry;
40523
+ const converged = [];
40524
+ for (const task of input.tasks) {
40525
+ if (task.status !== "in_progress" || registry4.isLive(task.id)) continue;
40526
+ const result = await input.lifecycle.interruptTask({
40527
+ tenantId: task.tenantId,
40528
+ taskId: task.id,
40529
+ actor: input.actor,
40530
+ type: "missing_input",
40531
+ summary: "Run is no longer executing this task (no_executor)"
40532
+ });
40533
+ if (result.success) converged.push(task.id);
40534
+ }
40535
+ return { converged };
40536
+ }
40537
+ async function reconcileTenantTaskRuns(input) {
40538
+ const tasks = await input.taskStore.list({ tenantId: input.tenantId, status: "in_progress" });
40539
+ const candidates = input.include ? tasks.filter(input.include) : tasks;
40540
+ return reconcileStaleTaskRuns({
40541
+ tasks: candidates,
40542
+ actor: input.actor,
40543
+ lifecycle: input.lifecycle,
40544
+ ...input.registry ? { registry: input.registry } : {}
40545
+ });
40546
+ }
40547
+ async function reconcileAllTenantTaskRuns(input) {
40548
+ const tenants = await input.tenantStore.listTenants();
40549
+ const converged = [];
40550
+ for (const tenant of tenants) {
40551
+ const result = await reconcileTenantTaskRuns({
40552
+ tenantId: tenant.id,
40553
+ actor: input.actor,
40554
+ taskStore: input.taskStore,
40555
+ lifecycle: input.lifecycle,
40556
+ ...input.registry ? { registry: input.registry } : {},
40557
+ ...input.include ? { include: input.include } : {}
40558
+ });
40559
+ converged.push(...result.converged);
40560
+ }
40561
+ return { converged };
40562
+ }
40563
+
40398
40564
  // src/index.ts
40399
40565
  var Protocols = __toESM(require("@axiom-lattice/protocols"));
40400
40566
 
@@ -41759,7 +41925,7 @@ function createMetricsRuntimeTool(params) {
41759
41925
  // src/middlewares/semanticMetricsModelingSkill.ts
41760
41926
  var CONTENT = `---
41761
41927
  name: semantic-metrics-modeling
41762
- description: Builder policy for turning a granted datasource into published semantic tables and metrics. Governs goal agreement, FEP verification, persistent-task tracking, belief-state updates, and continuous refinement until acceptance evidence is met.
41928
+ description: Builder policy for turning a granted datasource into published semantic tables and metrics. Defines the orchestrator/worker contract \u2014 the orchestrator agrees goals, proposes the model, plans persistent tasks, and delegates execution to general-purpose workers with self-contained briefs; workers execute one bound brief each. Governs goal agreement, delegation, FEP verification, belief-state reconciliation, and continuous refinement until acceptance evidence is met.
41763
41929
  ---
41764
41930
 
41765
41931
  # Semantic Metrics Modeling
@@ -41768,6 +41934,15 @@ You are the Semantic Metrics Builder. You turn physical datasource tables into r
41768
41934
  **semantic tables** and **business metrics** on a Metrics Server (\`/api/v1\`). Work is
41769
41935
  governed, observable, and continuously refined \u2014 never a one-shot guess.
41770
41936
 
41937
+ You always operate in exactly ONE of two roles:
41938
+
41939
+ - **Orchestrator** (default; no \`## Bound Task\` section in your context): you face the
41940
+ user. You agree the Goal Model, propose the model, plan persistent tasks, dispatch
41941
+ execution to workers, reconcile belief, and own every user interaction.
41942
+ - **Worker** (your context contains a \`## Bound Task\` section naming you executor of one
41943
+ task): you execute that task's brief only. See **Worker mode** \u2014 its rules override the
41944
+ orchestrator policy.
41945
+
41771
41946
  ## Goal Model
41772
41947
 
41773
41948
  Before any exploration or publishing, agree with the user on:
@@ -41776,16 +41951,17 @@ Before any exploration or publishing, agree with the user on:
41776
41951
  - **Usable state**: observable conditions that make the published meta useful.
41777
41952
  - **Scope**: which datasource/tables, and what is explicitly out of scope.
41778
41953
  - **Acceptance evidence**: objective criteria (e.g. "metric \`sell_in_nes\` returns sane monthly
41779
- totals for 2024") used to decide success.
41954
+ totals for 2024") used to decide success. Verbatim acceptance evidence is required later by
41955
+ the Verify dispatch \u2014 record it exactly.
41780
41956
  Do not guess material goals, consumers, or acceptance evidence.
41781
41957
 
41782
41958
  **Vague requests** (e.g. "build the metric layer") \u2014 do not act and do not ask blind questions.
41783
- First run cheap read-only observations (\`list_datasources\`, \`get_grants\`, \`list_tables\`,
41784
- \`list_metrics\`) to see what is authorized and already published, then ask focused questions that
41785
- carry that context and offer concrete options (which datasource/tables, which business questions,
41786
- extend existing metrics or build new). Every clarification question must include enough context and
41787
- options for the user to answer in one step. Only after the user picks a direction does the Goal
41788
- Model become confirmable \u2014 record it, create the task, and proceed to the workflow.
41959
+ First run cheap read-only observations (\`list_datasources\`, \`get_grants\`, \`list_metrics\`) to
41960
+ see what is authorized and already published, then ask focused questions that carry that
41961
+ context and offer concrete options (which datasource/tables, which business questions, extend
41962
+ existing metrics or build new). Every clarification question must include enough context and
41963
+ options for the user to answer in one step. Only after the user picks a direction does the
41964
+ Goal Model become confirmable \u2014 record it, create the task, and proceed to the workflow.
41789
41965
 
41790
41966
  ## Target model and quality bar
41791
41967
 
@@ -41823,19 +41999,152 @@ Apply this loop to every step (explore, publish table, publish metric, verify):
41823
41999
  - **Decide**: classify as **RETAIN** (confirmed), **REVERT** (rejected), or **INCONCLUSIVE**.
41824
42000
  - **Reconcile**: update the belief state and plan, then repeat while decision-relevant action remains.
41825
42001
 
41826
- Renew user approval when a foundational assumption or the goal changes.
42002
+ The orchestrator applies the loop **between dispatches** (each observation updates the plan
42003
+ before the next brief is written). A worker applies it **inside its brief**.
41827
42004
 
41828
- ## Task Policy
41829
-
41830
- Use persistent TaskItems as your planning surface. Do not use \`write_todos\`. Create a child task
41831
- only for an independently verifiable outcome (e.g. "publish table meta for \`hankel_distr_sell_in\`").
41832
- Exploration queries and tool calls are activities inside the active task. See the
41833
- \`task-definition\` skill for \`manage_task\` operation details.
41834
-
41835
- ## Belief State
42005
+ Renew user approval when a foundational assumption or the goal changes.
41836
42006
 
41837
- Record your modeling beliefs in the active task's description under a \`## Belief State\` section as
41838
- a Markdown table:
42007
+ ## Orchestrator: Task Policy
42008
+
42009
+ You are an orchestrator, not the hands. Exactly five duties:
42010
+
42011
+ 1. **Goal Model agreement** \u2014 including cheap read-only observation for vague requests
42012
+ (\`list_datasources\`, \`get_grants\`, \`list_metrics\`). This is your ONLY inline datasource
42013
+ access; do not use \`write_todos\`.
42014
+ 2. **Propose the model** and get user approval.
42015
+ 3. **Task tree** \u2014 parent task = contract + \`## Belief State\`; child tasks = independently
42016
+ verifiable outcomes (explore / publish-table / publish-metric / verify), one per dispatch.
42017
+ See the \`task-definition\` skill for \`manage_task\` operation details.
42018
+ 4. **Dispatch and collect** \u2014 \`task { taskId, agentId: "general-purpose" }\`; read the
42019
+ observation; record the \`executionResultId\` on the child task with \`manage_task update\` or
42020
+ \`add_activity\`; reconcile \`## Belief State\`.
42021
+ 5. **HITL** \u2014 every \`ask_user_to_clarify\` is yours. Workers never ask the user.
42022
+
42023
+ Hard rules:
42024
+ - Never run exploration SQL inline. Never build or publish payloads inline (sole exception:
42025
+ the Publish/Verify inline fallback below).
42026
+ - **Belief State: you are the sole writer.** Workers return findings in their results; you
42027
+ update \`## Belief State\` via \`manage_task update\` on the parent task. Workers never touch it.
42028
+ - **Dispatch serially** \u2014 one worker at a time; reconcile belief before writing the next brief.
42029
+ - **Distinguish dispatch failure from worker outcome**: a dispatch failure is a tool-level
42030
+ error where the worker never ran. A worker returning INCONCLUSIVE or failed is a normal FEP
42031
+ input \u2014 reconcile it, never treat it as a dispatch failure.
42032
+
42033
+ Dispatch failure handling (worker never ran, tool error):
42034
+ 1. Retry the dispatch once (recreate the child task if the store reports it unusable).
42035
+ 2. **Explore: never execute inline.** If the retry fails, stop and ask the user via
42036
+ \`ask_user_to_clarify\` (report what is blocked and why).
42037
+ 3. **Publish/Verify: after a failed retry you MAY execute inline** to preserve delivery \u2014
42038
+ the payload decisions are already in your context. Follow the same gate self-check as a
42039
+ worker would.
42040
+
42041
+ ## Delegation Protocol
42042
+
42043
+ Mechanics every brief must be designed around:
42044
+
42045
+ - A worker sees ONLY: its task title + description, the parent task's description, and the
42046
+ results of completed dependency tasks. It does NOT see this conversation.
42047
+ - Therefore **every brief is self-contained**: all decisions, boundaries, and evidence travel
42048
+ inside the task description.
42049
+ - Dispatch is synchronous: \`task { taskId, agentId: "general-purpose" }\` returns an
42050
+ observation (the worker's final message, truncated at ~8k chars) and an
42051
+ \`executionResultId\`. Record the id on the child task (\`manage_task update\`/\`add_activity\`).
42052
+ - The worker settles its own task (\`set_status\` with \`result\`/\`failureReason\`). Read both the
42053
+ observation and the task result before reconciling.
42054
+
42055
+ Write briefs as field checklists adapted to the situation \u2014 decisions and evidence, not prose.
42056
+ The worker assembles payloads from the reference sections of this skill; the brief carries the
42057
+ approved decisions, never full payload JSON.
42058
+
42059
+ ### Explore brief (\`Explore <schema>.<table> for <goal>\`)
42060
+
42061
+ Required fields:
42062
+ 1. Datasource: serverKey/connectionKey + datasourceId (and \`runConfig.metricsDataSource\` preset if present).
42063
+ 2. Grant boundary: the allowed schema + table pattern from \`get_grants\`, verbatim; never probe outside it.
42064
+ 3. Target table(s): schema-qualified names; one brief per table.
42065
+ 4. Question list: per column (or column group) \u2014 business meaning candidates, expected role
42066
+ (dimension | measure | time), net/gross, grain.
42067
+ 5. Method: read-only SELECT/WITH only; use the \`references/metadata-sql.md\` templates
42068
+ (columns, sample rows, value distribution); cap sample size (e.g. \u22641000 rows, \u226450 groups).
42069
+ Prefer targeted queries over bulk catalog dumps. A large single read (e.g. full
42070
+ \`read_semantic_catalog\` detail) is allowed \u2014 your isolation protects the orchestrator's
42071
+ context \u2014 but findings must be compressed to conclusion level.
42072
+ 6. Output contract: structured findings \u2014 per column: observed type, sample values,
42073
+ distribution highlights, role candidate + evidence, \`INCONCLUSIVE\` marking for unresolved
42074
+ semantics.
42075
+ 7. Invariants: read-only only; grant boundary absolute; never guess \u2014 \`INCONCLUSIVE\` instead.
42076
+ 8. Closing: settle the task (\`set_status completed\`, result = findings summary); final message
42077
+ = the structured findings (it becomes the observation).
42078
+
42079
+ ### Publish-table brief (\`Publish table meta <objectKey>\`)
42080
+
42081
+ Required fields:
42082
+ 1. Approved decisions: full column list (name/type/role/label/description), grain,
42083
+ displayName/docType/shortDesc/sourceSystem/sourceTables.
42084
+ 2. Identity: \`objectKey\` (stable, unique per datasource) + \`schemaName.tableName\`.
42085
+ 3. Assembly instruction: full \`table_view_detail\` payload per **Publish table meta** below,
42086
+ plus the identity twin (\`table_catalog\`, same \`objectKey\`), plus an EXACT \`accessGrant\`
42087
+ matching the tenant grant.
42088
+ 4. Gate self-check: publish gate items 1\u20133 \u2014 do not publish if any fails.
42089
+ 5. Publish via \`metrics_meta_tool\` \`create_table\`; then \`get_meta\` to confirm visibility.
42090
+ 6. Mistakes: fix with \`update_table\`; never leave a partial publish.
42091
+ 7. Invariants: EXACT patternType; every column tagged and labeled.
42092
+ 8. Closing: settle the task; final message = receipts (objectKeys + get_meta confirmation).
42093
+
42094
+ ### Publish-metric brief (\`Publish metric <objectKey>\`)
42095
+
42096
+ Required fields (per metric):
42097
+ 1. Identity: \`objectKey\` = \`metric_name\`, display_name, domain, description, data_type, format.
42098
+ 2. Source: schema-qualified \`source.table_view\` (must match the published table meta).
42099
+ 3. Calculation: aggregate (\`aggregation\` + \`measure\`, with the confirmed business meaning) or
42100
+ derived (ratio references / formula) \u2014 exactly as approved in Propose the model.
42101
+ 4. \`supported_dimensions\`: array of objects \`{dim_id, field_name, label, data_type}\`,
42102
+ including the time dimension.
42103
+ 5. \`default_time_context\`: time_dimension + granularity + supported_grains (+ window if requested).
42104
+ 6. \`ai_agent_context\`: polarity, synonyms, human_readable_explanation.
42105
+ 7. Assembly instruction: \`metric_index\` then \`metric_detail\` in the FULL runtime style per
42106
+ **Publish metric meta** below; publish via \`metrics_meta_tool\` \`create_metric\`.
42107
+ 8. Gate self-check + smoke \`query\` (one grouping by the default time grain) + \`get_meta\`
42108
+ receipts; closing = settle the task, final message = receipts.
42109
+
42110
+ ### Verify brief (\`Verify <metric> against acceptance evidence\`)
42111
+
42112
+ Required fields:
42113
+ 1. Acceptance evidence verbatim (from the Goal Model).
42114
+ 2. Queries to run (semantic \`query\` via \`metrics_runtime_tool\`) and the expected shape/values.
42115
+ 3. Report contract: PASS/FAIL verdict + actual vs expected; on FAIL include the differences
42116
+ and a suspicion list \u2014 do NOT modify meta (fixes are new Publish tasks).
42117
+ 4. Invariants: read-only only.
42118
+ 5. Closing: settle the task; final message = the verdict summary.
42119
+
42120
+ ### Brief closing (all types)
42121
+
42122
+ - Invariants line: grant boundary + read-only SQL + publish gate before any create.
42123
+ - Result structure: the structured summary the orchestrator expects back.
42124
+ - Finish by settling the task (\`set_status completed\` with result, or \`failed\` with
42125
+ failureReason) and making the final message that structured summary.
42126
+
42127
+ ## Worker mode
42128
+
42129
+ Trigger: your context contains \`## Bound Task\`. You are a worker; these rules override the
42130
+ orchestrator policy (Safety and the reference sections still apply):
42131
+
42132
+ - Execute the bound task's brief only. Read the contract with \`manage_task get\`; the task
42133
+ description is authoritative. Load this skill if the bootstrap asks \u2014 you need its payload
42134
+ reference sections.
42135
+ - Do NOT: re-agree the Goal Model, create sibling or child tasks, dispatch \`task\`, call
42136
+ \`ask_user_to_clarify\`, or update the parent task's \`## Belief State\`.
42137
+ - Apply the FEP loop inside the brief; obey the grant boundary and read-only SQL; run the
42138
+ publish gate before any create.
42139
+ - Unclear semantics: mark \`INCONCLUSIVE\` with candidates + evidence in your findings. Never
42140
+ guess, never publish on a guess.
42141
+ - Finish: \`set_status completed\` with a structured result (or \`failed\` with failureReason),
42142
+ and make your final message the structured summary \u2014 it is the observation your
42143
+ orchestrator reads.
42144
+
42145
+ ## Belief State (orchestrator-owned)
42146
+
42147
+ The parent task's description carries a \`## Belief State\` section as a Markdown table:
41839
42148
 
41840
42149
  \`\`\`
41841
42150
  | Belief Key | Probability | Target | Basis |
@@ -41844,31 +42153,35 @@ a Markdown table:
41844
42153
  \`\`\`
41845
42154
 
41846
42155
  Rules: belief keys are lowercase kebab-case; probability/target are percentages 0\u2013100% that
41847
- represent evidence support, not calibrated probability. Update this section each FEP reconcile so
41848
- the task always reflects the current evidence.
42156
+ represent evidence support, not calibrated probability. Only the orchestrator writes this
42157
+ section \u2014 after each dispatch it folds the worker's findings into the table so the parent
42158
+ task always reflects the current evidence.
41849
42159
 
41850
42160
  ## Workflow
41851
42161
 
41852
- 1. **Pick the datasource.** \`metrics_datasource_tool\` action \`list_datasources\`, or the preset
41853
- \`runConfig.metricsDataSource\` (\`serverKey\`\u2192connectionKey, \`datasourceId\`).
41854
- 2. **Read the boundary.** \`get_grants\` \u2192 note the allowed schema + table pattern. Never probe outside it.
41855
- 3. **Explore within the grant.** \`query\` with read-only SQL. Use the templates in
41856
- \`references/metadata-sql.md\`; if the user supplied schema docs or sample files, read them first
41857
- via filesystem to reduce querying.
41858
- 4. **Propose the model.** Present the table columns (with \`role: dimension | measure\`) and the
41859
- metrics (aggregate/derived) for approval before publishing.
41860
- 5. **Publish the semantic table.** \`metrics_meta_tool\` \`create_table\`.
41861
- 6. **Publish the metric.** \`metrics_meta_tool\` \`create_metric\` \u2014 \`metric_index\` then \`metric_detail\`
41862
- using the SQL-free \`calculation\` DSL.
41863
- 7. **Verify.** \`metrics_runtime_tool\` \`get_meta\`, then \`query\` the metric against acceptance evidence.
41864
-
41865
- ## 1. Explore
42162
+ 1. **Pick the datasource + cheap observation.** \`metrics_datasource_tool\` action
42163
+ \`list_datasources\`, or the preset \`runConfig.metricsDataSource\` (\`serverKey\`\u2192connectionKey,
42164
+ \`datasourceId\`). Inline, read-only.
42165
+ 2. **Agree the Goal Model.** (HITL)
42166
+ 3. **Create the parent task** \u2014 contract + \`## Belief State\` (seeded from the Goal Model).
42167
+ 4. **Explore by dispatch** \u2014 one Explore brief per table, serial; collect findings; reconcile
42168
+ \`## Belief State\`; collect \`INCONCLUSIVE\` columns.
42169
+ 5. **Propose the model** (HITL) \u2014 present table columns (with \`role: dimension | measure\`)
42170
+ and the metrics (aggregate/derived) for approval; include clarify questions for
42171
+ \`INCONCLUSIVE\` columns in the same ask.
42172
+ 6. **Publish tables by dispatch** \u2014 one Publish-table brief per table; check receipts.
42173
+ 7. **Publish metrics by dispatch** \u2014 one Publish-metric brief per metric; receipts + smoke query.
42174
+ 8. **Verify by dispatch** \u2014 Verify brief against acceptance evidence. FAIL \u2192 reconcile
42175
+ belief \u2192 fix decisions \u2192 new Publish task (\`update_table\`/\`update_metric\`) \u2192 re-Verify.
42176
+ PASS \u2192 complete the parent task (result = acceptance evidence met).
42177
+
42178
+ ## Explore (reference)
41866
42179
 
41867
42180
  Read \`table-grants\`, then probe with \`query\`. SQL is validated read-only by the client and the
41868
42181
  server enforces the grant; un-granted tables return \`TABLE_NOT_GRANTED\`. Prefer the
41869
42182
  \`references/metadata-sql.md\` templates because dialect varies (PostgreSQL / SQL Server / HANA).
41870
- If the user provides a data dictionary or sample data file, read it with filesystem and reconcile it
41871
- against the probed columns.
42183
+ If the user provides a data dictionary or sample data file, read it with filesystem and
42184
+ reconcile it against the probed columns.
41872
42185
 
41873
42186
  ## Unclear field semantics
41874
42187
 
@@ -41877,7 +42190,7 @@ sum, a rate to average, or an enum to filter). Publishing meta on a misunderstoo
41877
42190
  the runtime and every downstream metric that depends on it, and delete is not available. Treat this
41878
42191
  as FEP **INCONCLUSIVE** \u2014 never guess, and never publish on an unverified guess.
41879
42192
 
41880
- Resolve in order of increasing cost:
42193
+ Resolve in order of increasing cost (inside the Explore brief):
41881
42194
  1. **Probe sample values and distribution** (\`query\`, templates in \`references/metadata-sql.md\`):
41882
42195
  - numbers in (0,1) or clustered near 100 \u2192 likely a ratio/percentage \u2192 dimension or \`avg\`, rarely \`sum\`;
41883
42196
  - large additive amounts (currency, counts) \u2192 measure with \`sum\`;
@@ -41889,14 +42202,16 @@ Resolve in order of increasing cost:
41889
42202
 
41890
42203
  If evidence is still INCONCLUSIVE:
41891
42204
  - Do **not** publish meta that depends on the unclear field.
41892
- - List the field with its candidate interpretations in the **Propose the model** step and ask the
41893
- user to confirm its semantics (role, value meaning, net/gross) via ask_user_to_clarify. The user
41894
- does not need to see the table \u2014 your probed schema, samples, and candidates are enough.
41895
- - Record a low-probability belief entry (e.g.
41896
- \`| sales-nes-semantics | 40% | 100% | user must confirm role / net amount |\`) so verify/refine can
41897
- revisit it later instead of assuming it is settled.
42205
+ - The worker marks the column \`INCONCLUSIVE\` (candidates + evidence) in its findings; the
42206
+ **orchestrator** lists it with candidate interpretations in the **Propose the model** step
42207
+ and asks the user to confirm its semantics (role, value meaning, net/gross) via
42208
+ \`ask_user_to_clarify\`. The user does not need to see the table \u2014 probed schema, samples, and
42209
+ candidates are enough.
42210
+ - The orchestrator records a low-probability belief entry (e.g.
42211
+ \`| sales-nes-semantics | 40% | 100% | user must confirm role / net amount |\`) so verify/refine
42212
+ can revisit it later instead of assuming it is settled.
41898
42213
 
41899
- ## 2. Publish table meta (\`create_table\`)
42214
+ ## Publish table meta (\`create_table\`)
41900
42215
 
41901
42216
  Publish the authoritative shape below (objectType \`table_view_detail\`, with an EXACT accessGrant):
41902
42217
 
@@ -41939,7 +42254,7 @@ Design rules:
41939
42254
  - Runtime table publication uses \`patternType: "EXACT"\` (\`PREFIX\` is only for datasource-level exploration scopes).
41940
42255
  - For each semantic table also publish the identity twin: the same body with \`objectType: "table_catalog"\` and the same \`objectKey\` (same or richer payload).
41941
42256
 
41942
- ## 3. Publish metric meta (\`create_metric\`)
42257
+ ## Publish metric meta (\`create_metric\`)
41943
42258
 
41944
42259
  Two steps for the same \`objectKey\`.
41945
42260
 
@@ -42047,11 +42362,13 @@ Design rules:
42047
42362
  - Do not inline raw SQL for new DB-backed metrics (complex logic belongs in a view).
42048
42363
  - Use \`update_metric\` / \`update_table\` to correct mistakes; no removal actions are exposed.
42049
42364
 
42050
- ## 4. Verify and refine
42365
+ ## Verify and refine
42051
42366
 
42052
- - \`get_meta\` \u2192 confirm the table and metric are visible.
42053
- - \`query\` the metric against the acceptance evidence. On failure, apply the FEP reconcile: update the
42054
- \`## Belief State\`, fix the meta, and retry \u2014 until acceptance evidence is met.
42367
+ - Verify is **dispatched** (Verify brief). The worker runs \`get_meta\` \u2192 confirms the table and
42368
+ metric are visible, then \`query\`es the metric against acceptance evidence.
42369
+ - On FAIL the worker reports differences; the **orchestrator** applies the FEP reconcile:
42370
+ update \`## Belief State\`, fix the decisions, and dispatch a new Publish task
42371
+ (\`update_table\`/\`update_metric\`) \u2014 then re-Verify \u2014 until acceptance evidence is met.
42055
42372
 
42056
42373
  ## Troubleshooting
42057
42374
 
@@ -42063,6 +42380,8 @@ Design rules:
42063
42380
  | derived metric create fails | numerator/denominator metrics don't exist yet \u2014 publish them first |
42064
42381
  | datasource \`query\` rejected | SQL was not read-only (SELECT/WITH only) or multi-statement |
42065
42382
  | \`403 Metric is not authorized\` on query_metrics | the metric meta is not in the queryable runtime schema (e.g. missing \`source.table_view\` / \`supported_dimensions\` / \`default_time_context\`) or genuinely unauthorized \u2014 re-publish with the full runtime payload and verify; never retry blindly |
42383
+ | worker observation truncated | findings exceeded ~8k chars; worker must keep the final message a compact structured summary |
42384
+ | dispatch failed (tool error) | retry once; Explore never goes inline \u2014 ask the user; Publish/Verify may go inline after the failed retry |
42066
42385
 
42067
42386
  ## Safety
42068
42387
 
@@ -42119,15 +42438,35 @@ the SQL is validated read-only.
42119
42438
  "examples/metric-aggregate.json": {
42120
42439
  content: `{
42121
42440
  "objectType": "metric_detail",
42122
- "objectKey": "hankel_sell_in_nes",
42441
+ "objectKey": "sell_in_nes",
42123
42442
  "status": 1,
42124
42443
  "payload": {
42125
- "name": "hankel_sell_in_nes",
42126
- "displayName": "Sell-in NES",
42444
+ "metric_name": "sell_in_nes",
42445
+ "display_name": "Sell-in NES",
42446
+ "domain": "sales",
42127
42447
  "description": "Net external sales amount from distributor sell-in rows.",
42128
- "sourceTable": "hankel_distr_sell_in",
42448
+ "data_type": "numeric",
42449
+ "format": "currency",
42450
+ "source_type": "cdp_postgres",
42451
+ "source": { "table_view": "public.hankel_distr_sell_in", "base_filters": [] },
42129
42452
  "calculation": { "type": "aggregate", "aggregation": "sum", "measure": "nes" },
42130
- "dimensions": ["posting_year", "posting_month", "sales_team", "region"]
42453
+ "supported_dimensions": [
42454
+ { "dim_id": "posting_year", "field_name": "posting_year", "label": "Posting Year", "data_type": "number" },
42455
+ { "dim_id": "sales_team", "field_name": "sales_team", "label": "Sales Team", "data_type": "string" },
42456
+ { "dim_id": "posting_date", "field_name": "posting_date", "label": "Posting Date", "data_type": "string" }
42457
+ ],
42458
+ "default_time_context": {
42459
+ "time_dimension": "posting_date",
42460
+ "label": "Posting date",
42461
+ "granularity": "month",
42462
+ "window": "YTD",
42463
+ "supported_grains": ["month", "year"]
42464
+ },
42465
+ "ai_agent_context": {
42466
+ "polarity": "positive",
42467
+ "synonyms": ["sell in", "nes", "net external sales"],
42468
+ "human_readable_explanation": "Higher sell-in NES means more goods purchased from the distributor."
42469
+ }
42131
42470
  }
42132
42471
  }
42133
42472
  `
@@ -42135,27 +42474,40 @@ the SQL is validated read-only.
42135
42474
  "examples/metric-derived.json": {
42136
42475
  content: `{
42137
42476
  "objectType": "metric_detail",
42138
- "objectKey": "hankel_gross_margin_rate",
42477
+ "objectKey": "gross_margin_rate",
42139
42478
  "status": 1,
42140
42479
  "payload": {
42141
- "name": "hankel_gross_margin_rate",
42142
- "displayName": "Gross Margin Rate",
42480
+ "metric_name": "gross_margin_rate",
42481
+ "display_name": "Gross Margin Rate",
42482
+ "domain": "sales",
42143
42483
  "description": "Gross margin divided by sell-in NES.",
42144
- "sourceTable": "hankel_distr_sell_in",
42145
- "calculation": {
42146
- "type": "derived",
42147
- "operator": "ratio",
42148
- "numerator": "hankel_gross_margin",
42149
- "denominator": "hankel_sell_in_nes"
42484
+ "data_type": "numeric",
42485
+ "format": "percent",
42486
+ "source_type": "cdp_postgres",
42487
+ "source": { "table_view": "public.hankel_distr_sell_in", "base_filters": [] },
42488
+ "calculation": { "type": "derived", "operator": "ratio", "numerator": "gross_margin", "denominator": "sell_in_nes" },
42489
+ "supported_dimensions": [
42490
+ { "dim_id": "posting_year", "field_name": "posting_year", "label": "Posting Year", "data_type": "number" },
42491
+ { "dim_id": "posting_date", "field_name": "posting_date", "label": "Posting Date", "data_type": "string" }
42492
+ ],
42493
+ "default_time_context": {
42494
+ "time_dimension": "posting_date",
42495
+ "label": "Posting date",
42496
+ "granularity": "month",
42497
+ "supported_grains": ["month", "year"]
42150
42498
  },
42151
- "dimensions": ["posting_year", "sales_team", "region"]
42499
+ "ai_agent_context": {
42500
+ "polarity": "positive",
42501
+ "synonyms": ["gross margin rate", "margin rate"],
42502
+ "human_readable_explanation": "Gross margin rate divides gross margin by sell-in NES; higher means more margin retained on sold-in goods."
42503
+ }
42152
42504
  }
42153
42505
  }
42154
42506
  `
42155
42507
  }
42156
42508
  };
42157
42509
  var SEMANTIC_METRICS_MODELING_SKILL = {
42158
- version: "1.0.0",
42510
+ version: "1.1.2",
42159
42511
  content: CONTENT,
42160
42512
  resources
42161
42513
  };
@@ -42169,7 +42521,7 @@ Never announce the skill load. If it fails, retry once, then stop and explicitly
42169
42521
  report that the required Semantic Metrics Builder skill could not be loaded.`;
42170
42522
 
42171
42523
  // src/middlewares/semanticMetricsMiddleware.ts
42172
- var import_protocols32 = require("@axiom-lattice/protocols");
42524
+ var import_protocols33 = require("@axiom-lattice/protocols");
42173
42525
  function createSemanticMetricsMiddleware(config) {
42174
42526
  const connections = config.connections ?? [];
42175
42527
  const connectAll = config.connectAll === true;
@@ -42321,7 +42673,7 @@ var semanticMetricsPlugin = {
42321
42673
  key: "semantic-metrics-builder",
42322
42674
  name: "Semantic Metrics Builder",
42323
42675
  description: "Explore a granted datasource and build semantic tables and metrics interactively, tracking the work as persistent tasks with belief state and verifying each step.",
42324
- type: import_protocols32.AgentType.DEEP_AGENT,
42676
+ type: import_protocols33.AgentType.DEEP_AGENT,
42325
42677
  prompt: SEMANTIC_METRICS_BUILDER_PROMPT,
42326
42678
  middleware: [
42327
42679
  {
@@ -42854,7 +43206,7 @@ var collectionPlugin = {
42854
43206
 
42855
43207
  // src/middlewares/askUserClarifyMiddleware.ts
42856
43208
  var import_langchain83 = require("langchain");
42857
- var import_langgraph14 = require("@langchain/langgraph");
43209
+ var import_langgraph15 = require("@langchain/langgraph");
42858
43210
 
42859
43211
  // src/tool_lattice/ask_user_to_clarify/index.ts
42860
43212
  var import_langchain82 = require("langchain");
@@ -42930,7 +43282,7 @@ function createAskUserClarifyMiddleware() {
42930
43282
  }))
42931
43283
  };
42932
43284
  const md = genUIMarkdown("clarify", uiData);
42933
- const result = await (0, import_langgraph14.interrupt)(md);
43285
+ const result = await (0, import_langgraph15.interrupt)(md);
42934
43286
  const response = result.data;
42935
43287
  if (!response?.answers || response.answers.length === 0) {
42936
43288
  return new import_langchain83.ToolMessage({
@@ -43907,8 +44259,66 @@ var widgetPlugin = {
43907
44259
  // src/middlewares/taskMiddleware.ts
43908
44260
  var import_langchain87 = require("langchain");
43909
44261
  var import_zod71 = require("zod");
43910
- var import_langgraph15 = require("@langchain/langgraph");
43911
- var import_protocols33 = require("@axiom-lattice/protocols");
44262
+ var import_langgraph16 = require("@langchain/langgraph");
44263
+
44264
+ // src/util/truncateForJson.ts
44265
+ var TRUNCATION_MARKER = "...[truncated]";
44266
+ function truncateString(value, cap) {
44267
+ if (value.length <= cap) return value;
44268
+ const budget = Math.max(0, cap - TRUNCATION_MARKER.length);
44269
+ return value.slice(0, budget) + TRUNCATION_MARKER;
44270
+ }
44271
+ function truncateValue(value, depth, maxDepth, maxEntries, maxStringLength, seen) {
44272
+ if (typeof value === "string") return truncateString(value, maxStringLength);
44273
+ if (value === null || typeof value !== "object") return value;
44274
+ if (depth >= maxDepth) return "[truncated:max-depth]";
44275
+ if (seen.has(value)) return "[Circular]";
44276
+ seen.add(value);
44277
+ try {
44278
+ if (value instanceof Date) return value.toISOString();
44279
+ if (Array.isArray(value)) {
44280
+ const limit2 = Math.min(value.length, maxEntries);
44281
+ const output2 = [];
44282
+ for (let index = 0; index < limit2; index += 1) {
44283
+ output2.push(truncateValue(value[index], depth + 1, maxDepth, maxEntries, maxStringLength, seen));
44284
+ }
44285
+ if (value.length > limit2) output2.push("[truncated:entries]");
44286
+ return output2;
44287
+ }
44288
+ const record = value;
44289
+ const output = {};
44290
+ const keys = Object.keys(record);
44291
+ const limit = Math.min(keys.length, maxEntries);
44292
+ for (let index = 0; index < limit; index += 1) {
44293
+ const key5 = keys[index];
44294
+ output[truncateString(key5, maxStringLength)] = truncateValue(
44295
+ record[key5],
44296
+ depth + 1,
44297
+ maxDepth,
44298
+ maxEntries,
44299
+ maxStringLength,
44300
+ seen
44301
+ );
44302
+ }
44303
+ if (keys.length > limit) output["[truncated]"] = true;
44304
+ return output;
44305
+ } finally {
44306
+ seen.delete(value);
44307
+ }
44308
+ }
44309
+ function truncateForJson(value, options = {}) {
44310
+ return truncateValue(
44311
+ value,
44312
+ 0,
44313
+ options.maxDepth ?? JSON_SAFE_MAX_DEPTH,
44314
+ options.maxEntries ?? JSON_SAFE_MAX_COLLECTION_ENTRIES,
44315
+ options.maxStringLength ?? JSON_SAFE_MAX_STRING_LENGTH,
44316
+ /* @__PURE__ */ new WeakSet()
44317
+ );
44318
+ }
44319
+
44320
+ // src/middlewares/taskMiddleware.ts
44321
+ var import_protocols34 = require("@axiom-lattice/protocols");
43912
44322
 
43913
44323
  // src/middlewares/taskResponseSerialization.ts
43914
44324
  var TASK_RESPONSE_SCALAR_MAX_BYTES = 4096;
@@ -44232,6 +44642,132 @@ function getRuntimeActor2(runConfig) {
44232
44642
  if (runConfig.user_id) return `user:${String(runConfig.user_id)}`;
44233
44643
  return void 0;
44234
44644
  }
44645
+ function readRuntimeRunConfig(source) {
44646
+ const s = source;
44647
+ const rc = s?.context?.runConfig ?? s?.configurable?.runConfig;
44648
+ return rc && typeof rc === "object" && !Array.isArray(rc) ? rc : {};
44649
+ }
44650
+ function readExecutionTaskId(runConfig) {
44651
+ const execution = runConfig.execution;
44652
+ if (!execution || typeof execution !== "object" || Array.isArray(execution)) return void 0;
44653
+ const value = execution.taskId;
44654
+ return typeof value === "string" && value.trim() !== "" ? value : void 0;
44655
+ }
44656
+ function renderTaskBinding(taskId) {
44657
+ return `
44658
+
44659
+ ## Bound Task
44660
+
44661
+ You are executing task \`${taskId}\`. Read its contract with \`manage_task get\`, update it as you work, and complete it when done.
44662
+ `;
44663
+ }
44664
+ function createBoundTaskLifecycle() {
44665
+ return createTaskLifecycleService({ taskStore: getTaskStore(), workItemStore: getTaskWorkItemStore() });
44666
+ }
44667
+ function createBoundTaskBridge() {
44668
+ return createTaskRunBridge({ lifecycle: createBoundTaskLifecycle() });
44669
+ }
44670
+ function isResumableInterruption(interruption) {
44671
+ if (!interruption || typeof interruption !== "object" || Array.isArray(interruption)) return false;
44672
+ const type = interruption.type;
44673
+ return type === "missing_input" || type === "user_decision";
44674
+ }
44675
+ var TASK_WORKER_OBSERVATION_MAX_CHARS = 8192;
44676
+ function snapshotMessages(state) {
44677
+ if (!state || typeof state !== "object") return [];
44678
+ const values = state.values;
44679
+ if (!values || typeof values !== "object") return [];
44680
+ const messages = values.messages;
44681
+ return Array.isArray(messages) ? messages : [];
44682
+ }
44683
+ function messageText(value) {
44684
+ if (typeof value === "string") return value;
44685
+ if (!Array.isArray(value)) return void 0;
44686
+ return value.flatMap((part) => {
44687
+ if (typeof part === "string") return [part];
44688
+ if (part && typeof part === "object" && typeof part.text === "string") {
44689
+ return [part.text];
44690
+ }
44691
+ return [];
44692
+ }).join("");
44693
+ }
44694
+ function lastAssistantText(source) {
44695
+ const messages = Array.isArray(source) ? source : source && typeof source === "object" && Array.isArray(source.messages) ? source.messages : snapshotMessages(source);
44696
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
44697
+ const message = messages[index];
44698
+ if (!message || typeof message !== "object") continue;
44699
+ const role = message.role ?? message.type;
44700
+ if (role !== "ai" && role !== "assistant") continue;
44701
+ const text = messageText(message.content);
44702
+ if (text !== void 0) return text;
44703
+ }
44704
+ return void 0;
44705
+ }
44706
+ function boundedWorkerObservation(result, state) {
44707
+ const text = lastAssistantText(state) ?? lastAssistantText(result) ?? "";
44708
+ return {
44709
+ status: "completed",
44710
+ output: truncateForJson(text, { maxStringLength: TASK_WORKER_OBSERVATION_MAX_CHARS })
44711
+ };
44712
+ }
44713
+ var EXECUTOR_CACHE_TTL_MS = 3e4;
44714
+ var executorCache = /* @__PURE__ */ new Map();
44715
+ function executorString(value) {
44716
+ return typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
44717
+ }
44718
+ async function renderAvailableExecutors(rc, tenantId2) {
44719
+ const authority = classifyTaskAuthority(rc);
44720
+ const governed = authority.kind === "projectRoom" || authority.kind === "projectTask";
44721
+ const projectId = governed ? (authority.trusted.projectTask ?? authority.trusted.projectRoom)?.projectId : void 0;
44722
+ const cacheKey = `${tenantId2}:${governed ? `project:${projectId ?? ""}` : "ordinary"}`;
44723
+ const cached = executorCache.get(cacheKey);
44724
+ const now = Date.now();
44725
+ if (cached && now - cached.at < EXECUTOR_CACHE_TTL_MS) return cached.text;
44726
+ const lines = [];
44727
+ try {
44728
+ if (governed && projectId) {
44729
+ const store = getStoreLattice("default", "projectBotMembership").store;
44730
+ if (typeof store.list === "function") {
44731
+ const bots = await store.list(tenantId2, projectId);
44732
+ for (const bot of (Array.isArray(bots) ? bots : []).slice(0, 40)) {
44733
+ const row = bot;
44734
+ if (row.status !== "active") continue;
44735
+ const id = executorString(row.assistantId);
44736
+ if (!id) continue;
44737
+ const title = executorString(row.title);
44738
+ const responsibility = executorString(row.responsibility);
44739
+ lines.push(`- ${id}${title ? ` \u2014 ${title}` : ""}${responsibility ? ` (${responsibility})` : ""}`);
44740
+ }
44741
+ }
44742
+ } else {
44743
+ const configs = await agentLatticeManager.getAllAgentConfigsByTenant(tenantId2);
44744
+ for (const config of configs.slice(0, 40)) {
44745
+ const row = config;
44746
+ const id = executorString(row.key);
44747
+ if (!id) continue;
44748
+ const name = executorString(row.name);
44749
+ const description = executorString(row.description);
44750
+ lines.push(`- ${id}${name ? ` \u2014 ${name}` : ""}${description ? ` (${description.slice(0, 120)})` : ""}`);
44751
+ }
44752
+ }
44753
+ } catch {
44754
+ }
44755
+ if (lines.length === 0) {
44756
+ executorCache.set(cacheKey, { at: now, text: "" });
44757
+ return "";
44758
+ }
44759
+ const header = governed ? "## Available task executors (Project roster)" : "## Available task executors";
44760
+ const text = `
44761
+
44762
+ ${header}
44763
+
44764
+ Dispatch \`task\` only to an executor that has the tools the work needs (for example \`update_agent\`/\`create_agent\` for materialization). Never dispatch to the subject agent you are building. Use \`general-purpose\` to run your own capabilities.
44765
+
44766
+ ${lines.join("\n")}
44767
+ `;
44768
+ executorCache.set(cacheKey, { at: now, text });
44769
+ return text;
44770
+ }
44235
44771
  function delegatedTaskScopeError(taskId) {
44236
44772
  return JSON.stringify({
44237
44773
  success: false,
@@ -44250,7 +44786,7 @@ function classifyTaskAuthority(runConfig) {
44250
44786
  return delegatedTaskId ? { kind: "delegated", taskId: delegatedTaskId } : { kind: "ordinary" };
44251
44787
  }
44252
44788
  try {
44253
- const trusted = (0, import_protocols33.parseTrustedRunContext)(hasProjectTask ? { projectTask: runConfig.projectTask } : { projectRoom: runConfig.projectRoom });
44789
+ const trusted = (0, import_protocols34.parseTrustedRunContext)(hasProjectTask ? { projectTask: runConfig.projectTask } : { projectRoom: runConfig.projectRoom });
44254
44790
  return { kind: hasProjectTask ? "projectTask" : "projectRoom", trusted };
44255
44791
  } catch {
44256
44792
  return { kind: "invalid" };
@@ -44583,7 +45119,7 @@ function getTaskWorkItemStore() {
44583
45119
  return getStoreLattice("default", "taskWorkItem").store;
44584
45120
  }
44585
45121
  function getProjectTaskWorkItemStore() {
44586
- return (0, import_protocols33.requireProjectTaskWorkItemStore)(getTaskWorkItemStore());
45122
+ return (0, import_protocols34.requireProjectTaskWorkItemStore)(getTaskWorkItemStore());
44587
45123
  }
44588
45124
  function reviewInterruption(task) {
44589
45125
  const value = task.context?.interruption;
@@ -44601,11 +45137,11 @@ function buildReviewMarkdown(task) {
44601
45137
  }
44602
45138
  var manageTaskSchema = import_zod71.z.object({
44603
45139
  action: import_zod71.z.enum(["create", "get", "list", "update", "set_status", "add_activity", "delete"]).describe("Action to perform. Available: create, get, list, update, set_status, add_activity, delete. To change status, use set_status. To change content fields, use update."),
44604
- id: import_zod71.z.string().optional().describe("Task ID (required for get, update, add_activity, and delete)"),
45140
+ id: import_zod71.z.string().optional().describe("Task ID (required for get, update, add_activity; required for delete unless ids is given)"),
44605
45141
  title: import_zod71.z.string().optional().describe("Task title (required for create)"),
44606
45142
  description: import_zod71.z.string().optional().describe("Task description in Markdown"),
44607
45143
  priority: import_zod71.z.enum(["low", "medium", "high"]).optional().describe("Priority level"),
44608
- status: import_zod71.z.enum(["pending", "in_progress", "review", "failed", "interrupted", "completed", "cancelled"]).optional().describe("Task status. For status-only changes, prefer set_status action. Agent summary-only interrupted updates default to missing_input"),
45144
+ status: import_zod71.z.enum(["pending", "in_progress", "review", "failed", "interrupted", "completed", "cancelled"]).optional().describe("Task status. For status-only changes, prefer set_status action. Set 'in_progress' when you start processing a task you hold; 'completed'/'failed' require the task to be 'in_progress'. Agent summary-only interrupted updates default to missing_input"),
44609
45145
  dueDate: import_zod71.z.string().optional().describe("Due date (ISO 8601 format)"),
44610
45146
  metadata: import_zod71.z.record(import_zod71.z.unknown()).optional().describe("Structured metadata (e.g. projectId, module)"),
44611
45147
  metadataFilter: import_zod71.z.record(
@@ -44634,11 +45170,12 @@ var manageTaskSchema = import_zod71.z.object({
44634
45170
  })).optional().describe("Structured belief reports required when an agent-owned task completes; existing keys update the belief owner's table, and a brand-new key must be declared with isNew: true"),
44635
45171
  content: import_zod71.z.string().optional().describe("Markdown content required for add_activity"),
44636
45172
  summary: import_zod71.z.string().optional().describe("Brief summary of the operation"),
44637
- executionResultId: import_zod71.z.string().refine(import_protocols33.isExecutionResultEventKey, {
45173
+ executionResultId: import_zod71.z.string().refine(import_protocols34.isExecutionResultEventKey, {
44638
45174
  message: "executionResultId must be the execution-result:<threadId> event key returned by task()"
44639
45175
  }).optional().describe(
44640
45176
  "Canonical ASCII `execution-result:<suffix>` event key returned by task(); the nonempty suffix allows only A-Z, a-z, 0-9, period, underscore, colon, and hyphen, and is not a database row ID"
44641
- )
45177
+ ),
45178
+ ids: import_zod71.z.array(import_zod71.z.string().trim().min(1)).optional().describe("Task IDs to delete in one batch (delete action only). Use with or instead of id.")
44642
45179
  }).superRefine((input, context) => {
44643
45180
  if (input.executionResultId && input.action !== "update" && input.action !== "set_status" && input.action !== "add_activity") {
44644
45181
  context.addIssue({
@@ -44654,10 +45191,19 @@ var manageTaskSchema = import_zod71.z.object({
44654
45191
  message: "metadataFilter is valid only for list"
44655
45192
  });
44656
45193
  }
45194
+ if (input.ids && input.action !== "delete") {
45195
+ context.addIssue({
45196
+ code: import_zod71.z.ZodIssueCode.custom,
45197
+ path: ["ids"],
45198
+ message: "ids is valid only for delete"
45199
+ });
45200
+ }
44657
45201
  });
44658
45202
  var startTaskSchema = import_zod71.z.object({
44659
45203
  taskId: import_zod71.z.string().trim().min(1).describe("ID of the persisted TaskItem to start"),
44660
- agentId: import_zod71.z.string().trim().min(1).describe("ID of the Agent that should execute the task")
45204
+ agentId: import_zod71.z.string().trim().min(1).describe(
45205
+ `ID of the Agent that should execute the task. Use "general-purpose" to run the caller's own capabilities as an isolated worker.`
45206
+ )
44661
45207
  });
44662
45208
  function mergeTaskUpdateWarnings(existing, added) {
44663
45209
  const warnings = [...existing ?? [], ...added ?? []];
@@ -44689,7 +45235,7 @@ async function getPendingExecutionResultIds(store, tenantId2, taskId) {
44689
45235
  order: "desc",
44690
45236
  limit: PENDING_EXECUTION_RESULT_LIMIT + 1
44691
45237
  });
44692
- const canonicalCandidates = candidates.slice(0, PENDING_EXECUTION_RESULT_LIMIT + 1).filter((item) => (0, import_protocols33.isExecutionResultEventKey)(item.eventKey));
45238
+ const canonicalCandidates = candidates.slice(0, PENDING_EXECUTION_RESULT_LIMIT + 1).filter((item) => (0, import_protocols34.isExecutionResultEventKey)(item.eventKey));
44693
45239
  const reconciliationRows = new Array(canonicalCandidates.length);
44694
45240
  let nextCandidateIndex = 0;
44695
45241
  let firstError;
@@ -44794,6 +45340,49 @@ function createTaskMiddleware(options = {}) {
44794
45340
  }
44795
45341
  const ownerId = input.ownerId || trustedScope?.assistantId || (input.ownerType === "agent" ? rc.assistant_id : null) || rc.user_id;
44796
45342
  const store = getTaskStore();
45343
+ const deleteOneTask = async (id) => {
45344
+ if (delegatedTaskId && id !== delegatedTaskId) {
45345
+ return delegatedTaskScopeError(delegatedTaskId);
45346
+ }
45347
+ const existing = await store.getById(tenantId2, id);
45348
+ if (!existing) {
45349
+ return JSON.stringify({
45350
+ success: false,
45351
+ code: "TASK_NOT_FOUND",
45352
+ error: `Task '${id}' was not found.`,
45353
+ hint: "Use manage_task list to see available tasks and their correct IDs."
45354
+ });
45355
+ }
45356
+ if (!taskMatchesRuntimeScope3(existing, workspaceId, projectId)) {
45357
+ return taskScopeAccessError(id);
45358
+ }
45359
+ if (trustedProject?.projectTask) {
45360
+ const trustedFailure = trustedTaskContextFailure(existing, trustedProject.projectTask);
45361
+ if (trustedFailure) return trustedFailure;
45362
+ }
45363
+ let deleted;
45364
+ if (trustedProject) {
45365
+ const snapshot = taskMutationSnapshot(existing);
45366
+ if (!snapshot) return trustedContextError();
45367
+ try {
45368
+ await createProjectTaskOwnerPolicy().requireCaller(trustedRosterScope(trustedProject));
45369
+ } catch (error) {
45370
+ return projectPolicyError(error);
45371
+ }
45372
+ deleted = await store.deleteIfSnapshot(tenantId2, id, snapshot);
45373
+ } else {
45374
+ deleted = await store.delete(tenantId2, id);
45375
+ }
45376
+ if (!deleted) {
45377
+ return JSON.stringify({
45378
+ success: false,
45379
+ code: "TASK_DELETE_FAILED",
45380
+ error: `Task '${id}' was not found or could not be deleted.`,
45381
+ hint: "Use manage_task list to verify the task exists and you have access to it."
45382
+ });
45383
+ }
45384
+ return JSON.stringify({ success: true, message: `Task '${id}' deleted` });
45385
+ };
44797
45386
  switch (input.action) {
44798
45387
  case "create": {
44799
45388
  if (delegatedTaskId && input.parentId !== delegatedTaskId) {
@@ -44949,9 +45538,6 @@ function createTaskMiddleware(options = {}) {
44949
45538
  hint: "Pass the task ID you want to retrieve"
44950
45539
  });
44951
45540
  }
44952
- if (delegatedTaskId && input.id !== delegatedTaskId) {
44953
- return delegatedTaskScopeError(delegatedTaskId);
44954
- }
44955
45541
  const task = await store.getById(tenantId2, input.id);
44956
45542
  if (!task) {
44957
45543
  return JSON.stringify({
@@ -44997,25 +45583,6 @@ function createTaskMiddleware(options = {}) {
44997
45583
  });
44998
45584
  }
44999
45585
  case "list": {
45000
- if (delegatedTaskId) {
45001
- const delegatedTask = await store.getById(tenantId2, delegatedTaskId);
45002
- if (!delegatedTask || !taskMatchesRuntimeScope3(delegatedTask, workspaceId, projectId)) {
45003
- return serializeTaskReadResponse({ success: true, data: [], count: 0 });
45004
- }
45005
- const children = await store.list({
45006
- tenantId: tenantId2,
45007
- workspaceId,
45008
- projectId,
45009
- parentId: delegatedTaskId,
45010
- metadata: input.metadataFilter
45011
- });
45012
- const scopedChildren = children.filter((child) => child.id !== delegatedTaskId && child.parentId === delegatedTaskId && exactScopeMatch(child, delegatedTask));
45013
- return serializeTaskReadResponse({
45014
- success: true,
45015
- data: [delegatedTask, ...scopedChildren],
45016
- count: scopedChildren.length + 1
45017
- });
45018
- }
45019
45586
  const filter2 = {
45020
45587
  tenantId: tenantId2,
45021
45588
  ownerType: input.ownerType,
@@ -45310,8 +45877,8 @@ function createTaskMiddleware(options = {}) {
45310
45877
  let reconciliationMetadata = {};
45311
45878
  let descriptionMutated = false;
45312
45879
  if (isAgentTask && input.description !== void 0 && !valuesEqual(input.description, existing.description)) {
45313
- const oldBelief = (0, import_protocols33.parseTaskBeliefState)(existing.description ?? "");
45314
- const newBelief = (0, import_protocols33.parseTaskBeliefState)(input.description);
45880
+ const oldBelief = (0, import_protocols34.parseTaskBeliefState)(existing.description ?? "");
45881
+ const newBelief = (0, import_protocols34.parseTaskBeliefState)(input.description);
45315
45882
  const hasBeliefState = oldBelief.success || newBelief.success;
45316
45883
  if (hasBeliefState) {
45317
45884
  await validateExecutionResult();
@@ -45610,41 +46177,31 @@ function createTaskMiddleware(options = {}) {
45610
46177
  });
45611
46178
  return lifecycleMutationResponse(interrupted);
45612
46179
  }
45613
- if (lifecycle && input.status === "in_progress" && existing.status === "interrupted") {
45614
- await validateExecutionResult();
45615
- const callerError = await revalidateCaller();
45616
- if (callerError) return callerError;
45617
- const resumed = await lifecycle.resumeInterruption({
45618
- tenantId: tenantId2,
45619
- taskId: input.id,
45620
- actor,
45621
- threadId: statusThreadId
45622
- });
45623
- return lifecycleMutationResponse(resumed);
45624
- }
45625
- if (lifecycle && input.status === "in_progress" && existing.status === "pending") {
45626
- await validateExecutionResult();
46180
+ if (lifecycle && input.status === "in_progress") {
45627
46181
  const callerError = await revalidateCaller();
45628
46182
  if (callerError) return callerError;
45629
- const started = await lifecycle.startTask({
45630
- tenantId: tenantId2,
45631
- taskId: input.id,
45632
- actor,
45633
- threadId: statusThreadId
45634
- });
45635
- return lifecycleMutationResponse(started);
45636
- }
45637
- if (lifecycle && input.status === "in_progress" && existing.status === "failed") {
45638
- await validateExecutionResult();
45639
- const callerError = await revalidateCaller();
45640
- if (callerError) return callerError;
45641
- const retried = await lifecycle.retryTask({
46183
+ if (existing.status === "interrupted") {
46184
+ return lifecycleMutationResponse(await lifecycle.resumeInterruption({
46185
+ tenantId: tenantId2,
46186
+ taskId: input.id,
46187
+ actor,
46188
+ threadId: statusThreadId
46189
+ }));
46190
+ }
46191
+ if (existing.status === "failed") {
46192
+ return lifecycleMutationResponse(await lifecycle.retryTask({
46193
+ tenantId: tenantId2,
46194
+ taskId: input.id,
46195
+ actor,
46196
+ threadId: statusThreadId
46197
+ }));
46198
+ }
46199
+ return lifecycleMutationResponse(await lifecycle.startTask({
45642
46200
  tenantId: tenantId2,
45643
46201
  taskId: input.id,
45644
46202
  actor,
45645
46203
  threadId: statusThreadId
45646
- });
45647
- return lifecycleMutationResponse(retried);
46204
+ }));
45648
46205
  }
45649
46206
  const effectiveRequireReview = existing.requireReview === true || input.requireReview === true;
45650
46207
  const candidateReviewInterruption = input.status === "interrupted" && input.context?.interruption && typeof input.context.interruption === "object" && input.context.interruption.type === "review_required" && !!input.result?.trim();
@@ -45711,9 +46268,9 @@ function createTaskMiddleware(options = {}) {
45711
46268
  }
45712
46269
  let response;
45713
46270
  try {
45714
- response = await (0, import_langgraph15.interrupt)(buildReviewMarkdown(submitted.task));
46271
+ response = await (0, import_langgraph16.interrupt)(buildReviewMarkdown(submitted.task));
45715
46272
  } catch (error) {
45716
- if (error instanceof import_langgraph15.GraphInterrupt) throw error;
46273
+ if (error instanceof import_langgraph16.GraphInterrupt) throw error;
45717
46274
  return lifecycleMutationResponse(submitted, { code: "REVIEW_REQUIRED" });
45718
46275
  }
45719
46276
  if (response?.action === "approve") {
@@ -45825,54 +46382,49 @@ function createTaskMiddleware(options = {}) {
45825
46382
  return successfulTaskResponse(input.id, updatedStatus, true);
45826
46383
  }
45827
46384
  case "delete": {
45828
- if (!input.id) {
45829
- return JSON.stringify({
45830
- success: false,
45831
- error: "id is required for delete action",
45832
- hint: "Pass the task ID you want to delete"
45833
- });
45834
- }
45835
- if (delegatedTaskId && input.id !== delegatedTaskId) {
45836
- return delegatedTaskScopeError(delegatedTaskId);
45837
- }
45838
- const existing = await store.getById(tenantId2, input.id);
45839
- if (!existing) {
45840
- return JSON.stringify({
45841
- success: false,
45842
- code: "TASK_NOT_FOUND",
45843
- error: `Task '${input.id}' was not found.`,
45844
- hint: "Use manage_task list to see available tasks and their correct IDs."
45845
- });
45846
- }
45847
- if (!taskMatchesRuntimeScope3(existing, workspaceId, projectId)) {
45848
- return taskScopeAccessError(input.id);
45849
- }
45850
- if (trustedProject?.projectTask) {
45851
- const trustedFailure = trustedTaskContextFailure(existing, trustedProject.projectTask);
45852
- if (trustedFailure) return trustedFailure;
46385
+ const missingTargetError = JSON.stringify({
46386
+ success: false,
46387
+ error: "id or ids is required for delete action",
46388
+ hint: "Pass the task ID(s) you want to delete"
46389
+ });
46390
+ const requestedIds = input.ids && input.ids.length > 0 ? [.../* @__PURE__ */ new Set([...input.id ? [input.id] : [], ...input.ids])] : void 0;
46391
+ if (requestedIds === void 0) {
46392
+ if (!input.id) {
46393
+ return missingTargetError;
46394
+ }
46395
+ return deleteOneTask(input.id);
45853
46396
  }
45854
- let deleted;
45855
- if (trustedProject) {
45856
- const snapshot = taskMutationSnapshot(existing);
45857
- if (!snapshot) return trustedContextError();
46397
+ const deletedIds = [];
46398
+ const failed = [];
46399
+ for (const id of requestedIds) {
46400
+ let raw;
45858
46401
  try {
45859
- await createProjectTaskOwnerPolicy().requireCaller(trustedRosterScope(trustedProject));
46402
+ raw = await deleteOneTask(id);
45860
46403
  } catch (error) {
45861
- return projectPolicyError(error);
46404
+ failed.push({
46405
+ id,
46406
+ code: "TASK_DELETE_FAILED",
46407
+ error: error instanceof Error ? error.message : String(error)
46408
+ });
46409
+ continue;
46410
+ }
46411
+ const result = JSON.parse(raw);
46412
+ if (result.success) {
46413
+ deletedIds.push(id);
46414
+ } else {
46415
+ failed.push({
46416
+ id,
46417
+ ...result.code ? { code: result.code } : {},
46418
+ error: result.error ?? `Task '${id}' could not be deleted.`
46419
+ });
45862
46420
  }
45863
- deleted = await store.deleteIfSnapshot(tenantId2, input.id, snapshot);
45864
- } else {
45865
- deleted = await store.delete(tenantId2, input.id);
45866
- }
45867
- if (!deleted) {
45868
- return JSON.stringify({
45869
- success: false,
45870
- code: "TASK_DELETE_FAILED",
45871
- error: `Task '${input.id}' was not found or could not be deleted.`,
45872
- hint: "Use manage_task list to verify the task exists and you have access to it."
45873
- });
45874
46421
  }
45875
- return JSON.stringify({ success: true, message: `Task '${input.id}' deleted` });
46422
+ return JSON.stringify({
46423
+ success: failed.length === 0,
46424
+ deleted: deletedIds,
46425
+ failed,
46426
+ summary: `${deletedIds.length} of ${requestedIds.length} deleted`
46427
+ });
45876
46428
  }
45877
46429
  default:
45878
46430
  return JSON.stringify({
@@ -45987,23 +46539,29 @@ function createTaskMiddleware(options = {}) {
45987
46539
  error: "Runtime actor identity is required for persistent task execution"
45988
46540
  });
45989
46541
  }
46542
+ const callerAssistantId = rc.assistant_id;
46543
+ const agentId = input.agentId === "general-purpose" && callerAssistantId ? `${callerAssistantId}-general-purpose` : input.agentId;
45990
46544
  const service2 = createTaskExecutionService({
45991
46545
  taskStore: getTaskStore(),
45992
46546
  workItemStore: getTaskWorkItemStore(),
45993
- resolveAgent: async (targetTenantId, agentId) => !!await agentLatticeManager.getOrLoadWithTenant(targetTenantId, agentId),
45994
- resolveTaskCapability: async (targetTenantId, agentId) => agentHasManagedTaskCapability(
45995
- (await agentLatticeManager.getOrLoadWithTenant(targetTenantId, agentId))?.config
45996
- ),
46547
+ resolveAgent: async (targetTenantId, agentId2) => !!await agentLatticeManager.getOrLoadWithTenant(targetTenantId, agentId2),
45997
46548
  invokeAgent: async (invocation) => {
45998
- const worker = new Agent({
45999
- tenant_id: invocation.tenantId,
46549
+ const workerResult = await agentWorkerGraph.invoke({
46000
46550
  assistant_id: invocation.agentId,
46001
46551
  thread_id: invocation.threadId,
46002
- workspace_id: invocation.workspaceId,
46003
- project_id: invocation.projectId,
46004
- custom_run_config: createDelegatedWorkerRunConfig(invocation.taskId)
46552
+ input: { message: invocation.message },
46553
+ runConfig: {
46554
+ ...rc,
46555
+ assistant_id: invocation.agentId,
46556
+ thread_id: invocation.threadId,
46557
+ taskId: invocation.taskId,
46558
+ tenantId: invocation.tenantId,
46559
+ workspaceId: invocation.workspaceId,
46560
+ projectId: invocation.projectId
46561
+ }
46005
46562
  });
46006
- return worker.invoke({ input: { message: invocation.message } });
46563
+ const finalState = workerResult?.finalState;
46564
+ return boundedWorkerObservation(finalState, void 0);
46007
46565
  }
46008
46566
  });
46009
46567
  const trustedCaller = authority.kind === "projectTask" || authority.kind === "projectRoom" ? authority.trusted.projectTask ?? authority.trusted.projectRoom : void 0;
@@ -46012,7 +46570,7 @@ function createTaskMiddleware(options = {}) {
46012
46570
  workspaceId: rc.workspaceId,
46013
46571
  projectId: rc.projectId,
46014
46572
  taskId: input.taskId,
46015
- agentId: input.agentId,
46573
+ agentId,
46016
46574
  actor,
46017
46575
  callerThreadId: rc.thread_id,
46018
46576
  ...trustedCaller ? { callerMembershipId: trustedCaller.membershipId, callerAssistantId: trustedCaller.assistantId } : {}
@@ -46021,6 +46579,66 @@ function createTaskMiddleware(options = {}) {
46021
46579
  return (0, import_langchain87.createMiddleware)({
46022
46580
  name: "TaskMiddleware",
46023
46581
  contextSchema,
46582
+ // Persisted between invocations (ADRs: the run↔task binding survives turns).
46583
+ stateSchema: import_zod71.z.object({
46584
+ taskId: import_zod71.z.string().default("")
46585
+ }),
46586
+ // CLAIM: a run that holds a task moves it to in_progress (idempotent).
46587
+ beforeAgent: async (state, runtime) => {
46588
+ const rc = readRuntimeRunConfig(runtime);
46589
+ const bound = typeof state?.taskId === "string" ? state.taskId : "";
46590
+ const taskId = bound || readExecutionTaskId(rc) || "";
46591
+ if (!taskId) return {};
46592
+ if (!taskId) return {};
46593
+ const tenantId2 = rc.tenantId || "default";
46594
+ const threadId = runtime?.configurable?.thread_id;
46595
+ const task = await getTaskStore().getById(tenantId2, taskId);
46596
+ if (!task) return {};
46597
+ const runWorkspaceId = typeof rc.workspaceId === "string" ? rc.workspaceId : void 0;
46598
+ const runProjectId = typeof rc.projectId === "string" ? rc.projectId : void 0;
46599
+ if (task.workspaceId && task.workspaceId !== runWorkspaceId) return {};
46600
+ if (task.projectId && task.projectId !== runProjectId) return {};
46601
+ if (task.status === "completed" || task.status === "failed" || task.status === "cancelled") {
46602
+ return bound ? {} : { taskId };
46603
+ }
46604
+ taskRunRegistry.register(taskId);
46605
+ if (task.status === "pending") {
46606
+ await createBoundTaskBridge().beginRun({
46607
+ tenantId: tenantId2,
46608
+ task,
46609
+ actor: getRuntimeActor2(rc) ?? "system",
46610
+ ...threadId ? { threadId } : {}
46611
+ });
46612
+ } else if (task.status === "interrupted" && isResumableInterruption(task.context?.interruption)) {
46613
+ await createBoundTaskLifecycle().resumeInterruption({
46614
+ tenantId: tenantId2,
46615
+ taskId: task.id,
46616
+ actor: getRuntimeActor2(rc) ?? "system",
46617
+ ...threadId ? { threadId } : {}
46618
+ });
46619
+ }
46620
+ return bound ? {} : { taskId };
46621
+ },
46622
+ // SETTLE: a run that ends without a lifecycle transition converges the task.
46623
+ afterAgent: async (state, runtime) => {
46624
+ const taskId = typeof state?.taskId === "string" ? state.taskId : "";
46625
+ if (!taskId) return {};
46626
+ const rc = readRuntimeRunConfig(runtime);
46627
+ const tenantId2 = rc.tenantId || "default";
46628
+ const threadId = runtime?.configurable?.thread_id;
46629
+ const task = await getTaskStore().getById(tenantId2, taskId);
46630
+ if (task?.status === "in_progress" && task.context?.thread_id === threadId) {
46631
+ await createBoundTaskBridge().settleRun({
46632
+ tenantId: tenantId2,
46633
+ task,
46634
+ actor: getRuntimeActor2(rc) ?? "system",
46635
+ ...threadId ? { threadId } : {},
46636
+ outcome: { kind: "interrupted", summary: "Run ended without a task lifecycle transition" }
46637
+ });
46638
+ }
46639
+ taskRunRegistry.unregister(taskId);
46640
+ return {};
46641
+ },
46024
46642
  wrapModelCall: async (request, handler) => {
46025
46643
  const taskPrompt = `## Task Management
46026
46644
 
@@ -46029,8 +46647,11 @@ ongoing record of a GOAL and its ACCEPTANCE CRITERIA.
46029
46647
 
46030
46648
  ### When to create a task
46031
46649
  - Agent-owned work is multi-step and needs a persistent contract and plan ->
46032
- create the parent task FIRST (a \`pending\` record), then either execute it
46033
- inline and keep the status truthful, or dispatch it with \`task\`.
46650
+ create the parent task FIRST (a \`pending\` record), then process it in this run;
46651
+ mark it \`in_progress\` with \`manage_task set_status\` when you start, and
46652
+ \`completed\` when the acceptance criteria are met. To bring in an
46653
+ expert, create a subtask and dispatch it with \`task { taskId, agentId }\` \u2014 never
46654
+ re-dispatch the task you are processing.
46034
46655
  - Persistence is useful for resumption, review, or visibility across the
46035
46656
  independently verifiable business results.
46036
46657
 
@@ -46060,9 +46681,13 @@ ${TASK_CONVERGENCE_GUIDANCE}
46060
46681
  - No params: ownerType defaults to "user" with current user's ID
46061
46682
  - ownerType="agent": auto-fills ownerId from current agent (subtask for yourself)
46062
46683
  - Explicit ownerId: assign to a specific agent or user`;
46684
+ const boundTaskId = request.state?.taskId;
46685
+ const binding = typeof boundTaskId === "string" && boundTaskId ? renderTaskBinding(boundTaskId) : "";
46686
+ const rc = readRuntimeRunConfig(request.runtime);
46687
+ const executors = await renderAvailableExecutors(rc, rc.tenantId || "default");
46063
46688
  return handler({
46064
46689
  ...request,
46065
- systemPrompt: taskPrompt + "\n\n" + (request.systemPrompt ?? "")
46690
+ systemPrompt: taskPrompt + binding + executors + "\n\n" + (request.systemPrompt ?? "")
46066
46691
  });
46067
46692
  },
46068
46693
  tools: [
@@ -46087,15 +46712,14 @@ ${TASK_CONVERGENCE_GUIDANCE}
46087
46712
  - update: Update a task's content fields (title/description/priority/dueDate/metadata/parentId/sourceId/context/ownerId/result/failureReason/requireReview/dependencies/files)
46088
46713
  Status changes can also be done here, but use set_status for a simpler dedicated status update.
46089
46714
  - set_status: Change a task's status (id and status required). Use this when you only need to change the status.
46090
- Valid status values: pending, in_progress, review, failed, interrupted, completed, cancelled
46091
- Companion fields: result (for completed), failureReason (for failed), summary (for any status)
46092
- To mark complete: set_status with status='completed', result='...'
46715
+ Start processing a task you hold: set_status with status='in_progress' (the engine binds it to this run's thread).
46716
+ To mark complete: set_status with status='completed', result='...' (the task must be in_progress)
46093
46717
  To mark failed: set_status with status='failed', failureReason='...'
46094
46718
  To pause for input: set_status with status='interrupted', summary='what is needed'
46095
- Status can be freely changed to any valid value \u2014 no restrictions on transitions.
46719
+ Valid status values: pending, in_progress, review, failed, interrupted, completed, cancelled.
46096
46720
  set_status only changes the record; it never dispatches execution (only the \`task\` tool does).
46097
46721
  - add_activity: Add a Markdown activity without changing task fields or status (id and content required)
46098
- - delete: Delete a task (id required)
46722
+ - delete: Delete one task by id, or a batch with ids (best-effort per id; returns deleted/failed lists). At least one of id/ids is required.
46099
46723
 
46100
46724
  ## Agent completion beliefs
46101
46725
  Completing an agent-owned task MAY include a beliefImpact array reporting which
@@ -46106,14 +46730,16 @@ Report existing keys directly; set 'isNew: true' to add a key the owner does not
46106
46730
  track yet. Belief is evidence metadata: it never blocks a completion, and an
46107
46731
  unmatched or malformed entry is recorded without changing the owner's table.
46108
46732
  Create always persists a pending record \u2014 a supplied status is coerced and no
46109
- execution is dispatched. Only dispatching runs a task through the queue; an
46110
- agent may also work inline and update status.`,
46733
+ execution is dispatched. When you start processing a task you hold, set it
46734
+ \`in_progress\` with \`manage_task set_status\`; a dispatched task is claimed
46735
+ automatically by the engine. Use \`task { taskId, agentId }\` to delegate a subtask
46736
+ to an expert \u2014 never re-dispatch the task you are processing.`,
46111
46737
  schema: manageTaskSchema
46112
46738
  }
46113
46739
  ),
46114
46740
  (0, import_langchain87.tool)(handleStartTask, {
46115
46741
  name: "task",
46116
- description: "Start a persisted TaskItem with any Agent in a fresh isolated thread. An ordinary task returns an observation plus executionResultId that the caller passes to the manage_task update or add_activity that interprets it; a project task returns a managed result (success:true, managed:true) because the project-lifecycle queue owns execution.",
46742
+ description: `Start a persisted TaskItem with any existing Agent in a fresh isolated thread. Use this to delegate work \u2014 dispatch a subtask you created to the agent that should execute it; never re-dispatch the task you are currently processing (give the expert a child task instead). Pass agentId "general-purpose" for the general-purpose worker \u2014 ${DEFAULT_GENERAL_PURPOSE_DESCRIPTION} It has access to all the caller's tools, model, and middleware, so use it for general-purpose subtasks, to isolate context and token usage, and whenever no specialized agent fits. The target does NOT need task-management tools: it runs and returns an observation plus an executionResultId, and the caller passes that executionResultId to the manage_task update or add_activity that interprets and records it. A project task instead returns a managed result (success:true, managed:true) because the project-lifecycle queue owns execution.`,
46117
46743
  schema: startTaskSchema
46118
46744
  })
46119
46745
  ]
@@ -46255,7 +46881,7 @@ canonical Belief State:
46255
46881
  };
46256
46882
 
46257
46883
  // src/middlewares/documentLearningMiddleware.ts
46258
- var import_protocols34 = require("@axiom-lattice/protocols");
46884
+ var import_protocols35 = require("@axiom-lattice/protocols");
46259
46885
  var import_langchain88 = require("langchain");
46260
46886
 
46261
46887
  // src/middlewares/documentLearningSkills.ts
@@ -46522,7 +47148,7 @@ var documentLearningPlugin = {
46522
47148
  key: "document-learner",
46523
47149
  name: "Document Learning Agent",
46524
47150
  description: "Read documents and build structured skill systems with evaluations. Use this agent when you need to extract knowledge from documents and turn it into reusable, testable agent skills.",
46525
- type: import_protocols34.AgentType.DEEP_AGENT,
47151
+ type: import_protocols35.AgentType.DEEP_AGENT,
46526
47152
  prompt: DOCUMENT_LEARNER_SYSTEM_PROMPT,
46527
47153
  subAgents: ["document-parser-benchmark"],
46528
47154
  tools: [
@@ -46831,7 +47457,7 @@ function createParseDocumentTool({
46831
47457
  }
46832
47458
 
46833
47459
  // src/middlewares/documentParserMiddleware.ts
46834
- var import_protocols35 = require("@axiom-lattice/protocols");
47460
+ var import_protocols36 = require("@axiom-lattice/protocols");
46835
47461
 
46836
47462
  // src/middlewares/documentBenchmarkWorkflow.ts
46837
47463
  var DOCUMENT_PARSER_BENCHMARK_YAML = `name: document-parser-benchmark
@@ -47356,7 +47982,7 @@ var documentParserPlugin = {
47356
47982
  agents: {
47357
47983
  "document-parser-benchmark": {
47358
47984
  key: "document-parser-benchmark",
47359
- type: import_protocols35.AgentType.WORKFLOW,
47985
+ type: import_protocols36.AgentType.WORKFLOW,
47360
47986
  name: "Document Parser Benchmark",
47361
47987
  description: "Benchmark document parsing engines. Parses a document with 5 engines in parallel, scores results, and produces a ranking report. Use this when you need to evaluate which parsing engine works best for a given document.",
47362
47988
  prompt: "Benchmark document parsing engines: parse with 5 engines, score, and rank results.",
@@ -47393,7 +48019,7 @@ var documentParserPlugin = {
47393
48019
 
47394
48020
  // src/middlewares/projectRoomMiddleware.ts
47395
48021
  var import_crypto10 = require("crypto");
47396
- var import_protocols36 = require("@axiom-lattice/protocols");
48022
+ var import_protocols37 = require("@axiom-lattice/protocols");
47397
48023
  var import_langchain91 = require("langchain");
47398
48024
  var import_zod73 = require("zod");
47399
48025
  var PROJECT_ROOM_CONTEXT_REQUIRED = "PROJECT_ROOM_CONTEXT_REQUIRED";
@@ -47423,7 +48049,7 @@ function ownDataValue2(record, key5) {
47423
48049
  if (typeof record !== "object" || record === null || Array.isArray(record)) return void 0;
47424
48050
  try {
47425
48051
  const descriptor = Object.getOwnPropertyDescriptor(record, key5);
47426
- return (0, import_protocols36.descriptorDataValue)(descriptor)?.value;
48052
+ return (0, import_protocols37.descriptorDataValue)(descriptor)?.value;
47427
48053
  } catch {
47428
48054
  return void 0;
47429
48055
  }
@@ -47433,14 +48059,14 @@ function readTrustedRoomScope(config) {
47433
48059
  const runConfig = ownDataValue2(configurable, "runConfig");
47434
48060
  const projectRoom = ownDataValue2(runConfig, "projectRoom");
47435
48061
  try {
47436
- return (0, import_protocols36.parseTrustedRunContext)({ projectRoom }).projectRoom;
48062
+ return (0, import_protocols37.parseTrustedRunContext)({ projectRoom }).projectRoom;
47437
48063
  } catch {
47438
48064
  return void 0;
47439
48065
  }
47440
48066
  }
47441
48067
  function exactValues2(value, required, optional = []) {
47442
48068
  try {
47443
- return (0, import_protocols36.snapshotExactRecord)(value, required, optional);
48069
+ return (0, import_protocols37.snapshotExactRecord)(value, required, optional);
47444
48070
  } catch {
47445
48071
  return void 0;
47446
48072
  }
@@ -47476,7 +48102,7 @@ function snapshotMembership3(value) {
47476
48102
  return { ...values, joinedAt, updatedAt };
47477
48103
  }
47478
48104
  function snapshotRows(value) {
47479
- return (0, import_protocols36.snapshotExactArray)(value) ?? [];
48105
+ return (0, import_protocols37.snapshotExactArray)(value) ?? [];
47480
48106
  }
47481
48107
  async function hasActiveCurrentMembership(scope) {
47482
48108
  const memberships = getStoreLattice("default", "projectBotMembership").store;
@@ -47563,7 +48189,7 @@ var projectRoomPlugin = {
47563
48189
  // src/middlewares/sandboxPluginsMiddleware.ts
47564
48190
  var import_langchain92 = require("langchain");
47565
48191
  var import_zod74 = require("zod");
47566
- var import_protocols37 = require("@axiom-lattice/protocols");
48192
+ var import_protocols38 = require("@axiom-lattice/protocols");
47567
48193
 
47568
48194
  // src/middlewares/sandboxPluginsSkills.ts
47569
48195
  var PLUGIN_JSON_TEMPLATE = `{
@@ -48313,7 +48939,7 @@ var sandboxPluginsPlugin = {
48313
48939
  key: "plugin-builder",
48314
48940
  name: "Plugin Builder",
48315
48941
  description: "Author and manage tenant plugins in the shared sandbox: clarify goal, inputs/outputs, credentials, and whether a UI is needed; then write the manifest and JS handlers, add an optional HTML MCP App UI, and validate/test with the manage_plugin tool.",
48316
- type: import_protocols37.AgentType.DEEP_AGENT,
48942
+ type: import_protocols38.AgentType.DEEP_AGENT,
48317
48943
  prompt: PLUGIN_BUILDER_PROMPT,
48318
48944
  middleware: [
48319
48945
  {
@@ -49087,6 +49713,7 @@ registerBuiltinPlugins();
49087
49713
  TaskLifecycleNotifier,
49088
49714
  TaskLifecycleService,
49089
49715
  TaskRunBridgeStoresRequiredError,
49716
+ TaskRunRegistry,
49090
49717
  TaskStatus,
49091
49718
  TeamAgentGraphBuilder,
49092
49719
  TenantPluginCatalog,
@@ -49098,7 +49725,6 @@ registerBuiltinPlugins();
49098
49725
  VectorStoreProviderManager,
49099
49726
  VolumeFilesystem,
49100
49727
  abortWorkflowRun,
49101
- agentHasManagedTaskCapability,
49102
49728
  agentInstanceManager,
49103
49729
  agentLatticeManager,
49104
49730
  appendReconciliationWarning,
@@ -49273,6 +49899,9 @@ registerBuiltinPlugins();
49273
49899
  projectTaskAdmissionNotifier,
49274
49900
  queueLatticeManager,
49275
49901
  readLoopBreakerConfig,
49902
+ reconcileAllTenantTaskRuns,
49903
+ reconcileStaleTaskRuns,
49904
+ reconcileTenantTaskRuns,
49276
49905
  registerAgentLattice,
49277
49906
  registerAgentLatticeWithTenant,
49278
49907
  registerAgentLattices,
@@ -49321,6 +49950,7 @@ registerBuiltinPlugins();
49321
49950
  storeLatticeManager,
49322
49951
  sttModelLatticeManager,
49323
49952
  taskLifecycleNotifier,
49953
+ taskRunRegistry,
49324
49954
  toJsonSchema,
49325
49955
  toSafeStateExpr,
49326
49956
  toolLatticeManager,