pi-daddy 0.23.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +20 -6
  3. package/contracts/ledger/v4/README.md +1 -1
  4. package/dist/cli.d.ts +3 -1
  5. package/dist/cli.d.ts.map +1 -1
  6. package/dist/cli.js +49 -3
  7. package/dist/cli.js.map +1 -1
  8. package/dist/daily-dashboard-host.d.ts +123 -0
  9. package/dist/daily-dashboard-host.d.ts.map +1 -0
  10. package/dist/daily-dashboard-host.js +141 -0
  11. package/dist/daily-dashboard-host.js.map +1 -0
  12. package/dist/dashboard-cli.d.ts.map +1 -1
  13. package/dist/dashboard-cli.js +9 -2
  14. package/dist/dashboard-cli.js.map +1 -1
  15. package/dist/dashboard-handshake.d.ts.map +1 -1
  16. package/dist/dashboard-handshake.js +13 -4
  17. package/dist/dashboard-handshake.js.map +1 -1
  18. package/dist/dashboard-harness.d.ts +3 -0
  19. package/dist/dashboard-harness.d.ts.map +1 -1
  20. package/dist/dashboard-harness.js +15 -1
  21. package/dist/dashboard-harness.js.map +1 -1
  22. package/dist/dashboard-herdr.d.ts +1 -0
  23. package/dist/dashboard-herdr.d.ts.map +1 -1
  24. package/dist/dashboard-herdr.js +10 -8
  25. package/dist/dashboard-herdr.js.map +1 -1
  26. package/dist/dashboard-host-contract.d.ts +15 -1
  27. package/dist/dashboard-host-contract.d.ts.map +1 -1
  28. package/dist/dashboard-host-contract.js +1 -1
  29. package/dist/dashboard-host-contract.js.map +1 -1
  30. package/dist/dashboard-host-transport.d.ts +1 -0
  31. package/dist/dashboard-host-transport.d.ts.map +1 -1
  32. package/dist/dashboard-host-transport.js +5 -3
  33. package/dist/dashboard-host-transport.js.map +1 -1
  34. package/dist/dashboard-host.d.ts +62 -0
  35. package/dist/dashboard-host.d.ts.map +1 -1
  36. package/dist/dashboard-host.js +71 -6
  37. package/dist/dashboard-host.js.map +1 -1
  38. package/dist/dashboard-observation.d.ts.map +1 -1
  39. package/dist/dashboard-observation.js +6 -5
  40. package/dist/dashboard-observation.js.map +1 -1
  41. package/dist/experiment-store.d.ts +3 -1
  42. package/dist/experiment-store.d.ts.map +1 -1
  43. package/dist/experiment-store.js +20 -3
  44. package/dist/experiment-store.js.map +1 -1
  45. package/dist/experiment.d.ts.map +1 -1
  46. package/dist/experiment.js +9 -4
  47. package/dist/experiment.js.map +1 -1
  48. package/dist/index.d.ts +3 -0
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +3 -0
  51. package/dist/index.js.map +1 -1
  52. package/dist/intent-application.d.ts.map +1 -1
  53. package/dist/intent-application.js +3 -2
  54. package/dist/intent-application.js.map +1 -1
  55. package/dist/measured-order.d.ts +249 -0
  56. package/dist/measured-order.d.ts.map +1 -0
  57. package/dist/measured-order.js +251 -0
  58. package/dist/measured-order.js.map +1 -0
  59. package/dist/measured-session.d.ts +97 -0
  60. package/dist/measured-session.d.ts.map +1 -0
  61. package/dist/measured-session.js +134 -0
  62. package/dist/measured-session.js.map +1 -0
  63. package/dist/work-command.d.ts +32 -0
  64. package/dist/work-command.d.ts.map +1 -0
  65. package/dist/work-command.js +161 -0
  66. package/dist/work-command.js.map +1 -0
  67. package/extensions/daily-dashboard-session.ts +31 -0
  68. package/extensions/daily-work-session.ts +20 -0
  69. package/extensions/delegation.ts +21 -8
  70. package/extensions/execute-child.ts +10 -1
  71. package/extensions/executor-session.ts +9 -0
  72. package/extensions/grants-command.ts +16 -15
  73. package/extensions/grants-connected-command.ts +19 -0
  74. package/extensions/grants.ts +25 -3
  75. package/extensions/primary-shadow.ts +11 -0
  76. package/extensions/run-delegation.ts +2 -0
  77. package/extensions/session.ts +7 -19
  78. package/extensions/work-runtime.ts +44 -0
  79. package/package.json +13 -1
  80. package/src/cli.ts +41 -4
  81. package/src/daily-dashboard-host.ts +88 -0
  82. package/src/dashboard-cli.ts +7 -2
  83. package/src/dashboard-handshake.ts +14 -5
  84. package/src/dashboard-harness.ts +10 -1
  85. package/src/dashboard-herdr.ts +12 -9
  86. package/src/dashboard-host-contract.ts +8 -2
  87. package/src/dashboard-host-transport.ts +8 -6
  88. package/src/dashboard-host.ts +37 -9
  89. package/src/dashboard-observation.ts +4 -2
  90. package/src/experiment-store.ts +11 -3
  91. package/src/experiment.ts +8 -3
  92. package/src/index.ts +17 -0
  93. package/src/intent-application.ts +3 -2
  94. package/src/measured-order.ts +67 -0
  95. package/src/measured-session.ts +70 -0
  96. package/src/work-command.ts +185 -0
@@ -17,12 +17,19 @@ const hosts=new WeakSet<object>();
17
17
  export const isDashboardHost=(value:unknown):value is DashboardHost=>typeof value==="object"&&value!==null&&hosts.has(value);
18
18
  export type DashboardHost=ReturnType<typeof openDashboardHost>;
19
19
  export interface DashboardHostOptions { ordinary?:OrdinaryChildren; harness:DashboardHarness; config:DashboardHostConfig; budget:GovernedBudgetBinding; experiment?:ReturnType<typeof openExperiment>; authority:()=>DashboardHostAuthority|null;
20
+ /** Exact actions constructed and authorized by the owner; the dashboard exposes only human labels/keys. */
21
+ humanActions?:(context:{hostDigest:string;selectionDigest:string;tip:string;observations:readonly {sourceId:string;checkpointId:string}[];preparedPresentationDigest:string|null})=>readonly {key:string;label:string;request:DashboardHostRequest}[]|Promise<readonly {key:string;label:string;request:DashboardHostRequest}[]>;
22
+ /** Explicit approved observation effect only; refresh/frame never invokes it. */
23
+ beforeObservation?:(sourceId:string)=>Promise<void>;
24
+ /** Owner callback after the exact intent journal reports applied; failure keeps host acknowledgement unknown. */
25
+ afterIntentApplied?:(selection:IntentRequest["selection"])=>void|Promise<void>;
20
26
  /** Independently sourced current declaration. Never inferred from idle, a PID, title or request fields. */
21
27
  presence?:()=>{present:boolean;closing:boolean;evidenceDigest:string;expiresAt:number}|null;
22
28
  }
23
29
  function configuration(input:DashboardHostConfig){
24
- const c=detached(input);controlShape(c,["version","trustDirectory","trustPolicyId","archiveRoot","scope","author","policyPath","policySha256","sources","selection","cases","blind","budgetDigest","experimentDigest","harnessArtifactDigest",...(Object.hasOwn(c,"ordinaryDigest")?["ordinaryDigest"]:[])]);
30
+ const c=detached(input);controlShape(c,["version","trustDirectory","trustPolicyId","archiveRoot","scope","author","policyPath","policySha256","sources","selection","cases","blind","budgetDigest","experimentDigest","harnessArtifactDigest",...(Object.hasOwn(c,"ordinaryDigest")?["ordinaryDigest"]:[]),...(Object.hasOwn(c,"learningLifecycleId")?["learningLifecycleId"]:[])]);
25
31
  if(Object.hasOwn(c,"ordinaryDigest")&&!sha(c.ordinaryDigest))throw Error("invalid ordinary binding digest");
32
+ if(Object.hasOwn(c,"learningLifecycleId")&&!sha(c.learningLifecycleId))throw Error("invalid learning lifecycle manifest");
26
33
  if(c.version!=="producer-dashboard-host-v1"||![c.trustDirectory,c.archiveRoot,c.policyPath].every(p=>typeof p==="string"&&isAbsolute(p))||![c.trustPolicyId,c.policySha256,c.budgetDigest,c.harnessArtifactDigest].every(sha)||!(c.experimentDigest===null||sha(c.experimentDigest))||typeof c.scope!=="string"||!c.scope||c.scope.length>128||typeof c.author!=="string"||!c.author||c.author.length>256||!Array.isArray(c.sources)||c.sources.length<1||c.sources.length>32||new Set(c.sources.map(s=>s.id)).size!==c.sources.length||c.sources.filter(s=>s.kind==="work").length>1||c.sources.some(s=>!/^[a-zA-Z0-9:_-]{1,128}$/.test(s.id)||!["work","retention","facts"].includes(s.kind)))throw Error("invalid explicit dashboard host binding");
27
34
  return freeze(c);
28
35
  }
@@ -52,7 +59,9 @@ export function openDashboardHost(options:DashboardHostOptions){
52
59
  const observation=dashboardObservations(h,c,journal.read),history=()=>journal.read();
53
60
  const append=(value:Record<string,unknown>)=>journal.append(history().at(-1)!.id,value);
54
61
  const saved=():DebriefCheckpoint|null=>{const rows=history().filter(e=>e.value.type==="checkpoint");return rows.length?detached(rows.at(-1)!.value.checkpoint) as DebriefCheckpoint:null;};
55
- let presenter:DebriefPresenter|undefined,busy=false,poisoned=false,presentationRevision:number|null=null,presenceDigest:string|null=null,attentionDeferred:string|null=null;
62
+ let presenter:DebriefPresenter|undefined,busy=false,poisoned=false,presentationRevision:number|null=null,presenceDigest:string|null=null,attentionDeferred:string|null=null,presentationAcknowledged=false;
63
+ const ordinaryDispatchHoldKey=dataDigest({hostDigest,control:"pause-new-ordinary-dispatch"});
64
+ let displayedActions:null|{tip:string;actions:{key:string;label:string;operation:DashboardHostRequest["operation"];request:DashboardHostRequest}[];digest:string}=null;
56
65
  const presence=()=>{const p=options.presence?.();return p&&p.present===true&&p.closing===true&&sha(p.evidenceDigest)&&Number.isFinite(p.expiresAt)&&p.expiresAt>Date.now()?p:null;};
57
66
  const persistence:DebriefPersistence={durability:"host-owned",load:saved,compareAndSwap(expected,next){
58
67
  const rows=history(),previous=saved();if((previous?dataDigest(previous):null)!==expected)throw Error("stale attention CAS");
@@ -65,6 +74,14 @@ export function openDashboardHost(options:DashboardHostOptions){
65
74
  const paused=async(revision:number)=>{const b=await boundary();if(ordinary&&!ordinary.quiescent())throw Error("original ordinary children not quiescent");if(experiment){const view=await experiment.inspect();if(view.budget?.active!==0||view.control!=="not-assessed")throw Error("original experiment not quiescent");}if(!b.dispatch.paused||b.dispatch.admission!=="paused"||b.dispatch.revision!==revision||b.active!==0)throw Error("safe paused selection unavailable");};
66
75
  const currentSelection=async()=>budget.binding.intent?(await budget.intentControls(null).inspect()).selection:c.selection;
67
76
  const nativeRead=async()=>({dispatch:await budget.controls(null).inspect(),intent:budget.binding.intent?await budget.intentControls(null).inspect():null,experiment:experiment?await experiment.inspect():null,ordinary:ordinary?ordinary.inspect():null});
77
+ const availableHumanActions=async()=>{
78
+ const rows=history(),tip=rows.at(-1)!.id,selection=await currentSelection(),seen=new Set<string>();
79
+ const declared=await options.humanActions?.({hostDigest,selectionDigest:dashboardSelectionDigest(selection),tip,observations:observation.latest().map(x=>({sourceId:x.sourceId,checkpointId:x.checkpointId})),preparedPresentationDigest:presenter&&presentationRevision!==null&&!presentationAcknowledged?dataDigest(presenter.view()):null})??[],a=authority();
80
+ return declared.map(value=>{const x=detached(value);controlShape(x,["key","label","request"]);const request=dashboardHostRequest(x.request);
81
+ if(!/^[a-zA-Z0-9:_-]{1,64}$/.test(x.key)||typeof x.label!=="string"||!x.label.trim()||Buffer.byteLength(x.label)>120||seen.has(x.key))throw Error("invalid human dashboard action");seen.add(x.key);
82
+ if(request.hostDigest!==hostDigest||request.expectedTip!==tip||request.selectionDigest!==dashboardSelectionDigest(selection)||!a?.requestDigests.includes(dashboardHostRequestDigest(request)))throw Error("human dashboard action is stale or not exactly authorized");
83
+ return {key:x.key,label:x.label,operation:request.operation,request};});
84
+ };
68
85
  const api={hostDigest,selectionDigest,
69
86
  async frame(){
70
87
  const rows=history(),tip=rows.at(-1)!.id,a=authority();let source:unknown=null,error:string|null=null;
@@ -74,14 +91,21 @@ export function openDashboardHost(options:DashboardHostOptions){
74
91
  let debrief:unknown=null;
75
92
  if(a&&presenter&&presentationRevision!==null&&presence()?.evidenceDigest===presenceDigest){try{await paused(presentationRevision);debrief=presenter.view();}catch{debrief=null;}}
76
93
  let controls:unknown=null;try{controls=await nativeRead();}catch(e){error=String(e);}
77
- const attention=trust.inspect(Date.now());
78
- return freeze(detached({version:"producer-dashboard-frame-v1",hostDigest,selectionDigest:dashboardSelectionDigest(selection),selectionState,tip,source,controls,debrief,attention,error,
94
+ let learning:unknown=null;if(c.learningLifecycleId)try{learning=h.readLearningLifecycle(c.archiveRoot,c.learningLifecycleId);}catch(e){error=String(e);}
95
+ const attention=trust.inspect(Date.now());let actions:{key:string;label:string;operation:string}[]=[];
96
+ try{const available=await availableHumanActions(),digest=dataDigest(available.map(({key,label,operation,request})=>({key,label,operation,requestDigest:dashboardHostRequestDigest(request)})));
97
+ if(displayedActions?.tip===tip&&displayedActions.digest!==digest)throw Error("displayed dashboard actions changed before journal advance");
98
+ displayedActions={tip,actions:available,digest};actions=available.map(({key,label,operation})=>({key,label,operation}));}catch(e){error=String(e);}
99
+ return freeze(detached({version:"producer-dashboard-frame-v1",hostDigest,selectionDigest:dashboardSelectionDigest(selection),selectionState,tip,source,controls,learning,debrief,attention,error,actions,
79
100
  requests:rows.filter(e=>["claim","result","presentation","defer","ordinary-intent-pending"].includes(String(e.value.type))).map(e=>e.value),
80
101
  control:rows.some(e=>e.value.type==="host-failure")?"failed":rows.some(e=>e.value.type==="claim"&&!rows.some(r=>r.value.type==="result"&&r.value.requestId===e.value.requestId))?"unknown":"not-assessed",
81
102
  acknowledgement:poisoned?"unknown":"readback-only",identity:"independently-declared-host; not human/module authentication",activeBranch:null,acceptance:"not-assessed",freshness:"snapshot-unknown",workerInteractions:0}));
82
103
  },
83
104
  /** Explicit reconciliation is read-only. A retained claim is never replayed as an effect. */
84
105
  reconcile:()=>api.frame(),
106
+ async humanAction(key:string){const tip=history().at(-1)!.id;if(!displayedActions||displayedActions.tip!==tip)throw Error("dashboard actions must be displayed at the current tip");
107
+ const found=displayedActions.actions.find(action=>action.key===key);if(!found)throw Error(`unknown dashboard action ${JSON.stringify(key)}`);
108
+ const current=(await availableHumanActions()).find(action=>action.key===key);if(!current||current.label!==found.label||dashboardHostRequestDigest(current.request)!==dashboardHostRequestDigest(found.request))throw Error("displayed dashboard action changed; no effect attempted");return api.action(found.request);},
85
109
  async action(input:DashboardHostRequest){
86
110
  const request=dashboardHostRequest(input),digest=dashboardHostRequestDigest(request);
87
111
  if(busy||poisoned)throw Error("dashboard operation busy or acknowledgement unknown");busy=true;let attempted=false;
@@ -97,6 +121,7 @@ export function openDashboardHost(options:DashboardHostOptions){
97
121
  let result:unknown,releaseOrdinary:(()=>void)|undefined;
98
122
  try{
99
123
  if(request.operation==="observe"){
124
+ const sourceId=(request.payload as {sourceId?:unknown})?.sourceId;if(typeof sourceId!=="string")throw Error("exact observation source required");await options.beforeObservation?.(sourceId);
100
125
  const captured=observation.observe(request.payload,a!,selection);append({type:"observation",observation:captured});result=captured;
101
126
  }else if(request.operation==="present"){
102
127
  const p=detached(request.payload) as {userPresent:boolean;closing:boolean;evidenceDigest:string;dispatchRevision:number};
@@ -111,11 +136,11 @@ export function openDashboardHost(options:DashboardHostOptions){
111
136
  attentionDeferred=null;const view=await presenter.open({mode:"manual",userPresent:true});await paused(p.dispatchRevision);
112
137
  if(attentionDeferred){presenter.close();presentationRevision=null;append({type:"defer",requestId:request.requestId,reason:attentionDeferred,weekly:true});result={state:"deferred",reason:attentionDeferred};}
113
138
  else {if(view.state!=="open")throw Error("presentation data/attention acknowledgement unavailable");
114
- presentationRevision=p.dispatchRevision;presenceDigest=p.evidenceDigest;const frameDigest=dataDigest(view);append({type:"presentation",requestId:request.requestId,evidence:p,frameDigest,state:"prepared-not-delivery",automatic:"unqualified"});result={state:"prepared",frameDigest};}
139
+ presentationRevision=p.dispatchRevision;presenceDigest=p.evidenceDigest;presentationAcknowledged=false;const frameDigest=dataDigest(view);append({type:"presentation",requestId:request.requestId,evidence:p,frameDigest,state:"prepared-not-delivery",automatic:"unqualified"});result={state:"prepared",frameDigest};}
115
140
  }
116
141
  }else if(request.operation==="presented"){
117
142
  const p=detached(request.payload) as {frameDigest:string};if(Object.keys(p).join()!=="frameDigest"||!presenter||presentationRevision===null)throw Error("original prepared presentation required");
118
- await paused(presentationRevision);if(presence()?.evidenceDigest!==presenceDigest)throw Error("user absent/presence changed");if(p.frameDigest!==dataDigest(presenter.view()))throw Error("stale presentation acknowledgement");result={state:"host-acknowledged-presentation",frameDigest:p.frameDigest,humanAuthentication:"unqualified"};
143
+ await paused(presentationRevision);if(presence()?.evidenceDigest!==presenceDigest)throw Error("user absent/presence changed");if(p.frameDigest!==dataDigest(presenter.view()))throw Error("stale presentation acknowledgement");presentationAcknowledged=true;result={state:"host-acknowledged-presentation",frameDigest:p.frameDigest,humanAuthentication:"unqualified"};
119
144
  }else if(request.operation==="defer"){
120
145
  presenter?.close();presentationRevision=null;append({type:"defer",requestId:request.requestId,weekly:true,evidence:request.payload});result={state:"deferred"};
121
146
  }else if(request.operation==="debrief"){
@@ -124,13 +149,15 @@ export function openDashboardHost(options:DashboardHostOptions){
124
149
  presenter?.close();presentationRevision=null;
125
150
  if(request.operation==="dispatch"||request.operation==="dispatch-reconcile"){
126
151
  const native=dispatchRequest(request.payload as DispatchRequest);if(native.action==="cancel-execution")throw Error("cancellation needs exact separately approved native bridge");
127
- const port=budget.controls(a!.dispatch);
152
+ const port=budget.controls(a!.dispatch),ordinaryHold=ordinary&&["pause-dispatch","resume-dispatch"].includes(native.action)?holdOrdinaryDispatch(ordinary,ordinaryDispatchHoldKey):undefined;
128
153
  if(request.operation==="dispatch-reconcile"&&(await port.inspect()).records.find(r=>r.request.requestId===native.requestId)?.digest!==dispatchRequestDigest(native))throw Error("exact original dispatch reconciliation required");
129
154
  result=request.operation==="dispatch"?await port.request(native):await port.reconcile(native.requestId);
155
+ const record=(result as {records:{request:{requestId:string};decision:string;application:string}[]}).records.find(r=>r.request.requestId===native.requestId);
156
+ if(ordinaryHold&&(record?.decision!=="approved"||native.action==="resume-dispatch"&&record.application==="applied"))ordinaryHold.release();
130
157
  }else if(request.operation==="intent"||request.operation==="intent-reconcile"){
131
158
  const native=detached(intentRequest(request.payload as IntentRequest)) as IntentRequest,port=budget.intentControls(a!.dispatch),digest=intentRequestDigest(native);
132
159
  if(c.ordinaryDigest&&!ordinary)throw Error("original ordinary boundary unavailable; no recovery");
133
- if(!ordinary)result=request.operation==="intent"?await port.request(native):await port.reconcile(native);
160
+ if(!ordinary){result=request.operation==="intent"?await port.request(native):await port.reconcile(native);const snapshot=result as {selection:IntentRequest["selection"];records:{requestId:string;application:string}[]},outcome=snapshot.records.find(r=>r.requestId===native.requestId);if(outcome?.application==="applied")await options.afterIntentApplied?.(snapshot.selection);}
134
161
  else {
135
162
  const pending=history().filter(e=>e.value.type==="ordinary-intent-pending"&&e.value.nativeRequestId===native.requestId);
136
163
  if(pending.some(e=>e.value.digest!==digest))throw Error("immutable pending ordinary intent ID");
@@ -144,8 +171,9 @@ export function openDashboardHost(options:DashboardHostOptions){
144
171
  result={state:"pending-ordinary-boundary",nativeRequestId:native.requestId,application:"not-acknowledged"};
145
172
  }else{
146
173
  result=(request.operation==="intent"||pending.length>0&&!exists)?await port.request(native):await port.reconcile(native);
147
- const outcome=(result as {records:{requestId:string;application:string}[]}).records.find(r=>r.requestId===native.requestId);
174
+ const snapshot=result as {selection:IntentRequest["selection"];records:{requestId:string;application:string}[]},outcome=snapshot.records.find(r=>r.requestId===native.requestId);
148
175
  if(held&&!outcome)throw Error("native intent application acknowledgement unavailable");
176
+ if(outcome?.application==="applied")await options.afterIntentApplied?.(snapshot.selection);
149
177
  if(held&&["applied","not-applied"].includes(outcome!.application))releaseOrdinary=held.release;
150
178
  }
151
179
  }
@@ -20,10 +20,12 @@ export function dashboardObservations(h: DashboardHarness, c: DashboardHostConfi
20
20
  if(source.kind==="work"||source.kind==="facts"){
21
21
  const checkpoint=h.readArchiveCheckpoint(root,captured.checkpointId).checkpoint;sourceManifestId=checkpoint.sourceManifestId;bytes(sourceManifestId);
22
22
  if(source.kind==="facts") {if(input.facts!==null)throw Error("facts sources are declarations, not replacement authority");return {sourceId:source.id,kind:source.kind,checkpointId:captured.checkpointId,sourceManifestId,metadata:captured,cases:null,linkageManifestId:null};}
23
- const factSource=latest().find(r=>r.kind==="facts"&&r.sourceManifestId===input.facts);if(!factSource)throw Error("exact observed fact source required, not caller replacement facts");
24
- const facts=parseRetentionJson(new TextDecoder("utf-8",{fatal:true}).decode(bytes(factSource.sourceManifestId!)),256*1024);captured.factSourceManifestId=factSource.sourceManifestId;captured.factBasis="explicit-host-declarations-not-authenticated-truth";
23
+ const factSource=input.facts===null?null:latest().find(r=>r.kind==="facts"&&r.sourceManifestId===input.facts);if(input.facts!==null&&!factSource)throw Error("exact observed fact source required, not caller replacement facts");
24
+ const facts=factSource?parseRetentionJson(new TextDecoder("utf-8",{fatal:true}).decode(bytes(factSource.sourceManifestId!)),256*1024):null;
25
+ captured.factSourceManifestId=factSource?.sourceManifestId??null;captured.factBasis=factSource?"explicit-host-declarations-not-authenticated-truth":"derived-retained-work-only";
25
26
  if(dataDigest(authority.workContext.selectedSnapshot)!==dataDigest(selection))throw Error("independent source selection changed");
26
27
  try { const signals=h.captureArchivedWorkSignals(root,sourceManifestId,authority.workContext,facts);
28
+ captured.runtimeFactsManifestId=signals.runtimeFactsManifestId;
27
29
  cases={version:"work-signals-v1",batchId:signals.caseBatchId,observationId:signals.observationId};linkageManifestId=signals.linkageManifestId;
28
30
  } catch(error) { captured.semanticFailure=String(error); } // Retain checkpoint/gap even when nomination is unavailable.
29
31
  } else {if(input.facts!==null||captured.kind!=="execution-retention-v2")throw Error("retention source requires exact referenced-blobs policy");h.readRetainedExecution(root,captured.checkpointId);}
@@ -13,7 +13,8 @@ export async function ownedDirectory(path: string) {
13
13
  if (!s.isDirectory() || s.isSymbolicLink() || s.uid !== BigInt(process.getuid!()) || (s.mode & 0o077n) !== 0n || await realpath(path) !== path) throw new Error("canonical private experiment directory required");
14
14
  return s;
15
15
  }
16
- export async function readExperimentFile(path: string, max: number): Promise<Buffer> {
16
+ export async function readExperimentFile(path: string, max: number, options: { appendOnly?: true } = {}): Promise<Buffer> {
17
+ if (Object.keys(options).some(key => key !== "appendOnly") || options.appendOnly !== undefined && options.appendOnly !== true) throw new TypeError("invalid experiment read mode");
17
18
  const file = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
18
19
  return runWithFinalizers(async () => {
19
20
  const s = await file.stat({ bigint: true });
@@ -21,7 +22,14 @@ export async function readExperimentFile(path: string, max: number): Promise<Buf
21
22
  const data = Buffer.alloc(Number(s.size)); let offset = 0;
22
23
  while (offset < data.length) { const n = (await file.read(data, offset, data.length - offset, offset)).bytesRead; if (!n) throw new Error("short experiment read"); offset += n; }
23
24
  const end = await file.stat({ bigint: true }), current = await lstat(path, { bigint: true });
24
- if (end.size !== s.size || end.mtimeNs !== s.mtimeNs || current.dev !== s.dev || current.ino !== s.ino || current.nlink !== 1n) throw new Error("experiment source changed");
25
+ const identityChanged = current.dev !== s.dev || current.ino !== s.ino || current.nlink !== 1n;
26
+ if (!options.appendOnly && (end.size !== s.size || end.mtimeNs !== s.mtimeNs || identityChanged)) throw new Error("experiment source changed");
27
+ if (options.appendOnly) {
28
+ if (end.size < s.size || identityChanged) throw new Error("experiment source changed");
29
+ const verify = Buffer.alloc(data.length); let checked = 0;
30
+ while (checked < verify.length) { const n = (await file.read(verify, checked, verify.length - checked, checked)).bytesRead; if (!n) throw new Error("short experiment read"); checked += n; }
31
+ if (!verify.equals(data)) throw new Error("experiment source changed");
32
+ }
25
33
  return data;
26
34
  }, [{ label: "experiment read close failed", run: () => file.close() }]);
27
35
  }
@@ -59,7 +67,7 @@ export function experimentStore(value: ExperimentBinding) {
59
67
  if (String(root.dev) !== b.device || String(root.ino) !== b.inode || String(file.dev) !== b.journalDevice || String(file.ino) !== b.journalInode) throw new Error("experiment identity replaced");
60
68
  };
61
69
  const read = async () => {
62
- await check(); const bytes = await readExperimentFile(path, 256 * 1024); await check();
70
+ await check(); const bytes = await readExperimentFile(path, 256 * 1024, { appendOnly: true }); await check();
63
71
  const text = new TextDecoder("utf8", { fatal: true }).decode(bytes); if (!text.endsWith("\n")) throw new Error("torn experiment journal");
64
72
  const lines = text.slice(0, -1).split("\n"); if (lines.length > 260 || experimentHash(parseWorkJson(lines[0])) !== experimentHash(b)) throw new Error("experiment header/bound mismatch");
65
73
  let previous = experimentHash(b); const events: Record<string, unknown>[] = [];
package/src/experiment.ts CHANGED
@@ -146,6 +146,12 @@ export function openExperiment(input: ExperimentBinding, hostAuthority: Experime
146
146
  let resolve!: (success: boolean) => void;
147
147
  return { promise: new Promise<boolean>(done => { resolve = done; }), done: (success: boolean) => resolve(success) };
148
148
  });
149
+ // Public readiness includes the durable spawn observation, not merely process creation.
150
+ // This closes the process-start -> spawn-ledger race for inspect and cancellation callers.
151
+ const observedStarted = reserved.map((run, i) => run.started.then(async value => {
152
+ if (value === "spawned") await append({ type: "spawn", executionId: c.variants[i].executionId });
153
+ return value;
154
+ }));
149
155
  const one = async (i: number) => {
150
156
  const v = c.variants[i]; let success = false, launched = false, primaryResult: VariantRecord | null = null;
151
157
  try {
@@ -155,8 +161,7 @@ export function openExperiment(input: ExperimentBinding, hostAuthority: Experime
155
161
  launched = true;
156
162
  const running = reserved[i].run(handles.get(v.executionId)!.signal);
157
163
  // Consume both promises even on bookkeeping failure. No untracked rejection or worker.
158
- const observed = reserved[i].started.then(async value => { if (value === "spawned") await append({ type: "spawn", executionId: v.executionId }); });
159
- const [result, observation] = await Promise.allSettled([running, observed]);
164
+ const [result, observation] = await Promise.allSettled([running, observedStarted[i]]);
160
165
  if (result.status === "rejected" || observation.status === "rejected") throw new Error("runtime/accounting/observation unknown");
161
166
  const data = Buffer.from(JSON.stringify(result.value) + "\n"), out = result.value.output;
162
167
  const cancelled = out.aborted && (Boolean(out.signal) || await reserved[i].started === "settled-without-spawn");
@@ -218,7 +223,7 @@ export function openExperiment(input: ExperimentBinding, hostAuthority: Experime
218
223
  }
219
224
  const view = await inspect().catch(unknown); primary.resolve(view.variants[0]); resolveBoundary(view); return view;
220
225
  })();
221
- live = Object.freeze({ primary: primary.promise, completion, boundary: c.order ? boundary : completion, started: Object.freeze(reserved.map(r => r.started)) }); return live;
226
+ live = Object.freeze({ primary: primary.promise, completion, boundary: c.order ? boundary : completion, started: Object.freeze(observedStarted) }); return live;
222
227
  } catch (error) {
223
228
  fault = true;
224
229
  for (const run of reserved) await run.run(AbortSignal.abort()).catch(() => {});
package/src/index.ts CHANGED
@@ -37,6 +37,7 @@ export {
37
37
  export { createDebriefPresenter, type DebriefPresenter, type DebriefHost, type DebriefFrame, type DebriefCheckpoint, type DebriefPersistence } from "./debrief.ts";
38
38
  export { ordinaryChildrenFor, ordinaryCancellation, ordinaryCancellationDigest, isOrdinaryChildren, type OrdinaryChildren, type OrdinaryCancellation, type OrdinaryAuthority, type OrdinaryTarget } from "./ordinary-children.ts";
39
39
  export { loadDashboardHarness, loadedDashboardHarnessDigest, type DashboardHarnessArtifact } from "./dashboard-harness.ts";
40
+ export { startDailyDashboardHost, discoverDailyIntentActions, type DailyDashboardHostInput, type DailyIntentAction } from "./daily-dashboard-host.ts";
40
41
  export { createDashboardHost, openDashboardHost, dashboardHostDigest, dashboardHostRequestDigest, dashboardSelectionDigest, type DashboardHost, type DashboardHostConfig, type DashboardHostRequest, type DashboardHostAuthority, type DashboardHarness, type DashboardHostOptions } from "./dashboard-host.ts";
41
42
  export { serveDashboardHost, connectDashboardHost, ENV_DASHBOARD_HOST_SOCKET, type DashboardConnection } from "./dashboard-host-transport.ts";
42
43
  export { createRetainedDebrief, retainDebriefBlind, openDebriefBlindPreview, type DebriefHarness, type CaseSelection, type DurableBlindBinding } from "./debrief-host.ts";
@@ -154,6 +155,22 @@ export {
154
155
 
155
156
  export { renderDashboard, type DashboardRenderOptions } from "./dashboard-render.ts";
156
157
 
158
+ export {
159
+ runMeasuredAgentSession,
160
+ piSdkMeasuredSessionHost,
161
+ verifyMeasuredSubscription,
162
+ MeasuredSessionFailure,
163
+ MeasuredSessionUnknownError,
164
+ type MeasuredSessionInput,
165
+ type MeasuredSessionHost,
166
+ type MeasuredSessionHostInput,
167
+ type MeasuredSessionHostResult,
168
+ type ProviderUsage,
169
+ } from "./measured-session.ts";
170
+ export { createMeasuredOrder, openMeasuredOrder, measuredOrderDigest, measuredOrderAcknowledgementDigest, qualifyMeasuredOutput,
171
+ type MeasuredOrder, type MeasuredOrderNode, type MeasuredOrderAttempt, type MeasuredOutputContract, type MeasuredOrderBinding, type MeasuredOrderAuthority,
172
+ type MeasuredOrderAcknowledgement, type MeasuredOrderNodeView } from "./measured-order.ts";
173
+
157
174
  export {
158
175
  createApprovalGate,
159
176
  createApprovalGateProvider,
@@ -9,9 +9,10 @@ import { freezeWork } from "./work-ledger-json.ts";
9
9
  import { intentKey, intentSelection, intentPriorities, type WorkIntentBinding, type IntentRequest, type IntentState, type IntentSelection, type IntentPriority } from "./intent-control.ts";
10
10
  const ref = (r: WorkFrozen<WorkRevision>) => ({ kind: r.kind, id: r.id, revision: r.revision, digest: r.digest });
11
11
  const entity = (r: { kind: string; id: string } | null) => r ? `${r.kind}:${r.id}` : null;
12
+ const allowedIntentPath=(path:string)=>!path.split("/").includes(".pi")||path.endsWith("/.pi/work.jsonl");
12
13
  export function workIntentBinding(input: WorkIntentBinding): WorkIntentBinding {
13
14
  controlShape(input, ["version", "path", "device", "inode", "grantLedgerPath", "selection", "priorities"]);
14
- if (input.version !== "1.0" || typeof input.path !== "string" || input.path.length > 1024 || !isAbsolute(input.path) || resolve(input.path) !== input.path || input.path.split("/").includes(".pi") ||
15
+ if (input.version !== "1.0" || typeof input.path !== "string" || input.path.length > 1024 || !isAbsolute(input.path) || resolve(input.path) !== input.path || !allowedIntentPath(input.path) ||
15
16
  ![input.device, input.inode].every(x => typeof x === "string" && /^\d+$/.test(x)) || !(input.grantLedgerPath === null || typeof input.grantLedgerPath === "string" && input.grantLedgerPath.length <= 1024 && isAbsolute(input.grantLedgerPath))) throw new TypeError("invalid pinned work binding");
16
17
  return JSON.parse(intentKey({ ...input, selection: intentSelection(input.selection), priorities: intentPriorities(input.priorities) }));
17
18
  }
@@ -37,7 +38,7 @@ export function resolveIntent(text: string, selection: WorkFrozen<IntentSelectio
37
38
  export async function bindWorkIntent(input: { path: string; grantLedgerPath: string | null; selection: IntentSelection; priorities: IntentPriority[] }): Promise<WorkFrozen<WorkIntentBinding>> {
38
39
  controlShape(input, ["path", "grantLedgerPath", "selection", "priorities"]);
39
40
  const selection = intentSelection(input.selection), priorities = intentPriorities(input.priorities), path = input.path, grantLedgerPath = input.grantLedgerPath;
40
- if (typeof path !== "string" || !isAbsolute(path) || path.split("/").includes(".pi")) throw new TypeError("explicit work path required");
41
+ if (typeof path !== "string" || !isAbsolute(path) || !allowedIntentPath(path)) throw new TypeError("explicit work path required");
41
42
  await workDestination(path, grantLedgerPath);
42
43
  const parent = await lstat(dirname(path), { bigint: true });
43
44
  if (!parent.isDirectory() || (parent.mode & 0o077n) || parent.uid !== BigInt(process.getuid!())) throw new Error("owner-private work parent required");
@@ -0,0 +1,67 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { constants } from "node:fs";
3
+ import { mkdir, open, readFile, realpath, lstat, writeFile } from "node:fs/promises";
4
+ import { isAbsolute, join, resolve } from "node:path";
5
+ import { withFileLock } from "./file-lock.ts";
6
+ import { intentKey } from "./intent-control.ts";
7
+ import { runMeasuredAgentSession, MeasuredSessionFailure, MeasuredSessionUnknownError, type MeasuredSessionHost, type MeasuredSessionInput } from "./measured-session.ts";
8
+ import { openResourceBudget, resourceBindingDigest, type GovernedBudgetBinding } from "./resource-budget.ts";
9
+ import { freezeWork } from "./work-ledger-json.ts";
10
+
11
+ const ID=/^[a-zA-Z0-9:_-]{1,128}$/,HEX=/^[a-f0-9]{64}$/;
12
+ const hash=(value:unknown)=>createHash("sha256").update(typeof value==="string"?value:intentKey(value)).digest("hex");
13
+ const exact=(value:object,keys:string[])=>Object.keys(value).sort().join()===keys.sort().join();
14
+ export type MeasuredOutputContract={version:"measured-output-exact-v1";expectedSha256:string}|{version:"measured-output-json-projection-v1";requiredKeys:string[];textFields:string[];arrayField:string;minItems:number;maxItems:number;itemRequiredFields:string[];projectionFields:string[];expectedProjectionSha256:string};
15
+ export interface MeasuredOrderAttempt extends Omit<MeasuredSessionInput,"budget"|"attempt"|"signal">{attemptId:string;includeDependencies:boolean;qualification:MeasuredOutputContract}
16
+ export interface MeasuredOrderNode {nodeId:string;dependencies:string[];attempts:MeasuredOrderAttempt[]}
17
+ export interface MeasuredOrder {version:"measured-order-v1";orderId:string;authorityDigest:string;budgetDigest:string;nodes:MeasuredOrderNode[]}
18
+ export interface MeasuredOrderBinding {version:"measured-order-binding-v1";directory:string;directoryDevice:string;directoryInode:string;journalDevice:string;journalInode:string;budget:GovernedBudgetBinding;order:MeasuredOrder;digest:string}
19
+ export interface MeasuredOrderAuthority {authorityDigest:string;orderDigests:string[];acknowledgementDigests:string[]}
20
+ export interface MeasuredOrderAcknowledgement {version:"measured-order-ack-v1";requestId:string;bindingDigest:string;attemptId:string;claimOwner:string;claimDigest:string}
21
+ type JournalEvent=
22
+ |{type:"claim";sequence:number;previousDigest:string|null;attemptId:string;owner:string;digest:string}
23
+ |{type:"result";sequence:number;previousDigest:string;attemptId:string;owner:string;state:"completed"|"failed"|"cancelled"|"unknown"|"mismatch";outputSha256:string|null;result:unknown;digest:string}
24
+ |{type:"acknowledge-unknown";sequence:number;previousDigest:string;request:MeasuredOrderAcknowledgement;digest:string};
25
+ export interface MeasuredOrderNodeView {nodeId:string;state:"eligible"|"dependency-blocked"|"running"|"unknown"|"exhausted"|"satisfied";action:"dispatch"|"wait"|"acknowledge"|"none";attemptId:string|null;outputSha256:string|null;unknownClaim:{owner:string;digest:string}|null}
26
+ function field(value:unknown):value is string{return typeof value==="string"&&/^[A-Za-z][A-Za-z0-9_-]{0,63}$/.test(value);}
27
+ function checkedContract(input:MeasuredOutputContract):MeasuredOutputContract{const c=JSON.parse(intentKey(input)) as MeasuredOutputContract;if(c.version==="measured-output-exact-v1"){if(!exact(c,["version","expectedSha256"])||!HEX.test(c.expectedSha256))throw Error("invalid exact output contract");return c;}if(c.version!=="measured-output-json-projection-v1"||!exact(c,["version","requiredKeys","textFields","arrayField","minItems","maxItems","itemRequiredFields","projectionFields","expectedProjectionSha256"])||![c.requiredKeys,c.textFields,c.itemRequiredFields,c.projectionFields].every(x=>Array.isArray(x)&&x.length<=32&&new Set(x).size===x.length&&x.every(field))||!field(c.arrayField)||!Number.isSafeInteger(c.minItems)||!Number.isSafeInteger(c.maxItems)||c.minItems<0||c.maxItems<c.minItems||c.maxItems>4096||!c.requiredKeys.includes(c.arrayField)||c.textFields.some(x=>!c.requiredKeys.includes(x))||c.projectionFields.some(x=>!c.itemRequiredFields.includes(x))||!HEX.test(c.expectedProjectionSha256))throw Error("invalid deterministic JSON output contract");return c;}
28
+ export function qualifyMeasuredOutput(output:string,input:MeasuredOutputContract){const contract=checkedContract(input),outputSha256=hash(output);if(contract.version==="measured-output-exact-v1")return{eligible:outputSha256===contract.expectedSha256,outputSha256,projectionSha256:null};let value:unknown;try{value=JSON.parse(output);}catch{return{eligible:false,outputSha256,projectionSha256:null};}if(!value||typeof value!=="object"||Array.isArray(value)||Object.keys(value).sort().join()!==[...contract.requiredKeys].sort().join())return{eligible:false,outputSha256,projectionSha256:null};const object=value as Record<string,unknown>,items=object[contract.arrayField];if(contract.textFields.some(x=>typeof object[x]!=="string"||!(object[x] as string).trim())||!Array.isArray(items)||items.length<contract.minItems||items.length>contract.maxItems||items.some(x=>!x||typeof x!=="object"||Array.isArray(x)||contract.itemRequiredFields.some(k=>!Object.hasOwn(x,k))))return{eligible:false,outputSha256,projectionSha256:null};const projection=(items as Record<string,unknown>[]).map(item=>Object.fromEntries(contract.projectionFields.map(k=>[k,item[k]]))),projectionSha256=hash(intentKey(projection));return{eligible:projectionSha256===contract.expectedProjectionSha256,outputSha256,projectionSha256};}
29
+
30
+ function checkedOrder(input:MeasuredOrder,budget:GovernedBudgetBinding):MeasuredOrder{
31
+ const order=JSON.parse(intentKey(input)) as MeasuredOrder;if(!exact(order,["version","orderId","authorityDigest","budgetDigest","nodes"])||order.version!=="measured-order-v1"||!ID.test(order.orderId)||!HEX.test(order.authorityDigest)||order.budgetDigest!==resourceBindingDigest(budget)||!Array.isArray(order.nodes)||!order.nodes.length||order.nodes.length>8)throw Error("invalid bounded measured order");
32
+ const ids=new Set<string>(),attempts=new Set<string>();let inputBytes=0,count=0;
33
+ for(const node of order.nodes){if(!exact(node,["nodeId","dependencies","attempts"])||!ID.test(node.nodeId)||ids.has(node.nodeId)||!Array.isArray(node.dependencies)||node.dependencies.length>8||new Set(node.dependencies).size!==node.dependencies.length||!Array.isArray(node.attempts)||!node.attempts.length||node.attempts.length>3)throw Error("invalid node/dependency/recovery policy");ids.add(node.nodeId);
34
+ for(const a of node.attempts){if(!exact(a,["attemptId","model","thinkingLevel","prompt","deadlineMs","terminationGraceMs","maxRetainedOutputBytes","maxOutputTokens","includeDependencies","qualification"])||!ID.test(a.attemptId)||attempts.has(a.attemptId)||typeof a.includeDependencies!=="boolean"||!["openai-codex/gpt-5.6-sol","openai-codex/gpt-5.6-terra"].includes(a.model)||typeof a.prompt!=="string"||!a.prompt.length||Buffer.byteLength(a.prompt)>16384||!Number.isSafeInteger(a.deadlineMs)||a.deadlineMs<1000||a.deadlineMs>300000||!Number.isSafeInteger(a.terminationGraceMs)||a.terminationGraceMs<100||a.terminationGraceMs>10000||!Number.isSafeInteger(a.maxRetainedOutputBytes)||a.maxRetainedOutputBytes<1||a.maxRetainedOutputBytes>1024*1024||!Number.isSafeInteger(a.maxOutputTokens)||a.maxOutputTokens<1||a.maxOutputTokens>16384)throw Error("invalid read-only model attempt");checkedContract(a.qualification);attempts.add(a.attemptId);inputBytes+=Buffer.byteLength(a.prompt);count++;}
35
+ }
36
+ for(const node of order.nodes)for(const attempt of node.attempts)if(attempt.includeDependencies){let extra=0;for(const dependency of node.dependencies){const prior=order.nodes.find(n=>n.nodeId===dependency);extra+=256+Math.max(...(prior?.attempts.map(a=>a.maxRetainedOutputBytes)??[0]));}if(Buffer.byteLength(attempt.prompt)+extra>16384)throw Error("sealed dependency prompt exceeds attempt bound");inputBytes+=extra;}
37
+ if(count>budget.limits.maxAttempts||inputBytes>budget.limits.maxInputBytes||budget.limits.maxConcurrent<count)throw Error("order exceeds original accounting allowance");
38
+ const visiting=new Set<string>(),done=new Set<string>();const visit=(id:string)=>{if(done.has(id))return;if(visiting.has(id))throw Error("dependency cycle");const node=order.nodes.find(n=>n.nodeId===id);if(!node)throw Error("missing dependency");visiting.add(id);node.dependencies.forEach(visit);visiting.delete(id);done.add(id);};order.nodes.forEach(n=>visit(n.nodeId));return order;
39
+ }
40
+ export const measuredOrderDigest=(order:MeasuredOrder)=>hash(order);
41
+ export function measuredOrderAcknowledgementDigest(input:MeasuredOrderAcknowledgement){const value=JSON.parse(intentKey(input)) as MeasuredOrderAcknowledgement;if(!exact(value,["version","requestId","bindingDigest","attemptId","claimOwner","claimDigest"])||value.version!=="measured-order-ack-v1"||![value.requestId,value.attemptId,value.claimOwner].every(x=>ID.test(x))||![value.bindingDigest,value.claimDigest].every(x=>HEX.test(x)))throw Error("invalid unknown acknowledgement");return hash(value);}
42
+ function paths(directory:string){return{binding:join(directory,"binding.json"),journal:join(directory,"journal.jsonl")};}
43
+ async function privateDirectory(directory:string){const st=await lstat(directory,{bigint:true});if(!st.isDirectory()||(st.mode&0o077n)||st.uid!==BigInt(process.getuid!())||await realpath(directory)!==directory)throw Error("owner-private measured order directory required");}
44
+ export async function createMeasuredOrder(input:{directory:string;budget:GovernedBudgetBinding;order:MeasuredOrder}):Promise<Readonly<MeasuredOrderBinding>>{
45
+ if(!isAbsolute(input.directory)||resolve(input.directory)!==input.directory)throw Error("absolute measured order directory required");const order=checkedOrder(input.order,input.budget);await mkdir(input.directory,{recursive:false,mode:0o700});await privateDirectory(input.directory);
46
+ const p=paths(input.directory);await writeFile(p.journal,"",{mode:0o600,flag:"wx"});const directoryStat=await lstat(input.directory,{bigint:true}),journalStat=await lstat(p.journal,{bigint:true}),identity={directory:input.directory,directoryDevice:String(directoryStat.dev),directoryInode:String(directoryStat.ino),journalDevice:String(journalStat.dev),journalInode:String(journalStat.ino)};
47
+ const binding={version:"measured-order-binding-v1" as const,...identity,budget:input.budget,order,digest:hash({...identity,budgetDigest:resourceBindingDigest(input.budget),orderDigest:measuredOrderDigest(order)})};await writeFile(p.binding,intentKey(binding)+"\n",{mode:0o600,flag:"wx"});return freezeWork(binding) as unknown as Readonly<MeasuredOrderBinding>;
48
+ }
49
+ function checkedBinding(input:MeasuredOrderBinding):MeasuredOrderBinding{const b=JSON.parse(intentKey(input)) as MeasuredOrderBinding,identity={directory:b.directory,directoryDevice:b.directoryDevice,directoryInode:b.directoryInode,journalDevice:b.journalDevice,journalInode:b.journalInode};if(!exact(b,["version","directory","directoryDevice","directoryInode","journalDevice","journalInode","budget","order","digest"])||b.version!=="measured-order-binding-v1"||!isAbsolute(b.directory)||resolve(b.directory)!==b.directory||![b.directoryDevice,b.directoryInode,b.journalDevice,b.journalInode].every(x=>/^\d+$/.test(x))||b.digest!==hash({...identity,budgetDigest:resourceBindingDigest(b.budget),orderDigest:measuredOrderDigest(checkedOrder(b.order,b.budget))}))throw Error("invalid measured order binding");return b;}
50
+ async function pinnedJournal(binding:MeasuredOrderBinding,writable=false){const path=paths(binding.directory).journal,handle=await open(path,(writable?constants.O_RDWR|constants.O_APPEND:constants.O_RDONLY)|constants.O_NOFOLLOW);try{const st=await handle.stat({bigint:true});if(!st.isFile()||st.nlink!==1n||(st.mode&0o077n)||String(st.dev)!==binding.journalDevice||String(st.ino)!==binding.journalInode)throw Error("measured order binding changed");return handle;}catch(error){await handle.close();throw error;}}
51
+ async function linked(binding:MeasuredOrderBinding){const st=await lstat(paths(binding.directory).journal,{bigint:true});if(!st.isFile()||st.nlink!==1n||String(st.dev)!==binding.journalDevice||String(st.ino)!==binding.journalInode||(await realpath(paths(binding.directory).journal))!==paths(binding.directory).journal)throw Error("measured order binding changed");}
52
+ function parseJournal(text:string):JournalEvent[]{if(text&&!text.endsWith("\n"))throw Error("incomplete measured order journal");const rows=text?text.trimEnd().split("\n").map(line=>{const row=JSON.parse(line) as JournalEvent;if(intentKey(row)!==line)throw Error("noncanonical measured order journal");return row;}):[];let prior:string|null=null;for(let i=0;i<rows.length;i++){const row=rows[i],copy={...row} as any,claimed=copy.digest;delete copy.digest;if(row.sequence!==i||row.previousDigest!==prior||claimed!==hash(copy))throw Error("invalid measured order journal");prior=claimed;}return rows;}
53
+ async function appendJournal(binding:MeasuredOrderBinding,event:Omit<JournalEvent,"sequence"|"previousDigest"|"digest">){const path=paths(binding.directory).journal;return withFileLock(path,"measured order",async()=>{const handle=await pinnedJournal(binding,true);try{const rows=parseJournal(await handle.readFile("utf8")),base={...event,sequence:rows.length,previousDigest:rows.at(-1)?.digest??null} as any,record={...base,digest:hash(base)} as JournalEvent;await linked(binding);await handle.writeFile(intentKey(record)+"\n");await handle.sync();return record;}finally{await handle.close();}});}
54
+ function project(order:MeasuredOrder,events:JournalEvent[],currentOwner:string):MeasuredOrderNodeView[]{const rows=new Map<string,MeasuredOrderNodeView>();const evaluate=(node:MeasuredOrderNode):MeasuredOrderNodeView=>{const prior=rows.get(node.nodeId);if(prior)return prior;const row:MeasuredOrderNodeView={nodeId:node.nodeId,state:"exhausted",action:"none",attemptId:null,outputSha256:null,unknownClaim:null};if(node.dependencies.some(id=>evaluate(order.nodes.find(n=>n.nodeId===id)!).state!=="satisfied")){row.state="dependency-blocked";row.action="wait";rows.set(node.nodeId,row);return row;}
55
+ for(const attempt of node.attempts){const claim=events.find(e=>e.type==="claim"&&e.attemptId===attempt.attemptId) as Extract<JournalEvent,{type:"claim"}>|undefined;if(!claim){row.state="eligible";row.action="dispatch";row.attemptId=attempt.attemptId;break;}const result=events.find(e=>e.type==="result"&&e.attemptId===attempt.attemptId) as Extract<JournalEvent,{type:"result"}>|undefined;const acknowledged=events.some(e=>e.type==="acknowledge-unknown"&&e.request.attemptId===attempt.attemptId&&e.request.claimDigest===claim.digest&&e.request.claimOwner===claim.owner);if(!result){if(claim.owner!==currentOwner){if(acknowledged)continue;row.state="unknown";row.action="acknowledge";row.unknownClaim={owner:claim.owner,digest:claim.digest};}else{row.state="running";row.action="wait";}row.attemptId=attempt.attemptId;break;}if(result.state==="unknown"){if(!acknowledged){row.state="unknown";row.action="acknowledge";row.attemptId=attempt.attemptId;row.unknownClaim={owner:claim.owner,digest:claim.digest};break;}continue;}if(result.state==="completed"){row.state="satisfied";row.outputSha256=result.outputSha256;row.attemptId=attempt.attemptId;break;}}
56
+ rows.set(node.nodeId,row);return row;};return order.nodes.map(evaluate);}
57
+ export function openMeasuredOrder(input:MeasuredOrderBinding,hostAuthority:MeasuredOrderAuthority|null,host:MeasuredSessionHost){const binding=checkedBinding(input),p=paths(binding.directory),owner=randomUUID(),authorized=()=>hostAuthority?.authorityDigest===binding.order.authorityDigest&&hostAuthority.orderDigests.includes(measuredOrderDigest(binding.order));
58
+ const read=async()=>{await privateDirectory(binding.directory);const directoryStat=await lstat(binding.directory,{bigint:true});if(String(directoryStat.dev)!==binding.directoryDevice||String(directoryStat.ino)!==binding.directoryInode||(await readFile(p.binding,"utf8")).trimEnd()!==intentKey(binding))throw Error("measured order binding changed");await linked(binding);const handle=await pinnedJournal(binding);try{return parseJournal(await handle.readFile("utf8"));}finally{await handle.close();}};
59
+ const inspect=async()=>{const events=await read(),accounting=await openResourceBudget(binding.budget).inspect(),diagnostics=events.filter((e):e is Extract<JournalEvent,{type:"result"}>=>e.type==="result"&&e.state!=="completed").map(e=>`${e.attemptId}:${e.state}`);return freezeWork({version:"measured-order-view-v1",orderId:binding.order.orderId,nodes:project(binding.order,events,owner),resources:accounting,diagnostics,acceptance:"not-assessed",freshness:"snapshot-unknown" as const});};
60
+ return Object.freeze({inspect,async advance(){if(!authorized())throw Error("independent exact order authority required");await read();let selected:MeasuredOrderAttempt|undefined,claim:Extract<JournalEvent,{type:"claim"}>|undefined;
61
+ await withFileLock(p.journal,"measured order advance",async()=>{const handle=await pinnedJournal(binding,true);try{const events=parseJournal(await handle.readFile("utf8")),eligible=project(binding.order,events,owner).find(n=>n.state==="eligible");if(!eligible)return;selected=binding.order.nodes.flatMap(n=>n.attempts).find(a=>a.attemptId===eligible.attemptId)!;const base={type:"claim",sequence:events.length,previousDigest:events.at(-1)?.digest??null,attemptId:selected.attemptId,owner} as const;claim={...base,digest:hash(base)};await linked(binding);await handle.writeFile(intentKey(claim)+"\n");await handle.sync();}finally{await handle.close();}});
62
+ if(!selected||!claim)return inspect();const node=binding.order.nodes.find(n=>n.attempts.some(a=>a.attemptId===selected!.attemptId))!,index=node.attempts.findIndex(a=>a.attemptId===selected!.attemptId),controller=new AbortController();let state:Extract<JournalEvent,{type:"result"}>["state"]="failed",result:unknown=null,outputSha256:string|null=null;
63
+ try{const accounting=await openResourceBudget(binding.budget).inspect(),parent=[...node.attempts.slice(0,index)].reverse().find(a=>accounting.reservations.some(r=>r.attemptId===a.attemptId))??null,events=await read();let prompt=selected.prompt;if(selected.includeDependencies)for(const dependency of node.dependencies){const dependencyNode=binding.order.nodes.find(n=>n.nodeId===dependency)!,prior=dependencyNode.attempts.map(a=>events.find(e=>e.type==="result"&&e.attemptId===a.attemptId)).find((e):e is Extract<JournalEvent,{type:"result"}>=>e?.type==="result"&&e.state==="completed"),output=(prior?.result as {output?:unknown})?.output;if(!prior||typeof output!=="string"||hash(output)!==prior.outputSha256)throw Error("sealed dependency output unavailable");prompt+=`\n\n<sealed-dependency node=${JSON.stringify(dependency)} sha256=${JSON.stringify(prior.outputSha256)}>\n${output}\n</sealed-dependency>`;}
64
+ result=await runMeasuredAgentSession({budget:binding.budget,attempt:{attemptId:selected.attemptId,orderId:binding.order.orderId,experimentId:`${binding.order.orderId}:${node.nodeId}`,kind:parent?"retry":"primary",parentAttemptId:parent?.attemptId??null},model:selected.model,thinkingLevel:selected.thinkingLevel,prompt,deadlineMs:selected.deadlineMs,terminationGraceMs:selected.terminationGraceMs,maxRetainedOutputBytes:selected.maxRetainedOutputBytes,maxOutputTokens:selected.maxOutputTokens,signal:controller.signal},host);const qualified=qualifyMeasuredOutput((result as {output:string}).output,selected.qualification);outputSha256=qualified.outputSha256;state=qualified.eligible?"completed":"mismatch";}catch(error){result=error instanceof MeasuredSessionFailure?error.evidence:{errorType:error instanceof Error?error.name:"unknown",errorCode:typeof error==="object"&&error&&"code" in error?String(error.code):null,message:error instanceof Error?error.message:"unknown"};state=error instanceof MeasuredSessionUnknownError?"unknown":error instanceof MeasuredSessionFailure&&(error.evidence as {state?:string})?.state==="cancelled"?"cancelled":"failed";}
65
+ await appendJournal(binding,{type:"result",attemptId:selected.attemptId,owner:claim.owner,state,outputSha256,result} as Omit<JournalEvent,"sequence"|"previousDigest"|"digest">);return inspect();},
66
+ async acknowledgeUnknown(request:MeasuredOrderAcknowledgement,authority:MeasuredOrderAuthority|null){const digest=measuredOrderAcknowledgementDigest(request);if(authority?.authorityDigest!==binding.order.authorityDigest||!authority.acknowledgementDigests.includes(digest)||request.bindingDigest!==binding.digest)throw Error("independent exact acknowledgement authority required");await read();await withFileLock(p.journal,"measured order acknowledgement",async()=>{const handle=await pinnedJournal(binding,true);try{const events=parseJournal(await handle.readFile("utf8")),claim=events.find(e=>e.type==="claim"&&e.attemptId===request.attemptId) as Extract<JournalEvent,{type:"claim"}>|undefined,result=events.find(e=>e.type==="result"&&e.attemptId===request.attemptId) as Extract<JournalEvent,{type:"result"}>|undefined;if(!claim||claim.owner!==request.claimOwner||claim.digest!==request.claimDigest||!(result?.state==="unknown"||!result&&claim.owner!==owner)||events.some(e=>e.type==="acknowledge-unknown"&&e.request.attemptId===request.attemptId))throw Error("exact current unknown claim required");const base={type:"acknowledge-unknown",sequence:events.length,previousDigest:events.at(-1)!.digest,request} as const,record={...base,digest:hash(base)};await linked(binding);await handle.writeFile(intentKey(record)+"\n");await handle.sync();}finally{await handle.close();}});return inspect();}});
67
+ }
@@ -0,0 +1,70 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { mkdir, mkdtemp, rm } from 'node:fs/promises';
3
+ import { join, resolve } from 'node:path';
4
+ import { createAgentSession, DefaultResourceLoader, ModelRuntime, SessionManager, SettingsManager } from '@earendil-works/pi-coding-agent';
5
+ import { openResourceBudget, type AttemptDemand, type GovernedBudgetBinding } from './resource-budget.ts';
6
+ import { freezeWork } from './work-ledger-json.ts';
7
+
8
+ const MODELS=['openai-codex/gpt-5.6-sol','openai-codex/gpt-5.6-terra'] as const;
9
+ const THINKING=['off','minimal','low','medium','high','xhigh','max'] as const;
10
+ export interface ProviderUsage {input:number;output:number;cacheRead:number;cacheWrite:number;reasoning:number;totalTokens:number;cost:{input:number;output:number;cacheRead:number;cacheWrite:number;total:number}}
11
+ export interface MeasuredSessionHostInput {model:typeof MODELS[number];thinkingLevel:typeof THINKING[number];prompt:string;deadlineMs:number;maxOutputTokens:number;tools:readonly [];discovery:'factory-only';signal:AbortSignal}
12
+ export interface MeasuredSessionHostResult {output:string;usage:ProviderUsage;provider:'openai-codex';model:'gpt-5.6-sol'|'gpt-5.6-terra';stopReason:string}
13
+ export interface MeasuredSessionHost {run(input:MeasuredSessionHostInput):Promise<MeasuredSessionHostResult>}
14
+ export interface MeasuredSessionInput {budget:GovernedBudgetBinding;attempt:Omit<AttemptDemand,'inputBytes'|'inputDigest'>;model:typeof MODELS[number];thinkingLevel:typeof THINKING[number];prompt:string;deadlineMs:number;terminationGraceMs:number;maxRetainedOutputBytes:number;maxOutputTokens:number;signal?:AbortSignal}
15
+ export class MeasuredSessionFailure extends Error {readonly evidence:unknown;constructor(message:string,evidence:unknown){super(message);this.name='MeasuredSessionFailure';this.evidence=evidence;}}
16
+ export class MeasuredSessionUnknownError extends Error {constructor(){super('measured session did not settle after owned abort; reservation remains active');this.name='MeasuredSessionUnknownError';}}
17
+ const finite=(value:unknown)=>typeof value==='number'&&Number.isFinite(value)&&value>=0;
18
+ function checkedUsage(value:ProviderUsage):ProviderUsage {
19
+ if(!value||typeof value!=='object'||Object.keys(value).sort().join()!=='cacheRead,cacheWrite,cost,input,output,reasoning,totalTokens'||![value.input,value.output,value.cacheRead,value.cacheWrite,value.reasoning,value.totalTokens].every(finite)||!Number.isSafeInteger(value.totalTokens)||!value.cost||Object.keys(value.cost).sort().join()!=='cacheRead,cacheWrite,input,output,total'||!Object.values(value.cost).every(finite))throw Error('provider usage unavailable or malformed');
20
+ return JSON.parse(JSON.stringify(value));
21
+ }
22
+ function checked(input:MeasuredSessionInput):MeasuredSessionInput {
23
+ if(!input||typeof input!=='object'||!MODELS.includes(input.model)||!THINKING.includes(input.thinkingLevel))throw Error('measured sessions require exact Codex Sol or Terra');
24
+ if(typeof input.prompt!=='string'||!input.prompt.length||Buffer.byteLength(input.prompt)>65536||!Number.isSafeInteger(input.deadlineMs)||input.deadlineMs<1000||input.deadlineMs>300000||!Number.isSafeInteger(input.terminationGraceMs)||input.terminationGraceMs<100||input.terminationGraceMs>10000||!Number.isSafeInteger(input.maxRetainedOutputBytes)||input.maxRetainedOutputBytes<1||input.maxRetainedOutputBytes>1024*1024||!Number.isSafeInteger(input.maxOutputTokens)||input.maxOutputTokens<1||input.maxOutputTokens>16384)throw Error('invalid measured session bounds');
25
+ if(input.signal?.aborted)throw new MeasuredSessionFailure('measured session cancelled before reservation',{state:'cancelled-before-reservation',reason:'caller-signal'});
26
+ return input;
27
+ }
28
+ /** Opt-in one-prompt route. Existing resource budgets own attempts; byte retention is checked after generation. */
29
+ export async function runMeasuredAgentSession(raw:MeasuredSessionInput,host:MeasuredSessionHost){
30
+ const input=checked(raw),bytes=Buffer.from(input.prompt),budget=openResourceBudget(input.budget),permit=await budget.reserve({...input.attempt,inputBytes:bytes.length,inputDigest:createHash('sha256').update(bytes).digest('hex')});
31
+ const controller=new AbortController(),abort=()=>controller.abort(input.signal?.reason??'operator-cancellation');input.signal?.addEventListener('abort',abort,{once:true});if(input.signal?.aborted)abort();
32
+ const timer=setTimeout(()=>controller.abort('deadline'),input.deadlineMs);let outcome:'completed'|'failed'|'cancelled'|null='failed';
33
+ try{
34
+ const unknown=new Promise<never>((_,reject)=>controller.signal.addEventListener('abort',()=>setTimeout(()=>reject(new MeasuredSessionUnknownError()),input.terminationGraceMs),{once:true}));
35
+ if(controller.signal.aborted)throw new MeasuredSessionFailure('measured session cancelled after reservation',{state:'cancelled-before-host',reason:'caller-signal'});
36
+ const running=host.run({model:input.model,thinkingLevel:input.thinkingLevel,prompt:input.prompt,deadlineMs:input.deadlineMs,maxOutputTokens:input.maxOutputTokens,tools:[],discovery:'factory-only',signal:controller.signal});
37
+ const result=await Promise.race([running,unknown]);
38
+ const outputBytes=Buffer.from(result.output),outputEvidence={sha256:createHash('sha256').update(outputBytes).digest('hex'),bytes:outputBytes.length,retainedPrefix:outputBytes.subarray(0,input.maxRetainedOutputBytes).toString('utf8')};
39
+ let usage:ProviderUsage;try{usage=checkedUsage(result.usage);}catch(error){throw new MeasuredSessionFailure(String(error),{state:'failed',model:result.model,stopReason:result.stopReason,output:outputEvidence});}
40
+ if(result.provider!=='openai-codex'||`${result.provider}/${result.model}`!==input.model)throw new MeasuredSessionFailure('measured model identity mismatch',{state:'failed',observedProvider:result.provider,observedModel:result.model,stopReason:result.stopReason,output:outputEvidence,usage});
41
+ if(outputEvidence.bytes>input.maxRetainedOutputBytes)throw new MeasuredSessionFailure('measured retained-output bound exceeded',{state:'failed',model:input.model,stopReason:result.stopReason,output:outputEvidence,usage});
42
+ if(controller.signal.aborted||result.stopReason!=='stop')throw new MeasuredSessionFailure('measured assistant did not stop cleanly',{state:controller.signal.aborted?'cancelled':'failed',model:input.model,stopReason:result.stopReason,output:outputEvidence,usage});
43
+ outcome='completed';return freezeWork({version:'measured-session-result-v1',state:'completed',model:input.model,thinkingLevel:input.thinkingLevel,output:result.output,usage,stopReason:result.stopReason,acceptance:'not-assessed',grantExpansion:false});
44
+ }catch(error){
45
+ if(error instanceof MeasuredSessionUnknownError){outcome=null;throw error;}
46
+ if(controller.signal.aborted)outcome='cancelled';
47
+ if(error instanceof MeasuredSessionFailure)throw error;
48
+ throw new MeasuredSessionFailure('measured host failed',{state:controller.signal.aborted?'cancelled':'failed',errorType:error instanceof Error?error.name:'unknown'});
49
+ }
50
+ finally{clearTimeout(timer);input.signal?.removeEventListener('abort',abort);if(outcome)await permit.settle(outcome);}
51
+ }
52
+ export interface MeasuredAuthRuntime {isUsingOAuth(provider:string):boolean;isUsingSubscription(provider:string):boolean;checkAuth(provider:string,options?:{signal?:AbortSignal}):Promise<{type:string}|undefined>}
53
+ export async function verifyMeasuredSubscription(runtime:MeasuredAuthRuntime,signal?:AbortSignal){
54
+ signal?.throwIfAborted();const auth=await runtime.checkAuth('openai-codex',{signal});signal?.throwIfAborted();
55
+ if(auth?.type!=='oauth'||!runtime.isUsingOAuth('openai-codex')||!runtime.isUsingSubscription('openai-codex'))throw Error('exact OpenAI Codex OAuth subscription authorization required');
56
+ }
57
+ /** Concrete SDK host: in-memory session/settings, empty discovery roots, no tools, one factory-named inline extension. */
58
+ export function piSdkMeasuredSessionHost():MeasuredSessionHost{return{async run(input){
59
+ const root=await mkdtemp(join(resolve(process.env.TMPDIR??'/tmp'),'pi-daddy-measured-'));let session:Awaited<ReturnType<typeof createAgentSession>>['session']|undefined;
60
+ try{
61
+ const agentDir=join(root,'agent');await mkdir(agentDir,{mode:0o700});const settings=SettingsManager.inMemory({compaction:{enabled:false},retry:{enabled:false,maxRetries:0}});
62
+ const loader=new DefaultResourceLoader({cwd:root,agentDir,settingsManager:settings,systemPromptOverride:()=> 'Follow the supplied bounded task. Do not use tools or claim acceptance.',agentsFilesOverride:()=>({agentsFiles:[],diagnostics:[]}),skillsOverride:()=>({skills:[],diagnostics:[]}),promptsOverride:()=>({prompts:[],diagnostics:[]}),extensionFactories:[{name:'pi-daddy-measured-session',factory:()=>{}}]});await loader.reload();
63
+ input.signal.throwIfAborted();const runtime=await ModelRuntime.create({signal:input.signal});input.signal.throwIfAborted();await verifyMeasuredSubscription(runtime,input.signal);input.signal.throwIfAborted();
64
+ const [provider,modelId]=input.model.split('/'),found=runtime.getModel(provider,modelId);if(!found)throw Error('requested measured model unavailable');
65
+ const created=await createAgentSession({cwd:root,agentDir,model:{...found,maxTokens:input.maxOutputTokens},thinkingLevel:input.thinkingLevel,modelRuntime:runtime,resourceLoader:loader,sessionManager:SessionManager.inMemory(root),settingsManager:settings,noTools:'all'});session=created.session;if(created.extensionsResult.errors.length)throw Error('factory-only extension load failed');
66
+ const abort=()=>void session!.abort();input.signal.addEventListener('abort',abort,{once:true});try{if(input.signal.aborted){await session.abort();throw input.signal.reason;}await session.prompt(input.prompt,{expandPromptTemplates:false});}finally{input.signal.removeEventListener('abort',abort);}
67
+ const message=[...session.messages].reverse().find(m=>m.role==='assistant');if(!message)throw Error('measured assistant result unavailable');
68
+ return {output:message.content.filter(c=>c.type==='text').map(c=>c.text).join(''),usage:message.usage as ProviderUsage,provider:message.provider as MeasuredSessionHostResult['provider'],model:message.model as MeasuredSessionHostResult['model'],stopReason:message.stopReason};
69
+ }finally{session?.dispose();await rm(root,{recursive:true,force:true});}
70
+ }};}