@agent-surface/core 0.5.0 → 0.6.0
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/index.d.ts +8 -0
- package/dist/index.js +206 -71
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -171,6 +171,14 @@ declare function assignWireNames(entries: readonly WireNameEntry[]): WireNameAss
|
|
|
171
171
|
* names among them (`AS-WIRE-007`: consult `toolset.wireNameMap()` instead).
|
|
172
172
|
* Returning a plausible-but-wrong canonical id would take the audit identity
|
|
173
173
|
* with it, so this refuses anything it cannot re-encode byte-identically.
|
|
174
|
+
*
|
|
175
|
+
* Refusal is decided by what a name *is*, never by a substring it happens to
|
|
176
|
+
* contain. `view:at.a.a` encodes to `view_at__a__a`, where `_at_` is the plane
|
|
177
|
+
* separator meeting a segment named "at" — screening for the marker text cost
|
|
178
|
+
* every id with an `at` or `0` segment its own faithful encoding (`AS-ID-004`).
|
|
179
|
+
* Marker-bearing names are still refused, by the two checks that can tell:
|
|
180
|
+
* every underscore run must be exactly two (one "."), and the id must re-encode
|
|
181
|
+
* byte-identically.
|
|
174
182
|
*/
|
|
175
183
|
declare function decodeWireName(name: string): string | undefined;
|
|
176
184
|
|
package/dist/index.js
CHANGED
|
@@ -82,7 +82,7 @@ function formatDomainCapabilityId(path) {
|
|
|
82
82
|
return `domain:${path}`;
|
|
83
83
|
}
|
|
84
84
|
function parseCapabilityId(id) {
|
|
85
|
-
if (id.length > MAX_ID_LENGTH) return void 0;
|
|
85
|
+
if (typeof id !== "string" || id.length > MAX_ID_LENGTH) return void 0;
|
|
86
86
|
if (id.startsWith("view:")) {
|
|
87
87
|
const rest = id.slice("view:".length);
|
|
88
88
|
const lastDot = rest.lastIndexOf(".");
|
|
@@ -125,7 +125,7 @@ function encodeWireName(id) {
|
|
|
125
125
|
}
|
|
126
126
|
function encodeWireNameForInstance(id, instanceId, level = 0) {
|
|
127
127
|
const raw = rawWireName(id, instanceId);
|
|
128
|
-
if (level === 0 && raw.length <= MAX_WIRE_NAME_LENGTH) return raw;
|
|
128
|
+
if (level === 0 && raw.length <= MAX_WIRE_NAME_LENGTH && !id.includes("_")) return raw;
|
|
129
129
|
const hashLength = 7 + level * 2;
|
|
130
130
|
const keep = MAX_WIRE_NAME_LENGTH - SHORTENED_MARKER.length - hashLength;
|
|
131
131
|
const hash = hash36(`${id}#${instanceId ?? ""}#${level}`, hashLength);
|
|
@@ -169,12 +169,15 @@ function assignWireNames(entries) {
|
|
|
169
169
|
return { names, byName };
|
|
170
170
|
}
|
|
171
171
|
function decodeWireName(name) {
|
|
172
|
-
if (name.includes(SHORTENED_MARKER) || name.includes(INSTANCE_MARKER)) return void 0;
|
|
173
172
|
const planeEnd = name.indexOf("_");
|
|
174
173
|
if (planeEnd <= 0) return void 0;
|
|
175
174
|
const plane = name.slice(0, planeEnd);
|
|
176
175
|
if (plane !== "view" && plane !== "domain") return void 0;
|
|
177
|
-
const
|
|
176
|
+
const rest = name.slice(planeEnd + 1);
|
|
177
|
+
if (/_{3,}/.test(rest)) return void 0;
|
|
178
|
+
const path = rest.replaceAll("__", ".");
|
|
179
|
+
if (path.split(".").some((segment) => segment === "")) return void 0;
|
|
180
|
+
const id = `${plane}:${path}`;
|
|
178
181
|
if (id.includes("_") || !parseCapabilityId(id) || encodeWireName(id) !== name) return void 0;
|
|
179
182
|
return id;
|
|
180
183
|
}
|
|
@@ -1361,6 +1364,7 @@ var ConfirmationStore = class {
|
|
|
1361
1364
|
};
|
|
1362
1365
|
|
|
1363
1366
|
// src/internal.ts
|
|
1367
|
+
var DEV_WARN = /* @__PURE__ */ Symbol("agent-surface.dev-warn");
|
|
1364
1368
|
var DevDefectError = class extends Error {
|
|
1365
1369
|
constructor(message) {
|
|
1366
1370
|
super(message);
|
|
@@ -3034,6 +3038,10 @@ function createAgentSurfaceRegistry(options) {
|
|
|
3034
3038
|
dispatcher.clear();
|
|
3035
3039
|
}
|
|
3036
3040
|
};
|
|
3041
|
+
Object.defineProperty(registry, DEV_WARN, {
|
|
3042
|
+
value: (...args) => internals.devWarn(...args),
|
|
3043
|
+
enumerable: false
|
|
3044
|
+
});
|
|
3037
3045
|
return registry;
|
|
3038
3046
|
}
|
|
3039
3047
|
function combineSinks(...sinks) {
|
|
@@ -3050,6 +3058,75 @@ var EMPTY_INPUT_SCHEMA = {
|
|
|
3050
3058
|
properties: {},
|
|
3051
3059
|
additionalProperties: false
|
|
3052
3060
|
};
|
|
3061
|
+
var META_DISCOVER_SCHEMA = {
|
|
3062
|
+
type: "object",
|
|
3063
|
+
properties: {
|
|
3064
|
+
scope: {
|
|
3065
|
+
type: "array",
|
|
3066
|
+
items: { type: "string" },
|
|
3067
|
+
// No enum: valid tokens are live component types, and inlining them
|
|
3068
|
+
// would make this tool block churn on every mount — the churn
|
|
3069
|
+
// AS-META-005 and D28 exist to prevent.
|
|
3070
|
+
description: 'Component-type prefixes to narrow the result, e.g. ["devices.table"], taken from `components[].type` of an earlier call \u2014 omit on the first. Narrows only: prefixes outside this host\'s configured scope match nothing and come back in `scopeRejected`.'
|
|
3071
|
+
}
|
|
3072
|
+
},
|
|
3073
|
+
additionalProperties: false
|
|
3074
|
+
};
|
|
3075
|
+
var META_READ_SCHEMA = {
|
|
3076
|
+
type: "object",
|
|
3077
|
+
properties: {
|
|
3078
|
+
capabilityId: {
|
|
3079
|
+
type: "string",
|
|
3080
|
+
description: "Observation id, verbatim from `observations[].capabilityId` in a discover result."
|
|
3081
|
+
},
|
|
3082
|
+
instanceId: {
|
|
3083
|
+
type: "string",
|
|
3084
|
+
description: "Only when several components share a type: `components[].instanceId` picks one."
|
|
3085
|
+
}
|
|
3086
|
+
},
|
|
3087
|
+
required: ["capabilityId"],
|
|
3088
|
+
additionalProperties: false
|
|
3089
|
+
};
|
|
3090
|
+
var META_ACT_SCHEMA = {
|
|
3091
|
+
type: "object",
|
|
3092
|
+
properties: {
|
|
3093
|
+
capabilityId: {
|
|
3094
|
+
type: "string",
|
|
3095
|
+
description: "Action `capabilityId` or `procedureId`, verbatim from a discover result."
|
|
3096
|
+
},
|
|
3097
|
+
instanceId: {
|
|
3098
|
+
type: "string",
|
|
3099
|
+
description: "Only when several components share a type: `components[].instanceId` picks one."
|
|
3100
|
+
},
|
|
3101
|
+
// Typed, and not merely described: an untyped property is the one position
|
|
3102
|
+
// a provider's constrained decoder cannot constrain, so the model falls
|
|
3103
|
+
// back to its prior — a JSON-encoded string, the shape
|
|
3104
|
+
// `function_call.arguments` carries — and sorts the rest of the capability's
|
|
3105
|
+
// arguments into the sibling modifiers below. `type: "object"` costs
|
|
3106
|
+
// nothing in practice: direct mode already passes `act.inputSchema`
|
|
3107
|
+
// straight through as the tool schema, and providers require that to be an
|
|
3108
|
+
// object schema at the top level. No `additionalProperties` here — the
|
|
3109
|
+
// capability's own schema governs what goes inside.
|
|
3110
|
+
input: {
|
|
3111
|
+
type: "object",
|
|
3112
|
+
description: "Arguments matching that capability's `inputSchema`, as a JSON object \u2014 not a JSON-encoded string. Everything the capability declares goes in here, never beside it."
|
|
3113
|
+
},
|
|
3114
|
+
invocationId: {
|
|
3115
|
+
type: "string",
|
|
3116
|
+
description: "Reuse a previous call's id to retry without executing twice; required when resuming after CONFIRMATION_REQUIRED."
|
|
3117
|
+
},
|
|
3118
|
+
confirmationId: {
|
|
3119
|
+
type: "string",
|
|
3120
|
+
description: "The id returned with CONFIRMATION_REQUIRED, sent back after the user approves."
|
|
3121
|
+
},
|
|
3122
|
+
surfaceVersion: {
|
|
3123
|
+
type: "string",
|
|
3124
|
+
description: "The `surfaceVersion` you planned against. Send it for destructive or externally-visible calls: a surface that moved underneath the plan then fails instead of executing. Omitted, the call binds to what is live now."
|
|
3125
|
+
}
|
|
3126
|
+
},
|
|
3127
|
+
required: ["capabilityId"],
|
|
3128
|
+
additionalProperties: false
|
|
3129
|
+
};
|
|
3053
3130
|
function describePrefix(plane, effect, confirmation) {
|
|
3054
3131
|
const parts = [plane, effect];
|
|
3055
3132
|
if (confirmation === "required") parts.push("requires confirmation");
|
|
@@ -3075,6 +3152,8 @@ function createAgentToolset(registry, options) {
|
|
|
3075
3152
|
);
|
|
3076
3153
|
}
|
|
3077
3154
|
const confirmationsMode = options.confirmations ?? (options.topology === "remote" ? "two-phase" : "wait");
|
|
3155
|
+
const devWarn = registry[DEV_WARN] ?? (() => {
|
|
3156
|
+
});
|
|
3078
3157
|
const listeners = /* @__PURE__ */ new Set();
|
|
3079
3158
|
const pendingWaits = /* @__PURE__ */ new Set();
|
|
3080
3159
|
let disposed = false;
|
|
@@ -3204,6 +3283,15 @@ function createAgentToolset(registry, options) {
|
|
|
3204
3283
|
}));
|
|
3205
3284
|
return { tools, wireNames: assignment.byName };
|
|
3206
3285
|
}
|
|
3286
|
+
function envelopeFailure(metaCapabilityId, capabilityId, error, toolCallId) {
|
|
3287
|
+
return {
|
|
3288
|
+
status: "error",
|
|
3289
|
+
invocationId: toolCallId ?? `inv_${randomBase62(12)}`,
|
|
3290
|
+
capabilityId: typeof capabilityId === "string" && capabilityId.length > 0 ? capabilityId : metaCapabilityId,
|
|
3291
|
+
error,
|
|
3292
|
+
surfaceVersion: registry.getVersion()
|
|
3293
|
+
};
|
|
3294
|
+
}
|
|
3207
3295
|
function buildMetaTools() {
|
|
3208
3296
|
const snapshotFor = () => registry.snapshot({
|
|
3209
3297
|
consumer: options.consumer,
|
|
@@ -3213,21 +3301,12 @@ function createAgentToolset(registry, options) {
|
|
|
3213
3301
|
{
|
|
3214
3302
|
name: "surface_discover",
|
|
3215
3303
|
description: "[meta] Discover the current agent surface: components, capabilities, procedures, availability, schemas.",
|
|
3216
|
-
inputSchema:
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
// No enum: valid tokens are live component types, and inlining
|
|
3223
|
-
// them would make this tool block churn on every mount —
|
|
3224
|
-
// the churn AS-META-005 and D28 exist to prevent.
|
|
3225
|
-
description: 'Component-type prefixes to narrow the result, e.g. ["devices.table"], taken from `components[].type` of an earlier call \u2014 omit on the first. Narrows only: prefixes outside this host\'s configured scope match nothing and come back in `scopeRejected`.'
|
|
3226
|
-
}
|
|
3227
|
-
},
|
|
3228
|
-
additionalProperties: false
|
|
3229
|
-
},
|
|
3230
|
-
async execute(input) {
|
|
3304
|
+
inputSchema: META_DISCOVER_SCHEMA,
|
|
3305
|
+
async execute(input, call) {
|
|
3306
|
+
const invalid = validateEnvelope("surface_discover", META_DISCOVER_SCHEMA, input);
|
|
3307
|
+
if (invalid) {
|
|
3308
|
+
return envelopeFailure("meta:surface.discover", void 0, invalid, call.toolCallId);
|
|
3309
|
+
}
|
|
3231
3310
|
const requested = input?.scope;
|
|
3232
3311
|
const effective = intersectScope(options.scope, requested);
|
|
3233
3312
|
const snapshot = registry.snapshot({
|
|
@@ -3255,25 +3334,15 @@ function createAgentToolset(registry, options) {
|
|
|
3255
3334
|
{
|
|
3256
3335
|
name: "surface_read",
|
|
3257
3336
|
description: "[meta] Invoke an observation by capabilityId and return its output.",
|
|
3258
|
-
inputSchema:
|
|
3259
|
-
type: "object",
|
|
3260
|
-
properties: {
|
|
3261
|
-
capabilityId: {
|
|
3262
|
-
type: "string",
|
|
3263
|
-
description: "Observation id, verbatim from `observations[].capabilityId` in a discover result."
|
|
3264
|
-
},
|
|
3265
|
-
instanceId: {
|
|
3266
|
-
type: "string",
|
|
3267
|
-
description: "Only when several components share a type: `components[].instanceId` picks one."
|
|
3268
|
-
}
|
|
3269
|
-
},
|
|
3270
|
-
required: ["capabilityId"],
|
|
3271
|
-
additionalProperties: false
|
|
3272
|
-
},
|
|
3337
|
+
inputSchema: META_READ_SCHEMA,
|
|
3273
3338
|
async execute(input, call) {
|
|
3274
|
-
const req = input;
|
|
3339
|
+
const req = input ?? {};
|
|
3340
|
+
const invalid = validateEnvelope("surface_read", META_READ_SCHEMA, input);
|
|
3341
|
+
if (invalid) {
|
|
3342
|
+
return envelopeFailure("meta:surface.read", req.capabilityId, invalid, call.toolCallId);
|
|
3343
|
+
}
|
|
3275
3344
|
const snapshot = snapshotFor();
|
|
3276
|
-
const registrationId =
|
|
3345
|
+
const { registrationId } = findTarget(snapshot, req.capabilityId, req.instanceId);
|
|
3277
3346
|
return invokeThroughSurface(
|
|
3278
3347
|
{
|
|
3279
3348
|
capabilityId: req.capabilityId,
|
|
@@ -3291,38 +3360,27 @@ function createAgentToolset(registry, options) {
|
|
|
3291
3360
|
{
|
|
3292
3361
|
name: "surface_act",
|
|
3293
3362
|
description: "[meta] Invoke an action or procedure by capabilityId. Echo the surfaceVersion you discovered so a surface that changed underneath a destructive plan is rejected rather than executed.",
|
|
3294
|
-
inputSchema:
|
|
3295
|
-
type: "object",
|
|
3296
|
-
properties: {
|
|
3297
|
-
capabilityId: {
|
|
3298
|
-
type: "string",
|
|
3299
|
-
description: "Action `capabilityId` or `procedureId`, verbatim from a discover result."
|
|
3300
|
-
},
|
|
3301
|
-
instanceId: {
|
|
3302
|
-
type: "string",
|
|
3303
|
-
description: "Only when several components share a type: `components[].instanceId` picks one."
|
|
3304
|
-
},
|
|
3305
|
-
input: { description: "Arguments matching that capability's `inputSchema`." },
|
|
3306
|
-
invocationId: {
|
|
3307
|
-
type: "string",
|
|
3308
|
-
description: "Reuse a previous call's id to retry without executing twice; required when resuming after CONFIRMATION_REQUIRED."
|
|
3309
|
-
},
|
|
3310
|
-
confirmationId: {
|
|
3311
|
-
type: "string",
|
|
3312
|
-
description: "The id returned with CONFIRMATION_REQUIRED, sent back after the user approves."
|
|
3313
|
-
},
|
|
3314
|
-
surfaceVersion: {
|
|
3315
|
-
type: "string",
|
|
3316
|
-
description: "The `surfaceVersion` you planned against. Send it for destructive or externally-visible calls: a surface that moved underneath the plan then fails instead of executing. Omitted, the call binds to what is live now."
|
|
3317
|
-
}
|
|
3318
|
-
},
|
|
3319
|
-
required: ["capabilityId"],
|
|
3320
|
-
additionalProperties: false
|
|
3321
|
-
},
|
|
3363
|
+
inputSchema: META_ACT_SCHEMA,
|
|
3322
3364
|
async execute(input, call) {
|
|
3323
|
-
const req = input;
|
|
3365
|
+
const req = input ?? {};
|
|
3366
|
+
const invalid = validateEnvelope("surface_act", META_ACT_SCHEMA, input, ["input"]);
|
|
3367
|
+
if (invalid) {
|
|
3368
|
+
return envelopeFailure("meta:surface.act", req.capabilityId, invalid, call.toolCallId);
|
|
3369
|
+
}
|
|
3324
3370
|
const snapshot = snapshotFor();
|
|
3325
|
-
const registrationId =
|
|
3371
|
+
const { registrationId, inputSchema } = findTarget(
|
|
3372
|
+
snapshot,
|
|
3373
|
+
req.capabilityId,
|
|
3374
|
+
req.instanceId
|
|
3375
|
+
);
|
|
3376
|
+
let actInput = req.input;
|
|
3377
|
+
const parsed = parseStringifiedObject(actInput, inputSchema);
|
|
3378
|
+
if (parsed !== void 0) {
|
|
3379
|
+
actInput = parsed;
|
|
3380
|
+
devWarn(
|
|
3381
|
+
`[agent-surface] surface_act got \`input\` as a JSON-encoded string for "${req.capabilityId}" and parsed it; the provider is not honoring the tool schema.`
|
|
3382
|
+
);
|
|
3383
|
+
}
|
|
3326
3384
|
return invokeThroughSurface(
|
|
3327
3385
|
{
|
|
3328
3386
|
capabilityId: req.capabilityId,
|
|
@@ -3331,7 +3389,7 @@ function createAgentToolset(registry, options) {
|
|
|
3331
3389
|
surfaceVersion: req.surfaceVersion ?? snapshot.surfaceVersion,
|
|
3332
3390
|
kind: "action"
|
|
3333
3391
|
},
|
|
3334
|
-
|
|
3392
|
+
actInput,
|
|
3335
3393
|
call.toolCallId,
|
|
3336
3394
|
{
|
|
3337
3395
|
...req.invocationId !== void 0 ? { invocationId: req.invocationId } : {},
|
|
@@ -3425,7 +3483,7 @@ function intersectScope(floor, requested) {
|
|
|
3425
3483
|
}
|
|
3426
3484
|
return out.size > 0 ? { scope: [...out], empty: false, rejected } : { empty: true, rejected };
|
|
3427
3485
|
}
|
|
3428
|
-
function
|
|
3486
|
+
function findTarget(snapshot, capabilityId, instanceId) {
|
|
3429
3487
|
const matches = [];
|
|
3430
3488
|
for (const component of snapshot.components) {
|
|
3431
3489
|
if (instanceId !== void 0 && component.instanceId !== instanceId) continue;
|
|
@@ -3433,12 +3491,89 @@ function findRegistrationId(snapshot, capabilityId, instanceId) {
|
|
|
3433
3491
|
...component.observations,
|
|
3434
3492
|
...component.actions
|
|
3435
3493
|
];
|
|
3436
|
-
|
|
3494
|
+
const hit = all.find((c) => c.capabilityId === capabilityId);
|
|
3495
|
+
if (hit) {
|
|
3496
|
+
matches.push({
|
|
3497
|
+
registrationId: component.registrationId,
|
|
3498
|
+
..."inputSchema" in hit ? { inputSchema: hit.inputSchema } : {}
|
|
3499
|
+
});
|
|
3500
|
+
}
|
|
3437
3501
|
}
|
|
3438
3502
|
for (const proc of snapshot.procedures) {
|
|
3439
|
-
if (proc.procedureId === capabilityId)
|
|
3503
|
+
if (proc.procedureId === capabilityId) {
|
|
3504
|
+
matches.push({ registrationId: proc.registrationId, inputSchema: proc.inputSchema });
|
|
3505
|
+
}
|
|
3506
|
+
}
|
|
3507
|
+
return matches.length === 1 ? matches[0] : {};
|
|
3508
|
+
}
|
|
3509
|
+
function validateEnvelope(verb, schema, raw, exempt = []) {
|
|
3510
|
+
if (raw !== void 0 && raw !== null && (typeof raw !== "object" || Array.isArray(raw))) {
|
|
3511
|
+
return envelopeError(verb, [
|
|
3512
|
+
{ path: "", message: `\`${verb}\` takes a JSON object of arguments.` }
|
|
3513
|
+
]);
|
|
3514
|
+
}
|
|
3515
|
+
const properties = schema.properties ?? {};
|
|
3516
|
+
const known = Object.keys(properties);
|
|
3517
|
+
const required = schema.required ?? [];
|
|
3518
|
+
const req = raw ?? {};
|
|
3519
|
+
const issues = [];
|
|
3520
|
+
for (const key of required) {
|
|
3521
|
+
if (req[key] === void 0) issues.push({ path: key, message: `\`${key}\` is required.` });
|
|
3522
|
+
}
|
|
3523
|
+
for (const [key, value] of Object.entries(req)) {
|
|
3524
|
+
if (value === void 0) continue;
|
|
3525
|
+
if (!known.includes(key)) {
|
|
3526
|
+
if (schema.additionalProperties === false) {
|
|
3527
|
+
issues.push({
|
|
3528
|
+
path: key,
|
|
3529
|
+
// The high-value half is the pointer back at `input`: it turns the
|
|
3530
|
+
// dead end of a hoisted capability argument into a one-retry
|
|
3531
|
+
// recovery. A verb without an `input` has nowhere to point.
|
|
3532
|
+
message: `Unknown top-level property. \`${verb}\` accepts only ${known.join(", ")}.${known.includes("input") ? " An argument the capability declares belongs inside `input`." : ""}`
|
|
3533
|
+
});
|
|
3534
|
+
}
|
|
3535
|
+
continue;
|
|
3536
|
+
}
|
|
3537
|
+
if (exempt.includes(key)) continue;
|
|
3538
|
+
const issue = checkDeclaredType(key, properties[key], value);
|
|
3539
|
+
if (issue) issues.push(issue);
|
|
3540
|
+
}
|
|
3541
|
+
return issues.length > 0 ? envelopeError(verb, issues) : void 0;
|
|
3542
|
+
}
|
|
3543
|
+
function checkDeclaredType(key, property, value) {
|
|
3544
|
+
if (property.type === "string" && (typeof value !== "string" || value.length === 0)) {
|
|
3545
|
+
return { path: key, message: `\`${key}\` must be a non-empty string.` };
|
|
3546
|
+
}
|
|
3547
|
+
if (property.type === "array") {
|
|
3548
|
+
if (!Array.isArray(value)) return { path: key, message: `\`${key}\` must be an array.` };
|
|
3549
|
+
const items = property.items;
|
|
3550
|
+
if (items?.type === "string" && !value.every((item) => typeof item === "string")) {
|
|
3551
|
+
return { path: key, message: `\`${key}\` must be an array of strings.` };
|
|
3552
|
+
}
|
|
3553
|
+
}
|
|
3554
|
+
return void 0;
|
|
3555
|
+
}
|
|
3556
|
+
function envelopeError(verb, issues) {
|
|
3557
|
+
return {
|
|
3558
|
+
code: "INVALID_INPUT",
|
|
3559
|
+
// Names the envelope, not the capability: pointing the model at the
|
|
3560
|
+
// capability's schema when the wrapper is what is wrong sends it to fix
|
|
3561
|
+
// something that is already correct.
|
|
3562
|
+
message: `The \`${verb}\` call is malformed \u2014 the fault is in the tool's own arguments, not the capability's input. Fix the listed issues and retry.`,
|
|
3563
|
+
retry: "with-changes",
|
|
3564
|
+
details: { issues }
|
|
3565
|
+
};
|
|
3566
|
+
}
|
|
3567
|
+
function parseStringifiedObject(value, targetSchema) {
|
|
3568
|
+
if (typeof value !== "string" || targetSchema?.type !== "object") return void 0;
|
|
3569
|
+
let parsed;
|
|
3570
|
+
try {
|
|
3571
|
+
parsed = JSON.parse(value);
|
|
3572
|
+
} catch {
|
|
3573
|
+
return void 0;
|
|
3440
3574
|
}
|
|
3441
|
-
|
|
3575
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return void 0;
|
|
3576
|
+
return parsed;
|
|
3442
3577
|
}
|
|
3443
3578
|
export {
|
|
3444
3579
|
AGENT_CAPABILITY_ERROR_CODES,
|