gestalt-mobile 0.35.4 → 0.36.0
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/README.md +60 -3
- package/dist/client/assets/index-9XMv2BvD.js +38 -0
- package/dist/client/assets/{index-CPQBJDsg.css → index-Bxv5Z2w4.css} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/server/client/features/chat/activity-summary.js +71 -0
- package/dist/server/client/features/chat/chat-projection.js +722 -0
- package/dist/server/client/features/chat/message-store.js +30 -0
- package/dist/server/server/app.js +1 -1
- package/dist/server/server/cli.js +8 -0
- package/dist/server/server/composition.contracts.js +191 -8
- package/dist/server/server/composition.js +266 -56
- package/dist/server/server/features/autopilot/application/policy.js +2 -2
- package/dist/server/server/features/autopilot/register-routes.js +2 -2
- package/dist/server/server/features/autopilot/toggle/endpoint.js +5 -1
- package/dist/server/server/features/catalog/get-bootstrap/use-case.js +5 -3
- package/dist/server/server/features/plans/application/parse-supervised-plan.js +33 -8
- package/dist/server/server/features/sessions/get-session/endpoint.js +1 -1
- package/dist/server/server/features/sessions/list-models/endpoint.js +16 -0
- package/dist/server/server/features/sessions/list-recent-threads/endpoint.js +8 -3
- package/dist/server/server/features/sessions/list-sessions/endpoint.js +1 -1
- package/dist/server/server/features/sessions/model/relay-session.js +2 -0
- package/dist/server/server/features/sessions/promote-recent-thread/endpoint.js +6 -2
- package/dist/server/server/features/sessions/promote-recent-thread/use-case.js +7 -2
- package/dist/server/server/features/sessions/register-routes.js +8 -3
- package/dist/server/server/features/sessions/respond-interaction/endpoint.js +1 -1
- package/dist/server/server/features/sessions/select-model/endpoint.js +1 -1
- package/dist/server/server/features/sessions/start-session/request.js +1 -0
- package/dist/server/server/features/sessions/start-session/use-case.js +21 -7
- package/dist/server/server/features/skills/list-available/endpoint.js +4 -2
- package/dist/server/server/platform/catalog/provider-model-catalog.js +15 -0
- package/dist/server/server/platform/kimi/kimi-activity-facts.js +88 -0
- package/dist/server/server/platform/kimi/kimi-errors.js +30 -0
- package/dist/server/server/platform/kimi/kimi-model-catalog.js +63 -0
- package/dist/server/server/platform/kimi/kimi-normalizer.js +196 -0
- package/dist/server/server/platform/kimi/kimi-recent-session-lister.js +68 -0
- package/dist/server/server/platform/kimi/kimi-server-request.js +143 -0
- package/dist/server/server/platform/kimi/kimi-session-runtime.js +653 -0
- package/dist/server/server/platform/kimi/kimi-share-dir.js +65 -0
- package/dist/server/server/platform/kimi/kimi-skill-catalog.js +79 -0
- package/dist/server/server/platform/kimi/kimi-skills-materializer.js +41 -0
- package/dist/server/server/platform/kimi/kimi-web-client.js +63 -0
- package/dist/server/server/platform/kimi/kimi-web-server-manager.js +215 -0
- package/dist/server/server/platform/kimi/kimi-ws-client.js +154 -0
- package/dist/server/server/platform/persistence/migrate.js +2 -0
- package/dist/server/server/platform/persistence/sqlite-session-repository.js +3 -2
- package/dist/server/server/platform/skills/cached-skill-catalog.js +0 -0
- package/dist/server/shared/contracts/llm-provider.js +6 -0
- package/package.json +4 -3
- package/dist/client/assets/index-SZM7lpix.js +0 -38
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2026 Dyne.org foundation
|
|
3
|
+
* Designed by Denis Roio <jaromil@dyne.org>
|
|
4
|
+
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
*/
|
|
6
|
+
export function appendUserMessage(messages, id, text, occurredAt) {
|
|
7
|
+
return [
|
|
8
|
+
...messages,
|
|
9
|
+
{ id, role: 'user', text, ...(occurredAt ? { occurredAt } : {}), complete: true },
|
|
10
|
+
];
|
|
11
|
+
}
|
|
12
|
+
export function applyDelta(messages, id, text, occurredAt) {
|
|
13
|
+
const previous = messages.find((message) => message.id === id);
|
|
14
|
+
return previous
|
|
15
|
+
? messages.map((message) => message.id === id ? { ...message, text: message.text + text } : message)
|
|
16
|
+
: [
|
|
17
|
+
...messages,
|
|
18
|
+
{
|
|
19
|
+
id,
|
|
20
|
+
role: 'assistant',
|
|
21
|
+
phase: 'commentary',
|
|
22
|
+
text,
|
|
23
|
+
...(occurredAt ? { occurredAt } : {}),
|
|
24
|
+
complete: false,
|
|
25
|
+
},
|
|
26
|
+
];
|
|
27
|
+
}
|
|
28
|
+
export function completeMessage(messages, id) {
|
|
29
|
+
return messages.map((message) => (message.id === id ? { ...message, complete: true } : message));
|
|
30
|
+
}
|
|
@@ -46,7 +46,7 @@ export async function buildApp(deps) {
|
|
|
46
46
|
registerGetBootstrap(app, deps.bootstrap);
|
|
47
47
|
registerSessionRoutes(app, deps);
|
|
48
48
|
if (deps.autopilot)
|
|
49
|
-
registerAutopilotRoutes(app, deps.autopilot, deps.sessionRoutes?.idempotency);
|
|
49
|
+
registerAutopilotRoutes(app, deps.autopilot, deps.sessionRoutes?.idempotency, deps.sessionRoutes ? (id) => deps.sessionRoutes?.find(id)?.provider : undefined);
|
|
50
50
|
if (deps.orgPlanAttention)
|
|
51
51
|
registerOrgPlanAttentionRoutes(app, deps.orgPlanAttention);
|
|
52
52
|
registerPlanRoutes(app, deps);
|
|
@@ -98,6 +98,12 @@ export async function probeCodexVersion() {
|
|
|
98
98
|
.then(({ stdout }) => stdout.trim() || null)
|
|
99
99
|
.catch(() => null);
|
|
100
100
|
}
|
|
101
|
+
/** Detects the kimi CLI exactly like the codex probe; absence is a soft status. */
|
|
102
|
+
export async function probeKimiVersion() {
|
|
103
|
+
return runFile('kimi', ['--version'])
|
|
104
|
+
.then(({ stdout }) => stdout.trim() || null)
|
|
105
|
+
.catch(() => null);
|
|
106
|
+
}
|
|
101
107
|
export function installShutdownHandlers(app, signalSource = process) {
|
|
102
108
|
let closing;
|
|
103
109
|
const shutdown = () => {
|
|
@@ -205,6 +211,7 @@ export async function runCli(dependencies = {}) {
|
|
|
205
211
|
if (!diagnosis.ok)
|
|
206
212
|
stderr.write('WARNING: Startup diagnostics reported a problem; Gestalt Mobile will continue so it can show recovery controls.\n');
|
|
207
213
|
const installedCodexVersion = await (dependencies.probeCodexVersion ?? probeCodexVersion)();
|
|
214
|
+
const installedKimiVersion = await (dependencies.probeKimiVersion ?? probeKimiVersion)();
|
|
208
215
|
const mobileVersion = await packageVersion(moduleUrl);
|
|
209
216
|
const app = await (dependencies.compose ?? composeRelayApp)({
|
|
210
217
|
root: config.root,
|
|
@@ -214,6 +221,7 @@ export async function runCli(dependencies = {}) {
|
|
|
214
221
|
staticDir: packagedClientDir(moduleUrl),
|
|
215
222
|
profiles: new LauncherProfileCatalog(),
|
|
216
223
|
installedCodexVersion,
|
|
224
|
+
installedKimiVersion,
|
|
217
225
|
componentVersions: componentVersions(mobileVersion, installedCodexVersion, dependencies.environment),
|
|
218
226
|
startAppServers: true,
|
|
219
227
|
homeDirectory: dependencies.homeDirectory,
|
|
@@ -21,6 +21,7 @@ import { workspaceId } from './platform/catalog/workspace-id.js';
|
|
|
21
21
|
import { planStatusDirectoryPath, planStatusFilePath, } from './platform/plans/filesystem-plan-status-source.js';
|
|
22
22
|
import { toOrgPlanAttentionToolResponse } from '../shared/contracts/org-plan-attention.js';
|
|
23
23
|
import { toOrgPlanCheckpointToolResponse } from '../shared/contracts/org-plan-checkpoint.js';
|
|
24
|
+
import { applyProjectionEvent, createChatProjection, } from '../client/features/chat/chat-projection.js';
|
|
24
25
|
function fakeAppServer(calls) {
|
|
25
26
|
return {
|
|
26
27
|
rpc: {
|
|
@@ -125,7 +126,7 @@ async function createComposedSession(app) {
|
|
|
125
126
|
const created = await app.inject({
|
|
126
127
|
method: 'POST',
|
|
127
128
|
url: '/api/sessions',
|
|
128
|
-
payload: { workspaceId: workspace.id, profile: 'default' },
|
|
129
|
+
payload: { workspaceId: workspace.id, profile: 'default', provider: 'codex' },
|
|
129
130
|
});
|
|
130
131
|
expect(created.statusCode).toBe(202);
|
|
131
132
|
return created.json().id;
|
|
@@ -152,13 +153,13 @@ const autopilotPlanText = (childState = 'TODO') => `#+TITLE: Autopilot fixture
|
|
|
152
153
|
const completedAutopilotPlanText = () => autopilotPlanText('DONE')
|
|
153
154
|
.replace('* WIP [#A] Parent', '* DONE [#A] Parent')
|
|
154
155
|
.replace(':REVIEW_STATUS: UNREVIEWED', ':REVIEW_STATUS: REVIEWED');
|
|
155
|
-
async function installAutopilotPlan(app, sessionId, workspacePath, name, childState = 'TODO') {
|
|
156
|
+
async function installAutopilotPlan(app, sessionId, workspacePath, name, childState = 'TODO', reason = 'supervision-start') {
|
|
156
157
|
const planPath = join(workspacePath, `${name}.org`);
|
|
157
158
|
await writeFile(planPath, autopilotPlanText(childState));
|
|
158
159
|
await writeFile(planStatusFilePath(planStatusDirectoryPath(workspacePath, sessionId), planPath), JSON.stringify({
|
|
159
160
|
schemaVersion: 1,
|
|
160
161
|
planPath,
|
|
161
|
-
reason
|
|
162
|
+
reason,
|
|
162
163
|
updatedAt: new Date().toISOString(),
|
|
163
164
|
}));
|
|
164
165
|
await expect
|
|
@@ -2242,6 +2243,37 @@ describe('production composition', () => {
|
|
|
2242
2243
|
});
|
|
2243
2244
|
});
|
|
2244
2245
|
describeCompositionConcern('lifecycle', () => {
|
|
2246
|
+
it('retains an authored plan without starting supervision', async () => {
|
|
2247
|
+
const root = await mkdtemp(join(tmpdir(), 'gestalt-mobile-root-'));
|
|
2248
|
+
const dataDir = await mkdtemp(join(tmpdir(), 'gestalt-mobile-state-'));
|
|
2249
|
+
ownTemporaryPaths(root, dataDir);
|
|
2250
|
+
const workspacePath = join(root, 'workspace');
|
|
2251
|
+
await mkdir(workspacePath);
|
|
2252
|
+
const handles = [];
|
|
2253
|
+
const app = await composeAuthorizedApp({
|
|
2254
|
+
root,
|
|
2255
|
+
dataDir,
|
|
2256
|
+
relyingParty,
|
|
2257
|
+
installedCodexVersion: 'codex-cli 0.144.3',
|
|
2258
|
+
startAppServers: true,
|
|
2259
|
+
launchAppServer: liveAppServer(handles),
|
|
2260
|
+
profiles: {
|
|
2261
|
+
list: async () => [],
|
|
2262
|
+
require: async () => ({
|
|
2263
|
+
name: 'default',
|
|
2264
|
+
state: 'ok',
|
|
2265
|
+
status: 'ready',
|
|
2266
|
+
}),
|
|
2267
|
+
},
|
|
2268
|
+
});
|
|
2269
|
+
const sessionId = await createComposedSession(app);
|
|
2270
|
+
await installAutopilotPlan(app, sessionId, workspacePath, 'authored-only', 'TODO', 'authoring-start');
|
|
2271
|
+
expect((await app.inject(`/api/sessions/${sessionId}`)).json()).toMatchObject({
|
|
2272
|
+
autopilot: { enabled: false, state: 'disabled' },
|
|
2273
|
+
});
|
|
2274
|
+
expect(handles.flatMap((handle) => handle.requests.filter((request) => request.method === 'turn/start'))).toHaveLength(0);
|
|
2275
|
+
await app.close();
|
|
2276
|
+
});
|
|
2245
2277
|
it('rejects a wait lease when automatic continuation is unavailable', async () => {
|
|
2246
2278
|
const root = await mkdtemp(join(tmpdir(), 'gestalt-mobile-root-'));
|
|
2247
2279
|
const dataDir = await mkdtemp(join(tmpdir(), 'gestalt-mobile-state-'));
|
|
@@ -2649,6 +2681,7 @@ describe('production composition', () => {
|
|
|
2649
2681
|
['DELETE', '/api/auth/devices/:deviceId', '/api/auth/devices/device-1', 'protected'],
|
|
2650
2682
|
['GET', '/api/bootstrap', '/api/bootstrap', 'protected'],
|
|
2651
2683
|
['HEAD', '/api/bootstrap', '/api/bootstrap', 'protected'],
|
|
2684
|
+
['POST', '/api/session-models/:provider', '/api/session-models/codex', 'protected'],
|
|
2652
2685
|
['POST', '/api/sessions', '/api/sessions', 'protected'],
|
|
2653
2686
|
['GET', '/api/sessions', '/api/sessions', 'protected'],
|
|
2654
2687
|
['HEAD', '/api/sessions', '/api/sessions', 'protected'],
|
|
@@ -3029,7 +3062,7 @@ describe('production composition', () => {
|
|
|
3029
3062
|
const created = await app.inject({
|
|
3030
3063
|
method: 'POST',
|
|
3031
3064
|
url: '/api/sessions',
|
|
3032
|
-
payload: { workspaceId: workspace.id, profile: 'default' },
|
|
3065
|
+
payload: { workspaceId: workspace.id, profile: 'default', provider: 'codex' },
|
|
3033
3066
|
});
|
|
3034
3067
|
expect(created.statusCode).toBe(202);
|
|
3035
3068
|
const restored = await app.inject({
|
|
@@ -3042,6 +3075,156 @@ describe('production composition', () => {
|
|
|
3042
3075
|
});
|
|
3043
3076
|
await app.close();
|
|
3044
3077
|
});
|
|
3078
|
+
it('keeps cold bootstrap passive while explicit Kimi session creation resolves a Kimi model', async () => {
|
|
3079
|
+
const root = await mkdtemp(join(tmpdir(), 'gestalt-mobile-root-'));
|
|
3080
|
+
const dataDir = await mkdtemp(join(tmpdir(), 'gestalt-mobile-state-'));
|
|
3081
|
+
ownTemporaryPaths(root, dataDir);
|
|
3082
|
+
await mkdir(join(root, 'workspace'));
|
|
3083
|
+
const sessionModelCalls = [];
|
|
3084
|
+
const app = await composeAuthorizedApp({
|
|
3085
|
+
root,
|
|
3086
|
+
dataDir,
|
|
3087
|
+
relyingParty,
|
|
3088
|
+
installedCodexVersion: null,
|
|
3089
|
+
installedKimiVersion: '2.0.2',
|
|
3090
|
+
launchAppServer: () => fakeAppServer([]),
|
|
3091
|
+
sessionModelCatalog: {
|
|
3092
|
+
list: async (provider) => {
|
|
3093
|
+
sessionModelCalls.push(provider);
|
|
3094
|
+
return provider === 'kimi' ? ['k2-thinking'] : ['gpt-5.6-terra'];
|
|
3095
|
+
},
|
|
3096
|
+
},
|
|
3097
|
+
profiles: {
|
|
3098
|
+
list: async () => [],
|
|
3099
|
+
require: async () => {
|
|
3100
|
+
throw new Error('CODEX_LAUNCHER_UNAVAILABLE');
|
|
3101
|
+
},
|
|
3102
|
+
},
|
|
3103
|
+
});
|
|
3104
|
+
const bootstrap = await app.inject('/api/bootstrap');
|
|
3105
|
+
const workspace = bootstrap
|
|
3106
|
+
.json()
|
|
3107
|
+
.workspaces[0]?.children.find((item) => item.name === 'workspace');
|
|
3108
|
+
expect(bootstrap.json().models.kimi).toEqual([]);
|
|
3109
|
+
expect(sessionModelCalls).toEqual([]);
|
|
3110
|
+
const created = await app.inject({
|
|
3111
|
+
method: 'POST',
|
|
3112
|
+
url: '/api/sessions',
|
|
3113
|
+
payload: { workspaceId: workspace.id, profile: 'default', provider: 'kimi' },
|
|
3114
|
+
});
|
|
3115
|
+
expect(created.statusCode).toBe(202);
|
|
3116
|
+
expect(created.json()).toMatchObject({ provider: 'kimi', model: 'k2-thinking' });
|
|
3117
|
+
expect(sessionModelCalls).toEqual(['kimi']);
|
|
3118
|
+
await app.close();
|
|
3119
|
+
});
|
|
3120
|
+
it('keeps real server listen passive when Kimi is installed', async () => {
|
|
3121
|
+
const root = await mkdtemp(join(tmpdir(), 'gestalt-mobile-root-'));
|
|
3122
|
+
const dataDir = await mkdtemp(join(tmpdir(), 'gestalt-mobile-state-'));
|
|
3123
|
+
ownTemporaryPaths(root, dataDir);
|
|
3124
|
+
await mkdir(join(root, 'workspace'));
|
|
3125
|
+
const ensure = vi.fn(async () => {
|
|
3126
|
+
throw new Error('passive startup must not ensure Kimi');
|
|
3127
|
+
});
|
|
3128
|
+
const app = await composeAuthorizedApp({
|
|
3129
|
+
root,
|
|
3130
|
+
dataDir,
|
|
3131
|
+
relyingParty,
|
|
3132
|
+
installedCodexVersion: null,
|
|
3133
|
+
installedKimiVersion: '2.0.2',
|
|
3134
|
+
startAppServers: true,
|
|
3135
|
+
kimiServerManager: {
|
|
3136
|
+
ensure,
|
|
3137
|
+
get: () => null,
|
|
3138
|
+
list: () => [],
|
|
3139
|
+
stopAll: async () => { },
|
|
3140
|
+
},
|
|
3141
|
+
profiles: {
|
|
3142
|
+
list: async () => [],
|
|
3143
|
+
require: async () => {
|
|
3144
|
+
throw new Error('CODEX_LAUNCHER_UNAVAILABLE');
|
|
3145
|
+
},
|
|
3146
|
+
},
|
|
3147
|
+
});
|
|
3148
|
+
await app.listen({ host: '127.0.0.1', port: 0 });
|
|
3149
|
+
expect(ensure).not.toHaveBeenCalled();
|
|
3150
|
+
await app.close();
|
|
3151
|
+
});
|
|
3152
|
+
it('delivers a realistic Codex answer notification through the journal to the chat projection', async () => {
|
|
3153
|
+
const root = await mkdtemp(join(tmpdir(), 'gestalt-mobile-root-'));
|
|
3154
|
+
const dataDir = await mkdtemp(join(tmpdir(), 'gestalt-mobile-state-'));
|
|
3155
|
+
ownTemporaryPaths(root, dataDir);
|
|
3156
|
+
await mkdir(join(root, 'workspace'));
|
|
3157
|
+
const handles = [];
|
|
3158
|
+
const app = await composeAuthorizedApp({
|
|
3159
|
+
root,
|
|
3160
|
+
dataDir,
|
|
3161
|
+
relyingParty,
|
|
3162
|
+
installedCodexVersion: 'codex-cli 0.144.3',
|
|
3163
|
+
startAppServers: true,
|
|
3164
|
+
launchAppServer: liveAppServer(handles),
|
|
3165
|
+
profiles: {
|
|
3166
|
+
list: async () => [],
|
|
3167
|
+
require: async () => ({
|
|
3168
|
+
name: 'default',
|
|
3169
|
+
state: 'ok',
|
|
3170
|
+
status: 'ready',
|
|
3171
|
+
}),
|
|
3172
|
+
},
|
|
3173
|
+
});
|
|
3174
|
+
const sessionId = await createComposedSession(app);
|
|
3175
|
+
const handle = handles.find((candidate) => candidate.calls.includes('thread/start'));
|
|
3176
|
+
const started = await app.inject({
|
|
3177
|
+
method: 'POST',
|
|
3178
|
+
url: `/api/sessions/${sessionId}/turns`,
|
|
3179
|
+
payload: { text: 'give the answer' },
|
|
3180
|
+
});
|
|
3181
|
+
expect(started.statusCode).toBe(202);
|
|
3182
|
+
const turnId = started.json().activeTurnId;
|
|
3183
|
+
const threadId = (await app.inject(`/api/sessions/${sessionId}`)).json().threadId;
|
|
3184
|
+
await app.listen({ host: '127.0.0.1', port: 0 });
|
|
3185
|
+
const address = app.server.address();
|
|
3186
|
+
if (!address || typeof address === 'string')
|
|
3187
|
+
throw new Error('Expected TCP listener');
|
|
3188
|
+
const socket = new WebSocket(`ws://127.0.0.1:${address.port}/api/sessions/${sessionId}/events?after=0`, {
|
|
3189
|
+
headers: {
|
|
3190
|
+
origin: relyingParty.publicOrigin,
|
|
3191
|
+
cookie: 'gestalt_mobile_session=test-session',
|
|
3192
|
+
},
|
|
3193
|
+
});
|
|
3194
|
+
const messages = [];
|
|
3195
|
+
socket.on('message', (data) => messages.push(JSON.parse(String(data))));
|
|
3196
|
+
await once(socket, 'open');
|
|
3197
|
+
handle.notify({
|
|
3198
|
+
method: 'item/completed',
|
|
3199
|
+
params: {
|
|
3200
|
+
threadId,
|
|
3201
|
+
turnId,
|
|
3202
|
+
item: {
|
|
3203
|
+
id: 'answer-1',
|
|
3204
|
+
type: 'agentMessage',
|
|
3205
|
+
phase: 'final_answer',
|
|
3206
|
+
text: 'The provider boundary is intact.',
|
|
3207
|
+
},
|
|
3208
|
+
},
|
|
3209
|
+
});
|
|
3210
|
+
await vi.waitFor(() => expect(messages.some((message) => message.event.type === 'agentMessageCompleted')).toBe(true));
|
|
3211
|
+
const projection = messages
|
|
3212
|
+
.map((message) => message.event)
|
|
3213
|
+
.sort((left, right) => left.sequence - right.sequence)
|
|
3214
|
+
.reduce((current, event) => applyProjectionEvent(current, event), createChatProjection(sessionId));
|
|
3215
|
+
expect(projection.messages).toEqual([
|
|
3216
|
+
expect.objectContaining({
|
|
3217
|
+
id: 'assistant:answer-1',
|
|
3218
|
+
role: 'assistant',
|
|
3219
|
+
text: 'The provider boundary is intact.',
|
|
3220
|
+
phase: 'final_answer',
|
|
3221
|
+
complete: true,
|
|
3222
|
+
turnId,
|
|
3223
|
+
}),
|
|
3224
|
+
]);
|
|
3225
|
+
socket.close();
|
|
3226
|
+
await app.close();
|
|
3227
|
+
});
|
|
3045
3228
|
it('hands a replayed session-owned plan replacement off to its live close event exactly once', async () => {
|
|
3046
3229
|
const root = await mkdtemp(join(tmpdir(), 'gestalt-mobile-root-'));
|
|
3047
3230
|
const dataDir = await mkdtemp(join(tmpdir(), 'gestalt-mobile-state-'));
|
|
@@ -3079,7 +3262,7 @@ describe('production composition', () => {
|
|
|
3079
3262
|
const created = await app.inject({
|
|
3080
3263
|
method: 'POST',
|
|
3081
3264
|
url: '/api/sessions',
|
|
3082
|
-
payload: { workspaceId: workspace.id, profile: 'default' },
|
|
3265
|
+
payload: { workspaceId: workspace.id, profile: 'default', provider: 'codex' },
|
|
3083
3266
|
});
|
|
3084
3267
|
const sessionId = created.json().id;
|
|
3085
3268
|
await writeFile(planStatusFilePath(planStatusDirectoryPath(workspacePath, sessionId), planPath), JSON.stringify({
|
|
@@ -3203,7 +3386,7 @@ describe('production composition', () => {
|
|
|
3203
3386
|
const created = await first.inject({
|
|
3204
3387
|
method: 'POST',
|
|
3205
3388
|
url: '/api/sessions',
|
|
3206
|
-
payload: { workspaceId: workspace.id, profile: 'default' },
|
|
3389
|
+
payload: { workspaceId: workspace.id, profile: 'default', provider: 'codex' },
|
|
3207
3390
|
});
|
|
3208
3391
|
expect(created.statusCode).toBe(202);
|
|
3209
3392
|
expect(firstCalls).toEqual([
|
|
@@ -3285,7 +3468,7 @@ describe('production composition', () => {
|
|
|
3285
3468
|
await app.inject({
|
|
3286
3469
|
method: 'POST',
|
|
3287
3470
|
url: '/api/sessions',
|
|
3288
|
-
payload: { workspaceId: workspace.id, profile: 'default' },
|
|
3471
|
+
payload: { workspaceId: workspace.id, profile: 'default', provider: 'codex' },
|
|
3289
3472
|
});
|
|
3290
3473
|
await app.close();
|
|
3291
3474
|
// Model and skill catalogs run for bootstrap and session start; the active child closes with the relay.
|
|
@@ -3356,7 +3539,7 @@ describe('production composition', () => {
|
|
|
3356
3539
|
const created = await app.inject({
|
|
3357
3540
|
method: 'POST',
|
|
3358
3541
|
url: '/api/sessions',
|
|
3359
|
-
payload: { workspaceId: workspace.id, profile: 'default' },
|
|
3542
|
+
payload: { workspaceId: workspace.id, profile: 'default', provider: 'codex' },
|
|
3360
3543
|
});
|
|
3361
3544
|
const sessionId = created.json().id;
|
|
3362
3545
|
const handle = handles.find((candidate) => candidate.calls.includes('thread/start'));
|