@pentatonic-ai/ai-agent-sdk 0.5.11 → 0.7.0

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 (119) hide show
  1. package/README.md +345 -174
  2. package/bin/__tests__/callback-server.test.js +70 -0
  3. package/bin/__tests__/credentials.test.js +58 -0
  4. package/bin/__tests__/login.test.js +210 -0
  5. package/bin/__tests__/pkce.test.js +39 -0
  6. package/bin/__tests__/whoami.test.js +77 -0
  7. package/bin/cli.js +109 -440
  8. package/bin/commands/config.js +251 -0
  9. package/bin/commands/login.js +219 -0
  10. package/bin/commands/whoami.js +41 -0
  11. package/bin/lib/callback-server.js +137 -0
  12. package/bin/lib/credentials.js +100 -0
  13. package/bin/lib/pkce.js +26 -0
  14. package/package.json +4 -2
  15. package/packages/doctor/__tests__/detect.test.js +2 -6
  16. package/packages/doctor/src/checks/local-memory.js +164 -196
  17. package/packages/doctor/src/detect.js +11 -3
  18. package/packages/memory/src/__tests__/corpus-chunkers.test.js +143 -0
  19. package/packages/memory/src/__tests__/corpus-discover.test.js +175 -0
  20. package/packages/memory/src/__tests__/corpus-ingest.test.js +236 -0
  21. package/packages/memory/src/__tests__/corpus-signatures.test.js +175 -0
  22. package/packages/memory/src/__tests__/corpus-state.test.js +161 -0
  23. package/packages/memory/src/__tests__/ingest-corpus-opts.test.js +129 -0
  24. package/packages/memory/src/__tests__/search-kind.test.js +108 -0
  25. package/packages/memory/src/corpus/adapters.js +398 -0
  26. package/packages/memory/src/corpus/chunkers.js +328 -0
  27. package/packages/memory/src/corpus/cli.js +613 -0
  28. package/packages/memory/src/corpus/discover.js +379 -0
  29. package/packages/memory/src/corpus/index.js +68 -0
  30. package/packages/memory/src/corpus/ingest.js +356 -0
  31. package/packages/memory/src/corpus/signatures.js +280 -0
  32. package/packages/memory/src/corpus/state.js +134 -0
  33. package/packages/memory/src/index.js +18 -0
  34. package/packages/memory/src/ingest.js +20 -11
  35. package/packages/memory/src/openclaw/index.js +39 -1
  36. package/packages/memory/src/search.js +30 -7
  37. package/packages/memory-engine/.env.example +13 -0
  38. package/packages/memory-engine/README.md +131 -0
  39. package/packages/memory-engine/bench/README.md +99 -0
  40. package/packages/memory-engine/bench/scorecards-engine/agent-coding__pentatonic-baseline__20260427-142523.json +1115 -0
  41. package/packages/memory-engine/bench/scorecards-engine/chat-recall__pentatonic-baseline__20260427-142648.json +819 -0
  42. package/packages/memory-engine/bench/scorecards-engine/circular-economy__pentatonic-baseline__20260427-142757.json +1278 -0
  43. package/packages/memory-engine/bench/scorecards-engine/customer-support__pentatonic-baseline__20260427-142900.json +1018 -0
  44. package/packages/memory-engine/bench/scorecards-engine/marketplace-ops__pentatonic-baseline__20260427-142957.json +1038 -0
  45. package/packages/memory-engine/bench/scorecards-engine/product-catalogue__pentatonic-baseline__20260427-143122.json +961 -0
  46. package/packages/memory-engine/bench/scorecards-engine-via-docker/agent-coding__pentatonic-memory__20260427-161812.json +1115 -0
  47. package/packages/memory-engine/bench/scorecards-engine-via-docker/chat-recall__pentatonic-memory__20260427-161701.json +819 -0
  48. package/packages/memory-engine/bench/scorecards-engine-via-docker/circular-economy__pentatonic-memory__20260427-161713.json +1278 -0
  49. package/packages/memory-engine/bench/scorecards-engine-via-docker/customer-support__pentatonic-memory__20260427-161723.json +1018 -0
  50. package/packages/memory-engine/bench/scorecards-engine-via-docker/marketplace-ops__pentatonic-memory__20260427-161732.json +1038 -0
  51. package/packages/memory-engine/bench/scorecards-engine-via-docker/product-catalogue__pentatonic-memory__20260427-161741.json +937 -0
  52. package/packages/memory-engine/bench/scorecards-engine-via-l2-7-layer-populated/agent-coding__pentatonic-memory__20260427-184718.json +1115 -0
  53. package/packages/memory-engine/bench/scorecards-engine-via-l2-7-layer-populated/chat-recall__pentatonic-memory__20260427-184614.json +819 -0
  54. package/packages/memory-engine/bench/scorecards-engine-via-l2-7-layer-populated/circular-economy__pentatonic-memory__20260427-184809.json +1278 -0
  55. package/packages/memory-engine/bench/scorecards-engine-via-l2-7-layer-populated/customer-support__pentatonic-memory__20260427-184854.json +1018 -0
  56. package/packages/memory-engine/bench/scorecards-engine-via-l2-7-layer-populated/marketplace-ops__pentatonic-memory__20260427-184929.json +1038 -0
  57. package/packages/memory-engine/bench/scorecards-engine-via-l2-7-layer-populated/product-catalogue__pentatonic-memory__20260427-185015.json +961 -0
  58. package/packages/memory-engine/bench/scorecards-engine-via-l2-empty-layers/agent-coding__pentatonic-memory__20260427-175252.json +1115 -0
  59. package/packages/memory-engine/bench/scorecards-engine-via-l2-empty-layers/chat-recall__pentatonic-memory__20260427-175312.json +819 -0
  60. package/packages/memory-engine/bench/scorecards-engine-via-l2-empty-layers/circular-economy__pentatonic-memory__20260427-175335.json +1278 -0
  61. package/packages/memory-engine/bench/scorecards-engine-via-l2-empty-layers/customer-support__pentatonic-memory__20260427-175355.json +1018 -0
  62. package/packages/memory-engine/bench/scorecards-engine-via-l2-empty-layers/marketplace-ops__pentatonic-memory__20260427-175413.json +1038 -0
  63. package/packages/memory-engine/bench/scorecards-engine-via-l2-empty-layers/product-catalogue__pentatonic-memory__20260427-175430.json +883 -0
  64. package/packages/memory-engine/bench/scorecards-engine-via-shim/agent-coding__pentatonic-memory__20260427-155409.json +1115 -0
  65. package/packages/memory-engine/bench/scorecards-engine-via-shim/chat-recall__pentatonic-memory__20260427-155421.json +819 -0
  66. package/packages/memory-engine/bench/scorecards-engine-via-shim/circular-economy__pentatonic-memory__20260427-155433.json +1278 -0
  67. package/packages/memory-engine/bench/scorecards-engine-via-shim/customer-support__pentatonic-memory__20260427-155443.json +1018 -0
  68. package/packages/memory-engine/bench/scorecards-engine-via-shim/marketplace-ops__pentatonic-memory__20260427-155453.json +1038 -0
  69. package/packages/memory-engine/bench/scorecards-engine-via-shim/product-catalogue__pentatonic-memory__20260427-155503.json +937 -0
  70. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/agent-coding__pentatonic-memory-latest__20260427-145103.json +1115 -0
  71. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/agent-coding__pentatonic-memory__20260427-144909.json +1115 -0
  72. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/chat-recall__pentatonic-memory-latest__20260427-145153.json +819 -0
  73. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/chat-recall__pentatonic-memory__20260427-145120.json +542 -0
  74. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/circular-economy__pentatonic-memory-latest__20260427-145313.json +1278 -0
  75. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/circular-economy__pentatonic-memory__20260427-145207.json +894 -0
  76. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/customer-support__pentatonic-memory-latest__20260427-145412.json +1018 -0
  77. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/customer-support__pentatonic-memory__20260427-145327.json +680 -0
  78. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/marketplace-ops__pentatonic-memory-latest__20260427-145517.json +1038 -0
  79. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/marketplace-ops__pentatonic-memory__20260427-145422.json +693 -0
  80. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/product-catalogue__pentatonic-memory-latest__20260427-145616.json +961 -0
  81. package/packages/memory-engine/bench/scorecards-pentatonic-baseline/product-catalogue__pentatonic-memory__20260427-145528.json +727 -0
  82. package/packages/memory-engine/compat/Dockerfile +11 -0
  83. package/packages/memory-engine/compat/server.py +680 -0
  84. package/packages/memory-engine/docker-compose.yml +243 -0
  85. package/packages/memory-engine/docs/MIGRATION.md +178 -0
  86. package/packages/memory-engine/docs/RUNBOOK-AWS.md +375 -0
  87. package/packages/memory-engine/docs/why-v05-underperforms.md +138 -0
  88. package/packages/memory-engine/engine/README.md +52 -0
  89. package/packages/memory-engine/engine/l2-hybridrag-proxy.py +1543 -0
  90. package/packages/memory-engine/engine/l5-comms-layer.py +663 -0
  91. package/packages/memory-engine/engine/l6-document-store.py +1018 -0
  92. package/packages/memory-engine/engine/services/l2/Dockerfile +41 -0
  93. package/packages/memory-engine/engine/services/l2/init_databases.py +81 -0
  94. package/packages/memory-engine/engine/services/l2/l2-hybridrag-proxy.py +1543 -0
  95. package/packages/memory-engine/engine/services/l4/Dockerfile +15 -0
  96. package/packages/memory-engine/engine/services/l4/server.py +235 -0
  97. package/packages/memory-engine/engine/services/l5/Dockerfile +9 -0
  98. package/packages/memory-engine/engine/services/l5/l5-comms-layer.py +678 -0
  99. package/packages/memory-engine/engine/services/l6/Dockerfile +11 -0
  100. package/packages/memory-engine/engine/services/l6/l6-document-store.py +1016 -0
  101. package/packages/memory-engine/engine/services/nv-embed/Dockerfile +28 -0
  102. package/packages/memory-engine/engine/services/nv-embed/server.py +152 -0
  103. package/packages/memory-engine/pme_memory/__init__.py +0 -0
  104. package/packages/memory-engine/pme_memory/__main__.py +129 -0
  105. package/packages/memory-engine/pme_memory/artifacts.py +95 -0
  106. package/packages/memory-engine/pme_memory/embed.py +74 -0
  107. package/packages/memory-engine/pme_memory/health.py +36 -0
  108. package/packages/memory-engine/pme_memory/hygiene.py +159 -0
  109. package/packages/memory-engine/pme_memory/indexer.py +200 -0
  110. package/packages/memory-engine/pme_memory/needs.py +55 -0
  111. package/packages/memory-engine/pme_memory/provenance.py +80 -0
  112. package/packages/memory-engine/pme_memory/scoring.py +168 -0
  113. package/packages/memory-engine/pme_memory/search.py +52 -0
  114. package/packages/memory-engine/pme_memory/store.py +86 -0
  115. package/packages/memory-engine/pme_memory/synthesis.py +114 -0
  116. package/packages/memory-engine/pyproject.toml +65 -0
  117. package/packages/memory-engine/scripts/kg-extractor.py +557 -0
  118. package/packages/memory-engine/scripts/kg-preflexor-v2.py +738 -0
  119. package/packages/memory-engine/tests/test_api_contract.sh +57 -0
package/bin/cli.js CHANGED
@@ -1,10 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { createInterface } from "readline";
4
- import { execFileSync } from "child_process";
5
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
6
- import { join } from "path";
7
- import { homedir } from "os";
8
4
 
9
5
  const DEFAULT_ENDPOINT = "https://api.pentatonic.com";
10
6
 
@@ -31,8 +27,14 @@ function parseArgs() {
31
27
  flags.alert = true;
32
28
  } else if (a === "--no-plugins") {
33
29
  flags.noPlugins = true;
30
+ } else if (a === "--engine-url" && args[i + 1]) {
31
+ flags.engineUrl = args[++i];
32
+ } else if (a.startsWith("--engine-url=")) {
33
+ flags.engineUrl = a.split("=")[1];
34
34
  } else if (!a.startsWith("--")) {
35
- flags.command = a;
35
+ // First non-flag arg is the command; subsequent ones are subcommand
36
+ // arguments handled by the dispatched cmd (e.g. `ingest <path>`).
37
+ if (!flags.command) flags.command = a;
36
38
  }
37
39
  }
38
40
  return flags;
@@ -64,8 +66,6 @@ async function runDoctorCommand(flags) {
64
66
  if (report.summary.warning > 0) return 1;
65
67
  return 0;
66
68
  }
67
- const POLL_INTERVAL_MS = 3000;
68
- const POLL_TIMEOUT_MS = 300000; // 5 minutes
69
69
 
70
70
  let rl = createInterface({ input: process.stdin, output: process.stdout });
71
71
 
@@ -73,243 +73,125 @@ function ask(question) {
73
73
  return new Promise((resolve) => rl.question(question, resolve));
74
74
  }
75
75
 
76
- function askSecret(question) {
77
- // Non-TTY fallback: piped/redirected input can't use raw mode.
78
- // rl.close() would discard buffered stdin, so use readline directly instead.
79
- if (!process.stdin.isTTY) {
80
- return new Promise((resolve) => rl.question(question, resolve));
81
- }
82
-
83
- return new Promise((resolve) => {
84
- // Close readline so it stops echoing input
85
- rl.close();
86
-
87
- process.stdout.write(question);
88
- const stdin = process.stdin;
89
- stdin.setRawMode(true);
90
- stdin.resume();
91
-
92
- let input = "";
93
- const onData = (ch) => {
94
- const c = ch.toString();
95
- if (c === "\n" || c === "\r") {
96
- stdin.removeListener("data", onData);
97
- stdin.setRawMode(false);
98
- stdin.pause();
99
- process.stdout.write("\n");
100
- // Recreate readline for subsequent prompts
101
- rl = createInterface({ input: process.stdin, output: process.stdout });
102
- resolve(input);
103
- } else if (c === "\u007f" || c === "\b") {
104
- if (input.length > 0) {
105
- input = input.slice(0, -1);
106
- process.stdout.write("\b \b");
107
- }
108
- } else if (c === "\u0003") {
109
- process.exit(1);
110
- } else {
111
- input += c;
112
- process.stdout.write("*");
113
- }
114
- };
115
- stdin.on("data", onData);
116
- });
117
- }
118
-
119
- function askChoice(question, choices) {
120
- return new Promise((resolve) => {
121
- const choiceStr = choices.map((c, i) => ` ${i + 1}) ${c}`).join("\n");
122
- process.stdout.write(`${question}\n${choiceStr}\n`);
123
- rl.question("? Choice: ", (answer) => {
124
- const idx = parseInt(answer, 10) - 1;
125
- if (idx >= 0 && idx < choices.length) {
126
- resolve(choices[idx]);
127
- } else {
128
- const match = choices.find(
129
- (c) => c.toLowerCase() === answer.trim().toLowerCase()
130
- );
131
- resolve(match || choices[0]);
132
- }
133
- });
134
- });
135
- }
136
-
137
- function spinner(text) {
138
- const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
139
- let i = 0;
140
- const id = setInterval(() => {
141
- process.stdout.write(`\r${frames[i++ % frames.length]} ${text}`);
142
- }, 80);
143
- return {
144
- stop(result) {
145
- clearInterval(id);
146
- process.stdout.write(`\r✓ ${result}\n`);
147
- },
148
- fail(msg) {
149
- clearInterval(id);
150
- process.stdout.write(`\r✗ ${msg}\n`);
151
- },
152
- };
153
- }
154
-
155
- async function httpPost(url, body) {
156
- const response = await fetch(url, {
157
- method: "POST",
158
- headers: { "Content-Type": "application/json" },
159
- body: JSON.stringify(body),
160
- });
161
- const data = await response.json();
162
- return { status: response.status, ok: response.ok, data };
163
- }
164
-
165
- async function graphql(endpoint, token, query, variables) {
166
- const response = await fetch(`${endpoint}/api/graphql`, {
167
- method: "POST",
168
- headers: {
169
- "Content-Type": "application/json",
170
- Authorization: `Bearer ${token}`,
171
- },
172
- body: JSON.stringify({ query, variables }),
173
- });
174
- const data = await response.json();
175
- if (data.errors?.length) {
176
- throw new Error(data.errors[0].message);
177
- }
178
- return data.data;
179
- }
180
-
181
- function toClientId(companyName) {
182
- return companyName
183
- .toLowerCase()
184
- .replace(/[^a-z0-9]+/g, "-")
185
- .replace(/^-|-$/g, "");
186
- }
76
+ // setupLocalMemory + its `spinner` helper were the legacy "bring up
77
+ // Postgres + Ollama" wrapper for the in-process memory server. Removed
78
+ // in favour of:
79
+ // - `tes config local` writes the plugin config + prints engine
80
+ // bring-up instructions
81
+ // - `cd packages/memory-engine && docker compose up -d` → runs the
82
+ // actual engine
83
+ // `ask` is kept for any future interactive prompts.
187
84
 
188
- async function setupLocalMemory() {
189
- console.log(`\n Local Memory Setup\n`);
190
85
 
191
- // Check Docker
192
- try {
193
- execFileSync("docker", ["info"], { stdio: "pipe" });
194
- } catch {
195
- console.error(" Error: Docker is required. Install it from https://docker.com\n");
196
- process.exit(1);
197
- }
198
-
199
- const memoryDir = new URL("../packages/memory", import.meta.url).pathname;
86
+ async function main() {
87
+ const flags = parseArgs();
88
+ const TES_ENDPOINT = flags.endpoint || DEFAULT_ENDPOINT;
200
89
 
201
- // Start infrastructure + memory server
202
- const infraSpinner = spinner("Starting memory server + PostgreSQL + Ollama...");
203
- try {
204
- execFileSync("docker", ["compose", "up", "-d", "memory", "postgres", "ollama"], {
205
- cwd: memoryDir,
206
- stdio: "pipe",
207
- });
208
- infraSpinner.stop("Memory stack running!");
209
- } catch (err) {
210
- infraSpinner.fail(`Failed to start: ${err.message}`);
211
- process.exit(1);
90
+ if (flags.command === "doctor") {
91
+ const code = await runDoctorCommand(flags);
92
+ rl.close();
93
+ process.exit(code);
212
94
  }
213
95
 
214
- // Pull models
215
- const embModel = process.env.EMBEDDING_MODEL || "nomic-embed-text";
216
- const llmModel = process.env.LLM_MODEL || "llama3.2:3b";
217
-
218
- const embSpinner = spinner(`Pulling ${embModel}...`);
219
- try {
220
- execFileSync("docker", ["compose", "exec", "ollama", "ollama", "pull", embModel], {
221
- cwd: memoryDir,
222
- stdio: "pipe",
96
+ // SDK login (browser-based OAuth) — replaces the old in-terminal
97
+ // setupHostedTes flow. `login` opens api.pentatonic.com/cli-init in
98
+ // a browser, listens on localhost for the OAuth callback, exchanges
99
+ // for an access token, mints a long-lived tes_* via createClientApiToken,
100
+ // writes ~/.config/tes/credentials.json. `init` is kept as a one-major
101
+ // alias (Task 10).
102
+ if (flags.command === "login") {
103
+ const { runLoginCommand } = await import("./commands/login.js");
104
+ const { exitCode } = await runLoginCommand({
105
+ endpoint: TES_ENDPOINT,
223
106
  });
224
- embSpinner.stop(`${embModel} ready!`);
225
- } catch {
226
- embSpinner.fail(`Failed to pull ${embModel}. Run manually: docker compose exec ollama ollama pull ${embModel}`);
107
+ rl.close();
108
+ process.exit(exitCode);
227
109
  }
228
110
 
229
- const llmSpinner = spinner(`Pulling ${llmModel}...`);
230
- try {
231
- execFileSync("docker", ["compose", "exec", "ollama", "ollama", "pull", llmModel], {
232
- cwd: memoryDir,
233
- stdio: "pipe",
111
+ // SDK login identity check. Named `whoami` rather than `status`
112
+ // because the corpus subcommand `tes status` already exists (shows
113
+ // tracked repos). Matches the standard CLI convention for "who am
114
+ // I logged in as" and avoids the conflict.
115
+ if (flags.command === "whoami") {
116
+ const { runWhoamiCommand } = await import("./commands/whoami.js");
117
+ const { exitCode } = await runWhoamiCommand();
118
+ rl.close();
119
+ process.exit(exitCode);
120
+ }
121
+
122
+ // tes config <local|hosted|show> — point Claude Code's tes-memory
123
+ // plugin at a memory backend, or inspect what's configured. Each
124
+ // subcommand is a thin scaffold:
125
+ // local → write mode: local + memory_url; print engine bring-up steps
126
+ // hosted → run the login flow (delegates to runLoginCommand)
127
+ // show → read and print the current plugin config
128
+ // Future: `tes config set <key> <value>` for engine env-var tweaks.
129
+ if (flags.command === "config") {
130
+ const sub = process.argv.slice(3).find((a) => !a.startsWith("--"));
131
+ const { runConfigCommand } = await import("./commands/config.js");
132
+ const { exitCode } = await runConfigCommand({
133
+ sub,
134
+ endpoint: TES_ENDPOINT,
135
+ engineUrl: flags.engineUrl,
234
136
  });
235
- llmSpinner.stop(`${llmModel} ready!`);
236
- } catch {
237
- llmSpinner.fail(`Failed to pull ${llmModel}. Run manually: docker compose exec ollama ollama pull ${llmModel}`);
238
- }
239
-
240
- // Write local config (warn if hosted config exists)
241
- const configDir = join(homedir(), ".claude-pentatonic");
242
- if (!existsSync(configDir)) {
243
- mkdirSync(configDir, { recursive: true });
244
- }
245
-
246
- const configPath = join(configDir, "tes-memory.local.md");
247
- if (existsSync(configPath)) {
248
- const existing = readFileSync(configPath, "utf-8");
249
- if (existing.includes("tes_endpoint") && !existing.includes("mode: local")) {
250
- console.log("\n ⚠ Hosted TES config detected. Switching to local mode will");
251
- console.log(" disable hosted memory. To restore, run: npx @pentatonic-ai/ai-agent-sdk init\n");
252
- const confirm = await ask(" Switch to local mode? (y/n): ");
253
- if (confirm.toLowerCase() !== "y") {
254
- console.log(" Cancelled. Hosted config unchanged.\n");
255
- rl.close();
256
- return;
137
+ rl.close();
138
+ process.exit(exitCode);
139
+ }
140
+
141
+ // Corpus subcommands — onboarding/repo ingest (spec 01)
142
+ const CORPUS_COMMANDS = new Set([
143
+ "onboard", "ingest", "status", "resync", "corpus",
144
+ "install-git-hook", "ingest-paths",
145
+ ]);
146
+ if (CORPUS_COMMANDS.has(flags.command)) {
147
+ const corpusCli = await import("../packages/memory/src/corpus/cli.js");
148
+ const subArgs = process.argv.slice(2).filter((a) => a !== flags.command);
149
+ const ctx = { ask, close: () => rl.close() };
150
+ let code = 0;
151
+ try {
152
+ switch (flags.command) {
153
+ case "onboard": code = await corpusCli.cmdOnboard(subArgs, ctx); break;
154
+ case "ingest": code = await corpusCli.cmdIngest(subArgs); break;
155
+ case "status": code = await corpusCli.cmdStatus(); break;
156
+ case "resync": code = await corpusCli.cmdResync(subArgs); break;
157
+ case "corpus": code = await corpusCli.cmdCorpus(subArgs, ctx); break;
158
+ case "install-git-hook": code = await corpusCli.cmdInstallGitHook(); break;
159
+ case "ingest-paths": code = await corpusCli.cmdIngestPaths(subArgs); break;
257
160
  }
161
+ } catch (err) {
162
+ process.stderr.write(`Error: ${err.message}\n`);
163
+ code = 2;
258
164
  }
259
- }
260
-
261
- writeFileSync(
262
- configPath,
263
- `---
264
- mode: local
265
- memory_url: http://localhost:3333
266
- ---
267
- `
268
- );
269
-
270
- console.log(`\n Config written to ${configPath}`);
271
-
272
- const sdkDir = new URL("..", import.meta.url).pathname;
273
-
274
- console.log(`
275
- Memory server: http://localhost:3333
276
- Hooks are auto-configured to use local memory.
277
-
278
- Install the plugin in Claude Code:
279
- /plugin marketplace add Pentatonic-Ltd/ai-agent-sdk
280
- /plugin install tes-memory@pentatonic-ai
281
-
282
- You're ready! Every prompt auto-searches memory,
283
- every turn auto-stores. No MCP setup needed.
284
- `);
285
-
286
- rl.close();
287
- }
288
-
289
- async function main() {
290
- const flags = parseArgs();
291
- const TES_ENDPOINT = flags.endpoint || DEFAULT_ENDPOINT;
292
-
293
- if (flags.command === "memory") {
294
- await setupLocalMemory();
295
- return;
296
- }
297
-
298
- if (flags.command === "doctor") {
299
- const code = await runDoctorCommand(flags);
300
165
  rl.close();
301
166
  process.exit(code);
302
167
  }
303
168
 
304
- if (flags.command !== "init") {
305
- console.log(`
169
+ console.log(`
306
170
  @pentatonic-ai/ai-agent-sdk
307
171
 
308
172
  Usage:
309
- npx @pentatonic-ai/ai-agent-sdk init Set up hosted TES account
310
- npx @pentatonic-ai/ai-agent-sdk memory Set up local memory stack
173
+ npx @pentatonic-ai/ai-agent-sdk login First-time hosted setup: browser sign-in + writes credentials
174
+ npx @pentatonic-ai/ai-agent-sdk whoami Show current login identity
175
+ npx @pentatonic-ai/ai-agent-sdk config <sub> Configure memory backend; see 'config --help'
311
176
  npx @pentatonic-ai/ai-agent-sdk doctor Run health checks (exit 0/1/2)
312
- npx @pentatonic-ai/ai-agent-sdk init --endpoint URL Use a custom TES endpoint
177
+
178
+ config subcommands:
179
+ config local Point plugin at a local memory engine
180
+ config hosted Switch to hosted (delegates to login)
181
+ config show Print current plugin config + creds
182
+
183
+ Memory corpus (onboarding):
184
+ npx @pentatonic-ai/ai-agent-sdk onboard Interactive: pick paths, ingest, install hooks
185
+ npx @pentatonic-ai/ai-agent-sdk ingest <path> One-shot ingest of a path (any folder works)
186
+ npx @pentatonic-ai/ai-agent-sdk status Show tracked paths and corpus stats
187
+ npx @pentatonic-ai/ai-agent-sdk resync [<path>] Delta-sync (or all tracked paths)
188
+ npx @pentatonic-ai/ai-agent-sdk corpus list List tracked paths
189
+ npx @pentatonic-ai/ai-agent-sdk corpus remove <path> Stop tracking a path
190
+ npx @pentatonic-ai/ai-agent-sdk corpus reset Wipe local corpus state
191
+ npx @pentatonic-ai/ai-agent-sdk install-git-hook Install post-commit hook in cwd
192
+
193
+ Corpus commands route to the backend configured via 'config' (local engine
194
+ or hosted TES). Override with env vars: MEMORY_ENGINE_URL, TES_ENDPOINT, …
313
195
 
314
196
  doctor flags:
315
197
  --json Emit a JSON report
@@ -319,221 +201,8 @@ doctor flags:
319
201
  --timeout <ms> Per-check timeout (default 10000)
320
202
 
321
203
  For docs, see https://api.pentatonic.com
322
- `);
323
- process.exit(0);
324
- }
325
-
326
- const isLocal = /^http:\/\/(localhost|127\.0\.0\.1)(:\d+)?(\/|$)/.test(TES_ENDPOINT);
327
- if (!TES_ENDPOINT.startsWith("https://") && !isLocal) {
328
- console.error(`\n Error: endpoint must use https:// (http:// is only allowed for localhost)\n`);
329
- process.exit(1);
330
- }
331
-
332
- console.log(`\n Welcome to Pentatonic AI Events SDK`);
333
- if (TES_ENDPOINT !== DEFAULT_ENDPOINT) {
334
- console.log(` Using endpoint: ${TES_ENDPOINT}`);
335
- }
336
- console.log("");
337
-
338
- // Collect info
339
- const email = await ask("? Email: ");
340
- const clientId = toClientId(await ask("? Client ID: "));
341
- const password = await askSecret("? Password: ");
342
- const region = await askChoice("? Region:", ["EU", "US"]);
343
-
344
- // Try login first — account may already be verified from a previous run
345
- let accessToken = null;
346
- const loginSpinner = spinner("Checking for existing account...");
347
- try {
348
- const { ok, data } = await httpPost(
349
- `${TES_ENDPOINT}/api/enrollment/login`,
350
- { email, password, clientId }
351
- );
352
- if (ok && data.tokens?.accessToken) {
353
- accessToken = data.tokens.accessToken;
354
- loginSpinner.stop("Account already verified!");
355
- } else {
356
- loginSpinner.stop("No existing account found.");
357
- }
358
- } catch {
359
- loginSpinner.stop("No existing account found.");
360
- }
361
-
362
- // If not already verified, submit enrollment
363
- if (!accessToken) {
364
- const enrollSpinner = spinner("Creating account...");
365
- try {
366
- const { ok, data } = await httpPost(
367
- `${TES_ENDPOINT}/api/enrollment/submit`,
368
- {
369
- clientId,
370
- companyName: clientId,
371
- industryType: "technology",
372
- authProvider: "native",
373
- adminEmail: email,
374
- adminPassword: password,
375
- region: region.toLowerCase(),
376
- }
377
- );
378
-
379
- if (!ok) {
380
- const errors = data.errors || {};
381
- const isPending =
382
- errors.clientId?.includes("already pending") ||
383
- errors.adminEmail?.includes("already has a pending");
384
- const isAlreadyRegistered =
385
- errors.clientId?.includes("already registered");
386
-
387
- if (isPending) {
388
- enrollSpinner.stop("Enrollment already pending — waiting for verification.");
389
- } else if (isAlreadyRegistered) {
390
- enrollSpinner.fail(
391
- "This client ID is already registered.\n" +
392
- " If you belong to this organization, ask your admin to invite you.\n" +
393
- " Then run this command again — it will log you in automatically."
394
- );
395
- process.exit(1);
396
- } else {
397
- enrollSpinner.fail(
398
- data.message || Object.values(errors).join(", ") || "Enrollment failed"
399
- );
400
- process.exit(1);
401
- }
402
- } else {
403
- enrollSpinner.stop("Account created! Check your email to verify.");
404
- }
405
- } catch (err) {
406
- enrollSpinner.fail(`Failed to connect: ${err.message}`);
407
- process.exit(1);
408
- }
409
-
410
- // Poll for verification
411
- console.log("\n Waiting for email verification...");
412
- console.log(" (Check your inbox and click the verification link)\n");
413
-
414
- const pollSpinner = spinner("Waiting for verification...");
415
- const startTime = Date.now();
416
-
417
- while (Date.now() - startTime < POLL_TIMEOUT_MS) {
418
- await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
419
-
420
- try {
421
- const { ok, data } = await httpPost(
422
- `${TES_ENDPOINT}/api/enrollment/login`,
423
- { email, password, clientId }
424
- );
425
-
426
- if (ok && data.tokens?.accessToken) {
427
- accessToken = data.tokens.accessToken;
428
- pollSpinner.stop("Email verified!");
429
- break;
430
- }
431
- } catch {
432
- // Not verified yet, keep polling
433
- }
434
- }
435
-
436
- if (!accessToken) {
437
- pollSpinner.fail(
438
- "Verification timed out. Run `npx @pentatonic-ai/ai-agent-sdk init` again — it will resume where you left off."
439
- );
440
- process.exit(1);
441
- }
442
- }
443
-
444
- // Get API key — use the service token created during enrollment,
445
- // or create a new one if not available (e.g., existing account login)
446
- const keySpinner = spinner("Getting API key...");
447
- try {
448
- let apiKey;
449
-
450
- // Try to retrieve the enrollment service token first (created during verification)
451
- try {
452
- const tokenRes = await fetch(
453
- `${TES_ENDPOINT}/api/enrollment/service-token?client_id=${clientId}`
454
- );
455
- if (tokenRes.ok) {
456
- const tokenData = await tokenRes.json();
457
- if (tokenData.token) {
458
- apiKey = tokenData.token;
459
- }
460
- }
461
- } catch {
462
- // Service token not available, will create one
463
- }
464
-
465
- // Fallback: create a new token via GraphQL
466
- if (!apiKey) {
467
- const result = await graphql(
468
- TES_ENDPOINT,
469
- accessToken,
470
- `mutation CreateApiToken($clientId: String!, $input: CreateApiTokenInput!) {
471
- createClientApiToken(clientId: $clientId, input: $input) {
472
- success
473
- plainTextToken
474
- }
475
- }`,
476
- {
477
- clientId,
478
- input: {
479
- name: "ai-events-sdk",
480
- role: "agent-events",
481
- },
482
- }
483
- );
484
- apiKey = result.createClientApiToken.plainTextToken;
485
- }
486
-
487
- keySpinner.stop("API key ready!");
488
-
489
- // Print credentials
490
- const clientEndpoint =
491
- TES_ENDPOINT === DEFAULT_ENDPOINT
492
- ? `https://${clientId}.api.pentatonic.com`
493
- : TES_ENDPOINT;
494
-
495
- console.log("\n Add these to your environment:\n");
496
- console.log(` TES_ENDPOINT=${clientEndpoint}`);
497
- console.log(` TES_CLIENT_ID=${clientId}`);
498
- console.log(` TES_API_KEY=${apiKey}`);
499
- console.log("");
500
-
501
- // Install SDK
502
- const installChoice = await askChoice("Install SDK:", [
503
- "npm install @pentatonic-ai/ai-agent-sdk",
504
- "pip install pentatonic-ai-agent-sdk",
505
- "Skip — I'll install manually",
506
- ]);
507
-
508
- if (installChoice.startsWith("npm")) {
509
- const installSpinner = spinner("Installing @pentatonic-ai/ai-agent-sdk...");
510
- try {
511
- execFileSync("npm", ["install", "@pentatonic-ai/ai-agent-sdk"], { stdio: "pipe" });
512
- installSpinner.stop("@pentatonic-ai/ai-agent-sdk installed!");
513
- } catch {
514
- installSpinner.fail("Install failed. Run manually: npm install @pentatonic-ai/ai-agent-sdk");
515
- }
516
- } else if (installChoice.startsWith("pip")) {
517
- const installSpinner = spinner("Installing pentatonic-ai-agent-sdk...");
518
- try {
519
- execFileSync("pip", ["install", "pentatonic-ai-agent-sdk"], { stdio: "pipe" });
520
- installSpinner.stop("pentatonic-ai-agent-sdk installed!");
521
- } catch {
522
- installSpinner.fail("Install failed. Run manually: pip install pentatonic-ai-agent-sdk");
523
- }
524
- } else {
525
- console.log("\n Install later with:");
526
- console.log(" npm install @pentatonic-ai/ai-agent-sdk");
527
- console.log(" pip install pentatonic-ai-agent-sdk");
528
- }
529
-
530
- console.log(" You're ready! See docs at https://api.pentatonic.com\n");
531
- } catch (err) {
532
- keySpinner.fail(`Failed to generate key: ${err.message}`);
533
- process.exit(1);
534
- }
535
-
536
- rl.close();
204
+ `);
205
+ process.exit(0);
537
206
  }
538
207
 
539
208
  main().catch((err) => {