@meistrari/remy-cli 1.22.0-rc.1 → 1.22.0-rc.2

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.
Files changed (3) hide show
  1. package/README.md +15 -3
  2. package/dist/remy.js +410 -100
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -62,7 +62,7 @@ In a session, `Esc` dismisses open command/path suggestions or help before it ca
62
62
 
63
63
  Every terminal follow-up is appended as an explicit **Steer**, so it targets the work currently in progress. Queuing a message for a later turn (**Next**) is Web-only for now. Remy can replay sessions containing queued-message dispatch, withdrawal, and failure events. Messages withdrawn or failed before reaching Remy show an activity notice; an active submission ends as cancelled or failed instead of waiting for a turn that never started.
64
64
 
65
- Use Up/Down to select a session in the dashboard, Left/Right to load the adjacent page, and `Enter` to open it. Remy loads the complete retained conversation before showing the session view, then streams new activity as it arrives. Press `s` to search the loaded page by session ID, title, or number; `Enter` keeps the search active and `Esc` clears it. Press `f` to filter sessions by lifecycle status or `a` to filter by author. Both pickers use checkboxes: Up/Down or `j`/`k` moves, `Space` toggles, Right/`l` checks, Left/`h` clears, `G` and `gg` jump to the last and first option, and `Enter` applies every checked value. An empty picker means all values. Press `s` inside either picker to fuzzy-search its options; `Enter` keeps the matching list and `Esc` cancels the search. Press `Esc` outside search to discard staged checkbox changes. Filters apply to the complete remote result set and reset pagination to its first page. After the dashboard list or repository-review list has focus, Vim keys work too: `j`/`k` move, `h`/`l` go to the previous/next dashboard page (or clear/mark a repository), `G` goes to the last row, and `gg` goes to the first row. Press `Shift+L` in the dashboard to start a logout confirmation. In any message composer, plain `Enter` and `Shift+Enter` submit; `Option+Enter` on macOS (`Alt+Enter` elsewhere) inserts a newline. `Ctrl+J` also inserts a newline for terminals that do not report the Option/Alt modifier. The conversation header keeps the numbered session title on its first line and repository, pull request, work state, elapsed time, and connection state on its second line. The header also keeps the last provider-reported context observation across reconnects, such as `Context 50% left · 62,000 used`; when the provider does not report a context window, it shows only used tokens. This is an observation, not a live occupancy guarantee or billing total.
65
+ Use Up/Down to select a session in the dashboard, Left/Right to load the adjacent page, and `Enter` to open it. Remy loads the complete retained conversation before showing the session view, then streams new activity as it arrives. Press `s` to search the loaded page by session ID, title, or number; `Enter` keeps the search active and `Esc` clears it. Press `f` to filter sessions by lifecycle status or `a` to filter by author. Both pickers use checkboxes: Up/Down or `j`/`k` moves, `Space` toggles, Right/`l` checks, Left/`h` clears, `G` and `gg` jump to the last and first option, and `Enter` applies every checked value. An empty picker means all values. Press `s` inside either picker to fuzzy-search its options; `Enter` keeps the matching list and `Esc` cancels the search. Press `Esc` outside search to discard staged checkbox changes. Filters apply to the complete remote result set and reset pagination to its first page. After the dashboard list or repository-review list has focus, Vim keys work too: `j`/`k` move, `h`/`l` go to the previous/next dashboard page (or clear/mark a repository), `G` goes to the last row, and `gg` goes to the first row. Press `Shift+L` in the dashboard to start a logout confirmation. In any message composer, plain `Enter` and `Shift+Enter` submit; `Option+Enter` on macOS (`Alt+Enter` elsewhere) inserts a newline. `Ctrl+J` also inserts a newline for terminals that do not report the Option/Alt modifier. The conversation header keeps the numbered session title on its first line and repository, pull request, work state, elapsed time, connection state, and the agent's current `Harness · Model · Effort` on its second line. The header also keeps the last provider-reported context observation across reconnects, such as `Context 50% left · 62,000 used`; when the provider does not report a context window, it shows only used tokens. This is an observation, not a live occupancy guarantee or billing total.
66
66
 
67
67
  If a connected session loses its activity stream, Remy retries for up to two minutes and resumes from the last received event. After the live stream stays connected for at least 30 seconds, the next disconnect starts a fresh retry window. Connections that drop sooner share the existing window and backoff. While a submitted message is still awaiting its outcome, retries continue beyond that window.
68
68
 
@@ -86,6 +86,8 @@ Terminal previews show up to 50 content blocks and 8,000 characters per block, w
86
86
 
87
87
  Command and attachment feedback share progress, success, failure, and attention indicators. Failed stop requests show the reason and `esc` retry; failed complete/cancel requests restore the message prompt and show the retry command. Logout shows a confirmation with `y` to confirm and `Esc` to cancel.
88
88
 
89
+ **Change the model or effort:** enter `/model` or `/effort` to pick a new model or reasoning effort for your next message. The model list contains only the session's harness (a Claude session offers Claude models, a Codex session Codex models), in the same order and with the same labels as the new-session wizard. Arrows move, `Enter` stages the pick, and `Esc` closes the picker without changing what is already staged. Nothing is sent until you send a message: the change travels with it and stays the session's setting afterward. Because every terminal follow-up is a Steer, a change sent while Remy is working switches the running turn from its next model request; otherwise it applies from the turn your message starts. The header shows the staged change as `→ Sonnet 5 · High (next message)`, then `(switching…)` until the runtime applies it. The timeline then records `Switched to Sonnet 5 · High` right after your message, or `Switched to Sonnet 5 · High (from next turn)` when the running turn could not switch; its details (`Ctrl+O`) then show the provider's reason when one was given. Picking the current value removes it from the staged change.
90
+
89
91
  Enter `/compact` to admit a durable main-thread context compaction control without sending a human message. Remy reports request admission immediately and shows the eventual completed, no-op, failed, or unknown provider result in retained activity. Compaction waits for a safe point and does not interrupt active work or jump ahead of an earlier Next message. While admission is pending, you can keep editing or open other commands; repeated `/compact` invocations do not send another request.
90
92
 
91
93
  Drag to select visible text in any Remy view; Remy copies it to the local clipboard and emits OSC 52 for terminal or remote-session clipboard support, then clears the selection highlight. Published file artifacts show their filename and muted, full web preview URL; open the URL in a browser to select that artifact in its session. Tela Page rows retain their canonical Page URL. Downloads are explicit through `/files`; artifact bytes are never printed in the terminal. When the conversation has focus, press `Tab` to return to the composer.
@@ -96,6 +98,8 @@ Drag to select visible text in any Remy view; Remy copies it to the local clipbo
96
98
  | `/complete` | Complete an open session after its active turn stops. |
97
99
  | `/cancel` | Cancel the current session, including an active turn. |
98
100
  | `/compact` | Admit a main-thread context compaction request without sending a message; the provider result appears in retained activity. |
101
+ | `/model` | Pick a model of the session's harness for your next message. |
102
+ | `/effort` | Pick a reasoning effort for your next message. |
99
103
  | `/sessions` | Return to the dashboard. |
100
104
  | `/new` | Start another new-session flow. |
101
105
  | `/logout` | Sign out of Remy after confirmation. |
@@ -126,7 +130,7 @@ The interactive dashboard is the recommended path. Use these commands when a scr
126
130
  remy new --repository owner/repository "Add a health check endpoint"
127
131
  ```
128
132
 
129
- Repeat `--repository` to select more repositories from the same GitHub installation. If you do not select a repository, supply `--installation <github-installation-id>`. Repeat `--attach <path>` to upload files or directories; the 20 MiB per-attachment limit is measured directly for a file and across all regular files before compressing a directory. Remy sends each accepted directory as a gzip-compressed tar archive. Direct creation uses `medium` reasoning unless `--reasoning-effort` overrides it; omit `--model` to use the server's Codex `gpt-6-astra` default. `--model` accepts a Codex model (`gpt-6-astra`, `gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna`, `gpt-5.5`, `gpt-5.4`, `gpt-5.4-mini`, `gpt-5.2`) or a Claude model (`claude-opus-5-5`, `claude-sonnet-5`, `claude-fable-5-1`); the model chooses the session's harness, which stays fixed for the session. For example, `remy new --model claude-opus-5-5 --repository owner/repository "Add a health check endpoint"` starts a Claude session. In the interactive wizard, press `m` on the confirm step to cycle through every harness's models, shown as `Codex · GPT 6.0 Astra` or `Claude · Opus 5.5`. `remy new` creates the session from the prompt and flags, then opens it when the terminal is interactive.
133
+ Repeat `--repository` to select more repositories from the same GitHub installation. If you do not select a repository, supply `--installation <github-installation-id>`. Repeat `--attach <path>` to upload files or directories; the 20 MiB per-attachment limit is measured directly for a file and across all regular files before compressing a directory. Remy sends each accepted directory as a gzip-compressed tar archive. Direct creation uses `medium` reasoning unless `--reasoning-effort` overrides it; omit `--model` to use the server's Codex `gpt-6-astra` default. `--model` accepts a Codex model (`gpt-6-astra`, `gpt-6-sol`, `gpt-6-luna`, `gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna`, `gpt-5.5`) or a Claude model (`claude-opus-5-5`, `claude-sonnet-5`, `claude-fable-5-1`); the model chooses the session's harness, which stays fixed for the session. For example, `remy new --model claude-opus-5-5 --repository owner/repository "Add a health check endpoint"` starts a Claude session. In the interactive wizard, press `m` on the confirm step to cycle through every harness's models, shown as `Codex · GPT 6.0 Astra` or `Claude · Opus 5.5`. `remy new` creates the session from the prompt and flags, then opens it when the terminal is interactive.
130
134
 
131
135
  The branch-suggestion flow belongs to the interactive new-session wizard (`remy`, then `n`). Direct `remy new` creation does not analyze continuation references.
132
136
 
@@ -144,6 +148,14 @@ remy --session <session-id> "Add regression coverage for that fix"
144
148
 
145
149
  Remy sends the prompt as **Steer**, then opens the conversation. Omitting the prompt only attaches. An explicitly empty prompt is rejected. Use `--` before a prompt beginning with `--` to treat it as text.
146
150
 
151
+ To change the model or reasoning effort, pass `--model <name>`, `--reasoning-effort <low|medium|high|xhigh>`, or both with the prompt:
152
+
153
+ ```bash
154
+ remy --session <session-id> --model claude-sonnet-5 --reasoning-effort high "Finish the migration"
155
+ ```
156
+
157
+ The change travels with that message and stays the session's setting for later messages. A running turn switches from its next model request; otherwise the change applies from the turn the prompt starts. Either flag without a prompt is rejected, because settings only change with a message. The session keeps its harness, so `--model` must name one of that harness's models; Remy checks this against the session before sending and names the harness's models when it does not match. Omit both flags to keep the current settings.
158
+
147
159
  ### Automate a session
148
160
 
149
161
  `remy new` and `remy --session` use JSON Lines output automatically when standard input or output is not a terminal. Pass `--no-tui` to choose that mode explicitly; `--json` also selects it.
@@ -201,7 +213,7 @@ remy update
201
213
  remy --version | -v
202
214
  remy dashboard
203
215
  remy new [--repository <owner/name> ... --installation <id> --model <name> --reasoning-effort <low|medium|high|xhigh> --attach <path> ... --no-tui --json] <prompt>
204
- remy --session <session-id> [--no-tui] [--json] [prompt]
216
+ remy --session <session-id> [--model <name> --reasoning-effort <low|medium|high|xhigh> --no-tui --json] [prompt]
205
217
  ```
206
218
 
207
219
  Run `remy <command> --help` for flags and command-specific usage. `remy whoami` prints the saved signed-in identity and organization.
package/dist/remy.js CHANGED
@@ -32161,6 +32161,51 @@ var agentPromptSchema = zod_default2.object({
32161
32161
  blocks: zod_default2.array(wireAgentInputBlockSchema).min(1)
32162
32162
  });
32163
32163
 
32164
+ // ../../packages/agents-protocol/src/agent-model.ts
32165
+ var codexModelIds = [
32166
+ "gpt-6-astra",
32167
+ "gpt-6-sol",
32168
+ "gpt-6-luna",
32169
+ "gpt-5.6-sol",
32170
+ "gpt-5.6-terra",
32171
+ "gpt-5.6-luna",
32172
+ "gpt-5.5",
32173
+ "gpt-5.4",
32174
+ "gpt-5.4-mini",
32175
+ "gpt-5.2"
32176
+ ];
32177
+ var claudeModelIds = [
32178
+ "claude-opus-5-5",
32179
+ "claude-sonnet-5",
32180
+ "claude-fable-5-1",
32181
+ "claude-opus-5",
32182
+ "claude-fable-5",
32183
+ "sonnet",
32184
+ "claude-opus-4-8",
32185
+ "opus",
32186
+ "haiku",
32187
+ "claude-opus-4-7",
32188
+ "claude-mythos-preview",
32189
+ "claude-opus-4-6",
32190
+ "claude-sonnet-4-6",
32191
+ "claude-haiku-4-5",
32192
+ "claude-haiku-4-5-20251001",
32193
+ "claude-opus-4-5",
32194
+ "claude-opus-4-5-20251101",
32195
+ "claude-sonnet-4-5",
32196
+ "claude-sonnet-4-5-20250929",
32197
+ "claude-opus-4-1",
32198
+ "claude-opus-4-1-20250805",
32199
+ "claude-opus-4-0",
32200
+ "claude-opus-4-20250514",
32201
+ "claude-sonnet-4-0",
32202
+ "claude-sonnet-4-20250514",
32203
+ "claude-3-haiku-20240307"
32204
+ ];
32205
+ var codexModelIdSchema = zod_default2.enum(codexModelIds);
32206
+ var claudeModelIdSchema = zod_default2.enum(claudeModelIds);
32207
+ var agentModelIdSchema = zod_default2.union([codexModelIdSchema, claudeModelIdSchema]);
32208
+
32164
32209
  // ../../packages/agents-protocol/src/agent-provider.ts
32165
32210
  var agentProviderIdSchema = zod_default2.enum(["codex", "claude"]);
32166
32211
 
@@ -32346,6 +32391,12 @@ function objectRejectingKeys(shape, rejectedKeys) {
32346
32391
  // ../../packages/agents-protocol/src/agent-command.ts
32347
32392
  var agentReasoningEffortSchema = zod_default2.enum(["low", "medium", "high", "xhigh", "max"]);
32348
32393
  var agentPromptModeSchema = zod_default2.enum(["next", "steer"]);
32394
+ var agentSettingsChangeSchema = zod_default2.strictObject({
32395
+ model: agentModelIdSchema.optional(),
32396
+ reasoningEffort: agentReasoningEffortSchema.optional()
32397
+ }).refine((settings) => settings.model !== undefined || settings.reasoningEffort !== undefined, {
32398
+ message: "A settings change needs a model or a reasoning effort."
32399
+ });
32349
32400
  var agentAuthorSchema = zod_default2.object({
32350
32401
  name: zod_default2.string().min(1),
32351
32402
  email: zod_default2.string().min(1).optional(),
@@ -32363,7 +32414,8 @@ var sendPromptCommandSchema = runningCommandBaseSchema.extend({
32363
32414
  origin: zod_default2.enum(["user", "supervisor"]).default("user"),
32364
32415
  prompt: agentPromptSchema,
32365
32416
  commandId: commandIdSchema,
32366
- author: agentAuthorSchema.optional()
32417
+ author: agentAuthorSchema.optional(),
32418
+ settings: agentSettingsChangeSchema.optional()
32367
32419
  });
32368
32420
  var agentNotifyCommandSchema = runningCommandBaseSchema.extend({
32369
32421
  type: zod_default2.literal("agent.notify"),
@@ -32413,107 +32465,57 @@ var agentCommandSchema = zod_default2.union([
32413
32465
  respondUserInputAgentCommandSchema
32414
32466
  ]);
32415
32467
 
32416
- // ../../packages/agents-protocol/src/agent-model.ts
32417
- var codexModelIds = [
32418
- "gpt-6-astra",
32419
- "gpt-5.6-sol",
32420
- "gpt-5.6-terra",
32421
- "gpt-5.6-luna",
32422
- "gpt-5.5",
32423
- "gpt-5.4",
32424
- "gpt-5.4-mini",
32425
- "gpt-5.2"
32426
- ];
32427
- var claudeModelIds = [
32428
- "claude-opus-5-5",
32429
- "claude-sonnet-5",
32430
- "claude-fable-5-1",
32431
- "claude-opus-5",
32432
- "claude-fable-5",
32433
- "sonnet",
32434
- "claude-opus-4-8",
32435
- "opus",
32436
- "haiku",
32437
- "claude-opus-4-7",
32438
- "claude-mythos-preview",
32439
- "claude-opus-4-6",
32440
- "claude-sonnet-4-6",
32441
- "claude-haiku-4-5",
32442
- "claude-haiku-4-5-20251001",
32443
- "claude-opus-4-5",
32444
- "claude-opus-4-5-20251101",
32445
- "claude-sonnet-4-5",
32446
- "claude-sonnet-4-5-20250929",
32447
- "claude-opus-4-1",
32448
- "claude-opus-4-1-20250805",
32449
- "claude-opus-4-0",
32450
- "claude-opus-4-20250514",
32451
- "claude-sonnet-4-0",
32452
- "claude-sonnet-4-20250514",
32453
- "claude-3-haiku-20240307"
32454
- ];
32455
- var codexModelIdSchema = zod_default2.enum(codexModelIds);
32456
- var claudeModelIdSchema = zod_default2.enum(claudeModelIds);
32457
- var agentModelIdSchema = zod_default2.union([codexModelIdSchema, claudeModelIdSchema]);
32458
-
32459
32468
  // ../../packages/agents-protocol/src/agent-presentation.ts
32460
32469
  var CODEX_CONTEXT_WINDOW = 272000;
32461
32470
  var codexModelPresentation = {
32462
32471
  "gpt-6-astra": {
32463
32472
  label: "GPT 6.0 Astra",
32464
- description: "Our most capable model for complex, demanding work.",
32473
+ description: "Frontier intelligence for the most demanding work.",
32465
32474
  relativeCost: 5,
32466
32475
  maxInputTokens: CODEX_CONTEXT_WINDOW,
32467
32476
  isMultiModal: true
32468
32477
  },
32478
+ "gpt-6-sol": {
32479
+ label: "GPT 6.0 Sol",
32480
+ description: "Workhorse model for coding and everyday work.",
32481
+ relativeCost: 4,
32482
+ maxInputTokens: CODEX_CONTEXT_WINDOW,
32483
+ isMultiModal: true
32484
+ },
32485
+ "gpt-6-luna": {
32486
+ label: "GPT 6.0 Luna",
32487
+ description: "Fast and affordable model for easier tasks.",
32488
+ relativeCost: 2,
32489
+ maxInputTokens: CODEX_CONTEXT_WINDOW,
32490
+ isMultiModal: true
32491
+ },
32469
32492
  "gpt-5.6-sol": {
32470
32493
  label: "GPT 5.6 Sol",
32471
- description: "Reliable agentic workhorse for everyday tasks.",
32494
+ description: "Older coding model for complex work.",
32472
32495
  relativeCost: 5,
32473
32496
  maxInputTokens: CODEX_CONTEXT_WINDOW,
32474
32497
  isMultiModal: true
32475
32498
  },
32476
32499
  "gpt-5.6-terra": {
32477
32500
  label: "GPT 5.6 Terra",
32478
- description: "Balanced agentic coding model for everyday work.",
32501
+ description: "Older balanced model for straightforward work.",
32479
32502
  relativeCost: 3,
32480
32503
  maxInputTokens: CODEX_CONTEXT_WINDOW,
32481
32504
  isMultiModal: true
32482
32505
  },
32483
32506
  "gpt-5.6-luna": {
32484
32507
  label: "GPT 5.6 Luna",
32485
- description: "Fast and affordable agentic coding model.",
32508
+ description: "Older fast and efficient model.",
32486
32509
  relativeCost: 2,
32487
32510
  maxInputTokens: CODEX_CONTEXT_WINDOW,
32488
32511
  isMultiModal: true
32489
32512
  },
32490
32513
  "gpt-5.5": {
32491
32514
  label: "GPT 5.5",
32492
- description: "Proven previous-generation model for coding and general work.",
32515
+ description: "Legacy coding model.",
32493
32516
  relativeCost: 4,
32494
32517
  maxInputTokens: CODEX_CONTEXT_WINDOW,
32495
32518
  isMultiModal: true
32496
- },
32497
- "gpt-5.4": {
32498
- label: "GPT 5.4",
32499
- description: "Strong model for everyday coding.",
32500
- relativeCost: 3,
32501
- maxInputTokens: CODEX_CONTEXT_WINDOW,
32502
- isMultiModal: true
32503
- },
32504
- "gpt-5.4-mini": {
32505
- label: "GPT 5.4 Mini",
32506
- description: "Small, fast, and cost-efficient model for simpler coding tasks.",
32507
- relativeCost: 2,
32508
- maxInputTokens: CODEX_CONTEXT_WINDOW,
32509
- isMultiModal: true
32510
- },
32511
- "gpt-5.2": {
32512
- label: "GPT-5.2",
32513
- description: "Optimized for professional work and long-running agents.",
32514
- relativeCost: 3,
32515
- maxInputTokens: CODEX_CONTEXT_WINDOW,
32516
- isMultiModal: true
32517
32519
  }
32518
32520
  };
32519
32521
  var CLAUDE_CONTEXT_WINDOW = 1e6;
@@ -32553,7 +32555,15 @@ var agentReasoningEffortPresentation = {
32553
32555
  var selectableAgentReasoningEfforts = ["low", "medium", "high", "xhigh"];
32554
32556
 
32555
32557
  // ../../packages/agents-protocol/src/agent-catalog.ts
32556
- var selectableCodexModelIds = codexModelIds;
32558
+ var selectableCodexModelIds = [
32559
+ "gpt-6-astra",
32560
+ "gpt-6-sol",
32561
+ "gpt-6-luna",
32562
+ "gpt-5.6-sol",
32563
+ "gpt-5.6-terra",
32564
+ "gpt-5.6-luna",
32565
+ "gpt-5.5"
32566
+ ];
32557
32567
  var selectableClaudeModelIds = [
32558
32568
  "claude-opus-5-5",
32559
32569
  "claude-sonnet-5",
@@ -32901,6 +32911,19 @@ var sessionConfiguredAgentEventSchema = agentSessionEventBaseSchema.extend({
32901
32911
  environment: zod_default2.record(zod_default2.string().min(1), zod_default2.literal("[REDACTED]"))
32902
32912
  })
32903
32913
  });
32914
+ var agentSettingsAppliesFromSchema = zod_default2.enum(["current_turn", "next_turn"]);
32915
+ var currentTurnUnavailableReasonMaxLength = 300;
32916
+ var sessionSettingsChangedAgentEventSchema = agentSessionEventBaseSchema.extend({
32917
+ type: zod_default2.literal("session.settings.changed"),
32918
+ payload: zod_default2.object({
32919
+ model: zod_default2.string().min(1),
32920
+ reasoningEffort: zod_default2.string().min(1).optional(),
32921
+ commandId: zod_default2.string().min(1),
32922
+ turnId: zod_default2.string().min(1).optional(),
32923
+ appliesFrom: agentSettingsAppliesFromSchema,
32924
+ currentTurnUnavailableReason: zod_default2.string().min(1).max(currentTurnUnavailableReasonMaxLength).optional()
32925
+ })
32926
+ });
32904
32927
  var sessionSkillsUpdatedAgentEventSchema = agentSessionEventBaseSchema.extend({
32905
32928
  type: zod_default2.literal("session.skills.updated"),
32906
32929
  payload: zod_default2.object({
@@ -33149,6 +33172,7 @@ var errorAgentEventSchema = agentSessionEventBaseSchema.extend({
33149
33172
  var agentEventSchema = zod_default2.discriminatedUnion("type", [
33150
33173
  sessionStartedAgentEventSchema,
33151
33174
  sessionConfiguredAgentEventSchema,
33175
+ sessionSettingsChangedAgentEventSchema,
33152
33176
  sessionSkillsUpdatedAgentEventSchema,
33153
33177
  sessionStateChangedAgentEventSchema,
33154
33178
  sessionEndedAgentEventSchema,
@@ -33282,12 +33306,17 @@ var createSessionResponseSchema = exports_external2.strictObject({
33282
33306
  messages: exports_external2.tuple([sessionMessageSchema]),
33283
33307
  connections: exports_external2.array(sessionConnectionSchema).max(1)
33284
33308
  });
33309
+ var appendSessionMessageAgentInputSchema = exports_external2.strictObject({
33310
+ model: exports_external2.string().min(1).optional(),
33311
+ reasoningEffort: agentReasoningEffortSchema.optional()
33312
+ }).refine((agent) => agent.model !== undefined || agent.reasoningEffort !== undefined, { message: "agent must change the model, the reasoning effort, or both" });
33285
33313
  var appendSessionMessageInputSchema = exports_external2.strictObject({
33286
33314
  sessionId: exports_external2.string().min(1),
33287
33315
  text: exports_external2.string().refine((value) => value.trim().length > 0),
33288
33316
  fileIds: exports_external2.array(exports_external2.string().min(1)),
33289
33317
  idempotencyKey: exports_external2.string().min(1).max(128),
33290
- mode: exports_external2.enum(["next", "steer"]).optional()
33318
+ mode: exports_external2.enum(["next", "steer"]).optional(),
33319
+ agent: appendSessionMessageAgentInputSchema.optional()
33291
33320
  });
33292
33321
  var appendSessionMessageResponseSchema = exports_external2.strictObject({
33293
33322
  message: sessionMessageSchema
@@ -33341,7 +33370,17 @@ var sessionDetailResponseSchema = exports_external2.object({
33341
33370
  full_name: exports_external2.string()
33342
33371
  })),
33343
33372
  connections: exports_external2.array(sessionConnectionSchema).max(1),
33344
- artifacts: exports_external2.array(sessionArtifactSchema)
33373
+ artifacts: exports_external2.array(sessionArtifactSchema),
33374
+ agent: exports_external2.object({
33375
+ provider: exports_external2.string().min(1),
33376
+ model: exports_external2.string(),
33377
+ reasoning: exports_external2.string().nullable()
33378
+ }).nullable().optional(),
33379
+ agent_pending: exports_external2.object({
33380
+ model: exports_external2.string(),
33381
+ reasoning_effort: exports_external2.string().nullable(),
33382
+ message_id: exports_external2.string()
33383
+ }).nullable().optional()
33345
33384
  }).passthrough();
33346
33385
  var sessionSourceSchema = exports_external2.discriminatedUnion("provider", [
33347
33386
  exports_external2.object({
@@ -33444,7 +33483,13 @@ async function appendSessionMessage({
33444
33483
  body: JSON.stringify({
33445
33484
  text: parsedInput.text,
33446
33485
  file_ids: parsedInput.fileIds,
33447
- ...parsedInput.mode === undefined ? {} : { mode: parsedInput.mode }
33486
+ ...parsedInput.mode === undefined ? {} : { mode: parsedInput.mode },
33487
+ ...parsedInput.agent === undefined ? {} : {
33488
+ agent: {
33489
+ ...parsedInput.agent.model === undefined ? {} : { model: parsedInput.agent.model },
33490
+ ...parsedInput.agent.reasoningEffort === undefined ? {} : { reasoning_effort: parsedInput.agent.reasoningEffort }
33491
+ }
33492
+ }
33448
33493
  })
33449
33494
  });
33450
33495
  return parseAppendSessionMessageResponse(await parseJson2(response, "Session message append response was not valid JSON."));
@@ -34351,7 +34396,7 @@ function formatSessionJson({
34351
34396
  `;
34352
34397
  }
34353
34398
  // src/version.ts
34354
- var remyCliVersion = "1.22.0-rc.1";
34399
+ var remyCliVersion = "1.22.0-rc.2";
34355
34400
 
34356
34401
  // src/self-update.ts
34357
34402
  import { spawn } from "child_process";
@@ -34489,7 +34534,7 @@ async function confirmCliUpdate({ version: version5, signal, interrupt }) {
34489
34534
  // src/session-agent-options.ts
34490
34535
  function newSessionModelLabel(model) {
34491
34536
  const provider = newSessionModelHarness(model);
34492
- return `${agentHarnesses[provider].label} \xB7 ${selectableAgentModelPresentation(model)?.label ?? model}`;
34537
+ return `${agentHarnesses[provider].label} \xB7 ${agentModelLabel(model)}`;
34493
34538
  }
34494
34539
  function newSessionReasoningEffortLabel(effort) {
34495
34540
  return agentReasoningEffortPresentation[effort].label;
@@ -34505,6 +34550,37 @@ var newSessionReasoningEfforts = selectableAgentReasoningEfforts;
34505
34550
  function parseNewSessionModel(value) {
34506
34551
  return newSessionModels.find((model) => model === value);
34507
34552
  }
34553
+ function parseNewSessionReasoningEffort(value) {
34554
+ return newSessionReasoningEfforts.find((effort) => effort === value);
34555
+ }
34556
+ function sessionAgentChange({ model, reasoningEffort }) {
34557
+ if (model !== undefined)
34558
+ return { model, ...reasoningEffort === undefined ? {} : { reasoningEffort } };
34559
+ if (reasoningEffort !== undefined)
34560
+ return { reasoningEffort };
34561
+ return;
34562
+ }
34563
+ function sessionHarnessModels(provider) {
34564
+ const parsed = agentProviderIdSchema.safeParse(provider);
34565
+ return parsed.success ? agentHarnesses[parsed.data].models : [];
34566
+ }
34567
+ function agentHarnessLabel(provider) {
34568
+ const parsed = agentProviderIdSchema.safeParse(provider);
34569
+ return parsed.success ? agentHarnesses[parsed.data].label : provider;
34570
+ }
34571
+ function agentModelLabel(model) {
34572
+ return selectableAgentModelPresentation(model)?.label ?? model;
34573
+ }
34574
+ function agentModelDescription(model) {
34575
+ return selectableAgentModelPresentation(model)?.description ?? "";
34576
+ }
34577
+ function agentReasoningEffortLabel(effort) {
34578
+ const parsed = agentReasoningEffortSchema.safeParse(effort);
34579
+ return parsed.success ? agentReasoningEffortPresentation[parsed.data].label : effort;
34580
+ }
34581
+ function agentReasoningEffortDescription(effort) {
34582
+ return agentReasoningEffortPresentation[effort].description;
34583
+ }
34508
34584
  var defaultNewSessionAgent = {
34509
34585
  model: defaultAgentSelection.model,
34510
34586
  reasoningEffort: defaultAgentSelection.reasoningEffort
@@ -34522,6 +34598,9 @@ var wrappedSessionStartedAgentEventSchema = sessionStartedAgentEventSchema.exten
34522
34598
  var wrappedSessionConfiguredAgentEventSchema = sessionConfiguredAgentEventSchema.extend({
34523
34599
  type: zod_default2.literal("agent.session.configured")
34524
34600
  });
34601
+ var wrappedSessionSettingsChangedAgentEventSchema = sessionSettingsChangedAgentEventSchema.extend({
34602
+ type: zod_default2.literal("agent.session.settings.changed")
34603
+ });
34525
34604
  var wrappedSessionSkillsUpdatedAgentEventSchema = sessionSkillsUpdatedAgentEventSchema.extend({
34526
34605
  type: zod_default2.literal("agent.session.skills.updated")
34527
34606
  });
@@ -34606,6 +34685,7 @@ var wrappedErrorAgentEventSchema = errorAgentEventSchema.extend({
34606
34685
  var wrappedAgentEventSchema = zod_default2.discriminatedUnion("type", [
34607
34686
  wrappedSessionStartedAgentEventSchema,
34608
34687
  wrappedSessionConfiguredAgentEventSchema,
34688
+ wrappedSessionSettingsChangedAgentEventSchema,
34609
34689
  wrappedSessionSkillsUpdatedAgentEventSchema,
34610
34690
  wrappedSessionStateChangedAgentEventSchema,
34611
34691
  wrappedSessionEndedAgentEventSchema,
@@ -34795,6 +34875,8 @@ function createSessionViewState({ detail, activeMessageId }) {
34795
34875
  ...detail.agentStatus === undefined ? {} : { agentStatus: detail.agentStatus },
34796
34876
  connectionStatus: "connected",
34797
34877
  pullRequest: toSessionPullRequest(detail),
34878
+ agent: toSessionAgentSettings(detail.agent),
34879
+ agentPending: toSessionAgentPendingSettings(detail.agent_pending),
34798
34880
  ...activeMessageId !== undefined ? { activeMessageId } : {},
34799
34881
  seenRetainedEventIds: {},
34800
34882
  transcript: [],
@@ -34802,6 +34884,7 @@ function createSessionViewState({ detail, activeMessageId }) {
34802
34884
  connectionPreviews: toConnectionPreviews(detail),
34803
34885
  previews: { assistantText: "", reasoningText: "" },
34804
34886
  commandAuthorLabels: {},
34887
+ commandMessageIds: {},
34805
34888
  work: { items: [] },
34806
34889
  workTurnBaselines: {},
34807
34890
  messageTurns: {},
@@ -34831,6 +34914,8 @@ function updateSessionDetail({ state, detail }) {
34831
34914
  aggregateStatus: detail.status,
34832
34915
  ...detail.agentStatus === undefined ? {} : { agentStatus: detail.agentStatus },
34833
34916
  pullRequest: toSessionPullRequest(detail),
34917
+ ...detail.agent === undefined ? {} : { agent: toSessionAgentSettings(detail.agent) },
34918
+ ...detail.agent_pending === undefined ? {} : { agentPending: toSessionAgentPendingSettings(detail.agent_pending) },
34834
34919
  connectionPreviews: toConnectionPreviews(detail)
34835
34920
  };
34836
34921
  }
@@ -35035,6 +35120,7 @@ function projectSessionMessageCreated({ state, event, occurredAt }) {
35035
35120
  return {
35036
35121
  ...state,
35037
35122
  commandAuthorLabels: { ...state.commandAuthorLabels, [event.payload.commandId]: creatorLabel },
35123
+ commandMessageIds: { ...state.commandMessageIds, [event.payload.commandId]: event.payload.messageId },
35038
35124
  transcript: [...state.transcript, {
35039
35125
  kind: "message",
35040
35126
  messageId: event.payload.messageId,
@@ -35148,6 +35234,12 @@ function projectDurableAgentEvent({ state, event, occurredAt, retainedEventId, e
35148
35234
  return projectAgentMessageEnded({ state, event: agentEvent, occurredAt, retainedEventId, eventUrl });
35149
35235
  if (agentEvent.type === "agent.context.updated")
35150
35236
  return state;
35237
+ if (agentEvent.type === "agent.session.configured" || agentEvent.type === "agent.session.settings.changed") {
35238
+ const agent = { provider: agentEvent.provider, model: agentEvent.payload.model, reasoningEffort: agentEvent.payload.reasoningEffort ?? null };
35239
+ const stateWithAgent = { ...state, agent };
35240
+ const card2 = toAgentActivityCard(agentEvent);
35241
+ return agentEvent.type === "agent.session.settings.changed" ? placeActivityAfterCommandMessage({ state: stateWithAgent, commandId: agentEvent.payload.commandId, retainedEventId, occurredAt, card: card2, eventUrl }) : appendActivity({ state: stateWithAgent, retainedEventId, occurredAt, card: card2, eventUrl });
35242
+ }
35151
35243
  if (agentEvent.type === "agent.work.observed" && agentEvent.actor.type === "main")
35152
35244
  return projectAgentWorkObserved({ state, event: agentEvent, occurredAt });
35153
35245
  const stateWithTurnStart = agentEvent.type === "agent.turn.started" && agentEvent.actor.type === "main" ? {
@@ -35274,8 +35366,21 @@ function projectAgentMessageEnded({ state, event, occurredAt, retainedEventId, e
35274
35366
  transcript: [...state.transcript, { kind: "message", messageId, occurredAt, author, text: displayedText, attachments: [] }]
35275
35367
  };
35276
35368
  }
35277
- function appendActivity({ state, retainedEventId, occurredAt, card, eventUrl }) {
35278
- return { ...state, transcript: [...state.transcript, { kind: "activity", activityId: `activity:${retainedEventId}`, occurredAt, card, ...eventUrl === undefined ? {} : { eventUrl } }] };
35369
+ function activityItem({ retainedEventId, occurredAt, card, eventUrl }) {
35370
+ return { kind: "activity", activityId: `activity:${retainedEventId}`, occurredAt, card, ...eventUrl === undefined ? {} : { eventUrl } };
35371
+ }
35372
+ function appendActivity({ state, ...activity }) {
35373
+ return { ...state, transcript: [...state.transcript, activityItem(activity)] };
35374
+ }
35375
+ function placeActivityAfterCommandMessage({ state, commandId, ...activity }) {
35376
+ const messageId = state.commandMessageIds[commandId];
35377
+ const messageIndex = messageId === undefined ? -1 : state.transcript.findIndex((item) => item.kind === "message" && item.messageId === messageId);
35378
+ if (messageIndex === -1)
35379
+ return appendActivity({ state, ...activity });
35380
+ return {
35381
+ ...state,
35382
+ transcript: [...state.transcript.slice(0, messageIndex + 1), activityItem(activity), ...state.transcript.slice(messageIndex + 1)]
35383
+ };
35279
35384
  }
35280
35385
  function toAgentActivityCard(event) {
35281
35386
  const identity = childActivityIdentity(event);
@@ -35289,6 +35394,21 @@ function toMainAgentActivityCard(event) {
35289
35394
  return { kind: "lifecycle", weight: "noise", title: "Remy session started", summary: "Session is ready." };
35290
35395
  case "agent.session.configured":
35291
35396
  return { kind: "lifecycle", weight: "noise", title: "Remy session configured", summary: `Model ${event.payload.model}.` };
35397
+ case "agent.session.settings.changed": {
35398
+ const settings = [agentModelLabel(event.payload.model), ...event.payload.reasoningEffort === undefined ? [] : [agentReasoningEffortLabel(event.payload.reasoningEffort)]];
35399
+ const switched = `Switched to ${terminalSafeSingleLine(settings.join(" \xB7 "))}`;
35400
+ if (event.payload.appliesFrom === "current_turn")
35401
+ return { kind: "lifecycle", weight: "signal", title: switched, summary: switched };
35402
+ const title = `${switched} (from next turn)`;
35403
+ const reason = event.payload.currentTurnUnavailableReason;
35404
+ return {
35405
+ kind: "lifecycle",
35406
+ weight: "signal",
35407
+ title,
35408
+ summary: title,
35409
+ ...reason === undefined ? {} : { detail: `The running turn kept its settings: ${terminalSafeSingleLine(reason)}` }
35410
+ };
35411
+ }
35292
35412
  case "agent.session.skills.updated":
35293
35413
  return { kind: "lifecycle", weight: "noise", title: "Remy skills updated", summary: "Available skills updated." };
35294
35414
  case "agent.session.state.changed":
@@ -35449,6 +35569,7 @@ Background outcome: ${event.payload.output.success ? "completed" : "failed"}`,
35449
35569
  return createChildActivityCard({ identity, activityTitle: "Needs input", card: { kind: "approval-question", weight: "signal", summary: providerText(event.payload.prompt, "Subagent needs input."), detail: jsonDetail(event.payload.questions) } });
35450
35570
  case "agent.session.started":
35451
35571
  case "agent.session.configured":
35572
+ case "agent.session.settings.changed":
35452
35573
  case "agent.session.skills.updated":
35453
35574
  case "agent.session.state.changed":
35454
35575
  case "agent.session.ended":
@@ -35865,6 +35986,12 @@ function providerText(raw, fallback) {
35865
35986
  const cleaned = stripAnsi(raw ?? "").trim();
35866
35987
  return cleaned.length > 0 ? cleaned : fallback;
35867
35988
  }
35989
+ function toSessionAgentSettings(agent) {
35990
+ return agent ? { provider: agent.provider, model: agent.model, reasoningEffort: agent.reasoning } : null;
35991
+ }
35992
+ function toSessionAgentPendingSettings(pending) {
35993
+ return pending ? { model: pending.model, reasoningEffort: pending.reasoning_effort, messageId: pending.message_id } : null;
35994
+ }
35868
35995
  function toConnectionPreviews(detail) {
35869
35996
  return detail.connections.map((connection) => ({ provider: connection.provider, status: connection.status }));
35870
35997
  }
@@ -36236,7 +36363,7 @@ function createRemoteSessionController(dependencies) {
36236
36363
  await writeCache();
36237
36364
  cachedLastRetainedEventId = projectedState.lastRetainedEventId;
36238
36365
  }
36239
- if (frame.kind === "retained" && frame.data.event.type === "session.title.generated" && !previousState.seenRetainedEventIds[frame.id]) {
36366
+ if (frame.kind === "retained" && !previousState.seenRetainedEventIds[frame.id] && (frame.data.event.type === "session.title.generated" || settlesAgentSettings({ previousState, projectedState, frame }))) {
36240
36367
  try {
36241
36368
  await refreshDetail();
36242
36369
  } catch {}
@@ -36356,9 +36483,16 @@ function createRemoteSessionController(dependencies) {
36356
36483
  waitUntilReady,
36357
36484
  waitForTurn,
36358
36485
  recordActiveMessage,
36359
- updateDetail
36486
+ updateDetail,
36487
+ refreshDetail
36360
36488
  };
36361
36489
  }
36490
+ function settlesAgentSettings({ previousState, projectedState, frame }) {
36491
+ if (frame.data.event.type === "agent.session.settings.changed")
36492
+ return true;
36493
+ const pending = previousState.agentPending;
36494
+ return pending !== null && sessionTurnOutcome({ state: previousState, sessionMessageId: pending.messageId }).status !== "ended" && sessionTurnOutcome({ state: projectedState, sessionMessageId: pending.messageId }).status === "ended";
36495
+ }
36362
36496
  function resolveSessionCachePath({
36363
36497
  environment,
36364
36498
  sessionId
@@ -39254,6 +39388,8 @@ var composerSlashCommands = [
39254
39388
  { value: "/complete", description: "Finish this session" },
39255
39389
  { value: "/cancel", description: "Cancel this session" },
39256
39390
  { value: "/compact", description: "Compact main-thread context" },
39391
+ { value: "/model", description: "Change the model from your next message" },
39392
+ { value: "/effort", description: "Change reasoning effort from your next message" },
39257
39393
  { value: "/new", description: "Start a new session" },
39258
39394
  { value: "/logout", description: "Sign out of Remy" },
39259
39395
  { value: "/files", description: "Download artifacts and user attachments" },
@@ -39316,6 +39452,45 @@ async function createSessionTui({
39316
39452
  const steps = currentTurnActivityCount();
39317
39453
  const stepText = steps === 0 ? "" : `${steps} step${steps === 1 ? "" : "s"} \xB7 `;
39318
39454
  return renderer.width < 76 ? `${stepText}ctrl+o \xB7 esc stop` : `${stepText}ctrl+o full detail \xB7 esc to stop`;
39455
+ }, openAgentSettingsPicker = function(kind) {
39456
+ const target = agentSettingsTarget({ state: latestState, staged: composerDraft.agent });
39457
+ if (kind === "effort") {
39458
+ agentSettingsPicker = { kind, index: Math.max(0, newSessionReasoningEfforts.findIndex((effort) => effort === target.reasoningEffort)) };
39459
+ } else {
39460
+ const provider = latestState.agent?.provider;
39461
+ const models = provider === undefined ? [] : sessionHarnessModels(provider);
39462
+ if (provider === undefined || models.length === 0) {
39463
+ composerFeedback = {
39464
+ kind: "failure",
39465
+ message: provider === undefined ? "This session has not reported its harness yet, so its model cannot change." : `Remy does not offer model changes for the ${terminalSafeText(agentHarnessLabel(provider))} harness.`
39466
+ };
39467
+ render();
39468
+ return;
39469
+ }
39470
+ agentSettingsPicker = { kind, provider, models, index: Math.max(0, models.findIndex((model) => model === target.model)) };
39471
+ }
39472
+ composerFeedback = undefined;
39473
+ slashCompletions = [];
39474
+ helpVisible = false;
39475
+ pathCompletionMenuOpen = false;
39476
+ render();
39477
+ }, moveAgentSettingsPicker = function(step) {
39478
+ if (!agentSettingsPicker)
39479
+ return;
39480
+ const length = agentSettingsPicker.kind === "model" ? agentSettingsPicker.models.length : newSessionReasoningEfforts.length;
39481
+ agentSettingsPicker = { ...agentSettingsPicker, index: (agentSettingsPicker.index + step + length) % length };
39482
+ render();
39483
+ }, chooseAgentSetting = function() {
39484
+ const picker = agentSettingsPicker;
39485
+ if (!picker)
39486
+ return;
39487
+ agentSettingsPicker = undefined;
39488
+ const pick3 = picker.kind === "model" ? { model: picker.models[picker.index] } : { reasoningEffort: newSessionReasoningEfforts[picker.index] };
39489
+ const agent = stageAgentChange({ state: latestState, staged: composerDraft.agent, pick: pick3 });
39490
+ composerDraft = { text: composerDraft.text, attachments: composerDraft.attachments, ...agent ? { agent } : {} };
39491
+ const target = agentSettingsTarget({ state: latestState, staged: agent });
39492
+ composerFeedback = agent ? { kind: "info", message: `${terminalSafeText(agentSettingsLabel(target))} applies from your next message.` } : { kind: "info", message: "No model or effort change is staged." };
39493
+ render();
39319
39494
  }, composerAtVisualEdge = function(direction) {
39320
39495
  const cursor = composer.visualCursor;
39321
39496
  const visualRow = composer.scrollY + cursor.visualRow;
@@ -39363,7 +39538,7 @@ async function createSessionTui({
39363
39538
  const working = isRemyWorking(latestState);
39364
39539
  const startedAt = workingTurnStartedAt(latestState);
39365
39540
  const elapsedMs = startedAt === undefined ? 0 : Math.max(0, now3() - Date.parse(startedAt));
39366
- statusBand.content = renderStatusBand({ state: latestState, working, elapsedMs, stopState, lifecycleRequestState, repositoryLabel });
39541
+ statusBand.content = renderStatusBand({ state: latestState, working, elapsedMs, stopState, lifecycleRequestState, repositoryLabel, stagedAgent: composerDraft.agent });
39367
39542
  const liveIndicator = lifecycleRequestState.kind === "pending" ? renderWorkingIndicator({ frame: SPINNER_FRAMES[spinnerFrameIndex], elapsedMs: 0, mode: lifecycleRequestState.operation }) : working ? renderWorkingIndicator({
39368
39543
  frame: SPINNER_FRAMES[spinnerFrameIndex],
39369
39544
  elapsedMs,
@@ -39381,6 +39556,7 @@ async function createSessionTui({
39381
39556
  const submissionStatus = renderComposerStatus({ admittedSubmissions, width: renderer.width - 2 });
39382
39557
  const completionMenu = pathCompletionMenuOpen ? renderPathCompletionMenu({ completions: pathCompletions, selectedIndex: completionIndex, width: renderer.width - 2, maxRows: completionMenuRows(renderer.height) }) : undefined;
39383
39558
  const commandMenu = renderSlashCommandMenu({ commands: slashCompletions, selectedIndex: slashCompletionIndex, width: renderer.width - 2, maxRows: completionMenuRows(renderer.height) });
39559
+ const agentSettingsMenu = agentSettingsPicker && latestState.aggregateStatus === "open" ? renderAgentSettingsPicker({ picker: agentSettingsPicker, width: renderer.width - 2, maxRows: completionMenuRows(renderer.height) }) : undefined;
39384
39560
  const composerStatus = [
39385
39561
  attachmentFeedback ? renderAttachmentFeedback(attachmentFeedback) : undefined,
39386
39562
  composerFeedback ? renderFeedback(composerFeedback) : undefined,
@@ -39388,6 +39564,7 @@ async function createSessionTui({
39388
39564
  lifecycleRequestState.kind === "failed" ? renderFeedback({ kind: "failure", message: `Could not ${lifecycleRequestState.operation} the session: ${lifecycleRequestState.message}` }) : undefined,
39389
39565
  submissionStatus,
39390
39566
  commandMenu,
39567
+ agentSettingsMenu,
39391
39568
  completionMenu
39392
39569
  ].filter((part) => part !== undefined && part.chunks.length > 0);
39393
39570
  status.content = logoutConfirmationOpen ? renderFeedback({ kind: "attention", message: "Log out of Remy?" }) : composerStatus.length > 0 ? joinStyled(composerStatus, `
@@ -39420,6 +39597,7 @@ async function createSessionTui({
39420
39597
  pathCompletionMenuOpen = false;
39421
39598
  slashCompletions = [];
39422
39599
  helpVisible = false;
39600
+ agentSettingsPicker = undefined;
39423
39601
  composer.blur();
39424
39602
  root.visible = false;
39425
39603
  resourceRoot.visible = true;
@@ -39579,6 +39757,7 @@ async function createSessionTui({
39579
39757
  let pathCompletionMenuOpen = false;
39580
39758
  let slashCompletions = [];
39581
39759
  let slashCompletionIndex = 0;
39760
+ let agentSettingsPicker;
39582
39761
  let composerFeedback = initialFeedback ? { kind: "info", message: initialFeedback } : undefined;
39583
39762
  let attachmentFeedback;
39584
39763
  const localPromptHistory = [];
@@ -39677,6 +39856,12 @@ async function createSessionTui({
39677
39856
  }
39678
39857
  return;
39679
39858
  }
39859
+ if (agentSettingsPicker && key.name === "escape") {
39860
+ key.preventDefault();
39861
+ agentSettingsPicker = undefined;
39862
+ render();
39863
+ return;
39864
+ }
39680
39865
  if (resourceMode) {
39681
39866
  key.preventDefault();
39682
39867
  const choices = resourceChoices();
@@ -39788,6 +39973,14 @@ async function createSessionTui({
39788
39973
  key.preventDefault();
39789
39974
  return;
39790
39975
  }
39976
+ if (agentSettingsPicker) {
39977
+ key.preventDefault();
39978
+ if (key.name === "up" || key.name === "down")
39979
+ moveAgentSettingsPicker(key.name === "up" ? -1 : 1);
39980
+ else if (key.name === "return" || key.name === "enter")
39981
+ chooseAgentSetting();
39982
+ return;
39983
+ }
39791
39984
  if (slashCompletions.length > 0 && (key.name === "up" || key.name === "down")) {
39792
39985
  key.preventDefault();
39793
39986
  slashCompletionIndex = (slashCompletionIndex + (key.name === "up" ? -1 : 1) + slashCompletions.length) % slashCompletions.length;
@@ -40007,6 +40200,7 @@ async function createSessionTui({
40007
40200
  idempotencyKey: crypto.randomUUID(),
40008
40201
  text: composerDraft.text,
40009
40202
  attachments: [...composerDraft.attachments],
40203
+ ...composerDraft.agent ? { agent: composerDraft.agent } : {},
40010
40204
  status: "admitting"
40011
40205
  };
40012
40206
  admittedSubmissions = [...admittedSubmissions, submission];
@@ -40059,6 +40253,12 @@ async function createSessionTui({
40059
40253
  handleCompactionRequest();
40060
40254
  return true;
40061
40255
  }
40256
+ if (command === "/model" || command === "/effort") {
40257
+ composer.setText("");
40258
+ composerDraft = { ...composerDraft, text: "" };
40259
+ openAgentSettingsPicker(command === "/model" ? "model" : "effort");
40260
+ return true;
40261
+ }
40062
40262
  if (command === "/logout") {
40063
40263
  composer.setText("");
40064
40264
  composerDraft = { ...composerDraft, text: "" };
@@ -40085,18 +40285,19 @@ async function createSessionTui({
40085
40285
  await submitMessage({
40086
40286
  text: submission.text,
40087
40287
  attachments: submission.attachments,
40088
- idempotencyKey: submission.idempotencyKey
40288
+ idempotencyKey: submission.idempotencyKey,
40289
+ ...submission.agent ? { agent: submission.agent } : {}
40089
40290
  });
40090
40291
  if (!viewIsActive())
40091
40292
  return;
40092
40293
  localPromptHistory.push(submission.text);
40093
40294
  const messageId = latestState.activeMessageId;
40094
- admittedSubmissions = messageId ? admittedSubmissions.map((candidate) => candidate.id === submission.id ? { id: candidate.id, idempotencyKey: candidate.idempotencyKey, text: candidate.text, attachments: candidate.attachments, status: "submitted", messageId } : candidate) : admittedSubmissions.filter((candidate) => candidate.id !== submission.id);
40295
+ admittedSubmissions = messageId ? admittedSubmissions.map((candidate) => candidate.id === submission.id ? { id: candidate.id, idempotencyKey: candidate.idempotencyKey, text: candidate.text, attachments: candidate.attachments, ...candidate.agent ? { agent: candidate.agent } : {}, status: "submitted", messageId } : candidate) : admittedSubmissions.filter((candidate) => candidate.id !== submission.id);
40095
40296
  composerFeedback = undefined;
40096
40297
  } catch (error93) {
40097
40298
  if (!viewIsActive())
40098
40299
  return;
40099
- admittedSubmissions = admittedSubmissions.map((candidate) => candidate.id === submission.id ? { id: candidate.id, idempotencyKey: candidate.idempotencyKey, text: candidate.text, attachments: candidate.attachments, status: "failed", error: error93 instanceof Error ? error93.message : String(error93) } : candidate);
40300
+ admittedSubmissions = admittedSubmissions.map((candidate) => candidate.id === submission.id ? { id: candidate.id, idempotencyKey: candidate.idempotencyKey, text: candidate.text, attachments: candidate.attachments, ...candidate.agent ? { agent: candidate.agent } : {}, status: "failed", error: error93 instanceof Error ? error93.message : String(error93) } : candidate);
40100
40301
  }
40101
40302
  render();
40102
40303
  }
@@ -40111,6 +40312,7 @@ async function createSessionTui({
40111
40312
  idempotencyKey: failed.idempotencyKey,
40112
40313
  text: failed.text,
40113
40314
  attachments: failed.attachments,
40315
+ ...failed.agent ? { agent: failed.agent } : {},
40114
40316
  status: "admitting"
40115
40317
  };
40116
40318
  admittedSubmissions = admittedSubmissions.map((submission) => submission.id === failed.id ? retrying : submission);
@@ -40221,6 +40423,8 @@ async function createSessionTui({
40221
40423
  }
40222
40424
  unsubscribe = controller.subscribe(({ state }) => {
40223
40425
  latestState = state;
40426
+ if (latestState.aggregateStatus !== "open")
40427
+ agentSettingsPicker = undefined;
40224
40428
  if (!isRemyWorking(latestState) && stopState.kind !== "idle") {
40225
40429
  stopState = { kind: "idle" };
40226
40430
  if (composerFeedback?.message === "Stop requested.")
@@ -40297,10 +40501,58 @@ function renderSlashCommandMenu({ commands, selectedIndex, width, maxRows }) {
40297
40501
  maxRows
40298
40502
  });
40299
40503
  }
40504
+ function renderAgentSettingsPicker({ picker, width, maxRows }) {
40505
+ const heading = picker.kind === "model" ? `Model for your next message \xB7 ${terminalSafeText(agentHarnessLabel(picker.provider))} harness` : "Reasoning effort for your next message";
40506
+ const items = picker.kind === "model" ? picker.models.map((model) => ({ label: agentModelLabel(model), description: agentModelDescription(model) })) : newSessionReasoningEfforts.map((effort) => ({ label: agentReasoningEffortLabel(effort), description: agentReasoningEffortDescription(effort) }));
40507
+ const menu = renderCompletionMenu({
40508
+ items,
40509
+ selectedIndex: picker.index,
40510
+ hints: [{ key: "\u2191\u2193", action: "select" }, { key: "Enter", action: "use for next message" }, { key: "Esc", action: "keep current" }],
40511
+ width,
40512
+ maxRows: Math.max(1, maxRows - 1)
40513
+ });
40514
+ return menu && joinStyled([new StyledText9([bold5(fg10(PALETTE.remyAccent)(heading))]), menu], `
40515
+ `);
40516
+ }
40517
+ function agentSettingsTarget({ state, staged }) {
40518
+ const requested = state.agentPending ?? state.agent;
40519
+ return {
40520
+ model: staged?.model ?? requested?.model,
40521
+ reasoningEffort: staged?.reasoningEffort ?? requested?.reasoningEffort
40522
+ };
40523
+ }
40524
+ function stageAgentChange({ state, staged, pick: pick3 }) {
40525
+ const current = agentSettingsTarget({ state, staged: undefined });
40526
+ const model = pick3.model ?? staged?.model;
40527
+ const reasoningEffort = pick3.reasoningEffort ?? staged?.reasoningEffort;
40528
+ return sessionAgentChange({
40529
+ model: model === current.model ? undefined : model,
40530
+ reasoningEffort: reasoningEffort === current.reasoningEffort ? undefined : reasoningEffort
40531
+ });
40532
+ }
40533
+ function agentSettingsLabel({ model, reasoningEffort }) {
40534
+ return [
40535
+ ...model === undefined ? [] : [agentModelLabel(model)],
40536
+ ...reasoningEffort === undefined || reasoningEffort === null ? [] : [agentReasoningEffortLabel(reasoningEffort)]
40537
+ ].join(" \xB7 ");
40538
+ }
40539
+ function renderAgentSettingsStatus({ state, stagedAgent }) {
40540
+ const applied = state.agent;
40541
+ if (!applied)
40542
+ return;
40543
+ const appliedLabel = terminalSafeText(`${agentHarnessLabel(applied.provider)} \xB7 ${agentSettingsLabel(applied)}`);
40544
+ const target = agentSettingsTarget({ state, staged: stagedAgent });
40545
+ const phase = stagedAgent ? "next message" : state.agentPending ? "switching\u2026" : undefined;
40546
+ const changes = phase !== undefined && (target.model !== applied.model || (target.reasoningEffort ?? null) !== applied.reasoningEffort);
40547
+ return new StyledText9([
40548
+ fg10(PALETTE.bodyText)(appliedLabel),
40549
+ ...changes ? [fg10(PALETTE.approvalQuestion)(` \u2192 ${terminalSafeText(agentSettingsLabel(target))} (${phase})`)] : []
40550
+ ]);
40551
+ }
40300
40552
  function hasRunningTurn(state) {
40301
40553
  return state.aggregateStatus === "open" && Object.values(state.messageTurns).some((turn) => turn.outcome === undefined);
40302
40554
  }
40303
- function renderStatusBand({ state, working, elapsedMs, stopState, lifecycleRequestState, repositoryLabel }) {
40555
+ function renderStatusBand({ state, working, elapsedMs, stopState, lifecycleRequestState, repositoryLabel, stagedAgent }) {
40304
40556
  const stopping = stopState?.kind === "stopping";
40305
40557
  const pendingOperation = lifecycleRequestState?.kind === "pending" ? lifecycleRequestState.operation : undefined;
40306
40558
  const presentation = pendingOperation ? { label: lifecycleOperationPresentParticiple(pendingOperation), color: PALETTE.approvalQuestion } : stopping ? { label: "Stopping", color: PALETTE.approvalQuestion } : sessionViewStatePresentation({ aggregateStatus: state.aggregateStatus, isWorking: working });
@@ -40309,11 +40561,13 @@ function renderStatusBand({ state, working, elapsedMs, stopState, lifecycleReque
40309
40561
  const sessionLabel = state.sessionNumber !== undefined ? `#${state.sessionNumber}` : state.sessionId;
40310
40562
  const pullRequest = state.pullRequest;
40311
40563
  const contextStatus = renderContextStatus(state.context.snapshot);
40564
+ const agentSettings = renderAgentSettingsStatus({ state, stagedAgent: state.aggregateStatus === "open" ? stagedAgent : undefined });
40312
40565
  const headerContext = [
40313
40566
  ...repositoryLabel ? [[dim7(fg10(PALETTE.dimText)(repositoryLabel))]] : [],
40314
40567
  ...pullRequest ? [[bold5(fg10(statusColor(pullRequest.status))(`PR#${pullRequest.number}`))]] : [],
40315
40568
  [bold5(fg10(presentation.color)(`${presentation.label}${elapsed}`))],
40316
40569
  connection.chunks,
40570
+ ...agentSettings ? [agentSettings.chunks] : [],
40317
40571
  ...contextStatus ? [[dim7(fg10(PALETTE.dimText)(contextStatus))]] : []
40318
40572
  ];
40319
40573
  return joinStyled([
@@ -41454,7 +41708,7 @@ async function dispatchCliCommandWithShutdown({
41454
41708
  });
41455
41709
  }
41456
41710
  if (command.name === "session")
41457
- return await attachSession({ dependencies, sessionId: command.sessionId, noTui: command.noTui, json: command.json, prompt: command.prompt });
41711
+ return await attachSession({ dependencies, sessionId: command.sessionId, noTui: command.noTui, json: command.json, followUp: command.followUp });
41458
41712
  return await createNewSession({ dependencies, command });
41459
41713
  }
41460
41714
  async function installCliUpdate({ dependencies, version: version5 }) {
@@ -42368,26 +42622,35 @@ async function attachSession({
42368
42622
  sessionId,
42369
42623
  noTui,
42370
42624
  json: json3,
42371
- prompt
42625
+ followUp
42372
42626
  }) {
42373
42627
  const operations = await createSessionOperations(dependencies);
42374
42628
  const detail = await operations.getSession({ client: operations.client, sessionId });
42629
+ if (followUp?.agent?.model !== undefined && detail.agent)
42630
+ assertModelInSessionHarness({ model: followUp.agent.model, provider: detail.agent.provider });
42375
42631
  const repositories = detail.repositories.map((repository) => ({ id: repository.id, fullName: repository.full_name }));
42376
42632
  const cache = await (dependencies.readSessionCache ?? readSessionCache)(resolveSessionCachePathForCommand({ dependencies, sessionId }));
42377
42633
  let activeMessageId = cache?.activeMessageId;
42378
42634
  let cacheWarningReported = false;
42379
- const onCacheError = prompt === undefined ? undefined : () => {
42635
+ const onCacheError = followUp === undefined ? undefined : () => {
42380
42636
  if (cacheWarningReported)
42381
42637
  return;
42382
42638
  cacheWarningReported = true;
42383
42639
  dependencies.output.writeStderr(`Local session cache is unavailable; continuing to observe the accepted follow-up. Do not resubmit the prompt.
42384
42640
  `);
42385
42641
  };
42386
- if (prompt !== undefined) {
42642
+ if (followUp !== undefined) {
42387
42643
  throwIfAborted2(dependencies.abortSignal);
42388
42644
  const appended = await operations.appendSessionMessage({
42389
42645
  client: operations.client,
42390
- input: { sessionId, text: prompt, fileIds: [], mode: "steer", idempotencyKey: randomUUID5() }
42646
+ input: {
42647
+ sessionId,
42648
+ text: followUp.prompt,
42649
+ fileIds: [],
42650
+ mode: "steer",
42651
+ idempotencyKey: randomUUID5(),
42652
+ ...followUp.agent === undefined ? {} : { agent: followUp.agent }
42653
+ }
42391
42654
  });
42392
42655
  activeMessageId = appended.message.id;
42393
42656
  }
@@ -42419,6 +42682,13 @@ async function attachSession({
42419
42682
  onCacheError
42420
42683
  });
42421
42684
  }
42685
+ function assertModelInSessionHarness({ model, provider }) {
42686
+ const models = sessionHarnessModels(provider);
42687
+ if (models.includes(model))
42688
+ return;
42689
+ const offered = models.length === 0 ? "Remy does not offer model changes for that harness." : `Choose one of: ${models.join(", ")}.`;
42690
+ throw new Error(`--model ${model} is not available: this session runs on the ${agentHarnessLabel(provider)} harness, which cannot change mid-session. ${offered}`);
42691
+ }
42422
42692
  async function runAttachedSession({
42423
42693
  dependencies,
42424
42694
  operations,
@@ -42515,10 +42785,15 @@ async function runAttachedSession({
42515
42785
  text: submission.text,
42516
42786
  fileIds: submission.attachments.map((attachment) => attachment.fileId),
42517
42787
  idempotencyKey: submission.idempotencyKey,
42518
- mode: "steer"
42788
+ mode: "steer",
42789
+ ...submission.agent === undefined ? {} : { agent: submission.agent }
42519
42790
  }
42520
42791
  });
42521
42792
  await controller.recordActiveMessage(result.message.id);
42793
+ if (submission.agent !== undefined)
42794
+ controller.refreshDetail().catch(() => {
42795
+ return;
42796
+ });
42522
42797
  return { messageId: result.message.id };
42523
42798
  },
42524
42799
  requestInterrupt: async ({ idempotencyKey }) => {
@@ -42862,8 +43137,12 @@ Arguments:
42862
43137
  prompt Submit a follow-up as Steer before attaching
42863
43138
 
42864
43139
  With a prompt, JSON output waits for that message's turn result. Use -- before a prompt beginning with --.
43140
+ --model and --reasoning-effort require a prompt: the change travels with it and stays the session's setting.
43141
+ A running turn switches from its next model request; otherwise the change applies from the turn the prompt starts.
42865
43142
 
42866
43143
  Options:
43144
+ --model <name> Switch to another model of the session's harness
43145
+ --reasoning-effort <low|medium|high|xhigh> Switch agent reasoning effort
42867
43146
  --no-tui Do not open the interactive terminal view
42868
43147
  --json Write session updates as JSON
42869
43148
  --help, -h Show this help
@@ -42894,7 +43173,7 @@ Usage:
42894
43173
  Commands:
42895
43174
  remy Open interactive dashboard
42896
43175
  remy dashboard Open interactive dashboard
42897
- remy --session <session-id> [--no-tui] [--json] [prompt]
43176
+ remy --session <session-id> [--model <name> --reasoning-effort <low|medium|high|xhigh> --no-tui --json] [prompt]
42898
43177
  Attach to a session, optionally submitting a follow-up
42899
43178
  remy new [--repository <owner/name> ... --installation <id> --model <name> --reasoning-effort <low|medium|high|xhigh> --attach <path> ... --no-tui --json] <prompt>
42900
43179
  Create a session
@@ -42932,8 +43211,11 @@ function parseSessionAttach(argv) {
42932
43211
  let noTui = false;
42933
43212
  let json3 = false;
42934
43213
  let prompt;
43214
+ let model;
43215
+ let reasoningEffort;
42935
43216
  let positionalOnly = false;
42936
- for (const token of argv.slice(1)) {
43217
+ for (let index = 1;index < argv.length; index += 1) {
43218
+ const token = argv[index];
42937
43219
  if (!positionalOnly && token === "--") {
42938
43220
  positionalOnly = true;
42939
43221
  continue;
@@ -42946,6 +43228,17 @@ function parseSessionAttach(argv) {
42946
43228
  json3 = true;
42947
43229
  continue;
42948
43230
  }
43231
+ if (!positionalOnly && (token === "--model" || token === "--reasoning-effort")) {
43232
+ const value = argv[index + 1];
43233
+ if (!value || value.startsWith("--"))
43234
+ throw new Error(`${token} requires a value.`);
43235
+ index += 1;
43236
+ if (token === "--model")
43237
+ model = parseModelFlag(value);
43238
+ else
43239
+ reasoningEffort = parseReasoningEffortFlag(value);
43240
+ continue;
43241
+ }
42949
43242
  if (!positionalOnly && token.startsWith("--"))
42950
43243
  throw new Error(`Unknown --session flag ${token}.`);
42951
43244
  if (prompt !== undefined)
@@ -42954,7 +43247,28 @@ function parseSessionAttach(argv) {
42954
43247
  throw new Error("A follow-up prompt must not be empty.");
42955
43248
  prompt = token;
42956
43249
  }
42957
- return { name: "session", sessionId, noTui, json: json3, ...prompt === undefined ? {} : { prompt } };
43250
+ const agent = sessionAgentChange({ model, reasoningEffort });
43251
+ if (agent && prompt === undefined)
43252
+ throw new Error("--model and --reasoning-effort change the session with a follow-up message. Pass a prompt too.");
43253
+ return {
43254
+ name: "session",
43255
+ sessionId,
43256
+ noTui,
43257
+ json: json3,
43258
+ ...prompt === undefined ? {} : { followUp: { prompt, ...agent ? { agent } : {} } }
43259
+ };
43260
+ }
43261
+ function parseModelFlag(value) {
43262
+ const model = parseNewSessionModel(value);
43263
+ if (!model)
43264
+ throw new Error(`--model must name a supported model: ${newSessionModels.join(", ")}.`);
43265
+ return model;
43266
+ }
43267
+ function parseReasoningEffortFlag(value) {
43268
+ const reasoningEffort = parseNewSessionReasoningEffort(value);
43269
+ if (!reasoningEffort)
43270
+ throw new Error(`--reasoning-effort must be ${newSessionReasoningEfforts.slice(0, -1).join(", ")}, or ${newSessionReasoningEfforts.at(-1)}.`);
43271
+ return reasoningEffort;
42958
43272
  }
42959
43273
  function parseNewCommand(argv) {
42960
43274
  const repositories = [];
@@ -42990,15 +43304,11 @@ function parseNewCommand(argv) {
42990
43304
  } else if (token === "--attach") {
42991
43305
  attachments.push(value);
42992
43306
  } else if (token === "--model") {
42993
- model = parseNewSessionModel(value);
42994
- if (!model)
42995
- throw new Error(`--model must name a supported model: ${newSessionModels.join(", ")}.`);
43307
+ model = parseModelFlag(value);
42996
43308
  } else if (token === "--installation") {
42997
43309
  installationId = value;
42998
43310
  } else if (token === "--reasoning-effort") {
42999
- if (!selectableAgentReasoningEfforts.includes(value))
43000
- throw new Error("--reasoning-effort must be low, medium, high, or xhigh.");
43001
- reasoningEffort = value;
43311
+ reasoningEffort = parseReasoningEffortFlag(value);
43002
43312
  } else {
43003
43313
  throw new Error(`Unknown new flag ${token}.`);
43004
43314
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/remy-cli",
3
- "version": "1.22.0-rc.1",
3
+ "version": "1.22.0-rc.2",
4
4
  "description": "Remy, the Coding Agent terminal client.",
5
5
  "type": "module",
6
6
  "bin": {