@tangle-network/agent-provider-tangle 1.1.6 → 1.1.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.
package/README.md CHANGED
@@ -102,6 +102,11 @@ Reconstruct an exact session with `environment.session(reference.id, { controlRe
102
102
  Result, replay, and cancel operations select that exact execution instead of whichever execution most recently changed the shared session.
103
103
  Session status with an exact control reference reports a state only when the payload names that execution; a payload bound to a different or unnamed execution reports `unknown`.
104
104
 
105
+ Stream frames can contain complete tool output and cumulative assistant text.
106
+ The adapter bounds each complete serialized frame to 1 MiB of UTF-8, including keys and JSON escaping.
107
+ It retains accepted content without truncation in the event data and original provider event.
108
+ Identifiers, usage metadata, and structural JSON limits retain their existing bounds.
109
+
105
110
  ## Two capability documents
106
111
 
107
112
  Capabilities are derived in two stages, and the two stages answer different questions.
@@ -1,5 +1,4 @@
1
- import { CanonicalStreamEventSchema, } from "@tangle-network/agent-interface";
2
- import { assertBoundedJson } from "./tangle-contract-safety.js";
1
+ import { CanonicalStreamEventSchema, boundedEventContentRecordSchema, } from "@tangle-network/agent-interface";
3
2
  import { optionalNonEmptyString } from "./tangle-environment-values.js";
4
3
  import { tokenUsageFromData } from "./tangle-result-values.js";
5
4
  /** The sidecar sends this envelope when an SSE connection is ready. */
@@ -135,8 +134,7 @@ export function environmentEventFromSandboxEvent(event, expected = {}) {
135
134
  throw new Error("Tangle Sandbox event contained an invalid event id");
136
135
  }
137
136
  const data = record.data;
138
- assertBoundedRecord(data);
139
- assertBoundedJson(record);
137
+ assertBoundedRecord(record);
140
138
  if (Object.prototype.hasOwnProperty.call(data, "contextTransferReceipt")) {
141
139
  throw new Error("Tangle Sandbox emitted an unsolicited context transfer receipt");
142
140
  }
@@ -328,60 +326,8 @@ function detailFromSandboxData(data) {
328
326
  return detail === undefined ? {} : { detail };
329
327
  }
330
328
  function assertBoundedRecord(value) {
331
- if (Object.keys(value).length > 256) {
332
- throw new Error("Tangle Sandbox event data has too many fields");
333
- }
334
- const pending = [
335
- { value, depth: 0 },
336
- ];
337
- const ancestors = new Set();
338
- let nodes = 0;
339
- while (pending.length > 0) {
340
- const item = pending.pop();
341
- if (!item)
342
- continue;
343
- nodes += 1;
344
- if (nodes > 8_192)
345
- throw new Error("Tangle Sandbox event data has too many JSON nodes");
346
- const current = item.value;
347
- if (item.leave) {
348
- ancestors.delete(current);
349
- continue;
350
- }
351
- if (current === null || typeof current === "boolean")
352
- continue;
353
- if (typeof current === "string" || typeof current === "number") {
354
- if (typeof current === "string" && current.length > 16_384) {
355
- throw new Error("Tangle Sandbox event data exceeded its string bound");
356
- }
357
- if (typeof current === "number" && !Number.isFinite(current)) {
358
- throw new Error("Tangle Sandbox event data contained a non-finite number");
359
- }
360
- continue;
361
- }
362
- if (typeof current !== "object" || item.depth >= 16 || ancestors.has(current)) {
363
- throw new Error("Tangle Sandbox event data exceeded its JSON bound");
364
- }
365
- ancestors.add(current);
366
- pending.push({ value: current, depth: item.depth, leave: true });
367
- if (Array.isArray(current)) {
368
- if (current.length > 1_024) {
369
- throw new Error("Tangle Sandbox event data has too many array entries");
370
- }
371
- for (const entry of current)
372
- pending.push({ value: entry, depth: item.depth + 1 });
373
- continue;
374
- }
375
- if (Object.getPrototypeOf(current) !== Object.prototype && Object.getPrototypeOf(current) !== null) {
376
- throw new Error("Tangle Sandbox event data must be plain JSON");
377
- }
378
- const keys = Object.keys(current);
379
- if (keys.length > 256)
380
- throw new Error("Tangle Sandbox event map is too large");
381
- for (const key of keys) {
382
- if (key.length > 512)
383
- throw new Error("Tangle Sandbox event key is too long");
384
- pending.push({ value: current[key], depth: item.depth + 1 });
385
- }
329
+ const parsed = boundedEventContentRecordSchema.safeParse(value);
330
+ if (!parsed.success) {
331
+ throw new Error("Tangle Sandbox event exceeded its JSON content bound", { cause: parsed.error });
386
332
  }
387
333
  }
@@ -1,4 +1,4 @@
1
- import { agentProfileSchema, AgentExactRunControlRefSchema, AgentTurnInputSchema, ContextTransferReceiptSchema, contextTransferResultMatchesRequest, } from "@tangle-network/agent-interface";
1
+ import { agentProfileSchema, boundedEventContentRecordSchema, AgentExactRunControlRefSchema, AgentTurnInputSchema, ContextTransferReceiptSchema, contextTransferResultMatchesRequest, } from "@tangle-network/agent-interface";
2
2
  import { tokenUsageFromData } from "./tangle-result-values.js";
3
3
  import { assertBoundedJson, boundedString } from "./tangle-contract-safety.js";
4
4
  export function promptFromTurnInput(input) {
@@ -372,7 +372,12 @@ export function validatedSandboxPromptResult(result) {
372
372
  // `undefined`. They were absent on the JSON wire and must stay absent in the
373
373
  // provider-neutral result before the strict JSON check runs.
374
374
  const record = Object.fromEntries(Object.entries(source).filter(([field, value]) => value !== undefined || !SANDBOX_OPTIONAL_RESULT_FIELDS.has(field)));
375
- assertBoundedJson(record);
375
+ const content = boundedEventContentRecordSchema.safeParse(record);
376
+ if (!content.success) {
377
+ throw new Error("Tangle prompt result exceeded its JSON bound", { cause: content.error });
378
+ }
379
+ // Response text is content; all other fields retain their metadata limits.
380
+ assertBoundedJson(Object.fromEntries(Object.entries(record).filter(([field]) => field !== "response" && field !== "text" && field !== "finalText")));
376
381
  if (typeof record.success !== "boolean") {
377
382
  throw new Error("Tangle prompt result omitted its success status");
378
383
  }
@@ -19,7 +19,11 @@ export function execResultFromSandboxExecResult(result) {
19
19
  };
20
20
  }
21
21
  export function tokenUsageFromData(data) {
22
- assertBoundedJson(data);
22
+ // The enclosing event or result has its own content bound.
23
+ // Apply metadata limits only to fields used to compute usage.
24
+ assertBoundedJson(Object.fromEntries(["usage", "tokenUsage", "costUsd", "totalCostUsd"]
25
+ .filter((field) => Object.hasOwn(data, field))
26
+ .map((field) => [field, data[field]])));
23
27
  if (data.usage !== undefined &&
24
28
  (!data.usage || typeof data.usage !== "object" || Array.isArray(data.usage))) {
25
29
  throw new Error("Tangle usage must be an object");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-provider-tangle",
3
- "version": "1.1.6",
3
+ "version": "1.1.7",
4
4
  "description": "AgentEnvironmentProvider adapter for Tangle sandboxes",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -91,7 +91,7 @@
91
91
  "LICENSE"
92
92
  ],
93
93
  "dependencies": {
94
- "@tangle-network/agent-interface": "^2.4.0"
94
+ "@tangle-network/agent-interface": "^2.6.1"
95
95
  },
96
96
  "peerDependencies": {
97
97
  "@tangle-network/sandbox": ">=0.34.6 <1.0.0"