mirror of
https://github.com/stablyai/orca.git
synced 2026-09-29 08:03:20 +00:00
* refactor: split agent config and auth services * chore: repoint wsl and global-fetch guards at split module paths * fix: restore merge-base Claude CLI error propagation Drop the secret-redaction rewriting added to Claude CLI error paths in the refactor: spawn errors again reject with the original Error (preserving .code/.errno/.syscall/.stack) and command output/auth-status logs are no longer rewritten.
137 lines
7.0 KiB
TypeScript
137 lines
7.0 KiB
TypeScript
export function getStatusPluginLifecycleSource(): string[] {
|
|
return [
|
|
'async function handleLifecycleEvent(client, event, factoryID) {',
|
|
' const sessionID = event.properties?.sessionID;',
|
|
' const statusType = getStatusType(event);',
|
|
' const isResolutionEvent =',
|
|
' event.type === "permission.replied" ||',
|
|
' event.type === "question.replied" ||',
|
|
' event.type === "question.rejected";',
|
|
' if (isResolutionEvent) {',
|
|
' // Why: the stored owner already identifies the root, so replies clear',
|
|
' // immediately even when OpenCode session lookup is slow or unavailable.',
|
|
' const rootSessionID = clearAttentionForResolution(event, sessionID, factoryID);',
|
|
' if (rootSessionID) await publishOwnershipChange(factoryID, rootSessionID);',
|
|
' return;',
|
|
' }',
|
|
' const isAttentionEvent =',
|
|
' event.type === "permission.asked" ||',
|
|
' event.type === "question.asked";',
|
|
" // Why: attention without OpenCode's required sessionID cannot be",
|
|
' // correlated to a later reply/Idle, so it must not become UI authority.',
|
|
' if (isAttentionEvent && !sessionID) return;',
|
|
' const canFailOpen =',
|
|
' statusType === "busy" || statusType === "retry" || isAttentionEvent;',
|
|
' const rootSessionID = sessionID ? await resolveRootSessionID(client, sessionID) : null;',
|
|
' const childState = rootSessionID === null ? null : rootSessionID !== sessionID;',
|
|
' const isIdleEvent = event.type === "session.idle" || statusType === "idle";',
|
|
' const resolvedProvisionalBusy =',
|
|
' childState === false || (childState === true && isIdleEvent)',
|
|
' ? clearProvisionalBusy(sessionID, factoryID)',
|
|
' : false;',
|
|
' if (resolvedProvisionalBusy && childState === false) {',
|
|
' busyRootOwnerBySessionID.delete(sessionID);',
|
|
' busyRootOwnerBySessionID.set(sessionID, factoryID);',
|
|
' }',
|
|
' // Why: child work rolls up to the pane; ignore its normal lifecycle noise,',
|
|
' // but preserve blockers that still require the pane owner to respond.',
|
|
' if (childState === true && !isAttentionEvent) {',
|
|
' let attentionRootSessionID = null;',
|
|
' let childBusyChanged = false;',
|
|
' if (isIdleEvent) {',
|
|
' attentionRootSessionID = clearAttentionForSession(sessionID, factoryID);',
|
|
' childBusyChanged = clearBusyChild(sessionID, factoryID);',
|
|
' } else if (statusType === "busy" || statusType === "retry") {',
|
|
' childBusyChanged = rememberBusyChild(sessionID, rootSessionID, factoryID);',
|
|
' }',
|
|
' if (resolvedProvisionalBusy || attentionRootSessionID || childBusyChanged) {',
|
|
' await publishOwnershipChange(factoryID, attentionRootSessionID || rootSessionID);',
|
|
' }',
|
|
' return;',
|
|
' }',
|
|
' if (childState === null && !canFailOpen) {',
|
|
' if (isIdleEvent) {',
|
|
' // Why: recorded ownership can safely retire a blocker during an SDK',
|
|
' // outage without granting unknown child Idle authority over root state.',
|
|
' const attentionRootSessionID = clearAttentionForSession(sessionID, factoryID);',
|
|
' const clearedProvisionalBusy = clearProvisionalBusy(sessionID, factoryID);',
|
|
' const clearedKnownBusyRoot = clearKnownBusyRoot(sessionID, factoryID);',
|
|
' // Why: exact-session cleanup, so a child whose finishing Idle lands',
|
|
' // during an SDK outage cannot pin the pane Busy forever.',
|
|
' const clearedBusyChild = clearBusyChild(sessionID, factoryID);',
|
|
' if (',
|
|
' attentionRootSessionID ||',
|
|
' clearedProvisionalBusy ||',
|
|
' clearedKnownBusyRoot ||',
|
|
' clearedBusyChild',
|
|
' ) {',
|
|
' await publishOwnershipChange(factoryID, attentionRootSessionID || sessionID);',
|
|
' }',
|
|
' }',
|
|
' return;',
|
|
' }',
|
|
' if (childState === null && (statusType === "busy" || statusType === "retry")) {',
|
|
' // Unknown lineage may be child work, so keep exact provisional ownership',
|
|
' // only until matching Idle/disposal; other blockers still take priority.',
|
|
' clearAttentionForSession(sessionID, factoryID);',
|
|
' rememberProvisionalBusy(sessionID, factoryID);',
|
|
' await publishAggregateStatus(factoryID, sessionID);',
|
|
' return;',
|
|
' }',
|
|
' if (event.type === "permission.asked" || event.type === "question.asked") {',
|
|
' stateArrivalRevision += 1;',
|
|
' // Why: attention must share the lifecycle FIFO and retry target so a',
|
|
' // delayed Busy post cannot overwrite a newer human blocker.',
|
|
' const hookEventName =',
|
|
' event.type === "permission.asked" ? "PermissionRequest" : "AskUserQuestion";',
|
|
' // Why: show the blocker on the root turn while retaining its real child',
|
|
' // owner for exact reply, tool-completion, and disposal cleanup.',
|
|
' const properties = { ...(event.properties || {}), sessionID: rootSessionID || sessionID };',
|
|
' const requestID = properties.id || sessionID || "";',
|
|
' const key = attentionKey(factoryID, hookEventName, requestID, sessionID);',
|
|
' // Why: unresolved blockers are live UI authority and cannot be evicted;',
|
|
' // reply, exact Idle, tool completion, or factory disposal retires them.',
|
|
' pendingAttentionByKey.set(key, {',
|
|
' hookEventName,',
|
|
' properties,',
|
|
' factoryID,',
|
|
' sourceSessionID: sessionID,',
|
|
' });',
|
|
' await publishAggregateStatus(factoryID, rootSessionID || sessionID);',
|
|
' return;',
|
|
' }',
|
|
' if (isIdleEvent) {',
|
|
' stateArrivalRevision += 1;',
|
|
' const idleKey = "idle:" + (sessionID || "");',
|
|
' // Why: current OpenCode emits canonical idle followed by deprecated',
|
|
' // session.idle; a failed canonical post should keep its backoff.',
|
|
' if (',
|
|
' event.type === "session.idle" &&',
|
|
' desiredStatusKey === idleKey &&',
|
|
' statusDeliveryDirty &&',
|
|
' statusRetryTimer',
|
|
' ) return;',
|
|
' // Why: flush the coalesced final reply snapshot before the idle',
|
|
' // transition so the done-state preview shows the completed message.',
|
|
' await flushPendingAssistantPart(true);',
|
|
' clearAttentionForSession(sessionID, factoryID);',
|
|
' if (busyRootOwnerBySessionID.get(sessionID) === factoryID) {',
|
|
' busyRootOwnerBySessionID.delete(sessionID);',
|
|
' }',
|
|
' await publishAggregateStatus(factoryID, sessionID);',
|
|
' return;',
|
|
' }',
|
|
' // Why: recoverable compaction failures emit session.error and continue;',
|
|
' // canonical session.status is the authority for actual completion.',
|
|
' if (event.type === "session.error") return;',
|
|
' if (statusType === "busy" || statusType === "retry") {',
|
|
' clearAttentionForSession(sessionID, factoryID);',
|
|
' busyRootOwnerBySessionID.delete(sessionID);',
|
|
' busyRootOwnerBySessionID.set(sessionID, factoryID);',
|
|
' await publishAggregateStatus(factoryID, sessionID);',
|
|
' }',
|
|
'}',
|
|
''
|
|
]
|
|
}
|