@getpaseo/server 0.1.89 → 0.1.90
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/dist/server/server/agent/agent-prompt.js +4 -1
- package/dist/server/server/agent/agent-sdk-types.d.ts +1 -0
- package/dist/server/server/agent/agent-storage.js +2 -9
- package/dist/server/server/agent/create-agent/create.js +10 -2
- package/dist/server/server/agent/create-agent-mode.d.ts +3 -8
- package/dist/server/server/agent/create-agent-mode.js +16 -2
- package/dist/server/server/agent/import-sessions.js +1 -1
- package/dist/server/server/agent/provider-snapshot-manager.d.ts +2 -1
- package/dist/server/server/agent/provider-snapshot-manager.js +18 -2
- package/dist/server/server/agent/providers/acp-agent.d.ts +3 -3
- package/dist/server/server/agent/providers/acp-agent.js +18 -13
- package/dist/server/server/agent/providers/codex-app-server-agent.js +16 -22
- package/dist/server/server/agent/providers/mock-load-test-agent.d.ts +2 -0
- package/dist/server/server/agent/providers/mock-load-test-agent.js +69 -2
- package/dist/server/server/agent/providers/opencode-agent.js +19 -8
- package/dist/server/server/agent/timeline-projection.js +30 -1
- package/dist/server/server/atomic-file.d.ts +3 -0
- package/dist/server/server/atomic-file.js +19 -0
- package/dist/server/server/auto-archive-on-merge/archive-if-safe.js +4 -1
- package/dist/server/server/bootstrap.js +2 -0
- package/dist/server/server/chat/chat-service.js +2 -4
- package/dist/server/server/daemon-keypair.js +2 -2
- package/dist/server/server/loop-service.d.ts +4 -0
- package/dist/server/server/loop-service.js +27 -9
- package/dist/server/server/persisted-config.js +3 -3
- package/dist/server/server/private-files.d.ts +0 -1
- package/dist/server/server/private-files.js +0 -5
- package/dist/server/server/schedule/service.d.ts +6 -0
- package/dist/server/server/schedule/service.js +41 -18
- package/dist/server/server/schedule/store.js +3 -2
- package/dist/server/server/server-id.js +3 -3
- package/dist/server/server/session.d.ts +5 -15
- package/dist/server/server/session.js +184 -107
- package/dist/server/server/speech/providers/local/worker-client.js +1 -11
- package/dist/server/server/workspace-bootstrap-dedupe.d.ts +34 -0
- package/dist/server/server/workspace-bootstrap-dedupe.js +23 -0
- package/dist/server/server/workspace-directory.d.ts +8 -0
- package/dist/server/server/workspace-directory.js +141 -15
- package/dist/server/server/workspace-registry.js +2 -6
- package/dist/server/utils/checkout-git.d.ts +0 -1
- package/dist/server/utils/checkout-git.js +23 -31
- package/dist/src/server/persisted-config.js +3 -3
- package/dist/src/server/private-files.js +0 -5
- package/package.json +9 -7
- package/dist/server/server/editor-targets.d.ts +0 -18
- package/dist/server/server/editor-targets.js +0 -109
|
@@ -120,9 +120,12 @@ export async function sendPromptToAgent(params) {
|
|
|
120
120
|
if (params.sessionMode) {
|
|
121
121
|
await params.agentManager.setAgentMode(params.agentId, params.sessionMode);
|
|
122
122
|
}
|
|
123
|
+
const runOptions = params.messageId
|
|
124
|
+
? { ...params.runOptions, messageId: params.messageId }
|
|
125
|
+
: params.runOptions;
|
|
123
126
|
return startAgentRun(params.agentManager, params.agentId, params.prompt, params.logger, {
|
|
124
127
|
replaceRunning: true,
|
|
125
|
-
runOptions
|
|
128
|
+
runOptions,
|
|
126
129
|
});
|
|
127
130
|
}
|
|
128
131
|
export async function startCreatedAgentInitialPrompt(params) {
|
|
@@ -84,6 +84,7 @@ export interface ResolveAgentCreateConfigInput {
|
|
|
84
84
|
requestedMode: string | undefined;
|
|
85
85
|
featureValues: Record<string, unknown> | undefined;
|
|
86
86
|
parent: AgentCreateConfigParent | null;
|
|
87
|
+
unattended: boolean;
|
|
87
88
|
availableModes: AgentMode[] | undefined;
|
|
88
89
|
}
|
|
89
90
|
export interface ResolveAgentCreateConfigResult {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
1
|
import { promises as fs } from "node:fs";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
import { z } from "zod";
|
|
4
|
+
import { writeJsonFileAtomic } from "../atomic-file.js";
|
|
5
5
|
import { AgentFeatureSchema, AgentStatusSchema } from "../messages.js";
|
|
6
6
|
import { toStoredAgentRecord } from "./agent-projections.js";
|
|
7
7
|
const SERIALIZABLE_CONFIG_SCHEMA = z
|
|
@@ -109,8 +109,7 @@ export class AgentStorage {
|
|
|
109
109
|
const agentId = record.id;
|
|
110
110
|
const nextPath = this.buildRecordPath(record);
|
|
111
111
|
const previousPath = this.pathById.get(agentId);
|
|
112
|
-
await
|
|
113
|
-
await writeFileAtomically(nextPath, JSON.stringify(record, null, 2));
|
|
112
|
+
await writeJsonFileAtomic(nextPath, record);
|
|
114
113
|
this.addIndexedPath(agentId, nextPath);
|
|
115
114
|
if (previousPath && previousPath !== nextPath) {
|
|
116
115
|
try {
|
|
@@ -317,10 +316,4 @@ function projectDirNameFromCwd(cwd) {
|
|
|
317
316
|
}
|
|
318
317
|
return prefix + withoutRoot.replace(/[\\/]+/g, "-");
|
|
319
318
|
}
|
|
320
|
-
async function writeFileAtomically(targetPath, payload) {
|
|
321
|
-
const directory = path.dirname(targetPath);
|
|
322
|
-
const tempPath = path.join(directory, `.agent.tmp-${process.pid}-${Date.now()}-${randomUUID()}`);
|
|
323
|
-
await fs.writeFile(tempPath, payload, "utf8");
|
|
324
|
-
await fs.rename(tempPath, targetPath);
|
|
325
|
-
}
|
|
326
319
|
//# sourceMappingURL=agent-storage.js.map
|
|
@@ -3,7 +3,7 @@ import { expandUserPath, resolvePathFromBase } from "../../path-utils.js";
|
|
|
3
3
|
import { toWorktreeRequestError } from "../../worktree-errors.js";
|
|
4
4
|
import { scheduleAgentMetadataGeneration } from "../agent-metadata-generator.js";
|
|
5
5
|
import { setupFinishNotification, startCreatedAgentInitialPrompt } from "../agent-prompt.js";
|
|
6
|
-
import { resolveClientMessageId } from "../../client-message-id.js";
|
|
6
|
+
import { normalizeClientMessageId, resolveClientMessageId } from "../../client-message-id.js";
|
|
7
7
|
import { resolveRequiredProviderModel } from "../mcp-shared.js";
|
|
8
8
|
import { appendTimelineItemIfAgentKnown, emitLiveTimelineItemIfAgentKnown, } from "../timeline-append.js";
|
|
9
9
|
export async function createAgentCommand(dependencies, input) {
|
|
@@ -46,6 +46,13 @@ async function resolveSessionCreateAgent(dependencies, input) {
|
|
|
46
46
|
});
|
|
47
47
|
const prompt = buildAgentPrompt(trimmedPrompt ?? "", input.images, input.attachments);
|
|
48
48
|
const hasPromptContent = Array.isArray(prompt) ? prompt.length > 0 : prompt.length > 0;
|
|
49
|
+
const clientMessageId = normalizeClientMessageId(input.clientMessageId);
|
|
50
|
+
const runOptions = input.outputSchema || clientMessageId
|
|
51
|
+
? {
|
|
52
|
+
...(input.outputSchema ? { outputSchema: input.outputSchema } : {}),
|
|
53
|
+
...(clientMessageId ? { messageId: clientMessageId } : {}),
|
|
54
|
+
}
|
|
55
|
+
: undefined;
|
|
49
56
|
return {
|
|
50
57
|
config: sessionConfig,
|
|
51
58
|
createOptions: {
|
|
@@ -57,7 +64,7 @@ async function resolveSessionCreateAgent(dependencies, input) {
|
|
|
57
64
|
},
|
|
58
65
|
metadataInitialPrompt: trimmedPrompt,
|
|
59
66
|
prompt: hasPromptContent ? prompt : undefined,
|
|
60
|
-
runOptions
|
|
67
|
+
runOptions,
|
|
61
68
|
explicitTitle: input.explicitTitle,
|
|
62
69
|
setupContinuation,
|
|
63
70
|
background: true,
|
|
@@ -93,6 +100,7 @@ async function resolveMcpCreateAgent(dependencies, input) {
|
|
|
93
100
|
requestedMode: input.mode,
|
|
94
101
|
featureValues: input.features,
|
|
95
102
|
parent: parentAgent,
|
|
103
|
+
unattended: false,
|
|
96
104
|
});
|
|
97
105
|
const labels = mergeLabels({
|
|
98
106
|
callerAgentId: input.callerAgentId,
|
|
@@ -1,18 +1,13 @@
|
|
|
1
|
-
import type { AgentCreateConfigUnattendedInput, AgentProvider, ResolveAgentCreateConfigInput, ResolveAgentCreateConfigResult } from "./agent-sdk-types.js";
|
|
2
|
-
interface CreateAgentModeParent {
|
|
3
|
-
provider: AgentProvider;
|
|
4
|
-
modeId: string | null;
|
|
5
|
-
isUnattended: boolean;
|
|
6
|
-
}
|
|
1
|
+
import type { AgentCreateConfigParent, AgentCreateConfigUnattendedInput, AgentProvider, ResolveAgentCreateConfigInput, ResolveAgentCreateConfigResult } from "./agent-sdk-types.js";
|
|
7
2
|
export interface ResolveCreateAgentModeInput {
|
|
8
3
|
requestedMode: string | undefined;
|
|
9
4
|
targetProvider: AgentProvider;
|
|
10
|
-
parent:
|
|
5
|
+
parent: AgentCreateConfigParent | null;
|
|
6
|
+
unattended: boolean;
|
|
11
7
|
availableModes: string[] | undefined;
|
|
12
8
|
targetUnattendedMode: string | undefined;
|
|
13
9
|
}
|
|
14
10
|
export declare function resolveAndValidateCreateAgentMode(input: ResolveCreateAgentModeInput): string | undefined;
|
|
15
11
|
export declare function resolveDefaultAgentCreateConfig(input: ResolveAgentCreateConfigInput): ResolveAgentCreateConfigResult;
|
|
16
12
|
export declare function isDefaultAgentCreateConfigUnattended(input: AgentCreateConfigUnattendedInput): boolean;
|
|
17
|
-
export {};
|
|
18
13
|
//# sourceMappingURL=create-agent-mode.d.ts.map
|
|
@@ -4,6 +4,15 @@ function listModes(modes) {
|
|
|
4
4
|
}
|
|
5
5
|
return modes.length > 0 ? modes.join(", ") : "(none)";
|
|
6
6
|
}
|
|
7
|
+
function isUnattendedCreateConfigParent(parent) {
|
|
8
|
+
return parent.isUnattended;
|
|
9
|
+
}
|
|
10
|
+
function formatCreateConfigParentMode(parent) {
|
|
11
|
+
return parent.modeId ?? "<none>";
|
|
12
|
+
}
|
|
13
|
+
function formatCreateConfigParentSource(parent) {
|
|
14
|
+
return `caller (provider '${parent.provider}')`;
|
|
15
|
+
}
|
|
7
16
|
export function resolveAndValidateCreateAgentMode(input) {
|
|
8
17
|
const { requestedMode, targetProvider, parent, availableModes } = input;
|
|
9
18
|
if (requestedMode !== undefined) {
|
|
@@ -13,15 +22,19 @@ export function resolveAndValidateCreateAgentMode(input) {
|
|
|
13
22
|
return requestedMode;
|
|
14
23
|
}
|
|
15
24
|
if (!parent) {
|
|
25
|
+
if (input.unattended && input.targetUnattendedMode !== undefined) {
|
|
26
|
+
return input.targetUnattendedMode;
|
|
27
|
+
}
|
|
16
28
|
return undefined;
|
|
17
29
|
}
|
|
18
30
|
if (parent.provider === targetProvider) {
|
|
19
31
|
return parent.modeId ?? undefined;
|
|
20
32
|
}
|
|
21
|
-
if (
|
|
33
|
+
if ((input.unattended || isUnattendedCreateConfigParent(parent)) &&
|
|
34
|
+
input.targetUnattendedMode !== undefined) {
|
|
22
35
|
return input.targetUnattendedMode;
|
|
23
36
|
}
|
|
24
|
-
throw new Error(`cannot inherit mode '${parent
|
|
37
|
+
throw new Error(`cannot inherit mode '${formatCreateConfigParentMode(parent)}' from ${formatCreateConfigParentSource(parent)} for new agent (provider '${targetProvider}'). Pass an explicit mode. Available modes for '${targetProvider}': ${listModes(availableModes)}`);
|
|
25
38
|
}
|
|
26
39
|
export function resolveDefaultAgentCreateConfig(input) {
|
|
27
40
|
const availableModeIds = input.availableModes?.map((mode) => mode.id);
|
|
@@ -30,6 +43,7 @@ export function resolveDefaultAgentCreateConfig(input) {
|
|
|
30
43
|
requestedMode: input.requestedMode,
|
|
31
44
|
targetProvider: input.provider,
|
|
32
45
|
parent: input.parent,
|
|
46
|
+
unattended: input.unattended,
|
|
33
47
|
availableModes: availableModeIds,
|
|
34
48
|
targetUnattendedMode: input.availableModes?.find(isUnattendedMode)?.id,
|
|
35
49
|
}),
|
|
@@ -37,7 +37,7 @@ export async function listImportableProviderSessions(input) {
|
|
|
37
37
|
const providerFilter = request.providers ? new Set(request.providers) : undefined;
|
|
38
38
|
const importedHandles = await collectImportedProviderSessionHandles(agentManager, agentStorage);
|
|
39
39
|
const descriptors = await agentManager.listImportablePersistedAgents({
|
|
40
|
-
limit
|
|
40
|
+
limit,
|
|
41
41
|
providerFilter,
|
|
42
42
|
cwd: request.cwd,
|
|
43
43
|
});
|
|
@@ -28,12 +28,13 @@ interface ProviderSnapshotProviderOptions {
|
|
|
28
28
|
provider: AgentProvider;
|
|
29
29
|
wait?: boolean;
|
|
30
30
|
}
|
|
31
|
-
interface ResolveProviderCreateConfigOptions {
|
|
31
|
+
export interface ResolveProviderCreateConfigOptions {
|
|
32
32
|
cwd?: string | null;
|
|
33
33
|
provider: AgentProvider;
|
|
34
34
|
requestedMode: string | undefined;
|
|
35
35
|
featureValues: Record<string, unknown> | undefined;
|
|
36
36
|
parent: ManagedAgent | null;
|
|
37
|
+
unattended: boolean;
|
|
37
38
|
}
|
|
38
39
|
export interface ResolvedProviderCreateConfig {
|
|
39
40
|
modeId: string | undefined;
|
|
@@ -175,11 +175,13 @@ export class ProviderSnapshotManager {
|
|
|
175
175
|
wait: true,
|
|
176
176
|
});
|
|
177
177
|
const definition = this.requireProvider(input.provider);
|
|
178
|
+
const parent = input.parent ? this.resolveParent(input.parent) : null;
|
|
178
179
|
return definition.resolveCreateConfig({
|
|
179
180
|
provider: input.provider,
|
|
180
181
|
requestedMode: input.requestedMode,
|
|
181
182
|
featureValues: input.featureValues,
|
|
182
|
-
parent
|
|
183
|
+
parent,
|
|
184
|
+
unattended: input.unattended || parent?.isUnattended === true,
|
|
183
185
|
availableModes: entry.modes ?? [],
|
|
184
186
|
});
|
|
185
187
|
}
|
|
@@ -227,12 +229,26 @@ export class ProviderSnapshotManager {
|
|
|
227
229
|
this.providerLoads.clear();
|
|
228
230
|
}
|
|
229
231
|
buildRegistry() {
|
|
230
|
-
|
|
232
|
+
const registry = buildProviderRegistry(this.logger, {
|
|
231
233
|
runtimeSettings: this.runtimeSettings,
|
|
232
234
|
providerOverrides: this.providerOverrides,
|
|
233
235
|
workspaceGitService: this.workspaceGitService,
|
|
234
236
|
isDev: this.isDev,
|
|
235
237
|
});
|
|
238
|
+
for (const [provider, client] of Object.entries(this.extraClients)) {
|
|
239
|
+
const definition = registry[provider];
|
|
240
|
+
if (!definition)
|
|
241
|
+
continue;
|
|
242
|
+
registry[provider] = {
|
|
243
|
+
...definition,
|
|
244
|
+
createClient: () => client,
|
|
245
|
+
resolveCreateConfig: client.resolveCreateConfig?.bind(client) ?? definition.resolveCreateConfig,
|
|
246
|
+
isCreateConfigUnattended: client.isCreateConfigUnattended?.bind(client) ?? definition.isCreateConfigUnattended,
|
|
247
|
+
fetchModels: client.listModels.bind(client),
|
|
248
|
+
fetchModes: client.listModes?.bind(client) ?? definition.fetchModes,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
return registry;
|
|
236
252
|
}
|
|
237
253
|
resolveParent(parent) {
|
|
238
254
|
const definition = this.requireProvider(parent.provider);
|
|
@@ -181,6 +181,7 @@ export declare class ACPAgentSession implements AgentSession, ACPClient {
|
|
|
181
181
|
private readonly subscribers;
|
|
182
182
|
private readonly pendingPermissions;
|
|
183
183
|
private readonly messageAssemblies;
|
|
184
|
+
private readonly submittedUserMessageIds;
|
|
184
185
|
private readonly toolCalls;
|
|
185
186
|
private readonly terminalEntries;
|
|
186
187
|
private readonly persistedHistory;
|
|
@@ -208,15 +209,13 @@ export declare class ACPAgentSession implements AgentSession, ACPClient {
|
|
|
208
209
|
private closed;
|
|
209
210
|
private historyPending;
|
|
210
211
|
private replayingHistory;
|
|
211
|
-
private suppressUserEchoMessageId;
|
|
212
|
-
private suppressUserEchoText;
|
|
213
212
|
private bootstrapThreadEventPending;
|
|
214
213
|
constructor(config: AgentSessionConfig, options: ACPAgentSessionOptions);
|
|
215
214
|
get id(): string | null;
|
|
216
215
|
initializeNewSession(): Promise<void>;
|
|
217
216
|
initializeResumedSession(): Promise<void>;
|
|
218
217
|
run(prompt: AgentPromptInput, options?: AgentRunOptions): Promise<AgentRunResult>;
|
|
219
|
-
startTurn(prompt: AgentPromptInput,
|
|
218
|
+
startTurn(prompt: AgentPromptInput, options?: AgentRunOptions): Promise<{
|
|
220
219
|
turnId: string;
|
|
221
220
|
}>;
|
|
222
221
|
subscribe(callback: (event: AgentStreamEvent) => void): () => void;
|
|
@@ -275,6 +274,7 @@ export declare class ACPAgentSession implements AgentSession, ACPClient {
|
|
|
275
274
|
private handlePromptResponse;
|
|
276
275
|
private wrapTimeline;
|
|
277
276
|
private pushEvent;
|
|
277
|
+
private emitSubmittedUserMessage;
|
|
278
278
|
private runtimeInfo;
|
|
279
279
|
private finishTurn;
|
|
280
280
|
private emitBootstrapThreadEvent;
|
|
@@ -518,6 +518,7 @@ export class ACPAgentSession {
|
|
|
518
518
|
this.subscribers = new Set();
|
|
519
519
|
this.pendingPermissions = new Map();
|
|
520
520
|
this.messageAssemblies = new Map();
|
|
521
|
+
this.submittedUserMessageIds = new Set();
|
|
521
522
|
this.toolCalls = new Map();
|
|
522
523
|
this.terminalEntries = new Map();
|
|
523
524
|
this.persistedHistory = [];
|
|
@@ -539,8 +540,6 @@ export class ACPAgentSession {
|
|
|
539
540
|
this.closed = false;
|
|
540
541
|
this.historyPending = false;
|
|
541
542
|
this.replayingHistory = false;
|
|
542
|
-
this.suppressUserEchoMessageId = null;
|
|
543
|
-
this.suppressUserEchoText = null;
|
|
544
543
|
this.bootstrapThreadEventPending = false;
|
|
545
544
|
this.provider = options.provider;
|
|
546
545
|
this.capabilities = options.capabilities;
|
|
@@ -635,7 +634,7 @@ export class ACPAgentSession {
|
|
|
635
634
|
}
|
|
636
635
|
return result;
|
|
637
636
|
}
|
|
638
|
-
async startTurn(prompt,
|
|
637
|
+
async startTurn(prompt, options) {
|
|
639
638
|
if (this.closed) {
|
|
640
639
|
throw new Error(`${this.provider} session is closed`);
|
|
641
640
|
}
|
|
@@ -646,12 +645,11 @@ export class ACPAgentSession {
|
|
|
646
645
|
throw new Error("A foreground turn is already active");
|
|
647
646
|
}
|
|
648
647
|
const turnId = randomUUID();
|
|
649
|
-
const messageId = randomUUID();
|
|
648
|
+
const messageId = options?.messageId ?? randomUUID();
|
|
650
649
|
this.activeForegroundTurnId = turnId;
|
|
651
|
-
this.suppressUserEchoMessageId = messageId;
|
|
652
|
-
this.suppressUserEchoText = extractPromptText(prompt);
|
|
653
650
|
this.emitBootstrapThreadEvent();
|
|
654
651
|
this.pushEvent({ type: "turn_started", provider: this.provider, turnId });
|
|
652
|
+
this.emitSubmittedUserMessage(prompt, messageId, turnId);
|
|
655
653
|
void this.connection
|
|
656
654
|
.prompt({
|
|
657
655
|
sessionId: this.sessionId,
|
|
@@ -1360,11 +1358,7 @@ export class ACPAgentSession {
|
|
|
1360
1358
|
if (!item) {
|
|
1361
1359
|
return [];
|
|
1362
1360
|
}
|
|
1363
|
-
|
|
1364
|
-
update.messageId === this.suppressUserEchoMessageId &&
|
|
1365
|
-
this.suppressUserEchoText &&
|
|
1366
|
-
item.text === this.suppressUserEchoText;
|
|
1367
|
-
if (shouldSuppress) {
|
|
1361
|
+
if (update.messageId && this.submittedUserMessageIds.has(update.messageId)) {
|
|
1368
1362
|
return [];
|
|
1369
1363
|
}
|
|
1370
1364
|
return [this.wrapTimeline(item)];
|
|
@@ -1533,6 +1527,19 @@ export class ACPAgentSession {
|
|
|
1533
1527
|
subscriber(event);
|
|
1534
1528
|
}
|
|
1535
1529
|
}
|
|
1530
|
+
emitSubmittedUserMessage(prompt, messageId, turnId) {
|
|
1531
|
+
const text = extractPromptText(prompt);
|
|
1532
|
+
if (text.trim().length === 0) {
|
|
1533
|
+
return;
|
|
1534
|
+
}
|
|
1535
|
+
this.submittedUserMessageIds.add(messageId);
|
|
1536
|
+
this.pushEvent({
|
|
1537
|
+
type: "timeline",
|
|
1538
|
+
provider: this.provider,
|
|
1539
|
+
turnId,
|
|
1540
|
+
item: { type: "user_message", text, messageId },
|
|
1541
|
+
});
|
|
1542
|
+
}
|
|
1536
1543
|
runtimeInfo() {
|
|
1537
1544
|
return {
|
|
1538
1545
|
provider: this.provider,
|
|
@@ -1548,8 +1555,6 @@ export class ACPAgentSession {
|
|
|
1548
1555
|
}
|
|
1549
1556
|
finishTurn(event) {
|
|
1550
1557
|
this.activeForegroundTurnId = null;
|
|
1551
|
-
this.suppressUserEchoMessageId = null;
|
|
1552
|
-
this.suppressUserEchoText = null;
|
|
1553
1558
|
this.pushEvent(event);
|
|
1554
1559
|
}
|
|
1555
1560
|
emitBootstrapThreadEvent() {
|
|
@@ -572,6 +572,10 @@ function filterCodexThreadsByCwd(threads, cwd) {
|
|
|
572
572
|
const matchesCwd = createPathEquivalenceMatcher(cwd);
|
|
573
573
|
return threads.filter((thread) => typeof thread.cwd === "string" && matchesCwd(thread.cwd));
|
|
574
574
|
}
|
|
575
|
+
function buildCodexThreadListTimeline(thread) {
|
|
576
|
+
const preview = typeof thread.preview === "string" ? thread.preview.trim() : "";
|
|
577
|
+
return preview ? [{ type: "user_message", text: preview }] : [];
|
|
578
|
+
}
|
|
575
579
|
export function toAgentUsage(tokenUsage) {
|
|
576
580
|
const usage = toObjectRecord(tokenUsage);
|
|
577
581
|
if (!usage)
|
|
@@ -4315,31 +4319,21 @@ export class CodexAppServerAgentClient {
|
|
|
4315
4319
|
await client.request("initialize", buildCodexAppServerInitializeParams());
|
|
4316
4320
|
client.notify("initialized", {});
|
|
4317
4321
|
const limit = options?.limit ?? 20;
|
|
4318
|
-
// thread/list returns the cheap `cwd` field.
|
|
4319
|
-
//
|
|
4320
|
-
//
|
|
4321
|
-
// when filtering since most threads will be from other cwds.
|
|
4322
|
+
// thread/list returns the cheap `cwd` field. Fetch a wider window when
|
|
4323
|
+
// filtering since most threads will be from other cwds, then keep the
|
|
4324
|
+
// local realpath-aware filter for symlink-equivalent workspace paths.
|
|
4322
4325
|
const listLimit = options?.cwd ? Math.max(limit, 50) : limit;
|
|
4323
|
-
const response = toObjectRecord(await client.request("thread/list", {
|
|
4326
|
+
const response = toObjectRecord(await client.request("thread/list", {
|
|
4327
|
+
limit: listLimit,
|
|
4328
|
+
...(options?.cwd ? { cwd: options.cwd } : {}),
|
|
4329
|
+
}));
|
|
4324
4330
|
const allThreads = Array.isArray(response?.data) ? response.data.filter(isRecord) : [];
|
|
4325
4331
|
const threads = filterCodexThreadsByCwd(allThreads, options?.cwd);
|
|
4326
|
-
const descriptors =
|
|
4332
|
+
const descriptors = threads.slice(0, limit).map((thread) => {
|
|
4327
4333
|
const threadId = typeof thread.id === "string" ? thread.id : "";
|
|
4328
4334
|
const cwd = typeof thread.cwd === "string" ? thread.cwd : process.cwd();
|
|
4329
|
-
const
|
|
4330
|
-
|
|
4331
|
-
try {
|
|
4332
|
-
timeline = await loadCodexThreadHistoryTimeline({
|
|
4333
|
-
threadId,
|
|
4334
|
-
cwd,
|
|
4335
|
-
requestThread: (threadIdToRead) => {
|
|
4336
|
-
return readCodexThread(client, threadIdToRead);
|
|
4337
|
-
},
|
|
4338
|
-
});
|
|
4339
|
-
}
|
|
4340
|
-
catch {
|
|
4341
|
-
timeline = [];
|
|
4342
|
-
}
|
|
4335
|
+
const preview = typeof thread.preview === "string" ? thread.preview : null;
|
|
4336
|
+
const title = typeof thread.name === "string" && thread.name.trim() ? thread.name : preview;
|
|
4343
4337
|
return {
|
|
4344
4338
|
provider: CODEX_PROVIDER,
|
|
4345
4339
|
sessionId: threadId,
|
|
@@ -4359,9 +4353,9 @@ export class CodexAppServerAgentClient {
|
|
|
4359
4353
|
threadId,
|
|
4360
4354
|
},
|
|
4361
4355
|
},
|
|
4362
|
-
timeline:
|
|
4356
|
+
timeline: buildCodexThreadListTimeline(thread),
|
|
4363
4357
|
};
|
|
4364
|
-
})
|
|
4358
|
+
});
|
|
4365
4359
|
return descriptors;
|
|
4366
4360
|
}
|
|
4367
4361
|
finally {
|
|
@@ -65,7 +65,9 @@ export declare class MockLoadTestAgentSession implements AgentSession {
|
|
|
65
65
|
private scheduleLargePayloadTurn;
|
|
66
66
|
private scheduleStressTurn;
|
|
67
67
|
private schedulePlanApprovalTurn;
|
|
68
|
+
private scheduleQuestionPromptTurn;
|
|
68
69
|
private emitPlanApprovalTurn;
|
|
70
|
+
private emitQuestionPromptTurn;
|
|
69
71
|
private emitStressTurn;
|
|
70
72
|
private emitLargePayloadTurn;
|
|
71
73
|
private tick;
|
|
@@ -62,6 +62,9 @@ const MODELS = [
|
|
|
62
62
|
function shouldEmitPlanApprovalPrompt(prompt) {
|
|
63
63
|
return /emit\s+(?:a\s+)?synthetic\s+plan\s+approval/i.test(promptToText(prompt));
|
|
64
64
|
}
|
|
65
|
+
function shouldEmitQuestionPrompt(prompt) {
|
|
66
|
+
return /emit\s+(?:a\s+)?synthetic\s+questions?/i.test(promptToText(prompt));
|
|
67
|
+
}
|
|
65
68
|
function resolveModelProfile(modelId) {
|
|
66
69
|
const model = MODELS.find((entry) => entry.id === modelId) ?? MODELS[0];
|
|
67
70
|
const metadata = model.metadata ?? {};
|
|
@@ -389,6 +392,9 @@ export class MockLoadTestAgentSession {
|
|
|
389
392
|
if (shouldEmitPlanApprovalPrompt(prompt)) {
|
|
390
393
|
this.schedulePlanApprovalTurn(turn);
|
|
391
394
|
}
|
|
395
|
+
else if (shouldEmitQuestionPrompt(prompt)) {
|
|
396
|
+
this.scheduleQuestionPromptTurn(turn);
|
|
397
|
+
}
|
|
392
398
|
else if (largePayload) {
|
|
393
399
|
this.scheduleLargePayloadTurn(turn, largePayload);
|
|
394
400
|
}
|
|
@@ -432,9 +438,11 @@ export class MockLoadTestAgentSession {
|
|
|
432
438
|
return Array.from(this.pendingPermissions.values());
|
|
433
439
|
}
|
|
434
440
|
async respondToPermission(requestId, response) {
|
|
435
|
-
|
|
441
|
+
const request = this.pendingPermissions.get(requestId);
|
|
442
|
+
if (!request) {
|
|
436
443
|
return undefined;
|
|
437
444
|
}
|
|
445
|
+
this.pendingPermissions.delete(requestId);
|
|
438
446
|
const turn = this.activeTurn;
|
|
439
447
|
this.emit({
|
|
440
448
|
type: "permission_resolved",
|
|
@@ -444,7 +452,9 @@ export class MockLoadTestAgentSession {
|
|
|
444
452
|
...(turn ? { turnId: turn.turnId } : {}),
|
|
445
453
|
});
|
|
446
454
|
if (turn) {
|
|
447
|
-
this.finishTurnWithText(turn,
|
|
455
|
+
this.finishTurnWithText(turn, request.kind === "question"
|
|
456
|
+
? "Synthetic questions resolved"
|
|
457
|
+
: "Synthetic plan approval resolved");
|
|
448
458
|
}
|
|
449
459
|
return undefined;
|
|
450
460
|
}
|
|
@@ -527,6 +537,12 @@ export class MockLoadTestAgentSession {
|
|
|
527
537
|
}, 0);
|
|
528
538
|
turn.timer.unref?.();
|
|
529
539
|
}
|
|
540
|
+
scheduleQuestionPromptTurn(turn) {
|
|
541
|
+
turn.timer = setTimeout(() => {
|
|
542
|
+
this.emitQuestionPromptTurn(turn);
|
|
543
|
+
}, 0);
|
|
544
|
+
turn.timer.unref?.();
|
|
545
|
+
}
|
|
530
546
|
emitPlanApprovalTurn(turn) {
|
|
531
547
|
if (this.activeTurn !== turn) {
|
|
532
548
|
return;
|
|
@@ -575,6 +591,57 @@ export class MockLoadTestAgentSession {
|
|
|
575
591
|
turnId: turn.turnId,
|
|
576
592
|
});
|
|
577
593
|
}
|
|
594
|
+
emitQuestionPromptTurn(turn) {
|
|
595
|
+
if (this.activeTurn !== turn) {
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
this.clearTurnTimer(turn);
|
|
599
|
+
this.emit({
|
|
600
|
+
type: "turn_started",
|
|
601
|
+
provider: this.provider,
|
|
602
|
+
turnId: turn.turnId,
|
|
603
|
+
});
|
|
604
|
+
const request = {
|
|
605
|
+
id: `mock-questions-${turn.turnId}`,
|
|
606
|
+
provider: this.provider,
|
|
607
|
+
name: "MockQuestions",
|
|
608
|
+
kind: "question",
|
|
609
|
+
title: "Questions",
|
|
610
|
+
input: {
|
|
611
|
+
questions: [
|
|
612
|
+
{
|
|
613
|
+
question: "Which surface should this apply to?",
|
|
614
|
+
header: "surface",
|
|
615
|
+
options: [{ label: "App" }, { label: "Desktop" }],
|
|
616
|
+
multiSelect: false,
|
|
617
|
+
},
|
|
618
|
+
{
|
|
619
|
+
question: "Which rollout should we use?",
|
|
620
|
+
header: "rollout",
|
|
621
|
+
options: [{ label: "Immediately" }, { label: "Behind feature flag" }],
|
|
622
|
+
multiSelect: false,
|
|
623
|
+
},
|
|
624
|
+
{
|
|
625
|
+
question: "What success criteria should we use?",
|
|
626
|
+
header: "success",
|
|
627
|
+
options: [],
|
|
628
|
+
multiSelect: false,
|
|
629
|
+
placeholder: "Describe success...",
|
|
630
|
+
},
|
|
631
|
+
],
|
|
632
|
+
},
|
|
633
|
+
metadata: {
|
|
634
|
+
source: "mock_questions",
|
|
635
|
+
},
|
|
636
|
+
};
|
|
637
|
+
this.pendingPermissions.set(request.id, request);
|
|
638
|
+
this.emit({
|
|
639
|
+
type: "permission_requested",
|
|
640
|
+
provider: this.provider,
|
|
641
|
+
request,
|
|
642
|
+
turnId: turn.turnId,
|
|
643
|
+
});
|
|
644
|
+
}
|
|
578
645
|
emitStressTurn(turn, stress) {
|
|
579
646
|
if (this.activeTurn !== turn) {
|
|
580
647
|
return;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
2
|
import { createPathEquivalenceMatcher } from "../../../utils/path.js";
|
|
3
|
+
import pLimit from "p-limit";
|
|
3
4
|
import { z } from "zod";
|
|
4
5
|
import { getAgentStreamEventTurnId, } from "../agent-sdk-types.js";
|
|
5
6
|
import { isDefaultAgentCreateConfigUnattended, resolveDefaultAgentCreateConfig, } from "../create-agent-mode.js";
|
|
@@ -57,10 +58,17 @@ function withOpenCodeAutoAcceptFeature(featureValues, enabled) {
|
|
|
57
58
|
}
|
|
58
59
|
function resolveOpenCodeCreateConfig(input) {
|
|
59
60
|
const legacyFullAccess = input.requestedMode === OPENCODE_LEGACY_FULL_ACCESS_MODE_ID;
|
|
60
|
-
const
|
|
61
|
-
const
|
|
61
|
+
const parent = input.parent;
|
|
62
|
+
const isUnattendedCreate = input.unattended || parent?.isUnattended === true;
|
|
63
|
+
const inheritsUnattended = input.requestedMode === undefined && isUnattendedCreate;
|
|
64
|
+
const inheritedOpenCodeMode = inheritsUnattended && parent?.provider === input.provider
|
|
65
|
+
? (parent.modeId ?? undefined)
|
|
66
|
+
: undefined;
|
|
67
|
+
const requestedMode = legacyFullAccess
|
|
68
|
+
? OPENCODE_BUILD_MODE_ID
|
|
69
|
+
: (input.requestedMode ?? inheritedOpenCodeMode);
|
|
62
70
|
const featureValues = legacyFullAccess ||
|
|
63
|
-
(
|
|
71
|
+
(isUnattendedCreate && input.featureValues?.[OPENCODE_AUTO_ACCEPT_FEATURE_ID] === undefined)
|
|
64
72
|
? withOpenCodeAutoAcceptFeature(input.featureValues, true)
|
|
65
73
|
: input.featureValues;
|
|
66
74
|
if (inheritsUnattended && requestedMode === undefined) {
|
|
@@ -124,6 +132,8 @@ function resolveOpenCodePermissionReply(response) {
|
|
|
124
132
|
}
|
|
125
133
|
const MCP_ALREADY_PRESENT_ERROR_TOKENS = ["already", "exists", "connected"];
|
|
126
134
|
const OPENCODE_PROVIDER_LIST_TIMEOUT_MS = 30000;
|
|
135
|
+
const OPENCODE_METADATA_CONCURRENCY = 4;
|
|
136
|
+
const openCodeMetadataLimit = pLimit(OPENCODE_METADATA_CONCURRENCY);
|
|
127
137
|
const OPENCODE_HANDLED_BUILTIN_SLASH_COMMANDS = [
|
|
128
138
|
{ name: "compact", description: "Compact the current session", argumentHint: "" },
|
|
129
139
|
{ name: "summarize", description: "Compact the current session", argumentHint: "" },
|
|
@@ -918,7 +928,7 @@ export class OpenCodeAgentClient {
|
|
|
918
928
|
try {
|
|
919
929
|
// Background model discovery can be legitimately slow while OpenCode refreshes
|
|
920
930
|
// provider state, so allow longer than turn execution paths.
|
|
921
|
-
const response = await withTimeout(client.provider.list({ directory: options.cwd }), OPENCODE_PROVIDER_LIST_TIMEOUT_MS, `OpenCode provider.list timed out after ${OPENCODE_PROVIDER_LIST_TIMEOUT_MS / 1000}s - server may not be authenticated or connected to any providers`);
|
|
931
|
+
const response = await openCodeMetadataLimit(() => withTimeout(client.provider.list({ directory: options.cwd }), OPENCODE_PROVIDER_LIST_TIMEOUT_MS, `OpenCode provider.list timed out after ${OPENCODE_PROVIDER_LIST_TIMEOUT_MS / 1000}s - server may not be authenticated or connected to any providers`));
|
|
922
932
|
if (response.error) {
|
|
923
933
|
throw new Error(`Failed to fetch OpenCode providers: ${JSON.stringify(response.error)}`);
|
|
924
934
|
}
|
|
@@ -964,7 +974,7 @@ export class OpenCodeAgentClient {
|
|
|
964
974
|
const directory = options.cwd;
|
|
965
975
|
const client = this.runtime.createClient({ baseUrl: url, directory });
|
|
966
976
|
try {
|
|
967
|
-
const response = await withTimeout(client.app.agents({ directory }), 10000, "OpenCode app.agents timed out after 10s");
|
|
977
|
+
const response = await openCodeMetadataLimit(() => withTimeout(client.app.agents({ directory }), 10000, "OpenCode app.agents timed out after 10s"));
|
|
968
978
|
if (response.error || !response.data) {
|
|
969
979
|
return DEFAULT_MODES;
|
|
970
980
|
}
|
|
@@ -1102,7 +1112,7 @@ export class OpenCodeAgentClient {
|
|
|
1102
1112
|
return normalizeOpenCodeConfig({ ...config, provider: "opencode" });
|
|
1103
1113
|
}
|
|
1104
1114
|
async populateModelContextWindowCache(client, cwd) {
|
|
1105
|
-
const response = await client.provider.list({ directory: cwd });
|
|
1115
|
+
const response = await openCodeMetadataLimit(() => client.provider.list({ directory: cwd }));
|
|
1106
1116
|
if (response.error || !response.data) {
|
|
1107
1117
|
return;
|
|
1108
1118
|
}
|
|
@@ -1794,6 +1804,7 @@ function appendOpenCodeQuestionAsked(event, state, events) {
|
|
|
1794
1804
|
header: q.header,
|
|
1795
1805
|
options,
|
|
1796
1806
|
...(q.multiple === true ? { multiSelect: true } : {}),
|
|
1807
|
+
allowOther: true,
|
|
1797
1808
|
},
|
|
1798
1809
|
];
|
|
1799
1810
|
});
|
|
@@ -2467,9 +2478,9 @@ class OpenCodeAgentSession {
|
|
|
2467
2478
|
if (this.availableModesCache) {
|
|
2468
2479
|
return this.availableModesCache;
|
|
2469
2480
|
}
|
|
2470
|
-
const response = await this.client.app.agents({
|
|
2481
|
+
const response = await openCodeMetadataLimit(() => this.client.app.agents({
|
|
2471
2482
|
directory: this.config.cwd,
|
|
2472
|
-
});
|
|
2483
|
+
}));
|
|
2473
2484
|
const agents = response.error || !response.data ? [] : response.data;
|
|
2474
2485
|
const discoveredModes = agents.filter(isSelectableOpenCodeAgent).map(mapOpenCodeAgentToMode);
|
|
2475
2486
|
this.availableModesCache = mergeOpenCodeModes(discoveredModes);
|
|
@@ -270,7 +270,36 @@ export function selectProjectedTimelinePage(input) {
|
|
|
270
270
|
const limit = input.limit === undefined ? 0 : Math.max(0, Math.floor(input.limit));
|
|
271
271
|
const bounds = input.bounds ?? getTimelineBounds(input.rows);
|
|
272
272
|
const projectedAll = projectTimelineRows({ rows: input.rows, mode: "projected" });
|
|
273
|
-
if (
|
|
273
|
+
if (!bounds) {
|
|
274
|
+
return {
|
|
275
|
+
entries: [],
|
|
276
|
+
startSeq: null,
|
|
277
|
+
endSeq: null,
|
|
278
|
+
hasOlder: false,
|
|
279
|
+
hasNewer: false,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
if (projectedAll.length === 0) {
|
|
283
|
+
if (input.direction === "after") {
|
|
284
|
+
const cursorSeq = input.cursorSeq ?? bounds.minSeq - 1;
|
|
285
|
+
return {
|
|
286
|
+
entries: [],
|
|
287
|
+
startSeq: null,
|
|
288
|
+
endSeq: null,
|
|
289
|
+
hasOlder: cursorSeq >= bounds.minSeq,
|
|
290
|
+
hasNewer: cursorSeq < bounds.maxSeq,
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
if (input.direction === "before") {
|
|
294
|
+
const cursorSeq = input.cursorSeq ?? bounds.maxSeq + 1;
|
|
295
|
+
return {
|
|
296
|
+
entries: [],
|
|
297
|
+
startSeq: null,
|
|
298
|
+
endSeq: null,
|
|
299
|
+
hasOlder: cursorSeq > bounds.minSeq,
|
|
300
|
+
hasNewer: cursorSeq <= bounds.maxSeq,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
274
303
|
return {
|
|
275
304
|
entries: [],
|
|
276
305
|
startSeq: null,
|