@jskit-ai/assistant-core 0.1.199 → 0.1.201
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/examples/conversation/README.md +8 -1
- package/examples/conversation/package.json +5 -5
- package/fixtures/responsive-assistant/App.vue +4 -1
- package/package.json +99 -36
- package/src/client/conversation/AssistantConversationElement.vue +27 -4
- package/src/client/conversation/AssistantConversationStatus.vue +24 -0
- package/src/client/conversation/AssistantGoalControl.vue +8 -5
- package/src/client/conversation/AssistantTranscript.vue +10 -3
- package/src/client/conversation/index.js +2 -0
- package/src/client/conversation/messageDelivery.js +115 -32
- package/src/client/conversation/retainedConversation.js +32 -0
- package/src/client/conversation/useAssistantQuestions.js +76 -0
- package/src/client/lib/assistantApi.js +72 -2
- package/src/server/conversation/agentRun.js +222 -0
- package/src/server/conversation/attachments.js +93 -0
- package/src/server/conversation/claudeClient.js +221 -0
- package/src/server/conversation/claudeHistory.js +256 -0
- package/src/server/conversation/claudeProcess.js +327 -0
- package/src/server/conversation/claudeTools.js +42 -0
- package/src/server/conversation/claudeTurn.js +1082 -0
- package/src/server/conversation/codexConfiguration.js +1183 -0
- package/src/server/conversation/codexConversations.js +1082 -0
- package/src/server/conversation/codexDetachedTurn.js +332 -0
- package/src/server/conversation/codexEvents.js +194 -1
- package/src/server/conversation/codexGoalCommands.js +124 -0
- package/src/server/conversation/codexHelperExecution.js +306 -0
- package/src/server/conversation/codexHelperThreadLedger.js +804 -0
- package/src/server/conversation/codexHelperThreadRecovery.js +395 -0
- package/src/server/conversation/codexHelperThreads.js +638 -0
- package/src/server/conversation/codexHistoryAdapter.js +369 -0
- package/src/server/conversation/codexMessageCommands.js +663 -0
- package/src/server/conversation/codexMessageDelivery.js +211 -0
- package/src/server/conversation/codexNativeHistoryExport.js +95 -0
- package/src/server/conversation/codexNotificationObserver.js +482 -0
- package/src/server/conversation/codexNotificationQueue.js +103 -0
- package/src/server/conversation/codexObservationControl.js +174 -0
- package/src/server/conversation/codexOutput.js +931 -0
- package/src/server/conversation/codexProcess.js +1559 -0
- package/src/server/conversation/codexProcessLeader.js +14 -0
- package/src/server/conversation/codexProvider.js +2710 -0
- package/src/server/conversation/codexProviderOwner.js +1058 -0
- package/src/server/conversation/codexProviderSelection.js +218 -0
- package/src/server/conversation/codexReasoning.js +314 -0
- package/src/server/conversation/codexRenewalCommands.js +290 -0
- package/src/server/conversation/codexResultSettlement.js +409 -0
- package/src/server/conversation/codexRunJournal.js +658 -0
- package/src/server/conversation/codexRuntimeLifecycle.js +288 -0
- package/src/server/conversation/codexSessionCleanup.js +96 -0
- package/src/server/conversation/codexSessionReconciliation.js +284 -0
- package/src/server/conversation/codexThreadReadiness.js +397 -0
- package/src/server/conversation/codexThreadStatus.js +354 -0
- package/src/server/conversation/codexTurn.js +701 -255
- package/src/server/conversation/codexTurnControl.js +317 -0
- package/src/server/conversation/codexTurnRecovery.js +990 -0
- package/src/server/conversation/codexTurnState.js +549 -0
- package/src/server/conversation/commandHook.js +18 -0
- package/src/server/conversation/commandWrapper.js +37 -0
- package/src/server/conversation/configuration.js +43 -0
- package/src/server/conversation/continuity.js +289 -0
- package/src/server/conversation/fileStorage.js +55 -0
- package/src/server/conversation/hookBridge.js +19 -0
- package/src/server/conversation/index.js +13 -0
- package/src/server/conversation/localExecution.js +665 -0
- package/src/server/conversation/memoryStorage.js +7 -72
- package/src/server/conversation/nativeHistoryExport.js +90 -0
- package/src/server/conversation/nativeStorage.js +23 -0
- package/src/server/conversation/openCodeClient.js +209 -19
- package/src/server/conversation/openCodePlugin.js +84 -0
- package/src/server/conversation/openCodeProcess.js +476 -0
- package/src/server/conversation/openCodeRuntime.js +1578 -0
- package/src/server/conversation/openCodeTools.js +60 -0
- package/src/server/conversation/openCodeTurn.js +557 -0
- package/src/server/conversation/provider.js +30 -0
- package/src/server/conversation/providers/api.js +136 -0
- package/src/server/conversation/providers/claude.js +63 -0
- package/src/server/conversation/providers/claudeDriver.js +425 -0
- package/src/server/conversation/providers/codex.js +248 -0
- package/src/server/conversation/providers/codexDriver.js +1078 -0
- package/src/server/conversation/providers/opencode.js +49 -0
- package/src/server/conversation/providers/opencodeDriver.js +650 -0
- package/src/server/conversation/providers/scoped.js +312 -0
- package/src/server/conversation/runtime.js +2220 -0
- package/src/server/conversation/runtimeStateUpgrade.js +125 -0
- package/src/server/conversation/storage.js +184 -0
- package/src/server/conversation/streams.js +7 -2
- package/src/server/conversation/structuredOutput.js +240 -0
- package/src/server/conversation/systemPrompt.js +54 -0
- package/src/server/conversation/tools.js +84 -0
- package/src/server/conversation/transcript.js +5 -4
- package/src/server/hosts/claudeHistory.js +5 -0
- package/src/server/hosts/claudeProcess.js +11 -0
- package/src/server/hosts/claudeTurn.js +4 -0
- package/src/server/hosts/codexConfiguration.js +20 -0
- package/src/server/hosts/codexEvents.js +13 -0
- package/src/server/hosts/codexProcess.js +12 -0
- package/src/server/hosts/codexProvider.js +23 -0
- package/src/server/hosts/codexTurn.js +20 -0
- package/src/server/hosts/nativeHistory.js +3 -0
- package/src/server/hosts/openCodeClient.js +3 -0
- package/src/server/hosts/openCodeProcess.js +23 -0
- package/src/server/hosts/openCodeTurn.js +7 -0
- package/src/server/index.js +2 -0
- package/src/server/lib/aiConnectionClient.js +13 -6
- package/src/server/lib/assistantCompletion.js +190 -0
- package/src/server/lib/assistantToolLoop.js +314 -0
- package/src/server/lib/serviceToolCatalog.js +25 -10
- package/src/shared/assistantResponseText.js +29 -0
- package/src/shared/conversation/attachments.js +19 -0
- package/src/shared/conversation/index.js +3 -0
- package/src/shared/conversation/pagination.js +78 -0
- package/src/shared/conversation/patches.js +150 -0
- package/src/shared/conversation/questions.js +48 -0
- package/src/shared/conversation/streaming.js +7 -4
- package/src/shared/index.js +2 -0
- package/src/shared/nativeProviders.js +45 -0
- package/src/testing/conversationStorageContract.js +31 -1
- package/src/testing/nativeCodex.js +11 -0
- package/src/testing/nativeOpenCode.js +3 -0
- package/test/aiConnectionClient.test.js +2 -0
- package/test/apiConversationDriver.test.js +73 -0
- package/test/assistantApiSurfaceHeader.test.js +85 -0
- package/test/assistantBoundedToolLoop.test.js +285 -0
- package/test/assistantScroll.browser.test.js +16 -0
- package/test/claudeClient.test.js +189 -0
- package/test/claudeConversationSession.test.js +1173 -0
- package/test/claudeInstructionResume.test.js +82 -0
- package/test/claudeProcess.test.js +204 -0
- package/test/claudeTurn.test.js +253 -0
- package/test/codexConversationSession.test.js +2122 -0
- package/test/codexGoal.test.js +37 -0
- package/test/codexHelperDeletion.test.js +163 -0
- package/test/codexHelperThreadLedger.test.js +494 -0
- package/test/codexHistoryAdapter.test.js +541 -0
- package/test/codexInteractiveArguments.test.js +30 -0
- package/test/codexIsolation.test.js +756 -0
- package/test/codexModelCatalog.test.js +362 -0
- package/test/codexNativeHistoryExport.test.js +81 -0
- package/test/codexThreadSelection.test.js +136 -0
- package/test/codexTurn.test.js +2158 -0
- package/test/conversation.test.js +110 -0
- package/test/conversationAgentRun.test.js +326 -0
- package/test/conversationApiFacilities.test.js +90 -0
- package/test/conversationAttachments.test.js +238 -0
- package/test/conversationCommandWrapper.test.js +48 -0
- package/test/conversationConfiguration.test.js +174 -0
- package/test/conversationNativeStorage.test.js +25 -0
- package/test/conversationReplacement.test.js +172 -0
- package/test/conversationRuntime.test.js +241 -0
- package/test/conversationRuntimeStateUpgrade.test.js +144 -0
- package/test/conversationSession.test.js +1080 -0
- package/test/conversationStreaming.test.js +68 -0
- package/test/conversationSystemPrompt.test.js +84 -0
- package/test/conversationTools.test.js +416 -0
- package/test/fileConversationStorage.test.js +73 -0
- package/test/fixtures/codexHelperThreadLedger.js +39 -0
- package/test/localConversationExecution.test.js +90 -0
- package/test/messageDelivery.test.js +234 -1
- package/test/nativeConversationRetirement.test.js +215 -0
- package/test/openCodeClient.test.js +515 -0
- package/test/openCodeProcess.test.js +177 -0
- package/test/openCodeRuntime.test.js +322 -0
- package/test/openCodeTools.test.js +287 -0
- package/test/opencodeConversationSession.test.js +789 -0
- package/test/serviceToolCatalog.test.js +24 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { normalizeCodexRunText } from "./codexTurnState.js";
|
|
2
|
+
import { codexAppServerProjectHookTrustConfig } from "./codexConfiguration.js";
|
|
3
|
+
import { createCodexAppServerModelCatalogCache } from "./codexProvider.js";
|
|
4
|
+
import { readCodexAppServerAccountIdentity } from "./codexHelperExecution.js";
|
|
5
|
+
|
|
6
|
+
// Private composition of unchanged operations from the original run owner.
|
|
7
|
+
export function createCodexProviderSelection({
|
|
8
|
+
providerSessions,
|
|
9
|
+
turnState,
|
|
10
|
+
helperLifecycle,
|
|
11
|
+
helperOwnershipError,
|
|
12
|
+
modelCatalogCacheMs,
|
|
13
|
+
errorPrefix
|
|
14
|
+
}) {
|
|
15
|
+
const readCodexAppServerModelCatalog = createCodexAppServerModelCatalogCache({
|
|
16
|
+
cacheMs: modelCatalogCacheMs,
|
|
17
|
+
errorPrefix
|
|
18
|
+
});
|
|
19
|
+
function managedProviderKeyForThread(sessionId, workdir, threadId) {
|
|
20
|
+
for (const [providerKey, managed] of providerSessions.managed) {
|
|
21
|
+
const provider = providerSessions.owner.providers.get(providerKey);
|
|
22
|
+
if (
|
|
23
|
+
managed.sessionId !== sessionId ||
|
|
24
|
+
managed.workdir !== workdir ||
|
|
25
|
+
managed.threadId !== threadId ||
|
|
26
|
+
provider?.isAvailable?.() !== true
|
|
27
|
+
) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
return providerKey;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function ensureCodexAppServerProviderForManagedThread(session = {}, options = {}) {
|
|
36
|
+
const normalizedSessionId = normalizeCodexRunText(session.sessionId || session.id);
|
|
37
|
+
const turn = turnState(session);
|
|
38
|
+
if (!normalizedSessionId || !turn.threadId) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const normalizedExecutionRoot = normalizeCodexRunText(options.executionRoot);
|
|
42
|
+
const normalizedWorkdir = normalizeCodexRunText(options.workdir);
|
|
43
|
+
for (const [providerKey, managed] of providerSessions.managed.entries()) {
|
|
44
|
+
const fields = providerSessions.keyFields(providerKey);
|
|
45
|
+
if (
|
|
46
|
+
fields.sessionId !== normalizedSessionId ||
|
|
47
|
+
(normalizedExecutionRoot && fields.executionRoot !== normalizedExecutionRoot) ||
|
|
48
|
+
(normalizedWorkdir && fields.workdir !== normalizedWorkdir) ||
|
|
49
|
+
normalizeCodexRunText(managed?.sessionId) !== normalizedSessionId ||
|
|
50
|
+
normalizeCodexRunText(managed?.threadId) !== turn.threadId
|
|
51
|
+
) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const provider = providerSessions.owner.providers.get(providerKey);
|
|
55
|
+
if (provider) {
|
|
56
|
+
return {
|
|
57
|
+
provider: await providerSessions.owner.ensureSession(
|
|
58
|
+
providerSessions.context(normalizedSessionId, managed.providerOptions)
|
|
59
|
+
),
|
|
60
|
+
providerKey,
|
|
61
|
+
providerOptions: managed.providerOptions
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function ensureCodexAppServerProviderForActiveTurn(session = {}, options = {}) {
|
|
69
|
+
if (!turnState(session).active) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
return ensureCodexAppServerProviderForManagedThread(session, options);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function acquireCodexAppServerOutputProvider(sessionId, session, managedIdentity, prepareProviderOptions) {
|
|
76
|
+
const activeProvider = await ensureCodexAppServerProviderForActiveTurn(session, managedIdentity);
|
|
77
|
+
const provider = activeProvider?.provider;
|
|
78
|
+
if (provider) return provider;
|
|
79
|
+
const providerOptions = await prepareProviderOptions();
|
|
80
|
+
return await providerSessions.owner.ensureSession(
|
|
81
|
+
providerSessions.context(normalizeCodexRunText(sessionId), providerOptions)
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function codexAppServerRuntimeForVisibleTerminal(sessionId, session, options, managedIdentity, prepareProviderOptions) {
|
|
86
|
+
const activeProvider = await ensureCodexAppServerProviderForActiveTurn(session, managedIdentity);
|
|
87
|
+
const providerOptions = activeProvider?.providerOptions || await prepareProviderOptions();
|
|
88
|
+
const provider = activeProvider?.provider || await providerSessions.owner.ensureSession(
|
|
89
|
+
providerSessions.context(normalizeCodexRunText(sessionId), providerOptions)
|
|
90
|
+
);
|
|
91
|
+
await codexAppServerProjectHookTrustConfig(provider, options.workdir, {
|
|
92
|
+
persist: true
|
|
93
|
+
});
|
|
94
|
+
const providerKey = activeProvider?.providerKey || providerSessions.context(sessionId, providerOptions).providerKey;
|
|
95
|
+
return providerSessions.owner.acquireRuntime({
|
|
96
|
+
operation: () => provider.ensureRuntime(),
|
|
97
|
+
provider,
|
|
98
|
+
providerKey,
|
|
99
|
+
providerOptions
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function codexAppServerMessageContext(sessionId, context) {
|
|
104
|
+
const { runtime, session } = context;
|
|
105
|
+
return {
|
|
106
|
+
runtime,
|
|
107
|
+
session,
|
|
108
|
+
threadId: context.threadId,
|
|
109
|
+
async acquireProvider(currentSession) {
|
|
110
|
+
const activeProvider = await ensureCodexAppServerProviderForManagedThread(
|
|
111
|
+
currentSession, context.managedIdentity(currentSession)
|
|
112
|
+
);
|
|
113
|
+
if (activeProvider) return { provider: activeProvider.provider || null, reused: true };
|
|
114
|
+
return {
|
|
115
|
+
provider: await providerSessions.owner.ensureSession(providerSessions.context(
|
|
116
|
+
normalizeCodexRunText(sessionId), await context.providerOptions(currentSession)
|
|
117
|
+
)),
|
|
118
|
+
reused: false
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function codexAppServerControlContext(sessionId, context) {
|
|
125
|
+
return {
|
|
126
|
+
runtime: context.runtime,
|
|
127
|
+
admissionError: context.admissionError,
|
|
128
|
+
threadId: context.threadId,
|
|
129
|
+
async acquireProvider(currentSession) {
|
|
130
|
+
const activeProvider = await ensureCodexAppServerProviderForActiveTurn(
|
|
131
|
+
currentSession, context.managedIdentity(currentSession)
|
|
132
|
+
);
|
|
133
|
+
return activeProvider?.provider || await providerSessions.owner.ensureSession(
|
|
134
|
+
providerSessions.context(normalizeCodexRunText(sessionId), await context.providerOptions(currentSession))
|
|
135
|
+
);
|
|
136
|
+
},
|
|
137
|
+
observationOwner(threadId) {
|
|
138
|
+
for (const [key, managed] of providerSessions.managed) {
|
|
139
|
+
const owner = providerSessions.owner.providers.get(key);
|
|
140
|
+
if (managed.sessionId === sessionId && managed.threadId === threadId && owner?.observationFailure) {
|
|
141
|
+
return owner;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function codexAppServerObservationRecoveryContext(sessionId, context) {
|
|
150
|
+
return {
|
|
151
|
+
threadId: context.threadId,
|
|
152
|
+
async acquireProvider(current) {
|
|
153
|
+
return providerSessions.owner.ensureSession(providerSessions.context(
|
|
154
|
+
normalizeCodexRunText(sessionId), await context.providerOptions(current)
|
|
155
|
+
));
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function codexAppServerSessionObserverOptions(sessionId, providerOptions) {
|
|
161
|
+
return {
|
|
162
|
+
get providerKey() {
|
|
163
|
+
return providerSessions.context(normalizeCodexRunText(sessionId), providerOptions).providerKey;
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function rememberCodexAppServerPreparedThread(sessionId, providerOptions, context, thread) {
|
|
169
|
+
const connection = providerSessions.context(sessionId, providerOptions);
|
|
170
|
+
connection.runOwner.runtimeLifecycle.rememberManagedSession(connection.providerKey, {
|
|
171
|
+
providerOptions,
|
|
172
|
+
sessionId,
|
|
173
|
+
executionRoot: context.executionRoot,
|
|
174
|
+
threadId: thread.threadId,
|
|
175
|
+
workdir: context.workdir
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
async function readExecutionProfileModelCatalog(sessionId, preparation) {
|
|
181
|
+
if (!preparation.assistantScope) {
|
|
182
|
+
const restoration = await preparation.helperRestoration;
|
|
183
|
+
helperLifecycle.assertRestored(await helperLifecycle.restoreThreads(
|
|
184
|
+
restoration.projectRuntimeRoot, restoration.context
|
|
185
|
+
));
|
|
186
|
+
}
|
|
187
|
+
const provider = preparation.provider || await providerSessions.owner.ensureSession(
|
|
188
|
+
providerSessions.context(normalizeCodexRunText(sessionId), await preparation.providerOptions)
|
|
189
|
+
);
|
|
190
|
+
return readCodexAppServerModelCatalog(provider, { signal: preparation.signal });
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function readProviderAccountIdentity(sessionId, preparation) {
|
|
194
|
+
const restoration = await preparation.helperRestoration;
|
|
195
|
+
helperLifecycle.assertRestored(await helperLifecycle.restoreThreads(
|
|
196
|
+
restoration.projectRuntimeRoot, restoration.context
|
|
197
|
+
));
|
|
198
|
+
const providerOptions = await preparation.providerOptions;
|
|
199
|
+
const provider = await providerSessions.owner.withLifecycle(() =>
|
|
200
|
+
providerSessions.owner.providerForSession(providerSessions.context(sessionId, providerOptions))
|
|
201
|
+
);
|
|
202
|
+
return readCodexAppServerAccountIdentity(provider, helperOwnershipError);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
readExecutionProfileModelCatalog,
|
|
207
|
+
readProviderAccountIdentity,
|
|
208
|
+
managedProviderKeyForThread,
|
|
209
|
+
ensureCodexAppServerProviderForActiveTurn,
|
|
210
|
+
acquireCodexAppServerOutputProvider,
|
|
211
|
+
codexAppServerRuntimeForVisibleTerminal,
|
|
212
|
+
codexAppServerMessageContext,
|
|
213
|
+
codexAppServerControlContext,
|
|
214
|
+
codexAppServerObservationRecoveryContext,
|
|
215
|
+
codexAppServerSessionObserverOptions,
|
|
216
|
+
rememberCodexAppServerPreparedThread
|
|
217
|
+
};
|
|
218
|
+
}
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import { normalizeCodexRunText, codexAppServerTurnCanReceiveProviderActivity, codexAppServerTurnKey } from "./codexTurnState.js";
|
|
2
|
+
import { codexAppServerContentText, codexAppServerNotificationItem, codexAppServerOutputOwnerTurnId, codexAppServerNotificationParams, codexAppServerNotificationTurnId } from "./codexEvents.js";
|
|
3
|
+
|
|
4
|
+
// Original per-turn reasoning channels, persistence queue and cleanup.
|
|
5
|
+
// The containing output instance shares these exact Maps with its run owner.
|
|
6
|
+
export function createCodexAppServerReasoning({
|
|
7
|
+
createStore,
|
|
8
|
+
publish,
|
|
9
|
+
turnStateFromAgentRun,
|
|
10
|
+
debugLog,
|
|
11
|
+
readCodexAppServerAgentRunForSession
|
|
12
|
+
}) {
|
|
13
|
+
const codexAppServerReasoningTurns = new Map();
|
|
14
|
+
const codexAppServerReasoningPersistQueues = new Map();
|
|
15
|
+
|
|
16
|
+
function codexAppServerReasoningTurnKey(threadId = "", turnId = "") {
|
|
17
|
+
return codexAppServerTurnKey(threadId, turnId || "*");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function codexAppServerReasoningTurnState(threadId = "", turnId = "") {
|
|
21
|
+
const key = codexAppServerReasoningTurnKey(threadId, turnId);
|
|
22
|
+
const existing = codexAppServerReasoningTurns.get(key);
|
|
23
|
+
if (existing) {
|
|
24
|
+
return existing;
|
|
25
|
+
}
|
|
26
|
+
const created = {
|
|
27
|
+
createdAt: new Date().toISOString(),
|
|
28
|
+
segments: [],
|
|
29
|
+
channels: new Map(),
|
|
30
|
+
summaries: new Map()
|
|
31
|
+
};
|
|
32
|
+
codexAppServerReasoningTurns.set(key, created);
|
|
33
|
+
return created;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function codexAppServerReasoningExistingTurnState(threadId = "", turnId = "") {
|
|
37
|
+
return codexAppServerReasoningTurns.get(codexAppServerReasoningTurnKey(threadId, turnId)) ||
|
|
38
|
+
codexAppServerReasoningTurns.get(codexAppServerReasoningTurnKey(threadId, "*"));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function codexAppServerReasoningSummaryKey(notification = {}) {
|
|
42
|
+
const params = codexAppServerNotificationParams(notification);
|
|
43
|
+
const item = codexAppServerNotificationItem(notification);
|
|
44
|
+
const itemId = normalizeCodexRunText(params.itemId || item?.id || "summary");
|
|
45
|
+
const summaryIndex = String(params.summaryIndex ?? params.contentIndex ?? params.index ?? 0).trim() || "0";
|
|
46
|
+
return `${itemId}:${summaryIndex}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function createCodexAppServerReasoningSegment(state = {}, summary = null, summaryKey = "") {
|
|
50
|
+
const segment = {
|
|
51
|
+
chunks: [],
|
|
52
|
+
persistedAt: "",
|
|
53
|
+
persistedText: "",
|
|
54
|
+
summaryKey
|
|
55
|
+
};
|
|
56
|
+
if (Array.isArray(state.segments)) {
|
|
57
|
+
state.segments.push(segment);
|
|
58
|
+
}
|
|
59
|
+
if (summary) {
|
|
60
|
+
summary.currentSegment = segment;
|
|
61
|
+
}
|
|
62
|
+
return segment;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function codexAppServerReasoningSummaryDisplayText(value = "") {
|
|
66
|
+
let text = normalizeCodexRunText(value).replace(/\r\n/gu, "\n");
|
|
67
|
+
if (!text) {
|
|
68
|
+
return "";
|
|
69
|
+
}
|
|
70
|
+
text = text.replace(/\*\*([^*\n][\s\S]*?)\*\*/gu, "$1");
|
|
71
|
+
text = text.replace(/^\*\*\s*/u, "").replace(/\s*\*\*$/u, "");
|
|
72
|
+
return text
|
|
73
|
+
.split("\n")
|
|
74
|
+
.map((line) => line.trim())
|
|
75
|
+
.filter(Boolean)
|
|
76
|
+
.join(" ")
|
|
77
|
+
.replace(/\s+/gu, " ")
|
|
78
|
+
.trim();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function codexAppServerReasoningDisplayText(value = "") {
|
|
82
|
+
return normalizeCodexRunText(value)
|
|
83
|
+
.replace(/\r\n/gu, "\n")
|
|
84
|
+
.split(/\n{2,}/u)
|
|
85
|
+
.map(codexAppServerReasoningSummaryDisplayText)
|
|
86
|
+
.filter(Boolean)
|
|
87
|
+
.join("\n\n")
|
|
88
|
+
.trim();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function recordCodexAppServerReasoningNotification(threadId = "", notification = {}, {
|
|
92
|
+
turnId = ""
|
|
93
|
+
} = {}) {
|
|
94
|
+
const method = normalizeCodexRunText(notification.method);
|
|
95
|
+
const item = codexAppServerNotificationItem(notification);
|
|
96
|
+
const completed = method === "item/completed" && item?.type === "reasoning";
|
|
97
|
+
if (!completed && !["item/reasoning/summaryPartAdded", "item/reasoning/summaryTextDelta", "item/reasoning/textDelta"].includes(method)) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
const normalizedThreadId = normalizeCodexRunText(threadId);
|
|
101
|
+
const normalizedTurnId = normalizeCodexRunText(turnId) || codexAppServerNotificationTurnId(notification);
|
|
102
|
+
if (!normalizedThreadId) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
const state = codexAppServerReasoningTurnState(normalizedThreadId, normalizedTurnId);
|
|
106
|
+
const params = codexAppServerNotificationParams(notification);
|
|
107
|
+
const itemId = normalizeCodexRunText(params.itemId || item?.id || "summary");
|
|
108
|
+
if (completed) {
|
|
109
|
+
// Completed-only providers expose the same readable parts in the item.
|
|
110
|
+
// Streamed items have already been recorded; do not replay their text.
|
|
111
|
+
if (state.channels.has(itemId)) return false;
|
|
112
|
+
const summary = codexAppServerContentText(item.summary);
|
|
113
|
+
const text = normalizeCodexRunText(summary) ? summary : codexAppServerContentText(item.content);
|
|
114
|
+
return recordCodexAppServerReasoningNotification(normalizedThreadId, {
|
|
115
|
+
method: normalizeCodexRunText(summary) ? "item/reasoning/summaryTextDelta" : "item/reasoning/textDelta",
|
|
116
|
+
params: { itemId, delta: text }
|
|
117
|
+
}, { turnId: normalizedTurnId });
|
|
118
|
+
}
|
|
119
|
+
const channel = method === "item/reasoning/textDelta" ? "content" : "summary";
|
|
120
|
+
// Some providers expose both channels. Keep the first readable stream once.
|
|
121
|
+
if (state.channels.has(itemId) && state.channels.get(itemId) !== channel) return false;
|
|
122
|
+
const summaryKey = codexAppServerReasoningSummaryKey(notification);
|
|
123
|
+
const summary = state.summaries.get(summaryKey) || {
|
|
124
|
+
currentSegment: null
|
|
125
|
+
};
|
|
126
|
+
if (!summary.currentSegment) {
|
|
127
|
+
createCodexAppServerReasoningSegment(state, summary, summaryKey);
|
|
128
|
+
}
|
|
129
|
+
if (method !== "item/reasoning/summaryPartAdded") {
|
|
130
|
+
let delta = codexAppServerContentText(params.delta || params.text);
|
|
131
|
+
if (delta) {
|
|
132
|
+
state.channels.set(itemId, channel);
|
|
133
|
+
const startsNewSegment = /^\s*\n/u.test(delta) &&
|
|
134
|
+
summary.currentSegment?.chunks?.length &&
|
|
135
|
+
summary.currentSegment?.persistedText;
|
|
136
|
+
if (startsNewSegment) {
|
|
137
|
+
delta = delta.replace(/^\s*\n+/u, "");
|
|
138
|
+
createCodexAppServerReasoningSegment(state, summary, summaryKey);
|
|
139
|
+
}
|
|
140
|
+
summary.currentSegment.chunks.push(delta);
|
|
141
|
+
state.summaries.set(summaryKey, summary);
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
state.summaries.set(summaryKey, summary);
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async function recordCodexAppServerReasoningForSession(sessionId = "", threadId = "", notifications = []) {
|
|
150
|
+
const normalizedSessionId = normalizeCodexRunText(sessionId);
|
|
151
|
+
const normalizedThreadId = normalizeCodexRunText(threadId);
|
|
152
|
+
const store = await createStore(normalizedSessionId);
|
|
153
|
+
const run = await readCodexAppServerAgentRunForSession(store, normalizedSessionId);
|
|
154
|
+
const currentTurn = turnStateFromAgentRun(run || {});
|
|
155
|
+
const ownerTurnId = codexAppServerOutputOwnerTurnId({
|
|
156
|
+
notificationThreadId: normalizedThreadId,
|
|
157
|
+
notificationTurnId: codexAppServerNotificationTurnId(notifications[0]),
|
|
158
|
+
trackedActive: currentTurn.active,
|
|
159
|
+
trackedState: currentTurn.state,
|
|
160
|
+
trackedThreadId: currentTurn.threadId,
|
|
161
|
+
trackedTurnId: currentTurn.turnId
|
|
162
|
+
});
|
|
163
|
+
let changed = false;
|
|
164
|
+
for (const notification of notifications) {
|
|
165
|
+
if (recordCodexAppServerReasoningNotification(normalizedThreadId, notification, { turnId: ownerTurnId })) {
|
|
166
|
+
changed = true;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (!changed) return false;
|
|
170
|
+
await queueCodexAppServerReasoningPersist(
|
|
171
|
+
normalizedSessionId,
|
|
172
|
+
normalizedThreadId,
|
|
173
|
+
ownerTurnId
|
|
174
|
+
);
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function readCodexAppServerReasoningText(threadId = "", turnId = "") {
|
|
179
|
+
const state = codexAppServerReasoningExistingTurnState(threadId, turnId);
|
|
180
|
+
if (!state) {
|
|
181
|
+
return "";
|
|
182
|
+
}
|
|
183
|
+
return (Array.isArray(state.segments) ? state.segments : [])
|
|
184
|
+
.map((segment) => codexAppServerReasoningDisplayText(segment.chunks.join("")))
|
|
185
|
+
.filter(Boolean)
|
|
186
|
+
.join("\n\n")
|
|
187
|
+
.trim();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function codexAppServerReasoningPersistKey(sessionId = "", threadId = "", turnId = "") {
|
|
191
|
+
return [
|
|
192
|
+
normalizeCodexRunText(sessionId),
|
|
193
|
+
normalizeCodexRunText(threadId),
|
|
194
|
+
normalizeCodexRunText(turnId) || "*"
|
|
195
|
+
].filter(Boolean).join(":");
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async function persistCodexAppServerReasoningSummary(sessionId = "", threadId = "", turnId = "") {
|
|
199
|
+
const normalizedSessionId = normalizeCodexRunText(sessionId);
|
|
200
|
+
const state = codexAppServerReasoningExistingTurnState(threadId, turnId);
|
|
201
|
+
const segments = Array.isArray(state?.segments) ? state.segments : [];
|
|
202
|
+
const pendingSegments = segments
|
|
203
|
+
.map((segment) => ({
|
|
204
|
+
segment,
|
|
205
|
+
text: codexAppServerReasoningDisplayText(segment.chunks.join(""))
|
|
206
|
+
}))
|
|
207
|
+
.filter(({ segment, text }) => text && segment.persistedText !== text);
|
|
208
|
+
if (!normalizedSessionId || !state || !pendingSegments.length) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
const store = await createStore(normalizedSessionId);
|
|
212
|
+
const run = await readCodexAppServerAgentRunForSession(store, normalizedSessionId);
|
|
213
|
+
if (normalizeCodexRunText(run?.inputSource) === "terminal") {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const turn = turnStateFromAgentRun(run || {});
|
|
217
|
+
if (!codexAppServerTurnCanReceiveProviderActivity(turn, threadId, turnId)) {
|
|
218
|
+
debugLog("appServerReasoningSummary.ignored", {
|
|
219
|
+
currentState: turn.state,
|
|
220
|
+
currentStatus: turn.status,
|
|
221
|
+
currentThreadId: turn.threadId,
|
|
222
|
+
currentTurnId: turn.turnId,
|
|
223
|
+
sessionId: normalizedSessionId,
|
|
224
|
+
threadId: normalizeCodexRunText(threadId),
|
|
225
|
+
turnId: normalizeCodexRunText(turnId)
|
|
226
|
+
});
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
for (const {
|
|
230
|
+
segment,
|
|
231
|
+
text
|
|
232
|
+
} of pendingSegments) {
|
|
233
|
+
segment.persistedAt ||= new Date().toISOString();
|
|
234
|
+
const written = await store.writeConversationThinkingMessage(normalizedSessionId, {
|
|
235
|
+
at: segment.persistedAt,
|
|
236
|
+
nativeIdentity: { threadId, turnId },
|
|
237
|
+
requireOpenTurn: false,
|
|
238
|
+
text
|
|
239
|
+
});
|
|
240
|
+
if (!written) {
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
segment.persistedText = text;
|
|
244
|
+
await publish(normalizedSessionId, {
|
|
245
|
+
payload: {
|
|
246
|
+
conversationLogPatch: {
|
|
247
|
+
turn: written,
|
|
248
|
+
type: "upsert-turn"
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
reason: "codex-app-server-reasoning-summary"
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function queueCodexAppServerReasoningPersist(sessionId = "", threadId = "", turnId = "") {
|
|
257
|
+
const key = codexAppServerReasoningPersistKey(sessionId, threadId, turnId);
|
|
258
|
+
if (!key) {
|
|
259
|
+
return Promise.resolve();
|
|
260
|
+
}
|
|
261
|
+
return runQueuedCodexAppServerReasoningPersist(key, sessionId, threadId, turnId);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
async function flushCodexAppServerReasoningPersist(sessionId = "", threadId = "", turnId = "") {
|
|
265
|
+
const key = codexAppServerReasoningPersistKey(sessionId, threadId, turnId);
|
|
266
|
+
const queued = key ? codexAppServerReasoningPersistQueues.get(key) : null;
|
|
267
|
+
if (queued) {
|
|
268
|
+
await queued.catch(() => null);
|
|
269
|
+
}
|
|
270
|
+
await persistCodexAppServerReasoningSummary(sessionId, threadId, turnId);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function runQueuedCodexAppServerReasoningPersist(key = "", sessionId = "", threadId = "", turnId = "") {
|
|
274
|
+
const previous = codexAppServerReasoningPersistQueues.get(key) || Promise.resolve();
|
|
275
|
+
const next = previous
|
|
276
|
+
.catch(() => null)
|
|
277
|
+
.then(() => persistCodexAppServerReasoningSummary(sessionId, threadId, turnId));
|
|
278
|
+
codexAppServerReasoningPersistQueues.set(key, next);
|
|
279
|
+
next
|
|
280
|
+
.finally(() => {
|
|
281
|
+
if (codexAppServerReasoningPersistQueues.get(key) === next) {
|
|
282
|
+
codexAppServerReasoningPersistQueues.delete(key);
|
|
283
|
+
}
|
|
284
|
+
})
|
|
285
|
+
.catch(() => null);
|
|
286
|
+
return next;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function cleanupCodexAppServerReasoningTurn(threadId = "", turnId = "") {
|
|
290
|
+
codexAppServerReasoningTurns.delete(codexAppServerReasoningTurnKey(threadId, turnId));
|
|
291
|
+
codexAppServerReasoningTurns.delete(codexAppServerReasoningTurnKey(threadId, "*"));
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function splitCodexAppServerReasoningTurn(threadId = "", turnId = "") {
|
|
295
|
+
const state = codexAppServerReasoningExistingTurnState(threadId, turnId);
|
|
296
|
+
if (!state) {
|
|
297
|
+
return false;
|
|
298
|
+
}
|
|
299
|
+
for (const summary of state.summaries.values()) {
|
|
300
|
+
summary.currentSegment = null;
|
|
301
|
+
}
|
|
302
|
+
return true;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return {
|
|
306
|
+
reasoningTurns: codexAppServerReasoningTurns,
|
|
307
|
+
reasoningPersistQueues: codexAppServerReasoningPersistQueues,
|
|
308
|
+
recordReasoningForSession: recordCodexAppServerReasoningForSession,
|
|
309
|
+
readReasoningText: readCodexAppServerReasoningText,
|
|
310
|
+
flushReasoningPersist: flushCodexAppServerReasoningPersist,
|
|
311
|
+
cleanupReasoningTurn: cleanupCodexAppServerReasoningTurn,
|
|
312
|
+
splitReasoningTurn: splitCodexAppServerReasoningTurn
|
|
313
|
+
};
|
|
314
|
+
}
|