indusagi-coding-agent 0.2.13 → 0.2.14

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 (56) hide show
  1. package/README.md +19 -0
  2. package/dist/entry.js +1762 -958
  3. package/dist/guardrails.js +145 -35
  4. package/dist/index.js +1810 -1006
  5. package/dist/review-agent.js +344 -60
  6. package/dist/review-server.js +346 -62
  7. package/package.json +12 -3
  8. package/src/boot/auth-vault.ts +4 -5
  9. package/src/boot/runners/addon-wiring.ts +111 -3
  10. package/src/boot/runners/benchmark-integrity.ts +107 -0
  11. package/src/boot/runners/repl-runner.ts +2 -1
  12. package/src/boot/runners/session.ts +18 -8
  13. package/src/boot/server-token.ts +20 -11
  14. package/src/briefing/context-docs.ts +144 -12
  15. package/src/channels/oneshot.ts +6 -0
  16. package/src/conductor/conductor.ts +67 -12
  17. package/src/conductor/contract.ts +4 -3
  18. package/src/conductor/transcript-store/store.ts +4 -8
  19. package/src/console/auth-status.ts +9 -4
  20. package/src/console/components/AgentsView.ts +2 -2
  21. package/src/console/components/BackgroundAgents.ts +15 -2
  22. package/src/console/components/Banner.ts +20 -9
  23. package/src/console/components/Composer.ts +57 -82
  24. package/src/console/components/ScrollViewport.ts +46 -0
  25. package/src/console/components/TerminalConsole.ts +165 -162
  26. package/src/console/components/WorkingIndicator.ts +6 -5
  27. package/src/console/components/banner-sweep.ts +5 -1
  28. package/src/console/components/welcome.ts +3 -3
  29. package/src/console/contract.ts +10 -5
  30. package/src/console/index.ts +7 -7
  31. package/src/console/input/complete.ts +2 -2
  32. package/src/console/input/dir-reader.ts +3 -1
  33. package/src/console/input/graphemes.ts +22 -0
  34. package/src/console/input/keymap.ts +6 -4
  35. package/src/console/mount.ts +45 -3
  36. package/src/console/overlays/auth.ts +28 -8
  37. package/src/console/overlays/boards.ts +13 -40
  38. package/src/console/overlays/host.ts +10 -0
  39. package/src/console/overlays/pickers.ts +35 -30
  40. package/src/console/reducer.ts +12 -5
  41. package/src/console/slash/builtins.ts +4 -3
  42. package/src/console/slash/commands/integrations.ts +13 -14
  43. package/src/console/slash/commands/transcript.ts +3 -8
  44. package/src/console/slash/commands/workbench.ts +2 -2
  45. package/src/console/test-support.ts +48 -0
  46. package/src/integrations/sarvam/config.ts +15 -13
  47. package/src/kit/external-editor.ts +17 -1
  48. package/src/kit/run-shell.ts +31 -0
  49. package/src/launch/invocation/flags.ts +12 -0
  50. package/src/launch/login.ts +23 -10
  51. package/src/launch/oauth.ts +3 -0
  52. package/src/models.ts +78 -2
  53. package/src/security/private-state.ts +122 -0
  54. package/src/window-budget/summarize/condense.ts +4 -1
  55. package/src/workspace/brand.ts +1 -1
  56. package/src/workspace/locator.ts +2 -2
@@ -812,49 +812,52 @@ function composeBriefing(input) {
812
812
  }
813
813
 
814
814
  // src/briefing/context-docs.ts
815
- import { existsSync, readFileSync, realpathSync } from "node:fs";
816
- import { dirname, extname, isAbsolute, join, parse, resolve } from "node:path";
815
+ import { closeSync, constants, fstatSync, lstatSync, openSync, readFileSync, realpathSync } from "node:fs";
816
+ import { basename, dirname, extname, isAbsolute, join, parse, resolve, sep } from "node:path";
817
817
  var DEFAULT_MAX_IMPORT_DEPTH = 5;
818
818
  var DEFAULT_MAX_BYTES_PER_DOC = 4e4;
819
819
  var CANDIDATE_FILES = ["AGENTS.md", "CLAUDE.md", "INDUSAGI.md", "CLAUDE.local.md", "AGENTS.local.md", join(".indusagi", "CLAUDE.md"), join(".claude", "CLAUDE.md")];
820
- var TEXT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".markdown", ".txt", ".text", ".json", ".jsonc", ".yaml", ".yml", ".toml", ".xml", ".csv", ".html", ".htm", ".css", ".js", ".ts", ".tsx", ".jsx", ".mjs", ".cjs", ".mts", ".cts", ".py", ".rb", ".go", ".rs", ".sh", ".env"]);
820
+ var TEXT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".markdown", ".txt", ".text", ".json", ".jsonc", ".yaml", ".yml", ".toml", ".xml", ".csv", ".html", ".htm", ".css", ".js", ".ts", ".tsx", ".jsx", ".mjs", ".cjs", ".mts", ".cts", ".py", ".rb", ".go", ".rs", ".sh"]);
821
821
  var IMPORT_REGEX = /(?:^|\s)@((?:[^\s\\]|\\ )+)/g;
822
+ var NO_FOLLOW = constants.O_NOFOLLOW ?? 0;
823
+ var SECRET_BASENAMES = /* @__PURE__ */ new Set([
824
+ ".env",
825
+ "credentials",
826
+ "credentials.json",
827
+ "id_rsa",
828
+ "id_dsa",
829
+ "id_ecdsa",
830
+ "id_ed25519"
831
+ ]);
832
+ var SECRET_EXTENSIONS = /* @__PURE__ */ new Set([".key", ".pem", ".p12", ".pfx"]);
822
833
  function shortenPath(path, cwd, home) {
823
- if (cwd && path.startsWith(cwd)) {
834
+ if (cwd && (path === cwd || path.startsWith(`${cwd}${sep}`))) {
824
835
  const rest = path.slice(cwd.length).replace(/^[/\\]+/, "");
825
836
  return rest ? `./${rest}` : ".";
826
837
  }
827
- return home && path.startsWith(home) ? `~${path.slice(home.length)}` : path;
828
- }
829
- function dirChain(cwd) {
830
- const chain = [];
831
- let current = cwd;
832
- for (let i = 0; i < 256; i++) {
833
- chain.push(current);
834
- const parent = dirname(current);
835
- if (parent === current || current === parse(current).root) break;
836
- current = parent;
837
- }
838
- return chain.reverse();
839
- }
840
- function safeRealpath(path) {
841
- try {
842
- return realpathSync(path);
843
- } catch {
844
- return path;
845
- }
838
+ return home && (path === home || path.startsWith(`${home}${sep}`)) ? `~${path.slice(home.length)}` : path;
846
839
  }
847
840
  function isTextFile(path) {
848
841
  const extension = extname(path).toLowerCase();
849
842
  return !extension || TEXT_FILE_EXTENSIONS.has(extension);
850
843
  }
844
+ function isLikelySecretFile(path) {
845
+ const name = basename(path).toLowerCase();
846
+ return SECRET_BASENAMES.has(name) || name.startsWith(".env.") || SECRET_EXTENSIONS.has(extname(name));
847
+ }
851
848
  function readFileBody(path) {
852
- if (!existsSync(path)) return null;
849
+ let fd;
853
850
  try {
854
- const body = readFileSync(path, "utf8").trim();
851
+ const before = lstatSync(path);
852
+ if (before.isSymbolicLink() || !before.isFile()) return null;
853
+ fd = openSync(path, constants.O_RDONLY | NO_FOLLOW);
854
+ if (!fstatSync(fd).isFile()) return null;
855
+ const body = readFileSync(fd, "utf8").trim();
855
856
  return body || null;
856
857
  } catch {
857
858
  return null;
859
+ } finally {
860
+ if (fd !== void 0) closeSync(fd);
858
861
  }
859
862
  }
860
863
  function extractImports(body, baseDir, home) {
@@ -873,23 +876,46 @@ function extractImports(body, baseDir, home) {
873
876
  }
874
877
  return out;
875
878
  }
876
- function loadDocTree(path, cwd, home, seen, depth, maxDepth, maxBytes, out) {
877
- const real = safeRealpath(path);
878
- if (seen.has(real)) return;
879
+ function canonicalExistingFile(path) {
880
+ try {
881
+ const before = lstatSync(path);
882
+ if (before.isSymbolicLink() || !before.isFile()) return null;
883
+ return realpathSync(path);
884
+ } catch {
885
+ return null;
886
+ }
887
+ }
888
+ function isWithin(root, candidate) {
889
+ return candidate === root || candidate.startsWith(`${root}${sep}`);
890
+ }
891
+ function loadTrustedDocTree(path, cwd, home, seen, depth, maxDepth, maxBytes, out, policy) {
892
+ if (isLikelySecretFile(path)) return;
893
+ const real = canonicalExistingFile(path);
894
+ if (real === null || seen.has(real) || isLikelySecretFile(real)) return;
895
+ if (!isWithin(policy.root, real) && !policy.approvedExternal.has(real)) return;
879
896
  seen.add(real);
880
- const body = readFileBody(path);
897
+ const body = readFileBody(real);
881
898
  if (body === null) return;
882
- out.push({ path: shortenPath(path, cwd, home), body: body.length > maxBytes ? body.slice(0, maxBytes) : body });
899
+ out.push({ path: shortenPath(real, cwd, home), body: body.length > maxBytes ? body.slice(0, maxBytes) : body });
883
900
  if (depth >= maxDepth) return;
884
- for (const child of extractImports(body, dirname(path), home)) loadDocTree(child, cwd, home, seen, depth + 1, maxDepth, maxBytes, out);
901
+ for (const child of extractImports(body, dirname(real), home)) {
902
+ loadTrustedDocTree(child, cwd, home, seen, depth + 1, maxDepth, maxBytes, out, policy);
903
+ }
885
904
  }
886
- function gatherContextDocs(cwd, home, opts) {
905
+ function gatherContextDocs(cwd, home, opts = {}) {
887
906
  const out = [];
888
907
  try {
908
+ const root = realpathSync(cwd);
909
+ const approvedExternal = /* @__PURE__ */ new Set();
910
+ for (const path of opts.approvedExternalPaths ?? []) {
911
+ const real = canonicalExistingFile(path);
912
+ if (real !== null && !isLikelySecretFile(real)) approvedExternal.add(real);
913
+ }
914
+ const policy = { root, approvedExternal };
889
915
  const seen = /* @__PURE__ */ new Set();
890
- const roots = dirChain(cwd);
891
- if (home) roots.push(home);
892
- for (const dir of roots) for (const name of CANDIDATE_FILES) loadDocTree(join(dir, name), cwd, home, seen, 0, opts?.maxImportDepth ?? DEFAULT_MAX_IMPORT_DEPTH, opts?.maxBytesPerDoc ?? DEFAULT_MAX_BYTES_PER_DOC, out);
916
+ for (const name of CANDIDATE_FILES) {
917
+ loadTrustedDocTree(join(root, name), root, home, seen, 0, opts.maxImportDepth ?? DEFAULT_MAX_IMPORT_DEPTH, opts.maxBytesPerDoc ?? DEFAULT_MAX_BYTES_PER_DOC, out, policy);
918
+ }
893
919
  } catch {
894
920
  return out;
895
921
  }
@@ -2025,7 +2051,74 @@ var GPT_6_ASTRA_BASE = {
2025
2051
  }
2026
2052
  }
2027
2053
  };
2054
+ var GPT_6_SOL_BASE = {
2055
+ id: "gpt-6-sol",
2056
+ name: "GPT-6 Sol",
2057
+ api: "openai-responses",
2058
+ reasoning: true,
2059
+ reasoningEfforts: ["high"],
2060
+ input: ["text", "image"],
2061
+ cost: {
2062
+ input: 10,
2063
+ cacheRead: 1,
2064
+ cacheWrite: 12.5,
2065
+ output: 50
2066
+ },
2067
+ contextWindow: 105e4,
2068
+ maxTokens: 128e3,
2069
+ longContextPricing: {
2070
+ threshold: 272e3,
2071
+ multipliers: {
2072
+ input: 2,
2073
+ cacheRead: 2,
2074
+ cacheWrite: 2,
2075
+ output: 1.5
2076
+ }
2077
+ }
2078
+ };
2079
+ var GPT_6_LUNA_BASE = {
2080
+ id: "gpt-6-luna",
2081
+ name: "GPT-6 Luna",
2082
+ api: "openai-responses",
2083
+ reasoning: true,
2084
+ reasoningEfforts: ["low", "medium", "high", "xhigh", "max"],
2085
+ input: ["text", "image"],
2086
+ cost: {
2087
+ input: 0.1,
2088
+ cacheRead: 0.01,
2089
+ cacheWrite: 0.125,
2090
+ output: 0.5
2091
+ },
2092
+ contextWindow: 105e4,
2093
+ maxTokens: 128e3,
2094
+ longContextPricing: {
2095
+ threshold: 272e3,
2096
+ multipliers: { input: 2, cacheRead: 2, cacheWrite: 2, output: 1.5 }
2097
+ }
2098
+ };
2028
2099
  var CODING_AGENT_MODELS = [
2100
+ {
2101
+ ...GPT_6_SOL_BASE,
2102
+ provider: "openai",
2103
+ baseUrl: "https://api.openai.com/v1"
2104
+ },
2105
+ {
2106
+ ...GPT_6_SOL_BASE,
2107
+ api: "openai-codex-responses",
2108
+ provider: "openai-codex",
2109
+ baseUrl: "https://chatgpt.com/backend-api"
2110
+ },
2111
+ {
2112
+ ...GPT_6_LUNA_BASE,
2113
+ provider: "openai",
2114
+ baseUrl: "https://api.openai.com/v1"
2115
+ },
2116
+ {
2117
+ ...GPT_6_LUNA_BASE,
2118
+ api: "openai-codex-responses",
2119
+ provider: "openai-codex",
2120
+ baseUrl: "https://chatgpt.com/backend-api"
2121
+ },
2029
2122
  {
2030
2123
  ...GPT_6_ASTRA_BASE,
2031
2124
  provider: "openai",
@@ -2081,6 +2174,23 @@ import { ulid } from "ulid";
2081
2174
  // src/conductor/transcript-store/serialize.ts
2082
2175
  import { buildSessionContext, convertToLlm, loadEntriesFromFile, parseSessionEntries } from "indusagi/agent";
2083
2176
 
2177
+ // src/security/private-state.ts
2178
+ import {
2179
+ chmodSync,
2180
+ closeSync as closeSync2,
2181
+ constants as constants2,
2182
+ fchmodSync,
2183
+ fstatSync as fstatSync2,
2184
+ lstatSync as lstatSync2,
2185
+ mkdirSync as mkdirSync2,
2186
+ openSync as openSync2,
2187
+ readFileSync as readFileSync2,
2188
+ renameSync,
2189
+ rmSync as rmSync2,
2190
+ writeFileSync as writeFileSync2
2191
+ } from "node:fs";
2192
+ var NO_FOLLOW2 = constants2.O_NOFOLLOW ?? 0;
2193
+
2084
2194
  // src/window-budget/summarize/prompt.ts
2085
2195
  var CONDENSER_BRIEF = [
2086
2196
  "You are a transcript recorder for a long-running coding session.",