@loicngr/kobo 1.6.0 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +6 -1
- package/README.md +29 -16
- package/dist/server/db/index.js +17 -0
- package/dist/server/db/migrations.js +10 -0
- package/dist/server/db/schema.js +2 -1
- package/dist/server/index.js +24 -3
- package/dist/server/middleware/migration-guard.js +15 -0
- package/dist/server/routes/dev-server.js +3 -2
- package/dist/server/routes/engines.js +9 -0
- package/dist/server/routes/migration.js +5 -0
- package/dist/server/routes/workspaces.js +35 -11
- package/dist/server/services/agent/engines/claude-code/args-builder.js +22 -0
- package/dist/server/services/agent/engines/claude-code/capabilities.js +17 -0
- package/dist/server/services/agent/engines/claude-code/engine.js +163 -0
- package/dist/server/services/agent/engines/claude-code/mcp-config.js +23 -0
- package/dist/server/services/agent/engines/claude-code/stream-parser.js +224 -0
- package/dist/server/services/agent/engines/registry.js +21 -0
- package/dist/server/services/agent/engines/types.js +18 -0
- package/dist/server/services/agent/event-router.js +4 -0
- package/dist/server/services/agent/orchestrator.js +582 -0
- package/dist/server/services/agent/session-controller.js +79 -0
- package/dist/server/services/content-migration-service.js +155 -0
- package/dist/server/services/db-backup-service.js +15 -0
- package/dist/server/services/websocket-service.js +81 -50
- package/dist/server/services/workspace-service.js +11 -5
- package/dist/server/utils/paths.js +1 -1
- package/dist/shared/models.js +50 -0
- package/package.json +1 -1
- package/src/client/dist/spa/assets/ActivityFeed-DYtAK49y.js +7 -0
- package/src/client/dist/spa/assets/ActivityFeed-DiwnrdKX.css +1 -0
- package/src/client/dist/spa/assets/ClosePopup-DqhgFbQo.js +1 -0
- package/src/client/dist/spa/assets/CreatePage-DENfwzPL.js +2 -0
- package/src/client/dist/spa/assets/CreatePage-yu2IH7GW.css +1 -0
- package/src/client/dist/spa/assets/DiffViewer-C6q11kmw.js +2 -0
- package/src/client/dist/spa/assets/HealthPage-Cjc79NaA.js +1 -0
- package/src/client/dist/spa/assets/{MainLayout-_oPM07ln.js → MainLayout-CFbMw65L.js} +17 -17
- package/src/client/dist/spa/assets/QBadge-BUkmTO0P.js +1 -0
- package/src/client/dist/spa/assets/QBtn-p1aZtrJH.js +1 -0
- package/src/client/dist/spa/assets/QDialog-D42GLa1i.js +1 -0
- package/src/client/dist/spa/assets/QExpansionItem-5ekmpO-2.js +1 -0
- package/src/client/dist/spa/assets/{QSpinner-CliSLjf8.js → QIcon-B0-pH3Qs.js} +1 -1
- package/src/client/dist/spa/assets/QItemLabel-Czw5g0px.js +1 -0
- package/src/client/dist/spa/assets/QItemSection-GlMrLmz3.js +1 -0
- package/src/client/dist/spa/assets/QList-DNzlynsS.js +1 -0
- package/src/client/dist/spa/assets/QMenu-Q69oVX7b.js +1 -0
- package/src/client/dist/spa/assets/QPage-B09NY4Nf.js +1 -0
- package/src/client/dist/spa/assets/QScrollArea-L6wUiA20.js +1 -0
- package/src/client/dist/spa/assets/QSeparator-rkjCbX2M.js +1 -0
- package/src/client/dist/spa/assets/QSpace-PlDK6Fg3.js +1 -0
- package/src/client/dist/spa/assets/QSpinnerDots-By20ptst.js +1 -0
- package/src/client/dist/spa/assets/QTabPanels-Crs-ujNO.js +1 -0
- package/src/client/dist/spa/assets/QTooltip-Cg9E3Dvw.js +1 -0
- package/src/client/dist/spa/assets/SearchPage-Bf-iZnyE.js +1 -0
- package/src/client/dist/spa/assets/SettingsPage-BdcH3BSs.js +1 -0
- package/src/client/dist/spa/assets/TouchPan-DFx22dM3.js +1 -0
- package/src/client/dist/spa/assets/WorkspacePage-DPGiH02q.css +1 -0
- package/src/client/dist/spa/assets/WorkspacePage-UUE0pPCR.js +4 -0
- package/src/client/dist/spa/assets/{cssMode-DMX8jq8u.js → cssMode-BYtqFZtm.js} +1 -1
- package/src/client/dist/spa/assets/{editor.api-DirOkGGg.js → editor.api-D6ZaO4A_.js} +1 -1
- package/src/client/dist/spa/assets/{editor.main-DC4ezIu0.js → editor.main-Cc_RDKsq.js} +3 -3
- package/src/client/dist/spa/assets/format-uvONOeL4.js +1 -0
- package/src/client/dist/spa/assets/{formatters-BzaS4w0I.js → formatters-DiJ12fKd.js} +1 -1
- package/src/client/dist/spa/assets/{freemarker2-DI9xJfj0.js → freemarker2-CBm--bBd.js} +1 -1
- package/src/client/dist/spa/assets/{handlebars-B9F-pScn.js → handlebars-whX2mkV5.js} +1 -1
- package/src/client/dist/spa/assets/{html-DTe2v8Q8.js → html-D7ga_o6c.js} +1 -1
- package/src/client/dist/spa/assets/{htmlMode-F_XLjWfJ.js → htmlMode-BXImjcsv.js} +1 -1
- package/src/client/dist/spa/assets/i18n-BxLBrD1J.js +1 -0
- package/src/client/dist/spa/assets/index-D997aY4Y.js +2 -0
- package/src/client/dist/spa/assets/{javascript-B9xJRPC6.js → javascript-BwmzNMn5.js} +1 -1
- package/src/client/dist/spa/assets/{jsonMode-DTZ6j6UO.js → jsonMode-CN5Z5bK_.js} +1 -1
- package/src/client/dist/spa/assets/{liquid-BjU5MtW6.js → liquid-CzMNAPor.js} +1 -1
- package/src/client/dist/spa/assets/{marked.esm-DCmk6NO8.js → marked.esm-DW0ulF0a.js} +1 -1
- package/src/client/dist/spa/assets/{mdx-BMUpG7Be.js → mdx-DC_P05Da.js} +1 -1
- package/src/client/dist/spa/assets/models-DMQoi09X.js +1 -0
- package/src/client/dist/spa/assets/{monaco.contribution-D7JUf8DP.js → monaco.contribution-BsBaFOOD.js} +2 -2
- package/src/client/dist/spa/assets/private.use-form-D1RuEt2P.js +1 -0
- package/src/client/dist/spa/assets/{python-Dz0D4uSk.js → python-9DTZ8C3K.js} +1 -1
- package/src/client/dist/spa/assets/{razor-D7CFxuwR.js → razor-B1LfM20o.js} +1 -1
- package/src/client/dist/spa/assets/scroll-Dh2g7BwR.js +1 -0
- package/src/client/dist/spa/assets/touch-D_A29lik.js +1 -0
- package/src/client/dist/spa/assets/{tsMode-DjscaxpS.js → tsMode-DI2bWo8r.js} +1 -1
- package/src/client/dist/spa/assets/{typescript-DozCWZl2.js → typescript-BZ9QJ2_N.js} +1 -1
- package/src/client/dist/spa/assets/use-id-CeduaJbU.js +1 -0
- package/src/client/dist/spa/assets/use-portal-mhLq4Rqk.js +1 -0
- package/src/client/dist/spa/assets/use-quasar-BBrzedjR.js +1 -0
- package/src/client/dist/spa/assets/{xml-DFOJMT39.js → xml-D6qm6rp0.js} +1 -1
- package/src/client/dist/spa/assets/{yaml-yEefnsXm.js → yaml-D2dUr_wY.js} +1 -1
- package/src/client/dist/spa/index.html +11 -14
- package/src/mcp-server/README.md +1 -1
- package/dist/server/services/agent-manager.js +0 -621
- package/src/client/dist/spa/assets/ActivityFeed-0GR1zPoc.js +0 -10
- package/src/client/dist/spa/assets/ActivityFeed-CfsKExt9.css +0 -1
- package/src/client/dist/spa/assets/ClosePopup-CdSn7HO8.js +0 -1
- package/src/client/dist/spa/assets/CreatePage-dMi4xVYN.css +0 -1
- package/src/client/dist/spa/assets/CreatePage-je_7dC5I.js +0 -2
- package/src/client/dist/spa/assets/DiffViewer-DREYX-8k.js +0 -2
- package/src/client/dist/spa/assets/HealthPage-Do8QZdxw.js +0 -1
- package/src/client/dist/spa/assets/QBadge-Bvh-hQ8K.js +0 -1
- package/src/client/dist/spa/assets/QBtn-BsD8vrWq.js +0 -1
- package/src/client/dist/spa/assets/QDialog-CkbLS1If.js +0 -1
- package/src/client/dist/spa/assets/QExpansionItem-UgkE560c.js +0 -1
- package/src/client/dist/spa/assets/QList-D80ms7bw.js +0 -1
- package/src/client/dist/spa/assets/QMenu-DU-wiY_A.js +0 -1
- package/src/client/dist/spa/assets/QPage-BKY2-sf-.js +0 -1
- package/src/client/dist/spa/assets/QSpace-C5Ebr0vq.js +0 -1
- package/src/client/dist/spa/assets/QSpinnerDots-Dp12eHrB.js +0 -1
- package/src/client/dist/spa/assets/QTabPanels-C7lWp1yU.js +0 -1
- package/src/client/dist/spa/assets/QToggle-B0HvuNEg.js +0 -1
- package/src/client/dist/spa/assets/QTooltip-kLXuUa_m.js +0 -1
- package/src/client/dist/spa/assets/SearchPage-CCfyqBKh.js +0 -1
- package/src/client/dist/spa/assets/SettingsPage-CmyIsV-S.js +0 -1
- package/src/client/dist/spa/assets/TouchPan-CVMnGs0y.js +0 -1
- package/src/client/dist/spa/assets/WorkspacePage-CWRMLYs-.css +0 -1
- package/src/client/dist/spa/assets/WorkspacePage-Cl7YrG51.js +0 -4
- package/src/client/dist/spa/assets/focus-manager-DYbz9jFW.js +0 -1
- package/src/client/dist/spa/assets/format-Cyg8IgRi.js +0 -1
- package/src/client/dist/spa/assets/i18n-B13zBh1H.js +0 -1
- package/src/client/dist/spa/assets/i18n-CCWLBc0p.js +0 -1
- package/src/client/dist/spa/assets/index-DoNZ_5QK.js +0 -5
- package/src/client/dist/spa/assets/models-B8fzv7K4.js +0 -1
- package/src/client/dist/spa/assets/pinia-C3JsrLkB.js +0 -1
- package/src/client/dist/spa/assets/private.use-form-BhKyDtO7.js +0 -1
- package/src/client/dist/spa/assets/scroll-CLibRGI-.js +0 -1
- package/src/client/dist/spa/assets/settings-B69lIVX0.js +0 -1
- package/src/client/dist/spa/assets/touch-ChrvzrnI.js +0 -1
- package/src/client/dist/spa/assets/use-dark-DnuCB6tC.js +0 -1
- package/src/client/dist/spa/assets/use-quasar-DBoizHBW.js +0 -1
- /package/src/client/dist/spa/assets/{_plugin-vue_export-helper-Cxt1D8wE.js → _plugin-vue_export-helper-CEhRWsKN.js} +0 -0
- /package/src/client/dist/spa/assets/{abap-CFuyUYKP.js → abap-DiwvWnMr.js} +0 -0
- /package/src/client/dist/spa/assets/{apex-Ctq_xcrv.js → apex-CmtZjKlf.js} +0 -0
- /package/src/client/dist/spa/assets/{azcli-BBQSVn-C.js → azcli-DL2My_i-.js} +0 -0
- /package/src/client/dist/spa/assets/{bat-DbnqAfvr.js → bat-B-nC98wG.js} +0 -0
- /package/src/client/dist/spa/assets/{bicep-BtDlIXop.js → bicep-Ju5MwOgh.js} +0 -0
- /package/src/client/dist/spa/assets/{cameligo-BLeJgKTj.js → cameligo-8Eu1TyBr.js} +0 -0
- /package/src/client/dist/spa/assets/{clojure-aZUQIUKP.js → clojure-u-RpMkH3.js} +0 -0
- /package/src/client/dist/spa/assets/{coffee-Secadq9U.js → coffee-CdA7bbTe.js} +0 -0
- /package/src/client/dist/spa/assets/{cpp-JicRPTRv.js → cpp-CzNFP8ks.js} +0 -0
- /package/src/client/dist/spa/assets/{csharp-C7NSOZyj.js → csharp-j1LThmcE.js} +0 -0
- /package/src/client/dist/spa/assets/{csp-CIje7830.js → csp-CLRC61y6.js} +0 -0
- /package/src/client/dist/spa/assets/{css-G0bm1q_M.js → css-r6rC_7P2.js} +0 -0
- /package/src/client/dist/spa/assets/{cypher-CldD5D0u.js → cypher-CW08XVUh.js} +0 -0
- /package/src/client/dist/spa/assets/{dart-DIK3l8YT.js → dart-Cs9aL5T_.js} +0 -0
- /package/src/client/dist/spa/assets/{dockerfile-czxaGh2L.js → dockerfile-BWM0M184.js} +0 -0
- /package/src/client/dist/spa/assets/{ecl-BqdYhwmw.js → ecl-MJJuer5P.js} +0 -0
- /package/src/client/dist/spa/assets/{elixir-m52LePTW.js → elixir-D2AIuXqn.js} +0 -0
- /package/src/client/dist/spa/assets/{flow9-B5QJ9GvZ.js → flow9-B2H24giC.js} +0 -0
- /package/src/client/dist/spa/assets/{fsharp-B15czHsH.js → fsharp-CMk2OIJN.js} +0 -0
- /package/src/client/dist/spa/assets/{go-BkoQxDo1.js → go-BrMkuJg0.js} +0 -0
- /package/src/client/dist/spa/assets/{graphql-BnI6uRa_.js → graphql-PSR1UKGv.js} +0 -0
- /package/src/client/dist/spa/assets/{hcl-CAwwENT7.js → hcl-DAQrbDOW.js} +0 -0
- /package/src/client/dist/spa/assets/{ini-BHM5zh1H.js → ini-0TG5BxW0.js} +0 -0
- /package/src/client/dist/spa/assets/{java-B5i95QvQ.js → java-rgorz17v.js} +0 -0
- /package/src/client/dist/spa/assets/{julia-DPDm885q.js → julia-C8VMdHm8.js} +0 -0
- /package/src/client/dist/spa/assets/{kotlin-qoccd5BP.js → kotlin-CllWo3gX.js} +0 -0
- /package/src/client/dist/spa/assets/{less-B6RU166D.js → less-Cgca25AP.js} +0 -0
- /package/src/client/dist/spa/assets/{lexon-YfUeoL1V.js → lexon-D0GHdBaw.js} +0 -0
- /package/src/client/dist/spa/assets/{lua-BIUI5y9b.js → lua-DmRsNG-P.js} +0 -0
- /package/src/client/dist/spa/assets/{m3-D5SAbSdU.js → m3-BgL5dNKT.js} +0 -0
- /package/src/client/dist/spa/assets/{markdown-CVJLwHzJ.js → markdown-BuJfycGS.js} +0 -0
- /package/src/client/dist/spa/assets/{mips-R-FZ3zOR.js → mips-C9m_93PR.js} +0 -0
- /package/src/client/dist/spa/assets/{msdax-Blveyl9r.js → msdax-CpFHC9OI.js} +0 -0
- /package/src/client/dist/spa/assets/{mysql-D4mY1AFx.js → mysql-qFvltsqN.js} +0 -0
- /package/src/client/dist/spa/assets/{objective-c-BmXrLr4h.js → objective-c-Bnmr858J.js} +0 -0
- /package/src/client/dist/spa/assets/{pascal-yxckoyvV.js → pascal-WP0_D5AO.js} +0 -0
- /package/src/client/dist/spa/assets/{pascaligo-Q5JCwXMI.js → pascaligo-Blom4Rij.js} +0 -0
- /package/src/client/dist/spa/assets/{perl-BF1Rrs5h.js → perl-B-vk8g64.js} +0 -0
- /package/src/client/dist/spa/assets/{pgsql-CnYB97wm.js → pgsql-Cgvz6v67.js} +0 -0
- /package/src/client/dist/spa/assets/{php-CdDfQfSg.js → php-8a3Lrw9m.js} +0 -0
- /package/src/client/dist/spa/assets/{pla-whj-d71F.js → pla-DuFqEZ8V.js} +0 -0
- /package/src/client/dist/spa/assets/{postiats-ClfLr4I-.js → postiats-DkLtSgkp.js} +0 -0
- /package/src/client/dist/spa/assets/{powerquery-iRaBhuuk.js → powerquery-BJ1aNepW.js} +0 -0
- /package/src/client/dist/spa/assets/{powershell-DjiEt5xK.js → powershell-rE98k687.js} +0 -0
- /package/src/client/dist/spa/assets/{protobuf-B6dcIEUr.js → protobuf-CUheFacr.js} +0 -0
- /package/src/client/dist/spa/assets/{pug-DtmHnjM9.js → pug-LDcAMD8w.js} +0 -0
- /package/src/client/dist/spa/assets/{qsharp-CELCyd79.js → qsharp-DUKSQoR1.js} +0 -0
- /package/src/client/dist/spa/assets/{r-ZpJXWV-o.js → r-D-QApv87.js} +0 -0
- /package/src/client/dist/spa/assets/{rate-limit-labels-dCPVjS61.js → rate-limit-labels-BvYERsho.js} +0 -0
- /package/src/client/dist/spa/assets/{redis-BiHSNkAl.js → redis-SXdDyWR9.js} +0 -0
- /package/src/client/dist/spa/assets/{redshift-DzuwYCHP.js → redshift-Y6lsCryn.js} +0 -0
- /package/src/client/dist/spa/assets/{restructuredtext-YOT94bbS.js → restructuredtext-edObr9a8.js} +0 -0
- /package/src/client/dist/spa/assets/{ruby-BfiHr6Uu.js → ruby-CNnUfF-8.js} +0 -0
- /package/src/client/dist/spa/assets/{rust-JZ-uOoYM.js → rust-IHUZWzBr.js} +0 -0
- /package/src/client/dist/spa/assets/{sb-CBglP1-t.js → sb-DrUvY44N.js} +0 -0
- /package/src/client/dist/spa/assets/{scala-C9l41paw.js → scala-B4hbXGLM.js} +0 -0
- /package/src/client/dist/spa/assets/{scheme-B-InQ6hy.js → scheme-BGrd12j3.js} +0 -0
- /package/src/client/dist/spa/assets/{scss-v6OmJRN9.js → scss-x5G1ES4U.js} +0 -0
- /package/src/client/dist/spa/assets/{shell-Dyp6iwB6.js → shell-DOehe2Y8.js} +0 -0
- /package/src/client/dist/spa/assets/{solidity-D5epNWue.js → solidity-BeRvcwWV.js} +0 -0
- /package/src/client/dist/spa/assets/{sophia-Eva-79sB.js → sophia-DZbkUNjy.js} +0 -0
- /package/src/client/dist/spa/assets/{sparql-gvALLO1w.js → sparql-B7_oi5-h.js} +0 -0
- /package/src/client/dist/spa/assets/{sql-COdamZYI.js → sql-CTlsFWVE.js} +0 -0
- /package/src/client/dist/spa/assets/{st-eMoImIwE.js → st-DJVEJdPE.js} +0 -0
- /package/src/client/dist/spa/assets/{swift-7R_T9RYH.js → swift-CwhT3fYa.js} +0 -0
- /package/src/client/dist/spa/assets/{symbols-CAg-nBkV.js → symbols-DCYodwb2.js} +0 -0
- /package/src/client/dist/spa/assets/{systemverilog-1pCEfaHU.js → systemverilog-BQN63pkN.js} +0 -0
- /package/src/client/dist/spa/assets/{tcl-B_KgnhfE.js → tcl-DqwfpskA.js} +0 -0
- /package/src/client/dist/spa/assets/{twig-CFZUJxb9.js → twig-BiyenUgc.js} +0 -0
- /package/src/client/dist/spa/assets/{typespec-B1ZgHlud.js → typespec-CWOJribt.js} +0 -0
- /package/src/client/dist/spa/assets/{vb-DKdun5tL.js → vb-Cq5F87m3.js} +0 -0
- /package/src/client/dist/spa/assets/{vue-i18n-eUDnMrPl.js → vue-i18n-CeG0hR0Z.js} +0 -0
- /package/src/client/dist/spa/assets/{wgsl-CzNaxTrn.js → wgsl-BAvW2lVr.js} +0 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { existsSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
const MCP_FILENAME = '.mcp.json';
|
|
4
|
+
export function writeMcpConfig(workingDir, servers) {
|
|
5
|
+
const filePath = join(workingDir, MCP_FILENAME);
|
|
6
|
+
const mcpServers = {};
|
|
7
|
+
for (const s of servers) {
|
|
8
|
+
mcpServers[s.name] = { command: s.command, args: s.args, env: s.env };
|
|
9
|
+
}
|
|
10
|
+
writeFileSync(filePath, JSON.stringify({ mcpServers }, null, 2));
|
|
11
|
+
return filePath;
|
|
12
|
+
}
|
|
13
|
+
export function cleanupMcpConfig(workingDir) {
|
|
14
|
+
const filePath = join(workingDir, MCP_FILENAME);
|
|
15
|
+
if (existsSync(filePath)) {
|
|
16
|
+
try {
|
|
17
|
+
unlinkSync(filePath);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
// Best-effort cleanup
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
function normalizeResetsAt(raw) {
|
|
2
|
+
if (typeof raw === 'string' && raw.length > 0)
|
|
3
|
+
return raw;
|
|
4
|
+
if (typeof raw === 'number' && Number.isFinite(raw))
|
|
5
|
+
return new Date(raw * 1000).toISOString();
|
|
6
|
+
return undefined;
|
|
7
|
+
}
|
|
8
|
+
function extractUsedPct(source) {
|
|
9
|
+
const raw = (source.utilization ?? source.used_percent ?? source.percent_used ?? source.usedPct);
|
|
10
|
+
if (typeof raw === 'number' && Number.isFinite(raw))
|
|
11
|
+
return raw <= 1 ? raw * 100 : raw;
|
|
12
|
+
const used = source.used ?? source.current ?? source.spent;
|
|
13
|
+
const limit = source.limit ?? source.max ?? source.allowed;
|
|
14
|
+
if (typeof used === 'number' && typeof limit === 'number' && limit > 0)
|
|
15
|
+
return (used / limit) * 100;
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
function makeBucket(id, source) {
|
|
19
|
+
const usedPct = extractUsedPct(source) ?? source.__fallbackPct ?? null;
|
|
20
|
+
if (usedPct === null)
|
|
21
|
+
return null;
|
|
22
|
+
const resetsAt = normalizeResetsAt(source.resets_at ?? source.reset_at ?? source.resetsAt ?? source.resetAt);
|
|
23
|
+
const label = (typeof source.label === 'string' && source.label) || undefined;
|
|
24
|
+
const used = source.used ?? source.current ?? source.spent;
|
|
25
|
+
const limit = source.limit ?? source.max ?? source.allowed;
|
|
26
|
+
const details = used !== undefined && limit !== undefined ? `${String(used)} / ${String(limit)}` : undefined;
|
|
27
|
+
return { id, label, usedPct: Math.max(0, Math.min(100, usedPct)), resetsAt, details };
|
|
28
|
+
}
|
|
29
|
+
function normalizeRateLimitInfo(info) {
|
|
30
|
+
const buckets = [];
|
|
31
|
+
if (typeof info.rateLimitType === 'string') {
|
|
32
|
+
const b = makeBucket(info.rateLimitType, { ...info, __fallbackPct: 0 });
|
|
33
|
+
if (b)
|
|
34
|
+
buckets.push(b);
|
|
35
|
+
}
|
|
36
|
+
if (Array.isArray(info.buckets)) {
|
|
37
|
+
for (const entry of info.buckets) {
|
|
38
|
+
if (!entry || typeof entry !== 'object')
|
|
39
|
+
continue;
|
|
40
|
+
const obj = entry;
|
|
41
|
+
const id = (typeof obj.id === 'string' && obj.id) ||
|
|
42
|
+
(typeof obj.name === 'string' && obj.name) ||
|
|
43
|
+
(typeof obj.rateLimitType === 'string' && obj.rateLimitType) ||
|
|
44
|
+
'unknown';
|
|
45
|
+
const b = makeBucket(id, obj);
|
|
46
|
+
if (b)
|
|
47
|
+
buckets.push(b);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return { buckets };
|
|
51
|
+
}
|
|
52
|
+
export function createParserState() {
|
|
53
|
+
return { sessionStartedEmitted: false, openMessages: new Map() };
|
|
54
|
+
}
|
|
55
|
+
export function parseClaudeLine(line, state) {
|
|
56
|
+
const trimmed = line.trim();
|
|
57
|
+
if (!trimmed)
|
|
58
|
+
return { events: [], state };
|
|
59
|
+
// The marker can appear as a raw stdout line OR inside an assistant text block.
|
|
60
|
+
// We detect it in the raw line first so even unparseable lines that contain it
|
|
61
|
+
// still emit the signal. The assistant-branch handling below catches the
|
|
62
|
+
// structured case.
|
|
63
|
+
const markerDetected = trimmed.includes('[BRAINSTORM_COMPLETE]');
|
|
64
|
+
let parsed;
|
|
65
|
+
try {
|
|
66
|
+
parsed = JSON.parse(trimmed);
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
const events = [{ kind: 'message:raw', content: line }];
|
|
70
|
+
if (markerDetected)
|
|
71
|
+
events.push({ kind: 'session:brainstorm-complete' });
|
|
72
|
+
return { events, state };
|
|
73
|
+
}
|
|
74
|
+
const events = [];
|
|
75
|
+
const type = parsed.type;
|
|
76
|
+
const subtype = parsed.subtype;
|
|
77
|
+
const sessionId = typeof parsed.session_id === 'string' ? parsed.session_id : undefined;
|
|
78
|
+
if (type === 'system') {
|
|
79
|
+
if (subtype === 'compact' || subtype === 'compact_boundary') {
|
|
80
|
+
events.push({ kind: 'session:compacted' });
|
|
81
|
+
return { events, state };
|
|
82
|
+
}
|
|
83
|
+
if (subtype === 'rate_limit_event') {
|
|
84
|
+
const info = parsed.rate_limit_info;
|
|
85
|
+
if (info && typeof info === 'object') {
|
|
86
|
+
events.push({ kind: 'rate_limit', info: normalizeRateLimitInfo(info) });
|
|
87
|
+
}
|
|
88
|
+
return { events, state };
|
|
89
|
+
}
|
|
90
|
+
if (subtype === 'task_started' || subtype === 'task_progress' || subtype === 'task_notification') {
|
|
91
|
+
const toolCallId = typeof parsed.tool_use_id === 'string' ? parsed.tool_use_id : undefined;
|
|
92
|
+
if (toolCallId) {
|
|
93
|
+
const usage = parsed.usage;
|
|
94
|
+
const taskStatus = typeof parsed.status === 'string' ? parsed.status : undefined;
|
|
95
|
+
const isDone = subtype === 'task_notification' &&
|
|
96
|
+
taskStatus !== undefined &&
|
|
97
|
+
['completed', 'stopped', 'failed', 'cancelled'].includes(taskStatus);
|
|
98
|
+
events.push({
|
|
99
|
+
kind: 'subagent:progress',
|
|
100
|
+
toolCallId,
|
|
101
|
+
status: isDone ? 'done' : 'running',
|
|
102
|
+
description: typeof parsed.description === 'string' ? parsed.description : undefined,
|
|
103
|
+
taskType: typeof parsed.task_type === 'string' ? parsed.task_type : undefined,
|
|
104
|
+
lastToolName: typeof parsed.last_tool_name === 'string' ? parsed.last_tool_name : undefined,
|
|
105
|
+
totalTokens: typeof usage?.total_tokens === 'number' ? usage.total_tokens : undefined,
|
|
106
|
+
toolUses: typeof usage?.tool_uses === 'number' ? usage.tool_uses : undefined,
|
|
107
|
+
durationMs: typeof usage?.duration_ms === 'number' ? usage.duration_ms : undefined,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return { events, state };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (type === 'system' && subtype === 'init') {
|
|
114
|
+
if (sessionId && (!state.sessionStartedEmitted || state.sessionId !== sessionId)) {
|
|
115
|
+
events.push({
|
|
116
|
+
kind: 'session:started',
|
|
117
|
+
engineSessionId: sessionId,
|
|
118
|
+
model: typeof parsed.model === 'string' ? parsed.model : undefined,
|
|
119
|
+
});
|
|
120
|
+
state.sessionStartedEmitted = true;
|
|
121
|
+
state.sessionId = sessionId;
|
|
122
|
+
}
|
|
123
|
+
if (Array.isArray(parsed.slash_commands) && parsed.slash_commands.length > 0) {
|
|
124
|
+
events.push({ kind: 'skills:discovered', skills: parsed.slash_commands });
|
|
125
|
+
}
|
|
126
|
+
return { events, state };
|
|
127
|
+
}
|
|
128
|
+
if (type === 'assistant') {
|
|
129
|
+
const message = parsed.message;
|
|
130
|
+
const messageId = typeof message?.id === 'string' ? message.id : 'unknown';
|
|
131
|
+
const content = Array.isArray(message?.content) ? message?.content : [];
|
|
132
|
+
// A new messageId arriving means any previously-open message is done.
|
|
133
|
+
// Claude CLI's stream-json output doesn't always carry an explicit
|
|
134
|
+
// `stop_reason` or `message_stop` on the last chunk; some runs finish
|
|
135
|
+
// implicitly when the next turn begins. Close stale openMessages here
|
|
136
|
+
// so the UI's streaming spinner doesn't hang forever.
|
|
137
|
+
for (const openId of Array.from(state.openMessages.keys())) {
|
|
138
|
+
if (openId !== messageId) {
|
|
139
|
+
events.push({ kind: 'message:end', messageId: openId });
|
|
140
|
+
state.openMessages.delete(openId);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (!state.openMessages.has(messageId)) {
|
|
144
|
+
state.openMessages.set(messageId, { sawText: false });
|
|
145
|
+
}
|
|
146
|
+
const msgState = state.openMessages.get(messageId);
|
|
147
|
+
for (const block of content) {
|
|
148
|
+
const blockType = block.type;
|
|
149
|
+
if (blockType === 'text' && typeof block.text === 'string') {
|
|
150
|
+
events.push({ kind: 'message:text', messageId, text: block.text, streaming: true });
|
|
151
|
+
msgState.sawText = true;
|
|
152
|
+
}
|
|
153
|
+
if (blockType === 'tool_use') {
|
|
154
|
+
events.push({
|
|
155
|
+
kind: 'tool:call',
|
|
156
|
+
messageId,
|
|
157
|
+
toolCallId: typeof block.id === 'string' ? block.id : 'unknown',
|
|
158
|
+
name: typeof block.name === 'string' ? block.name : 'unknown',
|
|
159
|
+
input: block.input ?? {},
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
if (blockType === 'thinking') {
|
|
163
|
+
events.push({
|
|
164
|
+
kind: 'message:thinking',
|
|
165
|
+
messageId,
|
|
166
|
+
text: typeof block.thinking === 'string' ? block.thinking : '',
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
if (blockType === 'text' &&
|
|
170
|
+
typeof block.text === 'string' &&
|
|
171
|
+
block.text.includes('[BRAINSTORM_COMPLETE]')) {
|
|
172
|
+
events.push({ kind: 'session:brainstorm-complete' });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
// Claude CLI sends many intermediate deltas for the same message; most of
|
|
176
|
+
// them carry `stop_reason: null`. Only a truly terminal event has either
|
|
177
|
+
// `message_stop: true` at the root, or a non-null `stop_reason`. Checking
|
|
178
|
+
// `!== undefined` would spuriously emit message:end on every delta.
|
|
179
|
+
const stopReason = message?.stop_reason;
|
|
180
|
+
const isStop = parsed.message_stop === true || (stopReason !== undefined && stopReason !== null);
|
|
181
|
+
if (isStop) {
|
|
182
|
+
events.push({ kind: 'message:end', messageId });
|
|
183
|
+
state.openMessages.delete(messageId);
|
|
184
|
+
}
|
|
185
|
+
return { events, state };
|
|
186
|
+
}
|
|
187
|
+
if (type === 'user') {
|
|
188
|
+
const message = parsed.message;
|
|
189
|
+
const content = Array.isArray(message?.content) ? message?.content : [];
|
|
190
|
+
for (const block of content) {
|
|
191
|
+
if (block.type === 'tool_result') {
|
|
192
|
+
events.push({
|
|
193
|
+
kind: 'tool:result',
|
|
194
|
+
toolCallId: typeof block.tool_use_id === 'string' ? block.tool_use_id : 'unknown',
|
|
195
|
+
output: block.content ?? null,
|
|
196
|
+
isError: block.is_error === true,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return { events, state };
|
|
201
|
+
}
|
|
202
|
+
if (type === 'result') {
|
|
203
|
+
// Terminal event — close any message still considered "streaming".
|
|
204
|
+
for (const openId of Array.from(state.openMessages.keys())) {
|
|
205
|
+
events.push({ kind: 'message:end', messageId: openId });
|
|
206
|
+
state.openMessages.delete(openId);
|
|
207
|
+
}
|
|
208
|
+
const usage = parsed.usage;
|
|
209
|
+
if (usage) {
|
|
210
|
+
events.push({
|
|
211
|
+
kind: 'usage',
|
|
212
|
+
inputTokens: Number(usage.input_tokens ?? 0),
|
|
213
|
+
outputTokens: Number(usage.output_tokens ?? 0),
|
|
214
|
+
cacheRead: typeof usage.cache_read_input_tokens === 'number' ? usage.cache_read_input_tokens : undefined,
|
|
215
|
+
cacheWrite: typeof usage.cache_creation_input_tokens === 'number'
|
|
216
|
+
? usage.cache_creation_input_tokens
|
|
217
|
+
: undefined,
|
|
218
|
+
costUsd: typeof parsed.cost_usd === 'number' ? parsed.cost_usd : undefined,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
return { events, state };
|
|
222
|
+
}
|
|
223
|
+
return { events, state };
|
|
224
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { createClaudeCodeEngine } from './claude-code/engine.js';
|
|
2
|
+
const ENGINES = {
|
|
3
|
+
'claude-code': createClaudeCodeEngine(),
|
|
4
|
+
};
|
|
5
|
+
export function listEngines() {
|
|
6
|
+
return Object.values(ENGINES);
|
|
7
|
+
}
|
|
8
|
+
export function resolveEngine(id) {
|
|
9
|
+
const engine = ENGINES[id];
|
|
10
|
+
if (!engine)
|
|
11
|
+
throw new Error(`Unknown agent engine '${id}'`);
|
|
12
|
+
return engine;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Test-only seam. Replaces or adds an engine at runtime. Do not use in
|
|
16
|
+
* production — the static `ENGINES` map is the source of truth; this helper
|
|
17
|
+
* exists only so unit tests can inject fakes without wiring a DI container.
|
|
18
|
+
*/
|
|
19
|
+
export function _registerEngineForTest(engine) {
|
|
20
|
+
ENGINES[engine.id] = engine;
|
|
21
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Every AgentEvent kind, as a const for exhaustive iteration in tests. */
|
|
2
|
+
export const ALL_AGENT_EVENT_KINDS = [
|
|
3
|
+
'session:started',
|
|
4
|
+
'session:ended',
|
|
5
|
+
'session:compacted',
|
|
6
|
+
'session:brainstorm-complete',
|
|
7
|
+
'message:text',
|
|
8
|
+
'message:thinking',
|
|
9
|
+
'message:end',
|
|
10
|
+
'message:raw',
|
|
11
|
+
'tool:call',
|
|
12
|
+
'tool:result',
|
|
13
|
+
'subagent:progress',
|
|
14
|
+
'skills:discovered',
|
|
15
|
+
'usage',
|
|
16
|
+
'rate_limit',
|
|
17
|
+
'error',
|
|
18
|
+
];
|