@tiangong-ai/cli 0.0.35 → 0.0.37

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 (47) hide show
  1. package/AGENTS.md +16 -6
  2. package/README.md +128 -10
  3. package/dist/research/orchestration.js +334 -29
  4. package/dist/research/orchestration.js.map +1 -1
  5. package/dist/research/workspace/acquisition-routes.d.ts +14 -0
  6. package/dist/research/workspace/acquisition-routes.js +45 -0
  7. package/dist/research/workspace/acquisition-routes.js.map +1 -0
  8. package/dist/research/workspace/audit-bundle.d.ts +44 -0
  9. package/dist/research/workspace/audit-bundle.js +357 -0
  10. package/dist/research/workspace/audit-bundle.js.map +1 -0
  11. package/dist/research/workspace/broker.js +54 -10
  12. package/dist/research/workspace/broker.js.map +1 -1
  13. package/dist/research/workspace/downloads.d.ts +6 -0
  14. package/dist/research/workspace/downloads.js +32 -8
  15. package/dist/research/workspace/downloads.js.map +1 -1
  16. package/dist/research/workspace/evidence-exhaustion.d.ts +45 -0
  17. package/dist/research/workspace/evidence-exhaustion.js +365 -0
  18. package/dist/research/workspace/evidence-exhaustion.js.map +1 -0
  19. package/dist/research/workspace/native-activity.d.ts +6 -0
  20. package/dist/research/workspace/native-activity.js +24 -7
  21. package/dist/research/workspace/native-activity.js.map +1 -1
  22. package/dist/research/workspace/preflight.d.ts +34 -2
  23. package/dist/research/workspace/preflight.js +161 -1
  24. package/dist/research/workspace/preflight.js.map +1 -1
  25. package/dist/research/workspace/projects.d.ts +28 -4
  26. package/dist/research/workspace/projects.js +301 -12
  27. package/dist/research/workspace/projects.js.map +1 -1
  28. package/dist/research/workspace/publication-workflow.js +2 -0
  29. package/dist/research/workspace/publication-workflow.js.map +1 -1
  30. package/dist/research/workspace/runtime.d.ts +151 -3
  31. package/dist/research/workspace/runtime.js +180 -6
  32. package/dist/research/workspace/runtime.js.map +1 -1
  33. package/dist/research/workspace/sanitization.js +9 -3
  34. package/dist/research/workspace/sanitization.js.map +1 -1
  35. package/dist/research/workspace/scientific-design.d.ts +359 -0
  36. package/dist/research/workspace/scientific-design.js +2021 -0
  37. package/dist/research/workspace/scientific-design.js.map +1 -0
  38. package/dist/research/workspace/scientific-review.d.ts +101 -0
  39. package/dist/research/workspace/scientific-review.js +1167 -0
  40. package/dist/research/workspace/scientific-review.js.map +1 -0
  41. package/dist/research/workspace/setup-catalog.js +2 -2
  42. package/dist/research/workspace/setup.js +12 -3
  43. package/dist/research/workspace/setup.js.map +1 -1
  44. package/dist/research/workspace/types.d.ts +54 -0
  45. package/dist/research/workspace/workspace.js +25 -7
  46. package/dist/research/workspace/workspace.js.map +1 -1
  47. package/package.json +2 -1
@@ -9,6 +9,7 @@ import { stageEvidenceArtifacts } from "./artifacts.js";
9
9
  import { commitDiscoveryDecisions, materializeDiscoveryEvidence } from "./discovery.js";
10
10
  import { inspectDiscoveryProgress } from "./discovery-status.js";
11
11
  import { downloadBindingRecordSchema } from "./downloads.js";
12
+ import { parseEvidenceExhaustionHandoff, validateEvidenceExhaustionHandoff, } from "./evidence-exhaustion.js";
12
13
  import { loadProjectEvidenceReceipts, stageProjectEvidence } from "./evidence.js";
13
14
  import { appendEvidenceLedgerEvent, evidenceLedgerPath, listEvidenceCandidates, registerProjectInputCandidates, } from "./evidence-ledger.js";
14
15
  import { executeAgent } from "./executor.js";
@@ -19,17 +20,39 @@ import { nativeActivityRecordSchema } from "./native-activity.js";
19
20
  import { calculateAgentCallTokenReservation, RESEARCH_BROKER_MAX_TURNS, RESEARCH_ESTIMATED_BYTES_PER_TOKEN, RESEARCH_MAX_REPAIR_SOURCE_BYTES, RESEARCH_REPAIR_MAX_TURNS, researchStructuredOutputMaxTurns, reservedAgentPackageCost, } from "./preflight.js";
20
21
  import { listProjects, loadProject, nextReadyPackage, packageById, refreshProject, saveProject, } from "./projects.js";
21
22
  import { assertResearchPolicyBinding } from "./research-policy.js";
23
+ import { assertScientificGateForStage } from "./scientific-review.js";
22
24
  import { configuredResearchSecrets, sanitizeResearchRecord, sanitizeResearchText, } from "./sanitization.js";
23
25
  import { parseEvidenceRecord, schemaForDiscoveryAssessmentBatch, parseStructuredStageOutput, schemaForStage, StructuredOutputError, } from "./schemas.js";
24
26
  import { canonicalJson, ensureDirectory, fileRecord, isObject, pathExists, readJsonFile, regularTreeFiles, resolveContained, sha256File, sha256Text, workspacePaths, writeJsonAtomic, writeTextAtomic, } from "./storage.js";
25
27
  import { loadWorkspaceConfig, verifyDoctorAttestation, withWorkspaceLock } from "./workspace.js";
28
+ function identifierSetSchema(minItems, maxItems) {
29
+ return {
30
+ type: "array",
31
+ minItems,
32
+ maxItems,
33
+ uniqueItems: true,
34
+ items: { type: "string", pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$" },
35
+ };
36
+ }
26
37
  export const researchHandoffRecordSchema = {
27
- $id: "https://schemas.tiangong.ai/research/handoff-request-v1.json",
38
+ $id: "https://schemas.tiangong.ai/research/handoff-request-v2.json",
28
39
  type: "object",
29
40
  additionalProperties: false,
30
- required: ["schemaVersion", "state", "reasonCode", "summary", "requestedActions", "evidenceGaps"],
41
+ required: [
42
+ "schemaVersion",
43
+ "kind",
44
+ "state",
45
+ "reasonCode",
46
+ "summary",
47
+ "requestedActions",
48
+ "evidenceGaps",
49
+ ],
31
50
  properties: {
32
- schemaVersion: { type: "integer", const: 1 },
51
+ schemaVersion: { type: "integer", const: 2 },
52
+ kind: {
53
+ type: "string",
54
+ enum: ["interactive-challenge", "external-wait", "evidence-exhausted"],
55
+ },
33
56
  state: {
34
57
  type: "string",
35
58
  enum: ["user-action-required", "external-response-required"],
@@ -50,7 +73,92 @@ export const researchHandoffRecordSchema = {
50
73
  uniqueItems: true,
51
74
  items: { type: "string", minLength: 1, maxLength: 500 },
52
75
  },
76
+ exhaustion: {
77
+ type: "object",
78
+ additionalProperties: false,
79
+ required: ["missingEvidenceRoleIds", "routeAttempts", "remainingRouteIds"],
80
+ properties: {
81
+ missingEvidenceRoleIds: identifierSetSchema(1, 100),
82
+ routeAttempts: {
83
+ type: "array",
84
+ minItems: 1,
85
+ maxItems: 100,
86
+ items: {
87
+ type: "object",
88
+ additionalProperties: false,
89
+ required: ["routeId", "terminalEventHashes", "outcome"],
90
+ properties: {
91
+ routeId: { type: "string", pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$" },
92
+ terminalEventHashes: {
93
+ type: "array",
94
+ minItems: 1,
95
+ maxItems: 100,
96
+ uniqueItems: true,
97
+ items: { type: "string", pattern: "^[a-f0-9]{64}$" },
98
+ },
99
+ outcome: {
100
+ type: "string",
101
+ enum: ["completed-insufficient", "access-blocked", "deterministic-unavailable"],
102
+ },
103
+ },
104
+ },
105
+ },
106
+ remainingRouteIds: identifierSetSchema(0, 100),
107
+ },
108
+ },
109
+ accessRequests: {
110
+ type: "array",
111
+ minItems: 0,
112
+ maxItems: 100,
113
+ items: {
114
+ type: "object",
115
+ additionalProperties: false,
116
+ required: [
117
+ "id",
118
+ "routeId",
119
+ "resourceType",
120
+ "resourceName",
121
+ "officialLocator",
122
+ "evidenceRoleIds",
123
+ "rationale",
124
+ "alternativesTriedRouteIds",
125
+ "requestedAction",
126
+ "resumeCriteria",
127
+ "costStatus",
128
+ ],
129
+ properties: {
130
+ id: { type: "string", pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$" },
131
+ routeId: { type: "string", pattern: "^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$" },
132
+ resourceType: {
133
+ type: "string",
134
+ enum: [
135
+ "database-subscription",
136
+ "article-purchase",
137
+ "institutional-access",
138
+ "licensed-dataset",
139
+ "owner-provided-material",
140
+ "external-data-request",
141
+ "field-data-collection",
142
+ ],
143
+ },
144
+ resourceName: { type: "string", minLength: 3, maxLength: 500 },
145
+ officialLocator: { type: ["string", "null"], format: "uri" },
146
+ evidenceRoleIds: identifierSetSchema(1, 100),
147
+ rationale: { type: "string", minLength: 8, maxLength: 2_000 },
148
+ alternativesTriedRouteIds: identifierSetSchema(1, 100),
149
+ requestedAction: { type: "string", minLength: 8, maxLength: 1_000 },
150
+ resumeCriteria: { type: "string", minLength: 8, maxLength: 1_000 },
151
+ costStatus: { type: "string", enum: ["unknown", "provider-quote-required"] },
152
+ },
153
+ },
154
+ },
53
155
  },
156
+ allOf: [
157
+ {
158
+ if: { properties: { kind: { const: "evidence-exhausted" } } },
159
+ then: { required: ["exhaustion", "accessRequests"] },
160
+ },
161
+ ],
54
162
  };
55
163
  export async function runResearchWorkspace(root, options, packageExecutor = executeAgent) {
56
164
  return runResearchWorkspaceInternal(root, options, packageExecutor, false);
@@ -179,6 +287,7 @@ export async function prepareNativeResearchStage(input) {
179
287
  const config = await loadWorkspaceConfig(input.root);
180
288
  assertExecutionConfiguration(config);
181
289
  const project = await loadProject(input.root, input.projectId);
290
+ await assertScientificGateForStage(input.root, project, input.stage);
182
291
  await assertProjectPublicationPolicy(input.root, project);
183
292
  if (config.producer.agent !== input.hostAgent) {
184
293
  throw new CliError(`This workspace requires the current native ${config.producer.agent} host, not ${input.hostAgent}.`, { code: "RESEARCH_NATIVE_HOST_MISMATCH", exitCode: 3 });
@@ -297,6 +406,21 @@ export async function prepareNativeResearchStage(input) {
297
406
  maxWallSeconds: config.budget.packageMaxWallSeconds[input.stage],
298
407
  },
299
408
  commands: {
409
+ inspectAccess: project.scientificDesign
410
+ ? {
411
+ argv: [
412
+ "tiangong-ai",
413
+ "research",
414
+ "project",
415
+ "access",
416
+ "status",
417
+ project.id,
418
+ "--workspace",
419
+ input.root,
420
+ "--json",
421
+ ],
422
+ }
423
+ : null,
300
424
  requestHandoff: {
301
425
  argv: [
302
426
  "tiangong-ai",
@@ -517,6 +641,7 @@ export async function prepareNativeResearchStage(input) {
517
641
  rules: [
518
642
  "Current native host performs producer reasoning; the CLI does not spawn a producer.",
519
643
  "Native Web/Browser activity is visible in the evidence ledger but becomes admissible only after broker/input formalization and strict assessment.",
644
+ "An interactive challenge pauses immediately. A material evidence-exhausted handoff is valid only after inspectAccess proves every required plan-bound agent route with exact terminal event hashes.",
520
645
  "When the next material step requires user authorization or an external response, request a durable handoff and stop; do not keep searching low-yield substitutes.",
521
646
  "Only broker receipts or registered immutable inputs may support discover output.",
522
647
  "Only exact, structurally validated, content-addressed artifacts may support full-text acquisition claims.",
@@ -793,6 +918,17 @@ export async function requestResearchHandoff(input) {
793
918
  details: { state: project.handoff.state },
794
919
  });
795
920
  }
921
+ const evidenceHandoff = value.kind === "evidence-exhausted"
922
+ ? await validateEvidenceExhaustionHandoff({
923
+ root: input.root,
924
+ project,
925
+ state: value.state,
926
+ value: {
927
+ exhaustion: value.exhaustion,
928
+ accessRequests: value.accessRequests,
929
+ },
930
+ })
931
+ : null;
796
932
  const activePath = nativeStageSessionPath(input.root, input.projectId);
797
933
  const session = (await pathExists(activePath))
798
934
  ? await readNativeStageSession(input.root, input.projectId)
@@ -816,10 +952,13 @@ export async function requestResearchHandoff(input) {
816
952
  const requestedAt = new Date().toISOString();
817
953
  project.handoff = {
818
954
  state: value.state,
955
+ kind: value.kind,
819
956
  reasonCode: value.reasonCode,
820
957
  summary: value.summary,
821
958
  requestedActions: value.requestedActions,
822
959
  evidenceGaps: value.evidenceGaps,
960
+ exhaustion: evidenceHandoff?.exhaustion ?? null,
961
+ accessRequests: evidenceHandoff?.accessRequests ?? [],
823
962
  requestedAt,
824
963
  resolvedAt: null,
825
964
  resolutionNote: null,
@@ -828,10 +967,13 @@ export async function requestResearchHandoff(input) {
828
967
  await saveProject(input.root, project);
829
968
  const eventPayload = {
830
969
  state: value.state,
970
+ kind: value.kind,
831
971
  reasonCode: value.reasonCode,
832
972
  summary: value.summary,
833
973
  requestedActions: value.requestedActions,
834
974
  evidenceGaps: value.evidenceGaps,
975
+ exhaustion: evidenceHandoff?.exhaustion ?? null,
976
+ accessRequests: evidenceHandoff?.accessRequests ?? [],
835
977
  requestedAt,
836
978
  interruptedSessionId: session?.packet.sessionId ?? null,
837
979
  interruptedPackageId: session?.packet.packageId ?? null,
@@ -892,18 +1034,22 @@ function parseHandoffRequest(value) {
892
1034
  const sanitized = sanitizeResearchRecord(value, configuredResearchSecrets(process.env));
893
1035
  const allowed = new Set([
894
1036
  "schemaVersion",
1037
+ "kind",
895
1038
  "state",
896
1039
  "reasonCode",
897
1040
  "summary",
898
1041
  "requestedActions",
899
1042
  "evidenceGaps",
1043
+ "exhaustion",
1044
+ "accessRequests",
900
1045
  ]);
901
1046
  const requestedActions = Array.isArray(sanitized.requestedActions)
902
1047
  ? sanitized.requestedActions
903
1048
  : [];
904
1049
  const evidenceGaps = Array.isArray(sanitized.evidenceGaps) ? sanitized.evidenceGaps : [];
905
1050
  if (Object.keys(sanitized).some((key) => !allowed.has(key)) ||
906
- sanitized.schemaVersion !== 1 ||
1051
+ sanitized.schemaVersion !== 2 ||
1052
+ !["interactive-challenge", "external-wait", "evidence-exhausted"].includes(String(sanitized.kind)) ||
907
1053
  !["user-action-required", "external-response-required"].includes(String(sanitized.state)) ||
908
1054
  typeof sanitized.reasonCode !== "string" ||
909
1055
  !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(sanitized.reasonCode) ||
@@ -923,18 +1069,46 @@ function parseHandoffRequest(value) {
923
1069
  exitCode: 2,
924
1070
  });
925
1071
  }
1072
+ const kind = sanitized.kind;
1073
+ const state = sanitized.state;
1074
+ if ((kind === "interactive-challenge" && state !== "user-action-required") ||
1075
+ (kind === "external-wait" && state !== "external-response-required")) {
1076
+ throw new CliError("Handoff request failed validation.", {
1077
+ code: "RESEARCH_PROJECT_HANDOFF_INVALID",
1078
+ exitCode: 2,
1079
+ });
1080
+ }
1081
+ let exhaustion = null;
1082
+ let accessRequests = [];
1083
+ if (kind === "evidence-exhausted") {
1084
+ const parsed = parseEvidenceExhaustionHandoff(sanitized.exhaustion, sanitized.accessRequests);
1085
+ exhaustion = parsed.exhaustion;
1086
+ accessRequests = parsed.accessRequests;
1087
+ }
1088
+ else if (sanitized.exhaustion !== undefined ||
1089
+ (sanitized.accessRequests !== undefined &&
1090
+ (!Array.isArray(sanitized.accessRequests) || sanitized.accessRequests.length > 0))) {
1091
+ throw new CliError("Handoff request failed validation.", {
1092
+ code: "RESEARCH_PROJECT_HANDOFF_INVALID",
1093
+ exitCode: 2,
1094
+ });
1095
+ }
926
1096
  return {
927
- state: sanitized.state,
1097
+ kind,
1098
+ state,
928
1099
  reasonCode: sanitized.reasonCode,
929
1100
  summary: sanitized.summary.trim(),
930
1101
  requestedActions: requestedActions,
931
1102
  evidenceGaps: evidenceGaps,
1103
+ exhaustion,
1104
+ accessRequests,
932
1105
  };
933
1106
  }
934
1107
  async function executeWorkPackage(root, projectId, packageId, config, options, requestId, packageExecutor, doctorAttestation) {
935
1108
  const project = await loadProject(root, projectId);
936
- await assertProjectPublicationPolicy(root, project);
937
1109
  const workPackage = packageById(project, packageId);
1110
+ await assertScientificGateForStage(root, project, workPackage.stage);
1111
+ await assertProjectPublicationPolicy(root, project);
938
1112
  const now = new Date().toISOString();
939
1113
  workPackage.status = "running";
940
1114
  workPackage.attempts += 1;