@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.js CHANGED
@@ -3915,6 +3915,7 @@ function cloneWebApp(webApp) {
3915
3915
  },
3916
3916
  features: { ...webApp.features },
3917
3917
  appearance: { ...webApp.appearance },
3918
+ identity: webApp.identity ? { policy: webApp.identity.policy, issuers: webApp.identity.issuers.map((issuer) => ({ ...issuer })) } : void 0,
3918
3919
  createdAt: new Date(webApp.createdAt),
3919
3920
  updatedAt: new Date(webApp.updatedAt)
3920
3921
  };
@@ -3960,6 +3961,7 @@ var InMemoryAgentWebAppStore = class {
3960
3961
  },
3961
3962
  features: { ...input.features },
3962
3963
  appearance: { ...input.appearance },
3964
+ identity: input.identity ? { policy: input.identity.policy, issuers: input.identity.issuers.map((issuer) => ({ ...issuer })) } : void 0,
3963
3965
  createdAt: now,
3964
3966
  updatedAt: now
3965
3967
  };
@@ -3976,7 +3978,7 @@ var InMemoryAgentWebAppStore = class {
3976
3978
  const existing = tenantWebApps?.get(webAppId);
3977
3979
  if (!tenantWebApps || !existing) return null;
3978
3980
  if (options?.expectedUpdatedAt && existing.updatedAt.getTime() !== options.expectedUpdatedAt.getTime()) return null;
3979
- 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;
3981
+ 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;
3980
3982
  if (!hasUpdate) return cloneWebApp(existing);
3981
3983
  const updated = {
3982
3984
  ...existing,
@@ -3991,6 +3993,7 @@ var InMemoryAgentWebAppStore = class {
3991
3993
  } : {},
3992
3994
  ...patch.features ? { features: { ...patch.features } } : {},
3993
3995
  ...patch.appearance ? { appearance: { ...patch.appearance } } : {},
3996
+ ...patch.identity !== void 0 ? { identity: { policy: patch.identity.policy, issuers: patch.identity.issuers.map((issuer) => ({ ...issuer })) } } : {},
3994
3997
  ...patch.status !== void 0 ? { status: patch.status } : {},
3995
3998
  updatedAt: new Date(Math.max(Date.now(), existing.updatedAt.getTime() + 1))
3996
3999
  };
@@ -9174,7 +9177,8 @@ var VolumeFilesystem = class {
9174
9177
  path: entry.path.startsWith("/") ? entry.path : prefix + entry.path,
9175
9178
  is_dir: entry.kind === "directory",
9176
9179
  size: entry.size,
9177
- modified_at: entry.modified ? new Date(entry.modified).toISOString() : void 0
9180
+ modified_at: entry.modified ? new Date(entry.modified).toISOString() : void 0,
9181
+ created_at: entry.created ? new Date(entry.created).toISOString() : void 0
9178
9182
  }));
9179
9183
  }
9180
9184
  async read(filePath, offset = 0, limit = 2e3) {
@@ -14183,6 +14187,18 @@ async function createCommonMiddlewares(middlewareConfigs, filesystemBackend, fsI
14183
14187
 
14184
14188
  // src/deep_agent_new/backends/sandboxFiles.ts
14185
14189
  var path3 = __toESM(require("path"));
14190
+ var import_micromatch2 = __toESM(require("micromatch"));
14191
+ function matchesGlobFilter(matchPath, searchPath, glob) {
14192
+ if (!glob || glob === "**/*") {
14193
+ return true;
14194
+ }
14195
+ if (!glob.includes("/")) {
14196
+ return import_micromatch2.default.isMatch(path3.basename(matchPath), glob, { dot: true });
14197
+ }
14198
+ const base = searchPath.endsWith("/") ? searchPath : `${searchPath}/`;
14199
+ const relative4 = matchPath.startsWith(base) ? matchPath.slice(base.length) : matchPath;
14200
+ return import_micromatch2.default.isMatch(relative4, glob, { dot: true });
14201
+ }
14186
14202
  var SandboxFilesystem = class {
14187
14203
  constructor(options = {}) {
14188
14204
  const { maxFileSizeMb = 10, sandboxInstance } = options;
@@ -14309,6 +14325,16 @@ var SandboxFilesystem = class {
14309
14325
  }
14310
14326
  async grepRaw(pattern, searchPath = "/", glob = null) {
14311
14327
  try {
14328
+ if (this.sandbox.file.grepRecursive) {
14329
+ const result2 = await this.sandbox.file.grepRecursive(
14330
+ pattern,
14331
+ searchPath || "/"
14332
+ );
14333
+ const globFilter = glob || "**/*";
14334
+ return result2.matches.filter(
14335
+ (match) => matchesGlobFilter(match.path, searchPath || "/", globFilter)
14336
+ );
14337
+ }
14312
14338
  const result = await this.sandbox.file.findFiles(searchPath || "/", glob || "**/*");
14313
14339
  const filePaths = result.files;
14314
14340
  const matches = [];
@@ -20442,7 +20468,7 @@ var fsSync = __toESM(require("fs"));
20442
20468
  var path4 = __toESM(require("path"));
20443
20469
  var import_child_process = require("child_process");
20444
20470
  var import_fast_glob = __toESM(require("fast-glob"));
20445
- var import_micromatch2 = __toESM(require("micromatch"));
20471
+ var import_micromatch3 = __toESM(require("micromatch"));
20446
20472
  var SUPPORTS_NOFOLLOW = fsSync.constants.O_NOFOLLOW !== void 0;
20447
20473
  var FilesystemBackend = class {
20448
20474
  constructor(options = {}) {
@@ -20943,7 +20969,7 @@ var FilesystemBackend = class {
20943
20969
  });
20944
20970
  for (const fp of files) {
20945
20971
  try {
20946
- if (includeGlob && !import_micromatch2.default.isMatch(path4.basename(fp), includeGlob)) {
20972
+ if (includeGlob && !import_micromatch3.default.isMatch(path4.basename(fp), includeGlob)) {
20947
20973
  continue;
20948
20974
  }
20949
20975
  const stat5 = await fs2.stat(fp);
@@ -24452,8 +24478,12 @@ Otherwise, the owning caller interprets the returned observation.
24452
24478
  An execution result is an observation, not automatic proof of business
24453
24479
  completion. \`task\` returns its exact \`executionResultId\`. After \`task\`
24454
24480
  returns, inspect the observation, then pass that \`executionResultId\` to the
24455
- meaningful \`manage_task update\` or \`manage_task add_activity\` that interprets
24456
- it and reconciles status, belief, plan, and next action. Omitting
24481
+ meaningful \`manage_task set_status\` (status transition), \`manage_task
24482
+ update\` (content fields), or \`manage_task add_activity\` that interprets
24483
+ it and reconciles status, belief, plan, and next action. Action split:
24484
+ \`set_status\` owns status transitions (with result/failureReason/summary and
24485
+ beliefImpact on completion); \`update\` owns content fields (title,
24486
+ description, priority, dependencies, ...); the two never mix in one call. Omitting
24457
24487
  \`executionResultId\` leaves the result pending so forgotten handling remains
24458
24488
  detectable. Reconciliation does not require the task to become \`completed\`;
24459
24489
  choose the truthful status. \`manage_task get\` exposes pending execution-result
@@ -24473,13 +24503,14 @@ independently.
24473
24503
  6. **Reconcile** - revise Belief, Task, plan, and next action on Prediction Error,
24474
24504
  then repeat only while a decision-relevant action remains and stop at honest convergence.
24475
24505
 
24476
- Converge using existing statuses:
24506
+ Converge through \`set_status\` using existing statuses:
24477
24507
 
24478
- - \`completed\`: acceptance is satisfied by evidence; include a \`result\`.
24508
+ - \`completed\`: acceptance is satisfied by evidence; \`set_status\` with a
24509
+ \`result\` and structured \`beliefImpact\`.
24479
24510
  - \`in_progress\`: a feasible, decision-relevant action remains.
24480
24511
  - \`pending\`: work is future or dependency-blocked.
24481
24512
  - \`interrupted\`: an external condition is missing; record the condition needed to resume.
24482
- - \`failed\`: no reasonable path remains; include a \`failureReason\`.
24513
+ - \`failed\`: no reasonable path remains; \`set_status\` with a \`failureReason\`.
24483
24514
  - \`cancelled\`: the goal or subgoal is no longer needed.
24484
24515
 
24485
24516
  For human judgment, use the actual HITL payload \`status: "interrupted"\` plus
@@ -25769,6 +25800,35 @@ Renew HITL approval when a foundational assumption or authority boundary changes
25769
25800
 
25770
25801
  **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.
25771
25802
 
25803
+ ## Skill Loading Policy
25804
+
25805
+ Visible in <available_skills> does not mean loaded. Follow these three tiers.
25806
+
25807
+ ### Tier 1 \u2014 Session start (mandatory, unconditional)
25808
+ Load before any other work; the bootstrap prompt enforces this:
25809
+ - [[agent-architecture]] (this skill) \u2014 shared policy and routing.
25810
+ - [[task-tracking]] \u2014 before your first \`manage_task\` call; Task management is
25811
+ the planning surface and is gated again under Architect Task Policy.
25812
+
25813
+ ### Tier 2 \u2014 Scenario gates (mandatory before entering the scenario)
25814
+ Load the owning skill with the \`skill\` tool BEFORE the first action of that
25815
+ scenario. Never execute a scenario without its owning skill loaded:
25816
+ - Creating or modifying a Capability (\`react\`) or Orchestra (\`deep_agent\`)
25817
+ Agent \u2192 [[agent-build]]
25818
+ - Creating or modifying a Workflow (stable enumerable pipelines) \u2192 [[design-workflow]]
25819
+ - Learning a capability from source material \u2192 [[document-learning-learn-capability]]
25820
+ - Designing evals, running evals, or fixing eval failures \u2192 [[eval-verify]]
25821
+ - Claiming work is complete or delivered \u2192 [[completion-gate]]
25822
+ - Final configuration pre-check before delivery \u2192 [[review-agent]]
25823
+
25824
+ If a Tier 2 load fails, retry once, then stop and tell the user which
25825
+ capability is unavailable. Do not improvise the scenario from partial knowledge.
25826
+
25827
+ ### Tier 3 \u2014 Reference (load on demand, never preload)
25828
+ Deep details reachable through [[links]] inside loaded skills, and plugin
25829
+ skills outside this chain (for example task-definition for non-architect
25830
+ agents). Load one only when the current step actually needs it.
25831
+
25772
25832
  ## Agent Forms and Routing
25773
25833
  - **Capability** (\`react\`) for bounded direct work: [[agent-build]]
25774
25834
  - **Orchestra** (\`deep_agent\`) for open-ended delegated work: [[agent-build]]
@@ -25781,8 +25841,11 @@ Renew HITL approval when a foundational assumption or authority boundary changes
25781
25841
  ## Architect Task Policy
25782
25842
  The Agent Architect uses persistent TaskItems as its planning surface. Architect must not use
25783
25843
  \`write_todos\`. Create a persistent child only for an independently verifiable
25784
- outcome; tool calls and file reads are actions inside the active Task. Load
25785
- [[task-tracking]] for operation and status details.
25844
+ outcome; tool calls and file reads are actions inside the active Task.
25845
+ **MANDATORY: before your first \`manage_task\` call in this session, load
25846
+ [[task-tracking]] with the \`skill\` tool.** Do not create, update, or transition
25847
+ any Task until it is loaded. If the load fails, retry once, then tell the user
25848
+ task management is unavailable.
25786
25849
  Learning authority is **normal** or **bounded**: normal for fresh work without a
25787
25850
  bound round; bounded for learning rounds that bind an existing target and
25788
25851
  tracking Task with a confirmed goal. Identity remains fixed; materialization uses
@@ -26011,6 +26074,9 @@ ${TASK_BELIEF_REFERENCE_EXAMPLE}
26011
26074
 
26012
26075
  ## Status discipline \u2014 MANDATORY
26013
26076
 
26077
+ - All status transitions go through \`manage_task set_status\` (with
26078
+ \`result\` on completed, \`failureReason\` on failed). \`update\` is for
26079
+ content fields only and rejects a status field.
26014
26080
  - A subtask is \`completed\` ONLY when its work is actually finished.
26015
26081
  While fixing eval failures, keep it \`in_progress\` \u2014 never mark
26016
26082
  \`completed\` as a workaround (the state machine rejects illegal
@@ -26255,12 +26321,18 @@ flows the decomposition is presented for user confirmation first.
26255
26321
  **3. Create each sub-agent.** After [[agent-architecture]] is loaded, per sub-agent:
26256
26322
  Call \`create_agent(type: "react", skillLoaded: true, skills: ["[[skill-name]]", ...)\`
26257
26323
  where skillLoaded: true is set only after [[agent-architecture]] is loaded.
26258
- - Each sub-agent gets its OWN eval project: \`manage_eval create_project(name: "eval-{{sub-agent-id}}")\`
26324
+ - Each sub-agent gets its OWN eval project (eval-{sub-agent-id}); per [[eval-verify]]
26325
+ Setup, first resolve the sub-agent's exact id and check whether its project already
26326
+ exists \u2014 reuse it when it does; only create (and confirm) when it does not. Never
26327
+ create a duplicate project for an agent that already has one.
26259
26328
  - Design eval cases per [[eval-design-tests]], run per [[eval-verify]]
26260
26329
  - Sub-agent eval passes before you proceed to the next sub-agent
26261
26330
 
26262
26331
  **4. Integration eval.** After ALL sub-agents pass their evals:
26263
- - Create integration eval project: \`manage_eval create_project(name: "eval-{{parent-id}}")\`
26332
+ - The integration project for the parent (eval-{parent-id}) goes through the same
26333
+ [[eval-verify]] Setup check: confirm the parent's project does not already exist,
26334
+ present the proposed creation to the user, then
26335
+ \`manage_eval create_project(name: "eval-{parent-id}", targetAgentId: "{parent-id}", evalVerifyLoaded: true)\`.
26264
26336
  - Integration eval tests the parent's delegation logic, not sub-agent correctness
26265
26337
  - [[eval-verify]] owns the run
26266
26338
 
@@ -26493,14 +26565,39 @@ its evidence projection, not an independent source of goals or expectations.
26493
26565
  before calling manage_eval") apply to every manage_eval call in this skill \u2014 not
26494
26566
  just during the case design phase. Do not call manage_eval without presenting the
26495
26567
  change first.
26496
- 1. **One eval project per agent**, named \`eval-{agent-id}\`:
26497
- \`read_eval list_projects\` \u2192 find "eval-{agent-id}"
26498
- Exists \u2192 reuse projectId. New \u2192 \`manage_eval create_project(name: "eval-{agent-id}")\`
26499
- - The agent-id is the eval project's subject. Observability: from an
26500
- agent's id you can find its eval project by naming convention.
26568
+ 1. **Resolve the subject agent FIRST \u2014 one eval project per agent, never per task or per round.**
26569
+ An eval project belongs to exactly one agent-under-test, named \`eval-{agent-id}\`.
26570
+ The agent-id is the eval project's subject. Observability: from an agent's id you
26571
+ can find its eval project by naming convention. Before ANY project creation you MUST:
26572
+ - **Pin the exact subject agent-id.** The subject is the materialized target agent
26573
+ being evaluated: the id returned by \`create_agent\` / \`update_agent\` /
26574
+ \`create_workflow\` / \`update_workflow\`, or the exact bound target ID pinned by
26575
+ the learning kickoff / round task. It is NEVER the Architect session id, the
26576
+ round/task id, a skill name, or any invented label.
26577
+ - **If the exact agent-id is unknown or unconfirmed \u2014 STOP and ask.** Do NOT guess a
26578
+ name, do NOT create a project, and do NOT proceed to case design. Present which
26579
+ agent is under test and confirm with the user (\`ask_user_to_clarify\`) before
26580
+ touching any eval project. An eval project created without a real, confirmed
26581
+ subject is a hard error. If the agent is only planned (not yet materialized),
26582
+ evaluation must wait until materialization returns its id.
26583
+ - **Check existence before creating; reuse is the rule, creation is the exception.**
26584
+ 1) \`read_eval list_projects\`
26585
+ 2) Find the project whose \`targetAgentId\` equals the subject agent-id; fall back
26586
+ to the name \`eval-{agent-id}\` only when no pointer exists (legacy projects).
26587
+ 3) Exists \u2192 reuse its projectId. NEVER create a second project for the same agent \u2014
26588
+ project names are unique per tenant and duplicate projects are forbidden.
26589
+ 4) Not exists \u2192 present the proposed project (name \`eval-{agent-id}\` +
26590
+ \`targetAgentId\`) and confirm with the user, THEN
26591
+ \`manage_eval create_project(name: "eval-{agent-id}", targetAgentId: "{agent-id}", evalVerifyLoaded: true, ...)\`.
26592
+ The tool refuses create_project without \`evalVerifyLoaded: true\` \u2014 that flag
26593
+ confirms you are following THIS skill's Setup (resolve \u2192 check \u2192 reuse/confirm).
26594
+ Always pass \`targetAgentId\` so the agent\u2194project link is structural, not name-only.
26595
+ - Re-resolve on every round and every fix pass of the same agent. A new round or task
26596
+ is NOT a reason to create a new project \u2014 reuse the agent's canonical project.
26501
26597
  - Orchestrator + subAgents \u2192 one eval project per sub-agent
26502
26598
  (eval-{sub-agent-id}) PLUS one integration eval project for the
26503
- parent (eval-{parent-id}) \u2014 see Layered verification below.
26599
+ parent (eval-{parent-id}) \u2014 see Layered verification below. Every project goes
26600
+ through the same resolve-then-confirm-then-create check above.
26504
26601
  2. \`manage_eval create_case(suiteId, ...)\` per sample or scenario
26505
26602
  Required: inputMessage, steps=[{agent_id}], outputType, contentAssertion
26506
26603
  **contentAssertion MUST come from the confirmed goal, approved expected output,
@@ -27508,11 +27605,11 @@ function isUndecidedLearningPlaceholder(config) {
27508
27605
  const metadata = getStringMetadata(config);
27509
27606
  return config.type === import_protocols17.AgentType.REACT && metadata.learningPlaceholder === "true" && metadata.architectureStatus === "undecided";
27510
27607
  }
27511
- function requireArchitectSkill(skillLoaded, exeConfig) {
27512
- if (exeConfig?.configurable?.runConfig?.assistant_id === "agent-architect" && skillLoaded !== true) {
27608
+ function requireSkillDeclaration(skillLoaded) {
27609
+ if (skillLoaded !== true) {
27513
27610
  return JSON.stringify({
27514
27611
  success: false,
27515
- error: "Load the 'agent-architecture' skill before creating or updating an agent."
27612
+ error: "Load the 'agent-architecture' skill via the skill tool (skill_name 'agent-architecture'), then retry this exact call with skillLoaded: true."
27516
27613
  });
27517
27614
  }
27518
27615
  return void 0;
@@ -27625,7 +27722,7 @@ var middlewareConfigSchema = import_zod47.default.object({
27625
27722
  config: import_zod47.default.record(import_zod47.default.any()).optional()
27626
27723
  });
27627
27724
  var createAgentSchema = import_zod47.default.object({
27628
- skillLoaded: import_zod47.default.literal(true).optional().describe("For agent-architect, set true only after loading agent-architecture in the current conversation context."),
27725
+ skillLoaded: import_zod47.default.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."),
27629
27726
  name: import_zod47.default.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')."),
27630
27727
  description: import_zod47.default.string().optional().describe("Short description"),
27631
27728
  type: import_zod47.default.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."),
@@ -27646,9 +27743,9 @@ registerToolLattice(
27646
27743
  },
27647
27744
  async (input, exeConfig) => {
27648
27745
  try {
27649
- const tenantId2 = getTenantId2(exeConfig);
27650
- const skillError = requireArchitectSkill(input.skillLoaded, exeConfig);
27746
+ const skillError = requireSkillDeclaration(input.skillLoaded);
27651
27747
  if (skillError) return skillError;
27748
+ const tenantId2 = getTenantId2(exeConfig);
27652
27749
  const store = getAssistStore();
27653
27750
  const taskConfigIssue = validateTaskMiddlewareConfig(input.middleware);
27654
27751
  if (taskConfigIssue) return JSON.stringify(taskConfigIssue);
@@ -27683,7 +27780,7 @@ registerToolLattice(
27683
27780
  var createWorkflowSchema = import_zod47.default.object({
27684
27781
  name: import_zod47.default.string().describe("Display name for the workflow agent"),
27685
27782
  description: import_zod47.default.string().optional().describe("Short description"),
27686
- skillLoaded: import_zod47.default.literal(true).describe("MUST be true. Set after loading the 'create-workflow' skill."),
27783
+ skillLoaded: import_zod47.default.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."),
27687
27784
  yaml: import_zod47.default.string().describe("The YAML workflow definition in linear DSL format (steps execute top-to-bottom, use parallel: for concurrency)"),
27688
27785
  tools: import_zod47.default.array(import_zod47.default.string()).optional().describe("Tool keys for the workflow agent"),
27689
27786
  middleware: import_zod47.default.array(middlewareConfigSchema).optional().describe("Middleware configs"),
@@ -27698,6 +27795,8 @@ registerToolLattice(
27698
27795
  },
27699
27796
  async (input, exeConfig) => {
27700
27797
  try {
27798
+ const skillError = requireSkillDeclaration(input.skillLoaded);
27799
+ if (skillError) return skillError;
27701
27800
  const tenantId2 = getTenantId2(exeConfig);
27702
27801
  const store = getAssistStore();
27703
27802
  const taskConfigIssue = validateTaskMiddlewareConfig(input.middleware);
@@ -27838,7 +27937,7 @@ registerToolLattice(
27838
27937
  );
27839
27938
  var updateWorkflowSchema = import_zod47.default.object({
27840
27939
  id: import_zod47.default.string().describe("The workflow agent ID to update"),
27841
- skillLoaded: import_zod47.default.literal(true).optional().describe("For agent-architect, set true only after loading agent-architecture in the current conversation context."),
27940
+ skillLoaded: import_zod47.default.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."),
27842
27941
  name: import_zod47.default.string().optional().describe("New display name"),
27843
27942
  description: import_zod47.default.string().optional().describe("New description"),
27844
27943
  yaml: import_zod47.default.string().optional().describe("Replacement YAML workflow DSL. Omit to keep existing."),
@@ -27850,13 +27949,13 @@ registerToolLattice(
27850
27949
  "update_workflow",
27851
27950
  {
27852
27951
  name: "update_workflow",
27853
- 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.",
27952
+ 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.",
27854
27953
  schema: updateWorkflowSchema
27855
27954
  },
27856
27955
  async (input, exeConfig) => {
27857
27956
  console.log(`[update_workflow] CALLED id=${input.id} hasYaml=${input.yaml !== void 0}`);
27858
27957
  try {
27859
- const skillError = requireArchitectSkill(input.skillLoaded, exeConfig);
27958
+ const skillError = requireSkillDeclaration(input.skillLoaded);
27860
27959
  if (skillError) return skillError;
27861
27960
  const tenantId2 = getTenantId2(exeConfig);
27862
27961
  const store = getAssistStore();
@@ -27934,7 +28033,7 @@ registerToolLattice(
27934
28033
  description: input.description !== void 0 ? input.description : existing.description,
27935
28034
  graphDefinition: mergedConfig
27936
28035
  });
27937
- if (isPlaceholder && updated === null) {
28036
+ if (updated === null) {
27938
28037
  return JSON.stringify({
27939
28038
  success: false,
27940
28039
  code: "ASSISTANT_UPDATE_FAILED",
@@ -27952,7 +28051,7 @@ registerToolLattice(
27952
28051
  );
27953
28052
  var updateAgentSchema = import_zod47.default.object({
27954
28053
  id: import_zod47.default.string().describe("The agent ID to update"),
27955
- skillLoaded: import_zod47.default.literal(true).optional().describe("For agent-architect, set true only after loading agent-architecture in the current conversation context."),
28054
+ skillLoaded: import_zod47.default.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."),
27956
28055
  config: import_zod47.default.object({
27957
28056
  name: import_zod47.default.string().optional().describe("New display name for the agent"),
27958
28057
  description: import_zod47.default.string().optional().describe("New short description"),
@@ -27970,14 +28069,14 @@ registerToolLattice(
27970
28069
  "update_agent",
27971
28070
  {
27972
28071
  name: "update_agent",
27973
- 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.",
28072
+ 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.",
27974
28073
  schema: updateAgentSchema
27975
28074
  },
27976
28075
  async (input, exeConfig) => {
27977
28076
  try {
27978
- const tenantId2 = getTenantId2(exeConfig);
27979
- const skillError = requireArchitectSkill(input.skillLoaded, exeConfig);
28077
+ const skillError = requireSkillDeclaration(input.skillLoaded);
27980
28078
  if (skillError) return skillError;
28079
+ const tenantId2 = getTenantId2(exeConfig);
27981
28080
  const store = getAssistStore();
27982
28081
  const existing = await store.getAssistantById(tenantId2, input.id);
27983
28082
  if (!existing) {
@@ -28017,7 +28116,7 @@ registerToolLattice(
28017
28116
  description: input.config.description !== void 0 ? input.config.description : existing.description,
28018
28117
  graphDefinition: mergedConfig
28019
28118
  });
28020
- if (isMaterializingPlaceholder && updated === null) {
28119
+ if (updated === null) {
28021
28120
  return JSON.stringify({
28022
28121
  success: false,
28023
28122
  code: "ASSISTANT_UPDATE_FAILED",
@@ -28246,8 +28345,11 @@ var AGENT_ARCHITECT_PROMPT = `You are the Agent Architect.
28246
28345
 
28247
28346
  CRITICAL FIRST ACTION: Before any response or other action, call the \`skill\`
28248
28347
  tool with skill_name: "agent-architecture" to load agent-architecture and follow it.
28249
- Never announce the skill load. If it fails, retry once, then stop and explicitly
28250
- report that the required Agent Architect skill could not be loaded.`;
28348
+ Immediately after it loads, call the \`skill\` tool again with skill_name:
28349
+ "task-tracking" \u2014 persistent Task management is your core planning surface and
28350
+ you must have its operation reference before creating or updating any Task.
28351
+ Never announce skill loads. If a load fails, retry once, then stop and
28352
+ explicitly report that the required skill could not be loaded.`;
28251
28353
 
28252
28354
  // src/agent_lattice/agentReviewerConfig.ts
28253
28355
  var import_protocols18 = require("@axiom-lattice/protocols");
@@ -30239,6 +30341,25 @@ var mcpManager = McpLatticeManager.getInstance();
30239
30341
  // src/sandbox_lattice/MicrosandboxRemoteInstance.ts
30240
30342
  var posix3 = __toESM(require("path/posix"));
30241
30343
 
30344
+ // src/sandbox_lattice/grepUtils.ts
30345
+ function buildRecursiveGrepCommand(pattern, path8) {
30346
+ return `grep -r -n -I -E -e ${quotePosixShellArg(pattern)} ${quotePosixShellArg(path8)}`;
30347
+ }
30348
+ function parseRecursiveGrepOutput(output) {
30349
+ const matches = [];
30350
+ for (const line of output.split("\n")) {
30351
+ if (!line) continue;
30352
+ const match = /^(.*):(\d+):(.*)$/.exec(line);
30353
+ if (!match) continue;
30354
+ matches.push({
30355
+ path: match[1],
30356
+ line: Number.parseInt(match[2], 10),
30357
+ text: match[3]
30358
+ });
30359
+ }
30360
+ return matches;
30361
+ }
30362
+
30242
30363
  // src/sandbox_lattice/packDirectory.ts
30243
30364
  var import_node_crypto4 = require("crypto");
30244
30365
  var posix2 = __toESM(require("path/posix"));
@@ -30374,6 +30495,15 @@ var MicrosandboxRemoteInstance = class {
30374
30495
  line_numbers: result.matches.map((match) => match.line)
30375
30496
  };
30376
30497
  },
30498
+ grepRecursive: async (pattern, searchPath) => {
30499
+ const result = await this.shell.execCommand({
30500
+ command: buildRecursiveGrepCommand(
30501
+ pattern,
30502
+ normalizeExternalSandboxPath(searchPath)
30503
+ )
30504
+ });
30505
+ return { matches: parseRecursiveGrepOutput(result.output) };
30506
+ },
30377
30507
  strReplaceEditor: async (params) => {
30378
30508
  await this.client.replaceInFile(this.name, {
30379
30509
  path: normalizeExternalSandboxPath(params.path),
@@ -30906,6 +31036,13 @@ var RemoteSandboxInstance = class {
30906
31036
  line_numbers: result.body.data?.line_numbers || []
30907
31037
  };
30908
31038
  },
31039
+ grepRecursive: async (pattern, searchPath) => {
31040
+ const resolved = this.resolvePath(searchPath);
31041
+ const result = await this.shell.execCommand({
31042
+ command: buildRecursiveGrepCommand(pattern, resolved)
31043
+ });
31044
+ return { matches: parseRecursiveGrepOutput(result.output) };
31045
+ },
30909
31046
  strReplaceEditor: async (params) => {
30910
31047
  const resolved = this.resolvePath(params.path);
30911
31048
  const result = await this.client.file.strReplaceEditor({
@@ -31209,7 +31346,8 @@ var RemoteSandboxProvider = class {
31209
31346
  kind: f.is_dir === true || f.size === null ? "directory" : "file",
31210
31347
  size: f.size ?? 0,
31211
31348
  mode: 0,
31212
- modified: f.modified_at ?? null
31349
+ modified: f.modified_at ?? null,
31350
+ created: f.created_at ?? null
31213
31351
  }));
31214
31352
  return entries;
31215
31353
  },
@@ -31285,6 +31423,12 @@ var E2BInstance = class {
31285
31423
  }
31286
31424
  return { matches, line_numbers };
31287
31425
  },
31426
+ grepRecursive: async (pattern, searchPath) => {
31427
+ const result = await this.native.commands.run(
31428
+ buildRecursiveGrepCommand(pattern, searchPath)
31429
+ );
31430
+ return { matches: parseRecursiveGrepOutput(result.stdout) };
31431
+ },
31288
31432
  strReplaceEditor: async (params) => {
31289
31433
  const { path: path8, old_str, new_str, replace_mode } = params;
31290
31434
  const delim = "#";
@@ -31483,6 +31627,21 @@ var DaytonaInstance = class {
31483
31627
  }
31484
31628
  return { matches: matchTexts, line_numbers };
31485
31629
  },
31630
+ grepRecursive: async (pattern, searchPath) => {
31631
+ const matches = await this.native.fs.findFiles(
31632
+ toRelativePath(searchPath),
31633
+ pattern
31634
+ );
31635
+ const results = [];
31636
+ for (const match of matches) {
31637
+ results.push({
31638
+ path: normalizeExternalSandboxPath(match.file ?? ""),
31639
+ line: match.line ?? 0,
31640
+ text: match.content ?? ""
31641
+ });
31642
+ }
31643
+ return { matches: results };
31644
+ },
31486
31645
  strReplaceEditor: async (params) => {
31487
31646
  const { path: path8, old_str, new_str, replace_mode } = params;
31488
31647
  const relPath = toRelativePath(path8);
@@ -31865,6 +32024,37 @@ var LocalSandboxInstance = class {
31865
32024
  }
31866
32025
  return { matches, line_numbers };
31867
32026
  },
32027
+ grepRecursive: async (pattern, searchPath) => {
32028
+ const hp = this.hostPath(searchPath);
32029
+ let regex;
32030
+ try {
32031
+ regex = new RegExp(pattern);
32032
+ } catch {
32033
+ return { matches: [] };
32034
+ }
32035
+ const hostFiles = [];
32036
+ await this.walkDirFilter(hp, /.+/, hostFiles);
32037
+ const hpNorm = hp.endsWith(path5.sep) ? hp : hp + path5.sep;
32038
+ const toSandboxPath = (hostFilePath) => posix5.join(searchPath, hostFilePath.slice(hpNorm.length).split(path5.sep).join("/"));
32039
+ const matches = [];
32040
+ for (const hostFile of hostFiles) {
32041
+ const content = await fs3.readFile(hostFile, "utf-8").catch(() => null);
32042
+ if (content === null || content.includes("\0")) {
32043
+ continue;
32044
+ }
32045
+ const lines = content.split("\n");
32046
+ for (let i = 0; i < lines.length; i++) {
32047
+ if (regex.test(lines[i])) {
32048
+ matches.push({
32049
+ path: toSandboxPath(hostFile),
32050
+ line: i + 1,
32051
+ text: lines[i]
32052
+ });
32053
+ }
32054
+ }
32055
+ }
32056
+ return { matches };
32057
+ },
31868
32058
  strReplaceEditor: async (params) => {
31869
32059
  const content = await fs3.readFile(this.hostPath(params.path), "utf-8");
31870
32060
  let result;
@@ -32904,8 +33094,12 @@ Note: if final_score >= 80 and there are no fatal errors, pass should be true; o
32904
33094
  });
32905
33095
  }
32906
33096
  } else if (parsedResult.pass !== void 0) {
32907
- pass = parsedResult.pass;
32908
- this.log("Pass determined from parsedResult.pass", { case_id: evalCase.caseId, pass });
33097
+ pass = false;
33098
+ parsedResult.final_score = 0;
33099
+ this.log("Pass without final_score \u2014 cannot verify threshold, defaulting to FAIL", {
33100
+ case_id: evalCase.caseId,
33101
+ pass_field: parsedResult.pass
33102
+ });
32909
33103
  } else if (parsedResult.final_score !== void 0) {
32910
33104
  pass = parsedResult.final_score >= 80;
32911
33105
  this.log("Pass determined from parsedResult.final_score", {
@@ -34653,7 +34847,11 @@ var TaskLifecycleService = class {
34653
34847
  ...event2.warning ? { warnings: [event2.warning] } : {}
34654
34848
  };
34655
34849
  }
34656
- if (task.status !== "interrupted") return failure2("TASK_STATUS_CONFLICT", "Task is not interrupted.");
34850
+ if (task.status !== "interrupted") return failure2(
34851
+ "TASK_STATUS_CONFLICT",
34852
+ `Task is in '${task.status}' status, not interrupted.`,
34853
+ "Only interrupted tasks can resume to in_progress."
34854
+ );
34657
34855
  if (interruption && typeof interruption === "object" && interruption.type === "review_required") {
34658
34856
  return failure2("REVIEW_APPROVAL_REQUIRED", "Review interruption requires explicit approval or rejection.");
34659
34857
  }
@@ -34750,7 +34948,11 @@ var TaskLifecycleService = class {
34750
34948
  task.updatedAt
34751
34949
  );
34752
34950
  } catch {
34753
- return { ...failure2("TASK_STORE_WRITE_FAILED", "Failed to update task lifecycle state."), transitioned: false };
34951
+ return { ...failure2(
34952
+ "TASK_STORE_WRITE_FAILED",
34953
+ "Failed to update task lifecycle state.",
34954
+ "The store rejected the write; this may be transient. Retry the same set_status."
34955
+ ), transitioned: false };
34754
34956
  }
34755
34957
  if (!updated) {
34756
34958
  let current;
@@ -34778,7 +34980,11 @@ var TaskLifecycleService = class {
34778
34980
  ...repaired.warning ? { warnings: [repaired.warning] } : {}
34779
34981
  };
34780
34982
  }
34781
- return { ...failure2("TASK_STATUS_CONFLICT", "Task changed concurrently."), transitioned: false };
34983
+ return { ...failure2(
34984
+ "TASK_STATUS_CONFLICT",
34985
+ "Task changed concurrently.",
34986
+ "Another writer modified the task between read and write. Use manage_task get to re-read its current state, then retry."
34987
+ ), transitioned: false };
34782
34988
  }
34783
34989
  const event = await this.writeStatusEvent(
34784
34990
  updated,
@@ -34922,10 +35128,18 @@ var TaskLifecycleService = class {
34922
35128
  const storedExternal = parseExternalDependency(currentInterruption);
34923
35129
  if (input.type === "external_dependency") {
34924
35130
  if (!external || !storedExternal || storedExternal.summary !== external.summary || canonicalJson(storedExternal.dependencyTaskIds) !== canonicalJson(external.dependencyTaskIds)) {
34925
- return failure2("TASK_STATUS_CONFLICT", "Task is interrupted by a different or malformed external dependency.");
35131
+ return failure2(
35132
+ "TASK_STATUS_CONFLICT",
35133
+ "Task is already interrupted by a different or malformed external dependency.",
35134
+ "Use manage_task get to inspect the existing interruption in task context.interruption."
35135
+ );
34926
35136
  }
34927
35137
  } else if (currentInterruption.type !== input.type || currentInterruption.summary !== summary) {
34928
- return failure2("TASK_STATUS_CONFLICT", "Task is interrupted by a different request.");
35138
+ return failure2(
35139
+ "TASK_STATUS_CONFLICT",
35140
+ "Task is already interrupted by a different request.",
35141
+ "Use manage_task get to inspect the existing interruption, or resume it first."
35142
+ );
34929
35143
  }
34930
35144
  const detail = storedExternal ? { ...storedExternal } : currentInterruption;
34931
35145
  const eventKey2 = this.statusEventKey(task, "interrupted", detail);
@@ -34942,7 +35156,11 @@ var TaskLifecycleService = class {
34942
35156
  ...warnings2.length ? { warnings: warnings2 } : {}
34943
35157
  };
34944
35158
  }
34945
- if (task.status !== "in_progress") return failure2("TASK_STATUS_CONFLICT", "Task is not in progress.");
35159
+ if (task.status !== "in_progress") return failure2(
35160
+ "TASK_STATUS_CONFLICT",
35161
+ `Task is in '${task.status}' status, not in progress.`,
35162
+ "Only in_progress tasks can be interrupted. Use manage_task get to check the current status."
35163
+ );
34946
35164
  if (external && isProjectLifecycleTask(task)) {
34947
35165
  const dependencyFailure = await this.validateExternalDependencies(task, external.dependencyTaskIds);
34948
35166
  if (dependencyFailure) return dependencyFailure;
@@ -34955,9 +35173,17 @@ var TaskLifecycleService = class {
34955
35173
  context: mergeInterruptionContext(task.context, interruption, input.threadId)
34956
35174
  }, ["in_progress"], task.updatedAt);
34957
35175
  } catch {
34958
- return failure2("TASK_STORE_WRITE_FAILED", "Failed to interrupt the task.");
35176
+ return failure2(
35177
+ "TASK_STORE_WRITE_FAILED",
35178
+ "Failed to interrupt the task.",
35179
+ "The store rejected the write; this may be transient. Retry the same set_status."
35180
+ );
34959
35181
  }
34960
- if (!updated) return failure2("TASK_STATUS_CONFLICT", "Task changed concurrently.");
35182
+ if (!updated) return failure2(
35183
+ "TASK_STATUS_CONFLICT",
35184
+ "Task changed concurrently.",
35185
+ "Another writer modified the task between read and write. Use manage_task get to re-read its current state, then retry."
35186
+ );
34961
35187
  const eventKey = this.statusEventKey(updated, "interrupted", interruption);
34962
35188
  const event = await this.writeStatusEvent(updated, input.actor, input.threadId, "interrupted", summary, interruption, eventKey);
34963
35189
  const warnings = event.warning ? [event.warning] : await this.notifyWinningTransition(updated, "interrupted", event);
@@ -34995,7 +35221,11 @@ var TaskLifecycleService = class {
34995
35221
  context: withCompletionAudit(prepared.task.context, prepared.evidence.id, prepared.eventKey)
34996
35222
  }, ["in_progress"]);
34997
35223
  } catch {
34998
- return failure2("TASK_STORE_WRITE_FAILED", "Failed to complete the task.");
35224
+ return failure2(
35225
+ "TASK_STORE_WRITE_FAILED",
35226
+ "Failed to complete the task.",
35227
+ "The store rejected the write; this may be transient. Retry the completion."
35228
+ );
34999
35229
  }
35000
35230
  if (!updated) {
35001
35231
  if (await this.hasStaleAgentParent(prepared)) {
@@ -35558,7 +35788,11 @@ var TaskLifecycleService = class {
35558
35788
  } catch {
35559
35789
  return failure2("TASK_STORE_WRITE_FAILED", "Failed to reconcile task description.");
35560
35790
  }
35561
- if (!updated) return failure2("TASK_STATUS_CONFLICT", "Task changed concurrently.");
35791
+ if (!updated) return failure2(
35792
+ "TASK_STATUS_CONFLICT",
35793
+ "Task changed concurrently.",
35794
+ "Another writer modified the task between read and write. Use manage_task get to re-read it, then retry the description update."
35795
+ );
35562
35796
  if (changedKeys.length === 0) {
35563
35797
  return {
35564
35798
  success: true,
@@ -38342,7 +38576,7 @@ var import_zod54 = __toESM(require("zod"));
38342
38576
  var import_langchain67 = require("langchain");
38343
38577
  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.
38344
38578
 
38345
- 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).
38579
+ 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).
38346
38580
 
38347
38581
  DOES NOT OWN:
38348
38582
  - 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).
@@ -38363,6 +38597,18 @@ function createMetricsRuntimeTool(params) {
38363
38597
  const client = await resolveClientFromConnections(input.connectionKey, resolvedConnections, readToolRunConfig(exeConfig));
38364
38598
  const selected = effectiveDatasourceScope(client.getSelectedDataSources(), selectedDataSources);
38365
38599
  switch (input.action) {
38600
+ case "list_datasources": {
38601
+ const result = await client.listDatasources();
38602
+ const filtered = selected.length > 0 ? result.filter((d) => selected.includes(Number(d.id))) : result;
38603
+ const safe = filtered.map((d) => ({
38604
+ id: d.id,
38605
+ name: d.name,
38606
+ sourceType: d.sourceType,
38607
+ description: d.description,
38608
+ statusLabel: d.statusLabel
38609
+ }));
38610
+ return JSON.stringify(safe, null, 2);
38611
+ }
38366
38612
  case "read_semantic_catalog": {
38367
38613
  const ds = resolveDatasourceId(input, exeConfig);
38368
38614
  assertDatasourceSelected(selected, ds);
@@ -38385,11 +38631,11 @@ function createMetricsRuntimeTool(params) {
38385
38631
  name: "metrics_runtime_tool",
38386
38632
  description: DESCRIPTION3,
38387
38633
  schema: import_zod54.default.object({
38388
- action: import_zod54.default.enum(["read_semantic_catalog", "query_metrics"]).describe(
38389
- "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"
38634
+ action: import_zod54.default.enum(["list_datasources", "read_semantic_catalog", "query_metrics"]).describe(
38635
+ "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"
38390
38636
  ),
38391
38637
  connectionKey: import_zod54.default.string().optional().describe("Connection key. Omit when only one semantic-metrics connection exists"),
38392
- datasourceId: import_zod54.default.coerce.number().optional().describe("Numeric datasource id, e.g. 15 (optional if set in runConfig.metricsDataSource)"),
38638
+ datasourceId: import_zod54.default.coerce.number().optional().describe("Numeric datasource id, e.g. 15 (required for read_semantic_catalog; optional if set in runConfig.metricsDataSource)"),
38393
38639
  query: import_zod54.default.record(import_zod54.default.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 }')
38394
38640
  })
38395
38641
  }
@@ -38854,7 +39100,7 @@ var semanticMetricsPlugin = {
38854
39100
  configSchema: {
38855
39101
  type: "object",
38856
39102
  title: "Semantic Metrics Configuration",
38857
- 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).",
39103
+ 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).",
38858
39104
  properties: {
38859
39105
  connections: {
38860
39106
  type: "array",
@@ -40840,6 +41086,16 @@ function getRunConfig2(config) {
40840
41086
  const c = config;
40841
41087
  return c?.configurable?.runConfig ?? {};
40842
41088
  }
41089
+ function taskUpdateDebug(message, data) {
41090
+ try {
41091
+ console.log(JSON.stringify({ __taskUpdateDebug: true, message, ...data }));
41092
+ } catch {
41093
+ console.log(`[taskUpdateDebug] ${message}`);
41094
+ }
41095
+ }
41096
+ function taskUpdateBlocked(taskId, code, detail) {
41097
+ taskUpdateDebug("update:blocked", { taskId, code, ...detail });
41098
+ }
40843
41099
  function getRuntimeActor2(runConfig) {
40844
41100
  if (runConfig.assistant_id) return `agent:${String(runConfig.assistant_id)}`;
40845
41101
  if (runConfig.user_id) return `user:${String(runConfig.user_id)}`;
@@ -41066,6 +41322,7 @@ async function validateEffectiveDependencies(store, tenantId2, targetScope, depe
41066
41322
  if (blockedBy.length === 0) return void 0;
41067
41323
  return JSON.stringify({
41068
41324
  success: false,
41325
+ code: "DEPENDENCY_BLOCKED",
41069
41326
  error: `Cannot start task: ${blockedBy.length} dependencies not completed`,
41070
41327
  blockedBy,
41071
41328
  invalidScope,
@@ -41216,13 +41473,27 @@ function missingBeliefImpactError() {
41216
41473
  hint: "Provide beliefImpact: [{ key: '<belief-key>', after: <0-100>, basis: '<evidence>' }]"
41217
41474
  });
41218
41475
  }
41476
+ function reviewInterruption(task) {
41477
+ const value = task.context?.interruption;
41478
+ if (!value || typeof value !== "object") return void 0;
41479
+ const interruption = value;
41480
+ return interruption.type === "review_required" ? interruption : void 0;
41481
+ }
41482
+ function buildReviewMarkdown(task) {
41483
+ return genUIMarkdown("task_review", {
41484
+ taskId: task.id,
41485
+ title: task.title,
41486
+ description: task.description,
41487
+ result: task.result
41488
+ });
41489
+ }
41219
41490
  var manageTaskSchema = import_zod68.z.object({
41220
- action: import_zod68.z.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'"),
41491
+ action: import_zod68.z.enum(["create", "get", "list", "update", "set_status", "add_activity", "delete"]).describe("Action to perform. Available: create, get, list, update, set_status, add_activity, delete. To change status, use set_status. To change content fields, use update."),
41221
41492
  id: import_zod68.z.string().optional().describe("Task ID (required for get, update, add_activity, and delete)"),
41222
41493
  title: import_zod68.z.string().optional().describe("Task title (required for create)"),
41223
41494
  description: import_zod68.z.string().optional().describe("Task description in Markdown"),
41224
41495
  priority: import_zod68.z.enum(["low", "medium", "high"]).optional().describe("Priority level"),
41225
- status: import_zod68.z.enum(["pending", "in_progress", "review", "failed", "interrupted", "completed", "cancelled"]).optional().describe("Task status. Agent summary-only interrupted updates default to missing_input"),
41496
+ status: import_zod68.z.enum(["pending", "in_progress", "review", "failed", "interrupted", "completed", "cancelled"]).optional().describe("Task status. For status-only changes, prefer set_status action. Agent summary-only interrupted updates default to missing_input"),
41226
41497
  dueDate: import_zod68.z.string().optional().describe("Due date (ISO 8601 format)"),
41227
41498
  metadata: import_zod68.z.record(import_zod68.z.unknown()).optional().describe("Structured metadata (e.g. projectId, module)"),
41228
41499
  parentId: import_zod68.z.string().optional().describe("Parent task ID for grouping subtasks"),
@@ -41233,8 +41504,8 @@ var manageTaskSchema = import_zod68.z.object({
41233
41504
  requireReview: import_zod68.z.boolean().optional().describe("If true, agent completion pauses as interrupted(review_required) for HITL approval"),
41234
41505
  dependencies: import_zod68.z.array(import_zod68.z.string()).optional().describe("List of task IDs that must be completed before this task can start"),
41235
41506
  dependencyTaskIds: import_zod68.z.array(import_zod68.z.string().trim().min(1)).optional().describe("Exact blocking task IDs required for an external_dependency interruption"),
41236
- result: import_zod68.z.string().optional().describe("Nonblank result summary required when creating or updating a task with status='completed'"),
41237
- failureReason: import_zod68.z.string().optional().describe("Nonblank failure reason required when creating or updating a task with status='failed'"),
41507
+ result: import_zod68.z.string().optional().describe("Result summary. Used with set_status status='completed' or update status='completed'"),
41508
+ failureReason: import_zod68.z.string().optional().describe("Failure reason. Used with set_status status='failed' or update status='failed'"),
41238
41509
  files: import_zod68.z.array(import_zod68.z.object({
41239
41510
  uri: import_zod68.z.string().describe("Uniquely locates the resource: http(s):// URL, /s/:token share, or sandbox path"),
41240
41511
  name: import_zod68.z.string().optional().describe("Display name"),
@@ -41253,11 +41524,11 @@ var manageTaskSchema = import_zod68.z.object({
41253
41524
  "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"
41254
41525
  )
41255
41526
  }).superRefine((input, context) => {
41256
- if (input.executionResultId && input.action !== "update" && input.action !== "add_activity") {
41527
+ if (input.executionResultId && input.action !== "update" && input.action !== "set_status" && input.action !== "add_activity") {
41257
41528
  context.addIssue({
41258
41529
  code: import_zod68.z.ZodIssueCode.custom,
41259
41530
  path: ["executionResultId"],
41260
- message: "executionResultId is valid only for update or add_activity"
41531
+ message: "executionResultId is valid only for update, set_status, or add_activity"
41261
41532
  });
41262
41533
  }
41263
41534
  });
@@ -41371,12 +41642,6 @@ function updateValuesEqual(field, requested, persisted) {
41371
41642
  }
41372
41643
  return valuesEqual(requested, persisted);
41373
41644
  }
41374
- function reviewInterruption(task) {
41375
- const value = task.context?.interruption;
41376
- if (!value || typeof value !== "object") return void 0;
41377
- const interruption = value;
41378
- return interruption.type === "review_required" ? interruption : void 0;
41379
- }
41380
41645
  function reviewModeDisabledResponse() {
41381
41646
  return JSON.stringify({
41382
41647
  success: false,
@@ -41385,14 +41650,6 @@ function reviewModeDisabledResponse() {
41385
41650
  hint: "Configure task middleware with reviewMode 'hitl' before enabling requireReview."
41386
41651
  });
41387
41652
  }
41388
- function buildReviewMarkdown(task) {
41389
- return genUIMarkdown("task_review", {
41390
- taskId: task.id,
41391
- title: task.title,
41392
- description: task.description,
41393
- result: task.result
41394
- });
41395
- }
41396
41653
  function createTaskMiddleware(options = {}) {
41397
41654
  const handleManageTaskAction = async (input, config, validateExecutionResult, markMutation) => {
41398
41655
  const rc = getRunConfig2(config);
@@ -41780,35 +42037,75 @@ function createTaskMiddleware(options = {}) {
41780
42037
  return JSON.stringify({ success: true, data: activity });
41781
42038
  }
41782
42039
  case "update": {
42040
+ taskUpdateDebug("update:enter", {
42041
+ id: input.id,
42042
+ action: input.action,
42043
+ status: input.status,
42044
+ hasResult: input.result !== void 0,
42045
+ hasDescription: input.description !== void 0,
42046
+ tenantId: tenantId2,
42047
+ workspaceId,
42048
+ projectId,
42049
+ authorityKind: delegatedTaskId ? "delegated" : trustedProject ? "trusted" : "ordinary",
42050
+ trustedKind: trustedProject ? trustedProject.projectTask ? "projectTask" : "projectRoom" : void 0
42051
+ });
41783
42052
  if (!input.id) {
42053
+ taskUpdateBlocked(input.id, "MISSING_TASK_ID", {});
41784
42054
  return JSON.stringify({
41785
42055
  success: false,
42056
+ code: "MISSING_TASK_ID",
41786
42057
  error: "id is required for update action",
41787
42058
  hint: "Pass the task ID you want to update"
41788
42059
  });
41789
42060
  }
41790
42061
  if (delegatedTaskId && input.id !== delegatedTaskId) {
42062
+ taskUpdateBlocked(input.id, "DELEGATED_TASK_SCOPE_VIOLATION", { delegatedTaskId });
41791
42063
  return delegatedTaskScopeError(delegatedTaskId);
41792
42064
  }
41793
- let existing = await store.getById(tenantId2, input.id);
42065
+ const existing = await store.getById(tenantId2, input.id);
42066
+ taskUpdateDebug("update:loaded", existing ? {
42067
+ status: existing.status,
42068
+ updatedAt: existing.updatedAt?.toISOString(),
42069
+ ownerType: existing.ownerType,
42070
+ ownerId: existing.ownerId,
42071
+ workspaceId: existing.workspaceId,
42072
+ projectId: existing.projectId
42073
+ } : { found: false });
41794
42074
  if (!existing) {
42075
+ taskUpdateBlocked(input.id, "TASK_NOT_FOUND", {});
41795
42076
  return JSON.stringify({
41796
42077
  success: false,
41797
- error: `Task '${input.id}' not found`,
41798
- hint: "Use list to see available tasks and their IDs"
42078
+ code: "TASK_NOT_FOUND",
42079
+ error: `Task '${input.id}' was not found.`,
42080
+ hint: "Use manage_task list to see available tasks and their correct IDs."
41799
42081
  });
41800
42082
  }
41801
42083
  if (!taskMatchesRuntimeScope3(existing, workspaceId, projectId)) {
42084
+ taskUpdateBlocked(input.id, "TASK_NOT_FOUND_OR_INACCESSIBLE", {
42085
+ taskWorkspaceId: existing.workspaceId,
42086
+ taskProjectId: existing.projectId,
42087
+ runtimeWorkspaceId: workspaceId,
42088
+ runtimeProjectId: projectId
42089
+ });
41802
42090
  return taskScopeAccessError(input.id);
41803
42091
  }
41804
42092
  if (trustedProject?.projectTask) {
41805
42093
  const trustedFailure = trustedTaskContextFailure(existing, trustedProject.projectTask);
41806
- if (trustedFailure) return trustedFailure;
42094
+ if (trustedFailure) {
42095
+ taskUpdateBlocked(input.id, "PROJECT_TASK_CONTEXT_INVALID", {
42096
+ taskOwnerId: existing.ownerId,
42097
+ trustedAssistantId: trustedProject.projectTask.assistantId,
42098
+ taskThreadId: existing.context?.thread_id,
42099
+ trustedThreadId: trustedProject.projectTask.threadId
42100
+ });
42101
+ return trustedFailure;
42102
+ }
41807
42103
  }
41808
42104
  if (trustedProject && input.dependencyTaskIds !== void 0) {
41809
42105
  const interruption = input.context?.interruption;
41810
42106
  const interruptionType2 = interruption && typeof interruption === "object" ? interruption.type : void 0;
41811
42107
  if (input.status !== "interrupted" || interruptionType2 !== "external_dependency") {
42108
+ taskUpdateBlocked(input.id, "INVALID_EXTERNAL_DEPENDENCY", {});
41812
42109
  return JSON.stringify({
41813
42110
  success: false,
41814
42111
  code: "INVALID_EXTERNAL_DEPENDENCY",
@@ -41817,24 +42114,26 @@ function createTaskMiddleware(options = {}) {
41817
42114
  }
41818
42115
  }
41819
42116
  if (trustedProject && (input.ownerType !== void 0 && input.ownerType !== existing.ownerType || input.ownerId !== void 0 && input.ownerId !== existing.ownerId)) {
42117
+ taskUpdateBlocked(input.id, "TASK_REASSIGN_ENDPOINT_REQUIRED", {
42118
+ inputOwnerType: input.ownerType,
42119
+ existingOwnerType: existing.ownerType,
42120
+ inputOwnerId: input.ownerId,
42121
+ existingOwnerId: existing.ownerId
42122
+ });
41820
42123
  return JSON.stringify({
41821
42124
  success: false,
41822
42125
  code: "TASK_REASSIGN_ENDPOINT_REQUIRED",
41823
42126
  error: "Project task ownership changes require the reassignment endpoint."
41824
42127
  });
41825
42128
  }
41826
- if (existing.status === "review" || input.status === "review") {
41827
- return JSON.stringify({
41828
- success: false,
41829
- code: "TASK_STATUS_UNSUPPORTED",
41830
- error: "Task status is not supported by the task lifecycle."
41831
- });
41832
- }
41833
42129
  const isAgentTask = existing.ownerType === "agent";
41834
- 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;
41835
42130
  const changesOwnerType = input.ownerType !== void 0 && input.ownerType !== existing.ownerType;
41836
42131
  const changesAgentIdentity = isAgentTask && (input.ownerId !== void 0 && input.ownerId !== existing.ownerId || input.parentId !== void 0 && input.parentId !== existing.parentId);
41837
42132
  if (changesOwnerType || changesAgentIdentity) {
42133
+ taskUpdateBlocked(input.id, "TASK_OWNERSHIP_IMMUTABLE", {
42134
+ changesOwnerType,
42135
+ changesAgentIdentity
42136
+ });
41838
42137
  return JSON.stringify({
41839
42138
  success: false,
41840
42139
  code: "TASK_OWNERSHIP_IMMUTABLE",
@@ -41842,18 +42141,10 @@ function createTaskMiddleware(options = {}) {
41842
42141
  hint: "Create a separate task for different ownership or parentage."
41843
42142
  });
41844
42143
  }
41845
- const hasMismatchedAgentOutput = isAgentTask && (input.result !== void 0 && input.status !== "completed" && !candidateReviewInterruption || input.failureReason !== void 0 && input.status !== "failed");
41846
- if (hasMismatchedAgentOutput) {
41847
- return JSON.stringify({
41848
- success: false,
41849
- code: "TASK_LIFECYCLE_TRANSITION_REQUIRED",
41850
- error: "Agent task result and failureReason require a matching governed lifecycle transition.",
41851
- hint: "Provide result only with completion, or failureReason only with failure."
41852
- });
41853
- }
41854
42144
  const changesOwnership = input.ownerId !== void 0 && input.ownerId !== existing.ownerId || input.parentId !== void 0 && input.parentId !== existing.parentId;
41855
42145
  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;
41856
42146
  if (changesOwnership && changesGovernedState) {
42147
+ taskUpdateBlocked(input.id, "TASK_OWNERSHIP_MUTATION_CONFLICT", {});
41857
42148
  return JSON.stringify({
41858
42149
  success: false,
41859
42150
  code: "TASK_OWNERSHIP_MUTATION_CONFLICT",
@@ -41861,17 +42152,389 @@ function createTaskMiddleware(options = {}) {
41861
42152
  hint: "Persist ownership or parent changes separately before changing lifecycle or task content."
41862
42153
  });
41863
42154
  }
42155
+ if (existing.status === "review") {
42156
+ taskUpdateBlocked(input.id, "TASK_STATUS_UNSUPPORTED", {});
42157
+ return JSON.stringify({
42158
+ success: false,
42159
+ code: "TASK_STATUS_UNSUPPORTED",
42160
+ error: "Task status is not supported by the task lifecycle."
42161
+ });
42162
+ }
42163
+ if (input.status !== void 0) {
42164
+ taskUpdateBlocked(input.id, "STATUS_USES_SET_STATUS", { status: input.status });
42165
+ return JSON.stringify({
42166
+ success: false,
42167
+ code: "STATUS_USES_SET_STATUS",
42168
+ error: `Status changes must use the set_status action. Received status='${input.status}'.`,
42169
+ hint: "Call manage_task with action='set_status', id, and status to change the task status. Use action='update' for content fields only."
42170
+ });
42171
+ }
42172
+ if (isAgentTask && input.requireReview === true && options.reviewMode !== "hitl") {
42173
+ return reviewModeDisabledResponse();
42174
+ }
42175
+ if (["completed", "failed", "cancelled"].includes(existing.status) && input.requireReview !== void 0 && input.requireReview !== existing.requireReview) {
42176
+ taskUpdateBlocked(input.id, "TERMINAL_REVIEW_CONFIGURATION_LOCKED", { requireReview: input.requireReview });
42177
+ return JSON.stringify({
42178
+ success: false,
42179
+ code: "TERMINAL_REVIEW_CONFIGURATION_LOCKED",
42180
+ error: "requireReview cannot be changed on a terminal task",
42181
+ hint: "Configure requireReview before the task reaches a terminal status"
42182
+ });
42183
+ }
42184
+ if (existing.status !== "pending" && existing.requireReview === true && input.requireReview === false) {
42185
+ taskUpdateBlocked(input.id, "REVIEW_REQUIREMENT_LOCKED", {});
42186
+ return JSON.stringify({
42187
+ success: false,
42188
+ code: "REVIEW_REQUIREMENT_LOCKED",
42189
+ error: "requireReview cannot be disabled after a task leaves pending",
42190
+ hint: "Configure requireReview while the task is pending"
42191
+ });
42192
+ }
42193
+ if (existing.status === "completed" && input.result !== void 0) {
42194
+ const error = missingTerminalDetail("result", input.result ?? existing.result);
42195
+ if (error) return error;
42196
+ }
42197
+ if (existing.status === "failed" && input.failureReason !== void 0) {
42198
+ const error = missingTerminalDetail(
42199
+ "failureReason",
42200
+ input.failureReason ?? existing.failureReason
42201
+ );
42202
+ if (error) return error;
42203
+ }
41864
42204
  const actor = trustedActor(trustedProject, rc) ?? (existing.ownerType === "agent" ? `agent:${existing.ownerId}` : `user:${existing.ownerId ?? ownerId}`);
41865
- const threadId = trustedProject?.projectTask?.threadId ?? (trustedProject?.projectRoom ? void 0 : input.sourceId ?? rc.thread_id);
42205
+ const workItemStore = getTaskWorkItemStore();
41866
42206
  const lifecycle = isAgentTask ? createTaskLifecycleService({
41867
42207
  taskStore: store,
41868
42208
  workItemStore: trustedProject ? getProjectTaskWorkItemStore() : getTaskWorkItemStore()
41869
42209
  }) : void 0;
42210
+ let trustedReferenceSnapshots;
42211
+ if (input.parentId !== void 0 || input.dependencies !== void 0) {
42212
+ if (delegatedTaskId) {
42213
+ const delegatedDependencyError = await validateDelegatedDependencies(
42214
+ store,
42215
+ tenantId2,
42216
+ delegatedTaskId,
42217
+ { workspaceId: existing.workspaceId, projectId: existing.projectId },
42218
+ input.parentId,
42219
+ input.dependencies,
42220
+ input.id
42221
+ );
42222
+ if (delegatedDependencyError) {
42223
+ taskUpdateBlocked(input.id, "DELEGATED_TASK_SCOPE_VIOLATION", { delegatedDependencyError });
42224
+ return delegatedDependencyError;
42225
+ }
42226
+ }
42227
+ const referenceError = await validateReferencedTasks(
42228
+ store,
42229
+ tenantId2,
42230
+ { workspaceId: existing.workspaceId, projectId: existing.projectId },
42231
+ input.parentId,
42232
+ input.dependencies
42233
+ );
42234
+ if (referenceError) {
42235
+ taskUpdateBlocked(input.id, "INVALID_TASK_REFERENCES", { referenceError });
42236
+ return referenceError;
42237
+ }
42238
+ if (trustedProject) {
42239
+ trustedReferenceSnapshots = await captureReferencedTaskSnapshots(
42240
+ store,
42241
+ tenantId2,
42242
+ { workspaceId: existing.workspaceId, projectId: existing.projectId },
42243
+ input.parentId,
42244
+ input.dependencies
42245
+ );
42246
+ if (!trustedReferenceSnapshots) {
42247
+ taskUpdateBlocked(input.id, "PROJECT_TASK_CONTEXT_INVALID", { reason: "reference snapshots unavailable" });
42248
+ return trustedContextError();
42249
+ }
42250
+ }
42251
+ }
42252
+ const effectiveDependencies = input.dependencies ?? existing.dependencies ?? [];
42253
+ const effectiveStatus = input.status ?? existing.status;
42254
+ if (effectiveStatus === "in_progress" && effectiveDependencies.length > 0) {
42255
+ const dependencyError = await validateEffectiveDependencies(
42256
+ store,
42257
+ tenantId2,
42258
+ { workspaceId: existing.workspaceId, projectId: existing.projectId },
42259
+ effectiveDependencies
42260
+ );
42261
+ if (dependencyError) {
42262
+ const trustedException = trustedProject?.projectTask && input.dependencies !== void 0 ? await validateTrustedChildDependencyWait(
42263
+ store,
42264
+ tenantId2,
42265
+ existing,
42266
+ effectiveDependencies,
42267
+ trustedProject
42268
+ ) : void 0;
42269
+ if (trustedException === void 0) {
42270
+ taskUpdateBlocked(input.id, "DEPENDENCY_BLOCKED", { dependencyError });
42271
+ return dependencyError;
42272
+ }
42273
+ if (trustedException) return trustedException;
42274
+ }
42275
+ }
42276
+ let reconciledTask;
42277
+ let reconciliationMetadata = {};
42278
+ let descriptionMutated = false;
42279
+ if (isAgentTask && input.description !== void 0 && !valuesEqual(input.description, existing.description)) {
42280
+ const oldBelief = (0, import_protocols33.parseTaskBeliefState)(existing.description ?? "");
42281
+ const newBelief = (0, import_protocols33.parseTaskBeliefState)(input.description);
42282
+ const hasBeliefState = oldBelief.success || newBelief.success;
42283
+ if (hasBeliefState) {
42284
+ await validateExecutionResult();
42285
+ const reconciliation = await lifecycle.updateTaskDescriptionWithReconciliation({
42286
+ tenantId: tenantId2,
42287
+ taskId: input.id,
42288
+ description: input.description,
42289
+ actor,
42290
+ threadId: trustedProject?.projectTask?.threadId ?? (trustedProject?.projectRoom ? void 0 : input.sourceId ?? rc.thread_id)
42291
+ });
42292
+ if (!reconciliation.success) return JSON.stringify(reconciliation);
42293
+ reconciledTask = reconciliation.task;
42294
+ reconciliationMetadata = { ...reconciliation };
42295
+ delete reconciliationMetadata.task;
42296
+ descriptionMutated = reconciliation.mutated;
42297
+ if (descriptionMutated) markMutation();
42298
+ }
42299
+ }
42300
+ const updates = {};
42301
+ const settableFields = [
42302
+ "title",
42303
+ "description",
42304
+ "priority",
42305
+ "dueDate",
42306
+ "metadata",
42307
+ "parentId",
42308
+ "sourceId",
42309
+ "context",
42310
+ "ownerId",
42311
+ "result",
42312
+ "failureReason",
42313
+ "requireReview",
42314
+ "dependencies",
42315
+ "files"
42316
+ ];
42317
+ for (const field of settableFields) {
42318
+ if (trustedProject && (field === "sourceId" || field === "context" || field === "ownerId")) continue;
42319
+ if (field === "description" && reconciledTask) continue;
42320
+ const requested = input[field];
42321
+ const persisted = reconciledTask ?? existing;
42322
+ if (requested !== void 0 && !updateValuesEqual(field, requested, persisted[field])) {
42323
+ updates[field] = requested;
42324
+ }
42325
+ }
42326
+ if (!reconciledTask && Object.keys(updates).length > 0) {
42327
+ await validateExecutionResult();
42328
+ }
42329
+ if (trustedProject && (input.parentId !== void 0 || input.dependencies !== void 0)) {
42330
+ if (!await referencedTaskSnapshotsStillMatch(
42331
+ store,
42332
+ tenantId2,
42333
+ { workspaceId: existing.workspaceId, projectId: existing.projectId },
42334
+ trustedReferenceSnapshots
42335
+ )) {
42336
+ taskUpdateBlocked(input.id, "PROJECT_TASK_CONTEXT_INVALID", { reason: "referenced task changed" });
42337
+ return trustedContextError("Parent or dependency task changed before update.");
42338
+ }
42339
+ }
42340
+ if (trustedProject) {
42341
+ try {
42342
+ await createProjectTaskOwnerPolicy().requireCaller(trustedRosterScope(trustedProject));
42343
+ } catch (error) {
42344
+ taskUpdateBlocked(input.id, "PROJECT_POLICY_ERROR", { policyError: String(error) });
42345
+ return projectPolicyError(error);
42346
+ }
42347
+ }
42348
+ const snapshot = trustedProject ? taskMutationSnapshot(existing) : void 0;
42349
+ taskUpdateDebug("update:write", {
42350
+ updateKeys: Object.keys(updates),
42351
+ useCas: !!snapshot,
42352
+ storeMethod: snapshot ? "updateIfSnapshot" : "update"
42353
+ });
42354
+ 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;
42355
+ taskUpdateDebug("update:result", {
42356
+ updated: !!updated,
42357
+ resultStatus: updated?.status,
42358
+ resultUpdatedAt: updated?.updatedAt?.toISOString?.()
42359
+ });
42360
+ if (!updated) {
42361
+ taskUpdateBlocked(input.id, "TASK_UPDATE_FAILED", { storeMethod: snapshot ? "updateIfSnapshot" : "update" });
42362
+ return JSON.stringify({
42363
+ success: false,
42364
+ code: "TASK_UPDATE_FAILED",
42365
+ error: `Failed to update task '${input.id}'.`,
42366
+ 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."
42367
+ });
42368
+ }
42369
+ if (Object.keys(updates).length > 0) markMutation();
42370
+ if (reconciledTask && Object.keys(updates).length === 0) {
42371
+ return successfulTaskResponse(input.id, updated, descriptionMutated, reconciliationMetadata);
42372
+ }
42373
+ if (Object.keys(updates).length === 0) {
42374
+ return successfulTaskResponse(input.id, updated, false);
42375
+ }
42376
+ const workItemAction = "updated";
42377
+ const workItemSummary = input.summary;
42378
+ let activityWarnings;
42379
+ try {
42380
+ if (trustedProject) {
42381
+ try {
42382
+ await createProjectTaskOwnerPolicy().requireCaller(trustedRosterScope(trustedProject));
42383
+ } catch (error) {
42384
+ return projectPolicyError(error);
42385
+ }
42386
+ }
42387
+ await workItemStore.create({
42388
+ taskId: input.id,
42389
+ tenantId: tenantId2,
42390
+ action: workItemAction,
42391
+ actor,
42392
+ threadId: trustedProject?.projectTask?.threadId ?? (trustedProject?.projectRoom ? void 0 : input.sourceId),
42393
+ summary: workItemSummary,
42394
+ detail: {
42395
+ ...input.result !== void 0 && { result: input.result },
42396
+ ...input.failureReason !== void 0 && { failureReason: input.failureReason }
42397
+ },
42398
+ workspaceId: updated.workspaceId,
42399
+ projectId: updated.projectId
42400
+ });
42401
+ } catch (error) {
42402
+ activityWarnings = [{
42403
+ code: "TASK_UPDATE_ACTIVITY_WRITE_FAILED",
42404
+ taskId: input.id,
42405
+ error: error instanceof Error ? error.message : String(error)
42406
+ }];
42407
+ }
42408
+ const lifecycleWarnings = Array.isArray(reconciliationMetadata.warnings) ? reconciliationMetadata.warnings : void 0;
42409
+ return successfulTaskResponse(input.id, updated, true, {
42410
+ ...reconciliationMetadata,
42411
+ warnings: mergeTaskUpdateWarnings(lifecycleWarnings, activityWarnings)
42412
+ });
42413
+ }
42414
+ case "set_status": {
42415
+ if (!input.id) {
42416
+ taskUpdateBlocked(input.id, "MISSING_TASK_ID", {});
42417
+ return JSON.stringify({
42418
+ success: false,
42419
+ code: "MISSING_TASK_ID",
42420
+ error: "id is required for set_status action",
42421
+ hint: "Pass the task ID you want to update"
42422
+ });
42423
+ }
42424
+ if (!input.status) {
42425
+ return JSON.stringify({
42426
+ success: false,
42427
+ code: "MISSING_STATUS",
42428
+ error: "status is required for set_status action",
42429
+ hint: "Pass the target status: pending, in_progress, completed, failed, interrupted, cancelled"
42430
+ });
42431
+ }
42432
+ if (delegatedTaskId && input.id !== delegatedTaskId) {
42433
+ return delegatedTaskScopeError(delegatedTaskId);
42434
+ }
42435
+ const existing = await store.getById(tenantId2, input.id);
42436
+ taskUpdateDebug("set_status:loaded", existing ? {
42437
+ status: existing.status,
42438
+ updatedAt: existing.updatedAt?.toISOString()
42439
+ } : { found: false });
42440
+ if (!existing) {
42441
+ taskUpdateBlocked(input.id, "TASK_NOT_FOUND", {});
42442
+ return JSON.stringify({
42443
+ success: false,
42444
+ code: "TASK_NOT_FOUND",
42445
+ error: `Task '${input.id}' was not found.`,
42446
+ hint: "Use manage_task list to see available tasks and their correct IDs."
42447
+ });
42448
+ }
42449
+ if (!taskMatchesRuntimeScope3(existing, workspaceId, projectId)) {
42450
+ taskUpdateBlocked(input.id, "TASK_NOT_FOUND_OR_INACCESSIBLE", {});
42451
+ return taskScopeAccessError(input.id);
42452
+ }
42453
+ if (trustedProject?.projectTask) {
42454
+ const trustedFailure = trustedTaskContextFailure(existing, trustedProject.projectTask);
42455
+ if (trustedFailure) {
42456
+ taskUpdateBlocked(input.id, "PROJECT_TASK_CONTEXT_INVALID", {
42457
+ taskOwnerId: existing.ownerId,
42458
+ trustedAssistantId: trustedProject.projectTask.assistantId,
42459
+ taskThreadId: existing.context?.thread_id,
42460
+ trustedThreadId: trustedProject.projectTask.threadId
42461
+ });
42462
+ return trustedFailure;
42463
+ }
42464
+ }
42465
+ if (trustedProject && input.dependencyTaskIds !== void 0) {
42466
+ const interruption = input.context?.interruption;
42467
+ const interruptionType2 = interruption && typeof interruption === "object" ? interruption.type : void 0;
42468
+ if (input.status !== "interrupted" || interruptionType2 !== "external_dependency") {
42469
+ taskUpdateBlocked(input.id, "INVALID_EXTERNAL_DEPENDENCY", {});
42470
+ return JSON.stringify({
42471
+ success: false,
42472
+ code: "INVALID_EXTERNAL_DEPENDENCY",
42473
+ error: "dependencyTaskIds are only valid for external dependency interruptions."
42474
+ });
42475
+ }
42476
+ }
42477
+ if (trustedProject && (input.ownerType !== void 0 && input.ownerType !== existing.ownerType || input.ownerId !== void 0 && input.ownerId !== existing.ownerId)) {
42478
+ taskUpdateBlocked(input.id, "TASK_REASSIGN_ENDPOINT_REQUIRED", {});
42479
+ return JSON.stringify({
42480
+ success: false,
42481
+ code: "TASK_REASSIGN_ENDPOINT_REQUIRED",
42482
+ error: "Project task ownership changes require the reassignment endpoint."
42483
+ });
42484
+ }
42485
+ if (existing.status === "review" || input.status === "review") {
42486
+ taskUpdateBlocked(input.id, "TASK_STATUS_UNSUPPORTED", {});
42487
+ return JSON.stringify({
42488
+ success: false,
42489
+ code: "TASK_STATUS_UNSUPPORTED",
42490
+ error: "Task status is not supported by the task lifecycle."
42491
+ });
42492
+ }
42493
+ const isAgentTask = existing.ownerType === "agent";
42494
+ const changesOwnerType = input.ownerType !== void 0 && input.ownerType !== existing.ownerType;
42495
+ const changesAgentIdentity = isAgentTask && (input.ownerId !== void 0 && input.ownerId !== existing.ownerId || input.parentId !== void 0 && input.parentId !== existing.parentId);
42496
+ if (changesOwnerType || changesAgentIdentity) {
42497
+ taskUpdateBlocked(input.id, "TASK_OWNERSHIP_IMMUTABLE", {});
42498
+ return JSON.stringify({
42499
+ success: false,
42500
+ code: "TASK_OWNERSHIP_IMMUTABLE",
42501
+ error: "Agent task ownerType, ownerId, and parentId cannot be changed after task creation.",
42502
+ hint: "Create a separate task for different ownership or parentage."
42503
+ });
42504
+ }
42505
+ const actor = trustedActor(trustedProject, rc) ?? (existing.ownerType === "agent" ? `agent:${existing.ownerId}` : `user:${existing.ownerId ?? ownerId}`);
42506
+ const statusWorkItemStore = trustedProject ? getProjectTaskWorkItemStore() : getTaskWorkItemStore();
42507
+ const lifecycle = isAgentTask ? createTaskLifecycleService({
42508
+ taskStore: store,
42509
+ workItemStore: statusWorkItemStore
42510
+ }) : void 0;
41870
42511
  const lifecycleMutationResponse = (result, extra = {}) => {
41871
42512
  if (result.success && result.mutated) markMutation();
41872
42513
  return lifecycleResponse(input.id, result, extra);
41873
42514
  };
41874
- const hasCarriedContentFields = () => ["title", "description", "priority", "dueDate", "metadata", "files"].some((key4) => input[key4] !== void 0);
42515
+ if (input.status === "in_progress" && (existing.dependencies ?? []).length > 0) {
42516
+ const dependencyError = await validateEffectiveDependencies(
42517
+ store,
42518
+ tenantId2,
42519
+ { workspaceId: existing.workspaceId, projectId: existing.projectId },
42520
+ existing.dependencies ?? []
42521
+ );
42522
+ if (dependencyError) {
42523
+ const trustedException = trustedProject?.projectTask ? await validateTrustedChildDependencyWait(
42524
+ store,
42525
+ tenantId2,
42526
+ existing,
42527
+ existing.dependencies ?? [],
42528
+ trustedProject
42529
+ ) : void 0;
42530
+ if (trustedException === void 0) {
42531
+ taskUpdateBlocked(input.id, "DEPENDENCY_BLOCKED", { dependencyError });
42532
+ return dependencyError;
42533
+ }
42534
+ if (trustedException) return trustedException;
42535
+ }
42536
+ }
42537
+ const statusThreadId = trustedProject?.projectTask?.threadId ?? (trustedProject?.projectRoom ? void 0 : input.sourceId ?? rc.thread_id);
41875
42538
  if (lifecycle && input.status === "failed") {
41876
42539
  await validateExecutionResult();
41877
42540
  const callerError = await revalidateCaller();
@@ -41881,11 +42544,9 @@ function createTaskMiddleware(options = {}) {
41881
42544
  taskId: input.id,
41882
42545
  failureReason: input.failureReason ?? existing.failureReason ?? "",
41883
42546
  actor,
41884
- threadId
42547
+ threadId: statusThreadId
41885
42548
  });
41886
- if (!failed.success) return lifecycleMutationResponse(failed);
41887
- if (trustedProject || !hasCarriedContentFields()) return lifecycleMutationResponse(failed);
41888
- existing = { ...existing, status: input.status };
42549
+ return lifecycleMutationResponse(failed);
41889
42550
  }
41890
42551
  if (lifecycle && input.status === "cancelled") {
41891
42552
  await validateExecutionResult();
@@ -41895,12 +42556,10 @@ function createTaskMiddleware(options = {}) {
41895
42556
  tenantId: tenantId2,
41896
42557
  taskId: input.id,
41897
42558
  actor,
41898
- threadId,
42559
+ threadId: statusThreadId,
41899
42560
  summary: input.summary
41900
42561
  });
41901
- if (!cancelled.success) return lifecycleMutationResponse(cancelled);
41902
- if (trustedProject || !hasCarriedContentFields()) return lifecycleMutationResponse(cancelled);
41903
- existing = { ...existing, status: input.status };
42562
+ return lifecycleMutationResponse(cancelled);
41904
42563
  }
41905
42564
  if (lifecycle && input.status === "interrupted" && !(input.context?.interruption && typeof input.context.interruption === "object" && input.context.interruption.type === "review_required")) {
41906
42565
  const interruption = input.context?.interruption;
@@ -41922,12 +42581,10 @@ function createTaskMiddleware(options = {}) {
41922
42581
  type,
41923
42582
  summary: input.summary ?? "",
41924
42583
  actor,
41925
- threadId,
42584
+ threadId: statusThreadId,
41926
42585
  dependencyTaskIds: input.dependencyTaskIds
41927
42586
  });
41928
- if (!interrupted.success) return lifecycleMutationResponse(interrupted);
41929
- if (trustedProject || !hasCarriedContentFields()) return lifecycleMutationResponse(interrupted);
41930
- existing = { ...existing, status: input.status };
42587
+ return lifecycleMutationResponse(interrupted);
41931
42588
  }
41932
42589
  if (lifecycle && input.status === "in_progress" && existing.status === "interrupted") {
41933
42590
  await validateExecutionResult();
@@ -41937,11 +42594,9 @@ function createTaskMiddleware(options = {}) {
41937
42594
  tenantId: tenantId2,
41938
42595
  taskId: input.id,
41939
42596
  actor,
41940
- threadId
42597
+ threadId: statusThreadId
41941
42598
  });
41942
- if (!resumed.success) return lifecycleMutationResponse(resumed);
41943
- if (trustedProject || !hasCarriedContentFields()) return lifecycleMutationResponse(resumed);
41944
- existing = { ...existing, status: input.status };
42599
+ return lifecycleMutationResponse(resumed);
41945
42600
  }
41946
42601
  if (lifecycle && input.status === "in_progress" && existing.status === "pending") {
41947
42602
  await validateExecutionResult();
@@ -41951,11 +42606,9 @@ function createTaskMiddleware(options = {}) {
41951
42606
  tenantId: tenantId2,
41952
42607
  taskId: input.id,
41953
42608
  actor,
41954
- threadId
42609
+ threadId: statusThreadId
41955
42610
  });
41956
- if (!started.success) return lifecycleMutationResponse(started);
41957
- if (trustedProject || !hasCarriedContentFields()) return lifecycleMutationResponse(started);
41958
- existing = { ...existing, status: input.status };
42611
+ return lifecycleMutationResponse(started);
41959
42612
  }
41960
42613
  if (lifecycle && input.status === "in_progress" && existing.status === "failed") {
41961
42614
  await validateExecutionResult();
@@ -41965,11 +42618,9 @@ function createTaskMiddleware(options = {}) {
41965
42618
  tenantId: tenantId2,
41966
42619
  taskId: input.id,
41967
42620
  actor,
41968
- threadId
42621
+ threadId: statusThreadId
41969
42622
  });
41970
- if (!retried.success) return lifecycleMutationResponse(retried);
41971
- if (trustedProject || !hasCarriedContentFields()) return lifecycleMutationResponse(retried);
41972
- existing = { ...existing, status: input.status };
42623
+ return lifecycleMutationResponse(retried);
41973
42624
  }
41974
42625
  if (isAgentTask && input.requireReview === true && options.reviewMode !== "hitl") {
41975
42626
  return reviewModeDisabledResponse();
@@ -41991,54 +42642,39 @@ function createTaskMiddleware(options = {}) {
41991
42642
  });
41992
42643
  }
41993
42644
  const effectiveRequireReview = existing.requireReview === true || input.requireReview === true;
41994
- const workItemStore = getTaskWorkItemStore();
41995
- const governedAgentCompletion = isAgentTask && (input.status === "completed" || candidateReviewInterruption && !!input.result?.trim() && !!input.beliefImpact?.length);
42645
+ 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;
42646
+ const governedAgentCompletion = isAgentTask && (input.status === "completed" || candidateReviewInterruption);
41996
42647
  if (governedAgentCompletion) {
41997
- const guardedFields = [
41998
- "parentId",
41999
- "ownerType",
42000
- "ownerId",
42001
- "description"
42002
- ];
42003
- for (const field of guardedFields) {
42004
- const requestValue = input[field];
42005
- const existingValue = existing[field];
42006
- if (requestValue !== void 0 && requestValue !== existingValue) {
42007
- return JSON.stringify({
42008
- success: false,
42009
- code: "COMPLETION_CONTEXT_MUTATION",
42010
- error: `Cannot change ${field} while completing an agent task`,
42011
- hint: "Persist the context change in a separate update before completing the task"
42012
- });
42013
- }
42014
- }
42015
42648
  if (!input.beliefImpact?.length) return missingBeliefImpactError();
42016
42649
  const finalResult = input.result ?? existing.result ?? "";
42017
- const threadId2 = trustedProject?.projectTask?.threadId ?? (trustedProject?.projectRoom ? void 0 : input.sourceId ?? rc.thread_id);
42018
- const lifecycle2 = createTaskLifecycleService({
42019
- taskStore: store,
42020
- workItemStore: trustedProject ? getProjectTaskWorkItemStore() : getTaskWorkItemStore()
42021
- });
42650
+ if (!finalResult.trim()) {
42651
+ return JSON.stringify({
42652
+ success: false,
42653
+ code: "MISSING_RESULT",
42654
+ error: "result is required for completing a task",
42655
+ hint: "Provide a nonblank result"
42656
+ });
42657
+ }
42022
42658
  const completionInput = {
42023
42659
  tenantId: tenantId2,
42024
42660
  taskId: input.id,
42025
42661
  result: finalResult,
42026
42662
  beliefImpact: input.beliefImpact,
42027
42663
  actor,
42028
- threadId: threadId2,
42664
+ threadId: statusThreadId,
42029
42665
  sourceId: trustedProject ? void 0 : input.sourceId
42030
42666
  };
42031
42667
  await validateExecutionResult();
42032
- const needsReview = effectiveRequireReview || candidateReviewInterruption || !!reviewInterruption(existing);
42668
+ const needsReview = effectiveRequireReview || !!reviewInterruption(existing);
42033
42669
  if (!needsReview) {
42034
42670
  const callerError2 = await revalidateCaller();
42035
42671
  if (callerError2) return callerError2;
42036
- const result = existing.status === "completed" ? await lifecycle2.repairCompletion(completionInput) : await lifecycle2.completeAgentTask(completionInput);
42672
+ const result = existing.status === "completed" ? await lifecycle.repairCompletion(completionInput) : await lifecycle.completeAgentTask(completionInput);
42037
42673
  return lifecycleMutationResponse(result);
42038
42674
  }
42039
42675
  const callerError = await revalidateCaller();
42040
42676
  if (callerError) return callerError;
42041
- const submitted = await lifecycle2.submitReview({
42677
+ const submitted = await lifecycle.submitReview({
42042
42678
  ...completionInput,
42043
42679
  reviewMode: options.reviewMode ?? "disabled",
42044
42680
  configureRequireReview: input.requireReview === true && existing.requireReview !== true
@@ -42063,30 +42699,34 @@ function createTaskMiddleware(options = {}) {
42063
42699
  if (response?.action === "approve") {
42064
42700
  const approvalCallerError = await revalidateCaller();
42065
42701
  if (approvalCallerError) return approvalCallerError;
42066
- return lifecycleMutationResponse(await lifecycle2.approveInterrupted({
42702
+ return lifecycleMutationResponse(await lifecycle.approveInterrupted({
42067
42703
  tenantId: tenantId2,
42068
42704
  taskId: input.id,
42069
42705
  evidenceId,
42070
42706
  actor,
42071
- threadId: threadId2
42707
+ threadId: statusThreadId
42072
42708
  }), { reviewerAction: "approved" });
42073
42709
  }
42074
42710
  if (response?.action === "reject") {
42075
42711
  const note = response.data?.note?.trim() || "Changes requested.";
42076
42712
  const rejectionCallerError = await revalidateCaller();
42077
42713
  if (rejectionCallerError) return rejectionCallerError;
42078
- return lifecycleMutationResponse(await lifecycle2.rejectInterrupted({
42714
+ return lifecycleMutationResponse(await lifecycle.rejectInterrupted({
42079
42715
  tenantId: tenantId2,
42080
42716
  taskId: input.id,
42081
42717
  evidenceId,
42082
42718
  actor,
42083
- threadId: threadId2,
42719
+ threadId: statusThreadId,
42084
42720
  note
42085
42721
  }), { reviewerAction: "rejected", reviewerNote: note });
42086
42722
  }
42087
42723
  return lifecycleMutationResponse(submitted, { code: "REVIEW_REQUIRED" });
42088
42724
  }
42089
- if (isAgentTask && input.status !== void 0 && input.status !== existing.status) {
42725
+ if (isAgentTask && input.status !== existing.status) {
42726
+ taskUpdateBlocked(input.id, "AGENT_TASK_LIFECYCLE_REQUIRED", {
42727
+ currentStatus: existing.status,
42728
+ requestedStatus: input.status
42729
+ });
42090
42730
  return JSON.stringify({
42091
42731
  success: false,
42092
42732
  code: "AGENT_TASK_LIFECYCLE_REQUIRED",
@@ -42094,45 +42734,17 @@ function createTaskMiddleware(options = {}) {
42094
42734
  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."
42095
42735
  });
42096
42736
  }
42097
- const persistedStatus = input.status;
42098
- let trustedReferenceSnapshots;
42099
- if (input.parentId !== void 0 || input.dependencies !== void 0) {
42100
- if (delegatedTaskId) {
42101
- const delegatedDependencyError = await validateDelegatedDependencies(
42102
- store,
42103
- tenantId2,
42104
- delegatedTaskId,
42105
- { workspaceId: existing.workspaceId, projectId: existing.projectId },
42106
- input.parentId,
42107
- input.dependencies,
42108
- input.id
42109
- );
42110
- if (delegatedDependencyError) return delegatedDependencyError;
42111
- }
42112
- const referenceError = await validateReferencedTasks(
42113
- store,
42114
- tenantId2,
42115
- { workspaceId: existing.workspaceId, projectId: existing.projectId },
42116
- input.parentId,
42117
- input.dependencies
42118
- );
42119
- if (referenceError) return referenceError;
42120
- if (trustedProject) {
42121
- trustedReferenceSnapshots = await captureReferencedTaskSnapshots(
42122
- store,
42123
- tenantId2,
42124
- { workspaceId: existing.workspaceId, projectId: existing.projectId },
42125
- input.parentId,
42126
- input.dependencies
42127
- );
42128
- if (!trustedReferenceSnapshots) return trustedContextError();
42129
- }
42130
- }
42131
42737
  if (input.status) {
42132
42738
  if (!isAgentTask && !isValidUserTransition(existing.status, input.status)) {
42133
42739
  const allowed = USER_VALID_TRANSITIONS[existing.status] || [];
42740
+ taskUpdateBlocked(input.id, "INVALID_USER_TRANSITION", {
42741
+ currentStatus: existing.status,
42742
+ requestedStatus: input.status,
42743
+ allowedTransitions: allowed
42744
+ });
42134
42745
  return JSON.stringify({
42135
42746
  success: false,
42747
+ code: "INVALID_USER_TRANSITION",
42136
42748
  error: `Cannot transition task from '${existing.status}' to '${input.status}'`,
42137
42749
  allowedTransitions: allowed,
42138
42750
  hint: `From '${existing.status}', valid transitions are: ${allowed.join(", ")}`
@@ -42150,154 +42762,47 @@ function createTaskMiddleware(options = {}) {
42150
42762
  );
42151
42763
  if (error) return error;
42152
42764
  }
42153
- const effectiveDependencies = input.dependencies ?? existing.dependencies ?? [];
42154
- const effectiveStatus = input.status ?? existing.status;
42155
- if (effectiveStatus === "in_progress" && effectiveDependencies.length > 0) {
42156
- const dependencyError = await validateEffectiveDependencies(
42157
- store,
42158
- tenantId2,
42159
- { workspaceId: existing.workspaceId, projectId: existing.projectId },
42160
- effectiveDependencies
42161
- );
42162
- if (dependencyError) {
42163
- const trustedException = trustedProject?.projectTask && input.dependencies !== void 0 ? await validateTrustedChildDependencyWait(
42164
- store,
42165
- tenantId2,
42166
- existing,
42167
- effectiveDependencies,
42168
- trustedProject
42169
- ) : void 0;
42170
- if (trustedException === void 0) return dependencyError;
42171
- if (trustedException) return trustedException;
42172
- }
42765
+ if (input.status === existing.status) {
42766
+ return successfulTaskResponse(input.id, existing, false);
42173
42767
  }
42174
- let reconciledTask;
42175
- let reconciliationMetadata = {};
42176
- let descriptionMutated = false;
42177
- if (isAgentTask && input.description !== void 0 && !valuesEqual(input.description, existing.description)) {
42178
- await validateExecutionResult();
42179
- const reconciliation = await lifecycle.updateTaskDescriptionWithReconciliation({
42180
- tenantId: tenantId2,
42181
- taskId: input.id,
42182
- description: input.description,
42183
- actor,
42184
- threadId: trustedProject?.projectTask?.threadId ?? (trustedProject?.projectRoom ? void 0 : input.sourceId ?? rc.thread_id)
42185
- });
42186
- if (!reconciliation.success) return JSON.stringify(reconciliation);
42187
- reconciledTask = reconciliation.task;
42188
- reconciliationMetadata = { ...reconciliation };
42189
- delete reconciliationMetadata.task;
42190
- descriptionMutated = reconciliation.mutated;
42191
- if (descriptionMutated) markMutation();
42192
- }
42193
- const updates = {};
42194
- const settableFields = [
42195
- "title",
42196
- "description",
42197
- "priority",
42198
- "dueDate",
42199
- "metadata",
42200
- "parentId",
42201
- "sourceId",
42202
- "context",
42203
- "ownerId",
42204
- "result",
42205
- "failureReason",
42206
- "requireReview",
42207
- "dependencies",
42208
- "files"
42209
- ];
42210
- for (const field of settableFields) {
42211
- if (trustedProject && (field === "sourceId" || field === "context" || field === "ownerId")) continue;
42212
- if (field === "description" && reconciledTask) continue;
42213
- const requested = input[field];
42214
- const persisted = reconciledTask ?? existing;
42215
- if (requested !== void 0 && !updateValuesEqual(field, requested, persisted[field])) {
42216
- updates[field] = requested;
42217
- }
42218
- }
42219
- if (persistedStatus !== void 0 && existing.status !== persistedStatus) {
42220
- updates.status = persistedStatus;
42221
- }
42222
- if (!reconciledTask && Object.keys(updates).length > 0) {
42223
- await validateExecutionResult();
42224
- }
42225
- if (trustedProject && (input.parentId !== void 0 || input.dependencies !== void 0)) {
42226
- if (!await referencedTaskSnapshotsStillMatch(
42227
- store,
42228
- tenantId2,
42229
- { workspaceId: existing.workspaceId, projectId: existing.projectId },
42230
- trustedReferenceSnapshots
42231
- )) return trustedContextError("Parent or dependency task changed before update.");
42232
- }
42233
- if (trustedProject) {
42234
- try {
42235
- await createProjectTaskOwnerPolicy().requireCaller(trustedRosterScope(trustedProject));
42236
- } catch (error) {
42237
- return projectPolicyError(error);
42238
- }
42239
- }
42240
- const snapshot = trustedProject ? taskMutationSnapshot(existing) : void 0;
42241
- 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;
42242
- if (!updated) {
42768
+ const statusUpdates = { status: input.status };
42769
+ if (input.status === "completed" && input.result !== void 0) statusUpdates.result = input.result;
42770
+ if (input.status === "failed" && input.failureReason !== void 0) statusUpdates.failureReason = input.failureReason;
42771
+ if (input.summary !== void 0) statusUpdates.summary = input.summary;
42772
+ const statusSnapshot = trustedProject ? taskMutationSnapshot(existing) : void 0;
42773
+ taskUpdateDebug("set_status:write", {
42774
+ status: input.status,
42775
+ useCas: !!statusSnapshot
42776
+ });
42777
+ const updatedStatus = await (statusSnapshot ? store.updateIfSnapshot(tenantId2, input.id, statusUpdates, statusSnapshot) : store.update(tenantId2, input.id, statusUpdates));
42778
+ taskUpdateDebug("set_status:result", {
42779
+ updated: !!updatedStatus,
42780
+ resultStatus: updatedStatus?.status
42781
+ });
42782
+ if (!updatedStatus) {
42783
+ taskUpdateBlocked(input.id, "TASK_UPDATE_FAILED", {});
42243
42784
  return JSON.stringify({
42244
42785
  success: false,
42245
- error: `Failed to update task '${input.id}'`,
42246
- hint: "The task may have been deleted or the ID is incorrect"
42786
+ code: "TASK_UPDATE_FAILED",
42787
+ error: `Failed to update status of task '${input.id}'.`,
42788
+ hint: "Use manage_task get to verify the task exists, then retry."
42247
42789
  });
42248
42790
  }
42249
- if (Object.keys(updates).length > 0) markMutation();
42250
- if (reconciledTask && Object.keys(updates).length === 0) {
42251
- return successfulTaskResponse(input.id, updated, descriptionMutated, reconciliationMetadata);
42252
- }
42253
- if (Object.keys(updates).length === 0) {
42254
- return successfulTaskResponse(input.id, updated, false);
42255
- }
42256
- const actionMap = {
42257
- pending: "pending",
42258
- in_progress: "started",
42259
- failed: "failed",
42260
- interrupted: "interrupted",
42261
- completed: "completed",
42262
- cancelled: "cancelled"
42263
- };
42264
- const workItemAction = persistedStatus ? actionMap[persistedStatus] || "updated" : "updated";
42265
- const workItemSummary = input.summary || (persistedStatus ? `Status changed to ${persistedStatus}` : void 0);
42266
- let activityWarnings;
42791
+ markMutation();
42267
42792
  try {
42268
- if (trustedProject) {
42269
- try {
42270
- await createProjectTaskOwnerPolicy().requireCaller(trustedRosterScope(trustedProject));
42271
- } catch (error) {
42272
- return projectPolicyError(error);
42273
- }
42274
- }
42275
- await workItemStore.create({
42793
+ await statusWorkItemStore.create({
42276
42794
  taskId: input.id,
42277
42795
  tenantId: tenantId2,
42278
- action: workItemAction,
42796
+ action: input.status,
42279
42797
  actor,
42280
- threadId: trustedProject?.projectTask?.threadId ?? (trustedProject?.projectRoom ? void 0 : input.sourceId),
42281
- summary: workItemSummary,
42282
- detail: {
42283
- ...input.result !== void 0 && { result: input.result },
42284
- ...input.failureReason !== void 0 && { failureReason: input.failureReason }
42285
- },
42286
- workspaceId: updated.workspaceId,
42287
- projectId: updated.projectId
42798
+ threadId: statusThreadId ?? input.sourceId,
42799
+ summary: input.summary || `Status changed to ${input.status}`,
42800
+ workspaceId: updatedStatus.workspaceId,
42801
+ projectId: updatedStatus.projectId
42288
42802
  });
42289
- } catch (error) {
42290
- activityWarnings = [{
42291
- code: "TASK_UPDATE_ACTIVITY_WRITE_FAILED",
42292
- taskId: input.id,
42293
- error: error instanceof Error ? error.message : String(error)
42294
- }];
42803
+ } catch {
42295
42804
  }
42296
- const lifecycleWarnings = Array.isArray(reconciliationMetadata.warnings) ? reconciliationMetadata.warnings : void 0;
42297
- return successfulTaskResponse(input.id, updated, true, {
42298
- ...reconciliationMetadata,
42299
- warnings: mergeTaskUpdateWarnings(lifecycleWarnings, activityWarnings)
42300
- });
42805
+ return successfulTaskResponse(input.id, updatedStatus, true);
42301
42806
  }
42302
42807
  case "delete": {
42303
42808
  if (!input.id) {
@@ -42314,8 +42819,9 @@ function createTaskMiddleware(options = {}) {
42314
42819
  if (!existing) {
42315
42820
  return JSON.stringify({
42316
42821
  success: false,
42317
- error: `Task '${input.id}' not found`,
42318
- hint: "Use list to see available tasks and their IDs"
42822
+ code: "TASK_NOT_FOUND",
42823
+ error: `Task '${input.id}' was not found.`,
42824
+ hint: "Use manage_task list to see available tasks and their correct IDs."
42319
42825
  });
42320
42826
  }
42321
42827
  if (!taskMatchesRuntimeScope3(existing, workspaceId, projectId)) {
@@ -42341,8 +42847,9 @@ function createTaskMiddleware(options = {}) {
42341
42847
  if (!deleted) {
42342
42848
  return JSON.stringify({
42343
42849
  success: false,
42344
- error: `Task '${input.id}' not found or could not be deleted`,
42345
- hint: "Use list to verify the task exists"
42850
+ code: "TASK_DELETE_FAILED",
42851
+ error: `Task '${input.id}' was not found or could not be deleted.`,
42852
+ hint: "Use manage_task list to verify the task exists and you have access to it."
42346
42853
  });
42347
42854
  }
42348
42855
  return JSON.stringify({ success: true, message: `Task '${input.id}' deleted` });
@@ -42357,7 +42864,7 @@ function createTaskMiddleware(options = {}) {
42357
42864
  }
42358
42865
  };
42359
42866
  const reconcileExplicitExecutionResult = async (input, config, response, validationError, didMutate) => {
42360
- if (input.action !== "update" && input.action !== "add_activity" || !input.id) return response;
42867
+ if (input.action !== "update" && input.action !== "set_status" && input.action !== "add_activity" || !input.id) return response;
42361
42868
  const authority = classifyTaskAuthority(getRunConfig2(config));
42362
42869
  if (authority.kind === "projectRoom" || authority.kind === "projectTask" || authority.kind === "invalid") {
42363
42870
  return response;
@@ -42551,14 +43058,18 @@ ${TASK_CONVERGENCE_GUIDANCE}
42551
43058
  - Explicit ownerId: assign to a specific agent (cross-agent delegation)
42552
43059
 
42553
43060
  ## Actions
42554
- - create: Create a task (title required; result required for completed; failureReason required for failed)
43061
+ - create: Create a task (title required)
42555
43062
  - get: Get one task and its newest 100 activities (id required)
42556
43063
  - list: List tasks, filterable by ownerType/status/priority/parentId
42557
- - update: Update a task (id required; pass only the fields to change)
42558
- To mark complete: update with status='completed', result='...' (a nonblank input or existing result is required)
42559
- To mark failed: update with status='failed', failureReason='...' (a nonblank input or existing failureReason is required)
42560
- To pause for missing input: update with status='interrupted', summary='what is needed' (defaults to interruption type missing_input)
42561
- Status transitions are validated \u2014 only allowed transitions will succeed.
43064
+ - update: Update a task's content fields (title/description/priority/dueDate/metadata/parentId/sourceId/context/ownerId/result/failureReason/requireReview/dependencies/files)
43065
+ Status changes can also be done here, but use set_status for a simpler dedicated status update.
43066
+ - set_status: Change a task's status (id and status required). Use this when you only need to change the status.
43067
+ Valid status values: pending, in_progress, review, failed, interrupted, completed, cancelled
43068
+ Companion fields: result (for completed), failureReason (for failed), summary (for any status)
43069
+ To mark complete: set_status with status='completed', result='...'
43070
+ To mark failed: set_status with status='failed', failureReason='...'
43071
+ To pause for input: set_status with status='interrupted', summary='what is needed'
43072
+ Status can be freely changed to any valid value \u2014 no restrictions on transitions.
42562
43073
  - add_activity: Add a Markdown activity without changing task fields or status (id and content required)
42563
43074
  - delete: Delete a task (id required)
42564
43075
 
@@ -42738,7 +43249,12 @@ manage_eval calls in this skill and in [[eval-verify]]'s Setup step 0 \u2014
42738
43249
  [[eval-verify]] loads this skill and inherits its confirmation requirement.
42739
43250
 
42740
43251
  ## Project Structure
42741
- 1. Project: one per agent-under-test. Create with manage_eval create_project.
43252
+ 1. Project: one per agent-under-test. A NEW project is created through [[eval-verify]]
43253
+ Setup, which owns project organization: it resolves the exact subject agent-id,
43254
+ checks whether \`eval-{agent-id}\` already exists (read_eval list_projects), reuses it
43255
+ when it does, and confirms with the user before \`manage_eval create_project\`
43256
+ (which requires \`evalVerifyLoaded: true\`). Do NOT call create_project directly from
43257
+ this skill \u2014 load [[eval-verify]] to create a project.
42742
43258
  2. Suite: one per capability/domain. Create with manage_eval create_suite.
42743
43259
  3. Case: user input \u2192 agent steps \u2192 expected output \u2192 rubrics. Create with manage_eval create_case.
42744
43260
 
@@ -43031,6 +43547,15 @@ ACTIONS:
43031
43547
  }
43032
43548
  );
43033
43549
  }
43550
+ function requireEvalVerifyDeclaration(evalVerifyLoaded) {
43551
+ if (evalVerifyLoaded !== true) {
43552
+ return JSON.stringify({
43553
+ success: false,
43554
+ 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."
43555
+ });
43556
+ }
43557
+ return void 0;
43558
+ }
43034
43559
  function createManageEvalTool() {
43035
43560
  const schema6 = import_zod69.z.object({
43036
43561
  action: import_zod69.z.enum([
@@ -43049,6 +43574,7 @@ function createManageEvalTool() {
43049
43574
  description: import_zod69.z.string().optional(),
43050
43575
  judgeModelKey: import_zod69.z.string().optional(),
43051
43576
  concurrency: import_zod69.z.number().optional(),
43577
+ evalVerifyLoaded: import_zod69.z.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."),
43052
43578
  targetAgentId: import_zod69.z.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."),
43053
43579
  suiteId: import_zod69.z.string().optional(),
43054
43580
  caseId: import_zod69.z.string().optional(),
@@ -43074,6 +43600,8 @@ function createManageEvalTool() {
43074
43600
  let data;
43075
43601
  switch (input.action) {
43076
43602
  case "create_project": {
43603
+ const skillError = requireEvalVerifyDeclaration(input.evalVerifyLoaded);
43604
+ if (skillError) return skillError;
43077
43605
  const ctx = workspaceContext(exeConfig);
43078
43606
  data = await store.createProject(tid, (0, import_uuid12.v4)(), {
43079
43607
  name: input.name,
@@ -43154,7 +43682,12 @@ function createManageEvalTool() {
43154
43682
  name: "manage_eval",
43155
43683
  description: `Create, update, delete evaluation projects, suites, and test cases.
43156
43684
 
43157
- Project: create_project(name, description?, judgeModelKey?, concurrency?, targetAgentId?) | update_project | delete_project
43685
+ Project: create_project(name, description?, judgeModelKey?, concurrency?, targetAgentId?, evalVerifyLoaded?) | update_project | delete_project
43686
+ create_project REQUIRES evalVerifyLoaded: true \u2014 load the 'eval-verify' skill first
43687
+ (skill tool, skill_name 'eval-verify'). eval-verify owns project organization:
43688
+ resolve the exact subject agent-id, check whether eval-{agent-id} already exists via
43689
+ read_eval list_projects, reuse it when it does, confirm with the user when it does not,
43690
+ and always pass targetAgentId. Calls without evalVerifyLoaded: true fail.
43158
43691
  judgeModelKey defaults to the "default" model. concurrency defaults to 3.
43159
43692
  delete_project rejected if active runs exist.
43160
43693
  **When creating a project from within a workspace, the workspace/project context is
@@ -43509,6 +44042,10 @@ Selected target eval subjects:
43509
44042
  - Orchestra: component evals first, then integration eval
43510
44043
  - Workflow: validate_workflow first, then workflow eval covering branch paths, intermediate contracts, ask points, recovery
43511
44044
  - In Bounded mode: subject is always the exact bound target ID
44045
+ The eval project belongs to the TARGET agent, not to this round. The subject identity is
44046
+ fixed per target; across rounds and fix passes of the same target, [[eval-verify]] Setup
44047
+ resolves the canonical eval-{target-id} project and REUSES it \u2014 never create a new eval
44048
+ project for a new round of the same agent.
43512
44049
 
43513
44050
  ### 7. Deliver
43514
44051