@ashlr/hub 2.2.0 → 3.0.1

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 (177) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/README.md +95 -8
  3. package/dist/api/plugin.d.ts +1 -0
  4. package/dist/cli/backlog.js +2 -0
  5. package/dist/cli/backlog.js.map +1 -1
  6. package/dist/cli/digest.d.ts +6 -0
  7. package/dist/cli/digest.js +127 -2
  8. package/dist/cli/digest.js.map +1 -1
  9. package/dist/cli/eval-fixtures.d.ts +18 -0
  10. package/dist/cli/eval-fixtures.js +38 -0
  11. package/dist/cli/eval-fixtures.js.map +1 -0
  12. package/dist/cli/eval.d.ts +56 -0
  13. package/dist/cli/eval.js +283 -0
  14. package/dist/cli/eval.js.map +1 -0
  15. package/dist/cli/fleet.d.ts +27 -0
  16. package/dist/cli/fleet.js +222 -0
  17. package/dist/cli/fleet.js.map +1 -0
  18. package/dist/cli/goal.d.ts +15 -0
  19. package/dist/cli/goal.js +91 -0
  20. package/dist/cli/goal.js.map +1 -0
  21. package/dist/cli/help.js +10 -3
  22. package/dist/cli/help.js.map +1 -1
  23. package/dist/cli/inbox.d.ts +6 -0
  24. package/dist/cli/inbox.js +183 -8
  25. package/dist/cli/inbox.js.map +1 -1
  26. package/dist/cli/index.js +50 -0
  27. package/dist/cli/index.js.map +1 -1
  28. package/dist/cli/loop.d.ts +16 -0
  29. package/dist/cli/loop.js +66 -0
  30. package/dist/cli/loop.js.map +1 -0
  31. package/dist/cli/mcp.d.ts +12 -0
  32. package/dist/cli/mcp.js +148 -0
  33. package/dist/cli/mcp.js.map +1 -1
  34. package/dist/cli/onboard.d.ts +16 -0
  35. package/dist/cli/onboard.js +71 -0
  36. package/dist/cli/onboard.js.map +1 -1
  37. package/dist/cli/plugins.js +9 -8
  38. package/dist/cli/plugins.js.map +1 -1
  39. package/dist/cli/pulse.d.ts +10 -0
  40. package/dist/cli/pulse.js +261 -0
  41. package/dist/cli/pulse.js.map +1 -1
  42. package/dist/cli/run.js +21 -3
  43. package/dist/cli/run.js.map +1 -1
  44. package/dist/cli/stack.d.ts +34 -0
  45. package/dist/cli/stack.js +221 -0
  46. package/dist/cli/stack.js.map +1 -0
  47. package/dist/cli/update.js +12 -5
  48. package/dist/cli/update.js.map +1 -1
  49. package/dist/core/daemon/loop.d.ts +10 -5
  50. package/dist/core/daemon/loop.js +167 -37
  51. package/dist/core/daemon/loop.js.map +1 -1
  52. package/dist/core/env-bridge.js +3 -2
  53. package/dist/core/env-bridge.js.map +1 -1
  54. package/dist/core/fleet/automerge-pass.d.ts +33 -0
  55. package/dist/core/fleet/automerge-pass.js +61 -0
  56. package/dist/core/fleet/automerge-pass.js.map +1 -0
  57. package/dist/core/fleet/quota.d.ts +70 -0
  58. package/dist/core/fleet/quota.js +198 -0
  59. package/dist/core/fleet/quota.js.map +1 -0
  60. package/dist/core/fleet/router.d.ts +46 -0
  61. package/dist/core/fleet/router.js +121 -0
  62. package/dist/core/fleet/router.js.map +1 -0
  63. package/dist/core/fleet/self.d.ts +58 -0
  64. package/dist/core/fleet/self.js +187 -0
  65. package/dist/core/fleet/self.js.map +1 -0
  66. package/dist/core/fleet/status.d.ts +54 -0
  67. package/dist/core/fleet/status.js +157 -0
  68. package/dist/core/fleet/status.js.map +1 -0
  69. package/dist/core/foundry/provenance.d.ts +78 -0
  70. package/dist/core/foundry/provenance.js +172 -0
  71. package/dist/core/foundry/provenance.js.map +1 -0
  72. package/dist/core/git.d.ts +13 -0
  73. package/dist/core/git.js +26 -0
  74. package/dist/core/git.js.map +1 -1
  75. package/dist/core/inbox/merge.d.ts +151 -0
  76. package/dist/core/inbox/merge.js +858 -0
  77. package/dist/core/inbox/merge.js.map +1 -0
  78. package/dist/core/inbox/store.js +10 -1
  79. package/dist/core/inbox/store.js.map +1 -1
  80. package/dist/core/integrations/markdown.d.ts +66 -0
  81. package/dist/core/integrations/markdown.js +132 -0
  82. package/dist/core/integrations/markdown.js.map +1 -0
  83. package/dist/core/integrations/secrets.d.ts +37 -0
  84. package/dist/core/integrations/secrets.js +67 -0
  85. package/dist/core/integrations/secrets.js.map +1 -0
  86. package/dist/core/integrations/stack.d.ts +47 -0
  87. package/dist/core/integrations/stack.js +107 -0
  88. package/dist/core/integrations/stack.js.map +1 -0
  89. package/dist/core/mcp-native-engineer.d.ts +123 -0
  90. package/dist/core/mcp-native-engineer.js +936 -0
  91. package/dist/core/mcp-native-engineer.js.map +1 -0
  92. package/dist/core/mcp-native.js +20 -42
  93. package/dist/core/mcp-native.js.map +1 -1
  94. package/dist/core/mcp-registry.js +2 -0
  95. package/dist/core/mcp-registry.js.map +1 -1
  96. package/dist/core/observability/codex-source.d.ts +53 -0
  97. package/dist/core/observability/codex-source.js +390 -0
  98. package/dist/core/observability/codex-source.js.map +1 -0
  99. package/dist/core/observability/estimate.d.ts +12 -0
  100. package/dist/core/observability/estimate.js +26 -0
  101. package/dist/core/observability/estimate.js.map +1 -1
  102. package/dist/core/observability/limits.d.ts +52 -0
  103. package/dist/core/observability/limits.js +316 -0
  104. package/dist/core/observability/limits.js.map +1 -0
  105. package/dist/core/observability/usage-source.js +3 -1
  106. package/dist/core/observability/usage-source.js.map +1 -1
  107. package/dist/core/portfolio/scanners.d.ts +1 -0
  108. package/dist/core/portfolio/scanners.js +40 -1
  109. package/dist/core/portfolio/scanners.js.map +1 -1
  110. package/dist/core/providers.js +4 -0
  111. package/dist/core/providers.js.map +1 -1
  112. package/dist/core/quality/health.js +1 -0
  113. package/dist/core/quality/health.js.map +1 -1
  114. package/dist/core/run/agent-loop.d.ts +8 -0
  115. package/dist/core/run/agent-loop.js +24 -14
  116. package/dist/core/run/agent-loop.js.map +1 -1
  117. package/dist/core/run/engine-registry.d.ts +48 -0
  118. package/dist/core/run/engine-registry.js +190 -0
  119. package/dist/core/run/engine-registry.js.map +1 -0
  120. package/dist/core/run/engines.d.ts +17 -3
  121. package/dist/core/run/engines.js +63 -44
  122. package/dist/core/run/engines.js.map +1 -1
  123. package/dist/core/run/learned-router.d.ts +121 -0
  124. package/dist/core/run/learned-router.js +374 -0
  125. package/dist/core/run/learned-router.js.map +1 -0
  126. package/dist/core/run/model-manager.js +3 -3
  127. package/dist/core/run/model-manager.js.map +1 -1
  128. package/dist/core/run/model-profile.d.ts +67 -0
  129. package/dist/core/run/model-profile.js +132 -0
  130. package/dist/core/run/model-profile.js.map +1 -0
  131. package/dist/core/run/orchestrator.d.ts +1 -1
  132. package/dist/core/run/orchestrator.js +642 -510
  133. package/dist/core/run/orchestrator.js.map +1 -1
  134. package/dist/core/run/prompts/budget.d.ts +25 -0
  135. package/dist/core/run/prompts/budget.js +51 -0
  136. package/dist/core/run/prompts/budget.js.map +1 -0
  137. package/dist/core/run/prompts/index.d.ts +14 -0
  138. package/dist/core/run/prompts/index.js +59 -0
  139. package/dist/core/run/prompts/index.js.map +1 -0
  140. package/dist/core/run/prompts/layers.d.ts +26 -0
  141. package/dist/core/run/prompts/layers.js +73 -0
  142. package/dist/core/run/prompts/layers.js.map +1 -0
  143. package/dist/core/run/prompts/roles.d.ts +23 -0
  144. package/dist/core/run/prompts/roles.js +48 -0
  145. package/dist/core/run/prompts/roles.js.map +1 -0
  146. package/dist/core/run/prompts/types.d.ts +38 -0
  147. package/dist/core/run/prompts/types.js +5 -0
  148. package/dist/core/run/prompts/types.js.map +1 -0
  149. package/dist/core/run/provider-client.d.ts +6 -24
  150. package/dist/core/run/provider-client.js +125 -61
  151. package/dist/core/run/provider-client.js.map +1 -1
  152. package/dist/core/run/router.js +8 -0
  153. package/dist/core/run/router.js.map +1 -1
  154. package/dist/core/run/sandboxed-engine.d.ts +66 -0
  155. package/dist/core/run/sandboxed-engine.js +262 -0
  156. package/dist/core/run/sandboxed-engine.js.map +1 -0
  157. package/dist/core/run/verify-commands.d.ts +83 -0
  158. package/dist/core/run/verify-commands.js +234 -0
  159. package/dist/core/run/verify-commands.js.map +1 -0
  160. package/dist/core/run/verify.d.ts +31 -1
  161. package/dist/core/run/verify.js +48 -0
  162. package/dist/core/run/verify.js.map +1 -1
  163. package/dist/core/sandbox/audit.d.ts +27 -0
  164. package/dist/core/sandbox/audit.js +34 -0
  165. package/dist/core/sandbox/audit.js.map +1 -1
  166. package/dist/core/sandbox/confine.d.ts +134 -0
  167. package/dist/core/sandbox/confine.js +370 -0
  168. package/dist/core/sandbox/confine.js.map +1 -0
  169. package/dist/core/types.d.ts +227 -5
  170. package/dist/core/web/api.js +77 -28
  171. package/dist/core/web/api.js.map +1 -1
  172. package/dist/core/web/control.d.ts +116 -0
  173. package/dist/core/web/control.js +307 -0
  174. package/dist/core/web/control.js.map +1 -0
  175. package/dist/core/web/public/app.js +512 -6
  176. package/dist/core/web/public/styles.css +468 -0
  177. package/package.json +3 -2
@@ -0,0 +1,123 @@
1
+ /**
2
+ * M42: Native engineering tool surface — REAL, sandboxed tools for the hub's
3
+ * local-model agent loop.
4
+ *
5
+ * The hub loop (src/core/run/agent-loop.ts) only EXECUTES a tool spec that
6
+ * carries a callable `fn` (it builds toolExecutors from `spec.fn`). The native
7
+ * gateway tools from `listNativeTools()` ship NO `fn`, so they were dead in the
8
+ * loop. This module fixes that two ways:
9
+ * 1. `buildEngineerToolSpecs(eng)` — NEW read/write/exec tools (read_file,
10
+ * glob, grep, write_file, edit_file, bash), each wrapped with a `fn` that
11
+ * runs through `callEngineerTool` (the gated pipeline below).
12
+ * 2. `buildNativeToolSpecsWithFn()` — wraps the existing 11 native tools so
13
+ * they are executable in-process via `callNativeTool`.
14
+ *
15
+ * SECURITY MODEL — defense in depth, in order of authority:
16
+ * 1. WORKSPACE BOUNDARY (the primary spine): every path is resolved with
17
+ * `resolveInside(root, p)`, which realpath-resolves both the root and the
18
+ * target and refuses anything that is not the root itself or a descendant
19
+ * of `root + sep`. `workspaceRoot` is an absolute sandbox worktree path —
20
+ * NEVER `/` or `~`. A symlink that points outside the root is rejected
21
+ * because realpath resolves it before the prefix check.
22
+ * 2. KILL SWITCH + ENROLLMENT (reused, NOT reinvented): every mutating tool
23
+ * calls `assertMayMutate(eng.sourceRepo, { allowAnyRepo: true })`, which
24
+ * ALWAYS throws when ~/.ashlr/KILL is present and (unless the test env seam
25
+ * ASHLR_TEST_ALLOW_ANY_REPO=1 is set) when the repo is not enrolled.
26
+ * `callEngineerTool` also refuses non-read tools when the kill switch is on
27
+ * BEFORE loading config or running a handler.
28
+ * 3. COMMAND DENY-LIST (`assertCommandAllowed`): a best-effort guard against
29
+ * egress / destructive shell verbs. This is DEFENSE IN DEPTH only — the
30
+ * worktree boundary + kill-switch + minimal env are the real containment.
31
+ * 4. MINIMAL ENV (`minimalEnv`): `bash` spawns with a hand-built env (PATH +
32
+ * a scratch HOME under os.tmpdir() + LANG) merged with ashlr's NON-SECRET
33
+ * keys via `withToolEnv`. The full `process.env` (and thus any API keys it
34
+ * carries) is NEVER handed to the shell.
35
+ *
36
+ * Every tool result is rendered through `renderToolText` (serialize + scrub
37
+ * secrets + 32KB cap), and every call (ok / refused / error) is audited as
38
+ * 'mcp:engineer-call'. `callEngineerTool` NEVER throws — failures surface as
39
+ * error text the model can read.
40
+ */
41
+ /**
42
+ * The agent's engineering context: the absolute sandbox worktree it may read /
43
+ * write / exec within, the source repo used for the kill-switch + enrollment
44
+ * gate, and the per-session capability flags.
45
+ *
46
+ * `workspaceRoot` MUST be an absolute path to a real sandbox worktree directory
47
+ * — NEVER `/` or `~`. Callers create it (a git worktree / tmp checkout) before
48
+ * constructing the context.
49
+ */
50
+ export interface EngineerContext {
51
+ /** Absolute root the agent may read/write/exec within (a sandbox worktree). */
52
+ workspaceRoot: string;
53
+ /** Source repo path used for the assertMayMutate (kill-switch + enrollment) gate. */
54
+ sourceRepo: string;
55
+ /** Whether write_file / edit_file are permitted this session. */
56
+ allowWrite: boolean;
57
+ /** Whether bash is permitted this session. */
58
+ allowExec: boolean;
59
+ }
60
+ /** Clamp `n` into [min, max]. NaN/non-finite collapses to `min`. */
61
+ export declare function clamp(n: number, min: number, max: number): number;
62
+ /**
63
+ * Build the MINIMAL, cross-platform env for a spawned shell. Deliberately does
64
+ * NOT pass the full process.env — that would leak ANTHROPIC_API_KEY and friends
65
+ * to the shell. The full process.env (and any API keys it carries) is NEVER
66
+ * handed to the shell; ashlr's non-secret config keys are layered on by the
67
+ * caller via withToolEnv.
68
+ *
69
+ * Always carried: PATH and a scratch home dir under os.tmpdir().
70
+ * - posix: HOME = scratch, LANG = process.env.LANG ?? 'C'.
71
+ * - win32: USERPROFILE = scratch, plus the handful of system vars Windows
72
+ * tools genuinely NEED to function (SystemRoot/windir/COMSPEC/PATHEXT/…)
73
+ * and TEMP/TMP pointed AT the scratch dir. No secrets are passed through.
74
+ */
75
+ export declare function minimalEnv(): NodeJS.ProcessEnv;
76
+ /**
77
+ * Refuse a bash command that matches an egress / destructive / privilege verb.
78
+ *
79
+ * This is intentionally a coarse, defense-in-depth guard layered ON TOP of the
80
+ * real containment (workspace boundary + kill-switch + minimal env). It is NOT
81
+ * meant to be a complete shell parser — a determined adversary can obfuscate
82
+ * around any deny-list, which is exactly why the boundary + minimal env (no
83
+ * secrets, scratch HOME, cwd pinned to the worktree) are the load-bearing
84
+ * controls. Throws with a clear message on a match.
85
+ *
86
+ * NOTE: `bash` grants local code execution WITH network access; it must only be
87
+ * enabled (`--bash` / eng.allowExec) for goals you trust. This deny-list reduces
88
+ * the blast radius of an obviously-hostile command but is NOT the security
89
+ * boundary and must never be relied on as one.
90
+ */
91
+ export declare function assertCommandAllowed(command: string): void;
92
+ /**
93
+ * Execute an engineer tool through the full safety pipeline. NEVER throws:
94
+ * unknown tools, capability/kill-switch refusals, and handler failures all
95
+ * surface as rendered error text. Every outcome is audited. The returned
96
+ * string is always rendered through `renderToolText` (scrub + 32KB cap).
97
+ */
98
+ export declare function callEngineerTool(name: string, rawArgs: unknown, eng: EngineerContext): Promise<string>;
99
+ /** A tool spec carrying an executable `fn`, ready for the agent loop. */
100
+ export interface ExecutableToolSpec {
101
+ type: 'function';
102
+ function: {
103
+ name: string;
104
+ description: string;
105
+ parameters: object;
106
+ };
107
+ name: string;
108
+ fn: (args: unknown) => Promise<string>;
109
+ }
110
+ /**
111
+ * Build executable specs for the engineer tools, filtered by capability:
112
+ * read tools always; write tools only when eng.allowWrite; exec tools only when
113
+ * eng.allowExec. Each spec's `fn` routes through `callEngineerTool` so the full
114
+ * gated pipeline (boundary + kill-switch + audit + render) always runs.
115
+ */
116
+ export declare function buildEngineerToolSpecs(eng: EngineerContext): ExecutableToolSpec[];
117
+ /**
118
+ * Wrap the existing native tools (from listNativeTools) so they are EXECUTABLE
119
+ * in-process via `callNativeTool`. This fixes the pre-existing dead-tool bug:
120
+ * the native specs the gateway exposes carry no `fn`, so the agent loop's
121
+ * toolExecutors map never picked them up.
122
+ */
123
+ export declare function buildNativeToolSpecsWithFn(): ExecutableToolSpec[];