@tangle-network/agent-interface 0.47.0 → 0.48.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.
@@ -35,6 +35,9 @@ export declare const agentCandidateProfileSchema: z.ZodObject<{
35
35
  xhigh: "xhigh";
36
36
  ultracode: "ultracode";
37
37
  }>>;
38
+ maxVisibleOutputTokens: z.ZodOptional<z.ZodNumber>;
39
+ maxReasoningTokens: z.ZodOptional<z.ZodNumber>;
40
+ maxTotalOutputTokens: z.ZodOptional<z.ZodNumber>;
38
41
  default: z.ZodOptional<z.ZodString>;
39
42
  small: z.ZodOptional<z.ZodString>;
40
43
  provider: z.ZodOptional<z.ZodString>;
@@ -2,7 +2,7 @@ import { z } from "zod";
2
2
  import { agentCandidateResourcesSchema } from "./agent-candidate-artifact-schema.js";
3
3
  import { agentCandidateConfigValueSchema, environmentConfigSchema, isCanonicalJsonValue, isSafeExecutable, isSafeRelativePath, isWellFormedUnicode, } from "./agent-candidate-schema-common.js";
4
4
  import { harnessTypeSchema } from "./harness.js";
5
- import { agentProfileConfidentialSchema, agentProfileModeSchema, agentProfileModelHintsSchema, agentProfilePermissionSchema, agentProfilePromptSchema, agentSubagentProfileSchema, } from "./profile-schema.js";
5
+ import { agentProfileConfidentialSchema, agentProfileModeSchema, agentProfileModelHintsBaseSchema, agentProfilePermissionSchema, agentProfilePromptSchema, agentSubagentProfileSchema, enforceModelTokenBounds, } from "./profile-schema.js";
6
6
  const executableSchema = z
7
7
  .string()
8
8
  .refine(isSafeExecutable, "executable must be a canonical non-shell command");
@@ -39,14 +39,15 @@ export const agentCandidateHookCommandSchema = z
39
39
  env: environmentConfigSchema.optional(),
40
40
  })
41
41
  .strict();
42
- const candidateModelHintsSchema = agentProfileModelHintsSchema
42
+ const candidateModelHintsSchema = agentProfileModelHintsBaseSchema
43
43
  .omit({ metadata: true })
44
44
  .extend({
45
45
  default: z.string().min(1).optional(),
46
46
  small: z.string().min(1).optional(),
47
47
  provider: z.string().min(1).optional(),
48
48
  })
49
- .strict();
49
+ .strict()
50
+ .superRefine(enforceModelTokenBounds);
50
51
  const candidateSubagentSchema = agentSubagentProfileSchema
51
52
  .omit({ metadata: true })
52
53
  .extend({
@@ -26,6 +26,9 @@ export declare const agentCandidateExperimentSchema: z.ZodObject<{
26
26
  xhigh: "xhigh";
27
27
  ultracode: "ultracode";
28
28
  }>>;
29
+ maxVisibleOutputTokens: z.ZodOptional<z.ZodNumber>;
30
+ maxReasoningTokens: z.ZodOptional<z.ZodNumber>;
31
+ maxTotalOutputTokens: z.ZodOptional<z.ZodNumber>;
29
32
  default: z.ZodOptional<z.ZodString>;
30
33
  small: z.ZodOptional<z.ZodString>;
31
34
  provider: z.ZodOptional<z.ZodString>;
@@ -856,6 +859,9 @@ export declare const agentCandidateExperimentSchema: z.ZodObject<{
856
859
  xhigh: "xhigh";
857
860
  ultracode: "ultracode";
858
861
  }>>;
862
+ maxVisibleOutputTokens: z.ZodOptional<z.ZodNumber>;
863
+ maxReasoningTokens: z.ZodOptional<z.ZodNumber>;
864
+ maxTotalOutputTokens: z.ZodOptional<z.ZodNumber>;
859
865
  default: z.ZodOptional<z.ZodString>;
860
866
  small: z.ZodOptional<z.ZodString>;
861
867
  provider: z.ZodOptional<z.ZodString>;
@@ -3599,6 +3605,9 @@ export declare const agentImprovementMeasuredComparisonSchema: z.ZodObject<{
3599
3605
  xhigh: "xhigh";
3600
3606
  ultracode: "ultracode";
3601
3607
  }>>;
3608
+ maxVisibleOutputTokens: z.ZodOptional<z.ZodNumber>;
3609
+ maxReasoningTokens: z.ZodOptional<z.ZodNumber>;
3610
+ maxTotalOutputTokens: z.ZodOptional<z.ZodNumber>;
3602
3611
  default: z.ZodOptional<z.ZodString>;
3603
3612
  small: z.ZodOptional<z.ZodString>;
3604
3613
  provider: z.ZodOptional<z.ZodString>;
@@ -4429,6 +4438,9 @@ export declare const agentImprovementMeasuredComparisonSchema: z.ZodObject<{
4429
4438
  xhigh: "xhigh";
4430
4439
  ultracode: "ultracode";
4431
4440
  }>>;
4441
+ maxVisibleOutputTokens: z.ZodOptional<z.ZodNumber>;
4442
+ maxReasoningTokens: z.ZodOptional<z.ZodNumber>;
4443
+ maxTotalOutputTokens: z.ZodOptional<z.ZodNumber>;
4432
4444
  default: z.ZodOptional<z.ZodString>;
4433
4445
  small: z.ZodOptional<z.ZodString>;
4434
4446
  provider: z.ZodOptional<z.ZodString>;
@@ -8592,6 +8604,9 @@ export declare const agentImprovementProposalSchema: z.ZodObject<{
8592
8604
  xhigh: "xhigh";
8593
8605
  ultracode: "ultracode";
8594
8606
  }>>;
8607
+ maxVisibleOutputTokens: z.ZodOptional<z.ZodNumber>;
8608
+ maxReasoningTokens: z.ZodOptional<z.ZodNumber>;
8609
+ maxTotalOutputTokens: z.ZodOptional<z.ZodNumber>;
8595
8610
  default: z.ZodOptional<z.ZodString>;
8596
8611
  small: z.ZodOptional<z.ZodString>;
8597
8612
  provider: z.ZodOptional<z.ZodString>;
@@ -9422,6 +9437,9 @@ export declare const agentImprovementProposalSchema: z.ZodObject<{
9422
9437
  xhigh: "xhigh";
9423
9438
  ultracode: "ultracode";
9424
9439
  }>>;
9440
+ maxVisibleOutputTokens: z.ZodOptional<z.ZodNumber>;
9441
+ maxReasoningTokens: z.ZodOptional<z.ZodNumber>;
9442
+ maxTotalOutputTokens: z.ZodOptional<z.ZodNumber>;
9425
9443
  default: z.ZodOptional<z.ZodString>;
9426
9444
  small: z.ZodOptional<z.ZodString>;
9427
9445
  provider: z.ZodOptional<z.ZodString>;
@@ -186,7 +186,16 @@ export function isCanonicalJsonValue(value, ancestors = new Set()) {
186
186
  }
187
187
  const nextAncestors = new Set(ancestors).add(value);
188
188
  if (Array.isArray(value)) {
189
- return value.every((entry) => isCanonicalJsonValue(entry, nextAncestors));
189
+ for (let index = 0; index < value.length; index += 1) {
190
+ // A sparse hole serializes to the same bytes as a dense null but is a
191
+ // distinct in-memory value, so two observable inputs would share one
192
+ // content digest. Reject the sparse form; never change the output bytes.
193
+ if (!(index in value))
194
+ return false;
195
+ if (!isCanonicalJsonValue(value[index], nextAncestors))
196
+ return false;
197
+ }
198
+ return true;
190
199
  }
191
200
  return Object.entries(value).every(([key, entry]) => isWellFormedUnicode(key) && isCanonicalJsonValue(entry, nextAncestors));
192
201
  }
@@ -30,6 +30,9 @@ export declare const agentCandidateBundleSchema: z.ZodObject<{
30
30
  xhigh: "xhigh";
31
31
  ultracode: "ultracode";
32
32
  }>>;
33
+ maxVisibleOutputTokens: z.ZodOptional<z.ZodNumber>;
34
+ maxReasoningTokens: z.ZodOptional<z.ZodNumber>;
35
+ maxTotalOutputTokens: z.ZodOptional<z.ZodNumber>;
33
36
  default: z.ZodOptional<z.ZodString>;
34
37
  small: z.ZodOptional<z.ZodString>;
35
38
  provider: z.ZodOptional<z.ZodString>;
@@ -122,8 +122,26 @@ export interface AgentProfileModelHints {
122
122
  /**
123
123
  * Reasoning/thinking effort hint — a first-class, portable model dimension (not buried in
124
124
  * `extensions`). Backends map it to their native control at materialization.
125
+ *
126
+ * This is a quality dial, not a spend limit. It never bounds token count.
125
127
  */
126
128
  reasoningEffort?: ReasoningEffort;
129
+ /**
130
+ * Maximum visible answer tokens a backend may bill for one completion.
131
+ * A positive integer. Independent of the reasoning and total ceilings.
132
+ */
133
+ maxVisibleOutputTokens?: number;
134
+ /**
135
+ * Maximum hidden reasoning tokens a backend may bill for one completion.
136
+ * A positive integer, separate from visible answer tokens.
137
+ */
138
+ maxReasoningTokens?: number;
139
+ /**
140
+ * Maximum total billed completion tokens for visible answer and hidden
141
+ * reasoning tokens together. A positive integer. When it is set with either
142
+ * single ceiling, that single ceiling must not exceed it.
143
+ */
144
+ maxTotalOutputTokens?: number;
127
145
  /**
128
146
  * Backend-agnostic model metadata/hints.
129
147
  */
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { boundedIdentifierSchema, boundedStringSchema, CONTRACT_MAX_ARRAY_LENGTH, CONTRACT_MAX_MAP_ENTRIES, } from "./contract-limits.js";
3
- import { InteractionFieldNameSchema } from "./interaction-fields.js";
3
+ import { InteractionFieldNameSchema, RESERVED_INTERACTION_FIELD_NAMES, } from "./interaction-fields.js";
4
4
  export const InteractionSecretReferenceSchema = z.strictObject({
5
5
  kind: z.literal("secret_handle"),
6
6
  handleId: boundedIdentifierSchema,
@@ -15,6 +15,25 @@ const InteractionDataValueSchema = z.union([
15
15
  ]);
16
16
  /** Field values keyed by InteractionField.name. */
17
17
  export const InteractionDataSchema = z
18
+ .unknown()
19
+ // A record parser assigns keys onto an ordinary object, so a raw own key
20
+ // `__proto__` invokes the legacy prototype setter and vanishes before any
21
+ // key schema runs. Reject reserved keys on the raw input, so this schema and
22
+ // `validateInteractionResponse` refuse the same value rather than one of them
23
+ // accepting a silently emptied object.
24
+ .superRefine((value, refinement) => {
25
+ if (value === null || typeof value !== "object")
26
+ return;
27
+ for (const key of Object.getOwnPropertyNames(value)) {
28
+ if (RESERVED_INTERACTION_FIELD_NAMES.has(key)) {
29
+ refinement.addIssue({
30
+ code: "custom",
31
+ message: `interaction field name "${key}" is reserved`,
32
+ });
33
+ }
34
+ }
35
+ })
36
+ .pipe(z
18
37
  .record(InteractionFieldNameSchema, InteractionDataValueSchema)
19
38
  .superRefine((data, refinement) => {
20
39
  if (Object.keys(data).length > CONTRACT_MAX_MAP_ENTRIES) {
@@ -23,7 +42,7 @@ export const InteractionDataSchema = z
23
42
  message: "interaction data has too many fields",
24
43
  });
25
44
  }
26
- })
45
+ }))
27
46
  .transform((data) => {
28
47
  const safe = Object.create(null);
29
48
  for (const key of Object.keys(data))
@@ -1,4 +1,11 @@
1
1
  import { z } from "zod";
2
+ /**
3
+ * Field names whose own-key form collides with object internals. A record
4
+ * parser drops a raw `__proto__` key through the legacy prototype setter before
5
+ * a key schema runs, so every schema and validator over interaction data
6
+ * rejects these names against one owner rather than a private copy.
7
+ */
8
+ export declare const RESERVED_INTERACTION_FIELD_NAMES: ReadonlySet<string>;
2
9
  export declare const InteractionFieldNameSchema: z.ZodString;
3
10
  export declare const InteractionFieldSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
4
11
  type: z.ZodLiteral<"text">;
@@ -1,7 +1,17 @@
1
1
  import { z } from "zod";
2
2
  import { boundedIdentifierSchema, boundedStringSchema, CONTRACT_MAX_ARRAY_LENGTH, CONTRACT_MAX_STRING_LENGTH, } from "./contract-limits.js";
3
- const forbiddenFieldNames = new Set(["__proto__", "constructor", "prototype"]);
4
- export const InteractionFieldNameSchema = boundedIdentifierSchema.refine((value) => !forbiddenFieldNames.has(value), "interaction field name is reserved");
3
+ /**
4
+ * Field names whose own-key form collides with object internals. A record
5
+ * parser drops a raw `__proto__` key through the legacy prototype setter before
6
+ * a key schema runs, so every schema and validator over interaction data
7
+ * rejects these names against one owner rather than a private copy.
8
+ */
9
+ export const RESERVED_INTERACTION_FIELD_NAMES = new Set([
10
+ "__proto__",
11
+ "constructor",
12
+ "prototype",
13
+ ]);
14
+ export const InteractionFieldNameSchema = boundedIdentifierSchema.refine((value) => !RESERVED_INTERACTION_FIELD_NAMES.has(value), "interaction field name is reserved");
5
15
  // =============================================================================
6
16
  // Answer specification — describes the shape of a valid answer.
7
17
  // =============================================================================
@@ -1,6 +1,6 @@
1
1
  import { InteractionRequestSchema, InteractionResponseCommandSchema, InteractionResponseSchema, } from "./interaction-envelope.js";
2
+ import { RESERVED_INTERACTION_FIELD_NAMES } from "./interaction-fields.js";
2
3
  import { validateResolutionForRequest } from "./interaction-resolution-validation.js";
3
- const FORBIDDEN_DATA_KEYS = new Set(["__proto__", "constructor", "prototype"]);
4
4
  function validResponse(response) {
5
5
  const result = { ok: true };
6
6
  Object.defineProperty(result, "response", {
@@ -17,7 +17,7 @@ function forbiddenKeyErrors(response) {
17
17
  if (!data || typeof data !== "object")
18
18
  return [];
19
19
  return Object.getOwnPropertyNames(data)
20
- .filter((key) => FORBIDDEN_DATA_KEYS.has(key))
20
+ .filter((key) => RESERVED_INTERACTION_FIELD_NAMES.has(key))
21
21
  .map((key) => `unknown field "${key}"`);
22
22
  }
23
23
  /** Validate one response against the exact outstanding request. */
@@ -120,6 +120,34 @@ export declare const reasoningEffortSchema: z.ZodEnum<{
120
120
  xhigh: "xhigh";
121
121
  ultracode: "ultracode";
122
122
  }>;
123
+ /**
124
+ * Enforce the token ceilings that live together: each single ceiling must not
125
+ * exceed the total. It fails loud on a violation and never clamps a value.
126
+ * `reasoningEffort` is a separate quality dial and is not a bound here.
127
+ */
128
+ export declare function enforceModelTokenBounds(hints: {
129
+ maxVisibleOutputTokens?: number;
130
+ maxReasoningTokens?: number;
131
+ maxTotalOutputTokens?: number;
132
+ }, context: z.RefinementCtx): void;
133
+ export declare const agentProfileModelHintsBaseSchema: z.ZodObject<{
134
+ default: z.ZodOptional<z.ZodString>;
135
+ small: z.ZodOptional<z.ZodString>;
136
+ provider: z.ZodOptional<z.ZodString>;
137
+ reasoningEffort: z.ZodOptional<z.ZodEnum<{
138
+ none: "none";
139
+ minimal: "minimal";
140
+ low: "low";
141
+ medium: "medium";
142
+ high: "high";
143
+ xhigh: "xhigh";
144
+ ultracode: "ultracode";
145
+ }>>;
146
+ maxVisibleOutputTokens: z.ZodOptional<z.ZodNumber>;
147
+ maxReasoningTokens: z.ZodOptional<z.ZodNumber>;
148
+ maxTotalOutputTokens: z.ZodOptional<z.ZodNumber>;
149
+ metadata: z.ZodOptional<z.ZodType<Record<string, unknown>, unknown, z.core.$ZodTypeInternals<Record<string, unknown>, unknown>>>;
150
+ }, z.core.$strict>;
123
151
  export declare const agentProfileModelHintsSchema: z.ZodObject<{
124
152
  default: z.ZodOptional<z.ZodString>;
125
153
  small: z.ZodOptional<z.ZodString>;
@@ -133,6 +161,9 @@ export declare const agentProfileModelHintsSchema: z.ZodObject<{
133
161
  xhigh: "xhigh";
134
162
  ultracode: "ultracode";
135
163
  }>>;
164
+ maxVisibleOutputTokens: z.ZodOptional<z.ZodNumber>;
165
+ maxReasoningTokens: z.ZodOptional<z.ZodNumber>;
166
+ maxTotalOutputTokens: z.ZodOptional<z.ZodNumber>;
136
167
  metadata: z.ZodOptional<z.ZodType<Record<string, unknown>, unknown, z.core.$ZodTypeInternals<Record<string, unknown>, unknown>>>;
137
168
  }, z.core.$strict>;
138
169
  /**
@@ -321,6 +352,9 @@ export declare const agentProfileSchema: z.ZodObject<{
321
352
  xhigh: "xhigh";
322
353
  ultracode: "ultracode";
323
354
  }>>;
355
+ maxVisibleOutputTokens: z.ZodOptional<z.ZodNumber>;
356
+ maxReasoningTokens: z.ZodOptional<z.ZodNumber>;
357
+ maxTotalOutputTokens: z.ZodOptional<z.ZodNumber>;
324
358
  metadata: z.ZodOptional<z.ZodType<Record<string, unknown>, unknown, z.core.$ZodTypeInternals<Record<string, unknown>, unknown>>>;
325
359
  }, z.core.$strict>>;
326
360
  harness: z.ZodOptional<z.ZodEnum<{
@@ -91,13 +91,45 @@ export const agentProfileResourcesSchema = z.strictObject({
91
91
  failOnError: z.boolean().optional(),
92
92
  });
93
93
  export const reasoningEffortSchema = z.enum(REASONING_EFFORTS);
94
- export const agentProfileModelHintsSchema = z.strictObject({
94
+ /**
95
+ * Enforce the token ceilings that live together: each single ceiling must not
96
+ * exceed the total. It fails loud on a violation and never clamps a value.
97
+ * `reasoningEffort` is a separate quality dial and is not a bound here.
98
+ */
99
+ export function enforceModelTokenBounds(hints, context) {
100
+ const { maxVisibleOutputTokens, maxReasoningTokens, maxTotalOutputTokens } = hints;
101
+ if (maxTotalOutputTokens === undefined)
102
+ return;
103
+ if (maxVisibleOutputTokens !== undefined &&
104
+ maxVisibleOutputTokens > maxTotalOutputTokens) {
105
+ context.addIssue({
106
+ code: "custom",
107
+ path: ["maxVisibleOutputTokens"],
108
+ message: "maxVisibleOutputTokens must not exceed maxTotalOutputTokens",
109
+ });
110
+ }
111
+ if (maxReasoningTokens !== undefined &&
112
+ maxReasoningTokens > maxTotalOutputTokens) {
113
+ context.addIssue({
114
+ code: "custom",
115
+ path: ["maxReasoningTokens"],
116
+ message: "maxReasoningTokens must not exceed maxTotalOutputTokens",
117
+ });
118
+ }
119
+ }
120
+ // Base shape without cross-field checks, so candidate derivations may still
121
+ // call `.omit`/`.extend`; zod refuses those on a schema that carries a refinement.
122
+ export const agentProfileModelHintsBaseSchema = z.strictObject({
95
123
  default: z.string().optional(),
96
124
  small: z.string().optional(),
97
125
  provider: z.string().optional(),
98
126
  reasoningEffort: reasoningEffortSchema.optional(),
127
+ maxVisibleOutputTokens: z.number().int().positive().optional(),
128
+ maxReasoningTokens: z.number().int().positive().optional(),
129
+ maxTotalOutputTokens: z.number().int().positive().optional(),
99
130
  metadata: ownPropertyRecordSchema(z.unknown()).optional(),
100
131
  });
132
+ export const agentProfileModelHintsSchema = agentProfileModelHintsBaseSchema.superRefine(enforceModelTokenBounds);
101
133
  /**
102
134
  * Replacement and addition are separate, independently optional fields, and the
103
135
  * pair is admitted on purpose: the effective prompt is `systemPrompt` followed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-interface",
3
- "version": "0.47.0",
3
+ "version": "0.48.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "license": "MIT",
@@ -16,6 +16,41 @@
16
16
  "import": "./dist/environment-provider.js",
17
17
  "types": "./dist/environment-provider.d.ts",
18
18
  "default": "./dist/environment-provider.js"
19
+ },
20
+ "./profile": {
21
+ "import": "./dist/agent-profile.js",
22
+ "types": "./dist/agent-profile.d.ts",
23
+ "default": "./dist/agent-profile.js"
24
+ },
25
+ "./profile-snapshot": {
26
+ "import": "./dist/agent-profile-snapshot.js",
27
+ "types": "./dist/agent-profile-snapshot.d.ts",
28
+ "default": "./dist/agent-profile-snapshot.js"
29
+ },
30
+ "./profile-schema": {
31
+ "import": "./dist/profile-schema.js",
32
+ "types": "./dist/profile-schema.d.ts",
33
+ "default": "./dist/profile-schema.js"
34
+ },
35
+ "./profile-security": {
36
+ "import": "./dist/profile-security.js",
37
+ "types": "./dist/profile-security.d.ts",
38
+ "default": "./dist/profile-security.js"
39
+ },
40
+ "./harness": {
41
+ "import": "./dist/harness.js",
42
+ "types": "./dist/harness.d.ts",
43
+ "default": "./dist/harness.js"
44
+ },
45
+ "./harness-capabilities": {
46
+ "import": "./dist/harness-capabilities.js",
47
+ "types": "./dist/harness-capabilities.d.ts",
48
+ "default": "./dist/harness-capabilities.js"
49
+ },
50
+ "./interaction": {
51
+ "import": "./dist/interaction.js",
52
+ "types": "./dist/interaction.d.ts",
53
+ "default": "./dist/interaction.js"
19
54
  }
20
55
  },
21
56
  "repository": {