@bytetrue/byspace 0.14.4 → 0.14.5

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.
@@ -26,7 +26,7 @@ await client.close();
26
26
 
27
27
  The public API is the package root. Imports under `@bytetrue/client/internal/*` are unsupported implementation details used by BySpace's own packages.
28
28
 
29
- Read the [SDK documentation](https://paseo.sh/docs/sdk) for agents, workspaces, terminals, provider discovery, events, recipes, and the API reference. Runnable TypeScript patterns also live in [`examples/`](./examples/README.md).
29
+ Read the [SDK documentation](https://app.byspace.cc.cd/docs/sdk) for agents, workspaces, terminals, provider discovery, events, recipes, and the API reference. Runnable TypeScript patterns also live in [`examples/`](./examples/README.md).
30
30
 
31
31
  ## Runtime
32
32
 
@@ -37,7 +37,7 @@ Use a WebSocket URL ending in `/ws`, such as `ws://127.0.0.1:6767/ws`. Pass `pas
37
37
  The client advertises its supported protocol capabilities by default. Optional `capabilities`
38
38
  overrides extend or override that declaration; browser hosting must be supplied by the caller.
39
39
  Connecting alone does not subscribe to agent timelines or catalog events. See the
40
- [event guide](https://paseo.sh/docs/sdk/events) for subscription lifetimes and timeline replacements.
40
+ [event guide](https://app.byspace.cc.cd/docs/sdk/events) for subscription lifetimes and timeline replacements.
41
41
 
42
42
  ## Stability
43
43
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytetrue/client",
3
- "version": "0.14.4",
3
+ "version": "0.14.5",
4
4
  "description": "BySpace client SDK package",
5
5
  "files": [
6
6
  "dist",
@@ -39,8 +39,8 @@
39
39
  "test": "vitest run"
40
40
  },
41
41
  "dependencies": {
42
- "@bytetrue/protocol": "0.14.4",
43
- "@bytetrue/relay": "0.14.4"
42
+ "@bytetrue/protocol": "0.14.5",
43
+ "@bytetrue/relay": "0.14.5"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/node": "^20.9.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytetrue/highlight",
3
- "version": "0.14.4",
3
+ "version": "0.14.5",
4
4
  "files": [
5
5
  "dist",
6
6
  "!dist/**/*.map",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytetrue/protocol",
3
- "version": "0.14.4",
3
+ "version": "0.14.5",
4
4
  "description": "BySpace shared protocol schemas and wire types",
5
5
  "files": [
6
6
  "dist",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytetrue/relay",
3
- "version": "0.14.4",
3
+ "version": "0.14.5",
4
4
  "description": "BySpace relay for bridging daemon and client connections",
5
5
  "files": [
6
6
  "dist",
@@ -1118,7 +1118,7 @@ __d(function(g,r,_i,a,m,_e,d){"use strict";function e(e){return e&&e.__esModule?
1118
1118
  __d(function(g,r,i,a,m,_e,d){"use strict";function e(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(_e,'__esModule',{value:!0}),Object.defineProperty(_e,"default",{enumerable:!0,get:function(){return N}}),Object.defineProperty(_e,"AppOwnership",{enumerable:!0,get:function(){return l.AppOwnership}}),Object.defineProperty(_e,"ExecutionEnvironment",{enumerable:!0,get:function(){return l.ExecutionEnvironment}}),Object.defineProperty(_e,"UserInterfaceIdiom",{enumerable:!0,get:function(){return l.UserInterfaceIdiom}});var n=e(r(d[0])),t=r(d[1]);r(d[2]);var u=e(r(d[3])),l=r(d[4]),o=e(r(d[5]));o.default||console.warn("No native ExponentConstants module found, are you sure the expo-constants's module is linked properly?");const s=(0,t.requireOptionalNativeModule)('ExpoUpdates');let f=null;if(s){let e;s.manifest?e=s.manifest:s.manifestString&&(e=JSON.parse(s.manifestString)),e&&Object.keys(e).length>0&&(f=e)}let c=null;if(u.default.EXDevLauncher){let e;u.default.EXDevLauncher.manifestString&&(e=JSON.parse(u.default.EXDevLauncher.manifestString)),e&&Object.keys(e).length>0&&(c=e)}let p=null;if(o.default&&o.default.manifest){const e=o.default.manifest;p='string'==typeof e?JSON.parse(e):e}let b=f??c??p;const E=o.default||{},{appOwnership:O}=E,x=(0,n.default)(E,["name","appOwnership"]),v=Object.assign({},x,{appOwnership:O??null});function _(e){return!h(e)}function h(e){return'metadata'in e}function S(e=!1){if(!b){const e=null===b?'null':'undefined';if(x.executionEnvironment,l.ExecutionEnvironment.Bare,x.executionEnvironment===l.ExecutionEnvironment.StoreClient||x.executionEnvironment===l.ExecutionEnvironment.Standalone)throw new t.CodedError('ERR_CONSTANTS_MANIFEST_UNAVAILABLE',`Constants.manifest is ${e}, must be an object.`)}return b}Object.defineProperties(v,{__unsafeNoWarnManifest:{get(){const e=S(!0);return e&&_(e)?e:null},enumerable:!1},__unsafeNoWarnManifest2:{get(){const e=S(!0);return e&&h(e)?e:null},enumerable:!1},manifest:{get(){const e=S();return e&&_(e)?e:null},enumerable:!0},manifest2:{get(){const e=S();return e&&h(e)?e:null},enumerable:!0},expoConfig:{get(){const e=S(!0);return e?s&&s.isEmbeddedLaunch?p:h(e)?e.extra?.expoClient??null:_(e)?e:null:null},enumerable:!0},expoGoConfig:{get(){const e=S(!0);return e?h(e)?e.extra?.expoGo??null:_(e)?e:null:null},enumerable:!0},easConfig:{get(){const e=S(!0);return e?h(e)?e.extra?.eas??null:_(e)?e:null:null},enumerable:!0},__rawManifest_TEST:{get:()=>b,set(e){b=e},enumerable:!1}});var N=v},1021,[35,4,25,1022,1023,1024]);
1119
1119
  __d(function(g,r,i,a,m,_e,d){"use strict";Object.defineProperty(_e,'__esModule',{value:!0}),Object.defineProperty(_e,"default",{enumerable:!0,get:function(){return u}});var e,t=r(d[0]),u={UIManager:((e=t)&&e.__esModule?e:{default:e}).default}},1022,[159]);
1120
1120
  __d(function(g,r,i,a,m,e,d){"use strict";var t,n,o;Object.defineProperty(e,'__esModule',{value:!0}),Object.defineProperty(e,"AppOwnership",{enumerable:!0,get:function(){return t}}),Object.defineProperty(e,"ExecutionEnvironment",{enumerable:!0,get:function(){return n}}),Object.defineProperty(e,"UserInterfaceIdiom",{enumerable:!0,get:function(){return o}}),(function(t){t.Expo="expo"})(t||(t={})),(function(t){t.Bare="bare",t.Standalone="standalone",t.StoreClient="storeClient"})(n||(n={})),(function(t){t.Handset="handset",t.Tablet="tablet",t.Desktop="desktop",t.TV="tv",t.Unsupported="unsupported"})(o||(o={}))},1023,[]);
1121
- __d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),Object.defineProperty(e,"default",{enumerable:!0,get:function(){return s}});var n=r(d[0]);const t=(Date.now()+'-'+Math.floor(1e9*Math.random())).toString();function o(){if('undefined'!=typeof navigator&&'string'==typeof navigator.userAgent){const n=navigator.userAgent.toLowerCase();if(n.includes('edge'))return'Edge';if(n.includes('edg'))return'Chromium Edge';if(n.includes('opr')&&'opr'in window&&window.opr)return'Opera';if(n.includes('chrome')&&'chrome'in window&&window.chrome)return'Chrome';if(n.includes('trident'))return'IE';if(n.includes('firefox'))return'Firefox';if(n.includes('safari'))return'Safari'}}var s={get appOwnership(){return null},get executionEnvironment(){return n.ExecutionEnvironment.Bare},get sessionId(){return t},get isHeadless(){return'undefined'==typeof navigator||/\bHeadlessChrome\//.test(navigator.userAgent)},get expoVersion(){return this.manifest.sdkVersion||null},get linkingUri(){return'undefined'!=typeof location?location.origin:''},get expoRuntimeVersion(){return this.expoVersion},get deviceName(){return o()},get systemFonts(){return[]},get statusBarHeight(){return 0},get deviceYearClass(){return null},get manifest(){return"{\"name\":\"BySpace\",\"slug\":\"byspace\",\"version\":\"0.14.4\",\"orientation\":\"portrait\",\"icon\":\"./assets/images/icon.png\",\"scheme\":\"byspace\",\"userInterfaceStyle\":\"automatic\",\"newArchEnabled\":true,\"web\":{\"output\":\"single\",\"favicon\":\"./assets/images/favicon.png\",\"shortName\":\"BySpace\",\"orientation\":\"portrait\",\"name\":\"BySpace\"},\"autolinking\":{\"searchPaths\":[\"../../node_modules\",\"./node_modules\"]},\"experiments\":{\"typedRoutes\":true,\"reactCompiler\":true,\"autolinkingModuleResolution\":true},\"extra\":{\"router\":{}},\"sdkVersion\":\"54.0.0\",\"platforms\":[\"ios\",\"android\",\"web\"]}"},get manifest2(){return null},get experienceUrl(){return'undefined'!=typeof location?location.origin:''},get debugMode(){return!1},getWebViewUserAgentAsync:async()=>'undefined'!=typeof navigator?navigator.userAgent:null}},1024,[1023]);
1121
+ __d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),Object.defineProperty(e,"default",{enumerable:!0,get:function(){return s}});var n=r(d[0]);const t=(Date.now()+'-'+Math.floor(1e9*Math.random())).toString();function o(){if('undefined'!=typeof navigator&&'string'==typeof navigator.userAgent){const n=navigator.userAgent.toLowerCase();if(n.includes('edge'))return'Edge';if(n.includes('edg'))return'Chromium Edge';if(n.includes('opr')&&'opr'in window&&window.opr)return'Opera';if(n.includes('chrome')&&'chrome'in window&&window.chrome)return'Chrome';if(n.includes('trident'))return'IE';if(n.includes('firefox'))return'Firefox';if(n.includes('safari'))return'Safari'}}var s={get appOwnership(){return null},get executionEnvironment(){return n.ExecutionEnvironment.Bare},get sessionId(){return t},get isHeadless(){return'undefined'==typeof navigator||/\bHeadlessChrome\//.test(navigator.userAgent)},get expoVersion(){return this.manifest.sdkVersion||null},get linkingUri(){return'undefined'!=typeof location?location.origin:''},get expoRuntimeVersion(){return this.expoVersion},get deviceName(){return o()},get systemFonts(){return[]},get statusBarHeight(){return 0},get deviceYearClass(){return null},get manifest(){return"{\"name\":\"BySpace\",\"slug\":\"byspace\",\"version\":\"0.14.5\",\"orientation\":\"portrait\",\"icon\":\"./assets/images/icon.png\",\"scheme\":\"byspace\",\"userInterfaceStyle\":\"automatic\",\"newArchEnabled\":true,\"web\":{\"output\":\"single\",\"favicon\":\"./assets/images/favicon.png\",\"shortName\":\"BySpace\",\"orientation\":\"portrait\",\"name\":\"BySpace\"},\"autolinking\":{\"searchPaths\":[\"../../node_modules\",\"./node_modules\"]},\"experiments\":{\"typedRoutes\":true,\"reactCompiler\":true,\"autolinkingModuleResolution\":true},\"extra\":{\"router\":{}},\"sdkVersion\":\"54.0.0\",\"platforms\":[\"ios\",\"android\",\"web\"]}"},get manifest2(){return null},get experienceUrl(){return'undefined'!=typeof location?location.origin:''},get debugMode(){return!1},getWebViewUserAgentAsync:async()=>'undefined'!=typeof navigator?navigator.userAgent:null}},1024,[1023]);
1122
1122
  __d(function(g,r,i,a,m,_e,d){"use strict";Object.defineProperty(_e,'__esModule',{value:!0}),Object.defineProperty(_e,"default",{enumerable:!0,get:function(){return u}});var e,t=r(d[0]),n=r(d[1]),o=(e=n)&&e.__esModule?e:{default:e};async function u(){if(!o.default.unregisterForNotificationsAsync)throw new t.UnavailabilityError('ExpoNotifications','unregisterForNotificationsAsync');return o.default.unregisterForNotificationsAsync()}},1025,[4,1026]);
1123
1123
  __d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),Object.defineProperty(e,"default",{enumerable:!0,get:function(){return n}}),r(d[0]);let t=!1;var n={addListener:()=>(t||(console.warn("[expo-notifications] Listening to push token changes is not yet fully supported on web. Adding a listener will have no effect."),t=!0),{remove:()=>{}}),removeListener:()=>{},removeAllListeners:()=>{},emit:()=>{},listenerCount:()=>0}},1026,[4]);
1124
1124
  __d(function(g,r,i,a,m,_e,_d){"use strict";function e(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(_e,'__esModule',{value:!0}),Object.defineProperty(_e,"default",{enumerable:!0,get:function(){return u}});var t=(function(e){if(e&&e.__esModule)return e;var t={};return e&&Object.keys(e).forEach(function(o){var n=Object.getOwnPropertyDescriptor(e,o);Object.defineProperty(t,o,n.get?n:{enumerable:!0,get:function(){return e[o]}})}),t.default=e,t})(r(_d[0])),o=e(r(_d[1])),n=r(_d[2]),c=r(_d[3]),s=e(r(_d[4])),d=e(r(_d[5]));const p='https://exp.host/--/api/v2/';async function u(e={}){const s=e.devicePushToken||await(0,d.default)(),u=e.deviceId||await h(),R=e.projectId||o.default.easConfig?.projectId||o.default.expoConfig?.extra?.eas?.projectId;if(!R)throw new n.CodedError('ERR_NOTIFICATIONS_NO_EXPERIENCE_ID',"No \"projectId\" found. If \"projectId\" can't be inferred from the manifest (for instance, in bare workflow), you have to pass it in yourself.");const w=e.applicationId||t.applicationId;if(!w)throw new n.CodedError('ERR_NOTIFICATIONS_NO_APPLICATION_ID',"No \"applicationId\" found. If it can't be inferred from native configuration by expo-application, you have to pass it in yourself.");const O=e.type||y(s),_=e.development||await I(),v=e.baseUrl??p,N=e.url??`${v}push/getExpoPushToken`,x={type:O,deviceId:u.toLowerCase(),development:_,appId:w,deviceToken:E(s),projectId:R},T=await fetch(N,{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify(x)}).catch(e=>{throw new n.CodedError('ERR_NOTIFICATIONS_NETWORK_ERROR',`Error encountered while fetching Expo token: ${e}.`)});if(!T.ok){const e=T.statusText||T.status;let t;try{t=await T.text()}catch{}throw new n.CodedError('ERR_NOTIFICATIONS_SERVER_ERROR',`Error encountered while fetching Expo token, expected an OK response, received: ${e} (body: "${t}").`)}const b=l(await f(T));try{e.url||e.baseUrl?console.debug("[expo-notifications] Since the URL endpoint to register in has been customized in the options, expo-notifications won't try to auto-update the device push token on the server."):await(0,c.setAutoServerRegistrationEnabledAsync)(!0)}catch(e){console.warn('[expo-notifications] Could not enable automatically registering new device tokens with the Expo notification service',e)}return{type:'expo',data:b}}async function f(e){try{return await e.json()}catch{try{throw new n.CodedError('ERR_NOTIFICATIONS_SERVER_ERROR',`Expected a JSON response from server when fetching Expo token, received body: ${JSON.stringify(await e.text())}.`)}catch{throw new n.CodedError('ERR_NOTIFICATIONS_SERVER_ERROR',`Expected a JSON response from server when fetching Expo token, received response: ${JSON.stringify(e)}.`)}}}function l(e){if(!e||'object'!=typeof e||!e.data||'object'!=typeof e.data||!e.data.expoPushToken||'string'!=typeof e.data.expoPushToken)throw new n.CodedError('ERR_NOTIFICATIONS_SERVER_ERROR',`Malformed response from server, expected "{ data: { expoPushToken: string } }", received: ${JSON.stringify(e,null,2)}.`);return e.data.expoPushToken}async function h(){try{if(!s.default.getInstallationIdAsync)throw new n.UnavailabilityError('ExpoServerRegistrationModule','getInstallationIdAsync');return await s.default.getInstallationIdAsync()}catch(e){throw new n.CodedError('ERR_NOTIF_DEVICE_ID',`Could not have fetched installation ID of the application: ${e}.`)}}function E(e){return'string'==typeof e.data?e.data:JSON.stringify(e.data)}async function I(){return!1}function y(e){switch(e.type){case'ios':return'apns';case'android':return'fcm';default:return e.type}}},1027,[1028,1021,4,1031,1035,1020]);
@@ -15185,7 +15185,7 @@ __d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{v
15185
15185
  __d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),Object.defineProperty(e,"decodeOfferFragmentPayload",{enumerable:!0,get:function(){return n.decodeOfferFragmentPayload}}),Object.defineProperty(e,"buildDaemonWebSocketUrl",{enumerable:!0,get:function(){return t.buildDaemonWebSocketUrl}}),Object.defineProperty(e,"deriveLabelFromEndpoint",{enumerable:!0,get:function(){return t.deriveLabelFromEndpoint}}),Object.defineProperty(e,"extractHostPortFromWebSocketUrl",{enumerable:!0,get:function(){return t.extractHostPortFromWebSocketUrl}}),Object.defineProperty(e,"normalizeHostPort",{enumerable:!0,get:function(){return t.normalizeHostPort}}),Object.defineProperty(e,"parseConnectionUri",{enumerable:!0,get:function(){return t.parseConnectionUri}}),Object.defineProperty(e,"parseHostPort",{enumerable:!0,get:function(){return t.parseHostPort}}),Object.defineProperty(e,"serializeConnectionUri",{enumerable:!0,get:function(){return t.serializeConnectionUri}}),Object.defineProperty(e,"serializeConnectionUriForStorage",{enumerable:!0,get:function(){return t.serializeConnectionUriForStorage}}),Object.defineProperty(e,"shouldUseTlsForDefaultHostedRelay",{enumerable:!0,get:function(){return t.shouldUseTlsForDefaultHostedRelay}}),e.buildRelayWebSocketUrl=function(n){return(0,t.buildRelayWebSocketUrl)(Object.assign({},n,{role:"client"}))};var t=r(d[0]),n=r(d[1])},3540,[3515,3541]);
15186
15186
  __d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),Object.defineProperty(e,"ConnectionOfferV2Schema",{enumerable:!0,get:function(){return t}}),Object.defineProperty(e,"ConnectionOfferSchema",{enumerable:!0,get:function(){return o}}),e.decodeOfferFragmentPayload=l,e.parseConnectionOfferFromUrl=function(n){const t=f(n);if(!t)return null;const c=l(t);return o.parse(c)};var n=r(d[0]);const t=n.z.object({v:n.z.literal(2),serverId:n.z.string().min(1),daemonPublicKeyB64:n.z.string().min(1),relay:n.z.object({endpoint:n.z.string().min(1),useTls:n.z.boolean().optional()}),hostname:n.z.string().min(1).optional()}),o=t;function c(n){const t=n.replace(/-/g,"+").replace(/_/g,"/"),o=t.padEnd(t.length+(4-t.length%4)%4,"="),c=globalThis.atob(o),l=Uint8Array.from(c,n=>n.charCodeAt(0));return new TextDecoder("utf-8",{fatal:!0}).decode(l)}function l(n){const t=c(n);return JSON.parse(t)}const u="#offer=";function f(n){const t=n.trim();if(!t)return null;const o=t.indexOf(u);if(-1===o)return null;const c=t.slice(o+u.length).trim();return c.length>0?c:null}},3541,[1600]);
15187
15187
  __d(function(g,r,i,a,m,_e,d){"use strict";function e(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(_e,'__esModule',{value:!0}),_e.resolveAppVersion=function(){const e=u(t.default?.version);if(e)return e;const o=u(n.default.expoConfig?.version);if(o)return o;const f=u(n.default.manifest?.version);if(f)return f;return null};var n=e(r(d[0])),t=e(r(d[1]));function u(e){if("string"!=typeof e)return null;const n=e.trim();return 0===n.length?null:n}},3542,[1021,3543]);
15188
- __d(function(e,t,r,a,o,i,s){o.exports={name:"@bytetrue/app",version:"0.14.4",private:!0,main:"index.ts",scripts:{start:"npm run start:expo","start:expo":"cross-env APP_VARIANT=development expo start","reset-project":"node ./scripts/reset-project.js","android:clear":"node -e \"require('node:fs').rmSync('android', { recursive: true, force: true })\"",web:"npm --prefix ../.. run build:client && concurrently --kill-others --names protocol,client,expo --prefix-colors yellow,blue,magenta \"npm --prefix ../.. run watch:protocol\" \"npm --prefix ../.. run watch:client\" \"npm run web:expo\"","web:expo":"expo start --web",lint:"expo lint",typecheck:"tsgo --noEmit",test:"vitest run","test:browser":"vitest run --project browser","test:e2e":"playwright test --project=browser","test:e2e:diff-performance":"cross-env BYSPACE_DIFF_PERF_E2E=1 playwright test --project=browser e2e/browser/diff-performance.spec.ts","test:e2e:stream-smoothness":"cross-env BYSPACE_AGENT_STREAM_PERF_E2E=1 playwright test --project=browser e2e/browser/agent-stream-smoothness.spec.ts","test:e2e:real":"cross-env E2E_FORK_BYSPACE_HOME_FROM=../../.dev/byspace-home playwright test --project=real-provider","test:e2e:relay-deployment":"npm --prefix ../.. run build:server && npm --prefix ../.. run build:daemon-web-ui && cross-env E2E_FORK_BYSPACE_HOME_FROM=../../.dev/byspace-home playwright test --project=relay-deployment e2e/browser/relay-deployment-reconnect.real.spec.ts","test:e2e:ui":"playwright test --ui",build:"npm run build:web","build:web":"npm --prefix ../.. run build:app-deps && npm --prefix ../.. run build:config-schema && expo export --platform web","profile:composer-typing":"node ./scripts/profile-composer-typing.mjs","profile:explorer-toggle":"node ./scripts/profile-explorer-toggle.mjs","profile:side-pane":"playwright test --config playwright.profile.config.ts side-pane-performance.spec.ts","profile:workspace-switching":"node ./scripts/profile-workspace-switching.mjs","profile:workspace-tabs":"node ./scripts/profile-workspace-tabs.mjs","deploy:web":"npm run build:web && wrangler pages deploy dist --project-name byspace --branch main","deploy:web:beta":"npm run build:web && wrangler pages deploy dist --project-name byspace-beta --branch main","build:terminal-webview":"node ./scripts/build-terminal-webview-html.mjs","build:mermaid-runtime":"node ./src/components/markdown/fence/mermaid/build-runtime.mjs"},dependencies:{"@bytetrue/client":"*","@bytetrue/highlight":"*","@bytetrue/protocol":"*","@codemirror/commands":"6.10.4","@codemirror/language":"6.12.4","@codemirror/search":"6.7.1","@codemirror/state":"6.7.1","@codemirror/view":"6.43.6","@dnd-kit/core":"^6.3.1","@dnd-kit/sortable":"^10.0.0","@dnd-kit/utilities":"^3.2.2","@floating-ui/react-native":"^0.10.7","@gorhom/bottom-sheet":"^5.2.14","@gorhom/portal":"^1.0.14","@marijn/find-cluster-break":"^1.0.2","@react-native-async-storage/async-storage":"2.2.0","@react-native-masked-view/masked-view":"^0.3.2","@react-native/normalize-colors":"^0.81.5","@react-navigation/native":"^7.1.8","@replit/codemirror-vim":"6.3.0","@shopify/react-native-skia":"2.2.12","@tanstack/react-query":"^5.90.11","@tanstack/react-virtual":"^3.13.21","@xterm/addon-clipboard":"^0.3.0-beta.213","@xterm/addon-fit":"^0.12.0-beta.213","@xterm/addon-image":"^0.10.0-beta.213","@xterm/addon-ligatures":"^0.11.0-beta.213","@xterm/addon-search":"^0.17.0-beta.213","@xterm/addon-unicode11":"^0.10.0-beta.213","@xterm/addon-web-links":"^0.13.0-beta.213","@xterm/addon-webgl":"^0.20.0-beta.212","@xterm/xterm":"^6.1.0-beta.213",buffer:"^6.0.3",expo:"^54.0.18","expo-asset":"~12.0.12","expo-audio":"~1.0.13","expo-build-properties":"^1.0.9","expo-camera":"~17.0.10","expo-clipboard":"~8.0.7","expo-constants":"~18.0.9","expo-crypto":"^15.0.8","expo-dev-client":"^6.0.15","expo-document-picker":"~14.0.8","expo-file-system":"~19.0.17","expo-haptics":"~15.0.7","expo-image":"~3.0.10","expo-image-manipulator":"~14.0.8","expo-image-picker":"^17.0.8","expo-keep-awake":"^15.0.7","expo-linking":"~8.0.8","expo-localization":"~17.0.9","expo-notifications":"^0.32.16","expo-router":"~6.0.13","expo-sharing":"^14.0.8","expo-splash-screen":"~31.0.10","expo-sqlite":"~16.0.10","expo-system-ui":"~6.0.7","fast-deep-equal":"^3.1.3",htmlparser2:"^12.0.0",i18next:"^26.3.0","lucide-react-native":"^0.546.0","markdown-it":"^10.0.0",mermaid:"^11.16.0","mnemonic-id":"^3.2.7",qrcode:"^1.5.4",react:"19.1.0","react-dom":"19.1.0","react-i18next":"^17.0.8","react-native":"0.81.5","react-native-draggable-flatlist":"^4.0.3","react-native-edge-to-edge":"^1.7.0","react-native-gesture-handler":"~2.28.0","react-native-keyboard-controller":"1.21.12","react-native-markdown-display":"^7.0.2","react-native-nitro-modules":"0.35.5","react-native-reanimated":"~4.3.1","react-native-safe-area-context":"~5.6.0","react-native-screens":"~4.16.0","react-native-svg":"^15.14.0","react-native-unistyles":"^3.2.4","react-native-web":"~0.21.0","react-native-webview":"^13.16.0","react-native-worklets":"~0.8.3","tiny-invariant":"^1.3.3",turndown:"^7.2.4","turndown-plugin-gfm":"^1.0.2","use-sync-external-store":"^1.6.0",yaml:"^2.8.4",zod:"^4.4.3",zustand:"^5.0.9"},devDependencies:{"@playwright/test":"^1.56.1","@testing-library/dom":"^10.4.1","@testing-library/react":"^16.3.2","@types/jsdom":"^30.0.0","@types/markdown-it":"^14.1.2","@types/node":"~22.19.0","@types/qrcode":"^1.5.6","@types/react":"~19.2.0","@types/react-dom":"~19.2.0","@types/turndown":"^5.0.6","@types/ws":"^8.18.1","@vitest/browser":"^4.1.7","@vitest/browser-playwright":"^4.1.7","@xterm/headless":"^6.1.0-beta.213",dotenv:"^17.2.3","eas-cli":"^16.24.1",esbuild:"0.28.1",eslint:"^9.25.0","eslint-config-expo":"~10.0.0","expo-gradle-jvmargs":"^1.1.2","fake-indexeddb":"^6.2.5",jsdom:"^20.0.3","material-icon-theme":"^5.32.0",playwright:"^1.56.1","serve-sim":"^0.1.40",typescript:"~5.9.2",vitest:"^4.1.6",wrangler:"^4.105.0",ws:"^8.20.0"},reanimated:{staticFeatureFlags:{FORCE_REACT_RENDER_FOR_SETTLED_ANIMATIONS:!1}}}},3543,[]);
15188
+ __d(function(e,t,r,a,o,i,s){o.exports={name:"@bytetrue/app",version:"0.14.5",private:!0,main:"index.ts",scripts:{start:"npm run start:expo","start:expo":"cross-env APP_VARIANT=development expo start","reset-project":"node ./scripts/reset-project.js","android:clear":"node -e \"require('node:fs').rmSync('android', { recursive: true, force: true })\"",web:"npm --prefix ../.. run build:client && concurrently --kill-others --names protocol,client,expo --prefix-colors yellow,blue,magenta \"npm --prefix ../.. run watch:protocol\" \"npm --prefix ../.. run watch:client\" \"npm run web:expo\"","web:expo":"expo start --web",lint:"expo lint",typecheck:"tsgo --noEmit",test:"vitest run","test:browser":"vitest run --project browser","test:e2e":"playwright test --project=browser","test:e2e:diff-performance":"cross-env BYSPACE_DIFF_PERF_E2E=1 playwright test --project=browser e2e/browser/diff-performance.spec.ts","test:e2e:stream-smoothness":"cross-env BYSPACE_AGENT_STREAM_PERF_E2E=1 playwright test --project=browser e2e/browser/agent-stream-smoothness.spec.ts","test:e2e:real":"cross-env E2E_FORK_BYSPACE_HOME_FROM=../../.dev/byspace-home playwright test --project=real-provider","test:e2e:relay-deployment":"npm --prefix ../.. run build:server && npm --prefix ../.. run build:daemon-web-ui && cross-env E2E_FORK_BYSPACE_HOME_FROM=../../.dev/byspace-home playwright test --project=relay-deployment e2e/browser/relay-deployment-reconnect.real.spec.ts","test:e2e:ui":"playwright test --ui",build:"npm run build:web","build:web":"npm --prefix ../.. run build:app-deps && npm --prefix ../.. run build:config-schema && expo export --platform web","profile:composer-typing":"node ./scripts/profile-composer-typing.mjs","profile:explorer-toggle":"node ./scripts/profile-explorer-toggle.mjs","profile:side-pane":"playwright test --config playwright.profile.config.ts side-pane-performance.spec.ts","profile:workspace-switching":"node ./scripts/profile-workspace-switching.mjs","profile:workspace-tabs":"node ./scripts/profile-workspace-tabs.mjs","deploy:web":"npm run build:web && wrangler pages deploy dist --project-name byspace --branch main","deploy:web:beta":"npm run build:web && wrangler pages deploy dist --project-name byspace-beta --branch main","build:terminal-webview":"node ./scripts/build-terminal-webview-html.mjs","build:mermaid-runtime":"node ./src/components/markdown/fence/mermaid/build-runtime.mjs"},dependencies:{"@bytetrue/client":"*","@bytetrue/highlight":"*","@bytetrue/protocol":"*","@codemirror/commands":"6.10.4","@codemirror/language":"6.12.4","@codemirror/search":"6.7.1","@codemirror/state":"6.7.1","@codemirror/view":"6.43.6","@dnd-kit/core":"^6.3.1","@dnd-kit/sortable":"^10.0.0","@dnd-kit/utilities":"^3.2.2","@floating-ui/react-native":"^0.10.7","@gorhom/bottom-sheet":"^5.2.14","@gorhom/portal":"^1.0.14","@marijn/find-cluster-break":"^1.0.2","@react-native-async-storage/async-storage":"2.2.0","@react-native-masked-view/masked-view":"^0.3.2","@react-native/normalize-colors":"^0.81.5","@react-navigation/native":"^7.1.8","@replit/codemirror-vim":"6.3.0","@shopify/react-native-skia":"2.2.12","@tanstack/react-query":"^5.90.11","@tanstack/react-virtual":"^3.13.21","@xterm/addon-clipboard":"^0.3.0-beta.213","@xterm/addon-fit":"^0.12.0-beta.213","@xterm/addon-image":"^0.10.0-beta.213","@xterm/addon-ligatures":"^0.11.0-beta.213","@xterm/addon-search":"^0.17.0-beta.213","@xterm/addon-unicode11":"^0.10.0-beta.213","@xterm/addon-web-links":"^0.13.0-beta.213","@xterm/addon-webgl":"^0.20.0-beta.212","@xterm/xterm":"^6.1.0-beta.213",buffer:"^6.0.3",expo:"^54.0.18","expo-asset":"~12.0.12","expo-audio":"~1.0.13","expo-build-properties":"^1.0.9","expo-camera":"~17.0.10","expo-clipboard":"~8.0.7","expo-constants":"~18.0.9","expo-crypto":"^15.0.8","expo-dev-client":"^6.0.15","expo-document-picker":"~14.0.8","expo-file-system":"~19.0.17","expo-haptics":"~15.0.7","expo-image":"~3.0.10","expo-image-manipulator":"~14.0.8","expo-image-picker":"^17.0.8","expo-keep-awake":"^15.0.7","expo-linking":"~8.0.8","expo-localization":"~17.0.9","expo-notifications":"^0.32.16","expo-router":"~6.0.13","expo-sharing":"^14.0.8","expo-splash-screen":"~31.0.10","expo-sqlite":"~16.0.10","expo-system-ui":"~6.0.7","fast-deep-equal":"^3.1.3",htmlparser2:"^12.0.0",i18next:"^26.3.0","lucide-react-native":"^0.546.0","markdown-it":"^10.0.0",mermaid:"^11.16.0","mnemonic-id":"^3.2.7",qrcode:"^1.5.4",react:"19.1.0","react-dom":"19.1.0","react-i18next":"^17.0.8","react-native":"0.81.5","react-native-draggable-flatlist":"^4.0.3","react-native-edge-to-edge":"^1.7.0","react-native-gesture-handler":"~2.28.0","react-native-keyboard-controller":"1.21.12","react-native-markdown-display":"^7.0.2","react-native-nitro-modules":"0.35.5","react-native-reanimated":"~4.3.1","react-native-safe-area-context":"~5.6.0","react-native-screens":"~4.16.0","react-native-svg":"^15.14.0","react-native-unistyles":"^3.2.4","react-native-web":"~0.21.0","react-native-webview":"^13.16.0","react-native-worklets":"~0.8.3","tiny-invariant":"^1.3.3",turndown:"^7.2.4","turndown-plugin-gfm":"^1.0.2","use-sync-external-store":"^1.6.0",yaml:"^2.8.4",zod:"^4.4.3",zustand:"^5.0.9"},devDependencies:{"@playwright/test":"^1.56.1","@testing-library/dom":"^10.4.1","@testing-library/react":"^16.3.2","@types/jsdom":"^30.0.0","@types/markdown-it":"^14.1.2","@types/node":"~22.19.0","@types/qrcode":"^1.5.6","@types/react":"~19.2.0","@types/react-dom":"~19.2.0","@types/turndown":"^5.0.6","@types/ws":"^8.18.1","@vitest/browser":"^4.1.7","@vitest/browser-playwright":"^4.1.7","@xterm/headless":"^6.1.0-beta.213",dotenv:"^17.2.3","eas-cli":"^16.24.1",esbuild:"0.28.1",eslint:"^9.25.0","eslint-config-expo":"~10.0.0","expo-gradle-jvmargs":"^1.1.2","fake-indexeddb":"^6.2.5",jsdom:"^20.0.3","material-icon-theme":"^5.32.0",playwright:"^1.56.1","serve-sim":"^0.1.40",typescript:"~5.9.2",vitest:"^4.1.6",wrangler:"^4.105.0",ws:"^8.20.0"},reanimated:{staticFeatureFlags:{FORCE_REACT_RENDER_FOR_SETTLED_ANIMATIONS:!1}}}},3543,[]);
15189
15189
  __d(function(g,r,i,a,m,e,d){"use strict";Object.defineProperty(e,'__esModule',{value:!0}),e.assertDirectTcpConnectionAllowed=w,Object.defineProperty(e,"DaemonConnectionTestError",{enumerable:!0,get:function(){return h}}),e.buildClientConfig=y,e.connectAndProbe=C,e.connectToDaemon=async function(t,o,n=l){return C(await y(t,o?.serverId,o,n),E(t,o),n)};var t=r(d[0]),o=r(d[1]),n=r(d[2]),s=r(d[3]),c=r(d[4]);const l={getClientId:n.getOrCreateClientId,resolveAppVersion:s.resolveAppVersion,createClient:o=>new t.DaemonClient(o)};function u(t){if("string"!=typeof t)return null;const o=t.trim();return o.length>0?o:null}function p(t,o){const n=t&&("transport error"===t.toLowerCase()||"transport closed"===t.toLowerCase()),s=o&&("transport error"===o.toLowerCase()||"transport closed"===o.toLowerCase()||"unable to connect"===o.toLowerCase());return n&&o&&!s?o:t||(o||"Unable to connect")}function f(){return o.isWeb&&"undefined"!=typeof window?{isWeb:!0,protocol:window.location.protocol}:{isWeb:!1,protocol:null}}function w(t,o=f()){if("directTcp"!==t.type||!o.isWeb||"https:"!==o.protocol||t.useTls)return;let n;try{n=(0,c.parseHostPort)(t.endpoint).host.toLowerCase()}catch{return}if("localhost"!==n&&!n.endsWith(".localhost")&&"127.0.0.1"!==n&&"::1"!==n)throw new Error("Insecure direct connections are unavailable from hosted HTTPS pages. Enable SSL or the relay.")}function b(t){if(!t.config.password)return!1;const o=[t.reason,t.lastError].filter(Boolean).join("\n").toLowerCase();return o.includes("401")||o.includes("4001")||o.includes("unauthorized")||o.includes("code 1006")}class h extends Error{constructor(t,o){super(t),this.name="DaemonConnectionTestError",this.reason=o.reason,this.lastError=o.lastError}}async function y(t,o,n,s=l){w(t,n?.browserContext);const u=await s.getClientId(),p=Object.assign({clientId:u,clientType:"mobile",appVersion:s.resolveAppVersion()??void 0,suppressSendErrors:!0,reconnect:{enabled:!1}},n?.capabilities?{capabilities:n.capabilities}:{},n?.trace?{trace:n.trace}:{});if("directSocket"===t.type||"directPipe"===t.type)throw new Error("Socket/pipe daemon transports were desktop-only and are retired (issue 025 A3).");if("remoteSsh"===t.type)throw new Error("Remote SSH access is retired (issue 025 A5).");if("directTcp"===t.type)return Object.assign({},p,{url:(0,c.buildDaemonWebSocketUrl)(t.endpoint,{useTls:t.useTls??!1})},t.password?{password:t.password}:{});if(!o)throw new Error("serverId is required to probe a relay connection");return Object.assign({},p,{url:(0,c.buildRelayWebSocketUrl)({endpoint:t.relayEndpoint,useTls:t.useTls??(0,c.shouldUseTlsForDefaultHostedRelay)(t.relayEndpoint),serverId:o}),e2ee:{enabled:!0,daemonPublicKeyB64:t.daemonPublicKeyB64}})}function C(t,o,n=l){const s=n.createClient(t);return new Promise((n,c)=>{const l=setTimeout(()=>{s.close().catch(()=>{}),c(new h("Connection timed out",{reason:"Connection timed out",lastError:s.lastError??null}))},o);s.connect().then(()=>{clearTimeout(l);const t=s.getLastServerInfoMessage();if(!t)return s.close().catch(()=>{}),void c(new h("Missing server info message",{reason:"Missing server info message",lastError:s.lastError??null}));n({client:s,serverId:t.serverId,hostname:t.hostname})}).catch(o=>{clearTimeout(l);const n=u(o instanceof Error?o.message:String(o)),f=u(s.lastError),w=b({config:t,reason:n,lastError:f})?"Incorrect password":p(n,f);s.close().catch(()=>{}),c(new h(w,{reason:n,lastError:f}))})})}function E(t,o){return o?.timeoutMs?o.timeoutMs:"relay"===t.type?1e4:"remoteSsh"===t.type?18e4:6e3}},3544,[3492,1599,3545,3542,3540]);
15190
15190
  __d(function(g,r,i,a,m,_e,d){"use strict";Object.defineProperty(_e,'__esModule',{value:!0}),_e.createClientIdResolver=s,_e.getOrCreateClientId=async function(){return f.getOrCreate()};var t,e=r(d[0]),n=(t=e)&&t.__esModule?t:{default:t},o=r(d[1]),u=r(d[2]);const c="@byspace:client-id-v1",l=o.z.string().trim().min(1);function s(t){const e=t.storageKey??c;let n=null,o=null;return{async getOrCreate(){if(n)return n;if(o)return o;o=(async()=>{const o=await(0,u.readValidatedString)(t.storage,e,l);if(o)return n=o,o;const c=`cid_${t.generateUuid()}`;return await t.storage.setItem(e,c),n=c,c})();try{return await o}finally{o=null}}}}const f=s({storage:n.default,generateUuid:function(){const t=globalThis.crypto;return t&&"function"==typeof t.randomUUID?t.randomUUID().replace(/-/g,""):`${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`}})},3545,[1587,1600,1681]);
15191
15191
  __d(function(g,r,i,a,m,e,d){"use strict";function n(n){const t=n.probeByConnectionId.get(n.connectionId);return"available"===t?.status?t.latencyMs:null}Object.defineProperty(e,'__esModule',{value:!0}),e.selectBestConnection=function(t){const{candidates:o,probeByConnectionId:c}=t;if(0===o.length)return null;let l=null,u=null;for(const t of o){const o=n({connectionId:t.connectionId,probeByConnectionId:c});null!==o&&((null===u||o<u)&&(l=t.connectionId,u=o))}return l}},3546,[]);
@@ -86,6 +86,6 @@
86
86
  <body>
87
87
  <noscript>You need to enable JavaScript to run this app.</noscript>
88
88
  <div id="root"></div>
89
- <script src="/_expo/static/js/web/index-55d28ed834e87267df08c6f727462c0e.js" defer></script>
89
+ <script src="/_expo/static/js/web/index-a02b4240e2d16a2b83c35c3942514c67.js" defer></script>
90
90
  </body>
91
91
  </html>
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytetrue/server",
3
- "version": "0.14.4",
3
+ "version": "0.14.5",
4
4
  "description": "BySpace backend server",
5
5
  "files": [
6
6
  "dist/server",
@@ -67,10 +67,10 @@
67
67
  "test:e2e:ui": "vitest --ui e2e.test.ts"
68
68
  },
69
69
  "dependencies": {
70
- "@bytetrue/client": "0.14.4",
71
- "@bytetrue/highlight": "0.14.4",
72
- "@bytetrue/protocol": "0.14.4",
73
- "@bytetrue/relay": "0.14.4"
70
+ "@bytetrue/client": "0.14.5",
71
+ "@bytetrue/highlight": "0.14.5",
72
+ "@bytetrue/protocol": "0.14.5",
73
+ "@bytetrue/relay": "0.14.5"
74
74
  },
75
75
  "devDependencies": {
76
76
  "@playwright/test": "^1.56.1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytetrue/byspace",
3
- "version": "0.14.4",
3
+ "version": "0.14.5",
4
4
  "description": "BySpace CLI - control your AI coding agents from the command line",
5
5
  "bin": {
6
6
  "byspace": "bin/byspace"
@@ -30,11 +30,11 @@
30
30
  "@agentclientprotocol/sdk": "^0.17.1",
31
31
  "@anthropic-ai/claude-agent-sdk": "0.3.246",
32
32
  "@anthropic-ai/sdk": "^0.104.2",
33
- "@bytetrue/client": "0.14.4",
34
- "@bytetrue/highlight": "0.14.4",
35
- "@bytetrue/protocol": "0.14.4",
36
- "@bytetrue/relay": "0.14.4",
37
- "@bytetrue/server": "0.14.4",
33
+ "@bytetrue/client": "0.14.5",
34
+ "@bytetrue/highlight": "0.14.5",
35
+ "@bytetrue/protocol": "0.14.5",
36
+ "@bytetrue/relay": "0.14.5",
37
+ "@bytetrue/server": "0.14.5",
38
38
  "@clack/prompts": "^1.0.0",
39
39
  "@codemirror/language": "6.12.4",
40
40
  "@codemirror/legacy-modes": "^6.5.3",
@@ -103,7 +103,7 @@
103
103
  "bugs": {
104
104
  "url": "https://github.com/ByteTrue/byspace/issues"
105
105
  },
106
- "gitHead": "f73809dc4338a5566f9d15157f17f850e9d4a61f",
106
+ "gitHead": "aceb1f81000d3a24feb6c0a16b10c5c0c71f359b",
107
107
  "bundledDependencies": [
108
108
  "@bytetrue/highlight",
109
109
  "@bytetrue/relay",
@@ -1,11 +0,0 @@
1
- import type { PluginRequirements } from "./messages.js";
2
- export declare function validatePluginRequirements(requirements: PluginRequirements | undefined): void;
3
- interface PluginCompatibilityInput {
4
- id: string;
5
- requirements?: PluginRequirements;
6
- version: string | null;
7
- runtime: "daemon" | "app";
8
- }
9
- export declare function assertPluginCompatibility(input: PluginCompatibilityInput): void;
10
- export {};
11
- //# sourceMappingURL=plugin-requirements.d.ts.map
@@ -1,26 +0,0 @@
1
- import parse from "semver/functions/parse.js";
2
- import validRange from "semver/ranges/valid.js";
3
- import satisfies from "semver/functions/satisfies.js";
4
- export function validatePluginRequirements(requirements) {
5
- const range = requirements?.byspace;
6
- if (range !== undefined && (!range.trim() || validRange(range) === null)) {
7
- throw new Error(`Invalid requirements.byspace: ${JSON.stringify(range)}. Use an npm semver range such as ">=0.8.0".`);
8
- }
9
- }
10
- export function assertPluginCompatibility(input) {
11
- validatePluginRequirements(input.requirements);
12
- // COMPAT(plugin-requirements): added in v0.8.0-beta.1; remove after 2027-03-07 once pre-0.8 plugins and catalogs are unsupported. The legacy range excludes 0.8 prereleases and their stable core.
13
- const range = input.requirements?.byspace ?? "<0.8.0";
14
- const version = input.version ? parse(input.version) : null;
15
- if (!version) {
16
- throw new Error(`Cannot check plugin "${input.id}" requirements: BySpace ${input.runtime} version is unknown. Update the ${input.runtime}.`);
17
- }
18
- const stableCore = `${version.major}.${version.minor}.${version.patch}`;
19
- if (satisfies(version, range) || satisfies(stableCore, range))
20
- return;
21
- const action = input.requirements?.byspace === undefined
22
- ? "This plugin has no requirements.byspace and targets BySpace before 0.8. Ask its author to migrate it: https://paseo.sh/docs/plugins/v0.8/migration"
23
- : `Use a compatible plugin version or update the ${input.runtime}.`;
24
- throw new Error(`Plugin "${input.id}" requires BySpace ${range}. Your ${input.runtime} is ${input.version}. ${action}`);
25
- }
26
- //# sourceMappingURL=plugin-requirements.js.map
@@ -1,639 +0,0 @@
1
- ---
2
- name: byspace-plugin
3
- description: Build and manage trusted local BySpace plugins. Use when the user asks to create, edit, install, reload, enable, disable, remove, or troubleshoot a BySpace plugin; add lifecycle hooks; transform agent configuration, environment, MCP servers, or workspace creation; automate permissions or turn follow-ups; add a native surface, sidebar item, or workspace panel; add Command Center items or slash commands; add composer pills or attachment sources; transform, render, or append agent timeline items; contribute a theme; use BySpace from plugin code; or add plugin RPCs.
4
- ---
5
-
6
- # BySpace plugins
7
-
8
- Build or manage the requested plugin directly. Use the current public docs to catch contract changes, but keep working from this skill if the network is unavailable.
9
-
10
- **User's request:** $ARGUMENTS
11
-
12
- ## Check current documentation
13
-
14
- Read the current BySpace plugin documentation before changing a plugin:
15
-
16
- - [Plugin quickstart](https://byspace.sh/docs/plugins.md) ([browser page](https://byspace.sh/docs/plugins))
17
- - [Plugin reference](https://byspace.sh/docs/plugins/v0.8/reference.md) ([browser page](https://byspace.sh/docs/plugins/v0.8/reference))
18
-
19
- Use the repository docs when they disagree with this skill. Do not send the user away to read them instead of completing the work.
20
-
21
- In the BySpace repository, use `public-docs/plugins/v0.8/reference.md` for the checkout's API, including
22
- unreleased changes. Use `docs/plugins.md` for maintainer guidance. Complete contracts belong in the
23
- public docs; this skill indexes the references and examples.
24
-
25
- ## What a plugin can contribute
26
-
27
- Pick the contribution that matches the request. Each row names the registration, when it fits, and where the full contract lives. Most plugins combine several: a slash command that calls an RPC, which appends a timeline row, which a renderer draws.
28
-
29
- | Contribution | Registration | Use it when | Reference |
30
- | ------------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
31
- | Sidebar surface | `addSurface` + `addSidebarItem` | A full screen of plugin UI reachable from the sidebar | reference.md → Surfaces and sidebar items; `plugin-examples/local-plugin` |
32
- | Workspace panel | `addWorkspacePanel` | UI that lives as a tab beside agents, terminals, files, and diffs; `locations: ["explorer"]` for the Explorer | reference.md → Workspace panels |
33
- | Command Center item | `addCommandCenterItem` | A global, workspace, or agent action reachable from ⌘K | reference.md → Command Center items |
34
- | Client slash command | `addSlashCommand` | A `/command args` in the composer that runs plugin code instead of prompting the agent | reference.md → Client slash commands |
35
- | Composer pill | `addComposerPill` | A per-agent button in the composer track bar next to Tasks and Subagents | reference.md → Composer pills |
36
- | Timeline transformer | `addTimelineTransformer` + `addTimelineRenderer` | Replace, explode, or hide a built-in timeline item, including while it streams | reference.md → Timeline items; `plugin-examples/timeline-items`, `plugin-examples/inline-thinking` |
37
- | Timeline row | `byspace.agents.ref(id).timeline.append(...)` | Push a plugin-owned row into an agent timeline from a server handler and update it later | reference.md → Append a timeline row from the daemon |
38
- | Attachment source | `client.addAttachmentSource` + `server.handle` | Let the user attach a searchable external resource, such as an issue, to a prompt | reference.md → Add a composer attachment source; `plugin-examples/linear` |
39
- | Theme | `addTheme` | A light or dark palette under Settings → Appearance | reference.md → Contribute a theme; `plugin-examples/catppuccin` |
40
- | Plugin RPC | `defineRpc` + `server.handle` + `useRpc` | Daemon-side work that is not a normal BySpace operation: vendor APIs, credentials, local files | reference.md → Add plugin-specific backend behavior |
41
- | Lifecycle events | `server.on` | Observe agent/workspace lifecycle, inspect ended turns, and answer permission requests | [Lifecycle hooks](https://byspace.sh/docs/plugins/v0.8/reference.md#lifecycle-hooks) |
42
- | Creation and launch hooks | `server.before` | Change agent config, provider options, MCP servers, environment, or workspace isolation before the operation | [Before hooks](https://byspace.sh/docs/plugins/v0.8/reference.md#before-hooks) |
43
- | BySpace SDK | `useBySpace()` / handler `{ byspace }` | Normal BySpace operations: workspaces, agents, providers, config | reference.md → Use the BySpace SDK |
44
-
45
- | Lifecycle task | Example |
46
- | ------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
47
- | Log all eleven hooks | [lifecycle-logger](https://github.com/ByteTrue/byspace/tree/main/plugin-examples/lifecycle-logger) |
48
- | Follow-ups, permissions, environment, provider switching, worktrees | [lifecycle-actions](https://github.com/ByteTrue/byspace/tree/main/plugin-examples/lifecycle-actions) |
49
- | Inject MCP servers and change Codex sandbox/approval options | [agent-configuration](https://github.com/ByteTrue/byspace/tree/main/plugin-examples/agent-configuration) |
50
-
51
- ## Create the project
52
-
53
- Use an absolute path on the daemon machine. `init` writes files but does not install packages.
54
-
55
- ```bash
56
- byspace plugin init /absolute/path/to/my-plugin
57
- cd /absolute/path/to/my-plugin
58
- npm install
59
- ```
60
-
61
- The generated project contains:
62
-
63
- ```text
64
- my-plugin/
65
- byspace-plugin.json
66
- package.json
67
- tsconfig.json
68
- index.client.tsx
69
- index.server.ts
70
- client/greeting.tsx
71
- server/greeting.ts
72
- shared/greeting.ts
73
- ```
74
-
75
- The manifest supplies the default install ID and supported BySpace versions:
76
-
77
- ```json
78
- { "id": "my-plugin", "requirements": { "byspace": ">=0.8.0" } }
79
- ```
80
-
81
- Keep `requirements.byspace` correct whenever creating or editing a plugin. `init` uses `>=` followed
82
- by the CLI version. Raise the minimum when adopting newer APIs; add an upper bound when a later
83
- BySpace release is incompatible. Use npm semver ranges and explicitly include beta versions when
84
- targeting betas. Missing requirements mean `<0.8.0`; complete the 0.8 entry migration before adding
85
- `>=0.8.0`. Verify compatibility with both the daemon and the app running client contributions.
86
- See [requirements](https://paseo.sh/docs/plugins/v0.8/reference#requirements).
87
-
88
- Each runtime has its own optional entry. A plugin must have at least one. Both entries accept
89
- `.ts` or `.tsx`; use `.tsx` when an entry imports components.
90
-
91
- | Path | Runtime |
92
- | -------------------------------- | ----------------- |
93
- | `index.client.tsx` and `client/` | App |
94
- | `index.server.ts` and `server/` | Daemon subprocess |
95
- | `shared/` | Both |
96
-
97
- Do not put any other code modules in the plugin root.
98
-
99
- A client import of `server/`, a server import of `client/`, and every `node:` import reachable from
100
- client code is a compile error. A relative import to another code file in the plugin root is also a
101
- compile error; move it into `client/`, `server/`, or `shared/`. Shared modules contain Zod contracts
102
- and plain values; they do not import Node or React Native runtime APIs.
103
-
104
- Default-export one contribution function from each entry and return cleanup:
105
-
106
- ```tsx
107
- // index.client.tsx
108
- import type { PluginClientContext } from "@bytetrue/plugin/client";
109
-
110
- export default function contribute(client: PluginClientContext) {
111
- // Register components and client callbacks here.
112
- return () => {};
113
- }
114
- ```
115
-
116
- ```ts
117
- // index.server.ts
118
- import type { PluginServerContext } from "@bytetrue/plugin/server";
119
-
120
- export default function contribute(server: PluginServerContext) {
121
- // Register daemon-side RPC handlers here.
122
- return () => {};
123
- }
124
- ```
125
-
126
- Cleanup can be async. Use it for timers, watchers, sockets, subscriptions, and other resources
127
- created by plugin code. Every client `add*` method returns an idempotent remover. BySpace calls the
128
- entry cleanup first, removes registrations that remain, rejects pending RPCs, closes the plugin
129
- session, and stops the subprocess when the plugin stops.
130
-
131
- ## Add a workspace panel
132
-
133
- Workspace panels live beside agents, terminals, files, and diffs. Plugins run on desktop and
134
- mobile, and BySpace has multiple themes. Every `Text` must take its color from `theme.colors`.
135
- Use `layout.compact` for padding and stacking. Unstyled text is black and fails in dark themes.
136
-
137
- ```tsx
138
- import {
139
- type PluginClientContext,
140
- type PluginWorkspacePanelProps,
141
- useWorkspace,
142
- } from "@bytetrue/plugin/client";
143
- import { useMemo } from "react";
144
- import { Text, View } from "react-native";
145
-
146
- function Overview({ theme, layout, workspaceId }: PluginWorkspacePanelProps) {
147
- const name = useWorkspace(workspaceId, (workspace) => workspace.name);
148
- const styles = useMemo(
149
- () => ({
150
- screen: {
151
- flex: 1,
152
- padding: layout.compact ? 16 : 24,
153
- gap: layout.compact ? 8 : 12,
154
- backgroundColor: theme.colors.surface0,
155
- },
156
- title: { color: theme.colors.foreground, fontSize: layout.compact ? 20 : 24 },
157
- }),
158
- [theme, layout.compact],
159
- );
160
- return (
161
- <View style={styles.screen}>
162
- <Text style={styles.title}>{name}</Text>
163
- </View>
164
- );
165
- }
166
-
167
- export default function contribute(client: PluginClientContext) {
168
- client.addWorkspacePanel({
169
- id: "overview",
170
- title: "Workspace overview",
171
- icon: "PanelsTopLeft",
172
- context: "workspace",
173
- Component: Overview,
174
- });
175
- client.addCommandCenterItem({
176
- id: "open-overview",
177
- title: "Open workspace overview",
178
- icon: "PanelsTopLeft",
179
- context: "workspace",
180
- onSelect({ openPanel }) {
181
- openPanel("overview");
182
- },
183
- });
184
- return () => {};
185
- }
186
- ```
187
-
188
- Use `useWorkspace(id, selector)` and `useAgent(id, selector)`. Selectors are required
189
- and their results use shallow equality. Never select the whole snapshot or add an RPC to discover
190
- the active workspace or agent. Command callbacks receive the selected host's `byspace`, typed
191
- `rpc(contract, input)`, `openSurface(id)`, and contextual `openPanel(id)` capabilities.
192
-
193
- ## Add a sidebar surface
194
-
195
- Plugin surfaces use React Native primitives and work across desktop, browser, iOS, and Android. Register the surface before its sidebar item. Color text from `theme.colors` and pad from `layout.compact`.
196
-
197
- ```tsx
198
- import type { PluginClientContext, PluginSurfaceProps } from "@bytetrue/plugin/client";
199
- import { useMemo, useState } from "react";
200
- import { Pressable, Text, View } from "react-native";
201
-
202
- function Counter({ theme, layout }: PluginSurfaceProps) {
203
- const [count, setCount] = useState(0);
204
- const styles = useMemo(
205
- () => ({
206
- screen: {
207
- flex: 1,
208
- padding: layout.compact ? 16 : 24,
209
- gap: 16,
210
- backgroundColor: theme.colors.surface0,
211
- },
212
- count: { color: theme.colors.foreground, fontSize: layout.compact ? 36 : 48 },
213
- button: { padding: 14, borderRadius: 10, backgroundColor: theme.colors.accent },
214
- buttonText: { color: theme.colors.accentForeground, textAlign: "center" as const },
215
- }),
216
- [theme, layout.compact],
217
- );
218
- return (
219
- <View style={styles.screen}>
220
- <Text style={styles.count}>{count}</Text>
221
- <Pressable
222
- accessibilityRole="button"
223
- accessibilityLabel={`Increment counter, currently ${count}`}
224
- onPress={() => setCount((value) => value + 1)}
225
- style={styles.button}
226
- >
227
- <Text style={styles.buttonText}>Count me in</Text>
228
- </Pressable>
229
- </View>
230
- );
231
- }
232
-
233
- export default function contribute(client: PluginClientContext) {
234
- client.addSurface("main", Counter);
235
- client.addSidebarItem({
236
- id: "main",
237
- title: "Counter",
238
- icon: "ListPlus",
239
- surface: "main",
240
- });
241
- return () => {};
242
- }
243
- ```
244
-
245
- Icons are Lucide icon names. `theme` is a typed `PluginTheme` on every surface and panel. Primary text uses `theme.colors.foreground`; labels use `theme.colors.foregroundMuted`; the root view uses `theme.colors.surface0`. `layout.compact` is true on mobile and narrow windows. BySpace owns the route, header, host picker, close action, error boundary, and per-installation query client.
246
-
247
- Before writing imports, classify each module as shared, client, or server. Follow the
248
- [SDK import boundaries](https://byspace.sh/docs/plugins/v0.8/reference.md#runtime-modules), including
249
- transitive and type dependencies. The root is shared-only; hooks and client contexts belong to
250
- `@bytetrue/plugin/client`, server contexts to `/server`, and host UI to `/client/react-native` or `/client/ui`.
251
- Install dependencies locally for typechecking; BySpace supplies host runtime modules. JSX uses the
252
- automatic runtime. Do not import `/client/host` from plugin code.
253
-
254
- ## Works on mobile
255
-
256
- Before reporting a plugin done:
257
-
258
- - Use React Native primitives only: `View`, `Text`, `Pressable`, `ScrollView`, and `TextInput`.
259
- - Do not use HTML elements, `className`, CSS strings, or `onClick`.
260
- - Do not put `"DOM"` in `tsconfig.json` or use `/// <reference lib="dom" />`. Put DOM globals only in
261
- `client/web.ts`, declare only what that module uses, gate every export on `Platform.OS === "web"`,
262
- and provide the native alternative or a no-op.
263
- - Take colors from `theme.colors`.
264
- - Check the compact layout.
265
-
266
- Run this audit on `client/`:
267
-
268
- ```bash
269
- rg -n "document\.|window\.|localStorage|navigator\.|<[a-z]+[ >]|className=|onClick=" client/
270
- ```
271
-
272
- A hit outside `client/web.ts` is a bug.
273
-
274
- ## Choose the correct API
275
-
276
- Use the existing BySpace SDK for normal BySpace operations. Use plugin RPC only for plugin-specific backend behavior.
277
-
278
- ### Call BySpace from a surface
279
-
280
- `useBySpace()` borrows the selected host's current connection. Never create another client inside a surface.
281
-
282
- ```tsx
283
- import { useBySpace } from "@bytetrue/plugin/client";
284
-
285
- function PullRequestAction() {
286
- const byspace = useBySpace();
287
-
288
- async function createReviewWorkspace() {
289
- const workspace = await byspace.workspaces.create({
290
- title: "Review PR 42",
291
- source: {
292
- kind: "worktree",
293
- cwd: "/absolute/path/to/repository",
294
- action: "checkout",
295
- checkoutSource: { kind: "change_request", forge: "github", number: 42 },
296
- },
297
- });
298
- await workspace.agents.create({
299
- config: { provider: "codex/gpt-5.5" },
300
- prompt: "Review PR #42.",
301
- });
302
- }
303
-
304
- // Wire createReviewWorkspace to a Pressable.
305
- return null;
306
- }
307
- ```
308
-
309
- The API covers workspaces, agents, providers, and daemon config. It omits connection lifecycle because BySpace owns the connection. Consult the current [SDK reference](https://github.com/ByteTrue/byspace/blob/main/public-docs/sdk/reference.md) for method details.
310
-
311
- ### Add daemon-side behavior
312
-
313
- Define one Zod contract in `shared/`, register its subprocess handler in `index.server.ts`, and
314
- call it from client code with `useRpc()`:
315
-
316
- ```ts
317
- // shared/greeting.ts
318
- import { defineRpc } from "@bytetrue/plugin";
319
- import { z } from "zod";
320
-
321
- const greeting = defineRpc({
322
- name: "greeting.create",
323
- input: z.object({ name: z.string() }),
324
- output: z.object({ message: z.string() }),
325
- });
326
- ```
327
-
328
- ```ts
329
- // server/greeting.ts
330
- import type { RpcInput } from "@bytetrue/plugin";
331
- import { greeting } from "../shared/greeting";
332
-
333
- export async function createGreeting({ name }: RpcInput<typeof greeting>) {
334
- return { message: `Hello, ${name}!` };
335
- }
336
- ```
337
-
338
- ```ts
339
- // index.server.ts
340
- import type { PluginServerContext } from "@bytetrue/plugin/server";
341
- import { createGreeting } from "./server/greeting";
342
- import { greeting } from "./shared/greeting";
343
-
344
- export default function contribute(server: PluginServerContext) {
345
- server.handle(greeting, createGreeting);
346
- return () => {};
347
- }
348
- ```
349
-
350
- ```tsx
351
- // client/greeting.tsx
352
- import { useRpc } from "@bytetrue/plugin/client";
353
- import { greeting } from "../shared/greeting";
354
-
355
- function Greeting() {
356
- const createGreeting = useRpc(greeting);
357
- // Use createGreeting({ name: "Ada" }) in a query, mutation, or event.
358
- return null;
359
- }
360
- ```
361
-
362
- Inputs and outputs are validated on both sides. Backend handlers receive the same `BySpaceApi` as `{ byspace }`; their IPC-backed daemon session lives exactly as long as the subprocess. Backend code can use Node APIs and installed dependencies. Keep credentials, filesystem access, shell commands, and vendor API calls in the handler rather than the client surface.
363
-
364
- Use TanStack Query for async request state, caching, and mutations.
365
-
366
- ### Debug daemon-side behavior
367
-
368
- Backend contributions can use normal Node logging. `console.log()` writes to the plugin's stdout;
369
- `console.error()` writes to stderr. BySpace captures both streams without interfering with plugin IPC.
370
-
371
- Inspect recent output after install, reload, an RPC failure, or a subprocess crash:
372
-
373
- ```bash
374
- byspace plugin logs my-plugin
375
- byspace plugin logs my-plugin --json
376
- byspace plugin logs my-plugin --host <url>
377
- ```
378
-
379
- The same tail is available from **Settings → Plugins → Logs**. It includes initialization, handler,
380
- cleanup, and final crash output. Reload, disable, and process failure retain the tail. Removing the
381
- plugin clears it; restarting the daemon clears the in-memory tail. Structured copies also go to the
382
- daemon log. Never log credentials or other secrets.
383
-
384
- ## Add a composer attachment source
385
-
386
- Define a search RPC and declarative source in `shared/`, handle it on the server, and register it on
387
- the client:
388
-
389
- ```ts
390
- // shared/issues.ts
391
- import { defineAttachmentSource, defineRpc } from "@bytetrue/plugin";
392
- import { z } from "zod";
393
-
394
- const searchIssues = defineRpc({
395
- name: "issues.search",
396
- input: z.object({ query: z.string() }),
397
- output: z.object({
398
- items: z.array(
399
- z.object({
400
- id: z.string(),
401
- identifier: z.string(),
402
- title: z.string(),
403
- subtitle: z.string().optional(),
404
- url: z.string().url(),
405
- text: z.string(),
406
- resourceType: z.string(),
407
- }),
408
- ),
409
- }),
410
- });
411
-
412
- const issues = defineAttachmentSource({
413
- id: "issues",
414
- title: "Acme issue",
415
- icon: "CircleDot",
416
- pickerTitle: "Attach Acme issue",
417
- searchPlaceholder: "Search by identifier or title",
418
- search: searchIssues,
419
- });
420
- ```
421
-
422
- ```ts
423
- // index.server.ts
424
- import type { PluginServerContext } from "@bytetrue/plugin/server";
425
- import { searchIssues } from "./shared/issues";
426
-
427
- export default function contribute(server: PluginServerContext) {
428
- server.handle(searchIssues, ({ query }) => searchAcmeIssues(query));
429
- return () => {};
430
- }
431
- ```
432
-
433
- ```tsx
434
- // index.client.tsx
435
- import type { PluginClientContext } from "@bytetrue/plugin/client";
436
- import { issues } from "./shared/issues";
437
-
438
- export default function contribute(client: PluginClientContext) {
439
- client.addAttachmentSource(issues);
440
- return () => {};
441
- }
442
- ```
443
-
444
- Return complete text snapshots. BySpace owns the composer menu, picker, pills, drafts, and submission. Credentials and vendor calls stay in the daemon handler.
445
-
446
- ## Add a client slash command
447
-
448
- A slash command runs plugin code in the app when the user submits `/name args`. Nothing is sent to the agent. `args` is the raw text after the command name, trimmed; parse it in the plugin.
449
-
450
- ```ts
451
- client.addSlashCommand({
452
- name: "review",
453
- description: "Run the review bot",
454
- argumentHint: "[scope]",
455
- context: "agent", // or "workspace" so drafts get it too
456
- async onSubmit({ args, agent, rpc, openPanel }) {
457
- await rpc(startReview, { agentId: agent.id, scope: args });
458
- openPanel("review");
459
- },
460
- });
461
- ```
462
-
463
- The callback receives the same context as the matching Command Center item plus `args`. BySpace owns the autocomplete row, input clearing, and the error toast; put pending UI in a pill or panel. Precedence is built-in client commands, then plugin commands, then provider commands; a lower-precedence collision is dropped. Commands do not run while the composer has attachments. Server-side slash commands do not exist.
464
-
465
- ## Add a composer pill
466
-
467
- A pill is a per-agent button in the composer track bar next to Tasks and Subagents. Add and remove pills from the client entry lifecycle. `addComposerPill` exists on `PluginClientContext`.
468
-
469
- ```tsx
470
- export function contributeClient(client: PluginClientContext) {
471
- const pills = new Map<string, () => void>();
472
- const unsubscribe = client.byspace.agents.subscribe((update) => {
473
- if (update.kind !== "upsert" || !update.agent.workspaceId) return;
474
- const { id: agentId, workspaceId } = update.agent;
475
- pills.get(agentId)?.();
476
- pills.set(
477
- agentId,
478
- client.addComposerPill({
479
- id: "review",
480
- title: "Open review",
481
- workspaceId,
482
- agentId,
483
- Component: ReviewPill,
484
- async onPress() {
485
- client.openPanel("review", { workspaceId, agentId });
486
- },
487
- }),
488
- );
489
- });
490
- return () => {
491
- unsubscribe();
492
- for (const remove of pills.values()) remove();
493
- };
494
- }
495
- ```
496
-
497
- Call `contributeClient(client)` from `index.client.tsx`, or move its body into that entry. The component owns its icon and text; BySpace owns the pressable, chrome, pending state, error reporting, and placement. Removal functions are idempotent, and BySpace removes every pill when the plugin, client entrypoint, or host connection is torn down.
498
-
499
- ## Transform and render timeline items
500
-
501
- Timeline transformers and renderers are client contributions. A transformer selects one built-in `AgentTimelineItem.type`, inspects the item, and returns zero or more versioned plugin items. `undefined` keeps the source item, `items` replaces it, `[]` removes it. A renderer draws one `kind` and `version` after validating `data` with its Zod schema.
502
-
503
- ```ts
504
- client.addTimelineTransformer({
505
- id: "inline-thinking",
506
- query: { itemType: "reasoning" },
507
- transform: ({ item, phase }) => ({
508
- items: [
509
- { type: "plugin", kind: "inline-thinking", version: 1, data: { text: item.text, phase } },
510
- ],
511
- }),
512
- });
513
- client.addTimelineRenderer({
514
- kind: "inline-thinking",
515
- version: 1,
516
- schema: z.object({ text: z.string(), phase: z.enum(["streaming", "complete"]) }),
517
- Component: InlineThinking,
518
- });
519
- ```
520
-
521
- Transformers run while the render model is built, on fetched history and on every live update, so `phase` is `"streaming"` for a loading thought or running tool call. Identity comes from the source item, so a streaming item keeps its mounted component; set an output `id` when one source explodes into several items. Transformers must be synchronous and deterministic, `data` must be JSON, and a transformer that throws is logged and skipped. Use `useRevealedText(text, phase)` from `@bytetrue/plugin/client/react-native` to pace streaming text. `plugin-examples/inline-thinking` replaces the thinking row with inline text; `plugin-examples/timeline-items` replaces a Pi todo tool call with a task card.
522
-
523
- ## Append a timeline row from the daemon
524
-
525
- A server handler can push a plugin-owned row into any agent timeline. The same renderer registration draws it.
526
-
527
- ```ts
528
- server.handle(publishReview, async ({ agentId, verdict }, { byspace }) => {
529
- await byspace.agents.ref(agentId).timeline.append({
530
- type: "plugin",
531
- id: "review",
532
- kind: "review-result",
533
- version: 1,
534
- data: { verdict },
535
- });
536
- return {};
537
- });
538
- ```
539
-
540
- The daemon stamps `pluginId` from the plugin session, so only plugin code can call this. Re-appending with the same `id` replaces the earlier row live and on refetch, which is how a plugin updates a row. `data` is capped at 64 KiB serialized and rejected above that. Rows live in the daemon's in-memory timeline and survive scroll, refetch, and reconnect, but not a daemon restart. A row whose plugin is missing renders an unavailable placeholder. Hosts advertise support through `server_info.features.pluginTimelineItems`.
541
-
542
- ## Contribute a theme
543
-
544
- `addTheme` takes a small light or dark palette; BySpace expands it into the full token set. Every color is a hex string.
545
-
546
- ```ts
547
- client.addTheme({
548
- id: "mocha",
549
- name: "Catppuccin Mocha",
550
- appearance: "dark",
551
- colors: {
552
- background: "#1e1e2e",
553
- foreground: "#cdd6f4",
554
- raised: "#313244",
555
- control: "#45475a",
556
- border: "#45475a",
557
- accent: "#cba6f7",
558
- mutedForeground: "#a6adc8",
559
- ring: "#6c7086",
560
- },
561
- });
562
- ```
563
-
564
- It appears under Settings → Appearance. A client that predates `addTheme` cannot evaluate the entry and reports `client.addTheme is not a function`; update the client. See `plugin-examples/catppuccin`.
565
-
566
- ## Hosts and trust
567
-
568
- Plugins are installed per daemon and are trusted, unsandboxed code. Backend code can access files, processes, credentials, and network services on the daemon machine. Client contributions run inside the BySpace app. Do not install a plugin the user has not authorized or source code you have not inspected.
569
-
570
- ### Check the global switch before installing
571
-
572
- Identify the target daemon and inspect its root `pluginsEnabled` value in `config.json`. For the local daemon, `byspace daemon status --json` reports its `home`; the file is `<home>/config.json`. Treat a missing field as `false`. Do not infer the global value from a plugin's `disabled` status, because an individual plugin can also be disabled.
573
-
574
- If `pluginsEnabled` is already `true`, continue without asking the user to enable it.
575
-
576
- If it is false or absent, stop and ask the user for explicit permission before editing or enabling anything. Include this warning in the request:
577
-
578
- > Plugins are trusted, unsandboxed code. Backend plugin code can access your daemon machine, including files, processes, credentials, and network services. Client plugin code runs inside the BySpace app. May I enable plugins on this daemon?
579
-
580
- Do not continue unless the user agrees. After permission:
581
-
582
- 1. Preserve the rest of `config.json` and set the root `pluginsEnabled` field to `true`.
583
- 2. Run `byspace reload --json` against that daemon.
584
- 3. Require `pluginsEnabled` in `appliedPaths`, or accept an empty `appliedPaths` only after re-reading the file and confirming the live plugin catalog is enabled.
585
- 4. Run `byspace plugin ls` and verify the intended plugin reaches `running` after installation.
586
-
587
- If the user asks to disable the global switch, set `pluginsEnabled` to `false`, run `byspace reload --json`, and verify configured plugins report `disabled`.
588
-
589
- Do not edit a local config when the target is a remote daemon. Perform the edit on the daemon machine, or ask the user to use **Settings → Plugins → Enable plugins**. `byspace reload --host <url>` reloads the remote daemon's own file but does not edit it.
590
-
591
- When the same sidebar contribution exists on several connected hosts, BySpace shows it once with a host picker. The selected host owns the bundle, SDK calls, RPCs, and query cache. An offline selected host does not fall through to another host. Attachment sources stay scoped to the composer's host.
592
-
593
- ## Typecheck and manage
594
-
595
- Always typecheck before install or reload:
596
-
597
- ```bash
598
- npm run typecheck
599
- byspace plugin install /absolute/path/to/plugin
600
- byspace plugin install /absolute/path/to/plugin --id another-runtime-id
601
- byspace plugin add owner/repository # Git source; append :path for a monorepo subdirectory
602
- byspace plugin add owner/repository --ref main # branches track, tags and commits pin
603
- byspace plugin status
604
- byspace plugin update my-plugin
605
- byspace plugin ls
606
- byspace plugin reload my-plugin
607
- byspace plugin logs my-plugin
608
- byspace plugin disable my-plugin
609
- byspace plugin enable my-plugin
610
- byspace plugin remove my-plugin
611
- ```
612
-
613
- Use `--host <url>` when managing a daemon other than the CLI default. A Git source that must install or generate something declares `build` in `byspace-plugin.json` as a list of argv arrays; BySpace runs them without a shell on install and update and keeps the old version if one fails. Plugin source edits require `byspace plugin reload`; config changes to the global switch require `byspace reload`. A failed plugin reload stays failed; inspect `byspace plugin ls` for the load error and `byspace plugin logs <id>` for subprocess output, fix the source, typecheck, and reload again. `remove` deletes configuration, never the source directory.
614
-
615
- Do not restart the daemon to load source changes. Restarting it can kill the agent performing the work.
616
-
617
- For an old mixed entry, follow the standalone [v0.8 runtime-entry migration guide](https://paseo.sh/docs/plugins/v0.8/migration) mechanically.
618
-
619
- ## Verify the outcome
620
-
621
- After a change:
622
-
623
- 1. Run `npm run typecheck`.
624
- 2. Install or reload the exact runtime ID.
625
- 3. Run `byspace plugin ls` and require `running` with no error.
626
- 4. Confirm the contribution on the intended host. Open the Command Center with **⌘K** (macOS) or **Ctrl+K** (Windows/Linux). Type `/` in the composer for slash commands. For timeline work, run an agent turn that produces the source item and watch it while it streams, not only after it completes. For UI work, check a wide desktop window and a compact/mobile client, and switch theme to confirm text still uses `foreground` / `foregroundMuted`.
627
- 5. Exercise the changed action or RPC, including its error state.
628
-
629
- Common failures:
630
-
631
- - Missing sidebar item: wrong host, plugin not `running`, invalid Lucide icon, or sidebar item points to a missing surface.
632
- - Unavailable client module: client bundles can use only the host-provided modules listed above.
633
- - RPC rejection: input or output failed its Zod schema, or the handler threw. Inspect `byspace plugin logs <id>` for handler output.
634
- - Plugin exits or reload fails: inspect `byspace plugin ls` for status and `byspace plugin logs <id>` for initialization, cleanup, or crash output.
635
- - Stale UI: source was edited without `byspace plugin reload <id>`.
636
- - Timeline item shows "Plugin timeline item unavailable": no renderer registered for that `kind` and `version`, the renderer schema rejected `data`, or the plugin is not running on that host.
637
- - Transformer has no effect: `query.itemType` does not match the source type, the transform returned `undefined`, or it threw and was skipped; check the app console for `[Plugins] Timeline transformer failed`.
638
- - Slash command not offered: name collides with a built-in or another plugin, the context is `agent` on a draft, or the composer has attachments.
639
- - Append rejected: the caller is not a plugin session, `data` exceeds 64 KiB, or the host predates `features.pluginTimelineItems`.