@tangle-network/agent-provider-tangle 0.14.4 → 1.0.1

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.
@@ -72,7 +72,15 @@ export function defaultTangleSandboxCapabilities(harness) {
72
72
  eventIdentity: true,
73
73
  cancellationIdempotency: true,
74
74
  },
75
- workspace: { read: true, write: true, exec: true, git: false, upload: true, download: true },
75
+ workspace: {
76
+ read: true,
77
+ write: true,
78
+ exec: true,
79
+ git: false,
80
+ upload: true,
81
+ download: true,
82
+ cwdBases: { repository: true, host: false },
83
+ },
76
84
  // The complete Sandbox SDK surface backs this contract. Narrowing below
77
85
  // removes every flag when any recovery or cleanup method is absent.
78
86
  branching: {
@@ -1,9 +1,9 @@
1
1
  import type { BackendType, CreateSandboxOptions } from "@tangle-network/sandbox";
2
- import type { CreateAgentEnvironmentInput } from "@tangle-network/agent-interface/environment-provider";
3
- export declare function sandboxOptionsFromCreateInput(input: CreateAgentEnvironmentInput, defaultBackend: BackendType): CreateSandboxOptions;
2
+ import type { CreateAgentEnvironmentInput, WorkspaceRequest } from "@tangle-network/agent-interface/environment-provider";
3
+ export declare function sandboxOptionsFromCreateInput(input: CreateAgentEnvironmentInput, defaultBackend: BackendType, parsedWorkspace?: WorkspaceRequest): CreateSandboxOptions;
4
4
  /** Reject value-bearing secret maps before any custom mapper can drop them. */
5
- export declare function assertNoInlineSecretValues(input: CreateAgentEnvironmentInput): void;
5
+ export declare function assertNoInlineSecretValues(input: CreateAgentEnvironmentInput, parsedWorkspace?: WorkspaceRequest): void;
6
6
  /** A custom mapper must not smuggle a value map into the Sandbox request. */
7
7
  export declare function assertMappedSecretNames(options: CreateSandboxOptions): void;
8
- export declare function assertCreateInputShape(input: CreateAgentEnvironmentInput): void;
8
+ export declare function assertCreateInputShape(input: CreateAgentEnvironmentInput, parsedWorkspace?: WorkspaceRequest): WorkspaceRequest | undefined;
9
9
  export declare function assertMappedCreateOptions(options: CreateSandboxOptions): void;
@@ -1,8 +1,9 @@
1
+ import { WorkspaceRequestSchema, workspaceCwdPathForBase, } from "@tangle-network/agent-interface/environment-provider";
1
2
  import { assertBoundedJson, boundedIdentifier, boundedString, MAX_ARRAY_LENGTH, MAX_MAP_ENTRIES, } from "./tangle-contract-safety.js";
2
3
  import { sandboxResourcesFromResourceRequest } from "./tangle-resources.js";
3
- export function sandboxOptionsFromCreateInput(input, defaultBackend) {
4
- assertCreateInputShape(input);
5
- assertNoInlineSecretValues(input);
4
+ export function sandboxOptionsFromCreateInput(input, defaultBackend, parsedWorkspace) {
5
+ const workspace = assertCreateInputShape(input, parsedWorkspace) ?? {};
6
+ assertNoInlineSecretValues(input, workspace);
6
7
  if (input.providerOptions && Object.keys(input.providerOptions).length > 0) {
7
8
  throw new Error("Tangle create providerOptions are not supported");
8
9
  }
@@ -19,23 +20,10 @@ export function sandboxOptionsFromCreateInput(input, defaultBackend) {
19
20
  boundedIdentifier(input.backend, "Tangle backend");
20
21
  if (input.env !== undefined)
21
22
  assertStringRecord(input.env, "Tangle");
22
- const workspace = input.workspace ?? {};
23
23
  if (workspace.providerOptions && Object.keys(workspace.providerOptions).length > 0) {
24
24
  throw new Error("Tangle workspace providerOptions are not supported");
25
25
  }
26
- if (workspace.providerOptions)
27
- assertBoundedRecord(workspace.providerOptions, "Tangle workspace providerOptions");
28
- if (workspace.environment !== undefined) {
29
- boundedIdentifier(workspace.environment, "Tangle workspace environment");
30
- }
31
- if (workspace.image !== undefined)
32
- boundedString(workspace.image, "Tangle workspace image");
33
- if (workspace.cwd !== undefined)
34
- boundedString(workspace.cwd, "Tangle workspace cwd");
35
- if (workspace.repoUrl !== undefined)
36
- boundedString(workspace.repoUrl, "Tangle repository URL");
37
- if (workspace.gitRef !== undefined)
38
- boundedIdentifier(workspace.gitRef, "Tangle git ref");
26
+ const workspaceCwd = workspaceCwdPathForBase(workspace.cwd, "repository", "Tangle");
39
27
  if (input.resources?.providerOptions && Object.keys(input.resources.providerOptions).length > 0) {
40
28
  throw new Error("Tangle resource providerOptions are not supported");
41
29
  }
@@ -44,24 +32,16 @@ export function sandboxOptionsFromCreateInput(input, defaultBackend) {
44
32
  if (input.idempotencyKey !== undefined) {
45
33
  boundedIdentifier(input.idempotencyKey, "Tangle idempotency key");
46
34
  }
47
- if (input.workspace?.cwd === "")
48
- throw new Error("Tangle workspace cwd cannot be empty");
49
- if (workspace.image === "")
50
- throw new Error("Tangle workspace image cannot be empty");
51
- if (workspace.repoUrl === "")
52
- throw new Error("Tangle repository URL cannot be empty");
53
35
  const resources = sandboxResourcesFromResourceRequest(input.resources);
54
- if (workspace.environment !== undefined && workspace.image !== undefined) {
55
- throw new Error("Tangle workspace cannot specify both environment and image");
56
- }
57
36
  // Sandbox injects secrets by name from its own store. Accepting a name/value
58
37
  // record and dropping it would create an environment with no credentials and
59
38
  // no error, surfacing later as an unexplained tool failure.
60
39
  const environment = workspace.image ?? workspace.environment;
61
40
  const base = {};
62
- return {
41
+ const mapped = {
63
42
  ...base,
64
43
  ...(environment !== undefined ? { environment } : {}),
44
+ ...(workspaceCwd === undefined ? {} : { cwd: workspaceCwd }),
65
45
  ...(workspace.repoUrl
66
46
  ? {
67
47
  git: {
@@ -82,9 +62,10 @@ export function sandboxOptionsFromCreateInput(input, defaultBackend) {
82
62
  profile: inlineAgentProfile(input.profile),
83
63
  },
84
64
  };
65
+ return mapped;
85
66
  }
86
67
  /** Reject value-bearing secret maps before any custom mapper can drop them. */
87
- export function assertNoInlineSecretValues(input) {
68
+ export function assertNoInlineSecretValues(input, parsedWorkspace) {
88
69
  if (input.providerOptions !== undefined) {
89
70
  if (!input.providerOptions || typeof input.providerOptions !== "object" || Array.isArray(input.providerOptions)) {
90
71
  throw new Error("Tangle create providerOptions must be a JSON object");
@@ -95,11 +76,8 @@ export function assertNoInlineSecretValues(input) {
95
76
  }
96
77
  }
97
78
  if (input.workspace?.providerOptions !== undefined) {
98
- if (!input.workspace.providerOptions || typeof input.workspace.providerOptions !== "object" || Array.isArray(input.workspace.providerOptions)) {
99
- throw new Error("Tangle workspace providerOptions must be a JSON object");
100
- }
101
- assertBoundedJson(input.workspace.providerOptions);
102
- if (Object.keys(input.workspace.providerOptions).length > 0) {
79
+ const workspace = parsedWorkspace ?? WorkspaceRequestSchema.parse(input.workspace);
80
+ if (workspace.providerOptions && Object.keys(workspace.providerOptions).length > 0) {
103
81
  throw new Error("Tangle workspace providerOptions are not supported");
104
82
  }
105
83
  }
@@ -136,7 +114,7 @@ export function assertMappedSecretNames(options) {
136
114
  boundedIdentifier(secret, "Tangle mapped secret name");
137
115
  }
138
116
  }
139
- export function assertCreateInputShape(input) {
117
+ export function assertCreateInputShape(input, parsedWorkspace) {
140
118
  if (!input || typeof input !== "object" || Array.isArray(input)) {
141
119
  throw new Error("Tangle create input must be an object");
142
120
  }
@@ -166,16 +144,6 @@ export function assertCreateInputShape(input) {
166
144
  }
167
145
  assertBoundedJson(input.profile);
168
146
  }
169
- if (input.workspace !== undefined) {
170
- if (!input.workspace || typeof input.workspace !== "object" || Array.isArray(input.workspace)) {
171
- throw new Error("Tangle workspace must be an object");
172
- }
173
- const workspaceKeys = new Set(Object.keys(input.workspace));
174
- for (const key of ["environment", "image", "repoUrl", "gitRef", "cwd", "providerOptions"])
175
- workspaceKeys.delete(key);
176
- if (workspaceKeys.size > 0)
177
- throw new Error("Tangle workspace contains unsupported fields");
178
- }
179
147
  if (input.resources !== undefined) {
180
148
  if (!input.resources || typeof input.resources !== "object" || Array.isArray(input.resources)) {
181
149
  throw new Error("Tangle resources must be an object");
@@ -186,6 +154,11 @@ export function assertCreateInputShape(input) {
186
154
  if (resourceKeys.size > 0)
187
155
  throw new Error("Tangle resources contain unsupported fields");
188
156
  }
157
+ if (input.workspace === undefined)
158
+ return undefined;
159
+ const workspace = parsedWorkspace ?? WorkspaceRequestSchema.parse(input.workspace);
160
+ workspaceCwdPathForBase(workspace.cwd, "repository", "Tangle");
161
+ return workspace;
189
162
  }
190
163
  export function assertMappedCreateOptions(options) {
191
164
  if (!options || typeof options !== "object" || Array.isArray(options)) {
@@ -35,7 +35,6 @@ export function sandboxSessionAsAgentSession(session, controlRef, provider, envi
35
35
  ? tangleInteractionResponder({
36
36
  session,
37
37
  sessionId: session.id,
38
- provider,
39
38
  environmentId,
40
39
  })
41
40
  : undefined;
@@ -199,7 +199,6 @@ export async function sandboxInstanceAsEnvironment(box, providerName, client, de
199
199
  return tangleInteractionResponder({
200
200
  session,
201
201
  sessionId,
202
- provider: providerName,
203
202
  environmentId,
204
203
  })(command, options);
205
204
  },
@@ -4,7 +4,6 @@ export interface TangleInteractionResponderOptions {
4
4
  session: SandboxSessionLike;
5
5
  /** The session the bound ask must belong to. */
6
6
  sessionId: string;
7
- provider: string;
8
7
  environmentId: string;
9
8
  }
10
9
  /**
@@ -20,20 +20,22 @@ function acknowledge(command, status, extras = {}) {
20
20
  });
21
21
  }
22
22
  /**
23
- * The coordinates this environment can answer for. A command naming another
24
- * provider, environment, or session is refused here rather than sent, because
25
- * the Sandbox SDK rejects such a command by throwing, and a caller holding a
26
- * durable operation needs the refusal as an acknowledgement it can record.
23
+ * The coordinates this environment can answer for before the command reaches
24
+ * the deployment. Tangle hosts a backend adapter inside the sandbox, so the
25
+ * interaction binding's provider names that inner adapter (for example,
26
+ * `opencode`) rather than this outer provider. The deployment compares that
27
+ * provider with its durable interaction record. Environment and session are
28
+ * still checked here because the Sandbox SDK rejects those coordinates before
29
+ * it can return an acknowledgement.
27
30
  */
28
31
  function foreignBinding(command, options) {
29
32
  const binding = command.binding;
30
- if (binding.provider === options.provider &&
31
- binding.environmentId === options.environmentId &&
33
+ if (binding.environmentId === options.environmentId &&
32
34
  binding.sessionId === options.sessionId) {
33
35
  return undefined;
34
36
  }
35
37
  return acknowledge(command, "binding_mismatch", {
36
- message: `response command binding names another provider, environment, or session than ${options.provider}/${options.environmentId}/${options.sessionId}`,
38
+ message: `response command binding names another environment or session than ${options.environmentId}/${options.sessionId}`,
37
39
  });
38
40
  }
39
41
  /**
@@ -8,7 +8,7 @@ export declare function promptOptionsFromTurnInput(input: AgentTurnInput, target
8
8
  environmentId: string;
9
9
  sessionId?: string;
10
10
  }): PromptOptions;
11
- type SandboxRunStatus = "success" | "failed" | "blocked_on_approval" | "awaiting_question" | "awaiting_plan_decision";
11
+ type SandboxRunStatus = "success" | "failed" | "blocked_on_approval" | "awaiting_question" | "awaiting_interaction" | "awaiting_plan_decision";
12
12
  type ValidatedSandboxPromptResult = Record<string, unknown> & {
13
13
  success: boolean;
14
14
  status: SandboxRunStatus;
@@ -97,6 +97,7 @@ export function validatedSandboxPromptResult(result) {
97
97
  "failed",
98
98
  "blocked_on_approval",
99
99
  "awaiting_question",
100
+ "awaiting_interaction",
100
101
  "awaiting_plan_decision",
101
102
  ]);
102
103
  if (typeof record.status !== "string" ||
@@ -134,6 +135,7 @@ export function validatedSandboxPromptResult(result) {
134
135
  const AWAITING_STATUSES = new Set([
135
136
  "blocked_on_approval",
136
137
  "awaiting_question",
138
+ "awaiting_interaction",
137
139
  "awaiting_plan_decision",
138
140
  ]);
139
141
  export function agentTurnResultFromPromptRecord(record, options = {}) {
@@ -43,9 +43,9 @@ export function createTangleProvider(options) {
43
43
  const resolveCapabilities = async () => narrowedProviderCapabilities(await resolveDeclaredCapabilities());
44
44
  const createRecords = new Map();
45
45
  const createEnvironment = async (input) => {
46
- assertCreateInputShape(input);
46
+ const parsedWorkspace = assertCreateInputShape(input);
47
47
  input.signal?.throwIfAborted();
48
- assertNoInlineSecretValues(input);
48
+ assertNoInlineSecretValues(input, parsedWorkspace);
49
49
  if (input.providerOptions && Object.keys(input.providerOptions).length > 0) {
50
50
  throw new Error("Tangle create providerOptions are not supported");
51
51
  }
@@ -55,7 +55,7 @@ export function createTangleProvider(options) {
55
55
  const declaredCapabilities = await resolveDeclaredCapabilities();
56
56
  narrowedProviderCapabilities(declaredCapabilities);
57
57
  const createOptions = options.mapCreateInput?.(input) ??
58
- sandboxOptionsFromCreateInput(input, options.defaultBackend ?? "opencode");
58
+ sandboxOptionsFromCreateInput(input, options.defaultBackend ?? "opencode", parsedWorkspace);
59
59
  assertMappedCreateOptions(createOptions);
60
60
  if (input.idempotencyKey !== undefined &&
61
61
  createOptions.idempotencyKey !== input.idempotencyKey) {
@@ -202,6 +202,8 @@ export interface SandboxWorkspaceOperationLookupLike {
202
202
  outcome: "found" | "not_found" | "conflict" | "unknown";
203
203
  kind: "checkpoint" | "fork";
204
204
  state?: "pending" | "succeeded" | "failed";
205
+ /** The durable operation result, when the service records one. */
206
+ result?: Record<string, unknown>;
205
207
  }
206
208
  /** Fan-out acknowledgement returned by SandboxInstance.fork(). */
207
209
  export interface SandboxForkAcknowledgementLike {
@@ -116,7 +116,7 @@ export function createTangleWorkspaceBranching(options) {
116
116
  message: "Sandbox fork child identity is incomplete",
117
117
  };
118
118
  }
119
- const environment = await environmentFromChild(recovered.marker.request, child, provider, child.createdAt, options.confidentialAttestationVerifier, signal);
119
+ const environment = await environmentFromChild(recovered.marker.request, child, provider, recovered.createdAt ?? child.createdAt, options.confidentialAttestationVerifier, signal);
120
120
  if (!environment) {
121
121
  return {
122
122
  state: "undecided",
@@ -699,6 +699,24 @@ function validForkResult(result) {
699
699
  result.materializedCount === result.children.length &&
700
700
  typeof result.complete === "boolean");
701
701
  }
702
+ function validOperationRecord(value) {
703
+ return value !== null && typeof value === "object" && !Array.isArray(value);
704
+ }
705
+ function validOperationDate(value) {
706
+ return ((typeof value === "string" || value instanceof Date) && validDate(value));
707
+ }
708
+ function validSnapshotOperationResult(value) {
709
+ return (validOperationRecord(value) &&
710
+ safeIdentifier(value.snapshotId) !== undefined &&
711
+ validOperationDate(value.createdAt));
712
+ }
713
+ function validForkOperationChildResult(value) {
714
+ return (validOperationRecord(value) &&
715
+ safeIdentifier(value.sandboxId ?? value.id) !== undefined &&
716
+ (value.createdAt === undefined ||
717
+ value.createdAt === null ||
718
+ validOperationDate(value.createdAt)));
719
+ }
702
720
  function checkpointMarkerTags(request) {
703
721
  const marker = {
704
722
  version: 1,
@@ -755,22 +773,30 @@ function forkMarkerMetadata(request) {
755
773
  * A marker only names a candidate resource. Nothing is returned to a caller
756
774
  * until the ledger reports the operation succeeded.
757
775
  */
758
- async function checkpointOperationSucceeded(box, marker, signal) {
776
+ async function checkpointOperationLookup(box, marker, signal) {
759
777
  const lookup = await awaitWithSignal(box.getSnapshotOperation?.(marker.idempotencyKey, {
760
778
  tags: marker.legacy
761
779
  ? legacyCheckpointMarkerTags(marker.request)
762
780
  : checkpointMarkerTags(marker.request),
763
781
  }), signal);
782
+ return lookup;
783
+ }
784
+ async function checkpointOperationSucceeded(box, marker, signal) {
785
+ const lookup = await checkpointOperationLookup(box, marker, signal);
764
786
  return (lookup?.outcome === "found" &&
765
787
  lookup.kind === "checkpoint" &&
766
788
  lookup.state === "succeeded");
767
789
  }
768
790
  /** The fork equivalent of {@link checkpointOperationSucceeded}. */
769
- async function forkOperationSucceeded(box, marker, signal) {
791
+ async function forkOperationLookup(box, marker, signal) {
770
792
  const lookup = await awaitWithSignal(box.getForkOperation?.(marker.idempotencyKey, {
771
793
  count: 1,
772
794
  metadata: forkMarkerMetadata(marker.request),
773
795
  }), signal);
796
+ return lookup;
797
+ }
798
+ async function forkOperationSucceeded(box, marker, signal) {
799
+ const lookup = await forkOperationLookup(box, marker, signal);
774
800
  return (lookup?.outcome === "found" &&
775
801
  lookup.kind === "fork" &&
776
802
  lookup.state === "succeeded");
@@ -818,8 +844,14 @@ async function findCheckpointByKey(box, key, signal) {
818
844
  if (!marker)
819
845
  continue;
820
846
  try {
821
- if (await checkpointOperationSucceeded(box, marker, signal)) {
822
- return { snapshot, marker };
847
+ const lookup = await checkpointOperationLookup(box, marker, signal);
848
+ if (lookup?.outcome === "found" &&
849
+ lookup.kind === "checkpoint" &&
850
+ lookup.state === "succeeded") {
851
+ const authoritative = snapshotFromOperationResult(snapshot, lookup);
852
+ if (authoritative === undefined)
853
+ return undefined;
854
+ return { snapshot: authoritative, marker };
823
855
  }
824
856
  unresolved = true;
825
857
  }
@@ -830,6 +862,23 @@ async function findCheckpointByKey(box, key, signal) {
830
862
  }
831
863
  return unresolved ? undefined : null;
832
864
  }
865
+ /**
866
+ * Prefer the durable operation result over inventory metadata.
867
+ *
868
+ * Snapshot inventory and the operation ledger can expose different creation
869
+ * timestamps. The ledger result is the acknowledgement returned by the
870
+ * idempotent operation, so recovery must rebuild the exact checkpoint ref
871
+ * from it when the service provides that result.
872
+ */
873
+ function snapshotFromOperationResult(snapshot, lookup) {
874
+ if (lookup.result === undefined)
875
+ return snapshot;
876
+ if (!validSnapshotOperationResult(lookup.result) ||
877
+ lookup.result.snapshotId !== snapshot.snapshotId) {
878
+ return undefined;
879
+ }
880
+ return { ...snapshot, createdAt: lookup.result.createdAt };
881
+ }
833
882
  /**
834
883
  * Confirm that one snapshot id is a settled checkpoint this provider created.
835
884
  *
@@ -877,8 +926,15 @@ async function findForkByKey(client, box, provider, key, signal) {
877
926
  let unresolved = false;
878
927
  for (const candidate of candidates) {
879
928
  try {
880
- if (await forkOperationSucceeded(box, candidate.marker, signal))
881
- return candidate;
929
+ const lookup = await forkOperationLookup(box, candidate.marker, signal);
930
+ if (lookup?.outcome === "found" &&
931
+ lookup.kind === "fork" &&
932
+ lookup.state === "succeeded") {
933
+ const authoritative = childFromOperationResult(candidate.child, lookup);
934
+ if (authoritative === undefined)
935
+ return undefined;
936
+ return { ...authoritative, marker: candidate.marker };
937
+ }
882
938
  unresolved = true;
883
939
  }
884
940
  catch {
@@ -888,6 +944,34 @@ async function findForkByKey(client, box, provider, key, signal) {
888
944
  }
889
945
  return unresolved ? undefined : null;
890
946
  }
947
+ /**
948
+ * Prefer the durable fork result over account-inventory metadata.
949
+ *
950
+ * Fork inventory can report a child timestamp from a later registry read. The
951
+ * operation ledger stores the original child acknowledgement, which is the
952
+ * stable value required to replay one exact fork reference after a restart.
953
+ * Some Sandbox responses omit that timestamp, so the validated inventory
954
+ * record supplies it only when the operation result does not.
955
+ */
956
+ function childFromOperationResult(child, lookup) {
957
+ if (lookup.result === undefined) {
958
+ return { child, createdAt: child.createdAt };
959
+ }
960
+ const result = lookup.result;
961
+ if (!validOperationRecord(result))
962
+ return undefined;
963
+ const children = result.children;
964
+ if (!Array.isArray(children))
965
+ return undefined;
966
+ const operationChild = children.find((candidate) => validForkOperationChildResult(candidate) &&
967
+ (candidate.sandboxId ?? candidate.id) === child.id);
968
+ if (!operationChild)
969
+ return undefined;
970
+ const createdAt = operationChild.createdAt ?? child.createdAt;
971
+ if (!validOperationDate(createdAt))
972
+ return undefined;
973
+ return { child, createdAt };
974
+ }
891
975
  async function findForkChildById(client, box, provider, id, signal) {
892
976
  try {
893
977
  if (typeof client.get !== "function")
@@ -1229,7 +1313,7 @@ async function forkConflictFromRemote(client, box, provider, request, verifier,
1229
1313
  const child = await completeForkChild(client, recovered.child, signal);
1230
1314
  if (!child)
1231
1315
  return undefined;
1232
- const environment = await environmentFromChild(recovered.marker.request, child, provider, child.createdAt, verifier, signal);
1316
+ const environment = await environmentFromChild(recovered.marker.request, child, provider, recovered.createdAt ?? child.createdAt, verifier, signal);
1233
1317
  return environment === undefined
1234
1318
  ? undefined
1235
1319
  : forkSuccess(request, environment, "replayed");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-provider-tangle",
3
- "version": "0.14.4",
3
+ "version": "1.0.1",
4
4
  "description": "AgentEnvironmentProvider adapter for Tangle sandboxes",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -89,10 +89,10 @@
89
89
  "LICENSE"
90
90
  ],
91
91
  "dependencies": {
92
- "@tangle-network/agent-interface": "^1.8.0"
92
+ "@tangle-network/agent-interface": "^2.0.0"
93
93
  },
94
94
  "peerDependencies": {
95
- "@tangle-network/sandbox": ">=0.34.4 <1.0.0"
95
+ "@tangle-network/sandbox": ">=0.34.6 <1.0.0"
96
96
  },
97
97
  "peerDependenciesMeta": {
98
98
  "@tangle-network/sandbox": {
@@ -101,12 +101,12 @@
101
101
  },
102
102
  "devDependencies": {
103
103
  "@tangle-network/agent-eval": "0.170.0",
104
- "@tangle-network/agent-runtime": "0.178.0",
105
- "@tangle-network/sandbox": "0.34.4",
104
+ "@tangle-network/agent-runtime": "0.184.0",
105
+ "@tangle-network/sandbox": "0.34.6",
106
106
  "@types/node": "26.4.0",
107
107
  "typescript": "7.0.2",
108
108
  "vitest": "4.1.11",
109
- "@tangle-network/agent-provider-testkit": "0.8.4"
109
+ "@tangle-network/agent-provider-testkit": "0.8.6"
110
110
  },
111
111
  "scripts": {
112
112
  "build": "tsc -p tsconfig.json",