@getpaseo/server 0.1.97 → 0.1.98
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-manager.d.ts +11 -3
- package/dist/server/server/agent/agent-manager.js +94 -22
- package/dist/server/server/agent/agent-prompt.d.ts +1 -1
- package/dist/server/server/agent/agent-prompt.js +3 -10
- package/dist/server/server/agent/agent-sdk-types.d.ts +9 -3
- package/dist/server/server/agent/create-agent/create.d.ts +2 -0
- package/dist/server/server/agent/create-agent/create.js +8 -7
- package/dist/server/server/agent/lifecycle-command.d.ts +15 -1
- package/dist/server/server/agent/lifecycle-command.js +9 -2
- package/dist/server/server/agent/mcp-server.js +254 -115
- package/dist/server/server/agent/provider-notices.d.ts +3 -0
- package/dist/server/server/agent/provider-notices.js +5 -0
- package/dist/server/server/agent/provider-registry.d.ts +2 -0
- package/dist/server/server/agent/provider-registry.js +10 -3
- package/dist/server/server/agent/provider-snapshot-manager.d.ts +3 -0
- package/dist/server/server/agent/provider-snapshot-manager.js +11 -2
- package/dist/server/server/agent/providers/claude/agent.js +257 -143
- package/dist/server/server/agent/providers/claude/models.js +7 -3
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +4 -3
- package/dist/server/server/agent/providers/codex-app-server-agent.js +43 -1
- package/dist/server/server/agent/providers/copilot-acp-agent.js +4 -1
- package/dist/server/server/agent/providers/diagnostic-utils.d.ts +9 -0
- package/dist/server/server/agent/providers/diagnostic-utils.js +188 -0
- package/dist/server/server/agent/providers/mock-slow-provider.js +1 -1
- package/dist/server/server/agent/providers/opencode/server-manager.d.ts +29 -2
- package/dist/server/server/agent/providers/opencode/server-manager.js +83 -17
- package/dist/server/server/agent/providers/opencode-agent.d.ts +2 -0
- package/dist/server/server/agent/providers/opencode-agent.js +14 -9
- package/dist/server/server/agent/providers/pi/agent.js +27 -14
- package/dist/server/server/bootstrap.d.ts +2 -0
- package/dist/server/server/bootstrap.js +32 -2
- package/dist/server/server/managed-processes/managed-processes.d.ts +76 -0
- package/dist/server/server/managed-processes/managed-processes.js +326 -0
- package/dist/server/server/resolve-worktree-creation-intent.d.ts +3 -0
- package/dist/server/server/resolve-worktree-creation-intent.js +3 -3
- package/dist/server/server/session.d.ts +12 -1
- package/dist/server/server/session.js +230 -40
- package/dist/server/server/speech/providers/openai/runtime.js +3 -4
- package/dist/server/server/websocket-server.d.ts +1 -0
- package/dist/server/server/websocket-server.js +11 -0
- package/dist/server/server/workspace-archive-service.js +2 -3
- package/dist/server/server/workspace-directory.js +5 -5
- package/dist/server/server/workspace-reconciliation-service.js +2 -2
- package/dist/server/server/worktree-core.d.ts +1 -0
- package/dist/server/server/worktree-core.js +5 -1
- package/dist/server/services/quota-fetcher/manifest.d.ts +4 -0
- package/dist/server/services/quota-fetcher/manifest.js +47 -0
- package/dist/server/services/quota-fetcher/provider.d.ts +17 -0
- package/dist/server/services/quota-fetcher/provider.js +2 -0
- package/dist/server/services/quota-fetcher/providers/claude.d.ts +26 -0
- package/dist/server/services/quota-fetcher/providers/claude.js +217 -0
- package/dist/server/services/quota-fetcher/providers/codex.d.ts +23 -0
- package/dist/server/services/quota-fetcher/providers/codex.js +211 -0
- package/dist/server/services/quota-fetcher/providers/copilot.d.ts +17 -0
- package/dist/server/services/quota-fetcher/providers/copilot.js +75 -0
- package/dist/server/services/quota-fetcher/providers/cursor.d.ts +17 -0
- package/dist/server/services/quota-fetcher/providers/cursor.js +123 -0
- package/dist/server/services/quota-fetcher/providers/grok.d.ts +18 -0
- package/dist/server/services/quota-fetcher/providers/grok.js +89 -0
- package/dist/server/services/quota-fetcher/providers/kimi.d.ts +20 -0
- package/dist/server/services/quota-fetcher/providers/kimi.js +89 -0
- package/dist/server/services/quota-fetcher/providers/zai.d.ts +17 -0
- package/dist/server/services/quota-fetcher/providers/zai.js +58 -0
- package/dist/server/services/quota-fetcher/service.d.ts +28 -0
- package/dist/server/services/quota-fetcher/service.js +58 -0
- package/dist/server/services/quota-fetcher/usage.d.ts +22 -0
- package/dist/server/services/quota-fetcher/usage.js +49 -0
- package/dist/server/utils/directory-suggestions.js +98 -2
- package/package.json +5 -5
|
@@ -25,6 +25,7 @@ import { FileBackedChatService } from "./chat/chat-service.js";
|
|
|
25
25
|
import { LoopService } from "./loop-service.js";
|
|
26
26
|
import { ScheduleService } from "./schedule/service.js";
|
|
27
27
|
import { type GitHubService } from "../services/github-service.js";
|
|
28
|
+
import type { ProviderUsageService } from "../services/quota-fetcher/service.js";
|
|
28
29
|
import { generateBranchNameFromFirstAgentContext } from "./worktree-branch-name-generator.js";
|
|
29
30
|
export declare function resolveWaitForFinishError(options: {
|
|
30
31
|
status: "permission" | "error" | "idle";
|
|
@@ -74,6 +75,7 @@ export interface SessionOptions {
|
|
|
74
75
|
tts: Resolvable<TextToSpeechProvider | null>;
|
|
75
76
|
terminalManager: TerminalManager | null;
|
|
76
77
|
providerSnapshotManager: ProviderSnapshotManager;
|
|
78
|
+
providerUsageService: ProviderUsageService;
|
|
77
79
|
serviceProxy?: ServiceProxySubsystem;
|
|
78
80
|
scriptRuntimeStore?: WorkspaceScriptRuntimeStore;
|
|
79
81
|
workspaceSetupSnapshots?: Map<string, WorkspaceSetupSnapshot>;
|
|
@@ -180,6 +182,7 @@ export declare class Session {
|
|
|
180
182
|
private clientActivity;
|
|
181
183
|
private readonly terminalManager;
|
|
182
184
|
private readonly providerSnapshotManager;
|
|
185
|
+
private readonly providerUsageService;
|
|
183
186
|
private unsubscribeProviderSnapshotEvents;
|
|
184
187
|
private readonly serviceProxy;
|
|
185
188
|
private readonly scriptRuntimeStore;
|
|
@@ -273,6 +276,7 @@ export declare class Session {
|
|
|
273
276
|
private matchesAgentFilter;
|
|
274
277
|
private getAgentUpdateTargetId;
|
|
275
278
|
private bufferOrEmitAgentUpdate;
|
|
279
|
+
private emitStoredAgentUpdate;
|
|
276
280
|
private flushBootstrappedAgentUpdates;
|
|
277
281
|
private findExactWorkspaceByDirectory;
|
|
278
282
|
private resolveWorkspaceDirectory;
|
|
@@ -287,6 +291,7 @@ export declare class Session {
|
|
|
287
291
|
private dispatchInboundMessage;
|
|
288
292
|
private dispatchVoiceAndControlMessage;
|
|
289
293
|
private dispatchAgentRewindMessage;
|
|
294
|
+
private dispatchAgentRelationshipMessage;
|
|
290
295
|
private handleDictationStreamStart;
|
|
291
296
|
private dispatchAgentLifecycleMessage;
|
|
292
297
|
private dispatchAgentConfigMessage;
|
|
@@ -309,6 +314,7 @@ export declare class Session {
|
|
|
309
314
|
private handleDeleteAgentRequest;
|
|
310
315
|
private handleArchiveAgentRequest;
|
|
311
316
|
private archiveAgentForClose;
|
|
317
|
+
private handleDetachAgentRequest;
|
|
312
318
|
private handleCloseItemsRequest;
|
|
313
319
|
private unarchiveAgentByHandle;
|
|
314
320
|
private handleUpdateAgentRequest;
|
|
@@ -363,6 +369,7 @@ export declare class Session {
|
|
|
363
369
|
private handleGetProvidersSnapshotRequest;
|
|
364
370
|
private handleRefreshProvidersSnapshotRequest;
|
|
365
371
|
private handleProviderDiagnosticRequest;
|
|
372
|
+
private handleProviderUsageListRequest;
|
|
366
373
|
private assertSafeGitRef;
|
|
367
374
|
private isPathWithinRoot;
|
|
368
375
|
private generateCommitMessage;
|
|
@@ -462,6 +469,7 @@ export declare class Session {
|
|
|
462
469
|
private listAgentPayloads;
|
|
463
470
|
private resolveAgentIdentifier;
|
|
464
471
|
private getAgentPayloadById;
|
|
472
|
+
private resolveDelegationRootWorkspaceId;
|
|
465
473
|
private buildActiveProjectPlacementsByWorkspaceId;
|
|
466
474
|
private collectFetchAgentsEntries;
|
|
467
475
|
private listFetchAgentsEntries;
|
|
@@ -484,6 +492,8 @@ export declare class Session {
|
|
|
484
492
|
private findOrCreateWorkspaceForDirectory;
|
|
485
493
|
private resolveOrCreateWorkspaceIdForCreateAgent;
|
|
486
494
|
private createWorkspaceForDirectory;
|
|
495
|
+
private findOrCreateProjectForDirectory;
|
|
496
|
+
private buildProjectDescriptor;
|
|
487
497
|
private reclassifyOrUnarchiveWorkspaceForDirectory;
|
|
488
498
|
private resolveProjectRecordForPlacement;
|
|
489
499
|
private unarchiveOwningWorkspaceForAgent;
|
|
@@ -498,7 +508,7 @@ export declare class Session {
|
|
|
498
508
|
private emitWorkspaceUpdatesForWorkspaceIds;
|
|
499
509
|
private recordWorkspaceGitDescriptorState;
|
|
500
510
|
private buildWorkspaceRemoveUpdatePayload;
|
|
501
|
-
private
|
|
511
|
+
private resolveProjectWithoutActiveWorkspacesForArchivedWorkspace;
|
|
502
512
|
private emitWorkspaceUpdateForTerminalContribution;
|
|
503
513
|
private emitWorkspaceUpdateForCwd;
|
|
504
514
|
private handleFetchAgents;
|
|
@@ -513,6 +523,7 @@ export declare class Session {
|
|
|
513
523
|
private handleWorkspaceCreateWorktree;
|
|
514
524
|
private resolveWorktreeSourceCwd;
|
|
515
525
|
private handleOpenProjectRequest;
|
|
526
|
+
private handleProjectAddRequest;
|
|
516
527
|
private buildWorkspaceScriptPayloadSnapshot;
|
|
517
528
|
private resolveWorkspaceScriptGitMetadata;
|
|
518
529
|
private emitWorkspaceScriptStatusUpdate;
|
|
@@ -32,9 +32,10 @@ import { deriveProjectSlug } from "./workspace-git-metadata.js";
|
|
|
32
32
|
import { spawnWorkspaceScript } from "./worktree-bootstrap.js";
|
|
33
33
|
import { getErrorMessage, getErrorMessageOr } from "@getpaseo/protocol/error-utils";
|
|
34
34
|
import { getAgentStatusPriority } from "@getpaseo/protocol/agent-state-bucket";
|
|
35
|
+
import { getParentAgentIdFromLabels } from "@getpaseo/protocol/agent-labels";
|
|
35
36
|
import { resolveSnapshotCwd } from "./agent/provider-snapshot-manager.js";
|
|
36
37
|
import { createAgentCommand } from "./agent/create-agent/create.js";
|
|
37
|
-
import { archiveAgentCommand, cancelAgentRunCommand, closeAgentCommand, setAgentModeCommand, updateAgentCommand, } from "./agent/lifecycle-command.js";
|
|
38
|
+
import { archiveAgentCommand, cancelAgentRunCommand, closeAgentCommand, detachAgentCommand, setAgentModeCommand, updateAgentCommand, } from "./agent/lifecycle-command.js";
|
|
38
39
|
import { buildStoredAgentPayload, resolveEffectiveThinkingOptionId, resolveStoredAgentPayloadUpdatedAt, toAgentPayload, } from "./agent/agent-projections.js";
|
|
39
40
|
import { appendTimelineItemIfAgentKnown, emitLiveTimelineItemIfAgentKnown, } from "./agent/timeline-append.js";
|
|
40
41
|
import { projectTimelineRows, selectProjectedTimelinePage, } from "./agent/timeline-projection.js";
|
|
@@ -342,7 +343,7 @@ export class Session {
|
|
|
342
343
|
}
|
|
343
344
|
},
|
|
344
345
|
});
|
|
345
|
-
const { clientId, appVersion, clientCapabilities, onMessage, onBinaryMessage, getTransportBufferedAmount, onLifecycleIntent, logger, downloadTokenStore, pushTokenStore, paseoHome, worktreesRoot, agentManager, agentStorage, projectRegistry, workspaceRegistry, filesystem, chatService, scheduleService, loopService, checkoutDiffManager, github, renameCurrentBranch, generateWorkspaceName, workspaceGitService, daemonConfigStore, mcpBaseUrl, stt, sttLanguage, tts, terminalManager, providerSnapshotManager, serviceProxy, scriptRuntimeStore, workspaceSetupSnapshots, onBranchChanged, getDaemonTcpPort, getDaemonTcpHost, serviceProxyPublicBaseUrl, resolveScriptHealth, voice, voiceBridge, dictation, serverId, daemonVersion, daemonRuntimeConfig, } = options;
|
|
346
|
+
const { clientId, appVersion, clientCapabilities, onMessage, onBinaryMessage, getTransportBufferedAmount, onLifecycleIntent, logger, downloadTokenStore, pushTokenStore, paseoHome, worktreesRoot, agentManager, agentStorage, projectRegistry, workspaceRegistry, filesystem, chatService, scheduleService, loopService, checkoutDiffManager, github, renameCurrentBranch, generateWorkspaceName, workspaceGitService, daemonConfigStore, mcpBaseUrl, stt, sttLanguage, tts, terminalManager, providerSnapshotManager, providerUsageService, serviceProxy, scriptRuntimeStore, workspaceSetupSnapshots, onBranchChanged, getDaemonTcpPort, getDaemonTcpHost, serviceProxyPublicBaseUrl, resolveScriptHealth, voice, voiceBridge, dictation, serverId, daemonVersion, daemonRuntimeConfig, } = options;
|
|
346
347
|
this.clientId = clientId;
|
|
347
348
|
this.appVersion = appVersion ?? null;
|
|
348
349
|
this.clientCapabilities = parseClientCapabilities(clientCapabilities);
|
|
@@ -416,6 +417,7 @@ export class Session {
|
|
|
416
417
|
logger: this.sessionLogger,
|
|
417
418
|
});
|
|
418
419
|
this.providerSnapshotManager = providerSnapshotManager;
|
|
420
|
+
this.providerUsageService = providerUsageService;
|
|
419
421
|
this.serviceProxy = serviceProxy ?? null;
|
|
420
422
|
this.scriptRuntimeStore = scriptRuntimeStore ?? null;
|
|
421
423
|
this.workspaceSetupSnapshots = workspaceSetupSnapshots ?? new Map();
|
|
@@ -860,6 +862,39 @@ export class Session {
|
|
|
860
862
|
payload,
|
|
861
863
|
});
|
|
862
864
|
}
|
|
865
|
+
async emitStoredAgentUpdate(record) {
|
|
866
|
+
const payload = this.buildStoredAgentPayload(record);
|
|
867
|
+
const subscription = this.agentUpdatesSubscription;
|
|
868
|
+
if (!subscription) {
|
|
869
|
+
return payload;
|
|
870
|
+
}
|
|
871
|
+
const project = payload.workspaceId
|
|
872
|
+
? await this.buildProjectPlacementForWorkspaceId(payload.workspaceId)
|
|
873
|
+
: null;
|
|
874
|
+
if (!project) {
|
|
875
|
+
this.bufferOrEmitAgentUpdate(subscription, {
|
|
876
|
+
kind: "remove",
|
|
877
|
+
agentId: payload.id,
|
|
878
|
+
});
|
|
879
|
+
return payload;
|
|
880
|
+
}
|
|
881
|
+
const matches = this.matchesAgentFilter({
|
|
882
|
+
agent: payload,
|
|
883
|
+
project,
|
|
884
|
+
filter: subscription.filter,
|
|
885
|
+
});
|
|
886
|
+
this.bufferOrEmitAgentUpdate(subscription, matches
|
|
887
|
+
? {
|
|
888
|
+
kind: "upsert",
|
|
889
|
+
agent: payload,
|
|
890
|
+
project,
|
|
891
|
+
}
|
|
892
|
+
: {
|
|
893
|
+
kind: "remove",
|
|
894
|
+
agentId: payload.id,
|
|
895
|
+
});
|
|
896
|
+
return payload;
|
|
897
|
+
}
|
|
863
898
|
flushBootstrappedAgentUpdates(options) {
|
|
864
899
|
const subscription = this.agentUpdatesSubscription;
|
|
865
900
|
if (!subscription || !subscription.isBootstrapping) {
|
|
@@ -1026,6 +1061,7 @@ export class Session {
|
|
|
1026
1061
|
async dispatchInboundMessage(msg) {
|
|
1027
1062
|
const promise = this.dispatchVoiceAndControlMessage(msg) ??
|
|
1028
1063
|
this.dispatchAgentRewindMessage(msg) ??
|
|
1064
|
+
this.dispatchAgentRelationshipMessage(msg) ??
|
|
1029
1065
|
this.dispatchAgentLifecycleMessage(msg) ??
|
|
1030
1066
|
this.dispatchAgentConfigMessage(msg) ??
|
|
1031
1067
|
this.dispatchCheckoutMessage(msg) ??
|
|
@@ -1094,6 +1130,14 @@ export class Session {
|
|
|
1094
1130
|
return undefined;
|
|
1095
1131
|
}
|
|
1096
1132
|
}
|
|
1133
|
+
dispatchAgentRelationshipMessage(msg) {
|
|
1134
|
+
switch (msg.type) {
|
|
1135
|
+
case "agent.detach.request":
|
|
1136
|
+
return this.handleDetachAgentRequest(msg.agentId, msg.requestId);
|
|
1137
|
+
default:
|
|
1138
|
+
return undefined;
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1097
1141
|
async handleDictationStreamStart(msg) {
|
|
1098
1142
|
const unavailable = this.resolveVoiceFeatureUnavailableContext("dictation");
|
|
1099
1143
|
if (unavailable) {
|
|
@@ -1350,6 +1394,8 @@ export class Session {
|
|
|
1350
1394
|
return this.handleLegacyOpenInEditorRequest(msg);
|
|
1351
1395
|
case "open_project_request":
|
|
1352
1396
|
return this.handleOpenProjectRequest(msg);
|
|
1397
|
+
case "project.add.request":
|
|
1398
|
+
return this.handleProjectAddRequest(msg);
|
|
1353
1399
|
case "archive_workspace_request":
|
|
1354
1400
|
return this.handleArchiveWorkspaceRequest(msg);
|
|
1355
1401
|
case "project.remove.request":
|
|
@@ -1389,6 +1435,8 @@ export class Session {
|
|
|
1389
1435
|
return this.handleRefreshProvidersSnapshotRequest(msg);
|
|
1390
1436
|
case "provider_diagnostic_request":
|
|
1391
1437
|
return this.handleProviderDiagnosticRequest(msg);
|
|
1438
|
+
case "provider.usage.list.request":
|
|
1439
|
+
return this.handleProviderUsageListRequest(msg);
|
|
1392
1440
|
default:
|
|
1393
1441
|
return undefined;
|
|
1394
1442
|
}
|
|
@@ -1580,39 +1628,60 @@ export class Session {
|
|
|
1580
1628
|
logger: this.sessionLogger,
|
|
1581
1629
|
}, agentId);
|
|
1582
1630
|
if (this.agentUpdatesSubscription) {
|
|
1583
|
-
const payload = this.
|
|
1584
|
-
const project = payload.workspaceId
|
|
1585
|
-
? await this.buildProjectPlacementForWorkspaceId(payload.workspaceId)
|
|
1586
|
-
: null;
|
|
1587
|
-
if (project) {
|
|
1588
|
-
const matches = this.matchesAgentFilter({
|
|
1589
|
-
agent: payload,
|
|
1590
|
-
project,
|
|
1591
|
-
filter: this.agentUpdatesSubscription.filter,
|
|
1592
|
-
});
|
|
1593
|
-
this.bufferOrEmitAgentUpdate(this.agentUpdatesSubscription, matches
|
|
1594
|
-
? {
|
|
1595
|
-
kind: "upsert",
|
|
1596
|
-
agent: payload,
|
|
1597
|
-
project,
|
|
1598
|
-
}
|
|
1599
|
-
: {
|
|
1600
|
-
kind: "remove",
|
|
1601
|
-
agentId,
|
|
1602
|
-
});
|
|
1603
|
-
}
|
|
1604
|
-
else {
|
|
1605
|
-
this.bufferOrEmitAgentUpdate(this.agentUpdatesSubscription, {
|
|
1606
|
-
kind: "remove",
|
|
1607
|
-
agentId,
|
|
1608
|
-
});
|
|
1609
|
-
}
|
|
1631
|
+
const payload = await this.emitStoredAgentUpdate(archivedRecord);
|
|
1610
1632
|
if (payload.workspaceId) {
|
|
1611
1633
|
await this.emitWorkspaceUpdateForWorkspaceId(payload.workspaceId);
|
|
1612
1634
|
}
|
|
1613
1635
|
}
|
|
1614
1636
|
return { agentId, archivedAt };
|
|
1615
1637
|
}
|
|
1638
|
+
async handleDetachAgentRequest(agentId, requestId) {
|
|
1639
|
+
this.sessionLogger.info({ agentId, requestId }, "Detaching agent from parent");
|
|
1640
|
+
try {
|
|
1641
|
+
const result = await detachAgentCommand({ agentManager: this.agentManager }, agentId);
|
|
1642
|
+
const affectedWorkspaceIds = new Set();
|
|
1643
|
+
if (!result.live) {
|
|
1644
|
+
const payload = await this.emitStoredAgentUpdate(result.record);
|
|
1645
|
+
if (payload.workspaceId) {
|
|
1646
|
+
affectedWorkspaceIds.add(payload.workspaceId);
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
else if (result.record.workspaceId) {
|
|
1650
|
+
affectedWorkspaceIds.add(result.record.workspaceId);
|
|
1651
|
+
}
|
|
1652
|
+
if (result.previousParentAgentId) {
|
|
1653
|
+
const rootWorkspaceId = await this.resolveDelegationRootWorkspaceId(result.previousParentAgentId);
|
|
1654
|
+
if (rootWorkspaceId) {
|
|
1655
|
+
affectedWorkspaceIds.add(rootWorkspaceId);
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
await this.emitWorkspaceUpdatesForWorkspaceIds(affectedWorkspaceIds, {
|
|
1659
|
+
skipReconcile: true,
|
|
1660
|
+
});
|
|
1661
|
+
this.emit({
|
|
1662
|
+
type: "agent.detach.response",
|
|
1663
|
+
payload: {
|
|
1664
|
+
requestId,
|
|
1665
|
+
agentId,
|
|
1666
|
+
accepted: true,
|
|
1667
|
+
error: null,
|
|
1668
|
+
},
|
|
1669
|
+
});
|
|
1670
|
+
}
|
|
1671
|
+
catch (error) {
|
|
1672
|
+
const message = getErrorMessageOr(error, "Failed to detach agent");
|
|
1673
|
+
this.sessionLogger.error({ err: error, agentId, requestId }, "Failed to detach agent");
|
|
1674
|
+
this.emit({
|
|
1675
|
+
type: "agent.detach.response",
|
|
1676
|
+
payload: {
|
|
1677
|
+
requestId,
|
|
1678
|
+
agentId,
|
|
1679
|
+
accepted: false,
|
|
1680
|
+
error: message,
|
|
1681
|
+
},
|
|
1682
|
+
});
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1616
1685
|
async handleCloseItemsRequest(msg) {
|
|
1617
1686
|
const archiveResults = await Promise.allSettled(msg.agentIds.map((agentId) => this.archiveAgentForClose(agentId)));
|
|
1618
1687
|
const agents = [];
|
|
@@ -3075,6 +3144,32 @@ export class Session {
|
|
|
3075
3144
|
});
|
|
3076
3145
|
}
|
|
3077
3146
|
}
|
|
3147
|
+
async handleProviderUsageListRequest(msg) {
|
|
3148
|
+
try {
|
|
3149
|
+
const usage = await this.providerUsageService.listUsage();
|
|
3150
|
+
this.emit({
|
|
3151
|
+
type: "provider.usage.list.response",
|
|
3152
|
+
payload: {
|
|
3153
|
+
requestId: msg.requestId,
|
|
3154
|
+
fetchedAt: usage.fetchedAt,
|
|
3155
|
+
providers: usage.providers,
|
|
3156
|
+
},
|
|
3157
|
+
});
|
|
3158
|
+
}
|
|
3159
|
+
catch (error) {
|
|
3160
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
3161
|
+
this.sessionLogger.error({ err }, "Failed to list provider usage");
|
|
3162
|
+
this.emit({
|
|
3163
|
+
type: "rpc_error",
|
|
3164
|
+
payload: {
|
|
3165
|
+
requestId: msg.requestId,
|
|
3166
|
+
requestType: msg.type,
|
|
3167
|
+
error: `Failed to list provider usage: ${err.message}`,
|
|
3168
|
+
code: "provider_usage_list_failed",
|
|
3169
|
+
},
|
|
3170
|
+
});
|
|
3171
|
+
}
|
|
3172
|
+
}
|
|
3078
3173
|
assertSafeGitRef(ref, label) {
|
|
3079
3174
|
if (!/^[A-Za-z0-9._/-]+$/.test(ref)) {
|
|
3080
3175
|
throw new Error(`Invalid ${label}: ${ref}`);
|
|
@@ -3309,11 +3404,11 @@ export class Session {
|
|
|
3309
3404
|
async handleSetAgentModeRequest(agentId, modeId, requestId) {
|
|
3310
3405
|
this.sessionLogger.info({ agentId, modeId, requestId }, "session: set_agent_mode_request");
|
|
3311
3406
|
try {
|
|
3312
|
-
await setAgentModeCommand({ agentManager: this.agentManager }, { agentId, modeId });
|
|
3407
|
+
const result = await setAgentModeCommand({ agentManager: this.agentManager }, { agentId, modeId });
|
|
3313
3408
|
this.sessionLogger.info({ agentId, modeId, requestId }, "session: set_agent_mode_request success");
|
|
3314
3409
|
this.emit({
|
|
3315
3410
|
type: "set_agent_mode_response",
|
|
3316
|
-
payload: { requestId, agentId, accepted: true, error: null },
|
|
3411
|
+
payload: { requestId, agentId, accepted: true, error: null, notice: result.notice },
|
|
3317
3412
|
});
|
|
3318
3413
|
}
|
|
3319
3414
|
catch (error) {
|
|
@@ -3405,11 +3500,11 @@ export class Session {
|
|
|
3405
3500
|
async handleSetAgentThinkingRequest(agentId, thinkingOptionId, requestId) {
|
|
3406
3501
|
this.sessionLogger.info({ agentId, thinkingOptionId, requestId }, "session: set_agent_thinking_request");
|
|
3407
3502
|
try {
|
|
3408
|
-
await this.agentManager.setAgentThinkingOption(agentId, thinkingOptionId);
|
|
3503
|
+
const notice = await this.agentManager.setAgentThinkingOption(agentId, thinkingOptionId);
|
|
3409
3504
|
this.sessionLogger.info({ agentId, thinkingOptionId, requestId }, "session: set_agent_thinking_request success");
|
|
3410
3505
|
this.emit({
|
|
3411
3506
|
type: "set_agent_thinking_response",
|
|
3412
|
-
payload: { requestId, agentId, accepted: true, error: null },
|
|
3507
|
+
payload: { requestId, agentId, accepted: true, error: null, notice },
|
|
3413
3508
|
});
|
|
3414
3509
|
}
|
|
3415
3510
|
catch (error) {
|
|
@@ -4964,6 +5059,29 @@ export class Session {
|
|
|
4964
5059
|
const payload = this.buildStoredAgentPayload(record);
|
|
4965
5060
|
return this.isProviderVisibleToClient(payload.provider) ? payload : null;
|
|
4966
5061
|
}
|
|
5062
|
+
async resolveDelegationRootWorkspaceId(agentId) {
|
|
5063
|
+
const seen = new Set();
|
|
5064
|
+
let currentAgentId = agentId;
|
|
5065
|
+
while (true) {
|
|
5066
|
+
if (seen.has(currentAgentId)) {
|
|
5067
|
+
return null;
|
|
5068
|
+
}
|
|
5069
|
+
seen.add(currentAgentId);
|
|
5070
|
+
const live = this.agentManager.getAgent(currentAgentId);
|
|
5071
|
+
const source = live ?? (await this.agentStorage.get(currentAgentId));
|
|
5072
|
+
if (!source) {
|
|
5073
|
+
return null;
|
|
5074
|
+
}
|
|
5075
|
+
if ("archivedAt" in source && source.archivedAt) {
|
|
5076
|
+
return null;
|
|
5077
|
+
}
|
|
5078
|
+
const parentAgentId = getParentAgentIdFromLabels(source.labels);
|
|
5079
|
+
if (!parentAgentId) {
|
|
5080
|
+
return source.workspaceId ?? null;
|
|
5081
|
+
}
|
|
5082
|
+
currentAgentId = parentAgentId;
|
|
5083
|
+
}
|
|
5084
|
+
}
|
|
4967
5085
|
async buildActiveProjectPlacementsByWorkspaceId() {
|
|
4968
5086
|
const [persistedWorkspaces, persistedProjects] = await Promise.all([
|
|
4969
5087
|
this.workspaceRegistry.list(),
|
|
@@ -5371,6 +5489,26 @@ export class Session {
|
|
|
5371
5489
|
await this.workspaceRegistry.upsert(workspaceRecord);
|
|
5372
5490
|
return workspaceRecord;
|
|
5373
5491
|
}
|
|
5492
|
+
async findOrCreateProjectForDirectory(cwd) {
|
|
5493
|
+
const normalizedCwd = resolve(cwd);
|
|
5494
|
+
const checkout = await this.workspaceGitService.getCheckout(normalizedCwd);
|
|
5495
|
+
const membership = classifyDirectoryForProjectMembership({ cwd: normalizedCwd, checkout });
|
|
5496
|
+
const projectRecord = await this.resolveProjectRecordForPlacement({
|
|
5497
|
+
membership,
|
|
5498
|
+
timestamp: new Date().toISOString(),
|
|
5499
|
+
});
|
|
5500
|
+
await this.projectRegistry.upsert(projectRecord);
|
|
5501
|
+
return projectRecord;
|
|
5502
|
+
}
|
|
5503
|
+
buildProjectDescriptor(project) {
|
|
5504
|
+
return {
|
|
5505
|
+
projectId: project.projectId,
|
|
5506
|
+
projectDisplayName: resolveProjectDisplayName(project),
|
|
5507
|
+
projectCustomName: project.customName ?? null,
|
|
5508
|
+
projectRootPath: project.rootPath,
|
|
5509
|
+
projectKind: project.kind,
|
|
5510
|
+
};
|
|
5511
|
+
}
|
|
5374
5512
|
async reclassifyOrUnarchiveWorkspaceForDirectory(input) {
|
|
5375
5513
|
const checkout = await this.workspaceGitService.getCheckout(input.cwd);
|
|
5376
5514
|
const membership = classifyDirectoryForProjectMembership({ cwd: input.cwd, checkout });
|
|
@@ -5701,19 +5839,19 @@ export class Session {
|
|
|
5701
5839
|
return {
|
|
5702
5840
|
kind: "remove",
|
|
5703
5841
|
id: workspaceId,
|
|
5704
|
-
...(await this.
|
|
5842
|
+
...(await this.resolveProjectWithoutActiveWorkspacesForArchivedWorkspace(workspaceId)),
|
|
5705
5843
|
};
|
|
5706
5844
|
}
|
|
5707
|
-
// When a workspace is archived its project may
|
|
5708
|
-
//
|
|
5709
|
-
// sidebar
|
|
5710
|
-
async
|
|
5845
|
+
// When a workspace is archived its project may have no active workspaces left.
|
|
5846
|
+
// Resolve that project parent so the `remove` update can carry it, keeping the
|
|
5847
|
+
// sidebar in sync without a full re-hydration.
|
|
5848
|
+
async resolveProjectWithoutActiveWorkspacesForArchivedWorkspace(workspaceId) {
|
|
5711
5849
|
const archivedWorkspace = await this.workspaceRegistry.get(workspaceId);
|
|
5712
5850
|
if (!archivedWorkspace) {
|
|
5713
5851
|
return null;
|
|
5714
5852
|
}
|
|
5715
|
-
const
|
|
5716
|
-
return
|
|
5853
|
+
const projectWithoutActiveWorkspaces = (await this.workspaceDirectory.listEmptyProjects()).find((project) => project.projectId === archivedWorkspace.projectId);
|
|
5854
|
+
return projectWithoutActiveWorkspaces ? { emptyProject: projectWithoutActiveWorkspaces } : null;
|
|
5717
5855
|
}
|
|
5718
5856
|
async emitWorkspaceUpdateForTerminalContribution(event) {
|
|
5719
5857
|
// A terminal's activity contributes only to the workspace it carries. A
|
|
@@ -6159,6 +6297,58 @@ export class Session {
|
|
|
6159
6297
|
});
|
|
6160
6298
|
}
|
|
6161
6299
|
}
|
|
6300
|
+
async handleProjectAddRequest(request) {
|
|
6301
|
+
const requestedCwd = request.cwd;
|
|
6302
|
+
const cwd = expandTilde(requestedCwd);
|
|
6303
|
+
const directoryExists = await this.filesystem.isDirectory(cwd).catch(() => false);
|
|
6304
|
+
if (!directoryExists) {
|
|
6305
|
+
this.sessionLogger.info({ requestedCwd, resolvedCwd: cwd, reason: "directory_not_found" }, "Add project rejected");
|
|
6306
|
+
this.emit({
|
|
6307
|
+
type: "project.add.response",
|
|
6308
|
+
payload: {
|
|
6309
|
+
requestId: request.requestId,
|
|
6310
|
+
project: null,
|
|
6311
|
+
error: `Directory not found: ${cwd}`,
|
|
6312
|
+
errorCode: "directory_not_found",
|
|
6313
|
+
},
|
|
6314
|
+
});
|
|
6315
|
+
return;
|
|
6316
|
+
}
|
|
6317
|
+
try {
|
|
6318
|
+
const projectsBefore = new Map();
|
|
6319
|
+
for (const project of await this.projectRegistry.list()) {
|
|
6320
|
+
projectsBefore.set(project.projectId, project);
|
|
6321
|
+
}
|
|
6322
|
+
const project = await this.findOrCreateProjectForDirectory(cwd);
|
|
6323
|
+
this.sessionLogger.info({
|
|
6324
|
+
requestedCwd,
|
|
6325
|
+
resolvedCwd: cwd,
|
|
6326
|
+
projectId: project.projectId,
|
|
6327
|
+
projectKind: project.kind,
|
|
6328
|
+
projectTransition: describeRegistryTransition(projectsBefore.get(project.projectId) ?? null),
|
|
6329
|
+
}, "Project added");
|
|
6330
|
+
this.emit({
|
|
6331
|
+
type: "project.add.response",
|
|
6332
|
+
payload: {
|
|
6333
|
+
requestId: request.requestId,
|
|
6334
|
+
project: this.buildProjectDescriptor(project),
|
|
6335
|
+
error: null,
|
|
6336
|
+
},
|
|
6337
|
+
});
|
|
6338
|
+
}
|
|
6339
|
+
catch (error) {
|
|
6340
|
+
const message = error instanceof Error ? error.message : "Failed to add project";
|
|
6341
|
+
this.sessionLogger.error({ err: error, cwd }, "Failed to add project");
|
|
6342
|
+
this.emit({
|
|
6343
|
+
type: "project.add.response",
|
|
6344
|
+
payload: {
|
|
6345
|
+
requestId: request.requestId,
|
|
6346
|
+
project: null,
|
|
6347
|
+
error: message,
|
|
6348
|
+
},
|
|
6349
|
+
});
|
|
6350
|
+
}
|
|
6351
|
+
}
|
|
6162
6352
|
buildWorkspaceScriptPayloadSnapshot(workspaceId, workspaceDirectory) {
|
|
6163
6353
|
if (!this.serviceProxy || !this.scriptRuntimeStore) {
|
|
6164
6354
|
return [];
|
|
@@ -38,15 +38,14 @@ export function validateOpenAiCredentialRequirements(params) {
|
|
|
38
38
|
missingOpenAiCredentialsFor.push("dictation.stt");
|
|
39
39
|
}
|
|
40
40
|
if (missingOpenAiCredentialsFor.length > 0) {
|
|
41
|
-
logger.
|
|
41
|
+
logger.warn({
|
|
42
42
|
requestedProviders: {
|
|
43
43
|
dictationStt: providers.dictationStt.provider,
|
|
44
44
|
voiceStt: providers.voiceStt.provider,
|
|
45
45
|
voiceTts: providers.voiceTts.provider,
|
|
46
46
|
},
|
|
47
47
|
missingOpenAiCredentialsFor,
|
|
48
|
-
}, "Invalid speech configuration: OpenAI provider selected but credentials are missing");
|
|
49
|
-
throw new Error(`Missing OpenAI credentials for configured speech features: ${missingOpenAiCredentialsFor.join(", ")}`);
|
|
48
|
+
}, "Invalid speech configuration: OpenAI provider selected but credentials are missing — speech features will be unavailable");
|
|
50
49
|
}
|
|
51
50
|
}
|
|
52
51
|
function createOpenAiStt(apiKey, openaiConfig, logger) {
|
|
@@ -105,7 +104,7 @@ export function initializeOpenAiSpeechServices(params) {
|
|
|
105
104
|
}
|
|
106
105
|
}
|
|
107
106
|
else if (needsAnyOpenAi) {
|
|
108
|
-
|
|
107
|
+
// validateOpenAiCredentialRequirements already warned about missing credentials
|
|
109
108
|
}
|
|
110
109
|
return {
|
|
111
110
|
turnDetectionService,
|
|
@@ -93,6 +93,7 @@ export declare class VoiceAssistantWebSocketServer {
|
|
|
93
93
|
private eventLoopDelayMonitor;
|
|
94
94
|
private unsubscribeSpeechReadiness;
|
|
95
95
|
private unsubscribeDaemonConfigChange;
|
|
96
|
+
private readonly providerUsageService;
|
|
96
97
|
private unsubscribeTerminalActivity;
|
|
97
98
|
constructor(server: HTTPServer, logger: pino.Logger, serverId: string, agentManager: AgentManager, agentStorage: AgentStorage, downloadTokenStore: DownloadTokenStore, paseoHome: string, daemonConfigStore: DaemonConfigStore, mcpBaseUrl: string | null, wsConfig: WebSocketServerConfig, auth?: DaemonAuthConfig, speech?: SpeechService | null, terminalManager?: TerminalManager | null, dictation?: {
|
|
98
99
|
finalTimeoutMs?: number;
|
|
@@ -14,6 +14,7 @@ import { buildAgentAttentionNotificationPayload, findLatestPermissionRequest, }
|
|
|
14
14
|
import { createGitHubService } from "../services/github-service.js";
|
|
15
15
|
import { extractWsBearerProtocol, extractWsBearerToken, isBearerTokenValid, } from "./auth.js";
|
|
16
16
|
import { WebSocketRuntimeMetricsWindow, } from "./websocket/runtime-metrics.js";
|
|
17
|
+
import { ProviderUsageService } from "../services/quota-fetcher/service.js";
|
|
17
18
|
const WS_CLOSE_DAEMON_AUTH_FAILED = 4401;
|
|
18
19
|
function resolveTerminalAttentionReason(input) {
|
|
19
20
|
if (input.attentionReason === "finished")
|
|
@@ -298,6 +299,9 @@ export class VoiceAssistantWebSocketServer {
|
|
|
298
299
|
this.logger.warn({ err, agentId: params.agentId }, "Failed to broadcast agent attention");
|
|
299
300
|
});
|
|
300
301
|
});
|
|
302
|
+
this.providerUsageService = new ProviderUsageService({
|
|
303
|
+
logger: this.logger,
|
|
304
|
+
});
|
|
301
305
|
this.wss = this.createWebSocketServer(server, wsConfig, auth);
|
|
302
306
|
this.startRuntimeMetricsInterval();
|
|
303
307
|
this.logger.info("WebSocket server initialized on /ws");
|
|
@@ -649,6 +653,7 @@ export class VoiceAssistantWebSocketServer {
|
|
|
649
653
|
tts: () => this.speech?.resolveTts() ?? null,
|
|
650
654
|
terminalManager: this.terminalManager,
|
|
651
655
|
providerSnapshotManager: this.providerSnapshotManager,
|
|
656
|
+
providerUsageService: this.providerUsageService,
|
|
652
657
|
serviceProxy: this.serviceProxy ?? undefined,
|
|
653
658
|
scriptRuntimeStore: this.scriptRuntimeStore ?? undefined,
|
|
654
659
|
workspaceSetupSnapshots: this.workspaceSetupSnapshots,
|
|
@@ -809,8 +814,14 @@ export class VoiceAssistantWebSocketServer {
|
|
|
809
814
|
workspaceMultiplicity: true,
|
|
810
815
|
// COMPAT(projectRemove): added in v0.1.97, drop the gate when floor >= v0.1.97.
|
|
811
816
|
projectRemove: true,
|
|
817
|
+
// COMPAT(projectAdd): added in v0.1.97, drop the gate when floor >= v0.1.97.
|
|
818
|
+
projectAdd: true,
|
|
812
819
|
// COMPAT(worktreeRestore): added in v0.1.97, drop the gate when floor >= v0.1.97
|
|
813
820
|
worktreeRestore: true,
|
|
821
|
+
// COMPAT(providerUsageList): added in v0.1.98, drop the gate when daemon floor >= v0.1.98.
|
|
822
|
+
providerUsageList: true,
|
|
823
|
+
// COMPAT(agentDetach): added in v0.1.98, remove gate after 2026-12-19 once daemon floor >= v0.1.98.
|
|
824
|
+
agentDetach: true,
|
|
814
825
|
},
|
|
815
826
|
};
|
|
816
827
|
}
|
|
@@ -212,9 +212,8 @@ export async function killTerminalsForWorkspace(dependencies, workspaceId) {
|
|
|
212
212
|
}
|
|
213
213
|
}));
|
|
214
214
|
}
|
|
215
|
-
// Archiving the last workspace of a project leaves the project
|
|
216
|
-
//
|
|
217
|
-
// parent project here.
|
|
215
|
+
// Archiving the last workspace of a project leaves the project record active.
|
|
216
|
+
// The user removes the project explicitly, so we never archive the parent here.
|
|
218
217
|
export async function archivePersistedWorkspaceRecord(input) {
|
|
219
218
|
const existingWorkspace = await input.workspaceRegistry.get(input.workspaceId);
|
|
220
219
|
if (!existingWorkspace) {
|
|
@@ -295,9 +295,9 @@ export class WorkspaceDirectory {
|
|
|
295
295
|
const candidates = [...agentTimestamps, ...terminalTimestamps].sort();
|
|
296
296
|
return candidates.at(-1) ?? null;
|
|
297
297
|
}
|
|
298
|
-
// Project parents that have no active workspaces.
|
|
299
|
-
//
|
|
300
|
-
//
|
|
298
|
+
// Project parents that have no active workspaces. The wire field is the
|
|
299
|
+
// sidebar projection bucket for projects whose workspace list is currently
|
|
300
|
+
// empty; it is not a separate domain record.
|
|
301
301
|
async listEmptyProjects() {
|
|
302
302
|
const [persistedWorkspaces, persistedProjects] = await Promise.all([
|
|
303
303
|
this.deps.workspaceRegistry.list(),
|
|
@@ -359,8 +359,8 @@ export class WorkspaceDirectory {
|
|
|
359
359
|
const nextCursor = hasMore && pagedEntries.length > 0
|
|
360
360
|
? this.pager.encode(pagedEntries[pagedEntries.length - 1], sort)
|
|
361
361
|
: null;
|
|
362
|
-
//
|
|
363
|
-
// them without them
|
|
362
|
+
// Project parents with no active workspaces ride only on the first page so
|
|
363
|
+
// the sidebar can render them without duplicating them across pagination.
|
|
364
364
|
const projectIdFilter = filter?.projectId?.trim();
|
|
365
365
|
const emptyProjects = cursorToken
|
|
366
366
|
? []
|
|
@@ -98,8 +98,8 @@ export class WorkspaceReconciliationService {
|
|
|
98
98
|
// 2. Merge duplicate active project records that point at the same repo root.
|
|
99
99
|
await this.mergeDuplicateProjectsByRoot(activeProjects, workspacesByProject, changes);
|
|
100
100
|
// 3. Reconcile git metadata for active projects whose directories still exist.
|
|
101
|
-
//
|
|
102
|
-
//
|
|
101
|
+
// Projects persist until explicitly removed, even when they currently have
|
|
102
|
+
// zero active workspaces, so they still reconcile their own metadata.
|
|
103
103
|
// Skip projects archived earlier in this pass (e.g. merged duplicates) so we
|
|
104
104
|
// don't resurrect them by upserting a stale, non-archived copy.
|
|
105
105
|
const archivedProjectIds = new Set(changes
|
|
@@ -6,6 +6,9 @@ export async function createWorktreeCore(input, deps) {
|
|
|
6
6
|
const requestedWorktreeSlug = input.worktreeSlug
|
|
7
7
|
? normalizeWorktreeSlug(input.worktreeSlug)
|
|
8
8
|
: undefined;
|
|
9
|
+
const requestedBranchName = input.branchName
|
|
10
|
+
? validateWorktreeSlug(input.branchName.trim())
|
|
11
|
+
: undefined;
|
|
9
12
|
let intentInput;
|
|
10
13
|
if (input.action === "checkout") {
|
|
11
14
|
intentInput = {
|
|
@@ -27,6 +30,7 @@ export async function createWorktreeCore(input, deps) {
|
|
|
27
30
|
intentInput = {
|
|
28
31
|
action: "branch-off",
|
|
29
32
|
refName: input.refName,
|
|
33
|
+
branchName: requestedBranchName,
|
|
30
34
|
worktreeSlug,
|
|
31
35
|
};
|
|
32
36
|
}
|
|
@@ -37,7 +41,7 @@ export async function createWorktreeCore(input, deps) {
|
|
|
37
41
|
let normalizedSlug;
|
|
38
42
|
switch (intent.kind) {
|
|
39
43
|
case "branch-off": {
|
|
40
|
-
normalizedSlug = intent.branchName;
|
|
44
|
+
normalizedSlug = requestedWorktreeSlug ?? normalizeWorktreeSlug(intent.branchName);
|
|
41
45
|
break;
|
|
42
46
|
}
|
|
43
47
|
case "checkout-branch": {
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ProviderUsageFetcher, ProviderUsageFetcherFactoryOptions, ProviderUsageFetcherManifestEntry } from "./provider.js";
|
|
2
|
+
export declare const PROVIDER_USAGE_FETCHERS: readonly ProviderUsageFetcherManifestEntry[];
|
|
3
|
+
export declare function createProviderUsageFetchers(options: ProviderUsageFetcherFactoryOptions): ProviderUsageFetcher[];
|
|
4
|
+
//# sourceMappingURL=manifest.d.ts.map
|