harness-dispatch 0.5.0 → 0.6.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 (98) hide show
  1. package/CHANGELOG.md +177 -0
  2. package/README.md +5 -2
  3. package/config.default.yaml +10 -1
  4. package/dist/bin.d.ts.map +1 -1
  5. package/dist/bin.js +3 -176
  6. package/dist/bin.js.map +1 -1
  7. package/dist/config/protocol.d.ts.map +1 -1
  8. package/dist/config/protocol.js +10 -2
  9. package/dist/config/protocol.js.map +1 -1
  10. package/dist/config/route-fields.d.ts +72 -0
  11. package/dist/config/route-fields.d.ts.map +1 -0
  12. package/dist/config/route-fields.js +128 -0
  13. package/dist/config/route-fields.js.map +1 -0
  14. package/dist/config/validation.js +1 -1
  15. package/dist/config/validation.js.map +1 -1
  16. package/dist/config.d.ts.map +1 -1
  17. package/dist/config.js +92 -173
  18. package/dist/config.js.map +1 -1
  19. package/dist/configure-yaml.d.ts +23 -0
  20. package/dist/configure-yaml.d.ts.map +1 -0
  21. package/dist/configure-yaml.js +201 -0
  22. package/dist/configure-yaml.js.map +1 -0
  23. package/dist/dispatchers/base.d.ts +3 -0
  24. package/dist/dispatchers/base.d.ts.map +1 -1
  25. package/dist/dispatchers/base.js.map +1 -1
  26. package/dist/dispatchers/generic-cli.d.ts.map +1 -1
  27. package/dist/dispatchers/generic-cli.js +22 -2
  28. package/dist/dispatchers/generic-cli.js.map +1 -1
  29. package/dist/dispatchers/openai-compatible.d.ts.map +1 -1
  30. package/dist/dispatchers/openai-compatible.js +18 -0
  31. package/dist/dispatchers/openai-compatible.js.map +1 -1
  32. package/dist/dispatchers/shared/stream-subprocess.d.ts +11 -0
  33. package/dist/dispatchers/shared/stream-subprocess.d.ts.map +1 -1
  34. package/dist/dispatchers/shared/stream-subprocess.js +57 -35
  35. package/dist/dispatchers/shared/stream-subprocess.js.map +1 -1
  36. package/dist/http/parse.d.ts +64 -0
  37. package/dist/http/parse.d.ts.map +1 -0
  38. package/dist/http/parse.js +230 -0
  39. package/dist/http/parse.js.map +1 -0
  40. package/dist/http/server.d.ts +2 -11
  41. package/dist/http/server.d.ts.map +1 -1
  42. package/dist/http/server.js +26 -232
  43. package/dist/http/server.js.map +1 -1
  44. package/dist/index.d.ts +1 -1
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +3 -2
  47. package/dist/index.js.map +1 -1
  48. package/dist/jobs/context.d.ts +17 -0
  49. package/dist/jobs/context.d.ts.map +1 -0
  50. package/dist/jobs/context.js +84 -0
  51. package/dist/jobs/context.js.map +1 -0
  52. package/dist/jobs/store.d.ts +80 -0
  53. package/dist/jobs/store.d.ts.map +1 -0
  54. package/dist/jobs/store.js +279 -0
  55. package/dist/jobs/store.js.map +1 -0
  56. package/dist/jobs/types.d.ts +135 -0
  57. package/dist/jobs/types.d.ts.map +1 -0
  58. package/dist/jobs/types.js +11 -0
  59. package/dist/jobs/types.js.map +1 -0
  60. package/dist/jobs.d.ts +113 -123
  61. package/dist/jobs.d.ts.map +1 -1
  62. package/dist/jobs.js +314 -309
  63. package/dist/jobs.js.map +1 -1
  64. package/dist/mcp/tool-schemas.d.ts +194 -0
  65. package/dist/mcp/tool-schemas.d.ts.map +1 -0
  66. package/dist/mcp/tool-schemas.js +311 -0
  67. package/dist/mcp/tool-schemas.js.map +1 -0
  68. package/dist/mcp/tools.d.ts +15 -59
  69. package/dist/mcp/tools.d.ts.map +1 -1
  70. package/dist/mcp/tools.js +63 -239
  71. package/dist/mcp/tools.js.map +1 -1
  72. package/dist/quota.d.ts +19 -1
  73. package/dist/quota.d.ts.map +1 -1
  74. package/dist/quota.js +39 -4
  75. package/dist/quota.js.map +1 -1
  76. package/dist/router.d.ts +4 -0
  77. package/dist/router.d.ts.map +1 -1
  78. package/dist/router.js +8 -0
  79. package/dist/router.js.map +1 -1
  80. package/dist/status.d.ts +13 -0
  81. package/dist/status.d.ts.map +1 -1
  82. package/dist/status.js +6 -0
  83. package/dist/status.js.map +1 -1
  84. package/dist/types.d.ts +51 -0
  85. package/dist/types.d.ts.map +1 -1
  86. package/dist/workspace-resolve.d.ts +98 -0
  87. package/dist/workspace-resolve.d.ts.map +1 -0
  88. package/dist/workspace-resolve.js +382 -0
  89. package/dist/workspace-resolve.js.map +1 -0
  90. package/dist/workspaces.d.ts +1 -0
  91. package/dist/workspaces.d.ts.map +1 -1
  92. package/dist/workspaces.js +67 -15
  93. package/dist/workspaces.js.map +1 -1
  94. package/package.json +5 -4
  95. package/dist/dashboard/live.d.ts +0 -49
  96. package/dist/dashboard/live.d.ts.map +0 -1
  97. package/dist/dashboard/live.js +0 -149
  98. package/dist/dashboard/live.js.map +0 -1
@@ -0,0 +1,72 @@
1
+ /**
2
+ * One field contract shared by every route shape.
3
+ *
4
+ * THE DEFECT THIS EXISTS TO RETIRE. `clis:`, `endpoints:` and the legacy
5
+ * `services:` block each resolved route fields with their own hand-written
6
+ * list, so a key added to one was easy to forget in the others. That produced
7
+ * five separate silent-drop defects in one file, each the same shape: a
8
+ * correctly-spelled, correctly-valued setting written in config and read by
9
+ * nobody —
10
+ *
11
+ * workspace_policy honoured for services:/endpoints:, dropped for clis:
12
+ * api_keys honoured for clis:, dropped for endpoints:
13
+ * effective_safety honoured for clis:/services:, dropped for endpoints:
14
+ * escalate_model honoured for clis:, dropped for endpoints:
15
+ *
16
+ * These are safety and isolation controls, so "silently absent" means
17
+ * "silently less restrictive" — a route asked to run `read_only` running with
18
+ * write access, a route asked to run in an isolated copy running in the user's
19
+ * repository. Nothing warns, because nothing is wrong with what was written.
20
+ *
21
+ * WHY A TABLE RATHER THAN A SHARED FUNCTION. The obvious unification — have
22
+ * the CLI builder call the endpoint builder's `billingFields()` — is wrong,
23
+ * and tests/route-field-parity.test.ts says why: the CLI path resolves
24
+ * `entry.X ?? harnessDefaults.X` against the defaults shipped in
25
+ * config.default.yaml, and `billingFields(raw)` has no defaults layer at all.
26
+ * Collapsing them would silently drop the fallback every built-in harness
27
+ * relies on — trading five silent-drop bugs for a bigger one. A table carries
28
+ * the defaults layer explicitly: each field says how to parse it AND where its
29
+ * fallback comes from, and a shape with no defaults (endpoints) simply passes
30
+ * none.
31
+ *
32
+ * WHAT IS DELIBERATELY NOT HERE. Billing identity — provider, surface,
33
+ * auth_source, billing_kind, paid_usage_possible — resolves differently per
34
+ * shape for real reasons, not by accident: endpoints infer it from the base
35
+ * URL (localhost is local_compute), while CLI routes factor in whether an
36
+ * api_key is present (a key means a metered account exists whatever the
37
+ * declared kind says). Those stay in their builders. Pretending they are
38
+ * shared would be the same mistake in the other direction.
39
+ *
40
+ * tests/route-field-parity.test.ts still pins every key here against both
41
+ * shapes. It is no longer the only thing standing between a new field and a
42
+ * silent drop, but it is what proves this table is actually wired into both.
43
+ */
44
+ import type { SafetyProfile, ServiceConfig } from "../types.js";
45
+ /**
46
+ * The subset of a harness's shipped defaults that shared fields fall back to.
47
+ *
48
+ * Structural on purpose: `CliDefaults` satisfies it without this module
49
+ * needing to import config.ts (which imports this one).
50
+ */
51
+ export type RouteFieldDefaults = Partial<Pick<ServiceConfig, "leaderboardModel" | "thinkingLevel" | "maxOutputTokens" | "maxInputTokens" | "effectiveSafety" | "models" | "modelHint">>;
52
+ /**
53
+ * `effective_safety` is either one profile for every request, or a per-request
54
+ * map. Shared by all three shapes; lived in config.ts before this table.
55
+ */
56
+ export declare function effectiveSafetyFrom(raw: unknown): SafetyProfile | Partial<Record<SafetyProfile, SafetyProfile>> | undefined;
57
+ /** The YAML keys this table owns — used by tests to prove both shapes are wired to it. */
58
+ export declare const SHARED_ROUTE_FIELD_KEYS: readonly string[];
59
+ /**
60
+ * Resolve every shared field for one route entry.
61
+ *
62
+ * A field is emitted only when it resolves to something, so callers can spread
63
+ * the result over a partially-built ServiceConfig without overwriting anything
64
+ * with `undefined`.
65
+ *
66
+ * @param raw the route entry as written in config.yaml
67
+ * @param defaults the harness's shipped defaults, for shapes that have them
68
+ * (`clis:`). Endpoint entries pass nothing — they have no defaults layer, and
69
+ * inventing one is the mistake this table's header warns about.
70
+ */
71
+ export declare function resolveSharedRouteFields(raw: Record<string, unknown>, defaults?: RouteFieldDefaults): Partial<ServiceConfig>;
72
+ //# sourceMappingURL=route-fields.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-fields.d.ts","sourceRoot":"","sources":["../../src/config/route-fields.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAShE;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAAG,OAAO,CACtC,IAAI,CACF,aAAa,EACX,kBAAkB,GAClB,eAAe,GACf,iBAAiB,GACjB,gBAAgB,GAChB,iBAAiB,GACjB,QAAQ,GACR,WAAW,CACd,CACF,CAAC;AA0BF;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,OAAO,GACX,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC,GAAG,SAAS,CAW3E;AAiCD,0FAA0F;AAC1F,eAAO,MAAM,uBAAuB,EAAE,SAAS,MAAM,EAA0C,CAAC;AAEhG;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,QAAQ,CAAC,EAAE,kBAAkB,GAC5B,OAAO,CAAC,aAAa,CAAC,CAQxB"}
@@ -0,0 +1,128 @@
1
+ /**
2
+ * One field contract shared by every route shape.
3
+ *
4
+ * THE DEFECT THIS EXISTS TO RETIRE. `clis:`, `endpoints:` and the legacy
5
+ * `services:` block each resolved route fields with their own hand-written
6
+ * list, so a key added to one was easy to forget in the others. That produced
7
+ * five separate silent-drop defects in one file, each the same shape: a
8
+ * correctly-spelled, correctly-valued setting written in config and read by
9
+ * nobody —
10
+ *
11
+ * workspace_policy honoured for services:/endpoints:, dropped for clis:
12
+ * api_keys honoured for clis:, dropped for endpoints:
13
+ * effective_safety honoured for clis:/services:, dropped for endpoints:
14
+ * escalate_model honoured for clis:, dropped for endpoints:
15
+ *
16
+ * These are safety and isolation controls, so "silently absent" means
17
+ * "silently less restrictive" — a route asked to run `read_only` running with
18
+ * write access, a route asked to run in an isolated copy running in the user's
19
+ * repository. Nothing warns, because nothing is wrong with what was written.
20
+ *
21
+ * WHY A TABLE RATHER THAN A SHARED FUNCTION. The obvious unification — have
22
+ * the CLI builder call the endpoint builder's `billingFields()` — is wrong,
23
+ * and tests/route-field-parity.test.ts says why: the CLI path resolves
24
+ * `entry.X ?? harnessDefaults.X` against the defaults shipped in
25
+ * config.default.yaml, and `billingFields(raw)` has no defaults layer at all.
26
+ * Collapsing them would silently drop the fallback every built-in harness
27
+ * relies on — trading five silent-drop bugs for a bigger one. A table carries
28
+ * the defaults layer explicitly: each field says how to parse it AND where its
29
+ * fallback comes from, and a shape with no defaults (endpoints) simply passes
30
+ * none.
31
+ *
32
+ * WHAT IS DELIBERATELY NOT HERE. Billing identity — provider, surface,
33
+ * auth_source, billing_kind, paid_usage_possible — resolves differently per
34
+ * shape for real reasons, not by accident: endpoints infer it from the base
35
+ * URL (localhost is local_compute), while CLI routes factor in whether an
36
+ * api_key is present (a key means a metered account exists whatever the
37
+ * declared kind says). Those stay in their builders. Pretending they are
38
+ * shared would be the same mistake in the other direction.
39
+ *
40
+ * tests/route-field-parity.test.ts still pins every key here against both
41
+ * shapes. It is no longer the only thing standing between a new field and a
42
+ * silent drop, but it is what proves this table is actually wired into both.
43
+ */
44
+ import { normalizeSafetyProfile } from "../safety.js";
45
+ import { confidenceFrom, str, thinkingFrom, workspacePolicyFrom, } from "./coercions.js";
46
+ import { stringArrayFrom } from "./protocol.js";
47
+ /** A number only when it really is one — `int()`/`num()` coerce, and these must not. */
48
+ function numberOnly(raw) {
49
+ return typeof raw === "number" ? raw : undefined;
50
+ }
51
+ /** Likewise for booleans: an absent flag must stay absent, not become false. */
52
+ function booleanOnly(raw) {
53
+ return typeof raw === "boolean" ? raw : undefined;
54
+ }
55
+ /**
56
+ * `effective_safety` is either one profile for every request, or a per-request
57
+ * map. Shared by all three shapes; lived in config.ts before this table.
58
+ */
59
+ export function effectiveSafetyFrom(raw) {
60
+ const single = normalizeSafetyProfile(raw);
61
+ if (single !== undefined)
62
+ return single;
63
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw))
64
+ return undefined;
65
+ const out = {};
66
+ for (const [key, value] of Object.entries(raw)) {
67
+ const requested = normalizeSafetyProfile(key);
68
+ const floor = normalizeSafetyProfile(value);
69
+ if (requested !== undefined && floor !== undefined)
70
+ out[requested] = floor;
71
+ }
72
+ return Object.keys(out).length > 0 ? out : undefined;
73
+ }
74
+ /**
75
+ * Every optional field that means the same thing on every route shape.
76
+ *
77
+ * Adding a route setting means adding ONE row here (plus its key to
78
+ * KNOWN_ROUTE_KEYS in validation.ts, which is what catches misspellings) and
79
+ * it works on `clis:`, `endpoints:` and `services:` at once. That is the whole
80
+ * point: the old failure mode was a field that existed in one list and not the
81
+ * others.
82
+ */
83
+ const SHARED_ROUTE_FIELDS = [
84
+ { key: "leaderboard_model", field: "leaderboardModel", parse: str, fromDefaults: (d) => d.leaderboardModel },
85
+ { key: "thinking_level", field: "thinkingLevel", parse: thinkingFrom, fromDefaults: (d) => d.thinkingLevel },
86
+ { key: "escalate_model", field: "escalateModel", parse: str },
87
+ { key: "max_output_tokens", field: "maxOutputTokens", parse: numberOnly, fromDefaults: (d) => d.maxOutputTokens },
88
+ { key: "max_input_tokens", field: "maxInputTokens", parse: numberOnly, fromDefaults: (d) => d.maxInputTokens },
89
+ { key: "timeout_ms", field: "timeoutMs", parse: numberOnly },
90
+ { key: "resource_weight", field: "resourceWeight", parse: numberOnly },
91
+ // Safety and isolation. Four of the five original silent drops were here.
92
+ { key: "safety_profile", field: "safetyProfile", parse: normalizeSafetyProfile },
93
+ { key: "effective_safety", field: "effectiveSafety", parse: effectiveSafetyFrom, fromDefaults: (d) => d.effectiveSafety },
94
+ { key: "workspace_policy", field: "workspacePolicy", parse: workspacePolicyFrom },
95
+ // Billing fields that are plain declarations. The INFERRED ones
96
+ // (provider/surface/auth_source/billing_kind/paid_usage_possible) stay in
97
+ // the builders — see the header.
98
+ { key: "allow_paid_usage", field: "allowPaidUsage", parse: booleanOnly },
99
+ { key: "billing_confidence", field: "billingConfidence", parse: confidenceFrom },
100
+ { key: "billing_notes", field: "billingNotes", parse: str },
101
+ { key: "models", field: "models", parse: stringArrayFrom, fromDefaults: (d) => d.models },
102
+ { key: "model_hint", field: "modelHint", parse: str, fromDefaults: (d) => d.modelHint },
103
+ ];
104
+ /** The YAML keys this table owns — used by tests to prove both shapes are wired to it. */
105
+ export const SHARED_ROUTE_FIELD_KEYS = SHARED_ROUTE_FIELDS.map((f) => f.key);
106
+ /**
107
+ * Resolve every shared field for one route entry.
108
+ *
109
+ * A field is emitted only when it resolves to something, so callers can spread
110
+ * the result over a partially-built ServiceConfig without overwriting anything
111
+ * with `undefined`.
112
+ *
113
+ * @param raw the route entry as written in config.yaml
114
+ * @param defaults the harness's shipped defaults, for shapes that have them
115
+ * (`clis:`). Endpoint entries pass nothing — they have no defaults layer, and
116
+ * inventing one is the mistake this table's header warns about.
117
+ */
118
+ export function resolveSharedRouteFields(raw, defaults) {
119
+ const out = {};
120
+ for (const spec of SHARED_ROUTE_FIELDS) {
121
+ const declared = spec.parse(raw[spec.key]);
122
+ const value = declared ?? (defaults !== undefined ? spec.fromDefaults?.(defaults) : undefined);
123
+ if (value !== undefined)
124
+ out[spec.field] = value;
125
+ }
126
+ return out;
127
+ }
128
+ //# sourceMappingURL=route-fields.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-fields.js","sourceRoot":"","sources":["../../src/config/route-fields.ts"],"names":[],"mappings":"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"}
@@ -108,7 +108,7 @@ export const KNOWN_ROUTE_KEYS = new Set([
108
108
  "name", "harness", "type", "command", "enabled", "model", "models", "model_hint",
109
109
  "tier", "weight", "cli_capability", "capabilities", "timeout_ms",
110
110
  "max_input_tokens", "max_output_tokens", "thinking_level",
111
- "leaderboard_model", "escalate_model", "escalate_on",
111
+ "leaderboard_model", "escalate_model", "escalate_on", "resource_weight",
112
112
  "api_key", "base_url", "protocol", "filter",
113
113
  "provider", "surface", "auth_source", "billing_kind", "billing_confidence",
114
114
  "billing_notes", "paid_usage_possible", "allow_paid_usage",
@@ -1 +1 @@
1
- {"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/config/validation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/config/validation.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAyBH,2EAA2E;AAC3E,MAAM,MAAM,OAAO,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AA+B9D,OAAO,KAAK,EAOV,iBAAiB,EAGjB,YAAY,EAOb,MAAM,YAAY,CAAC;AAsEpB,iKAAiK;AACjK,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAI9D,CAAC;AAQF;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAKvD,CAAC;AAgrBF,MAAM,WAAW,iBAAiB;IAChC,0EAA0E;IAC1E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;;;;;;;GAQG;AACH,wBAAsB,UAAU,CAC9B,IAAI,CAAC,EAAE,MAAM,EACb,IAAI,GAAE,iBAAsB,GAC3B,OAAO,CAAC,YAAY,CAAC,CAwGvB;AAMD,MAAM,WAAW,aAAa;IAC5B,IAAI,IAAI,IAAI,CAAC;CACd;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,CAAC,CAAC,EAAE,YAAY,KAAK,IAAI,EACnC,IAAI,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,GACpD,aAAa,CAkCf"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AA6BH,2EAA2E;AAC3E,MAAM,MAAM,OAAO,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AA+B9D,OAAO,KAAK,EAOV,iBAAiB,EAGjB,YAAY,EAOb,MAAM,YAAY,CAAC;AAsEpB,iKAAiK;AACjK,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAI9D,CAAC;AAQF;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAKvD,CAAC;AA0lBF,MAAM,WAAW,iBAAiB;IAChC,0EAA0E;IAC1E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;;;;;;;GAQG;AACH,wBAAsB,UAAU,CAC9B,IAAI,CAAC,EAAE,MAAM,EACb,IAAI,GAAE,iBAAsB,GAC3B,OAAO,CAAC,YAAY,CAAC,CAkHvB;AAMD,MAAM,WAAW,aAAa;IAC5B,IAAI,IAAI,IAAI,CAAC;CACd;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,CAAC,CAAC,EAAE,YAAY,KAAK,IAAI,EACnC,IAAI,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,GACpD,aAAa,CAkCf"}
package/dist/config.js CHANGED
@@ -17,12 +17,12 @@ import path from "node:path";
17
17
  import { fileURLToPath } from "node:url";
18
18
  import yaml from "js-yaml";
19
19
  import which from "which";
20
- import { normalizeSafetyProfile, } from "./safety.js";
21
20
  import { inferredPaidUsagePossible } from "./billing.js";
22
- import { authSourceFrom, billingKindFrom, bool, confidenceFrom, endpointModeFrom, endpointProviderFrom, inferEndpointProvider, int, num, providerFrom, str, surfaceFrom, thinkingFrom, wireProtocolFrom, workspacePolicyFrom, } from "./config/coercions.js";
21
+ import { authSourceFrom, billingKindFrom, bool, endpointModeFrom, endpointProviderFrom, inferEndpointProvider, int, num, providerFrom, str, surfaceFrom, wireProtocolFrom, } from "./config/coercions.js";
23
22
  import { warnUnknownRouteKeys, warnUnknownSafetyEnums, warnUnknownTopLevelKeys, } from "./config/validation.js";
24
23
  import { parseProtocolFields, protocolFrom, stringArrayFrom } from "./config/protocol.js";
25
- import { interpolateTree } from "./config/env-interpolation.js";
24
+ import { effectiveSafetyFrom, resolveSharedRouteFields, } from "./config/route-fields.js";
25
+ import { ENV_VAR_RE, interpolateTree } from "./config/env-interpolation.js";
26
26
  /**
27
27
  * PATH lookups, memoised for the life of the process.
28
28
  *
@@ -182,29 +182,22 @@ function topLevelSettings(raw, policyWarnings = []) {
182
182
  * KEY or value inside the map is dropped individually — a typo must not
183
183
  * silently widen the floor for a request it was meant to restrict.
184
184
  */
185
- function effectiveSafetyFrom(raw) {
186
- const single = normalizeSafetyProfile(raw);
187
- if (single !== undefined)
188
- return single;
189
- if (raw === null || typeof raw !== "object" || Array.isArray(raw))
190
- return undefined;
191
- const out = {};
192
- for (const [key, value] of Object.entries(raw)) {
193
- const requested = normalizeSafetyProfile(key);
194
- const floor = normalizeSafetyProfile(value);
195
- if (requested !== undefined && floor !== undefined)
196
- out[requested] = floor;
197
- }
198
- return Object.keys(out).length > 0 ? out : undefined;
199
- }
185
+ /**
186
+ * Billing IDENTITY for a route — the part that genuinely differs per shape.
187
+ *
188
+ * Everything that means the same thing everywhere (safety_profile,
189
+ * effective_safety, workspace_policy, billing_notes, models, …) now comes from
190
+ * resolveSharedRouteFields instead. What is left here is the set the caller
191
+ * has already inferred per shape: endpoints derive provider/surface/kind from
192
+ * the base URL, CLI routes derive them from whether an api_key is present.
193
+ * This function only reads what it is handed.
194
+ */
200
195
  function billingFields(raw) {
201
196
  const out = {};
202
197
  const provider = providerFrom(raw.provider);
203
198
  const surface = surfaceFrom(raw.surface);
204
199
  const authSource = authSourceFrom(raw.auth_source);
205
200
  const billingKind = billingKindFrom(raw.billing_kind);
206
- const billingConfidence = confidenceFrom(raw.billing_confidence);
207
- const safetyProfile = normalizeSafetyProfile(raw.safety_profile);
208
201
  if (provider !== undefined)
209
202
  out.provider = provider;
210
203
  if (surface !== undefined)
@@ -213,31 +206,9 @@ function billingFields(raw) {
213
206
  out.authSource = authSource;
214
207
  if (billingKind !== undefined)
215
208
  out.billingKind = billingKind;
216
- if (billingConfidence !== undefined)
217
- out.billingConfidence = billingConfidence;
218
209
  if (typeof raw.paid_usage_possible === "boolean") {
219
210
  out.paidUsagePossible = raw.paid_usage_possible;
220
211
  }
221
- if (typeof raw.allow_paid_usage === "boolean")
222
- out.allowPaidUsage = raw.allow_paid_usage;
223
- const notes = str(raw.billing_notes);
224
- if (notes !== undefined)
225
- out.billingNotes = notes;
226
- if (safetyProfile !== undefined)
227
- out.safetyProfile = safetyProfile;
228
- // effective_safety was handled by the `clis:` builder and the legacy
229
- // `services:` path but not here, so an `endpoints:` entry declaring it was
230
- // silently ignored — the fourth instance of this file's parallel-field-list
231
- // defect, and the first one caught by a test rather than by a user or a
232
- // reviewer. Practically fail-safe (openai_compatible routes are read_only by
233
- // construction, so the ignored value could only have widened them), but the
234
- // inconsistency is the bug.
235
- const effectiveSafety = effectiveSafetyFrom(raw.effective_safety);
236
- if (effectiveSafety !== undefined)
237
- out.effectiveSafety = effectiveSafety;
238
- const workspacePolicy = workspacePolicyFrom(raw.workspace_policy);
239
- if (workspacePolicy !== undefined)
240
- out.workspacePolicy = workspacePolicy;
241
212
  return out;
242
213
  }
243
214
  function capsFrom(raw) {
@@ -300,31 +271,11 @@ function buildLegacyConfig(raw) {
300
271
  tier: int(svc.tier, 1),
301
272
  weight: num(svc.weight, 1.0),
302
273
  cliCapability: num(svc.cli_capability, 1.0),
303
- ...(str(svc.leaderboard_model) !== undefined
304
- ? { leaderboardModel: str(svc.leaderboard_model) }
305
- : {}),
306
- ...(() => {
307
- const t = thinkingFrom(svc.thinking_level);
308
- return t !== undefined ? { thinkingLevel: t } : {};
309
- })(),
310
- ...(str(svc.escalate_model) !== undefined
311
- ? { escalateModel: str(svc.escalate_model) }
312
- : {}),
313
274
  escalateOn: escalateOnFrom(svc.escalate_on),
314
275
  capabilities: capsFrom(svc.capabilities),
315
276
  ...(() => {
316
- // Declared context size drives router.ts's preferLargeContext boost
317
- // (not harness name), so a legacy entry that omits these but names
318
- // a known harness (e.g. antigravity_cli's 2M context) must still
319
- // inherit them — otherwise the boost silently becomes a no-op.
320
- const v = typeof svc.max_output_tokens === "number" ? svc.max_output_tokens : harnessDefaults?.maxOutputTokens;
321
- return v !== undefined ? { maxOutputTokens: v } : {};
322
- })(),
323
- ...(() => {
324
- const v = typeof svc.max_input_tokens === "number" ? svc.max_input_tokens : harnessDefaults?.maxInputTokens;
325
- return v !== undefined ? { maxInputTokens: v } : {};
326
- })(),
327
- ...(() => {
277
+ // Billing IDENTITY only the rest of the named harness's defaults
278
+ // reach this entry through resolveSharedRouteFields below.
328
279
  if (!harnessDefaults)
329
280
  return {};
330
281
  return {
@@ -337,30 +288,19 @@ function buildLegacyConfig(raw) {
337
288
  ...(harnessDefaults.paidUsagePossible !== undefined
338
289
  ? { paidUsagePossible: harnessDefaults.paidUsagePossible }
339
290
  : {}),
340
- ...(harnessDefaults.effectiveSafety !== undefined
341
- ? { effectiveSafety: harnessDefaults.effectiveSafety }
342
- : {}),
343
- ...(harnessDefaults.models !== undefined ? { models: harnessDefaults.models } : {}),
344
- ...(harnessDefaults.modelHint !== undefined
345
- ? { modelHint: harnessDefaults.modelHint }
346
- : {}),
347
291
  };
348
292
  })(),
349
293
  ...billingFields(svc),
350
- ...(() => {
351
- const effectiveSafety = effectiveSafetyFrom(svc.effective_safety);
352
- return effectiveSafety !== undefined ? { effectiveSafety } : {};
353
- })(),
354
- ...(() => {
355
- const models = stringArrayFrom(svc.models);
356
- return models !== undefined ? { models } : {};
357
- })(),
358
- ...(() => {
359
- const modelHint = str(svc.model_hint);
360
- return modelHint !== undefined ? { modelHint } : {};
361
- })(),
294
+ // The same table the `clis:` and `endpoints:` builders use. This shape
295
+ // previously read the shared keys through billingFields() plus four
296
+ // hand-written blocks, which is how it ended up with a DIFFERENT set
297
+ // again: it inherited maxInput/OutputTokens, effectiveSafety, models and
298
+ // modelHint from the named harness but not leaderboardModel or
299
+ // thinkingLevel, so `services: { x: { harness: antigravity_cli } }`
300
+ // silently lost its scoring key and its thinking level. Passing the
301
+ // defaults through one table fixes that too.
302
+ ...resolveSharedRouteFields(svc, harnessDefaults),
362
303
  ...endpointFields(svc, type, str(svc.base_url)),
363
- ...(typeof svc.timeout_ms === "number" ? { timeoutMs: svc.timeout_ms } : {}),
364
304
  ...(() => {
365
305
  // Falls back to the named harness's built-in default protocol (if
366
306
  // any) when this legacy-format entry doesn't declare its own —
@@ -416,31 +356,13 @@ function buildCliServiceConfig(name, defaults, override, apiKeys, warnings = [])
416
356
  weight: num(override.weight, 1.0),
417
357
  tier: int(override.tier, defaults.tier),
418
358
  cliCapability: num(override.cli_capability, defaults.cliCapability),
419
- leaderboardModel: str(override.leaderboard_model) ?? defaults.leaderboardModel,
420
- ...(() => {
421
- const overrideThinking = thinkingFrom(override.thinking_level);
422
- if (overrideThinking !== undefined)
423
- return { thinkingLevel: overrideThinking };
424
- if (defaults.thinkingLevel !== undefined) {
425
- return { thinkingLevel: defaults.thinkingLevel };
426
- }
427
- return {};
428
- })(),
429
- ...(str(override.escalate_model) !== undefined
430
- ? { escalateModel: str(override.escalate_model) }
431
- : {}),
432
359
  escalateOn: escalateOnFrom(override.escalate_on),
433
360
  capabilities: caps,
434
- ...(() => {
435
- const m = override.max_output_tokens;
436
- const v = typeof m === "number" ? m : defaults.maxOutputTokens;
437
- return v !== undefined ? { maxOutputTokens: v } : {};
438
- })(),
439
- ...(() => {
440
- const m = override.max_input_tokens;
441
- const v = typeof m === "number" ? m : defaults.maxInputTokens;
442
- return v !== undefined ? { maxInputTokens: v } : {};
443
- })(),
361
+ // Every field that means the same thing on every route shape, resolved
362
+ // from ONE table against this harness's shipped defaults. Spread early so
363
+ // the shape-specific resolutions below (billing identity, which depends on
364
+ // whether an api_key is present) still win.
365
+ ...resolveSharedRouteFields(override, defaults),
444
366
  provider: providerFrom(override.provider) ?? defaults.provider,
445
367
  surface: surfaceFrom(override.surface) ?? defaults.surface,
446
368
  authSource: authSourceFrom(override.auth_source) ?? (apiKey ? "api_key" : defaults.authSource),
@@ -472,47 +394,7 @@ function buildCliServiceConfig(name, defaults, override, apiKeys, warnings = [])
472
394
  : defaults.paidUsagePossible;
473
395
  return paidUsagePossible !== undefined ? { paidUsagePossible } : {};
474
396
  })(),
475
- ...(typeof override.allow_paid_usage === "boolean"
476
- ? { allowPaidUsage: override.allow_paid_usage }
477
- : {}),
478
- ...(() => {
479
- const c = confidenceFrom(override.billing_confidence);
480
- return c !== undefined ? { billingConfidence: c } : {};
481
- })(),
482
- ...(() => {
483
- const notes = str(override.billing_notes);
484
- return notes !== undefined ? { billingNotes: notes } : {};
485
- })(),
486
- ...(() => {
487
- const safetyProfile = normalizeSafetyProfile(override.safety_profile);
488
- return safetyProfile !== undefined ? { safetyProfile } : {};
489
- })(),
490
- ...(() => {
491
- // `clis:` entries never read this. It was handled for `services:` and
492
- // `endpoints:` (both go through billingFields, which this builder does
493
- // not call), so `workspace_policy: copy` on the primary, documented way
494
- // to define a route was silently dropped and the route fell back to
495
- // shared_locked — the LESS isolated default, with no warning, because
496
- // the value itself is perfectly valid. The fail-open enum check added
497
- // earlier cannot catch this: the key is right and the value is right;
498
- // nothing was reading it.
499
- const workspacePolicy = workspacePolicyFrom(override.workspace_policy);
500
- return workspacePolicy !== undefined ? { workspacePolicy } : {};
501
- })(),
502
- ...(() => {
503
- const effectiveSafety = effectiveSafetyFrom(override.effective_safety) ?? defaults.effectiveSafety;
504
- return effectiveSafety !== undefined ? { effectiveSafety } : {};
505
- })(),
506
- ...(() => {
507
- const models = stringArrayFrom(override.models) ?? defaults.models;
508
- return models !== undefined ? { models } : {};
509
- })(),
510
- ...(() => {
511
- const modelHint = str(override.model_hint) ?? defaults.modelHint;
512
- return modelHint !== undefined ? { modelHint } : {};
513
- })(),
514
397
  ...endpointFields(override, "cli", str(override.base_url)),
515
- ...(typeof override.timeout_ms === "number" ? { timeoutMs: override.timeout_ms } : {}),
516
398
  ...(() => {
517
399
  // Falls back to this harness's built-in default (if any) when no
518
400
  // override is given, or when the override is malformed — protocolFrom
@@ -608,6 +490,53 @@ function addClis(services, raw, apiKeys, warnings) {
608
490
  services[name] = buildCliServiceConfig(name, defaults, entry, apiKeys, warnings);
609
491
  }
610
492
  }
493
+ /**
494
+ * `api_key: ${VAR}` references keyed by route name, read from the RAW tree
495
+ * before interpolation.
496
+ *
497
+ * envRefs cannot cover this case. It maps a resolved value back to the
498
+ * reference that produced it, and an UNSET variable resolves to "" — which
499
+ * every unset variable shares, so the map would hand one route another route's
500
+ * variable name. interpolateEnv skips empty resolutions for exactly that
501
+ * reason.
502
+ *
503
+ * The consequence was silent: `configure --yes --force` run in a shell that
504
+ * had not exported the variable emitted the route with no `api_key` line at
505
+ * all, overwriting a correct config with one whose key was simply gone. Keyed
506
+ * by route name, which is unique per shape and is what configure has in hand.
507
+ */
508
+ function collectApiKeyRefs(parsed) {
509
+ const refs = new Map();
510
+ const note = (name, value) => {
511
+ if (typeof name === "string" && typeof value === "string" && ENV_VAR_RE.test(value)) {
512
+ refs.set(name, value);
513
+ }
514
+ };
515
+ for (const key of ["clis", "endpoints"]) {
516
+ const list = parsed[key];
517
+ if (!Array.isArray(list))
518
+ continue;
519
+ for (const entry of list) {
520
+ if (entry !== null && typeof entry === "object") {
521
+ const e = entry;
522
+ note(e.name, e.api_key);
523
+ }
524
+ }
525
+ }
526
+ for (const key of ["services", "api_keys"]) {
527
+ const block = parsed[key];
528
+ if (block === null || typeof block !== "object")
529
+ continue;
530
+ for (const [name, entry] of Object.entries(block)) {
531
+ if (key === "api_keys")
532
+ note(name, entry);
533
+ else if (entry !== null && typeof entry === "object") {
534
+ note(name, entry.api_key);
535
+ }
536
+ }
537
+ }
538
+ return refs;
539
+ }
611
540
  function collectApiKeys(raw) {
612
541
  const apiKeys = {};
613
542
  const rawApiKeys = (raw.api_keys ?? {});
@@ -669,16 +598,6 @@ function addEndpoints(services, raw, apiKeys, warnings = []) {
669
598
  weight: num(ep.weight, 0.6),
670
599
  tier: int(ep.tier, 3),
671
600
  cliCapability: num(ep.cli_capability, 1.0),
672
- ...(str(ep.leaderboard_model) !== undefined
673
- ? { leaderboardModel: str(ep.leaderboard_model) }
674
- : {}),
675
- // escalate_on was honoured here and escalate_model was not — the fifth
676
- // instance of this file's parallel-field-list defect, and the second
677
- // found by review rather than by the parity test, which pins 16 shared
678
- // keys and did not include this one. It does now.
679
- ...(str(ep.escalate_model) !== undefined
680
- ? { escalateModel: str(ep.escalate_model) }
681
- : {}),
682
601
  escalateOn: escalateOnFrom(ep.escalate_on),
683
602
  capabilities: capsFrom(ep.capabilities),
684
603
  ...billingFields({
@@ -695,21 +614,10 @@ function addEndpoints(services, raw, apiKeys, warnings = []) {
695
614
  billing_confidence: ep.billing_confidence ?? (inferEndpointProvider(baseUrl) === "custom" ? undefined : "documented"),
696
615
  }),
697
616
  ...endpointFields(ep, "openai_compatible", baseUrl),
698
- ...(typeof ep.timeout_ms === "number" ? { timeoutMs: ep.timeout_ms } : {}),
699
- ...(typeof ep.max_output_tokens === "number" ? { maxOutputTokens: ep.max_output_tokens } : {}),
700
- ...(typeof ep.max_input_tokens === "number" ? { maxInputTokens: ep.max_input_tokens } : {}),
701
- ...(() => {
702
- const t = thinkingFrom(ep.thinking_level);
703
- return t !== undefined ? { thinkingLevel: t } : {};
704
- })(),
705
- ...(() => {
706
- const models = stringArrayFrom(ep.models);
707
- return models !== undefined ? { models } : {};
708
- })(),
709
- ...(() => {
710
- const modelHint = str(ep.model_hint);
711
- return modelHint !== undefined ? { modelHint } : {};
712
- })(),
617
+ // No defaults argument: an endpoint has no harness whose shipped
618
+ // defaults it could fall back to. That absence is exactly why the CLI
619
+ // path could not simply reuse billingFields() see route-fields.ts.
620
+ ...resolveSharedRouteFields(ep),
713
621
  };
714
622
  services[name] = svc;
715
623
  }
@@ -728,11 +636,17 @@ export async function loadConfig(path, opts = {}) {
728
636
  let raw = {};
729
637
  const unsetEnvVars = new Set();
730
638
  const envRefs = new Map();
639
+ const apiKeyRefs = new Map();
731
640
  if (path) {
732
641
  try {
733
642
  const text = await fs.readFile(path, "utf-8");
734
643
  const parsed = yaml.load(text);
735
644
  if (parsed && typeof parsed === "object") {
645
+ // Before interpolation: an unset ${VAR} is indistinguishable from
646
+ // every other unset ${VAR} once it has resolved to "".
647
+ for (const [name, ref] of collectApiKeyRefs(parsed)) {
648
+ apiKeyRefs.set(name, ref);
649
+ }
736
650
  raw = interpolateTree(parsed, unsetEnvVars, envRefs);
737
651
  }
738
652
  }
@@ -777,7 +691,11 @@ export async function loadConfig(path, opts = {}) {
777
691
  if (enumWarnings.length > 0) {
778
692
  legacyCfg.configWarnings = [...(legacyCfg.configWarnings ?? []), ...enumWarnings];
779
693
  }
780
- const withRefs = envRefs.size > 0 ? { ...legacyCfg, envRefs } : legacyCfg;
694
+ const withRefs = {
695
+ ...legacyCfg,
696
+ ...(envRefs.size > 0 ? { envRefs } : {}),
697
+ ...(apiKeyRefs.size > 0 ? { apiKeyRefs } : {}),
698
+ };
781
699
  if (envVarWarning !== undefined) {
782
700
  return { ...withRefs, configWarnings: [...(withRefs.configWarnings ?? []), envVarWarning] };
783
701
  }
@@ -815,6 +733,7 @@ export async function loadConfig(path, opts = {}) {
815
733
  disabled,
816
734
  ...topLevelSettings(raw, warnings),
817
735
  ...(envRefs.size > 0 ? { envRefs } : {}),
736
+ ...(apiKeyRefs.size > 0 ? { apiKeyRefs } : {}),
818
737
  ...(warnings.length > 0 ? { configWarnings: warnings } : {}),
819
738
  };
820
739
  return cfg;