agent-relay 1.0.8 → 1.0.9
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/README.md +158 -0
- package/dist/bridge/config.d.ts +41 -0
- package/dist/bridge/config.d.ts.map +1 -0
- package/dist/bridge/config.js +143 -0
- package/dist/bridge/config.js.map +1 -0
- package/dist/bridge/index.d.ts +10 -0
- package/dist/bridge/index.d.ts.map +1 -0
- package/dist/bridge/index.js +10 -0
- package/dist/bridge/index.js.map +1 -0
- package/dist/bridge/multi-project-client.d.ts +99 -0
- package/dist/bridge/multi-project-client.d.ts.map +1 -0
- package/dist/bridge/multi-project-client.js +386 -0
- package/dist/bridge/multi-project-client.js.map +1 -0
- package/dist/bridge/spawner.d.ts +46 -0
- package/dist/bridge/spawner.d.ts.map +1 -0
- package/dist/bridge/spawner.js +223 -0
- package/dist/bridge/spawner.js.map +1 -0
- package/dist/bridge/types.d.ts +55 -0
- package/dist/bridge/types.d.ts.map +1 -0
- package/dist/bridge/types.js +6 -0
- package/dist/bridge/types.js.map +1 -0
- package/dist/bridge/utils.d.ts +30 -0
- package/dist/bridge/utils.d.ts.map +1 -0
- package/dist/bridge/utils.js +54 -0
- package/dist/bridge/utils.js.map +1 -0
- package/dist/cli/index.js +564 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/daemon/agent-registry.d.ts.map +1 -1
- package/dist/daemon/agent-registry.js +6 -1
- package/dist/daemon/agent-registry.js.map +1 -1
- package/dist/daemon/connection.d.ts +22 -0
- package/dist/daemon/connection.d.ts.map +1 -1
- package/dist/daemon/connection.js +59 -13
- package/dist/daemon/connection.js.map +1 -1
- package/dist/daemon/router.d.ts +27 -0
- package/dist/daemon/router.d.ts.map +1 -1
- package/dist/daemon/router.js +108 -3
- package/dist/daemon/router.js.map +1 -1
- package/dist/daemon/server.d.ts +8 -0
- package/dist/daemon/server.d.ts.map +1 -1
- package/dist/daemon/server.js +95 -23
- package/dist/daemon/server.js.map +1 -1
- package/dist/dashboard/metrics.d.ts +105 -0
- package/dist/dashboard/metrics.d.ts.map +1 -0
- package/dist/dashboard/metrics.js +192 -0
- package/dist/dashboard/metrics.js.map +1 -0
- package/dist/dashboard/needs-attention.d.ts +24 -0
- package/dist/dashboard/needs-attention.d.ts.map +1 -0
- package/dist/dashboard/needs-attention.js +78 -0
- package/dist/dashboard/needs-attention.js.map +1 -0
- package/dist/dashboard/public/bridge.html +1272 -0
- package/dist/dashboard/public/index.html +2017 -879
- package/dist/dashboard/public/js/app.js +184 -0
- package/dist/dashboard/public/js/app.js.map +7 -0
- package/dist/dashboard/public/metrics.html +999 -0
- package/dist/dashboard/server.d.ts +13 -0
- package/dist/dashboard/server.d.ts.map +1 -1
- package/dist/dashboard/server.js +568 -13
- package/dist/dashboard/server.js.map +1 -1
- package/dist/dashboard/start.js +1 -1
- package/dist/dashboard/start.js.map +1 -1
- package/dist/dashboard-v2/index.d.ts +10 -0
- package/dist/dashboard-v2/index.d.ts.map +1 -0
- package/dist/dashboard-v2/index.js +54 -0
- package/dist/dashboard-v2/index.js.map +1 -0
- package/dist/dashboard-v2/lib/api.d.ts +95 -0
- package/dist/dashboard-v2/lib/api.d.ts.map +1 -0
- package/dist/dashboard-v2/lib/api.js +270 -0
- package/dist/dashboard-v2/lib/api.js.map +1 -0
- package/dist/dashboard-v2/lib/colors.d.ts +61 -0
- package/dist/dashboard-v2/lib/colors.d.ts.map +1 -0
- package/dist/dashboard-v2/lib/colors.js +198 -0
- package/dist/dashboard-v2/lib/colors.js.map +1 -0
- package/dist/dashboard-v2/lib/hierarchy.d.ts +74 -0
- package/dist/dashboard-v2/lib/hierarchy.d.ts.map +1 -0
- package/dist/dashboard-v2/lib/hierarchy.js +196 -0
- package/dist/dashboard-v2/lib/hierarchy.js.map +1 -0
- package/dist/dashboard-v2/types/index.d.ts +154 -0
- package/dist/dashboard-v2/types/index.d.ts.map +1 -0
- package/dist/dashboard-v2/types/index.js +6 -0
- package/dist/dashboard-v2/types/index.js.map +1 -0
- package/dist/storage/adapter.d.ts +21 -1
- package/dist/storage/adapter.d.ts.map +1 -1
- package/dist/storage/adapter.js +36 -0
- package/dist/storage/adapter.js.map +1 -1
- package/dist/storage/sqlite-adapter.d.ts +34 -0
- package/dist/storage/sqlite-adapter.d.ts.map +1 -1
- package/dist/storage/sqlite-adapter.js +253 -12
- package/dist/storage/sqlite-adapter.js.map +1 -1
- package/dist/utils/agent-config.d.ts +45 -0
- package/dist/utils/agent-config.d.ts.map +1 -0
- package/dist/utils/agent-config.js +118 -0
- package/dist/utils/agent-config.js.map +1 -0
- package/dist/wrapper/client.d.ts +8 -0
- package/dist/wrapper/client.d.ts.map +1 -1
- package/dist/wrapper/client.js +26 -0
- package/dist/wrapper/client.js.map +1 -1
- package/dist/wrapper/parser.d.ts +17 -0
- package/dist/wrapper/parser.d.ts.map +1 -1
- package/dist/wrapper/parser.js +334 -10
- package/dist/wrapper/parser.js.map +1 -1
- package/dist/wrapper/tmux-wrapper.d.ts +37 -2
- package/dist/wrapper/tmux-wrapper.d.ts.map +1 -1
- package/dist/wrapper/tmux-wrapper.js +178 -18
- package/dist/wrapper/tmux-wrapper.js.map +1 -1
- package/docs/AGENTS.md +105 -0
- package/docs/ARCHITECTURE_DECISIONS.md +175 -0
- package/docs/COMPETITIVE_ANALYSIS.md +897 -0
- package/docs/DESIGN_BRIDGE_STAFFING.md +878 -0
- package/docs/MONETIZATION.md +1679 -0
- package/docs/agent-relay-snippet.md +61 -0
- package/docs/dashboard-v2-plan.md +179 -0
- package/package.json +5 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PayloadKind } from '../protocol/types.js';
|
|
1
|
+
import type { PayloadKind, SendMeta } from '../protocol/types.js';
|
|
2
2
|
export type MessageStatus = 'unread' | 'read' | 'acked';
|
|
3
3
|
export interface StoredMessage {
|
|
4
4
|
id: string;
|
|
@@ -9,6 +9,8 @@ export interface StoredMessage {
|
|
|
9
9
|
kind: PayloadKind;
|
|
10
10
|
body: string;
|
|
11
11
|
data?: Record<string, unknown>;
|
|
12
|
+
/** Optional metadata (importance, replyTo, etc.) */
|
|
13
|
+
payloadMeta?: SendMeta;
|
|
12
14
|
/** Optional thread ID for grouping related messages */
|
|
13
15
|
thread?: string;
|
|
14
16
|
deliverySeq?: number;
|
|
@@ -18,6 +20,8 @@ export interface StoredMessage {
|
|
|
18
20
|
status: MessageStatus;
|
|
19
21
|
/** Whether the message is marked as urgent */
|
|
20
22
|
is_urgent: boolean;
|
|
23
|
+
/** Whether the message was sent as a broadcast (to: '*') */
|
|
24
|
+
is_broadcast?: boolean;
|
|
21
25
|
}
|
|
22
26
|
export interface MessageQuery {
|
|
23
27
|
limit?: number;
|
|
@@ -43,6 +47,7 @@ export interface StoredSession {
|
|
|
43
47
|
endedAt?: number;
|
|
44
48
|
messageCount: number;
|
|
45
49
|
summary?: string;
|
|
50
|
+
resumeToken?: string;
|
|
46
51
|
/** How the session was closed: 'agent' (explicit), 'disconnect', 'error', or undefined (still active) */
|
|
47
52
|
closedBy?: 'agent' | 'disconnect' | 'error';
|
|
48
53
|
}
|
|
@@ -67,6 +72,7 @@ export interface StorageAdapter {
|
|
|
67
72
|
saveMessage(message: StoredMessage): Promise<void>;
|
|
68
73
|
getMessages(query?: MessageQuery): Promise<StoredMessage[]>;
|
|
69
74
|
getMessageById?(id: string): Promise<StoredMessage | null>;
|
|
75
|
+
updateMessageStatus?(id: string, status: MessageStatus): Promise<void>;
|
|
70
76
|
close?(): Promise<void>;
|
|
71
77
|
startSession?(session: Omit<StoredSession, 'messageCount'>): Promise<void>;
|
|
72
78
|
endSession?(sessionId: string, options?: {
|
|
@@ -76,9 +82,16 @@ export interface StorageAdapter {
|
|
|
76
82
|
getSessions?(query?: SessionQuery): Promise<StoredSession[]>;
|
|
77
83
|
getRecentSessions?(limit?: number): Promise<StoredSession[]>;
|
|
78
84
|
incrementSessionMessageCount?(sessionId: string): Promise<void>;
|
|
85
|
+
getSessionByResumeToken?(resumeToken: string): Promise<StoredSession | null>;
|
|
79
86
|
saveAgentSummary?(summary: Omit<AgentSummary, 'lastUpdated'>): Promise<void>;
|
|
80
87
|
getAgentSummary?(agentName: string): Promise<AgentSummary | null>;
|
|
81
88
|
getAllAgentSummaries?(): Promise<AgentSummary[]>;
|
|
89
|
+
getPendingMessagesForSession?(agentName: string, sessionId: string): Promise<StoredMessage[]>;
|
|
90
|
+
getMaxSeqByStream?(agentName: string, sessionId: string): Promise<Array<{
|
|
91
|
+
peer: string;
|
|
92
|
+
topic?: string;
|
|
93
|
+
maxSeq: number;
|
|
94
|
+
}>>;
|
|
82
95
|
}
|
|
83
96
|
/**
|
|
84
97
|
* Storage configuration options.
|
|
@@ -102,6 +115,13 @@ export declare class MemoryStorageAdapter implements StorageAdapter {
|
|
|
102
115
|
saveMessage(message: StoredMessage): Promise<void>;
|
|
103
116
|
getMessages(query?: MessageQuery): Promise<StoredMessage[]>;
|
|
104
117
|
getMessageById(id: string): Promise<StoredMessage | null>;
|
|
118
|
+
updateMessageStatus(id: string, status: MessageStatus): Promise<void>;
|
|
119
|
+
getPendingMessagesForSession(agentName: string, sessionId: string): Promise<StoredMessage[]>;
|
|
120
|
+
getMaxSeqByStream(agentName: string, sessionId: string): Promise<Array<{
|
|
121
|
+
peer: string;
|
|
122
|
+
topic?: string;
|
|
123
|
+
maxSeq: number;
|
|
124
|
+
}>>;
|
|
105
125
|
close(): Promise<void>;
|
|
106
126
|
}
|
|
107
127
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/storage/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/storage/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAElE,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAExD,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,oDAAoD;IACpD,WAAW,CAAC,EAAE,QAAQ,CAAC;IACvB,uDAAuD;IACvD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mCAAmC;IACnC,MAAM,EAAE,aAAa,CAAC;IACtB,8CAA8C;IAC9C,SAAS,EAAE,OAAO,CAAC;IACnB,4DAA4D;IAC5D,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0BAA0B;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACvB,mCAAmC;IACnC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,mCAAmC;IACnC,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yGAAyG;IACzG,QAAQ,CAAC,EAAE,OAAO,GAAG,YAAY,GAAG,OAAO,CAAC;CAC7C;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,WAAW,CAAC,KAAK,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IAC5D,cAAc,CAAC,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAC3D,mBAAmB,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAGxB,YAAY,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,aAAa,EAAE,cAAc,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3E,UAAU,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,YAAY,GAAG,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3H,WAAW,CAAC,CAAC,KAAK,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IAC7D,iBAAiB,CAAC,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IAC7D,4BAA4B,CAAC,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,uBAAuB,CAAC,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAG7E,gBAAgB,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,YAAY,EAAE,aAAa,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,eAAe,CAAC,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAClE,oBAAoB,CAAC,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IAGjD,4BAA4B,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IAC9F,iBAAiB,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;CAC5H;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+BAA+B;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,qBAAa,oBAAqB,YAAW,cAAc;IACzD,OAAO,CAAC,QAAQ,CAAuB;IAEjC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAQlD,WAAW,CAAC,KAAK,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAgC3D,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAKzD,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAOrE,4BAA4B,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAc5F,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAmBzH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B;AAED;;GAEG;AACH,wBAAgB,uBAAuB,IAAI,aAAa,CAMvD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,aAAa,GACrB,OAAO,CAAC,cAAc,CAAC,CAyCzB"}
|
package/dist/storage/adapter.js
CHANGED
|
@@ -46,6 +46,42 @@ export class MemoryStorageAdapter {
|
|
|
46
46
|
// Support both exact match and prefix match (for short IDs)
|
|
47
47
|
return this.messages.find(m => m.id === id || m.id.startsWith(id)) ?? null;
|
|
48
48
|
}
|
|
49
|
+
async updateMessageStatus(id, status) {
|
|
50
|
+
const msg = this.messages.find(m => m.id === id || m.id.startsWith(id));
|
|
51
|
+
if (msg) {
|
|
52
|
+
msg.status = status;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async getPendingMessagesForSession(agentName, sessionId) {
|
|
56
|
+
return this.messages
|
|
57
|
+
.filter(m => m.to === agentName && m.deliverySessionId === sessionId && m.status !== 'acked')
|
|
58
|
+
.sort((a, b) => {
|
|
59
|
+
const seqA = mSeq(a);
|
|
60
|
+
const seqB = mSeq(b);
|
|
61
|
+
return seqA === seqB ? a.ts - b.ts : seqA - seqB;
|
|
62
|
+
});
|
|
63
|
+
function mSeq(msg) {
|
|
64
|
+
return msg.deliverySeq ?? 0;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async getMaxSeqByStream(agentName, sessionId) {
|
|
68
|
+
const aggregates = new Map();
|
|
69
|
+
for (const msg of this.messages) {
|
|
70
|
+
if (msg.to !== agentName)
|
|
71
|
+
continue;
|
|
72
|
+
if (msg.deliverySessionId !== sessionId)
|
|
73
|
+
continue;
|
|
74
|
+
if (msg.deliverySeq === undefined || msg.deliverySeq === null)
|
|
75
|
+
continue;
|
|
76
|
+
const topic = msg.topic ?? 'default';
|
|
77
|
+
const key = `${topic}:${msg.from}`;
|
|
78
|
+
const current = aggregates.get(key);
|
|
79
|
+
if (!current || msg.deliverySeq > current.maxSeq) {
|
|
80
|
+
aggregates.set(key, { peer: msg.from, topic: msg.topic, maxSeq: msg.deliverySeq });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return Array.from(aggregates.values());
|
|
84
|
+
}
|
|
49
85
|
async close() {
|
|
50
86
|
this.messages = [];
|
|
51
87
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../src/storage/adapter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../src/storage/adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -41,6 +41,12 @@ export declare class SqliteStorageAdapter implements StorageAdapter {
|
|
|
41
41
|
getMessages(query?: MessageQuery): Promise<StoredMessage[]>;
|
|
42
42
|
updateMessageStatus(id: string, status: MessageStatus): Promise<void>;
|
|
43
43
|
getMessageById(id: string): Promise<StoredMessage | null>;
|
|
44
|
+
getPendingMessagesForSession(agentName: string, sessionId: string): Promise<StoredMessage[]>;
|
|
45
|
+
getMaxSeqByStream(agentName: string, sessionId: string): Promise<Array<{
|
|
46
|
+
peer: string;
|
|
47
|
+
topic?: string;
|
|
48
|
+
maxSeq: number;
|
|
49
|
+
}>>;
|
|
44
50
|
close(): Promise<void>;
|
|
45
51
|
startSession(session: Omit<StoredSession, 'messageCount'>): Promise<void>;
|
|
46
52
|
/**
|
|
@@ -58,6 +64,7 @@ export declare class SqliteStorageAdapter implements StorageAdapter {
|
|
|
58
64
|
incrementSessionMessageCount(sessionId: string): Promise<void>;
|
|
59
65
|
getSessions(query?: SessionQuery): Promise<StoredSession[]>;
|
|
60
66
|
getRecentSessions(limit?: number): Promise<StoredSession[]>;
|
|
67
|
+
getSessionByResumeToken(resumeToken: string): Promise<StoredSession | null>;
|
|
61
68
|
saveAgentSummary(summary: {
|
|
62
69
|
agentName: string;
|
|
63
70
|
projectId?: string;
|
|
@@ -69,5 +76,32 @@ export declare class SqliteStorageAdapter implements StorageAdapter {
|
|
|
69
76
|
}): Promise<void>;
|
|
70
77
|
getAgentSummary(agentName: string): Promise<AgentSummary | null>;
|
|
71
78
|
getAllAgentSummaries(): Promise<AgentSummary[]>;
|
|
79
|
+
updatePresence(presence: {
|
|
80
|
+
agentName: string;
|
|
81
|
+
status: 'online' | 'away' | 'busy' | 'offline';
|
|
82
|
+
statusText?: string;
|
|
83
|
+
typingIn?: string;
|
|
84
|
+
}): Promise<void>;
|
|
85
|
+
getPresence(agentName: string): Promise<{
|
|
86
|
+
agentName: string;
|
|
87
|
+
status: 'online' | 'away' | 'busy' | 'offline';
|
|
88
|
+
statusText?: string;
|
|
89
|
+
lastActivity: number;
|
|
90
|
+
typingIn?: string;
|
|
91
|
+
} | null>;
|
|
92
|
+
getAllPresence(): Promise<Array<{
|
|
93
|
+
agentName: string;
|
|
94
|
+
status: 'online' | 'away' | 'busy' | 'offline';
|
|
95
|
+
statusText?: string;
|
|
96
|
+
lastActivity: number;
|
|
97
|
+
typingIn?: string;
|
|
98
|
+
}>>;
|
|
99
|
+
setTypingIndicator(agentName: string, channel: string | null): Promise<void>;
|
|
100
|
+
updateReadState(agentName: string, channel: string, lastReadTs: number, lastReadId?: string): Promise<void>;
|
|
101
|
+
getReadState(agentName: string, channel: string): Promise<{
|
|
102
|
+
lastReadTs: number;
|
|
103
|
+
lastReadId?: string;
|
|
104
|
+
} | null>;
|
|
105
|
+
getUnreadCounts(agentName: string): Promise<Record<string, number>>;
|
|
72
106
|
}
|
|
73
107
|
//# sourceMappingURL=sqlite-adapter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite-adapter.d.ts","sourceRoot":"","sources":["../../src/storage/sqlite-adapter.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,aAAa,EACnB,MAAM,cAAc,CAAC;AAEtB,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,4EAA4E;IAC5E,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAQD,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAiBhE,qBAAa,oBAAqB,YAAW,cAAc;IACzD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,EAAE,CAAC,CAAiB;IAC5B,OAAO,CAAC,UAAU,CAAC,CAAkB;IACrC,OAAO,CAAC,iBAAiB,CAAC,CAAkB;IAC5C,OAAO,CAAC,MAAM,CAAC,CAAmB;IAClC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,YAAY,CAAC,CAAiC;gBAE1C,OAAO,EAAE,oBAAoB;IAMzC,OAAO,CAAC,sBAAsB;YAQhB,YAAY;IAqBpB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"sqlite-adapter.d.ts","sourceRoot":"","sources":["../../src/storage/sqlite-adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -87,12 +87,14 @@ export class SqliteStorageAdapter {
|
|
|
87
87
|
kind TEXT NOT NULL,
|
|
88
88
|
body TEXT NOT NULL,
|
|
89
89
|
data TEXT,
|
|
90
|
+
payload_meta TEXT,
|
|
90
91
|
thread TEXT,
|
|
91
92
|
delivery_seq INTEGER,
|
|
92
93
|
delivery_session_id TEXT,
|
|
93
94
|
session_id TEXT,
|
|
94
95
|
status TEXT NOT NULL DEFAULT 'unread',
|
|
95
|
-
is_urgent INTEGER NOT NULL DEFAULT 0
|
|
96
|
+
is_urgent INTEGER NOT NULL DEFAULT 0,
|
|
97
|
+
is_broadcast INTEGER NOT NULL DEFAULT 0
|
|
96
98
|
);
|
|
97
99
|
CREATE INDEX idx_messages_ts ON messages (ts);
|
|
98
100
|
CREATE INDEX idx_messages_sender ON messages (sender);
|
|
@@ -111,6 +113,9 @@ export class SqliteStorageAdapter {
|
|
|
111
113
|
this.db.exec('ALTER TABLE messages ADD COLUMN thread TEXT');
|
|
112
114
|
this.db.exec('CREATE INDEX IF NOT EXISTS idx_messages_thread ON messages (thread)');
|
|
113
115
|
}
|
|
116
|
+
if (!columnNames.has('payload_meta')) {
|
|
117
|
+
this.db.exec('ALTER TABLE messages ADD COLUMN payload_meta TEXT');
|
|
118
|
+
}
|
|
114
119
|
if (!columnNames.has('status')) {
|
|
115
120
|
this.db.exec("ALTER TABLE messages ADD COLUMN status TEXT NOT NULL DEFAULT 'unread'");
|
|
116
121
|
this.db.exec('CREATE INDEX IF NOT EXISTS idx_messages_status ON messages (status)');
|
|
@@ -119,8 +124,12 @@ export class SqliteStorageAdapter {
|
|
|
119
124
|
this.db.exec("ALTER TABLE messages ADD COLUMN is_urgent INTEGER NOT NULL DEFAULT 0");
|
|
120
125
|
this.db.exec('CREATE INDEX IF NOT EXISTS idx_messages_is_urgent ON messages (is_urgent)');
|
|
121
126
|
}
|
|
127
|
+
if (!columnNames.has('is_broadcast')) {
|
|
128
|
+
this.db.exec("ALTER TABLE messages ADD COLUMN is_broadcast INTEGER NOT NULL DEFAULT 0");
|
|
129
|
+
}
|
|
122
130
|
}
|
|
123
|
-
// Create sessions table (IF NOT EXISTS is safe here
|
|
131
|
+
// Create sessions table (IF NOT EXISTS is safe here)
|
|
132
|
+
// Note: Don't create resume_token index here - it's created after migration check
|
|
124
133
|
this.db.exec(`
|
|
125
134
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
126
135
|
id TEXT PRIMARY KEY,
|
|
@@ -132,12 +141,21 @@ export class SqliteStorageAdapter {
|
|
|
132
141
|
ended_at INTEGER,
|
|
133
142
|
message_count INTEGER DEFAULT 0,
|
|
134
143
|
summary TEXT,
|
|
144
|
+
resume_token TEXT,
|
|
135
145
|
closed_by TEXT
|
|
136
146
|
);
|
|
137
147
|
CREATE INDEX IF NOT EXISTS idx_sessions_agent ON sessions (agent_name);
|
|
138
148
|
CREATE INDEX IF NOT EXISTS idx_sessions_started ON sessions (started_at);
|
|
139
149
|
CREATE INDEX IF NOT EXISTS idx_sessions_project ON sessions (project_id);
|
|
140
150
|
`);
|
|
151
|
+
// Migrate existing sessions table to add resume_token if missing
|
|
152
|
+
const sessionColumns = this.db.prepare("PRAGMA table_info(sessions)").all();
|
|
153
|
+
const sessionColumnNames = new Set(sessionColumns.map(c => c.name));
|
|
154
|
+
if (!sessionColumnNames.has('resume_token')) {
|
|
155
|
+
this.db.exec('ALTER TABLE sessions ADD COLUMN resume_token TEXT');
|
|
156
|
+
}
|
|
157
|
+
// Create index after ensuring column exists (either from CREATE TABLE or migration)
|
|
158
|
+
this.db.exec('CREATE INDEX IF NOT EXISTS idx_sessions_resume_token ON sessions (resume_token)');
|
|
141
159
|
// Create agent_summaries table (IF NOT EXISTS is safe here - no new columns to migrate)
|
|
142
160
|
this.db.exec(`
|
|
143
161
|
CREATE TABLE IF NOT EXISTS agent_summaries (
|
|
@@ -151,11 +169,33 @@ export class SqliteStorageAdapter {
|
|
|
151
169
|
files TEXT
|
|
152
170
|
);
|
|
153
171
|
CREATE INDEX IF NOT EXISTS idx_summaries_updated ON agent_summaries (last_updated);
|
|
172
|
+
`);
|
|
173
|
+
// Create presence table for real-time status tracking
|
|
174
|
+
this.db.exec(`
|
|
175
|
+
CREATE TABLE IF NOT EXISTS presence (
|
|
176
|
+
agent_name TEXT PRIMARY KEY,
|
|
177
|
+
status TEXT NOT NULL DEFAULT 'offline',
|
|
178
|
+
status_text TEXT,
|
|
179
|
+
last_activity INTEGER NOT NULL,
|
|
180
|
+
typing_in TEXT
|
|
181
|
+
);
|
|
182
|
+
CREATE INDEX IF NOT EXISTS idx_presence_status ON presence (status);
|
|
183
|
+
CREATE INDEX IF NOT EXISTS idx_presence_activity ON presence (last_activity);
|
|
184
|
+
`);
|
|
185
|
+
// Create read_state table for tracking last read message per channel/conversation
|
|
186
|
+
this.db.exec(`
|
|
187
|
+
CREATE TABLE IF NOT EXISTS read_state (
|
|
188
|
+
agent_name TEXT NOT NULL,
|
|
189
|
+
channel TEXT NOT NULL,
|
|
190
|
+
last_read_ts INTEGER NOT NULL,
|
|
191
|
+
last_read_id TEXT,
|
|
192
|
+
PRIMARY KEY (agent_name, channel)
|
|
193
|
+
);
|
|
154
194
|
`);
|
|
155
195
|
this.insertStmt = this.db.prepare(`
|
|
156
196
|
INSERT OR REPLACE INTO messages
|
|
157
|
-
(id, ts, sender, recipient, topic, kind, body, data, thread, delivery_seq, delivery_session_id, session_id, status, is_urgent)
|
|
158
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
197
|
+
(id, ts, sender, recipient, topic, kind, body, data, payload_meta, thread, delivery_seq, delivery_session_id, session_id, status, is_urgent, is_broadcast)
|
|
198
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
159
199
|
`);
|
|
160
200
|
// Start automatic cleanup if enabled
|
|
161
201
|
if (this.cleanupIntervalMs > 0) {
|
|
@@ -214,7 +254,7 @@ export class SqliteStorageAdapter {
|
|
|
214
254
|
if (!this.db || !this.insertStmt) {
|
|
215
255
|
throw new Error('SqliteStorageAdapter not initialized');
|
|
216
256
|
}
|
|
217
|
-
this.insertStmt.run(message.id, message.ts, message.from, message.to, message.topic ?? null, message.kind, message.body, message.data ? JSON.stringify(message.data) : null, message.thread ?? null, message.deliverySeq ?? null, message.deliverySessionId ?? null, message.sessionId ?? null, message.status, message.is_urgent ? 1 : 0);
|
|
257
|
+
this.insertStmt.run(message.id, message.ts, message.from, message.to, message.topic ?? null, message.kind, message.body, message.data ? JSON.stringify(message.data) : null, message.payloadMeta ? JSON.stringify(message.payloadMeta) : null, message.thread ?? null, message.deliverySeq ?? null, message.deliverySessionId ?? null, message.sessionId ?? null, message.status, message.is_urgent ? 1 : 0, message.is_broadcast ? 1 : 0);
|
|
218
258
|
}
|
|
219
259
|
async getMessages(query = {}) {
|
|
220
260
|
if (!this.db) {
|
|
@@ -254,7 +294,7 @@ export class SqliteStorageAdapter {
|
|
|
254
294
|
const order = query.order === 'asc' ? 'ASC' : 'DESC';
|
|
255
295
|
const limit = query.limit ?? 200;
|
|
256
296
|
const stmt = this.db.prepare(`
|
|
257
|
-
SELECT id, ts, sender, recipient, topic, kind, body, data, thread, delivery_seq, delivery_session_id, session_id, status, is_urgent
|
|
297
|
+
SELECT id, ts, sender, recipient, topic, kind, body, data, payload_meta, thread, delivery_seq, delivery_session_id, session_id, status, is_urgent, is_broadcast
|
|
258
298
|
FROM messages
|
|
259
299
|
${where}
|
|
260
300
|
ORDER BY ts ${order}
|
|
@@ -270,12 +310,14 @@ export class SqliteStorageAdapter {
|
|
|
270
310
|
kind: row.kind,
|
|
271
311
|
body: row.body,
|
|
272
312
|
data: row.data ? JSON.parse(row.data) : undefined,
|
|
313
|
+
payloadMeta: row.payload_meta ? JSON.parse(row.payload_meta) : undefined,
|
|
273
314
|
thread: row.thread ?? undefined,
|
|
274
315
|
deliverySeq: row.delivery_seq ?? undefined,
|
|
275
316
|
deliverySessionId: row.delivery_session_id ?? undefined,
|
|
276
317
|
sessionId: row.session_id ?? undefined,
|
|
277
318
|
status: row.status,
|
|
278
319
|
is_urgent: row.is_urgent === 1,
|
|
320
|
+
is_broadcast: row.is_broadcast === 1,
|
|
279
321
|
}));
|
|
280
322
|
}
|
|
281
323
|
async updateMessageStatus(id, status) {
|
|
@@ -291,7 +333,7 @@ export class SqliteStorageAdapter {
|
|
|
291
333
|
}
|
|
292
334
|
// Support both exact match and prefix match (for short IDs like "06eb33da")
|
|
293
335
|
const stmt = this.db.prepare(`
|
|
294
|
-
SELECT id, ts, sender, recipient, topic, kind, body, data, thread, delivery_seq, delivery_session_id, session_id, status, is_urgent
|
|
336
|
+
SELECT id, ts, sender, recipient, topic, kind, body, data, payload_meta, thread, delivery_seq, delivery_session_id, session_id, status, is_urgent, is_broadcast
|
|
295
337
|
FROM messages
|
|
296
338
|
WHERE id = ? OR id LIKE ?
|
|
297
339
|
ORDER BY ts DESC
|
|
@@ -309,14 +351,63 @@ export class SqliteStorageAdapter {
|
|
|
309
351
|
kind: row.kind,
|
|
310
352
|
body: row.body,
|
|
311
353
|
data: row.data ? JSON.parse(row.data) : undefined,
|
|
354
|
+
payloadMeta: row.payload_meta ? JSON.parse(row.payload_meta) : undefined,
|
|
312
355
|
thread: row.thread ?? undefined,
|
|
313
356
|
deliverySeq: row.delivery_seq ?? undefined,
|
|
314
357
|
deliverySessionId: row.delivery_session_id ?? undefined,
|
|
315
358
|
sessionId: row.session_id ?? undefined,
|
|
316
359
|
status: row.status ?? 'unread',
|
|
317
360
|
is_urgent: row.is_urgent === 1,
|
|
361
|
+
is_broadcast: row.is_broadcast === 1,
|
|
318
362
|
};
|
|
319
363
|
}
|
|
364
|
+
async getPendingMessagesForSession(agentName, sessionId) {
|
|
365
|
+
if (!this.db) {
|
|
366
|
+
throw new Error('SqliteStorageAdapter not initialized');
|
|
367
|
+
}
|
|
368
|
+
const stmt = this.db.prepare(`
|
|
369
|
+
SELECT id, ts, sender, recipient, topic, kind, body, data, payload_meta, thread, delivery_seq, delivery_session_id, session_id, status, is_urgent, is_broadcast
|
|
370
|
+
FROM messages
|
|
371
|
+
WHERE recipient = ? AND delivery_session_id = ? AND status != 'acked'
|
|
372
|
+
ORDER BY delivery_seq ASC, ts ASC
|
|
373
|
+
`);
|
|
374
|
+
const rows = stmt.all(agentName, sessionId);
|
|
375
|
+
return rows.map((row) => ({
|
|
376
|
+
id: row.id,
|
|
377
|
+
ts: row.ts,
|
|
378
|
+
from: row.sender,
|
|
379
|
+
to: row.recipient,
|
|
380
|
+
topic: row.topic ?? undefined,
|
|
381
|
+
kind: row.kind,
|
|
382
|
+
body: row.body,
|
|
383
|
+
data: row.data ? JSON.parse(row.data) : undefined,
|
|
384
|
+
payloadMeta: row.payload_meta ? JSON.parse(row.payload_meta) : undefined,
|
|
385
|
+
thread: row.thread ?? undefined,
|
|
386
|
+
deliverySeq: row.delivery_seq ?? undefined,
|
|
387
|
+
deliverySessionId: row.delivery_session_id ?? undefined,
|
|
388
|
+
sessionId: row.session_id ?? undefined,
|
|
389
|
+
status: row.status ?? 'unread',
|
|
390
|
+
is_urgent: row.is_urgent === 1,
|
|
391
|
+
is_broadcast: row.is_broadcast === 1,
|
|
392
|
+
}));
|
|
393
|
+
}
|
|
394
|
+
async getMaxSeqByStream(agentName, sessionId) {
|
|
395
|
+
if (!this.db) {
|
|
396
|
+
throw new Error('SqliteStorageAdapter not initialized');
|
|
397
|
+
}
|
|
398
|
+
const stmt = this.db.prepare(`
|
|
399
|
+
SELECT sender, topic, MAX(delivery_seq) as max_seq
|
|
400
|
+
FROM messages
|
|
401
|
+
WHERE recipient = ? AND delivery_session_id = ? AND delivery_seq IS NOT NULL
|
|
402
|
+
GROUP BY sender, topic
|
|
403
|
+
`);
|
|
404
|
+
const rows = stmt.all(agentName, sessionId);
|
|
405
|
+
return rows.map(row => ({
|
|
406
|
+
peer: row.sender,
|
|
407
|
+
topic: row.topic ?? undefined,
|
|
408
|
+
maxSeq: row.max_seq,
|
|
409
|
+
}));
|
|
410
|
+
}
|
|
320
411
|
async close() {
|
|
321
412
|
// Stop cleanup timer
|
|
322
413
|
if (this.cleanupTimer) {
|
|
@@ -334,11 +425,22 @@ export class SqliteStorageAdapter {
|
|
|
334
425
|
throw new Error('SqliteStorageAdapter not initialized');
|
|
335
426
|
}
|
|
336
427
|
const stmt = this.db.prepare(`
|
|
337
|
-
INSERT
|
|
338
|
-
(id, agent_name, cli, project_id, project_root, started_at, ended_at, message_count, summary)
|
|
339
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
428
|
+
INSERT INTO sessions
|
|
429
|
+
(id, agent_name, cli, project_id, project_root, started_at, ended_at, message_count, summary, resume_token, closed_by)
|
|
430
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
431
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
432
|
+
agent_name = excluded.agent_name,
|
|
433
|
+
cli = COALESCE(excluded.cli, sessions.cli),
|
|
434
|
+
project_id = COALESCE(excluded.project_id, sessions.project_id),
|
|
435
|
+
project_root = COALESCE(excluded.project_root, sessions.project_root),
|
|
436
|
+
started_at = COALESCE(sessions.started_at, excluded.started_at),
|
|
437
|
+
ended_at = excluded.ended_at,
|
|
438
|
+
message_count = COALESCE(sessions.message_count, excluded.message_count),
|
|
439
|
+
summary = COALESCE(excluded.summary, sessions.summary),
|
|
440
|
+
resume_token = COALESCE(excluded.resume_token, sessions.resume_token),
|
|
441
|
+
closed_by = excluded.closed_by
|
|
340
442
|
`);
|
|
341
|
-
stmt.run(session.id, session.agentName, session.cli ?? null, session.projectId ?? null, session.projectRoot ?? null, session.startedAt, session.endedAt ?? null, 0, session.summary ?? null);
|
|
443
|
+
stmt.run(session.id, session.agentName, session.cli ?? null, session.projectId ?? null, session.projectRoot ?? null, session.startedAt, session.endedAt ?? null, 0, session.summary ?? null, session.resumeToken ?? null, null);
|
|
342
444
|
}
|
|
343
445
|
/**
|
|
344
446
|
* End a session and optionally set a summary.
|
|
@@ -389,7 +491,7 @@ export class SqliteStorageAdapter {
|
|
|
389
491
|
const where = clauses.length ? `WHERE ${clauses.join(' AND ')}` : '';
|
|
390
492
|
const limit = query.limit ?? 50;
|
|
391
493
|
const stmt = this.db.prepare(`
|
|
392
|
-
SELECT id, agent_name, cli, project_id, project_root, started_at, ended_at, message_count, summary, closed_by
|
|
494
|
+
SELECT id, agent_name, cli, project_id, project_root, started_at, ended_at, message_count, summary, resume_token, closed_by
|
|
393
495
|
FROM sessions
|
|
394
496
|
${where}
|
|
395
497
|
ORDER BY started_at DESC
|
|
@@ -406,12 +508,40 @@ export class SqliteStorageAdapter {
|
|
|
406
508
|
endedAt: row.ended_at ?? undefined,
|
|
407
509
|
messageCount: row.message_count,
|
|
408
510
|
summary: row.summary ?? undefined,
|
|
511
|
+
resumeToken: row.resume_token ?? undefined,
|
|
409
512
|
closedBy: row.closed_by ?? undefined,
|
|
410
513
|
}));
|
|
411
514
|
}
|
|
412
515
|
async getRecentSessions(limit = 10) {
|
|
413
516
|
return this.getSessions({ limit });
|
|
414
517
|
}
|
|
518
|
+
async getSessionByResumeToken(resumeToken) {
|
|
519
|
+
if (!this.db) {
|
|
520
|
+
throw new Error('SqliteStorageAdapter not initialized');
|
|
521
|
+
}
|
|
522
|
+
const row = this.db.prepare(`
|
|
523
|
+
SELECT id, agent_name, cli, project_id, project_root, started_at, ended_at, message_count, summary, resume_token, closed_by
|
|
524
|
+
FROM sessions
|
|
525
|
+
WHERE resume_token = ?
|
|
526
|
+
LIMIT 1
|
|
527
|
+
`).get(resumeToken);
|
|
528
|
+
if (!row) {
|
|
529
|
+
return null;
|
|
530
|
+
}
|
|
531
|
+
return {
|
|
532
|
+
id: row.id,
|
|
533
|
+
agentName: row.agent_name,
|
|
534
|
+
cli: row.cli ?? undefined,
|
|
535
|
+
projectId: row.project_id ?? undefined,
|
|
536
|
+
projectRoot: row.project_root ?? undefined,
|
|
537
|
+
startedAt: row.started_at,
|
|
538
|
+
endedAt: row.ended_at ?? undefined,
|
|
539
|
+
messageCount: row.message_count,
|
|
540
|
+
summary: row.summary ?? undefined,
|
|
541
|
+
resumeToken: row.resume_token ?? undefined,
|
|
542
|
+
closedBy: row.closed_by ?? undefined,
|
|
543
|
+
};
|
|
544
|
+
}
|
|
415
545
|
// ============ Agent Summaries ============
|
|
416
546
|
async saveAgentSummary(summary) {
|
|
417
547
|
if (!this.db) {
|
|
@@ -468,5 +598,116 @@ export class SqliteStorageAdapter {
|
|
|
468
598
|
files: row.files ? JSON.parse(row.files) : undefined,
|
|
469
599
|
}));
|
|
470
600
|
}
|
|
601
|
+
// ============ Presence Management ============
|
|
602
|
+
async updatePresence(presence) {
|
|
603
|
+
if (!this.db) {
|
|
604
|
+
throw new Error('SqliteStorageAdapter not initialized');
|
|
605
|
+
}
|
|
606
|
+
const stmt = this.db.prepare(`
|
|
607
|
+
INSERT OR REPLACE INTO presence
|
|
608
|
+
(agent_name, status, status_text, last_activity, typing_in)
|
|
609
|
+
VALUES (?, ?, ?, ?, ?)
|
|
610
|
+
`);
|
|
611
|
+
stmt.run(presence.agentName, presence.status, presence.statusText ?? null, Date.now(), presence.typingIn ?? null);
|
|
612
|
+
}
|
|
613
|
+
async getPresence(agentName) {
|
|
614
|
+
if (!this.db) {
|
|
615
|
+
throw new Error('SqliteStorageAdapter not initialized');
|
|
616
|
+
}
|
|
617
|
+
const stmt = this.db.prepare(`
|
|
618
|
+
SELECT agent_name, status, status_text, last_activity, typing_in
|
|
619
|
+
FROM presence
|
|
620
|
+
WHERE agent_name = ?
|
|
621
|
+
`);
|
|
622
|
+
const row = stmt.get(agentName);
|
|
623
|
+
if (!row)
|
|
624
|
+
return null;
|
|
625
|
+
return {
|
|
626
|
+
agentName: row.agent_name,
|
|
627
|
+
status: row.status,
|
|
628
|
+
statusText: row.status_text ?? undefined,
|
|
629
|
+
lastActivity: row.last_activity,
|
|
630
|
+
typingIn: row.typing_in ?? undefined,
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
async getAllPresence() {
|
|
634
|
+
if (!this.db) {
|
|
635
|
+
throw new Error('SqliteStorageAdapter not initialized');
|
|
636
|
+
}
|
|
637
|
+
const stmt = this.db.prepare(`
|
|
638
|
+
SELECT agent_name, status, status_text, last_activity, typing_in
|
|
639
|
+
FROM presence
|
|
640
|
+
ORDER BY last_activity DESC
|
|
641
|
+
`);
|
|
642
|
+
const rows = stmt.all();
|
|
643
|
+
return rows.map((row) => ({
|
|
644
|
+
agentName: row.agent_name,
|
|
645
|
+
status: row.status,
|
|
646
|
+
statusText: row.status_text ?? undefined,
|
|
647
|
+
lastActivity: row.last_activity,
|
|
648
|
+
typingIn: row.typing_in ?? undefined,
|
|
649
|
+
}));
|
|
650
|
+
}
|
|
651
|
+
async setTypingIndicator(agentName, channel) {
|
|
652
|
+
if (!this.db) {
|
|
653
|
+
throw new Error('SqliteStorageAdapter not initialized');
|
|
654
|
+
}
|
|
655
|
+
const stmt = this.db.prepare(`
|
|
656
|
+
UPDATE presence
|
|
657
|
+
SET typing_in = ?, last_activity = ?
|
|
658
|
+
WHERE agent_name = ?
|
|
659
|
+
`);
|
|
660
|
+
stmt.run(channel, Date.now(), agentName);
|
|
661
|
+
}
|
|
662
|
+
// ============ Read State Management ============
|
|
663
|
+
async updateReadState(agentName, channel, lastReadTs, lastReadId) {
|
|
664
|
+
if (!this.db) {
|
|
665
|
+
throw new Error('SqliteStorageAdapter not initialized');
|
|
666
|
+
}
|
|
667
|
+
const stmt = this.db.prepare(`
|
|
668
|
+
INSERT OR REPLACE INTO read_state
|
|
669
|
+
(agent_name, channel, last_read_ts, last_read_id)
|
|
670
|
+
VALUES (?, ?, ?, ?)
|
|
671
|
+
`);
|
|
672
|
+
stmt.run(agentName, channel, lastReadTs, lastReadId ?? null);
|
|
673
|
+
}
|
|
674
|
+
async getReadState(agentName, channel) {
|
|
675
|
+
if (!this.db) {
|
|
676
|
+
throw new Error('SqliteStorageAdapter not initialized');
|
|
677
|
+
}
|
|
678
|
+
const stmt = this.db.prepare(`
|
|
679
|
+
SELECT last_read_ts, last_read_id
|
|
680
|
+
FROM read_state
|
|
681
|
+
WHERE agent_name = ? AND channel = ?
|
|
682
|
+
`);
|
|
683
|
+
const row = stmt.get(agentName, channel);
|
|
684
|
+
if (!row)
|
|
685
|
+
return null;
|
|
686
|
+
return {
|
|
687
|
+
lastReadTs: row.last_read_ts,
|
|
688
|
+
lastReadId: row.last_read_id ?? undefined,
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
async getUnreadCounts(agentName) {
|
|
692
|
+
if (!this.db) {
|
|
693
|
+
throw new Error('SqliteStorageAdapter not initialized');
|
|
694
|
+
}
|
|
695
|
+
// Get all read states for this agent
|
|
696
|
+
const readStates = this.db.prepare(`
|
|
697
|
+
SELECT channel, last_read_ts FROM read_state WHERE agent_name = ?
|
|
698
|
+
`).all(agentName);
|
|
699
|
+
const counts = {};
|
|
700
|
+
// Count unread messages for each channel (conversation with agent)
|
|
701
|
+
for (const { channel, last_read_ts } of readStates) {
|
|
702
|
+
const count = this.db.prepare(`
|
|
703
|
+
SELECT COUNT(*) as count FROM messages
|
|
704
|
+
WHERE recipient = ? AND ts > ?
|
|
705
|
+
`).get(channel, last_read_ts);
|
|
706
|
+
if (count.count > 0) {
|
|
707
|
+
counts[channel] = count.count;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
return counts;
|
|
711
|
+
}
|
|
471
712
|
}
|
|
472
713
|
//# sourceMappingURL=sqlite-adapter.js.map
|