pi-openai-toolkit 0.14.9 → 0.14.10
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/docs/internals.md +2 -0
- package/package.json +5 -19
- package/src/auto-mode/reviewer.ts +16 -6
- package/src/context-management/messages.ts +4 -1
- package/src/context-management/tools.ts +9 -2
- package/src/extension-runtime.ts +1 -0
- package/src/payload-rewrite.ts +15 -3
- package/src/serializer.ts +127 -5
- package/tsconfig.check.json +0 -4
- package/extensions/auto-mode.ts +0 -1
- package/extensions/codex-astra.ts +0 -1
- package/extensions/compaction.ts +0 -1
- package/extensions/image-generation.ts +0 -1
- package/extensions/web-search.ts +0 -1
package/docs/internals.md
CHANGED
|
@@ -52,6 +52,7 @@ sequenceDiagram
|
|
|
52
52
|
Toolkit->>Toolkit: verify successful notes write in this window
|
|
53
53
|
Toolkit-->>Model: checkpoint receipt with the successful note path
|
|
54
54
|
Toolkit-->>Pi: window marker appended, previous window trim scheduled
|
|
55
|
+
Note over Model: Context switch is complete; read the receipt once, then resume the active task
|
|
55
56
|
Model->>Backend: next request with the new x-codex-window-id
|
|
56
57
|
Pi->>Toolkit: session_before_compact
|
|
57
58
|
Toolkit-->>Pi: no-summary boundary consuming the scheduled trim
|
|
@@ -62,6 +63,7 @@ State invariants the lifecycle code must keep honest:
|
|
|
62
63
|
- The checkpoint gate verifies a successful `notes` `append_to_file` or `write_file` against the persisted session branch after the latest window boundary, so it survives restarts and forks. A response with `ok: false` or `success: false` is not successful, even when HTTP returns 200.
|
|
63
64
|
- The gate uses the live session/branch identity from `ctx.sessionManager`; cached manager identity must not hide a valid current-window pair, and old or foreign-session evidence must remain rejected.
|
|
64
65
|
- Every rollover carries the most recent successful checkpoint path in its handoff message. The optional `thread_hint` is supplemental; a hint failure must not erase the local receipt.
|
|
66
|
+
- A successful rollover closes the pre-rollover checkpoint phase. The first turn in the new window reads the receipt once and resumes the active user task; it must not immediately create another checkpoint or call `new_context` as part of that handoff. A later checkpoint is only for a later rollover.
|
|
65
67
|
- `new_context` has no force escape hatch: a persisted successful notes checkpoint in the current window is always required before rollover. The model must wait for the notes result to be persisted before retrying.
|
|
66
68
|
- After a marker is accepted for sending, the manager keeps a session/window-anchored pending guard until that exact target marker is persisted or the session changes. A duplicate `new_context` during this persistence gap returns `started: false` and sends no second marker; projected/in-memory messages do not retire the guard.
|
|
67
69
|
- Budget checks are skipped until the current window has produced its own assistant usage. Acting on the previous window's usage anchor would burn the once-per-window reminder on a false alarm.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-openai-toolkit",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.10",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -32,20 +32,7 @@
|
|
|
32
32
|
"codex",
|
|
33
33
|
"responses"
|
|
34
34
|
],
|
|
35
|
-
"pi": {
|
|
36
|
-
"extensions": [
|
|
37
|
-
"./extensions/compaction.ts",
|
|
38
|
-
"./extensions/web-search.ts",
|
|
39
|
-
"./extensions/image-generation.ts",
|
|
40
|
-
"./extensions/auto-mode.ts",
|
|
41
|
-
"./extensions/codex-astra.ts"
|
|
42
|
-
]
|
|
43
|
-
},
|
|
44
35
|
"files": [
|
|
45
|
-
"extensions/compaction.ts",
|
|
46
|
-
"extensions/web-search.ts",
|
|
47
|
-
"extensions/image-generation.ts",
|
|
48
|
-
"extensions/auto-mode.ts",
|
|
49
36
|
"src/compact-client.ts",
|
|
50
37
|
"src/compaction-projection.ts",
|
|
51
38
|
"src/pi-context-hook.ts",
|
|
@@ -81,7 +68,6 @@
|
|
|
81
68
|
"src/auto-mode/reviewer.ts",
|
|
82
69
|
"src/auto-mode/classifier.ts",
|
|
83
70
|
"src/auto-mode/types.ts",
|
|
84
|
-
"extensions/codex-astra.ts",
|
|
85
71
|
"src/codex-astra/effort-planner.ts",
|
|
86
72
|
"src/codex-astra/extension.ts",
|
|
87
73
|
"src/model-scope.ts",
|
|
@@ -120,10 +106,10 @@
|
|
|
120
106
|
"@earendil-works/pi-tui": ">=0.85.1"
|
|
121
107
|
},
|
|
122
108
|
"devDependencies": {
|
|
123
|
-
"@earendil-works/pi-agent-core": "0.
|
|
124
|
-
"@earendil-works/pi-ai": "0.
|
|
125
|
-
"@earendil-works/pi-coding-agent": "0.
|
|
126
|
-
"@earendil-works/pi-tui": "0.
|
|
109
|
+
"@earendil-works/pi-agent-core": "0.86.0",
|
|
110
|
+
"@earendil-works/pi-ai": "0.86.0",
|
|
111
|
+
"@earendil-works/pi-coding-agent": "0.86.0",
|
|
112
|
+
"@earendil-works/pi-tui": "0.86.0",
|
|
127
113
|
"@types/node": "24.12.4",
|
|
128
114
|
"bun": "1.2.23",
|
|
129
115
|
"typescript": "5.9.3"
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
Api,
|
|
3
|
+
AssistantMessage,
|
|
4
|
+
Context,
|
|
5
|
+
Message,
|
|
6
|
+
Model,
|
|
7
|
+
Tool,
|
|
8
|
+
ToolCall,
|
|
9
|
+
} from "@earendil-works/pi-ai";
|
|
2
10
|
import type { ModelRegistry } from "@earendil-works/pi-coding-agent";
|
|
3
11
|
import { parseModelSpec } from "../runtime";
|
|
4
12
|
import { backfillVerdictFields, buildReviewPrompt, reviewerSystemPrompt } from "./prompt";
|
|
@@ -93,11 +101,13 @@ function firstTextBlock(message: AssistantMessage): string {
|
|
|
93
101
|
.trim();
|
|
94
102
|
}
|
|
95
103
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
104
|
+
/**
|
|
105
|
+
* Pi 0.85 typed tool-call arguments as `Record<string, any>` and Pi 0.86 tightened
|
|
106
|
+
* them to the public `JsonObject`. Narrow with Pi's exported `ToolCall` instead of a
|
|
107
|
+
* handwritten shape so the guard tracks whichever contract the installed Pi declares.
|
|
108
|
+
*/
|
|
109
|
+
function toolCallBlocks(message: AssistantMessage): ToolCall[] {
|
|
110
|
+
return message.content.filter((block): block is ToolCall => block.type === "toolCall");
|
|
101
111
|
}
|
|
102
112
|
|
|
103
113
|
type CompletionAttempt = {
|
|
@@ -35,7 +35,7 @@ export const CONTEXT_WINDOW_COMPACTION_SUMMARY =
|
|
|
35
35
|
"[Pi Codex context-window boundary; no conversation summary was generated.]";
|
|
36
36
|
|
|
37
37
|
const CONTEXT_WINDOW_GUIDANCE = `<context_window_guidance>
|
|
38
|
-
|
|
38
|
+
Before calling new_context, checkpoint the active request, known history IDs, decisions, progress, learnings and next steps in notes, and wait for that result to be persisted; only a persisted successful result in the current window unlocks the rollover. If new_context reports that a rollover is already scheduled, do not call it again in the same window. When this message includes a completed context-switch handoff, follow that post-rollover section first instead of restarting the pre-rollover checkpoint steps. A thread hint is supplemental and must not replace the local checkpoint receipt. No conversation summary carries over, and history is only for missing details.
|
|
39
39
|
</context_window_guidance>`;
|
|
40
40
|
|
|
41
41
|
export function renderContextWindowMessage(
|
|
@@ -51,8 +51,11 @@ export function renderContextWindowMessage(
|
|
|
51
51
|
];
|
|
52
52
|
if (identity.previousWindowId) lines.push(`Previous context window id: ${identity.previousWindowId}`);
|
|
53
53
|
if (checkpoint) {
|
|
54
|
+
lines.push("Context switch completed. This is the first turn in the new context window.");
|
|
54
55
|
lines.push("Checkpoint successfully written:");
|
|
55
56
|
lines.push(` Read this note before doing anything else with notes action "read_file": ${JSON.stringify(checkpoint.path)}`);
|
|
57
|
+
lines.push("After reading the checkpoint receipt, resume the active user task.");
|
|
58
|
+
lines.push("Do not immediately create another checkpoint or call new_context as part of this handoff. Only prepare a new checkpoint when a later context rollover is actually needed.");
|
|
56
59
|
}
|
|
57
60
|
if (threadHint) lines.push(threadHint);
|
|
58
61
|
lines.push("</context_window>");
|
|
@@ -94,7 +94,8 @@ export function createContextManagementTools(
|
|
|
94
94
|
parameters: NEW_CONTEXT_PARAMETERS,
|
|
95
95
|
promptSnippet: "Start a new remote Codex context window without summarizing history.",
|
|
96
96
|
promptGuidelines: [
|
|
97
|
-
"
|
|
97
|
+
"Before this new_context call, checkpoint active work in notes; only a persisted successful notes append/write in the current window unlocks this rollover, and no conversation summary carries over.",
|
|
98
|
+
"A successful new_context completes one context switch. In the next window, read the checkpoint receipt first when present, then resume the active user task; do not immediately create another checkpoint or call new_context as part of that handoff.",
|
|
98
99
|
"Wait for the notes tool result before calling new_context; an in-flight or failed write is not a checkpoint. If new_context reports that a rollover is already scheduled, do not call it again in the same window.",
|
|
99
100
|
],
|
|
100
101
|
executionMode: "sequential",
|
|
@@ -116,7 +117,12 @@ export function createContextManagementTools(
|
|
|
116
117
|
trimPreviousWindow: true,
|
|
117
118
|
});
|
|
118
119
|
return {
|
|
119
|
-
content: [{
|
|
120
|
+
content: [{
|
|
121
|
+
type: "text",
|
|
122
|
+
text: started
|
|
123
|
+
? "Context switch scheduled successfully. In the next context window, read the checkpoint receipt first when present, then resume the active user task; do not immediately create another checkpoint or call new_context."
|
|
124
|
+
: "A new context window is already scheduled.",
|
|
125
|
+
}],
|
|
120
126
|
details: { started },
|
|
121
127
|
};
|
|
122
128
|
},
|
|
@@ -161,6 +167,7 @@ export function createContextManagementTools(
|
|
|
161
167
|
promptSnippet: "Read and checkpoint remote Codex notes across context windows.",
|
|
162
168
|
promptGuidelines: [
|
|
163
169
|
"Before calling new_context, checkpoint the current turn's active work (unfinished tasks, decisions, open questions, references) into notes with append_to_file or write_file so it survives the window change; wait for that result to be persisted before calling new_context.",
|
|
170
|
+
"After a successful new_context handoff, read the checkpoint receipt first when present and resume the active user task; do not immediately create another checkpoint or call new_context unless a later rollover is actually needed.",
|
|
164
171
|
"When a large task spans multiple context windows, keep a running note per line of work and read it at the start of each new window; append new state instead of replacing it unless the note is stale.",
|
|
165
172
|
"Prefer read_file or search_contents for looking things up; reserve write_file for explicit rewrite/clear and append_to_file for incremental state.",
|
|
166
173
|
"Keep note text concise and self-contained: it may be read later without the rest of the conversation, so include identifiers and verbatim key decisions, not hearsay summaries.",
|
package/src/extension-runtime.ts
CHANGED
package/src/payload-rewrite.ts
CHANGED
|
@@ -337,7 +337,11 @@ export function removeNativeCompactionRetainedMessages(args: {
|
|
|
337
337
|
}
|
|
338
338
|
const firstKept = findEntryIndexByIdBeforeBoundary(args.branchEntries, args.compactionEntry.firstKeptEntryId, boundary);
|
|
339
339
|
if (firstKept === undefined) return { ok: false, reason: "first-kept-entry-not-found" };
|
|
340
|
-
const
|
|
340
|
+
const summaryMessage = sessionEntryToContextMessages(args.compactionEntry).find(
|
|
341
|
+
(message) => message.role === "compactionSummary",
|
|
342
|
+
);
|
|
343
|
+
if (!summaryMessage) return { ok: false, reason: "compaction-summary-not-found" };
|
|
344
|
+
const summary = structuredClone(summaryMessage);
|
|
341
345
|
const summaryIndex = args.messages.findIndex((message) => areEquivalentValues(message, summary));
|
|
342
346
|
if (summaryIndex < 0) return { ok: false, reason: "compaction-summary-not-found" };
|
|
343
347
|
const retained = args.branchEntries
|
|
@@ -349,7 +353,13 @@ export function removeNativeCompactionRetainedMessages(args: {
|
|
|
349
353
|
// removed when present, while a missing or rewritten copy is left alone.
|
|
350
354
|
// Required user/assistant/tool messages keep the ordered, fail-closed
|
|
351
355
|
// matching contract.
|
|
352
|
-
|
|
356
|
+
// Pi 0.86 promotes transcript system messages to the leading prompt when it
|
|
357
|
+
// rebuilds context after compaction. They can therefore appear before the
|
|
358
|
+
// compaction summary even when their session entry lies inside the retained
|
|
359
|
+
// span; keep them in place instead of treating that relocation as a mismatch.
|
|
360
|
+
const requiredRetained = retained.filter(
|
|
361
|
+
(message) => message.role !== "custom" && (message.role as string) !== "system",
|
|
362
|
+
);
|
|
353
363
|
const optionalRetained = retained.filter((message) => message.role === "custom");
|
|
354
364
|
const removed = new Set<number>();
|
|
355
365
|
let cursor = summaryIndex + 1;
|
|
@@ -388,7 +398,9 @@ export function serializeLiveTailToResponsesInput<TApi extends Api>(args: {
|
|
|
388
398
|
model: Model<TApi>;
|
|
389
399
|
entries: readonly SessionEntry[];
|
|
390
400
|
}): ResponsesInputItem[] {
|
|
391
|
-
return serializeMessagesToResponsesInput(args.model, collectReplayMessages(args.entries)
|
|
401
|
+
return serializeMessagesToResponsesInput(args.model, collectReplayMessages(args.entries), {
|
|
402
|
+
firstSystemMessageIsUpdate: true,
|
|
403
|
+
});
|
|
392
404
|
}
|
|
393
405
|
|
|
394
406
|
function buildNativeReplaySegmentsInternal<TApi extends Api>(args: {
|
package/src/serializer.ts
CHANGED
|
@@ -89,6 +89,21 @@ export type ResponsesFunctionCallOutputItem = {
|
|
|
89
89
|
|
|
90
90
|
export type ResponsesReasoningItem = Record<string, unknown>;
|
|
91
91
|
|
|
92
|
+
/**
|
|
93
|
+
* Pi 0.86 adds `SystemMessage` to the public `Message` union; 0.85.1 has no system
|
|
94
|
+
* variant and does not export that type name. Use the public shape when the installed
|
|
95
|
+
* Pi declares it and a structural fallback for the fields this serializer consumes
|
|
96
|
+
* otherwise, so one source file type-checks against both supported versions.
|
|
97
|
+
*/
|
|
98
|
+
type ResponsesSystemMessage = Extract<Message, { role: "system" }> extends never
|
|
99
|
+
? {
|
|
100
|
+
role: "system";
|
|
101
|
+
content: string | TextContent[];
|
|
102
|
+
sections?: Record<string, string | null>;
|
|
103
|
+
timestamp: number;
|
|
104
|
+
}
|
|
105
|
+
: Extract<Message, { role: "system" }>;
|
|
106
|
+
|
|
92
107
|
export type ResponsesInputItem =
|
|
93
108
|
| ResponsesInputMessageItem
|
|
94
109
|
| ResponsesAssistantOutputItem
|
|
@@ -106,6 +121,8 @@ export type NativeCompactionRequestBody = CompactionRequestExtras & {
|
|
|
106
121
|
export type SerializeResponsesMessagesOptions = {
|
|
107
122
|
instructions?: string;
|
|
108
123
|
includeInstructionsInInput?: boolean;
|
|
124
|
+
/** Treat a system message at the start of a sliced post-compaction tail as an update. */
|
|
125
|
+
firstSystemMessageIsUpdate?: boolean;
|
|
109
126
|
};
|
|
110
127
|
|
|
111
128
|
export type ResponsesParityReport = {
|
|
@@ -126,6 +143,13 @@ function sanitizeSurrogates(text: string): string {
|
|
|
126
143
|
return text.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "");
|
|
127
144
|
}
|
|
128
145
|
|
|
146
|
+
function instructionRole<TApi extends Api>(model: Model<TApi>): "developer" | "system" {
|
|
147
|
+
// Pi's public Model compat union is broader than the OpenAI Responses compat
|
|
148
|
+
// shape, while the runtime field is shared by both supported Pi versions.
|
|
149
|
+
const compat = model.compat as { supportsDeveloperRole?: boolean } | undefined;
|
|
150
|
+
return model.reasoning && compat?.supportsDeveloperRole !== false ? "developer" : "system";
|
|
151
|
+
}
|
|
152
|
+
|
|
129
153
|
export function collectCompactionWindowMessages(preparation: CompactionPreparation): AgentMessage[] {
|
|
130
154
|
return [...preparation.messagesToSummarize, ...preparation.turnPrefixMessages];
|
|
131
155
|
}
|
|
@@ -159,19 +183,38 @@ export function serializeMessagesToResponsesInput<TApi extends Api>(
|
|
|
159
183
|
messages: AgentMessage[],
|
|
160
184
|
options: SerializeResponsesMessagesOptions = {},
|
|
161
185
|
): ResponsesInputItem[] {
|
|
162
|
-
|
|
163
|
-
|
|
186
|
+
return serializeLlmMessagesToResponsesInput(model, convertToLlm(messages), options);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Serialize Pi's normalized LLM message union without running `convertToLlm` again.
|
|
191
|
+
* This keeps the system-message boundary directly testable while preserving the public
|
|
192
|
+
* AgentMessage-facing entry point above.
|
|
193
|
+
*/
|
|
194
|
+
export function serializeLlmMessagesToResponsesInput<TApi extends Api>(
|
|
195
|
+
model: Model<TApi>,
|
|
196
|
+
messages: Message[],
|
|
197
|
+
options: SerializeResponsesMessagesOptions = {},
|
|
198
|
+
): ResponsesInputItem[] {
|
|
199
|
+
const transformedMessages = transformMessagesForResponses(messages);
|
|
164
200
|
const input: ResponsesInputItem[] = [];
|
|
165
201
|
|
|
166
202
|
if (options.includeInstructionsInInput && options.instructions) {
|
|
167
203
|
input.push({
|
|
168
|
-
role: model
|
|
204
|
+
role: instructionRole(model),
|
|
169
205
|
content: sanitizeSurrogates(options.instructions),
|
|
170
206
|
});
|
|
171
207
|
}
|
|
172
208
|
|
|
173
209
|
let messageIndex = 0;
|
|
210
|
+
let sourceIndex = 0;
|
|
174
211
|
for (const message of transformedMessages) {
|
|
212
|
+
// Pi 0.85's Message union has no system member, so compare through the
|
|
213
|
+
// public role string while keeping the 0.86 discriminant runtime-compatible.
|
|
214
|
+
const isFirstMessage = sourceIndex++ === 0;
|
|
215
|
+
const isLeadingSystemMessage =
|
|
216
|
+
!options.firstSystemMessageIsUpdate && isFirstMessage && (message.role as string) === "system";
|
|
217
|
+
|
|
175
218
|
if (message.role === "user") {
|
|
176
219
|
const item = serializeUserMessage(message, model);
|
|
177
220
|
if (item) {
|
|
@@ -190,8 +233,21 @@ export function serializeMessagesToResponsesInput<TApi extends Api>(
|
|
|
190
233
|
continue;
|
|
191
234
|
}
|
|
192
235
|
|
|
193
|
-
|
|
194
|
-
|
|
236
|
+
if (message.role === "toolResult") {
|
|
237
|
+
input.push(serializeToolResultMessage(message, model));
|
|
238
|
+
messageIndex++;
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// In Pi 0.85.1 this branch narrows to `never`; in Pi 0.86 it is a SystemMessage.
|
|
243
|
+
// Handle it explicitly instead of letting a new message variant become tool output.
|
|
244
|
+
const systemItem = serializeSystemMessage(message, model, isLeadingSystemMessage);
|
|
245
|
+
if (systemItem) {
|
|
246
|
+
input.push(systemItem);
|
|
247
|
+
}
|
|
248
|
+
if (!isLeadingSystemMessage) {
|
|
249
|
+
messageIndex++;
|
|
250
|
+
}
|
|
195
251
|
}
|
|
196
252
|
|
|
197
253
|
return input;
|
|
@@ -446,6 +502,72 @@ function normalizeUserContent(content: UserMessage["content"]): Array<TextConten
|
|
|
446
502
|
return typeof content === "string" ? [{ type: "text", text: content }] : content;
|
|
447
503
|
}
|
|
448
504
|
|
|
505
|
+
/**
|
|
506
|
+
* Serialize one normalized Pi system message as a Responses input message.
|
|
507
|
+
*
|
|
508
|
+
* The role follows the same reasoning-model convention as `instructions`: reasoning
|
|
509
|
+
* models use `developer`, everything else keeps `system`. Leading messages include
|
|
510
|
+
* their complete section state; later messages use Pi's update framing so section
|
|
511
|
+
* additions/removals are not mistaken for a new complete prompt. An empty system
|
|
512
|
+
* message produces no item, matching Pi.
|
|
513
|
+
*/
|
|
514
|
+
function serializeSystemMessage<TApi extends Api>(
|
|
515
|
+
message: ResponsesSystemMessage,
|
|
516
|
+
model: Model<TApi>,
|
|
517
|
+
isLeadingSystemMessage: boolean,
|
|
518
|
+
): ResponsesInputMessageItem | undefined {
|
|
519
|
+
const text = sanitizeSurrogates(
|
|
520
|
+
isLeadingSystemMessage ? getSystemMessageText(message) : renderSystemMessageUpdate(message),
|
|
521
|
+
);
|
|
522
|
+
if (text.length === 0) {
|
|
523
|
+
return undefined;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
return {
|
|
527
|
+
role: instructionRole(model),
|
|
528
|
+
content: text,
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/** Mirrors Pi's public getSystemMessageText() without importing a 0.86-only helper. */
|
|
533
|
+
function getSystemMessageText(message: ResponsesSystemMessage): string {
|
|
534
|
+
const parts = [contentText(message.content)];
|
|
535
|
+
for (const text of Object.values(message.sections ?? {})) {
|
|
536
|
+
if (text !== null) {
|
|
537
|
+
parts.push(text);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
return parts.filter((part) => part.length > 0).join("\n\n");
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/** Mirrors Pi's public renderSystemMessageUpdate() for later transcript updates. */
|
|
544
|
+
function renderSystemMessageUpdate(message: ResponsesSystemMessage): string {
|
|
545
|
+
const parts: string[] = [];
|
|
546
|
+
const text = contentText(message.content);
|
|
547
|
+
if (text.length > 0) {
|
|
548
|
+
parts.push(text);
|
|
549
|
+
}
|
|
550
|
+
for (const [name, value] of Object.entries(message.sections ?? {})) {
|
|
551
|
+
parts.push(
|
|
552
|
+
value === null
|
|
553
|
+
? `Removed system prompt section "${name}".`
|
|
554
|
+
: `Updated system prompt section "${name}":\n\n${value}`,
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
return parts.join("\n\n");
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
function contentText(content: ResponsesSystemMessage["content"]): string {
|
|
561
|
+
if (typeof content === "string") {
|
|
562
|
+
return content;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
return content
|
|
566
|
+
.filter((item): item is TextContent => item.type === "text")
|
|
567
|
+
.map((item) => item.text)
|
|
568
|
+
.join("\n");
|
|
569
|
+
}
|
|
570
|
+
|
|
449
571
|
function parseReasoningItem(block: ThinkingContent): ResponsesReasoningItem | undefined {
|
|
450
572
|
if (!block.thinkingSignature) {
|
|
451
573
|
return undefined;
|
package/tsconfig.check.json
CHANGED
|
@@ -12,10 +12,6 @@
|
|
|
12
12
|
"typeRoots": ["./node_modules/@types"]
|
|
13
13
|
},
|
|
14
14
|
"include": [
|
|
15
|
-
"extensions/compaction.ts",
|
|
16
|
-
"extensions/web-search.ts",
|
|
17
|
-
"extensions/image-generation.ts",
|
|
18
|
-
"extensions/auto-mode.ts",
|
|
19
15
|
"src/compact-client.ts",
|
|
20
16
|
"src/compaction-projection.ts",
|
|
21
17
|
"src/pi-context-hook.ts",
|
package/extensions/auto-mode.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from "../src/auto-mode/extension.ts";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from "../src/codex-astra/extension.ts";
|
package/extensions/compaction.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from "../src/extension-runtime.ts";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from "../src/image-generation/extension.ts";
|
package/extensions/web-search.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from "../src/web-search/extension.ts";
|