openplanr 1.14.4 → 1.15.1
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/operate.d.ts.map +1 -1
- package/dist/cli/commands/operate.js +55 -87
- package/dist/cli/commands/operate.js.map +1 -1
- package/dist/services/operate/config.d.ts +4 -1
- package/dist/services/operate/config.d.ts.map +1 -1
- package/dist/services/operate/config.js +56 -2
- package/dist/services/operate/config.js.map +1 -1
- package/dist/services/operate/doctor.d.ts.map +1 -1
- package/dist/services/operate/doctor.js +15 -0
- package/dist/services/operate/doctor.js.map +1 -1
- package/dist/services/operate/evidence-classifications.d.ts +59 -0
- package/dist/services/operate/evidence-classifications.d.ts.map +1 -0
- package/dist/services/operate/evidence-classifications.js +132 -0
- package/dist/services/operate/evidence-classifications.js.map +1 -0
- package/dist/services/operate/evidence-diagnostics.d.ts +23 -0
- package/dist/services/operate/evidence-diagnostics.d.ts.map +1 -0
- package/dist/services/operate/evidence-diagnostics.js +152 -0
- package/dist/services/operate/evidence-diagnostics.js.map +1 -0
- package/dist/services/operate/evidence.d.ts.map +1 -1
- package/dist/services/operate/evidence.js +54 -7
- 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 +465 -62
- package/dist/services/operate/index.js.map +1 -1
- package/dist/services/operate/interaction/action-service.d.ts +17 -0
- package/dist/services/operate/interaction/action-service.d.ts.map +1 -0
- package/dist/services/operate/interaction/action-service.js +84 -0
- package/dist/services/operate/interaction/action-service.js.map +1 -0
- package/dist/services/operate/interaction/answer-service.d.ts +33 -0
- package/dist/services/operate/interaction/answer-service.d.ts.map +1 -0
- package/dist/services/operate/interaction/answer-service.js +239 -0
- package/dist/services/operate/interaction/answer-service.js.map +1 -0
- package/dist/services/operate/interaction/charter-suggestions.d.ts +29 -0
- package/dist/services/operate/interaction/charter-suggestions.d.ts.map +1 -0
- package/dist/services/operate/interaction/charter-suggestions.js +104 -0
- package/dist/services/operate/interaction/charter-suggestions.js.map +1 -0
- package/dist/services/operate/interaction/confirmation-service.d.ts +30 -0
- package/dist/services/operate/interaction/confirmation-service.d.ts.map +1 -0
- package/dist/services/operate/interaction/confirmation-service.js +78 -0
- package/dist/services/operate/interaction/confirmation-service.js.map +1 -0
- package/dist/services/operate/interaction/initialization-replay.d.ts +9 -0
- package/dist/services/operate/interaction/initialization-replay.d.ts.map +1 -0
- package/dist/services/operate/interaction/initialization-replay.js +64 -0
- package/dist/services/operate/interaction/initialization-replay.js.map +1 -0
- package/dist/services/operate/interaction/question-engine.d.ts +39 -0
- package/dist/services/operate/interaction/question-engine.d.ts.map +1 -0
- package/dist/services/operate/interaction/question-engine.js +277 -0
- package/dist/services/operate/interaction/question-engine.js.map +1 -0
- package/dist/services/operate/interaction/question-registry.d.ts +16 -0
- package/dist/services/operate/interaction/question-registry.d.ts.map +1 -0
- package/dist/services/operate/interaction/question-registry.js +190 -0
- package/dist/services/operate/interaction/question-registry.js.map +1 -0
- package/dist/services/operate/interaction/session-service.d.ts +55 -0
- package/dist/services/operate/interaction/session-service.d.ts.map +1 -0
- package/dist/services/operate/interaction/session-service.js +272 -0
- package/dist/services/operate/interaction/session-service.js.map +1 -0
- package/dist/services/operate/interaction/terminal-renderer.d.ts +8 -0
- package/dist/services/operate/interaction/terminal-renderer.d.ts.map +1 -0
- package/dist/services/operate/interaction/terminal-renderer.js +103 -0
- package/dist/services/operate/interaction/terminal-renderer.js.map +1 -0
- package/dist/services/operate/maintenance.d.ts.map +1 -1
- package/dist/services/operate/maintenance.js +40 -3
- package/dist/services/operate/maintenance.js.map +1 -1
- package/dist/services/operate/types.d.ts +194 -1
- 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 +1 -0
- package/dist/services/operate/workspace.d.ts.map +1 -1
- package/dist/services/operate/workspace.js +2 -0
- package/dist/services/operate/workspace.js.map +1 -1
- package/dist/services/pipeline-package-service.d.ts +14 -0
- package/dist/services/pipeline-package-service.d.ts.map +1 -1
- package/dist/services/pipeline-package-service.js +45 -1
- package/dist/services/pipeline-package-service.js.map +1 -1
- package/dist/services/runtime-manager-service.d.ts +3 -0
- package/dist/services/runtime-manager-service.d.ts.map +1 -1
- package/dist/services/runtime-manager-service.js +26 -2
- package/dist/services/runtime-manager-service.js.map +1 -1
- package/package.json +4 -3
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { canonicalDigest } from '../canonical.js';
|
|
4
|
+
import { inspectEmbeddedInstructions, normalizeUntrustedText, redactSensitiveText, } from '../redaction.js';
|
|
5
|
+
const ENGINE_VERSION = '1.0.0';
|
|
6
|
+
const MAX_METADATA_BYTES = 64 * 1024;
|
|
7
|
+
const MAX_SUGGESTION_LENGTH = 1_000;
|
|
8
|
+
async function readBoundedJson(projectRoot, relativePath) {
|
|
9
|
+
const target = path.join(projectRoot, relativePath);
|
|
10
|
+
const bytes = await readFile(target).catch(() => null);
|
|
11
|
+
if (!bytes || bytes.byteLength > MAX_METADATA_BYTES)
|
|
12
|
+
return null;
|
|
13
|
+
try {
|
|
14
|
+
const parsed = JSON.parse(bytes.toString('utf8'));
|
|
15
|
+
return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
|
|
16
|
+
? parsed
|
|
17
|
+
: null;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function safeMetadataValue(value) {
|
|
24
|
+
if (typeof value !== 'string')
|
|
25
|
+
return null;
|
|
26
|
+
const normalized = normalizeUntrustedText(value).replace(/\s+/g, ' ').trim();
|
|
27
|
+
if (!normalized || normalized.length > MAX_SUGGESTION_LENGTH)
|
|
28
|
+
return null;
|
|
29
|
+
const inspection = inspectEmbeddedInstructions(normalized);
|
|
30
|
+
if (inspection.annotations.length > 0 || inspection.quarantined)
|
|
31
|
+
return null;
|
|
32
|
+
try {
|
|
33
|
+
const redacted = redactSensitiveText(normalized);
|
|
34
|
+
return redacted.redactions.length === 0 ? redacted.value : null;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Produce bounded, provider-free charter drafts from local metadata.
|
|
42
|
+
*
|
|
43
|
+
* Only purpose is eligible at launch. Product stage, commercial facts,
|
|
44
|
+
* customers, goals, metrics, guardrails, and unknowns remain human-owned.
|
|
45
|
+
*/
|
|
46
|
+
export async function buildOperatingCharterSuggestions(input) {
|
|
47
|
+
const [packageJson, planrConfig] = await Promise.all([
|
|
48
|
+
readBoundedJson(input.projectRoot, 'package.json'),
|
|
49
|
+
readBoundedJson(input.projectRoot, '.planr/config.json'),
|
|
50
|
+
]);
|
|
51
|
+
const evidence = {
|
|
52
|
+
packageDescription: safeMetadataValue(packageJson?.description),
|
|
53
|
+
projectName: safeMetadataValue(planrConfig?.projectName),
|
|
54
|
+
};
|
|
55
|
+
const evidenceDigest = canonicalDigest({
|
|
56
|
+
engineVersion: ENGINE_VERSION,
|
|
57
|
+
evidence,
|
|
58
|
+
});
|
|
59
|
+
const suggestions = [];
|
|
60
|
+
if (evidence.packageDescription) {
|
|
61
|
+
suggestions.push({
|
|
62
|
+
field: 'purpose',
|
|
63
|
+
value: evidence.packageDescription,
|
|
64
|
+
draft: true,
|
|
65
|
+
confidence: 'high',
|
|
66
|
+
citation: {
|
|
67
|
+
source: 'package-json',
|
|
68
|
+
location: 'package.json#description',
|
|
69
|
+
digest: canonicalDigest(evidence.packageDescription),
|
|
70
|
+
},
|
|
71
|
+
engineVersion: ENGINE_VERSION,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
else if (evidence.projectName) {
|
|
75
|
+
suggestions.push({
|
|
76
|
+
field: 'purpose',
|
|
77
|
+
value: `Operate ${evidence.projectName} with explicit, evidence-cited decisions.`,
|
|
78
|
+
draft: true,
|
|
79
|
+
confidence: 'medium',
|
|
80
|
+
citation: {
|
|
81
|
+
source: 'planr-config',
|
|
82
|
+
location: '.planr/config.json#projectName',
|
|
83
|
+
digest: canonicalDigest(evidence.projectName),
|
|
84
|
+
},
|
|
85
|
+
engineVersion: ENGINE_VERSION,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
const suggested = new Set(suggestions.map((entry) => entry.field));
|
|
89
|
+
return {
|
|
90
|
+
evidenceDigest,
|
|
91
|
+
suggestions,
|
|
92
|
+
gaps: [
|
|
93
|
+
'purpose',
|
|
94
|
+
'stage',
|
|
95
|
+
'businessModel',
|
|
96
|
+
'idealCustomer',
|
|
97
|
+
'goals',
|
|
98
|
+
'successMetrics',
|
|
99
|
+
'guardrails',
|
|
100
|
+
'knownUnknowns',
|
|
101
|
+
].filter((field) => !suggested.has(field)),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=charter-suggestions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"charter-suggestions.js","sourceRoot":"","sources":["../../../../src/services/operate/interaction/charter-suggestions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type GuidedConfirmation, type OperatingActionEffect, type OperatingEventHead } from '../types.js';
|
|
2
|
+
export interface OperatingConfirmationMaterial {
|
|
3
|
+
actionId: string;
|
|
4
|
+
sessionId: string;
|
|
5
|
+
command: string;
|
|
6
|
+
effect: Exclude<OperatingActionEffect, 'read-only'>;
|
|
7
|
+
providerUse: boolean;
|
|
8
|
+
confirmationScope: string;
|
|
9
|
+
projectIdentity: `sha256:${string}`;
|
|
10
|
+
projectHead: `sha256:${string}`;
|
|
11
|
+
configHead: `sha256:${string}`;
|
|
12
|
+
eventHead?: OperatingEventHead | null;
|
|
13
|
+
evidenceHead?: `sha256:${string}` | null;
|
|
14
|
+
providerPolicy?: `sha256:${string}` | null;
|
|
15
|
+
arguments?: string[];
|
|
16
|
+
destinations?: string[];
|
|
17
|
+
writes?: string[];
|
|
18
|
+
createdAt?: string;
|
|
19
|
+
expiresAt?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function operatingConfirmationDigest(material: OperatingConfirmationMaterial): `sha256:${string}`;
|
|
22
|
+
export declare function createOperatingConfirmation(material: OperatingConfirmationMaterial): GuidedConfirmation;
|
|
23
|
+
export declare function assertOperatingConfirmation(input: {
|
|
24
|
+
expected: GuidedConfirmation;
|
|
25
|
+
actionId: string;
|
|
26
|
+
confirmationDigest?: string;
|
|
27
|
+
confirmed: boolean;
|
|
28
|
+
now?: Date;
|
|
29
|
+
}): GuidedConfirmation;
|
|
30
|
+
//# sourceMappingURL=confirmation-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confirmation-service.d.ts","sourceRoot":"","sources":["../../../../src/services/operate/interaction/confirmation-service.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,kBAAkB,EAEvB,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,EACxB,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC,qBAAqB,EAAE,WAAW,CAAC,CAAC;IACpD,WAAW,EAAE,OAAO,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,UAAU,MAAM,EAAE,CAAC;IACpC,WAAW,EAAE,UAAU,MAAM,EAAE,CAAC;IAChC,UAAU,EAAE,UAAU,MAAM,EAAE,CAAC;IAC/B,SAAS,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACtC,YAAY,CAAC,EAAE,UAAU,MAAM,EAAE,GAAG,IAAI,CAAC;IACzC,cAAc,CAAC,EAAE,UAAU,MAAM,EAAE,GAAG,IAAI,CAAC;IAC3C,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAMD,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,6BAA6B,GACtC,UAAU,MAAM,EAAE,CAkBpB;AAED,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,6BAA6B,GACtC,kBAAkB,CA4BpB;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE;IACjD,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ,GAAG,kBAAkB,CAkCrB"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { canonicalDigest } from '../canonical.js';
|
|
2
|
+
import { OperateError, } from '../types.js';
|
|
3
|
+
function sorted(values = []) {
|
|
4
|
+
return [...new Set(values)].sort();
|
|
5
|
+
}
|
|
6
|
+
export function operatingConfirmationDigest(material) {
|
|
7
|
+
return canonicalDigest({
|
|
8
|
+
actionId: material.actionId,
|
|
9
|
+
sessionId: material.sessionId,
|
|
10
|
+
command: material.command,
|
|
11
|
+
effect: material.effect,
|
|
12
|
+
providerUse: material.providerUse,
|
|
13
|
+
confirmationScope: material.confirmationScope,
|
|
14
|
+
projectIdentity: material.projectIdentity,
|
|
15
|
+
projectHead: material.projectHead,
|
|
16
|
+
configHead: material.configHead,
|
|
17
|
+
eventHead: material.eventHead ?? null,
|
|
18
|
+
evidenceHead: material.evidenceHead ?? null,
|
|
19
|
+
providerPolicy: material.providerPolicy ?? null,
|
|
20
|
+
arguments: [...(material.arguments ?? [])],
|
|
21
|
+
destinations: sorted(material.destinations),
|
|
22
|
+
writes: sorted(material.writes),
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
export function createOperatingConfirmation(material) {
|
|
26
|
+
const createdAt = material.createdAt ?? new Date().toISOString();
|
|
27
|
+
const expiresAt = material.expiresAt ?? new Date(Date.parse(createdAt) + 30 * 60 * 1_000).toISOString();
|
|
28
|
+
const confirmationDigest = operatingConfirmationDigest(material);
|
|
29
|
+
return {
|
|
30
|
+
kind: 'guided-confirmation',
|
|
31
|
+
schemaVersion: '1.0.0',
|
|
32
|
+
protocolVersion: '1.2.0',
|
|
33
|
+
confirmationId: `GIC-${confirmationDigest.slice('sha256:'.length, 'sha256:'.length + 24)}`,
|
|
34
|
+
state: 'preview',
|
|
35
|
+
actionId: material.actionId,
|
|
36
|
+
sessionId: material.sessionId,
|
|
37
|
+
command: material.command,
|
|
38
|
+
effect: material.effect,
|
|
39
|
+
providerUse: material.providerUse,
|
|
40
|
+
confirmationScope: material.confirmationScope,
|
|
41
|
+
confirmationDigest,
|
|
42
|
+
projectIdentity: material.projectIdentity,
|
|
43
|
+
projectHead: material.projectHead,
|
|
44
|
+
configHead: material.configHead,
|
|
45
|
+
eventHead: material.eventHead ?? null,
|
|
46
|
+
arguments: [...(material.arguments ?? [])],
|
|
47
|
+
destinations: sorted(material.destinations),
|
|
48
|
+
writes: sorted(material.writes),
|
|
49
|
+
createdAt,
|
|
50
|
+
expiresAt,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export function assertOperatingConfirmation(input) {
|
|
54
|
+
const now = input.now ?? new Date();
|
|
55
|
+
if (!input.confirmed ||
|
|
56
|
+
input.actionId !== input.expected.actionId ||
|
|
57
|
+
input.confirmationDigest !== input.expected.confirmationDigest) {
|
|
58
|
+
throw new OperateError('E_OPERATE_ROUTE_CONFIRMATION_REQUIRED', `Action ${input.expected.actionId} requires its exact named confirmation digest.`, {
|
|
59
|
+
actionId: input.expected.actionId,
|
|
60
|
+
confirmationDigest: input.expected.confirmationDigest,
|
|
61
|
+
previewCommand: input.expected.command.replace(/\s+--yes\b/g, ''),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if (now.getTime() >= Date.parse(input.expected.expiresAt)) {
|
|
65
|
+
throw new OperateError('E_OPERATE_ROUTE_DRIFT', `Confirmation for ${input.expected.actionId} expired; preview the action again.`, {
|
|
66
|
+
actionId: input.expected.actionId,
|
|
67
|
+
changedDimensions: ['expiry'],
|
|
68
|
+
previewCommand: input.expected.command.replace(/\s+--yes\b/g, ''),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
...input.expected,
|
|
73
|
+
state: 'confirmed',
|
|
74
|
+
confirmedAt: now.toISOString(),
|
|
75
|
+
confirmedBy: 'explicit-cli-authority',
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=confirmation-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confirmation-service.js","sourceRoot":"","sources":["../../../../src/services/operate/interaction/confirmation-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type OperatingInitAnswers } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Encode the already-reviewed, non-secret initialization answers into a
|
|
4
|
+
* shell-safe replay token. The confirmation digest still provides authority
|
|
5
|
+
* and detects any token modification before a write.
|
|
6
|
+
*/
|
|
7
|
+
export declare function encodeOperatingInitializationReplay(answers: OperatingInitAnswers): string;
|
|
8
|
+
export declare function decodeOperatingInitializationReplay(token: string): OperatingInitAnswers;
|
|
9
|
+
//# sourceMappingURL=initialization-replay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"initialization-replay.d.ts","sourceRoot":"","sources":["../../../../src/services/operate/interaction/initialization-replay.ts"],"names":[],"mappings":"AAIA,OAAO,EAAgB,KAAK,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAQtE;;;;GAIG;AACH,wBAAgB,mCAAmC,CAAC,OAAO,EAAE,oBAAoB,GAAG,MAAM,CAiBzF;AAED,wBAAgB,mCAAmC,CAAC,KAAK,EAAE,MAAM,GAAG,oBAAoB,CAuCvF"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { deflateRawSync, inflateRawSync } from 'node:zlib';
|
|
2
|
+
import { canonicalize } from '../canonical.js';
|
|
3
|
+
import { normalizeOperatingInitializationAnswers } from '../config.js';
|
|
4
|
+
import { parseStrictJson } from '../evidence-import.js';
|
|
5
|
+
import { OperateError } from '../types.js';
|
|
6
|
+
const MAX_REPLAY_BYTES = 64 * 1024;
|
|
7
|
+
// Leave headroom below the Windows 32,767-character command-line ceiling for
|
|
8
|
+
// the command, timestamp, confirmation digest, and JSON flags.
|
|
9
|
+
const MAX_TOKEN_LENGTH = 24 * 1024;
|
|
10
|
+
const BASE64URL = /^[A-Za-z0-9_-]+$/;
|
|
11
|
+
/**
|
|
12
|
+
* Encode the already-reviewed, non-secret initialization answers into a
|
|
13
|
+
* shell-safe replay token. The confirmation digest still provides authority
|
|
14
|
+
* and detects any token modification before a write.
|
|
15
|
+
*/
|
|
16
|
+
export function encodeOperatingInitializationReplay(answers) {
|
|
17
|
+
const normalized = normalizeOperatingInitializationAnswers(answers);
|
|
18
|
+
const raw = Buffer.from(canonicalize(normalized), 'utf8');
|
|
19
|
+
if (raw.byteLength > MAX_REPLAY_BYTES) {
|
|
20
|
+
throw new OperateError('E_OPERATE_INPUT_TOO_LARGE', `Initialization answers exceed the ${MAX_REPLAY_BYTES}-byte replay limit.`);
|
|
21
|
+
}
|
|
22
|
+
const token = deflateRawSync(raw, { level: 9 }).toString('base64url');
|
|
23
|
+
if (token.length > MAX_TOKEN_LENGTH) {
|
|
24
|
+
throw new OperateError('E_OPERATE_INPUT_TOO_LARGE', 'Encoded initialization replay token exceeds the supported command limit.');
|
|
25
|
+
}
|
|
26
|
+
return token;
|
|
27
|
+
}
|
|
28
|
+
export function decodeOperatingInitializationReplay(token) {
|
|
29
|
+
if (token.length === 0 || token.length > MAX_TOKEN_LENGTH || !BASE64URL.test(token)) {
|
|
30
|
+
throw new OperateError(token.length > MAX_TOKEN_LENGTH ? 'E_OPERATE_INPUT_TOO_LARGE' : 'E_OPERATE_SESSION_INVALID', 'Initialization replay token is malformed or exceeds the supported limit.');
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
const compressed = Buffer.from(token, 'base64url');
|
|
34
|
+
if (compressed.toString('base64url') !== token) {
|
|
35
|
+
throw new Error('non-canonical base64url');
|
|
36
|
+
}
|
|
37
|
+
const raw = inflateRawSync(compressed, {
|
|
38
|
+
maxOutputLength: MAX_REPLAY_BYTES,
|
|
39
|
+
}).toString('utf8');
|
|
40
|
+
const parsed = parseStrictJson(raw, {
|
|
41
|
+
maxBytes: MAX_REPLAY_BYTES,
|
|
42
|
+
maxDepth: 12,
|
|
43
|
+
maxScalars: 512,
|
|
44
|
+
maxStringLength: 4096,
|
|
45
|
+
});
|
|
46
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
47
|
+
throw new Error('replay payload is not an object');
|
|
48
|
+
}
|
|
49
|
+
const normalized = normalizeOperatingInitializationAnswers(parsed);
|
|
50
|
+
// Raw DEFLATE decoders may accept and ignore trailing bytes. Re-encode the
|
|
51
|
+
// validated payload so every accepted token has exactly one canonical byte
|
|
52
|
+
// representation and appended data cannot survive as a valid replay.
|
|
53
|
+
if (encodeOperatingInitializationReplay(normalized) !== token) {
|
|
54
|
+
throw new Error('non-canonical replay token');
|
|
55
|
+
}
|
|
56
|
+
return normalized;
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
if (error instanceof OperateError)
|
|
60
|
+
throw error;
|
|
61
|
+
throw new OperateError('E_OPERATE_SESSION_INVALID', 'Initialization replay token is invalid or corrupted.');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=initialization-replay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"initialization-replay.js","sourceRoot":"","sources":["../../../../src/services/operate/interaction/initialization-replay.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type GuidedQuestion, type GuidedQuestionnaire, type GuidedQuestionValue, type OperatingInitAnswers } from '../types.js';
|
|
2
|
+
import { type OperatingQuestionContext } from './question-registry.js';
|
|
3
|
+
export interface OperatingQuestionEngineContext extends OperatingQuestionContext {
|
|
4
|
+
projectRoot: string;
|
|
5
|
+
projectIdentity?: `sha256:${string}`;
|
|
6
|
+
runtime?: string;
|
|
7
|
+
interaction?: 'native' | 'chat' | 'terminal' | 'none';
|
|
8
|
+
projectHead?: `sha256:${string}`;
|
|
9
|
+
configHead?: `sha256:${string}`;
|
|
10
|
+
now?: string;
|
|
11
|
+
}
|
|
12
|
+
export type OperatingQuestionEngineResult = {
|
|
13
|
+
status: 'input-required';
|
|
14
|
+
stage: 'foundation' | 'product-charter';
|
|
15
|
+
answers: OperatingInitAnswers;
|
|
16
|
+
questions: GuidedQuestion[];
|
|
17
|
+
} | {
|
|
18
|
+
status: 'preview-ready';
|
|
19
|
+
stage: 'review';
|
|
20
|
+
answers: OperatingInitAnswers;
|
|
21
|
+
questions: GuidedQuestion[];
|
|
22
|
+
};
|
|
23
|
+
export declare function operatingInitAnswersFromOptions(options: Record<string, unknown>): OperatingInitAnswers;
|
|
24
|
+
export declare function mergeOperatingInitAnswersIntoOptions(options: Record<string, unknown>, answers: OperatingInitAnswers): Record<string, unknown>;
|
|
25
|
+
export declare function evaluateOperatingInitQuestions(input: {
|
|
26
|
+
answers?: OperatingInitAnswers;
|
|
27
|
+
context: OperatingQuestionEngineContext;
|
|
28
|
+
requireCharter?: boolean;
|
|
29
|
+
}): Promise<OperatingQuestionEngineResult>;
|
|
30
|
+
export declare function applyOperatingInitAnswer(answers: OperatingInitAnswers, context: OperatingQuestionContext, questionId: string, value: GuidedQuestionValue): OperatingInitAnswers;
|
|
31
|
+
export declare function createOperatingInitQuestionnaire(input: {
|
|
32
|
+
context: OperatingQuestionEngineContext;
|
|
33
|
+
questions: GuidedQuestion[];
|
|
34
|
+
stage: 'foundation' | 'product-charter' | 'review';
|
|
35
|
+
sessionId?: string;
|
|
36
|
+
createdAt?: string;
|
|
37
|
+
expiresAt?: string;
|
|
38
|
+
}): Promise<GuidedQuestionnaire>;
|
|
39
|
+
//# sourceMappingURL=question-engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"question-engine.d.ts","sourceRoot":"","sources":["../../../../src/services/operate/interaction/question-engine.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EAExB,KAAK,oBAAoB,EAC1B,MAAM,aAAa,CAAC;AAErB,OAAO,EAGL,KAAK,wBAAwB,EAE9B,MAAM,wBAAwB,CAAC;AAIhC,MAAM,WAAW,8BAA+B,SAAQ,wBAAwB;IAC9E,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,UAAU,MAAM,EAAE,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;IACtD,WAAW,CAAC,EAAE,UAAU,MAAM,EAAE,CAAC;IACjC,UAAU,CAAC,EAAE,UAAU,MAAM,EAAE,CAAC;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,6BAA6B,GACrC;IACE,MAAM,EAAE,gBAAgB,CAAC;IACzB,KAAK,EAAE,YAAY,GAAG,iBAAiB,CAAC;IACxC,OAAO,EAAE,oBAAoB,CAAC;IAC9B,SAAS,EAAE,cAAc,EAAE,CAAC;CAC7B,GACD;IACE,MAAM,EAAE,eAAe,CAAC;IACxB,KAAK,EAAE,QAAQ,CAAC;IAChB,OAAO,EAAE,oBAAoB,CAAC;IAC9B,SAAS,EAAE,cAAc,EAAE,CAAC;CAC7B,CAAC;AAEN,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,oBAAoB,CAwDtB;AAED,wBAAgB,oCAAoC,CAClD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,OAAO,EAAE,oBAAoB,GAC5B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAUzB;AA8GD,wBAAsB,8BAA8B,CAAC,KAAK,EAAE;IAC1D,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAC/B,OAAO,EAAE,8BAA8B,CAAC;IACxC,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,GAAG,OAAO,CAAC,6BAA6B,CAAC,CA6DzC;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,oBAAoB,EAC7B,OAAO,EAAE,wBAAwB,EACjC,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,mBAAmB,GACzB,oBAAoB,CAWtB;AAED,wBAAsB,gCAAgC,CAAC,KAAK,EAAE;IAC5D,OAAO,EAAE,8BAA8B,CAAC;IACxC,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,KAAK,EAAE,YAAY,GAAG,iBAAiB,GAAG,QAAQ,CAAC;IACnD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA2E/B"}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { resolveGuidedInteractionValidators } from '../../pipeline-package-service.js';
|
|
3
|
+
import { canonicalDigest } from '../canonical.js';
|
|
4
|
+
import { OperateError, } from '../types.js';
|
|
5
|
+
import { buildOperatingCharterSuggestions } from './charter-suggestions.js';
|
|
6
|
+
import { operatingInitQuestionRegistry, } from './question-registry.js';
|
|
7
|
+
const STAGES = ['foundation', 'product-charter', 'review'];
|
|
8
|
+
export function operatingInitAnswersFromOptions(options) {
|
|
9
|
+
const list = (value) => Array.isArray(value)
|
|
10
|
+
? value
|
|
11
|
+
.map(String)
|
|
12
|
+
.map((entry) => entry.trim())
|
|
13
|
+
.filter(Boolean)
|
|
14
|
+
: undefined;
|
|
15
|
+
const charter = options.charter && typeof options.charter === 'object' && !Array.isArray(options.charter)
|
|
16
|
+
? options.charter
|
|
17
|
+
: {
|
|
18
|
+
...(typeof options.purpose === 'string' ? { purpose: options.purpose } : {}),
|
|
19
|
+
...(typeof options.productStage === 'string' ? { stage: options.productStage } : {}),
|
|
20
|
+
...(typeof options.businessModel === 'string'
|
|
21
|
+
? { businessModel: options.businessModel }
|
|
22
|
+
: {}),
|
|
23
|
+
...(typeof options.idealCustomer === 'string'
|
|
24
|
+
? { idealCustomer: options.idealCustomer }
|
|
25
|
+
: {}),
|
|
26
|
+
...(list(options.goal) ? { goals: list(options.goal) } : {}),
|
|
27
|
+
...(list(options.successMetric) ? { successMetrics: list(options.successMetric) } : {}),
|
|
28
|
+
...(list(options.guardrail) ? { guardrails: list(options.guardrail) } : {}),
|
|
29
|
+
...(list(options.knownUnknown) ? { knownUnknowns: list(options.knownUnknown) } : {}),
|
|
30
|
+
};
|
|
31
|
+
return {
|
|
32
|
+
...(typeof options.profile === 'string'
|
|
33
|
+
? { profile: options.profile }
|
|
34
|
+
: {}),
|
|
35
|
+
...(typeof options.profileFile === 'string' ? { profileFile: options.profileFile } : {}),
|
|
36
|
+
...(typeof options.decisionOwner === 'string' ? { decisionOwner: options.decisionOwner } : {}),
|
|
37
|
+
...(typeof options.planningEngine === 'string'
|
|
38
|
+
? { planningEngine: options.planningEngine }
|
|
39
|
+
: {}),
|
|
40
|
+
...(typeof options.runtime === 'string'
|
|
41
|
+
? { runtime: options.runtime }
|
|
42
|
+
: {}),
|
|
43
|
+
...(typeof options.cadence === 'string'
|
|
44
|
+
? { cadence: options.cadence }
|
|
45
|
+
: {}),
|
|
46
|
+
...(typeof options.timezone === 'string' ? { timezone: options.timezone } : {}),
|
|
47
|
+
...(typeof options.sensitivityCeiling === 'string'
|
|
48
|
+
? {
|
|
49
|
+
sensitivityCeiling: options.sensitivityCeiling,
|
|
50
|
+
}
|
|
51
|
+
: {}),
|
|
52
|
+
...(list(options.sources ?? options.source)
|
|
53
|
+
? { sources: list(options.sources ?? options.source) }
|
|
54
|
+
: {}),
|
|
55
|
+
...(list(options.evidenceFile) ? { evidenceFiles: list(options.evidenceFile) } : {}),
|
|
56
|
+
...(list(options.components ?? options.component)
|
|
57
|
+
? { componentRoots: list(options.components ?? options.component) }
|
|
58
|
+
: {}),
|
|
59
|
+
...(Object.keys(charter ?? {}).length > 0 ? { charter } : {}),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export function mergeOperatingInitAnswersIntoOptions(options, answers) {
|
|
63
|
+
return {
|
|
64
|
+
...options,
|
|
65
|
+
...answers,
|
|
66
|
+
...(answers.sources === undefined ? {} : { source: answers.sources, sources: answers.sources }),
|
|
67
|
+
...(answers.evidenceFiles === undefined ? {} : { evidenceFile: answers.evidenceFiles }),
|
|
68
|
+
...(answers.componentRoots === undefined
|
|
69
|
+
? {}
|
|
70
|
+
: { component: answers.componentRoots, components: answers.componentRoots }),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function answered(value) {
|
|
74
|
+
if (typeof value === 'string')
|
|
75
|
+
return value.trim().length > 0;
|
|
76
|
+
if (Array.isArray(value))
|
|
77
|
+
return value.length > 0;
|
|
78
|
+
return value !== undefined;
|
|
79
|
+
}
|
|
80
|
+
function conditionVisible(definition, definitions, answers) {
|
|
81
|
+
return (definition.question.visibleWhen ?? []).every((condition) => {
|
|
82
|
+
const dependency = definitions.find((candidate) => candidate.question.questionId === condition.questionId);
|
|
83
|
+
const actual = dependency?.read(answers);
|
|
84
|
+
switch (condition.operator) {
|
|
85
|
+
case 'answered':
|
|
86
|
+
return answered(actual);
|
|
87
|
+
case 'not-answered':
|
|
88
|
+
return !answered(actual);
|
|
89
|
+
case 'equals':
|
|
90
|
+
return actual === condition.value;
|
|
91
|
+
case 'not-equals':
|
|
92
|
+
return actual !== condition.value;
|
|
93
|
+
case 'contains':
|
|
94
|
+
return Array.isArray(actual)
|
|
95
|
+
? actual.includes(String(condition.value))
|
|
96
|
+
: String(actual ?? '').includes(String(condition.value));
|
|
97
|
+
case 'not-contains':
|
|
98
|
+
return Array.isArray(actual)
|
|
99
|
+
? !actual.includes(String(condition.value))
|
|
100
|
+
: !String(actual ?? '').includes(String(condition.value));
|
|
101
|
+
}
|
|
102
|
+
return false;
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
function normalizeValue(question, value) {
|
|
106
|
+
const normalized = Array.isArray(value)
|
|
107
|
+
? [...new Set(value.map((entry) => entry.trim()).filter(Boolean))]
|
|
108
|
+
: typeof value === 'string'
|
|
109
|
+
? value.trim()
|
|
110
|
+
: value;
|
|
111
|
+
const choices = question.choices?.map((choice) => choice.id);
|
|
112
|
+
if (choices &&
|
|
113
|
+
(Array.isArray(normalized)
|
|
114
|
+
? normalized.some((entry) => !choices.includes(entry))
|
|
115
|
+
: !choices.includes(String(normalized)))) {
|
|
116
|
+
throw new OperateError('E_OPERATE_QUESTIONNAIRE_INVALID', `${question.label} contains an unsupported choice.`);
|
|
117
|
+
}
|
|
118
|
+
const length = typeof normalized === 'string' ? normalized.length : undefined;
|
|
119
|
+
const itemCount = Array.isArray(normalized) ? normalized.length : undefined;
|
|
120
|
+
if ((length !== undefined &&
|
|
121
|
+
((question.validation?.minLength !== undefined && length < question.validation.minLength) ||
|
|
122
|
+
(question.validation?.maxLength !== undefined &&
|
|
123
|
+
length > question.validation.maxLength))) ||
|
|
124
|
+
(itemCount !== undefined &&
|
|
125
|
+
((question.validation?.minItems !== undefined && itemCount < question.validation.minItems) ||
|
|
126
|
+
(question.validation?.maxItems !== undefined && itemCount > question.validation.maxItems)))) {
|
|
127
|
+
throw new OperateError('E_OPERATE_QUESTIONNAIRE_INVALID', `${question.label} does not satisfy its bounded validation rules.`);
|
|
128
|
+
}
|
|
129
|
+
return normalized;
|
|
130
|
+
}
|
|
131
|
+
function validateSemanticAnswers(answers, context) {
|
|
132
|
+
if (answers.timezone) {
|
|
133
|
+
try {
|
|
134
|
+
new Intl.DateTimeFormat('en', { timeZone: answers.timezone }).format(new Date(0));
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
throw new OperateError('E_OPERATE_QUESTIONNAIRE_INVALID', `Invalid IANA timezone: ${answers.timezone}.`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const unavailable = (answers.sources ?? []).filter((source) => !context.availableSources.includes(source));
|
|
141
|
+
if (unavailable.length > 0) {
|
|
142
|
+
throw new OperateError('E_OPERATE_QUESTIONNAIRE_INVALID', `Evidence source is unavailable: ${unavailable.join(', ')}. Configure it or leave it disabled.`);
|
|
143
|
+
}
|
|
144
|
+
for (const candidate of answers.evidenceFiles ?? []) {
|
|
145
|
+
if (path.isAbsolute(candidate) || candidate.split(/[\\/]+/).includes('..')) {
|
|
146
|
+
throw new OperateError('E_OPERATE_QUESTIONNAIRE_INVALID', 'Evidence import paths must remain relative to a configured workspace root.');
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
export async function evaluateOperatingInitQuestions(input) {
|
|
151
|
+
const definitions = operatingInitQuestionRegistry(input.context);
|
|
152
|
+
let answers = structuredClone(input.answers ?? {});
|
|
153
|
+
for (const definition of definitions) {
|
|
154
|
+
const current = definition.read(answers);
|
|
155
|
+
if (current !== undefined && answered(current)) {
|
|
156
|
+
answers = definition.write(answers, normalizeValue(definition.question, current));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
validateSemanticAnswers(answers, input.context);
|
|
160
|
+
for (const stage of STAGES.slice(0, input.requireCharter === false ? 1 : 2)) {
|
|
161
|
+
const visible = definitions.filter((definition) => definition.stage === stage && conditionVisible(definition, definitions, answers));
|
|
162
|
+
const missing = visible.filter((definition) => definition.question.type !== 'informational' &&
|
|
163
|
+
definition.question.required &&
|
|
164
|
+
!answered(definition.read(answers)));
|
|
165
|
+
if (missing.length > 0) {
|
|
166
|
+
const suggestions = stage === 'product-charter'
|
|
167
|
+
? await buildOperatingCharterSuggestions({ projectRoot: input.context.projectRoot })
|
|
168
|
+
: null;
|
|
169
|
+
return {
|
|
170
|
+
status: 'input-required',
|
|
171
|
+
stage: stage,
|
|
172
|
+
answers,
|
|
173
|
+
questions: visible
|
|
174
|
+
.filter((definition) => !answered(definition.read(answers)))
|
|
175
|
+
.map((definition) => {
|
|
176
|
+
const question = structuredClone(definition.question);
|
|
177
|
+
const suggestion = suggestions?.suggestions.find((entry) => entry.field === question.questionId);
|
|
178
|
+
if (!suggestion)
|
|
179
|
+
return question;
|
|
180
|
+
return {
|
|
181
|
+
...question,
|
|
182
|
+
valueSemantics: 'suggestion',
|
|
183
|
+
suggestedValue: suggestion.value,
|
|
184
|
+
suggestionReason: [
|
|
185
|
+
`Draft from ${suggestion.citation.location}`,
|
|
186
|
+
`${suggestion.confidence} confidence`,
|
|
187
|
+
`evidence ${suggestion.citation.digest}`,
|
|
188
|
+
`rules ${suggestion.engineVersion}`,
|
|
189
|
+
].join('; '),
|
|
190
|
+
};
|
|
191
|
+
}),
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
status: 'preview-ready',
|
|
197
|
+
stage: 'review',
|
|
198
|
+
answers,
|
|
199
|
+
questions: definitions
|
|
200
|
+
.filter((definition) => definition.stage === 'review')
|
|
201
|
+
.map((definition) => structuredClone(definition.question)),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
export function applyOperatingInitAnswer(answers, context, questionId, value) {
|
|
205
|
+
const definition = operatingInitQuestionRegistry(context).find((candidate) => candidate.question.questionId === questionId);
|
|
206
|
+
if (!definition) {
|
|
207
|
+
throw new OperateError('E_OPERATE_QUESTIONNAIRE_INVALID', `Unknown Operating Board question: ${questionId}.`);
|
|
208
|
+
}
|
|
209
|
+
return definition.write(answers, normalizeValue(definition.question, value));
|
|
210
|
+
}
|
|
211
|
+
export async function createOperatingInitQuestionnaire(input) {
|
|
212
|
+
const createdAt = input.createdAt ?? input.context.now ?? new Date().toISOString();
|
|
213
|
+
const expiresAt = input.expiresAt ?? new Date(new Date(createdAt).getTime() + 24 * 60 * 60 * 1000).toISOString();
|
|
214
|
+
const projectIdentity = input.context.projectIdentity ??
|
|
215
|
+
canonicalDigest({
|
|
216
|
+
projectRoot: path.resolve(input.context.projectRoot),
|
|
217
|
+
});
|
|
218
|
+
const projectHead = input.context.projectHead ?? canonicalDigest({ projectIdentity });
|
|
219
|
+
const configHead = input.context.configHead ?? canonicalDigest({ config: null });
|
|
220
|
+
const seed = {
|
|
221
|
+
command: 'operate.init',
|
|
222
|
+
projectIdentity,
|
|
223
|
+
projectHead,
|
|
224
|
+
configHead,
|
|
225
|
+
stage: input.stage,
|
|
226
|
+
questions: input.questions,
|
|
227
|
+
createdAt,
|
|
228
|
+
expiresAt,
|
|
229
|
+
};
|
|
230
|
+
const sessionId = input.sessionId ??
|
|
231
|
+
`GIS-${canonicalDigest(seed).slice('sha256:'.length, 'sha256:'.length + 24)}`;
|
|
232
|
+
const withoutDigest = {
|
|
233
|
+
kind: 'guided-questionnaire',
|
|
234
|
+
schemaVersion: '1.0.0',
|
|
235
|
+
protocolVersion: '1.2.0',
|
|
236
|
+
sessionId,
|
|
237
|
+
questionnaireVersion: '1.0.0',
|
|
238
|
+
command: 'operate.init',
|
|
239
|
+
projectIdentity,
|
|
240
|
+
projectHead,
|
|
241
|
+
configHead,
|
|
242
|
+
adapter: {
|
|
243
|
+
runtime: input.context.runtime ?? 'unknown',
|
|
244
|
+
version: 'detected',
|
|
245
|
+
interaction: input.context.interaction ?? 'none',
|
|
246
|
+
},
|
|
247
|
+
stage: input.stage,
|
|
248
|
+
step: STAGES.indexOf(input.stage) + 1,
|
|
249
|
+
totalSteps: 3,
|
|
250
|
+
title: 'Configure the Operating Board',
|
|
251
|
+
description: input.stage === 'review'
|
|
252
|
+
? 'Review exact writes and boundaries before applying initialization.'
|
|
253
|
+
: 'Answer only the governance questions in this stage.',
|
|
254
|
+
questions: input.questions,
|
|
255
|
+
createdAt,
|
|
256
|
+
expiresAt,
|
|
257
|
+
};
|
|
258
|
+
const questionnaire = {
|
|
259
|
+
...withoutDigest,
|
|
260
|
+
digest: canonicalDigest(withoutDigest),
|
|
261
|
+
};
|
|
262
|
+
let validators;
|
|
263
|
+
try {
|
|
264
|
+
validators = await resolveGuidedInteractionValidators();
|
|
265
|
+
}
|
|
266
|
+
catch (error) {
|
|
267
|
+
throw new OperateError(error instanceof Error && error.name === 'E_PIPELINE_NOT_INSTALLED'
|
|
268
|
+
? 'E_PIPELINE_NOT_INSTALLED'
|
|
269
|
+
: 'E_PIPELINE_VERSION_INCOMPATIBLE', error instanceof Error ? error.message : 'Compatible guided validators are unavailable.');
|
|
270
|
+
}
|
|
271
|
+
const errors = validators.validateGuidedQuestionnaire(questionnaire);
|
|
272
|
+
if (errors.length > 0) {
|
|
273
|
+
throw new OperateError('E_OPERATE_QUESTIONNAIRE_INVALID', 'The generated Operating Board questionnaire did not satisfy Protocol v1.2.', { validationErrors: errors });
|
|
274
|
+
}
|
|
275
|
+
return questionnaire;
|
|
276
|
+
}
|
|
277
|
+
//# sourceMappingURL=question-engine.js.map
|