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