primitive-admin 1.2.0-alpha.6 → 1.2.0-alpha.7

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 (63) hide show
  1. package/README.md +1 -1
  2. package/dist/src/commands/apps-children.d.ts +1 -1
  3. package/dist/src/commands/apps-children.js +170 -28
  4. package/dist/src/commands/apps-children.js.map +1 -1
  5. package/dist/src/commands/auth.js +41 -6
  6. package/dist/src/commands/auth.js.map +1 -1
  7. package/dist/src/commands/config.js +43 -4
  8. package/dist/src/commands/config.js.map +1 -1
  9. package/dist/src/commands/database-types.js +313 -13
  10. package/dist/src/commands/database-types.js.map +1 -1
  11. package/dist/src/commands/databases.js +130 -53
  12. package/dist/src/commands/databases.js.map +1 -1
  13. package/dist/src/commands/init.d.ts +7 -0
  14. package/dist/src/commands/init.js +9 -1
  15. package/dist/src/commands/init.js.map +1 -1
  16. package/dist/src/commands/llm.js +6 -9
  17. package/dist/src/commands/llm.js.map +1 -1
  18. package/dist/src/commands/settings.d.ts +9 -12
  19. package/dist/src/commands/settings.js +79 -31
  20. package/dist/src/commands/settings.js.map +1 -1
  21. package/dist/src/commands/sync.d.ts +118 -6
  22. package/dist/src/commands/sync.js +594 -55
  23. package/dist/src/commands/sync.js.map +1 -1
  24. package/dist/src/lib/child-apps-local.d.ts +45 -21
  25. package/dist/src/lib/child-apps-local.js +68 -30
  26. package/dist/src/lib/child-apps-local.js.map +1 -1
  27. package/dist/src/lib/credentials-store.d.ts +38 -3
  28. package/dist/src/lib/credentials-store.js +175 -9
  29. package/dist/src/lib/credentials-store.js.map +1 -1
  30. package/dist/src/lib/function-collect.d.ts +1 -1
  31. package/dist/src/lib/function-collect.js +4 -0
  32. package/dist/src/lib/function-collect.js.map +1 -1
  33. package/dist/src/lib/function-triggers.d.ts +3 -15
  34. package/dist/src/lib/function-triggers.js +10 -72
  35. package/dist/src/lib/function-triggers.js.map +1 -1
  36. package/dist/src/lib/generated-config-surfaces.d.ts +148 -0
  37. package/dist/src/lib/generated-config-surfaces.js +208 -25
  38. package/dist/src/lib/generated-config-surfaces.js.map +1 -1
  39. package/dist/src/lib/generated-sdk-types.d.ts +1 -1
  40. package/dist/src/lib/generated-sdk-types.js +1 -1
  41. package/dist/src/lib/generated-sdk-types.js.map +1 -1
  42. package/dist/src/lib/local-state.d.ts +21 -6
  43. package/dist/src/lib/local-state.js +78 -34
  44. package/dist/src/lib/local-state.js.map +1 -1
  45. package/dist/src/lib/push-failure-report.d.ts +45 -0
  46. package/dist/src/lib/push-failure-report.js +57 -0
  47. package/dist/src/lib/push-failure-report.js.map +1 -0
  48. package/dist/src/lib/sync-selectors.d.ts +5 -1
  49. package/dist/src/lib/sync-selectors.js +23 -13
  50. package/dist/src/lib/sync-selectors.js.map +1 -1
  51. package/dist/src/lib/transient-retry.d.ts +9 -0
  52. package/dist/src/lib/transient-retry.js +37 -0
  53. package/dist/src/lib/transient-retry.js.map +1 -1
  54. package/dist/src/lib/vars-override.d.ts +105 -0
  55. package/dist/src/lib/vars-override.js +143 -0
  56. package/dist/src/lib/vars-override.js.map +1 -0
  57. package/package.json +2 -2
  58. package/dist/src/lib/generated-workflow-model-fields.d.ts +0 -20
  59. package/dist/src/lib/generated-workflow-model-fields.js +0 -53
  60. package/dist/src/lib/generated-workflow-model-fields.js.map +0 -1
  61. package/dist/src/lib/workflow-field-descriptor.d.ts +0 -91
  62. package/dist/src/lib/workflow-field-descriptor.js +0 -153
  63. package/dist/src/lib/workflow-field-descriptor.js.map +0 -1
@@ -2695,6 +2695,18 @@ export type AgentDeclarationResolution = {
2695
2695
  * the block the author wrote.
2696
2696
  */
2697
2697
  export declare const AGENT_BLOCK_LABEL = "[prompt.agent]";
2698
+ /**
2699
+ * One place a declaration names a server function: the key, the tool's name
2700
+ * for a tool, the path that makes the reference, and whether the reference
2701
+ * needs the function's schemas (a tool's are its function's; a turn-context or
2702
+ * history hook only needs the function to exist).
2703
+ */
2704
+ export interface AgentFunctionReference {
2705
+ key: string;
2706
+ tool?: string;
2707
+ path: string;
2708
+ needsSchemas: boolean;
2709
+ }
2698
2710
  /**
2699
2711
  * The function keys a declaration references (tools, turn context, history),
2700
2712
  * each once, in declaration order. What a caller resolves into the
@@ -2712,6 +2724,39 @@ export declare const AGENT_BLOCK_LABEL = "[prompt.agent]";
2712
2724
  */
2713
2725
  export declare function parseAgentDeclarationBlock(agent: unknown): Record<string, unknown> | undefined;
2714
2726
  export declare function agentDeclarationFunctionKeys(agent: unknown): string[];
2727
+ /**
2728
+ * Every function reference of a declaration (tools, turn context, history), in
2729
+ * declaration order, with the path that makes it. `[]` for a block that is
2730
+ * absent or does not parse. A declaration written as one JSON string is read
2731
+ * as the object it encodes.
2732
+ */
2733
+ export declare function agentDeclarationFunctionReferences(agent: unknown): AgentFunctionReference[];
2734
+ /**
2735
+ * What a reference lacks: the function itself, or one of a tool's two schemas.
2736
+ * One deficit per (reference, kind), so a comparison of two states can tell a
2737
+ * schema restored from a schema removed without reading message text.
2738
+ */
2739
+ export type AgentFunctionDeficitKind = "absent" | "inputSchemaMissing" | "outputSchemaMissing";
2740
+ /** One thing a function reference of a declaration lacks. */
2741
+ export interface AgentFunctionDeficit {
2742
+ path: string;
2743
+ tool?: string;
2744
+ key: string;
2745
+ kind: AgentFunctionDeficitKind;
2746
+ }
2747
+ /**
2748
+ * Every deficit of a declaration's function references against `functions`:
2749
+ * an absent function (`null`, or missing from the map), and for a tool a
2750
+ * missing `inputSchema` and a missing `outputSchema`, each on its own. `[]` for
2751
+ * a block that is absent or does not parse.
2752
+ */
2753
+ export declare function agentFunctionDeficits(agent: unknown, functions: AgentFunctionFacts): AgentFunctionDeficit[];
2754
+ /**
2755
+ * The refusals for a list of deficits: one `AGENT_TOOL_FUNCTION_MISSING` per
2756
+ * absent reference, and one `AGENT_TOOL_FUNCTION_SCHEMA_MISSING` per tool
2757
+ * reference naming every schema it lacks.
2758
+ */
2759
+ export declare function agentFunctionDeficitRefusals(deficits: readonly AgentFunctionDeficit[]): AgentRefusal[];
2715
2760
  /**
2716
2761
  * The one rule: is this `[prompt.agent]` declaration (with the prompt's kind
2717
2762
  * and `outputSchema`) something the platform can run?
@@ -2872,6 +2917,109 @@ export interface AgentSchemaTranslationInput {
2872
2917
  * A declaration with no tools and no events reads no function schema at all.
2873
2918
  */
2874
2919
  export declare function agentSchemaTranslationRefusals(input: AgentSchemaTranslationInput): AgentRefusal[];
2920
+ /**
2921
+ * One schema an agent's model reads that a provider of a non-archived config
2922
+ * cannot carry — or, for a server tool, a function schema that does not parse.
2923
+ * The structured half of `agentSchemaTranslationRefusals`: its identity is
2924
+ * (`subject`, `which`, `provider`, `schemaPath`, `keyword`), or (`subject`,
2925
+ * `which`, `unparsable`), so two states can be compared without reading
2926
+ * message text.
2927
+ */
2928
+ export type AgentSchemaDeficit = {
2929
+ /** `tool "propose"`, `tool "lookup" (function "ledger")` or `event "applied"`. */
2930
+ subject: string;
2931
+ /** `inputSchema`, `outputSchema` or `schema`. */
2932
+ which: string;
2933
+ provider: string;
2934
+ /** The non-archived configs on `provider`. */
2935
+ configNames: string[];
2936
+ /** The dotted path from the schema root; `""` is the root. */
2937
+ schemaPath: string;
2938
+ keyword: string;
2939
+ reason: string;
2940
+ } | {
2941
+ subject: string;
2942
+ which: string;
2943
+ /** The function whose stored schema does not parse. */
2944
+ unparsable: string;
2945
+ };
2946
+ /**
2947
+ * Every deficit `agentSchemaTranslationRefusals` reports, in the same order,
2948
+ * before it is rendered. One per (schema, path, keyword, provider), and one
2949
+ * per server-tool schema that does not parse.
2950
+ */
2951
+ export declare function agentSchemaTranslationDeficits(input: AgentSchemaTranslationInput): AgentSchemaDeficit[];
2952
+ /** The `AGENT_SCHEMA_NOT_TRANSLATABLE` refusal one deficit renders as. */
2953
+ export declare function agentSchemaDeficitRefusal(deficit: AgentSchemaDeficit): AgentRefusal;
2954
+ /**
2955
+ * A change to a server function that an agent uses — issue #4441.
2956
+ *
2957
+ * An agent names server functions as its tools, its turn-context hook and its
2958
+ * history hook. The agent's own writes are checked against those functions
2959
+ * (`resolveAgentDeclaration`, `agentSchemaTranslationRefusals`), but a change
2960
+ * to the FUNCTION — archiving it, deleting it, changing its schemas — used to
2961
+ * be accepted and fail at the agent's next turn. This module is the rule for
2962
+ * that change: given one agent's declaration and configs, and the functions it
2963
+ * names before and after the change, which refusals does the change introduce?
2964
+ *
2965
+ * The admin function routes refuse such a change with `FUNCTION_USED_BY_AGENT`,
2966
+ * and `config push`'s preflight runs the same rule through the vendored
2967
+ * artifact, so the two cannot disagree.
2968
+ *
2969
+ * ONLY WHAT THE CHANGE INTRODUCES. Both states are reduced to structured
2970
+ * deficits (`agentFunctionDeficits`, `agentSchemaTranslationDeficits`) and the
2971
+ * ones already present before the change are dropped. An agent that is broken
2972
+ * for another reason does not block an unrelated change, and a partial repair
2973
+ * — restoring one of two missing schemas, removing one of two `$ref`s — is
2974
+ * accepted. Messages are rendered only afterwards, by the sibling rules' own
2975
+ * builders, so they read exactly as an agent write's refusals do.
2976
+ *
2977
+ * Pure and dependency-free, like every module under `src/config-surface/`.
2978
+ */
2979
+ /** The code a function route answers when an agent cannot take the change. */
2980
+ export declare const FUNCTION_USED_BY_AGENT = "FUNCTION_USED_BY_AGENT";
2981
+ /** One agent, and the functions it names before and after a change to them. */
2982
+ export interface AgentFunctionChangeInput {
2983
+ /** `[prompt.agent]` as stored: a table, or one JSON string encoding one. */
2984
+ declaration: unknown;
2985
+ /** Every config of the agent's prompt. Archived ones are skipped. */
2986
+ configs: ReadonlyArray<AgentSchemaConfigFact>;
2987
+ /** What the app holds for each function the declaration names, now. */
2988
+ before: AgentFunctionFacts;
2989
+ /** The same, as the change would leave it. */
2990
+ after: AgentFunctionFacts;
2991
+ }
2992
+ /**
2993
+ * Every refusal the change introduces for this agent: a reference whose
2994
+ * function goes away (`AGENT_TOOL_FUNCTION_MISSING`), a tool schema that goes
2995
+ * away (`AGENT_TOOL_FUNCTION_SCHEMA_MISSING`), and a schema a non-archived
2996
+ * config's provider cannot carry where the previous one could
2997
+ * (`AGENT_SCHEMA_NOT_TRANSLATABLE`). `[]` when the agent can take the change,
2998
+ * and for a declaration that does not parse (it names nothing).
2999
+ */
3000
+ export declare function agentFunctionChangeRefusals(input: AgentFunctionChangeInput): AgentRefusal[];
3001
+ /**
3002
+ * How an agent uses one function: each tool's name (or its path when the tool
3003
+ * has none), and `turnContext.function` / `history.function` for a hook, in
3004
+ * declaration order. `[]` when the declaration does not name the function.
3005
+ */
3006
+ export declare function agentFunctionUses(declaration: unknown, functionKey: string): string[];
3007
+ /** What a function route was asked to do. */
3008
+ export type FunctionChangeKind = "archive" | "delete" | "schema";
3009
+ /** One agent a function change would break. */
3010
+ export interface FunctionUsedByAgent {
3011
+ promptKey: string;
3012
+ /** `agentFunctionUses` of the changed function. */
3013
+ tools: string[];
3014
+ /** The refusals the change introduces for this agent. */
3015
+ refusals: ReadonlyArray<AgentRefusal>;
3016
+ }
3017
+ /**
3018
+ * The sentence a function route refuses with: the function, then one sentence
3019
+ * per agent naming its key and each use, then the remedy. A schema change also
3020
+ * carries each agent's refusals, since the remedy depends on which schema broke.
3021
+ */
3022
+ export declare function functionUsedByAgentMessage(functionKey: string, change: FunctionChangeKind, agents: ReadonlyArray<FunctionUsedByAgent>): string;
2875
3023
  /**
2876
3024
  * What an agent test case may say — issue #4262 (single-round prompt tests
2877
3025
  * from a seeded history; design-docs/agents.md §D1).
@@ -6255,35 +6255,82 @@ export function agentDeclarationFunctionKeys(agent) {
6255
6255
  return [];
6256
6256
  return [...new Set(agentFunctionReferences(parsed).map((ref) => ref.key))];
6257
6257
  }
6258
- function agentFunctionRefusals(agent, functions) {
6258
+ /**
6259
+ * Every function reference of a declaration (tools, turn context, history), in
6260
+ * declaration order, with the path that makes it. `[]` for a block that is
6261
+ * absent or does not parse. A declaration written as one JSON string is read
6262
+ * as the object it encodes.
6263
+ */
6264
+ export function agentDeclarationFunctionReferences(agent) {
6265
+ const parsed = parseAgentBlock(agent);
6266
+ return parsed ? agentFunctionReferences(parsed) : [];
6267
+ }
6268
+ /**
6269
+ * Every deficit of a declaration's function references against `functions`:
6270
+ * an absent function (`null`, or missing from the map), and for a tool a
6271
+ * missing `inputSchema` and a missing `outputSchema`, each on its own. `[]` for
6272
+ * a block that is absent or does not parse.
6273
+ */
6274
+ export function agentFunctionDeficits(agent, functions) {
6259
6275
  const out = [];
6260
- for (const ref of agentFunctionReferences(agent)) {
6261
- const subject = ref.tool
6262
- ? `tool "${ref.tool}" (${ref.path})`
6263
- : ref.path;
6276
+ for (const ref of agentDeclarationFunctionReferences(agent)) {
6277
+ const at = {
6278
+ path: ref.path,
6279
+ ...(ref.tool !== undefined ? { tool: ref.tool } : {}),
6280
+ key: ref.key,
6281
+ };
6264
6282
  const facts = Object.prototype.hasOwnProperty.call(functions, ref.key)
6265
6283
  ? functions[ref.key]
6266
6284
  : null;
6267
6285
  if (!facts) {
6268
- out.push(agentRefusal("AGENT_TOOL_FUNCTION_MISSING", `${AGENT_BLOCK} ${subject} names function "${ref.key}", which this app ` +
6269
- "does not have. Push the function first, or include its file in " +
6270
- "this push"));
6286
+ out.push({ ...at, kind: "absent" });
6271
6287
  continue;
6272
6288
  }
6273
6289
  if (!ref.needsSchemas)
6274
6290
  continue;
6275
- const missing = [
6276
- facts.hasInputSchema ? null : "inputSchema",
6277
- facts.hasOutputSchema ? null : "outputSchema",
6278
- ].filter((name) => name !== null);
6291
+ if (!facts.hasInputSchema)
6292
+ out.push({ ...at, kind: "inputSchemaMissing" });
6293
+ if (!facts.hasOutputSchema)
6294
+ out.push({ ...at, kind: "outputSchemaMissing" });
6295
+ }
6296
+ return out;
6297
+ }
6298
+ /**
6299
+ * The refusals for a list of deficits: one `AGENT_TOOL_FUNCTION_MISSING` per
6300
+ * absent reference, and one `AGENT_TOOL_FUNCTION_SCHEMA_MISSING` per tool
6301
+ * reference naming every schema it lacks.
6302
+ */
6303
+ export function agentFunctionDeficitRefusals(deficits) {
6304
+ const out = [];
6305
+ const byPath = new Map();
6306
+ for (const deficit of deficits) {
6307
+ const list = byPath.get(deficit.path);
6308
+ if (list)
6309
+ list.push(deficit);
6310
+ else
6311
+ byPath.set(deficit.path, [deficit]);
6312
+ }
6313
+ for (const list of byPath.values()) {
6314
+ const { path, tool, key } = list[0];
6315
+ const subject = tool ? `tool "${tool}" (${path})` : path;
6316
+ if (list.some((deficit) => deficit.kind === "absent")) {
6317
+ out.push(agentRefusal("AGENT_TOOL_FUNCTION_MISSING", `${AGENT_BLOCK} ${subject} names function "${key}", which this app ` +
6318
+ "does not have. Push the function first, or include its file in " +
6319
+ "this push"));
6320
+ continue;
6321
+ }
6322
+ const missing = ["inputSchema", "outputSchema"].filter((name) => list.some((deficit) => deficit.kind === `${name}Missing`));
6279
6323
  if (missing.length > 0) {
6280
- out.push(agentRefusal("AGENT_TOOL_FUNCTION_SCHEMA_MISSING", `${AGENT_BLOCK} ${subject} names function "${ref.key}", which declares ` +
6324
+ out.push(agentRefusal("AGENT_TOOL_FUNCTION_SCHEMA_MISSING", `${AGENT_BLOCK} ${subject} names function "${key}", which declares ` +
6281
6325
  `no ${missing.join(" or ")}. A tool's schemas are its function's: ` +
6282
- `declare ${missing.join(" and ")} in functions/${ref.key}.toml`));
6326
+ `declare ${missing.join(" and ")} in functions/${key}.toml`));
6283
6327
  }
6284
6328
  }
6285
6329
  return out;
6286
6330
  }
6331
+ function agentFunctionRefusals(agent, functions) {
6332
+ return agentFunctionDeficitRefusals(agentFunctionDeficits(agent, functions));
6333
+ }
6287
6334
  /**
6288
6335
  * The one rule: is this `[prompt.agent]` declaration (with the prompt's kind
6289
6336
  * and `outputSchema`) something the platform can run?
@@ -7068,6 +7115,14 @@ function configList(configNames) {
7068
7115
  * A declaration with no tools and no events reads no function schema at all.
7069
7116
  */
7070
7117
  export function agentSchemaTranslationRefusals(input) {
7118
+ return agentSchemaTranslationDeficits(input).map(agentSchemaDeficitRefusal);
7119
+ }
7120
+ /**
7121
+ * Every deficit `agentSchemaTranslationRefusals` reports, in the same order,
7122
+ * before it is rendered. One per (schema, path, keyword, provider), and one
7123
+ * per server-tool schema that does not parse.
7124
+ */
7125
+ export function agentSchemaTranslationDeficits(input) {
7071
7126
  const declaration = parseAgentDeclarationBlock(input.declaration);
7072
7127
  if (!declaration)
7073
7128
  return [];
@@ -7081,11 +7136,9 @@ export function agentSchemaTranslationRefusals(input) {
7081
7136
  for (const subject of subjects) {
7082
7137
  if (subject.unparsable !== undefined) {
7083
7138
  out.push({
7084
- code: "AGENT_SCHEMA_NOT_TRANSLATABLE",
7085
- message: `${AGENT_BLOCK_LABEL} ${subject.subject} ${subject.which} is not a ` +
7086
- `JSON Schema the platform can read, so it cannot be translated for ` +
7087
- `any provider. Fix ${subject.which} in ` +
7088
- `functions/${subject.unparsable}.toml`,
7139
+ subject: subject.subject,
7140
+ which: subject.which,
7141
+ unparsable: subject.unparsable,
7089
7142
  });
7090
7143
  continue;
7091
7144
  }
@@ -7095,18 +7148,148 @@ export function agentSchemaTranslationRefusals(input) {
7095
7148
  continue;
7096
7149
  for (const refusal of result.refusals) {
7097
7150
  out.push({
7098
- code: "AGENT_SCHEMA_NOT_TRANSLATABLE",
7099
- message: `${AGENT_BLOCK_LABEL} ${subject.subject} ${subject.which} at ` +
7100
- `${schemaPathLabel(refusal.path)}: ${refusal.keyword} cannot be ` +
7101
- `translated for ${provider} without losing meaning — ` +
7102
- `${refusal.reason}. ${configList(configNames)} run on ${provider}; ` +
7103
- `rewrite the schema, or archive every ${provider} config of this prompt`,
7151
+ subject: subject.subject,
7152
+ which: subject.which,
7153
+ provider,
7154
+ configNames,
7155
+ schemaPath: refusal.path,
7156
+ keyword: refusal.keyword,
7157
+ reason: refusal.reason,
7104
7158
  });
7105
7159
  }
7106
7160
  }
7107
7161
  }
7108
7162
  return out;
7109
7163
  }
7164
+ /** The `AGENT_SCHEMA_NOT_TRANSLATABLE` refusal one deficit renders as. */
7165
+ export function agentSchemaDeficitRefusal(deficit) {
7166
+ if ("unparsable" in deficit) {
7167
+ return {
7168
+ code: "AGENT_SCHEMA_NOT_TRANSLATABLE",
7169
+ message: `${AGENT_BLOCK_LABEL} ${deficit.subject} ${deficit.which} is not a ` +
7170
+ `JSON Schema the platform can read, so it cannot be translated for ` +
7171
+ `any provider. Fix ${deficit.which} in ` +
7172
+ `functions/${deficit.unparsable}.toml`,
7173
+ };
7174
+ }
7175
+ const { provider } = deficit;
7176
+ return {
7177
+ code: "AGENT_SCHEMA_NOT_TRANSLATABLE",
7178
+ message: `${AGENT_BLOCK_LABEL} ${deficit.subject} ${deficit.which} at ` +
7179
+ `${schemaPathLabel(deficit.schemaPath)}: ${deficit.keyword} cannot be ` +
7180
+ `translated for ${provider} without losing meaning — ` +
7181
+ `${deficit.reason}. ${configList(deficit.configNames)} run on ${provider}; ` +
7182
+ `rewrite the schema, or archive every ${provider} config of this prompt`,
7183
+ };
7184
+ }
7185
+ // ── src/config-surface/agent-function-change.ts ──────────────────────────
7186
+ /**
7187
+ * A change to a server function that an agent uses — issue #4441.
7188
+ *
7189
+ * An agent names server functions as its tools, its turn-context hook and its
7190
+ * history hook. The agent's own writes are checked against those functions
7191
+ * (`resolveAgentDeclaration`, `agentSchemaTranslationRefusals`), but a change
7192
+ * to the FUNCTION — archiving it, deleting it, changing its schemas — used to
7193
+ * be accepted and fail at the agent's next turn. This module is the rule for
7194
+ * that change: given one agent's declaration and configs, and the functions it
7195
+ * names before and after the change, which refusals does the change introduce?
7196
+ *
7197
+ * The admin function routes refuse such a change with `FUNCTION_USED_BY_AGENT`,
7198
+ * and `config push`'s preflight runs the same rule through the vendored
7199
+ * artifact, so the two cannot disagree.
7200
+ *
7201
+ * ONLY WHAT THE CHANGE INTRODUCES. Both states are reduced to structured
7202
+ * deficits (`agentFunctionDeficits`, `agentSchemaTranslationDeficits`) and the
7203
+ * ones already present before the change are dropped. An agent that is broken
7204
+ * for another reason does not block an unrelated change, and a partial repair
7205
+ * — restoring one of two missing schemas, removing one of two `$ref`s — is
7206
+ * accepted. Messages are rendered only afterwards, by the sibling rules' own
7207
+ * builders, so they read exactly as an agent write's refusals do.
7208
+ *
7209
+ * Pure and dependency-free, like every module under `src/config-surface/`.
7210
+ */
7211
+ /** The code a function route answers when an agent cannot take the change. */
7212
+ export const FUNCTION_USED_BY_AGENT = "FUNCTION_USED_BY_AGENT";
7213
+ function functionDeficitIdentity(deficit) {
7214
+ return `${deficit.path}\u0000${deficit.kind}`;
7215
+ }
7216
+ function schemaDeficitIdentity(deficit) {
7217
+ if ("unparsable" in deficit) {
7218
+ return [deficit.subject, deficit.which, "unparsable", deficit.unparsable].join("\u0000");
7219
+ }
7220
+ return [
7221
+ deficit.subject,
7222
+ deficit.which,
7223
+ deficit.provider,
7224
+ deficit.schemaPath,
7225
+ deficit.keyword,
7226
+ ].join("\u0000");
7227
+ }
7228
+ /**
7229
+ * Every refusal the change introduces for this agent: a reference whose
7230
+ * function goes away (`AGENT_TOOL_FUNCTION_MISSING`), a tool schema that goes
7231
+ * away (`AGENT_TOOL_FUNCTION_SCHEMA_MISSING`), and a schema a non-archived
7232
+ * config's provider cannot carry where the previous one could
7233
+ * (`AGENT_SCHEMA_NOT_TRANSLATABLE`). `[]` when the agent can take the change,
7234
+ * and for a declaration that does not parse (it names nothing).
7235
+ */
7236
+ export function agentFunctionChangeRefusals(input) {
7237
+ const { declaration, configs, before, after } = input;
7238
+ if (agentDeclarationFunctionReferences(declaration).length === 0)
7239
+ return [];
7240
+ const functionBefore = new Set(agentFunctionDeficits(declaration, before).map(functionDeficitIdentity));
7241
+ const functionIntroduced = agentFunctionDeficits(declaration, after).filter((deficit) => !functionBefore.has(functionDeficitIdentity(deficit)));
7242
+ const schemaBefore = new Set(agentSchemaTranslationDeficits({ declaration, functions: before, configs }).map(schemaDeficitIdentity));
7243
+ const schemaIntroduced = agentSchemaTranslationDeficits({
7244
+ declaration,
7245
+ functions: after,
7246
+ configs,
7247
+ }).filter((deficit) => !schemaBefore.has(schemaDeficitIdentity(deficit)));
7248
+ return [
7249
+ ...agentFunctionDeficitRefusals(functionIntroduced),
7250
+ ...schemaIntroduced.map(agentSchemaDeficitRefusal),
7251
+ ];
7252
+ }
7253
+ /**
7254
+ * How an agent uses one function: each tool's name (or its path when the tool
7255
+ * has none), and `turnContext.function` / `history.function` for a hook, in
7256
+ * declaration order. `[]` when the declaration does not name the function.
7257
+ */
7258
+ export function agentFunctionUses(declaration, functionKey) {
7259
+ return agentDeclarationFunctionReferences(declaration)
7260
+ .filter((ref) => ref.key === functionKey)
7261
+ .map((ref) => ref.tool ?? ref.path);
7262
+ }
7263
+ /** `tool "lookup"` for a tool's name, the path itself for a hook. */
7264
+ function useLabel(use) {
7265
+ return use.includes(".") ? use : `tool "${use}"`;
7266
+ }
7267
+ /**
7268
+ * The sentence a function route refuses with: the function, then one sentence
7269
+ * per agent naming its key and each use, then the remedy. A schema change also
7270
+ * carries each agent's refusals, since the remedy depends on which schema broke.
7271
+ */
7272
+ export function functionUsedByAgentMessage(functionKey, change, agents) {
7273
+ const verb = change === "archive"
7274
+ ? "cannot be archived"
7275
+ : change === "delete"
7276
+ ? "cannot be deleted"
7277
+ : "cannot take this schema change";
7278
+ const uses = agents.map((agent) => {
7279
+ const labels = agent.tools.map(useLabel).join(", ");
7280
+ const why = change === "schema" && agent.refusals.length > 0
7281
+ ? `: ${agent.refusals.map((refusal) => refusal.message).join("; ")}`
7282
+ : "";
7283
+ return `It is used by agent "${agent.promptKey}" as ${labels}${why}.`;
7284
+ });
7285
+ const files = agents.map((agent) => `prompts/${agent.promptKey}.toml`).join(", ");
7286
+ const remedy = change === "schema"
7287
+ ? `Change the agent in the same push (${files}) so it no longer needs ` +
7288
+ "what this change removes, or retire the agent, then retry."
7289
+ : `Remove the reference from [prompt.agent] in ${files} and push it, or ` +
7290
+ "retire the agent, then retry.";
7291
+ return `Function "${functionKey}" ${verb} while an agent uses it. ${uses.join(" ")} ${remedy}`;
7292
+ }
7110
7293
  // ── src/config-surface/agent-test-case.ts ────────────────────────────────
7111
7294
  /**
7112
7295
  * What an agent test case may say — issue #4262 (single-round prompt tests