ds-harness-remote 0.4.3 → 0.4.5
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 +57 -9
- package/bin/ds-harness-remote.js +11 -0
- package/cordis.patch.yml +22 -0
- package/dist/cli.d.ts +31 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +283 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.github.js +31 -15
- package/dist/client.js +31 -15
- package/dist/client.js.map +1 -1
- package/dist/codex/domain.d.ts +6 -2
- package/dist/codex/domain.d.ts.map +1 -1
- package/dist/codex/domain.js +279 -68
- package/dist/codex/domain.js.map +1 -1
- package/dist/codex/method-policy.d.ts +14 -7
- package/dist/codex/method-policy.d.ts.map +1 -1
- package/dist/codex/method-policy.js +3 -0
- package/dist/codex/method-policy.js.map +1 -1
- package/dist/codex/virtual-harness.d.ts +6 -0
- package/dist/codex/virtual-harness.d.ts.map +1 -1
- package/dist/codex/virtual-harness.js +161 -23
- package/dist/codex/virtual-harness.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/harness-api-bridge.d.ts.map +1 -1
- package/dist/harness-api-bridge.js +14 -6
- package/dist/harness-api-bridge.js.map +1 -1
- package/dist/harness-remote-bridge.d.ts +1 -1
- package/dist/harness-remote-bridge.d.ts.map +1 -1
- package/dist/harness-remote-bridge.js +1 -0
- package/dist/harness-remote-bridge.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1342 -270
- package/dist/index.js.map +4 -4
- package/dist/server-api.d.ts.map +1 -1
- package/dist/server-api.js +15 -2
- package/dist/server-api.js.map +1 -1
- package/dist/server-credentials.d.ts +2 -2
- package/dist/service.d.ts +4 -1
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +34 -2
- package/dist/service.js.map +1 -1
- package/dist/tui-command.d.ts +13 -0
- package/dist/tui-command.d.ts.map +1 -0
- package/dist/tui-command.js +378 -0
- package/dist/tui-command.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dsh-plugin.json +41 -0
- package/package.json +6 -1
- package/public.d.ts +1 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CodexRemoteClient, projectCodexThread, } from '@dsh-remote/client-core';
|
|
1
|
+
import { CodexRemoteClient, deriveCodexCwdWorkspaces, projectCodexThread, } from '@dsh-remote/client-core';
|
|
2
2
|
const CODEX_SESSION_PREFIX = 'codex:';
|
|
3
3
|
const CODEX_WORKSPACE_PREFIX = 'codex-workspace:';
|
|
4
4
|
const CODEX_PROVIDER = 'codex';
|
|
@@ -151,11 +151,14 @@ export class CodexVirtualHarness {
|
|
|
151
151
|
}));
|
|
152
152
|
}
|
|
153
153
|
case 'session/selectModel': return business(await this.selectModel(requestArg(args), signal));
|
|
154
|
-
case 'session/canOpenWorkspacePath': return business(
|
|
154
|
+
case 'session/canOpenWorkspacePath': return business(this.selectedWorkspaceId !== undefined);
|
|
155
155
|
case 'session/openWorkspacePath': return business(failure('bad-request', 'Opening Host paths is unavailable in CodeX mode.'));
|
|
156
|
+
case 'host/describe': return business(success(await this.describeHost(signal)));
|
|
157
|
+
case 'host/listDirectory': return business(await this.listDirectory(requestArg(args), signal));
|
|
158
|
+
case 'directoryPicker/list': return business(await this.listDirectory(requestArg(args), signal));
|
|
156
159
|
case 'skills/list': return business(success({ items: [] }));
|
|
157
160
|
case 'commands/list': return ok(this.commandList(args));
|
|
158
|
-
case 'commands/execute': return ok(this.executeCommand(args));
|
|
161
|
+
case 'commands/execute': return ok(await this.executeCommand(args, signal));
|
|
159
162
|
default: return fail('method-not-found', `CodeX virtual Harness does not implement ${endpoint}.`);
|
|
160
163
|
}
|
|
161
164
|
}
|
|
@@ -241,6 +244,10 @@ export class CodexVirtualHarness {
|
|
|
241
244
|
permissionSelection(sessionId) {
|
|
242
245
|
return this.selectedPermissions.get(sessionId) ?? CODEX_DEFAULT_PERMISSION;
|
|
243
246
|
}
|
|
247
|
+
setPermissionSelection(sessionId, preset) {
|
|
248
|
+
this.selectedPermissions.set(sessionId, preset);
|
|
249
|
+
this.publishProjection(sessionId, 'permissions', codexPermissionsProjection(preset), this.nextProjectionSeq());
|
|
250
|
+
}
|
|
244
251
|
commandList(args) {
|
|
245
252
|
nativeThreadId(requiredString(args.agentId, 'agentId'));
|
|
246
253
|
return [{
|
|
@@ -249,9 +256,9 @@ export class CodexVirtualHarness {
|
|
|
249
256
|
input: { hint: '<preset>' },
|
|
250
257
|
}];
|
|
251
258
|
}
|
|
252
|
-
executeCommand(args) {
|
|
259
|
+
async executeCommand(args, signal) {
|
|
253
260
|
const sessionId = requiredString(args.agentId, 'agentId');
|
|
254
|
-
nativeThreadId(sessionId);
|
|
261
|
+
const threadId = nativeThreadId(sessionId);
|
|
255
262
|
const line = requiredString(args.line, 'command line').trim();
|
|
256
263
|
if (!Array.isArray(args.images))
|
|
257
264
|
throw new Error('The CodeX command image list is required.');
|
|
@@ -275,9 +282,10 @@ export class CodexVirtualHarness {
|
|
|
275
282
|
commandId,
|
|
276
283
|
result: { kind: 'error', text: `unknown preset "${requested}" (available: workspace-write, danger-full-access)` },
|
|
277
284
|
};
|
|
278
|
-
this.
|
|
279
|
-
|
|
280
|
-
|
|
285
|
+
const result = await this.client.request('thread/resume', { threadId, permissionPreset: requested }, signal);
|
|
286
|
+
const effective = codexPermissionPresetFromResponse(result) ?? requested;
|
|
287
|
+
this.setPermissionSelection(sessionId, effective);
|
|
288
|
+
return { commandId, result: { kind: 'success', text: `preset ${effective}` } };
|
|
281
289
|
}
|
|
282
290
|
sessionTitle(sessionId) {
|
|
283
291
|
const session = this.catalog?.sessions.find(item => item.id === sessionId);
|
|
@@ -429,7 +437,7 @@ export class CodexVirtualHarness {
|
|
|
429
437
|
queue,
|
|
430
438
|
nextSeq: history.cursor + 1,
|
|
431
439
|
turn: history.nextTurn,
|
|
432
|
-
stepOpen:
|
|
440
|
+
stepOpen: history.activeTurnId !== undefined,
|
|
433
441
|
startedItems: new Set(),
|
|
434
442
|
completedItems: new Set(),
|
|
435
443
|
streamedBlocks: new Map(),
|
|
@@ -439,6 +447,7 @@ export class CodexVirtualHarness {
|
|
|
439
447
|
liveToolOutput: new Map(),
|
|
440
448
|
liveToolResultSeq: new Map(),
|
|
441
449
|
requestId: this.pendingRequestIds.get(sessionId),
|
|
450
|
+
...(history.activeTurnId === undefined ? {} : { activeTurnId: history.activeTurnId }),
|
|
442
451
|
};
|
|
443
452
|
this.follows.add(follow);
|
|
444
453
|
queue.push({
|
|
@@ -484,6 +493,12 @@ export class CodexVirtualHarness {
|
|
|
484
493
|
this.publishProjection(follow.sessionId, 'title', title, seq);
|
|
485
494
|
return;
|
|
486
495
|
}
|
|
496
|
+
if (frame.method === 'thread/settings/updated') {
|
|
497
|
+
const preset = codexPermissionPresetFromResponse(params);
|
|
498
|
+
if (preset !== undefined)
|
|
499
|
+
this.setPermissionSelection(follow.sessionId, preset);
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
487
502
|
if (frame.method === 'turn/started') {
|
|
488
503
|
const turn = record(params.turn);
|
|
489
504
|
this.resetLiveTurn(follow);
|
|
@@ -889,6 +904,49 @@ export class CodexVirtualHarness {
|
|
|
889
904
|
? failure('workspace-not-found', 'The CodeX virtual Workspace was not found.')
|
|
890
905
|
: success({ workspace: nativeWorkspace(workspace) });
|
|
891
906
|
}
|
|
907
|
+
selectedWorkspace(catalog) {
|
|
908
|
+
return this.selectedWorkspaceId === undefined
|
|
909
|
+
? undefined
|
|
910
|
+
: catalog.workspaces.find(item => item.workspaceId === this.selectedWorkspaceId);
|
|
911
|
+
}
|
|
912
|
+
async describeHost(signal) {
|
|
913
|
+
const catalog = await this.currentCatalog(signal);
|
|
914
|
+
const workspace = this.selectedWorkspace(catalog);
|
|
915
|
+
const models = await this.models(signal).catch(() => undefined);
|
|
916
|
+
return {
|
|
917
|
+
version: 'CodeX Remote',
|
|
918
|
+
cwd: workspace?.path ?? '',
|
|
919
|
+
home: workspace?.path ?? '',
|
|
920
|
+
provider: 'CodeX',
|
|
921
|
+
...(models === undefined ? {} : { model: models.default.model }),
|
|
922
|
+
attachedSessions: catalog.sessions.length,
|
|
923
|
+
canOpenPath: workspace !== undefined,
|
|
924
|
+
};
|
|
925
|
+
}
|
|
926
|
+
async listDirectory(request, signal) {
|
|
927
|
+
const catalog = await this.currentCatalog(signal);
|
|
928
|
+
const workspace = this.selectedWorkspace(catalog);
|
|
929
|
+
if (workspace === undefined)
|
|
930
|
+
return failure('workspace-not-found', 'The CodeX virtual Workspace was not found.');
|
|
931
|
+
const path = string(request.path) ?? workspace.path;
|
|
932
|
+
if (!containsPath(workspace.path, path)) {
|
|
933
|
+
return failure('workspace-not-found', 'The selected directory is outside the current CodeX virtual Workspace.');
|
|
934
|
+
}
|
|
935
|
+
const listing = record(await this.client.request('dsh/directoryList', { path }, signal));
|
|
936
|
+
if (!isCodexDirectoryListing(listing)) {
|
|
937
|
+
return failure('internal', 'CodeX Remote returned an invalid directory listing.');
|
|
938
|
+
}
|
|
939
|
+
const listedPaths = [
|
|
940
|
+
listing.path,
|
|
941
|
+
listing.home,
|
|
942
|
+
...listing.crumbs.map(item => item.path),
|
|
943
|
+
...listing.entries.map(item => item.path),
|
|
944
|
+
];
|
|
945
|
+
if (listedPaths.some(path => !containsPath(workspace.path, path))) {
|
|
946
|
+
return failure('internal', 'CodeX Remote returned a directory outside the current Workspace.');
|
|
947
|
+
}
|
|
948
|
+
return success(listing);
|
|
949
|
+
}
|
|
892
950
|
async archiveSession(request, signal) {
|
|
893
951
|
const sessionId = requiredString(request.sessionId, 'sessionId');
|
|
894
952
|
await this.client.request('thread/archive', { threadId: nativeThreadId(sessionId) }, signal);
|
|
@@ -899,18 +957,23 @@ export class CodexVirtualHarness {
|
|
|
899
957
|
async createSession(request, signal) {
|
|
900
958
|
const catalog = await this.currentCatalog(signal);
|
|
901
959
|
const workspaceId = string(request.workspaceId) ?? this.selectedWorkspaceId;
|
|
960
|
+
const workspace = workspaceId === undefined ? undefined : catalog.workspaces.find(item => item.workspaceId === workspaceId);
|
|
902
961
|
const cwd = string(request.cwd)
|
|
903
|
-
??
|
|
962
|
+
?? workspace?.path;
|
|
904
963
|
if (cwd === undefined)
|
|
905
964
|
return failure('workspace-not-found', 'The CodeX virtual Workspace was not found.');
|
|
965
|
+
if (workspace !== undefined && !containsPath(workspace.path, cwd)) {
|
|
966
|
+
return failure('workspace-not-found', 'The selected directory is outside the current CodeX virtual Workspace.');
|
|
967
|
+
}
|
|
906
968
|
const directory = await this.models(signal);
|
|
907
969
|
const selection = directory.default;
|
|
908
|
-
const
|
|
970
|
+
const requestedPreset = CODEX_DEFAULT_PERMISSION;
|
|
909
971
|
const result = record(await this.client.request('thread/start', {
|
|
910
972
|
cwd,
|
|
911
973
|
model: selection.model,
|
|
912
|
-
permissionPreset,
|
|
974
|
+
permissionPreset: requestedPreset,
|
|
913
975
|
}, signal));
|
|
976
|
+
const permissionPreset = codexPermissionPresetFromResponse(result) ?? requestedPreset;
|
|
914
977
|
const thread = { ...record(result.thread), cwd, turns: array(record(result.thread).turns) };
|
|
915
978
|
const projected = projectCodexThread(thread);
|
|
916
979
|
if (projected === undefined)
|
|
@@ -932,11 +995,12 @@ export class CodexVirtualHarness {
|
|
|
932
995
|
}
|
|
933
996
|
async forkSession(request, signal) {
|
|
934
997
|
const sessionId = requiredString(request.sessionId, 'sessionId');
|
|
935
|
-
|
|
998
|
+
let permissionPreset = this.selectedPermissions.get(sessionId);
|
|
936
999
|
const result = record(await this.client.request('thread/fork', {
|
|
937
1000
|
threadId: nativeThreadId(sessionId),
|
|
938
|
-
permissionPreset,
|
|
1001
|
+
...(permissionPreset === undefined ? {} : { permissionPreset }),
|
|
939
1002
|
}, signal));
|
|
1003
|
+
permissionPreset = codexPermissionPresetFromResponse(result) ?? permissionPreset ?? CODEX_DEFAULT_PERMISSION;
|
|
940
1004
|
const projected = projectCodexThread(record(result.thread));
|
|
941
1005
|
if (projected === undefined)
|
|
942
1006
|
return failure('internal', 'CodeX returned an invalid forked Thread.');
|
|
@@ -955,15 +1019,20 @@ export class CodexVirtualHarness {
|
|
|
955
1019
|
return failure('attachment-error', 'CodeX virtual Sessions accept text and pasted PNG, JPEG, WebP, or GIF images only.');
|
|
956
1020
|
const threadId = nativeThreadId(sessionId);
|
|
957
1021
|
const selection = this.modelSelection(sessionId, await this.models(signal));
|
|
958
|
-
|
|
1022
|
+
let permissionPreset = this.selectedPermissions.get(sessionId);
|
|
959
1023
|
const isFreshBlankThread = this.blankThreads.has(threadId) || this.pendingThreads.has(threadId);
|
|
960
1024
|
await this.ensureRcFollow(sessionId);
|
|
961
|
-
if (!isFreshBlankThread)
|
|
962
|
-
await this.client.request('thread/resume', {
|
|
1025
|
+
if (!isFreshBlankThread) {
|
|
1026
|
+
const resumeResult = await this.client.request('thread/resume', {
|
|
963
1027
|
threadId,
|
|
964
1028
|
model: selection.model,
|
|
965
|
-
permissionPreset,
|
|
966
1029
|
}, signal);
|
|
1030
|
+
const serverPreset = codexPermissionPresetFromResponse(resumeResult);
|
|
1031
|
+
if (permissionPreset === undefined && serverPreset !== undefined) {
|
|
1032
|
+
permissionPreset = serverPreset;
|
|
1033
|
+
this.setPermissionSelection(sessionId, serverPreset);
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
967
1036
|
this.pendingRequestIds.set(sessionId, string(request.requestId) ?? '');
|
|
968
1037
|
const mode = request.mode === 'steer' ? 'turn/steer' : 'turn/start';
|
|
969
1038
|
if (mode === 'turn/steer') {
|
|
@@ -977,7 +1046,7 @@ export class CodexVirtualHarness {
|
|
|
977
1046
|
threadId,
|
|
978
1047
|
input,
|
|
979
1048
|
...codexModelParams(selection),
|
|
980
|
-
permissionPreset,
|
|
1049
|
+
...(permissionPreset === undefined ? {} : { permissionPreset }),
|
|
981
1050
|
}, signal);
|
|
982
1051
|
}
|
|
983
1052
|
this.blankThreads.delete(threadId);
|
|
@@ -1136,7 +1205,7 @@ export class CodexVirtualHarness {
|
|
|
1136
1205
|
queue: new AsyncValueQueue(controller.signal),
|
|
1137
1206
|
nextSeq: history.cursor + 1,
|
|
1138
1207
|
turn: history.nextTurn,
|
|
1139
|
-
stepOpen:
|
|
1208
|
+
stepOpen: history.activeTurnId !== undefined,
|
|
1140
1209
|
startedItems: new Set(),
|
|
1141
1210
|
completedItems: new Set(),
|
|
1142
1211
|
streamedBlocks: new Map(),
|
|
@@ -1147,6 +1216,7 @@ export class CodexVirtualHarness {
|
|
|
1147
1216
|
liveToolResultSeq: new Map(),
|
|
1148
1217
|
requestId: this.pendingRequestIds.get(sessionId),
|
|
1149
1218
|
rcOnly: true,
|
|
1219
|
+
...(history.activeTurnId === undefined ? {} : { activeTurnId: history.activeTurnId }),
|
|
1150
1220
|
};
|
|
1151
1221
|
this.follows.add(follow);
|
|
1152
1222
|
try {
|
|
@@ -1225,7 +1295,10 @@ export class CodexVirtualHarness {
|
|
|
1225
1295
|
archiveSession: call('workspace.archiveSession'),
|
|
1226
1296
|
},
|
|
1227
1297
|
subagents: {},
|
|
1228
|
-
host: {
|
|
1298
|
+
host: {
|
|
1299
|
+
describe: call('host.describe'),
|
|
1300
|
+
listDirectory: call('host.listDirectory'),
|
|
1301
|
+
},
|
|
1229
1302
|
skills: { list: call('skills.list') },
|
|
1230
1303
|
agentPresets: {},
|
|
1231
1304
|
goals: {},
|
|
@@ -1289,7 +1362,7 @@ export class CodexVirtualHarness {
|
|
|
1289
1362
|
}
|
|
1290
1363
|
}
|
|
1291
1364
|
async function loadCatalog(client, signal, pendingThreads) {
|
|
1292
|
-
|
|
1365
|
+
let projects = await loadCodexProjects(client, signal);
|
|
1293
1366
|
const threads = [];
|
|
1294
1367
|
let cursor;
|
|
1295
1368
|
for (let page = 0; page < MAX_CODEX_PAGES; page += 1) {
|
|
@@ -1318,6 +1391,16 @@ async function loadCatalog(client, signal, pendingThreads) {
|
|
|
1318
1391
|
threads.unshift(thread);
|
|
1319
1392
|
}
|
|
1320
1393
|
}
|
|
1394
|
+
if (projects.length === 0) {
|
|
1395
|
+
projects = deriveCodexCwdWorkspaces(threads).map(workspace => ({
|
|
1396
|
+
id: workspace.id,
|
|
1397
|
+
name: workspace.name,
|
|
1398
|
+
roots: [{ path: workspace.path }],
|
|
1399
|
+
position: workspace.position,
|
|
1400
|
+
createdAt: workspace.createdAt,
|
|
1401
|
+
updatedAt: workspace.updatedAt,
|
|
1402
|
+
}));
|
|
1403
|
+
}
|
|
1321
1404
|
const projected = threads.map(thread => {
|
|
1322
1405
|
const session = projectCodexThread(thread);
|
|
1323
1406
|
return session === undefined ? undefined : { thread, session };
|
|
@@ -1440,6 +1523,7 @@ export function projectCodexNativeHistory(thread, sessionId) {
|
|
|
1440
1523
|
const entries = [];
|
|
1441
1524
|
let seq = 0;
|
|
1442
1525
|
let turnNumber = 0;
|
|
1526
|
+
const active = activeTurnId(thread.turns);
|
|
1443
1527
|
const append = (type, data, time = Date.now(), view, sourceEventSeqs) => {
|
|
1444
1528
|
const eventSeq = seq++;
|
|
1445
1529
|
entries.push({
|
|
@@ -1459,6 +1543,7 @@ export function projectCodexNativeHistory(thread, sessionId) {
|
|
|
1459
1543
|
for (const rawTurn of array(thread.turns)) {
|
|
1460
1544
|
const turn = record(rawTurn);
|
|
1461
1545
|
turnNumber += 1;
|
|
1546
|
+
const turnActive = isActiveCodexTurn(turn);
|
|
1462
1547
|
const time = normalizeTime(turn.createdAt) || normalizeTime(turn.startedAt) || normalizeTime(thread.createdAt) || Date.now();
|
|
1463
1548
|
append('turn/start', { turn: turnNumber }, time);
|
|
1464
1549
|
append('step/start', { turn: turnNumber, step: 1 }, time);
|
|
@@ -1471,6 +1556,8 @@ export function projectCodexNativeHistory(thread, sessionId) {
|
|
|
1471
1556
|
toolCallSeq = eventSeq;
|
|
1472
1557
|
}
|
|
1473
1558
|
}
|
|
1559
|
+
if (turnActive)
|
|
1560
|
+
continue;
|
|
1474
1561
|
append('step/end', { turn: turnNumber, step: 1 }, normalizeTime(turn.updatedAt) || normalizeTime(turn.completedAt) || time);
|
|
1475
1562
|
append('turn/end', {
|
|
1476
1563
|
turn: turnNumber,
|
|
@@ -1490,6 +1577,7 @@ export function projectCodexNativeHistory(thread, sessionId) {
|
|
|
1490
1577
|
entries,
|
|
1491
1578
|
lastSeq: seq - 1,
|
|
1492
1579
|
nextTurn: turnNumber,
|
|
1580
|
+
...(active === undefined ? {} : { activeTurnId: active }),
|
|
1493
1581
|
};
|
|
1494
1582
|
}
|
|
1495
1583
|
export function paginateCodexNativeHistory(history, request) {
|
|
@@ -1515,6 +1603,7 @@ export function paginateCodexNativeHistory(history, request) {
|
|
|
1515
1603
|
nextTurn: history.nextTurn,
|
|
1516
1604
|
records: window.filter(entry => entry.event.seq >= cut),
|
|
1517
1605
|
hasMore: window.some(entry => entry.event.seq < cut),
|
|
1606
|
+
...(history.activeTurnId === undefined ? {} : { activeTurnId: history.activeTurnId }),
|
|
1518
1607
|
};
|
|
1519
1608
|
}
|
|
1520
1609
|
function itemEvents(item, turn, step, requestId, selection = modelSelection(), sessionId = CODEX_SESSION_PREFIX) {
|
|
@@ -2155,6 +2244,25 @@ function codexImageLimitsProjection() {
|
|
|
2155
2244
|
function isCodexPermissionPreset(value) {
|
|
2156
2245
|
return value === 'workspace-write' || value === 'danger-full-access';
|
|
2157
2246
|
}
|
|
2247
|
+
function codexPermissionPresetFromResponse(value) {
|
|
2248
|
+
const root = record(value);
|
|
2249
|
+
return codexPermissionPresetFromPolicyRecord(root)
|
|
2250
|
+
?? codexPermissionPresetFromPolicyRecord(record(root.threadSettings));
|
|
2251
|
+
}
|
|
2252
|
+
function codexPermissionPresetFromPolicyRecord(value) {
|
|
2253
|
+
const sandbox = value.sandbox ?? value.sandboxPolicy;
|
|
2254
|
+
if (value.approvalPolicy === 'never' && isDangerFullAccessSandbox(sandbox))
|
|
2255
|
+
return 'danger-full-access';
|
|
2256
|
+
if (value.approvalPolicy === 'on-request' && isWorkspaceWriteSandbox(sandbox))
|
|
2257
|
+
return 'workspace-write';
|
|
2258
|
+
return undefined;
|
|
2259
|
+
}
|
|
2260
|
+
function isDangerFullAccessSandbox(value) {
|
|
2261
|
+
return value === 'danger-full-access' || record(value).type === 'dangerFullAccess';
|
|
2262
|
+
}
|
|
2263
|
+
function isWorkspaceWriteSandbox(value) {
|
|
2264
|
+
return value === 'workspace-write' || record(value).type === 'workspaceWrite';
|
|
2265
|
+
}
|
|
2158
2266
|
function modelCatalog(directory) {
|
|
2159
2267
|
return {
|
|
2160
2268
|
default: directory.default,
|
|
@@ -2318,6 +2426,19 @@ function nativeThreadId(sessionId) {
|
|
|
2318
2426
|
}
|
|
2319
2427
|
return sessionId.slice(CODEX_SESSION_PREFIX.length);
|
|
2320
2428
|
}
|
|
2429
|
+
function activeTurnId(value) {
|
|
2430
|
+
if (!Array.isArray(value))
|
|
2431
|
+
return undefined;
|
|
2432
|
+
for (let index = value.length - 1; index >= 0; index -= 1) {
|
|
2433
|
+
const turn = record(value[index]);
|
|
2434
|
+
if (isActiveCodexTurn(turn) && typeof turn.id === 'string' && turn.id.length > 0)
|
|
2435
|
+
return turn.id;
|
|
2436
|
+
}
|
|
2437
|
+
return undefined;
|
|
2438
|
+
}
|
|
2439
|
+
function isActiveCodexTurn(turn) {
|
|
2440
|
+
return turn.status === 'inProgress' || turn.status === 'running';
|
|
2441
|
+
}
|
|
2321
2442
|
function carrierArgs(payload) {
|
|
2322
2443
|
return record(record(payload).args);
|
|
2323
2444
|
}
|
|
@@ -2348,6 +2469,22 @@ function business(value) {
|
|
|
2348
2469
|
}
|
|
2349
2470
|
return ok(value);
|
|
2350
2471
|
}
|
|
2472
|
+
function isCodexDirectoryListing(value) {
|
|
2473
|
+
const listing = record(value);
|
|
2474
|
+
return typeof listing.path === 'string'
|
|
2475
|
+
&& typeof listing.home === 'string'
|
|
2476
|
+
&& Array.isArray(listing.crumbs)
|
|
2477
|
+
&& listing.crumbs.every(isCodexDirectoryEntry)
|
|
2478
|
+
&& Array.isArray(listing.entries)
|
|
2479
|
+
&& listing.entries.every(isCodexDirectoryEntry)
|
|
2480
|
+
&& typeof listing.truncated === 'boolean';
|
|
2481
|
+
}
|
|
2482
|
+
function isCodexDirectoryEntry(value) {
|
|
2483
|
+
const entry = record(value);
|
|
2484
|
+
return typeof entry.name === 'string'
|
|
2485
|
+
&& typeof entry.path === 'string'
|
|
2486
|
+
&& typeof entry.hidden === 'boolean';
|
|
2487
|
+
}
|
|
2351
2488
|
function fail(code, message, details = {}) {
|
|
2352
2489
|
return { ok: false, error: { code, message, details } };
|
|
2353
2490
|
}
|
|
@@ -2413,7 +2550,8 @@ function isCodexNativeHistoryPage(value, sessionId) {
|
|
|
2413
2550
|
&& integer(page.cursor) !== undefined
|
|
2414
2551
|
&& integer(page.nextTurn) !== undefined
|
|
2415
2552
|
&& Array.isArray(page.records)
|
|
2416
|
-
&& typeof page.hasMore === 'boolean'
|
|
2553
|
+
&& typeof page.hasMore === 'boolean'
|
|
2554
|
+
&& (page.activeTurnId === undefined || typeof page.activeTurnId === 'string');
|
|
2417
2555
|
}
|
|
2418
2556
|
function truncateCodePoints(value, maximum) {
|
|
2419
2557
|
return [...value].slice(0, maximum).join('');
|