@inflexa-ai/harness 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -0
- package/dist/agents/conversation-agent.d.ts +15 -11
- package/dist/agents/conversation-agent.js +31 -34
- package/dist/agents/report-builder.js +2 -1
- package/dist/agents/sandbox/bulk-transcriptomics-agent.js +1 -1
- package/dist/agents/sandbox/cheminformatics-agent.js +1 -19
- package/dist/agents/sandbox/chromatin-agent.js +1 -10
- package/dist/agents/sandbox/dna-methylation-agent.js +1 -10
- package/dist/agents/sandbox/drug-repurposing-agent.js +4 -14
- package/dist/agents/sandbox/enrichment-agent.js +1 -10
- package/dist/agents/sandbox/ephemeral-executor.js +1 -10
- package/dist/agents/sandbox/genomic-variant-agent.js +1 -3
- package/dist/agents/sandbox/immune-profiling-agent.js +1 -13
- package/dist/agents/sandbox/metabolomics-agent.js +1 -1
- package/dist/agents/sandbox/microbiome-agent.js +1 -1
- package/dist/agents/sandbox/multi-omics-integration-agent.js +1 -12
- package/dist/agents/sandbox/multimodal-sc-agent.js +1 -1
- package/dist/agents/sandbox/network-agent.js +1 -10
- package/dist/agents/sandbox/pkpd-clinical-response-agent.js +2 -8
- package/dist/agents/sandbox/proteomics-agent.js +1 -1
- package/dist/agents/sandbox/scientific-executor.js +1 -10
- package/dist/agents/sandbox/shared.d.ts +20 -6
- package/dist/agents/sandbox/shared.js +50 -39
- package/dist/agents/sandbox/single-cell-agent.js +1 -1
- package/dist/agents/sandbox/spatial-omics-agent.js +1 -1
- package/dist/agents/sandbox/statistical-modeling-agent.js +1 -1
- package/dist/agents/sandbox/translational-safety-agent.js +3 -9
- package/dist/agents/sandbox/types.d.ts +1 -1
- package/dist/agents/system-prompt.d.ts +37 -11
- package/dist/agents/system-prompt.js +35 -12
- package/dist/app/chat-turn.d.ts +3 -0
- package/dist/app/chat-turn.js +3 -1
- package/dist/app/data-profile-orientation.d.ts +34 -0
- package/dist/app/data-profile-orientation.js +117 -0
- package/dist/app/synthesize-run.d.ts +3 -0
- package/dist/app/synthesize-run.js +3 -1
- package/dist/contracts/schemas/chat-parts.d.ts +9 -9
- package/dist/execution/artifact-metadata.d.ts +3 -0
- package/dist/execution/artifact-metadata.js +10 -3
- package/dist/execution/artifact-registration.d.ts +2 -1
- package/dist/execution/artifact-registration.js +8 -2
- package/dist/execution/ephemeral-runner.d.ts +16 -0
- package/dist/execution/ephemeral-runner.js +31 -5
- package/dist/execution/post-step-pipeline.d.ts +3 -0
- package/dist/execution/post-step-pipeline.js +24 -4
- package/dist/execution/reconcile-manifest.d.ts +7 -0
- package/dist/execution/reconcile-manifest.js +19 -5
- package/dist/execution/report-runner.d.ts +3 -0
- package/dist/execution/report-runner.js +4 -4
- package/dist/execution/run-synthesis.js +20 -19
- package/dist/execution/step-summary.d.ts +3 -0
- package/dist/execution/step-summary.js +6 -3
- package/dist/index.d.ts +9 -2
- package/dist/index.js +24 -5
- package/dist/lib/chrome.d.ts +4 -1
- package/dist/lib/chrome.js +10 -7
- package/dist/lib/console-logger.d.ts +19 -0
- package/dist/lib/console-logger.js +62 -0
- package/dist/lib/logger.d.ts +88 -0
- package/dist/lib/logger.js +37 -0
- package/dist/lib/otel.d.ts +2 -1
- package/dist/lib/otel.js +8 -3
- package/dist/loop/__fixtures__/scripted-provider.d.ts +2 -2
- package/dist/loop/__fixtures__/scripted-provider.js +2 -1
- package/dist/loop/metrics.d.ts +33 -1
- package/dist/loop/metrics.js +56 -2
- package/dist/loop/run-agent.d.ts +13 -1
- package/dist/loop/run-agent.js +19 -4
- package/dist/memory/card-builders.d.ts +8 -1
- package/dist/memory/card-builders.js +20 -3
- package/dist/memory/reconstruct-cards.js +4 -3
- package/dist/memory/thread-history.d.ts +27 -2
- package/dist/memory/thread-history.js +45 -5
- package/dist/memory/working-memory.d.ts +93 -10
- package/dist/memory/working-memory.js +220 -84
- package/dist/prompts/SOUL.d.ts +22 -9
- package/dist/prompts/SOUL.js +56 -37
- package/dist/prompts/analogical-reasoner.d.ts +1 -1
- package/dist/prompts/analogical-reasoner.js +2 -2
- package/dist/prompts/briefing.d.ts +100 -0
- package/dist/prompts/briefing.js +166 -0
- package/dist/prompts/conversation.d.ts +1 -1
- package/dist/prompts/conversation.js +94 -440
- package/dist/prompts/literature-reviewer.d.ts +1 -1
- package/dist/prompts/literature-reviewer.js +10 -10
- package/dist/prompts/planner.js +28 -59
- package/dist/prompts/report-builder.d.ts +1 -1
- package/dist/prompts/report-builder.js +71 -24
- package/dist/prompts/sandbox/bulk-transcriptomics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/bulk-transcriptomics-agent.js +2 -7
- package/dist/prompts/sandbox/cheminformatics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/cheminformatics-agent.js +11 -15
- package/dist/prompts/sandbox/chromatin-agent.d.ts +1 -1
- package/dist/prompts/sandbox/chromatin-agent.js +3 -7
- package/dist/prompts/sandbox/data-profiler.d.ts +1 -1
- package/dist/prompts/sandbox/data-profiler.js +13 -8
- package/dist/prompts/sandbox/dna-methylation-agent.d.ts +1 -1
- package/dist/prompts/sandbox/dna-methylation-agent.js +2 -7
- package/dist/prompts/sandbox/drug-repurposing-agent.d.ts +1 -1
- package/dist/prompts/sandbox/drug-repurposing-agent.js +15 -16
- package/dist/prompts/sandbox/enrichment-agent.d.ts +1 -1
- package/dist/prompts/sandbox/enrichment-agent.js +2 -9
- package/dist/prompts/sandbox/genomic-variant-agent.d.ts +1 -1
- package/dist/prompts/sandbox/genomic-variant-agent.js +2 -6
- package/dist/prompts/sandbox/immune-profiling-agent.d.ts +1 -1
- package/dist/prompts/sandbox/immune-profiling-agent.js +3 -8
- package/dist/prompts/sandbox/metabolomics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/metabolomics-agent.js +1 -7
- package/dist/prompts/sandbox/microbiome-agent.d.ts +1 -1
- package/dist/prompts/sandbox/microbiome-agent.js +2 -7
- package/dist/prompts/sandbox/multi-omics-integration-agent.d.ts +1 -1
- package/dist/prompts/sandbox/multi-omics-integration-agent.js +2 -8
- package/dist/prompts/sandbox/multimodal-sc-agent.d.ts +1 -1
- package/dist/prompts/sandbox/multimodal-sc-agent.js +2 -7
- package/dist/prompts/sandbox/network-agent.d.ts +1 -1
- package/dist/prompts/sandbox/network-agent.js +2 -7
- package/dist/prompts/sandbox/proteomics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/proteomics-agent.js +2 -9
- package/dist/prompts/sandbox/single-cell-agent.d.ts +1 -1
- package/dist/prompts/sandbox/single-cell-agent.js +2 -9
- package/dist/prompts/sandbox/spatial-omics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/spatial-omics-agent.js +3 -6
- package/dist/prompts/sandbox/statistical-modeling-agent.d.ts +1 -1
- package/dist/prompts/sandbox/statistical-modeling-agent.js +2 -7
- package/dist/prompts/sandbox/translational-safety-agent.d.ts +1 -1
- package/dist/prompts/sandbox/translational-safety-agent.js +13 -14
- package/dist/prompts/sandbox-standards.d.ts +22 -14
- package/dist/prompts/sandbox-standards.js +91 -71
- package/dist/prompts/synthesis-agent.d.ts +1 -1
- package/dist/prompts/synthesis-agent.js +33 -131
- package/dist/provenance/collector.js +1 -1
- package/dist/providers/ai-sdk.js +31 -4
- package/dist/providers/prompt-cache.d.ts +74 -0
- package/dist/providers/prompt-cache.js +77 -0
- package/dist/providers/types.d.ts +33 -0
- package/dist/reference-data/catalog.d.ts +131 -0
- package/dist/reference-data/catalog.js +348 -0
- package/dist/reference-data/receipt.d.ts +41 -0
- package/dist/reference-data/receipt.js +40 -0
- package/dist/runtime/boot.d.ts +2 -2
- package/dist/runtime/boot.js +1 -1
- package/dist/runtime/connection-budget.d.ts +2 -2
- package/dist/runtime/connection-budget.js +5 -3
- package/dist/runtime/dbos.d.ts +7 -7
- package/dist/runtime/dbos.js +14 -11
- package/dist/runtime/shutdown.d.ts +2 -2
- package/dist/runtime/shutdown.js +4 -3
- package/dist/sandbox/create-sandbox.d.ts +2 -2
- package/dist/sandbox/docker-client.d.ts +2 -2
- package/dist/sandbox/docker-client.js +4 -1
- package/dist/sandbox/k8s-client.d.ts +3 -0
- package/dist/sandbox/k8s-client.js +5 -4
- package/dist/sandbox/notification-sweep.d.ts +3 -3
- package/dist/sandbox/notification-sweep.js +4 -2
- package/dist/sandbox/reaper.d.ts +3 -3
- package/dist/sandbox/reaper.js +7 -4
- package/dist/sandbox/watchdog.d.ts +3 -3
- package/dist/sandbox/watchdog.js +8 -2
- package/dist/schemas/data-profile-schemas.js +5 -3
- package/dist/schemas/plan-schemas.d.ts +21 -21
- package/dist/schemas/plan-schemas.js +9 -3
- package/dist/schemas/run-synthesis.d.ts +8 -4
- package/dist/schemas/run-synthesis.js +92 -56
- package/dist/schemas/validate-plan.js +1 -1
- package/dist/schemas/workflow-state.d.ts +7 -1
- package/dist/schemas/workflow-state.js +33 -19
- package/dist/state/artifacts.d.ts +13 -0
- package/dist/state/artifacts.js +27 -0
- package/dist/state/data-profile.d.ts +64 -5
- package/dist/state/index.d.ts +3 -3
- package/dist/state/index.js +1 -1
- package/dist/state/init.d.ts +2 -1
- package/dist/state/init.js +5 -6
- package/dist/tasks/data-profile.d.ts +25 -2
- package/dist/tasks/data-profile.js +95 -39
- package/dist/tools/bio/chembl.d.ts +38 -0
- package/dist/tools/bio/chembl.js +138 -0
- package/dist/tools/bio/comptox.d.ts +50 -0
- package/dist/tools/bio/comptox.js +149 -0
- package/dist/tools/bio/get-impc-ko-profile.js +9 -6
- package/dist/tools/bio/index.d.ts +5 -17
- package/dist/tools/bio/index.js +5 -17
- package/dist/tools/bio/keys.d.ts +14 -9
- package/dist/tools/bio/keys.js +16 -16
- package/dist/tools/bio/opentargets.d.ts +38 -0
- package/dist/tools/bio/opentargets.js +89 -0
- package/dist/tools/bio/pubchem.d.ts +29 -0
- package/dist/tools/bio/pubchem.js +92 -0
- package/dist/tools/bio/pubmed.d.ts +51 -0
- package/dist/tools/bio/pubmed.js +100 -0
- package/dist/tools/bio/search-bgee-expression.js +12 -6
- package/dist/tools/bio/search-clinical-trials.js +16 -5
- package/dist/tools/bio/search-dgidb.d.ts +11 -1
- package/dist/tools/bio/search-dgidb.js +92 -78
- package/dist/tools/bio/search-faers.js +8 -4
- package/dist/tools/bio/search-geo-datasets.js +20 -6
- package/dist/tools/bio/search-gwas-catalog.d.ts +1 -1
- package/dist/tools/bio/search-pharmgkb.js +10 -4
- package/dist/tools/display/index.d.ts +1 -0
- package/dist/tools/display/index.js +1 -0
- package/dist/tools/display/normalize-echart-spec.d.ts +61 -0
- package/dist/tools/display/normalize-echart-spec.js +211 -0
- package/dist/tools/display/show-user.d.ts +6 -1
- package/dist/tools/display/show-user.js +25 -12
- package/dist/tools/execute-plan.js +5 -2
- package/dist/tools/iterate-report.d.ts +171 -118
- package/dist/tools/iterate-report.js +336 -96
- package/dist/tools/lib/chembl-client.d.ts +12 -0
- package/dist/tools/lib/chembl-client.js +127 -0
- package/dist/tools/lib/ctx-ops.d.ts +224 -0
- package/dist/tools/lib/ctx-ops.js +450 -0
- package/dist/tools/lib/iuphar-client.js +10 -1
- package/dist/tools/lib/openfda-client.js +11 -3
- package/dist/tools/lib/pathway-client.js +10 -4
- package/dist/tools/lib/pubchem-ops.d.ts +74 -0
- package/dist/tools/lib/pubchem-ops.js +224 -0
- package/dist/tools/memory/update-working-memory.d.ts +45 -7
- package/dist/tools/memory/update-working-memory.js +111 -41
- package/dist/tools/report/index.d.ts +3 -2
- package/dist/tools/report/index.js +3 -2
- package/dist/tools/research/generate-analogy-report.js +25 -12
- package/dist/tools/research/generate-plan.d.ts +12 -5
- package/dist/tools/research/generate-plan.js +168 -37
- package/dist/tools/research/index.d.ts +1 -0
- package/dist/tools/research/index.js +1 -0
- package/dist/tools/research/inspect-data-profile.d.ts +85 -0
- package/dist/tools/research/inspect-data-profile.js +161 -0
- package/dist/tools/research/literature-reviewer.d.ts +3 -0
- package/dist/tools/research/literature-reviewer.js +5 -6
- package/dist/tools/sandbox/list-available-packages.d.ts +79 -5
- package/dist/tools/sandbox/list-available-packages.js +152 -10
- package/dist/tools/sandbox/list-available-refs.d.ts +141 -5
- package/dist/tools/sandbox/list-available-refs.js +647 -134
- package/dist/tools/sandbox/report-blocker.d.ts +39 -9
- package/dist/tools/sandbox/report-blocker.js +43 -20
- package/dist/tools/workspace/execute-command.d.ts +3 -0
- package/dist/tools/workspace/execute-command.js +3 -1
- package/dist/tools/workspace/read-file.js +4 -5
- package/dist/tools/workspace/run-exec.d.ts +4 -4
- package/dist/tools/workspace/run-exec.js +4 -4
- package/dist/tools/workspace/show-file.js +10 -6
- package/dist/tools/workspace/show-plan.js +12 -6
- package/dist/tools/workspace/workspace-search.d.ts +1 -1
- package/dist/tools/workspace/workspace-search.js +11 -5
- package/dist/workflows/execute-analysis.d.ts +46 -4
- package/dist/workflows/execute-analysis.js +118 -13
- package/dist/workflows/execute-target-assessment.d.ts +3 -0
- package/dist/workflows/execute-target-assessment.js +19 -15
- package/dist/workflows/sandbox-step.d.ts +9 -0
- package/dist/workflows/sandbox-step.js +39 -17
- package/dist/workflows/target-assessment/lib/citation-validator.js +2 -2
- package/dist/workflows/target-assessment/lib/clinical-consequence-annotator.js +2 -1
- package/dist/workflows/target-assessment/lib/llm-step.d.ts +3 -0
- package/dist/workflows/target-assessment/lib/llm-step.js +3 -1
- package/dist/workflows/target-assessment/lib/structured-llm.d.ts +3 -0
- package/dist/workflows/target-assessment/lib/structured-llm.js +1 -0
- package/dist/workflows/target-assessment/phase5-persist.d.ts +3 -0
- package/dist/workflows/target-assessment/phase5-persist.js +8 -3
- package/dist/workflows/target-assessment/progress.d.ts +2 -1
- package/dist/workflows/target-assessment/progress.js +4 -3
- package/dist/workspace/filesystem.js +163 -74
- package/dist/workspace/paths.d.ts +2 -2
- package/dist/workspace/paths.js +2 -2
- package/package.json +95 -96
- package/dist/execution/filesystem-artifact-registry.d.ts +0 -57
- package/dist/execution/filesystem-artifact-registry.js +0 -92
- package/dist/lib/identifiers.d.ts +0 -8
- package/dist/lib/identifiers.js +0 -10
- package/dist/lib/mime-detect.d.ts +0 -17
- package/dist/lib/mime-detect.js +0 -29
- package/dist/lib/streamed-download.d.ts +0 -28
- package/dist/lib/streamed-download.js +0 -111
- package/dist/lib/trace-context.d.ts +0 -11
- package/dist/lib/trace-context.js +0 -49
- package/dist/providers/llm-capabilities.d.ts +0 -33
- package/dist/providers/llm-capabilities.js +0 -58
- package/dist/schemas/render-step-prompt.d.ts +0 -32
- package/dist/schemas/render-step-prompt.js +0 -76
- package/dist/tools/bio/find-approval-precedent.d.ts +0 -32
- package/dist/tools/bio/find-approval-precedent.js +0 -72
- package/dist/tools/bio/get-article-details.d.ts +0 -11
- package/dist/tools/bio/get-article-details.js +0 -20
- package/dist/tools/bio/get-article-full-text.d.ts +0 -19
- package/dist/tools/bio/get-article-full-text.js +0 -32
- package/dist/tools/bio/get-bioactivity.d.ts +0 -11
- package/dist/tools/bio/get-bioactivity.js +0 -23
- package/dist/tools/bio/get-drug-info.d.ts +0 -9
- package/dist/tools/bio/get-drug-info.js +0 -22
- package/dist/tools/bio/get-mechanism.d.ts +0 -8
- package/dist/tools/bio/get-mechanism.js +0 -18
- package/dist/tools/bio/get-pubchem-assays.d.ts +0 -22
- package/dist/tools/bio/get-pubchem-assays.js +0 -107
- package/dist/tools/bio/get-pubchem-cross-refs.d.ts +0 -15
- package/dist/tools/bio/get-pubchem-cross-refs.js +0 -68
- package/dist/tools/bio/get-target-safety.d.ts +0 -18
- package/dist/tools/bio/get-target-safety.js +0 -26
- package/dist/tools/bio/search-compounds.d.ts +0 -22
- package/dist/tools/bio/search-compounds.js +0 -160
- package/dist/tools/bio/search-ctx-chemical.d.ts +0 -50
- package/dist/tools/bio/search-ctx-chemical.js +0 -130
- package/dist/tools/bio/search-ctx-exposure.d.ts +0 -55
- package/dist/tools/bio/search-ctx-exposure.js +0 -176
- package/dist/tools/bio/search-ctx-hazard.d.ts +0 -111
- package/dist/tools/bio/search-ctx-hazard.js +0 -178
- package/dist/tools/bio/search-opentargets.d.ts +0 -11
- package/dist/tools/bio/search-opentargets.js +0 -25
- package/dist/tools/bio/search-pubchem-compound.d.ts +0 -26
- package/dist/tools/bio/search-pubchem-compound.js +0 -110
- package/dist/tools/bio/search-pubmed.d.ts +0 -17
- package/dist/tools/bio/search-pubmed.js +0 -31
- package/dist/tools/bio/search-targets.d.ts +0 -9
- package/dist/tools/bio/search-targets.js +0 -20
- package/dist/tools/bio/search-toxcast.d.ts +0 -36
- package/dist/tools/bio/search-toxcast.js +0 -141
- package/dist/workflows/register-workflows.d.ts +0 -51
- package/dist/workflows/register-workflows.js +0 -48
- package/dist/workflows/resume-execute-analysis.d.ts +0 -70
- package/dist/workflows/resume-execute-analysis.js +0 -80
- package/dist/workflows/target-assessment/correction-loop.d.ts +0 -45
- package/dist/workflows/target-assessment/correction-loop.js +0 -62
- package/dist/workflows/target-assessment/steps/phase0-resolve.d.ts +0 -71
- package/dist/workflows/target-assessment/steps/phase0-resolve.js +0 -21
- package/dist/workflows/target-assessment/steps/phase1-aggregate.d.ts +0 -1018
- package/dist/workflows/target-assessment/steps/phase1-aggregate.js +0 -25
- package/dist/workspace/mount-strategy.d.ts +0 -50
- package/dist/workspace/mount-strategy.js +0 -67
- package/dist/workspace/provenance-collector.d.ts +0 -34
- package/dist/workspace/provenance-collector.js +0 -16
- package/dist/workspace/workspace-paths.d.ts +0 -26
- package/dist/workspace/workspace-paths.js +0 -31
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
* DBOS cache slot rather than replaying the cancelled prior attempt.
|
|
25
25
|
*/
|
|
26
26
|
import { DBOS } from "@dbos-inc/dbos-sdk";
|
|
27
|
+
import { createNoopLogger } from "../../../lib/console-logger.js";
|
|
27
28
|
import { unwrapOrThrow } from "../../../lib/result.js";
|
|
28
29
|
import { isBudgetExceeded } from "../../../loop/budget-exceeded.js";
|
|
29
30
|
/**
|
|
@@ -82,7 +83,8 @@ export async function runLlmStep(opts) {
|
|
|
82
83
|
await DBOS.runStep(() => DBOS.send(workflowId, marker, BUDGET_EXCEEDED_TOPIC), { name: `${stepName}:notify-budget-exceeded` });
|
|
83
84
|
}
|
|
84
85
|
catch (sendErr) {
|
|
85
|
-
|
|
86
|
+
const logger = (opts.logger ?? createNoopLogger()).named("ta-llm-step");
|
|
87
|
+
logger.warn("notify-budget-exceeded send failed (non-fatal)", { stepName, ...logger.errorFields(sendErr) });
|
|
86
88
|
}
|
|
87
89
|
return { kind: "budget-exceeded", sentinel: BUDGET_EXCEEDED_SENTINEL };
|
|
88
90
|
}
|
|
@@ -18,9 +18,12 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import { z } from "zod";
|
|
20
20
|
import type { AgentSession } from "../../../auth/types.js";
|
|
21
|
+
import type { Logger } from "../../../lib/logger.js";
|
|
21
22
|
import type { AgentChat } from "../../../providers/types.js";
|
|
22
23
|
import { BUDGET_EXCEEDED_SENTINEL } from "./llm-step.js";
|
|
23
24
|
export interface StructuredLlmCallOptions<TSchema extends z.ZodType> {
|
|
25
|
+
/** Operational logging seam, forwarded to `runLlmStep`; omitted falls back to no-op. */
|
|
26
|
+
readonly logger?: Logger;
|
|
24
27
|
/** Attempt-numbered durable-step name (e.g. `"ta-decision:modulator-triage:0"`). */
|
|
25
28
|
readonly stepName: string;
|
|
26
29
|
/** Agent id stamped on telemetry and billing-gateway provenance. */
|
|
@@ -78,6 +78,7 @@ export async function structuredLlmCall(opts) {
|
|
|
78
78
|
toolChoice: { type: "tool", toolName: SUBMIT_TOOL_NAME },
|
|
79
79
|
};
|
|
80
80
|
const result = await runLlmStep({
|
|
81
|
+
...(opts.logger ? { logger: opts.logger } : {}),
|
|
81
82
|
stepName: opts.stepName,
|
|
82
83
|
agentId: opts.agentId,
|
|
83
84
|
provider: opts.provider,
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* in §17 if a harness-native audit pipeline is wired.
|
|
16
16
|
*/
|
|
17
17
|
import { z } from "zod";
|
|
18
|
+
import type { Logger } from "../../lib/logger.js";
|
|
18
19
|
import { type DossierV4Body } from "@inflexa-ai/harness/contracts/target-dossier.js";
|
|
19
20
|
import type { Phase2Bundle } from "./steps/phase2-aggregate.js";
|
|
20
21
|
import type { DossierRecommendationStepOutput, LiabilityBulletsStepOutput, SafetyFlagsTrailStepOutput, TranslationalCommentaryStepOutput } from "./synthesis/index.js";
|
|
@@ -27,6 +28,8 @@ export declare class DossierDerivedInvariantError extends Error {
|
|
|
27
28
|
constructor(message: string, cause?: unknown | undefined);
|
|
28
29
|
}
|
|
29
30
|
export interface Phase5PersistInput {
|
|
31
|
+
/** Operational logging seam; omitted falls back to no-op. */
|
|
32
|
+
readonly logger?: Logger;
|
|
30
33
|
readonly assessmentId: string;
|
|
31
34
|
readonly phase4Dossier: DossierV4Body;
|
|
32
35
|
readonly phase2: Phase2Bundle;
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* in §17 if a harness-native audit pipeline is wired.
|
|
16
16
|
*/
|
|
17
17
|
import { z } from "zod";
|
|
18
|
+
import { createNoopLogger } from "../../lib/console-logger.js";
|
|
18
19
|
import { DossierV5Schema, } from "@inflexa-ai/harness/contracts/target-dossier.js";
|
|
19
20
|
import { getDrugPrimaryTargetUniprots } from "../../tools/lib/chembl-client.js";
|
|
20
21
|
import { deterministicTranslationalCommentary } from "./assemblers/index.js";
|
|
@@ -84,7 +85,7 @@ function pickCommentarySeverity(row) {
|
|
|
84
85
|
}
|
|
85
86
|
return "ok";
|
|
86
87
|
}
|
|
87
|
-
function stampSynthesis(phase4Dossier, syn, phase2) {
|
|
88
|
+
function stampSynthesis(logger, phase4Dossier, syn, phase2) {
|
|
88
89
|
const next = structuredClone(phase4Dossier);
|
|
89
90
|
if (syn.bullets.coverage === "available") {
|
|
90
91
|
next.liability_summary.liability_bullets = syn.bullets.data.bullets.map((b) => ({ text: b.text, rationale: b.rationale, category: b.category }));
|
|
@@ -147,7 +148,10 @@ function stampSynthesis(phase4Dossier, syn, phase2) {
|
|
|
147
148
|
try {
|
|
148
149
|
const audit = validateRecommendationCitations(syn.recommendation.data, next);
|
|
149
150
|
if (audit.citations_unresolved.length > 0) {
|
|
150
|
-
|
|
151
|
+
logger.warn("recommendation_audit: unresolved citation(s)", {
|
|
152
|
+
unresolvedCount: audit.citations_unresolved.length,
|
|
153
|
+
unresolved: audit.citations_unresolved,
|
|
154
|
+
});
|
|
151
155
|
}
|
|
152
156
|
next.analytics.recommendation_audit = {
|
|
153
157
|
coverage: "available",
|
|
@@ -388,12 +392,13 @@ async function upgradeDossierToV5(body, phase2) {
|
|
|
388
392
|
* rejects the final dossier.
|
|
389
393
|
*/
|
|
390
394
|
export async function phase5Persist(input) {
|
|
395
|
+
const logger = (input.logger ?? createNoopLogger()).named("phase5-persist").with({ assessmentId: input.assessmentId });
|
|
391
396
|
// The dossier rides through this function as an untyped working blob; the
|
|
392
397
|
// `as unknown as DossierV{4,5}Body` casts below are version-shim views that
|
|
393
398
|
// let the typed helpers operate on it. None of these casts is load-bearing
|
|
394
399
|
// for soundness: `DossierV5Schema.safeParse(fullDossier)` at the end is the
|
|
395
400
|
// single validation gate, and it throws before anything is returned.
|
|
396
|
-
let dossier = stampSynthesis(input.phase4Dossier, input.synthesis, input.phase2);
|
|
401
|
+
let dossier = stampSynthesis(logger, input.phase4Dossier, input.synthesis, input.phase2);
|
|
397
402
|
// Demote organ-claim flagged bullets into coverage_qualifier.unverified_bullets.
|
|
398
403
|
const exec = dossier.executive_recommendation;
|
|
399
404
|
if (exec?.coverage === "available") {
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import type { Pool } from "pg";
|
|
14
14
|
import type { TargetAssessmentPhase, TargetAssessmentProgressEvent } from "@inflexa-ai/harness/contracts/target-dossier.js";
|
|
15
|
+
import type { Logger } from "../../lib/logger.js";
|
|
15
16
|
/** DBOS stream key the SSE route reads via `DBOS.readStream(wfId, "progress")`. */
|
|
16
17
|
export declare const TA_PROGRESS_STREAM_KEY = "progress";
|
|
17
18
|
export type ProgressPhase = TargetAssessmentPhase;
|
|
@@ -35,4 +36,4 @@ export declare const PROGRESS_MESSAGES: Record<ProgressPhase, string>;
|
|
|
35
36
|
* Best-effort: a DB or stream failure logs and continues — progress
|
|
36
37
|
* emission MUST NOT block the workflow.
|
|
37
38
|
*/
|
|
38
|
-
export declare function emitProgress(pool: Pool, assessmentId: string, phase: ProgressPhase): Promise<void>;
|
|
39
|
+
export declare function emitProgress(pool: Pool, logger: Logger, assessmentId: string, phase: ProgressPhase): Promise<void>;
|
|
@@ -46,7 +46,8 @@ export const PROGRESS_MESSAGES = {
|
|
|
46
46
|
* Best-effort: a DB or stream failure logs and continues — progress
|
|
47
47
|
* emission MUST NOT block the workflow.
|
|
48
48
|
*/
|
|
49
|
-
export async function emitProgress(pool, assessmentId, phase) {
|
|
49
|
+
export async function emitProgress(pool, logger, assessmentId, phase) {
|
|
50
|
+
const log = logger.named("ta-progress").with({ assessmentId, phase });
|
|
50
51
|
const message = PROGRESS_MESSAGES[phase];
|
|
51
52
|
const percent = PROGRESS_PERCENTS[phase];
|
|
52
53
|
await DBOS.runStep(async () => {
|
|
@@ -58,7 +59,7 @@ export async function emitProgress(pool, assessmentId, phase) {
|
|
|
58
59
|
await DBOS.writeStream(TA_PROGRESS_STREAM_KEY, part);
|
|
59
60
|
}
|
|
60
61
|
catch (err) {
|
|
61
|
-
|
|
62
|
+
log.warn("writeStream failed", log.errorFields(err));
|
|
62
63
|
}
|
|
63
64
|
// The "suspended" phase is owned by the terminal handler
|
|
64
65
|
// (`markAssessmentSuspended` already set status + progress text). A
|
|
@@ -69,7 +70,7 @@ export async function emitProgress(pool, assessmentId, phase) {
|
|
|
69
70
|
// emission MUST NOT block the workflow. `updateProgress` returns
|
|
70
71
|
// its DB failure as an `Err`, so handle both branches here.
|
|
71
72
|
(await updateProgress(pool, assessmentId, message, rowStatus)).match(() => { }, (error) => {
|
|
72
|
-
|
|
73
|
+
log.warn("updateProgress failed", { err: describeDbError(error) });
|
|
73
74
|
});
|
|
74
75
|
}
|
|
75
76
|
}, { name: `ta-progress:${phase}` });
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
* OUTSIDE `tryFs`, so only the discrete `fs.open` / `fs.read` / `fs.readFile`
|
|
29
29
|
* calls become `err`.
|
|
30
30
|
*/
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
31
|
+
import { lstat as fsLstat, open as fsOpen, readdir as fsReaddir, stat as fsStat } from "node:fs/promises";
|
|
32
|
+
import { constants as fsConstants } from "node:fs";
|
|
33
33
|
import { createInterface } from "node:readline";
|
|
34
34
|
import { join } from "node:path";
|
|
35
35
|
import { ResultAsync, err, errAsync, ok } from "neverthrow";
|
|
@@ -123,10 +123,20 @@ export function createWorkspaceFilesystem(deps) {
|
|
|
123
123
|
return okAsync({ kind: "out_of_scope" });
|
|
124
124
|
}
|
|
125
125
|
const absolute = resolved.absolute;
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
126
|
+
// `lstat` first so a symlinked directory leaf is refused, not
|
|
127
|
+
// followed: a plain `readdir` would list the target's entries,
|
|
128
|
+
// and a leaf swapped to an escaping symlink after the realpath
|
|
129
|
+
// check would leak an off-tree directory's names. Node has no
|
|
130
|
+
// fd-based readdir, so this narrows (does not fully close) the
|
|
131
|
+
// readdir race — the atomic guarantee is on the content reads.
|
|
132
|
+
return safeLstat(absolute).andThen((s) => {
|
|
133
|
+
if (s?.isSymbolicLink())
|
|
134
|
+
return okAsync({ kind: "out_of_scope" });
|
|
135
|
+
return tryFs("workspace.list", () => fsReaddir(absolute, { withFileTypes: true }), { path: absolute, onAbsent: () => ({ notFound: true }) }).andThen((dirents) => {
|
|
136
|
+
if ("notFound" in dirents)
|
|
137
|
+
return okAsync({ kind: "not_found" });
|
|
138
|
+
return collectEntries(absolute, dirents).map((entries) => ({ kind: "ok", entries }));
|
|
139
|
+
});
|
|
130
140
|
});
|
|
131
141
|
});
|
|
132
142
|
},
|
|
@@ -135,9 +145,16 @@ export function createWorkspaceFilesystem(deps) {
|
|
|
135
145
|
if (resolved.kind === "out_of_scope") {
|
|
136
146
|
return okAsync({ kind: "out_of_scope" });
|
|
137
147
|
}
|
|
138
|
-
|
|
148
|
+
// `lstat`, not `stat`: a leaf symlink is refused rather than
|
|
149
|
+
// followed — same policy as the read open's `O_NOFOLLOW`.
|
|
150
|
+
// Reporting a followed target's type/size would re-open the
|
|
151
|
+
// metadata side of the confused-deputy leak, and it is atomic
|
|
152
|
+
// (a single non-following syscall, no check/use gap).
|
|
153
|
+
return safeLstat(resolved.absolute).map((s) => {
|
|
139
154
|
if (!s)
|
|
140
155
|
return { kind: "not_found" };
|
|
156
|
+
if (s.isSymbolicLink())
|
|
157
|
+
return { kind: "out_of_scope" };
|
|
141
158
|
return {
|
|
142
159
|
kind: "ok",
|
|
143
160
|
type: s.isDirectory() ? "directory" : "file",
|
|
@@ -160,29 +177,89 @@ function safeStat(absolute) {
|
|
|
160
177
|
onAbsent: () => null,
|
|
161
178
|
});
|
|
162
179
|
}
|
|
163
|
-
/**
|
|
180
|
+
/** Like {@link safeStat} but NEVER follows a leaf symlink (`lstat`) — the seam's
|
|
181
|
+
* read-side no-follow policy for the `stat`/`list` metadata surface. */
|
|
182
|
+
function safeLstat(absolute) {
|
|
183
|
+
return tryFs("workspace.lstat", () => fsLstat(absolute), {
|
|
184
|
+
path: absolute,
|
|
185
|
+
onAbsent: () => null,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
/** Lstat each directory entry to attach a size; absence/I/O failures degrade to
|
|
189
|
+
* no size. `lstat` (not `stat`) so a symlink entry reports its own size, never
|
|
190
|
+
* its target's — a followed target could be off-tree. `d.isDirectory()` comes
|
|
191
|
+
* from the dirent, which does not follow, so a symlink is a plain `file` entry. */
|
|
164
192
|
function collectEntries(dir, dirents) {
|
|
165
193
|
return ResultAsync.combine(dirents.map((d) => {
|
|
166
194
|
if (d.isDirectory())
|
|
167
195
|
return okAsync({ name: d.name, type: "directory" });
|
|
168
|
-
return
|
|
196
|
+
return safeLstat(join(dir, d.name)).map((s) => ({ name: d.name, type: "file", size: s?.size ?? undefined }));
|
|
169
197
|
}));
|
|
170
198
|
}
|
|
199
|
+
/**
|
|
200
|
+
* Read-only open flags that REFUSE to follow a final-component symlink.
|
|
201
|
+
* `classifyWithinRoot` validated a snapshot of the path; a symlink swapped into
|
|
202
|
+
* the leaf between that check and the open here would otherwise be followed
|
|
203
|
+
* off-tree, re-opening the read-exfiltration hole for the length of the race
|
|
204
|
+
* (CWE-367). `O_NOFOLLOW` makes the leaf open atomic — a symlinked final
|
|
205
|
+
* component fails `ELOOP` in the same syscall that opens it, so there is no
|
|
206
|
+
* check/use gap. `?? 0` keeps the mask valid on a platform that lacks the flag.
|
|
207
|
+
*
|
|
208
|
+
* This refuses EVERY leaf symlink, including an in-tree one the persistent check
|
|
209
|
+
* would allow: pure Node cannot express "follow only if it stays in-tree" (that
|
|
210
|
+
* needs `openat2(RESOLVE_BENEATH)`), so the atomic, race-free choice is to not
|
|
211
|
+
* follow the leaf at all. Hard-linked staged inputs are unaffected — a hard link
|
|
212
|
+
* is not a symlink. An intermediate-directory symlink is beyond this guard (Node
|
|
213
|
+
* exposes no `openat2`); the upstream realpath check still rejects the
|
|
214
|
+
* persistent case of that.
|
|
215
|
+
*/
|
|
216
|
+
const READ_NO_FOLLOW = fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0);
|
|
217
|
+
/**
|
|
218
|
+
* Open a workspace file read-only without following a leaf symlink (see
|
|
219
|
+
* {@link READ_NO_FOLLOW}). A symlinked final component surfaces as `out_of_scope`
|
|
220
|
+
* — the same verdict a persistent escape gets — never as off-tree bytes. Every
|
|
221
|
+
* content read (`readWithCap`, `readHeadLines`, `readTailLines`) opens through
|
|
222
|
+
* here so the bytes it streams come from a non-symlink leaf. Absence /
|
|
223
|
+
* permission / other I/O throws flow the `FsError` channel unchanged.
|
|
224
|
+
*/
|
|
225
|
+
function openReadNoFollow(absolute) {
|
|
226
|
+
return tryFs("workspace.openNoFollow", async () => {
|
|
227
|
+
try {
|
|
228
|
+
return { kind: "ok", fh: await fsOpen(absolute, READ_NO_FOLLOW) };
|
|
229
|
+
}
|
|
230
|
+
catch (cause) {
|
|
231
|
+
if (cause.code === "ELOOP")
|
|
232
|
+
return { kind: "out_of_scope" };
|
|
233
|
+
throw cause;
|
|
234
|
+
}
|
|
235
|
+
}, { path: absolute });
|
|
236
|
+
}
|
|
171
237
|
function readWithCap(absolute, totalSize, maxBytes) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
238
|
+
return openReadNoFollow(absolute).andThen((opened) => {
|
|
239
|
+
if (opened.kind === "out_of_scope")
|
|
240
|
+
return okAsync({ kind: "out_of_scope" });
|
|
241
|
+
const fh = opened.fh;
|
|
242
|
+
if (maxBytes !== undefined && totalSize > maxBytes) {
|
|
243
|
+
return tryFs("workspace.readCapped", async () => {
|
|
244
|
+
try {
|
|
245
|
+
const buf = Buffer.alloc(maxBytes);
|
|
246
|
+
await fh.read(buf, 0, maxBytes, 0);
|
|
247
|
+
return { kind: "truncated", content: buf, totalSize };
|
|
248
|
+
}
|
|
249
|
+
finally {
|
|
250
|
+
await fh.close();
|
|
251
|
+
}
|
|
252
|
+
}, { path: absolute });
|
|
253
|
+
}
|
|
254
|
+
return tryFs("workspace.readFile", async () => {
|
|
175
255
|
try {
|
|
176
|
-
|
|
177
|
-
await fh.read(buf, 0, maxBytes, 0);
|
|
178
|
-
return { kind: "truncated", content: buf, totalSize };
|
|
256
|
+
return { kind: "ok", content: await fh.readFile(), truncated: false };
|
|
179
257
|
}
|
|
180
258
|
finally {
|
|
181
259
|
await fh.close();
|
|
182
260
|
}
|
|
183
261
|
}, { path: absolute });
|
|
184
|
-
}
|
|
185
|
-
return tryFs("workspace.readFile", async () => ({ kind: "ok", content: await fsReadFile(absolute), truncated: false }), { path: absolute });
|
|
262
|
+
});
|
|
186
263
|
}
|
|
187
264
|
function capBuffer(content, maxBytes) {
|
|
188
265
|
if (maxBytes !== undefined && content.length > maxBytes) {
|
|
@@ -204,43 +281,51 @@ function capBuffer(content, maxBytes) {
|
|
|
204
281
|
* follow-up `safeStat` (a discrete `fs` call) flows the `FsError` channel.
|
|
205
282
|
*/
|
|
206
283
|
function readHeadLines(absolute, headLines, maxBytes) {
|
|
207
|
-
return
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
284
|
+
return openReadNoFollow(absolute).andThen((opened) => {
|
|
285
|
+
if (opened.kind === "out_of_scope")
|
|
286
|
+
return okAsync({ kind: "out_of_scope" });
|
|
287
|
+
const fh = opened.fh;
|
|
288
|
+
return new ResultAsync((async () => {
|
|
289
|
+
// `autoClose: false` keeps the FileHandle ours to close — destroying
|
|
290
|
+
// the stream must not race a close against our own `fh.close()`.
|
|
291
|
+
const stream = fh.createReadStream({ highWaterMark: 16 * 1024, autoClose: false });
|
|
292
|
+
const rl = createInterface({ input: stream, crlfDelay: Infinity });
|
|
293
|
+
const collected = [];
|
|
294
|
+
let bytes = 0;
|
|
295
|
+
let truncated = false;
|
|
296
|
+
try {
|
|
297
|
+
for await (const line of rl) {
|
|
298
|
+
const lineBytes = Buffer.byteLength(line, "utf8") + 1;
|
|
299
|
+
if (maxBytes !== undefined && bytes + lineBytes > maxBytes) {
|
|
300
|
+
truncated = true;
|
|
301
|
+
break;
|
|
302
|
+
}
|
|
303
|
+
collected.push(line);
|
|
304
|
+
bytes += lineBytes;
|
|
305
|
+
if (collected.length >= headLines)
|
|
306
|
+
break;
|
|
219
307
|
}
|
|
220
|
-
collected.push(line);
|
|
221
|
-
bytes += lineBytes;
|
|
222
|
-
if (collected.length >= headLines)
|
|
223
|
-
break;
|
|
224
308
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
309
|
+
finally {
|
|
310
|
+
rl.close();
|
|
311
|
+
stream.destroy();
|
|
312
|
+
await fh.close();
|
|
313
|
+
}
|
|
314
|
+
const content = Buffer.from(collected.join("\n"));
|
|
315
|
+
if (truncated) {
|
|
316
|
+
// Total size is unknown without a full stat; report it as a separate read.
|
|
317
|
+
return (await safeStat(absolute)).map((s) => ({
|
|
318
|
+
kind: "truncated",
|
|
319
|
+
content,
|
|
320
|
+
totalSize: s?.size ?? content.length,
|
|
321
|
+
}));
|
|
322
|
+
}
|
|
323
|
+
// Whether the loop exhausted the file or hit the line cap, the agent asked
|
|
324
|
+
// for exactly this window, so this is `ok` not `truncated`. The tool layer
|
|
325
|
+
// reports `mode: "head"` so the agent knows it's a window.
|
|
326
|
+
return ok({ kind: "ok", content, truncated: false });
|
|
327
|
+
})());
|
|
328
|
+
});
|
|
244
329
|
}
|
|
245
330
|
/**
|
|
246
331
|
* Read the last `tailLines` lines via a windowed read from the end. The
|
|
@@ -252,30 +337,34 @@ function readHeadLines(absolute, headLines, maxBytes) {
|
|
|
252
337
|
* slicing is pure and runs inside the same `fn`.
|
|
253
338
|
*/
|
|
254
339
|
function readTailLines(absolute, totalSize, tailLines, maxBytes) {
|
|
255
|
-
return
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
const fh =
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
340
|
+
return openReadNoFollow(absolute).andThen((opened) => {
|
|
341
|
+
if (opened.kind === "out_of_scope")
|
|
342
|
+
return okAsync({ kind: "out_of_scope" });
|
|
343
|
+
const fh = opened.fh;
|
|
344
|
+
return tryFs("workspace.readTail", async () => {
|
|
345
|
+
try {
|
|
346
|
+
const windowSize = Math.min(totalSize, maxBytes ?? totalSize);
|
|
347
|
+
const offset = totalSize - windowSize;
|
|
348
|
+
const buf = Buffer.alloc(windowSize);
|
|
349
|
+
if (windowSize > 0) {
|
|
350
|
+
await fh.read(buf, 0, windowSize, offset);
|
|
351
|
+
}
|
|
352
|
+
const allLines = buf.toString("utf8").split(/\r?\n/);
|
|
353
|
+
// Drop the partial leading line if the window doesn't cover the file start.
|
|
354
|
+
const candidateLines = offset > 0 ? allLines.slice(1) : allLines;
|
|
355
|
+
const sliced = candidateLines.length > tailLines ? candidateLines.slice(-tailLines) : candidateLines;
|
|
356
|
+
const content = Buffer.from(sliced.join("\n"));
|
|
357
|
+
const gotEnoughLines = sliced.length >= tailLines || offset === 0;
|
|
358
|
+
if (!gotEnoughLines) {
|
|
359
|
+
return { kind: "truncated", content, totalSize };
|
|
360
|
+
}
|
|
361
|
+
return { kind: "ok", content, truncated: false };
|
|
263
362
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
const candidateLines = offset > 0 ? allLines.slice(1) : allLines;
|
|
267
|
-
const sliced = candidateLines.length > tailLines ? candidateLines.slice(-tailLines) : candidateLines;
|
|
268
|
-
const content = Buffer.from(sliced.join("\n"));
|
|
269
|
-
const gotEnoughLines = sliced.length >= tailLines || offset === 0;
|
|
270
|
-
if (!gotEnoughLines) {
|
|
271
|
-
return { kind: "truncated", content, totalSize };
|
|
363
|
+
finally {
|
|
364
|
+
await fh.close();
|
|
272
365
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
finally {
|
|
276
|
-
await fh.close();
|
|
277
|
-
}
|
|
278
|
-
}, { path: absolute });
|
|
366
|
+
}, { path: absolute });
|
|
367
|
+
});
|
|
279
368
|
}
|
|
280
369
|
function sliceHeadLines(content, headLines, maxBytes) {
|
|
281
370
|
const capped = maxBytes !== undefined && content.length > maxBytes ? content.subarray(0, maxBytes) : content;
|
|
@@ -127,8 +127,8 @@ export declare function stepWritePrefix(args: {
|
|
|
127
127
|
* in-sandbox absolute path. The tree is bind-mounted at `/{resourceId}`, so
|
|
128
128
|
* the sandbox path is the resource id plus the root-relative tail — the host
|
|
129
129
|
* location of the root never leaks into the container. Single source of truth
|
|
130
|
-
* for the `execute_command` / `write_file` cwd and the
|
|
131
|
-
*
|
|
130
|
+
* for the `execute_command` / `write_file` cwd and for the working directory
|
|
131
|
+
* a step briefing names (see the harness-workspace-tools spec).
|
|
132
132
|
*/
|
|
133
133
|
export declare function toSandboxPath(workspaceRoot: string, resourceId: string, hostAbsPath: string): string;
|
|
134
134
|
/**
|
package/dist/workspace/paths.js
CHANGED
|
@@ -101,8 +101,8 @@ export function stepWritePrefix(args) {
|
|
|
101
101
|
* in-sandbox absolute path. The tree is bind-mounted at `/{resourceId}`, so
|
|
102
102
|
* the sandbox path is the resource id plus the root-relative tail — the host
|
|
103
103
|
* location of the root never leaks into the container. Single source of truth
|
|
104
|
-
* for the `execute_command` / `write_file` cwd and the
|
|
105
|
-
*
|
|
104
|
+
* for the `execute_command` / `write_file` cwd and for the working directory
|
|
105
|
+
* a step briefing names (see the harness-workspace-tools spec).
|
|
106
106
|
*/
|
|
107
107
|
export function toSandboxPath(workspaceRoot, resourceId, hostAbsPath) {
|
|
108
108
|
const tail = relativePath(workspaceRoot, hostAbsPath).split(sep).join("/");
|