@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.
- package/README.md +345 -174
- package/bin/__tests__/callback-server.test.js +70 -0
- package/bin/__tests__/credentials.test.js +58 -0
- package/bin/__tests__/login.test.js +210 -0
- package/bin/__tests__/pkce.test.js +39 -0
- package/bin/__tests__/whoami.test.js +77 -0
- package/bin/cli.js +109 -440
- package/bin/commands/config.js +251 -0
- package/bin/commands/login.js +219 -0
- package/bin/commands/whoami.js +41 -0
- package/bin/lib/callback-server.js +137 -0
- package/bin/lib/credentials.js +100 -0
- package/bin/lib/pkce.js +26 -0
- package/package.json +4 -2
- package/packages/doctor/__tests__/detect.test.js +2 -6
- package/packages/doctor/src/checks/local-memory.js +164 -196
- package/packages/doctor/src/detect.js +11 -3
- package/packages/memory/src/__tests__/corpus-chunkers.test.js +143 -0
- package/packages/memory/src/__tests__/corpus-discover.test.js +175 -0
- package/packages/memory/src/__tests__/corpus-ingest.test.js +236 -0
- package/packages/memory/src/__tests__/corpus-signatures.test.js +175 -0
- package/packages/memory/src/__tests__/corpus-state.test.js +161 -0
- package/packages/memory/src/__tests__/ingest-corpus-opts.test.js +129 -0
- package/packages/memory/src/__tests__/search-kind.test.js +108 -0
- package/packages/memory/src/corpus/adapters.js +398 -0
- package/packages/memory/src/corpus/chunkers.js +328 -0
- package/packages/memory/src/corpus/cli.js +613 -0
- package/packages/memory/src/corpus/discover.js +379 -0
- package/packages/memory/src/corpus/index.js +68 -0
- package/packages/memory/src/corpus/ingest.js +356 -0
- package/packages/memory/src/corpus/signatures.js +280 -0
- package/packages/memory/src/corpus/state.js +134 -0
- package/packages/memory/src/index.js +18 -0
- package/packages/memory/src/ingest.js +20 -11
- package/packages/memory/src/openclaw/index.js +39 -1
- package/packages/memory/src/search.js +30 -7
- package/packages/memory-engine/.env.example +13 -0
- package/packages/memory-engine/README.md +131 -0
- package/packages/memory-engine/bench/README.md +99 -0
- package/packages/memory-engine/bench/scorecards-engine/agent-coding__pentatonic-baseline__20260427-142523.json +1115 -0
- package/packages/memory-engine/bench/scorecards-engine/chat-recall__pentatonic-baseline__20260427-142648.json +819 -0
- package/packages/memory-engine/bench/scorecards-engine/circular-economy__pentatonic-baseline__20260427-142757.json +1278 -0
- package/packages/memory-engine/bench/scorecards-engine/customer-support__pentatonic-baseline__20260427-142900.json +1018 -0
- package/packages/memory-engine/bench/scorecards-engine/marketplace-ops__pentatonic-baseline__20260427-142957.json +1038 -0
- package/packages/memory-engine/bench/scorecards-engine/product-catalogue__pentatonic-baseline__20260427-143122.json +961 -0
- package/packages/memory-engine/bench/scorecards-engine-via-docker/agent-coding__pentatonic-memory__20260427-161812.json +1115 -0
- package/packages/memory-engine/bench/scorecards-engine-via-docker/chat-recall__pentatonic-memory__20260427-161701.json +819 -0
- package/packages/memory-engine/bench/scorecards-engine-via-docker/circular-economy__pentatonic-memory__20260427-161713.json +1278 -0
- package/packages/memory-engine/bench/scorecards-engine-via-docker/customer-support__pentatonic-memory__20260427-161723.json +1018 -0
- package/packages/memory-engine/bench/scorecards-engine-via-docker/marketplace-ops__pentatonic-memory__20260427-161732.json +1038 -0
- package/packages/memory-engine/bench/scorecards-engine-via-docker/product-catalogue__pentatonic-memory__20260427-161741.json +937 -0
- package/packages/memory-engine/bench/scorecards-engine-via-l2-7-layer-populated/agent-coding__pentatonic-memory__20260427-184718.json +1115 -0
- package/packages/memory-engine/bench/scorecards-engine-via-l2-7-layer-populated/chat-recall__pentatonic-memory__20260427-184614.json +819 -0
- package/packages/memory-engine/bench/scorecards-engine-via-l2-7-layer-populated/circular-economy__pentatonic-memory__20260427-184809.json +1278 -0
- package/packages/memory-engine/bench/scorecards-engine-via-l2-7-layer-populated/customer-support__pentatonic-memory__20260427-184854.json +1018 -0
- package/packages/memory-engine/bench/scorecards-engine-via-l2-7-layer-populated/marketplace-ops__pentatonic-memory__20260427-184929.json +1038 -0
- package/packages/memory-engine/bench/scorecards-engine-via-l2-7-layer-populated/product-catalogue__pentatonic-memory__20260427-185015.json +961 -0
- package/packages/memory-engine/bench/scorecards-engine-via-l2-empty-layers/agent-coding__pentatonic-memory__20260427-175252.json +1115 -0
- package/packages/memory-engine/bench/scorecards-engine-via-l2-empty-layers/chat-recall__pentatonic-memory__20260427-175312.json +819 -0
- package/packages/memory-engine/bench/scorecards-engine-via-l2-empty-layers/circular-economy__pentatonic-memory__20260427-175335.json +1278 -0
- package/packages/memory-engine/bench/scorecards-engine-via-l2-empty-layers/customer-support__pentatonic-memory__20260427-175355.json +1018 -0
- package/packages/memory-engine/bench/scorecards-engine-via-l2-empty-layers/marketplace-ops__pentatonic-memory__20260427-175413.json +1038 -0
- package/packages/memory-engine/bench/scorecards-engine-via-l2-empty-layers/product-catalogue__pentatonic-memory__20260427-175430.json +883 -0
- package/packages/memory-engine/bench/scorecards-engine-via-shim/agent-coding__pentatonic-memory__20260427-155409.json +1115 -0
- package/packages/memory-engine/bench/scorecards-engine-via-shim/chat-recall__pentatonic-memory__20260427-155421.json +819 -0
- package/packages/memory-engine/bench/scorecards-engine-via-shim/circular-economy__pentatonic-memory__20260427-155433.json +1278 -0
- package/packages/memory-engine/bench/scorecards-engine-via-shim/customer-support__pentatonic-memory__20260427-155443.json +1018 -0
- package/packages/memory-engine/bench/scorecards-engine-via-shim/marketplace-ops__pentatonic-memory__20260427-155453.json +1038 -0
- package/packages/memory-engine/bench/scorecards-engine-via-shim/product-catalogue__pentatonic-memory__20260427-155503.json +937 -0
- package/packages/memory-engine/bench/scorecards-pentatonic-baseline/agent-coding__pentatonic-memory-latest__20260427-145103.json +1115 -0
- package/packages/memory-engine/bench/scorecards-pentatonic-baseline/agent-coding__pentatonic-memory__20260427-144909.json +1115 -0
- package/packages/memory-engine/bench/scorecards-pentatonic-baseline/chat-recall__pentatonic-memory-latest__20260427-145153.json +819 -0
- package/packages/memory-engine/bench/scorecards-pentatonic-baseline/chat-recall__pentatonic-memory__20260427-145120.json +542 -0
- package/packages/memory-engine/bench/scorecards-pentatonic-baseline/circular-economy__pentatonic-memory-latest__20260427-145313.json +1278 -0
- package/packages/memory-engine/bench/scorecards-pentatonic-baseline/circular-economy__pentatonic-memory__20260427-145207.json +894 -0
- package/packages/memory-engine/bench/scorecards-pentatonic-baseline/customer-support__pentatonic-memory-latest__20260427-145412.json +1018 -0
- package/packages/memory-engine/bench/scorecards-pentatonic-baseline/customer-support__pentatonic-memory__20260427-145327.json +680 -0
- package/packages/memory-engine/bench/scorecards-pentatonic-baseline/marketplace-ops__pentatonic-memory-latest__20260427-145517.json +1038 -0
- package/packages/memory-engine/bench/scorecards-pentatonic-baseline/marketplace-ops__pentatonic-memory__20260427-145422.json +693 -0
- package/packages/memory-engine/bench/scorecards-pentatonic-baseline/product-catalogue__pentatonic-memory-latest__20260427-145616.json +961 -0
- package/packages/memory-engine/bench/scorecards-pentatonic-baseline/product-catalogue__pentatonic-memory__20260427-145528.json +727 -0
- package/packages/memory-engine/compat/Dockerfile +11 -0
- package/packages/memory-engine/compat/server.py +680 -0
- package/packages/memory-engine/docker-compose.yml +243 -0
- package/packages/memory-engine/docs/MIGRATION.md +178 -0
- package/packages/memory-engine/docs/RUNBOOK-AWS.md +375 -0
- package/packages/memory-engine/docs/why-v05-underperforms.md +138 -0
- package/packages/memory-engine/engine/README.md +52 -0
- package/packages/memory-engine/engine/l2-hybridrag-proxy.py +1543 -0
- package/packages/memory-engine/engine/l5-comms-layer.py +663 -0
- package/packages/memory-engine/engine/l6-document-store.py +1018 -0
- package/packages/memory-engine/engine/services/l2/Dockerfile +41 -0
- package/packages/memory-engine/engine/services/l2/init_databases.py +81 -0
- package/packages/memory-engine/engine/services/l2/l2-hybridrag-proxy.py +1543 -0
- package/packages/memory-engine/engine/services/l4/Dockerfile +15 -0
- package/packages/memory-engine/engine/services/l4/server.py +235 -0
- package/packages/memory-engine/engine/services/l5/Dockerfile +9 -0
- package/packages/memory-engine/engine/services/l5/l5-comms-layer.py +678 -0
- package/packages/memory-engine/engine/services/l6/Dockerfile +11 -0
- package/packages/memory-engine/engine/services/l6/l6-document-store.py +1016 -0
- package/packages/memory-engine/engine/services/nv-embed/Dockerfile +28 -0
- package/packages/memory-engine/engine/services/nv-embed/server.py +152 -0
- package/packages/memory-engine/pme_memory/__init__.py +0 -0
- package/packages/memory-engine/pme_memory/__main__.py +129 -0
- package/packages/memory-engine/pme_memory/artifacts.py +95 -0
- package/packages/memory-engine/pme_memory/embed.py +74 -0
- package/packages/memory-engine/pme_memory/health.py +36 -0
- package/packages/memory-engine/pme_memory/hygiene.py +159 -0
- package/packages/memory-engine/pme_memory/indexer.py +200 -0
- package/packages/memory-engine/pme_memory/needs.py +55 -0
- package/packages/memory-engine/pme_memory/provenance.py +80 -0
- package/packages/memory-engine/pme_memory/scoring.py +168 -0
- package/packages/memory-engine/pme_memory/search.py +52 -0
- package/packages/memory-engine/pme_memory/store.py +86 -0
- package/packages/memory-engine/pme_memory/synthesis.py +114 -0
- package/packages/memory-engine/pyproject.toml +65 -0
- package/packages/memory-engine/scripts/kg-extractor.py +557 -0
- package/packages/memory-engine/scripts/kg-preflexor-v2.py +738 -0
- 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
|
-
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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
|
-
//
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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
|
-
|
|
225
|
-
|
|
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
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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
|
-
|
|
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
|
|
310
|
-
npx @pentatonic-ai/ai-agent-sdk
|
|
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
|
-
|
|
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
|
-
|
|
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) => {
|