primitive-admin 1.2.0-alpha.0 → 1.2.0-alpha.2

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 (137) hide show
  1. package/README.md +6 -4
  2. package/dist/bin/primitive.js +22 -24
  3. package/dist/bin/primitive.js.map +1 -1
  4. package/dist/src/commands/agent-sessions.d.ts +20 -0
  5. package/dist/src/commands/agent-sessions.js +275 -0
  6. package/dist/src/commands/agent-sessions.js.map +1 -0
  7. package/dist/src/commands/apps-children.d.ts +54 -0
  8. package/dist/src/commands/apps-children.js +578 -0
  9. package/dist/src/commands/apps-children.js.map +1 -0
  10. package/dist/src/commands/apps.js +88 -9
  11. package/dist/src/commands/apps.js.map +1 -1
  12. package/dist/src/commands/auth-sessions.d.ts +3 -3
  13. package/dist/src/commands/auth-sessions.js +117 -23
  14. package/dist/src/commands/auth-sessions.js.map +1 -1
  15. package/dist/src/commands/auth.js +248 -15
  16. package/dist/src/commands/auth.js.map +1 -1
  17. package/dist/src/commands/connections.js +1 -5
  18. package/dist/src/commands/connections.js.map +1 -1
  19. package/dist/src/commands/databases.js +7 -109
  20. package/dist/src/commands/databases.js.map +1 -1
  21. package/dist/src/commands/documents.d.ts +29 -0
  22. package/dist/src/commands/documents.js +676 -191
  23. package/dist/src/commands/documents.js.map +1 -1
  24. package/dist/src/commands/env.d.ts +9 -1
  25. package/dist/src/commands/env.js +158 -45
  26. package/dist/src/commands/env.js.map +1 -1
  27. package/dist/src/commands/functions.js +7 -1
  28. package/dist/src/commands/functions.js.map +1 -1
  29. package/dist/src/commands/init.js +2 -1
  30. package/dist/src/commands/init.js.map +1 -1
  31. package/dist/src/commands/integrations.js +14 -2
  32. package/dist/src/commands/integrations.js.map +1 -1
  33. package/dist/src/commands/prompts.js +4 -0
  34. package/dist/src/commands/prompts.js.map +1 -1
  35. package/dist/src/commands/scripts.js +1 -1
  36. package/dist/src/commands/scripts.js.map +1 -1
  37. package/dist/src/commands/sessions.js +3 -8
  38. package/dist/src/commands/sessions.js.map +1 -1
  39. package/dist/src/commands/sync-app-settings.d.ts +25 -7
  40. package/dist/src/commands/sync-app-settings.js +81 -20
  41. package/dist/src/commands/sync-app-settings.js.map +1 -1
  42. package/dist/src/commands/sync.d.ts +183 -12
  43. package/dist/src/commands/sync.js +974 -452
  44. package/dist/src/commands/sync.js.map +1 -1
  45. package/dist/src/commands/tokens.js +1 -6
  46. package/dist/src/commands/tokens.js.map +1 -1
  47. package/dist/src/commands/users.js +77 -2
  48. package/dist/src/commands/users.js.map +1 -1
  49. package/dist/src/lib/api-client.d.ts +161 -35
  50. package/dist/src/lib/api-client.js +210 -86
  51. package/dist/src/lib/api-client.js.map +1 -1
  52. package/dist/src/lib/app-settings-descriptor.d.ts +9 -1
  53. package/dist/src/lib/app-settings-descriptor.js +22 -10
  54. package/dist/src/lib/app-settings-descriptor.js.map +1 -1
  55. package/dist/src/lib/auth-flow.d.ts +41 -1
  56. package/dist/src/lib/auth-flow.js +45 -16
  57. package/dist/src/lib/auth-flow.js.map +1 -1
  58. package/dist/src/lib/child-apps-local.d.ts +184 -0
  59. package/dist/src/lib/child-apps-local.js +282 -0
  60. package/dist/src/lib/child-apps-local.js.map +1 -0
  61. package/dist/src/lib/ci-session-request.d.ts +22 -0
  62. package/dist/src/lib/ci-session-request.js +51 -0
  63. package/dist/src/lib/ci-session-request.js.map +1 -0
  64. package/dist/src/lib/codegen-shared/generatedFiles.d.ts +4 -1
  65. package/dist/src/lib/codegen-shared/generatedFiles.js +19 -13
  66. package/dist/src/lib/codegen-shared/generatedFiles.js.map +1 -1
  67. package/dist/src/lib/config-object-descriptor.js +5 -5
  68. package/dist/src/lib/config-object-descriptor.js.map +1 -1
  69. package/dist/src/lib/config-payload.js +2 -3
  70. package/dist/src/lib/config-payload.js.map +1 -1
  71. package/dist/src/lib/config-surface.d.ts +2 -1
  72. package/dist/src/lib/config-surface.js +7 -28
  73. package/dist/src/lib/config-surface.js.map +1 -1
  74. package/dist/src/lib/credentials-store.d.ts +17 -2
  75. package/dist/src/lib/credentials-store.js +72 -6
  76. package/dist/src/lib/credentials-store.js.map +1 -1
  77. package/dist/src/lib/document-export-lookups.d.ts +31 -0
  78. package/dist/src/lib/document-export-lookups.js +53 -0
  79. package/dist/src/lib/document-export-lookups.js.map +1 -0
  80. package/dist/src/lib/document-export-permissions.d.ts +7 -5
  81. package/dist/src/lib/document-export-permissions.js +7 -5
  82. package/dist/src/lib/document-export-permissions.js.map +1 -1
  83. package/dist/src/lib/env-resolver-core.d.ts +69 -1
  84. package/dist/src/lib/env-resolver-core.js +137 -20
  85. package/dist/src/lib/env-resolver-core.js.map +1 -1
  86. package/dist/src/lib/env-resolver.d.ts +14 -2
  87. package/dist/src/lib/env-resolver.js +32 -10
  88. package/dist/src/lib/env-resolver.js.map +1 -1
  89. package/dist/src/lib/function-run.d.ts +4 -1
  90. package/dist/src/lib/function-run.js +24 -4
  91. package/dist/src/lib/function-run.js.map +1 -1
  92. package/dist/src/lib/function-sync.d.ts +5 -2
  93. package/dist/src/lib/function-sync.js +7 -4
  94. package/dist/src/lib/function-sync.js.map +1 -1
  95. package/dist/src/lib/generated-config-surfaces.d.ts +49 -50
  96. package/dist/src/lib/generated-config-surfaces.js +283 -166
  97. package/dist/src/lib/generated-config-surfaces.js.map +1 -1
  98. package/dist/src/lib/generated-sdk-types.d.ts +1 -1
  99. package/dist/src/lib/generated-sdk-types.js +1 -1
  100. package/dist/src/lib/generated-sdk-types.js.map +1 -1
  101. package/dist/src/lib/init-adopt.js +12 -3
  102. package/dist/src/lib/init-adopt.js.map +1 -1
  103. package/dist/src/lib/local-state.d.ts +17 -1
  104. package/dist/src/lib/local-state.js +94 -15
  105. package/dist/src/lib/local-state.js.map +1 -1
  106. package/dist/src/lib/local-test-cases.d.ts +5 -2
  107. package/dist/src/lib/local-test-cases.js +7 -4
  108. package/dist/src/lib/local-test-cases.js.map +1 -1
  109. package/dist/src/lib/log-inspection.d.ts +25 -0
  110. package/dist/src/lib/log-inspection.js +24 -0
  111. package/dist/src/lib/log-inspection.js.map +1 -1
  112. package/dist/src/lib/logout-admin-session.d.ts +2 -1
  113. package/dist/src/lib/logout-admin-session.js +7 -1
  114. package/dist/src/lib/logout-admin-session.js.map +1 -1
  115. package/dist/src/lib/pull-write.d.ts +31 -0
  116. package/dist/src/lib/pull-write.js +49 -0
  117. package/dist/src/lib/pull-write.js.map +1 -0
  118. package/dist/src/lib/root-import-decision.d.ts +59 -0
  119. package/dist/src/lib/root-import-decision.js +95 -0
  120. package/dist/src/lib/root-import-decision.js.map +1 -0
  121. package/dist/src/lib/scope-request.d.ts +69 -0
  122. package/dist/src/lib/scope-request.js +188 -0
  123. package/dist/src/lib/scope-request.js.map +1 -0
  124. package/dist/src/lib/session-id.d.ts +9 -0
  125. package/dist/src/lib/session-id.js +28 -0
  126. package/dist/src/lib/session-id.js.map +1 -0
  127. package/dist/src/lib/snapshots.d.ts +17 -1
  128. package/dist/src/lib/snapshots.js +50 -2
  129. package/dist/src/lib/snapshots.js.map +1 -1
  130. package/dist/src/lib/sync-paths.d.ts +35 -0
  131. package/dist/src/lib/sync-paths.js +57 -5
  132. package/dist/src/lib/sync-paths.js.map +1 -1
  133. package/dist/src/lib/transient-retry.d.ts +34 -0
  134. package/dist/src/lib/transient-retry.js +57 -0
  135. package/dist/src/lib/transient-retry.js.map +1 -0
  136. package/dist/src/types/index.d.ts +21 -4
  137. package/package.json +4 -3
@@ -94,8 +94,8 @@ export function writableFieldNames(table, mode) {
94
94
  * The field names the server PERSISTS for this table in `mode`: the writable
95
95
  * fields, plus the `deprecated` classifications — "still writable, superseded
96
96
  * by another field" is what that kind means (`types.ts`), so a superseded key
97
- * a handler still honors (`accessPolicy`, `passkeyRpId`) belongs in every
98
- * accepted set, and a field that is NOT writable belongs in none of them.
97
+ * a handler still honors (`passkeyRpId`) belongs in every accepted set, and a
98
+ * field that is NOT writable belongs in none of them.
99
99
  *
100
100
  * Stated once here because three consumers need the same answer: the generated
101
101
  * request schemas, `PUT /settings`'s write allow-list, and the guards.
@@ -401,8 +401,9 @@ export function acceptedGroupTomlKeys(table, group) {
401
401
  * is therefore an unknown key rather than one the payload builder silently
402
402
  * ignores (SO3626-013) — the builder reads a grouped field only from
403
403
  * `source[group][tomlKey]`, so accepting it here would drop the author's value
404
- * without a word. The nine deprecated flat chat keys stay accepted through
405
- * their `tomlOnlyKeys` declarations, which is what keeps old files pushing.
404
+ * without a word. A key whose root spelling was retired while it moved into a
405
+ * group (the flat chat keys, #3642) is refused the same way, with the guidance
406
+ * `RETIRED_TOML_SPELLINGS` states.
406
407
  */
407
408
  export function acceptedTomlKeys(table) {
408
409
  return new Set([
@@ -2343,9 +2344,11 @@ export const RETIRED_CONFIG_KEYS = {
2343
2344
  "names.",
2344
2345
  },
2345
2346
  // #2884 — email sign-in became ONE flow, so it has ONE setting. Both keys
2346
- // stay DECLARED in `models.yaml` and are mirrored on every write of
2347
- // `emailSignInEnabled`, but neither is authorable any more: a file that
2348
- // still carries one is asking for a method choice that no longer exists.
2347
+ // stay DECLARED in `models.yaml` as hidden attributes (#3993): never
2348
+ // written, never echoed, refused by `config push`, the tenant PUT and the
2349
+ // admin API alike, and read only by the fallback for a row that stores no
2350
+ // `emailSignInEnabled`. A file or body that still carries one is asking
2351
+ // for a method choice that no longer exists.
2349
2352
  magicLinkEnabled: {
2350
2353
  toml: "`magicLinkEnabled` is no longer authored in TOML (#2884). Email " +
2351
2354
  "sign-in is one flow — one request sends one email carrying a code " +
@@ -2363,6 +2366,27 @@ export const RETIRED_CONFIG_KEYS = {
2363
2366
  "send `emailSignInEnabled`.",
2364
2367
  },
2365
2368
  },
2369
+ // #3996 — a bucket's access is `preset` (#1020). The old spelling stayed
2370
+ // accepted as an alias, which left two vocabularies for one decision —
2371
+ // `owner-only` still read as "the uploader", the bug #1020 fixed — and an
2372
+ // echo whose value depended on the row's age. A file or body that still
2373
+ // carries it is refused rather than mapped, so the author meets the new
2374
+ // vocabulary once. The model attribute stays declared: a pre-#1020 row
2375
+ // still resolves to its preset (`resolveBucketPreset`).
2376
+ bucket: {
2377
+ accessPolicy: {
2378
+ toml: "`accessPolicy` is no longer authored in TOML (#3996): a bucket's " +
2379
+ "access is `preset`. Map the value — `public-read` → `public`, " +
2380
+ "`authenticated` → `authenticated`, `owner-only` → `admin-only` (it " +
2381
+ "always meant app admins; `personal-uploads` is the per-uploader " +
2382
+ "preset). Replace the line and push again, or `config pull` rewrites " +
2383
+ "the file.",
2384
+ request: "`accessPolicy` is retired (#3996): send `preset`. `public-read` → " +
2385
+ "`public`, `authenticated` → `authenticated`, `owner-only` → " +
2386
+ "`admin-only` (app admins; `personal-uploads` is the per-uploader " +
2387
+ "preset).",
2388
+ },
2389
+ },
2366
2390
  // #2645 — pausing moved off `state` onto its own field, so `state` stopped
2367
2391
  // being author-writable rather than becoming a second way to say "paused".
2368
2392
  // Every pre-#2645 pull wrote the line, so existing repos hit this on their
@@ -2520,6 +2544,47 @@ export const RETIRED_CONFIG_KEYS = {
2520
2544
  "Omit the field.",
2521
2545
  },
2522
2546
  },
2547
+ // #3998 — a test-case sidecar names what it pins: the configuration it runs
2548
+ // under and its judge prompt, by name, so the file means the same thing in
2549
+ // every app. The id spellings were deprecated TOML-only keys that won over
2550
+ // the name, and — unlike every other retired key — `config pull` still WROTE
2551
+ // them whenever its id→name lookup missed, so the spelling kept being
2552
+ // generated. Pull now reports such a case instead of writing an id.
2553
+ //
2554
+ // The three stay the stored references the admin API accepts on create and
2555
+ // update, so the `request` wording below is never shown: `rememberRetiredKeys`
2556
+ // and the admin API's unknown-key check both skip a key the wire accepts. It
2557
+ // is here because the entry's shape requires it.
2558
+ test: {
2559
+ configId: {
2560
+ toml: "`configId` is no longer authored in TOML (#3998): a test case names " +
2561
+ "the configuration it runs under, so the file means the same thing in " +
2562
+ "every app. Replace the line with `configName = \"…\"` (a " +
2563
+ "`[[configs]]` name of the block this sidecar belongs to), or delete it " +
2564
+ "to run the active configuration, and push again.",
2565
+ request: "`configId` is the stored configuration reference; only its TOML " +
2566
+ "spelling is retired (#3998). Sidecars author it as `configName`.",
2567
+ },
2568
+ evaluatorPromptId: {
2569
+ toml: "`evaluatorPromptId` is no longer authored in TOML (#3998): a test " +
2570
+ "case names its judge prompt, so the file means the same thing in " +
2571
+ "every app. Replace the line with `evaluatorPromptKey = \"…\"` (the " +
2572
+ "judge prompt's key) and push again.",
2573
+ request: "`evaluatorPromptId` is the stored judge-prompt reference; only its " +
2574
+ "TOML spelling is retired (#3998). Sidecars author it as " +
2575
+ "`evaluatorPromptKey`.",
2576
+ },
2577
+ evaluatorConfigId: {
2578
+ toml: "`evaluatorConfigId` is no longer authored in TOML (#3998): a test " +
2579
+ "case names its judge's configuration, so the file means the same " +
2580
+ "thing in every app. Replace the line with " +
2581
+ "`evaluatorConfigName = \"…\"` (a `[[configs]]` name of the prompt " +
2582
+ "`evaluatorPromptKey` names) and push again.",
2583
+ request: "`evaluatorConfigId` is the stored judge-configuration reference; only " +
2584
+ "its TOML spelling is retired (#3998). Sidecars author it as " +
2585
+ "`evaluatorConfigName`.",
2586
+ },
2587
+ },
2523
2588
  // #2803 — availability is one server-owned `status`. As a TOML key it made
2524
2589
  // `config push` a second writer: pushing a file pulled before an operator ran
2525
2590
  // `workflows disable` put the workflow back in service as a side effect.
@@ -2539,6 +2604,47 @@ export const RETIRED_CONFIG_KEYS = {
2539
2604
  export function retiredConfigKey(prefix, key) {
2540
2605
  return RETIRED_CONFIG_KEYS[prefix]?.[key] ?? null;
2541
2606
  }
2607
+ // #3642 — the pre-#3626 flat spelling of one chat setting. The six keys an
2608
+ // agent's model round shares (`AGENT_CONFIG_KEYS`) also name the agent block.
2609
+ // The names are written out rather than read from `prompt-kinds.ts`, which the
2610
+ // vendored artifact places after this module.
2611
+ function flatChatKeySpelling(key, sharedWithAgent) {
2612
+ return {
2613
+ toml: `\`${key}\` is no longer authored flat under \`[[configs]]\` (#3642). ` +
2614
+ "Chat settings live in the entry's `[configs.chat]` block" +
2615
+ (sharedWithAgent ? " (`[configs.agent]` on an agent prompt)" : "") +
2616
+ ": move the line into that block and push again. `config pull` " +
2617
+ "rewrites a file whose prompt already exists on the server.",
2618
+ };
2619
+ }
2620
+ /**
2621
+ * Spellings retired from TOML while the key itself stays live on the wire,
2622
+ * keyed like `RETIRED_CONFIG_KEYS`. The request side never reads this map: a
2623
+ * body that sends one of these keys is a body the schema accepts.
2624
+ */
2625
+ export const RETIRED_TOML_SPELLINGS = {
2626
+ // #3642 — the nine chat keys at the prompt `[[configs]]` root. The admin
2627
+ // API's create and update bodies still carry them flat; only the TOML
2628
+ // grouping (#3626) moved them under `[configs.chat]`.
2629
+ configs: {
2630
+ systemPrompt: flatChatKeySpelling("systemPrompt", true),
2631
+ userPromptTemplate: flatChatKeySpelling("userPromptTemplate", false),
2632
+ temperature: flatChatKeySpelling("temperature", true),
2633
+ topP: flatChatKeySpelling("topP", true),
2634
+ maxTokens: flatChatKeySpelling("maxTokens", true),
2635
+ outputFormat: flatChatKeySpelling("outputFormat", false),
2636
+ outputSchema: flatChatKeySpelling("outputSchema", false),
2637
+ reasoningEffort: flatChatKeySpelling("reasoningEffort", true),
2638
+ reasoningBudget: flatChatKeySpelling("reasoningBudget", true),
2639
+ },
2640
+ };
2641
+ /**
2642
+ * The TOML guidance for `key` under `prefix`: a fully retired key's, else a
2643
+ * retired spelling's, else null when the key is simply unknown.
2644
+ */
2645
+ export function retiredTomlKey(prefix, key) {
2646
+ return (retiredConfigKey(prefix, key) ?? RETIRED_TOML_SPELLINGS[prefix]?.[key] ?? null);
2647
+ }
2542
2648
  // ── src/config-surface/function-grants.ts ────────────────────────────────
2543
2649
  /**
2544
2650
  * The capability grammar for server functions — #3182 phase 1, rewritten by
@@ -4712,7 +4818,12 @@ export function checkCronTriggerEntry(entry) {
4712
4818
  * all read this one copy, so a refusal at push and a refusal at execution
4713
4819
  * cannot disagree.
4714
4820
  *
4715
- * Provider facts this encodes:
4821
+ * What this encodes is each provider's SPELLING, per model family — never
4822
+ * which values a particular model accepts (#4147). A value the spelling can
4823
+ * carry is sent, and a model that does not support it is refused by the
4824
+ * provider, whose answer the run reports. A per-model table of accepted levels
4825
+ * and floors went stale with each model release and refused or admitted the
4826
+ * wrong values.
4716
4827
  *
4717
4828
  * - OpenRouter takes a unified `reasoning` block: `{ effort }`,
4718
4829
  * `{ max_tokens }`, or `{ enabled: false }` to decline it. `max_tokens` is an
@@ -4725,13 +4836,13 @@ export function checkCronTriggerEntry(entry) {
4725
4836
  * is refused by OpenRouter and surfaces as a failed execution rather than a
4726
4837
  * silent drop.
4727
4838
  * - Gemini 3.x expresses thinking as a LEVEL
4728
- * (`thinkingConfig.thinkingLevel`) and cannot turn it off; `thinkingBudget`
4729
- * is accepted there only for backward compatibility and is translated to a
4730
- * level rather than honored as a bound, so a numeric budget is refused with
4731
- * a pointer at `reasoningEffort`.
4839
+ * (`thinkingConfig.thinkingLevel`), and the level spelling has no "off"
4840
+ * value, so `none` has nothing to translate to. `thinkingBudget` is accepted
4841
+ * there only for backward compatibility and is translated to a level rather
4842
+ * than honored as a bound, so a numeric budget is refused with a pointer at
4843
+ * `reasoningEffort`.
4732
4844
  * - Gemini 2.5 expresses it as a numeric BUDGET
4733
- * (`thinkingConfig.thinkingBudget`); `0` turns thinking off on Flash and
4734
- * Flash-Lite, while Pro has a floor of 128 and answers 400 for less.
4845
+ * (`thinkingConfig.thinkingBudget`); `none` is a budget of `0`.
4735
4846
  * - Gemini 2.0 and earlier have no thinking control at all.
4736
4847
  */
4737
4848
  /** The provider-neutral effort levels a config may state. */
@@ -4742,8 +4853,6 @@ export const REASONING_EFFORTS = [
4742
4853
  "medium",
4743
4854
  "high",
4744
4855
  ];
4745
- /** The smallest budget Gemini 2.5 Pro accepts; it cannot stop thinking. */
4746
- export const GEMINI_PRO_MIN_THINKING_BUDGET = 128;
4747
4856
  export function geminiReasoningFamily(model) {
4748
4857
  const match = /gemini-(\d+)(?:[.-](\d+))?/i.exec(model || "");
4749
4858
  if (!match)
@@ -4758,10 +4867,6 @@ export function geminiReasoningFamily(model) {
4758
4867
  return "budget";
4759
4868
  return "none";
4760
4869
  }
4761
- /** Gemini Pro: the tier that cannot stop thinking in either family. */
4762
- export function isGeminiProModel(model) {
4763
- return /(^|[-_/.])pro($|[-_/.])/i.test(model || "");
4764
- }
4765
4870
  /** Is EITHER reasoning key stated on this config? */
4766
4871
  export function hasPromptReasoning(input) {
4767
4872
  return (normalizeEffort(input.reasoningEffort) !== null ||
@@ -4845,7 +4950,6 @@ function openRouterDirective(effort, budget) {
4845
4950
  }
4846
4951
  function geminiResolution(model, effort, budget) {
4847
4952
  const family = geminiReasoningFamily(model);
4848
- const isPro = isGeminiProModel(model);
4849
4953
  if (family === "none") {
4850
4954
  return {
4851
4955
  ok: false,
@@ -4868,15 +4972,10 @@ function geminiResolution(model, effort, budget) {
4868
4972
  if (effort === "none") {
4869
4973
  return {
4870
4974
  ok: false,
4871
- error: `Gemini model '${model}' cannot turn thinking off. The lowest level it ` +
4872
- `accepts is '${isPro ? "low" : "minimal"}'.`,
4873
- };
4874
- }
4875
- if (isPro && (effort === "minimal" || effort === "medium")) {
4876
- return {
4877
- ok: false,
4878
- error: `Gemini Pro model '${model}' accepts only 'low' or 'high' as a ` +
4879
- `thinking level; '${effort}' is a Flash-only level.`,
4975
+ error: `Gemini model '${model}' expresses reasoning as a LEVEL, which has no ` +
4976
+ "'none' value. Use reasoningEffort " +
4977
+ `(${REASONING_EFFORTS.filter((e) => e !== "none").join(", ")}); the ` +
4978
+ "provider's model documentation lists which levels this model accepts.",
4880
4979
  };
4881
4980
  }
4882
4981
  return {
@@ -4897,14 +4996,6 @@ function geminiResolution(model, effort, budget) {
4897
4996
  };
4898
4997
  }
4899
4998
  const resolved = effort === "none" ? 0 : budget;
4900
- if (isPro && resolved < GEMINI_PRO_MIN_THINKING_BUDGET) {
4901
- return {
4902
- ok: false,
4903
- error: `Gemini Pro model '${model}' cannot turn thinking off: its smallest ` +
4904
- `thinking budget is ${GEMINI_PRO_MIN_THINKING_BUDGET} tokens, and ` +
4905
- `${resolved} is refused by the provider.`,
4906
- };
4907
- }
4908
4999
  return {
4909
5000
  ok: true,
4910
5001
  directive: {
@@ -4944,12 +5035,16 @@ function geminiResolution(model, effort, budget) {
4944
5035
  */
4945
5036
  export const PROMPT_KINDS = ["chat", "decisions", "agent"];
4946
5037
  /**
4947
- * The nine chat settings that existed before `[configs.chat]` did. Only these
4948
- * keep the deprecated FLAT spelling (#3642): a key added to the block later was
4949
- * never written flat by any file, so it has none (the `questions` posture,
4950
- * SO3626-013).
5038
+ * The settings that describe a CHAT COMPLETION and nothing else. They are
5039
+ * authored under `[configs.chat]` and only there: the pre-#3626 flat spelling
5040
+ * of the first nine is retired and refused at push (#3642,
5041
+ * `RETIRED_TOML_SPELLINGS`).
5042
+ *
5043
+ * #3801 — `strictOutput`, the opt-in that sends `[prompt].outputSchema` as a
5044
+ * strict `json_schema` on OpenRouter (`prompt-strict-output.ts`). Not an agent
5045
+ * key (§D1: an agent has no output schema), so the agent arm refuses it.
4951
5046
  */
4952
- export const LEGACY_FLAT_CHAT_KEYS = [
5047
+ export const CHAT_CONFIG_KEYS = [
4953
5048
  "systemPrompt",
4954
5049
  "userPromptTemplate",
4955
5050
  "temperature",
@@ -4959,18 +5054,6 @@ export const LEGACY_FLAT_CHAT_KEYS = [
4959
5054
  "outputSchema",
4960
5055
  "reasoningEffort",
4961
5056
  "reasoningBudget",
4962
- ];
4963
- /**
4964
- * The settings that describe a CHAT COMPLETION and nothing else. They are
4965
- * authored under `[configs.chat]`; the flat spelling of the nine legacy keys
4966
- * is deprecated (#3642).
4967
- *
4968
- * #3801 — `strictOutput`, the opt-in that sends `[prompt].outputSchema` as a
4969
- * strict `json_schema` on OpenRouter (`prompt-strict-output.ts`). Not an agent
4970
- * key (§D1: an agent has no output schema), so the agent arm refuses it.
4971
- */
4972
- export const CHAT_CONFIG_KEYS = [
4973
- ...LEGACY_FLAT_CHAT_KEYS,
4974
5057
  "strictOutput",
4975
5058
  ];
4976
5059
  /** The settings that describe a DECISIONS request. */
@@ -5406,45 +5489,6 @@ export function resolveDecisionsRunQuestions(questions, runCriteria) {
5406
5489
  }
5407
5490
  return { ok: true, questions: resolved };
5408
5491
  }
5409
- /**
5410
- * Move the deprecated flat chat keys of one `[[configs]]` entry under `chat`
5411
- * — #3626 §4, the blob-bucket `accessPolicy` precedent made reusable.
5412
- *
5413
- * Push keeps accepting the nine keys flat so no existing file breaks, and pull
5414
- * rewrites them, so one pull-then-push migrates a file. A key present in BOTH
5415
- * spellings is not merged: the grouped value is left standing and the key is
5416
- * reported in `conflicts`, which the caller turns into a refusal naming the
5417
- * entry. Nothing else in the entry is touched — `providerConfig` is
5418
- * provider-keyed rather than chat-keyed (D3626-002) and `questions` has no flat
5419
- * spelling at all (SO3626-013).
5420
- */
5421
- export function hoistDeprecatedChatKeys(entry) {
5422
- if (!isPlainObject(entry)) {
5423
- return { entry: {}, moved: [], conflicts: [] };
5424
- }
5425
- const out = { ...entry };
5426
- const existingGroup = isPlainObject(out.chat) ? { ...out.chat } : undefined;
5427
- const group = existingGroup ?? {};
5428
- const moved = [];
5429
- const conflicts = [];
5430
- // #3801 — only the legacy keys have a flat spelling to hoist; `strictOutput`
5431
- // at the root is an unknown key naming its block.
5432
- for (const key of LEGACY_FLAT_CHAT_KEYS) {
5433
- if (!(key in out))
5434
- continue;
5435
- if (existingGroup && key in existingGroup) {
5436
- conflicts.push(key);
5437
- delete out[key];
5438
- continue;
5439
- }
5440
- group[key] = out[key];
5441
- delete out[key];
5442
- moved.push(key);
5443
- }
5444
- if (existingGroup || moved.length > 0)
5445
- out.chat = group;
5446
- return { entry: out, moved: moved.sort(), conflicts: conflicts.sort() };
5447
- }
5448
5492
  // ── src/config-surface/prompt-strict-output.ts ───────────────────────────
5449
5493
  /**
5450
5494
  * Strict output for a chat config — issue #3801 (project `agents`, §D6).
@@ -5655,16 +5699,20 @@ export const AGENT_ERROR_CODES = [
5655
5699
  "AGENT_ROUND_EMPTY",
5656
5700
  ];
5657
5701
  /**
5658
- * The platform maximums. An agent may lower `maxSteps` and `waitLimitSeconds`,
5659
- * never raise them; the tool and event counts bound what one declaration can
5660
- * make every turn carry (principle 2).
5702
+ * The platform maximums. An agent may lower `maxSteps`, `waitLimitSeconds`
5703
+ * and `maxRowBytes`, never raise them; the tool and event counts bound what
5704
+ * one declaration can make every turn carry (principle 2).
5661
5705
  */
5662
5706
  export const AGENT_LIMITS = {
5663
5707
  maxSteps: 25,
5664
5708
  waitLimitSeconds: 604800,
5709
+ /** The JSON bytes of one session-document row a turn writes from model or tool output. */
5710
+ maxRowBytes: 262144,
5665
5711
  tools: 64,
5666
5712
  events: 64,
5667
5713
  };
5714
+ /** The smallest row ceiling an agent may declare: room for a call's own columns. */
5715
+ export const AGENT_ROW_BYTES_FLOOR = 1024;
5668
5716
  /**
5669
5717
  * A tool or event name: the intersection of OpenRouter's and Gemini's
5670
5718
  * function-name rules, so one declaration maps onto either provider.
@@ -5677,6 +5725,7 @@ const AGENT_TOP_LEVEL_KEYS = [
5677
5725
  "answeredBy",
5678
5726
  "maxSteps",
5679
5727
  "waitLimitSeconds",
5728
+ "maxRowBytes",
5680
5729
  "turnContext",
5681
5730
  "history",
5682
5731
  "tools",
@@ -5767,13 +5816,13 @@ function agentSchemaError(value, path) {
5767
5816
  return agentInvalid(path, `must be a table (a JSON Schema object); found ${found}. Write it as ` +
5768
5817
  "TOML tables, or as an object inside a block written as one JSON string");
5769
5818
  }
5770
- function agentIntegerInRange(value, path, max, out) {
5819
+ function agentIntegerInRange(value, path, max, out, min = 1) {
5771
5820
  if (typeof value !== "number" || !Number.isInteger(value)) {
5772
- out.push(agentInvalid(path, `must be an integer from 1 to ${max}`));
5821
+ out.push(agentInvalid(path, `must be an integer from ${min} to ${max}`));
5773
5822
  return;
5774
5823
  }
5775
- if (value < 1 || value > max) {
5776
- out.push(agentInvalid(path, `must be from 1 to ${max} (the platform maximum); found ${value}`));
5824
+ if (value < min || value > max) {
5825
+ out.push(agentInvalid(path, `must be from ${min} to ${max} (the platform maximum); found ${value}`));
5777
5826
  }
5778
5827
  }
5779
5828
  function checkAgentName(value, path, seen, what, out) {
@@ -5893,6 +5942,9 @@ function agentShapeRefusals(agent) {
5893
5942
  if (agent.waitLimitSeconds !== undefined) {
5894
5943
  agentIntegerInRange(agent.waitLimitSeconds, "waitLimitSeconds", AGENT_LIMITS.waitLimitSeconds, out);
5895
5944
  }
5945
+ if (agent.maxRowBytes !== undefined) {
5946
+ agentIntegerInRange(agent.maxRowBytes, "maxRowBytes", AGENT_LIMITS.maxRowBytes, out, AGENT_ROW_BYTES_FLOOR);
5947
+ }
5896
5948
  if (agent.turnContext !== undefined) {
5897
5949
  const turnContext = agent.turnContext;
5898
5950
  if (!agentIsPlainObject(turnContext)) {
@@ -6125,6 +6177,7 @@ export function normalizeAgentDeclaration(declaration) {
6125
6177
  answeredBy: source.answeredBy ?? "initiator",
6126
6178
  maxSteps: source.maxSteps ?? AGENT_LIMITS.maxSteps,
6127
6179
  waitLimitSeconds: source.waitLimitSeconds ?? AGENT_LIMITS.waitLimitSeconds,
6180
+ maxRowBytes: source.maxRowBytes ?? AGENT_LIMITS.maxRowBytes,
6128
6181
  turnContext: source.turnContext ?? null,
6129
6182
  history: source.history ?? null,
6130
6183
  tools: (source.tools ?? []).map((tool) => ({
@@ -6869,28 +6922,6 @@ const PROMPT_HANDLER = "src/admin-api.ts";
6869
6922
  * and output keys (`AGENT_CONFIG_KEYS`).
6870
6923
  */
6871
6924
  const SHARED_WITH_AGENT = ["chat", "agent"];
6872
- /**
6873
- * #3626 §4 — the nine chat keys, still accepted FLAT under `[[configs]]`.
6874
- *
6875
- * The blob-bucket `accessPolicy` precedent: declared `deprecated`, so
6876
- * `config push` keeps taking a file written before the `[configs.chat]` block
6877
- * existed, while `config pull` never writes the spelling again. One
6878
- * pull-then-push migrates a file. `hoistDeprecatedChatKeys` moves them into the
6879
- * group before the payload is built; rejecting the flat spelling outright is
6880
- * #3642. #3801 — only the nine: a chat key added later (`strictOutput`) was
6881
- * never written flat, so it has no deprecated spelling to accept.
6882
- */
6883
- const DEPRECATED_FLAT_CHAT_KEYS = Object.fromEntries(LEGACY_FLAT_CHAT_KEYS.map((key) => [
6884
- key,
6885
- {
6886
- kind: "deprecated",
6887
- note: `#3626 — the pre-kind spelling of \`[configs.chat].${key}\`. Still ` +
6888
- "accepted flat so no existing prompt file breaks (push hoists it " +
6889
- "into the block and warns); `config pull` writes only the grouped " +
6890
- "spelling, so one pull-then-push migrates the file. Rejecting the " +
6891
- "flat spelling is #3642.",
6892
- },
6893
- ]));
6894
6925
  export const PROMPT_SURFACE = {
6895
6926
  label: "prompt",
6896
6927
  tables: [
@@ -7100,7 +7131,7 @@ export const PROMPT_SURFACE = {
7100
7131
  note: "#3626 — the chat-completion settings of a `kind = \"chat\"` " +
7101
7132
  "prompt: the nine keys that describe a chat call and nothing " +
7102
7133
  "else. Authored under `[configs.chat]`; the flat spelling is " +
7103
- "accepted as deprecated (#3642) and rewritten by `config pull`.",
7134
+ "retired (#3642) and refused at push with `RETIRED_TOML_SPELLINGS`.",
7104
7135
  },
7105
7136
  decisions: {
7106
7137
  note: "#3626 — the settings of a `kind = \"decisions\"` prompt: the " +
@@ -7385,8 +7416,6 @@ export const PROMPT_SURFACE = {
7385
7416
  "active entry; push enacts it through the activate endpoint " +
7386
7417
  "(activatePromptConfig), never as a config-body key.",
7387
7418
  },
7388
- // #3626 §4 — the nine chat keys in their pre-kind FLAT spelling.
7389
- ...DEPRECATED_FLAT_CHAT_KEYS,
7390
7419
  },
7391
7420
  responseOnlyKeys: {},
7392
7421
  },
@@ -8213,9 +8242,12 @@ export const CRON_TRIGGER_SURFACE = {
8213
8242
  * `blob-buckets/<key>.toml` carries a single `[bucket]` table.
8214
8243
  *
8215
8244
  * `ttlTier` is `writableOn: ["create"]`: the update handler does not read it, so
8216
- * the retention tier is fixed at creation. `preset` and `ruleSetId` are the two
8217
- * halves of the access model (#1020) — at most one may change per request, which
8218
- * is the controller's rule and stays there.
8245
+ * the retention tier is fixed at creation. `preset` and the rule set are the two
8246
+ * halves of the access model (#1020), and the only access keys: at most one may
8247
+ * change per request, which is the controller's rule and stays there. The rule
8248
+ * set is authored by name (`ruleSetName`) and sent as `ruleSetId`, as the type
8249
+ * configs do (#4116); the id spelling in TOML is deprecated. The pre-#1020
8250
+ * `accessPolicy` spelling is a retired key (#3996, `retired-keys.ts`).
8219
8251
  */
8220
8252
  const BLOB_BUCKET_HANDLER = "src/app-api/controllers/blob-buckets-controller.ts";
8221
8253
  export const BLOB_BUCKET_SURFACE = {
@@ -8264,12 +8296,14 @@ export const BLOB_BUCKET_SURFACE = {
8264
8296
  },
8265
8297
  {
8266
8298
  field: "ruleSetId",
8267
- tomlKey: "ruleSetId",
8299
+ tomlKey: "ruleSetName",
8268
8300
  type: "string",
8269
8301
  emit: "whenSet",
8270
8302
  writableOn: BOTH,
8271
- // Existence-checked against AccessRuleSet; mutually exclusive with a
8272
- // preset change in the same request.
8303
+ // Authored as a name, sent as an id (#4116), so a tree copied into
8304
+ // another app binds that app's rule set. Existence-checked against
8305
+ // AccessRuleSet; mutually exclusive with a preset change in the same
8306
+ // request.
8273
8307
  validation: handledBy(BLOB_BUCKET_HANDLER, "BlobBucketsController", "apply"),
8274
8308
  },
8275
8309
  ],
@@ -8294,11 +8328,11 @@ export const BLOB_BUCKET_SURFACE = {
8294
8328
  "TOML spelling is declared in tomlOnlyKeys as `key`.",
8295
8329
  },
8296
8330
  accessPolicy: {
8297
- kind: "deprecated",
8298
- note: "#1020 — the legacy access field, superseded by `preset`. Still " +
8299
- "accepted on the wire and returned as a read alias, but authors " +
8300
- "write `preset`, so pull does not emit it (writing both would " +
8301
- "author the same decision twice).",
8331
+ kind: "server-owned",
8332
+ note: "RETIRED from the write surface by #3996, superseded by `preset` " +
8333
+ "(#1020). The attribute stays declared so `resolveBucketPreset` " +
8334
+ "can read a pre-#1020 row; no write path accepts it and no " +
8335
+ "serializer emits it.",
8302
8336
  },
8303
8337
  createdBy: {
8304
8338
  kind: "server-owned",
@@ -8319,11 +8353,13 @@ export const BLOB_BUCKET_SURFACE = {
8319
8353
  note: "The bucket key. `config pull` writes it for readability; push takes " +
8320
8354
  "the identity from the file name and attaches `bucketKey` itself.",
8321
8355
  },
8322
- accessPolicy: {
8356
+ ruleSetId: {
8323
8357
  kind: "deprecated",
8324
- note: "Accepted in existing files as the pre-#1020 spelling of `preset` " +
8325
- "and forwarded on push, so an unmigrated file still pushes. Pull " +
8326
- "writes `preset`.",
8358
+ note: "The id spelling of the rule-set reference (#4116). An id names a " +
8359
+ "rule set in one app, so a copied tree cannot resolve it; " +
8360
+ "`ruleSetName` is the authored key. Still accepted on push, with " +
8361
+ "an advisory line, and `config pull` rewrites the file to " +
8362
+ "`ruleSetName`. Retiring it is a later change of its own.",
8327
8363
  },
8328
8364
  },
8329
8365
  responseOnlyKeys: {},
@@ -9552,11 +9588,13 @@ export const SERVER_FUNCTION_SURFACE = {
9552
9588
  * which is exactly why the spec names them explicitly: nothing that syncs sits
9553
9589
  * outside the registry.
9554
9590
  *
9555
- * Three fields are stored as ids and AUTHORED as keys — `configId` /
9591
+ * Three fields are stored as ids and AUTHORED as names — `configId` /
9556
9592
  * `evaluatorPromptId` / `evaluatorConfigId` become `configName` /
9557
9593
  * `evaluatorPromptKey` / `evaluatorConfigName` — so a test file is portable
9558
- * across apps. The legacy id spellings stay accepted on push and are declared
9559
- * in `tomlOnlyKeys`.
9594
+ * across apps. The id spellings are retired from the TOML surface (#3998):
9595
+ * `config push` refuses a sidecar that still carries one, with the guidance in
9596
+ * `retired-keys.ts`, and `config pull` reports a reference it cannot resolve
9597
+ * rather than writing an id. On the wire the three stay ids.
9560
9598
  *
9561
9599
  * Three more are stored as JSON strings and carried in TOML as JSON strings
9562
9600
  * (`inputVariables`, `expectedOutputContains`, `expectedJsonSubset`) while the
@@ -9733,23 +9771,7 @@ export const TEST_CASE_SURFACE = {
9733
9771
  note: "Timestamp, assigned server-side.",
9734
9772
  },
9735
9773
  },
9736
- tomlOnlyKeys: {
9737
- configId: {
9738
- kind: "deprecated",
9739
- note: "The legacy id-based config reference. Still accepted on push so " +
9740
- "an unmigrated file pushes unchanged; pull writes `configName`.",
9741
- },
9742
- evaluatorPromptId: {
9743
- kind: "deprecated",
9744
- note: "The legacy id-based evaluator-prompt reference; pull writes " +
9745
- "`evaluatorPromptKey`.",
9746
- },
9747
- evaluatorConfigId: {
9748
- kind: "deprecated",
9749
- note: "The legacy id-based evaluator-config reference; pull writes " +
9750
- "`evaluatorConfigName`.",
9751
- },
9752
- },
9774
+ tomlOnlyKeys: {},
9753
9775
  responseOnlyKeys: {},
9754
9776
  },
9755
9777
  ],
@@ -9877,6 +9899,33 @@ export const APP_SETTINGS_SURFACE = {
9877
9899
  // offline work around it (`parseLargeDocumentWindowSetting`).
9878
9900
  validation: handledBy(SETTINGS_HANDLER, "SettingsController", "apply"),
9879
9901
  },
9902
+ {
9903
+ field: "rootDocumentFormat",
9904
+ tomlKey: "rootDocumentFormat",
9905
+ type: "number",
9906
+ // Whenever the app chose one. An app that never did mints legacy
9907
+ // roots, and a pulled file should not grow a key for it (#4195).
9908
+ emit: "whenSet",
9909
+ writableOn: UPDATE_ONLY,
9910
+ // 1, 2 or cleared; anything else is refused, because a root's format
9911
+ // is permanent and the mint must never guess
9912
+ // (`parseRootDocumentFormatSetting`).
9913
+ validation: handledBy(SETTINGS_HANDLER, "SettingsController", "apply"),
9914
+ },
9915
+ {
9916
+ field: "protected",
9917
+ tomlKey: "protected",
9918
+ type: "boolean",
9919
+ // Always: a pulled file states whether the app is live with real
9920
+ // users, rather than leaving it to the absence of a key (#3886).
9921
+ emit: "always",
9922
+ writableOn: UPDATE_ONLY,
9923
+ // Strict boolean, and only the app's console owner or a super-admin
9924
+ // may CHANGE it; a body carrying the stored value is a no-op for
9925
+ // anyone the route admits (`resolveProtectedFlagWrite`,
9926
+ // `src/services/protected-app.ts`).
9927
+ validation: handledBy(SETTINGS_HANDLER, "SettingsController", "apply"),
9928
+ },
9880
9929
  ],
9881
9930
  notExposed: {
9882
9931
  appId: {
@@ -9891,6 +9940,58 @@ export const APP_SETTINGS_SURFACE = {
9891
9940
  kind: "server-owned",
9892
9941
  note: "Admin who created the app; assigned server-side.",
9893
9942
  },
9943
+ // Child apps: lineage is set when a child is created and never
9944
+ // changes; expiry and activity are written by the server. The admin
9945
+ // update route refuses a changed value of any of these.
9946
+ parentAppId: {
9947
+ kind: "server-owned",
9948
+ note: "A child's parent app; set at creation, never authored.",
9949
+ },
9950
+ childSlug: {
9951
+ kind: "server-owned",
9952
+ note: "A child's slug under its parent; set at creation, never authored.",
9953
+ },
9954
+ parentSlugKey: {
9955
+ kind: "server-owned",
9956
+ note: "The `<parentAppId>#<slug>` uniqueness key of a child.",
9957
+ },
9958
+ ownerAdminId: {
9959
+ kind: "server-owned",
9960
+ note: "The admin who owns a child; the creator, set at creation.",
9961
+ },
9962
+ branch: {
9963
+ kind: "server-owned",
9964
+ note: "The git branch a child was created from; set at creation.",
9965
+ },
9966
+ idleDays: {
9967
+ kind: "server-owned",
9968
+ note: "Days of inactivity before a child expires; set at creation.",
9969
+ },
9970
+ expiresAt: {
9971
+ kind: "server-owned",
9972
+ note: "When a child expires; maintained by the server.",
9973
+ },
9974
+ warnedForExpiresAt: {
9975
+ kind: "server-owned",
9976
+ note: "The expiry a child's owner was last warned about.",
9977
+ },
9978
+ deletingAt: {
9979
+ kind: "server-owned",
9980
+ note: "When the expiry sweep claimed a child for deletion.",
9981
+ },
9982
+ previewOrigins: {
9983
+ kind: "server-owned",
9984
+ note: "A child's preview origins; written only by the preview-origins " +
9985
+ "route, so sync never sees it.",
9986
+ },
9987
+ childPartition: {
9988
+ kind: "server-owned",
9989
+ note: "The constant partition of the child-only expiry index.",
9990
+ },
9991
+ lastActivityAt: {
9992
+ kind: "server-owned",
9993
+ note: "When the app last saw counted activity; written by the server.",
9994
+ },
9894
9995
  passkeyRpId: {
9895
9996
  kind: "server-owned",
9896
9997
  note: "RETIRED from the write surface by #2891, superseded by " +
@@ -9924,15 +10025,17 @@ export const APP_SETTINGS_SURFACE = {
9924
10025
  note: "RETIRED from the write surface by #2884, superseded by " +
9925
10026
  "`emailSignInEnabled`. Email sign-in is ONE flow — one request, " +
9926
10027
  "one email carrying a code and (when a link can be issued) a " +
9927
- "link — so there is no method to enable separately. The " +
9928
- "attribute stays declared and is MIRRORED on every write of " +
9929
- "`emailSignInEnabled`, so a row read by an un-migrated consumer " +
9930
- "still matches behavior.",
10028
+ "link — so there is no method to enable separately. Hidden " +
10029
+ "since #3993: never written, never echoed, refused on write; " +
10030
+ "read only by the email sign-in fallback for a row that stores " +
10031
+ "no `emailSignInEnabled`.",
9931
10032
  },
9932
10033
  otpEnabled: {
9933
10034
  kind: "server-owned",
9934
10035
  note: "The other half of the retired pair (#2884); superseded by " +
9935
- "`emailSignInEnabled` and mirrored the same way.",
10036
+ "`emailSignInEnabled`. Hidden since #3993 the same way: never " +
10037
+ "written, never echoed, refused on write; read only by the " +
10038
+ "email sign-in fallback.",
9936
10039
  },
9937
10040
  redirectUris: {
9938
10041
  kind: "server-owned",
@@ -10314,6 +10417,20 @@ export const GENERATED_CONFIG_MODEL_FIELDS = {
10314
10417
  "memberInvitationsEnabled",
10315
10418
  "memberInvitationLimit",
10316
10419
  "largeDocumentWindowDays",
10420
+ "rootDocumentFormat",
10421
+ "protected",
10422
+ "parentAppId",
10423
+ "childSlug",
10424
+ "parentSlugKey",
10425
+ "ownerAdminId",
10426
+ "branch",
10427
+ "idleDays",
10428
+ "expiresAt",
10429
+ "warnedForExpiresAt",
10430
+ "deletingAt",
10431
+ "previewOrigins",
10432
+ "childPartition",
10433
+ "lastActivityAt",
10317
10434
  "createdAt",
10318
10435
  "modifiedAt",
10319
10436
  ],