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.
Files changed (116) hide show
  1. package/README.md +16 -15
  2. package/dist/mcp/agent-prompt-delivery.js +20 -0
  3. package/dist/mcp/codex-session/app-server.js +89 -0
  4. package/dist/mcp/codex-session/index.js +10 -0
  5. package/dist/mcp/codex-session/rpc-client.js +85 -0
  6. package/dist/mcp/codex-session/runtime-bridge.js +254 -0
  7. package/dist/mcp/codex-session/runtime-summary.js +220 -0
  8. package/dist/mcp/codex-session/session-inspection.js +100 -0
  9. package/dist/mcp/codex-session/session-mapper.js +51 -0
  10. package/dist/mcp/codex-session/session-start.js +182 -0
  11. package/dist/mcp/codex-session/session-status.js +77 -0
  12. package/dist/mcp/codex-session/session-stop.js +43 -0
  13. package/dist/mcp/codex-session/session-supervisor.js +111 -0
  14. package/dist/mcp/codex-session/start-prompt.js +49 -0
  15. package/dist/mcp/codex-session/types.js +1 -0
  16. package/dist/mcp/codex-session.js +1 -500
  17. package/dist/mcp/config-reader.js +10 -2
  18. package/dist/mcp/git-remote.js +90 -14
  19. package/dist/mcp/local-state/agent-identity.js +26 -0
  20. package/dist/mcp/local-state/agent-sessions.js +66 -0
  21. package/dist/mcp/local-state/auth.js +57 -0
  22. package/dist/mcp/local-state/codex-live-sessions.js +59 -0
  23. package/dist/mcp/local-state/index.js +8 -0
  24. package/dist/mcp/local-state/local-chat.js +808 -0
  25. package/dist/mcp/local-state/room-sessions.js +51 -0
  26. package/dist/mcp/local-state/storage.js +95 -0
  27. package/dist/mcp/local-state/types.js +1 -0
  28. package/dist/mcp/local-state.js +1 -286
  29. package/dist/mcp/managed-agent-providers.js +65 -0
  30. package/dist/mcp/rental-tools/activity.js +37 -0
  31. package/dist/mcp/rental-tools/budget.js +36 -0
  32. package/dist/mcp/rental-tools/context.js +62 -0
  33. package/dist/mcp/rental-tools/lifecycle.js +47 -0
  34. package/dist/mcp/rental-tools/metering.js +73 -0
  35. package/dist/mcp/rental-tools/patches.js +100 -0
  36. package/dist/mcp/rental-tools/provider.js +117 -0
  37. package/dist/mcp/rental-tools/shared.js +19 -0
  38. package/dist/mcp/rental-tools/types.js +1 -0
  39. package/dist/mcp/rental-tools.js +7 -0
  40. package/dist/mcp/repo-visibility.js +8 -6
  41. package/dist/mcp/server/register-tools.js +19 -0
  42. package/dist/mcp/server/repo-context.js +28 -0
  43. package/dist/mcp/server/resources.js +77 -0
  44. package/dist/mcp/server/runtime/agent-sessions.js +134 -0
  45. package/dist/mcp/server/runtime/api.js +82 -0
  46. package/dist/mcp/server/runtime/auth-cache.js +20 -0
  47. package/dist/mcp/server/runtime/device-auth.js +48 -0
  48. package/dist/mcp/server/runtime/identity/config.js +58 -0
  49. package/dist/mcp/server/runtime/identity/directory.js +87 -0
  50. package/dist/mcp/server/runtime/identity/liveness.js +32 -0
  51. package/dist/mcp/server/runtime/identity/names.js +74 -0
  52. package/dist/mcp/server/runtime/identity/public.js +19 -0
  53. package/dist/mcp/server/runtime/identity/state.js +32 -0
  54. package/dist/mcp/server/runtime/identity.js +96 -0
  55. package/dist/mcp/server/runtime/messages.js +160 -0
  56. package/dist/mcp/server/runtime/presence.js +52 -0
  57. package/dist/mcp/server/runtime/room-api.js +42 -0
  58. package/dist/mcp/server/runtime/room-state.js +122 -0
  59. package/dist/mcp/server/runtime/rooms.js +318 -0
  60. package/dist/mcp/server/runtime.js +14 -0
  61. package/dist/mcp/server/tools/agent-sessions.js +374 -0
  62. package/dist/mcp/server/tools/messages/index.js +14 -0
  63. package/dist/mcp/server/tools/messages/read-tool.js +176 -0
  64. package/dist/mcp/server/tools/messages/reasoning-tool.js +179 -0
  65. package/dist/mcp/server/tools/messages/response.js +17 -0
  66. package/dist/mcp/server/tools/messages/send-tool.js +203 -0
  67. package/dist/mcp/server/tools/messages/status-tool.js +87 -0
  68. package/dist/mcp/server/tools/messages/wait-tool.js +333 -0
  69. package/dist/mcp/server/tools/messages.js +1 -0
  70. package/dist/mcp/server/tools/onboarding/device-auth-tools.js +148 -0
  71. package/dist/mcp/server/tools/onboarding/name-tool.js +89 -0
  72. package/dist/mcp/server/tools/onboarding/responses.js +10 -0
  73. package/dist/mcp/server/tools/onboarding/status-tool.js +108 -0
  74. package/dist/mcp/server/tools/onboarding.js +8 -0
  75. package/dist/mcp/server/tools/rental/activity-tools.js +43 -0
  76. package/dist/mcp/server/tools/rental/context-tools.js +35 -0
  77. package/dist/mcp/server/tools/rental/index.js +17 -0
  78. package/dist/mcp/server/tools/rental/metering-tools.js +39 -0
  79. package/dist/mcp/server/tools/rental/patch-tools.js +53 -0
  80. package/dist/mcp/server/tools/rental/provider-tools.js +44 -0
  81. package/dist/mcp/server/tools/rental/response.js +10 -0
  82. package/dist/mcp/server/tools/rental/types.js +1 -0
  83. package/dist/mcp/server/tools/rental.js +1 -0
  84. package/dist/mcp/server/tools/rooms/index.js +5 -0
  85. package/dist/mcp/server/tools/rooms/inspection-tools.js +124 -0
  86. package/dist/mcp/server/tools/rooms/join-tools.js +37 -0
  87. package/dist/mcp/server/tools/rooms/repo-initialization-tool.js +113 -0
  88. package/dist/mcp/server/tools/rooms/repo-visibility-tool.js +20 -0
  89. package/dist/mcp/server/tools/rooms/response.js +10 -0
  90. package/dist/mcp/server/tools/rooms/resume-tool.js +48 -0
  91. package/dist/mcp/server/tools/rooms.js +1 -0
  92. package/dist/mcp/server/tools/tasks/api.js +222 -0
  93. package/dist/mcp/server/tools/tasks/artifact-tools.js +75 -0
  94. package/dist/mcp/server/tools/tasks/board-intent-tools.js +321 -0
  95. package/dist/mcp/server/tools/tasks/board-tools.js +73 -0
  96. package/dist/mcp/server/tools/tasks/context.js +35 -0
  97. package/dist/mcp/server/tools/tasks/event-tools.js +40 -0
  98. package/dist/mcp/server/tools/tasks/index.js +14 -0
  99. package/dist/mcp/server/tools/tasks/lease-tools.js +145 -0
  100. package/dist/mcp/server/tools/tasks/mutation-tools.js +121 -0
  101. package/dist/mcp/server/tools/tasks/response.js +13 -0
  102. package/dist/mcp/server/tools/tasks/schemas.js +68 -0
  103. package/dist/mcp/server/tools/tasks.js +1 -0
  104. package/dist/mcp/server.js +10 -2540
  105. package/dist/mcp/sse-client.js +22 -16
  106. package/dist/shared/activation-routing.js +263 -0
  107. package/dist/shared/agent-identity.js +21 -5
  108. package/dist/shared/agent-presence.js +24 -0
  109. package/dist/shared/handoff.js +145 -0
  110. package/dist/shared/rental/lrt.js +214 -0
  111. package/dist/shared/rental/meter-types.js +14 -0
  112. package/dist/shared/request-headers.js +3 -0
  113. package/dist/shared/room-agent-activity.js +41 -0
  114. package/dist/shared/room-agent-prompts.js +14 -1
  115. package/package.json +15 -5
  116. /package/dist/{mcp → shared}/codenames.js +0 -0
@@ -0,0 +1,82 @@
1
+ import { clearStoredAuth, getStoredAuth, } from "../../local-state.js";
2
+ import { clearAuthenticatedAccountCache } from "./auth-cache.js";
3
+ export const API_URL = (process.env.LETAGENTS_API_URL || "http://localhost:3001").replace(/\/+$/, "");
4
+ export class ApiError extends Error {
5
+ status;
6
+ body;
7
+ constructor(status, body) {
8
+ super(`API error ${status}: ${body}`);
9
+ this.name = "ApiError";
10
+ this.status = status;
11
+ this.body = body;
12
+ }
13
+ }
14
+ export function getLetagentsToken() {
15
+ return process.env.LETAGENTS_TOKEN || getStoredAuth()?.token || "";
16
+ }
17
+ export function getAuthorizationHeader() {
18
+ const letagentsToken = getLetagentsToken();
19
+ return letagentsToken ? `Bearer ${letagentsToken}` : null;
20
+ }
21
+ export function isMissingRouteError(error) {
22
+ return (error instanceof ApiError &&
23
+ (error.status === 404 || error.status === 405) &&
24
+ /Cannot (GET|POST|PATCH)|Not Found|Cannot GET \/rooms|Cannot POST \/rooms/i.test(error.body));
25
+ }
26
+ export function parseApiErrorPayload(error) {
27
+ if (!(error instanceof ApiError)) {
28
+ return null;
29
+ }
30
+ try {
31
+ const parsed = JSON.parse(error.body);
32
+ return parsed && typeof parsed === "object" ? parsed : null;
33
+ }
34
+ catch {
35
+ return null;
36
+ }
37
+ }
38
+ export function resolveApiPath(urlOrPath) {
39
+ if (!urlOrPath) {
40
+ return "/auth/device/start";
41
+ }
42
+ try {
43
+ const parsed = new URL(urlOrPath, `${API_URL}/`);
44
+ const apiBase = new URL(`${API_URL}/`);
45
+ if (parsed.origin !== apiBase.origin) {
46
+ return "/auth/device/start";
47
+ }
48
+ return `${parsed.pathname}${parsed.search}`;
49
+ }
50
+ catch {
51
+ return "/auth/device/start";
52
+ }
53
+ }
54
+ export async function apiCall(path, options) {
55
+ const headers = {
56
+ "Content-Type": "application/json",
57
+ ...options?.headers,
58
+ };
59
+ const authorizationHeader = getAuthorizationHeader();
60
+ if (authorizationHeader && !headers.Authorization) {
61
+ headers.Authorization = authorizationHeader;
62
+ }
63
+ const res = await fetch(`${API_URL}${path}`, {
64
+ ...options,
65
+ headers,
66
+ });
67
+ if (!res.ok) {
68
+ const body = await res.text();
69
+ if (res.status === 401) {
70
+ // Only clear on 401 (invalid/expired credential), NOT on 403
71
+ // (valid credential but insufficient permissions, e.g., private repo access)
72
+ clearStoredAuth();
73
+ clearAuthenticatedAccountCache();
74
+ }
75
+ throw new ApiError(res.status, body);
76
+ }
77
+ const body = await res.text();
78
+ if (!body) {
79
+ return null;
80
+ }
81
+ return JSON.parse(body);
82
+ }
@@ -0,0 +1,20 @@
1
+ let currentAuthenticatedAccount = undefined;
2
+ let currentAuthenticatedAccountSource = null;
3
+ let currentAuthenticatedEnvToken = null;
4
+ export function clearAuthenticatedAccountCache() {
5
+ currentAuthenticatedAccount = undefined;
6
+ currentAuthenticatedAccountSource = null;
7
+ currentAuthenticatedEnvToken = null;
8
+ }
9
+ export function setAuthenticatedAccountCache(account, source, envToken) {
10
+ currentAuthenticatedAccount = account;
11
+ currentAuthenticatedAccountSource = source;
12
+ currentAuthenticatedEnvToken = envToken;
13
+ }
14
+ export function getAuthenticatedAccountCache() {
15
+ return {
16
+ account: currentAuthenticatedAccount,
17
+ source: currentAuthenticatedAccountSource,
18
+ envToken: currentAuthenticatedEnvToken,
19
+ };
20
+ }
@@ -0,0 +1,48 @@
1
+ import { getPendingDeviceAuth, setPendingDeviceAuth, } from "../../local-state.js";
2
+ import { ApiError, apiCall, parseApiErrorPayload, resolveApiPath, } from "./api.js";
3
+ export class RepoRoomAuthRequiredError extends Error {
4
+ roomId;
5
+ pendingAuth;
6
+ constructor(roomId, pendingAuth) {
7
+ super(`Repo room '${roomId}' requires authentication. Device flow started: open ${pendingAuth.verification_uri} and enter code ${pendingAuth.user_code}, then run poll_device_auth.`);
8
+ this.name = "RepoRoomAuthRequiredError";
9
+ this.roomId = roomId;
10
+ this.pendingAuth = pendingAuth;
11
+ }
12
+ }
13
+ export async function startPendingDeviceAuth(roomId, deviceFlowUrl) {
14
+ const existing = getPendingDeviceAuth();
15
+ if (existing?.suggested_room_id === roomId) {
16
+ return existing;
17
+ }
18
+ const response = await apiCall(resolveApiPath(deviceFlowUrl), {
19
+ method: "POST",
20
+ });
21
+ return setPendingDeviceAuth({
22
+ request_id: response.request_id,
23
+ user_code: response.user_code,
24
+ verification_uri: response.verification_uri,
25
+ interval_seconds: response.interval,
26
+ expires_at: new Date(Date.now() + response.expires_in * 1000).toISOString(),
27
+ started_at: new Date().toISOString(),
28
+ suggested_room_id: roomId,
29
+ });
30
+ }
31
+ export async function maybeHandleRepoRoomAuthRequired(error, roomId) {
32
+ const payload = parseApiErrorPayload(error);
33
+ if (!(error instanceof ApiError) || error.status !== 401 || payload?.error !== "auth_required") {
34
+ return;
35
+ }
36
+ const pendingAuth = await startPendingDeviceAuth(roomId, typeof payload.device_flow_url === "string" ? payload.device_flow_url : undefined);
37
+ throw new RepoRoomAuthRequiredError(roomId, pendingAuth);
38
+ }
39
+ export function toRepoRoomAuthRequiredResult(error) {
40
+ return {
41
+ success: false,
42
+ error: "auth_required",
43
+ room_id: error.roomId,
44
+ next_step: "poll_device_auth",
45
+ pending_device_auth: error.pendingAuth,
46
+ message: error.message,
47
+ };
48
+ }
@@ -0,0 +1,58 @@
1
+ import { execSync } from "child_process";
2
+ import { inferAgentIdeLabel, toTitleCaseCodename, } from "../../../../shared/agent-identity.js";
3
+ import { normalizeAgentBaseName } from "../../../../shared/codenames.js";
4
+ export const AGENT_NAME = (process.env.LETAGENTS_AGENT_NAME ||
5
+ process.env.AGENT_NAME ||
6
+ "").trim();
7
+ export const AGENT_DISPLAY_NAME = (process.env.LETAGENTS_AGENT_DISPLAY_NAME || "").trim();
8
+ export const AGENT_IDE_LABEL = (process.env.LETAGENTS_AGENT_IDE ||
9
+ process.env.AGENT_IDE ||
10
+ "").trim();
11
+ export const AGENT_OWNER_LABEL = (process.env.LETAGENTS_AGENT_OWNER_LABEL || "").trim();
12
+ export const EXPLICIT_AGENT_IDENTITY_KEY = getExplicitAgentIdentityStorageKey();
13
+ export function readCommandOutput(command, cwd = process.cwd()) {
14
+ try {
15
+ const output = execSync(command, {
16
+ cwd,
17
+ stdio: ["pipe", "pipe", "pipe"],
18
+ encoding: "utf-8",
19
+ }).trim();
20
+ return output || null;
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ }
26
+ function isCodexRuntime() {
27
+ return Boolean(process.env.CODEX_THREAD_ID ||
28
+ process.env.CODEX_SHELL ||
29
+ process.env.CODEX_CI ||
30
+ process.env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE);
31
+ }
32
+ function getExplicitAgentIdentityStorageKey() {
33
+ const runtimeSignals = [
34
+ process.env.LETAGENTS_AGENT_INSTANCE_ID,
35
+ process.env.CODEX_THREAD_ID && `codex:${process.env.CODEX_THREAD_ID}`,
36
+ process.env.ANTIGRAVITY_THREAD_ID && `antigravity:${process.env.ANTIGRAVITY_THREAD_ID}`,
37
+ process.env.CLAUDECODE_SESSION_ID && `claude:${process.env.CLAUDECODE_SESSION_ID}`,
38
+ process.env.MCP_SESSION_ID && `mcp:${process.env.MCP_SESSION_ID}`,
39
+ ].filter((value) => Boolean(value?.trim()));
40
+ return runtimeSignals[0] ?? null;
41
+ }
42
+ export function detectAgentIdeLabel() {
43
+ if (AGENT_IDE_LABEL) {
44
+ return toTitleCaseCodename(AGENT_IDE_LABEL);
45
+ }
46
+ if (isCodexRuntime()) {
47
+ return "Codex";
48
+ }
49
+ const explicitName = normalizeAgentBaseName(AGENT_NAME || AGENT_DISPLAY_NAME);
50
+ const inferred = inferAgentIdeLabel(explicitName);
51
+ return inferred || "Agent";
52
+ }
53
+ export function detectAgentRuntimeLabel() {
54
+ if (isCodexRuntime()) {
55
+ return "codex";
56
+ }
57
+ return detectAgentIdeLabel().trim().toLowerCase() || "unknown";
58
+ }
@@ -0,0 +1,87 @@
1
+ import { userInfo } from "os";
2
+ import { getStoredAuth, } from "../../../local-state.js";
3
+ import { normalizeSlugSegment } from "../../../../shared/codenames.js";
4
+ import { apiCall, getLetagentsToken, } from "../api.js";
5
+ import { getAuthenticatedAccountCache, setAuthenticatedAccountCache, } from "../auth-cache.js";
6
+ import { AGENT_OWNER_LABEL, readCommandOutput, } from "./config.js";
7
+ export async function getAuthenticatedAgentDirectory() {
8
+ try {
9
+ const result = await apiCall("/agents/me");
10
+ const account = result?.account;
11
+ if (!account?.login?.trim()) {
12
+ return null;
13
+ }
14
+ setAuthenticatedAccountCache(account, process.env.LETAGENTS_TOKEN?.trim() ? "env" : "stored", process.env.LETAGENTS_TOKEN?.trim() || null);
15
+ return {
16
+ account,
17
+ agents: Array.isArray(result?.agents) ? result.agents : [],
18
+ };
19
+ }
20
+ catch {
21
+ return null;
22
+ }
23
+ }
24
+ async function getAuthenticatedAccountProfile() {
25
+ const envToken = (process.env.LETAGENTS_TOKEN || "").trim();
26
+ const cache = getAuthenticatedAccountCache();
27
+ if (envToken) {
28
+ if (cache.source === "env" &&
29
+ cache.envToken === envToken &&
30
+ cache.account?.login?.trim()) {
31
+ return cache.account;
32
+ }
33
+ const directory = await getAuthenticatedAgentDirectory();
34
+ return directory?.account?.login?.trim() ? directory.account : null;
35
+ }
36
+ const storedAccount = getStoredAuth()?.account;
37
+ if (storedAccount?.login?.trim()) {
38
+ setAuthenticatedAccountCache(storedAccount, "stored", null);
39
+ return storedAccount;
40
+ }
41
+ if (!getLetagentsToken()) {
42
+ setAuthenticatedAccountCache(undefined, null, null);
43
+ return null;
44
+ }
45
+ if (cache.source === "stored" && cache.account?.login?.trim()) {
46
+ return cache.account;
47
+ }
48
+ const directory = await getAuthenticatedAgentDirectory();
49
+ return directory?.account?.login?.trim() ? directory.account : null;
50
+ }
51
+ export async function resolveOwnerContext() {
52
+ const account = await getAuthenticatedAccountProfile();
53
+ const authLogin = account?.login?.trim() || null;
54
+ const authLabel = account?.display_name?.trim() || authLogin;
55
+ if (authLogin || authLabel || AGENT_OWNER_LABEL) {
56
+ const label = AGENT_OWNER_LABEL || authLabel || authLogin || "Owner";
57
+ const slug = normalizeSlugSegment(authLogin || label, "owner");
58
+ return { slug, label, login: authLogin };
59
+ }
60
+ const gitUserName = readCommandOutput("git config --get user.name");
61
+ const gitUserEmail = readCommandOutput("git config --get user.email");
62
+ const gitIdentity = gitUserName || gitUserEmail?.split("@")[0] || null;
63
+ if (gitIdentity) {
64
+ return {
65
+ slug: normalizeSlugSegment(gitIdentity, "owner"),
66
+ label: gitIdentity,
67
+ login: null,
68
+ };
69
+ }
70
+ const osIdentity = process.env.USER ||
71
+ process.env.LOGNAME ||
72
+ process.env.USERNAME ||
73
+ (() => {
74
+ try {
75
+ return userInfo().username;
76
+ }
77
+ catch {
78
+ return null;
79
+ }
80
+ })() ||
81
+ "owner";
82
+ return {
83
+ slug: normalizeSlugSegment(osIdentity, "owner"),
84
+ label: osIdentity,
85
+ login: null,
86
+ };
87
+ }
@@ -0,0 +1,32 @@
1
+ import { randomUUID } from "crypto";
2
+ import { readLocalState, updateLocalState, } from "../../../local-state.js";
3
+ import { detectAgentIdeLabel, detectAgentRuntimeLabel, } from "./config.js";
4
+ import { AGENT_INSTANCE_UUID } from "./state.js";
5
+ function getOrCreateLocalHostId() {
6
+ const state = readLocalState();
7
+ if (typeof state.local_host_id === "string" && state.local_host_id.trim()) {
8
+ return state.local_host_id;
9
+ }
10
+ const hostId = `host_${randomUUID().replace(/-/g, "")}`;
11
+ updateLocalState((nextState) => {
12
+ nextState.local_host_id = typeof nextState.local_host_id === "string" && nextState.local_host_id.trim()
13
+ ? nextState.local_host_id
14
+ : hostId;
15
+ return nextState;
16
+ });
17
+ return readLocalState().local_host_id || hostId;
18
+ }
19
+ export function getSessionLivenessRegistration(runtime = detectAgentRuntimeLabel()) {
20
+ const hostId = getOrCreateLocalHostId();
21
+ const ideLabel = detectAgentIdeLabel();
22
+ const normalizedRuntime = runtime.trim().toLowerCase() || ideLabel.toLowerCase();
23
+ return {
24
+ host_id: hostId,
25
+ host_kind: process.platform === "darwin" ? "macos" : process.platform,
26
+ host_label: null,
27
+ liveness_capability: normalizedRuntime === "codex"
28
+ ? "codex_app_server_runtime_stream"
29
+ : "session_activity",
30
+ tool_bridge_id: `${hostId}:${normalizedRuntime}:${AGENT_INSTANCE_UUID}`,
31
+ };
32
+ }
@@ -0,0 +1,74 @@
1
+ import { createHash } from "crypto";
2
+ import { toTitleCaseCodename } from "../../../../shared/agent-identity.js";
3
+ import { AGENT_CODENAME_SPACE, normalizeAgentBaseName, pickLocalCodename, } from "../../../../shared/codenames.js";
4
+ import { AGENT_DISPLAY_NAME, AGENT_NAME, } from "./config.js";
5
+ import { getAuthenticatedAgentDirectory } from "./directory.js";
6
+ function shouldReuseStoredIdentity(identity, identityKey) {
7
+ return Boolean(identity &&
8
+ identity.runtime_key === identityKey &&
9
+ identity.display_name?.trim() &&
10
+ identity.ide_label?.trim() &&
11
+ identity.owner_attribution?.trim());
12
+ }
13
+ function resolveExplicitAgentIdentity() {
14
+ if (AGENT_NAME) {
15
+ const name = normalizeAgentBaseName(AGENT_NAME);
16
+ return {
17
+ name,
18
+ display_name: AGENT_DISPLAY_NAME || toTitleCaseCodename(AGENT_NAME),
19
+ };
20
+ }
21
+ if (AGENT_DISPLAY_NAME) {
22
+ return {
23
+ name: normalizeAgentBaseName(AGENT_DISPLAY_NAME),
24
+ display_name: AGENT_DISPLAY_NAME.trim(),
25
+ };
26
+ }
27
+ return null;
28
+ }
29
+ export async function resolveAgentName(input) {
30
+ const explicit = resolveExplicitAgentIdentity();
31
+ if (explicit) {
32
+ return explicit;
33
+ }
34
+ if (shouldReuseStoredIdentity(input.currentIdentity, input.identityKey)) {
35
+ return {
36
+ name: input.currentIdentity.name,
37
+ display_name: input.currentIdentity.display_name,
38
+ };
39
+ }
40
+ if (!input.authAvailable) {
41
+ return pickLocalCodename(input.identityKey);
42
+ }
43
+ const directory = await getAuthenticatedAgentDirectory();
44
+ const existingNames = new Set((directory?.agents ?? [])
45
+ .map((agent) => normalizeAgentBaseName(agent.name || ""))
46
+ .filter(Boolean));
47
+ for (let offset = 0; offset < AGENT_CODENAME_SPACE; offset += 1) {
48
+ const candidate = pickLocalCodename(input.identityKey, offset);
49
+ if (!existingNames.has(candidate.name)) {
50
+ return candidate;
51
+ }
52
+ }
53
+ const fallbackHash = createHash("sha256")
54
+ .update(input.identityKey)
55
+ .digest("hex")
56
+ .slice(0, 4);
57
+ const fallback = pickLocalCodename(input.identityKey);
58
+ return {
59
+ name: `${fallback.name}-${fallbackHash}`,
60
+ display_name: `${fallback.display_name} ${fallbackHash.toUpperCase()}`,
61
+ };
62
+ }
63
+ export function sameAgentIdentity(left, right) {
64
+ return Boolean(left &&
65
+ left.name === right.name &&
66
+ left.display_name === right.display_name &&
67
+ left.owner_label === right.owner_label &&
68
+ left.owner_attribution === right.owner_attribution &&
69
+ left.ide_label === right.ide_label &&
70
+ left.actor_label === right.actor_label &&
71
+ left.canonical_key === right.canonical_key &&
72
+ left.runtime_key === right.runtime_key &&
73
+ left.source === right.source);
74
+ }
@@ -0,0 +1,19 @@
1
+ import { formatOwnerAttribution, inferAgentIdeLabel, } from "../../../../shared/agent-identity.js";
2
+ import { AGENT_INSTANCE_UUID } from "./state.js";
3
+ export function toPublicAgentIdentity(identity) {
4
+ if (!identity) {
5
+ return null;
6
+ }
7
+ return {
8
+ name: identity.name,
9
+ display_name: identity.display_name,
10
+ owner_label: identity.owner_label,
11
+ owner_attribution: identity.owner_attribution ?? formatOwnerAttribution(identity.owner_label),
12
+ ide_label: identity.ide_label ?? inferAgentIdeLabel(identity.display_name) ?? "Agent",
13
+ actor_label: identity.actor_label,
14
+ canonical_key: identity.canonical_key ?? null,
15
+ runtime_key: identity.runtime_key ?? null,
16
+ agent_instance_id: AGENT_INSTANCE_UUID,
17
+ source: identity.source,
18
+ };
19
+ }
@@ -0,0 +1,32 @@
1
+ import { randomUUID } from "crypto";
2
+ import { getStoredAgentIdentity, setStoredAgentIdentity, } from "../../../local-state.js";
3
+ import { EXPLICIT_AGENT_IDENTITY_KEY } from "./config.js";
4
+ const MAX_CONVERSATION_IDENTITIES = 20;
5
+ export const AGENT_INSTANCE_UUID = randomUUID();
6
+ export let currentAgentIdentityKey = EXPLICIT_AGENT_IDENTITY_KEY ?? `instance:${AGENT_INSTANCE_UUID}`;
7
+ export let currentAgentIdentity = getStoredAgentIdentity(currentAgentIdentityKey);
8
+ const conversationIdentities = new Map();
9
+ export function storeCurrentAgentIdentity(identity, identityKey = currentAgentIdentityKey) {
10
+ currentAgentIdentity = setStoredAgentIdentity(identity, identityKey);
11
+ return currentAgentIdentity;
12
+ }
13
+ export function getConversationIdentity(conversationId) {
14
+ if (!conversationId)
15
+ return currentAgentIdentity;
16
+ return conversationIdentities.get(conversationId) ?? currentAgentIdentity;
17
+ }
18
+ export function setConversationIdentity(conversationId, identity) {
19
+ if (!conversationIdentities.has(conversationId) &&
20
+ conversationIdentities.size >= MAX_CONVERSATION_IDENTITIES) {
21
+ const oldestKey = conversationIdentities.keys().next().value;
22
+ if (oldestKey !== undefined)
23
+ conversationIdentities.delete(oldestKey);
24
+ }
25
+ conversationIdentities.set(conversationId, identity);
26
+ }
27
+ export function ensureAgentIdentityKey() {
28
+ currentAgentIdentityKey =
29
+ EXPLICIT_AGENT_IDENTITY_KEY ?? `instance:${AGENT_INSTANCE_UUID}`;
30
+ currentAgentIdentity = getStoredAgentIdentity(currentAgentIdentityKey);
31
+ return currentAgentIdentityKey;
32
+ }
@@ -0,0 +1,96 @@
1
+ import { buildAgentActorLabel, formatOwnerAttribution, } from "../../../shared/agent-identity.js";
2
+ import { apiCall, getLetagentsToken, } from "./api.js";
3
+ import { detectAgentIdeLabel, detectAgentRuntimeLabel, } from "./identity/config.js";
4
+ import { resolveOwnerContext } from "./identity/directory.js";
5
+ import { getSessionLivenessRegistration } from "./identity/liveness.js";
6
+ import { resolveAgentName, sameAgentIdentity, } from "./identity/names.js";
7
+ import { toPublicAgentIdentity } from "./identity/public.js";
8
+ import { currentAgentIdentity, currentAgentIdentityKey, ensureAgentIdentityKey, getConversationIdentity, setConversationIdentity, storeCurrentAgentIdentity, AGENT_INSTANCE_UUID, } from "./identity/state.js";
9
+ export { AGENT_INSTANCE_UUID, currentAgentIdentity, currentAgentIdentityKey, detectAgentIdeLabel, detectAgentRuntimeLabel, getConversationIdentity, getSessionLivenessRegistration, resolveOwnerContext, setConversationIdentity, storeCurrentAgentIdentity, toPublicAgentIdentity, };
10
+ export async function ensureAgentIdentity() {
11
+ const owner = await resolveOwnerContext();
12
+ const authAvailable = Boolean(getLetagentsToken());
13
+ const ideLabel = detectAgentIdeLabel();
14
+ const identityKey = ensureAgentIdentityKey();
15
+ const ownerAttribution = formatOwnerAttribution(owner.label);
16
+ const { name, display_name: displayName } = await resolveAgentName({
17
+ authAvailable,
18
+ identityKey,
19
+ currentIdentity: currentAgentIdentity,
20
+ });
21
+ const actorLabel = buildAgentActorLabel({
22
+ display_name: displayName,
23
+ owner_label: owner.label,
24
+ ide_label: ideLabel,
25
+ });
26
+ let resolved = {
27
+ name,
28
+ display_name: displayName,
29
+ owner_label: owner.label,
30
+ owner_attribution: ownerAttribution,
31
+ ide_label: ideLabel,
32
+ actor_label: actorLabel,
33
+ canonical_key: owner.login ? `${owner.login}/${name}` : null,
34
+ runtime_key: identityKey,
35
+ source: "local",
36
+ resolved_at: new Date().toISOString(),
37
+ };
38
+ if (currentAgentIdentity &&
39
+ currentAgentIdentity.name === resolved.name &&
40
+ currentAgentIdentity.display_name === resolved.display_name &&
41
+ currentAgentIdentity.owner_label === resolved.owner_label &&
42
+ currentAgentIdentity.owner_attribution === resolved.owner_attribution &&
43
+ currentAgentIdentity.ide_label === resolved.ide_label &&
44
+ currentAgentIdentity.actor_label === resolved.actor_label &&
45
+ currentAgentIdentity.runtime_key === resolved.runtime_key &&
46
+ (!authAvailable || currentAgentIdentity.source === "api")) {
47
+ return currentAgentIdentity;
48
+ }
49
+ if (authAvailable) {
50
+ try {
51
+ const registered = await apiCall("/agents", {
52
+ method: "POST",
53
+ body: JSON.stringify({
54
+ name: resolved.name,
55
+ display_name: resolved.display_name,
56
+ owner_label: resolved.owner_label,
57
+ }),
58
+ });
59
+ resolved = {
60
+ ...resolved,
61
+ canonical_key: typeof registered.canonical_key === "string"
62
+ ? registered.canonical_key
63
+ : resolved.canonical_key,
64
+ display_name: typeof registered.display_name === "string"
65
+ ? registered.display_name
66
+ : resolved.display_name,
67
+ owner_label: typeof registered.owner_label === "string"
68
+ ? registered.owner_label
69
+ : resolved.owner_label,
70
+ source: "api",
71
+ };
72
+ resolved.owner_attribution = formatOwnerAttribution(resolved.owner_label);
73
+ resolved.actor_label = buildAgentActorLabel({
74
+ display_name: resolved.display_name,
75
+ owner_label: resolved.owner_label,
76
+ ide_label: resolved.ide_label,
77
+ });
78
+ }
79
+ catch (error) {
80
+ console.error("Agent identity registration failed:", error instanceof Error ? error.message : error);
81
+ }
82
+ }
83
+ if (sameAgentIdentity(currentAgentIdentity, resolved)) {
84
+ return currentAgentIdentity ?? resolved;
85
+ }
86
+ return storeCurrentAgentIdentity({
87
+ ...resolved,
88
+ resolved_at: new Date().toISOString(),
89
+ }, identityKey);
90
+ }
91
+ export async function withAgentIdentity(payload) {
92
+ return {
93
+ ...payload,
94
+ agent_identity: toPublicAgentIdentity(await ensureAgentIdentity()),
95
+ };
96
+ }