@north-light/cli 0.3.553 → 0.3.554
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/contract.d.ts +1 -1
- package/dist/api/contract.js +1 -1
- package/dist/builtin-memory/crouter-sdk/release-notes.md +4 -0
- package/dist/core/runs/operations.js +5 -5
- package/docs/sdk/release-notes.md +4 -0
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +13 -13
package/dist/api/contract.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** The contract level this build serves and requires. Written by `scripts/contract.mjs release`; never edit by hand. */
|
|
2
|
-
export declare const CONTRACT_LEVEL = "1.
|
|
2
|
+
export declare const CONTRACT_LEVEL = "1.33";
|
|
3
3
|
/** Request header naming the one contract level the caller needs, e.g. `1.3`. Optional: absent means no check. */
|
|
4
4
|
export declare const REQUIRED_CONTRACT_HEADER = "Crouter-Required-Contract";
|
|
5
5
|
/** Response header listing the contract levels the server serves, comma-separated, one per major, e.g. `1.3`. */
|
package/dist/api/contract.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var c=Object.defineProperty;var e=(r,t)=>c(r,"name",{value:t,configurable:!0});const f="1.
|
|
1
|
+
var c=Object.defineProperty;var e=(r,t)=>c(r,"name",{value:t,configurable:!0});const f="1.33",i="Crouter-Required-Contract",p="Crouter-Contract";function l(r){const t=/^(\d+)\.(\d+)$/.exec(r.trim());return t===null?null:{major:Number(t[1]),minor:Number(t[2])}}e(l,"parseContractLevel");function a(r){return r==null?[]:r.split(",").map(t=>l(t)).filter(t=>t!==null)}e(a,"parseServedContracts");function s(r,t){const n=l(r);return n===null?!1:(typeof t=="string"||t===null||t===void 0?a(t):t).some(u=>u.major===n.major&&u.minor>=n.minor)}e(s,"contractSatisfies");function C(r,t,n={}){if(r===void 0)return null;if(l(r)===null)return{status:400,code:"invalid_request",param:i,message:`${i} must be a contract level like 1.0.`};if(s(r,t))return null;const o=r.trim();return{status:426,code:"runtime_version_unsupported",message:t===null||a(t).length===0?`The runtime did not name its contract level; the caller requires contract ${o} or later. Roll the runtime forward.`:`The runtime serves contract ${t}; the caller requires contract ${o} or later within the same major. Roll the runtime forward, or pin an SDK whose crouterContract the runtime serves.`,details:{sdk_version:(n.legacyMinimum??"").trim(),runtime_version:n.runtimeVersion??null,required_contract:o,runtime_contract:t}}}e(C,"contractRefusal");export{p as CONTRACT_HEADER,f as CONTRACT_LEVEL,i as REQUIRED_CONTRACT_HEADER,C as contractRefusal,s as contractSatisfies,l as parseContractLevel,a as parseServedContracts};
|
|
@@ -9,6 +9,10 @@ when-and-why-to-read: When you need Release notes, this knowledge should be read
|
|
|
9
9
|
|
|
10
10
|
Changes to `@north-light/sdk` that can change what an existing app sees. Newest first.
|
|
11
11
|
|
|
12
|
+
## 0.3.554
|
|
13
|
+
|
|
14
|
+
`RunObject.recap` (`runs.get`, `runs.list`): the root's latest idle recap, `{ goal, state, at }`, where `goal` says what the conversation is for and `state` where it stands. The runtime writes it after a turn once the root goes idle; it is absent before the first one. Additive.
|
|
15
|
+
|
|
12
16
|
## 0.3.552
|
|
13
17
|
|
|
14
18
|
One ledger records every charge. Additive, except that the per-app cap now counts more.
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
var Oe=Object.defineProperty;var u=(e,t)=>Oe(e,"name",{value:t,configurable:!0});import{coveredByAny as be}from"../../shared/env.js";import{createHash as G}from"node:crypto";import{ApiError as De}from"../../api/errors.js";import{CrtrError as Me}from"../errors.js";import{isAbsolute as z,relative as Ie,resolve as Ae,sep as Te,join as Le}from"node:path";import{SessionManager as Ce}from"@earendil-works/pi-coding-agent";import{covers as U}from"@north-light/identity";import{openDb as B,withCanvasWrite as W}from"../canvas/db.js";import{deleteNodeWithOwned as ke}from"../graph/deletion.js";import{getById as xe}from"../graph/objects.js";import{repairRunConversations as Q}from"../canvas/sessions.js";import{getNode as O,nodeFiles as We,removeNodeFiles as Fe,updateNode as qe}from"../canvas/canvas.js";import{fullName as He}from"../canvas/labels.js";import{renameWindow as Ue}from"../runtime/placement.js";import{liveExecutionForNode as V}from"../canvas/executions.js";import{conversationStore as Z,isConversationNotFound as Be}from"../conversation-store/index.js";import{PARKED_RESIDENT_SQL as Pe}from"../canvas/canvas.js";import{WAKE_MAIL_SQL as $e}from"../canvas/deliveries.js";import{
|
|
1
|
+
var Oe=Object.defineProperty;var u=(e,t)=>Oe(e,"name",{value:t,configurable:!0});import{coveredByAny as be}from"../../shared/env.js";import{createHash as G}from"node:crypto";import{ApiError as De}from"../../api/errors.js";import{CrtrError as Me}from"../errors.js";import{isAbsolute as z,relative as Ie,resolve as Ae,sep as Te,join as Le}from"node:path";import{SessionManager as Ce}from"@earendil-works/pi-coding-agent";import{covers as U}from"@north-light/identity";import{openDb as B,withCanvasWrite as W}from"../canvas/db.js";import{deleteNodeWithOwned as ke}from"../graph/deletion.js";import{getById as xe}from"../graph/objects.js";import{repairRunConversations as Q}from"../canvas/sessions.js";import{getNode as O,nodeFiles as We,removeNodeFiles as Fe,updateNode as qe}from"../canvas/canvas.js";import{fullName as He}from"../canvas/labels.js";import{renameWindow as Ue}from"../runtime/placement.js";import{liveExecutionForNode as V}from"../canvas/executions.js";import{conversationStore as Z,isConversationNotFound as Be}from"../conversation-store/index.js";import{PARKED_RESIDENT_SQL as Pe}from"../canvas/canvas.js";import{WAKE_MAIL_SQL as $e}from"../canvas/deliveries.js";import{readNodeRecap as je}from"../canvas/node-recap.js";import{enqueueNodeMail as Je}from"../feed/messages.js";import{newestReportBody as Ye}from"../feed/reports.js";import{ensureAppProfile as Xe,loadExactProfileManifest as Ge}from"../profiles/manifest.js";import{stopNodeSandboxes as K}from"../spaces/stop.js";import{BrokerUnreachableError as ze}from"../runtime/broker-request.js";import{interruptLive as Qe}from"../runtime/interactive-deliver.js";import{isBrokerLive as ee}from"../runtime/model-swap.js";import{newNodeId as Ve}from"../runtime/nodes.js";import{reviveNode as P}from"../runtime/revive.js";import{spawnChildPrepared as Ze}from"../runtime/spawn.js";import{isPortableModelToken as Ke}from"../runtime/launch.js";import{parseDurationMs as et}from"../wake.js";import{expandModelCandidates as tt,parseModelSpec as nt}from"../model-routes.js";import{payerOfGrant as $}from"../grants/mirror.js";import{isolatedBrokerLauncherAvailable as st}from"../runtime/host.js";import{resolveRunKind as at,resolveRunModel as it}from"../runtime/launch-target.js";import{cycleAwareMessages as rt}from"../runtime/session-cycles.js";import{originFor as te}from"../runtime/stamp/protocol.js";import{readOutputRequest as ot,readOutputResult as ne}from"../runtime/structured-output.js";import{admitLegacyStructuredOutput as se}from"../../daemon/structured-requests/sidecar.js";import{runSchemaRepromptPending as dt}from"../runtime/stop-guard.js";import{toRunOutcomeDTO as ct}from"../../daemon/api/map.js";import{openAssistantMessageStartSequence as ut}from"../../daemon/runs/observer.js";import{closeNode as ae}from"../runtime/close.js";import{parseScope as mt}from"@north-light/identity";import{appendRunEvent as ie,subscribeRunEvents as lt}from"./events.js";import{runActivity as pt}from"./activity.js";import{cancelOpenQuestions as re,openNoticeIds as _t,openQuestionIds as oe}from"./questions.js";import{appSpace as j}from"../layout.js";import{ensureAppSpace as ft}from"../spaces/space.js";import{assertUploadsUsable as de,resolveStorageUrls as wt}from"../storage/attachments.js";import{storageClient as gt,storageConfig as Et}from"../storage/client.js";import{deleteUploadRow as yt,insertUploadUses as ce,releaseRunUploads as ht}from"../storage/tables.js";import{abortProviderCalls as ue}from"../providers/inflight.js";import{filterAsync as me,mapAsync as k}from"../async-array.js";const le=u((e,t,s)=>wt(t,e,s),"defaultResolver"),Rt=3e3;class N extends Error{static{u(this,"RunError")}status;code;param;details;scope;retryable;constructor(t,s,n,d,l,i,r=t===503){super(n),this.status=t,this.code=s,this.param=d,this.details=l,this.scope=i,this.retryable=r}}const S=u((e,t)=>new N(400,"invalid_request",e,t),"invalid"),J=u(()=>new N(404,"not_found","run not found"),"missing"),Y=u(()=>({input_tokens:0,output_tokens:0,cache_read_tokens:0,cache_write_tokens:0,cost_usd:0}),"usage"),I=u(async()=>await B(),"db");async function F(e,t,s=!1){const n=await O(e);if(!n||n.parent!==null||n.run_id!==e||n.grantee!==t||!s&&n.deleting_at)throw J();return n}u(F,"ownedRun");const pe=["waiting_on_user","idle","settled"];async function C(e){if(e.status==="canceled"||e.status==="dead"||e.status==="done"&&e.intent!=="parked")return"settled";const t=await(await I()).prepare(`SELECT 1 FROM node_rows n WHERE n.run_id = ? AND n.status = 'active' AND
|
|
2
2
|
(n.last_settled_at IS NULL OR EXISTS (
|
|
3
3
|
SELECT 1 FROM run_events e WHERE e.run_id = ? AND e.node_id = n.node_id AND e.type = 'node.turn.started'
|
|
4
4
|
AND e.sequence_number > COALESCE((SELECT MAX(c.sequence_number) FROM run_events c
|
|
5
5
|
WHERE c.run_id = e.run_id AND c.node_id = e.node_id AND c.type = 'node.turn.completed'), 0)
|
|
6
|
-
)) LIMIT 1`).get(e.node_id,e.node_id),s=!e.last_settled_at&&e.status!=="done";return t||s||await
|
|
6
|
+
)) LIMIT 1`).get(e.node_id,e.node_id),s=!e.last_settled_at&&e.status!=="done";return t||s||await dt(e)?"running":(await oe(e.node_id)).length?"waiting_on_user":await(await I()).prepare(`SELECT 1 FROM deliveries d JOIN nodes n ON n.node_id = d.destination_id
|
|
7
7
|
WHERE n.run_id = ? AND ${$e("d","'armed', 'pending'")}
|
|
8
|
-
AND (n.status IN ('active', 'idle') OR ${Pe("n")}) LIMIT 1`).get(e.node_id)?"running":"idle"}u(C,"runStatus");async function
|
|
8
|
+
AND (n.status IN ('active', 'idle') OR ${Pe("n")}) LIMIT 1`).get(e.node_id)?"running":"idle"}u(C,"runStatus");async function Dn(e){const t=await O(e);return t!==null&&await C(t)==="running"}u(Dn,"runIsRunning");async function x(e){await Q(e.node_id),e=await O(e.node_id)??e;const t=await C(e),s=t==="settled"?[]:await oe(e.node_id),n=await Ye(e.node_id)||void 0,d=t==="settled"?ct(e):null,l=d?.kind==="result"?ne(e.node_id,e.grantee):void 0,i=je(e.node_id);return{run_id:e.node_id,name:e.name,status:t,...d?{outcome:d}:{},...l===void 0?{}:{result:l},...n!==void 0?{last_report:n}:{},...i?{recap:i}:{},pending_question_ids:s,open_notice_ids:t==="settled"?[]:await _t(e.node_id),...e.last_turn_error_json?{last_turn_error:JSON.parse(e.last_turn_error_json)}:{},usage:JSON.parse(e.usage_json??JSON.stringify(Y())),payer:await $(e.grant_id),scopes:e.scopes,metadata:e.metadata_json?JSON.parse(e.metadata_json):{},created_at:e.created,...t==="settled"&&e.outcome_at?{settled_at:e.outcome_at}:{},latest_sequence:(e.event_cursor??1)-1,last_activity_at:e.last_activity_at,activity:await pt(e.node_id,t)}}u(x,"runObject");const X=864e5,_e=255;function Mn(e){if(e!==void 0){if(typeof e!="string"||!e.length||e.length>_e)throw S(`Idempotency-Key must be 1-${_e} characters`);return e}}u(Mn,"idempotencyKeyOf");async function q(e,t,s,n){n??=await B();const d=await n.prepare("SELECT body_hash, run_id, response_json, created_at FROM idempotency_keys WHERE grantee = ? AND key = ?").get(e,t);if(!(!d||Date.now()-Date.parse(d.created_at)>=X)){if(d.body_hash!==s)throw new N(409,"idempotency_conflict","key used with a different request");return{run_id:d.run_id,response:JSON.parse(d.response_json)}}}u(q,"findIdempotent");async function fe(e,t,s,n,d,l,i){await e.prepare("DELETE FROM idempotency_keys WHERE grantee = ? AND key = ? AND created_at <= ?").run(t,s,new Date(Date.now()-X).toISOString()),await e.prepare("INSERT INTO idempotency_keys (grantee, key, body_hash, run_id, response_json, created_at) VALUES (?, ?, ?, ?, ?, ?)").run(t,s,n,d,JSON.stringify(l),i)}u(fe,"recordIdempotent");async function In(e=Date.now()){return Number((await(await B()).prepare("DELETE FROM idempotency_keys WHERE created_at <= ?").run(new Date(e-X).toISOString())).changes)}u(In,"pruneExpiredIdempotencyKeys");async function we(e,t,s,n,d){const l=n.text;let i=0;return{messageId:(await Je(t,{kind:"message",label:l.slice(0,120),senderNodeId:"human",senderName:s,bodyJson:d?{body:l}:{body:l,start_turn:!1},urgency:d?"normal":"deferred",wake:d?1:0,withinEnqueue:u(async(c,m)=>{n.uses.length&&(await de(s,n.uses,c),await ce(n.uses.map(({upload_id:h,path:E})=>({upload_id:h,path:E,run_id:t})),c)),i=(await ie(c,t,t,"node.message.accepted",{message_id:m,start_turn:d})).sequence_number},"withinEnqueue")})).messageId,sequence:i}}u(we,"acceptRunMessage");class ge extends Error{static{u(this,"IdempotentReplay")}winner;constructor(t){super("idempotent replay"),this.winner=t}}const St=1440*60*1e3;function Ee(e){const t=typeof e=="string"?et(e):null;if(t===null||!Number.isSafeInteger(t))throw S(`deadline '${String(e)}' is not a duration; use one or more <int>[smhd] parts such as 10m or 1h30m`,"deadline");if(t<=0)throw S("deadline must be greater than zero","deadline");if(t>St)throw S("deadline must be at most 24h","deadline");return t}u(Ee,"parseRunDeadlineMs");async function An(e,t,s,n=le){if(t.isolated&&!st())throw new N(503,"isolated_unavailable","isolated runs require the Linux root launcher","isolated");t.deadline!==void 0&&Ee(t.deadline);const{causedBy:d,...l}=t,i=G("sha256").update(JSON.stringify(l)).digest("hex");if(s){const a=await q(e.grantee,s,i);if(a){const p=await F(a.run_id,e.grantee);return!p.pi_pid&&await C(p)==="running"&&(t.output_schema!==void 0&&!p.outcome_at&&ot(p.node_id).state==="absent"&&ne(p.node_id,p.grantee)===void 0&&await se(p.node_id,"terminal",t.output_schema),await P(p.node_id,{resume:!0,capacity:"freeze"})),a.response}}await ft(e.grantee);const r=t.profile===void 0?Xe(e.grantee):Ge(t.profile),c=r.manifest.grantee??"app:terminal";if(c!==e.grantee){const a=`crtr:memory:rw:${c}:profile:${r.profileId}`;if(!e.resolved.some(p=>U(p,a))||t.scopes!==null&&!t.scopes.some(p=>U(p,a)))throw new N(403,"scope_missing",`missing scope ${a}`,"profile",void 0,a)}if(t.model!==void 0){const a=t.model;if(Ke(a)){if(a.includes("/")&&await $(e.grant)!=="app"){const p=a.lastIndexOf("/"),D=a.slice(0,p),v=a.slice(p+1);if(["ultra","strong","medium","light"].includes(v)&&tt({kind:"logical",family:D,strength:v,spec:a},j(e.grantee).files,r.profileId).every(R=>R.family!==D))throw S(`model '${a}' names a model family that is not configured in this daemon`,"model")}}else if(nt(a)===void 0)throw S(`model '${a}' is not a strength (ultra, strong, medium, light), a family/strength, or a provider/model id`,"model")}const m=await Ve(),h=Le(j(e.grantee).nodes,m);if(t.isolated&&t.cwd!==void 0){const a=Ie(h,Ae(t.cwd));if(!z(t.cwd)||a===".."||a.startsWith(`..${Te}`)||z(a))throw S("cwd must be inside the isolated root node folder","cwd")}const E=await n(m,e.grantee,t.prompt),g=E.text;let o;try{o=t.message===void 0?void 0:await n(m,e.grantee,t.message)}catch(a){throw E.cleanup(),a}const _=await $(e.grant);let y;try{const a=it(r,t.model);await Ze({kind:at(r),mode:"base",root:!0,rootLifecycle:"resident",readiness:"launch",cwd:t.isolated?t.cwd??h:j(e.grantee).files,profile:r.profileId,prompt:g,nodeId:m,isolated:t.isolated??!1,...t.causedBy===void 0?{}:{causedBy:t.causedBy},...a===void 0?{}:{model:a},...t.deadline===void 0?{}:{deadlineAt:new Date(Date.now()+Ee(t.deadline)).toISOString()},...t.scopes===null?{}:{scopes:t.scopes},run:{grantee:e.grantee,sub:e.sub,grant_id:e.grant,client_id:e.client_id,metadata_json:JSON.stringify(t.metadata),run_id:m,event_cursor:1,usage_json:JSON.stringify(Y())},withinCreate:u(async(p,D)=>{if(s){const R=await q(e.grantee,s,i,p);if(R)throw new ge(R)}const v=E.uses;if(v.length&&(await de(e.grantee,v,p),await ce(v.map(({upload_id:R,path:A})=>({upload_id:R,path:A,run_id:m})),p)),y={run_id:m,node_id:m,grantee:e.grantee,grant:e.grant,rev:e.rev,scopes:t.scopes,payer:_,status:"running",created_at:D.created,metadata:t.metadata,sequence_number:0},await ie(p,m,m,"run.started",{scopes:t.scopes,payer:_,usage:Y()}),o){const R=await we(p,m,e.grantee,o,!1);y.message={run_id:m,message_id:R.messageId,sequence_number:R.sequence}}s&&await fe(p,e.grantee,s,i,m,y,D.created)},"withinCreate")},t.output_schema===void 0?void 0:async p=>{await se(p,"terminal",t.output_schema)})}catch(a){if(await O(m)||(E.cleanup(),o?.cleanup()),a instanceof ge)return a.winner.response;if(t.model!==void 0&&a instanceof Me&&a.code==="usage")throw S(a.message,"model");if(a instanceof De&&(a.code==="isolated_run"||a.code==="invalid_request")){const p=a.details&&typeof a.details=="object"&&"param"in a.details&&typeof a.details.param=="string"?a.details.param:void 0;throw new N(a.status,a.code,a.message,p)}throw a}if(!y)throw new Error("run creation did not commit");return y}u(An,"startRun");async function Tn(e,t){let s;if(t.after)try{const r=JSON.parse(Buffer.from(t.after,"base64url").toString());if(!r||typeof r!="object"||!("activity"in r)||typeof r.activity!="string"||!("id"in r)||typeof r.id!="string")throw new Error("invalid cursor");s={activity:r.activity,id:r.id}}catch{throw S("invalid list cursor","after")}const n=await(await I()).prepareUncached(`SELECT node_id, COALESCE(last_activity_at, created) AS activity FROM node_rows
|
|
9
9
|
WHERE run_id = node_id AND deleting_at IS NULL ${e===null?"":"AND grantee = ?"}
|
|
10
|
-
ORDER BY activity DESC, node_id DESC`).all(...e===null?[]:[e]),d=await me(await k((await me(n,async({node_id:r})=>!t.visible||await t.visible(r))).filter(({activity:r,node_id:c})=>!s||r<s.activity||r===s.activity&&c<s.id),async r=>({root:await O(r.node_id),activity:r.activity})),async({root:r})=>!t.status||await C(r)===t.status),l=d.slice(0,t.limit),i=l.at(-1);return{runs:await k(l,async({root:r})=>await x(r)),next:d.length>t.limit&&i?Buffer.from(JSON.stringify({activity:i.activity,id:i.root.node_id})).toString("base64url"):null}}u(
|
|
10
|
+
ORDER BY activity DESC, node_id DESC`).all(...e===null?[]:[e]),d=await me(await k((await me(n,async({node_id:r})=>!t.visible||await t.visible(r))).filter(({activity:r,node_id:c})=>!s||r<s.activity||r===s.activity&&c<s.id),async r=>({root:await O(r.node_id),activity:r.activity})),async({root:r})=>!t.status||await C(r)===t.status),l=d.slice(0,t.limit),i=l.at(-1);return{runs:await k(l,async({root:r})=>await x(r)),next:d.length>t.limit&&i?Buffer.from(JSON.stringify({activity:i.activity,id:i.root.node_id})).toString("base64url"):null}}u(Tn,"listRuns");async function Ln(e,t,s){const n=await F(e,t),d=u(async()=>await x(await F(e,t)),"read");return!s||pe.includes(await C(n))?await d():new Promise((l,i)=>{let r=u(()=>{},"unsubscribe");const c=u(()=>{clearTimeout(o),clearInterval(g),r()},"settle"),m=u(async()=>{c();try{l(await d())}catch(_){i(_)}},"finish"),h=u(async()=>{pe.includes(String((await d()).status))&&await m()},"check"),E=u(()=>{h().catch(_=>{c(),i(_)})},"run"),g=setInterval(E,250),o=setTimeout(()=>{m()},s*1e3);lt(e,E).then(_=>{r=_},_=>{c(),i(_)})})}u(Ln,"waitRun");async function Cn(e,t,s=le,n=!0,d){const l=e.status==="canceled"||e.outcome_reason==="closed",i=e.grantee??"app:terminal",r=G("sha256").update(JSON.stringify(["run.message",e.node_id,t,n])).digest("hex"),c=u(async g=>{const o=await O(e.node_id),_=await(await I()).prepare("SELECT 1 FROM deliveries WHERE message_id = ? AND state = 'pending' AND canceled_at IS NULL").get(String(g.message_id));return o&&!l&&n&&!o.pi_pid&&_&&await P(e.node_id,{resume:!0,capacity:"freeze"}),g},"replay");if(d){const g=await q(i,d,r);if(g)return c(g.response)}if(l)throw new N(409,"run_settled","canceled run cannot receive a message");const m=await s(e.node_id,i,t),h=n&&e.final_report&&!e.pi_pid?(await import("../runtime/reopen.js")).commitReopenResident:void 0;let E;try{E=await W(async g=>{if(d){const y=await q(i,d,r,g);if(y)return{replayed:y.response}}await h?.(e.node_id);const o=await we(g,e.node_id,i,m,n),_={run_id:e.node_id,message_id:o.messageId,sequence_number:o.sequence};return d&&await fe(g,i,d,r,e.node_id,_,new Date().toISOString()),{result:_}})}catch(g){throw m.cleanup(),g}return"replayed"in E?(m.cleanup(),c(E.replayed)):(n&&!e.pi_pid&&await P(e.node_id,{resume:!0,capacity:"freeze"}),E.result)}u(Cn,"messageRun");async function Nt(e){const t=u(()=>new N(503,"service_unavailable",`node ${e} broker was unavailable before the interrupt reached it`),"unavailable");for(let s=0;s<2;s++){const n=await O(e);if(!n)throw J();if(!ee(n)){if(n.streaming)throw t();return}const d=(await V(e))?.executionId;try{await Qe(e)==="turn"&&ue({nodes:[e]});return}catch(l){if(!(l instanceof ze))throw l;const i=await O(e);if(!i)throw J();if(!i.streaming)return;if(s===0&&ee(i)&&(await V(e))?.executionId!==d)continue;throw t()}}throw t()}u(Nt,"interruptNode");async function kn(e,t="stop"){const s=t==="stop"?(await(await I()).prepare("SELECT node_id FROM nodes WHERE run_id = ?").all(e.node_id)).map(({node_id:n})=>n):[e.node_id];return await Promise.all(s.map(n=>Nt(n))),await x(await O(e.node_id)??e)}u(kn,"interruptRun");async function xn(e,t={}){const s=(await(await I()).prepare("SELECT node_id FROM nodes WHERE run_id = ? ORDER BY created DESC").all(e.node_id)).map(i=>i.node_id);ue({run:e.node_id});const n=[];if(await C(e)!=="settled")n.push(...(await ae(e.node_id,{preserveEmpty:!0,stopReason:"run_canceled"})).closed);else for(const i of s){if(i===e.node_id)continue;const r=await O(i);r&&r.status!=="done"&&r.status!=="canceled"&&r.status!=="dead"&&n.push(...(await ae(i,{preserveEmpty:!0,stopReason:"run_canceled"})).closed)}if(await re({run_id:e.node_id},"the run was canceled"),(await Promise.all([...new Set([e.node_id,...n])].map(i=>K(i,"run_canceled")))).some(i=>!i.confirmed))throw new N(500,"server_error","run process teardown could not be confirmed",void 0,void 0,void 0,!0);const l=await O(e.node_id);return await x(l??e)}u(xn,"cancelRun");const ye=200;async function Wn(e,t){if(typeof t!="string"||!t.trim())throw S("name is required","name");const s=t.trim();if(s.length>ye)throw S(`name exceeds ${ye} characters`,"name");const n=await qe(e.node_id,{name:s});return n.window&&Ue(n.window,He(n)),await x(n)}u(Wn,"renameRun");async function Fn(e,t={config:Et,client:gt}){await re({run_id:e.node_id},"the run was deleted");const s=await W(async c=>(await c.prepare("UPDATE nodes SET deleting_at = COALESCE(deleting_at, ?) WHERE node_id = ?").run(new Date().toISOString(),e.node_id),(await c.prepare("SELECT node_id FROM nodes WHERE run_id = ? ORDER BY created DESC").all(e.node_id)).map(m=>m.node_id)));if((await Promise.all(s.map(c=>K(c,"run_deleted",{deleting:!0})))).some(c=>!c.confirmed))throw new N(500,"server_error","run process teardown could not be confirmed",void 0,void 0,void 0,!0);const d=await k(s,async c=>{const m=await We(c);if(m.app===null)throw new Error(`run node missing during deletion: ${c}`);return m}),l=await(await I()).prepare("SELECT DISTINCT session_id FROM sessions WHERE session_id IS NOT NULL AND node_id IN (SELECT node_id FROM nodes WHERE run_id = ?)").all(e.node_id);for(const{session_id:c}of l)try{await Z().delete(c)}catch(m){if(!Be(m))throw m}const i=await W(async c=>{for(const g of s){const o=await xe(c,g);o!==null&&o.deleted_at===null&&await ke(c,o)}const m=await ht([e.node_id],c);await c.prepare("DELETE FROM run_events WHERE run_id = ?").run(e.node_id),await c.prepare("DELETE FROM idempotency_keys WHERE run_id = ?").run(e.node_id);const h=s.map(()=>"?").join(","),E=await c.prepareUncached(`SELECT message_id FROM messages WHERE sender_node_id IN (${h}) UNION SELECT message_id FROM deliveries WHERE destination_id IN (${h})`).all(...s,...s);await c.prepareUncached(`DELETE FROM deliveries WHERE destination_id IN (${h})`).run(...s);for(const{message_id:g}of E)await c.prepare("DELETE FROM messages WHERE message_id = ? AND NOT EXISTS (SELECT 1 FROM deliveries WHERE message_id = ?)").run(g,g);return m});for(const c of d)await Fe(c);const{notifyRunDeleted:r}=await import("./events.js");return r(e.node_id),{uploads_pending:await vt(i,t)}}u(Fn,"deleteRun");async function vt(e,t){if(!e.length||!t.config())return e.length;let s;try{s=t.client()}catch{return e.length}const n=new Set,d=e.map(i=>s.deleteUpload(i).then(async()=>{await yt(i),n.add(i)},()=>{}));let l;return await Promise.race([Promise.all(d),new Promise(i=>{l=setTimeout(i,t.budgetMs??Rt),l.unref?.()})]),clearTimeout(l),e.length-n.size}u(vt,"deleteReleasedUploads");async function qn(e,t,s){await F(e,t),await Q(e);const n=await W(async o=>{const _=await o.prepare("SELECT node_id, parent FROM nodes WHERE run_id = ? ORDER BY CASE WHEN node_id = ? THEN 0 ELSE 1 END, created, node_id").all(e,e),y=new Map;for(const R of _)y.set(R.node_id,(await o.prepare("SELECT session_id FROM sessions WHERE node_id = ? GROUP BY session_id ORDER BY min(created_at)").all(R.node_id)).map(A=>A.session_id));const p=(await o.prepare("SELECT event_cursor FROM nodes WHERE node_id = ?").get(e)).event_cursor-1,D=await o.prepare(`SELECT node_id, sequence_number FROM run_events
|
|
11
11
|
WHERE run_id = ? AND sequence_number <= ? AND type IN ('node.thinking.done', 'node.output_text.done', 'usage')
|
|
12
12
|
ORDER BY sequence_number`).all(e,p),v=await o.prepare(`SELECT node_id, sequence_number, type, body_json FROM run_events
|
|
13
13
|
WHERE run_id = ? AND sequence_number <= ? AND type IN ('node.thinking.delta', 'node.output_text.delta')
|
|
14
|
-
ORDER BY sequence_number`).all(e,p);return{nodes:_,sessionIds:y,latestSequence:p,completed:D,deltas:v}}),{nodes:d,sessionIds:l,latestSequence:i,completed:r,deltas:c}=n,m=await k(d,async o=>({node_id:o.node_id,parent_node_id:o.parent,conversations:await k(l.get(o.node_id)??[],async _=>{const{header:y,entries:a}=await Z().read(_);if(s==="entries")return{conversation_id:_,header:y,entries:a};const p=Ce.inMemory(y.cwd,void 0,[y,...a]),D=
|
|
14
|
+
ORDER BY sequence_number`).all(e,p);return{nodes:_,sessionIds:y,latestSequence:p,completed:D,deltas:v}}),{nodes:d,sessionIds:l,latestSequence:i,completed:r,deltas:c}=n,m=await k(d,async o=>({node_id:o.node_id,parent_node_id:o.parent,conversations:await k(l.get(o.node_id)??[],async _=>{const{header:y,entries:a}=await Z().read(_);if(s==="entries")return{conversation_id:_,header:y,entries:a};const p=Ce.inMemory(y.cwd,void 0,[y,...a]),D=rt(p)??[],v=new Map(a.map((w,b)=>[w.id,b])),R=a.filter(w=>w.type==="custom"&&w.customType==="crtr-system-prompt").map(w=>({id:w.id,message:{role:"custom",customType:"crtr-system-prompt",data:"data"in w?w.data:void 0,timestamp:w.timestamp}})),A=a.filter(w=>w.type==="custom_message"&&w.customType==="app_message").map(w=>({id:w.id,message:{role:"custom",customType:"app_message",content:"content"in w?w.content:"",timestamp:w.timestamp}})),he=o.node_id===e?a.find(w=>{if(w.type!=="message"||w.message.role!=="user")return!1;const b=te(a,w.message.timestamp);return b?.type==="runtime"&&b.kind==="kickoff"})?.id:void 0,Re=[...D.filter(({id:w})=>!A.some(b=>b.id===w)),...R,...A].sort((w,b)=>(v.get(w.id)??Number.MAX_SAFE_INTEGER)-(v.get(b.id)??Number.MAX_SAFE_INTEGER)).map(({id:w,message:b})=>{const f=b,T=f.usage,H=f.role==="user"?te(a,Number(f.timestamp)):null,Se=H?.type==="human"||w===he,L=f.role==="toolResult"?"tool_result":f.role==="bashExecution"?"bash_execution":f.role==="compactionSummary"?"compaction_summary":f.role==="branchSummary"?"branch_summary":f.role==="custom"&&f.customType==="crtr-system-prompt"?"system":f.role==="user"?Se?"user":"custom":f.role,Ne=L==="custom"?f.role==="custom"?f.customType:H?.type==="runtime"?H.kind:"unknown":void 0,ve=L==="system"&&f.customType==="crtr-system-prompt"?[{type:"text",text:f.data?.text??""}]:typeof f.content=="string"?[{type:"text",text:f.content}]:Array.isArray(f.content)?f.content.map(M=>M.type==="toolCall"?{type:"tool_call",tool_call_id:M.id,name:M.name,arguments:M.arguments}:M.type==="image"?{type:"image",mime_type:M.mimeType}:M.type==="thinking"?{type:"thinking",text:M.thinking}:M):[];return{entry_id:w,role:L,...L==="custom"?{custom_type:Ne}:{},time:typeof f.timestamp=="string"?f.timestamp:new Date(Number(f.timestamp??Date.now())).toISOString(),content:ve,...L==="assistant"?{model:f.model,provider:f.provider,stop_reason:f.stopReason,...f.errorMessage?{error_message:f.errorMessage}:{},...T?{usage:{input_tokens:T.input??0,output_tokens:T.output??0,cache_read_tokens:T.cacheRead??0,cache_write_tokens:T.cacheWrite??0,cost_usd:T.cost?.total??0}}:{}}:{},...L==="tool_result"?{tool_call_id:f.toolCallId,tool_name:f.toolName,is_error:f.isError}:{}}})??[];return{conversation_id:_,messages:Re}})})),h=new Map(m.map(o=>[o.node_id,ut(o.node_id)]));for(const o of r){const _=h.get(o.node_id);_!=null&&o.sequence_number>=_&&h.set(o.node_id,null)}const E=new Map;for(const o of c){const _=h.get(o.node_id);if(_==null||o.sequence_number<_)continue;const y=JSON.parse(o.body_json),a=E.get(o.node_id)??{thinking:"",text:""};o.type==="node.thinking.delta"?a.thinking+=y.delta:a.text+=y.delta,E.set(o.node_id,a)}const g=m.map(o=>({...o,in_progress_message:h.get(o.node_id)==null?null:E.get(o.node_id)??{thinking:"",text:""}}));return{run_id:e,latest_sequence:i,nodes:g}}u(qn,"traceRun");function Hn(e,t,s){if(t===void 0){if(!e.some(n=>U(n,"crtr:llm")))throw new N(403,"scope_missing","missing scope crtr:llm",void 0,void 0,"crtr:llm");return null}if(!Array.isArray(t)||t.some(n=>typeof n!="string"))throw S("scopes must be a list of scope strings","scopes");for(const n of t){try{mt(n)}catch{throw S(`invalid scope ${n}`,"scopes")}if(/^crtr:files:(?:read|rw):app:/.test(n)&&n.split(":")[4]===s.slice(4))throw S(`own-app files cannot narrow a run: ${n}`,"scopes");if(!(/^crtr:memory:(?:read|rw):app:/.test(n)&&n.split(":")[4]===s.slice(4))&&!be(e,n))throw new N(403,"scope_missing",`missing scope ${n}`,void 0,void 0,n)}if(!t.includes("crtr:llm"))throw new N(403,"scope_missing","missing scope crtr:llm",void 0,void 0,"crtr:llm");return t}u(Hn,"requestedScopes");export{X as IDEMPOTENCY_WINDOW_MS,St as RUN_DEADLINE_MAX_MS,Rt as RUN_DELETE_STORAGE_BUDGET_MS,ye as RUN_NAME_MAX,N as RunError,xn as cancelRun,Fn as deleteRun,Mn as idempotencyKeyOf,kn as interruptRun,Tn as listRuns,Cn as messageRun,F as ownedRun,Ee as parseRunDeadlineMs,In as pruneExpiredIdempotencyKeys,Wn as renameRun,Hn as requestedScopes,Dn as runIsRunning,x as runObject,An as startRun,qn as traceRun,Ln as waitRun};
|
|
@@ -7,6 +7,10 @@ description: Behavior changes in @north-light/sdk that an app must act on, newes
|
|
|
7
7
|
|
|
8
8
|
Changes to `@north-light/sdk` that can change what an existing app sees. Newest first.
|
|
9
9
|
|
|
10
|
+
## 0.3.554
|
|
11
|
+
|
|
12
|
+
`RunObject.recap` (`runs.get`, `runs.list`): the root's latest idle recap, `{ goal, state, at }`, where `goal` says what the conversation is for and `state` where it stands. The runtime writes it after a turn once the root goes idle; it is absent before the first one. Additive.
|
|
13
|
+
|
|
10
14
|
## 0.3.552
|
|
11
15
|
|
|
12
16
|
One ledger records every charge. Additive, except that the per-app cap now counts more.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.554",
|
|
4
4
|
"description": "crtr — agent runtime with memory, plugins, and marketplaces",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -141,7 +141,7 @@
|
|
|
141
141
|
},
|
|
142
142
|
"license": "UNLICENSED",
|
|
143
143
|
"dependencies": {
|
|
144
|
-
"@north-light/identity": "0.3.
|
|
144
|
+
"@north-light/identity": "0.3.554",
|
|
145
145
|
"@earendil-works/pi-agent-core": "1.1.0",
|
|
146
146
|
"@earendil-works/pi-ai": "1.1.0",
|
|
147
147
|
"@earendil-works/pi-coding-agent": "1.1.0",
|
package/runtime.lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.554",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@north-light/cli",
|
|
9
|
-
"version": "0.3.
|
|
9
|
+
"version": "0.3.554",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "UNLICENSED",
|
|
12
12
|
"workspaces": [
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"@milkdown/preset-gfm": "^7.22.3",
|
|
25
25
|
"@milkdown/prose": "^7.22.3",
|
|
26
26
|
"@milkdown/utils": "^7.22.3",
|
|
27
|
-
"@north-light/identity": "0.3.
|
|
27
|
+
"@north-light/identity": "0.3.554",
|
|
28
28
|
"cron-parser": "^5.6.0",
|
|
29
29
|
"esbuild": "^0.27.7",
|
|
30
30
|
"jose": "^6.2.12",
|
|
@@ -12455,17 +12455,17 @@
|
|
|
12455
12455
|
},
|
|
12456
12456
|
"packages/crouter-api": {
|
|
12457
12457
|
"name": "@north-light/api",
|
|
12458
|
-
"version": "0.3.
|
|
12458
|
+
"version": "0.3.554",
|
|
12459
12459
|
"license": "UNLICENSED"
|
|
12460
12460
|
},
|
|
12461
12461
|
"packages/crouter-env-docker": {
|
|
12462
12462
|
"name": "@north-light/env-docker",
|
|
12463
|
-
"version": "0.3.
|
|
12463
|
+
"version": "0.3.554",
|
|
12464
12464
|
"license": "UNLICENSED"
|
|
12465
12465
|
},
|
|
12466
12466
|
"packages/crouter-identity": {
|
|
12467
12467
|
"name": "@north-light/identity",
|
|
12468
|
-
"version": "0.3.
|
|
12468
|
+
"version": "0.3.554",
|
|
12469
12469
|
"license": "UNLICENSED",
|
|
12470
12470
|
"dependencies": {
|
|
12471
12471
|
"jose": "^6.2.1"
|
|
@@ -12473,21 +12473,21 @@
|
|
|
12473
12473
|
},
|
|
12474
12474
|
"packages/crouter-plugin": {
|
|
12475
12475
|
"name": "@north-light/plugin",
|
|
12476
|
-
"version": "0.3.
|
|
12476
|
+
"version": "0.3.554",
|
|
12477
12477
|
"license": "UNLICENSED",
|
|
12478
12478
|
"dependencies": {
|
|
12479
|
-
"@north-light/api": "0.3.
|
|
12480
|
-
"@north-light/identity": "0.3.
|
|
12479
|
+
"@north-light/api": "0.3.554",
|
|
12480
|
+
"@north-light/identity": "0.3.554"
|
|
12481
12481
|
}
|
|
12482
12482
|
},
|
|
12483
12483
|
"packages/crouter-sdk": {
|
|
12484
12484
|
"name": "@north-light/sdk",
|
|
12485
|
-
"version": "0.3.
|
|
12485
|
+
"version": "0.3.554",
|
|
12486
12486
|
"license": "UNLICENSED",
|
|
12487
12487
|
"dependencies": {
|
|
12488
|
-
"@north-light/api": "0.3.
|
|
12489
|
-
"@north-light/identity": "0.3.
|
|
12490
|
-
"@north-light/plugin": "0.3.
|
|
12488
|
+
"@north-light/api": "0.3.554",
|
|
12489
|
+
"@north-light/identity": "0.3.554",
|
|
12490
|
+
"@north-light/plugin": "0.3.554",
|
|
12491
12491
|
"jose": "^6.2.1"
|
|
12492
12492
|
},
|
|
12493
12493
|
"bin": {
|