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.
- package/dist/agent.js +1247 -184
- package/dist/ai.js +72 -4
- package/dist/capabilities.js +69 -2
- package/dist/cli.js +83 -13
- package/dist/connectors-saas.js +66 -0
- package/dist/index.js +83 -13
- package/dist/interop.js +66 -0
- package/dist/mcp.js +270 -363
- package/dist/react-ink.js +15 -11
- package/dist/shell-app.js +83 -13
- package/dist/smithy.js +69 -2
- package/dist/swarm.js +69 -2
- package/dist/types/capabilities/backends/node-backends.d.ts +3 -1
- package/dist/types/capabilities/files/read-state-gate.d.ts +69 -0
- package/dist/types/capabilities/files/read-state-gate.test.d.ts +14 -0
- package/dist/types/capabilities/kernel/context.d.ts +4 -0
- package/dist/types/capabilities/kernel/index.d.ts +2 -2
- package/dist/types/capabilities/kernel/spec.d.ts +55 -0
- package/dist/types/facade/bot/actions/bash.d.ts +15 -0
- package/dist/types/facade/bot/actions/bash.test.d.ts +1 -0
- package/dist/types/facade/bot/actions/checkpoint.d.ts +49 -0
- package/dist/types/facade/bot/actions/checkpoint.test.d.ts +1 -0
- package/dist/types/facade/bot/actions/edit-utils.d.ts +86 -0
- package/dist/types/facade/bot/actions/edit.d.ts +18 -0
- package/dist/types/facade/bot/actions/edit.test.d.ts +1 -0
- package/dist/types/facade/bot/actions/find.d.ts +2 -0
- package/dist/types/facade/bot/actions/find.test.d.ts +1 -0
- package/dist/types/facade/bot/actions/grep.d.ts +10 -0
- package/dist/types/facade/bot/actions/grep.test.d.ts +1 -0
- package/dist/types/facade/bot/actions/index.d.ts +16 -0
- package/dist/types/facade/bot/actions/read-state.d.ts +83 -0
- package/dist/types/facade/bot/actions/read-state.test.d.ts +1 -0
- package/dist/types/facade/bot/actions/read.d.ts +7 -0
- package/dist/types/facade/bot/actions/read.test.d.ts +1 -0
- package/dist/types/facade/bot/actions/sandbox-backend.d.ts +99 -0
- package/dist/types/facade/bot/actions/sandbox-backend.test.d.ts +1 -0
- package/dist/types/facade/bot/actions/websearch.d.ts +5 -2
- package/dist/types/facade/bot/actions/websearch.test.d.ts +1 -0
- package/dist/types/facade/bot/actions/write.d.ts +15 -0
- package/dist/types/facade/bot/agent-loop.d.ts +10 -0
- package/dist/types/facade/bot/agent-loop.test.d.ts +1 -0
- package/dist/types/facade/bot/agent.d.ts +9 -1
- package/dist/types/facade/bot/permission-gate.test.d.ts +1 -0
- package/dist/types/facade/bot/types.d.ts +60 -0
- package/dist/types/facade/mcp-core/client.d.ts +71 -15
- package/dist/types/facade/mcp-core/client.test.d.ts +18 -0
- package/dist/types/facade/mcp-core/types.d.ts +10 -0
- package/dist/types/facade/ml/adapters/anthropic-retry.test.d.ts +1 -0
- package/dist/types/facade/ml/adapters/anthropic.d.ts +17 -0
- package/dist/types/facade/ml/adapters/simple-options.d.ts +13 -0
- package/dist/types/facade/ml/adapters/simple-options.test.d.ts +1 -0
- package/dist/types/react-ink/components/StatusLine.d.ts +10 -1
- package/dist/types/react-ink/components/ToolEventBlock.d.ts +2 -1
- package/dist/types/react-ink/components/ToolEventBlock.test.d.ts +1 -0
- 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
|
|
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, {
|
|
14763
|
-
/* @__PURE__ */ jsx(Text, {
|
|
14764
|
-
showSummaryInline && normalizedSummary ? /* @__PURE__ */ jsx(Text, {
|
|
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, {
|
|
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, {
|
|
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({
|