primitive-admin 1.1.0-alpha.81 → 1.1.0-alpha.83

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 (109) hide show
  1. package/assets/skill/skills/primitive-platform/SKILL.md +12 -0
  2. package/dist/src/commands/documents.js +455 -22
  3. package/dist/src/commands/documents.js.map +1 -1
  4. package/dist/src/commands/functions.js +889 -76
  5. package/dist/src/commands/functions.js.map +1 -1
  6. package/dist/src/commands/locks.js +15 -1
  7. package/dist/src/commands/locks.js.map +1 -1
  8. package/dist/src/commands/prompts.js +19 -2
  9. package/dist/src/commands/prompts.js.map +1 -1
  10. package/dist/src/commands/sync.d.ts +50 -0
  11. package/dist/src/commands/sync.js +587 -316
  12. package/dist/src/commands/sync.js.map +1 -1
  13. package/dist/src/commands/workflows.d.ts +1 -29
  14. package/dist/src/commands/workflows.js +8 -109
  15. package/dist/src/commands/workflows.js.map +1 -1
  16. package/dist/src/lib/api-client.d.ts +191 -2
  17. package/dist/src/lib/api-client.js +283 -7
  18. package/dist/src/lib/api-client.js.map +1 -1
  19. package/dist/src/lib/config-object-descriptor.js +12 -4
  20. package/dist/src/lib/config-object-descriptor.js.map +1 -1
  21. package/dist/src/lib/document-ingest-artifact.d.ts +120 -0
  22. package/dist/src/lib/document-ingest-artifact.js +505 -0
  23. package/dist/src/lib/document-ingest-artifact.js.map +1 -0
  24. package/dist/src/lib/document-ingest-input.d.ts +51 -0
  25. package/dist/src/lib/document-ingest-input.js +132 -0
  26. package/dist/src/lib/document-ingest-input.js.map +1 -0
  27. package/dist/src/lib/document-ingest.d.ts +78 -0
  28. package/dist/src/lib/document-ingest.js +231 -0
  29. package/dist/src/lib/document-ingest.js.map +1 -0
  30. package/dist/src/lib/env-resolver-core.d.ts +2 -2
  31. package/dist/src/lib/env-resolver-core.js +2 -2
  32. package/dist/src/lib/function-bundle.d.ts +6 -0
  33. package/dist/src/lib/function-bundle.js +8 -0
  34. package/dist/src/lib/function-bundle.js.map +1 -1
  35. package/dist/src/lib/function-collect.d.ts +1 -1
  36. package/dist/src/lib/function-collect.js +16 -0
  37. package/dist/src/lib/function-collect.js.map +1 -1
  38. package/dist/src/lib/function-db-types.d.ts +4 -0
  39. package/dist/src/lib/function-db-types.js +46 -2
  40. package/dist/src/lib/function-db-types.js.map +1 -1
  41. package/dist/src/lib/function-document-types.d.ts +83 -0
  42. package/dist/src/lib/function-document-types.js +137 -0
  43. package/dist/src/lib/function-document-types.js.map +1 -1
  44. package/dist/src/lib/function-grants-preflight.js +5 -0
  45. package/dist/src/lib/function-grants-preflight.js.map +1 -1
  46. package/dist/src/lib/function-log-lines.d.ts +76 -0
  47. package/dist/src/lib/function-log-lines.js +160 -0
  48. package/dist/src/lib/function-log-lines.js.map +1 -0
  49. package/dist/src/lib/function-log-row.d.ts +29 -0
  50. package/dist/src/lib/function-log-row.js +73 -0
  51. package/dist/src/lib/function-log-row.js.map +1 -0
  52. package/dist/src/lib/function-log-tail.d.ts +51 -2
  53. package/dist/src/lib/function-log-tail.js +160 -13
  54. package/dist/src/lib/function-log-tail.js.map +1 -1
  55. package/dist/src/lib/function-run.d.ts +289 -0
  56. package/dist/src/lib/function-run.js +389 -0
  57. package/dist/src/lib/function-run.js.map +1 -0
  58. package/dist/src/lib/function-schema-codegen.d.ts +17 -9
  59. package/dist/src/lib/function-schema-codegen.js +30 -29
  60. package/dist/src/lib/function-schema-codegen.js.map +1 -1
  61. package/dist/src/lib/function-sync.d.ts +226 -30
  62. package/dist/src/lib/function-sync.js +731 -77
  63. package/dist/src/lib/function-sync.js.map +1 -1
  64. package/dist/src/lib/function-versions.d.ts +122 -0
  65. package/dist/src/lib/function-versions.js +182 -0
  66. package/dist/src/lib/function-versions.js.map +1 -0
  67. package/dist/src/lib/generated-config-surfaces.d.ts +561 -91
  68. package/dist/src/lib/generated-config-surfaces.js +1858 -287
  69. package/dist/src/lib/generated-config-surfaces.js.map +1 -1
  70. package/dist/src/lib/generated-sdk-types.d.ts +1 -1
  71. package/dist/src/lib/generated-sdk-types.js +1 -1
  72. package/dist/src/lib/generated-sdk-types.js.map +1 -1
  73. package/dist/src/lib/log-inspection.d.ts +2 -0
  74. package/dist/src/lib/log-inspection.js +4 -0
  75. package/dist/src/lib/log-inspection.js.map +1 -1
  76. package/dist/src/lib/output.d.ts +15 -0
  77. package/dist/src/lib/output.js +28 -0
  78. package/dist/src/lib/output.js.map +1 -1
  79. package/dist/src/lib/prompt-schema-codegen.d.ts +94 -0
  80. package/dist/src/lib/prompt-schema-codegen.js +212 -0
  81. package/dist/src/lib/prompt-schema-codegen.js.map +1 -0
  82. package/dist/src/lib/snapshot-audit-source.d.ts +45 -0
  83. package/dist/src/lib/snapshot-audit-source.js +58 -0
  84. package/dist/src/lib/snapshot-audit-source.js.map +1 -0
  85. package/dist/src/lib/snapshot-audit-store.d.ts +52 -0
  86. package/dist/src/lib/snapshot-audit-store.js +196 -0
  87. package/dist/src/lib/snapshot-audit-store.js.map +1 -0
  88. package/dist/src/lib/snapshot-audit.d.ts +207 -0
  89. package/dist/src/lib/snapshot-audit.js +431 -0
  90. package/dist/src/lib/snapshot-audit.js.map +1 -0
  91. package/dist/src/lib/snapshot-build-rows.d.ts +60 -0
  92. package/dist/src/lib/snapshot-build-rows.js +87 -0
  93. package/dist/src/lib/snapshot-build-rows.js.map +1 -0
  94. package/dist/src/lib/snapshot-manifest-layout.d.ts +61 -0
  95. package/dist/src/lib/snapshot-manifest-layout.js +70 -0
  96. package/dist/src/lib/snapshot-manifest-layout.js.map +1 -0
  97. package/dist/src/lib/step-run-table.d.ts +43 -0
  98. package/dist/src/lib/step-run-table.js +129 -0
  99. package/dist/src/lib/step-run-table.js.map +1 -0
  100. package/dist/src/lib/swift-codegen/functionGenerator.d.ts +26 -13
  101. package/dist/src/lib/swift-codegen/functionGenerator.js +38 -27
  102. package/dist/src/lib/swift-codegen/functionGenerator.js.map +1 -1
  103. package/dist/src/lib/sync-selectors.d.ts +45 -2
  104. package/dist/src/lib/sync-selectors.js +66 -5
  105. package/dist/src/lib/sync-selectors.js.map +1 -1
  106. package/dist/src/lib/workflow-toml-validator.d.ts +9 -5
  107. package/dist/src/lib/workflow-toml-validator.js +18 -10
  108. package/dist/src/lib/workflow-toml-validator.js.map +1 -1
  109. package/package.json +2 -2
@@ -110,9 +110,19 @@ export interface ConfigField {
110
110
  * `"src/workflows/config/workflow-field-handlers.ts#normalizeWorkflowStatus"`.
111
111
  * `cli/tests/unit/config-surface-drift-guard.test.ts` fails when the module or
112
112
  * the export is missing (#2644 behavior 2b).
113
+ *
114
+ * `stage` says WHEN the handler runs, with exactly the meaning `RuleStage`
115
+ * gives a cross-field rule (#3375). Until this issue the slot named an owner
116
+ * and never said when, so intent criterion 5 — "a rule declared `preflight`
117
+ * is provably reached before the first mutating call" — could not cover a
118
+ * single-field rule at all. It is REQUIRED, so a new field handler cannot be
119
+ * declared without answering the question, and
120
+ * `findMalformedFieldValidationStages` closes the key set at
121
+ * `handler`/`stage`: the slot stays declarative like the field entry itself.
113
122
  */
114
123
  validation: "passthrough" | {
115
124
  handler: string;
125
+ stage: RuleStage;
116
126
  };
117
127
  /**
118
128
  * This field's declared type genuinely admits more than one TOML spelling
@@ -207,7 +217,18 @@ export interface ConfigTable {
207
217
  * CLI call exists would make the registry promise a refusal that in fact lands
208
218
  * mid-apply.
209
219
  */
210
- export type CrossFieldRuleStage = "preflight" | "apply";
220
+ export type RuleStage = "preflight" | "apply";
221
+ /**
222
+ * The cross-field spelling of `RuleStage`, kept as an exported alias.
223
+ *
224
+ * #3373 named the type for the only slot that had a stage. #3375 gave
225
+ * `ConfigField.validation` one too, with the same meaning, so the canonical
226
+ * name is the general one — and this alias stays because it is part of the
227
+ * registry's published surface (`CrossFieldRule.stage`, the guard, every
228
+ * `crossFieldRulesForStage` caller) and renaming it would be a breaking change
229
+ * to buy a shorter name.
230
+ */
231
+ export type CrossFieldRuleStage = RuleStage;
211
232
  /**
212
233
  * One rule of a configuration object that spans more than one field — `durable
213
234
  * = true` beside a webhook trigger, `runAs = "system"` beside a non-empty
@@ -337,8 +358,15 @@ export declare const UPDATE_ONLY: readonly ("create" | "update")[];
337
358
  * the definition never dispatches through it (#1976 decision of record); the
338
359
  * guard asserts the export exists so a renamed handler fails the CLI unit suite
339
360
  * instead of leaving a dangling reference (#2644 behavior 2b).
361
+ *
362
+ * `stage` is REQUIRED (#3375): a field handler that does not say when it runs
363
+ * makes the same empty promise the cross-field slot made before #3373 — it
364
+ * names an owner, and criterion 5 has nothing to check. A `preflight` handler
365
+ * is reached from `cli/src/commands/sync.ts#runConfigPushPreflight` before the
366
+ * first mutating call, which `findUnreachedPreflightRules` proves statically;
367
+ * an `apply` handler is everything else.
340
368
  */
341
- export declare function handledBy(modulePath: string, exportName: string): ConfigField["validation"];
369
+ export declare function handledBy(modulePath: string, exportName: string, stage: RuleStage): ConfigField["validation"];
342
370
  /**
343
371
  * Name the ONE export that enforces a cross-field rule, as
344
372
  * `"<repo-relative module>#<export>"` — the sibling of `handledBy` for a rule
@@ -739,6 +767,115 @@ export declare function hasDeclaredCrossFieldRules(surface: ConfigObjectSurface)
739
767
  * `[]`: "not declared yet" must never be read as "has no rules" (principle 6).
740
768
  */
741
769
  export declare function crossFieldRulesForStage(surface: ConfigObjectSurface, stage: CrossFieldRuleStage): readonly CrossFieldRule[];
770
+ /**
771
+ * Is a rule declared `preflight` actually reached before `config push` mutates
772
+ * anything? (issue #3375, project phase 3, intent §Success criteria 5.)
773
+ *
774
+ * #3373 gave a rule a `stage`; #3374 declared three surfaces' rules. Both left
775
+ * the string a PROMISE. "This runs before the first mutating call" was
776
+ * satisfied by typing `preflight` beside a rule that is not on the preflight
777
+ * path at all, because the push preflight was an inline block inside the
778
+ * command's `.action()` callback with no exported symbol — so "reached from the
779
+ * preflight" had nothing to resolve against.
780
+ *
781
+ * #3375 extracts that block into `cli/src/commands/sync.ts#runConfigPushPreflight`
782
+ * and this module answers the question statically, from the declared
783
+ * `<module>#<export>`:
784
+ *
785
+ * - `reachableExports` walks the call graph from one entry symbol over module
786
+ * SOURCE TEXT and returns what it reached — plus what it could NOT resolve,
787
+ * so silence is never a pass.
788
+ * - `findUnreachedPreflightRules` reports every `preflight` declaration,
789
+ * cross-field or single-field, whose handler is missing from that set.
790
+ * - `findMalformedFieldValidationStages` is the field-level twin of #3373's
791
+ * `findMalformedRuleDeclarations`: `ConfigField["validation"]` gained a
792
+ * `stage` here, and it must be well-formed to mean anything.
793
+ *
794
+ * Everything is a PURE function over values and over source text the caller
795
+ * passes in: `cli/tests/unit/config-surface-preflight-staging-3375.test.ts`
796
+ * does the filesystem walk, so this module stays vendorable (`types.ts`
797
+ * §Purity), exactly the split `rules.ts` already uses.
798
+ *
799
+ * ── Two things this check is NOT ──────────────────────────────────────────
800
+ *
801
+ * It is NECESSARY, not sufficient, and there is deliberately no converse. A
802
+ * handler reached on one surface's leg satisfies a `preflight` declaration on
803
+ * another (the residual the intent accepts and #3379 records), and asking "is
804
+ * this `apply` rule reached from the preflight?" would be wrong by
805
+ * construction: `validateSigningSecretDeclaration` IS reached on the function
806
+ * leg and IS correctly `apply` on the webhook surface, where no CLI call
807
+ * exists.
808
+ *
809
+ * The walk is identifier-based over masked source, so it over-approximates in
810
+ * the PASSING direction: a local variable shadowing an imported handler's name
811
+ * reads as a call. That is the safe direction for a guard whose failure mode
812
+ * must be "a promise you did not keep", not "a promise you did keep, reported
813
+ * as broken".
814
+ */
815
+ /** A field's `validation` object may carry these keys and no others (#3375). */
816
+ export declare const FIELD_VALIDATION_KEYS: readonly string[];
817
+ /**
818
+ * The modules a walk may read, and how a specifier resolves between them.
819
+ *
820
+ * The caller owns resolution because it owns the filesystem: the CLI test maps
821
+ * both spellings of the vendored artifact onto the `src/config-surface/`
822
+ * modules it is a copy of, which is the whole point of DSO-3375-002 — the
823
+ * generator strips every intra-directory import before concatenating, so the
824
+ * artifact itself carries no edges between its sections.
825
+ */
826
+ export interface StaticModuleGraph {
827
+ /** Repo-relative module path → that module's SOURCE TEXT. */
828
+ modules: ReadonlyMap<string, string>;
829
+ /**
830
+ * Candidate modules a specifier names, seen from `fromModule`.
831
+ *
832
+ * `null` = a bare package specifier: deliberately not followed, and never
833
+ * reported. `[]` = a relative specifier that resolves to no module in the
834
+ * graph, which IS reported — a guard that silently stops looking is worse
835
+ * than no guard.
836
+ */
837
+ resolve(fromModule: string, specifier: string): readonly string[] | null;
838
+ }
839
+ /** What one walk found, and what it could not resolve on the way. */
840
+ export interface ReachabilityWalk {
841
+ /** `<module>#<export>` for every symbol reached, module = where it is DECLARED. */
842
+ reached: ReadonlySet<string>;
843
+ /** Specifiers and entry symbols the graph could not answer for, sorted. */
844
+ unresolved: string[];
845
+ }
846
+ /**
847
+ * Walk the call graph from `entry` and return every symbol it reaches.
848
+ *
849
+ * An identifier inside a reached declaration's span is followed when it names
850
+ * another top-level declaration of the same module, or a value import — which
851
+ * resolves through the graph to whichever candidate module DECLARES the name,
852
+ * following `export { x } from` and `export * from` on the way. A symbol is
853
+ * recorded under the module that declares it, never under a barrel that merely
854
+ * re-exports it: a declaration that names the barrel is reported unreached,
855
+ * because the point of the string is to name the one implementation.
856
+ */
857
+ export declare function reachableExports(entry: string, graph: StaticModuleGraph): ReachabilityWalk;
858
+ /**
859
+ * Every fault in one surface's FIELD validation declarations, each naming the
860
+ * surface and the field. `[]` means every field is well-formed.
861
+ *
862
+ * Runs over every surface in the registry, migrated or not: field declarations
863
+ * exist independently of the cross-field slot, so the
864
+ * `CROSS_FIELD_RULES_UNMIGRATED` ratchet does not — and must not — stand
865
+ * between a field-level `preflight` declaration and this check (DSO-3375-001).
866
+ */
867
+ export declare function findMalformedFieldValidationStages(surface: ConfigObjectSurface): string[];
868
+ /**
869
+ * Every `preflight` declaration on this surface whose handler the walk did not
870
+ * reach, each naming the surface, the rule (or field), the handler and the
871
+ * entry point.
872
+ *
873
+ * The FIELD half runs on every surface. The CROSS-FIELD half runs only where a
874
+ * surface has declared, because `crossFieldRulesForStage` throws for one that
875
+ * has not — "not declared yet" is never "rule-free" (#3373), and asking it
876
+ * here would turn an unmigrated surface into a crash rather than a skip.
877
+ */
878
+ export declare function findUnreachedPreflightRules(surface: ConfigObjectSurface, reached: ReadonlySet<string>, entry: string): string[];
742
879
  /**
743
880
  * Request schemas generated from the configuration-object definitions
744
881
  * (issue #2644, phase 5 / criterion 9).
@@ -1239,70 +1376,107 @@ export interface ParsedManifest {
1239
1376
  */
1240
1377
  export declare function parseFunctionManifest(value: unknown): ParsedManifest;
1241
1378
  /**
1242
- * The function MODE vocabulary, and the one place its two spellings meet —
1243
- * #3281, project `server-functions` phase 4.
1244
- *
1245
- * `mode = "request" | "task"` is what an author writes (the intent, "Mode
1246
- * vocabulary?", sponsor decision 2026-09-09): a request function runs inside
1247
- * the call and returns its result; a task function returns a run id and
1248
- * survives restarts. `durable = true | false` is the key the first releases
1249
- * taught, and it stays ACCEPTED as an alias through the transition — a tree
1250
- * pushed with it keeps pushing, and pulls back byte for byte — while `durable`
1251
- * stays the adjective for the property itself.
1252
- *
1253
- * ── Why one module ───────────────────────────────────────────────────────
1254
- *
1255
- * Two spellings for one fact is a place two readers can disagree. So the
1256
- * resolution is written once, here, in the dependency-free tree the CLI
1257
- * vendors (`cli/scripts/gen-config-surfaces.mjs`), and every door reads it:
1258
- * the CLI preflight and diff entity, the server's trigger derivation, the
1259
- * admin config-version route (payload `mode` beside payload `durable`), the
1260
- * invoke branch and the trigger fires. A file — or a payload — that carries
1261
- * both keys has to have them AGREE, or it is refused naming both; a silent
1262
- * winner would be the one thing worse than two spellings.
1263
- *
1264
- * The stored column is still `ServerFunctionConfig.durable` (no `models.yaml`
1265
- * change before phase 7's rename sweep), which is why `configMode` exists:
1266
- * reading the row is the other half of the same translation.
1267
- */
1268
- export type FunctionMode = "request" | "task";
1269
- export declare const FUNCTION_MODES: readonly FunctionMode[];
1270
- /** What a declaration resolved to, or why it could not. */
1271
- export type DeclaredModeResolution = {
1272
- ok: true;
1273
- mode: FunctionMode;
1379
+ * The runtime keys a `functions/<key>.toml` no longer decides anything with —
1380
+ * #3482, project `server-functions` phase 4.
1381
+ *
1382
+ * The sponsor settled it on 2026-09-15 (the intent, "Mode vocabulary?",
1383
+ * amended again): the config says NOTHING about how a function runs. A
1384
+ * function is a function; the caller picks the runtime at each call, cron
1385
+ * fires always start a task run, webhook deliveries always run as a request,
1386
+ * and a function that must not run under one runtime tests for it in code with
1387
+ * `assertRuntime`.
1388
+ *
1389
+ * ── Why these keys are accepted, not refused ──────────────────────────────
1390
+ *
1391
+ * Every tree pushed since #3454 that declares a cron trigger carries
1392
+ * `mode = …` ON THE ENTRY, because `cron-trigger-mode-required` demanded it;
1393
+ * plenty carry a top-level `mode` or the older `durable`. Removing the keys
1394
+ * from the accepted set outright would refuse those trees with an unknown-key
1395
+ * error one day after the platform insisted on them, which is the opposite of
1396
+ * principle 5 inside a non-breaking phase.
1397
+ *
1398
+ * So through phase 4 they are ACCEPTED and IGNORED, with one warning per key
1399
+ * naming it and the line to delete; from phase 5 (#3188) they are refused with
1400
+ * the rest of the retirement matrix. The value is not validated — there is
1401
+ * nothing left to validate it against, and refusing a bad value for a key
1402
+ * nobody reads would be a refusal about spelling alone.
1403
+ *
1404
+ * Pure: no models, no tenant context, no `env` (see `types.ts` §Purity). Both
1405
+ * the CLI preflight (vendored by `cli/scripts/gen-config-surfaces.mjs`) and the
1406
+ * admin config-version route read this one scanner, so the warning an author
1407
+ * sees from `config push` is the sentence the API answers with.
1408
+ */
1409
+ /** The two `[function]` keys that no longer say anything (#3482). */
1410
+ export declare const RETIRED_FUNCTION_RUNTIME_KEYS: readonly ["mode", "durable"];
1411
+ export type RetiredFunctionRuntimeKey = (typeof RETIRED_FUNCTION_RUNTIME_KEYS)[number];
1412
+ /** One ignored key met in a `[function]` table. */
1413
+ export interface IgnoredFunctionKey {
1274
1414
  /**
1275
- * Whether the declaration SAID anything — false when neither key was
1276
- * present and the default applied. The admin route's divergence check
1277
- * reads this: an omitted mode is no claim, so it cannot diverge.
1415
+ * Where it was: `mode`, `durable`, or `triggers.cron.<name>.mode`. Never
1416
+ * rendered on its own — a reader that wants to branch reads this, and every
1417
+ * printer reads `message`.
1278
1418
  */
1279
- declared: boolean;
1280
- } | {
1281
- ok: false;
1282
- error: string;
1283
- };
1419
+ path: string;
1420
+ /** The sentence the author is owed, with no file prefix (the caller adds it). */
1421
+ message: string;
1422
+ }
1284
1423
  /**
1285
- * Resolve `mode` and its alias `durable` to one mode.
1424
+ * Every retired runtime key a `[function]` table still carries.
1286
1425
  *
1287
- * Absent both → `request`. `mode` must be exactly `"request"` or `"task"`;
1288
- * `durable` must be a boolean. Both present and disagreeing is refused naming
1289
- * both keys, so an author who half-migrated a file hears which line to fix.
1426
+ * Reads the table the way the author wrote it: the two top-level keys, and a
1427
+ * `mode` on each `[[function.triggers.cron]]` entry. A cron entry's sentence
1428
+ * says the extra thing its author needs to hear — that the schedule no longer
1429
+ * chooses, because every fire starts a task run (#3334's runner branch is
1430
+ * gone).
1431
+ *
1432
+ * Order is the order an author reads their file in: the top-level keys first,
1433
+ * then the cron entries in declaration order.
1290
1434
  */
1291
- export declare function resolveDeclaredMode(input: {
1292
- mode?: unknown;
1293
- durable?: unknown;
1294
- }): DeclaredModeResolution;
1435
+ export declare function ignoredFunctionKeys(functionTable: unknown): IgnoredFunctionKey[];
1295
1436
  /**
1296
- * The mode a stored config-version row runs in.
1437
+ * The database types a `[function]` table's retired trigger block names —
1438
+ * #3483.
1439
+ *
1440
+ * Beside `ignoredFunctionKeys` because it answers the same question about the
1441
+ * same table, for the same reader: `config pull` writes a version's AUTHORED
1442
+ * bytes, and a version pushed before the removal carries the block. Without
1443
+ * this the pulled tree would be refused on its next push — a tree an author
1444
+ * cannot get back into a working state, which is the failure D3482-007 already
1445
+ * had to close for the runtime keys.
1297
1446
  *
1298
- * The column is `durable`; a row written before #3281 carries only that, and a
1299
- * row written after it carries the same thing, so there is exactly one reading.
1447
+ * This is NOT a refusal and shares nothing with one. The block is refused on
1448
+ * input by `normalizeTriggerDeclaration`; what a strip needs is the list of
1449
+ * entries it is taking out so it can say so, which is why an entry whose
1450
+ * `type` is unreadable still counts as one entry (the empty string). It is the
1451
+ * block's PRESENCE that goes, not its contents.
1452
+ *
1453
+ * TOML spells the block three ways — an array of tables, a single table, and
1454
+ * an inline array — and a scan that missed one would leave a key behind
1455
+ * (CR3482-002's lesson, applied to a whole table).
1300
1456
  */
1301
- export declare function configMode(row: {
1302
- durable?: unknown;
1303
- } | null | undefined): FunctionMode;
1304
- /** Is this mode the durable one? The adjective, for the paths that branch on it. */
1305
- export declare function isDurableMode(mode: FunctionMode): boolean;
1457
+ export declare function retiredDatabaseTriggerTypes(functionTable: unknown): string[];
1458
+ /**
1459
+ * Whether the block is THERE at all — the question a strip has to ask, which
1460
+ * the type list above cannot answer (CR3483-001).
1461
+ *
1462
+ * `database = []` names no type, so the list is empty and a strip that decided
1463
+ * by its length left the key in the pulled file. That file then fails its own
1464
+ * next push: `normalizeTriggerDeclaration` refuses the key by PRESENCE, of any
1465
+ * shape, an empty array included — so the two halves disagreed about what the
1466
+ * block is, and the tree an author pulled was a tree they could not push.
1467
+ * Presence is the one rule both sides read now, spelled the way
1468
+ * `normalizeTriggerDeclaration` spells it — `!== undefined` — so a value one
1469
+ * of them would call a block and the other would not cannot exist.
1470
+ */
1471
+ export declare function hasRetiredDatabaseTriggerBlock(functionTable: unknown): boolean;
1472
+ /**
1473
+ * The same scan, rendered for one file — the shape `config push` and
1474
+ * `config diff` print and the admin route answers with.
1475
+ *
1476
+ * `label` is what the reader can act on: `functions/<key>.toml` from the CLI,
1477
+ * and the function's key from the API, which has no file to name.
1478
+ */
1479
+ export declare function ignoredFunctionKeyWarnings(label: string, functionTable: unknown): string[];
1306
1480
  /**
1307
1481
  * The trigger blocks a function declares, derived from its authored TOML
1308
1482
  * (#3181, project `server-functions`, criterion 4).
@@ -1349,29 +1523,20 @@ export declare function isDurableMode(mode: FunctionMode): boolean;
1349
1523
  * and the CLI has already parsed its own file by the time it asks.
1350
1524
  */
1351
1525
  /**
1352
- * The resolved mode the declaration is checked against (#3281).
1526
+ * Why a webhook trigger is the request runtime, and a cron fire a task run —
1527
+ * #3482, the intent's "Mode vocabulary?" decision of 2026-09-15.
1353
1528
  *
1354
- * The rule is per KIND, and permanent (the intent, "Sync functions write a run
1355
- * row?": *Webhook and database-change triggers require request mode; HTTP and
1356
- * cron allow both*). A webhook delivery and a database write are both waiting
1357
- * inside a request for the function to answer; a cron fire is waiting for
1358
- * nothing, so it starts a task run when the version is a task (#3334).
1529
+ * Neither is a rule about the FUNCTION any more, so neither is a refusal: a
1530
+ * function is a function, and the DOOR decides which runtime runs it. A
1531
+ * webhook delivery runs inside the receiver because the provider is holding
1532
+ * the connection open waiting for an answer; a cron fire starts a task run
1533
+ * because it is waiting for nothing. A function that must not run under one of
1534
+ * them tests for it in code with `assertRuntime`, which is the only lock left.
1359
1535
  *
1360
- * The caller resolves `mode`/`durable` once, through `resolveDeclaredMode`,
1361
- * and hands the answer here — this module never reads the alias itself.
1536
+ * What survives here is the SHAPE of a declaration — the keys, the names, the
1537
+ * cron expression, the caps. The retired `mode` keys are accepted and reported
1538
+ * as ignored by `ignoredFunctionKeys` in `function-retired-keys.ts`.
1362
1539
  */
1363
- export interface TriggerModeOptions {
1364
- mode?: FunctionMode;
1365
- }
1366
- /**
1367
- * Why a webhook cannot drive a task function, and what to do instead (#3334).
1368
- *
1369
- * The first clause is verbatim what it has always been, so sibling pins on the
1370
- * request-mode prefix (#3281's) keep holding; everything after the dash is the
1371
- * half that was missing — an author who meets this at the push door could not
1372
- * previously tell whether it was a limitation or a design, nor what to write.
1373
- */
1374
- export declare const WEBHOOK_ON_TASK_REFUSAL: string;
1375
1540
  /** A function's single webhook trigger, normalized. */
1376
1541
  export interface WebhookTriggerDeclaration {
1377
1542
  verificationScheme: string;
@@ -1392,15 +1557,9 @@ export interface CronTriggerDeclaration {
1392
1557
  overlapPolicy?: string;
1393
1558
  rootInput?: unknown;
1394
1559
  }
1395
- /** One `[[function.triggers.database]]` entry, normalized (#3184). */
1396
- export interface DatabaseTriggerDeclaration {
1397
- /** The database type key whose writes fire this function. */
1398
- type: string;
1399
- }
1400
1560
  export interface TriggerDeclaration {
1401
1561
  webhook: WebhookTriggerDeclaration | null;
1402
1562
  cron: CronTriggerDeclaration[];
1403
- databases: DatabaseTriggerDeclaration[];
1404
1563
  }
1405
1564
  export declare const EMPTY_TRIGGER_DECLARATION: TriggerDeclaration;
1406
1565
  /**
@@ -1413,14 +1572,22 @@ export declare const EMPTY_TRIGGER_DECLARATION: TriggerDeclaration;
1413
1572
  */
1414
1573
  export declare const MAX_CRON_TRIGGERS_PER_FUNCTION = 10;
1415
1574
  /**
1416
- * The most database types one function may watch (#3184, D-07).
1417
- *
1418
- * Per-OBJECT, like the cron cap above: a function watching more than a handful
1419
- * of types is describing a fan-in the platform cannot bound per write, and
1420
- * every extra type is another discovery read on somebody's write path.
1575
+ * The rule ids this module owns — one per refusal, declared on the `function`
1576
+ * surface (#3374). The union below closes `refuseTrigger`'s id parameter, so a
1577
+ * twenty-seventh refusal cannot compile without a twenty-seventh id, and that
1578
+ * id fails `cli-unit-tests` until the surface declares it (#3373's anchor).
1579
+ */
1580
+ export declare const TRIGGER_RULE_IDS: readonly ["triggers-table", "trigger-kind", "database-trigger-removed", "webhook-trigger-single", "webhook-trigger-table", "webhook-trigger-keys", "webhook-trigger-scheme-required", "webhook-trigger-scheme-none", "webhook-trigger-number", "webhook-trigger-dedup-boolean", "webhook-trigger-verification-table", "cron-trigger-cap", "cron-trigger-entry", "cron-trigger-keys", "cron-trigger-name-required", "cron-trigger-name-charset", "cron-trigger-duplicate", "cron-trigger-expression-required", "cron-trigger-overlap-policy"];
1581
+ export type TriggerRuleId = (typeof TRIGGER_RULE_IDS)[number];
1582
+ /**
1583
+ * A broken trigger rule. `ruleId` names which one, for a reader that wants to
1584
+ * branch without matching prose; every existing site reads `message` only.
1585
+ * Optional so `new TriggerDeclarationError(message)` — the CLI's own parse
1586
+ * refusal in `function-sync.ts` — keeps constructing.
1421
1587
  */
1422
- export declare const MAX_DATABASE_TRIGGERS_PER_FUNCTION = 5;
1423
1588
  export declare class TriggerDeclarationError extends Error {
1589
+ readonly ruleId?: TriggerRuleId;
1590
+ constructor(message: string, ruleId?: TriggerRuleId);
1424
1591
  }
1425
1592
  /** Is anything at all declared? Used to skip work on the common case. */
1426
1593
  export declare function hasDeclaredTriggers(declaration: TriggerDeclaration): boolean;
@@ -1433,11 +1600,20 @@ export declare function hasDeclaredTriggers(declaration: TriggerDeclaration): bo
1433
1600
  * push boundary can normalize a declaration it read from a stored row without
1434
1601
  * re-parsing bytes.
1435
1602
  */
1436
- export declare function normalizeTriggerDeclaration(functionTable: any, options?: TriggerModeOptions): TriggerDeclaration;
1603
+ export declare function normalizeTriggerDeclaration(functionTable: any): TriggerDeclaration;
1437
1604
  /** The stored JSON for `ServerFunctionConfig.triggers`, or null when empty. */
1438
1605
  export declare function serializeTriggerDeclaration(declaration: TriggerDeclaration): string | null;
1439
1606
  /** Read a stored `ServerFunctionConfig.triggers` value back. */
1440
1607
  export declare function parseStoredTriggerDeclaration(stored: unknown): TriggerDeclaration;
1608
+ /**
1609
+ * The database types a STORED declaration used to watch, for a reader that
1610
+ * wants to name them — #3483.
1611
+ *
1612
+ * Pure, and separate from the parse on purpose: the parse answers what the
1613
+ * version means NOW (nothing), and this answers what it said, which is only
1614
+ * ever used to write one log line. Nothing branches on it.
1615
+ */
1616
+ export declare function storedDatabaseTriggerTypes(stored: unknown): string[];
1441
1617
  /** The `CronTrigger.triggerKey` an entry owns. */
1442
1618
  export declare function cronTriggerKeyFor(functionKey: string, name: string): string;
1443
1619
  /** The entry name a `<functionKey>:<name>` trigger key carries. */
@@ -1597,12 +1773,24 @@ export declare function requiresSigningSecret(scheme: unknown): boolean;
1597
1773
  /** The coded refusals, so a client can branch without matching prose. */
1598
1774
  export declare const SIGNING_SECRET_MUST_BE_SECRET_REF = "SIGNING_SECRET_MUST_BE_SECRET_REF";
1599
1775
  export declare const SIGNING_SECRET_NOT_SUPPORTED_FOR_SCHEME = "SIGNING_SECRET_NOT_SUPPORTED_FOR_SCHEME";
1776
+ /**
1777
+ * The rule ids this module owns, declared on the `function` surface as
1778
+ * `preflight` and on the `webhook` surface as `apply` (#3374) — one rule, two
1779
+ * surfaces, one handler. `whole-secret-reference` is generic on purpose:
1780
+ * `validateWholeSecretReference` is reused by the Google client secret and by
1781
+ * both webhook controllers under their own labels and codes.
1782
+ */
1783
+ export declare const SIGNING_SECRET_RULE_IDS: readonly ["signing-secret-required", "whole-secret-reference", "signing-secret-not-supported"];
1784
+ type SigningSecretRuleId = (typeof SIGNING_SECRET_RULE_IDS)[number];
1600
1785
  /**
1601
1786
  * A refusal from this rule. `plain` marks the ones the admin webhook create
1602
1787
  * answered as a bare text body rather than coded JSON — an observable
1603
1788
  * distinction, so it travels with the error rather than being flattened.
1789
+ * `ruleId` names the rule and never reaches the wire: every reader picks
1790
+ * `message`, `code` and `plain`.
1604
1791
  */
1605
1792
  export interface SigningSecretRuleError {
1793
+ ruleId: SigningSecretRuleId;
1606
1794
  message: string;
1607
1795
  code?: string;
1608
1796
  plain?: boolean;
@@ -1632,7 +1820,288 @@ export declare function validateSigningSecretDeclaration(input: {
1632
1820
  verificationScheme: string;
1633
1821
  signingSecret?: unknown;
1634
1822
  }): SigningSecretRuleError | null;
1823
+ /**
1824
+ * The workflow rules two runners share (#3374, project `cli-server-rule-parity`
1825
+ * phase 2; intent §Success criteria 3).
1826
+ *
1827
+ * ── One predicate, two messages ──────────────────────────────────────────
1828
+ *
1829
+ * `runAs = "system"` beside a non-empty `accessRule` is dead config: a system
1830
+ * run can only be started by the system and never evaluates its access rule
1831
+ * (#1258, broadening #1172). The server refused it at save time
1832
+ * (`validateWorkflowIdentityConfig`) and the CLI refused it in the push
1833
+ * preflight (`validateWorkflowIdentity`), and the two carried the conjunction
1834
+ * byte-for-byte as separate copies kept in step by a comment. This module is
1835
+ * the one copy: it decides, and returns a STRUCTURED violation — the rule id
1836
+ * and the server's sentence — that each site renders in its own words. The
1837
+ * server pushes the sentence into its `string[]`; the CLI keeps its own hint,
1838
+ * which names the preflight remedy. Neither message changed.
1839
+ *
1840
+ * ── Why a `*-rules.ts` module ────────────────────────────────────────────
1841
+ *
1842
+ * The registry's unclaimed-export check (`findUnclaimedRuleModuleExports`)
1843
+ * keys on this name under `src/config-surface/`: every exported function here
1844
+ * is a rule, so one no surface declares fails `cli-unit-tests`. Shared
1845
+ * predicates that are not rules live elsewhere (`webhook-signing-secret.ts`).
1846
+ * The module is vendored into the CLI artifact like the rest of the
1847
+ * directory, which is what lets `workflow`'s declaration call the rule
1848
+ * `preflight`.
1849
+ */
1850
+ /** The rule ids this module owns — the union type below anchors them (#3373). */
1851
+ export declare const WORKFLOW_RULE_IDS: readonly ["system-access-rule"];
1852
+ type WorkflowRuleId = (typeof WORKFLOW_RULE_IDS)[number];
1853
+ /** A violation of one of this module's rules, for a site to render. */
1854
+ export interface WorkflowRuleViolation extends RuleRefusal<WorkflowRuleId> {
1855
+ }
1856
+ /**
1857
+ * `runAs = "system"` with a non-empty string `accessRule`, or null.
1858
+ *
1859
+ * Exactly the conjunction both sites carried: a default, unset, empty or
1860
+ * whitespace-only rule is no rule, and a non-string one (`accessRule = false`)
1861
+ * is malformed input for another check to name, not dead config. The message
1862
+ * is the server's save-time sentence; the CLI renders its own hint from the
1863
+ * same answer.
1864
+ */
1865
+ export declare function checkWorkflowSystemAccessRule(input: {
1866
+ runAs?: unknown;
1867
+ accessRule?: unknown;
1868
+ }): WorkflowRuleViolation | null;
1635
1869
  export declare const WORKFLOW_SURFACE: ConfigObjectSurface;
1870
+ /**
1871
+ * Cron-expression and timezone VALIDITY — the half of `src/cron-parser.ts`
1872
+ * both runners need (#3376, project `cli-server-rule-parity` phase 4).
1873
+ *
1874
+ * ── Why this module exists ───────────────────────────────────────────────
1875
+ *
1876
+ * A malformed cron expression and an unknown timezone are both decidable from
1877
+ * the authored file, so the intent lands both rules `preflight` — which under
1878
+ * this project's vocabulary means the implementation lives here, under
1879
+ * `src/config-surface/`, and is vendored into the CLI artifact.
1880
+ *
1881
+ * It got here by a SPLIT, not a move. `src/cron-parser.ts` is 415 lines, and
1882
+ * roughly 290 of them are timezone-aware scheduling arithmetic — "when does
1883
+ * this expression next fire?" — that only the cron Durable Object runs.
1884
+ * Vendoring is wholesale: the generator concatenates this whole directory, so
1885
+ * moving the file would have shipped that scheduler into the published CLI
1886
+ * package. The validator moved; the scheduler stayed; `src/cron-parser.ts`
1887
+ * re-exports what moved, so no server import changed.
1888
+ *
1889
+ * The scheduler's two entry points are deliberately not NAMED here: a CLI
1890
+ * unit test asserts the artifact does not contain either identifier, which is
1891
+ * how "the artifact carries the validator only" is kept true mechanically
1892
+ * rather than by review.
1893
+ *
1894
+ * ── Not a rules module ───────────────────────────────────────────────────
1895
+ *
1896
+ * `parseCron` THROWS, eight different sentences, and `tests/unit/
1897
+ * cron-parser.test.ts` pins that it throws. A module exporting a `*_RULE_IDS`
1898
+ * list may not contain a `throw` outside its one refusal constructor
1899
+ * (`findUnanchoredRefusals`, #3373), so the rule that renders these answers as
1900
+ * refusals is a separate module, `cron-rules.ts`, and this one is a plain
1901
+ * shared-predicate module — the `webhook-signing-secret.ts` / `function-mode.ts`
1902
+ * precedent.
1903
+ *
1904
+ * Pure: no models, no tenant context, no `env` (see `types.ts` §Purity).
1905
+ * `Intl.DateTimeFormat` is the one platform call, and it is the timezone
1906
+ * question itself.
1907
+ *
1908
+ * Supported fields (standard POSIX order):
1909
+ * minute (0-59)
1910
+ * hour (0-23)
1911
+ * day-of-month (1-31)
1912
+ * month (1-12)
1913
+ * day-of-week (0-6, where 0 = Sunday; 7 also accepted as Sunday)
1914
+ *
1915
+ * Supported syntax per field:
1916
+ * - "*" any value
1917
+ * - "5" exact value
1918
+ * - "5-10" range (inclusive)
1919
+ * - "star/step" every N values within the full range (e.g. "* /5" => 0,5,10,...)
1920
+ * - "a-b/step" every N values within a range
1921
+ * - "1,2,3" comma-separated list of values or ranges
1922
+ *
1923
+ * Not supported: month/day names, "?", "L", "W", "#", last-day modifiers.
1924
+ */
1925
+ export interface ParsedCron {
1926
+ minutes: Set<number>;
1927
+ hours: Set<number>;
1928
+ daysOfMonth: Set<number>;
1929
+ months: Set<number>;
1930
+ daysOfWeek: Set<number>;
1931
+ dayOfMonthWild: boolean;
1932
+ dayOfWeekWild: boolean;
1933
+ }
1934
+ export declare function parseCron(expr: string): ParsedCron;
1935
+ /**
1936
+ * The message `parseCron` would throw for `expr`, or null when it parses.
1937
+ *
1938
+ * The same grammar, asked as a question instead of as an exception, because
1939
+ * both callers want the sentence rather than the control flow: the CLI's push
1940
+ * preflight collects one message per broken entry, and the standalone cron
1941
+ * routes render theirs into a 400 body. `expr` is typed `unknown` because it
1942
+ * arrives as an authored TOML value, where it can be anything at all.
1943
+ */
1944
+ export declare function cronExpressionError(expr: unknown): string | null;
1945
+ /**
1946
+ * Whether `Intl.DateTimeFormat` accepts `name` as a timezone.
1947
+ *
1948
+ * This is THE timezone check — before #3376 the identical probe was written
1949
+ * out three times (the function push boundary and the standalone create and
1950
+ * update). Each of those sites keeps its own sentence and its own defaulting;
1951
+ * what they now share is this answer. A non-string is false rather than a
1952
+ * throw, for the same reason as above.
1953
+ */
1954
+ export declare function isValidTimezone(name: unknown): boolean;
1955
+ /**
1956
+ * The cron schedule rules two runners share (#3376, project
1957
+ * `cli-server-rule-parity` phase 4; intent §Success criteria 6).
1958
+ *
1959
+ * ── One implementation, two runners ──────────────────────────────────────
1960
+ *
1961
+ * A cron expression the grammar refuses, and a timezone `Intl` does not know,
1962
+ * are both decidable from the authored file. Before this issue only the SERVER
1963
+ * decided them, inside the apply loop: a `functions/<key>.toml` carrying
1964
+ * either passed `config push`'s preflight, sibling entities were written, and
1965
+ * the function alone was refused — while the command promises that a refusal
1966
+ * means nothing was applied. Now `cli/src/lib/function-sync.ts` and
1967
+ * `src/services/function-trigger-validation.ts` both call the rule below, and
1968
+ * the sentence an author reads is the same on either side of the wire because
1969
+ * it is built in one place.
1970
+ *
1971
+ * ── Why this is a `*-rules.ts` module and the parser is not ──────────────
1972
+ *
1973
+ * The registry's unclaimed-export check (`findUnclaimedRuleModuleExports`)
1974
+ * keys on this name under `src/config-surface/`: every exported function here
1975
+ * is a rule, so one no surface declares fails `cli-unit-tests`. The anchor
1976
+ * also forbids a `throw` outside the one refusal constructor — and `parseCron`
1977
+ * throws eight different sentences, which `tests/unit/cron-parser.test.ts`
1978
+ * pins. So the grammar and the `Intl` probe live next door in
1979
+ * `cron-expression.ts`, which is a shared-predicate module rather than a rules
1980
+ * module, and this one only decides and renders.
1981
+ *
1982
+ * ── What is NOT shared ───────────────────────────────────────────────────
1983
+ *
1984
+ * The standalone cron routes ask the same two predicates and render their own
1985
+ * `Invalid cron expression: …` / `Invalid timezone: …`, with their own
1986
+ * defaulting (create reads an absent timezone as UTC, update does not). That
1987
+ * is intent assumption 2 in the form its own fallback describes, and the shape
1988
+ * `workflow-rules.ts` set in phase 2: one predicate, each site's message and
1989
+ * defaulting preserved. No refusal message changed anywhere.
1990
+ */
1991
+ /** The rule ids this module owns — the union type below anchors them (#3373). */
1992
+ export declare const CRON_TRIGGER_RULE_IDS: readonly ["cron-trigger-expression", "cron-trigger-timezone"];
1993
+ type CronTriggerRuleId = (typeof CRON_TRIGGER_RULE_IDS)[number];
1994
+ /** A refused cron entry: the rule and the sentence the site renders. */
1995
+ export type CronTriggerRuleRefusal = RuleRefusal<CronTriggerRuleId>;
1996
+ /**
1997
+ * One `[[function.triggers.cron]]` entry's schedule, or null.
1998
+ *
1999
+ * Expression first, then timezone, which is the order the push boundary ran
2000
+ * them in — so an entry broken both ways still answers with the expression's
2001
+ * refusal, exactly as before. An unset or empty `timezone` reads as UTC, again
2002
+ * exactly as before: the boundary's `entry.timezone || "UTC"`.
2003
+ */
2004
+ export declare function checkCronTriggerEntry(entry: {
2005
+ name: string;
2006
+ cron: string;
2007
+ timezone?: string;
2008
+ }): CronTriggerRuleRefusal | null;
2009
+ /**
2010
+ * The prompt config's reasoning controls, resolved onto each provider's own
2011
+ * spelling — issue #3358.
2012
+ *
2013
+ * A `[[configs]]` entry states EITHER `reasoningEffort` (a provider-neutral
2014
+ * level, `none` through `high`) or `reasoningBudget` (a token count), never
2015
+ * both. This module is the whole translation: every authored pair either maps
2016
+ * onto the provider's native key or is REFUSED with a message naming the
2017
+ * remedy. Nothing is dropped — that was the trap `providerConfig` already laid
2018
+ * in the tree (stored, serialized, round-tripped, and never read at execution),
2019
+ * and the issue's third success criterion is precisely that a config asking for
2020
+ * something a provider cannot express fails rather than silently doing nothing.
2021
+ *
2022
+ * Pure and dependency-free, like every module under `src/config-surface/`: the
2023
+ * server's create/update handlers, the executor, and the vendored CLI artifact
2024
+ * all read this one copy, so a refusal at push and a refusal at execution
2025
+ * cannot disagree.
2026
+ *
2027
+ * Provider facts this encodes:
2028
+ *
2029
+ * - OpenRouter takes a unified `reasoning` block: `{ effort }`,
2030
+ * `{ max_tokens }`, or `{ enabled: false }` to decline it. `max_tokens` is an
2031
+ * exact bound on budget-native models and is translated to the nearest
2032
+ * effort level on effort-only ones, so it is documented as a bound, not a
2033
+ * guarantee — `metrics.reasoningTokens` is the measure of what was spent.
2034
+ * Default routing lets an endpoint IGNORE a parameter it does not support,
2035
+ * so a request carrying `reasoning` also carries
2036
+ * `provider: { require_parameters: true }`: an endpoint that cannot honor it
2037
+ * is refused by OpenRouter and surfaces as a failed execution rather than a
2038
+ * silent drop.
2039
+ * - Gemini 3.x expresses thinking as a LEVEL
2040
+ * (`thinkingConfig.thinkingLevel`) and cannot turn it off; `thinkingBudget`
2041
+ * is accepted there only for backward compatibility and is translated to a
2042
+ * level rather than honored as a bound, so a numeric budget is refused with
2043
+ * a pointer at `reasoningEffort`.
2044
+ * - Gemini 2.5 expresses it as a numeric BUDGET
2045
+ * (`thinkingConfig.thinkingBudget`); `0` turns thinking off on Flash and
2046
+ * Flash-Lite, while Pro has a floor of 128 and answers 400 for less.
2047
+ * - Gemini 2.0 and earlier have no thinking control at all.
2048
+ */
2049
+ /** The provider-neutral effort levels a config may state. */
2050
+ export declare const REASONING_EFFORTS: readonly ["none", "minimal", "low", "medium", "high"];
2051
+ export type ReasoningEffort = (typeof REASONING_EFFORTS)[number];
2052
+ /** The smallest budget Gemini 2.5 Pro accepts; it cannot stop thinking. */
2053
+ export declare const GEMINI_PRO_MIN_THINKING_BUDGET = 128;
2054
+ export interface PromptReasoningInput {
2055
+ provider?: string | null;
2056
+ model?: string | null;
2057
+ reasoningEffort?: string | null;
2058
+ reasoningBudget?: number | string | null;
2059
+ }
2060
+ /** What the executor adds to the upstream request, or `null` for "unset". */
2061
+ export type PromptReasoningDirective = {
2062
+ provider: "openrouter";
2063
+ /** The OpenRouter `reasoning` block. */
2064
+ reasoning: Record<string, unknown>;
2065
+ /**
2066
+ * `provider: { require_parameters: true }` — routing restricted to
2067
+ * endpoints that support every parameter in the request.
2068
+ */
2069
+ requireParameters: true;
2070
+ } | {
2071
+ provider: "gemini";
2072
+ /** The `generationConfig.thinkingConfig` block. */
2073
+ thinkingConfig: Record<string, unknown>;
2074
+ };
2075
+ export type PromptReasoningResolution = {
2076
+ ok: true;
2077
+ directive: PromptReasoningDirective | null;
2078
+ } | {
2079
+ ok: false;
2080
+ error: string;
2081
+ };
2082
+ /**
2083
+ * How a Gemini model spells its thinking control.
2084
+ *
2085
+ * `level` — Gemini 3.x, `thinkingLevel`, cannot be turned off.
2086
+ * `budget` — Gemini 2.5, `thinkingBudget` in tokens.
2087
+ * `none` — Gemini 2.0 and earlier, no control at all.
2088
+ */
2089
+ export type GeminiReasoningFamily = "level" | "budget" | "none";
2090
+ export declare function geminiReasoningFamily(model: string): GeminiReasoningFamily;
2091
+ /** Gemini Pro: the tier that cannot stop thinking in either family. */
2092
+ export declare function isGeminiProModel(model: string): boolean;
2093
+ /** Is EITHER reasoning key stated on this config? */
2094
+ export declare function hasPromptReasoning(input: PromptReasoningInput): boolean;
2095
+ /**
2096
+ * Resolve an authored pair onto the provider's spelling.
2097
+ *
2098
+ * Returns `{ ok: true, directive: null }` when neither key is set — the case
2099
+ * that must leave the upstream payload byte-identical to what it was before
2100
+ * this issue — a directive when the pair maps, and `{ ok: false, error }` when
2101
+ * it does not. The error text is what `config push` reports as the file's
2102
+ * error and what the admin routes answer 400 with.
2103
+ */
2104
+ export declare function resolvePromptReasoning(input: PromptReasoningInput): PromptReasoningResolution;
1636
2105
  export declare const PROMPT_SURFACE: ConfigObjectSurface;
1637
2106
  export declare const INTEGRATION_SURFACE: ConfigObjectSurface;
1638
2107
  export declare const WEBHOOK_SURFACE: ConfigObjectSurface;
@@ -1753,3 +2222,4 @@ export declare function getConfigTable(label: string, tomlPath: readonly string[
1753
2222
  * names, in declaration order — the coverage guard's anchor (#2644 criterion 2).
1754
2223
  */
1755
2224
  export declare const GENERATED_CONFIG_MODEL_FIELDS: Readonly<Record<string, readonly string[]>>;
2225
+ export {};