@remits/remits-cli 0.1.81 → 0.1.83
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/index.js +121 -51
- package/package.json +1 -1
- package/skills/remits-cli/SKILL.md +93 -64
package/index.js
CHANGED
|
@@ -2810,36 +2810,78 @@ function truncateText(value, maxChars = 20000) {
|
|
|
2810
2810
|
return text.slice(0, maxChars) + '\n...[truncated ' + (text.length - maxChars) + ' chars]';
|
|
2811
2811
|
}
|
|
2812
2812
|
|
|
2813
|
+
const DASHBOARD_FILE_PREVIEW_BYTES = 512 * 1024;
|
|
2814
|
+
|
|
2813
2815
|
function readJsonFilePretty(file) {
|
|
2814
2816
|
if (!fs.existsSync(file)) {
|
|
2815
2817
|
return null;
|
|
2816
2818
|
}
|
|
2819
|
+
const stat = fileStat(file);
|
|
2820
|
+
if (stat && stat.size > DASHBOARD_FILE_PREVIEW_BYTES) {
|
|
2821
|
+
const tail = tailTextFile(file, 80);
|
|
2822
|
+
return tail === null
|
|
2823
|
+
? null
|
|
2824
|
+
: '...[large file; showing tail only]\n' + tail;
|
|
2825
|
+
}
|
|
2817
2826
|
try {
|
|
2818
|
-
return JSON.stringify(JSON.parse(fs.readFileSync(file, 'utf8')), null, 2);
|
|
2827
|
+
return truncateText(JSON.stringify(JSON.parse(fs.readFileSync(file, 'utf8')), null, 2));
|
|
2819
2828
|
} catch (_) {
|
|
2820
2829
|
return truncateText(fs.readFileSync(file, 'utf8'));
|
|
2821
2830
|
}
|
|
2822
2831
|
}
|
|
2823
2832
|
|
|
2824
|
-
function
|
|
2833
|
+
function readTextTail(file, maxBytes = 512 * 1024) {
|
|
2825
2834
|
if (!fs.existsSync(file)) {
|
|
2835
|
+
return { text: null, truncated: false };
|
|
2836
|
+
}
|
|
2837
|
+
let fd = null;
|
|
2838
|
+
try {
|
|
2839
|
+
const stat = fs.statSync(file);
|
|
2840
|
+
const bytesToRead = Math.min(stat.size, maxBytes);
|
|
2841
|
+
const start = Math.max(0, stat.size - bytesToRead);
|
|
2842
|
+
const buffer = Buffer.allocUnsafe(bytesToRead);
|
|
2843
|
+
fd = fs.openSync(file, 'r');
|
|
2844
|
+
const bytesRead = fs.readSync(fd, buffer, 0, bytesToRead, start);
|
|
2845
|
+
return {
|
|
2846
|
+
text: buffer.subarray(0, bytesRead).toString('utf8'),
|
|
2847
|
+
truncated: start > 0
|
|
2848
|
+
};
|
|
2849
|
+
} catch (_) {
|
|
2850
|
+
return { text: null, truncated: false };
|
|
2851
|
+
} finally {
|
|
2852
|
+
if (fd !== null) {
|
|
2853
|
+
try { fs.closeSync(fd); } catch (_) {}
|
|
2854
|
+
}
|
|
2855
|
+
}
|
|
2856
|
+
}
|
|
2857
|
+
|
|
2858
|
+
function tailTextFile(file, maxLines = 80) {
|
|
2859
|
+
const tail = readTextTail(file);
|
|
2860
|
+
if (tail.text === null) {
|
|
2826
2861
|
return null;
|
|
2827
2862
|
}
|
|
2828
|
-
const
|
|
2829
|
-
const
|
|
2830
|
-
|
|
2863
|
+
const lines = tail.text.split('\n');
|
|
2864
|
+
const selected = lines.slice(-maxLines);
|
|
2865
|
+
if (tail.truncated && selected.length) {
|
|
2866
|
+
selected.unshift('...[earlier log content omitted]');
|
|
2867
|
+
}
|
|
2868
|
+
return truncateText(selected.join('\n'));
|
|
2831
2869
|
}
|
|
2832
2870
|
|
|
2833
2871
|
function readJsonLinesFile(file, maxEntries = 80) {
|
|
2834
|
-
if (!file
|
|
2872
|
+
if (!file) {
|
|
2835
2873
|
return [];
|
|
2836
2874
|
}
|
|
2837
2875
|
try {
|
|
2838
|
-
const
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2876
|
+
const tail = readTextTail(file, 1024 * 1024);
|
|
2877
|
+
if (tail.text === null) {
|
|
2878
|
+
return [];
|
|
2879
|
+
}
|
|
2880
|
+
let lines = tail.text.split('\n');
|
|
2881
|
+
if (tail.truncated && lines.length) {
|
|
2882
|
+
lines = lines.slice(1);
|
|
2883
|
+
}
|
|
2884
|
+
lines = lines.map((line) => line.trim()).filter(Boolean).slice(-maxEntries);
|
|
2843
2885
|
return lines.map((line) => {
|
|
2844
2886
|
try {
|
|
2845
2887
|
return JSON.parse(line);
|
|
@@ -3313,12 +3355,32 @@ async function reconnectManagedWebsockets() {
|
|
|
3313
3355
|
|
|
3314
3356
|
function startDashboardServer(preferredPort) {
|
|
3315
3357
|
return new Promise((resolve, reject) => {
|
|
3358
|
+
const sendDashboardError = (res, err, wantsJson = true) => {
|
|
3359
|
+
const message = describeError(err);
|
|
3360
|
+
appendGlobalActivityLog('dashboard', 'request.failed', { error: message }, 'error');
|
|
3361
|
+
if (res.headersSent) {
|
|
3362
|
+
res.end('');
|
|
3363
|
+
return;
|
|
3364
|
+
}
|
|
3365
|
+
res.statusCode = 500;
|
|
3366
|
+
if (wantsJson) {
|
|
3367
|
+
res.setHeader('Content-Type', 'application/json');
|
|
3368
|
+
res.end(JSON.stringify({ success: false, message }));
|
|
3369
|
+
} else {
|
|
3370
|
+
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
|
|
3371
|
+
res.end('Dashboard error: ' + message);
|
|
3372
|
+
}
|
|
3373
|
+
};
|
|
3316
3374
|
const server = http.createServer(async (req, res) => {
|
|
3317
3375
|
const requestUrl = new URL(req.url, 'http://' + DASHBOARD_HOST + ':' + (runtimeState.service.dashboardPort || preferredPort));
|
|
3318
3376
|
if (req.method === 'GET' && requestUrl.pathname === '/api/state') {
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3377
|
+
try {
|
|
3378
|
+
res.statusCode = 200;
|
|
3379
|
+
res.setHeader('Content-Type', 'application/json');
|
|
3380
|
+
res.end(JSON.stringify(collectDashboardSnapshot(), null, 2));
|
|
3381
|
+
} catch (err) {
|
|
3382
|
+
sendDashboardError(res, err, true);
|
|
3383
|
+
}
|
|
3322
3384
|
return;
|
|
3323
3385
|
}
|
|
3324
3386
|
|
|
@@ -3328,51 +3390,59 @@ function startDashboardServer(preferredPort) {
|
|
|
3328
3390
|
body += chunk.toString();
|
|
3329
3391
|
});
|
|
3330
3392
|
req.on('end', async () => {
|
|
3331
|
-
const params = new URLSearchParams(body);
|
|
3332
|
-
const action = params.get('action');
|
|
3333
|
-
let actionResult = null;
|
|
3334
|
-
runtimeState.lastAction = {
|
|
3335
|
-
action,
|
|
3336
|
-
at: new Date().toISOString()
|
|
3337
|
-
};
|
|
3338
|
-
if (action === 'export-issues') {
|
|
3339
|
-
const bundlePath = exportIssueBundle(collectDashboardSnapshot());
|
|
3340
|
-
actionResult = { bundlePath };
|
|
3341
|
-
runtimeState.lastAction.bundlePath = bundlePath;
|
|
3342
|
-
} else if (action === 'rescan-repos') {
|
|
3343
|
-
recordDiscoverySummary(discoverAccountRepos());
|
|
3344
|
-
} else if (action === 'open-tmux') {
|
|
3345
|
-
openTmuxSessionForUser();
|
|
3346
|
-
} else if (action === 'start-tmux') {
|
|
3347
|
-
ensureTmuxSession(os.homedir());
|
|
3348
|
-
} else if (action === 'stop-tmux') {
|
|
3349
|
-
killTmuxSession();
|
|
3350
|
-
} else if (action === 'reconnect-websockets') {
|
|
3351
|
-
await reconnectManagedWebsockets();
|
|
3352
|
-
}
|
|
3353
3393
|
const wantsJson = String(req.headers.accept || '').includes('application/json');
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3394
|
+
try {
|
|
3395
|
+
const params = new URLSearchParams(body);
|
|
3396
|
+
const action = params.get('action');
|
|
3397
|
+
let actionResult = null;
|
|
3398
|
+
runtimeState.lastAction = {
|
|
3359
3399
|
action,
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3400
|
+
at: new Date().toISOString()
|
|
3401
|
+
};
|
|
3402
|
+
if (action === 'export-issues') {
|
|
3403
|
+
const bundlePath = exportIssueBundle(collectDashboardSnapshot());
|
|
3404
|
+
actionResult = { bundlePath };
|
|
3405
|
+
runtimeState.lastAction.bundlePath = bundlePath;
|
|
3406
|
+
} else if (action === 'rescan-repos') {
|
|
3407
|
+
recordDiscoverySummary(discoverAccountRepos());
|
|
3408
|
+
} else if (action === 'open-tmux') {
|
|
3409
|
+
openTmuxSessionForUser();
|
|
3410
|
+
} else if (action === 'start-tmux') {
|
|
3411
|
+
ensureTmuxSession(os.homedir());
|
|
3412
|
+
} else if (action === 'stop-tmux') {
|
|
3413
|
+
killTmuxSession();
|
|
3414
|
+
} else if (action === 'reconnect-websockets') {
|
|
3415
|
+
await reconnectManagedWebsockets();
|
|
3416
|
+
}
|
|
3417
|
+
if (wantsJson) {
|
|
3418
|
+
res.statusCode = 200;
|
|
3419
|
+
res.setHeader('Content-Type', 'application/json');
|
|
3420
|
+
res.end(JSON.stringify({
|
|
3421
|
+
success: true,
|
|
3422
|
+
action,
|
|
3423
|
+
result: actionResult,
|
|
3424
|
+
state: collectDashboardSnapshot()
|
|
3425
|
+
}));
|
|
3426
|
+
return;
|
|
3427
|
+
}
|
|
3428
|
+
res.statusCode = 302;
|
|
3429
|
+
res.setHeader('Location', '/');
|
|
3430
|
+
res.end('');
|
|
3431
|
+
} catch (err) {
|
|
3432
|
+
sendDashboardError(res, err, wantsJson);
|
|
3364
3433
|
}
|
|
3365
|
-
res.statusCode = 302;
|
|
3366
|
-
res.setHeader('Location', '/');
|
|
3367
|
-
res.end('');
|
|
3368
3434
|
});
|
|
3369
3435
|
return;
|
|
3370
3436
|
}
|
|
3371
3437
|
|
|
3372
3438
|
if (req.method === 'GET' && requestUrl.pathname === '/') {
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3439
|
+
try {
|
|
3440
|
+
res.statusCode = 200;
|
|
3441
|
+
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
3442
|
+
res.end(renderDashboardHtml(collectDashboardSnapshot()));
|
|
3443
|
+
} catch (err) {
|
|
3444
|
+
sendDashboardError(res, err, false);
|
|
3445
|
+
}
|
|
3376
3446
|
return;
|
|
3377
3447
|
}
|
|
3378
3448
|
|
package/package.json
CHANGED
|
@@ -546,10 +546,14 @@ The goal of an investigation is not only to find a suspicious record. It is to e
|
|
|
546
546
|
|
|
547
547
|
### AI Session Groupings
|
|
548
548
|
|
|
549
|
-
- For front-stage investigation and tuning work, use `mcp_ai_session_search` to search persisted AI session groupings and
|
|
549
|
+
- For front-stage investigation and tuning work, use `mcp_ai_session_search` to search persisted AI session groupings and audit grouping detail. A grouping spans every related session that shares one grouping ID — the agent turns plus its guardrail and internal `ai()` calls.
|
|
550
550
|
- This tool mirrors the admin AI Groupings UI:
|
|
551
|
-
- search filters: `search`, `sessionId`, `groupingId`, `user`, `account`, `agent`, `scope`
|
|
552
|
-
- detail
|
|
551
|
+
- search filters: `search`, `sessionId`, `groupingId`, `user`, `account`, `agent`, `scope`, `status`
|
|
552
|
+
- detail parts: `index`, `stats`, `transcript`, `tool_calls`, plus the record sections `full`, `conversation_messages`, `system`, `response`, `tools`
|
|
553
|
+
- Audit a grouping with a **map → open** workflow, not a whole-detail dump (which re-sends the same cumulative prompt on every record and burns tokens):
|
|
554
|
+
1. `action:"detail"` + `summaryOnly:true` (or `parts:["index"]`) → the MAP, no payloads: a per-request record list (id, callType, agent, model, tokens, raw `finishReason`, `toolCallsRequested`, `responsePreview`) + a timeline of interleaved tool-call ids. Same rows the admin grouping detail shows.
|
|
555
|
+
2. Then open only what you need: `recordIds:[<id>]` + `parts:["conversation_messages"|"system"|"response"|"tools"|"full"]` for one record; `parts:["transcript"]` for the deduplicated end-to-end conversation (each message once, in order); `parts:["tool_calls"]` (then `toolCallIds:[<id>]`) for the exact input/result a tool produced.
|
|
556
|
+
- **Tool-result lens — do not conflate the two:** `parts:["tool_calls"]`/`toolCallIds` returns what a tool **PRODUCED** (the exact `ai_tool_call` result). That is **not** necessarily what the model saw next turn — front-stage control keys (`_offload`/`_offloadSynopsis`, `_hideResult`, `_message`, `_supersedes`/`_evict*`, `_stop`, …) can offload, hide, summarize, or collapse the re-provided value. To see what the AI **CONSUMED**, read the tool_result inside `conversation_messages`/`transcript`. (`resultChars` in the tool_calls index flags the large results most likely to have been transformed.)
|
|
553
557
|
- Use it when you need to understand:
|
|
554
558
|
- what prompts, system instructions, tools, and responses were actually sent for a session
|
|
555
559
|
- how an Agent behaved across one grouping, including prompt-tuning and tool-usage opportunities
|
|
@@ -618,44 +622,57 @@ Do not assume `--data-mode prod` implies the deployed prod host, or that `--data
|
|
|
618
622
|
|
|
619
623
|
### Tool Execution Lifecycle
|
|
620
624
|
|
|
621
|
-
`remits-cli tool` supports both synchronous and asynchronous execution.
|
|
622
|
-
|
|
623
|
-
Use normal synchronous execution for quick investigation tools:
|
|
625
|
+
`remits-cli tool` supports both synchronous and asynchronous execution. Use normal synchronous execution
|
|
626
|
+
for quick investigation tools:
|
|
624
627
|
|
|
625
628
|
```bash
|
|
626
629
|
remits-cli tool --name "mcp_account_view" --input '{"accountId": 37}' --data-mode prod
|
|
627
630
|
```
|
|
628
631
|
|
|
629
|
-
|
|
630
|
-
AI calls, document writes, or multi-step workflows:
|
|
632
|
+
**There are two independent async mechanisms — do not confuse or stack them:**
|
|
631
633
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
634
|
+
1. **The tool's own async mode** (`mcp_run_action` / `mcp_run_agent`, via `executionMode:"async"` in the
|
|
635
|
+
tool input). The tool spawns the long work server-side and **returns immediately in the same HTTP
|
|
636
|
+
response** with its own run identifiers — `actionRunId` (or `agentRunId`) and, for agents, a stable
|
|
637
|
+
`sessionId`. You poll it with the tool's **own** status protocol (`controlAction:"status"`). This is the
|
|
638
|
+
preferred path for long Actions/Agents, because the run ids come back on the very first call.
|
|
635
639
|
|
|
636
|
-
|
|
637
|
-
|
|
640
|
+
2. **The CLI transport async** (`--async true`). This wraps *any* tool call in a background server task and
|
|
641
|
+
returns a CLI-level `callId` immediately, which you poll with `remits-cli tool status --call-id`. Use it
|
|
642
|
+
for long tools that do **not** have their own async mode. Its start response carries `callId`,
|
|
643
|
+
`status:"running"`, `threadGroupingId`, `accountId`, `branchName`, and `dataMode` — but **not** any
|
|
644
|
+
tool-specific ids, because the tool has not run yet; those arrive inside the `result` of the polled
|
|
645
|
+
completed status.
|
|
638
646
|
|
|
639
|
-
|
|
640
|
-
|
|
647
|
+
For `mcp_run_action` / `mcp_run_agent`, prefer mechanism (1) alone — it already makes the call non-blocking
|
|
648
|
+
**and** returns the run ids up front. Pass your own `actionRunId`/`agentRunId` so you can poll it
|
|
649
|
+
deterministically:
|
|
650
|
+
|
|
651
|
+
```bash
|
|
652
|
+
remits-cli tool --name "mcp_run_action" --input '{"accountId":49,"actionId":200,"executionMode":"async","actionRunId":"my-stable-run-id","actionInput":{"sourceDocumentId":"..."}}' --data-mode prod
|
|
641
653
|
```
|
|
642
654
|
|
|
643
|
-
|
|
655
|
+
Every tool response is saved to `./.remits-cli/tool-responses/<callId>.json`.
|
|
656
|
+
|
|
657
|
+
Poll a CLI-transport async call (mechanism 2) by call id:
|
|
644
658
|
|
|
645
659
|
```bash
|
|
646
660
|
remits-cli tool status --call-id <callId> --data-mode prod
|
|
647
661
|
```
|
|
648
662
|
|
|
649
|
-
|
|
650
|
-
|
|
663
|
+
Pass `--wait true` to have the CLI process poll locally until the transport call completes (short polling
|
|
664
|
+
requests instead of one long HTTP connection):
|
|
651
665
|
|
|
652
666
|
```bash
|
|
653
|
-
remits-cli tool --name "
|
|
667
|
+
remits-cli tool --name "some_long_tool_without_its_own_async" --async true --wait true --input '{...}' --data-mode prod
|
|
654
668
|
```
|
|
655
669
|
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
670
|
+
Stacking both (`--async true` **and** `executionMode:"async"`) works but is redundant: the tool's
|
|
671
|
+
`actionRunId`/`agentRunId`/`sessionId` then appear only in the polled completed `result`, not in the CLI
|
|
672
|
+
start response — which is why the start response looks "incomplete." Pick one mechanism.
|
|
673
|
+
|
|
674
|
+
`--timeout-ms <ms>` controls the per-request HTTP timeout. Prefer async execution over a large timeout for
|
|
675
|
+
multi-minute work so the server task is not tied to one HTTP connection.
|
|
659
676
|
|
|
660
677
|
### Data Mode
|
|
661
678
|
|
|
@@ -760,11 +777,13 @@ For changes to Readers, Actions, or Rules that process data rather than display
|
|
|
760
777
|
remits-cli tool --name "mcp_firestore_search" --input '{"accountId": <ID>, "collection": "<collection>", "limit": 5, "sort": [{"field": "_lastModifiedAt", "direction": "DESC"}]}'
|
|
761
778
|
```
|
|
762
779
|
|
|
763
|
-
For long-running backend verification,
|
|
764
|
-
a single request
|
|
780
|
+
For long-running backend verification, use the Action runner's own async mode (`executionMode:"async"`) and
|
|
781
|
+
poll by `actionRunId` rather than holding a single request open (see "Tool Execution Lifecycle" for why not
|
|
782
|
+
to also stack the CLI `--async` flag):
|
|
765
783
|
|
|
766
784
|
```bash
|
|
767
|
-
remits-cli tool --name "mcp_run_action" --
|
|
785
|
+
remits-cli tool --name "mcp_run_action" --input '{"accountId": <ID>, "actionId": <ACTION_ID>, "executionMode": "async", "actionInput": {...}}' --data-mode test
|
|
786
|
+
# then poll: {"controlAction":"status","accountId": <ID>, "actionRunId":"<actionRunId>"}
|
|
768
787
|
```
|
|
769
788
|
|
|
770
789
|
#### Step 5: Iterate If Needed
|
|
@@ -1045,16 +1064,18 @@ remits-cli tool --name "mcp_firestore_search" --input '{"accountId": 37, "collec
|
|
|
1045
1064
|
|
|
1046
1065
|
Response saved to `./.remits-cli/tool-responses/<callId>.json`. Read the file to see results.
|
|
1047
1066
|
|
|
1048
|
-
For long-running
|
|
1067
|
+
For long-running Action/Agent runners, use the tool's own async mode (`executionMode:"async"`), which returns
|
|
1068
|
+
the `actionRunId`/`agentRunId` (and, for agents, `sessionId`) immediately:
|
|
1049
1069
|
|
|
1050
1070
|
```bash
|
|
1051
|
-
remits-cli tool --name "mcp_run_action" --
|
|
1052
|
-
|
|
1071
|
+
remits-cli tool --name "mcp_run_action" --input '{"accountId":37,"actionName":"Rebuild Invoice","executionMode":"async","actionInput":{"invoiceId":"abc"}}' --data-mode prod
|
|
1072
|
+
# poll by run id: {"controlAction":"status","accountId":37,"actionRunId":"<actionRunId>"}
|
|
1053
1073
|
```
|
|
1054
1074
|
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1075
|
+
For long tools that lack their own async mode, use the CLI transport async (`--async true`), optionally with
|
|
1076
|
+
`--wait true` to poll locally, and `remits-cli tool status --call-id <callId>`. Do not stack both mechanisms
|
|
1077
|
+
(see "Tool Execution Lifecycle"). Use `--timeout-ms <ms>` only to adjust the per-request client timeout; it is
|
|
1078
|
+
not a replacement for async mode on multi-minute workflows.
|
|
1058
1079
|
|
|
1059
1080
|
**Account-id precedence for tool calls.** When the CLI and the tool input both carry an account id, the server resolves them in this order:
|
|
1060
1081
|
|
|
@@ -1170,7 +1191,7 @@ Front-stage references:
|
|
|
1170
1191
|
|
|
1171
1192
|
| Parameter | Required | Description |
|
|
1172
1193
|
|-----------|----------|-------------|
|
|
1173
|
-
| `action` | no | `search` (default) or `
|
|
1194
|
+
| `action` | no | `search` (default), `detail`, or session control `pause`/`unpause`/`interrupt` |
|
|
1174
1195
|
| `search` | no | Broad text match against session IDs and grouping IDs |
|
|
1175
1196
|
| `sessionId` | no | Session ID filter in search mode, or grouping/session key in detail mode |
|
|
1176
1197
|
| `groupingId` | no | Grouping ID filter in search mode, or grouping key in detail mode |
|
|
@@ -1179,14 +1200,18 @@ Front-stage references:
|
|
|
1179
1200
|
| `account` | no | Account filter |
|
|
1180
1201
|
| `agent` | no | Agent filter |
|
|
1181
1202
|
| `scope` | no | `all`, `agents`, or `internal` |
|
|
1203
|
+
| `status` | no | Search mode: filter by live runtime status, comma-separated (e.g. `paused,interrupted`) |
|
|
1204
|
+
| `scanLimit` | no | Search mode: window scanned when `status` is set. Default `100`, max `500` |
|
|
1182
1205
|
| `page` | no | 1-based page number. Default: `1` |
|
|
1183
1206
|
| `pageSize` | no | Results per page. Default: `25`, max: `100` |
|
|
1184
|
-
| `
|
|
1185
|
-
| `parts` | no |
|
|
1207
|
+
| `summaryOnly` | no | Detail mode: return the MAP (record index + stats + timeline) with no payloads. Same as `parts:["index"]` |
|
|
1208
|
+
| `parts` | no | Detail parts: `index`, `stats`, `transcript`, `tool_calls`, and record sections `full`, `conversation_messages`, `system`, `response`, `tools` |
|
|
1186
1209
|
| `sections` | no | Alias for `parts` |
|
|
1210
|
+
| `recordIds` | no | Detail mode: open only these request/response record IDs |
|
|
1211
|
+
| `toolCallIds` | no | Detail mode: return the FULL exact input/result from `ai_tool_call` for these tool-call ids (what the tool PRODUCED — see the lens caveat) |
|
|
1187
1212
|
| `consolidateContext` | no | When `true`, collapses repeated XML-like prompt context into a consolidated section |
|
|
1188
1213
|
|
|
1189
|
-
|
|
1214
|
+
Audit flow: `action:"search"` to find the grouping → `action:"detail"` + `summaryOnly:true` for the MAP → re-call detail with `recordIds`/`toolCallIds` + `parts` to open exactly what you need. Prefer the map → open flow over a full-detail dump. Remember the two-lens rule: `tool_calls`/`toolCallIds` is what the tool PRODUCED; `conversation_messages`/`transcript` is what the AI CONSUMED (after any `_offload`/`_hideResult`/`_message`/supersede/evict transform).
|
|
1190
1215
|
|
|
1191
1216
|
### `mcp_run_action`
|
|
1192
1217
|
Run an Action on a target account, with explicit prod/test data mode, optional staged branch resolution, and
|
|
@@ -1198,33 +1223,39 @@ Use direct mode only for quick Actions:
|
|
|
1198
1223
|
remits-cli tool --name mcp_run_action --input '{"accountId":49,"actionId":200,"executionMode":"direct","actionInput":{"sourceDocumentId":"..."}}' --data-mode prod
|
|
1199
1224
|
```
|
|
1200
1225
|
|
|
1201
|
-
Use async mode for long-running
|
|
1226
|
+
Use the tool's own async mode for long-running Action execution — it returns immediately with an
|
|
1227
|
+
`actionRunId`. Pass your **own** `actionRunId` so you can poll deterministically without first parsing it out
|
|
1228
|
+
of the start response. Do **not** also pass the CLI `--async` flag; that only buries these ids behind the
|
|
1229
|
+
transport layer:
|
|
1202
1230
|
|
|
1203
1231
|
```bash
|
|
1204
|
-
remits-cli tool --name mcp_run_action --
|
|
1232
|
+
remits-cli tool --name mcp_run_action --input '{"accountId":49,"actionId":200,"executionMode":"async","actionRunId":"my-stable-run-id","actionInput":{"sourceDocumentId":"..."}}' --data-mode prod
|
|
1205
1233
|
```
|
|
1206
1234
|
|
|
1207
|
-
Then poll
|
|
1235
|
+
Then poll that run with another **regular tool call** carrying `controlAction:"status"` and the same
|
|
1236
|
+
`accountId` + `actionRunId`:
|
|
1208
1237
|
|
|
1209
1238
|
```bash
|
|
1210
|
-
remits-cli tool
|
|
1239
|
+
remits-cli tool --name mcp_run_action --input '{"controlAction":"status","accountId":49,"actionRunId":"my-stable-run-id"}' --data-mode prod
|
|
1211
1240
|
```
|
|
1212
1241
|
|
|
1213
|
-
|
|
1242
|
+
> This poll is a normal `remits-cli tool --name mcp_run_action` call — **not** `remits-cli tool status`,
|
|
1243
|
+
> which polls the CLI-transport `--async` `callId` (a different mechanism). Use `controlAction:"status"`
|
|
1244
|
+
> (rather than `command:"status"`) inside the input so it is never conflated with the transport-level status.
|
|
1245
|
+
> If you started the run with a different `userId`, include that same `userId` in the poll (the run's status
|
|
1246
|
+
> is keyed by account + user + `actionRunId`; it otherwise defaults to the current user).
|
|
1214
1247
|
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
For job-style Actions (`Action.job == true`), prefer `executionMode:"event"` when you want the durable Event
|
|
1220
|
-
lifecycle, Event status, and platform recovery behavior:
|
|
1248
|
+
For job-style Actions only (`Action.job == true`), prefer `executionMode:"event"` when you want the durable
|
|
1249
|
+
Event lifecycle, Event status, and platform recovery behavior. Event mode is inherently async; poll it the
|
|
1250
|
+
same way (`controlAction:"status"` + `actionRunId`) — the status resolves the backing Event's terminal state:
|
|
1221
1251
|
|
|
1222
|
-
```
|
|
1223
|
-
{"accountId":49,"actionId":200,"executionMode":"event","actionInput":{
|
|
1252
|
+
```bash
|
|
1253
|
+
remits-cli tool --name mcp_run_action --input '{"accountId":49,"actionId":200,"executionMode":"event","actionRunId":"my-stable-run-id","actionInput":{}}' --data-mode prod
|
|
1224
1254
|
```
|
|
1225
1255
|
|
|
1226
|
-
|
|
1227
|
-
|
|
1256
|
+
Returned fields on the async/event start: `actionRunId`, `status:"running"`, `executionMode`,
|
|
1257
|
+
`threadGroupingId`, `componentSource`, `componentSignature`, and (event mode) `eventId`/`eventStatus`. The
|
|
1258
|
+
`status` poll adds `result` on completion, or `message`/`error` on failure.
|
|
1228
1259
|
|
|
1229
1260
|
### `mcp_run_agent`
|
|
1230
1261
|
Run one real Agent turn on a target account. The Agent hooks and tools execute for real against the requested
|
|
@@ -1236,26 +1267,24 @@ Use direct mode only for short turns:
|
|
|
1236
1267
|
remits-cli tool --name mcp_run_agent --input '{"accountId":49,"agentName":"InvoiceAuditor","message":"Summarize this invoice context","executionMode":"direct"}' --data-mode prod
|
|
1237
1268
|
```
|
|
1238
1269
|
|
|
1239
|
-
Use async mode for autonomous or long Agent turns.
|
|
1240
|
-
`sessionId`
|
|
1270
|
+
Use the tool's own async mode for autonomous or long Agent turns. It returns immediately with both an
|
|
1271
|
+
`agentRunId` and a single, stable `sessionId` — the **same** id the running session uses, so you can inspect
|
|
1272
|
+
it right away. Pass your own `agentRunId` for deterministic polling. Do **not** also pass the CLI `--async`
|
|
1273
|
+
flag (that only delays these ids into a polled result):
|
|
1241
1274
|
|
|
1242
1275
|
```bash
|
|
1243
|
-
remits-cli tool --name mcp_run_agent --
|
|
1276
|
+
remits-cli tool --name mcp_run_agent --input '{"accountId":49,"agentName":"InvoiceAuditor","message":"Audit this invoice","executionMode":"async","agentRunId":"my-stable-run-id","context":{"invoiceId":"..."}}' --data-mode prod
|
|
1244
1277
|
```
|
|
1245
1278
|
|
|
1246
|
-
|
|
1279
|
+
Then poll that run with another **regular tool call** carrying `controlAction:"status"` and the same
|
|
1280
|
+
`accountId` + `agentRunId` (this is a normal `mcp_run_agent` call, **not** `remits-cli tool status`):
|
|
1247
1281
|
|
|
1248
1282
|
```bash
|
|
1249
|
-
remits-cli tool
|
|
1250
|
-
```
|
|
1251
|
-
|
|
1252
|
-
Or poll the tool-level agent run:
|
|
1253
|
-
|
|
1254
|
-
```json
|
|
1255
|
-
{"command":"status","accountId":49,"agentRunId":"<agentRunId>"}
|
|
1283
|
+
remits-cli tool --name mcp_run_agent --input '{"controlAction":"status","accountId":49,"agentRunId":"my-stable-run-id"}' --data-mode prod
|
|
1256
1284
|
```
|
|
1257
1285
|
|
|
1258
|
-
Inspect the AI session
|
|
1286
|
+
Inspect the live/persisted AI session at any time using the `sessionId` returned by the start call (it is the
|
|
1287
|
+
run's real, canonical session id):
|
|
1259
1288
|
|
|
1260
1289
|
```bash
|
|
1261
1290
|
remits-cli tool --name mcp_ai_session_search --input '{"action":"detail","sessionId":"<sessionId>","summaryOnly":true}' --data-mode prod
|
|
@@ -1617,8 +1646,8 @@ For tests specifically:
|
|
|
1617
1646
|
| 500 / `Internal Server Error` from Remits | Stop normal task work. Capture the exact command, error, and response artifact, then escalate to a Remits system admin. Do not invent a workaround. |
|
|
1618
1647
|
| Tool parameters rejected | Tool schemas may be cached. Run `remits-cli tools` to refresh `.remits-cli/tools/tools.json` with latest schemas. |
|
|
1619
1648
|
| Tool response missing | Check `./.remits-cli/tool-responses/` |
|
|
1620
|
-
| Long-running tool times out |
|
|
1621
|
-
| Need to continue tracking a long Action or Agent after terminal disconnect | Read `./.remits-cli/tool-responses/<callId>.json` for the returned `threadGroupingId
|
|
1649
|
+
| Long-running tool times out | For `mcp_run_action`/`mcp_run_agent`, use the tool's own `executionMode:"async"` and poll with a `controlAction:"status"` call (see "Tool Execution Lifecycle"). For other long tools without their own async, use `remits-cli tool --async true` and poll `remits-cli tool status --call-id <callId>`. `--timeout-ms` only adjusts the per-request HTTP timeout; it is not a substitute for async. |
|
|
1650
|
+
| Need to continue tracking a long Action or Agent after terminal disconnect | Read `./.remits-cli/tool-responses/<callId>.json` for the returned `actionRunId`/`agentRunId`/`sessionId`/`threadGroupingId`. Re-poll the run with a `controlAction:"status"` call carrying that `actionRunId`/`agentRunId`, or inspect the agent session via `mcp_ai_session_search` with the `sessionId`. |
|
|
1622
1651
|
| Staged change has no effect in a live (non-CLI) run | Staged overrides resolve only under a CLI TestMode (`branchName`+`cliUserId`). Live webhooks and other non-CLI runtime paths still use the DB. `commit` to make it durable. See "Component Resolution". |
|
|
1623
1652
|
| New source shown by `mcp_component_view` but old behavior persists after sync/commit | The compile cache (`CLOSURE_CACHE`) is keyed by `version:<N>:<sourceHash12>`, so a source change on the same version now invalidates it automatically — a run right after sync/commit picks up the new source. If old behavior still persists, confirm the run actually hit the synced instance and that no staged override is still shadowing DB (`remits-cli components status`). |
|
|
1624
1653
|
| Staged Reader test run throws `No enum constant ObjectType.<family>` | The staged entry has the component family in `type` (should be `kind`). Clear + re-stage; if it persists, the `mcp_component_edit` tool on that instance is on an old/cached version. Inspect with `mcp_cache`. |
|