@lazyingart/agintiflow 0.20.330 → 0.20.331

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.
@@ -721,3 +721,22 @@ repository and report whether a README exists. The scripted DeepSeek-shaped
721
721
  response calls `inspect_project` with `reason` plus a real `limit`; the persisted
722
722
  runtime dispatches the inspection, records zero tool-contract failures, and
723
723
  finishes with the verified README status without mutating the workspace.
724
+
725
+ ### Native tmux recovery from generic shell aliases
726
+
727
+ `tmux-run-command-native-recovery-094` covers a reusable coordination-tool
728
+ handoff gap seen in recent retained runtime evidence. A normal supervision
729
+ prompt selected the host tmux tool bundle, but the provider tried the native
730
+ tool name `tmux_list_sessions` and later `tmux list-sessions` through
731
+ `run_command`. AgInTiFlow treated those as generic shell commands, producing a
732
+ host permission pause or shell failure instead of using the already offered
733
+ native tmux listing tool.
734
+
735
+ The runtime now auto-corrects only exact read-only tmux session-list aliases
736
+ from `run_command` to `tmux_list_sessions` before shell guardrails run. It
737
+ records `tool.auto_corrected`, preserves the original requested tool in
738
+ `tool.started`, dispatches no generic shell command, and leaves arbitrary tmux
739
+ startup/send/mutation shell commands blocked by the existing permission policy.
740
+ The focused persisted-runtime regression verifies both exact aliases and the
741
+ negative mutating command case; the tmux guardrail, progressive-tool,
742
+ provider-handoff, syntax, package-audit, dry-pack, and full npm suites pass.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyingart/agintiflow",
3
- "version": "0.20.330",
3
+ "version": "0.20.331",
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",
@@ -19,6 +19,7 @@ import {
19
19
  evaluateSourceFreeResponseClaims,
20
20
  finishResultClaimsIncompleteWork,
21
21
  } from "../src/scs-evidence.js";
22
+ import { tmuxAvailable } from "../src/tmux-tools.js";
22
23
 
23
24
  const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
24
25
  const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "agintiflow-truthful-completion-"));
@@ -860,6 +861,118 @@ try {
860
861
  "permission blocker triggered an SCS replan instead of waiting for approval"
861
862
  );
862
863
 
864
+ if (await tmuxAvailable()) {
865
+ const tmuxAliasRecovery = await runCase({
866
+ id: "tmux-shell-alias-recovery",
867
+ goal: "Check durable host tmux sessions and report whether any sessions exist.",
868
+ taskProfile: "auto",
869
+ executionTier: "focused",
870
+ allowShellTool: true,
871
+ responses: [
872
+ assistant("", [toolCall("tmux-as-shell", "run_command", { command: "tmux_list_sessions" })]),
873
+ assistant("", [
874
+ toolCall("finish-tmux-alias", "finish", {
875
+ result: "Checked durable host tmux sessions through the native tmux listing tool.",
876
+ }),
877
+ ]),
878
+ ],
879
+ });
880
+ assert.equal(tmuxAliasRecovery.calls.length, 2);
881
+ assert.equal(tmuxAliasRecovery.result.stopped, undefined);
882
+ assert(
883
+ tmuxAliasRecovery.events.some(
884
+ (event) =>
885
+ event.type === "tool.auto_corrected" &&
886
+ event.data?.requestedToolName === "run_command" &&
887
+ event.data?.toolName === "tmux_list_sessions" &&
888
+ event.data?.originalCommand === "tmux_list_sessions"
889
+ ),
890
+ "run_command tmux_list_sessions alias was not recovered to the native tmux tool"
891
+ );
892
+ assert(
893
+ tmuxAliasRecovery.events.some(
894
+ (event) =>
895
+ event.type === "tool.started" &&
896
+ event.data?.toolName === "tmux_list_sessions" &&
897
+ event.data?.requestedToolName === "run_command"
898
+ ),
899
+ "native tmux listing was not started with original requested tool evidence"
900
+ );
901
+ assert(
902
+ tmuxAliasRecovery.events.some(
903
+ (event) =>
904
+ event.type === "tool.completed" &&
905
+ event.data?.toolName === "tmux_list_sessions"
906
+ ),
907
+ "native tmux listing did not complete after alias recovery"
908
+ );
909
+ assert(
910
+ !tmuxAliasRecovery.events.some(
911
+ (event) => event.type === "tool.started" && event.data?.toolName === "run_command"
912
+ ),
913
+ "tmux alias recovery still dispatched the generic shell command"
914
+ );
915
+ assert(
916
+ !tmuxAliasRecovery.events.some(
917
+ (event) => event.type === "session.stopped" && event.data?.reason === "permission_required"
918
+ ),
919
+ "tmux alias recovery still paused on shell permission"
920
+ );
921
+
922
+ const tmuxReadonlyCommandRecovery = await runCase({
923
+ id: "tmux-readonly-command-recovery",
924
+ goal: "List durable host tmux sessions using the available coordination tool.",
925
+ taskProfile: "auto",
926
+ executionTier: "focused",
927
+ allowShellTool: true,
928
+ responses: [
929
+ assistant("", [toolCall("tmux-command-as-shell", "run_command", { command: "tmux list-sessions" })]),
930
+ assistant("", [
931
+ toolCall("finish-tmux-command", "finish", {
932
+ result: "Checked durable host tmux sessions through the native tmux listing tool.",
933
+ }),
934
+ ]),
935
+ ],
936
+ });
937
+ assert.equal(tmuxReadonlyCommandRecovery.result.stopped, undefined);
938
+ assert(
939
+ tmuxReadonlyCommandRecovery.events.some(
940
+ (event) =>
941
+ event.type === "tool.auto_corrected" &&
942
+ event.data?.toolName === "tmux_list_sessions" &&
943
+ event.data?.originalCommand === "tmux list-sessions"
944
+ ),
945
+ "exact tmux list-sessions shell command was not recovered"
946
+ );
947
+ assert(
948
+ !tmuxReadonlyCommandRecovery.events.some(
949
+ (event) => event.type === "tool.started" && event.data?.toolName === "run_command"
950
+ ),
951
+ "exact tmux list-sessions recovery still dispatched run_command"
952
+ );
953
+
954
+ const tmuxMutationStillBlocked = await runCase({
955
+ id: "tmux-mutating-command-still-blocked",
956
+ goal: "Try to create a tmux session through the generic shell.",
957
+ taskProfile: "auto",
958
+ executionTier: "focused",
959
+ allowShellTool: true,
960
+ responses: [
961
+ assistant("", [
962
+ toolCall("tmux-mutating-shell", "run_command", {
963
+ command: "tmux new-session -d -s should-not-autocorrect",
964
+ }),
965
+ ]),
966
+ ],
967
+ });
968
+ assert.equal(tmuxMutationStillBlocked.result.stopped, true);
969
+ assert.equal(tmuxMutationStillBlocked.result.reason, "permission_required");
970
+ assert(
971
+ !tmuxMutationStillBlocked.events.some((event) => event.type === "tool.auto_corrected"),
972
+ "mutating tmux shell command was incorrectly auto-corrected"
973
+ );
974
+ }
975
+
863
976
  const reasoningTruncation = await runCase({
864
977
  id: "reasoning-only-tool-continuation",
865
978
  goal: "Run pwd and report the verified working directory.",
@@ -5058,6 +5058,58 @@ export function normalizeNoMatchQueryResult(result = {}, policy = {}) {
5058
5058
  };
5059
5059
  }
5060
5060
 
5061
+ function runtimeFlagDisabled(value) {
5062
+ if (value === false || value === 0) return true;
5063
+ return /^(false|off|no|0)$/i.test(String(value ?? "").trim());
5064
+ }
5065
+
5066
+ function exactTmuxListSessionsShellAlias(command = "") {
5067
+ const canonical = canonicalizeShellCommand(command);
5068
+ if (!canonical || canonical.includes("\n")) return null;
5069
+ const sequence = parseTopLevelShellSequence(canonical);
5070
+ if (
5071
+ sequence.openQuote ||
5072
+ sequence.trailingEscape ||
5073
+ sequence.trailingSeparator ||
5074
+ sequence.commands.length !== 1
5075
+ ) {
5076
+ return null;
5077
+ }
5078
+ const tokens = tokenizeShellWords(sequence.commands[0]).map((token) =>
5079
+ String(token || "")
5080
+ );
5081
+ if (tokens.length === 1 && tokens[0] === "tmux_list_sessions") {
5082
+ return { command: canonical, alias: "tool-name-as-shell-command" };
5083
+ }
5084
+ if (
5085
+ tokens.length === 2 &&
5086
+ tokens[0] === "tmux" &&
5087
+ (tokens[1] === "list-sessions" || tokens[1] === "ls")
5088
+ ) {
5089
+ return { command: canonical, alias: "tmux-readonly-list-command" };
5090
+ }
5091
+ return null;
5092
+ }
5093
+
5094
+ function recoverRunCommandTmuxListAlias(requestedToolName, args = {}, config = {}) {
5095
+ if (requestedToolName !== "run_command") return null;
5096
+ if (
5097
+ runtimeFlagDisabled(config.allowShellTool) ||
5098
+ runtimeFlagDisabled(config.allowTmuxTools) ||
5099
+ runtimeFlagDisabled(config.allowCoordinationTools)
5100
+ ) {
5101
+ return null;
5102
+ }
5103
+ const alias = exactTmuxListSessionsShellAlias(args.command);
5104
+ if (!alias) return null;
5105
+ return {
5106
+ requestedToolName,
5107
+ toolName: "tmux_list_sessions",
5108
+ reason: alias.alias,
5109
+ originalCommand: alias.command,
5110
+ };
5111
+ }
5112
+
5061
5113
  function hashForLog(value) {
5062
5114
  return crypto.createHash("sha256").update(String(value ?? "")).digest("hex");
5063
5115
  }
@@ -20506,6 +20558,14 @@ async function executeTool(browserState, toolCall, snapshot, config, store, obse
20506
20558
  prompt: "Inspect this exact generated image as verification evidence. Describe the visible content, readability, clipping, labels, scale, and any defects that require repair.",
20507
20559
  };
20508
20560
  }
20561
+ const tmuxListAliasCorrection = !autoCorrection
20562
+ ? recoverRunCommandTmuxListAlias(requestedToolName, args, config)
20563
+ : null;
20564
+ if (tmuxListAliasCorrection) {
20565
+ toolName = tmuxListAliasCorrection.toolName;
20566
+ args = { includePanes: true };
20567
+ autoCorrection = tmuxListAliasCorrection;
20568
+ }
20509
20569
  const repositoryCommitPaths = [
20510
20570
  ...(Array.isArray(config.repositoryStateRepairCommitPaths)
20511
20571
  ? config.repositoryStateRepairCommitPaths