@tangle-network/agent-interface 1.4.0 → 1.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.
@@ -0,0 +1,35 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Evidence that one captured artifact is the canonical bytes of its material.
4
+ *
5
+ * The artifact's hash must equal the digest the material canonicalizes to, and
6
+ * the artifact must actually carry bytes. Both are integrity claims: a record
7
+ * whose artifact hash names something other than its material is evidence for
8
+ * a different value, and a zero-length artifact is a claim with nothing behind
9
+ * it. `label` names the evidence in the refusal so a reader knows which record
10
+ * failed.
11
+ */
12
+ export declare function agentCandidateEvidenceSchema<TKind extends string, TMaterial>(kind: TKind, material: z.ZodType<TMaterial>, label: string): z.ZodObject<{
13
+ kind: z.ZodLiteral<TKind>;
14
+ digest: z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>;
15
+ material: z.ZodType<TMaterial, unknown, z.core.$ZodTypeInternals<TMaterial, unknown>>;
16
+ artifact: z.ZodUnion<readonly [z.ZodObject<{
17
+ locator: z.ZodDiscriminatedUnion<[z.ZodObject<{
18
+ kind: z.ZodLiteral<"s3">;
19
+ bucket: z.ZodString;
20
+ key: z.ZodString;
21
+ region: z.ZodOptional<z.ZodString>;
22
+ }, z.core.$strict>, z.ZodObject<{
23
+ kind: z.ZodLiteral<"ipfs">;
24
+ cid: z.ZodString;
25
+ path: z.ZodOptional<z.ZodString>;
26
+ }, z.core.$strict>], "kind">;
27
+ sha256: z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>;
28
+ byteLength: z.ZodNumber;
29
+ }, z.core.$strict>, z.ZodObject<{
30
+ encoding: z.ZodLiteral<"base64">;
31
+ content: z.ZodString;
32
+ sha256: z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>;
33
+ byteLength: z.ZodNumber;
34
+ }, z.core.$strict>]>;
35
+ }, z.core.$strict>;
@@ -0,0 +1,39 @@
1
+ import { z } from "zod";
2
+ import { agentCandidateCapturedArtifactSchema } from "./agent-candidate-artifact-schema.js";
3
+ import { sha256DigestSchema } from "./agent-candidate-schema-common.js";
4
+ /**
5
+ * Evidence that one captured artifact is the canonical bytes of its material.
6
+ *
7
+ * The artifact's hash must equal the digest the material canonicalizes to, and
8
+ * the artifact must actually carry bytes. Both are integrity claims: a record
9
+ * whose artifact hash names something other than its material is evidence for
10
+ * a different value, and a zero-length artifact is a claim with nothing behind
11
+ * it. `label` names the evidence in the refusal so a reader knows which record
12
+ * failed.
13
+ */
14
+ export function agentCandidateEvidenceSchema(kind, material, label) {
15
+ return z
16
+ .object({
17
+ kind: z.literal(kind),
18
+ digest: sha256DigestSchema,
19
+ material,
20
+ artifact: agentCandidateCapturedArtifactSchema,
21
+ })
22
+ .strict()
23
+ .superRefine((evidence, ctx) => {
24
+ if (evidence.artifact.sha256 !== evidence.digest) {
25
+ ctx.addIssue({
26
+ code: "custom",
27
+ path: ["artifact", "sha256"],
28
+ message: `${label} artifact hash must equal its canonical material digest`,
29
+ });
30
+ }
31
+ if (evidence.artifact.byteLength === 0) {
32
+ ctx.addIssue({
33
+ code: "custom",
34
+ path: ["artifact", "byteLength"],
35
+ message: `${label} artifact must contain canonical material bytes`,
36
+ });
37
+ }
38
+ });
39
+ }
@@ -1,3 +1,4 @@
1
+ import { agentCandidateEvidenceSchema } from "./agent-candidate-evidence-schema.js";
1
2
  import { z } from "zod";
2
3
  import { agentCandidateArtifactRefSchema, agentCandidateCapturedArtifactSchema, agentCandidateWorkspaceSnapshotEvidenceSchema, } from "./agent-candidate-artifact-schema.js";
3
4
  import { agentCandidateContainerSchema, agentCandidateInstructionDeliverySchema, agentCandidateWorkingDirectorySchema, } from "./agent-candidate-code-schema.js";
@@ -448,30 +449,7 @@ export const agentCandidateExecutionPlanMaterialSchema = z
448
449
  }
449
450
  });
450
451
  function planEvidenceSchema(kind, material) {
451
- return z
452
- .object({
453
- kind: z.literal(kind),
454
- digest: sha256DigestSchema,
455
- material,
456
- artifact: agentCandidateCapturedArtifactSchema,
457
- })
458
- .strict()
459
- .superRefine((evidence, ctx) => {
460
- if (evidence.artifact.sha256 !== evidence.digest) {
461
- ctx.addIssue({
462
- code: "custom",
463
- path: ["artifact", "sha256"],
464
- message: "plan artifact hash must equal its canonical material digest",
465
- });
466
- }
467
- if (evidence.artifact.byteLength === 0) {
468
- ctx.addIssue({
469
- code: "custom",
470
- path: ["artifact", "byteLength"],
471
- message: "plan artifact must contain canonical material bytes",
472
- });
473
- }
474
- });
452
+ return agentCandidateEvidenceSchema(kind, material, "plan");
475
453
  }
476
454
  export const agentCandidateProfilePlanEvidenceSchema = planEvidenceSchema("agent-profile-workspace-plan", agentCandidateProfilePlanMaterialSchema);
477
455
  export const agentCandidateProfileActivationSchema = createAgentProfileActivationEvidenceSchema(agentCandidateProfilePlanEvidenceSchema)
@@ -1,5 +1,6 @@
1
+ import { agentCandidateEvidenceSchema } from "./agent-candidate-evidence-schema.js";
1
2
  import { z } from "zod";
2
- import { agentCandidateArtifactRefSchema, agentCandidateCapturedArtifactSchema, agentCandidateWorkspaceSnapshotEvidenceSchema, } from "./agent-candidate-artifact-schema.js";
3
+ import { agentCandidateArtifactRefSchema, agentCandidateWorkspaceSnapshotEvidenceSchema, } from "./agent-candidate-artifact-schema.js";
3
4
  import { agentCandidateBenchmarkGraderIdentitySchema, agentCandidateResolvedModelSchema, } from "./agent-candidate-execution-plan-schema.js";
4
5
  import { agentCandidateMediaTypeSchema, gitObjectSchema, isCanonicalJsonValue, sameGitObjectFormat, sha256DigestSchema, } from "./agent-candidate-schema-common.js";
5
6
  const safeCountSchema = z
@@ -174,7 +175,7 @@ function refineModelSettlementMaterial(material, ctx) {
174
175
  });
175
176
  }
176
177
  }
177
- export const agentCandidateModelSettlementEvidenceSchema = evidenceSchema("agent-candidate-model-settlement", agentCandidateModelSettlementMaterialSchema, "model settlement");
178
+ export const agentCandidateModelSettlementEvidenceSchema = agentCandidateEvidenceSchema("agent-candidate-model-settlement", agentCandidateModelSettlementMaterialSchema, "model settlement");
178
179
  export const agentCandidateRepositoryStateSchema = z
179
180
  .object({
180
181
  identity: z.string().min(1),
@@ -278,7 +279,7 @@ export const agentCandidateTaskOutcomeMaterialSchema = z
278
279
  });
279
280
  }
280
281
  });
281
- export const agentCandidateTaskOutcomeEvidenceSchema = evidenceSchema("agent-candidate-task-outcome", agentCandidateTaskOutcomeMaterialSchema, "task outcome");
282
+ export const agentCandidateTaskOutcomeEvidenceSchema = agentCandidateEvidenceSchema("agent-candidate-task-outcome", agentCandidateTaskOutcomeMaterialSchema, "task outcome");
282
283
  export const agentCandidateBenchmarkDimensionSchema = z
283
284
  .object({
284
285
  name: normalizedDimensionNameSchema,
@@ -346,7 +347,7 @@ export const agentCandidateBenchmarkResultMaterialSchema = z
346
347
  });
347
348
  }
348
349
  });
349
- export const agentCandidateBenchmarkResultEvidenceSchema = evidenceSchema("agent-candidate-benchmark-result", agentCandidateBenchmarkResultMaterialSchema, "benchmark result");
350
+ export const agentCandidateBenchmarkResultEvidenceSchema = agentCandidateEvidenceSchema("agent-candidate-benchmark-result", agentCandidateBenchmarkResultMaterialSchema, "benchmark result");
350
351
  function sameFixedSpend(left, right) {
351
352
  return (left.inputTokens === right.inputTokens &&
352
353
  left.outputTokens === right.outputTokens &&
@@ -356,29 +357,3 @@ function sameFixedSpend(left, right) {
356
357
  left.costUsdNanos === right.costUsdNanos &&
357
358
  left.costProvenance === right.costProvenance);
358
359
  }
359
- function evidenceSchema(kind, material, label) {
360
- return z
361
- .object({
362
- kind: z.literal(kind),
363
- digest: sha256DigestSchema,
364
- material,
365
- artifact: agentCandidateCapturedArtifactSchema,
366
- })
367
- .strict()
368
- .superRefine((evidence, ctx) => {
369
- if (evidence.artifact.sha256 !== evidence.digest) {
370
- ctx.addIssue({
371
- code: "custom",
372
- path: ["artifact", "sha256"],
373
- message: `${label} artifact hash must equal its canonical material digest`,
374
- });
375
- }
376
- if (evidence.artifact.byteLength === 0) {
377
- ctx.addIssue({
378
- code: "custom",
379
- path: ["artifact", "byteLength"],
380
- message: `${label} artifact must contain canonical material bytes`,
381
- });
382
- }
383
- });
384
- }
@@ -8407,6 +8407,7 @@ export declare const agentImprovementProposalSchema: z.ZodObject<{
8407
8407
  "agent-profile": "agent-profile";
8408
8408
  memory: "memory";
8409
8409
  knowledge: "knowledge";
8410
+ "rollout-policy": "rollout-policy";
8410
8411
  }>], z.ZodEnum<{
8411
8412
  prompt: "prompt";
8412
8413
  tools: "tools";
@@ -8418,6 +8419,7 @@ export declare const agentImprovementProposalSchema: z.ZodObject<{
8418
8419
  "agent-profile": "agent-profile";
8419
8420
  memory: "memory";
8420
8421
  knowledge: "knowledge";
8422
+ "rollout-policy": "rollout-policy";
8421
8423
  }>>;
8422
8424
  proposedAt: z.ZodISODateTime;
8423
8425
  findings: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodCustom<AgentCandidateJsonValue, AgentCandidateJsonValue>>>;
@@ -14120,6 +14122,7 @@ export declare const agentImprovementActivationSchema: z.ZodObject<{
14120
14122
  "agent-profile": "agent-profile";
14121
14123
  memory: "memory";
14122
14124
  knowledge: "knowledge";
14125
+ "rollout-policy": "rollout-policy";
14123
14126
  }>;
14124
14127
  identity: z.ZodString;
14125
14128
  expectedBaseDigest: z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>;
@@ -14135,6 +14138,7 @@ export declare const agentImprovementActivationSchema: z.ZodObject<{
14135
14138
  "agent-profile": "agent-profile";
14136
14139
  memory: "memory";
14137
14140
  knowledge: "knowledge";
14141
+ "rollout-policy": "rollout-policy";
14138
14142
  }>;
14139
14143
  identity: z.ZodString;
14140
14144
  expectedBaseDigest: z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>;
@@ -14165,6 +14169,7 @@ export declare const agentImprovementActivationResultSchema: z.ZodObject<{
14165
14169
  "agent-profile": "agent-profile";
14166
14170
  memory: "memory";
14167
14171
  knowledge: "knowledge";
14172
+ "rollout-policy": "rollout-policy";
14168
14173
  }>;
14169
14174
  identity: z.ZodString;
14170
14175
  beforeDigest: z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>;
@@ -14181,6 +14186,7 @@ export declare const agentImprovementActivationResultSchema: z.ZodObject<{
14181
14186
  "agent-profile": "agent-profile";
14182
14187
  memory: "memory";
14183
14188
  knowledge: "knowledge";
14189
+ "rollout-policy": "rollout-policy";
14184
14190
  }>;
14185
14191
  identity: z.ZodString;
14186
14192
  beforeDigest: z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>;
@@ -14200,6 +14206,7 @@ export declare const agentImprovementActivationResultSchema: z.ZodObject<{
14200
14206
  "agent-profile": "agent-profile";
14201
14207
  memory: "memory";
14202
14208
  knowledge: "knowledge";
14209
+ "rollout-policy": "rollout-policy";
14203
14210
  }>;
14204
14211
  identity: z.ZodString;
14205
14212
  currentDigest: z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>;
@@ -14215,6 +14222,7 @@ export declare const agentImprovementActivationResultSchema: z.ZodObject<{
14215
14222
  "agent-profile": "agent-profile";
14216
14223
  memory: "memory";
14217
14224
  knowledge: "knowledge";
14225
+ "rollout-policy": "rollout-policy";
14218
14226
  }>;
14219
14227
  identity: z.ZodString;
14220
14228
  currentDigest: z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>;
@@ -14233,6 +14241,7 @@ export declare const agentImprovementActivationResultSchema: z.ZodObject<{
14233
14241
  "agent-profile": "agent-profile";
14234
14242
  memory: "memory";
14235
14243
  knowledge: "knowledge";
14244
+ "rollout-policy": "rollout-policy";
14236
14245
  }>;
14237
14246
  identity: z.ZodString;
14238
14247
  currentDigest: z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>;
@@ -14248,6 +14257,7 @@ export declare const agentImprovementActivationResultSchema: z.ZodObject<{
14248
14257
  "agent-profile": "agent-profile";
14249
14258
  memory: "memory";
14250
14259
  knowledge: "knowledge";
14260
+ "rollout-policy": "rollout-policy";
14251
14261
  }>;
14252
14262
  identity: z.ZodString;
14253
14263
  currentDigest: z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>;
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { AGENT_IMPROVEMENT_SURFACES } from "./agent-candidate.js";
2
3
  import { agentCandidateBundleSchema } from "./agent-candidate-schema.js";
3
4
  import { agentCandidateLineageSchema } from "./agent-candidate-lineage-schema.js";
4
5
  import { agentCandidateBenchmarkSuiteInputsSchema } from "./agent-candidate-task-schema.js";
@@ -7,18 +8,7 @@ import { refineAgentExecutionWithinLimits } from "./agent-execution-limits.js";
7
8
  import { agentCandidateMaterializationReceiptSchema, agentCandidateRunReceiptSchema, } from "./agent-candidate-receipt-schema.js";
8
9
  import { agentCandidateEvaluationPolicySchema, canonicalJsonObjectSchema, createMeasuredComparisonIdentityRegistry, measuredComparisonCommonShape, refineMeasuredComparisonSummary, } from "./agent-improvement-measurement-schema.js";
9
10
  import { agentProfileImprovementExecutionRefSchema, agentProfileImprovementMeasuredComparisonSchema, changedProfileImprovementSurfaces, } from "./agent-profile-improvement-schema.js";
10
- const improvementSurfaceSchema = z.enum([
11
- "prompt",
12
- "skills",
13
- "tools",
14
- "mcp",
15
- "hooks",
16
- "subagents",
17
- "agent-profile",
18
- "memory",
19
- "code",
20
- "knowledge",
21
- ]);
11
+ const improvementSurfaceSchema = z.enum(AGENT_IMPROVEMENT_SURFACES);
22
12
  export const agentCandidateExperimentSchema = z
23
13
  .object({
24
14
  kind: z.literal("agent-candidate-experiment"),
@@ -709,7 +709,16 @@ export interface AgentCandidateRunReceipt {
709
709
  benchmarkResult: AgentCandidateBenchmarkResultEvidence;
710
710
  digest: Sha256Digest;
711
711
  }
712
- export type AgentImprovementSurface = "prompt" | "skills" | "tools" | "mcp" | "hooks" | "subagents" | "agent-profile" | "memory" | "code" | "knowledge";
712
+ /**
713
+ * Every surface an improvement proposal can name. One owner: the validator
714
+ * ({@link agentCandidatePromotionSchema}'s surface enum) and every producer read this
715
+ * list, so a new surface cannot be proposable in one place and unnameable in another.
716
+ *
717
+ * `rollout-policy` is the inference-time structural-rollout dials
718
+ * (`profile.extensions['structural-rollout']`); `knowledge` is the corpus lane.
719
+ */
720
+ export declare const AGENT_IMPROVEMENT_SURFACES: readonly ["prompt", "skills", "tools", "mcp", "hooks", "subagents", "agent-profile", "memory", "code", "knowledge", "rollout-policy"];
721
+ export type AgentImprovementSurface = (typeof AGENT_IMPROVEMENT_SURFACES)[number];
713
722
  /** One paired Runtime execution from the exact signed experiment. */
714
723
  export interface AgentCandidateExperimentMeasurement {
715
724
  baseline: CandidateExecutionEvidence;
@@ -1,3 +1,24 @@
1
+ /**
2
+ * Every surface an improvement proposal can name. One owner: the validator
3
+ * ({@link agentCandidatePromotionSchema}'s surface enum) and every producer read this
4
+ * list, so a new surface cannot be proposable in one place and unnameable in another.
5
+ *
6
+ * `rollout-policy` is the inference-time structural-rollout dials
7
+ * (`profile.extensions['structural-rollout']`); `knowledge` is the corpus lane.
8
+ */
9
+ export const AGENT_IMPROVEMENT_SURFACES = Object.freeze([
10
+ "prompt",
11
+ "skills",
12
+ "tools",
13
+ "mcp",
14
+ "hooks",
15
+ "subagents",
16
+ "agent-profile",
17
+ "memory",
18
+ "code",
19
+ "knowledge",
20
+ "rollout-policy",
21
+ ]);
1
22
  /** Declare a candidate bundle while retaining literal inference. */
2
23
  export function defineAgentCandidateBundle(bundle) {
3
24
  return bundle;
@@ -1,4 +1,5 @@
1
1
  import { canonicalAgentProfileValue } from "./agent-profile-canonical.js";
2
+ import { deepFreeze } from "./deep-freeze.js";
2
3
  import { agentProfileSchema } from "./profile-schema.js";
3
4
  /**
4
5
  * Detach, validate, and recursively freeze one AgentProfile at an intake boundary.
@@ -12,13 +13,3 @@ export function snapshotAgentProfile(value) {
12
13
  canonicalAgentProfileValue(parsed);
13
14
  return deepFreeze(parsed);
14
15
  }
15
- function deepFreeze(value, seen = new Set()) {
16
- if (value === null || typeof value !== "object" || seen.has(value)) {
17
- return value;
18
- }
19
- seen.add(value);
20
- for (const child of Object.values(value)) {
21
- deepFreeze(child, seen);
22
- }
23
- return Object.freeze(value);
24
- }
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { deepFreeze } from "./deep-freeze.js";
2
3
  import { canonicalCandidateDigest, isSafeRelativePath, sha256DigestSchema, } from "./agent-candidate-schema-common.js";
3
4
  const MAX_INLINE_CONTEXT_BYTES = 65_536;
4
5
  const MAX_TOTAL_INLINE_CONTEXT_BYTES = 131_072;
@@ -203,15 +204,6 @@ export const certifiedContextSchema = z
203
204
  });
204
205
  }
205
206
  });
206
- function deepFreeze(value) {
207
- if (value !== null && typeof value === "object" && !Object.isFrozen(value)) {
208
- for (const child of Object.values(value)) {
209
- deepFreeze(child);
210
- }
211
- Object.freeze(value);
212
- }
213
- return value;
214
- }
215
207
  const _certifiedContextDeliverySchemaMatches = true;
216
208
  const _certifiedContextProvenanceSchemaMatches = true;
217
209
  const _certifiedContextEntrySchemaMatches = true;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Make a validated value immutable before it is handed out.
3
+ *
4
+ * A profile snapshot, a certified context, and a capability document are each
5
+ * evidence: something already checked, that a caller then reads and acts on.
6
+ * If the copy the caller holds can be written to, the evidence can be changed
7
+ * after the check that made it trustworthy, and a mutated capability flag
8
+ * describes a surface the environment does not have.
9
+ *
10
+ * Values already visited are skipped, so a value that refers back to itself is
11
+ * frozen once instead of recursing until the stack runs out.
12
+ */
13
+ export declare function deepFreeze<T>(value: T, seen?: Set<object>): T;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Make a validated value immutable before it is handed out.
3
+ *
4
+ * A profile snapshot, a certified context, and a capability document are each
5
+ * evidence: something already checked, that a caller then reads and acts on.
6
+ * If the copy the caller holds can be written to, the evidence can be changed
7
+ * after the check that made it trustworthy, and a mutated capability flag
8
+ * describes a surface the environment does not have.
9
+ *
10
+ * Values already visited are skipped, so a value that refers back to itself is
11
+ * frozen once instead of recursing until the stack runs out.
12
+ */
13
+ export function deepFreeze(value, seen = new Set()) {
14
+ if (value === null || typeof value !== "object" || seen.has(value)) {
15
+ return value;
16
+ }
17
+ seen.add(value);
18
+ for (const child of Object.values(value)) {
19
+ deepFreeze(child, seen);
20
+ }
21
+ return Object.freeze(value);
22
+ }
@@ -0,0 +1,48 @@
1
+ import type { ExecResult } from "./environment-requests.js";
2
+ import type { AgentEnvironment, AgentEnvironmentCapabilities, AgentEnvironmentEvent, AgentTurnInput } from "./environment-runtime.js";
3
+ /**
4
+ * Normalize the untyped return of a sandbox SDK's command call into
5
+ * {@link ExecResult}.
6
+ *
7
+ * Sandbox SDKs name the same three facts differently: an exit status under
8
+ * `exitCode` or `code`, captured output under `stdout` or `output`, and
9
+ * captured errors under `stderr` or `error`. Reading both names in one place
10
+ * keeps every adapter's exec surface answering with the same shape.
11
+ *
12
+ * A result carrying no exit status is refused. Exit zero is the one value that
13
+ * means the command succeeded, so reading an absent status as zero reports a
14
+ * command nobody watched as a command that worked, and the turn that ran it
15
+ * completes on evidence that was never measured. `source` names the SDK call
16
+ * whose answer could not be read, because that is where the fix belongs.
17
+ */
18
+ export declare function execResultFromUnknown(value: unknown, source: string): ExecResult;
19
+ export interface CommandTurnOptions {
20
+ /** The turn the caller asked this environment to run. */
21
+ input: AgentTurnInput;
22
+ /** The environment whose `exec` runs the command. */
23
+ environment: AgentEnvironment;
24
+ /** The adapter name the refusal names when no command can be resolved. */
25
+ providerLabel: string;
26
+ /** The caller's explicit command for this turn, chosen before any default. */
27
+ turnCommand?: (input: AgentTurnInput, environment: AgentEnvironment) => string | Promise<string>;
28
+ }
29
+ /**
30
+ * Run one turn as a single command in an environment whose only surface is a
31
+ * workspace, and emit the turn's events.
32
+ *
33
+ * The command comes from the caller's `turnCommand`, then
34
+ * `providerOptions.command` or `providerOptions.agentCommand`, then the
35
+ * prompt. An environment that reaches none of them cannot run the turn and
36
+ * the turn is refused rather than executing an empty command.
37
+ */
38
+ export declare function commandTurnEvents(options: CommandTurnOptions): AsyncIterable<AgentEnvironmentEvent>;
39
+ /**
40
+ * The capability document of a provider whose only surface is a workspace.
41
+ *
42
+ * Such an adapter runs commands and moves files in a sandbox. It owns no
43
+ * agent profile, serves no live or replayable stream, keeps no provider
44
+ * session, and branches no environment, so every surface outside `workspace`
45
+ * and `placement` reads false. The caller states its own workspace facts
46
+ * because they are the only ones that differ between these adapters.
47
+ */
48
+ export declare function execOnlyEnvironmentCapabilities(workspace: AgentEnvironmentCapabilities["workspace"]): AgentEnvironmentCapabilities;
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Read one non-empty string from an untyped provider-options bag.
3
+ *
4
+ * An absent key and a present key holding a non-string or an empty string are
5
+ * the same answer: the caller named nothing.
6
+ */
7
+ function stringOption(value) {
8
+ return typeof value === "string" && value.length > 0 ? value : undefined;
9
+ }
10
+ function number(value) {
11
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
12
+ }
13
+ /**
14
+ * Normalize the untyped return of a sandbox SDK's command call into
15
+ * {@link ExecResult}.
16
+ *
17
+ * Sandbox SDKs name the same three facts differently: an exit status under
18
+ * `exitCode` or `code`, captured output under `stdout` or `output`, and
19
+ * captured errors under `stderr` or `error`. Reading both names in one place
20
+ * keeps every adapter's exec surface answering with the same shape.
21
+ *
22
+ * A result carrying no exit status is refused. Exit zero is the one value that
23
+ * means the command succeeded, so reading an absent status as zero reports a
24
+ * command nobody watched as a command that worked, and the turn that ran it
25
+ * completes on evidence that was never measured. `source` names the SDK call
26
+ * whose answer could not be read, because that is where the fix belongs.
27
+ */
28
+ export function execResultFromUnknown(value, source) {
29
+ const record = value && typeof value === "object" ? value : {};
30
+ const exitCode = number(record.exitCode) ?? number(record.code);
31
+ if (exitCode === undefined) {
32
+ throw new Error(`${source} returned no exit status: a command result must carry a finite exitCode or code`);
33
+ }
34
+ return {
35
+ exitCode,
36
+ stdout: typeof record.stdout === "string"
37
+ ? record.stdout
38
+ : typeof record.output === "string"
39
+ ? record.output
40
+ : "",
41
+ stderr: typeof record.stderr === "string"
42
+ ? record.stderr
43
+ : typeof record.error === "string"
44
+ ? record.error
45
+ : "",
46
+ };
47
+ }
48
+ /**
49
+ * Run one turn as a single command in an environment whose only surface is a
50
+ * workspace, and emit the turn's events.
51
+ *
52
+ * The command comes from the caller's `turnCommand`, then
53
+ * `providerOptions.command` or `providerOptions.agentCommand`, then the
54
+ * prompt. An environment that reaches none of them cannot run the turn and
55
+ * the turn is refused rather than executing an empty command.
56
+ */
57
+ export async function* commandTurnEvents(options) {
58
+ const { input, environment, providerLabel } = options;
59
+ const command = (await options.turnCommand?.(input, environment)) ??
60
+ stringOption(input.providerOptions?.command) ??
61
+ stringOption(input.providerOptions?.agentCommand) ??
62
+ input.prompt;
63
+ if (!command) {
64
+ throw new Error(`${providerLabel} provider requires turnCommand, providerOptions.command, or prompt`);
65
+ }
66
+ const result = await environment.exec?.(command, {
67
+ cwd: stringOption(input.providerOptions?.cwd),
68
+ timeoutMs: input.timeoutMs,
69
+ signal: input.signal,
70
+ });
71
+ const text = result?.stdout ?? "";
72
+ yield { type: "message.part.updated", data: { delta: text } };
73
+ yield {
74
+ type: "result",
75
+ data: {
76
+ finalText: text,
77
+ status: result?.exitCode === 0 ? "completed" : "failed",
78
+ exitCode: result?.exitCode ?? 1,
79
+ stderr: result?.stderr ?? "",
80
+ },
81
+ };
82
+ }
83
+ /**
84
+ * The capability document of a provider whose only surface is a workspace.
85
+ *
86
+ * Such an adapter runs commands and moves files in a sandbox. It owns no
87
+ * agent profile, serves no live or replayable stream, keeps no provider
88
+ * session, and branches no environment, so every surface outside `workspace`
89
+ * and `placement` reads false. The caller states its own workspace facts
90
+ * because they are the only ones that differ between these adapters.
91
+ */
92
+ export function execOnlyEnvironmentCapabilities(workspace) {
93
+ return {
94
+ profile: {
95
+ namedProfiles: false,
96
+ systemPrompt: { replace: false, append: false },
97
+ instructions: false,
98
+ tools: false,
99
+ permissions: false,
100
+ mcp: false,
101
+ subagents: false,
102
+ resources: {
103
+ files: true,
104
+ instructions: false,
105
+ tools: false,
106
+ skills: false,
107
+ agents: false,
108
+ commands: false,
109
+ },
110
+ hooks: false,
111
+ modes: false,
112
+ runtimeUpdate: false,
113
+ validation: false,
114
+ },
115
+ streaming: {
116
+ live: false,
117
+ replay: false,
118
+ detach: false,
119
+ turnIdempotency: false,
120
+ },
121
+ sessions: { continue: false, list: false, messages: false },
122
+ workspace: { ...workspace },
123
+ branching: { checkpoint: false, fork: false },
124
+ placement: true,
125
+ usage: false,
126
+ confidential: false,
127
+ };
128
+ }
@@ -135,8 +135,8 @@ export declare const EnvironmentLifecycleSchema: z.ZodObject<{
135
135
  resumable: z.ZodBoolean;
136
136
  mode: z.ZodOptional<z.ZodEnum<{
137
137
  none: "none";
138
- native: "native";
139
138
  replayed: "replayed";
139
+ native: "native";
140
140
  }>>;
141
141
  }, z.core.$strict>>;
142
142
  persistence: z.ZodOptional<z.ZodObject<{
@@ -460,8 +460,8 @@ export declare const AgentEnvironmentObservationSchema: z.ZodObject<{
460
460
  resumable: z.ZodBoolean;
461
461
  mode: z.ZodOptional<z.ZodEnum<{
462
462
  none: "none";
463
- native: "native";
464
463
  replayed: "replayed";
464
+ native: "native";
465
465
  }>>;
466
466
  }, z.core.$strict>>;
467
467
  persistence: z.ZodOptional<z.ZodObject<{
@@ -508,8 +508,8 @@ export declare const AgentEnvironmentObservationSchema: z.ZodObject<{
508
508
  resumable: z.ZodBoolean;
509
509
  mode: z.ZodOptional<z.ZodEnum<{
510
510
  none: "none";
511
- native: "native";
512
511
  replayed: "replayed";
512
+ native: "native";
513
513
  }>>;
514
514
  }, z.core.$strict>>;
515
515
  persistence: z.ZodOptional<z.ZodObject<{
@@ -4,3 +4,4 @@ export * from "./environment-interactive.js";
4
4
  export * from "./environment-observation.js";
5
5
  export * from "./environment-terminal.js";
6
6
  export * from "./environment-runtime.js";
7
+ export * from "./environment-command-turn.js";
@@ -4,3 +4,4 @@ export * from "./environment-interactive.js";
4
4
  export * from "./environment-observation.js";
5
5
  export * from "./environment-terminal.js";
6
6
  export * from "./environment-runtime.js";
7
+ export * from "./environment-command-turn.js";
@@ -61,6 +61,15 @@ export interface ModelReasoningCapability {
61
61
  * lower ceiling caps the list there. Pass `model` from your catalog; omit it for the harness-only set.
62
62
  */
63
63
  export declare function reasoningEffortsFor(harness: HarnessType, model?: ModelReasoningCapability | null): readonly ReasoningEffort[];
64
+ /**
65
+ * The native control token a harness applies for one canonical effort, or `null` when it applies
66
+ * none. `effort: null` (nothing requested) is always `null`.
67
+ *
68
+ * One owner for both sides of the check: the adapter that builds the harness argv and the caller
69
+ * that verifies the receipt read this function, so a CLI that renames a rung moves both at once
70
+ * instead of turning into a refused run.
71
+ */
72
+ export declare function nativeReasoningControl(harness: HarnessType, effort: ReasoningEffort | null): string | null;
64
73
  /** Whether the harness's runner honors a per-turn MODEL override (vs. picking the model itself). */
65
74
  export declare function harnessHonorsModel(harness: HarnessType): boolean;
66
75
  /** Whether the harness's runner honors a reasoning-EFFORT override (vs. dropping it). */
@@ -123,8 +123,8 @@ export function snapHarnessToModel(harness, modelId) {
123
123
  * - claude-code: `--effort` accepts `low|medium|high|xhigh|max`; canonical `ultracode` maps to
124
124
  * native `max`. It cannot express `none` or `minimal`, and an unsupported value is warned about
125
125
  * and silently replaced with the default rather than rejected — so the set must not overstate.
126
- * - pi: `--thinking` accepts `off|minimal|low|medium|high|xhigh|max`; canonical `none` maps to
127
- * `off` and `ultracode` to `max`.
126
+ * - pi: `--thinking` accepts `off|minimal|low|medium|high|xhigh`; canonical `none` maps to
127
+ * `off` and `ultracode` clamps to `xhigh`, its top rung.
128
128
  * - prime: the prime fork of the pi line accepts the same `--thinking` set
129
129
  * (`off|minimal|low|medium|high|xhigh|max`); canonical `none` maps to `off` and `ultracode` to
130
130
  * `max`.
@@ -186,6 +186,70 @@ export function reasoningEffortsFor(harness, model) {
186
186
  }
187
187
  return efforts;
188
188
  }
189
+ // ── Native reasoning control (the exact token the harness process receives) ───
190
+ /**
191
+ * Canonical effort → the harness's OWN control token, or `null` when the harness applies no
192
+ * native reasoning control for that request. This is the value a materialization receipt carries
193
+ * as `reasoningEffort.applied`, so a caller can check that the effort it asked for reached the
194
+ * process instead of trusting an echo of its own request.
195
+ *
196
+ * Read from the argv builders that actually spawn each CLI, not from help text:
197
+ * - claude-code — `--effort <value>`; it cannot express `none` or `minimal`, so both clamp to
198
+ * `low`, and `ultracode` becomes its ceiling `max`.
199
+ * - codex — `-c model_reasoning_effort="<value>"`; it takes the canonical rungs directly and
200
+ * names its ceiling `ultra`.
201
+ * - pi — `--thinking <value>`; `none` becomes `off` and `ultracode` clamps to `xhigh`, the
202
+ * highest rung the pi line accepts.
203
+ * - prime — `--thinking <value>`; the fork carries `max` above `xhigh`, so `ultracode` reaches
204
+ * `max`. This is the one rung where prime and pi differ.
205
+ * - kimi-code — the control is the FLAG itself, `--thinking` or `--no-thinking`, because kimi's
206
+ * thinking switch is binary. `medium` is its default and passes no flag at all.
207
+ * - opencode — the router-backed variant name is the canonical rung unchanged.
208
+ *
209
+ * A harness with no entry applies NO native control: either it derives thinking from the model
210
+ * (gemini's `--thinking-budget`) or it plumbs no thinking flag at all (see
211
+ * {@link harnessHonorsEffort}). Both answer `null`, which is what their receipts carry — so an
212
+ * unknown harness is never asserted to have applied a control it cannot apply.
213
+ */
214
+ const harnessNativeReasoningControl = {
215
+ "claude-code": (effort) => {
216
+ if (effort === "none" || effort === "minimal")
217
+ return "low";
218
+ return effort === "ultracode" ? "max" : effort;
219
+ },
220
+ codex: (effort) => (effort === "ultracode" ? "ultra" : effort),
221
+ pi: (effort) => {
222
+ if (effort === "none")
223
+ return "off";
224
+ return effort === "ultracode" ? "xhigh" : effort;
225
+ },
226
+ prime: (effort) => {
227
+ if (effort === "none")
228
+ return "off";
229
+ return effort === "ultracode" ? "max" : effort;
230
+ },
231
+ "kimi-code": (effort) => {
232
+ if (effort === "medium")
233
+ return null;
234
+ return effort === "none" || effort === "minimal" || effort === "low"
235
+ ? "--no-thinking"
236
+ : "--thinking";
237
+ },
238
+ opencode: (effort) => effort,
239
+ };
240
+ /**
241
+ * The native control token a harness applies for one canonical effort, or `null` when it applies
242
+ * none. `effort: null` (nothing requested) is always `null`.
243
+ *
244
+ * One owner for both sides of the check: the adapter that builds the harness argv and the caller
245
+ * that verifies the receipt read this function, so a CLI that renames a rung moves both at once
246
+ * instead of turning into a refused run.
247
+ */
248
+ export function nativeReasoningControl(harness, effort) {
249
+ if (effort === null)
250
+ return null;
251
+ return harnessNativeReasoningControl[harness]?.(effort) ?? null;
252
+ }
189
253
  // ── Per-turn selector support (does the harness honor the chat pickers?) ──────
190
254
  /**
191
255
  * Harnesses whose runner DROPS a per-turn selector — grounded in the cli-bridge adapter audit, NOT a
package/dist/index.d.ts CHANGED
@@ -33,6 +33,7 @@ export * from "./agent-profile-materialization.js";
33
33
  export * from "./agent-execution-preparation.js";
34
34
  export * from "./agent-workspace-lease.js";
35
35
  export * from "./certified-context.js";
36
+ export * from "./deep-freeze.js";
36
37
  export * from "./profile-diff.js";
37
38
  export * from "./harness.js";
38
39
  export * from "./harness-capabilities.js";
package/dist/index.js CHANGED
@@ -31,6 +31,7 @@ export * from "./agent-profile-materialization.js";
31
31
  export * from "./agent-execution-preparation.js";
32
32
  export * from "./agent-workspace-lease.js";
33
33
  export * from "./certified-context.js";
34
+ export * from "./deep-freeze.js";
34
35
  export * from "./profile-diff.js";
35
36
  export * from "./harness.js";
36
37
  export * from "./harness-capabilities.js";
@@ -33,6 +33,15 @@ export declare const AgentExactRunControlRefSchema: z.ZodObject<{
33
33
  executionId: z.ZodString;
34
34
  requestDigest: z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>;
35
35
  }, z.core.$strict>;
36
+ /**
37
+ * Whether two references name the same run.
38
+ *
39
+ * Comparison is over the reference's canonical form rather than a list of
40
+ * field names, so a reference that gains a field is compared on it without a
41
+ * caller having to remember: two runs that differ only in a field nobody
42
+ * enumerated must not read as one run.
43
+ */
44
+ export declare function sameAgentRunControlRef(left: AgentRunControlRef, right: AgentRunControlRef): boolean;
36
45
  export type AgentRunCancellationEffect = "cancel_requested" | "cancelled" | "not_live" | "unknown";
37
46
  export interface AgentRunCancellationRequestMaterial {
38
47
  operationId: string;
@@ -18,6 +18,17 @@ export const AgentExactRunControlRefSchema = AgentRunControlRefSchema.extend({
18
18
  executionId: stableIdSchema,
19
19
  requestDigest: sha256DigestSchema,
20
20
  });
21
+ /**
22
+ * Whether two references name the same run.
23
+ *
24
+ * Comparison is over the reference's canonical form rather than a list of
25
+ * field names, so a reference that gains a field is compared on it without a
26
+ * caller having to remember: two runs that differ only in a field nobody
27
+ * enumerated must not read as one run.
28
+ */
29
+ export function sameAgentRunControlRef(left, right) {
30
+ return canonicalCandidateDigest(left) === canonicalCandidateDigest(right);
31
+ }
21
32
  export function agentRunCancellationRequestDigest(request) {
22
33
  const parsed = AgentRunCancellationRequestMaterialSchema.parse(request);
23
34
  return canonicalCandidateDigest({
@@ -111,8 +122,7 @@ export function agentRunCancellationAcknowledgementMatchesRequest(request, ackno
111
122
  return false;
112
123
  return (exactAcknowledgement.data.operationId === exactRequest.data.operationId &&
113
124
  exactAcknowledgement.data.requestDigest === exactRequest.data.requestDigest &&
114
- canonicalCandidateDigest(exactAcknowledgement.data.run) ===
115
- canonicalCandidateDigest(exactRequest.data.run));
125
+ sameAgentRunControlRef(exactAcknowledgement.data.run, exactRequest.data.run));
116
126
  }
117
127
  export function agentRunControlRequestDigest(request) {
118
128
  const parsed = AgentRunControlRequestMaterialSchema.parse(request);
@@ -188,8 +198,7 @@ export function agentRunControlAcknowledgementMatchesRequest(request, acknowledg
188
198
  return false;
189
199
  return (exactAcknowledgement.data.operationId === exactRequest.data.operationId &&
190
200
  exactAcknowledgement.data.requestDigest === exactRequest.data.requestDigest &&
191
- canonicalCandidateDigest(exactAcknowledgement.data.run) ===
192
- canonicalCandidateDigest(exactRequest.data.run));
201
+ sameAgentRunControlRef(exactAcknowledgement.data.run, exactRequest.data.run));
193
202
  }
194
203
  const unknownRecordSchema = boundedJsonRecordSchema;
195
204
  const partBase = {
@@ -1,7 +1,45 @@
1
+ import { z } from "zod";
1
2
  import type { Sha256Digest } from "./agent-candidate.js";
2
3
  import { sha256DigestSchema } from "./agent-candidate-schema-common.js";
3
4
  export { sha256DigestSchema };
4
- export declare const idSchema: import("zod").ZodString;
5
- export declare const jsonRecordSchema: import("zod").ZodCustom<Record<string, unknown>, Record<string, unknown>>;
5
+ export declare const idSchema: z.ZodString;
6
+ export declare const jsonRecordSchema: z.ZodCustom<Record<string, unknown>, Record<string, unknown>>;
6
7
  export declare function wireDigest(value: unknown): Sha256Digest;
7
8
  export declare function sameOptionalWireValue(left: unknown, right: unknown): boolean;
9
+ /**
10
+ * The two fields that identify one durable workspace operation.
11
+ *
12
+ * Every result and lookup answer carries them, so a caller can bind an answer
13
+ * to the exact request it asked before using the resource it names.
14
+ */
15
+ export declare const operationIdentityShape: {
16
+ idempotencyKey: z.ZodString;
17
+ requestDigest: z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>;
18
+ };
19
+ /**
20
+ * Refuse a conflict answer that names the request it was asked about.
21
+ *
22
+ * A conflict states that the key is already held by a different request. An
23
+ * answer whose `existingRequestDigest` equals the request's own digest states
24
+ * a conflict with itself, which is a replay, so the caller would retry a
25
+ * request the service already accepted.
26
+ */
27
+ export declare function refuseSelfConflict(result: {
28
+ status: string;
29
+ requestDigest: string;
30
+ existingRequestDigest?: string;
31
+ }, ctx: z.RefinementCtx): void;
32
+ /**
33
+ * Whether the resource an answer carries was produced by that same operation.
34
+ *
35
+ * A checkpoint or forked environment repeats the key and digest of the
36
+ * operation that made it. An answer carrying a resource stamped with anything
37
+ * else names a resource the caller did not ask for.
38
+ */
39
+ export declare function operationResourceIdentityMatches(operation: {
40
+ idempotencyKey: string;
41
+ requestDigest: string;
42
+ }, resource: {
43
+ idempotencyKey: string;
44
+ requestDigest: string;
45
+ }): boolean;
@@ -18,3 +18,42 @@ export function sameOptionalWireValue(left, right) {
18
18
  return left === right;
19
19
  return wireDigest(left) === wireDigest(right);
20
20
  }
21
+ /**
22
+ * The two fields that identify one durable workspace operation.
23
+ *
24
+ * Every result and lookup answer carries them, so a caller can bind an answer
25
+ * to the exact request it asked before using the resource it names.
26
+ */
27
+ export const operationIdentityShape = {
28
+ idempotencyKey: idSchema,
29
+ requestDigest: sha256DigestSchema,
30
+ };
31
+ /**
32
+ * Refuse a conflict answer that names the request it was asked about.
33
+ *
34
+ * A conflict states that the key is already held by a different request. An
35
+ * answer whose `existingRequestDigest` equals the request's own digest states
36
+ * a conflict with itself, which is a replay, so the caller would retry a
37
+ * request the service already accepted.
38
+ */
39
+ export function refuseSelfConflict(result, ctx) {
40
+ if (result.status === "conflict" &&
41
+ result.existingRequestDigest === result.requestDigest) {
42
+ ctx.addIssue({
43
+ code: "custom",
44
+ path: ["existingRequestDigest"],
45
+ message: "a conflict must identify a different existing request",
46
+ });
47
+ }
48
+ }
49
+ /**
50
+ * Whether the resource an answer carries was produced by that same operation.
51
+ *
52
+ * A checkpoint or forked environment repeats the key and digest of the
53
+ * operation that made it. An answer carrying a resource stamped with anything
54
+ * else names a resource the caller did not ask for.
55
+ */
56
+ export function operationResourceIdentityMatches(operation, resource) {
57
+ return (resource.idempotencyKey === operation.idempotencyKey &&
58
+ resource.requestDigest === operation.requestDigest);
59
+ }
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { AgentExactRunControlRefSchema } from "./runtime-control.js";
3
- import { idSchema, jsonRecordSchema, sameOptionalWireValue, sha256DigestSchema, wireDigest } from "./workspace-branching-shared.js";
3
+ import { idSchema, jsonRecordSchema, operationIdentityShape, operationResourceIdentityMatches, refuseSelfConflict, sameOptionalWireValue, sha256DigestSchema, wireDigest } from "./workspace-branching-shared.js";
4
4
  import { boundedStringSchema } from "./contract-limits.js";
5
5
  const WorkspaceCheckpointMaterialSchema = z.strictObject({
6
6
  source: AgentExactRunControlRefSchema,
@@ -56,45 +56,33 @@ export const WorkspaceCheckpointRefSchema = z
56
56
  });
57
57
  }
58
58
  });
59
- const checkpointOperationBase = {
60
- idempotencyKey: idSchema,
61
- requestDigest: sha256DigestSchema,
62
- };
63
59
  export const WorkspaceCheckpointResultSchema = z.discriminatedUnion("status", [
64
60
  z.strictObject({
65
61
  status: z.enum(["created", "replayed"]),
66
- ...checkpointOperationBase,
62
+ ...operationIdentityShape,
67
63
  checkpoint: WorkspaceCheckpointRefSchema,
68
64
  }),
69
65
  z.strictObject({
70
66
  status: z.literal("conflict"),
71
- ...checkpointOperationBase,
67
+ ...operationIdentityShape,
72
68
  existingRequestDigest: sha256DigestSchema,
73
69
  }),
74
70
  z.strictObject({
75
71
  status: z.literal("unknown"),
76
- ...checkpointOperationBase,
72
+ ...operationIdentityShape,
77
73
  message: boundedStringSchema.min(1),
78
74
  retryable: z.boolean(),
79
75
  }),
80
- ]).superRefine((result, refinement) => {
76
+ ]).superRefine((result, ctx) => {
81
77
  if ((result.status === "created" || result.status === "replayed") &&
82
- (result.checkpoint.idempotencyKey !== result.idempotencyKey ||
83
- result.checkpoint.requestDigest !== result.requestDigest)) {
84
- refinement.addIssue({
78
+ !operationResourceIdentityMatches(result, result.checkpoint)) {
79
+ ctx.addIssue({
85
80
  code: "custom",
86
81
  path: ["checkpoint"],
87
82
  message: "checkpoint identity must match its operation",
88
83
  });
89
84
  }
90
- if (result.status === "conflict" &&
91
- result.existingRequestDigest === result.requestDigest) {
92
- refinement.addIssue({
93
- code: "custom",
94
- path: ["existingRequestDigest"],
95
- message: "a conflict must identify a different existing request",
96
- });
97
- }
85
+ refuseSelfConflict(result, ctx);
98
86
  });
99
87
  export const WorkspaceOperationLookupRequestSchema = z.strictObject({
100
88
  idempotencyKey: idSchema,
@@ -103,42 +91,34 @@ export const WorkspaceOperationLookupRequestSchema = z.strictObject({
103
91
  export const WorkspaceCheckpointLookupResultSchema = z.discriminatedUnion("status", [
104
92
  z.strictObject({
105
93
  status: z.literal("found"),
106
- ...checkpointOperationBase,
94
+ ...operationIdentityShape,
107
95
  checkpoint: WorkspaceCheckpointRefSchema,
108
96
  }),
109
97
  z.strictObject({
110
98
  status: z.literal("not_found"),
111
- ...checkpointOperationBase,
99
+ ...operationIdentityShape,
112
100
  }),
113
101
  z.strictObject({
114
102
  status: z.literal("conflict"),
115
- ...checkpointOperationBase,
103
+ ...operationIdentityShape,
116
104
  existingRequestDigest: sha256DigestSchema,
117
105
  }),
118
106
  z.strictObject({
119
107
  status: z.literal("unknown"),
120
- ...checkpointOperationBase,
108
+ ...operationIdentityShape,
121
109
  message: boundedStringSchema.min(1),
122
110
  retryable: z.boolean(),
123
111
  }),
124
- ]).superRefine((result, refinement) => {
112
+ ]).superRefine((result, ctx) => {
125
113
  if (result.status === "found" &&
126
- (result.checkpoint.idempotencyKey !== result.idempotencyKey ||
127
- result.checkpoint.requestDigest !== result.requestDigest)) {
128
- refinement.addIssue({
114
+ !operationResourceIdentityMatches(result, result.checkpoint)) {
115
+ ctx.addIssue({
129
116
  code: "custom",
130
117
  path: ["checkpoint"],
131
118
  message: "checkpoint identity must match its lookup operation",
132
119
  });
133
120
  }
134
- if (result.status === "conflict" &&
135
- result.existingRequestDigest === result.requestDigest) {
136
- refinement.addIssue({
137
- code: "custom",
138
- path: ["existingRequestDigest"],
139
- message: "a conflict must identify a different existing request",
140
- });
141
- }
121
+ refuseSelfConflict(result, ctx);
142
122
  });
143
123
  /** Bind a checkpoint result to the exact request before using the resource. */
144
124
  export function workspaceCheckpointResultMatchesRequest(request, result) {
@@ -159,10 +139,8 @@ export function workspaceCheckpointResultMatchesRequest(request, result) {
159
139
  return false;
160
140
  }
161
141
  function checkpointResultMatchesParsed(request, result) {
162
- return (result.idempotencyKey === request.idempotencyKey &&
163
- result.requestDigest === request.requestDigest &&
164
- result.checkpoint.idempotencyKey === request.idempotencyKey &&
165
- result.checkpoint.requestDigest === request.requestDigest &&
142
+ return (operationResourceIdentityMatches(request, result) &&
143
+ operationResourceIdentityMatches(request, result.checkpoint) &&
166
144
  wireDigest(result.checkpoint.source) === wireDigest(request.source) &&
167
145
  sameOptionalWireValue(result.checkpoint.metadata, request.metadata) &&
168
146
  result.checkpoint.provider === request.source.provider);
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { AgentExactRunControlRefSchema } from "./runtime-control.js";
3
- import { idSchema, jsonRecordSchema, sameOptionalWireValue, sha256DigestSchema, wireDigest } from "./workspace-branching-shared.js";
3
+ import { idSchema, jsonRecordSchema, operationIdentityShape, operationResourceIdentityMatches, refuseSelfConflict, sameOptionalWireValue, sha256DigestSchema, wireDigest } from "./workspace-branching-shared.js";
4
4
  import { WorkspaceCheckpointRefSchema } from "./workspace-checkpoint.js";
5
5
  import { ConfidentialAttestationSchema, ConfidentialExecutionRequestSchema, confidentialExecutionVerified, } from "./workspace-confidentiality.js";
6
6
  import { boundedStringSchema } from "./contract-limits.js";
@@ -155,82 +155,62 @@ export function forkedEnvironmentConfidentialityVerified(request, environment, v
155
155
  ...(verifyProviderAttestation ? { verifyProviderAttestation } : {}),
156
156
  });
157
157
  }
158
- const forkOperationBase = {
159
- idempotencyKey: idSchema,
160
- requestDigest: sha256DigestSchema,
161
- };
162
158
  export const WorkspaceForkResultSchema = z.discriminatedUnion("status", [
163
159
  z.strictObject({
164
160
  status: z.enum(["created", "replayed"]),
165
- ...forkOperationBase,
161
+ ...operationIdentityShape,
166
162
  environment: ForkedEnvironmentRefSchema,
167
163
  }),
168
164
  z.strictObject({
169
165
  status: z.literal("conflict"),
170
- ...forkOperationBase,
166
+ ...operationIdentityShape,
171
167
  existingRequestDigest: sha256DigestSchema,
172
168
  }),
173
169
  z.strictObject({
174
170
  status: z.literal("unknown"),
175
- ...forkOperationBase,
171
+ ...operationIdentityShape,
176
172
  message: boundedStringSchema.min(1),
177
173
  retryable: z.boolean(),
178
174
  }),
179
- ]).superRefine((result, refinement) => {
175
+ ]).superRefine((result, ctx) => {
180
176
  if ((result.status === "created" || result.status === "replayed") &&
181
- (result.environment.idempotencyKey !== result.idempotencyKey ||
182
- result.environment.requestDigest !== result.requestDigest)) {
183
- refinement.addIssue({
177
+ !operationResourceIdentityMatches(result, result.environment)) {
178
+ ctx.addIssue({
184
179
  code: "custom",
185
180
  path: ["environment"],
186
181
  message: "forked environment identity must match its operation",
187
182
  });
188
183
  }
189
- if (result.status === "conflict" &&
190
- result.existingRequestDigest === result.requestDigest) {
191
- refinement.addIssue({
192
- code: "custom",
193
- path: ["existingRequestDigest"],
194
- message: "a conflict must identify a different existing request",
195
- });
196
- }
184
+ refuseSelfConflict(result, ctx);
197
185
  });
198
186
  export const WorkspaceForkLookupResultSchema = z.discriminatedUnion("status", [
199
187
  z.strictObject({
200
188
  status: z.literal("found"),
201
- ...forkOperationBase,
189
+ ...operationIdentityShape,
202
190
  environment: ForkedEnvironmentRefSchema,
203
191
  }),
204
- z.strictObject({ status: z.literal("not_found"), ...forkOperationBase }),
192
+ z.strictObject({ status: z.literal("not_found"), ...operationIdentityShape }),
205
193
  z.strictObject({
206
194
  status: z.literal("conflict"),
207
- ...forkOperationBase,
195
+ ...operationIdentityShape,
208
196
  existingRequestDigest: sha256DigestSchema,
209
197
  }),
210
198
  z.strictObject({
211
199
  status: z.literal("unknown"),
212
- ...forkOperationBase,
200
+ ...operationIdentityShape,
213
201
  message: boundedStringSchema.min(1),
214
202
  retryable: z.boolean(),
215
203
  }),
216
- ]).superRefine((result, refinement) => {
204
+ ]).superRefine((result, ctx) => {
217
205
  if (result.status === "found" &&
218
- (result.environment.idempotencyKey !== result.idempotencyKey ||
219
- result.environment.requestDigest !== result.requestDigest)) {
220
- refinement.addIssue({
206
+ !operationResourceIdentityMatches(result, result.environment)) {
207
+ ctx.addIssue({
221
208
  code: "custom",
222
209
  path: ["environment"],
223
210
  message: "forked environment identity must match its lookup operation",
224
211
  });
225
212
  }
226
- if (result.status === "conflict" &&
227
- result.existingRequestDigest === result.requestDigest) {
228
- refinement.addIssue({
229
- code: "custom",
230
- path: ["existingRequestDigest"],
231
- message: "a conflict must identify a different existing request",
232
- });
233
- }
213
+ refuseSelfConflict(result, ctx);
234
214
  });
235
215
  export function workspaceForkResultMatchesRequest(request, result) {
236
216
  const parsedRequest = WorkspaceForkRequestSchema.safeParse(request);
@@ -251,10 +231,8 @@ export function workspaceForkResultMatchesRequest(request, result) {
251
231
  }
252
232
  function forkResultMatchesParsed(request, result) {
253
233
  const source = request.checkpoint.source;
254
- return (result.idempotencyKey === request.idempotencyKey &&
255
- result.requestDigest === request.requestDigest &&
256
- result.environment.idempotencyKey === request.idempotencyKey &&
257
- result.environment.requestDigest === request.requestDigest &&
234
+ return (operationResourceIdentityMatches(request, result) &&
235
+ operationResourceIdentityMatches(request, result.environment) &&
258
236
  result.environment.sourceCheckpointId === request.checkpoint.checkpointId &&
259
237
  result.environment.provider === request.checkpoint.provider &&
260
238
  result.environment.sourceEnvironmentId === source.environmentId &&
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-interface",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "license": "MIT",