Skip to content

Commit 1fd25b6

Browse files
committed
improvement(mothership): keep the durable-run predicates out of the chat protocol graph
1 parent a0e1f41 commit 1fd25b6

7 files changed

Lines changed: 45 additions & 39 deletions

File tree

‎apps/sim/lib/mothership/async-runs/orphaned-runs.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import { getRedisClient } from '@/lib/core/config/redis'
3030
import type { DbTransaction } from '@/lib/db/types'
3131
import { SIM_TOOL_EXECUTION_VERSION } from '@/lib/mothership/async-runs/lifecycle'
3232
import { publishChatStatusChanged } from '@/lib/mothership/chat-status'
33-
import { isDurableStreamRun } from '@/lib/mothership/request/lifecycle/recovery-config'
33+
import { isDurableStreamRun } from '@/lib/mothership/request/lifecycle/durable-run'
3434
import {
3535
acquirePendingChatStream,
3636
getChatStreamLockOwners,

‎apps/sim/lib/mothership/async-runs/repository.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,10 @@ import {
6363
traceMothershipTransaction,
6464
} from '@/lib/mothership/observability/database'
6565
import {
66-
type BillingAdmission,
6766
isDurableStreamRun,
6867
parseRecordedSecretAuthority,
69-
} from '@/lib/mothership/request/lifecycle/recovery-config'
68+
} from '@/lib/mothership/request/lifecycle/durable-run'
69+
import type { BillingAdmission } from '@/lib/mothership/request/lifecycle/recovery-config'
7070
import { markSpanForError } from '@/lib/mothership/request/otel'
7171
import { sealClientToolSettlement } from '@/lib/mothership/request/tools/client-completion-seal.server'
7272
import {

‎apps/sim/lib/mothership/chat/stream-liveness.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { copilotChats, copilotRuns } from '@sim/db/schema'
33
import { createLogger } from '@sim/logger'
44
import { toError } from '@sim/utils/errors'
55
import { and, eq, inArray, notInArray } from 'drizzle-orm'
6-
import { isDurableStreamRun } from '@/lib/mothership/request/lifecycle/recovery-config'
6+
import { isDurableStreamRun } from '@/lib/mothership/request/lifecycle/durable-run'
77
import { getChatStreamLockOwners } from '@/lib/mothership/request/session'
88

99
const logger = createLogger('ChatStreamLiveness')

‎apps/sim/lib/mothership/request/application/recover-stream.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ import {
3030
claimRunController,
3131
planRecovery,
3232
} from '@/lib/mothership/request/lifecycle/controller-ownership'
33+
import { parseRecordedSecretAuthority } from '@/lib/mothership/request/lifecycle/durable-run'
3334
import {
34-
parseRecordedSecretAuthority,
3535
RESOURCE_AUTHORING_VERSION,
3636
StreamRecoveryConfigSchema,
3737
} from '@/lib/mothership/request/lifecycle/recovery-config'
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* What a run's saved request context says about its durability, kept free of the chat protocol
3+
* schema so the run repository and the sweeps can read it without loading the lifecycle.
4+
*/
5+
import { type AnyColumn, type SQL, sql } from 'drizzle-orm'
6+
import { z } from 'zod'
7+
import { secretMountPolicySchema } from '@/lib/api/contracts/secret-mount-policy'
8+
import type { TurnSecretAuthority } from '@/lib/mothership/secret-mount-policy'
9+
10+
/**
11+
* What a durable turn's recovery restores about its audience. An interactive stream answers a
12+
* person in the chat; an unattended stream (a scheduled job's occurrence) has nobody to answer a
13+
* prompt, so a recovered leg runs it non-interactively too, exactly as it was admitted.
14+
*/
15+
export const DURABLE_STREAM_KINDS = ['interactive_stream', 'unattended_stream'] as const
16+
export type DurableStreamKind = (typeof DURABLE_STREAM_KINDS)[number]
17+
18+
/** A run's saved request context names a durable turn that recovery can take over. */
19+
export function isDurableStreamRun(requestContext: AnyColumn): SQL {
20+
return sql`${requestContext}->'recovery'->>'kind' IN (${sql.join(
21+
DURABLE_STREAM_KINDS.map((kind) => sql`${kind}`),
22+
sql`, `
23+
)})`
24+
}
25+
26+
const RecordedSecretAuthoritySchema = z.object({
27+
secretMountPolicy: secretMountPolicySchema,
28+
secretActorUserId: z.string().nullable(),
29+
})
30+
31+
/**
32+
* The secret authority a run recorded at admission (`requestContext.secretAuthority`), or
33+
* undefined when it recorded none or garbage.
34+
*/
35+
export function parseRecordedSecretAuthority(value: unknown): TurnSecretAuthority | undefined {
36+
const recorded = RecordedSecretAuthoritySchema.safeParse(value)
37+
return recorded.success ? recorded.data : undefined
38+
}

‎apps/sim/lib/mothership/request/lifecycle/recovery-config.ts‎

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
import { type AnyColumn, type SQL, sql } from 'drizzle-orm'
21
import { z } from 'zod'
3-
import { secretMountPolicySchema } from '@/lib/api/contracts/secret-mount-policy'
42
import { hasCopilotResourceAdmission } from '@/lib/mothership/auth/application-delegation'
53
import { COPILOT_REQUEST_MODES } from '@/lib/mothership/constants'
64
import { ChatPayloadSchema } from '@/lib/mothership/generated/protocol'
5+
import { DURABLE_STREAM_KINDS } from '@/lib/mothership/request/lifecycle/durable-run'
76
import type { CopilotLifecycleOptions } from '@/lib/mothership/request/lifecycle/run'
8-
import type { TurnSecretAuthority } from '@/lib/mothership/secret-mount-policy'
97

108
export const BillingAdmissionSchema = z
119
.object({
@@ -40,22 +38,6 @@ export function durableResourceAuthoringVersion(
4038
return admitted ? RESOURCE_AUTHORING_VERSION : undefined
4139
}
4240

43-
/**
44-
* What a durable turn's recovery restores about its audience. An interactive stream answers a
45-
* person in the chat; an unattended stream (a scheduled job's occurrence) has nobody to answer a
46-
* prompt, so a recovered leg runs it non-interactively too, exactly as it was admitted.
47-
*/
48-
const DURABLE_STREAM_KINDS = ['interactive_stream', 'unattended_stream'] as const
49-
export type DurableStreamKind = (typeof DURABLE_STREAM_KINDS)[number]
50-
51-
/** A run's saved request context names a durable turn that recovery can take over. */
52-
export function isDurableStreamRun(requestContext: AnyColumn): SQL {
53-
return sql`${requestContext}->'recovery'->>'kind' IN (${sql.join(
54-
DURABLE_STREAM_KINDS.map((kind) => sql`${kind}`),
55-
sql`, `
56-
)})`
57-
}
58-
5941
export const StreamRecoveryConfigSchema = z
6042
.object({
6143
kind: z.enum(DURABLE_STREAM_KINDS),
@@ -109,17 +91,3 @@ export async function streamRecoveryConfig(
10991
...(resourceAuthoringVersion ? { resourceAuthoringVersion } : {}),
11092
})
11193
}
112-
113-
const RecordedSecretAuthoritySchema = z.object({
114-
secretMountPolicy: secretMountPolicySchema,
115-
secretActorUserId: z.string().nullable(),
116-
})
117-
118-
/**
119-
* The secret authority a run recorded at admission (`requestContext.secretAuthority`), or
120-
* undefined when it recorded none or garbage.
121-
*/
122-
export function parseRecordedSecretAuthority(value: unknown): TurnSecretAuthority | undefined {
123-
const recorded = RecordedSecretAuthoritySchema.safeParse(value)
124-
return recorded.success ? recorded.data : undefined
125-
}

‎apps/sim/lib/mothership/request/lifecycle/run.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ import {
6060
authorizeLifecycleContinuation,
6161
restoreBillingAdmission,
6262
} from '@/lib/mothership/request/lifecycle/admission'
63-
import type { DurableStreamKind } from '@/lib/mothership/request/lifecycle/recovery-config'
63+
import type { DurableStreamKind } from '@/lib/mothership/request/lifecycle/durable-run'
6464
import { StreamRetryWindow } from '@/lib/mothership/request/lifecycle/stream-retry'
6565
import { recordDegraded } from '@/lib/mothership/request/metrics'
6666
import { AbortReason } from '@/lib/mothership/request/session/abort-reason'

0 commit comments

Comments
 (0)