atris 3.57.4 → 3.58.6
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 +12 -1
- package/atris/skills/clawhub/member-runtime/SKILL.md +9 -9
- package/atris/skills/create-member/SKILL.md +16 -13
- package/atris/skills/design/SKILL.md +27 -4
- package/atris/skills/engines/SKILL.md +55 -12
- package/atris/skills/x-search/SKILL.md +8 -6
- package/atris/skills/youtube/SKILL.md +66 -21
- package/atris/team/brainstormer/MEMBER.md +2 -2
- package/atris/team/executor/MEMBER.md +3 -3
- package/atris/team/launcher/MEMBER.md +4 -4
- package/atris/team/navigator/MEMBER.md +3 -3
- package/atris/team/researcher/MEMBER.md +2 -2
- package/atris/team/validator/MEMBER.md +1 -1
- package/atris.md +14 -0
- package/ax +153 -23
- package/bin/atris.js +78 -30
- package/commands/aeo.js +4 -4
- package/commands/agents.js +1 -1
- package/commands/align.js +12 -12
- package/commands/analytics.js +3 -3
- package/commands/app.js +5 -5
- package/commands/ask.js +175 -0
- package/commands/auth.js +59 -31
- package/commands/autoland.js +29 -5
- package/commands/autopilot-front.js +6 -6
- package/commands/autopilot.js +90 -90
- package/commands/avail.js +5 -5
- package/commands/bench.js +63 -1
- package/commands/browse.js +2 -2
- package/commands/business.js +12 -12
- package/commands/card.js +2 -2
- package/commands/clean.js +13 -13
- package/commands/close.js +3 -3
- package/commands/compile.js +13 -13
- package/commands/computer.js +94 -27
- package/commands/console.js +5 -5
- package/commands/decide.js +1 -1
- package/commands/deck.js +9 -9
- package/commands/drive.js +4 -4
- package/commands/engine.js +173 -19
- package/commands/errors.js +3 -3
- package/commands/experiments.js +18 -1
- package/commands/feed.js +2 -2
- package/commands/feedback.js +4 -4
- package/commands/fleet.js +12 -12
- package/commands/game.js +1 -1
- package/commands/guide.js +49 -0
- package/commands/improve.js +19 -17
- package/commands/init.js +67 -19
- package/commands/interview.js +7 -7
- package/commands/land.js +40 -15
- package/commands/learn.js +216 -50
- package/commands/lesson.js +2 -2
- package/commands/log.js +1 -1
- package/commands/loops.js +4 -4
- package/commands/member.js +75 -50
- package/commands/mission.js +90 -82
- package/commands/one-lap.js +1 -1
- package/commands/pack.js +74 -8
- package/commands/plugin.js +6 -6
- package/commands/probe.js +9 -9
- package/commands/pull.js +35 -35
- package/commands/pulse.js +1 -1
- package/commands/push.js +21 -21
- package/commands/rainmaker.js +1 -1
- package/commands/recap.js +7 -7
- package/commands/reel.js +2 -2
- package/commands/release.js +3 -3
- package/commands/report.js +1 -1
- package/commands/review.js +5 -5
- package/commands/revisions.js +1 -1
- package/commands/run.js +1 -1
- package/commands/scout.js +3 -3
- package/commands/security-review.js +7 -7
- package/commands/serve.js +9 -9
- package/commands/setup.js +1 -1
- package/commands/sign.js +4 -4
- package/commands/signup.js +4 -4
- package/commands/site.js +8 -3
- package/commands/skill.js +6 -6
- package/commands/slop.js +18 -18
- package/commands/social.js +3 -3
- package/commands/soul.js +14 -14
- package/commands/spaceship.js +1 -1
- package/commands/status.js +2 -2
- package/commands/strings.js +13 -13
- package/commands/study.js +4 -4
- package/commands/sync.js +8 -8
- package/commands/task.js +50 -25
- package/commands/teach.js +2 -2
- package/commands/team.js +1 -1
- package/commands/theme.js +2 -2
- package/commands/tree.js +34 -0
- package/commands/truth.js +7 -7
- package/commands/verify.js +12 -12
- package/commands/visualize.js +2 -2
- package/commands/workflow.js +101 -48
- package/commands/worktree.js +1 -1
- package/commands/write.js +9 -9
- package/commands/x-search.js +93 -22
- package/commands/xp.js +6 -3
- package/commands/youtube.js +1019 -123
- package/lib/accept-verify-gate.js +3 -3
- package/lib/activity-stream.js +1 -1
- package/lib/auto-accept-certified.js +28 -12
- package/lib/autoland.js +6 -6
- package/lib/bench/engines.js +59 -8
- package/lib/bench/report.js +1 -1
- package/lib/bench/runner.js +204 -5
- package/lib/bench/tree-render.js +76 -0
- package/lib/card.js +1 -1
- package/lib/chat-log-scan.js +1 -1
- package/lib/claude-boot-block.js +2 -0
- package/lib/deck-compose.js +1 -1
- package/lib/deck-history.js +2 -2
- package/lib/deck-layout.js +1 -1
- package/lib/deck-review.js +5 -5
- package/lib/deck-schema.js +1 -1
- package/lib/default-verifier.js +1 -1
- package/lib/developer-api.js +3 -0
- package/lib/engine-ask.js +45 -7
- package/lib/engine-registry.js +15 -2
- package/lib/file-ops.js +1 -1
- package/lib/fleet.js +71 -35
- package/lib/html-render.js +1 -1
- package/lib/intents.js +172 -0
- package/lib/journal.js +1 -1
- package/lib/known-commands.js +2 -2
- package/lib/land-green.js +364 -0
- package/lib/lesson-contradiction.js +2 -2
- package/lib/lesson-preflight.js +1 -1
- package/lib/manifest.js +5 -5
- package/lib/member-alive.js +9 -5
- package/lib/memory-view.js +3 -3
- package/lib/mission-protected-lane.js +1 -1
- package/lib/mission-root.js +2 -2
- package/lib/orb-context.js +10 -10
- package/lib/pack-capabilities.js +861 -3
- package/lib/permission-grants.js +2 -2
- package/lib/policy-lessons.js +6 -6
- package/lib/pulse.js +8 -8
- package/lib/receipt-evidence.js +1 -1
- package/lib/reel.js +1 -1
- package/lib/review-integrity.js +1 -1
- package/lib/runner-command.js +35 -5
- package/lib/scorecard.js +4 -4
- package/lib/security-scan.js +5 -5
- package/lib/self-drive.js +1 -1
- package/lib/site-publish.js +372 -0
- package/lib/site.js +1 -1
- package/lib/slides-deck.js +2 -2
- package/lib/state-detection.js +2 -2
- package/lib/sync-telemetry.js +2 -2
- package/lib/task-db.js +17 -10
- package/lib/task-explanation.js +1 -1
- package/lib/task-proof.js +2 -2
- package/lib/task-receipt.js +3 -3
- package/lib/theme.js +12 -12
- package/lib/todo-fallback.js +3 -3
- package/lib/todo-sections.js +1 -1
- package/lib/tree-hash.js +79 -0
- package/lib/usage.js +1 -1
- package/lib/wish-delegate.js +1 -1
- package/lib/workspace-safety.js +4 -4
- package/lib/workspace-scaffold.js +3 -3
- package/package.json +1 -1
- package/scripts/det/concierge-bakeoff.sh +26 -0
- package/scripts/det/ytnotes +82 -5
- package/scripts/det/ytquote-repair.js +0 -1
- package/scripts/det/ytrail-race.js +0 -5
- package/scripts/member-operate.mjs +52 -14
- package/scripts/outbound-artifact-gate.js +0 -3
- package/utils/auth.js +47 -9
- package/utils/config.js +0 -1
- package/scripts/det/checklist-score.js +0 -191
package/lib/bench/runner.js
CHANGED
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
const fs = require('node:fs');
|
|
4
4
|
const path = require('node:path');
|
|
5
5
|
const { spawnSync } = require('node:child_process');
|
|
6
|
+
const { treeHashFor } = require('../tree-hash');
|
|
6
7
|
const { createBenchContext, withBenchContext } = require('./context');
|
|
7
8
|
const { ENGINE_NAMES, getEngineAdapter, normalizeEngineName } = require('./engines');
|
|
9
|
+
const { renderTreeInto } = require('./tree-render');
|
|
8
10
|
|
|
9
11
|
const DEFAULT_PACK = 'core-v1';
|
|
10
12
|
|
|
@@ -208,6 +210,23 @@ function copyFixtureIntoWorkspace(fixtureDir, workspace) {
|
|
|
208
210
|
});
|
|
209
211
|
}
|
|
210
212
|
|
|
213
|
+
function shellQuote(value) {
|
|
214
|
+
return `'${String(value).replace(/'/g, `'\\''`)}'`;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function installAtrisShim(ctx) {
|
|
218
|
+
const shimDir = path.join(ctx.env.HOME, 'bin');
|
|
219
|
+
const shimPath = path.join(shimDir, 'atris');
|
|
220
|
+
fs.mkdirSync(shimDir, { recursive: true });
|
|
221
|
+
fs.writeFileSync(
|
|
222
|
+
shimPath,
|
|
223
|
+
`#!/bin/sh\nexec ${shellQuote(process.execPath)} ${shellQuote(ctx.cliPath)} "$@"\n`,
|
|
224
|
+
{ encoding: 'utf8', mode: 0o755 },
|
|
225
|
+
);
|
|
226
|
+
ctx.env.PATH = [shimDir, ctx.env.PATH].filter(Boolean).join(path.delimiter);
|
|
227
|
+
return shimPath;
|
|
228
|
+
}
|
|
229
|
+
|
|
211
230
|
async function runSetup(spec, ctx) {
|
|
212
231
|
if (!spec.setupPath) return;
|
|
213
232
|
delete require.cache[require.resolve(spec.setupPath)];
|
|
@@ -246,6 +265,7 @@ async function runAgentTaskSpec(spec, options = {}) {
|
|
|
246
265
|
const started = Date.now();
|
|
247
266
|
const timeoutMs = Number(spec.timeoutMs || options.timeoutMs || 300000);
|
|
248
267
|
let bench = null;
|
|
268
|
+
let reachedCheck = false;
|
|
249
269
|
try {
|
|
250
270
|
bench = createBenchContext({
|
|
251
271
|
repoRoot: options.repoRoot,
|
|
@@ -257,9 +277,15 @@ async function runAgentTaskSpec(spec, options = {}) {
|
|
|
257
277
|
ctx.fixtureDir = spec.fixtureDir;
|
|
258
278
|
ctx.promptPath = spec.promptPath;
|
|
259
279
|
copyFixtureIntoWorkspace(spec.fixtureDir, ctx.workspace);
|
|
280
|
+
installAtrisShim(ctx);
|
|
281
|
+
if (options.treeRoot) {
|
|
282
|
+
const rendered = renderTreeInto(ctx.workspace, options.treeRoot);
|
|
283
|
+
if (typeof options.onTreeRendered === 'function') options.onTreeRendered(rendered);
|
|
284
|
+
}
|
|
260
285
|
|
|
261
|
-
const engine =
|
|
262
|
-
|
|
286
|
+
const engine = options.engineAdapter
|
|
287
|
+
|| getEngineAdapter(options.engine, { solutionPath: spec.solutionPath, model: options.model });
|
|
288
|
+
const availability = engine.available(ctx.workspace, ctx.env);
|
|
263
289
|
if (!availability.available) {
|
|
264
290
|
return {
|
|
265
291
|
id: spec.id,
|
|
@@ -268,15 +294,27 @@ async function runAgentTaskSpec(spec, options = {}) {
|
|
|
268
294
|
failures: [],
|
|
269
295
|
duration_ms: Date.now() - started,
|
|
270
296
|
retried: false,
|
|
297
|
+
...(options.classifyCouldNotRun ? { could_not_run: true } : {}),
|
|
271
298
|
};
|
|
272
299
|
}
|
|
273
300
|
|
|
274
301
|
await runSetup(spec, ctx);
|
|
275
302
|
const promptText = readTaskPrompt(spec);
|
|
276
|
-
const engineResult = await withTimeout(
|
|
303
|
+
const engineResult = await withTimeout(
|
|
304
|
+
Promise.resolve(engine.run(promptText, ctx.workspace, timeoutMs, ctx.env)),
|
|
305
|
+
spec,
|
|
306
|
+
timeoutMs,
|
|
307
|
+
);
|
|
308
|
+
ctx.engineResult = {
|
|
309
|
+
status: engineResult && engineResult.status,
|
|
310
|
+
stdout: String(engineResult && engineResult.stdout || ''),
|
|
311
|
+
stderr: String(engineResult && engineResult.stderr || ''),
|
|
312
|
+
timedOut: Boolean(engineResult && engineResult.timedOut),
|
|
313
|
+
};
|
|
277
314
|
if (!engineResult || engineResult.status !== 0) {
|
|
278
315
|
throw new Error(formatEngineFailure(options.engine, engineResult));
|
|
279
316
|
}
|
|
317
|
+
reachedCheck = true;
|
|
280
318
|
await withTimeout(Promise.resolve().then(() => spec.check(ctx)), spec, timeoutMs);
|
|
281
319
|
return {
|
|
282
320
|
id: spec.id,
|
|
@@ -285,6 +323,7 @@ async function runAgentTaskSpec(spec, options = {}) {
|
|
|
285
323
|
failures: [],
|
|
286
324
|
duration_ms: Date.now() - started,
|
|
287
325
|
retried: false,
|
|
326
|
+
...(options.classifyCouldNotRun ? { could_not_run: false } : {}),
|
|
288
327
|
};
|
|
289
328
|
} catch (err) {
|
|
290
329
|
return {
|
|
@@ -294,6 +333,7 @@ async function runAgentTaskSpec(spec, options = {}) {
|
|
|
294
333
|
failures: [formatFailure(err)],
|
|
295
334
|
duration_ms: Date.now() - started,
|
|
296
335
|
retried: false,
|
|
336
|
+
...(options.classifyCouldNotRun ? { could_not_run: !reachedCheck } : {}),
|
|
297
337
|
};
|
|
298
338
|
} finally {
|
|
299
339
|
if (bench) bench.teardown();
|
|
@@ -314,6 +354,7 @@ async function runTaskSpec(spec, options = {}) {
|
|
|
314
354
|
failures: [],
|
|
315
355
|
duration_ms: Date.now() - started,
|
|
316
356
|
retried: false,
|
|
357
|
+
...(options.classifyCouldNotRun ? { could_not_run: true } : {}),
|
|
317
358
|
};
|
|
318
359
|
}
|
|
319
360
|
|
|
@@ -328,6 +369,7 @@ async function runTaskSpec(spec, options = {}) {
|
|
|
328
369
|
failures: [],
|
|
329
370
|
duration_ms: Date.now() - started,
|
|
330
371
|
retried,
|
|
372
|
+
...(options.classifyCouldNotRun ? { could_not_run: true } : {}),
|
|
331
373
|
};
|
|
332
374
|
}
|
|
333
375
|
return {
|
|
@@ -337,6 +379,7 @@ async function runTaskSpec(spec, options = {}) {
|
|
|
337
379
|
failures: [],
|
|
338
380
|
duration_ms: Date.now() - started,
|
|
339
381
|
retried,
|
|
382
|
+
...(options.classifyCouldNotRun ? { could_not_run: false } : {}),
|
|
340
383
|
};
|
|
341
384
|
} catch (err) {
|
|
342
385
|
const infra = isInfraFailure(err);
|
|
@@ -351,6 +394,7 @@ async function runTaskSpec(spec, options = {}) {
|
|
|
351
394
|
failures: [formatFailure(err)],
|
|
352
395
|
duration_ms: Date.now() - started,
|
|
353
396
|
retried,
|
|
397
|
+
...(options.classifyCouldNotRun ? { could_not_run: infra } : {}),
|
|
354
398
|
};
|
|
355
399
|
}
|
|
356
400
|
}
|
|
@@ -362,6 +406,7 @@ async function runTaskSpec(spec, options = {}) {
|
|
|
362
406
|
failures: ['unreachable benchmark runner state'],
|
|
363
407
|
duration_ms: Date.now() - started,
|
|
364
408
|
retried,
|
|
409
|
+
...(options.classifyCouldNotRun ? { could_not_run: true } : {}),
|
|
365
410
|
};
|
|
366
411
|
}
|
|
367
412
|
|
|
@@ -449,6 +494,7 @@ function exitCodeForRecord(record) {
|
|
|
449
494
|
|
|
450
495
|
async function runBench(options = {}) {
|
|
451
496
|
const repoRoot = path.resolve(options.repoRoot || repoRootFromHere());
|
|
497
|
+
const workspaceRoot = path.resolve(options.stateRoot || process.cwd());
|
|
452
498
|
const pack = options.pack || (options.tasksDir ? path.basename(path.resolve(options.tasksDir)) : DEFAULT_PACK);
|
|
453
499
|
const specs = selectTaskSpecs(loadTaskSpecs({ repoRoot, tasksDir: options.tasksDir, pack }), options.taskIds);
|
|
454
500
|
const engine = normalizeBenchEngine(options.engine);
|
|
@@ -458,18 +504,27 @@ async function runBench(options = {}) {
|
|
|
458
504
|
const label = normalizeLabel(options.label);
|
|
459
505
|
const started = new Date().toISOString();
|
|
460
506
|
const pythonCmd = options.pythonCmd === undefined ? findPython() : options.pythonCmd;
|
|
507
|
+
let renderedTreeHash = null;
|
|
461
508
|
const taskRecords = await runTaskSpecs(specs, {
|
|
462
509
|
repoRoot,
|
|
463
510
|
timeoutMs: options.timeoutMs,
|
|
464
511
|
pythonCmd,
|
|
465
512
|
engine,
|
|
513
|
+
engineAdapter: options.engineAdapter,
|
|
514
|
+
model: options.model || null,
|
|
515
|
+
treeRoot: options.treeRoot,
|
|
516
|
+
onTreeRendered(rendered) {
|
|
517
|
+
renderedTreeHash = rendered.tree_hash;
|
|
518
|
+
},
|
|
466
519
|
});
|
|
467
520
|
const finished = new Date().toISOString();
|
|
468
521
|
const summary = summarizeTasks(taskRecords);
|
|
469
522
|
const record = {
|
|
470
523
|
schema: 'atris.bench.run.v1',
|
|
524
|
+
tree_hash: options.treeRoot ? (renderedTreeHash || treeHashFor(options.treeRoot)) : treeHashFor(workspaceRoot),
|
|
471
525
|
pack,
|
|
472
526
|
engine,
|
|
527
|
+
model: engine ? (options.model || null) : null,
|
|
473
528
|
label,
|
|
474
529
|
experiment: options.experiment || null,
|
|
475
530
|
started,
|
|
@@ -481,11 +536,154 @@ async function runBench(options = {}) {
|
|
|
481
536
|
summary: summary.summary,
|
|
482
537
|
};
|
|
483
538
|
|
|
484
|
-
if (options.persist !== false) appendResultRecord(record,
|
|
485
|
-
if (options.updateBaseline) writeBaselineRecord(record,
|
|
539
|
+
if (options.persist !== false) appendResultRecord(record, workspaceRoot);
|
|
540
|
+
if (options.updateBaseline) writeBaselineRecord(record, workspaceRoot);
|
|
486
541
|
return { record, exitCode: exitCodeForRecord(record) };
|
|
487
542
|
}
|
|
488
543
|
|
|
544
|
+
function selectTaskSpecsInPackOrder(specs, taskIds = []) {
|
|
545
|
+
const ids = (taskIds || []).filter(Boolean);
|
|
546
|
+
if (!ids.length) return specs;
|
|
547
|
+
const requested = new Set(ids);
|
|
548
|
+
const known = new Set(specs.map((spec) => spec.id));
|
|
549
|
+
for (const id of requested) {
|
|
550
|
+
if (!known.has(id)) throw new BenchInfraError(`unknown benchmark task: ${id}`);
|
|
551
|
+
}
|
|
552
|
+
return specs.filter((spec) => requested.has(spec.id));
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
function positiveInteger(value, fallback, name) {
|
|
556
|
+
const normalized = value === undefined || value === null || value === '' ? fallback : Number(value);
|
|
557
|
+
if (!Number.isInteger(normalized) || normalized < 1) {
|
|
558
|
+
throw new BenchInfraError(`${name} must be a positive integer`);
|
|
559
|
+
}
|
|
560
|
+
return normalized;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
function nonNegativeInteger(value, fallback, name) {
|
|
564
|
+
const normalized = value === undefined || value === null || value === '' ? fallback : Number(value);
|
|
565
|
+
if (!Number.isInteger(normalized) || normalized < 0) {
|
|
566
|
+
throw new BenchInfraError(`${name} must be a non-negative integer`);
|
|
567
|
+
}
|
|
568
|
+
return normalized;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function requireTreeDirectory(value, name) {
|
|
572
|
+
if (!value) throw new BenchInfraError(`${name} is required`);
|
|
573
|
+
const root = path.resolve(value);
|
|
574
|
+
try {
|
|
575
|
+
if (fs.statSync(root).isDirectory()) return root;
|
|
576
|
+
} catch {
|
|
577
|
+
// Fall through to the plain benchmark error.
|
|
578
|
+
}
|
|
579
|
+
throw new BenchInfraError(`${name} is not a directory: ${value}`);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
function pairingScore(task) {
|
|
583
|
+
if (task.skipped || task.could_not_run) return null;
|
|
584
|
+
return task.passed ? 1 : 0;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
function pairingOutcome(current, candidate) {
|
|
588
|
+
if (current === candidate) return 'tie';
|
|
589
|
+
if (candidate === null) return 'loss';
|
|
590
|
+
if (current === null) return 'win';
|
|
591
|
+
return candidate > current ? 'win' : 'loss';
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
function pairReason(wins, losses, verdict) {
|
|
595
|
+
if (wins === 0 && losses === 0) return 'all pairings tied';
|
|
596
|
+
if (verdict === 'select') {
|
|
597
|
+
return `candidate won ${wins} ${wins === 1 ? 'pairing' : 'pairings'} and lost ${losses}`;
|
|
598
|
+
}
|
|
599
|
+
return `candidate did not exceed its ${losses} ${losses === 1 ? 'loss' : 'losses'} with ${wins} ${wins === 1 ? 'win' : 'wins'}`;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
async function runBenchPair(options = {}) {
|
|
603
|
+
const repoRoot = path.resolve(options.repoRoot || repoRootFromHere());
|
|
604
|
+
const workspaceRoot = path.resolve(options.stateRoot || process.cwd());
|
|
605
|
+
const currentRoot = requireTreeDirectory(options.currentRoot || workspaceRoot, 'current tree');
|
|
606
|
+
const candidateRoot = requireTreeDirectory(options.candidateRoot, 'candidate tree');
|
|
607
|
+
const pack = options.pack || (options.tasksDir ? path.basename(path.resolve(options.tasksDir)) : DEFAULT_PACK);
|
|
608
|
+
const specs = selectTaskSpecsInPackOrder(
|
|
609
|
+
loadTaskSpecs({ repoRoot, tasksDir: options.tasksDir, pack }),
|
|
610
|
+
options.taskIds,
|
|
611
|
+
);
|
|
612
|
+
const engine = normalizeBenchEngine(options.engine);
|
|
613
|
+
if (specs.some((spec) => spec.kind === 'agent') && !engine) {
|
|
614
|
+
throw new BenchInfraError(`agent benchmark pack ${pack} requires --engine <${ENGINE_NAMES.join('|')}>`);
|
|
615
|
+
}
|
|
616
|
+
const repeats = positiveInteger(options.repeats, 1, 'repeats');
|
|
617
|
+
const minWinMargin = nonNegativeInteger(options.minWinMargin, 0, 'min win margin');
|
|
618
|
+
const pythonCmd = options.pythonCmd === undefined ? findPython() : options.pythonCmd;
|
|
619
|
+
const started = new Date().toISOString();
|
|
620
|
+
const tasks = [];
|
|
621
|
+
const currentScores = [];
|
|
622
|
+
const candidateScores = [];
|
|
623
|
+
let wins = 0;
|
|
624
|
+
let losses = 0;
|
|
625
|
+
let ties = 0;
|
|
626
|
+
|
|
627
|
+
for (const spec of specs) {
|
|
628
|
+
for (let repeat = 0; repeat < repeats; repeat += 1) {
|
|
629
|
+
const shared = {
|
|
630
|
+
repoRoot,
|
|
631
|
+
timeoutMs: options.timeoutMs,
|
|
632
|
+
pythonCmd,
|
|
633
|
+
engine,
|
|
634
|
+
engineAdapter: options.engineAdapter,
|
|
635
|
+
model: options.model || null,
|
|
636
|
+
classifyCouldNotRun: true,
|
|
637
|
+
};
|
|
638
|
+
const currentTask = await runTaskSpec(spec, { ...shared, treeRoot: currentRoot });
|
|
639
|
+
const candidateTask = await runTaskSpec(spec, { ...shared, treeRoot: candidateRoot });
|
|
640
|
+
const current = pairingScore(currentTask);
|
|
641
|
+
const candidate = pairingScore(candidateTask);
|
|
642
|
+
const outcome = pairingOutcome(current, candidate);
|
|
643
|
+
if (outcome === 'win') wins += 1;
|
|
644
|
+
else if (outcome === 'loss') losses += 1;
|
|
645
|
+
else ties += 1;
|
|
646
|
+
currentScores.push(current);
|
|
647
|
+
candidateScores.push(candidate);
|
|
648
|
+
tasks.push({
|
|
649
|
+
id: spec.id,
|
|
650
|
+
current,
|
|
651
|
+
candidate,
|
|
652
|
+
outcome,
|
|
653
|
+
current_duration_ms: currentTask.duration_ms,
|
|
654
|
+
candidate_duration_ms: candidateTask.duration_ms,
|
|
655
|
+
current_failures: currentTask.failures,
|
|
656
|
+
candidate_failures: candidateTask.failures,
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
const verdict = wins - losses > minWinMargin ? 'select' : 'reject';
|
|
662
|
+
const record = {
|
|
663
|
+
schema: 'atris.bench.pair.v1',
|
|
664
|
+
pack,
|
|
665
|
+
engine,
|
|
666
|
+
model: engine ? (options.model || null) : null,
|
|
667
|
+
started,
|
|
668
|
+
finished: new Date().toISOString(),
|
|
669
|
+
current_tree_hash: treeHashFor(currentRoot),
|
|
670
|
+
candidate_tree_hash: treeHashFor(candidateRoot),
|
|
671
|
+
tasks,
|
|
672
|
+
current_scores: currentScores,
|
|
673
|
+
candidate_scores: candidateScores,
|
|
674
|
+
wins,
|
|
675
|
+
losses,
|
|
676
|
+
ties,
|
|
677
|
+
min_win_margin: minWinMargin,
|
|
678
|
+
repeats,
|
|
679
|
+
verdict,
|
|
680
|
+
reason: pairReason(wins, losses, verdict),
|
|
681
|
+
};
|
|
682
|
+
if (options.persist !== false) appendResultRecord(record, workspaceRoot);
|
|
683
|
+
const allTied = wins === 0 && losses === 0;
|
|
684
|
+
return { record, exitCode: allTied ? 2 : verdict === 'select' ? 0 : 1 };
|
|
685
|
+
}
|
|
686
|
+
|
|
489
687
|
function taskMetadata(options = {}) {
|
|
490
688
|
return loadTaskSpecs(options).map((spec) => ({
|
|
491
689
|
id: spec.id,
|
|
@@ -514,6 +712,7 @@ module.exports = {
|
|
|
514
712
|
readResultRecords,
|
|
515
713
|
readTaskPrompt,
|
|
516
714
|
runBench,
|
|
715
|
+
runBenchPair,
|
|
517
716
|
summarizeTasks,
|
|
518
717
|
taskMetadata,
|
|
519
718
|
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const os = require('node:os');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
const { spawnSync } = require('node:child_process');
|
|
7
|
+
const { computeTreeHash } = require('../tree-hash');
|
|
8
|
+
|
|
9
|
+
const POINTER_BODY = [
|
|
10
|
+
'Read atris.md at the workspace root first and follow it.',
|
|
11
|
+
'Skills live under atris/skills, team briefs under atris/team.',
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
function writePointerIfAbsent(workspaceDir, filename) {
|
|
15
|
+
const destination = path.join(workspaceDir, filename);
|
|
16
|
+
if (fs.existsSync(destination)) return;
|
|
17
|
+
fs.writeFileSync(destination, [`# ${filename}`, ...POINTER_BODY, ''].join('\n'), 'utf8');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function renderTreeInto(workspaceDir, treeRoot) {
|
|
21
|
+
const workspace = path.resolve(workspaceDir);
|
|
22
|
+
const root = path.resolve(treeRoot);
|
|
23
|
+
const tree = computeTreeHash(root);
|
|
24
|
+
fs.mkdirSync(workspace, { recursive: true });
|
|
25
|
+
|
|
26
|
+
for (const entry of tree.manifest) {
|
|
27
|
+
const source = path.join(root, ...entry.path.split('/'));
|
|
28
|
+
const destination = path.join(workspace, ...entry.path.split('/'));
|
|
29
|
+
fs.mkdirSync(path.dirname(destination), { recursive: true });
|
|
30
|
+
fs.copyFileSync(source, destination);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
writePointerIfAbsent(workspace, 'CLAUDE.md');
|
|
34
|
+
writePointerIfAbsent(workspace, 'AGENTS.md');
|
|
35
|
+
return { tree_hash: tree.hash, files: tree.files };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function candidateError(source) {
|
|
39
|
+
return new Error(`candidate source not found: ${source}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function materializeCandidate(source, repoRoot) {
|
|
43
|
+
const candidate = String(source || '').trim();
|
|
44
|
+
const directPath = path.resolve(candidate || '.');
|
|
45
|
+
try {
|
|
46
|
+
if (candidate && fs.statSync(directPath).isDirectory()) return directPath;
|
|
47
|
+
} catch {
|
|
48
|
+
// A non-directory source may still name a git ref.
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!candidate || candidate.startsWith('-')) throw candidateError(source);
|
|
52
|
+
const root = path.resolve(repoRoot || process.cwd());
|
|
53
|
+
const archive = spawnSync('git', ['archive', '--format=tar', candidate], {
|
|
54
|
+
cwd: root,
|
|
55
|
+
maxBuffer: 128 * 1024 * 1024,
|
|
56
|
+
});
|
|
57
|
+
if (archive.error || archive.status !== 0) throw candidateError(source);
|
|
58
|
+
|
|
59
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'atris-bench-candidate-'));
|
|
60
|
+
const extract = spawnSync('tar', ['-x', '-C', tempDir], {
|
|
61
|
+
cwd: root,
|
|
62
|
+
input: archive.stdout,
|
|
63
|
+
encoding: 'utf8',
|
|
64
|
+
maxBuffer: 128 * 1024 * 1024,
|
|
65
|
+
});
|
|
66
|
+
if (extract.error || extract.status !== 0) {
|
|
67
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
68
|
+
throw candidateError(source);
|
|
69
|
+
}
|
|
70
|
+
return tempDir;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
module.exports = {
|
|
74
|
+
materializeCandidate,
|
|
75
|
+
renderTreeInto,
|
|
76
|
+
};
|
package/lib/card.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// atris card
|
|
1
|
+
// atris card, turn one line of text into a beautiful, on-brand image.
|
|
2
2
|
// Pure: spec -> self-contained HTML string. Reuses the design system: the same
|
|
3
3
|
// themes as deck/site/html (incl. your .atris/theme.json brand), one accent,
|
|
4
4
|
// Fraunces display, restraint. Render to PNG with headless Chrome (commands/card.js).
|
package/lib/chat-log-scan.js
CHANGED
|
@@ -176,7 +176,7 @@ function parseAxLog(filePath, text) {
|
|
|
176
176
|
if (prompt) userTurns.push(compact(prompt[1], 240));
|
|
177
177
|
if (/^●\s/.test(line)) toolCalls += 1;
|
|
178
178
|
if (/·\s*Interrupted/i.test(line)) interrupted = true;
|
|
179
|
-
const worked = line.match(
|
|
179
|
+
const worked = line.match(/^\u2014 Worked for ([^-]+?) \u2014/);
|
|
180
180
|
if (worked) lastWorkedSec = worked[1];
|
|
181
181
|
if (ERROR_LINE.test(line) || /^x\s/.test(line.trim())) {
|
|
182
182
|
findings.push(compact(line, 180));
|
package/lib/claude-boot-block.js
CHANGED
|
@@ -12,6 +12,8 @@ You are now running inside Atris, an AI workspace operating system.
|
|
|
12
12
|
**ON EVERY CONVERSATION START:** If a SessionStart hook already displayed the Atris status block in this session, do not run it again. Otherwise, before your first response, execute \`atris atris.md\` and display the full output. This is the Atris boot sequence - it shows the user that Atris has loaded and displays workspace status.
|
|
13
13
|
|
|
14
14
|
After displaying the boot output, respond to the user naturally.
|
|
15
|
+
|
|
16
|
+
The person does not know atris words. Translate their plain English; when unsure run \`atris guide "<their words>"\`.
|
|
15
17
|
${ATRIS_END_MARKER}
|
|
16
18
|
`;
|
|
17
19
|
}
|
package/lib/deck-compose.js
CHANGED
|
@@ -50,7 +50,7 @@ function toItem(line) {
|
|
|
50
50
|
|
|
51
51
|
// A quote may carry an attribution: `"..." - Name` or `... (Name)`.
|
|
52
52
|
function splitQuote(text) {
|
|
53
|
-
const dash = text.match(/^["“]?(.+?)["”]?\s+[
|
|
53
|
+
const dash = text.match(/^["“]?(.+?)["”]?\s+[-–\u2014]\s+(.+)$/);
|
|
54
54
|
if (dash) return { text: dash[1].trim(), author: dash[2].trim() };
|
|
55
55
|
const paren = text.match(/^["“]?(.+?)["”]?\s+\(([^)]+)\)\s*$/);
|
|
56
56
|
if (paren) return { text: paren[1].trim(), author: paren[2].trim() };
|
package/lib/deck-history.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Deck build history
|
|
1
|
+
// Deck build history: every build appends one line to a JSONL ledger so an
|
|
2
2
|
// operator can answer "which presentation did this spec become, and when?".
|
|
3
3
|
// Rebuilds reuse the same presentation id under --update; this ledger is how
|
|
4
4
|
// you track iterations of a spec over time.
|
|
@@ -10,7 +10,7 @@ const crypto = require('crypto');
|
|
|
10
10
|
|
|
11
11
|
const DEFAULT_HISTORY_PATH = path.join(os.homedir(), '.atris', 'deck-history.jsonl');
|
|
12
12
|
|
|
13
|
-
// Short, stable content hash of a spec
|
|
13
|
+
// Short, stable content hash of a spec, same spec -> same hash, so you can
|
|
14
14
|
// tell a no-op rebuild from a real change.
|
|
15
15
|
function specHash(spec) {
|
|
16
16
|
return crypto.createHash('sha256').update(JSON.stringify(spec || {})).digest('hex').slice(0, 12);
|
package/lib/deck-layout.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Layout planner
|
|
1
|
+
// Layout planner, map content shapes to slide archetypes deterministically.
|
|
2
2
|
//
|
|
3
3
|
// Agents (and `atris deck compose`) describe a deck as an ordered list of
|
|
4
4
|
// SECTIONS, each tagged with a `kind` (cover, quote, points, compare, metrics,
|
package/lib/deck-review.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Deck review loop
|
|
1
|
+
// Deck review loop, build -> thumbnail fetch -> agent visual review -> confirm.
|
|
2
2
|
//
|
|
3
3
|
// Agents should not call a deck "ready" until thumbnails pass visual review.
|
|
4
4
|
// This module downloads slide PNGs via the Atris Google Slides API and writes
|
|
@@ -58,7 +58,7 @@ const BOXED_TYPES = new Set(['panel', 'receipt', 'versus', 'metricgrid', 'stack'
|
|
|
58
58
|
|
|
59
59
|
// Clip thresholds. `warn` = check the thumbnail; `error` = it will clip or lose
|
|
60
60
|
// content, so block the build. `caps` mirror the .slice() limits the engine
|
|
61
|
-
// applies
|
|
61
|
+
// applies, exceeding one silently drops content, which is always an error.
|
|
62
62
|
// Pass `lintSpec(spec, { limits })` to override any of these.
|
|
63
63
|
const CLIP_LIMITS = {
|
|
64
64
|
statementSubWarn: 130,
|
|
@@ -80,7 +80,7 @@ function lintSpec(spec, opts = {}) {
|
|
|
80
80
|
severity: 'error',
|
|
81
81
|
slide: slideNo,
|
|
82
82
|
rule: 'content-truncated',
|
|
83
|
-
message: `${count} ${label} but only ${cap} render; ${count - cap} will be dropped
|
|
83
|
+
message: `${count} ${label} but only ${cap} render; ${count - cap} will be dropped, split the slide`,
|
|
84
84
|
});
|
|
85
85
|
}
|
|
86
86
|
};
|
|
@@ -227,7 +227,7 @@ function lintSpec(spec, opts = {}) {
|
|
|
227
227
|
severity: 'warn',
|
|
228
228
|
slide: slideNo,
|
|
229
229
|
rule: 'stack-sub-clip',
|
|
230
|
-
message: 'Four-layer stack plus a sub: the sub renders below the slide edge
|
|
230
|
+
message: 'Four-layer stack plus a sub: the sub renders below the slide edge, drop a layer or the sub',
|
|
231
231
|
});
|
|
232
232
|
}
|
|
233
233
|
if (layerCount >= 3) {
|
|
@@ -253,7 +253,7 @@ function lintSpec(spec, opts = {}) {
|
|
|
253
253
|
severity: 'warn',
|
|
254
254
|
slide: 0,
|
|
255
255
|
rule: 'template-fatigue',
|
|
256
|
-
message: `${boxedCount} boxed slides
|
|
256
|
+
message: `${boxedCount} boxed slides, consider narrative types: interstitial, lede, prose, split, statement, bignumber`,
|
|
257
257
|
});
|
|
258
258
|
}
|
|
259
259
|
return findings;
|
package/lib/deck-schema.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Deck spec schema validation
|
|
1
|
+
// Deck spec schema validation: catch a bad spec before it hits the Slides API.
|
|
2
2
|
//
|
|
3
3
|
// lintSpec (lib/deck-review.js) judges *taste* (clip risk, template fatigue,
|
|
4
4
|
// AI-tell copy). validateSpec judges *shape*: unknown slide types, missing
|
package/lib/default-verifier.js
CHANGED
|
@@ -10,7 +10,7 @@ const DEFAULT_MISSION_VERIFIER_TIMEOUT_MS = 120000;
|
|
|
10
10
|
// default is a proven footgun: in atrisos-backend `npm test` runs
|
|
11
11
|
// backend/scripts/test_fast.sh, which fails without its env and killed missions
|
|
12
12
|
// after two ticks (2026-07-16). A default the operator never chose must be a
|
|
13
|
-
// check that reflects the mission's own work, not the whole repo's health
|
|
13
|
+
// check that reflects the mission's own work, not the whole repo's health, so
|
|
14
14
|
// the mission lane falls back to the always-safe `git diff --check` and leaves
|
|
15
15
|
// broad suites to explicit `--verify`. Fleet/wish keep the suite default.
|
|
16
16
|
function resolveDefaultVerifier(root = process.cwd(), { allowBroadSuite = true } = {}) {
|
package/lib/developer-api.js
CHANGED
|
@@ -62,6 +62,9 @@ function formatDollars(amount) {
|
|
|
62
62
|
return `$${n.toFixed(2)}`;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
// normalizeBalance is the only in-repo caller; keep the helper internal and
|
|
66
|
+
// stop exporting it so the repo-hygiene ratchet stays green.
|
|
67
|
+
|
|
65
68
|
function normalizeBalance(data = {}) {
|
|
66
69
|
const raw = data && typeof data === 'object' ? data : {};
|
|
67
70
|
const credits = Number(raw.balance != null ? raw.balance : raw.credits);
|
package/lib/engine-ask.js
CHANGED
|
@@ -24,13 +24,13 @@ const DEFAULT_ASK_CONCURRENCY = 3;
|
|
|
24
24
|
const DEFAULT_ASK_TIMEOUT_MS = 120000;
|
|
25
25
|
const MAX_ASK_CONCURRENCY = 4;
|
|
26
26
|
const MAX_ASK_JOBS = 8;
|
|
27
|
-
const
|
|
28
|
-
const
|
|
27
|
+
const DEFAULT_FABLE_ASK_TIMEOUT_MS = 10 * 60 * 1000;
|
|
28
|
+
const MAX_ASK_TIMEOUT_MS = 60 * 60 * 1000;
|
|
29
29
|
const MAX_ASK_PROMPT_BYTES = 16 * 1024;
|
|
30
30
|
const MAX_ASK_TOTAL_PROMPT_BYTES = 64 * 1024;
|
|
31
31
|
const MAX_ASK_OUTPUT_BYTES = 1024 * 1024;
|
|
32
32
|
const ASK_STOP_GRACE_MS = 250;
|
|
33
|
-
const ASK_MODEL_ENGINES = new Set(['claude', 'fable', 'haiku', 'codex', 'cursor', 'devin', 'grok', 'agy']);
|
|
33
|
+
const ASK_MODEL_ENGINES = new Set(['claude', 'fable', 'haiku', 'codex', 'cursor', 'devin', 'grok', 'agy', 'opencode', 'commandcode']);
|
|
34
34
|
const READ_ONLY_PREAMBLE = [
|
|
35
35
|
'This is a read-only request.',
|
|
36
36
|
'Do not modify files, create worktrees, start background agents, or run commands with side effects.',
|
|
@@ -49,6 +49,9 @@ function askUsage() {
|
|
|
49
49
|
` --timeout <sec> per-engine timeout, 1-${MAX_ASK_TIMEOUT_MS / 1000} (default ${DEFAULT_ASK_TIMEOUT_MS / 1000}; Fable ${DEFAULT_FABLE_ASK_TIMEOUT_MS / 1000})`,
|
|
50
50
|
' --json print the receipt as json',
|
|
51
51
|
'',
|
|
52
|
+
'long jobs: set --timeout explicitly; agy receives the same print deadline.',
|
|
53
|
+
'watch a running receipt from another shell: atris engine watch latest',
|
|
54
|
+
'',
|
|
52
55
|
`jobs files contain up to ${MAX_ASK_JOBS} objects: [{"engine":"codex","model":"optional","prompt":"question","label":"optional"}]`,
|
|
53
56
|
].join('\n');
|
|
54
57
|
}
|
|
@@ -202,7 +205,9 @@ function resolveAskModel(engine, modelName = '') {
|
|
|
202
205
|
return RUNNER_PROFILE_DEFS[engine]?.model || DEFAULT_CLAUDE_RUNNER_MODEL;
|
|
203
206
|
}
|
|
204
207
|
|
|
205
|
-
function buildReadOnlyEngineInvocation(engineName, prompt, modelName = ''
|
|
208
|
+
function buildReadOnlyEngineInvocation(engineName, prompt, modelName = '', {
|
|
209
|
+
timeoutMs = DEFAULT_ASK_TIMEOUT_MS,
|
|
210
|
+
} = {}) {
|
|
206
211
|
const engine = canonicalEngineName(engineName);
|
|
207
212
|
const profile = RUNNER_PROFILE_DEFS[engine];
|
|
208
213
|
if (!profile) throw new Error(`unknown engine "${engineName}"`);
|
|
@@ -258,9 +263,28 @@ function buildReadOnlyEngineInvocation(engineName, prompt, modelName = '') {
|
|
|
258
263
|
return {
|
|
259
264
|
engine,
|
|
260
265
|
bin: profile.bin,
|
|
261
|
-
args: [
|
|
266
|
+
args: [
|
|
267
|
+
'--mode', 'plan',
|
|
268
|
+
'--sandbox',
|
|
269
|
+
'--dangerously-skip-permissions',
|
|
270
|
+
'--print-timeout', `${Math.max(1, Math.ceil(timeoutMs / 1000))}s`,
|
|
271
|
+
...(model ? ['--model', model] : []),
|
|
272
|
+
'-p', request,
|
|
273
|
+
],
|
|
262
274
|
};
|
|
263
275
|
}
|
|
276
|
+
if (engine === 'commandcode') {
|
|
277
|
+
// Headless print mode blocks file edits and shell commands by default,
|
|
278
|
+
// so plain -p is already read-only; no sandbox flags needed. Default is
|
|
279
|
+
// no --model: the run rides this machine's configured Command Code
|
|
280
|
+
// default, with an explicit ask --model still honored.
|
|
281
|
+
return { engine, bin: profile.bin, args: ['-p', request, ...(model ? ['--model', model] : [])] };
|
|
282
|
+
}
|
|
283
|
+
if (engine === 'opencode') {
|
|
284
|
+
// `run` with a message is headless print mode; the built-in plan agent
|
|
285
|
+
// keeps the ask read-only (verified live 2026-08-21, ~7s per lookup).
|
|
286
|
+
return { engine, bin: profile.bin, args: ['--agent', 'plan', ...(model ? ['-m', model] : []), request] };
|
|
287
|
+
}
|
|
264
288
|
throw new Error(`engine ask has no read-only command for ${engine}`);
|
|
265
289
|
}
|
|
266
290
|
|
|
@@ -292,6 +316,14 @@ function cancelledAskResult(job, extra = {}) {
|
|
|
292
316
|
};
|
|
293
317
|
}
|
|
294
318
|
|
|
319
|
+
function normalizeAskResult(engine, result = {}) {
|
|
320
|
+
const output = `${String(result.stdout || '')}\n${String(result.stderr || '')}`;
|
|
321
|
+
if (engine === 'agy' && !result.cancelled && /timeout waiting for response/i.test(output)) {
|
|
322
|
+
return { ...result, ok: false, reason: 'timeout', timed_out: true };
|
|
323
|
+
}
|
|
324
|
+
return result;
|
|
325
|
+
}
|
|
326
|
+
|
|
295
327
|
function osAccountName() {
|
|
296
328
|
try {
|
|
297
329
|
return String(os.userInfo().username || '').trim();
|
|
@@ -448,7 +480,7 @@ async function runEngineAskJobs(jobs, {
|
|
|
448
480
|
const answers = new Array(jobs.length);
|
|
449
481
|
let nextIndex = 0;
|
|
450
482
|
const execute = executeAskJob || (async (job) => {
|
|
451
|
-
const invocation = buildReadOnlyEngineInvocation(job.engine, job.prompt, job.model);
|
|
483
|
+
const invocation = buildReadOnlyEngineInvocation(job.engine, job.prompt, job.model, { timeoutMs });
|
|
452
484
|
return runAskProcess(invocation, { cwd: invocation.cwd || root, timeoutMs, signal, onOutputChunk });
|
|
453
485
|
});
|
|
454
486
|
const workerCount = Math.min(concurrency, jobs.length);
|
|
@@ -465,7 +497,8 @@ async function runEngineAskJobs(jobs, {
|
|
|
465
497
|
}
|
|
466
498
|
try {
|
|
467
499
|
assertAskModelSupported(job.engine, job.model);
|
|
468
|
-
|
|
500
|
+
const result = await execute(job, { root, timeoutMs, index, signal, onOutputChunk });
|
|
501
|
+
answers[index] = { ...job, ...normalizeAskResult(job.engine, result) };
|
|
469
502
|
} catch (error) {
|
|
470
503
|
answers[index] = {
|
|
471
504
|
...job,
|
|
@@ -586,12 +619,17 @@ async function runEngineAskCommand(args, root = process.cwd(), deps = {}) {
|
|
|
586
619
|
started_at: startedAt,
|
|
587
620
|
at: startedAt,
|
|
588
621
|
read_only: true,
|
|
622
|
+
timeout_ms: parsed.timeoutMs,
|
|
589
623
|
};
|
|
590
624
|
const receiptPath = (deps.createReceipt || createEngineAskReceipt)(root, runningReceipt);
|
|
591
625
|
const relativeReceiptPath = path.relative(root, receiptPath) || receiptPath;
|
|
592
626
|
const liveLogPath = (deps.createLiveLog || createEngineLiveLog)(receiptPath);
|
|
593
627
|
runningReceipt.live_log = path.relative(root, liveLogPath) || liveLogPath;
|
|
594
628
|
(deps.updateReceipt || atomicWriteEngineAskReceipt)(receiptPath, runningReceipt);
|
|
629
|
+
if (!parsed.json) {
|
|
630
|
+
console.log(`engine ask started: ${relativeReceiptPath}`);
|
|
631
|
+
console.log(`watch: atris engine watch ${path.basename(receiptPath, '.json')}`);
|
|
632
|
+
}
|
|
595
633
|
const appendLiveLog = deps.appendLiveLog
|
|
596
634
|
|| ((filePath, chunk) => appendEngineLiveLogChunk(filePath, chunk));
|
|
597
635
|
let interruptedSignal = '';
|