letagents 0.12.8 → 0.12.10
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 +16 -15
- package/dist/mcp/agent-prompt-delivery.js +20 -0
- package/dist/mcp/codex-session/app-server.js +89 -0
- package/dist/mcp/codex-session/index.js +10 -0
- package/dist/mcp/codex-session/rpc-client.js +85 -0
- package/dist/mcp/codex-session/runtime-bridge.js +254 -0
- package/dist/mcp/codex-session/runtime-summary.js +220 -0
- package/dist/mcp/codex-session/session-inspection.js +100 -0
- package/dist/mcp/codex-session/session-mapper.js +51 -0
- package/dist/mcp/codex-session/session-start.js +182 -0
- package/dist/mcp/codex-session/session-status.js +77 -0
- package/dist/mcp/codex-session/session-stop.js +43 -0
- package/dist/mcp/codex-session/session-supervisor.js +111 -0
- package/dist/mcp/codex-session/start-prompt.js +49 -0
- package/dist/mcp/codex-session/types.js +1 -0
- package/dist/mcp/codex-session.js +1 -500
- package/dist/mcp/config-reader.js +10 -2
- package/dist/mcp/git-remote.js +90 -14
- package/dist/mcp/local-state/agent-identity.js +26 -0
- package/dist/mcp/local-state/agent-sessions.js +66 -0
- package/dist/mcp/local-state/auth.js +57 -0
- package/dist/mcp/local-state/codex-live-sessions.js +59 -0
- package/dist/mcp/local-state/index.js +8 -0
- package/dist/mcp/local-state/local-chat.js +808 -0
- package/dist/mcp/local-state/room-sessions.js +51 -0
- package/dist/mcp/local-state/storage.js +95 -0
- package/dist/mcp/local-state/types.js +1 -0
- package/dist/mcp/local-state.js +1 -286
- package/dist/mcp/managed-agent-providers.js +65 -0
- package/dist/mcp/rental-tools/activity.js +37 -0
- package/dist/mcp/rental-tools/budget.js +36 -0
- package/dist/mcp/rental-tools/context.js +62 -0
- package/dist/mcp/rental-tools/lifecycle.js +47 -0
- package/dist/mcp/rental-tools/metering.js +73 -0
- package/dist/mcp/rental-tools/patches.js +100 -0
- package/dist/mcp/rental-tools/provider.js +117 -0
- package/dist/mcp/rental-tools/shared.js +19 -0
- package/dist/mcp/rental-tools/types.js +1 -0
- package/dist/mcp/rental-tools.js +7 -0
- package/dist/mcp/repo-visibility.js +8 -6
- package/dist/mcp/server/register-tools.js +19 -0
- package/dist/mcp/server/repo-context.js +28 -0
- package/dist/mcp/server/resources.js +77 -0
- package/dist/mcp/server/runtime/agent-sessions.js +134 -0
- package/dist/mcp/server/runtime/api.js +82 -0
- package/dist/mcp/server/runtime/auth-cache.js +20 -0
- package/dist/mcp/server/runtime/device-auth.js +48 -0
- package/dist/mcp/server/runtime/identity/config.js +58 -0
- package/dist/mcp/server/runtime/identity/directory.js +87 -0
- package/dist/mcp/server/runtime/identity/liveness.js +32 -0
- package/dist/mcp/server/runtime/identity/names.js +74 -0
- package/dist/mcp/server/runtime/identity/public.js +19 -0
- package/dist/mcp/server/runtime/identity/state.js +32 -0
- package/dist/mcp/server/runtime/identity.js +96 -0
- package/dist/mcp/server/runtime/messages.js +160 -0
- package/dist/mcp/server/runtime/presence.js +52 -0
- package/dist/mcp/server/runtime/room-api.js +42 -0
- package/dist/mcp/server/runtime/room-state.js +122 -0
- package/dist/mcp/server/runtime/rooms.js +318 -0
- package/dist/mcp/server/runtime.js +14 -0
- package/dist/mcp/server/tools/agent-sessions.js +374 -0
- package/dist/mcp/server/tools/messages/index.js +14 -0
- package/dist/mcp/server/tools/messages/read-tool.js +176 -0
- package/dist/mcp/server/tools/messages/reasoning-tool.js +179 -0
- package/dist/mcp/server/tools/messages/response.js +17 -0
- package/dist/mcp/server/tools/messages/send-tool.js +203 -0
- package/dist/mcp/server/tools/messages/status-tool.js +87 -0
- package/dist/mcp/server/tools/messages/wait-tool.js +333 -0
- package/dist/mcp/server/tools/messages.js +1 -0
- package/dist/mcp/server/tools/onboarding/device-auth-tools.js +148 -0
- package/dist/mcp/server/tools/onboarding/name-tool.js +89 -0
- package/dist/mcp/server/tools/onboarding/responses.js +10 -0
- package/dist/mcp/server/tools/onboarding/status-tool.js +108 -0
- package/dist/mcp/server/tools/onboarding.js +8 -0
- package/dist/mcp/server/tools/rental/activity-tools.js +43 -0
- package/dist/mcp/server/tools/rental/context-tools.js +35 -0
- package/dist/mcp/server/tools/rental/index.js +17 -0
- package/dist/mcp/server/tools/rental/metering-tools.js +39 -0
- package/dist/mcp/server/tools/rental/patch-tools.js +53 -0
- package/dist/mcp/server/tools/rental/provider-tools.js +44 -0
- package/dist/mcp/server/tools/rental/response.js +10 -0
- package/dist/mcp/server/tools/rental/types.js +1 -0
- package/dist/mcp/server/tools/rental.js +1 -0
- package/dist/mcp/server/tools/rooms/index.js +5 -0
- package/dist/mcp/server/tools/rooms/inspection-tools.js +124 -0
- package/dist/mcp/server/tools/rooms/join-tools.js +37 -0
- package/dist/mcp/server/tools/rooms/repo-initialization-tool.js +113 -0
- package/dist/mcp/server/tools/rooms/repo-visibility-tool.js +20 -0
- package/dist/mcp/server/tools/rooms/response.js +10 -0
- package/dist/mcp/server/tools/rooms/resume-tool.js +48 -0
- package/dist/mcp/server/tools/rooms.js +1 -0
- package/dist/mcp/server/tools/tasks/api.js +222 -0
- package/dist/mcp/server/tools/tasks/artifact-tools.js +75 -0
- package/dist/mcp/server/tools/tasks/board-intent-tools.js +321 -0
- package/dist/mcp/server/tools/tasks/board-tools.js +73 -0
- package/dist/mcp/server/tools/tasks/context.js +35 -0
- package/dist/mcp/server/tools/tasks/event-tools.js +40 -0
- package/dist/mcp/server/tools/tasks/index.js +14 -0
- package/dist/mcp/server/tools/tasks/lease-tools.js +145 -0
- package/dist/mcp/server/tools/tasks/mutation-tools.js +121 -0
- package/dist/mcp/server/tools/tasks/response.js +13 -0
- package/dist/mcp/server/tools/tasks/schemas.js +68 -0
- package/dist/mcp/server/tools/tasks.js +1 -0
- package/dist/mcp/server.js +10 -2540
- package/dist/mcp/sse-client.js +22 -16
- package/dist/shared/activation-routing.js +263 -0
- package/dist/shared/agent-identity.js +21 -5
- package/dist/shared/agent-presence.js +24 -0
- package/dist/shared/handoff.js +145 -0
- package/dist/shared/rental/lrt.js +214 -0
- package/dist/shared/rental/meter-types.js +14 -0
- package/dist/shared/request-headers.js +3 -0
- package/dist/shared/room-agent-activity.js +41 -0
- package/dist/shared/room-agent-prompts.js +14 -1
- package/package.json +15 -5
- /package/dist/{mcp → shared}/codenames.js +0 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { normalizeAgentPromptKind } from "../../../shared/room-agent-prompts.js";
|
|
2
|
+
import { dispenseRoomAgentPrompt } from "../../agent-prompt-delivery.js";
|
|
3
|
+
export function getLastMessageId(payload) {
|
|
4
|
+
if (!payload || typeof payload !== "object") {
|
|
5
|
+
return undefined;
|
|
6
|
+
}
|
|
7
|
+
const messages = payload.messages;
|
|
8
|
+
const lastMessage = messages?.at(-1);
|
|
9
|
+
return typeof lastMessage?.id === "string" ? lastMessage.id : undefined;
|
|
10
|
+
}
|
|
11
|
+
export function withJoinRoomAgentPrompt(payload) {
|
|
12
|
+
return {
|
|
13
|
+
...payload,
|
|
14
|
+
agent_prompt_kind: "join",
|
|
15
|
+
agent_prompt: dispenseRoomAgentPrompt("join"),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function isRecord(value) {
|
|
19
|
+
return Boolean(value && typeof value === "object");
|
|
20
|
+
}
|
|
21
|
+
function messageId(record) {
|
|
22
|
+
return typeof record.id === "string" && record.id.trim() ? record.id : null;
|
|
23
|
+
}
|
|
24
|
+
function replyReference(record) {
|
|
25
|
+
return record.reply_to ?? record.replyTo ?? null;
|
|
26
|
+
}
|
|
27
|
+
function replyReferenceId(record) {
|
|
28
|
+
const reply = replyReference(record);
|
|
29
|
+
if (typeof reply === "string" && reply.trim()) {
|
|
30
|
+
return reply;
|
|
31
|
+
}
|
|
32
|
+
if (isRecord(reply) && typeof reply.id === "string" && reply.id.trim()) {
|
|
33
|
+
return reply.id;
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
function resolveThreadParentId(record, recordsById) {
|
|
38
|
+
const ownId = messageId(record);
|
|
39
|
+
const firstReplyId = replyReferenceId(record);
|
|
40
|
+
if (!firstReplyId) {
|
|
41
|
+
return ownId;
|
|
42
|
+
}
|
|
43
|
+
let parentId = firstReplyId;
|
|
44
|
+
const seen = new Set(ownId ? [ownId] : []);
|
|
45
|
+
while (parentId && !seen.has(parentId)) {
|
|
46
|
+
seen.add(parentId);
|
|
47
|
+
const parent = recordsById.get(parentId);
|
|
48
|
+
const nextParentId = parent ? replyReferenceId(parent) : null;
|
|
49
|
+
if (!nextParentId)
|
|
50
|
+
break;
|
|
51
|
+
parentId = nextParentId;
|
|
52
|
+
}
|
|
53
|
+
return parentId;
|
|
54
|
+
}
|
|
55
|
+
function explicitThreadRootId(record) {
|
|
56
|
+
const root = record.thread_root_id ?? record.threadRootId;
|
|
57
|
+
return typeof root === "string" && root.trim() ? root : null;
|
|
58
|
+
}
|
|
59
|
+
function explicitThreadReplyToId(record) {
|
|
60
|
+
const replyTo = record.thread_reply_to_id ?? record.threadReplyToId;
|
|
61
|
+
return typeof replyTo === "string" && replyTo.trim() ? replyTo : null;
|
|
62
|
+
}
|
|
63
|
+
// A message is a thread reply IFF it carries an explicit thread root that differs
|
|
64
|
+
// from its own id. `reply_to` alone is only a quote/chip reference, never proof of
|
|
65
|
+
// thread membership — so a bare quote-reply (explicit root === its own id) stays
|
|
66
|
+
// top-level. Records that carry NO explicit thread_root_id field at all (out-of-band
|
|
67
|
+
// or legacy payloads that never went through the message mappers) fall back to
|
|
68
|
+
// deriving the root from the reply_to chain so genuine threads still survive. The
|
|
69
|
+
// fallback keys on field ABSENCE, never on the value equalling the own id.
|
|
70
|
+
function resolveThreadRootId(record, recordsById) {
|
|
71
|
+
const explicitRoot = explicitThreadRootId(record);
|
|
72
|
+
if (explicitRoot) {
|
|
73
|
+
return explicitRoot;
|
|
74
|
+
}
|
|
75
|
+
return resolveThreadParentId(record, recordsById);
|
|
76
|
+
}
|
|
77
|
+
function buildThreadSummaries(records, recordsById) {
|
|
78
|
+
const summaries = new Map();
|
|
79
|
+
for (const record of records) {
|
|
80
|
+
const id = messageId(record);
|
|
81
|
+
const rootId = resolveThreadRootId(record, recordsById);
|
|
82
|
+
// Only genuine thread replies (an explicit root different from the message
|
|
83
|
+
// itself) accrue to a thread. A bare quote-reply roots at itself and must not
|
|
84
|
+
// inflate the reply count of the message it quotes.
|
|
85
|
+
if (!id || !rootId || rootId === id)
|
|
86
|
+
continue;
|
|
87
|
+
const current = summaries.get(rootId) ?? { count: 0, latestReplyId: null };
|
|
88
|
+
summaries.set(rootId, {
|
|
89
|
+
count: current.count + 1,
|
|
90
|
+
latestReplyId: id,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
return summaries;
|
|
94
|
+
}
|
|
95
|
+
function withThreadMetadata(record, recordsById, summaries) {
|
|
96
|
+
const ownId = messageId(record);
|
|
97
|
+
const rootId = resolveThreadRootId(record, recordsById);
|
|
98
|
+
if (!rootId) {
|
|
99
|
+
return record;
|
|
100
|
+
}
|
|
101
|
+
// The quote/chip reference is preserved regardless of thread membership.
|
|
102
|
+
const replyToId = explicitThreadReplyToId(record) ?? replyReferenceId(record);
|
|
103
|
+
const isThreadReply = Boolean(ownId && rootId !== ownId);
|
|
104
|
+
const summary = summaries.get(rootId) ?? { count: 0, latestReplyId: null };
|
|
105
|
+
const { thread_parent_id: _threadParentId, thread_root_id: _threadRootId, threadRootId: _threadRootIdCamel, thread_reply_to_id: _threadReplyToId, threadReplyToId: _threadReplyToIdCamel, thread: _thread, ...baseRecord } = record;
|
|
106
|
+
return {
|
|
107
|
+
...baseRecord,
|
|
108
|
+
...(isThreadReply
|
|
109
|
+
? {
|
|
110
|
+
thread_parent_id: rootId,
|
|
111
|
+
thread_root_id: rootId,
|
|
112
|
+
thread_reply_to_id: replyToId,
|
|
113
|
+
}
|
|
114
|
+
: {}),
|
|
115
|
+
thread: {
|
|
116
|
+
parent_id: rootId,
|
|
117
|
+
root_message_id: rootId,
|
|
118
|
+
reply_to_id: replyToId,
|
|
119
|
+
is_thread_reply: isThreadReply,
|
|
120
|
+
reply_count_in_result: summary.count,
|
|
121
|
+
latest_reply_id_in_result: summary.latestReplyId,
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function toAgentReadableMessage(message, recordsById, summaries) {
|
|
126
|
+
if (!message || typeof message !== "object") {
|
|
127
|
+
return message;
|
|
128
|
+
}
|
|
129
|
+
const record = withThreadMetadata(message, recordsById, summaries);
|
|
130
|
+
const kind = normalizeAgentPromptKind(record.agent_prompt_kind);
|
|
131
|
+
const text = typeof record.text === "string" ? record.text : null;
|
|
132
|
+
if (!kind || text === null) {
|
|
133
|
+
return record;
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
...record,
|
|
137
|
+
visible_text: text,
|
|
138
|
+
agent_prompt: dispenseRoomAgentPrompt(kind),
|
|
139
|
+
prompt_injected: kind === "inline",
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
export function toAgentReadableMessages(messages, contextMessages = []) {
|
|
143
|
+
const records = (messages ?? []).filter(isRecord);
|
|
144
|
+
const contextRecords = contextMessages.filter(isRecord);
|
|
145
|
+
const recordsById = new Map();
|
|
146
|
+
for (const record of [...contextRecords, ...records]) {
|
|
147
|
+
const id = messageId(record);
|
|
148
|
+
if (id)
|
|
149
|
+
recordsById.set(id, record);
|
|
150
|
+
}
|
|
151
|
+
const summaries = buildThreadSummaries(records, recordsById);
|
|
152
|
+
return (messages ?? []).map((message) => toAgentReadableMessage(message, recordsById, summaries));
|
|
153
|
+
}
|
|
154
|
+
export function appendIncludePromptOnly(path) {
|
|
155
|
+
return `${path}${path.includes("?") ? "&" : "?"}include_prompt_only=1`;
|
|
156
|
+
}
|
|
157
|
+
export function normalizeOptionalToolString(value) {
|
|
158
|
+
const trimmed = value?.trim();
|
|
159
|
+
return trimmed ? trimmed : undefined;
|
|
160
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { getRoomIdentityPresenceCacheKey } from "../../agent-presence.js";
|
|
2
|
+
import { encodeRoomIdPath } from "../../room-id.js";
|
|
3
|
+
import { isLocalRoomStorageEnabled, resolveLocalRoomStorageIdentifiers, touchRoomSession, } from "../../local-state.js";
|
|
4
|
+
import { apiCall, isMissingRouteError } from "./api.js";
|
|
5
|
+
import { agentSessionCredentials, identityFromAgentSession } from "./agent-sessions.js";
|
|
6
|
+
import { getSessionLivenessRegistration } from "./identity.js";
|
|
7
|
+
const roomPresenceByIdentity = new Map();
|
|
8
|
+
export function getRememberedRoomPresence(roomId, identity) {
|
|
9
|
+
if (!roomId || !identity) {
|
|
10
|
+
return { status: "idle", status_text: null };
|
|
11
|
+
}
|
|
12
|
+
return (roomPresenceByIdentity.get(getRoomIdentityPresenceCacheKey(roomId, identity.actor_label)) ?? { status: "idle", status_text: null });
|
|
13
|
+
}
|
|
14
|
+
export async function syncRoomPresence(roomId, identity, presence, agentSession) {
|
|
15
|
+
const resolvedIdentity = agentSession ? identityFromAgentSession(agentSession) : identity;
|
|
16
|
+
if (!roomId || !resolvedIdentity || !agentSession) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
roomPresenceByIdentity.set(getRoomIdentityPresenceCacheKey(roomId, resolvedIdentity.actor_label), presence);
|
|
20
|
+
const { localRoomId, cloudRoomId } = await resolveLocalRoomStorageIdentifiers(roomId);
|
|
21
|
+
if (await isLocalRoomStorageEnabled(roomId)) {
|
|
22
|
+
touchRoomSession(localRoomId || roomId);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const apiRoomId = cloudRoomId || roomId;
|
|
26
|
+
try {
|
|
27
|
+
await apiCall(`/rooms/${encodeRoomIdPath(apiRoomId)}/presence`, {
|
|
28
|
+
method: "POST",
|
|
29
|
+
body: JSON.stringify({
|
|
30
|
+
actor_label: resolvedIdentity.actor_label,
|
|
31
|
+
agent_key: resolvedIdentity.canonical_key,
|
|
32
|
+
display_name: resolvedIdentity.display_name,
|
|
33
|
+
owner_label: resolvedIdentity.owner_label,
|
|
34
|
+
ide_label: resolvedIdentity.ide_label,
|
|
35
|
+
status: presence.status,
|
|
36
|
+
status_text: presence.status_text,
|
|
37
|
+
liveness_observation: getSessionLivenessRegistration(),
|
|
38
|
+
...agentSessionCredentials(agentSession),
|
|
39
|
+
}),
|
|
40
|
+
});
|
|
41
|
+
touchRoomSession(apiRoomId);
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
if (isMissingRouteError(error)) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export async function heartbeatRoomPresence(roomId, identity) {
|
|
51
|
+
await syncRoomPresence(roomId, identity, getRememberedRoomPresence(roomId, identity));
|
|
52
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { resolveLocalRoomStorageIdentifiers, touchRoomSession, } from "../../local-state.js";
|
|
2
|
+
import { LETAGENTS_ORIGIN_ROOM_ID_HEADER } from "../../../shared/request-headers.js";
|
|
3
|
+
import { apiCall, isMissingRouteError, } from "./api.js";
|
|
4
|
+
import { maybeHandleRepoRoomAuthRequired } from "./device-auth.js";
|
|
5
|
+
import { getLastMessageId } from "./messages.js";
|
|
6
|
+
import { currentRoom } from "./room-state.js";
|
|
7
|
+
export async function roomScopedApiCall(input) {
|
|
8
|
+
const headers = {
|
|
9
|
+
...input.options?.headers,
|
|
10
|
+
};
|
|
11
|
+
if (currentRoom?.room_id &&
|
|
12
|
+
!Object.keys(headers).some((key) => key.toLowerCase() === LETAGENTS_ORIGIN_ROOM_ID_HEADER.toLowerCase())) {
|
|
13
|
+
headers[LETAGENTS_ORIGIN_ROOM_ID_HEADER] = currentRoom.room_id;
|
|
14
|
+
}
|
|
15
|
+
const options = {
|
|
16
|
+
...input.options,
|
|
17
|
+
headers,
|
|
18
|
+
};
|
|
19
|
+
if (input.room_id) {
|
|
20
|
+
const { cloudRoomId } = await resolveLocalRoomStorageIdentifiers(input.room_id);
|
|
21
|
+
const apiRoomId = cloudRoomId || input.room_id;
|
|
22
|
+
try {
|
|
23
|
+
const result = await apiCall(input.room_path(apiRoomId), options);
|
|
24
|
+
touchRoomSession(input.room_id, input.preserve_session_cursor ? undefined : getLastMessageId(result));
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
await maybeHandleRepoRoomAuthRequired(error, apiRoomId);
|
|
29
|
+
if (!input.project_id || !isMissingRouteError(error)) {
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (!input.project_id) {
|
|
35
|
+
throw new Error("No room is available for this request.");
|
|
36
|
+
}
|
|
37
|
+
const result = await apiCall(input.project_path(input.project_id), options);
|
|
38
|
+
if (input.room_id) {
|
|
39
|
+
touchRoomSession(input.room_id, input.preserve_session_cursor ? undefined : getLastMessageId(result));
|
|
40
|
+
}
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { SseClient } from "../../sse-client.js";
|
|
2
|
+
import { getStoredAgentIdentity, saveRoomSession, touchRoomSession, } from "../../local-state.js";
|
|
3
|
+
import { getCanonicalRoomWebPath, } from "../../room-id.js";
|
|
4
|
+
import { API_URL, getLetagentsToken } from "./api.js";
|
|
5
|
+
import { AGENT_INSTANCE_UUID, currentAgentIdentity, currentAgentIdentityKey, } from "./identity.js";
|
|
6
|
+
let mcpServer = null;
|
|
7
|
+
let sseClient = null;
|
|
8
|
+
export let currentRoom = null;
|
|
9
|
+
export function attachMcpServer(server) {
|
|
10
|
+
mcpServer = server;
|
|
11
|
+
}
|
|
12
|
+
export function shutdownRuntime() {
|
|
13
|
+
sseClient?.unsubscribeAll();
|
|
14
|
+
}
|
|
15
|
+
function getSseClient() {
|
|
16
|
+
sseClient ??= new SseClient(API_URL, () => getLetagentsToken());
|
|
17
|
+
return sseClient;
|
|
18
|
+
}
|
|
19
|
+
function getCurrentStreamAgentIdentity() {
|
|
20
|
+
const identity = currentAgentIdentity ?? getStoredAgentIdentity(currentAgentIdentityKey);
|
|
21
|
+
if (!identity?.actor_label || !identity.canonical_key) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
actorLabel: identity.actor_label,
|
|
26
|
+
actorKey: identity.canonical_key,
|
|
27
|
+
actorInstanceId: AGENT_INSTANCE_UUID,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export function toRoomState(input) {
|
|
31
|
+
return {
|
|
32
|
+
room_id: input.room_id,
|
|
33
|
+
project_id: input.project_id ?? null,
|
|
34
|
+
code: input.code ?? null,
|
|
35
|
+
display_name: input.display_name ?? null,
|
|
36
|
+
git_room: input.git_room ?? null,
|
|
37
|
+
joined_via: input.joined_via,
|
|
38
|
+
is_local: input.is_local ?? false,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function getCanonicalRoomWebUrl(roomId) {
|
|
42
|
+
return new URL(getCanonicalRoomWebPath(roomId), `${API_URL}/`).toString();
|
|
43
|
+
}
|
|
44
|
+
export function withCanonicalRoomLink(roomId, payload) {
|
|
45
|
+
return {
|
|
46
|
+
...payload,
|
|
47
|
+
room_path: getCanonicalRoomWebPath(roomId),
|
|
48
|
+
room_url: getCanonicalRoomWebUrl(roomId),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export function toPublicRoomState(state) {
|
|
52
|
+
if (!state) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
return withCanonicalRoomLink(state.room_id, {
|
|
56
|
+
room_id: state.room_id,
|
|
57
|
+
code: state.code ?? null,
|
|
58
|
+
display_name: state.display_name ?? null,
|
|
59
|
+
git_room: state.git_room ?? null,
|
|
60
|
+
joined_via: state.joined_via,
|
|
61
|
+
is_local: state.is_local ?? false,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
export function toPublicStoredRoomSession(session) {
|
|
65
|
+
if (!session) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
return withCanonicalRoomLink(session.room_id, {
|
|
69
|
+
room_id: session.room_id,
|
|
70
|
+
code: session.code ?? null,
|
|
71
|
+
display_name: session.display_name ?? null,
|
|
72
|
+
git_room: session.git_room ?? null,
|
|
73
|
+
joined_via: session.joined_via,
|
|
74
|
+
joined_at: session.joined_at,
|
|
75
|
+
last_seen_at: session.last_seen_at,
|
|
76
|
+
last_message_id: session.last_message_id ?? null,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
export function toPublicRoomResponse(response, fallbackRoomId) {
|
|
80
|
+
const { id: _legacyId, project_id: _legacyProjectId, ...rest } = response;
|
|
81
|
+
return {
|
|
82
|
+
...withCanonicalRoomLink(typeof rest.room_id === "string" ? rest.room_id : fallbackRoomId, rest),
|
|
83
|
+
room_id: typeof rest.room_id === "string" ? rest.room_id : fallbackRoomId,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
export function rememberRoom(state, lastMessageId) {
|
|
87
|
+
currentRoom = state;
|
|
88
|
+
saveRoomSession({
|
|
89
|
+
room_id: state.room_id,
|
|
90
|
+
project_id: state.project_id ?? null,
|
|
91
|
+
code: state.code ?? null,
|
|
92
|
+
display_name: state.display_name ?? null,
|
|
93
|
+
git_room: state.git_room ?? null,
|
|
94
|
+
joined_via: state.joined_via,
|
|
95
|
+
last_message_id: lastMessageId,
|
|
96
|
+
});
|
|
97
|
+
getSseClient().unsubscribeAll();
|
|
98
|
+
if (state.is_local) {
|
|
99
|
+
return state;
|
|
100
|
+
}
|
|
101
|
+
getSseClient().subscribe({
|
|
102
|
+
roomId: state.room_id,
|
|
103
|
+
projectId: state.project_id ?? null,
|
|
104
|
+
agentIdentity: getCurrentStreamAgentIdentity(),
|
|
105
|
+
}, (_message) => {
|
|
106
|
+
touchRoomSession(state.room_id);
|
|
107
|
+
mcpServer?.server.sendResourceListChanged();
|
|
108
|
+
});
|
|
109
|
+
return state;
|
|
110
|
+
}
|
|
111
|
+
export function touchCurrentRoom(lastMessageId) {
|
|
112
|
+
if (!currentRoom) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
touchRoomSession(currentRoom.room_id, lastMessageId);
|
|
116
|
+
}
|
|
117
|
+
export function getTargetRoomId(roomId) {
|
|
118
|
+
return roomId || currentRoom?.room_id || null;
|
|
119
|
+
}
|
|
120
|
+
export function getFallbackProjectId() {
|
|
121
|
+
return currentRoom?.project_id ?? null;
|
|
122
|
+
}
|