stitchkit 0.85.0 → 0.85.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.
package/CHANGELOG.md CHANGED
@@ -15,6 +15,21 @@ additive**; the first breaking change landed in 0.10.0. Grep the file for
15
15
 
16
16
  ## [Unreleased]
17
17
 
18
+ ## [0.85.1] — 2026-09-07
19
+
20
+ ### Fixed
21
+
22
+ - **A later model step cannot outrun its durable lifecycle or the previous
23
+ structural checkpoint.** AI SDK lifecycle callbacks deliberately suppress
24
+ callback failures, so the 0.85.0 request-start write could lose a revision
25
+ race with stream persistence while the provider request still executed. The
26
+ runtime now uses the callback only for concrete-model call identity; awaited
27
+ step preparation and model middleware record request admission before `doStream`, and every step
28
+ after the first waits for the prior step's structural checkpoint. A rejected
29
+ lifecycle write therefore prevents the provider call instead of producing an
30
+ unrecorded inference. Deferred-store and failed-admission tests cover both
31
+ sides of the guarantee. → ADR 0174 (amended).
32
+
18
33
  ## [0.85.0] — 2026-09-07
19
34
 
20
35
  ### ⚠️ Breaking changes
@@ -1 +1 @@
1
- {"version":3,"file":"run-execution.d.ts","sourceRoot":"","sources":["../../src/agent-runtime/run-execution.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,OAAO,EACb,MAAM,IAAI,CAAC;AAMZ,OAAO,EAAE,KAAK,iBAAiB,EAAuB,MAAM,UAAU,CAAC;AAEvE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAI1D,OAAO,KAAK,EAAqB,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAavE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAKL,KAAK,QAAQ,EAOd,MAAM,WAAW,CAAC;AAuCnB,8DAA8D;AAC9D,MAAM,WAAW,uBAAuB,CAAC,OAAO,EAAE,KAAK,SAAS,OAAO;IACrE,MAAM,EAAE,kBAAkB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC3C,OAAO,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,IAAI,MAAM,CAAC;IACrB,GAAG,IAAI,IAAI,CAAC;IACZ,qBAAqB,EAAE,MAAM,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,sBAAsB,CAAC;CACpC;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,KAAK,SAAS,OAAO,EAC9D,YAAY,EAAE,uBAAuB,CAAC,OAAO,EAAE,KAAK,CAAC,WAcb;IACtC,WAAW,EAAE,QAAQ,CAAC;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,WAAW,CAAC;IACpB,gFAAgF;IAChF,GAAG,EAAE,MAAM,CAAC;IACZ,gFAAgF;IAChF,UAAU,CAAC,IAAI,IAAI,CAAC;CACrB,KAAG,OAAO,CAAC,kBAAkB,CAAC,CAuhChC"}
1
+ {"version":3,"file":"run-execution.d.ts","sourceRoot":"","sources":["../../src/agent-runtime/run-execution.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,OAAO,EACb,MAAM,IAAI,CAAC;AAMZ,OAAO,EAAE,KAAK,iBAAiB,EAAuB,MAAM,UAAU,CAAC;AAEvE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAI1D,OAAO,KAAK,EAAqB,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAavE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAKL,KAAK,QAAQ,EAOd,MAAM,WAAW,CAAC;AAuCnB,8DAA8D;AAC9D,MAAM,WAAW,uBAAuB,CAAC,OAAO,EAAE,KAAK,SAAS,OAAO;IACrE,MAAM,EAAE,kBAAkB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC3C,OAAO,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,IAAI,MAAM,CAAC;IACrB,GAAG,IAAI,IAAI,CAAC;IACZ,qBAAqB,EAAE,MAAM,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,sBAAsB,CAAC;CACpC;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,KAAK,SAAS,OAAO,EAC9D,YAAY,EAAE,uBAAuB,CAAC,OAAO,EAAE,KAAK,CAAC,WAcb;IACtC,WAAW,EAAE,QAAQ,CAAC;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,WAAW,CAAC;IACpB,gFAAgF;IAChF,GAAG,EAAE,MAAM,CAAC;IACZ,gFAAgF;IAChF,UAAU,CAAC,IAAI,IAAI,CAAC;CACrB,KAAG,OAAO,CAAC,kBAAkB,CAAC,CAoiChC"}
@@ -1,3 +1,4 @@
1
+ import { type LanguageModel } from 'ai';
1
2
  import type { AgentRuntimeEvent } from './event-schema.js';
2
3
  import { type AgentRun, type AgentSnapshot } from './schemas.js';
3
4
  import type { AgentRuntimeStore } from './store.js';
@@ -12,8 +13,11 @@ export interface AgentRunOperationLifecycleConfig {
12
13
  }
13
14
  /** One durable latest-operation state machine for an executing run. */
14
15
  export declare function createAgentRunOperationLifecycle(config: AgentRunOperationLifecycleConfig): {
16
+ noteProviderCall(callId: string): void;
17
+ prepareModel(model: LanguageModel, step: number, fallbackCallId: string): Promise<LanguageModel>;
18
+ checkpointStep(step: number): void;
19
+ failStepCheckpoints(error: unknown): void;
15
20
  startCompaction(operationId: string): Promise<void>;
16
- startModelRequest(providerCallId: string, step: number): Promise<void>;
17
21
  firstOutput(): Promise<void>;
18
22
  finish(phase: TerminalOperationPhase): Promise<void>;
19
23
  };
@@ -1 +1 @@
1
- {"version":3,"file":"run-operation-lifecycle.d.ts","sourceRoot":"","sources":["../../src/agent-runtime/run-operation-lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGxD,OAAO,EACL,KAAK,QAAQ,EAGb,KAAK,aAAa,EACnB,MAAM,WAAW,CAAC;AACnB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAGjD,KAAK,sBAAsB,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,CAAC;AAEnE,MAAM,WAAW,gCAAgC;IAC/C,KAAK,EAAE,iBAAiB,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,IAAI,QAAQ,CAAC;IACvB,cAAc,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;IAC9C,OAAO,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,GAAG,IAAI,IAAI,CAAC;CACb;AAED,uEAAuE;AACvE,wBAAgB,gCAAgC,CAAC,MAAM,EAAE,gCAAgC;IA+BrF,eAAe,cAAc,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUnD,iBAAiB,iBAAiB,MAAM,QAAQ,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWtE,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAY5B,MAAM,QAAQ,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC;EAavD"}
1
+ {"version":3,"file":"run-operation-lifecycle.d.ts","sourceRoot":"","sources":["../../src/agent-runtime/run-operation-lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAqB,MAAM,IAAI,CAAC;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGxD,OAAO,EACL,KAAK,QAAQ,EAGb,KAAK,aAAa,EACnB,MAAM,WAAW,CAAC;AACnB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAGjD,KAAK,sBAAsB,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,CAAC;AAEnE,MAAM,WAAW,gCAAgC;IAC/C,KAAK,EAAE,iBAAiB,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,IAAI,QAAQ,CAAC;IACvB,cAAc,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;IAC9C,OAAO,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,GAAG,IAAI,IAAI,CAAC;CACb;AAED,uEAAuE;AACvE,wBAAgB,gCAAgC,CAAC,MAAM,EAAE,gCAAgC;IA4CrF,gBAAgB,SAAS,MAAM,GAAG,IAAI;IAGhC,YAAY,QACT,aAAa,QACd,MAAM,kBACI,MAAM,GACrB,OAAO,CAAC,aAAa,CAAC;IAgCzB,cAAc,OAAO,MAAM,GAAG,IAAI;IAQlC,mBAAmB,QAAQ,OAAO,GAAG,IAAI;IAIzC,eAAe,cAAc,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUnD,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAY5B,MAAM,QAAQ,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC;EAavD"}
@@ -2,7 +2,7 @@ import {
2
2
  composeAgentPrompt,
3
3
  createAgentRuntime,
4
4
  createAgentRuntimeEventSink
5
- } from "./index-9merfjxf.js";
5
+ } from "./index-jpp7jd1p.js";
6
6
  import {
7
7
  AgentModelDescriptorSchema
8
8
  } from "./index-tg3m2ec5.js";
@@ -49,7 +49,7 @@ import {
49
49
  registerDeferredToolRepair,
50
50
  repairedSearchCall,
51
51
  selectAgentHistory
52
- } from "./index-9merfjxf.js";
52
+ } from "./index-jpp7jd1p.js";
53
53
  import {
54
54
  AgentModelCapabilitySchema,
55
55
  AgentModelCatalogEntrySchema,
@@ -934,6 +934,9 @@ function repairedSearchCall(toolCall, searchName) {
934
934
  };
935
935
  }
936
936
 
937
+ // src/agent-runtime/run-operation-lifecycle.ts
938
+ import { wrapLanguageModel } from "ai";
939
+
937
940
  // src/agent-runtime/runtime-internals.ts
938
941
  import { z as z2 } from "zod";
939
942
  function findRun(runs, runId) {
@@ -1078,6 +1081,9 @@ function createIdleDeadline(parent, timeoutMs) {
1078
1081
  // src/agent-runtime/run-operation-lifecycle.ts
1079
1082
  function createAgentRunOperationLifecycle(config) {
1080
1083
  let active;
1084
+ let providerCallId;
1085
+ let checkpointedStep = -1;
1086
+ const checkpointWaiters = new Map;
1081
1087
  const record = async (operation) => {
1082
1088
  const current = config.currentRun();
1083
1089
  const snapshot = appliedSnapshot(await config.store.recordRunOperation({
@@ -1101,7 +1107,57 @@ function createAgentRunOperationLifecycle(config) {
1101
1107
  emittedAt: config.now().toISOString()
1102
1108
  });
1103
1109
  };
1110
+ const startModelRequest = (callId, step) => record(AgentRunOperationSchema.parse({
1111
+ operationId: `${callId}:${step}`,
1112
+ kind: "model-request",
1113
+ phase: "started",
1114
+ step,
1115
+ startedAt: config.now().toISOString()
1116
+ }));
1104
1117
  return {
1118
+ noteProviderCall(callId) {
1119
+ providerCallId = callId;
1120
+ },
1121
+ async prepareModel(model, step, fallbackCallId) {
1122
+ if (step > checkpointedStep + 1) {
1123
+ const waiter = checkpointWaiters.get(step - 1) ?? Promise.withResolvers();
1124
+ checkpointWaiters.set(step - 1, waiter);
1125
+ await waiter.promise;
1126
+ }
1127
+ if (typeof model === "string") {
1128
+ await startModelRequest(fallbackCallId, step);
1129
+ return model;
1130
+ }
1131
+ return wrapLanguageModel({
1132
+ model,
1133
+ middleware: {
1134
+ specificationVersion: "v4",
1135
+ wrapStream: async ({ doStream }) => {
1136
+ const callId = providerCallId;
1137
+ providerCallId = undefined;
1138
+ if (!callId) {
1139
+ throw new Error("AgentRuntime model call reached the provider without lifecycle identity");
1140
+ }
1141
+ await startModelRequest(callId, step);
1142
+ return doStream();
1143
+ }
1144
+ }
1145
+ });
1146
+ },
1147
+ checkpointStep(step) {
1148
+ checkpointedStep = Math.max(checkpointedStep, step);
1149
+ for (const [waitingFor, waiter] of checkpointWaiters) {
1150
+ if (waitingFor > checkpointedStep)
1151
+ continue;
1152
+ checkpointWaiters.delete(waitingFor);
1153
+ waiter.resolve();
1154
+ }
1155
+ },
1156
+ failStepCheckpoints(error) {
1157
+ for (const waiter of checkpointWaiters.values())
1158
+ waiter.reject(error);
1159
+ checkpointWaiters.clear();
1160
+ },
1105
1161
  startCompaction(operationId) {
1106
1162
  return record(AgentRunOperationSchema.parse({
1107
1163
  operationId,
@@ -1110,15 +1166,6 @@ function createAgentRunOperationLifecycle(config) {
1110
1166
  startedAt: config.now().toISOString()
1111
1167
  }));
1112
1168
  },
1113
- startModelRequest(providerCallId, step) {
1114
- return record(AgentRunOperationSchema.parse({
1115
- operationId: `${providerCallId}:${step}`,
1116
- kind: "model-request",
1117
- phase: "started",
1118
- step,
1119
- startedAt: config.now().toISOString()
1120
- }));
1121
- },
1122
1169
  firstOutput() {
1123
1170
  if (active?.kind !== "model-request" || active.phase !== "started") {
1124
1171
  return Promise.resolve();
@@ -1506,16 +1553,17 @@ function createRunExecutor(dependencies) {
1506
1553
  }
1507
1554
  return messages;
1508
1555
  };
1556
+ const fallbackModel = selectedModel.model;
1509
1557
  const result = streamAgentTextBoundary({
1510
- model: selectedModel.model,
1558
+ model: fallbackModel,
1511
1559
  tools,
1512
1560
  instructions: withCarriedSystem(prompt.instructions),
1513
1561
  messages: history,
1514
1562
  abortSignal: executionSignal,
1515
1563
  maxRetries: 0,
1516
1564
  stopWhen: stopConditions,
1517
- onLanguageModelCallStart: async (event) => {
1518
- await operationLifecycle.startModelRequest(event.callId, step);
1565
+ onLanguageModelCallStart: (event) => {
1566
+ operationLifecycle.noteProviderCall(event.callId);
1519
1567
  },
1520
1568
  repairToolCall: deferredToolRepair(config.loop?.prepareStep),
1521
1569
  ...config.loop?.toolApproval && {
@@ -1525,25 +1573,27 @@ function createRunExecutor(dependencies) {
1525
1573
  ...config.loop?.toolApprovalSecret && {
1526
1574
  experimental_toolApprovalSecret: config.loop.toolApprovalSecret
1527
1575
  },
1528
- ...(config.loop?.prepareStep || injection) && {
1529
- prepareStep: async (options) => {
1530
- const previousStep = options.steps.at(-1);
1531
- if (previousStep) {
1532
- lastPromptTokens = selectedModel?.normalizeUsage?.({
1533
- usage: previousStep.usage,
1534
- providerMetadata: previousStep.providerMetadata
1535
- })?.inputTokens ?? normalizeSdkUsage(previousStep.usage).inputTokens;
1536
- }
1537
- const prepared = await config.loop?.prepareStep?.({
1538
- ...options,
1539
- ...runtimeContext
1540
- });
1541
- const injected = await takeInjectedMessages();
1542
- if (injected.length === 0)
1543
- return prepared;
1544
- const base = prepared?.messages ?? options.messages;
1545
- return { ...prepared, messages: [...base, ...injected] };
1576
+ prepareStep: async (options) => {
1577
+ const previousStep = options.steps.at(-1);
1578
+ if (previousStep) {
1579
+ lastPromptTokens = selectedModel?.normalizeUsage?.({
1580
+ usage: previousStep.usage,
1581
+ providerMetadata: previousStep.providerMetadata
1582
+ })?.inputTokens ?? normalizeSdkUsage(previousStep.usage).inputTokens;
1546
1583
  }
1584
+ const prepared = await config.loop?.prepareStep?.({
1585
+ ...options,
1586
+ ...runtimeContext
1587
+ });
1588
+ const injected = await takeInjectedMessages();
1589
+ const preparedStep = injected.length === 0 ? prepared : {
1590
+ ...prepared,
1591
+ messages: [...prepared?.messages ?? options.messages, ...injected]
1592
+ };
1593
+ return {
1594
+ ...preparedStep,
1595
+ model: await operationLifecycle.prepareModel(preparedStep?.model ?? fallbackModel, options.stepNumber, generateId())
1596
+ };
1547
1597
  }
1548
1598
  });
1549
1599
  for await (const part of result.stream) {
@@ -1819,6 +1869,8 @@ function createRunExecutor(dependencies) {
1819
1869
  if (structuralBoundary || eventsSinceCheckpoint >= checkpointEveryEvents) {
1820
1870
  await checkpoint();
1821
1871
  eventsSinceCheckpoint = 0;
1872
+ if (part.type === "finish-step")
1873
+ operationLifecycle.checkpointStep(step - 1);
1822
1874
  }
1823
1875
  }
1824
1876
  await operationLifecycle.finish(executionSignal.aborted ? "cancelled" : terminalReason === "provider_failure" ? "failed" : "completed");
@@ -1844,6 +1896,7 @@ function createRunExecutor(dependencies) {
1844
1896
  }
1845
1897
  }
1846
1898
  } catch (error) {
1899
+ operationLifecycle.failStepCheckpoints(error);
1847
1900
  internalCause = error;
1848
1901
  const latest = await config.store.loadRun({
1849
1902
  conversationId: run.conversationId,
package/llms-full.txt CHANGED
@@ -5276,8 +5276,11 @@ transaction. Drivers without this guarantee must leave the capability absent. Se
5276
5276
  - `run-operation` follows a successful operation CAS. Its `operation` is the
5277
5277
  durable `AgentRun.lastOperation`: `model-request` or `compaction`, with
5278
5278
  `started`, `first-output`, `completed`, `failed` or `cancelled` phase and the
5279
- original timestamps. Model request start means the provider call is about to
5280
- enter `doStream`, not that HTTP transmission is measured. First output is a
5279
+ original timestamps. Concrete models use SDK call identity in an awaited model
5280
+ middleware; global provider IDs use a runtime-generated identity during awaited
5281
+ step preparation. Both durably admit the request before entering `doStream`, so a failed
5282
+ write prevents the provider call. This does not claim HTTP transmission is
5283
+ measured. First output is a
5281
5284
  non-empty text/reasoning/tool-argument delta or complete tool call; metadata,
5282
5285
  usage, files and sources do not count;
5283
5286
  - `tool-status` is transient lifecycle presentation with JSON-safe input on
@@ -5292,7 +5295,9 @@ request/response and step-finish boundaries force an assistant checkpoint and
5292
5295
  reset that batch. The checkpoint is awaited after the managed loop observes the
5293
5296
  normalized boundary. It does **not** promise persistence before a tool side
5294
5297
  effect: tool fencing supplies the real ownership checks around execution, while
5295
- cross-crash effect idempotency stays application-owned. ADR 0174.
5298
+ cross-crash effect idempotency stays application-owned. The following model
5299
+ step waits for the previous step-finish checkpoint before its own durable
5300
+ request admission and provider invocation. → ADR 0174.
5296
5301
 
5297
5302
  `compaction` names invocation of the configured callback, including a
5298
5303
  `not_needed` context-budget check; it does not by itself say that history was
@@ -15187,7 +15192,7 @@ Server-only optional application runtime. See the
15187
15192
  | `AgentHistoryProjectionOptions` | _type_ | storage-neutral file resolver, explicit unresolved-file behavior, and how an interrupted turn reaches the model (`interruptedAssistant`) |
15188
15193
  | `createAgentToolFenceLifecycle` | function | pre-effect and post-effect run ownership fence for `mountAgent`; compose beside application idempotency for [durable operations](../guide/mcp-and-agents.md#durable-application-owned-execution) |
15189
15194
  | `AgentRuntimeEventSchema` | schema | transient stream lifecycle plus post-commit admission/checkpoint/run-state/run-operation/terminal projections |
15190
- | `AgentRunOperationKindSchema` / `AgentRunOperationPhaseSchema` / `AgentRunOperationSchema` / `AgentRunOperation` | schemas / _type_ | latest durable model-request or compaction phase with operation/step identity and original timestamps |
15195
+ | `AgentRunOperationKindSchema` / `AgentRunOperationPhaseSchema` / `AgentRunOperationSchema` / `AgentRunOperation` | schemas / _type_ | latest durable model-request or compaction phase with operation/step identity and original timestamps; request admission is awaited before provider invocation |
15191
15196
  | `RecordRunOperationSchema` / `RecordRunOperation` | schema / _type_ | owner/fencing/revision-checked mutation of `AgentRun.lastOperation` |
15192
15197
  | `createAgentObservability` | function | separate agent-run sink over the shared bounded observability lifecycle |
15193
15198
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stitchkit",
3
- "version": "0.85.0",
3
+ "version": "0.85.1",
4
4
  "description": "Contract-first backend framework — one defineContract() into an HTTP API, MCP tools, AI-agent tools and a typed client. Bun and Node.",
5
5
  "keywords": [
6
6
  "bun",
@@ -285,7 +285,7 @@
285
285
  "@types/json-schema": "^7.0.15",
286
286
  "@types/react": "^19.2.18",
287
287
  "@typescript/typescript6": "^6.0.2",
288
- "ai": "^7.0.79",
288
+ "ai": "^7.0.93",
289
289
  "fake-indexeddb": "^6.2.5",
290
290
  "grammy": "^1.45.1",
291
291
  "react": "^19.2.8",