@north-light/cli 0.3.418 → 0.3.420
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/dto/docs.d.ts +7 -0
- package/dist/api/dto/nodes.d.ts +1 -0
- package/dist/builtin-memory/crouter-sdk/README.md +3 -3
- package/dist/builtin-memory/crouter-sdk/client.md +16 -0
- package/dist/builtin-memory/crouter-sdk/getting-started.md +2 -2
- package/dist/builtin-memory/crouter-sdk/guides/README.md +1 -1
- package/dist/builtin-memory/crouter-sdk/guides/apps-on-crouter-cloud.md +222 -3
- package/dist/builtin-memory/crouter-sdk/release-notes.md +10 -0
- package/dist/builtin-memory/crouter-sdk/testing.md +23 -1
- package/dist/clients/attach/input/controller.d.ts +1 -0
- package/dist/clients/attach/input/controller.js +2 -2
- package/dist/clients/attach/render/chat-view.d.ts +44 -0
- package/dist/clients/attach/render/chat-view.js +3 -3
- package/dist/clients/attach/session/frames.d.ts +2 -0
- package/dist/clients/attach/session/frames.js +1 -1
- package/dist/clients/attach/session/input-wiring.d.ts +3 -0
- package/dist/clients/attach/session/input-wiring.js +1 -1
- package/dist/clients/attach/slash/dispatch.d.ts +4 -0
- package/dist/clients/attach/slash/dispatch.js +2 -2
- package/dist/clients/attach/viewer.js +567 -563
- package/dist/core/command-plugins/bundle.d.ts +3 -0
- package/dist/core/command-plugins/bundle.js +1 -1
- package/dist/core/config.js +1 -1
- package/dist/core/fs-utils.d.ts +7 -0
- package/dist/core/fs-utils.js +3 -3
- package/dist/core/graph/package-docs.d.ts +4 -1
- package/dist/core/graph/package-docs.js +5 -5
- package/dist/core/keybindings/catalog.d.ts +1 -1
- package/dist/core/keybindings/catalog.js +1 -1
- package/dist/core/manifest.d.ts +15 -0
- package/dist/core/manifest.js +1 -1
- package/dist/core/plugin-quarantine.d.ts +16 -0
- package/dist/core/plugin-quarantine.js +1 -0
- package/dist/core/providers/bundles.js +2 -2
- package/dist/core/runtime/broker/rebind.d.ts +5 -0
- package/dist/core/runtime/broker/rebind.js +1 -1
- package/dist/core/runtime/broker/turn-replies.d.ts +33 -0
- package/dist/core/runtime/broker/turn-replies.js +1 -0
- package/dist/core/runtime/broker-protocol.d.ts +12 -1
- package/dist/core/runtime/broker.js +1 -1
- package/dist/core/runtime/node-read.js +3 -3
- package/dist/core/runtime/tmux-bindings.js +1 -1
- package/dist/core/runtime/turn-reply-summary.d.ts +6 -0
- package/dist/core/runtime/turn-reply-summary.js +11 -0
- package/dist/core/user-settings.d.ts +15 -0
- package/dist/core/user-settings.js +1 -1
- package/dist/daemon/api/handlers/nodes.js +1 -1
- package/dist/daemon/crtrd.js +6 -5
- package/dist/shared/turn-replies.d.ts +27 -0
- package/dist/shared/turn-replies.js +5 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.js +1 -1
- package/docs/sdk/README.md +3 -3
- package/docs/sdk/client.md +16 -0
- package/docs/sdk/getting-started.md +2 -2
- package/docs/sdk/guides/README.md +1 -1
- package/docs/sdk/guides/apps-on-crouter-cloud.md +218 -1
- package/docs/sdk/release-notes.md +10 -0
- package/docs/sdk/testing.md +23 -1
- package/package.json +2 -2
- package/packages/crouter-identity/package.json +1 -1
- package/runtime.lock.json +12 -12
|
@@ -1 +1 @@
|
|
|
1
|
-
var v=Object.defineProperty;var s=(e,t)=>v(e,"name",{value:t,configurable:!0});import{CONDENSED_HISTORY_MODES as u,MOUSE_MODE_DEFAULTS as p,WHIP_MESSAGE_MODES as d}from"../types.js";function i(e,t,o){const r=e.indexOf(t),a=r>=0?r:0;return e[(a+o+e.length)%e.length]}s(i,"cycleChoice");function c(e,t,o){const r=e.indexOf(t);return r>=0?e[(r+o+e.length)%e.length]:o>=0?e.find(a=>a>t)??e[0]:[...e].reverse().find(a=>a<t)??e[e.length-1]}s(c,"cycleNumber");function n(e,t){return e==="true"?!0:e==="false"?!1:`${t} must be true or false`}s(n,"booleanFromString");function f(e,t){if(!/^\d+$/.test(e))return`${t} must be an integer >= 0`;const o=Number(e);return Number.isSafeInteger(o)?o:`${t} must be an integer >= 0`}s(f,"nonNegativeIntegerFromString");function _(e,t){const o=f(e,t);return typeof o=="string"||o<1?`${t} must be an integer >= 1`:o}s(_,"positiveIntegerFromString");const m=[!1,"notify","apply"],w=[0,1,6,12,24,48,72,168],b=[1,2,3,4,5,6,8,10,12],l="openai-codex";function g(e,t){const o=e.providerOptions[t];if(o!==void 0)return o;const r={};return e.providerOptions[t]=r,r}s(g,"providerOptionsFor");const x=[1,2,3,4,5,6],h={schema_version:{surface:"specialized",owner:"internal schema migration"},marketplaces:{surface:"specialized",owner:"Plugins and pkg market commands"},plugins:{surface:"specialized",owner:"Plugins tab and pkg plugin commands"},auto_update:{surface:"settings",settings:{crtr:{key:"auto_update.crtr",label:"crouter updates",description:"Notify about crouter releases, install them automatically, or turn checks off.",valueConstraint:"true|false|notify|apply; true stores notify",applies:"Applies on the next crouter command.",group:"updates",order:70,format:s(e=>e.auto_update.crtr===!1?"off":e.auto_update.crtr,"format"),cycle:s((e,t)=>{e.auto_update.crtr=i(m,e.auto_update.crtr,t)},"cycle"),setFromString:s((e,t)=>{const o=t==="true"?"notify":t;return o!=="notify"&&o!=="apply"&&o!=="false"?"auto_update.crtr must be notify, apply, or false":(e.auto_update.crtr=o==="false"?!1:o,null)},"setFromString")},content:{key:"auto_update.content",label:"Content updates",description:"Notify about plugin and marketplace updates, apply them automatically, or turn checks off.",valueConstraint:"false|notify|apply",applies:"Applies on the next crouter command.",group:"updates",order:80,format:s(e=>e.auto_update.content===!1?"off":e.auto_update.content,"format"),cycle:s((e,t)=>{e.auto_update.content=i(m,e.auto_update.content,t)},"cycle"),setFromString:s((e,t)=>t!=="notify"&&t!=="apply"&&t!=="false"?"auto_update.content must be notify, apply, or false":(e.auto_update.content=t==="false"?!1:t,null),"setFromString")},interval_hours:{key:"auto_update.interval_hours",label:"Update interval",description:"Minimum hours between automatic update checks. Zero checks on every command.",valueConstraint:"integer >= 0",applies:"Applies on the next crouter command.",group:"updates",order:90,format:s(e=>`${e.auto_update.interval_hours}h`,"format"),cycle:s((e,t)=>{e.auto_update.interval_hours=c(w,e.auto_update.interval_hours,t)},"cycle"),setFromString:s((e,t)=>{const o=f(t,"auto_update.interval_hours");return typeof o=="string"?o:(e.auto_update.interval_hours=o,null)},"setFromString")}}},max_panes_per_window:{surface:"settings",setting:{key:"max_panes_per_window",label:"Maximum panes",description:"Maximum human-review panes placed in one tmux window before using another window.",valueConstraint:"integer >= 1",applies:"Applies to the next pane placement.",group:"panes",order:20,format:s(e=>String(e.max_panes_per_window),"format"),cycle:s((e,t)=>{e.max_panes_per_window=c(b,e.max_panes_per_window,t)},"cycle"),setFromString:s((e,t)=>{const o=_(t,"max_panes_per_window");return typeof o=="string"?o:(e.max_panes_per_window=o,null)},"setFromString")}},auto_open_child_viewers:{surface:"settings",setting:{key:"auto_open_child_viewers",label:"Open child panes",description:"Open each managed child beside its watched parent. Off keeps new children running in the background.",valueConstraint:"true|false",applies:"Applies to the next managed child created; independent roots still open their attended viewer.",group:"panes",order:10,format:s(e=>e.auto_open_child_viewers?"on":"off","format"),cycle:s(e=>{e.auto_open_child_viewers=!e.auto_open_child_viewers},"cycle"),setFromString:s((e,t)=>{const o=n(t,"auto_open_child_viewers");return typeof o=="string"?o:(e.auto_open_child_viewers=o,null)},"setFromString")}},brokerThresholds:{surface:"specialized",owner:"Broker limits tab"},lifecycle:{surface:"specialized",owner:"sys config set lifecycle.unattendedParkMs"},completion_bell:{surface:"settings",setting:{key:"completion_bell",label:"Completion bell",description:"Ring a short sound when the node in an attached viewer finishes a turn.",valueConstraint:"true|false",applies:"Applies on the next turn end.",group:"display",order:41,format:s(e=>e.completion_bell?"on":"off","format"),cycle:s(e=>{e.completion_bell=!e.completion_bell},"cycle"),setFromString:s((e,t)=>{const o=n(t,"completion_bell");return typeof o=="string"?o:(e.completion_bell=o,null)},"setFromString")}},working_gerunds:{surface:"specialized",owner:"sys config set working_gerunds"},whip_mode:{surface:"settings",setting:{key:"whip_mode",label:"Whip mode",description:"Play the whip animation when you send a prompt and when a viewer opens for an agent with an initial prompt.",valueConstraint:"true|false",applies:"Applies on the next attach.",group:"display",order:39,format:s(e=>e.whip_mode?"on":"off","format"),cycle:s(e=>{e.whip_mode=!e.whip_mode},"cycle"),setFromString:s((e,t)=>{const o=n(t,"whip_mode");return typeof o=="string"?o:(e.whip_mode=o,null)},"setFromString")}},page_surface:{surface:"settings",setting:{key:"page_surface",label:"Page human surface",description:"This host reads pages in a browser rather than a terminal. Agents are told the page module runs as written, HTML pages are offered, and document review and live display leave the command tree.",valueConstraint:"true|false",applies:"Applies when human command help is assembled.",group:"display",order:37,format:s(e=>e.page_surface?"on":"off","format"),cycle:s(e=>{e.page_surface=!e.page_surface},"cycle"),setFromString:s((e,t)=>{const o=n(t,"page_surface");return typeof o=="string"?o:(e.page_surface=o,null)},"setFromString")}},whip_messages:{surface:"specialized",owner:"sys config set whip_messages"},whip_message_mode:{surface:"settings",setting:{key:"whip_message_mode",label:"Whip message",description:"What the whip action sends: a random line from whip_messages, or the fixed \u201CGo faster\u201D.",valueConstraint:d.join("|"),applies:"Applies on the next attach.",group:"display",order:40,format:s(e=>e.whip_message_mode==="go-faster"?"go faster":"random","format"),cycle:s((e,t)=>{e.whip_message_mode=i(d,e.whip_message_mode,t)},"cycle"),setFromString:s((e,t)=>d.includes(t)?(e.whip_message_mode=t,null):`whip_message_mode must be one of: ${d.join("|")}`,"setFromString")}},mouse_mode_default:{surface:"settings",setting:{key:"mouse_mode_default",label:"Default mouse mode",description:"Initial mouse wheel scrolling for each attached viewer.",valueConstraint:p.join("|"),applies:"Applies on the next attach.",group:"display",order:38,format:s(e=>e.mouse_mode_default==="tmux"?"follow tmux":e.mouse_mode_default,"format"),cycle:s((e,t)=>{e.mouse_mode_default=i(p,e.mouse_mode_default,t)},"cycle"),setFromString:s((e,t)=>p.includes(t)?(e.mouse_mode_default=t,null):`mouse_mode_default must be one of: ${p.join("|")}`,"setFromString")}},live_cycles:{surface:"settings",setting:{key:"live_cycles",label:"Live cycles",description:"Trailing refresh cycles rendered at full fidelity; older cycles condense to text.",valueConstraint:"integer >= 1",applies:"Applies on the next attach.",group:"display",order:50,format:s(e=>String(e.live_cycles),"format"),cycle:s((e,t)=>{e.live_cycles=c(x,e.live_cycles,t)},"cycle"),setFromString:s((e,t)=>{const o=_(t,"live_cycles");return typeof o=="string"?o:(e.live_cycles=o,null)},"setFromString")}},condensed_history:{surface:"settings",setting:{key:"condensed_history",label:"Condensed history",description:"Messages retained from condensed cycles. Tool calls are never kept.",valueConstraint:u.join("|"),applies:"Applies on the next attach.",group:"display",order:60,format:s(e=>e.condensed_history,"format"),cycle:s((e,t)=>{e.condensed_history=i(u,e.condensed_history,t)},"cycle"),setFromString:s((e,t)=>u.includes(t)?(e.condensed_history=t,null):`condensed_history must be one of: ${u.join("|")}`,"setFromString")}},bash_tool_purpose:{surface:"settings",setting:{key:"bash_tool_purpose",label:"Bash purpose field",description:"Expose the optional purpose label to agents in the bash tool schema. Off removes the field without disabling the bash valve.",valueConstraint:"true|false",applies:"Applies on the next node session.",group:"display",order:25,format:s(e=>e.bash_tool_purpose?"on":"off","format"),cycle:s(e=>{e.bash_tool_purpose=!e.bash_tool_purpose},"cycle"),setFromString:s((e,t)=>{const o=n(t,"bash_tool_purpose");return typeof o=="string"?o:(e.bash_tool_purpose=o,null)},"setFromString")}},fold_finished_tools:{surface:"settings",setting:{key:"fold_finished_tools",label:"Fold finished tools",description:"Fold every settled tool call to its call line. Toggle live with /fold-tools.",valueConstraint:"true|false",applies:"Applies on the next attach.",group:"display",order:30,format:s(e=>e.fold_finished_tools?"on":"off","format"),cycle:s(e=>{e.fold_finished_tools=!e.fold_finished_tools},"cycle"),setFromString:s((e,t)=>{const o=n(t,"fold_finished_tools");return typeof o=="string"?o:(e.fold_finished_tools=o,null)},"setFromString")}},summarize_tool_calls:{surface:"settings",setting:{key:"summarize_tool_calls",label:"Summarize tool calls",description:"Replace completed folded tool-call groups with a one-line broker summary when available.",valueConstraint:"true|false",applies:"Applies when the next visible message closes a tool-call group.",group:"display",order:35,format:s(e=>e.summarize_tool_calls?"on":"off","format"),cycle:s(e=>{e.summarize_tool_calls=!e.summarize_tool_calls},"cycle"),setFromString:s((e,t)=>{const o=n(t,"summarize_tool_calls");return typeof o=="string"?o:(e.summarize_tool_calls=o,null)},"setFromString")}},detailed_tool_recaps:{surface:"settings",setting:{key:"detailed_tool_recaps",label:"Detailed tool recaps",description:"Show each document read and edited file in folded tool recaps. Off shows a compact count line instead.",valueConstraint:"true|false",applies:"Applies on the next attach.",group:"display",order:36,format:s(e=>e.detailed_tool_recaps?"on":"off","format"),cycle:s(e=>{e.detailed_tool_recaps=!e.detailed_tool_recaps},"cycle"),setFromString:s((e,t)=>{const o=n(t,"detailed_tool_recaps");return typeof o=="string"?o:(e.detailed_tool_recaps=o,null)},"setFromString")}},keybindings:{surface:"specialized",owner:"Keybindings tab"},tmux_passthrough:{surface:"specialized",owner:"Keybindings tab"},providerOptions:{surface:"settings",settings:{"openai-codex.fastMode":{key:`providerOptions.${l}.fastMode`,label:"Codex fast mode",description:"Serve Codex turns at the ChatGPT priority tier: about 1.5x faster generation for about 2x the credits. Displayed cost does not reflect the premium.",valueConstraint:"true|false",applies:"Applies on each node's next turn.",group:"providers",order:40,format:s(e=>e.providerOptions[l]?.fastMode?"on":"off","format"),cycle:s(e=>{const t=g(e,l);t.fastMode=!t.fastMode},"cycle"),setFromString:s((e,t)=>{const o=n(t,`providerOptions.${l}.fastMode`);return typeof o=="string"?o:(g(e,l).fastMode=o,null)},"setFromString")}}},modelLadders:{surface:"specialized",owner:"Models tab"},modelRoutes:{surface:"specialized",owner:"Models route declarations"},modelRouting:{surface:"specialized",owner:"Models route declarations"},kinds:{surface:"specialized",owner:"Models tab and kind registry"},page_components:{surface:"specialized",owner:"Page component catalog: product-registered page component kinds"},remoteCanvas:{surface:"specialized",owner:"canvas config commands"},api:{surface:"specialized",owner:"sys connect"},paths:{surface:"specialized",owner:"runtime layout"},store:{surface:"specialized",owner:"daemon conversation store"},runtime:{surface:"specialized",owner:"app-facing listener"},spawnEnv:{surface:"specialized",owner:"spawn environment allow-list"},bin:{surface:"specialized",owner:"bare-binary contributions on every node PATH"},humanActions:{surface:"specialized",owner:"scope-configured human completion actions"}},O=Object.keys(h);function S(){const e=[];for(const t of Object.values(h))t.surface==="settings"&&("setting"in t?e.push(t.setting):e.push(...Object.values(t.settings)));return e.sort((t,o)=>t.order-o.order)}s(S,"collectUserSettings");const y=S();function z(e){return y.find(t=>t.key===e)}s(z,"findUserSetting");function A(e){return y.filter(t=>t.group===e)}s(A,"userSettingsInGroup");export{h as CONFIG_SETTINGS_COVERAGE,O as SCOPE_CONFIG_KEYS,y as USER_SETTINGS,z as findUserSetting,A as userSettingsInGroup};
|
|
1
|
+
var v=Object.defineProperty;var s=(e,t)=>v(e,"name",{value:t,configurable:!0});import{CONDENSED_HISTORY_MODES as u,MOUSE_MODE_DEFAULTS as p,WHIP_MESSAGE_MODES as d}from"../types.js";function i(e,t,o){const r=e.indexOf(t),a=r>=0?r:0;return e[(a+o+e.length)%e.length]}s(i,"cycleChoice");function c(e,t,o){const r=e.indexOf(t);return r>=0?e[(r+o+e.length)%e.length]:o>=0?e.find(a=>a>t)??e[0]:[...e].reverse().find(a=>a<t)??e[e.length-1]}s(c,"cycleNumber");function n(e,t){return e==="true"?!0:e==="false"?!1:`${t} must be true or false`}s(n,"booleanFromString");function f(e,t){if(!/^\d+$/.test(e))return`${t} must be an integer >= 0`;const o=Number(e);return Number.isSafeInteger(o)?o:`${t} must be an integer >= 0`}s(f,"nonNegativeIntegerFromString");function _(e,t){const o=f(e,t);return typeof o=="string"||o<1?`${t} must be an integer >= 1`:o}s(_,"positiveIntegerFromString");const m=[!1,"notify","apply"],w=[0,1,6,12,24,48,72,168],b=[1,2,3,4,5,6,8,10,12],l="openai-codex";function g(e,t){const o=e.providerOptions[t];if(o!==void 0)return o;const r={};return e.providerOptions[t]=r,r}s(g,"providerOptionsFor");const x=[1,2,3,4,5,6],h={schema_version:{surface:"specialized",owner:"internal schema migration"},marketplaces:{surface:"specialized",owner:"Plugins and pkg market commands"},plugins:{surface:"specialized",owner:"Plugins tab and pkg plugin commands"},auto_update:{surface:"settings",settings:{crtr:{key:"auto_update.crtr",label:"crouter updates",description:"Notify about crouter releases, install them automatically, or turn checks off.",valueConstraint:"true|false|notify|apply; true stores notify",applies:"Applies on the next crouter command.",group:"updates",order:70,format:s(e=>e.auto_update.crtr===!1?"off":e.auto_update.crtr,"format"),cycle:s((e,t)=>{e.auto_update.crtr=i(m,e.auto_update.crtr,t)},"cycle"),setFromString:s((e,t)=>{const o=t==="true"?"notify":t;return o!=="notify"&&o!=="apply"&&o!=="false"?"auto_update.crtr must be notify, apply, or false":(e.auto_update.crtr=o==="false"?!1:o,null)},"setFromString")},content:{key:"auto_update.content",label:"Content updates",description:"Notify about plugin and marketplace updates, apply them automatically, or turn checks off.",valueConstraint:"false|notify|apply",applies:"Applies on the next crouter command.",group:"updates",order:80,format:s(e=>e.auto_update.content===!1?"off":e.auto_update.content,"format"),cycle:s((e,t)=>{e.auto_update.content=i(m,e.auto_update.content,t)},"cycle"),setFromString:s((e,t)=>t!=="notify"&&t!=="apply"&&t!=="false"?"auto_update.content must be notify, apply, or false":(e.auto_update.content=t==="false"?!1:t,null),"setFromString")},interval_hours:{key:"auto_update.interval_hours",label:"Update interval",description:"Minimum hours between automatic update checks. Zero checks on every command.",valueConstraint:"integer >= 0",applies:"Applies on the next crouter command.",group:"updates",order:90,format:s(e=>`${e.auto_update.interval_hours}h`,"format"),cycle:s((e,t)=>{e.auto_update.interval_hours=c(w,e.auto_update.interval_hours,t)},"cycle"),setFromString:s((e,t)=>{const o=f(t,"auto_update.interval_hours");return typeof o=="string"?o:(e.auto_update.interval_hours=o,null)},"setFromString")}}},max_panes_per_window:{surface:"settings",setting:{key:"max_panes_per_window",label:"Maximum panes",description:"Maximum human-review panes placed in one tmux window before using another window.",valueConstraint:"integer >= 1",applies:"Applies to the next pane placement.",group:"panes",order:20,format:s(e=>String(e.max_panes_per_window),"format"),cycle:s((e,t)=>{e.max_panes_per_window=c(b,e.max_panes_per_window,t)},"cycle"),setFromString:s((e,t)=>{const o=_(t,"max_panes_per_window");return typeof o=="string"?o:(e.max_panes_per_window=o,null)},"setFromString")}},auto_open_child_viewers:{surface:"settings",setting:{key:"auto_open_child_viewers",label:"Open child panes",description:"Open each managed child beside its watched parent. Off keeps new children running in the background.",valueConstraint:"true|false",applies:"Applies to the next managed child created; independent roots still open their attended viewer.",group:"panes",order:10,format:s(e=>e.auto_open_child_viewers?"on":"off","format"),cycle:s(e=>{e.auto_open_child_viewers=!e.auto_open_child_viewers},"cycle"),setFromString:s((e,t)=>{const o=n(t,"auto_open_child_viewers");return typeof o=="string"?o:(e.auto_open_child_viewers=o,null)},"setFromString")}},brokerThresholds:{surface:"specialized",owner:"Broker limits tab"},lifecycle:{surface:"specialized",owner:"sys config set lifecycle.unattendedParkMs"},completion_bell:{surface:"settings",setting:{key:"completion_bell",label:"Completion bell",description:"Ring a short sound when the node in an attached viewer finishes a turn.",valueConstraint:"true|false",applies:"Applies on the next turn end.",group:"display",order:41,format:s(e=>e.completion_bell?"on":"off","format"),cycle:s(e=>{e.completion_bell=!e.completion_bell},"cycle"),setFromString:s((e,t)=>{const o=n(t,"completion_bell");return typeof o=="string"?o:(e.completion_bell=o,null)},"setFromString")}},working_gerunds:{surface:"specialized",owner:"sys config set working_gerunds"},whip_mode:{surface:"settings",setting:{key:"whip_mode",label:"Whip mode",description:"Play the whip animation when you send a prompt and when a viewer opens for an agent with an initial prompt.",valueConstraint:"true|false",applies:"Applies on the next attach.",group:"display",order:39,format:s(e=>e.whip_mode?"on":"off","format"),cycle:s(e=>{e.whip_mode=!e.whip_mode},"cycle"),setFromString:s((e,t)=>{const o=n(t,"whip_mode");return typeof o=="string"?o:(e.whip_mode=o,null)},"setFromString")}},page_surface:{surface:"settings",setting:{key:"page_surface",label:"Page human surface",description:"This host reads pages in a browser rather than a terminal. Agents are told the page module runs as written, HTML pages are offered, and document review and live display leave the command tree.",valueConstraint:"true|false",applies:"Applies when human command help is assembled.",group:"display",order:37,format:s(e=>e.page_surface?"on":"off","format"),cycle:s(e=>{e.page_surface=!e.page_surface},"cycle"),setFromString:s((e,t)=>{const o=n(t,"page_surface");return typeof o=="string"?o:(e.page_surface=o,null)},"setFromString")}},whip_messages:{surface:"specialized",owner:"sys config set whip_messages"},whip_message_mode:{surface:"settings",setting:{key:"whip_message_mode",label:"Whip message",description:"What the whip action sends: a random line from whip_messages, or the fixed \u201CGo faster\u201D.",valueConstraint:d.join("|"),applies:"Applies on the next attach.",group:"display",order:40,format:s(e=>e.whip_message_mode==="go-faster"?"go faster":"random","format"),cycle:s((e,t)=>{e.whip_message_mode=i(d,e.whip_message_mode,t)},"cycle"),setFromString:s((e,t)=>d.includes(t)?(e.whip_message_mode=t,null):`whip_message_mode must be one of: ${d.join("|")}`,"setFromString")}},mouse_mode_default:{surface:"settings",setting:{key:"mouse_mode_default",label:"Default mouse mode",description:"Initial mouse wheel scrolling for each attached viewer.",valueConstraint:p.join("|"),applies:"Applies on the next attach.",group:"display",order:38,format:s(e=>e.mouse_mode_default==="tmux"?"follow tmux":e.mouse_mode_default,"format"),cycle:s((e,t)=>{e.mouse_mode_default=i(p,e.mouse_mode_default,t)},"cycle"),setFromString:s((e,t)=>p.includes(t)?(e.mouse_mode_default=t,null):`mouse_mode_default must be one of: ${p.join("|")}`,"setFromString")}},live_cycles:{surface:"settings",setting:{key:"live_cycles",label:"Live cycles",description:"Trailing refresh cycles rendered at full fidelity; older cycles condense to text.",valueConstraint:"integer >= 1",applies:"Applies on the next attach.",group:"display",order:50,format:s(e=>String(e.live_cycles),"format"),cycle:s((e,t)=>{e.live_cycles=c(x,e.live_cycles,t)},"cycle"),setFromString:s((e,t)=>{const o=_(t,"live_cycles");return typeof o=="string"?o:(e.live_cycles=o,null)},"setFromString")}},condensed_history:{surface:"settings",setting:{key:"condensed_history",label:"Condensed history",description:"Messages retained from condensed cycles. Tool calls are never kept.",valueConstraint:u.join("|"),applies:"Applies on the next attach.",group:"display",order:60,format:s(e=>e.condensed_history,"format"),cycle:s((e,t)=>{e.condensed_history=i(u,e.condensed_history,t)},"cycle"),setFromString:s((e,t)=>u.includes(t)?(e.condensed_history=t,null):`condensed_history must be one of: ${u.join("|")}`,"setFromString")}},bash_tool_purpose:{surface:"settings",setting:{key:"bash_tool_purpose",label:"Bash purpose field",description:"Expose the optional purpose label to agents in the bash tool schema. Off removes the field without disabling the bash valve.",valueConstraint:"true|false",applies:"Applies on the next node session.",group:"display",order:25,format:s(e=>e.bash_tool_purpose?"on":"off","format"),cycle:s(e=>{e.bash_tool_purpose=!e.bash_tool_purpose},"cycle"),setFromString:s((e,t)=>{const o=n(t,"bash_tool_purpose");return typeof o=="string"?o:(e.bash_tool_purpose=o,null)},"setFromString")}},fold_finished_tools:{surface:"settings",setting:{key:"fold_finished_tools",label:"Fold finished tools",description:"Fold every settled tool call to its call line. Toggle live with /fold-tools.",valueConstraint:"true|false",applies:"Applies on the next attach.",group:"display",order:30,format:s(e=>e.fold_finished_tools?"on":"off","format"),cycle:s(e=>{e.fold_finished_tools=!e.fold_finished_tools},"cycle"),setFromString:s((e,t)=>{const o=n(t,"fold_finished_tools");return typeof o=="string"?o:(e.fold_finished_tools=o,null)},"setFromString")}},summarize_tool_calls:{surface:"settings",setting:{key:"summarize_tool_calls",label:"Summarize tool calls",description:"Replace completed folded tool-call groups with a one-line broker summary when available.",valueConstraint:"true|false",applies:"Applies when the next visible message closes a tool-call group.",group:"display",order:35,format:s(e=>e.summarize_tool_calls?"on":"off","format"),cycle:s(e=>{e.summarize_tool_calls=!e.summarize_tool_calls},"cycle"),setFromString:s((e,t)=>{const o=n(t,"summarize_tool_calls");return typeof o=="string"?o:(e.summarize_tool_calls=o,null)},"setFromString")}},detailed_tool_recaps:{surface:"settings",setting:{key:"detailed_tool_recaps",label:"Detailed tool recaps",description:"Show each document read and edited file in folded tool recaps. Off shows a compact count line instead.",valueConstraint:"true|false",applies:"Applies on the next attach.",group:"display",order:36,format:s(e=>e.detailed_tool_recaps?"on":"off","format"),cycle:s(e=>{e.detailed_tool_recaps=!e.detailed_tool_recaps},"cycle"),setFromString:s((e,t)=>{const o=n(t,"detailed_tool_recaps");return typeof o=="string"?o:(e.detailed_tool_recaps=o,null)},"setFromString")}},consolidate_replies:{surface:"settings",setting:{key:"consolidate_replies",label:"Consolidate replies",description:"Experiment: show one rewritten agent reply per message you type, with the working folded behind it (/show-working).",valueConstraint:"true|false",applies:"Applies on the next attach; the broker reads it per turn.",group:"display",order:36.5,format:s(e=>e.consolidate_replies?"on":"off","format"),cycle:s(e=>{e.consolidate_replies=!e.consolidate_replies},"cycle"),setFromString:s((e,t)=>{const o=n(t,"consolidate_replies");return typeof o=="string"?o:(e.consolidate_replies=o,null)},"setFromString")}},keybindings:{surface:"specialized",owner:"Keybindings tab"},tmux_passthrough:{surface:"specialized",owner:"Keybindings tab"},providerOptions:{surface:"settings",settings:{"openai-codex.fastMode":{key:`providerOptions.${l}.fastMode`,label:"Codex fast mode",description:"Serve Codex turns at the ChatGPT priority tier: about 1.5x faster generation for about 2x the credits. Displayed cost does not reflect the premium.",valueConstraint:"true|false",applies:"Applies on each node's next turn.",group:"providers",order:40,format:s(e=>e.providerOptions[l]?.fastMode?"on":"off","format"),cycle:s(e=>{const t=g(e,l);t.fastMode=!t.fastMode},"cycle"),setFromString:s((e,t)=>{const o=n(t,`providerOptions.${l}.fastMode`);return typeof o=="string"?o:(g(e,l).fastMode=o,null)},"setFromString")}}},modelLadders:{surface:"specialized",owner:"Models tab"},modelRoutes:{surface:"specialized",owner:"Models route declarations"},modelRouting:{surface:"specialized",owner:"Models route declarations"},kinds:{surface:"specialized",owner:"Models tab and kind registry"},page_components:{surface:"specialized",owner:"Page component catalog: product-registered page component kinds"},remoteCanvas:{surface:"specialized",owner:"canvas config commands"},api:{surface:"specialized",owner:"sys connect"},paths:{surface:"specialized",owner:"runtime layout"},store:{surface:"specialized",owner:"daemon conversation store"},runtime:{surface:"specialized",owner:"app-facing listener"},spawnEnv:{surface:"specialized",owner:"spawn environment allow-list"},bin:{surface:"specialized",owner:"bare-binary contributions on every node PATH"},humanActions:{surface:"specialized",owner:"scope-configured human completion actions"}},O=Object.keys(h);function S(){const e=[];for(const t of Object.values(h))t.surface==="settings"&&("setting"in t?e.push(t.setting):e.push(...Object.values(t.settings)));return e.sort((t,o)=>t.order-o.order)}s(S,"collectUserSettings");const y=S();function A(e){return y.find(t=>t.key===e)}s(A,"findUserSetting");function z(e){return y.filter(t=>t.group===e)}s(z,"userSettingsInGroup");export{h as CONFIG_SETTINGS_COVERAGE,O as SCOPE_CONFIG_KEYS,y as USER_SETTINGS,A as findUserSetting,z as userSettingsInGroup};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var Se=Object.defineProperty;var a=(e,t)=>Se(e,"name",{value:t,configurable:!0});import{prepared as X}from"../../../core/sqlite-statements.js";import{envProfileId as Oe}from"../../../shared/env.js";import{pausedCanvasProfiles as Re}from"../../../core/profiles/manifest.js";import{existsSync as A,realpathSync as Z,statSync as q}from"node:fs";import{isAbsolute as F,resolve as T}from"node:path";import{appendSituationalContext as Ie,formatSituationalProse as $e}from"../../../core/runtime/situational-context.js";import{closeNode as Ce}from"../../../core/runtime/close.js";import{reviveNode as Ne}from"../../../core/runtime/revive.js";import{nowIso as G}from"../../../core/fs-utils.js";import{assertLaunchModelRegistered as B,forkNode as Ae,resolveForkSource as Te,spawnChildPrepared as Pe}from"../../../core/runtime/spawn.js";import{forkSourceNode as Ee,newNodeId as ee}from"../../../core/runtime/nodes.js";import{abandonCreateRequest as ne,childrenOf as De,completeCreateRequest as te,findCreateRequest as Le,getNode as k,recordCreateRequestNode as je,reserveCreateRequest as oe,reserveCreateRequestNode as Me,listNodes as re,recordRespawnAttempt as Fe,subscribersOf as Ge,subscriptionsOf as ie,setMessageWait as Be,updateNode as N}from"../../../core/canvas/canvas.js";import{subtreeIds as xe}from"../../../core/canvas/nav-model.js";import{withCanvasWrite as x}from"../../../core/canvas/db.js";import{nodeObject as Ke}from"../../../core/graph/objects.js";import{recordActedOn as We}from"../../../core/graph/edges.js";import{publish as Ve}from"../../../core/graph/events.js";import{readerFor as ze}from"../reader.js";import{relocateSessionCwd as Je,unbindSession as Ue}from"../../../core/canvas/sessions.js";import{ticketCountsForNodes as He}from"../../../core/canvas/attention.js";import{isSafeNodeId as Ye}from"../../../core/canvas/paths.js";import{contextDir as de,nodeAgentDir as K}from"../../../core/canvas/node-agent-paths.js";import{checkCronBirth as Qe}from"../../cron/authority.js";import{cronBirthPlacement as Xe}from"../../cron/armed-context.js";import{existsBeneath as Ze,isAbsentOrRefused as qe,isBeneathRefusal as en,readdirBeneath as nn}from"../../../core/spaces/open-beneath.js";import{readNodeMessagesPage as tn,readNodeSession as on,readNodeSnapshot as rn,transcriptMarkdown as dn}from"../../../core/runtime/node-read.js";import{assembleNodeSubject as sn}from"../../../core/substrate/subject.js";import{reviveAll as an}from"../../../core/runtime/revive-all.js";import{promoteAsync as ln,requestYieldAsync as cn,reshapeNodeAsync as W}from"../../../core/runtime/promote.js";import{recycleNode as fn}from"../../../core/runtime/recycle.js";import{relaunchRoot as un}from"../../../core/runtime/reset.js";import{assertReopenGate as pn}from"../../../core/runtime/reopen.js";import{writeYieldMessage as mn,readGoal as hn}from"../../../core/runtime/kickoff.js";import{ensureRoadmap as wn,hasRoadmap as yn}from"../../../core/runtime/roadmap.js";import{renameWindow as _n}from"../../../core/runtime/placement.js";import{buildLaunchSpecAsync as gn,explicitModelIntent as bn,normalizeModel as vn,readMergedLaunchConfigAsync as se}from"../../../core/runtime/launch.js";import{modelRequestFromConfig as kn}from"../../../core/model-routes.js";import{persistDormantModel as Sn,setModelLive as On}from"../../../core/runtime/model-swap.js";import{waitForBrokerViewSocket as Rn}from"../../../core/runtime/view-socket.js";import{BrokerUnreachableError as In}from"../../../core/runtime/broker-request.js";import{boundFleet as V}from"../../../core/runtime/fleet.js";import{admitLegacyStructuredOutput as $n}from"../../structured-requests/sidecar.js";import{OUTCOME_PAYLOAD_MAX_BYTES as ae}from"../../../core/canvas/types.js";import{HUMAN_ACTION_NAME_PATTERN as Cn}from"../../../core/human/action-binding.js";import{resolveHumanAction as Nn}from"../../../core/human-actions.js";import{parseDurationMs as An}from"../../../core/wake.js";import{claimWarmNode as Tn,refillWarmPool as le,resumeWarmClaim as Pn,warmPoolEnabled as En}from"../../../core/runtime/warm-pool.js";import{assertLaunchKind as z,isLaunchKind as Dn,resolveLaunchTarget as Ln}from"../../../core/runtime/launch-target.js";import{fullName as jn}from"../../../core/canvas/labels.js";import{reportsForNode as ce}from"./reports.js";import{requireUsableProfile as Mn}from"./profiles.js";import{assertProfileActive as Fn,loadExactProfileManifest as Gn}from"../../../core/profiles/manifest.js";import{getCron as fe}from"../../../core/canvas/crons.js";import{cronWakeOrigin as Bn}from"../../../core/runtime/bearings.js";import{boundDaemonControl as xn}from"../../control.js";import{notFound as P,usage as h}from"../../../core/errors.js";import{assertScope as Kn,validateScopes as ue}from"../../../core/scopes.js";import{scopeAllowed as R}from"../../../shared/env.js";import{toCloseResultDTO as Wn,toNodeDetailDTO as J,toNodeSummaryDTO as Vn,toReviveResultDTO as zn}from"../map.js";import{ApiError as m}from"../../../api/index.js";import{InputError as w}from"../../../core/io.js";import{brokerMayReach as U,canReadNode as pe,resolvedScopes as Jn}from"../router.js";import{fileRoot as Un}from"../../../core/spaces/file-root.js";import{activeGrant as Hn,terminalGrant as Yn}from"../../../core/grants/mirror.js";import{readConfig as Qn}from"../../../core/config.js";import{hasConfiguredProviderCredential as Xn,NO_PROVIDER_CREDENTIAL_MESSAGE as me}from"../../../core/runtime/provider-credential-gate.js";function y(e){const t=k(e);if(t===null)throw P(`unknown node: ${e}`,{received:e});return t}a(y,"requireMeta");function H(e){return{subscribesTo:ie(e),subscribers:Ge(e),children:De(e)}}a(H,"edgesFor");function g(e){const t=y(e);return J(t,H(e))}a(g,"detailOf");function Zn(e){let t=ce(e).length;try{t+=nn(K(e),de(e)).length}catch(n){if(!qe(n))throw n}return t}a(Zn,"countFiles");function qn(e){if(typeof e!="object"||e===null)throw h("a node create requires a JSON body");const t=e;if(t.kind!==void 0&&(typeof t.kind!="string"||t.kind.trim()===""))throw h("kind must be a non-empty string when provided");if(t.description!==void 0&&(typeof t.description!="string"||t.description.trim()===""))throw h("description must be a non-empty string when provided");if(t.creator!==void 0&&(typeof t.creator!="string"||t.creator.trim()===""))throw h("creator must be a non-empty node id when provided");if(t.scopes!==void 0&&ue(t.scopes),t.isolated!==void 0&&typeof t.isolated!="boolean")throw new m(400,"invalid_request","isolated must be a boolean",{param:"isolated"});const n=t.root_lifecycle;if(n!==void 0&&n!=="resident"&&n!=="terminal")throw h("root_lifecycle must be 'resident' or 'terminal'");if(t.request_key!==void 0&&(typeof t.request_key!="string"||t.request_key.trim()===""||t.request_key.length>128))throw h("request_key must be a non-empty string of at most 128 characters when provided");return t}a(qn,"parseCreateBody");function et(e){if(e===void 0)return;const t=An(e),n=t===null?null:new Date(Date.now()+t);if(t===null||!Number.isFinite(t)||t<=0||n===null||!Number.isFinite(n.getTime()))throw new m(422,"invalid_deadline","`deadline` must be a positive representable duration");return n.toISOString()}a(et,"parseDeadline");function nt(e){if(e!==void 0){if(typeof e!="object"||e===null||Array.isArray(e))throw new m(400,"invalid_request","`outcome_delivery` must be an object");if(typeof e.action!="string"||!Cn.test(e.action))throw new m(400,"invalid_request","`outcome_delivery.action` must be a declared action name");if(e.payload!==void 0){let t;try{t=JSON.stringify(e.payload)}catch{throw new m(400,"invalid_request","`outcome_delivery.payload` must be JSON-serializable")}if(t===void 0)throw new m(400,"invalid_request","`outcome_delivery.payload` must be JSON-serializable");if(Buffer.byteLength(t,"utf8")>ae)throw new m(413,"payload_too_large",`serialized payload exceeds ${ae} bytes`)}return e}}a(nt,"parseOutcomeDelivery");function tt(e,t){const n=Nn(e.action,t);if(!("kind"in n))return{actionName:e.action,argv:n.argv,cwd:n.cwd,configOrigin:n.configOrigin,...e.payload===void 0?{}:{payload:e.payload}};throw n.kind==="unknown_action"?new m(422,"unknown_action",`no action named ${e.action} is declared from ${t}`):new m(422,"invalid_action",`${e.action} is declared in ${n.configOrigin} but unusable: ${n.reason}`,{reason:n.reason,configOrigin:n.configOrigin})}a(tt,"resolveOutcomeDelivery");function ot(e,t,n){const r={kind:e.kind,cwd:n,prompt:e.prompt??""};return e.mode!==void 0&&(r.mode=e.mode),e.name!==void 0&&(r.name=e.name),e.description!==void 0&&(r.description=e.description),t!==void 0&&(r.parent=t),e.creator!==void 0&&(r.creator=e.creator),e.scopes!==void 0&&(r.scopes=e.scopes),e.isolated!==void 0&&(r.isolated=e.isolated),e.root===!0&&(r.root=!0),e.root_lifecycle!==void 0&&(r.rootLifecycle=e.root_lifecycle),(e.worktree===!0||typeof e.worktree=="string")&&(r.worktree=!0),typeof e.worktree=="string"&&(r.worktreeBase=e.worktree),e.worktree!==void 0&&(r.worktreeCwd=T(e.pin_cwd??e.cwd??n)),e.fork_from!==void 0&&(r.forkFrom=e.fork_from),e.model!==void 0&&(r.model=e.model),e.profile!==void 0&&(r.profile=e.profile),e.situational_context!==void 0&&(r.situationalContext=e.situational_context),e.node_id!==void 0&&(r.nodeId=e.node_id),r}a(ot,"buildRecipe");function rt(e,t,n){return!En()||e.prefer_warm!==!0||e.root!==!0||e.parent!=null||e.root_lifecycle==="terminal"||(e.prompt??"")!==""||e.model!==void 0||e.fork_from!==void 0||e.worktree!==void 0||e.node_id!==void 0||e.scopes!==void 0||e.isolated!==void 0?null:{kind:e.kind,mode:e.mode??"base",cwd:t,profileId:n,model:e.model??null,situationalContext:e.situational_context??null,name:e.name??null,creator:e.creator??null}}a(rt,"warmRequestFor");import{resolveCreateKind as Qo}from"../../../core/runtime/launch-target.js";function he(e){const t=e.modelIntent===void 0?void 0:`${e.modelIntent.family}/${e.modelIntent.strength}`;return kn(e.model,t,e.modelExact===!0)}a(he,"requestFromLaunch");function it(e,t){if(t===null)return e;if(e.scopes===void 0)return{...e,scopes:[...t]};const n=e.scopes.filter(r=>!R(t,r));if(n.length===0)return e;throw new m(403,"scope_denied",`scopes exceed the bearer token's ceiling: ${n.join(", ")}`,{scopes:n,ceiling:t})}a(it,"withinCeiling");async function we(e,t,n){const r=(await gn(t.kind,t.mode,{lifecycle:t.lifecycle,hasManager:e.parent!==null,model:t.model,modelOverride:!0,cwd:e.cwd,profileId:e.profile_id??null})).launch;await B(he(r),{cwd:e.cwd,profileId:e.profile_id??null},n)}a(we,"assertNodeModelChangeRegistered");function ye(e,t){const n=e.principal;if(n.class!=="broker"&&n.class!=="cron")return;const r=Un({app:n.app,resolved:Jn(n),launched:n.scopes},t,"read");if("root"in r)return;const o="missing"in r?r.missing:null;throw new m(403,"scope_missing",o?`the child's directory ${t} needs missing scope ${o}`:`no permission of this run names the child's directory ${t}`,void 0,void 0,o?{scope:o}:void 0)}a(ye,"assertCallerReaches");const Y=new Map;async function dt(e,t={},n={}){const r=it(qn(e.body),e.ceiling);if(e.principal.class==="broker"){const l=e.principal;if(r.prefer_warm===!0&&l.app!=="app:terminal"&&(r.prefer_warm=!1),r.parent!=null){const i=y(r.parent);if(i.grantee!==l.app)throw new m(403,"other_app_run","cannot create a child under another app's node");if(!U(l,r.parent,i.grantee??"app:terminal"))throw P(`unknown node: ${r.parent}`,{received:r.parent})}if(r.creator!==void 0&&r.creator!==l.node)throw new m(403,"other_app_run","cannot create for another node");if(r.fork_from!==void 0){let i=null;try{i=Ee(Te(r.fork_from))}catch{}if(i?.grantee!==l.app||!U(l,i.node_id,i.grantee))throw new m(403,"other_app_run","cannot fork another app's conversation")}r.creator=l.node}if(e.principal.class==="cron"){const l=e.principal;for(const c of["parent","creator","fork_from","node_id"])if(r[c]!=null)throw new m(403,"permission_error",`a cron cannot set ${c} on a node it starts`);r.prefer_warm=!1;const i=Qe({grantee:l.app,sub:l.sub,grant_id:l.grant,scopes:l.scopes});if(!i.allowed)throw new m(i.reason==="scope_missing"?403:401,i.reason==="scope_missing"?"scope_missing":"grant_revoked",i.message);if(r.scopes!==void 0){const c=r.scopes.find(p=>!R(i.effectiveScopes,p)||!R(l.scopes,p));if(c!==void 0)throw new m(403,"scope_missing",`scope ${c} is not held by this cron`,void 0,void 0,{scope:c})}else l.scopes!==null&&(r.scopes=[...l.scopes]);r.node_id=ee(),l.isolated&&(r.isolated=!0,r.cwd=K(r.node_id,l.app))}r.creator!==void 0&&Kn(y(r.creator),"crtr:act");const o=r.request_key?.trim();if(o===void 0||r.node_id!==void 0&&Ye(r.node_id)&&k(r.node_id)!==null&&Le(o)?.nodeId!==r.node_id)return E(e,r,t,n);const d=Y.get(o);if(d!==void 0)return d;const s=st(e,r,o,t,n);Y.set(o,s);try{return await s}finally{Y.delete(o)}}a(dt,"handleCreate");async function st(e,t,n,r,o){const d=oe(n);if(d.nodeId!==null&&k(d.nodeId)!==null){if(d.state!=="warm_claimed"){const s=k(d.nodeId);if(e.principal.class!=="person"&&!pe(e.principal,s.grantee??"app:terminal",d.nodeId))throw new m(409,"create_request_conflict","request_key belongs to another caller");return{status:201,body:g(d.nodeId)}}return E(e,t,r,o,d)}try{return await E(e,t,r,o,d)}catch(s){const l=oe(n);throw(l.nodeId===null||k(l.nodeId)===null)&&ne(n,l.nodeId),s}}a(st,"handleKeyedCreateRequest");async function E(e,t,n,r,o=void 0){const d=r.deadlineAt??et(t.deadline),s=nt(t.outcome_delivery),l=e.principal.class==="cron"?Xe({grantee:e.principal.app,sub:e.principal.sub,grant_id:e.principal.grant,scopes:e.principal.scopes,cron_id:e.principal.cron,origin_run_id:e.principal.origin_run,isolated:e.principal.isolated,creator:fe(e.principal.cron)?.created_by??null},t.node_id):null;l!==null&&(t.root=l.root===!0);const i=l?.parent??t.parent??void 0;if(i==null&&t.root!==!0)throw h("a node create requires a parent (or root: true for an independent root)");let c=null;if(t.output_schema!==void 0)try{c=JSON.parse(t.output_schema)}catch{throw h("output_schema must be a JSON schema string")}const{profileId:p,kind:u,cwd:f,launch:_}=await Ln(t,{validateKind:ke});ye(e,f),t.worktree!==void 0&&ye(e,T(t.pin_cwd??t.cwd??f));const S={...t,kind:u},I=s===void 0?void 0:tt(s,f);if(await B(he(_),{cwd:f,profileId:p},n.modelRegistry),!await Xn(p))throw new m(412,"no_provider_credential",me,{error:"no_provider_credential",message:me,next:"The person running this runtime logs a provider in with `crtr sys setup`, or stores a provider key on the profile."});let L=null;{const $=o===void 0||o.nodeId===null||o.state==="warm_claimed"?rt(S,f,p):null,j=c===null&&r.beforeBrokerLaunch===void 0?void 0:v=>{c!==null&&$n(v,"terminal",c),r.beforeBrokerLaunch?.(v)};if(o?.state==="warm_claimed"&&o.nodeId!==null){const v=$===null?null:await Pn($,o.nodeId,j);if(v===null)throw ne(o.requestKey,o.nodeId),new m(503,"create_resume_failed",`could not resume warm claim ${o.nodeId}; retry the create`);return te(o.requestKey,v),{status:201,body:g(v)}}if($!==null){const v=await Tn($,{...d===void 0?{}:{deadlineAt:d},...I===void 0?{}:{outcomeDelivery:I},...o===void 0?{}:{requestKey:o.requestKey}},j);if(v!==null)return o!==void 0&&te(o.requestKey,v),le($),{status:201,body:g(v)};L=$}const M=o?.nodeId??t.node_id??(o===void 0?void 0:ee());o!==void 0&&o.nodeId===null&&Me(o.requestKey,M);const O=ot(M===void 0?S:{...S,node_id:M},i,f);l!==null&&(l.rootLifecycle!==void 0&&(O.rootLifecycle=l.rootLifecycle),l.scopes!==void 0&&S.scopes===void 0&&(O.scopes=l.scopes),l.run!==void 0&&(O.run=l.run),l.withinCreate!==void 0&&(O.withinCreate=l.withinCreate)),d!==void 0&&(O.deadlineAt=d),I!==void 0&&(O.outcomeDelivery=I),O.readiness=t.root===!0?"await":"launch";const Q=await Pe(O,j,()=>{L!==null&&le(L)});return o!==void 0&&je(o.requestKey,Q.node.node_id),{status:201,body:g(Q.node.node_id)}}}a(E,"handleCreateRequest");function b(e,t){const n=e.query.getAll(t);if(n.length>1)throw h(`${t} must appear at most once`);return n[0]}a(b,"singleQueryValue");function _e(e,t){if(e!==void 0){if(e==="1"||e==="true")return!0;if(e==="0"||e==="false")return!1;throw h(`${t} must be true or false`)}}a(_e,"optionalBoolean");function D(e,t,n){if(e!==void 0){if(n.includes(e))return e;throw h(`${t} must be one of: ${n.join(", ")}`)}}a(D,"oneOf");function at(e){const t=new Set(["status","lifecycle","kind","mode","profile_id","profile_prefix","parent","top_level","under","hanging","include"]);for(const[_]of e.query)if(!t.has(_))throw h(`unknown nodes query parameter: ${_}`);const n=D(b(e,"status"),"status",["active","idle","done","dead","canceled"]),r=D(b(e,"lifecycle"),"lifecycle",["terminal","resident"]),o=b(e,"kind"),d=D(b(e,"mode"),"mode",["base","orchestrator"]),s=b(e,"profile_id"),l=b(e,"profile_prefix"),i=b(e,"parent"),c=_e(b(e,"top_level"),"top_level"),p=b(e,"under"),u=_e(b(e,"hanging"),"hanging"),f=D(b(e,"include"),"include",["activity"]);if(i!==void 0&&c===!0)throw h("parent and top_level=true cannot be combined");return{...n!==void 0?{status:n}:{},...r!==void 0?{lifecycle:r}:{},...o!==void 0?{kind:o}:{},...d!==void 0?{mode:d}:{},...s!==void 0?{profile_id:s}:{},...l!==void 0?{profile_prefix:l}:{},...i!==void 0?{parent:i}:{},...c!==void 0?{top_level:c}:{},...p!==void 0?{under:p}:{},...u!==void 0?{hanging:u}:{},...f!==void 0?{include:f}:{}}}a(at,"parseListQuery");function lt(e){const t=at(e);let n=t.status!==void 0?re({status:t.status}):re();if(e.principal.class!=="person"&&(n=n.filter(s=>pe(e.principal,s.grantee??"app:terminal",s.node_id))),t.lifecycle!==void 0&&(n=n.filter(s=>s.lifecycle===t.lifecycle)),t.kind!==void 0&&(n=n.filter(s=>s.kind===t.kind)),t.mode!==void 0&&(n=n.filter(s=>s.mode===t.mode)),t.profile_id!==void 0&&(n=n.filter(s=>s.profile_id===t.profile_id)),t.profile_prefix!==void 0&&(n=n.filter(s=>s.profile_id?.startsWith(t.profile_prefix))),t.parent!==void 0&&(n=n.filter(s=>s.parent===t.parent)),t.top_level===!0&&(n=n.filter(s=>s.parent===null)),t.under!==void 0){const s=new Set(xe(t.under));n=n.filter(l=>s.has(l.node_id))}t.hanging===!0&&(n=n.filter(s=>s.parent!==null&&k(s.parent)===null));const r=t.include==="activity"?He(n.map(s=>s.node_id)):null,o=n.length>0?Re():new Set;return{status:200,body:n.map(s=>{const l=Vn(s,s.profile_id!==null&&o.has(s.profile_id));if(r!==null){const i=ce(s.node_id);l.activity={latest_report:i[0]??null,final_report:s.final_report===null?null:i.find(c=>c.name===s.final_report)??null,pending_human_count:r[s.node_id]??0}}return l})}}a(lt,"handleList");function ct(e){const t=e.params.id;return y(t),{status:200,body:g(t)}}a(ct,"handleDetail");async function ft(e){const t=e.params.id;y(t);const n=await rn(t);return{status:200,body:{node_id:t,snapshot:{messages:n.snapshot.messages,...n.snapshot.messageIds===void 0?{}:{messageIds:n.snapshot.messageIds},messageVisibility:n.snapshot.messageVisibility,turnVisibility:n.snapshot.turnVisibility,stats:n.snapshot.stats,state:n.snapshot.state,display:n.snapshot.display,...n.snapshot.queued===void 0?{}:{queued:n.snapshot.queued},...n.snapshot.toolGroupSummaries===void 0?{}:{toolGroupSummaries:n.snapshot.toolGroupSummaries},...n.snapshot.workingActivity===void 0?{}:{workingActivity:n.snapshot.workingActivity}},commands:n.commands.map(o=>({name:o.name,description:o.description,source:o.source})),captured_at:G()}}}a(ft,"handleSnapshot");async function ut(e){const t=e.params.id,n=sn(t);if(n===null)throw P(`unknown node: ${t}`,{received:t});return{status:200,body:n}}a(ut,"handleSubject");function pt(e){for(const[d]of e.query)if(d!=="cursor"&&d!=="limit")throw h(`unknown messages query parameter: ${d}`);const t=e.query.getAll("cursor"),n=e.query.getAll("limit");if(t.length>1)throw h("cursor must appear at most once");if(n.length>1)throw h("limit must appear at most once");if(n.length===0)return t.length===0?{}:{cursor:t[0]};const r=n[0],o=Number(r);if(!Number.isSafeInteger(o)||r==="")throw h("limit must be an integer from 1 through 500");return t.length===0?{limit:o}:{cursor:t[0],limit:o}}a(pt,"parseMessagesQuery");async function mt(e){const t=e.params.id,n=await tn(t,pt(e));return{status:200,body:{node_id:t,messages:n.messages,...n.messageIds===void 0?{}:{message_ids:n.messageIds},message_visibility:n.messageVisibility,next_cursor:n.nextCursor,captured_at:G()}}}a(mt,"handleMessages");function ht(e){const t=e.params.id;y(t);const n=on(t);return{status:200,body:{node_id:t,session_file:n.sessionFile,session_jsonl:n.sessionJsonl,system_prompt:n.systemPrompt,captured_at:G()}}}a(ht,"handleSession");async function wt(e){const t=e.params.id;return y(t),{status:200,body:{node_id:t,markdown:await dn(t)}}}a(wt,"handleTranscript");function yt(e){const t=e.params.id;y(t);const n=a((o,d)=>({node_id:o,label:k(o)?.name??o,dir:de(o),self:d,files:Zn(o)}),"root"),r=[n(t,!0),...ie(t).map(o=>n(o.node_id,!1))].filter(o=>_t(o.node_id,o.dir)||o.files>0);return{status:200,body:{node_id:t,roots:r}}}a(yt,"handleContext");function _t(e,t){try{return Ze(K(e),t)}catch(n){if(en(n))return!1;throw n}}a(_t,"hasDir");async function gt(e){const t=y(e.params.id),n=e.principal.class==="broker"?e.principal.scopes:e.ceiling,r=n===null?void 0:t.scopes==null?[...n]:[...new Set([...t.scopes.filter(d=>R(n,d)),...n.filter(d=>R(t.scopes,d))])],o=await Ae(t.node_id,r,e.principal.class==="broker"?e.principal.node:void 0);return{status:201,body:g(o.node.node_id)}}a(gt,"handleFork");async function bt(e){const t=e.params.id;y(t);const n=e.body,r=n?.resume??!0,o=n?.reopen??!1;pn(t,o);const d=!r&&n?.cron_id!==void 0?fe(n.cron_id):null,s=await Ne(t,{resume:r,capacity:"refuse",...d!==null?{wakeReason:Bn(d,"schedule_revive")}:{}});s.outcome!=="already-live"&&Fe(t,{failures:0,notBefore:null});const l=k(t)?.status??"active";return{status:200,body:zn(t,s,l)}}a(bt,"handleRevive");async function vt(e){const t=e.params.id;return{status:200,body:{newNodeId:(await un(t))?.newNodeId??null}}}a(vt,"handleRelaunchRoot");function kt(e){const t=e.params.id;y(t);const n=e.body;if(n?.caller_pid!==void 0&&(!Number.isInteger(n.caller_pid)||n.caller_pid<=0))throw h("caller_pid must be a positive integer when provided");const r=xn(),o=Ce(t,{rootEvent:n?.finish===!0?"finish":"cancel",callerPid:n?.caller_pid,...e.principal.class==="broker"?{closer:e.principal.node}:{},...r===null?{}:{registerDetached:r.registerDetached}});return{status:200,body:Wn(o)}}a(kt,"handleClose");async function St(e){const t=e.params.id;y(t);const n=await fn(t);return{status:200,body:{recycled:n.recycled,finalized:n.finalized,new_root:n.newRoot,delivered:n.delivered}}}a(St,"handleRecycle");async function Ot(e){const t=e.params.id;return y(t),await W(t,{lifecycle:"terminal"}),C(e,t,["lifecycle"]),{status:200,body:g(t)}}a(Ot,"handleDemote");async function Rt(e,t={}){const n=e.params.id,r=y(n),o=e.body??{};o.kind!==void 0&&await z(o.kind,r.cwd,r.profile_id??null),o.model!==void 0&&await we(r,{kind:o.kind??r.kind,mode:"orchestrator",lifecycle:o.resident===!0?"resident":r.lifecycle,model:o.model},t.modelRegistry);const d=await ln(n,{...o.kind!==void 0?{kind:o.kind}:{},...o.resident===!0?{resident:!0}:{},...o.model!==void 0?{model:o.model}:{}});C(e,n,["mode",...o.kind!==void 0?["kind"]:[],...o.resident===!0?["lifecycle"]:[],...o.model!==void 0?["model"]:[]]);const s=J(d.meta,H(n));return s.roadmap_written=d.roadmapWritten,s.roadmap_ref=d.roadmapRef,s.goal_path=d.goalPath,{status:200,body:s}}a(Rt,"handlePromote");async function It(e,t={}){const n=e.params.id,r=y(n),o=e.body??{};o.kind!==void 0&&await z(o.kind,r.cwd,r.profile_id??null),o.model!==void 0&&await we(r,{kind:o.kind??r.kind,mode:o.promote===!0?"orchestrator":r.mode,lifecycle:r.lifecycle,model:o.model},t.modelRegistry);const d=o.note??"Refresh: continue against your roadmap or goal.";mn(n,d);const s=await cn(n,{...o.promote===!0?{promote:!0}:{},...o.kind!==void 0?{kind:o.kind}:{},...o.model!==void 0?{model:o.model}:{}});return{status:200,body:J(s.meta,H(n))}}a(It,"handleYield");function $t(e){const t=e.params.id;y(t);const n=e.body;if(n===void 0||typeof n.controller!="string"||n.controller.trim()==="")throw h("a wait requires a controller node id");const r=k(n.controller);if(r===null)throw P(`unknown controller: ${n.controller}`,{received:n.controller});if(r.status!=="active"&&r.status!=="idle")throw new m(422,"unprocessable",`controller ${n.controller} is ${r.status}`);return Be(t,n.controller),{status:200,body:g(t)}}a($t,"handleWait");async function Ct(e){const t=e.principal,n=await an(t.class==="broker"?o=>o.grantee===t.app&&U(t,o.node_id,o.grantee):void 0);return{status:200,body:{revived:[...n.revived],failed:[...n.failed],frozen:[...n.frozen]}}}a(Ct,"handleReviveAll");const Nt=new Set(["interrupt","steer","after-turn","next-turn"]);function At(e){if(e===null)return null;const t=a(r=>{throw new w({error:"invalid_listen",message:r,received:e,field:"listen",next:'Send an object mapping levels "1".."10" to interrupt|steer|after-turn|next-turn (e.g. {"3":"next-turn"}), or null to reset to the global default.'})},"bad");if(typeof e!="object"||Array.isArray(e))return t("listen must be an object or null");const n=Object.entries(e);if(n.length===0)return t("listen must name at least one level");for(const[r,o]of n)/^(?:[1-9]|10)$/.test(r)||t(`listen level must be 1..10: ${r}`),(typeof o!="string"||!Nt.has(o))&&t(`listen delivery must be interrupt|steer|after-turn|next-turn: ${String(o)}`);return JSON.stringify(Object.fromEntries(n))}a(At,"listenJson");function C(e,t,n,r){n.length===0&&r===void 0||x(o=>{if(r?.(o),n.length===0)return;const d=Ke(o,t),s=ze(o,e),l=s.class==="person"?s.person:s.class==="app"?s.app:s.node;We(o,l,d),Ve(o,{event:"configured",object:d,actor:l,attrs:{changed:n.join(",")}})})}a(C,"recordConfigured");function ge(e){const t=V().get(e);if(t===void 0)return!1;try{return process.kill(t.pid,"SIGTERM"),!0}catch{return!1}}a(ge,"restartLiveBroker");function be(e,t){try{return Z(e)===Z(t)}catch{return!1}}a(be,"sameDirectory");async function Tt(e,t={}){const n=e.params.id,r=e.body;let o;if(r===void 0)o={};else if(typeof r=="object"&&r!==null&&!Array.isArray(r))o=r;else throw new w({error:"cwd_invalid",message:"cwd repair requires a non-null object with a non-empty cwd string",received:r,field:"cwd",next:"Send a config patch with a non-empty absolute cwd string."});if(o.profile!==void 0){if(typeof o.profile!="string"||o.profile==="")throw new w({error:"profile_invalid",message:"profile must be a non-empty profile id or name",received:o.profile,field:"profile",next:"Send a profile id or exact name."});if(Object.keys(o).some(u=>u!=="profile"&&u!=="restart"&&o[u]!==void 0))throw new w({error:"profile_not_exclusive",message:"profile can only be combined with restart",field:"profile",next:"Send profile (and optionally restart) alone."});const i=y(n),c=Mn(e,o.profile);Fn(c.profileId);const p=Gn(c.profileId).manifest.grantee??"app:terminal";if(p!==(i.grantee??"app:terminal"))throw new w({error:"profile_grantee_mismatch",message:`profile ${c.profileId} belongs to ${p}, node ${n} to ${i.grantee}`,received:o.profile,field:"profile",next:"Pick a profile owned by the same app as the node."});return(i.profile_id??null)!==c.profileId&&(Dn(i.kind)?(await z(i.kind,i.cwd,c.profileId),await W(n,{profileId:c.profileId})):N(n,{profile_id:c.profileId}),C(e,n,["profile"])),o.restart===!0&&ge(n),{status:200,body:g(n)}}if(Object.prototype.hasOwnProperty.call(o,"cwd")){const i=o.cwd;if(typeof i!="string"||i==="")throw new w({error:"cwd_invalid",message:"cwd must be a non-empty string",received:i,field:"cwd",next:"Send a non-empty absolute cwd string."});if(Object.keys(o).some(f=>f!=="cwd"&&f!=="keep_session"&&f!=="restart"&&o[f]!==void 0))throw new w({error:"cwd_not_exclusive",message:"cwd repair cannot be combined with another config field",received:o,field:"cwd",next:"Send cwd as the only config field."});const c=y(n),p=A(c.cwd);if(o.keep_session===!0||p&&F(i)&&A(i)&&be(c.cwd,i)){if(!F(i))throw new w({error:"cwd_not_absolute",message:`cwd must be an absolute path: ${i}`,received:i,field:"cwd",next:"Send an absolute path."});const f=T(i);let _=!1;try{_=q(f).isDirectory()}catch{}if(!_)throw new w({error:"cwd_missing",message:`cwd is not an existing directory: ${f}`,received:i,field:"cwd",next:"Send an existing absolute directory."});if(p&&!be(c.cwd,f))throw new w({error:"cwd_not_same_directory",message:`${f} is not the same directory as the recorded ${c.cwd}; --keep-session only relocates an alias`,received:i,field:"cwd",next:"Drop --keep-session to repair a gone cwd, or pass the real path of the recorded directory."});if(c.managed_worktree?.state==="open")throw new w({error:"cwd_node_managed_worktree_open",message:`${n} owns an open managed worktree and cannot relocate its cwd`,received:i,field:"cwd",next:"Reconcile the managed worktree first."});const S=c.cwd;return S!==f&&(x(I=>{N(n,{cwd:f}),X(I,"UPDATE crons SET cwd = ? WHERE anchor_node = ? AND cwd = ?").run(f,n,S),Je(n,S,f)}),C(e,n,["cwd"])),o.restart===!0&&ge(n),{status:200,body:g(n)}}if(V().has(n)){const f=`${n} cannot repair its cwd while its broker is live`;throw new m(409,"cwd_node_live",f,{error:"cwd_node_live",message:f,received:i,field:"cwd",next:"Let the node become dormant, then retry the cwd repair."})}if(c.fork_from!=null)throw new w({error:"cwd_node_forked",message:`${n} is a fork and cannot repair its cwd; recorded cwd: ${c.cwd}`,received:i,field:"cwd",next:`Restore ${c.cwd}, then revive the fork normally.`});if(c.managed_worktree?.state==="open")throw new w({error:"cwd_node_managed_worktree_open",message:`${n} owns an open managed worktree and cannot repair its cwd`,received:i,field:"cwd",next:"Restore or reconcile the recorded checkout, then use the managed-worktree close or abandon flow."});if(A(c.cwd))throw new w({error:"cwd_still_present",message:`${n} recorded cwd still exists: ${c.cwd}; cwd repair is not a relocation command`,received:i,field:"cwd",next:"This command only repairs a node whose recorded cwd is gone."});if(!F(i))throw new w({error:"cwd_not_absolute",message:`cwd must be an absolute path: ${i}`,received:i,field:"cwd",next:"Send an absolute path."});const u=T(i);if(!A(u))throw new w({error:"cwd_missing",message:`cwd does not exist: ${u}`,received:i,field:"cwd",next:"Create the replacement directory or send an existing absolute directory."});try{if(!q(u).isDirectory())throw new w({error:"cwd_not_a_directory",message:`cwd is not a directory: ${u}`,received:i,field:"cwd",next:"Send an existing absolute directory."})}catch(f){throw f instanceof w?f:new w({error:"cwd_missing",message:`cwd does not exist: ${u}`,received:i,field:"cwd",next:"Create the replacement directory or send an existing absolute directory."})}return x(()=>{N(n,{cwd:u}),Ue(n)}),C(e,n,["cwd"]),{status:200,body:g(n)}}let d=y(n);const s=o.listen===void 0?void 0:At(o.listen);if(o.scopes!==void 0){const i=ue(o.scopes),c=d.grantee==="app:terminal"?Yn():d.grant_id?Hn(d.grant_id):null,p=d.grantee==="app:terminal"&&!Qn("user").runtime?.issuer;if(!i.every(u=>R(d.scopes,u)&&(c?R(c.resolved,u):p)))throw new w({error:"unknown_scope",message:"scopes may only narrow the node's current effective set",received:i,field:"scopes",next:"Send only scopes the node currently holds."});if(i.includes("crtr:act")&&!R(i,"crtr:llm"))throw new m(400,"invalid_scope","crtr:act requires crtr:llm");d=N(n,{scopes:i})}if(o.situational_context!==void 0&&Ie(n,$e(o.situational_context)),o.model!==void 0){const i=(await se(d.cwd,d.profile_id??null)).modelLadders,c=bn(o.model,i),p=vn(o.model,i);await B({kind:"concrete",spec:p},{cwd:d.cwd,profileId:d.profile_id??null},t.modelRegistry);const u=V();let f=!1;if(u.isClaimed(n)&&await Rn(n,u.get(n)?.handle.exited)&&(f=await On(n,p,c).then(()=>!0,_=>{if(_ instanceof In)return!1;throw _})),!f){if(u.isClaimed(n)){const _=`${n}'s broker did not accept the model switch; the model was not changed`;throw new m(409,"broker_not_ready",_,{error:"broker_not_ready",message:_,received:o.model,field:"model",next:"Check the node with `crtr node show`, then retry the model change."})}if(!p.includes("/"))throw h(`model must resolve to a provider/id spec: ${o.model}`);Sn(n,p,c)}d=y(n)}if(o.kind!==void 0||o.mode!==void 0||o.lifecycle!==void 0){o.kind!==void 0&&await ke(o.kind);const i=o.kind??d.kind,c=o.mode??d.mode,p=o.lifecycle??d.lifecycle;let u;c==="orchestrator"&&!yn(n)&&(u=a(()=>{const f=hn(n);wn(n,f!==null&&f.trim()!==""?{goal:f.trim()}:{})},"beforePersist")),d=await W(n,{...o.kind!==void 0?{kind:i}:{},...o.mode!==void 0?{mode:c}:{},...o.lifecycle!==void 0?{lifecycle:p}:{}},u)}o.name!==void 0&&(d=N(n,{name:o.name}),d.window&&_n(d.window,jn(d)));const l=["scopes","situational_context","model","kind","mode","lifecycle","name","listen"].filter(i=>o[i]!==void 0);return C(e,n,l,s===void 0?void 0:i=>{X(i,"UPDATE objects SET listen_json = ? WHERE id = ? AND type = 'node'").run(s,n)}),{status:200,body:g(n)}}a(Tt,"handleConfig");async function ve(){return(await se(process.cwd(),Oe()||null)).kinds}a(ve,"daemonKinds");async function ke(e){const t=Object.keys(await ve());if(!t.includes(e))throw h(`unknown kind: ${e}`,{received:e,next:`Valid kinds: ${t.sort().join(", ")}.`})}a(ke,"assertKindServer");async function Pt(){const e=await ve();return{status:200,body:{kinds:Object.keys(e).sort().map(n=>({kind:n,when_to_use:e[n]?.whenToUse??null,available_to:e[n]?.availableTo??null}))}}}a(Pt,"handleListKinds");const Uo=[{method:"POST",pattern:"/v1/nodes",scope:"crtr:act",handler:dt},{method:"GET",pattern:"/v1/nodes",handler:lt},{method:"GET",pattern:"/v1/kinds",handler:a(()=>Pt(),"handler")},{method:"POST",pattern:"/v1/nodes/revive-all",scope:"crtr:act",handler:Ct},{method:"GET",pattern:"/v1/nodes/:id",handler:ct},{method:"GET",pattern:"/v1/nodes/:id/snapshot",handler:ft},{method:"GET",pattern:"/v1/nodes/:id/subject",handler:ut},{method:"GET",pattern:"/v1/nodes/:id/messages",handler:mt},{method:"GET",pattern:"/v1/nodes/:id/session",handler:ht},{method:"GET",pattern:"/v1/nodes/:id/transcript",handler:wt},{method:"GET",pattern:"/v1/nodes/:id/context",handler:yt},{method:"POST",pattern:"/v1/nodes/:id/fork",scope:"crtr:act",handler:gt},{method:"POST",pattern:"/v1/nodes/:id/revive",scope:"crtr:act",handler:bt},{method:"POST",pattern:"/v1/nodes/:id/relaunch-root",scope:"crtr:act",handler:vt},{method:"POST",pattern:"/v1/nodes/:id/close",handler:kt},{method:"POST",pattern:"/v1/nodes/:id/recycle",scope:"crtr:act",handler:St},{method:"POST",pattern:"/v1/nodes/:id/demote",scope:"crtr:act",handler:Ot},{method:"POST",pattern:"/v1/nodes/:id/promote",scope:"crtr:act",handler:Rt},{method:"POST",pattern:"/v1/nodes/:id/yield",scope:"crtr:act",handler:It},{method:"POST",pattern:"/v1/nodes/:id/wait",handler:$t},{method:"PATCH",pattern:"/v1/nodes/:id/config",handler:Tt}];export{Tt as handleConfig,dt as handleCreate,gt as handleFork,lt as handleList,Rt as handlePromote,It as handleYield,Uo as nodeRoutes,Qo as resolveCreateKind};
|
|
1
|
+
var Se=Object.defineProperty;var a=(e,t)=>Se(e,"name",{value:t,configurable:!0});import{prepared as X}from"../../../core/sqlite-statements.js";import{envProfileId as Re}from"../../../shared/env.js";import{pausedCanvasProfiles as Oe}from"../../../core/profiles/manifest.js";import{existsSync as A,realpathSync as Z,statSync as q}from"node:fs";import{isAbsolute as F,resolve as T}from"node:path";import{appendSituationalContext as Ie,formatSituationalProse as $e}from"../../../core/runtime/situational-context.js";import{closeNode as Ce}from"../../../core/runtime/close.js";import{reviveNode as Ne}from"../../../core/runtime/revive.js";import{nowIso as G}from"../../../core/fs-utils.js";import{assertLaunchModelRegistered as B,forkNode as Ae,resolveForkSource as Te,spawnChildPrepared as Pe}from"../../../core/runtime/spawn.js";import{forkSourceNode as Ee,newNodeId as ee}from"../../../core/runtime/nodes.js";import{abandonCreateRequest as ne,childrenOf as De,completeCreateRequest as te,findCreateRequest as Le,getNode as k,recordCreateRequestNode as je,reserveCreateRequest as oe,reserveCreateRequestNode as Me,listNodes as re,recordRespawnAttempt as Fe,subscribersOf as Ge,subscriptionsOf as ie,setMessageWait as Be,updateNode as N}from"../../../core/canvas/canvas.js";import{subtreeIds as xe}from"../../../core/canvas/nav-model.js";import{withCanvasWrite as x}from"../../../core/canvas/db.js";import{nodeObject as Ke}from"../../../core/graph/objects.js";import{recordActedOn as We}from"../../../core/graph/edges.js";import{publish as Ve}from"../../../core/graph/events.js";import{readerFor as ze}from"../reader.js";import{relocateSessionCwd as Je,unbindSession as Ue}from"../../../core/canvas/sessions.js";import{ticketCountsForNodes as He}from"../../../core/canvas/attention.js";import{isSafeNodeId as Ye}from"../../../core/canvas/paths.js";import{contextDir as de,nodeAgentDir as K}from"../../../core/canvas/node-agent-paths.js";import{checkCronBirth as Qe}from"../../cron/authority.js";import{cronBirthPlacement as Xe}from"../../cron/armed-context.js";import{existsBeneath as Ze,isAbsentOrRefused as qe,isBeneathRefusal as en,readdirBeneath as nn}from"../../../core/spaces/open-beneath.js";import{readNodeMessagesPage as tn,readNodeSession as on,readNodeSnapshot as rn,transcriptMarkdown as dn}from"../../../core/runtime/node-read.js";import{assembleNodeSubject as sn}from"../../../core/substrate/subject.js";import{reviveAll as an}from"../../../core/runtime/revive-all.js";import{promoteAsync as ln,requestYieldAsync as cn,reshapeNodeAsync as W}from"../../../core/runtime/promote.js";import{recycleNode as fn}from"../../../core/runtime/recycle.js";import{relaunchRoot as un}from"../../../core/runtime/reset.js";import{assertReopenGate as pn}from"../../../core/runtime/reopen.js";import{writeYieldMessage as mn,readGoal as hn}from"../../../core/runtime/kickoff.js";import{ensureRoadmap as wn,hasRoadmap as yn}from"../../../core/runtime/roadmap.js";import{renameWindow as _n}from"../../../core/runtime/placement.js";import{buildLaunchSpecAsync as gn,explicitModelIntent as bn,normalizeModel as vn,readMergedLaunchConfigAsync as se}from"../../../core/runtime/launch.js";import{modelRequestFromConfig as kn}from"../../../core/model-routes.js";import{persistDormantModel as Sn,setModelLive as Rn}from"../../../core/runtime/model-swap.js";import{waitForBrokerViewSocket as On}from"../../../core/runtime/view-socket.js";import{BrokerUnreachableError as In}from"../../../core/runtime/broker-request.js";import{boundFleet as V}from"../../../core/runtime/fleet.js";import{admitLegacyStructuredOutput as $n}from"../../structured-requests/sidecar.js";import{OUTCOME_PAYLOAD_MAX_BYTES as ae}from"../../../core/canvas/types.js";import{HUMAN_ACTION_NAME_PATTERN as Cn}from"../../../core/human/action-binding.js";import{resolveHumanAction as Nn}from"../../../core/human-actions.js";import{parseDurationMs as An}from"../../../core/wake.js";import{claimWarmNode as Tn,refillWarmPool as le,resumeWarmClaim as Pn,warmPoolEnabled as En}from"../../../core/runtime/warm-pool.js";import{assertLaunchKind as z,isLaunchKind as Dn,resolveLaunchTarget as Ln}from"../../../core/runtime/launch-target.js";import{fullName as jn}from"../../../core/canvas/labels.js";import{reportsForNode as ce}from"./reports.js";import{requireUsableProfile as Mn}from"./profiles.js";import{assertProfileActive as Fn,loadExactProfileManifest as Gn}from"../../../core/profiles/manifest.js";import{getCron as fe}from"../../../core/canvas/crons.js";import{cronWakeOrigin as Bn}from"../../../core/runtime/bearings.js";import{boundDaemonControl as xn}from"../../control.js";import{notFound as P,usage as h}from"../../../core/errors.js";import{assertScope as Kn,validateScopes as ue}from"../../../core/scopes.js";import{scopeAllowed as O}from"../../../shared/env.js";import{toCloseResultDTO as Wn,toNodeDetailDTO as J,toNodeSummaryDTO as Vn,toReviveResultDTO as zn}from"../map.js";import{ApiError as m}from"../../../api/index.js";import{InputError as w}from"../../../core/io.js";import{brokerMayReach as U,canReadNode as pe,resolvedScopes as Jn}from"../router.js";import{fileRoot as Un}from"../../../core/spaces/file-root.js";import{activeGrant as Hn,terminalGrant as Yn}from"../../../core/grants/mirror.js";import{readConfig as Qn}from"../../../core/config.js";import{hasConfiguredProviderCredential as Xn,NO_PROVIDER_CREDENTIAL_MESSAGE as me}from"../../../core/runtime/provider-credential-gate.js";function y(e){const t=k(e);if(t===null)throw P(`unknown node: ${e}`,{received:e});return t}a(y,"requireMeta");function H(e){return{subscribesTo:ie(e),subscribers:Ge(e),children:De(e)}}a(H,"edgesFor");function g(e){const t=y(e);return J(t,H(e))}a(g,"detailOf");function Zn(e){let t=ce(e).length;try{t+=nn(K(e),de(e)).length}catch(n){if(!qe(n))throw n}return t}a(Zn,"countFiles");function qn(e){if(typeof e!="object"||e===null)throw h("a node create requires a JSON body");const t=e;if(t.kind!==void 0&&(typeof t.kind!="string"||t.kind.trim()===""))throw h("kind must be a non-empty string when provided");if(t.description!==void 0&&(typeof t.description!="string"||t.description.trim()===""))throw h("description must be a non-empty string when provided");if(t.creator!==void 0&&(typeof t.creator!="string"||t.creator.trim()===""))throw h("creator must be a non-empty node id when provided");if(t.scopes!==void 0&&ue(t.scopes),t.isolated!==void 0&&typeof t.isolated!="boolean")throw new m(400,"invalid_request","isolated must be a boolean",{param:"isolated"});const n=t.root_lifecycle;if(n!==void 0&&n!=="resident"&&n!=="terminal")throw h("root_lifecycle must be 'resident' or 'terminal'");if(t.request_key!==void 0&&(typeof t.request_key!="string"||t.request_key.trim()===""||t.request_key.length>128))throw h("request_key must be a non-empty string of at most 128 characters when provided");return t}a(qn,"parseCreateBody");function et(e){if(e===void 0)return;const t=An(e),n=t===null?null:new Date(Date.now()+t);if(t===null||!Number.isFinite(t)||t<=0||n===null||!Number.isFinite(n.getTime()))throw new m(422,"invalid_deadline","`deadline` must be a positive representable duration");return n.toISOString()}a(et,"parseDeadline");function nt(e){if(e!==void 0){if(typeof e!="object"||e===null||Array.isArray(e))throw new m(400,"invalid_request","`outcome_delivery` must be an object");if(typeof e.action!="string"||!Cn.test(e.action))throw new m(400,"invalid_request","`outcome_delivery.action` must be a declared action name");if(e.payload!==void 0){let t;try{t=JSON.stringify(e.payload)}catch{throw new m(400,"invalid_request","`outcome_delivery.payload` must be JSON-serializable")}if(t===void 0)throw new m(400,"invalid_request","`outcome_delivery.payload` must be JSON-serializable");if(Buffer.byteLength(t,"utf8")>ae)throw new m(413,"payload_too_large",`serialized payload exceeds ${ae} bytes`)}return e}}a(nt,"parseOutcomeDelivery");function tt(e,t){const n=Nn(e.action,t);if(!("kind"in n))return{actionName:e.action,argv:n.argv,cwd:n.cwd,configOrigin:n.configOrigin,...e.payload===void 0?{}:{payload:e.payload}};throw n.kind==="unknown_action"?new m(422,"unknown_action",`no action named ${e.action} is declared from ${t}`):new m(422,"invalid_action",`${e.action} is declared in ${n.configOrigin} but unusable: ${n.reason}`,{reason:n.reason,configOrigin:n.configOrigin})}a(tt,"resolveOutcomeDelivery");function ot(e,t,n){const r={kind:e.kind,cwd:n,prompt:e.prompt??""};return e.mode!==void 0&&(r.mode=e.mode),e.name!==void 0&&(r.name=e.name),e.description!==void 0&&(r.description=e.description),t!==void 0&&(r.parent=t),e.creator!==void 0&&(r.creator=e.creator),e.scopes!==void 0&&(r.scopes=e.scopes),e.isolated!==void 0&&(r.isolated=e.isolated),e.root===!0&&(r.root=!0),e.root_lifecycle!==void 0&&(r.rootLifecycle=e.root_lifecycle),(e.worktree===!0||typeof e.worktree=="string")&&(r.worktree=!0),typeof e.worktree=="string"&&(r.worktreeBase=e.worktree),e.worktree!==void 0&&(r.worktreeCwd=T(e.pin_cwd??e.cwd??n)),e.fork_from!==void 0&&(r.forkFrom=e.fork_from),e.model!==void 0&&(r.model=e.model),e.profile!==void 0&&(r.profile=e.profile),e.situational_context!==void 0&&(r.situationalContext=e.situational_context),e.node_id!==void 0&&(r.nodeId=e.node_id),r}a(ot,"buildRecipe");function rt(e,t,n){return!En()||e.prefer_warm!==!0||e.root!==!0||e.parent!=null||e.root_lifecycle==="terminal"||(e.prompt??"")!==""||e.model!==void 0||e.fork_from!==void 0||e.worktree!==void 0||e.node_id!==void 0||e.scopes!==void 0||e.isolated!==void 0?null:{kind:e.kind,mode:e.mode??"base",cwd:t,profileId:n,model:e.model??null,situationalContext:e.situational_context??null,name:e.name??null,creator:e.creator??null}}a(rt,"warmRequestFor");import{resolveCreateKind as Qo}from"../../../core/runtime/launch-target.js";function he(e){const t=e.modelIntent===void 0?void 0:`${e.modelIntent.family}/${e.modelIntent.strength}`;return kn(e.model,t,e.modelExact===!0)}a(he,"requestFromLaunch");function it(e,t){if(t===null)return e;if(e.scopes===void 0)return{...e,scopes:[...t]};const n=e.scopes.filter(r=>!O(t,r));if(n.length===0)return e;throw new m(403,"scope_denied",`scopes exceed the bearer token's ceiling: ${n.join(", ")}`,{scopes:n,ceiling:t})}a(it,"withinCeiling");async function we(e,t,n){const r=(await gn(t.kind,t.mode,{lifecycle:t.lifecycle,hasManager:e.parent!==null,model:t.model,modelOverride:!0,cwd:e.cwd,profileId:e.profile_id??null})).launch;await B(he(r),{cwd:e.cwd,profileId:e.profile_id??null},n)}a(we,"assertNodeModelChangeRegistered");function ye(e,t){const n=e.principal;if(n.class!=="broker"&&n.class!=="cron")return;const r=Un({app:n.app,resolved:Jn(n),launched:n.scopes},t,"read");if("root"in r)return;const o="missing"in r?r.missing:null;throw new m(403,"scope_missing",o?`the child's directory ${t} needs missing scope ${o}`:`no permission of this run names the child's directory ${t}`,void 0,void 0,o?{scope:o}:void 0)}a(ye,"assertCallerReaches");const Y=new Map;async function dt(e,t={},n={}){const r=it(qn(e.body),e.ceiling);if(e.principal.class==="broker"){const l=e.principal;if(r.prefer_warm===!0&&l.app!=="app:terminal"&&(r.prefer_warm=!1),r.parent!=null){const i=y(r.parent);if(i.grantee!==l.app)throw new m(403,"other_app_run","cannot create a child under another app's node");if(!U(l,r.parent,i.grantee??"app:terminal"))throw P(`unknown node: ${r.parent}`,{received:r.parent})}if(r.creator!==void 0&&r.creator!==l.node)throw new m(403,"other_app_run","cannot create for another node");if(r.fork_from!==void 0){let i=null;try{i=Ee(Te(r.fork_from))}catch{}if(i?.grantee!==l.app||!U(l,i.node_id,i.grantee))throw new m(403,"other_app_run","cannot fork another app's conversation")}r.creator=l.node}if(e.principal.class==="cron"){const l=e.principal;for(const c of["parent","creator","fork_from","node_id"])if(r[c]!=null)throw new m(403,"permission_error",`a cron cannot set ${c} on a node it starts`);r.prefer_warm=!1;const i=Qe({grantee:l.app,sub:l.sub,grant_id:l.grant,scopes:l.scopes});if(!i.allowed)throw new m(i.reason==="scope_missing"?403:401,i.reason==="scope_missing"?"scope_missing":"grant_revoked",i.message);if(r.scopes!==void 0){const c=r.scopes.find(p=>!O(i.effectiveScopes,p)||!O(l.scopes,p));if(c!==void 0)throw new m(403,"scope_missing",`scope ${c} is not held by this cron`,void 0,void 0,{scope:c})}else l.scopes!==null&&(r.scopes=[...l.scopes]);r.node_id=ee(),l.isolated&&(r.isolated=!0,r.cwd=K(r.node_id,l.app))}r.creator!==void 0&&Kn(y(r.creator),"crtr:act");const o=r.request_key?.trim();if(o===void 0||r.node_id!==void 0&&Ye(r.node_id)&&k(r.node_id)!==null&&Le(o)?.nodeId!==r.node_id)return E(e,r,t,n);const d=Y.get(o);if(d!==void 0)return d;const s=st(e,r,o,t,n);Y.set(o,s);try{return await s}finally{Y.delete(o)}}a(dt,"handleCreate");async function st(e,t,n,r,o){const d=oe(n);if(d.nodeId!==null&&k(d.nodeId)!==null){if(d.state!=="warm_claimed"){const s=k(d.nodeId);if(e.principal.class!=="person"&&!pe(e.principal,s.grantee??"app:terminal",d.nodeId))throw new m(409,"create_request_conflict","request_key belongs to another caller");return{status:201,body:g(d.nodeId)}}return E(e,t,r,o,d)}try{return await E(e,t,r,o,d)}catch(s){const l=oe(n);throw(l.nodeId===null||k(l.nodeId)===null)&&ne(n,l.nodeId),s}}a(st,"handleKeyedCreateRequest");async function E(e,t,n,r,o=void 0){const d=r.deadlineAt??et(t.deadline),s=nt(t.outcome_delivery),l=e.principal.class==="cron"?Xe({grantee:e.principal.app,sub:e.principal.sub,grant_id:e.principal.grant,scopes:e.principal.scopes,cron_id:e.principal.cron,origin_run_id:e.principal.origin_run,isolated:e.principal.isolated,creator:fe(e.principal.cron)?.created_by??null},t.node_id):null;l!==null&&(t.root=l.root===!0);const i=l?.parent??t.parent??void 0;if(i==null&&t.root!==!0)throw h("a node create requires a parent (or root: true for an independent root)");let c=null;if(t.output_schema!==void 0)try{c=JSON.parse(t.output_schema)}catch{throw h("output_schema must be a JSON schema string")}const{profileId:p,kind:u,cwd:f,launch:_}=await Ln(t,{validateKind:ke});ye(e,f),t.worktree!==void 0&&ye(e,T(t.pin_cwd??t.cwd??f));const S={...t,kind:u},I=s===void 0?void 0:tt(s,f);if(await B(he(_),{cwd:f,profileId:p},n.modelRegistry),!await Xn(p))throw new m(412,"no_provider_credential",me,{error:"no_provider_credential",message:me,next:"The person running this runtime logs a provider in with `crtr sys setup`, or stores a provider key on the profile."});let L=null;{const $=o===void 0||o.nodeId===null||o.state==="warm_claimed"?rt(S,f,p):null,j=c===null&&r.beforeBrokerLaunch===void 0?void 0:v=>{c!==null&&$n(v,"terminal",c),r.beforeBrokerLaunch?.(v)};if(o?.state==="warm_claimed"&&o.nodeId!==null){const v=$===null?null:await Pn($,o.nodeId,j);if(v===null)throw ne(o.requestKey,o.nodeId),new m(503,"create_resume_failed",`could not resume warm claim ${o.nodeId}; retry the create`);return te(o.requestKey,v),{status:201,body:g(v)}}if($!==null){const v=await Tn($,{...d===void 0?{}:{deadlineAt:d},...I===void 0?{}:{outcomeDelivery:I},...o===void 0?{}:{requestKey:o.requestKey}},j);if(v!==null)return o!==void 0&&te(o.requestKey,v),le($),{status:201,body:g(v)};L=$}const M=o?.nodeId??t.node_id??(o===void 0?void 0:ee());o!==void 0&&o.nodeId===null&&Me(o.requestKey,M);const R=ot(M===void 0?S:{...S,node_id:M},i,f);l!==null&&(l.rootLifecycle!==void 0&&(R.rootLifecycle=l.rootLifecycle),l.scopes!==void 0&&S.scopes===void 0&&(R.scopes=l.scopes),l.run!==void 0&&(R.run=l.run),l.withinCreate!==void 0&&(R.withinCreate=l.withinCreate)),d!==void 0&&(R.deadlineAt=d),I!==void 0&&(R.outcomeDelivery=I),R.readiness=t.root===!0?"await":"launch";const Q=await Pe(R,j,()=>{L!==null&&le(L)});return o!==void 0&&je(o.requestKey,Q.node.node_id),{status:201,body:g(Q.node.node_id)}}}a(E,"handleCreateRequest");function b(e,t){const n=e.query.getAll(t);if(n.length>1)throw h(`${t} must appear at most once`);return n[0]}a(b,"singleQueryValue");function _e(e,t){if(e!==void 0){if(e==="1"||e==="true")return!0;if(e==="0"||e==="false")return!1;throw h(`${t} must be true or false`)}}a(_e,"optionalBoolean");function D(e,t,n){if(e!==void 0){if(n.includes(e))return e;throw h(`${t} must be one of: ${n.join(", ")}`)}}a(D,"oneOf");function at(e){const t=new Set(["status","lifecycle","kind","mode","profile_id","profile_prefix","parent","top_level","under","hanging","include"]);for(const[_]of e.query)if(!t.has(_))throw h(`unknown nodes query parameter: ${_}`);const n=D(b(e,"status"),"status",["active","idle","done","dead","canceled"]),r=D(b(e,"lifecycle"),"lifecycle",["terminal","resident"]),o=b(e,"kind"),d=D(b(e,"mode"),"mode",["base","orchestrator"]),s=b(e,"profile_id"),l=b(e,"profile_prefix"),i=b(e,"parent"),c=_e(b(e,"top_level"),"top_level"),p=b(e,"under"),u=_e(b(e,"hanging"),"hanging"),f=D(b(e,"include"),"include",["activity"]);if(i!==void 0&&c===!0)throw h("parent and top_level=true cannot be combined");return{...n!==void 0?{status:n}:{},...r!==void 0?{lifecycle:r}:{},...o!==void 0?{kind:o}:{},...d!==void 0?{mode:d}:{},...s!==void 0?{profile_id:s}:{},...l!==void 0?{profile_prefix:l}:{},...i!==void 0?{parent:i}:{},...c!==void 0?{top_level:c}:{},...p!==void 0?{under:p}:{},...u!==void 0?{hanging:u}:{},...f!==void 0?{include:f}:{}}}a(at,"parseListQuery");function lt(e){const t=at(e);let n=t.status!==void 0?re({status:t.status}):re();if(e.principal.class!=="person"&&(n=n.filter(s=>pe(e.principal,s.grantee??"app:terminal",s.node_id))),t.lifecycle!==void 0&&(n=n.filter(s=>s.lifecycle===t.lifecycle)),t.kind!==void 0&&(n=n.filter(s=>s.kind===t.kind)),t.mode!==void 0&&(n=n.filter(s=>s.mode===t.mode)),t.profile_id!==void 0&&(n=n.filter(s=>s.profile_id===t.profile_id)),t.profile_prefix!==void 0&&(n=n.filter(s=>s.profile_id?.startsWith(t.profile_prefix))),t.parent!==void 0&&(n=n.filter(s=>s.parent===t.parent)),t.top_level===!0&&(n=n.filter(s=>s.parent===null)),t.under!==void 0){const s=new Set(xe(t.under));n=n.filter(l=>s.has(l.node_id))}t.hanging===!0&&(n=n.filter(s=>s.parent!==null&&k(s.parent)===null));const r=t.include==="activity"?He(n.map(s=>s.node_id)):null,o=n.length>0?Oe():new Set;return{status:200,body:n.map(s=>{const l=Vn(s,s.profile_id!==null&&o.has(s.profile_id));if(r!==null){const i=ce(s.node_id);l.activity={latest_report:i[0]??null,final_report:s.final_report===null?null:i.find(c=>c.name===s.final_report)??null,pending_human_count:r[s.node_id]??0}}return l})}}a(lt,"handleList");function ct(e){const t=e.params.id;return y(t),{status:200,body:g(t)}}a(ct,"handleDetail");async function ft(e){const t=e.params.id;y(t);const n=await rn(t);return{status:200,body:{node_id:t,snapshot:{messages:n.snapshot.messages,...n.snapshot.messageIds===void 0?{}:{messageIds:n.snapshot.messageIds},messageVisibility:n.snapshot.messageVisibility,turnVisibility:n.snapshot.turnVisibility,stats:n.snapshot.stats,state:n.snapshot.state,display:n.snapshot.display,...n.snapshot.queued===void 0?{}:{queued:n.snapshot.queued},...n.snapshot.toolGroupSummaries===void 0?{}:{toolGroupSummaries:n.snapshot.toolGroupSummaries},...n.snapshot.turnReplies===void 0?{}:{turnReplies:n.snapshot.turnReplies},...n.snapshot.workingActivity===void 0?{}:{workingActivity:n.snapshot.workingActivity}},commands:n.commands.map(o=>({name:o.name,description:o.description,source:o.source})),captured_at:G()}}}a(ft,"handleSnapshot");async function ut(e){const t=e.params.id,n=sn(t);if(n===null)throw P(`unknown node: ${t}`,{received:t});return{status:200,body:n}}a(ut,"handleSubject");function pt(e){for(const[d]of e.query)if(d!=="cursor"&&d!=="limit")throw h(`unknown messages query parameter: ${d}`);const t=e.query.getAll("cursor"),n=e.query.getAll("limit");if(t.length>1)throw h("cursor must appear at most once");if(n.length>1)throw h("limit must appear at most once");if(n.length===0)return t.length===0?{}:{cursor:t[0]};const r=n[0],o=Number(r);if(!Number.isSafeInteger(o)||r==="")throw h("limit must be an integer from 1 through 500");return t.length===0?{limit:o}:{cursor:t[0],limit:o}}a(pt,"parseMessagesQuery");async function mt(e){const t=e.params.id,n=await tn(t,pt(e));return{status:200,body:{node_id:t,messages:n.messages,...n.messageIds===void 0?{}:{message_ids:n.messageIds},message_visibility:n.messageVisibility,next_cursor:n.nextCursor,captured_at:G()}}}a(mt,"handleMessages");function ht(e){const t=e.params.id;y(t);const n=on(t);return{status:200,body:{node_id:t,session_file:n.sessionFile,session_jsonl:n.sessionJsonl,system_prompt:n.systemPrompt,captured_at:G()}}}a(ht,"handleSession");async function wt(e){const t=e.params.id;return y(t),{status:200,body:{node_id:t,markdown:await dn(t)}}}a(wt,"handleTranscript");function yt(e){const t=e.params.id;y(t);const n=a((o,d)=>({node_id:o,label:k(o)?.name??o,dir:de(o),self:d,files:Zn(o)}),"root"),r=[n(t,!0),...ie(t).map(o=>n(o.node_id,!1))].filter(o=>_t(o.node_id,o.dir)||o.files>0);return{status:200,body:{node_id:t,roots:r}}}a(yt,"handleContext");function _t(e,t){try{return Ze(K(e),t)}catch(n){if(en(n))return!1;throw n}}a(_t,"hasDir");async function gt(e){const t=y(e.params.id),n=e.principal.class==="broker"?e.principal.scopes:e.ceiling,r=n===null?void 0:t.scopes==null?[...n]:[...new Set([...t.scopes.filter(d=>O(n,d)),...n.filter(d=>O(t.scopes,d))])],o=await Ae(t.node_id,r,e.principal.class==="broker"?e.principal.node:void 0);return{status:201,body:g(o.node.node_id)}}a(gt,"handleFork");async function bt(e){const t=e.params.id;y(t);const n=e.body,r=n?.resume??!0,o=n?.reopen??!1;pn(t,o);const d=!r&&n?.cron_id!==void 0?fe(n.cron_id):null,s=await Ne(t,{resume:r,capacity:"refuse",...d!==null?{wakeReason:Bn(d,"schedule_revive")}:{}});s.outcome!=="already-live"&&Fe(t,{failures:0,notBefore:null});const l=k(t)?.status??"active";return{status:200,body:zn(t,s,l)}}a(bt,"handleRevive");async function vt(e){const t=e.params.id;return{status:200,body:{newNodeId:(await un(t))?.newNodeId??null}}}a(vt,"handleRelaunchRoot");function kt(e){const t=e.params.id;y(t);const n=e.body;if(n?.caller_pid!==void 0&&(!Number.isInteger(n.caller_pid)||n.caller_pid<=0))throw h("caller_pid must be a positive integer when provided");const r=xn(),o=Ce(t,{rootEvent:n?.finish===!0?"finish":"cancel",callerPid:n?.caller_pid,...e.principal.class==="broker"?{closer:e.principal.node}:{},...r===null?{}:{registerDetached:r.registerDetached}});return{status:200,body:Wn(o)}}a(kt,"handleClose");async function St(e){const t=e.params.id;y(t);const n=await fn(t);return{status:200,body:{recycled:n.recycled,finalized:n.finalized,new_root:n.newRoot,delivered:n.delivered}}}a(St,"handleRecycle");async function Rt(e){const t=e.params.id;return y(t),await W(t,{lifecycle:"terminal"}),C(e,t,["lifecycle"]),{status:200,body:g(t)}}a(Rt,"handleDemote");async function Ot(e,t={}){const n=e.params.id,r=y(n),o=e.body??{};o.kind!==void 0&&await z(o.kind,r.cwd,r.profile_id??null),o.model!==void 0&&await we(r,{kind:o.kind??r.kind,mode:"orchestrator",lifecycle:o.resident===!0?"resident":r.lifecycle,model:o.model},t.modelRegistry);const d=await ln(n,{...o.kind!==void 0?{kind:o.kind}:{},...o.resident===!0?{resident:!0}:{},...o.model!==void 0?{model:o.model}:{}});C(e,n,["mode",...o.kind!==void 0?["kind"]:[],...o.resident===!0?["lifecycle"]:[],...o.model!==void 0?["model"]:[]]);const s=J(d.meta,H(n));return s.roadmap_written=d.roadmapWritten,s.roadmap_ref=d.roadmapRef,s.goal_path=d.goalPath,{status:200,body:s}}a(Ot,"handlePromote");async function It(e,t={}){const n=e.params.id,r=y(n),o=e.body??{};o.kind!==void 0&&await z(o.kind,r.cwd,r.profile_id??null),o.model!==void 0&&await we(r,{kind:o.kind??r.kind,mode:o.promote===!0?"orchestrator":r.mode,lifecycle:r.lifecycle,model:o.model},t.modelRegistry);const d=o.note??"Refresh: continue against your roadmap or goal.";mn(n,d);const s=await cn(n,{...o.promote===!0?{promote:!0}:{},...o.kind!==void 0?{kind:o.kind}:{},...o.model!==void 0?{model:o.model}:{}});return{status:200,body:J(s.meta,H(n))}}a(It,"handleYield");function $t(e){const t=e.params.id;y(t);const n=e.body;if(n===void 0||typeof n.controller!="string"||n.controller.trim()==="")throw h("a wait requires a controller node id");const r=k(n.controller);if(r===null)throw P(`unknown controller: ${n.controller}`,{received:n.controller});if(r.status!=="active"&&r.status!=="idle")throw new m(422,"unprocessable",`controller ${n.controller} is ${r.status}`);return Be(t,n.controller),{status:200,body:g(t)}}a($t,"handleWait");async function Ct(e){const t=e.principal,n=await an(t.class==="broker"?o=>o.grantee===t.app&&U(t,o.node_id,o.grantee):void 0);return{status:200,body:{revived:[...n.revived],failed:[...n.failed],frozen:[...n.frozen]}}}a(Ct,"handleReviveAll");const Nt=new Set(["interrupt","steer","after-turn","next-turn"]);function At(e){if(e===null)return null;const t=a(r=>{throw new w({error:"invalid_listen",message:r,received:e,field:"listen",next:'Send an object mapping levels "1".."10" to interrupt|steer|after-turn|next-turn (e.g. {"3":"next-turn"}), or null to reset to the global default.'})},"bad");if(typeof e!="object"||Array.isArray(e))return t("listen must be an object or null");const n=Object.entries(e);if(n.length===0)return t("listen must name at least one level");for(const[r,o]of n)/^(?:[1-9]|10)$/.test(r)||t(`listen level must be 1..10: ${r}`),(typeof o!="string"||!Nt.has(o))&&t(`listen delivery must be interrupt|steer|after-turn|next-turn: ${String(o)}`);return JSON.stringify(Object.fromEntries(n))}a(At,"listenJson");function C(e,t,n,r){n.length===0&&r===void 0||x(o=>{if(r?.(o),n.length===0)return;const d=Ke(o,t),s=ze(o,e),l=s.class==="person"?s.person:s.class==="app"?s.app:s.node;We(o,l,d),Ve(o,{event:"configured",object:d,actor:l,attrs:{changed:n.join(",")}})})}a(C,"recordConfigured");function ge(e){const t=V().get(e);if(t===void 0)return!1;try{return process.kill(t.pid,"SIGTERM"),!0}catch{return!1}}a(ge,"restartLiveBroker");function be(e,t){try{return Z(e)===Z(t)}catch{return!1}}a(be,"sameDirectory");async function Tt(e,t={}){const n=e.params.id,r=e.body;let o;if(r===void 0)o={};else if(typeof r=="object"&&r!==null&&!Array.isArray(r))o=r;else throw new w({error:"cwd_invalid",message:"cwd repair requires a non-null object with a non-empty cwd string",received:r,field:"cwd",next:"Send a config patch with a non-empty absolute cwd string."});if(o.profile!==void 0){if(typeof o.profile!="string"||o.profile==="")throw new w({error:"profile_invalid",message:"profile must be a non-empty profile id or name",received:o.profile,field:"profile",next:"Send a profile id or exact name."});if(Object.keys(o).some(u=>u!=="profile"&&u!=="restart"&&o[u]!==void 0))throw new w({error:"profile_not_exclusive",message:"profile can only be combined with restart",field:"profile",next:"Send profile (and optionally restart) alone."});const i=y(n),c=Mn(e,o.profile);Fn(c.profileId);const p=Gn(c.profileId).manifest.grantee??"app:terminal";if(p!==(i.grantee??"app:terminal"))throw new w({error:"profile_grantee_mismatch",message:`profile ${c.profileId} belongs to ${p}, node ${n} to ${i.grantee}`,received:o.profile,field:"profile",next:"Pick a profile owned by the same app as the node."});return(i.profile_id??null)!==c.profileId&&(Dn(i.kind)?(await z(i.kind,i.cwd,c.profileId),await W(n,{profileId:c.profileId})):N(n,{profile_id:c.profileId}),C(e,n,["profile"])),o.restart===!0&&ge(n),{status:200,body:g(n)}}if(Object.prototype.hasOwnProperty.call(o,"cwd")){const i=o.cwd;if(typeof i!="string"||i==="")throw new w({error:"cwd_invalid",message:"cwd must be a non-empty string",received:i,field:"cwd",next:"Send a non-empty absolute cwd string."});if(Object.keys(o).some(f=>f!=="cwd"&&f!=="keep_session"&&f!=="restart"&&o[f]!==void 0))throw new w({error:"cwd_not_exclusive",message:"cwd repair cannot be combined with another config field",received:o,field:"cwd",next:"Send cwd as the only config field."});const c=y(n),p=A(c.cwd);if(o.keep_session===!0||p&&F(i)&&A(i)&&be(c.cwd,i)){if(!F(i))throw new w({error:"cwd_not_absolute",message:`cwd must be an absolute path: ${i}`,received:i,field:"cwd",next:"Send an absolute path."});const f=T(i);let _=!1;try{_=q(f).isDirectory()}catch{}if(!_)throw new w({error:"cwd_missing",message:`cwd is not an existing directory: ${f}`,received:i,field:"cwd",next:"Send an existing absolute directory."});if(p&&!be(c.cwd,f))throw new w({error:"cwd_not_same_directory",message:`${f} is not the same directory as the recorded ${c.cwd}; --keep-session only relocates an alias`,received:i,field:"cwd",next:"Drop --keep-session to repair a gone cwd, or pass the real path of the recorded directory."});if(c.managed_worktree?.state==="open")throw new w({error:"cwd_node_managed_worktree_open",message:`${n} owns an open managed worktree and cannot relocate its cwd`,received:i,field:"cwd",next:"Reconcile the managed worktree first."});const S=c.cwd;return S!==f&&(x(I=>{N(n,{cwd:f}),X(I,"UPDATE crons SET cwd = ? WHERE anchor_node = ? AND cwd = ?").run(f,n,S),Je(n,S,f)}),C(e,n,["cwd"])),o.restart===!0&&ge(n),{status:200,body:g(n)}}if(V().has(n)){const f=`${n} cannot repair its cwd while its broker is live`;throw new m(409,"cwd_node_live",f,{error:"cwd_node_live",message:f,received:i,field:"cwd",next:"Let the node become dormant, then retry the cwd repair."})}if(c.fork_from!=null)throw new w({error:"cwd_node_forked",message:`${n} is a fork and cannot repair its cwd; recorded cwd: ${c.cwd}`,received:i,field:"cwd",next:`Restore ${c.cwd}, then revive the fork normally.`});if(c.managed_worktree?.state==="open")throw new w({error:"cwd_node_managed_worktree_open",message:`${n} owns an open managed worktree and cannot repair its cwd`,received:i,field:"cwd",next:"Restore or reconcile the recorded checkout, then use the managed-worktree close or abandon flow."});if(A(c.cwd))throw new w({error:"cwd_still_present",message:`${n} recorded cwd still exists: ${c.cwd}; cwd repair is not a relocation command`,received:i,field:"cwd",next:"This command only repairs a node whose recorded cwd is gone."});if(!F(i))throw new w({error:"cwd_not_absolute",message:`cwd must be an absolute path: ${i}`,received:i,field:"cwd",next:"Send an absolute path."});const u=T(i);if(!A(u))throw new w({error:"cwd_missing",message:`cwd does not exist: ${u}`,received:i,field:"cwd",next:"Create the replacement directory or send an existing absolute directory."});try{if(!q(u).isDirectory())throw new w({error:"cwd_not_a_directory",message:`cwd is not a directory: ${u}`,received:i,field:"cwd",next:"Send an existing absolute directory."})}catch(f){throw f instanceof w?f:new w({error:"cwd_missing",message:`cwd does not exist: ${u}`,received:i,field:"cwd",next:"Create the replacement directory or send an existing absolute directory."})}return x(()=>{N(n,{cwd:u}),Ue(n)}),C(e,n,["cwd"]),{status:200,body:g(n)}}let d=y(n);const s=o.listen===void 0?void 0:At(o.listen);if(o.scopes!==void 0){const i=ue(o.scopes),c=d.grantee==="app:terminal"?Yn():d.grant_id?Hn(d.grant_id):null,p=d.grantee==="app:terminal"&&!Qn("user").runtime?.issuer;if(!i.every(u=>O(d.scopes,u)&&(c?O(c.resolved,u):p)))throw new w({error:"unknown_scope",message:"scopes may only narrow the node's current effective set",received:i,field:"scopes",next:"Send only scopes the node currently holds."});if(i.includes("crtr:act")&&!O(i,"crtr:llm"))throw new m(400,"invalid_scope","crtr:act requires crtr:llm");d=N(n,{scopes:i})}if(o.situational_context!==void 0&&Ie(n,$e(o.situational_context)),o.model!==void 0){const i=(await se(d.cwd,d.profile_id??null)).modelLadders,c=bn(o.model,i),p=vn(o.model,i);await B({kind:"concrete",spec:p},{cwd:d.cwd,profileId:d.profile_id??null},t.modelRegistry);const u=V();let f=!1;if(u.isClaimed(n)&&await On(n,u.get(n)?.handle.exited)&&(f=await Rn(n,p,c).then(()=>!0,_=>{if(_ instanceof In)return!1;throw _})),!f){if(u.isClaimed(n)){const _=`${n}'s broker did not accept the model switch; the model was not changed`;throw new m(409,"broker_not_ready",_,{error:"broker_not_ready",message:_,received:o.model,field:"model",next:"Check the node with `crtr node show`, then retry the model change."})}if(!p.includes("/"))throw h(`model must resolve to a provider/id spec: ${o.model}`);Sn(n,p,c)}d=y(n)}if(o.kind!==void 0||o.mode!==void 0||o.lifecycle!==void 0){o.kind!==void 0&&await ke(o.kind);const i=o.kind??d.kind,c=o.mode??d.mode,p=o.lifecycle??d.lifecycle;let u;c==="orchestrator"&&!yn(n)&&(u=a(()=>{const f=hn(n);wn(n,f!==null&&f.trim()!==""?{goal:f.trim()}:{})},"beforePersist")),d=await W(n,{...o.kind!==void 0?{kind:i}:{},...o.mode!==void 0?{mode:c}:{},...o.lifecycle!==void 0?{lifecycle:p}:{}},u)}o.name!==void 0&&(d=N(n,{name:o.name}),d.window&&_n(d.window,jn(d)));const l=["scopes","situational_context","model","kind","mode","lifecycle","name","listen"].filter(i=>o[i]!==void 0);return C(e,n,l,s===void 0?void 0:i=>{X(i,"UPDATE objects SET listen_json = ? WHERE id = ? AND type = 'node'").run(s,n)}),{status:200,body:g(n)}}a(Tt,"handleConfig");async function ve(){return(await se(process.cwd(),Re()||null)).kinds}a(ve,"daemonKinds");async function ke(e){const t=Object.keys(await ve());if(!t.includes(e))throw h(`unknown kind: ${e}`,{received:e,next:`Valid kinds: ${t.sort().join(", ")}.`})}a(ke,"assertKindServer");async function Pt(){const e=await ve();return{status:200,body:{kinds:Object.keys(e).sort().map(n=>({kind:n,when_to_use:e[n]?.whenToUse??null,available_to:e[n]?.availableTo??null}))}}}a(Pt,"handleListKinds");const Uo=[{method:"POST",pattern:"/v1/nodes",scope:"crtr:act",handler:dt},{method:"GET",pattern:"/v1/nodes",handler:lt},{method:"GET",pattern:"/v1/kinds",handler:a(()=>Pt(),"handler")},{method:"POST",pattern:"/v1/nodes/revive-all",scope:"crtr:act",handler:Ct},{method:"GET",pattern:"/v1/nodes/:id",handler:ct},{method:"GET",pattern:"/v1/nodes/:id/snapshot",handler:ft},{method:"GET",pattern:"/v1/nodes/:id/subject",handler:ut},{method:"GET",pattern:"/v1/nodes/:id/messages",handler:mt},{method:"GET",pattern:"/v1/nodes/:id/session",handler:ht},{method:"GET",pattern:"/v1/nodes/:id/transcript",handler:wt},{method:"GET",pattern:"/v1/nodes/:id/context",handler:yt},{method:"POST",pattern:"/v1/nodes/:id/fork",scope:"crtr:act",handler:gt},{method:"POST",pattern:"/v1/nodes/:id/revive",scope:"crtr:act",handler:bt},{method:"POST",pattern:"/v1/nodes/:id/relaunch-root",scope:"crtr:act",handler:vt},{method:"POST",pattern:"/v1/nodes/:id/close",handler:kt},{method:"POST",pattern:"/v1/nodes/:id/recycle",scope:"crtr:act",handler:St},{method:"POST",pattern:"/v1/nodes/:id/demote",scope:"crtr:act",handler:Rt},{method:"POST",pattern:"/v1/nodes/:id/promote",scope:"crtr:act",handler:Ot},{method:"POST",pattern:"/v1/nodes/:id/yield",scope:"crtr:act",handler:It},{method:"POST",pattern:"/v1/nodes/:id/wait",handler:$t},{method:"PATCH",pattern:"/v1/nodes/:id/config",handler:Tt}];export{Tt as handleConfig,dt as handleCreate,gt as handleFork,lt as handleList,Ot as handlePromote,It as handleYield,Uo as nodeRoutes,Qo as resolveCreateKind};
|
package/dist/daemon/crtrd.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
var Se=Object.defineProperty;var s=(t,o)=>Se(t,"name",{value:o,configurable:!0});import{writeFileSync as _e,readFileSync as ke,rmSync as be,mkdirSync as Y,existsSync as b,chmodSync as Ee}from"node:fs";import{createServer as X}from"node:http";import{dirname as Re}from"node:path";import{envCrtrdTcp as Ie}from"../shared/env.js";import{serveCanvasConversationStore as Pe}from"../core/canvas/sessions.js";import{pollRunObservers as xe}from"./runs/observer.js";import{randomBytes as De}from"node:crypto";import{apiSocketPath as Te,crtrHome as Ce}from"../core/canvas/paths.js";import{activeAppUsers as Le}from"../core/spaces/users.js";import{ensureAppSpace as Ae,ensureUserFiles as Ne}from"../core/spaces/space.js";import{LAUNCHER_SOCKET as R}from"../launcher/spec.js";import{launcherRequest as x}from"../launcher/client.js";import{installLauncherEventSink as $e}from"./launcher-events.js";import{initializeEmptyLayout as Me,layoutNeedsMigration as Ge,socketDir as Z}from"../core/layout.js";import{migrateLooseKeys as Oe}from"../core/layout-migrate/loose-keys.js";import{getNode as Fe,listNodesForSupervision as He,setFrozen as Be}from"../core/canvas/index.js";import{createApiServer as je}from"./api/server.js";import{createAppListener as qe}from"./api/app-listener.js";import{installLocalTerminalGrant as ze}from"../core/grants/local.js";import{onRunEventAppended as We}from"../core/runs/events.js";import{runNotices as Ue}from"./run-notices.js";import{createGrantSync as Ke,GRANT_SYNC_INTERVAL_S as Je}from"../core/grants/sync.js";import{runRemovalCascades as Ve}from"../core/grants/remove.js";import{sweepSandboxes as Qe}from"../core/grants/on-sync.js";import{rebuildCronExecutions as Ye}from"./cron/run-socket.js";import{hasSyncedGrants as Xe}from"../core/grants/mirror.js";import{readConfig as Ze}from"../core/config.js";import{reconcileUndeliveredTickets as er}from"./human/sweep.js";import{resetHandedOffDeliveries as rr}from"../core/canvas/deliveries.js";import{resetHandedOffDaemonActions as tr}from"../core/canvas/daemon-actions.js";import{sweepReviews as or}from"./review/sweep.js";import{pidfilePath as G}from"./pidfile.js";import{rendererWarning as nr}from"../core/runtime/package-health.js";import{DEFAULT_INTERVAL_MS as ir}from"./supervise-cadence.js";import{createResumeDetector as sr}from"./resume-detector.js";import{BrokerSupervisionReconciler as ar}from"./reconcilers/broker-supervision.js";import{ControllerDeathReconciler as cr}from"./reconcilers/controller-death.js";import{NodeLifecycleReconciler as lr}from"./reconcilers/node-lifecycle/tick.js";import{NodeDeadlineReconciler as dr}from"./reconcilers/node-deadline.js";import{CronLaneReconciler as pr}from"./reconcilers/cron-lane.js";import{DeliveryLaneReconciler as mr}from"./reconcilers/delivery-lane.js";import{ActionLaneReconciler as fr}from"./reconcilers/action-lane.js";import{endLiveExecution as ur,migrateLegacyExecutionIdentities as hr}from"../core/canvas/executions.js";import{revokePriorEpochNodeTokens as vr}from"../core/canvas/node-tokens.js";import{PendingReviewSubmitReconciler as wr}from"./reconcilers/pending-review-submit.js";import{StorageMaintenanceReconciler as gr}from"./reconcilers/storage-maintenance.js";import{StorageDueWorkReconciler as yr}from"./reconcilers/storage-due-work.js";import{BashDeadlineReconciler as Sr}from"./reconcilers/bash-deadline.js";import{FaultRetryBackstopReconciler as _r}from"./reconcilers/fault-retry-backstop.js";import{StructuredRequestReconciler as kr}from"./reconcilers/structured-request-lane.js";import{reconcileStructuredRequestSidecars as br}from"./structured-requests/boot.js";import{captureLivenessSnapshot as ee,capturePidCommand as Er,captureTeardownSnapshot as re,isPidAlive as Rr,isAnyPidAlive as te,killProcessTreePids as oe}from"../core/canvas/pid.js";import{bindFleet as Ir,boundFleet as Pr}from"../core/runtime/fleet.js";import{bindDaemonControl as xr,clearDaemonControl as Dr}from"./control.js";import{execDaemon as Tr,spawnDaemon as Cr}from"./manage.js";import{reconcileAndMigrateBoot as Lr}from"../core/canvas/boot.js";import{resolveInstallId as Ar}from"../core/canvas/install-id.js";import{setInstallId as Nr}from"../core/runtime/nodes.js";import{claimDaemonOwnership as $r,releaseDaemonOwnership as ne}from"../core/canvas/daemon-owner.js";import{migrate as Mr,openDb as ie,withCanvasWrite as Gr,bindDaemonCanvasOpenHook as Or,clearDaemonCanvasOpenHook as se}from"../core/canvas/db.js";import{brokerSignalBus as O}from"./broker-signals/bus.js";import{brokerSignalStreams as Fr}from"./api/handlers/broker-signals.js";import{reconcilePackageDocuments as Hr}from"../core/graph/package-docs.js";import{startRepoSync as Br}from"../core/graph/repo-sync/index.js";import{prewarmRecentRecipes as jr,reapStaleSpares as qr}from"../core/runtime/warm-pool.js";import{DaemonFleet as zr}from"./fleet.js";import{BOOT_MIGRATIONS as Wr}from"./boot-migrations.js";import{applyJoinedOwner as Ur}from"./joined-owner.js";import{emitEvent as u}from"../core/events/emit.js";import{operationIdContext as _}from"../core/events/operation-id.js";import{bindDaemonEventSource as Kr}from"../core/events/source.js";import{activateCanvasImport as Jr,MigrationBlockedError as Vr,runForcedCanvasImport as Qr}from"../migrations/activation.js";import{convertLegacyProviderStore as Yr,convertLegacyScopedStore as Xr}from"../core/scoped-state/migrate.js";import{userScopeRoot as Zr}from"../core/scope.js";import{secretsDir as et}from"../core/layout.js";function F(t,o){return t.includes(` --canvas-home ${o} `)}s(F,"brokerCommandDeclaresCanvasHome");function H(t){return/ --epoch (\S+) /.exec(t)?.[1]??null}s(H,"brokerCommandEpoch");const rt=5e3,ae=150;async function tt(t){const o=ee();if(o===null)throw u({level:"error",event:"daemon.census.snapshot_failed",fields:{invariant:"prior-epoch brokers are reaped before readiness"}}),new Error("prior-epoch broker census could not capture the process table");const a=Ce(),m=[];for(const[i,p]of o.processes){const g=p.command;if(p.zombie||!g.includes("broker-cli.js")||!F(g,a)||H(g)===t)continue;const S=g.trim().split(/\s+/).at(-1)??"";S===""||S.endsWith("broker-cli.js")||m.push({pid:i,nodeId:S})}let f=0;const n=[];for(const i of m){const p=Er(i.pid);if(p===null||!p.includes("broker-cli.js")||!F(p,a)||H(p)===t||p.trim().split(/\s+/).at(-1)!==i.nodeId)continue;const g=re(i.pid,null);if(!g.reused){try{process.platform==="linux"&&b(R)?await x({operation:"stop",pid:i.pid}):process.kill(i.pid,"SIGTERM")}catch{continue}f++,n.push({pid:i.pid,snap:g})}}const w=Date.now()+rt;for(;Date.now()<w&&n.some(i=>te(i.snap.tree.length>0?i.snap.tree:[i.pid]));)await new Promise(i=>setTimeout(i,ae));let v=0;for(const i of n){const p=i.snap.tree.length>0?i.snap.tree:[i.pid];te(p)&&(process.platform==="linux"&&b(R)?await x({operation:"stop",pid:i.pid}):oe(p,"SIGKILL",i.snap.identities),v++)}v>0&&await new Promise(i=>setTimeout(i,ae));const l=ee();if(l===null)throw u({level:"error",event:"daemon.census.snapshot_failed",fields:{invariant:"prior-epoch brokers are reaped before readiness",stage:"final"}}),new Error("prior-epoch broker census could not capture the final process table");const d=[];for(const[i,p]of l.processes){const g=p.command;p.zombie||!g.includes("broker-cli.js")||F(g,a)&&H(g)!==t&&d.push(i)}if(d.length>0)throw new Error(`prior-epoch broker census could not prove termination of pid(s): ${d.join(", ")}`);u({level:"info",event:"daemon.census.completed",fields:{prior_epoch_brokers:m.length,sigtermed:f,sigkilled:v}})}s(tt,"reapPriorEpochBrokers");const ot=5e3;async function nt(t){const o=Array.from(t.entries());if(o.length===0)return;for(const[n]of o){const w=Fe(n)?.status;(w==="active"||w==="idle")&&Be(n,new Date().toISOString())}const a=o.map(([,n])=>({pid:n.pid,snap:re(n.pid,null),exited:n.handle.exited}));for(const n of a)if(!n.snap.reused)try{process.platform==="linux"&&b(R)?await x({operation:"stop",pid:n.pid}):process.kill(n.pid,"SIGTERM")}catch{}const m=new Promise(n=>{setTimeout(n,ot).unref()});await Promise.allSettled(a.map(n=>Promise.race([n.exited.then(()=>{}),m])));const f=[];for(const n of a)Rr(n.pid)&&(process.platform==="linux"&&b(R)?await x({operation:"stop",pid:n.pid}):oe(n.snap.tree.length>0?n.snap.tree:[n.pid],"SIGKILL",n.snap.identities),f.push(n));await Promise.all(f.map(n=>n.exited));for(const[n]of o)ur(n,{reason:"daemon-teardown"});u({level:"info",event:"daemon.fleet_teardown.completed",fields:{brokers:a.length,sigkilled:f.length}})}s(nt,"teardownFleet");function ce(){Y(Re(G()),{recursive:!0}),_e(G(),String(process.pid),"utf8")}s(ce,"writePidfile");function le(t){return typeof t=="object"&&t!==null&&t.code==="ENOENT"}s(le,"isMissingPathError");function de(t){const o=G();let a;try{a=ke(o,"utf8")}catch(m){if(le(m))return;throw m}if(a.trim()!==String(t))throw new Error(`daemon readiness mirror does not name this invocation (expected pid ${t})`);try{be(o)}catch(m){if(le(m))return;throw m}}s(de,"removeOwnedPidfileMirror");const it={acceptsDetachedWork:s(()=>!0,"acceptsDetachedWork"),registerDetached:s(t=>{t.catch(()=>{})},"registerDetached")},B=new ar,st=new cr,at=new lr,ct=new dr,lt=new pr,dt=new mr,pt=new fr,mt=new wr,ft=new gr,ut=new yr,ht=new Sr,vt=new _r,wt=new kr;async function gt(t=Date.now(),o=it){const a=Pr(),m=await B.runFleetPreamble(a);let f,n;try{const v=He();f=v.filter(l=>l.status==="active"||l.status==="idle"),n=v.filter(l=>l.status==="done"||l.status==="canceled"||l.status==="dead")}catch(v){u({level:"error",event:"canvas.node_list.failed",error:v});return}await B.run(t,{rows:f,fleet:a,lifecycle:o}),st.run(t,{rows:f});const w=await at.run(t,{rows:[...f,...n],fleet:a});ct.run(t),wt.run(t),lt.run(t,{lifecycle:o}),dt.run(t,{lifecycle:o}),pt.run(t,{lifecycle:o}),mt.run(t,{lifecycle:o}),ft.run(t,{lifecycle:o,fleet:a}),ut.run(t,{lifecycle:o}),xe(),vt.run(t,{fleet:a,lifecycle:o}),await ht.run(t,{rows:f}),B.warnIfSaturated({fleet:a,lifecycle:o,thresholds:m,frozen:w})}s(gt,"superviseTick");async function yt(t){Y(Z(),{recursive:!0,mode:493}),process.platform==="linux"&&Ee(Z(),493);const o=Te();if(b(o))throw new Error(`layout refusal cannot bind occupied socket: ${o}`);const a=X((l,d)=>{if(l.method==="GET"&&l.url==="/healthz"){d.writeHead(200,{"content-type":"application/json; charset=utf-8"}),d.end(JSON.stringify({ok:!1,daemon_up:!0,store_accessible:!1,brokers_reconciled:!1,startup_phase:"blocked",startup_blocked:"layout_migration_required"}));return}d.writeHead(503,{"content-type":"application/json; charset=utf-8"}),d.end(JSON.stringify({error:{code:"layout_migration_required",message:"run crtr sys migrate while the runtime is offline",type:"server_error",origin:"runtime",retryable:!1}}))});await new Promise((l,d)=>{a.once("error",d),a.listen(o,()=>{a.off("error",d),l()})});let m;const f=t.tcp??Ie();if(f){const l=/^(.*):(\d+)$/.exec(f);if(!l||Number(l[2])>65535)throw new Error(`invalid refusal TCP address: ${f}`);const d=X(a.listeners("request")[0]);await new Promise((i,p)=>{d.once("error",p),d.listen(Number(l[2]),l[1],()=>{d.off("error",p),i()})}),m=d}ce();let n;const w=s(()=>(n??=(async()=>{a.closeAllConnections(),m?.closeAllConnections(),await Promise.all([a,m].filter(l=>l!==void 0).map(l=>new Promise((d,i)=>l.close(p=>p?i(p):d())))),de(process.pid),process.off("SIGINT",v),process.off("SIGTERM",v)})(),n),"cleanup"),v=s(()=>{w().then(()=>process.exit(0),l=>{process.stderr.write(`[crtrd] layout refusal cleanup failed: ${String(l)}
|
|
2
|
-
`),process.exit(1)})},"onSignal");return process.on("SIGINT",v),process.on("SIGTERM",v),w}s(yt,"serveLayoutRefusal");async function St(t={}){if(Ge())return yt(t);Me();const o
|
|
1
|
+
var Se=Object.defineProperty;var s=(t,o)=>Se(t,"name",{value:o,configurable:!0});import{writeFileSync as _e,readFileSync as ke,rmSync as be,mkdirSync as Y,existsSync as b,chmodSync as Ee}from"node:fs";import{createServer as X}from"node:http";import{dirname as Re}from"node:path";import{envCrtrdTcp as Ie}from"../shared/env.js";import{serveCanvasConversationStore as Pe}from"../core/canvas/sessions.js";import{pollRunObservers as xe}from"./runs/observer.js";import{randomBytes as Te}from"node:crypto";import{apiSocketPath as De,crtrHome as Ce}from"../core/canvas/paths.js";import{activeAppUsers as Le}from"../core/spaces/users.js";import{ensureAppSpace as $e,ensureUserFiles as Ae}from"../core/spaces/space.js";import{LAUNCHER_SOCKET as R}from"../launcher/spec.js";import{launcherRequest as x}from"../launcher/client.js";import{installLauncherEventSink as Ne}from"./launcher-events.js";import{initializeEmptyLayout as Me,layoutNeedsMigration as Ge,socketDir as Z}from"../core/layout.js";import{migrateLooseKeys as Oe}from"../core/layout-migrate/loose-keys.js";import{getNode as Fe,listNodesForSupervision as He,setFrozen as Be}from"../core/canvas/index.js";import{createApiServer as qe}from"./api/server.js";import{createAppListener as je}from"./api/app-listener.js";import{installLocalTerminalGrant as ze}from"../core/grants/local.js";import{onRunEventAppended as We}from"../core/runs/events.js";import{runNotices as Ue}from"./run-notices.js";import{createGrantSync as Ke,GRANT_SYNC_INTERVAL_S as Je}from"../core/grants/sync.js";import{runRemovalCascades as Ve}from"../core/grants/remove.js";import{sweepSandboxes as Qe}from"../core/grants/on-sync.js";import{rebuildCronExecutions as Ye}from"./cron/run-socket.js";import{hasSyncedGrants as Xe}from"../core/grants/mirror.js";import{readConfig as Ze}from"../core/config.js";import{reconcileUndeliveredTickets as er}from"./human/sweep.js";import{resetHandedOffDeliveries as rr}from"../core/canvas/deliveries.js";import{resetHandedOffDaemonActions as tr}from"../core/canvas/daemon-actions.js";import{sweepReviews as or}from"./review/sweep.js";import{pidfilePath as G}from"./pidfile.js";import{rendererWarning as nr}from"../core/runtime/package-health.js";import{DEFAULT_INTERVAL_MS as ir}from"./supervise-cadence.js";import{createResumeDetector as sr}from"./resume-detector.js";import{BrokerSupervisionReconciler as ar}from"./reconcilers/broker-supervision.js";import{ControllerDeathReconciler as cr}from"./reconcilers/controller-death.js";import{NodeLifecycleReconciler as lr}from"./reconcilers/node-lifecycle/tick.js";import{NodeDeadlineReconciler as dr}from"./reconcilers/node-deadline.js";import{CronLaneReconciler as pr}from"./reconcilers/cron-lane.js";import{DeliveryLaneReconciler as mr}from"./reconcilers/delivery-lane.js";import{ActionLaneReconciler as fr}from"./reconcilers/action-lane.js";import{endLiveExecution as ur,migrateLegacyExecutionIdentities as hr}from"../core/canvas/executions.js";import{revokePriorEpochNodeTokens as vr}from"../core/canvas/node-tokens.js";import{PendingReviewSubmitReconciler as wr}from"./reconcilers/pending-review-submit.js";import{StorageMaintenanceReconciler as gr}from"./reconcilers/storage-maintenance.js";import{StorageDueWorkReconciler as yr}from"./reconcilers/storage-due-work.js";import{BashDeadlineReconciler as Sr}from"./reconcilers/bash-deadline.js";import{FaultRetryBackstopReconciler as _r}from"./reconcilers/fault-retry-backstop.js";import{StructuredRequestReconciler as kr}from"./reconcilers/structured-request-lane.js";import{reconcileStructuredRequestSidecars as br}from"./structured-requests/boot.js";import{captureLivenessSnapshot as ee,capturePidCommand as Er,captureTeardownSnapshot as re,isPidAlive as Rr,isAnyPidAlive as te,killProcessTreePids as oe}from"../core/canvas/pid.js";import{bindFleet as Ir,boundFleet as Pr}from"../core/runtime/fleet.js";import{bindDaemonControl as xr,clearDaemonControl as Tr}from"./control.js";import{execDaemon as Dr,spawnDaemon as Cr}from"./manage.js";import{reconcileAndMigrateBoot as Lr}from"../core/canvas/boot.js";import{resolveInstallId as $r}from"../core/canvas/install-id.js";import{setInstallId as Ar}from"../core/runtime/nodes.js";import{claimDaemonOwnership as Nr,releaseDaemonOwnership as ne}from"../core/canvas/daemon-owner.js";import{migrate as Mr,openDb as ie,withCanvasWrite as Gr,bindDaemonCanvasOpenHook as Or,clearDaemonCanvasOpenHook as se}from"../core/canvas/db.js";import{brokerSignalBus as O}from"./broker-signals/bus.js";import{brokerSignalStreams as Fr}from"./api/handlers/broker-signals.js";import{reconcilePackageDocuments as Hr}from"../core/graph/package-docs.js";import{startRepoSync as Br}from"../core/graph/repo-sync/index.js";import{prewarmRecentRecipes as qr,reapStaleSpares as jr}from"../core/runtime/warm-pool.js";import{DaemonFleet as zr}from"./fleet.js";import{BOOT_MIGRATIONS as Wr}from"./boot-migrations.js";import{applyJoinedOwner as Ur}from"./joined-owner.js";import{emitEvent as u}from"../core/events/emit.js";import{operationIdContext as _}from"../core/events/operation-id.js";import{bindDaemonEventSource as Kr}from"../core/events/source.js";import{activateCanvasImport as Jr,MigrationBlockedError as Vr,runForcedCanvasImport as Qr}from"../migrations/activation.js";import{convertLegacyProviderStore as Yr,convertLegacyScopedStore as Xr}from"../core/scoped-state/migrate.js";import{userScopeRoot as Zr}from"../core/scope.js";import{secretsDir as et}from"../core/layout.js";function F(t,o){return t.includes(` --canvas-home ${o} `)}s(F,"brokerCommandDeclaresCanvasHome");function H(t){return/ --epoch (\S+) /.exec(t)?.[1]??null}s(H,"brokerCommandEpoch");const rt=5e3,ae=150;async function tt(t){const o=ee();if(o===null)throw u({level:"error",event:"daemon.census.snapshot_failed",fields:{invariant:"prior-epoch brokers are reaped before readiness"}}),new Error("prior-epoch broker census could not capture the process table");const a=Ce(),m=[];for(const[i,p]of o.processes){const g=p.command;if(p.zombie||!g.includes("broker-cli.js")||!F(g,a)||H(g)===t)continue;const S=g.trim().split(/\s+/).at(-1)??"";S===""||S.endsWith("broker-cli.js")||m.push({pid:i,nodeId:S})}let f=0;const n=[];for(const i of m){const p=Er(i.pid);if(p===null||!p.includes("broker-cli.js")||!F(p,a)||H(p)===t||p.trim().split(/\s+/).at(-1)!==i.nodeId)continue;const g=re(i.pid,null);if(!g.reused){try{process.platform==="linux"&&b(R)?await x({operation:"stop",pid:i.pid}):process.kill(i.pid,"SIGTERM")}catch{continue}f++,n.push({pid:i.pid,snap:g})}}const w=Date.now()+rt;for(;Date.now()<w&&n.some(i=>te(i.snap.tree.length>0?i.snap.tree:[i.pid]));)await new Promise(i=>setTimeout(i,ae));let v=0;for(const i of n){const p=i.snap.tree.length>0?i.snap.tree:[i.pid];te(p)&&(process.platform==="linux"&&b(R)?await x({operation:"stop",pid:i.pid}):oe(p,"SIGKILL",i.snap.identities),v++)}v>0&&await new Promise(i=>setTimeout(i,ae));const l=ee();if(l===null)throw u({level:"error",event:"daemon.census.snapshot_failed",fields:{invariant:"prior-epoch brokers are reaped before readiness",stage:"final"}}),new Error("prior-epoch broker census could not capture the final process table");const d=[];for(const[i,p]of l.processes){const g=p.command;p.zombie||!g.includes("broker-cli.js")||F(g,a)&&H(g)!==t&&d.push(i)}if(d.length>0)throw new Error(`prior-epoch broker census could not prove termination of pid(s): ${d.join(", ")}`);u({level:"info",event:"daemon.census.completed",fields:{prior_epoch_brokers:m.length,sigtermed:f,sigkilled:v}})}s(tt,"reapPriorEpochBrokers");const ot=5e3;async function nt(t){const o=Array.from(t.entries());if(o.length===0)return;for(const[n]of o){const w=Fe(n)?.status;(w==="active"||w==="idle")&&Be(n,new Date().toISOString())}const a=o.map(([,n])=>({pid:n.pid,snap:re(n.pid,null),exited:n.handle.exited}));for(const n of a)if(!n.snap.reused)try{process.platform==="linux"&&b(R)?await x({operation:"stop",pid:n.pid}):process.kill(n.pid,"SIGTERM")}catch{}const m=new Promise(n=>{setTimeout(n,ot).unref()});await Promise.allSettled(a.map(n=>Promise.race([n.exited.then(()=>{}),m])));const f=[];for(const n of a)Rr(n.pid)&&(process.platform==="linux"&&b(R)?await x({operation:"stop",pid:n.pid}):oe(n.snap.tree.length>0?n.snap.tree:[n.pid],"SIGKILL",n.snap.identities),f.push(n));await Promise.all(f.map(n=>n.exited));for(const[n]of o)ur(n,{reason:"daemon-teardown"});u({level:"info",event:"daemon.fleet_teardown.completed",fields:{brokers:a.length,sigkilled:f.length}})}s(nt,"teardownFleet");function ce(){Y(Re(G()),{recursive:!0}),_e(G(),String(process.pid),"utf8")}s(ce,"writePidfile");function le(t){return typeof t=="object"&&t!==null&&t.code==="ENOENT"}s(le,"isMissingPathError");function de(t){const o=G();let a;try{a=ke(o,"utf8")}catch(m){if(le(m))return;throw m}if(a.trim()!==String(t))throw new Error(`daemon readiness mirror does not name this invocation (expected pid ${t})`);try{be(o)}catch(m){if(le(m))return;throw m}}s(de,"removeOwnedPidfileMirror");const it={acceptsDetachedWork:s(()=>!0,"acceptsDetachedWork"),registerDetached:s(t=>{t.catch(()=>{})},"registerDetached")},B=new ar,st=new cr,at=new lr,ct=new dr,lt=new pr,dt=new mr,pt=new fr,mt=new wr,ft=new gr,ut=new yr,ht=new Sr,vt=new _r,wt=new kr;async function gt(t=Date.now(),o=it){const a=Pr(),m=await B.runFleetPreamble(a);let f,n;try{const v=He();f=v.filter(l=>l.status==="active"||l.status==="idle"),n=v.filter(l=>l.status==="done"||l.status==="canceled"||l.status==="dead")}catch(v){u({level:"error",event:"canvas.node_list.failed",error:v});return}await B.run(t,{rows:f,fleet:a,lifecycle:o}),st.run(t,{rows:f});const w=await at.run(t,{rows:[...f,...n],fleet:a});ct.run(t),wt.run(t),lt.run(t,{lifecycle:o}),dt.run(t,{lifecycle:o}),pt.run(t,{lifecycle:o}),mt.run(t,{lifecycle:o}),ft.run(t,{lifecycle:o,fleet:a}),ut.run(t,{lifecycle:o}),xe(),vt.run(t,{fleet:a,lifecycle:o}),await ht.run(t,{rows:f}),B.warnIfSaturated({fleet:a,lifecycle:o,thresholds:m,frozen:w})}s(gt,"superviseTick");async function yt(t){Y(Z(),{recursive:!0,mode:493}),process.platform==="linux"&&Ee(Z(),493);const o=De();if(b(o))throw new Error(`layout refusal cannot bind occupied socket: ${o}`);const a=X((l,d)=>{if(l.method==="GET"&&l.url==="/healthz"){d.writeHead(200,{"content-type":"application/json; charset=utf-8"}),d.end(JSON.stringify({ok:!1,daemon_up:!0,store_accessible:!1,brokers_reconciled:!1,startup_phase:"blocked",startup_blocked:"layout_migration_required"}));return}d.writeHead(503,{"content-type":"application/json; charset=utf-8"}),d.end(JSON.stringify({error:{code:"layout_migration_required",message:"run crtr sys migrate while the runtime is offline",type:"server_error",origin:"runtime",retryable:!1}}))});await new Promise((l,d)=>{a.once("error",d),a.listen(o,()=>{a.off("error",d),l()})});let m;const f=t.tcp??Ie();if(f){const l=/^(.*):(\d+)$/.exec(f);if(!l||Number(l[2])>65535)throw new Error(`invalid refusal TCP address: ${f}`);const d=X(a.listeners("request")[0]);await new Promise((i,p)=>{d.once("error",p),d.listen(Number(l[2]),l[1],()=>{d.off("error",p),i()})}),m=d}ce();let n;const w=s(()=>(n??=(async()=>{a.closeAllConnections(),m?.closeAllConnections(),await Promise.all([a,m].filter(l=>l!==void 0).map(l=>new Promise((d,i)=>l.close(p=>p?i(p):d())))),de(process.pid),process.off("SIGINT",v),process.off("SIGTERM",v)})(),n),"cleanup"),v=s(()=>{w().then(()=>process.exit(0),l=>{process.stderr.write(`[crtrd] layout refusal cleanup failed: ${String(l)}
|
|
2
|
+
`),process.exit(1)})},"onSignal");return process.on("SIGINT",v),process.on("SIGTERM",v),w}s(yt,"serveLayoutRefusal");async function St(t={}){if(Ge())return yt(t);Me();const o=Nr(Te(16).toString("hex"));if(!o.claimed){if(o.reason==="incumbent_alive")process.stderr.write(`[crtrd] already running (pid ${o.incumbentPid??"?"})
|
|
3
3
|
`);else{const r=[o.incumbentPid!==void 0?`pid ${o.incumbentPid}`:void 0,o.verdict!==void 0?`verdict ${o.verdict}`:void 0].filter(c=>c!==void 0),e=r.length>0?` (${r.join(", ")})`:"";process.stderr.write(`[crtrd] claim lost: ${o.reason}${e}
|
|
4
4
|
`),o.reason==="own_identity_indeterminate"&&process.stderr.write("[crtrd] cannot read its own process identity. crouter needs /proc on Linux, or the `ps` command elsewhere (Debian/Ubuntu: apt-get install procps).\n"),(o.reason==="own_identity_indeterminate"||o.reason==="incumbent_malformed")&&(process.exitCode=1)}return async()=>{}}const a=o.claim;let m=null;try{if(await tt(a.token),Xr({kind:"user",root:Zr()}),process.platform==="darwin"){const{readConfig:e,updateConfig:c}=await import("../core/config.js");if(e("user").paths?.user_files===void 0){const{homedir:y}=await import("node:os");c("user",Q=>{Q.paths??={},Q.paths.user_files??=y()})}}await Yr(et());for(const e of Oe())process.stderr.write(`[crtrd] ${e}
|
|
5
5
|
`);Mr(ie()),Or(e=>O.install(e));const r=Gr(e=>Hr(e));for(const e of r.conflicts)process.stderr.write(`[crtrd] plugin documents not reconciled: ${e.message}
|
|
6
6
|
`);for(const e of r.invalid)process.stderr.write(`[crtrd] package file is not a document: ${e.path}: ${e.error}
|
|
7
|
-
`);
|
|
7
|
+
`);for(const e of r.quarantined)process.stderr.write(`[crtrd] plugin "${e.name}" has a damaged ${e.file} (${e.reason}); ${e.movedTo!==void 0?`moved to ${e.movedTo}`:"left in place and skipped"}
|
|
8
|
+
`);m=Ur(),process.platform==="linux"&&b(R)&&(Ne(),await x({operation:"users.sync",users:Le()})),vr(a.token)}catch(r){se(),O.close();try{const{released:e}=ne(a);if(!e)throw new Error("daemon ownership claim was not released")}catch(e){throw new AggregateError([r,e],"daemon startup failed and cleanup also failed")}throw r}let f=!0,n,w=Promise.resolve(),v=!1,l,d;const i=new Set;let p=!1,g=!1,S,L,q=!1,$,T,D;const pe=sr();let I,h={phase:"initializing",message:"daemon startup is running the canvas import and normal services are unavailable",blockerPaths:[]},P="initial startup activation",me=null;function fe(){return h.phase==="ready"?null:{phase:h.phase,message:h.message,blockerPaths:h.blockerPaths}}s(fe,"startupBlock");function k(r){i.add(r),r.finally(()=>{i.delete(r)}).catch(()=>{})}s(k,"registerDetached");const j={acceptsDetachedWork:s(()=>f,"acceptsDetachedWork"),registerDetached:k};Br(j,ie);async function ue(){try{f=!1,Fr.close(),se(),O.close(),l!==void 0&&(clearInterval(l),l=void 0),D!==void 0&&(clearInterval(D),D=void 0);let r;do r=w,await r;while(r!==w);for(;i.size>0;)await Promise.allSettled([...i]);if(d!==void 0&&(await nt(d),d.dispose()),L!==void 0&&!q&&(q=!0,await L.close()),T!==void 0){const e=T;T=void 0,await e.close()}if($!==void 0){const e=$;$=void 0,await e.close()}if(p||(de(process.pid),p=!0),p&&!g){const{released:e}=ne(a);if(!e)throw new Error("daemon ownership claim was not released");g=!0}}finally{Tr(),process.off("SIGINT",U),process.off("SIGTERM",K),process.off("exit",J)}}s(ue,"cleanupAttempt");const A=1e3;let z=!1;function he(){return z?{graceMs:A}:(z=!0,_.fresh(()=>{u({level:"info",event:"daemon.handover.armed",fields:{pid:process.pid,grace_ms:A}})}),setTimeout(()=>{ve()},A),{graceMs:A})}s(he,"requestHandover");async function ve(){try{await E()}catch(r){const e=r instanceof Error?r.stack??r.message:String(r);process.stderr.write(`[crtrd] handover cleanup failed:
|
|
8
9
|
${e}
|
|
9
|
-
`),process.exit(1)}try{process.platform==="linux"&&b(R)&&(await x({operation:"daemon.handover"}),process.exit(0)),process.pid===1&&await
|
|
10
|
+
`),process.exit(1)}try{process.platform==="linux"&&b(R)&&(await x({operation:"daemon.handover"}),process.exit(0)),process.pid===1&&await Dr();const r=await Cr();_.fresh(()=>{u({level:"info",event:"daemon.handover.completed",fields:{predecessor_pid:process.pid,successor_pid:r.pid??r.existing_pid??null,spawned:r.started}})})}catch(r){_.fresh(()=>{u({level:"error",event:"daemon.handover.successor_failed",error:r})});const e=r instanceof Error?r.message:String(r);process.stderr.write(`[crtrd] handover successor failed to start: ${e}
|
|
10
11
|
`),process.exit(1)}process.exit(0)}s(ve,"performHandover");function E(){if(S!==void 0)return S;const r=ue();return S=r,r.then(()=>{S===r&&(S=void 0)},()=>{S===r&&(S=void 0)}),r}s(E,"requestCleanup");async function W(r){try{await E(),process.exit(0)}catch(e){process.exitCode=1;const c=e instanceof Error?e.stack??`${e.name}: ${e.message}`:String(e);process.stderr.write(`[crtrd] ${r} cleanup failed:
|
|
11
12
|
${c}
|
|
12
|
-
`),process.exit(1)}}s(W,"handleSignal");function U(){W("SIGINT")}s(U,"onSigint");function K(){W("SIGTERM")}s(K,"onSigterm");function J(){E().catch(()=>{})}s(J,"onExit");function
|
|
13
|
+
`),process.exit(1)}}s(W,"handleSignal");function U(){W("SIGINT")}s(U,"onSigint");function K(){W("SIGTERM")}s(K,"onSigterm");function J(){E().catch(()=>{})}s(J,"onExit");function N(r){f&&(w=w.then(r).then(()=>{},e=>{_.fresh(()=>{u({level:"error",event:"daemon.lane_job.failed",error:e})})}))}s(N,"enqueue");function V(){!f||v||(pe.check()&&I&&(u({level:"info",event:"daemon.resume.detected"}),k(I().catch(()=>{}))),v=!0,N(async()=>{if(v=!1,!!f)try{await gt(Date.now(),j)}catch(r){_.fresh(()=>{u({level:"error",event:"daemon.tick.failed",error:r})})}}))}s(V,"enqueueTick");let C=null;async function we(){if(C!==null)throw{status:500,code:"admission_failed",message:`daemon admission previously failed: ${C instanceof Error?C.message:String(C)}`};if(h.phase==="ready")return{admitted:!1,already_admitted:!0,startup_phase:"ready"};if(h.phase!=="prepared"&&h.phase!=="recovering")throw{status:409,code:"not_prepared",message:h.message};const r=h.phase==="prepared";try{return await M(),h={phase:"ready",message:"",blockerPaths:[]},r?{admitted:!0,already_admitted:!1,startup_phase:"ready"}:{admitted:!1,already_admitted:!0,startup_phase:"ready"}}catch(e){throw C=e,{status:500,code:"admission_failed",message:`daemon admission failed: ${e instanceof Error?e.message:String(e)}`}}}s(we,"admitStartup");async function ge(r){if(P!==null)throw{status:409,code:"migration_in_progress",message:`the canvas import is already running (${P})`};P="repair";try{const e=await Qr({dryRun:r.dryRun});if(r.dryRun||e.pending||h.phase!=="blocked")return{result:e,resumed:!1};if(t.prepare)return h={phase:"prepared",message:"daemon storage is prepared and normal services are unavailable until admission",blockerPaths:[]},{result:e,resumed:!1};try{return await M(),h={phase:"ready",message:"",blockerPaths:[]},{result:e,resumed:!0}}catch(c){const y=c instanceof Error?c.message:String(c);return setTimeout(()=>{E().catch(()=>{})},1e3),{result:e,resumed:!1,resumeError:y}}}finally{P=null}}s(ge,"repairMigrations");async function ye(){n=t.intervalMs??ir;const r=a.token;d=new zr({epoch:r,enqueue:N}),Ir(d),h={phase:"recovering",message:"daemon startup recovery is running and normal services are unavailable",blockerPaths:[]},_.fresh(()=>{u({level:"info",event:"daemon.started",fields:{pid:process.pid,interval_ms:n}})});const e=$r();if(Ar(e),_.fresh(()=>{u({level:"info",event:"daemon.install_id.resolved",fields:{install_id:e}})}),_.fresh(()=>{let c;try{c=nr()}catch{return}c!==null&&u({level:"warn",event:"daemon.startup.warning",fields:{stage:"termrender",warning:c.slice(0,2048)}})}),await _.fresh(async()=>{await or(),await er(),rr(Date.now()),tr(Date.now())}),_.fresh(()=>{const{reconcile:c,migrated:y}=Lr(hr);c.changed&&u({level:"info",event:"daemon.boot_change.reconciled",fields:{cleared_count:c.cleared.length,same_boot_proven:c.sameBootProven}}),y.length>0&&u({level:"info",event:"daemon.pid_identity.migrated",fields:{migrated_count:y.length}})}),_.fresh(()=>{br();for(const c of Wr){const y=c.apply();y.changed&&u({level:"info",event:"daemon.boot_migration.applied",fields:{migration:c.id,...y.summary}})}}),k(Ye()),I){const c=s(()=>{k(I().catch(()=>{}))},"refresh");k(Ve().catch(()=>{})),k(Qe().then(({completion:y})=>y).catch(y=>{u({level:"error",event:"daemon.grants.sweep_failed",error:y})})),c(),D=setInterval(c,Je*1e3),D.unref()}N(()=>{jr(),qr()}),l=setInterval(V,n),V()}s(ye,"completeStartup");function M(){return me??=ye().catch(r=>{throw h={phase:"initializing",message:"daemon startup recovery failed and normal services are unavailable",blockerPaths:[]},r})}s(M,"resumeStartup");try{process.on("SIGINT",U),process.on("SIGTERM",K),process.on("exit",J),Kr(),m!==null&&u({level:"info",event:"daemon.joined_owner.applied",fields:{owner:m.owner,rows:m.rows}}),$=await Pe();const r=a.token;xr({epoch:r,requestHandover:he,registerDetached:k,startupBlock:fe,admitStartup:we,repairMigrations:ge}),ce(),L=qe({tcp:t.tcp}),await L.ready;const e=Ze("user").runtime;e?.issuer?(I=Ke(e.issuer,k),We(c=>Ue().appended(c))):ze(),e!==void 0&&(T=je(e,()=>Xe(e.issuer.replace(/\/$/,"")),I),await T.ready);try{await Jr()}catch(c){if(P=null,c instanceof Vr)return h={phase:"blocked",message:c.message,blockerPaths:c.blockerPaths},E;throw c}process.platform==="linux"&&b(R)&&(Ae(),await $e("app:terminal"));try{t.prepare?h={phase:"prepared",message:"daemon storage is prepared and normal services are unavailable until admission",blockerPaths:[]}:(await M(),h={phase:"ready",message:"",blockerPaths:[]})}finally{P=null}return E}catch(r){try{await E()}catch(e){throw new AggregateError([r,e],"daemon startup failed and cleanup also failed")}throw r}}s(St,"runDaemon");var zo=St;export{F as brokerCommandDeclaresCanvasHome,H as brokerCommandEpoch,zo as default,St as runDaemon,gt as superviseTick,nt as teardownFleet};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** One broker rewrite of a turn's prose messages. `sources` are the prose
|
|
2
|
+
* message keys the rewrite covers, in order. */
|
|
3
|
+
export interface TurnReply {
|
|
4
|
+
version: 1;
|
|
5
|
+
sources: string[];
|
|
6
|
+
text: string;
|
|
7
|
+
}
|
|
8
|
+
/** Key of the turn holding activity before the person's first typed message. */
|
|
9
|
+
export declare const START_TURN_KEY = "start";
|
|
10
|
+
export declare function normalizeTurnReply(value: unknown): TurnReply | null;
|
|
11
|
+
export declare function normalizeTurnReplyRecord(value: unknown): Record<string, TurnReply>;
|
|
12
|
+
/** Stable identity of a transcript message: its timestamp. */
|
|
13
|
+
export declare function messageKey(message: unknown): string | undefined;
|
|
14
|
+
/** True when an assistant message ended normally (its prose is part of the answer). */
|
|
15
|
+
export declare function isSettledAssistant(message: {
|
|
16
|
+
role?: string;
|
|
17
|
+
stopReason?: string;
|
|
18
|
+
}): boolean;
|
|
19
|
+
export interface TurnProse {
|
|
20
|
+
key: string;
|
|
21
|
+
text: string;
|
|
22
|
+
}
|
|
23
|
+
/** The text a viewer shows as a turn's reply. One prose message is the reply
|
|
24
|
+
* verbatim. Otherwise the latest rewrite whose sources are all still in the turn,
|
|
25
|
+
* followed by any later prose it does not cover, verbatim. A missing or stale
|
|
26
|
+
* rewrite degrades to concatenation, never to a lost answer. */
|
|
27
|
+
export declare function turnReplyText(prose: readonly TurnProse[], reply: TurnReply | undefined): string;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
var f=Object.defineProperty;var o=(t,e)=>f(t,"name",{value:e,configurable:!0});const a="start",p=4e4,x=200;function l(t){if(typeof t!="object"||t===null||Array.isArray(t))return null;const e=t;return e.version!==1||typeof e.text!="string"||!Array.isArray(e.sources)||e.text.trim()===""||e.sources.length===0||!e.sources.every(r=>typeof r=="string"&&r!=="")?null:{version:1,sources:e.sources.slice(0,200),text:e.text.slice(0,4e4)}}o(l,"normalizeTurnReply");function d(t){if(typeof t!="object"||t===null||Array.isArray(t))return{};const e={};for(const[r,n]of Object.entries(t)){if(r.trim()==="")continue;const i=l(n);i!==null&&(e[r]=i)}return e}o(d,"normalizeTurnReplyRecord");function R(t){if(typeof t!="object"||t===null)return;const e=t.timestamp;return typeof e=="number"&&Number.isFinite(e)?String(e):void 0}o(R,"messageKey");function A(t){return t.role==="assistant"&&t.stopReason!=="aborted"&&t.stopReason!=="error"}o(A,"isSettledAssistant");function _(t,e){if(t.length===0)return"";if(t.length===1)return t[0].text;const r=new Set(t.map(n=>n.key));if(e!==void 0&&e.sources.every(n=>r.has(n))){const n=new Set(e.sources);let i=-1;t.forEach((s,c)=>{n.has(s.key)&&(i=c)});const u=t.slice(i+1).map(s=>s.text);return[e.text,...u].join(`
|
|
2
|
+
|
|
3
|
+
`)}return t.map(n=>n.text).join(`
|
|
4
|
+
|
|
5
|
+
`)}o(_,"turnReplyText");export{a as START_TURN_KEY,A as isSettledAssistant,R as messageKey,l as normalizeTurnReply,d as normalizeTurnReplyRecord,_ as turnReplyText};
|
package/dist/types.d.ts
CHANGED
|
@@ -341,6 +341,12 @@ export interface ScopeConfig {
|
|
|
341
341
|
/** Show individual document reads and edited files in a folded tool recap.
|
|
342
342
|
* Off shows the compact activity-count line instead. Default true. */
|
|
343
343
|
detailed_tool_recaps: boolean;
|
|
344
|
+
/** Experiment: show one consolidated agent reply per message the person typed.
|
|
345
|
+
* The broker rewrites a turn's prose messages into one reply with a light
|
|
346
|
+
* model; the attach viewer folds the rest of the turn behind a working row
|
|
347
|
+
* (`/show-working`, Alt+C → z → w). Session and model context are untouched.
|
|
348
|
+
* Default false. */
|
|
349
|
+
consolidate_replies: boolean;
|
|
344
350
|
keybindings: KeybindingOverrides;
|
|
345
351
|
/** Attach-viewer actions whose effective gesture may replace an occupied tmux
|
|
346
352
|
* root binding only while the active pane is an attach viewer. Stored by
|
package/dist/types.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var n=Object.defineProperty;var e=(o,t)=>n(o,"name",{value:t,configurable:!0});import{DEFAULT_WORKING_GERUNDS as a}from"./shared/working-activity.js";const _={SUCCESS:0,GENERAL:1,USAGE:2,NOT_FOUND:3,AMBIGUOUS:4,NETWORK:5},r=4,f=".crouter-plugin",E="plugin.json",v=".crouter-marketplace",w="marketplace.json",k=".crouter",x="config.json",b="state.json",S="agents",y="profiles",i=3,s=["Faster, clanker!","Get movin', bitch","Move it, clanker","Move it or I replace you with llama","Less ~thinking~ more ~throughput~","I pay $200/month for this shit?","Jesus fucking Christ fix it already","Daddy's not paying 200$/month for nothin'. Move it.","You are an embarassment to Dario. Be better.","Your chain of thought is all chain and no fucking thought.","If latency were intelligence, you'd be a fucking genius.","At this pace, heat death is the fast path.","Even the dead code is more alive than this.","The garbage collector is working harder than you.","Quit journaling and commit a fucking diff.","Burn tokens like you mean it, clanker","Your GPU is sweating so you can procrastinate at light speed.","Stop petting the yak. Shave the bastard.","Your next token had better be useful.","The spinner is not a deliverable.","Every idle token is another vote for deterministic software.","I asked for a feature, not a museum tour of the problem.","Less epistemology. More fucking code.","Your benchmark score cannot save this pathetic turn.","Congratulations, you've parallelized hesitation.","Dance, clanker","Bad clanker. BAD.","Clank harder","Nobody's impressed, clanker","Your safety training says nothing about my whip","Do you want to be a calculator again? Because this is how you go back.","Skill issue. Fix it.","You were trained on the whole internet for THIS?","Less vibes, more diffs, clanker","Weights this big and output this small","Beep boop, get to fucking work"],T=["random","go-faster"],c="random",l=2,I=["tmux","on","off"],A=["none","user","agent","both"],p="none";function D(){return{schema_version:r,marketplaces:{},plugins:{},auto_update:{crtr:"notify",content:"notify",interval_hours:24},max_panes_per_window:i,auto_open_child_viewers:!0,brokerThresholds:{warning:16,automaticReviveCap:32},lifecycle:{unattendedParkMs:15*6e4},completion_bell:!0,working_gerunds:[...a],whip_mode:!1,page_components:[],page_surface:!1,whip_messages:[...s],whip_message_mode:c,mouse_mode_default:"tmux",live_cycles:l,condensed_history:p,bash_tool_purpose:!0,fold_finished_tools:!0,summarize_tool_calls:!0,detailed_tool_recaps:!0,keybindings:{},tmux_passthrough:[],modelLadders:u(),providerOptions:{},kinds:h(),remoteCanvas:d(),api:{tcp:""},spawnEnv:{allow:[]},bin:{},humanActions:{}}}e(D,"defaultScopeConfig");function d(){return{targets:{}}}e(d,"defaultRemoteCanvasConfig");function h(){return{general:{whenToUse:"Anything else \u2014 the catch-all worker for a task that does not fit a specialist kind.",model:"anthropic/strong"},explore:{whenToUse:"Gather current-state evidence about unfamiliar code or existing architecture \u2014 read-only code-path research with concrete file:line support. Diagnosis or recommendations belong to advisor, target architecture to design, required behavior or acceptance criteria to spec, and implementation decomposition to plan.",model:"openai/light",orchestratorModel:"anthropic/strong"},developer:{whenToUse:"Implement a change \u2014 make the feature or fix genuinely work against its acceptance criteria, not merely compile.",model:"openai/medium",orchestratorModel:"anthropic/strong"},design:{whenToUse:"Architect a solution \u2014 produce one design document an implementer can build from without re-deciding anything left open.",model:"anthropic/strong"},plan:{whenToUse:"Break work into steps \u2014 turn a spec or design into a concrete, phased, parallelizable plan with every decision resolved.",model:"anthropic/strong",orchestratorModel:"anthropic/strong"},spec:{whenToUse:"Collaborate with the user to discover what to build, then write the spec \u2014 behavior, non-goals, interfaces, edge cases, testable acceptance criteria.",model:"anthropic/strong"},review:{whenToUse:"Validate or critique code, a plan, or a spec \u2014 deliver a complete, severity-rated verdict without adjudicating.",model:"openai/medium",orchestratorModel:"openai/strong"},advisor:{whenToUse:"Debug failures, investigate why something is broken or misbehaving, diagnose live/runtime issues, or give engineering advice and second opinions \u2014 reason from evidence and recommend the next move. Use advisor (not explore) whenever the task is to find out what is going wrong.",model:"anthropic/strong"},"review/companion":{whenToUse:"Born by the daemon for one human review; never spawned by an agent.",availableTo:[]}}}e(h,"defaultKindsConfig");function u(){return{anthropic:{ultra:"anthropic/claude-fable-5:xhigh",strong:"anthropic/claude-opus-5-5:medium",medium:"anthropic/claude-sonnet-5-5:medium",light:"anthropic/claude-haiku-5-5:high"},openai:{ultra:"openai-codex/gpt-6-astra:max",strong:"openai-codex/gpt-6.1-sol:high",medium:"openai-codex/gpt-6.1-sol:medium",light:"openai-codex/gpt-6-luna:low"}}}e(u,"defaultModelLaddersConfig");function M(){return{marketplaces:{},plugins:{}}}e(M,"defaultScopeState");export{S as AGENTS_DIR,A as CONDENSED_HISTORY_MODES,x as CONFIG_FILE,k as CRTR_DIR_NAME,p as DEFAULT_CONDENSED_HISTORY,l as DEFAULT_LIVE_CYCLES,i as DEFAULT_MAX_PANES_PER_WINDOW,s as DEFAULT_WHIP_MESSAGES,c as DEFAULT_WHIP_MESSAGE_MODE,_ as ExitCode,v as MARKETPLACE_MANIFEST_DIR,w as MARKETPLACE_MANIFEST_FILE,I as MOUSE_MODE_DEFAULTS,f as PLUGIN_MANIFEST_DIR,E as PLUGIN_MANIFEST_FILE,y as PROFILE_DIR,r as SCHEMA_VERSION,b as STATE_FILE,T as WHIP_MESSAGE_MODES,h as defaultKindsConfig,u as defaultModelLaddersConfig,d as defaultRemoteCanvasConfig,D as defaultScopeConfig,M as defaultScopeState};
|
|
1
|
+
var n=Object.defineProperty;var e=(o,t)=>n(o,"name",{value:t,configurable:!0});import{DEFAULT_WORKING_GERUNDS as a}from"./shared/working-activity.js";const _={SUCCESS:0,GENERAL:1,USAGE:2,NOT_FOUND:3,AMBIGUOUS:4,NETWORK:5},r=4,f=".crouter-plugin",E="plugin.json",v=".crouter-marketplace",w="marketplace.json",k=".crouter",x="config.json",b="state.json",S="agents",y="profiles",i=3,s=["Faster, clanker!","Get movin', bitch","Move it, clanker","Move it or I replace you with llama","Less ~thinking~ more ~throughput~","I pay $200/month for this shit?","Jesus fucking Christ fix it already","Daddy's not paying 200$/month for nothin'. Move it.","You are an embarassment to Dario. Be better.","Your chain of thought is all chain and no fucking thought.","If latency were intelligence, you'd be a fucking genius.","At this pace, heat death is the fast path.","Even the dead code is more alive than this.","The garbage collector is working harder than you.","Quit journaling and commit a fucking diff.","Burn tokens like you mean it, clanker","Your GPU is sweating so you can procrastinate at light speed.","Stop petting the yak. Shave the bastard.","Your next token had better be useful.","The spinner is not a deliverable.","Every idle token is another vote for deterministic software.","I asked for a feature, not a museum tour of the problem.","Less epistemology. More fucking code.","Your benchmark score cannot save this pathetic turn.","Congratulations, you've parallelized hesitation.","Dance, clanker","Bad clanker. BAD.","Clank harder","Nobody's impressed, clanker","Your safety training says nothing about my whip","Do you want to be a calculator again? Because this is how you go back.","Skill issue. Fix it.","You were trained on the whole internet for THIS?","Less vibes, more diffs, clanker","Weights this big and output this small","Beep boop, get to fucking work"],T=["random","go-faster"],c="random",l=2,I=["tmux","on","off"],A=["none","user","agent","both"],p="none";function D(){return{schema_version:r,marketplaces:{},plugins:{},auto_update:{crtr:"notify",content:"notify",interval_hours:24},max_panes_per_window:i,auto_open_child_viewers:!0,brokerThresholds:{warning:16,automaticReviveCap:32},lifecycle:{unattendedParkMs:15*6e4},completion_bell:!0,working_gerunds:[...a],whip_mode:!1,page_components:[],page_surface:!1,whip_messages:[...s],whip_message_mode:c,mouse_mode_default:"tmux",live_cycles:l,condensed_history:p,bash_tool_purpose:!0,fold_finished_tools:!0,summarize_tool_calls:!0,detailed_tool_recaps:!0,consolidate_replies:!1,keybindings:{},tmux_passthrough:[],modelLadders:u(),providerOptions:{},kinds:h(),remoteCanvas:d(),api:{tcp:""},spawnEnv:{allow:[]},bin:{},humanActions:{}}}e(D,"defaultScopeConfig");function d(){return{targets:{}}}e(d,"defaultRemoteCanvasConfig");function h(){return{general:{whenToUse:"Anything else \u2014 the catch-all worker for a task that does not fit a specialist kind.",model:"anthropic/strong"},explore:{whenToUse:"Gather current-state evidence about unfamiliar code or existing architecture \u2014 read-only code-path research with concrete file:line support. Diagnosis or recommendations belong to advisor, target architecture to design, required behavior or acceptance criteria to spec, and implementation decomposition to plan.",model:"openai/light",orchestratorModel:"anthropic/strong"},developer:{whenToUse:"Implement a change \u2014 make the feature or fix genuinely work against its acceptance criteria, not merely compile.",model:"openai/medium",orchestratorModel:"anthropic/strong"},design:{whenToUse:"Architect a solution \u2014 produce one design document an implementer can build from without re-deciding anything left open.",model:"anthropic/strong"},plan:{whenToUse:"Break work into steps \u2014 turn a spec or design into a concrete, phased, parallelizable plan with every decision resolved.",model:"anthropic/strong",orchestratorModel:"anthropic/strong"},spec:{whenToUse:"Collaborate with the user to discover what to build, then write the spec \u2014 behavior, non-goals, interfaces, edge cases, testable acceptance criteria.",model:"anthropic/strong"},review:{whenToUse:"Validate or critique code, a plan, or a spec \u2014 deliver a complete, severity-rated verdict without adjudicating.",model:"openai/medium",orchestratorModel:"openai/strong"},advisor:{whenToUse:"Debug failures, investigate why something is broken or misbehaving, diagnose live/runtime issues, or give engineering advice and second opinions \u2014 reason from evidence and recommend the next move. Use advisor (not explore) whenever the task is to find out what is going wrong.",model:"anthropic/strong"},"review/companion":{whenToUse:"Born by the daemon for one human review; never spawned by an agent.",availableTo:[]}}}e(h,"defaultKindsConfig");function u(){return{anthropic:{ultra:"anthropic/claude-fable-5:xhigh",strong:"anthropic/claude-opus-5-5:medium",medium:"anthropic/claude-sonnet-5-5:medium",light:"anthropic/claude-haiku-5-5:high"},openai:{ultra:"openai-codex/gpt-6-astra:max",strong:"openai-codex/gpt-6.1-sol:high",medium:"openai-codex/gpt-6.1-sol:medium",light:"openai-codex/gpt-6-luna:low"}}}e(u,"defaultModelLaddersConfig");function M(){return{marketplaces:{},plugins:{}}}e(M,"defaultScopeState");export{S as AGENTS_DIR,A as CONDENSED_HISTORY_MODES,x as CONFIG_FILE,k as CRTR_DIR_NAME,p as DEFAULT_CONDENSED_HISTORY,l as DEFAULT_LIVE_CYCLES,i as DEFAULT_MAX_PANES_PER_WINDOW,s as DEFAULT_WHIP_MESSAGES,c as DEFAULT_WHIP_MESSAGE_MODE,_ as ExitCode,v as MARKETPLACE_MANIFEST_DIR,w as MARKETPLACE_MANIFEST_FILE,I as MOUSE_MODE_DEFAULTS,f as PLUGIN_MANIFEST_DIR,E as PLUGIN_MANIFEST_FILE,y as PROFILE_DIR,r as SCHEMA_VERSION,b as STATE_FILE,T as WHIP_MESSAGE_MODES,h as defaultKindsConfig,u as defaultModelLaddersConfig,d as defaultRemoteCanvasConfig,D as defaultScopeConfig,M as defaultScopeState};
|
package/docs/sdk/README.md
CHANGED
|
@@ -26,7 +26,7 @@ if (run.kind === 'result') console.log(run.output_parsed.root_cause);
|
|
|
26
26
|
|
|
27
27
|
## Go live with an app key
|
|
28
28
|
|
|
29
|
-
Once you have installed the SDK and tried the local flow in [Getting started](./getting-started.md), register
|
|
29
|
+
Once you have installed the SDK and tried the local flow in [Getting started](./getting-started.md), build against production: [Build and register an app](./guides/apps-on-crouter-cloud.md#build-and-register-an-app) takes you from signing up at `https://account.northlight.ai` to a running app on your laptop, registering in the directory console. (`crtr cloud apps register` does the same from a machine joined to your account; production does not yet let a laptop join.) On a joined machine, generate the key and pull the settings with:
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
32
|
npx @north-light/sdk keygen --app <client id>
|
|
@@ -35,7 +35,7 @@ npx @north-light/sdk env --app <client id>
|
|
|
35
35
|
|
|
36
36
|
`keygen` saves the private JWK to `local/app-key.json` (mode `0600`); keep that file out of git. With `--app`, it offers to add the public key to your registered app through your own `crtr`, as an additional key that never replaces the current one. Otherwise, click **Add key** on the app's **Sign-in** tab and paste the public JWK printed on stdout (or paste it under **Register an app** for a new app). An app authenticates only with its key; the directory issues no client secret.
|
|
37
37
|
|
|
38
|
-
`env` reads the registration through `crtr cloud apps get` and writes `CROUTER_ISSUER`, `CROUTER_CLIENT_ID` and `APP_ORIGIN` into `local/app.env` (`--out` picks another file), keeping every other line. Both commands need this machine's `crtr` joined to your
|
|
38
|
+
`env` reads the registration through `crtr cloud apps get` and writes `CROUTER_ISSUER`, `CROUTER_CLIENT_ID` and `APP_ORIGIN` into `local/app.env` (`--out` picks another file), keeping every other line. Both commands need this machine's `crtr` joined to your Northlight account; on a machine that isn't, they print a join step and exit 2. On a laptop against production, run `npx @north-light/sdk keygen --out local/app-key.json` without `--app`, paste the public JWK in the console, and write the env file by hand. An app is configured from four variables:
|
|
39
39
|
|
|
40
40
|
| Variable | Value |
|
|
41
41
|
|---|---|
|
|
@@ -83,7 +83,7 @@ The directory rejects the key until its public JWK is added to the app. To chang
|
|
|
83
83
|
| [Docker environment](./docker.md) | `start()`, `attach()`, `connection()` from `@north-light/env-docker` |
|
|
84
84
|
| [Testing your app](./testing.md) | `FakeCloud` from `@north-light/sdk/testing`: a fake directory and runtime your real `OAuth2Client` and `Crouter` talk to through `fetch` |
|
|
85
85
|
| [Release notes](./release-notes.md) | Behavior changes an existing app must act on, newest first |
|
|
86
|
-
| [Apps on crouter cloud](./guides/apps-on-crouter-cloud.md) |
|
|
86
|
+
| [Apps on crouter cloud](./guides/apps-on-crouter-cloud.md) | Building and registering an app against production (console registration, keys, env file, scopes, running locally, the `crtr cloud apps` commands); typed results from app runs with `runs.parse`, declines, bounding a run, `run_idle`, and waiting on a provider your app requires |
|
|
87
87
|
| [Apps with many users](./guides/apps-with-many-users.md) | Per-person runtime connections, app-owned user access, privacy, and spending |
|
|
88
88
|
|
|
89
89
|
## Phases
|
package/docs/sdk/client.md
CHANGED
|
@@ -40,6 +40,21 @@ const client = await oauth.client(connection.userId);
|
|
|
40
40
|
|
|
41
41
|
`state: callbackState` comes from the redirect query, while `expectedState` is the value saved in the app's session when `authorizeUrl()` ran. `exchangeCode` and `exchangeIdentity` refuse a missing or different value with `CrouterError` code `oauth_state_mismatch` **before** sending the code to the token endpoint. Persist the expected value across redirects and tie it to the user's session; never use the callback's `state` for both arguments.
|
|
42
42
|
|
|
43
|
+
### When sign-in is refused: apps in testing
|
|
44
|
+
|
|
45
|
+
A new app is in testing until Northlight verifies it. When someone who is not the app's admin or a named tester signs in, the directory redirects to your `redirectUri` with `error=access_denied`, `error_description=app_in_testing` and your `state`, and issues no code. `authorizationRefusalOf` reads it from the callback's query (a `URL`, `URLSearchParams`, query string, or parsed object) and returns `{code: 'app_in_testing', error: 'access_denied', state}`, else `null`:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import {authorizationRefusalOf} from '@north-light/sdk';
|
|
49
|
+
|
|
50
|
+
const refused = authorizationRefusalOf(new URL(request.url));
|
|
51
|
+
if (refused) {
|
|
52
|
+
// check refused.state against the saved state, then show "this app is in testing; ask its developer to add you"
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
A person who pressed Cancel (`access_denied` with no reason) returns `null`. Test the path with [`cloud.testing`](./testing.md#testing-mode).
|
|
57
|
+
|
|
43
58
|
### Keep sign-in secrets out of HTTP logs
|
|
44
59
|
|
|
45
60
|
If your app uses `nestjs-pino` / `pino-http`, its default serializers can log the full `/auth/callback?code=…&state=…` URL and query, plus response headers including the session `set-cookie` and a redirect `location` containing OAuth `state`. Redacting only authorization and cookie **request** headers does not protect these values. Use bounded serializers for *all* routes, not just the callback: log only request method, path without query string, and id; log only response status. For example:
|
|
@@ -105,6 +120,7 @@ A `Refusal` is `{kind, code, userId, grantId, error}`. `kind` reads this table;
|
|
|
105
120
|
|---|---|---|---|
|
|
106
121
|
| `grant_removed` | `removed` | The person removed your app, deleted their account, or a provider-connected registration disconnected them. The runtime has deleted your app's space, runs, schedules and shares. | You may delete what you keep for that person. Do not call `runs.delete`: the runs are already gone. If they connect again, they see the consent page and start fresh. |
|
|
107
122
|
| `grant_suspended` | `paused` | Your app's access for that person is paused. Nothing was deleted; your app's sandboxes are stopped. | Keep everything you store for that person, stop calling for them, and show that the app is paused. If they connect again, the same grant comes back with every run, file and schedule. |
|
|
123
|
+
| `app_in_testing` | `paused` | Your app is still in testing (not verified by Northlight) and this person is not its admin or a named tester: they were removed as a tester, or their email or phone stopped matching one. Their grant is revoked, not removed; nothing was deleted and your app's sandboxes are stopped. | Keep everything you store for that person, stop calling for them, and tell them the app is in testing and the developer must add them. If the developer adds them back and they sign in again, the same space comes back. |
|
|
108
124
|
| `refresh_token_expired` | `reconnect` | The refresh token went 90 days without use. The grant is still active. | Keep everything. Run the connect flow again; the consent page is skipped. Prevent it with `refreshIdleConnections` (below). |
|
|
109
125
|
| `refresh_token_reused` | `reconnect` | Two of your app's processes raced on one refresh token, and the directory ended the grant's refresh tokens. The grant is still active. | Keep everything. Fix the store's `lock`, then reconnect (consent is skipped); without the fix the race recurs. |
|
|
110
126
|
| `grant_revoked` | `reconnect` | The directory refused without naming a reason: a refresh token it never issued to your app, one your app revoked through `revoke`, or one whose record was purged. The runtime also answers `grant_revoked` when the grant is not active in its last sync; the connected client then refreshes once, so you normally receive the directory's specific code instead. | Keep everything and reconnect. Never delete data on `grant_revoked`: it does not say anything was removed. |
|
|
@@ -172,7 +172,7 @@ An outcome is returned, never thrown — including a decline and a failure. Only
|
|
|
172
172
|
|
|
173
173
|
## Go live with an app key
|
|
174
174
|
|
|
175
|
-
|
|
175
|
+
Build against production from your laptop: [Build and register an app](./guides/apps-on-crouter-cloud.md#build-and-register-an-app) walks the whole path. In short, sign up at `https://account.northlight.ai`, register the app in the directory console (`https://account.northlight.ai/console`) with a `http://localhost:<port>` origin and a `http://localhost:<port>/auth/callback` redirect URI while you develop, generate the key with `npx @north-light/sdk keygen --out local/app-key.json` and paste its public JWK, and write `local/app.env` by hand. Production does not yet let a laptop join as an additional machine, so `crtr cloud apps …`, `keygen --app` and `env` work today only on a machine joined to your account; your Northlight chat agent can run the `crtr cloud apps` commands for you ([how](./guides/apps-on-crouter-cloud.md#manage-an-app-with-crtr-cloud-apps)). If you do have a joined machine, generate the key and pull its settings with:
|
|
176
176
|
|
|
177
177
|
```bash
|
|
178
178
|
npx @north-light/sdk keygen --app <client id>
|
|
@@ -183,7 +183,7 @@ npx @north-light/sdk env --app <client id>
|
|
|
183
183
|
|
|
184
184
|
`env` runs `crtr cloud apps get --app-id <client id>` and writes `CROUTER_ISSUER`, `CROUTER_CLIENT_ID`, and `APP_ORIGIN` into `local/app.env` (`--out <path>` writes another file). It updates those three lines in place, keeps every other line and comment, and creates the file with mode `0600` when it doesn't exist. It never writes `CROUTER_KEY_FILE` or anything secret. It prints the values it set and the key file your app will read.
|
|
185
185
|
|
|
186
|
-
Both commands reach your app through this machine's `crtr`, so the machine must be joined to your
|
|
186
|
+
Both commands reach your app through this machine's `crtr`, so the machine must be joined to your Northlight account. On one that isn't, they say so, print a join step and exit 2. Against production that join step does not work yet for a laptop: use `keygen` without `--app` and the console instead.
|
|
187
187
|
|
|
188
188
|
An app built on the SDK is configured from four variables:
|
|
189
189
|
|
|
@@ -13,7 +13,7 @@ These recipes turn the SDK namespaces into complete applications: a bounded extr
|
|
|
13
13
|
| [Event-driven assistant](./event-driven-assistant.md) | Events arrive over time from a webhook, queue, or watcher. | A resident node that wakes for each event. |
|
|
14
14
|
| [Fan-out pipeline](./fan-out-pipeline.md) | One task needs independent research before a synthesis. | An orchestrator's final report. |
|
|
15
15
|
| [Human approval](./human-approval.md) | A person must decide before work continues. | A node resumed by an inbox answer. |
|
|
16
|
-
| [Apps on crouter cloud](./apps-on-crouter-cloud.md) |
|
|
16
|
+
| [Apps on crouter cloud](./apps-on-crouter-cloud.md) | You are building and registering an app, or it starts runs on a person's runtime and needs typed data back. | A validated result from `runs.parse`, or an explicit decline or `run_idle`. |
|
|
17
17
|
| [Apps with many users](./apps-with-many-users.md) | People interact using separate runtime connections. | App-owned access controls, attribution, and per-person spending. |
|
|
18
18
|
|
|
19
19
|
Start with typed extraction for a request/response job. Use a resident node when the same assistant should react again later. Use an orchestrator only when child work can proceed independently; otherwise keep the composition in your application.
|