botinabox 0.2.5 → 0.3.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/CHANGELOG.md CHANGED
@@ -6,6 +6,15 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versioning: [S
6
6
 
7
7
  ---
8
8
 
9
+ ## [0.3.0] — 2026-04-03
10
+
11
+ ### Added
12
+
13
+ - **Domain tables** — `defineDomainTables(db, options?)` creates standard multi-agent app tables: org, project, client, invoice, repository, file, channel, rule, event + junction tables. Configurable: disable clients, repos, files, channels, rules, or events.
14
+ - **Domain entity contexts** — `defineDomainEntityContexts(db, options?)` renders per-entity context directories for all domain tables. Projects get REPOS.md + RULES.md. Clients get REPOS.md + AGENTS.md + INVOICES.md.
15
+ - **Claude stream parser** — `parseClaudeStream(stdout)` parses Claude CLI NDJSON output into structured results (session, model, cost, tokens, text, errors). Plus `isMaxTurns()`, `isLoginRequired()`, `deactivateLocalImagePaths()`.
16
+ - **Process env builder** — `buildProcessEnv(allowedKeys?, inject?)` creates a clean subprocess environment with only safe variables. Strips all secrets.
17
+
9
18
  ## [0.2.0] — 2026-04-03
10
19
 
11
20
  ### Added
package/dist/index.js CHANGED
@@ -1673,6 +1673,501 @@ ${s.definition}` : null,
1673
1673
  });
1674
1674
  }
1675
1675
 
1676
+ // src/core/data/domain-schema.ts
1677
+ function defineDomainTables(db, options = {}) {
1678
+ const opts = {
1679
+ clients: true,
1680
+ repositories: true,
1681
+ files: true,
1682
+ channels: true,
1683
+ rules: true,
1684
+ events: true,
1685
+ ...options
1686
+ };
1687
+ db.define("org", {
1688
+ columns: {
1689
+ id: "TEXT PRIMARY KEY",
1690
+ name: "TEXT NOT NULL",
1691
+ type: "TEXT NOT NULL DEFAULT 'company'",
1692
+ description: "TEXT",
1693
+ mission: "TEXT",
1694
+ website: "TEXT",
1695
+ created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
1696
+ updated_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
1697
+ deleted_at: "TEXT"
1698
+ }
1699
+ });
1700
+ db.define("project", {
1701
+ columns: {
1702
+ id: "TEXT PRIMARY KEY",
1703
+ org_id: "TEXT NOT NULL",
1704
+ name: "TEXT NOT NULL",
1705
+ status: "TEXT",
1706
+ description: "TEXT",
1707
+ tech_stack: "TEXT",
1708
+ github_repo: "TEXT",
1709
+ deploy_target: "TEXT",
1710
+ production_url: "TEXT",
1711
+ branch_strategy: "TEXT",
1712
+ notes: "TEXT",
1713
+ created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
1714
+ updated_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
1715
+ deleted_at: "TEXT"
1716
+ }
1717
+ });
1718
+ db.define("agent_project", {
1719
+ columns: {
1720
+ agent_id: "TEXT NOT NULL",
1721
+ project_id: "TEXT NOT NULL",
1722
+ role: "TEXT",
1723
+ created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP"
1724
+ },
1725
+ primaryKey: ["agent_id", "project_id"]
1726
+ });
1727
+ db.define("secret_project", {
1728
+ columns: {
1729
+ secret_id: "TEXT NOT NULL",
1730
+ project_id: "TEXT NOT NULL",
1731
+ created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP"
1732
+ },
1733
+ primaryKey: ["secret_id", "project_id"]
1734
+ });
1735
+ db.define("secret_org", {
1736
+ columns: {
1737
+ secret_id: "TEXT NOT NULL",
1738
+ org_id: "TEXT NOT NULL",
1739
+ created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP"
1740
+ },
1741
+ primaryKey: ["secret_id", "org_id"]
1742
+ });
1743
+ if (opts.clients) {
1744
+ db.define("client", {
1745
+ columns: {
1746
+ id: "TEXT PRIMARY KEY",
1747
+ org_id: "TEXT NOT NULL",
1748
+ name: "TEXT NOT NULL",
1749
+ contact_name: "TEXT",
1750
+ contact_email: "TEXT",
1751
+ phone: "TEXT",
1752
+ status: "TEXT NOT NULL DEFAULT 'active'",
1753
+ notes: "TEXT",
1754
+ created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
1755
+ updated_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
1756
+ deleted_at: "TEXT"
1757
+ }
1758
+ });
1759
+ db.define("invoice", {
1760
+ columns: {
1761
+ id: "TEXT PRIMARY KEY",
1762
+ org_id: "TEXT NOT NULL",
1763
+ client_id: "TEXT NOT NULL",
1764
+ number: "TEXT",
1765
+ amount_cents: "INTEGER",
1766
+ currency: "TEXT DEFAULT 'USD'",
1767
+ status: "TEXT NOT NULL DEFAULT 'draft'",
1768
+ description: "TEXT",
1769
+ hours: "REAL",
1770
+ rate_cents: "INTEGER",
1771
+ items_json: "TEXT",
1772
+ issued_at: "TEXT",
1773
+ due_at: "TEXT",
1774
+ paid_at: "TEXT",
1775
+ notes: "TEXT",
1776
+ created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
1777
+ updated_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
1778
+ deleted_at: "TEXT"
1779
+ },
1780
+ tableConstraints: [
1781
+ "CREATE INDEX IF NOT EXISTS idx_invoice_client ON invoice(client_id)"
1782
+ ]
1783
+ });
1784
+ db.define("agent_client", {
1785
+ columns: {
1786
+ agent_id: "TEXT NOT NULL",
1787
+ client_id: "TEXT NOT NULL",
1788
+ created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP"
1789
+ },
1790
+ primaryKey: ["agent_id", "client_id"]
1791
+ });
1792
+ }
1793
+ if (opts.repositories) {
1794
+ db.define("repository", {
1795
+ columns: {
1796
+ id: "TEXT PRIMARY KEY",
1797
+ org_id: "TEXT NOT NULL",
1798
+ project_id: "TEXT",
1799
+ client_id: "TEXT",
1800
+ name: "TEXT NOT NULL",
1801
+ url: "TEXT",
1802
+ local_path: "TEXT",
1803
+ default_branch: "TEXT DEFAULT 'main'",
1804
+ platform: "TEXT DEFAULT 'github'",
1805
+ status: "TEXT NOT NULL DEFAULT 'active'",
1806
+ notes: "TEXT",
1807
+ created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
1808
+ updated_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
1809
+ deleted_at: "TEXT"
1810
+ },
1811
+ tableConstraints: [
1812
+ "CREATE INDEX IF NOT EXISTS idx_repository_project ON repository(project_id)",
1813
+ "CREATE INDEX IF NOT EXISTS idx_repository_client ON repository(client_id)"
1814
+ ]
1815
+ });
1816
+ }
1817
+ if (opts.files) {
1818
+ db.define("file", {
1819
+ columns: {
1820
+ id: "TEXT PRIMARY KEY",
1821
+ org_id: "TEXT",
1822
+ name: "TEXT NOT NULL",
1823
+ file_path: "TEXT",
1824
+ mime_type: "TEXT",
1825
+ size_bytes: "INTEGER",
1826
+ project_id: "TEXT",
1827
+ access_level: "TEXT NOT NULL DEFAULT 'org'",
1828
+ description: "TEXT",
1829
+ notes: "TEXT",
1830
+ created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
1831
+ updated_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
1832
+ deleted_at: "TEXT"
1833
+ }
1834
+ });
1835
+ }
1836
+ if (opts.channels) {
1837
+ db.define("channel", {
1838
+ columns: {
1839
+ id: "TEXT PRIMARY KEY",
1840
+ org_id: "TEXT",
1841
+ platform: "TEXT NOT NULL",
1842
+ external_id: "TEXT",
1843
+ name: "TEXT NOT NULL",
1844
+ type: "TEXT NOT NULL DEFAULT 'channel'",
1845
+ instructions: "TEXT",
1846
+ created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
1847
+ updated_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
1848
+ deleted_at: "TEXT"
1849
+ }
1850
+ });
1851
+ }
1852
+ if (opts.rules) {
1853
+ db.define("rule", {
1854
+ columns: {
1855
+ id: "TEXT PRIMARY KEY",
1856
+ org_id: "TEXT",
1857
+ title: "TEXT NOT NULL",
1858
+ rule_text: "TEXT NOT NULL",
1859
+ scope: "TEXT NOT NULL DEFAULT 'org'",
1860
+ category: "TEXT NOT NULL DEFAULT 'process'",
1861
+ priority: "INTEGER NOT NULL DEFAULT 50",
1862
+ created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
1863
+ updated_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
1864
+ deleted_at: "TEXT"
1865
+ }
1866
+ });
1867
+ db.define("rule_agent", {
1868
+ columns: {
1869
+ rule_id: "TEXT NOT NULL",
1870
+ agent_id: "TEXT NOT NULL",
1871
+ created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP"
1872
+ },
1873
+ primaryKey: ["rule_id", "agent_id"]
1874
+ });
1875
+ db.define("rule_project", {
1876
+ columns: {
1877
+ rule_id: "TEXT NOT NULL",
1878
+ project_id: "TEXT NOT NULL",
1879
+ created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP"
1880
+ },
1881
+ primaryKey: ["rule_id", "project_id"]
1882
+ });
1883
+ db.define("rule_org", {
1884
+ columns: {
1885
+ rule_id: "TEXT NOT NULL",
1886
+ org_id: "TEXT NOT NULL",
1887
+ created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP"
1888
+ },
1889
+ primaryKey: ["rule_id", "org_id"]
1890
+ });
1891
+ }
1892
+ if (opts.events) {
1893
+ db.define("event", {
1894
+ columns: {
1895
+ id: "TEXT PRIMARY KEY",
1896
+ org_id: "TEXT",
1897
+ type: "TEXT NOT NULL",
1898
+ summary: "TEXT NOT NULL",
1899
+ details: "TEXT",
1900
+ severity: "TEXT NOT NULL DEFAULT 'info'",
1901
+ actor_agent_id: "TEXT",
1902
+ actor_user_id: "TEXT",
1903
+ project_id: "TEXT",
1904
+ channel_id: "TEXT",
1905
+ created_at: "TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP",
1906
+ deleted_at: "TEXT"
1907
+ },
1908
+ tableConstraints: [
1909
+ "CREATE INDEX IF NOT EXISTS idx_event_type ON event(type, created_at)",
1910
+ "CREATE INDEX IF NOT EXISTS idx_event_project ON event(project_id)"
1911
+ ]
1912
+ });
1913
+ }
1914
+ }
1915
+
1916
+ // src/core/data/domain-entity-contexts.ts
1917
+ function defineDomainEntityContexts(db, options = {}) {
1918
+ const opts = {
1919
+ clients: true,
1920
+ repositories: true,
1921
+ files: true,
1922
+ channels: true,
1923
+ rules: true,
1924
+ ...options
1925
+ };
1926
+ db.defineEntityContext("org", {
1927
+ table: "org",
1928
+ directory: "orgs",
1929
+ slugColumn: "name",
1930
+ indexFile: "orgs/ORGS.md",
1931
+ files: {
1932
+ "ORG.md": {
1933
+ source: { type: "self" },
1934
+ render: (rows) => {
1935
+ const o = rows[0];
1936
+ if (!o) return "";
1937
+ return [
1938
+ `# ${o.name}`,
1939
+ "",
1940
+ o.type ? `**Type:** ${o.type}` : null,
1941
+ o.description ? `
1942
+ ${o.description}` : null,
1943
+ o.mission ? `
1944
+ **Mission:** ${o.mission}` : null,
1945
+ o.website ? `**Website:** ${o.website}` : null,
1946
+ ""
1947
+ ].filter(Boolean).join("\n");
1948
+ }
1949
+ }
1950
+ }
1951
+ });
1952
+ db.defineEntityContext("project", {
1953
+ table: "project",
1954
+ directory: "projects",
1955
+ slugColumn: "name",
1956
+ indexFile: "projects/PROJECTS.md",
1957
+ files: {
1958
+ "PROJECT.md": {
1959
+ source: { type: "self" },
1960
+ render: (rows) => {
1961
+ const p = rows[0];
1962
+ if (!p) return "";
1963
+ return [
1964
+ `# ${p.name}`,
1965
+ "",
1966
+ p.status ? `**Status:** ${p.status}` : null,
1967
+ p.description ? `
1968
+ ${p.description}` : null,
1969
+ p.tech_stack ? `
1970
+ **Tech Stack:** ${p.tech_stack}` : null,
1971
+ p.production_url ? `**URL:** ${p.production_url}` : null,
1972
+ p.github_repo ? `**GitHub:** ${p.github_repo}` : null,
1973
+ p.deploy_target ? `**Deploy:** ${p.deploy_target}` : null,
1974
+ p.branch_strategy ? `**Branch Strategy:** ${p.branch_strategy}` : null,
1975
+ p.notes ? `
1976
+ **Notes:**
1977
+ ${p.notes}` : null,
1978
+ ""
1979
+ ].filter(Boolean).join("\n");
1980
+ }
1981
+ },
1982
+ ...opts.repositories ? {
1983
+ "REPOS.md": {
1984
+ source: {
1985
+ type: "hasMany",
1986
+ table: "repository",
1987
+ foreignKey: "project_id"
1988
+ },
1989
+ render: (rows) => {
1990
+ if (!rows.length) return "";
1991
+ const lines = rows.map(
1992
+ (r) => `- **${r.name}** \u2014 ${r.url ?? ""}`
1993
+ );
1994
+ return `# Repositories
1995
+
1996
+ ${lines.join("\n")}
1997
+ `;
1998
+ },
1999
+ omitIfEmpty: true
2000
+ }
2001
+ } : {},
2002
+ ...opts.rules ? {
2003
+ "RULES.md": {
2004
+ source: {
2005
+ type: "manyToMany",
2006
+ junctionTable: "rule_project",
2007
+ localKey: "project_id",
2008
+ remoteKey: "rule_id",
2009
+ remoteTable: "rule",
2010
+ softDelete: true,
2011
+ orderBy: "priority"
2012
+ },
2013
+ render: (rows) => {
2014
+ if (!rows.length) return "";
2015
+ const lines = rows.map(
2016
+ (r) => `### ${r.title}
2017
+ ${r.rule_text}`
2018
+ );
2019
+ return `# Project Rules
2020
+
2021
+ ${lines.join("\n\n")}
2022
+ `;
2023
+ },
2024
+ omitIfEmpty: true
2025
+ }
2026
+ } : {}
2027
+ }
2028
+ });
2029
+ if (opts.clients) {
2030
+ db.defineEntityContext("client", {
2031
+ table: "client",
2032
+ directory: "clients",
2033
+ slugColumn: "name",
2034
+ indexFile: "clients/CLIENTS.md",
2035
+ files: {
2036
+ "CLIENT.md": {
2037
+ source: { type: "self" },
2038
+ render: (rows) => {
2039
+ const c = rows[0];
2040
+ if (!c) return "";
2041
+ return [
2042
+ `# ${c.name}`,
2043
+ "",
2044
+ c.contact_name ? `**Contact:** ${c.contact_name}` : null,
2045
+ c.contact_email ? `**Email:** ${c.contact_email}` : null,
2046
+ c.phone ? `**Phone:** ${c.phone}` : null,
2047
+ c.status ? `**Status:** ${c.status}` : null,
2048
+ c.notes ? `
2049
+ ${c.notes}` : null,
2050
+ ""
2051
+ ].filter(Boolean).join("\n");
2052
+ }
2053
+ },
2054
+ ...opts.repositories ? {
2055
+ "REPOS.md": {
2056
+ source: {
2057
+ type: "hasMany",
2058
+ table: "repository",
2059
+ foreignKey: "client_id"
2060
+ },
2061
+ render: (rows) => {
2062
+ if (!rows.length) return "";
2063
+ const lines = rows.map(
2064
+ (r) => `- **${r.name}** \u2014 ${r.url ?? ""}`
2065
+ );
2066
+ return `# Repositories
2067
+
2068
+ ${lines.join("\n")}
2069
+ `;
2070
+ },
2071
+ omitIfEmpty: true
2072
+ }
2073
+ } : {},
2074
+ AGENTS: {
2075
+ source: {
2076
+ type: "manyToMany",
2077
+ junctionTable: "agent_client",
2078
+ localKey: "client_id",
2079
+ remoteKey: "agent_id",
2080
+ remoteTable: "agents"
2081
+ },
2082
+ render: (rows) => {
2083
+ if (!rows.length) return "";
2084
+ const lines = rows.map(
2085
+ (r) => `- **${r.name}** (${r.role ?? "agent"})`
2086
+ );
2087
+ return `# Assigned Agents
2088
+
2089
+ ${lines.join("\n")}
2090
+ `;
2091
+ },
2092
+ omitIfEmpty: true
2093
+ },
2094
+ "INVOICES.md": {
2095
+ source: {
2096
+ type: "hasMany",
2097
+ table: "invoice",
2098
+ foreignKey: "client_id"
2099
+ },
2100
+ render: (rows) => {
2101
+ if (!rows.length) return "";
2102
+ const lines = rows.map((r) => {
2103
+ const amt = r.amount_cents ? `$${(r.amount_cents / 100).toFixed(2)}` : "TBD";
2104
+ return `- **${r.number ?? "Draft"}** \u2014 ${amt} (${r.status})${r.description ? ": " + r.description : ""}`;
2105
+ });
2106
+ return `# Invoices
2107
+
2108
+ ${lines.join("\n")}
2109
+ `;
2110
+ },
2111
+ omitIfEmpty: true
2112
+ }
2113
+ }
2114
+ });
2115
+ }
2116
+ if (opts.files) {
2117
+ db.defineEntityContext("file", {
2118
+ table: "file",
2119
+ directory: "files",
2120
+ slugColumn: "name",
2121
+ indexFile: "files/FILES.md",
2122
+ files: {
2123
+ "FILE.md": {
2124
+ source: { type: "self" },
2125
+ render: (rows) => {
2126
+ const f = rows[0];
2127
+ if (!f) return "";
2128
+ return [
2129
+ `# ${f.name}`,
2130
+ "",
2131
+ f.mime_type ? `**Type:** ${f.mime_type}` : null,
2132
+ f.access_level ? `**Access:** ${f.access_level}` : null,
2133
+ f.file_path ? `**Path:** ${f.file_path}` : null,
2134
+ f.description ? `
2135
+ ${f.description}` : null,
2136
+ ""
2137
+ ].filter(Boolean).join("\n");
2138
+ }
2139
+ }
2140
+ }
2141
+ });
2142
+ }
2143
+ if (opts.channels) {
2144
+ db.defineEntityContext("channel", {
2145
+ table: "channel",
2146
+ directory: "channels",
2147
+ slugColumn: "name",
2148
+ indexFile: "channels/CHANNELS.md",
2149
+ files: {
2150
+ "CHANNEL.md": {
2151
+ source: { type: "self" },
2152
+ render: (rows) => {
2153
+ const c = rows[0];
2154
+ if (!c) return "";
2155
+ return [
2156
+ `# ${c.name}`,
2157
+ "",
2158
+ c.platform ? `**Platform:** ${c.platform}` : null,
2159
+ c.type ? `**Type:** ${c.type}` : null,
2160
+ c.instructions ? `
2161
+ ${c.instructions}` : null,
2162
+ ""
2163
+ ].filter(Boolean).join("\n");
2164
+ }
2165
+ }
2166
+ }
2167
+ });
2168
+ }
2169
+ }
2170
+
1676
2171
  // src/core/security/sanitizer.ts
1677
2172
  import { Buffer as Buffer2 } from "buffer";
1678
2173
  var DEFAULT_FIELD_LIMIT = 65535;
@@ -1756,6 +2251,44 @@ var AuditEmitter = class {
1756
2251
  }
1757
2252
  };
1758
2253
 
2254
+ // src/core/security/process-env.ts
2255
+ var DEFAULT_ALLOWED_KEYS = /* @__PURE__ */ new Set([
2256
+ "PATH",
2257
+ "HOME",
2258
+ "USER",
2259
+ "SHELL",
2260
+ "LANG",
2261
+ "TERM",
2262
+ "TMPDIR",
2263
+ "XDG_RUNTIME_DIR",
2264
+ "NODE_ENV",
2265
+ // Git
2266
+ "GIT_AUTHOR_NAME",
2267
+ "GIT_AUTHOR_EMAIL",
2268
+ "GIT_COMMITTER_NAME",
2269
+ "GIT_COMMITTER_EMAIL",
2270
+ // Homebrew / system
2271
+ "HOMEBREW_PREFIX",
2272
+ "HOMEBREW_CELLAR",
2273
+ "HOMEBREW_REPOSITORY"
2274
+ ]);
2275
+ function buildProcessEnv(allowedKeys, inject) {
2276
+ const allowed = new Set(DEFAULT_ALLOWED_KEYS);
2277
+ if (allowedKeys) {
2278
+ for (const k of allowedKeys) allowed.add(k);
2279
+ }
2280
+ const env = {};
2281
+ for (const [key, value] of Object.entries(process.env)) {
2282
+ if (allowed.has(key) && value !== void 0) {
2283
+ env[key] = value;
2284
+ }
2285
+ }
2286
+ if (inject) {
2287
+ Object.assign(env, inject);
2288
+ }
2289
+ return env;
2290
+ }
2291
+
1759
2292
  // src/core/update/version-utils.ts
1760
2293
  function parseVersion(v) {
1761
2294
  const cleaned = v.replace(/^v/, "").split("-")[0] ?? v;
@@ -3233,6 +3766,92 @@ var SecretStore = class {
3233
3766
  };
3234
3767
  }
3235
3768
  };
3769
+
3770
+ // src/core/orchestrator/claude-stream-parser.ts
3771
+ function parseClaudeStream(stdout) {
3772
+ let sessionId = null;
3773
+ let model = null;
3774
+ let costUsd = null;
3775
+ let usage = null;
3776
+ let isError = false;
3777
+ let errorMessage = null;
3778
+ let stopReason = null;
3779
+ const textBlocks = [];
3780
+ for (const line of stdout.split("\n")) {
3781
+ if (!line.trim()) continue;
3782
+ let event;
3783
+ try {
3784
+ event = JSON.parse(line);
3785
+ } catch {
3786
+ continue;
3787
+ }
3788
+ const type = event.type;
3789
+ if (type === "system" && event.subtype === "init") {
3790
+ sessionId = event.session_id ?? null;
3791
+ model = event.model ?? null;
3792
+ }
3793
+ if (type === "assistant") {
3794
+ const msg = event.message;
3795
+ const content = msg?.content ?? event.content;
3796
+ if (Array.isArray(content)) {
3797
+ for (const block of content) {
3798
+ if (block.type === "text" && block.text) {
3799
+ textBlocks.push(block.text);
3800
+ }
3801
+ }
3802
+ }
3803
+ }
3804
+ if (type === "result") {
3805
+ isError = !!event.is_error;
3806
+ stopReason = event.stop_reason ?? null;
3807
+ costUsd = typeof event.total_cost_usd === "number" ? event.total_cost_usd : null;
3808
+ const u = event.usage;
3809
+ if (u) {
3810
+ usage = {
3811
+ inputTokens: u.input_tokens ?? 0,
3812
+ cachedInputTokens: u.cache_read_input_tokens ?? 0,
3813
+ outputTokens: u.output_tokens ?? 0
3814
+ };
3815
+ }
3816
+ if (isError) {
3817
+ errorMessage = event.error ?? event.result ?? "Unknown error";
3818
+ }
3819
+ const resultContent = event.result;
3820
+ if (typeof resultContent === "string" && resultContent) {
3821
+ textBlocks.push(resultContent);
3822
+ }
3823
+ }
3824
+ }
3825
+ return {
3826
+ sessionId,
3827
+ model,
3828
+ costUsd,
3829
+ usage,
3830
+ summary: textBlocks.join("\n"),
3831
+ isError,
3832
+ errorMessage,
3833
+ stopReason
3834
+ };
3835
+ }
3836
+ function isMaxTurns(parsed) {
3837
+ return parsed.stopReason === "max_turns" || parsed.stopReason === "tool_use";
3838
+ }
3839
+ function isLoginRequired(stdout) {
3840
+ const patterns = [
3841
+ "not logged in",
3842
+ "login required",
3843
+ "authentication required",
3844
+ "please log in"
3845
+ ];
3846
+ const lower = stdout.toLowerCase();
3847
+ return patterns.some((p) => lower.includes(p));
3848
+ }
3849
+ function deactivateLocalImagePaths(prompt) {
3850
+ return prompt.replace(
3851
+ /(?<=\s|^)(\/[\w./-]+\.(?:png|jpg|jpeg|gif|webp|svg))(?=\s|$)/gi,
3852
+ "[image-path:$1]"
3853
+ );
3854
+ }
3236
3855
  export {
3237
3856
  AGENT_STATUSES,
3238
3857
  AgentRegistry,
@@ -3275,6 +3894,7 @@ export {
3275
3894
  areDependenciesMet,
3276
3895
  buildAgentBindings,
3277
3896
  buildChainOrigin,
3897
+ buildProcessEnv,
3278
3898
  checkAllowlist,
3279
3899
  checkChainDepth,
3280
3900
  checkMentionGate,
@@ -3282,8 +3902,11 @@ export {
3282
3902
  classifyUpdate,
3283
3903
  compareVersions,
3284
3904
  createConfigRevision,
3905
+ deactivateLocalImagePaths,
3285
3906
  defineCoreEntityContexts,
3286
3907
  defineCoreTables,
3908
+ defineDomainEntityContexts,
3909
+ defineDomainTables,
3287
3910
  detectCycle,
3288
3911
  discoverChannels,
3289
3912
  discoverProviders,
@@ -3292,7 +3915,10 @@ export {
3292
3915
  initConfig,
3293
3916
  interpolate,
3294
3917
  interpolateEnv,
3918
+ isLoginRequired,
3919
+ isMaxTurns,
3295
3920
  loadConfig,
3921
+ parseClaudeStream,
3296
3922
  parseVersion,
3297
3923
  runPackageMigrations,
3298
3924
  sanitize,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "botinabox",
3
- "version": "0.2.5",
3
+ "version": "0.3.0",
4
4
  "description": "Bot in a Box — framework for building multi-agent bots",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",