@north-light/cli 0.3.534 → 0.3.536
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.
- package/dist/api/client.d.ts +3 -0
- package/dist/api/client.js +3 -3
- package/dist/api/contract.d.ts +1 -1
- package/dist/api/contract.js +1 -1
- package/dist/api/dto/nodes.d.ts +2 -0
- package/dist/api/dto/recovery.d.ts +2 -1
- package/dist/builtin-memory/crouter-sdk/release-notes.md +1 -1
- package/dist/clients/attach/viewer.js +406 -406
- package/dist/commands/api-client.d.ts +1 -0
- package/dist/commands/api-client.js +3 -3
- package/dist/commands/node/inspect.js +9 -9
- package/dist/core/bash-jobs.d.ts +4 -0
- package/dist/core/bash-jobs.js +8 -8
- package/dist/core/canvas/canvas.d.ts +26 -2
- package/dist/core/canvas/canvas.js +19 -19
- package/dist/core/canvas/crons.d.ts +4 -2
- package/dist/core/canvas/crons.js +1 -1
- package/dist/core/canvas/paths.d.ts +5 -0
- package/dist/core/canvas/paths.js +1 -1
- package/dist/core/canvas/recovery.d.ts +7 -0
- package/dist/core/canvas/recovery.js +2 -2
- package/dist/core/canvas/remote-canvas-source.d.ts +1 -0
- package/dist/core/canvas/remote-canvas-source.js +1 -1
- package/dist/core/canvas/source.d.ts +4 -0
- package/dist/core/canvas/source.js +1 -1
- package/dist/core/canvas/tree.d.ts +2 -0
- package/dist/core/canvas/tree.js +12 -5
- package/dist/core/feed/feed.js +3 -3
- package/dist/core/feed/messages.d.ts +6 -0
- package/dist/core/feed/messages.js +5 -5
- package/dist/core/graph/kinds/node.js +5 -5
- package/dist/core/graph/watches.d.ts +0 -2
- package/dist/core/graph/watches.js +7 -12
- package/dist/core/providers/mint.d.ts +2 -0
- package/dist/core/providers/mint.js +1 -1
- package/dist/core/runtime/broker/cli-relay.js +1 -1
- package/dist/core/runtime/broker/fault-retry.d.ts +18 -0
- package/dist/core/runtime/broker/fault-retry.js +1 -1
- package/dist/core/runtime/broker/run-turn-end.d.ts +3 -2
- package/dist/core/runtime/broker/run-turn-end.js +1 -1
- package/dist/core/runtime/launch.js +2 -2
- package/dist/core/runtime/lifecycle.d.ts +4 -0
- package/dist/core/runtime/lifecycle.js +2 -2
- package/dist/core/runtime/nodes.js +1 -1
- package/dist/core/runtime/revive.d.ts +1 -1
- package/dist/core/runtime/revive.js +1 -1
- package/dist/core/worktree-mutation-async.d.ts +10 -0
- package/dist/core/worktree-mutation-async.js +5 -5
- package/dist/core/worktree-sweep.js +6 -6
- package/dist/core/worktree.d.ts +1 -0
- package/dist/core/worktree.js +1 -1
- package/dist/daemon/api/handlers/broker-recovery.js +1 -1
- package/dist/daemon/api/handlers/messages.js +2 -2
- package/dist/daemon/api/handlers/nodes.js +1 -1
- package/dist/daemon/api/handlers/objects.js +3 -3
- package/dist/daemon/messaging/node-message.js +1 -1
- package/dist/daemon/peer/forward.js +1 -1
- package/dist/daemon/reconcilers/bash-deadline.d.ts +6 -0
- package/dist/daemon/reconcilers/bash-deadline.js +3 -2
- package/dist/daemon/runs/observer.d.ts +2 -0
- package/dist/daemon/runs/observer.js +2 -2
- package/docs/sdk/release-notes.md +1 -1
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +12 -12
- package/dist/core/canvas/nav-model.d.ts +0 -44
- package/dist/core/canvas/nav-model.js +0 -1
|
@@ -83,7 +83,16 @@ export declare class FaultRetry {
|
|
|
83
83
|
* this generation, since the replacement owns recovery from then on, and when
|
|
84
84
|
* another turn starts, since a stale fault would re-drive from the old anchor. */
|
|
85
85
|
publishSettledProviderFault(generation: SubscriptionGeneration): Promise<void>;
|
|
86
|
+
/** Run a daemon write until the daemon answers: a transport failure waits and
|
|
87
|
+
* tries again, and `assertCurrent` throws once the write has gone stale. */
|
|
88
|
+
private retryWhileDaemonUnreachable;
|
|
86
89
|
private recordStagedOutcome;
|
|
90
|
+
/** Where the next retry of this episode rewinds to. The episode keeps its
|
|
91
|
+
* original anchor while every attempt since failed without doing anything, so
|
|
92
|
+
* empty failures do not pile up on the branch. Once an attempt carries
|
|
93
|
+
* content or tool results past that anchor, rewinding would hide work whose
|
|
94
|
+
* effects cannot be undone, so the anchor advances to the current leaf. */
|
|
95
|
+
private retryAnchor;
|
|
87
96
|
/** Startup re-drive reads the durable episode surviving from the process this
|
|
88
97
|
* replaces. Revive deliberately clears the ordinary fault marker before this
|
|
89
98
|
* boundary, so the episode is the recovery authority. A `pending` episode
|
|
@@ -129,5 +138,14 @@ export declare class FaultRetry {
|
|
|
129
138
|
/** One retry attempt. Every outcome but a re-armed timer or a prompted recovery turn means no recovery will answer the settled turn. */
|
|
130
139
|
private runFaultRetry;
|
|
131
140
|
private attemptFaultRetry;
|
|
141
|
+
/** The admitted recovery prompt rejected, so unless a turn started nothing
|
|
142
|
+
* will settle the durable `admitted` episode. While this generation, session
|
|
143
|
+
* and episode still own the attempt and no turn has started since, a
|
|
144
|
+
* transient rejection reopens the episode for the existing retry policy with
|
|
145
|
+
* the attempt it consumed and the original anchor; a rejection that would
|
|
146
|
+
* fail the same way again (missing credentials, a refused request) ends the
|
|
147
|
+
* episode with a fatal fault, and so does a run node's, whose turn the app
|
|
148
|
+
* has already seen end. */
|
|
149
|
+
private settleRejectedRecoveryPrompt;
|
|
132
150
|
}
|
|
133
151
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var ae=Object.defineProperty;var h=(r,e)=>ae(r,"name",{value:e,configurable:!0});import{envModelIntent as N,envNodeCwd as G,envProfileId as U}from"../../../shared/env.js";import{isContextOverflow as Y}from"@earendil-works/pi-ai";import{classify as B}from"../../fault-classifier.js";import{isDaemonTransportApiError as de}from"../../../api/errors.js";import{isModelNotFoundError as le,isProviderUnconfiguredError as ce}from"../fault.js";import{brokerRecovery as ue,mutateBrokerFault as W,mutateBrokerProviderRetry as _}from"./daemon-ops.js";import{extractCoolingDeadline as pe}from"../managed-provider-cooling.js";import{extractProviderFailureDetails as fe}from"../../provider-failure.js";import{expandModelCandidates as q,modelRequestFromConfig as V,probeRouteAvailability as he}from"../../model-routes.js";import{operationIdContext as j}from"../../events/operation-id.js";import{emitEvent as w}from"../../events/emit.js";import{CONNECTION_FAULT_RECOVERY_BODY as me,PROVIDER_FAULT_RECOVERY_BODY as ge,RUN_CONNECTION_FAULT_RETRY_BODY as ye,formatCard as X,formatModelFallbackRecovery as ve}from"../../../shared/generated-context.js";import{markFatalOnExhaust as J,nextFaultRetry as z,policyFor as H}from"../fault-recovery.js";import{parseModelSpec as K}from"../model-selection.js";import{endedByAbort as Q}from"../session-cycles.js";import{resolveProviderCandidates as Fe}from"../../subscription-state.js";import{announceMessageOrigin as Z}from"../stamp/channel.js";import{LLM_REFUSAL_DIAGNOSTIC as we,refusalRetryAt as ke}from"./model-adapter.js";import{isSafetyRefusal as ee}from"../../llm/refusal.js";const be=2147483647,te=3e4;function _e(r,e,t){const n=Date.parse(r);return!Number.isFinite(n)||t+e-n>te}h(_e,"runTurnRetryExpired");class ne extends Error{static{h(this,"StaleOutcome")}}const Ee=1e3,Ie=3e4;function Re(r,e){return r!=="connection"?ge:e?ye:me}h(Re,"retryCardBody");function E(r){return{link:r.link,op:r.op,kind:r.kind,retry:r.retry,message:r.message,since:r.since,operation_id:r.operation_id,...r.anchor_entry_id===void 0?{}:{anchorEntryId:r.anchor_entry_id},...r.provider_error===void 0?{}:{providerError:r.provider_error}}}h(E,"policyFault");function x(r){return{link:r.link,op:r.op,kind:r.kind,retry:r.retry,message:r.message,...r.operation_id===void 0?{}:{operation_id:r.operation_id},...r.since===void 0?{}:{since:r.since},...r.anchorEntryId===void 0?{}:{anchor_entry_id:r.anchorEntryId},...r.providerError===void 0?{}:{provider_error:r.providerError}}}h(x,"faultInput");function ie(r,e){const t=h(n=>`${n.providerId}/${n.modelId}`===e.modelSpec,"onLiveModel");return(r.find(n=>t(n)&&n.thinkingLevel===e.thinkingLevel)??r.find(t))?.rank}h(ie,"liveRouteRank");function re(r){const e=B("pi\u2192provider",r),t=r.diagnostics?.some(n=>n.type===we)??!1;return{classified:e,disposition:t?"fatal":e.disposition}}h(re,"settledProviderFault");function He(r,e){if(r.stopReason!=="error"||Q(r)||ee(r)||Y(r,e))return!1;const{classified:t,disposition:n}=re(r);return n==="auto"&&(t.kind==="connection"||t.kind==="rate-limit"||t.kind==="overloaded")}h(He,"willDaemonRetry");const Se=/does not support assistant message prefill|conversation must end with a user message/i;function Ae(r,e){return e&&Se.test(r)}h(Ae,"isPrefillRejectionDuringEpisode");class Ke{static{h(this,"FaultRetry")}deps;timer=null;failedRetryRoutes=new Set;notFoundModels=new Set;providerFallbackInFlight=null;notFoundFallbackInFlight=null;stagedCapacityFailure=null;agentStarts=0;constructor(e){this.deps=e}clearTimer(){this.timer!==null&&(clearTimeout(this.timer),this.timer=null)}reset(){this.clearTimer(),this.deps.turnEnds?.abandon(),this.providerFallbackInFlight=null,this.notFoundFallbackInFlight=null,this.stagedCapacityFailure=null}abandonTurnEnd(){this.deps.turnEnds?.abandon()}state(){return ue(this.deps.nodeId)}async recordFault(e){return(await W(this.deps.nodeId,{op:"record",input:e})).fault}async clearFault(e,t=!1){await W(this.deps.nodeId,{op:"clear",...e===void 0?{}:{link:e},...t?{preserve_episode:!0}:{}})}sessionId(e){const t=e.sessionManager.getSessionId?.();return typeof t=="string"&&t!==""?t:null}async clearEpisode(e){const t=this.sessionId(e);t!==null&&await _(this.deps.nodeId,{op:"clear",session_id:t})}onEvent(e){e.type==="agent_start"&&this.agentStarts++,this.maybeSwitchProviderForRetry(e),this.maybeFallbackOnUnusableModel(e)}isActiveAutoFault(e){return e===null||e.retry.disposition!=="auto"||e.retry.by!=="daemon"?!1:e.kind==="connection"||e.kind==="rate-limit"||e.kind==="overloaded"}async publishSettledProviderFault(e){const t={agentEnd:e.stagedProviderAgentEnd,watchdogAbort:e.stagedWatchdogAbort,refreshAbort:e.stagedRefreshAbort,overflowFailure:e.stagedOverflowFailure,capacityFailure:this.stagedCapacityFailure?.generation===e?this.stagedCapacityFailure:null};this.stagedCapacityFailure=null,e.stagedProviderAgentEnd=null,e.stagedWatchdogAbort=!1,e.stagedRefreshAbort=!1,e.stagedOverflowFailure=null;const n=this.agentStarts,i=h(()=>{if(this.deps.installedGeneration()!==e||this.agentStarts!==n)throw new ne},"assertCurrent");let a=Ee;try{for(;;){try{await this.recordStagedOutcome(e,t,i);return}catch(o){if(!de(o))throw o;w({level:"warn",event:"broker.runtime.diagnostic",fields:{message:`settled turn fault write waiting for the daemon: ${o.message}`}})}await new Promise(o=>setTimeout(o,a)),a=Math.min(a*2,Ie),i()}}catch(o){if(!(o instanceof ne))throw o}}async recordStagedOutcome(e,t,n){const{agentEnd:i,watchdogAbort:a,refreshAbort:o,overflowFailure:d,capacityFailure:p}=t,s=e.session,l=h(async F=>{n(),await this.clearEpisode(F)},"clearEpisode"),y=h(async F=>{n(),await this.recordFault(F)},"recordFault"),u=h(async(F,oe)=>{n(),await this.recordPendingProviderFault(F,oe)},"recordPendingProviderFault"),v=await this.state(),m=v.fault,f=v.episode,I=this.sessionId(s),k=this.isActiveAutoFault(m)&&m.link==="pi\u2192provider"?m:f?.state==="admitted"&&f.session_id===I&&this.isActiveAutoFault(f.fault)?f.fault:null,g=k===null?null:{since:k.since,anchorEntryId:k.anchor_entry_id,attempt:k.retry.attempt},c=[...Array.isArray(i?.messages)?i.messages:[]].reverse().find(F=>typeof F=="object"&&F!==null&&F.role==="assistant");if(p!==null||d!==null){await l(s),await y({link:"pi\u2192provider",op:"context overflow recovery",kind:"context-overflow",retry:{disposition:"fatal"},message:p?.errorMessage??d.errorMessage,anchor_entry_id:s.sessionManager.getLeafId?.()??void 0});return}if(a&&c?.stopReason==="aborted"){await u(s,{link:"pi\u2192provider",op:"generation turn",kind:"connection",retry:{disposition:"auto",by:"daemon",...g?.attempt!==void 0?{attempt:g.attempt}:{}},message:typeof c.errorMessage=="string"?c.errorMessage:"engine error (no errorMessage recorded)",...g!==null?{since:g.since}:{},anchor_entry_id:g?.anchorEntryId??s.sessionManager.getLeafId?.()??void 0});return}if(c?.stopReason!=="error"){c!==void 0&&await l(s);return}if(o){await l(s);return}if(ee(c)){await l(s);return}const S=typeof c.errorMessage=="string"?c.errorMessage:"engine error (no errorMessage recorded)";if(Q({stopReason:"error",errorMessage:S})){await l(s);return}const b=Y(c,s.model?.contextWindow),A=re(c),T=g!==null&&A.disposition!=="auto"&&Ae(S,!0),se=T?{kind:"connection",disposition:"auto"}:A.classified,R=T?"auto":A.disposition,C=pe(c),O=ke(c),P=fe(c),$=typeof c.provider=="string"&&c.provider!==""?c.provider:s.model?.provider,L=typeof c.model=="string"&&c.model!==""?c.model:s.model?.id,D=P===void 0||$===void 0||L===void 0?void 0:{provider:$,model:L,...P};b&&await l(s);const M={link:"pi\u2192provider",op:"generation turn",kind:b?"context-overflow":se.kind,retry:b?{disposition:"fatal"}:R==="auto"?{disposition:"auto",by:"daemon",...C!==void 0?{nextAt:new Date(C).toISOString()}:{},...g?.attempt!==void 0?{attempt:g.attempt}:{}}:{disposition:R,...O!==void 0?{nextAt:new Date(O).toISOString()}:{}},message:S,...!b&&R==="auto"&&g!==null?{since:g.since}:{},anchor_entry_id:!b&&R==="auto"&&g?.anchorEntryId!==void 0?g.anchorEntryId:s.sessionManager.getLeafId?.()??void 0,...D===void 0?{}:{provider_error:D}};M.retry.disposition==="auto"?await u(s,M):(await l(s),await y(M))}async recoverOnStartup(){const e=this.deps.installedGeneration();if(e===void 0)return"none";const t=(await this.state()).episode,n=this.sessionId(e.session);if(this.deps.cfg.newCycle===!0)return t!==null&&n!==null&&t.session_id===n&&await _(this.deps.nodeId,{op:"invalidate",session_id:n}),"none";if(t===null||n===null||t.session_id!==n)return"none";if(t.state==="admitted")if(t.fault.anchor_entry_id!==void 0&&!this.hasProgressPastAnchor(e.session,t.fault.anchor_entry_id)){if(!(await _(this.deps.nodeId,{op:"reopen",session_id:n})).applied)return"none"}else return await this.clearEpisode(e.session),"none";else if(t.state!=="pending")return"none";return await this.recordFault(x(E(t.fault))),await this.schedule(),"retry-armed"}hasProgressPastAnchor(e,t){const n=e.sessionManager.getBranch(),i=n.findIndex(o=>o.id===t);return(i===-1?n:n.slice(i+1)).some(o=>{if(o.type!=="message")return!1;const{message:d}=o;return d.role==="toolResult"?!0:d.role==="assistant"&&Array.isArray(d.content)&&d.content.length>0})}async schedule(){const e=this.deps.installedGeneration(),t=this.deps.currentSession();if(t.isStreaming||this.timer!==null)return;if(e===void 0){this.abandonTurnEnd();return}const n=await this.state(),i=n.fault;if(!this.isActiveAutoFault(i)||this.sessionId(t)!==null&&!this.pendingEpisodeMatches(t,i,n)){this.clearTimer(),this.abandonTurnEnd();return}const a=Date.now(),o=this.faultRetryDelayMs(i,a);if(o!==null&&this.deps.turnEnds?.holding()===!0&&_e(i.since,o,a)){await this.endRunTurnOnFault(t,i,o);return}o!==null?this.clearAndArmFaultRetry(o,e):this.abandonTurnEnd()}async endRunTurnOnFault(e,t,n){this.clearTimer(),this.abandonTurnEnd(),w({level:"info",event:"broker.runtime.diagnostic",fields:{message:`run turn completed on provider fault: ${t.kind} retry in ${n}ms is past the ${te}ms turn window (since ${t.since}, attempt ${t.retry.attempt??0})`}}),await this.clearEpisode(e),await this.clearFault("pi\u2192provider")}async rearmIfStranded(){if(this.timer!==null||this.deps.installedGeneration()===void 0)return;const e=this.deps.currentSession();if(e.isStreaming)return;const t=await this.state();!this.isActiveAutoFault(t.fault)||!this.pendingEpisodeMatches(e,t.fault,t)||await this.schedule()}maybeSwitchProviderForRetry(e){if(e.type!=="auto_retry_start")return;const t=B("pi\u2192provider",e.errorMessage??"").kind;if(t!=="rate-limit"&&t!=="connection"||this.providerFallbackInFlight!==null||this.deps.cfg.modelExact===!0)return;const n=this.deps.installedGeneration();if(n===void 0)return;const i=this.deps.currentSession(),a=this.deps.currentServices(),o=this.deps.formatModelSpec(i.model,i.thinkingLevel);if(o===null)return;const d=V(this.deps.cfg.model,N(),!1);if(d===void 0)return;const p=q(d,G()??this.deps.cfg.cwd,U()),s=K(o),l=ie(p,s),y=l===void 0?{route:void 0,capacityExhausted:!1}:this.nextEligibleFallbackRoute(p.slice(l+1),this.deps.registryOf(a),i.getContextUsage()?.tokens,m=>!this.failedRetryRoutes.has(m.routeId));if(y.route===void 0){y.capacityExhausted&&(this.stageCapacityFailure(n,i),queueMicrotask(()=>{this.deps.installedGeneration()===n&&this.deps.currentSession()===i&&i.abortRetry()}));return}const u=y.route,v=(async()=>{const m=this.deps.registryOf(a).find(u.providerId,u.modelId);if(!m||(await i.setModel(m),this.deps.installedGeneration()!==n||this.deps.currentSession()!==i))return;const f=`${u.providerId}/${u.modelId}`===s.modelSpec?s.thinkingLevel:u.thinkingLevel;i.setThinkingLevel(f??"off"),this.failedRetryRoutes.add(u.routeId),await this.deps.broadcastModelChanged(!1)})().catch(m=>{w({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`provider route fallback failed: ${m instanceof Error?m.message:String(m)}`}})}).finally(()=>{this.deps.installedGeneration()===n&&(this.providerFallbackInFlight=null)});this.providerFallbackInFlight=v}maybeFallbackOnUnusableModel(e){if(e.type!=="message_end"||e.message.role!=="assistant"||e.message.stopReason!=="error")return;const t=ce(e.message.errorMessage??"");if(!t&&!le(e.message.errorMessage??"")||this.deps.cfg.modelExact===!0||this.notFoundFallbackInFlight!==null)return;const n=this.deps.installedGeneration();if(n===void 0)return;const i=this.deps.currentSession(),a=this.deps.currentServices(),o=this.deps.formatModelSpec(i.model,i.thinkingLevel);if(o===null)return;const d=K(o);this.notFoundModels.add(d.modelSpec);const p=V(this.deps.cfg.model,N(),!1),s=p?q(p,G()??this.deps.cfg.cwd,U()):[],l=ie(s,d),y=l===void 0?{route:void 0,capacityExhausted:!1}:this.nextEligibleFallbackRoute(s.slice(l+1),this.deps.registryOf(a),i.getContextUsage()?.tokens,f=>!this.notFoundModels.has(`${f.providerId}/${f.modelId}`));if(y.route===void 0){y.capacityExhausted&&this.stageCapacityFailure(n,i);return}const u=y.route,v=`${u.providerId}/${u.modelId}${u.thinkingLevel?`:${u.thinkingLevel}`:""}`,m=(async()=>{const f=this.deps.registryOf(a).find(u.providerId,u.modelId);if(f===void 0){w({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`${t?"unconfigured-provider":"not_found"} fallback skipped: ${v} is not registered`}});return}if(await i.setModel(f),this.deps.installedGeneration()!==n||this.deps.currentSession()!==i)return;i.setThinkingLevel(u.thinkingLevel??"off"),await this.deps.broadcastModelChanged();const I=this.deps.formatModelSpec(i.model,i.thinkingLevel)??v;if(w({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`${t?"unconfigured-provider":"not_found"} fallback: ${o} unavailable \u2192 ${I}; re-driving turn`}}),await i.agent.waitForIdle(),this.deps.installedGeneration()!==n||this.deps.currentSession()!==i)return;t&&await this.clearFault("pi\u2192provider");const k=X("recovery",{},ve(o,I,t?"credential":"not-found"));Z(k,{type:"runtime",kind:"recovery"}),await this.deps.turnAdmission.admit(g=>j.fresh(()=>i.prompt(k,{preflightResult:g})))})().catch(f=>{w({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`${t?"unconfigured-provider":"not_found"} fallback failed: ${f instanceof Error?f.message:String(f)}`}})}).finally(()=>{this.deps.installedGeneration()===n&&(this.notFoundFallbackInFlight=null)});this.notFoundFallbackInFlight=m}nextEligibleFallbackRoute(e,t,n,i){let a=0,o=0;for(const d of e){if(!i(d)||Fe([{...d,...he(d,t),automaticFallback:!0}]).candidates.length===0)continue;const p=t.find(d.providerId,d.modelId);if(p!==void 0){if(a++,this.targetCannotFitContext(p.contextWindow,n)){o++;continue}return{route:d,capacityExhausted:!1}}}return{route:void 0,capacityExhausted:a>0&&a===o}}targetCannotFitContext(e,t){return Number.isFinite(e)&&Number.isFinite(t)&&e<t}stageCapacityFailure(e,t){const n=t.getContextUsage()?.tokens;this.stagedCapacityFailure={generation:e,errorMessage:Number.isFinite(n)?`All available fallback models have context windows smaller than the current conversation (${n} tokens).`:"All available fallback models have context windows smaller than the current conversation."}}async recordPendingProviderFault(e,t){const n=this.sessionId(e);if(n===null){await this.recordFault(t);return}await _(this.deps.nodeId,{op:"record-pending",session_id:n,input:t})}pendingEpisodeMatches(e,t,n){const i=this.sessionId(e),a=n.episode;return i!==null&&a!==null&&a.state==="pending"&&a.session_id===i&&a.fault.fault_id===t.fault_id}faultRetryDelayMs(e,t){if(e.retry.nextAt!==void 0){const n=Date.parse(e.retry.nextAt)-t;return Number.isFinite(n)?Math.max(0,n):null}return z(E(e),t)?.delayMs??null}clearAndArmFaultRetry(e,t){if(this.deps.installedGeneration()!==t)return;this.clearTimer();const n=Math.min(Math.max(0,e),be),i=Math.max(0,e-n);this.timer=setTimeout(()=>{if(this.timer=null,this.deps.installedGeneration()===t){if(i>0){this.clearAndArmFaultRetry(i,t);return}this.runFaultRetry().catch(a=>{this.abandonTurnEnd(),w({level:"warn",event:"broker.runtime.diagnostic",fields:{message:`fault retry fire failed: ${a instanceof Error?a.message:String(a)}`}})})}},n),typeof this.timer.unref=="function"&&this.timer.unref()}async runFaultRetry(){await this.attemptFaultRetry()===void 0&&this.abandonTurnEnd()}async attemptFaultRetry(){const e=this.deps.installedGeneration();if(e===void 0)return;const t=e.session;if(t.isStreaming)return;const n=(await this.state()).fault;if(!this.isActiveAutoFault(n))return;let i=n;if(n.kind==="connection"&&this.deps.turnEnds===void 0){const s=await this.deps.retryProbe();if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t||t.isStreaming)return;const l=(await this.state()).fault;if(!this.isActiveAutoFault(l)||l.since!==n.since)return;if(!s)return this.clearAndArmFaultRetry(H(l.kind).baseDelayMs,e),"rearmed";i=l}const a=this.deps.turnEnds!==void 0,o=i.retry.attempt??0,d=H(i.kind);if(o>=d.max){await this.recordFault(x(J(E(i)))),await this.clearEpisode(t);return}const p=z(E(i),Date.now());if(p===null){await this.recordFault(x(J(E(i)))),await this.clearEpisode(t);return}if(i.anchor_entry_id!==void 0)try{if((t.sessionManager.getLeafId?.()??null)!==i.anchor_entry_id){const s=await t.navigateTree(i.anchor_entry_id);if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t)return;s.cancelled||this.deps.reWelcomeAll()}}catch(s){w({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`fault retry rewind skipped: ${s instanceof Error?s.message:String(s)}`}})}if(!(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t))return this.deps.turnAdmission.admit(async s=>{const l=(await this.state()).fault;if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t||t.isStreaming||!this.isActiveAutoFault(l)||l.since!==i.since){s();return}const y=this.sessionId(t);if(y===null){s();return}if(!(await _(this.deps.nodeId,{op:"admit",session_id:y,fault_id:l.fault_id,retry:p.fault.retry})).applied){s();return}const v=X("recovery",a?{attempt:p.attempt}:{attempt:p.attempt,max:p.max},Re(i.kind,a));Z(v,{type:"runtime",kind:"recovery"}),this.deps.turnEnds?.beginRecovery();try{await j.fresh(()=>t.prompt(v,{preflightResult:s}))}finally{this.deps.turnEnds?.recoveryReturned()}return"prompted"})}}export{Ke as FaultRetry,te as RUN_TURN_RETRY_WINDOW_MS,Ae as isPrefillRejectionDuringEpisode,Re as retryCardBody,_e as runTurnRetryExpired,re as settledProviderFault,He as willDaemonRetry};
|
|
1
|
+
var ue=Object.defineProperty;var m=(i,e)=>ue(i,"name",{value:e,configurable:!0});import{envModelIntent as j,envNodeCwd as Y,envProfileId as q}from"../../../shared/env.js";import{isContextOverflow as V}from"@earendil-works/pi-ai";import{classify as C}from"../../fault-classifier.js";import{isDaemonTransportApiError as X}from"../../../api/errors.js";import{isModelNotFoundError as fe,isProviderUnconfiguredError as pe}from"../fault.js";import{brokerRecovery as he,mutateBrokerFault as J,mutateBrokerProviderRetry as k}from"./daemon-ops.js";import{extractCoolingDeadline as me}from"../managed-provider-cooling.js";import{extractProviderFailureDetails as ge}from"../../provider-failure.js";import{expandModelCandidates as z,modelRequestFromConfig as H,probeRouteAvailability as ye}from"../../model-routes.js";import{operationIdContext as K}from"../../events/operation-id.js";import{emitEvent as F}from"../../events/emit.js";import{CONNECTION_FAULT_RECOVERY_BODY as ve,PROVIDER_FAULT_RECOVERY_BODY as we,RUN_CONNECTION_FAULT_RETRY_BODY as Fe,formatCard as Q,formatModelFallbackRecovery as be}from"../../../shared/generated-context.js";import{markFatalOnExhaust as O,nextFaultRetry as P,policyFor as Z}from"../fault-recovery.js";import{parseModelSpec as ee}from"../model-selection.js";import{endedByAbort as te}from"../session-cycles.js";import{resolveProviderCandidates as ke}from"../../subscription-state.js";import{announceMessageOrigin as ne}from"../stamp/channel.js";import{LLM_REFUSAL_DIAGNOSTIC as _e,refusalRetryAt as Ie}from"./model-adapter.js";import{isSafetyRefusal as re}from"../../llm/refusal.js";const Ee=2147483647,ie=3e4;function Re(i,e,t){const n=Date.parse(i);return!Number.isFinite(n)||t+e-n>ie}m(Re,"runTurnRetryExpired");class _ extends Error{static{m(this,"StaleOutcome")}}const Se=1e3,Ae=3e4;function Me(i,e){return i!=="connection"?we:e?Fe:ve}m(Me,"retryCardBody");function I(i){return{link:i.link,op:i.op,kind:i.kind,retry:i.retry,message:i.message,since:i.since,operation_id:i.operation_id,...i.anchor_entry_id===void 0?{}:{anchorEntryId:i.anchor_entry_id},...i.provider_error===void 0?{}:{providerError:i.provider_error}}}m(I,"policyFault");function se(i){return i instanceof Error?i.message:String(i)}m(se,"errorText");function oe(i){return X(i)||C("pi\u2192provider",i).disposition==="auto"}m(oe,"isTransientRejection");function A(i){return{link:i.link,op:i.op,kind:i.kind,retry:i.retry,message:i.message,...i.operation_id===void 0?{}:{operation_id:i.operation_id},...i.since===void 0?{}:{since:i.since},...i.anchorEntryId===void 0?{}:{anchor_entry_id:i.anchorEntryId},...i.providerError===void 0?{}:{provider_error:i.providerError}}}m(A,"faultInput");function ae(i,e){const t=m(n=>`${n.providerId}/${n.modelId}`===e.modelSpec,"onLiveModel");return(i.find(n=>t(n)&&n.thinkingLevel===e.thinkingLevel)??i.find(t))?.rank}m(ae,"liveRouteRank");function de(i){const e=C("pi\u2192provider",i),t=i.diagnostics?.some(n=>n.type===_e)??!1;return{classified:e,disposition:t?"fatal":e.disposition}}m(de,"settledProviderFault");function Qe(i,e){if(i.stopReason!=="error"||te(i)||re(i)||V(i,e))return!1;const{classified:t,disposition:n}=de(i);return n==="auto"&&(t.kind==="connection"||t.kind==="rate-limit"||t.kind==="overloaded")}m(Qe,"willDaemonRetry");const xe=/does not support assistant message prefill|conversation must end with a user message/i;function Te(i,e){return e&&xe.test(i)}m(Te,"isPrefillRejectionDuringEpisode");class Ze{static{m(this,"FaultRetry")}deps;timer=null;failedRetryRoutes=new Set;notFoundModels=new Set;providerFallbackInFlight=null;notFoundFallbackInFlight=null;stagedCapacityFailure=null;agentStarts=0;constructor(e){this.deps=e}clearTimer(){this.timer!==null&&(clearTimeout(this.timer),this.timer=null)}reset(){this.clearTimer(),this.deps.turnEnds?.abandon(),this.providerFallbackInFlight=null,this.notFoundFallbackInFlight=null,this.stagedCapacityFailure=null}abandonTurnEnd(){this.deps.turnEnds?.abandon()}state(){return he(this.deps.nodeId)}async recordFault(e){return(await J(this.deps.nodeId,{op:"record",input:e})).fault}async clearFault(e,t=!1){await J(this.deps.nodeId,{op:"clear",...e===void 0?{}:{link:e},...t?{preserve_episode:!0}:{}})}sessionId(e){const t=e.sessionManager.getSessionId?.();return typeof t=="string"&&t!==""?t:null}async clearEpisode(e){const t=this.sessionId(e);t!==null&&await k(this.deps.nodeId,{op:"clear",session_id:t})}onEvent(e){e.type==="agent_start"&&this.agentStarts++,this.maybeSwitchProviderForRetry(e),this.maybeFallbackOnUnusableModel(e)}isActiveAutoFault(e){return e===null||e.retry.disposition!=="auto"||e.retry.by!=="daemon"?!1:e.kind==="connection"||e.kind==="rate-limit"||e.kind==="overloaded"}async publishSettledProviderFault(e){const t={agentEnd:e.stagedProviderAgentEnd,watchdogAbort:e.stagedWatchdogAbort,refreshAbort:e.stagedRefreshAbort,overflowFailure:e.stagedOverflowFailure,capacityFailure:this.stagedCapacityFailure?.generation===e?this.stagedCapacityFailure:null};this.stagedCapacityFailure=null,e.stagedProviderAgentEnd=null,e.stagedWatchdogAbort=!1,e.stagedRefreshAbort=!1,e.stagedOverflowFailure=null;const n=this.agentStarts,r=m(()=>{if(this.deps.installedGeneration()!==e||this.agentStarts!==n)throw new _},"assertCurrent");try{await this.retryWhileDaemonUnreachable("settled turn fault write",()=>this.recordStagedOutcome(e,t,r),r)}catch(s){if(!(s instanceof _))throw s}}async retryWhileDaemonUnreachable(e,t,n){let r=Se;for(;;){try{await t();return}catch(s){if(!X(s))throw s;F({level:"warn",event:"broker.runtime.diagnostic",fields:{message:`${e} waiting for the daemon: ${s.message}`}})}await new Promise(s=>setTimeout(s,r)),r=Math.min(r*2,Ae),n()}}async recordStagedOutcome(e,t,n){const{agentEnd:r,watchdogAbort:s,refreshAbort:a,overflowFailure:l,capacityFailure:c}=t,o=e.session,f=m(async b=>{n(),await this.clearEpisode(b)},"clearEpisode"),g=m(async b=>{n(),await this.recordFault(b)},"recordFault"),p=m(async(b,ce)=>{n(),await this.recordPendingProviderFault(b,ce)},"recordPendingProviderFault"),y=await this.state(),u=y.fault,h=y.episode,E=this.sessionId(o),w=this.isActiveAutoFault(u)&&u.link==="pi\u2192provider"?u:h?.state==="admitted"&&h.session_id===E&&this.isActiveAutoFault(h.fault)?h.fault:null,v=w===null?null:{since:w.since,anchorEntryId:w.anchor_entry_id,attempt:w.retry.attempt},d=[...Array.isArray(r?.messages)?r.messages:[]].reverse().find(b=>typeof b=="object"&&b!==null&&b.role==="assistant");if(c!==null||l!==null){await f(o),await g({link:"pi\u2192provider",op:"context overflow recovery",kind:"context-overflow",retry:{disposition:"fatal"},message:c?.errorMessage??l.errorMessage,anchor_entry_id:o.sessionManager.getLeafId?.()??void 0});return}if(s&&d?.stopReason==="aborted"){await p(o,{link:"pi\u2192provider",op:"generation turn",kind:"connection",retry:{disposition:"auto",by:"daemon",...v?.attempt!==void 0?{attempt:v.attempt}:{}},message:typeof d.errorMessage=="string"?d.errorMessage:"engine error (no errorMessage recorded)",...v!==null?{since:v.since}:{},anchor_entry_id:this.retryAnchor(o,v?.anchorEntryId)});return}if(d?.stopReason!=="error"){d!==void 0&&await f(o);return}if(a){await f(o);return}if(re(d)){await f(o);return}const M=typeof d.errorMessage=="string"?d.errorMessage:"engine error (no errorMessage recorded)";if(te({stopReason:"error",errorMessage:M})){await f(o);return}const R=V(d,o.model?.contextWindow),x=de(d),D=v!==null&&x.disposition!=="auto"&&Te(M,!0),le=D?{kind:"connection",disposition:"auto"}:x.classified,S=D?"auto":x.disposition,L=me(d),N=Ie(d),G=ge(d),U=typeof d.provider=="string"&&d.provider!==""?d.provider:o.model?.provider,W=typeof d.model=="string"&&d.model!==""?d.model:o.model?.id,B=G===void 0||U===void 0||W===void 0?void 0:{provider:U,model:W,...G};R&&await f(o);const T={link:"pi\u2192provider",op:"generation turn",kind:R?"context-overflow":le.kind,retry:R?{disposition:"fatal"}:S==="auto"?{disposition:"auto",by:"daemon",...L!==void 0?{nextAt:new Date(L).toISOString()}:{},...v?.attempt!==void 0?{attempt:v.attempt}:{}}:{disposition:S,...N!==void 0?{nextAt:new Date(N).toISOString()}:{}},message:M,...!R&&S==="auto"&&v!==null?{since:v.since}:{},anchor_entry_id:!R&&S==="auto"?this.retryAnchor(o,v?.anchorEntryId):o.sessionManager.getLeafId?.()??void 0,...B===void 0?{}:{provider_error:B}};T.retry.disposition==="auto"?await p(o,T):(await f(o),await g(T))}retryAnchor(e,t){const n=e.sessionManager.getLeafId?.()??void 0;return t!==void 0&&!this.hasProgressPastAnchor(e,t)?t:n}async recoverOnStartup(){const e=this.deps.installedGeneration();if(e===void 0)return"none";const t=(await this.state()).episode,n=this.sessionId(e.session);if(this.deps.cfg.newCycle===!0)return t!==null&&n!==null&&t.session_id===n&&await k(this.deps.nodeId,{op:"invalidate",session_id:n}),"none";if(t===null||n===null||t.session_id!==n)return"none";if(t.state==="admitted")if(t.fault.anchor_entry_id!==void 0&&!this.hasProgressPastAnchor(e.session,t.fault.anchor_entry_id)){if(!(await k(this.deps.nodeId,{op:"reopen",session_id:n})).applied)return"none"}else return await this.clearEpisode(e.session),"none";else if(t.state!=="pending")return"none";return await this.recordFault(A(I(t.fault))),await this.schedule(),"retry-armed"}hasProgressPastAnchor(e,t){const n=e.sessionManager.getBranch(),r=n.findIndex(a=>a.id===t);return(r===-1?n:n.slice(r+1)).some(a=>{if(a.type!=="message")return!1;const{message:l}=a;return l.role==="toolResult"?!0:l.role==="assistant"&&Array.isArray(l.content)&&l.content.length>0})}async schedule(){const e=this.deps.installedGeneration(),t=this.deps.currentSession();if(t.isStreaming||this.timer!==null)return;if(e===void 0){this.abandonTurnEnd();return}const n=await this.state(),r=n.fault;if(!this.isActiveAutoFault(r)||this.sessionId(t)!==null&&!this.pendingEpisodeMatches(t,r,n)){this.clearTimer(),this.abandonTurnEnd();return}const s=Date.now(),a=this.faultRetryDelayMs(r,s);if(a!==null&&this.deps.turnEnds?.holding()===!0&&Re(r.since,a,s)){await this.endRunTurnOnFault(t,r,a);return}a!==null?this.clearAndArmFaultRetry(a,e):this.abandonTurnEnd()}async endRunTurnOnFault(e,t,n){this.clearTimer(),this.abandonTurnEnd(),F({level:"info",event:"broker.runtime.diagnostic",fields:{message:`run turn completed on provider fault: ${t.kind} retry in ${n}ms is past the ${ie}ms turn window (since ${t.since}, attempt ${t.retry.attempt??0})`}}),await this.clearEpisode(e),await this.clearFault("pi\u2192provider")}async rearmIfStranded(){if(this.timer!==null||this.deps.installedGeneration()===void 0)return;const e=this.deps.currentSession();if(e.isStreaming)return;const t=await this.state();!this.isActiveAutoFault(t.fault)||!this.pendingEpisodeMatches(e,t.fault,t)||await this.schedule()}maybeSwitchProviderForRetry(e){if(e.type!=="auto_retry_start")return;const t=C("pi\u2192provider",e.errorMessage??"").kind;if(t!=="rate-limit"&&t!=="connection"||this.providerFallbackInFlight!==null||this.deps.cfg.modelExact===!0)return;const n=this.deps.installedGeneration();if(n===void 0)return;const r=this.deps.currentSession(),s=this.deps.currentServices(),a=this.deps.formatModelSpec(r.model,r.thinkingLevel);if(a===null)return;const l=H(this.deps.cfg.model,j(),!1);if(l===void 0)return;const c=z(l,Y()??this.deps.cfg.cwd,q()),o=ee(a),f=ae(c,o),g=f===void 0?{route:void 0,capacityExhausted:!1}:this.nextEligibleFallbackRoute(c.slice(f+1),this.deps.registryOf(s),r.getContextUsage()?.tokens,u=>!this.failedRetryRoutes.has(u.routeId));if(g.route===void 0){g.capacityExhausted&&(this.stageCapacityFailure(n,r),queueMicrotask(()=>{this.deps.installedGeneration()===n&&this.deps.currentSession()===r&&r.abortRetry()}));return}const p=g.route,y=(async()=>{const u=this.deps.registryOf(s).find(p.providerId,p.modelId);if(!u||(await r.setModel(u),this.deps.installedGeneration()!==n||this.deps.currentSession()!==r))return;const h=`${p.providerId}/${p.modelId}`===o.modelSpec?o.thinkingLevel:p.thinkingLevel;r.setThinkingLevel(h??"off"),this.failedRetryRoutes.add(p.routeId),await this.deps.broadcastModelChanged(!1)})().catch(u=>{F({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`provider route fallback failed: ${u instanceof Error?u.message:String(u)}`}})}).finally(()=>{this.deps.installedGeneration()===n&&(this.providerFallbackInFlight=null)});this.providerFallbackInFlight=y}maybeFallbackOnUnusableModel(e){if(e.type!=="message_end"||e.message.role!=="assistant"||e.message.stopReason!=="error")return;const t=pe(e.message.errorMessage??"");if(!t&&!fe(e.message.errorMessage??"")||this.deps.cfg.modelExact===!0||this.notFoundFallbackInFlight!==null)return;const n=this.deps.installedGeneration();if(n===void 0)return;const r=this.deps.currentSession(),s=this.deps.currentServices(),a=this.deps.formatModelSpec(r.model,r.thinkingLevel);if(a===null)return;const l=ee(a);this.notFoundModels.add(l.modelSpec);const c=H(this.deps.cfg.model,j(),!1),o=c?z(c,Y()??this.deps.cfg.cwd,q()):[],f=ae(o,l),g=f===void 0?{route:void 0,capacityExhausted:!1}:this.nextEligibleFallbackRoute(o.slice(f+1),this.deps.registryOf(s),r.getContextUsage()?.tokens,h=>!this.notFoundModels.has(`${h.providerId}/${h.modelId}`));if(g.route===void 0){g.capacityExhausted&&this.stageCapacityFailure(n,r);return}const p=g.route,y=`${p.providerId}/${p.modelId}${p.thinkingLevel?`:${p.thinkingLevel}`:""}`,u=(async()=>{const h=this.deps.registryOf(s).find(p.providerId,p.modelId);if(h===void 0){F({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`${t?"unconfigured-provider":"not_found"} fallback skipped: ${y} is not registered`}});return}if(await r.setModel(h),this.deps.installedGeneration()!==n||this.deps.currentSession()!==r)return;r.setThinkingLevel(p.thinkingLevel??"off"),await this.deps.broadcastModelChanged();const E=this.deps.formatModelSpec(r.model,r.thinkingLevel)??y;if(F({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`${t?"unconfigured-provider":"not_found"} fallback: ${a} unavailable \u2192 ${E}; re-driving turn`}}),await r.agent.waitForIdle(),this.deps.installedGeneration()!==n||this.deps.currentSession()!==r)return;t&&await this.clearFault("pi\u2192provider");const w=Q("recovery",{},be(a,E,t?"credential":"not-found"));ne(w,{type:"runtime",kind:"recovery"}),await this.deps.turnAdmission.admit(v=>K.fresh(()=>r.prompt(w,{preflightResult:v})))})().catch(h=>{F({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`${t?"unconfigured-provider":"not_found"} fallback failed: ${h instanceof Error?h.message:String(h)}`}})}).finally(()=>{this.deps.installedGeneration()===n&&(this.notFoundFallbackInFlight=null)});this.notFoundFallbackInFlight=u}nextEligibleFallbackRoute(e,t,n,r){let s=0,a=0;for(const l of e){if(!r(l)||ke([{...l,...ye(l,t),automaticFallback:!0}]).candidates.length===0)continue;const c=t.find(l.providerId,l.modelId);if(c!==void 0){if(s++,this.targetCannotFitContext(c.contextWindow,n)){a++;continue}return{route:l,capacityExhausted:!1}}}return{route:void 0,capacityExhausted:s>0&&s===a}}targetCannotFitContext(e,t){return Number.isFinite(e)&&Number.isFinite(t)&&e<t}stageCapacityFailure(e,t){const n=t.getContextUsage()?.tokens;this.stagedCapacityFailure={generation:e,errorMessage:Number.isFinite(n)?`All available fallback models have context windows smaller than the current conversation (${n} tokens).`:"All available fallback models have context windows smaller than the current conversation."}}async recordPendingProviderFault(e,t){const n=this.sessionId(e);if(n===null){await this.recordFault(t);return}await k(this.deps.nodeId,{op:"record-pending",session_id:n,input:t})}pendingEpisodeMatches(e,t,n){const r=this.sessionId(e),s=n.episode;return r!==null&&s!==null&&s.state==="pending"&&s.session_id===r&&s.fault.fault_id===t.fault_id}faultRetryDelayMs(e,t){if(e.retry.nextAt!==void 0){const n=Date.parse(e.retry.nextAt)-t;return Number.isFinite(n)?Math.max(0,n):null}return P(I(e),t)?.delayMs??null}clearAndArmFaultRetry(e,t){if(this.deps.installedGeneration()!==t)return;this.clearTimer();const n=Math.min(Math.max(0,e),Ee),r=Math.max(0,e-n);this.timer=setTimeout(()=>{if(this.timer=null,this.deps.installedGeneration()===t){if(r>0){this.clearAndArmFaultRetry(r,t);return}this.runFaultRetry().catch(s=>{this.abandonTurnEnd(),F({level:"warn",event:"broker.runtime.diagnostic",fields:{message:`fault retry fire failed: ${s instanceof Error?s.message:String(s)}`}})})}},n),typeof this.timer.unref=="function"&&this.timer.unref()}async runFaultRetry(){await this.attemptFaultRetry()===void 0&&this.abandonTurnEnd()}async attemptFaultRetry(){const e=this.deps.installedGeneration();if(e===void 0)return;const t=e.session;if(t.isStreaming)return;const n=(await this.state()).fault;if(!this.isActiveAutoFault(n))return;let r=n;if(n.kind==="connection"&&this.deps.turnEnds===void 0){const y=await this.deps.retryProbe();if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t||t.isStreaming)return;const u=(await this.state()).fault;if(!this.isActiveAutoFault(u)||u.since!==n.since)return;if(!y)return this.clearAndArmFaultRetry(Z(u.kind).baseDelayMs,e),"rearmed";r=u}const s=this.deps.turnEnds!==void 0,a=r.retry.attempt??0,l=Z(r.kind);if(a>=l.max){await this.recordFault(A(O(I(r)))),await this.clearEpisode(t);return}const c=P(I(r),Date.now());if(c===null){await this.recordFault(A(O(I(r)))),await this.clearEpisode(t);return}if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t)return;let o,f;const g=await this.deps.turnAdmission.admit(async y=>{const u=(await this.state()).fault;if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t||t.isStreaming||!this.isActiveAutoFault(u)||u.since!==r.since){y();return}const h=this.sessionId(t);if(h===null){y();return}if(!(await k(this.deps.nodeId,{op:"admit",session_id:h,fault_id:u.fault_id,retry:c.fault.retry})).applied){y();return}const w=r.anchor_entry_id;if(w!==void 0&&(t.sessionManager.getLeafId?.()??null)!==w&&!this.hasProgressPastAnchor(t,w))try{const d=await t.navigateTree(w);if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t){y();return}d.cancelled||this.deps.reWelcomeAll()}catch(d){F({level:"debug",event:"broker.runtime.diagnostic",fields:{message:`fault retry rewind skipped: ${d instanceof Error?d.message:String(d)}`}})}const v=Q("recovery",s?{attempt:c.attempt}:{attempt:c.attempt,max:c.max},Me(r.kind,s));ne(v,{type:"runtime",kind:"recovery"}),this.deps.turnEnds?.beginRecovery();const $=this.agentStarts;try{await K.fresh(()=>t.prompt(v,{preflightResult:y}))}catch(d){o={error:d,faultId:u.fault_id,startsBefore:$},oe(d)||(f={code:"model_error",message:`recovery prompt rejected: ${se(d)}`});return}finally{this.deps.turnEnds?.recoveryReturned(f)}return"prompted"});if(o===void 0)return g;const p=await this.settleRejectedRecoveryPrompt(e,t,o);if(p==="stale")throw o.error;return p==="rearmed"?"rearmed":void 0}async settleRejectedRecoveryPrompt(e,t,n){const r=this.sessionId(t);if(r===null)return"stale";const s=m(()=>{if(this.deps.installedGeneration()!==e||this.deps.currentSession()!==t||t.isStreaming||this.agentStarts!==n.startsBefore)throw new _},"assertCurrent"),a=se(n.error),l=oe(n.error);try{let c;return await this.retryWhileDaemonUnreachable("rejected recovery prompt settlement",async()=>{s();const{episode:o}=await this.state();if(s(),o===null||o.state!=="admitted"||o.session_id!==r||o.fault.fault_id!==n.faultId)throw new _;const f=I(o.fault),g=l&&this.deps.turnEnds===void 0?P(f,Date.now()):null,p=g===null?O(l?f:{...f,kind:"other",message:`recovery prompt rejected: ${a}`}):{...f,retry:{...f.retry,nextAt:g.nextAt}};if(s(),!(await k(this.deps.nodeId,{op:"settle-rejected",session_id:r,fault_id:n.faultId,reopen:g!==null,input:A(p)})).applied)throw new _;c=g?.nextAt},s),c===void 0?(F({level:"warn",event:"broker.runtime.diagnostic",fields:{message:`recovery prompt rejected, ending the retry episode: ${a}`}}),"ended"):(F({level:"warn",event:"broker.runtime.diagnostic",fields:{message:`recovery prompt rejected before any turn started, retrying at ${c}: ${a}`}}),await this.schedule(),"rearmed")}catch(c){if(!(c instanceof _))throw c;return"stale"}}}export{Ze as FaultRetry,ie as RUN_TURN_RETRY_WINDOW_MS,Te as isPrefillRejectionDuringEpisode,Me as retryCardBody,Re as runTurnRetryExpired,de as settledProviderFault,Qe as willDaemonRetry};
|
|
@@ -27,8 +27,9 @@ export declare class RunTurnEnds {
|
|
|
27
27
|
settled(writer: TurnEndWriter, end: TurnEnd, retryPending: boolean): void;
|
|
28
28
|
/** The daemon's fault retry is about to prompt its recovery turn. */
|
|
29
29
|
beginRecovery(): void;
|
|
30
|
-
/** The recovery prompt returned. If its turn never started (preflight failed or an input handler consumed it), no recovery runs
|
|
31
|
-
|
|
30
|
+
/** The recovery prompt returned. If its turn never started (preflight failed or an input handler consumed it), no recovery runs and
|
|
31
|
+
* the held end is written, with `rejected` as its error when the prompt rejected for a reason a retry cannot cure. */
|
|
32
|
+
recoveryReturned(rejected?: TurnError): void;
|
|
32
33
|
/** No recovery turn will run (retries exhausted or abandoned, the prompt failed, the session was replaced): end the turn now. */
|
|
33
34
|
abandon(): void;
|
|
34
35
|
/** Whether a turn end is held for a recovery turn. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var d=Object.defineProperty;var r=(t,e)=>d(t,"name",{value:e,configurable:!0});const i="crtr-turn-end";class l{static{r(this,"RunTurnEnds")}held;recoveryStarting=!1;started(){if(this.recoveryStarting){this.recoveryStarting=!1,this.held=void 0;return}this.flush()}settled(e,n,s){this.flush(),s?this.held={writer:e,end:n}:e.appendCustomEntry(i,n)}beginRecovery(){this.recoveryStarting=!0}recoveryReturned(e){this.recoveryStarting&&(e!==void 0&&this.held!==void 0&&(this.held={...this.held,end:{...this.held.end,error:e}}),this.abandon())}abandon(){this.recoveryStarting=!1,this.flush()}holding(){return this.held!==void 0}flush(){const e=this.held;e!==void 0&&(this.held=void 0,e.writer.appendCustomEntry(i,e.end))}}export{l as RunTurnEnds,i as TURN_END_ENTRY};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
1
|
+
var V=Object.defineProperty;var s=(e,r)=>V(e,"name",{value:r,configurable:!0});import{envProfileId as T}from"../../shared/env.js";import{mkdirSync as M}from"node:fs";import{join as U}from"node:path";import{readMergedLaunchConfig as D}from"../config.js";import{fork as F}from"node:child_process";import{fileURLToPath as $}from"node:url";import{freshChildNodeExecPath as G,recheckChildNode as K}from"./branded-host.js";import{LAUNCH_SYSTEM_PROMPT as W}from"./launch-prompt.js";import{nodeEnv as X}from"./nodes.js";import{editorLabel as B}from"../canvas/index.js";import{CANVAS_EXTENSIONS as ye,CANVAS_STOPHOOK_PATH as ve,CANVAS_INBOX_WATCHER_PATH as Le,CANVAS_RECAP_PATH as Re,CANVAS_GOAL_CAPTURE_PATH as be,CANVAS_PASSIVE_CONTEXT_PATH as He,CANVAS_CONTEXT_INTRO_PATH as Ve,CANVAS_TOOL_GUIDE_PATH as Me,CANVAS_DOC_SUBSTRATE_PATH as Ue,CANVAS_STRUCTURED_OUTPUT_PATH as De,CANVAS_BASH_VALVE_PATH as Fe}from"./canvas-extensions.js";import{nodeRunDir as j}from"../canvas/paths.js";import{writeFileBeneath as z}from"../spaces/open-beneath.js";import{projectOutputSchema as Y}from"./structured-output.js";import{CANVAS_EXTENSIONS as Q,mergeCanvasExtensions as P,assertMandatoryCanvasExtensions as J}from"./canvas-extensions.js";import{modelLadders as a,defaultProvider as C,STRENGTH_ALIASES as p,MODEL_PROVIDER_KEYS as x,MODEL_STRENGTHS as A,THINKING_SUFFIXES as Z,stripThinkingSuffix as m}from"./model-selection.js";import{OPENAI_PROVIDER_ENV as Ge,configuredLadderProviders as Ke,inferPortableModelSelection as We}from"./model-selection.js";function k(e,r=a()){return r[e]}s(k,"ladderFor");const S={light:0,medium:1,strong:2,ultra:3};function q(e,r,n){const t=m(e);for(const o of[!0,!1]){const i=o?A:[...A].sort((c,u)=>S[c]-S[u]);for(const c of n)for(const u of i){const f=r[c][u];if(!(o?f!==e:m(f)!==t))return{provider:c,strength:u}}}return null}s(q,"findLadderCell");function N(e){const r=e.lastIndexOf("/"),n=r===-1?void 0:e.slice(0,r),t=r===-1?e:e.slice(r+1),o=p[t];return o!==void 0?o:n!==void 0&&n!=="anthropic"?null:t==="opus"?"strong":t==="sonnet"?"medium":t==="haiku"?"light":null}s(N,"modelTokenStrength");function w(e,r=a()){const n=N(e);return n!==null?n:q(_(e,r),r,x)?.strength??null}s(w,"resolvedModelStrength");function ee(e){return e==="review"||e.startsWith("review/")||e.includes("/reviewers/")}s(ee,"isReviewQualityKind");function ne(e,r,n,t=a()){if(r===void 0)return n;if(!ee(e)||n===void 0)return r;const o=w(r,t),i=w(n,t);return o===null||i===null?r:S[o]<S[i]?n:r}s(ne,"floorReviewModel");function I(e){if(e.includes("/")){const r=e.lastIndexOf("/"),n=e.slice(0,r),t=e.slice(r+1);return n!==""&&(p[t]!==void 0||n==="anthropic"&&(t==="opus"||t==="sonnet"||t==="haiku"))}return p[e]!==void 0||e==="opus"||e==="sonnet"||e==="haiku"}s(I,"isPortableModelToken");function O(e,r=a()){const n=N(e);if(n!==null)return e.includes("/")?{family:e.slice(0,e.lastIndexOf("/")),strength:n}:e==="opus"||e==="sonnet"||e==="haiku"?{family:"anthropic",strength:n}:{family:C(r),strength:n}}s(O,"logicalModelIntent");function Te(e,r=a()){return I(e)?O(e,r):void 0}s(Te,"explicitModelIntent");function _(e,r=a()){if(e.includes("/")){const t=e.indexOf("/"),o=e.slice(0,t),i=e.slice(t+1),c=p[i];if(c!==void 0&&(o==="anthropic"||o==="openai"))return k(o,r)[c];if(o==="anthropic"){if(i==="opus")return r.anthropic.strong;if(i==="sonnet")return r.anthropic.medium;if(i==="haiku")return r.anthropic.light}return e}const n=p[e];return n!==void 0?k(C(r),r)[n]:e==="opus"?r.anthropic.strong:e==="sonnet"?r.anthropic.medium:e==="haiku"?r.anthropic.light:e}s(_,"normalizeModel");function y(e){const r=e.lastIndexOf(":");if(r<=0)return;const n=e.slice(r+1);return Z.has(n)?n:void 0}s(y,"thinkingSuffixOf");function re(e=x){const r=e.length>0?e:x,n=a(),t=[];for(const o of A)for(const i of r)t.push(n[i][o]);return t}s(re,"interleavedLadderCycle");function Pe(e,r=1,n){const t=re(n),o=_(e),i=m(o),c=y(o);let u=t.findIndex(f=>m(f)===i&&y(f)===c);return u===-1&&(u=t.findIndex(f=>m(f)===i)),u===-1?t[0]:t[(u+r+t.length)%t.length]}s(Pe,"nextLadderModel");function v(e,r,n,t){const o=t.kinds[e],i=t.modelLadders,c=r==="orchestrator"?o?.orchestratorModel??o?.model:o?.model,u=n.modelExact===!0?n.model:ne(e,n.model,c,i),f=n.modelExact===!0||n.modelOverride===!0&&n.model!==void 0&&!I(n.model),l=n.modelExact===!0?u:u!==void 0?_(u,i):void 0,h=f?void 0:n.modelIntent??(u!==void 0?O(u,i):void 0);return{launch:{model:l,modelIntent:h,modelExact:f?!0:void 0,tools:o?.tools??[],extensions:P([...Q,...o?.extensions??[]]),systemPrompt:W,env:{...n.extraEnv??{},...h?{CRTR_MODEL_INTENT:`${h.family}/${h.strength}`}:{}}},lifecycle:n.lifecycle}}s(v,"buildLaunchSpecFromMerged");function Ce(e,r,n){return v(e,r,n,D(n.cwd??process.cwd(),n.profileId!==void 0?n.profileId:T()||null))}s(Ce,"buildLaunchSpec");async function ke(e,r,n){const t=n.cwd??process.cwd(),o=n.profileId!==void 0?n.profileId:T()||null,i=await oe(t,o);return v(e,r,n,i)}s(ke,"buildLaunchSpecAsync");const L=15e3;function te(e,r,n){const t=r!==null?`was killed by ${r}`:`exited with code ${e}`,o=n.trim().split(`
|
|
2
2
|
`).slice(-8).join(`
|
|
3
|
-
`).slice(-1500);return`launch config subprocess ${t}${o!==""?`: ${o}`:" (no stderr output)"}`}s(
|
|
3
|
+
`).slice(-1500);return`launch config subprocess ${t}${o!==""?`: ${o}`:" (no stderr output)"}`}s(te,"describeLaunchConfigExit");class R extends Error{static{s(this,"LaunchConfigWorkerCrash")}}async function oe(e,r,n={}){const t=n.execPath??await G(),o=n.workerPath??$(new URL("./launch-config-worker.js",import.meta.url));try{return await b(e,r,t,o)}catch(i){if(!(i instanceof R))throw i;const c=await K(t);if(c===t)throw i;return b(e,r,c,o)}}s(oe,"readMergedLaunchConfigAsync");function b(e,r,n,t){return new Promise((o,i)=>{const c=F(t,[],{execPath:n,stdio:["ignore","ignore","pipe","ipc"],serialization:"advanced"});let u="";c.stderr?.setEncoding("utf8").on("data",d=>{u=(u+d).slice(-8192)}),c.send({cwd:e,profileId:r});let f=!1;const l=s(d=>{f||(f=!0,clearTimeout(h),c.connected&&c.disconnect(),c.killed||c.kill("SIGKILL"),d())},"finish"),h=setTimeout(()=>l(()=>i(new Error(`timed out after ${L}ms reading launch config for ${e}`))),L);c.once("message",d=>{d.error!==void 0?l(()=>i(new Error(d.error))):d.config!==void 0?l(()=>o(d.config)):l(()=>i(new Error("launch config subprocess returned no result")))});let g=null;c.on("error",d=>{g??=d}),c.once("close",(d,H)=>{if(f)return;const E=te(d,H,u);l(()=>i(new R(g===null?E:`${E} (${g.message})`)))})})}s(b,"runLaunchConfigWorker");function ie(e,r){try{const n=j(e);M(n,{recursive:!0});const t=U(n,"system-prompt.md");return z(n,t,r,{atomic:!0}),t}catch{return null}}s(ie,"writeSystemPromptFile");function Ne(e,r={}){const n=e.launch,t=P(n?.extensions);J(t);const o=[];for(const i of t)o.push("-e",i);if(o.push("-n",B(e)),r.forkFrom!==void 0&&r.forkFrom!=="")o.push("--fork",r.forkFrom);else{const i=r.resumeSessionPath??r.resumeSessionId;i!==void 0&&o.push("--session",i),r.newCycle===!0&&o.push("--crtr-new-cycle")}if(n?.model!==void 0&&o.push("--model",n.model),n?.modelExact===!0&&o.push("--crtr-model-exact"),n?.tools!==void 0&&n.tools.length>0&&o.push("--tools",n.tools.join(",")),n?.systemPrompt!==void 0&&n.systemPrompt!==""){const i=ie(e.node_id,n.systemPrompt)??n.systemPrompt;o.push("--append-system-prompt",i)}return r.prompt!==void 0&&r.prompt!==""&&o.push(r.prompt),Y(e.node_id),{argv:o,env:X(e)}}s(Ne,"buildPiArgv");function we(e){for(const[t,o]of Object.entries(e.env))process.env[t]=o;const r={cwd:e.env.CRTR_NODE_CWD??process.cwd(),extensionPaths:[]},n=e.argv;for(let t=0;t<n.length;t++){const o=n[t];switch(o){case"-e":r.extensionPaths.push(n[++t]);break;case"-n":r.editorName=n[++t];break;case"--fork":r.forkFrom=n[++t];break;case"--session":{const i=n[++t];i.includes("/")||i.endsWith(".jsonl")?r.resumeSessionPath=i:r.resumeSessionId=i;break}case"--model":r.model=n[++t];break;case"--crtr-model-exact":r.modelExact=!0;break;case"--tools":r.tools=n[++t].split(",").filter(i=>i!=="");break;case"--append-system-prompt":r.appendSystemPromptPath=n[++t];break;case"--crtr-new-cycle":r.newCycle=!0;break;default:r.firstPrompt=o;break}}return r}s(we,"piInvocationToSdkConfig");export{Fe as CANVAS_BASH_VALVE_PATH,Ve as CANVAS_CONTEXT_INTRO_PATH,Ue as CANVAS_DOC_SUBSTRATE_PATH,ye as CANVAS_EXTENSIONS,be as CANVAS_GOAL_CAPTURE_PATH,Le as CANVAS_INBOX_WATCHER_PATH,He as CANVAS_PASSIVE_CONTEXT_PATH,Re as CANVAS_RECAP_PATH,ve as CANVAS_STOPHOOK_PATH,De as CANVAS_STRUCTURED_OUTPUT_PATH,Me as CANVAS_TOOL_GUIDE_PATH,Ge as OPENAI_PROVIDER_ENV,Ce as buildLaunchSpec,ke as buildLaunchSpecAsync,Ne as buildPiArgv,Ke as configuredLadderProviders,te as describeLaunchConfigExit,Te as explicitModelIntent,We as inferPortableModelSelection,re as interleavedLadderCycle,I as isPortableModelToken,O as logicalModelIntent,N as modelTokenStrength,Pe as nextLadderModel,_ as normalizeModel,we as piInvocationToSdkConfig,oe as readMergedLaunchConfigAsync};
|
|
@@ -17,6 +17,10 @@ export declare function transition(nodeId: string, event: LifecycleEvent, opts?:
|
|
|
17
17
|
reason?: TerminalReason;
|
|
18
18
|
outcome?: TerminalOutcomeInput;
|
|
19
19
|
}): Promise<NodeMeta>;
|
|
20
|
+
/** The last relay generation published when it bound. Missing means no replacement has bound yet. */
|
|
21
|
+
export declare function brokerRelayGeneration(nodeId: string): string | null;
|
|
22
|
+
/** The marker names the relay generation the committed disposition released. */
|
|
23
|
+
export declare function recordBrokerRelease(nodeId: string, relay: string | null): void;
|
|
20
24
|
/** Whether this row is a PARKED node — terminalized by the unattended clock
|
|
21
25
|
* rather than by finishing its own work. The one read of the park marker:
|
|
22
26
|
* `done` qualified by `intent='parked'`, both written in the same atomic
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
2
|
-
`)[0]??"";let n=0,
|
|
1
|
+
var L=Object.defineProperty;var o=(t,e)=>L(t,"name",{value:e,configurable:!0});import{getNode as y,cancelCronsOnWake as B,transitionStatusIntent as D}from"../canvas/index.js";import{closeSync as J,constants as N,existsSync as j,fstatSync as F,readSync as P}from"node:fs";import{join as E}from"node:path";import{BROKER_RELEASED_MARKER as $,BROKER_RELAY_GENERATION as M,nodeRunDir as x}from"../canvas/paths.js";import{openBeneath as T,writeFileBeneath as W,isAbsentOrRefused as q}from"../spaces/open-beneath.js";import{emitEvent as C}from"../events/emit.js";import{composeNodeOutcomeDocument as K}from"./outcome-document.js";import{afterCommit as Y,withCanvasWrite as G}from"../canvas/db.js";import{nodeObject as z}from"../graph/objects.js";import{endMessageSubscriptions as V}from"../graph/watches.js";import{settleNodeObjectWaits as v}from"../canvas/waits.js";import{runStructuredRequestEffects as H,settlePendingStructuredRequestForNodeIn as I}from"../canvas/structured-requests.js";const p="*",Q={finish:"finished",park:"parked",cancel:"stranded",crash:"crashed","refresh-launch":null,revive:null},u=["active","idle"],U={finish:{status:"done",intent:"done",from:u},park:{status:"done",intent:"parked",from:u},cancel:{status:"canceled",intent:null,from:p},crash:{status:"dead",from:u},yield:{intent:"refresh",from:u},release:{status:"idle",intent:"idle-release",from:[...u,"done"]},"refresh-launch":{status:"active",intent:"refresh",from:p},revive:{status:"active",intent:null,from:p}};function X(t,e){const r=t.split(`
|
|
2
|
+
`)[0]??"";let n=0,l="";for(const c of r){const s=Buffer.byteLength(c,"utf8");if(n+s>e)break;n+=s,l+=c}return l}o(X,"firstLineAtMost");function Z(t){if(t===void 0)return null;const e=t.message===void 0?void 0:X(t.message,500),r={...t,...e===void 0?{}:{message:e}},n=JSON.stringify(r);return Buffer.byteLength(n,"utf8")<=4*1024?n:JSON.stringify({schema:"crtr.node-outcome-detail/v1",truncated:!0,...e===void 0?{}:{message:e}})}o(Z,"detailJson");function ee(t,e){let r,n;return e==="finished"||e==="declined"?(r="done",n="done"):e==="parked"?(r="done",n="parked"):e==="closed"||e==="retired"||e==="stranded"||e==="deadline_exceeded"?(r="canceled",n=null):(r="dead",n=null),{...t,status:r,intent:n,terminal_reason:e}}o(ee,"nodeAtOutcome");async function Oe(t,e,r){const n=U[e],l=Object.prototype.hasOwnProperty.call(n,"status"),c=Object.prototype.hasOwnProperty.call(n,"intent"),s=r?.reason??Q[e],d={};l&&(d.status=n.status),c&&(d.intent=n.intent??null),s!==void 0&&(d.terminal_reason=s);const f=e==="finish"||e==="park"||e==="cancel"||e==="crash",_=f?new Date().toISOString():null,h=s,k=Z(r?.outcome?.detail),S=f?s==="deadline_exceeded"?{intended:"expired",failureReason:"node_deadline_exceeded",failureDetail:r?.outcome?.detail??null}:{intended:"failed",failureReason:s,failureDetail:r?.outcome?.detail??null}:null,w=await G(async i=>{const a=f?await y(t):null,O=f&&a?.kind!=="human",g=O&&a?.run_id!=null&&s!=="parked"&&a.outcome_at!==null&&(u.includes(a.status)||a.outcome_reason==="parked");let R;O&&a!==null&&(a.outcome_at===null||g)&&(R=JSON.stringify(await K({node:ee(a,h),outcome:{kind:"failure",reason:h,at:_,revision:(a.outcome_revision??0)+1,detailJson:k}})));const m=await D(t,d,{fromStatuses:n.from===p?void 0:n.from,requireNoFinalReport:e==="cancel",...O?{outcome:{kind:"failure",reason:h,detailJson:k,at:_,...g?{supersede:!0}:{},...R===void 0?{}:{documentJson:R}}}:{}});f&&m>0&&(await V(i,await z(i,t),"done"),await v(i,t));const A=m===1&&S!==null?await I(i,t,S):null;if(m===1&&(e==="release"||e==="park"||e==="finish"||e==="cancel")){const b=te(t);Y(()=>ne(t,b))}return{changes:m,settlement:A}});if(w.changes===0){const i=await y(t);if(i===null)throw new Error(`transition: unknown node ${t}`);if(e==="cancel"&&i.final_report!==null&&i.final_report!==void 0)return i;throw new Error(`illegal lifecycle transition: '${e}' from status='${i.status}' (node ${t})`)}return w.settlement?.changed&&H(w.settlement.row),(e==="finish"||e==="park"||e==="cancel")&&await B(t),await y(t)}o(Oe,"transition");function te(t){const e=x(t);let r;try{r=T(e,E(e,M),N.O_RDONLY|N.O_NOFOLLOW)}catch(n){if(q(n))return null;throw n}try{if(!F(r).isFile())return null;const n=Buffer.alloc(37),l=P(r,n,0,n.length,0),c=n.toString("utf8",0,l);return l===36&&/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(c)?c:null}finally{J(r)}}o(te,"brokerRelayGeneration");function ne(t,e){const r=x(t);if(!(e===null||!j(r)))try{W(r,E(r,$),e,{atomic:!0})}catch(n){C({level:"warn",event:"node.lifecycle.release_marker_failed",node_id:t,error:n})}}o(ne,"recordBrokerRelease");function re(t){return t.status==="done"&&t.intent==="parked"}o(re,"isParked");function Re(t){return re(t)&&t.lifecycle==="resident"&&t.final_report==null&&t.deleting_at==null}o(Re,"isParkedResident");function ye(t){return t?.intent==="idle-release"}o(ye,"hasIdleReleaseIntent");export{te as brokerRelayGeneration,ye as hasIdleReleaseIntent,re as isParked,Re as isParkedResident,ne as recordBrokerRelease,Oe as transition};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var K=Object.defineProperty;var l=(e,t)=>K(e,"name",{value:t,configurable:!0});import{envHomeOverride as X,envRuntimeHomeOverride as z,INBOX_BETWEEN_TURNS_ENV as V,SCOPES_FILE_ENV as Y,scopeAllowed as C}from"../../shared/env.js";import{isOwnMemoryScope as Q,validateScopes as Z}from"../scopes.js";import{activeGrant as B,terminalGrant as k}from"../grants/mirror.js";import{grantSnapshotMissing as ee}from"../../daemon/readiness.js";import{appendRunEvent as ne}from"../runs/events.js";import{readConfig as S}from"../config.js";import{InputError as T}from"../io.js";import{ApiError as g}from"../../api/errors.js";import{assertGranteeRunsHere as re}from"./unsandboxed.js";import{randomBytes as te}from"node:crypto";import{existsSync as P}from"node:fs";import{resolve as ie,relative as ae,isAbsolute as U,join as oe,sep as le}from"node:path";import{appSpace as se}from"../layout.js";import{scopesFilePath as de}from"../canvas/paths.js";import{createNode as ce,assertRunNotDeleting as ue,conversationOwner as fe,getNode as u,getRow as pe}from"../canvas/index.js";import{ensureAppSpace as me}from"../spaces/space.js";import{LAUNCHER_SOCKET as H}from"../../launcher/spec.js";import{resolveInstallId as _e}from"../canvas/install-id.js";import{isAppRun as we}from"../canvas/app-run.js";import{contextDir as ge}from"../canvas/paths.js";import{conversationIdFromPath as he}from"../conversation-store/index.js";import{usage as L}from"../errors.js";import{assertProfileAvailableForBirth as ve}from"../profiles/deletion-reservation.js";import{loadExactProfileManifest as Re}from"../profiles/manifest.js";import{admitNodeCreate as ye}from"../command-hooks/lifecycle-create.js";import{getById as Ee,nodeObject as I,ownerForGrantee as Ce}from"../graph/objects.js";import{recordActedOn as Ie}from"../graph/edges.js";import{recordWatch as Ne,copyWatchesForFork as ke}from"../graph/watches.js";import{publish as Se}from"../graph/events.js";import{canvasDatabaseUrl as Te}from"../canvas/db.js";import{someAsync as Oe}from"../async-array.js";let O;const j=new Map;function wn(e){O=e}l(wn,"setInstallId");async function xe(){if(O!==void 0)return O;const e=await Te(),t=j.get(e);if(t!==void 0)return t;const a=await _e();return j.set(e,a),a}l(xe,"installId");function De(){return`${Date.now().toString(36)}-${te(4).toString("hex")}`}l(De,"newJobId");async function be(){return`${await xe()}-${De()}`}l(be,"newNodeId");const x=128,Ae=/^[a-z0-9-]+$/;class q extends Error{static{l(this,"NodeIdConflictError")}nodeId;constructor(t){super(`node already exists: ${t}`),this.nodeId=t,this.name="NodeIdConflictError"}}function D(e){if(e.length===0||e.length>x)throw L(`invalid --node-id '${e}': must be 1-${x} characters`,{next:`Pass an id-safe string (lowercase letters, digits, hyphens) of at most ${x} characters.`});if(!Ae.test(e))throw L(`invalid --node-id '${e}': must contain only lowercase letters, digits, and hyphens (e.g. 'remote42', 'my-node-1')`,{next:"Use only lowercase letters, digits, and hyphens."})}l(D,"validateNodeIdFormat");async function gn(e){if(D(e),await u(e)!==null)throw new q(e)}l(gn,"preflightNodeId");async function Me(e){if(D(e),await u(e)!==null)throw new q(e);return e}l(Me,"validatedExplicitNodeId");async function hn(e){let t=e;const a=new Set;for(;;){if(a.has(t))return t;a.add(t);const s=await pe(t);if(s===null||s.parent==null)return t;t=s.parent}}l(hn,"rootOfSpine");function vn(){const e=process.env;return{nodeId:e.CRTR_NODE_ID??null,kind:e.CRTR_KIND??null,mode:e.CRTR_MODE??null}}l(vn,"currentNodeContext");function $e(e){return we(e)}l($e,"inboxBetweenTurns");function Rn(e){const t={CRTR_NODE_ID:e.node_id,CRTR_KIND:e.kind,CRTR_MODE:e.mode,CRTR_LIFECYCLE:e.lifecycle,CRTR_NODE_CWD:e.cwd,CRTR_CONTEXT_DIR:ge(e.node_id,e.grantee),CRTR_CYCLES:(e.cycles??0).toString(),CRTR_PROFILE_ID:e.profile_id??"",PI_CLAUDE_OAUTH_REINJECT_SCOPE:"never"};e.parent&&(t.CRTR_PARENT_NODE_ID=e.parent),e.run_id&&(t.CRTR_RUN_ID=e.run_id),$e(e)&&(t[V]="1"),e.scopes!==null&&e.scopes!==void 0&&(t[Y]=de(e.node_id)),t.CRTR_GRANTEE=e.grantee??"app:terminal";const a=X();a!==void 0&&a!==""&&(t.CRTR_HOME=a);const s=z();s!==void 0&&s!==""&&(t.CRTR_RUNTIME_HOME=s);const f=process.env.CRTR_SUBTREE;return f!==void 0&&f!==""&&(t.CRTR_SUBTREE=f),{...t,...e.launch?.env??{}}}l(Rn,"nodeEnv");async function Fe(e){const t=he(e),a=t===null?null:await fe(t);return a===null?null:await u(a)}l(Fe,"forkSourceNode");async function yn(e){const t=e.nodeId!==void 0?(D(e.nodeId),e.nodeId):await be(),a=e.parent??null,s=a===null,f=e.spawnedBy??a,m=e.creator??null,b=e.mode??"base",N=e.lifecycle??(s?"resident":"terminal"),h=e.run===void 0?await k():null,n={node_id:t,name:e.name??e.kind,description:e.description,cycles:0,created:new Date().toISOString(),cwd:e.cwd,host_kind:"broker",profile_id:e.profile_id??null,kind:e.kind,mode:b,lifecycle:N,persona_ack:{kind:e.kind,mode:b,lifecycle:N},status:"active",parent:a,spawned_by:f,creator:m,fork_from:e.forkFrom,fork_source_file:e.forkSourceFile,review_binding:e.reviewBinding,model_override:e.modelOverride??null,scopes:e.scopes??null,isolated:e.isolated===!0,grantee:e.run?.grantee??"app:terminal",sub:e.run?.sub??h?.sub??null,grant_id:e.run?.grant_id??h?.id??null,client_id:e.run?.client_id??null,metadata_json:s?e.run?.metadata_json??null:null,run_id:s?t:e.run?.run_id??null,event_cursor:s?e.run?.event_cursor??1:null,usage_json:s?e.run?.usage_json??JSON.stringify({input_tokens:0,output_tokens:0,cache_read_tokens:0,cache_write_tokens:0,cost_usd:0}):null,passive_default:e.passiveDefault??!1,...e.managedWorktree!==void 0?{managed_worktree:e.managedWorktree}:{},intent:null,deadline_at:e.deadlineAt??null,pi_session_id:null,pi_session_file:null,launch:e.launch},y=e.scopes===void 0?void 0:Z(e.scopes),v=a===null?null:await u(a);if(a!==null&&v===null)throw new Error(`cannot spawn under unknown parent node: ${a}`);if(v?.run_id&&(await u(v.run_id))?.deleting_at)throw new Error("run is deleting");const c=m===null?null:await u(m);if(m!==null&&c===null)throw new Error(`cannot spawn from unknown creator node: ${m}`);if(c?.run_id&&(await u(c.run_id))?.deleting_at)throw new Error("run is deleting");const A=e.forkSourceFile==null?null:await Fe(e.forkSourceFile),M=e.replaces==null?null:await u(e.replaces),_=v??A??M??c??(f===null?null:await u(f)),W=[v,c,A,M,f===null?null:await u(f)],$=await Oe(W,async r=>r?.isolated===!0||r?.run_id!=null&&(await u(r.run_id))?.isolated===!0);if($&&e.isolated===!1)throw new g(403,"isolated_run","isolation cannot be turned off under an isolated run",{param:"isolated"});if(n.isolated=$||e.isolated===!0,_!==null){const r=c??_;if(c&&_.grantee&&c.grantee&&c.grantee!==_.grantee)throw new g(403,"other_app_run","cannot create under another app's node");const i=c&&c!==_?[_,c]:[_],d=[],w=new Set;for(const o of i){o.scopes!==null&&o.scopes!==void 0&&d.push(o.scopes);const p=o.grantee==="app:terminal"?await k():o.grant_id?await B(o.grant_id):null;if(p){const R=[...p.resolved];w.add(R),d.push(R)}else if(!(o.grantee==="app:terminal"&&!S("user").runtime?.issuer)){const R=[];w.add(R),d.push(R)}}const E=l(o=>Q(o,r.grantee==="user:local"?"app:terminal":r.grantee??"app:terminal"),"ownMemory"),F=l((o,p)=>E(p)&&w.has(o)||C(o,p),"allowed");if(y!==void 0&&!y.every(o=>d.every(p=>F(p,o))))throw new T({error:"unknown_scope",message:"child scopes must be covered by the creating node, its source and their current grants",received:y,field:"scopes",next:"Request only scopes the creating node currently holds."});if(n.scopes=y??(i.every(o=>o.scopes==null)&&i.every(o=>o.grant_id===r.grant_id&&o.sub===r.sub)?null:i.length===1?_.scopes??null:[...new Set(d.flat().filter(o=>d.every(p=>F(p,o))))]),n.scopes?.includes("crtr:act")&&!C(n.scopes,"crtr:llm"))throw new T({error:"invalid_scope",message:"crtr:act requires crtr:llm",received:n.scopes,field:"scopes",next:"Include crtr:llm or omit crtr:act."});n.grantee=r.grantee==="user:local"?"app:terminal":r.grantee,n.sub=r.sub??(n.grantee==="app:terminal"?h?.sub:null)??null,n.grant_id=r.grant_id??(n.grantee==="app:terminal"?h?.id:null)??null,n.client_id=r.client_id,n.run_id=s?t:r.run_id??null}if(re(n.grantee),e.run===void 0&&n.grantee==="app:terminal"&&S("user").runtime?.issuer&&h===null)throw await ee()?new g(503,"runtime_starting","the first grant snapshot from the directory has not committed"):new g(403,"scope_missing","terminal grant is not active");if(n.isolated){if(process.platform!=="linux"||!P(H))throw new g(400,"isolated_unavailable","isolated nodes require the Linux root launcher");if(e.managedWorktree!==void 0)throw new g(400,"invalid_request","isolated nodes cannot use a worktree",{param:"worktree"});const r=oe(se(n.grantee??"app:terminal").nodes,t),i=ae(r,ie(n.cwd));if(!U(n.cwd)||i===".."||i.startsWith(`..${le}`)||U(i))throw new g(400,"invalid_request","isolated node cwd must be inside its own folder",{param:"cwd"})}if(n.profile_id){const r=Re(n.profile_id).manifest.grantee??"app:terminal";if(r!==n.grantee){const i=`crtr:memory:rw:${r}:profile:${n.profile_id}`,d=n.grantee==="app:terminal"?await k():n.grant_id?await B(n.grant_id):null;if(!(n.grantee==="app:terminal"&&!S("user").runtime?.issuer)&&(!d||d.grantee!==n.grantee||!C(d.resolved,i))||!C(n.scopes,i))throw new T({error:"scope_missing",message:`missing scope ${i}`,received:n.profile_id,field:"profile",next:"Select a profile covered by this run and its current grant."})}}if(ve(n.profile_id??null),!n.grantee)throw new Error("node creation requires an app");const G=await ye({kind:n.kind,mode:n.mode,cwd:n.cwd,profile:n.profile_id??null,root:s,parentId:a,requestingNodeId:m,lifecycle:N,replaces:e.replaces??null},n.grantee),J=l(async(r,i)=>{const d=await I(r,i.node_id),w=m??a??e.actor??f??null,E=w!==null&&(await Ee(r,w))?.type==="node"?await I(r,w):await Ce(r,e.run===void 0?"user:local":i.grantee);await Ie(r,E,d),a!==null&&e.subscribe!==!1&&await Ne(r,await I(r,a),d,"spawn"),e.forkSourceNode&&await ke(r,await I(r,e.forkSourceNode),d),await Se(r,{event:"created",object:d,actor:E,attrs:{name:i.name,kind:i.kind,creator:m??null}})},"recordBirth");try{e.nodeId!==void 0&&await Me(e.nodeId),await e.beforeCreate?.(),(process.platform!=="linux"||P(H))&&await me(n.grantee??"app:terminal"),await ce(n,{outcomeDelivery:e.outcomeDelivery,withinCreate:l(async(r,i)=>{i.run_id&&i.run_id!==i.node_id&&await ue(r,i.run_id),s&&e.run===void 0&&await ne(r,t,t,"run.started",{scopes:i.scopes??null,payer:"user",usage:JSON.parse(i.usage_json)}),await e.withinCreate?.(r,i),await J(r,i)},"withinCreate")})}finally{G()}return n}l(yn,"spawnNode");export{q as NodeIdConflictError,vn as currentNodeContext,Fe as forkSourceNode,$e as inboxBetweenTurns,De as newJobId,be as newNodeId,Rn as nodeEnv,gn as preflightNodeId,hn as rootOfSpine,wn as setInstallId,yn as spawnNode};
|
|
1
|
+
var K=Object.defineProperty;var l=(e,r)=>K(e,"name",{value:r,configurable:!0});import{envHomeOverride as X,envRuntimeHomeOverride as z,INBOX_BETWEEN_TURNS_ENV as V,SCOPES_FILE_ENV as Y,scopeAllowed as C}from"../../shared/env.js";import{isOwnMemoryScope as Q,validateScopes as Z}from"../scopes.js";import{activeGrant as B,terminalGrant as k}from"../grants/mirror.js";import{grantSnapshotMissing as ee}from"../../daemon/readiness.js";import{appendRunEvent as ne}from"../runs/events.js";import{readConfig as S}from"../config.js";import{InputError as T}from"../io.js";import{ApiError as g}from"../../api/errors.js";import{assertGranteeRunsHere as re}from"./unsandboxed.js";import{randomBytes as te}from"node:crypto";import{existsSync as P}from"node:fs";import{resolve as ie,relative as ae,isAbsolute as U,join as oe,sep as le}from"node:path";import{appSpace as se}from"../layout.js";import{scopesFilePath as de}from"../canvas/paths.js";import{createNode as ce,assertRunNotDeleting as ue,conversationOwner as fe,getNode as u,getRow as me}from"../canvas/index.js";import{ensureAppSpace as pe}from"../spaces/space.js";import{LAUNCHER_SOCKET as H}from"../../launcher/spec.js";import{resolveInstallId as _e}from"../canvas/install-id.js";import{isAppRun as we}from"../canvas/app-run.js";import{contextDir as ge}from"../canvas/paths.js";import{conversationIdFromPath as he}from"../conversation-store/index.js";import{usage as L}from"../errors.js";import{assertProfileAvailableForBirth as ve}from"../profiles/deletion-reservation.js";import{loadExactProfileManifest as Re}from"../profiles/manifest.js";import{admitNodeCreate as ye}from"../command-hooks/lifecycle-create.js";import{getById as Ee,nodeObject as I,ownerForGrantee as Ce}from"../graph/objects.js";import{recordActedOn as Ie}from"../graph/edges.js";import{recordWatch as Ne,copyWatchesForFork as ke}from"../graph/watches.js";import{publish as Se}from"../graph/events.js";import{canvasDatabaseUrl as Te}from"../canvas/db.js";import{someAsync as Oe}from"../async-array.js";import{admitIntoManagedCheckout as xe}from"../worktree-mutation-async.js";let O;const j=new Map;function hn(e){O=e}l(hn,"setInstallId");async function De(){if(O!==void 0)return O;const e=await Te(),r=j.get(e);if(r!==void 0)return r;const a=await _e();return j.set(e,a),a}l(De,"installId");function be(){return`${Date.now().toString(36)}-${te(4).toString("hex")}`}l(be,"newJobId");async function Ae(){return`${await De()}-${be()}`}l(Ae,"newNodeId");const x=128,Me=/^[a-z0-9-]+$/;class q extends Error{static{l(this,"NodeIdConflictError")}nodeId;constructor(r){super(`node already exists: ${r}`),this.nodeId=r,this.name="NodeIdConflictError"}}function D(e){if(e.length===0||e.length>x)throw L(`invalid --node-id '${e}': must be 1-${x} characters`,{next:`Pass an id-safe string (lowercase letters, digits, hyphens) of at most ${x} characters.`});if(!Me.test(e))throw L(`invalid --node-id '${e}': must contain only lowercase letters, digits, and hyphens (e.g. 'remote42', 'my-node-1')`,{next:"Use only lowercase letters, digits, and hyphens."})}l(D,"validateNodeIdFormat");async function vn(e){if(D(e),await u(e)!==null)throw new q(e)}l(vn,"preflightNodeId");async function $e(e){if(D(e),await u(e)!==null)throw new q(e);return e}l($e,"validatedExplicitNodeId");async function Rn(e){let r=e;const a=new Set;for(;;){if(a.has(r))return r;a.add(r);const s=await me(r);if(s===null||s.parent==null)return r;r=s.parent}}l(Rn,"rootOfSpine");function yn(){const e=process.env;return{nodeId:e.CRTR_NODE_ID??null,kind:e.CRTR_KIND??null,mode:e.CRTR_MODE??null}}l(yn,"currentNodeContext");function Fe(e){return we(e)}l(Fe,"inboxBetweenTurns");function En(e){const r={CRTR_NODE_ID:e.node_id,CRTR_KIND:e.kind,CRTR_MODE:e.mode,CRTR_LIFECYCLE:e.lifecycle,CRTR_NODE_CWD:e.cwd,CRTR_CONTEXT_DIR:ge(e.node_id,e.grantee),CRTR_CYCLES:(e.cycles??0).toString(),CRTR_PROFILE_ID:e.profile_id??"",PI_CLAUDE_OAUTH_REINJECT_SCOPE:"never"};e.parent&&(r.CRTR_PARENT_NODE_ID=e.parent),e.run_id&&(r.CRTR_RUN_ID=e.run_id),Fe(e)&&(r[V]="1"),e.scopes!==null&&e.scopes!==void 0&&(r[Y]=de(e.node_id)),r.CRTR_GRANTEE=e.grantee??"app:terminal";const a=X();a!==void 0&&a!==""&&(r.CRTR_HOME=a);const s=z();s!==void 0&&s!==""&&(r.CRTR_RUNTIME_HOME=s);const f=process.env.CRTR_SUBTREE;return f!==void 0&&f!==""&&(r.CRTR_SUBTREE=f),{...r,...e.launch?.env??{}}}l(En,"nodeEnv");async function Be(e){const r=he(e),a=r===null?null:await fe(r);return a===null?null:await u(a)}l(Be,"forkSourceNode");async function Cn(e){const r=e.nodeId!==void 0?(D(e.nodeId),e.nodeId):await Ae(),a=e.parent??null,s=a===null,f=e.spawnedBy??a,p=e.creator??null,b=e.mode??"base",N=e.lifecycle??(s?"resident":"terminal"),h=e.run===void 0?await k():null,n={node_id:r,name:e.name??e.kind,description:e.description,cycles:0,created:new Date().toISOString(),cwd:e.cwd,host_kind:"broker",profile_id:e.profile_id??null,kind:e.kind,mode:b,lifecycle:N,persona_ack:{kind:e.kind,mode:b,lifecycle:N},status:"active",parent:a,spawned_by:f,creator:p,fork_from:e.forkFrom,fork_source_file:e.forkSourceFile,review_binding:e.reviewBinding,model_override:e.modelOverride??null,scopes:e.scopes??null,isolated:e.isolated===!0,grantee:e.run?.grantee??"app:terminal",sub:e.run?.sub??h?.sub??null,grant_id:e.run?.grant_id??h?.id??null,client_id:e.run?.client_id??null,metadata_json:s?e.run?.metadata_json??null:null,run_id:s?r:e.run?.run_id??null,event_cursor:s?e.run?.event_cursor??1:null,usage_json:s?e.run?.usage_json??JSON.stringify({input_tokens:0,output_tokens:0,cache_read_tokens:0,cache_write_tokens:0,cost_usd:0}):null,passive_default:e.passiveDefault??!1,...e.managedWorktree!==void 0?{managed_worktree:e.managedWorktree}:{},intent:null,deadline_at:e.deadlineAt??null,pi_session_id:null,pi_session_file:null,launch:e.launch},y=e.scopes===void 0?void 0:Z(e.scopes),v=a===null?null:await u(a);if(a!==null&&v===null)throw new Error(`cannot spawn under unknown parent node: ${a}`);if(v?.run_id&&(await u(v.run_id))?.deleting_at)throw new Error("run is deleting");const c=p===null?null:await u(p);if(p!==null&&c===null)throw new Error(`cannot spawn from unknown creator node: ${p}`);if(c?.run_id&&(await u(c.run_id))?.deleting_at)throw new Error("run is deleting");const A=e.forkSourceFile==null?null:await Be(e.forkSourceFile),M=e.replaces==null?null:await u(e.replaces),_=v??A??M??c??(f===null?null:await u(f)),W=[v,c,A,M,f===null?null:await u(f)],$=await Oe(W,async t=>t?.isolated===!0||t?.run_id!=null&&(await u(t.run_id))?.isolated===!0);if($&&e.isolated===!1)throw new g(403,"isolated_run","isolation cannot be turned off under an isolated run",{param:"isolated"});if(n.isolated=$||e.isolated===!0,_!==null){const t=c??_;if(c&&_.grantee&&c.grantee&&c.grantee!==_.grantee)throw new g(403,"other_app_run","cannot create under another app's node");const i=c&&c!==_?[_,c]:[_],d=[],w=new Set;for(const o of i){o.scopes!==null&&o.scopes!==void 0&&d.push(o.scopes);const m=o.grantee==="app:terminal"?await k():o.grant_id?await B(o.grant_id):null;if(m){const R=[...m.resolved];w.add(R),d.push(R)}else if(!(o.grantee==="app:terminal"&&!S("user").runtime?.issuer)){const R=[];w.add(R),d.push(R)}}const E=l(o=>Q(o,t.grantee==="user:local"?"app:terminal":t.grantee??"app:terminal"),"ownMemory"),F=l((o,m)=>E(m)&&w.has(o)||C(o,m),"allowed");if(y!==void 0&&!y.every(o=>d.every(m=>F(m,o))))throw new T({error:"unknown_scope",message:"child scopes must be covered by the creating node, its source and their current grants",received:y,field:"scopes",next:"Request only scopes the creating node currently holds."});if(n.scopes=y??(i.every(o=>o.scopes==null)&&i.every(o=>o.grant_id===t.grant_id&&o.sub===t.sub)?null:i.length===1?_.scopes??null:[...new Set(d.flat().filter(o=>d.every(m=>F(m,o))))]),n.scopes?.includes("crtr:act")&&!C(n.scopes,"crtr:llm"))throw new T({error:"invalid_scope",message:"crtr:act requires crtr:llm",received:n.scopes,field:"scopes",next:"Include crtr:llm or omit crtr:act."});n.grantee=t.grantee==="user:local"?"app:terminal":t.grantee,n.sub=t.sub??(n.grantee==="app:terminal"?h?.sub:null)??null,n.grant_id=t.grant_id??(n.grantee==="app:terminal"?h?.id:null)??null,n.client_id=t.client_id,n.run_id=s?r:t.run_id??null}if(re(n.grantee),e.run===void 0&&n.grantee==="app:terminal"&&S("user").runtime?.issuer&&h===null)throw await ee()?new g(503,"runtime_starting","the first grant snapshot from the directory has not committed"):new g(403,"scope_missing","terminal grant is not active");if(n.isolated){if(process.platform!=="linux"||!P(H))throw new g(400,"isolated_unavailable","isolated nodes require the Linux root launcher");if(e.managedWorktree!==void 0)throw new g(400,"invalid_request","isolated nodes cannot use a worktree",{param:"worktree"});const t=oe(se(n.grantee??"app:terminal").nodes,r),i=ae(t,ie(n.cwd));if(!U(n.cwd)||i===".."||i.startsWith(`..${le}`)||U(i))throw new g(400,"invalid_request","isolated node cwd must be inside its own folder",{param:"cwd"})}if(n.profile_id){const t=Re(n.profile_id).manifest.grantee??"app:terminal";if(t!==n.grantee){const i=`crtr:memory:rw:${t}:profile:${n.profile_id}`,d=n.grantee==="app:terminal"?await k():n.grant_id?await B(n.grant_id):null;if(!(n.grantee==="app:terminal"&&!S("user").runtime?.issuer)&&(!d||d.grantee!==n.grantee||!C(d.resolved,i))||!C(n.scopes,i))throw new T({error:"scope_missing",message:`missing scope ${i}`,received:n.profile_id,field:"profile",next:"Select a profile covered by this run and its current grant."})}}if(ve(n.profile_id??null),!n.grantee)throw new Error("node creation requires an app");const G=await ye({kind:n.kind,mode:n.mode,cwd:n.cwd,profile:n.profile_id??null,root:s,parentId:a,requestingNodeId:p,lifecycle:N,replaces:e.replaces??null},n.grantee),J=l(async(t,i)=>{const d=await I(t,i.node_id),w=p??a??e.actor??f??null,E=w!==null&&(await Ee(t,w))?.type==="node"?await I(t,w):await Ce(t,e.run===void 0?"user:local":i.grantee);await Ie(t,E,d),a!==null&&e.subscribe!==!1&&await Ne(t,await I(t,a),d,"spawn"),e.forkSourceNode&&await ke(t,await I(t,e.forkSourceNode),d),await Se(t,{event:"created",object:d,actor:E,attrs:{name:i.name,kind:i.kind,creator:p??null}})},"recordBirth");try{e.nodeId!==void 0&&await $e(e.nodeId),await e.beforeCreate?.(),(process.platform!=="linux"||P(H))&&await pe(n.grantee??"app:terminal"),await xe(n.cwd,r,()=>ce(n,{outcomeDelivery:e.outcomeDelivery,withinCreate:l(async(t,i)=>{i.run_id&&i.run_id!==i.node_id&&await ue(t,i.run_id),s&&e.run===void 0&&await ne(t,r,r,"run.started",{scopes:i.scopes??null,payer:"user",usage:JSON.parse(i.usage_json)}),await e.withinCreate?.(t,i),await J(t,i)},"withinCreate")}))}finally{G()}return n}l(Cn,"spawnNode");export{q as NodeIdConflictError,yn as currentNodeContext,Be as forkSourceNode,Fe as inboxBetweenTurns,be as newJobId,Ae as newNodeId,En as nodeEnv,vn as preflightNodeId,Rn as rootOfSpine,hn as setInstallId,Cn as spawnNode};
|
|
@@ -85,7 +85,7 @@ export type ReviveCapacityPolicy = 'freeze' | 'refuse';
|
|
|
85
85
|
* died or was torn down — the daemon's respawn after a broker exit, and the
|
|
86
86
|
* `reviveAll` sweep over disconnected nodes. Nothing external arrived for the
|
|
87
87
|
* node, so such a launch is NOT a wake and does not consume the node's armed
|
|
88
|
-
* deadline (see the
|
|
88
|
+
* deadline (see the consumeDeadlineOnWake call below). A viewer attach (the
|
|
89
89
|
* `viewerAttach` flag {@link decideAttachRevive} sets) is not a wake either.
|
|
90
90
|
*
|
|
91
91
|
* Throws if the node does not exist. All other failures propagate as-is —
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var Y=Object.defineProperty;var a=(e,r)=>Y(e,"name",{value:r,configurable:!0});import{assertGranteeRunsHere as q}from"./unsandboxed.js";import{existsSync as B}from"node:fs";import{conversationIdFromPath as h,conversationStore as d,ConversationNotFoundError as
|
|
1
|
+
var Y=Object.defineProperty;var a=(e,r)=>Y(e,"name",{value:r,configurable:!0});import{assertGranteeRunsHere as q}from"./unsandboxed.js";import{existsSync as B}from"node:fs";import{conversationIdFromPath as h,conversationStore as d,ConversationNotFoundError as D}from"../conversation-store/index.js";import{isAbsolute as Q}from"node:path";import{findNodeBySessionFile as Z,getNode as R,updateNode as E,beginExecution as I,recordExecutionPid as ee,abortExecution as S,endLiveExecution as re,unbindSession as O,recordLaunch as ie,setFrozen as te,clearFrozen as ne,fullName as T}from"../canvas/index.js";import{emitEvent as oe}from"../events/emit.js";import{consumeDeadlineOnWake as se}from"../feed/messages.js";import{isParked as ae,transition as x}from"./lifecycle.js";import{publishNotice as le}from"./close.js";import{InputError as $}from"../io.js";import{buildPiArgv as p}from"./launch.js";import{buildReviveKickoff as ce,drainBearings as ue,hasRetainedReports as fe,readGoal as k,readYieldMessage as we,RUNTIME_RESTART_CONTINUATION as de}from"./kickoff.js";import{readRoadmap as z}from"./roadmap.js";import{encodeKickoffOrigin as he,KICKOFF_ORIGIN_ENV as pe}from"./stamp/protocol.js";import{headlessBrokerHost as H}from"./host.js";import{appMemoryFull as ve,appMemoryTooSmall as me,boundFleet as M,brokerCapReached as _e,brokerThresholdsForDaemon as U}from"./fleet.js";import{admitBrokerMemory as ye}from"./memory-admission.js";import{beginBootFaultAttempt as ke,clearFault as ge,hasAdmittedTurn as be}from"../canvas/recovery.js";import{clearNodeLedger as K,forkNodeLedger as Re,noticeChangedWhileDone as Ee}from"./ledger.js";import{forkSourceNode as Se,rootOfSpine as xe}from"./nodes.js";import{isReviewCompanionBound as $e}from"../review/companion.js";import{assertProfileActive as Ne}from"../profiles/manifest.js";import{isPidAlive as Ce}from"../canvas/pid.js";import{crtrHome as Fe,jobDir as Pe}from"../canvas/paths.js";import{contextDir as Ae,ensureNodeDirs as Be,nodeAgentDir as De}from"../canvas/node-agent-paths.js";import{discoverLifecycleHookRegistry as Oe}from"../command-hooks/discovery.js";import{invokeLifecycleHooks as Te}from"../command-hooks/transport/exec-lifecycle.js";async function ze(e,r){const i=e.pi_session_file,t=i?h(i):null;if(t===null)return{};try{const n=await d().header(t);return r?{resumeSessionPath:n.path}:{resumeSessionPath:n.path,newCycle:!0}}catch(n){if(n instanceof D)return{};throw n}}a(ze,"resumeArgs");async function G(e){const r=h(e);if(r===null)return!1;try{return await d().header(r),!0}catch(i){if(i instanceof D)return!1;throw i}}a(G,"conversationExists");async function He(e){const r=h(e);return r===null?!0:(await d().read(r)).entries.some(i=>i.type==="message")}a(He,"sessionHasConversation");async function Me(e){const r=h(e);if(r===null)return!0;const i=(await d().header(r)).header.cwd;return typeof i!="string"||B(i)}a(Me,"sessionCwdExists");async function Ue(e,r,i){const t=r.pi_session_file;if(!t||!await G(t))return{};const n=await Z(t);return n!==null&&n.node_id!==e&&Ce(n.pi_pid)?{}:await Me(t)?await ze(r,i):{}}a(Ue,"resumableSessionArgs");async function Ke(e){return await z(e)===null&&await we(e)===null&&!await fe(e)}a(Ke,"isUnstartedBirth");async function _r(e){const r=await k(e);if(r!==null&&r.trim()!=="")return!0;const i=await z(e);return i!==null&&i.trim()!==""}a(_r,"hasFreshGroundState");async function yr(e,r){const i=await L(e,r,!0);if(i.parked)throw new Error(`reviveNode: internal parked decision for activating revive ${e}`);return i.result}a(yr,"reviveNode");async function kr(e,r,i){return L(e,{...r,viewerAttach:!0},i)}a(kr,"decideAttachRevive");async function L(e,r,i){let t=await R(e);if(t===null)throw new Error(`reviveNode: unknown node ${e}`);if(t=await R(e)??t,t.run_id&&(await R(t.run_id))?.deleting_at)throw new Error("run is deleting");if(t.final_report!==null)throw new Error(`reviveNode: refusing to revive ${e} \u2014 its finalization latch is set (final_report=${t.final_report}). A final landed between the caller's reopen-gate check and this revive; re-run with --reopen if this retask is still wanted.`);Ne(t.profile_id),q(t.grantee);const n=M();if(n.has(e))return await x(e,t.intent==="refresh"?"refresh-launch":"revive"),{parked:!1,result:{window:null,session:t.tmux_session??null,resumed:!1,outcome:"already-live"}};if(n.isClaimed(e))return{parked:!1,result:{window:null,session:t.tmux_session??null,resumed:!1,outcome:"already-live"}};if(!i&&t.lifecycle==="resident"&&ae(t))return{parked:!0};const l=n.reserveSlot(e);let s=null;if(l){try{s=await ye(e)}catch(u){throw n.releaseReservation(e),u}s.verdict!=="admitted"&&n.releaseReservation(e)}if(s?.verdict==="too_small")throw me(s);if(!l||s?.verdict==="denied"){if(r.capacity==="refuse")throw s?.verdict==="denied"?ve(s):_e(U().automaticReviveCap,n.size());return t.frozen_at==null&&(await te(e,new Date().toISOString()),await re(e,{reason:"frozen"}),oe({level:"info",event:"broker.frozen",node_id:e,fields:s?.verdict==="denied"?{reason:"app_memory",free_bytes:s.free_bytes,needed_bytes:s.needed_bytes}:{reason:"broker_cap",live_count:n.size(),hard_cap:U().automaticReviveCap}})),{parked:!1,result:{window:null,session:t.tmux_session??null,resumed:!1,outcome:"frozen"}}}try{return{parked:!1,result:await Ge(e,t,r)}}finally{n.releaseReservation(e)}}a(L,"decideRevive");async function Ge(e,r,i){if(!B(r.cwd)){const v=r.fork_from!=null?`Restore ${r.cwd}, then revive the fork normally with \`crtr node lifecycle revive ${e}\`.`:r.managed_worktree?.state==="open"?"Restore or reconcile the recorded checkout, then use the managed-worktree close or abandon recovery.":`Recreate ${r.cwd}, or repair it with \`crtr node config --cwd <path> --node ${e}\`; durable node work resumes in a fresh conversation. Then retry \`crtr node lifecycle revive ${e}\`.`;throw new $({error:"cwd_missing",message:`${e} cannot be revived \u2014 its recorded cwd no longer exists: ${r.cwd}`,next:v})}r.host_kind!=="broker"&&(await E(e,{host_kind:"broker"}),r.host_kind="broker"),r.review_binding!=null&&!await $e(e)&&(r.pi_session_id!=null||r.pi_session_file!=null)&&(await O(e),r.pi_session_id=null,r.pi_session_file=null);const n=r.fork_from!=null&&r.pi_session_id==null,l=r.fork_source_file;if(n&&(typeof l!="string"||!Q(l)))throw new $({error:"fork_source_invalid",message:`${e} cannot be revived \u2014 its resolved fork source file is missing or invalid.`,next:`Restore the node's fork source metadata, then retry \`crtr node lifecycle revive ${e}\`.`});const s=n&&typeof l=="string";if(s&&!await G(l))throw new $({error:"fork_from_missing",message:`${e} cannot be revived \u2014 its fork source no longer exists: ${l}`,next:`Restore ${l}, or update the node's fork source, then retry \`crtr node lifecycle revive ${e}\`.`});const u=await I(e,{daemonEpoch:M().epoch()});try{r.cycles=(r.cycles??0)+1,await E(e,{cycles:r.cycles});const j=r.intent==="refresh"||r.cycle_pending===!0?!1:i.resume,w=s?{}:await Ue(e,r,j),m=w.resumeSessionPath!==void 0,N=m&&w.newCycle===!0,_=m&&!N,C=!m&&(i.wakeReason===void 0||i.wakeReason==="runtime-restart-abort")&&await Ke(e),g=_&&(i.wakeReason===void 0||i.wakeReason==="runtime-restart-abort")&&!await be(e)&&!await He(w.resumeSessionPath)?(await k(e))?.trim():void 0,F=g!==void 0&&g!=="",W=s||C||F,P=r.cycle_pending===!0||N;await E(e,{cycle_pending:P}),r.cycle_pending=P,!s&&!m&&(r.pi_session_id!=null||r.pi_session_file!=null)&&(await O(e),r.pi_session_id=null,r.pi_session_file=null);let c,f=null;const b=a(()=>({type:"runtime",kind:"kickoff",...r.spawned_by!=null&&r.spawned_by!==""?{spawnedBy:r.spawned_by}:{}}),"birthOrigin");if(s){const o=await k(e),A=h(l);if(A===null)throw new Error(`invalid fork source ${l}`);const X=(await d().header(await d().fork(A,e))).path;c=p(r,{resumeSessionPath:X,...o!=null&&o!==""?{prompt:o}:{}}),o!=null&&o!==""&&(f=b()),await Re(e,(await Se(l))?.node_id??null)}else if(F)c=p(r,{...w,prompt:g}),f=b();else if(_)c=p(r,{...w,...i.wakeReason==="runtime-restart-abort"?{prompt:de}:{}}),i.wakeReason==="runtime-restart-abort"&&(f={type:"runtime",kind:"restart-continuation"});else if(C){const o=(await k(e))?.trim();c=p(r,o!==void 0&&o!==""?{prompt:o}:{}),o!==void 0&&o!==""&&(f=b()),await K(e)}else{const o=await ue(r,u);c=p(r,{...w,prompt:await ce(r,o,i.wakeReason==="runtime-restart-abort"?void 0:i.wakeReason)}),r.run_id&&o.unreadMessageIds?.length&&(c.env.CRTR_KICKOFF_MESSAGE_IDS=JSON.stringify(o.unreadMessageIds)),f=i.wakeReason!==void 0&&i.wakeReason!=="runtime-restart-abort"?{type:"runtime",kind:"wake",wake:i.wakeReason}:{type:"runtime",kind:"revive",cycle:r.cycles??0},await K(e)}c.env={...c.env,CRTR_SUBTREE:await xe(e)},f!==null&&(c.env[pe]=he(f)),r.status==="done"&&r.last_settled_at!=null&&await Ee(e,r.last_settled_at),await x(e,r.intent==="refresh"?"refresh-launch":"revive"),await ge(e,{preserveEpisode:!0});const J=await ke(e);i.recovery!==!0&&i.viewerAttach!==!0&&await se(e),await Be(e,r.grantee);const V=Oe(r.cwd,r.profile_id).plans.get("node:start")??[];await Te(V,{node:{id:e,name:r.name,kind:r.kind,mode:r.mode,lifecycle:r.lifecycle,cwd:r.cwd,nodeDir:await De(e),contextDir:await Ae(e),jobDir:Pe(e)},runtime:{isBirth:W,canvasHome:Fe(),profile:r.profile_id??null}}),c.env={...c.env,CRTR_EXECUTION_ID:u};let y;try{if(y=await H.launch(e,c,{cwd:r.cwd,name:T(r),resuming:_}),y.pid===null)throw new Error(`broker host returned no pid for ${e}`)}catch(o){try{await x(e,"crash",{reason:"launch_failed",outcome:{detail:{schema:"crtr.node-outcome-detail/v1",message:o instanceof Error?o.message:String(o)}}}),await le(e,"notice.crash",`Child crashed \u2014 ${T(r)} (${e}) failed to relaunch and is now dead: ${o.message}
|
|
2
2
|
|
|
3
3
|
It stays dead until you revive it \u2014 \`crtr node lifecycle revive ${e}\`.`,{reason:"child-crashed",child:e})}catch{}throw await S(u,{reason:"launch_failed"}),new Error(`[broker] refusing to revive ${e}: ${o.message}`)}if(await ie(e,new Date().toISOString()),await ee(u,e,y.pid)==="shutdown")throw await S(u,{reason:"superseded"}),H.teardown(e),new Error(`[broker] refusing to revive ${e}: execution was superseded during launch`);return await ne(e),{window:null,session:r.tmux_session??null,resumed:_,launch:y,faultExplanation:J.explanation,outcome:"launched"}}catch(v){throw await S(u,{reason:"launch_failed"}),v}}a(Ge,"launchRevive");export{kr as decideAttachRevive,_r as hasFreshGroundState,ze as resumeArgs,yr as reviveNode};
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { type ManagedWorktree } from './canvas/index.js';
|
|
2
2
|
import { type AbandonManagedWorktreeResult, type AutoDroppedWorktree } from './worktree.js';
|
|
3
|
+
/** Run `admit` — the step that persists a node whose cwd is `cwd` — under the
|
|
4
|
+
* repository lock the cleanup sweep removes checkouts under, when `cwd` is in
|
|
5
|
+
* another node's managed checkout. The repository comes from that owner's
|
|
6
|
+
* recorded managed worktree, not from the checkout on disk, so the lock is
|
|
7
|
+
* still taken after the sweep has removed the checkout. The sweep decides to
|
|
8
|
+
* remove a checkout and removes it inside that lock, and a node admitted inside
|
|
9
|
+
* the same lock is either seen by that decision or finds the checkout already
|
|
10
|
+
* gone and is refused here, instead of being persisted with a cwd that no
|
|
11
|
+
* longer exists. */
|
|
12
|
+
export declare function admitIntoManagedCheckout<T>(cwd: string, nodeId: string, admit: () => Promise<T>): Promise<T>;
|
|
3
13
|
export declare function rollbackManagedWorktreeAsync(wt: ManagedWorktree): Promise<void>;
|
|
4
14
|
export declare function createManagedWorktreeAsync(cwd: string, nodeId: string, baseRef?: string): Promise<ManagedWorktree>;
|
|
5
15
|
export interface AbsentWorktreeConclusion {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
var
|
|
2
|
-
stderr: ${n.stderr.trim()||"<empty>"}`);return h(e,["rev-parse","--verify",
|
|
3
|
-
`).map(l=>l.trim()).includes(s)))return;
|
|
1
|
+
var O=Object.defineProperty;var o=(e,a)=>O(e,"name",{value:a,configurable:!0});import{existsSync as p,mkdirSync as B,readFileSync as q,realpathSync as I,rmSync as N,writeFileSync as j}from"node:fs";import{basename as G,dirname as K,isAbsolute as z,join as x,relative as J,resolve as Q,sep as E}from"node:path";import{abandonWorktreeAndRepoint as U,getNode as m,readWorktree as V}from"./canvas/index.js";import{isPidAlive as X}from"./canvas/pid.js";import{InputError as Y}from"./io.js";import{CONFIG_FILE as Z,CRTR_DIR_NAME as S,STATE_FILE as ee}from"../types.js";import{withExclusiveDirectoryLockAsync as W}from"./exclusive-lock.js";import{gitAsync as i}from"./git.js";import{WORKTREE_LOCK_WAIT_MS as te,canonicalPath as D,checkoutPathForBranchInList as re,isManagedWorktreeBase as ae,managedWorktreesRoot as ne,managedWorktreePath as oe,persistClosedWorktreeState as k,worktreeLockPathForCommonDir as M,WorktreeError as c}from"./worktree.js";import{proveBranchContained as ie}from"./worktree-containment.js";import{refDeleteTransactionStdin as se}from"./worktree-landing.js";import{deleteTrashAsync as ce,removeWorktreeCheckoutAsync as he}from"./worktree-remove.js";const C=o(()=>Math.min(te,25e3),"lockWaitMs");function v(e){return(e.stderr.trim()||e.stdout.trim()).trim()}o(v,"detailOf");async function h(e,a,r,n){const t=await i(a,e);if(t.status!==0)throw new c(r,`git ${a.join(" ")} failed`,n,v(t));return t.stdout.trim()}o(h,"runGit");async function L(e){return I(Q(e,await h(e,["rev-parse","--git-common-dir"],"git_common_dir_failed","Inspect the repository git metadata, then retry.")))}o(L,"commonGitDir");async function _(e,a){const r=await L(e);return W(M(r),a,{timeoutMs:C(),timeoutError:o(()=>new c("worktree_lock_timeout",`timed out waiting for the managed-worktree lock in ${r}`,"Wait for the other managed-worktree operation to finish, then retry."),"timeoutError")})}o(_,"withRepositoryLock");function ue(e,a){const r=D(ne()),n=J(r,D(e));if(n===""||n===".."||n.startsWith(`..${E}`)||z(n))return null;const t=n.split(E)[0];return t===a?null:x(r,t)}o(ue,"foreignManagedCheckout");async function Me(e,a,r){const n=ue(e,a);if(n===null)return r();const t=await V(G(n));return t===null?r():_(t.repo_root,async()=>{if(!p(e))throw new Y({error:"cwd_missing",message:`cwd is not an existing directory: ${e}`,received:e,field:"cwd",next:"Pass an existing directory; the managed checkout it was in has been cleaned up."});return r()})}o(Me,"admitIntoManagedCheckout");async function $(e,a){const r=`refs/heads/${a}`,n=await i(["show-ref","--verify","--quiet",r],e);if(n.status===1&&n.executionFailed!==!0)throw new c("missing_base_branch",`managed worktree base '${a}' does not name an existing local branch`,"The base must be a local branch that landing can fast-forward \u2014 freshen your local branch from origin first (rebase-not-merge), then base on it.");if(n.status!==0)throw new c("base_branch_inspection_failed",`could not inspect local base branch '${a}' (git show-ref exited with status ${n.status})`,"Inspect the repository and its Git metadata, then retry.",`status: ${n.status}
|
|
2
|
+
stderr: ${n.stderr.trim()||"<empty>"}`);return h(e,["rev-parse","--verify",r],"base_branch_resolve_failed","Inspect the local base branch, then retry.")}o($,"resolveLandableBase");function le(e,a){try{const r=x(a,"info","exclude");B(K(r),{recursive:!0});let n="";try{n=q(r,"utf8")}catch{}const t=[`/${S}/${Z}`,`/${S}/${ee}`];if(t.every(s=>n.split(`
|
|
3
|
+
`).map(l=>l.trim()).includes(s)))return;j(r,`${n}${n!==""&&!n.endsWith(`
|
|
4
4
|
`)?`
|
|
5
5
|
`:""}# crtr: exclude crtr's own untracked project-scope bookkeeping (auto-added at managed-worktree creation)
|
|
6
6
|
${t.filter(s=>!n.split(`
|
|
7
7
|
`).map(l=>l.trim()).includes(s)).join(`
|
|
8
8
|
`)}
|
|
9
|
-
`)}catch{}}o(
|
|
10
|
-
`).some(n=>n.trim()===`branch refs/heads/${e.branch}`)){if((await i(["worktree","remove","--force",e.path],e.repo_root)).status!==0)return}else if(p(e.path))try{
|
|
9
|
+
`)}catch{}}o(le,"seedExclude");async function Ce(e){await _(e.repo_root,async()=>{const a=await i(["rev-parse","--verify",`refs/heads/${e.branch}`],e.repo_root);if(a.status!==0||a.stdout.trim()!==e.base_sha)return;const r=await i(["worktree","list","--porcelain"],e.repo_root);if(r.status===0){if(r.stdout.split(`
|
|
10
|
+
`).some(n=>n.trim()===`branch refs/heads/${e.branch}`)){if((await i(["worktree","remove","--force",e.path],e.repo_root)).status!==0)return}else if(p(e.path))try{N(e.path,{recursive:!0,force:!0})}catch{return}await i(["update-ref","-d",`refs/heads/${e.branch}`,e.base_sha],e.repo_root)}})}o(Ce,"rollbackManagedWorktreeAsync");async function Le(e,a,r="main"){const n=await h(e,["rev-parse","--show-toplevel"],"not_git_repo","Pass --cwd <real repo> pointing inside a git repository.");if(ae(n))throw new c("managed_worktree_base",`cwd ${n} is a managed worktree and cannot be used as a managed worktree base`,"Pass `--cwd` pointing at the repository\u2019s primary checkout and keep `--worktree` set to the desired existing local base branch, including the parent branch.");const t=await L(n);return W(M(t),async()=>{const s=await $(n,r),l=oe(a),u=`crtr/${a}`;if(p(l))throw new c("worktree_path_exists",`managed worktree path already exists: ${l}`,"Remove the stale path or choose a fresh node id.");if((await i(["show-ref","--verify","--quiet",`refs/heads/${u}`],n)).status===0)throw new c("worktree_branch_exists",`managed worktree branch already exists: ${u}`,"Remove the stale branch or choose a fresh node id.");const b={state:"open",path:l,branch:u,repo_root:n,base_ref:r,base_sha:s,created:new Date().toISOString()};await h(n,["branch",u,s],"worktree_branch_create_failed","Inspect the git error, fix the repository/worktree state, then retry spawning with --worktree.");const f=await i(["worktree","add",l,u],n);if(f.status!==0)throw await i(["update-ref","-d",`refs/heads/${u}`,s],n),new c("worktree_add_failed","git worktree add failed","Inspect the git error, fix the repository/worktree state, then retry spawning with --worktree.",v(f));return le(n,t),b},{timeoutMs:C(),timeoutError:o(()=>new c("worktree_lock_timeout",`timed out waiting to create a managed worktree in ${t}`,"Wait for the other managed-worktree operation to finish, then retry."),"timeoutError")})}o(Le,"createManagedWorktreeAsync");async function P(e){if(e.state!=="open"||!p(e.path))return null;const a=await i(["rev-parse","--abbrev-ref","HEAD"],e.path),r=await i(["status","--porcelain"],e.path),n=await i(["rev-parse","HEAD"],e.path);if(a.status!==0||a.stdout.trim()!==e.branch||r.status!==0||r.stdout.trim()!==""||n.status!==0)return null;const t=await i(["rev-parse","--git-path","rebase-merge"],e.path);return t.status!==0||p(t.stdout.trim())?null:n.stdout.trim()}o(P,"cleanTip");async function F(e,a){try{const r=await $(e.path,e.base_ref);return(await i(["merge-base","--is-ancestor",a,r],e.path)).status===0}catch(r){if(r instanceof c&&r.code==="missing_base_branch"&&a===e.base_sha)return!0;throw r}}o(F,"containedInLocalBase");function de(e){return{node_id:"",branch:e.branch,worktree_path:e.path,worktree_removed:!1,worktree_remove_error:`cleanup pending: the caller checkout at ${e.path} remains intact.`,branch_deleted:!1,branch_delete_error:`cleanup pending: local branch ${e.branch} remains checked out at ${e.path}.`}}o(de,"deferred");async function R(e){if(e.state!=="open"||p(e.path))return null;const a=`refs/heads/${e.branch}`,r=await i(["show-ref","--verify","--quiet",a],e.repo_root);if(r.status===1&&r.executionFailed!==!0)return{branchSha:null};if(r.status!==0)return null;const n=await i(["rev-parse","--verify",a],e.repo_root);if(n.status!==0)return null;const t=n.stdout.trim(),s=await i(["worktree","list","--porcelain"],e.repo_root);if(s.status!==0||re(s.stdout,e.branch)!==null)return null;const l=await ie(e.repo_root,e,t);return l===null?null:{branchSha:t,containment:l}}o(R,"proveAbsentPathManagedWorktree");async function pe(e,a,r){return p(a.path)?null:r.branchSha===null?(await k(e,a,"complete"),{node_id:e,branch:a.branch,worktree_path:a.path,worktree_removed:!0,branch_deleted:!0}):r.containment===void 0?null:(await k(e,a,"complete"),{node_id:e,branch:a.branch,worktree_path:a.path,worktree_removed:!0,branch_deleted:!1,branch_delete_error:`local branch ${a.branch} is delivered but retained because its checkout status can change outside crtr's repository lock.`})}o(pe,"autoDropAbsentPathManagedWorktreeLocked");async function fe(e){const a=(await m(e))?.managed_worktree;return a==null||a.state!=="open"||p(a.path)||await R(a)===null?null:_(a.repo_root,async()=>{const r=(await m(e))?.managed_worktree;if(r==null||r.state!=="open")return null;const n=await R(r);if(n===null)return null;const t=await pe(e,r,n);return t===null?null:{dropped:t,landedSha:n.branchSha??r.base_sha}})}o(fe,"concludeAbsentManagedWorktreeAsync");async function Pe(e){const a=(await m(e))?.managed_worktree;if(a==null||a.state!=="open")return null;if(!p(a.path))return(await fe(e))?.dropped??null;const r=await P(a);return r===null||!await F(a,r)?null:_(a.path,async()=>{const n=(await m(e))?.managed_worktree;if(n==null||n.state!=="open")return null;const t=await P(n);return t===null||t!==r||!await F(n,t)?null:(await k(e,n,"pending"),{...de(n),node_id:e})})}o(Pe,"autoDropCleanManagedWorktreeAsync");async function T(e){for(const a of["rebase-merge","rebase-apply"]){const r=await i(["rev-parse","--git-path",a],e);if(r.status!==0||p(r.stdout.trim()))return!0}return!1}o(T,"rebaseInProgress");async function y(e,a,r,n,t,s=a.branch){await U(e,{cwd:a.repo_root,abandoned:{branch:s,tip_sha:r,at:n,by:t}})}o(y,"persistAbandoned");async function Fe(e,a){const r=(await m(e))?.managed_worktree;if(r==null||r.state!=="open")throw new c("no_managed_worktree",`node ${e} has no open managed worktree`,"Nothing to abandon for this node.");const n=[];try{return await _(r.path,()=>we(e,a,n))}finally{await ce(r.path,n[0])}}o(Fe,"abandonManagedWorktreeAsync");async function g(e,a){const{result:r,trash:n}=await he(e.repo_root,e.path);if(n!==void 0&&a.push(n),r.status!==0)throw new c("worktree_remove_failed",`git worktree remove ${e.path} failed`,"Inspect the worktree manually, then retry.",v(r))}o(g,"removeCheckout");async function we(e,a,r){const n=await m(e),t=n?.managed_worktree;if(n===null||t===void 0||t===null||t.state!=="open")throw new c("no_managed_worktree",`node ${e} has no open managed worktree`,"Nothing to abandon for this node.");if(n.status==="active"||n.status==="idle"||X(n.pi_pid))throw new c("node_running",`node ${e} is still running`,"Abandon requires a stopped node with no live broker process. From the running owner, use `crtr node worktree close` to land locally, or make the checkout clean, push its current tip to origin, and rerun `crtr push final`.");if(!p(t.path))throw new c("worktree_missing",`managed worktree path is missing: ${t.path}`,"Inspect the branch and worktree registration, then retry.");const s=await h(t.path,["rev-parse","--abbrev-ref","HEAD"],"branch_check_failed","Inspect the worktree manually, then retry."),l=await i(["show-ref","--verify","--quiet",`refs/heads/${t.branch}`],t.path);if(s!==t.branch){if(l.status===0||s==="HEAD")throw new c("wrong_branch",`managed worktree is not on its recorded branch (expected ${t.branch}, found ${s==="HEAD"?"detached HEAD":s})`,`Inspect ${t.path}, then rerun \`crtr node worktree abandon\`.`);if(await T(t.path)||await h(t.path,["status","--porcelain"],"status_failed","Inspect the worktree manually, then retry.")!=="")throw new c("dirty_worktree","managed worktree has uncommitted changes",`Commit or stash them in ${t.path}, then rerun \`crtr node worktree abandon\`.`);const d=await h(t.path,["rev-parse","HEAD"],"abandon_tip_failed","Inspect the worktree manually, then retry.");await g(t,r);const w=new Date().toISOString();return await y(e,t,d,w,a,s),{node_id:e,branch:s,tip_sha:d,worktree_path:t.path,branch_deleted:!1}}const u=await h(t.path,["rev-parse","HEAD"],"abandon_tip_failed","Inspect the branch manually, then retry.");let b=null;try{const d=await $(t.path,t.base_ref);(await i(["merge-base","--is-ancestor",u,d],t.path)).status===0&&(b=d)}catch(d){if(!(d instanceof c)||d.code!=="missing_base_branch")throw d}const f=new Date().toISOString(),H=await h(t.path,["status","--porcelain"],"status_failed","Inspect the worktree manually, then retry.");if(b!==null&&u===t.base_sha){const d=await h(t.repo_root,["rev-parse",`${u}^{tree}`],"abandon_tree_failed","Inspect the branch manually, then retry."),w=await h(t.repo_root,["-c","user.name=crtr-abandon","-c","user.email=crtr-abandon@localhost","commit-tree",d,"-p",u,"-m","crtr-abandon","-m",`Crtr-Abandon: ${e} ${f}`],"abandon_commit_failed","Inspect the branch manually, then retry.");return await h(t.repo_root,["update-ref",`refs/heads/${t.branch}`,w,u],"abandon_marker_failed","The branch changed while abandon was starting; inspect the worktree, then retry."),await g(t,r),(await i(["update-ref","--stdin"],t.repo_root,se(t.branch,w,`refs/heads/${t.base_ref}`,b))).status===0?(await k(e,t,"complete"),{node_id:e,branch:t.branch,tip_sha:w,worktree_path:t.path,branch_deleted:!0}):(await y(e,t,w,f,a),{node_id:e,branch:t.branch,tip_sha:w,worktree_path:t.path,branch_deleted:!1})}if(!await T(t.path)&&H==="")return await g(t,r),await y(e,t,u,f,a),{node_id:e,branch:t.branch,tip_sha:u,worktree_path:t.path,branch_deleted:!1};await h(t.path,["add","--all"],"abandon_stage_failed","Inspect the worktree manually, then retry."),await h(t.path,["-c","user.name=crtr-abandon","-c","user.email=crtr-abandon@localhost","commit","--allow-empty","-m","crtr-abandon","-m",`Crtr-Abandon: ${e} ${f}`],"abandon_commit_failed","Inspect the worktree manually, then retry.");const A=await h(t.path,["rev-parse","HEAD"],"abandon_tip_failed","Inspect the branch manually, then retry.");return await g(t,r),await y(e,t,A,f,a),{node_id:e,branch:t.branch,tip_sha:A,worktree_path:t.path,branch_deleted:!1}}o(we,"abandonLocked");export{Fe as abandonManagedWorktreeAsync,Me as admitIntoManagedCheckout,Pe as autoDropCleanManagedWorktreeAsync,fe as concludeAbsentManagedWorktreeAsync,Le as createManagedWorktreeAsync,Ce as rollbackManagedWorktreeAsync};
|