@tamsensedev/runner 0.19.0 → 0.20.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/dist/cli.js +31 -13
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3918,7 +3918,7 @@ function describeDevice(identity) {
|
|
|
3918
3918
|
}
|
|
3919
3919
|
|
|
3920
3920
|
// src/sense.ts
|
|
3921
|
-
import { mkdtemp, rm as rm5 } from "fs/promises";
|
|
3921
|
+
import { mkdtemp, rm as rm5, stat as stat2 } from "fs/promises";
|
|
3922
3922
|
import { tmpdir } from "os";
|
|
3923
3923
|
import { dirname as dirname4, join as join9 } from "path";
|
|
3924
3924
|
import process5 from "process";
|
|
@@ -3971,7 +3971,8 @@ async function sleep(ms, signal) {
|
|
|
3971
3971
|
|
|
3972
3972
|
// src/sense.ts
|
|
3973
3973
|
var SERVER_NAME3 = "tamsense";
|
|
3974
|
-
var
|
|
3974
|
+
var WEB_TOOL = "WebFetch";
|
|
3975
|
+
var MAX_SENSE_TURNS = 6;
|
|
3975
3976
|
var HEARTBEAT_MS = 5e3;
|
|
3976
3977
|
var STOPPED_HERE = "stopped_here";
|
|
3977
3978
|
var CLOSED_ON_SITE = "closed_on_site";
|
|
@@ -3989,12 +3990,13 @@ function claudeSenseRunner(startQuery) {
|
|
|
3989
3990
|
options: {
|
|
3990
3991
|
cwd: request.cwd,
|
|
3991
3992
|
systemPrompt: request.system,
|
|
3992
|
-
tools: [],
|
|
3993
|
+
tools: [WEB_TOOL],
|
|
3993
3994
|
settingSources: [],
|
|
3994
3995
|
persistSession: false,
|
|
3996
|
+
...request.resume === null ? {} : { resume: request.resume, forkSession: true },
|
|
3995
3997
|
strictMcpConfig: true,
|
|
3996
3998
|
mcpServers: tools.length > 0 ? { [SERVER_NAME3]: createSdkMcpServer3({ name: SERVER_NAME3, version: "1.0.0", tools }) } : {},
|
|
3997
|
-
allowedTools: tools.map((item) => `mcp__${SERVER_NAME3}__${item.name}`),
|
|
3999
|
+
allowedTools: [WEB_TOOL, ...tools.map((item) => `mcp__${SERVER_NAME3}__${item.name}`)],
|
|
3998
4000
|
permissionMode: "auto",
|
|
3999
4001
|
canUseTool: denyWhenAsked,
|
|
4000
4002
|
maxTurns: MAX_SENSE_TURNS,
|
|
@@ -4027,8 +4029,17 @@ function senseHeartbeat(api, job, abort) {
|
|
|
4027
4029
|
}
|
|
4028
4030
|
};
|
|
4029
4031
|
}
|
|
4032
|
+
async function authorSession(job, dataFolder) {
|
|
4033
|
+
if (!job.author) {
|
|
4034
|
+
return null;
|
|
4035
|
+
}
|
|
4036
|
+
const folder = taskFolder(dataFolder, { taskId: job.taskId, stage: { columnId: job.author.columnId } });
|
|
4037
|
+
const found = await stat2(folder).catch(() => null);
|
|
4038
|
+
return found?.isDirectory() ? { folder, sessionId: job.author.sessionId } : null;
|
|
4039
|
+
}
|
|
4030
4040
|
async function runSenseJob(job, runSense, tools, stop) {
|
|
4031
|
-
const
|
|
4041
|
+
const scratch = await mkdtemp(join9(tmpdir(), "tamsense-sense-"));
|
|
4042
|
+
const author = await authorSession(job, tools.dataFolder);
|
|
4032
4043
|
const run2 = new AbortController();
|
|
4033
4044
|
const stopRun = () => run2.abort(STOPPED_HERE);
|
|
4034
4045
|
if (stop.aborted) {
|
|
@@ -4038,17 +4049,24 @@ async function runSenseJob(job, runSense, tools, stop) {
|
|
|
4038
4049
|
const deadline = setTimeout(() => run2.abort(TOO_LONG), job.deadlineMs);
|
|
4039
4050
|
const beat = senseHeartbeat(tools.api, job, () => run2.abort(CLOSED_ON_SITE));
|
|
4040
4051
|
const beating = setInterval(() => void beat(), HEARTBEAT_MS);
|
|
4041
|
-
let body;
|
|
4052
|
+
let body = FAILED;
|
|
4042
4053
|
try {
|
|
4043
4054
|
const env = withNodeOnPath(process5.env, dirname4(process5.execPath));
|
|
4044
|
-
|
|
4055
|
+
const sources = sourceTools(job.tools, tools.api, sensePath(job.senseId, "sources"));
|
|
4056
|
+
const ask = (cwd, resume) => runSense({ system: job.system, prompt: job.prompt, schema: job.schema, model: job.model, cwd, resume, env, signal: run2.signal, sources }).catch(() => FAILED);
|
|
4057
|
+
if (author) {
|
|
4058
|
+
body = await ask(author.folder, author.sessionId);
|
|
4059
|
+
}
|
|
4060
|
+
if (body.outcome === "failed" && !run2.signal.aborted) {
|
|
4061
|
+
body = await ask(scratch, null);
|
|
4062
|
+
}
|
|
4045
4063
|
} catch {
|
|
4046
4064
|
body = FAILED;
|
|
4047
4065
|
} finally {
|
|
4048
4066
|
clearTimeout(deadline);
|
|
4049
4067
|
clearInterval(beating);
|
|
4050
4068
|
stop.removeEventListener("abort", stopRun);
|
|
4051
|
-
await rm5(
|
|
4069
|
+
await rm5(scratch, { recursive: true, force: true });
|
|
4052
4070
|
}
|
|
4053
4071
|
if (run2.signal.reason === CLOSED_ON_SITE) {
|
|
4054
4072
|
return;
|
|
@@ -4218,7 +4236,7 @@ async function prepareLibrary(folder, skills, source = LIBRARY_PATH) {
|
|
|
4218
4236
|
// src/recordings.ts
|
|
4219
4237
|
import { randomUUID } from "crypto";
|
|
4220
4238
|
import { createReadStream } from "fs";
|
|
4221
|
-
import { readdir as readdir2, readFile as readFile4, stat as
|
|
4239
|
+
import { readdir as readdir2, readFile as readFile4, stat as stat3, writeFile as writeFile4 } from "fs/promises";
|
|
4222
4240
|
import { basename as basename4, dirname as dirname5, extname as extname2, isAbsolute as isAbsolute3, join as join11 } from "path";
|
|
4223
4241
|
import { createInterface as createInterface2 } from "readline";
|
|
4224
4242
|
import { Readable } from "stream";
|
|
@@ -4325,7 +4343,7 @@ async function remember(dir, file) {
|
|
|
4325
4343
|
await writeFile4(join11(dir, LEDGER_FILE), JSON.stringify([...done2]));
|
|
4326
4344
|
}
|
|
4327
4345
|
async function nonEmpty(file) {
|
|
4328
|
-
return file !== null && ((await
|
|
4346
|
+
return file !== null && ((await stat3(file).catch(() => null))?.size ?? 0) > 0;
|
|
4329
4347
|
}
|
|
4330
4348
|
async function pendingRecordings(dir) {
|
|
4331
4349
|
const done2 = await ledger(dir);
|
|
@@ -4360,7 +4378,7 @@ async function uploadReplay(upload, file, label) {
|
|
|
4360
4378
|
}
|
|
4361
4379
|
async function uploadVideo(upload, file, label, meta) {
|
|
4362
4380
|
const contentType = VIDEO_TYPES_BY_EXTENSION[extname2(file).toLowerCase()];
|
|
4363
|
-
const info = await
|
|
4381
|
+
const info = await stat3(file);
|
|
4364
4382
|
if (info.size > MAX_VIDEO_BYTES) {
|
|
4365
4383
|
throw new Error(`The video is larger than ${MAX_VIDEO_BYTES / 1024 / 1024} MB. Record a shorter one.`);
|
|
4366
4384
|
}
|
|
@@ -4449,7 +4467,7 @@ async function uploadPendingRecordings(api, runId, dir) {
|
|
|
4449
4467
|
}
|
|
4450
4468
|
|
|
4451
4469
|
// src/screenshot.ts
|
|
4452
|
-
import { readFile as readFile5, stat as
|
|
4470
|
+
import { readFile as readFile5, stat as stat4 } from "fs/promises";
|
|
4453
4471
|
import { basename as basename5, extname as extname3, isAbsolute as isAbsolute4 } from "path";
|
|
4454
4472
|
var TYPES_BY_EXTENSION2 = {
|
|
4455
4473
|
".png": "image/png",
|
|
@@ -4471,7 +4489,7 @@ async function readScreenshot(request) {
|
|
|
4471
4489
|
} catch (error) {
|
|
4472
4490
|
return error instanceof Error ? error.message : String(error);
|
|
4473
4491
|
}
|
|
4474
|
-
const info = await
|
|
4492
|
+
const info = await stat4(request.path).catch(() => null);
|
|
4475
4493
|
if (!info?.isFile()) {
|
|
4476
4494
|
return `There is no file at ${request.path}.`;
|
|
4477
4495
|
}
|