@tangle-network/agent-interface 0.26.1 → 0.27.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.
@@ -0,0 +1,340 @@
1
+ /**
2
+ * Backward-compatibility surface for the candidate outcome and receipt exports
3
+ * that were renamed or collapsed when the candidate contract was unified.
4
+ *
5
+ * Every symbol here is deprecated and exists only so published consumers that
6
+ * still import the pre-unification names keep resolving at ESM import time.
7
+ * Prefer the current names; these will be removed in a future major.
8
+ *
9
+ * Two shapes of compat live in this module:
10
+ * - Pure renames (shape-identical to a current export) are re-exported as
11
+ * deprecated aliases and remain reference-equal to their new counterpart.
12
+ * - Symbols whose shape changed (a different `schemaVersion` literal, a
13
+ * restructured field, or a union that no longer exists) are re-declared here
14
+ * verbatim from the last release that published them, so old data still
15
+ * parses against the old name instead of silently binding to a newer shape.
16
+ */
17
+ import { z } from "zod";
18
+ import { agentCandidateModelSettlementCallSchema, agentCandidateModelSettlementEvidenceSchema, agentCandidateModelSettlementMaterialSchema, agentCandidateBenchmarkResultEvidenceSchema, agentCandidateFixedSpendSchema, agentCandidateTaskOutcomeEvidenceSchema, } from "./agent-candidate-outcome-schema.js";
19
+ import { agentCandidateResolvedModelSchema } from "./agent-candidate-execution-plan-schema.js";
20
+ import { agentCandidateSpendSchema } from "./agent-candidate-lineage-schema.js";
21
+ import { agentCandidateMemoryReceiptSchema, agentCandidateTerminationSchema, agentCandidateTraceEvidenceSchema, } from "./agent-candidate-receipt-schema.js";
22
+ import { isCanonicalJsonValue, sha256DigestSchema, } from "./agent-candidate-schema-common.js";
23
+ /**
24
+ * @deprecated Renamed to {@link agentCandidateModelSettlementCallSchema}.
25
+ * Reference-equal to the current export.
26
+ */
27
+ export const agentCandidateModelSettlementCallV2Schema = agentCandidateModelSettlementCallSchema;
28
+ /**
29
+ * @deprecated Renamed to {@link agentCandidateModelSettlementMaterialSchema}.
30
+ * Reference-equal to the current export.
31
+ */
32
+ export const agentCandidateModelSettlementMaterialV2Schema = agentCandidateModelSettlementMaterialSchema;
33
+ // -----------------------------------------------------------------------------
34
+ // Re-added schemas.
35
+ // -----------------------------------------------------------------------------
36
+ const safeCountSchema = z
37
+ .number()
38
+ .int()
39
+ .nonnegative()
40
+ .refine(Number.isSafeInteger, "value must be a nonnegative safe integer");
41
+ const boundedIdentifierSchema = z.string().min(1).max(256);
42
+ /**
43
+ * @deprecated The run receipt no longer carries a standalone model-usage block.
44
+ * Restored `resolved`/`usage` schema for consumers of the V1/V2 run receipt.
45
+ */
46
+ export const agentCandidateModelUsageSchema = z
47
+ .object({
48
+ resolved: agentCandidateResolvedModelSchema,
49
+ usage: agentCandidateSpendSchema,
50
+ })
51
+ .strict();
52
+ /**
53
+ * Restored per-call aggregate and canonical-value checks shared by the V1
54
+ * settlement material. Mirrors the current internal refinement but tolerates
55
+ * the pre-router call shape that omits `generationId`.
56
+ */
57
+ function refineLegacyModelSettlementMaterial(material, ctx) {
58
+ const callIds = new Set();
59
+ const traceSpanIds = new Set();
60
+ const totals = {
61
+ inputTokens: 0,
62
+ outputTokens: 0,
63
+ cachedInputTokens: 0,
64
+ reasoningTokens: 0,
65
+ modelCalls: material.calls.length,
66
+ costUsdNanos: 0,
67
+ };
68
+ for (const [index, call] of material.calls.entries()) {
69
+ if (callIds.has(call.callId)) {
70
+ ctx.addIssue({
71
+ code: "custom",
72
+ path: ["calls", index, "callId"],
73
+ message: "model settlement call ids must be unique",
74
+ });
75
+ }
76
+ callIds.add(call.callId);
77
+ if (traceSpanIds.has(call.traceSpanId)) {
78
+ ctx.addIssue({
79
+ code: "custom",
80
+ path: ["calls", index, "traceSpanId"],
81
+ message: "model settlement trace span ids must be unique",
82
+ });
83
+ }
84
+ traceSpanIds.add(call.traceSpanId);
85
+ if (call.model !== material.resolved.model) {
86
+ ctx.addIssue({
87
+ code: "custom",
88
+ path: ["calls", index, "model"],
89
+ message: "settled call model must match the resolved single model",
90
+ });
91
+ }
92
+ for (const field of [
93
+ "inputTokens",
94
+ "outputTokens",
95
+ "cachedInputTokens",
96
+ "reasoningTokens",
97
+ "costUsdNanos",
98
+ ]) {
99
+ const sum = totals[field] + call[field];
100
+ if (!Number.isSafeInteger(sum)) {
101
+ ctx.addIssue({
102
+ code: "custom",
103
+ path: ["calls", index, field],
104
+ message: `model settlement ${field} total exceeds safe integer range`,
105
+ });
106
+ }
107
+ else {
108
+ totals[field] = sum;
109
+ }
110
+ }
111
+ }
112
+ if (!sameFixedSpend(totals, material.usage)) {
113
+ ctx.addIssue({
114
+ code: "custom",
115
+ path: ["usage"],
116
+ message: "model settlement usage must equal the exact per-call aggregate",
117
+ });
118
+ }
119
+ if (!isCanonicalJsonValue(material)) {
120
+ ctx.addIssue({
121
+ code: "custom",
122
+ message: "model settlement material must contain only RFC 8785 JSON values",
123
+ });
124
+ }
125
+ }
126
+ const legacyModelSettlementCallV1Schema = z
127
+ .object({
128
+ callId: boundedIdentifierSchema,
129
+ traceSpanId: boundedIdentifierSchema,
130
+ model: boundedIdentifierSchema,
131
+ inputTokens: safeCountSchema,
132
+ outputTokens: safeCountSchema,
133
+ cachedInputTokens: safeCountSchema,
134
+ reasoningTokens: safeCountSchema,
135
+ costUsdNanos: safeCountSchema,
136
+ })
137
+ .strict();
138
+ /**
139
+ * @deprecated The settlement material collapsed to
140
+ * {@link agentCandidateModelSettlementMaterialSchema} (`schemaVersion: 2`).
141
+ * Restored `schemaVersion: 1` parser for the pre-router base call shape.
142
+ */
143
+ export const agentCandidateModelSettlementMaterialV1Schema = z
144
+ .object({
145
+ schemaVersion: z.literal(1),
146
+ kind: z.literal("agent-candidate-model-settlement-material"),
147
+ executionPlanDigest: sha256DigestSchema,
148
+ preparationId: boundedIdentifierSchema,
149
+ grantDigest: sha256DigestSchema,
150
+ closed: z.literal(true),
151
+ resolved: agentCandidateResolvedModelSchema,
152
+ usage: agentCandidateFixedSpendSchema,
153
+ calls: z.array(legacyModelSettlementCallV1Schema),
154
+ })
155
+ .strict()
156
+ .superRefine(refineLegacyModelSettlementMaterial);
157
+ /**
158
+ * @deprecated The run receipt collapsed to
159
+ * {@link agentCandidateRunReceiptSchema} (`schemaVersion: 3`). Restored
160
+ * `schemaVersion: 1` parser with aggregate usage accounting.
161
+ */
162
+ export const agentCandidateRunReceiptV1Schema = z
163
+ .object({
164
+ schemaVersion: z.literal(1),
165
+ kind: z.literal("agent-candidate-run"),
166
+ digestAlgorithm: z.literal("rfc8785-sha256"),
167
+ bundleDigest: sha256DigestSchema,
168
+ materializationReceiptDigest: sha256DigestSchema,
169
+ executionPlanDigest: sha256DigestSchema,
170
+ memory: agentCandidateMemoryReceiptSchema,
171
+ usage: agentCandidateSpendSchema,
172
+ modelUsage: agentCandidateModelUsageSchema,
173
+ trace: agentCandidateTraceEvidenceSchema,
174
+ termination: agentCandidateTerminationSchema,
175
+ digest: sha256DigestSchema,
176
+ })
177
+ .strict()
178
+ .superRefine((receipt, ctx) => {
179
+ const usageMatchesModel = receipt.usage.costUsd === receipt.modelUsage.usage.costUsd &&
180
+ receipt.usage.inputTokens === receipt.modelUsage.usage.inputTokens &&
181
+ receipt.usage.outputTokens === receipt.modelUsage.usage.outputTokens &&
182
+ receipt.usage.cachedInputTokens ===
183
+ receipt.modelUsage.usage.cachedInputTokens &&
184
+ receipt.usage.modelCalls === receipt.modelUsage.usage.modelCalls;
185
+ if (!usageMatchesModel) {
186
+ ctx.addIssue({
187
+ code: "custom",
188
+ path: ["modelUsage", "usage"],
189
+ message: "single-model usage must equal aggregate protected usage",
190
+ });
191
+ }
192
+ if (receipt.trace.modelCallCount !== receipt.modelUsage.usage.modelCalls) {
193
+ ctx.addIssue({
194
+ code: "custom",
195
+ path: ["trace", "modelCallCount"],
196
+ message: "trace model-call count must match protected single-model usage",
197
+ });
198
+ }
199
+ if (!isCanonicalJsonValue(receipt)) {
200
+ ctx.addIssue({
201
+ code: "custom",
202
+ message: "run receipt must contain only RFC 8785 JSON values",
203
+ });
204
+ }
205
+ });
206
+ function legacyUsageMatchesFixed(legacy, fixed) {
207
+ return (legacy.costUsd === fixed.costUsdNanos / 1_000_000_000 &&
208
+ legacy.inputTokens === fixed.inputTokens &&
209
+ legacy.outputTokens === fixed.outputTokens &&
210
+ (legacy.cachedInputTokens ?? 0) === fixed.cachedInputTokens &&
211
+ legacy.modelCalls === fixed.modelCalls);
212
+ }
213
+ /**
214
+ * @deprecated The run receipt collapsed to
215
+ * {@link agentCandidateRunReceiptSchema} (`schemaVersion: 3`). Restored
216
+ * `schemaVersion: 2` parser. Its evidence members bind the current settlement,
217
+ * task-outcome, and benchmark-result schemas.
218
+ */
219
+ export const agentCandidateRunReceiptV2Schema = z
220
+ .object({
221
+ schemaVersion: z.literal(2),
222
+ kind: z.literal("agent-candidate-run"),
223
+ digestAlgorithm: z.literal("rfc8785-sha256"),
224
+ bundleDigest: sha256DigestSchema,
225
+ materializationReceiptDigest: sha256DigestSchema,
226
+ executionPlanDigest: sha256DigestSchema,
227
+ memory: agentCandidateMemoryReceiptSchema,
228
+ usage: agentCandidateSpendSchema,
229
+ modelUsage: agentCandidateModelUsageSchema,
230
+ trace: agentCandidateTraceEvidenceSchema,
231
+ termination: agentCandidateTerminationSchema,
232
+ fixedUsage: agentCandidateFixedSpendSchema,
233
+ modelSettlement: agentCandidateModelSettlementEvidenceSchema,
234
+ taskOutcome: agentCandidateTaskOutcomeEvidenceSchema,
235
+ benchmarkResult: agentCandidateBenchmarkResultEvidenceSchema,
236
+ digest: sha256DigestSchema,
237
+ })
238
+ .strict()
239
+ .superRefine((receipt, ctx) => {
240
+ const legacyUsageMatchesModel = receipt.usage.costUsd === receipt.modelUsage.usage.costUsd &&
241
+ receipt.usage.inputTokens === receipt.modelUsage.usage.inputTokens &&
242
+ receipt.usage.outputTokens === receipt.modelUsage.usage.outputTokens &&
243
+ receipt.usage.cachedInputTokens ===
244
+ receipt.modelUsage.usage.cachedInputTokens &&
245
+ receipt.usage.modelCalls === receipt.modelUsage.usage.modelCalls;
246
+ if (!legacyUsageMatchesModel) {
247
+ ctx.addIssue({
248
+ code: "custom",
249
+ path: ["modelUsage", "usage"],
250
+ message: "single-model usage must equal aggregate protected usage",
251
+ });
252
+ }
253
+ if (!legacyUsageMatchesFixed(receipt.usage, receipt.fixedUsage)) {
254
+ ctx.addIssue({
255
+ code: "custom",
256
+ path: ["fixedUsage"],
257
+ message: "fixed usage must exactly preserve the legacy usage totals",
258
+ });
259
+ }
260
+ if (!sameFixedSpend(receipt.fixedUsage, receipt.modelSettlement.material.usage)) {
261
+ ctx.addIssue({
262
+ code: "custom",
263
+ path: ["modelSettlement", "material", "usage"],
264
+ message: "model settlement aggregate must equal fixed run usage",
265
+ });
266
+ }
267
+ if (JSON.stringify(receipt.modelUsage.resolved) !==
268
+ JSON.stringify(receipt.modelSettlement.material.resolved)) {
269
+ ctx.addIssue({
270
+ code: "custom",
271
+ path: ["modelSettlement", "material", "resolved"],
272
+ message: "model settlement must bind the run's resolved model",
273
+ });
274
+ }
275
+ if (receipt.modelSettlement.material.executionPlanDigest !==
276
+ receipt.executionPlanDigest) {
277
+ ctx.addIssue({
278
+ code: "custom",
279
+ path: ["modelSettlement", "material", "executionPlanDigest"],
280
+ message: "model settlement must bind the executed plan",
281
+ });
282
+ }
283
+ if (receipt.trace.modelCallCount !== receipt.fixedUsage.modelCalls) {
284
+ ctx.addIssue({
285
+ code: "custom",
286
+ path: ["trace", "modelCallCount"],
287
+ message: "trace model-call count must match fixed run usage",
288
+ });
289
+ }
290
+ if (receipt.taskOutcome.material.executionPlanDigest !==
291
+ receipt.executionPlanDigest) {
292
+ ctx.addIssue({
293
+ code: "custom",
294
+ path: ["taskOutcome", "material", "executionPlanDigest"],
295
+ message: "task outcome must bind the executed plan",
296
+ });
297
+ }
298
+ if (receipt.benchmarkResult.material.executionPlanDigest !==
299
+ receipt.executionPlanDigest) {
300
+ ctx.addIssue({
301
+ code: "custom",
302
+ path: ["benchmarkResult", "material", "executionPlanDigest"],
303
+ message: "benchmark result must bind the executed plan",
304
+ });
305
+ }
306
+ if (receipt.benchmarkResult.material.taskOutcomeDigest !==
307
+ receipt.taskOutcome.digest) {
308
+ ctx.addIssue({
309
+ code: "custom",
310
+ path: ["benchmarkResult", "material", "taskOutcomeDigest"],
311
+ message: "benchmark result must bind the exact task outcome",
312
+ });
313
+ }
314
+ if (!isCanonicalJsonValue(receipt)) {
315
+ ctx.addIssue({
316
+ code: "custom",
317
+ message: "run receipt must contain only RFC 8785 JSON values",
318
+ });
319
+ }
320
+ });
321
+ /**
322
+ * @deprecated The run receipt generations collapsed into a single current
323
+ * schema. Restored union parser for consumers that accepted both generations.
324
+ */
325
+ export const agentCandidateRunReceiptAnyVersionSchema = z.union([
326
+ agentCandidateRunReceiptV1Schema,
327
+ agentCandidateRunReceiptV2Schema,
328
+ ]);
329
+ /**
330
+ * @deprecated No longer exported from the outcome module. Restored spend-equality
331
+ * helper for consumers that compared fixed-point usage totals.
332
+ */
333
+ export function sameFixedSpend(left, right) {
334
+ return (left.inputTokens === right.inputTokens &&
335
+ left.outputTokens === right.outputTokens &&
336
+ left.cachedInputTokens === right.cachedInputTokens &&
337
+ left.reasoningTokens === right.reasoningTokens &&
338
+ left.modelCalls === right.modelCalls &&
339
+ left.costUsdNanos === right.costUsdNanos);
340
+ }
package/dist/index.d.ts CHANGED
@@ -5,7 +5,9 @@
5
5
  * This package defines the contract between the sidecar and provider implementations.
6
6
  */
7
7
  import type { InteractionRequest, InteractionResponse } from "./interaction.js";
8
+ import type { AgentExecutionOutcome, DurablePlan, PlanContinuation, SdkPlanHost } from "./plan.js";
8
9
  export type * from "./environment-provider.js";
10
+ export * from "./plan.js";
9
11
  export type BackendCapabilities = {
10
12
  streaming: boolean;
11
13
  toolUse: boolean;
@@ -218,6 +220,11 @@ export type StreamEvent = MessagePartUpdatedEvent | {
218
220
  type: "interaction.cancel";
219
221
  id: string;
220
222
  reason?: string;
223
+ }
224
+ /** A durable plan was committed. This event is observational, not a live ask. */
225
+ | {
226
+ type: "plan.submitted";
227
+ plan: DurablePlan;
221
228
  };
222
229
  export type ToolInvocation = {
223
230
  toolName: string;
@@ -267,8 +274,11 @@ export type AgentExecutionInput = {
267
274
  * client-initiated retries of the same intent.
268
275
  */
269
276
  turnId?: string;
277
+ /** Server-owned continuation of a previously committed plan decision. */
278
+ planContinuation?: PlanContinuation;
270
279
  };
271
280
  export type AgentExecutionResult = {
281
+ outcome: AgentExecutionOutcome;
272
282
  text: string;
273
283
  toolInvocations: ToolInvocation[];
274
284
  reasoning?: string[];
@@ -491,6 +501,7 @@ export interface SdkTraceContext {
491
501
  export type SdkHostServices = {
492
502
  memoryHost: SdkMemoryHost;
493
503
  toolHost: SdkToolHost;
504
+ planHost: SdkPlanHost;
494
505
  recorder: SdkRecorder;
495
506
  providerConfig: ProviderConfig;
496
507
  traceContext?: SdkTraceContext;
@@ -603,6 +614,7 @@ export * from "./interaction.js";
603
614
  export * from "./agent-candidate.js";
604
615
  export * from "./agent-candidate-schema.js";
605
616
  export * from "./agent-candidate-promotion-schema.js";
617
+ export * from "./agent-candidate-compat.js";
606
618
  export * from "./agent-profile.js";
607
619
  export * from "./profile-diff.js";
608
620
  export * from "./harness.js";
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@
4
4
  * Shared types and interfaces for SDK provider adapters.
5
5
  * This package defines the contract between the sidecar and provider implementations.
6
6
  */
7
+ export * from "./plan.js";
7
8
  /** Helper to check part type */
8
9
  export function isTextPart(part) {
9
10
  return part.type === "text";
@@ -126,6 +127,7 @@ export * from "./interaction.js";
126
127
  export * from "./agent-candidate.js";
127
128
  export * from "./agent-candidate-schema.js";
128
129
  export * from "./agent-candidate-promotion-schema.js";
130
+ export * from "./agent-candidate-compat.js";
129
131
  export * from "./agent-profile.js";
130
132
  export * from "./profile-diff.js";
131
133
  export * from "./harness.js";
package/dist/plan.d.ts ADDED
@@ -0,0 +1,126 @@
1
+ import { z } from "zod";
2
+ /** Harnesses with an enforceable durable-plan continuation contract. */
3
+ export declare const PlanProviderKindSchema: z.ZodEnum<{
4
+ "claude-code": "claude-code";
5
+ codex: "codex";
6
+ opencode: "opencode";
7
+ }>;
8
+ export type PlanProviderKind = z.infer<typeof PlanProviderKindSchema>;
9
+ /**
10
+ * Credential-free provider correlation committed with the plan. This union is
11
+ * deliberately closed: adding plan support for another harness requires an
12
+ * explicit resume contract instead of smuggling opaque metadata through.
13
+ */
14
+ export declare const PlanProviderStateSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
15
+ kind: z.ZodLiteral<"claude-code">;
16
+ version: z.ZodLiteral<1>;
17
+ nativeSessionId: z.ZodString;
18
+ }, z.core.$strict>, z.ZodObject<{
19
+ kind: z.ZodLiteral<"codex">;
20
+ version: z.ZodLiteral<1>;
21
+ threadId: z.ZodString;
22
+ }, z.core.$strict>, z.ZodObject<{
23
+ kind: z.ZodLiteral<"opencode">;
24
+ version: z.ZodLiteral<1>;
25
+ }, z.core.$strict>], "kind">;
26
+ export type PlanProviderState = z.infer<typeof PlanProviderStateSchema>;
27
+ /**
28
+ * A plan committed by the runtime before the planning turn is allowed to end.
29
+ * The full body is part of the contract so consumers never reconstruct it from
30
+ * rendered chat text.
31
+ */
32
+ export declare const DurablePlanSchema: z.ZodObject<{
33
+ id: z.ZodString;
34
+ revision: z.ZodNumber;
35
+ title: z.ZodOptional<z.ZodString>;
36
+ body: z.ZodString;
37
+ submittedAt: z.ZodString;
38
+ }, z.core.$strict>;
39
+ export type DurablePlan = z.infer<typeof DurablePlanSchema>;
40
+ /** Provider-originated data required to commit a plan exactly once. */
41
+ export declare const PlanSubmissionSchema: z.ZodObject<{
42
+ title: z.ZodOptional<z.ZodString>;
43
+ body: z.ZodString;
44
+ sourceToolCallId: z.ZodString;
45
+ providerState: z.ZodDiscriminatedUnion<[z.ZodObject<{
46
+ kind: z.ZodLiteral<"claude-code">;
47
+ version: z.ZodLiteral<1>;
48
+ nativeSessionId: z.ZodString;
49
+ }, z.core.$strict>, z.ZodObject<{
50
+ kind: z.ZodLiteral<"codex">;
51
+ version: z.ZodLiteral<1>;
52
+ threadId: z.ZodString;
53
+ }, z.core.$strict>, z.ZodObject<{
54
+ kind: z.ZodLiteral<"opencode">;
55
+ version: z.ZodLiteral<1>;
56
+ }, z.core.$strict>], "kind">;
57
+ }, z.core.$strict>;
58
+ export type PlanSubmission = z.infer<typeof PlanSubmissionSchema>;
59
+ /** The human verdict that starts the next turn. */
60
+ export declare const PlanDecisionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
61
+ outcome: z.ZodLiteral<"approved">;
62
+ }, z.core.$strict>, z.ZodObject<{
63
+ outcome: z.ZodLiteral<"rejected">;
64
+ feedback: z.ZodString;
65
+ }, z.core.$strict>], "outcome">;
66
+ export type PlanDecision = z.infer<typeof PlanDecisionSchema>;
67
+ /**
68
+ * Typed server-originated continuation. Adapters consume this instead of
69
+ * inferring approval from a free-form user message.
70
+ */
71
+ export declare const PlanContinuationSchema: z.ZodObject<{
72
+ version: z.ZodLiteral<1>;
73
+ plan: z.ZodObject<{
74
+ id: z.ZodString;
75
+ revision: z.ZodNumber;
76
+ title: z.ZodOptional<z.ZodString>;
77
+ body: z.ZodString;
78
+ submittedAt: z.ZodString;
79
+ }, z.core.$strict>;
80
+ sourceToolCallId: z.ZodString;
81
+ decision: z.ZodDiscriminatedUnion<[z.ZodObject<{
82
+ outcome: z.ZodLiteral<"approved">;
83
+ }, z.core.$strict>, z.ZodObject<{
84
+ outcome: z.ZodLiteral<"rejected">;
85
+ feedback: z.ZodString;
86
+ }, z.core.$strict>], "outcome">;
87
+ providerState: z.ZodDiscriminatedUnion<[z.ZodObject<{
88
+ kind: z.ZodLiteral<"claude-code">;
89
+ version: z.ZodLiteral<1>;
90
+ nativeSessionId: z.ZodString;
91
+ }, z.core.$strict>, z.ZodObject<{
92
+ kind: z.ZodLiteral<"codex">;
93
+ version: z.ZodLiteral<1>;
94
+ threadId: z.ZodString;
95
+ }, z.core.$strict>, z.ZodObject<{
96
+ kind: z.ZodLiteral<"opencode">;
97
+ version: z.ZodLiteral<1>;
98
+ }, z.core.$strict>], "kind">;
99
+ }, z.core.$strict>;
100
+ export type PlanContinuation = z.infer<typeof PlanContinuationSchema>;
101
+ /** Terminal meaning of a successfully completed adapter invocation. */
102
+ export declare const AgentExecutionOutcomeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
103
+ type: z.ZodLiteral<"completed">;
104
+ }, z.core.$strict>, z.ZodObject<{
105
+ type: z.ZodLiteral<"awaiting_plan_decision">;
106
+ plan: z.ZodObject<{
107
+ id: z.ZodString;
108
+ revision: z.ZodNumber;
109
+ title: z.ZodOptional<z.ZodString>;
110
+ body: z.ZodString;
111
+ submittedAt: z.ZodString;
112
+ }, z.core.$strict>;
113
+ }, z.core.$strict>], "type">;
114
+ export type AgentExecutionOutcome = z.infer<typeof AgentExecutionOutcomeSchema>;
115
+ /**
116
+ * Per-turn host command used by providers to commit a plan. Implementations
117
+ * bind project, host-session, and turn identity outside provider-controlled
118
+ * input and must resolve only after the durable write commits.
119
+ */
120
+ export interface SdkPlanHost {
121
+ submit(submission: PlanSubmission): Promise<DurablePlan>;
122
+ /** Publish a committed plan only after the provider turn has terminalized. */
123
+ confirm(planId: string): Promise<void>;
124
+ /** Compensate a committed plan when provider terminalization cannot be proven. */
125
+ withdraw(planId: string, reason: string): Promise<void>;
126
+ }
package/dist/plan.js ADDED
@@ -0,0 +1,77 @@
1
+ import { z } from "zod";
2
+ /** Harnesses with an enforceable durable-plan continuation contract. */
3
+ export const PlanProviderKindSchema = z.enum([
4
+ "claude-code",
5
+ "codex",
6
+ "opencode",
7
+ ]);
8
+ /**
9
+ * Credential-free provider correlation committed with the plan. This union is
10
+ * deliberately closed: adding plan support for another harness requires an
11
+ * explicit resume contract instead of smuggling opaque metadata through.
12
+ */
13
+ export const PlanProviderStateSchema = z.discriminatedUnion("kind", [
14
+ z.object({
15
+ kind: z.literal("claude-code"),
16
+ version: z.literal(1),
17
+ nativeSessionId: z.string().min(1),
18
+ }).strict(),
19
+ z.object({
20
+ kind: z.literal("codex"),
21
+ version: z.literal(1),
22
+ threadId: z.string().min(1),
23
+ }).strict(),
24
+ z.object({
25
+ kind: z.literal("opencode"),
26
+ version: z.literal(1),
27
+ }).strict(),
28
+ ]);
29
+ /**
30
+ * A plan committed by the runtime before the planning turn is allowed to end.
31
+ * The full body is part of the contract so consumers never reconstruct it from
32
+ * rendered chat text.
33
+ */
34
+ export const DurablePlanSchema = z.object({
35
+ id: z.string().min(1),
36
+ revision: z.number().int().positive(),
37
+ title: z.string().trim().min(1).optional(),
38
+ body: z.string().trim().min(1),
39
+ submittedAt: z.string().datetime({ offset: true }),
40
+ }).strict();
41
+ /** Provider-originated data required to commit a plan exactly once. */
42
+ export const PlanSubmissionSchema = z.object({
43
+ title: z.string().trim().min(1).optional(),
44
+ body: z.string().trim().min(1),
45
+ /** Stable provider tool-call identity reused when a lost acknowledgement retries. */
46
+ sourceToolCallId: z.string().min(1),
47
+ /** Credential-free correlation required to resume the deferred turn. */
48
+ providerState: PlanProviderStateSchema,
49
+ }).strict();
50
+ /** The human verdict that starts the next turn. */
51
+ export const PlanDecisionSchema = z.discriminatedUnion("outcome", [
52
+ z.object({ outcome: z.literal("approved") }).strict(),
53
+ z.object({
54
+ outcome: z.literal("rejected"),
55
+ feedback: z.string().trim().min(1),
56
+ }).strict(),
57
+ ]);
58
+ /**
59
+ * Typed server-originated continuation. Adapters consume this instead of
60
+ * inferring approval from a free-form user message.
61
+ */
62
+ export const PlanContinuationSchema = z.object({
63
+ version: z.literal(1),
64
+ plan: DurablePlanSchema,
65
+ sourceToolCallId: z.string().min(1),
66
+ decision: PlanDecisionSchema,
67
+ /** Exact payload previously committed with the plan submission. */
68
+ providerState: PlanProviderStateSchema,
69
+ }).strict();
70
+ /** Terminal meaning of a successfully completed adapter invocation. */
71
+ export const AgentExecutionOutcomeSchema = z.discriminatedUnion("type", [
72
+ z.object({ type: z.literal("completed") }).strict(),
73
+ z.object({
74
+ type: z.literal("awaiting_plan_decision"),
75
+ plan: DurablePlanSchema,
76
+ }).strict(),
77
+ ]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-interface",
3
- "version": "0.26.1",
3
+ "version": "0.27.1",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.js",