@data-club/ai-hub-server 0.0.9 → 0.0.11

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 (96) hide show
  1. package/dist/auth/encryption.d.ts +5 -5
  2. package/dist/auth/encryption.js +5 -5
  3. package/dist/auth/userView.d.ts +8 -15
  4. package/dist/auth/userView.d.ts.map +1 -1
  5. package/dist/auth/userView.js +10 -23
  6. package/dist/auth/userView.js.map +1 -1
  7. package/dist/bootstrap.d.ts +2 -8
  8. package/dist/bootstrap.d.ts.map +1 -1
  9. package/dist/bootstrap.js +2 -35
  10. package/dist/bootstrap.js.map +1 -1
  11. package/dist/db/migrate.js +6 -17
  12. package/dist/db/migrate.js.map +1 -1
  13. package/dist/db/schema.d.ts +17 -263
  14. package/dist/db/schema.d.ts.map +1 -1
  15. package/dist/db/schema.js +8 -57
  16. package/dist/db/schema.js.map +1 -1
  17. package/dist/db/seed.d.ts +4 -19
  18. package/dist/db/seed.d.ts.map +1 -1
  19. package/dist/db/seed.js +6 -33
  20. package/dist/db/seed.js.map +1 -1
  21. package/dist/handlers/fetch.d.ts +6 -7
  22. package/dist/handlers/fetch.d.ts.map +1 -1
  23. package/dist/handlers/fetch.js +1 -3
  24. package/dist/handlers/fetch.js.map +1 -1
  25. package/dist/handlers/types.d.ts +5 -6
  26. package/dist/handlers/types.d.ts.map +1 -1
  27. package/dist/letta/client.d.ts +86 -263
  28. package/dist/letta/client.d.ts.map +1 -1
  29. package/dist/letta/client.js +99 -404
  30. package/dist/letta/client.js.map +1 -1
  31. package/dist/letta/index.d.ts +6 -8
  32. package/dist/letta/index.d.ts.map +1 -1
  33. package/dist/letta/index.js +5 -7
  34. package/dist/letta/index.js.map +1 -1
  35. package/dist/letta/sandbox.d.ts +45 -9
  36. package/dist/letta/sandbox.d.ts.map +1 -1
  37. package/dist/letta/sandbox.js +123 -11
  38. package/dist/letta/sandbox.js.map +1 -1
  39. package/dist/letta/types.d.ts +6 -49
  40. package/dist/letta/types.d.ts.map +1 -1
  41. package/dist/letta/types.js +5 -10
  42. package/dist/letta/types.js.map +1 -1
  43. package/dist/observability/logger.d.ts.map +1 -1
  44. package/dist/observability/logger.js +0 -2
  45. package/dist/observability/logger.js.map +1 -1
  46. package/dist/routes/admin/agents.d.ts.map +1 -1
  47. package/dist/routes/admin/agents.js +0 -40
  48. package/dist/routes/admin/agents.js.map +1 -1
  49. package/dist/routes/admin/rotatingMessages.d.ts.map +1 -1
  50. package/dist/routes/admin/rotatingMessages.js +37 -1
  51. package/dist/routes/admin/rotatingMessages.js.map +1 -1
  52. package/dist/routes/admin/settings.d.ts +6 -15
  53. package/dist/routes/admin/settings.d.ts.map +1 -1
  54. package/dist/routes/admin/settings.js +9 -107
  55. package/dist/routes/admin/settings.js.map +1 -1
  56. package/dist/routes/auth.d.ts +0 -4
  57. package/dist/routes/auth.d.ts.map +1 -1
  58. package/dist/routes/auth.js +1 -5
  59. package/dist/routes/auth.js.map +1 -1
  60. package/dist/routes/sessions.d.ts.map +1 -1
  61. package/dist/routes/sessions.js +9 -30
  62. package/dist/routes/sessions.js.map +1 -1
  63. package/dist/state/rotationRunner.d.ts +6 -0
  64. package/dist/state/rotationRunner.d.ts.map +1 -1
  65. package/dist/state/rotationRunner.js +19 -2
  66. package/dist/state/rotationRunner.js.map +1 -1
  67. package/dist/state/rotationScheduler.d.ts +2 -0
  68. package/dist/state/rotationScheduler.d.ts.map +1 -1
  69. package/dist/state/rotationScheduler.js.map +1 -1
  70. package/dist/state/turnRunner.d.ts.map +1 -1
  71. package/dist/state/turnRunner.js +1 -75
  72. package/dist/state/turnRunner.js.map +1 -1
  73. package/package.json +4 -4
  74. package/src/auth/encryption.ts +5 -5
  75. package/src/auth/userView.ts +9 -22
  76. package/src/bootstrap.ts +4 -43
  77. package/src/db/migrate.ts +6 -17
  78. package/src/db/schema.ts +8 -68
  79. package/src/db/seed.ts +6 -47
  80. package/src/handlers/fetch.ts +7 -10
  81. package/src/handlers/types.ts +5 -6
  82. package/src/letta/client.ts +170 -634
  83. package/src/letta/index.ts +8 -21
  84. package/src/letta/sandbox.ts +151 -11
  85. package/src/letta/types.ts +9 -60
  86. package/src/observability/logger.ts +0 -2
  87. package/src/routes/admin/agents.ts +0 -49
  88. package/src/routes/admin/rotatingMessages.ts +47 -1
  89. package/src/routes/admin/settings.ts +9 -113
  90. package/src/routes/auth.ts +1 -5
  91. package/src/routes/sessions.ts +11 -32
  92. package/src/state/rotationRunner.ts +37 -1
  93. package/src/state/rotationScheduler.ts +2 -0
  94. package/src/state/turnRunner.ts +1 -77
  95. package/src/letta/lettaCliShim.mjs +0 -40
  96. package/src/letta/settingsFileSeed.ts +0 -254
@@ -1,254 +0,0 @@
1
- /**
2
- * Seeds Letta Code's global agent-settings file (`~/.letta/settings.json`)
3
- * with `{toolset: "none"}` for each ai-hub-registered Letta agent.
4
- *
5
- * **Why this matters.** Letta Code's bundled CLI auto-attaches a "toolset"
6
- * (a set of file-edit / shell / search tools — `Read`, `Write`, `Edit`,
7
- * `Bash`, etc.) to every agent at session start, based on the model handle.
8
- * ai-hub doesn't want those tools — we want only the tools the ADE admin
9
- * explicitly attached to the agent. The CLI honors a per-agent
10
- * `toolset: "none"` preference stored in its global settings file, which
11
- * suppresses the entire auto-toolset.
12
- *
13
- * **Why global, not project-local.** Letta Code's `SettingsManager` reads
14
- * the toolset preference from `~/.letta/settings.json` (the global file),
15
- * not `<cwd>/.letta/settings.local.json` (the project file). The global
16
- * settings carry agent records keyed by `(agentId, normalizedBaseUrl)`
17
- * where `normalizedBaseUrl` is the value of `LETTA_BASE_URL` with the
18
- * scheme and trailing slash stripped, or `undefined` when the agent lives
19
- * on Letta Cloud. Self-hosted Letta deployments always have a non-default
20
- * `LETTA_BASE_URL` so the agent record always has the `baseUrl` field set.
21
- *
22
- * The `LETTA_CLI_PATH` shim (which injects `--tools ""`) is the primary
23
- * mechanism that forces an empty tool registry; this settings-file seed
24
- * is the defense-in-depth fallback the TUI also honors.
25
- */
26
-
27
- import {
28
- existsSync,
29
- mkdirSync,
30
- readFileSync,
31
- renameSync,
32
- writeFileSync,
33
- } from "node:fs";
34
- import { homedir } from "node:os";
35
- import { dirname, join } from "node:path";
36
-
37
- import type { Logger } from "../observability/logger.js";
38
-
39
- /* ────────────────────────────────────────────────────────────────────────── */
40
- /* Settings file shape */
41
- /* ────────────────────────────────────────────────────────────────────────── */
42
-
43
- /**
44
- * Toolset preference values accepted by Letta Code. `"none"` is the
45
- * documented-but-not-help-listed value that suppresses every auto-attached
46
- * tool; the named toolsets (`codex`, `gemini`, `default`) attach specific
47
- * sets of file/shell tools that ai-hub doesn't want.
48
- *
49
- * The string is treated as an opaque scalar by Letta Code's persistence
50
- * layer; we still narrow the TypeScript type here so a typo in this file
51
- * fails at compile time.
52
- */
53
- export type ToolsetPreference = "none" | "auto" | "codex" | "codex_snake" | "gemini" | "gemini_snake" | "default";
54
-
55
- /** One agent entry inside the global settings file. Mirrors Letta Code's
56
- * internal shape so our writes round-trip with its own writes. Optional
57
- * fields are intentionally absent when unset, matching Letta Code's
58
- * delete-when-falsy convention. */
59
- export interface AgentSettingsEntry {
60
- agentId: string;
61
- /** Normalized base URL ("localhost:8283/v1" style — no scheme, no
62
- * trailing slash). Omitted for Letta Cloud agents. */
63
- baseUrl?: string;
64
- pinned?: boolean;
65
- memfs?: boolean;
66
- toolset?: ToolsetPreference;
67
- systemPromptPreset?: string;
68
- }
69
-
70
- /** Shape of `~/.letta/settings.json` that we touch. We pass through every
71
- * unknown top-level field unchanged so concurrent Letta Code edits don't
72
- * lose data. */
73
- export interface LettaCodeSettingsFile {
74
- agents?: AgentSettingsEntry[];
75
- /** Any other fields Letta Code writes (oauth state, env vars, …). We
76
- * treat them as opaque on read + write. */
77
- [other: string]: unknown;
78
- }
79
-
80
- /* ────────────────────────────────────────────────────────────────────────── */
81
- /* Path resolution */
82
- /* ────────────────────────────────────────────────────────────────────────── */
83
-
84
- /**
85
- * Returns the absolute path to Letta Code's global settings file. Tests
86
- * override `HOME` to point at a tmp dir.
87
- *
88
- * @returns the path. (Doesn't check existence — caller does.)
89
- */
90
- export function resolveGlobalSettingsPath(): string {
91
- return join(homedir(), ".letta", "settings.json");
92
- }
93
-
94
- /**
95
- * Normalizes `LETTA_BASE_URL` to match the key Letta Code's CLI uses to
96
- * look up agent settings AFTER ai-hub-server's `bootstrap.ts` runs its
97
- * own `/v1` stripping. The CLI itself only strips scheme + trailing
98
- * slash (so `http://host:p/v1` → `host:p/v1`), but bootstrap.ts mutates
99
- * `process.env.LETTA_BASE_URL` at boot to drop the `/v1` (because the
100
- * Letta Client SDK that the CLI uses internally appends `/v1` itself —
101
- * leaving it in the env produces double-`/v1` requests that 404).
102
- *
103
- * So by the time the spawned CLI looks up the agent settings, its
104
- * `getCurrentServerKey` reads `http://host:p` from env and produces
105
- * `host:p`. The settings file entry must use the same key — hence the
106
- * extra `/v1` strip here. If `bootstrap.ts` changes its policy, this
107
- * helper has to change in lockstep.
108
- *
109
- * Returns `undefined` for the Letta Cloud default URL (so the agent
110
- * record omits the `baseUrl` field, matching Letta Code's behavior on
111
- * Cloud).
112
- *
113
- * @param baseUrl - raw `LETTA_BASE_URL` value.
114
- * @returns the normalized form, or `undefined` for Letta Cloud.
115
- */
116
- export function normalizeLettaBaseUrl(baseUrl: string | undefined): string | undefined {
117
- if (!baseUrl) return undefined;
118
- const stripped = baseUrl
119
- .replace(/^https?:\/\//, "")
120
- .replace(/\/v1\/?$/i, "")
121
- .replace(/\/$/, "");
122
- if (stripped === "api.letta.com") return undefined;
123
- return stripped;
124
- }
125
-
126
- /* ────────────────────────────────────────────────────────────────────────── */
127
- /* Read + write */
128
- /* ────────────────────────────────────────────────────────────────────────── */
129
-
130
- /**
131
- * Reads the settings file from disk. Returns an empty object if the file
132
- * doesn't exist or is unparseable — both cases are recoverable by an
133
- * idempotent upsert that recreates the file.
134
- *
135
- * @param path - the resolved settings path.
136
- * @returns the parsed settings (or `{}`).
137
- */
138
- export function readSettingsFile(path: string): LettaCodeSettingsFile {
139
- if (!existsSync(path)) return {};
140
- try {
141
- const raw = readFileSync(path, "utf8");
142
- if (!raw.trim()) return {};
143
- const parsed = JSON.parse(raw);
144
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
145
- return parsed as LettaCodeSettingsFile;
146
- }
147
- return {};
148
- } catch {
149
- // Malformed file (concurrent write, disk corruption) — treat as empty
150
- // so the seed proceeds. Worst case the operator's other settings (if
151
- // any) are clobbered, which is acceptable when the file was already
152
- // broken.
153
- return {};
154
- }
155
- }
156
-
157
- /**
158
- * Writes the settings file atomically: write to `<path>.tmp` then rename.
159
- * The rename is atomic on every filesystem the ai-hub-server supports
160
- * (ext4, xfs, btrfs, apfs). Creates `~/.letta/` if missing.
161
- *
162
- * @param path - the resolved settings path.
163
- * @param settings - the new contents.
164
- */
165
- export function writeSettingsFile(path: string, settings: LettaCodeSettingsFile): void {
166
- mkdirSync(dirname(path), { recursive: true });
167
- const tmp = `${path}.tmp`;
168
- writeFileSync(tmp, JSON.stringify(settings, null, 2), { encoding: "utf8", mode: 0o600 });
169
- renameSync(tmp, path);
170
- }
171
-
172
- /* ────────────────────────────────────────────────────────────────────────── */
173
- /* Upsert */
174
- /* ────────────────────────────────────────────────────────────────────────── */
175
-
176
- /**
177
- * Outcome of a single seed call. Useful for tests and observability.
178
- */
179
- export type SeedOutcome =
180
- | { kind: "created"; previousToolset: undefined }
181
- | { kind: "updated"; previousToolset: ToolsetPreference | undefined }
182
- | { kind: "noop"; previousToolset: ToolsetPreference };
183
-
184
- /**
185
- * Idempotent upsert of `{toolset: "none"}` for a single agent in the
186
- * global settings file.
187
- *
188
- * Matching is by `(agentId, baseUrl)` — same composite key Letta Code
189
- * uses. If the agent record exists with a different toolset (`"auto"`,
190
- * `"codex"`, etc.), it's replaced. If it exists already with `"none"`,
191
- * we no-op (don't churn the file's mtime).
192
- *
193
- * @param opts.agentId - the Letta agent id (e.g. `agent-xxx`).
194
- * @param opts.lettaBaseUrl - the raw `LETTA_BASE_URL` value (so the
195
- * helper can normalize it the same way Letta Code does). Pass
196
- * `process.env.LETTA_BASE_URL` from the caller.
197
- * @param opts.settingsPath - override the settings-file path. Defaults to
198
- * `~/.letta/settings.json`. Tests inject a tmp path.
199
- * @param opts.logger - optional pino logger for observability lines.
200
- * @returns the outcome (created / updated / noop) so the caller can log.
201
- */
202
- export function seedAgentToolsetNone(opts: {
203
- agentId: string;
204
- lettaBaseUrl: string | undefined;
205
- settingsPath?: string;
206
- logger?: Logger;
207
- }): SeedOutcome {
208
- const path = opts.settingsPath ?? resolveGlobalSettingsPath();
209
- const normalizedBaseUrl = normalizeLettaBaseUrl(opts.lettaBaseUrl);
210
- const settings = readSettingsFile(path);
211
- const agents: AgentSettingsEntry[] = Array.isArray(settings.agents) ? [...settings.agents] : [];
212
-
213
- const matchIdx = agents.findIndex(
214
- (a) => a.agentId === opts.agentId && (a.baseUrl ?? undefined) === normalizedBaseUrl,
215
- );
216
-
217
- let outcome: SeedOutcome;
218
-
219
- if (matchIdx >= 0) {
220
- const existing = agents[matchIdx]!;
221
- const prev = existing.toolset;
222
- if (prev === "none") {
223
- outcome = { kind: "noop", previousToolset: "none" };
224
- opts.logger?.debug({ agentId: opts.agentId, baseUrl: normalizedBaseUrl }, "letta_code_settings_seed_noop");
225
- return outcome;
226
- }
227
- agents[matchIdx] = { ...existing, toolset: "none" };
228
- outcome = { kind: "updated", previousToolset: prev };
229
- } else {
230
- const entry: AgentSettingsEntry = {
231
- agentId: opts.agentId,
232
- toolset: "none",
233
- };
234
- if (normalizedBaseUrl !== undefined) entry.baseUrl = normalizedBaseUrl;
235
- agents.push(entry);
236
- outcome = { kind: "created", previousToolset: undefined };
237
- }
238
-
239
- const next: LettaCodeSettingsFile = { ...settings, agents };
240
- writeSettingsFile(path, next);
241
-
242
- opts.logger?.info(
243
- {
244
- agentId: opts.agentId,
245
- baseUrl: normalizedBaseUrl,
246
- outcome: outcome.kind,
247
- previousToolset: outcome.previousToolset,
248
- settingsPath: path,
249
- },
250
- "letta_code_settings_seed",
251
- );
252
-
253
- return outcome;
254
- }