primitive-admin 1.2.0-alpha.2 → 1.2.0-alpha.4
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.
- package/dist/src/commands/agent-sessions.js +4 -0
- package/dist/src/commands/agent-sessions.js.map +1 -1
- package/dist/src/commands/databases.js +13 -38
- package/dist/src/commands/databases.js.map +1 -1
- package/dist/src/commands/documents.d.ts +1 -1
- package/dist/src/commands/documents.js +70 -33
- package/dist/src/commands/documents.js.map +1 -1
- package/dist/src/commands/prompts.d.ts +7 -0
- package/dist/src/commands/prompts.js +44 -0
- package/dist/src/commands/prompts.js.map +1 -1
- package/dist/src/commands/sync.d.ts +12 -29
- package/dist/src/commands/sync.js +128 -138
- package/dist/src/commands/sync.js.map +1 -1
- package/dist/src/lib/aggregate-output.d.ts +25 -0
- package/dist/src/lib/aggregate-output.js +53 -0
- package/dist/src/lib/aggregate-output.js.map +1 -0
- package/dist/src/lib/api-client.d.ts +29 -16
- package/dist/src/lib/api-client.js +30 -28
- package/dist/src/lib/api-client.js.map +1 -1
- package/dist/src/lib/config-object-descriptor.js +14 -5
- package/dist/src/lib/config-object-descriptor.js.map +1 -1
- package/dist/src/lib/db-codegen/dbGenerator.js +5 -16
- package/dist/src/lib/db-codegen/dbGenerator.js.map +1 -1
- package/dist/src/lib/db-codegen/dbNaming.d.ts +3 -12
- package/dist/src/lib/db-codegen/dbNaming.js +3 -16
- package/dist/src/lib/db-codegen/dbNaming.js.map +1 -1
- package/dist/src/lib/db-codegen/dbTemplates.d.ts +7 -56
- package/dist/src/lib/db-codegen/dbTemplates.js +25 -125
- package/dist/src/lib/db-codegen/dbTemplates.js.map +1 -1
- package/dist/src/lib/db-codegen/dbTypeIR.d.ts +1 -1
- package/dist/src/lib/function-sync.d.ts +3 -16
- package/dist/src/lib/function-sync.js +14 -18
- package/dist/src/lib/function-sync.js.map +1 -1
- package/dist/src/lib/generated-config-surfaces.d.ts +172 -47
- package/dist/src/lib/generated-config-surfaces.js +599 -90
- package/dist/src/lib/generated-config-surfaces.js.map +1 -1
- package/dist/src/lib/generated-sdk-types.d.ts +1 -1
- package/dist/src/lib/generated-sdk-types.js +1 -1
- package/dist/src/lib/generated-sdk-types.js.map +1 -1
- package/dist/src/lib/prompt-schema-codegen.js +5 -2
- package/dist/src/lib/prompt-schema-codegen.js.map +1 -1
- package/dist/src/lib/swift-codegen/dbGenerator.d.ts +4 -3
- package/dist/src/lib/swift-codegen/dbGenerator.js +14 -117
- package/dist/src/lib/swift-codegen/dbGenerator.js.map +1 -1
- package/dist/src/lib/swift-codegen/generator.d.ts +5 -5
- package/dist/src/lib/swift-codegen/generator.js +8 -13
- package/dist/src/lib/swift-codegen/generator.js.map +1 -1
- package/dist/src/lib/workflow-codegen/generator.d.ts +7 -7
- package/dist/src/lib/workflow-codegen/generator.js +16 -23
- package/dist/src/lib/workflow-codegen/generator.js.map +1 -1
- package/dist/src/lib/workflow-codegen/invokerIR.d.ts +6 -12
- package/dist/src/lib/workflow-codegen/invokerIR.js +4 -5
- package/dist/src/lib/workflow-codegen/invokerIR.js.map +1 -1
- package/dist/src/lib/workflow-toml-validator.d.ts +2 -0
- package/dist/src/lib/workflow-toml-validator.js +13 -0
- package/dist/src/lib/workflow-toml-validator.js.map +1 -1
- package/package.json +2 -2
|
@@ -42,7 +42,10 @@ import { CONFIG_SURFACES, isEmptyForEmit, tomlGroupsOf, } from "../lib/generated
|
|
|
42
42
|
// preflight refuses exactly what the admin routes refuse.
|
|
43
43
|
import { promptKindOf, resolvePromptKind, } from "../lib/generated-config-surfaces.js";
|
|
44
44
|
// #3798 — the `[prompt.agent]` declaration rule, vendored the same way.
|
|
45
|
-
import { agentDeclarationFunctionKeys,
|
|
45
|
+
import { agentDeclarationFunctionKeys, resolveAgentDeclaration, } from "../lib/generated-config-surfaces.js";
|
|
46
|
+
// #4262 — the agent test case's shape rule (`[test.agent]`), vendored the same
|
|
47
|
+
// way, so the preflight refuses exactly the sidecars the admin routes refuse.
|
|
48
|
+
import { AGENT_TEST_CASE_FIELDS, resolveAgentTestCase, } from "../lib/generated-config-surfaces.js";
|
|
46
49
|
// #3799 — the meaning-preserving schema translation, vendored the same way, so
|
|
47
50
|
// the preflight refuses exactly the schemas the admin routes refuse.
|
|
48
51
|
import { agentSchemaTranslationRefusals } from "../lib/generated-config-surfaces.js";
|
|
@@ -51,7 +54,7 @@ import { agentSchemaTranslationRefusals } from "../lib/generated-config-surfaces
|
|
|
51
54
|
import { resolvePromptStrictOutput } from "../lib/generated-config-surfaces.js";
|
|
52
55
|
// #3179 — the function's two halves: the header payloads and the envelope
|
|
53
56
|
// `config push` sends, and the bytes `config pull` writes back.
|
|
54
|
-
import { applyFunctionPull, buildFunctionConfigPush, createDocumentSchemaDropWarner, documentSchemaRetentionFailure, buildFunctionCreatePayload, buildFunctionUpdatePayload, functionDiffEntity,
|
|
57
|
+
import { applyFunctionPull, buildFunctionConfigPush, createDocumentSchemaDropWarner, documentSchemaRetentionFailure, buildFunctionCreatePayload, buildFunctionUpdatePayload, functionDiffEntity, functionTomlPreflightErrors, planDocumentSchemaPull, planFunctionPull, serializeFunction, } from "../lib/function-sync.js";
|
|
55
58
|
import { newerVersionLine, pullNewerVersionLine, rollbackDiffHint, } from "../lib/function-versions.js";
|
|
56
59
|
import { FunctionBuildError } from "../lib/function-bundle.js";
|
|
57
60
|
// #3182 — the local half of capability validation. The grammar is the vendored
|
|
@@ -1672,9 +1675,9 @@ export function serializeBlobBucket(bucket, ruleSetIdToName = new Map(), logger)
|
|
|
1672
1675
|
* `config push` builds its bodies from and `config diff` compares.
|
|
1673
1676
|
*
|
|
1674
1677
|
* Read in CREATE mode, so the immutable `ttlTier` stays nameable. The rule set
|
|
1675
|
-
* is left as `_ruleSetName` for `resolveRuleSetReference
|
|
1676
|
-
* `ruleSetId`
|
|
1677
|
-
*
|
|
1678
|
+
* is left as `_ruleSetName` for `resolveRuleSetReference`. The id spelling
|
|
1679
|
+
* `ruleSetId` is a retired key (#4283): the preflight refuses a file carrying
|
|
1680
|
+
* it, and this parse never reads it.
|
|
1678
1681
|
*
|
|
1679
1682
|
* An omitted `name` stays omitted: the update leaves the server's name alone,
|
|
1680
1683
|
* and only the create body falls back to the key.
|
|
@@ -1697,43 +1700,8 @@ export function parseBlobBucketToml(doc) {
|
|
|
1697
1700
|
// Trimmed, as `applyRuleSetReference` does for the type configs.
|
|
1698
1701
|
entity._ruleSetName = section.ruleSetName.trim();
|
|
1699
1702
|
}
|
|
1700
|
-
else if (typeof section.ruleSetId === "string" && section.ruleSetId !== "") {
|
|
1701
|
-
entity.ruleSetId = section.ruleSetId;
|
|
1702
|
-
}
|
|
1703
1703
|
return entity;
|
|
1704
1704
|
}
|
|
1705
|
-
/**
|
|
1706
|
-
* The advisory `config push` prints for a bucket file still naming its rule set
|
|
1707
|
-
* by id (#4116). The key is deprecated, not retired: the file pushes.
|
|
1708
|
-
*/
|
|
1709
|
-
export function blobBucketDeprecatedKeyWarning(filePath, tomlData) {
|
|
1710
|
-
const bucket = tomlData?.bucket;
|
|
1711
|
-
if (!bucket || bucket.ruleSetId === undefined)
|
|
1712
|
-
return null;
|
|
1713
|
-
// Beside `ruleSetName` it is a refusal instead (`blobBucketFileErrors`).
|
|
1714
|
-
if (bucket.ruleSetName !== undefined)
|
|
1715
|
-
return null;
|
|
1716
|
-
return (`${filePath}: bucket.ruleSetId is deprecated — a rule-set id belongs to one app, ` +
|
|
1717
|
-
`so a copied tree cannot resolve it; write ruleSetName = "<name>" ` +
|
|
1718
|
-
"(config pull rewrites the file). Retiring the key is a later change.");
|
|
1719
|
-
}
|
|
1720
|
-
/** A bucket file's own refusals, decided from the file alone (#4116). */
|
|
1721
|
-
export function blobBucketFileErrors(filePath, tomlData) {
|
|
1722
|
-
const bucket = tomlData?.bucket;
|
|
1723
|
-
if (bucket && bucket.ruleSetId !== undefined && bucket.ruleSetName !== undefined) {
|
|
1724
|
-
return [`${filePath}: bucket.ruleSetId is set beside bucket.ruleSetName; keep ruleSetName`];
|
|
1725
|
-
}
|
|
1726
|
-
// The deprecated spelling is a TOML-only key, so the declared-type check
|
|
1727
|
-
// reads only `ruleSetName`; the old key keeps the field's type under its
|
|
1728
|
-
// own name, or a mistyped id would be dropped from the body unreported.
|
|
1729
|
-
const field = BLOB_BUCKET_TABLE.fields.find((f) => f.field === "ruleSetId");
|
|
1730
|
-
if (!field)
|
|
1731
|
-
return [];
|
|
1732
|
-
return configDeclaredTypeErrors(filePath, tomlData, {
|
|
1733
|
-
...BLOB_BUCKET_TABLE,
|
|
1734
|
-
fields: [{ ...field, tomlKey: "ruleSetId" }],
|
|
1735
|
-
});
|
|
1736
|
-
}
|
|
1737
1705
|
/**
|
|
1738
1706
|
* Build the PATCH payload for a blob-bucket update from local TOML.
|
|
1739
1707
|
*
|
|
@@ -2648,24 +2616,6 @@ export async function runConfigPushPreflight(input) {
|
|
|
2648
2616
|
}
|
|
2649
2617
|
}
|
|
2650
2618
|
}
|
|
2651
|
-
// #4116 — the deprecated `ruleSetId` spelling in a bucket file. Advisory
|
|
2652
|
-
// only: the file pushes. Bounded by `--only`, like the notice above.
|
|
2653
|
-
const preflightBucketsDir = join(configDir, "blob-buckets");
|
|
2654
|
-
if (existsSync(preflightBucketsDir)) {
|
|
2655
|
-
for (const file of selectFiles("blob-bucket", preflightBucketsDir, readdirSync(preflightBucketsDir).filter((f) => f.endsWith(".toml")))) {
|
|
2656
|
-
let tomlData;
|
|
2657
|
-
try {
|
|
2658
|
-
tomlData = parseTomlFile(join(preflightBucketsDir, file));
|
|
2659
|
-
}
|
|
2660
|
-
catch {
|
|
2661
|
-
// A file that does not parse is reported by the collector below.
|
|
2662
|
-
continue;
|
|
2663
|
-
}
|
|
2664
|
-
const advice = blobBucketDeprecatedKeyWarning(`blob-buckets/${file}`, tomlData);
|
|
2665
|
-
if (advice)
|
|
2666
|
-
warn(` ${advice}`);
|
|
2667
|
-
}
|
|
2668
|
-
}
|
|
2669
2619
|
// Validate app.toml settings up-front (issue #1033). A mistyped value
|
|
2670
2620
|
// is a hard error that must abort BEFORE any mutation — same
|
|
2671
2621
|
// fail-before-mutate contract as the vars preflight below. The apply
|
|
@@ -2781,7 +2731,7 @@ export async function runConfigPushPreflight(input) {
|
|
|
2781
2731
|
preflightValidationErrors.push(` ${message}`);
|
|
2782
2732
|
}
|
|
2783
2733
|
// #3320 — and everything else the file decides on its own: the
|
|
2784
|
-
// `entry`, the
|
|
2734
|
+
// `entry`, the retired runtime keys it refuses (#3188), the trigger
|
|
2785
2735
|
// rules, and a webhook trigger's signing-secret requirement and
|
|
2786
2736
|
// reference shape. These used to be raised inside the apply loop,
|
|
2787
2737
|
// after webhooks, integrations, vars and database types had
|
|
@@ -2796,19 +2746,6 @@ export async function runConfigPushPreflight(input) {
|
|
|
2796
2746
|
})) {
|
|
2797
2747
|
preflightValidationErrors.push(` ${filePath}: ${message}`);
|
|
2798
2748
|
}
|
|
2799
|
-
// #3482 — the retired runtime keys. NOT refusals through phase 4:
|
|
2800
|
-
// the file pushes with every other key applied, and the author
|
|
2801
|
-
// hears which line to delete before phase 5 refuses it. On
|
|
2802
|
-
// stderr, like every other advisory, so a scripted push reading
|
|
2803
|
-
// stdout is unaffected. Inside this loop rather than over the
|
|
2804
|
-
// whole tree, so `--only` bounds the advice as it bounds the
|
|
2805
|
-
// refusals (#2645).
|
|
2806
|
-
for (const message of functionTomlIgnoredKeyWarnings({
|
|
2807
|
-
functionKey,
|
|
2808
|
-
tomlData,
|
|
2809
|
-
})) {
|
|
2810
|
-
warn(` ${message}`);
|
|
2811
|
-
}
|
|
2812
2749
|
}
|
|
2813
2750
|
}
|
|
2814
2751
|
}
|
|
@@ -2868,8 +2805,10 @@ export async function runConfigPushPreflight(input) {
|
|
|
2868
2805
|
}
|
|
2869
2806
|
}
|
|
2870
2807
|
}
|
|
2871
|
-
// #3798 — a
|
|
2872
|
-
//
|
|
2808
|
+
// #3798, #4262 — a prompt's sidecar against its prompt's kind: an agent's
|
|
2809
|
+
// case is checked by the agent test case's shape rule, and `[test.agent]`
|
|
2810
|
+
// on a chat or decisions prompt's case is refused, before any file is
|
|
2811
|
+
// applied.
|
|
2873
2812
|
for (const message of promptAgentSidecarErrors(configDir)) {
|
|
2874
2813
|
preflightValidationErrors.push(` ${message}`);
|
|
2875
2814
|
}
|
|
@@ -3239,10 +3178,39 @@ export function partitionPromptFilesForPush(promptsDir, files) {
|
|
|
3239
3178
|
return { beforeFunctions, afterFunctions };
|
|
3240
3179
|
}
|
|
3241
3180
|
/**
|
|
3242
|
-
*
|
|
3243
|
-
*
|
|
3244
|
-
*
|
|
3245
|
-
|
|
3181
|
+
* The kind of the prompt a `prompts/<key>.tests/` sidecar belongs to, read
|
|
3182
|
+
* from the prompt's own file — `undefined` for another block type, or when the
|
|
3183
|
+
* file is absent or does not parse (the prompt leg reports that).
|
|
3184
|
+
*/
|
|
3185
|
+
function testCaseOwnerPromptKind(configDir, blockType, blockKey) {
|
|
3186
|
+
if (blockType !== "prompt")
|
|
3187
|
+
return undefined;
|
|
3188
|
+
const owner = join(configDir, "prompts", `${blockKey}.toml`);
|
|
3189
|
+
if (!existsSync(owner))
|
|
3190
|
+
return undefined;
|
|
3191
|
+
try {
|
|
3192
|
+
return promptKindOf(parseTomlFile(owner)?.prompt);
|
|
3193
|
+
}
|
|
3194
|
+
catch {
|
|
3195
|
+
return undefined;
|
|
3196
|
+
}
|
|
3197
|
+
}
|
|
3198
|
+
/** A sidecar's agent block and `expectedJsonSubset`, in wire spelling. */
|
|
3199
|
+
function agentTestCaseWireFields(payload) {
|
|
3200
|
+
const fields = {};
|
|
3201
|
+
for (const key of AGENT_TEST_CASE_FIELDS)
|
|
3202
|
+
fields[key] = payload?.[key];
|
|
3203
|
+
fields.expectedJsonSubset = payload?.expectedJsonSubset;
|
|
3204
|
+
return fields;
|
|
3205
|
+
}
|
|
3206
|
+
/**
|
|
3207
|
+
* Every `prompts/<key>.tests/*.toml` sidecar the agent test case's rule
|
|
3208
|
+
* refuses — #4262 (#3798 refused every sidecar on an agent). An agent's case
|
|
3209
|
+
* is checked by the vendored shape rule (`resolveAgentTestCase`), which the
|
|
3210
|
+
* admin routes run too; a `[test.agent]` block on a chat or decisions
|
|
3211
|
+
* prompt's case is refused by name. One line per file, naming it. What needs
|
|
3212
|
+
* the app (the input, context and event schemas, the declared names) is the
|
|
3213
|
+
* routes' to decide at apply.
|
|
3246
3214
|
*/
|
|
3247
3215
|
export function promptAgentSidecarErrors(configDir) {
|
|
3248
3216
|
const promptsDir = join(configDir, "prompts");
|
|
@@ -3252,21 +3220,25 @@ export function promptAgentSidecarErrors(configDir) {
|
|
|
3252
3220
|
if (!filePath.startsWith(promptsDir) || !parent.endsWith(".tests"))
|
|
3253
3221
|
continue;
|
|
3254
3222
|
const key = parent.slice(0, -".tests".length);
|
|
3255
|
-
const
|
|
3256
|
-
if (
|
|
3223
|
+
const kind = testCaseOwnerPromptKind(configDir, "prompt", key);
|
|
3224
|
+
if (kind === undefined)
|
|
3257
3225
|
continue;
|
|
3258
|
-
let
|
|
3226
|
+
let sidecar;
|
|
3259
3227
|
try {
|
|
3260
|
-
|
|
3228
|
+
sidecar = parseTomlFile(filePath);
|
|
3261
3229
|
}
|
|
3262
3230
|
catch {
|
|
3231
|
+
// The test-case collector reports a file that does not parse.
|
|
3263
3232
|
continue;
|
|
3264
3233
|
}
|
|
3265
|
-
|
|
3234
|
+
const resolved = resolveAgentTestCase({
|
|
3235
|
+
kind,
|
|
3236
|
+
fields: agentTestCaseWireFields(parseTestCaseToml(sidecar)),
|
|
3237
|
+
});
|
|
3238
|
+
if (!("refusals" in resolved))
|
|
3266
3239
|
continue;
|
|
3267
|
-
const promptKey = tomlData?.prompt?.key || key;
|
|
3268
3240
|
errors.push(`${filePath.slice(configDir.length + 1)}: ` +
|
|
3269
|
-
|
|
3241
|
+
resolved.refusals.map((refusal) => refusal.message).join("; "));
|
|
3270
3242
|
}
|
|
3271
3243
|
return errors;
|
|
3272
3244
|
}
|
|
@@ -3339,18 +3311,12 @@ export function configFileValidationMessages(filePath, tomlData, surface, tables
|
|
|
3339
3311
|
if (surface.label === "prompt") {
|
|
3340
3312
|
messages.push(...promptKindFileErrors(filePath, tomlData));
|
|
3341
3313
|
}
|
|
3342
|
-
// #4116 — a bucket naming its rule set both ways.
|
|
3343
|
-
if (surface.label === "blob-bucket") {
|
|
3344
|
-
messages.push(...blobBucketFileErrors(filePath, tomlData));
|
|
3345
|
-
}
|
|
3346
3314
|
return messages;
|
|
3347
3315
|
}
|
|
3348
3316
|
/**
|
|
3349
3317
|
* Whether `config pull` must rewrite a local file whatever it says (#4160):
|
|
3350
|
-
* `config push` would refuse it
|
|
3351
|
-
*
|
|
3352
|
-
* file because its VALUES match would leave the tree unpushable, or the
|
|
3353
|
-
* deprecation unresolved, after a pull that reported success.
|
|
3318
|
+
* `config push` would refuse it. Keeping such a file because its VALUES match
|
|
3319
|
+
* would leave the tree unpushable after a pull that reported success.
|
|
3354
3320
|
*/
|
|
3355
3321
|
export function pullMustRewrite(label, tomlData) {
|
|
3356
3322
|
// A test-case sidecar is not one of the preflight's per-directory targets;
|
|
@@ -3365,7 +3331,7 @@ export function pullMustRewrite(label, tomlData) {
|
|
|
3365
3331
|
return true;
|
|
3366
3332
|
}
|
|
3367
3333
|
}
|
|
3368
|
-
return
|
|
3334
|
+
return false;
|
|
3369
3335
|
}
|
|
3370
3336
|
export function collectConfigFileValidationErrors(configDir) {
|
|
3371
3337
|
const found = [];
|
|
@@ -3443,35 +3409,6 @@ export function collectFunctionTomlValidationErrors(configDir) {
|
|
|
3443
3409
|
}
|
|
3444
3410
|
return found;
|
|
3445
3411
|
}
|
|
3446
|
-
/**
|
|
3447
|
-
* The retired runtime keys every `functions/*.toml` in the tree still carries
|
|
3448
|
-
* — #3482, the warning half of the collector above.
|
|
3449
|
-
*
|
|
3450
|
-
* Separate from the errors because it IS separate: a file carrying one of
|
|
3451
|
-
* these pushes, with every other key applied, so it is not a row `config diff`
|
|
3452
|
-
* calls invalid. It is a line an author reads on stderr from both commands, so
|
|
3453
|
-
* the two keep reading identically (the #2880 symmetry the errors collector
|
|
3454
|
-
* exists for, applied to advice rather than refusals).
|
|
3455
|
-
*/
|
|
3456
|
-
export function collectFunctionTomlIgnoredKeyWarnings(configDir) {
|
|
3457
|
-
const found = [];
|
|
3458
|
-
const functionsDir = join(configDir, "functions");
|
|
3459
|
-
if (!existsSync(functionsDir))
|
|
3460
|
-
return found;
|
|
3461
|
-
for (const file of readdirSync(functionsDir).filter(isFunctionDeclarationFile)) {
|
|
3462
|
-
const filePath = join(functionsDir, file);
|
|
3463
|
-
let tomlData;
|
|
3464
|
-
try {
|
|
3465
|
-
tomlData = parseTomlFile(filePath);
|
|
3466
|
-
}
|
|
3467
|
-
catch {
|
|
3468
|
-
continue;
|
|
3469
|
-
}
|
|
3470
|
-
const key = tomlData?.function?.key || basename(file, ".toml");
|
|
3471
|
-
found.push(...functionTomlIgnoredKeyWarnings({ functionKey: key, tomlData }));
|
|
3472
|
-
}
|
|
3473
|
-
return found;
|
|
3474
|
-
}
|
|
3475
3412
|
/**
|
|
3476
3413
|
* The collectors' rows, one per diff row (#2880 symmetry, #3320).
|
|
3477
3414
|
*
|
|
@@ -5001,12 +4938,11 @@ const CONFIG_DIFF_SPECS = {
|
|
|
5001
4938
|
keys.push("name");
|
|
5002
4939
|
if (!bucket.preset)
|
|
5003
4940
|
keys.push("preset");
|
|
5004
|
-
//
|
|
5005
|
-
//
|
|
5006
|
-
//
|
|
4941
|
+
// A name manages the rule set (#4116) — read as the parse reads it, so a
|
|
4942
|
+
// blank name is no reference rather than a difference the update cannot
|
|
4943
|
+
// carry. The retired id spelling (#4283) manages nothing.
|
|
5007
4944
|
const named = typeof bucket.ruleSetName === "string" && bucket.ruleSetName.trim() !== "";
|
|
5008
|
-
|
|
5009
|
-
if (!named && !legacy)
|
|
4945
|
+
if (!named)
|
|
5010
4946
|
keys.push("ruleSetId");
|
|
5011
4947
|
return keys;
|
|
5012
4948
|
},
|
|
@@ -5974,6 +5910,17 @@ function testCaseSidecarTable(testCase, lookupMaps, unresolvedSpelling) {
|
|
|
5974
5910
|
expectedOutputPattern: (record) => record.expectedOutputPattern || "",
|
|
5975
5911
|
expectedOutputContains: (record) => jsonFieldText(record.expectedOutputContains, "[]"),
|
|
5976
5912
|
expectedJsonSubset: (record) => jsonFieldText(record.expectedJsonSubset, "{}"),
|
|
5913
|
+
// #4262 — `[test.agent]`, written only when a key is set, so a chat case's
|
|
5914
|
+
// sidecar is the one it always was. The history is the text the author
|
|
5915
|
+
// pushed; the two JSON fields come back parsed and are written as text.
|
|
5916
|
+
agentHistory: (record) => agentJsonText(record.agentHistory),
|
|
5917
|
+
agentContext: (record) => agentJsonText(record.agentContext),
|
|
5918
|
+
agentTimezone: (record) => record.agentTimezone || undefined,
|
|
5919
|
+
agentLocale: (record) => record.agentLocale || undefined,
|
|
5920
|
+
agentExpectedToolCall: (record) => agentJsonText(record.agentExpectedToolCall),
|
|
5921
|
+
agentExpectedNoToolCall: (record) => typeof record.agentExpectedNoToolCall === "boolean"
|
|
5922
|
+
? record.agentExpectedNoToolCall
|
|
5923
|
+
: undefined,
|
|
5977
5924
|
});
|
|
5978
5925
|
return { test, unresolved };
|
|
5979
5926
|
}
|
|
@@ -6028,6 +5975,38 @@ function jsonFieldText(value, empty) {
|
|
|
6028
5975
|
return empty;
|
|
6029
5976
|
}
|
|
6030
5977
|
}
|
|
5978
|
+
/**
|
|
5979
|
+
* #4262 — an agent block field as the sidecar's JSON text, or `undefined`
|
|
5980
|
+
* (omitted) when unset: `[test.agent]` has no empty spellings.
|
|
5981
|
+
*/
|
|
5982
|
+
function agentJsonText(value) {
|
|
5983
|
+
if (value === undefined || value === null)
|
|
5984
|
+
return undefined;
|
|
5985
|
+
if (typeof value === "string")
|
|
5986
|
+
return value === "" ? undefined : value;
|
|
5987
|
+
try {
|
|
5988
|
+
return JSON.stringify(value);
|
|
5989
|
+
}
|
|
5990
|
+
catch {
|
|
5991
|
+
return undefined;
|
|
5992
|
+
}
|
|
5993
|
+
}
|
|
5994
|
+
/**
|
|
5995
|
+
* #4262 — an agent block JSON field on the wire: parsed when the text parses,
|
|
5996
|
+
* the text itself otherwise (the shape rule names it), `undefined` when unset.
|
|
5997
|
+
*/
|
|
5998
|
+
function agentJsonWireValue(value) {
|
|
5999
|
+
if (value === undefined || value === null || value === "")
|
|
6000
|
+
return undefined;
|
|
6001
|
+
if (typeof value !== "string")
|
|
6002
|
+
return value;
|
|
6003
|
+
try {
|
|
6004
|
+
return JSON.parse(value);
|
|
6005
|
+
}
|
|
6006
|
+
catch {
|
|
6007
|
+
return value;
|
|
6008
|
+
}
|
|
6009
|
+
}
|
|
6031
6010
|
export function parseTestCaseToml(tomlData) {
|
|
6032
6011
|
const test = tomlData.test || {};
|
|
6033
6012
|
/** A JSON-string TOML value, or `undefined` when it is absent/the default. */
|
|
@@ -6068,6 +6047,15 @@ export function parseTestCaseToml(tomlData) {
|
|
|
6068
6047
|
expectedOutputPattern: (value) => (value ? value : undefined),
|
|
6069
6048
|
expectedOutputContains: (value) => parseJsonText(value, ["[]"]),
|
|
6070
6049
|
expectedJsonSubset: (value) => parseJsonText(value, ["{}"]),
|
|
6050
|
+
// #4262 — `[test.agent]`. The history travels as the JSON text it is
|
|
6051
|
+
// authored as; context and expectedToolCall travel parsed; `false` is a
|
|
6052
|
+
// value, not an absence.
|
|
6053
|
+
agentHistory: (value) => (value === "" ? undefined : value),
|
|
6054
|
+
agentContext: (value) => agentJsonWireValue(value),
|
|
6055
|
+
agentTimezone: (value) => (value === "" ? undefined : value),
|
|
6056
|
+
agentLocale: (value) => (value === "" ? undefined : value),
|
|
6057
|
+
agentExpectedToolCall: (value) => agentJsonWireValue(value),
|
|
6058
|
+
agentExpectedNoToolCall: (value) => value,
|
|
6071
6059
|
},
|
|
6072
6060
|
});
|
|
6073
6061
|
// An omitted field must be absent from the body, not present-and-undefined:
|
|
@@ -7293,6 +7281,15 @@ export async function pushTestCasesForBlock(params) {
|
|
|
7293
7281
|
evaluatorPromptId: testPayload.evaluatorPromptId ?? null,
|
|
7294
7282
|
evaluatorConfigId: testPayload.evaluatorConfigId ?? null,
|
|
7295
7283
|
};
|
|
7284
|
+
// #4262 — an agent prompt's case carries its `[test.agent]` block as
|
|
7285
|
+
// full state too: a key the file no longer sets goes as `null`, so
|
|
7286
|
+
// removing an assertion clears it. A chat or decisions prompt's case
|
|
7287
|
+
// sends none, so its body is the one it always was.
|
|
7288
|
+
if (testCaseOwnerPromptKind(configDir, blockType, blockKey) === "agent") {
|
|
7289
|
+
for (const key of AGENT_TEST_CASE_FIELDS) {
|
|
7290
|
+
updatePayload[key] = testPayload[key] ?? null;
|
|
7291
|
+
}
|
|
7292
|
+
}
|
|
7296
7293
|
// #2896 — the identity travels with the content: this is the lazy
|
|
7297
7294
|
// migration of a legacy case and the second half of a rename.
|
|
7298
7295
|
if (pendingKey !== undefined)
|
|
@@ -17467,18 +17464,11 @@ What a push guarantees (two stages):
|
|
|
17467
17464
|
// verbatim, so the two commands read identically — replaces whatever
|
|
17468
17465
|
// the comparison had to say about the file.
|
|
17469
17466
|
// #3320 — the function checks join them. Same posture, same messages:
|
|
17470
|
-
// a function file whose `entry`,
|
|
17471
|
-
// signing-secret the push preflight refuses is not
|
|
17472
|
-
// calls Synced, Modified or "local only — new".
|
|
17467
|
+
// a function file whose `entry`, retired runtime keys (#3188),
|
|
17468
|
+
// triggers or webhook signing-secret the push preflight refuses is not
|
|
17469
|
+
// a row this command calls Synced, Modified or "local only — new".
|
|
17473
17470
|
// Merged first: a file both collectors reject keeps every message push
|
|
17474
17471
|
// prints, rather than one collector's hint overwriting the other's.
|
|
17475
|
-
// #3482 — the same advisory `config push` prints, so an author sees
|
|
17476
|
-
// the retired key before they push rather than only during it. Not a
|
|
17477
|
-
// row status: the file is pushable, and saying otherwise would make
|
|
17478
|
-
// `config diff` disagree with the push it predicts.
|
|
17479
|
-
for (const message of collectFunctionTomlIgnoredKeyWarnings(configDir)) {
|
|
17480
|
-
warn(` ${message}`);
|
|
17481
|
-
}
|
|
17482
17472
|
for (const rejected of mergeConfigFileValidationErrors([
|
|
17483
17473
|
...collectConfigFileValidationErrors(configDir),
|
|
17484
17474
|
...collectFunctionTomlValidationErrors(configDir),
|