openplanr 1.18.0 → 1.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/backlog.d.ts.map +1 -1
- package/dist/cli/commands/backlog.js +3 -0
- package/dist/cli/commands/backlog.js.map +1 -1
- package/dist/cli/commands/config.d.ts.map +1 -1
- package/dist/cli/commands/config.js +3 -0
- package/dist/cli/commands/config.js.map +1 -1
- package/dist/cli/commands/epic.d.ts.map +1 -1
- package/dist/cli/commands/epic.js +2 -0
- package/dist/cli/commands/epic.js.map +1 -1
- package/dist/cli/commands/feature.d.ts.map +1 -1
- package/dist/cli/commands/feature.js +2 -0
- package/dist/cli/commands/feature.js.map +1 -1
- package/dist/cli/commands/operate.d.ts +5 -2
- package/dist/cli/commands/operate.d.ts.map +1 -1
- package/dist/cli/commands/operate.js +10 -101
- package/dist/cli/commands/operate.js.map +1 -1
- package/dist/cli/commands/plan.d.ts.map +1 -1
- package/dist/cli/commands/plan.js +2 -0
- package/dist/cli/commands/plan.js.map +1 -1
- package/dist/cli/commands/quick.d.ts.map +1 -1
- package/dist/cli/commands/quick.js +2 -0
- package/dist/cli/commands/quick.js.map +1 -1
- package/dist/cli/commands/refine.d.ts.map +1 -1
- package/dist/cli/commands/refine.js +2 -0
- package/dist/cli/commands/refine.js.map +1 -1
- package/dist/cli/commands/revise.d.ts.map +1 -1
- package/dist/cli/commands/revise.js +2 -0
- package/dist/cli/commands/revise.js.map +1 -1
- package/dist/cli/commands/spec.d.ts.map +1 -1
- package/dist/cli/commands/spec.js +2 -0
- package/dist/cli/commands/spec.js.map +1 -1
- package/dist/cli/commands/sprint.d.ts.map +1 -1
- package/dist/cli/commands/sprint.js +2 -0
- package/dist/cli/commands/sprint.js.map +1 -1
- package/dist/cli/commands/story.d.ts.map +1 -1
- package/dist/cli/commands/story.js +3 -0
- package/dist/cli/commands/story.js.map +1 -1
- package/dist/cli/commands/task.d.ts.map +1 -1
- package/dist/cli/commands/task.js +3 -0
- package/dist/cli/commands/task.js.map +1 -1
- package/dist/services/ai-service.d.ts +3 -5
- package/dist/services/ai-service.d.ts.map +1 -1
- package/dist/services/ai-service.js +7 -1
- package/dist/services/ai-service.js.map +1 -1
- package/dist/services/credentials-service.d.ts.map +1 -1
- package/dist/services/credentials-service.js +2 -0
- package/dist/services/credentials-service.js.map +1 -1
- package/dist/services/deprecation-notices.d.ts +5 -0
- package/dist/services/deprecation-notices.d.ts.map +1 -0
- package/dist/services/deprecation-notices.js +6 -0
- package/dist/services/deprecation-notices.js.map +1 -0
- package/dist/services/operate/advisors.d.ts +146 -108
- package/dist/services/operate/advisors.d.ts.map +1 -1
- package/dist/services/operate/advisors.js +178 -448
- package/dist/services/operate/advisors.js.map +1 -1
- package/dist/services/operate/citation-resolution.d.ts.map +1 -1
- package/dist/services/operate/citation-resolution.js +50 -1
- package/dist/services/operate/citation-resolution.js.map +1 -1
- package/dist/services/operate/config.d.ts +28 -52
- package/dist/services/operate/config.d.ts.map +1 -1
- package/dist/services/operate/config.js +68 -203
- package/dist/services/operate/config.js.map +1 -1
- package/dist/services/operate/doctor.d.ts +15 -0
- package/dist/services/operate/doctor.d.ts.map +1 -1
- package/dist/services/operate/doctor.js +142 -32
- package/dist/services/operate/doctor.js.map +1 -1
- package/dist/services/operate/engine.d.ts +22 -62
- package/dist/services/operate/engine.d.ts.map +1 -1
- package/dist/services/operate/engine.js +269 -191
- package/dist/services/operate/engine.js.map +1 -1
- package/dist/services/operate/evidence.d.ts +9 -78
- package/dist/services/operate/evidence.d.ts.map +1 -1
- package/dist/services/operate/evidence.js +0 -1194
- package/dist/services/operate/evidence.js.map +1 -1
- package/dist/services/operate/index.d.ts.map +1 -1
- package/dist/services/operate/index.js +15 -231
- package/dist/services/operate/index.js.map +1 -1
- package/dist/services/operate/integrity.d.ts +81 -0
- package/dist/services/operate/integrity.d.ts.map +1 -0
- package/dist/services/operate/integrity.js +152 -0
- package/dist/services/operate/integrity.js.map +1 -0
- package/dist/services/operate/interaction/answer-service.d.ts +0 -10
- package/dist/services/operate/interaction/answer-service.d.ts.map +1 -1
- package/dist/services/operate/interaction/answer-service.js +63 -43
- package/dist/services/operate/interaction/answer-service.js.map +1 -1
- package/dist/services/operate/interaction/question-engine.d.ts.map +1 -1
- package/dist/services/operate/interaction/question-engine.js +105 -21
- package/dist/services/operate/interaction/question-engine.js.map +1 -1
- package/dist/services/operate/interaction/question-registry.d.ts +16 -1
- package/dist/services/operate/interaction/question-registry.d.ts.map +1 -1
- package/dist/services/operate/interaction/question-registry.js +45 -39
- package/dist/services/operate/interaction/question-registry.js.map +1 -1
- package/dist/services/operate/interaction/session-service.d.ts.map +1 -1
- package/dist/services/operate/interaction/session-service.js +16 -11
- package/dist/services/operate/interaction/session-service.js.map +1 -1
- package/dist/services/operate/interaction/terminal-renderer.d.ts.map +1 -1
- package/dist/services/operate/interaction/terminal-renderer.js +16 -6
- package/dist/services/operate/interaction/terminal-renderer.js.map +1 -1
- package/dist/services/operate/lifecycle.d.ts.map +1 -1
- package/dist/services/operate/lifecycle.js +17 -1
- package/dist/services/operate/lifecycle.js.map +1 -1
- package/dist/services/operate/maintenance.d.ts.map +1 -1
- package/dist/services/operate/maintenance.js +95 -276
- package/dist/services/operate/maintenance.js.map +1 -1
- package/dist/services/operate/mission-dispatch.d.ts +73 -55
- package/dist/services/operate/mission-dispatch.d.ts.map +1 -1
- package/dist/services/operate/mission-dispatch.js +91 -67
- package/dist/services/operate/mission-dispatch.js.map +1 -1
- package/dist/services/operate/projection-persistence.d.ts +35 -7
- package/dist/services/operate/projection-persistence.d.ts.map +1 -1
- package/dist/services/operate/projection-persistence.js +142 -53
- package/dist/services/operate/projection-persistence.js.map +1 -1
- package/dist/services/operate/projection.d.ts +9 -1
- package/dist/services/operate/projection.d.ts.map +1 -1
- package/dist/services/operate/projection.js +22 -12
- package/dist/services/operate/projection.js.map +1 -1
- package/dist/services/operate/protocol.d.ts +1 -41
- package/dist/services/operate/protocol.d.ts.map +1 -1
- package/dist/services/operate/protocol.js +7 -45
- package/dist/services/operate/protocol.js.map +1 -1
- package/dist/services/operate/redaction.d.ts +3 -3
- package/dist/services/operate/redaction.js +4 -4
- package/dist/services/operate/redaction.js.map +1 -1
- package/dist/services/operate/reports.d.ts +20 -1
- package/dist/services/operate/reports.d.ts.map +1 -1
- package/dist/services/operate/reports.js +108 -4
- package/dist/services/operate/reports.js.map +1 -1
- package/dist/services/operate/routes.d.ts +84 -0
- package/dist/services/operate/routes.d.ts.map +1 -1
- package/dist/services/operate/routes.js +519 -10
- package/dist/services/operate/routes.js.map +1 -1
- package/dist/services/operate/types.d.ts +48 -133
- package/dist/services/operate/types.d.ts.map +1 -1
- package/dist/services/operate/types.js.map +1 -1
- package/dist/services/operate/workspace.d.ts +0 -1
- package/dist/services/operate/workspace.d.ts.map +1 -1
- package/dist/services/operate/workspace.js +0 -2
- package/dist/services/operate/workspace.js.map +1 -1
- package/dist/services/runtime-manager-service.js +2 -2
- package/dist/services/runtime-manager-service.js.map +1 -1
- package/package.json +3 -3
- package/dist/services/operate/evidence-classifications.d.ts +0 -59
- package/dist/services/operate/evidence-classifications.d.ts.map +0 -1
- package/dist/services/operate/evidence-classifications.js +0 -132
- package/dist/services/operate/evidence-classifications.js.map +0 -1
- package/dist/services/operate/evidence-diagnostics.d.ts +0 -23
- package/dist/services/operate/evidence-diagnostics.d.ts.map +0 -1
- package/dist/services/operate/evidence-diagnostics.js +0 -152
- package/dist/services/operate/evidence-diagnostics.js.map +0 -1
- package/dist/services/operate/evidence-readiness.d.ts +0 -8
- package/dist/services/operate/evidence-readiness.d.ts.map +0 -1
- package/dist/services/operate/evidence-readiness.js +0 -84
- package/dist/services/operate/evidence-readiness.js.map +0 -1
|
@@ -1,34 +1,21 @@
|
|
|
1
1
|
import { createHmac, randomBytes, randomUUID, timingSafeEqual } from 'node:crypto';
|
|
2
2
|
import { mkdir, readdir, readFile, rename, rm, unlink, writeFile } from 'node:fs/promises';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
import { advisorResponseContractDetails, assertAdvisorOutputMatchesBrief,
|
|
4
|
+
import { advisorResponseContractDetails, assertAdvisorOutputMatchesBrief, buildOperatingMandate, createNativeMissionOperatingRoleResult, } from './advisors.js';
|
|
5
5
|
import { canonicalDigest, canonicalize, sha256Digest } from './canonical.js';
|
|
6
|
-
import { operatingProjectKey, readOperatingAdapterLeaseDurationMs
|
|
7
|
-
import {
|
|
6
|
+
import { operatingProjectKey, readOperatingAdapterLeaseDurationMs } from './config.js';
|
|
7
|
+
import { gateRecordedProposalCitations } from './engine.js';
|
|
8
8
|
import { OperatingEventStore } from './event-store.js';
|
|
9
9
|
import { OperatingEvidenceCache } from './evidence-cache.js';
|
|
10
|
-
import { purgeStaleEvidenceClassifications } from './evidence-classifications.js';
|
|
11
|
-
import { listEvidenceDiagnostics } from './evidence-diagnostics.js';
|
|
12
|
-
import { evaluateEvidenceReadiness } from './evidence-readiness.js';
|
|
13
10
|
import { guidedSessionStatus, purgeGuidedSessions } from './interaction/session-service.js';
|
|
14
11
|
import { assertCommittedOperatingView, recoverOperatingTransactions } from './journal.js';
|
|
15
12
|
import { withOperatingLock } from './lock-service.js';
|
|
16
|
-
import { narrowMissionRootsToCeiling, operatingRegistryDispatchMode, operatingRuntimeEnforcesBoundedReadOnly, resolveOperatingDispatchMode, } from './mission-dispatch.js';
|
|
17
13
|
import { assertOperatingArtifact, loadOperatingProtocol } from './protocol.js';
|
|
18
14
|
import { containsSecret, redactSensitiveText } from './redaction.js';
|
|
19
15
|
import { OPERATE_PROTOCOL_VERSION, OPERATE_SCHEMA_VERSION, OperateError, } from './types.js';
|
|
20
16
|
import { readOperatingConfig, refreshOperatingWorkspaceManifest, resolveContainedPath, resolveOperatingPaths, } from './workspace.js';
|
|
21
|
-
/**
|
|
22
|
-
* Whether any bound role of this session resolved to native mission dispatch, so
|
|
23
|
-
* both adapter-handoff call sites can pass `protocolVersion: '1.3.0'` and the
|
|
24
|
-
* pipeline emits the v1.3 mission record action (`dispatch.agent` +
|
|
25
|
-
* `dispatch.missionPacketPointer`) instead of the v1.2 `rolePackPointer`.
|
|
26
|
-
*/
|
|
27
|
-
function sessionHasMissionRole(session) {
|
|
28
|
-
return Boolean(session.roleMissionPackets && Object.keys(session.roleMissionPackets).length > 0);
|
|
29
|
-
}
|
|
30
17
|
function adapterSessionSummary(session) {
|
|
31
|
-
const { roleBriefs: _roleBriefs,
|
|
18
|
+
const { roleBriefs: _roleBriefs, roleMandates: _roleMandates, ...summary } = session;
|
|
32
19
|
return summary;
|
|
33
20
|
}
|
|
34
21
|
async function adapterHandoff(session) {
|
|
@@ -42,10 +29,7 @@ async function adapterHandoff(session) {
|
|
|
42
29
|
: 'finalize-required';
|
|
43
30
|
const protocol = await loadOperatingProtocol();
|
|
44
31
|
const handoff = protocol.createOperatingAdapterHandoff({
|
|
45
|
-
|
|
46
|
-
// generated lens agent and points at its mission packet; v1.2 (pack) default
|
|
47
|
-
// otherwise, byte-compatible with the pack-mode handoff.
|
|
48
|
-
...(sessionHasMissionRole(session) ? { protocolVersion: '1.3.0' } : {}),
|
|
32
|
+
protocolVersion: '1.3.0',
|
|
49
33
|
phase: session.phase,
|
|
50
34
|
state,
|
|
51
35
|
cycleId: session.cycleId,
|
|
@@ -186,25 +170,12 @@ export async function operatingCacheAction(input) {
|
|
|
186
170
|
.catch(() => ({ sensitivityCeiling: 'internal' }));
|
|
187
171
|
const cache = new OperatingEvidenceCache(paths.evidence, preferences.sensitivityCeiling ?? 'internal');
|
|
188
172
|
if (input.action === 'status') {
|
|
189
|
-
const diagnostics = await listEvidenceDiagnostics({
|
|
190
|
-
projectRoot: input.projectRoot,
|
|
191
|
-
localRoot: input.localRoot,
|
|
192
|
-
});
|
|
193
|
-
const classifications = await purgeStaleEvidenceClassifications({
|
|
194
|
-
projectRoot: input.projectRoot,
|
|
195
|
-
localRoot: input.localRoot,
|
|
196
|
-
});
|
|
197
173
|
return {
|
|
198
174
|
evidence: await cache.status(),
|
|
199
175
|
sessions: await guidedSessionStatus({
|
|
200
176
|
projectRoot: input.projectRoot,
|
|
201
177
|
localRoot: input.localRoot,
|
|
202
178
|
}),
|
|
203
|
-
diagnostics: {
|
|
204
|
-
candidates: diagnostics.length,
|
|
205
|
-
classified: diagnostics.filter((entry) => entry.classification).length,
|
|
206
|
-
staleClassifications: classifications.stale,
|
|
207
|
-
},
|
|
208
179
|
};
|
|
209
180
|
}
|
|
210
181
|
if (!input.confirmed) {
|
|
@@ -215,11 +186,6 @@ export async function operatingCacheAction(input) {
|
|
|
215
186
|
projectRoot: input.projectRoot,
|
|
216
187
|
localRoot: input.localRoot,
|
|
217
188
|
});
|
|
218
|
-
const classifications = await purgeStaleEvidenceClassifications({
|
|
219
|
-
projectRoot: input.projectRoot,
|
|
220
|
-
localRoot: input.localRoot,
|
|
221
|
-
purge: true,
|
|
222
|
-
});
|
|
223
189
|
// FR4/FR11: clearing machine-local caches also drops board-bound adapter
|
|
224
190
|
// sessions and incremental evidence baselines, so this is the scoped fix the
|
|
225
191
|
// doctor names for stale adapter sessions and stale incremental baselines.
|
|
@@ -230,12 +196,10 @@ export async function operatingCacheAction(input) {
|
|
|
230
196
|
return {
|
|
231
197
|
removed: removed.length +
|
|
232
198
|
sessions.removed +
|
|
233
|
-
classifications.purged +
|
|
234
199
|
board.removedAdvisorSessions +
|
|
235
200
|
board.removedIncrementalBaselines,
|
|
236
201
|
evidence: { removed: removed.length, entries: removed },
|
|
237
202
|
sessions,
|
|
238
|
-
classifications,
|
|
239
203
|
adapterSessions: { removed: board.removedAdvisorSessions },
|
|
240
204
|
incrementalBaselines: { removed: board.removedIncrementalBaselines },
|
|
241
205
|
};
|
|
@@ -700,68 +664,31 @@ async function readAdapterSensitivityCeiling(projectRoot, localRoot) {
|
|
|
700
664
|
'internal')
|
|
701
665
|
.catch(() => 'internal');
|
|
702
666
|
}
|
|
667
|
+
// The mandate `boundaries.roots` items must satisfy this pattern (leading dot
|
|
668
|
+
// permitted, no `..` traversal), so `.planr` is a valid declared root.
|
|
669
|
+
const MANDATE_ROOT_PATTERN = /^(?!.*\.\.)[A-Za-z0-9._][A-Za-z0-9._/-]*$/;
|
|
703
670
|
/**
|
|
704
|
-
* FR2:
|
|
705
|
-
*
|
|
706
|
-
*
|
|
707
|
-
*
|
|
708
|
-
*
|
|
709
|
-
*
|
|
710
|
-
*
|
|
711
|
-
|
|
712
|
-
function narrowEvidenceToMissionCeiling(evidence, ceiling) {
|
|
713
|
-
const topSegment = (location) => {
|
|
714
|
-
const [top] = location.split('/');
|
|
715
|
-
return top && top.length > 0 ? top : null;
|
|
716
|
-
};
|
|
717
|
-
const declaredRoots = [
|
|
718
|
-
...new Set(evidence.items
|
|
719
|
-
.map((item) => topSegment(item.location))
|
|
720
|
-
.filter((segment) => Boolean(segment))),
|
|
721
|
-
];
|
|
722
|
-
const evidenceIndex = evidence.items.map((item) => ({
|
|
723
|
-
path: item.location,
|
|
724
|
-
sensitivity: item.sensitivity,
|
|
725
|
-
}));
|
|
726
|
-
const allowedRoots = new Set(narrowMissionRootsToCeiling({ declaredRoots, evidenceIndex, ceiling }));
|
|
727
|
-
return {
|
|
728
|
-
...evidence,
|
|
729
|
-
items: evidence.items.filter((item) => {
|
|
730
|
-
const top = topSegment(item.location);
|
|
731
|
-
return top === null || allowedRoots.has(top);
|
|
732
|
-
}),
|
|
733
|
-
};
|
|
734
|
-
}
|
|
735
|
-
/**
|
|
736
|
-
* Resolve every requested role's effective dispatch mode for THIS runtime (FR1):
|
|
737
|
-
* the v1.3 registry default, overridden by the machine-local
|
|
738
|
-
* `dispatchModeOverrides`, reconciled against whether the runtime natively
|
|
739
|
-
* enforces the bounded read-only boundary. The adapter lifecycle IS the native
|
|
740
|
-
* runtime path, so the hosting adapter is native-capable by construction; the
|
|
741
|
-
* runtime's own enforceability then decides whether a role receives a native
|
|
742
|
-
* bounded mission lens (`resolution.native`) or falls closed to the v1.2 pack.
|
|
671
|
+
* FR1/FR2: derive a mandate's declared read roots directly from the project
|
|
672
|
+
* working tree — every top-level directory the agent may traverse — rather than
|
|
673
|
+
* from a collected evidence index. `.planr/` is ALWAYS declared, whether or not
|
|
674
|
+
* it exists yet and REGARDLESS of git tracking, because the mission tool surface
|
|
675
|
+
* (`mission-dispatch.ts`) walks the filesystem directly rather than `git
|
|
676
|
+
* ls-files`, so a gitignored `.planr/` tree is fully readable (finding 2's
|
|
677
|
+
* tracked-file gap cannot reproduce here). `.git`/`node_modules` are never
|
|
678
|
+
* declared, and every root is filtered to the mandate's schema pattern.
|
|
743
679
|
*/
|
|
744
|
-
async function
|
|
745
|
-
const
|
|
746
|
-
const
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
const resolved = new Map();
|
|
755
|
-
for (const roleId of input.roles) {
|
|
756
|
-
resolved.set(roleId, resolveOperatingDispatchMode({
|
|
757
|
-
roleId: roleId,
|
|
758
|
-
registryDefault: registryDefaults.get(roleId) ?? 'mission',
|
|
759
|
-
override: overrides[roleId],
|
|
760
|
-
runtimeEnforcesBoundedReadOnly,
|
|
761
|
-
adapterNativeCapable: true,
|
|
762
|
-
}));
|
|
680
|
+
async function deriveOperatingMandateRoots(projectRoot) {
|
|
681
|
+
const entries = await readdir(projectRoot, { withFileTypes: true }).catch(() => []);
|
|
682
|
+
const roots = new Set(['.planr']);
|
|
683
|
+
for (const entry of entries) {
|
|
684
|
+
if (!entry.isDirectory())
|
|
685
|
+
continue;
|
|
686
|
+
if (entry.name === '.git' || entry.name === 'node_modules')
|
|
687
|
+
continue;
|
|
688
|
+
if (MANDATE_ROOT_PATTERN.test(entry.name))
|
|
689
|
+
roots.add(entry.name);
|
|
763
690
|
}
|
|
764
|
-
return
|
|
691
|
+
return [...roots].sort();
|
|
765
692
|
}
|
|
766
693
|
export async function createOperatingAdapterStartHandoff(input) {
|
|
767
694
|
const target = adapterSessionPath(input.projectRoot, input.cycleId, input.localRoot);
|
|
@@ -788,19 +715,8 @@ export async function createOperatingAdapterStartHandoff(input) {
|
|
|
788
715
|
}
|
|
789
716
|
}
|
|
790
717
|
const protocol = await loadOperatingProtocol();
|
|
791
|
-
// FR1 call site 2 (the start / prepare-required handoff): stamp v1.3 whenever
|
|
792
|
-
// any bound role would resolve to native mission dispatch on this runtime, so
|
|
793
|
-
// the whole lifecycle — including the record actions built after prepare —
|
|
794
|
-
// carries the mission protocol version rather than defaulting to pack.
|
|
795
|
-
const dispatchModes = await resolveBoundRoleDispatchModes({
|
|
796
|
-
projectRoot: input.projectRoot,
|
|
797
|
-
localRoot: input.localRoot,
|
|
798
|
-
runtime: input.runtime,
|
|
799
|
-
roles: input.roles,
|
|
800
|
-
});
|
|
801
|
-
const anyMission = [...dispatchModes.values()].some((resolution) => resolution.native);
|
|
802
718
|
const handoff = protocol.createOperatingAdapterHandoff({
|
|
803
|
-
|
|
719
|
+
protocolVersion: '1.3.0',
|
|
804
720
|
phase: input.phase,
|
|
805
721
|
state: 'prepare-required',
|
|
806
722
|
cycleId: input.cycleId,
|
|
@@ -929,123 +845,40 @@ export async function operateAdapterLifecycle(input) {
|
|
|
929
845
|
// FR4: a finalized/expired/cancelled same-board session no longer forces
|
|
930
846
|
// a whole-cycle-cancelling error on a new compatible prepare. The
|
|
931
847
|
// advisors→chair continuation and a same-phase re-prepare both fall
|
|
932
|
-
// through and supersede the dead session; an exact dead session
|
|
933
|
-
//
|
|
934
|
-
// rather than recomputed.
|
|
848
|
+
// through and supersede the dead session; an exact dead session reuses
|
|
849
|
+
// its already-built mandates so recorded advisory work is not recomputed.
|
|
935
850
|
if (exact && (sessionExpired(normalized) || normalized.state === 'cancelled')) {
|
|
936
851
|
recoverableSession = normalized;
|
|
937
852
|
}
|
|
938
853
|
}
|
|
939
854
|
}
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
//
|
|
964
|
-
|
|
965
|
-
// body-free v1.3 mission packet; every other role keeps the v1.2 role pack.
|
|
966
|
-
// A recovered dead-but-exact session reuses its stored packs/packets verbatim.
|
|
967
|
-
const dispatchModes = await resolveBoundRoleDispatchModes({
|
|
968
|
-
projectRoot: input.projectRoot,
|
|
969
|
-
localRoot: input.localRoot,
|
|
970
|
-
runtime,
|
|
971
|
-
roles,
|
|
972
|
-
});
|
|
973
|
-
const missionRoleIds = readiness.roles
|
|
974
|
-
.map((role) => role.roleId)
|
|
975
|
-
.filter((roleId) => dispatchModes.get(roleId)?.native);
|
|
976
|
-
const packReadinessRoles = readiness.roles.filter((role) => !dispatchModes.get(role.roleId)?.native);
|
|
977
|
-
const rolePacks = recoverableSession?.rolePacks ??
|
|
978
|
-
Object.fromEntries(await Promise.all(packReadinessRoles.map(async (role) => [
|
|
979
|
-
role.roleId,
|
|
980
|
-
await createOperatingAdvisorPack({
|
|
981
|
-
cycleId: input.cycleId,
|
|
982
|
-
role,
|
|
983
|
-
evidence: roleEvidence,
|
|
984
|
-
context,
|
|
985
|
-
}),
|
|
986
|
-
])));
|
|
987
|
-
// FR1: build the natively-dispatched roles' mission packets from live
|
|
988
|
-
// cycle/workspace state. Only reached when a role actually resolved mission,
|
|
989
|
-
// so a pure pack-mode prepare never loads config/workspace and stays
|
|
990
|
-
// byte-compatible with the v1.2 path. The evidence is ceiling-narrowed by
|
|
991
|
-
// `narrowMissionRootsToCeiling` first, so a declared read root can never reach
|
|
992
|
-
// an above-ceiling file even before the bounded reader's read-time check.
|
|
993
|
-
const roleMissionPackets = recoverableSession?.roleMissionPackets ??
|
|
994
|
-
(missionRoleIds.length > 0
|
|
995
|
-
? await (async () => {
|
|
996
|
-
const [config, workspace, sensitivityCeiling] = await Promise.all([
|
|
997
|
-
readOperatingConfig(input.projectRoot, { localRoot: input.localRoot }),
|
|
998
|
-
refreshOperatingWorkspaceManifest(input.projectRoot, { localRoot: input.localRoot }),
|
|
999
|
-
readAdapterSensitivityCeiling(input.projectRoot, input.localRoot),
|
|
1000
|
-
]);
|
|
1001
|
-
const packets = await buildOperatingMissionPackets({
|
|
1002
|
-
cycleId: input.cycleId,
|
|
1003
|
-
config,
|
|
1004
|
-
workspace,
|
|
1005
|
-
context,
|
|
1006
|
-
evidence: narrowEvidenceToMissionCeiling(roleEvidence, sensitivityCeiling),
|
|
1007
|
-
roleIds: missionRoleIds,
|
|
1008
|
-
sensitivityCeiling,
|
|
1009
|
-
});
|
|
1010
|
-
return Object.fromEntries(packets.map((packet) => [packet.roleId, packet]));
|
|
1011
|
-
})()
|
|
1012
|
-
: {});
|
|
1013
|
-
// FR2: fail closed before a native adapter session is assembled. A freshly
|
|
1014
|
-
// built pack is already measured inside `createOperatingAdvisorPack`, but the
|
|
1015
|
-
// session may instead be RECOVERED from disk (`recoverableSession`) — possibly
|
|
1016
|
-
// written before pack-budget enforcement existed. Re-measure every pack that
|
|
1017
|
-
// will back this session against its role's published v1.2 `maxInputBytes`, so
|
|
1018
|
-
// an oversized pack can never reach the adapter from either source instead of
|
|
1019
|
-
// silently constructing an oversized session. (Mission packets are measured
|
|
1020
|
-
// against their derived mission budget inside `buildOperatingMissionPackets`.)
|
|
1021
|
-
const packInputBudgets = new Map(protocol.listOperatingRoles().map((role) => {
|
|
1022
|
-
const maxInputBytes = role.budgets
|
|
1023
|
-
?.maxInputBytes;
|
|
1024
|
-
return [role.id, typeof maxInputBytes === 'number' ? maxInputBytes : 262_144];
|
|
1025
|
-
}));
|
|
1026
|
-
for (const [roleId, pack] of Object.entries(rolePacks)) {
|
|
1027
|
-
assertOperatingAdvisorPackWithinBudget(pack, packInputBudgets.get(roleId) ?? 262_144);
|
|
1028
|
-
}
|
|
1029
|
-
// A mission role's brief facet is the same registry-derived brief pack mode
|
|
1030
|
-
// uses, so the record path's brief-bound machinery (output limits, contract
|
|
1031
|
-
// details, output-vs-brief checks) applies uniformly across both modes.
|
|
1032
|
-
const roleBriefs = {
|
|
1033
|
-
...Object.fromEntries(Object.entries(rolePacks).map(([role, pack]) => [role, pack.roleBrief])),
|
|
1034
|
-
...Object.fromEntries(Object.keys(roleMissionPackets).map((role) => [
|
|
1035
|
-
role,
|
|
1036
|
-
protocol.createOperatingAdvisorBrief(role),
|
|
1037
|
-
])),
|
|
1038
|
-
};
|
|
1039
|
-
// A role's committed input digest is its pack's `inputDigest` (pack mode) or
|
|
1040
|
-
// its mission packet's `packetDigest` (mission mode). The record path binds a
|
|
1041
|
-
// recorded result to exactly this value, and a recovered machine-local result
|
|
1042
|
-
// is re-adopted only when it still matches it.
|
|
1043
|
-
const roleInputDigests = Object.fromEntries(roles.map((role) => {
|
|
1044
|
-
const digest = roleMissionPackets[role]?.packetDigest ?? rolePacks[role]?.inputDigest;
|
|
1045
|
-
return [role, digest];
|
|
1046
|
-
}));
|
|
855
|
+
// FR1/FR2: every requested role — the Chair included — dispatches an operating
|
|
856
|
+
// MANDATE. The role-dependent pack/mission split is retired, so there is no
|
|
857
|
+
// pre-dispatch readiness gate against collected evidence and no v1.2 role pack
|
|
858
|
+
// is built. A mandate carries the lens question, the investigation mandate, the
|
|
859
|
+
// declared read boundaries (the granted workspace roots — including a
|
|
860
|
+
// gitignored `.planr/` tree — the registry sensitivity ceiling, and any
|
|
861
|
+
// forbidden paths), the response schema, and the citation requirement. It
|
|
862
|
+
// carries NO evidence body and NO evidence index: nothing is collected,
|
|
863
|
+
// budgeted, packed, or pre-filtered. A response that grounds nothing is
|
|
864
|
+
// post-gated at record time by the universal citation gate
|
|
865
|
+
// (`gateRecordedProposalCitations`). A recovered dead-but-exact session reuses
|
|
866
|
+
// its stored mandates verbatim.
|
|
867
|
+
const roleMandates = recoverableSession?.roleMandates && Object.keys(recoverableSession.roleMandates).length > 0
|
|
868
|
+
? recoverableSession.roleMandates
|
|
869
|
+
: await (async () => {
|
|
870
|
+
const roots = await deriveOperatingMandateRoots(input.projectRoot);
|
|
871
|
+
const built = await Promise.all(roles.map(async (roleId) => [roleId, await buildOperatingMandate({ roleId, roots })]));
|
|
872
|
+
return Object.fromEntries(built);
|
|
873
|
+
})();
|
|
874
|
+
// Every requested role dispatches; the bound role set is exactly `roles`.
|
|
875
|
+
const dispatchedRoles = roles;
|
|
876
|
+
const roleBriefs = Object.fromEntries(Object.keys(roleMandates).map((role) => [role, protocol.createOperatingAdvisorBrief(role)]));
|
|
877
|
+
// A role's committed input digest is its mandate digest. The record path
|
|
878
|
+
// binds a recorded result to exactly this value.
|
|
879
|
+
const roleInputDigests = Object.fromEntries(dispatchedRoles.map((role) => [role, roleMandates[role].mandateDigest]));
|
|
1047
880
|
const validRecordedRoles = [];
|
|
1048
|
-
for (const role of
|
|
881
|
+
for (const role of dispatchedRoles) {
|
|
1049
882
|
const prior = await readFile(path.join(path.dirname(target), `${input.cycleId}.${role}.json`), 'utf8')
|
|
1050
883
|
.then((raw) => JSON.parse(raw))
|
|
1051
884
|
.catch(() => null);
|
|
@@ -1075,22 +908,16 @@ export async function operateAdapterLifecycle(input) {
|
|
|
1075
908
|
idempotencyKey: input.idempotencyKey,
|
|
1076
909
|
state: validRecordedRoles.length > 0 ? 'recording' : 'prepared',
|
|
1077
910
|
expiresAt: new Date(nowMs + leaseDurationMs).toISOString(),
|
|
1078
|
-
roles,
|
|
911
|
+
roles: dispatchedRoles,
|
|
1079
912
|
recordedRoles: validRecordedRoles.sort(),
|
|
1080
913
|
roleBriefs,
|
|
1081
|
-
|
|
1082
|
-
// Omitted entirely for a pure pack-mode session so its on-disk shape stays
|
|
1083
|
-
// byte-compatible with the v1.2 path.
|
|
1084
|
-
...(Object.keys(roleMissionPackets).length > 0 ? { roleMissionPackets } : {}),
|
|
914
|
+
roleMandates,
|
|
1085
915
|
roleInputDigests,
|
|
1086
916
|
};
|
|
1087
917
|
await atomicPrivateWrite(target, session);
|
|
1088
918
|
return {
|
|
1089
919
|
...session,
|
|
1090
|
-
|
|
1091
|
-
// record action's `dispatch.missionPacketPointer` resolves against the
|
|
1092
|
-
// prepare result; pack roles keep resolving at `/data/rolePacks/<role>`.
|
|
1093
|
-
...(Object.keys(roleMissionPackets).length > 0 ? { missionPackets: roleMissionPackets } : {}),
|
|
920
|
+
mandates: roleMandates,
|
|
1094
921
|
leaseStatus: adapterLeaseStatus(session, nowMs),
|
|
1095
922
|
handoff: await adapterHandoff(session),
|
|
1096
923
|
};
|
|
@@ -1173,9 +1000,12 @@ export async function operateAdapterLifecycle(input) {
|
|
|
1173
1000
|
}
|
|
1174
1001
|
const submittedRecord = submitted && typeof submitted === 'object' ? submitted : {};
|
|
1175
1002
|
let result;
|
|
1176
|
-
// FR1: unresolvable-citation gaps opened while
|
|
1177
|
-
// citations, surfaced in the record response
|
|
1003
|
+
// FR1/FR2: unresolvable-citation and empty-grounding gaps opened while
|
|
1004
|
+
// resolving a mandate response's citations, surfaced in the record response
|
|
1005
|
+
// for observability. `recordNotEvaluated` marks a role whose citations
|
|
1006
|
+
// grounded zero evidence (committed as a quiet result plus the governed gap).
|
|
1178
1007
|
let recordGaps = [];
|
|
1008
|
+
let recordNotEvaluated = false;
|
|
1179
1009
|
if (submittedRecord.kind === 'operating-role-result') {
|
|
1180
1010
|
throw new OperateError('E_OPERATE_ADVISOR_FAILED', 'Native advisors must submit only the compact response contract; OpenPlanr owns canonical result metadata.', {
|
|
1181
1011
|
...advisorResponseContractDetails(session.roleBriefs[input.role]),
|
|
@@ -1204,50 +1034,38 @@ export async function operateAdapterLifecycle(input) {
|
|
|
1204
1034
|
throw new OperateError('E_OPERATE_SECRET_DETECTED', `Native advisor response contains a secret at ${field.location}; nothing was persisted.`, { roleId: input.role, field: field.location });
|
|
1205
1035
|
}
|
|
1206
1036
|
}
|
|
1207
|
-
const
|
|
1037
|
+
const mandate = session.roleMandates[input.role];
|
|
1208
1038
|
try {
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1039
|
+
const gated = await createNativeMissionOperatingRoleResult({
|
|
1040
|
+
mandate,
|
|
1041
|
+
cycleId: input.cycleId,
|
|
1042
|
+
response: submitted,
|
|
1043
|
+
runtime: session.runtime,
|
|
1044
|
+
resolveCitations: async (roleResults) => {
|
|
1045
|
+
const [workspace, sensitivityCeiling, config] = await Promise.all([
|
|
1046
|
+
refreshOperatingWorkspaceManifest(input.projectRoot, {
|
|
1047
|
+
localRoot: input.localRoot,
|
|
1048
|
+
}),
|
|
1049
|
+
readAdapterSensitivityCeiling(input.projectRoot, input.localRoot),
|
|
1050
|
+
readOperatingConfig(input.projectRoot, { localRoot: input.localRoot }).catch(() => null),
|
|
1051
|
+
]);
|
|
1052
|
+
return gateRecordedProposalCitations({
|
|
1053
|
+
roleResults,
|
|
1054
|
+
context: {
|
|
1055
|
+
projectRoot: input.projectRoot,
|
|
1056
|
+
cycleId: input.cycleId,
|
|
1057
|
+
descriptor: workspace.controlRepository,
|
|
1058
|
+
cache: new OperatingEvidenceCache(resolveOperatingPaths(input.projectRoot, {
|
|
1222
1059
|
localRoot: input.localRoot,
|
|
1223
|
-
}),
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
descriptor: workspace.controlRepository,
|
|
1233
|
-
cache: new OperatingEvidenceCache(resolveOperatingPaths(input.projectRoot, {
|
|
1234
|
-
localRoot: input.localRoot,
|
|
1235
|
-
}).evidence, sensitivityCeiling),
|
|
1236
|
-
owner: config?.decisionOwner,
|
|
1237
|
-
},
|
|
1238
|
-
});
|
|
1239
|
-
},
|
|
1240
|
-
});
|
|
1241
|
-
result = gated.result;
|
|
1242
|
-
recordGaps = gated.gaps;
|
|
1243
|
-
}
|
|
1244
|
-
else {
|
|
1245
|
-
result = await createNativeOperatingRoleResult({
|
|
1246
|
-
pack: session.rolePacks[input.role],
|
|
1247
|
-
response: submitted,
|
|
1248
|
-
runtime: session.runtime,
|
|
1249
|
-
});
|
|
1250
|
-
}
|
|
1060
|
+
}).evidence, sensitivityCeiling),
|
|
1061
|
+
owner: config?.decisionOwner,
|
|
1062
|
+
},
|
|
1063
|
+
});
|
|
1064
|
+
},
|
|
1065
|
+
});
|
|
1066
|
+
result = gated.result;
|
|
1067
|
+
recordGaps = gated.gaps;
|
|
1068
|
+
recordNotEvaluated = gated.notEvaluated;
|
|
1251
1069
|
}
|
|
1252
1070
|
catch (error) {
|
|
1253
1071
|
if (error instanceof OperateError && error.code === 'E_OPERATE_ADVISOR_FAILED') {
|
|
@@ -1324,6 +1142,7 @@ export async function operateAdapterLifecycle(input) {
|
|
|
1324
1142
|
recorded: input.role,
|
|
1325
1143
|
result,
|
|
1326
1144
|
...(recordGaps.length > 0 ? { citationGaps: recordGaps } : {}),
|
|
1145
|
+
...(recordNotEvaluated ? { notEvaluated: true } : {}),
|
|
1327
1146
|
session: adapterSessionSummary(updated),
|
|
1328
1147
|
leaseStatus: adapterLeaseStatus(updated, nowMs),
|
|
1329
1148
|
handoff: await adapterHandoff(updated),
|