indusagi 0.12.33 → 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 (70) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/agent.js +1247 -184
  3. package/dist/ai.js +72 -4
  4. package/dist/capabilities.js +69 -2
  5. package/dist/cli.js +1353 -29
  6. package/dist/connectors-saas.js +66 -0
  7. package/dist/index.js +1353 -29
  8. package/dist/interop.js +66 -0
  9. package/dist/mcp.js +270 -363
  10. package/dist/react-ink.js +1391 -41
  11. package/dist/shell-app.js +1353 -29
  12. package/dist/smithy.js +69 -2
  13. package/dist/swarm.js +69 -2
  14. package/dist/types/capabilities/backends/node-backends.d.ts +3 -1
  15. package/dist/types/capabilities/files/read-state-gate.d.ts +69 -0
  16. package/dist/types/capabilities/files/read-state-gate.test.d.ts +14 -0
  17. package/dist/types/capabilities/kernel/context.d.ts +4 -0
  18. package/dist/types/capabilities/kernel/index.d.ts +2 -2
  19. package/dist/types/capabilities/kernel/spec.d.ts +55 -0
  20. package/dist/types/facade/bot/actions/bash.d.ts +15 -0
  21. package/dist/types/facade/bot/actions/bash.test.d.ts +1 -0
  22. package/dist/types/facade/bot/actions/checkpoint.d.ts +49 -0
  23. package/dist/types/facade/bot/actions/checkpoint.test.d.ts +1 -0
  24. package/dist/types/facade/bot/actions/edit-utils.d.ts +86 -0
  25. package/dist/types/facade/bot/actions/edit.d.ts +18 -0
  26. package/dist/types/facade/bot/actions/edit.test.d.ts +1 -0
  27. package/dist/types/facade/bot/actions/find.d.ts +2 -0
  28. package/dist/types/facade/bot/actions/find.test.d.ts +1 -0
  29. package/dist/types/facade/bot/actions/grep.d.ts +10 -0
  30. package/dist/types/facade/bot/actions/grep.test.d.ts +1 -0
  31. package/dist/types/facade/bot/actions/index.d.ts +16 -0
  32. package/dist/types/facade/bot/actions/read-state.d.ts +83 -0
  33. package/dist/types/facade/bot/actions/read-state.test.d.ts +1 -0
  34. package/dist/types/facade/bot/actions/read.d.ts +7 -0
  35. package/dist/types/facade/bot/actions/read.test.d.ts +1 -0
  36. package/dist/types/facade/bot/actions/sandbox-backend.d.ts +99 -0
  37. package/dist/types/facade/bot/actions/sandbox-backend.test.d.ts +1 -0
  38. package/dist/types/facade/bot/actions/websearch.d.ts +5 -2
  39. package/dist/types/facade/bot/actions/websearch.test.d.ts +1 -0
  40. package/dist/types/facade/bot/actions/write.d.ts +15 -0
  41. package/dist/types/facade/bot/agent-loop.d.ts +10 -0
  42. package/dist/types/facade/bot/agent-loop.test.d.ts +1 -0
  43. package/dist/types/facade/bot/agent.d.ts +9 -1
  44. package/dist/types/facade/bot/permission-gate.test.d.ts +1 -0
  45. package/dist/types/facade/bot/types.d.ts +60 -0
  46. package/dist/types/facade/mcp-core/client.d.ts +71 -15
  47. package/dist/types/facade/mcp-core/client.test.d.ts +18 -0
  48. package/dist/types/facade/mcp-core/types.d.ts +10 -0
  49. package/dist/types/facade/ml/adapters/anthropic-retry.test.d.ts +1 -0
  50. package/dist/types/facade/ml/adapters/anthropic.d.ts +17 -0
  51. package/dist/types/facade/ml/adapters/simple-options.d.ts +13 -0
  52. package/dist/types/facade/ml/adapters/simple-options.test.d.ts +1 -0
  53. package/dist/types/react-ink/components/StatusLine.d.ts +10 -1
  54. package/dist/types/react-ink/components/ToolEventBlock.d.ts +9 -1
  55. package/dist/types/react-ink/components/ToolEventBlock.test.d.ts +1 -0
  56. package/dist/types/react-ink/components/dialogs/SelectableDialog.d.ts +7 -1
  57. package/dist/types/react-ink/components/dialogs/ThemeDialog.d.ts +21 -2
  58. package/dist/types/react-ink/diff/Diff.d.ts +22 -0
  59. package/dist/types/react-ink/diff/diff.test.d.ts +1 -0
  60. package/dist/types/react-ink/diff/structured.d.ts +41 -0
  61. package/dist/types/react-ink/diff/word-diff.d.ts +27 -0
  62. package/dist/types/react-ink/index.d.ts +8 -0
  63. package/dist/types/react-ink/markdown/Markdown.d.ts +23 -0
  64. package/dist/types/react-ink/markdown/MarkdownTable.d.ts +19 -0
  65. package/dist/types/react-ink/markdown/StreamingMarkdown.d.ts +34 -0
  66. package/dist/types/react-ink/markdown/format-token.d.ts +39 -0
  67. package/dist/types/react-ink/markdown/highlight.d.ts +31 -0
  68. package/dist/types/react-ink/theme-adapter.d.ts +58 -1
  69. package/dist/types/react-ink/utils/tool-display.d.ts +17 -1
  70. package/package.json +5 -1
package/dist/ai.js CHANGED
@@ -13199,24 +13199,58 @@ function normalizeProviderError(error) {
13199
13199
  }
13200
13200
  return new SimpleOptionsProviderError("Unknown provider error", "unknown", error);
13201
13201
  }
13202
+ function abortReason(signal) {
13203
+ const reason = signal.reason;
13204
+ if (reason instanceof SimpleOptionsProviderError) return reason;
13205
+ if (reason instanceof Error) return new SimpleOptionsProviderError(reason.message, "unknown", reason);
13206
+ return new SimpleOptionsProviderError("Request was aborted", "unknown", reason);
13207
+ }
13208
+ function abortableSleep(ms, signal) {
13209
+ return new Promise((resolve, reject) => {
13210
+ if (signal?.aborted) {
13211
+ reject(abortReason(signal));
13212
+ return;
13213
+ }
13214
+ let onAbort;
13215
+ const timer = setTimeout(() => {
13216
+ if (signal && onAbort) signal.removeEventListener("abort", onAbort);
13217
+ resolve();
13218
+ }, ms);
13219
+ if (signal) {
13220
+ onAbort = () => {
13221
+ clearTimeout(timer);
13222
+ signal.removeEventListener("abort", onAbort);
13223
+ reject(abortReason(signal));
13224
+ };
13225
+ signal.addEventListener("abort", onAbort, { once: true });
13226
+ }
13227
+ });
13228
+ }
13202
13229
  async function executeWithRetry(operation, policy) {
13203
13230
  let attempt = 0;
13204
13231
  let lastError;
13205
13232
  while (attempt < policy.maxAttempts) {
13233
+ if (policy.signal?.aborted) {
13234
+ throw abortReason(policy.signal);
13235
+ }
13206
13236
  attempt++;
13207
13237
  try {
13208
13238
  return await operation();
13209
13239
  } catch (error) {
13210
13240
  lastError = error;
13211
13241
  const normalized = normalizeProviderError(error);
13242
+ if (policy.signal?.aborted) {
13243
+ throw normalized;
13244
+ }
13212
13245
  const defaultRetryable = normalized.code === "rate_limit" || normalized.code === "timeout" || normalized.code === "network";
13213
13246
  const retryable = policy.shouldRetry ? policy.shouldRetry(error, attempt) : defaultRetryable;
13214
13247
  if (!retryable || attempt >= policy.maxAttempts) {
13215
13248
  throw normalized;
13216
13249
  }
13217
13250
  const maxDelay = policy.maxDelayMs ?? Number.MAX_SAFE_INTEGER;
13218
- const backoff = Math.min(policy.baseDelayMs * 2 ** (attempt - 1), maxDelay);
13219
- await new Promise((resolve) => setTimeout(resolve, backoff));
13251
+ const retryAfterMs = policy.getRetryAfterMs?.(error) ?? null;
13252
+ const backoff = retryAfterMs != null ? Math.min(retryAfterMs, maxDelay) : Math.min(policy.baseDelayMs * 2 ** (attempt - 1), maxDelay);
13253
+ await abortableSleep(backoff, policy.signal);
13220
13254
  }
13221
13255
  }
13222
13256
  throw normalizeProviderError(lastError);
@@ -13692,6 +13726,30 @@ var AnthropicEventReducer = class {
13692
13726
  calculateCost(this.model, this.output.usage);
13693
13727
  }
13694
13728
  };
13729
+ function parseAnthropicRetryAfterMs(error) {
13730
+ if (error instanceof Anthropic.APIError) {
13731
+ const header = error.headers?.get?.("retry-after");
13732
+ const seconds = header ? parseInt(header, 10) : Number.NaN;
13733
+ if (!Number.isNaN(seconds) && seconds >= 0) return seconds * 1e3;
13734
+ }
13735
+ return null;
13736
+ }
13737
+ function shouldRetryAnthropic(error, _attempt) {
13738
+ if (error instanceof Anthropic.APIError) {
13739
+ const status = error.status;
13740
+ if (status === 408 || status === 409 || status === 429 || status === 529) return true;
13741
+ if (typeof status === "number" && status >= 500) return true;
13742
+ if (typeof status === "number" && status >= 400 && status < 500) return false;
13743
+ const body = `${error.message} ${JSON.stringify(error.error ?? "")}`.toLowerCase();
13744
+ if (body.includes('"type":"overloaded_error"') || body.includes("overloaded")) return true;
13745
+ return false;
13746
+ }
13747
+ if (error instanceof Error) {
13748
+ const msg = error.message.toLowerCase();
13749
+ return msg.includes("rate limit") || msg.includes("rate_limit") || msg.includes("timeout") || msg.includes("timed out") || msg.includes("network") || msg.includes("econnreset") || msg.includes("etimedout") || msg.includes("overloaded");
13750
+ }
13751
+ return false;
13752
+ }
13695
13753
  var streamAnthropic = (model, context, options) => {
13696
13754
  const stream2 = new AssistantMessageEventStream();
13697
13755
  const output = createAssistantMessageOutput(model);
@@ -13719,8 +13777,16 @@ var streamAnthropic = (model, context, options) => {
13719
13777
  });
13720
13778
  },
13721
13779
  {
13722
- maxAttempts: options?.signal ? 1 : 3,
13723
- baseDelayMs: 250
13780
+ // Transient retries are NO LONGER gated on signal presence:
13781
+ // the live path always carries a signal, so the old ternary
13782
+ // collapsed to a single attempt = zero retries on 429/529/5xx.
13783
+ // Abort still short-circuits immediately (see executeWithRetry).
13784
+ maxAttempts: 3,
13785
+ baseDelayMs: 500,
13786
+ maxDelayMs: 32e3,
13787
+ signal: options?.signal,
13788
+ shouldRetry: shouldRetryAnthropic,
13789
+ getRetryAfterMs: parseAnthropicRetryAfterMs
13724
13790
  }
13725
13791
  );
13726
13792
  if (options?.signal?.aborted) {
@@ -19700,6 +19766,7 @@ export {
19700
19766
  normalizeToolCallId,
19701
19767
  normalizeToolName,
19702
19768
  openaiCodexOAuthProvider,
19769
+ parseAnthropicRetryAfterMs,
19703
19770
  parseStreamingJson,
19704
19771
  parseStreamingJsonWithDiagnostics,
19705
19772
  processResponsesStream,
@@ -19719,6 +19786,7 @@ export {
19719
19786
  retainThoughtSignature,
19720
19787
  rotateEnvApiKey,
19721
19788
  sanitizeContentString,
19789
+ shouldRetryAnthropic,
19722
19790
  stream,
19723
19791
  streamAnthropic,
19724
19792
  streamAzureOpenAIResponses,
@@ -1,4 +1,5 @@
1
1
  // src/capabilities/kernel/spec.ts
2
+ var READ_STATE_HANDLE_KEY = "readState";
2
3
  function coerceInput(raw) {
3
4
  if (typeof raw === "string") {
4
5
  const trimmed = raw.trim();
@@ -452,7 +453,7 @@ var standardBudget = {
452
453
  [${omitted} bytes elided to stay within the output ceiling]
453
454
  `
454
455
  };
455
- function makeNodeContext(cwd, signal, budget) {
456
+ function makeNodeContext(cwd, signal, budget, framework) {
456
457
  if (typeof cwd !== "string" || cwd.length === 0) {
457
458
  throw new Error("makeNodeContext requires a non-empty working directory.");
458
459
  }
@@ -461,10 +462,60 @@ function makeNodeContext(cwd, signal, budget) {
461
462
  fs: nodeFs,
462
463
  shell: nodeShell,
463
464
  signal: signal ?? neverAborts(),
464
- budget: budget ?? standardBudget
465
+ budget: budget ?? standardBudget,
466
+ ...framework ? { framework } : {}
465
467
  };
466
468
  }
467
469
 
470
+ // src/capabilities/files/read-state-gate.ts
471
+ var READ_BEFORE_EDIT_MESSAGE = "File has not been read yet. Read it first before writing to it.";
472
+ 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.";
473
+ function getReadStateHandle(ctx) {
474
+ const bag = ctx.framework;
475
+ if (!bag || typeof bag !== "object") return void 0;
476
+ const candidate = bag[READ_STATE_HANDLE_KEY];
477
+ if (!candidate || typeof candidate !== "object") return void 0;
478
+ const handle = candidate;
479
+ if (typeof handle.get !== "function" || typeof handle.set !== "function" || typeof handle.has !== "function") {
480
+ return void 0;
481
+ }
482
+ return handle;
483
+ }
484
+ async function recordReadState(ctx, absPath, handle) {
485
+ if (!handle) return;
486
+ try {
487
+ const info = await ctx.fs.stat(absPath);
488
+ const record = {
489
+ mtimeMs: Math.floor(info.modifiedMs),
490
+ size: info.size,
491
+ readAt: Date.now()
492
+ };
493
+ handle.set(absPath, record);
494
+ } catch {
495
+ }
496
+ }
497
+ async function enforceReadGate(ctx, absPath, handle) {
498
+ if (!handle) return { ok: true };
499
+ if (!handle.has(absPath)) {
500
+ return { ok: false, message: READ_BEFORE_EDIT_MESSAGE };
501
+ }
502
+ const recorded = handle.get(absPath);
503
+ if (!recorded) {
504
+ return { ok: false, message: READ_BEFORE_EDIT_MESSAGE };
505
+ }
506
+ let info;
507
+ try {
508
+ info = await ctx.fs.stat(absPath);
509
+ } catch {
510
+ return { ok: true };
511
+ }
512
+ const currentMtime = Math.floor(info.modifiedMs);
513
+ if (currentMtime > recorded.mtimeMs || info.size !== recorded.size) {
514
+ return { ok: false, message: MODIFIED_SINCE_READ_MESSAGE };
515
+ }
516
+ return { ok: true };
517
+ }
518
+
468
519
  // src/capabilities/files/read.ts
469
520
  var GUTTER_WIDTH = 6;
470
521
  var DESCRIPTION = [
@@ -567,6 +618,7 @@ var readTool = defineTool({
567
618
  const detail = err instanceof Error ? err.message : String(err);
568
619
  return failure(`Could not read ${path}: ${detail}`);
569
620
  }
621
+ await recordReadState(ctx, path, getReadStateHandle(ctx));
570
622
  const allLines = toLines(text);
571
623
  const totalLines = allLines.length;
572
624
  if (totalLines === 0) {
@@ -674,11 +726,19 @@ var writeTool = defineTool({
674
726
  async run(input, ctx) {
675
727
  const path = readPath(input?.path);
676
728
  const content = readContent(input?.content);
729
+ const handle = getReadStateHandle(ctx);
730
+ if (handle && await ctx.fs.exists(path)) {
731
+ const gate = await enforceReadGate(ctx, path, handle);
732
+ if (!gate.ok) {
733
+ return asText(gate.message, true);
734
+ }
735
+ }
677
736
  const folder = parentDir(path);
678
737
  if (folder.length > 0) {
679
738
  await ctx.fs.mkdir(folder, { recursive: true });
680
739
  }
681
740
  await ctx.fs.writeFile(path, content, "utf8");
741
+ await recordReadState(ctx, path, handle);
682
742
  const bytes = Buffer.byteLength(content, "utf8");
683
743
  const unit = bytes === 1 ? "byte" : "bytes";
684
744
  return asText(`Saved ${bytes} ${unit} to ${path}.`);
@@ -968,6 +1028,11 @@ async function runEdit(input, ctx) {
968
1028
  if (!info.isFile) {
969
1029
  return failure2(`${path} is not a regular file, so it cannot be edited.`);
970
1030
  }
1031
+ const handle = getReadStateHandle(ctx);
1032
+ const gate = await enforceReadGate(ctx, path, handle);
1033
+ if (!gate.ok) {
1034
+ return failure2(gate.message);
1035
+ }
971
1036
  const before = await ctx.fs.readFile(path, "utf8");
972
1037
  const literalHits = countLiteral(before, oldText);
973
1038
  if (literalHits > 0) {
@@ -981,6 +1046,7 @@ async function runEdit(input, ctx) {
981
1046
  return failure2(`The replacement left ${path} unchanged.`);
982
1047
  }
983
1048
  await ctx.fs.writeFile(path, after2, "utf8");
1049
+ await recordReadState(ctx, path, handle);
984
1050
  return success(path, before, after2, replaceAll ? literalHits : 1);
985
1051
  }
986
1052
  const spans = findFuzzySpans(before, oldText);
@@ -1004,6 +1070,7 @@ async function runEdit(input, ctx) {
1004
1070
  return failure2(`The fuzzy replacement left ${path} unchanged.`);
1005
1071
  }
1006
1072
  await ctx.fs.writeFile(path, after, "utf8");
1073
+ await recordReadState(ctx, path, handle);
1007
1074
  return success(path, before, after, targets.length);
1008
1075
  }
1009
1076
  var editTool = defineTool({