@axiom-lattice/core 4.2.5 → 4.2.6

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
@@ -1982,6 +1982,7 @@ function cloneWebApp(webApp) {
1982
1982
  },
1983
1983
  features: { ...webApp.features },
1984
1984
  appearance: { ...webApp.appearance },
1985
+ identity: webApp.identity ? { policy: webApp.identity.policy, issuers: webApp.identity.issuers.map((issuer) => ({ ...issuer })) } : void 0,
1985
1986
  createdAt: new Date(webApp.createdAt),
1986
1987
  updatedAt: new Date(webApp.updatedAt)
1987
1988
  };
@@ -2027,6 +2028,7 @@ var InMemoryAgentWebAppStore = class {
2027
2028
  },
2028
2029
  features: { ...input.features },
2029
2030
  appearance: { ...input.appearance },
2031
+ identity: input.identity ? { policy: input.identity.policy, issuers: input.identity.issuers.map((issuer) => ({ ...issuer })) } : void 0,
2030
2032
  createdAt: now,
2031
2033
  updatedAt: now
2032
2034
  };
@@ -2043,7 +2045,7 @@ var InMemoryAgentWebAppStore = class {
2043
2045
  const existing = tenantWebApps?.get(webAppId);
2044
2046
  if (!tenantWebApps || !existing) return null;
2045
2047
  if (options?.expectedUpdatedAt && existing.updatedAt.getTime() !== options.expectedUpdatedAt.getTime()) return null;
2046
- const hasUpdate = patch.name !== void 0 || Object.prototype.hasOwnProperty.call(patch, "description") || patch.scope !== void 0 || patch.features !== void 0 || patch.appearance !== void 0 || patch.status !== void 0;
2048
+ const hasUpdate = patch.name !== void 0 || Object.prototype.hasOwnProperty.call(patch, "description") || patch.scope !== void 0 || patch.features !== void 0 || patch.appearance !== void 0 || patch.identity !== void 0 || patch.status !== void 0;
2047
2049
  if (!hasUpdate) return cloneWebApp(existing);
2048
2050
  const updated = {
2049
2051
  ...existing,
@@ -2058,6 +2060,7 @@ var InMemoryAgentWebAppStore = class {
2058
2060
  } : {},
2059
2061
  ...patch.features ? { features: { ...patch.features } } : {},
2060
2062
  ...patch.appearance ? { appearance: { ...patch.appearance } } : {},
2063
+ ...patch.identity !== void 0 ? { identity: { policy: patch.identity.policy, issuers: patch.identity.issuers.map((issuer) => ({ ...issuer })) } } : {},
2061
2064
  ...patch.status !== void 0 ? { status: patch.status } : {},
2062
2065
  updatedAt: new Date(Math.max(Date.now(), existing.updatedAt.getTime() + 1))
2063
2066
  };
@@ -7246,7 +7249,8 @@ var VolumeFilesystem = class {
7246
7249
  path: entry.path.startsWith("/") ? entry.path : prefix + entry.path,
7247
7250
  is_dir: entry.kind === "directory",
7248
7251
  size: entry.size,
7249
- modified_at: entry.modified ? new Date(entry.modified).toISOString() : void 0
7252
+ modified_at: entry.modified ? new Date(entry.modified).toISOString() : void 0,
7253
+ created_at: entry.created ? new Date(entry.created).toISOString() : void 0
7250
7254
  }));
7251
7255
  }
7252
7256
  async read(filePath, offset = 0, limit = 2e3) {
@@ -22564,8 +22568,12 @@ Otherwise, the owning caller interprets the returned observation.
22564
22568
  An execution result is an observation, not automatic proof of business
22565
22569
  completion. \`task\` returns its exact \`executionResultId\`. After \`task\`
22566
22570
  returns, inspect the observation, then pass that \`executionResultId\` to the
22567
- meaningful \`manage_task update\` or \`manage_task add_activity\` that interprets
22568
- it and reconciles status, belief, plan, and next action. Omitting
22571
+ meaningful \`manage_task set_status\` (status transition), \`manage_task
22572
+ update\` (content fields), or \`manage_task add_activity\` that interprets
22573
+ it and reconciles status, belief, plan, and next action. Action split:
22574
+ \`set_status\` owns status transitions (with result/failureReason/summary and
22575
+ beliefImpact on completion); \`update\` owns content fields (title,
22576
+ description, priority, dependencies, ...); the two never mix in one call. Omitting
22569
22577
  \`executionResultId\` leaves the result pending so forgotten handling remains
22570
22578
  detectable. Reconciliation does not require the task to become \`completed\`;
22571
22579
  choose the truthful status. \`manage_task get\` exposes pending execution-result
@@ -22585,13 +22593,14 @@ independently.
22585
22593
  6. **Reconcile** - revise Belief, Task, plan, and next action on Prediction Error,
22586
22594
  then repeat only while a decision-relevant action remains and stop at honest convergence.
22587
22595
 
22588
- Converge using existing statuses:
22596
+ Converge through \`set_status\` using existing statuses:
22589
22597
 
22590
- - \`completed\`: acceptance is satisfied by evidence; include a \`result\`.
22598
+ - \`completed\`: acceptance is satisfied by evidence; \`set_status\` with a
22599
+ \`result\` and structured \`beliefImpact\`.
22591
22600
  - \`in_progress\`: a feasible, decision-relevant action remains.
22592
22601
  - \`pending\`: work is future or dependency-blocked.
22593
22602
  - \`interrupted\`: an external condition is missing; record the condition needed to resume.
22594
- - \`failed\`: no reasonable path remains; include a \`failureReason\`.
22603
+ - \`failed\`: no reasonable path remains; \`set_status\` with a \`failureReason\`.
22595
22604
  - \`cancelled\`: the goal or subgoal is no longer needed.
22596
22605
 
22597
22606
  For human judgment, use the actual HITL payload \`status: "interrupted"\` plus
@@ -23881,6 +23890,35 @@ Renew HITL approval when a foundational assumption or authority boundary changes
23881
23890
 
23882
23891
  **Full reference**: [[eval-verify]] implements the same loop for the eval fix loop, with the full six-heading format (## Observed Failure / ## Implicated Design Assumption / ## Candidate Change / ## Expected Improvement / ## Possible Regression / ## Cases That Can Falsify the Change). [[task-tracking]] implements the same loop for persistent task operations.
23883
23892
 
23893
+ ## Skill Loading Policy
23894
+
23895
+ Visible in <available_skills> does not mean loaded. Follow these three tiers.
23896
+
23897
+ ### Tier 1 \u2014 Session start (mandatory, unconditional)
23898
+ Load before any other work; the bootstrap prompt enforces this:
23899
+ - [[agent-architecture]] (this skill) \u2014 shared policy and routing.
23900
+ - [[task-tracking]] \u2014 before your first \`manage_task\` call; Task management is
23901
+ the planning surface and is gated again under Architect Task Policy.
23902
+
23903
+ ### Tier 2 \u2014 Scenario gates (mandatory before entering the scenario)
23904
+ Load the owning skill with the \`skill\` tool BEFORE the first action of that
23905
+ scenario. Never execute a scenario without its owning skill loaded:
23906
+ - Creating or modifying a Capability (\`react\`) or Orchestra (\`deep_agent\`)
23907
+ Agent \u2192 [[agent-build]]
23908
+ - Creating or modifying a Workflow (stable enumerable pipelines) \u2192 [[design-workflow]]
23909
+ - Learning a capability from source material \u2192 [[document-learning-learn-capability]]
23910
+ - Designing evals, running evals, or fixing eval failures \u2192 [[eval-verify]]
23911
+ - Claiming work is complete or delivered \u2192 [[completion-gate]]
23912
+ - Final configuration pre-check before delivery \u2192 [[review-agent]]
23913
+
23914
+ If a Tier 2 load fails, retry once, then stop and tell the user which
23915
+ capability is unavailable. Do not improvise the scenario from partial knowledge.
23916
+
23917
+ ### Tier 3 \u2014 Reference (load on demand, never preload)
23918
+ Deep details reachable through [[links]] inside loaded skills, and plugin
23919
+ skills outside this chain (for example task-definition for non-architect
23920
+ agents). Load one only when the current step actually needs it.
23921
+
23884
23922
  ## Agent Forms and Routing
23885
23923
  - **Capability** (\`react\`) for bounded direct work: [[agent-build]]
23886
23924
  - **Orchestra** (\`deep_agent\`) for open-ended delegated work: [[agent-build]]
@@ -23893,8 +23931,11 @@ Renew HITL approval when a foundational assumption or authority boundary changes
23893
23931
  ## Architect Task Policy
23894
23932
  The Agent Architect uses persistent TaskItems as its planning surface. Architect must not use
23895
23933
  \`write_todos\`. Create a persistent child only for an independently verifiable
23896
- outcome; tool calls and file reads are actions inside the active Task. Load
23897
- [[task-tracking]] for operation and status details.
23934
+ outcome; tool calls and file reads are actions inside the active Task.
23935
+ **MANDATORY: before your first \`manage_task\` call in this session, load
23936
+ [[task-tracking]] with the \`skill\` tool.** Do not create, update, or transition
23937
+ any Task until it is loaded. If the load fails, retry once, then tell the user
23938
+ task management is unavailable.
23898
23939
  Learning authority is **normal** or **bounded**: normal for fresh work without a
23899
23940
  bound round; bounded for learning rounds that bind an existing target and
23900
23941
  tracking Task with a confirmed goal. Identity remains fixed; materialization uses
@@ -24123,6 +24164,9 @@ ${TASK_BELIEF_REFERENCE_EXAMPLE}
24123
24164
 
24124
24165
  ## Status discipline \u2014 MANDATORY
24125
24166
 
24167
+ - All status transitions go through \`manage_task set_status\` (with
24168
+ \`result\` on completed, \`failureReason\` on failed). \`update\` is for
24169
+ content fields only and rejects a status field.
24126
24170
  - A subtask is \`completed\` ONLY when its work is actually finished.
24127
24171
  While fixing eval failures, keep it \`in_progress\` \u2014 never mark
24128
24172
  \`completed\` as a workaround (the state machine rejects illegal
@@ -26391,8 +26435,11 @@ var AGENT_ARCHITECT_PROMPT = `You are the Agent Architect.
26391
26435
 
26392
26436
  CRITICAL FIRST ACTION: Before any response or other action, call the \`skill\`
26393
26437
  tool with skill_name: "agent-architecture" to load agent-architecture and follow it.
26394
- Never announce the skill load. If it fails, retry once, then stop and explicitly
26395
- report that the required Agent Architect skill could not be loaded.`;
26438
+ Immediately after it loads, call the \`skill\` tool again with skill_name:
26439
+ "task-tracking" \u2014 persistent Task management is your core planning surface and
26440
+ you must have its operation reference before creating or updating any Task.
26441
+ Never announce skill loads. If a load fails, retry once, then stop and
26442
+ explicitly report that the required skill could not be loaded.`;
26396
26443
 
26397
26444
  // src/agent_lattice/agentReviewerConfig.ts
26398
26445
  import { AgentType as AgentType4 } from "@axiom-lattice/protocols";
@@ -29384,7 +29431,8 @@ var RemoteSandboxProvider = class {
29384
29431
  kind: f.is_dir === true || f.size === null ? "directory" : "file",
29385
29432
  size: f.size ?? 0,
29386
29433
  mode: 0,
29387
- modified: f.modified_at ?? null
29434
+ modified: f.modified_at ?? null,
29435
+ created: f.created_at ?? null
29388
29436
  }));
29389
29437
  return entries;
29390
29438
  },
@@ -31131,8 +31179,12 @@ Note: if final_score >= 80 and there are no fatal errors, pass should be true; o
31131
31179
  });
31132
31180
  }
31133
31181
  } else if (parsedResult.pass !== void 0) {
31134
- pass = parsedResult.pass;
31135
- this.log("Pass determined from parsedResult.pass", { case_id: evalCase.caseId, pass });
31182
+ pass = false;
31183
+ parsedResult.final_score = 0;
31184
+ this.log("Pass without final_score \u2014 cannot verify threshold, defaulting to FAIL", {
31185
+ case_id: evalCase.caseId,
31186
+ pass_field: parsedResult.pass
31187
+ });
31136
31188
  } else if (parsedResult.final_score !== void 0) {
31137
31189
  pass = parsedResult.final_score >= 80;
31138
31190
  this.log("Pass determined from parsedResult.final_score", {
@@ -32880,7 +32932,11 @@ var TaskLifecycleService = class {
32880
32932
  ...event2.warning ? { warnings: [event2.warning] } : {}
32881
32933
  };
32882
32934
  }
32883
- if (task.status !== "interrupted") return failure2("TASK_STATUS_CONFLICT", "Task is not interrupted.");
32935
+ if (task.status !== "interrupted") return failure2(
32936
+ "TASK_STATUS_CONFLICT",
32937
+ `Task is in '${task.status}' status, not interrupted.`,
32938
+ "Only interrupted tasks can resume to in_progress."
32939
+ );
32884
32940
  if (interruption && typeof interruption === "object" && interruption.type === "review_required") {
32885
32941
  return failure2("REVIEW_APPROVAL_REQUIRED", "Review interruption requires explicit approval or rejection.");
32886
32942
  }
@@ -32977,7 +33033,11 @@ var TaskLifecycleService = class {
32977
33033
  task.updatedAt
32978
33034
  );
32979
33035
  } catch {
32980
- return { ...failure2("TASK_STORE_WRITE_FAILED", "Failed to update task lifecycle state."), transitioned: false };
33036
+ return { ...failure2(
33037
+ "TASK_STORE_WRITE_FAILED",
33038
+ "Failed to update task lifecycle state.",
33039
+ "The store rejected the write; this may be transient. Retry the same set_status."
33040
+ ), transitioned: false };
32981
33041
  }
32982
33042
  if (!updated) {
32983
33043
  let current;
@@ -33005,7 +33065,11 @@ var TaskLifecycleService = class {
33005
33065
  ...repaired.warning ? { warnings: [repaired.warning] } : {}
33006
33066
  };
33007
33067
  }
33008
- return { ...failure2("TASK_STATUS_CONFLICT", "Task changed concurrently."), transitioned: false };
33068
+ return { ...failure2(
33069
+ "TASK_STATUS_CONFLICT",
33070
+ "Task changed concurrently.",
33071
+ "Another writer modified the task between read and write. Use manage_task get to re-read its current state, then retry."
33072
+ ), transitioned: false };
33009
33073
  }
33010
33074
  const event = await this.writeStatusEvent(
33011
33075
  updated,
@@ -33149,10 +33213,18 @@ var TaskLifecycleService = class {
33149
33213
  const storedExternal = parseExternalDependency(currentInterruption);
33150
33214
  if (input.type === "external_dependency") {
33151
33215
  if (!external || !storedExternal || storedExternal.summary !== external.summary || canonicalJson(storedExternal.dependencyTaskIds) !== canonicalJson(external.dependencyTaskIds)) {
33152
- return failure2("TASK_STATUS_CONFLICT", "Task is interrupted by a different or malformed external dependency.");
33216
+ return failure2(
33217
+ "TASK_STATUS_CONFLICT",
33218
+ "Task is already interrupted by a different or malformed external dependency.",
33219
+ "Use manage_task get to inspect the existing interruption in task context.interruption."
33220
+ );
33153
33221
  }
33154
33222
  } else if (currentInterruption.type !== input.type || currentInterruption.summary !== summary) {
33155
- return failure2("TASK_STATUS_CONFLICT", "Task is interrupted by a different request.");
33223
+ return failure2(
33224
+ "TASK_STATUS_CONFLICT",
33225
+ "Task is already interrupted by a different request.",
33226
+ "Use manage_task get to inspect the existing interruption, or resume it first."
33227
+ );
33156
33228
  }
33157
33229
  const detail = storedExternal ? { ...storedExternal } : currentInterruption;
33158
33230
  const eventKey2 = this.statusEventKey(task, "interrupted", detail);
@@ -33169,7 +33241,11 @@ var TaskLifecycleService = class {
33169
33241
  ...warnings2.length ? { warnings: warnings2 } : {}
33170
33242
  };
33171
33243
  }
33172
- if (task.status !== "in_progress") return failure2("TASK_STATUS_CONFLICT", "Task is not in progress.");
33244
+ if (task.status !== "in_progress") return failure2(
33245
+ "TASK_STATUS_CONFLICT",
33246
+ `Task is in '${task.status}' status, not in progress.`,
33247
+ "Only in_progress tasks can be interrupted. Use manage_task get to check the current status."
33248
+ );
33173
33249
  if (external && isProjectLifecycleTask(task)) {
33174
33250
  const dependencyFailure = await this.validateExternalDependencies(task, external.dependencyTaskIds);
33175
33251
  if (dependencyFailure) return dependencyFailure;
@@ -33182,9 +33258,17 @@ var TaskLifecycleService = class {
33182
33258
  context: mergeInterruptionContext(task.context, interruption, input.threadId)
33183
33259
  }, ["in_progress"], task.updatedAt);
33184
33260
  } catch {
33185
- return failure2("TASK_STORE_WRITE_FAILED", "Failed to interrupt the task.");
33261
+ return failure2(
33262
+ "TASK_STORE_WRITE_FAILED",
33263
+ "Failed to interrupt the task.",
33264
+ "The store rejected the write; this may be transient. Retry the same set_status."
33265
+ );
33186
33266
  }
33187
- if (!updated) return failure2("TASK_STATUS_CONFLICT", "Task changed concurrently.");
33267
+ if (!updated) return failure2(
33268
+ "TASK_STATUS_CONFLICT",
33269
+ "Task changed concurrently.",
33270
+ "Another writer modified the task between read and write. Use manage_task get to re-read its current state, then retry."
33271
+ );
33188
33272
  const eventKey = this.statusEventKey(updated, "interrupted", interruption);
33189
33273
  const event = await this.writeStatusEvent(updated, input.actor, input.threadId, "interrupted", summary, interruption, eventKey);
33190
33274
  const warnings = event.warning ? [event.warning] : await this.notifyWinningTransition(updated, "interrupted", event);
@@ -33222,7 +33306,11 @@ var TaskLifecycleService = class {
33222
33306
  context: withCompletionAudit(prepared.task.context, prepared.evidence.id, prepared.eventKey)
33223
33307
  }, ["in_progress"]);
33224
33308
  } catch {
33225
- return failure2("TASK_STORE_WRITE_FAILED", "Failed to complete the task.");
33309
+ return failure2(
33310
+ "TASK_STORE_WRITE_FAILED",
33311
+ "Failed to complete the task.",
33312
+ "The store rejected the write; this may be transient. Retry the completion."
33313
+ );
33226
33314
  }
33227
33315
  if (!updated) {
33228
33316
  if (await this.hasStaleAgentParent(prepared)) {
@@ -33785,7 +33873,11 @@ var TaskLifecycleService = class {
33785
33873
  } catch {
33786
33874
  return failure2("TASK_STORE_WRITE_FAILED", "Failed to reconcile task description.");
33787
33875
  }
33788
- if (!updated) return failure2("TASK_STATUS_CONFLICT", "Task changed concurrently.");
33876
+ if (!updated) return failure2(
33877
+ "TASK_STATUS_CONFLICT",
33878
+ "Task changed concurrently.",
33879
+ "Another writer modified the task between read and write. Use manage_task get to re-read it, then retry the description update."
33880
+ );
33789
33881
  if (changedKeys.length === 0) {
33790
33882
  return {
33791
33883
  success: true,
@@ -38773,6 +38865,7 @@ import { z as z70 } from "zod";
38773
38865
  import { GraphInterrupt as GraphInterrupt2, interrupt as interrupt4 } from "@langchain/langgraph";
38774
38866
  import {
38775
38867
  isExecutionResultEventKey as isExecutionResultEventKey3,
38868
+ parseTaskBeliefState as parseTaskBeliefState3,
38776
38869
  parseTrustedRunContext as parseTrustedRunContext2,
38777
38870
  requireProjectTaskWorkItemStore
38778
38871
  } from "@axiom-lattice/protocols";
@@ -39083,6 +39176,16 @@ function getRunConfig2(config) {
39083
39176
  const c = config;
39084
39177
  return c?.configurable?.runConfig ?? {};
39085
39178
  }
39179
+ function taskUpdateDebug(message, data) {
39180
+ try {
39181
+ console.log(JSON.stringify({ __taskUpdateDebug: true, message, ...data }));
39182
+ } catch {
39183
+ console.log(`[taskUpdateDebug] ${message}`);
39184
+ }
39185
+ }
39186
+ function taskUpdateBlocked(taskId, code, detail) {
39187
+ taskUpdateDebug("update:blocked", { taskId, code, ...detail });
39188
+ }
39086
39189
  function getRuntimeActor2(runConfig) {
39087
39190
  if (runConfig.assistant_id) return `agent:${String(runConfig.assistant_id)}`;
39088
39191
  if (runConfig.user_id) return `user:${String(runConfig.user_id)}`;
@@ -39309,6 +39412,7 @@ async function validateEffectiveDependencies(store, tenantId2, targetScope, depe
39309
39412
  if (blockedBy.length === 0) return void 0;
39310
39413
  return JSON.stringify({
39311
39414
  success: false,
39415
+ code: "DEPENDENCY_BLOCKED",
39312
39416
  error: `Cannot start task: ${blockedBy.length} dependencies not completed`,
39313
39417
  blockedBy,
39314
39418
  invalidScope,
@@ -39459,13 +39563,27 @@ function missingBeliefImpactError() {
39459
39563
  hint: "Provide beliefImpact: [{ key: '<belief-key>', after: <0-100>, basis: '<evidence>' }]"
39460
39564
  });
39461
39565
  }
39566
+ function reviewInterruption(task) {
39567
+ const value = task.context?.interruption;
39568
+ if (!value || typeof value !== "object") return void 0;
39569
+ const interruption = value;
39570
+ return interruption.type === "review_required" ? interruption : void 0;
39571
+ }
39572
+ function buildReviewMarkdown(task) {
39573
+ return genUIMarkdown("task_review", {
39574
+ taskId: task.id,
39575
+ title: task.title,
39576
+ description: task.description,
39577
+ result: task.result
39578
+ });
39579
+ }
39462
39580
  var manageTaskSchema = z70.object({
39463
- action: z70.enum(["create", "get", "list", "update", "add_activity", "delete"]).describe("Action to perform. Available: create, get, list, update, add_activity, delete. To mark a task complete, use update with status='completed'"),
39581
+ action: z70.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."),
39464
39582
  id: z70.string().optional().describe("Task ID (required for get, update, add_activity, and delete)"),
39465
39583
  title: z70.string().optional().describe("Task title (required for create)"),
39466
39584
  description: z70.string().optional().describe("Task description in Markdown"),
39467
39585
  priority: z70.enum(["low", "medium", "high"]).optional().describe("Priority level"),
39468
- status: z70.enum(["pending", "in_progress", "review", "failed", "interrupted", "completed", "cancelled"]).optional().describe("Task status. Agent summary-only interrupted updates default to missing_input"),
39586
+ status: z70.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"),
39469
39587
  dueDate: z70.string().optional().describe("Due date (ISO 8601 format)"),
39470
39588
  metadata: z70.record(z70.unknown()).optional().describe("Structured metadata (e.g. projectId, module)"),
39471
39589
  parentId: z70.string().optional().describe("Parent task ID for grouping subtasks"),
@@ -39476,8 +39594,8 @@ var manageTaskSchema = z70.object({
39476
39594
  requireReview: z70.boolean().optional().describe("If true, agent completion pauses as interrupted(review_required) for HITL approval"),
39477
39595
  dependencies: z70.array(z70.string()).optional().describe("List of task IDs that must be completed before this task can start"),
39478
39596
  dependencyTaskIds: z70.array(z70.string().trim().min(1)).optional().describe("Exact blocking task IDs required for an external_dependency interruption"),
39479
- result: z70.string().optional().describe("Nonblank result summary required when creating or updating a task with status='completed'"),
39480
- failureReason: z70.string().optional().describe("Nonblank failure reason required when creating or updating a task with status='failed'"),
39597
+ result: z70.string().optional().describe("Result summary. Used with set_status status='completed' or update status='completed'"),
39598
+ failureReason: z70.string().optional().describe("Failure reason. Used with set_status status='failed' or update status='failed'"),
39481
39599
  files: z70.array(z70.object({
39482
39600
  uri: z70.string().describe("Uniquely locates the resource: http(s):// URL, /s/:token share, or sandbox path"),
39483
39601
  name: z70.string().optional().describe("Display name"),
@@ -39496,11 +39614,11 @@ var manageTaskSchema = z70.object({
39496
39614
  "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"
39497
39615
  )
39498
39616
  }).superRefine((input, context) => {
39499
- if (input.executionResultId && input.action !== "update" && input.action !== "add_activity") {
39617
+ if (input.executionResultId && input.action !== "update" && input.action !== "set_status" && input.action !== "add_activity") {
39500
39618
  context.addIssue({
39501
39619
  code: z70.ZodIssueCode.custom,
39502
39620
  path: ["executionResultId"],
39503
- message: "executionResultId is valid only for update or add_activity"
39621
+ message: "executionResultId is valid only for update, set_status, or add_activity"
39504
39622
  });
39505
39623
  }
39506
39624
  });
@@ -39614,12 +39732,6 @@ function updateValuesEqual(field, requested, persisted) {
39614
39732
  }
39615
39733
  return valuesEqual(requested, persisted);
39616
39734
  }
39617
- function reviewInterruption(task) {
39618
- const value = task.context?.interruption;
39619
- if (!value || typeof value !== "object") return void 0;
39620
- const interruption = value;
39621
- return interruption.type === "review_required" ? interruption : void 0;
39622
- }
39623
39735
  function reviewModeDisabledResponse() {
39624
39736
  return JSON.stringify({
39625
39737
  success: false,
@@ -39628,14 +39740,6 @@ function reviewModeDisabledResponse() {
39628
39740
  hint: "Configure task middleware with reviewMode 'hitl' before enabling requireReview."
39629
39741
  });
39630
39742
  }
39631
- function buildReviewMarkdown(task) {
39632
- return genUIMarkdown("task_review", {
39633
- taskId: task.id,
39634
- title: task.title,
39635
- description: task.description,
39636
- result: task.result
39637
- });
39638
- }
39639
39743
  function createTaskMiddleware(options = {}) {
39640
39744
  const handleManageTaskAction = async (input, config, validateExecutionResult, markMutation) => {
39641
39745
  const rc = getRunConfig2(config);
@@ -40023,35 +40127,75 @@ function createTaskMiddleware(options = {}) {
40023
40127
  return JSON.stringify({ success: true, data: activity });
40024
40128
  }
40025
40129
  case "update": {
40130
+ taskUpdateDebug("update:enter", {
40131
+ id: input.id,
40132
+ action: input.action,
40133
+ status: input.status,
40134
+ hasResult: input.result !== void 0,
40135
+ hasDescription: input.description !== void 0,
40136
+ tenantId: tenantId2,
40137
+ workspaceId,
40138
+ projectId,
40139
+ authorityKind: delegatedTaskId ? "delegated" : trustedProject ? "trusted" : "ordinary",
40140
+ trustedKind: trustedProject ? trustedProject.projectTask ? "projectTask" : "projectRoom" : void 0
40141
+ });
40026
40142
  if (!input.id) {
40143
+ taskUpdateBlocked(input.id, "MISSING_TASK_ID", {});
40027
40144
  return JSON.stringify({
40028
40145
  success: false,
40146
+ code: "MISSING_TASK_ID",
40029
40147
  error: "id is required for update action",
40030
40148
  hint: "Pass the task ID you want to update"
40031
40149
  });
40032
40150
  }
40033
40151
  if (delegatedTaskId && input.id !== delegatedTaskId) {
40152
+ taskUpdateBlocked(input.id, "DELEGATED_TASK_SCOPE_VIOLATION", { delegatedTaskId });
40034
40153
  return delegatedTaskScopeError(delegatedTaskId);
40035
40154
  }
40036
- let existing = await store.getById(tenantId2, input.id);
40155
+ const existing = await store.getById(tenantId2, input.id);
40156
+ taskUpdateDebug("update:loaded", existing ? {
40157
+ status: existing.status,
40158
+ updatedAt: existing.updatedAt?.toISOString(),
40159
+ ownerType: existing.ownerType,
40160
+ ownerId: existing.ownerId,
40161
+ workspaceId: existing.workspaceId,
40162
+ projectId: existing.projectId
40163
+ } : { found: false });
40037
40164
  if (!existing) {
40165
+ taskUpdateBlocked(input.id, "TASK_NOT_FOUND", {});
40038
40166
  return JSON.stringify({
40039
40167
  success: false,
40040
- error: `Task '${input.id}' not found`,
40041
- hint: "Use list to see available tasks and their IDs"
40168
+ code: "TASK_NOT_FOUND",
40169
+ error: `Task '${input.id}' was not found.`,
40170
+ hint: "Use manage_task list to see available tasks and their correct IDs."
40042
40171
  });
40043
40172
  }
40044
40173
  if (!taskMatchesRuntimeScope3(existing, workspaceId, projectId)) {
40174
+ taskUpdateBlocked(input.id, "TASK_NOT_FOUND_OR_INACCESSIBLE", {
40175
+ taskWorkspaceId: existing.workspaceId,
40176
+ taskProjectId: existing.projectId,
40177
+ runtimeWorkspaceId: workspaceId,
40178
+ runtimeProjectId: projectId
40179
+ });
40045
40180
  return taskScopeAccessError(input.id);
40046
40181
  }
40047
40182
  if (trustedProject?.projectTask) {
40048
40183
  const trustedFailure = trustedTaskContextFailure(existing, trustedProject.projectTask);
40049
- if (trustedFailure) return trustedFailure;
40184
+ if (trustedFailure) {
40185
+ taskUpdateBlocked(input.id, "PROJECT_TASK_CONTEXT_INVALID", {
40186
+ taskOwnerId: existing.ownerId,
40187
+ trustedAssistantId: trustedProject.projectTask.assistantId,
40188
+ taskThreadId: existing.context?.thread_id,
40189
+ trustedThreadId: trustedProject.projectTask.threadId
40190
+ });
40191
+ return trustedFailure;
40192
+ }
40050
40193
  }
40051
40194
  if (trustedProject && input.dependencyTaskIds !== void 0) {
40052
40195
  const interruption = input.context?.interruption;
40053
40196
  const interruptionType2 = interruption && typeof interruption === "object" ? interruption.type : void 0;
40054
40197
  if (input.status !== "interrupted" || interruptionType2 !== "external_dependency") {
40198
+ taskUpdateBlocked(input.id, "INVALID_EXTERNAL_DEPENDENCY", {});
40055
40199
  return JSON.stringify({
40056
40200
  success: false,
40057
40201
  code: "INVALID_EXTERNAL_DEPENDENCY",
@@ -40060,24 +40204,26 @@ function createTaskMiddleware(options = {}) {
40060
40204
  }
40061
40205
  }
40062
40206
  if (trustedProject && (input.ownerType !== void 0 && input.ownerType !== existing.ownerType || input.ownerId !== void 0 && input.ownerId !== existing.ownerId)) {
40207
+ taskUpdateBlocked(input.id, "TASK_REASSIGN_ENDPOINT_REQUIRED", {
40208
+ inputOwnerType: input.ownerType,
40209
+ existingOwnerType: existing.ownerType,
40210
+ inputOwnerId: input.ownerId,
40211
+ existingOwnerId: existing.ownerId
40212
+ });
40063
40213
  return JSON.stringify({
40064
40214
  success: false,
40065
40215
  code: "TASK_REASSIGN_ENDPOINT_REQUIRED",
40066
40216
  error: "Project task ownership changes require the reassignment endpoint."
40067
40217
  });
40068
40218
  }
40069
- if (existing.status === "review" || input.status === "review") {
40070
- return JSON.stringify({
40071
- success: false,
40072
- code: "TASK_STATUS_UNSUPPORTED",
40073
- error: "Task status is not supported by the task lifecycle."
40074
- });
40075
- }
40076
40219
  const isAgentTask = existing.ownerType === "agent";
40077
- const candidateReviewInterruption = input.status === "interrupted" && input.context?.interruption && typeof input.context.interruption === "object" && input.context.interruption.type === "review_required" && !!input.result?.trim() && !!input.beliefImpact?.length;
40078
40220
  const changesOwnerType = input.ownerType !== void 0 && input.ownerType !== existing.ownerType;
40079
40221
  const changesAgentIdentity = isAgentTask && (input.ownerId !== void 0 && input.ownerId !== existing.ownerId || input.parentId !== void 0 && input.parentId !== existing.parentId);
40080
40222
  if (changesOwnerType || changesAgentIdentity) {
40223
+ taskUpdateBlocked(input.id, "TASK_OWNERSHIP_IMMUTABLE", {
40224
+ changesOwnerType,
40225
+ changesAgentIdentity
40226
+ });
40081
40227
  return JSON.stringify({
40082
40228
  success: false,
40083
40229
  code: "TASK_OWNERSHIP_IMMUTABLE",
@@ -40085,18 +40231,10 @@ function createTaskMiddleware(options = {}) {
40085
40231
  hint: "Create a separate task for different ownership or parentage."
40086
40232
  });
40087
40233
  }
40088
- const hasMismatchedAgentOutput = isAgentTask && (input.result !== void 0 && input.status !== "completed" && !candidateReviewInterruption || input.failureReason !== void 0 && input.status !== "failed");
40089
- if (hasMismatchedAgentOutput) {
40090
- return JSON.stringify({
40091
- success: false,
40092
- code: "TASK_LIFECYCLE_TRANSITION_REQUIRED",
40093
- error: "Agent task result and failureReason require a matching governed lifecycle transition.",
40094
- hint: "Provide result only with completion, or failureReason only with failure."
40095
- });
40096
- }
40097
40234
  const changesOwnership = input.ownerId !== void 0 && input.ownerId !== existing.ownerId || input.parentId !== void 0 && input.parentId !== existing.parentId;
40098
40235
  const changesGovernedState = input.status !== void 0 || input.description !== void 0 || input.result !== void 0 || input.failureReason !== void 0 || input.requireReview !== void 0 || input.context !== void 0;
40099
40236
  if (changesOwnership && changesGovernedState) {
40237
+ taskUpdateBlocked(input.id, "TASK_OWNERSHIP_MUTATION_CONFLICT", {});
40100
40238
  return JSON.stringify({
40101
40239
  success: false,
40102
40240
  code: "TASK_OWNERSHIP_MUTATION_CONFLICT",
@@ -40104,17 +40242,389 @@ function createTaskMiddleware(options = {}) {
40104
40242
  hint: "Persist ownership or parent changes separately before changing lifecycle or task content."
40105
40243
  });
40106
40244
  }
40245
+ if (existing.status === "review") {
40246
+ taskUpdateBlocked(input.id, "TASK_STATUS_UNSUPPORTED", {});
40247
+ return JSON.stringify({
40248
+ success: false,
40249
+ code: "TASK_STATUS_UNSUPPORTED",
40250
+ error: "Task status is not supported by the task lifecycle."
40251
+ });
40252
+ }
40253
+ if (input.status !== void 0) {
40254
+ taskUpdateBlocked(input.id, "STATUS_USES_SET_STATUS", { status: input.status });
40255
+ return JSON.stringify({
40256
+ success: false,
40257
+ code: "STATUS_USES_SET_STATUS",
40258
+ error: `Status changes must use the set_status action. Received status='${input.status}'.`,
40259
+ hint: "Call manage_task with action='set_status', id, and status to change the task status. Use action='update' for content fields only."
40260
+ });
40261
+ }
40262
+ if (isAgentTask && input.requireReview === true && options.reviewMode !== "hitl") {
40263
+ return reviewModeDisabledResponse();
40264
+ }
40265
+ if (["completed", "failed", "cancelled"].includes(existing.status) && input.requireReview !== void 0 && input.requireReview !== existing.requireReview) {
40266
+ taskUpdateBlocked(input.id, "TERMINAL_REVIEW_CONFIGURATION_LOCKED", { requireReview: input.requireReview });
40267
+ return JSON.stringify({
40268
+ success: false,
40269
+ code: "TERMINAL_REVIEW_CONFIGURATION_LOCKED",
40270
+ error: "requireReview cannot be changed on a terminal task",
40271
+ hint: "Configure requireReview before the task reaches a terminal status"
40272
+ });
40273
+ }
40274
+ if (existing.status !== "pending" && existing.requireReview === true && input.requireReview === false) {
40275
+ taskUpdateBlocked(input.id, "REVIEW_REQUIREMENT_LOCKED", {});
40276
+ return JSON.stringify({
40277
+ success: false,
40278
+ code: "REVIEW_REQUIREMENT_LOCKED",
40279
+ error: "requireReview cannot be disabled after a task leaves pending",
40280
+ hint: "Configure requireReview while the task is pending"
40281
+ });
40282
+ }
40283
+ if (existing.status === "completed" && input.result !== void 0) {
40284
+ const error = missingTerminalDetail("result", input.result ?? existing.result);
40285
+ if (error) return error;
40286
+ }
40287
+ if (existing.status === "failed" && input.failureReason !== void 0) {
40288
+ const error = missingTerminalDetail(
40289
+ "failureReason",
40290
+ input.failureReason ?? existing.failureReason
40291
+ );
40292
+ if (error) return error;
40293
+ }
40107
40294
  const actor = trustedActor(trustedProject, rc) ?? (existing.ownerType === "agent" ? `agent:${existing.ownerId}` : `user:${existing.ownerId ?? ownerId}`);
40108
- const threadId = trustedProject?.projectTask?.threadId ?? (trustedProject?.projectRoom ? void 0 : input.sourceId ?? rc.thread_id);
40295
+ const workItemStore = getTaskWorkItemStore();
40109
40296
  const lifecycle = isAgentTask ? createTaskLifecycleService({
40110
40297
  taskStore: store,
40111
40298
  workItemStore: trustedProject ? getProjectTaskWorkItemStore() : getTaskWorkItemStore()
40112
40299
  }) : void 0;
40300
+ let trustedReferenceSnapshots;
40301
+ if (input.parentId !== void 0 || input.dependencies !== void 0) {
40302
+ if (delegatedTaskId) {
40303
+ const delegatedDependencyError = await validateDelegatedDependencies(
40304
+ store,
40305
+ tenantId2,
40306
+ delegatedTaskId,
40307
+ { workspaceId: existing.workspaceId, projectId: existing.projectId },
40308
+ input.parentId,
40309
+ input.dependencies,
40310
+ input.id
40311
+ );
40312
+ if (delegatedDependencyError) {
40313
+ taskUpdateBlocked(input.id, "DELEGATED_TASK_SCOPE_VIOLATION", { delegatedDependencyError });
40314
+ return delegatedDependencyError;
40315
+ }
40316
+ }
40317
+ const referenceError = await validateReferencedTasks(
40318
+ store,
40319
+ tenantId2,
40320
+ { workspaceId: existing.workspaceId, projectId: existing.projectId },
40321
+ input.parentId,
40322
+ input.dependencies
40323
+ );
40324
+ if (referenceError) {
40325
+ taskUpdateBlocked(input.id, "INVALID_TASK_REFERENCES", { referenceError });
40326
+ return referenceError;
40327
+ }
40328
+ if (trustedProject) {
40329
+ trustedReferenceSnapshots = await captureReferencedTaskSnapshots(
40330
+ store,
40331
+ tenantId2,
40332
+ { workspaceId: existing.workspaceId, projectId: existing.projectId },
40333
+ input.parentId,
40334
+ input.dependencies
40335
+ );
40336
+ if (!trustedReferenceSnapshots) {
40337
+ taskUpdateBlocked(input.id, "PROJECT_TASK_CONTEXT_INVALID", { reason: "reference snapshots unavailable" });
40338
+ return trustedContextError();
40339
+ }
40340
+ }
40341
+ }
40342
+ const effectiveDependencies = input.dependencies ?? existing.dependencies ?? [];
40343
+ const effectiveStatus = input.status ?? existing.status;
40344
+ if (effectiveStatus === "in_progress" && effectiveDependencies.length > 0) {
40345
+ const dependencyError = await validateEffectiveDependencies(
40346
+ store,
40347
+ tenantId2,
40348
+ { workspaceId: existing.workspaceId, projectId: existing.projectId },
40349
+ effectiveDependencies
40350
+ );
40351
+ if (dependencyError) {
40352
+ const trustedException = trustedProject?.projectTask && input.dependencies !== void 0 ? await validateTrustedChildDependencyWait(
40353
+ store,
40354
+ tenantId2,
40355
+ existing,
40356
+ effectiveDependencies,
40357
+ trustedProject
40358
+ ) : void 0;
40359
+ if (trustedException === void 0) {
40360
+ taskUpdateBlocked(input.id, "DEPENDENCY_BLOCKED", { dependencyError });
40361
+ return dependencyError;
40362
+ }
40363
+ if (trustedException) return trustedException;
40364
+ }
40365
+ }
40366
+ let reconciledTask;
40367
+ let reconciliationMetadata = {};
40368
+ let descriptionMutated = false;
40369
+ if (isAgentTask && input.description !== void 0 && !valuesEqual(input.description, existing.description)) {
40370
+ const oldBelief = parseTaskBeliefState3(existing.description ?? "");
40371
+ const newBelief = parseTaskBeliefState3(input.description);
40372
+ const hasBeliefState = oldBelief.success || newBelief.success;
40373
+ if (hasBeliefState) {
40374
+ await validateExecutionResult();
40375
+ const reconciliation = await lifecycle.updateTaskDescriptionWithReconciliation({
40376
+ tenantId: tenantId2,
40377
+ taskId: input.id,
40378
+ description: input.description,
40379
+ actor,
40380
+ threadId: trustedProject?.projectTask?.threadId ?? (trustedProject?.projectRoom ? void 0 : input.sourceId ?? rc.thread_id)
40381
+ });
40382
+ if (!reconciliation.success) return JSON.stringify(reconciliation);
40383
+ reconciledTask = reconciliation.task;
40384
+ reconciliationMetadata = { ...reconciliation };
40385
+ delete reconciliationMetadata.task;
40386
+ descriptionMutated = reconciliation.mutated;
40387
+ if (descriptionMutated) markMutation();
40388
+ }
40389
+ }
40390
+ const updates = {};
40391
+ const settableFields = [
40392
+ "title",
40393
+ "description",
40394
+ "priority",
40395
+ "dueDate",
40396
+ "metadata",
40397
+ "parentId",
40398
+ "sourceId",
40399
+ "context",
40400
+ "ownerId",
40401
+ "result",
40402
+ "failureReason",
40403
+ "requireReview",
40404
+ "dependencies",
40405
+ "files"
40406
+ ];
40407
+ for (const field of settableFields) {
40408
+ if (trustedProject && (field === "sourceId" || field === "context" || field === "ownerId")) continue;
40409
+ if (field === "description" && reconciledTask) continue;
40410
+ const requested = input[field];
40411
+ const persisted = reconciledTask ?? existing;
40412
+ if (requested !== void 0 && !updateValuesEqual(field, requested, persisted[field])) {
40413
+ updates[field] = requested;
40414
+ }
40415
+ }
40416
+ if (!reconciledTask && Object.keys(updates).length > 0) {
40417
+ await validateExecutionResult();
40418
+ }
40419
+ if (trustedProject && (input.parentId !== void 0 || input.dependencies !== void 0)) {
40420
+ if (!await referencedTaskSnapshotsStillMatch(
40421
+ store,
40422
+ tenantId2,
40423
+ { workspaceId: existing.workspaceId, projectId: existing.projectId },
40424
+ trustedReferenceSnapshots
40425
+ )) {
40426
+ taskUpdateBlocked(input.id, "PROJECT_TASK_CONTEXT_INVALID", { reason: "referenced task changed" });
40427
+ return trustedContextError("Parent or dependency task changed before update.");
40428
+ }
40429
+ }
40430
+ if (trustedProject) {
40431
+ try {
40432
+ await createProjectTaskOwnerPolicy().requireCaller(trustedRosterScope(trustedProject));
40433
+ } catch (error) {
40434
+ taskUpdateBlocked(input.id, "PROJECT_POLICY_ERROR", { policyError: String(error) });
40435
+ return projectPolicyError(error);
40436
+ }
40437
+ }
40438
+ const snapshot = trustedProject ? taskMutationSnapshot(existing) : void 0;
40439
+ taskUpdateDebug("update:write", {
40440
+ updateKeys: Object.keys(updates),
40441
+ useCas: !!snapshot,
40442
+ storeMethod: snapshot ? "updateIfSnapshot" : "update"
40443
+ });
40444
+ const updated = Object.keys(updates).length > 0 ? snapshot ? await store.updateIfSnapshot(tenantId2, input.id, updates, snapshot) : await store.update(tenantId2, input.id, updates) : reconciledTask ?? existing;
40445
+ taskUpdateDebug("update:result", {
40446
+ updated: !!updated,
40447
+ resultStatus: updated?.status,
40448
+ resultUpdatedAt: updated?.updatedAt?.toISOString?.()
40449
+ });
40450
+ if (!updated) {
40451
+ taskUpdateBlocked(input.id, "TASK_UPDATE_FAILED", { storeMethod: snapshot ? "updateIfSnapshot" : "update" });
40452
+ return JSON.stringify({
40453
+ success: false,
40454
+ code: "TASK_UPDATE_FAILED",
40455
+ error: `Failed to update task '${input.id}'.`,
40456
+ hint: "The task may have been deleted, the ID may be incorrect, or the store rejected the write. Use manage_task get to verify the task still exists, then retry the update."
40457
+ });
40458
+ }
40459
+ if (Object.keys(updates).length > 0) markMutation();
40460
+ if (reconciledTask && Object.keys(updates).length === 0) {
40461
+ return successfulTaskResponse(input.id, updated, descriptionMutated, reconciliationMetadata);
40462
+ }
40463
+ if (Object.keys(updates).length === 0) {
40464
+ return successfulTaskResponse(input.id, updated, false);
40465
+ }
40466
+ const workItemAction = "updated";
40467
+ const workItemSummary = input.summary;
40468
+ let activityWarnings;
40469
+ try {
40470
+ if (trustedProject) {
40471
+ try {
40472
+ await createProjectTaskOwnerPolicy().requireCaller(trustedRosterScope(trustedProject));
40473
+ } catch (error) {
40474
+ return projectPolicyError(error);
40475
+ }
40476
+ }
40477
+ await workItemStore.create({
40478
+ taskId: input.id,
40479
+ tenantId: tenantId2,
40480
+ action: workItemAction,
40481
+ actor,
40482
+ threadId: trustedProject?.projectTask?.threadId ?? (trustedProject?.projectRoom ? void 0 : input.sourceId),
40483
+ summary: workItemSummary,
40484
+ detail: {
40485
+ ...input.result !== void 0 && { result: input.result },
40486
+ ...input.failureReason !== void 0 && { failureReason: input.failureReason }
40487
+ },
40488
+ workspaceId: updated.workspaceId,
40489
+ projectId: updated.projectId
40490
+ });
40491
+ } catch (error) {
40492
+ activityWarnings = [{
40493
+ code: "TASK_UPDATE_ACTIVITY_WRITE_FAILED",
40494
+ taskId: input.id,
40495
+ error: error instanceof Error ? error.message : String(error)
40496
+ }];
40497
+ }
40498
+ const lifecycleWarnings = Array.isArray(reconciliationMetadata.warnings) ? reconciliationMetadata.warnings : void 0;
40499
+ return successfulTaskResponse(input.id, updated, true, {
40500
+ ...reconciliationMetadata,
40501
+ warnings: mergeTaskUpdateWarnings(lifecycleWarnings, activityWarnings)
40502
+ });
40503
+ }
40504
+ case "set_status": {
40505
+ if (!input.id) {
40506
+ taskUpdateBlocked(input.id, "MISSING_TASK_ID", {});
40507
+ return JSON.stringify({
40508
+ success: false,
40509
+ code: "MISSING_TASK_ID",
40510
+ error: "id is required for set_status action",
40511
+ hint: "Pass the task ID you want to update"
40512
+ });
40513
+ }
40514
+ if (!input.status) {
40515
+ return JSON.stringify({
40516
+ success: false,
40517
+ code: "MISSING_STATUS",
40518
+ error: "status is required for set_status action",
40519
+ hint: "Pass the target status: pending, in_progress, completed, failed, interrupted, cancelled"
40520
+ });
40521
+ }
40522
+ if (delegatedTaskId && input.id !== delegatedTaskId) {
40523
+ return delegatedTaskScopeError(delegatedTaskId);
40524
+ }
40525
+ const existing = await store.getById(tenantId2, input.id);
40526
+ taskUpdateDebug("set_status:loaded", existing ? {
40527
+ status: existing.status,
40528
+ updatedAt: existing.updatedAt?.toISOString()
40529
+ } : { found: false });
40530
+ if (!existing) {
40531
+ taskUpdateBlocked(input.id, "TASK_NOT_FOUND", {});
40532
+ return JSON.stringify({
40533
+ success: false,
40534
+ code: "TASK_NOT_FOUND",
40535
+ error: `Task '${input.id}' was not found.`,
40536
+ hint: "Use manage_task list to see available tasks and their correct IDs."
40537
+ });
40538
+ }
40539
+ if (!taskMatchesRuntimeScope3(existing, workspaceId, projectId)) {
40540
+ taskUpdateBlocked(input.id, "TASK_NOT_FOUND_OR_INACCESSIBLE", {});
40541
+ return taskScopeAccessError(input.id);
40542
+ }
40543
+ if (trustedProject?.projectTask) {
40544
+ const trustedFailure = trustedTaskContextFailure(existing, trustedProject.projectTask);
40545
+ if (trustedFailure) {
40546
+ taskUpdateBlocked(input.id, "PROJECT_TASK_CONTEXT_INVALID", {
40547
+ taskOwnerId: existing.ownerId,
40548
+ trustedAssistantId: trustedProject.projectTask.assistantId,
40549
+ taskThreadId: existing.context?.thread_id,
40550
+ trustedThreadId: trustedProject.projectTask.threadId
40551
+ });
40552
+ return trustedFailure;
40553
+ }
40554
+ }
40555
+ if (trustedProject && input.dependencyTaskIds !== void 0) {
40556
+ const interruption = input.context?.interruption;
40557
+ const interruptionType2 = interruption && typeof interruption === "object" ? interruption.type : void 0;
40558
+ if (input.status !== "interrupted" || interruptionType2 !== "external_dependency") {
40559
+ taskUpdateBlocked(input.id, "INVALID_EXTERNAL_DEPENDENCY", {});
40560
+ return JSON.stringify({
40561
+ success: false,
40562
+ code: "INVALID_EXTERNAL_DEPENDENCY",
40563
+ error: "dependencyTaskIds are only valid for external dependency interruptions."
40564
+ });
40565
+ }
40566
+ }
40567
+ if (trustedProject && (input.ownerType !== void 0 && input.ownerType !== existing.ownerType || input.ownerId !== void 0 && input.ownerId !== existing.ownerId)) {
40568
+ taskUpdateBlocked(input.id, "TASK_REASSIGN_ENDPOINT_REQUIRED", {});
40569
+ return JSON.stringify({
40570
+ success: false,
40571
+ code: "TASK_REASSIGN_ENDPOINT_REQUIRED",
40572
+ error: "Project task ownership changes require the reassignment endpoint."
40573
+ });
40574
+ }
40575
+ if (existing.status === "review" || input.status === "review") {
40576
+ taskUpdateBlocked(input.id, "TASK_STATUS_UNSUPPORTED", {});
40577
+ return JSON.stringify({
40578
+ success: false,
40579
+ code: "TASK_STATUS_UNSUPPORTED",
40580
+ error: "Task status is not supported by the task lifecycle."
40581
+ });
40582
+ }
40583
+ const isAgentTask = existing.ownerType === "agent";
40584
+ const changesOwnerType = input.ownerType !== void 0 && input.ownerType !== existing.ownerType;
40585
+ const changesAgentIdentity = isAgentTask && (input.ownerId !== void 0 && input.ownerId !== existing.ownerId || input.parentId !== void 0 && input.parentId !== existing.parentId);
40586
+ if (changesOwnerType || changesAgentIdentity) {
40587
+ taskUpdateBlocked(input.id, "TASK_OWNERSHIP_IMMUTABLE", {});
40588
+ return JSON.stringify({
40589
+ success: false,
40590
+ code: "TASK_OWNERSHIP_IMMUTABLE",
40591
+ error: "Agent task ownerType, ownerId, and parentId cannot be changed after task creation.",
40592
+ hint: "Create a separate task for different ownership or parentage."
40593
+ });
40594
+ }
40595
+ const actor = trustedActor(trustedProject, rc) ?? (existing.ownerType === "agent" ? `agent:${existing.ownerId}` : `user:${existing.ownerId ?? ownerId}`);
40596
+ const statusWorkItemStore = trustedProject ? getProjectTaskWorkItemStore() : getTaskWorkItemStore();
40597
+ const lifecycle = isAgentTask ? createTaskLifecycleService({
40598
+ taskStore: store,
40599
+ workItemStore: statusWorkItemStore
40600
+ }) : void 0;
40113
40601
  const lifecycleMutationResponse = (result, extra = {}) => {
40114
40602
  if (result.success && result.mutated) markMutation();
40115
40603
  return lifecycleResponse(input.id, result, extra);
40116
40604
  };
40117
- const hasCarriedContentFields = () => ["title", "description", "priority", "dueDate", "metadata", "files"].some((key4) => input[key4] !== void 0);
40605
+ if (input.status === "in_progress" && (existing.dependencies ?? []).length > 0) {
40606
+ const dependencyError = await validateEffectiveDependencies(
40607
+ store,
40608
+ tenantId2,
40609
+ { workspaceId: existing.workspaceId, projectId: existing.projectId },
40610
+ existing.dependencies ?? []
40611
+ );
40612
+ if (dependencyError) {
40613
+ const trustedException = trustedProject?.projectTask ? await validateTrustedChildDependencyWait(
40614
+ store,
40615
+ tenantId2,
40616
+ existing,
40617
+ existing.dependencies ?? [],
40618
+ trustedProject
40619
+ ) : void 0;
40620
+ if (trustedException === void 0) {
40621
+ taskUpdateBlocked(input.id, "DEPENDENCY_BLOCKED", { dependencyError });
40622
+ return dependencyError;
40623
+ }
40624
+ if (trustedException) return trustedException;
40625
+ }
40626
+ }
40627
+ const statusThreadId = trustedProject?.projectTask?.threadId ?? (trustedProject?.projectRoom ? void 0 : input.sourceId ?? rc.thread_id);
40118
40628
  if (lifecycle && input.status === "failed") {
40119
40629
  await validateExecutionResult();
40120
40630
  const callerError = await revalidateCaller();
@@ -40124,11 +40634,9 @@ function createTaskMiddleware(options = {}) {
40124
40634
  taskId: input.id,
40125
40635
  failureReason: input.failureReason ?? existing.failureReason ?? "",
40126
40636
  actor,
40127
- threadId
40637
+ threadId: statusThreadId
40128
40638
  });
40129
- if (!failed.success) return lifecycleMutationResponse(failed);
40130
- if (trustedProject || !hasCarriedContentFields()) return lifecycleMutationResponse(failed);
40131
- existing = { ...existing, status: input.status };
40639
+ return lifecycleMutationResponse(failed);
40132
40640
  }
40133
40641
  if (lifecycle && input.status === "cancelled") {
40134
40642
  await validateExecutionResult();
@@ -40138,12 +40646,10 @@ function createTaskMiddleware(options = {}) {
40138
40646
  tenantId: tenantId2,
40139
40647
  taskId: input.id,
40140
40648
  actor,
40141
- threadId,
40649
+ threadId: statusThreadId,
40142
40650
  summary: input.summary
40143
40651
  });
40144
- if (!cancelled.success) return lifecycleMutationResponse(cancelled);
40145
- if (trustedProject || !hasCarriedContentFields()) return lifecycleMutationResponse(cancelled);
40146
- existing = { ...existing, status: input.status };
40652
+ return lifecycleMutationResponse(cancelled);
40147
40653
  }
40148
40654
  if (lifecycle && input.status === "interrupted" && !(input.context?.interruption && typeof input.context.interruption === "object" && input.context.interruption.type === "review_required")) {
40149
40655
  const interruption = input.context?.interruption;
@@ -40165,12 +40671,10 @@ function createTaskMiddleware(options = {}) {
40165
40671
  type,
40166
40672
  summary: input.summary ?? "",
40167
40673
  actor,
40168
- threadId,
40674
+ threadId: statusThreadId,
40169
40675
  dependencyTaskIds: input.dependencyTaskIds
40170
40676
  });
40171
- if (!interrupted.success) return lifecycleMutationResponse(interrupted);
40172
- if (trustedProject || !hasCarriedContentFields()) return lifecycleMutationResponse(interrupted);
40173
- existing = { ...existing, status: input.status };
40677
+ return lifecycleMutationResponse(interrupted);
40174
40678
  }
40175
40679
  if (lifecycle && input.status === "in_progress" && existing.status === "interrupted") {
40176
40680
  await validateExecutionResult();
@@ -40180,11 +40684,9 @@ function createTaskMiddleware(options = {}) {
40180
40684
  tenantId: tenantId2,
40181
40685
  taskId: input.id,
40182
40686
  actor,
40183
- threadId
40687
+ threadId: statusThreadId
40184
40688
  });
40185
- if (!resumed.success) return lifecycleMutationResponse(resumed);
40186
- if (trustedProject || !hasCarriedContentFields()) return lifecycleMutationResponse(resumed);
40187
- existing = { ...existing, status: input.status };
40689
+ return lifecycleMutationResponse(resumed);
40188
40690
  }
40189
40691
  if (lifecycle && input.status === "in_progress" && existing.status === "pending") {
40190
40692
  await validateExecutionResult();
@@ -40194,11 +40696,9 @@ function createTaskMiddleware(options = {}) {
40194
40696
  tenantId: tenantId2,
40195
40697
  taskId: input.id,
40196
40698
  actor,
40197
- threadId
40699
+ threadId: statusThreadId
40198
40700
  });
40199
- if (!started.success) return lifecycleMutationResponse(started);
40200
- if (trustedProject || !hasCarriedContentFields()) return lifecycleMutationResponse(started);
40201
- existing = { ...existing, status: input.status };
40701
+ return lifecycleMutationResponse(started);
40202
40702
  }
40203
40703
  if (lifecycle && input.status === "in_progress" && existing.status === "failed") {
40204
40704
  await validateExecutionResult();
@@ -40208,11 +40708,9 @@ function createTaskMiddleware(options = {}) {
40208
40708
  tenantId: tenantId2,
40209
40709
  taskId: input.id,
40210
40710
  actor,
40211
- threadId
40711
+ threadId: statusThreadId
40212
40712
  });
40213
- if (!retried.success) return lifecycleMutationResponse(retried);
40214
- if (trustedProject || !hasCarriedContentFields()) return lifecycleMutationResponse(retried);
40215
- existing = { ...existing, status: input.status };
40713
+ return lifecycleMutationResponse(retried);
40216
40714
  }
40217
40715
  if (isAgentTask && input.requireReview === true && options.reviewMode !== "hitl") {
40218
40716
  return reviewModeDisabledResponse();
@@ -40234,54 +40732,39 @@ function createTaskMiddleware(options = {}) {
40234
40732
  });
40235
40733
  }
40236
40734
  const effectiveRequireReview = existing.requireReview === true || input.requireReview === true;
40237
- const workItemStore = getTaskWorkItemStore();
40238
- const governedAgentCompletion = isAgentTask && (input.status === "completed" || candidateReviewInterruption && !!input.result?.trim() && !!input.beliefImpact?.length);
40735
+ const candidateReviewInterruption = input.status === "interrupted" && input.context?.interruption && typeof input.context.interruption === "object" && input.context.interruption.type === "review_required" && !!input.result?.trim() && !!input.beliefImpact?.length;
40736
+ const governedAgentCompletion = isAgentTask && (input.status === "completed" || candidateReviewInterruption);
40239
40737
  if (governedAgentCompletion) {
40240
- const guardedFields = [
40241
- "parentId",
40242
- "ownerType",
40243
- "ownerId",
40244
- "description"
40245
- ];
40246
- for (const field of guardedFields) {
40247
- const requestValue = input[field];
40248
- const existingValue = existing[field];
40249
- if (requestValue !== void 0 && requestValue !== existingValue) {
40250
- return JSON.stringify({
40251
- success: false,
40252
- code: "COMPLETION_CONTEXT_MUTATION",
40253
- error: `Cannot change ${field} while completing an agent task`,
40254
- hint: "Persist the context change in a separate update before completing the task"
40255
- });
40256
- }
40257
- }
40258
40738
  if (!input.beliefImpact?.length) return missingBeliefImpactError();
40259
40739
  const finalResult = input.result ?? existing.result ?? "";
40260
- const threadId2 = trustedProject?.projectTask?.threadId ?? (trustedProject?.projectRoom ? void 0 : input.sourceId ?? rc.thread_id);
40261
- const lifecycle2 = createTaskLifecycleService({
40262
- taskStore: store,
40263
- workItemStore: trustedProject ? getProjectTaskWorkItemStore() : getTaskWorkItemStore()
40264
- });
40740
+ if (!finalResult.trim()) {
40741
+ return JSON.stringify({
40742
+ success: false,
40743
+ code: "MISSING_RESULT",
40744
+ error: "result is required for completing a task",
40745
+ hint: "Provide a nonblank result"
40746
+ });
40747
+ }
40265
40748
  const completionInput = {
40266
40749
  tenantId: tenantId2,
40267
40750
  taskId: input.id,
40268
40751
  result: finalResult,
40269
40752
  beliefImpact: input.beliefImpact,
40270
40753
  actor,
40271
- threadId: threadId2,
40754
+ threadId: statusThreadId,
40272
40755
  sourceId: trustedProject ? void 0 : input.sourceId
40273
40756
  };
40274
40757
  await validateExecutionResult();
40275
- const needsReview = effectiveRequireReview || candidateReviewInterruption || !!reviewInterruption(existing);
40758
+ const needsReview = effectiveRequireReview || !!reviewInterruption(existing);
40276
40759
  if (!needsReview) {
40277
40760
  const callerError2 = await revalidateCaller();
40278
40761
  if (callerError2) return callerError2;
40279
- const result = existing.status === "completed" ? await lifecycle2.repairCompletion(completionInput) : await lifecycle2.completeAgentTask(completionInput);
40762
+ const result = existing.status === "completed" ? await lifecycle.repairCompletion(completionInput) : await lifecycle.completeAgentTask(completionInput);
40280
40763
  return lifecycleMutationResponse(result);
40281
40764
  }
40282
40765
  const callerError = await revalidateCaller();
40283
40766
  if (callerError) return callerError;
40284
- const submitted = await lifecycle2.submitReview({
40767
+ const submitted = await lifecycle.submitReview({
40285
40768
  ...completionInput,
40286
40769
  reviewMode: options.reviewMode ?? "disabled",
40287
40770
  configureRequireReview: input.requireReview === true && existing.requireReview !== true
@@ -40306,30 +40789,34 @@ function createTaskMiddleware(options = {}) {
40306
40789
  if (response?.action === "approve") {
40307
40790
  const approvalCallerError = await revalidateCaller();
40308
40791
  if (approvalCallerError) return approvalCallerError;
40309
- return lifecycleMutationResponse(await lifecycle2.approveInterrupted({
40792
+ return lifecycleMutationResponse(await lifecycle.approveInterrupted({
40310
40793
  tenantId: tenantId2,
40311
40794
  taskId: input.id,
40312
40795
  evidenceId,
40313
40796
  actor,
40314
- threadId: threadId2
40797
+ threadId: statusThreadId
40315
40798
  }), { reviewerAction: "approved" });
40316
40799
  }
40317
40800
  if (response?.action === "reject") {
40318
40801
  const note = response.data?.note?.trim() || "Changes requested.";
40319
40802
  const rejectionCallerError = await revalidateCaller();
40320
40803
  if (rejectionCallerError) return rejectionCallerError;
40321
- return lifecycleMutationResponse(await lifecycle2.rejectInterrupted({
40804
+ return lifecycleMutationResponse(await lifecycle.rejectInterrupted({
40322
40805
  tenantId: tenantId2,
40323
40806
  taskId: input.id,
40324
40807
  evidenceId,
40325
40808
  actor,
40326
- threadId: threadId2,
40809
+ threadId: statusThreadId,
40327
40810
  note
40328
40811
  }), { reviewerAction: "rejected", reviewerNote: note });
40329
40812
  }
40330
40813
  return lifecycleMutationResponse(submitted, { code: "REVIEW_REQUIRED" });
40331
40814
  }
40332
- if (isAgentTask && input.status !== void 0 && input.status !== existing.status) {
40815
+ if (isAgentTask && input.status !== existing.status) {
40816
+ taskUpdateBlocked(input.id, "AGENT_TASK_LIFECYCLE_REQUIRED", {
40817
+ currentStatus: existing.status,
40818
+ requestedStatus: input.status
40819
+ });
40333
40820
  return JSON.stringify({
40334
40821
  success: false,
40335
40822
  code: "AGENT_TASK_LIFECYCLE_REQUIRED",
@@ -40337,45 +40824,17 @@ function createTaskMiddleware(options = {}) {
40337
40824
  hint: "Valid transitions: pending\u2192in_progress, in_progress\u2192completed (requires result + beliefImpact), in_progress\u2192failed (requires failureReason), in_progress\u2192cancelled, in_progress\u2192interrupted. Check the current status with manage_task get first."
40338
40825
  });
40339
40826
  }
40340
- const persistedStatus = input.status;
40341
- let trustedReferenceSnapshots;
40342
- if (input.parentId !== void 0 || input.dependencies !== void 0) {
40343
- if (delegatedTaskId) {
40344
- const delegatedDependencyError = await validateDelegatedDependencies(
40345
- store,
40346
- tenantId2,
40347
- delegatedTaskId,
40348
- { workspaceId: existing.workspaceId, projectId: existing.projectId },
40349
- input.parentId,
40350
- input.dependencies,
40351
- input.id
40352
- );
40353
- if (delegatedDependencyError) return delegatedDependencyError;
40354
- }
40355
- const referenceError = await validateReferencedTasks(
40356
- store,
40357
- tenantId2,
40358
- { workspaceId: existing.workspaceId, projectId: existing.projectId },
40359
- input.parentId,
40360
- input.dependencies
40361
- );
40362
- if (referenceError) return referenceError;
40363
- if (trustedProject) {
40364
- trustedReferenceSnapshots = await captureReferencedTaskSnapshots(
40365
- store,
40366
- tenantId2,
40367
- { workspaceId: existing.workspaceId, projectId: existing.projectId },
40368
- input.parentId,
40369
- input.dependencies
40370
- );
40371
- if (!trustedReferenceSnapshots) return trustedContextError();
40372
- }
40373
- }
40374
40827
  if (input.status) {
40375
40828
  if (!isAgentTask && !isValidUserTransition(existing.status, input.status)) {
40376
40829
  const allowed = USER_VALID_TRANSITIONS[existing.status] || [];
40830
+ taskUpdateBlocked(input.id, "INVALID_USER_TRANSITION", {
40831
+ currentStatus: existing.status,
40832
+ requestedStatus: input.status,
40833
+ allowedTransitions: allowed
40834
+ });
40377
40835
  return JSON.stringify({
40378
40836
  success: false,
40837
+ code: "INVALID_USER_TRANSITION",
40379
40838
  error: `Cannot transition task from '${existing.status}' to '${input.status}'`,
40380
40839
  allowedTransitions: allowed,
40381
40840
  hint: `From '${existing.status}', valid transitions are: ${allowed.join(", ")}`
@@ -40393,154 +40852,47 @@ function createTaskMiddleware(options = {}) {
40393
40852
  );
40394
40853
  if (error) return error;
40395
40854
  }
40396
- const effectiveDependencies = input.dependencies ?? existing.dependencies ?? [];
40397
- const effectiveStatus = input.status ?? existing.status;
40398
- if (effectiveStatus === "in_progress" && effectiveDependencies.length > 0) {
40399
- const dependencyError = await validateEffectiveDependencies(
40400
- store,
40401
- tenantId2,
40402
- { workspaceId: existing.workspaceId, projectId: existing.projectId },
40403
- effectiveDependencies
40404
- );
40405
- if (dependencyError) {
40406
- const trustedException = trustedProject?.projectTask && input.dependencies !== void 0 ? await validateTrustedChildDependencyWait(
40407
- store,
40408
- tenantId2,
40409
- existing,
40410
- effectiveDependencies,
40411
- trustedProject
40412
- ) : void 0;
40413
- if (trustedException === void 0) return dependencyError;
40414
- if (trustedException) return trustedException;
40415
- }
40855
+ if (input.status === existing.status) {
40856
+ return successfulTaskResponse(input.id, existing, false);
40416
40857
  }
40417
- let reconciledTask;
40418
- let reconciliationMetadata = {};
40419
- let descriptionMutated = false;
40420
- if (isAgentTask && input.description !== void 0 && !valuesEqual(input.description, existing.description)) {
40421
- await validateExecutionResult();
40422
- const reconciliation = await lifecycle.updateTaskDescriptionWithReconciliation({
40423
- tenantId: tenantId2,
40424
- taskId: input.id,
40425
- description: input.description,
40426
- actor,
40427
- threadId: trustedProject?.projectTask?.threadId ?? (trustedProject?.projectRoom ? void 0 : input.sourceId ?? rc.thread_id)
40428
- });
40429
- if (!reconciliation.success) return JSON.stringify(reconciliation);
40430
- reconciledTask = reconciliation.task;
40431
- reconciliationMetadata = { ...reconciliation };
40432
- delete reconciliationMetadata.task;
40433
- descriptionMutated = reconciliation.mutated;
40434
- if (descriptionMutated) markMutation();
40435
- }
40436
- const updates = {};
40437
- const settableFields = [
40438
- "title",
40439
- "description",
40440
- "priority",
40441
- "dueDate",
40442
- "metadata",
40443
- "parentId",
40444
- "sourceId",
40445
- "context",
40446
- "ownerId",
40447
- "result",
40448
- "failureReason",
40449
- "requireReview",
40450
- "dependencies",
40451
- "files"
40452
- ];
40453
- for (const field of settableFields) {
40454
- if (trustedProject && (field === "sourceId" || field === "context" || field === "ownerId")) continue;
40455
- if (field === "description" && reconciledTask) continue;
40456
- const requested = input[field];
40457
- const persisted = reconciledTask ?? existing;
40458
- if (requested !== void 0 && !updateValuesEqual(field, requested, persisted[field])) {
40459
- updates[field] = requested;
40460
- }
40461
- }
40462
- if (persistedStatus !== void 0 && existing.status !== persistedStatus) {
40463
- updates.status = persistedStatus;
40464
- }
40465
- if (!reconciledTask && Object.keys(updates).length > 0) {
40466
- await validateExecutionResult();
40467
- }
40468
- if (trustedProject && (input.parentId !== void 0 || input.dependencies !== void 0)) {
40469
- if (!await referencedTaskSnapshotsStillMatch(
40470
- store,
40471
- tenantId2,
40472
- { workspaceId: existing.workspaceId, projectId: existing.projectId },
40473
- trustedReferenceSnapshots
40474
- )) return trustedContextError("Parent or dependency task changed before update.");
40475
- }
40476
- if (trustedProject) {
40477
- try {
40478
- await createProjectTaskOwnerPolicy().requireCaller(trustedRosterScope(trustedProject));
40479
- } catch (error) {
40480
- return projectPolicyError(error);
40481
- }
40482
- }
40483
- const snapshot = trustedProject ? taskMutationSnapshot(existing) : void 0;
40484
- const updated = Object.keys(updates).length > 0 ? snapshot ? await store.updateIfSnapshot(tenantId2, input.id, updates, snapshot) : await store.update(tenantId2, input.id, updates) : reconciledTask ?? existing;
40485
- if (!updated) {
40858
+ const statusUpdates = { status: input.status };
40859
+ if (input.status === "completed" && input.result !== void 0) statusUpdates.result = input.result;
40860
+ if (input.status === "failed" && input.failureReason !== void 0) statusUpdates.failureReason = input.failureReason;
40861
+ if (input.summary !== void 0) statusUpdates.summary = input.summary;
40862
+ const statusSnapshot = trustedProject ? taskMutationSnapshot(existing) : void 0;
40863
+ taskUpdateDebug("set_status:write", {
40864
+ status: input.status,
40865
+ useCas: !!statusSnapshot
40866
+ });
40867
+ const updatedStatus = await (statusSnapshot ? store.updateIfSnapshot(tenantId2, input.id, statusUpdates, statusSnapshot) : store.update(tenantId2, input.id, statusUpdates));
40868
+ taskUpdateDebug("set_status:result", {
40869
+ updated: !!updatedStatus,
40870
+ resultStatus: updatedStatus?.status
40871
+ });
40872
+ if (!updatedStatus) {
40873
+ taskUpdateBlocked(input.id, "TASK_UPDATE_FAILED", {});
40486
40874
  return JSON.stringify({
40487
40875
  success: false,
40488
- error: `Failed to update task '${input.id}'`,
40489
- hint: "The task may have been deleted or the ID is incorrect"
40876
+ code: "TASK_UPDATE_FAILED",
40877
+ error: `Failed to update status of task '${input.id}'.`,
40878
+ hint: "Use manage_task get to verify the task exists, then retry."
40490
40879
  });
40491
40880
  }
40492
- if (Object.keys(updates).length > 0) markMutation();
40493
- if (reconciledTask && Object.keys(updates).length === 0) {
40494
- return successfulTaskResponse(input.id, updated, descriptionMutated, reconciliationMetadata);
40495
- }
40496
- if (Object.keys(updates).length === 0) {
40497
- return successfulTaskResponse(input.id, updated, false);
40498
- }
40499
- const actionMap = {
40500
- pending: "pending",
40501
- in_progress: "started",
40502
- failed: "failed",
40503
- interrupted: "interrupted",
40504
- completed: "completed",
40505
- cancelled: "cancelled"
40506
- };
40507
- const workItemAction = persistedStatus ? actionMap[persistedStatus] || "updated" : "updated";
40508
- const workItemSummary = input.summary || (persistedStatus ? `Status changed to ${persistedStatus}` : void 0);
40509
- let activityWarnings;
40881
+ markMutation();
40510
40882
  try {
40511
- if (trustedProject) {
40512
- try {
40513
- await createProjectTaskOwnerPolicy().requireCaller(trustedRosterScope(trustedProject));
40514
- } catch (error) {
40515
- return projectPolicyError(error);
40516
- }
40517
- }
40518
- await workItemStore.create({
40883
+ await statusWorkItemStore.create({
40519
40884
  taskId: input.id,
40520
40885
  tenantId: tenantId2,
40521
- action: workItemAction,
40886
+ action: input.status,
40522
40887
  actor,
40523
- threadId: trustedProject?.projectTask?.threadId ?? (trustedProject?.projectRoom ? void 0 : input.sourceId),
40524
- summary: workItemSummary,
40525
- detail: {
40526
- ...input.result !== void 0 && { result: input.result },
40527
- ...input.failureReason !== void 0 && { failureReason: input.failureReason }
40528
- },
40529
- workspaceId: updated.workspaceId,
40530
- projectId: updated.projectId
40888
+ threadId: statusThreadId ?? input.sourceId,
40889
+ summary: input.summary || `Status changed to ${input.status}`,
40890
+ workspaceId: updatedStatus.workspaceId,
40891
+ projectId: updatedStatus.projectId
40531
40892
  });
40532
- } catch (error) {
40533
- activityWarnings = [{
40534
- code: "TASK_UPDATE_ACTIVITY_WRITE_FAILED",
40535
- taskId: input.id,
40536
- error: error instanceof Error ? error.message : String(error)
40537
- }];
40893
+ } catch {
40538
40894
  }
40539
- const lifecycleWarnings = Array.isArray(reconciliationMetadata.warnings) ? reconciliationMetadata.warnings : void 0;
40540
- return successfulTaskResponse(input.id, updated, true, {
40541
- ...reconciliationMetadata,
40542
- warnings: mergeTaskUpdateWarnings(lifecycleWarnings, activityWarnings)
40543
- });
40895
+ return successfulTaskResponse(input.id, updatedStatus, true);
40544
40896
  }
40545
40897
  case "delete": {
40546
40898
  if (!input.id) {
@@ -40557,8 +40909,9 @@ function createTaskMiddleware(options = {}) {
40557
40909
  if (!existing) {
40558
40910
  return JSON.stringify({
40559
40911
  success: false,
40560
- error: `Task '${input.id}' not found`,
40561
- hint: "Use list to see available tasks and their IDs"
40912
+ code: "TASK_NOT_FOUND",
40913
+ error: `Task '${input.id}' was not found.`,
40914
+ hint: "Use manage_task list to see available tasks and their correct IDs."
40562
40915
  });
40563
40916
  }
40564
40917
  if (!taskMatchesRuntimeScope3(existing, workspaceId, projectId)) {
@@ -40584,8 +40937,9 @@ function createTaskMiddleware(options = {}) {
40584
40937
  if (!deleted) {
40585
40938
  return JSON.stringify({
40586
40939
  success: false,
40587
- error: `Task '${input.id}' not found or could not be deleted`,
40588
- hint: "Use list to verify the task exists"
40940
+ code: "TASK_DELETE_FAILED",
40941
+ error: `Task '${input.id}' was not found or could not be deleted.`,
40942
+ hint: "Use manage_task list to verify the task exists and you have access to it."
40589
40943
  });
40590
40944
  }
40591
40945
  return JSON.stringify({ success: true, message: `Task '${input.id}' deleted` });
@@ -40600,7 +40954,7 @@ function createTaskMiddleware(options = {}) {
40600
40954
  }
40601
40955
  };
40602
40956
  const reconcileExplicitExecutionResult = async (input, config, response, validationError, didMutate) => {
40603
- if (input.action !== "update" && input.action !== "add_activity" || !input.id) return response;
40957
+ if (input.action !== "update" && input.action !== "set_status" && input.action !== "add_activity" || !input.id) return response;
40604
40958
  const authority = classifyTaskAuthority(getRunConfig2(config));
40605
40959
  if (authority.kind === "projectRoom" || authority.kind === "projectTask" || authority.kind === "invalid") {
40606
40960
  return response;
@@ -40794,14 +41148,18 @@ ${TASK_CONVERGENCE_GUIDANCE}
40794
41148
  - Explicit ownerId: assign to a specific agent (cross-agent delegation)
40795
41149
 
40796
41150
  ## Actions
40797
- - create: Create a task (title required; result required for completed; failureReason required for failed)
41151
+ - create: Create a task (title required)
40798
41152
  - get: Get one task and its newest 100 activities (id required)
40799
41153
  - list: List tasks, filterable by ownerType/status/priority/parentId
40800
- - update: Update a task (id required; pass only the fields to change)
40801
- To mark complete: update with status='completed', result='...' (a nonblank input or existing result is required)
40802
- To mark failed: update with status='failed', failureReason='...' (a nonblank input or existing failureReason is required)
40803
- To pause for missing input: update with status='interrupted', summary='what is needed' (defaults to interruption type missing_input)
40804
- Status transitions are validated \u2014 only allowed transitions will succeed.
41154
+ - update: Update a task's content fields (title/description/priority/dueDate/metadata/parentId/sourceId/context/ownerId/result/failureReason/requireReview/dependencies/files)
41155
+ Status changes can also be done here, but use set_status for a simpler dedicated status update.
41156
+ - set_status: Change a task's status (id and status required). Use this when you only need to change the status.
41157
+ Valid status values: pending, in_progress, review, failed, interrupted, completed, cancelled
41158
+ Companion fields: result (for completed), failureReason (for failed), summary (for any status)
41159
+ To mark complete: set_status with status='completed', result='...'
41160
+ To mark failed: set_status with status='failed', failureReason='...'
41161
+ To pause for input: set_status with status='interrupted', summary='what is needed'
41162
+ Status can be freely changed to any valid value \u2014 no restrictions on transitions.
40805
41163
  - add_activity: Add a Markdown activity without changing task fields or status (id and content required)
40806
41164
  - delete: Delete a task (id required)
40807
41165