@garygentry/system1-core 0.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 (159) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +13 -0
  3. package/dist/config/consent.d.ts +11 -0
  4. package/dist/config/consent.d.ts.map +1 -0
  5. package/dist/config/consent.js +33 -0
  6. package/dist/config/consent.js.map +1 -0
  7. package/dist/config/load.d.ts +64 -0
  8. package/dist/config/load.d.ts.map +1 -0
  9. package/dist/config/load.js +216 -0
  10. package/dist/config/load.js.map +1 -0
  11. package/dist/config/session.d.ts +31 -0
  12. package/dist/config/session.d.ts.map +1 -0
  13. package/dist/config/session.js +36 -0
  14. package/dist/config/session.js.map +1 -0
  15. package/dist/config.d.ts +28 -0
  16. package/dist/config.d.ts.map +1 -0
  17. package/dist/config.js +64 -0
  18. package/dist/config.js.map +1 -0
  19. package/dist/decide.d.ts +57 -0
  20. package/dist/decide.d.ts.map +1 -0
  21. package/dist/decide.js +87 -0
  22. package/dist/decide.js.map +1 -0
  23. package/dist/egress/exclude.d.ts +24 -0
  24. package/dist/egress/exclude.d.ts.map +1 -0
  25. package/dist/egress/exclude.js +60 -0
  26. package/dist/egress/exclude.js.map +1 -0
  27. package/dist/egress/scrub.d.ts +12 -0
  28. package/dist/egress/scrub.d.ts.map +1 -0
  29. package/dist/egress/scrub.js +84 -0
  30. package/dist/egress/scrub.js.map +1 -0
  31. package/dist/egress/size.d.ts +16 -0
  32. package/dist/egress/size.d.ts.map +1 -0
  33. package/dist/egress/size.js +25 -0
  34. package/dist/egress/size.js.map +1 -0
  35. package/dist/errors.d.ts +44 -0
  36. package/dist/errors.d.ts.map +1 -0
  37. package/dist/errors.js +23 -0
  38. package/dist/errors.js.map +1 -0
  39. package/dist/fixtures/store.d.ts +34 -0
  40. package/dist/fixtures/store.d.ts.map +1 -0
  41. package/dist/fixtures/store.js +74 -0
  42. package/dist/fixtures/store.js.map +1 -0
  43. package/dist/index.d.ts +59 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +33 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/model/answers.d.ts +38 -0
  48. package/dist/model/answers.d.ts.map +1 -0
  49. package/dist/model/answers.js +48 -0
  50. package/dist/model/answers.js.map +1 -0
  51. package/dist/model/profiles.d.ts +39 -0
  52. package/dist/model/profiles.d.ts.map +1 -0
  53. package/dist/model/profiles.js +42 -0
  54. package/dist/model/profiles.js.map +1 -0
  55. package/dist/model/types.d.ts +107 -0
  56. package/dist/model/types.d.ts.map +1 -0
  57. package/dist/model/types.js +17 -0
  58. package/dist/model/types.js.map +1 -0
  59. package/dist/model/validate.d.ts +17 -0
  60. package/dist/model/validate.d.ts.map +1 -0
  61. package/dist/model/validate.js +151 -0
  62. package/dist/model/validate.js.map +1 -0
  63. package/dist/ping.d.ts +30 -0
  64. package/dist/ping.d.ts.map +1 -0
  65. package/dist/ping.js +50 -0
  66. package/dist/ping.js.map +1 -0
  67. package/dist/prepare.d.ts +39 -0
  68. package/dist/prepare.d.ts.map +1 -0
  69. package/dist/prepare.js +84 -0
  70. package/dist/prepare.js.map +1 -0
  71. package/dist/project/project.d.ts +64 -0
  72. package/dist/project/project.d.ts.map +1 -0
  73. package/dist/project/project.js +144 -0
  74. package/dist/project/project.js.map +1 -0
  75. package/dist/run/budget.d.ts +20 -0
  76. package/dist/run/budget.d.ts.map +1 -0
  77. package/dist/run/budget.js +31 -0
  78. package/dist/run/budget.js.map +1 -0
  79. package/dist/run/pool.d.ts +16 -0
  80. package/dist/run/pool.d.ts.map +1 -0
  81. package/dist/run/pool.js +35 -0
  82. package/dist/run/pool.js.map +1 -0
  83. package/dist/run/spend.d.ts +34 -0
  84. package/dist/run/spend.d.ts.map +1 -0
  85. package/dist/run/spend.js +55 -0
  86. package/dist/run/spend.js.map +1 -0
  87. package/dist/sources/read.d.ts +40 -0
  88. package/dist/sources/read.d.ts.map +1 -0
  89. package/dist/sources/read.js +294 -0
  90. package/dist/sources/read.js.map +1 -0
  91. package/dist/sources/types.d.ts +79 -0
  92. package/dist/sources/types.d.ts.map +1 -0
  93. package/dist/sources/types.js +2 -0
  94. package/dist/sources/types.js.map +1 -0
  95. package/dist/spec/expect.d.ts +39 -0
  96. package/dist/spec/expect.d.ts.map +1 -0
  97. package/dist/spec/expect.js +95 -0
  98. package/dist/spec/expect.js.map +1 -0
  99. package/dist/spec/spec.d.ts +90 -0
  100. package/dist/spec/spec.d.ts.map +1 -0
  101. package/dist/spec/spec.js +252 -0
  102. package/dist/spec/spec.js.map +1 -0
  103. package/dist/split/split.d.ts +33 -0
  104. package/dist/split/split.d.ts.map +1 -0
  105. package/dist/split/split.js +116 -0
  106. package/dist/split/split.js.map +1 -0
  107. package/dist/testdata/index.d.ts +5 -0
  108. package/dist/testdata/index.d.ts.map +1 -0
  109. package/dist/testdata/index.js +6 -0
  110. package/dist/testdata/index.js.map +1 -0
  111. package/dist/testkit/fake-model.d.ts +14 -0
  112. package/dist/testkit/fake-model.d.ts.map +1 -0
  113. package/dist/testkit/fake-model.js +57 -0
  114. package/dist/testkit/fake-model.js.map +1 -0
  115. package/dist/testkit/tmp.d.ts +6 -0
  116. package/dist/testkit/tmp.d.ts.map +1 -0
  117. package/dist/testkit/tmp.js +46 -0
  118. package/dist/testkit/tmp.js.map +1 -0
  119. package/dist/tools/ask.d.ts +32 -0
  120. package/dist/tools/ask.d.ts.map +1 -0
  121. package/dist/tools/ask.js +59 -0
  122. package/dist/tools/ask.js.map +1 -0
  123. package/dist/tools/context.d.ts +52 -0
  124. package/dist/tools/context.d.ts.map +1 -0
  125. package/dist/tools/context.js +130 -0
  126. package/dist/tools/context.js.map +1 -0
  127. package/dist/tools/doctor.d.ts +37 -0
  128. package/dist/tools/doctor.d.ts.map +1 -0
  129. package/dist/tools/doctor.js +203 -0
  130. package/dist/tools/doctor.js.map +1 -0
  131. package/dist/tools/many.d.ts +74 -0
  132. package/dist/tools/many.d.ts.map +1 -0
  133. package/dist/tools/many.js +141 -0
  134. package/dist/tools/many.js.map +1 -0
  135. package/dist/tools/schemas.d.ts +293 -0
  136. package/dist/tools/schemas.d.ts.map +1 -0
  137. package/dist/tools/schemas.js +112 -0
  138. package/dist/tools/schemas.js.map +1 -0
  139. package/dist/tools/spec-check.d.ts +48 -0
  140. package/dist/tools/spec-check.d.ts.map +1 -0
  141. package/dist/tools/spec-check.js +164 -0
  142. package/dist/tools/spec-check.js.map +1 -0
  143. package/dist/tools/usage.d.ts +11 -0
  144. package/dist/tools/usage.d.ts.map +1 -0
  145. package/dist/tools/usage.js +27 -0
  146. package/dist/tools/usage.js.map +1 -0
  147. package/dist/transport/openrouter.d.ts +38 -0
  148. package/dist/transport/openrouter.d.ts.map +1 -0
  149. package/dist/transport/openrouter.js +89 -0
  150. package/dist/transport/openrouter.js.map +1 -0
  151. package/dist/version.d.ts +4 -0
  152. package/dist/version.d.ts.map +1 -0
  153. package/dist/version.js +5 -0
  154. package/dist/version.js.map +1 -0
  155. package/dist/wiring.d.ts +30 -0
  156. package/dist/wiring.d.ts.map +1 -0
  157. package/dist/wiring.js +31 -0
  158. package/dist/wiring.js.map +1 -0
  159. package/package.json +63 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Gary Gentry
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,13 @@
1
+ # @garygentry/system1-core
2
+
3
+ The engine behind the [`decide`](https://www.npmjs.com/package/@garygentry/system1) CLI, as a library: model profiles, transport, sources and splitters, egress excludes and scrubbing, fixtures for replay, the spend ledger, projection, and the spec format.
4
+
5
+ ```ts
6
+ import { createContext, runMany } from "@garygentry/system1-core"
7
+ ```
8
+
9
+ The CLI bundles its own copy, so install this only to build on the engine directly.
10
+
11
+ Documentation: **https://github.com/garygentry/system1**
12
+
13
+ MIT
@@ -0,0 +1,11 @@
1
+ import { type Consent } from "./load.js";
2
+ /**
3
+ * Record (or revoke) egress consent in `<repo>/.system1/config.yaml`,
4
+ * preserving whatever else the file holds, comments included.
5
+ */
6
+ export declare function setConsent(repoRoot: string, granted: boolean, by?: string, now?: Date): Consent;
7
+ /**
8
+ * @throws DecisionsError `egress-refused` when this repo has not consented.
9
+ */
10
+ export declare function assertConsent(consent: Consent, repoRoot: string): void;
11
+ //# sourceMappingURL=consent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consent.d.ts","sourceRoot":"","sources":["../../src/config/consent.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,OAAO,EAAkB,MAAM,WAAW,CAAA;AAOxD;;;GAGG;AACH,wBAAgB,UAAU,CACxB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,OAAO,EAChB,EAAE,CAAC,EAAE,MAAM,EACX,GAAG,OAAa,GACf,OAAO,CAQT;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAStE"}
@@ -0,0 +1,33 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { dirname } from "node:path";
3
+ import { parseDocument } from "yaml";
4
+ import { DecisionsError } from "../errors.js";
5
+ import { repoConfigPath } from "./load.js";
6
+ const HEADER = `# decisions — per-repo configuration. See https://github.com/garygentry/system1
7
+ # egress.consent records that this repo agreed to send content to the decision
8
+ # model's provider (OpenRouter). Path excludes and secret scrubbing always apply.
9
+ `;
10
+ /**
11
+ * Record (or revoke) egress consent in `<repo>/.system1/config.yaml`,
12
+ * preserving whatever else the file holds, comments included.
13
+ */
14
+ export function setConsent(repoRoot, granted, by, now = new Date()) {
15
+ const file = repoConfigPath(repoRoot);
16
+ const doc = existsSync(file) ? parseDocument(readFileSync(file, "utf8")) : parseDocument(HEADER);
17
+ const consent = { granted, at: now.toISOString(), ...(by ? { by } : {}) };
18
+ doc.setIn(["egress", "consent"], consent);
19
+ mkdirSync(dirname(file), { recursive: true });
20
+ writeFileSync(file, doc.toString());
21
+ return consent;
22
+ }
23
+ /**
24
+ * @throws DecisionsError `egress-refused` when this repo has not consented.
25
+ */
26
+ export function assertConsent(consent, repoRoot) {
27
+ if (consent.granted)
28
+ return;
29
+ throw new DecisionsError("egress-refused", `This repo has not agreed to send content to the decision model's provider. ` +
30
+ `Run \`decide config egress allow\` in ${repoRoot} (or use the setup skill) to consent. ` +
31
+ `Replay of recorded answers works without consent.`, { repoRoot });
32
+ }
33
+ //# sourceMappingURL=consent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consent.js","sourceRoot":"","sources":["../../src/config/consent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,MAAM,CAAA;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAgB,cAAc,EAAE,MAAM,WAAW,CAAA;AAExD,MAAM,MAAM,GAAG;;;CAGd,CAAA;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CACxB,QAAgB,EAChB,OAAgB,EAChB,EAAW,EACX,GAAG,GAAG,IAAI,IAAI,EAAE;IAEhB,MAAM,IAAI,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAA;IACrC,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,CAAA;IAChG,MAAM,OAAO,GAAY,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,WAAW,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAA;IAClF,GAAG,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,OAAO,CAAC,CAAA;IACzC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAC7C,aAAa,CAAC,IAAI,EAAE,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAA;IACnC,OAAO,OAAO,CAAA;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,OAAgB,EAAE,QAAgB;IAC9D,IAAI,OAAO,CAAC,OAAO;QAAE,OAAM;IAC3B,MAAM,IAAI,cAAc,CACtB,gBAAgB,EAChB,6EAA6E;QAC3E,yCAAyC,QAAQ,wCAAwC;QACzF,mDAAmD,EACrD,EAAE,QAAQ,EAAE,CACb,CAAA;AACH,CAAC"}
@@ -0,0 +1,64 @@
1
+ import { type ModelProfile } from "../model/profiles.js";
2
+ import { type DetectedSession } from "./session.js";
3
+ export interface Consent {
4
+ granted: boolean;
5
+ /** ISO timestamp of the grant. */
6
+ at?: string;
7
+ /** Free text, e.g. who granted it or through which harness. */
8
+ by?: string;
9
+ }
10
+ export interface Budget {
11
+ /** Above this many calls, a request needs explicit confirmation. */
12
+ maxCalls: number;
13
+ /** Above this projected USD, a request needs explicit confirmation. */
14
+ maxUsd: number;
15
+ }
16
+ export interface DecisionsConfig {
17
+ model: string;
18
+ endpoint: string;
19
+ /** Server-side cap on simultaneous calls in a fan-out. */
20
+ concurrency: number;
21
+ /** Per-attempt request timeout. */
22
+ timeoutMs: number;
23
+ budget: Budget;
24
+ egress: {
25
+ /** Only ever taken from the repo layer. */
26
+ consent: Consent;
27
+ /** Extra path patterns never to send, on top of the defaults. */
28
+ exclude: string[];
29
+ };
30
+ /** Extra model profiles, on top of the built-in ones. */
31
+ profiles: ModelProfile[];
32
+ }
33
+ export interface ResolvedConfig extends DecisionsConfig {
34
+ repoRoot: string;
35
+ /** Absent when no key is configured. Never printed. */
36
+ apiKey: string | undefined;
37
+ apiKeySource: "env" | "credentials" | undefined;
38
+ replay: boolean;
39
+ /** `SYSTEM1_SESSION`, else the harness session id (`claude:…`, `codex:…`, `pi:…`). */
40
+ session: string | undefined;
41
+ sessionOrigin: DetectedSession["origin"] | undefined;
42
+ /** Which files contributed, for `decide config`. */
43
+ layers: {
44
+ user?: string;
45
+ repo?: string;
46
+ credentials?: string;
47
+ };
48
+ }
49
+ export declare const DEFAULTS: DecisionsConfig;
50
+ /** Walk up from `cwd` to the nearest directory holding `.system1/` or `.git`; else `cwd`. */
51
+ export declare function findRepoRoot(cwd: string): string;
52
+ export declare function userConfigDir(env?: NodeJS.ProcessEnv, home?: string): string;
53
+ /** Where a repo keeps its decisions state: config, fixtures, the spend ledger. */
54
+ export declare function stateDir(repoRoot: string): string;
55
+ export declare function repoConfigPath(repoRoot: string): string;
56
+ export interface LoadOptions {
57
+ cwd?: string;
58
+ env?: NodeJS.ProcessEnv;
59
+ home?: string;
60
+ }
61
+ export declare function loadConfig(options?: LoadOptions): ResolvedConfig;
62
+ /** All profiles the config knows: built-ins first, config additions after. */
63
+ export declare function allProfiles(config: Pick<DecisionsConfig, "profiles">): ModelProfile[];
64
+ //# sourceMappingURL=load.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load.d.ts","sourceRoot":"","sources":["../../src/config/load.ts"],"names":[],"mappings":"AAeA,OAAO,EAAoB,KAAK,YAAY,EAAY,MAAM,sBAAsB,CAAA;AAEpF,OAAO,EAAE,KAAK,eAAe,EAAiB,MAAM,cAAc,CAAA;AAElE,MAAM,WAAW,OAAO;IACtB,OAAO,EAAE,OAAO,CAAA;IAChB,kCAAkC;IAClC,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,+DAA+D;IAC/D,EAAE,CAAC,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,MAAM;IACrB,oEAAoE;IACpE,QAAQ,EAAE,MAAM,CAAA;IAChB,uEAAuE;IACvE,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,0DAA0D;IAC1D,WAAW,EAAE,MAAM,CAAA;IACnB,mCAAmC;IACnC,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE;QACN,2CAA2C;QAC3C,OAAO,EAAE,OAAO,CAAA;QAChB,iEAAiE;QACjE,OAAO,EAAE,MAAM,EAAE,CAAA;KAClB,CAAA;IACD,yDAAyD;IACzD,QAAQ,EAAE,YAAY,EAAE,CAAA;CACzB;AAED,MAAM,WAAW,cAAe,SAAQ,eAAe;IACrD,QAAQ,EAAE,MAAM,CAAA;IAChB,uDAAuD;IACvD,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,YAAY,EAAE,KAAK,GAAG,aAAa,GAAG,SAAS,CAAA;IAC/C,MAAM,EAAE,OAAO,CAAA;IACf,sFAAsF;IACtF,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B,aAAa,EAAE,eAAe,CAAC,QAAQ,CAAC,GAAG,SAAS,CAAA;IACpD,oDAAoD;IACpD,MAAM,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAC/D;AAED,eAAO,MAAM,QAAQ,EAAE,eAQtB,CAAA;AAED,6FAA6F;AAC7F,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAQhD;AAED,wBAAgB,aAAa,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,EAAE,IAAI,SAAY,GAAG,MAAM,CAG5F;AAED,kFAAkF;AAClF,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;IACvB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,wBAAgB,UAAU,CAAC,OAAO,GAAE,WAAgB,GAAG,cAAc,CA+CpE;AAED,8EAA8E;AAC9E,wBAAgB,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,eAAe,EAAE,UAAU,CAAC,GAAG,YAAY,EAAE,CAErF"}
@@ -0,0 +1,216 @@
1
+ /**
2
+ * Layered configuration:
3
+ *
4
+ * defaults → user (`$XDG_CONFIG_HOME/decisions/config.yaml`)
5
+ * → repo (`<repo>/.system1/config.yaml`) → env
6
+ *
7
+ * Later layers win. There is one exception: egress consent is read **only**
8
+ * from the repo layer. Consent is per repo (decision 0009), so a user-level
9
+ * grant is ignored rather than silently covering every repo.
10
+ */
11
+ import { existsSync, readFileSync, statSync } from "node:fs";
12
+ import { homedir } from "node:os";
13
+ import { dirname, join, resolve } from "node:path";
14
+ import { parse } from "yaml";
15
+ import { DecisionsError } from "../errors.js";
16
+ import { DEFAULT_MODEL_ID, PROFILES } from "../model/profiles.js";
17
+ import { DEFAULT_ENDPOINT, DEFAULT_TIMEOUT_MS } from "../transport/openrouter.js";
18
+ import { detectSession } from "./session.js";
19
+ export const DEFAULTS = {
20
+ model: DEFAULT_MODEL_ID,
21
+ endpoint: DEFAULT_ENDPOINT,
22
+ concurrency: 8,
23
+ timeoutMs: DEFAULT_TIMEOUT_MS,
24
+ budget: { maxCalls: 200, maxUsd: 0.05 },
25
+ egress: { consent: { granted: false }, exclude: [] },
26
+ profiles: [],
27
+ };
28
+ /** Walk up from `cwd` to the nearest directory holding `.system1/` or `.git`; else `cwd`. */
29
+ export function findRepoRoot(cwd) {
30
+ let dir = resolve(cwd);
31
+ while (true) {
32
+ if (existsSync(join(dir, ".system1")) || existsSync(join(dir, ".git")))
33
+ return dir;
34
+ const parent = dirname(dir);
35
+ if (parent === dir)
36
+ return resolve(cwd);
37
+ dir = parent;
38
+ }
39
+ }
40
+ export function userConfigDir(env = process.env, home = homedir()) {
41
+ const base = env.XDG_CONFIG_HOME?.trim() ? env.XDG_CONFIG_HOME : join(home, ".config");
42
+ return join(base, "system1");
43
+ }
44
+ /** Where a repo keeps its decisions state: config, fixtures, the spend ledger. */
45
+ export function stateDir(repoRoot) {
46
+ return join(repoRoot, ".system1");
47
+ }
48
+ export function repoConfigPath(repoRoot) {
49
+ return join(repoRoot, ".system1", "config.yaml");
50
+ }
51
+ export function loadConfig(options = {}) {
52
+ const env = options.env ?? process.env;
53
+ const repoRoot = findRepoRoot(options.cwd ?? process.cwd());
54
+ const userDir = userConfigDir(env, options.home);
55
+ const userFile = join(userDir, "config.yaml");
56
+ const repoFile = repoConfigPath(repoRoot);
57
+ const user = readLayer(userFile);
58
+ const repo = readLayer(repoFile);
59
+ const merged = {
60
+ model: pick("model", "string", repo, user) ?? DEFAULTS.model,
61
+ endpoint: pick("endpoint", "string", repo, user) ?? DEFAULTS.endpoint,
62
+ concurrency: pick("concurrency", "number", repo, user) ?? DEFAULTS.concurrency,
63
+ timeoutMs: pick("timeoutMs", "number", repo, user) ?? DEFAULTS.timeoutMs,
64
+ budget: {
65
+ maxCalls: pickIn("budget", "maxCalls", repo, user) ?? DEFAULTS.budget.maxCalls,
66
+ maxUsd: pickIn("budget", "maxUsd", repo, user) ?? DEFAULTS.budget.maxUsd,
67
+ },
68
+ egress: {
69
+ consent: readConsent(repo, repoFile),
70
+ exclude: [
71
+ ...stringList(user, "egress.exclude", userFile),
72
+ ...stringList(repo, "egress.exclude", repoFile),
73
+ ],
74
+ },
75
+ profiles: [...profileList(user, userFile), ...profileList(repo, repoFile)],
76
+ };
77
+ const key = resolveApiKey(env, userDir);
78
+ const session = detectSession(env);
79
+ return {
80
+ ...merged,
81
+ model: nonEmpty(env.SYSTEM1_MODEL) ?? merged.model,
82
+ endpoint: nonEmpty(env.SYSTEM1_ENDPOINT) ?? merged.endpoint,
83
+ repoRoot,
84
+ apiKey: key?.value,
85
+ apiKeySource: key?.source,
86
+ replay: /^(1|true|yes)$/i.test(env.SYSTEM1_REPLAY ?? ""),
87
+ session: session?.id,
88
+ sessionOrigin: session?.origin,
89
+ layers: {
90
+ ...(user ? { user: userFile } : {}),
91
+ ...(repo ? { repo: repoFile } : {}),
92
+ ...(key?.file ? { credentials: key.file } : {}),
93
+ },
94
+ };
95
+ }
96
+ /** All profiles the config knows: built-ins first, config additions after. */
97
+ export function allProfiles(config) {
98
+ return [...PROFILES, ...config.profiles];
99
+ }
100
+ function readLayer(file) {
101
+ if (!existsSync(file))
102
+ return undefined;
103
+ let parsed;
104
+ try {
105
+ parsed = parse(readFileSync(file, "utf8"));
106
+ }
107
+ catch (error) {
108
+ throw new DecisionsError("config-error", `${file} is not valid YAML: ${String(error)}`, {
109
+ file,
110
+ });
111
+ }
112
+ if (parsed === null || parsed === undefined)
113
+ return {};
114
+ if (typeof parsed !== "object" || Array.isArray(parsed)) {
115
+ throw new DecisionsError("config-error", `${file} must be a YAML mapping`, { file });
116
+ }
117
+ return parsed;
118
+ }
119
+ function pick(key, type, ...layers) {
120
+ for (const layer of layers) {
121
+ const value = layer?.[key];
122
+ if (typeof value === type)
123
+ return value;
124
+ }
125
+ return undefined;
126
+ }
127
+ function pickIn(section, key, ...layers) {
128
+ for (const layer of layers) {
129
+ const value = layer?.[section]?.[key];
130
+ if (typeof value === "number" && value >= 0)
131
+ return value;
132
+ }
133
+ return undefined;
134
+ }
135
+ function readConsent(repo, file) {
136
+ const consent = repo?.egress?.consent;
137
+ if (consent === undefined)
138
+ return { granted: false };
139
+ if (typeof consent !== "object" ||
140
+ consent === null ||
141
+ typeof consent.granted !== "boolean") {
142
+ throw new DecisionsError("config-error", `${file}: egress.consent must be {granted: true|false, at?, by?}`, { file });
143
+ }
144
+ const { granted, at, by } = consent;
145
+ return {
146
+ granted,
147
+ ...(typeof at === "string" ? { at } : {}),
148
+ ...(typeof by === "string" ? { by } : {}),
149
+ };
150
+ }
151
+ function stringList(layer, path, file) {
152
+ const value = layer?.egress?.exclude;
153
+ if (value === undefined)
154
+ return [];
155
+ if (!Array.isArray(value) || !value.every((v) => typeof v === "string")) {
156
+ throw new DecisionsError("config-error", `${file}: ${path} must be a list of glob patterns`, {
157
+ file,
158
+ });
159
+ }
160
+ return value;
161
+ }
162
+ function profileList(layer, file) {
163
+ const value = layer?.profiles;
164
+ if (value === undefined)
165
+ return [];
166
+ if (!Array.isArray(value))
167
+ throw new DecisionsError("config-error", `${file}: profiles must be a list`, { file });
168
+ return value.map((p, i) => {
169
+ const profile = p;
170
+ const ok = typeof profile.id === "string" &&
171
+ typeof profile.maxStateTokens === "number" &&
172
+ typeof profile.usdPerInputToken === "number" &&
173
+ typeof profile.undecidedFloor === "number";
174
+ if (!ok) {
175
+ throw new DecisionsError("config-error", `${file}: profiles[${i}] needs id, maxStateTokens, usdPerInputToken and undecidedFloor`, { file });
176
+ }
177
+ if (profile.maxChoices !== undefined &&
178
+ !(Number.isInteger(profile.maxChoices) && profile.maxChoices >= 2)) {
179
+ throw new DecisionsError("config-error", `${file}: profiles[${i}].maxChoices must be a whole number of at least 2`, { file });
180
+ }
181
+ return {
182
+ displayName: profile.id,
183
+ transport: "openrouter-decisions",
184
+ usdPerOutputToken: 0,
185
+ priceAsOf: "unknown",
186
+ calibrated: true,
187
+ // Same wire contract as Jev unless the config says otherwise.
188
+ maxChoices: 255,
189
+ ...profile,
190
+ };
191
+ });
192
+ }
193
+ /**
194
+ * The API key: `OPENROUTER_API_KEY` first, then the user credentials file.
195
+ *
196
+ * The credentials file holds `openrouter_api_key: sk-…` and must not be
197
+ * readable by group or others, the same rule ssh applies to private keys.
198
+ */
199
+ function resolveApiKey(env, userDir) {
200
+ const fromEnv = nonEmpty(env.OPENROUTER_API_KEY);
201
+ if (fromEnv)
202
+ return { value: fromEnv, source: "env" };
203
+ const file = join(userDir, "credentials");
204
+ if (!existsSync(file))
205
+ return undefined;
206
+ if (process.platform !== "win32" && (statSync(file).mode & 0o077) !== 0) {
207
+ throw new DecisionsError("config-error", `${file} is readable by other users. Run \`chmod 600 ${file}\`.`, { file });
208
+ }
209
+ const parsed = readLayer(file);
210
+ const value = nonEmpty(typeof parsed?.openrouter_api_key === "string" ? parsed.openrouter_api_key : undefined);
211
+ return value ? { value, source: "credentials", file } : undefined;
212
+ }
213
+ function nonEmpty(value) {
214
+ return value === undefined || value.trim() === "" ? undefined : value.trim();
215
+ }
216
+ //# sourceMappingURL=load.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load.js","sourceRoot":"","sources":["../../src/config/load.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Which agent session a call belongs to, so the spend ledger can answer
3
+ * "what has this session cost" across separate `decide` invocations.
4
+ *
5
+ * `SYSTEM1_SESSION` always wins. Otherwise the id comes from the harness
6
+ * running the shell. Harnesses leak their variables into child harnesses (a
7
+ * Pi or Codex run started from Claude Code still sees
8
+ * `CLAUDE_CODE_SESSION_ID`), so the order below picks the innermost one:
9
+ *
10
+ * 1. `AI_AGENT=pi`: Pi overwrites `AI_AGENT`, so this is Pi even if a parent
11
+ * harness's id is present.
12
+ * 2. `AI_AGENT=claude-code…` with no Codex id: Claude also overwrites
13
+ * `AI_AGENT`, so a leaked Pi id belongs to a parent.
14
+ * 3. `CODEX_THREAD_ID`: Codex leaves `AI_AGENT` alone, but no other harness
15
+ * sets this.
16
+ * 4. `PI_SESSION_ID`, then `CLAUDE_CODE_SESSION_ID`.
17
+ *
18
+ * Known limits: Claude Code started inside Codex resolves to Codex, and Codex
19
+ * started inside Pi resolves to Pi (Codex inherits the parent's `AI_AGENT`).
20
+ * The environment cannot tell these apart. Set `SYSTEM1_SESSION` to override.
21
+ */
22
+ export type Harness = "claude" | "codex" | "pi";
23
+ export interface DetectedSession {
24
+ /** `<harness>:<id>`, or the `SYSTEM1_SESSION` value verbatim. */
25
+ id: string;
26
+ origin: Harness | "env";
27
+ }
28
+ export declare function detectSession(env: NodeJS.ProcessEnv): DetectedSession | undefined;
29
+ /** The harness running this shell, by the same rule; also known without a session id. */
30
+ export declare function detectHarness(env: NodeJS.ProcessEnv): Harness | undefined;
31
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/config/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,IAAI,CAAA;AAE/C,MAAM,WAAW,eAAe;IAC9B,iEAAiE;IACjE,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,OAAO,GAAG,KAAK,CAAA;CACxB;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,eAAe,GAAG,SAAS,CAKjF;AAED,yFAAyF;AACzF,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,OAAO,GAAG,SAAS,CAEzE"}
@@ -0,0 +1,36 @@
1
+ export function detectSession(env) {
2
+ const explicit = nonEmpty(env.SYSTEM1_SESSION);
3
+ if (explicit)
4
+ return { id: explicit, origin: "env" };
5
+ const found = innermost(env);
6
+ return found?.id ? { id: `${found.harness}:${found.id}`, origin: found.harness } : undefined;
7
+ }
8
+ /** The harness running this shell, by the same rule; also known without a session id. */
9
+ export function detectHarness(env) {
10
+ return innermost(env)?.harness;
11
+ }
12
+ function innermost(env) {
13
+ const pi = nonEmpty(env.PI_SESSION_ID);
14
+ const codex = nonEmpty(env.CODEX_THREAD_ID) ?? nonEmpty(env.CODEX_SESSION_ID);
15
+ const claude = nonEmpty(env.CLAUDE_CODE_SESSION_ID);
16
+ const agent = env.AI_AGENT ?? "";
17
+ const claudeMarked = env.CLAUDECODE === "1" || claude !== undefined;
18
+ if (agent === "pi")
19
+ return found("pi", pi);
20
+ if (agent.startsWith("claude-code") && claudeMarked && !codex)
21
+ return found("claude", claude);
22
+ if (codex)
23
+ return found("codex", codex);
24
+ if (pi || env.PI_CODING_AGENT === "true")
25
+ return found("pi", pi);
26
+ if (claudeMarked)
27
+ return found("claude", claude);
28
+ return undefined;
29
+ }
30
+ function found(harness, id) {
31
+ return id ? { harness, id } : { harness };
32
+ }
33
+ function nonEmpty(value) {
34
+ return value?.trim() ? value.trim() : undefined;
35
+ }
36
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/config/session.ts"],"names":[],"mappings":"AA6BA,MAAM,UAAU,aAAa,CAAC,GAAsB;IAClD,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,eAAe,CAAC,CAAA;IAC9C,IAAI,QAAQ;QAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,CAAA;IACpD,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,CAAA;IAC5B,OAAO,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS,CAAA;AAC9F,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,aAAa,CAAC,GAAsB;IAClD,OAAO,SAAS,CAAC,GAAG,CAAC,EAAE,OAAO,CAAA;AAChC,CAAC;AAED,SAAS,SAAS,CAAC,GAAsB;IACvC,MAAM,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAC,CAAA;IACtC,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,QAAQ,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAA;IAC7E,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAA;IACnD,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAA;IAChC,MAAM,YAAY,GAAG,GAAG,CAAC,UAAU,KAAK,GAAG,IAAI,MAAM,KAAK,SAAS,CAAA;IAEnE,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;IAC1C,IAAI,KAAK,CAAC,UAAU,CAAC,aAAa,CAAC,IAAI,YAAY,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;IAC7F,IAAI,KAAK;QAAE,OAAO,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;IACvC,IAAI,EAAE,IAAI,GAAG,CAAC,eAAe,KAAK,MAAM;QAAE,OAAO,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;IAChE,IAAI,YAAY;QAAE,OAAO,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;IAChD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,SAAS,KAAK,CAAC,OAAgB,EAAE,EAAsB;IACrD,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAA;AAC3C,CAAC;AAED,SAAS,QAAQ,CAAC,KAAyB;IACzC,OAAO,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAA;AACjD,CAAC"}
@@ -0,0 +1,28 @@
1
+ import { type DecideMode, type Decider } from "./decide.js";
2
+ export { DEFAULT_ENDPOINT } from "./transport/openrouter.js";
3
+ export declare const DEFAULT_MODEL = "typesafe/jev-1.13";
4
+ export interface ConnectionConfig {
5
+ endpoint: string;
6
+ model: string;
7
+ /** Never printed; only its presence is ever reported. */
8
+ apiKey: string | undefined;
9
+ /** `DECISIONS_REPLAY` forces replay even when a key is present. */
10
+ replay: boolean;
11
+ }
12
+ export declare function resolveConnection(env?: NodeJS.ProcessEnv): ConnectionConfig;
13
+ /** Where a repo keeps its decisions state. */
14
+ export declare function stateDir(cwd: string): string;
15
+ export interface EnvDeciderOptions {
16
+ cwd?: string;
17
+ env?: NodeJS.ProcessEnv;
18
+ home?: string;
19
+ mode?: DecideMode;
20
+ fetch?: typeof fetch;
21
+ }
22
+ /**
23
+ * The standard wiring from layered config: profile, a transport only when a
24
+ * key exists, the repo's egress consent, fixtures under `.decisions/fixtures`,
25
+ * and spend appended to `.decisions/usage.jsonl`.
26
+ */
27
+ export declare function createDeciderFromEnv(options?: EnvDeciderOptions): Decider;
28
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAQA,OAAO,EAAiB,KAAK,UAAU,EAAE,KAAK,OAAO,EAAE,MAAM,aAAa,CAAA;AAO1E,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAA;AAC5D,eAAO,MAAM,aAAa,sBAAmB,CAAA;AAE7C,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,yDAAyD;IACzD,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,mEAAmE;IACnE,MAAM,EAAE,OAAO,CAAA;CAChB;AAED,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,gBAAgB,CAOxF;AAED,8CAA8C;AAC9C,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE5C;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;IACvB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;CACrB;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CA0B7E"}
package/dist/config.js ADDED
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Connection settings, resolved from the environment.
3
+ *
4
+ * The M1 subset. Layered config files (user, then repo) arrive with egress
5
+ * consent in M2, and env always wins over them. The engine never reads a
6
+ * `.env` file: a project's `.env` belongs to that project, not to us.
7
+ */
8
+ import { join } from "node:path";
9
+ import { createDecider } from "./decide.js";
10
+ import { FixtureStore } from "./fixtures/store.js";
11
+ import { allProfiles, loadConfig } from "./config/load.js";
12
+ import { DEFAULT_MODEL_ID, resolveProfile } from "./model/profiles.js";
13
+ import { SpendLedger } from "./run/spend.js";
14
+ import { createOpenRouterTransport, DEFAULT_ENDPOINT } from "./transport/openrouter.js";
15
+ export { DEFAULT_ENDPOINT } from "./transport/openrouter.js";
16
+ export const DEFAULT_MODEL = DEFAULT_MODEL_ID;
17
+ export function resolveConnection(env = process.env) {
18
+ return {
19
+ endpoint: nonEmpty(env.DECISIONS_ENDPOINT) ?? DEFAULT_ENDPOINT,
20
+ model: nonEmpty(env.DECISIONS_MODEL) ?? DEFAULT_MODEL,
21
+ apiKey: nonEmpty(env.OPENROUTER_API_KEY),
22
+ replay: /^(1|true|yes)$/i.test(env.DECISIONS_REPLAY ?? ""),
23
+ };
24
+ }
25
+ /** Where a repo keeps its decisions state. */
26
+ export function stateDir(cwd) {
27
+ return join(cwd, ".decisions");
28
+ }
29
+ /**
30
+ * The standard wiring from layered config: profile, a transport only when a
31
+ * key exists, the repo's egress consent, fixtures under `.decisions/fixtures`,
32
+ * and spend appended to `.decisions/usage.jsonl`.
33
+ */
34
+ export function createDeciderFromEnv(options = {}) {
35
+ const config = loadConfig({
36
+ ...(options.cwd ? { cwd: options.cwd } : {}),
37
+ ...(options.env ? { env: options.env } : {}),
38
+ ...(options.home ? { home: options.home } : {}),
39
+ });
40
+ const profile = resolveProfile(config.model, allProfiles(config));
41
+ const dir = stateDir(config.repoRoot);
42
+ return createDecider({
43
+ profile,
44
+ egressConsent: config.egress.consent.granted,
45
+ repoRoot: config.repoRoot,
46
+ ...(config.apiKey
47
+ ? {
48
+ transport: createOpenRouterTransport({
49
+ endpoint: config.endpoint,
50
+ apiKey: config.apiKey,
51
+ ...(options.fetch ? { fetch: options.fetch } : {}),
52
+ }),
53
+ }
54
+ : {}),
55
+ fixtures: new FixtureStore(join(dir, "fixtures")),
56
+ ledger: new SpendLedger(join(dir, "usage.jsonl")),
57
+ mode: config.replay ? "replay" : (options.mode ?? "auto"),
58
+ ...(config.session ? { session: config.session } : {}),
59
+ });
60
+ }
61
+ function nonEmpty(value) {
62
+ return value === undefined || value.trim() === "" ? undefined : value.trim();
63
+ }
64
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ import type { FixtureStore } from "./fixtures/store.js";
2
+ import type { ModelProfile } from "./model/profiles.js";
3
+ import type { Answers, QuestionSet, State, Usage } from "./model/types.js";
4
+ import type { AnswerSource, SpendLedger } from "./run/spend.js";
5
+ import type { Transport } from "./transport/openrouter.js";
6
+ /**
7
+ * - `auto`: live when a transport (i.e. a key) is available, otherwise replay.
8
+ * - `live`: always call the model.
9
+ * - `record`: call the model and write the fixture.
10
+ * - `replay`: never touch the network; a miss is `replay-miss`.
11
+ */
12
+ export type DecideMode = "auto" | "live" | "record" | "replay";
13
+ export interface DeciderOptions {
14
+ profile: ModelProfile;
15
+ /**
16
+ * Whether this repo consented to sending content off the machine (decision
17
+ * 0009). Required, so a caller can't forget it. Replay ignores it.
18
+ */
19
+ egressConsent: boolean;
20
+ /** Named in the `egress-refused` message. */
21
+ repoRoot?: string;
22
+ /** Absent when no API key is configured. */
23
+ transport?: Transport;
24
+ fixtures?: FixtureStore;
25
+ ledger?: SpendLedger;
26
+ mode?: DecideMode;
27
+ session?: string;
28
+ now?: () => Date;
29
+ }
30
+ export interface DecideInput {
31
+ state: State;
32
+ questions: QuestionSet;
33
+ /** Fixture namespace: a spec name, or `adhoc`. */
34
+ namespace?: string;
35
+ signal?: AbortSignal;
36
+ }
37
+ export interface DecisionResult {
38
+ source: AnswerSource;
39
+ /** Requested model id. */
40
+ model: string;
41
+ /** Build that actually answered (from the response, live or recorded). */
42
+ servedBy: string;
43
+ answers: Answers;
44
+ /** Names of answers too flat to act on. Never rounded to the top option. */
45
+ undecided: string[];
46
+ /** Measured for live calls; zero for replays (nothing was spent). */
47
+ usage: Usage;
48
+ latencyMs: number;
49
+ fixtureKey: string;
50
+ recordedAt?: string;
51
+ }
52
+ export interface Decider {
53
+ readonly mode: Exclude<DecideMode, "auto">;
54
+ decide(input: DecideInput): Promise<DecisionResult>;
55
+ }
56
+ export declare function createDecider(options: DeciderOptions): Decider;
57
+ //# sourceMappingURL=decide.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decide.d.ts","sourceRoot":"","sources":["../src/decide.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAGvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,KAAK,EAAE,OAAO,EAAmB,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AAE3F,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC/D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;AAE1D;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAE9D,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,YAAY,CAAA;IACrB;;;OAGG;IACH,aAAa,EAAE,OAAO,CAAA;IACtB,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,4CAA4C;IAC5C,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB,QAAQ,CAAC,EAAE,YAAY,CAAA;IACvB,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,KAAK,CAAA;IACZ,SAAS,EAAE,WAAW,CAAA;IACtB,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,YAAY,CAAA;IACpB,0BAA0B;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,OAAO,CAAA;IAChB,4EAA4E;IAC5E,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,qEAAqE;IACrE,KAAK,EAAE,KAAK,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC,CAAA;IAC1C,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC,CAAA;CACpD;AAID,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CA4F9D"}