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
26 changes: 26 additions & 0 deletions packages/sdk/src/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import type {
PromptResponse,
PromptResultResponse,
PromptUsage,
ProvidersConfig,
SessionData,
SessionEnv,
SessionStore,
Expand Down Expand Up @@ -122,6 +123,30 @@ interface InternalTaskOptions<S extends v.GenericSchema | undefined> extends Tas
inheritedThinkingLevel?: ThinkingLevel;
}

/**
* Build an `onPayload` hook that injects `store: <bool>` into OpenAI Responses
* API request payloads when the user has set
* `providers.openai.storeResponses`. Returns undefined when the knob is unset
* so pi-ai's default (no hook) keeps applying. The hook is a no-op for any
* model whose `api !== 'openai-responses'` (Codex, Azure, Anthropic, etc.).
*
* Resolves the user-facing 404 reported in flue#77: pi-ai hardcodes
* `store: false` on the OpenAI Responses provider, so item references
* (`fc_*`, `msg_*`) emitted on subsequent multi-turn calls 404 against items
* the server never persisted.
*/
function buildOpenAIStorePayloadHook(
providers: ProvidersConfig | undefined,
): ((payload: unknown, model: Model<any>) => unknown) | undefined {
const storeResponses = providers?.openai?.storeResponses;
if (storeResponses === undefined) return undefined;
return (payload, model) => {
if (model.api !== 'openai-responses') return undefined;
if (typeof payload !== 'object' || payload === null) return undefined;
return { ...(payload as Record<string, unknown>), store: storeResponses };
};
}

/** In-memory session store. Sessions persist for the lifetime of the process. */
export class InMemorySessionStore implements SessionStore {
private store = new Map<string, SessionData>();
Expand Down Expand Up @@ -214,6 +239,7 @@ export class Session implements FlueSession {
},
getApiKey: (provider) => this.getProviderApiKey(provider),
toolExecution: 'parallel',
onPayload: buildOpenAIStorePayloadHook(this.config.providers),
});

this.eventCallback = options.onAgentEvent;
Expand Down
12 changes: 12 additions & 0 deletions packages/sdk/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,18 @@ export interface ProviderSettings {
* come from the agent's runtime env instead of process-global env vars.
*/
apiKey?: string;
/**
* Persist OpenAI Responses API calls server-side so that `msg_*` / `fc_*`
* item references emitted on later turns resolve. Required for multi-turn
* `session.task()` flows that rely on item-id linkage (see flue#77). Only
* applied when the resolved model's `api === "openai-responses"`; ignored
* for Codex, Azure, and other provider APIs.
*
* Default unset → pi-ai's current behavior (`store: false`). Note: enabling
* this opts the conversation into OpenAI's server-side storage and its
* retention policy.
*/
storeResponses?: boolean;
}

export type ProvidersConfig = Record<string, ProviderSettings>;
Expand Down