@quickchatai/quickreview 1.2.0 → 1.3.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/README.md +116 -53
- package/bin/qcr.js +1 -1
- package/config.example.toml +25 -7
- package/deploy/ansible/README.md +20 -12
- package/deploy/ansible/inventory.example.ini +1 -1
- package/deploy/collect-secrets.sh +1 -1
- package/deploy/qcr-update.sh +1 -1
- package/dist/extensions/cli/home.js +14 -0
- package/dist/extensions/cli/home.js.map +1 -0
- package/dist/extensions/cli/selfimprove.js +18 -0
- package/dist/extensions/cli/selfimprove.js.map +1 -0
- package/dist/extensions/home.js +350 -0
- package/dist/extensions/home.js.map +1 -0
- package/dist/extensions/login-route.js +1 -1
- package/dist/extensions/login-route.js.map +1 -1
- package/dist/extensions/onboarding.js +8 -7
- package/dist/extensions/onboarding.js.map +1 -1
- package/dist/extensions/selfimprove.js +49 -0
- package/dist/extensions/selfimprove.js.map +1 -0
- package/dist/extensions/setup-scan.js +23 -592
- package/dist/extensions/setup-scan.js.map +1 -1
- package/dist/extensions/setup.js +2 -47
- package/dist/extensions/setup.js.map +1 -1
- package/dist/extensions/vendor/openviking-pi/index.js +4 -0
- package/dist/extensions/vendor/openviking-pi/index.js.map +1 -1
- package/dist/extensions/vendor/openviking-pi/qcr-hooks.js +3 -1
- package/dist/extensions/vendor/openviking-pi/qcr-hooks.js.map +1 -1
- package/dist/extensions/vendor/openviking-pi/sync.js +81 -0
- package/dist/extensions/vendor/openviking-pi/sync.js.map +1 -1
- package/dist/src/archive/restore.js +18 -15
- package/dist/src/archive/restore.js.map +1 -1
- package/dist/src/botworker.js +13 -2
- package/dist/src/botworker.js.map +1 -1
- package/dist/src/channelfs.js +3 -26
- package/dist/src/channelfs.js.map +1 -1
- package/dist/src/cli.js +14 -50
- package/dist/src/cli.js.map +1 -1
- package/dist/src/config.js +16 -0
- package/dist/src/config.js.map +1 -1
- package/dist/src/daemon.js +33 -4
- package/dist/src/daemon.js.map +1 -1
- package/dist/src/debuground.js +1 -0
- package/dist/src/debuground.js.map +1 -1
- package/dist/src/debugroundpi.js +1 -0
- package/dist/src/debugroundpi.js.map +1 -1
- package/dist/src/engine/caserunner-sdk.js +15 -251
- package/dist/src/engine/caserunner-sdk.js.map +1 -1
- package/dist/src/home/adapters.js +349 -0
- package/dist/src/home/adapters.js.map +1 -0
- package/dist/src/home/advanced.js +318 -0
- package/dist/src/home/advanced.js.map +1 -0
- package/dist/src/home/advancedtools.js +57 -0
- package/dist/src/home/advancedtools.js.map +1 -0
- package/dist/src/home/autoreview.js +325 -0
- package/dist/src/home/autoreview.js.map +1 -0
- package/dist/src/home/daemonstatus.js +32 -0
- package/dist/src/home/daemonstatus.js.map +1 -0
- package/dist/src/home/devicelogin.js +61 -0
- package/dist/src/home/devicelogin.js.map +1 -0
- package/dist/src/home/launch.js +202 -0
- package/dist/src/home/launch.js.map +1 -0
- package/dist/src/home/loginowner.js +205 -0
- package/dist/src/home/loginowner.js.map +1 -0
- package/dist/src/home/memory.js +122 -0
- package/dist/src/home/memory.js.map +1 -0
- package/dist/src/home/model.js +811 -0
- package/dist/src/home/model.js.map +1 -0
- package/dist/src/home/panel.js +520 -0
- package/dist/src/home/panel.js.map +1 -0
- package/dist/src/home/ports.js +19 -0
- package/dist/src/home/ports.js.map +1 -0
- package/dist/src/home/review.js +163 -0
- package/dist/src/home/review.js.map +1 -0
- package/dist/src/home/reviews.js +478 -0
- package/dist/src/home/reviews.js.map +1 -0
- package/dist/src/home/setup.js +507 -0
- package/dist/src/home/setup.js.map +1 -0
- package/dist/src/home/setupadapters.js +214 -0
- package/dist/src/home/setupadapters.js.map +1 -0
- package/dist/src/home/setupcard.js +199 -0
- package/dist/src/home/setupcard.js.map +1 -0
- package/dist/src/home/setuptools.js +45 -0
- package/dist/src/home/setuptools.js.map +1 -0
- package/dist/src/home/spend.js +160 -0
- package/dist/src/home/spend.js.map +1 -0
- package/dist/src/home/switches.js +209 -0
- package/dist/src/home/switches.js.map +1 -0
- package/dist/src/home/switchon.js +439 -0
- package/dist/src/home/switchon.js.map +1 -0
- package/dist/src/home/toolnames.js +41 -0
- package/dist/src/home/toolnames.js.map +1 -0
- package/dist/src/home/tune.js +341 -0
- package/dist/src/home/tune.js.map +1 -0
- package/dist/src/home/words.js +7 -0
- package/dist/src/home/words.js.map +1 -0
- package/dist/src/homes.js +14 -0
- package/dist/src/homes.js.map +1 -1
- package/dist/src/install.js +1 -1
- package/dist/src/install.js.map +1 -1
- package/dist/src/ipc/workerspec.js +8 -0
- package/dist/src/ipc/workerspec.js.map +1 -1
- package/dist/src/ledgercheck.js +39 -10
- package/dist/src/ledgercheck.js.map +1 -1
- package/dist/src/live/publish.js +1 -1
- package/dist/src/live/publish.js.map +1 -1
- package/dist/src/livecli.js +1 -1
- package/dist/src/livecli.js.map +1 -1
- package/dist/src/loginrefresh/handover.js +121 -0
- package/dist/src/loginrefresh/handover.js.map +1 -0
- package/dist/src/loginrefresh/lock.js +54 -3
- package/dist/src/loginrefresh/lock.js.map +1 -1
- package/dist/src/loginrefresh/refresher.js +33 -5
- package/dist/src/loginrefresh/refresher.js.map +1 -1
- package/dist/src/loginrefresh/supervisor.js +18 -1
- package/dist/src/loginrefresh/supervisor.js.map +1 -1
- package/dist/src/npmverbs.js +42 -12
- package/dist/src/npmverbs.js.map +1 -1
- package/dist/src/onboard/choices.js +6 -3
- package/dist/src/onboard/choices.js.map +1 -1
- package/dist/src/onboard/engine.js +33 -292
- package/dist/src/onboard/engine.js.map +1 -1
- package/dist/src/onboard/steps/bringup.js +3 -2
- package/dist/src/onboard/steps/bringup.js.map +1 -1
- package/dist/src/onboard/steps/configfill.js +18 -7
- package/dist/src/onboard/steps/configfill.js.map +1 -1
- package/dist/src/onboard/steps/loginshare.js +213 -0
- package/dist/src/onboard/steps/loginshare.js.map +1 -0
- package/dist/src/onboard/steps/memory.js +14 -2
- package/dist/src/onboard/steps/memory.js.map +1 -1
- package/dist/src/onboard/steps/repoconfig.js +72 -0
- package/dist/src/onboard/steps/repoconfig.js.map +1 -0
- package/dist/src/onboard/toolnames.js +2 -2
- package/dist/src/onboard/toolnames.js.map +1 -1
- package/dist/src/piagentdir.js +13 -0
- package/dist/src/piagentdir.js.map +1 -0
- package/dist/src/pilogin.js +31 -16
- package/dist/src/pilogin.js.map +1 -1
- package/dist/src/qcrbin.js +16 -11
- package/dist/src/qcrbin.js.map +1 -1
- package/dist/src/reviewargs.js +122 -0
- package/dist/src/reviewargs.js.map +1 -0
- package/dist/src/reviewcli.js +7 -161
- package/dist/src/reviewcli.js.map +1 -1
- package/dist/src/reviewdetach.js +76 -11
- package/dist/src/reviewdetach.js.map +1 -1
- package/dist/src/scancli.js +39 -691
- package/dist/src/scancli.js.map +1 -1
- package/dist/src/selfimprove.js +66 -0
- package/dist/src/selfimprove.js.map +1 -0
- package/dist/src/session-command-supervisor.js +38 -9
- package/dist/src/session-command-supervisor.js.map +1 -1
- package/dist/src/setup/adapters.js +5 -223
- package/dist/src/setup/adapters.js.map +1 -1
- package/dist/src/setup/caserunner.js +15 -249
- package/dist/src/setup/caserunner.js.map +1 -1
- package/dist/src/setup/engine.js +32 -341
- package/dist/src/setup/engine.js.map +1 -1
- package/dist/src/setup/facts.js +3 -592
- package/dist/src/setup/facts.js.map +1 -1
- package/dist/src/setup/findings.js +3 -947
- package/dist/src/setup/findings.js.map +1 -1
- package/dist/src/setup/presentation.js +3 -8
- package/dist/src/setup/presentation.js.map +1 -1
- package/dist/src/setup/scan.js +3 -724
- package/dist/src/setup/scan.js.map +1 -1
- package/dist/src/setup/scanbuild.js +9 -92
- package/dist/src/setup/scanbuild.js.map +1 -1
- package/dist/src/setup/scanlease.js +3 -250
- package/dist/src/setup/scanlease.js.map +1 -1
- package/dist/src/setup/scanreport.js +3 -288
- package/dist/src/setup/scanreport.js.map +1 -1
- package/dist/src/setup/scanrun.js +3 -1732
- package/dist/src/setup/scanrun.js.map +1 -1
- package/dist/src/setup/scantree.js +3 -256
- package/dist/src/setup/scantree.js.map +1 -1
- package/dist/src/setup/stage2request.js +3 -258
- package/dist/src/setup/stage2request.js.map +1 -1
- package/dist/src/setup/unitcut.js +3 -436
- package/dist/src/setup/unitcut.js.map +1 -1
- package/dist/src/state.js +75 -9
- package/dist/src/state.js.map +1 -1
- package/dist/src/takeover.js +1 -0
- package/dist/src/takeover.js.map +1 -1
- package/dist/src/takeoverpi.js +2 -1
- package/dist/src/takeoverpi.js.map +1 -1
- package/dist/src/tui/alerts.js +4 -1
- package/dist/src/tui/alerts.js.map +1 -1
- package/dist/src/tui/board.js +2 -2
- package/dist/src/tui/board.js.map +1 -1
- package/dist/src/tui/companion.js +2 -1
- package/dist/src/tui/companion.js.map +1 -1
- package/dist/src/tui/deploytab.js +2 -2
- package/dist/src/tui/deploytab.js.map +1 -1
- package/dist/src/tui/index.js +9 -6
- package/dist/src/tui/index.js.map +1 -1
- package/dist/src/tui/memory.js +3 -16
- package/dist/src/tui/memory.js.map +1 -1
- package/dist/src/tui/memoryurl.js +22 -0
- package/dist/src/tui/memoryurl.js.map +1 -0
- package/dist/src/tui/remote.js +1 -1
- package/dist/src/tui/remote.js.map +1 -1
- package/dist/src/tui/route.js +2 -2
- package/dist/src/tui/route.js.map +1 -1
- package/dist/src/tui/setupstate.js +8 -5
- package/dist/src/tui/setupstate.js.map +1 -1
- package/dist/src/tui/sshtriage.js +1 -1
- package/dist/src/tui/sshtriage.js.map +1 -1
- package/dist/src/verdicts.js +12 -0
- package/dist/src/verdicts.js.map +1 -1
- package/dist/src/wire.js +9 -4
- package/dist/src/wire.js.map +1 -1
- package/dist/src/workerbreaker.js +32 -5
- package/dist/src/workerbreaker.js.map +1 -1
- package/dist/src/workerworld.js +4 -0
- package/dist/src/workerworld.js.map +1 -1
- package/docker/README.md +1 -1
- package/docker/entrypoint.sh +40 -6
- package/docker-compose.yml +16 -1
- package/extensions/cli/home.ts +14 -0
- package/extensions/cli/selfimprove.ts +18 -0
- package/extensions/home.ts +484 -0
- package/extensions/login-route.ts +1 -1
- package/extensions/onboarding.ts +8 -7
- package/extensions/selfimprove.ts +65 -0
- package/extensions/setup-scan.ts +79 -766
- package/extensions/setup.ts +14 -70
- package/extensions/vendor/openviking-pi/NOTICE +5 -0
- package/extensions/vendor/openviking-pi/README.md +39 -5
- package/extensions/vendor/openviking-pi/index.ts +4 -0
- package/extensions/vendor/openviking-pi/patches/0014-one-turn-sync-in-flight.patch +239 -0
- package/extensions/vendor/openviking-pi/qcr-hooks.ts +3 -1
- package/extensions/vendor/openviking-pi/sync.ts +80 -0
- package/extensions/vendor/openviking-pi/upstream.json +2 -1
- package/package.json +6 -4
- package/prompts/onboarding.md +8 -7
- package/schemas/review-result.v1.json +60 -0
- package/scripts/deploy.sh +8 -6
- package/scripts/qcr-host.sh +28 -5
- package/scripts/vm-ghcr-login.sh +7 -4
- package/src/archive/restore.ts +18 -21
- package/src/botworker.ts +13 -2
- package/src/channelfs.ts +3 -26
- package/src/cli.ts +13 -54
- package/src/config.ts +38 -0
- package/src/daemon.ts +34 -4
- package/src/debuground.ts +3 -0
- package/src/debugroundpi.ts +1 -0
- package/src/engine/caserunner-sdk.ts +29 -314
- package/src/home/adapters.ts +418 -0
- package/src/home/advanced.ts +421 -0
- package/src/home/advancedtools.ts +96 -0
- package/src/home/autoreview.ts +385 -0
- package/src/home/daemonstatus.ts +55 -0
- package/src/home/devicelogin.ts +107 -0
- package/src/home/launch.ts +257 -0
- package/src/home/loginowner.ts +248 -0
- package/src/home/memory.ts +176 -0
- package/src/home/model.ts +965 -0
- package/src/home/panel.ts +666 -0
- package/src/home/ports.ts +338 -0
- package/src/home/review.ts +206 -0
- package/src/home/reviews.ts +565 -0
- package/src/home/setup.ts +689 -0
- package/src/home/setupadapters.ts +217 -0
- package/src/home/setupcard.ts +288 -0
- package/src/home/setuptools.ts +57 -0
- package/src/home/spend.ts +194 -0
- package/src/home/switches.ts +293 -0
- package/src/home/switchon.ts +554 -0
- package/src/home/toolnames.ts +46 -0
- package/src/home/tune.ts +405 -0
- package/src/home/words.ts +8 -0
- package/src/homes.ts +17 -0
- package/src/install.ts +1 -1
- package/src/ipc/workerspec.ts +15 -0
- package/src/ledgercheck.ts +45 -9
- package/src/live/publish.ts +4 -1
- package/src/livecli.ts +6 -1
- package/src/loginrefresh/handover.ts +138 -0
- package/src/loginrefresh/lock.ts +72 -3
- package/src/loginrefresh/refresher.ts +34 -5
- package/src/loginrefresh/supervisor.ts +25 -2
- package/src/npmverbs.ts +51 -11
- package/src/onboard/choices.ts +6 -3
- package/src/onboard/engine.ts +64 -434
- package/src/onboard/steps/bringup.ts +3 -2
- package/src/onboard/steps/configfill.ts +23 -7
- package/src/onboard/steps/loginshare.ts +258 -0
- package/src/onboard/steps/memory.ts +18 -2
- package/src/onboard/steps/repoconfig.ts +85 -0
- package/src/onboard/toolnames.ts +2 -2
- package/src/piagentdir.ts +12 -0
- package/src/pilogin.ts +32 -16
- package/src/qcrbin.ts +16 -11
- package/src/reviewargs.ts +119 -0
- package/src/reviewcli.ts +6 -167
- package/src/reviewdetach.ts +107 -13
- package/src/scancli.ts +75 -826
- package/src/selfimprove.ts +90 -0
- package/src/session-command-supervisor.ts +41 -8
- package/src/setup/adapters.ts +13 -221
- package/src/setup/caserunner.ts +34 -273
- package/src/setup/engine.ts +62 -496
- package/src/setup/facts.ts +3 -627
- package/src/setup/findings.ts +3 -1264
- package/src/setup/presentation.ts +3 -18
- package/src/setup/scan.ts +3 -1033
- package/src/setup/scanbuild.ts +20 -114
- package/src/setup/scanlease.ts +3 -297
- package/src/setup/scanreport.ts +3 -373
- package/src/setup/scanrun.ts +3 -2268
- package/src/setup/scantree.ts +3 -259
- package/src/setup/stage2request.ts +3 -347
- package/src/setup/unitcut.ts +3 -523
- package/src/state.ts +126 -11
- package/src/takeover.ts +5 -0
- package/src/takeoverpi.ts +3 -2
- package/src/tui/alerts.ts +9 -2
- package/src/tui/board.ts +2 -2
- package/src/tui/companion.ts +2 -1
- package/src/tui/deploytab.ts +2 -2
- package/src/tui/index.ts +9 -6
- package/src/tui/memory.ts +3 -15
- package/src/tui/memoryurl.ts +22 -0
- package/src/tui/remote.ts +1 -1
- package/src/tui/route.ts +2 -2
- package/src/tui/setupstate.ts +20 -16
- package/src/tui/sshtriage.ts +1 -1
- package/src/verdicts.ts +13 -0
- package/src/wire.ts +9 -4
- package/src/workerbreaker.ts +41 -5
- package/src/workerworld.ts +9 -1
- package/dist/src/live/projection.js +0 -450
- package/dist/src/live/projection.js.map +0 -1
- package/dist/src/live/read.js +0 -60
- package/dist/src/live/read.js.map +0 -1
- package/dist/src/onboard/launch.js +0 -75
- package/dist/src/onboard/launch.js.map +0 -1
- package/dist/src/setup/ledger.js +0 -207
- package/dist/src/setup/ledger.js.map +0 -1
- package/dist/src/setup/stage2.js +0 -617
- package/dist/src/setup/stage2.js.map +0 -1
- package/dist/src/setup/textsniff.js +0 -89
- package/dist/src/setup/textsniff.js.map +0 -1
- package/src/live/projection.ts +0 -491
- package/src/live/read.ts +0 -86
- package/src/onboard/launch.ts +0 -103
- package/src/setup/ledger.ts +0 -324
- package/src/setup/stage2.ts +0 -926
- package/src/setup/textsniff.ts +0 -95
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
// The scan's leaf runner on the SDK
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// runtime, with the same pieces the child process gets: the agent file's model,
|
|
5
|
-
// tools and body (appended to pi's own system prompt), bash-guard the one
|
|
6
|
-
// extension, no skill and no context file, a cwd that is first-party ground and
|
|
7
|
-
// never the reviewed checkout, and the allowlisted env. The outcome, the reads
|
|
8
|
-
// and the kept transcript are read off the same event stream by the same
|
|
9
|
-
// reader (`caseReadout`), so a case answers identically whichever host ran it.
|
|
1
|
+
// The scan driver's SDK leaves: the leaf runner on the SDK (scanleaves.ts
|
|
2
|
+
// `sdkCaseRunner`, the in-process twin of `piCaseRunner`) over a host this
|
|
3
|
+
// module builds per case, on the owner's borrowed login.
|
|
10
4
|
//
|
|
11
5
|
// WHERE IT RUNS. Only in scancli's detached driver, a disposable one-scan
|
|
12
6
|
// process (the scan sandbox or the host process `detachDriver` starts), whose
|
|
@@ -20,27 +14,10 @@
|
|
|
20
14
|
// login (engineCredentials): a token the companion refreshed mid-scan is
|
|
21
15
|
// picked up at the next case, a token the case would not outlive is refused
|
|
22
16
|
// before it starts, and nothing is ever refreshed or written here.
|
|
17
|
+
import { copyFileSync, existsSync, mkdirSync, rmSync } from "node:fs";
|
|
18
|
+
import { homedir } from "node:os";
|
|
19
|
+
import { isAbsolute, join } from "node:path";
|
|
23
20
|
import {
|
|
24
|
-
appendFileSync,
|
|
25
|
-
copyFileSync,
|
|
26
|
-
existsSync,
|
|
27
|
-
mkdirSync,
|
|
28
|
-
mkdtempSync,
|
|
29
|
-
readFileSync,
|
|
30
|
-
rmSync,
|
|
31
|
-
writeFileSync,
|
|
32
|
-
} from "node:fs";
|
|
33
|
-
import { homedir, tmpdir } from "node:os";
|
|
34
|
-
import { basename, dirname, isAbsolute, join } from "node:path";
|
|
35
|
-
import type { ExtensionFactory } from "@earendil-works/pi-coding-agent";
|
|
36
|
-
import {
|
|
37
|
-
bashGuardConfigFromEnv,
|
|
38
|
-
createBashGuard,
|
|
39
|
-
} from "@quickchatai/pi-quickreview/extensions/bash-guard.js";
|
|
40
|
-
import { CASE_STOP_GRACE_MS } from "@quickchatai/pi-quickreview/src/casebudget.js";
|
|
41
|
-
import { splitModelSpec } from "../config.js";
|
|
42
|
-
import {
|
|
43
|
-
AuthError,
|
|
44
21
|
AuthExpiring,
|
|
45
22
|
SCAN_AUTH_SRC_ENV,
|
|
46
23
|
SCAN_CASE_AUTH_BUDGET_MS,
|
|
@@ -48,293 +25,30 @@ import {
|
|
|
48
25
|
scanAuthExpiring,
|
|
49
26
|
scanAuthMissing,
|
|
50
27
|
} from "@quickchatai/pi-quickreview/src/engine/auth.js";
|
|
51
|
-
import {
|
|
52
|
-
TRANSCRIPT_RUNS_KEPT,
|
|
53
|
-
caseReadout,
|
|
54
|
-
leafEnv,
|
|
55
|
-
parseAgentFile,
|
|
56
|
-
pruneTranscripts,
|
|
57
|
-
stderrExcerpt,
|
|
58
|
-
transcriptFile,
|
|
59
|
-
type AgentFile,
|
|
60
|
-
} from "../setup/caserunner.js";
|
|
61
|
-
import type { CaseOutcome, CaseRunner, CaseSpec } from "../setup/engine.js";
|
|
62
|
-
import { LENS_SESSIONS_DIR } from "@quickchatai/pi-quickreview/src/engine/openable.js";
|
|
63
28
|
import { PiHost } from "@quickchatai/pi-quickreview/src/engine/pisdk.js";
|
|
64
|
-
import
|
|
65
|
-
Runtime,
|
|
66
|
-
SessionSpec,
|
|
67
|
-
TurnResult,
|
|
68
|
-
} from "@quickchatai/pi-quickreview/src/engine/port.js";
|
|
69
|
-
import {
|
|
70
|
-
sdkRuntime,
|
|
71
|
-
type SessionExtras,
|
|
72
|
-
} from "@quickchatai/pi-quickreview/src/engine/sdkruntime.js";
|
|
29
|
+
import { sdkRuntime } from "@quickchatai/pi-quickreview/src/engine/sdkruntime.js";
|
|
73
30
|
import { BUDGET_EXIT_CODE } from "@quickchatai/pi-quickreview/src/engine/budget.js";
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
"
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
"https_proxy",
|
|
96
|
-
"no_proxy",
|
|
97
|
-
"all_proxy",
|
|
98
|
-
];
|
|
99
|
-
|
|
100
|
-
/** The per-session pieces the runner hands whoever builds the runtime: the
|
|
101
|
-
* `extras` of `sdkRuntime` (packages/pi-plugin/src/engine/sdkruntime.ts). */
|
|
102
|
-
export type LeafExtras = (spec: SessionSpec) => SessionExtras;
|
|
103
|
-
|
|
104
|
-
export interface SdkCaseRunnerOptions {
|
|
105
|
-
/** The scan agent build: `<agent>.md` files. */
|
|
106
|
-
agentsDir: string;
|
|
107
|
-
/** The leaf's working directory: first-party ground, never the reviewed
|
|
108
|
-
* checkout (whose own pi config must not load). Also what a relative read is
|
|
109
|
-
* resolved against. */
|
|
110
|
-
cwd: string;
|
|
111
|
-
/** Where each case's event stream is kept; absent, none is. */
|
|
112
|
-
transcriptDir?: string;
|
|
113
|
-
/** The env the leaf's env is filtered from. Omitted: process.env. */
|
|
114
|
-
env?: NodeJS.ProcessEnv;
|
|
115
|
-
/** The model of an agent that names none. */
|
|
116
|
-
defaultModel?: string;
|
|
117
|
-
/** The runtime this case runs on, built with the case's `extras`. Called once
|
|
118
|
-
* per case: a route that must re-read its login per case builds its host
|
|
119
|
-
* here. */
|
|
120
|
-
runtime: (extras: LeafExtras) => Runtime | Promise<Runtime>;
|
|
121
|
-
/** The guard extension. Omitted: bash-guard over the leaf env. */
|
|
122
|
-
guard?: ExtensionFactory;
|
|
123
|
-
/** Wall clock for a case's turn. Omitted: `LEAF_TURN_TIMEOUT_MS`. */
|
|
124
|
-
timeoutMs?: number;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/** A transcript's path under `dir`, relative and absolute, with its run
|
|
128
|
-
* directory made (and the old runs pruned when it is new); null when it cannot
|
|
129
|
-
* be made. A transcript is evidence, never a reason to fail a case. */
|
|
130
|
-
function openTranscript(dir: string, id: string): { rel: string; at: string } | null {
|
|
131
|
-
try {
|
|
132
|
-
const rel = transcriptFile(id);
|
|
133
|
-
const at = join(dir, rel);
|
|
134
|
-
const runDir = rel.includes("/") ? rel.slice(0, rel.indexOf("/")) : null;
|
|
135
|
-
const fresh = runDir !== null && !existsSync(join(dir, runDir));
|
|
136
|
-
mkdirSync(dirname(at), { recursive: true });
|
|
137
|
-
if (fresh) pruneTranscripts(dir, TRANSCRIPT_RUNS_KEPT, runDir);
|
|
138
|
-
writeFileSync(at, "", { mode: 0o600 });
|
|
139
|
-
return { rel, at };
|
|
140
|
-
} catch {
|
|
141
|
-
return null;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/** Where a case's session JSONL goes: `<run>/lens-sessions/<case>/` beside the
|
|
146
|
-
* transcript `rel` names (`<run>/<case>.jsonl`). */
|
|
147
|
-
export function caseSessionDir(transcriptDir: string, rel: string): string {
|
|
148
|
-
return join(transcriptDir, dirname(rel), LENS_SESSIONS_DIR, basename(rel, ".jsonl"));
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Runs a case as one SDK session, the way `piCaseRunner` runs it as one child:
|
|
153
|
-
* the agent's model (its `:thinking` suffix as the session's thinking level),
|
|
154
|
-
* its tools, its body appended to pi's system prompt, the task as the one
|
|
155
|
-
* message. The answer is the last assistant message's text; the cost is pi's
|
|
156
|
-
* own per-message accounting summed (null when it reported none); `reads` is the
|
|
157
|
-
* `read`/`grep` calls that finished without an error, resolved against `cwd`.
|
|
158
|
-
* With `transcriptDir` the session's whole event stream is kept at
|
|
159
|
-
* `transcriptFile(spec.id)` under it, the format `pi --mode json` prints, and
|
|
160
|
-
* its session JSONL beside it (`caseSessionDir`). An
|
|
161
|
-
* abort stops the session (abort, then dispose after the stop grace). Every
|
|
162
|
-
* outcome resolves — a runtime that cannot be built or a session that cannot
|
|
163
|
-
* start is a failed case, never a rejection.
|
|
164
|
-
*/
|
|
165
|
-
export function sdkCaseRunner(opts: SdkCaseRunnerOptions): CaseRunner {
|
|
166
|
-
return {
|
|
167
|
-
run: async (
|
|
168
|
-
spec: CaseSpec,
|
|
169
|
-
signal: AbortSignal,
|
|
170
|
-
onCost?: (spentSoFar: number) => void,
|
|
171
|
-
): Promise<CaseOutcome> => {
|
|
172
|
-
if (!/^[a-z][a-z0-9-]*$/.test(spec.agent))
|
|
173
|
-
return {
|
|
174
|
-
ok: false,
|
|
175
|
-
reason: `not an agent name: ${JSON.stringify(spec.agent)}`,
|
|
176
|
-
cost_usd: 0,
|
|
177
|
-
};
|
|
178
|
-
let agent: AgentFile | null;
|
|
179
|
-
try {
|
|
180
|
-
agent = parseAgentFile(readFileSync(join(opts.agentsDir, `${spec.agent}.md`), "utf8"));
|
|
181
|
-
} catch {
|
|
182
|
-
return { ok: false, reason: `no agent ${spec.agent} in ${opts.agentsDir}`, cost_usd: 0 };
|
|
183
|
-
}
|
|
184
|
-
if (agent === null || agent.name !== spec.agent)
|
|
185
|
-
return { ok: false, reason: `${spec.agent}.md is not an agent file`, cost_usd: 0 };
|
|
186
|
-
const modelSpec = agent.model ?? opts.defaultModel;
|
|
187
|
-
if (!modelSpec)
|
|
188
|
-
return { ok: false, reason: `${spec.agent} names no model and none is set`, cost_usd: 0 };
|
|
189
|
-
if (signal.aborted) return { ok: false, reason: "stopped before it started", cost_usd: 0 };
|
|
190
|
-
|
|
191
|
-
let tmp: string;
|
|
192
|
-
try {
|
|
193
|
-
tmp = mkdtempSync(join(tmpdir(), "qcr-case-"));
|
|
194
|
-
} catch (err) {
|
|
195
|
-
return { ok: false, reason: `no temp directory for the case: ${String(err)}`, cost_usd: 0 };
|
|
196
|
-
}
|
|
197
|
-
let session: Awaited<ReturnType<Runtime["create"]>> | undefined;
|
|
198
|
-
try {
|
|
199
|
-
const env = leafEnv(opts.env);
|
|
200
|
-
const { model, thinking } = splitModelSpec(modelSpec);
|
|
201
|
-
const guard = opts.guard ?? createBashGuard(bashGuardConfigFromEnv(env, opts.cwd));
|
|
202
|
-
const extras: LeafExtras = () => ({
|
|
203
|
-
extensions: [guard],
|
|
204
|
-
appendSystemPrompt: [agent.systemPrompt],
|
|
205
|
-
bashEnv: { allow: LEAF_BASH_ENV_ALLOW },
|
|
206
|
-
...(thinking !== null
|
|
207
|
-
? { thinkingLevel: thinking as NonNullable<SessionExtras["thinkingLevel"]> }
|
|
208
|
-
: {}),
|
|
209
|
-
});
|
|
210
|
-
// The transcript: opened before the session, appended event by event,
|
|
211
|
-
// abandoned (and its name dropped from the outcome) at the first write
|
|
212
|
-
// that fails.
|
|
213
|
-
const opened =
|
|
214
|
-
opts.transcriptDir !== undefined ? openTranscript(opts.transcriptDir, spec.id) : null;
|
|
215
|
-
const sessionSpec: SessionSpec = {
|
|
216
|
-
cwd: opts.cwd,
|
|
217
|
-
env,
|
|
218
|
-
model,
|
|
219
|
-
...(agent.tools.length > 0 ? { tools: agent.tools } : {}),
|
|
220
|
-
name: spec.id,
|
|
221
|
-
// The case's session JSONL beside its transcript, so the case
|
|
222
|
-
// opens in pi: `qcr open <run dir> --lens <case>`.
|
|
223
|
-
...(opts.transcriptDir !== undefined && opened !== null
|
|
224
|
-
? { sessionDir: caseSessionDir(opts.transcriptDir, opened.rel) }
|
|
225
|
-
: {}),
|
|
226
|
-
eventsFile: join(tmp, "events.jsonl"),
|
|
227
|
-
stderrFile: join(tmp, "stderr.txt"),
|
|
228
|
-
limits: { timeoutMs: opts.timeoutMs ?? LEAF_TURN_TIMEOUT_MS },
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
let transcript: string | undefined = opened?.rel;
|
|
232
|
-
let transcriptAt: string | null = opened?.at ?? null;
|
|
233
|
-
const keep = (event: unknown): void => {
|
|
234
|
-
if (transcriptAt === null) return;
|
|
235
|
-
try {
|
|
236
|
-
appendFileSync(transcriptAt, JSON.stringify(event) + "\n");
|
|
237
|
-
} catch {
|
|
238
|
-
transcriptAt = null;
|
|
239
|
-
transcript = undefined;
|
|
240
|
-
}
|
|
241
|
-
};
|
|
242
|
-
const readout = caseReadout(opts.cwd, onCost);
|
|
243
|
-
const finish = <T extends CaseOutcome>(o: T): T =>
|
|
244
|
-
transcript === undefined ? o : { ...o, transcript };
|
|
245
|
-
|
|
246
|
-
try {
|
|
247
|
-
const runtime = await opts.runtime(extras);
|
|
248
|
-
session = await runtime.create(sessionSpec);
|
|
249
|
-
} catch (err) {
|
|
250
|
-
const reason = err instanceof Error ? err.message : String(err);
|
|
251
|
-
// A refused login already says what to do; anything else is the host's.
|
|
252
|
-
return {
|
|
253
|
-
ok: false,
|
|
254
|
-
reason: err instanceof AuthError ? reason : `could not start the session: ${reason}`,
|
|
255
|
-
cost_usd: 0,
|
|
256
|
-
};
|
|
257
|
-
}
|
|
258
|
-
session.subscribe((event) => {
|
|
259
|
-
keep(event);
|
|
260
|
-
readout.feed(event);
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
let stopped = false;
|
|
264
|
-
let hardStop: ((o: CaseOutcome) => void) | undefined;
|
|
265
|
-
const gone = new Promise<CaseOutcome>((resolve) => {
|
|
266
|
-
hardStop = resolve;
|
|
267
|
-
});
|
|
268
|
-
const onAbort = (): void => {
|
|
269
|
-
stopped = true;
|
|
270
|
-
const live = session;
|
|
271
|
-
void live?.abort().catch(() => {});
|
|
272
|
-
// The stop grace, then the session is released; a turn still not
|
|
273
|
-
// settled a grace later is given up on, so the case resolves.
|
|
274
|
-
setTimeout(() => void live?.dispose().catch(() => {}), CASE_STOP_GRACE_MS).unref();
|
|
275
|
-
setTimeout(
|
|
276
|
-
() => hardStop?.({ ok: false, reason: "stopped", cost_usd: readout.spent() }),
|
|
277
|
-
2 * CASE_STOP_GRACE_MS,
|
|
278
|
-
).unref();
|
|
279
|
-
};
|
|
280
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
281
|
-
if (signal.aborted) {
|
|
282
|
-
onAbort();
|
|
283
|
-
// Cancelled while the session was being created: no turn starts.
|
|
284
|
-
return finish({ ok: false, reason: "stopped", cost_usd: 0 });
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
const turn = (async (): Promise<CaseOutcome> => {
|
|
288
|
-
let result: TurnResult;
|
|
289
|
-
try {
|
|
290
|
-
result = await session.prompt(`Task: ${spec.prompt}`);
|
|
291
|
-
} catch (err) {
|
|
292
|
-
return {
|
|
293
|
-
ok: false,
|
|
294
|
-
reason: `the session failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
295
|
-
cost_usd: readout.spent(),
|
|
296
|
-
};
|
|
297
|
-
}
|
|
298
|
-
// What a case that ended without an answer had spent: a failure costs
|
|
299
|
-
// money too, and a caller holding a budget must count it.
|
|
300
|
-
const cost_usd = readout.spent();
|
|
301
|
-
if (stopped) return { ok: false, reason: "stopped", cost_usd };
|
|
302
|
-
if (result.timedOut)
|
|
303
|
-
return { ok: false, reason: "the case ran past its wall clock", cost_usd };
|
|
304
|
-
// The model's own reason leads: a provider error also ends the turn non-zero.
|
|
305
|
-
if (readout.failure !== null) return { ok: false, reason: readout.failure, cost_usd };
|
|
306
|
-
if (result.code !== 0) {
|
|
307
|
-
let tail = "";
|
|
308
|
-
try {
|
|
309
|
-
tail = stderrExcerpt(readFileSync(sessionSpec.stderrFile, "utf8"));
|
|
310
|
-
} catch {
|
|
311
|
-
// No stderr to quote.
|
|
312
|
-
}
|
|
313
|
-
return {
|
|
314
|
-
ok: false,
|
|
315
|
-
reason: `the session ended without a result${tail ? `: ${tail}` : ""}`,
|
|
316
|
-
cost_usd,
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
if (readout.final.trim() === "")
|
|
320
|
-
return { ok: false, reason: "the subagent gave no answer", cost_usd };
|
|
321
|
-
return { ok: true, output: readout.final, cost_usd, reads: readout.reads };
|
|
322
|
-
})();
|
|
323
|
-
try {
|
|
324
|
-
return finish(await Promise.race([turn, gone]));
|
|
325
|
-
} finally {
|
|
326
|
-
signal.removeEventListener("abort", onAbort);
|
|
327
|
-
}
|
|
328
|
-
} finally {
|
|
329
|
-
try {
|
|
330
|
-
await session?.dispose();
|
|
331
|
-
} catch {
|
|
332
|
-
// Already released.
|
|
333
|
-
}
|
|
334
|
-
rmSync(tmp, { recursive: true, force: true });
|
|
335
|
-
}
|
|
336
|
-
},
|
|
337
|
-
};
|
|
31
|
+
import type { CaseRunner } from "@quickchatai/pi-quickreview/src/scan/scanports.js";
|
|
32
|
+
import {
|
|
33
|
+
LEAF_TURN_TIMEOUT_MS,
|
|
34
|
+
sdkCaseRunner as sdkCaseRunnerOver,
|
|
35
|
+
type SdkCaseRunnerOptions,
|
|
36
|
+
} from "@quickchatai/pi-quickreview/src/scan/scanleaves.js";
|
|
37
|
+
|
|
38
|
+
export {
|
|
39
|
+
LEAF_TURN_TIMEOUT_MS,
|
|
40
|
+
caseSessionDir,
|
|
41
|
+
type LeafExtras,
|
|
42
|
+
type SdkCaseRunnerOptions,
|
|
43
|
+
} from "@quickchatai/pi-quickreview/src/scan/scanleaves.js";
|
|
44
|
+
|
|
45
|
+
/** `sdkCaseRunner` (scanleaves.ts) in this process: the leaf env filtered
|
|
46
|
+
* from this process's env and a read's `~` taken as this process's home,
|
|
47
|
+
* unless told otherwise. */
|
|
48
|
+
export function sdkCaseRunner(
|
|
49
|
+
opts: Omit<SdkCaseRunnerOptions, "env" | "home"> & { env?: NodeJS.ProcessEnv; home?: string },
|
|
50
|
+
): CaseRunner {
|
|
51
|
+
return sdkCaseRunnerOver({ ...opts, env: opts.env ?? process.env, home: opts.home ?? homedir() });
|
|
338
52
|
}
|
|
339
53
|
|
|
340
54
|
// ---- the scan driver's host ----------------------------------------------------
|
|
@@ -380,6 +94,7 @@ export function scanSdkCases(o: ScanSdkCasesOptions): CaseRunner {
|
|
|
380
94
|
throw new Error(`the SDK host's agentDir must be absolute: ${o.agentDir}`);
|
|
381
95
|
return sdkCaseRunner({
|
|
382
96
|
agentsDir: o.agentsDir,
|
|
97
|
+
home: homedir(),
|
|
383
98
|
cwd: o.cwd,
|
|
384
99
|
...(o.transcriptDir !== undefined ? { transcriptDir: o.transcriptDir } : {}),
|
|
385
100
|
env: o.env,
|