mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
* fix(native-chat): validate saved next-fence floors before use * WIP: separate saved conversation observations from runtime proof * Consolidate duplicate imports for WIP compiler checkpoint * Preserve saved conversation observations with strict runtime authority * Consolidate session admission and catalog imports * fix: align conversation observation contracts and fixtures * test: update conversation observation and writer fixtures * test: use checked host fixtures for structured workers * fix(native-chat): bound conversation projection work per request * test(native-chat): consolidate scale fixture imports * test(native-chat): use checked scale fixture types * test(native-chat): bound unreadable inventory traversal * Test native CLI admission and durable reference reads * test: bound cold conversation name subscription work * test: distinguish native receipt probes from terminal identity * test: retain exact mutation refusal recovery identity * fix: explain agent conversation preparation refusals * test: adapt remaining conversation readers and document CLI timeout * fix: register preparation refusal English message * test: align conversation fixtures with checked read APIs * test(native-chat): use checked conversation in Codex fixture
248 lines
10 KiB
TypeScript
248 lines
10 KiB
TypeScript
// Compose shared admission, cwd, environment, visuals and child sealing; agent parts add only agent-specific inputs and never set cwd/sealed env.
|
|
|
|
import type { AgentSessionJournalIdentity } from '../../shared/agent-session-journal-types'
|
|
import type { AgentChatPermissionChoice } from '../../shared/agent-chat-permission-mode'
|
|
import type { AgentSessionRecord } from '../../shared/agent-session-record'
|
|
import { isNativeLocalLocation } from '../../shared/execution-host'
|
|
import type { StructuredAgentDefinition } from '../native-chat/agent-session-wire/structured-agent-definition'
|
|
import { agentDrivesSession } from '../native-chat/agent-session-wire/structured-agent-session-provider-support'
|
|
import type { StructuredAgentCommandSettings } from '../native-chat/structured-agent-command-resolution'
|
|
import {
|
|
createNativeChatVisualsDelivery,
|
|
type NativeChatVisualsLaunch,
|
|
type PrepareNativeChatVisuals
|
|
} from '../native-chat/native-chat-visuals-delivery'
|
|
import { resolveAgentSessionLaunchDirectory } from './agent-session-launch-directory'
|
|
import type { AgentSessionRecordStore } from './agent-session-record-store'
|
|
import type { StructuredAgentAdapterContext } from './structured-agent-runtime-registrations'
|
|
import { sealStructuredSessionChild } from './structured-session-child-env'
|
|
|
|
/** Where a launch's shared inputs come from. */
|
|
export type StructuredAgentLaunchSources = {
|
|
store: Pick<
|
|
AgentSessionRecordStore,
|
|
'selectWritableRecord' | 'requireWritableRecord' | 'pinLaunchDirectory'
|
|
>
|
|
resolveWorkspacePath: (workspaceId: string) => Promise<string>
|
|
resolveBaseEnvironment: () => Promise<Record<string, string>>
|
|
/** The base environment with the user's overlay for `agent` laid over it. */
|
|
resolveAgentEnvironment: (agent: string) => Promise<Record<string, string>>
|
|
/** The user's per-agent environment overlay from settings. */
|
|
resolveAgentLaunchEnv?: (agent: string) => Record<string, string>
|
|
resolveCommandSettings?: () => StructuredAgentCommandSettings
|
|
prepareVisuals?: PrepareNativeChatVisuals
|
|
/** Rereads the base environment after a lookup that started at `askedAt` found no program in
|
|
* it; absent never retries. */
|
|
refreshAfterMissingProgram?: (askedAt: number) => Promise<void>
|
|
}
|
|
|
|
/** Resolve each input lazily once in agent-defined order; refusals before directory reads leave floating records unpinned. */
|
|
export type StructuredAgentLaunchBasis = {
|
|
/** Exists, belongs to this agent, runs on this host, and pins what this agent drives. */
|
|
record: AgentSessionRecord
|
|
launchDirectory(): Promise<string>
|
|
baseEnvironment(): Promise<Record<string, string>>
|
|
/** The user's overlay for this agent; empty when none. */
|
|
launchEnv(): Record<string, string>
|
|
/** The base environment with the user's overlay laid over it. */
|
|
environment(): Promise<Record<string, string>>
|
|
/** Null when the chat gets no visuals. */
|
|
visuals(): Promise<NativeChatVisualsLaunch | null>
|
|
commandSettings(): StructuredAgentCommandSettings
|
|
/** After a missing program, refresh both environment reads together; absent means no retry. */
|
|
refreshAfterMissingProgram?: (askedAt: number) => Promise<void>
|
|
/** The permission mode the host decided this start runs with; absent asks. */
|
|
permissionMode?: AgentChatPermissionChoice
|
|
}
|
|
|
|
export type StructuredLaunchRequest = {
|
|
identity: AgentSessionJournalIdentity
|
|
/** The reservation's token, which the seal hands the child. */
|
|
spawnToken: string
|
|
permissionMode?: AgentChatPermissionChoice
|
|
}
|
|
|
|
/** What the composition sets on a launch; an agent's part never does. */
|
|
type ComposedLaunchFields = 'cwd' | 'env' | 'envToDelete' | 'commandEnv'
|
|
|
|
export type StructuredAgentLaunchPart<L> = {
|
|
launch: Omit<L, ComposedLaunchFields>
|
|
/** The agent's environment, before the seal names the child. */
|
|
env: Record<string, string>
|
|
/** Inherited keys this agent's child must not keep; required, so an agent states even none. */
|
|
inheritedEnvToDelete: readonly string[]
|
|
/** See `sealStructuredSessionChild`. */
|
|
cliRuntimeCommand?: string
|
|
}
|
|
|
|
/** An agent's own part of a launch; it sees the session, never the spawn token. */
|
|
export type StructuredAgentLaunchPartResolver<L> = (
|
|
basis: StructuredAgentLaunchBasis,
|
|
identity: AgentSessionJournalIdentity
|
|
) => Promise<StructuredAgentLaunchPart<L>>
|
|
|
|
export type ComposedStructuredLaunch<L> = Omit<L, ComposedLaunchFields> & {
|
|
cwd: string
|
|
env: Record<string, string>
|
|
envToDelete: string[]
|
|
/** See `SealedStructuredSessionChild`; absent from a read, which seals no child. */
|
|
commandEnv?: Record<string, string>
|
|
}
|
|
|
|
/** The child to spawn, sealed with the reservation's token. */
|
|
export type StructuredAgentLaunchResolver<L> = (
|
|
request: StructuredLaunchRequest
|
|
) => Promise<ComposedStructuredLaunch<L>>
|
|
|
|
/** The same launch for a read that starts no child: the agent's own env, unsealed. */
|
|
export type StructuredAgentLaunchReader<L> = (
|
|
identity: AgentSessionJournalIdentity
|
|
) => Promise<ComposedStructuredLaunch<L>>
|
|
|
|
function once<T>(read: () => T): () => T {
|
|
let result: { value: T } | null = null
|
|
return () => (result ??= { value: read() }).value
|
|
}
|
|
|
|
function admittedRecord(
|
|
definition: StructuredAgentDefinition,
|
|
store: StructuredAgentLaunchSources['store'],
|
|
sessionId: string
|
|
): AgentSessionRecord {
|
|
if (store.selectWritableRecord(sessionId).kind === 'missing') {
|
|
throw new Error(`no durable agent-session record for ${sessionId}`)
|
|
}
|
|
const record = store.requireWritableRecord(sessionId)
|
|
if (record.provider !== definition.agent) {
|
|
throw new Error(`session ${sessionId} is a ${record.provider} session`)
|
|
}
|
|
const { location } = record
|
|
// A session pinned elsewhere belongs to that host; local launch would create a second provider-session writer.
|
|
if (!isNativeLocalLocation(location)) {
|
|
throw new Error(
|
|
`${definition.agent} structured sessions run on the local host, not ${location.executionHostId}`
|
|
)
|
|
}
|
|
const agents = { definition: (agent: string) => (agent === definition.agent ? definition : null) }
|
|
if (!agentDrivesSession(agents, record)) {
|
|
const pin = definition.accountHomeVariable ?? definition.accountLocatorKind
|
|
throw new Error(
|
|
`session ${sessionId}'s account home or handles are not ${definition.agent}'s: its sessions pin ${pin} and hold ${definition.handleTransport} handles`
|
|
)
|
|
}
|
|
return record
|
|
}
|
|
|
|
async function composedPart<L>(
|
|
definition: StructuredAgentDefinition,
|
|
sources: StructuredAgentLaunchSources,
|
|
part: StructuredAgentLaunchPartResolver<L>,
|
|
identity: AgentSessionJournalIdentity,
|
|
permissionMode?: AgentChatPermissionChoice
|
|
): Promise<StructuredAgentLaunchPart<L> & { cwd: string }> {
|
|
const record = admittedRecord(definition, sources.store, identity.sessionId)
|
|
const launchEnv = once(() => sources.resolveAgentLaunchEnv?.(definition.agent) ?? {})
|
|
const readEnvironments = () => ({
|
|
base: once(sources.resolveBaseEnvironment),
|
|
agent: once(() => sources.resolveAgentEnvironment(definition.agent))
|
|
})
|
|
let environments = readEnvironments()
|
|
const refresh = sources.refreshAfterMissingProgram
|
|
const basis: StructuredAgentLaunchBasis = {
|
|
record,
|
|
launchDirectory: once(() => resolveAgentSessionLaunchDirectory(sources, record)),
|
|
baseEnvironment: () => environments.base(),
|
|
launchEnv,
|
|
environment: () => environments.agent(),
|
|
visuals: once(async () => (await sources.prepareVisuals?.(record.sessionId)) ?? null),
|
|
commandSettings: once(() => sources.resolveCommandSettings?.() ?? {}),
|
|
...(refresh
|
|
? {
|
|
refreshAfterMissingProgram: async (askedAt) => {
|
|
await refresh(askedAt)
|
|
environments = readEnvironments()
|
|
}
|
|
}
|
|
: {}),
|
|
...(permissionMode ? { permissionMode } : {})
|
|
}
|
|
const composed = await part(basis, identity)
|
|
return { ...composed, cwd: await basis.launchDirectory() }
|
|
}
|
|
|
|
export function composeStructuredLaunch<L>(
|
|
definition: StructuredAgentDefinition,
|
|
sources: StructuredAgentLaunchSources,
|
|
part: StructuredAgentLaunchPartResolver<L>
|
|
): StructuredAgentLaunchResolver<L> {
|
|
return async ({ identity, spawnToken, permissionMode }) => {
|
|
const { launch, cwd, env, inheritedEnvToDelete, cliRuntimeCommand } = await composedPart(
|
|
definition,
|
|
sources,
|
|
part,
|
|
identity,
|
|
permissionMode
|
|
)
|
|
admittedRecord(definition, sources.store, identity.sessionId)
|
|
const child = sealStructuredSessionChild({
|
|
sessionId: identity.sessionId,
|
|
spawnToken,
|
|
env,
|
|
inheritedEnvToDelete,
|
|
...(cliRuntimeCommand ? { cliRuntimeCommand } : {})
|
|
})
|
|
return {
|
|
...launch,
|
|
cwd,
|
|
env: child.env,
|
|
envToDelete: child.envToDelete,
|
|
commandEnv: child.commandEnv
|
|
}
|
|
}
|
|
}
|
|
|
|
export function composeStructuredLaunchRead<L>(
|
|
definition: StructuredAgentDefinition,
|
|
sources: StructuredAgentLaunchSources,
|
|
part: StructuredAgentLaunchPartResolver<L>
|
|
): StructuredAgentLaunchReader<L> {
|
|
return async (identity) => {
|
|
const { launch, cwd, env, inheritedEnvToDelete } = await composedPart(
|
|
definition,
|
|
sources,
|
|
part,
|
|
identity
|
|
)
|
|
admittedRecord(definition, sources.store, identity.sessionId)
|
|
return { ...launch, cwd, env, envToDelete: [...inheritedEnvToDelete] }
|
|
}
|
|
}
|
|
|
|
/** The shared launch inputs of the runtime an agent's adapter is built in. */
|
|
export function structuredAgentLaunchSources(
|
|
context: Pick<StructuredAgentAdapterContext, 'deps' | 'store' | 'environment'>
|
|
): StructuredAgentLaunchSources {
|
|
const { deps } = context
|
|
return {
|
|
store: context.store,
|
|
resolveWorkspacePath: deps.resolveWorkspacePath,
|
|
resolveBaseEnvironment: context.environment.resolveBaseEnvironment,
|
|
resolveAgentEnvironment: context.environment.resolveAgentEnvironment,
|
|
refreshAfterMissingProgram: context.environment.refreshAfterMissingProgram,
|
|
...(deps.resolveAgentLaunchEnv ? { resolveAgentLaunchEnv: deps.resolveAgentLaunchEnv } : {}),
|
|
...(deps.resolveAgentCommandSettings
|
|
? { resolveCommandSettings: deps.resolveAgentCommandSettings }
|
|
: {}),
|
|
// Wired by the real hosts only, so a test runtime never loads the bundled skill.
|
|
...(deps.nativeChatVisuals
|
|
? {
|
|
prepareVisuals: createNativeChatVisualsDelivery({
|
|
stateDirectory: deps.stateDirectory,
|
|
logger: deps.logger,
|
|
isEnabled: deps.nativeChatVisuals.isEnabled
|
|
})
|
|
}
|
|
: {})
|
|
}
|
|
}
|