@axiom-lattice/core 4.2.4 → 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) {
@@ -12261,6 +12265,18 @@ async function createCommonMiddlewares(middlewareConfigs, filesystemBackend, fsI
12261
12265
 
12262
12266
  // src/deep_agent_new/backends/sandboxFiles.ts
12263
12267
  import * as path3 from "path";
12268
+ import micromatch2 from "micromatch";
12269
+ function matchesGlobFilter(matchPath, searchPath, glob) {
12270
+ if (!glob || glob === "**/*") {
12271
+ return true;
12272
+ }
12273
+ if (!glob.includes("/")) {
12274
+ return micromatch2.isMatch(path3.basename(matchPath), glob, { dot: true });
12275
+ }
12276
+ const base = searchPath.endsWith("/") ? searchPath : `${searchPath}/`;
12277
+ const relative4 = matchPath.startsWith(base) ? matchPath.slice(base.length) : matchPath;
12278
+ return micromatch2.isMatch(relative4, glob, { dot: true });
12279
+ }
12264
12280
  var SandboxFilesystem = class {
12265
12281
  constructor(options = {}) {
12266
12282
  const { maxFileSizeMb = 10, sandboxInstance } = options;
@@ -12387,6 +12403,16 @@ var SandboxFilesystem = class {
12387
12403
  }
12388
12404
  async grepRaw(pattern, searchPath = "/", glob = null) {
12389
12405
  try {
12406
+ if (this.sandbox.file.grepRecursive) {
12407
+ const result2 = await this.sandbox.file.grepRecursive(
12408
+ pattern,
12409
+ searchPath || "/"
12410
+ );
12411
+ const globFilter = glob || "**/*";
12412
+ return result2.matches.filter(
12413
+ (match) => matchesGlobFilter(match.path, searchPath || "/", globFilter)
12414
+ );
12415
+ }
12390
12416
  const result = await this.sandbox.file.findFiles(searchPath || "/", glob || "**/*");
12391
12417
  const filePaths = result.files;
12392
12418
  const matches = [];
@@ -18542,7 +18568,7 @@ import * as fsSync from "fs";
18542
18568
  import * as path4 from "path";
18543
18569
  import { spawn } from "child_process";
18544
18570
  import fg from "fast-glob";
18545
- import micromatch2 from "micromatch";
18571
+ import micromatch3 from "micromatch";
18546
18572
  var SUPPORTS_NOFOLLOW = fsSync.constants.O_NOFOLLOW !== void 0;
18547
18573
  var FilesystemBackend = class {
18548
18574
  constructor(options = {}) {
@@ -19043,7 +19069,7 @@ var FilesystemBackend = class {
19043
19069
  });
19044
19070
  for (const fp of files) {
19045
19071
  try {
19046
- if (includeGlob && !micromatch2.isMatch(path4.basename(fp), includeGlob)) {
19072
+ if (includeGlob && !micromatch3.isMatch(path4.basename(fp), includeGlob)) {
19047
19073
  continue;
19048
19074
  }
19049
19075
  const stat5 = await fs2.stat(fp);
@@ -22542,8 +22568,12 @@ Otherwise, the owning caller interprets the returned observation.
22542
22568
  An execution result is an observation, not automatic proof of business
22543
22569
  completion. \`task\` returns its exact \`executionResultId\`. After \`task\`
22544
22570
  returns, inspect the observation, then pass that \`executionResultId\` to the
22545
- meaningful \`manage_task update\` or \`manage_task add_activity\` that interprets
22546
- 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
22547
22577
  \`executionResultId\` leaves the result pending so forgotten handling remains
22548
22578
  detectable. Reconciliation does not require the task to become \`completed\`;
22549
22579
  choose the truthful status. \`manage_task get\` exposes pending execution-result
@@ -22563,13 +22593,14 @@ independently.
22563
22593
  6. **Reconcile** - revise Belief, Task, plan, and next action on Prediction Error,
22564
22594
  then repeat only while a decision-relevant action remains and stop at honest convergence.
22565
22595
 
22566
- Converge using existing statuses:
22596
+ Converge through \`set_status\` using existing statuses:
22567
22597
 
22568
- - \`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\`.
22569
22600
  - \`in_progress\`: a feasible, decision-relevant action remains.
22570
22601
  - \`pending\`: work is future or dependency-blocked.
22571
22602
  - \`interrupted\`: an external condition is missing; record the condition needed to resume.
22572
- - \`failed\`: no reasonable path remains; include a \`failureReason\`.
22603
+ - \`failed\`: no reasonable path remains; \`set_status\` with a \`failureReason\`.
22573
22604
  - \`cancelled\`: the goal or subgoal is no longer needed.
22574
22605
 
22575
22606
  For human judgment, use the actual HITL payload \`status: "interrupted"\` plus
@@ -23859,6 +23890,35 @@ Renew HITL approval when a foundational assumption or authority boundary changes
23859
23890
 
23860
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.
23861
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
+
23862
23922
  ## Agent Forms and Routing
23863
23923
  - **Capability** (\`react\`) for bounded direct work: [[agent-build]]
23864
23924
  - **Orchestra** (\`deep_agent\`) for open-ended delegated work: [[agent-build]]
@@ -23871,8 +23931,11 @@ Renew HITL approval when a foundational assumption or authority boundary changes
23871
23931
  ## Architect Task Policy
23872
23932
  The Agent Architect uses persistent TaskItems as its planning surface. Architect must not use
23873
23933
  \`write_todos\`. Create a persistent child only for an independently verifiable
23874
- outcome; tool calls and file reads are actions inside the active Task. Load
23875
- [[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.
23876
23939
  Learning authority is **normal** or **bounded**: normal for fresh work without a
23877
23940
  bound round; bounded for learning rounds that bind an existing target and
23878
23941
  tracking Task with a confirmed goal. Identity remains fixed; materialization uses
@@ -24101,6 +24164,9 @@ ${TASK_BELIEF_REFERENCE_EXAMPLE}
24101
24164
 
24102
24165
  ## Status discipline \u2014 MANDATORY
24103
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.
24104
24170
  - A subtask is \`completed\` ONLY when its work is actually finished.
24105
24171
  While fixing eval failures, keep it \`in_progress\` \u2014 never mark
24106
24172
  \`completed\` as a workaround (the state machine rejects illegal
@@ -24345,12 +24411,18 @@ flows the decomposition is presented for user confirmation first.
24345
24411
  **3. Create each sub-agent.** After [[agent-architecture]] is loaded, per sub-agent:
24346
24412
  Call \`create_agent(type: "react", skillLoaded: true, skills: ["[[skill-name]]", ...)\`
24347
24413
  where skillLoaded: true is set only after [[agent-architecture]] is loaded.
24348
- - Each sub-agent gets its OWN eval project: \`manage_eval create_project(name: "eval-{{sub-agent-id}}")\`
24414
+ - Each sub-agent gets its OWN eval project (eval-{sub-agent-id}); per [[eval-verify]]
24415
+ Setup, first resolve the sub-agent's exact id and check whether its project already
24416
+ exists \u2014 reuse it when it does; only create (and confirm) when it does not. Never
24417
+ create a duplicate project for an agent that already has one.
24349
24418
  - Design eval cases per [[eval-design-tests]], run per [[eval-verify]]
24350
24419
  - Sub-agent eval passes before you proceed to the next sub-agent
24351
24420
 
24352
24421
  **4. Integration eval.** After ALL sub-agents pass their evals:
24353
- - Create integration eval project: \`manage_eval create_project(name: "eval-{{parent-id}}")\`
24422
+ - The integration project for the parent (eval-{parent-id}) goes through the same
24423
+ [[eval-verify]] Setup check: confirm the parent's project does not already exist,
24424
+ present the proposed creation to the user, then
24425
+ \`manage_eval create_project(name: "eval-{parent-id}", targetAgentId: "{parent-id}", evalVerifyLoaded: true)\`.
24354
24426
  - Integration eval tests the parent's delegation logic, not sub-agent correctness
24355
24427
  - [[eval-verify]] owns the run
24356
24428
 
@@ -24583,14 +24655,39 @@ its evidence projection, not an independent source of goals or expectations.
24583
24655
  before calling manage_eval") apply to every manage_eval call in this skill \u2014 not
24584
24656
  just during the case design phase. Do not call manage_eval without presenting the
24585
24657
  change first.
24586
- 1. **One eval project per agent**, named \`eval-{agent-id}\`:
24587
- \`read_eval list_projects\` \u2192 find "eval-{agent-id}"
24588
- Exists \u2192 reuse projectId. New \u2192 \`manage_eval create_project(name: "eval-{agent-id}")\`
24589
- - The agent-id is the eval project's subject. Observability: from an
24590
- agent's id you can find its eval project by naming convention.
24658
+ 1. **Resolve the subject agent FIRST \u2014 one eval project per agent, never per task or per round.**
24659
+ An eval project belongs to exactly one agent-under-test, named \`eval-{agent-id}\`.
24660
+ The agent-id is the eval project's subject. Observability: from an agent's id you
24661
+ can find its eval project by naming convention. Before ANY project creation you MUST:
24662
+ - **Pin the exact subject agent-id.** The subject is the materialized target agent
24663
+ being evaluated: the id returned by \`create_agent\` / \`update_agent\` /
24664
+ \`create_workflow\` / \`update_workflow\`, or the exact bound target ID pinned by
24665
+ the learning kickoff / round task. It is NEVER the Architect session id, the
24666
+ round/task id, a skill name, or any invented label.
24667
+ - **If the exact agent-id is unknown or unconfirmed \u2014 STOP and ask.** Do NOT guess a
24668
+ name, do NOT create a project, and do NOT proceed to case design. Present which
24669
+ agent is under test and confirm with the user (\`ask_user_to_clarify\`) before
24670
+ touching any eval project. An eval project created without a real, confirmed
24671
+ subject is a hard error. If the agent is only planned (not yet materialized),
24672
+ evaluation must wait until materialization returns its id.
24673
+ - **Check existence before creating; reuse is the rule, creation is the exception.**
24674
+ 1) \`read_eval list_projects\`
24675
+ 2) Find the project whose \`targetAgentId\` equals the subject agent-id; fall back
24676
+ to the name \`eval-{agent-id}\` only when no pointer exists (legacy projects).
24677
+ 3) Exists \u2192 reuse its projectId. NEVER create a second project for the same agent \u2014
24678
+ project names are unique per tenant and duplicate projects are forbidden.
24679
+ 4) Not exists \u2192 present the proposed project (name \`eval-{agent-id}\` +
24680
+ \`targetAgentId\`) and confirm with the user, THEN
24681
+ \`manage_eval create_project(name: "eval-{agent-id}", targetAgentId: "{agent-id}", evalVerifyLoaded: true, ...)\`.
24682
+ The tool refuses create_project without \`evalVerifyLoaded: true\` \u2014 that flag
24683
+ confirms you are following THIS skill's Setup (resolve \u2192 check \u2192 reuse/confirm).
24684
+ Always pass \`targetAgentId\` so the agent\u2194project link is structural, not name-only.
24685
+ - Re-resolve on every round and every fix pass of the same agent. A new round or task
24686
+ is NOT a reason to create a new project \u2014 reuse the agent's canonical project.
24591
24687
  - Orchestrator + subAgents \u2192 one eval project per sub-agent
24592
24688
  (eval-{sub-agent-id}) PLUS one integration eval project for the
24593
- parent (eval-{parent-id}) \u2014 see Layered verification below.
24689
+ parent (eval-{parent-id}) \u2014 see Layered verification below. Every project goes
24690
+ through the same resolve-then-confirm-then-create check above.
24594
24691
  2. \`manage_eval create_case(suiteId, ...)\` per sample or scenario
24595
24692
  Required: inputMessage, steps=[{agent_id}], outputType, contentAssertion
24596
24693
  **contentAssertion MUST come from the confirmed goal, approved expected output,
@@ -25598,11 +25695,11 @@ function isUndecidedLearningPlaceholder(config) {
25598
25695
  const metadata = getStringMetadata(config);
25599
25696
  return config.type === AgentType3.REACT && metadata.learningPlaceholder === "true" && metadata.architectureStatus === "undecided";
25600
25697
  }
25601
- function requireArchitectSkill(skillLoaded, exeConfig) {
25602
- if (exeConfig?.configurable?.runConfig?.assistant_id === "agent-architect" && skillLoaded !== true) {
25698
+ function requireSkillDeclaration(skillLoaded) {
25699
+ if (skillLoaded !== true) {
25603
25700
  return JSON.stringify({
25604
25701
  success: false,
25605
- error: "Load the 'agent-architecture' skill before creating or updating an agent."
25702
+ error: "Load the 'agent-architecture' skill via the skill tool (skill_name 'agent-architecture'), then retry this exact call with skillLoaded: true."
25606
25703
  });
25607
25704
  }
25608
25705
  return void 0;
@@ -25715,7 +25812,7 @@ var middlewareConfigSchema = z49.object({
25715
25812
  config: z49.record(z49.any()).optional()
25716
25813
  });
25717
25814
  var createAgentSchema = z49.object({
25718
- skillLoaded: z49.literal(true).optional().describe("For agent-architect, set true only after loading agent-architecture in the current conversation context."),
25815
+ skillLoaded: z49.boolean().optional().describe("Required on every call: load the 'agent-architecture' skill first via the skill tool (skill_name 'agent-architecture'), then pass skillLoaded: true. Calls without skillLoaded: true fail. Never omit or pass false."),
25719
25816
  name: z49.string().describe("Human-friendly display name for the agent. The machine ID (used in other tools) is auto-generated as a slug from this name (e.g. 'My Cool Agent' \u2192 'my-cool-agent')."),
25720
25817
  description: z49.string().optional().describe("Short description"),
25721
25818
  type: z49.enum(["react", "deep_agent"]).describe("Agent type. Use 'react' for simple single-responsibility agents, 'deep_agent' for complex open-ended agents. For PROCESSING agents (workflow orchestration), use create_processing_agent instead."),
@@ -25736,9 +25833,9 @@ registerToolLattice(
25736
25833
  },
25737
25834
  async (input, exeConfig) => {
25738
25835
  try {
25739
- const tenantId2 = getTenantId2(exeConfig);
25740
- const skillError = requireArchitectSkill(input.skillLoaded, exeConfig);
25836
+ const skillError = requireSkillDeclaration(input.skillLoaded);
25741
25837
  if (skillError) return skillError;
25838
+ const tenantId2 = getTenantId2(exeConfig);
25742
25839
  const store = getAssistStore();
25743
25840
  const taskConfigIssue = validateTaskMiddlewareConfig(input.middleware);
25744
25841
  if (taskConfigIssue) return JSON.stringify(taskConfigIssue);
@@ -25773,7 +25870,7 @@ registerToolLattice(
25773
25870
  var createWorkflowSchema = z49.object({
25774
25871
  name: z49.string().describe("Display name for the workflow agent"),
25775
25872
  description: z49.string().optional().describe("Short description"),
25776
- skillLoaded: z49.literal(true).describe("MUST be true. Set after loading the 'create-workflow' skill."),
25873
+ skillLoaded: z49.boolean().optional().describe("Required on every call: load the 'agent-architecture' skill first via the skill tool (skill_name 'agent-architecture'), then pass skillLoaded: true. Calls without skillLoaded: true fail. Never omit or pass false."),
25777
25874
  yaml: z49.string().describe("The YAML workflow definition in linear DSL format (steps execute top-to-bottom, use parallel: for concurrency)"),
25778
25875
  tools: z49.array(z49.string()).optional().describe("Tool keys for the workflow agent"),
25779
25876
  middleware: z49.array(middlewareConfigSchema).optional().describe("Middleware configs"),
@@ -25788,6 +25885,8 @@ registerToolLattice(
25788
25885
  },
25789
25886
  async (input, exeConfig) => {
25790
25887
  try {
25888
+ const skillError = requireSkillDeclaration(input.skillLoaded);
25889
+ if (skillError) return skillError;
25791
25890
  const tenantId2 = getTenantId2(exeConfig);
25792
25891
  const store = getAssistStore();
25793
25892
  const taskConfigIssue = validateTaskMiddlewareConfig(input.middleware);
@@ -25928,7 +26027,7 @@ registerToolLattice(
25928
26027
  );
25929
26028
  var updateWorkflowSchema = z49.object({
25930
26029
  id: z49.string().describe("The workflow agent ID to update"),
25931
- skillLoaded: z49.literal(true).optional().describe("For agent-architect, set true only after loading agent-architecture in the current conversation context."),
26030
+ skillLoaded: z49.boolean().optional().describe("Required on every call: load the 'agent-architecture' skill first via the skill tool (skill_name 'agent-architecture'), then pass skillLoaded: true. Calls without skillLoaded: true fail. Never omit or pass false."),
25932
26031
  name: z49.string().optional().describe("New display name"),
25933
26032
  description: z49.string().optional().describe("New description"),
25934
26033
  yaml: z49.string().optional().describe("Replacement YAML workflow DSL. Omit to keep existing."),
@@ -25940,13 +26039,13 @@ registerToolLattice(
25940
26039
  "update_workflow",
25941
26040
  {
25942
26041
  name: "update_workflow",
25943
- description: "Update an existing workflow agent or materialize an eligible marked learning placeholder. For agent-architect, pass skillLoaded: true after loading agent-architecture. Placeholder eligibility is not proof that the user approved the architecture; approval remains a prompt/HITL contract.",
26042
+ description: "Update an existing workflow agent or materialize an eligible marked learning placeholder. Pass skillLoaded: true on every call after loading agent-architecture (skill tool, skill_name 'agent-architecture'); calls without it fail. Placeholder eligibility is not proof that the user approved the architecture; approval remains a prompt/HITL contract.",
25944
26043
  schema: updateWorkflowSchema
25945
26044
  },
25946
26045
  async (input, exeConfig) => {
25947
26046
  console.log(`[update_workflow] CALLED id=${input.id} hasYaml=${input.yaml !== void 0}`);
25948
26047
  try {
25949
- const skillError = requireArchitectSkill(input.skillLoaded, exeConfig);
26048
+ const skillError = requireSkillDeclaration(input.skillLoaded);
25950
26049
  if (skillError) return skillError;
25951
26050
  const tenantId2 = getTenantId2(exeConfig);
25952
26051
  const store = getAssistStore();
@@ -26024,7 +26123,7 @@ registerToolLattice(
26024
26123
  description: input.description !== void 0 ? input.description : existing.description,
26025
26124
  graphDefinition: mergedConfig
26026
26125
  });
26027
- if (isPlaceholder && updated === null) {
26126
+ if (updated === null) {
26028
26127
  return JSON.stringify({
26029
26128
  success: false,
26030
26129
  code: "ASSISTANT_UPDATE_FAILED",
@@ -26042,7 +26141,7 @@ registerToolLattice(
26042
26141
  );
26043
26142
  var updateAgentSchema = z49.object({
26044
26143
  id: z49.string().describe("The agent ID to update"),
26045
- skillLoaded: z49.literal(true).optional().describe("For agent-architect, set true only after loading agent-architecture in the current conversation context."),
26144
+ skillLoaded: z49.boolean().optional().describe("Required on every call: load the 'agent-architecture' skill first via the skill tool (skill_name 'agent-architecture'), then pass skillLoaded: true. Calls without skillLoaded: true fail. Never omit or pass false."),
26046
26145
  config: z49.object({
26047
26146
  name: z49.string().optional().describe("New display name for the agent"),
26048
26147
  description: z49.string().optional().describe("New short description"),
@@ -26060,14 +26159,14 @@ registerToolLattice(
26060
26159
  "update_agent",
26061
26160
  {
26062
26161
  name: "update_agent",
26063
- description: "Update an existing agent's configuration. Provide the agent ID and the fields to change. Returns the updated agent's ID and name. NOTE: For user approval/confirmation scenarios, add middleware with type: 'ask_user_to_clarify'. IMPORTANT: do NOT set 'modelKey' unless the user explicitly specified a model \u2014 leaving it unset makes the runtime use the 'default' model.",
26162
+ description: "Update an existing agent's configuration. Provide the agent ID and the fields to change. Returns the updated agent's ID and name. Pass skillLoaded: true on every call after loading agent-architecture (skill tool, skill_name 'agent-architecture'); calls without it fail. NOTE: For user approval/confirmation scenarios, add middleware with type: 'ask_user_to_clarify'. IMPORTANT: do NOT set 'modelKey' unless the user explicitly specified a model \u2014 leaving it unset makes the runtime use the 'default' model.",
26064
26163
  schema: updateAgentSchema
26065
26164
  },
26066
26165
  async (input, exeConfig) => {
26067
26166
  try {
26068
- const tenantId2 = getTenantId2(exeConfig);
26069
- const skillError = requireArchitectSkill(input.skillLoaded, exeConfig);
26167
+ const skillError = requireSkillDeclaration(input.skillLoaded);
26070
26168
  if (skillError) return skillError;
26169
+ const tenantId2 = getTenantId2(exeConfig);
26071
26170
  const store = getAssistStore();
26072
26171
  const existing = await store.getAssistantById(tenantId2, input.id);
26073
26172
  if (!existing) {
@@ -26107,7 +26206,7 @@ registerToolLattice(
26107
26206
  description: input.config.description !== void 0 ? input.config.description : existing.description,
26108
26207
  graphDefinition: mergedConfig
26109
26208
  });
26110
- if (isMaterializingPlaceholder && updated === null) {
26209
+ if (updated === null) {
26111
26210
  return JSON.stringify({
26112
26211
  success: false,
26113
26212
  code: "ASSISTANT_UPDATE_FAILED",
@@ -26336,8 +26435,11 @@ var AGENT_ARCHITECT_PROMPT = `You are the Agent Architect.
26336
26435
 
26337
26436
  CRITICAL FIRST ACTION: Before any response or other action, call the \`skill\`
26338
26437
  tool with skill_name: "agent-architecture" to load agent-architecture and follow it.
26339
- Never announce the skill load. If it fails, retry once, then stop and explicitly
26340
- 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.`;
26341
26443
 
26342
26444
  // src/agent_lattice/agentReviewerConfig.ts
26343
26445
  import { AgentType as AgentType4 } from "@axiom-lattice/protocols";
@@ -28324,6 +28426,25 @@ var mcpManager = McpLatticeManager.getInstance();
28324
28426
  // src/sandbox_lattice/MicrosandboxRemoteInstance.ts
28325
28427
  import * as posix3 from "path/posix";
28326
28428
 
28429
+ // src/sandbox_lattice/grepUtils.ts
28430
+ function buildRecursiveGrepCommand(pattern, path8) {
28431
+ return `grep -r -n -I -E -e ${quotePosixShellArg(pattern)} ${quotePosixShellArg(path8)}`;
28432
+ }
28433
+ function parseRecursiveGrepOutput(output) {
28434
+ const matches = [];
28435
+ for (const line of output.split("\n")) {
28436
+ if (!line) continue;
28437
+ const match = /^(.*):(\d+):(.*)$/.exec(line);
28438
+ if (!match) continue;
28439
+ matches.push({
28440
+ path: match[1],
28441
+ line: Number.parseInt(match[2], 10),
28442
+ text: match[3]
28443
+ });
28444
+ }
28445
+ return matches;
28446
+ }
28447
+
28327
28448
  // src/sandbox_lattice/packDirectory.ts
28328
28449
  import { randomBytes } from "crypto";
28329
28450
  import * as posix2 from "path/posix";
@@ -28459,6 +28580,15 @@ var MicrosandboxRemoteInstance = class {
28459
28580
  line_numbers: result.matches.map((match) => match.line)
28460
28581
  };
28461
28582
  },
28583
+ grepRecursive: async (pattern, searchPath) => {
28584
+ const result = await this.shell.execCommand({
28585
+ command: buildRecursiveGrepCommand(
28586
+ pattern,
28587
+ normalizeExternalSandboxPath(searchPath)
28588
+ )
28589
+ });
28590
+ return { matches: parseRecursiveGrepOutput(result.output) };
28591
+ },
28462
28592
  strReplaceEditor: async (params) => {
28463
28593
  await this.client.replaceInFile(this.name, {
28464
28594
  path: normalizeExternalSandboxPath(params.path),
@@ -28991,6 +29121,13 @@ var RemoteSandboxInstance = class {
28991
29121
  line_numbers: result.body.data?.line_numbers || []
28992
29122
  };
28993
29123
  },
29124
+ grepRecursive: async (pattern, searchPath) => {
29125
+ const resolved = this.resolvePath(searchPath);
29126
+ const result = await this.shell.execCommand({
29127
+ command: buildRecursiveGrepCommand(pattern, resolved)
29128
+ });
29129
+ return { matches: parseRecursiveGrepOutput(result.output) };
29130
+ },
28994
29131
  strReplaceEditor: async (params) => {
28995
29132
  const resolved = this.resolvePath(params.path);
28996
29133
  const result = await this.client.file.strReplaceEditor({
@@ -29294,7 +29431,8 @@ var RemoteSandboxProvider = class {
29294
29431
  kind: f.is_dir === true || f.size === null ? "directory" : "file",
29295
29432
  size: f.size ?? 0,
29296
29433
  mode: 0,
29297
- modified: f.modified_at ?? null
29434
+ modified: f.modified_at ?? null,
29435
+ created: f.created_at ?? null
29298
29436
  }));
29299
29437
  return entries;
29300
29438
  },
@@ -29370,6 +29508,12 @@ var E2BInstance = class {
29370
29508
  }
29371
29509
  return { matches, line_numbers };
29372
29510
  },
29511
+ grepRecursive: async (pattern, searchPath) => {
29512
+ const result = await this.native.commands.run(
29513
+ buildRecursiveGrepCommand(pattern, searchPath)
29514
+ );
29515
+ return { matches: parseRecursiveGrepOutput(result.stdout) };
29516
+ },
29373
29517
  strReplaceEditor: async (params) => {
29374
29518
  const { path: path8, old_str, new_str, replace_mode } = params;
29375
29519
  const delim = "#";
@@ -29568,6 +29712,21 @@ var DaytonaInstance = class {
29568
29712
  }
29569
29713
  return { matches: matchTexts, line_numbers };
29570
29714
  },
29715
+ grepRecursive: async (pattern, searchPath) => {
29716
+ const matches = await this.native.fs.findFiles(
29717
+ toRelativePath(searchPath),
29718
+ pattern
29719
+ );
29720
+ const results = [];
29721
+ for (const match of matches) {
29722
+ results.push({
29723
+ path: normalizeExternalSandboxPath(match.file ?? ""),
29724
+ line: match.line ?? 0,
29725
+ text: match.content ?? ""
29726
+ });
29727
+ }
29728
+ return { matches: results };
29729
+ },
29571
29730
  strReplaceEditor: async (params) => {
29572
29731
  const { path: path8, old_str, new_str, replace_mode } = params;
29573
29732
  const relPath = toRelativePath(path8);
@@ -29950,6 +30109,37 @@ var LocalSandboxInstance = class {
29950
30109
  }
29951
30110
  return { matches, line_numbers };
29952
30111
  },
30112
+ grepRecursive: async (pattern, searchPath) => {
30113
+ const hp = this.hostPath(searchPath);
30114
+ let regex;
30115
+ try {
30116
+ regex = new RegExp(pattern);
30117
+ } catch {
30118
+ return { matches: [] };
30119
+ }
30120
+ const hostFiles = [];
30121
+ await this.walkDirFilter(hp, /.+/, hostFiles);
30122
+ const hpNorm = hp.endsWith(path5.sep) ? hp : hp + path5.sep;
30123
+ const toSandboxPath = (hostFilePath) => posix5.join(searchPath, hostFilePath.slice(hpNorm.length).split(path5.sep).join("/"));
30124
+ const matches = [];
30125
+ for (const hostFile of hostFiles) {
30126
+ const content = await fs3.readFile(hostFile, "utf-8").catch(() => null);
30127
+ if (content === null || content.includes("\0")) {
30128
+ continue;
30129
+ }
30130
+ const lines = content.split("\n");
30131
+ for (let i = 0; i < lines.length; i++) {
30132
+ if (regex.test(lines[i])) {
30133
+ matches.push({
30134
+ path: toSandboxPath(hostFile),
30135
+ line: i + 1,
30136
+ text: lines[i]
30137
+ });
30138
+ }
30139
+ }
30140
+ }
30141
+ return { matches };
30142
+ },
29953
30143
  strReplaceEditor: async (params) => {
29954
30144
  const content = await fs3.readFile(this.hostPath(params.path), "utf-8");
29955
30145
  let result;
@@ -30989,8 +31179,12 @@ Note: if final_score >= 80 and there are no fatal errors, pass should be true; o
30989
31179
  });
30990
31180
  }
30991
31181
  } else if (parsedResult.pass !== void 0) {
30992
- pass = parsedResult.pass;
30993
- 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
+ });
30994
31188
  } else if (parsedResult.final_score !== void 0) {
30995
31189
  pass = parsedResult.final_score >= 80;
30996
31190
  this.log("Pass determined from parsedResult.final_score", {
@@ -32738,7 +32932,11 @@ var TaskLifecycleService = class {
32738
32932
  ...event2.warning ? { warnings: [event2.warning] } : {}
32739
32933
  };
32740
32934
  }
32741
- 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
+ );
32742
32940
  if (interruption && typeof interruption === "object" && interruption.type === "review_required") {
32743
32941
  return failure2("REVIEW_APPROVAL_REQUIRED", "Review interruption requires explicit approval or rejection.");
32744
32942
  }
@@ -32835,7 +33033,11 @@ var TaskLifecycleService = class {
32835
33033
  task.updatedAt
32836
33034
  );
32837
33035
  } catch {
32838
- 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 };
32839
33041
  }
32840
33042
  if (!updated) {
32841
33043
  let current;
@@ -32863,7 +33065,11 @@ var TaskLifecycleService = class {
32863
33065
  ...repaired.warning ? { warnings: [repaired.warning] } : {}
32864
33066
  };
32865
33067
  }
32866
- 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 };
32867
33073
  }
32868
33074
  const event = await this.writeStatusEvent(
32869
33075
  updated,
@@ -33007,10 +33213,18 @@ var TaskLifecycleService = class {
33007
33213
  const storedExternal = parseExternalDependency(currentInterruption);
33008
33214
  if (input.type === "external_dependency") {
33009
33215
  if (!external || !storedExternal || storedExternal.summary !== external.summary || canonicalJson(storedExternal.dependencyTaskIds) !== canonicalJson(external.dependencyTaskIds)) {
33010
- 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
+ );
33011
33221
  }
33012
33222
  } else if (currentInterruption.type !== input.type || currentInterruption.summary !== summary) {
33013
- 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
+ );
33014
33228
  }
33015
33229
  const detail = storedExternal ? { ...storedExternal } : currentInterruption;
33016
33230
  const eventKey2 = this.statusEventKey(task, "interrupted", detail);
@@ -33027,7 +33241,11 @@ var TaskLifecycleService = class {
33027
33241
  ...warnings2.length ? { warnings: warnings2 } : {}
33028
33242
  };
33029
33243
  }
33030
- 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
+ );
33031
33249
  if (external && isProjectLifecycleTask(task)) {
33032
33250
  const dependencyFailure = await this.validateExternalDependencies(task, external.dependencyTaskIds);
33033
33251
  if (dependencyFailure) return dependencyFailure;
@@ -33040,9 +33258,17 @@ var TaskLifecycleService = class {
33040
33258
  context: mergeInterruptionContext(task.context, interruption, input.threadId)
33041
33259
  }, ["in_progress"], task.updatedAt);
33042
33260
  } catch {
33043
- 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
+ );
33044
33266
  }
33045
- 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
+ );
33046
33272
  const eventKey = this.statusEventKey(updated, "interrupted", interruption);
33047
33273
  const event = await this.writeStatusEvent(updated, input.actor, input.threadId, "interrupted", summary, interruption, eventKey);
33048
33274
  const warnings = event.warning ? [event.warning] : await this.notifyWinningTransition(updated, "interrupted", event);
@@ -33080,7 +33306,11 @@ var TaskLifecycleService = class {
33080
33306
  context: withCompletionAudit(prepared.task.context, prepared.evidence.id, prepared.eventKey)
33081
33307
  }, ["in_progress"]);
33082
33308
  } catch {
33083
- 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
+ );
33084
33314
  }
33085
33315
  if (!updated) {
33086
33316
  if (await this.hasStaleAgentParent(prepared)) {
@@ -33643,7 +33873,11 @@ var TaskLifecycleService = class {
33643
33873
  } catch {
33644
33874
  return failure2("TASK_STORE_WRITE_FAILED", "Failed to reconcile task description.");
33645
33875
  }
33646
- 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
+ );
33647
33881
  if (changedKeys.length === 0) {
33648
33882
  return {
33649
33883
  success: true,
@@ -36427,7 +36661,7 @@ import z56 from "zod";
36427
36661
  import { tool as tool51 } from "langchain";
36428
36662
  var DESCRIPTION3 = `RUNTIME QUERY SURFACE \u2014 read what is published and answer business questions with numbers. Business agents use ONLY this tool; Builder agents also use it to verify freshly published metrics.
36429
36663
 
36430
- OWNS: read_semantic_catalog (the published semantic model for a datasource: tables, metrics, dimensions, filters) and query_metrics (the semantic metric query: metrics / groupBy / filters / orderBy / limit \u2014 the server generates the SQL, so there is no free-form SQL here).
36664
+ OWNS: list_datasources (self-contained datasource discovery \u2014 redacted output, the first step before anything else), read_semantic_catalog (the published semantic model for a datasource: tables, metrics, dimensions, filters) and query_metrics (the semantic metric query: metrics / groupBy / filters / orderBy / limit \u2014 the server generates the SQL, so there is no free-form SQL here).
36431
36665
 
36432
36666
  DOES NOT OWN:
36433
36667
  - Publishing or correcting semantic tables/metrics \u2192 metrics_meta_tool (create_table/update_table/create_metric/update_metric/list_tables/read_table_meta/list_metrics/read_metric_meta).
@@ -36448,6 +36682,18 @@ function createMetricsRuntimeTool(params) {
36448
36682
  const client = await resolveClientFromConnections(input.connectionKey, resolvedConnections, readToolRunConfig(exeConfig));
36449
36683
  const selected = effectiveDatasourceScope(client.getSelectedDataSources(), selectedDataSources);
36450
36684
  switch (input.action) {
36685
+ case "list_datasources": {
36686
+ const result = await client.listDatasources();
36687
+ const filtered = selected.length > 0 ? result.filter((d) => selected.includes(Number(d.id))) : result;
36688
+ const safe = filtered.map((d) => ({
36689
+ id: d.id,
36690
+ name: d.name,
36691
+ sourceType: d.sourceType,
36692
+ description: d.description,
36693
+ statusLabel: d.statusLabel
36694
+ }));
36695
+ return JSON.stringify(safe, null, 2);
36696
+ }
36451
36697
  case "read_semantic_catalog": {
36452
36698
  const ds = resolveDatasourceId(input, exeConfig);
36453
36699
  assertDatasourceSelected(selected, ds);
@@ -36470,11 +36716,11 @@ function createMetricsRuntimeTool(params) {
36470
36716
  name: "metrics_runtime_tool",
36471
36717
  description: DESCRIPTION3,
36472
36718
  schema: z56.object({
36473
- action: z56.enum(["read_semantic_catalog", "query_metrics"]).describe(
36474
- "Actions ONLY in this runtime tool: read_semantic_catalog (read the published semantic model: tables, metrics, dimensions \u2014 obtain the datasourceId from metrics_datasource_tool list_datasources first), query_metrics (run a semantic metric query \u2014 the only way to get business numbers). Do NOT put meta-tool actions (list_tables/read_table_meta/create_table/update_table/list_metrics/read_metric_meta/create_metric/update_metric) here"
36719
+ action: z56.enum(["list_datasources", "read_semantic_catalog", "query_metrics"]).describe(
36720
+ "Actions ONLY in this runtime tool: list_datasources (list available datasources \u2014 ALWAYS call this first to obtain the datasourceId; output is redacted to non-sensitive fields), read_semantic_catalog (read the published semantic model: tables, metrics, dimensions), query_metrics (run a semantic metric query \u2014 the only way to get business numbers). Do NOT put meta-tool actions (list_tables/read_table_meta/create_table/update_table/list_metrics/read_metric_meta/create_metric/update_metric) here"
36475
36721
  ),
36476
36722
  connectionKey: z56.string().optional().describe("Connection key. Omit when only one semantic-metrics connection exists"),
36477
- datasourceId: z56.coerce.number().optional().describe("Numeric datasource id, e.g. 15 (optional if set in runConfig.metricsDataSource)"),
36723
+ datasourceId: z56.coerce.number().optional().describe("Numeric datasource id, e.g. 15 (required for read_semantic_catalog; optional if set in runConfig.metricsDataSource)"),
36478
36724
  query: z56.record(z56.unknown()).optional().describe('Semantic metric query request. Required for the query_metrics action, e.g. { datasourceId: 15, metrics: ["sell_in_nes"], groupBy: ["sales_team"], filters: [{ dimension: "posting_year", operator: "GTE", values: [2024] }], limit: 10 }')
36479
36725
  })
36480
36726
  }
@@ -36939,7 +37185,7 @@ var semanticMetricsPlugin = {
36939
37185
  configSchema: {
36940
37186
  type: "object",
36941
37187
  title: "Semantic Metrics Configuration",
36942
- description: "First select connections (connections or connectAll). Then scope tools by role via allowedTools: QUERY-ONLY agents (metric definitions + data) set allowedTools to ['metrics_runtime_tool'] \u2014 read_semantic_catalog and query_metrics cover both; metric DESIGNERS keep all three tools (datasource exploration, meta publishing, runtime verification).",
37188
+ description: "First select connections (connections or connectAll). Then scope tools by role via allowedTools: QUERY-ONLY agents (metric definitions + data) set allowedTools to ['metrics_runtime_tool'] \u2014 it is self-contained (list_datasources, read_semantic_catalog, query_metrics); metric DESIGNERS keep all three tools (datasource exploration, meta publishing, runtime verification).",
36943
37189
  properties: {
36944
37190
  connections: {
36945
37191
  type: "array",
@@ -38619,6 +38865,7 @@ import { z as z70 } from "zod";
38619
38865
  import { GraphInterrupt as GraphInterrupt2, interrupt as interrupt4 } from "@langchain/langgraph";
38620
38866
  import {
38621
38867
  isExecutionResultEventKey as isExecutionResultEventKey3,
38868
+ parseTaskBeliefState as parseTaskBeliefState3,
38622
38869
  parseTrustedRunContext as parseTrustedRunContext2,
38623
38870
  requireProjectTaskWorkItemStore
38624
38871
  } from "@axiom-lattice/protocols";
@@ -38929,6 +39176,16 @@ function getRunConfig2(config) {
38929
39176
  const c = config;
38930
39177
  return c?.configurable?.runConfig ?? {};
38931
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
+ }
38932
39189
  function getRuntimeActor2(runConfig) {
38933
39190
  if (runConfig.assistant_id) return `agent:${String(runConfig.assistant_id)}`;
38934
39191
  if (runConfig.user_id) return `user:${String(runConfig.user_id)}`;
@@ -39155,6 +39412,7 @@ async function validateEffectiveDependencies(store, tenantId2, targetScope, depe
39155
39412
  if (blockedBy.length === 0) return void 0;
39156
39413
  return JSON.stringify({
39157
39414
  success: false,
39415
+ code: "DEPENDENCY_BLOCKED",
39158
39416
  error: `Cannot start task: ${blockedBy.length} dependencies not completed`,
39159
39417
  blockedBy,
39160
39418
  invalidScope,
@@ -39305,13 +39563,27 @@ function missingBeliefImpactError() {
39305
39563
  hint: "Provide beliefImpact: [{ key: '<belief-key>', after: <0-100>, basis: '<evidence>' }]"
39306
39564
  });
39307
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
+ }
39308
39580
  var manageTaskSchema = z70.object({
39309
- 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."),
39310
39582
  id: z70.string().optional().describe("Task ID (required for get, update, add_activity, and delete)"),
39311
39583
  title: z70.string().optional().describe("Task title (required for create)"),
39312
39584
  description: z70.string().optional().describe("Task description in Markdown"),
39313
39585
  priority: z70.enum(["low", "medium", "high"]).optional().describe("Priority level"),
39314
- 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"),
39315
39587
  dueDate: z70.string().optional().describe("Due date (ISO 8601 format)"),
39316
39588
  metadata: z70.record(z70.unknown()).optional().describe("Structured metadata (e.g. projectId, module)"),
39317
39589
  parentId: z70.string().optional().describe("Parent task ID for grouping subtasks"),
@@ -39322,8 +39594,8 @@ var manageTaskSchema = z70.object({
39322
39594
  requireReview: z70.boolean().optional().describe("If true, agent completion pauses as interrupted(review_required) for HITL approval"),
39323
39595
  dependencies: z70.array(z70.string()).optional().describe("List of task IDs that must be completed before this task can start"),
39324
39596
  dependencyTaskIds: z70.array(z70.string().trim().min(1)).optional().describe("Exact blocking task IDs required for an external_dependency interruption"),
39325
- result: z70.string().optional().describe("Nonblank result summary required when creating or updating a task with status='completed'"),
39326
- 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'"),
39327
39599
  files: z70.array(z70.object({
39328
39600
  uri: z70.string().describe("Uniquely locates the resource: http(s):// URL, /s/:token share, or sandbox path"),
39329
39601
  name: z70.string().optional().describe("Display name"),
@@ -39342,11 +39614,11 @@ var manageTaskSchema = z70.object({
39342
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"
39343
39615
  )
39344
39616
  }).superRefine((input, context) => {
39345
- 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") {
39346
39618
  context.addIssue({
39347
39619
  code: z70.ZodIssueCode.custom,
39348
39620
  path: ["executionResultId"],
39349
- message: "executionResultId is valid only for update or add_activity"
39621
+ message: "executionResultId is valid only for update, set_status, or add_activity"
39350
39622
  });
39351
39623
  }
39352
39624
  });
@@ -39460,12 +39732,6 @@ function updateValuesEqual(field, requested, persisted) {
39460
39732
  }
39461
39733
  return valuesEqual(requested, persisted);
39462
39734
  }
39463
- function reviewInterruption(task) {
39464
- const value = task.context?.interruption;
39465
- if (!value || typeof value !== "object") return void 0;
39466
- const interruption = value;
39467
- return interruption.type === "review_required" ? interruption : void 0;
39468
- }
39469
39735
  function reviewModeDisabledResponse() {
39470
39736
  return JSON.stringify({
39471
39737
  success: false,
@@ -39474,14 +39740,6 @@ function reviewModeDisabledResponse() {
39474
39740
  hint: "Configure task middleware with reviewMode 'hitl' before enabling requireReview."
39475
39741
  });
39476
39742
  }
39477
- function buildReviewMarkdown(task) {
39478
- return genUIMarkdown("task_review", {
39479
- taskId: task.id,
39480
- title: task.title,
39481
- description: task.description,
39482
- result: task.result
39483
- });
39484
- }
39485
39743
  function createTaskMiddleware(options = {}) {
39486
39744
  const handleManageTaskAction = async (input, config, validateExecutionResult, markMutation) => {
39487
39745
  const rc = getRunConfig2(config);
@@ -39869,35 +40127,75 @@ function createTaskMiddleware(options = {}) {
39869
40127
  return JSON.stringify({ success: true, data: activity });
39870
40128
  }
39871
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
+ });
39872
40142
  if (!input.id) {
40143
+ taskUpdateBlocked(input.id, "MISSING_TASK_ID", {});
39873
40144
  return JSON.stringify({
39874
40145
  success: false,
40146
+ code: "MISSING_TASK_ID",
39875
40147
  error: "id is required for update action",
39876
40148
  hint: "Pass the task ID you want to update"
39877
40149
  });
39878
40150
  }
39879
40151
  if (delegatedTaskId && input.id !== delegatedTaskId) {
40152
+ taskUpdateBlocked(input.id, "DELEGATED_TASK_SCOPE_VIOLATION", { delegatedTaskId });
39880
40153
  return delegatedTaskScopeError(delegatedTaskId);
39881
40154
  }
39882
- 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 });
39883
40164
  if (!existing) {
40165
+ taskUpdateBlocked(input.id, "TASK_NOT_FOUND", {});
39884
40166
  return JSON.stringify({
39885
40167
  success: false,
39886
- error: `Task '${input.id}' not found`,
39887
- 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."
39888
40171
  });
39889
40172
  }
39890
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
+ });
39891
40180
  return taskScopeAccessError(input.id);
39892
40181
  }
39893
40182
  if (trustedProject?.projectTask) {
39894
40183
  const trustedFailure = trustedTaskContextFailure(existing, trustedProject.projectTask);
39895
- 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
+ }
39896
40193
  }
39897
40194
  if (trustedProject && input.dependencyTaskIds !== void 0) {
39898
40195
  const interruption = input.context?.interruption;
39899
40196
  const interruptionType2 = interruption && typeof interruption === "object" ? interruption.type : void 0;
39900
40197
  if (input.status !== "interrupted" || interruptionType2 !== "external_dependency") {
40198
+ taskUpdateBlocked(input.id, "INVALID_EXTERNAL_DEPENDENCY", {});
39901
40199
  return JSON.stringify({
39902
40200
  success: false,
39903
40201
  code: "INVALID_EXTERNAL_DEPENDENCY",
@@ -39906,24 +40204,26 @@ function createTaskMiddleware(options = {}) {
39906
40204
  }
39907
40205
  }
39908
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
+ });
39909
40213
  return JSON.stringify({
39910
40214
  success: false,
39911
40215
  code: "TASK_REASSIGN_ENDPOINT_REQUIRED",
39912
40216
  error: "Project task ownership changes require the reassignment endpoint."
39913
40217
  });
39914
40218
  }
39915
- if (existing.status === "review" || input.status === "review") {
39916
- return JSON.stringify({
39917
- success: false,
39918
- code: "TASK_STATUS_UNSUPPORTED",
39919
- error: "Task status is not supported by the task lifecycle."
39920
- });
39921
- }
39922
40219
  const isAgentTask = existing.ownerType === "agent";
39923
- 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;
39924
40220
  const changesOwnerType = input.ownerType !== void 0 && input.ownerType !== existing.ownerType;
39925
40221
  const changesAgentIdentity = isAgentTask && (input.ownerId !== void 0 && input.ownerId !== existing.ownerId || input.parentId !== void 0 && input.parentId !== existing.parentId);
39926
40222
  if (changesOwnerType || changesAgentIdentity) {
40223
+ taskUpdateBlocked(input.id, "TASK_OWNERSHIP_IMMUTABLE", {
40224
+ changesOwnerType,
40225
+ changesAgentIdentity
40226
+ });
39927
40227
  return JSON.stringify({
39928
40228
  success: false,
39929
40229
  code: "TASK_OWNERSHIP_IMMUTABLE",
@@ -39931,18 +40231,10 @@ function createTaskMiddleware(options = {}) {
39931
40231
  hint: "Create a separate task for different ownership or parentage."
39932
40232
  });
39933
40233
  }
39934
- const hasMismatchedAgentOutput = isAgentTask && (input.result !== void 0 && input.status !== "completed" && !candidateReviewInterruption || input.failureReason !== void 0 && input.status !== "failed");
39935
- if (hasMismatchedAgentOutput) {
39936
- return JSON.stringify({
39937
- success: false,
39938
- code: "TASK_LIFECYCLE_TRANSITION_REQUIRED",
39939
- error: "Agent task result and failureReason require a matching governed lifecycle transition.",
39940
- hint: "Provide result only with completion, or failureReason only with failure."
39941
- });
39942
- }
39943
40234
  const changesOwnership = input.ownerId !== void 0 && input.ownerId !== existing.ownerId || input.parentId !== void 0 && input.parentId !== existing.parentId;
39944
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;
39945
40236
  if (changesOwnership && changesGovernedState) {
40237
+ taskUpdateBlocked(input.id, "TASK_OWNERSHIP_MUTATION_CONFLICT", {});
39946
40238
  return JSON.stringify({
39947
40239
  success: false,
39948
40240
  code: "TASK_OWNERSHIP_MUTATION_CONFLICT",
@@ -39950,17 +40242,389 @@ function createTaskMiddleware(options = {}) {
39950
40242
  hint: "Persist ownership or parent changes separately before changing lifecycle or task content."
39951
40243
  });
39952
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
+ }
39953
40294
  const actor = trustedActor(trustedProject, rc) ?? (existing.ownerType === "agent" ? `agent:${existing.ownerId}` : `user:${existing.ownerId ?? ownerId}`);
39954
- const threadId = trustedProject?.projectTask?.threadId ?? (trustedProject?.projectRoom ? void 0 : input.sourceId ?? rc.thread_id);
40295
+ const workItemStore = getTaskWorkItemStore();
39955
40296
  const lifecycle = isAgentTask ? createTaskLifecycleService({
39956
40297
  taskStore: store,
39957
40298
  workItemStore: trustedProject ? getProjectTaskWorkItemStore() : getTaskWorkItemStore()
39958
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;
39959
40601
  const lifecycleMutationResponse = (result, extra = {}) => {
39960
40602
  if (result.success && result.mutated) markMutation();
39961
40603
  return lifecycleResponse(input.id, result, extra);
39962
40604
  };
39963
- 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);
39964
40628
  if (lifecycle && input.status === "failed") {
39965
40629
  await validateExecutionResult();
39966
40630
  const callerError = await revalidateCaller();
@@ -39970,11 +40634,9 @@ function createTaskMiddleware(options = {}) {
39970
40634
  taskId: input.id,
39971
40635
  failureReason: input.failureReason ?? existing.failureReason ?? "",
39972
40636
  actor,
39973
- threadId
40637
+ threadId: statusThreadId
39974
40638
  });
39975
- if (!failed.success) return lifecycleMutationResponse(failed);
39976
- if (trustedProject || !hasCarriedContentFields()) return lifecycleMutationResponse(failed);
39977
- existing = { ...existing, status: input.status };
40639
+ return lifecycleMutationResponse(failed);
39978
40640
  }
39979
40641
  if (lifecycle && input.status === "cancelled") {
39980
40642
  await validateExecutionResult();
@@ -39984,12 +40646,10 @@ function createTaskMiddleware(options = {}) {
39984
40646
  tenantId: tenantId2,
39985
40647
  taskId: input.id,
39986
40648
  actor,
39987
- threadId,
40649
+ threadId: statusThreadId,
39988
40650
  summary: input.summary
39989
40651
  });
39990
- if (!cancelled.success) return lifecycleMutationResponse(cancelled);
39991
- if (trustedProject || !hasCarriedContentFields()) return lifecycleMutationResponse(cancelled);
39992
- existing = { ...existing, status: input.status };
40652
+ return lifecycleMutationResponse(cancelled);
39993
40653
  }
39994
40654
  if (lifecycle && input.status === "interrupted" && !(input.context?.interruption && typeof input.context.interruption === "object" && input.context.interruption.type === "review_required")) {
39995
40655
  const interruption = input.context?.interruption;
@@ -40011,12 +40671,10 @@ function createTaskMiddleware(options = {}) {
40011
40671
  type,
40012
40672
  summary: input.summary ?? "",
40013
40673
  actor,
40014
- threadId,
40674
+ threadId: statusThreadId,
40015
40675
  dependencyTaskIds: input.dependencyTaskIds
40016
40676
  });
40017
- if (!interrupted.success) return lifecycleMutationResponse(interrupted);
40018
- if (trustedProject || !hasCarriedContentFields()) return lifecycleMutationResponse(interrupted);
40019
- existing = { ...existing, status: input.status };
40677
+ return lifecycleMutationResponse(interrupted);
40020
40678
  }
40021
40679
  if (lifecycle && input.status === "in_progress" && existing.status === "interrupted") {
40022
40680
  await validateExecutionResult();
@@ -40026,11 +40684,9 @@ function createTaskMiddleware(options = {}) {
40026
40684
  tenantId: tenantId2,
40027
40685
  taskId: input.id,
40028
40686
  actor,
40029
- threadId
40687
+ threadId: statusThreadId
40030
40688
  });
40031
- if (!resumed.success) return lifecycleMutationResponse(resumed);
40032
- if (trustedProject || !hasCarriedContentFields()) return lifecycleMutationResponse(resumed);
40033
- existing = { ...existing, status: input.status };
40689
+ return lifecycleMutationResponse(resumed);
40034
40690
  }
40035
40691
  if (lifecycle && input.status === "in_progress" && existing.status === "pending") {
40036
40692
  await validateExecutionResult();
@@ -40040,11 +40696,9 @@ function createTaskMiddleware(options = {}) {
40040
40696
  tenantId: tenantId2,
40041
40697
  taskId: input.id,
40042
40698
  actor,
40043
- threadId
40699
+ threadId: statusThreadId
40044
40700
  });
40045
- if (!started.success) return lifecycleMutationResponse(started);
40046
- if (trustedProject || !hasCarriedContentFields()) return lifecycleMutationResponse(started);
40047
- existing = { ...existing, status: input.status };
40701
+ return lifecycleMutationResponse(started);
40048
40702
  }
40049
40703
  if (lifecycle && input.status === "in_progress" && existing.status === "failed") {
40050
40704
  await validateExecutionResult();
@@ -40054,11 +40708,9 @@ function createTaskMiddleware(options = {}) {
40054
40708
  tenantId: tenantId2,
40055
40709
  taskId: input.id,
40056
40710
  actor,
40057
- threadId
40711
+ threadId: statusThreadId
40058
40712
  });
40059
- if (!retried.success) return lifecycleMutationResponse(retried);
40060
- if (trustedProject || !hasCarriedContentFields()) return lifecycleMutationResponse(retried);
40061
- existing = { ...existing, status: input.status };
40713
+ return lifecycleMutationResponse(retried);
40062
40714
  }
40063
40715
  if (isAgentTask && input.requireReview === true && options.reviewMode !== "hitl") {
40064
40716
  return reviewModeDisabledResponse();
@@ -40080,54 +40732,39 @@ function createTaskMiddleware(options = {}) {
40080
40732
  });
40081
40733
  }
40082
40734
  const effectiveRequireReview = existing.requireReview === true || input.requireReview === true;
40083
- const workItemStore = getTaskWorkItemStore();
40084
- 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);
40085
40737
  if (governedAgentCompletion) {
40086
- const guardedFields = [
40087
- "parentId",
40088
- "ownerType",
40089
- "ownerId",
40090
- "description"
40091
- ];
40092
- for (const field of guardedFields) {
40093
- const requestValue = input[field];
40094
- const existingValue = existing[field];
40095
- if (requestValue !== void 0 && requestValue !== existingValue) {
40096
- return JSON.stringify({
40097
- success: false,
40098
- code: "COMPLETION_CONTEXT_MUTATION",
40099
- error: `Cannot change ${field} while completing an agent task`,
40100
- hint: "Persist the context change in a separate update before completing the task"
40101
- });
40102
- }
40103
- }
40104
40738
  if (!input.beliefImpact?.length) return missingBeliefImpactError();
40105
40739
  const finalResult = input.result ?? existing.result ?? "";
40106
- const threadId2 = trustedProject?.projectTask?.threadId ?? (trustedProject?.projectRoom ? void 0 : input.sourceId ?? rc.thread_id);
40107
- const lifecycle2 = createTaskLifecycleService({
40108
- taskStore: store,
40109
- workItemStore: trustedProject ? getProjectTaskWorkItemStore() : getTaskWorkItemStore()
40110
- });
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
+ }
40111
40748
  const completionInput = {
40112
40749
  tenantId: tenantId2,
40113
40750
  taskId: input.id,
40114
40751
  result: finalResult,
40115
40752
  beliefImpact: input.beliefImpact,
40116
40753
  actor,
40117
- threadId: threadId2,
40754
+ threadId: statusThreadId,
40118
40755
  sourceId: trustedProject ? void 0 : input.sourceId
40119
40756
  };
40120
40757
  await validateExecutionResult();
40121
- const needsReview = effectiveRequireReview || candidateReviewInterruption || !!reviewInterruption(existing);
40758
+ const needsReview = effectiveRequireReview || !!reviewInterruption(existing);
40122
40759
  if (!needsReview) {
40123
40760
  const callerError2 = await revalidateCaller();
40124
40761
  if (callerError2) return callerError2;
40125
- 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);
40126
40763
  return lifecycleMutationResponse(result);
40127
40764
  }
40128
40765
  const callerError = await revalidateCaller();
40129
40766
  if (callerError) return callerError;
40130
- const submitted = await lifecycle2.submitReview({
40767
+ const submitted = await lifecycle.submitReview({
40131
40768
  ...completionInput,
40132
40769
  reviewMode: options.reviewMode ?? "disabled",
40133
40770
  configureRequireReview: input.requireReview === true && existing.requireReview !== true
@@ -40152,30 +40789,34 @@ function createTaskMiddleware(options = {}) {
40152
40789
  if (response?.action === "approve") {
40153
40790
  const approvalCallerError = await revalidateCaller();
40154
40791
  if (approvalCallerError) return approvalCallerError;
40155
- return lifecycleMutationResponse(await lifecycle2.approveInterrupted({
40792
+ return lifecycleMutationResponse(await lifecycle.approveInterrupted({
40156
40793
  tenantId: tenantId2,
40157
40794
  taskId: input.id,
40158
40795
  evidenceId,
40159
40796
  actor,
40160
- threadId: threadId2
40797
+ threadId: statusThreadId
40161
40798
  }), { reviewerAction: "approved" });
40162
40799
  }
40163
40800
  if (response?.action === "reject") {
40164
40801
  const note = response.data?.note?.trim() || "Changes requested.";
40165
40802
  const rejectionCallerError = await revalidateCaller();
40166
40803
  if (rejectionCallerError) return rejectionCallerError;
40167
- return lifecycleMutationResponse(await lifecycle2.rejectInterrupted({
40804
+ return lifecycleMutationResponse(await lifecycle.rejectInterrupted({
40168
40805
  tenantId: tenantId2,
40169
40806
  taskId: input.id,
40170
40807
  evidenceId,
40171
40808
  actor,
40172
- threadId: threadId2,
40809
+ threadId: statusThreadId,
40173
40810
  note
40174
40811
  }), { reviewerAction: "rejected", reviewerNote: note });
40175
40812
  }
40176
40813
  return lifecycleMutationResponse(submitted, { code: "REVIEW_REQUIRED" });
40177
40814
  }
40178
- 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
+ });
40179
40820
  return JSON.stringify({
40180
40821
  success: false,
40181
40822
  code: "AGENT_TASK_LIFECYCLE_REQUIRED",
@@ -40183,45 +40824,17 @@ function createTaskMiddleware(options = {}) {
40183
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."
40184
40825
  });
40185
40826
  }
40186
- const persistedStatus = input.status;
40187
- let trustedReferenceSnapshots;
40188
- if (input.parentId !== void 0 || input.dependencies !== void 0) {
40189
- if (delegatedTaskId) {
40190
- const delegatedDependencyError = await validateDelegatedDependencies(
40191
- store,
40192
- tenantId2,
40193
- delegatedTaskId,
40194
- { workspaceId: existing.workspaceId, projectId: existing.projectId },
40195
- input.parentId,
40196
- input.dependencies,
40197
- input.id
40198
- );
40199
- if (delegatedDependencyError) return delegatedDependencyError;
40200
- }
40201
- const referenceError = await validateReferencedTasks(
40202
- store,
40203
- tenantId2,
40204
- { workspaceId: existing.workspaceId, projectId: existing.projectId },
40205
- input.parentId,
40206
- input.dependencies
40207
- );
40208
- if (referenceError) return referenceError;
40209
- if (trustedProject) {
40210
- trustedReferenceSnapshots = await captureReferencedTaskSnapshots(
40211
- store,
40212
- tenantId2,
40213
- { workspaceId: existing.workspaceId, projectId: existing.projectId },
40214
- input.parentId,
40215
- input.dependencies
40216
- );
40217
- if (!trustedReferenceSnapshots) return trustedContextError();
40218
- }
40219
- }
40220
40827
  if (input.status) {
40221
40828
  if (!isAgentTask && !isValidUserTransition(existing.status, input.status)) {
40222
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
+ });
40223
40835
  return JSON.stringify({
40224
40836
  success: false,
40837
+ code: "INVALID_USER_TRANSITION",
40225
40838
  error: `Cannot transition task from '${existing.status}' to '${input.status}'`,
40226
40839
  allowedTransitions: allowed,
40227
40840
  hint: `From '${existing.status}', valid transitions are: ${allowed.join(", ")}`
@@ -40239,154 +40852,47 @@ function createTaskMiddleware(options = {}) {
40239
40852
  );
40240
40853
  if (error) return error;
40241
40854
  }
40242
- const effectiveDependencies = input.dependencies ?? existing.dependencies ?? [];
40243
- const effectiveStatus = input.status ?? existing.status;
40244
- if (effectiveStatus === "in_progress" && effectiveDependencies.length > 0) {
40245
- const dependencyError = await validateEffectiveDependencies(
40246
- store,
40247
- tenantId2,
40248
- { workspaceId: existing.workspaceId, projectId: existing.projectId },
40249
- effectiveDependencies
40250
- );
40251
- if (dependencyError) {
40252
- const trustedException = trustedProject?.projectTask && input.dependencies !== void 0 ? await validateTrustedChildDependencyWait(
40253
- store,
40254
- tenantId2,
40255
- existing,
40256
- effectiveDependencies,
40257
- trustedProject
40258
- ) : void 0;
40259
- if (trustedException === void 0) return dependencyError;
40260
- if (trustedException) return trustedException;
40261
- }
40855
+ if (input.status === existing.status) {
40856
+ return successfulTaskResponse(input.id, existing, false);
40262
40857
  }
40263
- let reconciledTask;
40264
- let reconciliationMetadata = {};
40265
- let descriptionMutated = false;
40266
- if (isAgentTask && input.description !== void 0 && !valuesEqual(input.description, existing.description)) {
40267
- await validateExecutionResult();
40268
- const reconciliation = await lifecycle.updateTaskDescriptionWithReconciliation({
40269
- tenantId: tenantId2,
40270
- taskId: input.id,
40271
- description: input.description,
40272
- actor,
40273
- threadId: trustedProject?.projectTask?.threadId ?? (trustedProject?.projectRoom ? void 0 : input.sourceId ?? rc.thread_id)
40274
- });
40275
- if (!reconciliation.success) return JSON.stringify(reconciliation);
40276
- reconciledTask = reconciliation.task;
40277
- reconciliationMetadata = { ...reconciliation };
40278
- delete reconciliationMetadata.task;
40279
- descriptionMutated = reconciliation.mutated;
40280
- if (descriptionMutated) markMutation();
40281
- }
40282
- const updates = {};
40283
- const settableFields = [
40284
- "title",
40285
- "description",
40286
- "priority",
40287
- "dueDate",
40288
- "metadata",
40289
- "parentId",
40290
- "sourceId",
40291
- "context",
40292
- "ownerId",
40293
- "result",
40294
- "failureReason",
40295
- "requireReview",
40296
- "dependencies",
40297
- "files"
40298
- ];
40299
- for (const field of settableFields) {
40300
- if (trustedProject && (field === "sourceId" || field === "context" || field === "ownerId")) continue;
40301
- if (field === "description" && reconciledTask) continue;
40302
- const requested = input[field];
40303
- const persisted = reconciledTask ?? existing;
40304
- if (requested !== void 0 && !updateValuesEqual(field, requested, persisted[field])) {
40305
- updates[field] = requested;
40306
- }
40307
- }
40308
- if (persistedStatus !== void 0 && existing.status !== persistedStatus) {
40309
- updates.status = persistedStatus;
40310
- }
40311
- if (!reconciledTask && Object.keys(updates).length > 0) {
40312
- await validateExecutionResult();
40313
- }
40314
- if (trustedProject && (input.parentId !== void 0 || input.dependencies !== void 0)) {
40315
- if (!await referencedTaskSnapshotsStillMatch(
40316
- store,
40317
- tenantId2,
40318
- { workspaceId: existing.workspaceId, projectId: existing.projectId },
40319
- trustedReferenceSnapshots
40320
- )) return trustedContextError("Parent or dependency task changed before update.");
40321
- }
40322
- if (trustedProject) {
40323
- try {
40324
- await createProjectTaskOwnerPolicy().requireCaller(trustedRosterScope(trustedProject));
40325
- } catch (error) {
40326
- return projectPolicyError(error);
40327
- }
40328
- }
40329
- const snapshot = trustedProject ? taskMutationSnapshot(existing) : void 0;
40330
- 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;
40331
- 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", {});
40332
40874
  return JSON.stringify({
40333
40875
  success: false,
40334
- error: `Failed to update task '${input.id}'`,
40335
- 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."
40336
40879
  });
40337
40880
  }
40338
- if (Object.keys(updates).length > 0) markMutation();
40339
- if (reconciledTask && Object.keys(updates).length === 0) {
40340
- return successfulTaskResponse(input.id, updated, descriptionMutated, reconciliationMetadata);
40341
- }
40342
- if (Object.keys(updates).length === 0) {
40343
- return successfulTaskResponse(input.id, updated, false);
40344
- }
40345
- const actionMap = {
40346
- pending: "pending",
40347
- in_progress: "started",
40348
- failed: "failed",
40349
- interrupted: "interrupted",
40350
- completed: "completed",
40351
- cancelled: "cancelled"
40352
- };
40353
- const workItemAction = persistedStatus ? actionMap[persistedStatus] || "updated" : "updated";
40354
- const workItemSummary = input.summary || (persistedStatus ? `Status changed to ${persistedStatus}` : void 0);
40355
- let activityWarnings;
40881
+ markMutation();
40356
40882
  try {
40357
- if (trustedProject) {
40358
- try {
40359
- await createProjectTaskOwnerPolicy().requireCaller(trustedRosterScope(trustedProject));
40360
- } catch (error) {
40361
- return projectPolicyError(error);
40362
- }
40363
- }
40364
- await workItemStore.create({
40883
+ await statusWorkItemStore.create({
40365
40884
  taskId: input.id,
40366
40885
  tenantId: tenantId2,
40367
- action: workItemAction,
40886
+ action: input.status,
40368
40887
  actor,
40369
- threadId: trustedProject?.projectTask?.threadId ?? (trustedProject?.projectRoom ? void 0 : input.sourceId),
40370
- summary: workItemSummary,
40371
- detail: {
40372
- ...input.result !== void 0 && { result: input.result },
40373
- ...input.failureReason !== void 0 && { failureReason: input.failureReason }
40374
- },
40375
- workspaceId: updated.workspaceId,
40376
- 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
40377
40892
  });
40378
- } catch (error) {
40379
- activityWarnings = [{
40380
- code: "TASK_UPDATE_ACTIVITY_WRITE_FAILED",
40381
- taskId: input.id,
40382
- error: error instanceof Error ? error.message : String(error)
40383
- }];
40893
+ } catch {
40384
40894
  }
40385
- const lifecycleWarnings = Array.isArray(reconciliationMetadata.warnings) ? reconciliationMetadata.warnings : void 0;
40386
- return successfulTaskResponse(input.id, updated, true, {
40387
- ...reconciliationMetadata,
40388
- warnings: mergeTaskUpdateWarnings(lifecycleWarnings, activityWarnings)
40389
- });
40895
+ return successfulTaskResponse(input.id, updatedStatus, true);
40390
40896
  }
40391
40897
  case "delete": {
40392
40898
  if (!input.id) {
@@ -40403,8 +40909,9 @@ function createTaskMiddleware(options = {}) {
40403
40909
  if (!existing) {
40404
40910
  return JSON.stringify({
40405
40911
  success: false,
40406
- error: `Task '${input.id}' not found`,
40407
- 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."
40408
40915
  });
40409
40916
  }
40410
40917
  if (!taskMatchesRuntimeScope3(existing, workspaceId, projectId)) {
@@ -40430,8 +40937,9 @@ function createTaskMiddleware(options = {}) {
40430
40937
  if (!deleted) {
40431
40938
  return JSON.stringify({
40432
40939
  success: false,
40433
- error: `Task '${input.id}' not found or could not be deleted`,
40434
- 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."
40435
40943
  });
40436
40944
  }
40437
40945
  return JSON.stringify({ success: true, message: `Task '${input.id}' deleted` });
@@ -40446,7 +40954,7 @@ function createTaskMiddleware(options = {}) {
40446
40954
  }
40447
40955
  };
40448
40956
  const reconcileExplicitExecutionResult = async (input, config, response, validationError, didMutate) => {
40449
- 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;
40450
40958
  const authority = classifyTaskAuthority(getRunConfig2(config));
40451
40959
  if (authority.kind === "projectRoom" || authority.kind === "projectTask" || authority.kind === "invalid") {
40452
40960
  return response;
@@ -40640,14 +41148,18 @@ ${TASK_CONVERGENCE_GUIDANCE}
40640
41148
  - Explicit ownerId: assign to a specific agent (cross-agent delegation)
40641
41149
 
40642
41150
  ## Actions
40643
- - create: Create a task (title required; result required for completed; failureReason required for failed)
41151
+ - create: Create a task (title required)
40644
41152
  - get: Get one task and its newest 100 activities (id required)
40645
41153
  - list: List tasks, filterable by ownerType/status/priority/parentId
40646
- - update: Update a task (id required; pass only the fields to change)
40647
- To mark complete: update with status='completed', result='...' (a nonblank input or existing result is required)
40648
- To mark failed: update with status='failed', failureReason='...' (a nonblank input or existing failureReason is required)
40649
- To pause for missing input: update with status='interrupted', summary='what is needed' (defaults to interruption type missing_input)
40650
- 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.
40651
41163
  - add_activity: Add a Markdown activity without changing task fields or status (id and content required)
40652
41164
  - delete: Delete a task (id required)
40653
41165
 
@@ -40827,7 +41339,12 @@ manage_eval calls in this skill and in [[eval-verify]]'s Setup step 0 \u2014
40827
41339
  [[eval-verify]] loads this skill and inherits its confirmation requirement.
40828
41340
 
40829
41341
  ## Project Structure
40830
- 1. Project: one per agent-under-test. Create with manage_eval create_project.
41342
+ 1. Project: one per agent-under-test. A NEW project is created through [[eval-verify]]
41343
+ Setup, which owns project organization: it resolves the exact subject agent-id,
41344
+ checks whether \`eval-{agent-id}\` already exists (read_eval list_projects), reuses it
41345
+ when it does, and confirms with the user before \`manage_eval create_project\`
41346
+ (which requires \`evalVerifyLoaded: true\`). Do NOT call create_project directly from
41347
+ this skill \u2014 load [[eval-verify]] to create a project.
40831
41348
  2. Suite: one per capability/domain. Create with manage_eval create_suite.
40832
41349
  3. Case: user input \u2192 agent steps \u2192 expected output \u2192 rubrics. Create with manage_eval create_case.
40833
41350
 
@@ -41120,6 +41637,15 @@ ACTIONS:
41120
41637
  }
41121
41638
  );
41122
41639
  }
41640
+ function requireEvalVerifyDeclaration(evalVerifyLoaded) {
41641
+ if (evalVerifyLoaded !== true) {
41642
+ return JSON.stringify({
41643
+ success: false,
41644
+ error: "Load the 'eval-verify' skill via the skill tool (skill_name 'eval-verify'), then retry this exact call with evalVerifyLoaded: true. eval-verify owns eval project organization: it resolves the exact subject agent-id, checks whether eval-{agent-id} already exists, reuses it when it does, and confirms with the user before creating. Never create an eval project without following it."
41645
+ });
41646
+ }
41647
+ return void 0;
41648
+ }
41123
41649
  function createManageEvalTool() {
41124
41650
  const schema6 = z71.object({
41125
41651
  action: z71.enum([
@@ -41138,6 +41664,7 @@ function createManageEvalTool() {
41138
41664
  description: z71.string().optional(),
41139
41665
  judgeModelKey: z71.string().optional(),
41140
41666
  concurrency: z71.number().optional(),
41667
+ evalVerifyLoaded: z71.boolean().optional().describe("Required on create_project: load the 'eval-verify' skill first via the skill tool (skill_name 'eval-verify'), then pass evalVerifyLoaded: true. Calls without evalVerifyLoaded: true fail. Never omit or pass false."),
41141
41668
  targetAgentId: z71.string().optional().describe("Optional for create_project \u2014 the agent this eval project verifies. Recorded in targetServerConfig so the agent's detail page can find its eval data without relying on project naming."),
41142
41669
  suiteId: z71.string().optional(),
41143
41670
  caseId: z71.string().optional(),
@@ -41163,6 +41690,8 @@ function createManageEvalTool() {
41163
41690
  let data;
41164
41691
  switch (input.action) {
41165
41692
  case "create_project": {
41693
+ const skillError = requireEvalVerifyDeclaration(input.evalVerifyLoaded);
41694
+ if (skillError) return skillError;
41166
41695
  const ctx = workspaceContext(exeConfig);
41167
41696
  data = await store.createProject(tid, uuidv48(), {
41168
41697
  name: input.name,
@@ -41243,7 +41772,12 @@ function createManageEvalTool() {
41243
41772
  name: "manage_eval",
41244
41773
  description: `Create, update, delete evaluation projects, suites, and test cases.
41245
41774
 
41246
- Project: create_project(name, description?, judgeModelKey?, concurrency?, targetAgentId?) | update_project | delete_project
41775
+ Project: create_project(name, description?, judgeModelKey?, concurrency?, targetAgentId?, evalVerifyLoaded?) | update_project | delete_project
41776
+ create_project REQUIRES evalVerifyLoaded: true \u2014 load the 'eval-verify' skill first
41777
+ (skill tool, skill_name 'eval-verify'). eval-verify owns project organization:
41778
+ resolve the exact subject agent-id, check whether eval-{agent-id} already exists via
41779
+ read_eval list_projects, reuse it when it does, confirm with the user when it does not,
41780
+ and always pass targetAgentId. Calls without evalVerifyLoaded: true fail.
41247
41781
  judgeModelKey defaults to the "default" model. concurrency defaults to 3.
41248
41782
  delete_project rejected if active runs exist.
41249
41783
  **When creating a project from within a workspace, the workspace/project context is
@@ -41598,6 +42132,10 @@ Selected target eval subjects:
41598
42132
  - Orchestra: component evals first, then integration eval
41599
42133
  - Workflow: validate_workflow first, then workflow eval covering branch paths, intermediate contracts, ask points, recovery
41600
42134
  - In Bounded mode: subject is always the exact bound target ID
42135
+ The eval project belongs to the TARGET agent, not to this round. The subject identity is
42136
+ fixed per target; across rounds and fix passes of the same target, [[eval-verify]] Setup
42137
+ resolves the canonical eval-{target-id} project and REUSES it \u2014 never create a new eval
42138
+ project for a new round of the same agent.
41601
42139
 
41602
42140
  ### 7. Deliver
41603
42141