agent-ecosystem/src/main/services/team/runtime/OpenCodeTeamRuntimeAdapter.ts

1112 lines
42 KiB
TypeScript

import { randomUUID } from 'crypto';
import type {
OpenCodeBridgeRuntimeSnapshot,
OpenCodeLaunchTeamCommandBody,
OpenCodeLaunchTeamCommandData,
OpenCodeObserveMessageDeliveryCommandBody,
OpenCodeObserveMessageDeliveryCommandData,
OpenCodeReconcileTeamCommandBody,
OpenCodeSendMessageCommandBody,
OpenCodeSendMessageCommandData,
OpenCodeStopTeamCommandBody,
OpenCodeStopTeamCommandData,
OpenCodeTeamMemberLaunchBridgeState,
} from '../opencode/bridge/OpenCodeBridgeCommandContract';
import type { OpenCodeTeamLaunchReadiness } from '../opencode/readiness/OpenCodeTeamLaunchReadiness';
import type {
TeamLaunchRuntimeAdapter,
TeamRuntimeLaunchInput,
TeamRuntimeLaunchResult,
TeamRuntimeMemberLaunchEvidence,
TeamRuntimeMemberStopEvidence,
TeamRuntimePrepareResult,
TeamRuntimeReconcileInput,
TeamRuntimeReconcileResult,
TeamRuntimeStopInput,
TeamRuntimeStopResult,
} from './TeamRuntimeAdapter';
import type {
AgentActionMode,
InboxMessage,
InboxMessageKind,
OpenCodeAppManagedBootstrapCandidate,
TaskRef,
} from '@shared/types/team';
export interface OpenCodeTeamRuntimeBridgePort {
checkOpenCodeTeamLaunchReadiness(input: {
projectPath: string;
selectedModel: string | null;
requireExecutionProbe: boolean;
}): Promise<OpenCodeTeamLaunchReadiness>;
getLastOpenCodeRuntimeSnapshot?(projectPath: string): OpenCodeBridgeRuntimeSnapshot | null;
launchOpenCodeTeam?(input: OpenCodeLaunchTeamCommandBody): Promise<OpenCodeLaunchTeamCommandData>;
reconcileOpenCodeTeam?(
input: OpenCodeReconcileTeamCommandBody
): Promise<OpenCodeLaunchTeamCommandData>;
stopOpenCodeTeam?(input: OpenCodeStopTeamCommandBody): Promise<OpenCodeStopTeamCommandData>;
sendOpenCodeTeamMessage?(
input: OpenCodeSendMessageCommandBody
): Promise<OpenCodeSendMessageCommandData>;
observeOpenCodeTeamMessageDelivery?(
input: OpenCodeObserveMessageDeliveryCommandBody
): Promise<OpenCodeObserveMessageDeliveryCommandData>;
}
export interface OpenCodeTeamRuntimeMessageInput {
runId?: string;
teamName: string;
laneId: string;
memberName: string;
cwd: string;
text: string;
messageId?: string;
deliveryAttemptId?: string;
fileParts?: OpenCodeSendMessageCommandBody['fileParts'];
replyRecipient?: string;
actionMode?: AgentActionMode;
messageKind?: InboxMessageKind;
workSyncIntent?: InboxMessage['workSyncIntent'];
workSyncReviewRequestEventIds?: string[];
controlUrl?: string;
taskRefs?: TaskRef[];
bootstrapCheckinRetry?: {
runtimeSessionId: string;
reason?: string;
};
}
export interface OpenCodeTeamRuntimeMessageResult {
ok: boolean;
providerId: 'opencode';
memberName: string;
sessionId?: string;
runtimePid?: number;
prePromptCursor?: string | null;
runtimePromptMessageId?: string;
responseObservation?: OpenCodeSendMessageCommandData['responseObservation'];
diagnostics: string[];
}
const REQUIRED_READY_CHECKPOINTS = new Set([
'required_tools_proven',
'delivery_ready',
'member_ready',
'run_ready',
]);
const GENERIC_OPEN_CODE_MEMBER_FAILURE_REASON = 'OpenCode bridge reported member launch failure';
const SECRET_FLAG_PATTERN =
/(--(?:api-key|token|password|secret|authorization|auth-token)(?:=|\s+))("[^"]*"|'[^']*'|\S+)/gi;
const BEARER_TOKEN_PATTERN = /\bBearer\s+\S+/gi;
const SECRET_KEY_PATTERN = /\bsk-[A-Za-z0-9_-]{16,}\b/g;
function resolveOpenCodeRuntimeSettlementMode(
input: Pick<OpenCodeTeamRuntimeMessageInput, 'messageKind'>
): OpenCodeSendMessageCommandBody['settlementMode'] {
return input.messageKind === 'member_work_sync_nudge' ? 'observed' : 'acceptance';
}
export class OpenCodeTeamRuntimeAdapter implements TeamLaunchRuntimeAdapter {
readonly providerId = 'opencode' as const;
private readonly lastProjectPathByTeamName = new Map<string, string>();
private readonly lastReadinessByProjectPath = new Map<string, OpenCodeTeamLaunchReadiness>();
constructor(private readonly bridge: OpenCodeTeamRuntimeBridgePort) {}
async prepare(input: TeamRuntimeLaunchInput): Promise<TeamRuntimePrepareResult> {
const runtimeOnly = input.runtimeOnly === true;
const readiness = await this.bridge.checkOpenCodeTeamLaunchReadiness({
projectPath: input.cwd,
selectedModel: input.model ?? null,
requireExecutionProbe: !runtimeOnly,
});
this.lastReadinessByProjectPath.set(input.cwd, readiness);
if (!readiness.launchAllowed) {
return {
ok: false,
providerId: this.providerId,
reason: readiness.state,
retryable: isRetryableReadinessState(readiness.state),
diagnostics: mergeDiagnostics(readiness.diagnostics, readiness.missing),
warnings: [],
};
}
return {
ok: true,
providerId: this.providerId,
modelId: readiness.modelId,
diagnostics: readiness.diagnostics,
warnings: [],
};
}
getLastOpenCodeTeamLaunchReadiness(projectPath: string): OpenCodeTeamLaunchReadiness | null {
return this.lastReadinessByProjectPath.get(projectPath) ?? null;
}
async launch(input: TeamRuntimeLaunchInput): Promise<TeamRuntimeLaunchResult> {
const memberValidationDiagnostics = validateOpenCodeRuntimeMembers(
input.expectedMembers,
input.cwd
);
if (memberValidationDiagnostics.length > 0) {
return blockedLaunchResult(
input,
'opencode_invalid_expected_members',
memberValidationDiagnostics
);
}
const skipReadinessPreflight = input.skipReadinessPreflight === true;
let selectedModel = input.model?.trim() ?? '';
let launchWarnings: string[] = [];
if (!skipReadinessPreflight) {
const prepared = await this.prepare(input);
if (!prepared.ok) {
return blockedLaunchResult(input, prepared.reason, prepared.diagnostics, prepared.warnings);
}
selectedModel = prepared.modelId ?? selectedModel;
launchWarnings = prepared.warnings;
}
if (!this.bridge.launchOpenCodeTeam) {
return blockedLaunchResult(input, 'opencode_launch_bridge_missing', [
'OpenCode state-changing launch bridge is not registered.',
]);
}
if (!selectedModel) {
return blockedLaunchResult(input, 'opencode_model_unavailable', [
'OpenCode launch requires a selected raw model id.',
]);
}
const runtimeSnapshot = skipReadinessPreflight
? null
: (this.bridge.getLastOpenCodeRuntimeSnapshot?.(input.cwd) ?? null);
if (
!skipReadinessPreflight &&
this.bridge.getLastOpenCodeRuntimeSnapshot &&
!runtimeSnapshot?.capabilitySnapshotId
) {
return blockedLaunchResult(input, 'opencode_capability_snapshot_missing', [
'OpenCode app-managed launch requires a fresh capability snapshot before state-changing launch.',
]);
}
this.lastProjectPathByTeamName.set(input.teamName, input.cwd);
const data = await this.bridge.launchOpenCodeTeam({
runId: input.runId,
laneId: input.laneId?.trim() || 'primary',
teamId: input.teamName,
teamName: input.teamName,
projectPath: input.cwd,
selectedModel,
members: input.expectedMembers.map((member) => ({
name: member.name,
role: member.role?.trim() || member.workflow?.trim() || 'teammate',
prompt: buildMemberBootstrapPrompt(input, member),
})),
leadPrompt: input.prompt?.trim() ?? '',
expectedCapabilitySnapshotId: runtimeSnapshot?.capabilitySnapshotId ?? null,
manifestHighWatermark: null,
});
return mapOpenCodeLaunchDataToRuntimeResult(input, data, launchWarnings);
}
async reconcile(input: TeamRuntimeReconcileInput): Promise<TeamRuntimeReconcileResult> {
const memberValidationDiagnostics = validateOpenCodeRuntimeMembers(input.expectedMembers);
if (memberValidationDiagnostics.length > 0) {
return {
...blockedLaunchResult(
{
runId: input.runId,
teamName: input.teamName,
cwd: input.expectedMembers[0]?.cwd ?? '',
providerId: this.providerId,
skipPermissions: false,
expectedMembers: input.expectedMembers,
previousLaunchState: input.previousLaunchState,
},
'opencode_invalid_expected_members',
memberValidationDiagnostics
),
snapshot: input.previousLaunchState,
};
}
if (this.bridge.reconcileOpenCodeTeam) {
const projectPath =
input.expectedMembers[0]?.cwd ?? this.lastProjectPathByTeamName.get(input.teamName);
const runtimeSnapshot = projectPath
? (this.bridge.getLastOpenCodeRuntimeSnapshot?.(projectPath) ?? null)
: null;
const data = await this.bridge.reconcileOpenCodeTeam({
runId: input.runId,
laneId: input.laneId?.trim() || 'primary',
teamId: input.teamName,
teamName: input.teamName,
projectPath,
expectedCapabilitySnapshotId: runtimeSnapshot?.capabilitySnapshotId ?? null,
manifestHighWatermark: null,
reconcileAttemptId: `opencode-reconcile-${randomUUID()}`,
expectedMembers: input.expectedMembers.map((member) => ({
name: member.name,
model: member.model ?? null,
})),
reason: input.reason,
});
const mapped = mapOpenCodeLaunchDataToRuntimeResult(
{
runId: input.runId,
teamName: input.teamName,
cwd: input.expectedMembers[0]?.cwd ?? '',
providerId: this.providerId,
skipPermissions: false,
expectedMembers: input.expectedMembers,
previousLaunchState: input.previousLaunchState,
},
data,
[]
);
return {
...mapped,
snapshot: input.previousLaunchState,
};
}
const snapshot = input.previousLaunchState;
if (!snapshot) {
return {
runId: input.runId,
teamName: input.teamName,
launchPhase: 'reconciled',
teamLaunchState: 'partial_pending',
members: {},
snapshot: null,
warnings: [],
diagnostics: ['No previous OpenCode launch snapshot was available for reconciliation.'],
};
}
return {
runId: input.runId,
teamName: input.teamName,
launchPhase: snapshot.launchPhase,
teamLaunchState: snapshot.teamLaunchState,
members: Object.fromEntries(
Object.entries(snapshot.members).map(([memberName, member]) => [
memberName,
{
memberName,
providerId: this.providerId,
launchState: member.launchState,
agentToolAccepted: member.agentToolAccepted,
runtimeAlive: member.bootstrapConfirmed === true,
bootstrapConfirmed: member.bootstrapConfirmed,
hardFailure: member.hardFailure,
hardFailureReason: member.hardFailureReason,
diagnostics: member.diagnostics ?? [],
} satisfies TeamRuntimeMemberLaunchEvidence,
])
),
snapshot,
warnings: [],
diagnostics: [`OpenCode launch snapshot reconciled from ${input.reason}.`],
};
}
async sendMessageToMember(
input: OpenCodeTeamRuntimeMessageInput
): Promise<OpenCodeTeamRuntimeMessageResult> {
if (!this.bridge.sendOpenCodeTeamMessage) {
return {
ok: false,
providerId: this.providerId,
memberName: input.memberName,
diagnostics: ['OpenCode message bridge is not registered.'],
};
}
const data = await this.bridge.sendOpenCodeTeamMessage({
runId: input.runId,
laneId: input.laneId,
teamId: input.teamName,
teamName: input.teamName,
projectPath: input.cwd,
memberName: input.memberName,
text: buildOpenCodeRuntimeMessageText(input),
messageId: input.messageId,
...(input.deliveryAttemptId ? { deliveryAttemptId: input.deliveryAttemptId } : {}),
settlementMode: resolveOpenCodeRuntimeSettlementMode(input),
fileParts: input.fileParts,
actionMode: input.actionMode,
messageKind: input.messageKind,
taskRefs: input.taskRefs,
agent: 'teammate',
});
return {
ok: data.accepted,
providerId: this.providerId,
memberName: input.memberName,
sessionId: data.sessionId,
runtimePid: data.runtimePid,
prePromptCursor: data.prePromptCursor,
runtimePromptMessageId: data.runtimePromptMessageId,
responseObservation: data.responseObservation,
diagnostics: data.diagnostics.map((diagnostic) => diagnostic.message),
};
}
async observeMessageDelivery(
input: OpenCodeTeamRuntimeMessageInput & {
prePromptCursor?: string | null;
sessionId?: string;
runtimePromptMessageId?: string;
}
): Promise<OpenCodeTeamRuntimeMessageResult> {
if (!this.bridge.observeOpenCodeTeamMessageDelivery) {
return {
ok: false,
providerId: this.providerId,
memberName: input.memberName,
diagnostics: ['OpenCode message delivery observe bridge is not registered.'],
};
}
if (!input.messageId?.trim()) {
return {
ok: false,
providerId: this.providerId,
memberName: input.memberName,
diagnostics: ['OpenCode message delivery observe requires messageId.'],
};
}
const data = await this.bridge.observeOpenCodeTeamMessageDelivery({
runId: input.runId,
laneId: input.laneId,
teamId: input.teamName,
teamName: input.teamName,
projectPath: input.cwd,
memberName: input.memberName,
messageId: input.messageId,
sessionId: input.sessionId,
runtimePromptMessageId: input.runtimePromptMessageId,
prePromptCursor: input.prePromptCursor ?? null,
});
return {
ok: data.observed,
providerId: this.providerId,
memberName: input.memberName,
sessionId: data.sessionId,
runtimePid: data.runtimePid,
runtimePromptMessageId: data.runtimePromptMessageId,
responseObservation: data.responseObservation,
diagnostics: data.diagnostics.map((diagnostic) => diagnostic.message),
};
}
async stop(input: TeamRuntimeStopInput): Promise<TeamRuntimeStopResult> {
if (this.bridge.stopOpenCodeTeam) {
const projectPath = input.cwd ?? this.lastProjectPathByTeamName.get(input.teamName);
const runtimeSnapshot = projectPath
? (this.bridge.getLastOpenCodeRuntimeSnapshot?.(projectPath) ?? null)
: null;
const data = await this.bridge.stopOpenCodeTeam({
runId: input.runId,
laneId: input.laneId?.trim() || 'primary',
teamId: input.teamName,
teamName: input.teamName,
projectPath,
expectedCapabilitySnapshotId: runtimeSnapshot?.capabilitySnapshotId ?? null,
manifestHighWatermark: null,
reason: input.reason,
force: input.force,
});
if (data.stopped) {
this.lastProjectPathByTeamName.delete(input.teamName);
}
return {
runId: input.runId,
teamName: input.teamName,
stopped: data.stopped,
members: Object.fromEntries(
Object.entries(data.members).map(([memberName, member]) => [
memberName,
{
memberName,
providerId: this.providerId,
stopped: member.stopped,
sessionId: member.sessionId,
diagnostics: member.diagnostics,
} satisfies TeamRuntimeMemberStopEvidence,
])
),
warnings: data.warnings.map((warning) => warning.message),
diagnostics: data.diagnostics.map(formatOpenCodeBridgeDiagnostic),
};
}
const members = input.previousLaunchState
? Object.fromEntries(
Object.keys(input.previousLaunchState.members).map((memberName) => [
memberName,
{
memberName,
providerId: this.providerId,
stopped: true,
diagnostics: [
'No live OpenCode session stop command is wired in this adapter shell.',
],
} satisfies TeamRuntimeMemberStopEvidence,
])
)
: {};
return {
runId: input.runId,
teamName: input.teamName,
stopped: true,
members,
warnings: [],
diagnostics: input.previousLaunchState
? ['OpenCode stop was acknowledged without live session ownership changes.']
: ['No previous OpenCode launch snapshot was available to stop.'],
};
}
}
function mapOpenCodeLaunchDataToRuntimeResult(
input: TeamRuntimeLaunchInput,
data: OpenCodeLaunchTeamCommandData,
prepareWarnings: string[]
): TeamRuntimeLaunchResult {
const bridgeDiagnostics = data.diagnostics.map(formatOpenCodeBridgeDiagnostic);
const memberBridgeDiagnostics = bridgeDiagnostics.filter(
(diagnostic) => !isOpenCodeLaunchTimingDiagnostic(diagnostic)
);
const checkpointNames = extractCheckpointNames(data);
const readyCheckpointsPresent = [...REQUIRED_READY_CHECKPOINTS].every((name) =>
checkpointNames.has(name)
);
const bridgeReady = data.teamLaunchState === 'ready';
const isExpectedMemberConfirmed = (memberName: string): boolean => {
const bridgeMember = data.members[memberName];
return bridgeMember?.launchState === 'confirmed_alive';
};
const missingExpectedMembers = input.expectedMembers
.map((member) => member.name)
.filter((memberName) => data.members[memberName] == null);
const unconfirmedExpectedMembers = input.expectedMembers
.map((member) => member.name)
.filter((memberName) => !isExpectedMemberConfirmed(memberName));
const anyExpectedMemberFailed = input.expectedMembers.some(
(member) => data.members[member.name]?.launchState === 'failed'
);
const allExpectedMembersConfirmed =
input.expectedMembers.length > 0 && unconfirmedExpectedMembers.length === 0;
const success =
(bridgeReady && readyCheckpointsPresent && allExpectedMembersConfirmed) ||
(data.teamLaunchState === 'launching' && allExpectedMembersConfirmed);
const checkpointDiagnostic = success
? []
: bridgeReady && !readyCheckpointsPresent
? [
`OpenCode bridge reported ready without all required durable checkpoints: missing ${[
...REQUIRED_READY_CHECKPOINTS,
]
.filter((name) => !checkpointNames.has(name))
.join(', ')}`,
]
: [];
const incompleteReadyDiagnostic =
bridgeReady && readyCheckpointsPresent && !allExpectedMembersConfirmed
? [
`OpenCode bridge reported ready before all expected members were confirmed: pending ${unconfirmedExpectedMembers.join(', ')}`,
]
: [];
const members = Object.fromEntries(
input.expectedMembers.map((member) => {
const bridgeMember = data.members[member.name];
const fallbackLaunchState = bridgeMember
? bridgeMember.launchState
: data.teamLaunchState === 'failed'
? 'failed'
: 'created';
const checkpointDiagnosticsForMember = [
...checkpointDiagnostic,
...(missingExpectedMembers.includes(member.name) ? incompleteReadyDiagnostic : []),
];
const memberDiagnostics = [
...(bridgeMember
? []
: [
`OpenCode bridge response did not include ${member.name}; keeping the member pending until lane state materializes.`,
]),
...(bridgeMember?.diagnostics ?? []),
...(bridgeMember?.evidence ?? []).map(
(evidence) => `${evidence.kind} at ${evidence.observedAt}`
),
...memberBridgeDiagnostics,
...checkpointDiagnosticsForMember,
];
return [
member.name,
mapBridgeMemberToRuntimeEvidence(
member.name,
fallbackLaunchState,
bridgeMember?.sessionId,
bridgeMember?.runtimePid,
bridgeMember?.pendingPermissionRequestIds,
bridgeMember != null,
memberDiagnostics,
input.runId,
input.laneId?.trim() || 'primary',
input.teamName,
bridgeMember?.bootstrapEvidenceSource,
bridgeMember?.bootstrapMode,
bridgeMember?.appManagedBootstrapCandidate,
selectOpenCodeMemberFailureReason({
memberDiagnostics: bridgeMember?.diagnostics ?? [],
bridgeDiagnostics: data.diagnostics,
checkpointDiagnostics: checkpointDiagnosticsForMember,
fallback: GENERIC_OPEN_CODE_MEMBER_FAILURE_REASON,
})
),
];
})
);
return {
runId: input.runId,
teamName: input.teamName,
launchPhase: success
? 'finished'
: data.teamLaunchState === 'launching' || (bridgeReady && !anyExpectedMemberFailed)
? 'active'
: 'finished',
teamLaunchState: success
? 'clean_success'
: anyExpectedMemberFailed || data.teamLaunchState === 'failed'
? 'partial_failure'
: data.teamLaunchState === 'launching' ||
data.teamLaunchState === 'permission_blocked' ||
bridgeReady
? 'partial_pending'
: 'partial_failure',
members,
warnings: [...prepareWarnings, ...data.warnings.map((warning) => warning.message)],
diagnostics: [...bridgeDiagnostics, ...checkpointDiagnostic, ...incompleteReadyDiagnostic],
};
}
function isNonEmptyString(value: unknown): value is string {
return typeof value === 'string' && value.trim().length > 0;
}
function normalizeAppManagedBootstrapCandidate(
value: OpenCodeAppManagedBootstrapCandidate | undefined,
expected: {
teamName: string;
memberName: string;
runId: string;
laneId: string;
runtimeSessionId?: string;
}
): OpenCodeAppManagedBootstrapCandidate | undefined {
if (value?.schemaVersion !== 1 || value.source !== 'app_managed_bootstrap') {
return undefined;
}
if (
value.teamName !== expected.teamName ||
value.memberName !== expected.memberName ||
value.runId !== expected.runId ||
value.laneId !== expected.laneId ||
(expected.runtimeSessionId && value.runtimeSessionId !== expected.runtimeSessionId)
) {
return undefined;
}
if (
!isNonEmptyString(value.runtimeSessionId) ||
!isNonEmptyString(value.messageID) ||
!value.messageID.startsWith('msg') ||
!isNonEmptyString(value.contextHash) ||
!isNonEmptyString(value.briefingHash) ||
!isNonEmptyString(value.injectionVerifiedAt) ||
!isNonEmptyString(value.candidateAt)
) {
return undefined;
}
return {
schemaVersion: 1,
source: 'app_managed_bootstrap',
teamName: value.teamName,
memberName: value.memberName,
runId: value.runId,
laneId: value.laneId,
runtimeSessionId: value.runtimeSessionId,
messageID: value.messageID,
contextHash: value.contextHash,
briefingHash: value.briefingHash,
injectionVerifiedAt: value.injectionVerifiedAt,
candidateAt: value.candidateAt,
...(isNonEmptyString(value.model) ? { model: value.model } : {}),
...(isNonEmptyString(value.agent) ? { agent: value.agent } : {}),
};
}
function mapBridgeMemberToRuntimeEvidence(
memberName: string,
launchState: OpenCodeTeamMemberLaunchBridgeState,
sessionId: string | undefined,
runtimePid: number | undefined,
pendingPermissionRequestIds: string[] | undefined,
runtimeMaterialized: boolean,
diagnostics: string[],
runId: string,
laneId: string,
teamName: string,
bootstrapEvidenceSource: TeamRuntimeMemberLaunchEvidence['bootstrapEvidenceSource'] | undefined,
bootstrapMode: TeamRuntimeMemberLaunchEvidence['bootstrapMode'] | undefined,
appManagedBootstrapCandidate: OpenCodeAppManagedBootstrapCandidate | undefined,
selectedHardFailureReason: string
): TeamRuntimeMemberLaunchEvidence {
const normalizedAppManagedCandidate = normalizeAppManagedBootstrapCandidate(
appManagedBootstrapCandidate,
{
teamName,
memberName,
runId,
laneId,
runtimeSessionId: sessionId,
}
);
const appManagedCandidatePresent =
launchState === 'created' &&
isNonEmptyString(sessionId) &&
bootstrapEvidenceSource === 'app_managed_bootstrap' &&
bootstrapMode === 'app_managed_context' &&
normalizedAppManagedCandidate != null;
const confirmed = launchState === 'confirmed_alive';
const failed = launchState === 'failed';
const hasRuntimePid =
typeof runtimePid === 'number' && Number.isFinite(runtimePid) && runtimePid > 0;
const hasSessionId = typeof sessionId === 'string' && sessionId.trim().length > 0;
const hasRuntimeHandle = hasRuntimePid || hasSessionId;
const pendingRuntimeObserved = launchState === 'created' && hasRuntimeHandle;
const livenessKind = confirmed
? 'confirmed_bootstrap'
: pendingRuntimeObserved
? 'runtime_process_candidate'
: launchState === 'permission_blocked'
? 'permission_blocked'
: 'registered_only';
const runtimeDiagnostic = appManagedCandidatePresent
? 'OpenCode app-managed bootstrap context was injected and verified by the bridge; waiting for app-owned durable evidence commit.'
: pendingRuntimeObserved
? hasRuntimePid
? 'OpenCode runtime pid reported by bridge without local process verification'
: 'OpenCode session exists without verified runtime pid'
: launchState === 'permission_blocked'
? 'OpenCode runtime is waiting for permission approval'
: runtimeMaterialized
? 'OpenCode bridge did not report a runtime session or pid for this member'
: undefined;
const runtimeDiagnosticSeverity = appManagedCandidatePresent
? 'info'
: failed
? 'error'
: pendingRuntimeObserved || launchState === 'permission_blocked' || runtimeMaterialized
? 'warning'
: undefined;
return {
memberName,
providerId: 'opencode',
launchState: failed
? 'failed_to_start'
: confirmed
? 'confirmed_alive'
: launchState === 'permission_blocked'
? 'runtime_pending_permission'
: 'runtime_pending_bootstrap',
agentToolAccepted:
confirmed ||
pendingRuntimeObserved ||
launchState === 'permission_blocked' ||
hasRuntimeHandle,
runtimeAlive: confirmed,
bootstrapConfirmed: confirmed,
hardFailure: failed,
hardFailureReason: failed ? selectedHardFailureReason : undefined,
pendingPermissionRequestIds:
pendingPermissionRequestIds && pendingPermissionRequestIds.length > 0
? [...new Set(pendingPermissionRequestIds)]
: undefined,
sessionId,
...(appManagedCandidatePresent
? { bootstrapEvidenceSource: 'app_managed_bootstrap' as const }
: {}),
...(appManagedCandidatePresent ? { bootstrapMode: 'app_managed_context' as const } : {}),
...(normalizedAppManagedCandidate
? { appManagedBootstrapCandidate: normalizedAppManagedCandidate }
: {}),
...(hasRuntimePid ? { runtimePid } : {}),
livenessKind,
...(hasRuntimePid ? { pidSource: 'opencode_bridge' as const } : {}),
...(runtimeDiagnostic ? { runtimeDiagnostic } : {}),
...(runtimeDiagnosticSeverity ? { runtimeDiagnosticSeverity } : {}),
diagnostics,
};
}
function selectOpenCodeMemberFailureReason(input: {
memberDiagnostics: readonly string[];
bridgeDiagnostics: readonly {
code: string;
severity: 'info' | 'warning' | 'error';
message: string;
}[];
checkpointDiagnostics: readonly string[];
fallback: string;
}): string {
return (
firstDisplayableOpenCodeFailureMessage(input.memberDiagnostics, { includeGeneric: false }) ??
firstDisplayableOpenCodeFailureMessage(
input.bridgeDiagnostics
.filter((diagnostic) => diagnostic.severity === 'error')
.map((diagnostic) => diagnostic.message),
{ includeGeneric: false }
) ??
firstDisplayableOpenCodeFailureMessage(input.memberDiagnostics, { includeGeneric: true }) ??
firstDisplayableOpenCodeFailureMessage(input.checkpointDiagnostics, { includeGeneric: true }) ??
firstDisplayableOpenCodeFailureMessage(
input.bridgeDiagnostics
.filter((diagnostic) => diagnostic.severity !== 'info')
.map((diagnostic) => diagnostic.message),
{ includeGeneric: true }
) ??
normalizeOpenCodeFailureMessage(input.fallback) ??
GENERIC_OPEN_CODE_MEMBER_FAILURE_REASON
);
}
function firstDisplayableOpenCodeFailureMessage(
values: readonly string[],
options: { includeGeneric: boolean }
): string | undefined {
for (const value of values) {
const normalized = normalizeOpenCodeFailureMessage(value);
if (!normalized) {
continue;
}
if (!options.includeGeneric && isGenericOpenCodeFailureMessage(normalized)) {
continue;
}
return normalized;
}
return undefined;
}
function normalizeOpenCodeFailureMessage(value: string | undefined): string | undefined {
const trimmed = value?.replace(/\s+/g, ' ').trim();
if (!trimmed) {
return undefined;
}
return trimmed
.replace(SECRET_FLAG_PATTERN, '$1[redacted]')
.replace(BEARER_TOKEN_PATTERN, 'Bearer [redacted]')
.replace(SECRET_KEY_PATTERN, '[redacted-api-key]');
}
function isGenericOpenCodeFailureMessage(message: string): boolean {
return (
message === GENERIC_OPEN_CODE_MEMBER_FAILURE_REASON ||
message.startsWith(`${GENERIC_OPEN_CODE_MEMBER_FAILURE_REASON}:`) ||
message.startsWith('OpenCode secondary lane timing:') ||
message.startsWith(
'OpenCode bridge reported ready without all required durable checkpoints:'
) ||
message.startsWith(
'OpenCode bridge reported ready before all expected members were confirmed:'
) ||
message.startsWith(
'OpenCode bootstrap MCP did not complete required tools before assistant response:'
) ||
isOpenCodeLaunchTimingDiagnostic(message)
);
}
function extractCheckpointNames(data: OpenCodeLaunchTeamCommandData): Set<string> {
const names = new Set<string>();
for (const checkpoint of data.durableCheckpoints ?? []) {
if (checkpoint.name.trim()) names.add(checkpoint.name);
}
for (const member of Object.values(data.members)) {
for (const evidence of member.evidence) {
if (evidence.kind.trim()) names.add(evidence.kind);
}
}
return names;
}
function buildMemberBootstrapPrompt(
input: TeamRuntimeLaunchInput,
member: TeamRuntimeLaunchInput['expectedMembers'][number]
): string {
const teamPrompt = input.prompt?.trim();
const role = member.role?.trim() || member.workflow?.trim() || 'teammate';
const workflow = member.workflow?.trim();
return [
'<agent_teams_app_managed_bootstrap_briefing>',
'AGENT_TEAMS_APP_MANAGED_BOOTSTRAP_V1',
`You are ${member.name}, a ${role} on team "${input.teamName}".`,
teamPrompt ? `Team launch context:\n${teamPrompt}` : null,
workflow ? `Workflow:\n${workflow}` : null,
'',
'This OpenCode session is created, attached, and launch-verified by the desktop app.',
'Do not call runtime_bootstrap_checkin or member_briefing just to prove launch readiness.',
'Do NOT create local team files, run join scripts, or search the project for a fake team registry.',
'Use the app MCP tools exposed by the "agent-teams" server for team communication and task state.',
'Launch bootstrap is a silent attach, not a user/team conversation turn.',
'Do not call task_briefing, message_send, or cross_team_send just to announce readiness, say understood, report no tasks, or ask for work.',
'If the briefing says there are no actionable tasks, stay idle silently.',
'',
'When you need to message the human user, team lead, or another teammate, call MCP tool agent-teams_message_send (or mcp__agent-teams__message_send) with teamName, to, from, text, and optional summary.',
`Always set from="${member.name}" when sending a team message from this OpenCode teammate.`,
'Do not answer team/app messages only as plain assistant text when agent-teams_message_send is available.',
'</agent_teams_app_managed_bootstrap_briefing>',
]
.filter((line): line is string => line !== null)
.join('\n');
}
function buildOpenCodeRuntimeMessageText(input: OpenCodeTeamRuntimeMessageInput): string {
if (input.bootstrapCheckinRetry) {
const runtimeSessionId = input.bootstrapCheckinRetry.runtimeSessionId.trim();
return [
'<opencode_runtime_bootstrap_checkin_retry>',
'The desktop app detected that this OpenCode session exists, but runtime_bootstrap_checkin has not committed durable runtime evidence yet.',
input.bootstrapCheckinRetry.reason
? `Reason: ${input.bootstrapCheckinRetry.reason.trim()}`
: null,
'Before any other tool or message, call MCP tool agent-teams_runtime_bootstrap_checkin or mcp__agent-teams__runtime_bootstrap_checkin with exactly:',
JSON.stringify({
runId: input.runId,
teamName: input.teamName,
memberName: input.memberName,
runtimeSessionId,
}),
'Do not call member_briefing, task tools, message_send, or cross_team_send before runtime_bootstrap_checkin completes.',
'After runtime_bootstrap_checkin succeeds, stop this turn immediately and wait silently.',
'If runtime_bootstrap_checkin is unavailable or fails, reply with one short sentence containing the exact error text, then stop.',
'</opencode_runtime_bootstrap_checkin_retry>',
]
.filter((line): line is string => line !== null)
.join('\n');
}
const replyRecipient = input.replyRecipient?.trim() || 'user';
const deliveryContext =
input.messageId && (input.taskRefs?.length || input.messageKind)
? JSON.stringify({
schemaVersion: 1,
kind: 'opencode-delivery-context',
teamName: input.teamName,
laneId: input.laneId,
memberName: input.memberName,
inboundMessageId: input.messageId,
...(input.messageKind ? { messageKind: input.messageKind } : {}),
...(input.workSyncIntent ? { workSyncIntent: input.workSyncIntent } : {}),
...(input.workSyncReviewRequestEventIds?.length
? { workSyncReviewRequestEventIds: input.workSyncReviewRequestEventIds }
: {}),
taskRefs: input.taskRefs,
})
: null;
const isWorkSyncNudge = input.messageKind === 'member_work_sync_nudge';
const isReviewPickupNudge = isWorkSyncNudge && input.workSyncIntent === 'review_pickup';
const workSyncToolArgs = buildOpenCodeWorkSyncToolArgs(input);
const taskIds =
input.taskRefs
?.map((ref) => ref.taskId?.trim())
.filter((taskId): taskId is string => Boolean(taskId)) ?? [];
// Work-sync nudges are health/reporting probes. Requiring a visible
// message_send reply here causes false delivery failures, so accept the
// dedicated member_work_sync_report proof path while keeping normal user
// messages on the visible reply contract.
const responseInstructions = isReviewPickupNudge
? [
'This delivered app message is a targeted member-work-sync review pickup nudge.',
'Process the current review request now if it is still assigned to you. Open the task, verify reviewState/status, then use the review workflow tools to start or continue the review.',
'Do not mark the review complete from this prompt alone.',
'A visible agent-teams_message_send reply is optional. Concrete review progress, review tool usage, or agent-teams_member_work_sync_report (or mcp__agent-teams__member_work_sync_report) is sufficient response proof.',
`If you cannot pick up the review now, call agent-teams_member_work_sync_status (or mcp__agent-teams__member_work_sync_status) with ${workSyncToolArgs}, then report state "blocked" or "still_working" only for the real current state.`,
taskIds.length ? `Relevant taskIds: ${taskIds.map((id) => `"${id}"`).join(', ')}.` : null,
`Do not use provider names, runtime names, or team names as memberName; use exactly "${input.memberName}".`,
'Do not reply only with acknowledgement.',
]
: isWorkSyncNudge
? [
'This delivered app message is a member-work-sync nudge.',
'A visible agent-teams_message_send reply is optional. Concrete task progress or agent-teams_member_work_sync_report (or mcp__agent-teams__member_work_sync_report) is sufficient response proof.',
`Call agent-teams_member_work_sync_status (or mcp__agent-teams__member_work_sync_status) with ${workSyncToolArgs}.`,
`Then call agent-teams_member_work_sync_report (or mcp__agent-teams__member_work_sync_report) with ${workSyncToolArgs}, the returned agendaFingerprint/reportToken, and state "still_working" or "blocked".`,
taskIds.length
? `When reporting, include taskIds: ${taskIds.map((id) => `"${id}"`).join(', ')}.`
: null,
`Do not use provider names, runtime names, or team names as memberName; use exactly "${input.memberName}".`,
'Do not reply only with acknowledgement.',
]
: [
'To make your reply visible in the app Messages UI, call MCP tool agent-teams_message_send (or mcp__agent-teams__message_send if that is the exposed name).',
`Use teamName="${input.teamName}", to="${replyRecipient}", from="${input.memberName}", text, and summary.`,
'Include source="runtime_delivery" in that message_send call.',
input.messageId
? `Include relayOfMessageId="${input.messageId}" in that message_send call.`
: null,
input.taskRefs?.length
? `If taskRefs are present in <opencode_delivery_context>, include taskRefs exactly as provided in that message_send call: ${JSON.stringify(input.taskRefs)}.`
: null,
'If message_send returns an unavailable, not connected, or missing-tool error, write the exact concise reply as plain assistant text once, then stop.',
'After the message_send tool call succeeds, stop immediately. Do not send follow-up confirmations or repeat the same answer.',
'You must not end this turn empty.',
'Do not answer only with plain assistant text when agent-teams_message_send is available.',
];
return [
'<opencode_app_message_delivery>',
deliveryContext
? `<opencode_delivery_context>${deliveryContext}</opencode_delivery_context>`
: null,
'You are running in OpenCode, not Claude Code or Codex native.',
...responseInstructions,
'Do not call runtime_bootstrap_checkin or member_briefing just to answer this delivered app message.',
'Do not use SendMessage or runtime_deliver_message for ordinary visible replies.',
'Do not invent placeholder task labels. If no explicit taskRefs are provided and the reply is not about a real board task, do not prefix text or summary with a # task label; never use #00000000.',
'The inbound app message follows. Treat it as the actual instruction to process now, not as background context.',
'If the inbound message asks for exact reply text, use that exact text. Do not replace concrete instructions with a generic greeting or availability message.',
input.actionMode ? `Action mode for this message: ${input.actionMode}.` : null,
'</opencode_app_message_delivery>',
'',
'<opencode_inbound_app_message>',
input.text,
'</opencode_inbound_app_message>',
]
.filter((line): line is string => line !== null)
.join('\n');
}
function buildOpenCodeWorkSyncToolArgs(input: OpenCodeTeamRuntimeMessageInput): string {
const args = [`teamName="${input.teamName}"`, `memberName="${input.memberName}"`];
const controlUrl = input.controlUrl?.trim();
if (controlUrl) {
args.push(`controlUrl=${JSON.stringify(controlUrl)}`);
}
return args.join(', ');
}
function validateOpenCodeRuntimeMembers(
members: TeamRuntimeLaunchInput['expectedMembers'],
launchCwd?: string
): string[] {
if (members.length === 0) {
return ['OpenCode runtime adapter requires at least one expected OpenCode member.'];
}
const diagnostics = members.flatMap((member, index) => {
const name = member.name.trim() || `<index ${index}>`;
if (member.providerId === 'opencode') {
return [];
}
return [
`OpenCode runtime adapter received non-OpenCode member "${name}" with provider "${member.providerId}".`,
];
});
const memberCwds = [
...new Set(members.map((member) => member.cwd.trim()).filter((cwd) => cwd.length > 0)),
];
if (memberCwds.length > 1) {
diagnostics.push(
'OpenCode runtime adapter currently supports one project path per lane. Launch isolated OpenCode teammates as separate side lanes.'
);
}
const onlyMemberCwd = memberCwds.length === 1 ? memberCwds[0] : null;
if (launchCwd?.trim() && onlyMemberCwd && onlyMemberCwd !== launchCwd.trim()) {
diagnostics.push(
`OpenCode runtime lane cwd mismatch: launch cwd "${launchCwd.trim()}" differs from member cwd "${onlyMemberCwd}".`
);
}
return diagnostics;
}
function formatOpenCodeBridgeDiagnostic(diagnostic: {
code: string;
severity: 'info' | 'warning' | 'error';
message: string;
}): string {
return `${diagnostic.severity}:${diagnostic.code}: ${diagnostic.message}`;
}
function isOpenCodeLaunchTimingDiagnostic(diagnostic: string): boolean {
return (
diagnostic.startsWith('info:opencode_launch_member_timing:') ||
diagnostic.startsWith('info:opencode_launch_total_timing:')
);
}
function blockedLaunchResult(
input: TeamRuntimeLaunchInput,
reason: string,
diagnostics: string[],
warnings: string[] = []
): TeamRuntimeLaunchResult {
const hardFailureReason =
reason === 'unknown_error' && diagnostics[0]?.trim() ? diagnostics[0].trim() : reason;
const members = Object.fromEntries(
input.expectedMembers.map((member) => [
member.name,
{
memberName: member.name,
providerId: 'opencode' as const,
launchState: 'failed_to_start' as const,
agentToolAccepted: false,
runtimeAlive: false,
bootstrapConfirmed: false,
hardFailure: true,
hardFailureReason,
diagnostics,
},
])
);
return {
runId: input.runId,
teamName: input.teamName,
launchPhase: 'finished',
teamLaunchState: 'partial_failure',
members,
warnings,
diagnostics,
};
}
function isRetryableReadinessState(state: OpenCodeTeamLaunchReadiness['state']): boolean {
return (
state === 'not_installed' ||
state === 'not_authenticated' ||
state === 'runtime_store_blocked' ||
state === 'mcp_unavailable' ||
state === 'model_unavailable' ||
state === 'unknown_error'
);
}
function mergeDiagnostics(left: string[], right: string[]): string[] {
return [...new Set([...left, ...right].filter((value) => value.trim().length > 0))];
}