thinyai 0.1.1 → 0.1.2
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/dist/bin.js +53 -35
- package/package.json +6 -6
package/dist/bin.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/main.ts
|
|
4
4
|
import { createInterface } from "readline/promises";
|
|
5
|
-
import { clearLine, cursorTo } from "readline";
|
|
5
|
+
import { clearLine, cursorTo, emitKeypressEvents } from "readline";
|
|
6
6
|
import { stdin, stdout } from "process";
|
|
7
7
|
import { mkdirSync } from "fs";
|
|
8
8
|
import { homedir } from "os";
|
|
@@ -452,9 +452,9 @@ async function createAgent(config) {
|
|
|
452
452
|
const seed = config.systemPrompt && !history.some((m) => m.role === "system") ? [systemMessage(config.systemPrompt), ...history] : history;
|
|
453
453
|
const generate = composeModel(extensions.middleware.model, async (req) => {
|
|
454
454
|
if (opts.onToken && config.model.stream) {
|
|
455
|
-
return assembleStream(config.model.stream(req.messages, req.tools), opts.onToken);
|
|
455
|
+
return assembleStream(config.model.stream(req.messages, req.tools, opts.signal), opts.onToken);
|
|
456
456
|
}
|
|
457
|
-
return config.model.generate(req.messages, req.tools);
|
|
457
|
+
return config.model.generate(req.messages, req.tools, opts.signal);
|
|
458
458
|
});
|
|
459
459
|
const runComposedTool = composeTool(
|
|
460
460
|
extensions.middleware.tool,
|
|
@@ -829,44 +829,38 @@ function buildToolOptions(tools) {
|
|
|
829
829
|
if (tools.length === 0) return { tools: void 0, toolChoice: void 0 };
|
|
830
830
|
return { tools: toAiTools(tools), toolChoice: "auto" };
|
|
831
831
|
}
|
|
832
|
+
var KNOWN_PROVIDERS = /* @__PURE__ */ new Set(["openai", "openai-compat", "anthropic"]);
|
|
832
833
|
function resolveModel(model, opts) {
|
|
833
834
|
if (typeof model !== "string") return model;
|
|
834
835
|
const colonIdx = model.indexOf(":");
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
836
|
+
const prefix = colonIdx === -1 ? "" : model.slice(0, colonIdx);
|
|
837
|
+
if (KNOWN_PROVIDERS.has(prefix)) {
|
|
838
|
+
const modelId = model.slice(colonIdx + 1);
|
|
839
|
+
if (prefix === "anthropic") {
|
|
840
|
+
return createAnthropic({ baseURL: opts.anthropic?.baseURL, apiKey: opts.anthropic?.apiKey })(
|
|
841
|
+
modelId
|
|
839
842
|
);
|
|
840
843
|
}
|
|
841
|
-
return createOpenAI({ baseURL: opts.openai?.baseURL, apiKey: opts.openai?.apiKey })(model);
|
|
842
|
-
}
|
|
843
|
-
const provider = model.slice(0, colonIdx);
|
|
844
|
-
const modelId = model.slice(colonIdx + 1);
|
|
845
|
-
if (provider === "openai" || provider === "openai-compat") {
|
|
846
844
|
return createOpenAI({ baseURL: opts.openai?.baseURL, apiKey: opts.openai?.apiKey })(modelId);
|
|
847
845
|
}
|
|
848
|
-
if (
|
|
849
|
-
return createAnthropic({ baseURL: opts.anthropic
|
|
850
|
-
|
|
846
|
+
if (opts.anthropic?.baseURL) {
|
|
847
|
+
return createAnthropic({ baseURL: opts.anthropic.baseURL, apiKey: opts.anthropic.apiKey })(
|
|
848
|
+
model
|
|
851
849
|
);
|
|
852
850
|
}
|
|
853
|
-
|
|
854
|
-
`unknown provider "${provider}" in model string "${model}"
|
|
855
|
-
Supported prefixes: "openai:<id>", "openai-compat:<id>", "anthropic:<id>"
|
|
856
|
-
Or omit the prefix and set THINY_OPENAI_BASE_URL / THINY_ANTHROPIC_BASE_URL instead.
|
|
857
|
-
Or pass a LanguageModel instance directly.`
|
|
858
|
-
);
|
|
851
|
+
return createOpenAI({ baseURL: opts.openai?.baseURL, apiKey: opts.openai?.apiKey })(model);
|
|
859
852
|
}
|
|
860
853
|
function aiSdkModel(opts) {
|
|
861
854
|
const model = resolveModel(opts.model, opts);
|
|
862
855
|
const maxRetries = opts.maxRetries ?? 2;
|
|
863
856
|
return {
|
|
864
|
-
async generate(messages, tools) {
|
|
857
|
+
async generate(messages, tools, signal) {
|
|
865
858
|
const result = await generateText({
|
|
866
859
|
model,
|
|
867
860
|
messages: toCoreMessages(messages),
|
|
868
861
|
...buildToolOptions(tools),
|
|
869
|
-
maxRetries
|
|
862
|
+
maxRetries,
|
|
863
|
+
abortSignal: signal
|
|
870
864
|
});
|
|
871
865
|
return {
|
|
872
866
|
text: result.text || void 0,
|
|
@@ -880,12 +874,13 @@ function aiSdkModel(opts) {
|
|
|
880
874
|
usage: normalizeUsage(result.usage)
|
|
881
875
|
};
|
|
882
876
|
},
|
|
883
|
-
async *stream(messages, tools) {
|
|
877
|
+
async *stream(messages, tools, signal) {
|
|
884
878
|
const result = streamText({
|
|
885
879
|
model,
|
|
886
880
|
messages: toCoreMessages(messages),
|
|
887
881
|
...buildToolOptions(tools),
|
|
888
|
-
maxRetries
|
|
882
|
+
maxRetries,
|
|
883
|
+
abortSignal: signal
|
|
889
884
|
});
|
|
890
885
|
for await (const part of result.fullStream) {
|
|
891
886
|
if (part.type === "text-delta") {
|
|
@@ -956,7 +951,7 @@ function pinoLogger(opts = {}) {
|
|
|
956
951
|
);
|
|
957
952
|
}
|
|
958
953
|
if (opts.file) {
|
|
959
|
-
const destination = pino2.destination({ dest: opts.file, sync:
|
|
954
|
+
const destination = pino2.destination({ dest: opts.file, sync: true });
|
|
960
955
|
return adaptPinoLogger(pino2(pinoConfig(level), destination));
|
|
961
956
|
}
|
|
962
957
|
const usePretty = opts.pretty ?? (opts.file === void 0 && process.env.NODE_ENV !== "production");
|
|
@@ -1827,7 +1822,9 @@ async function runCli() {
|
|
|
1827
1822
|
namespace: process.env.MEMWAL_NAMESPACE ?? userId
|
|
1828
1823
|
}) : walrusMemoryPlugin({
|
|
1829
1824
|
client: walrus,
|
|
1830
|
-
|
|
1825
|
+
// Stable per-user location (~/.thiny) so cross-session memory works no matter which
|
|
1826
|
+
// directory `thiny` is launched from — a cwd-relative file would fragment per folder.
|
|
1827
|
+
pointers: filePointerStore(process.env.WALRUS_POINTERS ?? join(thinyDir, "thiny-pointers.json")),
|
|
1831
1828
|
userId,
|
|
1832
1829
|
onStoreStart: () => pendingWrites += 1,
|
|
1833
1830
|
onStore: (ref) => {
|
|
@@ -1899,6 +1896,7 @@ async function runCli() {
|
|
|
1899
1896
|
if (walrusAudit)
|
|
1900
1897
|
renderInfo(`Walrus audit: ON (${network}) \u2014 each turn's action log is stored + verifiable`);
|
|
1901
1898
|
const rl = createInterface({ input: stdin, output: stdout });
|
|
1899
|
+
emitKeypressEvents(stdin);
|
|
1902
1900
|
const spinner = new Spinner();
|
|
1903
1901
|
const flushMemory = memoryPlugin.flush;
|
|
1904
1902
|
const PROMPT = "\x1B[36mYou\x1B[0m \x1B[2m\u203A\x1B[0m ";
|
|
@@ -2011,10 +2009,15 @@ Audit trail ${blobId}
|
|
|
2011
2009
|
continue;
|
|
2012
2010
|
}
|
|
2013
2011
|
renderAgentLabel(personaName);
|
|
2014
|
-
spinner.start("thinking\u2026");
|
|
2012
|
+
spinner.start("thinking\u2026 (esc to cancel)");
|
|
2015
2013
|
budget.reset();
|
|
2016
2014
|
resetTurn(turn);
|
|
2017
2015
|
const startedAt = Date.now();
|
|
2016
|
+
const ac = new AbortController();
|
|
2017
|
+
const onKey = (_s, key) => {
|
|
2018
|
+
if (key?.name === "escape") ac.abort();
|
|
2019
|
+
};
|
|
2020
|
+
stdin.on("keypress", onKey);
|
|
2018
2021
|
try {
|
|
2019
2022
|
let firstToken = true;
|
|
2020
2023
|
const stream = createMarkdownWriter((s) => stdout.write(s));
|
|
@@ -2026,15 +2029,28 @@ Audit trail ${blobId}
|
|
|
2026
2029
|
spinner.start("running\u2026");
|
|
2027
2030
|
};
|
|
2028
2031
|
agent.events.on("beforeToolCall", toolHandler);
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
+
let reply;
|
|
2033
|
+
try {
|
|
2034
|
+
reply = await agent.run(trimmed, {
|
|
2035
|
+
sessionId: currentSessionId,
|
|
2036
|
+
signal: ac.signal,
|
|
2037
|
+
onToken: (delta) => {
|
|
2038
|
+
spinner.stop();
|
|
2039
|
+
firstToken = false;
|
|
2040
|
+
stream.push(delta);
|
|
2041
|
+
}
|
|
2042
|
+
});
|
|
2043
|
+
} catch (err) {
|
|
2044
|
+
if (ac.signal.aborted) {
|
|
2032
2045
|
spinner.stop();
|
|
2033
|
-
|
|
2034
|
-
|
|
2046
|
+
stream.end();
|
|
2047
|
+
stdout.write("\n \x1B[2m\u2298 cancelled (Esc)\x1B[0m\n");
|
|
2048
|
+
continue;
|
|
2035
2049
|
}
|
|
2036
|
-
|
|
2037
|
-
|
|
2050
|
+
throw err;
|
|
2051
|
+
} finally {
|
|
2052
|
+
agent.events.off("beforeToolCall", toolHandler);
|
|
2053
|
+
}
|
|
2038
2054
|
spinner.stop();
|
|
2039
2055
|
if (firstToken) {
|
|
2040
2056
|
stream.push(reply.length > 0 ? reply : "\x1B[2m(model returned empty response)\x1B[0m");
|
|
@@ -2079,6 +2095,8 @@ Audit trail ${blobId}
|
|
|
2079
2095
|
looksLikeModelError ? `${msg}
|
|
2080
2096
|
\u21B3 Check your model, base URL, and API key (run \`thiny init\`, or edit ~/.thiny/config.json / .env).` : msg
|
|
2081
2097
|
);
|
|
2098
|
+
} finally {
|
|
2099
|
+
stdin.off("keypress", onKey);
|
|
2082
2100
|
}
|
|
2083
2101
|
}
|
|
2084
2102
|
} finally {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thinyai",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Thiny AI — a beautiful terminal agent: interactive chat, tools, Walrus memory, and Sui execution.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -36,14 +36,14 @@
|
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"tsup": "^8.5.1",
|
|
38
38
|
"typescript": "^5.5.0",
|
|
39
|
-
"@thiny/
|
|
40
|
-
"@thiny/core": "0.1.0",
|
|
39
|
+
"@thiny/logger-pino": "0.1.0",
|
|
41
40
|
"@thiny/model-aisdk": "0.1.0",
|
|
41
|
+
"@thiny/core": "0.1.0",
|
|
42
42
|
"@thiny/memory-memwal": "0.1.0",
|
|
43
|
-
"@thiny/
|
|
44
|
-
"@thiny/walrus": "0.1.0",
|
|
43
|
+
"@thiny/mcp": "0.1.0",
|
|
45
44
|
"@thiny/plugin-agents": "0.1.0",
|
|
46
|
-
"@thiny/skills": "0.1.0"
|
|
45
|
+
"@thiny/skills": "0.1.0",
|
|
46
|
+
"@thiny/walrus": "0.1.0"
|
|
47
47
|
},
|
|
48
48
|
"author": "Thiny AI",
|
|
49
49
|
"engines": {
|