@lazyingart/agintiflow 0.20.297 → 0.20.299

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.297",
3
+ "version": "0.20.299",
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",
@@ -1151,6 +1151,13 @@ const canvasArtifactContract = deriveScsTaskContract({
1151
1151
  const jsonObjectContract = deriveScsTaskContract({
1152
1152
  goal: "Extract a valid JSON object with schema from this text.",
1153
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
+ });
1154
1161
  const virtualFileContract = deriveScsTaskContract({
1155
1162
  goal: "Create file: /workspace/virtual-output.txt with virtual Docker path support.",
1156
1163
  });
@@ -1230,6 +1237,20 @@ assert(
1230
1237
  !jsonObjectContract.requiredEvidence.some((item) => item.category === "file"),
1231
1238
  "JSON object extraction should not be treated as a workspace file requirement without an explicit file/path"
1232
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
+ );
1233
1254
  assert(
1234
1255
  virtualFileContract.requiredEvidence.some((item) => item.category === "file") &&
1235
1256
  !virtualFileContract.requiredEvidence.some((item) => item.category === "artifact"),
@@ -309,6 +309,11 @@ try {
309
309
  String(explanation.state.messages.find((message) => message.role === "system")?.content || "").length < 10_000,
310
310
  "focused runtime prompt did not use progressive disclosure"
311
311
  );
312
+ assert.match(
313
+ String(explanation.state.messages.find((message) => message.role === "system")?.content || ""),
314
+ /run that command unchanged before probing --help, alternate wrappers/i,
315
+ "focused runtime did not prioritize exact established routine commands"
316
+ );
312
317
  assert(
313
318
  Math.max(
314
319
  ...explanation.state.messages
@@ -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),
@@ -2795,6 +2801,7 @@ function focusedCapabilityContext(config = {}) {
2795
2801
  ? `Advisory wrapper: ${normalizeWrapperName(config.preferredWrapper)} (${wrapperStatusText()}).`
2796
2802
  : "Advisory wrappers: disabled.",
2797
2803
  config.allowAuxiliaryTools ? "Auxiliary generation tools: enabled when the requested artifact needs them." : "Auxiliary tools: disabled.",
2804
+ "When the current request, project instructions, or routine contract names an exact established command, run that command unchanged before probing --help, alternate wrappers, process lists, or implementation source. If its result answers the request, stop discovery and finish.",
2798
2805
  "Discovery must be bounded: after a blocked path or search, change method once; never use recursive grep. Prefer exact manifests, workspace search, or targeted rg with an explicit path, globs, and result limit.",
2799
2806
  isRetainedWorkspaceProfile(config)
2800
2807
  ? isRetainedVisionWorkspaceProfile(config)
@@ -2891,7 +2898,11 @@ async function createInitialState(config, sessionId) {
2891
2898
  const projectRoot = config.commandCwd || config.baseDir || process.cwd();
2892
2899
  const selectedSkills = isRetainedWorkspaceProfile(config)
2893
2900
  ? []
2894
- : selectSkillsForGoal(config.goal, { taskProfile: config.taskProfile, limit: 6, projectRoot });
2901
+ : selectSkillsForGoal(scopedChatopsEvidenceGoal(config.goal, config.taskProfile), {
2902
+ taskProfile: config.taskProfile,
2903
+ limit: 6,
2904
+ projectRoot,
2905
+ });
2895
2906
  const skillContext = formatSkillsForPrompt(selectedSkills);
2896
2907
  const projectInstructions = await readProjectInstructions(config.baseDir || config.commandCwd || process.cwd());
2897
2908
  const projectInstructionContext = formatProjectInstructions(projectInstructions);
@@ -4050,9 +4061,12 @@ export function resetSameTaskExecutionContract(state = {}, revision = 0) {
4050
4061
  refreshPersistedProjectAcceptance(state);
4051
4062
  const activeRevision = Math.max(0, Number(revision || state.meta?.goalContract?.revision || 0));
4052
4063
  const currentRequest = String(state.meta?.goalContract?.currentRequest || state.goal || "").trim();
4064
+ const taskProfile = state.meta?.taskProfile || "auto";
4065
+ const scopedCurrentRequest =
4066
+ scopedChatopsEvidenceGoal(currentRequest, taskProfile) || currentRequest;
4053
4067
  const currentTurnContract = deriveScsTaskContract({
4054
- goal: currentRequest,
4055
- taskProfile: state.meta?.taskProfile || "auto",
4068
+ goal: scopedCurrentRequest,
4069
+ taskProfile,
4056
4070
  });
4057
4071
  const currentTurnCommands = normalizedRequiredProjectCommands(
4058
4072
  currentTurnContract.requiredProjectCommands
@@ -4094,7 +4108,7 @@ export function resetSameTaskExecutionContract(state = {}, revision = 0) {
4094
4108
  currentTurnContract.requiresSourceGrounding === true ||
4095
4109
  (
4096
4110
  currentTurnContract.requiresFileMutation === true &&
4097
- goalClearlyAllowsOverwrite(currentRequest)
4111
+ goalClearlyAllowsOverwrite(scopedCurrentRequest)
4098
4112
  )
4099
4113
  ),
4100
4114
  requiredProjectCommands: currentTurnCommands,
@@ -4208,7 +4222,11 @@ async function applyContinuationPrompt(state, config, observers) {
4208
4222
  const projectRoot = config.commandCwd || config.baseDir || process.cwd();
4209
4223
  const selectedSkills = isRetainedWorkspaceProfile(config)
4210
4224
  ? []
4211
- : selectSkillsForGoal(config.goal, { taskProfile: config.taskProfile, limit: 6, projectRoot });
4225
+ : selectSkillsForGoal(scopedChatopsEvidenceGoal(config.goal, config.taskProfile), {
4226
+ taskProfile: config.taskProfile,
4227
+ limit: 6,
4228
+ projectRoot,
4229
+ });
4212
4230
  const skillContext = formatSkillsForPrompt(selectedSkills);
4213
4231
  const projectInstructions = await readProjectInstructions(config.baseDir || config.commandCwd || process.cwd());
4214
4232
  state.meta = state.meta || {};
@@ -6384,9 +6402,9 @@ export function recordProjectVerificationOutcome(state = {}, toolResult = {}, co
6384
6402
  }
6385
6403
  }
6386
6404
 
6387
- const projectMutationPaths = successfulProjectMutationPaths(toolResult);
6405
+ const projectMutationPaths = successfulProjectMutationPaths(toolResult, state, config);
6388
6406
  const privateMutationPaths = successfulPrivateVerificationMutationPaths(toolResult);
6389
- const materialMutationPaths = materialProjectMutationPaths(toolResult);
6407
+ const materialMutationPaths = materialProjectMutationPaths(toolResult, state, config);
6390
6408
  if (["write_file", "apply_patch"].includes(toolName) && privateMutationPaths.length) {
6391
6409
  delete state.meta.verifiedCompletionCandidate;
6392
6410
  verification.privateMutationRevision += 1;
@@ -6549,12 +6567,23 @@ export function recordProjectVerificationOutcome(state = {}, toolResult = {}, co
6549
6567
  config,
6550
6568
  { verificationCommand }
6551
6569
  );
6570
+ const scopedTaskArtifactWrite = commandWritesOnlyScopedTaskArtifacts(
6571
+ mutationCommand,
6572
+ state,
6573
+ config
6574
+ );
6575
+ const readOnlyArtifactValidation = Boolean(
6576
+ commandMutationPaths.length === 0 &&
6577
+ commandIsBoundedReadOnlyArtifactValidation(mutationCommand)
6578
+ );
6552
6579
  const projectContentMutation = Boolean(
6553
6580
  command &&
6554
6581
  toolResult.blocked !== true &&
6555
6582
  !retainedExactVerification &&
6556
6583
  !disposableGeneratedCleanup &&
6557
6584
  !disposableGeneratedVerificationSideEffects &&
6585
+ !scopedTaskArtifactWrite &&
6586
+ !readOnlyArtifactValidation &&
6558
6587
  (commandSucceeded || commandMutationPaths.length > 0) &&
6559
6588
  (
6560
6589
  commandCanMutateProjectContent(mutationCommand, commandPolicy) ||
@@ -6569,6 +6598,12 @@ export function recordProjectVerificationOutcome(state = {}, toolResult = {}, co
6569
6598
  toolResult.disposableGeneratedVerificationSideEffects = true;
6570
6599
  toolResult.disposableGeneratedVerificationPaths = commandMutationPaths;
6571
6600
  }
6601
+ if (scopedTaskArtifactWrite) {
6602
+ toolResult.scopedTaskArtifactWrite = true;
6603
+ }
6604
+ if (readOnlyArtifactValidation) {
6605
+ toolResult.readOnlyArtifactValidation = true;
6606
+ }
6572
6607
  let requiredBatch = currentRequiredCommandBatch(verification, requiredCommands);
6573
6608
  const activeExecutionContract = state.meta?.activeExecutionContract;
6574
6609
  const activeTurnCommands =
@@ -12084,7 +12119,27 @@ function isPrivateVerificationEvidencePath(value = "") {
12084
12119
  );
12085
12120
  }
12086
12121
 
12087
- function commandWritesOnlyPrivateVerificationEvidence(command = "") {
12122
+ function isScopedTaskArtifactEvidencePath(value = "", state = {}, config = {}) {
12123
+ const artifactRoot = scopedArtifactRoot(completionContractGoal(config, state));
12124
+ if (!artifactRoot) return false;
12125
+ const raw = String(value || "")
12126
+ .trim()
12127
+ .replace(/^(["'])|(["'])$/g, "")
12128
+ .replace(/\\/g, "/");
12129
+ if (!raw || raw.includes("\0") || /^https?:\/\//i.test(raw)) return false;
12130
+ const commandCwd = path.resolve(
12131
+ config.commandCwd || state.commandCwd || process.cwd()
12132
+ );
12133
+ const absoluteRoot = path.resolve(commandCwd, artifactRoot);
12134
+ const absoluteCandidate = path.resolve(commandCwd, raw);
12135
+ const relative = path.relative(absoluteRoot, absoluteCandidate);
12136
+ return Boolean(
12137
+ relative === "" ||
12138
+ (!relative.startsWith("..") && !path.isAbsolute(relative))
12139
+ );
12140
+ }
12141
+
12142
+ function commandWritesOnlyEvidenceMatching(command = "", pathMatches = () => false) {
12088
12143
  const normalized = String(command || "").trim();
12089
12144
  if (!normalized) return false;
12090
12145
  const tokens = tokenizeShellWords(normalized);
@@ -12093,27 +12148,97 @@ function commandWritesOnlyPrivateVerificationEvidence(command = "") {
12093
12148
  if (["-a", "--append"].includes(tokens[index])) index += 1;
12094
12149
  if (tokens[index] === "--") index += 1;
12095
12150
  const targets = tokens.slice(index);
12096
- return targets.length > 0 && targets.every(isPrivateVerificationEvidencePath);
12151
+ return targets.length > 0 && targets.every(pathMatches);
12097
12152
  }
12098
12153
  if (tokens[0] === "mkdir") {
12099
12154
  const targets = tokens.slice(1).filter((token) => token !== "-p" && token !== "--");
12100
- return targets.length > 0 && targets.every(isPrivateVerificationEvidencePath);
12155
+ return targets.length > 0 && targets.every(pathMatches);
12101
12156
  }
12102
12157
 
12103
- let strippedPrivateRedirect = false;
12104
- const withoutPrivateRedirects = normalized.replace(
12158
+ let strippedEvidenceRedirect = false;
12159
+ const withoutEvidenceRedirects = normalized.replace(
12105
12160
  /(^|\s)(?:\d*>>?|&>>?)\s*("[^"]+"|'[^']+'|[^\s;&|]+)/g,
12106
12161
  (match, prefix, target) => {
12107
- if (!isPrivateVerificationEvidencePath(target)) return match;
12108
- strippedPrivateRedirect = true;
12162
+ if (!pathMatches(target)) return match;
12163
+ strippedEvidenceRedirect = true;
12109
12164
  return prefix;
12110
12165
  }
12111
12166
  ).trim();
12112
- if (!strippedPrivateRedirect || !withoutPrivateRedirects) return false;
12113
- const underlyingPolicy = classifyCommand(withoutPrivateRedirects);
12167
+ if (!strippedEvidenceRedirect || !withoutEvidenceRedirects) return false;
12168
+ const underlyingPolicy = classifyCommand(withoutEvidenceRedirects);
12114
12169
  return underlyingPolicy.writesWorkspace !== true && underlyingPolicy.mayMutateProject !== true;
12115
12170
  }
12116
12171
 
12172
+ function commandWritesOnlyPrivateVerificationEvidence(command = "") {
12173
+ return commandWritesOnlyEvidenceMatching(
12174
+ command,
12175
+ isPrivateVerificationEvidencePath
12176
+ );
12177
+ }
12178
+
12179
+ function commandWritesOnlyScopedTaskArtifacts(command = "", state = {}, config = {}) {
12180
+ if (commandWritesOnlyEvidenceMatching(
12181
+ command,
12182
+ (candidate) => isScopedTaskArtifactEvidencePath(candidate, state, config)
12183
+ )) {
12184
+ return true;
12185
+ }
12186
+ const normalized = String(command || "").trim();
12187
+ const artifactRoot = scopedArtifactRoot(completionContractGoal(config, state));
12188
+ if (!normalized || !artifactRoot) return false;
12189
+ const assignments = new Map();
12190
+ for (const match of normalized.matchAll(
12191
+ /\b([A-Za-z_]\w*)\s*=\s*("[^"\n]+"|'[^'\n]+')/g
12192
+ )) {
12193
+ assignments.set(match[1], match[2]);
12194
+ }
12195
+ const writeTargets = [];
12196
+ for (const match of normalized.matchAll(
12197
+ /\bopen\s*\(\s*([^,\n]+)\s*,\s*(["'][^"']*[wax+][^"']*["'])/gi
12198
+ )) {
12199
+ const operand = String(match[1] || "").trim();
12200
+ const target = assignments.get(operand) || operand;
12201
+ if (!/^(?:"[^"\n]+"|'[^'\n]+')$/.test(target)) return false;
12202
+ writeTargets.push(target);
12203
+ }
12204
+ return Boolean(
12205
+ writeTargets.length > 0 &&
12206
+ writeTargets.every((candidate) =>
12207
+ isScopedTaskArtifactEvidencePath(candidate, state, config)
12208
+ )
12209
+ );
12210
+ }
12211
+
12212
+ function commandIsBoundedReadOnlyArtifactValidation(command = "") {
12213
+ const normalized = String(command || "").trim();
12214
+ if (!normalized) return false;
12215
+ if (
12216
+ !/\bassert\b|\bjson\.(?:load|loads)\s*\(|\bJSON\.parse\s*\(|\bjq\b|\bsha256sum\b|\bmd5sum\b|\b(?:valid|pass|verified?)\b/i.test(
12217
+ normalized
12218
+ )
12219
+ ) {
12220
+ return false;
12221
+ }
12222
+ // Heredoc input (`<<`) is compatible with read-only validation. Any output
12223
+ // redirection or known mutator keeps conservative project revision tracking.
12224
+ if (/(^|[^<])>{1,2}(?!=)/m.test(normalized)) return false;
12225
+ if (
12226
+ /\b(?:cp|install|mkdir|mv|rm|rmdir|tee|touch|truncate)\b|\bsed\s+-i\b|\bperl\s+-pi\b/i.test(
12227
+ normalized
12228
+ )
12229
+ ) {
12230
+ return false;
12231
+ }
12232
+ if (
12233
+ /\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(
12234
+ normalized
12235
+ )
12236
+ ) {
12237
+ return false;
12238
+ }
12239
+ return true;
12240
+ }
12241
+
12117
12242
  const DISPOSABLE_GENERATED_PATH_SEGMENTS = new Set([
12118
12243
  ".build",
12119
12244
  ".gradle",
@@ -12974,6 +13099,17 @@ export function canonicalizeVerifiedArtifactCompletion(state = {}, result = "")
12974
13099
  function successfulMutationPaths(toolResult = {}) {
12975
13100
  if (!toolResult || toolResult.ok === false || toolResult.blocked || toolResult.skipped) return [];
12976
13101
  if (toolResult.toolName === "deep_research" && toolResult.reportPath) return [toolResult.reportPath];
13102
+ if (toolResult.toolName === "run_command") {
13103
+ return [
13104
+ ...new Set(
13105
+ (Array.isArray(toolResult.verifiedGeneratedOutputPaths)
13106
+ ? toolResult.verifiedGeneratedOutputPaths
13107
+ : [])
13108
+ .map((item) => String(item || "").trim())
13109
+ .filter(Boolean)
13110
+ ),
13111
+ ];
13112
+ }
12977
13113
  if (!["write_file", "apply_patch"].includes(String(toolResult.toolName || ""))) return [];
12978
13114
  const changes = [
12979
13115
  ...(Array.isArray(toolResult.changes) ? toolResult.changes : []),
@@ -13026,9 +13162,11 @@ function successfulWorkspaceMutationPaths(toolResult = {}) {
13026
13162
  ];
13027
13163
  }
13028
13164
 
13029
- function successfulProjectMutationPaths(toolResult = {}) {
13165
+ function successfulProjectMutationPaths(toolResult = {}, state = {}, config = {}) {
13030
13166
  return successfulWorkspaceMutationPaths(toolResult).filter(
13031
- (candidate) => !isPrivateVerificationEvidencePath(candidate)
13167
+ (candidate) =>
13168
+ !isPrivateVerificationEvidencePath(candidate) &&
13169
+ !isScopedTaskArtifactEvidencePath(candidate, state, config)
13032
13170
  );
13033
13171
  }
13034
13172
 
@@ -13052,7 +13190,7 @@ function diffHasMaterialContentChange(diff = "") {
13052
13190
  return JSON.stringify(compactRemoved) !== JSON.stringify(compactAdded);
13053
13191
  }
13054
13192
 
13055
- function materialProjectMutationPaths(toolResult = {}) {
13193
+ function materialProjectMutationPaths(toolResult = {}, state = {}, config = {}) {
13056
13194
  if (!toolResult || toolResult.blocked || toolResult.skipped) {
13057
13195
  return [];
13058
13196
  }
@@ -13065,7 +13203,13 @@ function materialProjectMutationPaths(toolResult = {}) {
13065
13203
  ];
13066
13204
  const paths = [];
13067
13205
  for (const change of changes) {
13068
- if (!change || isPrivateVerificationEvidencePath(change.path)) continue;
13206
+ if (
13207
+ !change ||
13208
+ isPrivateVerificationEvidencePath(change.path) ||
13209
+ isScopedTaskArtifactEvidencePath(change.path, state, config)
13210
+ ) {
13211
+ continue;
13212
+ }
13069
13213
  if (
13070
13214
  change.created === true ||
13071
13215
  change.deleted === true ||
@@ -18472,7 +18616,7 @@ async function executeTool(browserState, toolCall, snapshot, config, store, obse
18472
18616
  const isRequiredCommand = requiredCommands.some((candidate) =>
18473
18617
  projectCommandsEquivalent(candidate, observedInnerCommand, config)
18474
18618
  );
18475
- const exactOutputSnapshotsBefore = isRequiredCommand && policy.writesWorkspace
18619
+ const exactOutputSnapshotsBefore = policy.writesWorkspace && exactOutputPathsForState(state).length
18476
18620
  ? await captureExactOutputSnapshots(state, config)
18477
18621
  : [];
18478
18622
  const gitWorktreeBefore = captureMutationScope
@@ -18562,6 +18706,16 @@ async function executeTool(browserState, toolCall, snapshot, config, store, obse
18562
18706
  const eventResult = sanitizeToolResult(result);
18563
18707
  await store.appendEvent("tool.completed", eventResult);
18564
18708
  observers.event("tool.completed", eventResult);
18709
+ for (const generatedPath of generatedOutputPaths) {
18710
+ const change = {
18711
+ path: generatedPath,
18712
+ toolName: "run_command",
18713
+ commandCwd: config.commandCwd,
18714
+ generatedByCommand: true,
18715
+ };
18716
+ await store.appendEvent("file.changed", change);
18717
+ observers.event("file.changed", change);
18718
+ }
18565
18719
  return result;
18566
18720
  }
18567
18721
  case "tmux_list_sessions": {
@@ -19848,6 +20002,11 @@ async function completionEvidenceDecision({ config, state, store, observers, ste
19848
20002
  const blocker = deterministicFinishBlocker(assessment.contract, assessment.ledger, assessment.evaluation);
19849
20003
  const verificationDeficits = projectVerificationDeficits(state, config);
19850
20004
  const requiredEvidence = (assessment.contract.requiredEvidence || []).map((item) => item.category);
20005
+ const requiresProjectTestEvidence = Boolean(
20006
+ requiredEvidence.includes("test") ||
20007
+ projectTestBlock ||
20008
+ verificationDeficits.failedTestRun
20009
+ );
19851
20010
  const presentEvidence = assessment.ledger.categories || [];
19852
20011
  const progressCount = Number(assessment.progressCount || 0);
19853
20012
  const semanticFailureReason = assessment.semantic.checked && !assessment.semantic.ok
@@ -19881,8 +20040,13 @@ async function completionEvidenceDecision({ config, state, store, observers, ste
19881
20040
  freshMutationRequirement.artifactQualityRepairRequired,
19882
20041
  missingToolCalls: assessment.evaluation.missingToolCalls || [],
19883
20042
  pendingProjectCommands: verificationDeficits.pendingCommands,
19884
- pendingProjectTests: verificationDeficits.testsCurrent ? [] : verificationDeficits.discoveredTests,
19885
- suggestedTestCommands: verificationDeficits.suggestedTestCommands,
20043
+ pendingProjectTests:
20044
+ requiresProjectTestEvidence && !verificationDeficits.testsCurrent
20045
+ ? verificationDeficits.discoveredTests
20046
+ : [],
20047
+ suggestedTestCommands: requiresProjectTestEvidence
20048
+ ? verificationDeficits.suggestedTestCommands
20049
+ : [],
19886
20050
  projectMutationRevision: verificationDeficits.revision,
19887
20051
  failedProjectTestCommand: verificationDeficits.failedTestRun?.command || "",
19888
20052
  failedProjectTestSignature: verificationDeficits.failedTestRun?.failureSignature || "",
@@ -23182,7 +23346,7 @@ async function runAgentOnceUnlocked(config) {
23182
23346
 
23183
23347
  try {
23184
23348
  throwIfAborted(config);
23185
- const goalIntent = classifyGoalIntent(config.goal);
23349
+ const goalIntent = classifyGoalIntent(scopedChatopsEvidenceGoal(config.goal, config.taskProfile));
23186
23350
  const canFinishDirectly =
23187
23351
  !config.resume && !state.plan && Number(state.stepsCompleted || 0) === 0 && isDirectAnswerIntent(goalIntent);
23188
23352
  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 = "") {
@@ -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