@north-light/cli 0.3.414 → 0.3.415

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 (88) hide show
  1. package/dist/api/client.d.ts +20 -0
  2. package/dist/api/client.js +3 -3
  3. package/dist/api/dto/common.d.ts +1 -1
  4. package/dist/api/dto/crons.d.ts +7 -1
  5. package/dist/api/dto/human-requests.d.ts +1 -1
  6. package/dist/api/dto/run-events.d.ts +1 -1
  7. package/dist/api/dto/usage.d.ts +27 -0
  8. package/dist/api/dto/usage.js +0 -0
  9. package/dist/api/errors.js +1 -1
  10. package/dist/api/index.d.ts +3 -2
  11. package/dist/api/index.js +1 -1
  12. package/dist/builtin-memory/crouter-sdk/README.md +22 -4
  13. package/dist/builtin-memory/crouter-sdk/client.md +32 -1
  14. package/dist/builtin-memory/crouter-sdk/errors.md +3 -0
  15. package/dist/builtin-memory/crouter-sdk/getting-started.md +26 -4
  16. package/dist/builtin-memory/crouter-sdk/release-notes.md +23 -0
  17. package/dist/builtin-memory/crouter-sdk/testing.md +9 -3
  18. package/dist/builtin-memory/internal/nodes-and-canvas.md +4 -0
  19. package/dist/clients/attach/viewer.js +606 -606
  20. package/dist/commands/cron.js +1 -1
  21. package/dist/commands/node/create.js +1 -1
  22. package/dist/core/canvas/crons.d.ts +15 -1
  23. package/dist/core/canvas/crons.js +13 -13
  24. package/dist/core/canvas/delivery-contract.d.ts +1 -1
  25. package/dist/core/canvas/migrations.js +73 -66
  26. package/dist/core/canvas/types.d.ts +1 -1
  27. package/dist/core/command.d.ts +7 -0
  28. package/dist/core/command.js +9 -9
  29. package/dist/core/grants/mirror.d.ts +4 -0
  30. package/dist/core/grants/mirror.js +2 -2
  31. package/dist/core/layout.d.ts +2 -0
  32. package/dist/core/layout.js +2 -2
  33. package/dist/core/llm/run.d.ts +5 -1
  34. package/dist/core/llm/run.js +3 -3
  35. package/dist/core/providers/call.d.ts +2 -0
  36. package/dist/core/providers/call.js +1 -1
  37. package/dist/core/providers/errors.js +1 -1
  38. package/dist/core/providers/receipts.d.ts +2 -0
  39. package/dist/core/providers/receipts.js +2 -3
  40. package/dist/core/runs/operations.d.ts +2 -0
  41. package/dist/core/runs/operations.js +4 -4
  42. package/dist/core/runtime/broker/model-adapter.js +2 -2
  43. package/dist/core/runtime/run-command.d.ts +6 -0
  44. package/dist/core/runtime/run-command.js +1 -1
  45. package/dist/core/spaces/mounts.d.ts +2 -0
  46. package/dist/core/spaces/mounts.js +1 -1
  47. package/dist/core/spaces/process.d.ts +14 -2
  48. package/dist/core/spaces/process.js +1 -1
  49. package/dist/core/usage/ledger.d.ts +49 -0
  50. package/dist/core/usage/ledger.js +10 -0
  51. package/dist/daemon/api/app-listener.js +12 -12
  52. package/dist/daemon/api/handlers/crons.js +1 -1
  53. package/dist/daemon/api/handlers/llm.d.ts +8 -0
  54. package/dist/daemon/api/handlers/llm.js +3 -3
  55. package/dist/daemon/api/map.d.ts +1 -1
  56. package/dist/daemon/api/map.js +1 -1
  57. package/dist/daemon/api/operations.js +1 -1
  58. package/dist/daemon/cron/armed-context.d.ts +21 -4
  59. package/dist/daemon/cron/armed-context.js +1 -1
  60. package/dist/daemon/cron/authority.d.ts +27 -0
  61. package/dist/daemon/cron/authority.js +1 -0
  62. package/dist/daemon/cron/disposition.d.ts +2 -0
  63. package/dist/daemon/cron/disposition.js +1 -1
  64. package/dist/daemon/cron/escalation.js +3 -3
  65. package/dist/daemon/cron/folders.d.ts +5 -0
  66. package/dist/daemon/cron/folders.js +1 -0
  67. package/dist/daemon/cron/lease-recovery.js +1 -1
  68. package/dist/daemon/cron/settle.d.ts +10 -0
  69. package/dist/daemon/cron/settle.js +1 -1
  70. package/dist/daemon/cron/sinks.d.ts +2 -1
  71. package/dist/daemon/cron/sinks.js +2 -2
  72. package/dist/daemon/cron-run.js +1 -1
  73. package/dist/daemon/reconcilers/action-lane.js +1 -1
  74. package/dist/daemon/reconcilers/broker-supervision.d.ts +10 -0
  75. package/dist/daemon/reconcilers/broker-supervision.js +4 -4
  76. package/dist/daemon/reconcilers/cron-lane.d.ts +1 -0
  77. package/dist/daemon/reconcilers/cron-lane.js +1 -1
  78. package/docs/sdk/README.md +22 -4
  79. package/docs/sdk/client.md +32 -1
  80. package/docs/sdk/errors.md +3 -0
  81. package/docs/sdk/getting-started.md +26 -4
  82. package/docs/sdk/release-notes.md +23 -0
  83. package/docs/sdk/testing.md +9 -3
  84. package/package.json +2 -2
  85. package/packages/crouter-identity/dist/index.d.ts +29 -2
  86. package/packages/crouter-identity/dist/index.js +81 -12
  87. package/packages/crouter-identity/package.json +1 -1
  88. package/runtime.lock.json +12 -12
@@ -18,7 +18,7 @@ export type Lifecycle = 'terminal' | 'resident';
18
18
  export type Mode = 'base' | 'orchestrator';
19
19
  /** Why a node last stopped — drives the daemon's reap-vs-revive decision. */
20
20
  export type ExitIntent = 'done' | 'refresh' | 'idle-release' | 'parked' | null;
21
- export type TerminalReason = 'finalized' | 'finished' | 'parked' | 'closed' | 'retired' | 'crashed' | 'stranded' | 'boot_failed' | 'crash_looped' | 'launch_failed' | 'context_overflow' | 'deadline_exceeded' | 'provider_fatal' | 'declined' | 'out_of_memory';
21
+ export type TerminalReason = 'finalized' | 'finished' | 'parked' | 'closed' | 'retired' | 'crashed' | 'stranded' | 'boot_failed' | 'crash_looped' | 'launch_failed' | 'context_overflow' | 'deadline_exceeded' | 'provider_fatal' | 'declined' | 'out_of_memory' | 'cap_exhausted';
22
22
  export type NodeOutcomeKind = 'result' | 'failure';
23
23
  /** Maximum serialized opaque action payload; enforced at registration in Phase 5. */
24
24
  export declare const OUTCOME_PAYLOAD_MAX_BYTES: number;
@@ -180,6 +180,13 @@ export declare function renderNode(node: AnyNode): string;
180
180
  * the full path to the matched node (not just its local name), so the recovery
181
181
  * hint is a command that actually exists. No fuzzy matching. */
182
182
  export declare function unknownPathError(node: AnyNode, path: string[], bad: string): CrtrError;
183
+ /** crouter cloud's account site: where a person creates a join token. */
184
+ export declare const DEFAULT_DIRECTORY_ISSUER = "https://account.northlight.ai";
185
+ /** An unknown top-level command on a runtime never joined to a directory
186
+ * (no `runtime.issuer`). Every `cloud` leaf and every other provider command
187
+ * is installed by grant sync, which runs only on a joined runtime, so the
188
+ * likeliest cause of a missing command there is the missing join. */
189
+ export declare function notJoinedError(bad: string): CrtrError;
183
190
  /** Options for parseArgv. */
184
191
  export interface ParseArgvOptions {
185
192
  /** Optional collector for tracking which parameters were explicitly provided.
@@ -1,10 +1,10 @@
1
- var z=Object.defineProperty;var f=(e,i)=>z(e,"name",{value:i,configurable:!0});import{renderRoot as q,renderBranch as F,renderLeafArgv as O,withAsyncHelpState as J,collapseWideChildren as G,rootPromotedChildren as B}from"./help.js";import{beginPreview as K,publishPreviewResult as L,readStdinRaw as I,peekStdinRaw as D,emit as Y,handle as Q,setJsonOutput as X,isJsonOutput as Z}from"./io.js";import{renderResult as ee}from"./render.js";import{CrtrError as R}from"./errors.js";import{isManifestStaleError as ne}from"./manifest-stale.js";import{operationIdContext as te}from"./events/operation-id.js";import{ExitCode as C}from"../types.js";import{envNodeId as ie}from"../shared/env.js";import{readFileSync as re}from"node:fs";import{spawnSync as ae}from"node:child_process";import{constants as oe}from"node:os";import{isatty as se}from"node:tty";import{resolve as le}from"node:path";function V(e){if(e===null||typeof e!="object"||Object.isFrozen(e))return e;for(const i of Object.values(e))V(i);return Object.freeze(e)}f(V,"freezeCommandMetadata");function Te(e){const i=V(e.help),{helpState:r}=e;if(r!==void 0&&e.effectiveHelp!==void 0)throw new Error(`leaf ${e.name} declares both helpState and effectiveHelp`);return Object.freeze({kind:"leaf",name:e.name,description:e.description,whenToUse:e.whenToUse,tier:e.tier,help:i,effectiveHelp:e.effectiveHelp??(r===void 0?void 0:n=>J(i,r,n)),slash:e.slash===void 0?void 0:V(e.slash),hookEligible:e.hookEligible,plugin:e.plugin,run:e.run,render:e.render,preview:e.preview,stdoutExact:e.stdoutExact})}f(Te,"defineLeaf");function de(e){return e.kind!=="branch"?0:(e.help.listing??[]).filter(i=>i.tier!=="hidden").length}f(de,"visibleSubCount");function Oe(e){if(e.passthrough!==void 0&&e.children.length>0)throw new Error(`defineBranch("${e.name}"): a passthrough branch must declare no children (found ${e.children.length})`);return e.help.listing=e.children.map(i=>{const r=de(i);return{name:i.name,description:i.description??"",whenToUse:i.whenToUse??"",tier:i.tier??"normal",kind:i.kind,...i.plugin!==void 0?{plugin:i.plugin}:{},...r>0?{subCount:r}:{}}}),{kind:"branch",name:e.name,description:e.description,whenToUse:e.whenToUse,tier:e.tier,help:e.help,effectiveHelp:e.effectiveHelp,rootEntry:e.rootEntry,extensible:e.extensible,plugin:e.plugin,slash:e.slash,passthrough:e.passthrough,children:e.children}}f(Oe,"defineBranch");function Ve(e){const i=[],r=f(n=>{if(n.slash!==void 0&&i.push(n.slash),n.kind==="branch")for(const a of n.children)r(a)},"visit");for(const n of e.subtrees)r(n);return i}f(Ve,"collectSlashSpecs");function We(e){const i=e.subtrees.filter(n=>n.rootEntry!==void 0).map(n=>{const a=(n.help.listing??[]).filter(s=>s.tier!=="hidden"),d=B(a).map(s=>({path:`${n.name} ${s.name}`,desc:s.tier==="important"?s.description:void 0}));return{name:n.name,concept:n.rootEntry.concept,desc:n.rootEntry.desc,useWhen:n.rootEntry.useWhen,dynamicState:n.rootEntry.dynamicState,subcommands:d.length>0?d:void 0,otherSubcommandCount:a.length-d.length}});return{kind:"root",help:{tagline:e.tagline,commands:i,globals:e.globals},subtrees:e.subtrees}}f(We,"defineRoot");function fe(e){if(e.kind==="root")return e.subtrees.filter(i=>i.tier!=="hidden").map(i=>i.name);if(e.kind==="branch"){const{listed:i,unlisted:r}=G(e.children.filter(n=>n.tier!=="hidden"));return[...i.map(n=>n.name),...r>0?[`(and ${r} more)`]:[]]}return[]}f(fe,"childNames");function ue(e,i){let r=e;const n=[];let a=0;for(;a<i.length;){const d=i[a];if(d==="-h"||d==="--help")break;if(r.kind==="root"){const s=r.subtrees.find(o=>o.name===d);if(s===void 0)break;r=s,n.push(s.name),a++}else if(r.kind==="branch"){const s=r.children.find(o=>o.name===d);if(s===void 0)break;r=s,n.push(s.name),a++}else break}return{node:r,path:n,remaining:i.slice(a)}}f(ue,"walk");function ce(e){return e.kind==="root"?q(e.help):e.kind==="branch"?F(e.help):O(e.help)}f(ce,"renderNode");async function M(e,i,r){if(r!==void 0&&e.kind==="leaf")return O(e.effectiveHelp!==void 0?await e.effectiveHelp(r):e.help,r);const n=e.kind==="leaf"&&e.effectiveHelp!==void 0?O(await e.effectiveHelp()):e.kind==="branch"&&e.effectiveHelp!==void 0?F(await e.effectiveHelp()):ce(e);if(i.length===0)return n;const{hasEffectiveCommandPlugins:a}=await import("./command-plugins/presence.js");if(!a())return n;const[{collectHelpAddenda:d},{SUBTREE_NAMES:s,coreCommandPathsCovering:o,extensibleCoreBranches:x}]=await Promise.all([import("./command-plugins/help-addenda.js"),import("../build-root.js")]),w=await d(i.join(" "),{reservedCoreNames:new Set(s),coreCommandPaths:o,extensibleCoreBranches:x});return n+w.map($=>`
1
+ var M=Object.defineProperty;var d=(e,i)=>M(e,"name",{value:i,configurable:!0});import{renderRoot as V,renderBranch as q,renderLeafArgv as O,withAsyncHelpState as z,collapseWideChildren as G,rootPromotedChildren as I}from"./help.js";import{beginPreview as L,publishPreviewResult as K,readStdinRaw as B,peekStdinRaw as D,emit as Y,handle as Q,setJsonOutput as X,isJsonOutput as Z}from"./io.js";import{renderResult as ee}from"./render.js";import{CrtrError as H}from"./errors.js";import{isManifestStaleError as ne}from"./manifest-stale.js";import{operationIdContext as te}from"./events/operation-id.js";import{ExitCode as $}from"../types.js";import{envNodeId as ie}from"../shared/env.js";import{readFileSync as re}from"node:fs";import{spawnSync as oe}from"node:child_process";import{constants as ae}from"node:os";import{isatty as se}from"node:tty";import{resolve as le}from"node:path";function W(e){if(e===null||typeof e!="object"||Object.isFrozen(e))return e;for(const i of Object.values(e))W(i);return Object.freeze(e)}d(W,"freezeCommandMetadata");function Fe(e){const i=W(e.help),{helpState:r}=e;if(r!==void 0&&e.effectiveHelp!==void 0)throw new Error(`leaf ${e.name} declares both helpState and effectiveHelp`);return Object.freeze({kind:"leaf",name:e.name,description:e.description,whenToUse:e.whenToUse,tier:e.tier,help:i,effectiveHelp:e.effectiveHelp??(r===void 0?void 0:n=>z(i,r,n)),slash:e.slash===void 0?void 0:W(e.slash),hookEligible:e.hookEligible,plugin:e.plugin,run:e.run,render:e.render,preview:e.preview,stdoutExact:e.stdoutExact})}d(Fe,"defineLeaf");function de(e){return e.kind!=="branch"?0:(e.help.listing??[]).filter(i=>i.tier!=="hidden").length}d(de,"visibleSubCount");function Ve(e){if(e.passthrough!==void 0&&e.children.length>0)throw new Error(`defineBranch("${e.name}"): a passthrough branch must declare no children (found ${e.children.length})`);return e.help.listing=e.children.map(i=>{const r=de(i);return{name:i.name,description:i.description??"",whenToUse:i.whenToUse??"",tier:i.tier??"normal",kind:i.kind,...i.plugin!==void 0?{plugin:i.plugin}:{},...r>0?{subCount:r}:{}}}),{kind:"branch",name:e.name,description:e.description,whenToUse:e.whenToUse,tier:e.tier,help:e.help,effectiveHelp:e.effectiveHelp,rootEntry:e.rootEntry,extensible:e.extensible,plugin:e.plugin,slash:e.slash,passthrough:e.passthrough,children:e.children}}d(Ve,"defineBranch");function qe(e){const i=[],r=d(n=>{if(n.slash!==void 0&&i.push(n.slash),n.kind==="branch")for(const o of n.children)r(o)},"visit");for(const n of e.subtrees)r(n);return i}d(qe,"collectSlashSpecs");function Ie(e){const i=e.subtrees.filter(n=>n.rootEntry!==void 0).map(n=>{const o=(n.help.listing??[]).filter(s=>s.tier!=="hidden"),f=I(o).map(s=>({path:`${n.name} ${s.name}`,desc:s.tier==="important"?s.description:void 0}));return{name:n.name,concept:n.rootEntry.concept,desc:n.rootEntry.desc,useWhen:n.rootEntry.useWhen,dynamicState:n.rootEntry.dynamicState,subcommands:f.length>0?f:void 0,otherSubcommandCount:o.length-f.length}});return{kind:"root",help:{tagline:e.tagline,commands:i,globals:e.globals},subtrees:e.subtrees}}d(Ie,"defineRoot");function fe(e){if(e.kind==="root")return e.subtrees.filter(i=>i.tier!=="hidden").map(i=>i.name);if(e.kind==="branch"){const{listed:i,unlisted:r}=G(e.children.filter(n=>n.tier!=="hidden"));return[...i.map(n=>n.name),...r>0?[`(and ${r} more)`]:[]]}return[]}d(fe,"childNames");function ce(e,i){let r=e;const n=[];let o=0;for(;o<i.length;){const f=i[o];if(f==="-h"||f==="--help")break;if(r.kind==="root"){const s=r.subtrees.find(a=>a.name===f);if(s===void 0)break;r=s,n.push(s.name),o++}else if(r.kind==="branch"){const s=r.children.find(a=>a.name===f);if(s===void 0)break;r=s,n.push(s.name),o++}else break}return{node:r,path:n,remaining:i.slice(o)}}d(ce,"walk");function ue(e){return e.kind==="root"?V(e.help):e.kind==="branch"?q(e.help):O(e.help)}d(ue,"renderNode");async function J(e,i,r){if(r!==void 0&&e.kind==="leaf")return O(e.effectiveHelp!==void 0?await e.effectiveHelp(r):e.help,r);const n=e.kind==="leaf"&&e.effectiveHelp!==void 0?O(await e.effectiveHelp()):e.kind==="branch"&&e.effectiveHelp!==void 0?q(await e.effectiveHelp()):ue(e);if(i.length===0)return n;const{hasEffectiveCommandPlugins:o}=await import("./command-plugins/presence.js");if(!o())return n;const[{collectHelpAddenda:f},{SUBTREE_NAMES:s,coreCommandPathsCovering:a,extensibleCoreBranches:x}]=await Promise.all([import("./command-plugins/help-addenda.js"),import("../build-root.js")]),w=await f(i.join(" "),{reservedCoreNames:new Set(s),coreCommandPaths:a,extensibleCoreBranches:x});return n+w.map(S=>`
2
2
 
3
- <plugin-help plugin="${$.plugin}">
4
- ${$.text}
5
- </plugin-help>`).join("")}f(M,"renderNodeWithAddenda");function he(e){return e.some(i=>i==="-h"||i==="--help")}f(he,"helpRequested");function pe(e,i){if(e.kind!=="leaf"||i.length!==2)return;const[r,n]=i;if(n!=="-h"&&n!=="--help"||!r.startsWith("--")||r.includes("="))return;const a=r.slice(2);return(e.help.params??[]).find(s=>s.kind==="flag"&&s.name===a)?.focusedHelp!==void 0?a:void 0}f(pe,"focusedHelpFlag");function me(e,i){let r=0;for(let n=0;n<e.length;n++){if(r===i.length)return e.slice(n);e[n]!=="--json"&&e[n]===i[r]&&r++}return[]}f(me,"rawArgsAfterPath");function ge(e,i){const r=ae(e.bin,i,{stdio:"inherit"});if(r.error!==void 0){const n=r.error;throw new R("binary_missing",`${e.bin} could not be run (${n.code??n.message})`,C.USAGE,{received:e.bin,next:e.installHint})}if(r.status!==null&&process.exit(r.status),r.signal!==null){const n=oe.signals[r.signal];n!==void 0&&process.exit(128+n)}process.exit(1)}f(ge,"runPassthrough");function we(e,i,r){const n=fe(e),a=n.length>0?n.join(", "):"(none)",d=i.length>0?`crtr ${i.join(" ")} -h`:"crtr -h",s=n.some(o=>o.startsWith("(and "));return new R("unknown_path",`unknown subcommand: ${r}`,C.USAGE,{received:r,next:s?`Valid children include: ${a}. Run \`${d}\` for how to find the rest.`:`Valid children: ${a}. Run \`${d}\` for the full list.`})}f(we,"unknownPathError");function m(e,i,r,n,a){return new R(e,i,C.USAGE,{received:r,field:n,next:a!==void 0?a:"Run the command with -h to see the parameter schema."})}f(m,"parseArgvError");function v(e){return e.replace(/-([a-z])/g,(i,r)=>r.toUpperCase())}f(v,"flagNameToKey");async function be(e,i,r){const n={},a=new Set,d=new Map,s=e.filter(t=>t.kind==="positional"),o=e.find(t=>t.kind==="stdin"),x=e.find(t=>t.kind==="context-file"),w=e.filter(t=>t.kind==="flag"),$=new Map(w.map(t=>[t.name,t])),y=w.some(t=>t.sensitive===!0);for(const t of w)t.type==="bool"?n[v(t.name)]=t.default!==void 0?t.default:!1:t.default!==void 0&&(n[v(t.name)]=t.default);const g=[];let u=0,k=!1,A=!1,b=0;for(;b<i.length;){const t=i[b];if(t==="--"&&!A){A=!0,b++;continue}if(!A&&t.startsWith("--")){const h=t.indexOf("=");let l,S;if(h!==-1?(l=t.slice(2,h),S=t.slice(h+1)):l=t.slice(2),l==="context-file"){if(x===void 0)throw m("unknown_flag","unknown flag: --context-file","--context-file",void 0,"This leaf does not accept --context-file.");const p=S!==void 0?S:i[++b];if(p===void 0)throw m("missing_parameter","--context-file requires a PATH argument","--context-file","context-file");let N;try{N=re(p,"utf8")}catch{throw m("invalid_type",`--context-file: cannot read file: ${p}`,p,"context-file","Provide a readable file path.")}let W;try{W=JSON.parse(N)}catch{throw m("invalid_type",`--context-file: file is not valid JSON: ${p}`,p,"context-file","Ensure the file contains a valid JSON object.")}const T=v(x.name);n[T]=W,d.set(T,le(p)),a.add(T),b++;continue}const E=$.get(l);if(E===void 0)throw m("unknown_flag",`unknown flag: --${l}`,`--${l}`,void 0,"Use --<flag-name> for declared flags only. Run -h for the schema.");const j=v(l);if(a.add(j),E.type==="bool"){if(S!==void 0)throw m("invalid_type",`boolean flag --${l} takes no value`,`--${l}=${S}`,l,`Use --${l} (presence = true) with no value.`);n[j]=!0,b++;continue}const P=S!==void 0?S:i[++b];if(P===void 0||S===void 0&&P.startsWith("--")){const p=E.focusedHelp!==void 0&&(r?.leafPath?.length??0)>0?`Run \`crtr ${r.leafPath.join(" ")} --${l} -h\` and read the focused value contract before retrying.`:void 0;throw m("missing_parameter",`--${l} requires a value`,`--${l}`,l,p)}let H;if(E.type==="int"){const p=Number(P);if(!Number.isInteger(p))throw m("invalid_type",`--${l} must be an integer`,P,l,`Provide an integer value for --${l}.`);H=p}else if(E.type==="enum"){const p=E.choices;if(p!==void 0&&!p.includes(P))throw m("invalid_type",`--${l} must be one of: ${p.join(", ")}`,P,l,`Retry with one of: ${p.join(", ")}.`);H=P}else H=P;if(E.repeatable===!0){const p=n[j],N=Array.isArray(p)?p:[];N.push(H),n[j]=N}else n[j]=H;b++;continue}const c=s[u];if(c!==void 0){const h=v(c.name);if(c.repeatable===!0){const l=n[h]??[];l.push(t),n[h]=l}else n[h]=t,u++;g.push(t),k=!0,a.add(h),b++;continue}if(g.length>0){if(o!==void 0&&s.length===0){g.push(t),k=!0,b++;continue}throw o!==void 0?m("bad_invocation",`unexpected extra positional argument (value withheld: this leaf reads "${o.name}" from stdin, never argv)`,void 0,void 0,"Pipe it on stdin instead of passing it as a positional argument."):m("bad_invocation",y?"unexpected extra positional argument (value withheld: this leaf takes a secret)":`unexpected extra positional argument: ${t}`,y?void 0:i.join(" "),void 0,"Use declared flags or positional parameters. Run -h for the schema.")}if(s.length===0&&(o===void 0||o.allowPositional===!1))throw m("bad_invocation",y?"this leaf takes no positional arguments (value withheld: this leaf takes a secret)":`this leaf takes no positional arguments: ${t}`,y?void 0:t,void 0,"Use --flag for parameters. Run -h for the schema.");g.push(t),k=!0,b++}let U=!1;if(o!==void 0){if(g.length>1&&s.length===0){const t=o.positionalStdinConflict?.(n);throw t!==void 0?m("bad_invocation",t.message,void 0,t.field,t.next):m("bad_invocation",`unexpected extra positional argument (value withheld: this leaf reads "${o.name}" from stdin, never argv)`,void 0,void 0,"Pipe it on stdin instead of passing it as a positional argument.")}if(g.length>0&&s.length===0&&o.allowPositional!==!1){const t=g[0];if(t==="-"){const c=await I();(c.trim()!==""||!o.required)&&(n[v(o.name)]=c),U=!0}else{if((await D()).trim()!==""){const h=o.positionalStdinConflict?.(n);throw m("bad_invocation",h?.message??`both a positional argument ("${t}") and piped stdin were provided for ${o.name}`,t,h?.field??o.name,h?.next??"Pass exactly one: either the positional argument or piped stdin, not both.")}n[v(o.name)]=t,U=!0}}else{const t=se(0),c=await I();(c.trim()!==""||!o.required)&&(n[v(o.name)]=c),U=!t||k}}U&&o!==void 0&&a.add(v(o.name));for(const t of e){if(t.kind!=="flag"&&t.kind!=="positional"||t.defaultFromEnv===void 0)continue;const c=v(t.name);if(a.has(c))continue;const h=process.env[t.defaultFromEnv];h===void 0||h===""||(n[c]=t.kind==="positional"&&t.repeatable===!0?[h]:h,a.add(c))}const _=[];for(const t of e){const c=v(t.name),h=t.kind==="flag"&&t.type==="bool"&&!a.has(c);if(t.required&&(h||n[c]===void 0||n[c]===null)){const l=t.kind==="positional"?`positional ${t.name.toUpperCase()}`:t.kind==="flag"?`--${t.name}`:t.kind==="context-file"?"--context-file":"stdin";_.push({display:l,name:t.name})}}if(_.length>0){const t=_.map(l=>l.display).join(", "),c=r?.leafPath,h=c!==void 0&&c.length>0?`\`crtr ${c.join(" ")} -h\``:"this command with -h";throw m("missing_parameter",_.length===1?`required parameter is missing: ${t}`:`required parameters are missing: ${t}`,void 0,_.map(l=>l.name).join(", "),`Run ${h} and read the full schema before re-issuing \u2014 do not guess parameters one at a time.`)}return r?.onProvidedParams!==void 0&&r.onProvidedParams(a),r?.onContextFilePaths!==void 0&&r.onContextFilePaths(d),n}f(be,"parseArgv");const ve=new Set(["--json","--no-autostart"]);async function qe(e,i){const r=i.slice(2);r.includes("--json")&&X(!0);const n=r.filter(o=>!ve.has(o));if(n.length===0||n.length===1&&(n[0]==="-h"||n[0]==="--help")){const o=ie()?{...e.help,commands:await Promise.all(e.help.commands.map(async x=>{const w=e.subtrees.find(u=>u.name===x.name);if(w?.effectiveHelp===void 0)return x;const y=((await w.effectiveHelp()).listing??[]).filter(u=>u.tier!=="hidden"),g=B(y).map(u=>({path:`${w.name} ${u.name}`,desc:u.tier==="important"?u.description:void 0}));return{...x,subcommands:g.length?g:void 0,otherSubcommandCount:y.length-g.length}}))}:e.help;process.stdout.write(q(o)+`
6
- `),process.exitCode=C.SUCCESS;return}if(n.length===1&&(n[0]==="--version"||n[0]==="-v")){const{readPackageVersion:o}=await import("../commands/sys/shared.js");process.stdout.write(o()+`
7
- `),process.exitCode=C.SUCCESS;return}let{node:a,path:d,remaining:s}=ue(e,n);try{if(a.kind==="branch"&&a.passthrough!==void 0&&ge(a.passthrough,me(r,d)),a.kind==="branch"&&a.unavailable!==void 0&&s.length>0&&s[0]!=="-h"&&s[0]!=="--help")throw new R("provider_unavailable",a.unavailable,C.NETWORK,{next:"The daemon retries bundle installation on grant sync; retry after the provider becomes available."});if((a.kind==="root"||a.kind==="branch")&&s.length>0&&s[0]!=="-h"&&s[0]!=="--help")throw we(a,d,s[0]);if(he(s)){process.stdout.write(await M(a,d,pe(a,s))+`
8
- `),process.exitCode=C.SUCCESS;return}if(a.kind==="root"||a.kind==="branch"){process.stdout.write(await M(a,d)+`
9
- `),process.exitCode=C.SUCCESS;return}const o=a;K(d.join(" "));const x=o.help.params!==void 0?o.help.params:[];let w=new Set,$=new Map;const y=await be(x,s,{leafPath:d,onProvidedParams:f(k=>{w=k},"onProvidedParams"),onContextFilePaths:f(k=>{$=k},"onContextFilePaths")}),g={providedParams:w,contextFilePaths:$},u=await te.fresh(()=>o.run(y,g));if(L(u==null?u:o.preview?.(u)??u),u!=null)if(Z())Y(u);else{const k=o.render!==void 0?o.render(u):ee(u,o.help);process.stdout.write(k+(o.stdoutExact?.(u)?"":`
10
- `))}}catch(o){if(ne(o))throw o;Q(o)}}f(qe,"runCli");export{ve as GLOBAL_TOKENS,Ve as collectSlashSpecs,Oe as defineBranch,Te as defineLeaf,We as defineRoot,be as parseArgv,ce as renderNode,qe as runCli,we as unknownPathError,ue as walk};
3
+ <plugin-help plugin="${S.plugin}">
4
+ ${S.text}
5
+ </plugin-help>`).join("")}d(J,"renderNodeWithAddenda");function he(e){return e.some(i=>i==="-h"||i==="--help")}d(he,"helpRequested");function pe(e,i){if(e.kind!=="leaf"||i.length!==2)return;const[r,n]=i;if(n!=="-h"&&n!=="--help"||!r.startsWith("--")||r.includes("="))return;const o=r.slice(2);return(e.help.params??[]).find(s=>s.kind==="flag"&&s.name===o)?.focusedHelp!==void 0?o:void 0}d(pe,"focusedHelpFlag");function me(e,i){let r=0;for(let n=0;n<e.length;n++){if(r===i.length)return e.slice(n);e[n]!=="--json"&&e[n]===i[r]&&r++}return[]}d(me,"rawArgsAfterPath");function ge(e,i){const r=oe(e.bin,i,{stdio:"inherit"});if(r.error!==void 0){const n=r.error;throw new H("binary_missing",`${e.bin} could not be run (${n.code??n.message})`,$.USAGE,{received:e.bin,next:e.installHint})}if(r.status!==null&&process.exit(r.status),r.signal!==null){const n=ae.signals[r.signal];n!==void 0&&process.exit(128+n)}process.exit(1)}d(ge,"runPassthrough");function we(e,i,r){const n=fe(e),o=n.length>0?n.join(", "):"(none)",f=i.length>0?`crtr ${i.join(" ")} -h`:"crtr -h",s=n.some(a=>a.startsWith("(and "));return new H("unknown_path",`unknown subcommand: ${r}`,$.USAGE,{received:r,next:s?`Valid children include: ${o}. Run \`${f}\` for how to find the rest.`:`Valid children: ${o}. Run \`${f}\` for the full list.`})}d(we,"unknownPathError");const be="https://account.northlight.ai";function ve(e){const i=be,r=`open ${i}/account/machines, choose Create join token, then run \`crtr sys join --directory ${i} --token <token>\``;return new H("not_joined",`\`crtr ${e}\` isn't a command on this machine. If it comes from crouter cloud, this machine isn't joined to your account yet: ${r}.`,$.USAGE,{received:e,next:`${r[0].toUpperCase()}${r.slice(1)}.`})}d(ve,"notJoinedError");async function xe(){try{const{readConfig:e}=await import("./config.js");return!e("user").runtime?.issuer}catch{return!1}}d(xe,"runtimeUnjoined");function m(e,i,r,n,o){return new H(e,i,$.USAGE,{received:r,field:n,next:o!==void 0?o:"Run the command with -h to see the parameter schema."})}d(m,"parseArgvError");function v(e){return e.replace(/-([a-z])/g,(i,r)=>r.toUpperCase())}d(v,"flagNameToKey");async function ke(e,i,r){const n={},o=new Set,f=new Map,s=e.filter(t=>t.kind==="positional"),a=e.find(t=>t.kind==="stdin"),x=e.find(t=>t.kind==="context-file"),w=e.filter(t=>t.kind==="flag"),S=new Map(w.map(t=>[t.name,t])),y=w.some(t=>t.sensitive===!0);for(const t of w)t.type==="bool"?n[v(t.name)]=t.default!==void 0?t.default:!1:t.default!==void 0&&(n[v(t.name)]=t.default);const g=[];let c=0,k=!1,A=!1,b=0;for(;b<i.length;){const t=i[b];if(t==="--"&&!A){A=!0,b++;continue}if(!A&&t.startsWith("--")){const h=t.indexOf("=");let l,C;if(h!==-1?(l=t.slice(2,h),C=t.slice(h+1)):l=t.slice(2),l==="context-file"){if(x===void 0)throw m("unknown_flag","unknown flag: --context-file","--context-file",void 0,"This leaf does not accept --context-file.");const p=C!==void 0?C:i[++b];if(p===void 0)throw m("missing_parameter","--context-file requires a PATH argument","--context-file","context-file");let R;try{R=re(p,"utf8")}catch{throw m("invalid_type",`--context-file: cannot read file: ${p}`,p,"context-file","Provide a readable file path.")}let F;try{F=JSON.parse(R)}catch{throw m("invalid_type",`--context-file: file is not valid JSON: ${p}`,p,"context-file","Ensure the file contains a valid JSON object.")}const T=v(x.name);n[T]=F,f.set(T,le(p)),o.add(T),b++;continue}const E=S.get(l);if(E===void 0)throw m("unknown_flag",`unknown flag: --${l}`,`--${l}`,void 0,"Use --<flag-name> for declared flags only. Run -h for the schema.");const U=v(l);if(o.add(U),E.type==="bool"){if(C!==void 0)throw m("invalid_type",`boolean flag --${l} takes no value`,`--${l}=${C}`,l,`Use --${l} (presence = true) with no value.`);n[U]=!0,b++;continue}const P=C!==void 0?C:i[++b];if(P===void 0||C===void 0&&P.startsWith("--")){const p=E.focusedHelp!==void 0&&(r?.leafPath?.length??0)>0?`Run \`crtr ${r.leafPath.join(" ")} --${l} -h\` and read the focused value contract before retrying.`:void 0;throw m("missing_parameter",`--${l} requires a value`,`--${l}`,l,p)}let _;if(E.type==="int"){const p=Number(P);if(!Number.isInteger(p))throw m("invalid_type",`--${l} must be an integer`,P,l,`Provide an integer value for --${l}.`);_=p}else if(E.type==="enum"){const p=E.choices;if(p!==void 0&&!p.includes(P))throw m("invalid_type",`--${l} must be one of: ${p.join(", ")}`,P,l,`Retry with one of: ${p.join(", ")}.`);_=P}else _=P;if(E.repeatable===!0){const p=n[U],R=Array.isArray(p)?p:[];R.push(_),n[U]=R}else n[U]=_;b++;continue}const u=s[c];if(u!==void 0){const h=v(u.name);if(u.repeatable===!0){const l=n[h]??[];l.push(t),n[h]=l}else n[h]=t,c++;g.push(t),k=!0,o.add(h),b++;continue}if(g.length>0){if(a!==void 0&&s.length===0){g.push(t),k=!0,b++;continue}throw a!==void 0?m("bad_invocation",`unexpected extra positional argument (value withheld: this leaf reads "${a.name}" from stdin, never argv)`,void 0,void 0,"Pipe it on stdin instead of passing it as a positional argument."):m("bad_invocation",y?"unexpected extra positional argument (value withheld: this leaf takes a secret)":`unexpected extra positional argument: ${t}`,y?void 0:i.join(" "),void 0,"Use declared flags or positional parameters. Run -h for the schema.")}if(s.length===0&&(a===void 0||a.allowPositional===!1))throw m("bad_invocation",y?"this leaf takes no positional arguments (value withheld: this leaf takes a secret)":`this leaf takes no positional arguments: ${t}`,y?void 0:t,void 0,"Use --flag for parameters. Run -h for the schema.");g.push(t),k=!0,b++}let N=!1;if(a!==void 0){if(g.length>1&&s.length===0){const t=a.positionalStdinConflict?.(n);throw t!==void 0?m("bad_invocation",t.message,void 0,t.field,t.next):m("bad_invocation",`unexpected extra positional argument (value withheld: this leaf reads "${a.name}" from stdin, never argv)`,void 0,void 0,"Pipe it on stdin instead of passing it as a positional argument.")}if(g.length>0&&s.length===0&&a.allowPositional!==!1){const t=g[0];if(t==="-"){const u=await B();(u.trim()!==""||!a.required)&&(n[v(a.name)]=u),N=!0}else{if((await D()).trim()!==""){const h=a.positionalStdinConflict?.(n);throw m("bad_invocation",h?.message??`both a positional argument ("${t}") and piped stdin were provided for ${a.name}`,t,h?.field??a.name,h?.next??"Pass exactly one: either the positional argument or piped stdin, not both.")}n[v(a.name)]=t,N=!0}}else{const t=se(0),u=await B();(u.trim()!==""||!a.required)&&(n[v(a.name)]=u),N=!t||k}}N&&a!==void 0&&o.add(v(a.name));for(const t of e){if(t.kind!=="flag"&&t.kind!=="positional"||t.defaultFromEnv===void 0)continue;const u=v(t.name);if(o.has(u))continue;const h=process.env[t.defaultFromEnv];h===void 0||h===""||(n[u]=t.kind==="positional"&&t.repeatable===!0?[h]:h,o.add(u))}const j=[];for(const t of e){const u=v(t.name),h=t.kind==="flag"&&t.type==="bool"&&!o.has(u);if(t.required&&(h||n[u]===void 0||n[u]===null)){const l=t.kind==="positional"?`positional ${t.name.toUpperCase()}`:t.kind==="flag"?`--${t.name}`:t.kind==="context-file"?"--context-file":"stdin";j.push({display:l,name:t.name})}}if(j.length>0){const t=j.map(l=>l.display).join(", "),u=r?.leafPath,h=u!==void 0&&u.length>0?`\`crtr ${u.join(" ")} -h\``:"this command with -h";throw m("missing_parameter",j.length===1?`required parameter is missing: ${t}`:`required parameters are missing: ${t}`,void 0,j.map(l=>l.name).join(", "),`Run ${h} and read the full schema before re-issuing \u2014 do not guess parameters one at a time.`)}return r?.onProvidedParams!==void 0&&r.onProvidedParams(o),r?.onContextFilePaths!==void 0&&r.onContextFilePaths(f),n}d(ke,"parseArgv");const ye=new Set(["--json","--no-autostart"]);async function Be(e,i){const r=i.slice(2);r.includes("--json")&&X(!0);const n=r.filter(a=>!ye.has(a));if(n.length===0||n.length===1&&(n[0]==="-h"||n[0]==="--help")){const a=ie()?{...e.help,commands:await Promise.all(e.help.commands.map(async x=>{const w=e.subtrees.find(c=>c.name===x.name);if(w?.effectiveHelp===void 0)return x;const y=((await w.effectiveHelp()).listing??[]).filter(c=>c.tier!=="hidden"),g=I(y).map(c=>({path:`${w.name} ${c.name}`,desc:c.tier==="important"?c.description:void 0}));return{...x,subcommands:g.length?g:void 0,otherSubcommandCount:y.length-g.length}}))}:e.help;process.stdout.write(V(a)+`
6
+ `),process.exitCode=$.SUCCESS;return}if(n.length===1&&(n[0]==="--version"||n[0]==="-v")){const{readPackageVersion:a}=await import("../commands/sys/shared.js");process.stdout.write(a()+`
7
+ `),process.exitCode=$.SUCCESS;return}let{node:o,path:f,remaining:s}=ce(e,n);try{if(o.kind==="branch"&&o.passthrough!==void 0&&ge(o.passthrough,me(r,f)),o.kind==="branch"&&o.unavailable!==void 0&&s.length>0&&s[0]!=="-h"&&s[0]!=="--help")throw new H("provider_unavailable",o.unavailable,$.NETWORK,{next:"The daemon retries bundle installation on grant sync; retry after the provider becomes available."});if((o.kind==="root"||o.kind==="branch")&&s.length>0&&s[0]!=="-h"&&s[0]!=="--help")throw o.kind==="root"&&!s[0].startsWith("-")&&await xe()?ve(s[0]):we(o,f,s[0]);if(he(s)){process.stdout.write(await J(o,f,pe(o,s))+`
8
+ `),process.exitCode=$.SUCCESS;return}if(o.kind==="root"||o.kind==="branch"){process.stdout.write(await J(o,f)+`
9
+ `),process.exitCode=$.SUCCESS;return}const a=o;L(f.join(" "));const x=a.help.params!==void 0?a.help.params:[];let w=new Set,S=new Map;const y=await ke(x,s,{leafPath:f,onProvidedParams:d(k=>{w=k},"onProvidedParams"),onContextFilePaths:d(k=>{S=k},"onContextFilePaths")}),g={providedParams:w,contextFilePaths:S},c=await te.fresh(()=>a.run(y,g));if(K(c==null?c:a.preview?.(c)??c),c!=null)if(Z())Y(c);else{const k=a.render!==void 0?a.render(c):ee(c,a.help);process.stdout.write(k+(a.stdoutExact?.(c)?"":`
10
+ `))}}catch(a){if(ne(a))throw a;Q(a)}}d(Be,"runCli");export{be as DEFAULT_DIRECTORY_ISSUER,ye as GLOBAL_TOKENS,qe as collectSlashSpecs,Ve as defineBranch,Fe as defineLeaf,Ie as defineRoot,ve as notJoinedError,ke as parseArgv,ue as renderNode,Be as runCli,we as unknownPathError,ce as walk};
@@ -54,6 +54,8 @@ export interface GrantSyncState {
54
54
  export declare function grantSyncState(grantId: string): GrantSyncState | null;
55
55
  /** The mirrored state of the grant `sub` gave `grantee`, whatever its status, or null when the mirror lacks it. */
56
56
  export declare function grantSyncStateFor(grantee: string, sub: string): GrantSyncState | null;
57
+ /** The mirrored grant `grantId`, whatever its status, or null when the mirror lacks it or the row is unparsable. */
58
+ export declare function grantRecord(grantId: string): GrantRecord | null;
57
59
  export declare function activeGrant(grantId: string): ActiveGrant | null;
58
60
  export declare function grantFor(grantee: string, sub: string): ActiveGrant | null;
59
61
  export declare function activeGrantForApp(grantee: string): ActiveGrant | null;
@@ -64,6 +66,8 @@ export declare function runNoticeTarget(grantId: string): {
64
66
  grantee: string;
65
67
  } | null;
66
68
  export declare function terminalGrant(): ActiveGrant | null;
69
+ /** Where the grant mirror came from: `'local'` for an unjoined runtime's own grant, the directory issuer once joined, null before any install. */
70
+ export declare function grantsSource(): string | null;
67
71
  export declare function hasSyncedGrants(source: string): boolean;
68
72
  /** OWN-11 for a joined runtime: the unjoined runtime's local mirror grants
69
73
  * `user:local` full local scopes and is not the directory's. Delete it so no
@@ -1,4 +1,4 @@
1
- var g=Object.defineProperty;var r=(e,t)=>g(e,"name",{value:t,configurable:!0});import{createHash as _}from"node:crypto";import{prepared as s}from"../sqlite-statements.js";import{parseScope as y}from"@north-light/identity";import{openDb as i,withCanvasWrite as S}from"../canvas/db.js";const v=["not_started","pending","ready","failed"];function c(e){if(!Array.isArray(e))throw new TypeError("grant scopes must be a list");return e.map(t=>{if(!t||typeof t!="object"||typeof t.scope!="string"||!["always","ask"].includes(t.mode))throw new TypeError("invalid grant scope entry");return{scope:t.scope,mode:t.mode}})}r(c,"entries");function T(e){if(!e||typeof e!="object"||Array.isArray(e))return null;const{state:t,message:o,next:u}=e;return typeof t!="string"||!v.includes(t)||o!==void 0&&typeof o!="string"||typeof u!="string"||!u?null:{state:t,...o===void 0?{}:{message:o},next:u}}r(T,"dependencyOf");function l(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const t={};for(const[o,u]of Object.entries(e)){const n=T(u);n&&(t[o]=n)}return t}r(l,"dependenciesOf");function E(e){return{id:e.id,runtime:e.runtime,sub:e.sub,grantee:e.grantee,rev:e.rev,scopes:c(JSON.parse(e.scopes_json)),resolved_scopes:c(JSON.parse(e.resolved_scopes_json)),spend_cap:e.spend_cap,expires_at:e.expires_at,status:e.status,revoked_at:e.revoked_at,approved_at:e.approved_at,payer:e.payer,dependencies:l(JSON.parse(e.dependencies_json)),run_notice_url:e.run_notice_url??null,synced_at:e.synced_at}}r(E,"record");function p(e){if(!e||e.status!=="active")return null;try{return m(E(e))}catch(t){if(t instanceof TypeError||t instanceof SyntaxError)return null;throw t}}r(p,"active");function h(){return s(i(),"SELECT * FROM grants").all().flatMap(e=>{try{return[E(e)]}catch(t){if(t instanceof TypeError||t instanceof SyntaxError)return[];throw t}})}r(h,"allGrants");function m(e){if(e.status!=="active")return null;try{const t=e.resolved_scopes.map(({scope:o})=>y(o));return{...e,resolved:e.resolved_scopes.flatMap(({mode:o},u)=>o==="always"?[t[u]]:[])}}catch(t){if(t instanceof TypeError||t instanceof SyntaxError)return null;throw t}}r(m,"activeRecord");function d(e){return Array.isArray(e)?`[${e.map(d).join(",")}]`:e&&typeof e=="object"?`{${Object.keys(e).sort().filter(t=>e[t]!==void 0).map(t=>`${JSON.stringify(t)}:${d(e[t])}`).join(",")}}`:JSON.stringify(e)??"null"}r(d,"canonicalJson");function L(e){const t={rev:e.rev,resolved_scopes:e.resolved_scopes,spend_cap:e.spend_cap,payer:e.payer,dependencies:e.dependencies??{}};return _("sha256").update(d(t)).digest("base64url").slice(0,22)}r(L,"grantVersion");function F(e){return!!s(i(),"SELECT 1 FROM grants WHERE id = ?").get(e)}r(F,"hasGrant");function C(e){return s(i(),"SELECT rev, status FROM grants WHERE id = ?").get(e)??null}r(C,"grantSyncState");function M(e,t){return s(i(),"SELECT rev, status FROM grants WHERE grantee = ? AND sub = ?").get(e,t)??null}r(M,"grantSyncStateFor");function b(e){return p(s(i(),"SELECT * FROM grants WHERE id = ?").get(e))}r(b,"activeGrant");function j(e,t){return p(s(i(),"SELECT * FROM grants WHERE grantee = ? AND sub = ?").get(e,t))}r(j,"grantFor");function k(e){const t=s(i(),"SELECT * FROM grants WHERE grantee = ? AND status = 'active'").all(e);if(t.length>1)throw new Error(`multiple active grants for ${e}`);return p(t[0])}r(k,"activeGrantForApp");function D(e){const t=s(i(),"SELECT run_notice_url AS url, sub, grantee FROM grants WHERE id = ? AND status = 'active'").get(e);return t&&t.url?{url:t.url,sub:t.sub,grantee:t.grantee}:null}r(D,"runNoticeTarget");function H(){const e=s(i(),"SELECT * FROM grants WHERE grantee = 'app:terminal' AND status = 'active'").all();if(e.length>1)throw new Error("multiple active terminal grants");return p(e[0])}r(H,"terminalGrant");function W(e){return s(i(),"SELECT value FROM runtime_state WHERE key = 'grants_source'").get()?.value===e}r(W,"hasSyncedGrants");function G(e){return s(e,"SELECT value FROM runtime_state WHERE key = 'grants_source'").get()?.value!=="local"?0:(s(e,"DELETE FROM runtime_state WHERE key = 'grants_source'").run(),Number(s(e,"DELETE FROM grants").run().changes))}r(G,"dropLocalGrantMirror");function I(e,t,o){const u=new Set;for(const n of e){if(u.has(n.id)||!n.id||!n.runtime||!n.sub||!n.grantee||!Number.isInteger(n.rev)||!["active","revoked","removed"].includes(n.status))throw new TypeError("invalid grant sync response");u.add(n.id),c(n.scopes),c(n.resolved_scopes)}S(n=>{n.exec("DELETE FROM grants");const f=s(n,`INSERT INTO grants
1
+ var g=Object.defineProperty;var r=(e,t)=>g(e,"name",{value:t,configurable:!0});import{createHash as _}from"node:crypto";import{prepared as n}from"../sqlite-statements.js";import{parseScope as y}from"@north-light/identity";import{openDb as i,withCanvasWrite as S}from"../canvas/db.js";const v=["not_started","pending","ready","failed"];function c(e){if(!Array.isArray(e))throw new TypeError("grant scopes must be a list");return e.map(t=>{if(!t||typeof t!="object"||typeof t.scope!="string"||!["always","ask"].includes(t.mode))throw new TypeError("invalid grant scope entry");return{scope:t.scope,mode:t.mode}})}r(c,"entries");function T(e){if(!e||typeof e!="object"||Array.isArray(e))return null;const{state:t,message:a,next:u}=e;return typeof t!="string"||!v.includes(t)||a!==void 0&&typeof a!="string"||typeof u!="string"||!u?null:{state:t,...a===void 0?{}:{message:a},next:u}}r(T,"dependencyOf");function l(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const t={};for(const[a,u]of Object.entries(e)){const s=T(u);s&&(t[a]=s)}return t}r(l,"dependenciesOf");function E(e){return{id:e.id,runtime:e.runtime,sub:e.sub,grantee:e.grantee,rev:e.rev,scopes:c(JSON.parse(e.scopes_json)),resolved_scopes:c(JSON.parse(e.resolved_scopes_json)),spend_cap:e.spend_cap,expires_at:e.expires_at,status:e.status,revoked_at:e.revoked_at,approved_at:e.approved_at,payer:e.payer,dependencies:l(JSON.parse(e.dependencies_json)),run_notice_url:e.run_notice_url??null,synced_at:e.synced_at}}r(E,"record");function p(e){if(!e||e.status!=="active")return null;try{return m(E(e))}catch(t){if(t instanceof TypeError||t instanceof SyntaxError)return null;throw t}}r(p,"active");function h(){return n(i(),"SELECT * FROM grants").all().flatMap(e=>{try{return[E(e)]}catch(t){if(t instanceof TypeError||t instanceof SyntaxError)return[];throw t}})}r(h,"allGrants");function m(e){if(e.status!=="active")return null;try{const t=e.resolved_scopes.map(({scope:a})=>y(a));return{...e,resolved:e.resolved_scopes.flatMap(({mode:a},u)=>a==="always"?[t[u]]:[])}}catch(t){if(t instanceof TypeError||t instanceof SyntaxError)return null;throw t}}r(m,"activeRecord");function d(e){return Array.isArray(e)?`[${e.map(d).join(",")}]`:e&&typeof e=="object"?`{${Object.keys(e).sort().filter(t=>e[t]!==void 0).map(t=>`${JSON.stringify(t)}:${d(e[t])}`).join(",")}}`:JSON.stringify(e)??"null"}r(d,"canonicalJson");function L(e){const t={rev:e.rev,resolved_scopes:e.resolved_scopes,spend_cap:e.spend_cap,payer:e.payer,dependencies:e.dependencies??{}};return _("sha256").update(d(t)).digest("base64url").slice(0,22)}r(L,"grantVersion");function F(e){return!!n(i(),"SELECT 1 FROM grants WHERE id = ?").get(e)}r(F,"hasGrant");function C(e){return n(i(),"SELECT rev, status FROM grants WHERE id = ?").get(e)??null}r(C,"grantSyncState");function M(e,t){return n(i(),"SELECT rev, status FROM grants WHERE grantee = ? AND sub = ?").get(e,t)??null}r(M,"grantSyncStateFor");function b(e){const t=n(i(),"SELECT * FROM grants WHERE id = ?").get(e);if(!t)return null;try{return E(t)}catch(a){if(a instanceof TypeError||a instanceof SyntaxError)return null;throw a}}r(b,"grantRecord");function k(e){return p(n(i(),"SELECT * FROM grants WHERE id = ?").get(e))}r(k,"activeGrant");function H(e,t){return p(n(i(),"SELECT * FROM grants WHERE grantee = ? AND sub = ?").get(e,t))}r(H,"grantFor");function W(e){const t=n(i(),"SELECT * FROM grants WHERE grantee = ? AND status = 'active'").all(e);if(t.length>1)throw new Error(`multiple active grants for ${e}`);return p(t[0])}r(W,"activeGrantForApp");function j(e){const t=n(i(),"SELECT run_notice_url AS url, sub, grantee FROM grants WHERE id = ? AND status = 'active'").get(e);return t&&t.url?{url:t.url,sub:t.sub,grantee:t.grantee}:null}r(j,"runNoticeTarget");function D(){const e=n(i(),"SELECT * FROM grants WHERE grantee = 'app:terminal' AND status = 'active'").all();if(e.length>1)throw new Error("multiple active terminal grants");return p(e[0])}r(D,"terminalGrant");function G(){return n(i(),"SELECT value FROM runtime_state WHERE key = 'grants_source'").get()?.value??null}r(G,"grantsSource");function I(e){return n(i(),"SELECT value FROM runtime_state WHERE key = 'grants_source'").get()?.value===e}r(I,"hasSyncedGrants");function J(e){return n(e,"SELECT value FROM runtime_state WHERE key = 'grants_source'").get()?.value!=="local"?0:(n(e,"DELETE FROM runtime_state WHERE key = 'grants_source'").run(),Number(n(e,"DELETE FROM grants").run().changes))}r(J,"dropLocalGrantMirror");function U(e,t,a){const u=new Set;for(const s of e){if(u.has(s.id)||!s.id||!s.runtime||!s.sub||!s.grantee||!Number.isInteger(s.rev)||!["active","revoked","removed"].includes(s.status))throw new TypeError("invalid grant sync response");u.add(s.id),c(s.scopes),c(s.resolved_scopes)}S(s=>{s.exec("DELETE FROM grants");const f=n(s,`INSERT INTO grants
2
2
  (id, runtime, sub, grantee, rev, scopes_json, resolved_scopes_json, spend_cap, expires_at,
3
3
  status, revoked_at, approved_at, payer, dependencies_json, run_notice_url, synced_at)
4
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);for(const a of e)f.run(a.id,a.runtime,a.sub,a.grantee,a.rev,JSON.stringify(a.scopes),JSON.stringify(a.resolved_scopes),a.spend_cap,a.expires_at,a.status,a.revoked_at,a.approved_at,a.payer,JSON.stringify(l(a.dependencies)),a.run_notice_url??null,a.synced_at);s(n,"INSERT INTO runtime_state (key, value) VALUES ('grants_source', ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value").run(o),s(n,"INSERT INTO runtime_state (key, value) VALUES ('grants_generation', '1') ON CONFLICT(key) DO UPDATE SET value = CAST(CAST(value AS INTEGER) + 1 AS TEXT)").run()})}r(I,"replaceGrants");function J(){const e=s(i(),"SELECT value FROM runtime_state WHERE key = 'grants_generation'").get();return e?Number(e.value):0}r(J,"grantsGeneration");export{b as activeGrant,k as activeGrantForApp,m as activeRecord,h as allGrants,l as dependenciesOf,G as dropLocalGrantMirror,j as grantFor,C as grantSyncState,M as grantSyncStateFor,L as grantVersion,J as grantsGeneration,F as hasGrant,W as hasSyncedGrants,I as replaceGrants,D as runNoticeTarget,H as terminalGrant};
4
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);for(const o of e)f.run(o.id,o.runtime,o.sub,o.grantee,o.rev,JSON.stringify(o.scopes),JSON.stringify(o.resolved_scopes),o.spend_cap,o.expires_at,o.status,o.revoked_at,o.approved_at,o.payer,JSON.stringify(l(o.dependencies)),o.run_notice_url??null,o.synced_at);n(s,"INSERT INTO runtime_state (key, value) VALUES ('grants_source', ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value").run(a),n(s,"INSERT INTO runtime_state (key, value) VALUES ('grants_generation', '1') ON CONFLICT(key) DO UPDATE SET value = CAST(CAST(value AS INTEGER) + 1 AS TEXT)").run()})}r(U,"replaceGrants");function w(){const e=n(i(),"SELECT value FROM runtime_state WHERE key = 'grants_generation'").get();return e?Number(e.value):0}r(w,"grantsGeneration");export{k as activeGrant,W as activeGrantForApp,m as activeRecord,h as allGrants,l as dependenciesOf,J as dropLocalGrantMirror,H as grantFor,b as grantRecord,C as grantSyncState,M as grantSyncStateFor,L as grantVersion,w as grantsGeneration,G as grantsSource,F as hasGrant,I as hasSyncedGrants,U as replaceGrants,j as runNoticeTarget,D as terminalGrant};
@@ -22,6 +22,8 @@ export declare function appSpace(app: string): {
22
22
  home: string;
23
23
  cache: string;
24
24
  };
25
+ /** The folder an isolated cron runs in: `<app space>/crons/<cron_id>/`. It belongs to the cron, not to the node that armed it. */
26
+ export declare function cronFolder(app: string, cronId: string): string;
25
27
  /** The HOME an app's processes run with. With no sandbox (a Mac) the terminal is
26
28
  * the person at their own machine — its grant already holds all of the person's
27
29
  * files and nothing at the OS level confines its bash — so it gets the person's
@@ -1,2 +1,2 @@
1
- var w=Object.defineProperty;var t=(r,e)=>w(r,"name",{value:e,configurable:!0});import{existsSync as i,mkdirSync as m,readFileSync as E,statSync as R,writeFileSync as _}from"node:fs";import{homedir as S,userInfo as T}from"node:os";import{basename as p,dirname as O,isAbsolute as b,join as o,resolve as f}from"node:path";import{envHomeOverride as g}from"../shared/env.js";import{readConfig as A}from"./config.js";const l="1";function s(){const r=f(g()||(process.platform==="linux"?"/":o(S(),".crouter")));if($()&&r===z())throw new Error(`refusing to use the account's real crouter root ${r} from a node --test process; point CRTR_HOME (not only HOME) at a temporary directory`);return r}t(s,"layoutRoot");function $(){return process.env.NODE_TEST_CONTEXT!==void 0||process.execArgv.includes("--test")}t($,"runningUnderNodeTest");let c;function z(){if(c===void 0)try{c=f(T().homedir,".crouter")}catch{c=null}return c}t(z,"accountLayoutRoot");function u(){return o(s(),"runtime")}t(u,"runtimeRoot");function d(){return o(s(),"user")}t(d,"userRoot");function h(){return o(s(),"apps")}t(h,"appsRoot");function y(){return o(u(),"secrets")}t(y,"secretsDir");function M(r,e){if(!/^[a-z0-9]+(?:-[a-z0-9]+)*-[0-9a-f]{8}$/.test(e))throw new Error(`invalid profile id: ${e}`);const n=v(r);return{memory:o(n.memory,"profiles",e),env:o(y(),"env",p(n.root),e),home:n.home}}t(M,"profileSpace");function x(){return process.platform==="linux"?"/run/crtr":u()}t(x,"socketDir");function a(){return o(u(),"layout")}t(a,"layoutVersionFile");function C(){const r=A("user").paths?.user_files??(process.platform==="linux"?o(d(),"files"):O(s()));if(!b(r))throw new Error("paths.user_files must be absolute");return f(r)}t(C,"userFilesRoot");function v(r){const e=r.startsWith("app:")?r.slice(4):r;if(!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(e)||e==="."||e==="..")throw new Error(`invalid app id: ${r}`);const n=o(h(),e);return{root:n,memory:o(n,"memory"),conversations:o(n,"conversations"),nodes:o(n,"nodes"),files:o(n,"files"),home:o(n,"home"),cache:o(n,"cache")}}t(v,"appSpace");function I(r){const e=v(r);return process.platform==="linux"?e.home:p(e.root)==="terminal"?C():e.home}t(I,"appProcessHome");function F(){if(process.env.CRTR_LAYOUT_REFUSE==="1")return!0;if(i(a())){if(E(a(),"utf8").trim()!==l)throw new Error("unsupported runtime layout version");return!1}const r=s(),e=process.platform==="linux"&&r==="/"&&i("/volume")?"/volume":r;return i(o(e,"canvas"))||i(o(e,"canvas.db"))?!0:i(o(u(),"canvas.db"))}t(F,"layoutNeedsMigration");function V(){if(F())throw new Error("layout_migration_required");for(const r of[u(),d(),h(),y()])m(r,{recursive:!0,mode:448});if(m(x(),{recursive:!0,mode:493}),process.platform==="linux"&&(R(x()).mode&511)!==493)throw new Error("launcher socket directory must be mode 0755");i(a())||_(a(),`${l}
2
- `,{flag:"wx",mode:384})}t(V,"initializeEmptyLayout");export{l as LAYOUT_VERSION,I as appProcessHome,v as appSpace,h as appsRoot,V as initializeEmptyLayout,F as layoutNeedsMigration,s as layoutRoot,a as layoutVersionFile,M as profileSpace,u as runtimeRoot,y as secretsDir,x as socketDir,C as userFilesRoot,d as userRoot};
1
+ var w=Object.defineProperty;var t=(r,o)=>w(r,"name",{value:o,configurable:!0});import{existsSync as i,mkdirSync as m,readFileSync as E,statSync as R,writeFileSync as _}from"node:fs";import{homedir as S,userInfo as T}from"node:os";import{basename as l,dirname as A,isAbsolute as O,join as e,resolve as f}from"node:path";import{envHomeOverride as $}from"../shared/env.js";import{readConfig as b}from"./config.js";const d="1";function s(){const r=f($()||(process.platform==="linux"?"/":e(S(),".crouter")));if(g()&&r===z())throw new Error(`refusing to use the account's real crouter root ${r} from a node --test process; point CRTR_HOME (not only HOME) at a temporary directory`);return r}t(s,"layoutRoot");function g(){return process.env.NODE_TEST_CONTEXT!==void 0||process.execArgv.includes("--test")}t(g,"runningUnderNodeTest");let c;function z(){if(c===void 0)try{c=f(T().homedir,".crouter")}catch{c=null}return c}t(z,"accountLayoutRoot");function u(){return e(s(),"runtime")}t(u,"runtimeRoot");function h(){return e(s(),"user")}t(h,"userRoot");function x(){return e(s(),"apps")}t(x,"appsRoot");function y(){return e(u(),"secrets")}t(y,"secretsDir");function D(r,o){if(!/^[a-z0-9]+(?:-[a-z0-9]+)*-[0-9a-f]{8}$/.test(o))throw new Error(`invalid profile id: ${o}`);const n=p(r);return{memory:e(n.memory,"profiles",o),env:e(y(),"env",l(n.root),o),home:n.home}}t(D,"profileSpace");function v(){return process.platform==="linux"?"/run/crtr":u()}t(v,"socketDir");function a(){return e(u(),"layout")}t(a,"layoutVersionFile");function F(){const r=b("user").paths?.user_files??(process.platform==="linux"?e(h(),"files"):A(s()));if(!O(r))throw new Error("paths.user_files must be absolute");return f(r)}t(F,"userFilesRoot");function p(r){const o=r.startsWith("app:")?r.slice(4):r;if(!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(o)||o==="."||o==="..")throw new Error(`invalid app id: ${r}`);const n=e(x(),o);return{root:n,memory:e(n,"memory"),conversations:e(n,"conversations"),nodes:e(n,"nodes"),files:e(n,"files"),home:e(n,"home"),cache:e(n,"cache")}}t(p,"appSpace");function M(r,o){if(!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(o))throw new Error(`invalid cron id: ${o}`);return e(p(r).root,"crons",o)}t(M,"cronFolder");function V(r){const o=p(r);return process.platform==="linux"?o.home:l(o.root)==="terminal"?F():o.home}t(V,"appProcessHome");function C(){if(process.env.CRTR_LAYOUT_REFUSE==="1")return!0;if(i(a())){if(E(a(),"utf8").trim()!==d)throw new Error("unsupported runtime layout version");return!1}const r=s(),o=process.platform==="linux"&&r==="/"&&i("/volume")?"/volume":r;return i(e(o,"canvas"))||i(e(o,"canvas.db"))?!0:i(e(u(),"canvas.db"))}t(C,"layoutNeedsMigration");function Y(){if(C())throw new Error("layout_migration_required");for(const r of[u(),h(),x(),y()])m(r,{recursive:!0,mode:448});if(m(v(),{recursive:!0,mode:493}),process.platform==="linux"&&(R(v()).mode&511)!==493)throw new Error("launcher socket directory must be mode 0755");i(a())||_(a(),`${d}
2
+ `,{flag:"wx",mode:384})}t(Y,"initializeEmptyLayout");export{d as LAYOUT_VERSION,V as appProcessHome,p as appSpace,x as appsRoot,M as cronFolder,Y as initializeEmptyLayout,C as layoutNeedsMigration,s as layoutRoot,a as layoutVersionFile,D as profileSpace,u as runtimeRoot,y as secretsDir,v as socketDir,F as userFilesRoot,h as userRoot};
@@ -1,4 +1,5 @@
1
1
  import { type ThinkingLevel } from '@earendil-works/pi-ai/compat';
2
+ import { type SpendTarget } from '../usage/ledger.js';
2
3
  type WireContent = string | null | Array<{
3
4
  type: 'text';
4
5
  text: string;
@@ -53,8 +54,11 @@ export interface LlmResult {
53
54
  completion: Record<string, unknown>;
54
55
  chunks: Array<Record<string, unknown>>;
55
56
  }
57
+ /** `spend`: the grantee a user-paid call bills and its grant's cap. The call is refused
58
+ * `cap_exhausted` once the grantee's month spend reached the cap, and a completed call
59
+ * is added to the grantee's month. Omitted, the call is neither capped nor counted. */
56
60
  export declare function runLlm(input: LlmRequest, signal?: AbortSignal, onChunk?: (chunk: Record<string, unknown>) => void, target?: {
57
61
  cwd: string;
58
62
  profileId: string | null;
59
- }): Promise<LlmResult>;
63
+ }, spend?: SpendTarget): Promise<LlmResult>;
60
64
  export {};
@@ -1,4 +1,4 @@
1
- var ie=Object.defineProperty;var p=(t,e)=>ie(t,"name",{value:e,configurable:!0});import{randomUUID as ae}from"node:crypto";import{streamSimple as le}from"@earendil-works/pi-ai/compat";import{expandModelCandidates as Q,parseModelSpec as de,probeRouteAvailability as ce}from"../model-routes.js";import{readMergedLaunchConfig as V}from"../config.js";import{openModelRegistry as fe}from"../runtime/model-registry.js";import{coolingUntilFor as X,isManagedProvider as P,markSubscriptionRateLimited as ue,markSubscriptionSuccess as me,readSubscriptionPool as pe,retryAfterMsFromHeaders as K,retryAfterMsFromError as he,refreshSubscriptionCredential as ye,subscriptionNeedsSuccessWrite as _e}from"../subscription-state.js";import{classifyRateLimit as ge,describeRateLimitScope as Y,rateLimitHeaders as ve}from"../rate-limit-scope.js";import{RunError as l}from"../runs/operations.js";import{recognizeProviderError as ee}from"../provider-error-recognition.js";import{isSafetyRefusal as be}from"./refusal.js";function we(t){return typeof t=="string"||t===null||Array.isArray(t)&&t.every(e=>e&&typeof e=="object"&&(e.type==="text"&&typeof e.text=="string"||e.type==="image_url"&&typeof e.image_url?.url=="string"&&/^data:image\/(png|jpeg|gif|webp);base64,[A-Za-z0-9+/]+={0,2}$/.test(e.image_url.url)))}p(we,"validContent");function te(t){return Array.isArray(t)?t.map(e=>{if(e.type==="text")return e;const[,n,r]=/^data:(image\/(?:png|jpeg|gif|webp));base64,(.*)$/.exec(e.image_url.url);return{type:"image",mimeType:n,data:r}}):t??""}p(te,"imageContent");function De(t){if(!t||typeof t!="object"||Array.isArray(t))throw new l(400,"invalid_request","body must be an object");const e=t;if(!Array.isArray(e.messages)||!e.messages.every(n=>n&&typeof n=="object"&&["system","user","assistant","tool"].includes(n.role)&&we(n.content)))throw new l(400,"invalid_request","messages must be chat messages","messages");if(e.messages.some(n=>["system","assistant"].includes(n.role)&&typeof n.content!="string"&&n.content!==null))throw new l(400,"invalid_request","system and assistant content must be text","messages");if(e.model!==void 0&&typeof e.model!="string")throw new l(400,"invalid_request","model must be a string","model");if(e.model_policy!==void 0&&!["exact","substitute"].includes(e.model_policy))throw new l(400,"invalid_request","invalid model_policy","model_policy");if(e.payer!==void 0&&!["user","app"].includes(e.payer))throw new l(400,"invalid_request","invalid payer","payer");if(e.reasoning_effort!==void 0&&!["minimal","low","medium","high","xhigh","max"].includes(e.reasoning_effort))throw new l(400,"invalid_request","invalid reasoning_effort","reasoning_effort");if(e.stream!==void 0&&typeof e.stream!="boolean")throw new l(400,"invalid_request","stream must be boolean","stream");if(e.tools!==void 0&&(!Array.isArray(e.tools)||!e.tools.every(n=>n?.type==="function"&&typeof n.function?.name=="string"&&n.function.name.length>0&&(n.function.description===void 0||typeof n.function.description=="string")&&(n.function.parameters===void 0||n.function.parameters!==null&&typeof n.function.parameters=="object"&&!Array.isArray(n.function.parameters)&&n.function.parameters.type==="object"))))throw new l(400,"invalid_request","invalid tools","tools");if(e.tool_choice!==void 0&&e.tool_choice!=="auto"&&e.tool_choice!=="none"&&e.tool_choice!=="required"&&(typeof e.tool_choice!="object"||e.tool_choice===null||e.tool_choice.type!=="function"||typeof e.tool_choice.function?.name!="string"))throw new l(400,"invalid_request","invalid tool_choice","tool_choice");if(e.messages.some(n=>n.role==="assistant"&&n.tool_calls!==void 0&&(!Array.isArray(n.tool_calls)||!n.tool_calls.every(r=>r&&typeof r.id=="string"&&typeof r.function?.name=="string"&&typeof r.function.arguments=="string"&&xe(r.function.arguments)))))throw new l(400,"invalid_request","invalid assistant tool_calls","messages");if(e.stop!==void 0&&!(typeof e.stop=="string"||Array.isArray(e.stop)&&e.stop.every(n=>typeof n=="string")))throw new l(400,"invalid_request","invalid stop","stop");if(e.seed!==void 0&&!Number.isSafeInteger(e.seed))throw new l(400,"invalid_request","invalid seed","seed");for(const[n,r]of[["temperature",e.temperature],["top_p",e.top_p],["max_tokens",e.max_tokens]])if(r!==void 0&&(typeof r!="number"||!Number.isFinite(r)||n==="temperature"&&(r<0||r>2)||n==="top_p"&&(r<0||r>1)||n==="max_tokens"&&(!Number.isSafeInteger(r)||r<1)))throw new l(400,"invalid_request",`invalid ${n}`,n);if(e.response_format!==void 0){const n=e.response_format;if(!n||typeof n!="object"||Array.isArray(n)||!["text","json_object","json_schema"].includes(n.type)||n.type==="json_schema"&&(typeof n.json_schema?.name!="string"||!n.json_schema.name||!n.json_schema.schema||typeof n.json_schema.schema!="object"||Array.isArray(n.json_schema.schema)))throw new l(400,"invalid_request","invalid response_format","response_format")}if(e.metadata!==void 0&&Buffer.byteLength(JSON.stringify(e.metadata)??"")>4096)throw new l(400,"invalid_request","metadata exceeds 4 KB","metadata");return e}p(De,"parseLlmRequest");function xe(t){try{const e=JSON.parse(t);return e!==null&&typeof e=="object"&&!Array.isArray(e)}catch{return!1}}p(xe,"isJsonObject");function je(t,e){const n=Date.now(),r=t.messages.map(s=>s.role==="system"?{role:"system",content:s.content??"",timestamp:n}:s.role==="tool"?{role:"toolResult",toolCallId:s.tool_call_id??"",toolName:"",content:typeof s.content=="string"||s.content===null?[{type:"text",text:s.content??""}]:te(s.content),isError:!1,timestamp:n}:s.role==="assistant"?{role:"assistant",content:[...s.content?[{type:"text",text:s.content}]:[],...(s.tool_calls??[]).map(m=>({type:"toolCall",id:m.id,name:m.function.name,arguments:JSON.parse(m.function.arguments)}))],api:"openai-completions",provider:"openai",model:t.model??"",usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"stop",timestamp:n}:{role:"user",content:te(s.content),timestamp:n});if(e){const s=r.filter(c=>c.role==="system").map(c=>c.content).join(`
2
- `).slice(0,16384),m=r.findIndex(c=>c.role==="user");if(s&&m>=0){const c=r[m];r[m]={...c,content:typeof c.content=="string"?`${s}
1
+ var de=Object.defineProperty;var p=(t,e)=>de(t,"name",{value:e,configurable:!0});import{randomUUID as ce}from"node:crypto";import{streamSimple as fe}from"@earendil-works/pi-ai/compat";import{expandModelCandidates as Y,parseModelSpec as ue,probeRouteAvailability as me}from"../model-routes.js";import{readMergedLaunchConfig as ee}from"../config.js";import{openModelRegistry as pe}from"../runtime/model-registry.js";import{coolingUntilFor as te,isManagedProvider as E,markSubscriptionRateLimited as he,markSubscriptionSuccess as ye,readSubscriptionPool as _e,retryAfterMsFromHeaders as Z,retryAfterMsFromError as ge,refreshSubscriptionCredential as ve,subscriptionNeedsSuccessWrite as be}from"../subscription-state.js";import{classifyRateLimit as we,describeRateLimitScope as oe,rateLimitHeaders as xe}from"../rate-limit-scope.js";import{RunError as l}from"../runs/operations.js";import{recognizeProviderError as ne}from"../provider-error-recognition.js";import{isSafetyRefusal as ke}from"./refusal.js";import{assertUnderCap as je,recordModelUsage as qe}from"../usage/ledger.js";function Ae(t){return typeof t=="string"||t===null||Array.isArray(t)&&t.every(e=>e&&typeof e=="object"&&(e.type==="text"&&typeof e.text=="string"||e.type==="image_url"&&typeof e.image_url?.url=="string"&&/^data:image\/(png|jpeg|gif|webp);base64,[A-Za-z0-9+/]+={0,2}$/.test(e.image_url.url)))}p(Ae,"validContent");function re(t){return Array.isArray(t)?t.map(e=>{if(e.type==="text")return e;const[,n,r]=/^data:(image\/(?:png|jpeg|gif|webp));base64,(.*)$/.exec(e.image_url.url);return{type:"image",mimeType:n,data:r}}):t??""}p(re,"imageContent");function We(t){if(!t||typeof t!="object"||Array.isArray(t))throw new l(400,"invalid_request","body must be an object");const e=t;if(!Array.isArray(e.messages)||!e.messages.every(n=>n&&typeof n=="object"&&["system","user","assistant","tool"].includes(n.role)&&Ae(n.content)))throw new l(400,"invalid_request","messages must be chat messages","messages");if(e.messages.some(n=>["system","assistant"].includes(n.role)&&typeof n.content!="string"&&n.content!==null))throw new l(400,"invalid_request","system and assistant content must be text","messages");if(e.model!==void 0&&typeof e.model!="string")throw new l(400,"invalid_request","model must be a string","model");if(e.model_policy!==void 0&&!["exact","substitute"].includes(e.model_policy))throw new l(400,"invalid_request","invalid model_policy","model_policy");if(e.payer!==void 0&&!["user","app"].includes(e.payer))throw new l(400,"invalid_request","invalid payer","payer");if(e.reasoning_effort!==void 0&&!["minimal","low","medium","high","xhigh","max"].includes(e.reasoning_effort))throw new l(400,"invalid_request","invalid reasoning_effort","reasoning_effort");if(e.stream!==void 0&&typeof e.stream!="boolean")throw new l(400,"invalid_request","stream must be boolean","stream");if(e.tools!==void 0&&(!Array.isArray(e.tools)||!e.tools.every(n=>n?.type==="function"&&typeof n.function?.name=="string"&&n.function.name.length>0&&(n.function.description===void 0||typeof n.function.description=="string")&&(n.function.parameters===void 0||n.function.parameters!==null&&typeof n.function.parameters=="object"&&!Array.isArray(n.function.parameters)&&n.function.parameters.type==="object"))))throw new l(400,"invalid_request","invalid tools","tools");if(e.tool_choice!==void 0&&e.tool_choice!=="auto"&&e.tool_choice!=="none"&&e.tool_choice!=="required"&&(typeof e.tool_choice!="object"||e.tool_choice===null||e.tool_choice.type!=="function"||typeof e.tool_choice.function?.name!="string"))throw new l(400,"invalid_request","invalid tool_choice","tool_choice");if(e.messages.some(n=>n.role==="assistant"&&n.tool_calls!==void 0&&(!Array.isArray(n.tool_calls)||!n.tool_calls.every(r=>r&&typeof r.id=="string"&&typeof r.function?.name=="string"&&typeof r.function.arguments=="string"&&Me(r.function.arguments)))))throw new l(400,"invalid_request","invalid assistant tool_calls","messages");if(e.stop!==void 0&&!(typeof e.stop=="string"||Array.isArray(e.stop)&&e.stop.every(n=>typeof n=="string")))throw new l(400,"invalid_request","invalid stop","stop");if(e.seed!==void 0&&!Number.isSafeInteger(e.seed))throw new l(400,"invalid_request","invalid seed","seed");for(const[n,r]of[["temperature",e.temperature],["top_p",e.top_p],["max_tokens",e.max_tokens]])if(r!==void 0&&(typeof r!="number"||!Number.isFinite(r)||n==="temperature"&&(r<0||r>2)||n==="top_p"&&(r<0||r>1)||n==="max_tokens"&&(!Number.isSafeInteger(r)||r<1)))throw new l(400,"invalid_request",`invalid ${n}`,n);if(e.response_format!==void 0){const n=e.response_format;if(!n||typeof n!="object"||Array.isArray(n)||!["text","json_object","json_schema"].includes(n.type)||n.type==="json_schema"&&(typeof n.json_schema?.name!="string"||!n.json_schema.name||!n.json_schema.schema||typeof n.json_schema.schema!="object"||Array.isArray(n.json_schema.schema)))throw new l(400,"invalid_request","invalid response_format","response_format")}if(e.metadata!==void 0&&Buffer.byteLength(JSON.stringify(e.metadata)??"")>4096)throw new l(400,"invalid_request","metadata exceeds 4 KB","metadata");return e}p(We,"parseLlmRequest");function Me(t){try{const e=JSON.parse(t);return e!==null&&typeof e=="object"&&!Array.isArray(e)}catch{return!1}}p(Me,"isJsonObject");function $e(t,e){const n=Date.now(),r=t.messages.map(s=>s.role==="system"?{role:"system",content:s.content??"",timestamp:n}:s.role==="tool"?{role:"toolResult",toolCallId:s.tool_call_id??"",toolName:"",content:typeof s.content=="string"||s.content===null?[{type:"text",text:s.content??""}]:re(s.content),isError:!1,timestamp:n}:s.role==="assistant"?{role:"assistant",content:[...s.content?[{type:"text",text:s.content}]:[],...(s.tool_calls??[]).map(c=>({type:"toolCall",id:c.id,name:c.function.name,arguments:JSON.parse(c.function.arguments)}))],api:"openai-completions",provider:"openai",model:t.model??"",usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"stop",timestamp:n}:{role:"user",content:re(s.content),timestamp:n});if(e){const s=r.filter(f=>f.role==="system").map(f=>f.content).join(`
2
+ `).slice(0,16384),c=r.findIndex(f=>f.role==="user");if(s&&c>=0){const f=r[c];r[c]={...f,content:typeof f.content=="string"?`${s}
3
3
 
4
- ${c.content}`:[{type:"text",text:s},...c.content]}}s&&m<0&&r.unshift({role:"user",content:s,timestamp:n});for(let c=r.length-1;c>=0;c--)r[c]?.role==="system"&&r.splice(c,1)}const g=t.tools?.map(({function:s})=>({name:s.name,description:s.description??"",parameters:s.parameters??{type:"object",properties:{}}}));return{messages:r,...g?{tools:g}:{}}}p(je,"contextOf");function oe(t,e,n,r){if(!t){const s=V(n,r),m=Object.values({...s.modelRoutes??{},...s.legacyModelRoutes??{}}),c=m.length?[s.modelLadders.defaultProvider,...s.modelRouting?.familyOrder??[],...m.map(x=>x.family)].find(x=>m.some(w=>w.family===x)):s.modelLadders.defaultProvider??"anthropic";return{kind:"logical",family:c,strength:"strong",spec:`${c}/strong`}}const g=/^([^/]+)\/(ultra|strong|medium|light)$/.exec(t);return g?{kind:"logical",family:g[1],strength:g[2],spec:t}:{kind:e==="exact"?"exact":"concrete",spec:t}}p(oe,"requestFor");async function Ne(t,e,n,r){if(t.payer==="app")throw new l(403,"payer_not_app","app-paid inference routes are not registered");const g=await fe(),s=oe(t.model,t.model_policy,r?.cwd,r?.profileId),m=p(u=>u.routeId!=="concrete"&&u.routeId!=="exact","configured"),c=p(u=>{const a=g.find(u.providerId,u.modelId);return a!==void 0&&(!t.reasoning_effort||a.reasoning)&&(t.seed===void 0&&t.response_format===void 0||a.api==="openai-completions")&&(t.stop===void 0||["openai-completions","anthropic-messages"].includes(a.api))},"matchesFeatures"),x=Q(s.kind==="exact"?{kind:"concrete",spec:s.spec}:s,r?.cwd,r?.profileId).filter(m);let w;if(s.kind==="logical")w=t.model_policy==="exact"?x.filter(u=>u.family===s.family&&u.strength===s.strength).slice(0,1):x;else{const u=de(s.spec),a=x.filter(f=>f.providerId===u?.providerId&&f.modelId===u.modelId&&(t.model_policy!=="exact"||f.thinkingLevel===u.thinkingLevel)&&c(f));if(t.model_policy==="exact")w=a.slice(0,1);else if(a.length)w=a;else{const f=V(r?.cwd,r?.profileId),A=[...Object.values(f.modelRoutes??{}).flatMap(i=>Object.entries(i.models)),...[f.modelLadders.anthropic,f.modelLadders.openai].flatMap(i=>Object.entries(i))].find(([,i])=>i===s.spec)?.[0],S=oe(void 0,"substitute",r?.cwd,r?.profileId);w=A&&S.kind==="logical"?Q({...S,strength:A,spec:s.spec},r?.cwd,r?.profileId).filter(i=>m(i)&&i.strength===A):[]}}if(w=w.filter(c),w.length===0)throw t.model_policy==="exact"?new l(400,"substitution_refused","no eligible model route is configured"):new l(404,"model_unavailable","no eligible model route is configured");const W=`chatcmpl-${ae()}`;let k=1/0,_,F;for(const u of w){if(!ce(u,g).authenticated)continue;const a=g.find(u.providerId,u.modelId);if(!a)continue;const f=u.providerId,M=P(f)?pe(f).filter(i=>!i.authFailure):[],A=M.filter(i=>X(i,a.id)<=Date.now());if(M.length&&!A.length){const i=Math.min(...M.map(q=>X(q,a.id)));i<k&&(k=i,F=`${a.provider}/${a.id}`);continue}const S=M.length?A:[void 0];for(const i of S){const q=Date.now();let B=i?.access;if(i&&i.expires<=Date.now()&&P(f)){const o=g.getProvider(f)?.auth.oauth;if(!o)throw new l(503,"upstream_model_unavailable",`no OAuth refresh for ${f}`,void 0,{served_model:`${a.provider}/${a.id}`});B=(await ye(f,i.label,async(v,G)=>{const N=await o.refresh({type:"oauth",refresh:v,access:"",expires:0,...G?{clientId:G}:{}},e??new AbortController().signal),H=N.clientId;return{refresh:N.refresh,access:N.access,expires:N.expires,...typeof H=="string"&&H?{clientId:H}:{}}})).access}const ne=je(t,!!i),R=i?void 0:await g.getApiKeyAndHeaders(a);if(R&&!R.ok)continue;const h=`${a.provider}/${a.id}`,$=t.model??h;let j=!1,z,b=3e4,I;const E=[],J=[],C=[];let d;const L=p((o,y=null)=>({id:W,object:"chat.completion.chunk",created:Math.floor(q/1e3),model:h,served_model:h,requested_model:$,substituted:$!==h&&!/^([^/]+)\/(ultra|strong|medium|light)$/.test($),choices:[{index:0,delta:o,finish_reason:y}]}),"chunk"),O=p(o=>{E.push(o),n?.(o)},"emit"),re=le(a,ne,{apiKey:B??(R?.ok?R.apiKey:void 0),...R?.ok?{headers:R.headers}:{},signal:e,reasoning:t.reasoning_effort??u.thinkingLevel,...t.temperature!==void 0?{temperature:t.temperature}:{},...t.max_tokens!==void 0?{maxTokens:t.max_tokens}:{},...t.top_p!==void 0?{samplingParams:{top_p:t.top_p}}:{},...t.tool_choice==="none"?{toolChoice:"none"}:{},onPayload:p(o=>{if(!o||typeof o!="object")return;const y=t.tool_choice,v=y==="required"?a.api==="anthropic-messages"?{type:"any"}:"required":typeof y=="object"&&y!==null?a.api==="anthropic-messages"?{type:"tool",name:y.function.name}:y:void 0;return{...o,...v?{tool_choice:v}:{},...t.stop!==void 0?{[a.api==="anthropic-messages"?"stop_sequences":"stop"]:typeof t.stop=="string"?[t.stop]:t.stop}:{},...t.seed!==void 0?{seed:t.seed}:{},...t.response_format!==void 0?{response_format:t.response_format}:{}}},"onPayload"),fetch:p(async(o,y)=>{const v=await fetch(o,y);return z=v.status,v.status===429&&(j=!0,I=v.headers,b=K(v.headers)??15e3),v},"fetch"),onResponse:p(o=>{o.status===429&&(j=!0,I=o.headers,b=K(o.headers)??15e3)},"onResponse")});try{for await(const o of re)o.type==="text_delta"&&(J.push(o.delta),O(L({content:o.delta}))),o.type==="thinking_delta"&&(C.push(o.delta),O(L({reasoning_content:o.delta}))),o.type==="done"&&(d=o.message),o.type==="error"&&(d=o.error)}catch(o){const y=he(o);I===void 0&&o&&typeof o=="object"&&"headers"in o&&Object.keys(ve(o.headers)).length&&(I=o.headers);const v=ee({status:o&&typeof o=="object"&&"status"in o?o.status:void 0,message:o instanceof Error?o.message:String(o)});if(!j&&v.kind!=="rate-limit")throw E.length?new l(503,"upstream_model_unavailable",o instanceof Error?o.message:String(o),void 0,{served_model:h}):o;j=!0,b=y??b}if(!j&&d?.stopReason==="error"&&ee({message:d.errorMessage}).kind==="rate-limit"&&(j=!0,b=15e3),j){const o=ge(I,a.id);if(K(I)===void 0&&o.resetAt!==void 0&&(b=Math.max(b,o.resetAt-Date.now())),_={provider:f,model:a.id,...i?{account:i.label}:{},observation:o},console.error(`[llm] 429 from ${h}${i?` account "${i.label}"`:""}: scope=${Y(o.scope)} retry_after_ms=${b} headers=${JSON.stringify(o.headers)}`),i&&P(f)&&await ue(f,i.label,b,q,Date.now(),o.scope),Date.now()+b<k&&(k=Date.now()+b,F=h),J.length||C.length)throw new l(503,"service_unavailable","vendor rate limit after output began",void 0,{served_model:h});continue}const D=be(d);if(z===400&&t.tool_choice!==void 0&&t.tool_choice!=="auto"&&t.tool_choice!=="none"&&/\btool[_ ]choice\b/i.test(d?.errorMessage??"")&&/not supported|does not support|unsupported|cannot|must be auto/i.test(d?.errorMessage??""))throw new l(400,"invalid_request",d.errorMessage,"tool_choice",{served_model:h});if(!d||d.stopReason==="error"&&!D||d.stopReason==="aborted")throw new l(503,"upstream_model_unavailable",d?.errorMessage??"model request failed",void 0,{served_model:h});i&&P(f)&&_e(i,a.id)&&await me(f,i.label,q,a.id);const T=d.content.filter(o=>o.type==="toolCall").map(o=>({id:o.id,type:"function",function:{name:o.name,arguments:JSON.stringify(o.arguments)}})),U=D?"content_filter":d.stopReason==="toolUse"?"tool_calls":d.stopReason==="length"?"length":"stop",Z={prompt_tokens:d.usage.input,completion_tokens:d.usage.output,total_tokens:d.usage.totalTokens,cache_read_tokens:d.usage.cacheRead,cache_write_tokens:d.usage.cacheWrite,usd_equivalent:null,served_model:h},se={id:W,object:"chat.completion",created:Math.floor(q/1e3),model:h,served_model:h,requested_model:$,substituted:$!==h&&!/^([^/]+)\/(ultra|strong|medium|light)$/.test($),choices:[{index:0,message:{role:"assistant",content:J.join("")||null,...C.length?{reasoning_content:C.join("")}:{},...T.length?{tool_calls:T}:{},...D?{refusal:d.errorMessage??"Content filtered"}:{}},finish_reason:U}],usage:Z,...t.metadata!==void 0?{metadata:t.metadata}:{}};for(const[o,y]of T.entries())O(L({tool_calls:[{index:o,...y}]}));return O({...L({},U),...D?{choices:[{index:0,delta:{},finish_reason:U,message:{refusal:d.errorMessage??"Content filtered"}}]}:{},usage:Z}),{completion:se,chunks:E}}}throw k!==1/0?new l(429,"all_routes_exhausted","all eligible model routes are rate-limited",void 0,{retry_after_s:Math.max(1,Math.ceil((k-Date.now())/1e3)),served_model:_?`${_.provider}/${_.model}`:F,..._?{rate_limit:{provider:_.provider,model:_.model,..._.account?{account:_.account}:{},scope:Y(_.observation.scope),..._.observation.claim?{claim:_.observation.claim}:{},headers:_.observation.headers}}:{}},void 0,!0):t.model_policy==="exact"?new l(400,"substitution_refused","the requested model is unavailable"):new l(404,"model_unavailable","no usable model route is available")}p(Ne,"runLlm");export{De as parseLlmRequest,Ne as runLlm};
4
+ ${f.content}`:[{type:"text",text:s},...f.content]}}s&&c<0&&r.unshift({role:"user",content:s,timestamp:n});for(let f=r.length-1;f>=0;f--)r[f]?.role==="system"&&r.splice(f,1)}const _=t.tools?.map(({function:s})=>({name:s.name,description:s.description??"",parameters:s.parameters??{type:"object",properties:{}}}));return{messages:r,..._?{tools:_}:{}}}p($e,"contextOf");function se(t,e,n,r){if(!t){const s=ee(n,r),c=Object.values({...s.modelRoutes??{},...s.legacyModelRoutes??{}}),f=c.length?[s.modelLadders.defaultProvider,...s.modelRouting?.familyOrder??[],...c.map(k=>k.family)].find(k=>c.some(I=>I.family===k)):s.modelLadders.defaultProvider??"anthropic";return{kind:"logical",family:f,strength:"strong",spec:`${f}/strong`}}const _=/^([^/]+)\/(ultra|strong|medium|light)$/.exec(t);return _?{kind:"logical",family:_[1],strength:_[2],spec:t}:{kind:e==="exact"?"exact":"concrete",spec:t}}p(se,"requestFor");function Re(t,e){const{input:n,output:r,cacheRead:_,cacheWrite:s}=t.cost;return n===0&&r===0&&_===0&&s===0?null:Number.isFinite(e)&&e>=0?e:null}p(Re,"usdEquivalent");function Ie(t,e){return t.compat?.allowedFallbackModels?.find(r=>r.provider===t.provider&&r.model===e)}p(Ie,"allowedFallback");async function He(t,e,n,r,_){if(t.payer==="app")throw new l(403,"payer_not_app","app-paid inference routes are not registered");je(_);const s=await pe(),c=se(t.model,t.model_policy,r?.cwd,r?.profileId),f=p(m=>m.routeId!=="concrete"&&m.routeId!=="exact","configured"),k=p(m=>{const a=s.find(m.providerId,m.modelId);return a!==void 0&&(!t.reasoning_effort||a.reasoning)&&(t.seed===void 0&&t.response_format===void 0||a.api==="openai-completions")&&(t.stop===void 0||["openai-completions","anthropic-messages"].includes(a.api))},"matchesFeatures"),I=Y(c.kind==="exact"?{kind:"concrete",spec:c.spec}:c,r?.cwd,r?.profileId).filter(f);let w;if(c.kind==="logical")w=t.model_policy==="exact"?I.filter(m=>m.family===c.family&&m.strength===c.strength).slice(0,1):I;else{const m=ue(c.spec),a=I.filter(u=>u.providerId===m?.providerId&&u.modelId===m.modelId&&(t.model_policy!=="exact"||u.thinkingLevel===m.thinkingLevel)&&k(u));if(t.model_policy==="exact")w=a.slice(0,1);else if(a.length)w=a;else{const u=ee(r?.cwd,r?.profileId),q=[...Object.values(u.modelRoutes??{}).flatMap(i=>Object.entries(i.models)),...[u.modelLadders.anthropic,u.modelLadders.openai].flatMap(i=>Object.entries(i))].find(([,i])=>i===c.spec)?.[0],L=se(void 0,"substitute",r?.cwd,r?.profileId);w=q&&L.kind==="logical"?Y({...L,strength:q,spec:c.spec},r?.cwd,r?.profileId).filter(i=>f(i)&&i.strength===q):[]}}if(w=w.filter(k),w.length===0)throw t.model_policy==="exact"?new l(400,"substitution_refused","no eligible model route is configured"):new l(404,"model_unavailable","no eligible model route is configured");const G=`chatcmpl-${ce()}`;let j=1/0,y,U;for(const m of w){if(!me(m,s).authenticated)continue;const a=s.find(m.providerId,m.modelId);if(!a)continue;const u=m.providerId,S=E(u)?_e(u).filter(i=>!i.authFailure):[],q=S.filter(i=>te(i,a.id)<=Date.now());if(S.length&&!q.length){const i=Math.min(...S.map(A=>te(A,a.id)));i<j&&(j=i,U=`${a.provider}/${a.id}`);continue}const L=S.length?q:[void 0];for(const i of L){const A=Date.now();let Q=i?.access;if(i&&i.expires<=Date.now()&&E(u)){const o=s.getProvider(u)?.auth.oauth;if(!o)throw new l(503,"upstream_model_unavailable",`no OAuth refresh for ${u}`,void 0,{served_model:`${a.provider}/${a.id}`});Q=(await ve(u,i.label,async(g,X)=>{const P=await o.refresh({type:"oauth",refresh:g,access:"",expires:0,...X?{clientId:X}:{}},e??new AbortController().signal),z=P.clientId;return{refresh:P.refresh,access:P.access,expires:P.expires,...typeof z=="string"&&z?{clientId:z}:{}}})).access}const ie=$e(t,!!i),M=i?void 0:await s.getApiKeyAndHeaders(a);if(M&&!M.ok)continue;const v=`${a.provider}/${a.id}`,$=t.model??v;let x=!1,V,b=3e4,R;const J=[],T=[],O=[];let d;const N=p((o,h=null)=>({id:G,object:"chat.completion.chunk",created:Math.floor(A/1e3),model:v,served_model:v,requested_model:$,substituted:$!==v&&!/^([^/]+)\/(ultra|strong|medium|light)$/.test($),choices:[{index:0,delta:o,finish_reason:h}]}),"chunk"),D=p(o=>{J.push(o),n?.(o)},"emit"),ae=fe(a,ie,{apiKey:Q??(M?.ok?M.apiKey:void 0),...M?.ok?{headers:M.headers}:{},signal:e,reasoning:t.reasoning_effort??m.thinkingLevel,...t.temperature!==void 0?{temperature:t.temperature}:{},...t.max_tokens!==void 0?{maxTokens:t.max_tokens}:{},...t.top_p!==void 0?{samplingParams:{top_p:t.top_p}}:{},...t.tool_choice==="none"?{toolChoice:"none"}:{},onPayload:p(o=>{if(!o||typeof o!="object")return;const h=t.tool_choice,g=h==="required"?a.api==="anthropic-messages"?{type:"any"}:"required":typeof h=="object"&&h!==null?a.api==="anthropic-messages"?{type:"tool",name:h.function.name}:h:void 0;return{...o,...g?{tool_choice:g}:{},...t.stop!==void 0?{[a.api==="anthropic-messages"?"stop_sequences":"stop"]:typeof t.stop=="string"?[t.stop]:t.stop}:{},...t.seed!==void 0?{seed:t.seed}:{},...t.response_format!==void 0?{response_format:t.response_format}:{}}},"onPayload"),fetch:p(async(o,h)=>{const g=await fetch(o,h);return V=g.status,g.status===429&&(x=!0,R=g.headers,b=Z(g.headers)??15e3),g},"fetch"),onResponse:p(o=>{o.status===429&&(x=!0,R=o.headers,b=Z(o.headers)??15e3)},"onResponse")});try{for await(const o of ae)o.type==="text_delta"&&(T.push(o.delta),D(N({content:o.delta}))),o.type==="thinking_delta"&&(O.push(o.delta),D(N({reasoning_content:o.delta}))),o.type==="done"&&(d=o.message),o.type==="error"&&(d=o.error)}catch(o){const h=ge(o);R===void 0&&o&&typeof o=="object"&&"headers"in o&&Object.keys(xe(o.headers)).length&&(R=o.headers);const g=ne({status:o&&typeof o=="object"&&"status"in o?o.status:void 0,message:o instanceof Error?o.message:String(o)});if(!x&&g.kind!=="rate-limit")throw J.length?new l(503,"upstream_model_unavailable",o instanceof Error?o.message:String(o),void 0,{served_model:v}):o;x=!0,b=h??b}if(!x&&d?.stopReason==="error"&&ne({message:d.errorMessage}).kind==="rate-limit"&&(x=!0,b=15e3),x){const o=we(R,a.id);if(Z(R)===void 0&&o.resetAt!==void 0&&(b=Math.max(b,o.resetAt-Date.now())),y={provider:u,model:a.id,...i?{account:i.label}:{},observation:o},console.error(`[llm] 429 from ${v}${i?` account "${i.label}"`:""}: scope=${oe(o.scope)} retry_after_ms=${b} headers=${JSON.stringify(o.headers)}`),i&&E(u)&&await he(u,i.label,b,A,Date.now(),o.scope),Date.now()+b<j&&(j=Date.now()+b,U=v),T.length||O.length)throw new l(503,"service_unavailable","vendor rate limit after output began",void 0,{served_model:v});continue}const F=ke(d);if(V===400&&t.tool_choice!==void 0&&t.tool_choice!=="auto"&&t.tool_choice!=="none"&&/\btool[_ ]choice\b/i.test(d?.errorMessage??"")&&/not supported|does not support|unsupported|cannot|must be auto/i.test(d?.errorMessage??""))throw new l(400,"invalid_request",d.errorMessage,"tool_choice",{served_model:v});if(!d||d.stopReason==="error"&&!F||d.stopReason==="aborted")throw new l(503,"upstream_model_unavailable",d?.errorMessage??"model request failed",void 0,{served_model:v});i&&E(u)&&be(i,a.id)&&await ye(u,i.label,A,a.id);const W=d.content.filter(o=>o.type==="toolCall").map(o=>({id:o.id,type:"function",function:{name:o.name,arguments:JSON.stringify(o.arguments)}})),H=F?"content_filter":d.stopReason==="toolUse"?"tool_calls":d.stopReason==="length"?"length":"stop",K=d.responseModel?Ie(a,d.responseModel):void 0,C=K?`${a.provider}/${K.model}`:v,B={prompt_tokens:d.usage.input,completion_tokens:d.usage.output,total_tokens:d.usage.totalTokens,cache_read_tokens:d.usage.cacheRead,cache_write_tokens:d.usage.cacheWrite,usd_equivalent:Re(K??a,d.usage.cost.total),served_model:C};_&&qe(_.grantee,"user",C,d.usage,B.usd_equivalent);const le={id:G,object:"chat.completion",created:Math.floor(A/1e3),model:C,served_model:C,requested_model:$,substituted:$!==C&&!/^([^/]+)\/(ultra|strong|medium|light)$/.test($),choices:[{index:0,message:{role:"assistant",content:T.join("")||null,...O.length?{reasoning_content:O.join("")}:{},...W.length?{tool_calls:W}:{},...F?{refusal:d.errorMessage??"Content filtered"}:{}},finish_reason:H}],usage:B,...t.metadata!==void 0?{metadata:t.metadata}:{}};for(const[o,h]of W.entries())D(N({tool_calls:[{index:o,...h}]}));return D({...N({},H),...F?{choices:[{index:0,delta:{},finish_reason:H,message:{refusal:d.errorMessage??"Content filtered"}}]}:{},usage:B}),{completion:le,chunks:J}}}throw j!==1/0?new l(429,"all_routes_exhausted","all eligible model routes are rate-limited",void 0,{retry_after_s:Math.max(1,Math.ceil((j-Date.now())/1e3)),served_model:y?`${y.provider}/${y.model}`:U,...y?{rate_limit:{provider:y.provider,model:y.model,...y.account?{account:y.account}:{},scope:oe(y.observation.scope),...y.observation.claim?{claim:y.observation.claim}:{},headers:y.observation.headers}}:{}},void 0,!0):t.model_policy==="exact"?new l(400,"substitution_refused","the requested model is unavailable"):new l(404,"model_unavailable","no usable model route is available")}p(He,"runLlm");export{We as parseLlmRequest,He as runLlm};
@@ -53,6 +53,8 @@ export interface ProviderResponseContext {
53
53
  run_id: string | null;
54
54
  sub: string;
55
55
  grantee: string;
56
+ /** Whose month the receipt adds to: the calling app, or `app:terminal` for the person (whose outbound identity is their sub). */
57
+ ledger_grantee: string;
56
58
  jti: string;
57
59
  status: number;
58
60
  headers: Record<string, string | string[] | undefined>;
@@ -1 +1 @@
1
- var K=Object.defineProperty;var c=(e,r)=>K(e,"name",{value:r,configurable:!0});import{validateDeclaredResult as Q}from"../../api/command-manifest/result.js";import{callerAllows as T,requireGroup as V}from"../../daemon/api/handlers/providers.js";import{terminalGrant as W}from"../grants/mirror.js";import{listInstalledPlugins as X}from"../resolver.js";import{ProviderError as u}from"./errors.js";import{ensureProviderBundle as Y,refreshProviderBundle as Z,resolveProviderBundle as S,unavailableProviderError as ee}from"./bundles.js";import{prepareFileCall as re,resignForRetry as U,resolveFileResult as oe}from"./files.js";import{consumeApproval as te,requireProviderApproval as ne}from"./approval.js";import{outboundToken as ie}from"./mint.js";import{discardFile as se}from"./token-cache.js";import{registerProviderCall as ae}from"./inflight.js";import{buildRequestPlan as de}from"../command-plugins/transport/http-plan.js";import{sendProviderRequest as pe,ProviderTransportError as ue}from"./request.js";import{acceptProviderReceipt as le}from"./receipts.js";import{writeProviderAudit as ce}from"./audit.js";function O(e,r){const n=[],o=c((i,a)=>{const d=[...a,i.name];if(i.kind==="branch"){for(const g of i.children)o(g,d);return}const l=d[0]===r?d.slice(1).join(" "):d.join(" ");n.push({tool:l,leaf:i})},"walk");for(const i of e.roots)o(i,[]);return n}c(O,"providerLeaves");function D(e,r,n){if(e.kind!=="branch")return null;const o=[];for(const h of e.children)if(h.kind==="branch"){const w=D(h,r,n);w&&o.push(w)}else if("group"in h&&h.group&&T(n,`${r}:${h.group}`)){const{kind:w,name:k,description:t,whenToUse:A,tier:m,summary:q,params:b,output:j,effects:$,group:f}=h;o.push({kind:w,name:k,description:t,whenToUse:A,...m===void 0?{}:{tier:m},summary:q,params:b,output:j,effects:$,group:f})}if(o.length===0)return null;const{kind:i,name:a,description:d,whenToUse:l,tier:g,summary:v,model:_,rootEntry:y}=e;return{kind:i,name:a,description:d,whenToUse:l,...g===void 0?{}:{tier:g},summary:v,..._===void 0?{}:{model:_},...y===void 0?{}:{rootEntry:y},children:o}}c(D,"providerHelpBranch");async function Ne(e,r){const n=await S(r),o=O(n.commands,r),i=X("user").find(a=>a.name===r)?.manifest.description??r;return{provider:r,status:"available",major:n.major,display_name:i,groups:n.provider.groups.filter(a=>T(e,`${r}:${a.group}`)).map(a=>({group:a.group,leaves:o.filter(({leaf:d})=>d.group===a.group).map(({tool:d,leaf:l})=>({tool:d,description:l.description,summary:l.summary,params:l.params,output:l.output}))})),commands:n.commands.roots.flatMap(a=>{const d=D(a,r,e);return d?[d]:[]})}}c(Ne,"providerTools");function fe(e){return le(e.headers["crtr-receipt"],e)}c(fe,"onProviderResponse");function me(e){if(e.class==="person"){const r=W()?.sub;if(!r)throw new u("grant_revoked","the terminal has no active grant");return{sub:r,grantee:r}}if(!e.sub||!e.grantee)throw new u("grant_revoked","the caller has no active grant");return{sub:e.sub,grantee:e.grantee}}c(me,"identity");function ve(e,r){return[`${e.id}:${r.group}`]}c(ve,"callScope");function ge(e,r){const n=O(e.commands,e.id).find(o=>o.tool===r);if(!n)throw new u("not_found",`tool "${r}" is not in provider "${e.id}"`,{param:"tool"});if(!n.leaf.group)throw new u("not_found",`tool "${r}" has no group`,{param:"tool"});return n.leaf}c(ge,"leafFor");function J(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const r=e.error;return!r||typeof r!="object"||Array.isArray(r)?{}:r}c(J,"errorBody");function he(e){const r=Array.isArray(e)?e[0]:e;if(!r)return;const n=Number(r);if(Number.isFinite(n)&&n>=0)return n;const o=Date.parse(r);return Number.isFinite(o)?Math.max(0,Math.ceil((o-Date.now())/1e3)):void 0}c(he,"retryAfter");function H(e,r,n,o=!1){const i=J(r),a=typeof i.code=="string"?i.code:void 0,d=typeof i.message=="string"?i.message:`provider answered HTTP ${e}`,l=typeof i.field=="string"?i.field:void 0,v={origin:"provider",details:{code:a??null,message:d,...l?{field:l}:{},...typeof i.next=="string"?{next:i.next}:{},...i.received!==void 0?{received:i.received}:{},status:e},...l?{param:l}:{}};return e===429?new u("provider_rate_limited",d,{...v,retry_after_s:he(n["retry-after"])}):e===401||e>=500?new u("provider_unavailable",d,v):e===403&&a==="provider_refused"?new u("provider_refused",d,v):e===403&&a==="approval_required"?new u("provider_error",o?"provider asked for approval again":"provider approval question is invalid",{...v,status:e}):new u("provider_error",d,{...v,status:e})}c(H,"mapProviderResponse");function L(e,r){return JSON.stringify({major:e.major,endpoint:e.transport.endpoint,baseUrl:e.commands.baseUrl??null,rest:r.rest,params:r.params,output:r.output})}c(L,"approvalShape");async function Se(e,r,n){const o=new AbortController,i=c(()=>o.abort(n.signal.reason),"abort");n.signal.aborted?i():n.signal.addEventListener("abort",i,{once:!0});let a=n.requestId;const d=ae({request_id:a,run_id:e.run_id,node_id:e.node_id,controller:o});let l,g,v,_,y,h,w,k="allowed";try{try{await Y(r.provider)}catch{throw ee(r.provider)}let t=await S(r.provider,r.version),A=!1,m,q,b,j=!1,$=!1;for(;;){const f=ge(t,r.tool);v=`${t.id}:${f.group}`,V(e,t.id,f.group);let I=null;if(r.approval&&!j){const s=te(e,r,L(t,f));b=s.option,I=s.prepared,w=r.approval,a=s.requestId,j=!0}const x={params:f.params.filter(s=>"type"in s&&s.type==="file").map(s=>s.name),output:f.output.find(s=>s.type==="file")?.name??null},B=JSON.stringify(x);if(q!==void 0&&q!==B)throw new u("provider_bad_response","the refreshed manifest changed the tool file contract",{origin:"provider",details:{status:409,message:"manifest_stale changed the tool file contract"}});q=B,I&&(m=await U(e,x,r.arguments,I,{signal:o.signal})),m??=await re(e,x,r.arguments,{signal:o.signal});const{sub:C,grantee:N}=me(e),R=await ie({audience:t.transport.endpoint,sub:C,grantee:N,initiating_app:N,scope:ve(t,f)},{signal:o.signal});_=R.jti;const G={registration:{name:t.id,endpoint:t.transport.endpoint},...t.commands.baseUrl?{baseUrl:t.commands.baseUrl}:{},...t.commands.timeouts?{timeouts:t.commands.timeouts}:{},params:f.params,rest:f.rest,output:f.output,commandPath:[t.id,...r.tool.split(" ")]},z=de(G,m.arguments,void 0,{authorization:`Bearer ${R.access_token}`,requestId:a,fileMode:"pass-through",extraHeaders:{...e.run_id?{"Crtr-Run-Id":e.run_id}:{},...b?{"Crtr-Approval":b}:{},...m.headers}});g??=Date.now()+(t.commands.timeouts?.requestMs??3e4);let p;try{if(p=await pe(z,{connectMs:t.commands.timeouts?.connectMs,requestMs:t.commands.timeouts?.requestMs,deadlineAt:g,signal:o.signal}),o.signal.aborted)throw o.signal.reason??new Error("provider call aborted")}catch(s){throw o.signal.aborted?o.signal.reason??s:s instanceof ue?new u("provider_unavailable",s.message,{origin:"provider",details:{status:null,code:s.kind}}):s}if(y??=fe({provider:t.id,tool:r.tool,request_id:a,run_id:e.run_id,sub:C,grantee:N,jti:R.jti,status:p.status,headers:p.headers}),h=p.headers["crtr-responder"],p.status===401&&se(R.key),p.status>=200&&p.status<300){const s=p.body;if(!s||typeof s!="object"||Array.isArray(s))throw new u("provider_bad_response","provider result is not a JSON object",{origin:"provider",details:{status:p.status,message:"provider result is not a JSON object"}});let E;try{E=await oe(e,m,s,{signal:o.signal})}catch(P){throw P instanceof u&&P.code==="provider_error"?new u("provider_bad_response",P.message,{origin:"provider",details:{status:p.status,message:P.message}}):P}l=E.cleanup;const F=Q(f.output,E.result);if(F)throw new u("provider_bad_response",`provider result has an invalid output field "${F.field}"`,{origin:"provider",param:F.field,details:{status:p.status,...F}});if(o.signal.aborted)throw o.signal.reason??new Error("provider call aborted");return{status:200,body:E.result}}const M=J(p.body);if(p.status===403&&M.code==="approval_required"&&!b&&!$){$=!0;const s=await ne(e,r,a,M.question,o.signal,L(t,f),m);if(w=s.questionId,b=s.option,m=await U(e,x,r.arguments,m,{signal:o.signal}),g=void 0,o.signal.aborted)throw new u("user_denied","the approval wait was canceled");continue}if(p.status===403&&M.code==="approval_required"&&b)throw H(p.status,p.body,p.headers,!0);if(p.status!==401&&M.manifest_stale===!0&&!A){A=!0,await Z(t.id,{conditional:!1,...r.version===void 0?{}:{version:r.version}}),t=await S(t.id,r.version);continue}throw H(p.status,p.body,p.headers,!!b)}}catch(t){throw t instanceof u&&t.question_id&&(w=t.question_id),k=`refused:${t instanceof u?t.code:"server_error"}`,l?.(),t}finally{try{ce({request_id:a,caller:e,provider:r.provider,tool:r.tool,scope_checked:v,outcome:k,jti:_,receipt_id:y,responder:h,question_id:w})}finally{d()}n.signal.removeEventListener("abort",i)}}c(Se,"callProvider");export{Se as callProvider,H as mapProviderResponse,fe as onProviderResponse,O as providerLeaves,Ne as providerTools};
1
+ var V=Object.defineProperty;var c=(e,r)=>V(e,"name",{value:r,configurable:!0});import{validateDeclaredResult as W}from"../../api/command-manifest/result.js";import{callerAllows as S,requireGroup as X}from"../../daemon/api/handlers/providers.js";import{grantFor as Y,terminalGrant as Z}from"../grants/mirror.js";import{assertUnderCap as B,UNCAPPED_GRANTEE as ee}from"../usage/ledger.js";import{listInstalledPlugins as re}from"../resolver.js";import{ProviderError as u}from"./errors.js";import{ensureProviderBundle as oe,refreshProviderBundle as te,resolveProviderBundle as M,unavailableProviderError as ne}from"./bundles.js";import{prepareFileCall as ie,resignForRetry as D,resolveFileResult as se}from"./files.js";import{consumeApproval as ae,requireProviderApproval as de}from"./approval.js";import{outboundToken as pe}from"./mint.js";import{discardFile as ue}from"./token-cache.js";import{registerProviderCall as le}from"./inflight.js";import{buildRequestPlan as ce}from"../command-plugins/transport/http-plan.js";import{sendProviderRequest as fe,ProviderTransportError as me}from"./request.js";import{acceptProviderReceipt as ge}from"./receipts.js";import{writeProviderAudit as ve}from"./audit.js";function O(e,r){const n=[],o=c((i,a)=>{const d=[...a,i.name];if(i.kind==="branch"){for(const v of i.children)o(v,d);return}const l=d[0]===r?d.slice(1).join(" "):d.join(" ");n.push({tool:l,leaf:i})},"walk");for(const i of e.roots)o(i,[]);return n}c(O,"providerLeaves");function J(e,r,n){if(e.kind!=="branch")return null;const o=[];for(const h of e.children)if(h.kind==="branch"){const w=J(h,r,n);w&&o.push(w)}else if("group"in h&&h.group&&S(n,`${r}:${h.group}`)){const{kind:w,name:A,description:t,whenToUse:k,tier:m,summary:q,params:_,output:j,effects:$,group:f}=h;o.push({kind:w,name:A,description:t,whenToUse:k,...m===void 0?{}:{tier:m},summary:q,params:_,output:j,effects:$,group:f})}if(o.length===0)return null;const{kind:i,name:a,description:d,whenToUse:l,tier:v,summary:g,model:b,rootEntry:y}=e;return{kind:i,name:a,description:d,whenToUse:l,...v===void 0?{}:{tier:v},summary:g,...b===void 0?{}:{model:b},...y===void 0?{}:{rootEntry:y},children:o}}c(J,"providerHelpBranch");async function Be(e,r){const n=await M(r),o=O(n.commands,r),i=re("user").find(a=>a.name===r)?.manifest.description??r;return{provider:r,status:"available",major:n.major,display_name:i,groups:n.provider.groups.filter(a=>S(e,`${r}:${a.group}`)).map(a=>({group:a.group,leaves:o.filter(({leaf:d})=>d.group===a.group).map(({tool:d,leaf:l})=>({tool:d,description:l.description,summary:l.summary,params:l.params,output:l.output}))})),commands:n.commands.roots.flatMap(a=>{const d=J(a,r,e);return d?[d]:[]})}}c(Be,"providerTools");function he(e){return ge(e.headers["crtr-receipt"],e)}c(he,"onProviderResponse");function G(e){if(!(e.class==="person"||!e.sub||!e.grantee))return{grantee:e.grantee,spend_cap:Y(e.grantee,e.sub)?.spend_cap??null}}c(G,"capTarget");function we(e){if(e.class==="person"){const r=Z()?.sub;if(!r)throw new u("grant_revoked","This machine has no active crouter cloud grant for your terminal. Run `crtr sys join` again from /account/machines.");return{sub:r,grantee:r}}if(!e.sub||!e.grantee)throw new u("grant_revoked","the caller has no active grant");return{sub:e.sub,grantee:e.grantee}}c(we,"identity");function _e(e,r){return[`${e.id}:${r.group}`]}c(_e,"callScope");function be(e,r){const n=O(e.commands,e.id).find(o=>o.tool===r);if(!n)throw new u("not_found",`tool "${r}" is not in provider "${e.id}"`,{param:"tool"});if(!n.leaf.group)throw new u("not_found",`tool "${r}" has no group`,{param:"tool"});return n.leaf}c(be,"leafFor");function H(e){if(!e||typeof e!="object"||Array.isArray(e))return{};const r=e.error;return!r||typeof r!="object"||Array.isArray(r)?{}:r}c(H,"errorBody");function ye(e){const r=Array.isArray(e)?e[0]:e;if(!r)return;const n=Number(r);if(Number.isFinite(n)&&n>=0)return n;const o=Date.parse(r);return Number.isFinite(o)?Math.max(0,Math.ceil((o-Date.now())/1e3)):void 0}c(ye,"retryAfter");function L(e,r,n,o=!1){const i=H(r),a=typeof i.code=="string"?i.code:void 0,d=typeof i.message=="string"?i.message:`provider answered HTTP ${e}`,l=typeof i.field=="string"?i.field:void 0,g={origin:"provider",details:{code:a??null,message:d,...l?{field:l}:{},...typeof i.next=="string"?{next:i.next}:{},...i.received!==void 0?{received:i.received}:{},status:e},...l?{param:l}:{}};return e===429?new u("provider_rate_limited",d,{...g,retry_after_s:ye(n["retry-after"])}):e===401||e>=500?new u("provider_unavailable",d,g):e===403&&a==="provider_refused"?new u("provider_refused",d,g):e===403&&a==="approval_required"?new u("provider_error",o?"provider asked for approval again":"provider approval question is invalid",{...g,status:e}):new u("provider_error",d,{...g,status:e})}c(L,"mapProviderResponse");function z(e,r){return JSON.stringify({major:e.major,endpoint:e.transport.endpoint,baseUrl:e.commands.baseUrl??null,rest:r.rest,params:r.params,output:r.output})}c(z,"approvalShape");async function De(e,r,n){const o=new AbortController,i=c(()=>o.abort(n.signal.reason),"abort");n.signal.aborted?i():n.signal.addEventListener("abort",i,{once:!0});let a=n.requestId;const d=le({request_id:a,run_id:e.run_id,node_id:e.node_id,controller:o});let l,v,g,b,y,h,w,A="allowed";try{try{await oe(r.provider)}catch{throw ne(r.provider)}let t=await M(r.provider,r.version),k=!1,m,q,_,j=!1,$=!1;for(;;){const f=be(t,r.tool);g=`${t.id}:${f.group}`,X(e,t.id,f.group),B(G(e));let F=null;if(r.approval&&!j){const s=ae(e,r,z(t,f));_=s.option,F=s.prepared,w=r.approval,a=s.requestId,j=!0}const R={params:f.params.filter(s=>"type"in s&&s.type==="file").map(s=>s.name),output:f.output.find(s=>s.type==="file")?.name??null},U=JSON.stringify(R);if(q!==void 0&&q!==U)throw new u("provider_bad_response","the refreshed manifest changed the tool file contract",{origin:"provider",details:{status:409,message:"manifest_stale changed the tool file contract"}});q=U,F&&(m=await D(e,R,r.arguments,F,{signal:o.signal})),m??=await ie(e,R,r.arguments,{signal:o.signal});const{sub:I,grantee:x}=we(e);B(G(e));const E=await pe({audience:t.transport.endpoint,sub:I,grantee:x,initiating_app:x,scope:_e(t,f)},{signal:o.signal});b=E.jti;const K={registration:{name:t.id,endpoint:t.transport.endpoint},...t.commands.baseUrl?{baseUrl:t.commands.baseUrl}:{},...t.commands.timeouts?{timeouts:t.commands.timeouts}:{},params:f.params,rest:f.rest,output:f.output,commandPath:[t.id,...r.tool.split(" ")]},Q=ce(K,m.arguments,void 0,{authorization:`Bearer ${E.access_token}`,requestId:a,fileMode:"pass-through",extraHeaders:{...e.run_id?{"Crtr-Run-Id":e.run_id}:{},..._?{"Crtr-Approval":_}:{},...m.headers}});v??=Date.now()+(t.commands.timeouts?.requestMs??3e4);let p;try{if(p=await fe(Q,{connectMs:t.commands.timeouts?.connectMs,requestMs:t.commands.timeouts?.requestMs,deadlineAt:v,signal:o.signal}),o.signal.aborted)throw o.signal.reason??new Error("provider call aborted")}catch(s){throw o.signal.aborted?o.signal.reason??s:s instanceof me?new u("provider_unavailable",s.message,{origin:"provider",details:{status:null,code:s.kind}}):s}if(y??=he({provider:t.id,tool:r.tool,request_id:a,run_id:e.run_id,sub:I,grantee:x,ledger_grantee:e.class==="person"?ee:x,jti:E.jti,status:p.status,headers:p.headers}),h=p.headers["crtr-responder"],p.status===401&&ue(E.key),p.status>=200&&p.status<300){const s=p.body;if(!s||typeof s!="object"||Array.isArray(s))throw new u("provider_bad_response","provider result is not a JSON object",{origin:"provider",details:{status:p.status,message:"provider result is not a JSON object"}});let T;try{T=await se(e,m,s,{signal:o.signal})}catch(P){throw P instanceof u&&P.code==="provider_error"?new u("provider_bad_response",P.message,{origin:"provider",details:{status:p.status,message:P.message}}):P}l=T.cleanup;const C=W(f.output,T.result);if(C)throw new u("provider_bad_response",`provider result has an invalid output field "${C.field}"`,{origin:"provider",param:C.field,details:{status:p.status,...C}});if(o.signal.aborted)throw o.signal.reason??new Error("provider call aborted");return{status:200,body:T.result}}const N=H(p.body);if(p.status===403&&N.code==="approval_required"&&!_&&!$){$=!0;const s=await de(e,r,a,N.question,o.signal,z(t,f),m);if(w=s.questionId,_=s.option,m=await D(e,R,r.arguments,m,{signal:o.signal}),v=void 0,o.signal.aborted)throw new u("user_denied","the approval wait was canceled");continue}if(p.status===403&&N.code==="approval_required"&&_)throw L(p.status,p.body,p.headers,!0);if(p.status!==401&&N.manifest_stale===!0&&!k){k=!0,await te(t.id,{conditional:!1,...r.version===void 0?{}:{version:r.version}}),t=await M(t.id,r.version);continue}throw L(p.status,p.body,p.headers,!!_)}}catch(t){throw t instanceof u&&t.question_id&&(w=t.question_id),A=`refused:${t instanceof u?t.code:"server_error"}`,l?.(),t}finally{try{ve({request_id:a,caller:e,provider:r.provider,tool:r.tool,scope_checked:g,outcome:A,jti:b,receipt_id:y,responder:h,question_id:w})}finally{d()}n.signal.removeEventListener("abort",i)}}c(De,"callProvider");export{De as callProvider,L as mapProviderResponse,he as onProviderResponse,O as providerLeaves,Be as providerTools};
@@ -1 +1 @@
1
- var y=Object.defineProperty;var d=(e,i)=>y(e,"name",{value:i,configurable:!0});import{RunError as _}from"../runs/operations.js";import{errorCodes as n}from"../../api/error-codes.js";const m={provider_error:{type:"invalid_request_error",status:400,retryable:!1},provider_rate_limited:n.provider_rate_limited,provider_unavailable:n.provider_unavailable,provider_refused:n.provider_refused,provider_not_connected:n.provider_not_connected,provider_version_retired:n.provider_version_retired,storage_unavailable:n.storage_unavailable,approval_pending:{...n.approval_pending,user_action:"answer_question"},user_denied:n.user_denied},g={unauthorized:n.unauthorized,grant_revoked:n.grant_revoked,scope_missing:n.scope_missing,permission_error:n.permission_error,invalid_request:n.invalid_request,not_found:n.not_found,not_implemented:n.not_implemented,runtime_starting:n.runtime_starting,server_error:n.server_error,provider_bad_response:n.provider_bad_response};function a(e,i){return m[e]??g[e]??(Object.prototype.hasOwnProperty.call(n,e)?n[e]:void 0)??{type:i===401?"authentication_error":i===403?"permission_error":i===429?"rate_limit_error":i>=500?"server_error":"invalid_request_error",status:i,retryable:i>=500}}d(a,"codeRow");class u extends _{static{d(this,"ProviderError")}origin;type;retry_after_s;question_id;user_action;run_id;constructor(i,s,t={}){const r=a(i,t.status??500);super(t.status??r.status,i,s,t.param,t.details,t.scope,r.retryable),this.type=r.type,this.origin=t.origin??"runtime",t.retry_after_s!==void 0&&(this.retry_after_s=t.retry_after_s),t.question_id!==void 0&&(this.question_id=t.question_id),t.run_id!==void 0&&(this.run_id=t.run_id),r.user_action!==void 0&&(this.user_action=r.user_action)}}function x(e){return new u("scope_missing",`missing scope ${e}`,{scope:e})}d(x,"scopeMissing");function b(e){if(!(e instanceof Error))return null;const{status:i,code:s,details:t,retryable:r}=e;return typeof i!="number"||typeof s!="string"||i<400||i>599?null:new _(i,s,e.message,void 0,t,void 0,typeof r=="boolean"?r:a(s,i).retryable)}d(b,"shaped");function q(e){if(e.code==="unauthorized"||e.code==="grant_revoked")return"reconnect";if(e.code==="scope_missing"&&e.scope)return`grant_scope:${e.scope}`}d(q,"directoryAction");function O(e,i,s,t){const r=e instanceof _?e:b(e)??new u("server_error","provider request failed"),o=r instanceof u?r:null,l=a(r.code,r.status),c=o?.run_id??t,p=o?.user_action??(s?q(r):void 0),v={code:r.code,message:r.message,...r.details===void 0?{}:{details:r.details},type:o?.type??l.type,...r.param?{param:r.param}:{},origin:o?.origin??"runtime",retryable:r.retryable,...o?.retry_after_s===void 0?{}:{retry_after_s:o.retry_after_s},request_id:i,...c?{run_id:c}:{},...o?.question_id?{question_id:o.question_id}:{},...p?{user_action:p}:{},...r.scope?{scope:r.scope}:{}},f={"Request-Id":i};return o?.retry_after_s!==void 0&&(f["Retry-After"]=String(o.retry_after_s)),{status:r.status,body:{error:v},headers:f}}d(O,"providerErrorBody");export{m as PROVIDER_CODES,u as ProviderError,a as codeRow,O as providerErrorBody,x as scopeMissing};
1
+ var m=Object.defineProperty;var d=(r,t)=>m(r,"name",{value:t,configurable:!0});import{RunError as _}from"../runs/operations.js";import{errorCodes as i}from"../../api/error-codes.js";const g={provider_error:{type:"invalid_request_error",status:400,retryable:!1},provider_rate_limited:i.provider_rate_limited,provider_unavailable:i.provider_unavailable,provider_refused:i.provider_refused,provider_not_connected:i.provider_not_connected,provider_version_retired:i.provider_version_retired,storage_unavailable:i.storage_unavailable,approval_pending:{...i.approval_pending,user_action:"answer_question"},user_denied:i.user_denied},b={unauthorized:i.unauthorized,grant_revoked:i.grant_revoked,scope_missing:i.scope_missing,permission_error:i.permission_error,invalid_request:i.invalid_request,not_found:i.not_found,not_implemented:i.not_implemented,runtime_starting:i.runtime_starting,server_error:i.server_error,provider_bad_response:i.provider_bad_response};function a(r,t){return g[r]??b[r]??(Object.prototype.hasOwnProperty.call(i,r)?i[r]:void 0)??{type:t===401?"authentication_error":t===403?"permission_error":t===429?"rate_limit_error":t>=500?"server_error":"invalid_request_error",status:t,retryable:t>=500}}d(a,"codeRow");class u extends _{static{d(this,"ProviderError")}origin;type;retry_after_s;question_id;user_action;run_id;constructor(t,o,n={}){const e=a(t,n.status??500);super(n.status??e.status,t,o,n.param,n.details,n.scope,e.retryable),this.type=e.type,this.origin=n.origin??"runtime",n.retry_after_s!==void 0&&(this.retry_after_s=n.retry_after_s),n.question_id!==void 0&&(this.question_id=n.question_id),n.run_id!==void 0&&(this.run_id=n.run_id),e.user_action!==void 0&&(this.user_action=e.user_action)}}function O(r){return new u("scope_missing",`missing scope ${r}`,{scope:r})}d(O,"scopeMissing");function q(r){if(!(r instanceof Error))return null;const{status:t,code:o,details:n,retryable:e}=r;return typeof t!="number"||typeof o!="string"||t<400||t>599?null:new _(t,o,r.message,void 0,n,void 0,typeof e=="boolean"?e:a(o,t).retryable)}d(q,"shaped");function h(r){if(r.code==="unauthorized"||r.code==="grant_revoked")return"reconnect";if(r.code==="scope_missing"&&r.scope)return`grant_scope:${r.scope}`}d(h,"directoryAction");function w(r,t,o,n){const e=r instanceof _?r:q(r)??new u("server_error","provider request failed"),s=e instanceof u?e:null,v=a(e.code,e.status),c=s?.run_id??n,p=s?.user_action??(e.code==="cap_exhausted"?"raise_cap":o?h(e):void 0),f=e.code==="cap_exhausted"?e.details?.reset_at:void 0,y={code:e.code,message:e.message,...e.details===void 0?{}:{details:e.details},type:s?.type??v.type,...e.param?{param:e.param}:{},origin:s?.origin??"runtime",retryable:e.retryable,...s?.retry_after_s===void 0?{}:{retry_after_s:s.retry_after_s},...typeof f=="string"?{reset_at:f}:{},request_id:t,...c?{run_id:c}:{},...s?.question_id?{question_id:s.question_id}:{},...p?{user_action:p}:{},...e.scope?{scope:e.scope}:{}},l={"Request-Id":t};return s?.retry_after_s!==void 0&&(l["Retry-After"]=String(s.retry_after_s)),{status:e.status,body:{error:y},headers:l}}d(w,"providerErrorBody");export{g as PROVIDER_CODES,u as ProviderError,a as codeRow,w as providerErrorBody,O as scopeMissing};
@@ -6,4 +6,6 @@ export declare function acceptProviderReceipt(value: string | string[] | undefin
6
6
  sub: string;
7
7
  grantee: string;
8
8
  run_id: string | null;
9
+ /** Whose month the receipt adds to when it differs from the receipt's grantee (the person's calls count under `app:terminal`). */
10
+ ledger_grantee?: string;
9
11
  }): string | undefined;
@@ -1,3 +1,2 @@
1
- var a=Object.defineProperty;var _=(r,t)=>a(r,"name",{value:t,configurable:!0});import{prepared as s}from"../sqlite-statements.js";import{withCanvasWrite as g}from"../canvas/db.js";function E(r,t){const d=Array.isArray(r)?r[0]:r;if(!d||d.length>16384)return;let n;try{n=JSON.parse(d)}catch{return}if(!n||typeof n!="object"||Array.isArray(n))return;const e=n;if(!(typeof e.receipt_id!="string"||!e.receipt_id||e.receipt_id.length>256||e.provider!==`app:${t.provider}`||e.tool!==t.tool||e.request_id!==t.request_id||e.sub!==t.sub||e.grantee!==t.grantee||e.payer!==t.sub||(e.run_id??null)!==t.run_id||typeof e.unit!="string"||!e.unit||typeof e.units!="number"||!Number.isFinite(e.units)||e.units<0||typeof e.time!="string"||!Number.isFinite(Date.parse(e.time))||typeof e.usd_equivalent!="number"||!Number.isFinite(e.usd_equivalent)||e.usd_equivalent<0))return g(i=>{if(!s(i,`INSERT OR IGNORE INTO provider_receipts
2
- (provider, receipt_id, grantee, run_id, usd_equivalent, received_at) VALUES (?, ?, ?, ?, ?, ?)`).run(t.provider,e.receipt_id,t.grantee,t.run_id,e.usd_equivalent,new Date().toISOString()).changes)return;const p=new Date().toISOString().slice(0,7);if(s(i,`INSERT INTO grantee_usage (grantee, month, usd_equivalent) VALUES (?, ?, ?)
3
- ON CONFLICT(grantee, month) DO UPDATE SET usd_equivalent = usd_equivalent + excluded.usd_equivalent`).run(t.grantee,p,e.usd_equivalent),t.run_id){const o=s(i,"SELECT usage_json FROM nodes WHERE node_id = ? AND run_id = ?").get(t.run_id,t.run_id);if(o){const u=o.usage_json?JSON.parse(o.usage_json):{};u.cost_usd=(typeof u.cost_usd=="number"?u.cost_usd:0)+e.usd_equivalent,s(i,"UPDATE nodes SET usage_json = ? WHERE node_id = ?").run(JSON.stringify(u),t.run_id)}}}),e.receipt_id}_(E,"acceptProviderReceipt");export{E as acceptProviderReceipt};
1
+ var p=Object.defineProperty;var _=(t,r)=>p(t,"name",{value:r,configurable:!0});import{prepared as o}from"../sqlite-statements.js";import{withCanvasWrite as a}from"../canvas/db.js";import{addUsage as g}from"../usage/ledger.js";function E(t,r){const s=Array.isArray(t)?t[0]:t;if(!s||s.length>16384)return;let i;try{i=JSON.parse(s)}catch{return}if(!i||typeof i!="object"||Array.isArray(i))return;const e=i;if(!(typeof e.receipt_id!="string"||!e.receipt_id||e.receipt_id.length>256||e.provider!==`app:${r.provider}`||e.tool!==r.tool||e.request_id!==r.request_id||e.sub!==r.sub||e.grantee!==r.grantee||e.payer!==r.sub||(e.run_id??null)!==r.run_id||typeof e.unit!="string"||!e.unit||typeof e.units!="number"||!Number.isFinite(e.units)||e.units<0||typeof e.time!="string"||!Number.isFinite(Date.parse(e.time))||typeof e.usd_equivalent!="number"||!Number.isFinite(e.usd_equivalent)||e.usd_equivalent<0))return a(n=>{if(o(n,`INSERT OR IGNORE INTO provider_receipts
2
+ (provider, receipt_id, grantee, run_id, usd_equivalent, received_at) VALUES (?, ?, ?, ?, ?, ?)`).run(r.provider,e.receipt_id,r.grantee,r.run_id,e.usd_equivalent,new Date().toISOString()).changes&&(g(n,{grantee:r.ledger_grantee??r.grantee,payer:"user",source:`provider:${r.provider}`,served_model:"",input_tokens:0,output_tokens:0,cached_tokens:0,usd_equivalent:e.usd_equivalent}),r.run_id)){const d=o(n,"SELECT usage_json FROM nodes WHERE node_id = ? AND run_id = ?").get(r.run_id,r.run_id);if(d){const u=d.usage_json?JSON.parse(d.usage_json):{};u.cost_usd=(typeof u.cost_usd=="number"?u.cost_usd:0)+e.usd_equivalent,o(n,"UPDATE nodes SET usage_json = ? WHERE node_id = ?").run(JSON.stringify(u),r.run_id)}}}),e.receipt_id}_(E,"acceptProviderReceipt");export{E as acceptProviderReceipt};
@@ -28,6 +28,8 @@ export type RunStatus = 'running' | 'waiting_on_user' | 'idle' | 'settled';
28
28
  /** Whether the run has a turn in flight or work owed to it (the `running` run status). */
29
29
  export declare function runIsRunning(runId: string): boolean;
30
30
  export declare function runObject(root: NodeMeta): Record<string, unknown>;
31
+ /** How long a kept key replays: a record this old or older reads as absent. The SDK's conformance scenarios hold FakeCloud to it. */
32
+ export declare const IDEMPOTENCY_WINDOW_MS = 86400000;
31
33
  /** An `Idempotency-Key` header value: absent, or 1–255 characters. */
32
34
  export declare function idempotencyKeyOf(header: unknown): string | undefined;
33
35
  /** Delete keys past their window; returns how many were removed. */