Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 96 additions & 1 deletion src/core/task/Task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
type TaskMetadata,
type TaskEvents,
type ProviderSettings,
type ReasoningEffortExtended,
type TokenUsage,
type ToolUsage,
type ToolName,
Expand Down Expand Up @@ -318,6 +319,13 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
// API
apiConfiguration: ProviderSettings
api: ApiHandler
// DTE series 2/5: task-local thinking effort override. Transient per-task state —
// never persisted to settings; cleared on dispose (see dispose()).
private runtimeThinkingEffort?: ReasoningEffortExtended
private runtimeThinkingEffortSource?: string
// Settings-derived effort captured when the override activates, so clearing
// (undefined) restores it in the in-memory apiConfiguration copy.
private preOverrideReasoningEffort?: ProviderSettings["reasoningEffort"]
private rateLimitClock: RateLimitClock
private autoApprovalHandler: AutoApprovalHandler

Expand Down Expand Up @@ -1630,14 +1638,89 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
* Updates the API configuration and rebuilds the API handler.
* There is no tool-protocol switching or tool parser swapping.
*
* DTE series 2/5: when a task-local thinking effort override is active
* (`setRuntimeThinkingEffort`), the incoming configuration's `reasoningEffort`
* becomes the new restore value and the override is re-applied on top of the
* fresh in-memory copy — clearing the override later restores the NEW profile's
* value, not a stale one.
*
* @param newApiConfiguration - The new API configuration to use
*/
public updateApiConfiguration(newApiConfiguration: ProviderSettings): void {
// Update the configuration and rebuild the API handler
this.apiConfiguration = newApiConfiguration
if (this.runtimeThinkingEffort !== undefined) {
// DTE series 2/5: a task-local override is active, so re-capture the
// incoming profile's value as the restore value and re-apply the
// override on top of the new in-memory copy — clearing the override
// must restore the NEW profile's value, not the stale one.
this.preOverrideReasoningEffort = newApiConfiguration.reasoningEffort
this.apiConfiguration = { ...newApiConfiguration, reasoningEffort: this.runtimeThinkingEffort }
} else {
this.apiConfiguration = newApiConfiguration
}
this.api = buildApiHandler(this.apiConfiguration)
}

/**
* DTE series 2/5: sets — or clears with `undefined` — the task-local thinking
* effort override.
*
* Resolution order for the affected requests (strongest first): this
* task-local override → settings `reasoningEffort` → model default. The
* override applies to the NEXT API request only (no mid-stream effect): it is
* passed per request as `metadata.reasoningEffort` and, while active, is
* merged into the in-memory `apiConfiguration` copy (profile-switch /
* `updateApiConfiguration` precedent) so the rebuilt handler reflects it too.
* `undefined` clears the override and restores the settings-derived value in
* the copy. Nothing is ever written to persisted settings.
*
* @param effort - The task-local effort, or `undefined` to clear.
* @param source - Optional provenance label (UI wiring lands in a later PR).
*/
public setRuntimeThinkingEffort(effort: ReasoningEffortExtended | undefined, source?: string): void {
const wasActive = this.runtimeThinkingEffort !== undefined
this.runtimeThinkingEffort = effort
this.runtimeThinkingEffortSource = effort === undefined ? undefined : source

if (effort !== undefined) {
// Capture the settings-derived value once so clearing can restore it.
if (!wasActive) {
this.preOverrideReasoningEffort = this.apiConfiguration.reasoningEffort
}
// Merge into the in-memory copy (never the persisted settings object).
this.apiConfiguration = { ...this.apiConfiguration, reasoningEffort: effort }
} else if (wasActive) {
// Restore the settings-derived value captured when the override activated.
this.apiConfiguration = { ...this.apiConfiguration, reasoningEffort: this.preOverrideReasoningEffort }
this.preOverrideReasoningEffort = undefined
} else {
// Already inactive: nothing to clear.
return
}

// Rebuild the handler from the updated copy so the next request uses it.
this.api = buildApiHandler(this.apiConfiguration)
}

/**
* DTE series 2/5: reads the current task-local thinking effort override.
*/
public getRuntimeThinkingEffort(): { effort?: ReasoningEffortExtended; source?: string } {
return {
effort: this.runtimeThinkingEffort,
source: this.runtimeThinkingEffortSource,
}
}

/**
* DTE series 2/5: metadata fragment carrying the active task-local effort
* override on a single request. Empty when no override is active, so the
* existing settings resolution applies unchanged.
*/
private getRuntimeThinkingEffortMetadata(): Pick<ApiHandlerCreateMessageMetadata, "reasoningEffort"> {
return this.runtimeThinkingEffort !== undefined ? { reasoningEffort: this.runtimeThinkingEffort } : {}
}

public async submitUserMessage(
text: string,
images?: string[],
Expand Down Expand Up @@ -1754,6 +1837,8 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
parallelToolCalls: true,
}
: {}),
// DTE series 2/5: carry the active task-local effort override.
...this.getRuntimeThinkingEffortMetadata(),
}
// Generate environment details to include in the condensed summary
const environmentDetails = await getEnvironmentDetails(this, true)
Expand Down Expand Up @@ -2514,6 +2599,10 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
}
}

/**
* Centralized task teardown: releases task resources and resets transient
* task-local state.
*/
public dispose(): void {
console.log(`[Task#dispose] disposing task ${this.taskId}.${this.instanceId}`)

Expand Down Expand Up @@ -4177,6 +4266,8 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
parallelToolCalls: true,
}
: {}),
// DTE series 2/5: carry the active task-local effort override.
...this.getRuntimeThinkingEffortMetadata(),
}

try {
Expand Down Expand Up @@ -4403,6 +4494,8 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
parallelToolCalls: true,
}
: {}),
// DTE series 2/5: carry the active task-local effort override.
...this.getRuntimeThinkingEffortMetadata(),
}

// Only generate environment details when context management will actually run.
Expand Down Expand Up @@ -4568,6 +4661,8 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
taskId: this.taskId,
suppressPreviousResponseId: this.skipPrevResponseIdOnce,
abortSignal,
// DTE series 2/5: carry the active task-local effort override for this request.
...this.getRuntimeThinkingEffortMetadata(),
// Include tools whenever they are present.
...(shouldIncludeTools
? {
Expand Down
Loading
Loading