@theokit/sdk-tools 0.26.1 → 0.26.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,45 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.26.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 8790f70: Refuse a `workspace:` range before it can reach npm.
8
+
9
+ Five of this repo's twelve publishable packages declare internal dependencies as `workspace:^`, which
10
+ is correct on disk and becomes an unrecoverable defect if the publish goes out through a tool that
11
+ does not rewrite it: `pnpm` resolves the protocol while packing, `npm` ships the manifest verbatim.
12
+ A version published that way fails to install for everyone and cannot be corrected — only
13
+ deprecated.
14
+
15
+ Every publishable package now runs the guard in `prepublishOnly`, so it fires whichever way the
16
+ publish is invoked, and `pnpm release` runs it once across the repo before `changeset publish`.
17
+
18
+ Note for anyone reading a published manifest: the `prepublishOnly` entry points at a path inside
19
+ this repository. It never runs for a consumer — the hook only fires when the package itself is
20
+ published — and guarding the entry point that a hand-run `npm publish` actually uses was worth the
21
+ cosmetic wart of shipping the line.
22
+
23
+ ## 0.26.2
24
+
25
+ ### Patch Changes
26
+
27
+ - a713fc7: `interactive_shell` no longer flattens a session-cap error into
28
+ `interactive_unavailable`.
29
+
30
+ `toErrorJson` matched `InteractiveUnavailableError` first, so `MaxSessionsError` — which extends it —
31
+ took that branch and lost `max` and `liveSessionIds`. Those are the only actionable fields in the
32
+ error: without them the model cannot tell a missing backend from a session cap it could clear by
33
+ reusing one of the open sessions, which is exactly what `@theokit/sdk-pty`'s docblock says those
34
+ fields exist for.
35
+
36
+ The check is structural rather than `instanceof`, because the class lives in `@theokit/sdk-pty` and
37
+ this package does not depend on it — and the tool takes an injected backend, so any provider
38
+ reporting the same two fields gets the same treatment.
39
+
40
+ Measured from a consumer that had forked this tool's entire schema and handler to recover the fields,
41
+ since there is no error seam to override.
42
+
3
43
  ## 0.20.1
4
44
 
5
45
  ### Patch Changes
package/README.md CHANGED
@@ -4,7 +4,7 @@ Built-in tools for `@theokit/sdk` agents. File system, git, subprocess, search-t
4
4
 
5
5
  Extracted from `@theokit/sdk@1.7.0` as part of the SDK 2.0 package split.
6
6
 
7
- See the [**Theo Harness Capability Map**](../../docs/harness-capability-map.md) for every tool + guard primitive (`buildRepoMap`, `isBlockedIp`, `screenedFetch`, `catastrophicShellReason`, ...) with import paths and examples.
7
+ See the [**Theo Harness Capability Map**](../../wiki/reference/harness-capability-map.md) for every tool + guard primitive (`buildRepoMap`, `isBlockedIp`, `screenedFetch`, `catastrophicShellReason`, ...) with import paths and examples.
8
8
 
9
9
  ## Install
10
10
 
package/dist/index.cjs CHANGED
@@ -793,12 +793,12 @@ function checkPathScope(path, projectRoot) {
793
793
  throw err;
794
794
  }
795
795
  }
796
- var SEGMENTOS_SENSIVEIS = /* @__PURE__ */ new Set([".env", ".git", "node_modules", ".theo"]);
797
- function ehProibidoEmQualquerProfundidade(path) {
796
+ var SENSITIVE_SEGMENTS = /* @__PURE__ */ new Set([".env", ".git", "node_modules", ".theo"]);
797
+ function isForbiddenAtAnyDepth(path) {
798
798
  const segs = path.replace(/\\/g, "/").split("/").filter(Boolean);
799
799
  return segs.some((s) => {
800
800
  if (s === ".env.example") return false;
801
- return SEGMENTOS_SENSIVEIS.has(s) || /^\.env\./.test(s);
801
+ return SENSITIVE_SEGMENTS.has(s) || /^\.env\./.test(s);
802
802
  });
803
803
  }
804
804
 
@@ -866,7 +866,7 @@ function createGitStatusTool(opts) {
866
866
  }
867
867
  const scopeCheck = checkPathScope(path$1, projectRoot);
868
868
  if (scopeCheck !== null) return scopeCheck;
869
- const args = montarArgs(path$1, opts.includeBranch !== false);
869
+ const args = buildArgs(path$1, opts.includeBranch !== false);
870
870
  if (opts.sandbox !== void 0) {
871
871
  return statusViaSandbox(opts.sandbox, ctx, args, timeoutMs);
872
872
  }
@@ -875,9 +875,9 @@ function createGitStatusTool(opts) {
875
875
  }
876
876
  });
877
877
  }
878
- function montarArgs(path, comBranch) {
878
+ function buildArgs(path, withBranch) {
879
879
  const args = ["status", "--porcelain=v1"];
880
- if (comBranch) args.push("-b");
880
+ if (withBranch) args.push("-b");
881
881
  if (path !== void 0 && path !== "") args.push("--", path);
882
882
  return args;
883
883
  }
@@ -1004,7 +1004,26 @@ function globToRegex(pattern) {
1004
1004
  }
1005
1005
  return new RegExp(`^${regexStr}$`);
1006
1006
  }
1007
+ function capFields(err) {
1008
+ const e = err;
1009
+ if (typeof e.max !== "number") return void 0;
1010
+ if (!Array.isArray(e.liveSessionIds)) return void 0;
1011
+ if (!e.liveSessionIds.every((id) => typeof id === "string")) return void 0;
1012
+ return { max: e.max, liveSessionIds: e.liveSessionIds };
1013
+ }
1007
1014
  function toErrorJson(err) {
1015
+ if (typeof err === "object" && err !== null) {
1016
+ const cap = capFields(err);
1017
+ if (cap !== void 0) {
1018
+ return JSON.stringify({
1019
+ ok: false,
1020
+ error: "interactive_session_limit",
1021
+ max: cap.max,
1022
+ live_session_ids: [...cap.liveSessionIds],
1023
+ message: err.message
1024
+ });
1025
+ }
1026
+ }
1008
1027
  if (err instanceof interactive.InteractiveUnavailableError) {
1009
1028
  return JSON.stringify({ ok: false, error: "interactive_unavailable" });
1010
1029
  }
@@ -1567,10 +1586,10 @@ function createListDirTool(opts) {
1567
1586
  }),
1568
1587
  handler: async ({ path }, ctx) => {
1569
1588
  const relative3 = path === "" || path === "." ? "." : path;
1570
- const veredito = decidirEscopo(relative3, path, opts.allowAbsolute === true);
1571
- if (veredito.erro !== void 0) return veredito.erro;
1572
- if (veredito.raizAbsoluta !== void 0) {
1573
- return listViaLocalFs(veredito.raizAbsoluta, ".", path, max);
1589
+ const verdict = decideScope(relative3, path, opts.allowAbsolute === true);
1590
+ if (verdict.error !== void 0) return verdict.error;
1591
+ if (verdict.absoluteRoot !== void 0) {
1592
+ return listViaLocalFs(verdict.absoluteRoot, ".", path, max);
1574
1593
  }
1575
1594
  if (filesystem$1) {
1576
1595
  const backend = await filesystem.resolveFilesystem(filesystem$1, ctx ?? {});
@@ -1580,15 +1599,15 @@ function createListDirTool(opts) {
1580
1599
  }
1581
1600
  });
1582
1601
  }
1583
- function decidirEscopo(relative3, original, allowAbsolute) {
1584
- const recusa = (error) => ({
1585
- erro: JSON.stringify({ ok: false, error, path: original })
1602
+ function decideScope(relative3, original, allowAbsolute) {
1603
+ const refuse = (error) => ({
1604
+ error: JSON.stringify({ ok: false, error, path: original })
1586
1605
  });
1587
- if (relative3 !== "." && pathSafety.isForbiddenPath(relative3)) return recusa("forbidden_path");
1606
+ if (relative3 !== "." && pathSafety.isForbiddenPath(relative3)) return refuse("forbidden_path");
1588
1607
  if (!path.isAbsolute(relative3)) return {};
1589
- if (!allowAbsolute) return recusa("path_traversal");
1590
- if (ehProibidoEmQualquerProfundidade(relative3)) return recusa("forbidden_path");
1591
- return { raizAbsoluta: relative3 };
1608
+ if (!allowAbsolute) return refuse("path_traversal");
1609
+ if (isForbiddenAtAnyDepth(relative3)) return refuse("forbidden_path");
1610
+ return { absoluteRoot: relative3 };
1592
1611
  }
1593
1612
  async function listViaLocalFs(projectRoot, relative3, originalPath, max) {
1594
1613
  const boundary = resolveDirBoundary(relative3, projectRoot, originalPath);
@@ -1750,10 +1769,10 @@ function createPlanModeTool(options) {
1750
1769
  }
1751
1770
 
1752
1771
  // src/question.ts
1753
- function askerDoContexto(context) {
1772
+ function askerFromContext(context) {
1754
1773
  if (typeof context !== "object" || context === null) return void 0;
1755
- const candidato = context.askUser;
1756
- return typeof candidato === "function" ? candidato : void 0;
1774
+ const candidate = context.askUser;
1775
+ return typeof candidate === "function" ? candidate : void 0;
1757
1776
  }
1758
1777
  function createQuestionTool(opts) {
1759
1778
  const timeoutMs = opts.timeoutMs ?? 3e5;
@@ -1768,7 +1787,7 @@ function createQuestionTool(opts) {
1768
1787
  required: ["question"]
1769
1788
  },
1770
1789
  handler: async (input, ctx) => {
1771
- const askUser = askerDoContexto(ctx?.context) ?? opts.askUser;
1790
+ const askUser = askerFromContext(ctx?.context) ?? opts.askUser;
1772
1791
  if (askUser === void 0) {
1773
1792
  return JSON.stringify({
1774
1793
  ok: false,
@@ -1805,7 +1824,7 @@ function forbiddenReadError(path$1, allowAbsolute) {
1805
1824
  if (pathSafety.isForbiddenPath(path$1)) {
1806
1825
  return JSON.stringify({ ok: false, error: "forbidden_path", path: path$1 });
1807
1826
  }
1808
- if (allowAbsolute && path.isAbsolute(path$1) && ehProibidoEmQualquerProfundidade(path$1)) {
1827
+ if (allowAbsolute && path.isAbsolute(path$1) && isForbiddenAtAnyDepth(path$1)) {
1809
1828
  return JSON.stringify({ ok: false, error: "forbidden_path", path: path$1 });
1810
1829
  }
1811
1830
  return null;