indusagi 0.12.34 → 0.13.0

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.
Files changed (55) hide show
  1. package/dist/agent.js +1247 -184
  2. package/dist/ai.js +72 -4
  3. package/dist/capabilities.js +69 -2
  4. package/dist/cli.js +83 -13
  5. package/dist/connectors-saas.js +66 -0
  6. package/dist/index.js +83 -13
  7. package/dist/interop.js +66 -0
  8. package/dist/mcp.js +270 -363
  9. package/dist/react-ink.js +15 -11
  10. package/dist/shell-app.js +83 -13
  11. package/dist/smithy.js +69 -2
  12. package/dist/swarm.js +69 -2
  13. package/dist/types/capabilities/backends/node-backends.d.ts +3 -1
  14. package/dist/types/capabilities/files/read-state-gate.d.ts +69 -0
  15. package/dist/types/capabilities/files/read-state-gate.test.d.ts +14 -0
  16. package/dist/types/capabilities/kernel/context.d.ts +4 -0
  17. package/dist/types/capabilities/kernel/index.d.ts +2 -2
  18. package/dist/types/capabilities/kernel/spec.d.ts +55 -0
  19. package/dist/types/facade/bot/actions/bash.d.ts +15 -0
  20. package/dist/types/facade/bot/actions/bash.test.d.ts +1 -0
  21. package/dist/types/facade/bot/actions/checkpoint.d.ts +49 -0
  22. package/dist/types/facade/bot/actions/checkpoint.test.d.ts +1 -0
  23. package/dist/types/facade/bot/actions/edit-utils.d.ts +86 -0
  24. package/dist/types/facade/bot/actions/edit.d.ts +18 -0
  25. package/dist/types/facade/bot/actions/edit.test.d.ts +1 -0
  26. package/dist/types/facade/bot/actions/find.d.ts +2 -0
  27. package/dist/types/facade/bot/actions/find.test.d.ts +1 -0
  28. package/dist/types/facade/bot/actions/grep.d.ts +10 -0
  29. package/dist/types/facade/bot/actions/grep.test.d.ts +1 -0
  30. package/dist/types/facade/bot/actions/index.d.ts +16 -0
  31. package/dist/types/facade/bot/actions/read-state.d.ts +83 -0
  32. package/dist/types/facade/bot/actions/read-state.test.d.ts +1 -0
  33. package/dist/types/facade/bot/actions/read.d.ts +7 -0
  34. package/dist/types/facade/bot/actions/read.test.d.ts +1 -0
  35. package/dist/types/facade/bot/actions/sandbox-backend.d.ts +99 -0
  36. package/dist/types/facade/bot/actions/sandbox-backend.test.d.ts +1 -0
  37. package/dist/types/facade/bot/actions/websearch.d.ts +5 -2
  38. package/dist/types/facade/bot/actions/websearch.test.d.ts +1 -0
  39. package/dist/types/facade/bot/actions/write.d.ts +15 -0
  40. package/dist/types/facade/bot/agent-loop.d.ts +10 -0
  41. package/dist/types/facade/bot/agent-loop.test.d.ts +1 -0
  42. package/dist/types/facade/bot/agent.d.ts +9 -1
  43. package/dist/types/facade/bot/permission-gate.test.d.ts +1 -0
  44. package/dist/types/facade/bot/types.d.ts +60 -0
  45. package/dist/types/facade/mcp-core/client.d.ts +71 -15
  46. package/dist/types/facade/mcp-core/client.test.d.ts +18 -0
  47. package/dist/types/facade/mcp-core/types.d.ts +10 -0
  48. package/dist/types/facade/ml/adapters/anthropic-retry.test.d.ts +1 -0
  49. package/dist/types/facade/ml/adapters/anthropic.d.ts +17 -0
  50. package/dist/types/facade/ml/adapters/simple-options.d.ts +13 -0
  51. package/dist/types/facade/ml/adapters/simple-options.test.d.ts +1 -0
  52. package/dist/types/react-ink/components/StatusLine.d.ts +10 -1
  53. package/dist/types/react-ink/components/ToolEventBlock.d.ts +2 -1
  54. package/dist/types/react-ink/components/ToolEventBlock.test.d.ts +1 -0
  55. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -4512,6 +4512,7 @@ __export(capabilities_exports, {
4512
4512
  });
4513
4513
 
4514
4514
  // src/capabilities/kernel/spec.ts
4515
+ var READ_STATE_HANDLE_KEY = "readState";
4515
4516
  function coerceInput(raw) {
4516
4517
  if (typeof raw === "string") {
4517
4518
  const trimmed = raw.trim();
@@ -4965,7 +4966,7 @@ var standardBudget = {
4965
4966
  [${omitted} bytes elided to stay within the output ceiling]
4966
4967
  `
4967
4968
  };
4968
- function makeNodeContext(cwd, signal, budget) {
4969
+ function makeNodeContext(cwd, signal, budget, framework) {
4969
4970
  if (typeof cwd !== "string" || cwd.length === 0) {
4970
4971
  throw new Error("makeNodeContext requires a non-empty working directory.");
4971
4972
  }
@@ -4974,10 +4975,60 @@ function makeNodeContext(cwd, signal, budget) {
4974
4975
  fs: nodeFs,
4975
4976
  shell: nodeShell,
4976
4977
  signal: signal ?? neverAborts(),
4977
- budget: budget ?? standardBudget
4978
+ budget: budget ?? standardBudget,
4979
+ ...framework ? { framework } : {}
4978
4980
  };
4979
4981
  }
4980
4982
 
4983
+ // src/capabilities/files/read-state-gate.ts
4984
+ var READ_BEFORE_EDIT_MESSAGE = "File has not been read yet. Read it first before writing to it.";
4985
+ var MODIFIED_SINCE_READ_MESSAGE = "File has been modified since read, either by the user or by a linter. Read it again before attempting to write it.";
4986
+ function getReadStateHandle(ctx) {
4987
+ const bag = ctx.framework;
4988
+ if (!bag || typeof bag !== "object") return void 0;
4989
+ const candidate = bag[READ_STATE_HANDLE_KEY];
4990
+ if (!candidate || typeof candidate !== "object") return void 0;
4991
+ const handle = candidate;
4992
+ if (typeof handle.get !== "function" || typeof handle.set !== "function" || typeof handle.has !== "function") {
4993
+ return void 0;
4994
+ }
4995
+ return handle;
4996
+ }
4997
+ async function recordReadState(ctx, absPath, handle) {
4998
+ if (!handle) return;
4999
+ try {
5000
+ const info = await ctx.fs.stat(absPath);
5001
+ const record = {
5002
+ mtimeMs: Math.floor(info.modifiedMs),
5003
+ size: info.size,
5004
+ readAt: Date.now()
5005
+ };
5006
+ handle.set(absPath, record);
5007
+ } catch {
5008
+ }
5009
+ }
5010
+ async function enforceReadGate(ctx, absPath, handle) {
5011
+ if (!handle) return { ok: true };
5012
+ if (!handle.has(absPath)) {
5013
+ return { ok: false, message: READ_BEFORE_EDIT_MESSAGE };
5014
+ }
5015
+ const recorded = handle.get(absPath);
5016
+ if (!recorded) {
5017
+ return { ok: false, message: READ_BEFORE_EDIT_MESSAGE };
5018
+ }
5019
+ let info;
5020
+ try {
5021
+ info = await ctx.fs.stat(absPath);
5022
+ } catch {
5023
+ return { ok: true };
5024
+ }
5025
+ const currentMtime = Math.floor(info.modifiedMs);
5026
+ if (currentMtime > recorded.mtimeMs || info.size !== recorded.size) {
5027
+ return { ok: false, message: MODIFIED_SINCE_READ_MESSAGE };
5028
+ }
5029
+ return { ok: true };
5030
+ }
5031
+
4981
5032
  // src/capabilities/files/read.ts
4982
5033
  var GUTTER_WIDTH = 6;
4983
5034
  var DESCRIPTION = [
@@ -5080,6 +5131,7 @@ var readTool = defineTool({
5080
5131
  const detail = err instanceof Error ? err.message : String(err);
5081
5132
  return failure(`Could not read ${path2}: ${detail}`);
5082
5133
  }
5134
+ await recordReadState(ctx, path2, getReadStateHandle(ctx));
5083
5135
  const allLines = toLines(text);
5084
5136
  const totalLines = allLines.length;
5085
5137
  if (totalLines === 0) {
@@ -5187,11 +5239,19 @@ var writeTool = defineTool({
5187
5239
  async run(input, ctx) {
5188
5240
  const path2 = readPath(input?.path);
5189
5241
  const content = readContent(input?.content);
5242
+ const handle = getReadStateHandle(ctx);
5243
+ if (handle && await ctx.fs.exists(path2)) {
5244
+ const gate = await enforceReadGate(ctx, path2, handle);
5245
+ if (!gate.ok) {
5246
+ return asText(gate.message, true);
5247
+ }
5248
+ }
5190
5249
  const folder = parentDir(path2);
5191
5250
  if (folder.length > 0) {
5192
5251
  await ctx.fs.mkdir(folder, { recursive: true });
5193
5252
  }
5194
5253
  await ctx.fs.writeFile(path2, content, "utf8");
5254
+ await recordReadState(ctx, path2, handle);
5195
5255
  const bytes = Buffer.byteLength(content, "utf8");
5196
5256
  const unit = bytes === 1 ? "byte" : "bytes";
5197
5257
  return asText(`Saved ${bytes} ${unit} to ${path2}.`);
@@ -5481,6 +5541,11 @@ async function runEdit(input, ctx) {
5481
5541
  if (!info.isFile) {
5482
5542
  return failure2(`${path2} is not a regular file, so it cannot be edited.`);
5483
5543
  }
5544
+ const handle = getReadStateHandle(ctx);
5545
+ const gate = await enforceReadGate(ctx, path2, handle);
5546
+ if (!gate.ok) {
5547
+ return failure2(gate.message);
5548
+ }
5484
5549
  const before = await ctx.fs.readFile(path2, "utf8");
5485
5550
  const literalHits = countLiteral(before, oldText);
5486
5551
  if (literalHits > 0) {
@@ -5494,6 +5559,7 @@ async function runEdit(input, ctx) {
5494
5559
  return failure2(`The replacement left ${path2} unchanged.`);
5495
5560
  }
5496
5561
  await ctx.fs.writeFile(path2, after2, "utf8");
5562
+ await recordReadState(ctx, path2, handle);
5497
5563
  return success(path2, before, after2, replaceAll ? literalHits : 1);
5498
5564
  }
5499
5565
  const spans = findFuzzySpans(before, oldText);
@@ -5517,6 +5583,7 @@ async function runEdit(input, ctx) {
5517
5583
  return failure2(`The fuzzy replacement left ${path2} unchanged.`);
5518
5584
  }
5519
5585
  await ctx.fs.writeFile(path2, after, "utf8");
5586
+ await recordReadState(ctx, path2, handle);
5520
5587
  return success(path2, before, after, targets.length);
5521
5588
  }
5522
5589
  var editTool = defineTool({
@@ -14702,6 +14769,9 @@ function statusMarker(status) {
14702
14769
  return ">";
14703
14770
  }
14704
14771
  }
14772
+ function statusColorKey(status) {
14773
+ return status === "error" ? "error" : "text";
14774
+ }
14705
14775
  function plainToolText(text) {
14706
14776
  return stripAnsi5(text);
14707
14777
  }
@@ -14743,7 +14813,7 @@ function ToolEventBlock({
14743
14813
  showTitle = true,
14744
14814
  status,
14745
14815
  summary,
14746
- theme: _theme,
14816
+ theme,
14747
14817
  title
14748
14818
  }) {
14749
14819
  const normalizedSummary = summary?.trim();
@@ -14759,19 +14829,19 @@ function ToolEventBlock({
14759
14829
  const { visibleLines, hiddenLineCount } = clampContentLines(combinedLines, maxContentLines);
14760
14830
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom, marginLeft: indent, children: [
14761
14831
  showTitle ? /* @__PURE__ */ jsxs(Box, { children: [
14762
- /* @__PURE__ */ jsx(Text, { color: "white", children: plainToolText(`${statusMarker(status)} `) }),
14763
- /* @__PURE__ */ jsx(Text, { color: "white", children: plainToolText(title) }),
14764
- showSummaryInline && normalizedSummary ? /* @__PURE__ */ jsx(Text, { color: "white", children: plainToolText(` (${normalizedSummary})`) }) : null
14832
+ /* @__PURE__ */ jsx(Text, { children: theme.color(statusColorKey(status), plainToolText(`${statusMarker(status)} `)) }),
14833
+ /* @__PURE__ */ jsx(Text, { children: theme.color(statusColorKey(status), plainToolText(title)) }),
14834
+ showSummaryInline && normalizedSummary ? /* @__PURE__ */ jsx(Text, { children: theme.muted(plainToolText(` (${normalizedSummary})`)) }) : null
14765
14835
  ] }) : null,
14766
14836
  visibleLines.map((line4, index) => /* @__PURE__ */ jsx(
14767
14837
  Box,
14768
14838
  {
14769
14839
  marginLeft: line4.kind === "response" ? showTitle ? 2 : 0 : showTitle ? 4 : 2,
14770
- children: preformatted ? /* @__PURE__ */ jsx(Text, { children: line4.text }) : /* @__PURE__ */ jsx(Text, { color: "white", children: plainToolText(line4.text) })
14840
+ children: preformatted ? /* @__PURE__ */ jsx(Text, { children: line4.text }) : /* @__PURE__ */ jsx(Text, { children: theme.color("text", plainToolText(line4.text)) })
14771
14841
  },
14772
14842
  `${line4.kind}:${index}:${stripAnsi5(line4.text)}`
14773
14843
  )),
14774
- hiddenLineCount > 0 ? /* @__PURE__ */ jsx(Box, { marginLeft: showTitle ? 4 : 2, children: /* @__PURE__ */ jsx(Text, { color: "white", children: plainToolText(`... ${hiddenLineCount} more line(s)`) }) }) : null
14844
+ hiddenLineCount > 0 ? /* @__PURE__ */ jsx(Box, { marginLeft: showTitle ? 4 : 2, children: /* @__PURE__ */ jsx(Text, { children: theme.muted(plainToolText(`... ${hiddenLineCount} more line(s)`)) }) }) : null
14775
14845
  ] });
14776
14846
  }
14777
14847
 
@@ -15114,20 +15184,20 @@ function MessageList({
15114
15184
  }
15115
15185
 
15116
15186
  // src/react-ink/components/StatusLine.tsx
15117
- function StatusLine({ snapshot, status, theme }) {
15187
+ function StatusLine({ snapshot, status, theme, showBusyText = true }) {
15118
15188
  let text = status?.text;
15119
15189
  let tone = status?.kind ?? "info";
15120
15190
  if (!text) {
15121
- if (snapshot.isCompacting) {
15191
+ if (showBusyText && snapshot.isCompacting) {
15122
15192
  text = "Compacting conversation context...";
15123
15193
  tone = "busy";
15124
- } else if (snapshot.isBashRunning) {
15194
+ } else if (showBusyText && snapshot.isBashRunning) {
15125
15195
  text = "Running bash command...";
15126
15196
  tone = "busy";
15127
- } else if (snapshot.pendingToolCallCount > 0 || snapshot.pendingMessageCount > 0) {
15197
+ } else if (showBusyText && (snapshot.pendingToolCallCount > 0 || snapshot.pendingMessageCount > 0)) {
15128
15198
  text = "Agent working...";
15129
15199
  tone = "busy";
15130
- } else if (snapshot.isStreaming) {
15200
+ } else if (showBusyText && snapshot.isStreaming) {
15131
15201
  text = "Agent working...";
15132
15202
  tone = "busy";
15133
15203
  } else if (snapshot.error) {
package/dist/interop.js CHANGED
@@ -427,6 +427,7 @@ async function startServerFleet(config) {
427
427
  }
428
428
 
429
429
  // src/capabilities/kernel/spec.ts
430
+ var READ_STATE_HANDLE_KEY = "readState";
430
431
  function coerceInput(raw) {
431
432
  if (typeof raw === "string") {
432
433
  const trimmed = raw.trim();
@@ -878,6 +879,55 @@ var standardBudget = {
878
879
  `
879
880
  };
880
881
 
882
+ // src/capabilities/files/read-state-gate.ts
883
+ var READ_BEFORE_EDIT_MESSAGE = "File has not been read yet. Read it first before writing to it.";
884
+ var MODIFIED_SINCE_READ_MESSAGE = "File has been modified since read, either by the user or by a linter. Read it again before attempting to write it.";
885
+ function getReadStateHandle(ctx) {
886
+ const bag = ctx.framework;
887
+ if (!bag || typeof bag !== "object") return void 0;
888
+ const candidate = bag[READ_STATE_HANDLE_KEY];
889
+ if (!candidate || typeof candidate !== "object") return void 0;
890
+ const handle = candidate;
891
+ if (typeof handle.get !== "function" || typeof handle.set !== "function" || typeof handle.has !== "function") {
892
+ return void 0;
893
+ }
894
+ return handle;
895
+ }
896
+ async function recordReadState(ctx, absPath, handle) {
897
+ if (!handle) return;
898
+ try {
899
+ const info = await ctx.fs.stat(absPath);
900
+ const record = {
901
+ mtimeMs: Math.floor(info.modifiedMs),
902
+ size: info.size,
903
+ readAt: Date.now()
904
+ };
905
+ handle.set(absPath, record);
906
+ } catch {
907
+ }
908
+ }
909
+ async function enforceReadGate(ctx, absPath, handle) {
910
+ if (!handle) return { ok: true };
911
+ if (!handle.has(absPath)) {
912
+ return { ok: false, message: READ_BEFORE_EDIT_MESSAGE };
913
+ }
914
+ const recorded = handle.get(absPath);
915
+ if (!recorded) {
916
+ return { ok: false, message: READ_BEFORE_EDIT_MESSAGE };
917
+ }
918
+ let info;
919
+ try {
920
+ info = await ctx.fs.stat(absPath);
921
+ } catch {
922
+ return { ok: true };
923
+ }
924
+ const currentMtime = Math.floor(info.modifiedMs);
925
+ if (currentMtime > recorded.mtimeMs || info.size !== recorded.size) {
926
+ return { ok: false, message: MODIFIED_SINCE_READ_MESSAGE };
927
+ }
928
+ return { ok: true };
929
+ }
930
+
881
931
  // src/capabilities/files/read.ts
882
932
  var GUTTER_WIDTH = 6;
883
933
  var DESCRIPTION = [
@@ -980,6 +1030,7 @@ var readTool = defineTool({
980
1030
  const detail = err instanceof Error ? err.message : String(err);
981
1031
  return failure(`Could not read ${path}: ${detail}`);
982
1032
  }
1033
+ await recordReadState(ctx, path, getReadStateHandle(ctx));
983
1034
  const allLines = toLines(text);
984
1035
  const totalLines = allLines.length;
985
1036
  if (totalLines === 0) {
@@ -1087,11 +1138,19 @@ var writeTool = defineTool({
1087
1138
  async run(input, ctx) {
1088
1139
  const path = readPath(input?.path);
1089
1140
  const content = readContent(input?.content);
1141
+ const handle = getReadStateHandle(ctx);
1142
+ if (handle && await ctx.fs.exists(path)) {
1143
+ const gate = await enforceReadGate(ctx, path, handle);
1144
+ if (!gate.ok) {
1145
+ return asText(gate.message, true);
1146
+ }
1147
+ }
1090
1148
  const folder = parentDir(path);
1091
1149
  if (folder.length > 0) {
1092
1150
  await ctx.fs.mkdir(folder, { recursive: true });
1093
1151
  }
1094
1152
  await ctx.fs.writeFile(path, content, "utf8");
1153
+ await recordReadState(ctx, path, handle);
1095
1154
  const bytes = Buffer.byteLength(content, "utf8");
1096
1155
  const unit = bytes === 1 ? "byte" : "bytes";
1097
1156
  return asText(`Saved ${bytes} ${unit} to ${path}.`);
@@ -1381,6 +1440,11 @@ async function runEdit(input, ctx) {
1381
1440
  if (!info.isFile) {
1382
1441
  return failure2(`${path} is not a regular file, so it cannot be edited.`);
1383
1442
  }
1443
+ const handle = getReadStateHandle(ctx);
1444
+ const gate = await enforceReadGate(ctx, path, handle);
1445
+ if (!gate.ok) {
1446
+ return failure2(gate.message);
1447
+ }
1384
1448
  const before = await ctx.fs.readFile(path, "utf8");
1385
1449
  const literalHits = countLiteral(before, oldText);
1386
1450
  if (literalHits > 0) {
@@ -1394,6 +1458,7 @@ async function runEdit(input, ctx) {
1394
1458
  return failure2(`The replacement left ${path} unchanged.`);
1395
1459
  }
1396
1460
  await ctx.fs.writeFile(path, after2, "utf8");
1461
+ await recordReadState(ctx, path, handle);
1397
1462
  return success(path, before, after2, replaceAll ? literalHits : 1);
1398
1463
  }
1399
1464
  const spans = findFuzzySpans(before, oldText);
@@ -1417,6 +1482,7 @@ async function runEdit(input, ctx) {
1417
1482
  return failure2(`The fuzzy replacement left ${path} unchanged.`);
1418
1483
  }
1419
1484
  await ctx.fs.writeFile(path, after, "utf8");
1485
+ await recordReadState(ctx, path, handle);
1420
1486
  return success(path, before, after, targets.length);
1421
1487
  }
1422
1488
  var editTool = defineTool({