@ricsam/r5dctl 0.0.134 → 0.0.135
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/dist/cjs/cli.cjs +47 -4
- package/dist/cjs/package.json +1 -1
- package/dist/mjs/cli.mjs +47 -4
- package/dist/mjs/package.json +1 -1
- package/dist/types/cli.d.ts +2 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -158,13 +158,15 @@ r5dctl ps stop <run-id>
|
|
|
158
158
|
|
|
159
159
|
`session start` creates an ordinary agent-mode chat on an explicitly selected existing worktree, or creates a named linked worktree from an explicit `worktree:<branch>` source. A new worktree is cut from the source branch's current commit; `--working-tree carry` (the default, `git switch -c` semantics) also copies the source checkout's uncommitted changes into it, while `--working-tree clean` checks out that commit only. The new session's first message states which mode applies, and the start output prints `Source` and `Working tree` lines. A clean request fails with a clear error when the selected worker predates the option, because such a worker would silently carry instead. It requires project, worker, model, and prompt and never infers them from the current shell. Start returns a durable `provisioning` handle immediately; workspace synchronization, linked-worktree creation, and prompt enqueue continue in the background and are visible through `session status`. Idempotent request retries return the same handle. Prompts return after durable enqueue. Task specialization belongs in the prompt; there are no predefined debug, explore, research, or test agents.
|
|
160
160
|
|
|
161
|
-
`session prompt` works on any project session you can access, including one created in the web UI. `--delivery` decides how the prompt reaches a session whose agent is busy: `steer` (the default) lands inside the current turn right after the agent's next tool result, `queue` waits until the agent finishes the whole turn, and `force` aborts the agent's current round of tool calls and continues from the prompt instead. An idle
|
|
161
|
+
`session prompt` works on any project session you can access, including one created in the web UI. `--delivery` decides how the prompt reaches a session whose agent is busy: `steer` (the default) lands inside the current turn right after the agent's next tool result, `queue` waits until the agent finishes the whole turn, and `force` aborts the agent's current round of tool calls and continues from the prompt instead. An idle conversation can start a new generation, but an unfinished turn may still require an explicit user continuation, an interactive answer, worker recovery, or confirmation of interrupted tool replay. A peer prompt never grants that authority. Prompt and answer responses include `messageDelivery: { messageId, queueId, status, blocker }`; `status` distinguishes `blocked` from ordinary `queued`, `steered`, `starting`, `started`, or `forced` delivery. The original message ID and current queue key let callers identify the saved input without sending it again. A blocked response does not start a generation, even when the command exits successfully. Continuation blockers retain the accepted input; `superseded_message` instead identifies a discarded obsolete notice, and `message_already_processed` identifies input already recorded in the conversation.
|
|
162
|
+
|
|
163
|
+
For `pending_tool_replay`, inspect the named tools and current files before deciding whether their actions can safely be repeated. To continue without replaying the interrupted round, an authorized user can use **Force now** on an existing queued message in the session UI. Dispatch the earliest urgent queued message first so later urgent messages retain their order. Force changes the active conversation branch; it does not undo filesystem effects. Do not send another force prompt to replace an already queued continuation. `session stop` returns as soon as the agent run is stopped; the stopped worktree's workspace Git synchronization finishes asynchronously behind the response.
|
|
162
164
|
|
|
163
165
|
Use the optional `--sender "Authentication review"` flag to display a title above the usual message bubble. Agent shells expose `R5D_SESSION_NAME`, refreshed at each command start from the session name (or its ID until named), so agents can pass `--sender "$R5D_SESSION_NAME"`. The sender is a caller-supplied label; omitting the flag sends no title and leaves the message body unchanged.
|
|
164
166
|
|
|
165
167
|
For messages to an active session, explicitly use `--delivery steer` for coordination such as merge notices, requests to pause or avoid files, blockers, corrections, and handoff or resume notices. Use `--delivery force` when its current tool round must be interrupted. Reserve `--delivery queue` for work intentionally deferred until the entire current turn ends; even an FYI needs steering when it affects current work. These rules apply equally to peers, parents, and children.
|
|
166
168
|
|
|
167
|
-
Delivery is not acknowledgment. If a merge or shared-checkout change depends on another session pausing edits, send the pause request separately, wait for its explicit reply, and recheck the working tree before proceeding. `
|
|
169
|
+
Delivery is not acknowledgment. If a merge or shared-checkout change depends on another session pausing edits, send the pause request separately, wait for its explicit reply, and recheck the working tree before proceeding. `Delivery: queued` or `blocked` cannot establish that coordination, and even `steered` or `forced` does not prove the agent has read or acted on the request. Reply to coordination requests and send resume notices with `--delivery steer`.
|
|
168
170
|
|
|
169
171
|
`get sessions` lists a project's sessions, newest first, and appends each session's live state (`running`, `stopping`, `paused`, `provisioning`, `queued`, or `idle`) and how long ago its conversation last advanced (`running, last active 12s ago`). Several sessions can work in the same branch checkout at once (task chips, shared-mode sub-agents, sessions the user starts), so run it with `--branch` before relying on a working tree or reviewing what changed in it. A server that predates the fields prints the id, branch, and name only.
|
|
170
172
|
|
package/dist/cjs/cli.cjs
CHANGED
|
@@ -189,7 +189,8 @@ const SESSION_RUN_HELP_TEXT = [
|
|
|
189
189
|
"",
|
|
190
190
|
"Create, inspect, resume, and stop ordinary agent sessions.",
|
|
191
191
|
"prompt reaches a busy session by --delivery: steer (default) lands after the agent's next tool result,",
|
|
192
|
-
"queue waits for the current turn to finish
|
|
192
|
+
"queue waits for the current turn to finish; force replaces the current tool round. Idle unfinished sessions may still require a user continuation.",
|
|
193
|
+
"Blocked delivery does not start a generation: inspect its reason and original message/queue ID. Use the session UI for Resume or queued Force now.",
|
|
193
194
|
"Send only messages that need action: grants, releases, blockers, corrections, pause/resume, questions, and completion reports;",
|
|
194
195
|
"batch progress and waiting reports into the update the recipient asked for. Every steer wakes a waiting recipient for a full turn.",
|
|
195
196
|
"Use force only when the current tool round must be interrupted; queue is only for a follow-up task deliberately deferred until the turn ends.",
|
|
@@ -347,7 +348,7 @@ const SHARED_HELP_ENTRIES = [
|
|
|
347
348
|
{
|
|
348
349
|
section: "sessions",
|
|
349
350
|
usage: 'session prompt --worker <label> --mode <mode> --model <tier> [--delivery queue|steer|force] [--sender <name>] <session-id> "<prompt>"',
|
|
350
|
-
description: "Prompt a session: steer
|
|
351
|
+
description: "Prompt a session: steer after the next tool result (default), queue a deferred follow-up, or force in place of the current tool round. An idle unfinished session may require explicit continuation or confirmation of interrupted tools. Blocked delivery reports the saved message/queue ID and reason; inspect the existing queue in the session UI instead of sending duplicates. The recipient sees the verified sender session id; --sender only adds a display title."
|
|
351
352
|
},
|
|
352
353
|
{
|
|
353
354
|
section: "sessions",
|
|
@@ -2335,6 +2336,49 @@ function formatChildSessionsLine(session) {
|
|
|
2335
2336
|
if (!Number.isInteger(total) || !Number.isInteger(active) || total <= 0 || active < 0) return void 0;
|
|
2336
2337
|
return `Child sessions: ${active} active of ${total}`;
|
|
2337
2338
|
}
|
|
2339
|
+
function formatMessageDelivery(delivery) {
|
|
2340
|
+
const blockedDescription = delivery.blocker?.reason === "superseded_message" ? "superseded; not accepted" : "accepted; not started";
|
|
2341
|
+
const lines = [
|
|
2342
|
+
`Message: ${delivery.messageId}`,
|
|
2343
|
+
...delivery.queueId ? [`Queue item: ${delivery.queueId}`] : [],
|
|
2344
|
+
`Delivery: ${delivery.status === "blocked" ? `blocked (${blockedDescription})` : delivery.status}`
|
|
2345
|
+
];
|
|
2346
|
+
if (delivery.status === "recorded") lines.push("This message is already recorded; this retry did not request another generation.");
|
|
2347
|
+
const blocker = delivery.blocker;
|
|
2348
|
+
if (!blocker) return lines;
|
|
2349
|
+
switch (blocker.reason) {
|
|
2350
|
+
case "pending_tool_replay":
|
|
2351
|
+
lines.push(`Blocked by: interrupted tools require replay confirmation (${blocker.pendingToolNames.join(", ")}).`);
|
|
2352
|
+
break;
|
|
2353
|
+
case "manual_stop":
|
|
2354
|
+
lines.push("Blocked by: explicit Stop. An authorized user must resume the session in the UI.");
|
|
2355
|
+
break;
|
|
2356
|
+
case "worker_recovery":
|
|
2357
|
+
lines.push("Blocked by: worker recovery. Wait for recovery, then resume the session explicitly.");
|
|
2358
|
+
break;
|
|
2359
|
+
case "interactive_input":
|
|
2360
|
+
lines.push(
|
|
2361
|
+
`Blocked by: pending ${[blocker.questionIds.length ? `questions (${blocker.questionIds.join(", ")})` : "", blocker.environmentRequest ? "environment request" : ""].filter(Boolean).join(" and ") || "interactive input"}. Answer it in the session UI.`
|
|
2362
|
+
);
|
|
2363
|
+
break;
|
|
2364
|
+
case "explicit_continuation_required":
|
|
2365
|
+
lines.push("Blocked by: this unfinished turn needs an explicit user continuation; a peer message cannot start it.");
|
|
2366
|
+
break;
|
|
2367
|
+
case "workspace_remediation":
|
|
2368
|
+
lines.push("Blocked by: workspace remediation. The saved input waits for that recovery.");
|
|
2369
|
+
break;
|
|
2370
|
+
case "superseded_message":
|
|
2371
|
+
lines.push("The source outcome changed; this superseded notice cannot start a generation.");
|
|
2372
|
+
break;
|
|
2373
|
+
}
|
|
2374
|
+
if (delivery.queueId && ["pending_tool_replay", "explicit_continuation_required"].includes(blocker.reason)) {
|
|
2375
|
+
lines.push(
|
|
2376
|
+
"To replace the interrupted round without replaying it, use Force now on the earliest urgent queued message in the session UI. Later urgent messages retain their order.",
|
|
2377
|
+
"Inspect current files before retrying interrupted edits. Use the existing queue item; do not send the prompt again."
|
|
2378
|
+
);
|
|
2379
|
+
}
|
|
2380
|
+
return lines;
|
|
2381
|
+
}
|
|
2338
2382
|
function renderSessionRunStatus(session) {
|
|
2339
2383
|
const status = formatStatusValue(session.status) ?? "unknown";
|
|
2340
2384
|
const lines = [`Session: ${responseString(session, "sessionId") ?? "(unknown)"}`, `Status: ${status}`];
|
|
@@ -2342,8 +2386,7 @@ function renderSessionRunStatus(session) {
|
|
|
2342
2386
|
if (runStatus) lines.push(`Run: ${runStatus}`);
|
|
2343
2387
|
const provisioningQueue = formatProvisioningQueueLine(session);
|
|
2344
2388
|
if (provisioningQueue) lines.push(provisioningQueue);
|
|
2345
|
-
|
|
2346
|
-
if (promptDisposition) lines.push(`Prompt: ${promptDisposition}`);
|
|
2389
|
+
if (session.messageDelivery) lines.push(...formatMessageDelivery(session.messageDelivery));
|
|
2347
2390
|
const branch = responseString(session, "branchName");
|
|
2348
2391
|
if (branch) lines.push(`Worktree: ${branch}`);
|
|
2349
2392
|
pushWorktreeOriginLines(lines, session);
|
package/dist/cjs/package.json
CHANGED
package/dist/mjs/cli.mjs
CHANGED
|
@@ -79,7 +79,8 @@ const SESSION_RUN_HELP_TEXT = [
|
|
|
79
79
|
"",
|
|
80
80
|
"Create, inspect, resume, and stop ordinary agent sessions.",
|
|
81
81
|
"prompt reaches a busy session by --delivery: steer (default) lands after the agent's next tool result,",
|
|
82
|
-
"queue waits for the current turn to finish
|
|
82
|
+
"queue waits for the current turn to finish; force replaces the current tool round. Idle unfinished sessions may still require a user continuation.",
|
|
83
|
+
"Blocked delivery does not start a generation: inspect its reason and original message/queue ID. Use the session UI for Resume or queued Force now.",
|
|
83
84
|
"Send only messages that need action: grants, releases, blockers, corrections, pause/resume, questions, and completion reports;",
|
|
84
85
|
"batch progress and waiting reports into the update the recipient asked for. Every steer wakes a waiting recipient for a full turn.",
|
|
85
86
|
"Use force only when the current tool round must be interrupted; queue is only for a follow-up task deliberately deferred until the turn ends.",
|
|
@@ -237,7 +238,7 @@ const SHARED_HELP_ENTRIES = [
|
|
|
237
238
|
{
|
|
238
239
|
section: "sessions",
|
|
239
240
|
usage: 'session prompt --worker <label> --mode <mode> --model <tier> [--delivery queue|steer|force] [--sender <name>] <session-id> "<prompt>"',
|
|
240
|
-
description: "Prompt a session: steer
|
|
241
|
+
description: "Prompt a session: steer after the next tool result (default), queue a deferred follow-up, or force in place of the current tool round. An idle unfinished session may require explicit continuation or confirmation of interrupted tools. Blocked delivery reports the saved message/queue ID and reason; inspect the existing queue in the session UI instead of sending duplicates. The recipient sees the verified sender session id; --sender only adds a display title."
|
|
241
242
|
},
|
|
242
243
|
{
|
|
243
244
|
section: "sessions",
|
|
@@ -2225,6 +2226,49 @@ function formatChildSessionsLine(session) {
|
|
|
2225
2226
|
if (!Number.isInteger(total) || !Number.isInteger(active) || total <= 0 || active < 0) return void 0;
|
|
2226
2227
|
return `Child sessions: ${active} active of ${total}`;
|
|
2227
2228
|
}
|
|
2229
|
+
function formatMessageDelivery(delivery) {
|
|
2230
|
+
const blockedDescription = delivery.blocker?.reason === "superseded_message" ? "superseded; not accepted" : "accepted; not started";
|
|
2231
|
+
const lines = [
|
|
2232
|
+
`Message: ${delivery.messageId}`,
|
|
2233
|
+
...delivery.queueId ? [`Queue item: ${delivery.queueId}`] : [],
|
|
2234
|
+
`Delivery: ${delivery.status === "blocked" ? `blocked (${blockedDescription})` : delivery.status}`
|
|
2235
|
+
];
|
|
2236
|
+
if (delivery.status === "recorded") lines.push("This message is already recorded; this retry did not request another generation.");
|
|
2237
|
+
const blocker = delivery.blocker;
|
|
2238
|
+
if (!blocker) return lines;
|
|
2239
|
+
switch (blocker.reason) {
|
|
2240
|
+
case "pending_tool_replay":
|
|
2241
|
+
lines.push(`Blocked by: interrupted tools require replay confirmation (${blocker.pendingToolNames.join(", ")}).`);
|
|
2242
|
+
break;
|
|
2243
|
+
case "manual_stop":
|
|
2244
|
+
lines.push("Blocked by: explicit Stop. An authorized user must resume the session in the UI.");
|
|
2245
|
+
break;
|
|
2246
|
+
case "worker_recovery":
|
|
2247
|
+
lines.push("Blocked by: worker recovery. Wait for recovery, then resume the session explicitly.");
|
|
2248
|
+
break;
|
|
2249
|
+
case "interactive_input":
|
|
2250
|
+
lines.push(
|
|
2251
|
+
`Blocked by: pending ${[blocker.questionIds.length ? `questions (${blocker.questionIds.join(", ")})` : "", blocker.environmentRequest ? "environment request" : ""].filter(Boolean).join(" and ") || "interactive input"}. Answer it in the session UI.`
|
|
2252
|
+
);
|
|
2253
|
+
break;
|
|
2254
|
+
case "explicit_continuation_required":
|
|
2255
|
+
lines.push("Blocked by: this unfinished turn needs an explicit user continuation; a peer message cannot start it.");
|
|
2256
|
+
break;
|
|
2257
|
+
case "workspace_remediation":
|
|
2258
|
+
lines.push("Blocked by: workspace remediation. The saved input waits for that recovery.");
|
|
2259
|
+
break;
|
|
2260
|
+
case "superseded_message":
|
|
2261
|
+
lines.push("The source outcome changed; this superseded notice cannot start a generation.");
|
|
2262
|
+
break;
|
|
2263
|
+
}
|
|
2264
|
+
if (delivery.queueId && ["pending_tool_replay", "explicit_continuation_required"].includes(blocker.reason)) {
|
|
2265
|
+
lines.push(
|
|
2266
|
+
"To replace the interrupted round without replaying it, use Force now on the earliest urgent queued message in the session UI. Later urgent messages retain their order.",
|
|
2267
|
+
"Inspect current files before retrying interrupted edits. Use the existing queue item; do not send the prompt again."
|
|
2268
|
+
);
|
|
2269
|
+
}
|
|
2270
|
+
return lines;
|
|
2271
|
+
}
|
|
2228
2272
|
function renderSessionRunStatus(session) {
|
|
2229
2273
|
const status = formatStatusValue(session.status) ?? "unknown";
|
|
2230
2274
|
const lines = [`Session: ${responseString(session, "sessionId") ?? "(unknown)"}`, `Status: ${status}`];
|
|
@@ -2232,8 +2276,7 @@ function renderSessionRunStatus(session) {
|
|
|
2232
2276
|
if (runStatus) lines.push(`Run: ${runStatus}`);
|
|
2233
2277
|
const provisioningQueue = formatProvisioningQueueLine(session);
|
|
2234
2278
|
if (provisioningQueue) lines.push(provisioningQueue);
|
|
2235
|
-
|
|
2236
|
-
if (promptDisposition) lines.push(`Prompt: ${promptDisposition}`);
|
|
2279
|
+
if (session.messageDelivery) lines.push(...formatMessageDelivery(session.messageDelivery));
|
|
2237
2280
|
const branch = responseString(session, "branchName");
|
|
2238
2281
|
if (branch) lines.push(`Worktree: ${branch}`);
|
|
2239
2282
|
pushWorktreeOriginLines(lines, session);
|
package/dist/mjs/package.json
CHANGED
package/dist/types/cli.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { R5dctlApiError, R5dctlClient, type R5dctlConversationForkResponse, type R5dctlConversationHeadResponse, type R5dctlConversationRenderOptions, type R5dctlConversationResponse, type R5dctlConversationNodeResponse, type R5dctlConversationOverviewResponse, type R5dctlConversationWorkDetail, type R5dctlConversationWorkResponse, type R5dctlEnvData, type R5dctlProcessInspection, type R5dctlProcessListInput, type R5dctlProcessLog, type R5dctlProcessRun, type R5dctlWorkerCapacityReport, type R5dctlBranch, type R5dctlBranchDeletionStatus, type R5dctlBranchDescription, type R5dctlBranchOperation, type R5dctlSessionSummary, type R5dctlCheckoutPathResponse, type R5dctlSessionClaim, type R5dctlSessionClaimListResponse, type R5dctlSessionClaimScope, type R5dctlSessionEvent, type R5dctlSessionStartInput, type R5dctlWorkspaceStatus, type R5dctlWorkspaceSyncResult, type R5dctlWorkingTreeMode, type R5dctlWorktreeSource, type R5dctlPromptDelivery, type R5dctlSessionMode, type ChatMode, type ModelTier } from "@ricsam/r5d-api";
|
|
1
|
+
import { R5dctlApiError, R5dctlClient, type R5dctlConversationForkResponse, type R5dctlConversationHeadResponse, type R5dctlConversationRenderOptions, type R5dctlConversationResponse, type R5dctlConversationNodeResponse, type R5dctlConversationOverviewResponse, type R5dctlConversationWorkDetail, type R5dctlConversationWorkResponse, type R5dctlEnvData, type R5dctlProcessInspection, type R5dctlProcessListInput, type R5dctlProcessLog, type R5dctlProcessRun, type R5dctlWorkerCapacityReport, type R5dctlBranch, type R5dctlBranchDeletionStatus, type R5dctlBranchDescription, type R5dctlBranchOperation, type R5dctlSessionSummary, type R5dctlCheckoutPathResponse, type R5dctlSessionClaim, type R5dctlSessionClaimListResponse, type R5dctlSessionClaimScope, type R5dctlSessionEvent, type R5dctlSessionStartInput, type R5dctlWorkspaceStatus, type R5dctlWorkspaceSyncResult, type R5dctlWorkingTreeMode, type R5dctlWorktreeSource, type R5dctlPromptDelivery, type R5dctlMessageDelivery, type R5dctlSessionMode, type ChatMode, type ModelTier } from "@ricsam/r5d-api";
|
|
2
2
|
import { type AccountCommand } from "./account";
|
|
3
3
|
export type GlobalOptions = {
|
|
4
4
|
baseUrl?: string;
|
|
@@ -359,7 +359,7 @@ export type R5dctlCommandResponse = {
|
|
|
359
359
|
/** Legacy fields older servers still send; never rendered because a reported hash goes stale. */
|
|
360
360
|
headCommit?: string | null;
|
|
361
361
|
headCommitError?: string;
|
|
362
|
-
|
|
362
|
+
messageDelivery?: R5dctlMessageDelivery;
|
|
363
363
|
error?: string;
|
|
364
364
|
summary?: string;
|
|
365
365
|
diffSummary?: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ricsam/r5dctl",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.135",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/cjs/cli.cjs",
|
|
6
6
|
"module": "./dist/mjs/cli.mjs",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"r5dctl": "dist/cjs/main.cjs"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@ricsam/r5d-api": "^0.0.
|
|
29
|
+
"@ricsam/r5d-api": "^0.0.135",
|
|
30
30
|
"dotenv": "^17",
|
|
31
31
|
"qrcode": "^1.5.4",
|
|
32
32
|
"ws": "^8.18.3"
|