@getpaseo/server 0.1.91-beta.2 → 0.1.91
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/scripts/supervisor.js +21 -0
- package/dist/server/server/agent/agent-manager.d.ts +1 -1
- package/dist/server/server/agent/agent-manager.js +23 -48
- package/dist/server/server/agent/prompt-attachments.js +8 -0
- package/dist/server/server/agent/provider-registry.d.ts +0 -1
- package/dist/server/server/agent/provider-registry.js +21 -3
- package/dist/server/server/agent/providers/codex-app-server-agent.js +9 -5
- package/dist/server/server/agent/providers/opencode/test-utils/test-opencode-runtime.d.ts +1 -0
- package/dist/server/server/agent/providers/opencode/test-utils/test-opencode-runtime.js +4 -0
- package/dist/server/server/agent/providers/opencode-agent.d.ts +16 -2
- package/dist/server/server/agent/providers/opencode-agent.js +75 -4
- package/dist/server/server/agent/providers/pi/agent.d.ts +23 -1
- package/dist/server/server/agent/providers/pi/agent.js +219 -13
- package/dist/server/server/agent/providers/pi/cli-runtime.js +9 -0
- package/dist/server/server/agent/providers/pi/rpc-types.d.ts +9 -0
- package/dist/server/server/agent/providers/pi/runtime.d.ts +2 -0
- package/dist/server/server/agent/providers/pi/session-descriptor.d.ts +12 -0
- package/dist/server/server/agent/providers/pi/session-descriptor.js +304 -0
- package/dist/server/server/agent/providers/pi/test-utils/fake-pi.d.ts +8 -0
- package/dist/server/server/agent/providers/pi/test-utils/fake-pi.js +22 -0
- package/dist/server/server/agent/runtime-mcp-config.d.ts +8 -0
- package/dist/server/server/agent/runtime-mcp-config.js +50 -0
- package/dist/server/server/auto-archive-on-merge/archive-if-safe.js +2 -2
- package/dist/server/server/daemon-worker.js +84 -1
- package/dist/server/server/file-upload/index.d.ts +27 -0
- package/dist/server/server/file-upload/index.js +158 -0
- package/dist/server/server/loop-service.d.ts +12 -12
- package/dist/server/server/persisted-config.d.ts +8 -0
- package/dist/server/server/persisted-config.js +1 -1
- package/dist/server/server/persistence-hooks.js +6 -4
- package/dist/server/server/session.d.ts +5 -2
- package/dist/server/server/session.js +20 -2
- package/dist/server/server/speech/providers/local/runtime.js +1 -0
- package/dist/server/server/speech/providers/local/worker-client.d.ts +14 -1
- package/dist/server/server/speech/providers/local/worker-client.js +169 -7
- package/dist/server/server/websocket-server.d.ts +2 -0
- package/dist/server/server/websocket-server.js +20 -7
- package/dist/server/server/workspace-registry.d.ts +4 -4
- package/dist/src/server/persisted-config.js +1 -1
- package/package.json +5 -5
|
@@ -127,6 +127,26 @@ export function runSupervisor(options) {
|
|
|
127
127
|
execArgv: workerExecArgv,
|
|
128
128
|
});
|
|
129
129
|
}
|
|
130
|
+
const currentChild = child;
|
|
131
|
+
const heartbeat = setInterval(() => {
|
|
132
|
+
const message = { type: "paseo:supervisor-heartbeat" };
|
|
133
|
+
if (currentChild.connected) {
|
|
134
|
+
currentChild.send?.(message, (error) => {
|
|
135
|
+
if (error) {
|
|
136
|
+
writeLifecycleLog("Worker heartbeat IPC send failed", {
|
|
137
|
+
error: error instanceof Error ? error.message : String(error),
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
writeLifecycleLog("Worker heartbeat skipped because IPC channel is disconnected");
|
|
144
|
+
}
|
|
145
|
+
}, 1000);
|
|
146
|
+
heartbeat.unref();
|
|
147
|
+
child.on("disconnect", () => {
|
|
148
|
+
writeLifecycleLog("Worker IPC channel disconnected");
|
|
149
|
+
});
|
|
130
150
|
child.stdout?.on("data", (chunk) => {
|
|
131
151
|
process.stdout.write(chunk);
|
|
132
152
|
writeDurableChunk(chunk);
|
|
@@ -157,6 +177,7 @@ export function runSupervisor(options) {
|
|
|
157
177
|
requestRestart("Restart requested by worker");
|
|
158
178
|
});
|
|
159
179
|
child.on("close", (code, signal) => {
|
|
180
|
+
clearInterval(heartbeat);
|
|
160
181
|
const exitDescriptor = describeExit(code, signal);
|
|
161
182
|
writeLifecycleLog("Worker exited", { code, signal, exit: exitDescriptor });
|
|
162
183
|
if (shuttingDown) {
|
|
@@ -163,7 +163,6 @@ export interface AgentMetricsSnapshot {
|
|
|
163
163
|
export declare class AgentManager {
|
|
164
164
|
private readonly clients;
|
|
165
165
|
private readonly providerEnabled;
|
|
166
|
-
private readonly providerDerivedFromId;
|
|
167
166
|
private readonly agents;
|
|
168
167
|
private readonly timelineStore;
|
|
169
168
|
private readonly agentsAwaitingInitialSnapshotPersist;
|
|
@@ -337,6 +336,7 @@ export declare class AgentManager {
|
|
|
337
336
|
private dispatchStream;
|
|
338
337
|
private dispatch;
|
|
339
338
|
private normalizeConfig;
|
|
339
|
+
private prepareSessionConfig;
|
|
340
340
|
private applyDaemonAppendSystemPrompt;
|
|
341
341
|
private buildLaunchContext;
|
|
342
342
|
private requireAvailableClient;
|
|
@@ -10,9 +10,9 @@ import { InMemoryAgentTimelineStore, } from "./agent-timeline-store.js";
|
|
|
10
10
|
import { AGENT_STREAM_COALESCE_DEFAULT_WINDOW_MS, AgentStreamCoalescer, } from "./agent-stream-coalescer.js";
|
|
11
11
|
import { ForegroundRunState } from "./foreground-run-state.js";
|
|
12
12
|
import { getAgentProviderDefinition } from "@getpaseo/protocol/provider-manifest";
|
|
13
|
-
import { IMPORTABLE_PROVIDERS } from "./provider-registry.js";
|
|
14
13
|
import { invokeRewindCapability } from "./rewind/rewind.js";
|
|
15
14
|
import { isSystemInjectedEnvelope } from "./agent-prompt.js";
|
|
15
|
+
import { stripInternalPaseoMcpServer, withRuntimePaseoMcpServer } from "./runtime-mcp-config.js";
|
|
16
16
|
const RELOAD_SESSION_CLOSE_TIMEOUT_MS = 3000;
|
|
17
17
|
const INTERRUPT_SESSION_TIMEOUT_MS = 2000;
|
|
18
18
|
const STORED_AGENT_CAPABILITIES = {
|
|
@@ -54,7 +54,7 @@ function buildStoredAgentConfig(record) {
|
|
|
54
54
|
}
|
|
55
55
|
if (record.config.mcpServers != null)
|
|
56
56
|
config.mcpServers = record.config.mcpServers;
|
|
57
|
-
return config;
|
|
57
|
+
return stripInternalPaseoMcpServer(config);
|
|
58
58
|
}
|
|
59
59
|
export { AGENT_LIFECYCLE_STATUSES };
|
|
60
60
|
function resolveInitialAttention(input) {
|
|
@@ -126,7 +126,6 @@ export class AgentManager {
|
|
|
126
126
|
constructor(options) {
|
|
127
127
|
this.clients = new Map();
|
|
128
128
|
this.providerEnabled = new Map();
|
|
129
|
-
this.providerDerivedFromId = new Map();
|
|
130
129
|
this.agents = new Map();
|
|
131
130
|
this.timelineStore = new InMemoryAgentTimelineStore();
|
|
132
131
|
this.agentsAwaitingInitialSnapshotPersist = new Set();
|
|
@@ -166,7 +165,6 @@ export class AgentManager {
|
|
|
166
165
|
for (const [provider, definition] of Object.entries(input.providerDefinitions)) {
|
|
167
166
|
if (definition) {
|
|
168
167
|
this.providerEnabled.set(provider, definition.enabled);
|
|
169
|
-
this.providerDerivedFromId.set(provider, definition.derivedFromProviderId ?? null);
|
|
170
168
|
}
|
|
171
169
|
}
|
|
172
170
|
for (const [provider, client] of Object.entries(input.clients)) {
|
|
@@ -303,15 +301,9 @@ export class AgentManager {
|
|
|
303
301
|
.slice(0, limit);
|
|
304
302
|
}
|
|
305
303
|
isProviderImportable(provider, providerFilter) {
|
|
306
|
-
if (!IMPORTABLE_PROVIDERS.includes(provider)) {
|
|
307
|
-
return false;
|
|
308
|
-
}
|
|
309
304
|
if (this.providerEnabled.get(provider) === false) {
|
|
310
305
|
return false;
|
|
311
306
|
}
|
|
312
|
-
if (this.providerDerivedFromId.get(provider) != null) {
|
|
313
|
-
return false;
|
|
314
|
-
}
|
|
315
307
|
if (providerFilter && !providerFilter.has(provider)) {
|
|
316
308
|
return false;
|
|
317
309
|
}
|
|
@@ -427,27 +419,15 @@ export class AgentManager {
|
|
|
427
419
|
}
|
|
428
420
|
async createAgent(config, agentId, options) {
|
|
429
421
|
const resolvedAgentId = validateAgentId(agentId ?? this.idFactory(), "createAgent");
|
|
430
|
-
const
|
|
431
|
-
|
|
432
|
-
: {
|
|
433
|
-
...config,
|
|
434
|
-
mcpServers: {
|
|
435
|
-
paseo: {
|
|
436
|
-
type: "http",
|
|
437
|
-
url: `${this.mcpBaseUrl}?callerAgentId=${resolvedAgentId}`,
|
|
438
|
-
},
|
|
439
|
-
...config.mcpServers,
|
|
440
|
-
},
|
|
441
|
-
};
|
|
442
|
-
this.requireEnabledProvider(injectedConfig.provider);
|
|
443
|
-
const normalizedConfig = this.applyDaemonAppendSystemPrompt(await this.normalizeConfig(injectedConfig));
|
|
422
|
+
const { storedConfig, launchConfig } = await this.prepareSessionConfig(config, resolvedAgentId);
|
|
423
|
+
this.requireEnabledProvider(storedConfig.provider);
|
|
444
424
|
const launchContext = this.buildLaunchContext(resolvedAgentId, options?.env);
|
|
445
425
|
const client = await this.requireAvailableClient({
|
|
446
|
-
provider:
|
|
426
|
+
provider: storedConfig.provider,
|
|
447
427
|
});
|
|
448
428
|
const createOptions = this.buildCreateSessionOptions(options);
|
|
449
|
-
const session = await client.createSession(
|
|
450
|
-
return this.registerSession(session,
|
|
429
|
+
const session = await client.createSession(launchConfig, launchContext, createOptions);
|
|
430
|
+
return this.registerSession(session, storedConfig, resolvedAgentId, {
|
|
451
431
|
labels: options?.labels,
|
|
452
432
|
workspaceId: options?.workspaceId,
|
|
453
433
|
initialTitle: options?.initialTitle,
|
|
@@ -468,29 +448,15 @@ export class AgentManager {
|
|
|
468
448
|
...overrides,
|
|
469
449
|
provider: handle.provider,
|
|
470
450
|
};
|
|
471
|
-
const
|
|
472
|
-
const resumeOverrides = { ...overrides };
|
|
473
|
-
let hasResumeOverrides = overrides !== undefined;
|
|
474
|
-
if (normalizedConfig.model !== mergedConfig.model) {
|
|
475
|
-
resumeOverrides.model = normalizedConfig.model;
|
|
476
|
-
hasResumeOverrides = true;
|
|
477
|
-
}
|
|
478
|
-
if (normalizedConfig.modeId !== mergedConfig.modeId) {
|
|
479
|
-
resumeOverrides.modeId = normalizedConfig.modeId;
|
|
480
|
-
hasResumeOverrides = true;
|
|
481
|
-
}
|
|
482
|
-
if (metadata.daemonAppendSystemPrompt !== normalizedConfig.daemonAppendSystemPrompt) {
|
|
483
|
-
resumeOverrides.daemonAppendSystemPrompt = normalizedConfig.daemonAppendSystemPrompt;
|
|
484
|
-
hasResumeOverrides = true;
|
|
485
|
-
}
|
|
451
|
+
const { storedConfig, launchConfig } = await this.prepareSessionConfig(mergedConfig, resolvedAgentId);
|
|
486
452
|
const launchContext = this.buildLaunchContext(resolvedAgentId);
|
|
487
453
|
const client = this.requireClient(handle.provider);
|
|
488
454
|
const available = await client.isAvailable();
|
|
489
455
|
if (!available) {
|
|
490
456
|
throw new Error(`Provider '${handle.provider}' is not available. Please ensure the CLI is installed.`);
|
|
491
457
|
}
|
|
492
|
-
const session = await client.resumeSession(handle,
|
|
493
|
-
return this.registerSession(session,
|
|
458
|
+
const session = await client.resumeSession(handle, launchConfig, launchContext);
|
|
459
|
+
return this.registerSession(session, storedConfig, resolvedAgentId, options);
|
|
494
460
|
}
|
|
495
461
|
// Hot-reload an active agent session with config overrides. By default the
|
|
496
462
|
// in-memory timeline is preserved (used for voice-mode toggles and similar
|
|
@@ -517,11 +483,11 @@ export class AgentManager {
|
|
|
517
483
|
...overrides,
|
|
518
484
|
provider,
|
|
519
485
|
};
|
|
520
|
-
const
|
|
486
|
+
const { storedConfig, launchConfig } = await this.prepareSessionConfig(refreshConfig, agentId);
|
|
521
487
|
const launchContext = this.buildLaunchContext(agentId);
|
|
522
488
|
const session = handle
|
|
523
|
-
? await client.resumeSession(handle,
|
|
524
|
-
: await client.createSession(
|
|
489
|
+
? await client.resumeSession(handle, launchConfig, launchContext)
|
|
490
|
+
: await client.createSession(launchConfig, launchContext);
|
|
525
491
|
this.agentStreamCoalescer.flushAndDiscard(agentId);
|
|
526
492
|
// Remove the existing agent entry before swapping sessions
|
|
527
493
|
this.agents.delete(agentId);
|
|
@@ -539,7 +505,7 @@ export class AgentManager {
|
|
|
539
505
|
this.timelineStore.delete(agentId);
|
|
540
506
|
}
|
|
541
507
|
// Preserve existing labels and timeline during reload.
|
|
542
|
-
return this.registerSession(session,
|
|
508
|
+
return this.registerSession(session, storedConfig, agentId, {
|
|
543
509
|
labels: existing.labels,
|
|
544
510
|
createdAt: existing.createdAt,
|
|
545
511
|
updatedAt: existing.updatedAt,
|
|
@@ -2503,6 +2469,15 @@ export class AgentManager {
|
|
|
2503
2469
|
}
|
|
2504
2470
|
return normalized;
|
|
2505
2471
|
}
|
|
2472
|
+
async prepareSessionConfig(config, agentId) {
|
|
2473
|
+
const storedConfig = await this.normalizeConfig(stripInternalPaseoMcpServer(config));
|
|
2474
|
+
const launchConfig = this.applyDaemonAppendSystemPrompt(withRuntimePaseoMcpServer({
|
|
2475
|
+
config: storedConfig,
|
|
2476
|
+
agentId,
|
|
2477
|
+
mcpBaseUrl: this.mcpBaseUrl,
|
|
2478
|
+
}));
|
|
2479
|
+
return { storedConfig, launchConfig };
|
|
2480
|
+
}
|
|
2506
2481
|
applyDaemonAppendSystemPrompt(config) {
|
|
2507
2482
|
const daemonAppendSystemPrompt = this.appendSystemPrompt.trim();
|
|
2508
2483
|
const next = { ...config };
|
|
@@ -45,6 +45,14 @@ export function renderPromptAttachmentAsText(attachment) {
|
|
|
45
45
|
});
|
|
46
46
|
return lines.join("\n");
|
|
47
47
|
}
|
|
48
|
+
case "uploaded_file": {
|
|
49
|
+
return [
|
|
50
|
+
`Uploaded file: ${attachment.fileName}`,
|
|
51
|
+
`Path: ${attachment.path}`,
|
|
52
|
+
`MIME: ${attachment.mimeType}`,
|
|
53
|
+
`Size: ${attachment.size} bytes`,
|
|
54
|
+
].join("\n");
|
|
55
|
+
}
|
|
48
56
|
default:
|
|
49
57
|
throw new Error("unreachable");
|
|
50
58
|
}
|
|
@@ -19,7 +19,6 @@ export interface ProviderDefinition extends AgentProviderDefinition {
|
|
|
19
19
|
fetchModels: (options: ListModelsOptions) => Promise<AgentModelDefinition[]>;
|
|
20
20
|
fetchModes: (options: ListModesOptions) => Promise<AgentMode[]>;
|
|
21
21
|
}
|
|
22
|
-
export { IMPORTABLE_PROVIDERS } from "@getpaseo/protocol/importable-providers";
|
|
23
22
|
export interface BuildProviderRegistryOptions {
|
|
24
23
|
runtimeSettings?: AgentProviderRuntimeSettingsMap;
|
|
25
24
|
providerOverrides?: Record<string, ProviderOverride>;
|
|
@@ -14,7 +14,6 @@ function isNonEmptyStringArray(value) {
|
|
|
14
14
|
return value.length > 0;
|
|
15
15
|
}
|
|
16
16
|
export { AGENT_PROVIDER_DEFINITIONS, getAgentProviderDefinition };
|
|
17
|
-
export { IMPORTABLE_PROVIDERS } from "@getpaseo/protocol/importable-providers";
|
|
18
17
|
const PROVIDER_CLIENT_FACTORIES = {
|
|
19
18
|
claude: (logger, runtimeSettings) => new ClaudeAgentClient({
|
|
20
19
|
logger,
|
|
@@ -34,9 +33,22 @@ const PROVIDER_CLIENT_FACTORIES = {
|
|
|
34
33
|
env: runtimeSettings?.env,
|
|
35
34
|
}),
|
|
36
35
|
opencode: (logger, runtimeSettings) => new OpenCodeAgentClient(logger, runtimeSettings),
|
|
37
|
-
pi: (logger, runtimeSettings) => new PiRpcAgentClient({
|
|
36
|
+
pi: (logger, runtimeSettings, options) => new PiRpcAgentClient({
|
|
38
37
|
logger,
|
|
39
38
|
runtimeSettings,
|
|
39
|
+
providerParams: options?.providerParams,
|
|
40
|
+
}),
|
|
41
|
+
omp: (logger, runtimeSettings, options) => new PiRpcAgentClient({
|
|
42
|
+
logger,
|
|
43
|
+
runtimeSettings: mergeRuntimeSettings({
|
|
44
|
+
command: {
|
|
45
|
+
mode: "replace",
|
|
46
|
+
argv: ["omp"],
|
|
47
|
+
},
|
|
48
|
+
}, runtimeSettings),
|
|
49
|
+
providerParams: options?.providerParams ?? {
|
|
50
|
+
sessionDir: "~/.omp/agent/sessions",
|
|
51
|
+
},
|
|
40
52
|
}),
|
|
41
53
|
mock: (logger) => new MockLoadTestAgentClient(logger),
|
|
42
54
|
"mock-slow": () => new MockSlowProviderClient(),
|
|
@@ -293,10 +305,12 @@ function buildResolvedBuiltinProviders(providerOverrides, runtimeSettings, optio
|
|
|
293
305
|
profileModels: override?.models ?? [],
|
|
294
306
|
additionalModels: override?.additionalModels ?? [],
|
|
295
307
|
profileModelsAreAdditive: false,
|
|
296
|
-
enabled: override?.enabled
|
|
308
|
+
enabled: override?.enabled ?? definition.enabledByDefault ?? true,
|
|
297
309
|
derivedFromProviderId: null,
|
|
310
|
+
providerParams: override?.params,
|
|
298
311
|
createBaseClient: (logger) => factory(logger, mergedRuntimeSettings, {
|
|
299
312
|
workspaceGitService: options.workspaceGitService,
|
|
313
|
+
providerParams: override?.params,
|
|
300
314
|
}),
|
|
301
315
|
});
|
|
302
316
|
}
|
|
@@ -330,6 +344,7 @@ function addDerivedProviders(resolvedProviders, providerOverrides) {
|
|
|
330
344
|
profileModelsAreAdditive: false,
|
|
331
345
|
enabled: override.enabled !== false,
|
|
332
346
|
derivedFromProviderId: null,
|
|
347
|
+
providerParams: override.params,
|
|
333
348
|
createBaseClient: (logger) => providerId === "cursor"
|
|
334
349
|
? new CursorACPAgentClient({
|
|
335
350
|
logger,
|
|
@@ -356,6 +371,7 @@ function addDerivedProviders(resolvedProviders, providerOverrides) {
|
|
|
356
371
|
const mergedRuntimeSettings = mergeRuntimeSettings(baseProvider.runtimeSettings, toRuntimeSettings(override));
|
|
357
372
|
const baseDefinition = baseProvider.definition;
|
|
358
373
|
const baseFactory = getProviderClientFactory(baseProviderId);
|
|
374
|
+
const providerParams = override.params ?? baseProvider.providerParams;
|
|
359
375
|
resolvedProviders.set(providerId, {
|
|
360
376
|
definition: createDerivedDefinition(providerId, baseDefinition, override),
|
|
361
377
|
runtimeSettings: mergedRuntimeSettings,
|
|
@@ -364,7 +380,9 @@ function addDerivedProviders(resolvedProviders, providerOverrides) {
|
|
|
364
380
|
profileModelsAreAdditive: false,
|
|
365
381
|
enabled: override.enabled !== false,
|
|
366
382
|
derivedFromProviderId: baseProviderId,
|
|
383
|
+
providerParams,
|
|
367
384
|
createBaseClient: (logger) => baseFactory(logger, mergedRuntimeSettings, {
|
|
385
|
+
providerParams,
|
|
368
386
|
customProvider: {
|
|
369
387
|
id: providerId,
|
|
370
388
|
label: override.label ?? providerId,
|
|
@@ -34,6 +34,14 @@ const TURN_START_TIMEOUT_MS = 90 * 1000;
|
|
|
34
34
|
const INTERRUPT_TIMEOUT_MS = 2000;
|
|
35
35
|
const CODEX_PROVIDER = "codex";
|
|
36
36
|
const CODEX_IMAGE_ATTACHMENT_DIR = "paseo-attachments";
|
|
37
|
+
// Codex treats most app-server client names as the model-request originator.
|
|
38
|
+
// This reserved Codex name is non-originating, so requests keep Codex's default
|
|
39
|
+
// CLI identity instead of showing up as Paseo in provider usage logs.
|
|
40
|
+
const CODEX_NON_ORIGINATING_APP_SERVER_CLIENT_INFO = {
|
|
41
|
+
name: "codex_app_server_daemon",
|
|
42
|
+
title: "Codex App Server Daemon",
|
|
43
|
+
version: "0.0.0",
|
|
44
|
+
};
|
|
37
45
|
const ASSISTANT_MESSAGE_BOUNDARY_MARKDOWN = "\n\n---\n\n";
|
|
38
46
|
const CODEX_TOOL_THREAD_ITEM_TYPES = new Set([
|
|
39
47
|
"commandExecution",
|
|
@@ -2093,11 +2101,7 @@ export function buildCodexAppServerEnv(runtimeSettings, launchEnv) {
|
|
|
2093
2101
|
}
|
|
2094
2102
|
function buildCodexAppServerInitializeParams() {
|
|
2095
2103
|
return {
|
|
2096
|
-
clientInfo:
|
|
2097
|
-
name: "paseo",
|
|
2098
|
-
title: "Paseo",
|
|
2099
|
-
version: "0.0.0",
|
|
2100
|
-
},
|
|
2104
|
+
clientInfo: CODEX_NON_ORIGINATING_APP_SERVER_CLIENT_INFO,
|
|
2101
2105
|
capabilities: {
|
|
2102
2106
|
experimentalApi: true,
|
|
2103
2107
|
},
|
|
@@ -76,6 +76,7 @@ export declare class TestOpenCodeClient {
|
|
|
76
76
|
sessionMessagesResponse: OpenCodeResponse;
|
|
77
77
|
sessionPromptAsyncEvents: unknown[];
|
|
78
78
|
sessionPromptAsyncResponse: OpenCodeResponse;
|
|
79
|
+
sessionSummarizeEvents: unknown[];
|
|
79
80
|
sessionSummarizeResponse: OpenCodeResponse;
|
|
80
81
|
sessionUpdateResponse: OpenCodeResponse;
|
|
81
82
|
private readonly queuedEventStream;
|
|
@@ -70,6 +70,7 @@ export class TestOpenCodeClient {
|
|
|
70
70
|
this.sessionMessagesResponse = { data: [] };
|
|
71
71
|
this.sessionPromptAsyncEvents = [idleEvent()];
|
|
72
72
|
this.sessionPromptAsyncResponse = {};
|
|
73
|
+
this.sessionSummarizeEvents = [idleEvent()];
|
|
73
74
|
this.sessionSummarizeResponse = { data: {} };
|
|
74
75
|
this.sessionUpdateResponse = {};
|
|
75
76
|
this.queuedEventStream = createQueuedEventStream();
|
|
@@ -182,6 +183,9 @@ export class TestOpenCodeClient {
|
|
|
182
183
|
},
|
|
183
184
|
summarize: async (parameters) => {
|
|
184
185
|
this.calls.sessionSummarize.push(parameters);
|
|
186
|
+
for (const event of this.sessionSummarizeEvents) {
|
|
187
|
+
this.emitEvent(event);
|
|
188
|
+
}
|
|
185
189
|
return this.sessionSummarizeResponse;
|
|
186
190
|
},
|
|
187
191
|
update: async (parameters) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type AssistantMessage as OpenCodeAssistantMessage, type Event as OpenCodeEvent, type FilePartInput as OpenCodeFilePartInput, type OpencodeClient, type TextPartInput as OpenCodeTextPartInput } from "@opencode-ai/sdk/v2/client";
|
|
1
|
+
import { type AssistantMessage as OpenCodeAssistantMessage, type Event as OpenCodeEvent, type FilePartInput as OpenCodeFilePartInput, type Message as OpenCodeMessage, type OpencodeClient, type Part as OpenCodePart, type TextPartInput as OpenCodeTextPartInput } from "@opencode-ai/sdk/v2/client";
|
|
2
2
|
import type { Logger } from "pino";
|
|
3
|
-
import { type AgentCapabilityFlags, type AgentClient, type AgentCreateSessionOptions, type AgentFeature, type AgentLaunchContext, type AgentMode, type AgentModelDefinition, type AgentPermissionRequest, type AgentPermissionResponse, type AgentPersistenceHandle, type AgentPromptInput, type AgentRunOptions, type AgentRunResult, type AgentRuntimeInfo, type AgentSession, type AgentSessionConfig, type AgentSlashCommand, type AgentStreamEvent, type AgentUsage, type ResolveAgentCreateConfigInput, type ResolveAgentCreateConfigResult, type ListModelsOptions, type ListModesOptions, type ListPersistedAgentsOptions, type PersistedAgentDescriptor, type ToolCallTimelineItem } from "../agent-sdk-types.js";
|
|
3
|
+
import { type AgentCapabilityFlags, type AgentClient, type AgentCreateSessionOptions, type AgentFeature, type AgentLaunchContext, type AgentMode, type AgentModelDefinition, type AgentPermissionRequest, type AgentPermissionResponse, type AgentPersistenceHandle, type AgentPromptInput, type AgentRunOptions, type AgentRunResult, type AgentRuntimeInfo, type AgentSession, type AgentSessionConfig, type AgentSlashCommand, type AgentStreamEvent, type AgentTimelineItem, type AgentUsage, type ResolveAgentCreateConfigInput, type ResolveAgentCreateConfigResult, type ListModelsOptions, type ListModesOptions, type ListPersistedAgentsOptions, type PersistedAgentDescriptor, type ToolCallTimelineItem } from "../agent-sdk-types.js";
|
|
4
4
|
import { isDefaultAgentCreateConfigUnattended } from "../create-agent-mode.js";
|
|
5
5
|
import { type ProviderRuntimeSettings } from "../provider-launch-config.js";
|
|
6
6
|
import { type OpenCodeRuntime } from "./opencode/runtime.js";
|
|
@@ -10,6 +10,10 @@ type OpenCodeAgentConfig = AgentSessionConfig & {
|
|
|
10
10
|
provider: "opencode";
|
|
11
11
|
};
|
|
12
12
|
type OpenCodeMessageRole = "user" | "assistant";
|
|
13
|
+
interface OpenCodeSessionMessage {
|
|
14
|
+
info: OpenCodeMessage;
|
|
15
|
+
parts: OpenCodePart[];
|
|
16
|
+
}
|
|
13
17
|
declare function reconcileOpenCodeSessionClose(params: {
|
|
14
18
|
client: Pick<OpencodeClient, "session">;
|
|
15
19
|
sessionId: string;
|
|
@@ -79,8 +83,10 @@ declare function mergeOpenCodeStepFinishUsage(usage: AgentUsage, part: {
|
|
|
79
83
|
}): void;
|
|
80
84
|
declare function hasNormalizedOpenCodeUsage(usage: AgentUsage): boolean;
|
|
81
85
|
declare function buildOpenCodePromptParts(prompt: AgentPromptInput): Array<OpenCodeTextPartInput | OpenCodeFilePartInput>;
|
|
86
|
+
declare function buildOpenCodeSessionTimeline(messages: ReadonlyArray<OpenCodeSessionMessage>): AgentTimelineItem[];
|
|
82
87
|
export declare const __openCodeInternals: {
|
|
83
88
|
buildOpenCodePromptParts: typeof buildOpenCodePromptParts;
|
|
89
|
+
buildOpenCodeSessionTimeline: typeof buildOpenCodeSessionTimeline;
|
|
84
90
|
buildOpenCodeModelContextWindowLookup: typeof buildOpenCodeModelContextWindowLookup;
|
|
85
91
|
buildOpenCodeModelDefinition: typeof buildOpenCodeModelDefinition;
|
|
86
92
|
buildOpenCodeModelLookupKey: typeof buildOpenCodeModelLookupKey;
|
|
@@ -133,6 +139,11 @@ export interface OpenCodeEventTranslationState {
|
|
|
133
139
|
sessionTotalCostUsd?: number;
|
|
134
140
|
streamedPartKeys: Set<string>;
|
|
135
141
|
emittedStructuredMessageIds: Set<string>;
|
|
142
|
+
compactionSummaryMessageIds: Set<string>;
|
|
143
|
+
emittedCompactionPartIds: Set<string>;
|
|
144
|
+
suppressAssistantMessagesUntilIdle?: {
|
|
145
|
+
active: boolean;
|
|
146
|
+
};
|
|
136
147
|
/** Tracks the type of each part by ID, learned from message.part.updated events. */
|
|
137
148
|
partTypes: Map<string, string>;
|
|
138
149
|
subAgentsByCallId?: Map<string, OpenCodeSubAgentActivityState>;
|
|
@@ -186,6 +197,9 @@ declare class OpenCodeAgentSession implements AgentSession {
|
|
|
186
197
|
private streamedPartKeys;
|
|
187
198
|
/** Tracks assistant messages already emitted from structured payloads. */
|
|
188
199
|
private emittedStructuredMessageIds;
|
|
200
|
+
private compactionSummaryMessageIds;
|
|
201
|
+
private emittedCompactionPartIds;
|
|
202
|
+
private suppressAssistantMessagesUntilIdle;
|
|
189
203
|
/** Tracks the type of each part by ID, learned from message.part.updated events. */
|
|
190
204
|
private partTypes;
|
|
191
205
|
private availableModesCache;
|
|
@@ -728,6 +728,13 @@ function buildOpenCodeReplayPartTimelineEvent(params) {
|
|
|
728
728
|
part,
|
|
729
729
|
});
|
|
730
730
|
}
|
|
731
|
+
function isOpenCodeCompactionSummaryMessage(message) {
|
|
732
|
+
return (message.role === "assistant" &&
|
|
733
|
+
(message.summary === true || message.agent === "compaction" || message.mode === "compaction"));
|
|
734
|
+
}
|
|
735
|
+
function findOpenCodeCompactionPart(message) {
|
|
736
|
+
return message.parts.find((part) => part.type === "compaction");
|
|
737
|
+
}
|
|
731
738
|
async function readOpenCodeSessionMessagesFromSdk(client, session) {
|
|
732
739
|
const response = await client.session.messages({
|
|
733
740
|
sessionID: session.id,
|
|
@@ -739,7 +746,24 @@ async function readOpenCodeSessionMessagesFromSdk(client, session) {
|
|
|
739
746
|
return filterOpenCodeRevertedMessages(response.data, session.revert);
|
|
740
747
|
}
|
|
741
748
|
function buildOpenCodeSessionTimeline(messages) {
|
|
742
|
-
|
|
749
|
+
const timeline = [];
|
|
750
|
+
let hideNextAssistantAfterCompaction = false;
|
|
751
|
+
for (const message of messages) {
|
|
752
|
+
const compactionPart = findOpenCodeCompactionPart(message);
|
|
753
|
+
if (message.info.role === "assistant" && hideNextAssistantAfterCompaction) {
|
|
754
|
+
hideNextAssistantAfterCompaction = false;
|
|
755
|
+
continue;
|
|
756
|
+
}
|
|
757
|
+
if (message.info.role === "user" && !compactionPart) {
|
|
758
|
+
hideNextAssistantAfterCompaction = false;
|
|
759
|
+
}
|
|
760
|
+
timeline.push(...buildOpenCodeReplayTimelineEvents(message).map((event) => event.item));
|
|
761
|
+
if (message.info.role === "user" && compactionPart) {
|
|
762
|
+
timeline.push(createCompactionTimelineItem("completed", compactionPart.auto ? "auto" : "manual"));
|
|
763
|
+
hideNextAssistantAfterCompaction = true;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
return timeline;
|
|
743
767
|
}
|
|
744
768
|
function filterOpenCodeRevertedMessages(messages, revert) {
|
|
745
769
|
if (!revert?.messageID || revert.partID) {
|
|
@@ -778,6 +802,9 @@ function readOpenCodeMessageModel(message) {
|
|
|
778
802
|
}
|
|
779
803
|
function buildOpenCodeReplayTimelineEvents(message) {
|
|
780
804
|
const { info, parts } = message;
|
|
805
|
+
if (isOpenCodeCompactionSummaryMessage(info)) {
|
|
806
|
+
return [];
|
|
807
|
+
}
|
|
781
808
|
if (info.role === "user") {
|
|
782
809
|
const text = parts
|
|
783
810
|
.filter((part) => part.type === "text")
|
|
@@ -816,6 +843,7 @@ function buildOpenCodeReplayTimelineEvents(message) {
|
|
|
816
843
|
}
|
|
817
844
|
export const __openCodeInternals = {
|
|
818
845
|
buildOpenCodePromptParts,
|
|
846
|
+
buildOpenCodeSessionTimeline,
|
|
819
847
|
buildOpenCodeModelContextWindowLookup,
|
|
820
848
|
buildOpenCodeModelDefinition,
|
|
821
849
|
buildOpenCodeModelLookupKey,
|
|
@@ -1370,6 +1398,11 @@ export function translateOpenCodeEvent(event, state) {
|
|
|
1370
1398
|
function resetOpenCodeTurnTrackingState(state) {
|
|
1371
1399
|
state.streamedPartKeys.clear();
|
|
1372
1400
|
state.partTypes.clear();
|
|
1401
|
+
state.compactionSummaryMessageIds.clear();
|
|
1402
|
+
state.emittedCompactionPartIds.clear();
|
|
1403
|
+
if (state.suppressAssistantMessagesUntilIdle) {
|
|
1404
|
+
state.suppressAssistantMessagesUntilIdle.active = false;
|
|
1405
|
+
}
|
|
1373
1406
|
}
|
|
1374
1407
|
function getOpenCodeSubAgentMaps(state) {
|
|
1375
1408
|
state.subAgentsByCallId ?? (state.subAgentsByCallId = new Map());
|
|
@@ -1592,6 +1625,14 @@ function appendOpenCodeMessageUpdated(event, state, events) {
|
|
|
1592
1625
|
if (info.role !== "assistant") {
|
|
1593
1626
|
return;
|
|
1594
1627
|
}
|
|
1628
|
+
if (state.suppressAssistantMessagesUntilIdle?.active) {
|
|
1629
|
+
state.compactionSummaryMessageIds.add(info.id);
|
|
1630
|
+
return;
|
|
1631
|
+
}
|
|
1632
|
+
if (isOpenCodeCompactionSummaryMessage(info)) {
|
|
1633
|
+
state.compactionSummaryMessageIds.add(info.id);
|
|
1634
|
+
return;
|
|
1635
|
+
}
|
|
1595
1636
|
const modelLookupKey = resolveOpenCodeModelLookupKeyFromAssistantMessage(info);
|
|
1596
1637
|
if (modelLookupKey) {
|
|
1597
1638
|
const contextWindowMaxTokens = state.modelContextWindowsByModelKey?.get(modelLookupKey);
|
|
@@ -1638,6 +1679,13 @@ function appendOpenCodeMessagePartUpdated(event, state, events) {
|
|
|
1638
1679
|
}
|
|
1639
1680
|
const messageRole = state.messageRoles.get(part.messageID);
|
|
1640
1681
|
state.partTypes.set(part.id, part.type);
|
|
1682
|
+
if (state.compactionSummaryMessageIds.has(part.messageID)) {
|
|
1683
|
+
return;
|
|
1684
|
+
}
|
|
1685
|
+
if (shouldSuppressOpenCodeAssistantPart(part, messageRole, state)) {
|
|
1686
|
+
state.compactionSummaryMessageIds.add(part.messageID);
|
|
1687
|
+
return;
|
|
1688
|
+
}
|
|
1641
1689
|
if (part.type === "text") {
|
|
1642
1690
|
appendOpenCodeTextPart(part, messageRole, state, events);
|
|
1643
1691
|
return;
|
|
@@ -1654,6 +1702,10 @@ function appendOpenCodeMessagePartUpdated(event, state, events) {
|
|
|
1654
1702
|
return;
|
|
1655
1703
|
}
|
|
1656
1704
|
if (part.type === "compaction") {
|
|
1705
|
+
if (state.emittedCompactionPartIds.has(part.id)) {
|
|
1706
|
+
return;
|
|
1707
|
+
}
|
|
1708
|
+
state.emittedCompactionPartIds.add(part.id);
|
|
1657
1709
|
events.push({
|
|
1658
1710
|
type: "timeline",
|
|
1659
1711
|
provider: "opencode",
|
|
@@ -1678,6 +1730,11 @@ function appendOpenCodeMessagePartUpdated(event, state, events) {
|
|
|
1678
1730
|
}
|
|
1679
1731
|
}
|
|
1680
1732
|
}
|
|
1733
|
+
function shouldSuppressOpenCodeAssistantPart(part, messageRole, state) {
|
|
1734
|
+
return (state.suppressAssistantMessagesUntilIdle?.active === true &&
|
|
1735
|
+
part.type === "text" &&
|
|
1736
|
+
messageRole !== "user");
|
|
1737
|
+
}
|
|
1681
1738
|
function appendOpenCodeTextPart(part, messageRole, state, events) {
|
|
1682
1739
|
if (messageRole === "user") {
|
|
1683
1740
|
return;
|
|
@@ -1724,6 +1781,9 @@ function appendOpenCodeMessagePartDelta(event, state, events) {
|
|
|
1724
1781
|
const messageRole = messageID ? state.messageRoles.get(messageID) : undefined;
|
|
1725
1782
|
const knownPartType = partID ? state.partTypes.get(partID) : undefined;
|
|
1726
1783
|
const isReasoning = knownPartType === "reasoning" || field === "reasoning";
|
|
1784
|
+
if (messageID && state.compactionSummaryMessageIds.has(messageID)) {
|
|
1785
|
+
return;
|
|
1786
|
+
}
|
|
1727
1787
|
if (isReasoning) {
|
|
1728
1788
|
if (partID) {
|
|
1729
1789
|
state.streamedPartKeys.add(`reasoning:${partID}`);
|
|
@@ -1741,6 +1801,10 @@ function appendOpenCodeMessagePartDelta(event, state, events) {
|
|
|
1741
1801
|
if (messageRole === "user") {
|
|
1742
1802
|
return;
|
|
1743
1803
|
}
|
|
1804
|
+
if (messageID && state.suppressAssistantMessagesUntilIdle?.active === true) {
|
|
1805
|
+
state.compactionSummaryMessageIds.add(messageID);
|
|
1806
|
+
return;
|
|
1807
|
+
}
|
|
1744
1808
|
if (partID) {
|
|
1745
1809
|
state.streamedPartKeys.add(`text:${partID}`);
|
|
1746
1810
|
}
|
|
@@ -1938,6 +2002,9 @@ class OpenCodeAgentSession {
|
|
|
1938
2002
|
this.streamedPartKeys = new Set();
|
|
1939
2003
|
/** Tracks assistant messages already emitted from structured payloads. */
|
|
1940
2004
|
this.emittedStructuredMessageIds = new Set();
|
|
2005
|
+
this.compactionSummaryMessageIds = new Set();
|
|
2006
|
+
this.emittedCompactionPartIds = new Set();
|
|
2007
|
+
this.suppressAssistantMessagesUntilIdle = { active: false };
|
|
1941
2008
|
/** Tracks the type of each part by ID, learned from message.part.updated events. */
|
|
1942
2009
|
this.partTypes = new Map();
|
|
1943
2010
|
this.availableModesCache = null;
|
|
@@ -2069,6 +2136,7 @@ class OpenCodeAgentSession {
|
|
|
2069
2136
|
this.accumulatedUsage = contextWindowMaxTokens !== undefined ? { contextWindowMaxTokens } : {};
|
|
2070
2137
|
const parts = buildOpenCodePromptParts(prompt);
|
|
2071
2138
|
this.pendingUserMessageText = buildOpenCodeUserTimelineText(prompt);
|
|
2139
|
+
this.suppressAssistantMessagesUntilIdle.active = false;
|
|
2072
2140
|
const model = this.parseModel(this.config.model);
|
|
2073
2141
|
const thinkingOptionId = this.config.thinkingOptionId;
|
|
2074
2142
|
const effectiveVariant = thinkingOptionId ?? undefined;
|
|
@@ -2088,6 +2156,7 @@ class OpenCodeAgentSession {
|
|
|
2088
2156
|
const slashCommand = await this.resolveSlashCommandInvocation(prompt);
|
|
2089
2157
|
if (slashCommand) {
|
|
2090
2158
|
if (slashCommand.commandName === "compact" || slashCommand.commandName === "summarize") {
|
|
2159
|
+
this.suppressAssistantMessagesUntilIdle.active = true;
|
|
2091
2160
|
void this.client.session
|
|
2092
2161
|
.summarize({
|
|
2093
2162
|
sessionID: this.sessionId,
|
|
@@ -2096,18 +2165,17 @@ class OpenCodeAgentSession {
|
|
|
2096
2165
|
})
|
|
2097
2166
|
.then((response) => {
|
|
2098
2167
|
if (response.error) {
|
|
2168
|
+
this.suppressAssistantMessagesUntilIdle.active = false;
|
|
2099
2169
|
this.finishForegroundTurn({
|
|
2100
2170
|
type: "turn_failed",
|
|
2101
2171
|
provider: "opencode",
|
|
2102
2172
|
error: toDiagnosticErrorMessage(response.error),
|
|
2103
2173
|
}, turnId);
|
|
2104
2174
|
}
|
|
2105
|
-
else {
|
|
2106
|
-
this.finishForegroundTurn({ type: "turn_completed", provider: "opencode", usage: undefined }, turnId);
|
|
2107
|
-
}
|
|
2108
2175
|
return;
|
|
2109
2176
|
})
|
|
2110
2177
|
.catch((error) => {
|
|
2178
|
+
this.suppressAssistantMessagesUntilIdle.active = false;
|
|
2111
2179
|
this.finishForegroundTurn({
|
|
2112
2180
|
type: "turn_failed",
|
|
2113
2181
|
provider: "opencode",
|
|
@@ -2710,6 +2778,9 @@ class OpenCodeAgentSession {
|
|
|
2710
2778
|
sessionTotalCostUsd: this.sessionTotalCostUsd,
|
|
2711
2779
|
streamedPartKeys: this.streamedPartKeys,
|
|
2712
2780
|
emittedStructuredMessageIds: this.emittedStructuredMessageIds,
|
|
2781
|
+
compactionSummaryMessageIds: this.compactionSummaryMessageIds,
|
|
2782
|
+
emittedCompactionPartIds: this.emittedCompactionPartIds,
|
|
2783
|
+
suppressAssistantMessagesUntilIdle: this.suppressAssistantMessagesUntilIdle,
|
|
2713
2784
|
partTypes: this.partTypes,
|
|
2714
2785
|
subAgentsByCallId: this.subAgentsByCallId,
|
|
2715
2786
|
subAgentCallIdByChildSessionId: this.subAgentCallIdByChildSessionId,
|