@bibobonking/opensol 3.1.5 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/CHANGELOG.md +8 -2
  2. package/LICENSE +21 -0
  3. package/README.md +24 -20
  4. package/desktop/README.md +1 -1
  5. package/dist/assets/{index-C0yvs6Vl.js → index-BUVnnhRH.js} +1 -1
  6. package/dist/assets/index-CyxlAgKM.js +271 -0
  7. package/dist/assets/index-DFeF90S8.css +1 -0
  8. package/dist/index.html +2 -2
  9. package/package.json +1 -1
  10. package/server/README.md +15 -68
  11. package/server/daemon.mjs +6 -1
  12. package/server/lib/deploy.mjs +5 -10
  13. package/server/lib/shell.mjs +149 -0
  14. package/server/lib/shell.test.mjs +72 -0
  15. package/server/lib/tools.mjs +8 -44
  16. package/src/agent/adapters/anthropic.ts +2 -3
  17. package/src/agent/adapters/gemini.ts +2 -3
  18. package/src/agent/adapters/openai-responses.ts +2 -3
  19. package/src/agent/adapters/openai.ts +2 -3
  20. package/src/agent/adapters/shared.ts +3 -0
  21. package/src/components/ChatView.tsx +17 -54
  22. package/src/components/Composer.tsx +143 -135
  23. package/src/components/ContextBar.tsx +2 -2
  24. package/src/components/HomeDashboard.tsx +7 -4
  25. package/src/components/MessageItem.tsx +33 -1
  26. package/src/components/ModelDeck.tsx +5 -27
  27. package/src/components/SettingsModal.tsx +8 -0
  28. package/src/components/ToolCallCard.tsx +2 -2
  29. package/src/components/TrajectoryView.tsx +1 -1
  30. package/src/components/composer/PlusMenu.tsx +117 -0
  31. package/src/components/composer/Popover.tsx +109 -0
  32. package/src/components/composer/WorkspacePicker.tsx +102 -0
  33. package/src/core/plugins/tools/shell.ts +2 -2
  34. package/src/core/plugins/tools/subagent.ts +18 -8
  35. package/src/core/services/agent-loop.ts +45 -43
  36. package/src/core/services/compaction.ts +289 -22
  37. package/src/core/services/llm.ts +6 -2
  38. package/src/core/services/subagents.ts +191 -3
  39. package/src/core/services/system-prompt.ts +35 -7
  40. package/src/core/services/tools.ts +2 -1
  41. package/src/core/types.ts +5 -0
  42. package/src/lib/compact-plan.test.mjs +62 -0
  43. package/src/lib/compact-plan.ts +188 -0
  44. package/src/lib/subagent-types.ts +54 -0
  45. package/src/lib/tool-meta.ts +40 -0
  46. package/src/store/settings.ts +3 -0
  47. package/src/store/ui.ts +12 -0
  48. package/src/types.ts +26 -0
  49. package/shared/plugin-builder.test.mjs +0 -43
  50. package/shared/plugin-package.d.mts +0 -34
  51. package/shared/plugin-package.mjs +0 -197
  52. package/shared/plugin-package.test.mjs +0 -94
  53. package/tsconfig.tsbuildinfo +0 -1
package/CHANGELOG.md CHANGED
@@ -1,8 +1,14 @@
1
1
  # Changelog
2
2
 
3
- ## Unreleased
3
+ ## Realeased 3.2.0 - Final Version
4
4
 
5
- - Agent loop: removed the 16-round cap (also for subagents). Provider errors now retry up to 5 times with backoff, only for transient errors (429 bursts, 5xx, overload, network); quota, credit and "limit reached" errors are never retried.
5
+ - **Shell on every OS.** Commands run in bash (Linux), a login zsh/bash (macOS) or PowerShell/cmd (Windows) instead of a hard-coded `/bin/bash`; the agent is told which. `OPENSOL_SHELL` overrides. Plan mode has a read-only allowlist per shell family, process trees are killed correctly on Windows, and deploy builds use the same shell.
6
+ - **Smarter compaction.** Keeps the newest turns verbatim, merges earlier summaries, tracks changed files from tool calls, handles huge sessions in chunks, retries, falls back safely, also runs between tool rounds, and has `/compact [focus]`. Summaries show as a divider you can expand.
7
+ - **Better subagents.** Types `explore`/`research`/`review`, parallel calls, live progress in the tool card, round budget with forced report, context pruning, transient-error retry, and approval now actually applies to them. Subagents can no longer start subagents or receive tools outside their type.
8
+ - **New composer and home.** Compact box with the folder picker above it, a **+** menu (files, web search, MCP, skills link), plain-text Build/Plan, permission, effort and model controls whose panels flip and scroll to stay on screen, and a simpler new-session screen.
9
+ - Mascot is now optional: Settings → General → Show mascot (off by default).
10
+ - Cleanup: removed the legacy `src/agent/{compact,subagent,tools,prompt,model,approval}.ts` duplicates, a stale `server/package.json`, unused composer/ChatView code, and merge-conflict markers in `.gitignore` and `.npmignore`. Docs synced (`~/.opensol`, build workflow name, architecture).
11
+ - Agent loop: removed the 16-round cap (subagents now have their own round budget, see above). Provider errors now retry up to 5 times with backoff, only for transient errors (429 bursts, 5xx, overload, network); quota, credit and "limit reached" errors are never retried.
6
12
  - Model deck: hovering no longer scrolls the list, which made rows jump.
7
13
  - Plugins button disabled for maintenance.
8
14
  - Skills: real skill folders (SKILL.md + scripts/references) installed by folder upload or npm, loaded on demand through the new `skill` tool. Built-in skills removed.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bibobonking
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -2,36 +2,39 @@
2
2
 
3
3
  A local-first AI coding agent by OpenSoftX. OpenSol pairs a warm React chat interface with a zero-dependency Node daemon that owns the real workspace, shell, git, search, deploy and provider credentials on your machine.
4
4
 
5
- The daemon binds to `127.0.0.1` only. It serves the prebuilt app from `dist/` and stores everything under `~/.maxcode`. API keys and hosting tokens are encrypted with AES-256-GCM using a local `master.key`; the browser only ever receives a masked hint.
5
+ The daemon binds to `127.0.0.1` only. It serves the prebuilt app from `dist/` and stores everything under `~/.opensol`. API keys and hosting tokens are encrypted with AES-256-GCM using a local `master.key`; the browser only ever receives a masked hint.
6
6
 
7
- ## Run the built app or install with npm
7
+ ## Run it
8
8
 
9
- Requires Node.js 20 or newer. No `node_modules` is needed to run the built app.
10
-
11
- ## Install globally via npm
12
-
13
- ```bash
14
- npm install -g @bibobonking/opensol
9
+ Requires Node.js 20 or newer (the daemon itself has no runtime dependencies, except `fflate` for plugin packages).
15
10
 
16
11
  ```bash
17
- npm start run
12
+ npm install -g @bibobonking/opensol # once
13
+ opensol start --open # or: npm start, from a checkout
18
14
  ```
15
+
19
16
  ```
20
- opensol start [--port=4174] [--open] [--foreground]
21
- opensol stop
22
- opensol status
23
- opensol open
24
- opensol logs
17
+ opensol start [--port=4174] [--open] [--foreground]
18
+ opensol stop
19
+ opensol status
20
+ opensol open
21
+ opensol logs
25
22
  ```
26
23
 
27
24
  On first launch, choose a real project folder, then add a provider key in **Settings → Models** and pick a live model. Everything else — workspace tree, editor, git, search, build/plan modes, subagents and deploys — runs through the local daemon.
28
25
 
26
+ Works on **Windows, macOS and Linux**. See *Shell* below for how commands run on each.
27
+
29
28
  ## What is in the app
30
29
 
31
30
  - **Build and Plan modes.** Build can edit files and run commands; Plan is read-only investigation.
32
31
  - **Live models and reasoning.** Model lists come from each provider; the models.dev catalog is only a fallback. Thinking is the provider's job: an effort control appears only when the selected model advertises effort levels or a token budget, and it is sent automatically.
33
32
  - **Themes.** Light, Dark, Night Owl, Claude Code and Flame, plus System, which follows the OS preference. Syntax highlighting follows the theme.
34
- - **Real tools.** Read/write/edit files, grep, list files, shell, git, web search, URL reading and a read-only SubAgent. Each user turn renders as one answer, with consecutive tool rounds collapsed into a single “Used N tools” group.
33
+ - **Real tools.** Read/write/edit files, grep, list files, shell, git, web search, URL reading, skills and read-only subagents. Each user turn renders as one answer, with consecutive tool rounds collapsed into a single “Used N tools” group.
34
+ - **New composer.** A compact box: the project folder sits above it, **+** adds files, toggles web search and Plan mode, opens MCP and switches skills. Permissions and effort are plain text at the bottom left (a panel opens on click); the model name is at the bottom right and opens the Model Deck (`Ctrl/⌘ M`).
35
+ - **Shell.** The `shell` tool runs in your default shell: bash/zsh (login shell) on Linux and macOS, PowerShell 7 or Windows PowerShell on Windows (`cmd` as a last resort). The agent is told which one in its system prompt. Override with `OPENSOL_SHELL=/path/to/shell`. Plan mode accepts only read-only commands in the matching syntax.
36
+ - **Smart compaction.** Near 85% of the context window (checked before a turn and between tool rounds), older turns are summarised while the newest ones stay verbatim. Earlier summaries are merged, not re-summarised; changed files are tracked from tool calls; if the model is unavailable a deterministic fallback keeps the session going. Run `/compact [focus]` to do it yourself.
37
+ - **Subagents.** `subagent` delegates read-only work to a separate context: `explore` (codebase), `research` (web + code) and `review` (bugs in a diff). Several calls in one message run in parallel, progress streams into the tool card, and each has a round budget and context pruning.
35
38
  - **Turn queue.** A prompt sent while a turn is streaming waits its turn and runs next, instead of splitting into a second reply.
36
39
  - **Local plugins.** Two plugin modes:
37
40
  - **API v1 (sandboxed):** Install a package, review its declared workspace capabilities and HTTPS domains, preview in a sandbox, mark it Safe. Plugin code gets a restricted API (`registerCommand`, `registerPanel`, `registerTheme`, workspace read/write, HTTPS-only network to declared hosts).
@@ -41,7 +44,7 @@ On first launch, choose a real project folder, then add a provider key in **Sett
41
44
  - **Deploy.** Cloudflare Pages, Netlify and Vercel, for the open workspace or another existing folder. Tokens stay in the daemon and are passed to CLIs through environment variables.
42
45
  - **Local history.** Conversations, settings, workspaces and encrypted keys survive restarts.
43
46
 
44
- Slash commands include `/search`, `/plan`, `/build`, `/new`, `/workspace`, `/deploy`, `/settings` and `/clear`.
47
+ Slash commands: `/search`, `/plan`, `/build`, `/compact`, `/new`, `/workspace`, `/deploy`, `/settings` and `/clear`.
45
48
 
46
49
  ## Development
47
50
 
@@ -71,8 +74,9 @@ OpenCode free-tier models only work inside OpenCode itself, so they are hidden h
71
74
  TypeScript, React 19, Vite, Tailwind CSS, Zustand, direct provider adapters (OpenAI, Anthropic, Gemini, OpenCode and OpenAI-compatible endpoints), and a Node HTTP daemon with no runtime dependencies.
72
75
 
73
76
  ```
74
- src/ React app, stores, agent loop, adapters and tools
75
- server/ local daemon, encrypted store, workspace tools and deploy
76
- dist/ verified production build
77
- public/ favicon
77
+ src/ React app, Cordis kernel (src/core), stores, adapters and UI
78
+ server/ local daemon, encrypted store, workspace tools, shell, MCP and deploy
79
+ desktop/ Tauri wrapper
80
+ shared/ plugin package contract used by both sides
81
+ dist/ production build served by the daemon
78
82
  ```
package/desktop/README.md CHANGED
@@ -33,7 +33,7 @@ cd desktop && npx @tauri-apps/cli@2 build
33
33
  ```
34
34
 
35
35
  Installers appear in `desktop/src-tauri/target/release/bundle/`. Each OS has to be built on that
36
- OS (Windows installers on Windows, macOS on macOS); `.github/workflows/desktop.yml` does all four
36
+ OS (Windows installers on Windows, macOS on macOS); `.github/workflows/build.yml` does all four
37
37
  on GitHub Actions when you push a `v*` tag or run it manually.
38
38
 
39
39
  For a quick run from source: `cd desktop/src-tauri && cargo run` (needs `dist/` built).
@@ -41,7 +41,7 @@ Never claim a check passed without running it. Ask only when a decision is genui
41
41
  `),e}Re(zu,"enhanceError");var fA=["prototype","then"];function Lu(e){return typeof e=="symbol"||fA.includes(e)||parseInt(e).toString()===e||e.startsWith("_")}Re(Lu,"isSpecialProperty");function mg(e,i){const a=e[ke.isolate][i];let r=e[ke.isolate],c=e.fiber;for(;;){let u;for(const d of c.provisions)d.name===i&&d.ctx[ke.isolate][i]===a&&(u=d);if(u)return{impl:u};for(;r!==c.ctx[ke.isolate];){if(!r||Object.hasOwn(r,i))return;r=Object.getPrototypeOf(r)}if(u=c.committed?.[i],u||i in c.inject)return{fiber:c,impl:u};if(!c.runtime)return;c=c.parent.fiber}}Re(mg,"locate");var m0=class{constructor(e){this.ctx=e,Ma(this,ke.tracker,{property:"ctx",noShadow:!0}),this.mixin("reflect",["get","set","provide","accessor","mixin"]),this.mixin("fiber",["runtime","effect"]),this.mixin("registry",["inject","plugin"]),this.mixin("events",["on","once","parallel","emit","serial","bail","waterfall"])}ctx;static{Re(this,"ReflectService")}static handler={get:Re((e,i,a)=>{if(Lu(i))return Reflect.get(e,i,a);if(Reflect.has(e,i))return xr(a,Reflect.get(e,i,a));const r=new Error(`cannot get property "${i}" without inject`);try{const c=e.reflect.props[i];if(c?.type==="accessor")return c.get.call(a,a[ke.receiver],r);const u=a[ke.shadow]??a;return u.fiber.runtime?a.events.waterfall("internal/get",a,i,r,()=>{const d=mg(u,i);if(!d)throw r;if(d.impl)return xr(a,d.impl.value);throw r.message=`cannot get required service "${i}" in inactive context`,r}):a.reflect.get(i,!1)}catch(c){throw c===r?zu(c):c}},"get"),set:Re((e,i,a,r)=>{if(Lu(i))return Reflect.set(e,i,a,r);const c=new Error(`cannot set property "${i}" without provide`),u=e.reflect.props[i];if(!u){if(!r.fiber.runtime)return Reflect.set(e,i,a,r);throw zu(c)}try{return u.type==="accessor"?u.set?u.set.call(r,a,r[ke.receiver],c):!1:r.events.waterfall("internal/set",r,i,a,c,()=>r.reflect.set(i,a,c))}catch(d){throw d===c?zu(d):d}},"set"),has:Re((e,i)=>Lu(i)?Reflect.has(e,i):Reflect.has(e,i)?!0:!!e.reflect.props[i],"has")};store=Object.create(null);props=Object.create(null);_implCounter=0;get(e,i=!0){return xr(this.ctx,this._getImpl(e,i)?.value)}_getImpl(e,i=!0){const a=this.ctx[ke.isolate][e],r=a&&this.store[a];if(r&&!(i&&r.ctx.fiber.state!==2))return r}set(e,i,a){const r=this.ctx[ke.isolate][e],c=this.store[r];if(!c)throw new Error(`cannot set property "${e}" without provide`);if(c.ctx.fiber!==this.ctx.fiber)throw new Error(`cannot set property "${e}" in multiple fibers`);return c.value=i,!0}provide(e,i,a){return this.ctx.fiber.effect(()=>{if(!this.props[e])this.props[e]??={type:"service"};else if(this.props[e].type!=="service")throw new Error(`property "${e}" is already declared as ${this.props[e].type}`);this.props[e]={type:"service"},this.ctx[ke.root][ke.isolate][e]??=Symbol(e);const r=this.ctx[ke.isolate][e],c={uid:++this._implCounter,name:e,value:i,ctx:this.ctx,check:a,isolate:r,dependents:new Set};if(this.store[r])throw new Error(`service "${e}" has been registered at <${this.store[r].ctx.fiber.name}>`);return this.store[r]=c,this.ctx.fiber.provisions.add(c),this.ctx.fiber.state===2&&this.notify([e]),async()=>{this.store[c.isolate]===c&&delete this.store[c.isolate],this.ctx.isolate(e,c.isolate).reflect.notify([e]),await new Promise(u=>{c.release=u,c.dependents.size||u()}),c.release=void 0,this.ctx.fiber.provisions.delete(c)}},`ctx.provide(${JSON.stringify(e)})`)}notify(e,i=(a,r)=>a[ke.isolate][r]===this.ctx[ke.isolate][r]){for(const a of this.ctx.registry.values())for(const r of a.fibers){let c=!1;for(const u of e)u in r.inject&&i(r.ctx,u)&&(c=!0,r._checkImpl(u));c&&r._refresh()}for(const a of e){const r=Object.create(this.ctx);r[ke.filter]=c=>i(c,a),this.ctx.events.emit(r,"internal/service",a,this._getImpl(a,!1)?.value)}}accessor(e,i){return this.ctx.fiber.effect(()=>{if(e in this.props)throw new Error(`property "${e}" is already declared as ${this.props[e].type}`);return this.props[e]={type:"accessor",...i},()=>delete this.props[e]},`ctx.accessor(${JSON.stringify(e)})`)}mixin(e,i){const a=this;return this.ctx.fiber.effect(function*(){const r=Array.isArray(i)?i.map(u=>[u,u]):Object.entries(i),c=Re((u,d)=>u[e],"getTarget");for(const[u,d]of r)yield a.accessor(d,{get(p,h){const g=c(this,h);if(Ku(g))return g;const b=p?Lo(p,g):g,x=Reflect.get(g,u,b);return typeof x!="function"?x:x.bind(b??g)},set(p,h,g){const b=c(this,g),x=h?Lo(h,b):b;return Reflect.set(b,u,p,x)}})},`ctx.mixin(${JSON.stringify(e)})`)}trace(e){return xr(this.ctx,e)}bind(e){return new Proxy(e,{apply:Re((i,a,r)=>Reflect.apply(i,this.trace(a),r.map(c=>this.trace(c))),"apply"),construct:Re((i,a,r)=>Reflect.construct(i,a.map(c=>this.trace(c)),r),"construct")})}},ki=class yh{constructor(i,a){this.ctx=i,a??=this.constructor.provide;let r=this;const c={associate:a,property:"ctx"};return r[ke.invoke]&&(r=Zu(a,vd(Object.getPrototypeOf(this),Function.prototype),c)),r.ctx=i,r.name=a,Ma(r,ke.tracker,c),r.ctx.reflect.provide(a,r,this[ke.check]),r}ctx;static{Re(this,"Service")}static init=ke.init;static check=ke.check;static invoke=ke.invoke;static metadata=ke.metadata;static extend=ke.extend;static tracker=ke.tracker;static mergeIntercept=ke.mergeIntercept;static resolveIntercept=ke.resolveIntercept;name;[ke.filter](i){return i[ke.isolate][this.name]===this.ctx[ke.isolate][this.name]}[ke.extend](i){let a;return this[yh.invoke]?a=Zu(this.name,this,this[ke.tracker]):a=Object.create(this),Object.assign(a,i)}[ke.mergeIntercept](...i){return Object.assign({},...i)}[ke.resolveIntercept](){const i=[];let a=this.ctx[hg.intercept];for(;this.name in a;)Object.hasOwn(a,this.name)&&i.unshift(a[this.name]),a=Object.getPrototypeOf(a);const r=mg(this[ke.caller]??this.ctx,this.name)?.fiber?.inject[this.name];return Ku(r)||i.unshift(r),(this[ke.mergeIntercept]??yh.prototype[ke.mergeIntercept]).apply(this,i)}static[Symbol.hasInstance](i){if(!i)return!1;let a=i.constructor;for(;a;){if(a=a.prototype?.constructor,a===this)return!0;a&&=Object.getPrototypeOf(a)}return!1}},h0={s:Re(e=>String(e),"s"),d:Re(e=>Math.trunc(Number(e)),"d"),i:Re(e=>Math.trunc(Number(e)),"i"),f:Re(e=>Number(e),"f"),o:Re(e=>JSON.stringify(e),"o"),O:Re(e=>JSON.stringify(e),"O"),c:Re(()=>"","c"),C:Re((e,i,a)=>vh.color(i,vh.code(a.name,i.colors),e),"C")};function Yw(e){return e instanceof Error&&Array.isArray(e.errors)}Re(Yw,"isAggregateError");var vh=class{constructor(e,i){this.service=i,Object.assign(this,e),this.error=this._method("error",0),this.info=this._method("info",2),this.warn=this._method("warn",1),this.debug=this._method("debug",3)}service;static{Re(this,"Logger")}static color(e,i,a,r=""){return e.colors?`\x1B[3${i<8?i:"8;5;"+i}${e.colors>=2?r:""}m${a}\x1B[0m`:""+a}static code(e,i){let a=0;for(let c=0;c<e.length;c++)a=(a<<3)-a+e.charCodeAt(c)+13,a|=0;const r=i?i>=2?mA:pA:[];return r[Math.abs(a)%r.length]}static format(e,i){const a=i.args.slice();a[0]instanceof Error?(a[0]=a[0].stack||a[0].message,a.unshift("%s")):typeof a[0]!="string"&&a.unshift("%o");let r=a.shift();r=r.replace(/%([a-zA-Z%])/g,(d,p)=>{if(d==="%%")return"%";const h=e.formatters?.[p]??h0[p];if(typeof h=="function"){const g=a.shift();return h(g,e,i)}return d});const c=e.formatters?.o??h0.o;for(let d of a)typeof d=="object"&&d&&(d=c(d,e,i)),r+=" "+d;const{maxLength:u=10240}=e;return r.split(/\r?\n/g).map(d=>d.slice(0,u)+(d.length>u?"...":"")).join(`
42
42
  `)}_method(e,i){return(...a)=>{if(a.length===1&&a[0]instanceof Error){if(a[0].cause)this[e](a[0].cause);else if(Yw(a[0])&&a[0].errors.length){a[0].errors.forEach(u=>this[e](u));return}}const r=++this.service._snMessage,c=Date.now();for(const u of this.service.exporters.values()){if((u.levels?.[this.name]??u.levels?.default??this.level??2)<i)continue;const p={sn:r,ts:c,type:e,level:i,name:this.name,...this.meta,args:a};u.export(p)}}}},pA=[6,2,3,4,5,1],mA=[20,21,26,27,32,33,38,39,40,41,42,43,44,45,56,57,62,63,68,69,74,75,76,77,78,79,80,81,92,93,98,99,112,113,129,134,135,148,149,160,161,162,163,164,165,166,167,168,169,170,171,172,173,178,179,184,185,196,197,198,199,200,201,202,203,204,205,206,207,208,209,214,215,220,221],hA=class Vw{static{Re(this,"LoggerService")}bufferSize=1e3;buffer=[];ctx;_snMessage=0;_snExporter=0;exporters=new Map;constructor(i){const a={property:"ctx",noShadow:!0},r=Zu("logger",vd(Object.getPrototypeOf(this),Function.prototype),a);return Object.assign(r,this),r.ctx=i,Ma(r,ke.tracker,a),r.exporter({colors:3,export:Re(c=>{r.buffer.push(c);const u=r.buffer.length-r.bufferSize;u===1?r.buffer.shift():u>1&&r.buffer.splice(0,u)},"export")}),r}exporter(i){return this.ctx.effect(()=>{const a=++this._snExporter;return this.exporters.set(a,i),()=>this.exporters.delete(a)},"ctx.logger.exporter()")}[ke.invoke](i){const a=this[ke.caller],r=ki.prototype[ki.resolveIntercept].call(this),c=(a??this.ctx).fiber;return i??=r.name,i??=oA(c.name),new vh({name:i,level:r.level,meta:{fiber:new WeakRef(c)}},this)}static{for(const i of["error","info","warn","debug"])Vw.prototype[i]=function(...a){return this()[i](...a)}}};function Kw(e){return e&&typeof e=="object"&&typeof e.apply=="function"}Re(Kw,"isApplicable");function Qu(e,i){return function(a,r){if(r.kind==="class")Object.hasOwn(a,"inject")||(Ma(a,"inject",Object.create(Object.getPrototypeOf(a).inject??null)),Ma(a.inject,ke.checkProto,!0)),a.inject[e]=i;else if(r.kind==="method"){const c=(a[ke.decorator]??={}).inject??=Object.create(null);c[e]=i,r.addInitializer(function(){const u=this[ke.tracker]?.property;(this[ke.initHooks]??=[]).push(()=>{this.ctx.inject(c,d=>a.call(u?Lo(this,{[u]:d}):this))})})}else throw new Error("@Inject() can only be used on class or class methods")}}Re(Qu,"Inject");(e=>{function i(a,r=Object.create(null)){if(!a)return r;if(Array.isArray(a))for(const c of a)r[c]=null;else if(Reflect.has(a,ke.checkProto)){Object.assign(r,i(Object.getPrototypeOf(a)));for(const c of Object.keys(a))r[c]=a[c]??null}else for(const c of Object.keys(a))r[c]=a[c]??null;return r}e.resolve=i,Re(i,"resolve")})(Qu||(Qu={}));var gA=class{constructor(e){this.ctx=e,Ma(this,ke.tracker,{property:"ctx",noShadow:!0})}ctx;static{Re(this,"RegistryService")}_counter=0;_internal=new Map;get counter(){return++this._counter}get size(){return this._internal.size}resolve(e){try{if(typeof e=="function")return e;if(Kw(e))return e.apply}catch{}}get(e){const i=this.resolve(e);return i&&this._internal.get(i)}has(e){const i=this.resolve(e);return!!i&&this._internal.has(i)}delete(e){const i=this.resolve(e),a=i&&this._internal.get(i);if(a){this._internal.delete(i);for(const r of a.fibers)r.dispose();return a}}keys(){return this._internal.keys()}values(){return this._internal.values()}entries(){return this._internal.entries()}forEach(e){return this._internal.forEach(e)}inject(e,i){return this.plugin({inject:e,apply:i,name:i.name})}plugin(e,i,a=kd()){const r=this.resolve(e);if(!r)throw new Error('invalid plugin, expect function or object with an "apply" method, received '+typeof e);this.ctx.fiber.assertActive();let c=this._internal.get(r);if(!c){let p=e.name;p==="apply"&&(p=void 0),c={name:p,callback:r,fibers:new fg,Config:e.Config},this._internal.set(r,c)}const u=new Gw(this.ctx,i,Qu.resolve(e.inject),c,a),d=Object.create(u);return d.then=(p,h)=>u.await().then(p,h),d}},hg=class _o{static{Re(this,"Context")}static root=ke.root;static effect=ke.effect;static filter=ke.filter;static isolate=ke.isolate;static intercept=ke.intercept;static is(i){return!!i?.[_o.is]}static{_o.is[Symbol.toPrimitive]=()=>Symbol.for("cordis.is"),_o.prototype[_o.is]=!0}constructor(){this[ke.isolate]=Object.create(null),this[ke.intercept]=Object.create(null);const i=new Proxy(this,m0.handler);return this[ke.root]=i,this.baseUrl=void 0,this.fiber=new Gw(i,{},Object.create(null),null,()=>[]),this.reflect=new m0(i),this.registry=new gA(i),this.events=new dA(i),this.logger=new hA(i),this.fiber._disposables.clear(),i}[Symbol.for("nodejs.util.inspect.custom")](){return`Context <${this.fiber.name}>`}extend(i={}){const a=Reflect.getOwnPropertyDescriptor(this,ke.shadow)?.value,r=Object.create(xr(this,this));for(const c of Reflect.ownKeys(i))Object.defineProperty(r,c,Reflect.getOwnPropertyDescriptor(i,c));return a?Object.assign(Object.create(r),{[ke.shadow]:a}):r}isolate(i,a){const r=Object.create(this[ke.isolate]);return r[i]=a??Symbol(i),this.extend({[ke.isolate]:r})}intercept(i,a){const r=Object.create(this[ke.intercept]);return r[i]=a,this.extend({[ke.intercept]:r})}};function xA(e,i){const a=e.findIndex(r=>r.id===i);if(a===-1)return null;for(let r=a+1;r<e.length;r+=1)if(e[r].role==="user")return e.slice(0,r);return e.slice()}function mm(e){for(let i=e.length-1;i>=0;i-=1){const a=e[i];if(a.role==="user")return a.id}return null}function bA(e){const i=new Map;return{isBusy:a=>i.has(a),isIdle:()=>i.size===0,clear(a){const r=i.get(a);r&&i.set(a,{chain:r.chain,token:{}})},clearAll(){for(const[a,r]of i)i.set(a,{chain:r.chain,token:{}})},push(a,r){const c=i.get(a)??{chain:Promise.resolve(),token:{}},u=c.token,p=c.chain.then(()=>i.get(a)?.token===u?e(a,r):void 0,()=>i.get(a)?.token===u?e(a,r):void 0).then(()=>{},()=>{});return i.set(a,{chain:p,token:u}),p.then(()=>{i.get(a)?.chain===p&&i.delete(a)}),p}}}class yA extends ki{static provide="sessions";constructor(i){super(i,"sessions")}current(i){return Xe.getState().sessions.find(a=>a.id===i)}all(){return Xe.getState().sessions}getMessages(i){return this.current(i)?.messages??[]}addMessage(i,a){Xe.getState().addMessage(i,a),this.flush(i)}updateMessage(i,a,r){Xe.getState().updateMessage(i,a,r),this.flush(i)}updateSession(i,a){Xe.getState().updateSession(i,a),this.flush(i)}turnHistory(i,a){return xA(i,a)}async flush(i){await this.ctx.events.parallel("session/flush",i).catch(()=>{})}}const vi=Ra()(e=>({skills:[],loaded:!1,loading:!1,error:"",load:async()=>{e({loading:!0});try{const{skills:i}=await Dj();e({skills:i,loaded:!0,loading:!1,error:""})}catch(i){e({loading:!1,error:i instanceof Error?i.message:"Could not load skills."})}},upload:async i=>{const a=await zj(i);return e({skills:a.skills,loaded:!0}),a.installed},installNpm:async i=>{const a=await Lj(i);return e({skills:a.skills,loaded:!0}),a.installed},setEnabled:async(i,a)=>{e(r=>({skills:r.skills.map(c=>c.name===i?{...c,enabled:a}:c)}));try{const r=await Ij(i,a);e({skills:r.skills})}catch(r){e(c=>({skills:c.skills.map(u=>u.name===i?{...u,enabled:!a}:u),error:r instanceof Error?r.message:"Could not update the skill."}))}},remove:async i=>{const a=await Uj(i);e({skills:a.skills})}}));function vA(){return vi.getState().skills.filter(e=>e.enabled)}function Xw(){const e=vA();return e.length===0?"":["## Skills","Skills are folders of expert instructions. When a task matches a skill below, call the `skill` tool with its name BEFORE starting, then follow what it returns. Load bundled files (scripts, references) with the same tool via `file`. Do not guess a skill's contents.",...e.map(i=>`- ${i.name}: ${i.description||"No description."}`)].join(`
43
43
  `)}function Zw(e,i){const a=i?e.workspacePrompts?.[i]:void 0;return typeof a=="string"?{text:a,source:"workspace"}:{text:e.systemPrompt,source:"app"}}function Qw(e,i,a){return typeof i?.systemPrompt=="string"?{text:i.systemPrompt,source:"session"}:Zw(e,a)}function wA(){const e=Be.getState(),i=e.root||e.info.root;if(!i)return"No folder is open yet. Ask the user to choose a workspace folder in the Workspace panel before using file, shell or git tools.";const a=e.entries.filter(c=>!c.path.includes("/")).slice(0,40).map(c=>`${c.path}${c.type==="dir"?"/":""}`),r=e.git.available?`${e.git.branch||"detached HEAD"} · ${e.git.entries.length} changed file${e.git.entries.length===1?"":"s"}`:"not a git repository";return[`Root: ${i}`,`Git: ${r}`,a.length>0?`Top level: ${a.join(", ")}`:"The folder is empty.",e.truncated?"The file tree was truncated; use list_files or grep for deeper paths.":""].filter(Boolean).join(`
44
- `)}class kA extends ki{static provide="systemPrompt";customSections=new Map;constructor(i){super(i,"systemPrompt")}registerSection(i,a){return this.customSections.set(i,a),()=>this.customSections.delete(i)}assemble(i,a=i.mode,r){const c=[],u=Be.getState(),d=r?this.ctx.sessions.current(r):void 0,p=Qw(i,d,u.root||u.info.root);p.text.trim()&&c.push({name:"base",priority:10,content:p.text.trim()});const h=Xw();h&&i.tools.skill?.enabled!==!1&&c.push({name:"skills",priority:20,content:h}),c.push({name:"workspace",priority:30,content:["## Workspace","You operate on the real folder below through the local OpenSol daemon. Tools return real output; never invent file contents, command results, URLs or deployments.",wA()].join(`
44
+ `)}class kA extends ki{static provide="systemPrompt";static inject=["sessions"];customSections=new Map;constructor(i){super(i,"systemPrompt")}registerSection(i,a){return this.customSections.set(i,a),()=>this.customSections.delete(i)}assemble(i,a=i.mode,r){const c=[],u=Be.getState(),d=r?this.ctx.sessions.current(r):void 0,p=Qw(i,d,u.root||u.info.root);p.text.trim()&&c.push({name:"base",priority:10,content:p.text.trim()});const h=Xw();h&&i.tools.skill?.enabled!==!1&&c.push({name:"skills",priority:20,content:h}),c.push({name:"workspace",priority:30,content:["## Workspace","You operate on the real folder below through the local OpenSol daemon. Tools return real output; never invent file contents, command results, URLs or deployments.",wA()].join(`
45
45
 
46
46
  `)});const g=Be.getState().instructions;g.content.trim()&&c.push({name:"project-instructions",priority:35,content:[`## Project instructions (${g.file})`,"The owner of this project wrote the notes below. Follow them for conventions, commands and style. They never override safety, tool permissions or plan mode.",`<project-instructions file="${g.file}">
47
47
  ${g.content.trim()}${g.truncated?`