@meistrari/agent-core 0.2.0-experimental.7 → 0.2.0-experimental.8
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 +7 -0
- package/docs/diagrams/model-effort-change.png +0 -0
- package/docs/model-effort-changes.md +60 -0
- package/package.json +1 -1
- package/src/agents/agent-run.ts +2 -0
- package/src/agents/claude/claude-run.ts +42 -15
- package/src/agents/codex/codex-provider.ts +5 -4
- package/src/agents/codex/codex-run.ts +22 -16
- package/src/agents/model-rebinding-run.ts +63 -13
- package/src/protocol/agent-command.ts +10 -0
- package/src/provenance.gen.ts +3 -3
- package/src/supervisor/agent-provider-factory.ts +19 -3
- package/src/supervisor/persistence/supervisor-store.ts +29 -0
- package/src/supervisor/provider-factory.ts +2 -1
- package/src/supervisor/resident.ts +1 -0
- package/src/supervisor/runtime-handler.ts +23 -23
- package/src/supervisor-protocol/command-body.ts +4 -1
package/README.md
CHANGED
|
@@ -149,3 +149,10 @@ bypass-permissions mode as root, so run them as a regular user.
|
|
|
149
149
|
Regenerate the experimental Codex wire types with `bun run generate:codex-protocol`.
|
|
150
150
|
The generator pins Codex 0.159.0 and verifies any `CODEX_EXECUTABLE` override before
|
|
151
151
|
using it. Commit generated files with generator changes; CI checks reproducibility.
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
## Standalone settings changes
|
|
155
|
+
|
|
156
|
+
`agent.change-settings` carries a full `settings: { model, reasoningEffort }` pair through the supervisor wire and the agent protocol. Claude and Codex apply it only at an idle turn boundary, without submitting input, and emit `session.settings.changed` with the command ID and `appliesFrom: next_turn`. Product catalog, provider compatibility and admission ordering remain the caller's responsibility.
|
|
157
|
+
|
|
158
|
+
The supervisor advertises `capabilities["model-change"] = "v1"` in health. It commits the settings command outcome and inbox status in one SQLite transaction. After a process restart, an interrupted settings command can be reapplied idempotently; prompt commands retain their uncertain-delivery policy and are never retried by this path. Confirmed settings override the immutable bootstrap defaults when resuming the provider. Prompt and user-input-answer commands may carry resolved `settings` so their execution does not rely solely on mutable defaults.
|
|
Binary file
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Model and reasoning effort changes
|
|
2
|
+
|
|
3
|
+
agent-core can update a session's model and reasoning effort without sending a
|
|
4
|
+
conversational prompt. Healthy Claude and Codex sessions keep the same CLI process
|
|
5
|
+
and provider session, preserving their warmed runtime state.
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
## Standalone command
|
|
10
|
+
|
|
11
|
+
Send `agent.change-settings` with `settings: { model, reasoningEffort }` at an idle
|
|
12
|
+
turn boundary. The caller schedules that boundary; a busy run defers the operation.
|
|
13
|
+
|
|
14
|
+
The supervisor persists the command in its inbox and acknowledges receipt with
|
|
15
|
+
`command.ack`. Receipt does not mean the provider has applied the settings.
|
|
16
|
+
|
|
17
|
+
| Provider | How settings are applied | Acceptance |
|
|
18
|
+
| --- | --- | --- |
|
|
19
|
+
| Claude | `query.setModel(model)` and `query.applyFlagSettings({ effortLevel })`, skipping unchanged known values | The required control responses succeed |
|
|
20
|
+
| Codex | `thread/settings/update` with `{ threadId, model, effort }` | The JSON-RPC request succeeds |
|
|
21
|
+
|
|
22
|
+
After acceptance, the run updates its defaults and emits `session.settings.changed`
|
|
23
|
+
with `model`, `reasoningEffort`, the initiating `commandId`, and
|
|
24
|
+
`appliesFrom: 'next_turn'`. The supervisor commits the accepted settings together
|
|
25
|
+
with command completion, then sends `supervisor.command.applied`.
|
|
26
|
+
|
|
27
|
+
A provider failure does not produce a successful settings-change event or a
|
|
28
|
+
successful command completion. Receipt alone must never update a consumer's
|
|
29
|
+
display of confirmed settings.
|
|
30
|
+
|
|
31
|
+
## Settings carried by inputs
|
|
32
|
+
|
|
33
|
+
`agent.send-prompt.settings` and `agent.respond-user-input.settings` can carry
|
|
34
|
+
partial settings. Omitted fields retain the currently accepted values. Prompt
|
|
35
|
+
settings retain the provider's `next` and `steer` behavior; the confirmation event
|
|
36
|
+
identifies whether the change applies to the current or next turn. Already-delivered
|
|
37
|
+
answers do not change defaults or emit another settings-change event.
|
|
38
|
+
|
|
39
|
+
For prompts, a supplied `settings` object takes precedence over the entire legacy
|
|
40
|
+
top-level `model` / `reasoningEffort` pair, even when the object is empty. Without
|
|
41
|
+
that object, accepted legacy fields are also persisted as default changes. Missing
|
|
42
|
+
legacy fields do not clear previously accepted defaults.
|
|
43
|
+
|
|
44
|
+
Claude's legacy top-level effort field must match the current accepted effort.
|
|
45
|
+
Use `settings.reasoningEffort` or the standalone command to change it. For example,
|
|
46
|
+
after starting at `medium` and successfully applying standalone `high`, a legacy
|
|
47
|
+
prompt specifying `high` is accepted; one specifying `medium` is rejected.
|
|
48
|
+
|
|
49
|
+
## Recovery
|
|
50
|
+
|
|
51
|
+
The supervisor restores the latest accepted defaults across standalone commands,
|
|
52
|
+
prompts, and answers. For example, standalone A/high, followed by a legacy prompt
|
|
53
|
+
B/low, followed by `settings: { reasoningEffort: 'medium' }`, recovers as B/medium.
|
|
54
|
+
Failed commands and already-delivered answers do not replace those defaults.
|
|
55
|
+
|
|
56
|
+
The Bedrock wrapper retains its legacy prompt model-rebinding path. Standalone
|
|
57
|
+
changes bypass that path on a healthy connection. If a previous replacement failed
|
|
58
|
+
and left the old run stopped, the wrapper first recovers a connection with its
|
|
59
|
+
previously accepted settings, then applies the requested update. Recovery preserves
|
|
60
|
+
the provider session identity and does not replay the failed prompt.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meistrari/agent-core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.0-experimental.
|
|
4
|
+
"version": "0.2.0-experimental.8",
|
|
5
5
|
"packageManager": "bun@1.3.12",
|
|
6
6
|
"description": "Shared contracts and runtime modules for Tela coding-agent sandboxes: agent protocol, supervisor wire protocol, resident supervisor, worker runtime client, and Claude/Codex harness adapters.",
|
|
7
7
|
"license": "UNLICENSED",
|
package/src/agents/agent-run.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type {
|
|
|
3
3
|
AgentEvent,
|
|
4
4
|
AgentNotifyCommand,
|
|
5
5
|
AgentRunMetadata,
|
|
6
|
+
ChangeSettingsAgentCommand,
|
|
6
7
|
CompactContextAgentCommand,
|
|
7
8
|
InterruptAgentCommand,
|
|
8
9
|
RespondUserInputAgentCommand,
|
|
@@ -34,6 +35,7 @@ export interface AgentRun {
|
|
|
34
35
|
prepareWorkspace: (options?: AgentOperationOptions) => Promise<void>
|
|
35
36
|
// Providers without root registration accept the root without provider I/O.
|
|
36
37
|
addWorkspaceRepositoryRoot: (root: string, options?: AgentOperationOptions) => Promise<void>
|
|
38
|
+
changeSettings?: (command: ChangeSettingsAgentCommand, options?: AgentOperationOptions) => Promise<void>
|
|
37
39
|
sendPrompt: (command: SendPromptCommand, options?: AgentPromptOperationOptions) => Promise<AgentPromptAcceptance>
|
|
38
40
|
// Optional capabilities: the Claude and Codex runs implement both; other runs may omit them.
|
|
39
41
|
notify?: (command: AgentNotifyCommand, options?: AgentOperationOptions) => Promise<AgentNotificationAcceptance>
|
|
@@ -1,18 +1,6 @@
|
|
|
1
1
|
import type { Query, SDKMessage, SDKUserMessage } from '@anthropic-ai/claude-agent-sdk'
|
|
2
2
|
import type { UUID } from 'node:crypto'
|
|
3
|
-
import type {
|
|
4
|
-
AgentContextCompactionRequestResult,
|
|
5
|
-
AgentError,
|
|
6
|
-
AgentNotifyCommand,
|
|
7
|
-
AgentReasoningEffort,
|
|
8
|
-
AgentRunMetadata,
|
|
9
|
-
AgentSettingsChange,
|
|
10
|
-
CompactContextAgentCommand,
|
|
11
|
-
InterruptAgentCommand,
|
|
12
|
-
RespondUserInputAgentCommand,
|
|
13
|
-
SendPromptCommand,
|
|
14
|
-
StopAgentCommand,
|
|
15
|
-
} from '../../protocol'
|
|
3
|
+
import type { AgentContextCompactionRequestResult, AgentError, AgentNotifyCommand, AgentReasoningEffort, AgentRunMetadata, AgentSettingsChange, ChangeSettingsAgentCommand, CompactContextAgentCommand, InterruptAgentCommand, RespondUserInputAgentCommand, SendPromptCommand, StopAgentCommand } from '../../protocol'
|
|
16
4
|
import type { AgentEventDraft } from '../agent-event-stream'
|
|
17
5
|
import type { AgentOperationOptions, AgentPromptOperationOptions } from '../agent-operation'
|
|
18
6
|
import type { AgentNotificationAcceptance, AgentPromptAcceptance, AgentRun, AgentUserInputSubmission } from '../agent-run'
|
|
@@ -297,6 +285,10 @@ export class ClaudeRun implements AgentRun {
|
|
|
297
285
|
this.events.push(decorated)
|
|
298
286
|
if (draft.type === 'turn.started' && draft.payload.commandId)
|
|
299
287
|
this.emitAcceptedMessage(draft.payload.commandId, draft.turnId)
|
|
288
|
+
// Running may precede the input's started receipt. Consumption identifies the turn
|
|
289
|
+
// after that receipt, before the mapper emits the assistant's message.
|
|
290
|
+
if (draft.type === 'prompt.turn-correlated')
|
|
291
|
+
this.emitAcceptedMessage(draft.payload.commandId, draft.payload.turnId)
|
|
300
292
|
}
|
|
301
293
|
|
|
302
294
|
// The accepted input becomes a user message of the turn that consumed it, correlated with the
|
|
@@ -356,6 +348,28 @@ export class ClaudeRun implements AgentRun {
|
|
|
356
348
|
this.skillReloadOwed = false
|
|
357
349
|
}
|
|
358
350
|
|
|
351
|
+
async changeSettings(command: ChangeSettingsAgentCommand, options: AgentOperationOptions = {}): Promise<void> {
|
|
352
|
+
this.assertCommandTargetsRun(command)
|
|
353
|
+
this.assertRunning()
|
|
354
|
+
options.signal?.throwIfAborted()
|
|
355
|
+
if (this.eventMapperState.currentTurnId || this.commandByClientMessageId.size > 0 || this.modelChangeInFlight)
|
|
356
|
+
throw new AgentPromptDeferredError({ message: 'Settings changes require an idle turn boundary.' })
|
|
357
|
+
this.modelChangeInFlight = true
|
|
358
|
+
try {
|
|
359
|
+
const settings = this.settingsWith(command.settings)
|
|
360
|
+
await this.applyProviderSettings(settings)
|
|
361
|
+
this.current = settings
|
|
362
|
+
if (settings.model) {
|
|
363
|
+
this.metadata.model = settings.model
|
|
364
|
+
this.init.requestedModel = settings.model
|
|
365
|
+
}
|
|
366
|
+
this.pushEvent(sessionSettingsChangedEvent({ model: settings.model!, reasoningEffort: settings.effort, commandId: command.commandId, appliesFrom: 'next_turn' }))
|
|
367
|
+
}
|
|
368
|
+
finally {
|
|
369
|
+
this.modelChangeInFlight = false
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
359
373
|
async sendPrompt(command: SendPromptCommand, options: AgentPromptOperationOptions = {}): Promise<AgentPromptAcceptance> {
|
|
360
374
|
this.assertCommandTargetsRun(command)
|
|
361
375
|
this.assertRunning()
|
|
@@ -365,7 +379,7 @@ export class ClaudeRun implements AgentRun {
|
|
|
365
379
|
message: 'Bypass submission requires a steer prompt.',
|
|
366
380
|
})
|
|
367
381
|
}
|
|
368
|
-
if (command.reasoningEffort !== undefined && command.reasoningEffort !== this.
|
|
382
|
+
if (!command.settings && command.reasoningEffort !== undefined && command.reasoningEffort !== this.current.effort)
|
|
369
383
|
throw new AgentNotAcceptedError({ message: 'Claude reasoning effort changes are not supported by this adapter.', recovery: { kind: 'terminal', reason: 'reasoning_effort_change_not_supported' } })
|
|
370
384
|
// Claude Code steers by cancelling the running ask, which would abort a manual compaction
|
|
371
385
|
// (CLI 2.1.280). Like Codex's non-steerable compaction turn, the steer waits for it to end.
|
|
@@ -441,6 +455,8 @@ export class ClaudeRun implements AgentRun {
|
|
|
441
455
|
private async applyCommandModel(command: SendPromptCommand, options: AgentOperationOptions): Promise<void> {
|
|
442
456
|
if (this.modelChangeInFlight)
|
|
443
457
|
throw new AgentPromptDeferredError({ message: 'Claude model configuration is still in progress.' })
|
|
458
|
+
if (command.settings)
|
|
459
|
+
return
|
|
444
460
|
const currentModel = this.init.requestedModel ?? this.metadata.model
|
|
445
461
|
if (!command.model || command.model === currentModel)
|
|
446
462
|
return
|
|
@@ -742,7 +758,8 @@ export class ClaudeRun implements AgentRun {
|
|
|
742
758
|
return 'already_delivered'
|
|
743
759
|
// Deliver first: on a prebound resume this is the run's first input, and the resolved
|
|
744
760
|
// event must not precede the lifecycle events the init barrier emits.
|
|
745
|
-
|
|
761
|
+
const settings = this.settingsWith(command.settings)
|
|
762
|
+
await this.applyProviderSettings(settings)
|
|
746
763
|
// The answer opens its turn as the user's input, correlated like a prompt: the turn it
|
|
747
764
|
// starts carries this command, and the answer is accepted as that turn's user message.
|
|
748
765
|
const clientMessageId = deriveMessageId(command.commandId)
|
|
@@ -762,6 +779,16 @@ export class ClaudeRun implements AgentRun {
|
|
|
762
779
|
finally {
|
|
763
780
|
this.acceptanceWaiters.delete(clientMessageId)
|
|
764
781
|
}
|
|
782
|
+
if (command.settings) {
|
|
783
|
+
this.current = settings
|
|
784
|
+
this.pushEvent(sessionSettingsChangedEvent({
|
|
785
|
+
model: settings.model ?? await this.initialization,
|
|
786
|
+
reasoningEffort: settings.effort,
|
|
787
|
+
commandId: command.commandId,
|
|
788
|
+
turnId: this.eventMapperState.currentTurnId,
|
|
789
|
+
appliesFrom: 'current_turn',
|
|
790
|
+
}))
|
|
791
|
+
}
|
|
765
792
|
if (resolved.event)
|
|
766
793
|
this.pushEvent(resolved.event)
|
|
767
794
|
this.userInputRequests.markDelivered(command.requestId)
|
|
@@ -181,18 +181,19 @@ export class CodexProvider implements AgentProvider<'codex'> {
|
|
|
181
181
|
return this.wrapBedrockRun(await this.resumeRawSession(input, options), input)
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
-
//
|
|
185
|
-
//
|
|
184
|
+
// Legacy prompt-only model changes rebind at an idle boundary. Explicit settings
|
|
185
|
+
// commands use the native update on the existing thread and keep the CLI warm.
|
|
186
186
|
private wrapBedrockRun(run: CodexRun, input: AgentSessionOpenInput<'codex'>): AgentRun {
|
|
187
187
|
if (this.auth.kind !== 'amazon-bedrock')
|
|
188
188
|
return run
|
|
189
189
|
const roots = new Set(input.workspaceRepositoryRoots)
|
|
190
|
-
return new ModelRebindingRun(run, async (model, signal) => await this.resumeRawSession({
|
|
190
|
+
return new ModelRebindingRun(run, async (model, signal, reasoningEffort) => await this.resumeRawSession({
|
|
191
191
|
...input,
|
|
192
192
|
model: codexModelIdSchema.parse(model),
|
|
193
|
+
reasoningEffort,
|
|
193
194
|
providerSessionId: run.metadata.providerSessionId,
|
|
194
195
|
workspaceRepositoryRoots: [...roots],
|
|
195
|
-
}, { signal }), root => roots.add(root))
|
|
196
|
+
}, { signal }), root => roots.add(root), input.reasoningEffort)
|
|
196
197
|
}
|
|
197
198
|
|
|
198
199
|
private async resumeRawSession(input: AgentSessionResumeInput<'codex'>, options: AgentOperationOptions = {}): Promise<CodexRun> {
|
|
@@ -1,17 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
AgentAuthor,
|
|
3
|
-
AgentContextCompactionRequestResult,
|
|
4
|
-
AgentNotifyCommand,
|
|
5
|
-
AgentPrompt,
|
|
6
|
-
AgentReasoningEffort,
|
|
7
|
-
AgentRunMetadata,
|
|
8
|
-
AgentSettingsChange,
|
|
9
|
-
CompactContextAgentCommand,
|
|
10
|
-
InterruptAgentCommand,
|
|
11
|
-
RespondUserInputAgentCommand,
|
|
12
|
-
SendPromptCommand,
|
|
13
|
-
StopAgentCommand,
|
|
14
|
-
} from '../../protocol'
|
|
1
|
+
import type { AgentAuthor, AgentContextCompactionRequestResult, AgentNotifyCommand, AgentPrompt, AgentReasoningEffort, AgentRunMetadata, AgentSettingsChange, ChangeSettingsAgentCommand, CompactContextAgentCommand, InterruptAgentCommand, RespondUserInputAgentCommand, SendPromptCommand, StopAgentCommand } from '../../protocol'
|
|
15
2
|
import type { AgentEventDraft } from '../agent-event-stream'
|
|
16
3
|
import type { AgentOperationOptions, AgentPromptOperationOptions } from '../agent-operation'
|
|
17
4
|
import type { AgentNotificationAcceptance, AgentPromptAcceptance, AgentRun, AgentUserInputSubmission } from '../agent-run'
|
|
@@ -274,6 +261,22 @@ export class CodexRun implements AgentRun {
|
|
|
274
261
|
await stopping.promise
|
|
275
262
|
}
|
|
276
263
|
|
|
264
|
+
async changeSettings(command: ChangeSettingsAgentCommand, options: AgentOperationOptions = {}): Promise<void> {
|
|
265
|
+
this.assertCommandTargetsRun(command)
|
|
266
|
+
this.assertRunning()
|
|
267
|
+
options.signal?.throwIfAborted()
|
|
268
|
+
this.deferNextWhileTurnActive('next')
|
|
269
|
+
const settings = withSettingsChange(this.current, command.settings)
|
|
270
|
+
try {
|
|
271
|
+
await this.client.request('thread/settings/update', { threadId: this.metadata.providerSessionId, model: this.resolveInferenceModel(settings.model), effort: codexEffort(settings.effort) })
|
|
272
|
+
}
|
|
273
|
+
catch (error) {
|
|
274
|
+
throw mapSettingsTransportLoss(error)
|
|
275
|
+
}
|
|
276
|
+
this.metadata.model = settings.model
|
|
277
|
+
this.settingsAccepted({ settings, commandId: command.commandId, turnId: undefined, placement: { appliesFrom: 'next_turn' } })
|
|
278
|
+
}
|
|
279
|
+
|
|
277
280
|
async sendPrompt(command: SendPromptCommand, options: AgentPromptOperationOptions = {}): Promise<AgentPromptAcceptance> {
|
|
278
281
|
this.assertCommandTargetsRun(command)
|
|
279
282
|
this.assertRunning()
|
|
@@ -583,11 +586,14 @@ export class CodexRun implements AgentRun {
|
|
|
583
586
|
if (resolved.event)
|
|
584
587
|
this.pushEvent(resolved.event)
|
|
585
588
|
const prepared = await this.prepareTurn({ prompt: resolved.prompt, commandId: command.commandId, options })
|
|
586
|
-
|
|
589
|
+
const settings = withSettingsChange(this.current, command.settings)
|
|
590
|
+
const { receipt } = await this.startTurn({
|
|
587
591
|
commandId: prepared.commandId,
|
|
588
|
-
settings
|
|
592
|
+
settings,
|
|
589
593
|
content: { kind: 'prompt', input: prepared.input, origin: prepared.origin },
|
|
590
594
|
}, options)
|
|
595
|
+
if (command.settings)
|
|
596
|
+
this.settingsAccepted({ settings, commandId: command.commandId, turnId: receipt.turnId, placement: { appliesFrom: 'current_turn' } })
|
|
591
597
|
this.sessionContextDelivered(prepared.sessionContext)
|
|
592
598
|
this.userInputRequests.markDelivered(command.requestId)
|
|
593
599
|
return 'submitted'
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentEvent, AgentRunMetadata, SendPromptCommand } from '../protocol'
|
|
1
|
+
import type { AgentEvent, AgentReasoningEffort, AgentRunMetadata, ChangeSettingsAgentCommand, SendPromptCommand } from '../protocol'
|
|
2
2
|
import type { AgentOperationOptions, AgentPromptOperationOptions } from './agent-operation'
|
|
3
3
|
import type { AgentPromptAcceptance, AgentRun } from './agent-run'
|
|
4
4
|
import { AgentEventStream } from './agent-event-stream'
|
|
@@ -12,7 +12,8 @@ export class ModelRebindingRun implements AgentRun {
|
|
|
12
12
|
private pump: Promise<void>
|
|
13
13
|
private activeTurn: string | undefined
|
|
14
14
|
private lastEndedTurn: string | undefined
|
|
15
|
-
private dispatching: Promise<
|
|
15
|
+
private dispatching: Promise<unknown> | undefined
|
|
16
|
+
private settingsChanging = false
|
|
16
17
|
private changing = false
|
|
17
18
|
private needsRebind = false
|
|
18
19
|
private stopped = false
|
|
@@ -21,7 +22,7 @@ export class ModelRebindingRun implements AgentRun {
|
|
|
21
22
|
private readonly lifetime = new AbortController()
|
|
22
23
|
private readonly controls = new Set<Promise<unknown>>()
|
|
23
24
|
|
|
24
|
-
constructor(run: AgentRun, private readonly reopen: (model: string, signal: AbortSignal) => Promise<AgentRun>, private readonly rootAdded?: (root: string) => void) {
|
|
25
|
+
constructor(run: AgentRun, private readonly reopen: (model: string, signal: AbortSignal, reasoningEffort?: AgentReasoningEffort) => Promise<AgentRun>, private readonly rootAdded?: (root: string) => void, private reasoningEffort?: AgentReasoningEffort) {
|
|
25
26
|
this.run = run
|
|
26
27
|
this.metadata = { ...run.metadata }
|
|
27
28
|
this.events = new AgentEventStream(this.metadata)
|
|
@@ -42,7 +43,13 @@ export class ModelRebindingRun implements AgentRun {
|
|
|
42
43
|
})
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
respondUserInput: AgentRun['respondUserInput'] = async (command, options) => await this.withControl(signal =>
|
|
46
|
+
respondUserInput: AgentRun['respondUserInput'] = async (command, options) => await this.withControl(async (signal) => {
|
|
47
|
+
const result = await this.run.respondUserInput(command, { ...options, signal })
|
|
48
|
+
if (result === 'submitted')
|
|
49
|
+
this.reasoningEffort = command.settings?.reasoningEffort ?? this.reasoningEffort
|
|
50
|
+
return result
|
|
51
|
+
}, options)
|
|
52
|
+
|
|
46
53
|
interrupt: AgentRun['interrupt'] = async (command, options) => await this.withControl(signal => this.run.interrupt(command, { ...options, signal }), options)
|
|
47
54
|
notify: NonNullable<AgentRun['notify']> = async (command, options) => await this.withControl(async (signal) => {
|
|
48
55
|
if (!this.run.notify)
|
|
@@ -74,7 +81,56 @@ export class ModelRebindingRun implements AgentRun {
|
|
|
74
81
|
throw new AgentRunStateError({ message: 'Prompt does not target the bound run.' })
|
|
75
82
|
if (this.changing)
|
|
76
83
|
throw new AgentPromptDeferredError({ message: 'The provider model is being rebound.' })
|
|
77
|
-
const
|
|
84
|
+
const settings = command.settings ?? { model: command.model, reasoningEffort: command.reasoningEffort }
|
|
85
|
+
await this.rebind(settings.model ?? this.metadata.model, options)
|
|
86
|
+
this.lifetime.signal.throwIfAborted()
|
|
87
|
+
options.signal?.throwIfAborted()
|
|
88
|
+
const acceptance = await this.run.sendPrompt(command, { ...options, signal: AbortSignal.any([this.lifetime.signal, ...(options.signal ? [options.signal] : [])]) })
|
|
89
|
+
this.reasoningEffort = settings.reasoningEffort ?? this.reasoningEffort
|
|
90
|
+
if (acceptance.kind === 'turn' && acceptance.turnId !== this.lastEndedTurn)
|
|
91
|
+
this.activeTurn = acceptance.turnId
|
|
92
|
+
return acceptance
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async changeSettings(command: ChangeSettingsAgentCommand, options: AgentOperationOptions = {}): Promise<void> {
|
|
96
|
+
if (this.stopped)
|
|
97
|
+
throw new AgentRunStateError({ message: 'The run stopped.' })
|
|
98
|
+
if (command.sessionId !== this.metadata.sessionId || command.providerSessionId !== this.metadata.providerSessionId || command.provider !== this.metadata.provider)
|
|
99
|
+
throw new AgentRunStateError({ message: 'Settings command does not target the bound run.' })
|
|
100
|
+
if (this.dispatching || this.workspaceChanging || this.controls.size || this.changing)
|
|
101
|
+
throw new AgentPromptDeferredError({ message: 'A provider operation is already in progress.' })
|
|
102
|
+
if (this.activeTurn)
|
|
103
|
+
throw new AgentPromptDeferredError({ message: 'Settings changes require an idle turn boundary.' })
|
|
104
|
+
this.settingsChanging = true
|
|
105
|
+
const task = this.dispatchSettings(command, options)
|
|
106
|
+
this.dispatching = task
|
|
107
|
+
try {
|
|
108
|
+
await task
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
this.dispatching = undefined
|
|
112
|
+
this.settingsChanging = false
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private async dispatchSettings(command: ChangeSettingsAgentCommand, options: AgentOperationOptions): Promise<void> {
|
|
117
|
+
const signal = AbortSignal.any([this.lifetime.signal, ...(options.signal ? [options.signal] : [])])
|
|
118
|
+
signal.throwIfAborted()
|
|
119
|
+
// A failed legacy prompt rebind leaves the old run stopped. Recover its
|
|
120
|
+
// accepted settings before applying the new command on the replacement.
|
|
121
|
+
if (this.needsRebind)
|
|
122
|
+
await this.rebind(this.metadata.model ?? command.settings.model, { signal })
|
|
123
|
+
signal.throwIfAborted()
|
|
124
|
+
// Native settings updates configure the existing app-server thread, including
|
|
125
|
+
// empty threads. Keep the CLI and its warmed state instead of reopening it.
|
|
126
|
+
if (!this.run.changeSettings)
|
|
127
|
+
throw new AgentRunStateError({ message: 'The provider run does not support settings changes.' })
|
|
128
|
+
await this.run.changeSettings(command, { signal })
|
|
129
|
+
this.metadata.model = this.run.metadata.model
|
|
130
|
+
this.reasoningEffort = command.settings.reasoningEffort
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private async rebind(model: string | undefined, options: AgentOperationOptions): Promise<void> {
|
|
78
134
|
if (model && (this.needsRebind || model !== this.metadata.model)) {
|
|
79
135
|
if (this.activeTurn)
|
|
80
136
|
throw new AgentPromptDeferredError({ message: 'Model changes require an idle turn boundary.' })
|
|
@@ -88,7 +144,7 @@ export class ModelRebindingRun implements AgentRun {
|
|
|
88
144
|
await this.run.stop({ type: 'agent.stop', ...this.target(), reason: 'model_rebind' }, { signal })
|
|
89
145
|
await this.pump
|
|
90
146
|
signal.throwIfAborted()
|
|
91
|
-
replacement = await this.reopen(model, signal)
|
|
147
|
+
replacement = await this.reopen(model, signal, this.reasoningEffort)
|
|
92
148
|
signal.throwIfAborted()
|
|
93
149
|
if (replacement.metadata.providerSessionId !== this.metadata.providerSessionId || replacement.metadata.sessionId !== this.metadata.sessionId || replacement.metadata.provider !== this.metadata.provider || replacement.metadata.model !== model) {
|
|
94
150
|
identityMismatch = true
|
|
@@ -111,12 +167,6 @@ export class ModelRebindingRun implements AgentRun {
|
|
|
111
167
|
this.changing = false
|
|
112
168
|
}
|
|
113
169
|
}
|
|
114
|
-
this.lifetime.signal.throwIfAborted()
|
|
115
|
-
options.signal?.throwIfAborted()
|
|
116
|
-
const acceptance = await this.run.sendPrompt(command, { ...options, signal: AbortSignal.any([this.lifetime.signal, ...(options.signal ? [options.signal] : [])]) })
|
|
117
|
-
if (acceptance.kind === 'turn' && acceptance.turnId !== this.lastEndedTurn)
|
|
118
|
-
this.activeTurn = acceptance.turnId
|
|
119
|
-
return acceptance
|
|
120
170
|
}
|
|
121
171
|
|
|
122
172
|
stop: AgentRun['stop'] = async (command, options) => {
|
|
@@ -138,7 +188,7 @@ export class ModelRebindingRun implements AgentRun {
|
|
|
138
188
|
private async withControl<T>(operation: (signal: AbortSignal) => Promise<T>, options: AgentOperationOptions = {}): Promise<T> {
|
|
139
189
|
if (this.stopped)
|
|
140
190
|
throw new AgentRunStateError({ message: 'The run stopped.' })
|
|
141
|
-
if (this.changing || this.needsRebind)
|
|
191
|
+
if (this.settingsChanging || this.changing || this.needsRebind)
|
|
142
192
|
throw new AgentPromptDeferredError({ message: 'The provider model is being rebound.' })
|
|
143
193
|
const signal = AbortSignal.any([this.lifetime.signal, ...(options.signal ? [options.signal] : [])])
|
|
144
194
|
const task = Promise.resolve().then(async () => {
|
|
@@ -90,10 +90,18 @@ export const respondUserInputAgentCommandSchema = runningCommandBaseSchema.exten
|
|
|
90
90
|
commandId: commandIdSchema,
|
|
91
91
|
requestId: z.string().min(1),
|
|
92
92
|
answers: agentUserInputAnswersSchema,
|
|
93
|
+
settings: agentSettingsChangeSchema.optional(),
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
export const changeSettingsAgentCommandSchema = runningCommandBaseSchema.extend({
|
|
97
|
+
type: z.literal('agent.change-settings'),
|
|
98
|
+
commandId: commandIdSchema,
|
|
99
|
+
settings: z.strictObject({ model: agentModelIdSchema, reasoningEffort: agentReasoningEffortSchema }),
|
|
93
100
|
})
|
|
94
101
|
|
|
95
102
|
export const agentCommandSchema = z.union([
|
|
96
103
|
sendPromptCommandSchema,
|
|
104
|
+
changeSettingsAgentCommandSchema,
|
|
97
105
|
agentNotifyCommandSchema,
|
|
98
106
|
interruptAgentCommandSchema,
|
|
99
107
|
compactContextAgentCommandSchema,
|
|
@@ -112,3 +120,5 @@ export type CompactContextAgentCommand = z.infer<typeof compactContextAgentComma
|
|
|
112
120
|
export type StopAgentCommand = z.infer<typeof stopAgentCommandSchema>
|
|
113
121
|
export type RespondUserInputAgentCommand = z.infer<typeof respondUserInputAgentCommandSchema>
|
|
114
122
|
export type AgentCommand = z.infer<typeof agentCommandSchema>
|
|
123
|
+
|
|
124
|
+
export type ChangeSettingsAgentCommand = z.infer<typeof changeSettingsAgentCommandSchema>
|
package/src/provenance.gen.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Generated by scripts/write-provenance.ts. Do not edit by hand.
|
|
2
2
|
export const generatedProvenance = {
|
|
3
|
-
version: "0.2.0-experimental.
|
|
4
|
-
sha: "
|
|
5
|
-
buildTime: "2026-10-
|
|
3
|
+
version: "0.2.0-experimental.8",
|
|
4
|
+
sha: "4b63c95e2cc33fde3fbae56b5135006a2afac1e8",
|
|
5
|
+
buildTime: "2026-10-20T14:09:24.027Z",
|
|
6
6
|
} as const
|
|
@@ -176,12 +176,18 @@ class AgentSupervisorRuntime implements SupervisorAgentRuntime {
|
|
|
176
176
|
|
|
177
177
|
async ready(): Promise<void> { await this.recovered.promise }
|
|
178
178
|
|
|
179
|
-
async handle(input: Parameters<SupervisorAgentRuntime['handle']>[0]):
|
|
179
|
+
async handle(input: Parameters<SupervisorAgentRuntime['handle']>[0]): ReturnType<SupervisorAgentRuntime['handle']> {
|
|
180
180
|
this.attachEmitter(input.emit)
|
|
181
181
|
await this.ready()
|
|
182
182
|
if (this.eventFailure)
|
|
183
183
|
throw this.eventFailure
|
|
184
184
|
const metadata = this.input.run.metadata
|
|
185
|
+
if (input.body.type === 'agent.change-settings') {
|
|
186
|
+
if (!this.input.run.changeSettings)
|
|
187
|
+
throw new Error('Runtime does not support standalone settings changes.')
|
|
188
|
+
await this.input.run.changeSettings({ ...metadata, type: 'agent.change-settings', commandId: input.commandId, settings: input.body.settings }, { signal: input.signal })
|
|
189
|
+
return { appliedSettings: input.body.settings }
|
|
190
|
+
}
|
|
185
191
|
if (input.body.type === 'agent.send-prompt') {
|
|
186
192
|
const inputAttachmentPreparation = await this.input.prepareInputAttachments?.({
|
|
187
193
|
commandId: input.commandId,
|
|
@@ -196,6 +202,7 @@ class AgentSupervisorRuntime implements SupervisorAgentRuntime {
|
|
|
196
202
|
commandId: input.commandId,
|
|
197
203
|
mode: input.body.mode,
|
|
198
204
|
turnId: input.body.turnId,
|
|
205
|
+
settings: input.body.settings,
|
|
199
206
|
model: input.body.model,
|
|
200
207
|
reasoningEffort: input.body.reasoningEffort,
|
|
201
208
|
origin: 'user',
|
|
@@ -212,7 +219,13 @@ class AgentSupervisorRuntime implements SupervisorAgentRuntime {
|
|
|
212
219
|
inputAttachmentPreparation,
|
|
213
220
|
senderContext: input.body.user?.senderContext,
|
|
214
221
|
})
|
|
215
|
-
|
|
222
|
+
// Both prompt formats can change later defaults. Omitted legacy fields must
|
|
223
|
+
// not overwrite accepted values, and settings takes whole-object precedence.
|
|
224
|
+
const settings = input.body.settings ?? {
|
|
225
|
+
...(input.body.model === undefined ? {} : { model: input.body.model }),
|
|
226
|
+
...(input.body.reasoningEffort === undefined ? {} : { reasoningEffort: input.body.reasoningEffort }),
|
|
227
|
+
}
|
|
228
|
+
return input.body.settings || Object.keys(settings).length ? { appliedSettings: settings } : undefined
|
|
216
229
|
}
|
|
217
230
|
if (input.body.type === 'agent.interrupt') {
|
|
218
231
|
await this.input.run.interrupt({
|
|
@@ -225,7 +238,7 @@ class AgentSupervisorRuntime implements SupervisorAgentRuntime {
|
|
|
225
238
|
}, { signal: input.signal })
|
|
226
239
|
return
|
|
227
240
|
}
|
|
228
|
-
await this.input.run.respondUserInput({
|
|
241
|
+
const submission = await this.input.run.respondUserInput({
|
|
229
242
|
type: 'agent.respond-user-input',
|
|
230
243
|
provider: metadata.provider,
|
|
231
244
|
sessionId: metadata.sessionId,
|
|
@@ -233,7 +246,10 @@ class AgentSupervisorRuntime implements SupervisorAgentRuntime {
|
|
|
233
246
|
commandId: input.commandId,
|
|
234
247
|
requestId: input.body.requestId,
|
|
235
248
|
answers: input.body.answers,
|
|
249
|
+
settings: input.body.settings,
|
|
236
250
|
}, { signal: input.signal })
|
|
251
|
+
if (submission === 'submitted' && input.body.settings)
|
|
252
|
+
return { appliedSettings: input.body.settings }
|
|
237
253
|
}
|
|
238
254
|
|
|
239
255
|
async refreshSecrets(input: {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AgentSettingsChange } from '../../protocol'
|
|
1
2
|
import type { SessionBootstrapBody } from '../../supervisor-protocol/bootstrap'
|
|
2
3
|
import type { CommandEnvelope } from '../../supervisor-protocol/envelopes/control-plane-to-supervisor'
|
|
3
4
|
import type { DurableEventEnvelope } from '../../supervisor-protocol/envelopes/supervisor-to-control-plane'
|
|
@@ -6,6 +7,7 @@ import type { SupervisorAgentRunSnapshot } from '../../supervisor-protocol/super
|
|
|
6
7
|
import { mkdirSync } from 'node:fs'
|
|
7
8
|
import { dirname } from 'node:path'
|
|
8
9
|
import { Database } from 'bun:sqlite'
|
|
10
|
+
import { agentSettingsChangeSchema } from '../../protocol'
|
|
9
11
|
import { sessionBootstrapBodySchema } from '../../supervisor-protocol/bootstrap'
|
|
10
12
|
import { wireCommandBodySchema } from '../../supervisor-protocol/command-body'
|
|
11
13
|
import { wireEventBodySchema } from '../../supervisor-protocol/event-body'
|
|
@@ -215,6 +217,27 @@ export class SupervisorStore {
|
|
|
215
217
|
})()
|
|
216
218
|
}
|
|
217
219
|
|
|
220
|
+
/** Restore accepted defaults in application order; the bootstrap remains immutable. */
|
|
221
|
+
appliedSettings(): AgentSettingsChange | undefined {
|
|
222
|
+
const row = this.database.query<{ value: string }, []>('SELECT value FROM metadata WHERE key = \'applied_settings\'').get()
|
|
223
|
+
return row ? agentSettingsChangeSchema.parse(JSON.parse(row.value)) : undefined
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** Commit settings, the outcome and inbox state together, including partial prompt/answer updates. */
|
|
227
|
+
finishCommand(input: { commandSeq: number, body: WireEventBody, appliedSettings?: AgentSettingsChange }): DurableEventEnvelope {
|
|
228
|
+
return this.database.transaction(() => {
|
|
229
|
+
const event = this.appendDurableEvent({ occurredAt: new Date().toISOString(), body: input.body })
|
|
230
|
+
const applied = input.body.type === 'supervisor.command.applied'
|
|
231
|
+
if (applied && input.appliedSettings) {
|
|
232
|
+
const settings = agentSettingsChangeSchema.parse({ ...this.appliedSettings(), ...input.appliedSettings })
|
|
233
|
+
this.database.query(`INSERT INTO metadata (key, value) VALUES ('applied_settings', ?)
|
|
234
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value`).run(canonicalJson(settings))
|
|
235
|
+
}
|
|
236
|
+
this.markCommand({ commandSeq: input.commandSeq, status: applied ? 'applied' : 'failed' })
|
|
237
|
+
return event
|
|
238
|
+
})()
|
|
239
|
+
}
|
|
240
|
+
|
|
218
241
|
/** Commit before calling a provider: a process crash makes the outcome uncertain. */
|
|
219
242
|
beginCommandDispatch(commandSeq: number): void {
|
|
220
243
|
this.database.query('INSERT INTO command_dispatches(seq) VALUES (?)').run(commandSeq)
|
|
@@ -228,6 +251,12 @@ export class SupervisorStore {
|
|
|
228
251
|
JOIN command_dispatches ON command_dispatches.seq=commands.seq ORDER BY commands.seq
|
|
229
252
|
`).all()
|
|
230
253
|
for (const row of rows) {
|
|
254
|
+
// Reapplying a full settings pair is idempotent and submits no input. Prompt
|
|
255
|
+
// commands retain the existing uncertain-delivery policy below.
|
|
256
|
+
if (commandBodyFromRow(row).type === 'agent.change-settings') {
|
|
257
|
+
this.database.query('DELETE FROM command_dispatches WHERE seq = ?').run(row.seq)
|
|
258
|
+
continue
|
|
259
|
+
}
|
|
231
260
|
this.appendDurableEvent({
|
|
232
261
|
occurredAt: new Date().toISOString(),
|
|
233
262
|
body: {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AgentSettingsChange } from '../protocol'
|
|
1
2
|
import type { EphemeralCredentials, SessionBootstrapBody, SessionBootstrapGitToken } from '../supervisor-protocol/bootstrap'
|
|
2
3
|
import type { WireCommandBody } from '../supervisor-protocol/command-body'
|
|
3
4
|
import type { WireEventBody } from '../supervisor-protocol/event-body'
|
|
@@ -13,7 +14,7 @@ export interface SupervisorAgentRuntime {
|
|
|
13
14
|
body: WireCommandBody
|
|
14
15
|
signal: AbortSignal
|
|
15
16
|
emit: (body: WireEventBody) => void
|
|
16
|
-
}) => Promise<void>
|
|
17
|
+
}) => Promise<void | { appliedSettings: AgentSettingsChange }>
|
|
17
18
|
refreshSecrets?: (input: {
|
|
18
19
|
credentials?: EphemeralCredentials
|
|
19
20
|
gitToken?: SessionBootstrapGitToken
|
|
@@ -105,6 +105,7 @@ export async function createResidentSupervisor(input: {
|
|
|
105
105
|
binding: binding.status,
|
|
106
106
|
profile: input.profile,
|
|
107
107
|
agentCore: agentCoreProvenance(),
|
|
108
|
+
capabilities: { 'model-change': 'v1' },
|
|
108
109
|
}),
|
|
109
110
|
getBoundSessionId: () => binding.status === 'bound' ? binding.sessionId : undefined,
|
|
110
111
|
getLocalEventMax: () => binding.status === 'bound' ? binding.store.localEventMax() : 0,
|
|
@@ -207,7 +207,7 @@ export class SupervisorRuntimeHandler implements RuntimeConnectionHandler {
|
|
|
207
207
|
gitToken: this.dependencies.gitToken,
|
|
208
208
|
}
|
|
209
209
|
this.createAgentTask = this.dependencies.providerFactory.create({
|
|
210
|
-
bootstrap: binding.body,
|
|
210
|
+
bootstrap: { ...binding.body, agent: { ...binding.body.agent, ...this.dependencies.store.appliedSettings() } },
|
|
211
211
|
...initialSecrets,
|
|
212
212
|
rpc: this.rpc,
|
|
213
213
|
signal: this.lifecycleController.signal,
|
|
@@ -240,42 +240,42 @@ export class SupervisorRuntimeHandler implements RuntimeConnectionHandler {
|
|
|
240
240
|
agent: SupervisorAgentRuntime
|
|
241
241
|
command: CommandEnvelope & { body: WireCommandBody }
|
|
242
242
|
}): Promise<void> {
|
|
243
|
+
this.dependencies.store.beginCommandDispatch(input.command.commandSeq)
|
|
244
|
+
let result: Awaited<ReturnType<SupervisorAgentRuntime['handle']>>
|
|
243
245
|
try {
|
|
244
|
-
|
|
245
|
-
await input.agent.handle({
|
|
246
|
+
result = await input.agent.handle({
|
|
246
247
|
commandId: input.command.commandId,
|
|
247
248
|
commandSeq: input.command.commandSeq,
|
|
248
249
|
body: input.command.body,
|
|
249
250
|
signal: this.lifecycleController.signal,
|
|
250
251
|
emit: body => this.emit(body),
|
|
251
252
|
})
|
|
252
|
-
this.dependencies.store.markCommand({ commandSeq: input.command.commandSeq, status: 'applied' })
|
|
253
|
-
this.emit({
|
|
254
|
-
type: 'supervisor.command.applied',
|
|
255
|
-
payload: {
|
|
256
|
-
commandId: input.command.commandId,
|
|
257
|
-
commandSeq: input.command.commandSeq,
|
|
258
|
-
bodyType: input.command.body.type,
|
|
259
|
-
},
|
|
260
|
-
})
|
|
261
253
|
}
|
|
262
|
-
catch {
|
|
254
|
+
catch (error) {
|
|
263
255
|
if (this.lifecycleController.signal.aborted)
|
|
264
256
|
return
|
|
265
|
-
this.dependencies.
|
|
266
|
-
this.
|
|
267
|
-
|
|
268
|
-
payload: {
|
|
257
|
+
this.dependencies.logger.error({ err: error, commandId: input.command.commandId, bodyType: input.command.body.type }, 'Agent command execution failed')
|
|
258
|
+
this.sendEvent(this.dependencies.store.finishCommand({
|
|
259
|
+
commandSeq: input.command.commandSeq,
|
|
260
|
+
body: { type: 'supervisor.command.failed', payload: {
|
|
269
261
|
commandId: input.command.commandId,
|
|
270
262
|
commandSeq: input.command.commandSeq,
|
|
271
263
|
bodyType: input.command.body.type,
|
|
272
|
-
failure: {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
},
|
|
277
|
-
})
|
|
264
|
+
failure: { type: 'command', detail: 'Agent command execution failed.' },
|
|
265
|
+
} },
|
|
266
|
+
}))
|
|
267
|
+
return
|
|
278
268
|
}
|
|
269
|
+
// Persistence and socket errors must not turn an accepted command into a failed one.
|
|
270
|
+
this.sendEvent(this.dependencies.store.finishCommand({
|
|
271
|
+
commandSeq: input.command.commandSeq,
|
|
272
|
+
appliedSettings: result?.appliedSettings,
|
|
273
|
+
body: { type: 'supervisor.command.applied', payload: {
|
|
274
|
+
commandId: input.command.commandId,
|
|
275
|
+
commandSeq: input.command.commandSeq,
|
|
276
|
+
bodyType: input.command.body.type,
|
|
277
|
+
} },
|
|
278
|
+
}))
|
|
279
279
|
}
|
|
280
280
|
|
|
281
281
|
private emit(body: WireEventBody): void {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import z from 'zod'
|
|
2
|
-
import { agentPromptModeSchema, agentPromptSchema, agentReasoningEffortSchema, agentUserInputAnswersSchema, jsonObjectSchema, jsonUtf8ByteLength } from '../protocol'
|
|
2
|
+
import { agentPromptModeSchema, agentPromptSchema, agentReasoningEffortSchema, agentSettingsChangeSchema, agentUserInputAnswersSchema, jsonObjectSchema, jsonUtf8ByteLength } from '../protocol'
|
|
3
3
|
|
|
4
4
|
const wireSenderContextSchema = z.discriminatedUnion('kind', [
|
|
5
5
|
z.object({ kind: z.literal('application') }).strict(),
|
|
@@ -43,6 +43,7 @@ const wireSendPromptCommandSchema = z.object({
|
|
|
43
43
|
turnId: z.string().min(1).optional(),
|
|
44
44
|
model: z.string().trim().min(1).max(256).optional(),
|
|
45
45
|
reasoningEffort: agentReasoningEffortSchema.optional(),
|
|
46
|
+
settings: agentSettingsChangeSchema.optional(),
|
|
46
47
|
prompt: agentPromptSchema,
|
|
47
48
|
attachments: z.array(wireInputAttachmentRefSchema).superRefine((attachments, context) => {
|
|
48
49
|
const attachmentIds = new Set<string>()
|
|
@@ -69,10 +70,12 @@ const wireRespondUserInputCommandSchema = z.object({
|
|
|
69
70
|
type: z.literal('agent.respond-user-input'),
|
|
70
71
|
requestId: z.string().min(1),
|
|
71
72
|
answers: agentUserInputAnswersSchema,
|
|
73
|
+
settings: agentSettingsChangeSchema.optional(),
|
|
72
74
|
user: wireCommandUserSchema.optional(),
|
|
73
75
|
}).strict()
|
|
74
76
|
|
|
75
77
|
export const wireCommandBodySchema = z.discriminatedUnion('type', [
|
|
78
|
+
z.strictObject({ type: z.literal('agent.change-settings'), settings: z.strictObject({ model: z.string().trim().min(1).max(256), reasoningEffort: agentReasoningEffortSchema }) }),
|
|
76
79
|
wireSendPromptCommandSchema,
|
|
77
80
|
wireInterruptCommandSchema,
|
|
78
81
|
wireRespondUserInputCommandSchema,
|