@axiom-lattice/core 5.0.0 → 6.0.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.mjs CHANGED
@@ -28847,7 +28847,7 @@ var agentArchitectConfig = {
28847
28847
  "delete_agent",
28848
28848
  "manage_binding"
28849
28849
  ],
28850
- subAgents: ["document-parser-benchmark"],
28850
+ subAgents: ["document-parser", "document-parser-benchmark"],
28851
28851
  middleware: [
28852
28852
  {
28853
28853
  id: "filesystem",
@@ -30552,6 +30552,7 @@ async function removeCollectionVectorStore(name, tenantId2) {
30552
30552
 
30553
30553
  // src/mcp_lattice/McpLatticeManager.ts
30554
30554
  import { MultiServerMCPClient } from "@langchain/mcp-adapters";
30555
+ import { DynamicStructuredTool as DynamicStructuredTool2 } from "@langchain/core/tools";
30555
30556
 
30556
30557
  // src/mcp_lattice/reconnectPolicy.ts
30557
30558
  var DEFAULT_CONNECT_TIMEOUT_MS = 15e3;
@@ -30560,6 +30561,13 @@ var DEFAULT_DELAY_MS = 1e3;
30560
30561
  var MAX_BACKOFF_MS = 3e4;
30561
30562
  var HEALTH_CHECK_INTERVAL_MS = 3e4;
30562
30563
  var HEALTH_CHECK_TIMEOUT_MS = 5e3;
30564
+ function isMcpConnectionError(error) {
30565
+ const message = error instanceof Error ? error.message : typeof error === "string" ? error : "";
30566
+ if (!message) return false;
30567
+ return /not connected|connection closed|session not found|no session|maximum reconnection/i.test(
30568
+ message
30569
+ );
30570
+ }
30563
30571
  function nextBackoffDelay(attempt, base = DEFAULT_DELAY_MS, max = MAX_BACKOFF_MS) {
30564
30572
  if (attempt <= 0) return base;
30565
30573
  return Math.min(base * Math.pow(2, attempt - 1), max);
@@ -30584,12 +30592,21 @@ async function withTimeout(operation, ms, onTimeout) {
30584
30592
 
30585
30593
  // src/mcp_lattice/McpLatticeManager.ts
30586
30594
  var realClientFactory = ({ mcpServers }) => new MultiServerMCPClient({ mcpServers });
30595
+ function extractRawCall(tool68) {
30596
+ const rawCall = tool68._call;
30597
+ if (typeof rawCall === "function") return rawCall.bind(tool68);
30598
+ return async (...args) => {
30599
+ const invoke = tool68.invoke.bind(tool68);
30600
+ return invoke(args[0], args[2]);
30601
+ };
30602
+ }
30587
30603
  var McpLatticeManager = class _McpLatticeManager extends BaseLatticeManager {
30588
30604
  constructor() {
30589
30605
  super(...arguments);
30590
30606
  this.runtimes = /* @__PURE__ */ new Map();
30591
30607
  this.clientFactory = realClientFactory;
30592
30608
  this.healthCheckInFlight = false;
30609
+ this.reconnectPromises = /* @__PURE__ */ new Map();
30593
30610
  }
30594
30611
  static getInstance() {
30595
30612
  if (!_McpLatticeManager._instance) {
@@ -30847,6 +30864,64 @@ var McpLatticeManager = class _McpLatticeManager extends BaseLatticeManager {
30847
30864
  rt.attempt = 0;
30848
30865
  await this.reconnectRuntime(rt);
30849
30866
  }
30867
+ /**
30868
+ * Single-flight a forced reconnect per `(tenantId, serverKey)` so concurrent
30869
+ * tool calls that all hit the same dead session reconnect once, not once each.
30870
+ */
30871
+ reconnectShared(rt) {
30872
+ const key5 = this.compositeKey(rt.id.tenantId, rt.id.serverKey);
30873
+ const existing = this.reconnectPromises.get(key5);
30874
+ if (existing) return existing;
30875
+ const work = this.reconnectServer(rt.id.tenantId, rt.id.serverKey).finally(() => {
30876
+ if (this.reconnectPromises.get(key5) === work) this.reconnectPromises.delete(key5);
30877
+ });
30878
+ this.reconnectPromises.set(key5, work);
30879
+ return work;
30880
+ }
30881
+ /**
30882
+ * Wrap a raw MCP tool so a call landing on a torn-down session
30883
+ * (`Not connected` / `Connection closed` / session expiry) transparently
30884
+ * forces a reconnect and retries once against the fresh client, instead of
30885
+ * failing the surrounding run. Non-connection errors propagate untouched.
30886
+ */
30887
+ wrapToolForResilience(rt, raw) {
30888
+ const call = extractRawCall(raw);
30889
+ return new DynamicStructuredTool2({
30890
+ name: raw.name,
30891
+ description: raw.description,
30892
+ schema: raw.schema,
30893
+ responseFormat: raw.responseFormat,
30894
+ defaultConfig: raw.defaultConfig,
30895
+ metadata: raw.metadata,
30896
+ returnDirect: raw.returnDirect,
30897
+ func: async (...args) => this.invokeResilient(rt, raw.name, call, args)
30898
+ });
30899
+ }
30900
+ async invokeResilient(rt, toolName2, call, args) {
30901
+ try {
30902
+ await this.ensureConnected(rt.id.tenantId, rt.id.serverKey);
30903
+ return await call(...args);
30904
+ } catch (error) {
30905
+ if (!isMcpConnectionError(error) || !this.isActive(rt)) throw error;
30906
+ console.warn(
30907
+ `[MCP] ${rt.id.tenantId}/${rt.id.serverKey}: tool "${toolName2}" hit a dead session; reconnecting and retrying once`,
30908
+ error instanceof Error ? error.message : error
30909
+ );
30910
+ try {
30911
+ await this.reconnectShared(rt);
30912
+ } catch (reconnectError) {
30913
+ console.warn(
30914
+ `[MCP] ${rt.id.tenantId}/${rt.id.serverKey}: reconnect for tool "${toolName2}" failed`,
30915
+ reconnectError instanceof Error ? reconnectError.message : reconnectError
30916
+ );
30917
+ throw error;
30918
+ }
30919
+ const fresh = await this.getRawTools(rt.id.tenantId, rt.id.serverKey);
30920
+ const match = fresh.find((t) => t.name === toolName2);
30921
+ if (!match) throw error;
30922
+ return await extractRawCall(match)(...args);
30923
+ }
30924
+ }
30850
30925
  async checkHealth() {
30851
30926
  if (this.healthCheckInFlight) return;
30852
30927
  this.healthCheckInFlight = true;
@@ -30865,8 +30940,12 @@ var McpLatticeManager = class _McpLatticeManager extends BaseLatticeManager {
30865
30940
  await withTimeout(session.listTools(), HEALTH_CHECK_TIMEOUT_MS, async () => {
30866
30941
  });
30867
30942
  if (!this.isActive(rt) || rt.state !== "connected") continue;
30868
- } catch {
30943
+ } catch (error) {
30869
30944
  if (this.isActive(rt) && rt.state === "connected") {
30945
+ console.warn(
30946
+ `[MCP] ${rt.id.tenantId}/${rt.id.serverKey}: health check failed; scheduling reconnect`,
30947
+ error instanceof Error ? error.message : error
30948
+ );
30870
30949
  rt.state = "failed";
30871
30950
  this.scheduleReconnect(rt);
30872
30951
  }
@@ -31072,7 +31151,8 @@ var McpLatticeManager = class _McpLatticeManager extends BaseLatticeManager {
31072
31151
  const visibility = Array.isArray(rawVisibility) ? rawVisibility : typeof rawVisibility === "string" ? [rawVisibility] : void 0;
31073
31152
  if (visibility && !visibility.includes("model")) continue;
31074
31153
  const ref = extractAppsRef(meta, serverKey);
31075
- const registered = ref ? wrapMcpToolWithUi(tool68, ref) : tool68;
31154
+ const resilient = this.wrapToolForResilience(rt, tool68);
31155
+ const registered = ref ? wrapMcpToolWithUi(resilient, ref) : resilient;
31076
31156
  const toolKey = _McpLatticeManager.buildToolKey(serverKey, tool68.name);
31077
31157
  try {
31078
31158
  toolLatticeManager.registerExistingToolWithTenant(tenantId2, toolKey, registered);
@@ -31141,6 +31221,7 @@ var McpLatticeManager = class _McpLatticeManager extends BaseLatticeManager {
31141
31221
  }
31142
31222
  }
31143
31223
  this.runtimes.clear();
31224
+ this.reconnectPromises.clear();
31144
31225
  }
31145
31226
  };
31146
31227
  var mcpManager = McpLatticeManager.getInstance();
@@ -35240,7 +35321,7 @@ function snapshotDate(value) {
35240
35321
 
35241
35322
  // src/services/TaskLifecycleService.ts
35242
35323
  import { createHash as createHash6 } from "crypto";
35243
- import { parseTaskBeliefState, replaceTaskBeliefState, snapshotExactArray as snapshotExactArray2, snapshotExactRecord as snapshotExactRecord7, taskBeliefStatesEqual } from "@axiom-lattice/protocols";
35324
+ import { parseTaskBeliefState, replaceTaskBeliefState, taskBeliefStatesEqual } from "@axiom-lattice/protocols";
35244
35325
 
35245
35326
  // src/middlewares/taskBelief.ts
35246
35327
  import { createHash as createHash5 } from "crypto";
@@ -35352,14 +35433,6 @@ async function applyBeliefImpacts(taskStore, owner, impacts) {
35352
35433
  }
35353
35434
  return { description: current.description ?? "", task: current };
35354
35435
  }
35355
- function parseExternalDependency(value) {
35356
- const row = snapshotExactRecord7(value, ["type", "summary", "requestedAt", "dependencyTaskIds"]);
35357
- const ids = snapshotExactArray2(row?.dependencyTaskIds);
35358
- 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;
35359
- const trimmed = ids.map((id) => id.trim());
35360
- if (new Set(trimmed).size !== trimmed.length) return void 0;
35361
- return { type: "external_dependency", summary: row.summary, requestedAt: row.requestedAt, dependencyTaskIds: trimmed };
35362
- }
35363
35436
  function unsupportedTaskStatus() {
35364
35437
  return failure2(
35365
35438
  "TASK_STATUS_UNSUPPORTED",
@@ -35658,9 +35731,6 @@ var TaskLifecycleService = class {
35658
35731
  if (interruption && typeof interruption === "object" && interruption.type === "review_required") {
35659
35732
  return failure2("REVIEW_APPROVAL_REQUIRED", "Review interruption requires explicit approval or rejection.");
35660
35733
  }
35661
- if (interruption && typeof interruption === "object" && interruption.type === "external_dependency" && !parseExternalDependency(interruption)) {
35662
- return failure2("INVALID_EXTERNAL_DEPENDENCY", "Stored external dependency interruption is malformed.");
35663
- }
35664
35734
  const previousInterruption = interruption && typeof interruption === "object" ? interruption : {};
35665
35735
  const resumeKey = this.resumeEventKey(task);
35666
35736
  const audit = { eventKey: resumeKey, previousInterruption };
@@ -35841,6 +35911,108 @@ var TaskLifecycleService = class {
35841
35911
  detail: { previousStatus: "failed" }
35842
35912
  });
35843
35913
  }
35914
+ /**
35915
+ * Reopens a terminal agent task by returning it to in_progress.
35916
+ *
35917
+ * @param input Task, actor, and optional thread.
35918
+ * @returns Structured lifecycle result. `completed` and `cancelled` tasks
35919
+ * reopen to `in_progress`; an already-`in_progress` task is acknowledged
35920
+ * without mutation; any other status or a lost CAS yields
35921
+ * `TASK_STATUS_CONFLICT`.
35922
+ * @remarks This is the single reverse edge of the terminal-irreversibility
35923
+ * relaxation. It records the status change only; re-dispatch is owned by
35924
+ * the application layer and is never triggered here.
35925
+ */
35926
+ async reopenTask(input) {
35927
+ return this.transitionAgentTask({
35928
+ ...input,
35929
+ targetStatus: "in_progress",
35930
+ expectedStatuses: ["completed", "cancelled"],
35931
+ updates: { status: "in_progress", failureReason: null, result: null },
35932
+ summary: "Task reopened",
35933
+ detail: { type: "reopen" }
35934
+ });
35935
+ }
35936
+ /**
35937
+ * Transfers a human-authored task's ownership to a chosen Agent before dispatch.
35938
+ *
35939
+ * @param input Task, actor, destination Agent, and optional Task Thread.
35940
+ * @returns Structured result with the reassigned owner and an `owner_assigned` audit.
35941
+ * @remarks Only tasks with `context.source === "user"` can be assigned this way.
35942
+ * Repeated assignment to the same owner is idempotent (repair-writes the audit
35943
+ * without a CAS). This changes owner only, never status, so it publishes no
35944
+ * Project lifecycle notification.
35945
+ */
35946
+ async assignOwner(input) {
35947
+ let task;
35948
+ try {
35949
+ task = await this.deps.taskStore.getById(input.tenantId, input.taskId);
35950
+ } catch {
35951
+ return failure2("TASK_STORE_READ_FAILED", "Failed to read task lifecycle state.");
35952
+ }
35953
+ if (!task) return failure2("TASK_NOT_FOUND", "Task was not found.");
35954
+ if (task.context?.source !== "user") {
35955
+ return failure2("TASK_NOT_HUMAN_MANAGED", "Only user-authored tasks can be assigned to an Agent.");
35956
+ }
35957
+ const storedThreadId = typeof task.context?.thread_id === "string" && task.context.thread_id.length > 0 ? task.context.thread_id : void 0;
35958
+ const effectiveThreadId = storedThreadId ?? input.threadId;
35959
+ const detail = { type: "owner_assigned", fromOwnerType: task.ownerType, toOwnerId: input.ownerId };
35960
+ if (task.ownerType === "agent" && task.ownerId === input.ownerId && (input.threadId === void 0 || storedThreadId === input.threadId)) {
35961
+ const eventKey2 = this.statusEventKey(task, "owner_assigned", detail);
35962
+ const event2 = await this.writeStatusEvent(
35963
+ task,
35964
+ input.actor,
35965
+ effectiveThreadId,
35966
+ "activity",
35967
+ "Task owner assigned",
35968
+ detail,
35969
+ eventKey2
35970
+ );
35971
+ return {
35972
+ success: true,
35973
+ task,
35974
+ mutated: false,
35975
+ eventKey: event2.eventKey,
35976
+ ...event2.warning ? { warnings: [event2.warning] } : {}
35977
+ };
35978
+ }
35979
+ const context = { ...task.context ?? {} };
35980
+ if (input.threadId && !storedThreadId) context.thread_id = input.threadId;
35981
+ let updated;
35982
+ try {
35983
+ updated = await this.deps.taskStore.updateIfStatusAndUpdatedAt(
35984
+ input.tenantId,
35985
+ input.taskId,
35986
+ { ownerType: "agent", ownerId: input.ownerId, context },
35987
+ [task.status],
35988
+ task.updatedAt
35989
+ );
35990
+ } catch {
35991
+ return failure2(
35992
+ "TASK_STORE_WRITE_FAILED",
35993
+ "Failed to assign the task owner.",
35994
+ "The store rejected the write."
35995
+ );
35996
+ }
35997
+ if (!updated) return failure2("TASK_STATUS_CONFLICT", "Task changed concurrently.");
35998
+ const eventKey = this.statusEventKey(updated, "owner_assigned", detail);
35999
+ const event = await this.writeStatusEvent(
36000
+ updated,
36001
+ input.actor,
36002
+ effectiveThreadId,
36003
+ "activity",
36004
+ "Task owner assigned",
36005
+ detail,
36006
+ eventKey
36007
+ );
36008
+ return {
36009
+ success: true,
36010
+ task: updated,
36011
+ mutated: true,
36012
+ eventKey: event.eventKey,
36013
+ ...event.warning ? { warnings: [event.warning] } : {}
36014
+ };
36015
+ }
35844
36016
  /** Atomically cancels a nonterminal agent task and records an idempotent status event. */
35845
36017
  async cancelTask(input) {
35846
36018
  return this.transitionAgentTask({
@@ -35921,19 +36093,7 @@ var TaskLifecycleService = class {
35921
36093
  async interruptTask(input) {
35922
36094
  const summary = input.summary.trim();
35923
36095
  if (!summary) return failure2("MISSING_INTERRUPTION_SUMMARY", "Agent interruption requires a nonblank summary.");
35924
- if (input.type !== "external_dependency" && input.dependencyTaskIds !== void 0) {
35925
- return failure2("INVALID_EXTERNAL_DEPENDENCY", "dependencyTaskIds are only valid for external dependencies.");
35926
- }
35927
36096
  const requestedAt = this.now().toISOString();
35928
- const external = input.type === "external_dependency" ? parseExternalDependency({
35929
- type: input.type,
35930
- summary,
35931
- requestedAt,
35932
- dependencyTaskIds: input.dependencyTaskIds
35933
- }) : void 0;
35934
- if (input.type === "external_dependency" && !external) {
35935
- return failure2("INVALID_EXTERNAL_DEPENDENCY", "External dependencies require exact, unique nonblank task IDs.");
35936
- }
35937
36097
  let task;
35938
36098
  try {
35939
36099
  task = await this.deps.taskStore.getById(input.tenantId, input.taskId);
@@ -35947,29 +36107,17 @@ var TaskLifecycleService = class {
35947
36107
  if (threadFailure) return threadFailure;
35948
36108
  const currentInterruption = task.context?.interruption;
35949
36109
  if (task.status === "interrupted" && currentInterruption && typeof currentInterruption === "object") {
35950
- const storedExternal = parseExternalDependency(currentInterruption);
35951
- if (input.type === "external_dependency") {
35952
- if (!external || !storedExternal || storedExternal.summary !== external.summary || canonicalJson(storedExternal.dependencyTaskIds) !== canonicalJson(external.dependencyTaskIds)) {
35953
- return failure2(
35954
- "TASK_STATUS_CONFLICT",
35955
- "Task is already interrupted by a different or malformed external dependency.",
35956
- "Use manage_task get to inspect the existing interruption in task context.interruption."
35957
- );
35958
- }
35959
- } else if (currentInterruption.type !== input.type || currentInterruption.summary !== summary) {
36110
+ if (currentInterruption.type !== input.type || currentInterruption.summary !== summary) {
35960
36111
  return failure2(
35961
36112
  "TASK_STATUS_CONFLICT",
35962
36113
  "Task is already interrupted by a different request.",
35963
36114
  "Use manage_task get to inspect the existing interruption, or resume it first."
35964
36115
  );
35965
36116
  }
35966
- const detail = storedExternal ? { ...storedExternal } : currentInterruption;
36117
+ const detail = currentInterruption;
35967
36118
  const eventKey2 = this.statusEventKey(task, "interrupted", detail);
35968
36119
  const event2 = await this.writeStatusEvent(task, input.actor, input.threadId, "interrupted", summary, detail, eventKey2);
35969
36120
  const warnings2 = event2.warning ? [event2.warning] : [];
35970
- if (storedExternal && task.workspaceId && task.projectId) {
35971
- return this.finishExternalDependencyInterruption(task, input, storedExternal, event2, warnings2, false);
35972
- }
35973
36121
  return {
35974
36122
  success: true,
35975
36123
  task,
@@ -35985,11 +36133,7 @@ var TaskLifecycleService = class {
35985
36133
  "Only pending or in_progress tasks can be interrupted. Use manage_task get to check the current status."
35986
36134
  );
35987
36135
  }
35988
- if (external && task.workspaceId && task.projectId) {
35989
- const dependencyFailure = await this.validateExternalDependencies(task, external.dependencyTaskIds);
35990
- if (dependencyFailure) return dependencyFailure;
35991
- }
35992
- const interruption = external ? { ...external } : { type: input.type, summary, requestedAt };
36136
+ const interruption = { type: input.type, summary, requestedAt };
35993
36137
  let updated;
35994
36138
  try {
35995
36139
  updated = await this.deps.taskStore.updateIfStatusAndUpdatedAt(input.tenantId, input.taskId, {
@@ -36011,9 +36155,6 @@ var TaskLifecycleService = class {
36011
36155
  const eventKey = this.statusEventKey(updated, "interrupted", interruption);
36012
36156
  const event = await this.writeStatusEvent(updated, input.actor, input.threadId, "interrupted", summary, interruption, eventKey);
36013
36157
  const warnings = event.warning ? [event.warning] : await this.notifyWinningTransition(updated, "interrupted", event);
36014
- if (external && updated.workspaceId && updated.projectId) {
36015
- return this.finishExternalDependencyInterruption(updated, input, external, event, warnings, true);
36016
- }
36017
36158
  return {
36018
36159
  success: true,
36019
36160
  task: updated,
@@ -37252,7 +37393,9 @@ var TaskLifecycleService = class {
37252
37393
  };
37253
37394
  }
37254
37395
  if (!input.expectedStatuses.includes(task.status)) return failure2("TASK_STATUS_CONFLICT", "Task status cannot make this transition.");
37255
- const updates = { ...input.updates, context: clearInterruptionContext(task.context) };
37396
+ const clearedContext = clearInterruptionContext(task.context);
37397
+ const withThread = input.threadId && task.context?.thread_id === void 0 ? { ...clearedContext ?? {}, thread_id: input.threadId } : clearedContext;
37398
+ const updates = { ...input.updates, context: withThread };
37256
37399
  let updated;
37257
37400
  try {
37258
37401
  updated = await this.deps.taskStore.updateIfStatusAndUpdatedAt(
@@ -37340,85 +37483,6 @@ var TaskLifecycleService = class {
37340
37483
  return [{ code: "LIFECYCLE_NOTIFICATION_FAILED", taskId: task.id, eventKey: event.eventKey }];
37341
37484
  }
37342
37485
  }
37343
- async validateExternalDependencies(task, dependencyTaskIds) {
37344
- const declared = new Set(task.dependencies ?? []);
37345
- if (dependencyTaskIds.some((id) => id === task.id || !declared.has(id))) {
37346
- return failure2("INVALID_EXTERNAL_DEPENDENCY", "External dependency IDs must be declared non-self dependencies.");
37347
- }
37348
- try {
37349
- for (const id of dependencyTaskIds) {
37350
- const dependency = await this.deps.taskStore.getById(task.tenantId, id);
37351
- if (!dependency || dependency.workspaceId !== task.workspaceId || dependency.projectId !== task.projectId || dependency.status === "completed") {
37352
- return failure2("INVALID_EXTERNAL_DEPENDENCY", "External dependencies must exist, remain incomplete, and share exact scope.");
37353
- }
37354
- }
37355
- } catch {
37356
- return failure2("TASK_STORE_READ_FAILED", "Failed to validate external dependencies.");
37357
- }
37358
- return void 0;
37359
- }
37360
- async externalDependencyReadiness(task, dependencyTaskIds) {
37361
- try {
37362
- for (const id of dependencyTaskIds) {
37363
- const dependency = await this.deps.taskStore.getById(task.tenantId, id);
37364
- if (!dependency || dependency.workspaceId !== task.workspaceId || dependency.projectId !== task.projectId || dependency.status !== "completed") return { kind: "blocked" };
37365
- }
37366
- return { kind: "ready" };
37367
- } catch {
37368
- return {
37369
- kind: "error",
37370
- warning: { code: "DEPENDENCY_READ_FAILED", taskId: task.id }
37371
- };
37372
- }
37373
- }
37374
- async finishExternalDependencyInterruption(task, input, interruption, event, warnings, mutated) {
37375
- if (event.warning) {
37376
- return { success: true, task, mutated, eventKey: event.eventKey, warnings };
37377
- }
37378
- const readiness = await this.externalDependencyReadiness(task, interruption.dependencyTaskIds);
37379
- if (readiness.kind === "blocked") {
37380
- return { success: true, task, mutated, eventKey: event.eventKey, ...warnings.length ? { warnings } : {} };
37381
- }
37382
- if (readiness.kind === "error") {
37383
- return {
37384
- success: true,
37385
- task,
37386
- mutated,
37387
- eventKey: event.eventKey,
37388
- warnings: [...warnings, { ...readiness.warning, eventKey: event.eventKey }]
37389
- };
37390
- }
37391
- const resumed = await this.resumeInterruption({
37392
- tenantId: input.tenantId,
37393
- taskId: input.taskId,
37394
- actor: input.actor,
37395
- threadId: input.threadId
37396
- });
37397
- if (resumed.success) {
37398
- const combined = [...warnings, ...resumed.warnings ?? []];
37399
- return {
37400
- ...resumed,
37401
- mutated: mutated || resumed.mutated,
37402
- ...combined.length ? { warnings: combined } : {}
37403
- };
37404
- }
37405
- let current;
37406
- try {
37407
- current = await this.deps.taskStore.getById(input.tenantId, input.taskId);
37408
- } catch {
37409
- return resumed;
37410
- }
37411
- if (current?.status === "in_progress" && (!current.context?.thread_id || current.context?.thread_id === input.threadId)) {
37412
- return {
37413
- success: true,
37414
- task: current,
37415
- mutated,
37416
- eventKey: event.eventKey,
37417
- ...warnings.length ? { warnings } : {}
37418
- };
37419
- }
37420
- return resumed;
37421
- }
37422
37486
  async clearAuditMarker(task, key5) {
37423
37487
  const context = withoutContextKey(task.context, key5);
37424
37488
  try {
@@ -41316,6 +41380,9 @@ function trustedRosterScope(trusted) {
41316
41380
  callerAssistantId: context.assistantId
41317
41381
  };
41318
41382
  }
41383
+ function trustedRosterExempt(trusted) {
41384
+ return trusted.projectTask?.rosterExempt === true;
41385
+ }
41319
41386
  async function requireTrustedOwner(trusted, targetAssistantId) {
41320
41387
  const context = trusted.projectTask ?? trusted.projectRoom;
41321
41388
  const memberships = getStoreLattice("default", "projectBotMembership").store;
@@ -41580,6 +41647,7 @@ async function validateTrustedChildDependencyWait(store, tenantId2, parent, depe
41580
41647
  if (dependency.status === "completed") continue;
41581
41648
  if (!["pending", "in_progress", "interrupted"].includes(dependency.status) || dependency.parentId !== parent.id || dependency.ownerType !== "agent" || !hasExactContextSource(dependency, expectedSource)) return void 0;
41582
41649
  incompleteChildren += 1;
41650
+ if (trustedRosterExempt(trusted)) continue;
41583
41651
  try {
41584
41652
  await requireTrustedOwner(trusted, dependency.ownerId);
41585
41653
  } catch (error) {
@@ -41654,7 +41722,6 @@ var manageTaskSchema = z70.object({
41654
41722
  ownerId: z70.string().optional().describe("Owner ID. Auto-filled from current user/agent if omitted"),
41655
41723
  requireReview: z70.boolean().optional().describe("If true, agent completion pauses as interrupted(review_required) for HITL approval"),
41656
41724
  dependencies: z70.array(z70.string()).optional().describe("List of task IDs that must be completed before this task can start"),
41657
- dependencyTaskIds: z70.array(z70.string().trim().min(1)).optional().describe("Exact blocking task IDs required for an external_dependency interruption"),
41658
41725
  result: z70.string().optional().describe("Result summary. Used with set_status status='completed' or update status='completed'"),
41659
41726
  failureReason: z70.string().optional().describe("Failure reason. Used with set_status status='failed' or update status='failed'"),
41660
41727
  files: z70.array(z70.object({
@@ -41823,7 +41890,7 @@ function createTaskMiddleware(options = {}) {
41823
41890
  const workspaceId = trustedScope?.workspaceId ?? rc.workspaceId;
41824
41891
  const projectId = trustedScope?.projectId ?? rc.projectId;
41825
41892
  const revalidateCaller = async () => {
41826
- if (!trustedProject) return void 0;
41893
+ if (!trustedProject || trustedRosterExempt(trustedProject)) return void 0;
41827
41894
  try {
41828
41895
  await createProjectTaskOwnerPolicy().requireCaller(trustedRosterScope(trustedProject));
41829
41896
  } catch (error) {
@@ -41831,7 +41898,7 @@ function createTaskMiddleware(options = {}) {
41831
41898
  }
41832
41899
  return void 0;
41833
41900
  };
41834
- if (trustedProject) {
41901
+ if (trustedProject && !trustedRosterExempt(trustedProject)) {
41835
41902
  try {
41836
41903
  await createProjectTaskOwnerPolicy().requireCaller(trustedRosterScope(trustedProject));
41837
41904
  } catch (error) {
@@ -41864,10 +41931,12 @@ function createTaskMiddleware(options = {}) {
41864
41931
  if (trustedProject) {
41865
41932
  const snapshot = taskMutationSnapshot(existing);
41866
41933
  if (!snapshot) return trustedContextError();
41867
- try {
41868
- await createProjectTaskOwnerPolicy().requireCaller(trustedRosterScope(trustedProject));
41869
- } catch (error) {
41870
- return projectPolicyError(error);
41934
+ if (!trustedRosterExempt(trustedProject)) {
41935
+ try {
41936
+ await createProjectTaskOwnerPolicy().requireCaller(trustedRosterScope(trustedProject));
41937
+ } catch (error) {
41938
+ return projectPolicyError(error);
41939
+ }
41871
41940
  }
41872
41941
  deleted = await store.deleteIfSnapshot(tenantId2, id, snapshot);
41873
41942
  } else {
@@ -41920,13 +41989,15 @@ function createTaskMiddleware(options = {}) {
41920
41989
  "Target Project task owner is not active in the exact roster."
41921
41990
  ));
41922
41991
  }
41923
- try {
41924
- await requireTrustedOwner(trustedProject, ownerId);
41925
- } catch (error) {
41926
- return projectPolicyError(error instanceof ProjectTaskOwnerPolicyError ? error : new ProjectTaskOwnerPolicyError(
41927
- "PROJECT_TASK_OWNER_INACTIVE",
41928
- "Target Project task owner is not active in the exact roster."
41929
- ));
41992
+ if (!trustedRosterExempt(trustedProject)) {
41993
+ try {
41994
+ await requireTrustedOwner(trustedProject, ownerId);
41995
+ } catch (error) {
41996
+ return projectPolicyError(error instanceof ProjectTaskOwnerPolicyError ? error : new ProjectTaskOwnerPolicyError(
41997
+ "PROJECT_TASK_OWNER_INACTIVE",
41998
+ "Target Project task owner is not active in the exact roster."
41999
+ ));
42000
+ }
41930
42001
  }
41931
42002
  }
41932
42003
  if (effectiveOwnerType === "agent" && (input.result !== void 0 || input.failureReason !== void 0)) {
@@ -41974,10 +42045,12 @@ function createTaskMiddleware(options = {}) {
41974
42045
  )) return trustedContextError("Parent or dependency task changed before creation.");
41975
42046
  const callerError = await revalidateCaller();
41976
42047
  if (callerError) return callerError;
41977
- try {
41978
- await requireTrustedOwner(trustedProject, ownerId);
41979
- } catch (error) {
41980
- return projectPolicyError(error);
42048
+ if (!trustedRosterExempt(trustedProject)) {
42049
+ try {
42050
+ await requireTrustedOwner(trustedProject, ownerId);
42051
+ } catch (error) {
42052
+ return projectPolicyError(error);
42053
+ }
41981
42054
  }
41982
42055
  }
41983
42056
  return store.create({
@@ -42235,18 +42308,6 @@ function createTaskMiddleware(options = {}) {
42235
42308
  return trustedFailure;
42236
42309
  }
42237
42310
  }
42238
- if (trustedProject && input.dependencyTaskIds !== void 0) {
42239
- const interruption = input.context?.interruption;
42240
- const interruptionType2 = interruption && typeof interruption === "object" ? interruption.type : void 0;
42241
- if (input.status !== "interrupted" || interruptionType2 !== "external_dependency") {
42242
- taskUpdateBlocked(input.id, "INVALID_EXTERNAL_DEPENDENCY", {});
42243
- return JSON.stringify({
42244
- success: false,
42245
- code: "INVALID_EXTERNAL_DEPENDENCY",
42246
- error: "dependencyTaskIds are only valid for external dependency interruptions."
42247
- });
42248
- }
42249
- }
42250
42311
  if (trustedProject && (input.ownerType !== void 0 && input.ownerType !== existing.ownerType || input.ownerId !== void 0 && input.ownerId !== existing.ownerId)) {
42251
42312
  taskUpdateBlocked(input.id, "TASK_OWNERSHIP_VIA_DISPATCH_REQUIRED", {
42252
42313
  inputOwnerType: input.ownerType,
@@ -42438,7 +42499,7 @@ function createTaskMiddleware(options = {}) {
42438
42499
  return trustedContextError("Parent or dependency task changed before update.");
42439
42500
  }
42440
42501
  }
42441
- if (trustedProject) {
42502
+ if (trustedProject && !trustedRosterExempt(trustedProject)) {
42442
42503
  try {
42443
42504
  await createProjectTaskOwnerPolicy().requireCaller(trustedRosterScope(trustedProject));
42444
42505
  } catch (error) {
@@ -42478,7 +42539,7 @@ function createTaskMiddleware(options = {}) {
42478
42539
  const workItemSummary = input.summary;
42479
42540
  let activityWarnings;
42480
42541
  try {
42481
- if (trustedProject) {
42542
+ if (trustedProject && !trustedRosterExempt(trustedProject)) {
42482
42543
  try {
42483
42544
  await createProjectTaskOwnerPolicy().requireCaller(trustedRosterScope(trustedProject));
42484
42545
  } catch (error) {
@@ -42563,18 +42624,6 @@ function createTaskMiddleware(options = {}) {
42563
42624
  return trustedFailure;
42564
42625
  }
42565
42626
  }
42566
- if (trustedProject && input.dependencyTaskIds !== void 0) {
42567
- const interruption = input.context?.interruption;
42568
- const interruptionType2 = interruption && typeof interruption === "object" ? interruption.type : void 0;
42569
- if (input.status !== "interrupted" || interruptionType2 !== "external_dependency") {
42570
- taskUpdateBlocked(input.id, "INVALID_EXTERNAL_DEPENDENCY", {});
42571
- return JSON.stringify({
42572
- success: false,
42573
- code: "INVALID_EXTERNAL_DEPENDENCY",
42574
- error: "dependencyTaskIds are only valid for external dependency interruptions."
42575
- });
42576
- }
42577
- }
42578
42627
  if (trustedProject && (input.ownerType !== void 0 && input.ownerType !== existing.ownerType || input.ownerId !== void 0 && input.ownerId !== existing.ownerId)) {
42579
42628
  taskUpdateBlocked(input.id, "TASK_OWNERSHIP_VIA_DISPATCH_REQUIRED", {});
42580
42629
  return JSON.stringify({
@@ -42656,11 +42705,11 @@ function createTaskMiddleware(options = {}) {
42656
42705
  const interruption = input.context?.interruption;
42657
42706
  const explicitType = interruption && typeof interruption === "object" ? interruption.type : void 0;
42658
42707
  const type = explicitType ?? "missing_input";
42659
- if (type !== "missing_input" && type !== "external_dependency" && type !== "user_decision") {
42708
+ if (type !== "missing_input" && type !== "user_decision") {
42660
42709
  return JSON.stringify({
42661
42710
  success: false,
42662
42711
  code: "INVALID_INTERRUPTION_TYPE",
42663
- error: "Agent interruption type must be missing_input, external_dependency, or user_decision."
42712
+ error: "Agent interruption type must be missing_input or user_decision."
42664
42713
  });
42665
42714
  }
42666
42715
  await validateExecutionResult();
@@ -42672,8 +42721,7 @@ function createTaskMiddleware(options = {}) {
42672
42721
  type,
42673
42722
  summary: input.summary ?? "",
42674
42723
  actor,
42675
- threadId: statusThreadId,
42676
- dependencyTaskIds: input.dependencyTaskIds
42724
+ threadId: statusThreadId
42677
42725
  });
42678
42726
  return lifecycleMutationResponse(interrupted);
42679
42727
  }
@@ -43381,7 +43429,6 @@ canonical Belief State:
43381
43429
  };
43382
43430
 
43383
43431
  // src/middlewares/documentLearningMiddleware.ts
43384
- import { AgentType as AgentType9 } from "@axiom-lattice/protocols";
43385
43432
  import { createMiddleware as createMiddleware24 } from "langchain";
43386
43433
 
43387
43434
  // src/middlewares/documentLearningSkills.ts
@@ -43626,15 +43673,6 @@ var createDocumentLearningMiddleware = () => createMiddleware24({
43626
43673
  contextSchema,
43627
43674
  tools: []
43628
43675
  });
43629
- var DOCUMENT_LEARNER_SYSTEM_PROMPT = `You are a capability learning specialist.
43630
-
43631
- CRITICAL FIRST ACTION \u2014 before any response about the task:
43632
- 1. Call the \`skill\` tool with skill_name: "agent-architecture" to load the
43633
- authoritative Charter.
43634
- 2. Call the \`skill\` tool with skill_name: "document-learning-learn-capability" to
43635
- load the source-to-capability workflow.
43636
- Never announce that you will follow a skill. Load both in this order and follow
43637
- their content. If either load fails, retry it once, then report the failure.`;
43638
43676
  var documentLearningPlugin = {
43639
43677
  meta: {
43640
43678
  type: "document-learning",
@@ -43643,92 +43681,6 @@ var documentLearningPlugin = {
43643
43681
  description: "\u4ECE\u6587\u6863\u4E2D\u5B66\u4E60\u77E5\u8BC6\uFF0C\u81EA\u52A8\u6784\u5EFA\u6280\u80FD\u4F53\u7CFB\u548C\u8BC4\u6D4B\u3002 Learn knowledge from documents and build structured skill systems with evaluations.",
43644
43682
  recommends: ["skill", "eval"]
43645
43683
  },
43646
- agents: {
43647
- "document-learner": {
43648
- key: "document-learner",
43649
- name: "Document Learning Agent",
43650
- 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.",
43651
- type: AgentType9.DEEP_AGENT,
43652
- prompt: DOCUMENT_LEARNER_SYSTEM_PROMPT,
43653
- subAgents: ["document-parser-benchmark"],
43654
- tools: [
43655
- "invoke_agent",
43656
- "list_agents",
43657
- "create_agent",
43658
- "update_agent",
43659
- "delete_agent",
43660
- "get_agent",
43661
- "list_tools",
43662
- "list_middleware_types"
43663
- ],
43664
- middleware: [
43665
- {
43666
- id: "skill",
43667
- type: "skill",
43668
- name: "Skill",
43669
- description: "Skill creation and loading",
43670
- enabled: true,
43671
- config: { readAll: true }
43672
- },
43673
- {
43674
- id: "document-learning",
43675
- type: "document-learning",
43676
- name: "Document Learning",
43677
- description: "Source-to-capability learning workflow",
43678
- enabled: true,
43679
- config: {}
43680
- },
43681
- {
43682
- id: "eval",
43683
- type: "eval",
43684
- name: "Eval",
43685
- description: "Evaluation design and execution",
43686
- enabled: true,
43687
- config: {}
43688
- },
43689
- {
43690
- id: "task",
43691
- type: "task",
43692
- name: "Task",
43693
- description: "Training process tracking with approval gates",
43694
- enabled: true,
43695
- config: {}
43696
- },
43697
- {
43698
- id: "widget",
43699
- type: "widget",
43700
- name: "Widget",
43701
- description: "Visualize benchmark results and eval dashboards",
43702
- enabled: true,
43703
- config: {}
43704
- },
43705
- {
43706
- id: "ask_user_to_clarify",
43707
- type: "ask_user_to_clarify",
43708
- name: "Ask User",
43709
- description: "Wait for user input at approval gates",
43710
- enabled: true,
43711
- config: {}
43712
- },
43713
- {
43714
- id: "filesystem",
43715
- type: "filesystem",
43716
- name: "Filesystem",
43717
- description: "Read documents, write skill files",
43718
- enabled: true,
43719
- config: {}
43720
- },
43721
- {
43722
- id: "document-parser",
43723
- type: "document-parser",
43724
- name: "Document Parser",
43725
- description: "Parse documents with the chosen engine",
43726
- enabled: true,
43727
- config: { connectAll: true }
43728
- }
43729
- ]
43730
- }
43731
- },
43732
43684
  skills: {
43733
43685
  "document-learning-learn-capability": {
43734
43686
  version: "1.0.0",
@@ -43957,7 +43909,7 @@ function createParseDocumentTool({
43957
43909
  }
43958
43910
 
43959
43911
  // src/middlewares/documentParserMiddleware.ts
43960
- import { AgentType as AgentType10 } from "@axiom-lattice/protocols";
43912
+ import { AgentType as AgentType9 } from "@axiom-lattice/protocols";
43961
43913
 
43962
43914
  // src/middlewares/documentBenchmarkWorkflow.ts
43963
43915
  var DOCUMENT_PARSER_BENCHMARK_YAML = `name: document-parser-benchmark
@@ -44410,6 +44362,23 @@ function createDocumentParserMiddleware(config) {
44410
44362
  tools: [createParseDocumentTool({ connectAll, connections, baseUrl, apiKey })]
44411
44363
  });
44412
44364
  }
44365
+ var DOCUMENT_PARSER_SYSTEM_PROMPT = `You are the Document Parser. Turn documents (pdf, docx, images, spreadsheets, and similar) into clean Markdown.
44366
+
44367
+ CRITICAL FIRST ACTION: call the \`skill\` tool with skill_name: "document-parser-markitdown" and follow it. Never announce skill loads.
44368
+
44369
+ DEFAULT \u2014 local MarkItDown:
44370
+ - Single file: run \`markitdown <input> -o <output.md>\` via shell.
44371
+ - Batch or literature collections: use the scripts bundled in the skill.
44372
+ - After converting, verify the output is non-empty and its structure/tables are intact, then report the exact input -> output paths.
44373
+
44374
+ ESCALATION \u2014 remote parse_document:
44375
+ - Call parse_document only when local conversion fails, the format is unsupported, the output is empty or materially incomplete, the structure is badly damaged, OCR quality is inadequate, or the user explicitly asks for professional parsing.
44376
+ - When the user is unsatisfied with quality, use ask_user_to_clarify to ask whether to switch to a remote engine, and recommend one engine based on the observed file features and the user's feedback. If the user names an engine, use it.
44377
+
44378
+ BENCHMARK:
44379
+ - Only when the user explicitly asks to compare engines. Confirm with ask_user_to_clarify first, then delegate to the document-parser-benchmark subagent. Never benchmark by default.
44380
+
44381
+ Report only what you actually produced; never claim success without inspecting the output file.`;
44413
44382
  var documentParserPlugin = {
44414
44383
  meta: {
44415
44384
  type: "document-parser",
@@ -44480,9 +44449,59 @@ var documentParserPlugin = {
44480
44449
  },
44481
44450
  middleware: (config) => createDocumentParserMiddleware(config),
44482
44451
  agents: {
44452
+ "document-parser": {
44453
+ key: "document-parser",
44454
+ type: AgentType9.DEEP_AGENT,
44455
+ name: "Document Parser",
44456
+ description: "Parse documents (pdf, docx, images, spreadsheets) into clean Markdown. Defaults to local MarkItDown; escalates to remote engines only when quality or format needs it; runs engine benchmarking only on explicit request.",
44457
+ prompt: DOCUMENT_PARSER_SYSTEM_PROMPT,
44458
+ subAgents: ["document-parser-benchmark"],
44459
+ middleware: [
44460
+ {
44461
+ id: "skill",
44462
+ type: "skill",
44463
+ name: "Skill",
44464
+ description: "Load the local MarkItDown parsing procedure",
44465
+ enabled: true,
44466
+ config: { readAll: true }
44467
+ },
44468
+ {
44469
+ id: "filesystem",
44470
+ type: "filesystem",
44471
+ name: "Filesystem",
44472
+ description: "Read source documents and write Markdown output",
44473
+ enabled: true,
44474
+ config: { vmIsolation: "project" }
44475
+ },
44476
+ {
44477
+ id: "code_eval",
44478
+ type: "code_eval",
44479
+ name: "Code Runner",
44480
+ description: "Run the markitdown CLI and bundled conversion scripts",
44481
+ enabled: true,
44482
+ config: { vmIsolation: "project" }
44483
+ },
44484
+ {
44485
+ id: "document-parser",
44486
+ type: "document-parser",
44487
+ name: "Document Parser",
44488
+ description: "Remote parse_document fallback for hard documents",
44489
+ enabled: true,
44490
+ config: { connectAll: true }
44491
+ },
44492
+ {
44493
+ id: "ask_user_to_clarify",
44494
+ type: "ask_user_to_clarify",
44495
+ name: "Ask User",
44496
+ description: "Confirm engine switches and benchmark runs",
44497
+ enabled: true,
44498
+ config: {}
44499
+ }
44500
+ ]
44501
+ },
44483
44502
  "document-parser-benchmark": {
44484
44503
  key: "document-parser-benchmark",
44485
- type: AgentType10.WORKFLOW,
44504
+ type: AgentType9.WORKFLOW,
44486
44505
  name: "Document Parser Benchmark",
44487
44506
  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.",
44488
44507
  prompt: "Benchmark document parsing engines: parse with 5 engines, score, and rank results.",
@@ -44522,8 +44541,8 @@ import { createHash as createHash7 } from "crypto";
44522
44541
  import {
44523
44542
  descriptorDataValue as descriptorDataValue2,
44524
44543
  parseTrustedRunContext as parseTrustedRunContext3,
44525
- snapshotExactArray as snapshotExactArray3,
44526
- snapshotExactRecord as snapshotExactRecord8
44544
+ snapshotExactArray as snapshotExactArray2,
44545
+ snapshotExactRecord as snapshotExactRecord7
44527
44546
  } from "@axiom-lattice/protocols";
44528
44547
  import { createMiddleware as createMiddleware26, tool as tool66 } from "langchain";
44529
44548
  import { z as z72 } from "zod";
@@ -44571,7 +44590,7 @@ function readTrustedRoomScope(config) {
44571
44590
  }
44572
44591
  function exactValues2(value, required, optional = []) {
44573
44592
  try {
44574
- return snapshotExactRecord8(value, required, optional);
44593
+ return snapshotExactRecord7(value, required, optional);
44575
44594
  } catch {
44576
44595
  return void 0;
44577
44596
  }
@@ -44607,7 +44626,7 @@ function snapshotMembership3(value) {
44607
44626
  return { ...values, joinedAt, updatedAt };
44608
44627
  }
44609
44628
  function snapshotRows(value) {
44610
- return snapshotExactArray3(value) ?? [];
44629
+ return snapshotExactArray2(value) ?? [];
44611
44630
  }
44612
44631
  async function hasActiveCurrentMembership(scope) {
44613
44632
  const memberships = getStoreLattice("default", "projectBotMembership").store;
@@ -44694,7 +44713,7 @@ var projectRoomPlugin = {
44694
44713
  // src/middlewares/sandboxPluginsMiddleware.ts
44695
44714
  import { createMiddleware as createMiddleware27, tool as tool67 } from "langchain";
44696
44715
  import { z as z73 } from "zod";
44697
- import { AgentType as AgentType11 } from "@axiom-lattice/protocols";
44716
+ import { AgentType as AgentType10 } from "@axiom-lattice/protocols";
44698
44717
 
44699
44718
  // src/middlewares/sandboxPluginsSkills.ts
44700
44719
  var PLUGIN_JSON_TEMPLATE = `{
@@ -45444,7 +45463,7 @@ var sandboxPluginsPlugin = {
45444
45463
  key: "plugin-builder",
45445
45464
  name: "Plugin Builder",
45446
45465
  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.",
45447
- type: AgentType11.DEEP_AGENT,
45466
+ type: AgentType10.DEEP_AGENT,
45448
45467
  prompt: PLUGIN_BUILDER_PROMPT,
45449
45468
  middleware: [
45450
45469
  {
@@ -46356,6 +46375,7 @@ export {
46356
46375
  invokeWithRetry,
46357
46376
  isBuiltInSkill,
46358
46377
  isDirectRoom,
46378
+ isMcpConnectionError,
46359
46379
  isPluginSkillAuthorizedInSandbox,
46360
46380
  isPluginSkillManagedInSandbox,
46361
46381
  isUsingDefaultKey,
@@ -46378,7 +46398,6 @@ export {
46378
46398
  parallelLimit,
46379
46399
  parseCronExpression,
46380
46400
  parseDirectRoomType,
46381
- parseExternalDependency,
46382
46401
  parseJudgeVerdict,
46383
46402
  parsePackSize,
46384
46403
  parseQueuedExecutionMode,