mirror of
https://github.com/harivansh-afk/sandbox-agent.git
synced 2026-04-15 07:04:48 +00:00
feat(inspector): improve session UI, skills dropdown, and visual polish (#179)
- Add delete button on ended sessions (visible on hover) - Darken ended sessions with opacity and "ended" pill badge - Sort ended sessions to bottom of list - Add token usage pill in chat header - Disable input when session ended - Add Official Skills dropdown with SDK and Rivet presets - Format session IDs shorter with full ID on hover - Add arrow icon to "Configure persistence" link - Add agent logo SVGs
This commit is contained in:
parent
1c381c552a
commit
e134012955
22 changed files with 2283 additions and 395 deletions
|
|
@ -175,6 +175,8 @@ export class LiveAcpConnection {
|
|||
private readonly pendingNewSessionLocals: string[] = [];
|
||||
private readonly pendingRequestSessionById = new Map<string, string>();
|
||||
private readonly pendingReplayByLocalSessionId = new Map<string, string>();
|
||||
private lastAdapterExit: { success: boolean; code: number | null } | null = null;
|
||||
private lastAdapterExitAt = 0;
|
||||
|
||||
private readonly onObservedEnvelope: (
|
||||
connection: LiveAcpConnection,
|
||||
|
|
@ -230,6 +232,10 @@ export class LiveAcpConnection {
|
|||
sessionUpdate: async (_notification: SessionNotification) => {
|
||||
// Session updates are observed via envelope persistence.
|
||||
},
|
||||
extNotification: async (method: string, params: Record<string, unknown>) => {
|
||||
if (!live) return;
|
||||
live.handleAdapterNotification(method, params);
|
||||
},
|
||||
},
|
||||
onEnvelope: (envelope, direction) => {
|
||||
if (!live) {
|
||||
|
|
@ -286,6 +292,7 @@ export class LiveAcpConnection {
|
|||
localSessionId: string,
|
||||
sessionInit: Omit<NewSessionRequest, "_meta">,
|
||||
): Promise<NewSessionResponse> {
|
||||
const createStartedAt = Date.now();
|
||||
this.pendingNewSessionLocals.push(localSessionId);
|
||||
|
||||
try {
|
||||
|
|
@ -297,6 +304,11 @@ export class LiveAcpConnection {
|
|||
if (index !== -1) {
|
||||
this.pendingNewSessionLocals.splice(index, 1);
|
||||
}
|
||||
const adapterExit = this.lastAdapterExit;
|
||||
if (adapterExit && this.lastAdapterExitAt >= createStartedAt) {
|
||||
const suffix = adapterExit.code == null ? "" : ` (code ${adapterExit.code})`;
|
||||
throw new Error(`Agent process exited while creating session${suffix}`);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
|
@ -356,6 +368,17 @@ export class LiveAcpConnection {
|
|||
this.onObservedEnvelope(this, envelope, direction, localSessionId);
|
||||
}
|
||||
|
||||
private handleAdapterNotification(method: string, params: Record<string, unknown>): void {
|
||||
if (method !== "_adapter/agent_exited") {
|
||||
return;
|
||||
}
|
||||
this.lastAdapterExit = {
|
||||
success: params.success === true,
|
||||
code: typeof params.code === "number" ? params.code : null,
|
||||
};
|
||||
this.lastAdapterExitAt = Date.now();
|
||||
}
|
||||
|
||||
private resolveSessionId(envelope: AnyMessage, direction: AcpEnvelopeDirection): string | null {
|
||||
const id = envelopeId(envelope);
|
||||
const method = envelopeMethod(envelope);
|
||||
|
|
@ -413,6 +436,7 @@ export class SandboxAgent {
|
|||
private spawnHandle?: SandboxAgentSpawnHandle;
|
||||
|
||||
private readonly liveConnections = new Map<string, LiveAcpConnection>();
|
||||
private readonly pendingLiveConnections = new Map<string, Promise<LiveAcpConnection>>();
|
||||
private readonly sessionHandles = new Map<string, Session>();
|
||||
private readonly eventListeners = new Map<string, Set<SessionEventListener>>();
|
||||
private readonly nextSessionEventIndexBySession = new Map<string, number>();
|
||||
|
|
@ -463,6 +487,15 @@ export class SandboxAgent {
|
|||
async dispose(): Promise<void> {
|
||||
const connections = [...this.liveConnections.values()];
|
||||
this.liveConnections.clear();
|
||||
const pending = [...this.pendingLiveConnections.values()];
|
||||
this.pendingLiveConnections.clear();
|
||||
|
||||
const pendingSettled = await Promise.allSettled(pending);
|
||||
for (const item of pendingSettled) {
|
||||
if (item.status === "fulfilled") {
|
||||
connections.push(item.value);
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
connections.map(async (connection) => {
|
||||
|
|
@ -725,21 +758,43 @@ export class SandboxAgent {
|
|||
return existing;
|
||||
}
|
||||
|
||||
const serverId = `sdk-${agent}-${randomId()}`;
|
||||
const created = await LiveAcpConnection.create({
|
||||
baseUrl: this.baseUrl,
|
||||
token: this.token,
|
||||
fetcher: this.fetcher,
|
||||
headers: this.defaultHeaders,
|
||||
agent,
|
||||
serverId,
|
||||
onObservedEnvelope: (connection, envelope, direction, localSessionId) => {
|
||||
void this.persistObservedEnvelope(connection, envelope, direction, localSessionId);
|
||||
},
|
||||
});
|
||||
const pending = this.pendingLiveConnections.get(agent);
|
||||
if (pending) {
|
||||
return pending;
|
||||
}
|
||||
|
||||
this.liveConnections.set(agent, created);
|
||||
return created;
|
||||
const creating = (async () => {
|
||||
const serverId = `sdk-${agent}-${randomId()}`;
|
||||
const created = await LiveAcpConnection.create({
|
||||
baseUrl: this.baseUrl,
|
||||
token: this.token,
|
||||
fetcher: this.fetcher,
|
||||
headers: this.defaultHeaders,
|
||||
agent,
|
||||
serverId,
|
||||
onObservedEnvelope: (connection, envelope, direction, localSessionId) => {
|
||||
void this.persistObservedEnvelope(connection, envelope, direction, localSessionId);
|
||||
},
|
||||
});
|
||||
|
||||
const raced = this.liveConnections.get(agent);
|
||||
if (raced) {
|
||||
await created.close();
|
||||
return raced;
|
||||
}
|
||||
|
||||
this.liveConnections.set(agent, created);
|
||||
return created;
|
||||
})();
|
||||
|
||||
this.pendingLiveConnections.set(agent, creating);
|
||||
try {
|
||||
return await creating;
|
||||
} finally {
|
||||
if (this.pendingLiveConnections.get(agent) === creating) {
|
||||
this.pendingLiveConnections.delete(agent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async persistObservedEnvelope(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue