remote-codex 0.11.41 → 0.11.43
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/apps/relay-server/dist/index.js +161 -53
- package/apps/supervisor-api/dist/index.js +113 -23
- package/apps/supervisor-web/dist/assets/frontend-DNAQzqc-.js +1704 -0
- package/apps/supervisor-web/dist/assets/index-BT0SM9C-.js +21 -0
- package/apps/supervisor-web/dist/assets/index-BcCLYWAf.css +1 -0
- package/apps/supervisor-web/dist/assets/styles-BEieA99i.css +1 -0
- package/apps/supervisor-web/dist/assets/thread-ui-Dmrigdek.js +3714 -0
- package/apps/supervisor-web/dist/index.html +3 -3
- package/package.json +1 -1
- package/packages/claude/src/runtimeAdapter.test.ts +12 -5
- package/packages/claude/src/runtimeAdapter.ts +72 -9
- package/packages/shared/src/index.ts +91 -1
- package/apps/supervisor-web/dist/assets/index-B-qj7e8G.js +0 -21
- package/apps/supervisor-web/dist/assets/index-CDFuOeJN.css +0 -1
- package/apps/supervisor-web/dist/assets/thread-ui-xWvswa2v.js +0 -3710
|
@@ -675,6 +675,20 @@ var RelayStore = class _RelayStore {
|
|
|
675
675
|
).get(deviceId, userId);
|
|
676
676
|
return row ? { sandboxId: row.id, enabled: Boolean(row.workspace_isolation_enabled) } : null;
|
|
677
677
|
}
|
|
678
|
+
hostedWorkspaceBootstrapContext(deviceId) {
|
|
679
|
+
const sandbox = this.hostedWorkspaceIsolation(deviceId);
|
|
680
|
+
if (!sandbox?.enabled) {
|
|
681
|
+
return null;
|
|
682
|
+
}
|
|
683
|
+
const users = this.sqlite.prepare(
|
|
684
|
+
`SELECT u.*
|
|
685
|
+
FROM relay_hosted_sandbox_members m
|
|
686
|
+
JOIN relay_users u ON u.id = m.user_id
|
|
687
|
+
WHERE m.sandbox_id = ? AND u.enabled = 1
|
|
688
|
+
ORDER BY m.position ASC, m.created_at ASC`
|
|
689
|
+
).all(sandbox.sandboxId).map((row) => this.rowToUser(row)).filter((user) => Boolean(user)).map((user) => this.publicUser(user));
|
|
690
|
+
return { sandboxId: sandbox.sandboxId, users };
|
|
691
|
+
}
|
|
678
692
|
hostedUserWorkspaceIds(sandboxId, userId) {
|
|
679
693
|
return this.sqlite.prepare(
|
|
680
694
|
`SELECT workspace_id FROM relay_hosted_user_workspaces
|
|
@@ -683,11 +697,24 @@ var RelayStore = class _RelayStore {
|
|
|
683
697
|
}
|
|
684
698
|
recordHostedUserWorkspace(sandboxId, userId, workspaceId, initial = false) {
|
|
685
699
|
this.sqlite.prepare(
|
|
686
|
-
`INSERT
|
|
700
|
+
`INSERT INTO relay_hosted_user_workspaces
|
|
687
701
|
(sandbox_id, user_id, workspace_id, initial_workspace, created_at)
|
|
688
|
-
VALUES (?, ?, ?, ?, ?)
|
|
702
|
+
VALUES (?, ?, ?, ?, ?)
|
|
703
|
+
ON CONFLICT(sandbox_id, workspace_id) DO UPDATE SET
|
|
704
|
+
initial_workspace = MAX(
|
|
705
|
+
relay_hosted_user_workspaces.initial_workspace,
|
|
706
|
+
excluded.initial_workspace
|
|
707
|
+
)`
|
|
689
708
|
).run(sandboxId, userId, workspaceId, initial ? 1 : 0, (/* @__PURE__ */ new Date()).toISOString());
|
|
690
709
|
}
|
|
710
|
+
hostedInitialWorkspaceId(sandboxId, userId) {
|
|
711
|
+
const row = this.sqlite.prepare(
|
|
712
|
+
`SELECT workspace_id FROM relay_hosted_user_workspaces
|
|
713
|
+
WHERE sandbox_id = ? AND user_id = ? AND initial_workspace = 1
|
|
714
|
+
ORDER BY created_at ASC LIMIT 1`
|
|
715
|
+
).get(sandboxId, userId);
|
|
716
|
+
return row?.workspace_id ?? null;
|
|
717
|
+
}
|
|
691
718
|
ownsHostedWorkspace(sandboxId, userId, workspaceId) {
|
|
692
719
|
return Boolean(
|
|
693
720
|
this.sqlite.prepare(
|
|
@@ -711,6 +738,15 @@ var RelayStore = class _RelayStore {
|
|
|
711
738
|
).get(sandboxId, userId, threadId)
|
|
712
739
|
);
|
|
713
740
|
}
|
|
741
|
+
hasHostedUserThreadInWorkspace(sandboxId, userId, workspaceId) {
|
|
742
|
+
return Boolean(
|
|
743
|
+
this.sqlite.prepare(
|
|
744
|
+
`SELECT 1 FROM relay_hosted_user_threads
|
|
745
|
+
WHERE sandbox_id = ? AND user_id = ? AND workspace_id = ?
|
|
746
|
+
LIMIT 1`
|
|
747
|
+
).get(sandboxId, userId, workspaceId)
|
|
748
|
+
);
|
|
749
|
+
}
|
|
714
750
|
listHostedProviderRecords() {
|
|
715
751
|
return this.sqlite.prepare(
|
|
716
752
|
"SELECT id, credential_ref FROM relay_hosted_sandboxes ORDER BY id"
|
|
@@ -3951,6 +3987,37 @@ function buildRelayServer(config2, options = {}) {
|
|
|
3951
3987
|
hostedSandboxProvider,
|
|
3952
3988
|
config2.hostedSandbox
|
|
3953
3989
|
);
|
|
3990
|
+
const scheduleHostedUserBootstraps = (deviceId) => {
|
|
3991
|
+
const context = store.hostedWorkspaceBootstrapContext(deviceId);
|
|
3992
|
+
const supervisor = state.supervisors.get(deviceId);
|
|
3993
|
+
if (!context || !supervisor || supervisor.socket.readyState !== WEBSOCKET_OPEN) {
|
|
3994
|
+
return;
|
|
3995
|
+
}
|
|
3996
|
+
void Promise.allSettled(
|
|
3997
|
+
context.users.map(
|
|
3998
|
+
(bootstrapUser) => ensureHostedUserBootstrap({
|
|
3999
|
+
store,
|
|
4000
|
+
supervisor,
|
|
4001
|
+
deviceId,
|
|
4002
|
+
sandboxId: context.sandboxId,
|
|
4003
|
+
user: bootstrapUser
|
|
4004
|
+
})
|
|
4005
|
+
)
|
|
4006
|
+
).then((results) => {
|
|
4007
|
+
results.forEach((result, index) => {
|
|
4008
|
+
if (result.status === "rejected") {
|
|
4009
|
+
app2.log.warn(
|
|
4010
|
+
{
|
|
4011
|
+
err: result.reason,
|
|
4012
|
+
deviceId,
|
|
4013
|
+
userId: context.users[index]?.id
|
|
4014
|
+
},
|
|
4015
|
+
"Hosted VM user bootstrap failed."
|
|
4016
|
+
);
|
|
4017
|
+
}
|
|
4018
|
+
});
|
|
4019
|
+
});
|
|
4020
|
+
};
|
|
3954
4021
|
const allowedWebViewCorsOrigins = webViewCorsOrigins(
|
|
3955
4022
|
options.env ?? process.env
|
|
3956
4023
|
);
|
|
@@ -4303,10 +4370,12 @@ function buildRelayServer(config2, options = {}) {
|
|
|
4303
4370
|
}
|
|
4304
4371
|
const { sandboxId } = z.object({ sandboxId: z.string().uuid() }).parse(request.params);
|
|
4305
4372
|
const body = updateHostedSandboxMembersSchema.parse(request.body ?? {});
|
|
4306
|
-
|
|
4373
|
+
const sandbox = hostedSandboxService.updateMembers(
|
|
4307
4374
|
sandboxId,
|
|
4308
4375
|
body.assignedUserIds
|
|
4309
4376
|
);
|
|
4377
|
+
scheduleHostedUserBootstraps(sandbox.deviceId);
|
|
4378
|
+
return sandbox;
|
|
4310
4379
|
}
|
|
4311
4380
|
);
|
|
4312
4381
|
app2.patch(
|
|
@@ -4316,10 +4385,14 @@ function buildRelayServer(config2, options = {}) {
|
|
|
4316
4385
|
if (!user) return;
|
|
4317
4386
|
const { sandboxId } = z.object({ sandboxId: z.string().uuid() }).parse(request.params);
|
|
4318
4387
|
const body = updateHostedSandboxSettingsSchema.parse(request.body ?? {});
|
|
4319
|
-
|
|
4388
|
+
const sandbox = store.setHostedWorkspaceIsolation(
|
|
4320
4389
|
sandboxId,
|
|
4321
4390
|
body.workspaceIsolationEnabled
|
|
4322
4391
|
);
|
|
4392
|
+
if (sandbox.workspaceIsolationEnabled) {
|
|
4393
|
+
scheduleHostedUserBootstraps(sandbox.deviceId);
|
|
4394
|
+
}
|
|
4395
|
+
return sandbox;
|
|
4323
4396
|
}
|
|
4324
4397
|
);
|
|
4325
4398
|
app2.post(
|
|
@@ -4629,6 +4702,10 @@ function buildRelayServer(config2, options = {}) {
|
|
|
4629
4702
|
parsed.clientId
|
|
4630
4703
|
);
|
|
4631
4704
|
const eventThreadId = threadIdFromSocketPayload(parsed.payload);
|
|
4705
|
+
const shellEventForClient = clientConnection ? isShellEventForClient(
|
|
4706
|
+
parsed.payload,
|
|
4707
|
+
clientConnection.attachedShellId
|
|
4708
|
+
) : false;
|
|
4632
4709
|
if (clientConnection) {
|
|
4633
4710
|
const freshAccess = store.effectiveAccess(
|
|
4634
4711
|
clientConnection.user.id,
|
|
@@ -4651,7 +4728,7 @@ function buildRelayServer(config2, options = {}) {
|
|
|
4651
4728
|
clientConnection.user.id
|
|
4652
4729
|
);
|
|
4653
4730
|
const connectionControlEvent = parsed.payload.type === "supervisor.connected" || parsed.payload.type === "supervisor.pong";
|
|
4654
|
-
if (isolation?.enabled && !connectionControlEvent && (!eventThreadId || !store.ownsHostedThread(
|
|
4731
|
+
if (isolation?.enabled && !connectionControlEvent && !shellEventForClient && (!eventThreadId || !store.ownsHostedThread(
|
|
4655
4732
|
isolation.sandboxId,
|
|
4656
4733
|
clientConnection.user.id,
|
|
4657
4734
|
eventThreadId
|
|
@@ -4661,7 +4738,8 @@ function buildRelayServer(config2, options = {}) {
|
|
|
4661
4738
|
}
|
|
4662
4739
|
if (clientConnection && clientConnection.socket.readyState === WEBSOCKET_OPEN && shouldForwardSocketEvent(
|
|
4663
4740
|
parsed.payload,
|
|
4664
|
-
clientConnection.threadId
|
|
4741
|
+
clientConnection.threadId,
|
|
4742
|
+
clientConnection.attachedShellId
|
|
4665
4743
|
)) {
|
|
4666
4744
|
clientConnection.socket.send(JSON.stringify(parsed.payload));
|
|
4667
4745
|
}
|
|
@@ -4681,6 +4759,7 @@ function buildRelayServer(config2, options = {}) {
|
|
|
4681
4759
|
clientConnection.socket.close();
|
|
4682
4760
|
}
|
|
4683
4761
|
});
|
|
4762
|
+
scheduleHostedUserBootstraps(deviceId);
|
|
4684
4763
|
}
|
|
4685
4764
|
});
|
|
4686
4765
|
realtimeApp.route({
|
|
@@ -5073,9 +5152,6 @@ async function forwardSharedThreadList(input) {
|
|
|
5073
5152
|
);
|
|
5074
5153
|
}
|
|
5075
5154
|
async function ensureHostedUserBootstrap(input) {
|
|
5076
|
-
if (input.store.hostedUserWorkspaceIds(input.sandboxId, input.user.id).length) {
|
|
5077
|
-
return;
|
|
5078
|
-
}
|
|
5079
5155
|
const key = `${input.sandboxId}:${input.user.id}`;
|
|
5080
5156
|
const existing = hostedBootstrapPromises.get(key);
|
|
5081
5157
|
if (existing) return existing;
|
|
@@ -5084,55 +5160,69 @@ async function ensureHostedUserBootstrap(input) {
|
|
|
5084
5160
|
const directory = `${slug}-${input.user.id.slice(0, 8)}`;
|
|
5085
5161
|
const absoluteDirectory = `/home/remote-codex/workspaces/${directory}`;
|
|
5086
5162
|
const label = `${input.user.username}'s workspace`;
|
|
5087
|
-
|
|
5088
|
-
input.
|
|
5089
|
-
input.
|
|
5090
|
-
"GET",
|
|
5091
|
-
"/api/workspaces"
|
|
5092
|
-
);
|
|
5093
|
-
const currentWorkspaces = Array.isArray(current) ? current : [];
|
|
5094
|
-
let workspace = currentWorkspaces.find(
|
|
5095
|
-
(candidate) => isObject(candidate) && typeof candidate.absPath === "string" && candidate.absPath === absoluteDirectory
|
|
5163
|
+
let workspaceId = input.store.hostedInitialWorkspaceId(
|
|
5164
|
+
input.sandboxId,
|
|
5165
|
+
input.user.id
|
|
5096
5166
|
);
|
|
5097
|
-
if (!
|
|
5098
|
-
|
|
5167
|
+
if (!workspaceId) {
|
|
5168
|
+
const current = await forwardSupervisorCommandJson(
|
|
5099
5169
|
input.supervisor,
|
|
5100
5170
|
input.deviceId,
|
|
5101
|
-
"
|
|
5102
|
-
"/api/workspaces"
|
|
5103
|
-
{ absPath: absoluteDirectory, label }
|
|
5171
|
+
"GET",
|
|
5172
|
+
"/api/workspaces"
|
|
5104
5173
|
);
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
model: "gpt-5.6-sol",
|
|
5118
|
-
reasoningEffort: "low",
|
|
5119
|
-
approvalMode: "yolo"
|
|
5174
|
+
const currentWorkspaces = Array.isArray(current) ? current : [];
|
|
5175
|
+
let workspace = currentWorkspaces.find(
|
|
5176
|
+
(candidate) => isObject(candidate) && typeof candidate.absPath === "string" && candidate.absPath === absoluteDirectory
|
|
5177
|
+
);
|
|
5178
|
+
if (!workspace) {
|
|
5179
|
+
workspace = await forwardSupervisorCommandJson(
|
|
5180
|
+
input.supervisor,
|
|
5181
|
+
input.deviceId,
|
|
5182
|
+
"POST",
|
|
5183
|
+
"/api/workspaces",
|
|
5184
|
+
{ absPath: absoluteDirectory, label }
|
|
5185
|
+
);
|
|
5120
5186
|
}
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
input.
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5187
|
+
workspaceId = stringField(workspace, "id");
|
|
5188
|
+
if (!workspaceId) {
|
|
5189
|
+
throw new Error("Initial workspace creation returned no id.");
|
|
5190
|
+
}
|
|
5191
|
+
input.store.recordHostedUserWorkspace(
|
|
5192
|
+
input.sandboxId,
|
|
5193
|
+
input.user.id,
|
|
5194
|
+
workspaceId,
|
|
5195
|
+
true
|
|
5196
|
+
);
|
|
5197
|
+
}
|
|
5198
|
+
if (!input.store.hasHostedUserThreadInWorkspace(
|
|
5131
5199
|
input.sandboxId,
|
|
5132
5200
|
input.user.id,
|
|
5133
|
-
threadId,
|
|
5134
5201
|
workspaceId
|
|
5135
|
-
)
|
|
5202
|
+
)) {
|
|
5203
|
+
const thread = await forwardSupervisorCommandJson(
|
|
5204
|
+
input.supervisor,
|
|
5205
|
+
input.deviceId,
|
|
5206
|
+
"POST",
|
|
5207
|
+
"/api/threads/start",
|
|
5208
|
+
{
|
|
5209
|
+
workspaceId,
|
|
5210
|
+
title: "Getting started",
|
|
5211
|
+
provider: "codex",
|
|
5212
|
+
model: "gpt-5.6-sol",
|
|
5213
|
+
reasoningEffort: "low",
|
|
5214
|
+
approvalMode: "yolo"
|
|
5215
|
+
}
|
|
5216
|
+
);
|
|
5217
|
+
const threadId = stringField(thread, "id");
|
|
5218
|
+
if (!threadId) throw new Error("Initial thread creation returned no id.");
|
|
5219
|
+
input.store.recordHostedUserThread(
|
|
5220
|
+
input.sandboxId,
|
|
5221
|
+
input.user.id,
|
|
5222
|
+
threadId,
|
|
5223
|
+
workspaceId
|
|
5224
|
+
);
|
|
5225
|
+
}
|
|
5136
5226
|
})().finally(() => hostedBootstrapPromises.delete(key));
|
|
5137
5227
|
hostedBootstrapPromises.set(key, pending);
|
|
5138
5228
|
return pending;
|
|
@@ -5234,6 +5324,7 @@ function connectRelayWebsocket(supervisor, socket, store, user, deviceId, thread
|
|
|
5234
5324
|
supervisor.clientSockets.set(clientId, {
|
|
5235
5325
|
socket,
|
|
5236
5326
|
threadId,
|
|
5327
|
+
attachedShellId: null,
|
|
5237
5328
|
deviceId,
|
|
5238
5329
|
user,
|
|
5239
5330
|
access
|
|
@@ -5266,6 +5357,10 @@ function connectRelayWebsocket(supervisor, socket, store, user, deviceId, thread
|
|
|
5266
5357
|
socket.close(1008, "Shared read-only session cannot control supervisor.");
|
|
5267
5358
|
return;
|
|
5268
5359
|
}
|
|
5360
|
+
const attachedShellId = shellAttachIdFromSocketPayload(payload);
|
|
5361
|
+
if (clientConnection && attachedShellId) {
|
|
5362
|
+
clientConnection.attachedShellId = attachedShellId;
|
|
5363
|
+
}
|
|
5269
5364
|
sendToSupervisor(supervisor, {
|
|
5270
5365
|
type: "relay.client.message",
|
|
5271
5366
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -5763,7 +5858,9 @@ function isAllowedSharedRuntimeMetadataRequest(method, pathname) {
|
|
|
5763
5858
|
function threadIdFromPath(pathValue) {
|
|
5764
5859
|
const pathname = new URL(pathValue, "http://relay.local").pathname;
|
|
5765
5860
|
const match = /^\/api\/threads\/([^/?#]+)/.exec(pathname);
|
|
5766
|
-
|
|
5861
|
+
if (!match) return null;
|
|
5862
|
+
const threadId = decodeURIComponent(match[1]);
|
|
5863
|
+
return threadId === "start" || threadId === "import" ? null : threadId;
|
|
5767
5864
|
}
|
|
5768
5865
|
function workspaceIdFromPath(pathValue) {
|
|
5769
5866
|
const pathname = new URL(pathValue, "http://relay.local").pathname;
|
|
@@ -5960,14 +6057,25 @@ function isAllowedSharedWorkspacePath(access, methodName, pathname, workspaceId)
|
|
|
5960
6057
|
];
|
|
5961
6058
|
return ["POST", "PUT", "PATCH", "DELETE"].includes(methodName) && writePatterns.some((pattern) => pattern.test(pathname));
|
|
5962
6059
|
}
|
|
5963
|
-
function shouldForwardSocketEvent(event, threadId) {
|
|
6060
|
+
function shouldForwardSocketEvent(event, threadId, attachedShellId) {
|
|
5964
6061
|
if (!threadId) {
|
|
5965
6062
|
return true;
|
|
5966
6063
|
}
|
|
5967
6064
|
if (event.type === "supervisor.connected" || event.type === "supervisor.pong") {
|
|
5968
6065
|
return true;
|
|
5969
6066
|
}
|
|
5970
|
-
|
|
6067
|
+
if ("threadId" in event && event.threadId === threadId) {
|
|
6068
|
+
return true;
|
|
6069
|
+
}
|
|
6070
|
+
return isShellEventForClient(event, attachedShellId);
|
|
6071
|
+
}
|
|
6072
|
+
function shellAttachIdFromSocketPayload(payload) {
|
|
6073
|
+
return isObject(payload) && payload.type === "shell.attach" && typeof payload.shellId === "string" ? payload.shellId : null;
|
|
6074
|
+
}
|
|
6075
|
+
function isShellEventForClient(event, attachedShellId) {
|
|
6076
|
+
return Boolean(
|
|
6077
|
+
attachedShellId && event.type.startsWith("shell.") && "shellId" in event && event.shellId === attachedShellId
|
|
6078
|
+
);
|
|
5971
6079
|
}
|
|
5972
6080
|
function relayRequestBody(body) {
|
|
5973
6081
|
if (body === void 0 || body === null) {
|
|
@@ -12089,7 +12089,7 @@ function mapModelInfo(model, index) {
|
|
|
12089
12089
|
return {
|
|
12090
12090
|
id: model.value,
|
|
12091
12091
|
model: model.value,
|
|
12092
|
-
displayName: model
|
|
12092
|
+
displayName: versionedClaudeModelDisplayName(model),
|
|
12093
12093
|
description: model.description,
|
|
12094
12094
|
isDefault: index === 0,
|
|
12095
12095
|
hidden: false,
|
|
@@ -12100,11 +12100,44 @@ function mapModelInfo(model, index) {
|
|
|
12100
12100
|
defaultReasoningEffort: model.supportsEffort ? "medium" : null
|
|
12101
12101
|
};
|
|
12102
12102
|
}
|
|
12103
|
+
function versionedClaudeModelDisplayName(model) {
|
|
12104
|
+
const resolved = model.resolvedModel?.match(
|
|
12105
|
+
/^claude-(sonnet|opus|haiku|fable)-(\d+)(?:-(\d+))?(?:-|\[|$)/i
|
|
12106
|
+
);
|
|
12107
|
+
if (!resolved) {
|
|
12108
|
+
return model.displayName;
|
|
12109
|
+
}
|
|
12110
|
+
const family = `${resolved[1][0].toUpperCase()}${resolved[1].slice(1).toLowerCase()}`;
|
|
12111
|
+
const version2 = resolved[3] ? `${resolved[2]}.${resolved[3]}` : resolved[2];
|
|
12112
|
+
if (model.value === "default") {
|
|
12113
|
+
return `Default \xB7 ${family} ${version2}`;
|
|
12114
|
+
}
|
|
12115
|
+
const qualifier = model.displayName.match(/\s*(\([^)]+\))\s*$/)?.[1];
|
|
12116
|
+
return `${family} \xB7 ${version2}${qualifier ? ` ${qualifier}` : ""}`;
|
|
12117
|
+
}
|
|
12118
|
+
function versionedAlias(alias, discovered, qualifier) {
|
|
12119
|
+
if (!discovered) {
|
|
12120
|
+
return alias;
|
|
12121
|
+
}
|
|
12122
|
+
return {
|
|
12123
|
+
...alias,
|
|
12124
|
+
displayName: `${discovered.displayName.replace(/\s+\([^)]+\)\s*$/, "")}${qualifier ?? ""}`,
|
|
12125
|
+
description: discovered.description,
|
|
12126
|
+
supportedReasoningEfforts: discovered.supportedReasoningEfforts,
|
|
12127
|
+
defaultReasoningEffort: discovered.defaultReasoningEffort
|
|
12128
|
+
};
|
|
12129
|
+
}
|
|
12103
12130
|
function withClaudeCodeModelAliases(models) {
|
|
12104
12131
|
const output = [...models];
|
|
12105
|
-
const
|
|
12106
|
-
const
|
|
12107
|
-
const
|
|
12132
|
+
const discoveredSonnet = output.find((model) => /^Sonnet · /.test(model.displayName));
|
|
12133
|
+
const discoveredFable = output.find((model) => /^Fable · /.test(model.displayName));
|
|
12134
|
+
const defaultSonnet = versionedAlias(DEFAULT_CLAUDE_MODELS[0], discoveredSonnet);
|
|
12135
|
+
const oneMillionSonnet = versionedAlias(
|
|
12136
|
+
DEFAULT_CLAUDE_MODELS[1],
|
|
12137
|
+
discoveredSonnet,
|
|
12138
|
+
" (1M context)"
|
|
12139
|
+
);
|
|
12140
|
+
const fable = versionedAlias(DEFAULT_CLAUDE_MODELS[2], discoveredFable);
|
|
12108
12141
|
const hasSonnetAlias = output.some((model) => model.model === "sonnet");
|
|
12109
12142
|
if (!hasSonnetAlias) {
|
|
12110
12143
|
output.unshift(defaultSonnet);
|
|
@@ -12613,6 +12646,7 @@ var ClaudeRuntimeAdapter = class extends EventEmitter5 {
|
|
|
12613
12646
|
subscriptionUsageWindows = /* @__PURE__ */ new Map();
|
|
12614
12647
|
subscriptionAuthKind = "unknown";
|
|
12615
12648
|
subscriptionUsageObservedAt = null;
|
|
12649
|
+
modelOptionsCache = null;
|
|
12616
12650
|
historicalTurnIdAliases = /* @__PURE__ */ new Map();
|
|
12617
12651
|
clientApp;
|
|
12618
12652
|
sdkLoadError = null;
|
|
@@ -12733,14 +12767,33 @@ var ClaudeRuntimeAdapter = class extends EventEmitter5 {
|
|
|
12733
12767
|
}
|
|
12734
12768
|
async listModels() {
|
|
12735
12769
|
const active = [...this.activeTurns.values()][0];
|
|
12736
|
-
if (!active) {
|
|
12737
|
-
return
|
|
12770
|
+
if (!active && this.modelOptionsCache) {
|
|
12771
|
+
return this.modelOptionsCache;
|
|
12738
12772
|
}
|
|
12773
|
+
let query = active?.query ?? null;
|
|
12739
12774
|
try {
|
|
12740
|
-
|
|
12741
|
-
|
|
12775
|
+
query ??= this.queryFactory({
|
|
12776
|
+
prompt: "",
|
|
12777
|
+
options: queryOptionsForRuntime({
|
|
12778
|
+
home: this.options.home,
|
|
12779
|
+
command: this.options.command,
|
|
12780
|
+
clientApp: this.clientApp,
|
|
12781
|
+
approvalMode: "guarded",
|
|
12782
|
+
includePartialMessages: false,
|
|
12783
|
+
tools: [],
|
|
12784
|
+
maxTurns: 1
|
|
12785
|
+
})
|
|
12786
|
+
});
|
|
12787
|
+
const models = await query.supportedModels();
|
|
12788
|
+
const mapped = withClaudeCodeModelAliases(models.map(mapModelInfo));
|
|
12789
|
+
this.modelOptionsCache = mapped;
|
|
12790
|
+
return mapped;
|
|
12742
12791
|
} catch {
|
|
12743
|
-
return DEFAULT_CLAUDE_MODELS;
|
|
12792
|
+
return this.modelOptionsCache ?? DEFAULT_CLAUDE_MODELS;
|
|
12793
|
+
} finally {
|
|
12794
|
+
if (!active && query) {
|
|
12795
|
+
query.close();
|
|
12796
|
+
}
|
|
12744
12797
|
}
|
|
12745
12798
|
}
|
|
12746
12799
|
async listSessions() {
|
|
@@ -16459,7 +16512,7 @@ var ThreadAuxiliaryStateStore = class {
|
|
|
16459
16512
|
);
|
|
16460
16513
|
return {
|
|
16461
16514
|
id: record.id,
|
|
16462
|
-
kind: "fastMode",
|
|
16515
|
+
kind: record.kind === "goal" ? "goal" : "fastMode",
|
|
16463
16516
|
text: record.text,
|
|
16464
16517
|
createdAt: record.createdAt,
|
|
16465
16518
|
anchorTurnId: record.anchorTurnId ?? fallbackAnchor?.id ?? null
|
|
@@ -16673,13 +16726,10 @@ var ThreadGoalCoordinator = class {
|
|
|
16673
16726
|
}
|
|
16674
16727
|
try {
|
|
16675
16728
|
await this.ensureGoalsFeatureEnabled(record.provider);
|
|
16676
|
-
const
|
|
16729
|
+
const goalHistoryBeforeUpdate = this.listThreadGoalHistory(record.id);
|
|
16730
|
+
const activeGoal = goalHistoryBeforeUpdate.find(
|
|
16677
16731
|
(goal2) => ["active", "paused", "budgetLimited"].includes(goal2.status)
|
|
16678
16732
|
) ?? null;
|
|
16679
|
-
const creatingNewGoal = goalObjectiveChanged(activeGoal, input.objective);
|
|
16680
|
-
if (creatingNewGoal) {
|
|
16681
|
-
markActiveThreadGoalRecordTerminated(this.db, record.id);
|
|
16682
|
-
}
|
|
16683
16733
|
if (input.status === "terminated") {
|
|
16684
16734
|
const terminatedGoal = markActiveThreadGoalRecordTerminated(this.db, record.id);
|
|
16685
16735
|
const goalHistory = this.listThreadGoalHistory(record.id);
|
|
@@ -16690,6 +16740,23 @@ var ThreadGoalCoordinator = class {
|
|
|
16690
16740
|
});
|
|
16691
16741
|
return goal2;
|
|
16692
16742
|
}
|
|
16743
|
+
const startingNewGoal = shouldStartNewGoal(activeGoal, input.objective);
|
|
16744
|
+
if (startingNewGoal && (record.providerTurnId || record.status === "running")) {
|
|
16745
|
+
throw new HttpError(409, {
|
|
16746
|
+
code: "conflict",
|
|
16747
|
+
message: "Interrupt the running turn before replacing this goal."
|
|
16748
|
+
});
|
|
16749
|
+
}
|
|
16750
|
+
if (startingNewGoal && goalHistoryBeforeUpdate.length > 0) {
|
|
16751
|
+
if (!runtime.clearGoal) {
|
|
16752
|
+
throw new HttpError(409, {
|
|
16753
|
+
code: "conflict",
|
|
16754
|
+
message: "This backend cannot safely replace an existing goal."
|
|
16755
|
+
});
|
|
16756
|
+
}
|
|
16757
|
+
await runtime.clearGoal(providerSessionId);
|
|
16758
|
+
markActiveThreadGoalRecordTerminated(this.db, record.id);
|
|
16759
|
+
}
|
|
16693
16760
|
const upstreamStatus = input.status;
|
|
16694
16761
|
const goal = await runtime.setGoal({
|
|
16695
16762
|
providerSessionId,
|
|
@@ -16701,10 +16768,15 @@ var ThreadGoalCoordinator = class {
|
|
|
16701
16768
|
toThreadGoalDtoFromAgentGoal(goal),
|
|
16702
16769
|
record
|
|
16703
16770
|
);
|
|
16704
|
-
const dto =
|
|
16771
|
+
const dto = startingNewGoal ? startFreshGoalLifecycle(upstreamDto) : upstreamDto;
|
|
16705
16772
|
const persistedGoal = toThreadGoalDtoFromRecord(
|
|
16706
|
-
this.persistThreadGoalSnapshot(record.id, dto
|
|
16773
|
+
this.persistThreadGoalSnapshot(record.id, dto, {
|
|
16774
|
+
createNew: startingNewGoal
|
|
16775
|
+
})
|
|
16707
16776
|
);
|
|
16777
|
+
if (startingNewGoal) {
|
|
16778
|
+
this.callbacks.appendGoalActivityNote(record.id, persistedGoal.objective);
|
|
16779
|
+
}
|
|
16708
16780
|
this.callbacks.emitThreadEvent("thread.goal.updated", record.id, {
|
|
16709
16781
|
goal: persistedGoal,
|
|
16710
16782
|
goalHistory: this.listThreadGoalHistory(record.id)
|
|
@@ -16772,12 +16844,13 @@ var ThreadGoalCoordinator = class {
|
|
|
16772
16844
|
throw error;
|
|
16773
16845
|
}
|
|
16774
16846
|
}
|
|
16775
|
-
persistThreadGoalSnapshot(localThreadId, goal) {
|
|
16847
|
+
persistThreadGoalSnapshot(localThreadId, goal, options = {}) {
|
|
16776
16848
|
const dto = "createdAt" in goal && typeof goal.createdAt === "string" ? goal : toThreadGoalDto(goal);
|
|
16777
16849
|
return upsertThreadGoalRecord(this.db, {
|
|
16778
16850
|
threadId: localThreadId,
|
|
16779
16851
|
providerSessionId: dto.threadId,
|
|
16780
16852
|
localGoalId: dto.localGoalId ?? null,
|
|
16853
|
+
createNew: options.createNew ?? false,
|
|
16781
16854
|
objective: dto.objective,
|
|
16782
16855
|
status: dto.status,
|
|
16783
16856
|
tokenBudget: dto.tokenBudget,
|
|
@@ -16900,15 +16973,19 @@ function mergeGoalHistoryEntry(existing, incoming) {
|
|
|
16900
16973
|
function goalHistoryStatusRank(status) {
|
|
16901
16974
|
return ["active", "paused", "budgetLimited"].includes(status) ? 0 : 1;
|
|
16902
16975
|
}
|
|
16903
|
-
function
|
|
16976
|
+
function startFreshGoalLifecycle(goal) {
|
|
16977
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
16904
16978
|
return {
|
|
16905
16979
|
...goal,
|
|
16906
16980
|
tokensUsed: 0,
|
|
16907
|
-
timeUsedSeconds: 0
|
|
16981
|
+
timeUsedSeconds: 0,
|
|
16982
|
+
createdAt: now,
|
|
16983
|
+
updatedAt: now,
|
|
16984
|
+
completedAt: null
|
|
16908
16985
|
};
|
|
16909
16986
|
}
|
|
16910
|
-
function
|
|
16911
|
-
return
|
|
16987
|
+
function shouldStartNewGoal(existing, nextObjective) {
|
|
16988
|
+
return typeof nextObjective === "string" && nextObjective.trim().length > 0 && (existing === null || nextObjective !== existing.objective);
|
|
16912
16989
|
}
|
|
16913
16990
|
function isLocalGoalStatus(status) {
|
|
16914
16991
|
return ["active", "paused", "budgetLimited"].includes(status);
|
|
@@ -18109,6 +18186,14 @@ var ThreadRuntimeEventProjector = class {
|
|
|
18109
18186
|
if (!record) {
|
|
18110
18187
|
return;
|
|
18111
18188
|
}
|
|
18189
|
+
const currentGoal = await callbacks.getThreadGoalAfterRuntimeClear(record);
|
|
18190
|
+
if (currentGoal) {
|
|
18191
|
+
callbacks.emitThreadEvent("thread.goal.updated", record.id, {
|
|
18192
|
+
goal: currentGoal,
|
|
18193
|
+
goalHistory: callbacks.listThreadGoalHistory(record.id)
|
|
18194
|
+
});
|
|
18195
|
+
return;
|
|
18196
|
+
}
|
|
18112
18197
|
markActiveThreadGoalRecordTerminated(db, record.id);
|
|
18113
18198
|
callbacks.emitThreadEvent("thread.goal.cleared", record.id, {
|
|
18114
18199
|
goalHistory: callbacks.listThreadGoalHistory(record.id)
|
|
@@ -22423,7 +22508,11 @@ var ThreadService = class {
|
|
|
22423
22508
|
this.goalCoordinator = new ThreadGoalCoordinator(db, providerFeatures, {
|
|
22424
22509
|
emitThreadEvent: (type, threadId, payload) => this.emitThreadEvent(type, threadId, payload),
|
|
22425
22510
|
requireProviderSessionId: (record) => this.requireProviderSessionId(record),
|
|
22426
|
-
runtimeForProvider: (provider2) => this.runtimeForProvider(provider2)
|
|
22511
|
+
runtimeForProvider: (provider2) => this.runtimeForProvider(provider2),
|
|
22512
|
+
appendGoalActivityNote: (threadId, objective) => this.auxiliaryState.appendActivityNote(threadId, {
|
|
22513
|
+
kind: "goal",
|
|
22514
|
+
text: objective
|
|
22515
|
+
})
|
|
22427
22516
|
});
|
|
22428
22517
|
this.auxiliaryState = new ThreadAuxiliaryStateStore(db, {
|
|
22429
22518
|
cachedTurns: (localThreadId) => this.detailAssembler.cachedTurns(localThreadId),
|
|
@@ -22521,6 +22610,7 @@ var ThreadService = class {
|
|
|
22521
22610
|
resetThreadContextUsage: (localThreadId, emitEvent) => this.resetThreadContextUsage(localThreadId, emitEvent),
|
|
22522
22611
|
setThreadContextUsage: (localThreadId, usage, emitEvent) => this.setThreadContextUsage(localThreadId, usage, emitEvent),
|
|
22523
22612
|
getThreadContextUsage: (localThreadId) => this.getThreadContextUsage(localThreadId),
|
|
22613
|
+
getThreadGoalAfterRuntimeClear: (record) => this.goalCoordinator.getThreadGoalForRecord(record),
|
|
22524
22614
|
toThreadGoalDtoFromAgentGoal: (goal) => this.goalCoordinator.toThreadGoalDtoFromAgentGoal(goal),
|
|
22525
22615
|
toThreadGoalDtoFromRecord: (record) => this.goalCoordinator.toThreadGoalDtoFromRecord(record)
|
|
22526
22616
|
}
|