Split two files back under the line cap after the merge

Merging main put both one effective line over 300, and the cap forbids a
disable or a shave. The wire module's refusal vocabulary moves to its own file
and is re-exported, so its consumers are untouched; the host's four thin
mutation delegates move next to the functions they call.
This commit is contained in:
Merge Sim
2026-09-10 11:37:27 -07:00
parent 2bfbae0683
commit 3149caaa17
4 changed files with 77 additions and 65 deletions
@@ -193,3 +193,25 @@ export async function settleStructuredAgentSessionLateDispatch(
})
context.publish(input.sessionId, session.journal)
}
/** The host's thin mutation surface. Each call re-reads the context, so a session
* map or fence that moves between calls is never captured by a stale closure. */
export function structuredAgentSessionMutationDelegates(
context: () => StructuredAgentSessionMutationContext
) {
return {
cancel: (
caller: StructuredAgentSessionCaller,
params: Parameters<typeof cancelStructuredAgentSessionTurn>[2]
) => cancelStructuredAgentSessionTurn(context(), caller, params),
respondToPrompt: (
caller: StructuredAgentSessionCaller,
params: Parameters<typeof respondToStructuredAgentSessionPrompt>[2]
) => respondToStructuredAgentSessionPrompt(context(), caller, params),
setOption: (
caller: StructuredAgentSessionCaller,
params: Parameters<typeof setStructuredAgentSessionOption>[2]
) => setStructuredAgentSessionOption(context(), caller, params),
readOptions: (sessionId: string) => readStructuredAgentSessionOptions(context(), sessionId)
}
}
@@ -36,10 +36,7 @@ import type {
import type { StructuredAgentSessionAttachContext } from './structured-agent-session-attach-context'
import { listStructuredAgentSessionTabs } from './structured-agent-session-host-tabs'
import {
cancelStructuredAgentSessionTurn,
readStructuredAgentSessionOptions,
respondToStructuredAgentSessionPrompt,
setStructuredAgentSessionOption,
structuredAgentSessionMutationDelegates,
settleStructuredAgentSessionLateDispatch,
type StructuredAgentSessionMutationContext
} from './structured-agent-session-host-mutations'
@@ -283,23 +280,11 @@ export class StructuredAgentSessionHost {
send = (...args: Parameters<StructuredConversationCommandController['send']>) =>
this.conversationCommands.send(...args)
cancel = (
caller: StructuredAgentSessionCaller,
params: Parameters<typeof cancelStructuredAgentSessionTurn>[2]
): ReturnType<typeof cancelStructuredAgentSessionTurn> =>
cancelStructuredAgentSessionTurn(this.mutationContext(), caller, params)
respondToPrompt = (
caller: StructuredAgentSessionCaller,
params: Parameters<typeof respondToStructuredAgentSessionPrompt>[2]
): ReturnType<typeof respondToStructuredAgentSessionPrompt> =>
respondToStructuredAgentSessionPrompt(this.mutationContext(), caller, params)
setOption = (
caller: StructuredAgentSessionCaller,
params: Parameters<typeof setStructuredAgentSessionOption>[2]
): ReturnType<typeof setStructuredAgentSessionOption> =>
setStructuredAgentSessionOption(this.mutationContext(), caller, params)
private mutations = structuredAgentSessionMutationDelegates(() => this.mutationContext())
cancel = this.mutations.cancel
respondToPrompt = this.mutations.respondToPrompt
setOption = this.mutations.setOption
readOptions = this.mutations.readOptions
requestHandoff = (
caller: StructuredAgentSessionCaller,
@@ -307,9 +292,6 @@ export class StructuredAgentSessionHost {
): Promise<SessionWire.AgentSessionMutationResult<SessionWire.AgentSessionHandoffResult>> =>
this.handoffs.request(caller.callerKey, params)
readOptions = (sessionId: string): Promise<SessionWire.AgentSessionOptionsResult> =>
readStructuredAgentSessionOptions(this.mutationContext(), sessionId)
rewind = (caller: StructuredAgentSessionCaller, params: AgentSessionRewindParams) =>
rewindStructuredAgentSession(this.mutationContext(), this.attachContext(), caller, params)
+46
View File
@@ -0,0 +1,46 @@
// How the host declines an `agentSession.*` mutation: the closed code list, its
// narrowing guard, and the refusal body a client reads.
import type { AgentJournalResolution } from './agent-session-journal-types'
import type { AgentSessionRewindReason } from './agent-session-rewind'
export const AGENT_SESSION_WIRE_REFUSAL_CODES = [
'structured_agent_session_unsupported',
'agent_session_checkpoint_stale',
'agent_session_conflict',
'agent_session_ownership_unknown',
'agent_session_operation_conflict',
'agent_session_operation_expired',
'agent_session_operation_capacity',
'agent_session_operation_invalid',
'agent_session_operation_unknown',
'agent_session_item_revision_stale',
'agent_session_already_resolved',
'agent_session_identity_required',
'agent_session_journal_unreadable',
'execution_owner_reconciling'
] as const
export type AgentSessionWireRefusalCode = (typeof AGENT_SESSION_WIRE_REFUSAL_CODES)[number]
/** For a host path that raises its refusal as the thrown code. Narrowing through this keeps an
* unrelated fault from being reported to the client as a tidy, wrong refusal. */
export function isAgentSessionWireRefusalCode(
value: unknown
): value is AgentSessionWireRefusalCode {
return (
typeof value === 'string' &&
(AGENT_SESSION_WIRE_REFUSAL_CODES as readonly string[]).includes(value)
)
}
export type AgentSessionWireRefusal = {
rewindReason?: AgentSessionRewindReason
code: AgentSessionWireRefusalCode
message: string
/** On a stale fence, so the client can retry without another round trip. */
currentFence?: number
/** On a lost compare-and-set: the winning answer and who gave it. */
resolution?: AgentJournalResolution
/** On a lost compare-and-set: the revision the host actually holds. */
currentRevision?: number
}
+3 -41
View File
@@ -1,4 +1,7 @@
import type { AgentSessionRewindReason, AgentSessionRewindSupport } from './agent-session-rewind'
import type { AgentSessionWireRefusal } from './agent-session-wire-refusals'
export * from './agent-session-wire-refusals'
import type { AgentSessionConversationCommand } from './agent-session-conversation-command'
// ─── Structured agent-session wire contract ─────────────────────────────────
// The shapes `agentSession.*` accepts and publishes. Phase 2 builds provider
@@ -302,47 +305,6 @@ export type AgentSessionMutationEnvelope = {
payloadFingerprint: string
}
export const AGENT_SESSION_WIRE_REFUSAL_CODES = [
'structured_agent_session_unsupported',
'agent_session_checkpoint_stale',
'agent_session_conflict',
'agent_session_ownership_unknown',
'agent_session_operation_conflict',
'agent_session_operation_expired',
'agent_session_operation_capacity',
'agent_session_operation_invalid',
'agent_session_operation_unknown',
'agent_session_item_revision_stale',
'agent_session_already_resolved',
'agent_session_identity_required',
'agent_session_journal_unreadable',
'execution_owner_reconciling'
] as const
export type AgentSessionWireRefusalCode = (typeof AGENT_SESSION_WIRE_REFUSAL_CODES)[number]
/** For a host path that raises its refusal as the thrown code. Narrowing through this keeps an
* unrelated fault from being reported to the client as a tidy, wrong refusal. */
export function isAgentSessionWireRefusalCode(
value: unknown
): value is AgentSessionWireRefusalCode {
return (
typeof value === 'string' &&
(AGENT_SESSION_WIRE_REFUSAL_CODES as readonly string[]).includes(value)
)
}
export type AgentSessionWireRefusal = {
rewindReason?: AgentSessionRewindReason
code: AgentSessionWireRefusalCode
message: string
/** On a stale fence, so the client can retry without another round trip. */
currentFence?: number
/** On a lost compare-and-set: the winning answer and who gave it. */
resolution?: AgentJournalResolution
/** On a lost compare-and-set: the revision the host actually holds. */
currentRevision?: number
}
export type AgentSessionMutationResult<TValue> =
| {
ok: true