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