@rho-workspace/rho 0.5.3 → 0.5.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.
- package/README.md +24 -5
- package/dist/browser-access.js +3 -2
- package/dist/capabilities.d.ts +29 -2
- package/dist/capabilities.js +79 -2
- package/dist/cli.js +237 -99
- package/dist/contract.generated.d.ts +441 -16
- package/dist/contract.generated.js +1 -1
- package/dist/contracts/owners.generated.js +459 -10
- package/dist/conversation-presentation.d.ts +3 -1
- package/dist/conversation-presentation.js +16 -6
- package/dist/conversations.d.ts +10 -6
- package/dist/conversations.js +17 -13
- package/dist/discovery.d.ts +1 -0
- package/dist/discovery.js +3 -3
- package/dist/extension-contract.d.ts +99 -0
- package/dist/extension-contract.js +52 -0
- package/dist/extension-directory.d.ts +9 -0
- package/dist/extension-directory.js +11 -0
- package/dist/extension-worker.d.ts +1 -0
- package/dist/extension-worker.js +36 -0
- package/dist/extensions.d.ts +157 -0
- package/dist/extensions.js +349 -0
- package/dist/installs.d.ts +11 -0
- package/dist/installs.js +179 -0
- package/dist/plugin-cli.d.ts +1 -0
- package/dist/plugin-cli.js +139 -0
- package/dist/plugin-inventory.json +18 -0
- package/dist/plugins.d.ts +3 -3
- package/dist/project-notes.js +2 -0
- package/dist/project-work.d.ts +3 -1
- package/dist/project-work.js +15 -2
- package/dist/protocol.d.ts +7 -0
- package/dist/protocol.js +11 -7
- package/dist/screen-check-skill.d.ts +2 -0
- package/dist/screen-check-skill.js +39 -0
- package/dist/server.d.ts +6 -1
- package/dist/server.js +105 -15
- package/dist/web/.vite/manifest.json +65 -65
- package/dist/web/assets/{canvas-BXhC8RDZ.js → canvas-D8rDdfDz.js} +1 -1
- package/dist/web/assets/choices-b0XhrGQV.js +1 -0
- package/dist/web/assets/{console-e1P5qYQ5.js → console-BSjLGe0T.js} +1 -1
- package/dist/web/assets/{dist-Dd3iAGFy.js → dist-Buk0lwzI.js} +2 -2
- package/dist/web/assets/dist-Cw8hRIlT.js +1 -0
- package/dist/web/assets/dist-Tzoe_zm-.js +3 -0
- package/dist/web/assets/{dist-DEorbOW0.js → dist-foH9IKc6.js} +1 -1
- package/dist/web/assets/explore-in-BVUy3ZW_.js +1 -0
- package/dist/web/assets/graph-1OdIJ9l7.js +2 -0
- package/dist/web/assets/index-CsC3sTRq.css +1 -0
- package/dist/web/assets/{index-BYuKpyoi.js → index-CxmItXqa.js} +220 -121
- package/dist/web/assets/{notes-meaning-B3HFbh84.js → notes-meaning-pVbbQUGP.js} +1 -1
- package/dist/web/assets/overview-B9UtVJ32.js +6 -0
- package/dist/web/assets/preview-CksD_5KS.js +1 -0
- package/dist/web/assets/{review-BVh9jWMB.js → review-CMxQlQ-C.js} +1 -1
- package/dist/web/assets/{skills-DPaEYkln.js → skills-BFNSyrnD.js} +1 -1
- package/dist/web/assets/{skills-work-dvjfy_tu.js → skills-work-BGPSnF_I.js} +1 -1
- package/dist/web/assets/{studio-process-BYm8mjRJ.js → studio-process-CsRoTHOh.js} +2 -2
- package/dist/web/assets/{timeline-events-CDZ7wiJX.js → timeline-events-BbuuFWHV.js} +1 -1
- package/dist/web/assets/{work-turns-BeCt303c.js → work-turns-BvJlIMmA.js} +1 -1
- package/dist/web/assets/workspace-CWlkjLNx.js +5 -0
- package/dist/web/composition.json +80 -80
- package/dist/web/index.html +3 -3
- package/dist/web/renderer.json +1 -1
- package/dist/web-assets.d.ts +2 -1
- package/dist/web-assets.js +3 -2
- package/dist/work-contract.js +2 -2
- package/docs/FRONTEND.md +30 -14
- package/docs/HEADLESS.md +60 -14
- package/docs/PLUGIN-AUTHORING.md +4 -0
- package/docs/THIRD-PARTY-PLUGINS.md +190 -0
- package/example/pa14-qs/.rho/rho/conversations/c-67f77cfebaba2cea.json +1 -1
- package/example/pa14-qs/.rho/rho/conversations/c-91aa2e46b1ac1a6c.json +1 -1
- package/example/pa14-qs/.rho/rho/conversations/c-fa24dfd80dfe4bcb.json +1 -1
- package/node_modules/@hono/node-server/dist/index.js +11 -0
- package/node_modules/@hono/node-server/dist/index.mjs +11 -0
- package/node_modules/@hono/node-server/dist/listener.js +11 -0
- package/node_modules/@hono/node-server/dist/listener.mjs +11 -0
- package/node_modules/@hono/node-server/dist/request.js +11 -0
- package/node_modules/@hono/node-server/dist/request.mjs +11 -0
- package/node_modules/@hono/node-server/dist/server.js +11 -0
- package/node_modules/@hono/node-server/dist/server.mjs +11 -0
- package/node_modules/@hono/node-server/dist/vercel.js +11 -0
- package/node_modules/@hono/node-server/dist/vercel.mjs +11 -0
- package/node_modules/@hono/node-server/package.json +1 -1
- package/node_modules/@rho-workspace/plugin-agent/README.md +42 -1
- package/node_modules/@rho-workspace/plugin-agent/dist/acp-conversation.js +14 -3
- package/node_modules/@rho-workspace/plugin-agent/dist/activity-normalize.d.ts +1 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/activity-normalize.js +2 -2
- package/node_modules/@rho-workspace/plugin-agent/dist/conversation.d.ts +1 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/conversation.js +1 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/dsh-session.d.ts +6 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/dsh-session.js +174 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/external-session-contract.d.ts +82 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/external-session-contract.js +15 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/external-session.d.ts +73 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/external-session.js +190 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/generic-acp.d.ts +11 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/generic-acp.js +7 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/index.d.ts +373 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/index.js +1 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/mcp.js +5 -1
- package/node_modules/@rho-workspace/plugin-agent/dist/operations.d.ts +33 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/operations.js +3 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/presentation.d.ts +14 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/presentation.js +2 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/ui-external-session.d.ts +15 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/ui-external-session.js +37 -0
- package/node_modules/@rho-workspace/plugin-agent/package.json +5 -1
- package/node_modules/@rho-workspace/plugin-annotations/package.json +1 -1
- package/node_modules/@rho-workspace/plugin-documents/README.md +4 -0
- package/node_modules/@rho-workspace/plugin-documents/dist/index.d.ts +3 -0
- package/node_modules/@rho-workspace/plugin-documents/dist/index.js +1 -1
- package/node_modules/@rho-workspace/plugin-documents/package.json +1 -1
- package/node_modules/@rho-workspace/plugin-kernel/README.md +71 -10
- package/node_modules/@rho-workspace/plugin-kernel/dist/code.d.ts +2 -0
- package/node_modules/@rho-workspace/plugin-kernel/dist/code.js +4 -1
- package/node_modules/@rho-workspace/plugin-kernel/dist/contract.js +5 -0
- package/node_modules/@rho-workspace/plugin-kernel/dist/index.d.ts +7 -0
- package/node_modules/@rho-workspace/plugin-kernel/dist/index.js +55 -8
- package/node_modules/@rho-workspace/plugin-kernel/dist/lineage.d.ts +9 -6
- package/node_modules/@rho-workspace/plugin-kernel/dist/lineage.js +18 -41
- package/node_modules/@rho-workspace/plugin-kernel/dist/operations.js +5 -2
- package/node_modules/@rho-workspace/plugin-kernel/dist/script-flow.d.ts +39 -0
- package/node_modules/@rho-workspace/plugin-kernel/dist/script-flow.js +661 -0
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui-base.d.ts +3 -0
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui-base.js +10 -0
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui-flow.d.ts +38 -4
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui-flow.js +194 -97
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui-notebook-work.js +1 -2
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui-notebook.d.ts +10 -2
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui-notebook.js +79 -18
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui-styles.js +115 -45
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui.d.ts +1 -1
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui.js +1 -1
- package/node_modules/@rho-workspace/plugin-kernel/dist/value-graph.d.ts +63 -0
- package/node_modules/@rho-workspace/plugin-kernel/dist/value-graph.js +344 -0
- package/node_modules/@rho-workspace/plugin-kernel/package.json +1 -1
- package/node_modules/@rho-workspace/plugin-kernel/runners/runner.R +6 -3
- package/node_modules/@rho-workspace/plugin-kernel/runners/runner.py +6 -3
- package/node_modules/express-rate-limit/dist/index.cjs +1 -1
- package/node_modules/express-rate-limit/dist/index.mjs +1 -1
- package/node_modules/express-rate-limit/package.json +1 -1
- package/node_modules/ip-address/dist/ipv6.js +1 -1
- package/node_modules/ip-address/package.json +1 -1
- package/node_modules/playwright-core/LICENSE +202 -0
- package/node_modules/playwright-core/NOTICE +5 -0
- package/node_modules/playwright-core/ThirdPartyNotices.txt +13 -0
- package/node_modules/playwright-core/bin/install_media_pack.ps1 +5 -0
- package/node_modules/playwright-core/bin/install_webkit_wsl.ps1 +33 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_beta_linux.sh +42 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_beta_mac.sh +13 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_beta_win.ps1 +24 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_stable_linux.sh +42 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_stable_mac.sh +12 -0
- package/node_modules/playwright-core/bin/reinstall_chrome_stable_win.ps1 +24 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_beta_linux.sh +48 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_beta_mac.sh +11 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_beta_win.ps1 +23 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_dev_linux.sh +48 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_dev_mac.sh +11 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_dev_win.ps1 +23 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_stable_linux.sh +48 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_stable_mac.sh +11 -0
- package/node_modules/playwright-core/bin/reinstall_msedge_stable_win.ps1 +24 -0
- package/node_modules/playwright-core/browsers.json +52 -0
- package/node_modules/playwright-core/cli.js +22 -0
- package/node_modules/playwright-core/index.js +17 -0
- package/node_modules/playwright-core/index.mjs +28 -0
- package/node_modules/playwright-core/lib/bootstrap.js +149 -0
- package/node_modules/playwright-core/lib/coreBundle.js +78041 -0
- package/node_modules/playwright-core/lib/entry/cliDaemon.js +5 -0
- package/node_modules/playwright-core/lib/entry/dashboardApp.js +3 -0
- package/node_modules/playwright-core/lib/entry/mcp.js +10 -0
- package/node_modules/playwright-core/lib/entry/oopBrowserDownload.js +3 -0
- package/node_modules/playwright-core/lib/package.js +50 -0
- package/node_modules/playwright-core/lib/server/chromium/appIcon.png +0 -0
- package/node_modules/playwright-core/lib/server/electron/loader.js +123 -0
- package/node_modules/playwright-core/lib/serverRegistry.js +1939 -0
- package/node_modules/playwright-core/lib/serverRegistry.js.LICENSE +65 -0
- package/node_modules/playwright-core/lib/tools/cli-client/channelSessions.js +141 -0
- package/node_modules/playwright-core/lib/tools/cli-client/cli.js +7 -0
- package/node_modules/playwright-core/lib/tools/cli-client/help.json +808 -0
- package/node_modules/playwright-core/lib/tools/cli-client/minimist.js +128 -0
- package/node_modules/playwright-core/lib/tools/cli-client/output.js +352 -0
- package/node_modules/playwright-core/lib/tools/cli-client/program.js +422 -0
- package/node_modules/playwright-core/lib/tools/cli-client/registry.js +180 -0
- package/node_modules/playwright-core/lib/tools/cli-client/session.js +261 -0
- package/node_modules/playwright-core/lib/tools/dashboard/appIcon.png +0 -0
- package/node_modules/playwright-core/lib/tools/utils/extension.js +112 -0
- package/node_modules/playwright-core/lib/tools/utils/socketConnection.js +108 -0
- package/node_modules/playwright-core/lib/utilsBundle.js +86078 -0
- package/node_modules/playwright-core/lib/utilsBundle.js.LICENSE +1890 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/codeMirrorModule--QdMvsKi.css +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/codeMirrorModule-CQ8RZGBm.js +32 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/codicon-DCmgc-ay.ttf +0 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/firefox-1bWoP6pv.svg +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/firefox-beta-k3eOH_eK.svg +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/firefox-nightly-Cp5nfeDT.svg +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/index-AwgwcYie.css +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/index-hRvO4yU2.js +12 -0
- package/node_modules/playwright-core/lib/vite/dashboard/assets/safari-na3_-uQk.svg +1 -0
- package/node_modules/playwright-core/lib/vite/dashboard/index.html +29 -0
- package/node_modules/playwright-core/lib/vite/dashboard/playwright-logo.svg +24 -0
- package/node_modules/playwright-core/lib/vite/htmlReport/index.html +16 -0
- package/node_modules/playwright-core/lib/vite/htmlReport/report.css +2 -0
- package/node_modules/playwright-core/lib/vite/htmlReport/report.js +32 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule--QdMvsKi.css +1 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule-DK72OMRi.js +32 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/codicon-DCmgc-ay.ttf +0 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/index-BZpYJZ-P.css +1 -0
- package/node_modules/playwright-core/lib/vite/recorder/assets/index-Cn0mmDuv.js +129 -0
- package/node_modules/playwright-core/lib/vite/recorder/index.html +29 -0
- package/node_modules/playwright-core/lib/vite/recorder/playwright-logo.svg +9 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/codeMirrorModule-BEB2e8M3.js +32 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-D-g2vrvf.js +184 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/urlMatch-L3liM589.js +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/assets/xtermModule-B794rD2Z.js +7 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/codeMirrorModule.-QdMvsKi.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/codicon.DCmgc-ay.ttf +0 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/defaultSettingsView.C8BeHuxd.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/index.B_TqY17P.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/index.html +44 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/index.oNry3Hjv.js +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/manifest.webmanifest +16 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/playwright-logo.svg +9 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/snapshot.B_Jk1wbt.js +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/snapshot.html +10 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/sw.bundle.js +4 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.CYwC_gH1.js +5 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.CyMfwkXJ.css +1 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +18 -0
- package/node_modules/playwright-core/lib/vite/traceViewer/xtermModule.kHJ-D0s7.css +1 -0
- package/node_modules/playwright-core/lib/webp_codec.LICENSE +173 -0
- package/node_modules/playwright-core/lib/webp_codec.wasm +0 -0
- package/node_modules/playwright-core/lib/xdg-open +1267 -0
- package/node_modules/playwright-core/package.json +34 -0
- package/package.json +13 -7
- package/protocol/index.json +75 -21
- package/protocol/operations/agent.sessions.list.json +100 -0
- package/protocol/operations/agent.sessions.read.json +266 -0
- package/protocol/operations/conversation.settings.check.json +147 -4
- package/protocol/operations/conversation.settings.read.json +145 -3
- package/protocol/operations/conversation.settings.update.json +166 -4
- package/protocol/operations/conversation.start.json +2 -1
- package/protocol/operations/extension.call.json +132 -0
- package/protocol/operations/extension.content.json +78 -0
- package/protocol/operations/extension.describe.json +195 -0
- package/protocol/operations/extension.install.json +198 -0
- package/protocol/operations/extension.list.json +123 -0
- package/protocol/operations/extension.manage.json +55 -0
- package/protocol/operations/extension.read.json +112 -0
- package/protocol/operations/kernel.interrupt.json +20 -0
- package/protocol/operations/kernel.read.json +30 -0
- package/protocol/operations/kernel.run.json +36 -1
- package/protocol/operations/kernel.stop.json +10 -0
- package/protocol/operations/project.work.read.json +3 -2
- package/protocol/operations/session.grant.json +9 -0
- package/scripts/browser-checks.mjs +53 -0
- package/scripts/screen-check.mjs +193 -0
- package/scripts/third-party-acceptance.mjs +77 -0
- package/vendor/composition.json +111 -111
- package/vendor/rho-workspace-plugin-agent-0.4.29.tgz +0 -0
- package/vendor/rho-workspace-plugin-annotations-0.5.2.tgz +0 -0
- package/vendor/rho-workspace-plugin-documents-0.4.11.tgz +0 -0
- package/vendor/rho-workspace-plugin-kernel-0.5.5.tgz +0 -0
- package/web/theme.css +17 -2
- package/dist/web/assets/choices-B-y2OYvE.js +0 -1
- package/dist/web/assets/dist-CBM2oq6g.js +0 -1
- package/dist/web/assets/dist-CH56yNDn.js +0 -3
- package/dist/web/assets/explore-in-muWXOGOY.js +0 -1
- package/dist/web/assets/graph-CK-df5lh.js +0 -2
- package/dist/web/assets/index-DBd77u4a.css +0 -1
- package/dist/web/assets/overview-D1Ev2OC_.js +0 -6
- package/dist/web/assets/preview-HbmQKUGY.js +0 -1
- package/dist/web/assets/workspace-d5Hw_ClZ.js +0 -5
- package/vendor/rho-workspace-plugin-agent-0.4.27.tgz +0 -0
- package/vendor/rho-workspace-plugin-documents-0.4.10.tgz +0 -0
- package/vendor/rho-workspace-plugin-kernel-0.5.2.tgz +0 -0
|
@@ -52,6 +52,15 @@ var RequestError = class extends Error {
|
|
|
52
52
|
this.name = "RequestError";
|
|
53
53
|
}
|
|
54
54
|
};
|
|
55
|
+
var validateRequestTarget = (target) => {
|
|
56
|
+
const backslash = target.indexOf("\\");
|
|
57
|
+
if (backslash !== -1) {
|
|
58
|
+
const query = target.indexOf("?");
|
|
59
|
+
if (query === -1 || backslash < query) {
|
|
60
|
+
throw new RequestError("Backslash in request path");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
55
64
|
var toRequestError = (e) => {
|
|
56
65
|
if (e instanceof RequestError) {
|
|
57
66
|
return e;
|
|
@@ -212,6 +221,7 @@ var newRequest = (incoming, defaultHostname) => {
|
|
|
212
221
|
throw new RequestError("Absolute URL for :path is not allowed in HTTP/2");
|
|
213
222
|
}
|
|
214
223
|
try {
|
|
224
|
+
validateRequestTarget(incomingUrl);
|
|
215
225
|
const url2 = new URL(incomingUrl);
|
|
216
226
|
req[urlKey] = url2.href;
|
|
217
227
|
} catch (e) {
|
|
@@ -232,6 +242,7 @@ var newRequest = (incoming, defaultHostname) => {
|
|
|
232
242
|
} else {
|
|
233
243
|
scheme = incoming.socket && incoming.socket.encrypted ? "https" : "http";
|
|
234
244
|
}
|
|
245
|
+
validateRequestTarget(incomingUrl);
|
|
235
246
|
const url = new URL(`${scheme}://${host}${incomingUrl}`);
|
|
236
247
|
if (url.hostname.length !== host.length && url.hostname !== host.replace(/:\d+$/, "")) {
|
|
237
248
|
throw new RequestError("Invalid host header");
|
|
@@ -13,6 +13,15 @@ var RequestError = class extends Error {
|
|
|
13
13
|
this.name = "RequestError";
|
|
14
14
|
}
|
|
15
15
|
};
|
|
16
|
+
var validateRequestTarget = (target) => {
|
|
17
|
+
const backslash = target.indexOf("\\");
|
|
18
|
+
if (backslash !== -1) {
|
|
19
|
+
const query = target.indexOf("?");
|
|
20
|
+
if (query === -1 || backslash < query) {
|
|
21
|
+
throw new RequestError("Backslash in request path");
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
};
|
|
16
25
|
var toRequestError = (e) => {
|
|
17
26
|
if (e instanceof RequestError) {
|
|
18
27
|
return e;
|
|
@@ -173,6 +182,7 @@ var newRequest = (incoming, defaultHostname) => {
|
|
|
173
182
|
throw new RequestError("Absolute URL for :path is not allowed in HTTP/2");
|
|
174
183
|
}
|
|
175
184
|
try {
|
|
185
|
+
validateRequestTarget(incomingUrl);
|
|
176
186
|
const url2 = new URL(incomingUrl);
|
|
177
187
|
req[urlKey] = url2.href;
|
|
178
188
|
} catch (e) {
|
|
@@ -193,6 +203,7 @@ var newRequest = (incoming, defaultHostname) => {
|
|
|
193
203
|
} else {
|
|
194
204
|
scheme = incoming.socket && incoming.socket.encrypted ? "https" : "http";
|
|
195
205
|
}
|
|
206
|
+
validateRequestTarget(incomingUrl);
|
|
196
207
|
const url = new URL(`${scheme}://${host}${incomingUrl}`);
|
|
197
208
|
if (url.hostname.length !== host.length && url.hostname !== host.replace(/:\d+$/, "")) {
|
|
198
209
|
throw new RequestError("Invalid host header");
|
|
@@ -44,6 +44,15 @@ var RequestError = class extends Error {
|
|
|
44
44
|
this.name = "RequestError";
|
|
45
45
|
}
|
|
46
46
|
};
|
|
47
|
+
var validateRequestTarget = (target) => {
|
|
48
|
+
const backslash = target.indexOf("\\");
|
|
49
|
+
if (backslash !== -1) {
|
|
50
|
+
const query = target.indexOf("?");
|
|
51
|
+
if (query === -1 || backslash < query) {
|
|
52
|
+
throw new RequestError("Backslash in request path");
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
};
|
|
47
56
|
var toRequestError = (e) => {
|
|
48
57
|
if (e instanceof RequestError) {
|
|
49
58
|
return e;
|
|
@@ -204,6 +213,7 @@ var newRequest = (incoming, defaultHostname) => {
|
|
|
204
213
|
throw new RequestError("Absolute URL for :path is not allowed in HTTP/2");
|
|
205
214
|
}
|
|
206
215
|
try {
|
|
216
|
+
validateRequestTarget(incomingUrl);
|
|
207
217
|
const url2 = new URL(incomingUrl);
|
|
208
218
|
req[urlKey] = url2.href;
|
|
209
219
|
} catch (e) {
|
|
@@ -224,6 +234,7 @@ var newRequest = (incoming, defaultHostname) => {
|
|
|
224
234
|
} else {
|
|
225
235
|
scheme = incoming.socket && incoming.socket.encrypted ? "https" : "http";
|
|
226
236
|
}
|
|
237
|
+
validateRequestTarget(incomingUrl);
|
|
227
238
|
const url = new URL(`${scheme}://${host}${incomingUrl}`);
|
|
228
239
|
if (url.hostname.length !== host.length && url.hostname !== host.replace(/:\d+$/, "")) {
|
|
229
240
|
throw new RequestError("Invalid host header");
|
|
@@ -10,6 +10,15 @@ var RequestError = class extends Error {
|
|
|
10
10
|
this.name = "RequestError";
|
|
11
11
|
}
|
|
12
12
|
};
|
|
13
|
+
var validateRequestTarget = (target) => {
|
|
14
|
+
const backslash = target.indexOf("\\");
|
|
15
|
+
if (backslash !== -1) {
|
|
16
|
+
const query = target.indexOf("?");
|
|
17
|
+
if (query === -1 || backslash < query) {
|
|
18
|
+
throw new RequestError("Backslash in request path");
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
};
|
|
13
22
|
var toRequestError = (e) => {
|
|
14
23
|
if (e instanceof RequestError) {
|
|
15
24
|
return e;
|
|
@@ -170,6 +179,7 @@ var newRequest = (incoming, defaultHostname) => {
|
|
|
170
179
|
throw new RequestError("Absolute URL for :path is not allowed in HTTP/2");
|
|
171
180
|
}
|
|
172
181
|
try {
|
|
182
|
+
validateRequestTarget(incomingUrl);
|
|
173
183
|
const url2 = new URL(incomingUrl);
|
|
174
184
|
req[urlKey] = url2.href;
|
|
175
185
|
} catch (e) {
|
|
@@ -190,6 +200,7 @@ var newRequest = (incoming, defaultHostname) => {
|
|
|
190
200
|
} else {
|
|
191
201
|
scheme = incoming.socket && incoming.socket.encrypted ? "https" : "http";
|
|
192
202
|
}
|
|
203
|
+
validateRequestTarget(incomingUrl);
|
|
193
204
|
const url = new URL(`${scheme}://${host}${incomingUrl}`);
|
|
194
205
|
if (url.hostname.length !== host.length && url.hostname !== host.replace(/:\d+$/, "")) {
|
|
195
206
|
throw new RequestError("Invalid host header");
|
|
@@ -38,6 +38,15 @@ var RequestError = class extends Error {
|
|
|
38
38
|
this.name = "RequestError";
|
|
39
39
|
}
|
|
40
40
|
};
|
|
41
|
+
var validateRequestTarget = (target) => {
|
|
42
|
+
const backslash = target.indexOf("\\");
|
|
43
|
+
if (backslash !== -1) {
|
|
44
|
+
const query = target.indexOf("?");
|
|
45
|
+
if (query === -1 || backslash < query) {
|
|
46
|
+
throw new RequestError("Backslash in request path");
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
41
50
|
var toRequestError = (e) => {
|
|
42
51
|
if (e instanceof RequestError) {
|
|
43
52
|
return e;
|
|
@@ -198,6 +207,7 @@ var newRequest = (incoming, defaultHostname) => {
|
|
|
198
207
|
throw new RequestError("Absolute URL for :path is not allowed in HTTP/2");
|
|
199
208
|
}
|
|
200
209
|
try {
|
|
210
|
+
validateRequestTarget(incomingUrl);
|
|
201
211
|
const url2 = new URL(incomingUrl);
|
|
202
212
|
req[urlKey] = url2.href;
|
|
203
213
|
} catch (e) {
|
|
@@ -218,6 +228,7 @@ var newRequest = (incoming, defaultHostname) => {
|
|
|
218
228
|
} else {
|
|
219
229
|
scheme = incoming.socket && incoming.socket.encrypted ? "https" : "http";
|
|
220
230
|
}
|
|
231
|
+
validateRequestTarget(incomingUrl);
|
|
221
232
|
const url = new URL(`${scheme}://${host}${incomingUrl}`);
|
|
222
233
|
if (url.hostname.length !== host.length && url.hostname !== host.replace(/:\d+$/, "")) {
|
|
223
234
|
throw new RequestError("Invalid host header");
|
|
@@ -7,6 +7,15 @@ var RequestError = class extends Error {
|
|
|
7
7
|
this.name = "RequestError";
|
|
8
8
|
}
|
|
9
9
|
};
|
|
10
|
+
var validateRequestTarget = (target) => {
|
|
11
|
+
const backslash = target.indexOf("\\");
|
|
12
|
+
if (backslash !== -1) {
|
|
13
|
+
const query = target.indexOf("?");
|
|
14
|
+
if (query === -1 || backslash < query) {
|
|
15
|
+
throw new RequestError("Backslash in request path");
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
};
|
|
10
19
|
var toRequestError = (e) => {
|
|
11
20
|
if (e instanceof RequestError) {
|
|
12
21
|
return e;
|
|
@@ -167,6 +176,7 @@ var newRequest = (incoming, defaultHostname) => {
|
|
|
167
176
|
throw new RequestError("Absolute URL for :path is not allowed in HTTP/2");
|
|
168
177
|
}
|
|
169
178
|
try {
|
|
179
|
+
validateRequestTarget(incomingUrl);
|
|
170
180
|
const url2 = new URL(incomingUrl);
|
|
171
181
|
req[urlKey] = url2.href;
|
|
172
182
|
} catch (e) {
|
|
@@ -187,6 +197,7 @@ var newRequest = (incoming, defaultHostname) => {
|
|
|
187
197
|
} else {
|
|
188
198
|
scheme = incoming.socket && incoming.socket.encrypted ? "https" : "http";
|
|
189
199
|
}
|
|
200
|
+
validateRequestTarget(incomingUrl);
|
|
190
201
|
const url = new URL(`${scheme}://${host}${incomingUrl}`);
|
|
191
202
|
if (url.hostname.length !== host.length && url.hostname !== host.replace(/:\d+$/, "")) {
|
|
192
203
|
throw new RequestError("Invalid host header");
|
|
@@ -48,6 +48,15 @@ var RequestError = class extends Error {
|
|
|
48
48
|
this.name = "RequestError";
|
|
49
49
|
}
|
|
50
50
|
};
|
|
51
|
+
var validateRequestTarget = (target) => {
|
|
52
|
+
const backslash = target.indexOf("\\");
|
|
53
|
+
if (backslash !== -1) {
|
|
54
|
+
const query = target.indexOf("?");
|
|
55
|
+
if (query === -1 || backslash < query) {
|
|
56
|
+
throw new RequestError("Backslash in request path");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
51
60
|
var toRequestError = (e) => {
|
|
52
61
|
if (e instanceof RequestError) {
|
|
53
62
|
return e;
|
|
@@ -208,6 +217,7 @@ var newRequest = (incoming, defaultHostname) => {
|
|
|
208
217
|
throw new RequestError("Absolute URL for :path is not allowed in HTTP/2");
|
|
209
218
|
}
|
|
210
219
|
try {
|
|
220
|
+
validateRequestTarget(incomingUrl);
|
|
211
221
|
const url2 = new URL(incomingUrl);
|
|
212
222
|
req[urlKey] = url2.href;
|
|
213
223
|
} catch (e) {
|
|
@@ -228,6 +238,7 @@ var newRequest = (incoming, defaultHostname) => {
|
|
|
228
238
|
} else {
|
|
229
239
|
scheme = incoming.socket && incoming.socket.encrypted ? "https" : "http";
|
|
230
240
|
}
|
|
241
|
+
validateRequestTarget(incomingUrl);
|
|
231
242
|
const url = new URL(`${scheme}://${host}${incomingUrl}`);
|
|
232
243
|
if (url.hostname.length !== host.length && url.hostname !== host.replace(/:\d+$/, "")) {
|
|
233
244
|
throw new RequestError("Invalid host header");
|
|
@@ -13,6 +13,15 @@ var RequestError = class extends Error {
|
|
|
13
13
|
this.name = "RequestError";
|
|
14
14
|
}
|
|
15
15
|
};
|
|
16
|
+
var validateRequestTarget = (target) => {
|
|
17
|
+
const backslash = target.indexOf("\\");
|
|
18
|
+
if (backslash !== -1) {
|
|
19
|
+
const query = target.indexOf("?");
|
|
20
|
+
if (query === -1 || backslash < query) {
|
|
21
|
+
throw new RequestError("Backslash in request path");
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
};
|
|
16
25
|
var toRequestError = (e) => {
|
|
17
26
|
if (e instanceof RequestError) {
|
|
18
27
|
return e;
|
|
@@ -173,6 +182,7 @@ var newRequest = (incoming, defaultHostname) => {
|
|
|
173
182
|
throw new RequestError("Absolute URL for :path is not allowed in HTTP/2");
|
|
174
183
|
}
|
|
175
184
|
try {
|
|
185
|
+
validateRequestTarget(incomingUrl);
|
|
176
186
|
const url2 = new URL(incomingUrl);
|
|
177
187
|
req[urlKey] = url2.href;
|
|
178
188
|
} catch (e) {
|
|
@@ -193,6 +203,7 @@ var newRequest = (incoming, defaultHostname) => {
|
|
|
193
203
|
} else {
|
|
194
204
|
scheme = incoming.socket && incoming.socket.encrypted ? "https" : "http";
|
|
195
205
|
}
|
|
206
|
+
validateRequestTarget(incomingUrl);
|
|
196
207
|
const url = new URL(`${scheme}://${host}${incomingUrl}`);
|
|
197
208
|
if (url.hostname.length !== host.length && url.hostname !== host.replace(/:\d+$/, "")) {
|
|
198
209
|
throw new RequestError("Invalid host header");
|
|
@@ -46,6 +46,15 @@ var RequestError = class extends Error {
|
|
|
46
46
|
this.name = "RequestError";
|
|
47
47
|
}
|
|
48
48
|
};
|
|
49
|
+
var validateRequestTarget = (target) => {
|
|
50
|
+
const backslash = target.indexOf("\\");
|
|
51
|
+
if (backslash !== -1) {
|
|
52
|
+
const query = target.indexOf("?");
|
|
53
|
+
if (query === -1 || backslash < query) {
|
|
54
|
+
throw new RequestError("Backslash in request path");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
};
|
|
49
58
|
var toRequestError = (e) => {
|
|
50
59
|
if (e instanceof RequestError) {
|
|
51
60
|
return e;
|
|
@@ -206,6 +215,7 @@ var newRequest = (incoming, defaultHostname) => {
|
|
|
206
215
|
throw new RequestError("Absolute URL for :path is not allowed in HTTP/2");
|
|
207
216
|
}
|
|
208
217
|
try {
|
|
218
|
+
validateRequestTarget(incomingUrl);
|
|
209
219
|
const url2 = new URL(incomingUrl);
|
|
210
220
|
req[urlKey] = url2.href;
|
|
211
221
|
} catch (e) {
|
|
@@ -226,6 +236,7 @@ var newRequest = (incoming, defaultHostname) => {
|
|
|
226
236
|
} else {
|
|
227
237
|
scheme = incoming.socket && incoming.socket.encrypted ? "https" : "http";
|
|
228
238
|
}
|
|
239
|
+
validateRequestTarget(incomingUrl);
|
|
229
240
|
const url = new URL(`${scheme}://${host}${incomingUrl}`);
|
|
230
241
|
if (url.hostname.length !== host.length && url.hostname !== host.replace(/:\d+$/, "")) {
|
|
231
242
|
throw new RequestError("Invalid host header");
|
|
@@ -10,6 +10,15 @@ var RequestError = class extends Error {
|
|
|
10
10
|
this.name = "RequestError";
|
|
11
11
|
}
|
|
12
12
|
};
|
|
13
|
+
var validateRequestTarget = (target) => {
|
|
14
|
+
const backslash = target.indexOf("\\");
|
|
15
|
+
if (backslash !== -1) {
|
|
16
|
+
const query = target.indexOf("?");
|
|
17
|
+
if (query === -1 || backslash < query) {
|
|
18
|
+
throw new RequestError("Backslash in request path");
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
};
|
|
13
22
|
var toRequestError = (e) => {
|
|
14
23
|
if (e instanceof RequestError) {
|
|
15
24
|
return e;
|
|
@@ -170,6 +179,7 @@ var newRequest = (incoming, defaultHostname) => {
|
|
|
170
179
|
throw new RequestError("Absolute URL for :path is not allowed in HTTP/2");
|
|
171
180
|
}
|
|
172
181
|
try {
|
|
182
|
+
validateRequestTarget(incomingUrl);
|
|
173
183
|
const url2 = new URL(incomingUrl);
|
|
174
184
|
req[urlKey] = url2.href;
|
|
175
185
|
} catch (e) {
|
|
@@ -190,6 +200,7 @@ var newRequest = (incoming, defaultHostname) => {
|
|
|
190
200
|
} else {
|
|
191
201
|
scheme = incoming.socket && incoming.socket.encrypted ? "https" : "http";
|
|
192
202
|
}
|
|
203
|
+
validateRequestTarget(incomingUrl);
|
|
193
204
|
const url = new URL(`${scheme}://${host}${incomingUrl}`);
|
|
194
205
|
if (url.hostname.length !== host.length && url.hostname !== host.replace(/:\d+$/, "")) {
|
|
195
206
|
throw new RequestError("Invalid host header");
|
|
@@ -343,7 +343,9 @@ results. Results survive MCP restart. One call to the service may take
|
|
|
343
343
|
`RHO_TRANSPORT_MS` (30 s), plus the `waitMs` it asks the service to wait (at most ten
|
|
344
344
|
minutes, as for a Notebook cell); the call uses `node:http`, whose wait for a reply has
|
|
345
345
|
no limit of its own. A transport overrun keeps the outcome uncertain and directs
|
|
346
|
-
observation of the original identity, never replacement work.
|
|
346
|
+
observation of the original identity, never replacement work. A refused connection is the one
|
|
347
|
+
transport failure whose outcome is known: nothing reached the service, so the call
|
|
348
|
+
returns `unavailable` naming the address and saying nothing ran (`delivered: false`).
|
|
347
349
|
|
|
348
350
|
The pure `./layered-contract` entry defines the four tool inputs and Agent note
|
|
349
351
|
props. `./layered` provides the bounded complete-object cache and caller checks;
|
|
@@ -388,3 +390,42 @@ fetched; an explicit response excludes replies already present in that context.
|
|
|
388
390
|
ACP retains Codex's native MCP spelling while identifying its operation, and names
|
|
389
391
|
an otherwise unnamed permission from the original tool update. These observations
|
|
390
392
|
do not expand native permission or grant access to arbitrary session history.
|
|
393
|
+
|
|
394
|
+
## External conversations
|
|
395
|
+
|
|
396
|
+
Agent 0.4.29 exports `dsh-session` for a Harness adapter to retain a read-only
|
|
397
|
+
observation of a native version-4 session. `observeDshSession(projectRoot, header,
|
|
398
|
+
events)` requires the real native working folder to equal the selected project.
|
|
399
|
+
It preserves session, turn and tool-call identities. Repeated cuts replace the
|
|
400
|
+
same record; a changed prefix or backwards cursor is refused. It never creates
|
|
401
|
+
an Agent, executes a tool, changes a grant or resumes a runtime.
|
|
402
|
+
|
|
403
|
+
`external-session` supplies bounded `listExternalSessions` and
|
|
404
|
+
`readExternalSession` reads. The operation contracts are `agent.sessions.list`
|
|
405
|
+
and `agent.sessions.read`; an embedding application supplies authorization.
|
|
406
|
+
`ui-external-session` renders prompts, visible replies, operations and results,
|
|
407
|
+
with input explicitly retained by the native client. Reasoning, injected context
|
|
408
|
+
and system messages are excluded; credential-shaped strings are redacted.
|
|
409
|
+
This is an observation of the client's reports, not an independent execution proof.
|
|
410
|
+
|
|
411
|
+
A successful resource-registration reply carries its exact resource identity and
|
|
412
|
+
version. The application must verify that tuple against its own project resources.
|
|
413
|
+
A native deliverable without a receipt is only a reported path, with no historical
|
|
414
|
+
bytes asserted. Tool return, native exit code and turn completion remain separate.
|
|
415
|
+
An unfinished turn with no observation for 10 seconds has an unknown outcome.
|
|
416
|
+
|
|
417
|
+
`externalSessionLimits` names the fixed local observation budgets: 64 sessions
|
|
418
|
+
per project, 200 retained turns per session, 256 tools per turn, 32,768 source
|
|
419
|
+
events / 32 MiB per cut, 32,768 characters per prompt/reply and earlier-reply
|
|
420
|
+
collection, 8,192 characters per tool field, and 4 MiB per stored record.
|
|
421
|
+
Text/tools/earlier turns beyond their budgets are omitted with counts. An
|
|
422
|
+
oversized source, conflicting cut, invalid record or busy writer retains the
|
|
423
|
+
previous record and reports failure. Storage is `.rho/agent/external`, contained
|
|
424
|
+
in the project without symlink directories; atomic replacement leaves readers a
|
|
425
|
+
complete record. A positively dead PID lock can be reclaimed; an unknown/live
|
|
426
|
+
lock cannot. Reads create no directories or runtime state. These limits protect
|
|
427
|
+
local storage and observation work; they do not limit the native Agent.
|
|
428
|
+
|
|
429
|
+
`test/external-session.test.mjs` checks identity, prefix conflicts, omissions,
|
|
430
|
+
missing outcomes, redaction, project containment, retained failure and pagination.
|
|
431
|
+
The Rho application records its separate real Harness/browser acceptance.
|
|
@@ -195,10 +195,21 @@ export class AcpConversation {
|
|
|
195
195
|
let model;
|
|
196
196
|
this.observationOrigin = this.sessionId ? 'history' : 'initial';
|
|
197
197
|
if (this.sessionId) {
|
|
198
|
-
|
|
198
|
+
const caps = initialized.agentCapabilities;
|
|
199
|
+
if (caps?.loadSession) {
|
|
200
|
+
const loaded = await connection.agent.request(acp.methods.agent.session.load, { ...params, sessionId: this.sessionId });
|
|
201
|
+
this.settings = loaded.configOptions ?? [];
|
|
202
|
+
}
|
|
203
|
+
else if (caps?.sessionCapabilities?.resume) {
|
|
204
|
+
// ACP session/resume (e.g. dsh): restore the persisted session on a fresh process. It composes
|
|
205
|
+
// the request's MCP connections and validates only the workspace, so the non-standard
|
|
206
|
+
// systemPrompt is omitted; history is restored from the Agent's own persistence, not replayed.
|
|
207
|
+
const resumed = await connection.agent.request(acp.methods.agent.session.resume, { cwd: o.cwd, mcpServers: o.mcpServers, sessionId: this.sessionId });
|
|
208
|
+
this.settings = resumed?.configOptions ?? [];
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
199
211
|
throw new Error('This Agent cannot load the original session. Nothing was resent.');
|
|
200
|
-
|
|
201
|
-
this.settings = loaded.configOptions ?? [];
|
|
212
|
+
}
|
|
202
213
|
}
|
|
203
214
|
else {
|
|
204
215
|
const created = await connection.agent.request(acp.methods.agent.session.new, params);
|
|
@@ -10,5 +10,6 @@ export interface CaptureOptions {
|
|
|
10
10
|
maxEventBytes?: number;
|
|
11
11
|
}
|
|
12
12
|
export declare function canonicalActivity(value: any): string;
|
|
13
|
+
export declare function activityPayload(value: any, options: CaptureOptions): any;
|
|
13
14
|
export declare function normalizeClaude(records: any[], sessionId: string, project: string, options?: CaptureOptions): ObservedEvent[];
|
|
14
15
|
export declare function normalizeDsh(records: any[], options?: CaptureOptions): ObservedEvent[];
|
|
@@ -9,7 +9,7 @@ export function canonicalActivity(value) {
|
|
|
9
9
|
const sensitive = /(?:^|[_-])(?:authorization|cookie|password|passwd|secret|api[_-]?key|access[_-]?token|auth[_-]?token|token)$/i;
|
|
10
10
|
const hidden = /^(?:thinking|reasoning|reasoning_content|signature)$/i;
|
|
11
11
|
const txt = (v) => typeof v === 'string' ? v : Array.isArray(v) ? v.filter(b => b?.type === 'text').map(b => b.text).join('\n') : '';
|
|
12
|
-
function
|
|
12
|
+
export function activityPayload(value, options) {
|
|
13
13
|
let redacted = false, truncated = false;
|
|
14
14
|
const cap = options.maxFieldBytes ?? 8192;
|
|
15
15
|
const identities = new Set(['native_id', 'call_id', 'root_call_id', 'job_id', 'parent_message_id']);
|
|
@@ -76,7 +76,7 @@ function inputSummary(input) {
|
|
|
76
76
|
function chained(items, options) {
|
|
77
77
|
let prefix = hash(activityNormalizer);
|
|
78
78
|
return items.map(({ kind, data }, sequence) => {
|
|
79
|
-
const safe =
|
|
79
|
+
const safe = activityPayload(data, options);
|
|
80
80
|
prefix = hash(prefix + canonicalActivity({ kind, data: safe }));
|
|
81
81
|
return { sequence, kind, data: { ...safe, prefix_sha256: prefix } };
|
|
82
82
|
});
|
|
@@ -123,5 +123,6 @@ export declare function visibleError(error: unknown, env?: NodeJS.ProcessEnv): s
|
|
|
123
123
|
export { ClaudeAcpConversation, type ClaudeAcpOptions } from './claude-acp.js';
|
|
124
124
|
export { writeClaudePlugin, claudePluginLimits, type ClaudePlugin, type ClaudePluginSkill, type ClaudePluginHook } from './claude-plugin.js';
|
|
125
125
|
export { CodexAcpConversation, type CodexAcpOptions } from './codex-acp.js';
|
|
126
|
+
export { GenericAcpConversation, type GenericAcpOptions } from './generic-acp.js';
|
|
126
127
|
export { ClaudeCliConversation } from './claude-cli.js';
|
|
127
128
|
export type { SessionObservation, SessionTool, ConfigOption, ConfigValue } from './session-observation.js';
|
|
@@ -25,4 +25,5 @@ export function visibleError(error, env = process.env) {
|
|
|
25
25
|
export { ClaudeAcpConversation } from './claude-acp.js';
|
|
26
26
|
export { writeClaudePlugin, claudePluginLimits } from './claude-plugin.js';
|
|
27
27
|
export { CodexAcpConversation } from './codex-acp.js';
|
|
28
|
+
export { GenericAcpConversation } from './generic-acp.js';
|
|
28
29
|
export { ClaudeCliConversation } from './claude-cli.js';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type ExternalSessionRecord } from './external-session.js';
|
|
2
|
+
export declare function externalProjectPath(root: string, input: unknown): string | null;
|
|
3
|
+
/** Project the native committed log. System prompts, injected context and reasoning
|
|
4
|
+
* are excluded. Original session, turn and call identities survive every backfill. */
|
|
5
|
+
export declare function projectDshSession(root: string, header: any, events: readonly any[], observedAt?: string): ExternalSessionRecord;
|
|
6
|
+
export declare function observeDshSession(root: string, header: any, events: readonly any[]): import("./external-session.js").ExternalSessionSummary;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
import { activityPayload, canonicalActivity } from './activity-normalize.js';
|
|
5
|
+
import { externalSessionId, retainedExternalSession, saveExternalSession } from './external-session.js';
|
|
6
|
+
import { externalSessionLimits as limits } from './external-session-contract.js';
|
|
7
|
+
const text = (v) => typeof v === 'string' ? v : Array.isArray(v) ? v.filter(b => b?.type === 'text' && typeof b.text === 'string').map(b => b.text).join('\n') : '';
|
|
8
|
+
const time = (v) => { const n = typeof v === 'number' ? v : Date.parse(v); if (!Number.isFinite(n))
|
|
9
|
+
throw new Error('Native event has no valid timestamp.'); return new Date(n).toISOString(); };
|
|
10
|
+
const hash = (v) => createHash('sha256').update(canonicalActivity(v)).digest('hex');
|
|
11
|
+
const safe = (value, cap = limits.maxTextChars) => (activityPayload({ text: value.replace(/\b([\w-]*(?:token|cookie|authorization)["']?\s*[=:]\s*)(?:"[^"]*"|'[^']*'|[^\s'";]+)/gi, '$1[redacted]') }, { content: true, maxFieldBytes: cap * 4, maxEventBytes: cap * 8 }).text ?? '').slice(0, cap).replace(/[\uD800-\uDBFF]$/, '');
|
|
12
|
+
export function externalProjectPath(root, input) {
|
|
13
|
+
if (typeof input !== 'string' || input.length > 1024 || input.includes('\0'))
|
|
14
|
+
return null;
|
|
15
|
+
const relative = path.relative(root, path.resolve(root, input)).split(path.sep).join('/');
|
|
16
|
+
return relative && !relative.startsWith('../') && !path.isAbsolute(relative) && !relative.split('/').some(p => ['..', '.rho', '.git'].includes(p)) ? relative : null;
|
|
17
|
+
}
|
|
18
|
+
function paths(root, args, content) {
|
|
19
|
+
const candidates = [args?.path, args?.file_path, args?.filename, args?.declaration?.path];
|
|
20
|
+
// These are explicitly labelled mentions, never inferred file writes or historical snapshots.
|
|
21
|
+
for (const m of content.matchAll(/(?<![\w/.:\-])\/?(?:[\w.\-\u0080-\uFFFF]+\/)*[\w.\-\u0080-\uFFFF]+\.(?:R|r|py|md|csv|tsv|png|svg|pdf|json|txt|qmd|Rmd)\b/g))
|
|
22
|
+
candidates.push(m[0]);
|
|
23
|
+
return [...new Set(candidates.map(p => externalProjectPath(root, p)).filter((p) => !!p))].slice(0, 32);
|
|
24
|
+
}
|
|
25
|
+
/** Project the native committed log. System prompts, injected context and reasoning
|
|
26
|
+
* are excluded. Original session, turn and call identities survive every backfill. */
|
|
27
|
+
export function projectDshSession(root, header, events, observedAt = new Date().toISOString()) {
|
|
28
|
+
root = fs.realpathSync(root);
|
|
29
|
+
if (!header || header.version !== 4 || typeof header.id !== 'string' || !/^[A-Za-z0-9._:@-]{1,128}$/.test(header.id) || typeof header.cwd !== 'string' || fs.realpathSync(header.cwd) !== root)
|
|
30
|
+
throw new Error('Harness session does not match the selected project.');
|
|
31
|
+
if (events.length > limits.maxEvents || Buffer.byteLength(JSON.stringify(events)) > limits.maxSourceBytes)
|
|
32
|
+
throw new Error('Harness observation exceeds source limits; previous observation retained.');
|
|
33
|
+
const turns = [], byTurn = new Map(), tools = new Map();
|
|
34
|
+
let current, title = '', lastAt = time(header.createdAt), omittedTurns = 0;
|
|
35
|
+
const makeTurn = (id, at) => {
|
|
36
|
+
let t = byTurn.get(id);
|
|
37
|
+
if (t)
|
|
38
|
+
return t;
|
|
39
|
+
t = { id: 'turn:' + id, at, state: 'unknown', prompt: '', reply: '', messages: [], tools: [], results: [], omittedTools: 0, omittedChars: 0 };
|
|
40
|
+
byTurn.set(id, t);
|
|
41
|
+
turns.push(t);
|
|
42
|
+
if (turns.length > limits.maxTurns) {
|
|
43
|
+
const gone = turns.shift();
|
|
44
|
+
omittedTurns++;
|
|
45
|
+
for (const [k, v] of tools)
|
|
46
|
+
if (v.turn === gone)
|
|
47
|
+
tools.delete(k);
|
|
48
|
+
for (const [k, v] of byTurn)
|
|
49
|
+
if (v === gone)
|
|
50
|
+
byTurn.delete(k);
|
|
51
|
+
}
|
|
52
|
+
return t;
|
|
53
|
+
};
|
|
54
|
+
const appendText = (t, field, value, replace = false) => {
|
|
55
|
+
const full = replace ? value : t[field] + (t[field] ? '\n\n' : '') + value;
|
|
56
|
+
t[field] = safe(full);
|
|
57
|
+
t.omittedChars += Math.max(0, full.length - t[field].length);
|
|
58
|
+
};
|
|
59
|
+
for (let i = 0; i < events.length; i++) {
|
|
60
|
+
const e = events[i];
|
|
61
|
+
if (e.seq !== i)
|
|
62
|
+
throw new Error('Harness source has a missing or reordered native event.');
|
|
63
|
+
const d = e.data ?? {}, at = time(e.time);
|
|
64
|
+
lastAt = at;
|
|
65
|
+
if (e.type === 'session/title' && typeof d.title === 'string')
|
|
66
|
+
title = safe(d.title, 512);
|
|
67
|
+
if (e.type === 'turn/start') {
|
|
68
|
+
current = makeTurn(d.turn, at);
|
|
69
|
+
current.state = 'running';
|
|
70
|
+
}
|
|
71
|
+
if (e.type === 'user/message' && d.source?.kind === 'user') {
|
|
72
|
+
if (!current)
|
|
73
|
+
current = makeTurn(e.seq, at);
|
|
74
|
+
appendText(current, 'prompt', text(d.content));
|
|
75
|
+
}
|
|
76
|
+
if (e.type === 'assistant/message') {
|
|
77
|
+
const t = byTurn.get(d.turn) ?? current;
|
|
78
|
+
if (!t)
|
|
79
|
+
continue;
|
|
80
|
+
const message = text(d.message?.content);
|
|
81
|
+
if (message) {
|
|
82
|
+
if (t.reply)
|
|
83
|
+
t.messages.push({ at, text: t.reply });
|
|
84
|
+
while (t.messages.length > 200 || t.messages.reduce((n, m) => n + m.text.length, 0) > limits.maxTextChars)
|
|
85
|
+
t.omittedChars += t.messages.shift().text.length;
|
|
86
|
+
appendText(t, 'reply', message, true);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (e.type === 'tool/call') {
|
|
90
|
+
const t = byTurn.get(d.turn) ?? current;
|
|
91
|
+
if (!t || typeof d.callId !== 'string' || typeof d.name !== 'string')
|
|
92
|
+
continue;
|
|
93
|
+
if (tools.has(d.callId))
|
|
94
|
+
throw new Error('Harness call identity was reused.');
|
|
95
|
+
if (t.tools.length >= limits.maxTools) {
|
|
96
|
+
t.omittedTools++;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
let args = d.arguments;
|
|
100
|
+
if (typeof args === 'string') {
|
|
101
|
+
try {
|
|
102
|
+
args = JSON.parse(args);
|
|
103
|
+
}
|
|
104
|
+
catch { }
|
|
105
|
+
}
|
|
106
|
+
const name = d.name === 'mcp__rho__rho_call' && typeof args?.operation === 'string' ? args.operation : d.name;
|
|
107
|
+
const input = args?.input ?? args;
|
|
108
|
+
const detail = safe(typeof input?.command === 'string' ? input.command : JSON.stringify(input) ?? '', limits.maxDetailChars);
|
|
109
|
+
const tool = { id: d.callId, at, name, detail, output: '', outcome: 'requested', paths: paths(root, input, detail) };
|
|
110
|
+
t.tools.push(tool);
|
|
111
|
+
tools.set(d.callId, { turn: t, tool });
|
|
112
|
+
}
|
|
113
|
+
if (e.type === 'tool/result') {
|
|
114
|
+
const message = d.message, item = tools.get(message?.toolCallId ?? message?.source?.callId);
|
|
115
|
+
if (!item)
|
|
116
|
+
continue;
|
|
117
|
+
const output = text(message?.content);
|
|
118
|
+
item.tool.output = safe(output, limits.maxDetailChars);
|
|
119
|
+
item.tool.outcome = message?.isError ? 'failed' : 'returned';
|
|
120
|
+
item.turn.omittedChars += Math.max(0, output.length - item.tool.output.length);
|
|
121
|
+
// Only a successful Rho registration/capture reply asserts an exact resource version.
|
|
122
|
+
if (['resource.register', 'resource.capture', 'agent.activity.capture'].includes(item.tool.name) && !message?.isError) {
|
|
123
|
+
try {
|
|
124
|
+
const response = JSON.parse(output), r = response.value?.resource ?? response.value;
|
|
125
|
+
const relative = externalProjectPath(root, r?.path);
|
|
126
|
+
if (response.ok === true && relative && /^[a-f0-9]{64}$/.test(r.id) && /^[a-f0-9]{64}$/.test(r.version) && typeof r.projectId === 'string' && item.turn.results.length < 64)
|
|
127
|
+
item.turn.results.push({ path: relative, id: r.id, version: r.version, projectId: r.projectId, toolId: item.tool.id });
|
|
128
|
+
}
|
|
129
|
+
catch { /* The bounded receipt can be incomplete; never fabricate a resource. */ }
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (e.type === 'job/settled') {
|
|
133
|
+
const item = tools.get(d.toolCall?.rootCallId ?? d.toolCall?.callId), exit = typeof d.detail === 'string' ? /^exit code: (-?\d+)$/.exec(d.detail) : null;
|
|
134
|
+
if (item && exit)
|
|
135
|
+
item.tool.exitCode = Number(exit[1]);
|
|
136
|
+
}
|
|
137
|
+
if (e.type === 'deliverables/presented') {
|
|
138
|
+
const t = byTurn.get(d.turn) ?? current;
|
|
139
|
+
if (!t)
|
|
140
|
+
continue;
|
|
141
|
+
for (const file of d.files ?? []) {
|
|
142
|
+
const relative = externalProjectPath(root, file.path);
|
|
143
|
+
if (relative && !t.results.some(r => r.path === relative) && t.results.length < 64)
|
|
144
|
+
t.results.push({ path: relative, toolId: d.callId ?? 'event:' + e.seq });
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (e.type === 'turn/end') {
|
|
148
|
+
const t = byTurn.get(d.turn);
|
|
149
|
+
if (!t)
|
|
150
|
+
continue;
|
|
151
|
+
t.state = d.reason?.kind === 'completed' ? 'completed' : d.reason?.kind === 'cancelled' ? 'stopped' : d.reason?.kind === 'error' ? 'failed' : 'unknown';
|
|
152
|
+
t.endedAt = at;
|
|
153
|
+
for (const tool of t.tools)
|
|
154
|
+
if (tool.outcome === 'requested')
|
|
155
|
+
tool.outcome = 'unknown';
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const summary = { id: externalSessionId('dsh', header.id), producer: 'dsh', sessionId: header.id, title: title || turns.find(t => t.prompt)?.prompt.slice(0, 160) || 'Harness conversation', lastAt, observedAt,
|
|
159
|
+
state: turns.at(-1)?.state ?? 'unknown', turns: turns.length + omittedTurns, sourceCursor: events.length, omittedTurns, inputOwner: 'external' };
|
|
160
|
+
const result = { format: 1, projectRoot: root, summary, turns, sourceDigest: hash(events) };
|
|
161
|
+
// A suffix is retained at the record budget; gaps are explicit, earlier files stay untouched.
|
|
162
|
+
while (Buffer.byteLength(JSON.stringify(result)) > limits.maxRecordBytes && result.turns.length > 1) {
|
|
163
|
+
result.turns.shift();
|
|
164
|
+
result.summary.omittedTurns++;
|
|
165
|
+
}
|
|
166
|
+
if (Buffer.byteLength(JSON.stringify(result)) > limits.maxRecordBytes)
|
|
167
|
+
throw new Error('Harness turn exceeds maxRecordBytes; previous observation retained.');
|
|
168
|
+
return result;
|
|
169
|
+
}
|
|
170
|
+
export function observeDshSession(root, header, events) {
|
|
171
|
+
const old = retainedExternalSession(root, 'dsh', header.id);
|
|
172
|
+
const value = projectDshSession(root, header, events);
|
|
173
|
+
return saveExternalSession(root, value, old ? hash(events.slice(0, old.summary.sourceCursor)) : undefined);
|
|
174
|
+
}
|