mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
Add experimental turn additional context (#24154)
## Summary
Adds experimental `additionalContext` support to `turn/start` and
`turn/steer` so clients can provide ephemeral external context, such as
browser or automation state, without turning that plumbing into a
visible user prompt or triggering user-prompt lifecycle behavior.
## API Shape
The parameter shape is:
```ts
additionalContext?: Record<string, {
value: string
kind: "untrusted" | "application"
}> | null
```
Example:
```json
{
"additionalContext": {
"browser_info": {
"value": "Active tab is CI failures.",
"kind": "untrusted"
},
"automation_info": {
"value": "CI rerun is in progress.",
"kind": "application"
}
}
}
```
The keys are opaque and caller-defined.
## Context Injection
When provided, accepted entries are inserted into model context as
hidden contextual message items, not as visible thread user-message
items.
`kind: "untrusted"` entries are inserted with role `user`:
```text
<external_${key}>${value}</external_${key}>
```
`kind: "application"` entries are inserted with role `developer`:
```text
<${key}>${value}</${key}>
```
Values are not escaped. Each value is truncated to 1k approximate tokens
before wrapping.
For `turn/start`, accepted additional context is inserted before normal
user input. For `turn/steer`, additional context is merged only when the
steer includes non-empty user input; context-only steers still reject as
empty input.
## Dedupe Strategy
`AdditionalContextStore` lives on session state and stores the latest
complete additional-context map.
Each `turn/start` or non-empty `turn/steer` treats its
`additionalContext` as the current complete set of values. Entries are
injected only when the key is new or the exact entry for that key
changed, including `value` or `kind`. After merging, the store is
replaced with the provided map, so omitted keys are removed from the
retained set and can be injected again later if reintroduced.
Omitting `additionalContext`, passing `null`, or passing an empty object
resets the store to empty and injects nothing.
## What Changed
- Threads experimental v2 `additionalContext` through app-server into
core turn start and steer handling.
- Adds separate contextual fragment types for untrusted user-role
context and application developer-role context.
- Uses pending response input items so additional context can be
combined with normal user input without treating it as prompt text.
- Adds integration coverage for start/steer flow, role routing,
dedupe/reset behavior, deletion/re-add behavior, hook-blocked input
behavior, empty context-only steer rejection, external-fragment marker
matching, and truncation.
This commit is contained in:
1 parent
cd934c8bcb
commit
768848ab6f
108 files changed
+1583
-57
No files matched your search
@@ -10,6 +10,7 @@ use tracing::debug_span;
|
||||
use tracing::info_span;
|
||||
|
||||
use crate::session::SteerInputError;
|
||||
use crate::session::TurnInput;
|
||||
use crate::session::session::Session;
|
||||
use crate::session::session::SessionSettingsUpdate;
|
||||
|
||||
@@ -194,6 +195,7 @@ pub(super) async fn user_input_or_turn_inner(
|
||||
environments,
|
||||
final_output_json_schema,
|
||||
responsesapi_client_metadata,
|
||||
additional_context,
|
||||
thread_settings,
|
||||
} = op
|
||||
else {
|
||||
@@ -224,6 +226,7 @@ pub(super) async fn user_input_or_turn_inner(
|
||||
let accepted_items = match sess
|
||||
.steer_input(
|
||||
items.clone(),
|
||||
additional_context.clone(),
|
||||
/*expected_turn_id*/ None,
|
||||
responsesapi_client_metadata.clone(),
|
||||
)
|
||||
@@ -246,9 +249,20 @@ pub(super) async fn user_input_or_turn_inner(
|
||||
)
|
||||
.await;
|
||||
let accepted_items = items.clone();
|
||||
let additional_context_input = {
|
||||
let mut state = sess.state.lock().await;
|
||||
state.additional_context.merge(additional_context)
|
||||
};
|
||||
let mut task_input = additional_context_input
|
||||
.into_iter()
|
||||
.map(TurnInput::ResponseInputItem)
|
||||
.collect::<Vec<_>>();
|
||||
if !items.is_empty() {
|
||||
task_input.push(TurnInput::UserInput(items));
|
||||
}
|
||||
sess.spawn_task(
|
||||
Arc::clone(¤t_context),
|
||||
items,
|
||||
task_input,
|
||||
crate::tasks::RegularTask::new(),
|
||||
)
|
||||
.await;
|
||||
|
||||
Reference in new issue
Block a user