@rho-workspace/rho 0.5.4 → 0.5.6
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 +8 -0
- package/dist/agent-connect.js +15 -0
- package/dist/browser-access.js +3 -2
- package/dist/capabilities.d.ts +2 -2
- package/dist/capabilities.js +6 -1
- package/dist/cli.js +219 -176
- package/dist/contract.generated.d.ts +472 -4
- package/dist/contract.generated.js +1 -1
- package/dist/contracts/owners.generated.js +455 -6
- package/dist/conversations.d.ts +157 -4
- package/dist/conversations.js +69 -6
- 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/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-work.d.ts +3 -1
- package/dist/project-work.js +15 -2
- package/dist/protocol.d.ts +8 -0
- package/dist/protocol.js +16 -7
- package/dist/server.d.ts +5 -10
- package/dist/server.js +122 -12
- package/dist/web/.vite/manifest.json +67 -67
- package/dist/web/assets/{canvas-BXhC8RDZ.js → canvas-D8rDdfDz.js} +1 -1
- package/dist/web/assets/choices-D-YfnFP6.js +1 -0
- package/dist/web/assets/{console-Dbdo-5oO.js → console-Trqc0swD.js} +1 -1
- package/dist/web/assets/{dist-B2V0VDO0.js → dist-DZmszrjG.js} +2 -2
- package/dist/web/assets/dist-DpIHRk3B.js +1 -0
- package/dist/web/assets/{dist-D25Vn17i.js → dist-eMx6b8N1.js} +1 -1
- package/dist/web/assets/dist-pF902jHA.js +3 -0
- package/dist/web/assets/explore-in-BCHqjJ7B.js +1 -0
- package/dist/web/assets/graph-lENgfQNH.js +2 -0
- package/dist/web/assets/index-B3_OVy91.css +1 -0
- package/dist/web/assets/{index-BHQIaDsx.js → index-BpL_Kl_s.js} +87 -86
- package/dist/web/assets/{notes-meaning-DdPbtNzJ.js → notes-meaning-DcVGUBBY.js} +1 -1
- package/dist/web/assets/overview-CDnVKlOt.js +6 -0
- package/dist/web/assets/preview-BEC1E9MK.js +1 -0
- package/dist/web/assets/{review-CmWou4Iv.js → review-DYP0aCXb.js} +1 -1
- package/dist/web/assets/{skills-Dy7n3L9d.js → skills-1ec0Z-jx.js} +1 -1
- package/dist/web/assets/{skills-work-Cj0W6b-I.js → skills-work-DNexV4OJ.js} +1 -1
- package/dist/web/assets/{studio-process-QOY_n2_8.js → studio-process-EYQgikxq.js} +2 -2
- package/dist/web/assets/{timeline-events-DmX1BCh3.js → timeline-events-Chj2Gbx7.js} +1 -1
- package/dist/web/assets/{work-turns-Co0C71rJ.js → work-turns-CT9RVcWY.js} +1 -1
- package/dist/web/assets/workspace-COmCbGV3.js +5 -0
- package/dist/web/composition.json +65 -65
- 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 +26 -18
- package/docs/HEADLESS.md +38 -28
- package/docs/PLUGIN-AUTHORING.md +4 -0
- package/docs/THIRD-PARTY-PLUGINS.md +190 -0
- package/node_modules/@rho-workspace/plugin-agent/README.md +68 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/acp-command.d.ts +1 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/acp-command.js +9 -1
- 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 +10 -0
- 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/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 +20 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/external-session.d.ts +84 -0
- package/node_modules/@rho-workspace/plugin-agent/dist/external-session.js +190 -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/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/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 +6 -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/package.json +5 -3
- package/protocol/index.json +93 -33
- package/protocol/operations/agent.sessions.list.json +146 -0
- package/protocol/operations/agent.sessions.read.json +315 -0
- package/protocol/operations/conversation.configure.json +29 -0
- package/protocol/operations/conversation.decide.json +29 -0
- package/protocol/operations/conversation.list.json +29 -0
- package/protocol/operations/conversation.permission.json +29 -0
- package/protocol/operations/conversation.read.json +29 -0
- package/protocol/operations/conversation.restore.json +29 -0
- package/protocol/operations/conversation.send.json +29 -0
- package/protocol/operations/conversation.settings.check.json +92 -0
- package/protocol/operations/conversation.settings.read.json +92 -0
- package/protocol/operations/conversation.settings.update.json +92 -0
- package/protocol/operations/conversation.start.json +46 -1
- package/protocol/operations/conversation.stop.json +29 -0
- package/protocol/operations/conversation.trash.json +29 -0
- 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/observation.snapshot.json +29 -0
- package/protocol/operations/project.work.read.json +3 -2
- package/protocol/operations/session.grant.json +9 -0
- package/protocol/operations/session.instructions.json +43 -0
- package/scripts/third-party-acceptance.mjs +77 -0
- package/vendor/composition.json +83 -83
- package/vendor/rho-workspace-plugin-agent-0.4.30.tgz +0 -0
- package/web/theme.css +12 -0
- package/dist/web/assets/choices-DU9kHlyQ.js +0 -1
- package/dist/web/assets/dist-BV3hWfn_.js +0 -3
- package/dist/web/assets/dist-CLNF2g-E.js +0 -1
- package/dist/web/assets/explore-in-8Q5ftt-E.js +0 -1
- package/dist/web/assets/graph-hS2kvHTI.js +0 -2
- package/dist/web/assets/index-Cj1_iqyK.css +0 -1
- package/dist/web/assets/overview-BV6ZO5TO.js +0 -6
- package/dist/web/assets/preview-n5lOOdjN.js +0 -1
- package/dist/web/assets/workspace-BKFefRfJ.js +0 -5
- package/vendor/rho-workspace-plugin-agent-0.4.28.tgz +0 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import * as fs from 'node:fs/promises';
|
|
2
|
+
import { watch } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
import { parseArgs } from 'node:util';
|
|
6
|
+
import { randomUUID } from 'node:crypto';
|
|
7
|
+
import { Extensions } from './extensions.js';
|
|
8
|
+
import { RhoClient } from './client.js';
|
|
9
|
+
const help = `Independent Rho plugins (API 1)
|
|
10
|
+
rho plugin create DIRECTORY [--id my-plugin]
|
|
11
|
+
rho plugin check DIRECTORY
|
|
12
|
+
rho plugin add SOURCE [--connection FILE]
|
|
13
|
+
rho plugin list
|
|
14
|
+
rho plugin update ID [--source SOURCE]
|
|
15
|
+
rho plugin enable|disable|remove ID
|
|
16
|
+
rho plugin dev DIRECTORY
|
|
17
|
+
rho plugin call ID OPERATION --project RELATIVE_FOLDER --input JSON [--change-id ID]
|
|
18
|
+
|
|
19
|
+
SOURCE: npm package, local folder/.tgz, public HTTPS .tgz, or
|
|
20
|
+
git+https://host/repo.git#ref:relative/package
|
|
21
|
+
Management uses the running Rho's private connection file (default ~/Rho/state/connection.json).
|
|
22
|
+
Only install packages you trust: hosts run native code with your OS permissions.
|
|
23
|
+
Install skips lifecycle scripts. Build packages before adding them.
|
|
24
|
+
dev watches source and installs checked snapshots; an invalid edit keeps the previous version.
|
|
25
|
+
Remove preserves project files, live sessions, receipts and retained package revisions.
|
|
26
|
+
`;
|
|
27
|
+
export async function pluginCli(args) {
|
|
28
|
+
const { values, positionals } = parseArgs({ args, allowPositionals: true, options: { connection: { type: 'string' }, id: { type: 'string' }, source: { type: 'string' }, project: { type: 'string' }, input: { type: 'string' }, 'change-id': { type: 'string' }, help: { type: 'boolean' } } });
|
|
29
|
+
const [command, target, operation] = positionals;
|
|
30
|
+
if (values.help || !command) {
|
|
31
|
+
process.stdout.write(help);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (command === 'create') {
|
|
35
|
+
if (!target)
|
|
36
|
+
throw new Error('Name a new plugin directory.');
|
|
37
|
+
const id = values.id ?? path.basename(path.resolve(target));
|
|
38
|
+
if (!/^[a-z0-9][a-z0-9._-]{0,95}$/.test(id))
|
|
39
|
+
throw new Error('Use a lowercase plugin id containing letters, digits, dots, underscores or hyphens.');
|
|
40
|
+
await fs.mkdir(path.resolve(target));
|
|
41
|
+
const manifest = { apiVersion: 1, id, title: id, description: 'An independent Rho plugin.', host: 'host.mjs', operations: [{ id: 'greet', title: 'Greet', description: 'Return a greeting without changing project data.', effect: 'read', methods: [], inputSchema: { type: 'object', properties: { name: { type: 'string', maxLength: 120 } }, required: ['name'], additionalProperties: false } }], skills: [{ id: 'guide', title: 'Use this plugin', file: 'SKILL.md' }], views: [{ id: 'main', title: id, file: 'view.html' }] };
|
|
42
|
+
const files = {
|
|
43
|
+
'package.json': JSON.stringify({ name: 'rho-plugin-' + id, version: '0.1.0', type: 'module', license: 'MIT', rhoPlugin: 'rho-plugin.json', files: ['rho-plugin.json', 'host.mjs', 'view.html', 'SKILL.md'] }, null, 2) + '\n',
|
|
44
|
+
'rho-plugin.json': JSON.stringify(manifest, null, 2) + '\n',
|
|
45
|
+
'host.mjs': `export async function run(operation, input, context) {\n if (operation === 'greet') return { greeting: 'Hello, ' + input.name + '!' };\n throw new Error('Unknown operation');\n}\n`,
|
|
46
|
+
'SKILL.md': `# ${id}\n\nRead extension.describe for the installed digest and operation schemas. Call greet with a name. This operation reads only. Use a stable changeId for native operations; observe extension.read after a lost reply.\n`,
|
|
47
|
+
'view.html': `<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${id}</title><style>body{font:16px system-ui;margin:24px;color:#24312b}input,button{font:inherit;padding:8px}output{display:block;margin-top:16px}</style><h1>${id}</h1><form><label>Your name <input name="name" value="World" maxlength="120"></label> <button>Greet</button></form><output aria-live="polite"></output><script>document.querySelector('form').onsubmit=async e=>{e.preventDefault();const out=document.querySelector('output');try{const result=await rho.call('greet',{name:e.target.elements.name.value});out.textContent=result.value?.greeting||result.error}catch(error){out.textContent=error.message}};</script></html>\n`,
|
|
48
|
+
'README.md': `# ${id}\n\nDevelop: \`rho plugin dev .\`\nCheck: \`rho plugin check .\`\nDistribute: \`npm pack --ignore-scripts\`, then share the tarball, Git package source or publish to your own npm namespace.\n\nSee Rho's docs/THIRD-PARTY-PLUGINS.md for the host and iframe APIs, permissions and contribution process.\n`,
|
|
49
|
+
};
|
|
50
|
+
for (const [file, text] of Object.entries(files))
|
|
51
|
+
await fs.writeFile(path.join(target, file), text, { flag: 'wx' });
|
|
52
|
+
process.stdout.write(`Created ${path.resolve(target)}. Run rho plugin check, then rho plugin dev.\n`);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (command === 'check') {
|
|
56
|
+
if (!target)
|
|
57
|
+
throw new Error('Name a plugin directory.');
|
|
58
|
+
const checked = await new Extensions('').check(path.resolve(target));
|
|
59
|
+
process.stdout.write(JSON.stringify({ valid: true, ...checked }, null, 2) + '\n');
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const client = await RhoClient.fromFile(path.resolve(values.connection ?? path.join(homedir(), 'Rho', 'state', 'connection.json')));
|
|
63
|
+
const list = async () => (await client.call('extension.list', {})).plugins;
|
|
64
|
+
const find = async () => { const p = (await list()).find(p => p.id === target); if (!p)
|
|
65
|
+
throw new Error('Plugin is not installed: ' + target); return p; };
|
|
66
|
+
// npm/Git staging can take longer than the ordinary 30-second observation client.
|
|
67
|
+
// There is no execution retry here; a lost response is reconciled with list.
|
|
68
|
+
const install = async (body) => {
|
|
69
|
+
const local = body.source.startsWith('file:') ? body.source.slice(5) : body.source;
|
|
70
|
+
if (await fs.stat(path.resolve(local)).catch(() => null))
|
|
71
|
+
body = { ...body, source: path.resolve(local) };
|
|
72
|
+
const response = await fetch(new URL('/api/v2/extension.install', client.connection.url), { method: 'POST', headers: { authorization: 'Bearer ' + client.connection.token, 'content-type': 'application/json' }, body: JSON.stringify(body) });
|
|
73
|
+
const result = await response.json();
|
|
74
|
+
if (!result.ok)
|
|
75
|
+
throw new Error(result.error.message);
|
|
76
|
+
return result.value;
|
|
77
|
+
};
|
|
78
|
+
let result;
|
|
79
|
+
if (command === 'list')
|
|
80
|
+
result = await list();
|
|
81
|
+
else if (command === 'add') {
|
|
82
|
+
if (!target)
|
|
83
|
+
throw new Error('Name a package source.');
|
|
84
|
+
result = await install({ source: target });
|
|
85
|
+
}
|
|
86
|
+
else if (command === 'update') {
|
|
87
|
+
const p = await find();
|
|
88
|
+
result = await install({ source: values.source ?? p.source, replace: p.id, expectedDigest: p.digest });
|
|
89
|
+
}
|
|
90
|
+
else if (['enable', 'disable', 'remove'].includes(command)) {
|
|
91
|
+
const p = await find();
|
|
92
|
+
result = await client.call('extension.manage', { pluginId: p.id, expectedDigest: p.digest, action: command });
|
|
93
|
+
}
|
|
94
|
+
else if (command === 'call') {
|
|
95
|
+
if (!operation || !values.project)
|
|
96
|
+
throw new Error('Call needs an operation and --project.');
|
|
97
|
+
const p = await find(), project = await client.call('project.open', { path: values.project });
|
|
98
|
+
const changeId = values['change-id'] ?? randomUUID();
|
|
99
|
+
process.stderr.write('changeId: ' + changeId + '\n');
|
|
100
|
+
result = await client.call('extension.call', { projectId: project.id, pluginId: p.id, expectedDigest: p.digest, operation, input: JSON.parse(values.input ?? '{}'), changeId });
|
|
101
|
+
}
|
|
102
|
+
else if (command === 'dev') {
|
|
103
|
+
if (!target)
|
|
104
|
+
throw new Error('Name a plugin directory.');
|
|
105
|
+
const root = path.resolve(target), checked = await new Extensions('').check(root);
|
|
106
|
+
let busy = false, pending = false;
|
|
107
|
+
const update = async () => {
|
|
108
|
+
if (busy) {
|
|
109
|
+
pending = true;
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
busy = true;
|
|
113
|
+
try {
|
|
114
|
+
const p = (await list()).find(p => p.id === checked.manifest.id);
|
|
115
|
+
const installed = await install({ source: root, ...(p ? { replace: p.id, expectedDigest: p.digest } : {}) });
|
|
116
|
+
process.stdout.write(`Installed ${installed.id} ${installed.version} ${installed.digest}. Reopen its view to load this snapshot.\n`);
|
|
117
|
+
}
|
|
118
|
+
catch (e) {
|
|
119
|
+
process.stderr.write((e instanceof Error ? e.message : String(e)) + '\n');
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
busy = false;
|
|
123
|
+
if (pending) {
|
|
124
|
+
pending = false;
|
|
125
|
+
void update();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
await update();
|
|
130
|
+
let timer;
|
|
131
|
+
watch(root, { recursive: true }, (_event, filename) => { if (filename?.split(/[\\/]/).some(p => p === 'node_modules' || p === '.git'))
|
|
132
|
+
return; clearTimeout(timer); timer = setTimeout(() => void update(), 400); });
|
|
133
|
+
process.stdout.write('Watching ' + root + '. Stop with Ctrl-C.\n');
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
else
|
|
137
|
+
throw new Error('Unknown plugin command.\n' + help);
|
|
138
|
+
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
139
|
+
}
|
|
@@ -768,6 +768,24 @@
|
|
|
768
768
|
},
|
|
769
769
|
"listing": {
|
|
770
770
|
"actions": [
|
|
771
|
+
{
|
|
772
|
+
"presentation": {
|
|
773
|
+
"order": 58,
|
|
774
|
+
"displayName": "External conversations",
|
|
775
|
+
"description": "Reads retained conversations observed in this project. Input remains in the original client.",
|
|
776
|
+
"readOnly": true,
|
|
777
|
+
"runsCode": false
|
|
778
|
+
}
|
|
779
|
+
},
|
|
780
|
+
{
|
|
781
|
+
"presentation": {
|
|
782
|
+
"order": 59,
|
|
783
|
+
"displayName": "External conversation",
|
|
784
|
+
"description": "Reads native turns, tool outcomes and result receipts without sending or resuming anything.",
|
|
785
|
+
"readOnly": true,
|
|
786
|
+
"runsCode": false
|
|
787
|
+
}
|
|
788
|
+
},
|
|
771
789
|
{
|
|
772
790
|
"presentation": {
|
|
773
791
|
"order": 62,
|
package/dist/plugins.d.ts
CHANGED
|
@@ -74,7 +74,7 @@ export declare class Plugins {
|
|
|
74
74
|
timeoutSeconds?: number;
|
|
75
75
|
}[];
|
|
76
76
|
};
|
|
77
|
-
ids(): ("media" | "r" | "agent" | "files" | "documents" | "requirements" | "tables" | "charts" | "annotations" | "console" | "kernel"
|
|
77
|
+
ids(): ("media" | "r" | "agent" | "files" | "skills" | "documents" | "requirements" | "tables" | "charts" | "annotations" | "console" | "kernel")[];
|
|
78
78
|
isKnown(id: string): id is PluginId;
|
|
79
79
|
availabilityFor(id: PluginId): any;
|
|
80
80
|
need<K extends PluginId>(id: K): Modules[K];
|
|
@@ -161,10 +161,10 @@ export declare class Plugins {
|
|
|
161
161
|
reason?: undefined;
|
|
162
162
|
};
|
|
163
163
|
descriptors(): PluginDescriptor[];
|
|
164
|
-
availableIds(): ("media" | "r" | "agent" | "files" | "documents" | "requirements" | "tables" | "charts" | "annotations" | "console" | "kernel"
|
|
164
|
+
availableIds(): ("media" | "r" | "agent" | "files" | "skills" | "documents" | "requirements" | "tables" | "charts" | "annotations" | "console" | "kernel")[];
|
|
165
165
|
descriptor(id: string): PluginDescriptor | undefined;
|
|
166
166
|
nativeAvailability(rscript: string): Promise<any>;
|
|
167
|
-
operationOwner(method: string): "media" | "r" | "agent" | "files" | "documents" | "requirements" | "tables" | "charts" | "annotations" | "console" | "kernel" |
|
|
167
|
+
operationOwner(method: string): "media" | "r" | "agent" | "files" | "skills" | "documents" | "requirements" | "tables" | "charts" | "annotations" | "console" | "kernel" | undefined;
|
|
168
168
|
operationDeclarations(): {
|
|
169
169
|
owner: string;
|
|
170
170
|
method: string;
|
package/dist/project-work.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Conversations } from './conversations.js';
|
|
2
2
|
import type { Project } from './state.js';
|
|
3
|
-
import type { observeSessionFiles, readAgentActivity, readExternalAcp } from '@rho-workspace/plugin-agent';
|
|
3
|
+
import type { observeSessionFiles, readAgentActivity, readExternalAcp, listExternalSessions, readExternalSession } from '@rho-workspace/plugin-agent';
|
|
4
4
|
import type { RhoOutput } from './contract.generated.js';
|
|
5
5
|
export declare function projectWork(input: {
|
|
6
6
|
project: Project;
|
|
@@ -24,4 +24,6 @@ export declare function projectWork(input: {
|
|
|
24
24
|
native: Awaited<ReturnType<typeof readAgentActivity>> | null;
|
|
25
25
|
external?: ReturnType<typeof readExternalAcp>;
|
|
26
26
|
inspect?: typeof observeSessionFiles;
|
|
27
|
+
observed?: ReturnType<typeof listExternalSessions>;
|
|
28
|
+
readObserved?: typeof readExternalSession;
|
|
27
29
|
}): Promise<RhoOutput<'project.work.read'>>;
|
package/dist/project-work.js
CHANGED
|
@@ -20,10 +20,23 @@ export async function projectWork(input) {
|
|
|
20
20
|
lastMessage: s.lastMessage, omittedMessageChars: s.omittedMessageChars, pendingPermissions: s.pendingPermissions,
|
|
21
21
|
operationIds: [], resourceRefs: [] }));
|
|
22
22
|
const refs = [...owned, ...external].flatMap(s => [...(s.observation?.tools ?? [])].reverse().flatMap(t => t.locations.map(l => ({ path: l.path, session: s.id, tool: t.id }))));
|
|
23
|
+
const observed = (input.observed?.sessions ?? []).slice(0, 8).map(s => {
|
|
24
|
+
let turns = [];
|
|
25
|
+
try {
|
|
26
|
+
turns = input.readObserved?.(input.project.root, { session: s.id, limit: 2 }).turns ?? [];
|
|
27
|
+
}
|
|
28
|
+
catch { /* Summary remains available when detail cannot be read. */ }
|
|
29
|
+
for (const turn of turns)
|
|
30
|
+
for (const tool of turn.tools)
|
|
31
|
+
for (const path of tool.paths)
|
|
32
|
+
refs.push({ path, session: s.id, tool: tool.id });
|
|
33
|
+
const resources = turns.flatMap(t => t.results.filter(r => r.id && r.version && r.projectId === input.project.id).map(r => ({ id: r.id, version: r.version })));
|
|
34
|
+
return { id: s.id, source: 'external-session', sessionId: s.sessionId, title: s.title, agent: s.producer, state: s.state, version: s.sourceCursor, observation: null, operationIds: [], resourceRefs: [...new Map(resources.map(r => [r.id + '@' + r.version, r])).values()] };
|
|
35
|
+
});
|
|
23
36
|
const inspected = input.inspect ? await input.inspect(input.project.root, refs) : { files: [], omitted: refs.length };
|
|
24
37
|
return { projectId: input.project.id, cursor: input.cursor, observedAt: new Date().toISOString(),
|
|
25
38
|
notes: { path: notesPath, ...(note ? { documentId: note.id, version: note.version, dirty: note.dirty } : {}) },
|
|
26
|
-
sessions: [...owned, ...external, ...native], files: inspected.files,
|
|
39
|
+
sessions: [...owned, ...observed, ...external, ...native], files: inspected.files,
|
|
27
40
|
native: { state: input.native ? 'available' : 'unavailable', message: input.native ? 'Retained native sources; input stays in their original window.' : 'Native activity is not available from the current Host. Reading this view does not start it.' },
|
|
28
|
-
omitted: { conversations: input.conversations.omitted, fileReferences: inspected.omitted, external: (input.external?.omitted ?? 0) + Math.max(0, externalSessions.length - external.length) } };
|
|
41
|
+
omitted: { conversations: input.conversations.omitted, fileReferences: inspected.omitted, external: (input.external?.omitted ?? 0) + (input.observed?.omitted ?? 0) + Math.max(0, (input.observed?.sessions.length ?? 0) - observed.length) + Math.max(0, externalSessions.length - external.length) } };
|
|
29
42
|
}
|
package/dist/protocol.d.ts
CHANGED
|
@@ -173,6 +173,7 @@ export declare const descriptions: {
|
|
|
173
173
|
'conversation.settings.update': string;
|
|
174
174
|
'conversation.settings.check': string;
|
|
175
175
|
'session.connect': string;
|
|
176
|
+
'session.instructions': string;
|
|
176
177
|
'conversation.read': string;
|
|
177
178
|
'project.work.read': string;
|
|
178
179
|
'resource.register': string;
|
|
@@ -192,6 +193,13 @@ export declare const descriptions: {
|
|
|
192
193
|
'workspace.enhance': string;
|
|
193
194
|
'capability.index': string;
|
|
194
195
|
'capability.resolve': string;
|
|
196
|
+
'extension.list': string;
|
|
197
|
+
'extension.describe': string;
|
|
198
|
+
'extension.content': string;
|
|
199
|
+
'extension.call': string;
|
|
200
|
+
'extension.read': string;
|
|
201
|
+
'extension.install': string;
|
|
202
|
+
'extension.manage': string;
|
|
195
203
|
};
|
|
196
204
|
export declare const errorKinds: string[];
|
|
197
205
|
export declare const errorSchema: {
|
package/dist/protocol.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { extensionOperations, extensionResponses, extensionDescriptions } from './extension-contract.js';
|
|
1
2
|
import { evidenceSchema } from './evidence-capture.js';
|
|
2
3
|
import { ownerContracts } from './contracts/owners.generated.js';
|
|
3
4
|
import { enhancementSchemas } from './enhancement-contracts.js';
|
|
@@ -42,7 +43,7 @@ for (const owner of ['documents', 'files', 'r', 'requirements', 'tables', 'media
|
|
|
42
43
|
ownerOperations[method] = contract;
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
|
-
export const operations = { ...base.operations,
|
|
46
|
+
export const operations = { ...base.operations, ...extensionOperations,
|
|
46
47
|
'protocol.describe': obj({ ...project, method: text, cursor: { type: ['string', 'null'], maxLength: 128 }, limit: { type: 'integer', minimum: 1, maximum: 100 }, maxBytes: { type: 'integer', minimum: 1024, maximum: 65536 } }, []),
|
|
47
48
|
'project.pick': obj({}),
|
|
48
49
|
'project.source.pick': obj({}),
|
|
@@ -70,7 +71,8 @@ export const operations = { ...base.operations,
|
|
|
70
71
|
'data.compare': obj({ ...project, left: ref, right: ref, options: obj({ leftKey: text, rightKey: text, leftNamespace: text, rightNamespace: text, mapping: { const: 'equal-string' }, leftField: text, rightField: text, join: { enum: ['left', 'inner', 'full'] } }) }),
|
|
71
72
|
'media.describe': obj({ ...project, resource: ref }),
|
|
72
73
|
// A conversation the person starts in Rho: their words, under a request identity they hold.
|
|
73
|
-
'conversation.start': obj({ ...project, changeId, text: { type: 'string', minLength: 1, maxLength: 16384 }, backend: agentBackend, config: choices, contexts: { ...array(digest), maxItems: 8 }
|
|
74
|
+
'conversation.start': obj({ ...project, changeId, text: { type: 'string', minLength: 1, maxLength: 16384 }, backend: agentBackend, config: choices, contexts: { ...array(digest), maxItems: 8 },
|
|
75
|
+
continue: obj({ session: { type: 'string', pattern: '^x-[a-f0-9]{64}$' }, copy: { type: 'boolean' } }, ['session']) }, ['projectId', 'changeId', 'text']),
|
|
74
76
|
'conversation.send': obj({ ...project, conversationId: changeId, changeId, text: { type: 'string', minLength: 1, maxLength: 16384 }, contexts: { ...array(digest), maxItems: 8 }, continuationOf: changeId }, ['projectId', 'conversationId', 'changeId', 'text']),
|
|
75
77
|
'conversation.permission': obj({ ...project, conversationId: changeId, turnId: changeId, permissionId: changeId, optionId: { type: 'string', minLength: 1, maxLength: 256 } }),
|
|
76
78
|
'conversation.stop': obj({ ...project, conversationId: changeId, turnId: changeId }),
|
|
@@ -86,7 +88,8 @@ export const operations = { ...base.operations,
|
|
|
86
88
|
'conversation.settings.update': obj({ ...project, expectedVersion: integer, backend: { anyOf: [agentBackend, { type: 'null' }] }, choices: obj({ 'claude-acp': choices, 'codex-acp': choices, 'acp': choices }, []), describe: obj({ enabled: { type: 'boolean' } }, ['enabled']) }, ['projectId', 'expectedVersion']),
|
|
87
89
|
'conversation.settings.check': obj({ ...project, backend: agentBackend }),
|
|
88
90
|
// Claude Code or Codex running outside Rho, given this project and told how to start Rho's MCP server.
|
|
89
|
-
'session.connect': obj({ ...project, agent: outsideAgent })
|
|
91
|
+
'session.connect': obj({ ...project, agent: outsideAgent }),
|
|
92
|
+
'session.instructions': obj(project)
|
|
90
93
|
};
|
|
91
94
|
// Every owner-declared operation and response comes from the snapshot, scoped once.
|
|
92
95
|
for (const [method, contract] of Object.entries(ownerOperations))
|
|
@@ -199,14 +202,17 @@ const catalog = { ...contract.capabilityCatalogSchema };
|
|
|
199
202
|
const conversationSummary = response({ conversationId: text, title: text, client: text, principal: { anyOf: [text, { type: 'null' }] }, createdAt: text, updatedAt: text, version: integer, turns: integer, state: { enum: ['idle', 'running', 'completed', 'failed', 'stopped', 'unknown'] }, lastAt: text,
|
|
200
203
|
backend: text, agentTitle: { anyOf: [text, { type: 'null' }] }, adapter: { anyOf: [response({ name: text, version: text }, ['name']), { type: 'null' }] }, model: { anyOf: [text, { type: 'null' }] }, config: choices, turnLimit: { type: 'boolean' }, waitingPermission: { type: 'boolean' },
|
|
201
204
|
usage: { anyOf: [response({ used: { type: 'number', minimum: 0 }, size: { type: 'number', minimum: 0 }, cost: { anyOf: [response({ amount: { type: 'number' }, currency: text }), { type: 'null' }] } }), { type: 'null' }] },
|
|
202
|
-
costUsd: { anyOf: [{ type: 'number' }, { type: 'null' }] }, sessionId: { anyOf: [text, { type: 'null' }] }, trashedAt: { anyOf: [text, { type: 'null' }] }
|
|
205
|
+
costUsd: { anyOf: [{ type: 'number' }, { type: 'null' }] }, sessionId: { anyOf: [text, { type: 'null' }] }, trashedAt: { anyOf: [text, { type: 'null' }] },
|
|
206
|
+
origin: { anyOf: [response({ producer: text, where: text, copyOf: text }, ['producer', 'where']), { type: 'null' }] } }, ['conversationId', 'title', 'client', 'principal', 'createdAt', 'updatedAt', 'version', 'turns', 'state', 'lastAt']);
|
|
203
207
|
const sessionObservation = ownerSchema('agent-session', 'sessionObservationSchema');
|
|
204
208
|
// What one Agent offered, from a check or a conversation's first session, and the person's choices.
|
|
205
209
|
const offered = response({ at: text, source: { enum: ['check', 'conversation'] }, adapter: { anyOf: [response({ name: text, version: text }, ['name']), { type: 'null' }] }, options: array(ownerSchema('agent-session', 'configOptionSchema')), omitted: integer });
|
|
206
210
|
const agentSettings = response({ version: integer, updatedAt: { anyOf: [text, { type: 'null' }] }, backend: { anyOf: [agentBackend, { type: 'null' }] }, serviceBackend: { enum: ['claude-acp', 'codex-acp', 'claude-cli', 'acp'] },
|
|
207
211
|
choices: response({ 'claude-acp': choices, 'codex-acp': choices, 'acp': choices }, []), offered: response({ 'claude-acp': offered, 'codex-acp': offered, 'acp': offered }, []), checking: array(agentBackend),
|
|
208
212
|
// Whether a model describes the files a message writes; the facts are read either way.
|
|
209
|
-
describe: response({ enabled: { type: 'boolean' } })
|
|
213
|
+
describe: response({ enabled: { type: 'boolean' } }),
|
|
214
|
+
// Which Agents this service can start, without starting them.
|
|
215
|
+
agents: response(Object.fromEntries(['claude-acp', 'codex-acp', 'acp'].map(k => [k, response({ found: { type: 'boolean' }, problem: text, name: { anyOf: [text, { type: 'null' }] } }, ['found'])]))) }, ['version', 'updatedAt', 'backend', 'serviceBackend', 'choices', 'offered', 'checking']);
|
|
210
216
|
// A tool step keeps its original identity, the Agent's bounded human title, its ACP
|
|
211
217
|
// category and one identifying input (command, path, pattern or query), for a command the
|
|
212
218
|
// Agent's own few words on what it is for (purpose), when it was
|
|
@@ -274,7 +280,7 @@ observationSnapshot.required = observationSnapshot.required.filter(key => key !=
|
|
|
274
280
|
const structuredComposition = contract.structuredCompositionSchema;
|
|
275
281
|
const composition = { anyOf: [contract.legacyCompositionSchema, structuredComposition] };
|
|
276
282
|
const taskMap = { ...contract.taskMapStateSchema, properties: { ...contract.taskMapStateSchema.properties, receipt: obj({ changeId: identity, requestDigest: digest, kind: { enum: ['declare', 'present', 'respond'] }, author: text, version: positive, at: { type: 'string', format: 'date-time' }, outcome: contract.taskMapReceiptField }, ['changeId', 'kind', 'author', 'version', 'at']) } };
|
|
277
|
-
export const responses = { ...base.responses, ...ownerResponses,
|
|
283
|
+
export const responses = { ...base.responses, ...ownerResponses, ...extensionResponses,
|
|
278
284
|
'project.work.read': projectWorkSchema(sessionObservation),
|
|
279
285
|
'project.open': { ...contract.projectRecordSchema, properties: { ...contract.projectRecordSchema.properties, host: contract.cliSchema }, required: [...contract.projectRecordSchema.required, 'host'] },
|
|
280
286
|
'project.pick': response({ cancelled: { type: 'boolean' }, project: { anyOf: [contract.projectSummarySchema, { type: 'null' }] } }),
|
|
@@ -332,6 +338,7 @@ export const responses = { ...base.responses, ...ownerResponses,
|
|
|
332
338
|
'conversation.settings.update': agentSettings,
|
|
333
339
|
'conversation.settings.check': agentSettings,
|
|
334
340
|
'session.connect': response({ principal: text, label: text, methods: integer, granted: { type: 'boolean' }, added: addedTo }),
|
|
341
|
+
'session.instructions': response({ request: text, prompt: text, command: array(text) }),
|
|
335
342
|
'protocol.describe': response({ apiVersion: { const: '2.0' }, schemaVersion: { const: 2 }, revision: digest, entries: array(response({ method: text, summary: text })), next: { type: ['string', 'null'] }, complete: { type: 'boolean' }, contract: response({ method: text, inputSchema: { type: 'object' }, outputSchema: { type: 'object' }, definitions: { type: 'object' }, description: text }) }, ['apiVersion', 'schemaVersion', 'revision', 'entries', 'next', 'complete']),
|
|
336
343
|
'capability.list': { ...catalog, properties: { ...catalog.properties, host: contract.cliSchema }, required: [...(catalog.required ?? []), 'host'] },
|
|
337
344
|
'capability.configure': response({ version: positive, enabled: array(text), receipt: response({ changeId: text, author: text, version: positive, enabled: array(text), at: text }) }),
|
|
@@ -374,6 +381,7 @@ export const responses = { ...base.responses, ...ownerResponses,
|
|
|
374
381
|
};
|
|
375
382
|
responses['session.grant'].properties.profile = operations['session.grant'].properties.profile;
|
|
376
383
|
export const descriptions = {
|
|
384
|
+
...extensionDescriptions,
|
|
377
385
|
'workspace.observe': 'Assigned presenter only: bounded layout/basis/owner summary/visibility projection. No feedback, full documents or native trace. Reading starts no model or renderer.',
|
|
378
386
|
'workspace.enhance': 'Assigned presenter only: conditional display emphasis/title/plain Agent note using exact current generation, witness and observation. Does not modify business resources, feedback or protection.',
|
|
379
387
|
'capability.index': 'Read a small role-narrowed index of currently authorized, enabled operations and immutable Skill/contract references.',
|
|
@@ -393,7 +401,7 @@ export const descriptions = {
|
|
|
393
401
|
'action.submit': 'Execute the explicitly selected project R script snapshot and requirements. Persist operationId before delivery; identical retries retain the original Core identity. Output directory is the first R argument, followed by explicit args or frozen resource paths. Artifact paths are output-relative; completion is run-directory-relative, returned as plan.completion, so an inventory can remain beside immutable producer artifacts.',
|
|
394
402
|
'binding.activity.append': 'Registered native adapter only: retain original CLI tool and artifact-ready observations. This does not execute, validate, or present artifacts.',
|
|
395
403
|
'task.routing.put': 'Control plane only: conditionally assign one task and business binding to this project without starting a native session.',
|
|
396
|
-
'conversation.start': 'Person at this machine only: start a conversation with the selected Claude Code or Codex Agent in this project. Rho selects the Agent plugin backend, attaches this project\'s MCP grant and records the message before sending it. Without backend or config, the person\'s saved Agent and settings are used and recorded with the conversation. A repeated changeId with the same text returns the original conversation and starts nothing.',
|
|
404
|
+
'conversation.start': 'Person at this machine only: start a conversation with the selected Claude Code or Codex Agent in this project. Rho selects the Agent plugin backend, attaches this project\'s MCP grant and records the message before sending it. Without backend or config, the person\'s saved Agent and settings are used and recorded with the conversation. A repeated changeId with the same text returns the original conversation and starts nothing. With continue, the message continues an Agent session listed by agent.sessions.list (one started in a terminal, an editor or the Agent\'s own app) through that same session, or through a copy of it with copy; a session another live window holds open is refused unless copied.',
|
|
397
405
|
'conversation.send': 'Person at this machine only: send the next message to a conversation started in Rho. A continuationOf links a new message to the latest confirmed Agent turn-limit stop; the original request is not resent. Refused while the previous message is being answered. A repeated changeId with the same input returns the original message.',
|
|
398
406
|
'conversation.decide': 'Person at this machine only: say to use an answer, or that it needs changes and why. The decision holds for the file versions that message kept when it was given and says nothing of later versions. A new decision supersedes the one before; none is removed. Refused while the Agent is still answering.',
|
|
399
407
|
'conversation.stop': 'Person at this machine only: ask the original Agent to stop answering one message. ACP cancellation is confirmed by the original prompt response; a lost connection keeps the outcome unknown. Legacy CLI conversations confirm process exit. Steps already taken stay done.',
|
|
@@ -430,6 +438,7 @@ export const descriptions = {
|
|
|
430
438
|
'conversation.settings.update': 'Person at this machine only: choose the Agent and its settings for new conversations, among the values that Agent last offered, and whether a model describes the files a message writes. A stale expectedVersion is refused.',
|
|
431
439
|
'conversation.settings.check': 'Person at this machine only: start the chosen Agent in this project to read the settings it offers, then remove that session and end the process. No message is sent; one check per Agent runs at a time.',
|
|
432
440
|
'session.connect': 'Person at this machine only: give Claude Code or Codex running outside Rho access to this project, then add Rho\'s MCP server with that Agent\'s own `mcp add` (Claude Code: for this project folder; Codex: in its user configuration). A grant whose connection still reaches this service is kept, so a connected session keeps working; a new grant the Agent refuses is removed. No message is sent and no Agent session starts.',
|
|
441
|
+
'session.instructions': 'Person at this machine or operator only: read a prompt for the person\'s own MCP client to connect itself to this existing project. Returns local launcher paths, never credential contents. Reading or copying grants no access, edits no client configuration and starts no runtime.',
|
|
433
442
|
'conversation.read': 'Read a conversation started in Rho: the person\'s messages, the Agent\'s visible text and tool steps, and how each message ended. Reading starts and resumes nothing.',
|
|
434
443
|
'project.work.read': 'Person at this machine only: read linked Agent sessions, retained native activity identities, current referenced files and the shared notes document. Source cursors and resource versions remain independent. Does not start an Agent, collector or Host.',
|
|
435
444
|
'resource.register': 'Declare scoped existing bytes and their type/schema at the supplied digest. This read and registration starts no runtime.',
|
package/dist/server.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Extensions, extensionLimits } from './extensions.js';
|
|
1
2
|
import { evidenceExportLimits } from './evidence-export.js';
|
|
2
3
|
import * as http from 'node:http';
|
|
3
4
|
import { Plugins, type PluginConfig } from './plugins.js';
|
|
@@ -49,6 +50,7 @@ export declare const serviceLimits: {
|
|
|
49
50
|
export interface ServiceConfig extends Partial<typeof serviceLimits>, Partial<typeof requestLimits>, PluginConfig {
|
|
50
51
|
root: string;
|
|
51
52
|
stateDir: string;
|
|
53
|
+
extensions?: Partial<typeof extensionLimits>;
|
|
52
54
|
coreBinary?: string;
|
|
53
55
|
rscript: string;
|
|
54
56
|
python?: string;
|
|
@@ -93,6 +95,7 @@ export declare const layeredGrantLimits: {
|
|
|
93
95
|
};
|
|
94
96
|
export declare class RhoService {
|
|
95
97
|
config: ServiceConfig;
|
|
98
|
+
extensions: Extensions;
|
|
96
99
|
plugins: Plugins;
|
|
97
100
|
state: State;
|
|
98
101
|
bridge: CoreBridge;
|
|
@@ -165,22 +168,14 @@ export declare class RhoService {
|
|
|
165
168
|
agentConnection(projectId: string, externalSessionId: string, file: string, methods?: string[], pluginFilter?: string[], profile?: 'layered-business' | 'layered-presentation', who?: {
|
|
166
169
|
label?: string;
|
|
167
170
|
conversationId?: string;
|
|
168
|
-
}): Promise<
|
|
169
|
-
apiVersion: "2.0";
|
|
170
|
-
pluginFilter?: ("media" | "r" | "agent" | "files" | "documents" | "requirements" | "tables" | "charts" | "annotations" | "console" | "kernel" | "skills")[] | undefined;
|
|
171
|
-
profile?: "layered-business" | "layered-presentation" | undefined;
|
|
172
|
-
url: string;
|
|
173
|
-
token: string;
|
|
174
|
-
projectId: string;
|
|
175
|
-
externalSessionId: string;
|
|
176
|
-
methods: string[];
|
|
177
|
-
}>;
|
|
171
|
+
}): Promise<any>;
|
|
178
172
|
private authenticate;
|
|
179
173
|
private handle;
|
|
180
174
|
private readBody;
|
|
181
175
|
private json;
|
|
182
176
|
private serial;
|
|
183
177
|
private recordActivity;
|
|
178
|
+
private agentSessions;
|
|
184
179
|
private projectActivity;
|
|
185
180
|
private agentSummaries;
|
|
186
181
|
private dataDir;
|