@lazyingart/agintiflow 0.20.300 → 0.20.301

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.
@@ -56,6 +56,22 @@ in `evidence_json` and `events`.
56
56
 
57
57
  ## Current Campaign Findings
58
58
 
59
+ ### Established media-chain reuse and permission boundaries
60
+
61
+ `media-chain-readiness-020` passed after reconciling a stale machine-ledger
62
+ `running` row with the retained session's terminal `session.finished` event.
63
+ AgInTi inspected the existing Musia, LabCanvas, LALACHAN, Xiaoyunque, and
64
+ LazyEdit entry points and produced `media-routine-readiness.md` without
65
+ generating media, starting or focusing a browser, uploading, copying to
66
+ Nutstore, creating a remote job, or publishing.
67
+
68
+ The independent acceptance contract verified every required source-grounded
69
+ entry, 16 read-only command results, zero forbidden external-action tools, and
70
+ zero long jobs. The resulting contract keeps four permissions independent:
71
+ reviewed-song generation, music-video generation, LazyEdit processing, and
72
+ public platform publication. An earlier stage or old context cannot authorize a
73
+ later irreversible stage.
74
+
59
75
  ### Retained dependencies, linked-worktree commits, and clean visual evidence
60
76
 
61
77
  `data-analysis-retained-dependencies-060` resumed the original DeepSeek session
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyingart/agintiflow",
3
- "version": "0.20.300",
3
+ "version": "0.20.301",
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",
@@ -31,14 +31,23 @@ import {
31
31
  } from "../src/scs-controller.js";
32
32
  import {
33
33
  buildScsEvidenceLedger,
34
+ buildTmuxGitIntent,
34
35
  deriveScsTaskContract,
35
36
  evaluateScsEvidence,
36
37
  gitActionsSatisfyContract,
37
38
  inferSuccessfulGitActionsFromCommandResult,
39
+ inferSuccessfulGitActionsFromTmuxCapture,
40
+ reconcileTmuxGitEvidenceEvents,
38
41
  } from "../src/scs-evidence.js";
39
42
  import { resolveRuntimeConfig } from "../src/config.js";
40
43
  import { classifyGoalIntent, isDirectAnswerIntent } from "../src/goal-intent.js";
41
44
  import { languageWriterDefaults } from "../src/writing-specialist.js";
45
+ import {
46
+ attachVerifiedTmuxGitEvidence,
47
+ recordDurableEvidenceCategories,
48
+ rememberTmuxGitIntent,
49
+ sanitizeToolResult,
50
+ } from "../src/agent-runner.js";
42
51
 
43
52
  const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
44
53
  for (const key of [
@@ -1117,6 +1126,195 @@ assert(
1117
1126
  !committedEvaluation.missing.some((item) => item.category === "git"),
1118
1127
  "a successful git commit did not satisfy the explicit git requirement"
1119
1128
  );
1129
+ const tmuxCommitCommand = [
1130
+ 'echo "===AGINTI_REVIEW_COMMIT_START==="',
1131
+ 'git add README.md && git commit -m "finish review"',
1132
+ 'echo "===COMMIT_EXIT:$?==="',
1133
+ ].join(" ; ");
1134
+ const tmuxCommitIntent = buildTmuxGitIntent(tmuxCommitCommand);
1135
+ assert(
1136
+ tmuxCommitIntent?.actions.includes("commit") &&
1137
+ tmuxCommitIntent.markers.includes("===AGINTI_REVIEW_COMMIT_START==="),
1138
+ "a marked workspace tmux commit command did not retain bounded Git intent"
1139
+ );
1140
+ const tmuxCommitCapture = {
1141
+ ok: true,
1142
+ toolName: "tmux_capture_pane",
1143
+ target: "review:0.0",
1144
+ content: [
1145
+ "===AGINTI_REVIEW_COMMIT_START===",
1146
+ "[review abc1234] finish review",
1147
+ " 1 file changed, 2 insertions(+)",
1148
+ "===COMMIT_EXIT:0===",
1149
+ ].join("\n"),
1150
+ };
1151
+ const verifiedTmuxCommit = inferSuccessfulGitActionsFromTmuxCapture(
1152
+ tmuxCommitIntent,
1153
+ tmuxCommitCapture
1154
+ );
1155
+ assert(
1156
+ verifiedTmuxCommit.length === 1 && verifiedTmuxCommit[0] === "commit",
1157
+ "a fresh marked tmux capture did not prove its successful Git commit"
1158
+ );
1159
+ assert(
1160
+ inferSuccessfulGitActionsFromTmuxCapture(tmuxCommitIntent, {
1161
+ ...tmuxCommitCapture,
1162
+ content: tmuxCommitCapture.content.replace("COMMIT_EXIT:0", "COMMIT_EXIT:1"),
1163
+ }).length === 0,
1164
+ "a nonzero tmux commit exit marker was accepted as Git evidence"
1165
+ );
1166
+ assert(
1167
+ inferSuccessfulGitActionsFromTmuxCapture(tmuxCommitIntent, {
1168
+ ...tmuxCommitCapture,
1169
+ content: tmuxCommitCapture.content.replace("AGINTI_REVIEW_COMMIT_START", "STALE_COMMIT_START"),
1170
+ }).length === 0,
1171
+ "a tmux capture without the current command marker was accepted as fresh Git evidence"
1172
+ );
1173
+ const tmuxEvidenceState = {
1174
+ meta: {
1175
+ goalContract: { revision: 2 },
1176
+ projectVerification: { mutationRevision: 3 },
1177
+ },
1178
+ };
1179
+ const tmuxSendResult = {
1180
+ ok: true,
1181
+ toolName: "tmux_send_keys",
1182
+ target: "review:0.0",
1183
+ sentTextSha256: "a".repeat(64),
1184
+ };
1185
+ rememberTmuxGitIntent(
1186
+ tmuxEvidenceState,
1187
+ { text: tmuxCommitCommand },
1188
+ tmuxSendResult
1189
+ );
1190
+ const trackedTmuxCapture = { ...tmuxCommitCapture };
1191
+ assert(
1192
+ attachVerifiedTmuxGitEvidence(tmuxEvidenceState, trackedTmuxCapture).includes("commit"),
1193
+ "the same-pane tmux capture did not close the pending Git intent"
1194
+ );
1195
+ recordDurableEvidenceCategories(tmuxEvidenceState, trackedTmuxCapture);
1196
+ assert(
1197
+ tmuxEvidenceState.meta.durableGitEvidence.some(
1198
+ (item) =>
1199
+ item.action === "commit" &&
1200
+ item.goalRevision === 2 &&
1201
+ item.mutationRevision === 3
1202
+ ),
1203
+ "verified tmux commit evidence was not retained at the active goal and mutation revision"
1204
+ );
1205
+ assert(
1206
+ !tmuxEvidenceState.meta.pendingTmuxGitEvidence["review:0.0"],
1207
+ "verified tmux Git intent remained pending after same-pane capture"
1208
+ );
1209
+ const tmuxCommitLedger = buildScsEvidenceLedger({
1210
+ context: {
1211
+ events: [
1212
+ {
1213
+ type: "tool.completed",
1214
+ data: {
1215
+ ...tmuxCommitCapture,
1216
+ verifiedGitActions: verifiedTmuxCommit,
1217
+ verifiedGitGoalRevision: 1,
1218
+ verifiedGitMutationRevision: 1,
1219
+ },
1220
+ },
1221
+ ],
1222
+ },
1223
+ });
1224
+ assert(
1225
+ evaluateScsEvidence(commitContract, tmuxCommitLedger).missingGitActions.length === 0,
1226
+ "verified tmux commit evidence did not satisfy the completion contract"
1227
+ );
1228
+ const historicalTmuxEvents = [
1229
+ { type: "goal.updated", data: { revision: 2 } },
1230
+ {
1231
+ type: "tool.completed",
1232
+ data: {
1233
+ ok: true,
1234
+ toolName: "run_command",
1235
+ goalRevision: 2,
1236
+ projectMutationRevision: 3,
1237
+ args: { command: "git status --short" },
1238
+ exitCode: 0,
1239
+ stdout: " M README.md",
1240
+ },
1241
+ },
1242
+ {
1243
+ type: "tool.started",
1244
+ data: {
1245
+ toolName: "tmux_send_keys",
1246
+ args: { target: "review:0.0", text: tmuxCommitCommand },
1247
+ },
1248
+ },
1249
+ {
1250
+ type: "tool.completed",
1251
+ data: {
1252
+ ...tmuxSendResult,
1253
+ target: "review:0.0",
1254
+ },
1255
+ },
1256
+ {
1257
+ type: "tool.completed",
1258
+ data: {
1259
+ ...tmuxCommitCapture,
1260
+ content: undefined,
1261
+ contentPreview: tmuxCommitCapture.content,
1262
+ },
1263
+ },
1264
+ ];
1265
+ const reconciledHistoricalEvents = reconcileTmuxGitEvidenceEvents(
1266
+ historicalTmuxEvents
1267
+ );
1268
+ assert(
1269
+ reconciledHistoricalEvents.at(-1)?.data?.verifiedGitActions?.includes("commit") &&
1270
+ reconciledHistoricalEvents.at(-1)?.data?.verifiedGitGoalRevision === 2 &&
1271
+ reconciledHistoricalEvents.at(-1)?.data?.verifiedGitMutationRevision === 3,
1272
+ "restart reconciliation did not recover revision-scoped tmux commit evidence"
1273
+ );
1274
+ assert(
1275
+ evaluateScsEvidence(
1276
+ commitContract,
1277
+ buildScsEvidenceLedger({ context: { events: historicalTmuxEvents } })
1278
+ ).missingGitActions.length === 0,
1279
+ "historical marked tmux commit events did not satisfy the completion contract"
1280
+ );
1281
+ for (const invalidEvents of [
1282
+ historicalTmuxEvents.map((event, index) =>
1283
+ index === 3 ? { ...event, type: "tool.failed", data: { ...event.data, ok: false } } : event
1284
+ ),
1285
+ historicalTmuxEvents.map((event, index) =>
1286
+ index === 4 ? { ...event, data: { ...event.data, target: "other:0.0" } } : event
1287
+ ),
1288
+ historicalTmuxEvents.map((event, index) =>
1289
+ index === 4
1290
+ ? {
1291
+ ...event,
1292
+ data: {
1293
+ ...event.data,
1294
+ contentPreview: event.data.contentPreview.replace("COMMIT_EXIT:0", "COMMIT_EXIT:1"),
1295
+ },
1296
+ }
1297
+ : event
1298
+ ),
1299
+ ]) {
1300
+ assert(
1301
+ !reconcileTmuxGitEvidenceEvents(invalidEvents).at(-1)?.data?.verifiedGitActions,
1302
+ "failed, cross-pane, or nonzero historical tmux evidence was accepted"
1303
+ );
1304
+ }
1305
+ const sanitizedLongTmuxCapture = sanitizeToolResult({
1306
+ ...tmuxCommitCapture,
1307
+ content: `${"old scrollback\n".repeat(200)}${tmuxCommitCapture.content}`,
1308
+ });
1309
+ assert(
1310
+ sanitizedLongTmuxCapture.contentTruncated === true &&
1311
+ sanitizedLongTmuxCapture.contentTail.includes("===COMMIT_EXIT:0===") &&
1312
+ inferSuccessfulGitActionsFromTmuxCapture(
1313
+ tmuxCommitIntent,
1314
+ sanitizedLongTmuxCapture
1315
+ ).includes("commit"),
1316
+ "truncated tmux evidence did not preserve and verify its bounded content tail"
1317
+ );
1120
1318
  const recoveredCommitActions = inferSuccessfulGitActionsFromCommandResult({
1121
1319
  ok: true,
1122
1320
  exitCode: 0,
@@ -147,9 +147,11 @@ import {
147
147
  finishResultClaimsBlocker,
148
148
  finishResultClaimsIncompleteWork,
149
149
  hasScsBlockerEvidence,
150
+ buildTmuxGitIntent,
150
151
  inferAuthoritativeReadOnlyRoutine,
151
152
  inferGitActionsFromCommand,
152
153
  inferSuccessfulGitActionsFromCommandResult,
154
+ inferSuccessfulGitActionsFromTmuxCapture,
153
155
  isResponseOnlyEvidenceScope,
154
156
  successfulGitCommitProvesFileMutation,
155
157
  isObservationalGitAction,
@@ -9788,6 +9790,9 @@ export function sanitizeToolResult(result) {
9788
9790
  safeResult.contentTruncated = false;
9789
9791
  } else {
9790
9792
  safeResult.contentPreview = safeResult.content.slice(0, TOOL_RESULT_CONTENT_PREVIEW_CHARS);
9793
+ if (safeResult.toolName === "tmux_capture_pane") {
9794
+ safeResult.contentTail = safeResult.content.slice(-TOOL_RESULT_CONTENT_PREVIEW_CHARS);
9795
+ }
9791
9796
  safeResult.contentTruncated = true;
9792
9797
  delete safeResult.content;
9793
9798
  }
@@ -13778,7 +13783,55 @@ export function mergeDurableGitEvidence(
13778
13783
  return [...merged.values()].slice(-Math.max(1, Number(limit || 40)));
13779
13784
  }
13780
13785
 
13781
- function recordDurableEvidenceCategories(state = {}, toolResult = {}) {
13786
+ export function rememberTmuxGitIntent(state = {}, args = {}, toolResult = {}) {
13787
+ const gitIntent = toolResult?.ok ? buildTmuxGitIntent(args.text || "") : null;
13788
+ if (!gitIntent || !toolResult?.target) return null;
13789
+ state.meta = state.meta || {};
13790
+ const pendingByTarget = state.meta.pendingTmuxGitEvidence || {};
13791
+ const pending = {
13792
+ ...gitIntent,
13793
+ target: toolResult.target,
13794
+ sentTextSha256: toolResult.sentTextSha256,
13795
+ goalRevision: Math.max(0, Number(state.meta?.goalContract?.revision || 0)),
13796
+ mutationRevision: Math.max(
13797
+ 0,
13798
+ Number(state.meta?.projectVerification?.mutationRevision || 0)
13799
+ ),
13800
+ };
13801
+ pendingByTarget[toolResult.target] = pending;
13802
+ state.meta.pendingTmuxGitEvidence = Object.fromEntries(
13803
+ Object.entries(pendingByTarget).slice(-8)
13804
+ );
13805
+ toolResult.tmuxGitIntentActions = gitIntent.actions;
13806
+ toolResult.tmuxGitIntentMarkers = gitIntent.markers;
13807
+ return pending;
13808
+ }
13809
+
13810
+ export function attachVerifiedTmuxGitEvidence(state = {}, toolResult = {}) {
13811
+ const pendingByTarget = state.meta?.pendingTmuxGitEvidence || {};
13812
+ const pending = pendingByTarget[toolResult?.target];
13813
+ const verifiedGitActions = inferSuccessfulGitActionsFromTmuxCapture(
13814
+ pending,
13815
+ toolResult
13816
+ );
13817
+ if (!verifiedGitActions.length) return [];
13818
+ toolResult.verifiedGitActions = verifiedGitActions;
13819
+ toolResult.verifiedGitSource = "tmux_capture_pane";
13820
+ toolResult.verifiedGitGoalRevision = Math.max(
13821
+ 0,
13822
+ Number(pending.goalRevision || 0)
13823
+ );
13824
+ toolResult.verifiedGitMutationRevision = Math.max(
13825
+ 0,
13826
+ Number(pending.mutationRevision || 0)
13827
+ );
13828
+ toolResult.verifiedGitCommandSha256 = String(pending.sentTextSha256 || "");
13829
+ delete pendingByTarget[toolResult.target];
13830
+ state.meta.pendingTmuxGitEvidence = pendingByTarget;
13831
+ return verifiedGitActions;
13832
+ }
13833
+
13834
+ export function recordDurableEvidenceCategories(state = {}, toolResult = {}) {
13782
13835
  if (!toolResult || toolResult.ok === false || toolResult.blocked || toolResult.skipped) return;
13783
13836
  state.meta = state.meta || {};
13784
13837
  const categories = new Set(
@@ -13798,33 +13851,50 @@ function recordDurableEvidenceCategories(state = {}, toolResult = {}) {
13798
13851
  categories.add("artifact");
13799
13852
  }
13800
13853
  if (toolName === "read_image") categories.add("visual");
13854
+ const gitActions = toolName === "run_command" && Number(toolResult.exitCode ?? 0) === 0
13855
+ ? inferSuccessfulGitActionsFromCommandResult(toolResult)
13856
+ : Array.isArray(toolResult.verifiedGitActions)
13857
+ ? toolResult.verifiedGitActions
13858
+ : [];
13801
13859
  if (toolName === "run_command" && Number(toolResult.exitCode ?? 0) === 0) {
13802
13860
  categories.add("command");
13803
13861
  const command = String(toolResult.args?.command || "").trim();
13804
- const gitActions = inferSuccessfulGitActionsFromCommandResult(toolResult);
13805
- if (gitActions.length) {
13806
- categories.add("git");
13807
- if (successfulGitCommitProvesFileMutation(toolResult)) categories.add("file");
13808
- state.meta.durableGitActions = [
13809
- ...new Set([
13810
- ...(Array.isArray(state.meta.durableGitActions) ? state.meta.durableGitActions : []),
13811
- ...gitActions,
13812
- ]),
13813
- ];
13814
- const goalRevision = Math.max(0, Number(state.meta?.goalContract?.revision || 0));
13815
- const existingGitEvidence = Array.isArray(state.meta.durableGitEvidence)
13816
- ? state.meta.durableGitEvidence
13817
- : [];
13818
- state.meta.durableGitEvidence = mergeDurableGitEvidence(existingGitEvidence, gitActions, {
13819
- goalRevision,
13820
- mutationRevision: Math.max(
13821
- 0,
13822
- Number(state.meta?.projectVerification?.mutationRevision || 0)
13823
- ),
13824
- });
13825
- }
13826
13862
  if (/\b(?:pytest|unittest|npm\s+test|pnpm\s+test|yarn\s+test)\b/i.test(command)) categories.add("test");
13827
13863
  }
13864
+ if (gitActions.length) {
13865
+ categories.add("git");
13866
+ if (
13867
+ successfulGitCommitProvesFileMutation(toolResult) ||
13868
+ (gitActions.includes("commit") && toolResult.verifiedGitSource === "tmux_capture_pane")
13869
+ ) {
13870
+ categories.add("file");
13871
+ }
13872
+ state.meta.durableGitActions = [
13873
+ ...new Set([
13874
+ ...(Array.isArray(state.meta.durableGitActions) ? state.meta.durableGitActions : []),
13875
+ ...gitActions,
13876
+ ]),
13877
+ ];
13878
+ const goalRevision = Math.max(
13879
+ 0,
13880
+ Number(toolResult.verifiedGitGoalRevision ?? state.meta?.goalContract?.revision ?? 0)
13881
+ );
13882
+ const mutationRevision = Math.max(
13883
+ 0,
13884
+ Number(
13885
+ toolResult.verifiedGitMutationRevision ??
13886
+ state.meta?.projectVerification?.mutationRevision ??
13887
+ 0
13888
+ )
13889
+ );
13890
+ const existingGitEvidence = Array.isArray(state.meta.durableGitEvidence)
13891
+ ? state.meta.durableGitEvidence
13892
+ : [];
13893
+ state.meta.durableGitEvidence = mergeDurableGitEvidence(existingGitEvidence, gitActions, {
13894
+ goalRevision,
13895
+ mutationRevision,
13896
+ });
13897
+ }
13828
13898
  state.meta.durableEvidenceCategories = [...categories];
13829
13899
  }
13830
13900
 
@@ -18759,6 +18829,7 @@ async function executeTool(browserState, toolCall, snapshot, config, store, obse
18759
18829
  }
18760
18830
  case "tmux_capture_pane": {
18761
18831
  const result = await captureTmuxPane(args, config);
18832
+ attachVerifiedTmuxGitEvidence(state, result);
18762
18833
  const eventResult = sanitizeToolResult(result);
18763
18834
  await store.appendEvent(result.ok ? "tool.completed" : "tool.failed", eventResult);
18764
18835
  observers.event(result.ok ? "tool.completed" : "tool.failed", eventResult);
@@ -18766,6 +18837,7 @@ async function executeTool(browserState, toolCall, snapshot, config, store, obse
18766
18837
  }
18767
18838
  case "tmux_send_keys": {
18768
18839
  const result = await sendTmuxKeys(args, config);
18840
+ rememberTmuxGitIntent(state, args, result);
18769
18841
  const eventResult = sanitizeToolResult(result);
18770
18842
  await store.appendEvent(result.ok ? "tool.completed" : "tool.failed", eventResult);
18771
18843
  observers.event(result.ok ? "tool.completed" : "tool.failed", eventResult);
@@ -2120,6 +2120,147 @@ export function inferSuccessfulGitActionsFromCommandResult(payload = {}) {
2120
2120
  return [];
2121
2121
  }
2122
2122
 
2123
+ export function buildTmuxGitIntent(command = "") {
2124
+ const text = String(command || "");
2125
+ // `$?` is expected in the explicit tmux exit marker. Remove only that
2126
+ // non-command expansion before parsing; all other shell expansion remains
2127
+ // subject to the normal ambiguity guard.
2128
+ const parseable = text.replace(/\$\?/g, "0");
2129
+ const actions = inferGitActionsFromCommand(parseable, {
2130
+ requireFailurePropagation: false,
2131
+ }).filter((action) => !isObservationalGitAction(action));
2132
+ const markers = [
2133
+ ...text.matchAll(/={3,}([A-Z0-9][A-Z0-9_-]{3,96}(?:START|BEGIN))={3,}/g),
2134
+ ].map((match) => String(match[0] || ""));
2135
+ if (!actions.length || !markers.length) return null;
2136
+ return {
2137
+ actions: unique(actions),
2138
+ markers: unique(markers).slice(0, 4),
2139
+ };
2140
+ }
2141
+
2142
+ export function inferSuccessfulGitActionsFromTmuxCapture(intent = {}, payload = {}) {
2143
+ if (
2144
+ !intent ||
2145
+ !payload ||
2146
+ payload.ok === false ||
2147
+ payload.blocked ||
2148
+ String(payload.toolName || "") !== "tmux_capture_pane"
2149
+ ) {
2150
+ return [];
2151
+ }
2152
+ const content = [payload.content, payload.contentPreview, payload.contentTail]
2153
+ .map((item) => String(item || ""))
2154
+ .filter(Boolean)
2155
+ .join("\n");
2156
+ const markers = Array.isArray(intent.markers)
2157
+ ? intent.markers.map(String).filter(Boolean)
2158
+ : [];
2159
+ if (!content || !markers.length || !markers.some((marker) => content.includes(marker))) {
2160
+ return [];
2161
+ }
2162
+ const requested = Array.isArray(intent.actions)
2163
+ ? intent.actions.map((item) => String(item || "").toLowerCase()).filter(Boolean)
2164
+ : [];
2165
+ const verified = [];
2166
+ if (
2167
+ requested.includes("commit") &&
2168
+ /={3,}(?:GIT_)?COMMIT_EXIT\s*[:=]\s*0={3,}/i.test(content) &&
2169
+ /^\[[^\]\n]+\s+[0-9a-f]{7,}\]\s+\S+/mi.test(content)
2170
+ ) {
2171
+ verified.push("commit");
2172
+ }
2173
+ return verified;
2174
+ }
2175
+
2176
+ export function reconcileTmuxGitEvidenceEvents(events = []) {
2177
+ const source = Array.isArray(events) ? events : [];
2178
+ const pendingStarts = new Map();
2179
+ const activeIntents = new Map();
2180
+ let goalRevision = 0;
2181
+ let mutationRevision = 0;
2182
+
2183
+ return source.map((event) => {
2184
+ const type = String(event?.type || "");
2185
+ const data = event?.data && typeof event.data === "object" ? event.data : {};
2186
+ goalRevision = Math.max(
2187
+ goalRevision,
2188
+ Number(
2189
+ data.goalRevision ??
2190
+ (type === "goal.updated" ? data.revision : 0) ??
2191
+ 0
2192
+ ) || 0
2193
+ );
2194
+ mutationRevision = Math.max(
2195
+ mutationRevision,
2196
+ Number(data.projectMutationRevision ?? data.mutationRevision ?? 0) || 0
2197
+ );
2198
+
2199
+ const toolName = String(data.toolName || "");
2200
+ const target = String(data.target || data.args?.target || "");
2201
+ if (type === "tool.started" && toolName === "tmux_send_keys" && target) {
2202
+ const intent = buildTmuxGitIntent(data.args?.text || "");
2203
+ if (intent) {
2204
+ pendingStarts.set(target, {
2205
+ ...intent,
2206
+ target,
2207
+ goalRevision,
2208
+ mutationRevision,
2209
+ });
2210
+ } else {
2211
+ pendingStarts.delete(target);
2212
+ activeIntents.delete(target);
2213
+ }
2214
+ return event;
2215
+ }
2216
+
2217
+ if (["tool.completed", "tool.failed"].includes(type) && toolName === "tmux_send_keys" && target) {
2218
+ const pending = pendingStarts.get(target);
2219
+ pendingStarts.delete(target);
2220
+ if (type === "tool.completed" && data.ok !== false && pending) {
2221
+ activeIntents.set(target, {
2222
+ ...pending,
2223
+ sentTextSha256: String(data.sentTextSha256 || ""),
2224
+ });
2225
+ } else {
2226
+ activeIntents.delete(target);
2227
+ }
2228
+ return event;
2229
+ }
2230
+
2231
+ if (type === "tool.completed" && toolName === "tmux_capture_pane" && target) {
2232
+ const pending = activeIntents.get(target);
2233
+ const verifiedGitActions = inferSuccessfulGitActionsFromTmuxCapture(
2234
+ pending,
2235
+ data
2236
+ );
2237
+ if (verifiedGitActions.length) {
2238
+ activeIntents.delete(target);
2239
+ return {
2240
+ ...event,
2241
+ data: {
2242
+ ...data,
2243
+ verifiedGitActions,
2244
+ verifiedGitSource: "tmux_capture_pane",
2245
+ verifiedGitGoalRevision: Math.max(
2246
+ 0,
2247
+ Number(pending.goalRevision || 0)
2248
+ ),
2249
+ verifiedGitMutationRevision: Math.max(
2250
+ 0,
2251
+ Number(pending.mutationRevision || 0)
2252
+ ),
2253
+ verifiedGitCommandSha256: String(
2254
+ pending.sentTextSha256 || ""
2255
+ ),
2256
+ },
2257
+ };
2258
+ }
2259
+ }
2260
+ return event;
2261
+ });
2262
+ }
2263
+
2123
2264
  export function successfulGitCommitProvesFileMutation(payload = {}) {
2124
2265
  if (!inferSuccessfulGitActionsFromCommandResult(payload).includes("commit")) return false;
2125
2266
  const output = String(payload.stdout || payload.result || "");
@@ -3955,7 +4096,9 @@ function toolPayloadToEvidence(payload = {}, source = "tool") {
3955
4096
  if (["read_image", "generate_image"].includes(toolName) || /\b(screenshot|visible|thumbnail|preview|image)\b/.test(text)) {
3956
4097
  push("visual", `${toolName || "tool"} supplied visual evidence`, payload.path || payload.outputPath || args.path || "");
3957
4098
  }
3958
- const gitActions = inferSuccessfulGitActionsFromCommandResult(payload);
4099
+ const gitActions = Array.isArray(payload.verifiedGitActions)
4100
+ ? payload.verifiedGitActions
4101
+ : inferSuccessfulGitActionsFromCommandResult(payload);
3959
4102
  if (gitActions.length) {
3960
4103
  push(
3961
4104
  "git",
@@ -3964,8 +4107,18 @@ function toolPayloadToEvidence(payload = {}, source = "tool") {
3964
4107
  {
3965
4108
  gitAction: gitActions.at(-1),
3966
4109
  gitActions,
3967
- goalRevision: Math.max(0, Number(payload.goalRevision || 0)),
3968
- mutationRevision: Math.max(0, Number(payload.projectMutationRevision || 0)),
4110
+ goalRevision: Math.max(
4111
+ 0,
4112
+ Number(payload.verifiedGitGoalRevision ?? payload.goalRevision ?? 0)
4113
+ ),
4114
+ mutationRevision: Math.max(
4115
+ 0,
4116
+ Number(
4117
+ payload.verifiedGitMutationRevision ??
4118
+ payload.projectMutationRevision ??
4119
+ 0
4120
+ )
4121
+ ),
3969
4122
  }
3970
4123
  );
3971
4124
  }
@@ -4038,7 +4191,9 @@ function messageToEvidence(message = {}) {
4038
4191
  }
4039
4192
 
4040
4193
  export function buildScsEvidenceLedger({ state = {}, context = {} } = {}) {
4041
- const events = Array.isArray(context.events) ? context.events : [];
4194
+ const events = reconcileTmuxGitEvidenceEvents(
4195
+ Array.isArray(context.events) ? context.events : []
4196
+ );
4042
4197
  const messages = Array.isArray(state.messages) ? state.messages : [];
4043
4198
  const eventEvidence = events.flatMap(eventToEvidence);
4044
4199
  // Completed tools are mirrored into both the append-only event stream and