@vostride/agent-qa-agent-local 0.2.2 → 0.2.4
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 +78 -0
- package/dist/index.cjs +452 -600
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +299 -1197
- package/dist/index.d.ts +299 -1197
- package/dist/index.js +401 -561
- package/dist/index.js.map +1 -1
- package/package.json +12 -11
package/dist/index.cjs
CHANGED
|
@@ -30,7 +30,6 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
-
ALLOW_PRODUCTION_OPT_IN: () => import_agent_qa_schema.ALLOW_PRODUCTION_OPT_IN,
|
|
34
33
|
BRIEF_CEILING: () => BRIEF_CEILING,
|
|
35
34
|
BRIEF_FRACTION: () => BRIEF_FRACTION,
|
|
36
35
|
BuildOrdinalReservationError: () => import_agent_qa_runner_local4.BuildOrdinalReservationError,
|
|
@@ -47,25 +46,32 @@ __export(index_exports, {
|
|
|
47
46
|
EMBEDDER_WORKER_TERM_GRACE_MS: () => import_agent_qa_knowledge_local.EMBEDDER_WORKER_TERM_GRACE_MS,
|
|
48
47
|
ENTITY_INDEX_SWEEP_INTERVAL_MS: () => import_agent_qa_knowledge_local.ENTITY_INDEX_SWEEP_INTERVAL_MS,
|
|
49
48
|
EmbedderWorkerTimeoutError: () => import_agent_qa_knowledge_local.EmbedderWorkerTimeoutError,
|
|
49
|
+
FsMarkLedger: () => FsMarkLedger,
|
|
50
50
|
GLOBAL_RECORD_CAP: () => GLOBAL_RECORD_CAP,
|
|
51
|
+
HANDOFF_FILE: () => import_agent_qa_runner_local6.HANDOFF_FILE,
|
|
51
52
|
InvalidChatSessionIdError: () => InvalidChatSessionIdError,
|
|
52
53
|
JOB_RECOVERY_MAX_RESUMED_JOBS: () => JOB_RECOVERY_MAX_RESUMED_JOBS,
|
|
53
54
|
LOCAL_CURATION_CAPABILITIES: () => LOCAL_CURATION_CAPABILITIES,
|
|
54
55
|
LOCAL_DAEMON_CAPABILITIES: () => LOCAL_DAEMON_CAPABILITIES,
|
|
55
56
|
LOCAL_KNOWLEDGE_PROBES: () => LOCAL_KNOWLEDGE_PROBES,
|
|
56
57
|
LocalAgentClosedError: () => LocalAgentClosedError,
|
|
58
|
+
MARKS_LEDGER_FILE: () => MARKS_LEDGER_FILE,
|
|
59
|
+
MARKS_LEDGER_LOCK_FILE: () => MARKS_LEDGER_LOCK_FILE,
|
|
60
|
+
MARK_INTENT_FILE: () => MARK_INTENT_FILE,
|
|
57
61
|
MIN_BRIEF: () => MIN_BRIEF,
|
|
58
62
|
MIN_CAUTION: () => MIN_CAUTION,
|
|
59
63
|
MIN_MEMORY_TOOL_CONTEXT_WINDOW: () => MIN_MEMORY_TOOL_CONTEXT_WINDOW,
|
|
64
|
+
MarkIntentUnusableError: () => MarkIntentUnusableError,
|
|
65
|
+
MarkLedgerBusyError: () => MarkLedgerBusyError,
|
|
66
|
+
MarkLedgerCorruptError: () => MarkLedgerCorruptError,
|
|
67
|
+
MarkLedgerNoRunError: () => MarkLedgerNoRunError,
|
|
60
68
|
QUARANTINE_CLASS_DISPOSITIONS: () => import_reader3.QUARANTINE_CLASS_DISPOSITIONS,
|
|
61
69
|
QUARANTINE_FLOOR_ELIGIBILITY: () => import_reader3.QUARANTINE_FLOOR_ELIGIBILITY,
|
|
62
70
|
READ_BODY_FRACTION: () => READ_BODY_FRACTION,
|
|
63
|
-
|
|
64
|
-
RunDeniedError: () => RunDeniedError,
|
|
71
|
+
RUNS_SUBDIR: () => import_agent_qa_runner_local6.RUNS_SUBDIR,
|
|
65
72
|
RunIdAlreadyInUseError: () => RunIdAlreadyInUseError,
|
|
66
73
|
RunIdNotMintedError: () => RunIdNotMintedError,
|
|
67
74
|
RunProducedNoEventsError: () => RunProducedNoEventsError,
|
|
68
|
-
RunRequestFieldRefusedError: () => RunRequestFieldRefusedError,
|
|
69
75
|
SUITE_LIFECYCLE_LANE: () => SUITE_LIFECYCLE_LANE,
|
|
70
76
|
SYSTEM_CLOCK: () => SYSTEM_CLOCK,
|
|
71
77
|
SuiteBoundaryWhileLiveError: () => SuiteBoundaryWhileLiveError,
|
|
@@ -74,7 +80,6 @@ __export(index_exports, {
|
|
|
74
80
|
WEAK_MODEL_CAP_SCALE: () => WEAK_MODEL_CAP_SCALE,
|
|
75
81
|
applyKnowledgeChange: () => applyKnowledgeChange,
|
|
76
82
|
buildMemberRunConfig: () => buildMemberRunConfig,
|
|
77
|
-
buildProductionRefusal: () => import_agent_qa_schema.buildProductionRefusal,
|
|
78
83
|
buildRunPorts: () => buildRunPorts,
|
|
79
84
|
buildScreenshotCompressor: () => buildScreenshotCompressor,
|
|
80
85
|
buildSuiteMemberExecutor: () => buildSuiteMemberExecutor,
|
|
@@ -94,15 +99,17 @@ __export(index_exports, {
|
|
|
94
99
|
createRunsReader: () => createRunsReader,
|
|
95
100
|
createSuiteLifecycle: () => createSuiteLifecycle,
|
|
96
101
|
latestBuildOrdinal: () => import_agent_qa_runner_local4.latestBuildOrdinal,
|
|
102
|
+
latestMarkReceiptFor: () => latestMarkReceiptFor,
|
|
103
|
+
latestMarkReceiptsPerAddress: () => latestMarkReceiptsPerAddress,
|
|
97
104
|
listSessions: () => listSessions,
|
|
105
|
+
loadCompletedRunHandoff: () => import_agent_qa_runner_local6.loadCompletedRunHandoff,
|
|
98
106
|
lookupEvidenceBlob: () => lookupEvidenceBlob,
|
|
99
107
|
mapVerdictToExitCode: () => import_agent_qa_runner3.mapVerdictToExitCode,
|
|
100
108
|
memoryToolGate: () => memoryToolGate,
|
|
101
109
|
mintChatSessionId: () => mintChatSessionId,
|
|
102
110
|
openRunKnowledgeComposition: () => openRunKnowledgeComposition,
|
|
111
|
+
purgeActionCache: () => import_agent_qa_runner_local7.purgeActionCache,
|
|
103
112
|
readEvidenceBlobBytes: () => readEvidenceBlobBytes,
|
|
104
|
-
refuseProductionWithoutOptIn: () => refuseProductionWithoutOptIn,
|
|
105
|
-
refuseUnavailableRunId: () => refuseUnavailableRunId,
|
|
106
113
|
registerAdapterLiveStream: () => registerAdapterLiveStream,
|
|
107
114
|
reserveBuildOrdinalForProducts: () => import_agent_qa_runner_local4.reserveBuildOrdinalForProducts,
|
|
108
115
|
resolveBuildTag: () => import_agent_qa_runner_local5.resolveBuildTag,
|
|
@@ -189,7 +196,6 @@ function createFrozenKnowledgeReader(reader) {
|
|
|
189
196
|
}
|
|
190
197
|
function constructKnowledgeReader(input) {
|
|
191
198
|
return createFrozenKnowledgeReader((0, import_reader.openKnowledgeReader)({
|
|
192
|
-
...input.resolveTrustPriors === void 0 ? {} : { priors: input.resolveTrustPriors() },
|
|
193
199
|
knowledgeRoot: input.knowledgeRoot,
|
|
194
200
|
...input.product === void 0 ? {} : { product: input.product },
|
|
195
201
|
scopeDefaults: {
|
|
@@ -220,7 +226,6 @@ function openRunKnowledgeComposition(input) {
|
|
|
220
226
|
recordCap: scaledRecordCap(input.capabilities)
|
|
221
227
|
};
|
|
222
228
|
const reader = constructKnowledgeReader({
|
|
223
|
-
...input.resolveTrustPriors === void 0 ? {} : { resolveTrustPriors: input.resolveTrustPriors },
|
|
224
229
|
knowledgeRoot: input.knowledgeRoot,
|
|
225
230
|
...input.product === void 0 ? {} : { product: input.product },
|
|
226
231
|
...input.parentScopes === void 0 ? {} : { parentScopes: input.parentScopes },
|
|
@@ -471,8 +476,7 @@ function buildMemberRunConfig(input) {
|
|
|
471
476
|
cache: input.cache,
|
|
472
477
|
workingMemory: input.workingMemory,
|
|
473
478
|
storageState: input.storageState,
|
|
474
|
-
//
|
|
475
|
-
// otherwise) replaces the runner-local ceiling fallback on every tool-output value.
|
|
479
|
+
// The model's canonical compaction applies to every tool-output value.
|
|
476
480
|
guardBlob: import_agent_qa_model.guardToolOutput,
|
|
477
481
|
compressor: input.compressor,
|
|
478
482
|
knowledgeReader: input.knowledgeReader,
|
|
@@ -527,24 +531,6 @@ function buildMemberRunConfig(input) {
|
|
|
527
531
|
var import_node_fs = require("node:fs");
|
|
528
532
|
var import_node_path2 = require("node:path");
|
|
529
533
|
var import_agent_qa_runner2 = require("@vostride/agent-qa-runner");
|
|
530
|
-
|
|
531
|
-
// src/composition/run-risk.ts
|
|
532
|
-
var import_agent_qa_schema = require("@vostride/agent-qa-schema");
|
|
533
|
-
|
|
534
|
-
// src/composition/run-invocation-core.ts
|
|
535
|
-
var REFUSED_RUN_REQUEST_FIELDS = [
|
|
536
|
-
"scope",
|
|
537
|
-
"executionContext"
|
|
538
|
-
];
|
|
539
|
-
var RunRequestFieldRefusedError = class extends Error {
|
|
540
|
-
constructor(refusedFields) {
|
|
541
|
-
super(
|
|
542
|
-
`agent-qa cannot start a run with: ${refusedFields.join(", ")}. scope resolves from the config the run is started against, and executionContext is supplied by the composition that owns the door \u2014 a caller-named execution context would be a caller relabelling where its run came from.`
|
|
543
|
-
);
|
|
544
|
-
this.refusedFields = refusedFields;
|
|
545
|
-
this.name = "RunRequestFieldRefusedError";
|
|
546
|
-
}
|
|
547
|
-
};
|
|
548
534
|
var RunIdNotMintedError = class extends Error {
|
|
549
535
|
constructor(runId) {
|
|
550
536
|
super(
|
|
@@ -563,22 +549,6 @@ var RunIdAlreadyInUseError = class extends Error {
|
|
|
563
549
|
this.name = "RunIdAlreadyInUseError";
|
|
564
550
|
}
|
|
565
551
|
};
|
|
566
|
-
var RunDeniedError = class extends Error {
|
|
567
|
-
constructor(runId, reason) {
|
|
568
|
-
super(reason);
|
|
569
|
-
this.runId = runId;
|
|
570
|
-
this.reason = reason;
|
|
571
|
-
this.name = "RunDeniedError";
|
|
572
|
-
this.neutralReason = reason;
|
|
573
|
-
}
|
|
574
|
-
neutralReason;
|
|
575
|
-
};
|
|
576
|
-
function refuseProductionWithoutOptIn(runId, declaration) {
|
|
577
|
-
const production = declaration.productionTargetNames.length > 0 || declaration.targetClass === "production";
|
|
578
|
-
if (!production || declaration.allowProduction === true) return;
|
|
579
|
-
const reason = (0, import_agent_qa_schema.buildProductionRefusal)(declaration.productionTargetNames);
|
|
580
|
-
throw new RunDeniedError(runId, reason);
|
|
581
|
-
}
|
|
582
552
|
function refuseUnavailableRunId(input) {
|
|
583
553
|
if (!input.isMintedRunId(input.runId)) throw new RunIdNotMintedError(input.runId);
|
|
584
554
|
if ((0, import_node_fs.existsSync)(runDirFor({ configDir: input.configDir, runId: input.runId }))) {
|
|
@@ -589,8 +559,6 @@ function createRunInvocationCore(options) {
|
|
|
589
559
|
const configDir = (0, import_node_path2.dirname)(options.agentQaRoot);
|
|
590
560
|
return {
|
|
591
561
|
async execute(request) {
|
|
592
|
-
const refused = REFUSED_RUN_REQUEST_FIELDS.filter((field) => request[field] !== void 0);
|
|
593
|
-
if (refused.length > 0) throw new RunRequestFieldRefusedError(refused);
|
|
594
562
|
if (request.runId !== void 0) {
|
|
595
563
|
refuseUnavailableRunId({
|
|
596
564
|
configDir,
|
|
@@ -599,9 +567,6 @@ function createRunInvocationCore(options) {
|
|
|
599
567
|
});
|
|
600
568
|
}
|
|
601
569
|
const runId = request.runId ?? options.mintRunId();
|
|
602
|
-
const declaration = await options.resolveTargetDeclaration(request);
|
|
603
|
-
const targetClass = declaration.targetClass;
|
|
604
|
-
refuseProductionWithoutOptIn(runId, declaration);
|
|
605
570
|
await options.afterRefusal?.();
|
|
606
571
|
const ports = buildRunPorts({ configDir, runId });
|
|
607
572
|
const postVerdict = options.postVerdict?.({ runId, configDir });
|
|
@@ -614,8 +579,7 @@ function createRunInvocationCore(options) {
|
|
|
614
579
|
...postVerdict === void 0 ? {} : { postVerdict }
|
|
615
580
|
},
|
|
616
581
|
configDir,
|
|
617
|
-
configPath: request.configPath ?? options.configPath
|
|
618
|
-
targetClass
|
|
582
|
+
configPath: request.configPath ?? options.configPath
|
|
619
583
|
});
|
|
620
584
|
const session = (0, import_agent_qa_runner2.startRun)({
|
|
621
585
|
...assembled,
|
|
@@ -679,12 +643,12 @@ function readEvidenceBlobBytes(input, runId, sha) {
|
|
|
679
643
|
}
|
|
680
644
|
|
|
681
645
|
// src/composition/quarantine-snapshot.ts
|
|
682
|
-
var
|
|
646
|
+
var import_agent_qa_schema = require("@vostride/agent-qa-schema");
|
|
683
647
|
var import_reader2 = require("@vostride/agent-qa-knowledge-local/reader");
|
|
684
648
|
function resolveRunQuarantineSnapshot(input) {
|
|
685
649
|
const warn = input.warn ?? (() => {
|
|
686
650
|
});
|
|
687
|
-
const product = (0,
|
|
651
|
+
const product = (0, import_agent_qa_schema.slugBranch)(input.product);
|
|
688
652
|
let read;
|
|
689
653
|
try {
|
|
690
654
|
read = (0, import_reader2.readQuarantineLedger)(input.knowledgeRoot);
|
|
@@ -722,7 +686,7 @@ var import_agent_qa_knowledge_local = require("@vostride/agent-qa-knowledge-loca
|
|
|
722
686
|
var import_agent_qa_runner4 = require("@vostride/agent-qa-runner");
|
|
723
687
|
var import_agent_qa_safety = require("@vostride/agent-qa-safety");
|
|
724
688
|
var import_agent_qa_runner_local3 = require("@vostride/agent-qa-runner-local");
|
|
725
|
-
var
|
|
689
|
+
var import_agent_qa_schema9 = require("@vostride/agent-qa-schema");
|
|
726
690
|
|
|
727
691
|
// src/composition/knowledge-review-reader.ts
|
|
728
692
|
var import_reader4 = require("@vostride/agent-qa-knowledge-local/reader");
|
|
@@ -735,31 +699,20 @@ function createKnowledgeReaderPort(input) {
|
|
|
735
699
|
}
|
|
736
700
|
}
|
|
737
701
|
return {
|
|
738
|
-
//
|
|
739
|
-
// scans it; the chat and MCP doors mask declared values in every op result.
|
|
740
|
-
async candidateInjection({ candidateId }) {
|
|
741
|
-
return (0, import_reader4.candidateInjectionFor)({
|
|
742
|
-
...input.resolveTrustPriors === void 0 ? {} : { priors: input.resolveTrustPriors() },
|
|
743
|
-
knowledgeRoot: await input.resolveKnowledgeRoot(),
|
|
744
|
-
candidateId
|
|
745
|
-
});
|
|
746
|
-
},
|
|
747
|
-
// 416 D-17: the caller supplied the identity; forward the reader's answer verbatim.
|
|
702
|
+
// The caller supplied the identity; forward the reader's answer verbatim.
|
|
748
703
|
async gateReceipts({ candidateId }) {
|
|
749
704
|
return (0, import_reader4.gateReceiptsFor)({
|
|
750
705
|
knowledgeRoot: await input.resolveKnowledgeRoot(),
|
|
751
|
-
candidateId
|
|
752
|
-
resolvePromotionPosture: await input.resolvePromotionPosture?.()
|
|
706
|
+
candidateId
|
|
753
707
|
});
|
|
754
708
|
},
|
|
755
709
|
async listBundles() {
|
|
756
|
-
const rows = (0, import_reader4.listBundles)(await input.resolveKnowledgeRoot()
|
|
710
|
+
const rows = (0, import_reader4.listBundles)(await input.resolveKnowledgeRoot());
|
|
757
711
|
await sight(rows.map((row) => row.bundleId));
|
|
758
712
|
return rows;
|
|
759
713
|
},
|
|
760
714
|
async inspectRecords({ filter }) {
|
|
761
715
|
const records = (0, import_reader4.inspectRecords)({
|
|
762
|
-
...input.resolveTrustPriors === void 0 ? {} : { priors: input.resolveTrustPriors() },
|
|
763
716
|
knowledgeRoot: await input.resolveKnowledgeRoot(),
|
|
764
717
|
filter
|
|
765
718
|
});
|
|
@@ -776,7 +729,6 @@ function createKnowledgeReaderPort(input) {
|
|
|
776
729
|
},
|
|
777
730
|
async showRecord({ recordId }) {
|
|
778
731
|
const result = (0, import_reader4.showRecord)({
|
|
779
|
-
...input.resolveTrustPriors === void 0 ? {} : { priors: input.resolveTrustPriors() },
|
|
780
732
|
knowledgeRoot: await input.resolveKnowledgeRoot(),
|
|
781
733
|
recordId
|
|
782
734
|
});
|
|
@@ -788,19 +740,7 @@ function createKnowledgeReaderPort(input) {
|
|
|
788
740
|
async showRecords({ recordIds }) {
|
|
789
741
|
return (0, import_reader4.showRecords)({ knowledgeRoot: await input.resolveKnowledgeRoot(), recordIds });
|
|
790
742
|
},
|
|
791
|
-
|
|
792
|
-
* 407 D-08: what became of the canary lines one run served.
|
|
793
|
-
*
|
|
794
|
-
* Resolved per call like its four siblings, and NOT sighted. The caller already holds every
|
|
795
|
-
* candidate id in the tuples it passes — it read them off its own run's evidence — so a
|
|
796
|
-
* sighting here would record that the reader was handed ids rather than that an entity was
|
|
797
|
-
* surfaced to anyone, which is the distinction the ledger exists to keep.
|
|
798
|
-
*
|
|
799
|
-
* The reader entry's answer is forwarded VERBATIM, `skippedBundles` included (407-19, REVIEW
|
|
800
|
-
* WR-03). This binding neither counts nor interprets: how much of the store went unread is a
|
|
801
|
-
* fact only the walk knows, and a composition that dropped it here would hand the op a
|
|
802
|
-
* clean-looking answer over a half-read store.
|
|
803
|
-
*/
|
|
743
|
+
// Not sighted; `skippedBundles` is forwarded verbatim so a half-read store never looks clean.
|
|
804
744
|
async canaryAttributions({ runId, tuples }) {
|
|
805
745
|
return (0, import_reader4.canaryAttributionsFor)({
|
|
806
746
|
knowledgeRoot: await input.resolveKnowledgeRoot(),
|
|
@@ -815,7 +755,7 @@ function createKnowledgeReaderPort(input) {
|
|
|
815
755
|
var import_node_fs3 = require("node:fs");
|
|
816
756
|
var import_node_crypto2 = require("node:crypto");
|
|
817
757
|
var import_node_path4 = require("node:path");
|
|
818
|
-
var
|
|
758
|
+
var import_agent_qa_schema2 = require("@vostride/agent-qa-schema");
|
|
819
759
|
var ANALYSIS_STAGE_PREFIX = "analysis.json";
|
|
820
760
|
function fsyncFile(file) {
|
|
821
761
|
const fd = (0, import_node_fs3.openSync)(file, "r");
|
|
@@ -829,7 +769,7 @@ function writeAnalysisArtifact(path, artifact) {
|
|
|
829
769
|
const dir = (0, import_node_path4.dirname)(path);
|
|
830
770
|
const staged = (0, import_node_path4.join)(dir, `.${ANALYSIS_STAGE_PREFIX}.${(0, import_node_crypto2.randomUUID)()}.tmp`);
|
|
831
771
|
try {
|
|
832
|
-
(0, import_node_fs3.writeFileSync)(staged, `${(0,
|
|
772
|
+
(0, import_node_fs3.writeFileSync)(staged, `${(0, import_agent_qa_schema2.canonicalJson)(artifact)}
|
|
833
773
|
`, { encoding: "utf8", mode: 384 });
|
|
834
774
|
fsyncFile(staged);
|
|
835
775
|
(0, import_node_fs3.renameSync)(staged, path);
|
|
@@ -843,16 +783,16 @@ function writeAnalysisArtifact(path, artifact) {
|
|
|
843
783
|
var import_node_fs5 = require("node:fs");
|
|
844
784
|
var import_node_path6 = require("node:path");
|
|
845
785
|
var import_agent_qa_agent2 = require("@vostride/agent-qa-agent");
|
|
846
|
-
var
|
|
786
|
+
var import_agent_qa_schema5 = require("@vostride/agent-qa-schema");
|
|
847
787
|
|
|
848
788
|
// src/store/job-checkpoint-store.ts
|
|
849
789
|
var import_node_crypto3 = require("node:crypto");
|
|
850
790
|
var import_node_fs4 = require("node:fs");
|
|
851
791
|
var import_node_path5 = require("node:path");
|
|
852
|
-
var
|
|
792
|
+
var import_agent_qa_schema4 = require("@vostride/agent-qa-schema");
|
|
853
793
|
|
|
854
794
|
// src/store/store-errors.ts
|
|
855
|
-
var
|
|
795
|
+
var import_agent_qa_schema3 = require("@vostride/agent-qa-schema");
|
|
856
796
|
var import_agent_qa_agent = require("@vostride/agent-qa-agent");
|
|
857
797
|
var JobStoreSchemaVersionError = class extends Error {
|
|
858
798
|
constructor(path, expectedVersion, observedVersion) {
|
|
@@ -885,7 +825,7 @@ var InvalidJobCheckpointIdError = class extends Error {
|
|
|
885
825
|
var JobCheckpointSchemaVersionError = class extends Error {
|
|
886
826
|
constructor(path, observedVersion) {
|
|
887
827
|
super(
|
|
888
|
-
`Agent job checkpoint ${path} was written at schema version ${String(observedVersion)}, but this build reads version ${
|
|
828
|
+
`Agent job checkpoint ${path} was written at schema version ${String(observedVersion)}, but this build reads version ${import_agent_qa_schema3.JOB_CHECKPOINT_SCHEMA_VERSION}. There is no migration machinery by design \u2014 clear agent job queue state and re-enqueue.`
|
|
889
829
|
);
|
|
890
830
|
this.path = path;
|
|
891
831
|
this.observedVersion = observedVersion;
|
|
@@ -938,10 +878,10 @@ function readEnvelopeOn(fs, root, jobId, checkpointId) {
|
|
|
938
878
|
throw error;
|
|
939
879
|
}
|
|
940
880
|
const raw = JSON.parse(text);
|
|
941
|
-
if (raw?.schemaVersion !==
|
|
881
|
+
if (raw?.schemaVersion !== import_agent_qa_schema4.JOB_CHECKPOINT_SCHEMA_VERSION) {
|
|
942
882
|
throw new JobCheckpointSchemaVersionError(file, raw?.schemaVersion);
|
|
943
883
|
}
|
|
944
|
-
const envelope =
|
|
884
|
+
const envelope = import_agent_qa_schema4.jobCheckpointEnvelopeSchema.parse(raw);
|
|
945
885
|
if (envelope.jobId !== jobId || envelope.checkpointId !== checkpointId) {
|
|
946
886
|
throw new TypeError("Agent job checkpoint envelope does not match its address");
|
|
947
887
|
}
|
|
@@ -984,8 +924,8 @@ function createJobCheckpointStoreOn(fs, options) {
|
|
|
984
924
|
publish(jobId, checkpointId, payload) {
|
|
985
925
|
const file = pathFor(jobId, checkpointId);
|
|
986
926
|
const envelope = {
|
|
987
|
-
schemaVersion:
|
|
988
|
-
jobId: (0,
|
|
927
|
+
schemaVersion: import_agent_qa_schema4.JOB_CHECKPOINT_SCHEMA_VERSION,
|
|
928
|
+
jobId: (0, import_agent_qa_schema4.asJobId)(jobId),
|
|
989
929
|
checkpointId,
|
|
990
930
|
payload
|
|
991
931
|
};
|
|
@@ -994,7 +934,7 @@ function createJobCheckpointStoreOn(fs, options) {
|
|
|
994
934
|
let fd;
|
|
995
935
|
try {
|
|
996
936
|
fd = fs.openSync(temporary, "wx");
|
|
997
|
-
fs.writeFileSync(fd, `${(0,
|
|
937
|
+
fs.writeFileSync(fd, `${(0, import_agent_qa_schema4.canonicalJson)(envelope)}
|
|
998
938
|
`, "utf8");
|
|
999
939
|
fs.fsyncSync(fd);
|
|
1000
940
|
fs.closeSync(fd);
|
|
@@ -1088,10 +1028,10 @@ function guardHeader(sessionId, line) {
|
|
|
1088
1028
|
throw new ChatSessionHeaderError(sessionId, "its header line is not an object");
|
|
1089
1029
|
}
|
|
1090
1030
|
const header = parsed;
|
|
1091
|
-
if (header.schemaVersion !==
|
|
1031
|
+
if (header.schemaVersion !== import_agent_qa_schema5.CURRENT_CHAT_SESSION_SCHEMA_VERSION) {
|
|
1092
1032
|
throw new ChatSessionHeaderError(
|
|
1093
1033
|
sessionId,
|
|
1094
|
-
`it declares schemaVersion ${String(header.schemaVersion)} and this build reads ` + String(
|
|
1034
|
+
`it declares schemaVersion ${String(header.schemaVersion)} and this build reads ` + String(import_agent_qa_schema5.CURRENT_CHAT_SESSION_SCHEMA_VERSION)
|
|
1095
1035
|
);
|
|
1096
1036
|
}
|
|
1097
1037
|
if (header.sessionId !== sessionId) {
|
|
@@ -1290,16 +1230,12 @@ function createJsonlSessionLogOn(fs, options) {
|
|
|
1290
1230
|
const createdAt = options.now();
|
|
1291
1231
|
const id = mintChatSessionId(createdAt, random);
|
|
1292
1232
|
const header = {
|
|
1293
|
-
schemaVersion:
|
|
1233
|
+
schemaVersion: import_agent_qa_schema5.CURRENT_CHAT_SESSION_SCHEMA_VERSION,
|
|
1294
1234
|
sessionId: id,
|
|
1295
1235
|
createdAt,
|
|
1296
1236
|
model: options.model(),
|
|
1297
1237
|
toolsetHash: options.toolsetHash(),
|
|
1298
|
-
//
|
|
1299
|
-
// unconditional `surface: options.surface` would put `"surface":undefined` through
|
|
1300
|
-
// `JSON.stringify` — which drops it — but would also make an explicit `undefined` and an
|
|
1301
|
-
// unsupplied option two spellings of one state at every call site above. Absent here means
|
|
1302
|
-
// the option was never given, and that is exactly the pre-400 population's meaning.
|
|
1238
|
+
// Spread conditionally, so absent means the option is not given.
|
|
1303
1239
|
...options.surface === void 0 ? {} : { surface: options.surface }
|
|
1304
1240
|
};
|
|
1305
1241
|
const created = fs.openSync(pathFor(id), "wx");
|
|
@@ -1321,20 +1257,20 @@ function createJsonlSessionLogOn(fs, options) {
|
|
|
1321
1257
|
var import_node_fs8 = require("node:fs");
|
|
1322
1258
|
var import_node_path9 = require("node:path");
|
|
1323
1259
|
var import_node_sqlite3 = require("node:sqlite");
|
|
1324
|
-
var
|
|
1260
|
+
var import_agent_qa_schema8 = require("@vostride/agent-qa-schema");
|
|
1325
1261
|
|
|
1326
1262
|
// src/store/sqlite-job-store.ts
|
|
1327
1263
|
var import_node_fs7 = require("node:fs");
|
|
1328
1264
|
var import_node_path8 = require("node:path");
|
|
1329
1265
|
var import_node_sqlite2 = require("node:sqlite");
|
|
1330
1266
|
var import_agent_qa_agent3 = require("@vostride/agent-qa-agent");
|
|
1331
|
-
var
|
|
1267
|
+
var import_agent_qa_schema7 = require("@vostride/agent-qa-schema");
|
|
1332
1268
|
|
|
1333
1269
|
// src/store/logical-dump.ts
|
|
1334
1270
|
var import_node_fs6 = require("node:fs");
|
|
1335
1271
|
var import_node_path7 = require("node:path");
|
|
1336
1272
|
var import_node_sqlite = require("node:sqlite");
|
|
1337
|
-
var
|
|
1273
|
+
var import_agent_qa_schema6 = require("@vostride/agent-qa-schema");
|
|
1338
1274
|
var SAFE_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
1339
1275
|
function requireIdentifier(value) {
|
|
1340
1276
|
if (!SAFE_IDENTIFIER.test(value)) {
|
|
@@ -1391,7 +1327,7 @@ function checkpointDigests(checkpointsRoot) {
|
|
|
1391
1327
|
}
|
|
1392
1328
|
found.push({
|
|
1393
1329
|
path: (0, import_node_path7.relative)(checkpointsRoot, path).split(import_node_path7.sep).join("/"),
|
|
1394
|
-
digest: (0,
|
|
1330
|
+
digest: (0, import_agent_qa_schema6.sha256hex)((0, import_node_fs6.readFileSync)(path, "utf8"))
|
|
1395
1331
|
});
|
|
1396
1332
|
}
|
|
1397
1333
|
}
|
|
@@ -1406,7 +1342,7 @@ function createLogicalDump(input) {
|
|
|
1406
1342
|
};
|
|
1407
1343
|
}
|
|
1408
1344
|
function canonicalLogicalDump(dump) {
|
|
1409
|
-
return (0,
|
|
1345
|
+
return (0, import_agent_qa_schema6.canonicalJson)(dump);
|
|
1410
1346
|
}
|
|
1411
1347
|
|
|
1412
1348
|
// src/store/sqlite-job-store.ts
|
|
@@ -1429,22 +1365,14 @@ var SCHEMA_STATEMENTS = [
|
|
|
1429
1365
|
-- Nullable because most rows never fail; written only by recordFailure.
|
|
1430
1366
|
last_failure_kind TEXT,
|
|
1431
1367
|
last_failure_reason TEXT,
|
|
1432
|
-
--
|
|
1433
|
-
-- same reason the claim is one: the job status vocabulary describes lifecycle stages,
|
|
1434
|
-
-- and "still queued, but asked to stop" is a fact about a request, not a stage.
|
|
1435
|
-
-- Appended LAST deliberately - a column inserted in the middle would move every
|
|
1436
|
-
-- positional read of this table for no gain.
|
|
1368
|
+
-- A stop request, not a lifecycle stage. New columns go last, or positional reads would move.
|
|
1437
1369
|
cancel_requested_at TEXT,
|
|
1438
|
-
--
|
|
1439
|
-
-- claim column answers who holds the row now; no column answered how long the work took.
|
|
1440
|
-
-- Written once by the claim, cleared only by the operator requeue. Appended last, as above.
|
|
1370
|
+
-- When the row was first claimed; survives lease release, cleared only by operator requeue.
|
|
1441
1371
|
started_at TEXT,
|
|
1442
|
-
--
|
|
1372
|
+
-- A retrying row is not claimable before this instant.
|
|
1443
1373
|
not_before TEXT
|
|
1444
1374
|
)`,
|
|
1445
|
-
//
|
|
1446
|
-
// caller performs is what turns "two enqueues of the same work produce one job" from a
|
|
1447
|
-
// convention into something no caller, in any process, can get wrong.
|
|
1375
|
+
// The dedupe is this unique index, so no caller in any process can enqueue the same work twice.
|
|
1448
1376
|
`CREATE UNIQUE INDEX IF NOT EXISTS jobs_kind_idempotency_key
|
|
1449
1377
|
ON jobs (kind, idempotency_key)`,
|
|
1450
1378
|
`CREATE TABLE IF NOT EXISTS job_store_fence (
|
|
@@ -1464,18 +1392,13 @@ var SCHEMA_STATEMENTS = [
|
|
|
1464
1392
|
epoch INTEGER NOT NULL,
|
|
1465
1393
|
PRIMARY KEY (job_id, attempt, checkpoint_id)
|
|
1466
1394
|
)`,
|
|
1467
|
-
//
|
|
1468
|
-
// second durable medium. `created_at` is written from the injected clock like every other
|
|
1469
|
-
// timestamp in this file — a SQL-side default would leak host time into the D-03 dump and
|
|
1470
|
-
// make two identical histories compare unequal.
|
|
1395
|
+
// `created_at` comes from the injected clock; a SQL default would leak host time into the dump.
|
|
1471
1396
|
`CREATE TABLE IF NOT EXISTS notifications (
|
|
1472
1397
|
run_id TEXT NOT NULL,
|
|
1473
1398
|
payload TEXT NOT NULL,
|
|
1474
1399
|
created_at TEXT NOT NULL
|
|
1475
1400
|
)`,
|
|
1476
|
-
//
|
|
1477
|
-
// reason: a crash-replayed chain terminal re-attempts the insert, and "one notification per
|
|
1478
|
-
// run, ever" has to be something no caller — in this process or the next — can get wrong.
|
|
1401
|
+
// One notification per run, ever: a crash-replayed terminal hits this unique index.
|
|
1479
1402
|
`CREATE UNIQUE INDEX IF NOT EXISTS notifications_run_id
|
|
1480
1403
|
ON notifications (run_id)`
|
|
1481
1404
|
];
|
|
@@ -1484,7 +1407,7 @@ var UNREADABLE_KIND_CONCLUSION = "unreadable_kind";
|
|
|
1484
1407
|
var QUARANTINED_UNREADABLE_KIND_REASON = "agent job quarantined at claim: this build does not read the kind recorded for this row, so the row was landed permanently without being parsed. The original kind, id and payload are preserved in the row for inspection.";
|
|
1485
1408
|
var QUARANTINE_SCAN_BOUND = 1e3;
|
|
1486
1409
|
function isUnreadableKindRow(row) {
|
|
1487
|
-
return !(0,
|
|
1410
|
+
return !(0, import_agent_qa_schema7.isJobKind)(String(row.kind));
|
|
1488
1411
|
}
|
|
1489
1412
|
function quarantineConclusionOf(row) {
|
|
1490
1413
|
if (!isUnreadableKindRow(row)) return void 0;
|
|
@@ -1666,7 +1589,7 @@ function createSqliteJobStore(options) {
|
|
|
1666
1589
|
if (row === void 0) {
|
|
1667
1590
|
throw new UnknownJobRowError("<missing>");
|
|
1668
1591
|
}
|
|
1669
|
-
return
|
|
1592
|
+
return import_agent_qa_schema7.jobSchema.parse({
|
|
1670
1593
|
id: String(row.id),
|
|
1671
1594
|
kind: String(row.kind),
|
|
1672
1595
|
status: String(row.status),
|
|
@@ -1724,8 +1647,7 @@ function createSqliteJobStore(options) {
|
|
|
1724
1647
|
attempts: toNumber(row.attempts),
|
|
1725
1648
|
claimed: row.claimed_at !== null,
|
|
1726
1649
|
cancelRequested: row.cancel_requested_at !== null,
|
|
1727
|
-
//
|
|
1728
|
-
// serializes this row does not publish an empty conclusion key for every ordinary job.
|
|
1650
|
+
// Omitted rather than `undefined`, so a serialized row publishes no empty conclusion key.
|
|
1729
1651
|
...conclusionKind === void 0 ? {} : { conclusionKind },
|
|
1730
1652
|
createdAt: String(row.created_at),
|
|
1731
1653
|
updatedAt: String(row.updated_at)
|
|
@@ -1847,15 +1769,7 @@ function createSqliteJobStore(options) {
|
|
|
1847
1769
|
markCancelHonoured.run(now, String(jobId));
|
|
1848
1770
|
});
|
|
1849
1771
|
},
|
|
1850
|
-
//
|
|
1851
|
-
//
|
|
1852
|
-
// Phase 413's pump composed with no post-run capabilities, so the kind registry selected
|
|
1853
|
-
// the STAND-IN analyze-run handler: nineteen rows recorded a `verdicts-authored`
|
|
1854
|
-
// checkpoint and reached `done` with nothing authored. Resetting the status alone would
|
|
1855
|
-
// not undo that, it would REPEAT it — `selectLatestCheckpoint` reads
|
|
1856
|
-
// `ORDER BY attempt DESC`, so the real handler's next attempt would resume on the
|
|
1857
|
-
// stand-in's recorded value, author nothing again, and report completion again. So the
|
|
1858
|
-
// rows go with the status, inside the same transaction, and the files follow the rows.
|
|
1772
|
+
// Checkpoint rows go with the status, or the real handler resumes on the stand-in's value.
|
|
1859
1773
|
async requeueTerminal(jobId, fence) {
|
|
1860
1774
|
const now = clock.now();
|
|
1861
1775
|
const decided = inTransaction(() => {
|
|
@@ -1885,8 +1799,8 @@ function createSqliteJobStore(options) {
|
|
|
1885
1799
|
},
|
|
1886
1800
|
async publishCheckpoint(jobId, checkpointId, payload, fence) {
|
|
1887
1801
|
const now = clock.now();
|
|
1888
|
-
const payloadJson = (0,
|
|
1889
|
-
const payloadDigest = (0,
|
|
1802
|
+
const payloadJson = (0, import_agent_qa_schema7.canonicalJson)(payload);
|
|
1803
|
+
const payloadDigest = (0, import_agent_qa_schema7.sha256hex)(payloadJson);
|
|
1890
1804
|
inTransaction(() => {
|
|
1891
1805
|
requireFence(fence);
|
|
1892
1806
|
requireRow(String(jobId));
|
|
@@ -1915,7 +1829,7 @@ function createSqliteJobStore(options) {
|
|
|
1915
1829
|
try {
|
|
1916
1830
|
const projection = checkpoints.read(String(jobId), checkpointId);
|
|
1917
1831
|
if (!projection.found) return authoritative;
|
|
1918
|
-
const projectionDigest = (0,
|
|
1832
|
+
const projectionDigest = (0, import_agent_qa_schema7.sha256hex)((0, import_agent_qa_schema7.canonicalJson)(projection.payload));
|
|
1919
1833
|
return projectionDigest === String(row.payload_digest) ? projection : authoritative;
|
|
1920
1834
|
} catch {
|
|
1921
1835
|
return authoritative;
|
|
@@ -1933,7 +1847,7 @@ function createSqliteJobStore(options) {
|
|
|
1933
1847
|
async listNotifications() {
|
|
1934
1848
|
return selectNotifications.all().map((row) => ({
|
|
1935
1849
|
runId: String(row.run_id),
|
|
1936
|
-
payload: (0,
|
|
1850
|
+
payload: (0, import_agent_qa_schema7.guardRunNotification)(JSON.parse(String(row.payload))),
|
|
1937
1851
|
createdAt: String(row.created_at)
|
|
1938
1852
|
}));
|
|
1939
1853
|
},
|
|
@@ -1976,13 +1890,13 @@ function readCurationSnapshot(db, root, ids, runId) {
|
|
|
1976
1890
|
if (row === void 0) return void 0;
|
|
1977
1891
|
try {
|
|
1978
1892
|
const authoritative = JSON.parse(String(row.payload_json));
|
|
1979
|
-
if ((0,
|
|
1893
|
+
if ((0, import_agent_qa_schema8.sha256hex)((0, import_agent_qa_schema8.canonicalJson)(authoritative)) !== row.payload_digest) {
|
|
1980
1894
|
throw new CurationSnapshotReadError();
|
|
1981
1895
|
}
|
|
1982
1896
|
let payload = authoritative;
|
|
1983
1897
|
try {
|
|
1984
1898
|
const cached = readJobCheckpointEnvelope({ root }, String(ids.curateMemory), checkpointId);
|
|
1985
|
-
if (cached.found && (0,
|
|
1899
|
+
if (cached.found && (0, import_agent_qa_schema8.sha256hex)((0, import_agent_qa_schema8.canonicalJson)(cached.envelope.payload)) === row.payload_digest) {
|
|
1986
1900
|
payload = cached.envelope.payload;
|
|
1987
1901
|
}
|
|
1988
1902
|
} catch {
|
|
@@ -1993,7 +1907,7 @@ function readCurationSnapshot(db, root, ids, runId) {
|
|
|
1993
1907
|
if (value.curationSnapshot === void 0) return void 0;
|
|
1994
1908
|
const job = db.prepare("SELECT kind, idempotency_key, payload, attempts FROM jobs WHERE id = ?").get(String(ids.curateMemory));
|
|
1995
1909
|
if (job === void 0 || job.kind !== "curate-memory" || job.idempotency_key !== `${ids.analyzeRun}->curate-memory` || Number(job.attempts) < Number(checkpoint.attempt) || recordOf(JSON.parse(String(job.payload))).runId !== runId || value.runId !== runId) throw new CurationSnapshotReadError();
|
|
1996
|
-
return
|
|
1910
|
+
return import_agent_qa_schema8.CurationSnapshotSchema.parse(value.curationSnapshot);
|
|
1997
1911
|
} catch {
|
|
1998
1912
|
throw new CurationSnapshotReadError();
|
|
1999
1913
|
}
|
|
@@ -2003,7 +1917,7 @@ function toNumber2(value) {
|
|
|
2003
1917
|
}
|
|
2004
1918
|
function toRow(row) {
|
|
2005
1919
|
if (row === void 0) return void 0;
|
|
2006
|
-
const conclusionKind = !(0,
|
|
1920
|
+
const conclusionKind = !(0, import_agent_qa_schema8.isJobKind)(String(row.kind)) && row.status === "failed" && row.last_failure_kind === "permanent" && row.last_failure_reason === QUARANTINED_UNREADABLE_KIND_REASON ? "unreadable_kind" : void 0;
|
|
2007
1921
|
return {
|
|
2008
1922
|
status: String(row.status),
|
|
2009
1923
|
claimed: row.claimed_at !== null,
|
|
@@ -2044,15 +1958,11 @@ function createPostProcessingReader(options) {
|
|
|
2044
1958
|
const curateMemory = toRow(
|
|
2045
1959
|
selectJob.get(String(ids.curateMemory))
|
|
2046
1960
|
);
|
|
2047
|
-
const
|
|
2048
|
-
selectJob.get(String(ids.proposeImprovements))
|
|
2049
|
-
);
|
|
2050
|
-
const notification = notificationRow === void 0 ? void 0 : (0, import_agent_qa_schema9.guardRunNotification)(JSON.parse(String(notificationRow.payload)));
|
|
1961
|
+
const notification = notificationRow === void 0 ? void 0 : (0, import_agent_qa_schema8.guardRunNotification)(JSON.parse(String(notificationRow.payload)));
|
|
2051
1962
|
const curationSnapshot = readCurationSnapshot(db, options.agentQaRoot, ids, runId);
|
|
2052
1963
|
return {
|
|
2053
1964
|
...analyzeRun === void 0 ? {} : { analyzeRun },
|
|
2054
1965
|
...curateMemory === void 0 ? {} : { curateMemory },
|
|
2055
|
-
...proposeImprovements === void 0 ? {} : { proposeImprovements },
|
|
2056
1966
|
...notification === void 0 ? {} : { notification },
|
|
2057
1967
|
...curationSnapshot === void 0 ? {} : { curationSnapshot }
|
|
2058
1968
|
};
|
|
@@ -2101,7 +2011,7 @@ var LOCAL_DAEMON_CAPABILITIES = Object.freeze({
|
|
|
2101
2011
|
verifyEmbedderWorkerIncarnation: import_agent_qa_knowledge_local.verifyEmbedderWorkerIncarnation,
|
|
2102
2012
|
resolveProcessIncarnation: import_agent_qa_knowledge_local.resolveProcessIncarnation,
|
|
2103
2013
|
removeEmbedderWorkerRegistration: import_agent_qa_knowledge_local.removeEmbedderWorkerRegistration,
|
|
2104
|
-
resetEmbedderCapability: () => (0, import_agent_qa_knowledge_local.resetEmbedderCapability)(
|
|
2014
|
+
resetEmbedderCapability: () => (0, import_agent_qa_knowledge_local.resetEmbedderCapability)(import_agent_qa_schema9.DEFAULT_EMBEDDING_PROFILE),
|
|
2105
2015
|
pinLocalHeadlessExecutionContext: () => {
|
|
2106
2016
|
(0, import_agent_qa_safety.setActiveExecutionContextResolver)(() => "local-headless");
|
|
2107
2017
|
}
|
|
@@ -2112,8 +2022,6 @@ function receiptWriter(input) {
|
|
|
2112
2022
|
executionContext: input.executionContext,
|
|
2113
2023
|
writerLeaseId: input.lease.leaseId,
|
|
2114
2024
|
epoch: input.lease.epoch,
|
|
2115
|
-
...input.resolveTrustPriors === void 0 ? {} : { resolveTrustPriors: input.resolveTrustPriors },
|
|
2116
|
-
...input.resolvePromotionPosture === void 0 ? {} : { resolvePromotionPosture: input.resolvePromotionPosture },
|
|
2117
2025
|
...input.sourceBuildOrdinal === void 0 ? {} : { sourceBuildOrdinal: input.sourceBuildOrdinal },
|
|
2118
2026
|
...input.sourceRunId === void 0 ? {} : { sourceRunId: input.sourceRunId }
|
|
2119
2027
|
});
|
|
@@ -2162,10 +2070,6 @@ var LOCAL_CURATION_CAPABILITIES = Object.freeze({
|
|
|
2162
2070
|
reconcileRunOutcomesProjection: import_agent_qa_knowledge_local.reconcileRunOutcomesProjection,
|
|
2163
2071
|
enqueueCurationCandidateProjection: import_agent_qa_knowledge_local.enqueueCurationCandidateProjection,
|
|
2164
2072
|
readCanonicalBundleAnchors: import_agent_qa_knowledge_local.readCanonicalBundleAnchors,
|
|
2165
|
-
readCanonicalBundleStateDigests: import_agent_qa_knowledge_local.readCanonicalBundleStateDigests,
|
|
2166
|
-
readCanaryPostureFacts: import_agent_qa_knowledge_local.canaryPostureFactsFor,
|
|
2167
|
-
readStandingGateReceipts: import_agent_qa_knowledge_local.standingReceiptDigestsFor,
|
|
2168
|
-
readLowerTrustShares: import_agent_qa_knowledge_local.lowerTrustSharesFor,
|
|
2169
2073
|
runOutcomesProjectionVersion: import_agent_qa_knowledge_local.RUN_OUTCOMES_PROJECTION_VERSION,
|
|
2170
2074
|
canaryOutcomeObservationId: import_reader5.canaryOutcomeObservationId,
|
|
2171
2075
|
maintenance: Object.freeze({
|
|
@@ -2189,21 +2093,11 @@ function isTargetName(value) {
|
|
|
2189
2093
|
return typeof value === "string" && value.trim().length > 0;
|
|
2190
2094
|
}
|
|
2191
2095
|
function recordedTargetsOf(artifact) {
|
|
2192
|
-
const
|
|
2193
|
-
if (!
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
}
|
|
2198
|
-
if (source.kind !== "suite" || !Array.isArray(source.members)) return { status: "unknown" };
|
|
2199
|
-
const names = /* @__PURE__ */ new Set();
|
|
2200
|
-
for (const member of source.members) {
|
|
2201
|
-
if (!isRecord(member)) return { status: "unknown" };
|
|
2202
|
-
if (member.target === void 0 || member.target === null) continue;
|
|
2203
|
-
if (!isTargetName(member.target)) return { status: "unknown" };
|
|
2204
|
-
names.add(member.target);
|
|
2205
|
-
}
|
|
2206
|
-
return names.size === 0 ? { status: "unknown" } : { status: "recorded", names: [...names].sort() };
|
|
2096
|
+
const targets = isRecord(artifact) ? artifact.targets : void 0;
|
|
2097
|
+
if (!Array.isArray(targets) || targets.length === 0 || !targets.every(isTargetName)) {
|
|
2098
|
+
return { status: "unknown" };
|
|
2099
|
+
}
|
|
2100
|
+
return { status: "recorded", names: [...new Set(targets)].sort() };
|
|
2207
2101
|
}
|
|
2208
2102
|
function createRunsReader(agentQaRoot) {
|
|
2209
2103
|
function enumerate() {
|
|
@@ -2226,25 +2120,11 @@ function createRunsReader(agentQaRoot) {
|
|
|
2226
2120
|
list() {
|
|
2227
2121
|
return Promise.resolve(enumerate());
|
|
2228
2122
|
},
|
|
2229
|
-
|
|
2230
|
-
* Resolution runs over the ENUMERATED run documents and matches on the recorded `runId`,
|
|
2231
|
-
* never by joining the caller's string onto a path (T-384-08): a `runId` of `../../etc` can
|
|
2232
|
-
* therefore address nothing outside the runs directory, because it is never used to build a
|
|
2233
|
-
* path at all.
|
|
2234
|
-
*/
|
|
2123
|
+
// Matches the recorded `runId` over enumerated documents, so `../../etc` addresses nothing.
|
|
2235
2124
|
get(runId) {
|
|
2236
2125
|
return Promise.resolve(enumerate().find((document) => document.runId === runId));
|
|
2237
2126
|
},
|
|
2238
|
-
|
|
2239
|
-
* Deep replay of one run's `events.jsonl` through the SAME concrete store the v49 bus appends
|
|
2240
|
-
* with, so a replayed stream is byte-for-byte what the run emitted.
|
|
2241
|
-
*
|
|
2242
|
-
* Two cases are deliberately distinct. An absent or empty log is an ordinary empty replay —
|
|
2243
|
-
* nothing was recorded, so nothing was lost. A log whose earliest surviving seq is ABOVE the
|
|
2244
|
-
* requested resume point means events the caller asked for existed and are gone; that is
|
|
2245
|
-
* reported with the bus's own `ReplayIncompleteError` carrying the floor, never papered over
|
|
2246
|
-
* by returning the surviving suffix (D-07/D-08).
|
|
2247
|
-
*/
|
|
2127
|
+
// A resume point below the earliest surviving seq throws `ReplayIncompleteError`.
|
|
2248
2128
|
readEvents(runId, fromSeq) {
|
|
2249
2129
|
const recorded = [...new import_agent_qa_runner_local3.FsEventLog({ root: agentQaRoot, runId }).read(0)].sort((left, right) => left.seq - right.seq);
|
|
2250
2130
|
if (recorded.length === 0) return Promise.resolve([]);
|
|
@@ -2254,22 +2134,7 @@ function createRunsReader(agentQaRoot) {
|
|
|
2254
2134
|
}
|
|
2255
2135
|
return Promise.resolve(recorded.filter((event) => event.seq >= fromSeq));
|
|
2256
2136
|
},
|
|
2257
|
-
|
|
2258
|
-
* One step's persisted evidence, read back out of the run's own handoff document
|
|
2259
|
-
* (387 KREV-02 / D-15).
|
|
2260
|
-
*
|
|
2261
|
-
* Through `loadCompletedRunHandoff` — the version guard — and NOT through a raw JSON read,
|
|
2262
|
-
* which is the load-bearing half. 387-02 gave the guard a present-implies-valid clause for
|
|
2263
|
-
* the retrieval trace, so a document whose trace is present but malformed is refused here
|
|
2264
|
-
* rather than reaching the why-verb looking well-formed.
|
|
2265
|
-
*
|
|
2266
|
-
* The ambiguity check is the never-guess law at this seam. `stepIndex` numbers steps per
|
|
2267
|
-
* MEMBER TEST, so a run with two tests has two step 0s; when the address does not name a
|
|
2268
|
-
* test and more than one candidate answers to it, this reports the candidates rather than
|
|
2269
|
-
* picking whichever test the document happened to list first. Picking would have produced a
|
|
2270
|
-
* confident, wrong answer to a question that was never asked — and a why-verb that does
|
|
2271
|
-
* that is worse than one that refuses, because its answer looks exactly like a right one.
|
|
2272
|
-
*/
|
|
2137
|
+
// Read through the version guard; an ambiguous step reports its candidates instead of guessing.
|
|
2273
2138
|
readStepEvidence(runId, address) {
|
|
2274
2139
|
const document = enumerate().find((candidate) => candidate.runId === runId);
|
|
2275
2140
|
if (document === void 0) return Promise.resolve({ kind: "run-not-recorded" });
|
|
@@ -2287,12 +2152,7 @@ function createRunsReader(agentQaRoot) {
|
|
|
2287
2152
|
evidence === void 0 ? { kind: "step-not-found" } : { kind: "found", evidence }
|
|
2288
2153
|
);
|
|
2289
2154
|
},
|
|
2290
|
-
|
|
2291
|
-
* Resolved over the ENUMERATED run directories (T-384-08, T-431-01): the caller's id is only
|
|
2292
|
-
* ever compared, never joined onto a path, and the directory must hold a completed handoff
|
|
2293
|
-
* recording that same id, so one run's snapshot can never answer for another. A snapshot that
|
|
2294
|
-
* is absent, unreadable or gone since `list()` answered is `unknown`, never an error.
|
|
2295
|
-
*/
|
|
2155
|
+
// The caller's id is compared, never joined onto a path; an absent snapshot is `unknown`.
|
|
2296
2156
|
getRecordedTargets(runId) {
|
|
2297
2157
|
const runsDir = (0, import_node_path10.join)(agentQaRoot, import_agent_qa_runner_local3.RUNS_SUBDIR);
|
|
2298
2158
|
if (!(0, import_node_fs9.existsSync)(runsDir)) return Promise.resolve({ status: "unknown" });
|
|
@@ -2312,11 +2172,9 @@ function createRunsReader(agentQaRoot) {
|
|
|
2312
2172
|
}
|
|
2313
2173
|
};
|
|
2314
2174
|
}
|
|
2315
|
-
function createKnowledgeReader(resolveKnowledgeRoot, sightMemoryEntities
|
|
2175
|
+
function createKnowledgeReader(resolveKnowledgeRoot, sightMemoryEntities) {
|
|
2316
2176
|
return createKnowledgeReaderPort({
|
|
2317
2177
|
resolveKnowledgeRoot,
|
|
2318
|
-
...resolveTrustPriors === void 0 ? {} : { resolveTrustPriors },
|
|
2319
|
-
...resolvePromotionPosture === void 0 ? {} : { resolvePromotionPosture },
|
|
2320
2178
|
...sightMemoryEntities === void 0 ? {} : { sightMemoryEntities }
|
|
2321
2179
|
});
|
|
2322
2180
|
}
|
|
@@ -2350,10 +2208,7 @@ function createKnowledgeReview(input) {
|
|
|
2350
2208
|
return next;
|
|
2351
2209
|
}
|
|
2352
2210
|
return {
|
|
2353
|
-
//
|
|
2354
|
-
// (432-06). Nothing is filed: the change is committed under the lease, handed to a live
|
|
2355
|
-
// holder, or refused by type. A missing identity is minted here, once per call, and returned on
|
|
2356
|
-
// the receipt so the caller can retry the same change.
|
|
2211
|
+
// A missing identity is minted once per call and returned, so a retry is the same change.
|
|
2357
2212
|
change: (request) => serialized(async () => {
|
|
2358
2213
|
const { knowledgeRoot } = await roots();
|
|
2359
2214
|
const operationId = request.operationId ?? (0, import_node_crypto4.randomUUID)();
|
|
@@ -2367,18 +2222,14 @@ function createKnowledgeReview(input) {
|
|
|
2367
2222
|
// `knowledge.reject` or `knowledge.forget`, which is the verb's own op id.
|
|
2368
2223
|
opId: `knowledge.${request.verb}`,
|
|
2369
2224
|
executionContext: (input.resolveExecutionContext ?? import_agent_qa_safety.getActiveExecutionContext)(),
|
|
2370
|
-
...input.resolveTrustPriors === void 0 ? {} : { resolveTrustPriors: input.resolveTrustPriors },
|
|
2371
|
-
...input.resolvePromotionPosture === void 0 ? {} : { resolvePromotionPosture: input.resolvePromotionPosture },
|
|
2372
2225
|
...input.knowledgeChangeDelegate === void 0 ? {} : { delegate: input.knowledgeChangeDelegate },
|
|
2373
2226
|
...authority === void 0 ? {} : { writerAuthority: authority }
|
|
2374
2227
|
});
|
|
2375
2228
|
return knowledgeChangeOutput(request.verb, operationId, result);
|
|
2376
2229
|
})
|
|
2377
|
-
// 2026-10-01 (432-17, D-05/D-22): `propose` and `apply` are deleted with the proposal inbox.
|
|
2378
2230
|
};
|
|
2379
2231
|
}
|
|
2380
2232
|
async function applyKnowledgeChange(input) {
|
|
2381
|
-
const posture = await input.resolvePromotionPosture?.();
|
|
2382
2233
|
const ledger = (0, import_agent_qa_knowledge_local.openAddressingLedger)({
|
|
2383
2234
|
path: (0, import_node_path10.join)(input.agentQaRoot, import_agent_qa_knowledge_local.ADDRESSING_LEDGER_DB_FILE)
|
|
2384
2235
|
});
|
|
@@ -2391,8 +2242,6 @@ async function applyKnowledgeChange(input) {
|
|
|
2391
2242
|
opId: input.opId,
|
|
2392
2243
|
executionContext: input.executionContext,
|
|
2393
2244
|
resolveBundleShortId: (shortId) => ledger.lookupByShortId(shortId)?.machineId,
|
|
2394
|
-
...input.resolveTrustPriors === void 0 ? {} : { resolveTrustPriors: input.resolveTrustPriors },
|
|
2395
|
-
...posture === void 0 ? {} : { resolvePromotionPosture: posture },
|
|
2396
2245
|
...input.delegate === void 0 ? {} : { delegate: input.delegate },
|
|
2397
2246
|
...input.writerAuthority === void 0 ? {} : { writerAuthority: input.writerAuthority }
|
|
2398
2247
|
});
|
|
@@ -2425,9 +2274,6 @@ async function withEntityResolutionDeps(input, body) {
|
|
|
2425
2274
|
return await body({
|
|
2426
2275
|
ledger,
|
|
2427
2276
|
entityIndex,
|
|
2428
|
-
// One kind is wired by the tracer. 387-03 registers the remaining sources here and
|
|
2429
|
-
// neither the resolver nor the ops change — which is the property the tracer exists
|
|
2430
|
-
// to establish before anything expands.
|
|
2431
2277
|
sources: [(0, import_agent_qa_knowledge_local.createMemorySightingSource)(() => knowledgeRoot)]
|
|
2432
2278
|
});
|
|
2433
2279
|
} finally {
|
|
@@ -2449,7 +2295,7 @@ async function sweepEntityIndexProjection(input) {
|
|
|
2449
2295
|
let embedded = 0;
|
|
2450
2296
|
const acquire = input.acquireEmbedderCapability ?? import_agent_qa_knowledge_local.acquireEmbedder;
|
|
2451
2297
|
const capability = await acquire({
|
|
2452
|
-
preferredProfile:
|
|
2298
|
+
preferredProfile: import_agent_qa_schema9.DEFAULT_EMBEDDING_PROFILE,
|
|
2453
2299
|
allowDownload: false,
|
|
2454
2300
|
agentQaRoot: input.agentQaRoot
|
|
2455
2301
|
});
|
|
@@ -2560,16 +2406,9 @@ function createDraftEvaluation(input) {
|
|
|
2560
2406
|
elementsForTest: (filePath, testName) => fingerprints().elements.get(draftIdentityKey({ filePath, testName })) ?? []
|
|
2561
2407
|
},
|
|
2562
2408
|
textCorpus,
|
|
2563
|
-
//
|
|
2564
|
-
// proposals inbox and for its reason — a receipt is machine-local working state under the
|
|
2565
|
-
// runtime root, not a committed artifact a pull request has any business showing.
|
|
2409
|
+
// Receipts are machine-local working state under the runtime root, not committed artifacts.
|
|
2566
2410
|
receipts: (0, import_agent_qa_knowledge_local.createDraftReceiptsStore)({ resolveAgentQaRoot: () => input.agentQaRoot }),
|
|
2567
|
-
//
|
|
2568
|
-
// is opened against — read off the first gate-run acknowledgment, never derived from
|
|
2569
|
-
// configuration — so the stability read and the suite index can never be asking about two
|
|
2570
|
-
// different applications. Before any run is acknowledged it is `undefined` and the
|
|
2571
|
-
// consultation is skipped; D-04 forbids short-circuiting, so a flip cannot be observed
|
|
2572
|
-
// until at least one run has been.
|
|
2411
|
+
// `product` is gate 3's observed product: stability and the index ask about one app.
|
|
2573
2412
|
suiteStability: {
|
|
2574
2413
|
port: input.authoringFunnel,
|
|
2575
2414
|
product: () => observedProduct
|
|
@@ -2590,7 +2429,7 @@ var SYSTEM_CLOCK = Object.freeze({
|
|
|
2590
2429
|
now: () => (/* @__PURE__ */ new Date()).toISOString()
|
|
2591
2430
|
});
|
|
2592
2431
|
var digestJobIdSource = {
|
|
2593
|
-
jobIdFor: (kind, idempotencyKey) => (0,
|
|
2432
|
+
jobIdFor: (kind, idempotencyKey) => (0, import_agent_qa_schema9.asJobId)(`job_${(0, import_agent_qa_schema9.sha256hex16)(`${kind} ${idempotencyKey}`)}`)
|
|
2594
2433
|
};
|
|
2595
2434
|
var DIGEST_JOB_ID_SOURCE = Object.freeze(digestJobIdSource);
|
|
2596
2435
|
var LocalAgentClosedError = class extends Error {
|
|
@@ -2603,10 +2442,7 @@ var LocalAgentClosedError = class extends Error {
|
|
|
2603
2442
|
name = "LocalAgentClosedError";
|
|
2604
2443
|
};
|
|
2605
2444
|
var UNCLAIMABLE_BY_PROSE_POSTURE = Object.freeze({
|
|
2606
|
-
// Frozen
|
|
2607
|
-
// here uses is not enough for this one: the VALUES are the arrays the queue claims
|
|
2608
|
-
// against, so an unfrozen inner array is a writable disclosure guard reachable from any
|
|
2609
|
-
// module that can import the table.
|
|
2445
|
+
// Frozen deep, because the inner arrays are what the queue claims against.
|
|
2610
2446
|
"masker-active": Object.freeze([]),
|
|
2611
2447
|
"no-masker": Object.freeze(["curate-memory"])
|
|
2612
2448
|
});
|
|
@@ -2716,7 +2552,7 @@ function createProjectionRecurrenceProvider(resolveKnowledgeRoot, now) {
|
|
|
2716
2552
|
function createAnalysisArtifactReader(agentQaRoot) {
|
|
2717
2553
|
return {
|
|
2718
2554
|
getArtifact(runId) {
|
|
2719
|
-
const path = (0, import_node_path10.join)(agentQaRoot, import_agent_qa_runner_local3.RUNS_SUBDIR, (0, import_agent_qa_runner_local3.slugBranch)(runId),
|
|
2555
|
+
const path = (0, import_node_path10.join)(agentQaRoot, import_agent_qa_runner_local3.RUNS_SUBDIR, (0, import_agent_qa_runner_local3.slugBranch)(runId), import_agent_qa_schema9.ANALYSIS_FILE);
|
|
2720
2556
|
let bytes;
|
|
2721
2557
|
try {
|
|
2722
2558
|
bytes = (0, import_node_fs9.readFileSync)(path, "utf8");
|
|
@@ -2724,71 +2560,32 @@ function createAnalysisArtifactReader(agentQaRoot) {
|
|
|
2724
2560
|
if (error.code === "ENOENT") return void 0;
|
|
2725
2561
|
throw error;
|
|
2726
2562
|
}
|
|
2727
|
-
const admission = (0,
|
|
2728
|
-
(0,
|
|
2563
|
+
const admission = (0, import_agent_qa_schema9.admitAnalysisArtifactForRun)(
|
|
2564
|
+
(0, import_agent_qa_schema9.guardRunAnalysisArtifact)(JSON.parse(bytes)),
|
|
2729
2565
|
runId
|
|
2730
2566
|
);
|
|
2731
2567
|
return admission.kind === "admitted" ? admission.artifact : void 0;
|
|
2732
2568
|
}
|
|
2733
2569
|
};
|
|
2734
2570
|
}
|
|
2735
|
-
function postRunCapabilities(input, knowledgeRoot, analysisTokenBudget
|
|
2571
|
+
function postRunCapabilities(input, knowledgeRoot, analysisTokenBudget) {
|
|
2736
2572
|
const postRun = input.postRun;
|
|
2737
2573
|
if (postRun === void 0) return void 0;
|
|
2738
2574
|
return {
|
|
2739
|
-
// 422 D-14: ONE narrow read — this job's own durable Gate-1 charge, addressed by its own id,
|
|
2740
|
-
// through the authoritative `readCheckpoint` the store already publishes. The handler is
|
|
2741
|
-
// handed no store, no checkpoint reader and no path; the shape check is `agent`'s, beside
|
|
2742
|
-
// the shape it checks, so two hosts cannot disagree about what a readable charge is.
|
|
2743
|
-
...jobStore === void 0 ? {} : {
|
|
2744
|
-
readProposeGate1Allowance: async (jobId) => (0, import_agent_qa_agent4.parseProposeGate1Allowance)(
|
|
2745
|
-
await jobStore.readCheckpoint((0, import_agent_qa_schema10.asJobId)(jobId), import_agent_qa_agent4.PROPOSE_GATE1_ALLOWANCE_CHECKPOINT)
|
|
2746
|
-
)
|
|
2747
|
-
},
|
|
2748
2575
|
loadCompletedRunHandoff: (runId) => (0, import_agent_qa_runner_local3.loadCompletedRunHandoff)(
|
|
2749
2576
|
(0, import_node_path10.join)(input.agentQaRoot, import_agent_qa_runner_local3.RUNS_SUBDIR, (0, import_agent_qa_runner_local3.slugBranch)(runId), import_agent_qa_runner_local3.HANDOFF_FILE)
|
|
2750
2577
|
),
|
|
2751
2578
|
curateRun: postRun.curateRun,
|
|
2752
|
-
//
|
|
2753
|
-
// is an ABSENT member rather than a present `undefined` — the handler decides on presence,
|
|
2754
|
-
// in one place, and a host that declined to bind cannot be mistaken for one that bound a
|
|
2755
|
-
// hole.
|
|
2756
|
-
...postRun.stageAdversarialProposal === void 0 ? {} : { stageAdversarialProposal: postRun.stageAdversarialProposal },
|
|
2757
|
-
// 422-09 (D-14): the Gate-1 leg's four ports and its bound, forwarded on the same
|
|
2758
|
-
// presence-spread idiom one line up. The handler's own `screenGate1` requires ALL FOUR before
|
|
2759
|
-
// it screens anything, so a host that binds three of them is a host that screens nothing —
|
|
2760
|
-
// which is why they are forwarded one by one rather than as a bundle a caller could
|
|
2761
|
-
// half-populate without noticing.
|
|
2762
|
-
...postRun.runReplayGate === void 0 ? {} : { runReplayGate: postRun.runReplayGate },
|
|
2763
|
-
...postRun.listBundles === void 0 ? {} : { listBundles: postRun.listBundles },
|
|
2764
|
-
...postRun.gateReceiptsFor === void 0 ? {} : { gateReceiptsFor: postRun.gateReceiptsFor },
|
|
2765
|
-
...postRun.readProposeCorpusPosture === void 0 ? {} : { readProposeCorpusPosture: postRun.readProposeCorpusPosture },
|
|
2766
|
-
...postRun.readMisdirectionRootPosture === void 0 ? {} : { readMisdirectionRootPosture: postRun.readMisdirectionRootPosture },
|
|
2767
|
-
...proposeGate1MaxPerJob === void 0 ? {} : { proposeGate1MaxPerJob },
|
|
2768
|
-
// 389 D-09: the artifact's path is DERIVED from the artifact's own runId through the
|
|
2769
|
-
// same `runs/<slug>/` law as the handoff read above — `slugBranch` is the traversal
|
|
2770
|
-
// guard (T-389-01), and a job payload never carries a path.
|
|
2579
|
+
// Derived from the artifact's own `runId` through `slugBranch`; a payload carries no path.
|
|
2771
2580
|
persistAnalysis: (artifact) => writeAnalysisArtifact(
|
|
2772
|
-
(0, import_node_path10.join)(input.agentQaRoot, import_agent_qa_runner_local3.RUNS_SUBDIR, (0, import_agent_qa_runner_local3.slugBranch)(artifact.runId),
|
|
2581
|
+
(0, import_node_path10.join)(input.agentQaRoot, import_agent_qa_runner_local3.RUNS_SUBDIR, (0, import_agent_qa_runner_local3.slugBranch)(artifact.runId), import_agent_qa_schema9.ANALYSIS_FILE),
|
|
2773
2582
|
artifact
|
|
2774
2583
|
),
|
|
2775
|
-
//
|
|
2776
|
-
// (product, filePath, testName), the stable identity, NEVER a per-run explicit id.
|
|
2777
|
-
// The query arrives already keyed by the author callback; this binding only reads.
|
|
2584
|
+
// Keyed by the stable identity (product, filePath, testName), never a per-run id; read-only.
|
|
2778
2585
|
readTestHistory: (query) => readProjectedTestHistory(knowledgeRoot, query),
|
|
2779
|
-
//
|
|
2780
|
-
// `FsEvidenceArtifactStore.getBlob` validates the lowercase 64-hex shape BEFORE any
|
|
2781
|
-
// path use (T-389-14) and answers ENOENT as honest absence; the runId becomes a path
|
|
2782
|
-
// segment only through `slugBranch` inside the store's own constructor.
|
|
2783
|
-
//
|
|
2784
|
-
// 405-05: THROUGH THE SHARED RULE, not a lambda of its own. `runs.artifact` binds a port over
|
|
2785
|
-
// the same `lookupEvidenceBlob` below, and two lambdas over one store would be two answers to
|
|
2786
|
-
// where a run's blobs live — agreeing on the day the second is written and diverging the first
|
|
2787
|
-
// time either side changes a root. This job wants only the found bytes, so the three-arm
|
|
2788
|
-
// lookup collapses here; the SPLIT is what the op needs and what this caller does not.
|
|
2586
|
+
// Through the shared `lookupEvidenceBlob`, so `runs.artifact` and this job agree on blobs.
|
|
2789
2587
|
readEvidenceBlob: (runId, sha) => readEvidenceBlobBytes({ agentQaRoot: input.agentQaRoot }, runId, sha),
|
|
2790
|
-
// Host-resolved
|
|
2791
|
-
// same profile resolution as the job model, and this composition adds no knob of its own.
|
|
2588
|
+
// Host-resolved from the job model's profile; this composition adds no knob.
|
|
2792
2589
|
...analysisTokenBudget === void 0 ? {} : { analysisTokenBudget }
|
|
2793
2590
|
};
|
|
2794
2591
|
}
|
|
@@ -2806,7 +2603,7 @@ function chatToolsetIds(registry) {
|
|
|
2806
2603
|
}
|
|
2807
2604
|
function chatToolsetHash(registry) {
|
|
2808
2605
|
if (registry === void 0) return "unbound";
|
|
2809
|
-
return (0,
|
|
2606
|
+
return (0, import_agent_qa_schema9.sha256hex16)(chatToolsetIds(registry).join("\n"));
|
|
2810
2607
|
}
|
|
2811
2608
|
function projectChatMembers(value, rules, mask) {
|
|
2812
2609
|
return Object.fromEntries(Object.entries(value).map(([key, member]) => {
|
|
@@ -2814,11 +2611,11 @@ function projectChatMembers(value, rules, mask) {
|
|
|
2814
2611
|
if (rule === "as-written") return [key, member];
|
|
2815
2612
|
if (typeof rule === "function") return [key, rule(member, mask)];
|
|
2816
2613
|
if (rule === "text" && typeof member === "string") return [key, mask(member)];
|
|
2817
|
-
return [key, (0,
|
|
2614
|
+
return [key, (0, import_agent_qa_schema9.maskPayloadTree)(member, mask)];
|
|
2818
2615
|
}));
|
|
2819
2616
|
}
|
|
2820
2617
|
var eachChatRecord = (rules) => (value, mask) => {
|
|
2821
|
-
const project = (entry) => isRecord(entry) ? projectChatMembers(entry, rules, mask) : (0,
|
|
2618
|
+
const project = (entry) => isRecord(entry) ? projectChatMembers(entry, rules, mask) : (0, import_agent_qa_schema9.maskPayloadTree)(entry, mask);
|
|
2822
2619
|
return Array.isArray(value) ? value.map(project) : project(value);
|
|
2823
2620
|
};
|
|
2824
2621
|
var CHAT_EVENT_BASE = {
|
|
@@ -2902,7 +2699,6 @@ function composeLocalAgent(input) {
|
|
|
2902
2699
|
// which is also what its header will say. A container seeded from another session's override
|
|
2903
2700
|
// would make one conversation's verb the next conversation's opening posture.
|
|
2904
2701
|
state: (0, import_agent_qa_agent4.createChatState)({ activeModel: composedChatModelName })
|
|
2905
|
-
// 432-13 (D-14): no pin holder. `/pin` left chat resolution with the op gate it tightened.
|
|
2906
2702
|
};
|
|
2907
2703
|
chatScopes.set(sessionId, scope);
|
|
2908
2704
|
return scope;
|
|
@@ -2913,11 +2709,7 @@ function composeLocalAgent(input) {
|
|
|
2913
2709
|
const chatSessionLog = createJsonlSessionLog({
|
|
2914
2710
|
root: input.agentQaRoot,
|
|
2915
2711
|
now: () => clock.now(),
|
|
2916
|
-
//
|
|
2917
|
-
// This thunk is read when a header is WRITTEN, which happens exactly once per session and only
|
|
2918
|
-
// for a session that has not existed until now — so no `/model` can have run in it. Answering
|
|
2919
|
-
// with a live override would stamp one conversation's switch onto the opening header of every
|
|
2920
|
-
// conversation opened after it. 441 GAP3-D4: written and listed through the declared mask.
|
|
2712
|
+
// The composed model, masked: a live override would stamp one session's switch on later ones.
|
|
2921
2713
|
model: () => input.mask(composedChatModelName),
|
|
2922
2714
|
toolsetHash: () => chatToolsetHash(composedRegistry)
|
|
2923
2715
|
});
|
|
@@ -2968,14 +2760,7 @@ function composeLocalAgent(input) {
|
|
|
2968
2760
|
stage = (0, import_agent_qa_agent4.createPlannerStage)({
|
|
2969
2761
|
model: chatModelsByName.get(activeName) ?? input.chatModel,
|
|
2970
2762
|
tools: (0, import_agent_qa_agent4.projectPlannerTools)(composedRegistry, CHAT_PLANNER_CALLER),
|
|
2971
|
-
// Read
|
|
2972
|
-
// is shared by every session planning under the same model name, so a context
|
|
2973
|
-
// thunk taking no argument had exactly one conversation to answer with, and on
|
|
2974
|
-
// two interleaved turns it answered with whichever one published last. Taking the
|
|
2975
|
-
// requesting session's id makes "the conversation this request replays" a fact
|
|
2976
|
-
// about the call. The read is still LIVE, so a `/model` override still moves what
|
|
2977
|
-
// the prompt states without the stage being rebuilt, and the conversation block
|
|
2978
|
-
// still carries what compaction left rather than a second replay of the log.
|
|
2763
|
+
// Read per request: a stage is shared by every session planning under one model.
|
|
2979
2764
|
context: (contextRequest) => {
|
|
2980
2765
|
const scope = chatScopeFor(contextRequest.sessionId).state.read();
|
|
2981
2766
|
return { activeModel: scope.activeModel, conversation: scope.conversation };
|
|
@@ -2989,41 +2774,22 @@ function composeLocalAgent(input) {
|
|
|
2989
2774
|
const chatCompaction = input.chatAccounting === void 0 ? void 0 : (0, import_agent_qa_agent4.createChatCompactionHook)({
|
|
2990
2775
|
accounting: input.chatAccounting,
|
|
2991
2776
|
now: () => clock.now(),
|
|
2992
|
-
//
|
|
2993
|
-
// prompt renders, and it lands in the live state on EVERY turn rather than only on a
|
|
2994
|
-
// triggered pass — a sink that fired only when a pass ran would leave the prompt holding
|
|
2995
|
-
// a stale conversation on every quiet turn. Into the SESSION'S OWN container (plan 13):
|
|
2996
|
-
// the id is the compacted turn's, so a pruned history cannot land in another
|
|
2997
|
-
// conversation however many turns are in flight.
|
|
2777
|
+
// What compaction leaves is the conversation the prompt renders, kept per session.
|
|
2998
2778
|
onConversation: (messages, sessionId) => {
|
|
2999
2779
|
chatScopeFor(sessionId).state.setConversation(messages);
|
|
3000
2780
|
}
|
|
3001
2781
|
});
|
|
3002
2782
|
const chatTurn = (0, import_agent_qa_agent4.createTurnEngine)({
|
|
3003
2783
|
table: import_agent_qa_agent4.CHAT_RESOLVER_TABLE,
|
|
3004
|
-
//
|
|
3005
|
-
// whole-utterance placeholder is gone; this is the seam filled, not widened.
|
|
2784
|
+
// The real conjunction grammar, quote-aware.
|
|
3006
2785
|
split: import_agent_qa_agent4.splitClauses,
|
|
3007
2786
|
reconcile: input.chatReconcile ?? import_agent_qa_agent4.deterministicOverrideStrategy,
|
|
3008
2787
|
sessionLog: trackedChatSessionLog,
|
|
3009
|
-
//
|
|
3010
|
-
// table hit is a person typing and mints `human`; a planner-planned call mints `agent`, and the
|
|
3011
|
-
// turn's receipts record which.
|
|
3012
|
-
// 2026-10-01 (432-21, D-12): NOT STAMPED WITH THE SESSION ANY MORE. The session id rode the
|
|
3013
|
-
// caller for the gate's deny overlay (plan 13, W-03) and the overlay is deleted (432-13/18/19);
|
|
3014
|
-
// nothing reads `FunctionCaller.sessionId`. The session still reaches the ops that record it
|
|
3015
|
-
// through the turn's own session facts, not through the caller.
|
|
2788
|
+
// Minted per stage: a table hit is a person typing (`human`); a planned call is `agent`.
|
|
3016
2789
|
mint: {
|
|
3017
2790
|
forStage: (stage) => stage === "resolver" ? CHAT_RESOLVER_CALLER : CHAT_PLANNER_CALLER
|
|
3018
2791
|
},
|
|
3019
|
-
//
|
|
3020
|
-
// place in the system holding both the registry and the invoke call. The chat subtree cannot
|
|
3021
|
-
// reach a spec by construction (384 FUNC-03), so the verdict and the render fold have to arrive
|
|
3022
|
-
// as data on this return — which is what `ChatInvokeAnswer` is. `foldFunctionResult` owns the
|
|
3023
|
-
// fail-closed rule so both composition roots get the same one rather than a copy each.
|
|
3024
|
-
//
|
|
3025
|
-
// invokeFunction validates canonical output; it does not promise masking. Classify those
|
|
3026
|
-
// original bytes before projecting the answer into results[] and the durable clause_result.
|
|
2792
|
+
// Classify the original bytes before projecting: `invokeFunction` does not mask.
|
|
3027
2793
|
invoke: async (functionId, invokeInput, caller) => {
|
|
3028
2794
|
if (composedRegistry === void 0) {
|
|
3029
2795
|
throw new TypeError("a chat turn ran before its registry finished composing");
|
|
@@ -3034,24 +2800,9 @@ function composeLocalAgent(input) {
|
|
|
3034
2800
|
},
|
|
3035
2801
|
...chatPlanner === void 0 ? {} : { planner: chatPlanner },
|
|
3036
2802
|
...chatCompaction === void 0 ? {} : { compaction: chatCompaction },
|
|
3037
|
-
//
|
|
3038
|
-
// `directive recognised but no handler` refusal from the shipped behaviour into an
|
|
3039
|
-
// unreachable arm on this host. `/help` and `/quit` are REPL chrome and answer in prose;
|
|
3040
|
-
// `/model` changes what the session is and returns the EFFECT it had. (432-13: `/pin` is gone.)
|
|
3041
|
-
//
|
|
3042
|
-
// RESOLVED PER SESSION (plan 13, CR-02 / W-03). Each one acts on a conversation rather than the
|
|
3043
|
-
// project, so the handler set is built over the scope of the session running the turn:
|
|
3044
|
-
// `/model` moves that conversation's model and `/quit` asks that loop to end.
|
|
2803
|
+
// Resolved per session: `/model` and `/quit` act on the conversation running the turn.
|
|
3045
2804
|
directives: (sessionId) => ({
|
|
3046
|
-
//
|
|
3047
|
-
// before it applies the first one, so a refused later directive leaves an earlier one
|
|
3048
|
-
// unapplied.
|
|
3049
|
-
//
|
|
3050
|
-
// IT APPLIES NOTHING. `/model` RESOLVES the model and caches it under its name — a cache is
|
|
3051
|
-
// not the session's state, and the session's own `activeModel` is left exactly where it was.
|
|
3052
|
-
// The resolution is also the only part of `/model` that can fail, so caching it here is what
|
|
3053
|
-
// makes the later apply unable to throw after an earlier directive has already been carried
|
|
3054
|
-
// out.
|
|
2805
|
+
// Applies nothing: `/model` resolves and caches by name, so the apply cannot throw midway.
|
|
3055
2806
|
validate: async (directive, argument) => {
|
|
3056
2807
|
if (directive === "model" && argument !== void 0 && input.chatModelFor !== void 0) {
|
|
3057
2808
|
chatModelsByName.set(argument, await input.chatModelFor(argument));
|
|
@@ -3073,17 +2824,7 @@ function composeLocalAgent(input) {
|
|
|
3073
2824
|
}
|
|
3074
2825
|
}),
|
|
3075
2826
|
state: (sessionId) => chatScopeFor(sessionId).state,
|
|
3076
|
-
//
|
|
3077
|
-
// value, because the drift worth detecting is precisely the one that happens underneath a
|
|
3078
|
-
// running session — a model resolved differently by the host, or a build whose registry
|
|
3079
|
-
// reaches further than the one this session opened against. The engine folds the log for what
|
|
3080
|
-
// was last recorded and emits `model_change` / `active_tools_change` on the difference.
|
|
3081
|
-
//
|
|
3082
|
-
// THE MODEL IT REPORTS IS THE ASKING SESSION'S (plan 13). A composition-wide read would answer
|
|
3083
|
-
// one conversation's `/model` override for every conversation, and the per-turn diff would
|
|
3084
|
-
// then write a `model_change` into sessions nobody had switched.
|
|
3085
|
-
//
|
|
3086
|
-
// 441 GAP3-D4: masked, because the log it is compared with is masked on append.
|
|
2827
|
+
// Re-read per turn and per session, and masked like the log it is compared with.
|
|
3087
2828
|
config: (sessionId) => {
|
|
3088
2829
|
const ids = chatToolsetIds(composedRegistry);
|
|
3089
2830
|
return {
|
|
@@ -3091,20 +2832,9 @@ function composeLocalAgent(input) {
|
|
|
3091
2832
|
toolset: { hash: chatToolsetHash(composedRegistry), ids }
|
|
3092
2833
|
};
|
|
3093
2834
|
},
|
|
3094
|
-
//
|
|
3095
|
-
// Bound off the SAME host verb `authoringRead.configuredTargetNames` uses, so the seam that
|
|
3096
|
-
// decides whether to offer a scaffold and the loop that later checks what a model emitted are
|
|
3097
|
-
// reading one answer rather than two. A host that cannot name its targets binds nothing and the
|
|
3098
|
-
// route never runs, which leaves the shipped dispatch and the loop's own backstop exactly as
|
|
3099
|
-
// they were.
|
|
3100
|
-
// BOUND ONLY WHERE THE LOOP IS. A host that cannot author has nothing for this route to route,
|
|
3101
|
-
// and binding it anyway would answer an empty vocabulary — which the route reads as the
|
|
3102
|
-
// from-zero case and refuses, reporting the wrong cause for a host whose real problem is that
|
|
3103
|
-
// `authoring.draft.write` is unbound. Absent leaves the shipped dispatch untouched.
|
|
2835
|
+
// Bound only where the loop is; elsewhere an empty vocabulary refuses with the wrong cause.
|
|
3104
2836
|
...input.draftAuthoring !== void 0 && input.runExecution !== void 0 ? { configuredTargets: () => input.draftAuthoring.configuredTargetNames() } : {},
|
|
3105
|
-
//
|
|
3106
|
-
// Conditionally spread on the exact idiom above, so a host that binds no surface leaves the
|
|
3107
|
-
// field ABSENT and the engine's own "renders nothing" posture is what answers — in one place.
|
|
2837
|
+
// Spread conditionally, so a host binding no surface leaves `present` absent.
|
|
3108
2838
|
...input.chatPresent === void 0 ? {} : { present: input.chatPresent },
|
|
3109
2839
|
now: () => clock.now(),
|
|
3110
2840
|
mintTurnId: () => `turn-${(0, import_node_crypto4.randomUUID)()}`
|
|
@@ -3120,19 +2850,7 @@ function composeLocalAgent(input) {
|
|
|
3120
2850
|
...input.resolveWriterAuthority === void 0 ? {} : { resolveWriterAuthority: input.resolveWriterAuthority }
|
|
3121
2851
|
}).list()
|
|
3122
2852
|
},
|
|
3123
|
-
//
|
|
3124
|
-
//
|
|
3125
|
-
// ONE CONNECTION PER VERB, opened read-only and closed in `finally`, on the shape
|
|
3126
|
-
// `readProjectedTestHistory` above already takes. An absent database, a store predating the
|
|
3127
|
-
// projection tables and an identity nothing has counted all answer EMPTY — which
|
|
3128
|
-
// `assessSuiteStability` turns into the honest `no-history` posture rather than into a clean
|
|
3129
|
-
// bill of health. Any other read failure propagates: a corrupt store is not "no history".
|
|
3130
|
-
//
|
|
3131
|
-
// THE OUTCOME WORDS. The durable window keeps one boolean per build (`last_outcomes_json`),
|
|
3132
|
-
// which is what the fold reduced each member's outcome to, so this spells it back with the two
|
|
3133
|
-
// words it was reduced FROM. Nothing downstream compares those strings against a member list —
|
|
3134
|
-
// `assessSuiteStability` counts DISTINCT values — so a flip in the window is a flip here, and
|
|
3135
|
-
// no vocabulary is invented beyond what the projection actually kept.
|
|
2853
|
+
// One read-only connection per verb; absent data answers empty, other failures propagate.
|
|
3136
2854
|
outcomes: {
|
|
3137
2855
|
testsIn: async (product, filePath) => {
|
|
3138
2856
|
const knowledgeRoot = await input.knowledgeRoot();
|
|
@@ -3166,145 +2884,47 @@ function composeLocalAgent(input) {
|
|
|
3166
2884
|
return runsReader.get(runId);
|
|
3167
2885
|
}
|
|
3168
2886
|
},
|
|
3169
|
-
//
|
|
3170
|
-
// an inspect, a diff or a show surfaces gets its permanent address the first time it
|
|
3171
|
-
// is surfaced — which is what "pre-existing entities get IDs the day they are first
|
|
3172
|
-
// referenced" means in practice, and it costs the creation paths nothing.
|
|
2887
|
+
// List verbs are live sighting sites: an entity gets its address when first surfaced.
|
|
3173
2888
|
knowledge: createKnowledgeReader(
|
|
3174
2889
|
input.knowledgeRoot,
|
|
3175
|
-
createMemorySightingHook(entityResolutionInput)
|
|
3176
|
-
input.resolvePromotionPosture,
|
|
3177
|
-
input.resolveTrustPriors
|
|
2890
|
+
createMemorySightingHook(entityResolutionInput)
|
|
3178
2891
|
),
|
|
3179
2892
|
config: input.config,
|
|
3180
2893
|
entities: createEntityDirectory(entityResolutionInput),
|
|
3181
|
-
//
|
|
3182
|
-
// local host owns the knowledge root it serves from. It stays an OPTIONAL dep so a host
|
|
3183
|
-
// that is genuinely read-only — a listing, a remote surface — reports the four mutating
|
|
3184
|
-
// verbs as `unbound` BY NAME rather than as a hole in the catalogue.
|
|
2894
|
+
// Optional, so a read-only host reports the mutating verbs as `unbound` by name.
|
|
3185
2895
|
knowledgeReview: createKnowledgeReview({
|
|
3186
|
-
...input.resolveTrustPriors === void 0 ? {} : { resolveTrustPriors: input.resolveTrustPriors },
|
|
3187
|
-
...input.resolvePromotionPosture === void 0 ? {} : { resolvePromotionPosture: input.resolvePromotionPosture },
|
|
3188
2896
|
agentQaRoot: input.agentQaRoot,
|
|
3189
2897
|
resolveKnowledgeRoot: input.knowledgeRoot,
|
|
3190
2898
|
...input.knowledgeChangeDelegate === void 0 ? {} : { knowledgeChangeDelegate: input.knowledgeChangeDelegate },
|
|
3191
2899
|
...input.resolveWriterAuthority === void 0 ? {} : { resolveWriterAuthority: input.resolveWriterAuthority },
|
|
3192
|
-
//
|
|
3193
|
-
// actually made in. (432-39: the registry no longer receives it; this is its one reader.)
|
|
2900
|
+
// The host's own context, so a direct change records the context it is made in.
|
|
3194
2901
|
...input.resolveExecutionContext === void 0 ? {} : { resolveExecutionContext: input.resolveExecutionContext }
|
|
3195
2902
|
}),
|
|
3196
|
-
//
|
|
3197
|
-
// different questions and conflating them would be the bug: `postRun` says whether this
|
|
3198
|
-
// host can DO the chain's work, while this says whether it can SEE what the chain did —
|
|
3199
|
-
// and a host that never drains anything (a status query, a listing, the CLI after a
|
|
3200
|
-
// handed-off tail) is exactly the caller that most needs to read durable state.
|
|
3201
|
-
//
|
|
3202
|
-
// Given the SAME id source the queue below is built with, so the rows projected are the
|
|
3203
|
-
// rows enqueued. It opens nothing here: the reader constructs no handle until it is asked
|
|
3204
|
-
// a question, and never one that could create the database (Pitfall 4).
|
|
2903
|
+
// On every host, unlike `postRun`: a host that never drains still reads what the chain did.
|
|
3205
2904
|
postProcessing: createPostProcessingReader({
|
|
3206
2905
|
agentQaRoot: input.agentQaRoot,
|
|
3207
2906
|
idSource: input.idSource ?? DIGEST_JOB_ID_SOURCE
|
|
3208
2907
|
}),
|
|
3209
|
-
//
|
|
3210
|
-
// absent port must be an ABSENT field and not a present `undefined` one, because the
|
|
3211
|
-
// registry reads exactly this record to decide what the capability LISTS as. Unlike
|
|
3212
|
-
// `postProcessing` above it is NOT present on every local host: chip truth is readable from
|
|
3213
|
-
// `agentQaRoot` alone, but a run's projected step tree lives in a dashboard database that
|
|
3214
|
-
// only the dashboard process holds.
|
|
3215
|
-
// 423 D-04: runs.evidence shares this reader with runs.detail and the blob reader
|
|
3216
|
-
// below. The registry requires both for evidence without narrowing detail availability.
|
|
2908
|
+
// An absent port must be an absent field: the registry lists capabilities off this record.
|
|
3217
2909
|
...input.runDetail === void 0 ? {} : { runDetail: input.runDetail },
|
|
3218
|
-
//
|
|
3219
|
-
// conditionally spread — unlike `runDetail` one line up, which needs a dashboard database
|
|
3220
|
-
// this host may not hold, every local composition has a runtime root and therefore has the
|
|
3221
|
-
// run directories these blobs live in. It is the SAME reader the analysis job's
|
|
3222
|
-
// `readEvidenceBlob` is built over, so "resolve this ref" and "diet this evidence" cannot
|
|
3223
|
-
// disagree about where a blob is.
|
|
2910
|
+
// On every host, and the same reader the analysis job uses, so both agree where a blob is.
|
|
3224
2911
|
evidenceBlobs: createEvidenceBlobReader({ agentQaRoot: input.agentQaRoot }),
|
|
3225
|
-
// Present
|
|
3226
|
-
// decide what `runs.start` lists as, so the declaration and the dispatch cannot
|
|
3227
|
-
// disagree about whether this composition can run it (D-16).
|
|
2912
|
+
// Present only if this host can run; the registry lists `runs.start` from the same record.
|
|
3228
2913
|
...input.runExecution === void 0 ? {} : {
|
|
3229
2914
|
runInvocation: createLocalRunInvocation({
|
|
3230
2915
|
agentQaRoot: input.agentQaRoot,
|
|
3231
2916
|
execution: input.runExecution
|
|
3232
2917
|
})
|
|
3233
2918
|
},
|
|
3234
|
-
//
|
|
3235
|
-
// recurrence join the one renderer consults. Both are bindable from `agentQaRoot`
|
|
3236
|
-
// and the knowledge root alone, so every local composition carries them — a host
|
|
3237
|
-
// with no artifact on disk serves the floor, and a host with no recorded history
|
|
3238
|
-
// renders the explicit no-history clause; neither is a refusal (plan 05's law).
|
|
2919
|
+
// Bindable from the roots alone, so every host serves the floor or the no-history clause.
|
|
3239
2920
|
analysis: createAnalysisArtifactReader(input.agentQaRoot),
|
|
3240
2921
|
recurrence: recurrence.provider,
|
|
3241
|
-
//
|
|
3242
|
-
// straight through from the host. Conditionally spread rather than assigned, on the
|
|
3243
|
-
// `runInvocation` precedent: an absent port must be an ABSENT field and not a present
|
|
3244
|
-
// `undefined` one, because the registry reads exactly this record to decide what the
|
|
3245
|
-
// capability LISTS as — so the declaration and the dispatch cannot disagree about whether
|
|
3246
|
-
// this composition can explain itself (D-16).
|
|
2922
|
+
// Spread conditionally so `agent.explain` lists `unbound` where no catalog is handed in.
|
|
3247
2923
|
...input.capabilityCatalog === void 0 ? {} : { capabilityCatalog: input.capabilityCatalog },
|
|
3248
|
-
//
|
|
3249
|
-
// must be an ABSENT field, so `runs.liveurl` declares `unbound` where no origin is served.
|
|
2924
|
+
// Spread conditionally, so `runs.liveurl` declares `unbound` where no origin is served.
|
|
3250
2925
|
...input.liveView === void 0 ? {} : { liveView: input.liveView },
|
|
3251
|
-
//
|
|
3252
|
-
// the CLI tail and 386's daemon compose the same root, which is the whole point of
|
|
3253
|
-
// 385 D-15 (a second binding site is a second answer to "what is quarantined").
|
|
3254
|
-
//
|
|
3255
|
-
// Present in every local composition for the same reason the knowledge WRITE half is: a
|
|
3256
|
-
// local host owns the committed knowledge root the ledger lives at. It stays an OPTIONAL
|
|
3257
|
-
// dep so a genuinely read-only or remote composition reports all three `unbound` BY NAME
|
|
3258
|
-
// rather than answering an empty list about a ledger it has never been able to open.
|
|
3259
|
-
//
|
|
3260
|
-
// The port takes the root as a FACTORY (the CLI resolves it asynchronously) and the clock
|
|
3261
|
-
// as an argument, so this composition supplies both and the implementation reads neither
|
|
3262
|
-
// of its own accord. Nothing opens here: the first byte is read when a verb is invoked.
|
|
3263
|
-
//
|
|
3264
|
-
// 390-09 / CR-03: the SAME `resolveWriterAuthority` the apply path consumes three bindings
|
|
3265
|
-
// above, and it is what lands each host on the correct lease arm with zero daemon changes.
|
|
3266
|
-
// The daemon supplies it (`cli/src/daemon/host.ts:1839`) because it acquired the lease at
|
|
3267
|
-
// startup and holds it for its lifetime, so the verbs ride that authority instead of asking
|
|
3268
|
-
// for a lease the process cannot be granted twice; the CLI in-process host supplies nothing,
|
|
3269
|
-
// so the verbs acquire and release per call. Conditionally spread like its neighbour rather
|
|
3270
|
-
// than assigned through: an absent authority must be an ABSENT field, not a present
|
|
3271
|
-
// `undefined` one, so the implementation's `?.()` arm distinguishes the two hosts.
|
|
3272
|
-
//
|
|
3273
|
-
// 390-12 / D-01: the identity mint is handed straight through, REQUIRED and unspread. The
|
|
3274
|
-
// verbs key the committed ledger on `filePath` and every reader compares those bytes with
|
|
3275
|
-
// `===`, so a host that supplied nothing would be back to storing whatever a human typed —
|
|
3276
|
-
// the round-2 regression. Unlike the authority above there is no host without a checkout
|
|
3277
|
-
// root, so the absent case is a compile error rather than a second posture.
|
|
3278
|
-
// 394-07 D-12 / WTST-05: the honesty funnel's reads, bound HERE and therefore on BOTH hosts,
|
|
3279
|
-
// which is the whole point of 385 D-15 — a second binding site would be a second answer to
|
|
3280
|
-
// "how many drafts did this session keep", and that number is the one WTST-05 exists to keep
|
|
3281
|
-
// honest.
|
|
3282
|
-
//
|
|
3283
|
-
// UNCONDITIONAL, unlike the loop's two halves above it, and the asymmetry is the point: the
|
|
3284
|
-
// funnel is a READ over records this host owns by owning `agentQaRoot`, so every local
|
|
3285
|
-
// composition can answer it whether or not it can author anything. An empty receipts
|
|
3286
|
-
// directory and an absent one are the same answer, and nothing opens here — the first byte
|
|
3287
|
-
// is read when the verb is invoked.
|
|
3288
|
-
//
|
|
3289
|
-
// THE RECEIPTS PORT IS THE READ HALF ONLY. The evaluator one binding below is handed the
|
|
3290
|
-
// same store's `put`; this bundle names `list` and nothing else, so the projection is
|
|
3291
|
-
// structurally unable to write the records it reports on. Two narrow mirrors over one store
|
|
3292
|
-
// rather than one wide port.
|
|
3293
|
-
//
|
|
3294
|
-
// 2026-10-01 (432-17, D-06): the bundle no longer reads the proposals inbox. 2026-10-02
|
|
3295
|
-
// (quick 261002-ctr): the terminal stage is `writeEligible`, read off the receipts this
|
|
3296
|
-
// bundle lists (see `projectAuthoringFunnel`).
|
|
3297
|
-
//
|
|
3298
|
-
// `outcomes` IS BOUND (394-17, step b): the port's two verbs now LEAD with `product`, so the
|
|
3299
|
-
// binding reads the run-outcome projection on its own primary key instead of guessing a
|
|
3300
|
-
// partition or serving every product's history under one file path.
|
|
3301
|
-
//
|
|
3302
|
-
// ASSEMBLED ABOVE rather than inline, because the gate-2 flip path is handed the SAME
|
|
3303
|
-
// bundle: one composition-level answer to "what does this project know about this suite",
|
|
3304
|
-
// reached by the funnel read op and by the evaluator's consultation alike.
|
|
2926
|
+
// The funnel reads records this host owns, so it is unconditional; its receipts port lists.
|
|
3305
2927
|
authoringFunnel,
|
|
3306
|
-
// 2026-10-01 (432-17, D-05/D-22): the proposals inbox (391 PROP-03) is not bound — it is deleted
|
|
3307
|
-
// with its ops. Old files under `<agentQaRoot>/proposals/` are never opened again.
|
|
3308
2928
|
...input.feedback === void 0 ? {} : { feedback: input.feedback },
|
|
3309
2929
|
...input.feedbackReads === void 0 ? {} : { feedbackReads: input.feedbackReads },
|
|
3310
2930
|
quarantine: (0, import_agent_qa_knowledge_local.createQuarantineOperations)({
|
|
@@ -3313,24 +2933,13 @@ function composeLocalAgent(input) {
|
|
|
3313
2933
|
mintIdentityPath: input.mintQuarantineIdentityPath,
|
|
3314
2934
|
...input.resolveWriterAuthority === void 0 ? {} : { resolveWriterAuthority: input.resolveWriterAuthority }
|
|
3315
2935
|
}),
|
|
3316
|
-
//
|
|
3317
|
-
// `agent.ask` is a real capability wherever the composition root runs — and unbound nowhere,
|
|
3318
|
-
// which is what lets 397's parity suite read it off the registry with no chat-specific case.
|
|
2936
|
+
// Bound here, so `agent.ask` is a real capability on every host.
|
|
3319
2937
|
chatTurn: {
|
|
3320
2938
|
// The engine also echoes authored clause text on receipts and refusal answers. Mask
|
|
3321
2939
|
// the complete outward projection after execution, never the message it dispatches.
|
|
3322
2940
|
runTurn: async (turnInput) => projectChatMembers(await chatTurn.runTurn(turnInput), CHAT_TURN_RESULT_RULES, input.mask)
|
|
3323
2941
|
},
|
|
3324
|
-
//
|
|
3325
|
-
// BOTH hosts — the CLI and 386's daemon compose this same root, which is the whole point of
|
|
3326
|
-
// 385 D-15 (a second binding site would be a second answer to "is this draft worth keeping",
|
|
3327
|
-
// and the two would disagree about a decision that gets written into a durable receipt).
|
|
3328
|
-
//
|
|
3329
|
-
// Conditionally spread on BOTH halves, because a capability that can parse but cannot run is
|
|
3330
|
-
// not a degraded evaluator — it is a host that must say `unbound` by name. `runs.start` is
|
|
3331
|
-
// dispatched through the registry this very call is about to produce, which is the same
|
|
3332
|
-
// deferred reference the chat engine takes and is safe for the same reason: no op can run
|
|
3333
|
-
// before the registry it is registered in has been built.
|
|
2942
|
+
// Both halves spread conditionally: a host that can parse but not run says `unbound`.
|
|
3334
2943
|
...input.draftAuthoring === void 0 || input.runExecution === void 0 ? {} : {
|
|
3335
2944
|
draftEvaluation: createDraftEvaluation({
|
|
3336
2945
|
authoring: input.draftAuthoring,
|
|
@@ -3351,50 +2960,7 @@ function composeLocalAgent(input) {
|
|
|
3351
2960
|
}
|
|
3352
2961
|
})
|
|
3353
2962
|
},
|
|
3354
|
-
//
|
|
3355
|
-
// hosts, under the SAME condition the evaluator above is bound under — the loop's stage 4
|
|
3356
|
-
// dispatches `authoring.draft.verify`, so a host that cannot verify a draft cannot author
|
|
3357
|
-
// one either, and a third answer to "can this host author" would be a third thing to keep
|
|
3358
|
-
// in step.
|
|
3359
|
-
//
|
|
3360
|
-
// THE READ HALF IS `getAuthoringBrief`'S FIRST PRODUCTION CALLER. The port has been
|
|
3361
|
-
// implemented and unbound since v51 (`knowledge-local/src/serving/authoring-reader.ts`),
|
|
3362
|
-
// which means every field of its contract is crossing a real seam for the first time here;
|
|
3363
|
-
// a mismatch surfaces at this binding rather than in a surface. The reader is built PER
|
|
3364
|
-
// CALL over a freshly resolved knowledge root, on the standing-approval reader's stated
|
|
3365
|
-
// reasoning — the CLI resolves its root asynchronously and per call, and a root captured
|
|
3366
|
-
// once would serve a brief out of a knowledge tree the invocation is not serving from. No
|
|
3367
|
-
// `product` is bound: the composition does not own one, and the SCOPE arrives on the op's
|
|
3368
|
-
// input, which is the honest half of the pair the reader filters on.
|
|
3369
|
-
//
|
|
3370
|
-
// `suiteTextCorpus` is the SAME host walk gate 3's degraded lane already consumes, reached
|
|
3371
|
-
// here for stage 2's pre-authoring screen. One walk, one notion of what a suite test's text
|
|
3372
|
-
// is; a second enumeration would let the screen and the gate disagree about the same pair.
|
|
3373
|
-
//
|
|
3374
|
-
// THE WRITE HALF bundles the model, the shelf and the verify dispatch, because none of the
|
|
3375
|
-
// three is independently bindable: a host with a model and no shelf cannot author, one with
|
|
3376
|
-
// a shelf and no model has nothing to author with, and one that can do both but cannot
|
|
3377
|
-
// verify would produce a test and have nothing to say about whether it is worth keeping.
|
|
3378
|
-
// The model is a FACTORY, resolved at most once and only when a draft is actually compiled,
|
|
3379
|
-
// the same idiom `openJobs` uses — binding this capability must not construct a client for a
|
|
3380
|
-
// host that never authors.
|
|
3381
|
-
//
|
|
3382
|
-
// WHY THE ROUTER (plan 05) WILL READ `DraftRunPortLike.get`'s `CompletedRunHandoff` AND NOT
|
|
3383
|
-
// `runs.get`'s PROJECTION. D-08 routes on run EVIDENCE — step terminations, planning
|
|
3384
|
-
// failure, cycle flags, the per-step sub-action record — and `runs.get` projects a
|
|
3385
|
-
// `RunDetail` in which none of that survives (`stepCount` is its only step-shaped field).
|
|
3386
|
-
// This is `createDraftEvaluation`'s own posture, stated one layer out: the op is reached for
|
|
3387
|
-
// what an op owns, and the reader is read for evidence no projection carries. A router built
|
|
3388
|
-
// on the projection would have to infer a mechanics failure from a member verdict, and
|
|
3389
|
-
// inferring an app defect out of a bare member verdict is precisely the cardinal error D-08
|
|
3390
|
-
// exists to make unreachable.
|
|
3391
|
-
//
|
|
3392
|
-
// A NOTE FOR WHOEVER EDITS THIS PARAGRAPH. The arch dependency scan reads import specifiers
|
|
3393
|
-
// out of this file's RAW BYTES, comments included, so any prose shaped like an import's
|
|
3394
|
-
// trailing clause — the keyword, then a quoted phrase — is reported as an undeclared
|
|
3395
|
-
// external dependency of this package. Two drafts of this comment tripped it, once on a
|
|
3396
|
-
// sentence about verdicts and once on the sentence explaining the first. Keep quoted phrases
|
|
3397
|
-
// away from that keyword here.
|
|
2963
|
+
// The arch import scan reads comments too: never put a quote after the word `from`.
|
|
3398
2964
|
...authoringLoopHost === void 0 ? {} : {
|
|
3399
2965
|
authoringRead: {
|
|
3400
2966
|
async getAuthoringBrief(intent, scope) {
|
|
@@ -3402,35 +2968,21 @@ function composeLocalAgent(input) {
|
|
|
3402
2968
|
return (0, import_agent_qa_knowledge_local.createAuthoringReader)({ knowledgeRoot }).getAuthoringBrief(intent, scope);
|
|
3403
2969
|
},
|
|
3404
2970
|
suiteTextCorpus: () => authoringLoopHost.suiteTextCorpus(),
|
|
3405
|
-
//
|
|
3406
|
-
// SAME port the brief and the corpus come from, so a host that can run the loop's read
|
|
3407
|
-
// stages can name its own targets by construction — see the port's own doc.
|
|
2971
|
+
// Off the same port as the brief and corpus, so a loop host names its own targets.
|
|
3408
2972
|
configuredTargetNames: () => authoringLoopHost.configuredTargetNames()
|
|
3409
2973
|
},
|
|
3410
|
-
//
|
|
3411
|
-
// BOTH hosts, off the same port the read and compile halves come from. Its own deps key
|
|
3412
|
-
// rather than a third verb on `authoringRead`, because deriving a scope needs the resolved
|
|
3413
|
-
// config and the workspace while the brief needs a knowledge index — a host can have one
|
|
3414
|
-
// without the other, and the op refuses on THIS key by name when a scope-less call arrives
|
|
3415
|
-
// at a composition that never bound it.
|
|
2974
|
+
// Its own key: a scope needs config and workspace, the brief needs an index.
|
|
3416
2975
|
authoringScope: {
|
|
3417
2976
|
deriveDefault: () => authoringLoopHost.deriveDefaultScope()
|
|
3418
2977
|
},
|
|
3419
2978
|
draftShelf: {
|
|
3420
2979
|
model: async () => await input.model(),
|
|
3421
2980
|
write: (contents) => authoringLoopHost.stageDraft(contents),
|
|
3422
|
-
//
|
|
3423
|
-
// yaml CST and neither `agent` nor this package may depend on `core`. Same seam,
|
|
3424
|
-
// same reason and same shape as `stageDraft` right above it.
|
|
2981
|
+
// The one RFC 6902 applier, at the host because it needs `core`'s yaml CST.
|
|
3425
2982
|
applyPatches: (draft, patchSet) => authoringLoopHost.applyPatchesToDraft(draft, patchSet),
|
|
3426
|
-
//
|
|
3427
|
-
// and same reason as the two verbs above: the boundary check needs `core`.
|
|
2983
|
+
// Same seam as the verbs above: the boundary check needs `core`.
|
|
3428
2984
|
publish: (request) => authoringLoopHost.publishDraft(request),
|
|
3429
|
-
//
|
|
3430
|
-
// the same `runsReader` the gate evaluator is bound to — a second reader would be a
|
|
3431
|
-
// second answer to what one run recorded — and the starter dispatches the REGISTERED
|
|
3432
|
-
// `runs.start` on the gate-run caller, so a probe is marker-stamped exactly as a gate
|
|
3433
|
-
// run is and is excluded from outcomes for the same reason.
|
|
2985
|
+
// The evaluator's reader; probes use the gate-run caller, so outcomes exclude them.
|
|
3434
2986
|
runs: {
|
|
3435
2987
|
start: async (request) => {
|
|
3436
2988
|
if (composedRegistry === void 0) {
|
|
@@ -3445,8 +2997,6 @@ function composeLocalAgent(input) {
|
|
|
3445
2997
|
},
|
|
3446
2998
|
get: (runId) => runsReader.get(runId)
|
|
3447
2999
|
},
|
|
3448
|
-
// 2026-10-01 (432-18/432-19, D-15): no dispatch-scoped grant exists. The authoring loop
|
|
3449
|
-
// runs its dispatch directly, and no gate run it starts consults any approval.
|
|
3450
3000
|
verifyDraft: async (verifyInput) => {
|
|
3451
3001
|
if (composedRegistry === void 0) {
|
|
3452
3002
|
throw new TypeError("a draft was verified before its registry finished composing");
|
|
@@ -3459,28 +3009,13 @@ function composeLocalAgent(input) {
|
|
|
3459
3009
|
);
|
|
3460
3010
|
}
|
|
3461
3011
|
}
|
|
3462
|
-
// 2026-10-01 (432-17, D-06): no authoring proposal writer is bound; an authored test is
|
|
3463
|
-
// written in its own call through the shelf's direct writer.
|
|
3464
3012
|
},
|
|
3465
|
-
//
|
|
3466
|
-
// makes no git commits. The final authored test is written through `draftShelf.publish`.
|
|
3467
|
-
//
|
|
3468
|
-
// 395-01 D-01/D-03/D-17 · CFGM-01/03/04: the config editor's host half, each spread on its
|
|
3469
|
-
// OWN condition rather than as one bundle, so a host missing one reports that one `unbound`.
|
|
3013
|
+
// Each config-editor half spreads on its own condition, so a missing one reads `unbound`.
|
|
3470
3014
|
...input.configFiles === void 0 ? {} : { configFiles: input.configFiles },
|
|
3471
3015
|
...input.configValidate === void 0 ? {} : { configValidate: input.configValidate },
|
|
3472
3016
|
...input.scaffoldContent === void 0 ? {} : { scaffoldContent: input.scaffoldContent },
|
|
3473
3017
|
...input.configWrite === void 0 ? {} : { configWrite: input.configWrite }
|
|
3474
|
-
// 2026-10-14 (441-39, D-15): a config change is not scanned for secrets, so no scan is bound.
|
|
3475
|
-
// 2026-10-01 (432-17, D-05): no config proposal filer is bound; the six config ops write in
|
|
3476
|
-
// the call through `configWrite`.
|
|
3477
3018
|
}
|
|
3478
|
-
// 2026-10-15 (441-28, D-15): no audit sink; the guardrail audit went with the guardrails.
|
|
3479
|
-
// 432-13 (D-14/D-15): no standing-approval reader, confirm port or session deny overlay reaches
|
|
3480
|
-
// the registry — `invokeFunction` asks nobody. 432-18 deleted the grant reader with the ledger.
|
|
3481
|
-
// 2026-10-02 (432-39, D-12): the host's execution-context answer is no longer forwarded here.
|
|
3482
|
-
// The registry's option was retired and unread since 432-13 and is deleted; the same answer
|
|
3483
|
-
// still reaches the knowledge review above, which records a direct change under it.
|
|
3484
3019
|
});
|
|
3485
3020
|
composedRegistry = registry;
|
|
3486
3021
|
let opened;
|
|
@@ -3492,7 +3027,6 @@ function composeLocalAgent(input) {
|
|
|
3492
3027
|
const model = await input.model();
|
|
3493
3028
|
const knowledgeRoot = await input.knowledgeRoot();
|
|
3494
3029
|
const analysisTokenBudget = input.analysisTokenBudget === void 0 ? void 0 : await input.analysisTokenBudget();
|
|
3495
|
-
const proposeGate1MaxPerJob = input.postRun?.proposeGate1MaxPerJob === void 0 ? void 0 : await input.postRun.proposeGate1MaxPerJob();
|
|
3496
3030
|
const store = createSqliteJobStore({
|
|
3497
3031
|
path: (0, import_node_path10.join)(input.agentQaRoot, AGENT_JOB_STORE_DB_FILE),
|
|
3498
3032
|
clock,
|
|
@@ -3501,7 +3035,7 @@ function composeLocalAgent(input) {
|
|
|
3501
3035
|
let jobs;
|
|
3502
3036
|
try {
|
|
3503
3037
|
const kinds = input.jobKinds ?? (0, import_agent_qa_agent4.createRealJobKindRegistry)(
|
|
3504
|
-
postRunCapabilities(input, knowledgeRoot, analysisTokenBudget
|
|
3038
|
+
postRunCapabilities(input, knowledgeRoot, analysisTokenBudget)
|
|
3505
3039
|
);
|
|
3506
3040
|
const idSource = input.idSource ?? DIGEST_JOB_ID_SOURCE;
|
|
3507
3041
|
const unclaimableKinds = UNCLAIMABLE_BY_PROSE_POSTURE[input.postRun?.candidateProse ?? "no-masker"];
|
|
@@ -3584,13 +3118,324 @@ function composeLocalAgent(input) {
|
|
|
3584
3118
|
var import_agent_qa_runner_local4 = require("@vostride/agent-qa-runner-local");
|
|
3585
3119
|
var import_agent_qa_runner_local5 = require("@vostride/agent-qa-runner-local");
|
|
3586
3120
|
|
|
3587
|
-
// src/
|
|
3121
|
+
// src/store/mark-ledger.ts
|
|
3122
|
+
var import_node_fs11 = require("node:fs");
|
|
3123
|
+
var import_node_path12 = require("node:path");
|
|
3124
|
+
var import_agent_qa_schema10 = require("@vostride/agent-qa-schema");
|
|
3125
|
+
var import_zod2 = require("zod");
|
|
3126
|
+
|
|
3127
|
+
// src/composition/run-layout.ts
|
|
3128
|
+
var import_agent_qa_runner_local6 = require("@vostride/agent-qa-runner-local");
|
|
3129
|
+
|
|
3130
|
+
// src/store/mark-lock.ts
|
|
3131
|
+
var import_node_crypto5 = require("node:crypto");
|
|
3132
|
+
var import_node_fs10 = require("node:fs");
|
|
3588
3133
|
var import_node_path11 = require("node:path");
|
|
3134
|
+
var import_zod = require("zod");
|
|
3135
|
+
var ownerSchema = import_zod.z.object({
|
|
3136
|
+
schemaVersion: import_zod.z.literal(2),
|
|
3137
|
+
kind: import_zod.z.literal("mark-ledger-lock"),
|
|
3138
|
+
pid: import_zod.z.number().int().positive(),
|
|
3139
|
+
token: import_zod.z.uuid(),
|
|
3140
|
+
runId: import_zod.z.string().min(1)
|
|
3141
|
+
}).strict();
|
|
3142
|
+
function syncDirectory(path) {
|
|
3143
|
+
const fd = (0, import_node_fs10.openSync)(path, "r");
|
|
3144
|
+
try {
|
|
3145
|
+
(0, import_node_fs10.fsyncSync)(fd);
|
|
3146
|
+
} finally {
|
|
3147
|
+
(0, import_node_fs10.closeSync)(fd);
|
|
3148
|
+
}
|
|
3149
|
+
}
|
|
3150
|
+
function readOwner(path, runId) {
|
|
3151
|
+
try {
|
|
3152
|
+
const directory = (0, import_node_fs10.lstatSync)(path);
|
|
3153
|
+
if (!directory.isDirectory() || directory.isSymbolicLink()) return void 0;
|
|
3154
|
+
const ownerFile = (0, import_node_path11.join)(path, "owner.json");
|
|
3155
|
+
const file = (0, import_node_fs10.lstatSync)(ownerFile);
|
|
3156
|
+
if (!file.isFile() || file.isSymbolicLink()) return void 0;
|
|
3157
|
+
const parsed = ownerSchema.safeParse(JSON.parse((0, import_node_fs10.readFileSync)(ownerFile, "utf8")));
|
|
3158
|
+
return parsed.success && parsed.data.runId === runId ? parsed.data : void 0;
|
|
3159
|
+
} catch {
|
|
3160
|
+
return void 0;
|
|
3161
|
+
}
|
|
3162
|
+
}
|
|
3163
|
+
function positivelyDead(pid) {
|
|
3164
|
+
try {
|
|
3165
|
+
process.kill(pid, 0);
|
|
3166
|
+
return false;
|
|
3167
|
+
} catch (error) {
|
|
3168
|
+
return error.code === "ESRCH";
|
|
3169
|
+
}
|
|
3170
|
+
}
|
|
3171
|
+
async function acquireMarkLock(lock, runId, busy) {
|
|
3172
|
+
const owner = { schemaVersion: 2, kind: "mark-ledger-lock", pid: process.pid, token: (0, import_node_crypto5.randomUUID)(), runId };
|
|
3173
|
+
const candidate = `${lock}.candidate-${owner.token}`;
|
|
3174
|
+
(0, import_node_fs10.mkdirSync)(candidate);
|
|
3175
|
+
const fd = (0, import_node_fs10.openSync)((0, import_node_path11.join)(candidate, "owner.json"), "wx", 384);
|
|
3176
|
+
try {
|
|
3177
|
+
(0, import_node_fs10.writeFileSync)(fd, JSON.stringify(owner));
|
|
3178
|
+
(0, import_node_fs10.fsyncSync)(fd);
|
|
3179
|
+
} finally {
|
|
3180
|
+
(0, import_node_fs10.closeSync)(fd);
|
|
3181
|
+
}
|
|
3182
|
+
syncDirectory(candidate);
|
|
3183
|
+
const started = Date.now();
|
|
3184
|
+
let acquired = false;
|
|
3185
|
+
try {
|
|
3186
|
+
while (!acquired) {
|
|
3187
|
+
try {
|
|
3188
|
+
(0, import_node_fs10.mkdirSync)(lock);
|
|
3189
|
+
(0, import_node_fs10.renameSync)((0, import_node_path11.join)(candidate, "owner.json"), (0, import_node_path11.join)(lock, "owner.json"));
|
|
3190
|
+
syncDirectory(lock);
|
|
3191
|
+
syncDirectory((0, import_node_path11.dirname)(lock));
|
|
3192
|
+
(0, import_node_fs10.rmSync)(candidate, { recursive: true });
|
|
3193
|
+
acquired = true;
|
|
3194
|
+
} catch (error) {
|
|
3195
|
+
if (!["EEXIST", "ENOTEMPTY", "ENOTDIR", "EISDIR", "EPERM", "EACCES"].includes(error.code ?? "")) throw error;
|
|
3196
|
+
const current = readOwner(lock, runId);
|
|
3197
|
+
if (current !== void 0 && positivelyDead(current.pid)) {
|
|
3198
|
+
try {
|
|
3199
|
+
(0, import_node_fs10.renameSync)(lock, `${lock}.dead-${current.token}`);
|
|
3200
|
+
syncDirectory((0, import_node_path11.dirname)(lock));
|
|
3201
|
+
continue;
|
|
3202
|
+
} catch (recoveryError) {
|
|
3203
|
+
if (recoveryError.code === "ENOENT") continue;
|
|
3204
|
+
if (!["EEXIST", "ENOTEMPTY", "ENOTDIR", "EISDIR", "EPERM", "EACCES"].includes(recoveryError.code ?? "")) throw recoveryError;
|
|
3205
|
+
}
|
|
3206
|
+
}
|
|
3207
|
+
const remaining = 1e4 - (Date.now() - started);
|
|
3208
|
+
if (remaining <= 0) throw busy();
|
|
3209
|
+
await new Promise((resolve2) => setTimeout(resolve2, Math.min(50, remaining)));
|
|
3210
|
+
}
|
|
3211
|
+
}
|
|
3212
|
+
} finally {
|
|
3213
|
+
if (!acquired) (0, import_node_fs10.rmSync)(candidate, { recursive: true, force: true });
|
|
3214
|
+
}
|
|
3215
|
+
return () => {
|
|
3216
|
+
if (readOwner(lock, runId)?.token !== owner.token) return;
|
|
3217
|
+
const released = `${lock}.released-${owner.token}`;
|
|
3218
|
+
(0, import_node_fs10.renameSync)(lock, released);
|
|
3219
|
+
syncDirectory((0, import_node_path11.dirname)(lock));
|
|
3220
|
+
(0, import_node_fs10.rmSync)(released, { recursive: true });
|
|
3221
|
+
syncDirectory((0, import_node_path11.dirname)(lock));
|
|
3222
|
+
};
|
|
3223
|
+
}
|
|
3224
|
+
|
|
3225
|
+
// src/store/mark-ledger.ts
|
|
3226
|
+
var MARKS_LEDGER_FILE = "marks.jsonl";
|
|
3227
|
+
var MARKS_LEDGER_LOCK_FILE = "marks.jsonl.lock";
|
|
3228
|
+
var MARK_INTENT_FILE = "mark-intent.json";
|
|
3229
|
+
var MARK_INTENT_STAGES = ["prepared", "evidence", "applied"];
|
|
3230
|
+
var intentSchema = import_zod2.z.object({
|
|
3231
|
+
schemaVersion: import_zod2.z.literal(2),
|
|
3232
|
+
mark: import_agent_qa_schema10.MarkReceiptSchema,
|
|
3233
|
+
stage: import_zod2.z.enum(MARK_INTENT_STAGES),
|
|
3234
|
+
operationId: import_zod2.z.uuid(),
|
|
3235
|
+
plan: import_agent_qa_schema10.ImprovementPlanSchema.optional()
|
|
3236
|
+
}).strict().superRefine((intent, ctx) => {
|
|
3237
|
+
if (intent.stage === "applied" && intent.mark.disposition.kind === "drafted") {
|
|
3238
|
+
ctx.addIssue({ code: "custom", path: ["mark", "disposition"], message: "a mark applies its fix; it never files a draft" });
|
|
3239
|
+
}
|
|
3240
|
+
});
|
|
3241
|
+
function atomicPublish(path, text) {
|
|
3242
|
+
const staged = `${path}.pending`;
|
|
3243
|
+
const fd = (0, import_node_fs11.openSync)(staged, "w", 384);
|
|
3244
|
+
try {
|
|
3245
|
+
(0, import_node_fs11.writeFileSync)(fd, text);
|
|
3246
|
+
(0, import_node_fs11.fsyncSync)(fd);
|
|
3247
|
+
} finally {
|
|
3248
|
+
(0, import_node_fs11.closeSync)(fd);
|
|
3249
|
+
}
|
|
3250
|
+
(0, import_node_fs11.renameSync)(staged, path);
|
|
3251
|
+
}
|
|
3252
|
+
var MarkLedgerBusyError = class extends Error {
|
|
3253
|
+
constructor() {
|
|
3254
|
+
super("Mark ledger is busy; retry the mark after the current transaction finishes");
|
|
3255
|
+
this.name = "MarkLedgerBusyError";
|
|
3256
|
+
}
|
|
3257
|
+
};
|
|
3258
|
+
var MarkLedgerCorruptError = class extends Error {
|
|
3259
|
+
constructor(line) {
|
|
3260
|
+
super(`Mark ledger is corrupt at line ${line}`);
|
|
3261
|
+
this.line = line;
|
|
3262
|
+
this.name = "MarkLedgerCorruptError";
|
|
3263
|
+
}
|
|
3264
|
+
};
|
|
3265
|
+
var MarkIntentUnusableError = class extends Error {
|
|
3266
|
+
constructor(intentPath, reason) {
|
|
3267
|
+
const what = reason === "earlier-version" ? "was written by an earlier agent-qa version, which this version does not read" : reason === "unreadable" ? "is unreadable" : "does not match this run's mark ledger";
|
|
3268
|
+
super(`The unfinished-mark record ${intentPath} ${what}; nothing was marked. Remove that file, then mark again.`);
|
|
3269
|
+
this.intentPath = intentPath;
|
|
3270
|
+
this.reason = reason;
|
|
3271
|
+
this.name = "MarkIntentUnusableError";
|
|
3272
|
+
}
|
|
3273
|
+
};
|
|
3274
|
+
function isEarlierVersionIntent(value) {
|
|
3275
|
+
if (value === null || typeof value !== "object") return false;
|
|
3276
|
+
const intent = value;
|
|
3277
|
+
return "proposalUuid" in intent || "testDraft" in intent || intent["stage"] === "drafted";
|
|
3278
|
+
}
|
|
3279
|
+
function latestMarkReceiptsPerAddress(receipts) {
|
|
3280
|
+
const latest = /* @__PURE__ */ new Map();
|
|
3281
|
+
for (const receipt of receipts) {
|
|
3282
|
+
const key = (0, import_agent_qa_schema10.markAddressKey)(receipt);
|
|
3283
|
+
if ((latest.get(key)?.ordinal ?? 0) < receipt.ordinal) latest.set(key, receipt);
|
|
3284
|
+
}
|
|
3285
|
+
return [...latest.values()].sort(
|
|
3286
|
+
(a, b) => a.testIndex - b.testIndex || (a.stepIndex ?? -1) - (b.stepIndex ?? -1)
|
|
3287
|
+
);
|
|
3288
|
+
}
|
|
3289
|
+
function latestMarkReceiptFor(receipts, runId, address) {
|
|
3290
|
+
if (address.runId !== runId) return void 0;
|
|
3291
|
+
return latestMarkReceiptsPerAddress(receipts).find((receipt) => (0, import_agent_qa_schema10.markAddressKey)(receipt) === (0, import_agent_qa_schema10.markAddressKey)(address) && (address.testId === void 0 || receipt.testId === address.testId));
|
|
3292
|
+
}
|
|
3293
|
+
var MarkLedgerNoRunError = class extends Error {
|
|
3294
|
+
constructor() {
|
|
3295
|
+
super("run-not-found: mark ledger requires a completed run handoff");
|
|
3296
|
+
this.name = "MarkLedgerNoRunError";
|
|
3297
|
+
}
|
|
3298
|
+
};
|
|
3299
|
+
var FsMarkLedger = class {
|
|
3300
|
+
dir;
|
|
3301
|
+
file;
|
|
3302
|
+
lock;
|
|
3303
|
+
runId;
|
|
3304
|
+
runSlug;
|
|
3305
|
+
inTransaction = false;
|
|
3306
|
+
intentValue;
|
|
3307
|
+
constructor(config) {
|
|
3308
|
+
this.runId = config.runId;
|
|
3309
|
+
this.runSlug = (0, import_agent_qa_schema10.slugBranch)(config.runId);
|
|
3310
|
+
this.dir = (0, import_node_path12.join)(config.root, import_agent_qa_runner_local6.RUNS_SUBDIR, this.runSlug);
|
|
3311
|
+
this.file = (0, import_node_path12.join)(this.dir, MARKS_LEDGER_FILE);
|
|
3312
|
+
this.lock = (0, import_node_path12.join)(this.dir, MARKS_LEDGER_LOCK_FILE);
|
|
3313
|
+
}
|
|
3314
|
+
read() {
|
|
3315
|
+
let text;
|
|
3316
|
+
try {
|
|
3317
|
+
text = (0, import_node_fs11.readFileSync)(this.file, "utf8");
|
|
3318
|
+
} catch (error) {
|
|
3319
|
+
if (error.code === "ENOENT") return [];
|
|
3320
|
+
throw error;
|
|
3321
|
+
}
|
|
3322
|
+
if (text === "") return [];
|
|
3323
|
+
const lines = text.split("\n");
|
|
3324
|
+
if (lines.pop() !== "") throw new MarkLedgerCorruptError(lines.length + 1);
|
|
3325
|
+
return lines.map((line, index) => {
|
|
3326
|
+
try {
|
|
3327
|
+
const receipt = import_agent_qa_schema10.MarkReceiptSchema.parse(JSON.parse(line));
|
|
3328
|
+
if (receipt.ordinal !== index + 1 || receipt.runId !== this.runId || receipt.runSlug !== this.runSlug) {
|
|
3329
|
+
throw new Error("ledger identity or ordinal mismatch");
|
|
3330
|
+
}
|
|
3331
|
+
return receipt;
|
|
3332
|
+
} catch {
|
|
3333
|
+
throw new MarkLedgerCorruptError(index + 1);
|
|
3334
|
+
}
|
|
3335
|
+
});
|
|
3336
|
+
}
|
|
3337
|
+
latestPerAddress() {
|
|
3338
|
+
return latestMarkReceiptsPerAddress(this.read());
|
|
3339
|
+
}
|
|
3340
|
+
latestFor(address) {
|
|
3341
|
+
if (address.runId !== this.runId) return void 0;
|
|
3342
|
+
return latestMarkReceiptFor(this.read(), this.runId, address);
|
|
3343
|
+
}
|
|
3344
|
+
/** Private resumable state is admitted only while this run's existing lock is held. */
|
|
3345
|
+
prepareIntent(mark, operationId) {
|
|
3346
|
+
if (!this.inTransaction) throw new Error("mark intent requires the run transaction");
|
|
3347
|
+
const prior = this.intentValue;
|
|
3348
|
+
if (prior !== void 0) {
|
|
3349
|
+
const request = (value) => ({
|
|
3350
|
+
runId: value.runId,
|
|
3351
|
+
testIndex: value.testIndex,
|
|
3352
|
+
testId: value.testId,
|
|
3353
|
+
stepIndex: value.stepIndex,
|
|
3354
|
+
kind: value.kind,
|
|
3355
|
+
note: value.note
|
|
3356
|
+
});
|
|
3357
|
+
if ((0, import_agent_qa_schema10.canonicalJson)(request(prior.mark)) !== (0, import_agent_qa_schema10.canonicalJson)(request(mark))) {
|
|
3358
|
+
throw new Error("An unfinished mark must be retried with its original address, kind and note");
|
|
3359
|
+
}
|
|
3360
|
+
return prior;
|
|
3361
|
+
}
|
|
3362
|
+
return this.checkpointIntent({ schemaVersion: 2, mark, operationId, stage: "prepared" });
|
|
3363
|
+
}
|
|
3364
|
+
checkpointIntent(value) {
|
|
3365
|
+
if (!this.inTransaction) throw new Error("mark intent requires the run transaction");
|
|
3366
|
+
const intent = intentSchema.parse(value);
|
|
3367
|
+
if (intent.mark.runId !== this.runId || intent.mark.runSlug !== this.runSlug || intent.mark.ordinal !== this.read().length + 1) throw new MarkLedgerCorruptError(intent.mark.ordinal);
|
|
3368
|
+
atomicPublish((0, import_node_path12.join)(this.dir, MARK_INTENT_FILE), `${(0, import_agent_qa_schema10.canonicalJson)(intent)}
|
|
3369
|
+
`);
|
|
3370
|
+
this.intentValue = intent;
|
|
3371
|
+
return intent;
|
|
3372
|
+
}
|
|
3373
|
+
async transact(fn) {
|
|
3374
|
+
if (!(0, import_node_fs11.existsSync)((0, import_node_path12.join)(this.dir, import_agent_qa_runner_local6.HANDOFF_FILE))) throw new MarkLedgerNoRunError();
|
|
3375
|
+
const release = await acquireMarkLock(this.lock, this.runId, () => new MarkLedgerBusyError());
|
|
3376
|
+
try {
|
|
3377
|
+
const receipts = this.read();
|
|
3378
|
+
const nextOrdinal = receipts.length + 1;
|
|
3379
|
+
const intentPath = (0, import_node_path12.join)(this.dir, MARK_INTENT_FILE);
|
|
3380
|
+
this.intentValue = void 0;
|
|
3381
|
+
if ((0, import_node_fs11.existsSync)(intentPath)) {
|
|
3382
|
+
let raw;
|
|
3383
|
+
try {
|
|
3384
|
+
raw = JSON.parse((0, import_node_fs11.readFileSync)(intentPath, "utf8"));
|
|
3385
|
+
} catch {
|
|
3386
|
+
throw new MarkIntentUnusableError(intentPath, "unreadable");
|
|
3387
|
+
}
|
|
3388
|
+
const parsed = intentSchema.safeParse(raw);
|
|
3389
|
+
if (!parsed.success) {
|
|
3390
|
+
throw new MarkIntentUnusableError(
|
|
3391
|
+
intentPath,
|
|
3392
|
+
isEarlierVersionIntent(raw) ? "earlier-version" : "unreadable"
|
|
3393
|
+
);
|
|
3394
|
+
}
|
|
3395
|
+
const intent = parsed.data;
|
|
3396
|
+
if (intent.mark.runId !== this.runId || intent.mark.runSlug !== this.runSlug) {
|
|
3397
|
+
throw new MarkIntentUnusableError(intentPath, "mismatched");
|
|
3398
|
+
}
|
|
3399
|
+
if (intent.mark.ordinal === nextOrdinal) this.intentValue = intent;
|
|
3400
|
+
else if ((0, import_agent_qa_schema10.canonicalJson)(receipts[intent.mark.ordinal - 1]) === (0, import_agent_qa_schema10.canonicalJson)(intent.mark)) (0, import_node_fs11.unlinkSync)(intentPath);
|
|
3401
|
+
else throw new MarkIntentUnusableError(intentPath, "mismatched");
|
|
3402
|
+
}
|
|
3403
|
+
this.inTransaction = true;
|
|
3404
|
+
const record = await fn(nextOrdinal);
|
|
3405
|
+
if (record === void 0) return void 0;
|
|
3406
|
+
const receipt = import_agent_qa_schema10.MarkReceiptSchema.parse({ ...record, ordinal: nextOrdinal });
|
|
3407
|
+
if (receipt.runId !== this.runId || receipt.runSlug !== this.runSlug) {
|
|
3408
|
+
throw new Error("mark receipt does not belong to this run ledger");
|
|
3409
|
+
}
|
|
3410
|
+
if (this.intentValue !== void 0 && (this.intentValue.stage !== "applied" || (0, import_agent_qa_schema10.canonicalJson)(this.intentValue.mark) !== (0, import_agent_qa_schema10.canonicalJson)(receipt))) {
|
|
3411
|
+
throw new MarkLedgerCorruptError(nextOrdinal);
|
|
3412
|
+
}
|
|
3413
|
+
const before = (0, import_node_fs11.existsSync)(this.file) ? (0, import_node_fs11.readFileSync)(this.file, "utf8") : "";
|
|
3414
|
+
atomicPublish(this.file, `${before}${(0, import_agent_qa_schema10.canonicalJson)(receipt)}
|
|
3415
|
+
`);
|
|
3416
|
+
if (this.intentValue !== void 0) (0, import_node_fs11.unlinkSync)(intentPath);
|
|
3417
|
+
return receipt;
|
|
3418
|
+
} finally {
|
|
3419
|
+
this.inTransaction = false;
|
|
3420
|
+
this.intentValue = void 0;
|
|
3421
|
+
release();
|
|
3422
|
+
}
|
|
3423
|
+
}
|
|
3424
|
+
append(record) {
|
|
3425
|
+
return this.transact(async () => record);
|
|
3426
|
+
}
|
|
3427
|
+
};
|
|
3428
|
+
|
|
3429
|
+
// src/composition/action-cache.ts
|
|
3430
|
+
var import_agent_qa_runner_local7 = require("@vostride/agent-qa-runner-local");
|
|
3431
|
+
|
|
3432
|
+
// src/composition/compose-job-recovery.ts
|
|
3433
|
+
var import_node_path13 = require("node:path");
|
|
3589
3434
|
var import_agent_qa_agent5 = require("@vostride/agent-qa-agent");
|
|
3590
3435
|
var JOB_RECOVERY_MAX_RESUMED_JOBS = 64;
|
|
3591
3436
|
function composeJobRecovery(input) {
|
|
3592
3437
|
const store = createSqliteJobStore({
|
|
3593
|
-
path: (0,
|
|
3438
|
+
path: (0, import_node_path13.join)(input.agentQaRoot, AGENT_JOB_STORE_DB_FILE),
|
|
3594
3439
|
clock: input.clock,
|
|
3595
3440
|
checkpoints: createJobCheckpointStore({ root: input.agentQaRoot })
|
|
3596
3441
|
});
|
|
@@ -3711,7 +3556,6 @@ function createMemorySessionLog(options) {
|
|
|
3711
3556
|
}
|
|
3712
3557
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3713
3558
|
0 && (module.exports = {
|
|
3714
|
-
ALLOW_PRODUCTION_OPT_IN,
|
|
3715
3559
|
BRIEF_CEILING,
|
|
3716
3560
|
BRIEF_FRACTION,
|
|
3717
3561
|
BuildOrdinalReservationError,
|
|
@@ -3728,25 +3572,32 @@ function createMemorySessionLog(options) {
|
|
|
3728
3572
|
EMBEDDER_WORKER_TERM_GRACE_MS,
|
|
3729
3573
|
ENTITY_INDEX_SWEEP_INTERVAL_MS,
|
|
3730
3574
|
EmbedderWorkerTimeoutError,
|
|
3575
|
+
FsMarkLedger,
|
|
3731
3576
|
GLOBAL_RECORD_CAP,
|
|
3577
|
+
HANDOFF_FILE,
|
|
3732
3578
|
InvalidChatSessionIdError,
|
|
3733
3579
|
JOB_RECOVERY_MAX_RESUMED_JOBS,
|
|
3734
3580
|
LOCAL_CURATION_CAPABILITIES,
|
|
3735
3581
|
LOCAL_DAEMON_CAPABILITIES,
|
|
3736
3582
|
LOCAL_KNOWLEDGE_PROBES,
|
|
3737
3583
|
LocalAgentClosedError,
|
|
3584
|
+
MARKS_LEDGER_FILE,
|
|
3585
|
+
MARKS_LEDGER_LOCK_FILE,
|
|
3586
|
+
MARK_INTENT_FILE,
|
|
3738
3587
|
MIN_BRIEF,
|
|
3739
3588
|
MIN_CAUTION,
|
|
3740
3589
|
MIN_MEMORY_TOOL_CONTEXT_WINDOW,
|
|
3590
|
+
MarkIntentUnusableError,
|
|
3591
|
+
MarkLedgerBusyError,
|
|
3592
|
+
MarkLedgerCorruptError,
|
|
3593
|
+
MarkLedgerNoRunError,
|
|
3741
3594
|
QUARANTINE_CLASS_DISPOSITIONS,
|
|
3742
3595
|
QUARANTINE_FLOOR_ELIGIBILITY,
|
|
3743
3596
|
READ_BODY_FRACTION,
|
|
3744
|
-
|
|
3745
|
-
RunDeniedError,
|
|
3597
|
+
RUNS_SUBDIR,
|
|
3746
3598
|
RunIdAlreadyInUseError,
|
|
3747
3599
|
RunIdNotMintedError,
|
|
3748
3600
|
RunProducedNoEventsError,
|
|
3749
|
-
RunRequestFieldRefusedError,
|
|
3750
3601
|
SUITE_LIFECYCLE_LANE,
|
|
3751
3602
|
SYSTEM_CLOCK,
|
|
3752
3603
|
SuiteBoundaryWhileLiveError,
|
|
@@ -3755,7 +3606,6 @@ function createMemorySessionLog(options) {
|
|
|
3755
3606
|
WEAK_MODEL_CAP_SCALE,
|
|
3756
3607
|
applyKnowledgeChange,
|
|
3757
3608
|
buildMemberRunConfig,
|
|
3758
|
-
buildProductionRefusal,
|
|
3759
3609
|
buildRunPorts,
|
|
3760
3610
|
buildScreenshotCompressor,
|
|
3761
3611
|
buildSuiteMemberExecutor,
|
|
@@ -3775,15 +3625,17 @@ function createMemorySessionLog(options) {
|
|
|
3775
3625
|
createRunsReader,
|
|
3776
3626
|
createSuiteLifecycle,
|
|
3777
3627
|
latestBuildOrdinal,
|
|
3628
|
+
latestMarkReceiptFor,
|
|
3629
|
+
latestMarkReceiptsPerAddress,
|
|
3778
3630
|
listSessions,
|
|
3631
|
+
loadCompletedRunHandoff,
|
|
3779
3632
|
lookupEvidenceBlob,
|
|
3780
3633
|
mapVerdictToExitCode,
|
|
3781
3634
|
memoryToolGate,
|
|
3782
3635
|
mintChatSessionId,
|
|
3783
3636
|
openRunKnowledgeComposition,
|
|
3637
|
+
purgeActionCache,
|
|
3784
3638
|
readEvidenceBlobBytes,
|
|
3785
|
-
refuseProductionWithoutOptIn,
|
|
3786
|
-
refuseUnavailableRunId,
|
|
3787
3639
|
registerAdapterLiveStream,
|
|
3788
3640
|
reserveBuildOrdinalForProducts,
|
|
3789
3641
|
resolveBuildTag,
|