ai-runtime-engine 1.1.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 (269) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/LICENSE +15 -0
  3. package/README.md +489 -0
  4. package/dist/artifacts/artifacts.d.ts +28 -0
  5. package/dist/artifacts/artifacts.js +46 -0
  6. package/dist/benchmark/benchmark.d.ts +23 -0
  7. package/dist/benchmark/benchmark.js +40 -0
  8. package/dist/cli/cli.d.ts +6 -0
  9. package/dist/cli/cli.js +161 -0
  10. package/dist/cli/commands/cleanup.d.ts +47 -0
  11. package/dist/cli/commands/cleanup.js +96 -0
  12. package/dist/cli/commands/config.d.ts +8 -0
  13. package/dist/cli/commands/config.js +28 -0
  14. package/dist/cli/commands/doctor.d.ts +57 -0
  15. package/dist/cli/commands/doctor.js +86 -0
  16. package/dist/cli/commands/executions.d.ts +9 -0
  17. package/dist/cli/commands/executions.js +25 -0
  18. package/dist/cli/commands/info.d.ts +43 -0
  19. package/dist/cli/commands/info.js +53 -0
  20. package/dist/cli/commands/init.d.ts +5 -0
  21. package/dist/cli/commands/init.js +75 -0
  22. package/dist/cli/commands/inspect.d.ts +16 -0
  23. package/dist/cli/commands/inspect.js +60 -0
  24. package/dist/cli/commands/phase2.d.ts +22 -0
  25. package/dist/cli/commands/phase2.js +83 -0
  26. package/dist/cli/commands/route.d.ts +14 -0
  27. package/dist/cli/commands/route.js +49 -0
  28. package/dist/cli/commands/run.d.ts +11 -0
  29. package/dist/cli/commands/run.js +37 -0
  30. package/dist/cli/commands/setup.d.ts +34 -0
  31. package/dist/cli/commands/setup.js +104 -0
  32. package/dist/cli/commands/skills.d.ts +28 -0
  33. package/dist/cli/commands/skills.js +48 -0
  34. package/dist/cli/commands/test.d.ts +7 -0
  35. package/dist/cli/commands/test.js +29 -0
  36. package/dist/cli/context.d.ts +12 -0
  37. package/dist/cli/context.js +16 -0
  38. package/dist/cli/interactive/repl.d.ts +6 -0
  39. package/dist/cli/interactive/repl.js +45 -0
  40. package/dist/cli/interactive/session.d.ts +36 -0
  41. package/dist/cli/interactive/session.js +356 -0
  42. package/dist/cli/prompt.d.ts +6 -0
  43. package/dist/cli/prompt.js +18 -0
  44. package/dist/cli/render.d.ts +7 -0
  45. package/dist/cli/render.js +14 -0
  46. package/dist/comparison/analysis.d.ts +46 -0
  47. package/dist/comparison/analysis.js +177 -0
  48. package/dist/comparison/comparator.d.ts +46 -0
  49. package/dist/comparison/comparator.js +270 -0
  50. package/dist/comparison/comparison.d.ts +140 -0
  51. package/dist/comparison/comparison.js +9 -0
  52. package/dist/comparison/render.d.ts +7 -0
  53. package/dist/comparison/render.js +66 -0
  54. package/dist/config/defaults.d.ts +52 -0
  55. package/dist/config/defaults.js +56 -0
  56. package/dist/config/load.d.ts +17 -0
  57. package/dist/config/load.js +50 -0
  58. package/dist/config/providerDefaults.d.ts +17 -0
  59. package/dist/config/providerDefaults.js +61 -0
  60. package/dist/config/schema.d.ts +9 -0
  61. package/dist/config/schema.js +78 -0
  62. package/dist/context/budget.d.ts +13 -0
  63. package/dist/context/budget.js +17 -0
  64. package/dist/context/compiler.d.ts +61 -0
  65. package/dist/context/compiler.js +125 -0
  66. package/dist/context/tokens.d.ts +19 -0
  67. package/dist/context/tokens.js +38 -0
  68. package/dist/conversations/conversations.d.ts +38 -0
  69. package/dist/conversations/conversations.js +64 -0
  70. package/dist/core/capabilities/evidence.d.ts +40 -0
  71. package/dist/core/capabilities/evidence.js +102 -0
  72. package/dist/core/capabilities/overlay.d.ts +15 -0
  73. package/dist/core/capabilities/overlay.js +0 -0
  74. package/dist/core/capabilities/taxonomy.d.ts +19 -0
  75. package/dist/core/capabilities/taxonomy.js +25 -0
  76. package/dist/core/fallback/errors.d.ts +30 -0
  77. package/dist/core/fallback/errors.js +80 -0
  78. package/dist/core/fallback/fallback.d.ts +40 -0
  79. package/dist/core/fallback/fallback.js +82 -0
  80. package/dist/core/fallback/retryPolicy.d.ts +11 -0
  81. package/dist/core/fallback/retryPolicy.js +14 -0
  82. package/dist/core/health/health.d.ts +3 -0
  83. package/dist/core/health/health.js +5 -0
  84. package/dist/core/health/monitor.d.ts +23 -0
  85. package/dist/core/health/monitor.js +82 -0
  86. package/dist/core/policies/budget.d.ts +19 -0
  87. package/dist/core/policies/budget.js +37 -0
  88. package/dist/core/registry/builtinTasks.d.ts +8 -0
  89. package/dist/core/registry/builtinTasks.js +54 -0
  90. package/dist/core/registry/registry.d.ts +18 -0
  91. package/dist/core/registry/registry.js +33 -0
  92. package/dist/core/registry/taskRegistry.d.ts +15 -0
  93. package/dist/core/registry/taskRegistry.js +30 -0
  94. package/dist/core/router/confidence.d.ts +7 -0
  95. package/dist/core/router/confidence.js +20 -0
  96. package/dist/core/router/dimensions.d.ts +16 -0
  97. package/dist/core/router/dimensions.js +60 -0
  98. package/dist/core/router/executor.d.ts +16 -0
  99. package/dist/core/router/executor.js +25 -0
  100. package/dist/core/router/filter.d.ts +34 -0
  101. package/dist/core/router/filter.js +113 -0
  102. package/dist/core/router/normalize.d.ts +30 -0
  103. package/dist/core/router/normalize.js +119 -0
  104. package/dist/core/router/request.d.ts +4 -0
  105. package/dist/core/router/request.js +21 -0
  106. package/dist/core/router/router.d.ts +32 -0
  107. package/dist/core/router/router.js +195 -0
  108. package/dist/core/router/routingPrefs.d.ts +11 -0
  109. package/dist/core/router/routingPrefs.js +30 -0
  110. package/dist/core/router/scorer.d.ts +19 -0
  111. package/dist/core/router/scorer.js +50 -0
  112. package/dist/core/router/weights.d.ts +9 -0
  113. package/dist/core/router/weights.js +31 -0
  114. package/dist/core/validation/validator.d.ts +16 -0
  115. package/dist/core/validation/validator.js +33 -0
  116. package/dist/discovery/modelCatalog.d.ts +28 -0
  117. package/dist/discovery/modelCatalog.js +105 -0
  118. package/dist/discovery/openapi.d.ts +25 -0
  119. package/dist/discovery/openapi.js +76 -0
  120. package/dist/executions/checkpoint.d.ts +26 -0
  121. package/dist/executions/checkpoint.js +114 -0
  122. package/dist/executions/execution.d.ts +51 -0
  123. package/dist/executions/execution.js +8 -0
  124. package/dist/executions/store.d.ts +52 -0
  125. package/dist/executions/store.js +124 -0
  126. package/dist/generation/generateAdapter.d.ts +17 -0
  127. package/dist/generation/generateAdapter.js +30 -0
  128. package/dist/index.d.ts +147 -0
  129. package/dist/index.js +107 -0
  130. package/dist/learning/feedback.d.ts +9 -0
  131. package/dist/learning/feedback.js +18 -0
  132. package/dist/learning/learningStore.d.ts +68 -0
  133. package/dist/learning/learningStore.js +138 -0
  134. package/dist/learning/performanceStore.d.ts +27 -0
  135. package/dist/learning/performanceStore.js +0 -0
  136. package/dist/marketplace/presets.d.ts +24 -0
  137. package/dist/marketplace/presets.js +52 -0
  138. package/dist/mcp/mcp.d.ts +31 -0
  139. package/dist/mcp/mcp.js +54 -0
  140. package/dist/memory/bm25.d.ts +16 -0
  141. package/dist/memory/bm25.js +56 -0
  142. package/dist/memory/classifier.d.ts +14 -0
  143. package/dist/memory/classifier.js +17 -0
  144. package/dist/memory/memory.d.ts +80 -0
  145. package/dist/memory/memory.js +191 -0
  146. package/dist/orchestration/executor.d.ts +35 -0
  147. package/dist/orchestration/executor.js +65 -0
  148. package/dist/orchestration/orchestrator.d.ts +42 -0
  149. package/dist/orchestration/orchestrator.js +63 -0
  150. package/dist/orchestration/plan.d.ts +37 -0
  151. package/dist/orchestration/plan.js +70 -0
  152. package/dist/orchestration/planner.d.ts +29 -0
  153. package/dist/orchestration/planner.js +69 -0
  154. package/dist/plugin/ai.d.ts +82 -0
  155. package/dist/plugin/ai.js +167 -0
  156. package/dist/probing/probe.d.ts +25 -0
  157. package/dist/probing/probe.js +63 -0
  158. package/dist/providers/factory.d.ts +18 -0
  159. package/dist/providers/factory.js +54 -0
  160. package/dist/providers/httpClient.d.ts +34 -0
  161. package/dist/providers/httpClient.js +80 -0
  162. package/dist/providers/httpProvider.d.ts +49 -0
  163. package/dist/providers/httpProvider.js +135 -0
  164. package/dist/providers/mock/demo.d.ts +13 -0
  165. package/dist/providers/mock/demo.js +58 -0
  166. package/dist/providers/mock/mockProvider.d.ts +35 -0
  167. package/dist/providers/mock/mockProvider.js +121 -0
  168. package/dist/providers/mock/scenarios.d.ts +44 -0
  169. package/dist/providers/mock/scenarios.js +30 -0
  170. package/dist/providers/provider.d.ts +26 -0
  171. package/dist/providers/provider.js +11 -0
  172. package/dist/providers/wire/anthropicWire.d.ts +6 -0
  173. package/dist/providers/wire/anthropicWire.js +83 -0
  174. package/dist/providers/wire/openaiWire.d.ts +7 -0
  175. package/dist/providers/wire/openaiWire.js +81 -0
  176. package/dist/providers/wire/registry.d.ts +8 -0
  177. package/dist/providers/wire/registry.js +20 -0
  178. package/dist/providers/wire/types.d.ts +39 -0
  179. package/dist/providers/wire/types.js +24 -0
  180. package/dist/runtime/config.d.ts +31 -0
  181. package/dist/runtime/config.js +121 -0
  182. package/dist/runtime/context.d.ts +34 -0
  183. package/dist/runtime/context.js +11 -0
  184. package/dist/runtime/events.d.ts +99 -0
  185. package/dist/runtime/events.js +82 -0
  186. package/dist/runtime/host.d.ts +27 -0
  187. package/dist/runtime/host.js +7 -0
  188. package/dist/runtime/intent/classifier.d.ts +30 -0
  189. package/dist/runtime/intent/classifier.js +60 -0
  190. package/dist/runtime/intent/signals.d.ts +19 -0
  191. package/dist/runtime/intent/signals.js +46 -0
  192. package/dist/runtime/modes/availability.d.ts +11 -0
  193. package/dist/runtime/modes/availability.js +17 -0
  194. package/dist/runtime/modes/chat.d.ts +18 -0
  195. package/dist/runtime/modes/chat.js +67 -0
  196. package/dist/runtime/modes/modeResolver.d.ts +43 -0
  197. package/dist/runtime/modes/modeResolver.js +78 -0
  198. package/dist/runtime/policy.d.ts +72 -0
  199. package/dist/runtime/policy.js +59 -0
  200. package/dist/runtime/providerView.d.ts +62 -0
  201. package/dist/runtime/providerView.js +105 -0
  202. package/dist/runtime/routing.d.ts +26 -0
  203. package/dist/runtime/routing.js +65 -0
  204. package/dist/runtime/runtime.d.ts +191 -0
  205. package/dist/runtime/runtime.js +718 -0
  206. package/dist/runtime/types.d.ts +153 -0
  207. package/dist/runtime/types.js +9 -0
  208. package/dist/runtime/workspace/detectors.d.ts +15 -0
  209. package/dist/runtime/workspace/detectors.js +57 -0
  210. package/dist/runtime/workspace/workspace.d.ts +29 -0
  211. package/dist/runtime/workspace/workspace.js +116 -0
  212. package/dist/security/credentials.d.ts +26 -0
  213. package/dist/security/credentials.js +34 -0
  214. package/dist/security/redact.d.ts +16 -0
  215. package/dist/security/redact.js +57 -0
  216. package/dist/skills/builtins/fileAnalyzer.d.ts +7 -0
  217. package/dist/skills/builtins/fileAnalyzer.js +47 -0
  218. package/dist/skills/builtins/repositoryAnalyzer.d.ts +6 -0
  219. package/dist/skills/builtins/repositoryAnalyzer.js +47 -0
  220. package/dist/skills/discovery.d.ts +61 -0
  221. package/dist/skills/discovery.js +211 -0
  222. package/dist/skills/manifest.d.ts +30 -0
  223. package/dist/skills/manifest.js +75 -0
  224. package/dist/skills/registry.d.ts +15 -0
  225. package/dist/skills/registry.js +22 -0
  226. package/dist/skills/skill.d.ts +64 -0
  227. package/dist/skills/skill.js +8 -0
  228. package/dist/store/area.d.ts +54 -0
  229. package/dist/store/area.js +164 -0
  230. package/dist/store/paths.d.ts +15 -0
  231. package/dist/store/paths.js +48 -0
  232. package/dist/store/store.d.ts +59 -0
  233. package/dist/store/store.js +140 -0
  234. package/dist/telemetry/sinks/file.d.ts +12 -0
  235. package/dist/telemetry/sinks/file.js +28 -0
  236. package/dist/telemetry/telemetry.d.ts +36 -0
  237. package/dist/telemetry/telemetry.js +63 -0
  238. package/dist/tools/builtins/filesystem.d.ts +7 -0
  239. package/dist/tools/builtins/filesystem.js +53 -0
  240. package/dist/tools/builtins/git.d.ts +10 -0
  241. package/dist/tools/builtins/git.js +66 -0
  242. package/dist/tools/builtins/shell.d.ts +17 -0
  243. package/dist/tools/builtins/shell.js +91 -0
  244. package/dist/tools/jail.d.ts +12 -0
  245. package/dist/tools/jail.js +98 -0
  246. package/dist/tools/permissions.d.ts +25 -0
  247. package/dist/tools/permissions.js +24 -0
  248. package/dist/tools/registry.d.ts +10 -0
  249. package/dist/tools/registry.js +20 -0
  250. package/dist/tools/runner.d.ts +23 -0
  251. package/dist/tools/runner.js +64 -0
  252. package/dist/tools/tool.d.ts +53 -0
  253. package/dist/tools/tool.js +24 -0
  254. package/dist/tools/untrusted.d.ts +13 -0
  255. package/dist/tools/untrusted.js +30 -0
  256. package/dist/types.d.ts +460 -0
  257. package/dist/types.js +12 -0
  258. package/dist/util/clock.d.ts +6 -0
  259. package/dist/util/clock.js +4 -0
  260. package/dist/util/extractJson.d.ts +8 -0
  261. package/dist/util/extractJson.js +54 -0
  262. package/dist/verification/verify.d.ts +26 -0
  263. package/dist/verification/verify.js +67 -0
  264. package/docs/GUIDE.md +358 -0
  265. package/docs/README.md +21 -0
  266. package/docs/architecture.md +78 -0
  267. package/docs/router.md +376 -0
  268. package/docs/security.md +55 -0
  269. package/package.json +67 -0
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Public Runtime types (Phase 1). The Runtime is a thin orchestration facade OVER the existing `AI`
3
+ * router — it never routes itself. These types are additive and provider-agnostic; nothing here
4
+ * duplicates a router concept.
5
+ */
6
+ import type { InputPart, RouterConfig, RouterErrorInfo, RoutingPreferences, RoutingReport, RunRequest, Sensitivity, Strategy } from '../types.js';
7
+ import type { CompileMetrics, ContextValidation } from '../context/compiler.js';
8
+ import type { PermissionPolicy } from './policy.js';
9
+ import type { ExecutionPlan } from '../orchestration/plan.js';
10
+ import type { ComparisonPin, ComparisonResult } from '../comparison/comparison.js';
11
+ export declare const RUNTIME_MODES: readonly ["auto", "chat", "plan", "execute", "orchestrate", "agent", "compare", "debug"];
12
+ export type RuntimeMode = (typeof RUNTIME_MODES)[number];
13
+ /** A mode that can actually run — never 'auto' (auto resolves TO one of these). */
14
+ export type ExecutableMode = Exclude<RuntimeMode, 'auto'>;
15
+ /** Which rung of the precedence chain decided the selected mode. */
16
+ export type ModeSource = 'explicit' | 'config' | 'env' | 'auto';
17
+ export interface ModeResolution {
18
+ /** What the caller asked for on this run; 'auto' when omitted. */
19
+ requested: RuntimeMode;
20
+ /** What precedence + detection decided. */
21
+ selected: ExecutableMode;
22
+ /** What actually ran. In Phase 1 always 'chat' (unimplemented modes degrade to chat). */
23
+ executed: ExecutableMode;
24
+ /** 1.0 for explicit/config/env; the classifier score for auto detection. */
25
+ confidence: number;
26
+ /** Human-readable; carries the degrade notice when executed !== selected. */
27
+ reason: string;
28
+ /** Which precedence rung fixed `selected`. */
29
+ source: ModeSource;
30
+ }
31
+ export interface ArtifactRef {
32
+ id: string;
33
+ type: string;
34
+ source: string;
35
+ /** Where the actual content lives (resolved by the Artifact Store, Phase 7). */
36
+ contentRef: string;
37
+ checksum?: string;
38
+ size?: number;
39
+ metadata?: Record<string, unknown>;
40
+ }
41
+ /** Task-level status. `ok` (on RuntimeResult) is whether the Runtime OP succeeded; this is whether the TASK is done/waiting. */
42
+ export type RuntimeStatus = 'completed' | 'waiting_for_clarification' | 'waiting_for_approval' | 'failed';
43
+ export interface Clarification {
44
+ question: string;
45
+ options?: string[];
46
+ }
47
+ export interface RuntimeSettings {
48
+ /** Project default mode. 'auto' means "always detect". */
49
+ defaultMode?: RuntimeMode;
50
+ defaultStrategy?: Strategy;
51
+ /** Context-compilation settings (token budget for the compiled model context). */
52
+ context?: {
53
+ maxTokens?: number;
54
+ };
55
+ /** Project exclude/prefer routing (merged with env + per-run; EXCLUDE hard, PREFER soft). */
56
+ routing?: RoutingPreferences;
57
+ /** Skill discovery: consented local paths, npm skill packs to load, and whether to auto-load on load. */
58
+ skills?: {
59
+ paths?: string[];
60
+ packages?: string[];
61
+ autoload?: boolean;
62
+ };
63
+ }
64
+ export interface RuntimeConfig {
65
+ runtime?: RuntimeSettings;
66
+ /** Tool permission grants (deny-by-default for write/exec; see PermissionPolicy). */
67
+ permissions?: PermissionPolicy;
68
+ /** The existing router configuration (providers, strategy, learning, verification, budget, policy, tasks…). */
69
+ router: RouterConfig;
70
+ }
71
+ export interface RuntimeRunInput {
72
+ input: string | {
73
+ text?: string;
74
+ parts?: InputPart[];
75
+ };
76
+ /** Explicit per-run mode (highest precedence; never overridden by detection). */
77
+ mode?: RuntimeMode;
78
+ /** Explicit per-run strategy (highest precedence). */
79
+ strategy?: Strategy;
80
+ /** System prompt, appended after the workspace summary line. */
81
+ system?: string;
82
+ sensitivity?: Sensitivity;
83
+ output?: RunRequest['output'];
84
+ /** Prepend a one-line workspace summary to the system prompt (default true). Never sends repo contents. */
85
+ workspaceContext?: boolean;
86
+ /** Per-run context-compilation overrides (highest-precedence token budget). */
87
+ context?: {
88
+ maxTokens?: number;
89
+ };
90
+ /** Dry-run: plan + report what WOULD happen, performing zero mutations (plan/execute/orchestrate). */
91
+ dryRun?: boolean;
92
+ /** Caller idempotency identity (dedup enforced from Phase 7). */
93
+ requestId?: string;
94
+ /** Per-run exclude/prefer routing (highest precedence; merged with config + env). */
95
+ routing?: RoutingPreferences;
96
+ /** Compare-mode options (Phase 8): which pins to fan out across, an optional judge, the router task. */
97
+ compare?: CompareRunOptions;
98
+ /** Escape hatch: merged onto the generated RunRequest last, but cannot override task/input. */
99
+ request?: Partial<Omit<RunRequest, 'task' | 'input'>>;
100
+ }
101
+ /** Compare-mode inputs. With no pins, the runtime fans out across every enabled provider (default model). */
102
+ export interface CompareRunOptions {
103
+ /** Candidate provider/model pins. Default: one pin per enabled provider. */
104
+ pins?: ComparisonPin[];
105
+ /** Optional judge model (a labeled opinion used only when no evidence function is supplied). */
106
+ judge?: ComparisonPin;
107
+ /** Router task each pin runs (default 'chat'). */
108
+ task?: string;
109
+ /** Agreement/clustering similarity threshold, 0..1 (default 0.75). */
110
+ agreementThreshold?: number;
111
+ }
112
+ export interface RuntimeResult {
113
+ /** Whether the Runtime operation itself succeeded (NOT whether the task is complete — see `status`). */
114
+ ok: boolean;
115
+ /** Identity of this single runtime.run() invocation. */
116
+ runId: string;
117
+ mode: ModeResolution;
118
+ status: RuntimeStatus;
119
+ response?: {
120
+ text?: string;
121
+ json?: unknown;
122
+ };
123
+ /** Present when the runtime needs the user to disambiguate. Never a failure. */
124
+ clarification?: Clarification;
125
+ /** Verbatim passthrough of the router's decision trace when a model ran. */
126
+ routing?: RoutingReport;
127
+ error?: RouterErrorInfo;
128
+ /** Artifact references produced by this run (empty until the Artifact Store lands in Phase 7). */
129
+ artifacts: ArtifactRef[];
130
+ /** What memory was retrieved into context and/or captured this run (Phase 3). */
131
+ memory?: MemoryTrace;
132
+ /** How the model context was compiled this run: metrics + loss validation (Phase 4). */
133
+ context?: ContextReport;
134
+ /** The plan the orchestrator produced/executed (with per-step statuses) — Phase 6. */
135
+ plan?: ExecutionPlan;
136
+ /** The multi-model comparison this run produced (compare mode / runtime.compare) — Phase 8. */
137
+ comparison?: ComparisonResult;
138
+ execution?: unknown;
139
+ learning?: unknown;
140
+ }
141
+ /** Memory activity for a run: facts pulled into context, and any fact captured from the input. */
142
+ export interface MemoryTrace {
143
+ retrieved: string[];
144
+ captured?: {
145
+ id: string;
146
+ scope: string;
147
+ };
148
+ }
149
+ /** How the context compiler built the model context this run. */
150
+ export interface ContextReport {
151
+ metrics: CompileMetrics;
152
+ validation: ContextValidation;
153
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Public Runtime types (Phase 1). The Runtime is a thin orchestration facade OVER the existing `AI`
3
+ * router — it never routes itself. These types are additive and provider-agnostic; nothing here
4
+ * duplicates a router concept.
5
+ */
6
+ // ─────────────────────────────────────────────────────────────────────────────
7
+ // Modes
8
+ // ─────────────────────────────────────────────────────────────────────────────
9
+ export const RUNTIME_MODES = ['auto', 'chat', 'plan', 'execute', 'orchestrate', 'agent', 'compare', 'debug'];
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Table-driven workspace trait detection. Pure data + tiny helpers — fs marker lookups only, never a
3
+ * child process, never a recursive walk. Adding a marker is the sanctioned way to grow detection.
4
+ */
5
+ /** Marker file (in the workspace root) → language it implies. */
6
+ export declare const LANGUAGE_MARKERS: ReadonlyArray<readonly [string, string]>;
7
+ export type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun' | 'poetry' | 'uv' | 'cargo' | 'go' | 'maven' | 'gradle';
8
+ /** Lockfile/marker (in the root) → package manager. First match wins; JS lockfiles are checked first. */
9
+ export declare const LOCKFILE_TO_PM: ReadonlyArray<readonly [string, PackageManager]>;
10
+ /** Dependency name (in package.json deps/devDeps) → framework. First match wins. */
11
+ export declare const DEP_TO_FRAMEWORK: ReadonlyArray<readonly [string, string]>;
12
+ /** Dependency name → test framework. First match wins; node:test inferred separately from scripts.test. */
13
+ export declare const DEP_TO_TEST_FRAMEWORK: ReadonlyArray<readonly [string, string]>;
14
+ /** Parse the current branch out of raw `.git/HEAD` text. Returns 'detached' for a raw SHA. */
15
+ export declare function branchFromHead(headText: string): string;
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Table-driven workspace trait detection. Pure data + tiny helpers — fs marker lookups only, never a
3
+ * child process, never a recursive walk. Adding a marker is the sanctioned way to grow detection.
4
+ */
5
+ /** Marker file (in the workspace root) → language it implies. */
6
+ export const LANGUAGE_MARKERS = [
7
+ ['tsconfig.json', 'typescript'],
8
+ ['package.json', 'javascript'],
9
+ ['pyproject.toml', 'python'],
10
+ ['requirements.txt', 'python'],
11
+ ['setup.py', 'python'],
12
+ ['go.mod', 'go'],
13
+ ['Cargo.toml', 'rust'],
14
+ ['pom.xml', 'java'],
15
+ ['build.gradle', 'java'],
16
+ ];
17
+ /** Lockfile/marker (in the root) → package manager. First match wins; JS lockfiles are checked first. */
18
+ export const LOCKFILE_TO_PM = [
19
+ ['pnpm-lock.yaml', 'pnpm'],
20
+ ['yarn.lock', 'yarn'],
21
+ ['bun.lockb', 'bun'],
22
+ ['bun.lock', 'bun'],
23
+ ['package-lock.json', 'npm'],
24
+ ['poetry.lock', 'poetry'],
25
+ ['uv.lock', 'uv'],
26
+ ['Cargo.lock', 'cargo'],
27
+ ['go.sum', 'go'],
28
+ ['pom.xml', 'maven'],
29
+ ['build.gradle', 'gradle'],
30
+ ];
31
+ /** Dependency name (in package.json deps/devDeps) → framework. First match wins. */
32
+ export const DEP_TO_FRAMEWORK = [
33
+ ['next', 'next'],
34
+ ['@remix-run/react', 'remix'],
35
+ ['nuxt', 'nuxt'],
36
+ ['@angular/core', 'angular'],
37
+ ['svelte', 'svelte'],
38
+ ['vue', 'vue'],
39
+ ['react', 'react'],
40
+ ['@nestjs/core', 'nest'],
41
+ ['fastify', 'fastify'],
42
+ ['express', 'express'],
43
+ ];
44
+ /** Dependency name → test framework. First match wins; node:test inferred separately from scripts.test. */
45
+ export const DEP_TO_TEST_FRAMEWORK = [
46
+ ['@playwright/test', 'playwright'],
47
+ ['playwright', 'playwright'],
48
+ ['vitest', 'vitest'],
49
+ ['jest', 'jest'],
50
+ ['mocha', 'mocha'],
51
+ ['pytest', 'pytest'],
52
+ ];
53
+ /** Parse the current branch out of raw `.git/HEAD` text. Returns 'detached' for a raw SHA. */
54
+ export function branchFromHead(headText) {
55
+ const m = /^ref:\s*refs\/heads\/(.+)\s*$/m.exec(headText);
56
+ return m?.[1]?.trim() ?? 'detached';
57
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Workspace detection — fs-only, deterministic, never throws. It probes a fixed marker list in the
3
+ * root directory (no recursion, no child process; git branch is parsed from `.git/HEAD` text) and
4
+ * produces a WorkspaceInfo. The ONLY thing that ever reaches a provider is summarizeWorkspace()'s one
5
+ * line (trait names + basename), never file contents and never the absolute path.
6
+ */
7
+ import type { Clock } from '../../util/clock.js';
8
+ import type { PackageManager } from './detectors.js';
9
+ export interface WorkspaceInfo {
10
+ /** Absolute root. */
11
+ root: string;
12
+ name?: string;
13
+ exists: boolean;
14
+ git: {
15
+ isRepo: boolean;
16
+ branch?: string;
17
+ };
18
+ languages: string[];
19
+ packageManager?: PackageManager;
20
+ framework?: string;
21
+ testFramework?: string;
22
+ /** Marker files actually found (relative names). */
23
+ projectFiles: string[];
24
+ detectedAt: number;
25
+ }
26
+ /** Detect workspace traits under `root`. Never throws; a missing/unreadable root yields exists:false. */
27
+ export declare function detectWorkspace(root: string, clock?: Clock): WorkspaceInfo;
28
+ /** One-line, provider-safe summary: trait names + basename only — never paths (beyond basename) or contents. */
29
+ export declare function summarizeWorkspace(info: WorkspaceInfo): string;
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Workspace detection — fs-only, deterministic, never throws. It probes a fixed marker list in the
3
+ * root directory (no recursion, no child process; git branch is parsed from `.git/HEAD` text) and
4
+ * produces a WorkspaceInfo. The ONLY thing that ever reaches a provider is summarizeWorkspace()'s one
5
+ * line (trait names + basename), never file contents and never the absolute path.
6
+ */
7
+ import { existsSync, readFileSync } from 'node:fs';
8
+ import { basename, join } from 'node:path';
9
+ import { systemClock } from '../../util/clock.js';
10
+ import { LANGUAGE_MARKERS, LOCKFILE_TO_PM, DEP_TO_FRAMEWORK, DEP_TO_TEST_FRAMEWORK, branchFromHead } from './detectors.js';
11
+ function readJsonSafe(path) {
12
+ try {
13
+ if (!existsSync(path))
14
+ return undefined;
15
+ const parsed = JSON.parse(readFileSync(path, 'utf8'));
16
+ return parsed && typeof parsed === 'object' ? parsed : undefined;
17
+ }
18
+ catch {
19
+ return undefined;
20
+ }
21
+ }
22
+ function readTextSafe(path) {
23
+ try {
24
+ return existsSync(path) ? readFileSync(path, 'utf8') : undefined;
25
+ }
26
+ catch {
27
+ return undefined;
28
+ }
29
+ }
30
+ /** Detect workspace traits under `root`. Never throws; a missing/unreadable root yields exists:false. */
31
+ export function detectWorkspace(root, clock = systemClock) {
32
+ const detectedAt = clock.now();
33
+ const info = {
34
+ root,
35
+ exists: existsSync(root),
36
+ git: { isRepo: false },
37
+ languages: [],
38
+ projectFiles: [],
39
+ detectedAt,
40
+ };
41
+ if (!info.exists)
42
+ return info;
43
+ // Language markers + recorded project files.
44
+ const found = new Set();
45
+ for (const [marker, language] of LANGUAGE_MARKERS) {
46
+ if (existsSync(join(root, marker))) {
47
+ found.add(marker);
48
+ if (!info.languages.includes(language))
49
+ info.languages.push(language);
50
+ }
51
+ }
52
+ // Package manager (first lockfile/marker hit).
53
+ for (const [marker, pm] of LOCKFILE_TO_PM) {
54
+ if (existsSync(join(root, marker))) {
55
+ found.add(marker);
56
+ info.packageManager = pm;
57
+ break;
58
+ }
59
+ }
60
+ // package.json: name, framework, test framework, node:test inference.
61
+ const pkg = readJsonSafe(join(root, 'package.json'));
62
+ if (pkg) {
63
+ found.add('package.json');
64
+ if (typeof pkg.name === 'string')
65
+ info.name = pkg.name;
66
+ const deps = {
67
+ ...pkg.dependencies,
68
+ ...pkg.devDependencies,
69
+ };
70
+ const depNames = new Set(Object.keys(deps));
71
+ for (const [dep, framework] of DEP_TO_FRAMEWORK) {
72
+ if (depNames.has(dep)) {
73
+ info.framework = framework;
74
+ break;
75
+ }
76
+ }
77
+ for (const [dep, tf] of DEP_TO_TEST_FRAMEWORK) {
78
+ if (depNames.has(dep)) {
79
+ info.testFramework = tf;
80
+ break;
81
+ }
82
+ }
83
+ if (!info.testFramework) {
84
+ const testScript = pkg.scripts?.test;
85
+ if (typeof testScript === 'string' && /(^|\s)--test(\s|$)/.test(testScript))
86
+ info.testFramework = 'node:test';
87
+ }
88
+ }
89
+ if (!info.name)
90
+ info.name = basename(root);
91
+ // Git (parse .git/HEAD; no child process).
92
+ const head = readTextSafe(join(root, '.git', 'HEAD'));
93
+ if (head !== undefined) {
94
+ info.git = { isRepo: true, branch: branchFromHead(head) };
95
+ }
96
+ info.projectFiles = [...found].sort();
97
+ return info;
98
+ }
99
+ /** One-line, provider-safe summary: trait names + basename only — never paths (beyond basename) or contents. */
100
+ export function summarizeWorkspace(info) {
101
+ if (!info.exists)
102
+ return 'Workspace: (not found).';
103
+ const name = info.name ?? basename(info.root);
104
+ const bits = [];
105
+ if (info.languages.length)
106
+ bits.push(info.languages.join('/') + (info.packageManager ? ` (${info.packageManager})` : ''));
107
+ else if (info.packageManager)
108
+ bits.push(info.packageManager);
109
+ if (info.framework)
110
+ bits.push(`framework: ${info.framework}`);
111
+ if (info.testFramework)
112
+ bits.push(`tests: ${info.testFramework}`);
113
+ if (info.git.isRepo && info.git.branch)
114
+ bits.push(`git: ${info.git.branch}`);
115
+ return `Workspace "${name}"${bits.length ? ': ' + bits.join(', ') : ''}.`;
116
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Credential resolution. Config carries the NAME of an env var; this reads the value at construction,
3
+ * registers it with the redactor, and holds it in a private (`#`) field so it is non-enumerable and
4
+ * never serialized. `use()` is the only accessor and is called solely by the HTTP layer to build the
5
+ * auth header. `toJSON()` deliberately omits the key, so logging or returning a Credential is safe.
6
+ */
7
+ /**
8
+ * Enterprise credential management: a pluggable resolver lets keys come from an OS keychain,
9
+ * a cloud secret manager, or a CI secret store instead of the process environment. It receives only
10
+ * the env-var NAME and returns the value; the resolved value is still held non-enumerably and redacted.
11
+ */
12
+ export type CredentialResolver = (envName: string) => string | undefined;
13
+ /** Install a custom credential resolver (e.g. a secret-manager lookup). Pass undefined to clear. */
14
+ export declare function setCredentialResolver(resolver: CredentialResolver | undefined): void;
15
+ export declare class Credential {
16
+ #private;
17
+ readonly envName?: string;
18
+ readonly hasKey: boolean;
19
+ constructor(envName?: string, env?: NodeJS.ProcessEnv);
20
+ /** The resolved key value — call site is the HTTP header builder only. Returns undefined when unset. */
21
+ use(): string | undefined;
22
+ toJSON(): {
23
+ envName?: string;
24
+ hasKey: boolean;
25
+ };
26
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Credential resolution. Config carries the NAME of an env var; this reads the value at construction,
3
+ * registers it with the redactor, and holds it in a private (`#`) field so it is non-enumerable and
4
+ * never serialized. `use()` is the only accessor and is called solely by the HTTP layer to build the
5
+ * auth header. `toJSON()` deliberately omits the key, so logging or returning a Credential is safe.
6
+ */
7
+ import { registerSecret } from './redact.js';
8
+ let customResolver;
9
+ /** Install a custom credential resolver (e.g. a secret-manager lookup). Pass undefined to clear. */
10
+ export function setCredentialResolver(resolver) {
11
+ customResolver = resolver;
12
+ }
13
+ export class Credential {
14
+ envName;
15
+ hasKey;
16
+ #key;
17
+ constructor(envName, env = process.env) {
18
+ if (envName !== undefined)
19
+ this.envName = envName;
20
+ // Precedence: explicit env (process/test) first, then a custom resolver (secret manager).
21
+ const value = envName ? (env[envName] ?? customResolver?.(envName)) : undefined;
22
+ this.#key = value;
23
+ this.hasKey = Boolean(value);
24
+ if (value)
25
+ registerSecret(value);
26
+ }
27
+ /** The resolved key value — call site is the HTTP header builder only. Returns undefined when unset. */
28
+ use() {
29
+ return this.#key;
30
+ }
31
+ toJSON() {
32
+ return { ...(this.envName !== undefined ? { envName: this.envName } : {}), hasKey: this.hasKey };
33
+ }
34
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Reference-based secret redaction, applied at every egress (logs, telemetry, errors, CLI output).
3
+ *
4
+ * Shape-only redaction (matching `sk-`/`Bearer` patterns) is fail-open for unknown key formats, so
5
+ * the primary defense here is REFERENCE-based: the credential layer registers each resolved key VALUE
6
+ * via `registerSecret`, and we scrub those exact values wherever they appear. Shape-based patterns
7
+ * remain only as defense-in-depth. This runs BEFORE any truncation so a secret can never survive at a
8
+ * truncation boundary.
9
+ */
10
+ /** Register a resolved key value so it is scrubbed from every egress. Short values are ignored to avoid over-redaction. */
11
+ export declare function registerSecret(value: string | null | undefined): void;
12
+ /** Test/utility hook. */
13
+ export declare function clearSecrets(): void;
14
+ export declare function redactString(input: string): string;
15
+ /** Deep-redact any value, returning a scrubbed clone. Non-plain objects are stringified defensively. */
16
+ export declare function redact<T>(value: T): T;
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Reference-based secret redaction, applied at every egress (logs, telemetry, errors, CLI output).
3
+ *
4
+ * Shape-only redaction (matching `sk-`/`Bearer` patterns) is fail-open for unknown key formats, so
5
+ * the primary defense here is REFERENCE-based: the credential layer registers each resolved key VALUE
6
+ * via `registerSecret`, and we scrub those exact values wherever they appear. Shape-based patterns
7
+ * remain only as defense-in-depth. This runs BEFORE any truncation so a secret can never survive at a
8
+ * truncation boundary.
9
+ */
10
+ const secretValues = new Set();
11
+ /** Register a resolved key value so it is scrubbed from every egress. Short values are ignored to avoid over-redaction. */
12
+ export function registerSecret(value) {
13
+ if (value && value.length >= 8)
14
+ secretValues.add(value);
15
+ }
16
+ /** Test/utility hook. */
17
+ export function clearSecrets() {
18
+ secretValues.clear();
19
+ }
20
+ const SHAPE_PATTERNS = [
21
+ [/(authorization"?\s*[:=]\s*"?bearer\s+)[^\s"',}]+/gi, '$1[redacted]'],
22
+ [/(x-api-key"?\s*[:=]\s*"?)[^\s"',}]+/gi, '$1[redacted]'],
23
+ [/([?&](?:key|api_key|access_token)=)[^&\s"']+/gi, '$1[redacted]'],
24
+ [/\b(sk-[a-z]*-?)[A-Za-z0-9_-]{6,}\b/gi, '$1[redacted]'],
25
+ [/\b(gsk_)[A-Za-z0-9_-]{6,}\b/g, '$1[redacted]'],
26
+ ];
27
+ export function redactString(input) {
28
+ let out = input;
29
+ for (const secret of secretValues) {
30
+ if (secret)
31
+ out = out.split(secret).join('[redacted]');
32
+ }
33
+ for (const [pattern, replacement] of SHAPE_PATTERNS) {
34
+ out = out.replace(pattern, replacement);
35
+ }
36
+ return out;
37
+ }
38
+ /** Deep-redact any value, returning a scrubbed clone. Non-plain objects are stringified defensively. */
39
+ export function redact(value) {
40
+ return redactValue(value, new WeakSet());
41
+ }
42
+ function redactValue(value, seen) {
43
+ if (typeof value === 'string')
44
+ return redactString(value);
45
+ if (value === null || typeof value !== 'object')
46
+ return value;
47
+ if (seen.has(value))
48
+ return '[circular]';
49
+ seen.add(value);
50
+ if (Array.isArray(value))
51
+ return value.map((v) => redactValue(v, seen));
52
+ const out = {};
53
+ for (const [k, v] of Object.entries(value)) {
54
+ out[k] = redactValue(v, seen);
55
+ }
56
+ return out;
57
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * file-analyzer — a generic skill: read a file (via the jailed filesystem tool), then ask a model to
3
+ * analyze it. Validates that a file was actually read and a non-empty analysis was produced (evidence,
4
+ * not a model claim). QA-neutral.
5
+ */
6
+ import type { Skill } from '../skill.js';
7
+ export declare const fileAnalyzerSkill: Skill;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * file-analyzer — a generic skill: read a file (via the jailed filesystem tool), then ask a model to
3
+ * analyze it. Validates that a file was actually read and a non-empty analysis was produced (evidence,
4
+ * not a model claim). QA-neutral.
5
+ */
6
+ import { wrapUntrusted } from '../../tools/untrusted.js';
7
+ export const fileAnalyzerSkill = {
8
+ id: 'file-analyzer',
9
+ version: '1.0.0',
10
+ runtimeCompatibility: '>=0.1.0',
11
+ name: 'File analyzer',
12
+ description: 'Read a file within the workspace and analyze it.',
13
+ capabilities: [
14
+ { group: 'intelligence', key: 'reasoning' },
15
+ { group: 'output', key: 'text' },
16
+ ],
17
+ tools: ['filesystem'],
18
+ async plan() {
19
+ return { steps: [{ description: 'read the file', tool: 'filesystem' }, { description: 'analyze its contents' }] };
20
+ },
21
+ async execute(ctx) {
22
+ const { path, question } = (ctx.input ?? {});
23
+ if (!path)
24
+ return { ok: false, error: { message: 'file-analyzer requires { path }' } };
25
+ const read = await ctx.callTool('filesystem', { op: 'read', path });
26
+ if (!read.ok)
27
+ return { ok: false, error: { message: `could not read ${path}: ${read.error?.message ?? 'unknown'}` } };
28
+ const content = String(read.output ?? '');
29
+ // File content is UNTRUSTED data — fence it so its text can't act as instructions.
30
+ const prompt = `${question ? question + '\n\n' : 'Analyze the following file and summarize its purpose and structure.\n\n'}File: ${path}\n${wrapUntrusted(path, content)}`;
31
+ const run = await ctx.ai.run({ task: 'file-analyzer', input: prompt, requirements: { intelligence: ['reasoning'], output: ['text'] } });
32
+ if (!run.ok)
33
+ return { ok: false, error: { message: `analysis failed: ${run.error?.category ?? 'unknown'}` } };
34
+ return { ok: true, output: run.response?.text ?? '', data: { path, bytes: content.length } };
35
+ },
36
+ async validate(result) {
37
+ const readOk = !!result.data?.bytes;
38
+ const analyzed = !!result.output && result.output.trim().length > 0;
39
+ return {
40
+ ok: readOk && analyzed,
41
+ checks: [
42
+ { name: 'file-read', ok: readOk, ...(readOk ? {} : { why: 'no file content was read' }) },
43
+ { name: 'analysis-produced', ok: analyzed, ...(analyzed ? {} : { why: 'the model returned no analysis' }) },
44
+ ],
45
+ };
46
+ },
47
+ };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * repository-analyzer — a generic skill: list the workspace root (via the jailed filesystem tool) and
3
+ * ask a model to characterize the project. Validates that entries were listed and a summary produced.
4
+ */
5
+ import type { Skill } from '../skill.js';
6
+ export declare const repositoryAnalyzerSkill: Skill;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * repository-analyzer — a generic skill: list the workspace root (via the jailed filesystem tool) and
3
+ * ask a model to characterize the project. Validates that entries were listed and a summary produced.
4
+ */
5
+ import { wrapUntrusted } from '../../tools/untrusted.js';
6
+ export const repositoryAnalyzerSkill = {
7
+ id: 'repository-analyzer',
8
+ version: '1.0.0',
9
+ runtimeCompatibility: '>=0.1.0',
10
+ name: 'Repository analyzer',
11
+ description: 'Survey the workspace and summarize the project at a high level.',
12
+ capabilities: [
13
+ { group: 'intelligence', key: 'reasoning' },
14
+ { group: 'intelligence', key: 'summarization' },
15
+ { group: 'output', key: 'text' },
16
+ ],
17
+ tools: ['filesystem'],
18
+ async plan() {
19
+ return { steps: [{ description: 'list the workspace root', tool: 'filesystem' }, { description: 'summarize the project' }] };
20
+ },
21
+ async execute(ctx) {
22
+ const list = await ctx.callTool('filesystem', { op: 'list', path: '.' });
23
+ if (!list.ok)
24
+ return { ok: false, error: { message: `could not list the workspace: ${list.error?.message ?? 'unknown'}` } };
25
+ const entries = list.data ?? [];
26
+ // Directory entries are UNTRUSTED (attacker-chosen filenames) — fence them.
27
+ const run = await ctx.ai.run({
28
+ task: 'repository-analyzer',
29
+ input: `${wrapUntrusted('workspace-listing', entries.join('\n'))}\n\nBriefly characterize this project (language, purpose, structure) from the entries above.`,
30
+ requirements: { intelligence: ['reasoning', 'summarization'], output: ['text'] },
31
+ });
32
+ if (!run.ok)
33
+ return { ok: false, error: { message: `summary failed: ${run.error?.category ?? 'unknown'}` } };
34
+ return { ok: true, output: run.response?.text ?? '', data: { entryCount: entries.length } };
35
+ },
36
+ async validate(result) {
37
+ const listed = (result.data?.entryCount ?? 0) > 0;
38
+ const summarized = !!result.output && result.output.trim().length > 0;
39
+ return {
40
+ ok: listed && summarized,
41
+ checks: [
42
+ { name: 'workspace-listed', ok: listed, ...(listed ? {} : { why: 'no entries were listed' }) },
43
+ { name: 'summary-produced', ok: summarized, ...(summarized ? {} : { why: 'the model returned no summary' }) },
44
+ ],
45
+ };
46
+ },
47
+ };