@openhands/agent-canvas 1.0.0-beta.2 → 1.0.0-beta.4
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/build/assets/{agent-server-conversation-service.api-DFvqqEDo.js → agent-server-conversation-service.api-C2V5SlHu.js} +1 -1
- package/build/assets/{automation-detail-Di7EOIZD.js → automation-detail-DJvbVSYK.js} +1 -1
- package/build/assets/{automations-list-IsIWdDiw.js → automations-list-rMki-8au.js} +1 -1
- package/build/assets/{conversation-HlncOV7n.js → conversation-DVrKU0oz.js} +3 -3
- package/build/assets/conversation-Dlys-D5A.js +1 -0
- package/build/assets/conversation-panel-iF09WjZ4.js +1 -0
- package/build/assets/{conversation-service.api-nb5W1PqR.js → conversation-service.api-CCfztilW.js} +1 -1
- package/build/assets/{conversation-websocket-context-C8_PkGLi.js → conversation-websocket-context-DhJhqUna.js} +1 -1
- package/build/assets/{files-tab-BhnLgimi.js → files-tab-R5z0lLdY.js} +1 -1
- package/build/assets/{git-control-bar-branch-button-M34A5_vX.js → git-control-bar-branch-button-COdRAYHb.js} +1 -1
- package/build/assets/{git-provider-icon-D5dCNy-k.js → git-provider-icon-BzLbc0yC.js} +1 -1
- package/build/assets/{home-CYQv7yc_.js → home-XxBpNOVq.js} +1 -1
- package/build/assets/{launch-DHEUYn2A.js → launch-CshDse3e.js} +1 -1
- package/build/assets/{manifest-6b31008e.js → manifest-a1d22045.js} +1 -1
- package/build/assets/{messages-BMzyOW2V.js → messages-D0rWot7s.js} +1 -1
- package/build/assets/{path-utils-YohAYyMv.js → path-utils-C3bQf6lJ.js} +1 -1
- package/build/assets/{planner-tab-CFc-hV07.js → planner-tab-BlrCpv-7.js} +1 -1
- package/build/assets/{recommended-automations-launcher-sgvfU62c.js → recommended-automations-launcher-BQChv2rc.js} +2 -2
- package/build/assets/root-35yk2E_M.js +2 -0
- package/build/assets/{root-layout-DVepR4To.js → root-layout-Czo9Ma6Q.js} +1 -1
- package/build/assets/{shared-conversation-DChOdb0t.js → shared-conversation-AMyqXvpk.js} +1 -1
- package/build/assets/{sidebar-mobile-menu-toggle-BWuf4PRH.js → sidebar-mobile-menu-toggle-Do_aA9Zm.js} +1 -1
- package/build/assets/{terminal-CRf9S0Z2.js → terminal-DgQk1Ay6.js} +1 -1
- package/build/assets/{use-active-conversation-Db3IWSPK.js → use-active-conversation-BEFNwnFk.js} +1 -1
- package/build/assets/{use-agent-state-Bn8vS5sY.js → use-agent-state-Bkrd1FZq.js} +1 -1
- package/build/assets/{use-create-conversation-CKS3EAHu.js → use-create-conversation-CEgXpkfH.js} +1 -1
- package/build/assets/{use-handle-plan-click-C9zJpK8A.js → use-handle-plan-click-Ckkm5eIY.js} +1 -1
- package/build/assets/{use-runtime-is-ready-CQCE3xZC.js → use-runtime-is-ready-B7EF4BKU.js} +1 -1
- package/build/assets/{use-unified-vscode-url-sZt29HrC.js → use-unified-vscode-url-DdSRw-6P.js} +1 -1
- package/build/assets/{use-user-conversation-DfgEB6RW.js → use-user-conversation-C6hrMMtn.js} +1 -1
- package/build/assets/{vscode-tab-Zb-QbTuV.js → vscode-tab-DjNArCgY.js} +1 -1
- package/build/index.html +3 -3
- package/dist/api/agent-server-adapter.cjs +1 -1
- package/dist/api/agent-server-adapter.cjs.map +1 -1
- package/dist/api/agent-server-adapter.js +1 -1
- package/dist/api/agent-server-adapter.js.map +1 -1
- package/dist/package.cjs +1 -1
- package/dist/package.cjs.map +1 -1
- package/dist/package.js +2 -1
- package/dist/package.js.map +1 -1
- package/package.json +2 -1
- package/scripts/dev-safe.mjs +1 -1
- package/build/assets/conversation-MtnkpqA9.js +0 -1
- package/build/assets/conversation-panel-DxnM6tRe.js +0 -1
- package/build/assets/root-E6pcRIYp.js +0 -2
package/build/index.html
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><title>OpenHands</title><meta name="description" content="Let's Start Building!"/><link rel="icon" type="image/svg+xml" href="/favicon.svg"/><link rel="modulepreload" href="/assets/manifest-
|
|
1
|
+
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><title>OpenHands</title><meta name="description" content="Let's Start Building!"/><link rel="icon" type="image/svg+xml" href="/favicon.svg"/><link rel="modulepreload" href="/assets/manifest-a1d22045.js"/><link rel="modulepreload" href="/assets/entry.client-1VMHpktY.js"/><link rel="modulepreload" href="/assets/rolldown-runtime-BFRubm34.js"/><link rel="modulepreload" href="/assets/preload-helper-CT1Z6Pdu.js"/><link rel="modulepreload" href="/assets/vendor~entry.client-CySJvEhK.js"/><link rel="modulepreload" href="/assets/providers-Bx6EfrzZ.js"/><link rel="modulepreload" href="/assets/declaration-BNMqORFE.js"/><link rel="modulepreload" href="/assets/vendor~entry.client-Du9GpFmX.js"/><link rel="modulepreload" href="/assets/react-CM_dJw1Z.js"/><link rel="modulepreload" href="/assets/i18n-CTohRuoO.js"/><link rel="modulepreload" href="/assets/react-dom-hVBnwgwZ.js"/><link rel="modulepreload" href="/assets/vendor~entry.client~root~root-layout~index-redirect~home~conversation-panel~conversation~la~cnj3raoq-DTEXlLSB.js"/><link rel="modulepreload" href="/assets/query-client-config-B7u9asM0.js"/><link rel="modulepreload" href="/assets/QueryClientProvider-CkGuhXg-.js"/><link rel="modulepreload" href="/assets/vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~skills-set~jfc6hidu-VnmIZrq3.js"/><link rel="modulepreload" href="/assets/context-CEQZwATj.js"/><link rel="modulepreload" href="/assets/option-service.api-KvY_mZMY.js"/><link rel="modulepreload" href="/assets/utils-i18rdUj2.js"/><link rel="modulepreload" href="/assets/active-backend-context-cCM1vYYZ.js"/><link rel="modulepreload" href="/assets/query-keys-tAsQcc_9.js"/><link rel="modulepreload" href="/assets/agent-server-ui-style-scope-BwIZYdC1.js"/><link rel="modulepreload" href="/assets/vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~g56ukk6u-DsSvIDZQ.js"/><link rel="modulepreload" href="/assets/mutation-D0OogFCz.js"/><link rel="modulepreload" href="/assets/custom-toast-handlers-C-SZFmto.js"/><link rel="modulepreload" href="/assets/health-store-BDC2rM-X.js"/><link rel="modulepreload" href="/assets/retrieve-axios-error-message-BY-yIkIq.js"/><link rel="modulepreload" href="/assets/createLucideIcon-Ddu8jDOQ.js"/><link rel="modulepreload" href="/assets/vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~hkqzh1hb-BZ0HXuHD.js"/><link rel="modulepreload" href="/assets/vendor~entry.client~root~root-layout~home~conversation-panel~conversation~skills-settings~m~o9nrx3fm-D44TR8hL.js"/><link rel="modulepreload" href="/assets/vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~skills-set~zm51vy4j-iOsylxCS.js"/><link rel="modulepreload" href="/assets/agent-server-client-options-Bc5ZorQZ.js"/><link rel="modulepreload" href="/assets/llm-client-DaH1TuyR.js"/><link rel="modulepreload" href="/assets/agent-server-compatibility-BlkUsrX2.js"/><link rel="modulepreload" href="/assets/server-client-DyAQ3NZ_.js"/><link rel="modulepreload" href="/assets/settings-client-C73C7IgV.js"/><link rel="modulepreload" href="/assets/root-35yk2E_M.js"/><link rel="modulepreload" href="/assets/useTranslation-DCOdSSMl.js"/><link rel="modulepreload" href="/assets/brand-button-Br7f0kZJ.js"/><link rel="modulepreload" href="/assets/base-modal-_dYTw1ri.js"/><link rel="modulepreload" href="/assets/loading-spinner-BPtYORNK.js"/><link rel="modulepreload" href="/assets/modal-backdrop-BAbgYsqB.js"/><link rel="modulepreload" href="/assets/modal-body-BI6Ru2Qr.js"/><link rel="modulepreload" href="/assets/use-config-Co1O8-Ey.js"/><link rel="modulepreload" href="/assets/color-themes-DSaoIL6A.js"/><link rel="modulepreload" href="/assets/vendor~root~root-layout~home~conversation-panel~conversation~launch~extensions-hub~skills-s~f2l2lr17-CDXvdvb2.js"/><link rel="stylesheet" href="/assets/root-DHeCXo9N.css"/></head><body data-agent-server-ui="" class="m-0"><div data-agent-server-ui="" style="--oh-color-logo:#cfb755;--oh-color-base:var(--cool-grey-950);--oh-color-base-secondary:var(--cool-grey-925);--oh-color-danger:#e76a5e;--oh-color-success:#a5e75e;--oh-color-basic:var(--cool-grey-400);--oh-color-tertiary:var(--cool-grey-800);--oh-color-tertiary-light:var(--cool-grey-300);--oh-color-content:var(--cool-grey-100);--oh-color-content-2:var(--cool-grey-50);--oh-background:var(--cool-grey-950);--oh-foreground:var(--cool-grey-100);--oh-surface:var(--cool-grey-925);--oh-surface-foreground:var(--cool-grey-100);--oh-surface-raised:var(--cool-grey-900);--oh-surface-deep:var(--cool-grey-975);--oh-overlay:var(--cool-grey-925);--oh-overlay-foreground:var(--cool-grey-100);--oh-muted:var(--cool-grey-400);--oh-text-secondary:var(--cool-grey-300);--oh-text-tertiary:var(--cool-grey-200);--oh-text-dim:var(--cool-grey-500);--oh-text-subtle:var(--cool-grey-600);--oh-interactive-hover:var(--cool-grey-700);--oh-interactive-hover-low:var(--cool-grey-900);--oh-interactive-active:var(--cool-grey-800);--oh-interactive-selected:var(--cool-grey-600);--oh-scrollbar:color-mix(in srgb, var(--cool-grey-400) 30%, transparent);--oh-scrollbar-hover:color-mix(in srgb, var(--cool-grey-400) 50%, transparent);--oh-default:var(--cool-grey-800);--oh-default-foreground:var(--cool-grey-100);--oh-accent-foreground:var(--cool-grey-950);--oh-success:#a5e75e;--oh-success-foreground:var(--cool-grey-950);--oh-warning-foreground:var(--cool-grey-950);--oh-danger:#e76a5e;--oh-danger-foreground:var(--cool-grey-50);--oh-segment:var(--cool-grey-925);--oh-segment-foreground:var(--cool-grey-100);--oh-border-width:1px;--oh-field-border-width:1px;--oh-border:var(--cool-grey-700);--oh-border-input:var(--cool-grey-600);--oh-border-subtle:var(--cool-grey-800);--oh-separator:rgba(113, 120, 136, 0.5);--oh-focus:#ffffff;--oh-status-success:#1FBD53;--oh-status-error:#FF684E;--oh-link:var(--cool-grey-100);--oh-radius:8px;--oh-field-radius:8px;--oh-surface-shadow:none;--oh-overlay-shadow:none;--oh-field-shadow:none;--oh-bg-dark:var(--cool-grey-950);--oh-bg-light:var(--cool-grey-900);--oh-bg-input:var(--cool-grey-800);--oh-bg-workspace:var(--cool-grey-925);--oh-text-editor-base:var(--cool-grey-400);--oh-text-editor-active:var(--cool-grey-300);--oh-bg-editor-sidebar:var(--cool-grey-925);--oh-bg-editor-active:var(--cool-grey-900);--oh-border-editor-sidebar:var(--cool-grey-800);--oh-bg-neutral-muted:color-mix(in srgb, var(--cool-grey-300) 20%, transparent)"><div class="dark min-h-screen text-foreground" data-theme="dark"><script>
|
|
2
2
|
console.log(
|
|
3
3
|
"💿 Hey developer 👋. You can provide a way better UX than this " +
|
|
4
4
|
"when your app is loading JS modules and/or running `clientLoader` " +
|
|
5
5
|
"functions. Check out https://reactrouter.com/start/framework/route-module#hydratefallback " +
|
|
6
6
|
"for more information."
|
|
7
7
|
);
|
|
8
|
-
</script><div data-rht-toaster="" style="position:fixed;z-index:9999;top:16px;left:16px;right:16px;bottom:16px;pointer-events:none"></div><div id="modal-portal-exit"></div></div></div><script>window.__reactRouterContext = {"basename":"/","future":{"unstable_optimizeDeps":false,"unstable_passThroughRequests":false,"unstable_subResourceIntegrity":false,"unstable_trailingSlashAwareDataRequests":false,"unstable_previewServerPrerendering":false,"v8_middleware":false,"v8_splitRouteModules":false,"v8_viteEnvironmentApi":false},"routeDiscovery":{"mode":"initial"},"ssr":false,"isSpaMode":true};window.__reactRouterContext.stream = new ReadableStream({start(controller){window.__reactRouterContext.streamController = controller;}}).pipeThrough(new TextEncoderStream());</script><script type="module" async="">import "/assets/manifest-
|
|
9
|
-
import * as route0 from "/assets/root-
|
|
8
|
+
</script><div data-rht-toaster="" style="position:fixed;z-index:9999;top:16px;left:16px;right:16px;bottom:16px;pointer-events:none"></div><div id="modal-portal-exit"></div></div></div><script>window.__reactRouterContext = {"basename":"/","future":{"unstable_optimizeDeps":false,"unstable_passThroughRequests":false,"unstable_subResourceIntegrity":false,"unstable_trailingSlashAwareDataRequests":false,"unstable_previewServerPrerendering":false,"v8_middleware":false,"v8_splitRouteModules":false,"v8_viteEnvironmentApi":false},"routeDiscovery":{"mode":"initial"},"ssr":false,"isSpaMode":true};window.__reactRouterContext.stream = new ReadableStream({start(controller){window.__reactRouterContext.streamController = controller;}}).pipeThrough(new TextEncoderStream());</script><script type="module" async="">import "/assets/manifest-a1d22045.js";
|
|
9
|
+
import * as route0 from "/assets/root-35yk2E_M.js";
|
|
10
10
|
|
|
11
11
|
window.__reactRouterRouteModules = {"root":route0};
|
|
12
12
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require(`../_virtual/_rolldown/runtime.cjs`);const e=require(`./agent-server-config.cjs`),t=require(`./backend-registry/active-store.cjs`),n=require(`./backend-registry/auth.cjs`),r=require(`./agent-server-client-options.cjs`),i=require(`./conversation-metadata-store.cjs`),a=require(`../services/settings.cjs`),o=require(`../types/agent-server/core/base/common.cjs`),s=require(`../constants/acp-providers.cjs`),c=require(`./agent-server-compatibility.cjs`),l=require(`./settings-service/settings-service.api.cjs`);var u=`canvas_ui`,d=`canvas_ui_tool`,f=[`terminal`,`file_editor`,`task_tracker`,u],p=`browser_tool_set`,m=`task_tool_set`;function h(){return!0}function g(){let e=(void 0)?.trim();if(!e)return null;try{let t=JSON.parse(e);return!t||typeof t!=`object`?null:t}catch{return null}}function _(){let e=g();if(!e?.services)return;let t=[];t.push(`<RUNTIME_SERVICES>`),e.mode?t.push(`You are running inside an agent-canvas dev stack started in '${e.mode}' mode.`):t.push(`You are running inside an agent-canvas dev stack.`),t.push(`The following services are reachable from your sandbox. URLs are written`,`from your point of view (i.e., as you should curl/fetch them).`,``);let{agent_server:n,ingress:r,automation:i}=e.services,a=e.services.frontend??e.services.vite;n?.url_from_agent&&t.push(`* Agent Server (you): ${n.url_from_agent}`,` ${n.description??`The agent-server hosting your tool calls.`}`),r?.url_from_agent&&t.push(`* Ingress: ${r.url_from_agent}`,` ${r.description??`Unified entry point for browser-facing traffic.`}`),a?.url_from_agent&&t.push(`* Frontend: ${a.url_from_agent}`,` ${a.description??`Frontend dev server.`}`),i?.url_from_agent?(t.push(`* Automation backend: ${i.url_from_agent}`,` ${i.description??`OpenHands Automations service.`}`),i.docs_url&&t.push(` Docs: ${i.docs_url}`),i.openapi_url&&t.push(` OpenAPI: ${i.openapi_url}`),i.auth_env_var&&t.push(` Auth: header 'X-API-Key: $${i.auth_env_var}'`)):t.push(`* Automation backend: not running in this dev mode (skip /api/automation calls).`);let o=n?.url_from_agent;return t.push(``,`Trust this block over guessing: do not assume any other URLs are running.`),o&&t.push(`In particular, ${o} inside your sandbox is the Agent Server`,`you are running inside of — NOT the automation backend.`),t.push(`</RUNTIME_SERVICES>`),t.join(`
|
|
1
|
+
require(`../_virtual/_rolldown/runtime.cjs`);const e=require(`./agent-server-config.cjs`),t=require(`./backend-registry/active-store.cjs`),n=require(`./backend-registry/auth.cjs`),r=require(`./agent-server-client-options.cjs`),i=require(`./conversation-metadata-store.cjs`),a=require(`../services/settings.cjs`),o=require(`../types/agent-server/core/base/common.cjs`),s=require(`../constants/acp-providers.cjs`),c=require(`./agent-server-compatibility.cjs`),l=require(`./settings-service/settings-service.api.cjs`);var u=`canvas_ui`,d=`canvas_ui_tool`,f=[`terminal`,`file_editor`,`task_tracker`,u],p=`browser_tool_set`,m=`task_tool_set`;function h(){return!0}function g(){let e=(void 0)?.trim();if(!e)return null;try{let t=JSON.parse(e);return!t||typeof t!=`object`?null:t}catch{return null}}function _(){let e=g();if(!e?.services)return;let t=[];t.push(`<RUNTIME_SERVICES>`),e.mode?t.push(`You are running inside an agent-canvas dev stack started in '${e.mode}' mode.`):t.push(`You are running inside an agent-canvas dev stack.`),t.push(`The following services are reachable from your sandbox. URLs are written`,`from your point of view (i.e., as you should curl/fetch them).`,``);let{agent_server:n,ingress:r,automation:i}=e.services,a=e.services.frontend??e.services.vite;n?.url_from_agent&&t.push(`* Agent Server (you): ${n.url_from_agent}`,` ${n.description??`The agent-server hosting your tool calls.`}`),r?.url_from_agent&&t.push(`* Ingress: ${r.url_from_agent}`,` ${r.description??`Unified entry point for browser-facing traffic.`}`),a?.url_from_agent&&t.push(`* Frontend: ${a.url_from_agent}`,` ${a.description??`Frontend dev server.`}`),i?.url_from_agent?(t.push(`* Automation backend: ${i.url_from_agent}`,` ${i.description??`OpenHands Automations service.`}`),i.docs_url&&t.push(` Docs: ${i.docs_url}`),i.openapi_url&&t.push(` OpenAPI: ${i.openapi_url}`),i.auth_env_var&&t.push(` Auth: header 'X-Session-API-Key: $${i.auth_env_var}'`)):t.push(`* Automation backend: not running in this dev mode (skip /api/automation calls).`);let o=n?.url_from_agent;return t.push(``,`Trust this block over guessing: do not assume any other URLs are running.`),o&&t.push(`In particular, ${o} inside your sandbox is the Agent Server`,`you are running inside of — NOT the automation backend.`),t.push(`</RUNTIME_SERVICES>`),t.join(`
|
|
2
2
|
`)}function v(e){let{host:t}=r.getAgentServerClientOptions();return`${t}/api/conversations/${e}`}function y(e){return`Conversation ${e.slice(0,5)}`}function b(t){let n=i.getStoredConversationMetadata(t.id),c=t.agent?.kind===`ACPAgent`,l=c?t.tags?.acpserver??null:null;return{id:t.id,created_by_user_id:null,selected_repository:n?.selected_repository??null,selected_branch:n?.selected_branch??null,git_provider:n?.git_provider??null,selected_workspace:n?.selected_workspace??null,title:t.title?.trim()?t.title:y(t.id),trigger:null,pr_number:[],agent_kind:c?`acp`:`openhands`,acp_server:l,llm_model:c?s.resolveEffectiveAcpModel({runtimeName:t.current_model_name,runtimeId:t.current_model_id,configured:t.agent?.acp_model,sdkLlm:t.agent?.llm?.model}):t.agent?.llm?.model??a.DEFAULT_SETTINGS.llm_model,metrics:t.metrics?{accumulated_cost:t.metrics.accumulated_cost??null,max_budget_per_task:t.metrics.max_budget_per_task??null,accumulated_token_usage:t.metrics.accumulated_token_usage?{prompt_tokens:t.metrics.accumulated_token_usage.prompt_tokens??0,completion_tokens:t.metrics.accumulated_token_usage.completion_tokens??0,cache_read_tokens:t.metrics.accumulated_token_usage.cache_read_tokens??0,cache_write_tokens:t.metrics.accumulated_token_usage.cache_write_tokens??0,context_window:t.metrics.accumulated_token_usage.context_window??0,per_turn_token:t.metrics.accumulated_token_usage.per_turn_token??0}:null}:null,created_at:t.created_at,updated_at:t.updated_at,execution_status:t.execution_status??o.ExecutionStatus.IDLE,sandbox_status:t.sandbox_status??null,conversation_url:v(t.id),session_api_key:r.getAgentServerClientOptions().apiKey??null,sandbox_id:null,workspace:{working_dir:t.workspace?.working_dir??e.getAgentServerWorkingDir()},public:!1,sub_conversation_ids:[]}}function x(e){return{items:e.items.map(b),next_page_id:e.next_page_id??null}}var S=[`acp_command`,`acp_args`,`acp_env`,`acp_model`,`acp_session_mode`,`acp_prompt_timeout`],C=`acpserver`,w=new Set([`schema_version`,`agent_settings`,`workspace`,`conversation_id`,`initial_message`,`plugins`]);function T(e){return!e||typeof e!=`object`||Array.isArray(e)?{}:structuredClone(e)}function E(e){if(typeof e!=`string`)return;let t=e.trim();return t.length>0?t:void 0}function D(e){return e.confirmation_mode===!0?e.security_analyzer===`llm`?{kind:`ConfirmRisky`,threshold:`HIGH`,confirm_unknown:!0}:{kind:`AlwaysConfirm`}:{kind:`NeverConfirm`}}function O(e){switch(e.security_analyzer){case`llm`:return{kind:`LLMSecurityAnalyzer`};case`pattern`:return{kind:`PatternSecurityAnalyzer`};case`policy_rail`:return{kind:`PolicyRailSecurityAnalyzer`};default:return}}function k(e){return!!e&&typeof e==`object`&&!Array.isArray(e)&&typeof e.name==`string`}function A(e,t){return e===p?h()&&c.isAgentServerToolAvailable(e):e===m?t.enable_sub_agents===!0&&c.isAgentServerToolAvailable(e):!0}function j(e){let t=new Map;for(let e of f)t.set(e,{name:e,params:{}});for(let n of[p,m])A(n,e)&&t.set(n,{name:n,params:{}});let n=e.tools;if(Array.isArray(n)&&n.every(e=>k(e)))for(let r of n)A(r.name,e)&&t.set(r.name,{name:r.name,params:T(r.params)});return Array.from(t.values())}function M(e,t){let n=[e?.trim(),t?.trim()].filter(Boolean);return n.length===0?null:{role:`user`,content:[{type:`text`,text:n.join(`
|
|
3
3
|
|
|
4
4
|
`)}],run:!0}}function N(t){let n=_();return{...T(t.agent_context),load_public_skills:e.shouldLoadPublicSkills(),load_user_skills:!0,load_project_skills:!0,...n?{system_message_suffix:n}:{}}}function P(e){return T(e.agent_settings).agent_kind===`acp`}function F(e){let t=T(e.agent_settings).acp_server;return typeof t==`string`&&t.length>0?t:void 0}function I(e){let t=e.acp_command;if(!(Array.isArray(t)&&t.length===0)&&t!==void 0)return t;let n=s.getAcpProvider(typeof e.acp_server==`string`?e.acp_server:void 0);return n?[...n.default_command]:t}function L(e){let t=T(e.agent_settings),n={agent_kind:`acp`,agent_context:N(t)};for(let e of S){if(e===`acp_model`)continue;let r=e===`acp_command`?I(t):t[e];r!=null&&(n[e]=r)}let r=s.getAcpProvider(typeof t.acp_server==`string`?t.acp_server:void 0),i=s.resolveEffectiveAcpModel({configured:t.acp_model,providerDefault:r?.default_model});return i&&(n.acp_model=i),n}function R(e){let t=T(e.agent_settings),n=T(t.llm);n.model=typeof n.model==`string`&&n.model.trim().length>0?n.model:a.DEFAULT_SETTINGS.llm_model;let r=E(n.api_key);r?n.api_key=r:delete n.api_key;let i=E(n.base_url);i?n.base_url=i:delete n.base_url;let o=T(t.mcp_config);(Object.keys(o).length===0||!(`mcpServers`in o))&&delete t.mcp_config,delete t.acp_server;for(let e of S)delete t[e];return{...t,llm:n,agent_context:N(t),tools:j(t)}}function z(e){return P(e)?L(e):R(e)}function B(t){let{settings:n,query:r,conversationInstructions:i,plugins:a,workingDir:o}=t,s=T(n.conversation_settings),c=M(r,i);return w.forEach(e=>delete s[e]),{...s,workspace:{kind:`LocalWorkspace`,working_dir:o??e.getAgentServerWorkingDir()},...c?{initial_message:c}:{},...a?.length?{plugins:a.map(e=>({source:e.source,...e.ref?{ref:e.ref}:{},...e.repo_path?{repo_path:e.repo_path}:{}}))}:{}}}function V(e){let r=e.encryptedAgentSettings?{...e.settings,agent_settings:e.encryptedAgentSettings}:e.settings,i=P(r),a=z(r),o=i?F(r):void 0,s=B(e.encryptedConversationSettings?{...e,settings:{...e.settings,conversation_settings:e.encryptedConversationSettings}}:e),c={agent_settings:a,workspace:s.workspace,confirmation_policy:D(s),max_iterations:typeof s.max_iterations==`number`?s.max_iterations:500,stuck_detection:!0,autotitle:!0,worktree:!0};o&&(c.tags={[C]:o}),e.secretsEncrypted&&(c.secrets_encrypted=!0),e.conversationId&&(c.conversation_id=e.conversationId);let l=O(s);if(l&&(c.security_analyzer=l),s.initial_message&&(c.initial_message=s.initial_message),s.plugins&&(c.plugins=s.plugins),s.hook_config&&(c.hook_config=s.hook_config),c.tool_module_qualnames={[u]:d,...s.tool_module_qualnames??{}},s.agent_definitions&&(c.agent_definitions=s.agent_definitions),e.customSecrets&&e.customSecrets.length>0){let r=n.buildAuthHeaders(t.getEffectiveLocalBackend()),a={};for(let t of e.customSecrets){let e={kind:`LookupSecret`,url:`/api/settings/secrets/${encodeURIComponent(t.name)}`,description:t.description};Object.keys(r).length>0&&(e.headers=r),a[t.name]=e}c.secrets=a,i&&(c.agent_settings.agent_context={...c.agent_settings.agent_context,secrets:a})}return c}async function H(e){let{SecretsService:t}=await Promise.resolve().then(()=>require(`./secrets-service.cjs`)),[n,r]=await Promise.all([l.default.getSettingsForConversation(),t.getSecrets()]),{agentSettings:i,conversationSettings:a,secretsEncrypted:o}=n;return V({...e,encryptedAgentSettings:i,encryptedConversationSettings:a,secretsEncrypted:o,customSecrets:r})}function U(){return{hooks:[]}}exports.buildStartConversationRequestWithEncryptedSettings=H,exports.emptyHooksResponse=U,exports.getDefaultConversationTitle=y,exports.toAppConversation=b,exports.toConversationPage=x;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-server-adapter.cjs","names":[],"sources":["../../src/api/agent-server-adapter.ts"],"sourcesContent":["import { DEFAULT_SETTINGS } from \"#/services/settings\";\nimport { ExecutionStatus } from \"#/types/agent-server/core\";\nimport { Settings, SettingsValue } from \"#/types/settings\";\nimport {\n getAcpProvider,\n resolveEffectiveAcpModel,\n} from \"#/constants/acp-providers\";\nimport { getAgentServerClientOptions } from \"./agent-server-client-options\";\nimport { isAgentServerToolAvailable } from \"./agent-server-compatibility\";\nimport {\n getAgentServerWorkingDir,\n shouldLoadPublicSkills,\n} from \"./agent-server-config\";\nimport { getEffectiveLocalBackend } from \"./backend-registry/active-store\";\nimport { buildAuthHeaders } from \"./backend-registry/auth\";\nimport {\n GetHooksResponse,\n PluginSpec,\n AppConversation,\n AppConversationPage,\n SandboxStatus,\n} from \"./conversation-service/agent-server-conversation-service.types\";\nimport SettingsService from \"./settings-service/settings-service.api\";\nimport { getStoredConversationMetadata } from \"./conversation-metadata-store\";\n\nexport interface DirectConversationInfo {\n id: string;\n title?: string | null;\n created_at: string;\n updated_at: string;\n execution_status?: string | null;\n /** Cloud-only sandbox lifecycle state. Omitted / null for local agent-server conversations. */\n sandbox_status?: string | null;\n metrics?: {\n accumulated_cost?: number | null;\n max_budget_per_task?: number | null;\n accumulated_token_usage?: {\n prompt_tokens?: number;\n completion_tokens?: number;\n cache_read_tokens?: number;\n cache_write_tokens?: number;\n context_window?: number;\n per_turn_token?: number;\n } | null;\n } | null;\n agent?: {\n /**\n * Pydantic discriminator from the SDK union: ``\"ACPAgent\"`` for ACP CLI\n * subprocesses (model lives on the subprocess via ``acp_model``),\n * ``\"Agent\"`` for direct litellm. Read by {@link toAppConversation}.\n */\n kind?: string | null;\n acp_model?: string | null;\n llm?: {\n model?: string | null;\n } | null;\n } | null;\n current_model_id?: string | null;\n current_model_name?: string | null;\n workspace?: {\n working_dir?: string | null;\n } | null;\n /**\n * Arbitrary string-keyed conversation tags surfaced by the agent-server\n * (see ``ConversationInfo.tags``). Canvas only consumes one key today —\n * ``ACP_SERVER_TAG_KEY`` (\"acpserver\") — but the field is typed as a\n * generic record so future readers don't need another wire-shape change.\n * Keys are constrained to ``^[a-z0-9]+$`` by the agent-server validator;\n * values are opaque strings.\n */\n tags?: Record<string, string> | null;\n}\n\n// Module qualname for the Canvas-UI tool. The agent-server imports this via\n// tool_module_qualnames; the host directory is exposed via OH_EXTRA_PYTHON_PATH\n// (see scripts/dev-safe.mjs).\nconst CANVAS_UI_TOOL_NAME = \"canvas_ui\";\nconst CANVAS_UI_TOOL_MODULE = \"canvas_ui_tool\";\n\nconst DEFAULT_TOOL_NAMES = [\n \"terminal\",\n \"file_editor\",\n \"task_tracker\",\n CANVAS_UI_TOOL_NAME,\n];\nconst BROWSER_TOOL_SET_NAME = \"browser_tool_set\";\nconst TASK_TOOL_SET_NAME = \"task_tool_set\";\n\nfunction browserToolsEnabled() {\n return import.meta.env.VITE_ENABLE_BROWSER_TOOLS !== \"false\";\n}\n\n/**\n * Shape of `VITE_RUNTIME_SERVICES_INFO` (set by the dev launchers in\n * scripts/dev-*.mjs). All URLs are written from the agent's point of view,\n * not the browser's. The block is rendered into the agent's system prompt\n * via `AgentContext.system_message_suffix` so the agent knows what's\n * reachable from inside its sandbox without having to probe.\n */\ninterface RuntimeServicesInfo {\n mode?: string;\n agent_host_alias?: string;\n services?: {\n agent_server?: { description?: string; url_from_agent?: string };\n ingress?: { description?: string; url_from_agent?: string };\n frontend?: {\n kind?: \"vite\" | \"static\";\n description?: string;\n url_from_agent?: string;\n };\n // `vite` is the legacy key name for the frontend entry, accepted for\n // one release while older dev-stack launchers may still emit it.\n vite?: { description?: string; url_from_agent?: string };\n automation?: {\n description?: string;\n url_from_agent?: string;\n api_prefix?: string;\n docs_url?: string;\n openapi_url?: string;\n auth_env_var?: string;\n };\n };\n}\n\nfunction parseRuntimeServicesInfo(): RuntimeServicesInfo | null {\n const raw = import.meta.env.VITE_RUNTIME_SERVICES_INFO?.trim();\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as RuntimeServicesInfo;\n if (!parsed || typeof parsed !== \"object\") return null;\n return parsed;\n } catch {\n // Malformed JSON: ignore and fall back to no runtime info, rather than\n // tearing down conversation creation over a misconfigured dev env var.\n return null;\n }\n}\n\n/**\n * Render the runtime services info into a markdown block suitable for\n * appending to the system prompt via `AgentContext.system_message_suffix`.\n *\n * Returns `undefined` when no runtime info is configured, so callers can\n * safely omit the field on production builds (where the launcher doesn't\n * set `VITE_RUNTIME_SERVICES_INFO`).\n */\nexport function buildRuntimeServicesSystemSuffix(): string | undefined {\n const info = parseRuntimeServicesInfo();\n if (!info?.services) return undefined;\n\n const lines: string[] = [];\n lines.push(\"<RUNTIME_SERVICES>\");\n if (info.mode) {\n lines.push(\n `You are running inside an agent-canvas dev stack started in '${info.mode}' mode.`,\n );\n } else {\n lines.push(\"You are running inside an agent-canvas dev stack.\");\n }\n lines.push(\n \"The following services are reachable from your sandbox. URLs are written\",\n \"from your point of view (i.e., as you should curl/fetch them).\",\n \"\",\n );\n\n const { agent_server, ingress, automation } = info.services;\n // Accept `frontend` (current key) or `vite` (legacy key) for the\n // frontend service entry. The legacy fallback can be removed once all\n // launchers in this repo emit `frontend`.\n const frontend = info.services.frontend ?? info.services.vite;\n\n if (agent_server?.url_from_agent) {\n lines.push(\n `* Agent Server (you): ${agent_server.url_from_agent}`,\n ` ${agent_server.description ?? \"The agent-server hosting your tool calls.\"}`,\n );\n }\n if (ingress?.url_from_agent) {\n lines.push(\n `* Ingress: ${ingress.url_from_agent}`,\n ` ${ingress.description ?? \"Unified entry point for browser-facing traffic.\"}`,\n );\n }\n if (frontend?.url_from_agent) {\n lines.push(\n `* Frontend: ${frontend.url_from_agent}`,\n ` ${frontend.description ?? \"Frontend dev server.\"}`,\n );\n }\n if (automation?.url_from_agent) {\n lines.push(\n `* Automation backend: ${automation.url_from_agent}`,\n ` ${automation.description ?? \"OpenHands Automations service.\"}`,\n );\n if (automation.docs_url) {\n lines.push(` Docs: ${automation.docs_url}`);\n }\n if (automation.openapi_url) {\n lines.push(` OpenAPI: ${automation.openapi_url}`);\n }\n if (automation.auth_env_var) {\n lines.push(\n ` Auth: header 'X-API-Key: $${automation.auth_env_var}'`,\n );\n }\n } else {\n lines.push(\n \"* Automation backend: not running in this dev mode (skip /api/automation calls).\",\n );\n }\n\n // Anchor the \"don't guess\" warning to the actual agent-server URL for\n // this stack instead of a hardcoded port. The agent-server listens on\n // different ports across dev modes, and baking the wrong port into the\n // system prompt is exactly the kind of confusion this block is meant to\n // prevent.\n const agentServerUrl = agent_server?.url_from_agent;\n lines.push(\n \"\",\n \"Trust this block over guessing: do not assume any other URLs are running.\",\n );\n if (agentServerUrl) {\n lines.push(\n `In particular, ${agentServerUrl} inside your sandbox is the Agent Server`,\n \"you are running inside of — NOT the automation backend.\",\n );\n }\n lines.push(\"</RUNTIME_SERVICES>\");\n\n return lines.join(\"\\n\");\n}\n\nexport function toConversationUrl(conversationId: string): string {\n // Local-format conversation URL — points at whichever local agent-server\n // is actually serving the conversation (the bundled one when the active\n // selection is cloud).\n const { host } = getAgentServerClientOptions();\n return `${host}/api/conversations/${conversationId}`;\n}\n\n// TODO(i18n): extract \"Conversation\" once we add CONVERSATION$DEFAULT_TITLE\n// with `{{shortId}}` interpolation. Kept as a literal for now to keep the\n// fallback inside this pure adapter rather than fanning out to display sites.\nexport function getDefaultConversationTitle(conversationId: string): string {\n return `Conversation ${conversationId.slice(0, 5)}`;\n}\n\nexport function toAppConversation(\n info: DirectConversationInfo,\n): AppConversation {\n const metadata = getStoredConversationMetadata(info.id);\n // ACPAgent conversations carry a sentinel ``llm`` on older SDKs. Prefer the\n // runtime model fields when available, then the configured ``acp_model`` that\n // Canvas saves for built-in providers. ``agent_kind`` still gates model\n // switching, so surfacing this string is display-only.\n const isAcp = info.agent?.kind === \"ACPAgent\";\n // Only surface ``acp_server`` for ACP conversations even if the wire\n // payload accidentally carries an ``acpserver`` tag on an OpenHands\n // conversation — the chip is identity info for the ACP CLI subprocess,\n // and showing it on a non-ACP conversation would be a lie.\n const acpServer = isAcp ? (info.tags?.[ACP_SERVER_TAG_KEY] ?? null) : null;\n return {\n id: info.id,\n created_by_user_id: null,\n selected_repository: metadata?.selected_repository ?? null,\n selected_branch: metadata?.selected_branch ?? null,\n git_provider: metadata?.git_provider ?? null,\n selected_workspace: metadata?.selected_workspace ?? null,\n title: info.title?.trim()\n ? info.title\n : getDefaultConversationTitle(info.id),\n trigger: null,\n pr_number: [],\n agent_kind: isAcp ? \"acp\" : \"openhands\",\n acp_server: acpServer,\n // Chip path: no ``providerDefault`` — the chip must distinguish\n // \"no concrete model\" (fall back to the provider display name in\n // ConversationCardFooter) from \"default\" (would lie about what's\n // running on the subprocess).\n llm_model: isAcp\n ? resolveEffectiveAcpModel({\n runtimeName: info.current_model_name,\n runtimeId: info.current_model_id,\n configured: info.agent?.acp_model,\n sdkLlm: info.agent?.llm?.model,\n })\n : (info.agent?.llm?.model ?? DEFAULT_SETTINGS.llm_model),\n metrics: info.metrics\n ? {\n accumulated_cost: info.metrics.accumulated_cost ?? null,\n max_budget_per_task: info.metrics.max_budget_per_task ?? null,\n accumulated_token_usage: info.metrics.accumulated_token_usage\n ? {\n prompt_tokens:\n info.metrics.accumulated_token_usage.prompt_tokens ?? 0,\n completion_tokens:\n info.metrics.accumulated_token_usage.completion_tokens ?? 0,\n cache_read_tokens:\n info.metrics.accumulated_token_usage.cache_read_tokens ?? 0,\n cache_write_tokens:\n info.metrics.accumulated_token_usage.cache_write_tokens ?? 0,\n context_window:\n info.metrics.accumulated_token_usage.context_window ?? 0,\n per_turn_token:\n info.metrics.accumulated_token_usage.per_turn_token ?? 0,\n }\n : null,\n }\n : null,\n created_at: info.created_at,\n updated_at: info.updated_at,\n execution_status:\n (info.execution_status as AppConversation[\"execution_status\"]) ??\n ExecutionStatus.IDLE,\n sandbox_status: (info.sandbox_status as SandboxStatus | null) ?? null,\n conversation_url: toConversationUrl(info.id),\n session_api_key: getAgentServerClientOptions().apiKey ?? null,\n sandbox_id: null,\n workspace: {\n working_dir: info.workspace?.working_dir ?? getAgentServerWorkingDir(),\n },\n public: false,\n sub_conversation_ids: [],\n };\n}\n\nexport function toConversationPage(data: {\n items: DirectConversationInfo[];\n next_page_id?: string | null;\n}): AppConversationPage {\n return {\n items: data.items.map(toAppConversation),\n next_page_id: data.next_page_id ?? null,\n };\n}\n\ntype SettingsRecord = Record<string, unknown>;\n\ninterface AgentToolSpec {\n name: string;\n params: SettingsRecord;\n}\n\ntype AgentSettingsPayload = SettingsRecord & {\n llm?: SettingsRecord;\n agent_context: SettingsRecord;\n tools?: AgentToolSpec[];\n};\n\ninterface LocalWorkspacePayload {\n kind: \"LocalWorkspace\";\n working_dir: string;\n}\n\ninterface InitialMessagePayload {\n role: \"user\";\n content: Array<{ type: \"text\"; text: string }>;\n run: true;\n}\n\ntype ConversationSettingsPayload = SettingsRecord & {\n workspace: LocalWorkspacePayload;\n initial_message?: InitialMessagePayload;\n};\n\nconst ACP_SETTINGS_KEYS = [\n \"acp_command\",\n \"acp_args\",\n \"acp_env\",\n \"acp_model\",\n \"acp_session_mode\",\n \"acp_prompt_timeout\",\n] as const;\n\nexport const ACP_SERVER_TAG_KEY = \"acpserver\";\n\nconst CONVERSATION_SETTINGS_METADATA_KEYS = new Set([\n \"schema_version\",\n \"agent_settings\",\n \"workspace\",\n \"conversation_id\",\n \"initial_message\",\n \"plugins\",\n]);\n\nfunction toRecord(value: unknown): SettingsRecord {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n return {};\n }\n\n return structuredClone(value as SettingsRecord);\n}\n\nfunction normalizeSecretString(value: unknown): string | undefined {\n if (typeof value !== \"string\") {\n return undefined;\n }\n\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction getConversationConfirmationPolicy(\n conversationSettings: SettingsRecord,\n) {\n if (conversationSettings.confirmation_mode !== true) {\n return { kind: \"NeverConfirm\" };\n }\n\n if (conversationSettings.security_analyzer === \"llm\") {\n return { kind: \"ConfirmRisky\", threshold: \"HIGH\", confirm_unknown: true };\n }\n\n return { kind: \"AlwaysConfirm\" };\n}\n\nfunction getConversationSecurityAnalyzer(conversationSettings: SettingsRecord) {\n switch (conversationSettings.security_analyzer) {\n case \"llm\":\n return { kind: \"LLMSecurityAnalyzer\" };\n case \"pattern\":\n return { kind: \"PatternSecurityAnalyzer\" };\n case \"policy_rail\":\n return { kind: \"PolicyRailSecurityAnalyzer\" };\n default:\n return undefined;\n }\n}\n\nfunction isToolRecord(\n value: unknown,\n): value is { name: string; params?: unknown } {\n return (\n !!value &&\n typeof value === \"object\" &&\n !Array.isArray(value) &&\n typeof (value as { name?: unknown }).name === \"string\"\n );\n}\n\nfunction shouldIncludeTool(name: string, agentSettings: SettingsRecord) {\n if (name === BROWSER_TOOL_SET_NAME) {\n return browserToolsEnabled() && isAgentServerToolAvailable(name);\n }\n\n if (name === TASK_TOOL_SET_NAME) {\n return (\n agentSettings.enable_sub_agents === true &&\n isAgentServerToolAvailable(name)\n );\n }\n\n return true;\n}\n\nfunction getAgentTools(agentSettings: SettingsRecord): AgentToolSpec[] {\n const tools = new Map<string, AgentToolSpec>();\n\n for (const name of DEFAULT_TOOL_NAMES) {\n tools.set(name, { name, params: {} });\n }\n\n for (const name of [BROWSER_TOOL_SET_NAME, TASK_TOOL_SET_NAME]) {\n if (shouldIncludeTool(name, agentSettings)) {\n tools.set(name, { name, params: {} });\n }\n }\n\n const configuredTools = agentSettings.tools;\n if (\n Array.isArray(configuredTools) &&\n configuredTools.every((tool) => isToolRecord(tool))\n ) {\n for (const tool of configuredTools) {\n if (shouldIncludeTool(tool.name, agentSettings)) {\n tools.set(tool.name, {\n name: tool.name,\n params: toRecord(tool.params),\n });\n }\n }\n }\n\n return Array.from(tools.values());\n}\n\nfunction buildInitialMessage(\n query?: string,\n conversationInstructions?: string,\n): InitialMessagePayload | null {\n const parts = [query?.trim(), conversationInstructions?.trim()].filter(\n Boolean,\n );\n if (parts.length === 0) {\n return null;\n }\n\n return {\n role: \"user\",\n content: [{ type: \"text\", text: parts.join(\"\\n\\n\") }],\n run: true,\n };\n}\n\nfunction buildAgentContext(agentSettings: SettingsRecord): SettingsRecord {\n const runtimeServicesSuffix = buildRuntimeServicesSystemSuffix();\n return {\n ...toRecord(agentSettings.agent_context),\n load_public_skills: shouldLoadPublicSkills(),\n load_user_skills: true,\n load_project_skills: true,\n ...(runtimeServicesSuffix\n ? { system_message_suffix: runtimeServicesSuffix }\n : {}),\n };\n}\n\nfunction isAcpAgent(settings: Settings): boolean {\n const agentSettings = toRecord(settings.agent_settings);\n return agentSettings.agent_kind === \"acp\";\n}\n\nfunction getAcpServerTag(settings: Settings): string | undefined {\n const agentSettings = toRecord(settings.agent_settings);\n const value = agentSettings.acp_server;\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction resolveAcpCommand(agentSettings: SettingsRecord): unknown {\n const cmd = agentSettings.acp_command;\n const isEmpty = Array.isArray(cmd) && cmd.length === 0;\n const noCommand = cmd === undefined;\n if (!isEmpty && !noCommand) {\n return cmd;\n }\n\n const serverKey =\n typeof agentSettings.acp_server === \"string\"\n ? agentSettings.acp_server\n : undefined;\n const provider = getAcpProvider(serverKey);\n return provider ? [...provider.default_command] : cmd;\n}\n\nfunction buildConfiguredAcpAgentSettings(\n settings: Settings,\n): AgentSettingsPayload {\n const agentSettings = toRecord(settings.agent_settings);\n const payload: AgentSettingsPayload = {\n agent_kind: \"acp\",\n agent_context: buildAgentContext(agentSettings),\n };\n\n for (const key of ACP_SETTINGS_KEYS) {\n // ``acp_model`` is resolved separately below so a saved ``null`` still\n // falls back to the provider's default rather than being dropped.\n if (key === \"acp_model\") continue;\n const value =\n key === \"acp_command\"\n ? resolveAcpCommand(agentSettings)\n : agentSettings[key];\n if (value !== undefined && value !== null) {\n payload[key] = value;\n }\n }\n\n // Saved settings may carry ``acp_model: null`` (existing users predating\n // the default-model registry, or saved fields the agent-server stripped).\n // Fall back to the provider's ``default_model`` so the conversation starts\n // with whatever the Settings → Agent UI shows — without that, the form's\n // displayed default would silently not take effect at runtime until the\n // user re-saved the page.\n const serverKey =\n typeof agentSettings.acp_server === \"string\"\n ? agentSettings.acp_server\n : undefined;\n const provider = getAcpProvider(serverKey);\n const effectiveModel = resolveEffectiveAcpModel({\n configured: agentSettings.acp_model as string | null | undefined,\n providerDefault: provider?.default_model,\n });\n if (effectiveModel) {\n payload.acp_model = effectiveModel;\n }\n\n return payload;\n}\n\nfunction buildConfiguredOpenHandsAgentSettings(\n settings: Settings,\n): AgentSettingsPayload {\n const agentSettings = toRecord(settings.agent_settings);\n const llm = toRecord(agentSettings.llm);\n\n llm.model =\n typeof llm.model === \"string\" && llm.model.trim().length > 0\n ? llm.model\n : DEFAULT_SETTINGS.llm_model;\n\n const apiKey = normalizeSecretString(llm.api_key);\n if (apiKey) {\n llm.api_key = apiKey;\n } else {\n delete llm.api_key;\n }\n\n const baseUrl = normalizeSecretString(llm.base_url);\n if (baseUrl) {\n llm.base_url = baseUrl;\n } else {\n delete llm.base_url;\n }\n\n const mcpConfig = toRecord(agentSettings.mcp_config);\n if (Object.keys(mcpConfig).length === 0 || !(\"mcpServers\" in mcpConfig)) {\n delete agentSettings.mcp_config;\n }\n\n delete agentSettings.acp_server;\n for (const key of ACP_SETTINGS_KEYS) {\n delete agentSettings[key];\n }\n\n return {\n ...agentSettings,\n llm,\n agent_context: buildAgentContext(agentSettings),\n tools: getAgentTools(agentSettings),\n };\n}\n\nfunction buildConfiguredAgentSettings(\n settings: Settings,\n): AgentSettingsPayload {\n return isAcpAgent(settings)\n ? buildConfiguredAcpAgentSettings(settings)\n : buildConfiguredOpenHandsAgentSettings(settings);\n}\n\nfunction buildConfiguredConversationSettings(options: {\n settings: Settings;\n query?: string;\n conversationInstructions?: string;\n plugins?: PluginSpec[];\n workingDir?: string;\n}): ConversationSettingsPayload {\n const { settings, query, conversationInstructions, plugins, workingDir } =\n options;\n const conversationSettings = toRecord(settings.conversation_settings);\n const initialMessage = buildInitialMessage(query, conversationInstructions);\n\n CONVERSATION_SETTINGS_METADATA_KEYS.forEach(\n (key) => delete conversationSettings[key],\n );\n\n const payload: ConversationSettingsPayload = {\n ...conversationSettings,\n workspace: {\n kind: \"LocalWorkspace\",\n working_dir: workingDir ?? getAgentServerWorkingDir(),\n },\n ...(initialMessage ? { initial_message: initialMessage } : {}),\n ...(plugins?.length\n ? {\n plugins: plugins.map((plugin) => ({\n source: plugin.source,\n ...(plugin.ref ? { ref: plugin.ref } : {}),\n ...(plugin.repo_path ? { repo_path: plugin.repo_path } : {}),\n })),\n }\n : {}),\n };\n\n return payload;\n}\n\ninterface LookupSecret {\n kind: \"LookupSecret\";\n url: string;\n headers?: Record<string, string>;\n description?: string;\n}\n\ntype StartConversationPayload = Record<string, unknown> & {\n agent_settings: AgentSettingsPayload;\n workspace: LocalWorkspacePayload;\n confirmation_policy: SettingsRecord;\n security_analyzer?: SettingsRecord;\n initial_message?: InitialMessagePayload;\n max_iterations: number;\n stuck_detection: true;\n autotitle: true;\n worktree: true;\n secrets_encrypted?: true;\n conversation_id?: string;\n secrets?: Record<string, LookupSecret>;\n tags?: Record<string, string>;\n tool_module_qualnames?: Record<string, string>;\n};\n\nexport interface StartConversationOptions {\n settings: Settings;\n query?: string;\n conversationInstructions?: string;\n plugins?: PluginSpec[];\n conversationId?: string;\n workingDir?: string;\n encryptedAgentSettings?: Record<string, SettingsValue>;\n encryptedConversationSettings?: Record<string, SettingsValue>;\n secretsEncrypted?: boolean;\n customSecrets?: Array<{ name: string; description?: string }>;\n}\n\nexport function buildStartConversationRequest(\n options: StartConversationOptions,\n): StartConversationPayload {\n const sourceAgentSettings = options.encryptedAgentSettings\n ? { ...options.settings, agent_settings: options.encryptedAgentSettings }\n : options.settings;\n\n const acpMode = isAcpAgent(sourceAgentSettings);\n const agentSettings = buildConfiguredAgentSettings(sourceAgentSettings);\n const acpServerTag = acpMode\n ? getAcpServerTag(sourceAgentSettings)\n : undefined;\n\n const sourceConversationOptions = options.encryptedConversationSettings\n ? {\n ...options,\n settings: {\n ...options.settings,\n conversation_settings: options.encryptedConversationSettings,\n },\n }\n : options;\n\n const conversationSettings = buildConfiguredConversationSettings(\n sourceConversationOptions,\n );\n\n const payload: StartConversationPayload = {\n agent_settings: agentSettings,\n workspace: conversationSettings.workspace,\n confirmation_policy:\n getConversationConfirmationPolicy(conversationSettings),\n max_iterations:\n typeof conversationSettings.max_iterations === \"number\"\n ? conversationSettings.max_iterations\n : 500,\n stuck_detection: true,\n autotitle: true,\n worktree: true,\n };\n\n if (acpServerTag) {\n payload.tags = { [ACP_SERVER_TAG_KEY]: acpServerTag };\n }\n\n if (options.secretsEncrypted) {\n payload.secrets_encrypted = true;\n }\n\n if (options.conversationId) {\n payload.conversation_id = options.conversationId;\n }\n\n const securityAnalyzer =\n getConversationSecurityAnalyzer(conversationSettings);\n if (securityAnalyzer) {\n payload.security_analyzer = securityAnalyzer;\n }\n\n if (conversationSettings.initial_message) {\n payload.initial_message = conversationSettings.initial_message;\n }\n\n if (conversationSettings.plugins) {\n payload.plugins = conversationSettings.plugins;\n }\n\n if (conversationSettings.hook_config) {\n payload.hook_config = conversationSettings.hook_config;\n }\n\n payload.tool_module_qualnames = {\n [CANVAS_UI_TOOL_NAME]: CANVAS_UI_TOOL_MODULE,\n ...((conversationSettings.tool_module_qualnames as\n | Record<string, string>\n | undefined) ?? {}),\n };\n\n if (conversationSettings.agent_definitions) {\n payload.agent_definitions = conversationSettings.agent_definitions;\n }\n\n if (options.customSecrets && options.customSecrets.length > 0) {\n const backend = getEffectiveLocalBackend();\n const headers = buildAuthHeaders(backend);\n\n const secrets: Record<string, LookupSecret> = {};\n for (const secret of options.customSecrets) {\n const lookupSecret: LookupSecret = {\n kind: \"LookupSecret\",\n url: `/api/settings/secrets/${encodeURIComponent(secret.name)}`,\n description: secret.description,\n };\n\n if (Object.keys(headers).length > 0) {\n lookupSecret.headers = headers;\n }\n\n secrets[secret.name] = lookupSecret;\n }\n\n payload.secrets = secrets;\n\n if (acpMode) {\n payload.agent_settings.agent_context = {\n ...payload.agent_settings.agent_context,\n secrets,\n };\n }\n }\n\n return payload;\n}\n\nexport async function buildStartConversationRequestWithEncryptedSettings(options: {\n settings: Settings;\n query?: string;\n conversationInstructions?: string;\n plugins?: PluginSpec[];\n conversationId?: string;\n workingDir?: string;\n}): Promise<Record<string, unknown>> {\n const { SecretsService } = await import(\"./secrets-service\");\n\n const [settingsResult, customSecrets] = await Promise.all([\n SettingsService.getSettingsForConversation(),\n SecretsService.getSecrets(),\n ]);\n\n const { agentSettings, conversationSettings, secretsEncrypted } =\n settingsResult;\n\n return buildStartConversationRequest({\n ...options,\n encryptedAgentSettings: agentSettings,\n encryptedConversationSettings: conversationSettings,\n secretsEncrypted,\n customSecrets,\n });\n}\n\nexport function emptyHooksResponse(): GetHooksResponse {\n return { hooks: [] };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"agent-server-adapter.cjs","names":[],"sources":["../../src/api/agent-server-adapter.ts"],"sourcesContent":["import { DEFAULT_SETTINGS } from \"#/services/settings\";\nimport { ExecutionStatus } from \"#/types/agent-server/core\";\nimport { Settings, SettingsValue } from \"#/types/settings\";\nimport {\n getAcpProvider,\n resolveEffectiveAcpModel,\n} from \"#/constants/acp-providers\";\nimport { getAgentServerClientOptions } from \"./agent-server-client-options\";\nimport { isAgentServerToolAvailable } from \"./agent-server-compatibility\";\nimport {\n getAgentServerWorkingDir,\n shouldLoadPublicSkills,\n} from \"./agent-server-config\";\nimport { getEffectiveLocalBackend } from \"./backend-registry/active-store\";\nimport { buildAuthHeaders } from \"./backend-registry/auth\";\nimport {\n GetHooksResponse,\n PluginSpec,\n AppConversation,\n AppConversationPage,\n SandboxStatus,\n} from \"./conversation-service/agent-server-conversation-service.types\";\nimport SettingsService from \"./settings-service/settings-service.api\";\nimport { getStoredConversationMetadata } from \"./conversation-metadata-store\";\n\nexport interface DirectConversationInfo {\n id: string;\n title?: string | null;\n created_at: string;\n updated_at: string;\n execution_status?: string | null;\n /** Cloud-only sandbox lifecycle state. Omitted / null for local agent-server conversations. */\n sandbox_status?: string | null;\n metrics?: {\n accumulated_cost?: number | null;\n max_budget_per_task?: number | null;\n accumulated_token_usage?: {\n prompt_tokens?: number;\n completion_tokens?: number;\n cache_read_tokens?: number;\n cache_write_tokens?: number;\n context_window?: number;\n per_turn_token?: number;\n } | null;\n } | null;\n agent?: {\n /**\n * Pydantic discriminator from the SDK union: ``\"ACPAgent\"`` for ACP CLI\n * subprocesses (model lives on the subprocess via ``acp_model``),\n * ``\"Agent\"`` for direct litellm. Read by {@link toAppConversation}.\n */\n kind?: string | null;\n acp_model?: string | null;\n llm?: {\n model?: string | null;\n } | null;\n } | null;\n current_model_id?: string | null;\n current_model_name?: string | null;\n workspace?: {\n working_dir?: string | null;\n } | null;\n /**\n * Arbitrary string-keyed conversation tags surfaced by the agent-server\n * (see ``ConversationInfo.tags``). Canvas only consumes one key today —\n * ``ACP_SERVER_TAG_KEY`` (\"acpserver\") — but the field is typed as a\n * generic record so future readers don't need another wire-shape change.\n * Keys are constrained to ``^[a-z0-9]+$`` by the agent-server validator;\n * values are opaque strings.\n */\n tags?: Record<string, string> | null;\n}\n\n// Module qualname for the Canvas-UI tool. The agent-server imports this via\n// tool_module_qualnames; the host directory is exposed via OH_EXTRA_PYTHON_PATH\n// (see scripts/dev-safe.mjs).\nconst CANVAS_UI_TOOL_NAME = \"canvas_ui\";\nconst CANVAS_UI_TOOL_MODULE = \"canvas_ui_tool\";\n\nconst DEFAULT_TOOL_NAMES = [\n \"terminal\",\n \"file_editor\",\n \"task_tracker\",\n CANVAS_UI_TOOL_NAME,\n];\nconst BROWSER_TOOL_SET_NAME = \"browser_tool_set\";\nconst TASK_TOOL_SET_NAME = \"task_tool_set\";\n\nfunction browserToolsEnabled() {\n return import.meta.env.VITE_ENABLE_BROWSER_TOOLS !== \"false\";\n}\n\n/**\n * Shape of `VITE_RUNTIME_SERVICES_INFO` (set by the dev launchers in\n * scripts/dev-*.mjs). All URLs are written from the agent's point of view,\n * not the browser's. The block is rendered into the agent's system prompt\n * via `AgentContext.system_message_suffix` so the agent knows what's\n * reachable from inside its sandbox without having to probe.\n */\ninterface RuntimeServicesInfo {\n mode?: string;\n agent_host_alias?: string;\n services?: {\n agent_server?: { description?: string; url_from_agent?: string };\n ingress?: { description?: string; url_from_agent?: string };\n frontend?: {\n kind?: \"vite\" | \"static\";\n description?: string;\n url_from_agent?: string;\n };\n // `vite` is the legacy key name for the frontend entry, accepted for\n // one release while older dev-stack launchers may still emit it.\n vite?: { description?: string; url_from_agent?: string };\n automation?: {\n description?: string;\n url_from_agent?: string;\n api_prefix?: string;\n docs_url?: string;\n openapi_url?: string;\n auth_env_var?: string;\n };\n };\n}\n\nfunction parseRuntimeServicesInfo(): RuntimeServicesInfo | null {\n const raw = import.meta.env.VITE_RUNTIME_SERVICES_INFO?.trim();\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as RuntimeServicesInfo;\n if (!parsed || typeof parsed !== \"object\") return null;\n return parsed;\n } catch {\n // Malformed JSON: ignore and fall back to no runtime info, rather than\n // tearing down conversation creation over a misconfigured dev env var.\n return null;\n }\n}\n\n/**\n * Render the runtime services info into a markdown block suitable for\n * appending to the system prompt via `AgentContext.system_message_suffix`.\n *\n * Returns `undefined` when no runtime info is configured, so callers can\n * safely omit the field on production builds (where the launcher doesn't\n * set `VITE_RUNTIME_SERVICES_INFO`).\n */\nexport function buildRuntimeServicesSystemSuffix(): string | undefined {\n const info = parseRuntimeServicesInfo();\n if (!info?.services) return undefined;\n\n const lines: string[] = [];\n lines.push(\"<RUNTIME_SERVICES>\");\n if (info.mode) {\n lines.push(\n `You are running inside an agent-canvas dev stack started in '${info.mode}' mode.`,\n );\n } else {\n lines.push(\"You are running inside an agent-canvas dev stack.\");\n }\n lines.push(\n \"The following services are reachable from your sandbox. URLs are written\",\n \"from your point of view (i.e., as you should curl/fetch them).\",\n \"\",\n );\n\n const { agent_server, ingress, automation } = info.services;\n // Accept `frontend` (current key) or `vite` (legacy key) for the\n // frontend service entry. The legacy fallback can be removed once all\n // launchers in this repo emit `frontend`.\n const frontend = info.services.frontend ?? info.services.vite;\n\n if (agent_server?.url_from_agent) {\n lines.push(\n `* Agent Server (you): ${agent_server.url_from_agent}`,\n ` ${agent_server.description ?? \"The agent-server hosting your tool calls.\"}`,\n );\n }\n if (ingress?.url_from_agent) {\n lines.push(\n `* Ingress: ${ingress.url_from_agent}`,\n ` ${ingress.description ?? \"Unified entry point for browser-facing traffic.\"}`,\n );\n }\n if (frontend?.url_from_agent) {\n lines.push(\n `* Frontend: ${frontend.url_from_agent}`,\n ` ${frontend.description ?? \"Frontend dev server.\"}`,\n );\n }\n if (automation?.url_from_agent) {\n lines.push(\n `* Automation backend: ${automation.url_from_agent}`,\n ` ${automation.description ?? \"OpenHands Automations service.\"}`,\n );\n if (automation.docs_url) {\n lines.push(` Docs: ${automation.docs_url}`);\n }\n if (automation.openapi_url) {\n lines.push(` OpenAPI: ${automation.openapi_url}`);\n }\n if (automation.auth_env_var) {\n // X-Session-API-Key is the local convention shared by the agent-server\n // and automation backend (see openhands-automation auth.py).\n lines.push(\n ` Auth: header 'X-Session-API-Key: $${automation.auth_env_var}'`,\n );\n }\n } else {\n lines.push(\n \"* Automation backend: not running in this dev mode (skip /api/automation calls).\",\n );\n }\n\n // Anchor the \"don't guess\" warning to the actual agent-server URL for\n // this stack instead of a hardcoded port. The agent-server listens on\n // different ports across dev modes, and baking the wrong port into the\n // system prompt is exactly the kind of confusion this block is meant to\n // prevent.\n const agentServerUrl = agent_server?.url_from_agent;\n lines.push(\n \"\",\n \"Trust this block over guessing: do not assume any other URLs are running.\",\n );\n if (agentServerUrl) {\n lines.push(\n `In particular, ${agentServerUrl} inside your sandbox is the Agent Server`,\n \"you are running inside of — NOT the automation backend.\",\n );\n }\n lines.push(\"</RUNTIME_SERVICES>\");\n\n return lines.join(\"\\n\");\n}\n\nexport function toConversationUrl(conversationId: string): string {\n // Local-format conversation URL — points at whichever local agent-server\n // is actually serving the conversation (the bundled one when the active\n // selection is cloud).\n const { host } = getAgentServerClientOptions();\n return `${host}/api/conversations/${conversationId}`;\n}\n\n// TODO(i18n): extract \"Conversation\" once we add CONVERSATION$DEFAULT_TITLE\n// with `{{shortId}}` interpolation. Kept as a literal for now to keep the\n// fallback inside this pure adapter rather than fanning out to display sites.\nexport function getDefaultConversationTitle(conversationId: string): string {\n return `Conversation ${conversationId.slice(0, 5)}`;\n}\n\nexport function toAppConversation(\n info: DirectConversationInfo,\n): AppConversation {\n const metadata = getStoredConversationMetadata(info.id);\n // ACPAgent conversations carry a sentinel ``llm`` on older SDKs. Prefer the\n // runtime model fields when available, then the configured ``acp_model`` that\n // Canvas saves for built-in providers. ``agent_kind`` still gates model\n // switching, so surfacing this string is display-only.\n const isAcp = info.agent?.kind === \"ACPAgent\";\n // Only surface ``acp_server`` for ACP conversations even if the wire\n // payload accidentally carries an ``acpserver`` tag on an OpenHands\n // conversation — the chip is identity info for the ACP CLI subprocess,\n // and showing it on a non-ACP conversation would be a lie.\n const acpServer = isAcp ? (info.tags?.[ACP_SERVER_TAG_KEY] ?? null) : null;\n return {\n id: info.id,\n created_by_user_id: null,\n selected_repository: metadata?.selected_repository ?? null,\n selected_branch: metadata?.selected_branch ?? null,\n git_provider: metadata?.git_provider ?? null,\n selected_workspace: metadata?.selected_workspace ?? null,\n title: info.title?.trim()\n ? info.title\n : getDefaultConversationTitle(info.id),\n trigger: null,\n pr_number: [],\n agent_kind: isAcp ? \"acp\" : \"openhands\",\n acp_server: acpServer,\n // Chip path: no ``providerDefault`` — the chip must distinguish\n // \"no concrete model\" (fall back to the provider display name in\n // ConversationCardFooter) from \"default\" (would lie about what's\n // running on the subprocess).\n llm_model: isAcp\n ? resolveEffectiveAcpModel({\n runtimeName: info.current_model_name,\n runtimeId: info.current_model_id,\n configured: info.agent?.acp_model,\n sdkLlm: info.agent?.llm?.model,\n })\n : (info.agent?.llm?.model ?? DEFAULT_SETTINGS.llm_model),\n metrics: info.metrics\n ? {\n accumulated_cost: info.metrics.accumulated_cost ?? null,\n max_budget_per_task: info.metrics.max_budget_per_task ?? null,\n accumulated_token_usage: info.metrics.accumulated_token_usage\n ? {\n prompt_tokens:\n info.metrics.accumulated_token_usage.prompt_tokens ?? 0,\n completion_tokens:\n info.metrics.accumulated_token_usage.completion_tokens ?? 0,\n cache_read_tokens:\n info.metrics.accumulated_token_usage.cache_read_tokens ?? 0,\n cache_write_tokens:\n info.metrics.accumulated_token_usage.cache_write_tokens ?? 0,\n context_window:\n info.metrics.accumulated_token_usage.context_window ?? 0,\n per_turn_token:\n info.metrics.accumulated_token_usage.per_turn_token ?? 0,\n }\n : null,\n }\n : null,\n created_at: info.created_at,\n updated_at: info.updated_at,\n execution_status:\n (info.execution_status as AppConversation[\"execution_status\"]) ??\n ExecutionStatus.IDLE,\n sandbox_status: (info.sandbox_status as SandboxStatus | null) ?? null,\n conversation_url: toConversationUrl(info.id),\n session_api_key: getAgentServerClientOptions().apiKey ?? null,\n sandbox_id: null,\n workspace: {\n working_dir: info.workspace?.working_dir ?? getAgentServerWorkingDir(),\n },\n public: false,\n sub_conversation_ids: [],\n };\n}\n\nexport function toConversationPage(data: {\n items: DirectConversationInfo[];\n next_page_id?: string | null;\n}): AppConversationPage {\n return {\n items: data.items.map(toAppConversation),\n next_page_id: data.next_page_id ?? null,\n };\n}\n\ntype SettingsRecord = Record<string, unknown>;\n\ninterface AgentToolSpec {\n name: string;\n params: SettingsRecord;\n}\n\ntype AgentSettingsPayload = SettingsRecord & {\n llm?: SettingsRecord;\n agent_context: SettingsRecord;\n tools?: AgentToolSpec[];\n};\n\ninterface LocalWorkspacePayload {\n kind: \"LocalWorkspace\";\n working_dir: string;\n}\n\ninterface InitialMessagePayload {\n role: \"user\";\n content: Array<{ type: \"text\"; text: string }>;\n run: true;\n}\n\ntype ConversationSettingsPayload = SettingsRecord & {\n workspace: LocalWorkspacePayload;\n initial_message?: InitialMessagePayload;\n};\n\nconst ACP_SETTINGS_KEYS = [\n \"acp_command\",\n \"acp_args\",\n \"acp_env\",\n \"acp_model\",\n \"acp_session_mode\",\n \"acp_prompt_timeout\",\n] as const;\n\nexport const ACP_SERVER_TAG_KEY = \"acpserver\";\n\nconst CONVERSATION_SETTINGS_METADATA_KEYS = new Set([\n \"schema_version\",\n \"agent_settings\",\n \"workspace\",\n \"conversation_id\",\n \"initial_message\",\n \"plugins\",\n]);\n\nfunction toRecord(value: unknown): SettingsRecord {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n return {};\n }\n\n return structuredClone(value as SettingsRecord);\n}\n\nfunction normalizeSecretString(value: unknown): string | undefined {\n if (typeof value !== \"string\") {\n return undefined;\n }\n\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction getConversationConfirmationPolicy(\n conversationSettings: SettingsRecord,\n) {\n if (conversationSettings.confirmation_mode !== true) {\n return { kind: \"NeverConfirm\" };\n }\n\n if (conversationSettings.security_analyzer === \"llm\") {\n return { kind: \"ConfirmRisky\", threshold: \"HIGH\", confirm_unknown: true };\n }\n\n return { kind: \"AlwaysConfirm\" };\n}\n\nfunction getConversationSecurityAnalyzer(conversationSettings: SettingsRecord) {\n switch (conversationSettings.security_analyzer) {\n case \"llm\":\n return { kind: \"LLMSecurityAnalyzer\" };\n case \"pattern\":\n return { kind: \"PatternSecurityAnalyzer\" };\n case \"policy_rail\":\n return { kind: \"PolicyRailSecurityAnalyzer\" };\n default:\n return undefined;\n }\n}\n\nfunction isToolRecord(\n value: unknown,\n): value is { name: string; params?: unknown } {\n return (\n !!value &&\n typeof value === \"object\" &&\n !Array.isArray(value) &&\n typeof (value as { name?: unknown }).name === \"string\"\n );\n}\n\nfunction shouldIncludeTool(name: string, agentSettings: SettingsRecord) {\n if (name === BROWSER_TOOL_SET_NAME) {\n return browserToolsEnabled() && isAgentServerToolAvailable(name);\n }\n\n if (name === TASK_TOOL_SET_NAME) {\n return (\n agentSettings.enable_sub_agents === true &&\n isAgentServerToolAvailable(name)\n );\n }\n\n return true;\n}\n\nfunction getAgentTools(agentSettings: SettingsRecord): AgentToolSpec[] {\n const tools = new Map<string, AgentToolSpec>();\n\n for (const name of DEFAULT_TOOL_NAMES) {\n tools.set(name, { name, params: {} });\n }\n\n for (const name of [BROWSER_TOOL_SET_NAME, TASK_TOOL_SET_NAME]) {\n if (shouldIncludeTool(name, agentSettings)) {\n tools.set(name, { name, params: {} });\n }\n }\n\n const configuredTools = agentSettings.tools;\n if (\n Array.isArray(configuredTools) &&\n configuredTools.every((tool) => isToolRecord(tool))\n ) {\n for (const tool of configuredTools) {\n if (shouldIncludeTool(tool.name, agentSettings)) {\n tools.set(tool.name, {\n name: tool.name,\n params: toRecord(tool.params),\n });\n }\n }\n }\n\n return Array.from(tools.values());\n}\n\nfunction buildInitialMessage(\n query?: string,\n conversationInstructions?: string,\n): InitialMessagePayload | null {\n const parts = [query?.trim(), conversationInstructions?.trim()].filter(\n Boolean,\n );\n if (parts.length === 0) {\n return null;\n }\n\n return {\n role: \"user\",\n content: [{ type: \"text\", text: parts.join(\"\\n\\n\") }],\n run: true,\n };\n}\n\nfunction buildAgentContext(agentSettings: SettingsRecord): SettingsRecord {\n const runtimeServicesSuffix = buildRuntimeServicesSystemSuffix();\n return {\n ...toRecord(agentSettings.agent_context),\n load_public_skills: shouldLoadPublicSkills(),\n load_user_skills: true,\n load_project_skills: true,\n ...(runtimeServicesSuffix\n ? { system_message_suffix: runtimeServicesSuffix }\n : {}),\n };\n}\n\nfunction isAcpAgent(settings: Settings): boolean {\n const agentSettings = toRecord(settings.agent_settings);\n return agentSettings.agent_kind === \"acp\";\n}\n\nfunction getAcpServerTag(settings: Settings): string | undefined {\n const agentSettings = toRecord(settings.agent_settings);\n const value = agentSettings.acp_server;\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction resolveAcpCommand(agentSettings: SettingsRecord): unknown {\n const cmd = agentSettings.acp_command;\n const isEmpty = Array.isArray(cmd) && cmd.length === 0;\n const noCommand = cmd === undefined;\n if (!isEmpty && !noCommand) {\n return cmd;\n }\n\n const serverKey =\n typeof agentSettings.acp_server === \"string\"\n ? agentSettings.acp_server\n : undefined;\n const provider = getAcpProvider(serverKey);\n return provider ? [...provider.default_command] : cmd;\n}\n\nfunction buildConfiguredAcpAgentSettings(\n settings: Settings,\n): AgentSettingsPayload {\n const agentSettings = toRecord(settings.agent_settings);\n const payload: AgentSettingsPayload = {\n agent_kind: \"acp\",\n agent_context: buildAgentContext(agentSettings),\n };\n\n for (const key of ACP_SETTINGS_KEYS) {\n // ``acp_model`` is resolved separately below so a saved ``null`` still\n // falls back to the provider's default rather than being dropped.\n if (key === \"acp_model\") continue;\n const value =\n key === \"acp_command\"\n ? resolveAcpCommand(agentSettings)\n : agentSettings[key];\n if (value !== undefined && value !== null) {\n payload[key] = value;\n }\n }\n\n // Saved settings may carry ``acp_model: null`` (existing users predating\n // the default-model registry, or saved fields the agent-server stripped).\n // Fall back to the provider's ``default_model`` so the conversation starts\n // with whatever the Settings → Agent UI shows — without that, the form's\n // displayed default would silently not take effect at runtime until the\n // user re-saved the page.\n const serverKey =\n typeof agentSettings.acp_server === \"string\"\n ? agentSettings.acp_server\n : undefined;\n const provider = getAcpProvider(serverKey);\n const effectiveModel = resolveEffectiveAcpModel({\n configured: agentSettings.acp_model as string | null | undefined,\n providerDefault: provider?.default_model,\n });\n if (effectiveModel) {\n payload.acp_model = effectiveModel;\n }\n\n return payload;\n}\n\nfunction buildConfiguredOpenHandsAgentSettings(\n settings: Settings,\n): AgentSettingsPayload {\n const agentSettings = toRecord(settings.agent_settings);\n const llm = toRecord(agentSettings.llm);\n\n llm.model =\n typeof llm.model === \"string\" && llm.model.trim().length > 0\n ? llm.model\n : DEFAULT_SETTINGS.llm_model;\n\n const apiKey = normalizeSecretString(llm.api_key);\n if (apiKey) {\n llm.api_key = apiKey;\n } else {\n delete llm.api_key;\n }\n\n const baseUrl = normalizeSecretString(llm.base_url);\n if (baseUrl) {\n llm.base_url = baseUrl;\n } else {\n delete llm.base_url;\n }\n\n const mcpConfig = toRecord(agentSettings.mcp_config);\n if (Object.keys(mcpConfig).length === 0 || !(\"mcpServers\" in mcpConfig)) {\n delete agentSettings.mcp_config;\n }\n\n delete agentSettings.acp_server;\n for (const key of ACP_SETTINGS_KEYS) {\n delete agentSettings[key];\n }\n\n return {\n ...agentSettings,\n llm,\n agent_context: buildAgentContext(agentSettings),\n tools: getAgentTools(agentSettings),\n };\n}\n\nfunction buildConfiguredAgentSettings(\n settings: Settings,\n): AgentSettingsPayload {\n return isAcpAgent(settings)\n ? buildConfiguredAcpAgentSettings(settings)\n : buildConfiguredOpenHandsAgentSettings(settings);\n}\n\nfunction buildConfiguredConversationSettings(options: {\n settings: Settings;\n query?: string;\n conversationInstructions?: string;\n plugins?: PluginSpec[];\n workingDir?: string;\n}): ConversationSettingsPayload {\n const { settings, query, conversationInstructions, plugins, workingDir } =\n options;\n const conversationSettings = toRecord(settings.conversation_settings);\n const initialMessage = buildInitialMessage(query, conversationInstructions);\n\n CONVERSATION_SETTINGS_METADATA_KEYS.forEach(\n (key) => delete conversationSettings[key],\n );\n\n const payload: ConversationSettingsPayload = {\n ...conversationSettings,\n workspace: {\n kind: \"LocalWorkspace\",\n working_dir: workingDir ?? getAgentServerWorkingDir(),\n },\n ...(initialMessage ? { initial_message: initialMessage } : {}),\n ...(plugins?.length\n ? {\n plugins: plugins.map((plugin) => ({\n source: plugin.source,\n ...(plugin.ref ? { ref: plugin.ref } : {}),\n ...(plugin.repo_path ? { repo_path: plugin.repo_path } : {}),\n })),\n }\n : {}),\n };\n\n return payload;\n}\n\ninterface LookupSecret {\n kind: \"LookupSecret\";\n url: string;\n headers?: Record<string, string>;\n description?: string;\n}\n\ntype StartConversationPayload = Record<string, unknown> & {\n agent_settings: AgentSettingsPayload;\n workspace: LocalWorkspacePayload;\n confirmation_policy: SettingsRecord;\n security_analyzer?: SettingsRecord;\n initial_message?: InitialMessagePayload;\n max_iterations: number;\n stuck_detection: true;\n autotitle: true;\n worktree: true;\n secrets_encrypted?: true;\n conversation_id?: string;\n secrets?: Record<string, LookupSecret>;\n tags?: Record<string, string>;\n tool_module_qualnames?: Record<string, string>;\n};\n\nexport interface StartConversationOptions {\n settings: Settings;\n query?: string;\n conversationInstructions?: string;\n plugins?: PluginSpec[];\n conversationId?: string;\n workingDir?: string;\n encryptedAgentSettings?: Record<string, SettingsValue>;\n encryptedConversationSettings?: Record<string, SettingsValue>;\n secretsEncrypted?: boolean;\n customSecrets?: Array<{ name: string; description?: string }>;\n}\n\nexport function buildStartConversationRequest(\n options: StartConversationOptions,\n): StartConversationPayload {\n const sourceAgentSettings = options.encryptedAgentSettings\n ? { ...options.settings, agent_settings: options.encryptedAgentSettings }\n : options.settings;\n\n const acpMode = isAcpAgent(sourceAgentSettings);\n const agentSettings = buildConfiguredAgentSettings(sourceAgentSettings);\n const acpServerTag = acpMode\n ? getAcpServerTag(sourceAgentSettings)\n : undefined;\n\n const sourceConversationOptions = options.encryptedConversationSettings\n ? {\n ...options,\n settings: {\n ...options.settings,\n conversation_settings: options.encryptedConversationSettings,\n },\n }\n : options;\n\n const conversationSettings = buildConfiguredConversationSettings(\n sourceConversationOptions,\n );\n\n const payload: StartConversationPayload = {\n agent_settings: agentSettings,\n workspace: conversationSettings.workspace,\n confirmation_policy:\n getConversationConfirmationPolicy(conversationSettings),\n max_iterations:\n typeof conversationSettings.max_iterations === \"number\"\n ? conversationSettings.max_iterations\n : 500,\n stuck_detection: true,\n autotitle: true,\n worktree: true,\n };\n\n if (acpServerTag) {\n payload.tags = { [ACP_SERVER_TAG_KEY]: acpServerTag };\n }\n\n if (options.secretsEncrypted) {\n payload.secrets_encrypted = true;\n }\n\n if (options.conversationId) {\n payload.conversation_id = options.conversationId;\n }\n\n const securityAnalyzer =\n getConversationSecurityAnalyzer(conversationSettings);\n if (securityAnalyzer) {\n payload.security_analyzer = securityAnalyzer;\n }\n\n if (conversationSettings.initial_message) {\n payload.initial_message = conversationSettings.initial_message;\n }\n\n if (conversationSettings.plugins) {\n payload.plugins = conversationSettings.plugins;\n }\n\n if (conversationSettings.hook_config) {\n payload.hook_config = conversationSettings.hook_config;\n }\n\n payload.tool_module_qualnames = {\n [CANVAS_UI_TOOL_NAME]: CANVAS_UI_TOOL_MODULE,\n ...((conversationSettings.tool_module_qualnames as\n | Record<string, string>\n | undefined) ?? {}),\n };\n\n if (conversationSettings.agent_definitions) {\n payload.agent_definitions = conversationSettings.agent_definitions;\n }\n\n if (options.customSecrets && options.customSecrets.length > 0) {\n const backend = getEffectiveLocalBackend();\n const headers = buildAuthHeaders(backend);\n\n const secrets: Record<string, LookupSecret> = {};\n for (const secret of options.customSecrets) {\n const lookupSecret: LookupSecret = {\n kind: \"LookupSecret\",\n url: `/api/settings/secrets/${encodeURIComponent(secret.name)}`,\n description: secret.description,\n };\n\n if (Object.keys(headers).length > 0) {\n lookupSecret.headers = headers;\n }\n\n secrets[secret.name] = lookupSecret;\n }\n\n payload.secrets = secrets;\n\n if (acpMode) {\n payload.agent_settings.agent_context = {\n ...payload.agent_settings.agent_context,\n secrets,\n };\n }\n }\n\n return payload;\n}\n\nexport async function buildStartConversationRequestWithEncryptedSettings(options: {\n settings: Settings;\n query?: string;\n conversationInstructions?: string;\n plugins?: PluginSpec[];\n conversationId?: string;\n workingDir?: string;\n}): Promise<Record<string, unknown>> {\n const { SecretsService } = await import(\"./secrets-service\");\n\n const [settingsResult, customSecrets] = await Promise.all([\n SettingsService.getSettingsForConversation(),\n SecretsService.getSecrets(),\n ]);\n\n const { agentSettings, conversationSettings, secretsEncrypted } =\n settingsResult;\n\n return buildStartConversationRequest({\n ...options,\n encryptedAgentSettings: agentSettings,\n encryptedConversationSettings: conversationSettings,\n secretsEncrypted,\n customSecrets,\n });\n}\n\nexport function emptyHooksResponse(): GetHooksResponse {\n return { hooks: [] };\n}\n"],"mappings":"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"}
|
|
@@ -34,7 +34,7 @@ function y() {
|
|
|
34
34
|
let t = [];
|
|
35
35
|
t.push("<RUNTIME_SERVICES>"), e.mode ? t.push(`You are running inside an agent-canvas dev stack started in '${e.mode}' mode.`) : t.push("You are running inside an agent-canvas dev stack."), t.push("The following services are reachable from your sandbox. URLs are written", "from your point of view (i.e., as you should curl/fetch them).", "");
|
|
36
36
|
let { agent_server: n, ingress: r, automation: i } = e.services, a = e.services.frontend ?? e.services.vite;
|
|
37
|
-
n?.url_from_agent && t.push(`* Agent Server (you): ${n.url_from_agent}`, ` ${n.description ?? "The agent-server hosting your tool calls."}`), r?.url_from_agent && t.push(`* Ingress: ${r.url_from_agent}`, ` ${r.description ?? "Unified entry point for browser-facing traffic."}`), a?.url_from_agent && t.push(`* Frontend: ${a.url_from_agent}`, ` ${a.description ?? "Frontend dev server."}`), i?.url_from_agent ? (t.push(`* Automation backend: ${i.url_from_agent}`, ` ${i.description ?? "OpenHands Automations service."}`), i.docs_url && t.push(` Docs: ${i.docs_url}`), i.openapi_url && t.push(` OpenAPI: ${i.openapi_url}`), i.auth_env_var && t.push(` Auth: header 'X-API-Key: $${i.auth_env_var}'`)) : t.push("* Automation backend: not running in this dev mode (skip /api/automation calls).");
|
|
37
|
+
n?.url_from_agent && t.push(`* Agent Server (you): ${n.url_from_agent}`, ` ${n.description ?? "The agent-server hosting your tool calls."}`), r?.url_from_agent && t.push(`* Ingress: ${r.url_from_agent}`, ` ${r.description ?? "Unified entry point for browser-facing traffic."}`), a?.url_from_agent && t.push(`* Frontend: ${a.url_from_agent}`, ` ${a.description ?? "Frontend dev server."}`), i?.url_from_agent ? (t.push(`* Automation backend: ${i.url_from_agent}`, ` ${i.description ?? "OpenHands Automations service."}`), i.docs_url && t.push(` Docs: ${i.docs_url}`), i.openapi_url && t.push(` OpenAPI: ${i.openapi_url}`), i.auth_env_var && t.push(` Auth: header 'X-Session-API-Key: $${i.auth_env_var}'`)) : t.push("* Automation backend: not running in this dev mode (skip /api/automation calls).");
|
|
38
38
|
let o = n?.url_from_agent;
|
|
39
39
|
return t.push("", "Trust this block over guessing: do not assume any other URLs are running."), o && t.push(`In particular, ${o} inside your sandbox is the Agent Server`, "you are running inside of — NOT the automation backend."), t.push("</RUNTIME_SERVICES>"), t.join("\n");
|
|
40
40
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-server-adapter.js","names":[],"sources":["../../src/api/agent-server-adapter.ts"],"sourcesContent":["import { DEFAULT_SETTINGS } from \"#/services/settings\";\nimport { ExecutionStatus } from \"#/types/agent-server/core\";\nimport { Settings, SettingsValue } from \"#/types/settings\";\nimport {\n getAcpProvider,\n resolveEffectiveAcpModel,\n} from \"#/constants/acp-providers\";\nimport { getAgentServerClientOptions } from \"./agent-server-client-options\";\nimport { isAgentServerToolAvailable } from \"./agent-server-compatibility\";\nimport {\n getAgentServerWorkingDir,\n shouldLoadPublicSkills,\n} from \"./agent-server-config\";\nimport { getEffectiveLocalBackend } from \"./backend-registry/active-store\";\nimport { buildAuthHeaders } from \"./backend-registry/auth\";\nimport {\n GetHooksResponse,\n PluginSpec,\n AppConversation,\n AppConversationPage,\n SandboxStatus,\n} from \"./conversation-service/agent-server-conversation-service.types\";\nimport SettingsService from \"./settings-service/settings-service.api\";\nimport { getStoredConversationMetadata } from \"./conversation-metadata-store\";\n\nexport interface DirectConversationInfo {\n id: string;\n title?: string | null;\n created_at: string;\n updated_at: string;\n execution_status?: string | null;\n /** Cloud-only sandbox lifecycle state. Omitted / null for local agent-server conversations. */\n sandbox_status?: string | null;\n metrics?: {\n accumulated_cost?: number | null;\n max_budget_per_task?: number | null;\n accumulated_token_usage?: {\n prompt_tokens?: number;\n completion_tokens?: number;\n cache_read_tokens?: number;\n cache_write_tokens?: number;\n context_window?: number;\n per_turn_token?: number;\n } | null;\n } | null;\n agent?: {\n /**\n * Pydantic discriminator from the SDK union: ``\"ACPAgent\"`` for ACP CLI\n * subprocesses (model lives on the subprocess via ``acp_model``),\n * ``\"Agent\"`` for direct litellm. Read by {@link toAppConversation}.\n */\n kind?: string | null;\n acp_model?: string | null;\n llm?: {\n model?: string | null;\n } | null;\n } | null;\n current_model_id?: string | null;\n current_model_name?: string | null;\n workspace?: {\n working_dir?: string | null;\n } | null;\n /**\n * Arbitrary string-keyed conversation tags surfaced by the agent-server\n * (see ``ConversationInfo.tags``). Canvas only consumes one key today —\n * ``ACP_SERVER_TAG_KEY`` (\"acpserver\") — but the field is typed as a\n * generic record so future readers don't need another wire-shape change.\n * Keys are constrained to ``^[a-z0-9]+$`` by the agent-server validator;\n * values are opaque strings.\n */\n tags?: Record<string, string> | null;\n}\n\n// Module qualname for the Canvas-UI tool. The agent-server imports this via\n// tool_module_qualnames; the host directory is exposed via OH_EXTRA_PYTHON_PATH\n// (see scripts/dev-safe.mjs).\nconst CANVAS_UI_TOOL_NAME = \"canvas_ui\";\nconst CANVAS_UI_TOOL_MODULE = \"canvas_ui_tool\";\n\nconst DEFAULT_TOOL_NAMES = [\n \"terminal\",\n \"file_editor\",\n \"task_tracker\",\n CANVAS_UI_TOOL_NAME,\n];\nconst BROWSER_TOOL_SET_NAME = \"browser_tool_set\";\nconst TASK_TOOL_SET_NAME = \"task_tool_set\";\n\nfunction browserToolsEnabled() {\n return import.meta.env.VITE_ENABLE_BROWSER_TOOLS !== \"false\";\n}\n\n/**\n * Shape of `VITE_RUNTIME_SERVICES_INFO` (set by the dev launchers in\n * scripts/dev-*.mjs). All URLs are written from the agent's point of view,\n * not the browser's. The block is rendered into the agent's system prompt\n * via `AgentContext.system_message_suffix` so the agent knows what's\n * reachable from inside its sandbox without having to probe.\n */\ninterface RuntimeServicesInfo {\n mode?: string;\n agent_host_alias?: string;\n services?: {\n agent_server?: { description?: string; url_from_agent?: string };\n ingress?: { description?: string; url_from_agent?: string };\n frontend?: {\n kind?: \"vite\" | \"static\";\n description?: string;\n url_from_agent?: string;\n };\n // `vite` is the legacy key name for the frontend entry, accepted for\n // one release while older dev-stack launchers may still emit it.\n vite?: { description?: string; url_from_agent?: string };\n automation?: {\n description?: string;\n url_from_agent?: string;\n api_prefix?: string;\n docs_url?: string;\n openapi_url?: string;\n auth_env_var?: string;\n };\n };\n}\n\nfunction parseRuntimeServicesInfo(): RuntimeServicesInfo | null {\n const raw = import.meta.env.VITE_RUNTIME_SERVICES_INFO?.trim();\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as RuntimeServicesInfo;\n if (!parsed || typeof parsed !== \"object\") return null;\n return parsed;\n } catch {\n // Malformed JSON: ignore and fall back to no runtime info, rather than\n // tearing down conversation creation over a misconfigured dev env var.\n return null;\n }\n}\n\n/**\n * Render the runtime services info into a markdown block suitable for\n * appending to the system prompt via `AgentContext.system_message_suffix`.\n *\n * Returns `undefined` when no runtime info is configured, so callers can\n * safely omit the field on production builds (where the launcher doesn't\n * set `VITE_RUNTIME_SERVICES_INFO`).\n */\nexport function buildRuntimeServicesSystemSuffix(): string | undefined {\n const info = parseRuntimeServicesInfo();\n if (!info?.services) return undefined;\n\n const lines: string[] = [];\n lines.push(\"<RUNTIME_SERVICES>\");\n if (info.mode) {\n lines.push(\n `You are running inside an agent-canvas dev stack started in '${info.mode}' mode.`,\n );\n } else {\n lines.push(\"You are running inside an agent-canvas dev stack.\");\n }\n lines.push(\n \"The following services are reachable from your sandbox. URLs are written\",\n \"from your point of view (i.e., as you should curl/fetch them).\",\n \"\",\n );\n\n const { agent_server, ingress, automation } = info.services;\n // Accept `frontend` (current key) or `vite` (legacy key) for the\n // frontend service entry. The legacy fallback can be removed once all\n // launchers in this repo emit `frontend`.\n const frontend = info.services.frontend ?? info.services.vite;\n\n if (agent_server?.url_from_agent) {\n lines.push(\n `* Agent Server (you): ${agent_server.url_from_agent}`,\n ` ${agent_server.description ?? \"The agent-server hosting your tool calls.\"}`,\n );\n }\n if (ingress?.url_from_agent) {\n lines.push(\n `* Ingress: ${ingress.url_from_agent}`,\n ` ${ingress.description ?? \"Unified entry point for browser-facing traffic.\"}`,\n );\n }\n if (frontend?.url_from_agent) {\n lines.push(\n `* Frontend: ${frontend.url_from_agent}`,\n ` ${frontend.description ?? \"Frontend dev server.\"}`,\n );\n }\n if (automation?.url_from_agent) {\n lines.push(\n `* Automation backend: ${automation.url_from_agent}`,\n ` ${automation.description ?? \"OpenHands Automations service.\"}`,\n );\n if (automation.docs_url) {\n lines.push(` Docs: ${automation.docs_url}`);\n }\n if (automation.openapi_url) {\n lines.push(` OpenAPI: ${automation.openapi_url}`);\n }\n if (automation.auth_env_var) {\n lines.push(\n ` Auth: header 'X-API-Key: $${automation.auth_env_var}'`,\n );\n }\n } else {\n lines.push(\n \"* Automation backend: not running in this dev mode (skip /api/automation calls).\",\n );\n }\n\n // Anchor the \"don't guess\" warning to the actual agent-server URL for\n // this stack instead of a hardcoded port. The agent-server listens on\n // different ports across dev modes, and baking the wrong port into the\n // system prompt is exactly the kind of confusion this block is meant to\n // prevent.\n const agentServerUrl = agent_server?.url_from_agent;\n lines.push(\n \"\",\n \"Trust this block over guessing: do not assume any other URLs are running.\",\n );\n if (agentServerUrl) {\n lines.push(\n `In particular, ${agentServerUrl} inside your sandbox is the Agent Server`,\n \"you are running inside of — NOT the automation backend.\",\n );\n }\n lines.push(\"</RUNTIME_SERVICES>\");\n\n return lines.join(\"\\n\");\n}\n\nexport function toConversationUrl(conversationId: string): string {\n // Local-format conversation URL — points at whichever local agent-server\n // is actually serving the conversation (the bundled one when the active\n // selection is cloud).\n const { host } = getAgentServerClientOptions();\n return `${host}/api/conversations/${conversationId}`;\n}\n\n// TODO(i18n): extract \"Conversation\" once we add CONVERSATION$DEFAULT_TITLE\n// with `{{shortId}}` interpolation. Kept as a literal for now to keep the\n// fallback inside this pure adapter rather than fanning out to display sites.\nexport function getDefaultConversationTitle(conversationId: string): string {\n return `Conversation ${conversationId.slice(0, 5)}`;\n}\n\nexport function toAppConversation(\n info: DirectConversationInfo,\n): AppConversation {\n const metadata = getStoredConversationMetadata(info.id);\n // ACPAgent conversations carry a sentinel ``llm`` on older SDKs. Prefer the\n // runtime model fields when available, then the configured ``acp_model`` that\n // Canvas saves for built-in providers. ``agent_kind`` still gates model\n // switching, so surfacing this string is display-only.\n const isAcp = info.agent?.kind === \"ACPAgent\";\n // Only surface ``acp_server`` for ACP conversations even if the wire\n // payload accidentally carries an ``acpserver`` tag on an OpenHands\n // conversation — the chip is identity info for the ACP CLI subprocess,\n // and showing it on a non-ACP conversation would be a lie.\n const acpServer = isAcp ? (info.tags?.[ACP_SERVER_TAG_KEY] ?? null) : null;\n return {\n id: info.id,\n created_by_user_id: null,\n selected_repository: metadata?.selected_repository ?? null,\n selected_branch: metadata?.selected_branch ?? null,\n git_provider: metadata?.git_provider ?? null,\n selected_workspace: metadata?.selected_workspace ?? null,\n title: info.title?.trim()\n ? info.title\n : getDefaultConversationTitle(info.id),\n trigger: null,\n pr_number: [],\n agent_kind: isAcp ? \"acp\" : \"openhands\",\n acp_server: acpServer,\n // Chip path: no ``providerDefault`` — the chip must distinguish\n // \"no concrete model\" (fall back to the provider display name in\n // ConversationCardFooter) from \"default\" (would lie about what's\n // running on the subprocess).\n llm_model: isAcp\n ? resolveEffectiveAcpModel({\n runtimeName: info.current_model_name,\n runtimeId: info.current_model_id,\n configured: info.agent?.acp_model,\n sdkLlm: info.agent?.llm?.model,\n })\n : (info.agent?.llm?.model ?? DEFAULT_SETTINGS.llm_model),\n metrics: info.metrics\n ? {\n accumulated_cost: info.metrics.accumulated_cost ?? null,\n max_budget_per_task: info.metrics.max_budget_per_task ?? null,\n accumulated_token_usage: info.metrics.accumulated_token_usage\n ? {\n prompt_tokens:\n info.metrics.accumulated_token_usage.prompt_tokens ?? 0,\n completion_tokens:\n info.metrics.accumulated_token_usage.completion_tokens ?? 0,\n cache_read_tokens:\n info.metrics.accumulated_token_usage.cache_read_tokens ?? 0,\n cache_write_tokens:\n info.metrics.accumulated_token_usage.cache_write_tokens ?? 0,\n context_window:\n info.metrics.accumulated_token_usage.context_window ?? 0,\n per_turn_token:\n info.metrics.accumulated_token_usage.per_turn_token ?? 0,\n }\n : null,\n }\n : null,\n created_at: info.created_at,\n updated_at: info.updated_at,\n execution_status:\n (info.execution_status as AppConversation[\"execution_status\"]) ??\n ExecutionStatus.IDLE,\n sandbox_status: (info.sandbox_status as SandboxStatus | null) ?? null,\n conversation_url: toConversationUrl(info.id),\n session_api_key: getAgentServerClientOptions().apiKey ?? null,\n sandbox_id: null,\n workspace: {\n working_dir: info.workspace?.working_dir ?? getAgentServerWorkingDir(),\n },\n public: false,\n sub_conversation_ids: [],\n };\n}\n\nexport function toConversationPage(data: {\n items: DirectConversationInfo[];\n next_page_id?: string | null;\n}): AppConversationPage {\n return {\n items: data.items.map(toAppConversation),\n next_page_id: data.next_page_id ?? null,\n };\n}\n\ntype SettingsRecord = Record<string, unknown>;\n\ninterface AgentToolSpec {\n name: string;\n params: SettingsRecord;\n}\n\ntype AgentSettingsPayload = SettingsRecord & {\n llm?: SettingsRecord;\n agent_context: SettingsRecord;\n tools?: AgentToolSpec[];\n};\n\ninterface LocalWorkspacePayload {\n kind: \"LocalWorkspace\";\n working_dir: string;\n}\n\ninterface InitialMessagePayload {\n role: \"user\";\n content: Array<{ type: \"text\"; text: string }>;\n run: true;\n}\n\ntype ConversationSettingsPayload = SettingsRecord & {\n workspace: LocalWorkspacePayload;\n initial_message?: InitialMessagePayload;\n};\n\nconst ACP_SETTINGS_KEYS = [\n \"acp_command\",\n \"acp_args\",\n \"acp_env\",\n \"acp_model\",\n \"acp_session_mode\",\n \"acp_prompt_timeout\",\n] as const;\n\nexport const ACP_SERVER_TAG_KEY = \"acpserver\";\n\nconst CONVERSATION_SETTINGS_METADATA_KEYS = new Set([\n \"schema_version\",\n \"agent_settings\",\n \"workspace\",\n \"conversation_id\",\n \"initial_message\",\n \"plugins\",\n]);\n\nfunction toRecord(value: unknown): SettingsRecord {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n return {};\n }\n\n return structuredClone(value as SettingsRecord);\n}\n\nfunction normalizeSecretString(value: unknown): string | undefined {\n if (typeof value !== \"string\") {\n return undefined;\n }\n\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction getConversationConfirmationPolicy(\n conversationSettings: SettingsRecord,\n) {\n if (conversationSettings.confirmation_mode !== true) {\n return { kind: \"NeverConfirm\" };\n }\n\n if (conversationSettings.security_analyzer === \"llm\") {\n return { kind: \"ConfirmRisky\", threshold: \"HIGH\", confirm_unknown: true };\n }\n\n return { kind: \"AlwaysConfirm\" };\n}\n\nfunction getConversationSecurityAnalyzer(conversationSettings: SettingsRecord) {\n switch (conversationSettings.security_analyzer) {\n case \"llm\":\n return { kind: \"LLMSecurityAnalyzer\" };\n case \"pattern\":\n return { kind: \"PatternSecurityAnalyzer\" };\n case \"policy_rail\":\n return { kind: \"PolicyRailSecurityAnalyzer\" };\n default:\n return undefined;\n }\n}\n\nfunction isToolRecord(\n value: unknown,\n): value is { name: string; params?: unknown } {\n return (\n !!value &&\n typeof value === \"object\" &&\n !Array.isArray(value) &&\n typeof (value as { name?: unknown }).name === \"string\"\n );\n}\n\nfunction shouldIncludeTool(name: string, agentSettings: SettingsRecord) {\n if (name === BROWSER_TOOL_SET_NAME) {\n return browserToolsEnabled() && isAgentServerToolAvailable(name);\n }\n\n if (name === TASK_TOOL_SET_NAME) {\n return (\n agentSettings.enable_sub_agents === true &&\n isAgentServerToolAvailable(name)\n );\n }\n\n return true;\n}\n\nfunction getAgentTools(agentSettings: SettingsRecord): AgentToolSpec[] {\n const tools = new Map<string, AgentToolSpec>();\n\n for (const name of DEFAULT_TOOL_NAMES) {\n tools.set(name, { name, params: {} });\n }\n\n for (const name of [BROWSER_TOOL_SET_NAME, TASK_TOOL_SET_NAME]) {\n if (shouldIncludeTool(name, agentSettings)) {\n tools.set(name, { name, params: {} });\n }\n }\n\n const configuredTools = agentSettings.tools;\n if (\n Array.isArray(configuredTools) &&\n configuredTools.every((tool) => isToolRecord(tool))\n ) {\n for (const tool of configuredTools) {\n if (shouldIncludeTool(tool.name, agentSettings)) {\n tools.set(tool.name, {\n name: tool.name,\n params: toRecord(tool.params),\n });\n }\n }\n }\n\n return Array.from(tools.values());\n}\n\nfunction buildInitialMessage(\n query?: string,\n conversationInstructions?: string,\n): InitialMessagePayload | null {\n const parts = [query?.trim(), conversationInstructions?.trim()].filter(\n Boolean,\n );\n if (parts.length === 0) {\n return null;\n }\n\n return {\n role: \"user\",\n content: [{ type: \"text\", text: parts.join(\"\\n\\n\") }],\n run: true,\n };\n}\n\nfunction buildAgentContext(agentSettings: SettingsRecord): SettingsRecord {\n const runtimeServicesSuffix = buildRuntimeServicesSystemSuffix();\n return {\n ...toRecord(agentSettings.agent_context),\n load_public_skills: shouldLoadPublicSkills(),\n load_user_skills: true,\n load_project_skills: true,\n ...(runtimeServicesSuffix\n ? { system_message_suffix: runtimeServicesSuffix }\n : {}),\n };\n}\n\nfunction isAcpAgent(settings: Settings): boolean {\n const agentSettings = toRecord(settings.agent_settings);\n return agentSettings.agent_kind === \"acp\";\n}\n\nfunction getAcpServerTag(settings: Settings): string | undefined {\n const agentSettings = toRecord(settings.agent_settings);\n const value = agentSettings.acp_server;\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction resolveAcpCommand(agentSettings: SettingsRecord): unknown {\n const cmd = agentSettings.acp_command;\n const isEmpty = Array.isArray(cmd) && cmd.length === 0;\n const noCommand = cmd === undefined;\n if (!isEmpty && !noCommand) {\n return cmd;\n }\n\n const serverKey =\n typeof agentSettings.acp_server === \"string\"\n ? agentSettings.acp_server\n : undefined;\n const provider = getAcpProvider(serverKey);\n return provider ? [...provider.default_command] : cmd;\n}\n\nfunction buildConfiguredAcpAgentSettings(\n settings: Settings,\n): AgentSettingsPayload {\n const agentSettings = toRecord(settings.agent_settings);\n const payload: AgentSettingsPayload = {\n agent_kind: \"acp\",\n agent_context: buildAgentContext(agentSettings),\n };\n\n for (const key of ACP_SETTINGS_KEYS) {\n // ``acp_model`` is resolved separately below so a saved ``null`` still\n // falls back to the provider's default rather than being dropped.\n if (key === \"acp_model\") continue;\n const value =\n key === \"acp_command\"\n ? resolveAcpCommand(agentSettings)\n : agentSettings[key];\n if (value !== undefined && value !== null) {\n payload[key] = value;\n }\n }\n\n // Saved settings may carry ``acp_model: null`` (existing users predating\n // the default-model registry, or saved fields the agent-server stripped).\n // Fall back to the provider's ``default_model`` so the conversation starts\n // with whatever the Settings → Agent UI shows — without that, the form's\n // displayed default would silently not take effect at runtime until the\n // user re-saved the page.\n const serverKey =\n typeof agentSettings.acp_server === \"string\"\n ? agentSettings.acp_server\n : undefined;\n const provider = getAcpProvider(serverKey);\n const effectiveModel = resolveEffectiveAcpModel({\n configured: agentSettings.acp_model as string | null | undefined,\n providerDefault: provider?.default_model,\n });\n if (effectiveModel) {\n payload.acp_model = effectiveModel;\n }\n\n return payload;\n}\n\nfunction buildConfiguredOpenHandsAgentSettings(\n settings: Settings,\n): AgentSettingsPayload {\n const agentSettings = toRecord(settings.agent_settings);\n const llm = toRecord(agentSettings.llm);\n\n llm.model =\n typeof llm.model === \"string\" && llm.model.trim().length > 0\n ? llm.model\n : DEFAULT_SETTINGS.llm_model;\n\n const apiKey = normalizeSecretString(llm.api_key);\n if (apiKey) {\n llm.api_key = apiKey;\n } else {\n delete llm.api_key;\n }\n\n const baseUrl = normalizeSecretString(llm.base_url);\n if (baseUrl) {\n llm.base_url = baseUrl;\n } else {\n delete llm.base_url;\n }\n\n const mcpConfig = toRecord(agentSettings.mcp_config);\n if (Object.keys(mcpConfig).length === 0 || !(\"mcpServers\" in mcpConfig)) {\n delete agentSettings.mcp_config;\n }\n\n delete agentSettings.acp_server;\n for (const key of ACP_SETTINGS_KEYS) {\n delete agentSettings[key];\n }\n\n return {\n ...agentSettings,\n llm,\n agent_context: buildAgentContext(agentSettings),\n tools: getAgentTools(agentSettings),\n };\n}\n\nfunction buildConfiguredAgentSettings(\n settings: Settings,\n): AgentSettingsPayload {\n return isAcpAgent(settings)\n ? buildConfiguredAcpAgentSettings(settings)\n : buildConfiguredOpenHandsAgentSettings(settings);\n}\n\nfunction buildConfiguredConversationSettings(options: {\n settings: Settings;\n query?: string;\n conversationInstructions?: string;\n plugins?: PluginSpec[];\n workingDir?: string;\n}): ConversationSettingsPayload {\n const { settings, query, conversationInstructions, plugins, workingDir } =\n options;\n const conversationSettings = toRecord(settings.conversation_settings);\n const initialMessage = buildInitialMessage(query, conversationInstructions);\n\n CONVERSATION_SETTINGS_METADATA_KEYS.forEach(\n (key) => delete conversationSettings[key],\n );\n\n const payload: ConversationSettingsPayload = {\n ...conversationSettings,\n workspace: {\n kind: \"LocalWorkspace\",\n working_dir: workingDir ?? getAgentServerWorkingDir(),\n },\n ...(initialMessage ? { initial_message: initialMessage } : {}),\n ...(plugins?.length\n ? {\n plugins: plugins.map((plugin) => ({\n source: plugin.source,\n ...(plugin.ref ? { ref: plugin.ref } : {}),\n ...(plugin.repo_path ? { repo_path: plugin.repo_path } : {}),\n })),\n }\n : {}),\n };\n\n return payload;\n}\n\ninterface LookupSecret {\n kind: \"LookupSecret\";\n url: string;\n headers?: Record<string, string>;\n description?: string;\n}\n\ntype StartConversationPayload = Record<string, unknown> & {\n agent_settings: AgentSettingsPayload;\n workspace: LocalWorkspacePayload;\n confirmation_policy: SettingsRecord;\n security_analyzer?: SettingsRecord;\n initial_message?: InitialMessagePayload;\n max_iterations: number;\n stuck_detection: true;\n autotitle: true;\n worktree: true;\n secrets_encrypted?: true;\n conversation_id?: string;\n secrets?: Record<string, LookupSecret>;\n tags?: Record<string, string>;\n tool_module_qualnames?: Record<string, string>;\n};\n\nexport interface StartConversationOptions {\n settings: Settings;\n query?: string;\n conversationInstructions?: string;\n plugins?: PluginSpec[];\n conversationId?: string;\n workingDir?: string;\n encryptedAgentSettings?: Record<string, SettingsValue>;\n encryptedConversationSettings?: Record<string, SettingsValue>;\n secretsEncrypted?: boolean;\n customSecrets?: Array<{ name: string; description?: string }>;\n}\n\nexport function buildStartConversationRequest(\n options: StartConversationOptions,\n): StartConversationPayload {\n const sourceAgentSettings = options.encryptedAgentSettings\n ? { ...options.settings, agent_settings: options.encryptedAgentSettings }\n : options.settings;\n\n const acpMode = isAcpAgent(sourceAgentSettings);\n const agentSettings = buildConfiguredAgentSettings(sourceAgentSettings);\n const acpServerTag = acpMode\n ? getAcpServerTag(sourceAgentSettings)\n : undefined;\n\n const sourceConversationOptions = options.encryptedConversationSettings\n ? {\n ...options,\n settings: {\n ...options.settings,\n conversation_settings: options.encryptedConversationSettings,\n },\n }\n : options;\n\n const conversationSettings = buildConfiguredConversationSettings(\n sourceConversationOptions,\n );\n\n const payload: StartConversationPayload = {\n agent_settings: agentSettings,\n workspace: conversationSettings.workspace,\n confirmation_policy:\n getConversationConfirmationPolicy(conversationSettings),\n max_iterations:\n typeof conversationSettings.max_iterations === \"number\"\n ? conversationSettings.max_iterations\n : 500,\n stuck_detection: true,\n autotitle: true,\n worktree: true,\n };\n\n if (acpServerTag) {\n payload.tags = { [ACP_SERVER_TAG_KEY]: acpServerTag };\n }\n\n if (options.secretsEncrypted) {\n payload.secrets_encrypted = true;\n }\n\n if (options.conversationId) {\n payload.conversation_id = options.conversationId;\n }\n\n const securityAnalyzer =\n getConversationSecurityAnalyzer(conversationSettings);\n if (securityAnalyzer) {\n payload.security_analyzer = securityAnalyzer;\n }\n\n if (conversationSettings.initial_message) {\n payload.initial_message = conversationSettings.initial_message;\n }\n\n if (conversationSettings.plugins) {\n payload.plugins = conversationSettings.plugins;\n }\n\n if (conversationSettings.hook_config) {\n payload.hook_config = conversationSettings.hook_config;\n }\n\n payload.tool_module_qualnames = {\n [CANVAS_UI_TOOL_NAME]: CANVAS_UI_TOOL_MODULE,\n ...((conversationSettings.tool_module_qualnames as\n | Record<string, string>\n | undefined) ?? {}),\n };\n\n if (conversationSettings.agent_definitions) {\n payload.agent_definitions = conversationSettings.agent_definitions;\n }\n\n if (options.customSecrets && options.customSecrets.length > 0) {\n const backend = getEffectiveLocalBackend();\n const headers = buildAuthHeaders(backend);\n\n const secrets: Record<string, LookupSecret> = {};\n for (const secret of options.customSecrets) {\n const lookupSecret: LookupSecret = {\n kind: \"LookupSecret\",\n url: `/api/settings/secrets/${encodeURIComponent(secret.name)}`,\n description: secret.description,\n };\n\n if (Object.keys(headers).length > 0) {\n lookupSecret.headers = headers;\n }\n\n secrets[secret.name] = lookupSecret;\n }\n\n payload.secrets = secrets;\n\n if (acpMode) {\n payload.agent_settings.agent_context = {\n ...payload.agent_settings.agent_context,\n secrets,\n };\n }\n }\n\n return payload;\n}\n\nexport async function buildStartConversationRequestWithEncryptedSettings(options: {\n settings: Settings;\n query?: string;\n conversationInstructions?: string;\n plugins?: PluginSpec[];\n conversationId?: string;\n workingDir?: string;\n}): Promise<Record<string, unknown>> {\n const { SecretsService } = await import(\"./secrets-service\");\n\n const [settingsResult, customSecrets] = await Promise.all([\n SettingsService.getSettingsForConversation(),\n SecretsService.getSecrets(),\n ]);\n\n const { agentSettings, conversationSettings, secretsEncrypted } =\n settingsResult;\n\n return buildStartConversationRequest({\n ...options,\n encryptedAgentSettings: agentSettings,\n encryptedConversationSettings: conversationSettings,\n secretsEncrypted,\n customSecrets,\n });\n}\n\nexport function emptyHooksResponse(): GetHooksResponse {\n return { hooks: [] };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"agent-server-adapter.js","names":[],"sources":["../../src/api/agent-server-adapter.ts"],"sourcesContent":["import { DEFAULT_SETTINGS } from \"#/services/settings\";\nimport { ExecutionStatus } from \"#/types/agent-server/core\";\nimport { Settings, SettingsValue } from \"#/types/settings\";\nimport {\n getAcpProvider,\n resolveEffectiveAcpModel,\n} from \"#/constants/acp-providers\";\nimport { getAgentServerClientOptions } from \"./agent-server-client-options\";\nimport { isAgentServerToolAvailable } from \"./agent-server-compatibility\";\nimport {\n getAgentServerWorkingDir,\n shouldLoadPublicSkills,\n} from \"./agent-server-config\";\nimport { getEffectiveLocalBackend } from \"./backend-registry/active-store\";\nimport { buildAuthHeaders } from \"./backend-registry/auth\";\nimport {\n GetHooksResponse,\n PluginSpec,\n AppConversation,\n AppConversationPage,\n SandboxStatus,\n} from \"./conversation-service/agent-server-conversation-service.types\";\nimport SettingsService from \"./settings-service/settings-service.api\";\nimport { getStoredConversationMetadata } from \"./conversation-metadata-store\";\n\nexport interface DirectConversationInfo {\n id: string;\n title?: string | null;\n created_at: string;\n updated_at: string;\n execution_status?: string | null;\n /** Cloud-only sandbox lifecycle state. Omitted / null for local agent-server conversations. */\n sandbox_status?: string | null;\n metrics?: {\n accumulated_cost?: number | null;\n max_budget_per_task?: number | null;\n accumulated_token_usage?: {\n prompt_tokens?: number;\n completion_tokens?: number;\n cache_read_tokens?: number;\n cache_write_tokens?: number;\n context_window?: number;\n per_turn_token?: number;\n } | null;\n } | null;\n agent?: {\n /**\n * Pydantic discriminator from the SDK union: ``\"ACPAgent\"`` for ACP CLI\n * subprocesses (model lives on the subprocess via ``acp_model``),\n * ``\"Agent\"`` for direct litellm. Read by {@link toAppConversation}.\n */\n kind?: string | null;\n acp_model?: string | null;\n llm?: {\n model?: string | null;\n } | null;\n } | null;\n current_model_id?: string | null;\n current_model_name?: string | null;\n workspace?: {\n working_dir?: string | null;\n } | null;\n /**\n * Arbitrary string-keyed conversation tags surfaced by the agent-server\n * (see ``ConversationInfo.tags``). Canvas only consumes one key today —\n * ``ACP_SERVER_TAG_KEY`` (\"acpserver\") — but the field is typed as a\n * generic record so future readers don't need another wire-shape change.\n * Keys are constrained to ``^[a-z0-9]+$`` by the agent-server validator;\n * values are opaque strings.\n */\n tags?: Record<string, string> | null;\n}\n\n// Module qualname for the Canvas-UI tool. The agent-server imports this via\n// tool_module_qualnames; the host directory is exposed via OH_EXTRA_PYTHON_PATH\n// (see scripts/dev-safe.mjs).\nconst CANVAS_UI_TOOL_NAME = \"canvas_ui\";\nconst CANVAS_UI_TOOL_MODULE = \"canvas_ui_tool\";\n\nconst DEFAULT_TOOL_NAMES = [\n \"terminal\",\n \"file_editor\",\n \"task_tracker\",\n CANVAS_UI_TOOL_NAME,\n];\nconst BROWSER_TOOL_SET_NAME = \"browser_tool_set\";\nconst TASK_TOOL_SET_NAME = \"task_tool_set\";\n\nfunction browserToolsEnabled() {\n return import.meta.env.VITE_ENABLE_BROWSER_TOOLS !== \"false\";\n}\n\n/**\n * Shape of `VITE_RUNTIME_SERVICES_INFO` (set by the dev launchers in\n * scripts/dev-*.mjs). All URLs are written from the agent's point of view,\n * not the browser's. The block is rendered into the agent's system prompt\n * via `AgentContext.system_message_suffix` so the agent knows what's\n * reachable from inside its sandbox without having to probe.\n */\ninterface RuntimeServicesInfo {\n mode?: string;\n agent_host_alias?: string;\n services?: {\n agent_server?: { description?: string; url_from_agent?: string };\n ingress?: { description?: string; url_from_agent?: string };\n frontend?: {\n kind?: \"vite\" | \"static\";\n description?: string;\n url_from_agent?: string;\n };\n // `vite` is the legacy key name for the frontend entry, accepted for\n // one release while older dev-stack launchers may still emit it.\n vite?: { description?: string; url_from_agent?: string };\n automation?: {\n description?: string;\n url_from_agent?: string;\n api_prefix?: string;\n docs_url?: string;\n openapi_url?: string;\n auth_env_var?: string;\n };\n };\n}\n\nfunction parseRuntimeServicesInfo(): RuntimeServicesInfo | null {\n const raw = import.meta.env.VITE_RUNTIME_SERVICES_INFO?.trim();\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as RuntimeServicesInfo;\n if (!parsed || typeof parsed !== \"object\") return null;\n return parsed;\n } catch {\n // Malformed JSON: ignore and fall back to no runtime info, rather than\n // tearing down conversation creation over a misconfigured dev env var.\n return null;\n }\n}\n\n/**\n * Render the runtime services info into a markdown block suitable for\n * appending to the system prompt via `AgentContext.system_message_suffix`.\n *\n * Returns `undefined` when no runtime info is configured, so callers can\n * safely omit the field on production builds (where the launcher doesn't\n * set `VITE_RUNTIME_SERVICES_INFO`).\n */\nexport function buildRuntimeServicesSystemSuffix(): string | undefined {\n const info = parseRuntimeServicesInfo();\n if (!info?.services) return undefined;\n\n const lines: string[] = [];\n lines.push(\"<RUNTIME_SERVICES>\");\n if (info.mode) {\n lines.push(\n `You are running inside an agent-canvas dev stack started in '${info.mode}' mode.`,\n );\n } else {\n lines.push(\"You are running inside an agent-canvas dev stack.\");\n }\n lines.push(\n \"The following services are reachable from your sandbox. URLs are written\",\n \"from your point of view (i.e., as you should curl/fetch them).\",\n \"\",\n );\n\n const { agent_server, ingress, automation } = info.services;\n // Accept `frontend` (current key) or `vite` (legacy key) for the\n // frontend service entry. The legacy fallback can be removed once all\n // launchers in this repo emit `frontend`.\n const frontend = info.services.frontend ?? info.services.vite;\n\n if (agent_server?.url_from_agent) {\n lines.push(\n `* Agent Server (you): ${agent_server.url_from_agent}`,\n ` ${agent_server.description ?? \"The agent-server hosting your tool calls.\"}`,\n );\n }\n if (ingress?.url_from_agent) {\n lines.push(\n `* Ingress: ${ingress.url_from_agent}`,\n ` ${ingress.description ?? \"Unified entry point for browser-facing traffic.\"}`,\n );\n }\n if (frontend?.url_from_agent) {\n lines.push(\n `* Frontend: ${frontend.url_from_agent}`,\n ` ${frontend.description ?? \"Frontend dev server.\"}`,\n );\n }\n if (automation?.url_from_agent) {\n lines.push(\n `* Automation backend: ${automation.url_from_agent}`,\n ` ${automation.description ?? \"OpenHands Automations service.\"}`,\n );\n if (automation.docs_url) {\n lines.push(` Docs: ${automation.docs_url}`);\n }\n if (automation.openapi_url) {\n lines.push(` OpenAPI: ${automation.openapi_url}`);\n }\n if (automation.auth_env_var) {\n // X-Session-API-Key is the local convention shared by the agent-server\n // and automation backend (see openhands-automation auth.py).\n lines.push(\n ` Auth: header 'X-Session-API-Key: $${automation.auth_env_var}'`,\n );\n }\n } else {\n lines.push(\n \"* Automation backend: not running in this dev mode (skip /api/automation calls).\",\n );\n }\n\n // Anchor the \"don't guess\" warning to the actual agent-server URL for\n // this stack instead of a hardcoded port. The agent-server listens on\n // different ports across dev modes, and baking the wrong port into the\n // system prompt is exactly the kind of confusion this block is meant to\n // prevent.\n const agentServerUrl = agent_server?.url_from_agent;\n lines.push(\n \"\",\n \"Trust this block over guessing: do not assume any other URLs are running.\",\n );\n if (agentServerUrl) {\n lines.push(\n `In particular, ${agentServerUrl} inside your sandbox is the Agent Server`,\n \"you are running inside of — NOT the automation backend.\",\n );\n }\n lines.push(\"</RUNTIME_SERVICES>\");\n\n return lines.join(\"\\n\");\n}\n\nexport function toConversationUrl(conversationId: string): string {\n // Local-format conversation URL — points at whichever local agent-server\n // is actually serving the conversation (the bundled one when the active\n // selection is cloud).\n const { host } = getAgentServerClientOptions();\n return `${host}/api/conversations/${conversationId}`;\n}\n\n// TODO(i18n): extract \"Conversation\" once we add CONVERSATION$DEFAULT_TITLE\n// with `{{shortId}}` interpolation. Kept as a literal for now to keep the\n// fallback inside this pure adapter rather than fanning out to display sites.\nexport function getDefaultConversationTitle(conversationId: string): string {\n return `Conversation ${conversationId.slice(0, 5)}`;\n}\n\nexport function toAppConversation(\n info: DirectConversationInfo,\n): AppConversation {\n const metadata = getStoredConversationMetadata(info.id);\n // ACPAgent conversations carry a sentinel ``llm`` on older SDKs. Prefer the\n // runtime model fields when available, then the configured ``acp_model`` that\n // Canvas saves for built-in providers. ``agent_kind`` still gates model\n // switching, so surfacing this string is display-only.\n const isAcp = info.agent?.kind === \"ACPAgent\";\n // Only surface ``acp_server`` for ACP conversations even if the wire\n // payload accidentally carries an ``acpserver`` tag on an OpenHands\n // conversation — the chip is identity info for the ACP CLI subprocess,\n // and showing it on a non-ACP conversation would be a lie.\n const acpServer = isAcp ? (info.tags?.[ACP_SERVER_TAG_KEY] ?? null) : null;\n return {\n id: info.id,\n created_by_user_id: null,\n selected_repository: metadata?.selected_repository ?? null,\n selected_branch: metadata?.selected_branch ?? null,\n git_provider: metadata?.git_provider ?? null,\n selected_workspace: metadata?.selected_workspace ?? null,\n title: info.title?.trim()\n ? info.title\n : getDefaultConversationTitle(info.id),\n trigger: null,\n pr_number: [],\n agent_kind: isAcp ? \"acp\" : \"openhands\",\n acp_server: acpServer,\n // Chip path: no ``providerDefault`` — the chip must distinguish\n // \"no concrete model\" (fall back to the provider display name in\n // ConversationCardFooter) from \"default\" (would lie about what's\n // running on the subprocess).\n llm_model: isAcp\n ? resolveEffectiveAcpModel({\n runtimeName: info.current_model_name,\n runtimeId: info.current_model_id,\n configured: info.agent?.acp_model,\n sdkLlm: info.agent?.llm?.model,\n })\n : (info.agent?.llm?.model ?? DEFAULT_SETTINGS.llm_model),\n metrics: info.metrics\n ? {\n accumulated_cost: info.metrics.accumulated_cost ?? null,\n max_budget_per_task: info.metrics.max_budget_per_task ?? null,\n accumulated_token_usage: info.metrics.accumulated_token_usage\n ? {\n prompt_tokens:\n info.metrics.accumulated_token_usage.prompt_tokens ?? 0,\n completion_tokens:\n info.metrics.accumulated_token_usage.completion_tokens ?? 0,\n cache_read_tokens:\n info.metrics.accumulated_token_usage.cache_read_tokens ?? 0,\n cache_write_tokens:\n info.metrics.accumulated_token_usage.cache_write_tokens ?? 0,\n context_window:\n info.metrics.accumulated_token_usage.context_window ?? 0,\n per_turn_token:\n info.metrics.accumulated_token_usage.per_turn_token ?? 0,\n }\n : null,\n }\n : null,\n created_at: info.created_at,\n updated_at: info.updated_at,\n execution_status:\n (info.execution_status as AppConversation[\"execution_status\"]) ??\n ExecutionStatus.IDLE,\n sandbox_status: (info.sandbox_status as SandboxStatus | null) ?? null,\n conversation_url: toConversationUrl(info.id),\n session_api_key: getAgentServerClientOptions().apiKey ?? null,\n sandbox_id: null,\n workspace: {\n working_dir: info.workspace?.working_dir ?? getAgentServerWorkingDir(),\n },\n public: false,\n sub_conversation_ids: [],\n };\n}\n\nexport function toConversationPage(data: {\n items: DirectConversationInfo[];\n next_page_id?: string | null;\n}): AppConversationPage {\n return {\n items: data.items.map(toAppConversation),\n next_page_id: data.next_page_id ?? null,\n };\n}\n\ntype SettingsRecord = Record<string, unknown>;\n\ninterface AgentToolSpec {\n name: string;\n params: SettingsRecord;\n}\n\ntype AgentSettingsPayload = SettingsRecord & {\n llm?: SettingsRecord;\n agent_context: SettingsRecord;\n tools?: AgentToolSpec[];\n};\n\ninterface LocalWorkspacePayload {\n kind: \"LocalWorkspace\";\n working_dir: string;\n}\n\ninterface InitialMessagePayload {\n role: \"user\";\n content: Array<{ type: \"text\"; text: string }>;\n run: true;\n}\n\ntype ConversationSettingsPayload = SettingsRecord & {\n workspace: LocalWorkspacePayload;\n initial_message?: InitialMessagePayload;\n};\n\nconst ACP_SETTINGS_KEYS = [\n \"acp_command\",\n \"acp_args\",\n \"acp_env\",\n \"acp_model\",\n \"acp_session_mode\",\n \"acp_prompt_timeout\",\n] as const;\n\nexport const ACP_SERVER_TAG_KEY = \"acpserver\";\n\nconst CONVERSATION_SETTINGS_METADATA_KEYS = new Set([\n \"schema_version\",\n \"agent_settings\",\n \"workspace\",\n \"conversation_id\",\n \"initial_message\",\n \"plugins\",\n]);\n\nfunction toRecord(value: unknown): SettingsRecord {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n return {};\n }\n\n return structuredClone(value as SettingsRecord);\n}\n\nfunction normalizeSecretString(value: unknown): string | undefined {\n if (typeof value !== \"string\") {\n return undefined;\n }\n\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction getConversationConfirmationPolicy(\n conversationSettings: SettingsRecord,\n) {\n if (conversationSettings.confirmation_mode !== true) {\n return { kind: \"NeverConfirm\" };\n }\n\n if (conversationSettings.security_analyzer === \"llm\") {\n return { kind: \"ConfirmRisky\", threshold: \"HIGH\", confirm_unknown: true };\n }\n\n return { kind: \"AlwaysConfirm\" };\n}\n\nfunction getConversationSecurityAnalyzer(conversationSettings: SettingsRecord) {\n switch (conversationSettings.security_analyzer) {\n case \"llm\":\n return { kind: \"LLMSecurityAnalyzer\" };\n case \"pattern\":\n return { kind: \"PatternSecurityAnalyzer\" };\n case \"policy_rail\":\n return { kind: \"PolicyRailSecurityAnalyzer\" };\n default:\n return undefined;\n }\n}\n\nfunction isToolRecord(\n value: unknown,\n): value is { name: string; params?: unknown } {\n return (\n !!value &&\n typeof value === \"object\" &&\n !Array.isArray(value) &&\n typeof (value as { name?: unknown }).name === \"string\"\n );\n}\n\nfunction shouldIncludeTool(name: string, agentSettings: SettingsRecord) {\n if (name === BROWSER_TOOL_SET_NAME) {\n return browserToolsEnabled() && isAgentServerToolAvailable(name);\n }\n\n if (name === TASK_TOOL_SET_NAME) {\n return (\n agentSettings.enable_sub_agents === true &&\n isAgentServerToolAvailable(name)\n );\n }\n\n return true;\n}\n\nfunction getAgentTools(agentSettings: SettingsRecord): AgentToolSpec[] {\n const tools = new Map<string, AgentToolSpec>();\n\n for (const name of DEFAULT_TOOL_NAMES) {\n tools.set(name, { name, params: {} });\n }\n\n for (const name of [BROWSER_TOOL_SET_NAME, TASK_TOOL_SET_NAME]) {\n if (shouldIncludeTool(name, agentSettings)) {\n tools.set(name, { name, params: {} });\n }\n }\n\n const configuredTools = agentSettings.tools;\n if (\n Array.isArray(configuredTools) &&\n configuredTools.every((tool) => isToolRecord(tool))\n ) {\n for (const tool of configuredTools) {\n if (shouldIncludeTool(tool.name, agentSettings)) {\n tools.set(tool.name, {\n name: tool.name,\n params: toRecord(tool.params),\n });\n }\n }\n }\n\n return Array.from(tools.values());\n}\n\nfunction buildInitialMessage(\n query?: string,\n conversationInstructions?: string,\n): InitialMessagePayload | null {\n const parts = [query?.trim(), conversationInstructions?.trim()].filter(\n Boolean,\n );\n if (parts.length === 0) {\n return null;\n }\n\n return {\n role: \"user\",\n content: [{ type: \"text\", text: parts.join(\"\\n\\n\") }],\n run: true,\n };\n}\n\nfunction buildAgentContext(agentSettings: SettingsRecord): SettingsRecord {\n const runtimeServicesSuffix = buildRuntimeServicesSystemSuffix();\n return {\n ...toRecord(agentSettings.agent_context),\n load_public_skills: shouldLoadPublicSkills(),\n load_user_skills: true,\n load_project_skills: true,\n ...(runtimeServicesSuffix\n ? { system_message_suffix: runtimeServicesSuffix }\n : {}),\n };\n}\n\nfunction isAcpAgent(settings: Settings): boolean {\n const agentSettings = toRecord(settings.agent_settings);\n return agentSettings.agent_kind === \"acp\";\n}\n\nfunction getAcpServerTag(settings: Settings): string | undefined {\n const agentSettings = toRecord(settings.agent_settings);\n const value = agentSettings.acp_server;\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction resolveAcpCommand(agentSettings: SettingsRecord): unknown {\n const cmd = agentSettings.acp_command;\n const isEmpty = Array.isArray(cmd) && cmd.length === 0;\n const noCommand = cmd === undefined;\n if (!isEmpty && !noCommand) {\n return cmd;\n }\n\n const serverKey =\n typeof agentSettings.acp_server === \"string\"\n ? agentSettings.acp_server\n : undefined;\n const provider = getAcpProvider(serverKey);\n return provider ? [...provider.default_command] : cmd;\n}\n\nfunction buildConfiguredAcpAgentSettings(\n settings: Settings,\n): AgentSettingsPayload {\n const agentSettings = toRecord(settings.agent_settings);\n const payload: AgentSettingsPayload = {\n agent_kind: \"acp\",\n agent_context: buildAgentContext(agentSettings),\n };\n\n for (const key of ACP_SETTINGS_KEYS) {\n // ``acp_model`` is resolved separately below so a saved ``null`` still\n // falls back to the provider's default rather than being dropped.\n if (key === \"acp_model\") continue;\n const value =\n key === \"acp_command\"\n ? resolveAcpCommand(agentSettings)\n : agentSettings[key];\n if (value !== undefined && value !== null) {\n payload[key] = value;\n }\n }\n\n // Saved settings may carry ``acp_model: null`` (existing users predating\n // the default-model registry, or saved fields the agent-server stripped).\n // Fall back to the provider's ``default_model`` so the conversation starts\n // with whatever the Settings → Agent UI shows — without that, the form's\n // displayed default would silently not take effect at runtime until the\n // user re-saved the page.\n const serverKey =\n typeof agentSettings.acp_server === \"string\"\n ? agentSettings.acp_server\n : undefined;\n const provider = getAcpProvider(serverKey);\n const effectiveModel = resolveEffectiveAcpModel({\n configured: agentSettings.acp_model as string | null | undefined,\n providerDefault: provider?.default_model,\n });\n if (effectiveModel) {\n payload.acp_model = effectiveModel;\n }\n\n return payload;\n}\n\nfunction buildConfiguredOpenHandsAgentSettings(\n settings: Settings,\n): AgentSettingsPayload {\n const agentSettings = toRecord(settings.agent_settings);\n const llm = toRecord(agentSettings.llm);\n\n llm.model =\n typeof llm.model === \"string\" && llm.model.trim().length > 0\n ? llm.model\n : DEFAULT_SETTINGS.llm_model;\n\n const apiKey = normalizeSecretString(llm.api_key);\n if (apiKey) {\n llm.api_key = apiKey;\n } else {\n delete llm.api_key;\n }\n\n const baseUrl = normalizeSecretString(llm.base_url);\n if (baseUrl) {\n llm.base_url = baseUrl;\n } else {\n delete llm.base_url;\n }\n\n const mcpConfig = toRecord(agentSettings.mcp_config);\n if (Object.keys(mcpConfig).length === 0 || !(\"mcpServers\" in mcpConfig)) {\n delete agentSettings.mcp_config;\n }\n\n delete agentSettings.acp_server;\n for (const key of ACP_SETTINGS_KEYS) {\n delete agentSettings[key];\n }\n\n return {\n ...agentSettings,\n llm,\n agent_context: buildAgentContext(agentSettings),\n tools: getAgentTools(agentSettings),\n };\n}\n\nfunction buildConfiguredAgentSettings(\n settings: Settings,\n): AgentSettingsPayload {\n return isAcpAgent(settings)\n ? buildConfiguredAcpAgentSettings(settings)\n : buildConfiguredOpenHandsAgentSettings(settings);\n}\n\nfunction buildConfiguredConversationSettings(options: {\n settings: Settings;\n query?: string;\n conversationInstructions?: string;\n plugins?: PluginSpec[];\n workingDir?: string;\n}): ConversationSettingsPayload {\n const { settings, query, conversationInstructions, plugins, workingDir } =\n options;\n const conversationSettings = toRecord(settings.conversation_settings);\n const initialMessage = buildInitialMessage(query, conversationInstructions);\n\n CONVERSATION_SETTINGS_METADATA_KEYS.forEach(\n (key) => delete conversationSettings[key],\n );\n\n const payload: ConversationSettingsPayload = {\n ...conversationSettings,\n workspace: {\n kind: \"LocalWorkspace\",\n working_dir: workingDir ?? getAgentServerWorkingDir(),\n },\n ...(initialMessage ? { initial_message: initialMessage } : {}),\n ...(plugins?.length\n ? {\n plugins: plugins.map((plugin) => ({\n source: plugin.source,\n ...(plugin.ref ? { ref: plugin.ref } : {}),\n ...(plugin.repo_path ? { repo_path: plugin.repo_path } : {}),\n })),\n }\n : {}),\n };\n\n return payload;\n}\n\ninterface LookupSecret {\n kind: \"LookupSecret\";\n url: string;\n headers?: Record<string, string>;\n description?: string;\n}\n\ntype StartConversationPayload = Record<string, unknown> & {\n agent_settings: AgentSettingsPayload;\n workspace: LocalWorkspacePayload;\n confirmation_policy: SettingsRecord;\n security_analyzer?: SettingsRecord;\n initial_message?: InitialMessagePayload;\n max_iterations: number;\n stuck_detection: true;\n autotitle: true;\n worktree: true;\n secrets_encrypted?: true;\n conversation_id?: string;\n secrets?: Record<string, LookupSecret>;\n tags?: Record<string, string>;\n tool_module_qualnames?: Record<string, string>;\n};\n\nexport interface StartConversationOptions {\n settings: Settings;\n query?: string;\n conversationInstructions?: string;\n plugins?: PluginSpec[];\n conversationId?: string;\n workingDir?: string;\n encryptedAgentSettings?: Record<string, SettingsValue>;\n encryptedConversationSettings?: Record<string, SettingsValue>;\n secretsEncrypted?: boolean;\n customSecrets?: Array<{ name: string; description?: string }>;\n}\n\nexport function buildStartConversationRequest(\n options: StartConversationOptions,\n): StartConversationPayload {\n const sourceAgentSettings = options.encryptedAgentSettings\n ? { ...options.settings, agent_settings: options.encryptedAgentSettings }\n : options.settings;\n\n const acpMode = isAcpAgent(sourceAgentSettings);\n const agentSettings = buildConfiguredAgentSettings(sourceAgentSettings);\n const acpServerTag = acpMode\n ? getAcpServerTag(sourceAgentSettings)\n : undefined;\n\n const sourceConversationOptions = options.encryptedConversationSettings\n ? {\n ...options,\n settings: {\n ...options.settings,\n conversation_settings: options.encryptedConversationSettings,\n },\n }\n : options;\n\n const conversationSettings = buildConfiguredConversationSettings(\n sourceConversationOptions,\n );\n\n const payload: StartConversationPayload = {\n agent_settings: agentSettings,\n workspace: conversationSettings.workspace,\n confirmation_policy:\n getConversationConfirmationPolicy(conversationSettings),\n max_iterations:\n typeof conversationSettings.max_iterations === \"number\"\n ? conversationSettings.max_iterations\n : 500,\n stuck_detection: true,\n autotitle: true,\n worktree: true,\n };\n\n if (acpServerTag) {\n payload.tags = { [ACP_SERVER_TAG_KEY]: acpServerTag };\n }\n\n if (options.secretsEncrypted) {\n payload.secrets_encrypted = true;\n }\n\n if (options.conversationId) {\n payload.conversation_id = options.conversationId;\n }\n\n const securityAnalyzer =\n getConversationSecurityAnalyzer(conversationSettings);\n if (securityAnalyzer) {\n payload.security_analyzer = securityAnalyzer;\n }\n\n if (conversationSettings.initial_message) {\n payload.initial_message = conversationSettings.initial_message;\n }\n\n if (conversationSettings.plugins) {\n payload.plugins = conversationSettings.plugins;\n }\n\n if (conversationSettings.hook_config) {\n payload.hook_config = conversationSettings.hook_config;\n }\n\n payload.tool_module_qualnames = {\n [CANVAS_UI_TOOL_NAME]: CANVAS_UI_TOOL_MODULE,\n ...((conversationSettings.tool_module_qualnames as\n | Record<string, string>\n | undefined) ?? {}),\n };\n\n if (conversationSettings.agent_definitions) {\n payload.agent_definitions = conversationSettings.agent_definitions;\n }\n\n if (options.customSecrets && options.customSecrets.length > 0) {\n const backend = getEffectiveLocalBackend();\n const headers = buildAuthHeaders(backend);\n\n const secrets: Record<string, LookupSecret> = {};\n for (const secret of options.customSecrets) {\n const lookupSecret: LookupSecret = {\n kind: \"LookupSecret\",\n url: `/api/settings/secrets/${encodeURIComponent(secret.name)}`,\n description: secret.description,\n };\n\n if (Object.keys(headers).length > 0) {\n lookupSecret.headers = headers;\n }\n\n secrets[secret.name] = lookupSecret;\n }\n\n payload.secrets = secrets;\n\n if (acpMode) {\n payload.agent_settings.agent_context = {\n ...payload.agent_settings.agent_context,\n secrets,\n };\n }\n }\n\n return payload;\n}\n\nexport async function buildStartConversationRequestWithEncryptedSettings(options: {\n settings: Settings;\n query?: string;\n conversationInstructions?: string;\n plugins?: PluginSpec[];\n conversationId?: string;\n workingDir?: string;\n}): Promise<Record<string, unknown>> {\n const { SecretsService } = await import(\"./secrets-service\");\n\n const [settingsResult, customSecrets] = await Promise.all([\n SettingsService.getSettingsForConversation(),\n SecretsService.getSecrets(),\n ]);\n\n const { agentSettings, conversationSettings, secretsEncrypted } =\n settingsResult;\n\n return buildStartConversationRequest({\n ...options,\n encryptedAgentSettings: agentSettings,\n encryptedConversationSettings: conversationSettings,\n secretsEncrypted,\n customSecrets,\n });\n}\n\nexport function emptyHooksResponse(): GetHooksResponse {\n return { hooks: [] };\n}\n"],"mappings":";;;;;;;;;;;AA4EA,IAAM,IAAsB,aACtB,IAAwB,kBAExB,IAAqB;CACzB;CACA;CACA;CACA;CACD,EACK,IAAwB,oBACxB,IAAqB;AAE3B,SAAS,IAAsB;AAC7B,QAAO;;AAmCT,SAAS,IAAuD;CAC9D,IAAM,KAAA,KAAA,IAAkD,MAAM;AAC9D,KAAI,CAAC,EAAK,QAAO;AACjB,KAAI;EACF,IAAM,IAAS,KAAK,MAAM,EAAI;AAE9B,SADI,CAAC,KAAU,OAAO,KAAW,WAAiB,OAC3C;SACD;AAGN,SAAO;;;AAYX,SAAgB,IAAuD;CACrE,IAAM,IAAO,GAA0B;AACvC,KAAI,CAAC,GAAM,SAAU;CAErB,IAAM,IAAkB,EAAE;AAS1B,CARA,EAAM,KAAK,qBAAqB,EAC5B,EAAK,OACP,EAAM,KACJ,gEAAgE,EAAK,KAAK,SAC3E,GAED,EAAM,KAAK,oDAAoD,EAEjE,EAAM,KACJ,4EACA,kEACA,GACD;CAED,IAAM,EAAE,iBAAc,YAAS,kBAAe,EAAK,UAI7C,IAAW,EAAK,SAAS,YAAY,EAAK,SAAS;AAoBzD,CAlBI,GAAc,kBAChB,EAAM,KACJ,yBAAyB,EAAa,kBACtC,OAAO,EAAa,eAAe,8CACpC,EAEC,GAAS,kBACX,EAAM,KACJ,cAAc,EAAQ,kBACtB,OAAO,EAAQ,eAAe,oDAC/B,EAEC,GAAU,kBACZ,EAAM,KACJ,eAAe,EAAS,kBACxB,OAAO,EAAS,eAAe,yBAChC,EAEC,GAAY,kBACd,EAAM,KACJ,yBAAyB,EAAW,kBACpC,OAAO,EAAW,eAAe,mCAClC,EACG,EAAW,YACb,EAAM,KAAK,gBAAgB,EAAW,WAAW,EAE/C,EAAW,eACb,EAAM,KAAK,gBAAgB,EAAW,cAAc,EAElD,EAAW,gBAGb,EAAM,KACJ,4CAA4C,EAAW,aAAa,GACrE,IAGH,EAAM,KACJ,mFACD;CAQH,IAAM,IAAiB,GAAc;AAarC,QAZA,EAAM,KACJ,IACA,4EACD,EACG,KACF,EAAM,KACJ,kBAAkB,EAAe,2CACjC,0DACD,EAEH,EAAM,KAAK,sBAAsB,EAE1B,EAAM,KAAK,KAAK;;AAGzB,SAAgB,EAAkB,GAAgC;CAIhE,IAAM,EAAE,YAAS,GAA6B;AAC9C,QAAO,GAAG,EAAK,qBAAqB;;AAMtC,SAAgB,EAA4B,GAAgC;AAC1E,QAAO,gBAAgB,EAAe,MAAM,GAAG,EAAE;;AAGnD,SAAgB,EACd,GACiB;CACjB,IAAM,IAAW,EAA8B,EAAK,GAAG,EAKjD,IAAQ,EAAK,OAAO,SAAS,YAK7B,IAAY,IAAS,EAAK,MAAA,aAA8B,OAAQ;AACtE,QAAO;EACL,IAAI,EAAK;EACT,oBAAoB;EACpB,qBAAqB,GAAU,uBAAuB;EACtD,iBAAiB,GAAU,mBAAmB;EAC9C,cAAc,GAAU,gBAAgB;EACxC,oBAAoB,GAAU,sBAAsB;EACpD,OAAO,EAAK,OAAO,MAAM,GACrB,EAAK,QACL,EAA4B,EAAK,GAAG;EACxC,SAAS;EACT,WAAW,EAAE;EACb,YAAY,IAAQ,QAAQ;EAC5B,YAAY;EAKZ,WAAW,IACP,EAAyB;GACvB,aAAa,EAAK;GAClB,WAAW,EAAK;GAChB,YAAY,EAAK,OAAO;GACxB,QAAQ,EAAK,OAAO,KAAK;GAC1B,CAAC,GACD,EAAK,OAAO,KAAK,SAAS,EAAiB;EAChD,SAAS,EAAK,UACV;GACE,kBAAkB,EAAK,QAAQ,oBAAoB;GACnD,qBAAqB,EAAK,QAAQ,uBAAuB;GACzD,yBAAyB,EAAK,QAAQ,0BAClC;IACE,eACE,EAAK,QAAQ,wBAAwB,iBAAiB;IACxD,mBACE,EAAK,QAAQ,wBAAwB,qBAAqB;IAC5D,mBACE,EAAK,QAAQ,wBAAwB,qBAAqB;IAC5D,oBACE,EAAK,QAAQ,wBAAwB,sBAAsB;IAC7D,gBACE,EAAK,QAAQ,wBAAwB,kBAAkB;IACzD,gBACE,EAAK,QAAQ,wBAAwB,kBAAkB;IAC1D,GACD;GACL,GACD;EACJ,YAAY,EAAK;EACjB,YAAY,EAAK;EACjB,kBACG,EAAK,oBACN,EAAgB;EAClB,gBAAiB,EAAK,kBAA2C;EACjE,kBAAkB,EAAkB,EAAK,GAAG;EAC5C,iBAAiB,GAA6B,CAAC,UAAU;EACzD,YAAY;EACZ,WAAW,EACT,aAAa,EAAK,WAAW,eAAe,GAA0B,EACvE;EACD,QAAQ;EACR,sBAAsB,EAAE;EACzB;;AAGH,SAAgB,EAAmB,GAGX;AACtB,QAAO;EACL,OAAO,EAAK,MAAM,IAAI,EAAkB;EACxC,cAAc,EAAK,gBAAgB;EACpC;;AAgCH,IAAM,IAAoB;CACxB;CACA;CACA;CACA;CACA;CACA;CACD,EAEY,IAAqB,aAE5B,IAAsC,IAAI,IAAI;CAClD;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;AAEF,SAAS,EAAS,GAAgC;AAKhD,QAJI,CAAC,KAAS,OAAO,KAAU,YAAY,MAAM,QAAQ,EAAM,GACtD,EAAE,GAGJ,gBAAgB,EAAwB;;AAGjD,SAAS,EAAsB,GAAoC;AACjE,KAAI,OAAO,KAAU,SACnB;CAGF,IAAM,IAAU,EAAM,MAAM;AAC5B,QAAO,EAAQ,SAAS,IAAI,IAAU,KAAA;;AAGxC,SAAS,EACP,GACA;AASA,QARI,EAAqB,sBAAsB,KAI3C,EAAqB,sBAAsB,QACtC;EAAE,MAAM;EAAgB,WAAW;EAAQ,iBAAiB;EAAM,GAGpE,EAAE,MAAM,iBAAiB,GAPvB,EAAE,MAAM,gBAAgB;;AAUnC,SAAS,EAAgC,GAAsC;AAC7E,SAAQ,EAAqB,mBAA7B;EACE,KAAK,MACH,QAAO,EAAE,MAAM,uBAAuB;EACxC,KAAK,UACH,QAAO,EAAE,MAAM,2BAA2B;EAC5C,KAAK,cACH,QAAO,EAAE,MAAM,8BAA8B;EAC/C,QACE;;;AAIN,SAAS,EACP,GAC6C;AAC7C,QACE,CAAC,CAAC,KACF,OAAO,KAAU,YACjB,CAAC,MAAM,QAAQ,EAAM,IACrB,OAAQ,EAA6B,QAAS;;AAIlD,SAAS,EAAkB,GAAc,GAA+B;AAYtE,QAXI,MAAS,IACJ,GAAqB,IAAI,EAA2B,EAAK,GAG9D,MAAS,IAET,EAAc,sBAAsB,MACpC,EAA2B,EAAK,GAI7B;;AAGT,SAAS,EAAc,GAAgD;CACrE,IAAM,oBAAQ,IAAI,KAA4B;AAE9C,MAAK,IAAM,KAAQ,EACjB,GAAM,IAAI,GAAM;EAAE;EAAM,QAAQ,EAAE;EAAE,CAAC;AAGvC,MAAK,IAAM,KAAQ,CAAC,GAAuB,EAAmB,CAC5D,CAAI,EAAkB,GAAM,EAAc,IACxC,EAAM,IAAI,GAAM;EAAE;EAAM,QAAQ,EAAE;EAAE,CAAC;CAIzC,IAAM,IAAkB,EAAc;AACtC,KACE,MAAM,QAAQ,EAAgB,IAC9B,EAAgB,OAAO,MAAS,EAAa,EAAK,CAAC,OAE9C,IAAM,KAAQ,EACjB,CAAI,EAAkB,EAAK,MAAM,EAAc,IAC7C,EAAM,IAAI,EAAK,MAAM;EACnB,MAAM,EAAK;EACX,QAAQ,EAAS,EAAK,OAAO;EAC9B,CAAC;AAKR,QAAO,MAAM,KAAK,EAAM,QAAQ,CAAC;;AAGnC,SAAS,EACP,GACA,GAC8B;CAC9B,IAAM,IAAQ,CAAC,GAAO,MAAM,EAAE,GAA0B,MAAM,CAAC,CAAC,OAC9D,QACD;AAKD,QAJI,EAAM,WAAW,IACZ,OAGF;EACL,MAAM;EACN,SAAS,CAAC;GAAE,MAAM;GAAQ,MAAM,EAAM,KAAK,OAAO;GAAE,CAAC;EACrD,KAAK;EACN;;AAGH,SAAS,EAAkB,GAA+C;CACxE,IAAM,IAAwB,GAAkC;AAChE,QAAO;EACL,GAAG,EAAS,EAAc,cAAc;EACxC,oBAAoB,GAAwB;EAC5C,kBAAkB;EAClB,qBAAqB;EACrB,GAAI,IACA,EAAE,uBAAuB,GAAuB,GAChD,EAAE;EACP;;AAGH,SAAS,EAAW,GAA6B;AAE/C,QADsB,EAAS,EAAS,eACjC,CAAc,eAAe;;AAGtC,SAAS,EAAgB,GAAwC;CAE/D,IAAM,IADgB,EAAS,EAAS,eAC1B,CAAc;AAC5B,QAAO,OAAO,KAAU,YAAY,EAAM,SAAS,IAAI,IAAQ,KAAA;;AAGjE,SAAS,EAAkB,GAAwC;CACjE,IAAM,IAAM,EAAc;AAG1B,KAAI,EAFY,MAAM,QAAQ,EAAI,IAAI,EAAI,WAAW,MACnC,MAAQ,KAAA,EAExB,QAAO;CAOT,IAAM,IAAW,EAHf,OAAO,EAAc,cAAe,WAChC,EAAc,aACd,KAAA,EACoC;AAC1C,QAAO,IAAW,CAAC,GAAG,EAAS,gBAAgB,GAAG;;AAGpD,SAAS,EACP,GACsB;CACtB,IAAM,IAAgB,EAAS,EAAS,eAAe,EACjD,IAAgC;EACpC,YAAY;EACZ,eAAe,EAAkB,EAAc;EAChD;AAED,MAAK,IAAM,KAAO,GAAmB;AAGnC,MAAI,MAAQ,YAAa;EACzB,IAAM,IACJ,MAAQ,gBACJ,EAAkB,EAAc,GAChC,EAAc;AACpB,EAAI,KAAiC,SACnC,EAAQ,KAAO;;CAcnB,IAAM,IAAW,EAHf,OAAO,EAAc,cAAe,WAChC,EAAc,aACd,KAAA,EACoC,EACpC,IAAiB,EAAyB;EAC9C,YAAY,EAAc;EAC1B,iBAAiB,GAAU;EAC5B,CAAC;AAKF,QAJI,MACF,EAAQ,YAAY,IAGf;;AAGT,SAAS,EACP,GACsB;CACtB,IAAM,IAAgB,EAAS,EAAS,eAAe,EACjD,IAAM,EAAS,EAAc,IAAI;AAEvC,GAAI,QACF,OAAO,EAAI,SAAU,YAAY,EAAI,MAAM,MAAM,CAAC,SAAS,IACvD,EAAI,QACJ,EAAiB;CAEvB,IAAM,IAAS,EAAsB,EAAI,QAAQ;AACjD,CAAI,IACF,EAAI,UAAU,IAEd,OAAO,EAAI;CAGb,IAAM,IAAU,EAAsB,EAAI,SAAS;AACnD,CAAI,IACF,EAAI,WAAW,IAEf,OAAO,EAAI;CAGb,IAAM,IAAY,EAAS,EAAc,WAAW;AAKpD,EAJI,OAAO,KAAK,EAAU,CAAC,WAAW,KAAK,EAAE,gBAAgB,OAC3D,OAAO,EAAc,YAGvB,OAAO,EAAc;AACrB,MAAK,IAAM,KAAO,EAChB,QAAO,EAAc;AAGvB,QAAO;EACL,GAAG;EACH;EACA,eAAe,EAAkB,EAAc;EAC/C,OAAO,EAAc,EAAc;EACpC;;AAGH,SAAS,EACP,GACsB;AACtB,QAAO,EAAW,EAAS,GACvB,EAAgC,EAAS,GACzC,EAAsC,EAAS;;AAGrD,SAAS,EAAoC,GAMb;CAC9B,IAAM,EAAE,aAAU,UAAO,6BAA0B,YAAS,kBAC1D,GACI,IAAuB,EAAS,EAAS,sBAAsB,EAC/D,IAAiB,EAAoB,GAAO,EAAyB;AAwB3E,QAtBA,EAAoC,SACjC,MAAQ,OAAO,EAAqB,GACtC,EAoBM;EAjBL,GAAG;EACH,WAAW;GACT,MAAM;GACN,aAAa,KAAc,GAA0B;GACtD;EACD,GAAI,IAAiB,EAAE,iBAAiB,GAAgB,GAAG,EAAE;EAC7D,GAAI,GAAS,SACT,EACE,SAAS,EAAQ,KAAK,OAAY;GAChC,QAAQ,EAAO;GACf,GAAI,EAAO,MAAM,EAAE,KAAK,EAAO,KAAK,GAAG,EAAE;GACzC,GAAI,EAAO,YAAY,EAAE,WAAW,EAAO,WAAW,GAAG,EAAE;GAC5D,EAAE,EACJ,GACD,EAAE;EAGD;;AAwCT,SAAgB,EACd,GAC0B;CAC1B,IAAM,IAAsB,EAAQ,yBAChC;EAAE,GAAG,EAAQ;EAAU,gBAAgB,EAAQ;EAAwB,GACvE,EAAQ,UAEN,IAAU,EAAW,EAAoB,EACzC,IAAgB,EAA6B,EAAoB,EACjE,IAAe,IACjB,EAAgB,EAAoB,GACpC,KAAA,GAYE,IAAuB,EAVK,EAAQ,gCACtC;EACE,GAAG;EACH,UAAU;GACR,GAAG,EAAQ;GACX,uBAAuB,EAAQ;GAChC;EACF,GACD,EAIH,EAEK,IAAoC;EACxC,gBAAgB;EAChB,WAAW,EAAqB;EAChC,qBACE,EAAkC,EAAqB;EACzD,gBACE,OAAO,EAAqB,kBAAmB,WAC3C,EAAqB,iBACrB;EACN,iBAAiB;EACjB,WAAW;EACX,UAAU;EACX;AAUD,CARI,MACF,EAAQ,OAAO,GAAG,IAAqB,GAAc,GAGnD,EAAQ,qBACV,EAAQ,oBAAoB,KAG1B,EAAQ,mBACV,EAAQ,kBAAkB,EAAQ;CAGpC,IAAM,IACJ,EAAgC,EAAqB;AA4BvD,KA3BI,MACF,EAAQ,oBAAoB,IAG1B,EAAqB,oBACvB,EAAQ,kBAAkB,EAAqB,kBAG7C,EAAqB,YACvB,EAAQ,UAAU,EAAqB,UAGrC,EAAqB,gBACvB,EAAQ,cAAc,EAAqB,cAG7C,EAAQ,wBAAwB;GAC7B,IAAsB;EACvB,GAAK,EAAqB,yBAER,EAAE;EACrB,EAEG,EAAqB,sBACvB,EAAQ,oBAAoB,EAAqB,oBAG/C,EAAQ,iBAAiB,EAAQ,cAAc,SAAS,GAAG;EAE7D,IAAM,IAAU,EADA,GACiB,CAAQ,EAEnC,IAAwC,EAAE;AAChD,OAAK,IAAM,KAAU,EAAQ,eAAe;GAC1C,IAAM,IAA6B;IACjC,MAAM;IACN,KAAK,yBAAyB,mBAAmB,EAAO,KAAK;IAC7D,aAAa,EAAO;IACrB;AAMD,GAJI,OAAO,KAAK,EAAQ,CAAC,SAAS,MAChC,EAAa,UAAU,IAGzB,EAAQ,EAAO,QAAQ;;AAKzB,EAFA,EAAQ,UAAU,GAEd,MACF,EAAQ,eAAe,gBAAgB;GACrC,GAAG,EAAQ,eAAe;GAC1B;GACD;;AAIL,QAAO;;AAGT,eAAsB,EAAmD,GAOpC;CACnC,IAAM,EAAE,sBAAmB,MAAM,OAAO,yBAElC,CAAC,GAAgB,KAAiB,MAAM,QAAQ,IAAI,CACxD,EAAgB,4BAA4B,EAC5C,EAAe,YAAY,CAC5B,CAAC,EAEI,EAAE,kBAAe,yBAAsB,wBAC3C;AAEF,QAAO,EAA8B;EACnC,GAAG;EACH,wBAAwB;EACxB,+BAA+B;EAC/B;EACA;EACD,CAAC;;AAGJ,SAAgB,IAAuC;AACrD,QAAO,EAAE,OAAO,EAAE,EAAE"}
|
package/dist/package.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
require(`./_virtual/_rolldown/runtime.cjs`);var e={name:`@openhands/agent-canvas`,version:`1.0.0-beta.
|
|
1
|
+
require(`./_virtual/_rolldown/runtime.cjs`);var e={name:`@openhands/agent-canvas`,version:`1.0.0-beta.4`,description:`Agent Canvas UI for OpenHands - run AI coding agents with a visual interface`,license:`MIT`,private:!1,type:`module`,repository:{type:`git`,url:`https://github.com/OpenHands/agent-canvas`},homepage:`https://github.com/OpenHands/agent-canvas#readme`,bugs:{url:`https://github.com/OpenHands/agent-canvas/issues`},bin:{"agent-canvas":`bin/agent-canvas.mjs`},engines:{node:`>=22.12.0`},dependencies:{"@heroui/react":`2.8.10`,"@microlink/react-json-view":`1.31.20`,"@monaco-editor/react":`4.7.0`,"@openhands/extensions":`git+https://github.com/OpenHands/extensions.git#e14f740c59b4bfd7369d4bb6aea5eeb33dd05909`,"@openhands/typescript-client":`1.24.3`,"@react-router/node":`7.14.2`,"@react-router/serve":`7.14.2`,"@tailwindcss/vite":`4.2.4`,"@tanstack/react-query":`5.100.9`,"@types/shell-quote":`^1.7.5`,"@uidotdev/usehooks":`2.4.1`,"@xterm/addon-fit":`0.11.0`,"@xterm/xterm":`6.0.0`,axios:`1.16.0`,"class-variance-authority":`0.7.1`,clsx:`2.1.1`,downshift:`9.3.2`,"framer-motion":`12.38.0`,i18next:`26.0.8`,"i18next-browser-languagedetector":`8.2.1`,"i18next-http-backend":`4.0.0`,isbot:`5.1.39`,"lucide-react":`1.14.0`,"monaco-editor":`0.55.1`,"posthog-js":`1.372.6`,react:`19.2.5`,"react-dom":`19.2.5`,"react-hot-toast":`2.6.0`,"react-i18next":`17.0.6`,"react-icons":`5.6.0`,"react-markdown":`10.1.0`,"react-router":`7.14.2`,"react-syntax-highlighter":`16.1.1`,"rehype-raw":`7.0.0`,"rehype-sanitize":`6.0.0`,"remark-breaks":`4.0.0`,"remark-gfm":`4.0.1`,"shell-quote":`^1.8.3`,"sirv-cli":`3.0.1`,"socket.io-client":`4.8.3`,"tailwind-merge":`3.5.0`,"tailwind-scrollbar":`4.0.2`,"unist-util-visit":`5.1.0`,uuid:`14.0.0`,vite:`8.0.10`,zustand:`5.0.12`},scripts:{dev:`node --env-file-if-exists=.env scripts/dev-with-automation.mjs`,"dev:static":`node --env-file-if-exists=.env scripts/dev-static.mjs`,"dev:extra-backend":`node --env-file-if-exists=.env scripts/dev-extra-backend.mjs`,"dev:minimal":`node --env-file-if-exists=.env scripts/dev-safe.mjs`,"dev:frontend":`npm run make-i18n && cross-env VITE_MOCK_API=false react-router dev`,"dev:mock":`npm run make-i18n && cross-env VITE_MOCK_API=true react-router dev`,build:`npm run build:app`,"build:mock":`npm run make-i18n && cross-env VITE_MOCK_API=true react-router build`,start:`npx sirv-cli build/ --single`,test:`npm run make-i18n && vitest run`,"test:e2e":`playwright test --pass-with-no-tests`,"test:e2e:live":`node --env-file-if-exists=.env tests/e2e/live/scripts/run-live-e2e.mjs`,"test:e2e:mock-llm":`playwright test --config=playwright.mock-llm.config.ts`,"test:e2e:mock-llm:docker":`playwright test --config=playwright.mock-llm-docker.config.ts`,"test:e2e:snapshots":`playwright test tests/e2e/snapshots --project=chromium --retries=0`,"test:e2e:snapshots:update":`playwright test tests/e2e/snapshots --project=chromium --update-snapshots`,"test:coverage":`npm run make-i18n && vitest run --coverage`,dev_wsl:`VITE_WATCH_USE_POLLING=true vite`,preview:`vite preview`,"make-i18n":`node scripts/make-i18n-translations.cjs`,prelint:`npm run make-i18n`,lint:`npm run typecheck && eslint src && prettier --check src/**/*.{ts,tsx}`,"lint:fix":`eslint src --fix && prettier --write src/**/*.{ts,tsx}`,prepare:`husky`,typecheck:`react-router typegen && tsc`,"typecheck:staged":`react-router typegen && npx tsc --noEmit --skipLibCheck`,"check-translation-completeness":`node scripts/check-translation-completeness.cjs`,"build:app":`npm run make-i18n && react-router build`,"build:lib":`npm run make-i18n && react-router typegen && cross-env BUILD_LIB=true VITE_APP_ENV=production vite build && tsc -p tsconfig.lib.json`,"build:docker":`node scripts/docker-build.mjs`},"lint-staged":{"src/**/*.{ts,tsx,js}":[`eslint --fix`,`prettier --write`],"src/**/*.{ts,tsx}":[`bash -c 'npm run typecheck:staged'`],"src/**/*":[`npm run check-translation-completeness`]},devDependencies:{"@eslint/eslintrc":`3.3.1`,"@eslint/js":`9.39.4`,"@mswjs/socket.io-binding":`0.2.0`,"@playwright/test":`1.59.1`,"@react-router/dev":`7.14.2`,"@tailwindcss/typography":`0.5.19`,"@tanstack/eslint-plugin-query":`5.100.9`,"@testing-library/dom":`10.4.1`,"@testing-library/jest-dom":`6.9.1`,"@testing-library/react":`16.3.2`,"@testing-library/user-event":`14.6.1`,"@types/mdast":`4.0.4`,"@types/node":`25.6.0`,"@types/react":`19.2.14`,"@types/react-dom":`19.2.3`,"@types/react-syntax-highlighter":`15.5.13`,"@typescript-eslint/eslint-plugin":`8.59.2`,"@typescript-eslint/parser":`8.59.2`,"@vercel/react-router":`1.3.0`,"@vitest/coverage-v8":`4.1.5`,"cross-env":`10.1.0`,eslint:`9.39.4`,"eslint-config-prettier":`10.1.8`,"eslint-import-resolver-typescript":`4.4.4`,"eslint-plugin-i18next":`6.1.4`,"eslint-plugin-import-x":`4.16.2`,"eslint-plugin-jsx-a11y":`6.10.2`,"eslint-plugin-prettier":`5.5.5`,"eslint-plugin-react":`7.37.5`,"eslint-plugin-react-hooks":`7.1.1`,"eslint-plugin-unused-imports":`4.4.1`,globals:`16.5.0`,husky:`9.1.7`,jsdom:`29.1.1`,"lint-staged":`16.4.0`,msw:`2.14.2`,"postcss-prefix-selector":`2.1.1`,prettier:`3.8.3`,tailwindcss:`4.2.4`,typescript:`6.0.3`,"vite-plugin-svgr":`5.2.0`,vitest:`4.1.5`},packageManager:`npm@10.5.0`,volta:{node:`22.12.0`},msw:{workerDirectory:[`public`]},overrides:{dompurify:`3.3.2`},main:`./dist/index.cjs`,module:`./dist/index.js`,types:`./dist/index.d.ts`,files:[`dist`,`bin`,`build`,`config`,`scripts`,`tools`],exports:{".":{types:`./dist/index.d.ts`,import:`./dist/index.js`,require:`./dist/index.cjs`},"./browser":{types:`./dist/components/browser/index.d.ts`,import:`./dist/components/browser/index.js`,require:`./dist/components/browser/index.cjs`},"./conversation":{types:`./dist/components/conversation/index.d.ts`,import:`./dist/components/conversation/index.js`,require:`./dist/components/conversation/index.cjs`},"./files":{types:`./dist/components/files/index.d.ts`,import:`./dist/components/files/index.js`,require:`./dist/components/files/index.cjs`},"./settings":{types:`./dist/components/settings/index.d.ts`,import:`./dist/components/settings/index.js`,require:`./dist/components/settings/index.cjs`},"./sidebar":{types:`./dist/components/sidebar/index.d.ts`,import:`./dist/components/sidebar/index.js`,require:`./dist/components/sidebar/index.cjs`},"./terminal":{types:`./dist/components/terminal/index.d.ts`,import:`./dist/components/terminal/index.js`,require:`./dist/components/terminal/index.cjs`},"./i18n":{types:`./dist/i18n/index.d.ts`,import:`./dist/i18n/index.js`,require:`./dist/i18n/index.cjs`},"./package.json":`./package.json`},peerDependencies:{react:`19.2.5`,"react-dom":`19.2.5`,"react-router":`7.14.2`}};exports.default=e;
|
|
2
2
|
//# sourceMappingURL=package.cjs.map
|
package/dist/package.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"package.cjs","names":[],"sources":["../package.json"],"sourcesContent":["{\n \"name\": \"@openhands/agent-canvas\",\n \"version\": \"1.0.0-beta.
|
|
1
|
+
{"version":3,"file":"package.cjs","names":[],"sources":["../package.json"],"sourcesContent":["{\n \"name\": \"@openhands/agent-canvas\",\n \"version\": \"1.0.0-beta.4\",\n \"description\": \"Agent Canvas UI for OpenHands - run AI coding agents with a visual interface\",\n \"license\": \"MIT\",\n \"private\": false,\n \"type\": \"module\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/OpenHands/agent-canvas\"\n },\n \"homepage\": \"https://github.com/OpenHands/agent-canvas#readme\",\n \"bugs\": {\n \"url\": \"https://github.com/OpenHands/agent-canvas/issues\"\n },\n \"bin\": {\n \"agent-canvas\": \"bin/agent-canvas.mjs\"\n },\n \"engines\": {\n \"node\": \">=22.12.0\"\n },\n \"dependencies\": {\n \"@heroui/react\": \"2.8.10\",\n \"@microlink/react-json-view\": \"1.31.20\",\n \"@monaco-editor/react\": \"4.7.0\",\n \"@openhands/extensions\": \"git+https://github.com/OpenHands/extensions.git#e14f740c59b4bfd7369d4bb6aea5eeb33dd05909\",\n \"@openhands/typescript-client\": \"1.24.3\",\n \"@react-router/node\": \"7.14.2\",\n \"@react-router/serve\": \"7.14.2\",\n \"@tailwindcss/vite\": \"4.2.4\",\n \"@tanstack/react-query\": \"5.100.9\",\n \"@types/shell-quote\": \"^1.7.5\",\n \"@uidotdev/usehooks\": \"2.4.1\",\n \"@xterm/addon-fit\": \"0.11.0\",\n \"@xterm/xterm\": \"6.0.0\",\n \"axios\": \"1.16.0\",\n \"class-variance-authority\": \"0.7.1\",\n \"clsx\": \"2.1.1\",\n \"downshift\": \"9.3.2\",\n \"framer-motion\": \"12.38.0\",\n \"i18next\": \"26.0.8\",\n \"i18next-browser-languagedetector\": \"8.2.1\",\n \"i18next-http-backend\": \"4.0.0\",\n \"isbot\": \"5.1.39\",\n \"lucide-react\": \"1.14.0\",\n \"monaco-editor\": \"0.55.1\",\n \"posthog-js\": \"1.372.6\",\n \"react\": \"19.2.5\",\n \"react-dom\": \"19.2.5\",\n \"react-hot-toast\": \"2.6.0\",\n \"react-i18next\": \"17.0.6\",\n \"react-icons\": \"5.6.0\",\n \"react-markdown\": \"10.1.0\",\n \"react-router\": \"7.14.2\",\n \"react-syntax-highlighter\": \"16.1.1\",\n \"rehype-raw\": \"7.0.0\",\n \"rehype-sanitize\": \"6.0.0\",\n \"remark-breaks\": \"4.0.0\",\n \"remark-gfm\": \"4.0.1\",\n \"shell-quote\": \"^1.8.3\",\n \"sirv-cli\": \"3.0.1\",\n \"socket.io-client\": \"4.8.3\",\n \"tailwind-merge\": \"3.5.0\",\n \"tailwind-scrollbar\": \"4.0.2\",\n \"unist-util-visit\": \"5.1.0\",\n \"uuid\": \"14.0.0\",\n \"vite\": \"8.0.10\",\n \"zustand\": \"5.0.12\"\n },\n \"scripts\": {\n \"dev\": \"node --env-file-if-exists=.env scripts/dev-with-automation.mjs\",\n \"dev:static\": \"node --env-file-if-exists=.env scripts/dev-static.mjs\",\n \"dev:extra-backend\": \"node --env-file-if-exists=.env scripts/dev-extra-backend.mjs\",\n \"dev:minimal\": \"node --env-file-if-exists=.env scripts/dev-safe.mjs\",\n \"dev:frontend\": \"npm run make-i18n && cross-env VITE_MOCK_API=false react-router dev\",\n \"dev:mock\": \"npm run make-i18n && cross-env VITE_MOCK_API=true react-router dev\",\n \"build\": \"npm run build:app\",\n \"build:mock\": \"npm run make-i18n && cross-env VITE_MOCK_API=true react-router build\",\n \"start\": \"npx sirv-cli build/ --single\",\n \"test\": \"npm run make-i18n && vitest run\",\n \"test:e2e\": \"playwright test --pass-with-no-tests\",\n \"test:e2e:live\": \"node --env-file-if-exists=.env tests/e2e/live/scripts/run-live-e2e.mjs\",\n \"test:e2e:mock-llm\": \"playwright test --config=playwright.mock-llm.config.ts\",\n \"test:e2e:mock-llm:docker\": \"playwright test --config=playwright.mock-llm-docker.config.ts\",\n \"test:e2e:snapshots\": \"playwright test tests/e2e/snapshots --project=chromium --retries=0\",\n \"test:e2e:snapshots:update\": \"playwright test tests/e2e/snapshots --project=chromium --update-snapshots\",\n \"test:coverage\": \"npm run make-i18n && vitest run --coverage\",\n \"dev_wsl\": \"VITE_WATCH_USE_POLLING=true vite\",\n \"preview\": \"vite preview\",\n \"make-i18n\": \"node scripts/make-i18n-translations.cjs\",\n \"prelint\": \"npm run make-i18n\",\n \"lint\": \"npm run typecheck && eslint src && prettier --check src/**/*.{ts,tsx}\",\n \"lint:fix\": \"eslint src --fix && prettier --write src/**/*.{ts,tsx}\",\n \"prepare\": \"husky\",\n \"typecheck\": \"react-router typegen && tsc\",\n \"typecheck:staged\": \"react-router typegen && npx tsc --noEmit --skipLibCheck\",\n \"check-translation-completeness\": \"node scripts/check-translation-completeness.cjs\",\n \"build:app\": \"npm run make-i18n && react-router build\",\n \"build:lib\": \"npm run make-i18n && react-router typegen && cross-env BUILD_LIB=true VITE_APP_ENV=production vite build && tsc -p tsconfig.lib.json\",\n \"build:docker\": \"node scripts/docker-build.mjs\"\n },\n \"lint-staged\": {\n \"src/**/*.{ts,tsx,js}\": [\n \"eslint --fix\",\n \"prettier --write\"\n ],\n \"src/**/*.{ts,tsx}\": [\n \"bash -c 'npm run typecheck:staged'\"\n ],\n \"src/**/*\": [\n \"npm run check-translation-completeness\"\n ]\n },\n \"devDependencies\": {\n \"@eslint/eslintrc\": \"3.3.1\",\n \"@eslint/js\": \"9.39.4\",\n \"@mswjs/socket.io-binding\": \"0.2.0\",\n \"@playwright/test\": \"1.59.1\",\n \"@react-router/dev\": \"7.14.2\",\n \"@tailwindcss/typography\": \"0.5.19\",\n \"@tanstack/eslint-plugin-query\": \"5.100.9\",\n \"@testing-library/dom\": \"10.4.1\",\n \"@testing-library/jest-dom\": \"6.9.1\",\n \"@testing-library/react\": \"16.3.2\",\n \"@testing-library/user-event\": \"14.6.1\",\n \"@types/mdast\": \"4.0.4\",\n \"@types/node\": \"25.6.0\",\n \"@types/react\": \"19.2.14\",\n \"@types/react-dom\": \"19.2.3\",\n \"@types/react-syntax-highlighter\": \"15.5.13\",\n \"@typescript-eslint/eslint-plugin\": \"8.59.2\",\n \"@typescript-eslint/parser\": \"8.59.2\",\n \"@vercel/react-router\": \"1.3.0\",\n \"@vitest/coverage-v8\": \"4.1.5\",\n \"cross-env\": \"10.1.0\",\n \"eslint\": \"9.39.4\",\n \"eslint-config-prettier\": \"10.1.8\",\n \"eslint-import-resolver-typescript\": \"4.4.4\",\n \"eslint-plugin-i18next\": \"6.1.4\",\n \"eslint-plugin-import-x\": \"4.16.2\",\n \"eslint-plugin-jsx-a11y\": \"6.10.2\",\n \"eslint-plugin-prettier\": \"5.5.5\",\n \"eslint-plugin-react\": \"7.37.5\",\n \"eslint-plugin-react-hooks\": \"7.1.1\",\n \"eslint-plugin-unused-imports\": \"4.4.1\",\n \"globals\": \"16.5.0\",\n \"husky\": \"9.1.7\",\n \"jsdom\": \"29.1.1\",\n \"lint-staged\": \"16.4.0\",\n \"msw\": \"2.14.2\",\n \"postcss-prefix-selector\": \"2.1.1\",\n \"prettier\": \"3.8.3\",\n \"tailwindcss\": \"4.2.4\",\n \"typescript\": \"6.0.3\",\n \"vite-plugin-svgr\": \"5.2.0\",\n \"vitest\": \"4.1.5\"\n },\n \"packageManager\": \"npm@10.5.0\",\n \"volta\": {\n \"node\": \"22.12.0\"\n },\n \"msw\": {\n \"workerDirectory\": [\n \"public\"\n ]\n },\n \"overrides\": {\n \"dompurify\": \"3.3.2\"\n },\n \"main\": \"./dist/index.cjs\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"files\": [\n \"dist\",\n \"bin\",\n \"build\",\n \"config\",\n \"scripts\",\n \"tools\"\n ],\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\",\n \"require\": \"./dist/index.cjs\"\n },\n \"./browser\": {\n \"types\": \"./dist/components/browser/index.d.ts\",\n \"import\": \"./dist/components/browser/index.js\",\n \"require\": \"./dist/components/browser/index.cjs\"\n },\n \"./conversation\": {\n \"types\": \"./dist/components/conversation/index.d.ts\",\n \"import\": \"./dist/components/conversation/index.js\",\n \"require\": \"./dist/components/conversation/index.cjs\"\n },\n \"./files\": {\n \"types\": \"./dist/components/files/index.d.ts\",\n \"import\": \"./dist/components/files/index.js\",\n \"require\": \"./dist/components/files/index.cjs\"\n },\n \"./settings\": {\n \"types\": \"./dist/components/settings/index.d.ts\",\n \"import\": \"./dist/components/settings/index.js\",\n \"require\": \"./dist/components/settings/index.cjs\"\n },\n \"./sidebar\": {\n \"types\": \"./dist/components/sidebar/index.d.ts\",\n \"import\": \"./dist/components/sidebar/index.js\",\n \"require\": \"./dist/components/sidebar/index.cjs\"\n },\n \"./terminal\": {\n \"types\": \"./dist/components/terminal/index.d.ts\",\n \"import\": \"./dist/components/terminal/index.js\",\n \"require\": \"./dist/components/terminal/index.cjs\"\n },\n \"./i18n\": {\n \"types\": \"./dist/i18n/index.d.ts\",\n \"import\": \"./dist/i18n/index.js\",\n \"require\": \"./dist/i18n/index.cjs\"\n },\n \"./package.json\": \"./package.json\"\n },\n \"peerDependencies\": {\n \"react\": \"19.2.5\",\n \"react-dom\": \"19.2.5\",\n \"react-router\": \"7.14.2\"\n }\n}\n"],"mappings":""}
|
package/dist/package.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
var e = {
|
|
2
2
|
name: "@openhands/agent-canvas",
|
|
3
|
-
version: "1.0.0-beta.
|
|
3
|
+
version: "1.0.0-beta.4",
|
|
4
4
|
description: "Agent Canvas UI for OpenHands - run AI coding agents with a visual interface",
|
|
5
5
|
license: "MIT",
|
|
6
6
|
private: !1,
|
|
@@ -75,6 +75,7 @@ var e = {
|
|
|
75
75
|
"test:e2e": "playwright test --pass-with-no-tests",
|
|
76
76
|
"test:e2e:live": "node --env-file-if-exists=.env tests/e2e/live/scripts/run-live-e2e.mjs",
|
|
77
77
|
"test:e2e:mock-llm": "playwright test --config=playwright.mock-llm.config.ts",
|
|
78
|
+
"test:e2e:mock-llm:docker": "playwright test --config=playwright.mock-llm-docker.config.ts",
|
|
78
79
|
"test:e2e:snapshots": "playwright test tests/e2e/snapshots --project=chromium --retries=0",
|
|
79
80
|
"test:e2e:snapshots:update": "playwright test tests/e2e/snapshots --project=chromium --update-snapshots",
|
|
80
81
|
"test:coverage": "npm run make-i18n && vitest run --coverage",
|