@tiangong-ai/cli 0.0.43 → 0.0.45

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.
Files changed (51) hide show
  1. package/AGENTS.md +3 -1
  2. package/README.md +76 -8
  3. package/dist/research/orchestration.js +192 -8
  4. package/dist/research/orchestration.js.map +1 -1
  5. package/dist/research/setup-command.js +13 -1
  6. package/dist/research/setup-command.js.map +1 -1
  7. package/dist/research/workspace/audit-bundle.js +1 -0
  8. package/dist/research/workspace/audit-bundle.js.map +1 -1
  9. package/dist/research/workspace/executor.d.ts +9 -0
  10. package/dist/research/workspace/executor.js +161 -4
  11. package/dist/research/workspace/executor.js.map +1 -1
  12. package/dist/research/workspace/inference.js +6 -2
  13. package/dist/research/workspace/inference.js.map +1 -1
  14. package/dist/research/workspace/preflight.d.ts +2 -0
  15. package/dist/research/workspace/preflight.js +11 -2
  16. package/dist/research/workspace/preflight.js.map +1 -1
  17. package/dist/research/workspace/projects.js +11 -1
  18. package/dist/research/workspace/projects.js.map +1 -1
  19. package/dist/research/workspace/publication-workflow.js +1 -1
  20. package/dist/research/workspace/publication-workflow.js.map +1 -1
  21. package/dist/research/workspace/review-executor.d.ts +50 -0
  22. package/dist/research/workspace/review-executor.js +854 -0
  23. package/dist/research/workspace/review-executor.js.map +1 -0
  24. package/dist/research/workspace/runtime.js +63 -15
  25. package/dist/research/workspace/runtime.js.map +1 -1
  26. package/dist/research/workspace/scientific-design.d.ts +5 -5
  27. package/dist/research/workspace/scientific-design.js +92 -16
  28. package/dist/research/workspace/scientific-design.js.map +1 -1
  29. package/dist/research/workspace/scientific-objects.d.ts +53 -0
  30. package/dist/research/workspace/scientific-objects.js +395 -0
  31. package/dist/research/workspace/scientific-objects.js.map +1 -0
  32. package/dist/research/workspace/scientific-review.d.ts +4 -0
  33. package/dist/research/workspace/scientific-review.js +87 -25
  34. package/dist/research/workspace/scientific-review.js.map +1 -1
  35. package/dist/research/workspace/setup-catalog.js +22 -2
  36. package/dist/research/workspace/setup-catalog.js.map +1 -1
  37. package/dist/research/workspace/setup-declarative.d.ts +4 -0
  38. package/dist/research/workspace/setup-declarative.js +16 -1
  39. package/dist/research/workspace/setup-declarative.js.map +1 -1
  40. package/dist/research/workspace/setup-wizard.js +46 -3
  41. package/dist/research/workspace/setup-wizard.js.map +1 -1
  42. package/dist/research/workspace/setup.d.ts +3 -1
  43. package/dist/research/workspace/setup.js +76 -18
  44. package/dist/research/workspace/setup.js.map +1 -1
  45. package/dist/research/workspace/storage.d.ts +1 -0
  46. package/dist/research/workspace/storage.js +7 -0
  47. package/dist/research/workspace/storage.js.map +1 -1
  48. package/dist/research/workspace/types.d.ts +43 -1
  49. package/dist/research/workspace/workspace.js +62 -5
  50. package/dist/research/workspace/workspace.js.map +1 -1
  51. package/package.json +1 -1
package/AGENTS.md CHANGED
@@ -18,7 +18,7 @@ checkPaths:
18
18
  - docs/agents/**
19
19
  - src/**
20
20
  lastReviewedAt: 2026-08-19
21
- lastReviewedCommit: cd92a737acbdfd05a36c0d0d5b14c7e531ae6ee7
21
+ lastReviewedCommit: 6adf1ad32bdfaa74bc1449bc46c2e6591bae67a1
22
22
  ---
23
23
 
24
24
  # Tiangong AI CLI Contract
@@ -51,7 +51,9 @@ This repository owns the Tiangong AI command-line interface.
51
51
  - `tiangong-ai research setup`
52
52
  - `tiangong-ai research policy`
53
53
  - `tiangong-ai research publication`
54
+ - `tiangong-ai research scientific`
54
55
  - `tiangong-ai research workspace`
56
+ - `tiangong-ai research reviewer`
55
57
  - `tiangong-ai research capability`
56
58
  - `tiangong-ai research project`
57
59
  - `tiangong-ai research status`
package/README.md CHANGED
@@ -376,7 +376,7 @@ binds the manifest and every document by SHA-256; edits, manifest tampering, or
376
376
  expiry block preflight and all later stages until the Policy is reviewed and
377
377
  approved again.
378
378
 
379
- Before search, the current native Codex or Claude host must author a
379
+ Before search, the current native Codex, Claude, WorkBuddy, or CodeBuddy host must author a
380
380
  project-specific scientific design. The CLI owns the closed schema and rejects
381
381
  designs that confuse model-to-model disagreement with observed truth, inflate
382
382
  independent sample size through resampling, omit quantity/threshold semantics,
@@ -387,10 +387,39 @@ the design or launch a nested producer.
387
387
  Hash binding alone does not make a model executable. Each model declares raw
388
388
  implementation bytes, a retrievable safe locator and entrypoint, exact
389
389
  environment-lock bytes, implementation/environment status, and a freeze gate.
390
+ Before authoring a design with frozen model objects, register each external
391
+ regular non-symlink file through the public content-addressed intake:
392
+
393
+ ```bash
394
+ tiangong-ai research scientific object register \
395
+ --kind model-implementation \
396
+ --path /absolute/path/to/model.py \
397
+ --media-type text/x-python \
398
+ --workspace /absolute/path/to/workspace --json
399
+ tiangong-ai research scientific object register \
400
+ --kind environment-lock \
401
+ --path /absolute/path/to/requirements.lock \
402
+ --media-type text/plain \
403
+ --workspace /absolute/path/to/workspace --json
404
+ ```
405
+
406
+ Use the returned `sha256` and `objectLocator` verbatim in the design. Registration
407
+ is workspace-scoped because it must happen before project admission. It hashes
408
+ raw bytes, atomically stores an immutable `lineage/objects/<sha256>/blob`, and
409
+ records only deterministic non-path metadata. It never accepts a source inside
410
+ `.tiangong-research`, a symlink as the selected file, an unsupported/binary
411
+ media type, or invalid UTF-8. Canonical parent-directory aliases are resolved
412
+ before containment checks, including macOS `/var` aliases and Windows
413
+ cross-volume paths. Re-registration is idempotent, and `research scientific object inspect`
414
+ revalidates the record and blob before returning it. Do not hand-copy files into
415
+ the control directory.
416
+
390
417
  Source-derived uncertainty states also declare whether their values are frozen
391
418
  or pending, and every joint state maps exact parameter-state IDs. Pending model,
392
419
  environment, or uncertainty objects are allowed only when a planned Policy rule
393
- owns the same due gate. They are exposed in every earlier review packet as
420
+ owns the same due gate. Pending implementations use `null` for implementation
421
+ SHA-256, locator, and entrypoint; pending environments use `null` for lock
422
+ SHA-256 and locator. They are exposed in every earlier review packet as
394
423
  `futureGateObligations` and become blocking mechanical errors at that gate.
395
424
  Freezing them requires a new authoritative generation; it never upgrades the
396
425
  old object in place.
@@ -454,7 +483,10 @@ Policy, design, and fresh native producer session; it cannot inherit scientific
454
483
  approval from a superseded generation.
455
484
 
456
485
  Review packet `stageInputs` identify promoted portable objects by purpose,
457
- owner, source locator, and SHA-256 over raw file bytes. `packetSha256` is the
486
+ owner, safe source locator, media type, object kind, registration-record hash,
487
+ and SHA-256 over raw file bytes. Registered model code and environment locks are
488
+ copied as exact project-local `blob` bytes; they are never parsed as JSON merely
489
+ because the packet is JSON. `packetSha256` is the
458
490
  logical packet identity that excludes its own identity field; the portable
459
491
  audit manifest separately records the raw stored packet-file digest. This keeps
460
492
  packet identity and byte-level transfer verification explicit rather than
@@ -602,13 +634,19 @@ The workspace stores its current protocol state under `.tiangong-research/`.
602
634
  Each project follows the evidence-first sequence: broad discovery, strict
603
635
  admission, acquisition audit, immutable evidence freeze, analysis, synthesis,
604
636
  independent review, and mechanical closure. Discover, acquire, analyze, and
605
- synthesize run in the current interactive Codex app/session or Claude Code
606
- session. The CLI never launches a nested producer process. Independent review
637
+ synthesize run in the current interactive Codex, Claude Code, WorkBuddy, or
638
+ CodeBuddy session. The CLI never launches a nested producer process. Independent review
607
639
  runs through the other configured agent family's CLI, and execution is blocked
608
640
  when the two roles use the same family.
609
641
 
610
- Independent reviewer execution requires `/usr/bin/sandbox-exec` on macOS or
611
- Bubblewrap (`bwrap`) on Linux. Windows can inspect and configure workspaces but
642
+ Independent reviewer execution always requires `/usr/bin/sandbox-exec` on macOS
643
+ or Bubblewrap (`bwrap`) on Linux. `reviewerExecution.transport=native-direct`
644
+ creates that capsule in the current process. `sandbox-bridge` sends one
645
+ hash-bound request to an owner-started, exact-version sidecar outside an IDE
646
+ sandbox; the sidecar creates the same capsule and returns an Ed25519-signed
647
+ attestation bound to workspace/config/runtime/capsule/request/result/model and
648
+ policy hashes. The two transports are explicit and never fall back to each
649
+ other. Windows can inspect and configure workspaces but
612
650
  does not launch reviewer packages; smoke-test setup reports a non-blocking
613
651
  warning there, while production readiness fails closed. The current native producer remains governed
614
652
  by its host application's own permissions; the CLI supplies a hash-bound packet
@@ -628,7 +666,7 @@ tiangong-ai research run --workspace /absolute/path/to/workspace \
628
666
  --project gpu-resource-impact --progress-jsonl
629
667
  # When stopReason is native-stage-required, perform the returned stage here:
630
668
  tiangong-ai research project stage prepare gpu-resource-impact \
631
- --stage discover --host-agent codex \
669
+ --stage discover --host-agent workbuddy \
632
670
  --workspace /absolute/path/to/workspace --json
633
671
  tiangong-ai research project stage submit gpu-resource-impact \
634
672
  --session SESSION_ID --output /absolute/path/to/discover.json \
@@ -637,6 +675,36 @@ tiangong-ai research project stage submit gpu-resource-impact \
637
675
  tiangong-ai research status --workspace /absolute/path/to/workspace --json
638
676
  ```
639
677
 
678
+ For WorkBuddy/CodeBuddy, keep Default Permission and start the sidecar from a
679
+ separate native terminal with a private non-symlink state directory outside the
680
+ workspace:
681
+
682
+ ```bash
683
+ tiangong-ai research reviewer serve \
684
+ --workspace /absolute/path/to/workspace \
685
+ --state-dir /absolute/private/reviewer-sidecar --json
686
+
687
+ # From the sandboxed IDE:
688
+ tiangong-ai research reviewer status --workspace /absolute/path/to/workspace --json
689
+ tiangong-ai research reviewer doctor --confirm-agent-smoke-cost \
690
+ --workspace /absolute/path/to/workspace --json
691
+ ```
692
+
693
+ Sidecar readiness includes real filesystem negative probes and a fixed
694
+ `execute|fingerprint|status` protocol. It has no arbitrary-command endpoint;
695
+ reviewer shell, browser, web, undeclared MCP, and Skill tools remain disabled.
696
+ Do not use Full Access, sandbox-disable flags, unsandboxed-command exceptions,
697
+ or silent transport fallback.
698
+
699
+ WorkBuddy/CodeBuddy capsule teardown never requests recursive bulk deletion
700
+ inside the outer IDE. Native stages remove only the single active-session
701
+ binding, while completed, aborted, handed-off, and reviewer/work-package
702
+ capsules are retained. The journal records
703
+ `capsuleDisposition=retained-outer-sandbox` plus a non-sensitive capsule ID.
704
+ Native Codex/Claude hosts keep the existing automatic capsule deletion behavior.
705
+ No path silently falls back to Full Access, and retained capsules are never
706
+ reported as active sessions.
707
+
640
708
  The discover packet derives a bounded multi-channel plan from reviewed evidence
641
709
  requirements. Required channels run first; exact repeated requests reuse the
642
710
  project cache without another provider call but still consume a bounded context
@@ -6,6 +6,7 @@ import { parseStrictArgs, strictBoolean, strictString } from "../strict-args.js"
6
6
  import { researchSetupHelp, runResearchSetupCommand } from "./setup-command.js";
7
7
  import { loadCapabilityDeclarations, lockCapabilities, verifyCapabilities, } from "./workspace/capabilities.js";
8
8
  import { inspectResearchContext } from "./workspace/context.js";
9
+ import { packageVersion } from "./workspace/constants.js";
9
10
  import { setCapabilityCredentialFromEnvironment } from "./workspace/credentials.js";
10
11
  import { configureExternalSkillProfile, doctorExternalCapabilities, EXTERNAL_SKILL_CONTEXT_PROFILE, EXTERNAL_SKILL_MEDIA_PROFILE, EXTERNAL_SKILL_PROFILE, importExternalCapability, inspectExternalSkillCatalog, } from "./workspace/external-skills.js";
11
12
  import { appendJournalEvent } from "./workspace/journal.js";
@@ -20,6 +21,7 @@ import { recordDiscoveryAssessmentBatch } from "./workspace/discovery.js";
20
21
  import { bindEvidenceDownload } from "./workspace/downloads.js";
21
22
  import { registerNativeDiscoveryCandidate } from "./workspace/evidence-ledger.js";
22
23
  import { recordNativeResearchActivity } from "./workspace/native-activity.js";
24
+ import { inspectReviewerBridgeStatus, startReviewerBridgeSidecar, } from "./workspace/review-executor.js";
23
25
  import { readAndVerifyProjectInputPlan } from "./workspace/input-plan.js";
24
26
  import { loadCurrentClaimEvidenceGraph, loadCurrentInferenceSnapshot, } from "./workspace/inference.js";
25
27
  import { addProjectInput, createProjectAddendum, initializeProject, forkProject, listProjects, loadProject, nextReadyPackage, normalizeEvidenceRequirements, refreshProject, retryProjectPackage, setProjectDisposition, } from "./workspace/projects.js";
@@ -30,9 +32,10 @@ import { resolveInstalledResearchPolicySource, runInteractiveResearchPolicyWizar
30
32
  import { abortNativeResearchStage, inspectNativeResearchStage, prepareNativeResearchStage, requestResearchHandoff, resolveResearchHandoff, runResearchWorkspace, submitNativeResearchStage, } from "./workspace/runtime.js";
31
33
  import { schemaForStage } from "./workspace/schemas.js";
32
34
  import { readAndVerifyScientificDesign, scientificDesignSchema, } from "./workspace/scientific-design.js";
35
+ import { inspectScientificObject, parseScientificObjectKind, registerScientificObject, } from "./workspace/scientific-objects.js";
33
36
  import { inspectScientificReviewStatus, prepareScientificReview, scientificGateAssessmentSchema, scientificReviewSchema, submitScientificReview, } from "./workspace/scientific-review.js";
34
37
  import { isObject, pathExists, sha256Text, workspacePaths } from "./workspace/storage.js";
35
- import { doctorResearchWorkspace, initializeResearchWorkspace, requireResearchWorkspace, withWorkspaceLock, } from "./workspace/workspace.js";
38
+ import { doctorResearchWorkspace, initializeResearchWorkspace, loadWorkspaceConfig, requireResearchWorkspace, withWorkspaceLock, } from "./workspace/workspace.js";
36
39
  const COMMON_OPTIONS = { help: "boolean", json: "boolean" };
37
40
  const WORKSPACE_OPTIONS = { ...COMMON_OPTIONS, workspace: "string" };
38
41
  export async function runResearchOrchestrationCommand(subcommand, argv, io) {
@@ -42,12 +45,16 @@ export async function runResearchOrchestrationCommand(subcommand, argv, io) {
42
45
  return runResearchSetupCommand(argv, io);
43
46
  if (subcommand === "workspace")
44
47
  return runWorkspace(argv, io);
48
+ if (subcommand === "reviewer")
49
+ return runReviewer(argv, io);
45
50
  if (subcommand === "capability")
46
51
  return runCapability(argv, io);
47
52
  if (subcommand === "policy")
48
53
  return runPolicy(argv, io);
49
54
  if (subcommand === "publication")
50
55
  return runPublication(argv, io);
56
+ if (subcommand === "scientific")
57
+ return runScientific(argv, io);
51
58
  if (subcommand === "project")
52
59
  return runProject(argv, io);
53
60
  if (subcommand === "schema")
@@ -65,6 +72,9 @@ export function researchOrchestrationHelp() {
65
72
  tiangong-ai research context inspect [--path <absolute-path>] [--json]
66
73
  tiangong-ai research workspace init <absolute-path> [--name <name>] [--mode smoke-test|production-research] [--json]
67
74
  tiangong-ai research workspace doctor [--workspace <absolute-path>] [--agent-smoke] [--capability-smoke] [--json]
75
+ tiangong-ai research reviewer serve --state-dir <absolute-private-directory> [--workspace <absolute-path>] [--json]
76
+ tiangong-ai research reviewer status [--workspace <absolute-path>] [--json]
77
+ tiangong-ai research reviewer doctor --confirm-agent-smoke-cost [--workspace <absolute-path>] [--json]
68
78
  tiangong-ai research capability catalog [--path <absolute-path>] [--workspace <absolute-path>] [--skill-root <absolute-path>] [--json]
69
79
  tiangong-ai research capability configure [--profile ${EXTERNAL_SKILL_PROFILE}|${EXTERNAL_SKILL_CONTEXT_PROFILE}|${EXTERNAL_SKILL_MEDIA_PROFILE}] [--skill-root <absolute-path>] [--workspace <absolute-path>] [--json]
70
80
  tiangong-ai research capability import --definition <absolute-json> [--workspace <absolute-path>] [--json]
@@ -79,11 +89,13 @@ export function researchOrchestrationHelp() {
79
89
  tiangong-ai research policy validate <project-id> [--workspace <path>] [--json]
80
90
  tiangong-ai research policy approve <project-id> --confirm [--acknowledge-defaults] [--workspace <path>] [--json]
81
91
  tiangong-ai research policy resolve <project-id> [--workspace <path>] [--json]
82
- tiangong-ai research publication freeze <project-id> --manuscript <absolute-file> --assessment <absolute-json> --submission <absolute-json> --producer-agent codex|claude --producer-session <opaque-id> [--supplements <absolute-json-array>] [--workspace <path>] [--json]
92
+ tiangong-ai research publication freeze <project-id> --manuscript <absolute-file> --assessment <absolute-json> --submission <absolute-json> --producer-agent codex|claude|workbuddy|codebuddy --producer-session <opaque-id> [--supplements <absolute-json-array>] [--workspace <path>] [--json]
83
93
  tiangong-ai research publication review prepare <project-id> --role evidence|methods-reproducibility|domain-novelty|journal-editor --reviewer-agent codex|claude --reviewer-session <opaque-id> [--workspace <path>] [--json]
84
94
  tiangong-ai research publication review submit <project-id> --role evidence|methods-reproducibility|domain-novelty|journal-editor --review <absolute-json> [--workspace <path>] [--json]
85
95
  tiangong-ai research publication status <project-id> [--workspace <path>] [--json]
86
96
  tiangong-ai research publication close <project-id> [--workspace <path>] [--json]
97
+ tiangong-ai research scientific object register --kind model-implementation|environment-lock --path <absolute-file> [--media-type <type>] [--workspace <path>] [--json]
98
+ tiangong-ai research scientific object inspect --kind model-implementation|environment-lock --locator <control-relative-locator> [--workspace <path>] [--json]
87
99
  tiangong-ai research project init <project-id> --question <question> [--goal evidence-report|top-journal] [--design <absolute-json> --design-producer-agent codex|claude --design-producer-session <opaque-id>] [--requirements <absolute-json>] [--input-plan <absolute-json>] [--confirm-budget] [--workspace <path>] [--json]
88
100
  tiangong-ai research project preflight --question <question> [--goal evidence-report|top-journal] [--policy-project <project-id> --design <absolute-json>] [--requirements <absolute-json>] [--input-plan <absolute-json>] [--workspace <path>] [--json]
89
101
  tiangong-ai research project input add <project-id> --path <absolute-file> [--role primary|reference|replication] [--trust-status verified-owner-input|unverified-owner-input|reference-only|replication-candidate] [--independently-reproduced] [--workspace <path>] [--json]
@@ -100,7 +112,7 @@ export function researchOrchestrationHelp() {
100
112
  tiangong-ai research project scientific status <project-id> [--workspace <path>] [--json]
101
113
  tiangong-ai research project audit export <project-id> --output <absolute-new-directory> [--workspace <path>] [--json]
102
114
  tiangong-ai research project audit verify --bundle <absolute-directory> [--json]
103
- tiangong-ai research project stage prepare <project-id> --stage discover|acquire|analyze|synthesize --host-agent codex|claude [--workspace <path>] [--json]
115
+ tiangong-ai research project stage prepare <project-id> --stage discover|acquire|analyze|synthesize --host-agent codex|claude|workbuddy|codebuddy [--workspace <path>] [--json]
104
116
  tiangong-ai research project stage submit <project-id> --session <id> --output <absolute-json> [--confirm-model <id>] [--workspace <path>] [--json]
105
117
  tiangong-ai research project stage abort <project-id> --session <id> [--workspace <path>] [--json]
106
118
  tiangong-ai research project evidence fetch <project-id> --request <absolute-json> [--workspace <path>] [--json]
@@ -120,6 +132,158 @@ export function researchOrchestrationHelp() {
120
132
  ${researchSetupHelp()}
121
133
  `;
122
134
  }
135
+ async function runScientific(argv, io) {
136
+ const [action, ...rest] = argv;
137
+ if (!action || action === "--help" || action === "-h")
138
+ return writeHelp(io);
139
+ if (action !== "object")
140
+ throw unknownAction("research scientific", action);
141
+ const [objectAction, ...objectRest] = rest;
142
+ if (objectAction === "register") {
143
+ const args = parseStrictArgs(objectRest, { ...WORKSPACE_OPTIONS, kind: "string", path: "string", "media-type": "string" }, "research scientific object register");
144
+ if (strictBoolean(args, "help"))
145
+ return writeHelp(io);
146
+ rejectPositionals(args.positionals, "research scientific object register");
147
+ const sourcePath = strictString(args, "path");
148
+ if (!sourcePath) {
149
+ throw new CliError("Scientific object register requires --kind and --path.", {
150
+ code: "RESEARCH_SCIENTIFIC_OBJECT_INVALID",
151
+ exitCode: 2,
152
+ });
153
+ }
154
+ const root = await workspaceFromArgs(args);
155
+ const objectKind = parseScientificObjectKind(strictString(args, "kind"));
156
+ const record = await withWorkspaceLock(root, "research.scientific-object.register", async () => {
157
+ const value = await registerScientificObject({
158
+ root,
159
+ objectKind,
160
+ path: sourcePath,
161
+ ...(strictString(args, "media-type")
162
+ ? { mediaType: strictString(args, "media-type") }
163
+ : {}),
164
+ });
165
+ await appendJournalEvent(workspacePaths(root).journal, "scientific-object.registered", "workspace", {
166
+ objectKind: value.objectKind,
167
+ sha256: value.sha256,
168
+ bytes: value.bytes,
169
+ mediaType: value.mediaType,
170
+ objectLocator: value.objectLocator,
171
+ recordLocator: value.recordLocator,
172
+ recordSha256: value.recordSha256,
173
+ });
174
+ return value;
175
+ });
176
+ writeJson(io, record, args);
177
+ return 0;
178
+ }
179
+ if (objectAction === "inspect") {
180
+ const args = parseStrictArgs(objectRest, { ...WORKSPACE_OPTIONS, kind: "string", locator: "string" }, "research scientific object inspect");
181
+ if (strictBoolean(args, "help"))
182
+ return writeHelp(io);
183
+ rejectPositionals(args.positionals, "research scientific object inspect");
184
+ const objectLocator = strictString(args, "locator");
185
+ if (!objectLocator) {
186
+ throw new CliError("Scientific object inspect requires --kind and --locator.", {
187
+ code: "RESEARCH_SCIENTIFIC_OBJECT_INVALID",
188
+ exitCode: 2,
189
+ });
190
+ }
191
+ const root = await workspaceFromArgs(args);
192
+ writeJson(io, await inspectScientificObject({
193
+ root,
194
+ objectKind: parseScientificObjectKind(strictString(args, "kind")),
195
+ objectLocator,
196
+ }), args);
197
+ return 0;
198
+ }
199
+ throw unknownAction("research scientific object", objectAction ?? "");
200
+ }
201
+ async function runReviewer(argv, io) {
202
+ const [action, ...rest] = argv;
203
+ if (!action || action === "--help" || action === "-h")
204
+ return writeHelp(io);
205
+ if (action === "status") {
206
+ const args = parseStrictArgs(rest, WORKSPACE_OPTIONS, "research reviewer status");
207
+ if (strictBoolean(args, "help"))
208
+ return writeHelp(io);
209
+ if (args.positionals.length)
210
+ throw unknownAction("research reviewer status", args.positionals[0]);
211
+ const root = await workspaceFromArgs(args);
212
+ writeJson(io, await inspectReviewerBridgeStatus(root), args);
213
+ return 0;
214
+ }
215
+ if (action === "doctor") {
216
+ const args = parseStrictArgs(rest, { ...WORKSPACE_OPTIONS, "confirm-agent-smoke-cost": "boolean" }, "research reviewer doctor");
217
+ if (strictBoolean(args, "help"))
218
+ return writeHelp(io);
219
+ if (args.positionals.length)
220
+ throw unknownAction("research reviewer doctor", args.positionals[0]);
221
+ if (!strictBoolean(args, "confirm-agent-smoke-cost")) {
222
+ throw new CliError("Reviewer doctor requires explicit model-cost confirmation.", {
223
+ code: "RESEARCH_REVIEW_BRIDGE_CONFIRMATION_REQUIRED",
224
+ exitCode: 3,
225
+ details: {
226
+ minimumAction: "Rerun with --confirm-agent-smoke-cost after reviewing reviewer provider quota and cost.",
227
+ },
228
+ });
229
+ }
230
+ const root = await workspaceFromArgs(args);
231
+ const result = await doctorResearchWorkspace(root, {
232
+ agentSmoke: true,
233
+ environment: io.env,
234
+ });
235
+ writeJson(io, result, args);
236
+ return result.status === "ready" ? 0 : 3;
237
+ }
238
+ if (action === "serve") {
239
+ const args = parseStrictArgs(rest, { ...WORKSPACE_OPTIONS, "state-dir": "string" }, "research reviewer serve");
240
+ if (strictBoolean(args, "help"))
241
+ return writeHelp(io);
242
+ if (args.positionals.length)
243
+ throw unknownAction("research reviewer serve", args.positionals[0]);
244
+ const stateDirectory = strictString(args, "state-dir");
245
+ if (!stateDirectory ||
246
+ !isAbsolute(stateDirectory) ||
247
+ resolve(stateDirectory) !== stateDirectory) {
248
+ throw new CliError("Reviewer sidecar --state-dir must be an explicit absolute directory.", {
249
+ code: "RESEARCH_REVIEW_BRIDGE_STATE_INVALID",
250
+ exitCode: 2,
251
+ });
252
+ }
253
+ const root = await workspaceFromArgs(args);
254
+ const sidecar = await startReviewerBridgeSidecar({
255
+ root,
256
+ stateDirectory,
257
+ environment: io.env,
258
+ });
259
+ writeJson(io, {
260
+ status: "ready",
261
+ workspaceId: sidecar.workspaceId,
262
+ packageVersion: packageVersion(),
263
+ keyFingerprint: sidecar.keyFingerprint,
264
+ supportedActions: ["execute", "fingerprint", "status"],
265
+ }, args);
266
+ try {
267
+ await waitForReviewerSidecarTermination();
268
+ }
269
+ finally {
270
+ await sidecar.close();
271
+ }
272
+ return 0;
273
+ }
274
+ throw unknownAction("research reviewer", action);
275
+ }
276
+ async function waitForReviewerSidecarTermination() {
277
+ await new Promise((resolvePromise) => {
278
+ const stop = () => {
279
+ process.off("SIGINT", stop);
280
+ process.off("SIGTERM", stop);
281
+ resolvePromise();
282
+ };
283
+ process.once("SIGINT", stop);
284
+ process.once("SIGTERM", stop);
285
+ });
286
+ }
123
287
  async function runPublication(argv, io) {
124
288
  const [action, ...rest] = argv;
125
289
  if (!action || action === "--help" || action === "-h")
@@ -1314,6 +1478,7 @@ async function runStatus(argv, io) {
1314
1478
  return writeHelp(io);
1315
1479
  rejectPositionals(args.positionals, "research status");
1316
1480
  const root = await workspaceFromArgs(args);
1481
+ const config = await loadWorkspaceConfig(root);
1317
1482
  const selectedProject = strictString(args, "project");
1318
1483
  const workspaceProjects = await listProjects(root);
1319
1484
  const allProjects = selectedProject
@@ -1329,6 +1494,20 @@ async function runStatus(argv, io) {
1329
1494
  project.status !== "abandoned");
1330
1495
  const result = {
1331
1496
  workspace: root,
1497
+ execution: {
1498
+ producer: {
1499
+ host: config.producer.agent,
1500
+ model: config.producer.model,
1501
+ mode: config.producer.executionMode,
1502
+ },
1503
+ reviewer: {
1504
+ agent: config.reviewer.agent,
1505
+ model: config.reviewer.model,
1506
+ mode: config.reviewer.executionMode,
1507
+ transport: config.reviewerExecution.transport,
1508
+ isolationProvider: config.reviewerExecution.isolationProvider,
1509
+ },
1510
+ },
1332
1511
  hiddenSupersededProjects: selectedProject || strictBoolean(args, "all")
1333
1512
  ? 0
1334
1513
  : workspaceProjects.filter((project) => project.lineage.supersededBy !== null).length,
@@ -1610,7 +1789,7 @@ function projectRecommendedAction(root, project, readyPackage, nativeStage, scie
1610
1789
  : "Inspect the blocking package and use explicit retry or fork recovery.";
1611
1790
  }
1612
1791
  if (readyPackage && ["discover", "acquire", "analyze", "synthesize"].includes(readyPackage)) {
1613
- return `Prepare native ${readyPackage}: tiangong-ai research project stage prepare ${project.id} --stage ${readyPackage} --host-agent <codex|claude> --workspace ${root}`;
1792
+ return `Prepare native ${readyPackage}: tiangong-ai research project stage prepare ${project.id} --stage ${readyPackage} --host-agent <codex|claude|workbuddy|codebuddy> --workspace ${root}`;
1614
1793
  }
1615
1794
  return readyPackage === "review"
1616
1795
  ? `Run the independent reviewer package: tiangong-ai research run --project ${project.id} --workspace ${root}`
@@ -1731,9 +1910,10 @@ function nativeProducerStage(value) {
1731
1910
  });
1732
1911
  }
1733
1912
  function nativeHostAgent(value) {
1734
- if (value === "codex" || value === "claude")
1913
+ if (value === "codex" || value === "claude" || value === "workbuddy" || value === "codebuddy") {
1735
1914
  return value;
1736
- throw new CliError("--host-agent must be codex or claude.", {
1915
+ }
1916
+ throw new CliError("--host-agent must be codex, claude, workbuddy, or codebuddy.", {
1737
1917
  code: "RESEARCH_NATIVE_HOST_INVALID",
1738
1918
  exitCode: 2,
1739
1919
  });
@@ -1846,9 +2026,13 @@ function scientificReviewRole(value) {
1846
2026
  });
1847
2027
  }
1848
2028
  function publicationAgent(value, label) {
1849
- if (value === "codex" || value === "claude")
2029
+ const producer = label.includes("producer");
2030
+ if (value === "codex" ||
2031
+ value === "claude" ||
2032
+ (producer && (value === "workbuddy" || value === "codebuddy"))) {
1850
2033
  return value;
1851
- throw new CliError(`--${label}-agent must be codex or claude.`, {
2034
+ }
2035
+ throw new CliError(`--${label}-agent must be ${producer ? "codex, claude, workbuddy, or codebuddy" : "codex or claude"}.`, {
1852
2036
  code: "RESEARCH_PUBLICATION_AGENT_INVALID",
1853
2037
  exitCode: 2,
1854
2038
  });