@gricha/perry 0.3.12 → 0.3.14
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/agent/router.js +142 -40
- package/dist/agent/web/assets/index-ChUt0xgV.js +104 -0
- package/dist/agent/web/index.html +1 -1
- package/dist/agents/__tests__/opencode.test.js +37 -1
- package/dist/agents/sync/opencode.js +8 -1
- package/dist/perry-worker +0 -0
- package/dist/session-manager/adapters/opencode.js +102 -13
- package/dist/session-manager/bun-handler.js +1 -1
- package/dist/session-manager/manager.js +35 -12
- package/dist/sessions/registry.js +19 -0
- package/dist/shared/constants.js +1 -0
- package/package.json +1 -1
- package/dist/agent/web/assets/index-hNfXv8YX.js +0 -104
package/dist/agent/router.js
CHANGED
|
@@ -11,6 +11,7 @@ import { setSessionName, getSessionNamesForWorkspace, deleteSessionName, } from
|
|
|
11
11
|
import * as sessionRegistry from '../sessions/registry';
|
|
12
12
|
import { discoverSSHKeys } from '../ssh/discovery';
|
|
13
13
|
import { discoverAllSessions, getSessionDetails as getAgentSessionDetails, getSessionMessages, findSessionMessages, deleteSession as deleteSessionFromProvider, searchSessions as searchSessionsInContainer, } from '../sessions/agents';
|
|
14
|
+
import { decodeClaudeProjectPath } from '../sessions/agents/utils';
|
|
14
15
|
import { discoverClaudeCodeModels, discoverHostOpencodeModels, discoverContainerOpencodeModels, } from '../models/discovery';
|
|
15
16
|
import { deleteOpencodeSession } from '../sessions/agents/opencode-storage';
|
|
16
17
|
import { SessionIndex } from '../worker/session-index';
|
|
@@ -435,6 +436,34 @@ export function createRouter(ctx) {
|
|
|
435
436
|
});
|
|
436
437
|
const hostSessionIndex = new SessionIndex();
|
|
437
438
|
let hostSessionIndexInitialized = false;
|
|
439
|
+
function toRegistryAgentType(agentType) {
|
|
440
|
+
return agentType === 'claude-code' ? 'claude' : agentType;
|
|
441
|
+
}
|
|
442
|
+
function toClientAgentType(agentType) {
|
|
443
|
+
return agentType === 'claude' ? 'claude-code' : agentType;
|
|
444
|
+
}
|
|
445
|
+
async function ensureRegistrySession(workspaceName, agentType, agentSessionId, options) {
|
|
446
|
+
const existing = await sessionRegistry.findByAgentSessionId(ctx.stateDir, agentSessionId);
|
|
447
|
+
if (existing) {
|
|
448
|
+
return existing;
|
|
449
|
+
}
|
|
450
|
+
return sessionRegistry.importExternalSession(ctx.stateDir, {
|
|
451
|
+
perrySessionId: `imported-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
|
|
452
|
+
workspaceName,
|
|
453
|
+
agentType: toRegistryAgentType(agentType),
|
|
454
|
+
agentSessionId,
|
|
455
|
+
projectPath: options?.projectPath ?? null,
|
|
456
|
+
createdAt: options?.createdAt,
|
|
457
|
+
lastActivity: options?.lastActivity,
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
async function resolveSessionRecord(sessionId) {
|
|
461
|
+
const byPerry = await sessionRegistry.getSession(ctx.stateDir, sessionId);
|
|
462
|
+
if (byPerry) {
|
|
463
|
+
return byPerry;
|
|
464
|
+
}
|
|
465
|
+
return sessionRegistry.findByAgentSessionId(ctx.stateDir, sessionId);
|
|
466
|
+
}
|
|
438
467
|
async function listHostSessions(input) {
|
|
439
468
|
if (!hostSessionIndexInitialized) {
|
|
440
469
|
await hostSessionIndex.initialize();
|
|
@@ -450,14 +479,27 @@ export function createRouter(ctx) {
|
|
|
450
479
|
}
|
|
451
480
|
const nonEmptySessions = sessions.filter((s) => s.messageCount > 0);
|
|
452
481
|
const sessionNames = await getSessionNamesForWorkspace(ctx.configDir, HOST_WORKSPACE_NAME);
|
|
453
|
-
const
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
482
|
+
const paginatedRaw = nonEmptySessions.slice(offset, offset + limit);
|
|
483
|
+
const paginatedSessions = await Promise.all(paginatedRaw.map(async (session) => {
|
|
484
|
+
const projectPath = session.agentType === 'claude'
|
|
485
|
+
? decodeClaudeProjectPath(session.directory)
|
|
486
|
+
: session.directory;
|
|
487
|
+
const record = await ensureRegistrySession(HOST_WORKSPACE_NAME, session.agentType, session.id, {
|
|
488
|
+
projectPath,
|
|
489
|
+
createdAt: new Date(session.lastActivity).toISOString(),
|
|
490
|
+
lastActivity: new Date(session.lastActivity).toISOString(),
|
|
491
|
+
});
|
|
492
|
+
const name = sessionNames[record.perrySessionId] || sessionNames[session.id] || null;
|
|
493
|
+
return {
|
|
494
|
+
id: record.perrySessionId,
|
|
495
|
+
agentSessionId: session.id,
|
|
496
|
+
name,
|
|
497
|
+
agentType: toClientAgentType(session.agentType),
|
|
498
|
+
projectPath,
|
|
499
|
+
messageCount: session.messageCount,
|
|
500
|
+
lastActivity: new Date(session.lastActivity).toISOString(),
|
|
501
|
+
firstPrompt: session.firstPrompt,
|
|
502
|
+
};
|
|
461
503
|
}));
|
|
462
504
|
return {
|
|
463
505
|
sessions: paginatedSessions,
|
|
@@ -471,12 +513,14 @@ export function createRouter(ctx) {
|
|
|
471
513
|
hostSessionIndex.startWatchers();
|
|
472
514
|
hostSessionIndexInitialized = true;
|
|
473
515
|
}
|
|
474
|
-
const
|
|
516
|
+
const record = await resolveSessionRecord(sessionId);
|
|
517
|
+
const agentSessionId = record?.agentSessionId || sessionId;
|
|
518
|
+
const session = hostSessionIndex.get(agentSessionId);
|
|
475
519
|
if (!session) {
|
|
476
520
|
return { id: sessionId, messages: [] };
|
|
477
521
|
}
|
|
478
|
-
const result = await hostSessionIndex.getMessages(
|
|
479
|
-
const agentType = session.agentType
|
|
522
|
+
const result = await hostSessionIndex.getMessages(agentSessionId, { limit: 10000, offset: 0 });
|
|
523
|
+
const agentType = toClientAgentType(session.agentType);
|
|
480
524
|
const messages = result.messages.map((m) => ({
|
|
481
525
|
type: m.type,
|
|
482
526
|
content: m.content,
|
|
@@ -485,7 +529,13 @@ export function createRouter(ctx) {
|
|
|
485
529
|
toolInput: m.toolInput,
|
|
486
530
|
timestamp: m.timestamp,
|
|
487
531
|
}));
|
|
488
|
-
|
|
532
|
+
const ensured = record ||
|
|
533
|
+
(await ensureRegistrySession(HOST_WORKSPACE_NAME, session.agentType, agentSessionId, {
|
|
534
|
+
projectPath: session.agentType === 'claude'
|
|
535
|
+
? decodeClaudeProjectPath(session.directory)
|
|
536
|
+
: session.directory,
|
|
537
|
+
}));
|
|
538
|
+
return { id: ensured.perrySessionId, agentType, messages, agentSessionId };
|
|
489
539
|
}
|
|
490
540
|
async function listSessionsCore(input) {
|
|
491
541
|
const limit = input.limit ?? 50;
|
|
@@ -508,20 +558,13 @@ export function createRouter(ctx) {
|
|
|
508
558
|
const containerName = `workspace-${input.workspaceName}`;
|
|
509
559
|
const rawSessions = await discoverAllSessions(containerName, execInContainer);
|
|
510
560
|
const customNames = await getSessionNamesForWorkspace(ctx.stateDir, input.workspaceName);
|
|
511
|
-
const
|
|
512
|
-
const trackedAgentIds = new Set(registrySessions.filter((s) => s.agentSessionId).map((s) => s.agentSessionId));
|
|
561
|
+
const registryByAgentId = new Map();
|
|
513
562
|
for (const raw of rawSessions) {
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
agentType: agentType,
|
|
520
|
-
agentSessionId: raw.id,
|
|
521
|
-
createdAt: new Date(raw.mtime).toISOString(),
|
|
522
|
-
lastActivity: new Date(raw.mtime).toISOString(),
|
|
523
|
-
});
|
|
524
|
-
}
|
|
563
|
+
const record = await ensureRegistrySession(input.workspaceName, raw.agentType, raw.id, {
|
|
564
|
+
createdAt: new Date(raw.mtime).toISOString(),
|
|
565
|
+
lastActivity: new Date(raw.mtime).toISOString(),
|
|
566
|
+
});
|
|
567
|
+
registryByAgentId.set(raw.id, record);
|
|
525
568
|
}
|
|
526
569
|
const filteredSessions = rawSessions
|
|
527
570
|
.filter((s) => !input.agentType || s.agentType === input.agentType)
|
|
@@ -530,10 +573,21 @@ export function createRouter(ctx) {
|
|
|
530
573
|
const detailsResults = await Promise.all(paginatedRawSessions.map((rawSession) => getAgentSessionDetails(containerName, rawSession, execInContainer)));
|
|
531
574
|
const sessions = detailsResults
|
|
532
575
|
.filter((details) => details !== null)
|
|
533
|
-
.map((details) =>
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
576
|
+
.map((details) => {
|
|
577
|
+
const record = registryByAgentId.get(details.id);
|
|
578
|
+
const perryId = record?.perrySessionId || details.id;
|
|
579
|
+
const name = customNames[perryId] || customNames[details.id] || details.name;
|
|
580
|
+
const projectPath = details.agentType === 'claude-code'
|
|
581
|
+
? decodeClaudeProjectPath(details.projectPath)
|
|
582
|
+
: details.projectPath;
|
|
583
|
+
return {
|
|
584
|
+
...details,
|
|
585
|
+
id: perryId,
|
|
586
|
+
agentSessionId: details.id,
|
|
587
|
+
name,
|
|
588
|
+
projectPath,
|
|
589
|
+
};
|
|
590
|
+
});
|
|
537
591
|
return {
|
|
538
592
|
sessions,
|
|
539
593
|
total: filteredSessions.length,
|
|
@@ -578,9 +632,35 @@ export function createRouter(ctx) {
|
|
|
578
632
|
throw new ORPCError('PRECONDITION_FAILED', { message: 'Workspace is not running' });
|
|
579
633
|
}
|
|
580
634
|
const containerName = `workspace-${input.workspaceName}`;
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
635
|
+
const record = await resolveSessionRecord(input.sessionId);
|
|
636
|
+
if (record && !record.agentSessionId) {
|
|
637
|
+
throw new ORPCError('NOT_FOUND', { message: 'Session not found' });
|
|
638
|
+
}
|
|
639
|
+
const agentSessionId = record?.agentSessionId || input.sessionId;
|
|
640
|
+
const resolvedAgentType = record?.agentType
|
|
641
|
+
? toClientAgentType(record.agentType)
|
|
642
|
+
: input.agentType;
|
|
643
|
+
result = resolvedAgentType
|
|
644
|
+
? await getSessionMessages(containerName, agentSessionId, resolvedAgentType, execInContainer, input.projectPath)
|
|
645
|
+
: await findSessionMessages(containerName, agentSessionId, execInContainer);
|
|
646
|
+
if (result && !record) {
|
|
647
|
+
const agentType = toRegistryAgentType(result.agentType || resolvedAgentType);
|
|
648
|
+
const created = await ensureRegistrySession(input.workspaceName, agentType, result.id, {
|
|
649
|
+
projectPath: input.projectPath,
|
|
650
|
+
});
|
|
651
|
+
result = {
|
|
652
|
+
...result,
|
|
653
|
+
id: created.perrySessionId,
|
|
654
|
+
agentSessionId: result.id,
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
else if (result && record) {
|
|
658
|
+
result = {
|
|
659
|
+
...result,
|
|
660
|
+
id: record.perrySessionId,
|
|
661
|
+
agentSessionId: record.agentSessionId || agentSessionId,
|
|
662
|
+
};
|
|
663
|
+
}
|
|
584
664
|
}
|
|
585
665
|
if (!result) {
|
|
586
666
|
throw new ORPCError('NOT_FOUND', { message: 'Session not found' });
|
|
@@ -651,14 +731,18 @@ export function createRouter(ctx) {
|
|
|
651
731
|
if (!config.allowHostAccess) {
|
|
652
732
|
throw new ORPCError('PRECONDITION_FAILED', { message: 'Host access is disabled' });
|
|
653
733
|
}
|
|
654
|
-
const
|
|
734
|
+
const record = await resolveSessionRecord(input.sessionId);
|
|
735
|
+
const agentSessionId = record?.agentSessionId || input.sessionId;
|
|
736
|
+
const agentType = record?.agentType ? toClientAgentType(record.agentType) : input.agentType;
|
|
737
|
+
const result = await deleteHostSession(agentSessionId, agentType);
|
|
655
738
|
if (!result.success) {
|
|
656
739
|
throw new ORPCError('INTERNAL_SERVER_ERROR', {
|
|
657
740
|
message: result.error || 'Failed to delete session',
|
|
658
741
|
});
|
|
659
742
|
}
|
|
660
|
-
|
|
661
|
-
await ctx.
|
|
743
|
+
const perryId = record?.perrySessionId || input.sessionId;
|
|
744
|
+
await deleteSessionName(ctx.stateDir, input.workspaceName, perryId);
|
|
745
|
+
await ctx.sessionsCache.removeSession(input.workspaceName, perryId);
|
|
662
746
|
return { success: true };
|
|
663
747
|
}
|
|
664
748
|
const workspace = await ctx.workspaces.get(input.workspaceName);
|
|
@@ -669,14 +753,18 @@ export function createRouter(ctx) {
|
|
|
669
753
|
throw new ORPCError('PRECONDITION_FAILED', { message: 'Workspace is not running' });
|
|
670
754
|
}
|
|
671
755
|
const containerName = `workspace-${input.workspaceName}`;
|
|
672
|
-
const
|
|
756
|
+
const record = await resolveSessionRecord(input.sessionId);
|
|
757
|
+
const agentSessionId = record?.agentSessionId || input.sessionId;
|
|
758
|
+
const agentType = record?.agentType ? toClientAgentType(record.agentType) : input.agentType;
|
|
759
|
+
const result = await deleteSessionFromProvider(containerName, agentSessionId, agentType, execInContainer);
|
|
673
760
|
if (!result.success) {
|
|
674
761
|
throw new ORPCError('INTERNAL_SERVER_ERROR', {
|
|
675
762
|
message: result.error || 'Failed to delete session',
|
|
676
763
|
});
|
|
677
764
|
}
|
|
678
|
-
|
|
679
|
-
await ctx.
|
|
765
|
+
const perryId = record?.perrySessionId || input.sessionId;
|
|
766
|
+
await deleteSessionName(ctx.stateDir, input.workspaceName, perryId);
|
|
767
|
+
await ctx.sessionsCache.removeSession(input.workspaceName, perryId);
|
|
680
768
|
return { success: true };
|
|
681
769
|
});
|
|
682
770
|
const searchSessions = os
|
|
@@ -702,7 +790,15 @@ export function createRouter(ctx) {
|
|
|
702
790
|
throw new ORPCError('PRECONDITION_FAILED', { message: 'Workspace is not running' });
|
|
703
791
|
}
|
|
704
792
|
const containerName = `workspace-${input.workspaceName}`;
|
|
705
|
-
const
|
|
793
|
+
const rawResults = await searchSessionsInContainer(containerName, input.query, execInContainer);
|
|
794
|
+
const results = await Promise.all(rawResults.map(async (result) => {
|
|
795
|
+
const record = await ensureRegistrySession(input.workspaceName, result.agentType, result.sessionId);
|
|
796
|
+
return {
|
|
797
|
+
...result,
|
|
798
|
+
sessionId: record.perrySessionId,
|
|
799
|
+
agentSessionId: result.sessionId,
|
|
800
|
+
};
|
|
801
|
+
}));
|
|
706
802
|
return { results };
|
|
707
803
|
});
|
|
708
804
|
async function searchHostSessions(query) {
|
|
@@ -759,7 +855,13 @@ export function createRouter(ctx) {
|
|
|
759
855
|
}
|
|
760
856
|
}
|
|
761
857
|
if (sessionId && agentType) {
|
|
762
|
-
|
|
858
|
+
const record = await ensureRegistrySession(HOST_WORKSPACE_NAME, agentType, sessionId);
|
|
859
|
+
results.push({
|
|
860
|
+
sessionId: record.perrySessionId,
|
|
861
|
+
agentSessionId: sessionId,
|
|
862
|
+
agentType,
|
|
863
|
+
matchCount: 1,
|
|
864
|
+
});
|
|
763
865
|
}
|
|
764
866
|
}
|
|
765
867
|
return results;
|