@rho-workspace/rho 0.5.5 → 0.5.7
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/dist/agent-connect.d.ts +42 -0
- package/dist/agent-connect.js +68 -0
- package/dist/answer-board.js +3 -3
- package/dist/bridge.js +6 -1
- package/dist/browser-access.d.ts +1 -0
- package/dist/browser-access.js +5 -3
- package/dist/capabilities.d.ts +21 -5
- package/dist/capabilities.js +6 -4
- package/dist/cli.js +113 -18
- package/dist/composition.d.ts +1 -1
- package/dist/composition.js +13 -3
- package/dist/connect-cli.d.ts +50 -0
- package/dist/connect-cli.js +128 -0
- package/dist/contract.generated.d.ts +1437 -381
- package/dist/contract.generated.js +1 -1
- package/dist/contracts/owners.generated.js +1160 -267
- package/dist/conversation-presentation.d.ts +9 -2
- package/dist/conversation-presentation.js +3 -1
- package/dist/conversations.d.ts +306 -7
- package/dist/conversations.js +208 -22
- package/dist/discovery.js +3 -3
- package/dist/extension-contract.d.ts +19 -0
- package/dist/extension-contract.js +15 -6
- package/dist/extension-markets.d.ts +74 -0
- package/dist/extension-markets.js +166 -0
- package/dist/extensions.d.ts +29 -1
- package/dist/extensions.js +13 -4
- package/dist/external-work.d.ts +22 -0
- package/dist/external-work.js +91 -0
- package/dist/plugin-cli.js +75 -3
- package/dist/plugin-inventory.d.ts +56 -0
- package/dist/plugin-inventory.js +41 -0
- package/dist/plugin-inventory.json +1581 -102
- package/dist/plugin-presentation.d.ts +105 -2
- package/dist/plugin-presentation.js +59 -5
- package/dist/plugins.d.ts +14 -3
- package/dist/plugins.js +12 -6
- package/dist/project-notes.js +1 -1
- package/dist/protocol.d.ts +7 -0
- package/dist/protocol.js +40 -15
- package/dist/replay.js +4 -0
- package/dist/response-contracts.js +26 -11
- package/dist/running.d.ts +10 -0
- package/dist/running.js +45 -0
- package/dist/server.d.ts +4 -10
- package/dist/server.js +140 -14
- package/dist/state.d.ts +8 -0
- package/dist/state.js +8 -0
- package/dist/studio-layout.js +3 -3
- package/dist/task-frame.d.ts +1 -1
- package/dist/timeline-events.d.ts +1 -0
- package/dist/timeline-events.js +8 -1
- package/dist/ui-context.d.ts +73 -0
- package/dist/ui-context.js +98 -0
- package/dist/ui-contract.d.ts +23 -0
- package/dist/web/.vite/manifest.json +135 -76
- package/dist/web/assets/answer-board-CeYS8k8y.js +1 -0
- package/dist/web/assets/choices-D8vGwsl2.js +1 -0
- package/dist/web/assets/{console-BSjLGe0T.js → console-Dyuo1q4N.js} +4 -4
- package/dist/web/assets/{dist-Tzoe_zm-.js → dist-CSiCyBml.js} +1 -1
- package/dist/web/assets/{dist-Cw8hRIlT.js → dist-D9FLb9Hm.js} +1 -1
- package/dist/web/assets/{dist-foH9IKc6.js → dist-FXbTBwML.js} +1 -1
- package/dist/web/assets/{dist-Buk0lwzI.js → dist-l3DUhbw5.js} +1 -1
- package/dist/web/assets/{explore-in-BVUy3ZW_.js → explore-in-BfSFGRh2.js} +1 -1
- package/dist/web/assets/graph-BU2_o5uA.js +2 -0
- package/dist/web/assets/index-C16mXVvk.css +1 -0
- package/dist/web/assets/index-CWg9XYXx.js +952 -0
- package/dist/web/assets/markdown.reader-Ct2-e2Zt.png +0 -0
- package/dist/web/assets/{notes-meaning-pVbbQUGP.js → notes-meaning-T2m5zQfv.js} +1 -1
- package/dist/web/assets/overview-PzfLJCo-.js +6 -0
- package/dist/web/assets/preview-BguEdsza.js +1 -0
- package/dist/web/assets/previews-pWNik-2g.js +3 -0
- package/dist/web/assets/{review-CMxQlQ-C.js → review-Dy_85lcH.js} +1 -1
- package/dist/web/assets/rho.editor-CCJeyHma.png +0 -0
- package/dist/web/assets/rho.flow-WkMj6fRS.png +0 -0
- package/dist/web/assets/rho.media-Cc6amodO.png +0 -0
- package/dist/web/assets/rho.notebook-6vsZKGDq.png +0 -0
- package/dist/web/assets/rho.object-BGoeXjte.png +0 -0
- package/dist/web/assets/rho.plots-yIiISCkb.png +0 -0
- package/dist/web/assets/rho.scatter-DwHxesCs.png +0 -0
- package/dist/web/assets/rho.servers-DKf_mnNq.png +0 -0
- package/dist/web/assets/rho.table-DWOGaWDa.png +0 -0
- package/dist/web/assets/rho.variables-CKcWczhQ.png +0 -0
- package/dist/web/assets/{skills-BFNSyrnD.js → skills-Dyz7mBlc.js} +1 -1
- package/dist/web/assets/{skills-work-BGPSnF_I.js → skills-work-BEGqlCkx.js} +1 -1
- package/dist/web/assets/studio-process-D-ykbC4D.js +2 -0
- package/dist/web/assets/timeline-events-DKTho9fU.js +1 -0
- package/dist/web/assets/work-turns-DSabCogR.js +1 -0
- package/dist/web/assets/{workspace-CWlkjLNx.js → workspace-DfjBL--r.js} +2 -2
- package/dist/web/composition.json +135 -80
- package/dist/web/index.html +3 -3
- package/dist/web/renderer.json +1 -1
- package/dist/web-assets.js +2 -2
- package/dist/work-graph.d.ts +11 -1
- package/dist/work-graph.js +16 -5
- package/dist/workspace.d.ts +2 -2
- package/dist/workspace.js +8 -7
- package/docs/FRONTEND.md +71 -21
- package/docs/HEADLESS.md +127 -24
- package/docs/THIRD-PARTY-PLUGINS.md +75 -3
- package/node_modules/@rho-workspace/plugin-agent/README.md +34 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/acp-command.d.ts +32 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/acp-command.js +54 -11
- package/node_modules/@rho-workspace/plugin-agent/dist/acp-conversation.d.ts +1 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/acp-conversation.js +29 -3
- package/node_modules/@rho-workspace/plugin-agent/dist/external-session-contract.js +11 -5
- package/node_modules/@rho-workspace/plugin-agent/dist/external-session.d.ts +12 -1
- package/node_modules/@rho-workspace/plugin-agent/dist/index.d.ts +8 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/index.js +3 -2
- package/node_modules/@rho-workspace/plugin-agent/dist/mcp-entry.d.ts +13 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/mcp-entry.js +13 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/mcp.js +16 -8
- package/node_modules/@rho-workspace/plugin-agent/dist/native-sessions.d.ts +70 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/native-sessions.js +538 -0
- package/node_modules/@rho-workspace/plugin-agent/package.json +2 -1
- package/node_modules/@rho-workspace/plugin-figures/LICENSE +661 -0
- package/node_modules/@rho-workspace/plugin-figures/README.md +191 -0
- package/node_modules/@rho-workspace/plugin-figures/dist/index.d.ts +236 -0
- package/node_modules/@rho-workspace/plugin-figures/dist/index.js +165 -0
- package/node_modules/@rho-workspace/plugin-figures/dist/operations.d.ts +44 -0
- package/node_modules/@rho-workspace/plugin-figures/dist/operations.js +39 -0
- package/node_modules/@rho-workspace/plugin-figures/dist/presentation.d.ts +19 -0
- package/node_modules/@rho-workspace/plugin-figures/dist/presentation.js +26 -0
- package/node_modules/@rho-workspace/plugin-figures/dist/record.d.ts +188 -0
- package/node_modules/@rho-workspace/plugin-figures/dist/record.js +143 -0
- package/node_modules/@rho-workspace/plugin-figures/dist/ui-styles.d.ts +1 -0
- package/node_modules/@rho-workspace/plugin-figures/dist/ui-styles.js +68 -0
- package/node_modules/@rho-workspace/plugin-figures/dist/ui.d.ts +56 -0
- package/node_modules/@rho-workspace/plugin-figures/dist/ui.js +363 -0
- package/node_modules/@rho-workspace/plugin-figures/package.json +41 -0
- package/node_modules/@rho-workspace/plugin-figures/py/figures.py +856 -0
- package/node_modules/@rho-workspace/plugin-figures/r/figures.R +775 -0
- package/node_modules/@rho-workspace/plugin-kernel/README.md +27 -3
- package/node_modules/@rho-workspace/plugin-kernel/dist/contract.d.ts +12 -0
- package/node_modules/@rho-workspace/plugin-kernel/dist/contract.js +8 -1
- package/node_modules/@rho-workspace/plugin-kernel/dist/index.d.ts +61 -0
- package/node_modules/@rho-workspace/plugin-kernel/dist/index.js +125 -9
- package/node_modules/@rho-workspace/plugin-kernel/dist/session-scope.d.ts +14 -0
- package/node_modules/@rho-workspace/plugin-kernel/dist/session-scope.js +9 -0
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui-base.d.ts +23 -5
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui-notebook-work.js +1 -1
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui-notebook.d.ts +6 -4
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui-notebook.js +44 -17
- package/node_modules/@rho-workspace/plugin-kernel/dist/ui-styles.js +6 -0
- package/node_modules/@rho-workspace/plugin-kernel/package.json +1 -1
- package/node_modules/@rho-workspace/plugin-kernel/runners/runner.R +144 -4
- package/node_modules/@rho-workspace/plugin-kernel/runners/runner.py +106 -1
- package/node_modules/@rho-workspace/plugin-tables/README.md +5 -0
- package/node_modules/@rho-workspace/plugin-tables/dist/index.d.ts +1 -0
- package/node_modules/@rho-workspace/plugin-tables/dist/index.js +19 -3
- package/node_modules/@rho-workspace/plugin-tables/dist/ui.js +1 -1
- package/node_modules/@rho-workspace/plugin-tables/package.json +1 -1
- package/node_modules/hono/dist/adapter/aws-lambda/handler.js +1 -1
- package/node_modules/hono/dist/cjs/adapter/aws-lambda/handler.js +1 -1
- package/node_modules/hono/dist/cjs/adapter/bun/serve-static.js +1 -1
- package/node_modules/hono/dist/cjs/adapter/bun/websocket.js +1 -1
- package/node_modules/hono/dist/cjs/client/fetch-result-please.js +1 -1
- package/node_modules/hono/dist/cjs/helper/accepts/accepts.js +2 -1
- package/node_modules/hono/dist/cjs/helper/ssg/ssg.js +5 -3
- package/node_modules/hono/dist/cjs/jsx/base.js +49 -30
- package/node_modules/hono/dist/cjs/jsx/components.js +2 -2
- package/node_modules/hono/dist/cjs/jsx/constants.js +2 -0
- package/node_modules/hono/dist/cjs/jsx/jsx-runtime.js +13 -17
- package/node_modules/hono/dist/cjs/middleware/etag/index.js +3 -1
- package/node_modules/hono/dist/cjs/middleware/pretty-json/index.js +1 -0
- package/node_modules/hono/dist/cjs/middleware/timing/timing.js +4 -3
- package/node_modules/hono/dist/cjs/utils/cookie.js +2 -2
- package/node_modules/hono/dist/cjs/utils/mime.js +1 -1
- package/node_modules/hono/dist/cjs/utils/url.js +1 -1
- package/node_modules/hono/dist/client/fetch-result-please.js +1 -1
- package/node_modules/hono/dist/helper/accepts/accepts.js +2 -1
- package/node_modules/hono/dist/helper/ssg/ssg.js +5 -3
- package/node_modules/hono/dist/jsx/base.js +49 -32
- package/node_modules/hono/dist/jsx/components.js +2 -2
- package/node_modules/hono/dist/jsx/constants.js +2 -1
- package/node_modules/hono/dist/jsx/jsx-runtime.js +15 -19
- package/node_modules/hono/dist/middleware/etag/index.js +3 -1
- package/node_modules/hono/dist/middleware/language/language.js +1 -1
- package/node_modules/hono/dist/middleware/pretty-json/index.js +1 -0
- package/node_modules/hono/dist/middleware/timing/timing.js +4 -3
- package/node_modules/hono/dist/utils/cookie.js +2 -2
- package/node_modules/hono/dist/utils/mime.js +1 -1
- package/node_modules/hono/dist/utils/url.js +1 -1
- package/node_modules/hono/package.json +1 -1
- package/node_modules/jose/dist/webapi/jwe/general/decrypt.js +3 -2
- package/node_modules/jose/dist/webapi/jwks/remote.js +1 -1
- package/node_modules/jose/dist/webapi/jws/general/verify.js +2 -8
- package/node_modules/jose/dist/webapi/lib/jwe_decrypt.js +11 -8
- package/node_modules/jose/dist/webapi/lib/key_management.js +0 -1
- package/node_modules/jose/package.json +1 -1
- package/package.json +6 -4
- package/protocol/index.json +106 -64
- package/protocol/operations/agent.sessions.list.json +57 -3
- package/protocol/operations/agent.sessions.read.json +61 -4
- package/protocol/operations/capability.list.json +382 -6
- package/protocol/operations/composition.apply.json +10 -2
- package/protocol/operations/composition.delete.json +845 -0
- package/protocol/operations/composition.read.json +10 -2
- package/protocol/operations/composition.undo.json +10 -2
- package/protocol/operations/conversation.agents.add.json +346 -0
- package/protocol/operations/conversation.agents.remove.json +340 -0
- package/protocol/operations/conversation.configure.json +29 -0
- package/protocol/operations/conversation.decide.json +225 -0
- package/protocol/operations/conversation.list.json +29 -0
- package/protocol/operations/conversation.permission.json +225 -0
- package/protocol/operations/conversation.read.json +225 -0
- package/protocol/operations/conversation.restore.json +29 -0
- package/protocol/operations/conversation.send.json +294 -0
- package/protocol/operations/conversation.settings.check.json +226 -416
- package/protocol/operations/conversation.settings.read.json +224 -411
- package/protocol/operations/conversation.settings.update.json +248 -475
- package/protocol/operations/conversation.start.json +313 -6
- package/protocol/operations/conversation.stop.json +225 -0
- package/protocol/operations/conversation.trash.json +29 -0
- package/protocol/operations/extension.describe.json +39 -0
- package/protocol/operations/extension.install.json +62 -0
- package/protocol/operations/extension.list.json +164 -1
- package/protocol/operations/extension.market.change.json +210 -0
- package/protocol/operations/figure.read.json +265 -0
- package/protocol/operations/figure.select.json +157 -0
- package/protocol/operations/kernel.interrupt.json +119 -0
- package/protocol/operations/kernel.read.json +119 -0
- package/protocol/operations/kernel.run.json +119 -0
- package/protocol/operations/observation.snapshot.json +445 -8
- package/protocol/operations/project.openFile.json +10 -2
- package/protocol/operations/session.grant.json +4 -0
- package/protocol/operations/session.instructions.json +43 -0
- package/protocol/operations/shared.json +1 -0
- package/protocol/operations/view.close.json +10 -2
- package/protocol/operations/view.protection.set.json +10 -2
- package/vendor/composition.json +174 -109
- package/vendor/rho-workspace-plugin-agent-0.4.33.tgz +0 -0
- package/vendor/rho-workspace-plugin-figures-0.5.1.tgz +0 -0
- package/vendor/rho-workspace-plugin-kernel-0.5.13.tgz +0 -0
- package/vendor/rho-workspace-plugin-tables-0.5.2.tgz +0 -0
- package/web/theme.css +17 -0
- package/dist/web/assets/answer-board-CBkGbSLD.js +0 -1
- package/dist/web/assets/canvas-D8rDdfDz.js +0 -1
- package/dist/web/assets/choices-b0XhrGQV.js +0 -1
- package/dist/web/assets/graph-1OdIJ9l7.js +0 -2
- package/dist/web/assets/index-CsC3sTRq.css +0 -1
- package/dist/web/assets/index-CxmItXqa.js +0 -891
- package/dist/web/assets/overview-B9UtVJ32.js +0 -6
- package/dist/web/assets/preview-CksD_5KS.js +0 -1
- package/dist/web/assets/studio-process-CsRoTHOh.js +0 -2
- package/dist/web/assets/timeline-events-BbuuFWHV.js +0 -1
- package/dist/web/assets/work-turns-BvJlIMmA.js +0 -1
- package/vendor/rho-workspace-plugin-agent-0.4.29.tgz +0 -0
- package/vendor/rho-workspace-plugin-kernel-0.5.5.tgz +0 -0
- package/vendor/rho-workspace-plugin-tables-0.5.0.tgz +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
import { fileURLToPath } from 'node:url';
|
|
4
5
|
export function acpCommand(agent) {
|
|
@@ -14,19 +15,25 @@ const clientName = { claude: 'Claude Code', codex: 'Codex' };
|
|
|
14
15
|
* executable, else the one already named in the environment, else the client on PATH. */
|
|
15
16
|
export function acpEnvironment(agent, env, executable) {
|
|
16
17
|
const name = executableVariable[agent];
|
|
17
|
-
return { ...env, [name]: executable || env[name] || agent };
|
|
18
|
+
return { ...withoutSessionMarkers(env), [name]: executable || env[name] || agent };
|
|
18
19
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
// When Rho itself was started from inside a Claude Code session (an Agent ran the setup), the
|
|
21
|
+
// environment carries that session's markers. A Claude Code started with them takes itself for
|
|
22
|
+
// its child: it saves no transcript (so its session cannot be continued anywhere) and reports to
|
|
23
|
+
// the parent's socket. Rho's sessions are the person's own, so those markers are not passed on.
|
|
24
|
+
const sessionMarkers = /^(CLAUDECODE|CLAUDE_CODE_(CHILD_SESSION|ENTRYPOINT|SESSION_ID|SESSION_ATTENDED|MESSAGING_SOCKET|MESSAGING_TOKEN|EXECPATH|SSE_PORT)|CLAUDE_PID)$/;
|
|
25
|
+
export function withoutSessionMarkers(env) {
|
|
26
|
+
return Object.fromEntries(Object.entries(env).filter(([key]) => !sessionMarkers.test(key)));
|
|
27
|
+
}
|
|
28
|
+
/** The file a shell runs for `program`: the program itself when it names a path, else the first
|
|
29
|
+
* runnable match on PATH. On Windows a copied environment keeps the variable's own case (Path),
|
|
30
|
+
* and a program is a file with an extension PATHEXT names (claude.exe, an npm codex.cmd). */
|
|
31
|
+
export function locate(program, env) {
|
|
25
32
|
const windows = process.platform === 'win32';
|
|
26
33
|
const variable = (name) => windows ? Object.entries(env).find(([key]) => key.toUpperCase() === name)?.[1] : env[name];
|
|
27
|
-
const named =
|
|
28
|
-
const extensions = windows && !path.extname(
|
|
29
|
-
const candidates = (named ? [
|
|
34
|
+
const named = program.includes('/') || (windows && program.includes('\\'));
|
|
35
|
+
const extensions = windows && !path.extname(program) ? (variable('PATHEXT') ?? '.COM;.EXE;.BAT;.CMD').split(';').filter(Boolean) : [''];
|
|
36
|
+
const candidates = (named ? [program] : (variable('PATH') ?? '').split(path.delimiter).filter(Boolean).map(directory => path.join(directory, program))).flatMap(file => extensions.map(extension => file + extension));
|
|
30
37
|
const runnable = (file) => { try {
|
|
31
38
|
fs.accessSync(file, fs.constants.X_OK);
|
|
32
39
|
return fs.statSync(file).isFile();
|
|
@@ -34,7 +41,43 @@ export function missingClient(agent, env) {
|
|
|
34
41
|
catch {
|
|
35
42
|
return false;
|
|
36
43
|
} };
|
|
37
|
-
|
|
44
|
+
return candidates.find(runnable) ?? null;
|
|
45
|
+
}
|
|
46
|
+
/** Why the adapter could not start the person's client, or null when its executable is
|
|
47
|
+
* there. The adapters report a missing client only as a closed connection. */
|
|
48
|
+
export function missingClient(agent, env) {
|
|
49
|
+
const executable = env[executableVariable[agent]] || agent;
|
|
50
|
+
if (locate(executable, env))
|
|
38
51
|
return null;
|
|
52
|
+
const named = executable.includes('/') || (process.platform === 'win32' && executable.includes('\\'));
|
|
39
53
|
return `${clientName[agent]} was not found ${named ? 'at ' + executable : 'on PATH (' + executable + ')'}. Install it, or select its executable.`;
|
|
40
54
|
}
|
|
55
|
+
export const knownAcpAgents = [
|
|
56
|
+
// DeepSeek Harness serves ACP from its `acp` profile, installed apart from the launcher.
|
|
57
|
+
{ id: 'dsh', name: 'DeepSeek Harness', program: 'dsh', args: ['acp'] },
|
|
58
|
+
{ id: 'hermes', name: 'Hermes Agent', program: 'hermes', args: ['acp'] },
|
|
59
|
+
{ id: 'kimi', name: 'Kimi Code', program: 'kimi', args: ['acp'] },
|
|
60
|
+
{ id: 'opencode', name: 'OpenCode', program: 'opencode', args: ['acp'] },
|
|
61
|
+
{ id: 'gemini', name: 'Gemini CLI', program: 'gemini', args: ['--acp'] },
|
|
62
|
+
{ id: 'qwen', name: 'Qwen Code', program: 'qwen', args: ['--acp'] },
|
|
63
|
+
{ id: 'goose', name: 'goose', program: 'goose', args: ['acp'] },
|
|
64
|
+
{ id: 'cursor', name: 'Cursor Agent', program: 'cursor-agent', args: ['acp'] },
|
|
65
|
+
{ id: 'auggie', name: 'Auggie CLI', program: 'auggie', args: ['--acp'] },
|
|
66
|
+
{ id: 'codebuddy', name: 'CodeBuddy Code', program: 'codebuddy', args: ['--acp'] },
|
|
67
|
+
{ id: 'droid', name: 'Factory Droid', program: 'droid', args: ['exec', '--output-format', 'acp-daemon'] },
|
|
68
|
+
];
|
|
69
|
+
const harnessProfile = (env) => fs.existsSync(path.join(env.DSH_HOME || path.join(os.homedir(), '.dsh'), 'profiles', 'acp', 'package.json'));
|
|
70
|
+
/** The known Agents installed on this computer, each with the file its command starts. Finding
|
|
71
|
+
* them reads PATH (and Harness's profile folder) and starts nothing: whether one answers as an
|
|
72
|
+
* ACP Agent is known only once it is started. */
|
|
73
|
+
export function installedAcpAgents(env = process.env) {
|
|
74
|
+
return knownAcpAgents.flatMap(agent => {
|
|
75
|
+
const file = locate(agent.program, env);
|
|
76
|
+
return file && (agent.id !== 'dsh' || harnessProfile(env)) ? [{ id: agent.id, name: agent.name, command: { file, args: agent.args } }] : [];
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/** The known Agent a command starts, when it starts one with its ACP arguments. */
|
|
80
|
+
export function knownAcpAgent(command) {
|
|
81
|
+
const program = path.basename(command.file).replace(/\.(exe|cmd|bat)$/i, '').toLowerCase();
|
|
82
|
+
return knownAcpAgents.find(agent => agent.program === program && agent.args.length === command.args.length && agent.args.every((arg, i) => arg === command.args[i])) ?? null;
|
|
83
|
+
}
|
|
@@ -148,9 +148,16 @@ export class AcpConversation {
|
|
|
148
148
|
} });
|
|
149
149
|
bounded.on('error', () => { });
|
|
150
150
|
child.stdout.pipe(bounded);
|
|
151
|
-
// Drain diagnostics without storing credentials, URLs or native reasoning.
|
|
152
|
-
|
|
153
|
-
|
|
151
|
+
// Drain diagnostics without storing credentials, URLs or native reasoning. Its last few
|
|
152
|
+
// kilobytes stay in memory only, to say why an Agent that exits before it answers did not
|
|
153
|
+
// start (an unknown argument, a missing sign-in); only its last line is shown, redacted.
|
|
154
|
+
let printed = '';
|
|
155
|
+
child.stderr.on('data', (chunk) => {
|
|
156
|
+
this.outputBytes += chunk.length;
|
|
157
|
+
if (this.outputBytes > o.limits.maxOutputBytes)
|
|
158
|
+
overflow('output-limit');
|
|
159
|
+
printed = (printed + chunk.toString('utf8')).slice(-4096);
|
|
160
|
+
});
|
|
154
161
|
this.connection = acp.client({ name: 'rho-agent' })
|
|
155
162
|
.onNotification(acp.methods.client.session.update, ({ params }) => {
|
|
156
163
|
try {
|
|
@@ -211,6 +218,16 @@ export class AcpConversation {
|
|
|
211
218
|
throw new Error('This Agent cannot load the original session. Nothing was resent.');
|
|
212
219
|
}
|
|
213
220
|
}
|
|
221
|
+
else if (o.forkFrom) {
|
|
222
|
+
const caps = initialized.agentCapabilities;
|
|
223
|
+
if (!caps?.sessionCapabilities?.fork)
|
|
224
|
+
throw new Error('This Agent cannot continue a copy of a session. Nothing was sent.');
|
|
225
|
+
const forked = await connection.agent.request('session/fork', { ...params, sessionId: o.forkFrom });
|
|
226
|
+
if (typeof forked?.sessionId !== 'string' || forked.sessionId === o.forkFrom)
|
|
227
|
+
throw new Error('The Agent did not create a separate copy of the session. Nothing was sent.');
|
|
228
|
+
this.sessionId = forked.sessionId;
|
|
229
|
+
this.settings = forked.configOptions ?? [];
|
|
230
|
+
}
|
|
214
231
|
else {
|
|
215
232
|
const created = await connection.agent.request(acp.methods.agent.session.new, params);
|
|
216
233
|
this.sessionId = created.sessionId;
|
|
@@ -229,6 +246,15 @@ export class AcpConversation {
|
|
|
229
246
|
...(initialized.agentInfo ? { adapter: { name: initialized.agentInfo.name, version: initialized.agentInfo.version } } : {}), ...(model ? { model } : {}) };
|
|
230
247
|
this.observationOrigin = 'live';
|
|
231
248
|
}
|
|
249
|
+
catch (error) {
|
|
250
|
+
await Promise.race([this.childEnded, new Promise(resolve => setTimeout(resolve, 300))]);
|
|
251
|
+
const last = printed.replace(/\x1b\[[0-9;]*[A-Za-z]/g, '').split(/\r?\n/).map(line => line.trim()).filter(Boolean).at(-1);
|
|
252
|
+
// The process may have ended while this waited, which the type of exit does not follow.
|
|
253
|
+
const exit = this.exit;
|
|
254
|
+
if (exit && last && !this.fault)
|
|
255
|
+
throw new Error(`The Agent exited before it answered${exit.code !== null ? ' (exit ' + exit.code + ')' : ''}. It printed: ${last.slice(0, 300)}`);
|
|
256
|
+
throw error;
|
|
257
|
+
}
|
|
232
258
|
finally {
|
|
233
259
|
clearTimeout(timer);
|
|
234
260
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
// A retained observation of another client's
|
|
1
|
+
// A conversation an Agent holds in its own record: a retained observation of another client's
|
|
2
|
+
// conversation, or a session the Agent keeps in its own files. Reading it starts nothing.
|
|
2
3
|
const text = { type: 'string' }, count = { type: 'integer', minimum: 0 };
|
|
3
4
|
const obj = (properties, required = Object.keys(properties)) => ({ type: 'object', properties, required, additionalProperties: false });
|
|
4
5
|
const array = (items, maxItems) => ({ type: 'array', items, maxItems });
|
|
@@ -6,10 +7,15 @@ export const externalSessionLimits = { maxSessions: 64, maxTurns: 200, maxTools:
|
|
|
6
7
|
const state = { enum: ['running', 'completed', 'failed', 'stopped', 'unknown'] };
|
|
7
8
|
export const externalResultSchema = obj({ path: text, id: text, version: text, projectId: text, toolId: text }, ['path', 'toolId']);
|
|
8
9
|
export const externalToolSchema = obj({ id: text, at: text, name: text, detail: text, output: text, outcome: { enum: ['requested', 'returned', 'failed', 'unknown'] }, exitCode: { type: 'integer' }, paths: array(text, 32) }, ['id', 'at', 'name', 'detail', 'output', 'outcome', 'paths']);
|
|
9
|
-
export const externalTurnSchema = obj({ id: text, at: text, endedAt: text, state, prompt: text, reply: text, messages: array(obj({ at: text, text }), 200), tools: array(externalToolSchema, externalSessionLimits.maxTools), results: array(externalResultSchema, 64), omittedTools: count, omittedChars: count }, ['id', 'at', 'state', 'prompt', 'reply', 'messages', 'tools', 'results', 'omittedTools', 'omittedChars']);
|
|
10
|
-
export const externalSessionSummarySchema = obj({ id: { type: 'string', pattern: '^x-[a-f0-9]{64}$' }, producer: text, sessionId: text, title: text, lastAt: text, observedAt: text, state, turns: count, sourceCursor: count, omittedTurns: count, problem: text, inputOwner: { const: 'external' }
|
|
10
|
+
export const externalTurnSchema = obj({ id: text, at: text, endedAt: text, state, prompt: text, reply: text, messages: array(obj({ at: text, text }), 200), tools: array(externalToolSchema, externalSessionLimits.maxTools), results: array(externalResultSchema, 64), omittedTools: count, omittedChars: count, where: text }, ['id', 'at', 'state', 'prompt', 'reply', 'messages', 'tools', 'results', 'omittedTools', 'omittedChars']);
|
|
11
|
+
export const externalSessionSummarySchema = obj({ id: { type: 'string', pattern: '^x-[a-f0-9]{64}$' }, producer: text, sessionId: text, title: text, lastAt: text, observedAt: text, state, turns: count, sourceCursor: count, omittedTurns: count, problem: text, inputOwner: { const: 'external' },
|
|
12
|
+
// Native sessions: where it was started, a live process holding it, the command that continues it in a terminal
|
|
13
|
+
// and its size. The application adds the Agent it continues with in Rho (acp:… is an ACP Agent the person
|
|
14
|
+
// added there) and the Rho conversation that continued it.
|
|
15
|
+
where: text, open: { anyOf: [{ type: 'null' }, obj({ where: text, since: text, pid: count }, ['where'])] }, resume: text, bytes: count,
|
|
16
|
+
continueWith: { anyOf: [{ enum: ['claude-acp', 'codex-acp', 'acp', null] }, { type: 'string', pattern: '^acp:[a-z0-9][a-z0-9-]{0,39}$' }] }, conversationId: text }, ['id', 'producer', 'sessionId', 'title', 'lastAt', 'observedAt', 'state', 'turns', 'sourceCursor', 'omittedTurns']);
|
|
11
17
|
export const externalSessionSchemas = { summary: externalSessionSummarySchema, turn: externalTurnSchema };
|
|
12
18
|
export const externalSessionOperations = {
|
|
13
|
-
'agent.sessions.list': { inputSchema: obj({}), outputSchema: obj({ sessions: array(externalSessionSummarySchema,
|
|
14
|
-
'agent.sessions.read': { inputSchema: obj({ session: { type: 'string', pattern: '^x-[a-f0-9]{64}$' }, before: count, limit: { type: 'integer', minimum: 1, maximum:
|
|
19
|
+
'agent.sessions.list': { inputSchema: obj({}), outputSchema: obj({ sessions: array(externalSessionSummarySchema, 128), omitted: count }), requires: [], effect: 'read' },
|
|
20
|
+
'agent.sessions.read': { inputSchema: obj({ session: { type: 'string', pattern: '^x-[a-f0-9]{64}$' }, before: count, limit: { type: 'integer', minimum: 1, maximum: 50 } }, ['session']), outputSchema: obj({ session: externalSessionSummarySchema, first: count, turns: array(externalTurnSchema, 50) }), requires: [], effect: 'read' },
|
|
15
21
|
};
|
|
@@ -31,6 +31,7 @@ export interface ExternalTurn {
|
|
|
31
31
|
results: ExternalResult[];
|
|
32
32
|
omittedTools: number;
|
|
33
33
|
omittedChars: number;
|
|
34
|
+
where?: string;
|
|
34
35
|
}
|
|
35
36
|
export interface ExternalSessionSummary {
|
|
36
37
|
id: string;
|
|
@@ -44,7 +45,17 @@ export interface ExternalSessionSummary {
|
|
|
44
45
|
sourceCursor: number;
|
|
45
46
|
omittedTurns: number;
|
|
46
47
|
problem?: string;
|
|
47
|
-
inputOwner
|
|
48
|
+
inputOwner?: 'external';
|
|
49
|
+
where?: string;
|
|
50
|
+
open?: {
|
|
51
|
+
where: string;
|
|
52
|
+
since?: string;
|
|
53
|
+
pid?: number;
|
|
54
|
+
} | null;
|
|
55
|
+
resume?: string;
|
|
56
|
+
bytes?: number;
|
|
57
|
+
continueWith?: 'claude-acp' | 'codex-acp' | 'acp' | `acp:${string}` | null;
|
|
58
|
+
conversationId?: string;
|
|
48
59
|
}
|
|
49
60
|
export interface ExternalSessionRecord {
|
|
50
61
|
format: 1;
|
|
@@ -1971,6 +1971,14 @@ export declare const plugin: {
|
|
|
1971
1971
|
modelLoop: string;
|
|
1972
1972
|
contextDelivery: string;
|
|
1973
1973
|
proactivePush: string;
|
|
1974
|
+
mcp: {
|
|
1975
|
+
entry: string;
|
|
1976
|
+
transport: string;
|
|
1977
|
+
tools: {
|
|
1978
|
+
name: string;
|
|
1979
|
+
description: string;
|
|
1980
|
+
}[];
|
|
1981
|
+
};
|
|
1974
1982
|
};
|
|
1975
1983
|
};
|
|
1976
1984
|
export { listExternalSessions, readExternalSession } from './external-session.js';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { layeredSchemas } from './layered-contract.js';
|
|
2
|
+
import { mcpEntry } from './mcp-entry.js';
|
|
2
3
|
import { skillPresentation, presentation, operationPresentation } from './presentation.js';
|
|
3
4
|
import { operationContracts } from './operations.js';
|
|
4
5
|
import * as fs from 'node:fs/promises';
|
|
@@ -302,9 +303,9 @@ const presentationSkill = {
|
|
|
302
303
|
],
|
|
303
304
|
verify: { command: 'npm run test:presenter', description: 'Presentation enhances display within its witness and role, and refuses business feedback or document access.' }
|
|
304
305
|
};
|
|
305
|
-
export const plugin = { entryVersion: '1.0', id: 'agent', package: '@rho-workspace/plugin-agent', version: '0.4.
|
|
306
|
+
export const plugin = { entryVersion: '1.0', id: 'agent', package: '@rho-workspace/plugin-agent', version: '0.4.33', requires: [], capabilities: ['agent-context'], presentation,
|
|
306
307
|
operations: Object.entries(operationContracts).map(([method, contract]) => ({ method, presentation: operationPresentation[method], version: '2.0', ...structuredClone(contract), requires: [...contract.requires] })),
|
|
307
308
|
components: [{ id: 'rho.agent-note', version: '0.2.0', owner: 'rho-plugins.agent', bindings: {}, propsSchema: layeredSchemas.AgentNoteProps, ui: './ui', presentation: { displayName: 'Agent note', description: 'A plain presentation explanation with authenticated author and binding basis.', readOnly: true, runsCode: false } }],
|
|
308
309
|
skills: [{ ...agentSkill, presentation: skillPresentation }, { ...feedbackSkill, presentation: { displayName: 'Business feedback', description: 'Reads original feedback, preserves current human edits and links an explicit reply.' } }, { ...presentationSkill, presentation: { displayName: 'Presentation', description: 'Enhances the assigned visible layout without business feedback or document access.' } }],
|
|
309
|
-
agent: { id: 'claude-code-mcp', transport: 'stdio', modelLoop: 'external-client', contextDelivery: 'pull-tool-result', proactivePush: 'unsupported' } };
|
|
310
|
+
agent: { id: 'claude-code-mcp', transport: 'stdio', modelLoop: 'external-client', contextDelivery: 'pull-tool-result', proactivePush: 'unsupported', mcp: mcpEntry } };
|
|
310
311
|
export { listExternalSessions, readExternalSession } from './external-session.js';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { layeredSchemas } from './layered-contract.js';
|
|
2
|
+
// The public MCP entry and its actual tool list share one declaration. Reading
|
|
3
|
+
// this metadata imports no server, connection file or Agent process.
|
|
4
|
+
export const mcpTools = [
|
|
5
|
+
{ name: 'rho_index', description: 'Discover optional Rho workspace capabilities alongside your native tools. Core managed execution is chosen only when needed. Select owner or search to page authorized operations. No full schemas are loaded.', inputSchema: layeredSchemas.McpIndexInput },
|
|
6
|
+
{ name: 'rho_resolve', description: 'Fetch complete guidance and contracts only for selected operations. Follow whole-object pages; knownRefs reuse bodies you still hold.', inputSchema: layeredSchemas.McpResolveInput },
|
|
7
|
+
{ name: 'rho_call', description: 'Call one resolved authorized operation. Large replies return an immutable result reference for rho_read; never repeat a write to retrieve its output.', inputSchema: layeredSchemas.McpCallInput },
|
|
8
|
+
{ name: 'rho_read', description: 'Inspect a retained result by JSON pointer, page containers or text, or explicitly release it. Text offsets count UTF-16 units. Reading never invokes the original operation.', inputSchema: layeredSchemas.McpReadInput }
|
|
9
|
+
];
|
|
10
|
+
export const mcpEntry = {
|
|
11
|
+
entry: './mcp', transport: 'stdio',
|
|
12
|
+
tools: mcpTools.map(({ name, description }) => ({ name, description }))
|
|
13
|
+
};
|
|
@@ -3,7 +3,7 @@ import * as fs from 'node:fs/promises';
|
|
|
3
3
|
import http from 'node:http';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { retainAgentIntent } from './agent-intents.js';
|
|
6
|
-
import {
|
|
6
|
+
import { mcpTools as tools } from './mcp-entry.js';
|
|
7
7
|
import { LayeredMcp } from './layered.js';
|
|
8
8
|
import { ResultPages, resultLimits } from './result-pages.js';
|
|
9
9
|
import { Ajv } from 'ajv';
|
|
@@ -31,11 +31,25 @@ requireThat(Number.isSafeInteger(transportMs) && transportMs > 0, 'invalid', 'In
|
|
|
31
31
|
const allowance = (input) => transportMs + (Number.isSafeInteger(input.waitMs) && input.waitMs > 0 ? Math.min(input.waitMs, 600000) : 0);
|
|
32
32
|
// node:http rather than fetch: fetch stops waiting for a response's headers after 300 s whatever
|
|
33
33
|
// the signal allows, and the service answers a waited cell only when the cell ends (P3, run 2).
|
|
34
|
+
// The client that started this server, as it named itself in MCP initialize, so the person's
|
|
35
|
+
// screen can show whose window the connection is. A name and version only, each bounded.
|
|
36
|
+
function clientHeader() {
|
|
37
|
+
let info;
|
|
38
|
+
try {
|
|
39
|
+
info = server.getClientVersion();
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return {};
|
|
43
|
+
}
|
|
44
|
+
if (!info?.name)
|
|
45
|
+
return {};
|
|
46
|
+
return { 'x-rho-client': [info.name, info.version].filter(Boolean).map(x => encodeURIComponent(String(x).slice(0, 128))).join('/') };
|
|
47
|
+
}
|
|
34
48
|
async function invoke(method, input = {}, contractRef) {
|
|
35
49
|
const body = JSON.stringify({ ...input, projectId: c.projectId });
|
|
36
50
|
const response = await new Promise((resolve, reject) => {
|
|
37
51
|
const request = http.request(new URL('/api/v2/' + method, endpoint), { method: 'POST', signal: AbortSignal.timeout(allowance(input)),
|
|
38
|
-
headers: { authorization: 'Bearer ' + c.token, 'content-type': 'application/json', 'content-length': Buffer.byteLength(body), ...(contractRef ? { 'x-rho-contract-ref': contractRef } : {}) } }, resolve);
|
|
52
|
+
headers: { authorization: 'Bearer ' + c.token, 'content-type': 'application/json', 'content-length': Buffer.byteLength(body), ...clientHeader(), ...(contractRef ? { 'x-rho-contract-ref': contractRef } : {}) } }, resolve);
|
|
39
53
|
// A refused connection is the one failure whose outcome is known: nothing reached the
|
|
40
54
|
// service, so nothing ran. It is usually Rho not running, and the person can start it.
|
|
41
55
|
request.once('error', error => reject(error.code === 'ECONNREFUSED'
|
|
@@ -125,12 +139,6 @@ const results = new ResultPages(path.dirname(descriptor), digest(c.projectId + '
|
|
|
125
139
|
const index = await layer.index({ search: operation });
|
|
126
140
|
requireThat(index.ok && index.value.entries.some((e) => e.id === operation), 'scope', 'The operation that produced this result is no longer available to this connection.');
|
|
127
141
|
}, limits);
|
|
128
|
-
const tools = [
|
|
129
|
-
{ name: 'rho_index', description: 'Discover optional Rho workspace capabilities alongside your native tools. Core managed execution is chosen only when needed. Select owner or search to page authorized operations. No full schemas are loaded.', inputSchema: layeredSchemas.McpIndexInput },
|
|
130
|
-
{ name: 'rho_resolve', description: 'Fetch complete guidance and contracts only for selected operations. Follow whole-object pages; knownRefs reuse bodies you still hold.', inputSchema: layeredSchemas.McpResolveInput },
|
|
131
|
-
{ name: 'rho_call', description: 'Call one resolved authorized operation. Large replies return an immutable result reference for rho_read; never repeat a write to retrieve its output.', inputSchema: layeredSchemas.McpCallInput },
|
|
132
|
-
{ name: 'rho_read', description: 'Inspect a retained result by JSON pointer, page containers or text, or explicitly release it. Text offsets count UTF-16 units. Reading never invokes the original operation.', inputSchema: layeredSchemas.McpReadInput }
|
|
133
|
-
];
|
|
134
142
|
const ajv = new Ajv({ strict: false });
|
|
135
143
|
const validators = Object.fromEntries(tools.map(t => [t.name, ajv.compile(t.inputSchema)]));
|
|
136
144
|
const server = new Server({ name: 'rho-project', version: '0.4.0' }, { capabilities: { tools: {} } });
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { type ExternalSessionSummary, type ExternalTurn } from './external-session.js';
|
|
2
|
+
export declare const nativeSessionLimits: {
|
|
3
|
+
maxSessions: number;
|
|
4
|
+
maxScanFiles: number;
|
|
5
|
+
maxHeadBytes: number;
|
|
6
|
+
maxTailBytes: number;
|
|
7
|
+
maxReadBytes: number;
|
|
8
|
+
maxTurns: number;
|
|
9
|
+
maxTools: number;
|
|
10
|
+
maxTextChars: number;
|
|
11
|
+
maxDetailChars: number;
|
|
12
|
+
activeMs: number;
|
|
13
|
+
};
|
|
14
|
+
export type NativeProducer = 'claude-code' | 'codex';
|
|
15
|
+
export interface NativeSessionSources {
|
|
16
|
+
claudeConfigDir?: string;
|
|
17
|
+
codexHome?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface NativeSessionSummary extends ExternalSessionSummary {
|
|
20
|
+
producer: NativeProducer;
|
|
21
|
+
where: string;
|
|
22
|
+
open: {
|
|
23
|
+
where: string;
|
|
24
|
+
since?: string;
|
|
25
|
+
pid?: number;
|
|
26
|
+
} | null;
|
|
27
|
+
resume: string;
|
|
28
|
+
bytes: number;
|
|
29
|
+
}
|
|
30
|
+
export declare function nativeSources(env?: NodeJS.ProcessEnv): Required<NativeSessionSources>;
|
|
31
|
+
/** The Agents' own sessions whose working folder is `root`, newest first. Reads files only. */
|
|
32
|
+
export declare function listNativeSessions(root: string, sources?: NativeSessionSources): {
|
|
33
|
+
sessions: NativeSessionSummary[];
|
|
34
|
+
omitted: number;
|
|
35
|
+
};
|
|
36
|
+
/** One session's turns, read through its file (its last maxReadBytes when larger). */
|
|
37
|
+
export declare function readNativeSession(root: string, input: {
|
|
38
|
+
session: string;
|
|
39
|
+
before?: number;
|
|
40
|
+
limit?: number;
|
|
41
|
+
}, sources?: NativeSessionSources): Promise<{
|
|
42
|
+
session: {
|
|
43
|
+
turns: number;
|
|
44
|
+
omittedTurns: number;
|
|
45
|
+
producer: NativeProducer;
|
|
46
|
+
where: string;
|
|
47
|
+
open: {
|
|
48
|
+
where: string;
|
|
49
|
+
since?: string;
|
|
50
|
+
pid?: number;
|
|
51
|
+
} | null;
|
|
52
|
+
resume: string;
|
|
53
|
+
bytes: number;
|
|
54
|
+
id: string;
|
|
55
|
+
sessionId: string;
|
|
56
|
+
title: string;
|
|
57
|
+
lastAt: string;
|
|
58
|
+
observedAt: string;
|
|
59
|
+
state: ExternalTurn["state"];
|
|
60
|
+
sourceCursor: number;
|
|
61
|
+
problem?: string;
|
|
62
|
+
inputOwner?: "external";
|
|
63
|
+
continueWith?: "claude-acp" | "codex-acp" | "acp" | `acp:${string}` | null;
|
|
64
|
+
conversationId?: string;
|
|
65
|
+
};
|
|
66
|
+
first: number;
|
|
67
|
+
turns: ExternalTurn[];
|
|
68
|
+
} | null>;
|
|
69
|
+
/** Whether `sessionId` names a session of this Agent in `root`, and its summary. */
|
|
70
|
+
export declare function nativeSession(root: string, producer: NativeProducer, sessionId: string, sources?: NativeSessionSources): NativeSessionSummary | null;
|