@remits/remits-cli 0.1.80 → 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 +243 -60
- package/package.json +1 -1
- package/skills/remits-cli/SKILL.md +254 -8
package/index.js
CHANGED
|
@@ -413,6 +413,18 @@ function writeToolResponseFile(cwd, callId, responsePayload) {
|
|
|
413
413
|
return file;
|
|
414
414
|
}
|
|
415
415
|
|
|
416
|
+
function readToolResponseFile(cwd, callId) {
|
|
417
|
+
if (!callId) return null;
|
|
418
|
+
const paths = ensureLocalState(cwd);
|
|
419
|
+
const file = path.join(paths.toolResponsesDir, callId + '.json');
|
|
420
|
+
if (!fs.existsSync(file)) return null;
|
|
421
|
+
try {
|
|
422
|
+
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
423
|
+
} catch (_) {
|
|
424
|
+
return null;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
416
428
|
function normalizeName(name) {
|
|
417
429
|
return name.replace(/([a-z])([A-Z])/g, '$1 $2').replace(/\s+/g, ' ').trim();
|
|
418
430
|
}
|
|
@@ -1191,9 +1203,14 @@ function collectComponents(cwd) {
|
|
|
1191
1203
|
return components;
|
|
1192
1204
|
}
|
|
1193
1205
|
|
|
1194
|
-
function
|
|
1206
|
+
function parsePositiveInt(value, fallback) {
|
|
1207
|
+
const parsed = Number(value);
|
|
1208
|
+
return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : fallback;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
function buildAxios(baseUrl, token, timeoutMs = 60000) {
|
|
1195
1212
|
const headers = token ? { Authorization: 'Bearer ' + token } : {};
|
|
1196
|
-
return axios.create({ baseURL: baseUrl, timeout: 60000, headers });
|
|
1213
|
+
return axios.create({ baseURL: baseUrl, timeout: parsePositiveInt(timeoutMs, 60000), headers });
|
|
1197
1214
|
}
|
|
1198
1215
|
|
|
1199
1216
|
function describeError(err) {
|
|
@@ -1934,6 +1951,37 @@ async function waitForStatus(api, cwd, accountId, branchName, taskId, token, dat
|
|
|
1934
1951
|
}
|
|
1935
1952
|
}
|
|
1936
1953
|
|
|
1954
|
+
async function waitForToolStatus(api, cwd, options) {
|
|
1955
|
+
const started = Date.now();
|
|
1956
|
+
let pollDelayMs = parsePositiveInt(options.pollIntervalMs, 1000);
|
|
1957
|
+
const maxPollDelayMs = parsePositiveInt(options.maxPollIntervalMs, 5000);
|
|
1958
|
+
const deadline = options.waitTimeoutMs ? (started + parsePositiveInt(options.waitTimeoutMs, 0)) : null;
|
|
1959
|
+
|
|
1960
|
+
while (true) {
|
|
1961
|
+
const statusResponse = await loggedPost(api, cwd, '/cli/tool', {
|
|
1962
|
+
token: options.token,
|
|
1963
|
+
command: 'status',
|
|
1964
|
+
accountId: options.accountId,
|
|
1965
|
+
accountIdExplicit: options.accountIdExplicit,
|
|
1966
|
+
branchName: options.branchName,
|
|
1967
|
+
dataMode: options.dataMode,
|
|
1968
|
+
callId: options.callId
|
|
1969
|
+
}, { toolResponseFile: true, toolCallId: options.callId });
|
|
1970
|
+
|
|
1971
|
+
const status = statusResponse.data;
|
|
1972
|
+
if (status.status === 'completed' || status.status === 'failed') {
|
|
1973
|
+
return { data: status, responseFile: statusResponse.responseFile };
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1976
|
+
if (deadline && Date.now() >= deadline) {
|
|
1977
|
+
throw new Error('Timed out waiting for async tool call ' + options.callId + ' after ' + (Date.now() - started) + 'ms');
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
await new Promise((r) => setTimeout(r, pollDelayMs));
|
|
1981
|
+
pollDelayMs = Math.min(maxPollDelayMs, pollDelayMs + 500);
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1937
1985
|
function watchWebsocket(baseUrl, topicId, taskId) {
|
|
1938
1986
|
const client = new Client({
|
|
1939
1987
|
brokerURL: webSocketUrl(baseUrl),
|
|
@@ -2130,8 +2178,10 @@ async function toolCommand(flags) {
|
|
|
2130
2178
|
const sessionContext = resolveSessionContext(cwd, flags);
|
|
2131
2179
|
const { session, accountId } = sessionContext;
|
|
2132
2180
|
|
|
2181
|
+
const subcommand = flags._ && flags._[1];
|
|
2182
|
+
const statusMode = subcommand === 'status' || flagEnabled(flags.status);
|
|
2133
2183
|
const toolName = flags.name || flags.tool;
|
|
2134
|
-
if (!toolName) {
|
|
2184
|
+
if (!toolName && !statusMode) {
|
|
2135
2185
|
throw new Error('Missing --name <toolName>');
|
|
2136
2186
|
}
|
|
2137
2187
|
|
|
@@ -2144,8 +2194,48 @@ async function toolCommand(flags) {
|
|
|
2144
2194
|
const baseUrl = flags['base-url'] || session.baseUrl || DEFAULT_BASE_URL;
|
|
2145
2195
|
const branchName = flags.branch || currentBranch(cwd);
|
|
2146
2196
|
const dataMode = resolveDataMode(flags, session);
|
|
2147
|
-
const
|
|
2148
|
-
const
|
|
2197
|
+
const timeoutMs = parsePositiveInt(flags['timeout-ms'] || flags.timeoutMs, 60000);
|
|
2198
|
+
const callId = String(flags['call-id'] || flags.callId || crypto.randomUUID());
|
|
2199
|
+
const asyncMode = flagEnabled(flags.async) || flagEnabled(input.async);
|
|
2200
|
+
const waitForAsync = flagEnabled(flags.wait);
|
|
2201
|
+
const pollIntervalMs = parsePositiveInt(flags['poll-interval-ms'] || flags.pollIntervalMs, 1000);
|
|
2202
|
+
const waitTimeoutMs = flags['wait-timeout-ms'] || flags.waitTimeoutMs;
|
|
2203
|
+
const api = buildAxios(baseUrl, session.token, timeoutMs);
|
|
2204
|
+
|
|
2205
|
+
if (statusMode) {
|
|
2206
|
+
const requestedCallId = String(flags['call-id'] || flags.callId || '');
|
|
2207
|
+
if (!requestedCallId) {
|
|
2208
|
+
throw new Error('Missing --call-id <callId>');
|
|
2209
|
+
}
|
|
2210
|
+
|
|
2211
|
+
const stored = readToolResponseFile(cwd, requestedCallId);
|
|
2212
|
+
const statusAccountId = flags['account-id'] || (stored && stored.accountId) || accountId;
|
|
2213
|
+
const statusResponse = await loggedPost(api, cwd, '/cli/tool', {
|
|
2214
|
+
token: session.token,
|
|
2215
|
+
command: 'status',
|
|
2216
|
+
accountId: statusAccountId,
|
|
2217
|
+
accountIdExplicit: accountIdExplicit || Boolean(stored && stored.accountId),
|
|
2218
|
+
branchName: flags.branch || (stored && stored.branchName) || branchName,
|
|
2219
|
+
dataMode: flags['data-mode'] || (stored && stored.dataMode) || dataMode,
|
|
2220
|
+
callId: requestedCallId
|
|
2221
|
+
}, { toolResponseFile: true, toolCallId: requestedCallId });
|
|
2222
|
+
|
|
2223
|
+
const data = statusResponse.data;
|
|
2224
|
+
if (!data.success) {
|
|
2225
|
+
throw new Error(data.message || 'Tool status lookup failed');
|
|
2226
|
+
}
|
|
2227
|
+
|
|
2228
|
+
printSessionResolutionWarning(sessionContext);
|
|
2229
|
+
printResolvedBaseUrl(baseUrl);
|
|
2230
|
+
console.log('Tool call status:', data.status);
|
|
2231
|
+
console.log('Call ID:', requestedCallId);
|
|
2232
|
+
console.log('Data mode:', data.dataMode || dataMode);
|
|
2233
|
+
if (data.threadGroupingId) console.log('Thread grouping ID:', data.threadGroupingId);
|
|
2234
|
+
console.log('Session log:', sessionJsonlFile(cwd));
|
|
2235
|
+
console.log('Tool response file:', statusResponse.responseFile);
|
|
2236
|
+
if (data.status === 'failed') process.exitCode = 1;
|
|
2237
|
+
return;
|
|
2238
|
+
}
|
|
2149
2239
|
|
|
2150
2240
|
const response = await loggedPost(api, cwd, '/cli/tool', {
|
|
2151
2241
|
token: session.token,
|
|
@@ -2155,7 +2245,8 @@ async function toolCommand(flags) {
|
|
|
2155
2245
|
dataMode,
|
|
2156
2246
|
name: String(toolName),
|
|
2157
2247
|
input,
|
|
2158
|
-
callId
|
|
2248
|
+
callId,
|
|
2249
|
+
async: asyncMode
|
|
2159
2250
|
}, { toolResponseFile: true, toolCallId: callId });
|
|
2160
2251
|
|
|
2161
2252
|
const data = response.data;
|
|
@@ -2165,9 +2256,11 @@ async function toolCommand(flags) {
|
|
|
2165
2256
|
|
|
2166
2257
|
printSessionResolutionWarning(sessionContext);
|
|
2167
2258
|
printResolvedBaseUrl(baseUrl);
|
|
2168
|
-
console.log('Tool call succeeded.');
|
|
2259
|
+
console.log(asyncMode ? 'Tool call started.' : 'Tool call succeeded.');
|
|
2169
2260
|
console.log('Call ID:', callId);
|
|
2170
|
-
console.log('Data mode:', dataMode);
|
|
2261
|
+
console.log('Data mode:', data.dataMode || dataMode);
|
|
2262
|
+
if (data.status) console.log('Status:', data.status);
|
|
2263
|
+
if (data.threadGroupingId) console.log('Thread grouping ID:', data.threadGroupingId);
|
|
2171
2264
|
// Make the resolved component version explicit so callers never assume staged vs DB.
|
|
2172
2265
|
if (data.componentSource) {
|
|
2173
2266
|
const sig = data.componentSignature ? ` (${data.componentSignature})` : '';
|
|
@@ -2175,6 +2268,25 @@ async function toolCommand(flags) {
|
|
|
2175
2268
|
}
|
|
2176
2269
|
console.log('Session log:', sessionJsonlFile(cwd));
|
|
2177
2270
|
console.log('Tool response file:', response.responseFile);
|
|
2271
|
+
|
|
2272
|
+
if (asyncMode && waitForAsync) {
|
|
2273
|
+
const finalStatus = await waitForToolStatus(api, cwd, {
|
|
2274
|
+
token: session.token,
|
|
2275
|
+
accountId: data.accountId || accountId,
|
|
2276
|
+
accountIdExplicit: true,
|
|
2277
|
+
branchName: data.branchName || branchName,
|
|
2278
|
+
dataMode: data.dataMode || dataMode,
|
|
2279
|
+
callId,
|
|
2280
|
+
pollIntervalMs,
|
|
2281
|
+
waitTimeoutMs
|
|
2282
|
+
});
|
|
2283
|
+
|
|
2284
|
+
console.log('Final status:', finalStatus.data.status);
|
|
2285
|
+
console.log('Tool response file:', finalStatus.responseFile);
|
|
2286
|
+
if (finalStatus.data.status !== 'completed') {
|
|
2287
|
+
process.exitCode = 1;
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2178
2290
|
}
|
|
2179
2291
|
|
|
2180
2292
|
async function dataModeCommand(flags, subcommand) {
|
|
@@ -2698,36 +2810,78 @@ function truncateText(value, maxChars = 20000) {
|
|
|
2698
2810
|
return text.slice(0, maxChars) + '\n...[truncated ' + (text.length - maxChars) + ' chars]';
|
|
2699
2811
|
}
|
|
2700
2812
|
|
|
2813
|
+
const DASHBOARD_FILE_PREVIEW_BYTES = 512 * 1024;
|
|
2814
|
+
|
|
2701
2815
|
function readJsonFilePretty(file) {
|
|
2702
2816
|
if (!fs.existsSync(file)) {
|
|
2703
2817
|
return null;
|
|
2704
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
|
+
}
|
|
2705
2826
|
try {
|
|
2706
|
-
return JSON.stringify(JSON.parse(fs.readFileSync(file, 'utf8')), null, 2);
|
|
2827
|
+
return truncateText(JSON.stringify(JSON.parse(fs.readFileSync(file, 'utf8')), null, 2));
|
|
2707
2828
|
} catch (_) {
|
|
2708
2829
|
return truncateText(fs.readFileSync(file, 'utf8'));
|
|
2709
2830
|
}
|
|
2710
2831
|
}
|
|
2711
2832
|
|
|
2712
|
-
function
|
|
2833
|
+
function readTextTail(file, maxBytes = 512 * 1024) {
|
|
2713
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) {
|
|
2714
2861
|
return null;
|
|
2715
2862
|
}
|
|
2716
|
-
const
|
|
2717
|
-
const
|
|
2718
|
-
|
|
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'));
|
|
2719
2869
|
}
|
|
2720
2870
|
|
|
2721
2871
|
function readJsonLinesFile(file, maxEntries = 80) {
|
|
2722
|
-
if (!file
|
|
2872
|
+
if (!file) {
|
|
2723
2873
|
return [];
|
|
2724
2874
|
}
|
|
2725
2875
|
try {
|
|
2726
|
-
const
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
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);
|
|
2731
2885
|
return lines.map((line) => {
|
|
2732
2886
|
try {
|
|
2733
2887
|
return JSON.parse(line);
|
|
@@ -3201,12 +3355,32 @@ async function reconnectManagedWebsockets() {
|
|
|
3201
3355
|
|
|
3202
3356
|
function startDashboardServer(preferredPort) {
|
|
3203
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
|
+
};
|
|
3204
3374
|
const server = http.createServer(async (req, res) => {
|
|
3205
3375
|
const requestUrl = new URL(req.url, 'http://' + DASHBOARD_HOST + ':' + (runtimeState.service.dashboardPort || preferredPort));
|
|
3206
3376
|
if (req.method === 'GET' && requestUrl.pathname === '/api/state') {
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
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
|
+
}
|
|
3210
3384
|
return;
|
|
3211
3385
|
}
|
|
3212
3386
|
|
|
@@ -3216,51 +3390,59 @@ function startDashboardServer(preferredPort) {
|
|
|
3216
3390
|
body += chunk.toString();
|
|
3217
3391
|
});
|
|
3218
3392
|
req.on('end', async () => {
|
|
3219
|
-
const params = new URLSearchParams(body);
|
|
3220
|
-
const action = params.get('action');
|
|
3221
|
-
let actionResult = null;
|
|
3222
|
-
runtimeState.lastAction = {
|
|
3223
|
-
action,
|
|
3224
|
-
at: new Date().toISOString()
|
|
3225
|
-
};
|
|
3226
|
-
if (action === 'export-issues') {
|
|
3227
|
-
const bundlePath = exportIssueBundle(collectDashboardSnapshot());
|
|
3228
|
-
actionResult = { bundlePath };
|
|
3229
|
-
runtimeState.lastAction.bundlePath = bundlePath;
|
|
3230
|
-
} else if (action === 'rescan-repos') {
|
|
3231
|
-
recordDiscoverySummary(discoverAccountRepos());
|
|
3232
|
-
} else if (action === 'open-tmux') {
|
|
3233
|
-
openTmuxSessionForUser();
|
|
3234
|
-
} else if (action === 'start-tmux') {
|
|
3235
|
-
ensureTmuxSession(os.homedir());
|
|
3236
|
-
} else if (action === 'stop-tmux') {
|
|
3237
|
-
killTmuxSession();
|
|
3238
|
-
} else if (action === 'reconnect-websockets') {
|
|
3239
|
-
await reconnectManagedWebsockets();
|
|
3240
|
-
}
|
|
3241
3393
|
const wantsJson = String(req.headers.accept || '').includes('application/json');
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3394
|
+
try {
|
|
3395
|
+
const params = new URLSearchParams(body);
|
|
3396
|
+
const action = params.get('action');
|
|
3397
|
+
let actionResult = null;
|
|
3398
|
+
runtimeState.lastAction = {
|
|
3247
3399
|
action,
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
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);
|
|
3252
3433
|
}
|
|
3253
|
-
res.statusCode = 302;
|
|
3254
|
-
res.setHeader('Location', '/');
|
|
3255
|
-
res.end('');
|
|
3256
3434
|
});
|
|
3257
3435
|
return;
|
|
3258
3436
|
}
|
|
3259
3437
|
|
|
3260
3438
|
if (req.method === 'GET' && requestUrl.pathname === '/') {
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
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
|
+
}
|
|
3264
3446
|
return;
|
|
3265
3447
|
}
|
|
3266
3448
|
|
|
@@ -4550,7 +4732,8 @@ async function main() {
|
|
|
4550
4732
|
console.log(' remits-cli data-mode [set test|prod]');
|
|
4551
4733
|
console.log(' remits-cli install --skills [--target codex|claude|gemini|all] [--overwrite true]');
|
|
4552
4734
|
console.log(' remits-cli tools [--base-url URL] [--account-id ID] [--data-mode test|prod]');
|
|
4553
|
-
console.log(' remits-cli tool --name <toolName> [--base-url URL] [--input \"{...}\"|--input-file file.json] [--data-mode test|prod]');
|
|
4735
|
+
console.log(' remits-cli tool --name <toolName> [--base-url URL] [--input \"{...}\"|--input-file file.json] [--data-mode test|prod] [--timeout-ms 60000] [--async true --wait true]');
|
|
4736
|
+
console.log(' remits-cli tool status --call-id <callId> [--base-url URL] [--account-id ID] [--data-mode test|prod]');
|
|
4554
4737
|
console.log(' remits-cli components stage [--base-url URL] [--account-id ID] [--branch BRANCH] [--data-mode test|prod] [--json|--verbose]');
|
|
4555
4738
|
console.log(' remits-cli components status [--base-url URL] [--account-id ID] [--branch BRANCH] [--component-type TYPE --component-id ID] [--json|--verbose]');
|
|
4556
4739
|
console.log(' remits-cli components clear [--base-url URL] [--account-id ID] [--branch BRANCH] [--component-type TYPE] [--component-id ID] [--all] [--json|--verbose]');
|
package/package.json
CHANGED
|
@@ -5,6 +5,85 @@ description: Use remits-cli for fast branch-scoped component staging, test execu
|
|
|
5
5
|
|
|
6
6
|
# remits-cli
|
|
7
7
|
|
|
8
|
+
## Table of Contents
|
|
9
|
+
|
|
10
|
+
- Line 89: Account Targeting Model
|
|
11
|
+
- Line 112: Component Integrity Rules: Repo ↔ Database Reconciliation (read before any sync/commit)
|
|
12
|
+
- Line 118: How the platform reconciles the repo into the database (the mechanism you must understand)
|
|
13
|
+
- Line 144: The surfaces and their intended behavior
|
|
14
|
+
- Line 166: Intended workflows
|
|
15
|
+
- Line 185: Pre-sync safety check (confirm ALL before `components sync` or `components commit`)
|
|
16
|
+
- Line 195: If something looks wrong — stop, don't paper over
|
|
17
|
+
- Line 219: Required Local Index Reads
|
|
18
|
+
- Line 253: Big Picture: How remits-cli State Is Organized
|
|
19
|
+
- Line 283: Support Ticket Mental Model
|
|
20
|
+
- Line 319: Efficiency Rules
|
|
21
|
+
- Line 331: Account Repository Index
|
|
22
|
+
- Line 356: Two Workflows
|
|
23
|
+
- Line 363: Test Mode vs Prod Mode
|
|
24
|
+
- Line 395: Platform Mental Model: Front Stage vs Back Stage
|
|
25
|
+
- Line 402: Documents vs Records
|
|
26
|
+
- Line 412: HTTP Audits
|
|
27
|
+
- Line 466: Record `content` / Context Mental Model
|
|
28
|
+
- Line 486: Provenance Fields
|
|
29
|
+
- Line 504: Why Persisted Context Looks Different From Runtime Context
|
|
30
|
+
- Line 525: Investigation Rule for Record Context
|
|
31
|
+
- Line 540: AI Request Response Records
|
|
32
|
+
- Line 547: AI Session Groupings
|
|
33
|
+
- Line 562: Correlation Keys
|
|
34
|
+
- Line 568: Node Reference Table
|
|
35
|
+
- Line 578: Repository vs External Context
|
|
36
|
+
- Line 585: Getting Started
|
|
37
|
+
- Line 587: Authentication
|
|
38
|
+
- Line 600: Host vs Data Mode
|
|
39
|
+
- Line 619: Tool Execution Lifecycle
|
|
40
|
+
- Line 660: Data Mode
|
|
41
|
+
- Line 670: Development Workflow
|
|
42
|
+
- Line 672: The Golden Rule: Writing Code Is Not Finishing the Job
|
|
43
|
+
- Line 684: The Development Fast Loop
|
|
44
|
+
- Line 843: User Confirmation Preferences
|
|
45
|
+
- Line 853: Component Resolution: Staging Cache vs DB (which "version" actually runs)
|
|
46
|
+
- Line 859: The two source layers + the compile cache
|
|
47
|
+
- Line 870: Staging cache key format
|
|
48
|
+
- Line 883: How the platform picks staged vs DB (the compile signature)
|
|
49
|
+
- Line 900: When staged overrides apply
|
|
50
|
+
- Line 924: Diagnosing which version is in play
|
|
51
|
+
- Line 945: Stage / commit / clear with the MCP tools
|
|
52
|
+
- Line 969: Stale after sync / commit (the in-memory compile cache)
|
|
53
|
+
- Line 979: Production Support Workflow
|
|
54
|
+
- Line 987: Investigation Strategy
|
|
55
|
+
- Line 1017: Presenting Findings
|
|
56
|
+
- Line 1025: Verifying a Production Issue Fix
|
|
57
|
+
- Line 1038: Tool Reference
|
|
58
|
+
- Line 1040: Execute a Tool
|
|
59
|
+
- Line 1067: `mcp_account_view`
|
|
60
|
+
- Line 1074: `mcp_firestore_search`
|
|
61
|
+
- Line 1112: `mcp_object_activity`
|
|
62
|
+
- Line 1121: `mcp_record_listing`
|
|
63
|
+
- Line 1146: `mcp_record_view`
|
|
64
|
+
- Line 1159: `mcp_ai_session_search`
|
|
65
|
+
- Line 1191: `mcp_run_action`
|
|
66
|
+
- Line 1229: `mcp_run_agent`
|
|
67
|
+
- Line 1267: `mcp_system_logs`
|
|
68
|
+
- Line 1290: `mcp_component_view`
|
|
69
|
+
- Line 1308: `mcp_component_grep`
|
|
70
|
+
- Line 1321: `mcp_support_ticket`
|
|
71
|
+
- Line 1370: `mcp_run_test`
|
|
72
|
+
- Line 1379: `mcp_component_edit`
|
|
73
|
+
- Line 1394: `mcp_component_create`
|
|
74
|
+
- Line 1408: `mcp_component_commit`
|
|
75
|
+
- Line 1416: `mcp_cache`
|
|
76
|
+
- Line 1428: Multi-Session Support
|
|
77
|
+
- Line 1448: Persistent Service, Control Center, and Agent Dispatch
|
|
78
|
+
- Line 1457: Starting the Service
|
|
79
|
+
- Line 1483: Control Center
|
|
80
|
+
- Line 1498: Agent Dispatch
|
|
81
|
+
- Line 1519: Configuring the Preferred Agent
|
|
82
|
+
- Line 1530: Local State Files
|
|
83
|
+
- Line 1578: Command Reference
|
|
84
|
+
- Line 1605: Troubleshooting
|
|
85
|
+
- Line 1632: When Something Doesn't Work as Expected
|
|
86
|
+
|
|
8
87
|
`remits-cli` is the brainstem for both **development** and **production support**. You might run it inside an account repository (where `account-info.json` and `/components` already exist) or from outside any repo when a user needs help on another account. Your users are business professionals — they think in terms of what they can see in a browser.
|
|
9
88
|
|
|
10
89
|
## Account Targeting Model
|
|
@@ -467,10 +546,14 @@ The goal of an investigation is not only to find a suspicious record. It is to e
|
|
|
467
546
|
|
|
468
547
|
### AI Session Groupings
|
|
469
548
|
|
|
470
|
-
- 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.
|
|
471
550
|
- This tool mirrors the admin AI Groupings UI:
|
|
472
|
-
- search filters: `search`, `sessionId`, `groupingId`, `user`, `account`, `agent`, `scope`
|
|
473
|
-
- 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.)
|
|
474
557
|
- Use it when you need to understand:
|
|
475
558
|
- what prompts, system instructions, tools, and responses were actually sent for a session
|
|
476
559
|
- how an Agent behaved across one grouping, including prompt-tuning and tool-usage opportunities
|
|
@@ -537,6 +620,60 @@ remits-cli tool --base-url http://localhost:8080 --name mcp_account_view --data-
|
|
|
537
620
|
|
|
538
621
|
Do not assume `--data-mode prod` implies the deployed prod host, or that `--data-mode test` implies localhost. If host matters, read `~/.remits-cli/sessions.json` first and pass `--base-url` explicitly.
|
|
539
622
|
|
|
623
|
+
### Tool Execution Lifecycle
|
|
624
|
+
|
|
625
|
+
`remits-cli tool` supports both synchronous and asynchronous execution. Use normal synchronous execution
|
|
626
|
+
for quick investigation tools:
|
|
627
|
+
|
|
628
|
+
```bash
|
|
629
|
+
remits-cli tool --name "mcp_account_view" --input '{"accountId": 37}' --data-mode prod
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
**There are two independent async mechanisms — do not confuse or stack them:**
|
|
633
|
+
|
|
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.
|
|
639
|
+
|
|
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.
|
|
646
|
+
|
|
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
|
|
653
|
+
```
|
|
654
|
+
|
|
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:
|
|
658
|
+
|
|
659
|
+
```bash
|
|
660
|
+
remits-cli tool status --call-id <callId> --data-mode prod
|
|
661
|
+
```
|
|
662
|
+
|
|
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):
|
|
665
|
+
|
|
666
|
+
```bash
|
|
667
|
+
remits-cli tool --name "some_long_tool_without_its_own_async" --async true --wait true --input '{...}' --data-mode prod
|
|
668
|
+
```
|
|
669
|
+
|
|
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.
|
|
676
|
+
|
|
540
677
|
### Data Mode
|
|
541
678
|
|
|
542
679
|
Controls whether you work with test data or production data. **Default is `test`.**
|
|
@@ -640,6 +777,15 @@ For changes to Readers, Actions, or Rules that process data rather than display
|
|
|
640
777
|
remits-cli tool --name "mcp_firestore_search" --input '{"accountId": <ID>, "collection": "<collection>", "limit": 5, "sort": [{"field": "_lastModifiedAt", "direction": "DESC"}]}'
|
|
641
778
|
```
|
|
642
779
|
|
|
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):
|
|
783
|
+
|
|
784
|
+
```bash
|
|
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>"}
|
|
787
|
+
```
|
|
788
|
+
|
|
643
789
|
#### Step 5: Iterate If Needed
|
|
644
790
|
|
|
645
791
|
If verification reveals issues, repeat the loop: **edit → stage → run**. Every iteration must include a fresh `remits-cli components stage` after your edits and before the next test run. Never run a test immediately after editing without staging first — the platform will execute the previous version, not your latest changes.
|
|
@@ -918,6 +1064,19 @@ remits-cli tool --name "mcp_firestore_search" --input '{"accountId": 37, "collec
|
|
|
918
1064
|
|
|
919
1065
|
Response saved to `./.remits-cli/tool-responses/<callId>.json`. Read the file to see results.
|
|
920
1066
|
|
|
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:
|
|
1069
|
+
|
|
1070
|
+
```bash
|
|
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>"}
|
|
1073
|
+
```
|
|
1074
|
+
|
|
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.
|
|
1079
|
+
|
|
921
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:
|
|
922
1081
|
|
|
923
1082
|
1. Explicit `--account-id <ID>` flag — always wins. Use this when you want to be certain the tool runs against a specific account (and the user's session covers it).
|
|
@@ -1032,7 +1191,7 @@ Front-stage references:
|
|
|
1032
1191
|
|
|
1033
1192
|
| Parameter | Required | Description |
|
|
1034
1193
|
|-----------|----------|-------------|
|
|
1035
|
-
| `action` | no | `search` (default) or `
|
|
1194
|
+
| `action` | no | `search` (default), `detail`, or session control `pause`/`unpause`/`interrupt` |
|
|
1036
1195
|
| `search` | no | Broad text match against session IDs and grouping IDs |
|
|
1037
1196
|
| `sessionId` | no | Session ID filter in search mode, or grouping/session key in detail mode |
|
|
1038
1197
|
| `groupingId` | no | Grouping ID filter in search mode, or grouping key in detail mode |
|
|
@@ -1041,14 +1200,98 @@ Front-stage references:
|
|
|
1041
1200
|
| `account` | no | Account filter |
|
|
1042
1201
|
| `agent` | no | Agent filter |
|
|
1043
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` |
|
|
1044
1205
|
| `page` | no | 1-based page number. Default: `1` |
|
|
1045
1206
|
| `pageSize` | no | Results per page. Default: `25`, max: `100` |
|
|
1046
|
-
| `
|
|
1047
|
-
| `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` |
|
|
1048
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) |
|
|
1049
1212
|
| `consolidateContext` | no | When `true`, collapses repeated XML-like prompt context into a consolidated section |
|
|
1050
1213
|
|
|
1051
|
-
|
|
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).
|
|
1215
|
+
|
|
1216
|
+
### `mcp_run_action`
|
|
1217
|
+
Run an Action on a target account, with explicit prod/test data mode, optional staged branch resolution, and
|
|
1218
|
+
staged-vs-DB provenance in the result.
|
|
1219
|
+
|
|
1220
|
+
Use direct mode only for quick Actions:
|
|
1221
|
+
|
|
1222
|
+
```bash
|
|
1223
|
+
remits-cli tool --name mcp_run_action --input '{"accountId":49,"actionId":200,"executionMode":"direct","actionInput":{"sourceDocumentId":"..."}}' --data-mode prod
|
|
1224
|
+
```
|
|
1225
|
+
|
|
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:
|
|
1230
|
+
|
|
1231
|
+
```bash
|
|
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
|
|
1233
|
+
```
|
|
1234
|
+
|
|
1235
|
+
Then poll that run with another **regular tool call** carrying `controlAction:"status"` and the same
|
|
1236
|
+
`accountId` + `actionRunId`:
|
|
1237
|
+
|
|
1238
|
+
```bash
|
|
1239
|
+
remits-cli tool --name mcp_run_action --input '{"controlAction":"status","accountId":49,"actionRunId":"my-stable-run-id"}' --data-mode prod
|
|
1240
|
+
```
|
|
1241
|
+
|
|
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).
|
|
1247
|
+
|
|
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:
|
|
1251
|
+
|
|
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
|
|
1254
|
+
```
|
|
1255
|
+
|
|
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.
|
|
1259
|
+
|
|
1260
|
+
### `mcp_run_agent`
|
|
1261
|
+
Run one real Agent turn on a target account. The Agent hooks and tools execute for real against the requested
|
|
1262
|
+
`dataMode`; pass `dataMode:"test"` for safer tuning.
|
|
1263
|
+
|
|
1264
|
+
Use direct mode only for short turns:
|
|
1265
|
+
|
|
1266
|
+
```bash
|
|
1267
|
+
remits-cli tool --name mcp_run_agent --input '{"accountId":49,"agentName":"InvoiceAuditor","message":"Summarize this invoice context","executionMode":"direct"}' --data-mode prod
|
|
1268
|
+
```
|
|
1269
|
+
|
|
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):
|
|
1274
|
+
|
|
1275
|
+
```bash
|
|
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
|
|
1277
|
+
```
|
|
1278
|
+
|
|
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`):
|
|
1281
|
+
|
|
1282
|
+
```bash
|
|
1283
|
+
remits-cli tool --name mcp_run_agent --input '{"controlAction":"status","accountId":49,"agentRunId":"my-stable-run-id"}' --data-mode prod
|
|
1284
|
+
```
|
|
1285
|
+
|
|
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):
|
|
1288
|
+
|
|
1289
|
+
```bash
|
|
1290
|
+
remits-cli tool --name mcp_ai_session_search --input '{"action":"detail","sessionId":"<sessionId>","summaryOnly":true}' --data-mode prod
|
|
1291
|
+
```
|
|
1292
|
+
|
|
1293
|
+
Key returned fields: `agentRunId`, `sessionId`, `status`, `threadGroupingId`, runtime pause/interruption
|
|
1294
|
+
hints, `lastAssistantMessage`, `toolCallCount`, `result`, `message`, and `error`.
|
|
1052
1295
|
|
|
1053
1296
|
### `mcp_system_logs`
|
|
1054
1297
|
Query Cloud Run service logs.
|
|
@@ -1380,7 +1623,8 @@ remits-cli components commit [--message "msg"] [--data-mode test|prod]
|
|
|
1380
1623
|
remits-cli test run --test <id|name> [--names "a,b"] [--watch true|false] [--data-mode test|prod]
|
|
1381
1624
|
remits-cli token [--path <embeddablePathOrId>] [--data-mode test|prod]
|
|
1382
1625
|
remits-cli tools [--branch <name>] [--data-mode test|prod]
|
|
1383
|
-
remits-cli tool --name <toolName> [--branch <name>] [--input "{...}"] [--data-mode test|prod]
|
|
1626
|
+
remits-cli tool --name <toolName> [--branch <name>] [--input "{...}"] [--data-mode test|prod] [--timeout-ms 60000] [--async true --wait true]
|
|
1627
|
+
remits-cli tool status --call-id <callId> [--data-mode test|prod]
|
|
1384
1628
|
```
|
|
1385
1629
|
|
|
1386
1630
|
For tests specifically:
|
|
@@ -1402,6 +1646,8 @@ For tests specifically:
|
|
|
1402
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. |
|
|
1403
1647
|
| Tool parameters rejected | Tool schemas may be cached. Run `remits-cli tools` to refresh `.remits-cli/tools/tools.json` with latest schemas. |
|
|
1404
1648
|
| Tool response missing | Check `./.remits-cli/tool-responses/` |
|
|
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`. |
|
|
1405
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". |
|
|
1406
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`). |
|
|
1407
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`. |
|