@ouro.bot/cli 0.1.0-alpha.733 → 0.1.0-alpha.734
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/changelog.json +25 -0
- package/deploy/unraid/Dockerfile +27 -0
- package/deploy/unraid/README.txt +1368 -0
- package/deploy/unraid/audit-container-spec.sh +4 -0
- package/deploy/unraid/container-runtime.json +1 -0
- package/deploy/unraid/sanctuary-acceptance-adapter.sh +37 -0
- package/deploy/unraid/sanctuary-acceptance-contract.json +200 -0
- package/deploy/unraid/sanctuary-acceptance-harness.sh +23 -0
- package/deploy/unraid/sanctuary-deployment-target.mjs +705 -0
- package/deploy/unraid/sanctuary-safe-rename.py +24 -0
- package/deploy/unraid/sanctuary-unit16-host-broker.mjs +1777 -0
- package/deploy/unraid/sanctuary-unit16-run.sh +502 -0
- package/deploy/unraid/sanctuary-unit18-target-audit.sh +27 -0
- package/deploy/unraid/sanctuary.ouro/agent.json +24 -0
- package/deploy/unraid/sanctuary.ouro/arc/README.md +3 -0
- package/deploy/unraid/sanctuary.ouro/bundle-meta.json +5 -0
- package/deploy/unraid/sanctuary.ouro/habits/sanctuary-health.md +8 -0
- package/deploy/unraid/sanctuary.ouro/provider-readiness.json +18 -0
- package/deploy/unraid/sanctuary.ouro/psyche/ASPIRATIONS.md +3 -0
- package/deploy/unraid/sanctuary.ouro/psyche/IDENTITY.md +3 -0
- package/deploy/unraid/sanctuary.ouro/psyche/LORE.md +3 -0
- package/deploy/unraid/sanctuary.ouro/psyche/SOUL.md +5 -0
- package/deploy/unraid/sanctuary.ouro/psyche/TACIT.md +7 -0
- package/deploy/unraid/sanctuary.ouro/tool-profiles.json +21 -0
- package/deploy/unraid/sanctuary.xml +25 -0
- package/dist/heart/approval-files.js +113 -0
- package/dist/heart/approval-store.js +168 -9
- package/dist/heart/auth/auth-flow.js +17 -1
- package/dist/heart/autonomy-budget.js +44 -1
- package/dist/heart/awaiting/await-scheduler.js +6 -2
- package/dist/heart/config.js +8 -0
- package/dist/heart/core.js +117 -132
- package/dist/heart/daemon/cli-exec.js +73 -1
- package/dist/heart/daemon/cli-help.js +5 -0
- package/dist/heart/daemon/cli-parse.js +6 -4
- package/dist/heart/daemon/container-credential-bootstrap.js +185 -0
- package/dist/heart/daemon/container-healthcheck.js +94 -0
- package/dist/heart/daemon/container-runtime.js +84 -0
- package/dist/heart/daemon/container-spec-auditor-main.js +156 -0
- package/dist/heart/daemon/container-spec-auditor.js +156 -0
- package/dist/heart/daemon/daemon-bootstrap-startup.js +41 -0
- package/dist/heart/daemon/daemon-entry.js +116 -28
- package/dist/heart/daemon/daemon-health.js +10 -0
- package/dist/heart/daemon/daemon.js +92 -5
- package/dist/heart/daemon/runtime-logging.js +6 -0
- package/dist/heart/daemon/runtime-mode.js +13 -2
- package/dist/heart/daemon/sanctuary-acceptance-adapter.js +2365 -0
- package/dist/heart/daemon/sanctuary-acceptance-harness.js +1624 -0
- package/dist/heart/daemon/sanctuary-acceptance-marker.js +267 -0
- package/dist/heart/daemon/sanctuary-acceptance-scenarios.js +985 -0
- package/dist/heart/daemon/sanctuary-scheduler-liveness.js +227 -0
- package/dist/heart/daemon/sanctuary-scheduler-origin.js +126 -0
- package/dist/heart/daemon/sense-manager.js +25 -6
- package/dist/heart/daemon/supercronic-supervisor.js +267 -0
- package/dist/heart/habits/habit-scheduler.js +7 -3
- package/dist/heart/identity.js +13 -3
- package/dist/heart/provider-credentials.js +9 -1
- package/dist/heart/provider-models.js +8 -0
- package/dist/heart/provider-ping.js +5 -2
- package/dist/heart/providers/openai-compatible.js +177 -0
- package/dist/heart/runtime-credentials.js +217 -8
- package/dist/heart/sense-truth.js +2 -0
- package/dist/heart/tool-approval.js +1 -0
- package/dist/heart/turn-context.js +8 -0
- package/dist/mind/prompt.js +9 -0
- package/dist/nerves/coverage/source-scanner.js +4 -4
- package/dist/nerves/index.js +75 -9
- package/dist/nerves/runtime.js +12 -0
- package/dist/repertoire/tool-arguments.js +39 -0
- package/dist/repertoire/tools-unraid.js +353 -0
- package/dist/repertoire/tools.js +19 -1
- package/dist/repertoire/unraid-client.js +155 -0
- package/dist/repertoire/unraid-restart.js +130 -0
- package/dist/senses/private-runtime-worker.js +3 -2
- package/dist/senses/sanctuary-grounding.js +118 -0
- package/dist/senses/sanctuary-health-acceptance-probe-entry.js +12 -0
- package/dist/senses/sanctuary-health-acceptance-probe.js +772 -0
- package/dist/senses/sanctuary-health-runner.js +146 -0
- package/dist/senses/sanctuary-health.js +447 -0
- package/dist/senses/sanctuary-interactive-control.js +306 -0
- package/dist/senses/sanctuary-runtime.js +206 -0
- package/dist/senses/shared-turn.js +11 -3
- package/dist/senses/telegram-approval-runtime.js +526 -0
- package/dist/senses/telegram-audit-ledger.js +220 -0
- package/dist/senses/telegram-client.js +1471 -0
- package/dist/senses/telegram-entry.js +80 -0
- package/dist/senses/telegram.js +1232 -0
- package/npm-shrinkwrap.json +6558 -0
- package/package.json +4 -2
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.SupercronicSupervisor = void 0;
|
|
37
|
+
const node_child_process_1 = require("node:child_process");
|
|
38
|
+
const fs = __importStar(require("node:fs"));
|
|
39
|
+
const path = __importStar(require("node:path"));
|
|
40
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
41
|
+
const NAMESPACE = /^(?:habit|await):[a-z0-9](?:[a-z0-9._-]{0,62})$/u;
|
|
42
|
+
const SAFE_JOB = /^[^\r\n\0]+$/u;
|
|
43
|
+
const RESTART_DELAYS_MS = [1_000, 5_000, 15_000];
|
|
44
|
+
function defaultDurableWrite(filePath, content, mode) {
|
|
45
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
46
|
+
const temporary = `${filePath}.${process.pid}.tmp`;
|
|
47
|
+
const fd = fs.openSync(temporary, "w", mode);
|
|
48
|
+
try {
|
|
49
|
+
fs.writeFileSync(fd, content, "utf8");
|
|
50
|
+
fs.fsyncSync(fd);
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
fs.closeSync(fd);
|
|
54
|
+
}
|
|
55
|
+
fs.chmodSync(temporary, mode);
|
|
56
|
+
fs.renameSync(temporary, filePath);
|
|
57
|
+
const directory = fs.openSync(path.dirname(filePath), "r");
|
|
58
|
+
try {
|
|
59
|
+
fs.fsyncSync(directory);
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
fs.closeSync(directory);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function defaultDeps() {
|
|
66
|
+
return {
|
|
67
|
+
mkdir: (target) => fs.mkdirSync(target, { recursive: true }),
|
|
68
|
+
readFile: (target) => fs.readFileSync(target, "utf8"),
|
|
69
|
+
durableWrite: defaultDurableWrite,
|
|
70
|
+
removeFile: (target) => { try {
|
|
71
|
+
fs.unlinkSync(target);
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
if (error.code !== "ENOENT")
|
|
75
|
+
throw error;
|
|
76
|
+
} },
|
|
77
|
+
processAlive: (pid) => { try {
|
|
78
|
+
process.kill(pid, 0);
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return false;
|
|
83
|
+
} },
|
|
84
|
+
spawn: (binary, args) => (0, node_child_process_1.spawn)(binary, args, { stdio: ["ignore", "inherit", "inherit"] }),
|
|
85
|
+
setTimer: (callback, delayMs) => setTimeout(callback, delayMs),
|
|
86
|
+
clearTimer: (timer) => clearTimeout(timer),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
class SupercronicSupervisor {
|
|
90
|
+
deps;
|
|
91
|
+
binaryPath;
|
|
92
|
+
crontabPath;
|
|
93
|
+
pidPath;
|
|
94
|
+
onFatal;
|
|
95
|
+
manifests = new Map();
|
|
96
|
+
child = null;
|
|
97
|
+
restartTimer = null;
|
|
98
|
+
restartCount = 0;
|
|
99
|
+
stopping = false;
|
|
100
|
+
constructor(options) {
|
|
101
|
+
this.binaryPath = options.binaryPath;
|
|
102
|
+
this.crontabPath = options.crontabPath;
|
|
103
|
+
this.pidPath = options.pidPath ?? `${options.crontabPath}.pid`;
|
|
104
|
+
this.deps = options.deps ?? defaultDeps();
|
|
105
|
+
this.onFatal = options.onFatal ?? ((error) => { throw error; });
|
|
106
|
+
}
|
|
107
|
+
start() {
|
|
108
|
+
if (this.child || this.restartTimer)
|
|
109
|
+
return;
|
|
110
|
+
this.deps.mkdir(path.dirname(this.crontabPath));
|
|
111
|
+
const priorPid = this.readPriorPid();
|
|
112
|
+
if (priorPid !== null && this.deps.processAlive(priorPid))
|
|
113
|
+
throw new Error(`Supercronic is already running with PID ${priorPid}`);
|
|
114
|
+
this.render();
|
|
115
|
+
this.stopping = false;
|
|
116
|
+
this.spawnChild();
|
|
117
|
+
(0, runtime_1.emitNervesEvent)({ component: "daemon", event: "daemon.supercronic_state", message: "Supercronic supervisor started", meta: { crontabPath: this.crontabPath } });
|
|
118
|
+
}
|
|
119
|
+
namespace(namespace) {
|
|
120
|
+
if (!NAMESPACE.test(namespace))
|
|
121
|
+
throw new Error(`invalid Supercronic namespace: ${namespace}`);
|
|
122
|
+
return {
|
|
123
|
+
sync: (jobs) => {
|
|
124
|
+
const next = new Map();
|
|
125
|
+
for (const job of jobs) {
|
|
126
|
+
if (!SAFE_JOB.test(job.id) || !SAFE_JOB.test(job.taskId) || !SAFE_JOB.test(job.schedule) || !SAFE_JOB.test(job.command)) {
|
|
127
|
+
throw new Error("invalid Supercronic job");
|
|
128
|
+
}
|
|
129
|
+
if (next.has(job.id))
|
|
130
|
+
throw new Error(`duplicate Supercronic job: ${job.id}`);
|
|
131
|
+
next.set(job.id, structuredClone(job));
|
|
132
|
+
}
|
|
133
|
+
this.manifests.set(namespace, next);
|
|
134
|
+
this.render();
|
|
135
|
+
(0, runtime_1.emitNervesEvent)({ component: "daemon", event: "daemon.supercronic_state", message: "Supercronic manifest synchronized", meta: { namespace, jobCount: jobs.length } });
|
|
136
|
+
},
|
|
137
|
+
removeAll: () => {
|
|
138
|
+
this.manifests.delete(namespace);
|
|
139
|
+
this.render();
|
|
140
|
+
},
|
|
141
|
+
list: () => [...(this.manifests.get(namespace)?.values() ?? [])].map((job) => job.taskId).sort(),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
verifyNamespace(namespace, jobs) {
|
|
145
|
+
if (!this.isHealthy())
|
|
146
|
+
return false;
|
|
147
|
+
const manifest = this.manifests.get(namespace);
|
|
148
|
+
if (!manifest || manifest.size !== jobs.length)
|
|
149
|
+
return false;
|
|
150
|
+
for (const job of jobs) {
|
|
151
|
+
const existing = manifest.get(job.id);
|
|
152
|
+
if (!existing || existing.schedule !== job.schedule || existing.command !== job.command)
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
return this.deps.readFile(this.crontabPath) === this.renderedContent();
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
verificationOutput() {
|
|
163
|
+
if (!this.isHealthy())
|
|
164
|
+
return "";
|
|
165
|
+
const expected = this.renderedContent();
|
|
166
|
+
try {
|
|
167
|
+
return this.deps.readFile(this.crontabPath) === expected ? expected : "";
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
return "";
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
authenticatedSnapshot(namespace) {
|
|
174
|
+
if (!NAMESPACE.test(namespace))
|
|
175
|
+
throw new Error(`invalid Supercronic namespace: ${namespace}`);
|
|
176
|
+
if (!this.isHealthy() || typeof this.child?.pid !== "number")
|
|
177
|
+
throw new Error("Supercronic supervisor has no healthy child");
|
|
178
|
+
const manifest = this.manifests.get(namespace);
|
|
179
|
+
if (!manifest)
|
|
180
|
+
throw new Error(`Supercronic supervisor has no manifest for ${namespace}`);
|
|
181
|
+
const renderedCrontab = this.verificationOutput();
|
|
182
|
+
if (renderedCrontab.length === 0)
|
|
183
|
+
throw new Error("Supercronic supervisor crontab is not verified");
|
|
184
|
+
return {
|
|
185
|
+
schemaVersion: "supercronic-supervisor-snapshot-v1",
|
|
186
|
+
daemonPid: process.pid,
|
|
187
|
+
childCount: 1,
|
|
188
|
+
childPid: this.child.pid,
|
|
189
|
+
healthy: true,
|
|
190
|
+
binaryPath: this.binaryPath,
|
|
191
|
+
args: ["-split-logs", "-inotify", this.crontabPath],
|
|
192
|
+
crontabPath: this.crontabPath,
|
|
193
|
+
namespace,
|
|
194
|
+
manifest: [...manifest.values()].sort((left, right) => left.id.localeCompare(right.id)).map((job) => structuredClone(job)),
|
|
195
|
+
renderedCrontab,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
isHealthy() {
|
|
199
|
+
return this.child !== null && typeof this.child.pid === "number" && this.deps.processAlive(this.child.pid);
|
|
200
|
+
}
|
|
201
|
+
async stop() {
|
|
202
|
+
this.stopping = true;
|
|
203
|
+
if (this.restartTimer)
|
|
204
|
+
this.deps.clearTimer(this.restartTimer);
|
|
205
|
+
this.restartTimer = null;
|
|
206
|
+
const child = this.child;
|
|
207
|
+
if (!child) {
|
|
208
|
+
this.deps.removeFile(this.pidPath);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
await new Promise((resolve) => {
|
|
212
|
+
let settled = false;
|
|
213
|
+
const finish = () => { if (settled)
|
|
214
|
+
return; settled = true; resolve(); };
|
|
215
|
+
child.once("exit", finish);
|
|
216
|
+
const timer = this.deps.setTimer(() => { child.kill("SIGKILL"); finish(); }, 10_000);
|
|
217
|
+
child.once("exit", () => this.deps.clearTimer(timer));
|
|
218
|
+
child.kill("SIGTERM");
|
|
219
|
+
});
|
|
220
|
+
this.child = null;
|
|
221
|
+
this.deps.removeFile(this.pidPath);
|
|
222
|
+
(0, runtime_1.emitNervesEvent)({ component: "daemon", event: "daemon.supercronic_state", message: "Supercronic supervisor stopped", meta: { crontabPath: this.crontabPath } });
|
|
223
|
+
}
|
|
224
|
+
readPriorPid() {
|
|
225
|
+
try {
|
|
226
|
+
const value = Number(this.deps.readFile(this.pidPath).trim());
|
|
227
|
+
return Number.isSafeInteger(value) && value > 0 ? value : null;
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
renderedContent() {
|
|
234
|
+
const lines = [];
|
|
235
|
+
for (const namespace of [...this.manifests.keys()].sort()) {
|
|
236
|
+
const jobs = [...this.manifests.get(namespace).values()].sort((left, right) => left.id.localeCompare(right.id));
|
|
237
|
+
for (const job of jobs)
|
|
238
|
+
lines.push(`# ouro:${namespace}:${job.id}`, `${job.schedule} ${job.command}`);
|
|
239
|
+
}
|
|
240
|
+
return lines.length > 0 ? `${lines.join("\n")}\n` : "";
|
|
241
|
+
}
|
|
242
|
+
render() {
|
|
243
|
+
this.deps.durableWrite(this.crontabPath, this.renderedContent(), 0o600);
|
|
244
|
+
}
|
|
245
|
+
spawnChild() {
|
|
246
|
+
const child = this.deps.spawn(this.binaryPath, ["-split-logs", "-inotify", this.crontabPath]);
|
|
247
|
+
if (!child.pid || child.pid <= 0)
|
|
248
|
+
throw new Error("Supercronic did not return a child PID");
|
|
249
|
+
this.child = child;
|
|
250
|
+
this.deps.durableWrite(this.pidPath, `${child.pid}\n`, 0o600);
|
|
251
|
+
child.once("exit", (code, signal) => {
|
|
252
|
+
if (this.child !== child)
|
|
253
|
+
return;
|
|
254
|
+
this.child = null;
|
|
255
|
+
this.deps.removeFile(this.pidPath);
|
|
256
|
+
if (this.stopping)
|
|
257
|
+
return;
|
|
258
|
+
if (this.restartCount >= RESTART_DELAYS_MS.length) {
|
|
259
|
+
this.onFatal(new Error(`Supercronic exited repeatedly (code=${String(code)}, signal=${String(signal)})`));
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
const delay = RESTART_DELAYS_MS[this.restartCount++];
|
|
263
|
+
this.restartTimer = this.deps.setTimer(() => { this.restartTimer = null; this.spawnChild(); }, delay);
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
exports.SupercronicSupervisor = SupercronicSupervisor;
|
|
@@ -49,6 +49,7 @@ class HabitScheduler {
|
|
|
49
49
|
onHabitFire;
|
|
50
50
|
deps;
|
|
51
51
|
execForVerify;
|
|
52
|
+
verifyJobs;
|
|
52
53
|
platform;
|
|
53
54
|
watcher = null;
|
|
54
55
|
debounceTimer = null;
|
|
@@ -65,6 +66,7 @@ class HabitScheduler {
|
|
|
65
66
|
this.onHabitFire = options.onHabitFire;
|
|
66
67
|
this.deps = options.deps;
|
|
67
68
|
this.execForVerify = options.execForVerify;
|
|
69
|
+
this.verifyJobs = options.verifyJobs;
|
|
68
70
|
this.platform = options.platform ?? process.platform;
|
|
69
71
|
}
|
|
70
72
|
start() {
|
|
@@ -268,11 +270,13 @@ class HabitScheduler {
|
|
|
268
270
|
}, intervalMs);
|
|
269
271
|
}
|
|
270
272
|
verifyCronAndCreateFallbacks(state) {
|
|
271
|
-
if (!this.execForVerify || state.jobs.length === 0)
|
|
273
|
+
if ((!this.execForVerify && !this.verifyJobs) || state.jobs.length === 0)
|
|
272
274
|
return state;
|
|
273
275
|
let currentState = state;
|
|
274
276
|
for (let attempt = 1; attempt <= MAX_CRON_VERIFICATION_ATTEMPTS; attempt += 1) {
|
|
275
|
-
const verifiedLabels = this.
|
|
277
|
+
const verifiedLabels = this.verifyJobs?.(currentState.jobs)
|
|
278
|
+
? new Set(currentState.jobs.map((job) => this.platform === "darwin" ? `bot.ouro.${job.agent}.${job.taskId}` : job.taskId))
|
|
279
|
+
: this.execForVerify ? this.verifyCronEntries() : new Set();
|
|
276
280
|
const revalidatedState = this.revalidateAndCorrectJobs(currentState);
|
|
277
281
|
if (revalidatedState === null)
|
|
278
282
|
return null;
|
|
@@ -586,7 +590,7 @@ class HabitScheduler {
|
|
|
586
590
|
taskId: habit.name,
|
|
587
591
|
schedule: cronSchedule,
|
|
588
592
|
lastRun: habit.lastRun,
|
|
589
|
-
command: `${this.deps.ouroPath} poke ${this.agent} --habit ${habit.name} --trigger launchd`,
|
|
593
|
+
command: `${this.deps.ouroPath} poke ${this.agent} --habit ${habit.name} --trigger ${this.platform === "darwin" ? "launchd" : "cron"}`,
|
|
590
594
|
taskPath: path.join(this.habitsDir, `${habit.name}.md`),
|
|
591
595
|
});
|
|
592
596
|
}
|
package/dist/heart/identity.js
CHANGED
|
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.HARNESS_CANONICAL_REPO_URL = exports.DEFAULT_AGENT_SENSES = exports.LEGACY_VAULT_SERVER_URL_ALIASES = exports.DEFAULT_VAULT_SERVER_URL = exports.DEFAULT_AGENT_PHRASES = exports.DEFAULT_AGENT_CONTEXT = exports.PROVIDER_CREDENTIALS = void 0;
|
|
36
|
+
exports.HARNESS_CANONICAL_REPO_URL = exports.DEFAULT_AGENT_SENSES = exports.LEGACY_VAULT_SERVER_URL_ALIASES = exports.DEFAULT_VAULT_SERVER_URL = exports.DEFAULT_AGENT_PHRASES = exports.DEFAULT_AGENT_CONTEXT = exports.PROVIDER_CREDENTIALS = exports.normalizeHabitPaidTurnsPerDay = void 0;
|
|
37
37
|
exports.normalizeVaultServerUrl = normalizeVaultServerUrl;
|
|
38
38
|
exports.getVaultServerUrlCandidates = getVaultServerUrlCandidates;
|
|
39
39
|
exports.defaultStableVaultEmail = defaultStableVaultEmail;
|
|
@@ -62,6 +62,9 @@ const os = __importStar(require("os"));
|
|
|
62
62
|
const path = __importStar(require("path"));
|
|
63
63
|
const runtime_1 = require("../nerves/runtime");
|
|
64
64
|
const migrate_config_1 = require("./migrate-config");
|
|
65
|
+
const autonomy_budget_1 = require("./autonomy-budget");
|
|
66
|
+
var autonomy_budget_2 = require("./autonomy-budget");
|
|
67
|
+
Object.defineProperty(exports, "normalizeHabitPaidTurnsPerDay", { enumerable: true, get: function () { return autonomy_budget_2.normalizeHabitPaidTurnsPerDay; } });
|
|
65
68
|
/** Single source of truth for per-provider credential field names, env var mappings, and prompt labels. */
|
|
66
69
|
exports.PROVIDER_CREDENTIALS = {
|
|
67
70
|
anthropic: { required: ["setupToken"], envVars: { ANTHROPIC_API_KEY: "setupToken" }, promptLabels: { setupToken: "Anthropic setup-token" } },
|
|
@@ -69,6 +72,8 @@ exports.PROVIDER_CREDENTIALS = {
|
|
|
69
72
|
azure: { required: ["apiKey", "endpoint", "deployment"], envVars: { AZURE_OPENAI_API_KEY: "apiKey", AZURE_OPENAI_KEY: "apiKey", AZURE_OPENAI_ENDPOINT: "endpoint", AZURE_OPENAI_DEPLOYMENT: "deployment" }, promptLabels: { apiKey: "Azure API key", endpoint: "Azure endpoint", deployment: "Azure deployment" } },
|
|
70
73
|
minimax: { required: ["apiKey"], envVars: { MINIMAX_API_KEY: "apiKey" }, promptLabels: { apiKey: "MiniMax API key" } },
|
|
71
74
|
"github-copilot": { required: ["githubToken", "baseUrl"], envVars: { GH_TOKEN: "githubToken", GITHUB_TOKEN: "githubToken" }, promptLabels: { githubToken: "GitHub token" } },
|
|
75
|
+
"openai-compatible": { required: ["apiKey", "baseUrl"], envVars: {}, promptLabels: { apiKey: "Z.ai API key", baseUrl: "Z.ai canonical base URL" } },
|
|
76
|
+
"openai-compatible-gemini": { required: ["apiKey", "baseUrl"], envVars: {}, promptLabels: { apiKey: "Gemini API key", baseUrl: "Gemini canonical base URL" } },
|
|
72
77
|
};
|
|
73
78
|
exports.DEFAULT_AGENT_CONTEXT = {
|
|
74
79
|
maxTokens: 80000,
|
|
@@ -138,6 +143,7 @@ exports.DEFAULT_AGENT_SENSES = {
|
|
|
138
143
|
mail: { enabled: false },
|
|
139
144
|
voice: { enabled: false },
|
|
140
145
|
a2a: { enabled: false },
|
|
146
|
+
telegram: { enabled: false },
|
|
141
147
|
workbench: { enabled: false },
|
|
142
148
|
};
|
|
143
149
|
function normalizeSenses(value, configFile) {
|
|
@@ -148,6 +154,7 @@ function normalizeSenses(value, configFile) {
|
|
|
148
154
|
mail: { ...exports.DEFAULT_AGENT_SENSES.mail },
|
|
149
155
|
voice: { ...exports.DEFAULT_AGENT_SENSES.voice },
|
|
150
156
|
a2a: { ...exports.DEFAULT_AGENT_SENSES.a2a },
|
|
157
|
+
telegram: { ...exports.DEFAULT_AGENT_SENSES.telegram },
|
|
151
158
|
workbench: { ...exports.DEFAULT_AGENT_SENSES.workbench },
|
|
152
159
|
};
|
|
153
160
|
if (value === undefined) {
|
|
@@ -164,7 +171,7 @@ function normalizeSenses(value, configFile) {
|
|
|
164
171
|
throw new Error(`agent.json at ${configFile} must include senses as an object when present.`);
|
|
165
172
|
}
|
|
166
173
|
const raw = value;
|
|
167
|
-
const senseNames = ["cli", "teams", "bluebubbles", "mail", "voice", "a2a", "workbench"];
|
|
174
|
+
const senseNames = ["cli", "teams", "bluebubbles", "mail", "voice", "a2a", "telegram", "workbench"];
|
|
168
175
|
for (const senseName of senseNames) {
|
|
169
176
|
const rawSense = raw[senseName];
|
|
170
177
|
if (rawSense === undefined) {
|
|
@@ -202,6 +209,7 @@ function buildDefaultAgentTemplate(_agentName) {
|
|
|
202
209
|
humanFacing: { provider: "anthropic", model: "claude-opus-4-6" },
|
|
203
210
|
agentFacing: { provider: "anthropic", model: "claude-opus-4-6" },
|
|
204
211
|
context: { ...exports.DEFAULT_AGENT_CONTEXT },
|
|
212
|
+
habitPaidTurnsPerDay: autonomy_budget_1.DEFAULT_HABIT_PAID_TURNS_PER_DAY,
|
|
205
213
|
senses: {
|
|
206
214
|
cli: { ...exports.DEFAULT_AGENT_SENSES.cli },
|
|
207
215
|
teams: { ...exports.DEFAULT_AGENT_SENSES.teams },
|
|
@@ -209,6 +217,7 @@ function buildDefaultAgentTemplate(_agentName) {
|
|
|
209
217
|
mail: { ...exports.DEFAULT_AGENT_SENSES.mail },
|
|
210
218
|
voice: { ...exports.DEFAULT_AGENT_SENSES.voice },
|
|
211
219
|
a2a: { ...exports.DEFAULT_AGENT_SENSES.a2a },
|
|
220
|
+
telegram: { ...exports.DEFAULT_AGENT_SENSES.telegram },
|
|
212
221
|
workbench: { ...exports.DEFAULT_AGENT_SENSES.workbench },
|
|
213
222
|
},
|
|
214
223
|
phrases: {
|
|
@@ -301,7 +310,7 @@ function getAgentMailroomRoot(agentName) {
|
|
|
301
310
|
function getAgentToolsRoot(agentName) {
|
|
302
311
|
return path.join(getAgentStateRoot(resolveOptionalAgentName(agentName)), "tools");
|
|
303
312
|
}
|
|
304
|
-
const VALID_PROVIDERS = ["azure", "minimax", "anthropic", "openai-codex", "github-copilot"];
|
|
313
|
+
const VALID_PROVIDERS = ["azure", "minimax", "anthropic", "openai-codex", "github-copilot", "openai-compatible", "openai-compatible-gemini"];
|
|
305
314
|
function isValidProvider(value) {
|
|
306
315
|
return typeof value === "string" && VALID_PROVIDERS.includes(value);
|
|
307
316
|
}
|
|
@@ -464,6 +473,7 @@ function loadAgentConfig() {
|
|
|
464
473
|
enabled,
|
|
465
474
|
humanFacing,
|
|
466
475
|
agentFacing,
|
|
476
|
+
habitPaidTurnsPerDay: (0, autonomy_budget_1.normalizeHabitPaidTurnsPerDay)(getAgentName(), parsed.habitPaidTurnsPerDay, configFile),
|
|
467
477
|
senses: normalizeSenses(parsed.senses, configFile),
|
|
468
478
|
phrases: parsed.phrases,
|
|
469
479
|
};
|
|
@@ -53,7 +53,7 @@ const crypto = __importStar(require("node:crypto"));
|
|
|
53
53
|
const os = __importStar(require("node:os"));
|
|
54
54
|
const runtime_1 = require("../nerves/runtime");
|
|
55
55
|
const credential_access_1 = require("../repertoire/credential-access");
|
|
56
|
-
const VALID_PROVIDERS = ["azure", "minimax", "anthropic", "openai-codex", "github-copilot"];
|
|
56
|
+
const VALID_PROVIDERS = ["azure", "minimax", "anthropic", "openai-codex", "github-copilot", "openai-compatible", "openai-compatible-gemini"];
|
|
57
57
|
const VALID_PROVENANCE_SOURCES = ["auth-flow", "manual"];
|
|
58
58
|
const VAULT_ITEM_PREFIX = "providers/";
|
|
59
59
|
exports.PROVIDER_CREDENTIAL_POOL_NOT_LOADED_ERROR = "provider credentials have not been loaded from vault";
|
|
@@ -78,6 +78,14 @@ const PROVIDER_FIELD_SPLITS = {
|
|
|
78
78
|
credentials: ["githubToken"],
|
|
79
79
|
config: ["baseUrl"],
|
|
80
80
|
},
|
|
81
|
+
"openai-compatible": {
|
|
82
|
+
credentials: ["apiKey"],
|
|
83
|
+
config: ["baseUrl"],
|
|
84
|
+
},
|
|
85
|
+
"openai-compatible-gemini": {
|
|
86
|
+
credentials: ["apiKey"],
|
|
87
|
+
config: ["baseUrl"],
|
|
88
|
+
},
|
|
81
89
|
};
|
|
82
90
|
let cachedPools = new Map();
|
|
83
91
|
function isRecord(value) {
|
|
@@ -14,6 +14,8 @@ exports.DEFAULT_PROVIDER_MODELS = {
|
|
|
14
14
|
minimax: "MiniMax-M2.7",
|
|
15
15
|
"openai-codex": "gpt-5.4",
|
|
16
16
|
"github-copilot": "claude-sonnet-4.6",
|
|
17
|
+
"openai-compatible": "glm-5.2",
|
|
18
|
+
"openai-compatible-gemini": "gemini-3.6-flash",
|
|
17
19
|
};
|
|
18
20
|
const PROVIDER_NAMES = {
|
|
19
21
|
anthropic: "Anthropic",
|
|
@@ -21,6 +23,8 @@ const PROVIDER_NAMES = {
|
|
|
21
23
|
minimax: "MiniMax",
|
|
22
24
|
"openai-codex": "OpenAI Codex",
|
|
23
25
|
"github-copilot": "GitHub Copilot",
|
|
26
|
+
"openai-compatible": "Z.ai OpenAI-compatible",
|
|
27
|
+
"openai-compatible-gemini": "Gemini OpenAI-compatible",
|
|
24
28
|
};
|
|
25
29
|
function normalized(model) {
|
|
26
30
|
return model.trim().toLowerCase();
|
|
@@ -46,6 +50,10 @@ function isModelClearlyIncompatibleWithProvider(provider, model) {
|
|
|
46
50
|
return value.startsWith("claude-") || value.startsWith("minimax");
|
|
47
51
|
case "github-copilot":
|
|
48
52
|
return false;
|
|
53
|
+
case "openai-compatible":
|
|
54
|
+
return value !== "glm-5.2" && value !== "glm-5-turbo";
|
|
55
|
+
case "openai-compatible-gemini":
|
|
56
|
+
return value !== "gemini-3.6-flash";
|
|
49
57
|
}
|
|
50
58
|
}
|
|
51
59
|
function resolveModelForProviderSelection(provider, currentModel) {
|
|
@@ -11,6 +11,7 @@ const azure_1 = require("./providers/azure");
|
|
|
11
11
|
const minimax_1 = require("./providers/minimax");
|
|
12
12
|
const openai_codex_1 = require("./providers/openai-codex");
|
|
13
13
|
const github_copilot_1 = require("./providers/github-copilot");
|
|
14
|
+
const openai_compatible_1 = require("./providers/openai-compatible");
|
|
14
15
|
const provider_models_1 = require("./provider-models");
|
|
15
16
|
const runtime_1 = require("../nerves/runtime");
|
|
16
17
|
const provider_attempt_1 = require("./provider-attempt");
|
|
@@ -177,7 +178,9 @@ function createProviderRuntimeForConfig(provider, config, options = {}) {
|
|
|
177
178
|
return (0, openai_codex_1.createOpenAICodexProviderRuntime)(resolvedModel, config);
|
|
178
179
|
case "github-copilot":
|
|
179
180
|
return (0, github_copilot_1.createGithubCopilotProviderRuntime)(resolvedModel, config);
|
|
180
|
-
|
|
181
|
+
case "openai-compatible":
|
|
182
|
+
case "openai-compatible-gemini":
|
|
183
|
+
return (0, openai_compatible_1.createOpenAICompatibleProviderRuntime)(provider, resolvedModel, config);
|
|
181
184
|
default:
|
|
182
185
|
throw new Error(`unsupported provider for ping: ${provider}`);
|
|
183
186
|
}
|
|
@@ -233,7 +236,7 @@ async function pingProvider(provider, config, options = {}) {
|
|
|
233
236
|
attempts: attempt.attempts,
|
|
234
237
|
};
|
|
235
238
|
}
|
|
236
|
-
const PINGABLE_PROVIDERS = ["anthropic", "openai-codex", "azure", "minimax", "github-copilot"];
|
|
239
|
+
const PINGABLE_PROVIDERS = ["anthropic", "openai-codex", "azure", "minimax", "github-copilot", "openai-compatible", "openai-compatible-gemini"];
|
|
237
240
|
async function runHealthInventory(agentName, currentProvider, deps = {}) {
|
|
238
241
|
/* v8 ignore next -- default: tests inject ping dep @preserve */
|
|
239
242
|
const ping = deps.ping ?? pingProvider;
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createOpenAICompatibleProviderRuntime = createOpenAICompatibleProviderRuntime;
|
|
4
|
+
const bundle_manifest_1 = require("../../mind/bundle-manifest");
|
|
5
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
6
|
+
const error_classification_1 = require("./error-classification");
|
|
7
|
+
const CANONICAL_BASE_URLS = {
|
|
8
|
+
"openai-compatible": "https://api.z.ai/api/paas/v4/",
|
|
9
|
+
"openai-compatible-gemini": "https://generativelanguage.googleapis.com/v1beta/openai/",
|
|
10
|
+
};
|
|
11
|
+
function canonicalBaseUrl(provider, value) {
|
|
12
|
+
let parsed;
|
|
13
|
+
try {
|
|
14
|
+
parsed = new URL(value);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
throw new Error(`${provider} requires its canonical base URL`);
|
|
18
|
+
}
|
|
19
|
+
const normalized = parsed.toString().replace(/\/+$/u, "/");
|
|
20
|
+
const normalizedInput = value.replace(/\/+$/u, "/");
|
|
21
|
+
if (normalizedInput !== CANONICAL_BASE_URLS[provider] || normalized !== CANONICAL_BASE_URLS[provider]) {
|
|
22
|
+
throw new Error(`${provider} requires its canonical base URL ${CANONICAL_BASE_URLS[provider]}`);
|
|
23
|
+
}
|
|
24
|
+
return normalized;
|
|
25
|
+
}
|
|
26
|
+
function safeError(error, apiKey) {
|
|
27
|
+
const redact = (value) => value.split(apiKey).join("[redacted]");
|
|
28
|
+
const message = redact(error instanceof Error ? error.message : String(error));
|
|
29
|
+
const safe = new Error(message);
|
|
30
|
+
if (error instanceof Error) {
|
|
31
|
+
const source = error;
|
|
32
|
+
if (typeof source.status === "number" && Number.isFinite(source.status))
|
|
33
|
+
safe.status = source.status;
|
|
34
|
+
if (typeof source.code === "string")
|
|
35
|
+
safe.code = redact(source.code);
|
|
36
|
+
}
|
|
37
|
+
return safe;
|
|
38
|
+
}
|
|
39
|
+
function requiredString(value, label) {
|
|
40
|
+
if (typeof value !== "string" || value.trim().length === 0)
|
|
41
|
+
throw new Error(`OpenAI-compatible response ${label} is missing`);
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
function parseUsage(value) {
|
|
45
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
46
|
+
return undefined;
|
|
47
|
+
const usage = value;
|
|
48
|
+
const input = usage.prompt_tokens;
|
|
49
|
+
const output = usage.completion_tokens;
|
|
50
|
+
const total = usage.total_tokens;
|
|
51
|
+
if (![input, output, total].every((entry) => Number.isSafeInteger(entry) && entry >= 0))
|
|
52
|
+
return undefined;
|
|
53
|
+
const details = usage.completion_tokens_details;
|
|
54
|
+
const reasoning = details && typeof details === "object" && !Array.isArray(details)
|
|
55
|
+
&& Number.isSafeInteger(details.reasoning_tokens)
|
|
56
|
+
? Number(details.reasoning_tokens)
|
|
57
|
+
: 0;
|
|
58
|
+
return { input_tokens: Number(input), output_tokens: Number(output), reasoning_tokens: reasoning, total_tokens: Number(total) };
|
|
59
|
+
}
|
|
60
|
+
function parseCompletion(value, callbacks) {
|
|
61
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
62
|
+
throw new Error("OpenAI-compatible response envelope is invalid");
|
|
63
|
+
const choices = value.choices;
|
|
64
|
+
if (!Array.isArray(choices) || choices.length !== 1 || !choices[0] || typeof choices[0] !== "object") {
|
|
65
|
+
throw new Error("OpenAI-compatible response must contain exactly one choice");
|
|
66
|
+
}
|
|
67
|
+
const choice = choices[0];
|
|
68
|
+
const message = choice.message;
|
|
69
|
+
if (!message || typeof message !== "object" || Array.isArray(message))
|
|
70
|
+
throw new Error("OpenAI-compatible choice message is invalid");
|
|
71
|
+
const record = message;
|
|
72
|
+
if (Object.prototype.hasOwnProperty.call(record, "function_call")) {
|
|
73
|
+
throw new Error("OpenAI-compatible legacy function_call is unsupported");
|
|
74
|
+
}
|
|
75
|
+
const rawCalls = record.tool_calls;
|
|
76
|
+
const calls = rawCalls === undefined || rawCalls === null ? [] : rawCalls;
|
|
77
|
+
if (!Array.isArray(calls))
|
|
78
|
+
throw new Error("OpenAI-compatible tool_calls must be an array");
|
|
79
|
+
const toolCalls = calls.map((call) => {
|
|
80
|
+
if (!call || typeof call !== "object" || Array.isArray(call))
|
|
81
|
+
throw new Error("OpenAI-compatible tool call is invalid");
|
|
82
|
+
const callRecord = call;
|
|
83
|
+
const fn = callRecord.function;
|
|
84
|
+
if (callRecord.type !== "function" || !fn || typeof fn !== "object" || Array.isArray(fn)) {
|
|
85
|
+
throw new Error("OpenAI-compatible tool call must be a function");
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
id: requiredString(callRecord.id, "tool call id"),
|
|
89
|
+
name: requiredString(fn.name, "tool name"),
|
|
90
|
+
arguments: requiredString(fn.arguments, "tool arguments"),
|
|
91
|
+
};
|
|
92
|
+
});
|
|
93
|
+
const content = typeof record.content === "string" ? record.content : "";
|
|
94
|
+
if (choice.finish_reason === "stop") {
|
|
95
|
+
if (toolCalls.length !== 0 || content.trim().length === 0)
|
|
96
|
+
throw new Error("OpenAI-compatible stop response shape is invalid");
|
|
97
|
+
}
|
|
98
|
+
else if (choice.finish_reason === "tool_calls") {
|
|
99
|
+
if (toolCalls.length < 1 || toolCalls.length > 8 || content.trim().length !== 0) {
|
|
100
|
+
throw new Error("OpenAI-compatible tool_calls response shape is invalid");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
throw new Error("OpenAI-compatible finish_reason is unsupported");
|
|
105
|
+
}
|
|
106
|
+
callbacks.onModelStreamStart();
|
|
107
|
+
if (content)
|
|
108
|
+
callbacks.onTextChunk(content);
|
|
109
|
+
return {
|
|
110
|
+
content,
|
|
111
|
+
toolCalls,
|
|
112
|
+
outputItems: [],
|
|
113
|
+
usage: parseUsage(value.usage),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function createOpenAICompatibleProviderRuntime(provider, model, config, deps = {}) {
|
|
117
|
+
const apiKey = config.apiKey?.trim();
|
|
118
|
+
if (!apiKey)
|
|
119
|
+
throw new Error(`provider '${provider}' is selected but apiKey is missing in the agent vault`);
|
|
120
|
+
const baseUrl = canonicalBaseUrl(provider, config.baseUrl);
|
|
121
|
+
const fetchImpl = deps.fetch ?? fetch;
|
|
122
|
+
const packageVersion = deps.packageVersion ?? (0, bundle_manifest_1.getPackageVersion)();
|
|
123
|
+
(0, runtime_1.emitNervesEvent)({
|
|
124
|
+
component: "engine",
|
|
125
|
+
event: "engine.provider_init",
|
|
126
|
+
message: "OpenAI-compatible provider init",
|
|
127
|
+
meta: { provider },
|
|
128
|
+
});
|
|
129
|
+
const request = async (messages, tools, signal, toolChoiceRequired = false) => {
|
|
130
|
+
const body = { model, messages, tools, stream: false };
|
|
131
|
+
if (provider === "openai-compatible")
|
|
132
|
+
body.temperature = 0;
|
|
133
|
+
if (toolChoiceRequired)
|
|
134
|
+
body.tool_choice = "required";
|
|
135
|
+
try {
|
|
136
|
+
const response = await fetchImpl(new URL("chat/completions", baseUrl), {
|
|
137
|
+
method: "POST",
|
|
138
|
+
headers: {
|
|
139
|
+
Authorization: `Bearer ${apiKey}`,
|
|
140
|
+
"Content-Type": "application/json",
|
|
141
|
+
...(provider === "openai-compatible-gemini"
|
|
142
|
+
? { "x-goog-api-client": `ouroboros-harness-oai/${packageVersion}` }
|
|
143
|
+
: {}),
|
|
144
|
+
},
|
|
145
|
+
body: JSON.stringify(body),
|
|
146
|
+
signal,
|
|
147
|
+
});
|
|
148
|
+
if (!response.ok)
|
|
149
|
+
throw Object.assign(new Error(`provider request failed with HTTP ${response.status}`), { status: response.status });
|
|
150
|
+
return await response.json();
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
throw safeError(error, apiKey);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
return {
|
|
157
|
+
id: provider,
|
|
158
|
+
model,
|
|
159
|
+
client: { baseUrl },
|
|
160
|
+
capabilities: new Set(),
|
|
161
|
+
resetTurnState() { },
|
|
162
|
+
appendToolOutput() { },
|
|
163
|
+
async streamTurn(turnRequest) {
|
|
164
|
+
return parseCompletion(await request(turnRequest.messages, turnRequest.activeTools, turnRequest.signal, turnRequest.toolChoiceRequired), turnRequest.callbacks);
|
|
165
|
+
},
|
|
166
|
+
async ping(signal) {
|
|
167
|
+
const callbacks = {
|
|
168
|
+
onModelStreamStart() { },
|
|
169
|
+
onTextChunk() { },
|
|
170
|
+
};
|
|
171
|
+
parseCompletion(await request([{ role: "user", content: "ping" }], [], signal), callbacks);
|
|
172
|
+
},
|
|
173
|
+
classifyError(error) {
|
|
174
|
+
return (0, error_classification_1.classifyHttpError)(error);
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
}
|