@mastra/factory 0.22.0 → 0.23.0-alpha.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.
Files changed (41) hide show
  1. package/dist/factory.d.ts.map +1 -1
  2. package/dist/factory.js +1 -0
  3. package/dist/factory.js.map +1 -1
  4. package/dist/routes/config.d.ts +2 -7
  5. package/dist/routes/config.d.ts.map +1 -1
  6. package/dist/routes/config.js +8 -26
  7. package/dist/routes/config.js.map +1 -1
  8. package/dist/routes/provider-credentials.d.ts +5 -4
  9. package/dist/routes/provider-credentials.d.ts.map +1 -1
  10. package/dist/routes/provider-credentials.js +1 -8
  11. package/dist/routes/provider-credentials.js.map +1 -1
  12. package/dist/routes/source-control-sessions.d.ts.map +1 -1
  13. package/dist/routes/source-control-sessions.js +11 -15
  14. package/dist/routes/source-control-sessions.js.map +1 -1
  15. package/dist/routes/surface.d.ts.map +1 -1
  16. package/dist/routes/surface.js +1 -0
  17. package/dist/routes/surface.js.map +1 -1
  18. package/dist/rules/dispatcher.d.ts.map +1 -1
  19. package/dist/rules/dispatcher.js +0 -2
  20. package/dist/rules/dispatcher.js.map +1 -1
  21. package/dist/rules/processor.js +1 -1
  22. package/dist/rules/processor.js.map +1 -1
  23. package/dist/session/default-model-hydration.d.ts +3 -3
  24. package/dist/session/default-model-hydration.d.ts.map +1 -1
  25. package/dist/session/default-model-hydration.js +3 -8
  26. package/dist/session/default-model-hydration.js.map +1 -1
  27. package/dist/session/run-audit.d.ts +1 -5
  28. package/dist/session/run-audit.d.ts.map +1 -1
  29. package/dist/session/run-audit.js +2 -8
  30. package/dist/session/run-audit.js.map +1 -1
  31. package/dist/session/work-item-repository.d.ts.map +1 -1
  32. package/dist/session/work-item-repository.js +21 -10
  33. package/dist/session/work-item-repository.js.map +1 -1
  34. package/dist/storage/domains/audit/base.d.ts +0 -1
  35. package/dist/storage/domains/audit/base.d.ts.map +1 -1
  36. package/dist/storage/domains/audit/base.js +1 -4
  37. package/dist/storage/domains/audit/base.js.map +1 -1
  38. package/dist/storage/domains/audit/domain.d.ts.map +1 -1
  39. package/dist/storage/domains/audit/domain.js +1 -3
  40. package/dist/storage/domains/audit/domain.js.map +1 -1
  41. package/package.json +6 -6
@@ -1 +1 @@
1
- {"version":3,"file":"processor.js","names":[],"sources":["../../src/rules/processor.ts"],"sourcesContent":["import { resolveRequestThinkingLevel } from '@mastra/code-sdk/agents/model';\nimport type { ThinkingLevel } from '@mastra/code-sdk/providers/openai-codex';\nimport type { MastraCodeState } from '@mastra/code-sdk/schema';\nimport type { MastraDBMessage, MessageList } from '@mastra/core/agent/message-list';\nimport type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport type {\n ComputeStateSignalArgs,\n ComputeStateSignalResult,\n ProcessInputArgs,\n ProcessInputStepArgs,\n Processor,\n} from '@mastra/core/processors';\n\nimport { boardForWorkItem, resolveBoardToolRule, workItemPhaseSemantics } from '../boards/index.js';\nimport type { BoardRegistry } from '../boards/index.js';\nimport type { FactoryRunBindingRecord, WorkItemsStorage, WorkItemRow } from '../storage/domains/work-items/base.js';\nimport { findSessionRunBinding, getFactorySessionCoordinates } from './binding-context.js';\nimport type { FactoryTransitionService } from './transition-service.js';\nimport { workItemSource } from './types.js';\nimport type {\n FactoryCommitDecision,\n FactoryRuleDecision,\n FactoryRuleJsonValue,\n FactoryRuleStage,\n FactoryToolResultRuleContext,\n} from './types.js';\nimport {\n assertFactoryDecisionTarget,\n normalizeFactoryRuleJsonValue,\n validateFactoryRuleDecisions,\n} from './validation.js';\n\nconst STATE_ID = 'factory-phase';\nconst RULE_TIMEOUT_MS = 5_000;\nconst TRANSCRIPT_PAGE_SIZE = 50;\nconst MAX_LINKED_ITEMS = 5;\nfunction ruleStage(boards: BoardRegistry, item: WorkItemRow | null | undefined): string | undefined {\n return item && workItemPhaseSemantics(boards, item) ? item.stages[0] : undefined;\n}\n\nfunction itemInRuleStage(boards: BoardRegistry, item: WorkItemRow | null | undefined): item is WorkItemRow {\n return ruleStage(boards, item) !== undefined;\n}\n\ntype PersistedMessageReader = {\n listMessages(input: {\n threadId: string;\n resourceId?: string;\n page: number;\n perPage: number;\n filter?: { dateRange?: { start?: Date } };\n orderBy: { field: 'createdAt'; direction: 'ASC' };\n }): Promise<{ messages: MastraDBMessage[]; hasMore: boolean }>;\n};\n\ntype CompletedToolResult = {\n assistantMessageId: string;\n messageCreatedAt: Date;\n toolCallId: string;\n toolName: string;\n input: FactoryRuleJsonValue;\n status: 'success' | 'error';\n value: FactoryRuleJsonValue;\n};\n\nfunction isApprovedPlanResult(result: CompletedToolResult): boolean {\n if (result.toolName !== 'submit_plan' || result.status !== 'success') return false;\n if (typeof result.value === 'string') return result.value.startsWith('Plan approved.');\n if (!result.value || typeof result.value !== 'object' || Array.isArray(result.value)) return false;\n const content = result.value.content;\n return typeof content === 'string' && content.startsWith('Plan approved.');\n}\n\ntype RuntimeSnapshot = {\n modelId: string;\n thinkingLevel: ThinkingLevel;\n};\n\ntype ActivePhaseSnapshotBase = {\n bindingId: string;\n itemId: string;\n revision: number;\n stage: FactoryRuleStage;\n role: string;\n configVersion: string;\n status: 'active';\n};\n\ntype ActivePhaseSnapshotValue = ActivePhaseSnapshotBase & {\n board: string;\n modelId?: string;\n thinkingLevel?: ThinkingLevel;\n};\n\ntype PhaseSnapshotValue = ActivePhaseSnapshotValue | { bindingId?: string; status: 'none' };\n\nfunction reviewRuntimeFromRequestContext(requestContext: ComputeStateSignalArgs['requestContext']): RuntimeSnapshot {\n if (!requestContext || typeof requestContext.get !== 'function') {\n throw new Error('Factory review phase requires a controller request context.');\n }\n const context = requestContext.get<'controller', AgentControllerRequestContext<MastraCodeState>>('controller');\n const modelId = context?.session?.modelId.trim();\n if (!modelId) {\n throw new Error('Factory review phase requires a selected session model.');\n }\n return { modelId, thinkingLevel: resolveRequestThinkingLevel(context) };\n}\n\nfunction workItemSourceKey(item: WorkItemRow): string | null {\n const source = item.externalSource;\n return source ? `${source.integrationId}:${source.type}:${source.externalId}` : null;\n}\n\nfunction boundedError(value: unknown): FactoryRuleJsonValue {\n const message = value instanceof Error ? value.message : typeof value === 'string' ? value : 'Tool execution failed.';\n return { message: message.slice(0, 2_000) };\n}\n\nfunction boundedResult(value: unknown): FactoryRuleJsonValue {\n try {\n return normalizeFactoryRuleJsonValue(value);\n } catch {\n return { message: 'Tool result was not serializable.' };\n }\n}\n\nfunction messageParts(message: MastraDBMessage): unknown[] {\n const content = message.content as { parts?: unknown[]; toolInvocations?: unknown[] } | unknown[] | undefined;\n if (Array.isArray(content)) return content;\n if (Array.isArray(content?.parts)) return content.parts;\n return Array.isArray(content?.toolInvocations) ? content.toolInvocations : [];\n}\n\nfunction completedStepToolCallIds(steps: unknown[]): Set<string> {\n const ids = new Set<string>();\n for (const rawStep of steps) {\n if (!rawStep || typeof rawStep !== 'object') continue;\n const toolResults = (rawStep as { toolResults?: unknown[] }).toolResults;\n if (!Array.isArray(toolResults)) continue;\n for (const rawResult of toolResults) {\n if (!rawResult || typeof rawResult !== 'object') continue;\n const toolCallId = (rawResult as { toolCallId?: unknown }).toolCallId;\n if (typeof toolCallId === 'string') ids.add(toolCallId);\n }\n }\n return ids;\n}\n\nfunction completedToolResults(message: MastraDBMessage): CompletedToolResult[] {\n if (message.role !== 'assistant') return [];\n const createdAt = message.createdAt instanceof Date ? message.createdAt : new Date(message.createdAt);\n const completed: CompletedToolResult[] = [];\n for (const rawPart of messageParts(message)) {\n if (!rawPart || typeof rawPart !== 'object') continue;\n const part = rawPart as Record<string, unknown>;\n const invocation =\n part.type === 'tool-invocation' && part.toolInvocation && typeof part.toolInvocation === 'object'\n ? (part.toolInvocation as Record<string, unknown>)\n : part;\n const state = invocation.state;\n if (state !== 'result' && state !== 'error') continue;\n const toolCallId = invocation.toolCallId;\n const toolName = invocation.toolName ?? invocation.name;\n if (typeof toolCallId !== 'string' || typeof toolName !== 'string') continue;\n completed.push({\n assistantMessageId: message.id,\n messageCreatedAt: createdAt,\n toolCallId,\n toolName: toolName.slice(0, 256),\n input: boundedResult(invocation.args ?? {}),\n status: state === 'error' ? 'error' : 'success',\n value: state === 'error' ? boundedError(invocation.result ?? invocation.error) : boundedResult(invocation.result),\n });\n }\n return completed;\n}\n\nfunction currentCompletedToolMessage(\n messages: MastraDBMessage[],\n toolCallIds: ReadonlySet<string>,\n): MastraDBMessage | undefined {\n for (let index = messages.length - 1; index >= 0; index -= 1) {\n const message = messages[index]!;\n if (completedToolResults(message).some(result => toolCallIds.has(result.toolCallId))) return message;\n }\n return undefined;\n}\n\nasync function phaseCacheKey(value: ActivePhaseSnapshotValue, linked: WorkItemRow[]): Promise<string> {\n const serialized = JSON.stringify({\n ...value,\n linked: linked.map(item => [item.id, item.revision, item.stages[0]]),\n });\n return Buffer.from(await globalThis.crypto.subtle.digest('SHA-256', new TextEncoder().encode(serialized))).toString(\n 'hex',\n );\n}\n\nfunction escapeText(value: string): string {\n return value.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');\n}\n\nfunction phaseFromSignal(signal: { metadata?: Record<string, unknown> } | undefined): PhaseSnapshotValue | undefined {\n return (signal?.metadata?.value as { phase?: PhaseSnapshotValue } | undefined)?.phase;\n}\n\nfunction latestPhase(args: ComputeStateSignalArgs): PhaseSnapshotValue | undefined {\n for (const signal of [...args.activeStateSignals].reverse()) {\n const phase = phaseFromSignal(signal);\n if (phase) return phase;\n }\n return phaseFromSignal(args.lastSnapshot);\n}\n\nasync function withRuleTimeout<T>(operation: Promise<T>): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n operation,\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new Error('FACTORY_RULE_TIMEOUT')), RULE_TIMEOUT_MS);\n }),\n ]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\nconst reportedUnavailableMemorySettings = new WeakSet<object>();\n\n/**\n * Marks this run's Factory memory settings as unavailable so memory falls back\n * to auto, and tells the thread once per request that saved choices were not\n * applied. The reserved request-context key keeps clients from spoofing it.\n */\nexport function reportMemorySettingsUnavailable(\n requestContext: NonNullable<ProcessInputArgs['requestContext']>,\n reason: string,\n): void {\n requestContext.set('mastra__factoryMemorySettings', { status: 'unavailable', reason });\n console.warn('[Factory Memory Settings] Failed to load settings for run', { error: reason });\n if (reportedUnavailableMemorySettings.has(requestContext)) return;\n reportedUnavailableMemorySettings.add(requestContext);\n const controller = requestContext.get('controller') as\n | Pick<AgentControllerRequestContext<MastraCodeState>, 'emitEvent'>\n | undefined;\n controller?.emitEvent?.({\n type: 'error',\n error: new Error(\n 'Memory settings could not be loaded for this run, so observational memory is using Auto models and default thresholds.',\n ),\n errorType: 'factory_memory_settings_unavailable',\n retryable: false,\n });\n}\n\nexport class FactoryPhaseStateProcessor implements Processor<'factory-phase'> {\n readonly id = STATE_ID;\n readonly stateId = STATE_ID;\n /** Request contexts whose caller row is already resolved for this request. */\n private readonly loadedMemorySettings = new WeakSet<object>();\n\n constructor(\n private readonly options: {\n configVersion: string;\n storage: WorkItemsStorage;\n boards: BoardRegistry;\n loadMemorySettings?: (args: {\n requestContext: ProcessInputStepArgs['requestContext'];\n binding: FactoryRunBindingRecord | null;\n }) => Promise<void>;\n transitionService?: Pick<FactoryTransitionService, 'transition'>;\n messageReader?: PersistedMessageReader;\n recordPullRequestProvenance?: (input: {\n binding: FactoryRunBindingRecord;\n item: WorkItemRow;\n assistantMessageId: string;\n toolCallId: string;\n toolName: string;\n toolInput: FactoryRuleJsonValue;\n toolResult: FactoryRuleJsonValue;\n status: 'success' | 'error';\n }) => Promise<void>;\n },\n ) {}\n\n /**\n * Memory settings are caller state, not run state. Factory invokes this while\n * resolving its configured processor list, before Agent resolves dynamic\n * Memory and enumerates Memory's processors. This guarantees both the Memory\n * options and its model callbacks see the authoritative row for this\n * invocation. Doing this in `processInputStep` would be too late: Memory's\n * input-step hook runs before configured processors.\n */\n async prepareMemorySettings(requestContext: ProcessInputArgs['requestContext']): Promise<void> {\n await this.loadCallerMemorySettings(requestContext, undefined, true);\n }\n\n async processInput(args: ProcessInputArgs): Promise<MessageList> {\n await this.loadCallerMemorySettings(args.requestContext);\n return args.messageList;\n }\n\n async processInputStep(args: ProcessInputStepArgs): Promise<MessageList | undefined> {\n const address = getFactorySessionCoordinates(args.requestContext);\n let binding: FactoryRunBindingRecord | null | undefined;\n try {\n binding = address ? await this.options.storage.findRunBindingBySession(address) : undefined;\n } catch {\n await this.loadCallerMemorySettings(args.requestContext, undefined);\n return;\n }\n // Resumed runs skip the input-processor phase, so re-check here: it is a\n // no-op once `processInput` already placed the row for this request. Passing\n // `undefined` (not `null`) keeps the recovery lookup inside the loader, so a\n // recovered session reaches its project row from this hook too.\n await this.loadCallerMemorySettings(args.requestContext, binding);\n if (!binding || binding.status !== 'active') return;\n const completedToolCallIds = completedStepToolCallIds(args.steps);\n const completedMessage = currentCompletedToolMessage(args.messages, completedToolCallIds);\n if (completedMessage) {\n await this.ingestMessages(binding, [completedMessage], completedToolCallIds);\n }\n }\n\n /**\n * Loads the caller's row once per invocation. The input hook always refreshes\n * it in case the controller reused a request context; later step hooks reuse\n * that value (`undefined` means it has not been loaded yet; `null` means an\n * empty row).\n *\n * The WeakSet covers loaders that do not set the context key themselves, so a\n * request is still loaded once. A failed load records an unavailable sentinel\n * (memory then runs both roles on auto with default thresholds) and is not\n * marked loaded so the next step can retry.\n */\n private async loadCallerMemorySettings(\n requestContext: ProcessInputStepArgs['requestContext'] | ProcessInputArgs['requestContext'],\n binding?: FactoryRunBindingRecord | null,\n force = false,\n ): Promise<void> {\n if (!requestContext || !this.options.loadMemorySettings) return;\n const currentSettings = requestContext.get('mastra__factoryMemorySettings') as\n | { status?: unknown }\n | null\n | undefined;\n if (force) this.loadedMemorySettings.delete(requestContext);\n if (!force && currentSettings !== undefined && currentSettings?.status !== 'unavailable') return;\n if (!force && this.loadedMemorySettings.has(requestContext)) return;\n try {\n let runBinding = binding ?? null;\n if (binding === undefined) {\n const address = getFactorySessionCoordinates(requestContext);\n runBinding = address\n ? await this.options.storage.findRunBindingBySession(address)\n : // Crash-recovered sessions come back with empty state, so the binding\n // table is the last source of the project whose row applies. Without\n // this the run would silently read the caller's personal row.\n await findSessionRunBinding(requestContext, this.options.storage);\n }\n await this.options.loadMemorySettings({ requestContext, binding: runBinding });\n this.loadedMemorySettings.add(requestContext);\n } catch (error) {\n reportMemorySettingsUnavailable(requestContext, error instanceof Error ? error.message : String(error));\n }\n }\n\n async computeStateSignal(args: ComputeStateSignalArgs): Promise<ComputeStateSignalResult> {\n const address = getFactorySessionCoordinates(args.requestContext);\n if (!address) return;\n const binding = await this.options.storage.findRunBindingBySession(address);\n const prior = latestPhase(args);\n const hasBase = Boolean(args.lastSnapshot) && args.contextWindow.hasSnapshot;\n\n if (!binding) return;\n if (binding.status !== 'active') {\n if (!hasBase || prior?.status !== 'active') return;\n return {\n id: STATE_ID,\n cacheKey: `factory:none:${prior.bindingId ?? 'revoked'}`,\n mode: 'snapshot',\n tagName: 'factory-phase',\n contents: '\\n',\n value: { phase: { status: 'none' } },\n attributes: { status: 'none' },\n metadata: { value: { phase: { status: 'none' } } },\n };\n }\n\n const item = await this.options.storage.get({ orgId: binding.orgId, id: binding.workItemId });\n const stage = ruleStage(this.options.boards, item);\n if (!item || !stage) return;\n const allItems = await this.options.storage.list({\n orgId: binding.orgId,\n factoryProjectId: binding.factoryProjectId,\n });\n const linked = allItems\n .filter(candidate => candidate.parentWorkItemId === item.id || item.parentWorkItemId === candidate.id)\n .slice(0, MAX_LINKED_ITEMS);\n const board = boardForWorkItem(item);\n const baseValue: ActivePhaseSnapshotBase = {\n status: 'active',\n bindingId: binding.id,\n itemId: item.id,\n revision: item.revision,\n stage,\n role: binding.role,\n configVersion: this.options.configVersion,\n };\n const value: ActivePhaseSnapshotValue =\n board === 'review'\n ? { ...baseValue, board, ...reviewRuntimeFromRequestContext(args.requestContext) }\n : { ...baseValue, board };\n const cacheKey = await phaseCacheKey(value, linked);\n if (hasBase && (args.tracking?.currentCacheKey ?? args.lastSnapshot?.metadata?.state?.cacheKey) === cacheKey)\n return;\n\n const linkedText = linked.length\n ? `\\nLinked items: ${linked.map(candidate => `${workItemSource(candidate.externalSource)} ${candidate.title}`).join('; ')}`\n : '';\n const phaseLabel = this.options.boards.get(board)?.phases[stage]?.title ?? stage;\n const runtime =\n value.modelId && value.thinkingLevel ? { modelId: value.modelId, thinkingLevel: value.thinkingLevel } : null;\n const snapshotContents =\n `Factory ${board} phase: ${escapeText(phaseLabel)} (${escapeText(stage)})\\n` +\n `Work item: ${escapeText(item.title)} (${item.id})\\n` +\n `Role: ${escapeText(binding.role)}\\nRevision: ${item.revision}\\nConfig: ${escapeText(this.options.configVersion)}\\n` +\n (runtime\n ? `Runtime: model=${escapeText(runtime.modelId)}, reasoning-setting=${escapeText(runtime.thinkingLevel)}\\n`\n : '') +\n ((board === 'work' || board === 'review') && stage === 'intake'\n ? 'This card rests in Intake: its work is paused. Answer questions without moving it; when the user asks to resume, request the transition into the working stage first, then continue the work in this session.\\n'\n : '') +\n `Use factory_transition_work_item with expectedRevision ${item.revision} to request a phase change.${escapeText(linkedText)}`;\n const isDelta = hasBase && prior?.status === 'active';\n return {\n id: STATE_ID,\n cacheKey,\n mode: isDelta ? 'delta' : 'snapshot',\n tagName: 'factory-phase',\n contents: isDelta ? `Factory phase update:\\n${snapshotContents}` : snapshotContents,\n value: { phase: value },\n ...(isDelta ? { delta: { phase: value } } : {}),\n attributes: {\n status: 'active',\n board,\n stage,\n role: binding.role,\n revision: item.revision,\n ...(runtime ?? {}),\n },\n metadata: { value: { phase: value } },\n };\n }\n\n async reconcileAllBoundThreads(): Promise<void> {\n if (!this.options.messageReader) return;\n const bindings = await this.options.storage.listActiveRunBindings();\n for (const binding of bindings) await this.reconcileBinding(binding);\n }\n\n async reconcileBinding(binding: FactoryRunBindingRecord): Promise<void> {\n const reader = this.options.messageReader;\n if (!reader || binding.status !== 'active') return;\n // One keyed read up front: a binding whose item is gone or no longer in a\n // single rule stage would ingest nothing, so skip the cursor and message\n // reads entirely instead of paying them on every walk.\n const item = await this.options.storage.get({ orgId: binding.orgId, id: binding.workItemId });\n if (!itemInRuleStage(this.options.boards, item)) return;\n const cursor = await this.options.storage.getToolResultCursor(binding.orgId, binding.factoryProjectId, binding.id);\n let page = 0;\n while (true) {\n const result = await reader.listMessages({\n threadId: binding.threadId,\n resourceId: binding.resourceId,\n page,\n perPage: TRANSCRIPT_PAGE_SIZE,\n ...(cursor ? { filter: { dateRange: { start: cursor.lastMessageCreatedAt } } } : {}),\n orderBy: { field: 'createdAt', direction: 'ASC' },\n });\n await this.ingestMessages(binding, result.messages, undefined, item);\n const last = result.messages.at(-1);\n if (last) {\n await this.options.storage.advanceToolResultCursor({\n bindingId: binding.id,\n orgId: binding.orgId,\n factoryProjectId: binding.factoryProjectId,\n lastMessageId: last.id,\n lastMessageCreatedAt: last.createdAt instanceof Date ? last.createdAt : new Date(last.createdAt),\n updatedAt: new Date(),\n });\n }\n if (!result.hasMore) break;\n page += 1;\n }\n }\n\n private async ingestMessages(\n binding: FactoryRunBindingRecord,\n messages: MastraDBMessage[],\n toolCallIds?: ReadonlySet<string>,\n preloadedItem?: WorkItemRow,\n ): Promise<void> {\n const item = preloadedItem ?? (await this.options.storage.get({ orgId: binding.orgId, id: binding.workItemId }));\n if (!itemInRuleStage(this.options.boards, item)) return;\n for (const message of messages) {\n for (const toolResult of completedToolResults(message)) {\n if (toolCallIds && !toolCallIds.has(toolResult.toolCallId)) continue;\n try {\n await this.options.recordPullRequestProvenance?.({\n binding,\n item,\n assistantMessageId: toolResult.assistantMessageId,\n toolCallId: toolResult.toolCallId,\n toolName: toolResult.toolName,\n toolInput: toolResult.input,\n toolResult: toolResult.value,\n status: toolResult.status,\n });\n } catch {\n // Provenance is supporting evidence and must not block authoritative rule ingress.\n }\n await this.ingestToolResult(binding, item, toolResult);\n }\n }\n }\n\n private async ingestToolResult(\n binding: FactoryRunBindingRecord,\n item: WorkItemRow,\n toolResult: CompletedToolResult,\n ): Promise<void> {\n const board = boardForWorkItem(item);\n const rule = resolveBoardToolRule(this.options.boards, board, toolResult.toolName);\n if (!rule) return;\n const ingressId = JSON.stringify([\n binding.id,\n binding.threadId,\n toolResult.assistantMessageId,\n toolResult.toolCallId,\n ]);\n const prior = await this.options.storage.getTransitionResultByIngress(\n binding.orgId,\n binding.factoryProjectId,\n ingressId,\n );\n if (prior) return;\n const context: FactoryToolResultRuleContext = {\n tenant: { orgId: binding.orgId, projectId: binding.factoryProjectId },\n actor: { type: 'agent', bindingId: binding.id, role: binding.role },\n ingress: { type: 'toolResult', id: ingressId },\n cause: `Completed ${toolResult.toolName}`,\n causalChain: [],\n configVersion: this.options.configVersion,\n item: {\n id: item.id,\n source: workItemSource(item.externalSource),\n sourceKey: workItemSourceKey(item),\n parentWorkItemId: item.parentWorkItemId,\n title: item.title,\n url: item.externalSource?.url ?? null,\n stages: item.stages,\n acceptedAt: item.acceptedAt,\n metadata: item.metadata,\n },\n board,\n itemRevision: item.revision,\n toolName: toolResult.toolName,\n threadId: binding.threadId,\n assistantMessageId: toolResult.assistantMessageId,\n toolCallId: toolResult.toolCallId,\n result: { status: toolResult.status, value: toolResult.value },\n };\n\n let decision: FactoryRuleDecision | void = undefined;\n let decisions: FactoryCommitDecision[] = [];\n let outcome: { status: 'accepted' | 'rejected'; code?: string; reason?: string } = { status: 'accepted' };\n try {\n decision = await withRuleTimeout(Promise.resolve(rule(Object.freeze(context))));\n if (decision?.type === 'reject') {\n outcome = { status: 'rejected', code: decision.code, reason: decision.reason };\n } else if (decision) {\n const validated = validateFactoryRuleDecisions([decision]);\n for (const entry of validated) assertFactoryDecisionTarget(entry, this.options.boards, board);\n decisions = validated;\n }\n } catch (error) {\n const timedOut = error instanceof Error && error.message === 'FACTORY_RULE_TIMEOUT';\n outcome = {\n status: 'rejected',\n code: timedOut ? 'timeout' : 'rule_error',\n reason: timedOut\n ? 'Factory rule evaluation timed out.'\n : error instanceof Error\n ? error.message.slice(0, 2_000)\n : 'Factory tool-result rule failed.',\n };\n }\n const committed = await this.options.storage.commitRuleEvaluation({\n orgId: binding.orgId,\n factoryProjectId: binding.factoryProjectId,\n workItemId: item.id,\n ingress: { identity: ingressId, triggerType: 'tool.result' },\n configVersion: this.options.configVersion,\n expectedRevision: item.revision,\n actor: { ...context.actor },\n outcome,\n decisions: decisions.map(entry => ({ ...entry })),\n causalChain: [],\n now: new Date(),\n });\n if (committed.status !== 'committed' || !this.options.transitionService) return;\n for (const entry of decisions) {\n if (entry.type !== 'transition') continue;\n await this.options.transitionService.transition({\n orgId: binding.orgId,\n factoryProjectId: binding.factoryProjectId,\n workItemId: item.id,\n board: entry.board,\n stage: entry.stage,\n expectedRevision: item.revision,\n actor: { type: 'system', id: 'factory-tool-result-rule' },\n ingress: { type: 'rule', identity: `decision:${entry.idempotencyKey}` },\n cause: 'tool_result_rule',\n causalChain: [{ ingressId, decisionType: entry.type }],\n ...(isApprovedPlanResult(toolResult) ? { planApproved: true } : {}),\n });\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"processor.js","names":[],"sources":["../../src/rules/processor.ts"],"sourcesContent":["import { resolveRequestThinkingLevel } from '@mastra/code-sdk/agents/model';\nimport type { ThinkingLevel } from '@mastra/code-sdk/providers/openai-codex';\nimport type { MastraCodeState } from '@mastra/code-sdk/schema';\nimport type { MastraDBMessage, MessageList } from '@mastra/core/agent/message-list';\nimport type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport type {\n ComputeStateSignalArgs,\n ComputeStateSignalResult,\n ProcessInputArgs,\n ProcessInputStepArgs,\n Processor,\n} from '@mastra/core/processors';\n\nimport { boardForWorkItem, resolveBoardToolRule, workItemPhaseSemantics } from '../boards/index.js';\nimport type { BoardRegistry } from '../boards/index.js';\nimport type { FactoryRunBindingRecord, WorkItemsStorage, WorkItemRow } from '../storage/domains/work-items/base.js';\nimport { findSessionRunBinding, getFactorySessionCoordinates } from './binding-context.js';\nimport type { FactoryTransitionService } from './transition-service.js';\nimport { workItemSource } from './types.js';\nimport type {\n FactoryCommitDecision,\n FactoryRuleDecision,\n FactoryRuleJsonValue,\n FactoryRuleStage,\n FactoryToolResultRuleContext,\n} from './types.js';\nimport {\n assertFactoryDecisionTarget,\n normalizeFactoryRuleJsonValue,\n validateFactoryRuleDecisions,\n} from './validation.js';\n\nconst STATE_ID = 'factory-phase';\nconst RULE_TIMEOUT_MS = 5_000;\nconst TRANSCRIPT_PAGE_SIZE = 50;\nconst MAX_LINKED_ITEMS = 5;\nfunction ruleStage(boards: BoardRegistry, item: WorkItemRow | null | undefined): string | undefined {\n return item && workItemPhaseSemantics(boards, item) ? item.stages[0] : undefined;\n}\n\nfunction itemInRuleStage(boards: BoardRegistry, item: WorkItemRow | null | undefined): item is WorkItemRow {\n return ruleStage(boards, item) !== undefined;\n}\n\ntype PersistedMessageReader = {\n listMessages(input: {\n threadId: string;\n resourceId?: string;\n page: number;\n perPage: number;\n filter?: { dateRange?: { start?: Date } };\n orderBy: { field: 'createdAt'; direction: 'ASC' };\n }): Promise<{ messages: MastraDBMessage[]; hasMore: boolean }>;\n};\n\ntype CompletedToolResult = {\n assistantMessageId: string;\n messageCreatedAt: Date;\n toolCallId: string;\n toolName: string;\n input: FactoryRuleJsonValue;\n status: 'success' | 'error';\n value: FactoryRuleJsonValue;\n};\n\nfunction isApprovedPlanResult(result: CompletedToolResult): boolean {\n if (result.toolName !== 'submit_plan' || result.status !== 'success') return false;\n if (typeof result.value === 'string') return result.value.startsWith('Plan approved.');\n if (!result.value || typeof result.value !== 'object' || Array.isArray(result.value)) return false;\n const content = result.value.content;\n return typeof content === 'string' && content.startsWith('Plan approved.');\n}\n\ntype RuntimeSnapshot = {\n modelId: string;\n thinkingLevel: ThinkingLevel;\n};\n\ntype ActivePhaseSnapshotBase = {\n bindingId: string;\n itemId: string;\n revision: number;\n stage: FactoryRuleStage;\n role: string;\n configVersion: string;\n status: 'active';\n};\n\ntype ActivePhaseSnapshotValue = ActivePhaseSnapshotBase & {\n board: string;\n modelId?: string;\n thinkingLevel?: ThinkingLevel;\n};\n\ntype PhaseSnapshotValue = ActivePhaseSnapshotValue | { bindingId?: string; status: 'none' };\n\nfunction reviewRuntimeFromRequestContext(requestContext: ComputeStateSignalArgs['requestContext']): RuntimeSnapshot {\n if (!requestContext || typeof requestContext.get !== 'function') {\n throw new Error('Factory review phase requires a controller request context.');\n }\n const context = requestContext.get<'controller', AgentControllerRequestContext<MastraCodeState>>('controller');\n const modelId = context?.session?.modelId.trim();\n if (!modelId) {\n throw new Error('Factory review phase requires a selected session model.');\n }\n return { modelId, thinkingLevel: resolveRequestThinkingLevel({ state: context?.state }) };\n}\n\nfunction workItemSourceKey(item: WorkItemRow): string | null {\n const source = item.externalSource;\n return source ? `${source.integrationId}:${source.type}:${source.externalId}` : null;\n}\n\nfunction boundedError(value: unknown): FactoryRuleJsonValue {\n const message = value instanceof Error ? value.message : typeof value === 'string' ? value : 'Tool execution failed.';\n return { message: message.slice(0, 2_000) };\n}\n\nfunction boundedResult(value: unknown): FactoryRuleJsonValue {\n try {\n return normalizeFactoryRuleJsonValue(value);\n } catch {\n return { message: 'Tool result was not serializable.' };\n }\n}\n\nfunction messageParts(message: MastraDBMessage): unknown[] {\n const content = message.content as { parts?: unknown[]; toolInvocations?: unknown[] } | unknown[] | undefined;\n if (Array.isArray(content)) return content;\n if (Array.isArray(content?.parts)) return content.parts;\n return Array.isArray(content?.toolInvocations) ? content.toolInvocations : [];\n}\n\nfunction completedStepToolCallIds(steps: unknown[]): Set<string> {\n const ids = new Set<string>();\n for (const rawStep of steps) {\n if (!rawStep || typeof rawStep !== 'object') continue;\n const toolResults = (rawStep as { toolResults?: unknown[] }).toolResults;\n if (!Array.isArray(toolResults)) continue;\n for (const rawResult of toolResults) {\n if (!rawResult || typeof rawResult !== 'object') continue;\n const toolCallId = (rawResult as { toolCallId?: unknown }).toolCallId;\n if (typeof toolCallId === 'string') ids.add(toolCallId);\n }\n }\n return ids;\n}\n\nfunction completedToolResults(message: MastraDBMessage): CompletedToolResult[] {\n if (message.role !== 'assistant') return [];\n const createdAt = message.createdAt instanceof Date ? message.createdAt : new Date(message.createdAt);\n const completed: CompletedToolResult[] = [];\n for (const rawPart of messageParts(message)) {\n if (!rawPart || typeof rawPart !== 'object') continue;\n const part = rawPart as Record<string, unknown>;\n const invocation =\n part.type === 'tool-invocation' && part.toolInvocation && typeof part.toolInvocation === 'object'\n ? (part.toolInvocation as Record<string, unknown>)\n : part;\n const state = invocation.state;\n if (state !== 'result' && state !== 'error') continue;\n const toolCallId = invocation.toolCallId;\n const toolName = invocation.toolName ?? invocation.name;\n if (typeof toolCallId !== 'string' || typeof toolName !== 'string') continue;\n completed.push({\n assistantMessageId: message.id,\n messageCreatedAt: createdAt,\n toolCallId,\n toolName: toolName.slice(0, 256),\n input: boundedResult(invocation.args ?? {}),\n status: state === 'error' ? 'error' : 'success',\n value: state === 'error' ? boundedError(invocation.result ?? invocation.error) : boundedResult(invocation.result),\n });\n }\n return completed;\n}\n\nfunction currentCompletedToolMessage(\n messages: MastraDBMessage[],\n toolCallIds: ReadonlySet<string>,\n): MastraDBMessage | undefined {\n for (let index = messages.length - 1; index >= 0; index -= 1) {\n const message = messages[index]!;\n if (completedToolResults(message).some(result => toolCallIds.has(result.toolCallId))) return message;\n }\n return undefined;\n}\n\nasync function phaseCacheKey(value: ActivePhaseSnapshotValue, linked: WorkItemRow[]): Promise<string> {\n const serialized = JSON.stringify({\n ...value,\n linked: linked.map(item => [item.id, item.revision, item.stages[0]]),\n });\n return Buffer.from(await globalThis.crypto.subtle.digest('SHA-256', new TextEncoder().encode(serialized))).toString(\n 'hex',\n );\n}\n\nfunction escapeText(value: string): string {\n return value.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');\n}\n\nfunction phaseFromSignal(signal: { metadata?: Record<string, unknown> } | undefined): PhaseSnapshotValue | undefined {\n return (signal?.metadata?.value as { phase?: PhaseSnapshotValue } | undefined)?.phase;\n}\n\nfunction latestPhase(args: ComputeStateSignalArgs): PhaseSnapshotValue | undefined {\n for (const signal of [...args.activeStateSignals].reverse()) {\n const phase = phaseFromSignal(signal);\n if (phase) return phase;\n }\n return phaseFromSignal(args.lastSnapshot);\n}\n\nasync function withRuleTimeout<T>(operation: Promise<T>): Promise<T> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n operation,\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new Error('FACTORY_RULE_TIMEOUT')), RULE_TIMEOUT_MS);\n }),\n ]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\nconst reportedUnavailableMemorySettings = new WeakSet<object>();\n\n/**\n * Marks this run's Factory memory settings as unavailable so memory falls back\n * to auto, and tells the thread once per request that saved choices were not\n * applied. The reserved request-context key keeps clients from spoofing it.\n */\nexport function reportMemorySettingsUnavailable(\n requestContext: NonNullable<ProcessInputArgs['requestContext']>,\n reason: string,\n): void {\n requestContext.set('mastra__factoryMemorySettings', { status: 'unavailable', reason });\n console.warn('[Factory Memory Settings] Failed to load settings for run', { error: reason });\n if (reportedUnavailableMemorySettings.has(requestContext)) return;\n reportedUnavailableMemorySettings.add(requestContext);\n const controller = requestContext.get('controller') as\n | Pick<AgentControllerRequestContext<MastraCodeState>, 'emitEvent'>\n | undefined;\n controller?.emitEvent?.({\n type: 'error',\n error: new Error(\n 'Memory settings could not be loaded for this run, so observational memory is using Auto models and default thresholds.',\n ),\n errorType: 'factory_memory_settings_unavailable',\n retryable: false,\n });\n}\n\nexport class FactoryPhaseStateProcessor implements Processor<'factory-phase'> {\n readonly id = STATE_ID;\n readonly stateId = STATE_ID;\n /** Request contexts whose caller row is already resolved for this request. */\n private readonly loadedMemorySettings = new WeakSet<object>();\n\n constructor(\n private readonly options: {\n configVersion: string;\n storage: WorkItemsStorage;\n boards: BoardRegistry;\n loadMemorySettings?: (args: {\n requestContext: ProcessInputStepArgs['requestContext'];\n binding: FactoryRunBindingRecord | null;\n }) => Promise<void>;\n transitionService?: Pick<FactoryTransitionService, 'transition'>;\n messageReader?: PersistedMessageReader;\n recordPullRequestProvenance?: (input: {\n binding: FactoryRunBindingRecord;\n item: WorkItemRow;\n assistantMessageId: string;\n toolCallId: string;\n toolName: string;\n toolInput: FactoryRuleJsonValue;\n toolResult: FactoryRuleJsonValue;\n status: 'success' | 'error';\n }) => Promise<void>;\n },\n ) {}\n\n /**\n * Memory settings are caller state, not run state. Factory invokes this while\n * resolving its configured processor list, before Agent resolves dynamic\n * Memory and enumerates Memory's processors. This guarantees both the Memory\n * options and its model callbacks see the authoritative row for this\n * invocation. Doing this in `processInputStep` would be too late: Memory's\n * input-step hook runs before configured processors.\n */\n async prepareMemorySettings(requestContext: ProcessInputArgs['requestContext']): Promise<void> {\n await this.loadCallerMemorySettings(requestContext, undefined, true);\n }\n\n async processInput(args: ProcessInputArgs): Promise<MessageList> {\n await this.loadCallerMemorySettings(args.requestContext);\n return args.messageList;\n }\n\n async processInputStep(args: ProcessInputStepArgs): Promise<MessageList | undefined> {\n const address = getFactorySessionCoordinates(args.requestContext);\n let binding: FactoryRunBindingRecord | null | undefined;\n try {\n binding = address ? await this.options.storage.findRunBindingBySession(address) : undefined;\n } catch {\n await this.loadCallerMemorySettings(args.requestContext, undefined);\n return;\n }\n // Resumed runs skip the input-processor phase, so re-check here: it is a\n // no-op once `processInput` already placed the row for this request. Passing\n // `undefined` (not `null`) keeps the recovery lookup inside the loader, so a\n // recovered session reaches its project row from this hook too.\n await this.loadCallerMemorySettings(args.requestContext, binding);\n if (!binding || binding.status !== 'active') return;\n const completedToolCallIds = completedStepToolCallIds(args.steps);\n const completedMessage = currentCompletedToolMessage(args.messages, completedToolCallIds);\n if (completedMessage) {\n await this.ingestMessages(binding, [completedMessage], completedToolCallIds);\n }\n }\n\n /**\n * Loads the caller's row once per invocation. The input hook always refreshes\n * it in case the controller reused a request context; later step hooks reuse\n * that value (`undefined` means it has not been loaded yet; `null` means an\n * empty row).\n *\n * The WeakSet covers loaders that do not set the context key themselves, so a\n * request is still loaded once. A failed load records an unavailable sentinel\n * (memory then runs both roles on auto with default thresholds) and is not\n * marked loaded so the next step can retry.\n */\n private async loadCallerMemorySettings(\n requestContext: ProcessInputStepArgs['requestContext'] | ProcessInputArgs['requestContext'],\n binding?: FactoryRunBindingRecord | null,\n force = false,\n ): Promise<void> {\n if (!requestContext || !this.options.loadMemorySettings) return;\n const currentSettings = requestContext.get('mastra__factoryMemorySettings') as\n | { status?: unknown }\n | null\n | undefined;\n if (force) this.loadedMemorySettings.delete(requestContext);\n if (!force && currentSettings !== undefined && currentSettings?.status !== 'unavailable') return;\n if (!force && this.loadedMemorySettings.has(requestContext)) return;\n try {\n let runBinding = binding ?? null;\n if (binding === undefined) {\n const address = getFactorySessionCoordinates(requestContext);\n runBinding = address\n ? await this.options.storage.findRunBindingBySession(address)\n : // Crash-recovered sessions come back with empty state, so the binding\n // table is the last source of the project whose row applies. Without\n // this the run would silently read the caller's personal row.\n await findSessionRunBinding(requestContext, this.options.storage);\n }\n await this.options.loadMemorySettings({ requestContext, binding: runBinding });\n this.loadedMemorySettings.add(requestContext);\n } catch (error) {\n reportMemorySettingsUnavailable(requestContext, error instanceof Error ? error.message : String(error));\n }\n }\n\n async computeStateSignal(args: ComputeStateSignalArgs): Promise<ComputeStateSignalResult> {\n const address = getFactorySessionCoordinates(args.requestContext);\n if (!address) return;\n const binding = await this.options.storage.findRunBindingBySession(address);\n const prior = latestPhase(args);\n const hasBase = Boolean(args.lastSnapshot) && args.contextWindow.hasSnapshot;\n\n if (!binding) return;\n if (binding.status !== 'active') {\n if (!hasBase || prior?.status !== 'active') return;\n return {\n id: STATE_ID,\n cacheKey: `factory:none:${prior.bindingId ?? 'revoked'}`,\n mode: 'snapshot',\n tagName: 'factory-phase',\n contents: '\\n',\n value: { phase: { status: 'none' } },\n attributes: { status: 'none' },\n metadata: { value: { phase: { status: 'none' } } },\n };\n }\n\n const item = await this.options.storage.get({ orgId: binding.orgId, id: binding.workItemId });\n const stage = ruleStage(this.options.boards, item);\n if (!item || !stage) return;\n const allItems = await this.options.storage.list({\n orgId: binding.orgId,\n factoryProjectId: binding.factoryProjectId,\n });\n const linked = allItems\n .filter(candidate => candidate.parentWorkItemId === item.id || item.parentWorkItemId === candidate.id)\n .slice(0, MAX_LINKED_ITEMS);\n const board = boardForWorkItem(item);\n const baseValue: ActivePhaseSnapshotBase = {\n status: 'active',\n bindingId: binding.id,\n itemId: item.id,\n revision: item.revision,\n stage,\n role: binding.role,\n configVersion: this.options.configVersion,\n };\n const value: ActivePhaseSnapshotValue =\n board === 'review'\n ? { ...baseValue, board, ...reviewRuntimeFromRequestContext(args.requestContext) }\n : { ...baseValue, board };\n const cacheKey = await phaseCacheKey(value, linked);\n if (hasBase && (args.tracking?.currentCacheKey ?? args.lastSnapshot?.metadata?.state?.cacheKey) === cacheKey)\n return;\n\n const linkedText = linked.length\n ? `\\nLinked items: ${linked.map(candidate => `${workItemSource(candidate.externalSource)} ${candidate.title}`).join('; ')}`\n : '';\n const phaseLabel = this.options.boards.get(board)?.phases[stage]?.title ?? stage;\n const runtime =\n value.modelId && value.thinkingLevel ? { modelId: value.modelId, thinkingLevel: value.thinkingLevel } : null;\n const snapshotContents =\n `Factory ${board} phase: ${escapeText(phaseLabel)} (${escapeText(stage)})\\n` +\n `Work item: ${escapeText(item.title)} (${item.id})\\n` +\n `Role: ${escapeText(binding.role)}\\nRevision: ${item.revision}\\nConfig: ${escapeText(this.options.configVersion)}\\n` +\n (runtime\n ? `Runtime: model=${escapeText(runtime.modelId)}, reasoning-setting=${escapeText(runtime.thinkingLevel)}\\n`\n : '') +\n ((board === 'work' || board === 'review') && stage === 'intake'\n ? 'This card rests in Intake: its work is paused. Answer questions without moving it; when the user asks to resume, request the transition into the working stage first, then continue the work in this session.\\n'\n : '') +\n `Use factory_transition_work_item with expectedRevision ${item.revision} to request a phase change.${escapeText(linkedText)}`;\n const isDelta = hasBase && prior?.status === 'active';\n return {\n id: STATE_ID,\n cacheKey,\n mode: isDelta ? 'delta' : 'snapshot',\n tagName: 'factory-phase',\n contents: isDelta ? `Factory phase update:\\n${snapshotContents}` : snapshotContents,\n value: { phase: value },\n ...(isDelta ? { delta: { phase: value } } : {}),\n attributes: {\n status: 'active',\n board,\n stage,\n role: binding.role,\n revision: item.revision,\n ...(runtime ?? {}),\n },\n metadata: { value: { phase: value } },\n };\n }\n\n async reconcileAllBoundThreads(): Promise<void> {\n if (!this.options.messageReader) return;\n const bindings = await this.options.storage.listActiveRunBindings();\n for (const binding of bindings) await this.reconcileBinding(binding);\n }\n\n async reconcileBinding(binding: FactoryRunBindingRecord): Promise<void> {\n const reader = this.options.messageReader;\n if (!reader || binding.status !== 'active') return;\n // One keyed read up front: a binding whose item is gone or no longer in a\n // single rule stage would ingest nothing, so skip the cursor and message\n // reads entirely instead of paying them on every walk.\n const item = await this.options.storage.get({ orgId: binding.orgId, id: binding.workItemId });\n if (!itemInRuleStage(this.options.boards, item)) return;\n const cursor = await this.options.storage.getToolResultCursor(binding.orgId, binding.factoryProjectId, binding.id);\n let page = 0;\n while (true) {\n const result = await reader.listMessages({\n threadId: binding.threadId,\n resourceId: binding.resourceId,\n page,\n perPage: TRANSCRIPT_PAGE_SIZE,\n ...(cursor ? { filter: { dateRange: { start: cursor.lastMessageCreatedAt } } } : {}),\n orderBy: { field: 'createdAt', direction: 'ASC' },\n });\n await this.ingestMessages(binding, result.messages, undefined, item);\n const last = result.messages.at(-1);\n if (last) {\n await this.options.storage.advanceToolResultCursor({\n bindingId: binding.id,\n orgId: binding.orgId,\n factoryProjectId: binding.factoryProjectId,\n lastMessageId: last.id,\n lastMessageCreatedAt: last.createdAt instanceof Date ? last.createdAt : new Date(last.createdAt),\n updatedAt: new Date(),\n });\n }\n if (!result.hasMore) break;\n page += 1;\n }\n }\n\n private async ingestMessages(\n binding: FactoryRunBindingRecord,\n messages: MastraDBMessage[],\n toolCallIds?: ReadonlySet<string>,\n preloadedItem?: WorkItemRow,\n ): Promise<void> {\n const item = preloadedItem ?? (await this.options.storage.get({ orgId: binding.orgId, id: binding.workItemId }));\n if (!itemInRuleStage(this.options.boards, item)) return;\n for (const message of messages) {\n for (const toolResult of completedToolResults(message)) {\n if (toolCallIds && !toolCallIds.has(toolResult.toolCallId)) continue;\n try {\n await this.options.recordPullRequestProvenance?.({\n binding,\n item,\n assistantMessageId: toolResult.assistantMessageId,\n toolCallId: toolResult.toolCallId,\n toolName: toolResult.toolName,\n toolInput: toolResult.input,\n toolResult: toolResult.value,\n status: toolResult.status,\n });\n } catch {\n // Provenance is supporting evidence and must not block authoritative rule ingress.\n }\n await this.ingestToolResult(binding, item, toolResult);\n }\n }\n }\n\n private async ingestToolResult(\n binding: FactoryRunBindingRecord,\n item: WorkItemRow,\n toolResult: CompletedToolResult,\n ): Promise<void> {\n const board = boardForWorkItem(item);\n const rule = resolveBoardToolRule(this.options.boards, board, toolResult.toolName);\n if (!rule) return;\n const ingressId = JSON.stringify([\n binding.id,\n binding.threadId,\n toolResult.assistantMessageId,\n toolResult.toolCallId,\n ]);\n const prior = await this.options.storage.getTransitionResultByIngress(\n binding.orgId,\n binding.factoryProjectId,\n ingressId,\n );\n if (prior) return;\n const context: FactoryToolResultRuleContext = {\n tenant: { orgId: binding.orgId, projectId: binding.factoryProjectId },\n actor: { type: 'agent', bindingId: binding.id, role: binding.role },\n ingress: { type: 'toolResult', id: ingressId },\n cause: `Completed ${toolResult.toolName}`,\n causalChain: [],\n configVersion: this.options.configVersion,\n item: {\n id: item.id,\n source: workItemSource(item.externalSource),\n sourceKey: workItemSourceKey(item),\n parentWorkItemId: item.parentWorkItemId,\n title: item.title,\n url: item.externalSource?.url ?? null,\n stages: item.stages,\n acceptedAt: item.acceptedAt,\n metadata: item.metadata,\n },\n board,\n itemRevision: item.revision,\n toolName: toolResult.toolName,\n threadId: binding.threadId,\n assistantMessageId: toolResult.assistantMessageId,\n toolCallId: toolResult.toolCallId,\n result: { status: toolResult.status, value: toolResult.value },\n };\n\n let decision: FactoryRuleDecision | void = undefined;\n let decisions: FactoryCommitDecision[] = [];\n let outcome: { status: 'accepted' | 'rejected'; code?: string; reason?: string } = { status: 'accepted' };\n try {\n decision = await withRuleTimeout(Promise.resolve(rule(Object.freeze(context))));\n if (decision?.type === 'reject') {\n outcome = { status: 'rejected', code: decision.code, reason: decision.reason };\n } else if (decision) {\n const validated = validateFactoryRuleDecisions([decision]);\n for (const entry of validated) assertFactoryDecisionTarget(entry, this.options.boards, board);\n decisions = validated;\n }\n } catch (error) {\n const timedOut = error instanceof Error && error.message === 'FACTORY_RULE_TIMEOUT';\n outcome = {\n status: 'rejected',\n code: timedOut ? 'timeout' : 'rule_error',\n reason: timedOut\n ? 'Factory rule evaluation timed out.'\n : error instanceof Error\n ? error.message.slice(0, 2_000)\n : 'Factory tool-result rule failed.',\n };\n }\n const committed = await this.options.storage.commitRuleEvaluation({\n orgId: binding.orgId,\n factoryProjectId: binding.factoryProjectId,\n workItemId: item.id,\n ingress: { identity: ingressId, triggerType: 'tool.result' },\n configVersion: this.options.configVersion,\n expectedRevision: item.revision,\n actor: { ...context.actor },\n outcome,\n decisions: decisions.map(entry => ({ ...entry })),\n causalChain: [],\n now: new Date(),\n });\n if (committed.status !== 'committed' || !this.options.transitionService) return;\n for (const entry of decisions) {\n if (entry.type !== 'transition') continue;\n await this.options.transitionService.transition({\n orgId: binding.orgId,\n factoryProjectId: binding.factoryProjectId,\n workItemId: item.id,\n board: entry.board,\n stage: entry.stage,\n expectedRevision: item.revision,\n actor: { type: 'system', id: 'factory-tool-result-rule' },\n ingress: { type: 'rule', identity: `decision:${entry.idempotencyKey}` },\n cause: 'tool_result_rule',\n causalChain: [{ ingressId, decisionType: entry.type }],\n ...(isApprovedPlanResult(toolResult) ? { planApproved: true } : {}),\n });\n }\n }\n}\n"],"mappings":"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"}
@@ -23,8 +23,8 @@ export interface DefaultModelHydrationSession extends DefaultModelApplicableSess
23
23
  };
24
24
  thread: {
25
25
  getId(): string | null | undefined;
26
- getSetting(args: {
27
- key: string;
26
+ firstUserMessage(args: {
27
+ threadId: string;
28
28
  }): Promise<unknown>;
29
29
  };
30
30
  }
@@ -35,6 +35,6 @@ export interface DefaultModelHydrationDependencies {
35
35
  workItems: Pick<WorkItemsStorage, 'findActiveRunBindingByThread'>;
36
36
  modelDefaults: Pick<ModelDefaultsStorage, 'get'>;
37
37
  }
38
- /** Seed the user's default model unless the interactive thread already selected its own model. */
38
+ /** Seed the user's default model into a new interactive chat; a chat with a user message keeps its model. */
39
39
  export declare function hydrateSessionDefaultModel(session: DefaultModelHydrationSession, { sourceControl, workItems, modelDefaults }: DefaultModelHydrationDependencies): Promise<void>;
40
40
  //# sourceMappingURL=default-model-hydration.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"default-model-hydration.d.ts","sourceRoot":"","sources":["../../src/session/default-model-hydration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACtF,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,2CAA2C,CAAC;AAC5F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AAE9E,MAAM,WAAW,6BAA6B;IAC5C,KAAK,EAAE;QAAE,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;KAAE,CAAC;IACrD,SAAS,EAAE;QAAE,KAAK,EAAE;YAAE,GAAG,CAAC,IAAI,EAAE;gBAAE,OAAO,EAAE,MAAM,CAAC;gBAAC,SAAS,EAAE,MAAM,CAAA;aAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;SAAE,CAAA;KAAE,CAAC;CAC/F;AAED,MAAM,WAAW,4BAA6B,SAAQ,6BAA6B;IACjF,QAAQ,CAAC,QAAQ,EAAE;QAAE,aAAa,IAAI,MAAM,CAAA;KAAE,CAAC;IAC/C,KAAK,EAAE;QAAE,GAAG,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAA;KAAE,CAAC;IACtD,MAAM,EAAE;QACN,KAAK,IAAI,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;QACnC,UAAU,CAAC,IAAI,EAAE;YAAE,GAAG,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;KACrD,CAAC;CACH;AAUD,MAAM,WAAW,iCAAiC;IAChD,aAAa,EAAE;QACb,QAAQ,EAAE,IAAI,CAAC,0BAA0B,CAAC,UAAU,CAAC,EAAE,gBAAgB,CAAC,CAAC;KAC1E,CAAC;IACF,SAAS,EAAE,IAAI,CAAC,gBAAgB,EAAE,8BAA8B,CAAC,CAAC;IAClE,aAAa,EAAE,IAAI,CAAC,oBAAoB,EAAE,KAAK,CAAC,CAAC;CAClD;AAED,kGAAkG;AAClG,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,4BAA4B,EACrC,EAAE,aAAa,EAAE,SAAS,EAAE,aAAa,EAAE,EAAE,iCAAiC,GAC7E,OAAO,CAAC,IAAI,CAAC,CA+Bf"}
1
+ {"version":3,"file":"default-model-hydration.d.ts","sourceRoot":"","sources":["../../src/session/default-model-hydration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACtF,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,2CAA2C,CAAC;AAC5F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AAE9E,MAAM,WAAW,6BAA6B;IAC5C,KAAK,EAAE;QAAE,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;KAAE,CAAC;IACrD,SAAS,EAAE;QAAE,KAAK,EAAE;YAAE,GAAG,CAAC,IAAI,EAAE;gBAAE,OAAO,EAAE,MAAM,CAAC;gBAAC,SAAS,EAAE,MAAM,CAAA;aAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;SAAE,CAAA;KAAE,CAAC;CAC/F;AAED,MAAM,WAAW,4BAA6B,SAAQ,6BAA6B;IACjF,QAAQ,CAAC,QAAQ,EAAE;QAAE,aAAa,IAAI,MAAM,CAAA;KAAE,CAAC;IAC/C,KAAK,EAAE;QAAE,GAAG,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAA;KAAE,CAAC;IACtD,MAAM,EAAE;QACN,KAAK,IAAI,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;QACnC,gBAAgB,CAAC,IAAI,EAAE;YAAE,QAAQ,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;KAChE,CAAC;CACH;AAUD,MAAM,WAAW,iCAAiC;IAChD,aAAa,EAAE;QACb,QAAQ,EAAE,IAAI,CAAC,0BAA0B,CAAC,UAAU,CAAC,EAAE,gBAAgB,CAAC,CAAC;KAC1E,CAAC;IACF,SAAS,EAAE,IAAI,CAAC,gBAAgB,EAAE,8BAA8B,CAAC,CAAC;IAClE,aAAa,EAAE,IAAI,CAAC,oBAAoB,EAAE,KAAK,CAAC,CAAC;CAClD;AAED,6GAA6G;AAC7G,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,4BAA4B,EACrC,EAAE,aAAa,EAAE,SAAS,EAAE,aAAa,EAAE,EAAE,iCAAiC,GAC7E,OAAO,CAAC,IAAI,CAAC,CAyBf"}
@@ -10,10 +10,10 @@ async function applyDefaultModel(session, modelId) {
10
10
  agentType
11
11
  });
12
12
  }
13
- /** Seed the user's default model unless the interactive thread already selected its own model. */
13
+ /** Seed the user's default model into a new interactive chat; a chat with a user message keeps its model. */
14
14
  async function hydrateSessionDefaultModel(session, { sourceControl, workItems, modelDefaults }) {
15
15
  const resourceId = session.identity.getResourceId();
16
- if (session.state.get()?.factoryProjectId || typeof session.thread.getSetting !== "function") return;
16
+ if (session.state.get()?.factoryProjectId || typeof session.thread.firstUserMessage !== "function") return;
17
17
  try {
18
18
  const sourceSession = await sourceControl.sessions.getBySessionId(resourceId);
19
19
  if (!sourceSession) return;
@@ -24,12 +24,7 @@ async function hydrateSessionDefaultModel(session, { sourceControl, workItems, m
24
24
  resourceId,
25
25
  sessionId: resourceId
26
26
  })) return;
27
- if ((await Promise.all([
28
- session.thread.getSetting({ key: "currentModelId" }),
29
- session.thread.getSetting({ key: "modeModelId_build" }),
30
- session.thread.getSetting({ key: "modeModelId_plan" }),
31
- session.thread.getSetting({ key: "modeModelId_fast" })
32
- ])).some((setting) => typeof setting === "string")) return;
27
+ if (threadId && await session.thread.firstUserMessage({ threadId })) return;
33
28
  const modelDefault = await modelDefaults.get({
34
29
  orgId: sourceSession.orgId,
35
30
  userId: sourceSession.userId
@@ -1 +1 @@
1
- {"version":3,"file":"default-model-hydration.js","names":[],"sources":["../../src/session/default-model-hydration.ts"],"sourcesContent":["import type { ModelDefaultsStorage } from '../storage/domains/model-defaults/base.js';\nimport type { SourceControlStorageHandle } from '../storage/domains/source-control/base.js';\nimport type { WorkItemsStorage } from '../storage/domains/work-items/base.js';\n\nexport interface DefaultModelApplicableSession {\n model: { switch(modelId: string): Promise<unknown> };\n subagents: { model: { set(args: { modelId: string; agentType: string }): Promise<unknown> } };\n}\n\nexport interface DefaultModelHydrationSession extends DefaultModelApplicableSession {\n readonly identity: { getResourceId(): string };\n state: { get(): Record<string, unknown> | undefined };\n thread: {\n getId(): string | null | undefined;\n getSetting(args: { key: string }): Promise<unknown>;\n };\n}\n\nasync function applyDefaultModel(session: DefaultModelApplicableSession, modelId: string): Promise<void> {\n await session.model.switch(modelId);\n\n for (const agentType of ['explore', 'plan', 'execute']) {\n await session.subagents.model.set({ modelId, agentType });\n }\n}\n\nexport interface DefaultModelHydrationDependencies {\n sourceControl: {\n sessions: Pick<SourceControlStorageHandle['sessions'], 'getBySessionId'>;\n };\n workItems: Pick<WorkItemsStorage, 'findActiveRunBindingByThread'>;\n modelDefaults: Pick<ModelDefaultsStorage, 'get'>;\n}\n\n/** Seed the user's default model unless the interactive thread already selected its own model. */\nexport async function hydrateSessionDefaultModel(\n session: DefaultModelHydrationSession,\n { sourceControl, workItems, modelDefaults }: DefaultModelHydrationDependencies,\n): Promise<void> {\n const resourceId = session.identity.getResourceId();\n if (session.state.get()?.factoryProjectId || typeof session.thread.getSetting !== 'function') return;\n try {\n const sourceSession = await sourceControl.sessions.getBySessionId(resourceId);\n if (!sourceSession) return;\n const threadId = session.thread.getId();\n if (\n threadId &&\n (await workItems.findActiveRunBindingByThread({\n orgId: sourceSession.orgId,\n threadId,\n resourceId,\n sessionId: resourceId,\n }))\n ) {\n return;\n }\n const existingThreadSettings = await Promise.all([\n session.thread.getSetting({ key: 'currentModelId' }),\n session.thread.getSetting({ key: 'modeModelId_build' }),\n session.thread.getSetting({ key: 'modeModelId_plan' }),\n session.thread.getSetting({ key: 'modeModelId_fast' }),\n ]);\n if (existingThreadSettings.some(setting => typeof setting === 'string')) return;\n\n const modelDefault = await modelDefaults.get({ orgId: sourceSession.orgId, userId: sourceSession.userId });\n if (modelDefault) await applyDefaultModel(session, modelDefault.modelId);\n } catch (error) {\n console.warn('[Factory default-model hydration] Unable to apply the user default model.', error);\n }\n}\n"],"mappings":";AAkBA,eAAe,kBAAkB,SAAwC,SAAgC;CACvG,MAAM,QAAQ,MAAM,OAAO,OAAO;CAElC,KAAK,MAAM,aAAa;EAAC;EAAW;EAAQ;CAAS,GACnD,MAAM,QAAQ,UAAU,MAAM,IAAI;EAAE;EAAS;CAAU,CAAC;AAE5D;;AAWA,eAAsB,2BACpB,SACA,EAAE,eAAe,WAAW,iBACb;CACf,MAAM,aAAa,QAAQ,SAAS,cAAc;CAClD,IAAI,QAAQ,MAAM,IAAI,CAAC,EAAE,oBAAoB,OAAO,QAAQ,OAAO,eAAe,YAAY;CAC9F,IAAI;EACF,MAAM,gBAAgB,MAAM,cAAc,SAAS,eAAe,UAAU;EAC5E,IAAI,CAAC,eAAe;EACpB,MAAM,WAAW,QAAQ,OAAO,MAAM;EACtC,IACE,YACC,MAAM,UAAU,6BAA6B;GAC5C,OAAO,cAAc;GACrB;GACA;GACA,WAAW;EACb,CAAC,GAED;EAQF,KAAI,MANiC,QAAQ,IAAI;GAC/C,QAAQ,OAAO,WAAW,EAAE,KAAK,iBAAiB,CAAC;GACnD,QAAQ,OAAO,WAAW,EAAE,KAAK,oBAAoB,CAAC;GACtD,QAAQ,OAAO,WAAW,EAAE,KAAK,mBAAmB,CAAC;GACrD,QAAQ,OAAO,WAAW,EAAE,KAAK,mBAAmB,CAAC;EACvD,CAAC,EAAA,CAC0B,MAAK,YAAW,OAAO,YAAY,QAAQ,GAAG;EAEzE,MAAM,eAAe,MAAM,cAAc,IAAI;GAAE,OAAO,cAAc;GAAO,QAAQ,cAAc;EAAO,CAAC;EACzG,IAAI,cAAc,MAAM,kBAAkB,SAAS,aAAa,OAAO;CACzE,SAAS,OAAO;EACd,QAAQ,KAAK,6EAA6E,KAAK;CACjG;AACF"}
1
+ {"version":3,"file":"default-model-hydration.js","names":[],"sources":["../../src/session/default-model-hydration.ts"],"sourcesContent":["import type { ModelDefaultsStorage } from '../storage/domains/model-defaults/base.js';\nimport type { SourceControlStorageHandle } from '../storage/domains/source-control/base.js';\nimport type { WorkItemsStorage } from '../storage/domains/work-items/base.js';\n\nexport interface DefaultModelApplicableSession {\n model: { switch(modelId: string): Promise<unknown> };\n subagents: { model: { set(args: { modelId: string; agentType: string }): Promise<unknown> } };\n}\n\nexport interface DefaultModelHydrationSession extends DefaultModelApplicableSession {\n readonly identity: { getResourceId(): string };\n state: { get(): Record<string, unknown> | undefined };\n thread: {\n getId(): string | null | undefined;\n firstUserMessage(args: { threadId: string }): Promise<unknown>;\n };\n}\n\nasync function applyDefaultModel(session: DefaultModelApplicableSession, modelId: string): Promise<void> {\n await session.model.switch(modelId);\n\n for (const agentType of ['explore', 'plan', 'execute']) {\n await session.subagents.model.set({ modelId, agentType });\n }\n}\n\nexport interface DefaultModelHydrationDependencies {\n sourceControl: {\n sessions: Pick<SourceControlStorageHandle['sessions'], 'getBySessionId'>;\n };\n workItems: Pick<WorkItemsStorage, 'findActiveRunBindingByThread'>;\n modelDefaults: Pick<ModelDefaultsStorage, 'get'>;\n}\n\n/** Seed the user's default model into a new interactive chat; a chat with a user message keeps its model. */\nexport async function hydrateSessionDefaultModel(\n session: DefaultModelHydrationSession,\n { sourceControl, workItems, modelDefaults }: DefaultModelHydrationDependencies,\n): Promise<void> {\n const resourceId = session.identity.getResourceId();\n if (session.state.get()?.factoryProjectId || typeof session.thread.firstUserMessage !== 'function') return;\n try {\n const sourceSession = await sourceControl.sessions.getBySessionId(resourceId);\n if (!sourceSession) return;\n const threadId = session.thread.getId();\n if (\n threadId &&\n (await workItems.findActiveRunBindingByThread({\n orgId: sourceSession.orgId,\n threadId,\n resourceId,\n sessionId: resourceId,\n }))\n ) {\n return;\n }\n if (threadId && (await session.thread.firstUserMessage({ threadId }))) return;\n\n const modelDefault = await modelDefaults.get({ orgId: sourceSession.orgId, userId: sourceSession.userId });\n if (modelDefault) await applyDefaultModel(session, modelDefault.modelId);\n } catch (error) {\n console.warn('[Factory default-model hydration] Unable to apply the user default model.', error);\n }\n}\n"],"mappings":";AAkBA,eAAe,kBAAkB,SAAwC,SAAgC;CACvG,MAAM,QAAQ,MAAM,OAAO,OAAO;CAElC,KAAK,MAAM,aAAa;EAAC;EAAW;EAAQ;CAAS,GACnD,MAAM,QAAQ,UAAU,MAAM,IAAI;EAAE;EAAS;CAAU,CAAC;AAE5D;;AAWA,eAAsB,2BACpB,SACA,EAAE,eAAe,WAAW,iBACb;CACf,MAAM,aAAa,QAAQ,SAAS,cAAc;CAClD,IAAI,QAAQ,MAAM,IAAI,CAAC,EAAE,oBAAoB,OAAO,QAAQ,OAAO,qBAAqB,YAAY;CACpG,IAAI;EACF,MAAM,gBAAgB,MAAM,cAAc,SAAS,eAAe,UAAU;EAC5E,IAAI,CAAC,eAAe;EACpB,MAAM,WAAW,QAAQ,OAAO,MAAM;EACtC,IACE,YACC,MAAM,UAAU,6BAA6B;GAC5C,OAAO,cAAc;GACrB;GACA;GACA,WAAW;EACb,CAAC,GAED;EAEF,IAAI,YAAa,MAAM,QAAQ,OAAO,iBAAiB,EAAE,SAAS,CAAC,GAAI;EAEvE,MAAM,eAAe,MAAM,cAAc,IAAI;GAAE,OAAO,cAAc;GAAO,QAAQ,cAAc;EAAO,CAAC;EACzG,IAAI,cAAc,MAAM,kBAAkB,SAAS,aAAa,OAAO;CACzE,SAAS,OAAO;EACd,QAAQ,KAAK,6EAA6E,KAAK;CACjG;AACF"}
@@ -15,7 +15,6 @@ declare const factoryOpenRunSchema: z.ZodObject<{
15
15
  sessionId: z.ZodString;
16
16
  threadId: z.ZodString;
17
17
  branch: z.ZodString;
18
- agentName: z.ZodString;
19
18
  ownerId: z.ZodOptional<z.ZodString>;
20
19
  heartbeatAt: z.ZodOptional<z.ZodNumber>;
21
20
  }, z.core.$strip>;
@@ -33,9 +32,6 @@ export interface RunEndCaptureSession {
33
32
  value: unknown;
34
33
  }): Promise<void>;
35
34
  };
36
- readonly mode: {
37
- get(): string;
38
- };
39
35
  subscribe(listener: (event: AgentControllerEvent) => void): () => void;
40
36
  }
41
37
  type RunEndReason = Extract<AgentControllerEvent, {
@@ -45,7 +41,7 @@ export declare function listSessionOpenRuns(session: RunEndCaptureSession): Prom
45
41
  export declare function waitForSessionRunAudit(session: RunEndCaptureSession): Promise<void>;
46
42
  export declare function recordSessionRunStart(session: RunEndCaptureSession, { audit, run, actorType, observedEnd, }: {
47
43
  audit: AuditRecorder;
48
- run: Omit<FactoryOpenRun, 'agentName'>;
44
+ run: FactoryOpenRun;
49
45
  actorType: AuditActorType;
50
46
  observedEnd: () => RunEndReason;
51
47
  }): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"run-audit.d.ts","sourceRoot":"","sources":["../../src/session/run-audit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAC1E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AAExE,eAAO,MAAM,yBAAyB,oBAAoB,CAAC;AAE3D,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;;;iBAsBxB,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,6GAA6G;AAC7G,eAAO,MAAM,yBAAyB,QAAS,CAAC;AAEhD,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,OAAO,CAOtG;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,EAAE;QACf,UAAU,CAAC,IAAI,EAAE;YAAE,GAAG,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QACpD,UAAU,CAAC,IAAI,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,OAAO,CAAA;SAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KAClE,CAAC;IACF,QAAQ,CAAC,IAAI,EAAE;QAAE,GAAG,IAAI,MAAM,CAAA;KAAE,CAAC;IACjC,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CACxE;AAED,KAAK,YAAY,GAAG,OAAO,CAAC,oBAAoB,EAAE;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,CAAC,CAAC,QAAQ,CAAC,CAAC;AAWnF,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAGlG;AAED,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAEzF;AA4CD,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,oBAAoB,EAC7B,EACE,KAAK,EACL,GAAG,EACH,SAAS,EACT,WAAW,GACZ,EAAE;IACD,KAAK,EAAE,aAAa,CAAC;IACrB,GAAG,EAAE,IAAI,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;IACvC,SAAS,EAAE,cAAc,CAAC;IAC1B,WAAW,EAAE,MAAM,YAAY,CAAC;CACjC,GACA,OAAO,CAAC,IAAI,CAAC,CAef;AAED,4FAA4F;AAC5F,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,oBAAoB,EAC7B,SAAS,EAAE,MAAM,EACjB,EAAE,SAAa,GACd,OAAO,CAAC,IAAI,CAAC,CASf;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,oBAAoB,EAAE,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,aAAa,CAAA;CAAE,GAAG,MAAM,IAAI,CAKnH"}
1
+ {"version":3,"file":"run-audit.d.ts","sourceRoot":"","sources":["../../src/session/run-audit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAC1E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AAExE,eAAO,MAAM,yBAAyB,oBAAoB,CAAC;AAE3D,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;;iBAqBxB,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,6GAA6G;AAC7G,eAAO,MAAM,yBAAyB,QAAS,CAAC;AAEhD,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,SAAa,GAAG,OAAO,CAOtG;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,EAAE;QACf,UAAU,CAAC,IAAI,EAAE;YAAE,GAAG,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QACpD,UAAU,CAAC,IAAI,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,OAAO,CAAA;SAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KAClE,CAAC;IACF,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CACxE;AAED,KAAK,YAAY,GAAG,OAAO,CAAC,oBAAoB,EAAE;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,CAAC,CAAC,QAAQ,CAAC,CAAC;AAWnF,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAGlG;AAED,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAEzF;AA4CD,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,oBAAoB,EAC7B,EACE,KAAK,EACL,GAAG,EACH,SAAS,EACT,WAAW,GACZ,EAAE;IACD,KAAK,EAAE,aAAa,CAAC;IACrB,GAAG,EAAE,cAAc,CAAC;IACpB,SAAS,EAAE,cAAc,CAAC;IAC1B,WAAW,EAAE,MAAM,YAAY,CAAC;CACjC,GACA,OAAO,CAAC,IAAI,CAAC,CAcf;AAED,4FAA4F;AAC5F,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,oBAAoB,EAC7B,SAAS,EAAE,MAAM,EACjB,EAAE,SAAa,GACd,OAAO,CAAC,IAAI,CAAC,CASf;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,oBAAoB,EAAE,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,aAAa,CAAA;CAAE,GAAG,MAAM,IAAI,CAKnH"}
@@ -1,4 +1,3 @@
1
- import { auditAgentName } from "../storage/domains/audit/base.js";
2
1
  import { z } from "zod";
3
2
  //#region src/session/run-audit.ts
4
3
  const FACTORY_OPEN_RUNS_SETTING = "factoryOpenRuns";
@@ -14,7 +13,6 @@ const factoryOpenRunSchema = z.object({
14
13
  sessionId: z.string(),
15
14
  threadId: z.string(),
16
15
  branch: z.string(),
17
- agentName: z.string(),
18
16
  /**
19
17
  * Which dispatcher process is watching this run, and when it last said so
20
18
  * (epoch ms). The ledger is a thread setting shared by every replica, so a
@@ -85,18 +83,14 @@ async function recordRunEnds(session, audit, reason, kickoffId) {
85
83
  });
86
84
  }
87
85
  function recordSessionRunStart(session, { audit, run, actorType, observedEnd }) {
88
- const openRun = {
89
- ...run,
90
- agentName: auditAgentName(session.mode.get())
91
- };
92
86
  return serializeRunAudit(session, async () => {
93
87
  const runs = await listSessionOpenRuns(session);
94
88
  if (!runs.some((existing) => existing.kickoffId === run.kickoffId)) await session.thread.setSetting({
95
89
  key: FACTORY_OPEN_RUNS_SETTING,
96
- value: [...runs, openRun]
90
+ value: [...runs, run]
97
91
  });
98
92
  await audit.record({
99
- ...runAuditInput(openRun),
93
+ ...runAuditInput(run),
100
94
  actorId: run.startedBy,
101
95
  actorType,
102
96
  action: "factory.run.started"
@@ -1 +1 @@
1
- {"version":3,"file":"run-audit.js","names":[],"sources":["../../src/session/run-audit.ts"],"sourcesContent":["import type { AgentControllerEvent } from '@mastra/core/agent-controller';\nimport { z } from 'zod';\n\nimport { auditAgentName } from '../storage/domains/audit/base.js';\nimport type { AuditActorType } from '../storage/domains/audit/base.js';\nimport type { AuditRecorder } from '../storage/domains/audit/domain.js';\n\nexport const FACTORY_OPEN_RUNS_SETTING = 'factoryOpenRuns';\n\nconst factoryOpenRunSchema = z.object({\n kickoffId: z.string(),\n bindingId: z.string(),\n role: z.string(),\n startedBy: z.string(),\n orgId: z.string(),\n factoryProjectId: z.string(),\n workItemId: z.string(),\n workItemName: z.string().optional(),\n sessionId: z.string(),\n threadId: z.string(),\n branch: z.string(),\n agentName: z.string(),\n /**\n * Which dispatcher process is watching this run, and when it last said so\n * (epoch ms). The ledger is a thread setting shared by every replica, so a\n * remote reader needs these to tell a run that is live elsewhere from one\n * that died with its process. Absent on entries written before ownership\n * was recorded; those read as stale.\n */\n ownerId: z.string().optional(),\n heartbeatAt: z.number().optional(),\n});\n\nexport type FactoryOpenRun = z.infer<typeof factoryOpenRunSchema>;\n\n/** A heartbeat older than this means the owner is gone; nothing renews it once the run's dispatcher dies. */\nexport const FACTORY_OPEN_RUN_STALE_MS = 30_000;\n\nexport function isOpenRunLiveElsewhere(run: FactoryOpenRun, ownerId: string, now = Date.now()): boolean {\n return (\n run.ownerId !== undefined &&\n run.ownerId !== ownerId &&\n run.heartbeatAt !== undefined &&\n now - run.heartbeatAt < FACTORY_OPEN_RUN_STALE_MS\n );\n}\n\nexport interface RunEndCaptureSession {\n readonly thread: {\n getSetting(args: { key: string }): Promise<unknown>;\n setSetting(args: { key: string; value: unknown }): Promise<void>;\n };\n readonly mode: { get(): string };\n subscribe(listener: (event: AgentControllerEvent) => void): () => void;\n}\n\ntype RunEndReason = Extract<AgentControllerEvent, { type: 'agent_end' }>['reason'];\nconst pendingWrites = new WeakMap<RunEndCaptureSession, Promise<void>>();\n\nfunction serializeRunAudit(session: RunEndCaptureSession, write: () => Promise<void>): Promise<void> {\n const pending = (pendingWrites.get(session) ?? Promise.resolve())\n .then(write)\n .catch(error => console.warn('[Factory run audit] Unable to record run lifecycle.', error));\n pendingWrites.set(session, pending);\n return pending;\n}\n\nexport async function listSessionOpenRuns(session: RunEndCaptureSession): Promise<FactoryOpenRun[]> {\n const stored = await session.thread.getSetting({ key: FACTORY_OPEN_RUNS_SETTING });\n return z.array(factoryOpenRunSchema).parse(stored ?? []);\n}\n\nexport async function waitForSessionRunAudit(session: RunEndCaptureSession): Promise<void> {\n await (pendingWrites.get(session) ?? Promise.resolve());\n}\n\nfunction runAuditInput(run: FactoryOpenRun) {\n // Ownership fields are dispatcher bookkeeping, not part of the run's audit record.\n const { orgId, factoryProjectId, workItemId, workItemName, ownerId, heartbeatAt, ...metadata } = run;\n void ownerId;\n void heartbeatAt;\n return {\n orgId,\n factoryProjectId,\n idempotencyKey: run.kickoffId,\n targets: [{ type: 'work_item', id: workItemId, ...(workItemName ? { name: workItemName } : {}) }],\n metadata,\n };\n}\n\nasync function recordRunEnds(\n session: RunEndCaptureSession,\n audit: AuditRecorder,\n reason: RunEndReason,\n kickoffId?: string,\n): Promise<void> {\n if (!reason || reason === 'suspended') return;\n const runs = await listSessionOpenRuns(session);\n if (runs.length === 0) return;\n const remaining = [];\n for (const run of runs) {\n if (kickoffId && run.kickoffId !== kickoffId) {\n remaining.push(run);\n continue;\n }\n const input = runAuditInput(run);\n const recorded = await audit.record({\n ...input,\n actorId: `agent:${run.threadId}`,\n actorType: 'agent',\n action: 'factory.run.ended',\n metadata: { ...input.metadata, reason },\n });\n if (!recorded) remaining.push(run);\n }\n await session.thread.setSetting({ key: FACTORY_OPEN_RUNS_SETTING, value: remaining });\n}\n\nexport function recordSessionRunStart(\n session: RunEndCaptureSession,\n {\n audit,\n run,\n actorType,\n observedEnd,\n }: {\n audit: AuditRecorder;\n run: Omit<FactoryOpenRun, 'agentName'>;\n actorType: AuditActorType;\n observedEnd: () => RunEndReason;\n },\n): Promise<void> {\n const openRun = { ...run, agentName: auditAgentName(session.mode.get()) };\n return serializeRunAudit(session, async () => {\n const runs = await listSessionOpenRuns(session);\n if (!runs.some(existing => existing.kickoffId === run.kickoffId)) {\n await session.thread.setSetting({ key: FACTORY_OPEN_RUNS_SETTING, value: [...runs, openRun] });\n }\n await audit.record({\n ...runAuditInput(openRun),\n actorId: run.startedBy,\n actorType,\n action: 'factory.run.started',\n });\n await recordRunEnds(session, audit, observedEnd(), run.kickoffId);\n });\n}\n\n/** Renew this process's claim on an open run so other replicas keep treating it as live. */\nexport function heartbeatSessionOpenRun(\n session: RunEndCaptureSession,\n kickoffId: string,\n at = Date.now(),\n): Promise<void> {\n return serializeRunAudit(session, async () => {\n const runs = await listSessionOpenRuns(session);\n if (!runs.some(run => run.kickoffId === kickoffId)) return;\n await session.thread.setSetting({\n key: FACTORY_OPEN_RUNS_SETTING,\n value: runs.map(run => (run.kickoffId === kickoffId ? { ...run, heartbeatAt: at } : run)),\n });\n });\n}\n\nexport function observeSessionRunEnd(session: RunEndCaptureSession, { audit }: { audit: AuditRecorder }): () => void {\n return session.subscribe(event => {\n if (event.type !== 'agent_end' || !event.reason || event.reason === 'suspended') return;\n void serializeRunAudit(session, () => recordRunEnds(session, audit, event.reason));\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"run-audit.js","names":[],"sources":["../../src/session/run-audit.ts"],"sourcesContent":["import type { AgentControllerEvent } from '@mastra/core/agent-controller';\nimport { z } from 'zod';\n\nimport type { AuditActorType } from '../storage/domains/audit/base.js';\nimport type { AuditRecorder } from '../storage/domains/audit/domain.js';\n\nexport const FACTORY_OPEN_RUNS_SETTING = 'factoryOpenRuns';\n\nconst factoryOpenRunSchema = z.object({\n kickoffId: z.string(),\n bindingId: z.string(),\n role: z.string(),\n startedBy: z.string(),\n orgId: z.string(),\n factoryProjectId: z.string(),\n workItemId: z.string(),\n workItemName: z.string().optional(),\n sessionId: z.string(),\n threadId: z.string(),\n branch: z.string(),\n /**\n * Which dispatcher process is watching this run, and when it last said so\n * (epoch ms). The ledger is a thread setting shared by every replica, so a\n * remote reader needs these to tell a run that is live elsewhere from one\n * that died with its process. Absent on entries written before ownership\n * was recorded; those read as stale.\n */\n ownerId: z.string().optional(),\n heartbeatAt: z.number().optional(),\n});\n\nexport type FactoryOpenRun = z.infer<typeof factoryOpenRunSchema>;\n\n/** A heartbeat older than this means the owner is gone; nothing renews it once the run's dispatcher dies. */\nexport const FACTORY_OPEN_RUN_STALE_MS = 30_000;\n\nexport function isOpenRunLiveElsewhere(run: FactoryOpenRun, ownerId: string, now = Date.now()): boolean {\n return (\n run.ownerId !== undefined &&\n run.ownerId !== ownerId &&\n run.heartbeatAt !== undefined &&\n now - run.heartbeatAt < FACTORY_OPEN_RUN_STALE_MS\n );\n}\n\nexport interface RunEndCaptureSession {\n readonly thread: {\n getSetting(args: { key: string }): Promise<unknown>;\n setSetting(args: { key: string; value: unknown }): Promise<void>;\n };\n subscribe(listener: (event: AgentControllerEvent) => void): () => void;\n}\n\ntype RunEndReason = Extract<AgentControllerEvent, { type: 'agent_end' }>['reason'];\nconst pendingWrites = new WeakMap<RunEndCaptureSession, Promise<void>>();\n\nfunction serializeRunAudit(session: RunEndCaptureSession, write: () => Promise<void>): Promise<void> {\n const pending = (pendingWrites.get(session) ?? Promise.resolve())\n .then(write)\n .catch(error => console.warn('[Factory run audit] Unable to record run lifecycle.', error));\n pendingWrites.set(session, pending);\n return pending;\n}\n\nexport async function listSessionOpenRuns(session: RunEndCaptureSession): Promise<FactoryOpenRun[]> {\n const stored = await session.thread.getSetting({ key: FACTORY_OPEN_RUNS_SETTING });\n return z.array(factoryOpenRunSchema).parse(stored ?? []);\n}\n\nexport async function waitForSessionRunAudit(session: RunEndCaptureSession): Promise<void> {\n await (pendingWrites.get(session) ?? Promise.resolve());\n}\n\nfunction runAuditInput(run: FactoryOpenRun) {\n // Ownership fields are dispatcher bookkeeping, not part of the run's audit record.\n const { orgId, factoryProjectId, workItemId, workItemName, ownerId, heartbeatAt, ...metadata } = run;\n void ownerId;\n void heartbeatAt;\n return {\n orgId,\n factoryProjectId,\n idempotencyKey: run.kickoffId,\n targets: [{ type: 'work_item', id: workItemId, ...(workItemName ? { name: workItemName } : {}) }],\n metadata,\n };\n}\n\nasync function recordRunEnds(\n session: RunEndCaptureSession,\n audit: AuditRecorder,\n reason: RunEndReason,\n kickoffId?: string,\n): Promise<void> {\n if (!reason || reason === 'suspended') return;\n const runs = await listSessionOpenRuns(session);\n if (runs.length === 0) return;\n const remaining = [];\n for (const run of runs) {\n if (kickoffId && run.kickoffId !== kickoffId) {\n remaining.push(run);\n continue;\n }\n const input = runAuditInput(run);\n const recorded = await audit.record({\n ...input,\n actorId: `agent:${run.threadId}`,\n actorType: 'agent',\n action: 'factory.run.ended',\n metadata: { ...input.metadata, reason },\n });\n if (!recorded) remaining.push(run);\n }\n await session.thread.setSetting({ key: FACTORY_OPEN_RUNS_SETTING, value: remaining });\n}\n\nexport function recordSessionRunStart(\n session: RunEndCaptureSession,\n {\n audit,\n run,\n actorType,\n observedEnd,\n }: {\n audit: AuditRecorder;\n run: FactoryOpenRun;\n actorType: AuditActorType;\n observedEnd: () => RunEndReason;\n },\n): Promise<void> {\n return serializeRunAudit(session, async () => {\n const runs = await listSessionOpenRuns(session);\n if (!runs.some(existing => existing.kickoffId === run.kickoffId)) {\n await session.thread.setSetting({ key: FACTORY_OPEN_RUNS_SETTING, value: [...runs, run] });\n }\n await audit.record({\n ...runAuditInput(run),\n actorId: run.startedBy,\n actorType,\n action: 'factory.run.started',\n });\n await recordRunEnds(session, audit, observedEnd(), run.kickoffId);\n });\n}\n\n/** Renew this process's claim on an open run so other replicas keep treating it as live. */\nexport function heartbeatSessionOpenRun(\n session: RunEndCaptureSession,\n kickoffId: string,\n at = Date.now(),\n): Promise<void> {\n return serializeRunAudit(session, async () => {\n const runs = await listSessionOpenRuns(session);\n if (!runs.some(run => run.kickoffId === kickoffId)) return;\n await session.thread.setSetting({\n key: FACTORY_OPEN_RUNS_SETTING,\n value: runs.map(run => (run.kickoffId === kickoffId ? { ...run, heartbeatAt: at } : run)),\n });\n });\n}\n\nexport function observeSessionRunEnd(session: RunEndCaptureSession, { audit }: { audit: AuditRecorder }): () => void {\n return session.subscribe(event => {\n if (event.type !== 'agent_end' || !event.reason || event.reason === 'suspended') return;\n void serializeRunAudit(session, () => recordRunEnds(session, audit, event.reason));\n });\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"work-item-repository.d.ts","sourceRoot":"","sources":["../../src/session/work-item-repository.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,0BAA0B,EAChC,MAAM,2CAA2C,CAAC;AACnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uCAAuC,CAAC;AAEzE,MAAM,MAAM,4BAA4B,GACpC;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,mBAAmB,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACjE;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAqElD,wBAAsB,yBAAyB,CAAC,IAAI,EAAE;IACpD,aAAa,EAAE,0BAA0B,CAAC;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,EAAE,MAAM,CAAC;IACzB,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IACpC,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9C,GAAG,OAAO,CAAC,4BAA4B,CAAC,CA2CxC"}
1
+ {"version":3,"file":"work-item-repository.d.ts","sourceRoot":"","sources":["../../src/session/work-item-repository.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,0BAA0B,EAChC,MAAM,2CAA2C,CAAC;AACnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uCAAuC,CAAC;AAEzE,MAAM,MAAM,4BAA4B,GACpC;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,mBAAmB,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACjE;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAkFlD,wBAAsB,yBAAyB,CAAC,IAAI,EAAE;IACpD,aAAa,EAAE,0BAA0B,CAAC;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,EAAE,MAAM,CAAC;IACzB,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IACpC,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9C,GAAG,OAAO,CAAC,4BAA4B,CAAC,CA4CxC"}
@@ -22,7 +22,8 @@ async function listLinkedRepositories(args) {
22
22
  for (const { projectRepository, repository } of repositories) if (repository) linked.push({
23
23
  projectRepositoryId: projectRepository.id,
24
24
  externalId: repository.externalId,
25
- slug: repository.slug
25
+ slug: repository.slug,
26
+ inEnvironment: projectRepository.inEnvironment
26
27
  });
27
28
  } catch (error) {
28
29
  if (!(error instanceof SourceControlConnectionNotFoundError)) throw error;
@@ -42,10 +43,20 @@ function getExternalRepositoryId(integrationId, metadata) {
42
43
  }
43
44
  return id == null ? void 0 : String(id);
44
45
  }
45
- function findUniqueRepositoryByExternalId(repositories, externalId) {
46
- const matches = repositories.filter((repository) => repository.externalId === externalId);
47
- const [match] = matches;
48
- return matches.length === 1 ? match : void 0;
46
+ /**
47
+ * The one link among `matches` a run can use. A repository can be linked more
48
+ * than once (a provider-app reinstall adds a second link for the same slug),
49
+ * but only one of those links is in the environment, and only an environment
50
+ * link can start a session, so it wins over its out-of-environment duplicates.
51
+ */
52
+ function pickLink(matches) {
53
+ if (matches.length === 1) return matches[0];
54
+ const inEnvironment = matches.filter((repository) => repository.inEnvironment);
55
+ return inEnvironment.length === 1 ? inEnvironment[0] : void 0;
56
+ }
57
+ function findRepositoryBySlug(repositories, slug) {
58
+ const matches = repositories.filter((repository) => repository.slug === slug);
59
+ return pickLink(matches) ?? matches[0];
49
60
  }
50
61
  function resolvedRepository(repository) {
51
62
  return {
@@ -63,11 +74,11 @@ async function resolveWorkItemRepository(args) {
63
74
  const linearProjectId = typeof metadata.linearProjectId === "string" ? metadata.linearProjectId : void 0;
64
75
  const mappedRepository = linearProjectId ? linearRepositoryMap?.[linearProjectId] : void 0;
65
76
  if (externalRepositorySignal !== void 0) {
66
- const match = findUniqueRepositoryByExternalId(linked, externalRepositorySignal);
77
+ const match = pickLink(linked.filter((repository) => repository.externalId === externalRepositorySignal));
67
78
  if (match) return resolvedRepository(match);
68
79
  }
69
80
  if (repositorySignal) {
70
- const match = linked.find((repository) => repository.slug === repositorySignal);
81
+ const match = findRepositoryBySlug(linked, repositorySignal);
71
82
  return match ? resolvedRepository(match) : {
72
83
  status: "unlinked",
73
84
  hint: `Repository ${repositorySignal} is not linked to this Factory.`
@@ -78,7 +89,7 @@ async function resolveWorkItemRepository(args) {
78
89
  hint: `Source-control repository ${externalRepositorySignal} is not linked to this Factory.`
79
90
  };
80
91
  if (mappedRepository) {
81
- const match = linked.find((repository) => repository.slug === mappedRepository);
92
+ const match = findRepositoryBySlug(linked, mappedRepository);
82
93
  return match ? resolvedRepository(match) : {
83
94
  status: "unlinked",
84
95
  hint: `Mapped repository ${mappedRepository} is not linked to this Factory.`
@@ -89,8 +100,8 @@ async function resolveWorkItemRepository(args) {
89
100
  status: "unlinked",
90
101
  hint: "This Factory has no linked source-control repositories."
91
102
  };
92
- const [firstRepository] = linked;
93
- if (candidates.length === 1 && firstRepository) return resolvedRepository(firstRepository);
103
+ const onlyRepository = candidates.length === 1 ? pickLink(linked) ?? linked[0] : void 0;
104
+ if (onlyRepository) return resolvedRepository(onlyRepository);
94
105
  return {
95
106
  status: "ambiguous",
96
107
  candidates
@@ -1 +1 @@
1
- {"version":3,"file":"work-item-repository.js","names":[],"sources":["../../src/session/work-item-repository.ts"],"sourcesContent":["import {\n SourceControlConnectionNotFoundError,\n type SourceControlStorageHandle,\n} from '../storage/domains/source-control/base.js';\nimport type { WorkItemRow } from '../storage/domains/work-items/base.js';\n\nexport type WorkItemRepositoryResolution =\n | { status: 'resolved'; projectRepositoryId: string; slug: string }\n | { status: 'unlinked'; hint: string }\n | { status: 'ambiguous'; candidates: string[] };\n\ninterface LinkedRepository {\n projectRepositoryId: string;\n externalId: string;\n slug: string;\n}\n\nasync function listLinkedRepositories(args: {\n sourceControl: SourceControlStorageHandle;\n orgId: string;\n factoryProjectId: string;\n}): Promise<LinkedRepository[]> {\n const { sourceControl, orgId, factoryProjectId } = args;\n const connections = await sourceControl.connections.list({ orgId, factoryProjectId });\n const linked: LinkedRepository[] = [];\n\n for (const connection of connections.filter(candidate => candidate.integrationId === sourceControl.integrationId)) {\n try {\n const projectRepositories = await sourceControl.projectRepositories.list({ orgId, connectionId: connection.id });\n const repositories = await Promise.all(\n projectRepositories.map(async projectRepository => ({\n projectRepository,\n repository: await sourceControl.repositories.get({ orgId, id: projectRepository.repositoryId }),\n })),\n );\n for (const { projectRepository, repository } of repositories) {\n if (repository) {\n linked.push({\n projectRepositoryId: projectRepository.id,\n externalId: repository.externalId,\n slug: repository.slug,\n });\n }\n }\n } catch (error) {\n if (!(error instanceof SourceControlConnectionNotFoundError)) throw error;\n // A stale provider connection must not hide healthy linked repositories.\n }\n }\n\n return linked;\n}\n\nfunction getExternalRepositoryId(integrationId: string, metadata: Record<string, unknown>): string | undefined {\n let id: unknown;\n switch (integrationId) {\n case 'github':\n id = metadata.githubRepositoryId;\n break;\n case 'gitlab':\n id = metadata.gitlabProjectId;\n break;\n default:\n return undefined;\n }\n return id == null ? undefined : String(id);\n}\n\nfunction findUniqueRepositoryByExternalId(repositories: LinkedRepository[], externalId: string) {\n const matches = repositories.filter(repository => repository.externalId === externalId);\n const [match] = matches;\n return matches.length === 1 ? match : undefined;\n}\n\nfunction resolvedRepository(repository: LinkedRepository): WorkItemRepositoryResolution {\n return { status: 'resolved', projectRepositoryId: repository.projectRepositoryId, slug: repository.slug };\n}\n\nexport async function resolveWorkItemRepository(args: {\n sourceControl: SourceControlStorageHandle;\n orgId: string;\n factoryProjectId: string;\n item: Pick<WorkItemRow, 'metadata'>;\n linearRepositoryMap?: Record<string, string>;\n}): Promise<WorkItemRepositoryResolution> {\n const { sourceControl, item, linearRepositoryMap } = args;\n const metadata = item.metadata ?? {};\n const linked = await listLinkedRepositories(args);\n const repositorySignal = typeof metadata.repository === 'string' ? metadata.repository : undefined;\n const externalRepositorySignal = getExternalRepositoryId(sourceControl.integrationId, metadata);\n const linearProjectId = typeof metadata.linearProjectId === 'string' ? metadata.linearProjectId : undefined;\n const mappedRepository = linearProjectId ? linearRepositoryMap?.[linearProjectId] : undefined;\n\n // The provider id survives repository renames, so it outranks a possibly stale slug.\n if (externalRepositorySignal !== undefined) {\n const match = findUniqueRepositoryByExternalId(linked, externalRepositorySignal);\n if (match) return resolvedRepository(match);\n }\n\n if (repositorySignal) {\n const match = linked.find(repository => repository.slug === repositorySignal);\n return match\n ? resolvedRepository(match)\n : { status: 'unlinked', hint: `Repository ${repositorySignal} is not linked to this Factory.` };\n }\n\n if (externalRepositorySignal !== undefined) {\n return {\n status: 'unlinked',\n hint: `Source-control repository ${externalRepositorySignal} is not linked to this Factory.`,\n };\n }\n\n if (mappedRepository) {\n const match = linked.find(repository => repository.slug === mappedRepository);\n return match\n ? resolvedRepository(match)\n : { status: 'unlinked', hint: `Mapped repository ${mappedRepository} is not linked to this Factory.` };\n }\n\n const candidates = [...new Set(linked.map(repository => repository.slug))].sort();\n if (candidates.length === 0) {\n return { status: 'unlinked', hint: 'This Factory has no linked source-control repositories.' };\n }\n const [firstRepository] = linked;\n if (candidates.length === 1 && firstRepository) return resolvedRepository(firstRepository);\n return { status: 'ambiguous', candidates };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"work-item-repository.js","names":[],"sources":["../../src/session/work-item-repository.ts"],"sourcesContent":["import {\n SourceControlConnectionNotFoundError,\n type SourceControlStorageHandle,\n} from '../storage/domains/source-control/base.js';\nimport type { WorkItemRow } from '../storage/domains/work-items/base.js';\n\nexport type WorkItemRepositoryResolution =\n | { status: 'resolved'; projectRepositoryId: string; slug: string }\n | { status: 'unlinked'; hint: string }\n | { status: 'ambiguous'; candidates: string[] };\n\ninterface LinkedRepository {\n projectRepositoryId: string;\n externalId: string;\n slug: string;\n inEnvironment: boolean;\n}\n\nasync function listLinkedRepositories(args: {\n sourceControl: SourceControlStorageHandle;\n orgId: string;\n factoryProjectId: string;\n}): Promise<LinkedRepository[]> {\n const { sourceControl, orgId, factoryProjectId } = args;\n const connections = await sourceControl.connections.list({ orgId, factoryProjectId });\n const linked: LinkedRepository[] = [];\n\n for (const connection of connections.filter(candidate => candidate.integrationId === sourceControl.integrationId)) {\n try {\n const projectRepositories = await sourceControl.projectRepositories.list({ orgId, connectionId: connection.id });\n const repositories = await Promise.all(\n projectRepositories.map(async projectRepository => ({\n projectRepository,\n repository: await sourceControl.repositories.get({ orgId, id: projectRepository.repositoryId }),\n })),\n );\n for (const { projectRepository, repository } of repositories) {\n if (repository) {\n linked.push({\n projectRepositoryId: projectRepository.id,\n externalId: repository.externalId,\n slug: repository.slug,\n inEnvironment: projectRepository.inEnvironment,\n });\n }\n }\n } catch (error) {\n if (!(error instanceof SourceControlConnectionNotFoundError)) throw error;\n // A stale provider connection must not hide healthy linked repositories.\n }\n }\n\n return linked;\n}\n\nfunction getExternalRepositoryId(integrationId: string, metadata: Record<string, unknown>): string | undefined {\n let id: unknown;\n switch (integrationId) {\n case 'github':\n id = metadata.githubRepositoryId;\n break;\n case 'gitlab':\n id = metadata.gitlabProjectId;\n break;\n default:\n return undefined;\n }\n return id == null ? undefined : String(id);\n}\n\n/**\n * The one link among `matches` a run can use. A repository can be linked more\n * than once (a provider-app reinstall adds a second link for the same slug),\n * but only one of those links is in the environment, and only an environment\n * link can start a session, so it wins over its out-of-environment duplicates.\n */\nfunction pickLink(matches: LinkedRepository[]): LinkedRepository | undefined {\n if (matches.length === 1) return matches[0];\n const inEnvironment = matches.filter(repository => repository.inEnvironment);\n return inEnvironment.length === 1 ? inEnvironment[0] : undefined;\n}\n\nfunction findRepositoryBySlug(repositories: LinkedRepository[], slug: string) {\n const matches = repositories.filter(repository => repository.slug === slug);\n return pickLink(matches) ?? matches[0];\n}\n\nfunction resolvedRepository(repository: LinkedRepository): WorkItemRepositoryResolution {\n return { status: 'resolved', projectRepositoryId: repository.projectRepositoryId, slug: repository.slug };\n}\n\nexport async function resolveWorkItemRepository(args: {\n sourceControl: SourceControlStorageHandle;\n orgId: string;\n factoryProjectId: string;\n item: Pick<WorkItemRow, 'metadata'>;\n linearRepositoryMap?: Record<string, string>;\n}): Promise<WorkItemRepositoryResolution> {\n const { sourceControl, item, linearRepositoryMap } = args;\n const metadata = item.metadata ?? {};\n const linked = await listLinkedRepositories(args);\n const repositorySignal = typeof metadata.repository === 'string' ? metadata.repository : undefined;\n const externalRepositorySignal = getExternalRepositoryId(sourceControl.integrationId, metadata);\n const linearProjectId = typeof metadata.linearProjectId === 'string' ? metadata.linearProjectId : undefined;\n const mappedRepository = linearProjectId ? linearRepositoryMap?.[linearProjectId] : undefined;\n\n // The provider id survives repository renames, so it outranks a possibly stale slug.\n if (externalRepositorySignal !== undefined) {\n const match = pickLink(linked.filter(repository => repository.externalId === externalRepositorySignal));\n if (match) return resolvedRepository(match);\n }\n\n if (repositorySignal) {\n const match = findRepositoryBySlug(linked, repositorySignal);\n return match\n ? resolvedRepository(match)\n : { status: 'unlinked', hint: `Repository ${repositorySignal} is not linked to this Factory.` };\n }\n\n if (externalRepositorySignal !== undefined) {\n return {\n status: 'unlinked',\n hint: `Source-control repository ${externalRepositorySignal} is not linked to this Factory.`,\n };\n }\n\n if (mappedRepository) {\n const match = findRepositoryBySlug(linked, mappedRepository);\n return match\n ? resolvedRepository(match)\n : { status: 'unlinked', hint: `Mapped repository ${mappedRepository} is not linked to this Factory.` };\n }\n\n const candidates = [...new Set(linked.map(repository => repository.slug))].sort();\n if (candidates.length === 0) {\n return { status: 'unlinked', hint: 'This Factory has no linked source-control repositories.' };\n }\n // Every link shares the one slug here, so prefer the environment link among them.\n const onlyRepository = candidates.length === 1 ? (pickLink(linked) ?? linked[0]) : undefined;\n if (onlyRepository) return resolvedRepository(onlyRepository);\n return { status: 'ambiguous', candidates };\n}\n"],"mappings":"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"}
@@ -32,7 +32,6 @@ export interface AuditActorProfileInput {
32
32
  avatarUrl?: string;
33
33
  }
34
34
  export declare const ACTOR_PROFILE_METADATA_KEY = "__actorProfile";
35
- export declare function auditAgentName(modeId: string): string;
36
35
  export declare function auditActorProfile(user: {
37
36
  name?: string;
38
37
  email?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/audit/base.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/audit/base.ts"],"names":[],"mappings":"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"}
@@ -16,9 +16,6 @@ import { FactoryStorageDomain, UniqueViolationError } from "@mastra/core/storage
16
16
  * whose message drove the run. Rule-driven events carry `actor_type = 'system'`.
17
17
  */
18
18
  const ACTOR_PROFILE_METADATA_KEY = "__actorProfile";
19
- function auditAgentName(modeId) {
20
- return `${modeId} agent`;
21
- }
22
19
  function auditActorProfile(user) {
23
20
  const name = user?.name?.trim() || user?.email?.trim();
24
21
  const avatarUrl = user?.avatarUrl?.trim();
@@ -204,6 +201,6 @@ var AuditStorage = class extends FactoryStorageDomain {
204
201
  }
205
202
  };
206
203
  //#endregion
207
- export { ACTOR_PROFILE_METADATA_KEY, AUDIT_EVENTS_SCHEMA, AuditStorage, auditActorProfile, auditAgentName, boundAuditMetadata, clampAuditLimit, decodeAuditCursor, encodeAuditCursor };
204
+ export { ACTOR_PROFILE_METADATA_KEY, AUDIT_EVENTS_SCHEMA, AuditStorage, auditActorProfile, boundAuditMetadata, clampAuditLimit, decodeAuditCursor, encodeAuditCursor };
208
205
 
209
206
  //# sourceMappingURL=base.js.map