@tangle-network/agent-app 0.45.5 → 0.45.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -54,6 +54,55 @@ declare function respondToSessionInteraction(connection: SidecarInteractionsConn
54
54
  outcome: InteractionOutcome;
55
55
  data?: InteractionData;
56
56
  }): Promise<SidecarInteractionsResult<void>>;
57
+ /**
58
+ * A sandbox session's lifecycle as the sidecar reports it.
59
+ *
60
+ * Every field is optional because the sidecar's payload has grown over time and
61
+ * an older box answers with a subset. A reader that assumes a field is present
62
+ * mis-reads an old box as terminal; the shapes below are read defensively for
63
+ * that reason, not out of caution about types.
64
+ */
65
+ interface SidecarSessionState {
66
+ state?: string;
67
+ activeExecutionId?: string | null;
68
+ activeExecutionStatus?: string | null;
69
+ reconnectable?: boolean;
70
+ registryAuthority?: string | null;
71
+ terminalReason?: string | null;
72
+ lastEventAt?: string | number | null;
73
+ outstandingInteractions?: unknown[];
74
+ }
75
+ interface SidecarAbortResult {
76
+ cancelled: boolean;
77
+ reason?: string;
78
+ session?: SidecarSessionState;
79
+ }
80
+ /**
81
+ * Whether a session has finished and will not produce more events.
82
+ *
83
+ * The three early returns are the ones that matter: a session with a live
84
+ * execution, one the platform says is reconnectable, or one holding an
85
+ * unanswered interaction is NOT terminal however its `state` string reads. An
86
+ * app that treats such a session as finished abandons a turn mid-flight, or
87
+ * leaves an agent blocked on an answer nobody will send.
88
+ */
89
+ declare function isTerminalSidecarState(state: {
90
+ state?: string;
91
+ activeExecutionId?: string | null;
92
+ reconnectable?: boolean;
93
+ outstandingInteractions?: unknown[];
94
+ }): boolean;
95
+ /** The session's current lifecycle, for reconnect and resume decisions. */
96
+ declare function getSessionState(connection: SidecarInteractionsConnection): Promise<SidecarInteractionsResult<SidecarSessionState>>;
97
+ /**
98
+ * Cancel a running session.
99
+ *
100
+ * A 404 is reported as a SUCCESSFUL no-op rather than an error: the caller's
101
+ * intent is "this session must not be running", and a session the sidecar has
102
+ * never heard of already satisfies that. Surfacing it as a failure makes every
103
+ * cancel-after-completion look like an outage.
104
+ */
105
+ declare function abortSession(connection: SidecarInteractionsConnection): Promise<SidecarInteractionsResult<SidecarAbortResult>>;
57
106
 
58
107
  /**
59
108
  * Framework-neutral interaction-answer endpoints, lifted out of the per-app
@@ -206,4 +255,4 @@ interface InteractionAnswerRoute {
206
255
  /** Create an interaction answer route that handles listing and resolving interaction requests */
207
256
  declare function createInteractionAnswerRoute(options: InteractionAnswerRouteOptions): InteractionAnswerRoute;
208
257
 
209
- export { type BeforeInteractionAnswerArgs, type DurableInteractionRouteArgs, type DurableInteractionRoutePersistence, type InteractionAnswerBodyValidation, type InteractionAnswerRoute, type InteractionAnswerRouteOptions, type InteractionClientOutcome, type InteractionConnectionResolution, InteractionRequestWire, type InteractionRouteLogger, type ResolveInteractionConnectionArgs, type SidecarInteractionsConnection, type SidecarInteractionsError, type SidecarInteractionsResult, createInteractionAnswerRoute, listSessionInteractions, mapInteractionRespondFailure, respondToSessionInteraction, validateInteractionAnswerBody };
258
+ export { type BeforeInteractionAnswerArgs, type DurableInteractionRouteArgs, type DurableInteractionRoutePersistence, type InteractionAnswerBodyValidation, type InteractionAnswerRoute, type InteractionAnswerRouteOptions, type InteractionClientOutcome, type InteractionConnectionResolution, InteractionRequestWire, type InteractionRouteLogger, type ResolveInteractionConnectionArgs, type SidecarAbortResult, type SidecarInteractionsConnection, type SidecarInteractionsError, type SidecarInteractionsResult, type SidecarSessionState, abortSession, createInteractionAnswerRoute, getSessionState, isTerminalSidecarState, listSessionInteractions, mapInteractionRespondFailure, respondToSessionInteraction, validateInteractionAnswerBody };
@@ -1,10 +1,13 @@
1
1
  import {
2
+ abortSession,
2
3
  createInteractionAnswerRoute,
4
+ getSessionState,
5
+ isTerminalSidecarState,
3
6
  listSessionInteractions,
4
7
  mapInteractionRespondFailure,
5
8
  respondToSessionInteraction,
6
9
  validateInteractionAnswerBody
7
- } from "../chunk-6KMAM7BL.js";
10
+ } from "../chunk-3BK3BC2L.js";
8
11
  import {
9
12
  INTERACTION_CANCEL_EVENT,
10
13
  INTERACTION_EVENT,
@@ -34,6 +37,7 @@ export {
34
37
  INTERACTION_CANCEL_EVENT,
35
38
  INTERACTION_EVENT,
36
39
  INTERACTION_RESOLVED_EVENT,
40
+ abortSession,
37
41
  canTransitionInteractionStatus,
38
42
  cancelStatusFor,
39
43
  composerAnswerData,
@@ -41,12 +45,14 @@ export {
41
45
  createInteractionAnswerRoute,
42
46
  dedupeQuestionInteractionsByContent,
43
47
  fieldAcceptsFreeText,
48
+ getSessionState,
44
49
  interactionFromWireRequest,
45
50
  interactionPartKey,
46
51
  interactionToPersistedPart,
47
52
  isRenderableInteractionKind,
48
53
  isSafeInteractionFieldKey,
49
54
  isTerminalInteractionStatus,
55
+ isTerminalSidecarState,
50
56
  listSessionInteractions,
51
57
  mapInteractionRespondFailure,
52
58
  noticePart,
@@ -262,25 +262,221 @@ declare function statSandboxFileSize(box: SandboxExecChannel, absolutePath: stri
262
262
  declare function readSandboxBinaryBytes(box: SandboxExecChannel, absolutePath: string, expectedSize: number, options?: SandboxExecOptions): Promise<SandboxFileBytesOutcome>;
263
263
 
264
264
  /**
265
- * Read a sandbox provisioning failure without leaking what it carries.
265
+ * What an app does after the platform hands back a sandbox it cannot use.
266
266
  *
267
- * Provisioning errors arrive as deep `cause` chains from the sandbox API, the
268
- * runtime sidecar, and the app's own vault code, and they routinely carry
269
- * bearer tokens and signed URLs in their messages. Every app on this shell
270
- * needs the same three things from one: a redacted shape it can log, a
271
- * classification it can act on, and a sentence it can show a person. Before
272
- * this module each app either wrote its own or — far more often — wrote none,
273
- * and surfaced the raw error or a generic apology.
267
+ * `replaceUnbringableBox` (see `./index`) decides whether to abandon a dead box.
268
+ * This decides everything around that: which box key the next attempt uses,
269
+ * whether the old box's snapshot is safe to restore from, whether an owner has
270
+ * to confirm the loss first, and what the person waiting is told. It is
271
+ * bookkeeping, not I/O the app supplies storage through
272
+ * {@link WorkspaceSandboxRecoveryStore}.
274
273
  *
275
- * The classifiers are deliberately narrow. `isSandboxApiSandboxMissingFailure`
276
- * does not fire on a 404 from inside a live box; `isSandboxHostCapacityFailure`
277
- * does not fire on capacity wording from any origin but the sandbox API. A
278
- * classifier that over-matches turns a transient failure into a box deletion.
274
+ * ── Why the tables ──────────────────────────────────────────────────────────
275
+ * Every fact about an action or a code lives in ACTIONS/CODES below, and the
276
+ * types are DERIVED from those tables. That is deliberate, and it is the whole
277
+ * reliability argument for this module.
278
+ *
279
+ * The hand-maintained alternative — a union type plus a separate `x === 'a' ||
280
+ * x === 'b' || …` list per question — silently drops anything not on the list.
281
+ * A recovery recorded under an unlisted action is written to storage, read
282
+ * back, discarded as malformed, and the workspace re-provisions the box it just
283
+ * abandoned. Nothing throws. The fix looks correct, ships, and does nothing.
284
+ * That failure was hit twice inside one change before this module existed.
285
+ *
286
+ * With the tables, adding an action or code is a compile error until every
287
+ * question about it is answered. There is no list to forget.
279
288
  */
280
- /** Error code an app raises when a stopped box's egress proxy must be rebuilt
281
- * before the box can be reused. Declared here so the classifier below stands
282
- * alone; apps that model this failure re-export the same literal. */
289
+ /** The box exists and still holds unsnapshotted state, so discarding it is an
290
+ * owner's decision, not the runtime's. */
283
291
  declare const EGRESS_PROXY_RECOVERY_REQUIRED = "EGRESS_PROXY_RECOVERY_REQUIRED";
292
+ declare const EGRESS_PROXY_RECOVERY_PHASE = "egress_proxy_recovery";
293
+ /** The platform no longer has the box. Nothing to confirm, delete, or restore. */
294
+ declare const WORKSPACE_SANDBOX_MISSING = "WORKSPACE_SANDBOX_MISSING";
295
+ /** The box exists but its host has no free slot, so it can never be resumed
296
+ * where it is. A replacement can be placed on a host with room. */
297
+ declare const WORKSPACE_SANDBOX_HOST_EXHAUSTED = "WORKSPACE_SANDBOX_HOST_EXHAUSTED";
298
+ /** The platform ran its own recovery, failed, and asked for a replacement.
299
+ * Covers every way a box ends up unbringable with no more specific cause. */
300
+ declare const WORKSPACE_SANDBOX_UNRECOVERABLE = "WORKSPACE_SANDBOX_UNRECOVERABLE";
301
+ declare const WORKSPACE_SANDBOX_SNAPSHOT_MAX_AGE_MS: number;
302
+ /**
303
+ * Every recovery cause, and the one thing the runtime must know about each:
304
+ * whether the box it names can still be read from.
305
+ *
306
+ * `snapshotUsable: false` is not a preference. A snapshot is addressed by the
307
+ * sandbox it was taken from, so when that sandbox cannot be started the restore
308
+ * fails exactly the way the resume did — and an app that tried it would turn a
309
+ * recoverable workspace into a stuck one.
310
+ */
311
+ declare const CODES: {
312
+ readonly EGRESS_PROXY_RECOVERY_REQUIRED: {
313
+ readonly snapshotUsable: true;
314
+ };
315
+ readonly WORKSPACE_SANDBOX_MISSING: {
316
+ readonly snapshotUsable: false;
317
+ };
318
+ readonly WORKSPACE_SANDBOX_HOST_EXHAUSTED: {
319
+ readonly snapshotUsable: false;
320
+ };
321
+ readonly WORKSPACE_SANDBOX_UNRECOVERABLE: {
322
+ readonly snapshotUsable: false;
323
+ };
324
+ };
325
+ type WorkspaceSandboxRecoveryCode = keyof typeof CODES;
326
+ /**
327
+ * Every recovery action, and whether it means a replacement box has been
328
+ * CHOSEN — the single question that decides which box key the next provisioning
329
+ * attempt uses.
330
+ *
331
+ * `replacementChosen: false` covers the states where a key may be recorded but
332
+ * must not be used yet: an owner has been asked and has not answered, or has
333
+ * answered no. Handing back a key in those states replaces a box the owner
334
+ * declined to lose.
335
+ */
336
+ declare const ACTIONS: {
337
+ readonly confirmation_required: {
338
+ readonly replacementChosen: false;
339
+ };
340
+ readonly deletion_declined: {
341
+ readonly replacementChosen: false;
342
+ };
343
+ readonly replacement_authorized: {
344
+ readonly replacementChosen: false;
345
+ };
346
+ readonly snapshot_replacement_authorized: {
347
+ readonly replacementChosen: false;
348
+ };
349
+ readonly replacement_started: {
350
+ readonly replacementChosen: true;
351
+ };
352
+ readonly replacement_completed: {
353
+ readonly replacementChosen: true;
354
+ };
355
+ readonly snapshot_replacement_started: {
356
+ readonly replacementChosen: true;
357
+ };
358
+ readonly snapshot_restore_failed: {
359
+ readonly replacementChosen: true;
360
+ };
361
+ readonly snapshot_replacement_completed: {
362
+ readonly replacementChosen: true;
363
+ };
364
+ readonly missing_replacement_started: {
365
+ readonly replacementChosen: true;
366
+ };
367
+ readonly missing_replacement_completed: {
368
+ readonly replacementChosen: true;
369
+ };
370
+ readonly unrecoverable_replacement_started: {
371
+ readonly replacementChosen: true;
372
+ };
373
+ readonly unrecoverable_replacement_completed: {
374
+ readonly replacementChosen: true;
375
+ };
376
+ };
377
+ type WorkspaceSandboxRecoveryAction = keyof typeof ACTIONS;
378
+ type WorkspaceSandboxSnapshotAvailability = 'available' | 'missing' | 'stale';
379
+ type WorkspaceSandboxSnapshotFreshness = 'fresh' | 'stale' | 'unknown';
380
+ /** A snapshot the app took of a box, addressed by the box it came from. */
381
+ interface WorkspaceSandboxSnapshot {
382
+ fromSandboxId: string;
383
+ createdAt: string;
384
+ [key: string]: unknown;
385
+ }
386
+ interface WorkspaceSandboxSnapshotAssessment {
387
+ availability: WorkspaceSandboxSnapshotAvailability;
388
+ freshness: WorkspaceSandboxSnapshotFreshness;
389
+ snapshot?: WorkspaceSandboxSnapshot;
390
+ }
391
+ interface WorkspaceSandboxRecoveryState {
392
+ code: WorkspaceSandboxRecoveryCode;
393
+ sandboxId: string;
394
+ detectedAt: string;
395
+ snapshot: WorkspaceSandboxSnapshotAssessment;
396
+ action: WorkspaceSandboxRecoveryAction;
397
+ replacementBoxKey?: string;
398
+ replacementSandboxId?: string;
399
+ confirmedAt?: string;
400
+ }
401
+ type WorkspaceSandboxRecoveryDecision = 'replace' | 'decline';
402
+ /** Raised when chat cannot continue until an owner decides about the box. */
403
+ declare class WorkspaceSandboxRecoveryRequiredError extends Error {
404
+ readonly code = "EGRESS_PROXY_RECOVERY_REQUIRED";
405
+ readonly status = 409;
406
+ readonly phase = "egress_proxy_recovery";
407
+ readonly recovery: WorkspaceSandboxRecoveryState;
408
+ constructor(recovery: WorkspaceSandboxRecoveryState, cause: Error);
409
+ }
410
+ declare function isEgressProxyRecoveryRequiredError(error: unknown): boolean;
411
+ declare function isWorkspaceSandboxSnapshotRestoreError(error: unknown): boolean;
412
+ /**
413
+ * Judge a snapshot against the box being replaced.
414
+ *
415
+ * Fresh means BOTH that it came from this exact sandbox and that it is inside
416
+ * the age bound — a snapshot from a different box restores someone else's
417
+ * filesystem, which is worse than starting empty.
418
+ */
419
+ declare function assessWorkspaceSandboxSnapshot(snapshot: WorkspaceSandboxSnapshot | undefined, sandboxId: string, now?: number): WorkspaceSandboxSnapshotAssessment;
420
+ declare function isWorkspaceSandboxRecoveryAction(value: unknown): value is WorkspaceSandboxRecoveryAction;
421
+ declare function isWorkspaceSandboxRecoveryCode(value: unknown): value is WorkspaceSandboxRecoveryCode;
422
+ declare function isWorkspaceSandboxRecoveryState(value: unknown): value is WorkspaceSandboxRecoveryState;
423
+ declare function workspaceSandboxRecoveryFromError(error: unknown): WorkspaceSandboxRecoveryState | undefined;
424
+ /** What to tell the person waiting. Never names the product, so an app can
425
+ * surface it verbatim. */
426
+ declare function workspaceSandboxRecoveryMessage(recovery: WorkspaceSandboxRecoveryState): string;
427
+ declare function workspaceSandboxRecoveryRecommendedActions(recovery: WorkspaceSandboxRecoveryState): string[];
428
+ /** Flat key/value shape for a log line — no nesting, no secrets. */
429
+ declare function workspaceSandboxRecoveryDiagnostic(recovery: WorkspaceSandboxRecoveryState): Record<string, string | undefined>;
430
+ /**
431
+ * The box key the next provisioning attempt should use, or undefined to keep
432
+ * using the workspace's own key.
433
+ *
434
+ * Reads {@link ACTIONS} rather than a hand-kept list, because the failure mode
435
+ * of a hand-kept list here is invisible: the key is recorded, silently ignored,
436
+ * and provisioning goes back to the box the app just decided to abandon.
437
+ */
438
+ declare function preferredWorkspaceSandboxRecoveryBoxKey(recovery: WorkspaceSandboxRecoveryState | undefined): string | undefined;
439
+ /** Whether the replacement should be restored from the old box's snapshot. */
440
+ declare function shouldRestoreWorkspaceSandboxRecovery(recovery: WorkspaceSandboxRecoveryState | undefined): boolean;
441
+ /**
442
+ * Where an app keeps recovery state. One row per workspace, last write wins —
443
+ * a recovery is a current situation, not a history.
444
+ */
445
+ interface WorkspaceSandboxRecoveryStore {
446
+ read: (workspaceId: string) => Promise<WorkspaceSandboxRecoveryState | undefined>;
447
+ write: (workspaceId: string, recovery: WorkspaceSandboxRecoveryState) => Promise<void>;
448
+ }
449
+ interface WorkspaceSandboxRecoveryManager {
450
+ read: (workspaceId: string) => Promise<WorkspaceSandboxRecoveryState | undefined>;
451
+ record: (workspaceId: string, recovery: WorkspaceSandboxRecoveryState) => Promise<void>;
452
+ /** Record an owner's decision. Returns undefined when the stored recovery does
453
+ * not name this sandbox — a decision about a box that has already been
454
+ * replaced must not resurrect it. */
455
+ decide: (args: {
456
+ workspaceId: string;
457
+ sandboxId: string;
458
+ decision: WorkspaceSandboxRecoveryDecision;
459
+ replacementBoxKey?: string;
460
+ }) => Promise<WorkspaceSandboxRecoveryState | undefined>;
461
+ /** Mark a replacement finished and name the box that took over. */
462
+ complete: (args: {
463
+ workspaceId: string;
464
+ replacementSandboxId: string;
465
+ }) => Promise<WorkspaceSandboxRecoveryState | undefined>;
466
+ }
467
+ /**
468
+ * Bind the recovery bookkeeping to an app's storage.
469
+ *
470
+ * The app owns persistence — a D1 column, a KV key, a Postgres row — and
471
+ * nothing else. Every rule about which action means what stays here, so it
472
+ * cannot drift between apps.
473
+ */
474
+ declare function createWorkspaceSandboxRecoveryManager(store: WorkspaceSandboxRecoveryStore): WorkspaceSandboxRecoveryManager;
475
+ /** Every declared action, for exhaustiveness tests in apps and here. */
476
+ declare const WORKSPACE_SANDBOX_RECOVERY_ACTIONS: readonly WorkspaceSandboxRecoveryAction[];
477
+ /** Every declared cause. */
478
+ declare const WORKSPACE_SANDBOX_RECOVERY_CODES: readonly WorkspaceSandboxRecoveryCode[];
479
+
284
480
  interface SafeSandboxErrorCause {
285
481
  name?: string;
286
482
  message?: string;
@@ -1363,4 +1559,4 @@ declare function isTerminalPromptEvent(event: unknown): boolean;
1363
1559
  /** Resolve the interactive question text from a structured event or return null if none found */
1364
1560
  declare function detectInteractiveQuestion(event: unknown): string | null;
1365
1561
 
1366
- export { type AppToolDescriptor, type BuildAppToolMcpServersOptions, type BuildSandboxToolFileMountsOptions, type D1PrewarmClaimStoreOptions, DEFAULT_PREWARM_CLAIM_TABLE, DEFAULT_SANDBOX_RESOURCES, DEFAULT_SIDECAR_PROCESS_PATTERN, type DriveSandboxTurnOptions, EGRESS_PROXY_RECOVERY_REQUIRED, ENV_TOTAL_MAX_BYTES, ENV_VALUE_MAX_BYTES, type EnsureWorkspaceSandboxOptions, type LivenessProbeConfig, type MemberSyncSeam, type ModelSelection, type ModelSelectionError, type ModelSelectionFailure, type ModelSelectionSource, type Outcome, PREWARM_CLAIM_TABLE_DDL, PROVISION_PAYLOAD_MAX_BYTES, type PeekWorkspaceSandboxOutcome, type PrewarmClaimD1Like, type PrewarmClaimStore, type PrewarmDecision, type PrewarmEvent, type PrewarmOutcome, type PrewarmResult, type ProfileComposeOptions, type PromptInputPart, type ProviderResolutionConfig, type ProvisionPayloadSections, type ProvisionProfileSection, type ResolveSandboxClientCredentialsOptions, type ResolvedModel, type SafeSandboxErrorCause, type SafeSandboxErrorDiagnostics, type SandboxBuildContext, type SandboxClientCredentials, type SandboxCredentialEnvironment, SandboxEgressPolicyMismatchError, type SandboxEgressPolicySource, type SandboxExecChannel, type SandboxExecOptions, type SandboxExistingBoxStage, type SandboxFileBytesOutcome, type SandboxFileSizeOutcome, SandboxModelResolutionError, type SandboxPermissionLevel, type SandboxPrewarmScope, type SandboxPrewarmer, type SandboxPrewarmerOptions, type SandboxReadiness, SandboxRecoveryFailedError, type SandboxRecoveryPhase, type SandboxResourceConfig, type SandboxRestoreSpec, SandboxRuntimeAuthRefreshError, type SandboxRuntimeConfig, type SandboxScope, type SandboxStepTransition, type SandboxTerminalConnectionRouteOptions, type SandboxToolPathOptions, type SandboxToolSpec, type ScopedTokenResult, type SecretStore, type StoppedSandboxResumeFailure, type StoppedSandboxResumeRecovery, type StreamSandboxPromptOptions, type TerminalConnectionBoxLike, type WorkspaceSandboxEnsureContext, type WorkspaceSandboxInstanceLike, type WorkspaceSandboxManager, type WorkspaceSandboxManagerOptions, type WriteProfileFilesOptions, assertEnvWithinLimits, assertProvisionPayloadWithinCap, attachReasoningEffort, buildAppToolMcpServers, buildSandboxToolFileMounts, buildSandboxToolPathSetupScript, classifySeveredStream, collectSandboxPromptText, createD1PrewarmClaimStore, createSandboxPrewarmer, createSandboxTerminalConnectionRoute, createWorkspaceSandboxManager, deferredCorpusHash, deleteSecret, detectInteractiveQuestion, driveSandboxTurn, ensureWorkspaceSandbox, flattenHistory, formatSandboxProvisioningSupportDetails, formatSandboxProvisioningUserMessage, getClient, isSandboxApiBearerAuthFailure, isSandboxApiSandboxMissingFailure, isSandboxAuthFailure, isSandboxHostCapacityFailure, isTerminalPromptEvent, mergeExtraMcp, mergeHistoryIntoParts, mintSandboxScopedToken, peekWorkspaceSandbox, readSandboxBinaryBytes, readSecret, requireTransportableModel, resetClientCache, resolveModel, resolveModelSelection, resolveSandboxClientCredentials, runSandboxPrompt, runSandboxToolPathSetup, sandboxToolBinDir, sandboxToolPath, sandboxToolRootDir, secretStoreFromClient, serializeSandboxProvisioningError, shellQuote, splitDeferredProfileFiles, statSandboxFileSize, storeSecret, streamSandboxPrompt, syncSandboxMemberAdd, syncSandboxMemberRemove, syncSandboxMemberRole, writeProfileFilesToBox };
1562
+ export { type AppToolDescriptor, type BuildAppToolMcpServersOptions, type BuildSandboxToolFileMountsOptions, type D1PrewarmClaimStoreOptions, DEFAULT_PREWARM_CLAIM_TABLE, DEFAULT_SANDBOX_RESOURCES, DEFAULT_SIDECAR_PROCESS_PATTERN, type DriveSandboxTurnOptions, EGRESS_PROXY_RECOVERY_PHASE, EGRESS_PROXY_RECOVERY_REQUIRED, ENV_TOTAL_MAX_BYTES, ENV_VALUE_MAX_BYTES, type EnsureWorkspaceSandboxOptions, type LivenessProbeConfig, type MemberSyncSeam, type ModelSelection, type ModelSelectionError, type ModelSelectionFailure, type ModelSelectionSource, type Outcome, PREWARM_CLAIM_TABLE_DDL, PROVISION_PAYLOAD_MAX_BYTES, type PeekWorkspaceSandboxOutcome, type PrewarmClaimD1Like, type PrewarmClaimStore, type PrewarmDecision, type PrewarmEvent, type PrewarmOutcome, type PrewarmResult, type ProfileComposeOptions, type PromptInputPart, type ProviderResolutionConfig, type ProvisionPayloadSections, type ProvisionProfileSection, type ResolveSandboxClientCredentialsOptions, type ResolvedModel, type SafeSandboxErrorCause, type SafeSandboxErrorDiagnostics, type SandboxBuildContext, type SandboxClientCredentials, type SandboxCredentialEnvironment, SandboxEgressPolicyMismatchError, type SandboxEgressPolicySource, type SandboxExecChannel, type SandboxExecOptions, type SandboxExistingBoxStage, type SandboxFileBytesOutcome, type SandboxFileSizeOutcome, SandboxModelResolutionError, type SandboxPermissionLevel, type SandboxPrewarmScope, type SandboxPrewarmer, type SandboxPrewarmerOptions, type SandboxReadiness, SandboxRecoveryFailedError, type SandboxRecoveryPhase, type SandboxResourceConfig, type SandboxRestoreSpec, SandboxRuntimeAuthRefreshError, type SandboxRuntimeConfig, type SandboxScope, type SandboxStepTransition, type SandboxTerminalConnectionRouteOptions, type SandboxToolPathOptions, type SandboxToolSpec, type ScopedTokenResult, type SecretStore, type StoppedSandboxResumeFailure, type StoppedSandboxResumeRecovery, type StreamSandboxPromptOptions, type TerminalConnectionBoxLike, WORKSPACE_SANDBOX_HOST_EXHAUSTED, WORKSPACE_SANDBOX_MISSING, WORKSPACE_SANDBOX_RECOVERY_ACTIONS, WORKSPACE_SANDBOX_RECOVERY_CODES, WORKSPACE_SANDBOX_SNAPSHOT_MAX_AGE_MS, WORKSPACE_SANDBOX_UNRECOVERABLE, type WorkspaceSandboxEnsureContext, type WorkspaceSandboxInstanceLike, type WorkspaceSandboxManager, type WorkspaceSandboxManagerOptions, type WorkspaceSandboxRecoveryAction, type WorkspaceSandboxRecoveryCode, type WorkspaceSandboxRecoveryDecision, type WorkspaceSandboxRecoveryManager, WorkspaceSandboxRecoveryRequiredError, type WorkspaceSandboxRecoveryState, type WorkspaceSandboxRecoveryStore, type WorkspaceSandboxSnapshot, type WorkspaceSandboxSnapshotAssessment, type WorkspaceSandboxSnapshotAvailability, type WorkspaceSandboxSnapshotFreshness, type WriteProfileFilesOptions, assertEnvWithinLimits, assertProvisionPayloadWithinCap, assessWorkspaceSandboxSnapshot, attachReasoningEffort, buildAppToolMcpServers, buildSandboxToolFileMounts, buildSandboxToolPathSetupScript, classifySeveredStream, collectSandboxPromptText, createD1PrewarmClaimStore, createSandboxPrewarmer, createSandboxTerminalConnectionRoute, createWorkspaceSandboxManager, createWorkspaceSandboxRecoveryManager, deferredCorpusHash, deleteSecret, detectInteractiveQuestion, driveSandboxTurn, ensureWorkspaceSandbox, flattenHistory, formatSandboxProvisioningSupportDetails, formatSandboxProvisioningUserMessage, getClient, isEgressProxyRecoveryRequiredError, isSandboxApiBearerAuthFailure, isSandboxApiSandboxMissingFailure, isSandboxAuthFailure, isSandboxHostCapacityFailure, isTerminalPromptEvent, isWorkspaceSandboxRecoveryAction, isWorkspaceSandboxRecoveryCode, isWorkspaceSandboxRecoveryState, isWorkspaceSandboxSnapshotRestoreError, mergeExtraMcp, mergeHistoryIntoParts, mintSandboxScopedToken, peekWorkspaceSandbox, preferredWorkspaceSandboxRecoveryBoxKey, readSandboxBinaryBytes, readSecret, requireTransportableModel, resetClientCache, resolveModel, resolveModelSelection, resolveSandboxClientCredentials, runSandboxPrompt, runSandboxToolPathSetup, sandboxToolBinDir, sandboxToolPath, sandboxToolRootDir, secretStoreFromClient, serializeSandboxProvisioningError, shellQuote, shouldRestoreWorkspaceSandboxRecovery, splitDeferredProfileFiles, statSandboxFileSize, storeSecret, streamSandboxPrompt, syncSandboxMemberAdd, syncSandboxMemberRemove, syncSandboxMemberRole, workspaceSandboxRecoveryDiagnostic, workspaceSandboxRecoveryFromError, workspaceSandboxRecoveryMessage, workspaceSandboxRecoveryRecommendedActions, writeProfileFilesToBox };
@@ -2,6 +2,7 @@ import {
2
2
  DEFAULT_PREWARM_CLAIM_TABLE,
3
3
  DEFAULT_SANDBOX_RESOURCES,
4
4
  DEFAULT_SIDECAR_PROCESS_PATTERN,
5
+ EGRESS_PROXY_RECOVERY_PHASE,
5
6
  EGRESS_PROXY_RECOVERY_REQUIRED,
6
7
  ENV_TOTAL_MAX_BYTES,
7
8
  ENV_VALUE_MAX_BYTES,
@@ -11,8 +12,16 @@ import {
11
12
  SandboxModelResolutionError,
12
13
  SandboxRecoveryFailedError,
13
14
  SandboxRuntimeAuthRefreshError,
15
+ WORKSPACE_SANDBOX_HOST_EXHAUSTED,
16
+ WORKSPACE_SANDBOX_MISSING,
17
+ WORKSPACE_SANDBOX_RECOVERY_ACTIONS,
18
+ WORKSPACE_SANDBOX_RECOVERY_CODES,
19
+ WORKSPACE_SANDBOX_SNAPSHOT_MAX_AGE_MS,
20
+ WORKSPACE_SANDBOX_UNRECOVERABLE,
21
+ WorkspaceSandboxRecoveryRequiredError,
14
22
  assertEnvWithinLimits,
15
23
  assertProvisionPayloadWithinCap,
24
+ assessWorkspaceSandboxSnapshot,
16
25
  attachReasoningEffort,
17
26
  buildAppToolMcpServers,
18
27
  buildSandboxToolFileMounts,
@@ -23,6 +32,7 @@ import {
23
32
  createSandboxPrewarmer,
24
33
  createSandboxTerminalConnectionRoute,
25
34
  createWorkspaceSandboxManager,
35
+ createWorkspaceSandboxRecoveryManager,
26
36
  deferredCorpusHash,
27
37
  deleteSecret,
28
38
  detectInteractiveQuestion,
@@ -32,15 +42,21 @@ import {
32
42
  formatSandboxProvisioningSupportDetails,
33
43
  formatSandboxProvisioningUserMessage,
34
44
  getClient,
45
+ isEgressProxyRecoveryRequiredError,
35
46
  isSandboxApiBearerAuthFailure,
36
47
  isSandboxApiSandboxMissingFailure,
37
48
  isSandboxAuthFailure,
38
49
  isSandboxHostCapacityFailure,
39
50
  isTerminalPromptEvent,
51
+ isWorkspaceSandboxRecoveryAction,
52
+ isWorkspaceSandboxRecoveryCode,
53
+ isWorkspaceSandboxRecoveryState,
54
+ isWorkspaceSandboxSnapshotRestoreError,
40
55
  mergeExtraMcp,
41
56
  mergeHistoryIntoParts,
42
57
  mintSandboxScopedToken,
43
58
  peekWorkspaceSandbox,
59
+ preferredWorkspaceSandboxRecoveryBoxKey,
44
60
  readSandboxBinaryBytes,
45
61
  readSecret,
46
62
  requireTransportableModel,
@@ -56,6 +72,7 @@ import {
56
72
  secretStoreFromClient,
57
73
  serializeSandboxProvisioningError,
58
74
  shellQuote,
75
+ shouldRestoreWorkspaceSandboxRecovery,
59
76
  splitDeferredProfileFiles,
60
77
  statSandboxFileSize,
61
78
  storeSecret,
@@ -63,8 +80,12 @@ import {
63
80
  syncSandboxMemberAdd,
64
81
  syncSandboxMemberRemove,
65
82
  syncSandboxMemberRole,
83
+ workspaceSandboxRecoveryDiagnostic,
84
+ workspaceSandboxRecoveryFromError,
85
+ workspaceSandboxRecoveryMessage,
86
+ workspaceSandboxRecoveryRecommendedActions,
66
87
  writeProfileFilesToBox
67
- } from "../chunk-QGHQ2IRC.js";
88
+ } from "../chunk-4XNCMUVI.js";
68
89
  import "../chunk-LWSJK546.js";
69
90
  import "../chunk-73F3CKVK.js";
70
91
  import "../chunk-ITCINLSU.js";
@@ -76,6 +97,7 @@ export {
76
97
  DEFAULT_PREWARM_CLAIM_TABLE,
77
98
  DEFAULT_SANDBOX_RESOURCES,
78
99
  DEFAULT_SIDECAR_PROCESS_PATTERN,
100
+ EGRESS_PROXY_RECOVERY_PHASE,
79
101
  EGRESS_PROXY_RECOVERY_REQUIRED,
80
102
  ENV_TOTAL_MAX_BYTES,
81
103
  ENV_VALUE_MAX_BYTES,
@@ -85,8 +107,16 @@ export {
85
107
  SandboxModelResolutionError,
86
108
  SandboxRecoveryFailedError,
87
109
  SandboxRuntimeAuthRefreshError,
110
+ WORKSPACE_SANDBOX_HOST_EXHAUSTED,
111
+ WORKSPACE_SANDBOX_MISSING,
112
+ WORKSPACE_SANDBOX_RECOVERY_ACTIONS,
113
+ WORKSPACE_SANDBOX_RECOVERY_CODES,
114
+ WORKSPACE_SANDBOX_SNAPSHOT_MAX_AGE_MS,
115
+ WORKSPACE_SANDBOX_UNRECOVERABLE,
116
+ WorkspaceSandboxRecoveryRequiredError,
88
117
  assertEnvWithinLimits,
89
118
  assertProvisionPayloadWithinCap,
119
+ assessWorkspaceSandboxSnapshot,
90
120
  attachReasoningEffort,
91
121
  buildAppToolMcpServers,
92
122
  buildSandboxToolFileMounts,
@@ -97,6 +127,7 @@ export {
97
127
  createSandboxPrewarmer,
98
128
  createSandboxTerminalConnectionRoute,
99
129
  createWorkspaceSandboxManager,
130
+ createWorkspaceSandboxRecoveryManager,
100
131
  deferredCorpusHash,
101
132
  deleteSecret,
102
133
  detectInteractiveQuestion,
@@ -106,15 +137,21 @@ export {
106
137
  formatSandboxProvisioningSupportDetails,
107
138
  formatSandboxProvisioningUserMessage,
108
139
  getClient,
140
+ isEgressProxyRecoveryRequiredError,
109
141
  isSandboxApiBearerAuthFailure,
110
142
  isSandboxApiSandboxMissingFailure,
111
143
  isSandboxAuthFailure,
112
144
  isSandboxHostCapacityFailure,
113
145
  isTerminalPromptEvent,
146
+ isWorkspaceSandboxRecoveryAction,
147
+ isWorkspaceSandboxRecoveryCode,
148
+ isWorkspaceSandboxRecoveryState,
149
+ isWorkspaceSandboxSnapshotRestoreError,
114
150
  mergeExtraMcp,
115
151
  mergeHistoryIntoParts,
116
152
  mintSandboxScopedToken,
117
153
  peekWorkspaceSandbox,
154
+ preferredWorkspaceSandboxRecoveryBoxKey,
118
155
  readSandboxBinaryBytes,
119
156
  readSecret,
120
157
  requireTransportableModel,
@@ -130,6 +167,7 @@ export {
130
167
  secretStoreFromClient,
131
168
  serializeSandboxProvisioningError,
132
169
  shellQuote,
170
+ shouldRestoreWorkspaceSandboxRecovery,
133
171
  splitDeferredProfileFiles,
134
172
  statSandboxFileSize,
135
173
  storeSecret,
@@ -137,6 +175,10 @@ export {
137
175
  syncSandboxMemberAdd,
138
176
  syncSandboxMemberRemove,
139
177
  syncSandboxMemberRole,
178
+ workspaceSandboxRecoveryDiagnostic,
179
+ workspaceSandboxRecoveryFromError,
180
+ workspaceSandboxRecoveryMessage,
181
+ workspaceSandboxRecoveryRecommendedActions,
140
182
  writeProfileFilesToBox
141
183
  };
142
184
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-app",
3
- "version": "0.45.5",
3
+ "version": "0.45.7",
4
4
  "packageManager": "pnpm@11.17.0",
5
5
  "description": "Build agent applications with typed chat, tools, sandboxes, integrations, billing, and evaluation.",
6
6
  "keywords": [
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/interactions/sidecar.ts","../src/interactions/route.ts"],"sourcesContent":["/**\n * Server-side client for the sandbox sidecar's generic interaction routes\n * (`GET/POST {runtimeUrl}/agents/sessions/{sessionId}/interactions`). The\n * pinned sandbox SDK exposes only the question-specific `session().answer()`\n * convenience; these raw calls are backend-agnostic (question/permission/plan,\n * any harness) and carry explicit outcomes (accepted/declined).\n *\n * Server-only: the sidecar bearer must never reach browser code. The caller\n * supplies the connection as a structural value (runtime URL + bearer +\n * session id) — no sandbox-SDK import, so any box-resolution strategy works.\n */\n\nimport type { InteractionData, InteractionOutcome, InteractionRequestWire } from './contract'\n\n/** Describe error details including code, message, and upstream HTTP status for sidecar interactions */\nexport interface SidecarInteractionsError {\n code: string\n message: string\n /** Upstream HTTP status; 0 when the sidecar was unreachable. */\n status: number\n}\n\n/** Represent the outcome of sidecar interactions with success or error details */\nexport type SidecarInteractionsResult<T> =\n | { succeeded: true; value: T }\n | { succeeded: false; error: SidecarInteractionsError }\n\n/** Where and how to reach one session's interaction registry. */\nexport interface SidecarInteractionsConnection {\n runtimeUrl: string\n authToken?: string\n /** The sidecar agent-session id (the chat thread's session). */\n sessionId: string\n /** Request deadline. A pending interaction means the box is up and the\n * sidecar responsive; a short default keeps a wedged runtime from stalling\n * the answering request. */\n timeoutMs?: number\n /** Injection seam for tests; defaults to global fetch. */\n fetchImpl?: typeof fetch\n}\n\nconst DEFAULT_TIMEOUT_MS = 5_000\n\n/** Strips bearer tokens / key material before an upstream message is logged\n * or surfaced. */\nfunction sanitizeUpstreamMessage(input: unknown): string {\n const message = input instanceof Error ? input.message : String(input)\n return message\n .replace(/Bearer\\s+[^\\s]+/gi, 'Bearer [redacted]')\n .replace(/\\b(?:sk|pk|tc)[_-][A-Za-z0-9_-]{8,}\\b/g, '[redacted-key]')\n}\n\nasync function interactionsFetch(\n connection: SidecarInteractionsConnection,\n init: { method: 'GET' } | { method: 'POST'; body: Record<string, unknown> },\n): Promise<SidecarInteractionsResult<Record<string, unknown>>> {\n const doFetch = connection.fetchImpl ?? fetch\n const url = `${connection.runtimeUrl.replace(/\\/$/, '')}/agents/sessions/${encodeURIComponent(connection.sessionId)}/interactions`\n let response: Response\n try {\n response = await doFetch(url, {\n method: init.method,\n headers: {\n ...(connection.authToken ? { Authorization: `Bearer ${connection.authToken}` } : {}),\n ...(init.method === 'POST' ? { 'Content-Type': 'application/json' } : {}),\n },\n ...(init.method === 'POST' ? { body: JSON.stringify(init.body) } : {}),\n signal: AbortSignal.timeout(connection.timeoutMs ?? DEFAULT_TIMEOUT_MS),\n })\n } catch (err) {\n return {\n succeeded: false,\n error: { code: 'UPSTREAM_UNREACHABLE', message: sanitizeUpstreamMessage(err), status: 0 },\n }\n }\n const raw = await response.text().catch(() => '')\n let parsed: Record<string, unknown> = {}\n try {\n parsed = raw ? (JSON.parse(raw) as Record<string, unknown>) : {}\n } catch {\n // Non-JSON error bodies (proxy 502 pages) fall through to the status check.\n }\n if (!response.ok) {\n const upstreamError = (parsed.error ?? {}) as { code?: unknown; message?: unknown }\n return {\n succeeded: false,\n error: {\n code: typeof upstreamError.code === 'string' && upstreamError.code ? upstreamError.code : 'UPSTREAM_ERROR',\n message: sanitizeUpstreamMessage(\n typeof upstreamError.message === 'string' && upstreamError.message\n ? upstreamError.message\n : `sidecar interactions ${init.method} failed (${response.status})`,\n ),\n status: response.status,\n },\n }\n }\n return { succeeded: true, value: parsed }\n}\n\n/** Outstanding (unanswered) interactions for the session — the sidecar's\n * registry is authoritative, so this is the reconnect/reload source of truth. */\nexport async function listSessionInteractions(\n connection: SidecarInteractionsConnection,\n): Promise<SidecarInteractionsResult<InteractionRequestWire[]>> {\n const result = await interactionsFetch(connection, { method: 'GET' })\n if (!result.succeeded) return result\n const data = result.value.data as { interactions?: unknown } | undefined\n if (!Array.isArray(data?.interactions)) {\n return {\n succeeded: false,\n error: { code: 'MALFORMED_RESPONSE', message: 'sidecar list returned no interactions array', status: 200 },\n }\n }\n return { succeeded: true, value: data.interactions as InteractionRequestWire[] }\n}\n\n/** Resolves one interaction. `data` is required by the sidecar only for\n * `accepted` outcomes and is validated fail-closed against the answerSpec\n * (400 INVALID_INTERACTION_ANSWER on mismatch). */\nexport async function respondToSessionInteraction(\n connection: SidecarInteractionsConnection,\n response: { id: string; outcome: InteractionOutcome; data?: InteractionData },\n): Promise<SidecarInteractionsResult<void>> {\n const result = await interactionsFetch(connection, {\n method: 'POST',\n body: {\n id: response.id,\n outcome: response.outcome,\n ...(response.data ? { data: response.data } : {}),\n },\n })\n if (!result.succeeded) return result\n return { succeeded: true, value: undefined }\n}\n","/**\n * Framework-neutral interaction-answer endpoints, lifted out of the per-app\n * route files (gtm `api.chat.interactions`, legal `api.chat.interactions`,\n * tax `api.sessions.$id.interactions` — three byte-similar forks):\n *\n * list(request) — GET: outstanding asks for a live turn (reload restore)\n * answer(request) — POST `{ id, outcome, data? }`: resolve one ask\n *\n * The product supplies ONE seam, `resolveConnection`: authenticate the caller,\n * authorize the thread/session, and resolve the sidecar connection. Everything\n * behind the seam is mechanism the forks kept re-fixing:\n *\n * - body validation (safe field keys, typed values),\n * - sidecar error → client contract mapping (every \"the ask is gone\" shape\n * becomes 410 so the card flips to its expired state),\n * - duplicate resolution: after answering, every other outstanding ask with\n * the same content signature (a re-emitted duplicate) gets the same answer,\n * - unblock verification: re-list and fail loud (503 INTERACTION_STILL_PENDING)\n * when the sidecar accepted the POST but the ask is still open,\n * - best-effort list: sidecar failures return `{ interactions: [],\n * unavailable }` so a reload restore never breaks the live stream.\n *\n * Handlers return web-standard `Response`s (Workers, Node 18+, Deno) — no\n * router import anywhere.\n */\n\nimport {\n interactionFromWireRequest,\n isSafeInteractionFieldKey,\n questionInteractionContentSignature,\n type InteractionData,\n type InteractionRequestWire,\n} from './contract'\nimport {\n listSessionInteractions,\n respondToSessionInteraction,\n type SidecarInteractionsConnection,\n type SidecarInteractionsError,\n} from './sidecar'\n\n// A client resolves an ask by answering (`accepted`) or refusing (`declined`).\n// Withdrawal (`cancelled`) is an agent/broker outcome delivered via the\n// `interaction.cancel` event, never a client POST, so it is not accepted here.\n/** Define possible outcomes for an interaction client as accepted or declined */\nexport type InteractionClientOutcome = 'accepted' | 'declined'\n\n/** Validate interaction answer body and return success with data or failure with error message */\nexport type InteractionAnswerBodyValidation =\n | { ok: true; id: string; outcome: InteractionClientOutcome; data?: InteractionData }\n | { ok: false; error: string }\n\n/** Validates the client POST body: `{ id, outcome, data? }` with\n * identifier-safe field keys and primitive/string-array values only. */\nexport function validateInteractionAnswerBody(body: Record<string, unknown>): InteractionAnswerBodyValidation {\n const id = typeof body.id === 'string' && body.id ? body.id : null\n if (!id) return { ok: false, error: 'Missing interaction id' }\n const outcome = body.outcome\n if (outcome !== 'accepted' && outcome !== 'declined') {\n return { ok: false, error: 'Invalid outcome: expected accepted or declined' }\n }\n if (body.data === undefined) return { ok: true, id, outcome }\n if (!body.data || typeof body.data !== 'object' || Array.isArray(body.data)) {\n return { ok: false, error: 'Invalid data: expected an object of field values' }\n }\n const data: InteractionData = {}\n for (const [key, value] of Object.entries(body.data)) {\n if (!isSafeInteractionFieldKey(key)) {\n return { ok: false, error: 'Invalid data: field names must contain only letters, numbers, underscores, or hyphens' }\n }\n const validValue =\n typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean' ||\n (Array.isArray(value) && value.every((item) => typeof item === 'string'))\n if (!validValue) {\n return { ok: false, error: 'Invalid data: field values must be strings, numbers, booleans, or string arrays' }\n }\n data[key] = value as InteractionData[string]\n }\n return { ok: true, id, outcome, data }\n}\n\n/** Provide logging methods for warnings and errors in interaction routes */\nexport type InteractionRouteLogger = Pick<Console, 'warn' | 'error'>\n\n/** Sidecar error → the client-actionable contract. Every \"the ask is gone\"\n * shape maps to 410 so the card flips to its expired state — a raw 404/409\n * must never surface. */\nexport function mapInteractionRespondFailure(\n error: SidecarInteractionsError,\n logger: InteractionRouteLogger = console,\n): Response {\n if (error.code === 'INVALID_INTERACTION_ANSWER') {\n return Response.json(\n { code: 'INVALID_INTERACTION_ANSWER', error: 'This question needs an answer from the card above — pick one of the listed options.' },\n { status: 400 },\n )\n }\n if (error.status === 404) {\n return Response.json(\n { code: 'INTERACTION_EXPIRED', error: 'This question is no longer waiting for an answer.' },\n { status: 410 },\n )\n }\n if (error.status === 501 || error.code === 'NOT_IMPLEMENTED') {\n return Response.json(\n { code: 'INTERACTIONS_UNSUPPORTED', error: 'This agent backend cannot accept answers this way.' },\n { status: 501 },\n )\n }\n logger.error('[interactions] respond failed:', error)\n return Response.json(\n { code: 'INTERACTION_UPSTREAM_FAILED', error: 'Could not reach the agent. Try again.' },\n { status: 503 },\n )\n}\n\n/** The product seam's verdict for one request. `response` short-circuits with\n * a product-authored Response (401/404/429…); `unavailable` means the caller\n * is fine but the sandbox runtime is not reachable — the factory shapes that\n * per intent (empty list for `list`, 503 for `answer`). */\nexport type InteractionConnectionResolution =\n | { ok: true; connection: SidecarInteractionsConnection }\n | { ok: false; response: Response }\n | { ok: false; unavailable: string }\n\n/** Define arguments required to resolve interaction connections based on request and intent */\nexport interface ResolveInteractionConnectionArgs {\n request: Request\n intent: 'list' | 'answer'\n /** The parsed, validated POST body (answer intent only) so the resolver can\n * read product routing fields (workspaceId/threadId) without re-parsing. */\n body?: Record<string, unknown>\n}\n\n/** Describe the arguments provided before processing an interaction answer including request, body, and connection details */\nexport interface BeforeInteractionAnswerArgs {\n request: Request\n /** Original parsed body, including product routing fields. */\n body: Record<string, unknown>\n /** Shared validation result; products never need to parse the answer again. */\n answer: Extract<InteractionAnswerBodyValidation, { ok: true }>\n connection: SidecarInteractionsConnection\n /** The route's single authoritative pre-answer sidecar snapshot. */\n outstanding: InteractionRequestWire[]\n answeredRequest?: InteractionRequestWire\n /** Content-identical questions that the shared route will also answer. */\n duplicateRequests: InteractionRequestWire[]\n}\n\n/** Define arguments for durable interaction routes including a stable caller-created attempt key */\nexport interface DurableInteractionRouteArgs extends BeforeInteractionAnswerArgs {\n /** Caller-created opaque identifier, stable across an ambiguous retry. */\n attemptKey: string\n}\n\n/** Crash-recoverable persistence lifecycle for the answer route. The product\n * binds this structural port to its OWN durable store — agent-app ships no\n * implementation (the `/durable-chat` module that used to provide one was\n * removed in 0.44.0 after nine repos produced zero imports of it). Existing\n * `beforeAnswer` behavior remains independent and unchanged. */\nexport interface DurableInteractionRoutePersistence<TPrepared = unknown> {\n guarantee: 'reconciled' | 'best-effort'\n prepare(args: DurableInteractionRouteArgs): TPrepared | Promise<TPrepared>\n /** Resolve an ambiguous retry (for example, the sidecar says the ask is\n * already gone). Only `settled:true` permits the route to report success. */\n reconcile(args: DurableInteractionRouteArgs & { prepared: TPrepared }):\n | { settled: boolean }\n | Promise<{ settled: boolean }>\n /** Record the authority acknowledgement before terminal projection. */\n acknowledge(args: DurableInteractionRouteArgs & {\n prepared: TPrepared\n duplicateIds: string[]\n }): void | Promise<void>\n /** Idempotently materialize terminal status and accepted values. */\n finalize(args: DurableInteractionRouteArgs & {\n prepared: TPrepared\n duplicateIds: string[]\n }): void | Promise<void>\n fail?(args: DurableInteractionRouteArgs & { prepared: TPrepared; error: unknown }): void | Promise<void>\n}\n\n/** Define options to authenticate, authorize, and manage persistence for interaction answer routes */\nexport interface InteractionAnswerRouteOptions {\n /** Authenticate + authorize the caller and resolve the sidecar connection.\n * This is the only product-supplied step: session auth, workspace/thread\n * access, rate limiting, and box resolution all live here. */\n resolveConnection: (args: ResolveInteractionConnectionArgs) => Promise<InteractionConnectionResolution>\n /** Product persistence seam that runs before the answer can unblock and\n * finalize the agent turn. A throw aborts the request before any answer POST. */\n beforeAnswer?: (args: BeforeInteractionAnswerArgs) => void | Promise<void>\n /** Additive crash-recoverable settlement. When configured, POST requires an\n * `attemptKey`; accepted values are finalized only after sidecar ack. */\n durable?: DurableInteractionRoutePersistence\n logger?: InteractionRouteLogger\n}\n\n/** Define routes to list outstanding interactions and resolve answers for live turns */\nexport interface InteractionAnswerRoute {\n /** GET — outstanding interactions for a live turn. Failures return an empty\n * list with an explicit `unavailable` code: the caller is a best-effort\n * reload restore, and the live/replayed stream must stay untouched when the\n * sidecar cannot answer. */\n list: (request: Request) => Promise<Response>\n /** POST `{ id, outcome, data?, ...productFields }` — resolve one ask, answer\n * content-identical duplicates the same way, then re-list to prove the run\n * actually unblocked. */\n answer: (request: Request) => Promise<Response>\n}\n\n/** Create an interaction answer route that handles listing and resolving interaction requests */\nexport function createInteractionAnswerRoute(options: InteractionAnswerRouteOptions): InteractionAnswerRoute {\n const logger = options.logger ?? console\n\n async function list(request: Request): Promise<Response> {\n const resolution = await options.resolveConnection({ request, intent: 'list' })\n if (!resolution.ok) {\n if ('response' in resolution) return resolution.response\n return Response.json({ interactions: [], unavailable: resolution.unavailable })\n }\n const result = await listSessionInteractions(resolution.connection)\n if (!result.succeeded) {\n logger.warn('[interactions] list failed:', result.error)\n return Response.json({ interactions: [], unavailable: result.error.code })\n }\n return Response.json({ interactions: result.value })\n }\n\n async function answer(request: Request): Promise<Response> {\n const body = await request.json().catch(() => null) as Record<string, unknown> | null\n if (!body || typeof body !== 'object' || Array.isArray(body)) {\n return Response.json({ error: 'Invalid JSON body' }, { status: 400 })\n }\n const validation = validateInteractionAnswerBody(body)\n if (!validation.ok) return Response.json({ error: validation.error }, { status: 400 })\n const attemptKey = typeof body.attemptKey === 'string' ? body.attemptKey.trim() : ''\n if (options.durable && !attemptKey) {\n return Response.json({ error: 'Missing attemptKey for durable interaction answer' }, { status: 400 })\n }\n\n const resolution = await options.resolveConnection({ request, intent: 'answer', body })\n if (!resolution.ok) {\n if ('response' in resolution) return resolution.response\n return mapInteractionRespondFailure(\n { code: resolution.unavailable, message: 'sandbox runtime unavailable', status: 0 },\n logger,\n )\n }\n const connection = resolution.connection\n const answerPayload = {\n outcome: validation.outcome,\n ...(validation.data ? { data: validation.data } : {}),\n }\n\n // Snapshot the answered ask's content signature BEFORE resolving it, so any\n // content-identical duplicates still outstanding afterwards (the agent may\n // have re-emitted the same question N times) can be answered the same way.\n const before = await listSessionInteractions(connection)\n if (!before.succeeded && (options.beforeAnswer || options.durable)) {\n return mapInteractionRespondFailure(before.error, logger)\n }\n const answeredRequest = before.succeeded ? before.value.find((item) => item.id === validation.id) : undefined\n if (options.beforeAnswer && !options.durable && !answeredRequest) {\n return mapInteractionRespondFailure(\n { code: 'NOT_FOUND', message: 'interaction not found', status: 404 },\n logger,\n )\n }\n const answeredSignature = answeredRequest\n ? questionInteractionContentSignature(interactionFromWireRequest(answeredRequest))\n : null\n const duplicateRequests = before.succeeded && answeredSignature\n ? before.value.filter((item) => item.id !== validation.id && (\n questionInteractionContentSignature(interactionFromWireRequest(item)) === answeredSignature\n ))\n : []\n\n const lifecycleArgs: BeforeInteractionAnswerArgs = {\n request,\n body,\n answer: validation,\n connection,\n outstanding: before.succeeded ? before.value : [],\n ...(answeredRequest ? { answeredRequest } : {}),\n duplicateRequests,\n }\n\n if (options.beforeAnswer && answeredRequest) {\n try {\n await options.beforeAnswer(lifecycleArgs)\n } catch (error) {\n logger.error('[interactions] beforeAnswer failed:', error)\n return Response.json(\n { code: 'INTERACTION_BEFORE_ANSWER_FAILED', error: 'Could not save the answer. Try again.' },\n { status: 503 },\n )\n }\n }\n\n let prepared: unknown\n const durableArgs = options.durable\n ? { ...lifecycleArgs, attemptKey }\n : null\n if (options.durable && durableArgs) {\n try {\n prepared = await options.durable.prepare(durableArgs)\n } catch (error) {\n logger.error('[interactions] durable prepare failed:', error)\n return Response.json(\n { code: 'INTERACTION_PREPARE_FAILED', error: 'Could not prepare the answer. Try again.' },\n { status: 503 },\n )\n }\n if (!answeredRequest) {\n let reconciled: { settled: boolean }\n try {\n reconciled = await options.durable.reconcile({ ...durableArgs, prepared })\n } catch (error) {\n logger.error('[interactions] durable reconcile failed:', error)\n reconciled = { settled: false }\n }\n if (reconciled.settled) return Response.json({ ok: true, idempotent: true })\n return Response.json(\n { code: 'INTERACTION_RECONCILIATION_PENDING', error: 'The answer status is still being checked. Retry with the same attempt.' },\n { status: 503 },\n )\n }\n }\n\n const result = await respondToSessionInteraction(connection, { id: validation.id, ...answerPayload })\n if (!result.succeeded) {\n if (options.durable && durableArgs) {\n let reconciled: { settled: boolean }\n try {\n reconciled = await options.durable.reconcile({ ...durableArgs, prepared })\n } catch {\n reconciled = { settled: false }\n }\n if (reconciled.settled) return Response.json({ ok: true, idempotent: true })\n if (result.error.status === 404) {\n return Response.json(\n { code: 'INTERACTION_RECONCILIATION_PENDING', error: 'The answer status is still being checked. Retry with the same attempt.' },\n { status: 503 },\n )\n }\n try {\n await options.durable.fail?.({ ...durableArgs, prepared, error: result.error })\n } catch {\n // The upstream failure remains authoritative; a persistence cleanup\n // failure must not replace its client-facing mapping.\n }\n }\n return mapInteractionRespondFailure(result.error, logger)\n }\n\n let remaining = await listSessionInteractions(connection)\n const acknowledgedDuplicateIds: string[] = []\n if (remaining.succeeded && answeredSignature) {\n const remainingDuplicates = remaining.value.filter((item) => {\n if (item.id === validation.id) return false\n return questionInteractionContentSignature(interactionFromWireRequest(item)) === answeredSignature\n })\n for (const duplicate of remainingDuplicates) {\n const duplicateResult = await respondToSessionInteraction(connection, { id: duplicate.id, ...answerPayload })\n if (!duplicateResult.succeeded) break\n acknowledgedDuplicateIds.push(duplicate.id)\n }\n if (remainingDuplicates.length > 0) remaining = await listSessionInteractions(connection)\n }\n\n // Prove the run actually unblocked: neither the answered id nor any\n // content-duplicate may still be outstanding. If one is, the sidecar\n // accepted the POST but did not release the run — report it rather than\n // tell the user it was answered.\n if (remaining.succeeded && remaining.value.some((item) => item.id === validation.id || (\n answeredSignature && questionInteractionContentSignature(interactionFromWireRequest(item)) === answeredSignature\n ))) {\n logger.error('[interactions] respond returned ok but interaction is still pending:', {\n sessionId: connection.sessionId,\n interactionId: validation.id,\n })\n return Response.json(\n { code: 'INTERACTION_STILL_PENDING', error: 'The agent did not accept the answer. Try answering again.' },\n { status: 503 },\n )\n }\n\n if (options.durable && durableArgs) {\n try {\n await options.durable.acknowledge({\n ...durableArgs,\n prepared,\n duplicateIds: acknowledgedDuplicateIds,\n })\n await options.durable.finalize({\n ...durableArgs,\n prepared,\n duplicateIds: acknowledgedDuplicateIds,\n })\n } catch (error) {\n logger.error('[interactions] durable finalize failed:', error)\n return Response.json(\n { code: 'INTERACTION_FINALIZE_FAILED', error: 'The answer was accepted but is still being saved. Retry to reconcile it.' },\n { status: 503 },\n )\n }\n }\n\n return Response.json({ ok: true })\n }\n\n return { list, answer }\n}\n"],"mappings":"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","names":[]}