@lazyingart/agintiflow 0.20.296 → 0.20.298

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyingart/agintiflow",
3
- "version": "0.20.296",
3
+ "version": "0.20.298",
4
4
  "type": "module",
5
5
  "description": "AgInTiFlow is a project-aware agent workspace for hybrid wet-dry R&D, hardware-aware intelligence, software automation, and industrial workflows.",
6
6
  "license": "Apache-2.0",
@@ -5800,6 +5800,167 @@ try {
5800
5800
  evaluateScsEvidence(privateEvidenceContract, privateEvidenceLedger).ok,
5801
5801
  "ignored private verification evidence invalidated a successful canonical build"
5802
5802
  );
5803
+ const scopedTaskRoot = path.join(workspace, "output", "agent-task-1");
5804
+ const scopedTaskGoal = [
5805
+ "Create and verify output/result.json.",
5806
+ `AGINTI_EVIDENCE_SCOPE_JSON: ${JSON.stringify({
5807
+ mode: "task",
5808
+ request: "Create and verify output/result.json.",
5809
+ artifact_root: scopedTaskRoot,
5810
+ })}`,
5811
+ ].join("\n");
5812
+ const scopedTaskState = {
5813
+ goal: scopedTaskGoal,
5814
+ commandCwd: workspace,
5815
+ meta: {
5816
+ goalContract: {
5817
+ revision: 1,
5818
+ taskGoal: scopedTaskGoal,
5819
+ currentRequest: "Create and verify output/result.json.",
5820
+ },
5821
+ },
5822
+ };
5823
+ recordProjectVerificationOutcome(
5824
+ scopedTaskState,
5825
+ {
5826
+ toolName: "write_file",
5827
+ ok: true,
5828
+ path: "output/result.json",
5829
+ changes: [
5830
+ {
5831
+ path: "output/result.json",
5832
+ created: true,
5833
+ beforeHash: "",
5834
+ afterHash: "requested-output",
5835
+ },
5836
+ ],
5837
+ },
5838
+ { commandCwd: workspace, taskProfile: "auto" }
5839
+ );
5840
+ const requestedOutputRevision =
5841
+ scopedTaskState.meta.projectVerification?.mutationRevision;
5842
+ const readOnlyArtifactValidation = {
5843
+ toolName: "run_command",
5844
+ ok: true,
5845
+ exitCode: 0,
5846
+ args: {
5847
+ command:
5848
+ "python3 -c \"import json; d=json.load(open('output/result.json')); assert d; print('PASS valid JSON')\"",
5849
+ },
5850
+ projectMutationPaths: [],
5851
+ stdout: "PASS valid JSON\n",
5852
+ stderr: "",
5853
+ };
5854
+ recordProjectVerificationOutcome(
5855
+ scopedTaskState,
5856
+ readOnlyArtifactValidation,
5857
+ {
5858
+ commandCwd: workspace,
5859
+ taskProfile: "auto",
5860
+ allowShellTool: true,
5861
+ sandboxMode: "host",
5862
+ }
5863
+ );
5864
+ recordProjectVerificationOutcome(
5865
+ scopedTaskState,
5866
+ {
5867
+ toolName: "write_file",
5868
+ ok: true,
5869
+ path: path.join(scopedTaskRoot, "agent-result.json"),
5870
+ changes: [
5871
+ {
5872
+ path: path.join(scopedTaskRoot, "agent-result.json"),
5873
+ created: true,
5874
+ beforeHash: "",
5875
+ afterHash: "delivery-manifest",
5876
+ },
5877
+ ],
5878
+ },
5879
+ { commandCwd: workspace, taskProfile: "auto" }
5880
+ );
5881
+ const scopedManifestCommand = {
5882
+ toolName: "run_command",
5883
+ ok: true,
5884
+ exitCode: 0,
5885
+ args: {
5886
+ command: `printf '%s\\n' '{}' > ${JSON.stringify(
5887
+ path.join(scopedTaskRoot, "agent-result.json")
5888
+ )}`,
5889
+ },
5890
+ stdout: "",
5891
+ stderr: "",
5892
+ };
5893
+ recordProjectVerificationOutcome(
5894
+ scopedTaskState,
5895
+ scopedManifestCommand,
5896
+ {
5897
+ commandCwd: workspace,
5898
+ taskProfile: "auto",
5899
+ allowShellTool: true,
5900
+ sandboxMode: "host",
5901
+ }
5902
+ );
5903
+ const pythonScopedManifestCommand = {
5904
+ toolName: "run_command",
5905
+ ok: true,
5906
+ exitCode: 0,
5907
+ args: {
5908
+ command: [
5909
+ "python3 - <<'PY'",
5910
+ `out = ${JSON.stringify(path.join(scopedTaskRoot, "agent-result.json"))}`,
5911
+ "with open(out, 'w', encoding='utf-8') as handle:",
5912
+ " handle.write('{}')",
5913
+ "PY",
5914
+ ].join("\n"),
5915
+ },
5916
+ projectMutationPaths: [],
5917
+ stdout: "",
5918
+ stderr: "",
5919
+ };
5920
+ recordProjectVerificationOutcome(
5921
+ scopedTaskState,
5922
+ pythonScopedManifestCommand,
5923
+ {
5924
+ commandCwd: workspace,
5925
+ taskProfile: "auto",
5926
+ allowShellTool: true,
5927
+ sandboxMode: "host",
5928
+ }
5929
+ );
5930
+ assert(
5931
+ requestedOutputRevision === 1 &&
5932
+ scopedTaskState.meta.projectVerification?.mutationRevision === 1 &&
5933
+ readOnlyArtifactValidation.readOnlyArtifactValidation === true &&
5934
+ scopedManifestCommand.scopedTaskArtifactWrite === true &&
5935
+ pythonScopedManifestCommand.scopedTaskArtifactWrite === true,
5936
+ "task-scoped delivery bookkeeping invalidated requested artifact evidence"
5937
+ );
5938
+ const mutatingInlinePython = {
5939
+ toolName: "run_command",
5940
+ ok: true,
5941
+ exitCode: 0,
5942
+ args: {
5943
+ command: "python3 -c \"open('output/side.txt','w').write('changed')\"",
5944
+ },
5945
+ projectMutationPaths: [],
5946
+ stdout: "",
5947
+ stderr: "",
5948
+ };
5949
+ recordProjectVerificationOutcome(
5950
+ scopedTaskState,
5951
+ mutatingInlinePython,
5952
+ {
5953
+ commandCwd: workspace,
5954
+ taskProfile: "auto",
5955
+ allowShellTool: true,
5956
+ sandboxMode: "host",
5957
+ }
5958
+ );
5959
+ assert(
5960
+ scopedTaskState.meta.projectVerification?.mutationRevision === 2 &&
5961
+ mutatingInlinePython.readOnlyArtifactValidation !== true,
5962
+ "write-capable inline Python was incorrectly classified as read-only validation"
5963
+ );
5803
5964
  const privateVerifierCommand =
5804
5965
  "python3 .aginti/verification/lifecycle/smoke_test.py";
5805
5966
  const privateVerifierState = {
@@ -8080,6 +8241,34 @@ try {
8080
8241
  sourceGroundingRefreshState.meta.activeExecutionContract.requiresSourceGrounding === true,
8081
8242
  "a concrete file correction did not require current source grounding"
8082
8243
  );
8244
+ const scopedStaticArtifactRequest =
8245
+ "Create and verify output/parity/result.json with one JSON object. Read it back and finish.";
8246
+ const scopedStaticArtifactGoal = [
8247
+ "You are the persistent LabCanvas agent.",
8248
+ `AGINTI_EVIDENCE_SCOPE_JSON: ${JSON.stringify({
8249
+ mode: "task",
8250
+ request: scopedStaticArtifactRequest,
8251
+ artifact_root: "/tmp/labcanvas-task-1",
8252
+ })}`,
8253
+ "Read AGENTS.md and inspect implementation before editing.",
8254
+ ].join("\n\n");
8255
+ const scopedStaticArtifactState = {
8256
+ goal: scopedStaticArtifactGoal,
8257
+ meta: {
8258
+ taskProfile: "auto",
8259
+ goalContract: {
8260
+ revision: 10,
8261
+ currentRequest: scopedStaticArtifactGoal,
8262
+ },
8263
+ projectVerification: { mutationRevision: 0 },
8264
+ },
8265
+ };
8266
+ resetSameTaskExecutionContract(scopedStaticArtifactState, 10);
8267
+ assert(
8268
+ scopedStaticArtifactState.meta.activeExecutionContract.requiresFileMutation === true &&
8269
+ scopedStaticArtifactState.meta.activeExecutionContract.requiresSourceGrounding === false,
8270
+ "LabCanvas wrapper instructions leaked into the scoped static-artifact execution contract"
8271
+ );
8083
8272
  const concreteBudgetConfig = { maxSteps: 81, resetStepBudget: false };
8084
8273
  assert(
8085
8274
  applyConcreteContinuationStepBudgetBoundary(
@@ -11426,6 +11615,29 @@ try {
11426
11615
  );
11427
11616
  assert(artifactProgress.justActivated, "exact output mutation did not activate artifact validation");
11428
11617
  assert(artifactState.meta.artifactProgress.complete, "exact output progress was not persisted");
11618
+ const commandArtifactState = {
11619
+ meta: {
11620
+ scs: {
11621
+ taskContract: {
11622
+ exactOutputPaths: ["output/generated.json"],
11623
+ },
11624
+ },
11625
+ },
11626
+ };
11627
+ const commandArtifactProgress = recordExactOutputProgress(
11628
+ commandArtifactState,
11629
+ {
11630
+ ok: true,
11631
+ toolName: "run_command",
11632
+ verifiedGeneratedOutputPaths: ["output/generated.json"],
11633
+ },
11634
+ { commandCwd: workspace }
11635
+ );
11636
+ assert(
11637
+ commandArtifactProgress.justActivated &&
11638
+ commandArtifactState.meta.artifactProgress.complete,
11639
+ "snapshot-verified shell output did not activate exact artifact progress"
11640
+ );
11429
11641
  assert(
11430
11642
  nextStepRuntimeConfig({ provider: "localllm" }, artifactState).artifactValidationPhase === true,
11431
11643
  "next step did not enter artifact validation mode"
@@ -15,6 +15,8 @@ import {
15
15
  } from "../src/context-budget-controller.js";
16
16
  import { maxStepsForExecutionPolicy, selectExecutionPolicy } from "../src/execution-policy.js";
17
17
  import { SessionStore } from "../src/session-store.js";
18
+ import { selectSkillsForGoal } from "../src/skill-library.js";
19
+ import { scopedChatopsEvidenceGoal } from "../src/scs-evidence.js";
18
20
 
19
21
  const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
20
22
  const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "agintiflow-execution-policy-"));
@@ -97,6 +99,46 @@ try {
97
99
  assert(responseOnlyConfig.executionTier === "focused", "response-only runtime did not select focused execution");
98
100
  assert(!responseOnlyConfig.executionPolicy.requiresPlan, "response-only runtime still requires a plan");
99
101
 
102
+ const scopedArtifactGoal = [
103
+ "LabCanvas host wrapper: use browser automation, deep research, source ingestion, and recovery skills when relevant.",
104
+ "General host guidance: inspect complex repositories carefully and create a plan for difficult work.",
105
+ `AGINTI_EVIDENCE_SCOPE_JSON: ${JSON.stringify({
106
+ mode: "task",
107
+ request: "Create and verify output/parity/scoped-artifact.json with one valid JSON object.",
108
+ })}`,
109
+ ].join("\n");
110
+ const scopedArtifactConfig = resolveRuntimeConfig(
111
+ {
112
+ provider: "mock",
113
+ routingMode: "smart",
114
+ model: "mock-agent",
115
+ goal: scopedArtifactGoal,
116
+ taskProfile: "auto",
117
+ enableScs: "auto",
118
+ },
119
+ {
120
+ baseDir: runtimeDir,
121
+ packageDir: repoRoot,
122
+ provider: "mock",
123
+ routingMode: "smart",
124
+ model: "mock-agent",
125
+ }
126
+ );
127
+ assert(
128
+ scopedArtifactConfig.executionTier === "focused" && !scopedArtifactConfig.executionPolicy.requiresPlan,
129
+ "host wrapper keywords inflated a scoped static-artifact task into planned execution"
130
+ );
131
+ assert(!scopedArtifactConfig.scsActive, "host wrapper keywords incorrectly activated SCS for a scoped static artifact");
132
+ const scopedSkillIds = selectSkillsForGoal(
133
+ scopedChatopsEvidenceGoal(scopedArtifactGoal, "auto"),
134
+ { taskProfile: "auto", limit: 6, projectRoot: workspace }
135
+ ).map((skill) => skill.id);
136
+ assert(scopedSkillIds.includes("structured-json"), "scoped JSON task did not retain its relevant structured-data skill");
137
+ assert(
138
+ !scopedSkillIds.some((id) => ["source-ingestion", "autonomous-artifact-pipeline", "wechat-labcanvas-chatops"].includes(id)),
139
+ `literal paths or payload values selected unrelated skills: ${scopedSkillIds.join(", ")}`
140
+ );
141
+
100
142
  const simpleConfig = resolveRuntimeConfig(
101
143
  {
102
144
  provider: "mock",
@@ -807,6 +807,17 @@ const readinessOnlyContract = deriveScsTaskContract({
807
807
  ].join("\n"),
808
808
  taskProfile: "auto",
809
809
  });
810
+ const releaseProofFilenameContract = deriveScsTaskContract({
811
+ goal: [
812
+ 'AGINTI_EVIDENCE_SCOPE_JSON: {"mode":"task","request":"Create a task-owned plain-text artifact named scoped-path-release-proof.txt, read it back, and finish without external actions."}',
813
+ "The host writes a final task record after the agent finishes.",
814
+ ].join("\n"),
815
+ taskProfile: "auto",
816
+ });
817
+ const explicitPackageReleaseContract = deriveScsTaskContract({
818
+ goal: "Release the package to npm and verify the published version.",
819
+ taskProfile: "auto",
820
+ });
810
821
  assert(
811
822
  readinessOnlyContract.requiredEvidence.some((item) => item.category === "file") &&
812
823
  readinessOnlyContract.requiredEvidence.some((item) => item.category === "command"),
@@ -816,6 +827,15 @@ assert(
816
827
  !readinessOnlyContract.requiredEvidence.some((item) => ["publish", "browser", "visual", "artifact"].includes(item.category)),
817
828
  "read-only readiness contract required a forbidden external action or target artifact"
818
829
  );
830
+ assert(
831
+ releaseProofFilenameContract.requiredEvidence.some((item) => item.category === "file") &&
832
+ !releaseProofFilenameContract.requiredEvidence.some((item) => item.category === "publish"),
833
+ "a release-like artifact filename fabricated an external publication obligation"
834
+ );
835
+ assert(
836
+ explicitPackageReleaseContract.requiredEvidence.some((item) => item.category === "publish"),
837
+ "an explicit package release lost its publication evidence obligation"
838
+ );
819
839
  assert(
820
840
  uploadContract.exactInputPaths.includes("/tmp/reference-a.png") &&
821
841
  uploadContract.exactInputPaths.includes("/tmp/reference-b.png") &&
@@ -1131,6 +1151,13 @@ const canvasArtifactContract = deriveScsTaskContract({
1131
1151
  const jsonObjectContract = deriveScsTaskContract({
1132
1152
  goal: "Extract a valid JSON object with schema from this text.",
1133
1153
  });
1154
+ const scopedStaticJsonContract = deriveScsTaskContract({
1155
+ goal: [
1156
+ "Host wrapper: validate code, run commands, inspect browsers, and perform deep research when the request needs them.",
1157
+ 'AGINTI_EVIDENCE_SCOPE_JSON: {"mode":"task","request":"Create and verify output/parity/aginti_verified_artifact.json with one valid JSON object, then read it back."}',
1158
+ ].join("\n"),
1159
+ taskProfile: "code",
1160
+ });
1134
1161
  const virtualFileContract = deriveScsTaskContract({
1135
1162
  goal: "Create file: /workspace/virtual-output.txt with virtual Docker path support.",
1136
1163
  });
@@ -1210,6 +1237,20 @@ assert(
1210
1237
  !jsonObjectContract.requiredEvidence.some((item) => item.category === "file"),
1211
1238
  "JSON object extraction should not be treated as a workspace file requirement without an explicit file/path"
1212
1239
  );
1240
+ assert(
1241
+ scopedStaticJsonContract.requiredEvidence.some((item) => item.category === "file") &&
1242
+ !scopedStaticJsonContract.requiredEvidence.some((item) => item.category === "command"),
1243
+ "a scoped standalone JSON artifact should require file evidence without an unrelated shell command"
1244
+ );
1245
+ assert(
1246
+ scopedStaticJsonContract.exactOutputPaths.includes("output/parity/aginti_verified_artifact.json") &&
1247
+ !scopedStaticJsonContract.exactInputPaths.includes("output/parity/aginti_verified_artifact.json"),
1248
+ "create-and-verify should retain the structured-data path as an output rather than an input"
1249
+ );
1250
+ assert(
1251
+ scopedStaticJsonContract.requiredExecutableTerms.length === 0,
1252
+ "a required JSON boolean value was misclassified as executable production-source syntax"
1253
+ );
1213
1254
  assert(
1214
1255
  virtualFileContract.requiredEvidence.some((item) => item.category === "file") &&
1215
1256
  !virtualFileContract.requiredEvidence.some((item) => item.category === "artifact"),
@@ -364,11 +364,14 @@ function textToolRetryInstruction(response) {
364
364
 
365
365
  function buildScsRuntimeContext(config = {}, state = {}, extra = {}) {
366
366
  const projectRoot = config.commandCwd || config.baseDir || process.cwd();
367
- const selectedSkills = isRetainedWorkspaceProfile(config) ? [] : selectSkillsForGoal(state.goal || config.goal || "", {
368
- taskProfile: config.taskProfile,
369
- limit: 6,
370
- projectRoot,
371
- });
367
+ const selectedSkills = isRetainedWorkspaceProfile(config) ? [] : selectSkillsForGoal(
368
+ scopedChatopsEvidenceGoal(state.goal || config.goal || "", config.taskProfile),
369
+ {
370
+ taskProfile: config.taskProfile,
371
+ limit: 6,
372
+ projectRoot,
373
+ }
374
+ );
372
375
  const verification = state.meta?.projectVerification || {};
373
376
  const mutationRevision = Number(verification.mutationRevision || 0);
374
377
  const privateMutationRevision = Number(verification.privateMutationRevision || 0);
@@ -435,11 +438,14 @@ function buildScsRuntimeContext(config = {}, state = {}, extra = {}) {
435
438
 
436
439
  function withSelectedSkillReadOnlyRoots(config = {}, state = {}) {
437
440
  const projectRoot = config.commandCwd || config.baseDir || process.cwd();
438
- const selectedSkills = isRetainedWorkspaceProfile(config) ? [] : selectSkillsForGoal(state.goal || config.goal || "", {
439
- taskProfile: config.taskProfile,
440
- limit: 6,
441
- projectRoot,
442
- });
441
+ const selectedSkills = isRetainedWorkspaceProfile(config) ? [] : selectSkillsForGoal(
442
+ scopedChatopsEvidenceGoal(state.goal || config.goal || "", config.taskProfile),
443
+ {
444
+ taskProfile: config.taskProfile,
445
+ limit: 6,
446
+ projectRoot,
447
+ }
448
+ );
443
449
  const skillReadOnlyRoots = [
444
450
  ...(Array.isArray(config.skillReadOnlyRoots) ? config.skillReadOnlyRoots : []),
445
451
  ...selectedSkills.map((skill) => skill.path).filter(Boolean),
@@ -2891,7 +2897,11 @@ async function createInitialState(config, sessionId) {
2891
2897
  const projectRoot = config.commandCwd || config.baseDir || process.cwd();
2892
2898
  const selectedSkills = isRetainedWorkspaceProfile(config)
2893
2899
  ? []
2894
- : selectSkillsForGoal(config.goal, { taskProfile: config.taskProfile, limit: 6, projectRoot });
2900
+ : selectSkillsForGoal(scopedChatopsEvidenceGoal(config.goal, config.taskProfile), {
2901
+ taskProfile: config.taskProfile,
2902
+ limit: 6,
2903
+ projectRoot,
2904
+ });
2895
2905
  const skillContext = formatSkillsForPrompt(selectedSkills);
2896
2906
  const projectInstructions = await readProjectInstructions(config.baseDir || config.commandCwd || process.cwd());
2897
2907
  const projectInstructionContext = formatProjectInstructions(projectInstructions);
@@ -4050,9 +4060,12 @@ export function resetSameTaskExecutionContract(state = {}, revision = 0) {
4050
4060
  refreshPersistedProjectAcceptance(state);
4051
4061
  const activeRevision = Math.max(0, Number(revision || state.meta?.goalContract?.revision || 0));
4052
4062
  const currentRequest = String(state.meta?.goalContract?.currentRequest || state.goal || "").trim();
4063
+ const taskProfile = state.meta?.taskProfile || "auto";
4064
+ const scopedCurrentRequest =
4065
+ scopedChatopsEvidenceGoal(currentRequest, taskProfile) || currentRequest;
4053
4066
  const currentTurnContract = deriveScsTaskContract({
4054
- goal: currentRequest,
4055
- taskProfile: state.meta?.taskProfile || "auto",
4067
+ goal: scopedCurrentRequest,
4068
+ taskProfile,
4056
4069
  });
4057
4070
  const currentTurnCommands = normalizedRequiredProjectCommands(
4058
4071
  currentTurnContract.requiredProjectCommands
@@ -4094,7 +4107,7 @@ export function resetSameTaskExecutionContract(state = {}, revision = 0) {
4094
4107
  currentTurnContract.requiresSourceGrounding === true ||
4095
4108
  (
4096
4109
  currentTurnContract.requiresFileMutation === true &&
4097
- goalClearlyAllowsOverwrite(currentRequest)
4110
+ goalClearlyAllowsOverwrite(scopedCurrentRequest)
4098
4111
  )
4099
4112
  ),
4100
4113
  requiredProjectCommands: currentTurnCommands,
@@ -4208,7 +4221,11 @@ async function applyContinuationPrompt(state, config, observers) {
4208
4221
  const projectRoot = config.commandCwd || config.baseDir || process.cwd();
4209
4222
  const selectedSkills = isRetainedWorkspaceProfile(config)
4210
4223
  ? []
4211
- : selectSkillsForGoal(config.goal, { taskProfile: config.taskProfile, limit: 6, projectRoot });
4224
+ : selectSkillsForGoal(scopedChatopsEvidenceGoal(config.goal, config.taskProfile), {
4225
+ taskProfile: config.taskProfile,
4226
+ limit: 6,
4227
+ projectRoot,
4228
+ });
4212
4229
  const skillContext = formatSkillsForPrompt(selectedSkills);
4213
4230
  const projectInstructions = await readProjectInstructions(config.baseDir || config.commandCwd || process.cwd());
4214
4231
  state.meta = state.meta || {};
@@ -6384,9 +6401,9 @@ export function recordProjectVerificationOutcome(state = {}, toolResult = {}, co
6384
6401
  }
6385
6402
  }
6386
6403
 
6387
- const projectMutationPaths = successfulProjectMutationPaths(toolResult);
6404
+ const projectMutationPaths = successfulProjectMutationPaths(toolResult, state, config);
6388
6405
  const privateMutationPaths = successfulPrivateVerificationMutationPaths(toolResult);
6389
- const materialMutationPaths = materialProjectMutationPaths(toolResult);
6406
+ const materialMutationPaths = materialProjectMutationPaths(toolResult, state, config);
6390
6407
  if (["write_file", "apply_patch"].includes(toolName) && privateMutationPaths.length) {
6391
6408
  delete state.meta.verifiedCompletionCandidate;
6392
6409
  verification.privateMutationRevision += 1;
@@ -6549,12 +6566,23 @@ export function recordProjectVerificationOutcome(state = {}, toolResult = {}, co
6549
6566
  config,
6550
6567
  { verificationCommand }
6551
6568
  );
6569
+ const scopedTaskArtifactWrite = commandWritesOnlyScopedTaskArtifacts(
6570
+ mutationCommand,
6571
+ state,
6572
+ config
6573
+ );
6574
+ const readOnlyArtifactValidation = Boolean(
6575
+ commandMutationPaths.length === 0 &&
6576
+ commandIsBoundedReadOnlyArtifactValidation(mutationCommand)
6577
+ );
6552
6578
  const projectContentMutation = Boolean(
6553
6579
  command &&
6554
6580
  toolResult.blocked !== true &&
6555
6581
  !retainedExactVerification &&
6556
6582
  !disposableGeneratedCleanup &&
6557
6583
  !disposableGeneratedVerificationSideEffects &&
6584
+ !scopedTaskArtifactWrite &&
6585
+ !readOnlyArtifactValidation &&
6558
6586
  (commandSucceeded || commandMutationPaths.length > 0) &&
6559
6587
  (
6560
6588
  commandCanMutateProjectContent(mutationCommand, commandPolicy) ||
@@ -6569,6 +6597,12 @@ export function recordProjectVerificationOutcome(state = {}, toolResult = {}, co
6569
6597
  toolResult.disposableGeneratedVerificationSideEffects = true;
6570
6598
  toolResult.disposableGeneratedVerificationPaths = commandMutationPaths;
6571
6599
  }
6600
+ if (scopedTaskArtifactWrite) {
6601
+ toolResult.scopedTaskArtifactWrite = true;
6602
+ }
6603
+ if (readOnlyArtifactValidation) {
6604
+ toolResult.readOnlyArtifactValidation = true;
6605
+ }
6572
6606
  let requiredBatch = currentRequiredCommandBatch(verification, requiredCommands);
6573
6607
  const activeExecutionContract = state.meta?.activeExecutionContract;
6574
6608
  const activeTurnCommands =
@@ -12084,7 +12118,27 @@ function isPrivateVerificationEvidencePath(value = "") {
12084
12118
  );
12085
12119
  }
12086
12120
 
12087
- function commandWritesOnlyPrivateVerificationEvidence(command = "") {
12121
+ function isScopedTaskArtifactEvidencePath(value = "", state = {}, config = {}) {
12122
+ const artifactRoot = scopedArtifactRoot(completionContractGoal(config, state));
12123
+ if (!artifactRoot) return false;
12124
+ const raw = String(value || "")
12125
+ .trim()
12126
+ .replace(/^(["'])|(["'])$/g, "")
12127
+ .replace(/\\/g, "/");
12128
+ if (!raw || raw.includes("\0") || /^https?:\/\//i.test(raw)) return false;
12129
+ const commandCwd = path.resolve(
12130
+ config.commandCwd || state.commandCwd || process.cwd()
12131
+ );
12132
+ const absoluteRoot = path.resolve(commandCwd, artifactRoot);
12133
+ const absoluteCandidate = path.resolve(commandCwd, raw);
12134
+ const relative = path.relative(absoluteRoot, absoluteCandidate);
12135
+ return Boolean(
12136
+ relative === "" ||
12137
+ (!relative.startsWith("..") && !path.isAbsolute(relative))
12138
+ );
12139
+ }
12140
+
12141
+ function commandWritesOnlyEvidenceMatching(command = "", pathMatches = () => false) {
12088
12142
  const normalized = String(command || "").trim();
12089
12143
  if (!normalized) return false;
12090
12144
  const tokens = tokenizeShellWords(normalized);
@@ -12093,27 +12147,97 @@ function commandWritesOnlyPrivateVerificationEvidence(command = "") {
12093
12147
  if (["-a", "--append"].includes(tokens[index])) index += 1;
12094
12148
  if (tokens[index] === "--") index += 1;
12095
12149
  const targets = tokens.slice(index);
12096
- return targets.length > 0 && targets.every(isPrivateVerificationEvidencePath);
12150
+ return targets.length > 0 && targets.every(pathMatches);
12097
12151
  }
12098
12152
  if (tokens[0] === "mkdir") {
12099
12153
  const targets = tokens.slice(1).filter((token) => token !== "-p" && token !== "--");
12100
- return targets.length > 0 && targets.every(isPrivateVerificationEvidencePath);
12154
+ return targets.length > 0 && targets.every(pathMatches);
12101
12155
  }
12102
12156
 
12103
- let strippedPrivateRedirect = false;
12104
- const withoutPrivateRedirects = normalized.replace(
12157
+ let strippedEvidenceRedirect = false;
12158
+ const withoutEvidenceRedirects = normalized.replace(
12105
12159
  /(^|\s)(?:\d*>>?|&>>?)\s*("[^"]+"|'[^']+'|[^\s;&|]+)/g,
12106
12160
  (match, prefix, target) => {
12107
- if (!isPrivateVerificationEvidencePath(target)) return match;
12108
- strippedPrivateRedirect = true;
12161
+ if (!pathMatches(target)) return match;
12162
+ strippedEvidenceRedirect = true;
12109
12163
  return prefix;
12110
12164
  }
12111
12165
  ).trim();
12112
- if (!strippedPrivateRedirect || !withoutPrivateRedirects) return false;
12113
- const underlyingPolicy = classifyCommand(withoutPrivateRedirects);
12166
+ if (!strippedEvidenceRedirect || !withoutEvidenceRedirects) return false;
12167
+ const underlyingPolicy = classifyCommand(withoutEvidenceRedirects);
12114
12168
  return underlyingPolicy.writesWorkspace !== true && underlyingPolicy.mayMutateProject !== true;
12115
12169
  }
12116
12170
 
12171
+ function commandWritesOnlyPrivateVerificationEvidence(command = "") {
12172
+ return commandWritesOnlyEvidenceMatching(
12173
+ command,
12174
+ isPrivateVerificationEvidencePath
12175
+ );
12176
+ }
12177
+
12178
+ function commandWritesOnlyScopedTaskArtifacts(command = "", state = {}, config = {}) {
12179
+ if (commandWritesOnlyEvidenceMatching(
12180
+ command,
12181
+ (candidate) => isScopedTaskArtifactEvidencePath(candidate, state, config)
12182
+ )) {
12183
+ return true;
12184
+ }
12185
+ const normalized = String(command || "").trim();
12186
+ const artifactRoot = scopedArtifactRoot(completionContractGoal(config, state));
12187
+ if (!normalized || !artifactRoot) return false;
12188
+ const assignments = new Map();
12189
+ for (const match of normalized.matchAll(
12190
+ /\b([A-Za-z_]\w*)\s*=\s*("[^"\n]+"|'[^'\n]+')/g
12191
+ )) {
12192
+ assignments.set(match[1], match[2]);
12193
+ }
12194
+ const writeTargets = [];
12195
+ for (const match of normalized.matchAll(
12196
+ /\bopen\s*\(\s*([^,\n]+)\s*,\s*(["'][^"']*[wax+][^"']*["'])/gi
12197
+ )) {
12198
+ const operand = String(match[1] || "").trim();
12199
+ const target = assignments.get(operand) || operand;
12200
+ if (!/^(?:"[^"\n]+"|'[^'\n]+')$/.test(target)) return false;
12201
+ writeTargets.push(target);
12202
+ }
12203
+ return Boolean(
12204
+ writeTargets.length > 0 &&
12205
+ writeTargets.every((candidate) =>
12206
+ isScopedTaskArtifactEvidencePath(candidate, state, config)
12207
+ )
12208
+ );
12209
+ }
12210
+
12211
+ function commandIsBoundedReadOnlyArtifactValidation(command = "") {
12212
+ const normalized = String(command || "").trim();
12213
+ if (!normalized) return false;
12214
+ if (
12215
+ !/\bassert\b|\bjson\.(?:load|loads)\s*\(|\bJSON\.parse\s*\(|\bjq\b|\bsha256sum\b|\bmd5sum\b|\b(?:valid|pass|verified?)\b/i.test(
12216
+ normalized
12217
+ )
12218
+ ) {
12219
+ return false;
12220
+ }
12221
+ // Heredoc input (`<<`) is compatible with read-only validation. Any output
12222
+ // redirection or known mutator keeps conservative project revision tracking.
12223
+ if (/(^|[^<])>{1,2}(?!=)/m.test(normalized)) return false;
12224
+ if (
12225
+ /\b(?:cp|install|mkdir|mv|rm|rmdir|tee|touch|truncate)\b|\bsed\s+-i\b|\bperl\s+-pi\b/i.test(
12226
+ normalized
12227
+ )
12228
+ ) {
12229
+ return false;
12230
+ }
12231
+ if (
12232
+ /\bopen\s*\([^\n)]*,\s*["'][^"']*[wax+][^"']*["']|\.(?:append|mkdir|rename|replace|rmdir|touch|unlink|write|write_bytes|write_text)\s*\(|\b(?:os\.(?:makedirs|mkdir|remove|rename|replace|rmdir|system|unlink)|shutil\.|subprocess\.)/i.test(
12233
+ normalized
12234
+ )
12235
+ ) {
12236
+ return false;
12237
+ }
12238
+ return true;
12239
+ }
12240
+
12117
12241
  const DISPOSABLE_GENERATED_PATH_SEGMENTS = new Set([
12118
12242
  ".build",
12119
12243
  ".gradle",
@@ -12974,6 +13098,17 @@ export function canonicalizeVerifiedArtifactCompletion(state = {}, result = "")
12974
13098
  function successfulMutationPaths(toolResult = {}) {
12975
13099
  if (!toolResult || toolResult.ok === false || toolResult.blocked || toolResult.skipped) return [];
12976
13100
  if (toolResult.toolName === "deep_research" && toolResult.reportPath) return [toolResult.reportPath];
13101
+ if (toolResult.toolName === "run_command") {
13102
+ return [
13103
+ ...new Set(
13104
+ (Array.isArray(toolResult.verifiedGeneratedOutputPaths)
13105
+ ? toolResult.verifiedGeneratedOutputPaths
13106
+ : [])
13107
+ .map((item) => String(item || "").trim())
13108
+ .filter(Boolean)
13109
+ ),
13110
+ ];
13111
+ }
12977
13112
  if (!["write_file", "apply_patch"].includes(String(toolResult.toolName || ""))) return [];
12978
13113
  const changes = [
12979
13114
  ...(Array.isArray(toolResult.changes) ? toolResult.changes : []),
@@ -13026,9 +13161,11 @@ function successfulWorkspaceMutationPaths(toolResult = {}) {
13026
13161
  ];
13027
13162
  }
13028
13163
 
13029
- function successfulProjectMutationPaths(toolResult = {}) {
13164
+ function successfulProjectMutationPaths(toolResult = {}, state = {}, config = {}) {
13030
13165
  return successfulWorkspaceMutationPaths(toolResult).filter(
13031
- (candidate) => !isPrivateVerificationEvidencePath(candidate)
13166
+ (candidate) =>
13167
+ !isPrivateVerificationEvidencePath(candidate) &&
13168
+ !isScopedTaskArtifactEvidencePath(candidate, state, config)
13032
13169
  );
13033
13170
  }
13034
13171
 
@@ -13052,7 +13189,7 @@ function diffHasMaterialContentChange(diff = "") {
13052
13189
  return JSON.stringify(compactRemoved) !== JSON.stringify(compactAdded);
13053
13190
  }
13054
13191
 
13055
- function materialProjectMutationPaths(toolResult = {}) {
13192
+ function materialProjectMutationPaths(toolResult = {}, state = {}, config = {}) {
13056
13193
  if (!toolResult || toolResult.blocked || toolResult.skipped) {
13057
13194
  return [];
13058
13195
  }
@@ -13065,7 +13202,13 @@ function materialProjectMutationPaths(toolResult = {}) {
13065
13202
  ];
13066
13203
  const paths = [];
13067
13204
  for (const change of changes) {
13068
- if (!change || isPrivateVerificationEvidencePath(change.path)) continue;
13205
+ if (
13206
+ !change ||
13207
+ isPrivateVerificationEvidencePath(change.path) ||
13208
+ isScopedTaskArtifactEvidencePath(change.path, state, config)
13209
+ ) {
13210
+ continue;
13211
+ }
13069
13212
  if (
13070
13213
  change.created === true ||
13071
13214
  change.deleted === true ||
@@ -18472,7 +18615,7 @@ async function executeTool(browserState, toolCall, snapshot, config, store, obse
18472
18615
  const isRequiredCommand = requiredCommands.some((candidate) =>
18473
18616
  projectCommandsEquivalent(candidate, observedInnerCommand, config)
18474
18617
  );
18475
- const exactOutputSnapshotsBefore = isRequiredCommand && policy.writesWorkspace
18618
+ const exactOutputSnapshotsBefore = policy.writesWorkspace && exactOutputPathsForState(state).length
18476
18619
  ? await captureExactOutputSnapshots(state, config)
18477
18620
  : [];
18478
18621
  const gitWorktreeBefore = captureMutationScope
@@ -18562,6 +18705,16 @@ async function executeTool(browserState, toolCall, snapshot, config, store, obse
18562
18705
  const eventResult = sanitizeToolResult(result);
18563
18706
  await store.appendEvent("tool.completed", eventResult);
18564
18707
  observers.event("tool.completed", eventResult);
18708
+ for (const generatedPath of generatedOutputPaths) {
18709
+ const change = {
18710
+ path: generatedPath,
18711
+ toolName: "run_command",
18712
+ commandCwd: config.commandCwd,
18713
+ generatedByCommand: true,
18714
+ };
18715
+ await store.appendEvent("file.changed", change);
18716
+ observers.event("file.changed", change);
18717
+ }
18565
18718
  return result;
18566
18719
  }
18567
18720
  case "tmux_list_sessions": {
@@ -19848,6 +20001,11 @@ async function completionEvidenceDecision({ config, state, store, observers, ste
19848
20001
  const blocker = deterministicFinishBlocker(assessment.contract, assessment.ledger, assessment.evaluation);
19849
20002
  const verificationDeficits = projectVerificationDeficits(state, config);
19850
20003
  const requiredEvidence = (assessment.contract.requiredEvidence || []).map((item) => item.category);
20004
+ const requiresProjectTestEvidence = Boolean(
20005
+ requiredEvidence.includes("test") ||
20006
+ projectTestBlock ||
20007
+ verificationDeficits.failedTestRun
20008
+ );
19851
20009
  const presentEvidence = assessment.ledger.categories || [];
19852
20010
  const progressCount = Number(assessment.progressCount || 0);
19853
20011
  const semanticFailureReason = assessment.semantic.checked && !assessment.semantic.ok
@@ -19881,8 +20039,13 @@ async function completionEvidenceDecision({ config, state, store, observers, ste
19881
20039
  freshMutationRequirement.artifactQualityRepairRequired,
19882
20040
  missingToolCalls: assessment.evaluation.missingToolCalls || [],
19883
20041
  pendingProjectCommands: verificationDeficits.pendingCommands,
19884
- pendingProjectTests: verificationDeficits.testsCurrent ? [] : verificationDeficits.discoveredTests,
19885
- suggestedTestCommands: verificationDeficits.suggestedTestCommands,
20042
+ pendingProjectTests:
20043
+ requiresProjectTestEvidence && !verificationDeficits.testsCurrent
20044
+ ? verificationDeficits.discoveredTests
20045
+ : [],
20046
+ suggestedTestCommands: requiresProjectTestEvidence
20047
+ ? verificationDeficits.suggestedTestCommands
20048
+ : [],
19886
20049
  projectMutationRevision: verificationDeficits.revision,
19887
20050
  failedProjectTestCommand: verificationDeficits.failedTestRun?.command || "",
19888
20051
  failedProjectTestSignature: verificationDeficits.failedTestRun?.failureSignature || "",
@@ -23182,7 +23345,7 @@ async function runAgentOnceUnlocked(config) {
23182
23345
 
23183
23346
  try {
23184
23347
  throwIfAborted(config);
23185
- const goalIntent = classifyGoalIntent(config.goal);
23348
+ const goalIntent = classifyGoalIntent(scopedChatopsEvidenceGoal(config.goal, config.taskProfile));
23186
23349
  const canFinishDirectly =
23187
23350
  !config.resume && !state.plan && Number(state.stepsCompleted || 0) === 0 && isDirectAnswerIntent(goalIntent);
23188
23351
  if (canFinishDirectly) {
package/src/config.js CHANGED
@@ -25,7 +25,7 @@ import { normalizeDynamicStepsMode } from "./step-budget-controller.js";
25
25
  import { maxStepsForExecutionPolicy, selectExecutionPolicy } from "./execution-policy.js";
26
26
  import { normalizeContextBudgetMode } from "./context-budget-controller.js";
27
27
  import { BASELINE_PROVIDER, normalizeProviderBaseURL, normalizeProviderId } from "./provider-contract.js";
28
- import { isResponseOnlyEvidenceScope } from "./scs-evidence.js";
28
+ import { isResponseOnlyEvidenceScope, scopedChatopsEvidenceGoal } from "./scs-evidence.js";
29
29
 
30
30
  function parseBoolean(value, fallback) {
31
31
  if (value === undefined) return fallback;
@@ -101,6 +101,7 @@ export function resolveRuntimeConfig(args, overrides = {}) {
101
101
  const requestedProvider = resolveRequestedProvider(overrides, args);
102
102
  const routingMode = normalizeRoutingMode(overrides.routingMode || args.routingMode || process.env.AGENT_ROUTING_MODE || "smart");
103
103
  const taskProfile = normalizeTaskProfile(overrides.taskProfile || args.taskProfile || process.env.AGINTI_TASK_PROFILE || "auto");
104
+ const taskSelectionGoal = scopedChatopsEvidenceGoal(args.goal || "", taskProfile);
104
105
  const language = resolveLanguage(overrides.language || args.language || process.env.AGINTI_LANGUAGE || "");
105
106
  // Capability/resource snapshots are trusted runtime inputs. They are deliberately
106
107
  // not inferred from ambient cloud credentials or optimistic environment flags.
@@ -141,7 +142,7 @@ export function resolveRuntimeConfig(args, overrides = {}) {
141
142
  routingMode: sessionModelLocked ? "manual" : routingMode,
142
143
  provider: requestedProvider,
143
144
  model: requestedModel,
144
- goal: args.goal || "",
145
+ goal: taskSelectionGoal,
145
146
  taskProfile,
146
147
  routeProvider,
147
148
  routeModel: overrides.routeModel || args.routeModel || process.env.AGINTI_ROUTE_MODEL || "",
@@ -190,7 +191,7 @@ export function resolveRuntimeConfig(args, overrides = {}) {
190
191
  overrides.scsValidationMode ?? args.scsValidationMode ?? process.env.AGINTI_SCS_VALIDATION_MODE ?? "auto"
191
192
  );
192
193
  const scsActive = shouldActivateScs(scsMode, {
193
- goal: args.goal || "",
194
+ goal: taskSelectionGoal,
194
195
  taskProfile,
195
196
  complexityScore: route.complexityScore,
196
197
  });
@@ -226,7 +227,7 @@ export function resolveRuntimeConfig(args, overrides = {}) {
226
227
  ? normalizeProviderBaseURL(activeProvider, overrides.baseURL || defaults.baseURL)
227
228
  : overrides.baseURL || defaults.baseURL;
228
229
  const defaultMaxSteps = recommendedMaxStepsForTask({
229
- goal: args.goal || "",
230
+ goal: taskSelectionGoal,
230
231
  taskProfile,
231
232
  complexityScore: route.complexityScore,
232
233
  });
@@ -31,6 +31,7 @@ import {
31
31
  estimateToolSchemaTokens,
32
32
  } from "./context-budget-controller.js";
33
33
  import { attachToolContract } from "./tool-contract.js";
34
+ import { scopedChatopsEvidenceGoal } from "./scs-evidence.js";
34
35
 
35
36
  function isRetainedWorkspaceProfile(config = {}) {
36
37
  return config.integrationSessionProfile === INTEGRATION_TEXT_WORKSPACE_PROFILE_ID ||
@@ -1241,11 +1242,14 @@ export async function createPlan(client, config, state) {
1241
1242
  const engineeringGuidance = isRetainedWorkspaceProfile(config)
1242
1243
  ? ""
1243
1244
  : engineeringGuidanceForTask(state.goal, config.taskProfile);
1244
- const selectedSkills = isRetainedWorkspaceProfile(config) ? [] : selectSkillsForGoal(state.goal, {
1245
- taskProfile: config.taskProfile,
1246
- limit: 5,
1247
- projectRoot: config.commandCwd || config.baseDir || process.cwd(),
1248
- });
1245
+ const selectedSkills = isRetainedWorkspaceProfile(config) ? [] : selectSkillsForGoal(
1246
+ scopedChatopsEvidenceGoal(state.goal, config.taskProfile),
1247
+ {
1248
+ taskProfile: config.taskProfile,
1249
+ limit: 5,
1250
+ projectRoot: config.commandCwd || config.baseDir || process.cwd(),
1251
+ }
1252
+ );
1249
1253
  const skillContext = formatSkillsForPrompt(selectedSkills);
1250
1254
  const projectInstructions = state.meta?.projectInstructions;
1251
1255
  const platform = platformInfo();
@@ -277,6 +277,15 @@ export function shouldActivateScs(mode = "off", context = {}) {
277
277
  const profile = String(context.taskProfile || "").toLowerCase();
278
278
  const goal = String(context.goal || "");
279
279
  if (SCS_AUTO_PROFILES.has(profile)) return true;
280
+ const staticDataPaths = goal.match(/(?:^|[\s`'"(])[^\s`'"()]+\.(?:csv|json|toml|ya?ml)\b/gi) || [];
281
+ const simpleStaticDataWrite =
282
+ staticDataPaths.length === 1 &&
283
+ /\b(?:create|emit|generate|save|write)\b/i.test(goal) &&
284
+ !/\b(?:app|application|build|cli|code|codebase|compile|database|dataset|deploy|implement|library|migrate|package|publish|refactor|release|repository|script|server|source|test|upload)\b/i.test(
285
+ goal
286
+ ) &&
287
+ Number(context.complexityScore || 0) < 3;
288
+ if (simpleStaticDataWrite) return false;
280
289
  if (SCS_AUTO_HINTS.some((hint) => hint.test(goal))) return true;
281
290
  return Number(context.complexityScore || 0) >= 5;
282
291
  }
@@ -538,7 +538,10 @@ function inferExactOutputPaths(goal = "") {
538
538
  if (!isOutputListItem && !hasDirectOutputAction) continue;
539
539
  const sourceLine = !isOutputListItem && hasDirectOutputAction ? line.slice(directOutputIndex) : line;
540
540
  if (!isOutputListItem && negatedOutputLine.test(sourceLine)) continue;
541
- if (!isOutputListItem && nonOutputToolLine.test(sourceLine)) continue;
541
+ const verifierIndex = line.search(/\b(?:validate|verify|check|compile|run|execute)\b/i);
542
+ if (!isOutputListItem && nonOutputToolLine.test(line) && verifierIndex >= 0 && verifierIndex < directOutputIndex) {
543
+ continue;
544
+ }
542
545
  if (!isOutputListItem && /\boutput\s+(?:subfolders?|directories|folders?|paths?)\b/i.test(sourceLine)) continue;
543
546
  const outputDirMatch = sourceLine.match(/(?:to|at|in|under|到|至|在)\s*([^\s,,、;;。]+\/)/i);
544
547
  activeOutputDir = outputDirMatch?.[1] || "";
@@ -962,6 +965,12 @@ export function inferRequiredExecutableTerms(goal = "") {
962
965
  if (executableRequirementIsNegated(source, index)) continue;
963
966
  if (indexFallsInsideInlineCommand(source, index)) continue;
964
967
  const window = source.slice(Math.max(0, index - 180), Math.min(source.length, index + match[0].length + 220));
968
+ const structuredDataLiteral =
969
+ /\b(?:csv|json|toml|ya?ml)\b/i.test(window) &&
970
+ !/\b(?:actual|canonical|executable|implementation|source\s+code|function|call|argument|parameter|repair|fix|replace)\b/i.test(
971
+ window
972
+ );
973
+ if (structuredDataLiteral) continue;
965
974
  const implementationRequirement =
966
975
  /\b(?:actual|canonical|executable|implementation|source|code|call|argument|keyword|parameter|repair|fix|correct|replace|add|set|pass|use|must|required)\b/iu.test(window) ||
967
976
  /实际|實際|实现|實現|源码|源碼|代码|代碼|调用|調用|参数|參數|修复|修復|改正|替换|替換|添加|设置|設定|使用|必须|必須|実装|ソース|コード|呼び出し|引数|修正|置換|追加|設定|使用/.test(window);
@@ -1229,12 +1238,22 @@ function codeProfileRequiresCommand(goal = "") {
1229
1238
  /\b(?:note|notes?|markdown|readme|documentation|text file)\b/.test(text) ||
1230
1239
  /\bnotes?\/[^\s]+\.(?:md|txt)\b/.test(text) ||
1231
1240
  /\.(?:md|txt)\b/.test(text);
1241
+ const structuredDataProse = text.replace(
1242
+ /(?:^|[\s`'"(])[^\s`'"()]+\.(?:csv|json|toml|ya?ml)\b/g,
1243
+ " "
1244
+ );
1245
+ const simpleStructuredDataWrite =
1246
+ /\.(?:csv|json|toml|ya?ml)\b/.test(text) &&
1247
+ /\b(?:create|emit|generate|save|write)\b/.test(text) &&
1248
+ !/\b(?:app|application|build|cli|code|codebase|compile|execute|function|implement|library|lint|package|refactor|run|script|server|source|test|typecheck)\b/.test(
1249
+ structuredDataProse
1250
+ );
1232
1251
  const substantiveCodeWork =
1233
1252
  /\b(?:fix|repair|bug|implement|feature|refactor|test|run|build|compile|lint|typecheck|verify|validate|package|library|cli|api server|app|application|script|codebase|src\/|source code)\b/.test(
1234
1253
  text
1235
1254
  ) ||
1236
1255
  /\.(?:js|jsx|ts|tsx|mjs|cjs|py|rs|go|java|kt|swift|rb|php|cs|cpp|c|h|hpp|sh)\b/.test(text);
1237
- return substantiveCodeWork && !simpleDocumentWrite;
1256
+ return substantiveCodeWork && !simpleDocumentWrite && !simpleStructuredDataWrite;
1238
1257
  }
1239
1258
 
1240
1259
  function goalRequestsExplicitTestMutation(text = "") {
@@ -1439,7 +1458,17 @@ function inferRequirementCategories(goal = "", taskProfile = "", acceptanceCrite
1439
1458
  ) {
1440
1459
  categories.add("visual");
1441
1460
  }
1442
- if (textHas(text, /\b(publish|deploy|submit|upload to|generate video|external service|npm publish|release)\b/) || /发布|部署|提交|生成视频|外部服务/.test(text)) {
1461
+ const explicitPublishAction =
1462
+ textHas(
1463
+ text,
1464
+ /\b(?:publish|deploy|submit|upload to|generate video|external service|npm publish)\b/
1465
+ ) ||
1466
+ textHas(
1467
+ text,
1468
+ /\b(?:release|ship)\s+(?:(?:the|this|that|a|an)\s+)?(?:package|version|build|app|application|software|library|plugin|extension|product)\b|\b(?:release|ship)\s+(?:to|on|via|through)\b|\bcut\s+(?:a\s+)?release\b/
1469
+ ) ||
1470
+ /发布|部署|提交|生成视频|外部服务/.test(text);
1471
+ if (explicitPublishAction) {
1443
1472
  categories.add("publish");
1444
1473
  }
1445
1474
 
@@ -482,6 +482,7 @@ const GENERIC_ROUTING_TERMS = new Set([
482
482
  "analysis",
483
483
  "app",
484
484
  "application",
485
+ "artifact",
485
486
  "code",
486
487
  "commit",
487
488
  "data",
@@ -498,6 +499,18 @@ const GENERIC_ROUTING_TERMS = new Set([
498
499
  "work",
499
500
  ]);
500
501
 
502
+ function normalizedSkillRoutingText(goal = "") {
503
+ return String(goal || "")
504
+ .toLowerCase()
505
+ .replace(
506
+ /(?:^|[\s`'"(])(?:~|\.{1,2}|\/|[a-z0-9_-])[a-z0-9_./~-]*\.([a-z0-9]{1,12})\b/gi,
507
+ (_match, extension) => ` file.${String(extension || "").toLowerCase()} `
508
+ )
509
+ .replace(/\b[a-z_][a-z0-9_.-]*\s*=\s*[a-z0-9_.-]+\b/gi, " ")
510
+ .replace(/\s+/g, " ")
511
+ .trim();
512
+ }
513
+
501
514
  function descriptionTerms(value = "") {
502
515
  return [
503
516
  ...new Set(
@@ -564,7 +577,7 @@ function textHasTrigger(text, needle) {
564
577
  }
565
578
 
566
579
  export function selectSkillsForGoal(goal = "", { taskProfile = "auto", limit = 6, includeBody = true, projectRoot = process.cwd() } = {}) {
567
- const goalText = String(goal || "").toLowerCase();
580
+ const goalText = normalizedSkillRoutingText(goal);
568
581
  const text = goalText;
569
582
  const skills = listSkills({ includeBody, projectRoot });
570
583
  const ranked = skills