@open-agent-toolkit/cli 0.2.64 → 0.2.66
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/assets/bundle-metadata.json +1 -1
- package/assets/docs/cli-utilities/configuration.md +19 -1
- package/assets/docs/cli-utilities/index.md +3 -0
- package/assets/docs/cli-utilities/remote-project-management.md +191 -0
- package/assets/docs/contributing/skills.md +15 -5
- package/assets/docs/reference/cli-reference.md +12 -1
- package/assets/docs/reference/file-locations.md +22 -0
- package/assets/docs/reference/oat-directory-structure.md +30 -17
- package/assets/docs/reference/troubleshooting.md +44 -0
- package/assets/docs/workflows/skills/index.md +33 -0
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/analyze/SKILL.md +2 -1
- package/assets/skills/authoring-docs/SKILL.md +2 -1
- package/assets/skills/compare/SKILL.md +2 -1
- package/assets/skills/create-agnostic-skill/SKILL.md +2 -1
- package/assets/skills/deep-research/SKILL.md +2 -1
- package/assets/skills/explainer-kit/SKILL.md +2 -1
- package/assets/skills/oat-agent-instructions-analyze/SKILL.md +2 -1
- package/assets/skills/oat-agent-instructions-apply/SKILL.md +2 -1
- package/assets/skills/oat-brainstorm/SKILL.md +2 -1
- package/assets/skills/oat-cursor-cloud-projects/SKILL.md +2 -1
- package/assets/skills/oat-dispatch-subagents/SKILL.md +2 -1
- package/assets/skills/oat-docs/SKILL.md +2 -1
- package/assets/skills/oat-docs-analyze/SKILL.md +2 -1
- package/assets/skills/oat-docs-apply/SKILL.md +2 -1
- package/assets/skills/oat-docs-authoring/SKILL.md +2 -1
- package/assets/skills/oat-docs-bootstrap/SKILL.md +2 -1
- package/assets/skills/oat-doctor/SKILL.md +5 -4
- package/assets/skills/oat-explainer-kit/SKILL.md +2 -1
- package/assets/skills/oat-explainer-kit/scripts/check-core.mjs +322 -2
- package/assets/skills/oat-idea-ideate/SKILL.md +2 -1
- package/assets/skills/oat-idea-new/SKILL.md +2 -1
- package/assets/skills/oat-idea-scratchpad/SKILL.md +2 -1
- package/assets/skills/oat-idea-summarize/SKILL.md +2 -1
- package/assets/skills/oat-pjm-add-backlog-item/SKILL.md +2 -1
- package/assets/skills/oat-pjm-decision/SKILL.md +2 -1
- package/assets/skills/oat-pjm-remote/SKILL.md +101 -0
- package/assets/skills/oat-pjm-remote/references/external-action-protocol.md +43 -0
- package/assets/skills/oat-pjm-review-backlog/SKILL.md +2 -1
- package/assets/skills/oat-pjm-update-repo-reference/SKILL.md +2 -1
- package/assets/skills/oat-project-autonomous/SKILL.md +2 -1
- package/assets/skills/oat-project-capture/SKILL.md +2 -1
- package/assets/skills/oat-project-clear-active/SKILL.md +2 -1
- package/assets/skills/oat-project-complete/SKILL.md +2 -1
- package/assets/skills/oat-project-design/SKILL.md +2 -1
- package/assets/skills/oat-project-discover/SKILL.md +2 -1
- package/assets/skills/oat-project-dispatch-subagents/SKILL.md +2 -1
- package/assets/skills/oat-project-document/SKILL.md +2 -1
- package/assets/skills/oat-project-implement/SKILL.md +2 -1
- package/assets/skills/oat-project-import-plan/SKILL.md +2 -1
- package/assets/skills/oat-project-lite/SKILL.md +2 -1
- package/assets/skills/oat-project-new/SKILL.md +2 -1
- package/assets/skills/oat-project-next/SKILL.md +2 -1
- package/assets/skills/oat-project-open/SKILL.md +2 -1
- package/assets/skills/oat-project-plan/SKILL.md +2 -1
- package/assets/skills/oat-project-plan-writing/SKILL.md +2 -1
- package/assets/skills/oat-project-pr-final/SKILL.md +2 -1
- package/assets/skills/oat-project-pr-progress/SKILL.md +2 -1
- package/assets/skills/oat-project-progress/SKILL.md +2 -1
- package/assets/skills/oat-project-promote-spec-driven/SKILL.md +2 -1
- package/assets/skills/oat-project-quick-start/SKILL.md +2 -1
- package/assets/skills/oat-project-reconcile/SKILL.md +2 -1
- package/assets/skills/oat-project-retro/SKILL.md +2 -1
- package/assets/skills/oat-project-retro-file/SKILL.md +2 -1
- package/assets/skills/oat-project-review-provide/SKILL.md +2 -1
- package/assets/skills/oat-project-review-provide-remote/SKILL.md +2 -1
- package/assets/skills/oat-project-review-receive/SKILL.md +2 -1
- package/assets/skills/oat-project-review-receive-remote/SKILL.md +2 -1
- package/assets/skills/oat-project-revise/SKILL.md +2 -1
- package/assets/skills/oat-project-spec/SKILL.md +2 -1
- package/assets/skills/oat-project-split/SKILL.md +2 -1
- package/assets/skills/oat-project-summary/SKILL.md +2 -1
- package/assets/skills/oat-repo-improve/SKILL.md +1 -1
- package/assets/skills/oat-repo-knowledge-index/SKILL.md +2 -1
- package/assets/skills/oat-repo-maintainability-review/SKILL.md +2 -1
- package/assets/skills/oat-review-provide/SKILL.md +2 -1
- package/assets/skills/oat-review-provide-remote/SKILL.md +2 -1
- package/assets/skills/oat-review-receive/SKILL.md +2 -1
- package/assets/skills/oat-review-receive-remote/SKILL.md +2 -1
- package/assets/skills/oat-wave-execute/SKILL.md +2 -1
- package/assets/skills/oat-wave-program/SKILL.md +2 -1
- package/assets/skills/oat-worktree-bootstrap/SKILL.md +2 -1
- package/assets/skills/oat-worktree-bootstrap-auto/SKILL.md +2 -1
- package/assets/skills/oat-wrap-up/SKILL.md +2 -1
- package/assets/skills/recon/SKILL.md +2 -1
- package/assets/skills/skeptic/SKILL.md +2 -1
- package/assets/skills/subagent-orchestration/SKILL.md +2 -1
- package/assets/skills/synthesize/SKILL.md +2 -1
- package/dist/commands/backlog/new.d.ts +1 -0
- package/dist/commands/backlog/new.d.ts.map +1 -1
- package/dist/commands/backlog/new.js +3 -1
- package/dist/commands/config/index.d.ts +1 -0
- package/dist/commands/config/index.d.ts.map +1 -1
- package/dist/commands/config/index.js +232 -1
- package/dist/commands/pjm/doctor.d.ts +2 -0
- package/dist/commands/pjm/doctor.d.ts.map +1 -1
- package/dist/commands/pjm/doctor.js +71 -1
- package/dist/commands/pjm/index.d.ts +2 -0
- package/dist/commands/pjm/index.d.ts.map +1 -1
- package/dist/commands/pjm/index.js +3 -0
- package/dist/commands/pjm/remote/__integration__/lifecycle-harness.d.ts +60 -0
- package/dist/commands/pjm/remote/__integration__/lifecycle-harness.d.ts.map +1 -0
- package/dist/commands/pjm/remote/__integration__/lifecycle-harness.js +151 -0
- package/dist/commands/pjm/remote/association.d.ts +27 -0
- package/dist/commands/pjm/remote/association.d.ts.map +1 -0
- package/dist/commands/pjm/remote/association.js +68 -0
- package/dist/commands/pjm/remote/authority.d.ts +210 -0
- package/dist/commands/pjm/remote/authority.d.ts.map +1 -0
- package/dist/commands/pjm/remote/authority.js +341 -0
- package/dist/commands/pjm/remote/batch.d.ts +15 -0
- package/dist/commands/pjm/remote/batch.d.ts.map +1 -0
- package/dist/commands/pjm/remote/batch.js +126 -0
- package/dist/commands/pjm/remote/closeout.d.ts +69 -0
- package/dist/commands/pjm/remote/closeout.d.ts.map +1 -0
- package/dist/commands/pjm/remote/closeout.js +208 -0
- package/dist/commands/pjm/remote/create-binding.d.ts +62 -0
- package/dist/commands/pjm/remote/create-binding.d.ts.map +1 -0
- package/dist/commands/pjm/remote/create-binding.js +126 -0
- package/dist/commands/pjm/remote/credential-safety.d.ts +7 -0
- package/dist/commands/pjm/remote/credential-safety.d.ts.map +1 -0
- package/dist/commands/pjm/remote/credential-safety.js +21 -0
- package/dist/commands/pjm/remote/discussion.d.ts +49 -0
- package/dist/commands/pjm/remote/discussion.d.ts.map +1 -0
- package/dist/commands/pjm/remote/discussion.js +124 -0
- package/dist/commands/pjm/remote/doctor.d.ts +22 -0
- package/dist/commands/pjm/remote/doctor.d.ts.map +1 -0
- package/dist/commands/pjm/remote/doctor.js +380 -0
- package/dist/commands/pjm/remote/external-action.d.ts +208 -0
- package/dist/commands/pjm/remote/external-action.d.ts.map +1 -0
- package/dist/commands/pjm/remote/external-action.js +367 -0
- package/dist/commands/pjm/remote/host-execution.d.ts +28 -0
- package/dist/commands/pjm/remote/host-execution.d.ts.map +1 -0
- package/dist/commands/pjm/remote/host-execution.js +63 -0
- package/dist/commands/pjm/remote/index.d.ts +41 -0
- package/dist/commands/pjm/remote/index.d.ts.map +1 -0
- package/dist/commands/pjm/remote/index.js +280 -0
- package/dist/commands/pjm/remote/jira-adf.d.ts +35 -0
- package/dist/commands/pjm/remote/jira-adf.d.ts.map +1 -0
- package/dist/commands/pjm/remote/jira-adf.js +169 -0
- package/dist/commands/pjm/remote/lifecycle.d.ts +163 -0
- package/dist/commands/pjm/remote/lifecycle.d.ts.map +1 -0
- package/dist/commands/pjm/remote/lifecycle.js +224 -0
- package/dist/commands/pjm/remote/local-projection.d.ts +30 -0
- package/dist/commands/pjm/remote/local-projection.d.ts.map +1 -0
- package/dist/commands/pjm/remote/local-projection.js +95 -0
- package/dist/commands/pjm/remote/managed-markdown.d.ts +30 -0
- package/dist/commands/pjm/remote/managed-markdown.d.ts.map +1 -0
- package/dist/commands/pjm/remote/managed-markdown.js +128 -0
- package/dist/commands/pjm/remote/migrate.d.ts +14 -0
- package/dist/commands/pjm/remote/migrate.d.ts.map +1 -0
- package/dist/commands/pjm/remote/migrate.js +32 -0
- package/dist/commands/pjm/remote/operation-state.d.ts +15 -0
- package/dist/commands/pjm/remote/operation-state.d.ts.map +1 -0
- package/dist/commands/pjm/remote/operation-state.js +143 -0
- package/dist/commands/pjm/remote/outbound-projection-safety.d.ts +25 -0
- package/dist/commands/pjm/remote/outbound-projection-safety.d.ts.map +1 -0
- package/dist/commands/pjm/remote/outbound-projection-safety.js +99 -0
- package/dist/commands/pjm/remote/output.d.ts +60 -0
- package/dist/commands/pjm/remote/output.d.ts.map +1 -0
- package/dist/commands/pjm/remote/output.js +48 -0
- package/dist/commands/pjm/remote/preview.d.ts +88 -0
- package/dist/commands/pjm/remote/preview.d.ts.map +1 -0
- package/dist/commands/pjm/remote/preview.js +154 -0
- package/dist/commands/pjm/remote/provider-conformance.d.ts +13 -0
- package/dist/commands/pjm/remote/provider-conformance.d.ts.map +1 -0
- package/dist/commands/pjm/remote/provider-conformance.js +37 -0
- package/dist/commands/pjm/remote/provider.d.ts +57 -0
- package/dist/commands/pjm/remote/provider.d.ts.map +1 -0
- package/dist/commands/pjm/remote/provider.js +28 -0
- package/dist/commands/pjm/remote/providers/github-publication-safety.d.ts +46 -0
- package/dist/commands/pjm/remote/providers/github-publication-safety.d.ts.map +1 -0
- package/dist/commands/pjm/remote/providers/github-publication-safety.js +126 -0
- package/dist/commands/pjm/remote/providers/github.d.ts +207 -0
- package/dist/commands/pjm/remote/providers/github.d.ts.map +1 -0
- package/dist/commands/pjm/remote/providers/github.js +1456 -0
- package/dist/commands/pjm/remote/providers/jira.d.ts +245 -0
- package/dist/commands/pjm/remote/providers/jira.d.ts.map +1 -0
- package/dist/commands/pjm/remote/providers/jira.js +1650 -0
- package/dist/commands/pjm/remote/providers/linear.d.ts +182 -0
- package/dist/commands/pjm/remote/providers/linear.d.ts.map +1 -0
- package/dist/commands/pjm/remote/providers/linear.js +1289 -0
- package/dist/commands/pjm/remote/purpose-policy.d.ts +29 -0
- package/dist/commands/pjm/remote/purpose-policy.d.ts.map +1 -0
- package/dist/commands/pjm/remote/purpose-policy.js +118 -0
- package/dist/commands/pjm/remote/reconcile.d.ts +42 -0
- package/dist/commands/pjm/remote/reconcile.d.ts.map +1 -0
- package/dist/commands/pjm/remote/reconcile.js +101 -0
- package/dist/commands/pjm/remote/resolution.d.ts +122 -0
- package/dist/commands/pjm/remote/resolution.d.ts.map +1 -0
- package/dist/commands/pjm/remote/resolution.js +243 -0
- package/dist/commands/pjm/remote/schema.d.ts +9309 -0
- package/dist/commands/pjm/remote/schema.d.ts.map +1 -0
- package/dist/commands/pjm/remote/schema.js +1503 -0
- package/dist/commands/pjm/remote/service.d.ts +20 -0
- package/dist/commands/pjm/remote/service.d.ts.map +1 -0
- package/dist/commands/pjm/remote/service.js +5121 -0
- package/dist/commands/pjm/remote/shared-storage.d.ts +36 -0
- package/dist/commands/pjm/remote/shared-storage.d.ts.map +1 -0
- package/dist/commands/pjm/remote/shared-storage.js +110 -0
- package/dist/commands/pjm/remote/snapshot.d.ts +32 -0
- package/dist/commands/pjm/remote/snapshot.d.ts.map +1 -0
- package/dist/commands/pjm/remote/snapshot.js +140 -0
- package/dist/commands/pjm/remote/storage-locator.d.ts +29 -0
- package/dist/commands/pjm/remote/storage-locator.d.ts.map +1 -0
- package/dist/commands/pjm/remote/storage-locator.js +62 -0
- package/dist/commands/pjm/remote/store.d.ts +85 -0
- package/dist/commands/pjm/remote/store.d.ts.map +1 -0
- package/dist/commands/pjm/remote/store.js +553 -0
- package/dist/commands/pjm/remote/verification.d.ts +40 -0
- package/dist/commands/pjm/remote/verification.d.ts.map +1 -0
- package/dist/commands/pjm/remote/verification.js +100 -0
- package/dist/commands/tools/shared/pack-manifest.d.ts.map +1 -1
- package/dist/commands/tools/shared/pack-manifest.js +1 -0
- package/dist/config/oat-config.d.ts +26 -0
- package/dist/config/oat-config.d.ts.map +1 -1
- package/dist/config/oat-config.js +186 -2
- package/package.json +2 -2
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
export const OPERATION_STATES = [
|
|
2
|
+
'planned',
|
|
3
|
+
'pending',
|
|
4
|
+
'authorized',
|
|
5
|
+
'attempt-started',
|
|
6
|
+
'verification-pending',
|
|
7
|
+
'blocked',
|
|
8
|
+
'verified',
|
|
9
|
+
'partial',
|
|
10
|
+
'uncertain',
|
|
11
|
+
'failed',
|
|
12
|
+
'rejected',
|
|
13
|
+
];
|
|
14
|
+
const VALID_TRANSITIONS = {
|
|
15
|
+
planned: new Set(['pending', 'authorized', 'blocked', 'failed']),
|
|
16
|
+
pending: new Set(['authorized', 'blocked', 'failed']),
|
|
17
|
+
authorized: new Set(['attempt-started', 'blocked', 'failed']),
|
|
18
|
+
'attempt-started': new Set([
|
|
19
|
+
'verification-pending',
|
|
20
|
+
'partial',
|
|
21
|
+
'uncertain',
|
|
22
|
+
'rejected',
|
|
23
|
+
]),
|
|
24
|
+
'verification-pending': new Set(['verified', 'partial', 'uncertain']),
|
|
25
|
+
blocked: new Set(),
|
|
26
|
+
verified: new Set(),
|
|
27
|
+
partial: new Set(),
|
|
28
|
+
uncertain: new Set(),
|
|
29
|
+
failed: new Set(),
|
|
30
|
+
rejected: new Set(),
|
|
31
|
+
};
|
|
32
|
+
const ATTEMPTED_STATES = new Set([
|
|
33
|
+
'attempt-started',
|
|
34
|
+
'verification-pending',
|
|
35
|
+
'verified',
|
|
36
|
+
'partial',
|
|
37
|
+
'uncertain',
|
|
38
|
+
'rejected',
|
|
39
|
+
]);
|
|
40
|
+
const ADVERSE_STATES = new Set([
|
|
41
|
+
'partial',
|
|
42
|
+
'uncertain',
|
|
43
|
+
'rejected',
|
|
44
|
+
'blocked',
|
|
45
|
+
'failed',
|
|
46
|
+
]);
|
|
47
|
+
export function transitionRemoteOperation(current, next, subject = 'parent') {
|
|
48
|
+
if (!VALID_TRANSITIONS[current].has(next)) {
|
|
49
|
+
throw new Error(`Invalid remote ${subject} transition from '${current}' to '${next}'.`);
|
|
50
|
+
}
|
|
51
|
+
return next;
|
|
52
|
+
}
|
|
53
|
+
export function reduceCompositeOperationState(substeps) {
|
|
54
|
+
if (substeps.length === 0) {
|
|
55
|
+
throw new Error('Composite operation reduction requires substeps.');
|
|
56
|
+
}
|
|
57
|
+
const ids = substeps.map((substep) => substep.substepId);
|
|
58
|
+
if (new Set(ids).size !== ids.length) {
|
|
59
|
+
throw new Error('Composite substep IDs must be unique.');
|
|
60
|
+
}
|
|
61
|
+
for (const [index, substep] of substeps.entries()) {
|
|
62
|
+
if (index > 0 &&
|
|
63
|
+
ATTEMPTED_STATES.has(substep.state) &&
|
|
64
|
+
substeps
|
|
65
|
+
.slice(0, index)
|
|
66
|
+
.some((dependency) => dependency.state !== 'verified')) {
|
|
67
|
+
throw new Error(`Composite substep '${substep.substepId}' attempted before its dependency verified.`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const verifiedSubstepIds = substeps
|
|
71
|
+
.filter((substep) => substep.state === 'verified')
|
|
72
|
+
.map((substep) => substep.substepId);
|
|
73
|
+
const allVerified = verifiedSubstepIds.length === substeps.length;
|
|
74
|
+
if (allVerified) {
|
|
75
|
+
return {
|
|
76
|
+
state: 'verified',
|
|
77
|
+
verifiedSubstepIds,
|
|
78
|
+
continuableSubstepId: null,
|
|
79
|
+
retryDisposition: 'not-applicable',
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
const states = new Set(substeps.map((substep) => substep.state));
|
|
83
|
+
let state;
|
|
84
|
+
if (states.has('partial') ||
|
|
85
|
+
(verifiedSubstepIds.length > 0 &&
|
|
86
|
+
substeps.some((substep) => ADVERSE_STATES.has(substep.state)))) {
|
|
87
|
+
state = 'partial';
|
|
88
|
+
}
|
|
89
|
+
else if (states.has('uncertain')) {
|
|
90
|
+
state = 'uncertain';
|
|
91
|
+
}
|
|
92
|
+
else if (states.has('rejected')) {
|
|
93
|
+
state = 'rejected';
|
|
94
|
+
}
|
|
95
|
+
else if (states.has('verification-pending')) {
|
|
96
|
+
state = 'verification-pending';
|
|
97
|
+
}
|
|
98
|
+
else if (states.has('attempt-started')) {
|
|
99
|
+
state = 'attempt-started';
|
|
100
|
+
}
|
|
101
|
+
else if (states.has('failed')) {
|
|
102
|
+
state = 'failed';
|
|
103
|
+
}
|
|
104
|
+
else if (states.has('blocked')) {
|
|
105
|
+
state = 'blocked';
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
const next = firstContinuableSubstep(substeps);
|
|
109
|
+
state = next?.state ?? 'planned';
|
|
110
|
+
}
|
|
111
|
+
const reconcileRequired = state === 'partial' ||
|
|
112
|
+
state === 'uncertain' ||
|
|
113
|
+
state === 'rejected' ||
|
|
114
|
+
state === 'attempt-started' ||
|
|
115
|
+
state === 'verification-pending';
|
|
116
|
+
const continuable = reconcileRequired
|
|
117
|
+
? null
|
|
118
|
+
: (firstContinuableSubstep(substeps)?.substepId ?? null);
|
|
119
|
+
return {
|
|
120
|
+
state,
|
|
121
|
+
verifiedSubstepIds,
|
|
122
|
+
continuableSubstepId: continuable,
|
|
123
|
+
retryDisposition: reconcileRequired
|
|
124
|
+
? 'reconcile-required'
|
|
125
|
+
: continuable
|
|
126
|
+
? 'safe-before-attempt'
|
|
127
|
+
: 'not-applicable',
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function firstContinuableSubstep(substeps) {
|
|
131
|
+
for (const [index, substep] of substeps.entries()) {
|
|
132
|
+
if (substep.state === 'verified')
|
|
133
|
+
continue;
|
|
134
|
+
if (!['planned', 'pending', 'authorized'].includes(substep.state) ||
|
|
135
|
+
substeps
|
|
136
|
+
.slice(0, index)
|
|
137
|
+
.some((dependency) => dependency.state !== 'verified')) {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
return substep;
|
|
141
|
+
}
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface OutboundProjection {
|
|
2
|
+
title?: string;
|
|
3
|
+
description?: string | null;
|
|
4
|
+
priority?: string | null;
|
|
5
|
+
status?: string;
|
|
6
|
+
annotation?: string;
|
|
7
|
+
sourceRevision?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface OutboundProjectionSafetyResult {
|
|
10
|
+
schemaVersion: 1;
|
|
11
|
+
projectionDigest: string;
|
|
12
|
+
verdict: 'safe' | 'blocked';
|
|
13
|
+
resultDigest: string;
|
|
14
|
+
reasons: Array<{
|
|
15
|
+
code: 'sensitive-content' | 'private-artifact' | 'invalid-projection';
|
|
16
|
+
field: keyof OutboundProjection | null;
|
|
17
|
+
}>;
|
|
18
|
+
assessedAt: string;
|
|
19
|
+
}
|
|
20
|
+
export declare function digestOutboundProjection(projection: OutboundProjection): string;
|
|
21
|
+
export declare function assessOutboundProjectionSafety(projection: OutboundProjection, options: {
|
|
22
|
+
assessedAt: string;
|
|
23
|
+
}): OutboundProjectionSafetyResult;
|
|
24
|
+
export declare function requireCurrentOutboundSafety(projection: OutboundProjection, result: OutboundProjectionSafetyResult | null | undefined): asserts result is OutboundProjectionSafetyResult;
|
|
25
|
+
//# sourceMappingURL=outbound-projection-safety.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outbound-projection-safety.d.ts","sourceRoot":"","sources":["../../../../src/commands/pjm/remote/outbound-projection-safety.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,8BAA8B;IAC7C,aAAa,EAAE,CAAC,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,KAAK,CAAC;QACb,IAAI,EAAE,mBAAmB,GAAG,kBAAkB,GAAG,oBAAoB,CAAC;QACtE,KAAK,EAAE,MAAM,kBAAkB,GAAG,IAAI,CAAC;KACxC,CAAC,CAAC;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAiBD,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,kBAAkB,GAC7B,MAAM,CAER;AAED,wBAAgB,8BAA8B,CAC5C,UAAU,EAAE,kBAAkB,EAC9B,OAAO,EAAE;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,GAC9B,8BAA8B,CA8ChC;AAED,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,kBAAkB,EAC9B,MAAM,EAAE,8BAA8B,GAAG,IAAI,GAAG,SAAS,GACxD,OAAO,CAAC,MAAM,IAAI,8BAA8B,CAiBlD"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { containsSensitiveContentSignalInValue } from './credential-safety.js';
|
|
3
|
+
const ALLOWED_FIELDS = new Set([
|
|
4
|
+
'title',
|
|
5
|
+
'description',
|
|
6
|
+
'priority',
|
|
7
|
+
'status',
|
|
8
|
+
'annotation',
|
|
9
|
+
'sourceRevision',
|
|
10
|
+
]);
|
|
11
|
+
const PRIVATE_ARTIFACT_MARKERS = [
|
|
12
|
+
'.oat/projects/',
|
|
13
|
+
'discovery.md',
|
|
14
|
+
'implementation.md',
|
|
15
|
+
'review artifact',
|
|
16
|
+
];
|
|
17
|
+
export function digestOutboundProjection(projection) {
|
|
18
|
+
return digest(projection);
|
|
19
|
+
}
|
|
20
|
+
export function assessOutboundProjectionSafety(projection, options) {
|
|
21
|
+
if (!Number.isFinite(Date.parse(options.assessedAt))) {
|
|
22
|
+
throw new Error('Outbound safety assessment requires a valid timestamp.');
|
|
23
|
+
}
|
|
24
|
+
const reasons = [];
|
|
25
|
+
for (const [rawField, value] of Object.entries(projection)) {
|
|
26
|
+
const field = rawField;
|
|
27
|
+
if (!ALLOWED_FIELDS.has(field) ||
|
|
28
|
+
(value !== null && typeof value !== 'string')) {
|
|
29
|
+
reasons.push({
|
|
30
|
+
code: 'invalid-projection',
|
|
31
|
+
field: ALLOWED_FIELDS.has(field) ? field : null,
|
|
32
|
+
});
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (containsSensitiveContentSignalInValue(value)) {
|
|
36
|
+
reasons.push({ code: 'sensitive-content', field });
|
|
37
|
+
}
|
|
38
|
+
if (typeof value === 'string' &&
|
|
39
|
+
PRIVATE_ARTIFACT_MARKERS.some((marker) => value.toLowerCase().includes(marker))) {
|
|
40
|
+
reasons.push({ code: 'private-artifact', field });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const projectionDigest = digest(projection);
|
|
44
|
+
const verdict = reasons.length === 0 ? 'safe' : 'blocked';
|
|
45
|
+
const resultDigest = digest({
|
|
46
|
+
schemaVersion: 1,
|
|
47
|
+
projectionDigest,
|
|
48
|
+
verdict,
|
|
49
|
+
reasons,
|
|
50
|
+
assessedAt: options.assessedAt,
|
|
51
|
+
});
|
|
52
|
+
return {
|
|
53
|
+
schemaVersion: 1,
|
|
54
|
+
projectionDigest,
|
|
55
|
+
verdict,
|
|
56
|
+
resultDigest,
|
|
57
|
+
reasons,
|
|
58
|
+
assessedAt: options.assessedAt,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export function requireCurrentOutboundSafety(projection, result) {
|
|
62
|
+
if (!result)
|
|
63
|
+
throw new Error('Outbound safety evidence is missing.');
|
|
64
|
+
if (result.verdict !== 'safe')
|
|
65
|
+
throw new Error('Outbound safety evidence blocks execution.');
|
|
66
|
+
if (result.projectionDigest !== digestOutboundProjection(projection)) {
|
|
67
|
+
throw new Error('Outbound safety evidence is stale or mismatched.');
|
|
68
|
+
}
|
|
69
|
+
const expectedResultDigest = digest({
|
|
70
|
+
schemaVersion: result.schemaVersion,
|
|
71
|
+
projectionDigest: result.projectionDigest,
|
|
72
|
+
verdict: result.verdict,
|
|
73
|
+
reasons: result.reasons,
|
|
74
|
+
assessedAt: result.assessedAt,
|
|
75
|
+
});
|
|
76
|
+
if (expectedResultDigest !== result.resultDigest) {
|
|
77
|
+
throw new Error('Outbound safety result digest is invalid.');
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function digest(value) {
|
|
81
|
+
return `sha256:${createHash('sha256')
|
|
82
|
+
.update(JSON.stringify(canonicalize(value)))
|
|
83
|
+
.digest('hex')}`;
|
|
84
|
+
}
|
|
85
|
+
function canonicalize(value) {
|
|
86
|
+
if (value === null || ['string', 'boolean'].includes(typeof value))
|
|
87
|
+
return value;
|
|
88
|
+
if (typeof value === 'number')
|
|
89
|
+
return value;
|
|
90
|
+
if (Array.isArray(value))
|
|
91
|
+
return value.map(canonicalize);
|
|
92
|
+
if (value && typeof value === 'object') {
|
|
93
|
+
return Object.fromEntries(Object.entries(value)
|
|
94
|
+
.filter(([, item]) => item !== undefined)
|
|
95
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
96
|
+
.map(([key, item]) => [key, canonicalize(item)]));
|
|
97
|
+
}
|
|
98
|
+
throw new Error('Outbound projection contains an unsupported value.');
|
|
99
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { DiscussionEvidence } from './discussion.js';
|
|
2
|
+
import type { ExternalActionEnvelope } from './external-action.js';
|
|
3
|
+
import type { BindingPreview } from './preview.js';
|
|
4
|
+
export type RemoteCommandStatus = 'ok' | 'pending' | 'needs-review' | 'partial' | 'uncertain' | 'blocked' | 'rejected' | 'failed';
|
|
5
|
+
export interface RemoteCommandEnvelope {
|
|
6
|
+
schemaVersion: 1;
|
|
7
|
+
status: RemoteCommandStatus;
|
|
8
|
+
operation: string;
|
|
9
|
+
projectRoot: string;
|
|
10
|
+
persisted: boolean;
|
|
11
|
+
results: Array<{
|
|
12
|
+
bindingId: string;
|
|
13
|
+
provider: 'github' | 'linear' | 'jira';
|
|
14
|
+
target: string;
|
|
15
|
+
status: RemoteCommandStatus;
|
|
16
|
+
freshness: string;
|
|
17
|
+
authority: string;
|
|
18
|
+
diagnosticCode: string | null;
|
|
19
|
+
}>;
|
|
20
|
+
externalAction: ExternalActionEnvelope | null;
|
|
21
|
+
discussionEvidence?: DiscussionEvidence;
|
|
22
|
+
approvalPreview?: {
|
|
23
|
+
operationId: string;
|
|
24
|
+
digest: string;
|
|
25
|
+
operationClass: BindingPreview['operationClass'];
|
|
26
|
+
fieldMask: BindingPreview['fieldMask'];
|
|
27
|
+
renderedFields: Partial<BindingPreview['renderedFields']>;
|
|
28
|
+
authority: string;
|
|
29
|
+
componentDigests: {
|
|
30
|
+
target: string;
|
|
31
|
+
baseline: string;
|
|
32
|
+
capability: string;
|
|
33
|
+
authority: string;
|
|
34
|
+
policy: string;
|
|
35
|
+
projection: string;
|
|
36
|
+
outboundSafety: string;
|
|
37
|
+
};
|
|
38
|
+
revision: {
|
|
39
|
+
digest: string;
|
|
40
|
+
evidenceDigest: string;
|
|
41
|
+
source: 'remote' | 'remote-unobserved' | 'local-source-unbound';
|
|
42
|
+
strength: 'token' | 'updated-at-and-hash' | 'hash-only' | 'unknown';
|
|
43
|
+
updatedAt: string | null;
|
|
44
|
+
observedAt: string | null;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
recovery: Array<{
|
|
48
|
+
code: string;
|
|
49
|
+
instruction: string;
|
|
50
|
+
}>;
|
|
51
|
+
}
|
|
52
|
+
export interface RenderedRemoteCommand {
|
|
53
|
+
stdout: string;
|
|
54
|
+
stderr: string;
|
|
55
|
+
exitCode: 0 | 1 | 2;
|
|
56
|
+
}
|
|
57
|
+
export declare function renderRemoteCommand(envelope: RemoteCommandEnvelope, options: {
|
|
58
|
+
json: boolean;
|
|
59
|
+
}): RenderedRemoteCommand;
|
|
60
|
+
//# sourceMappingURL=output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../../../src/commands/pjm/remote/output.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD,MAAM,MAAM,mBAAmB,GAC3B,IAAI,GACJ,SAAS,GACT,cAAc,GACd,SAAS,GACT,WAAW,GACX,SAAS,GACT,UAAU,GACV,QAAQ,CAAC;AAEb,MAAM,WAAW,qBAAqB;IACpC,aAAa,EAAE,CAAC,CAAC;IACjB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,KAAK,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;QACvC,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,mBAAmB,CAAC;QAC5B,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;QAClB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;KAC/B,CAAC,CAAC;IACH,cAAc,EAAE,sBAAsB,GAAG,IAAI,CAAC;IAC9C,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC,eAAe,CAAC,EAAE;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,cAAc,EAAE,cAAc,CAAC,gBAAgB,CAAC,CAAC;QACjD,SAAS,EAAE,cAAc,CAAC,WAAW,CAAC,CAAC;QACvC,cAAc,EAAE,OAAO,CAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC,CAAC;QAC1D,SAAS,EAAE,MAAM,CAAC;QAClB,gBAAgB,EAAE;YAChB,MAAM,EAAE,MAAM,CAAC;YACf,QAAQ,EAAE,MAAM,CAAC;YACjB,UAAU,EAAE,MAAM,CAAC;YACnB,SAAS,EAAE,MAAM,CAAC;YAClB,MAAM,EAAE,MAAM,CAAC;YACf,UAAU,EAAE,MAAM,CAAC;YACnB,cAAc,EAAE,MAAM,CAAC;SACxB,CAAC;QACF,QAAQ,EAAE;YACR,MAAM,EAAE,MAAM,CAAC;YACf,cAAc,EAAE,MAAM,CAAC;YACvB,MAAM,EAAE,QAAQ,GAAG,mBAAmB,GAAG,sBAAsB,CAAC;YAChE,QAAQ,EAAE,OAAO,GAAG,qBAAqB,GAAG,WAAW,GAAG,SAAS,CAAC;YACpE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;YACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;SAC3B,CAAC;KACH,CAAC;IACF,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACxD;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;CACrB;AAED,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,qBAAqB,EAC/B,OAAO,EAAE;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACzB,qBAAqB,CAyDvB"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export function renderRemoteCommand(envelope, options) {
|
|
2
|
+
validateEnvelope(envelope);
|
|
3
|
+
const exitCode = envelope.status === 'ok' ? 0 : envelope.status === 'failed' ? 2 : 1;
|
|
4
|
+
if (options.json) {
|
|
5
|
+
return { stdout: `${JSON.stringify(envelope)}\n`, stderr: '', exitCode };
|
|
6
|
+
}
|
|
7
|
+
const lines = [
|
|
8
|
+
`${envelope.operation}: ${envelope.status}`,
|
|
9
|
+
`persisted: ${envelope.persisted ? 'yes' : 'no'}`,
|
|
10
|
+
...envelope.results.map((result) => `${result.provider} ${result.target} [${result.bindingId}]: ${result.status}; freshness=${result.freshness}; authority=${result.authority}${result.diagnosticCode ? `; code=${result.diagnosticCode}` : ''}`),
|
|
11
|
+
...envelope.recovery.map((item) => `recovery ${item.code}: ${item.instruction}`),
|
|
12
|
+
];
|
|
13
|
+
if (envelope.externalAction)
|
|
14
|
+
lines.push('external action: durable handoff required');
|
|
15
|
+
if (envelope.discussionEvidence) {
|
|
16
|
+
lines.push(`discussion evidence: ${envelope.discussionEvidence.status}; items=${envelope.discussionEvidence.items.length}; pages=${envelope.discussionEvidence.pagesRead}; truncated=${String(envelope.discussionEvidence.truncated)}; persisted=${String(envelope.discussionEvidence.persisted)}`);
|
|
17
|
+
lines.push(...envelope.discussionEvidence.items.map((item) => `discussion item ${item.id}: ${item.body}${item.incomplete ? ' (incomplete)' : ''}`));
|
|
18
|
+
}
|
|
19
|
+
if (envelope.approvalPreview) {
|
|
20
|
+
const preview = envelope.approvalPreview;
|
|
21
|
+
lines.push(`preview ${preview.operationId}: ${preview.operationClass}; digest=${preview.digest}; fields=${preview.fieldMask.join(',')}; authority=${preview.authority}; revision=${preview.revision.digest}; revision-evidence=${preview.revision.evidenceDigest}; revision-source=${preview.revision.source}; strength=${preview.revision.strength}; updated=${preview.revision.updatedAt ?? 'unobserved'}; observed=${preview.revision.observedAt ?? 'unobserved'}`);
|
|
22
|
+
lines.push(`components target=${preview.componentDigests.target}; baseline=${preview.componentDigests.baseline}; capability=${preview.componentDigests.capability}; authority=${preview.componentDigests.authority}; policy=${preview.componentDigests.policy}; projection=${preview.componentDigests.projection}; safety=${preview.componentDigests.outboundSafety}`);
|
|
23
|
+
for (const field of preview.fieldMask) {
|
|
24
|
+
const rendered = preview.renderedFields[field];
|
|
25
|
+
if (!rendered) {
|
|
26
|
+
throw new Error(`Approval preview is missing rendered field '${field}'.`);
|
|
27
|
+
}
|
|
28
|
+
lines.push(rendered.kind === 'hash'
|
|
29
|
+
? `preview field ${field}: hash=${rendered.digest}; bytes=${rendered.bytes}`
|
|
30
|
+
: `preview field ${field}: value=${JSON.stringify(rendered.value)}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const rendered = `${lines.join('\n')}\n`;
|
|
34
|
+
return envelope.status === 'failed'
|
|
35
|
+
? { stdout: '', stderr: rendered, exitCode }
|
|
36
|
+
: { stdout: rendered, stderr: '', exitCode };
|
|
37
|
+
}
|
|
38
|
+
function validateEnvelope(envelope) {
|
|
39
|
+
if (envelope.schemaVersion !== 1)
|
|
40
|
+
throw new Error('Unsupported remote command envelope version.');
|
|
41
|
+
if (envelope.status === 'ok' && !envelope.persisted) {
|
|
42
|
+
throw new Error('A verified remote outcome must be durably persisted.');
|
|
43
|
+
}
|
|
44
|
+
if (envelope.externalAction &&
|
|
45
|
+
(!envelope.persisted || envelope.status !== 'pending')) {
|
|
46
|
+
throw new Error('An external action requires a persisted pending handoff.');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { OatPjmRemoteOperationClass, OatPjmRemoteProvider } from '../../../config/oat-config.js';
|
|
2
|
+
import { type OutboundProjectionSafetyResult } from './outbound-projection-safety.js';
|
|
3
|
+
import type { SharedRemoteField } from './purpose-policy.js';
|
|
4
|
+
export interface BuildBindingPreviewInput {
|
|
5
|
+
binding: {
|
|
6
|
+
bindingId: string;
|
|
7
|
+
provider: OatPjmRemoteProvider;
|
|
8
|
+
purposes: string[];
|
|
9
|
+
};
|
|
10
|
+
target: {
|
|
11
|
+
stableId: string;
|
|
12
|
+
context: Record<string, string>;
|
|
13
|
+
};
|
|
14
|
+
baseline: {
|
|
15
|
+
baselineId: string;
|
|
16
|
+
digest: string;
|
|
17
|
+
} | null;
|
|
18
|
+
revision: {
|
|
19
|
+
strength: string;
|
|
20
|
+
token: string | null;
|
|
21
|
+
updatedAt: string | null;
|
|
22
|
+
contentHash: string;
|
|
23
|
+
};
|
|
24
|
+
capability: {
|
|
25
|
+
surfaceKind: 'connector' | 'configured-cli';
|
|
26
|
+
evidenceDigest: string;
|
|
27
|
+
semanticCapabilities: string[];
|
|
28
|
+
context: Record<string, string>;
|
|
29
|
+
};
|
|
30
|
+
policy: Record<string, unknown>;
|
|
31
|
+
projection: {
|
|
32
|
+
title: string;
|
|
33
|
+
description: string | null;
|
|
34
|
+
priority: string | null;
|
|
35
|
+
};
|
|
36
|
+
outboundSafety: OutboundProjectionSafetyResult;
|
|
37
|
+
operationClass: OatPjmRemoteOperationClass;
|
|
38
|
+
fieldMask: SharedRemoteField[];
|
|
39
|
+
createdAt: string;
|
|
40
|
+
}
|
|
41
|
+
type RenderedField = {
|
|
42
|
+
kind: 'value';
|
|
43
|
+
value: string | null;
|
|
44
|
+
} | {
|
|
45
|
+
kind: 'hash';
|
|
46
|
+
digest: string;
|
|
47
|
+
bytes: number;
|
|
48
|
+
};
|
|
49
|
+
export interface BindingPreview {
|
|
50
|
+
schemaVersion: 1;
|
|
51
|
+
digest: string;
|
|
52
|
+
bindingId: string;
|
|
53
|
+
provider: OatPjmRemoteProvider;
|
|
54
|
+
operationClass: OatPjmRemoteOperationClass;
|
|
55
|
+
fieldMask: SharedRemoteField[];
|
|
56
|
+
createdAt: string;
|
|
57
|
+
componentDigests: {
|
|
58
|
+
target: string;
|
|
59
|
+
baseline: string;
|
|
60
|
+
revision: string;
|
|
61
|
+
capability: string;
|
|
62
|
+
policy: string;
|
|
63
|
+
projection: string;
|
|
64
|
+
outboundSafety: string;
|
|
65
|
+
};
|
|
66
|
+
renderedFields: Record<SharedRemoteField, RenderedField>;
|
|
67
|
+
}
|
|
68
|
+
export interface PreviewApproval {
|
|
69
|
+
previewDigest: string;
|
|
70
|
+
operationClass: OatPjmRemoteOperationClass;
|
|
71
|
+
approvedAt: string;
|
|
72
|
+
actor: string;
|
|
73
|
+
source: string;
|
|
74
|
+
}
|
|
75
|
+
export type ApprovalValidationReason = 'digest-mismatch' | 'operation-mismatch' | 'invalid-time' | 'approval-before-preview' | 'future-approval' | 'expired' | 'unsafe-evidence';
|
|
76
|
+
export declare function buildBindingPreview(input: BuildBindingPreviewInput): BindingPreview;
|
|
77
|
+
export declare function validatePreviewApproval(preview: BindingPreview, approval: PreviewApproval, options: {
|
|
78
|
+
now: string;
|
|
79
|
+
maxAgeMs: number;
|
|
80
|
+
}): {
|
|
81
|
+
valid: true;
|
|
82
|
+
reason: null;
|
|
83
|
+
} | {
|
|
84
|
+
valid: false;
|
|
85
|
+
reason: ApprovalValidationReason;
|
|
86
|
+
};
|
|
87
|
+
export {};
|
|
88
|
+
//# sourceMappingURL=preview.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../../../../src/commands/pjm/remote/preview.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { containsSensitiveContentSignal } from './credential-safety.js';
|
|
3
|
+
import { requireCurrentOutboundSafety, } from './outbound-projection-safety.js';
|
|
4
|
+
import { WHOLE_FIELD_SUPPRESSION_MARKER } from './schema.js';
|
|
5
|
+
const FIELD_ORDER = [
|
|
6
|
+
'title',
|
|
7
|
+
'description',
|
|
8
|
+
'priority',
|
|
9
|
+
];
|
|
10
|
+
export function buildBindingPreview(input) {
|
|
11
|
+
assertTimestamp(input.createdAt, 'preview creation');
|
|
12
|
+
if (canonicalJson(input.target.context) !==
|
|
13
|
+
canonicalJson(input.capability.context)) {
|
|
14
|
+
throw new Error('Preview target and capability contexts must match.');
|
|
15
|
+
}
|
|
16
|
+
const fieldMask = normalizeFieldMask(input.fieldMask);
|
|
17
|
+
requireCurrentOutboundSafety(input.projection, input.outboundSafety);
|
|
18
|
+
const componentDigests = {
|
|
19
|
+
target: hashCanonical(input.target),
|
|
20
|
+
baseline: hashCanonical(input.baseline),
|
|
21
|
+
revision: hashCanonical(input.revision),
|
|
22
|
+
capability: hashCanonical(input.capability),
|
|
23
|
+
policy: hashCanonical(input.policy),
|
|
24
|
+
projection: input.outboundSafety.projectionDigest,
|
|
25
|
+
outboundSafety: input.outboundSafety.resultDigest,
|
|
26
|
+
};
|
|
27
|
+
const digest = hashCanonical({
|
|
28
|
+
schemaVersion: 1,
|
|
29
|
+
binding: input.binding,
|
|
30
|
+
operationClass: input.operationClass,
|
|
31
|
+
fieldMask,
|
|
32
|
+
createdAt: input.createdAt,
|
|
33
|
+
componentDigests,
|
|
34
|
+
});
|
|
35
|
+
return {
|
|
36
|
+
schemaVersion: 1,
|
|
37
|
+
digest,
|
|
38
|
+
bindingId: input.binding.bindingId,
|
|
39
|
+
provider: input.binding.provider,
|
|
40
|
+
operationClass: input.operationClass,
|
|
41
|
+
fieldMask,
|
|
42
|
+
createdAt: input.createdAt,
|
|
43
|
+
componentDigests,
|
|
44
|
+
renderedFields: {
|
|
45
|
+
title: renderConcise(input.projection.title),
|
|
46
|
+
description: renderBody(input.projection.description),
|
|
47
|
+
priority: renderConcise(input.projection.priority),
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export function validatePreviewApproval(preview, approval, options) {
|
|
52
|
+
if (approval.previewDigest !== preview.digest) {
|
|
53
|
+
return { valid: false, reason: 'digest-mismatch' };
|
|
54
|
+
}
|
|
55
|
+
if (approval.operationClass !== preview.operationClass) {
|
|
56
|
+
return { valid: false, reason: 'operation-mismatch' };
|
|
57
|
+
}
|
|
58
|
+
const approvedAt = Date.parse(approval.approvedAt);
|
|
59
|
+
const previewCreatedAt = Date.parse(preview.createdAt);
|
|
60
|
+
const now = Date.parse(options.now);
|
|
61
|
+
if (!Number.isFinite(approvedAt) ||
|
|
62
|
+
!Number.isFinite(previewCreatedAt) ||
|
|
63
|
+
!Number.isFinite(now) ||
|
|
64
|
+
!Number.isFinite(options.maxAgeMs) ||
|
|
65
|
+
options.maxAgeMs < 0) {
|
|
66
|
+
return { valid: false, reason: 'invalid-time' };
|
|
67
|
+
}
|
|
68
|
+
if (approvedAt < previewCreatedAt) {
|
|
69
|
+
return { valid: false, reason: 'approval-before-preview' };
|
|
70
|
+
}
|
|
71
|
+
if (approvedAt > now)
|
|
72
|
+
return { valid: false, reason: 'future-approval' };
|
|
73
|
+
if (now - approvedAt > options.maxAgeMs) {
|
|
74
|
+
return { valid: false, reason: 'expired' };
|
|
75
|
+
}
|
|
76
|
+
if (!isSafeEvidence(approval.actor) || !isSafeEvidence(approval.source)) {
|
|
77
|
+
return { valid: false, reason: 'unsafe-evidence' };
|
|
78
|
+
}
|
|
79
|
+
return { valid: true, reason: null };
|
|
80
|
+
}
|
|
81
|
+
function normalizeFieldMask(fieldMask) {
|
|
82
|
+
const unique = new Set(fieldMask);
|
|
83
|
+
if (unique.size !== fieldMask.length || fieldMask.length === 0) {
|
|
84
|
+
throw new Error('Preview field mask must be non-empty and unique.');
|
|
85
|
+
}
|
|
86
|
+
for (const field of unique) {
|
|
87
|
+
if (!FIELD_ORDER.includes(field)) {
|
|
88
|
+
throw new Error(`Unsupported preview field '${String(field)}'.`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return FIELD_ORDER.filter((field) => unique.has(field));
|
|
92
|
+
}
|
|
93
|
+
function renderConcise(value) {
|
|
94
|
+
if (value === null)
|
|
95
|
+
return { kind: 'value', value: null };
|
|
96
|
+
if (containsSensitiveContentSignal(value)) {
|
|
97
|
+
return { kind: 'value', value: WHOLE_FIELD_SUPPRESSION_MARKER };
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
kind: 'value',
|
|
101
|
+
value: value.length > 160 ? `${value.slice(0, 157)}...` : value,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function renderBody(value) {
|
|
105
|
+
if (value === null)
|
|
106
|
+
return { kind: 'value', value: null };
|
|
107
|
+
return {
|
|
108
|
+
kind: 'hash',
|
|
109
|
+
digest: hashText(value),
|
|
110
|
+
bytes: Buffer.byteLength(value, 'utf8'),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function isSafeEvidence(value) {
|
|
114
|
+
return (value.length > 0 &&
|
|
115
|
+
value.length <= 255 &&
|
|
116
|
+
!/[\r\n]/.test(value) &&
|
|
117
|
+
!value.includes('\0') &&
|
|
118
|
+
!containsSensitiveContentSignal(value));
|
|
119
|
+
}
|
|
120
|
+
function hashCanonical(value) {
|
|
121
|
+
return hashText(canonicalJson(value));
|
|
122
|
+
}
|
|
123
|
+
function hashText(value) {
|
|
124
|
+
return `sha256:${createHash('sha256').update(value).digest('hex')}`;
|
|
125
|
+
}
|
|
126
|
+
function canonicalJson(value) {
|
|
127
|
+
return JSON.stringify(canonicalize(value));
|
|
128
|
+
}
|
|
129
|
+
function canonicalize(value) {
|
|
130
|
+
if (value === null ||
|
|
131
|
+
typeof value === 'string' ||
|
|
132
|
+
typeof value === 'boolean') {
|
|
133
|
+
return value;
|
|
134
|
+
}
|
|
135
|
+
if (typeof value === 'number') {
|
|
136
|
+
if (!Number.isFinite(value))
|
|
137
|
+
throw new Error('Preview inputs must be finite.');
|
|
138
|
+
return value;
|
|
139
|
+
}
|
|
140
|
+
if (Array.isArray(value))
|
|
141
|
+
return value.map(canonicalize);
|
|
142
|
+
if (value && typeof value === 'object') {
|
|
143
|
+
return Object.fromEntries(Object.entries(value)
|
|
144
|
+
.filter(([, entry]) => entry !== undefined)
|
|
145
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
146
|
+
.map(([key, entry]) => [key, canonicalize(entry)]));
|
|
147
|
+
}
|
|
148
|
+
throw new Error(`Unsupported preview input type '${typeof value}'.`);
|
|
149
|
+
}
|
|
150
|
+
function assertTimestamp(value, label) {
|
|
151
|
+
if (!Number.isFinite(Date.parse(value))) {
|
|
152
|
+
throw new Error(`Invalid ${label} time '${value}'.`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ProviderAdapter, type SanitizedProviderObservation, type SemanticAction } from './provider.js';
|
|
2
|
+
export interface ProviderConformanceFixture {
|
|
3
|
+
adapter: ProviderAdapter;
|
|
4
|
+
observation: SanitizedProviderObservation;
|
|
5
|
+
expected: {
|
|
6
|
+
stableId: string;
|
|
7
|
+
aliases: string[];
|
|
8
|
+
verificationFields: string[];
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export declare function evaluateProviderConformance(fixture: ProviderConformanceFixture): string[];
|
|
12
|
+
export declare function validateSemanticObservation(action: SemanticAction, observation: SanitizedProviderObservation): string[];
|
|
13
|
+
//# sourceMappingURL=provider-conformance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-conformance.d.ts","sourceRoot":"","sources":["../../../../src/commands/pjm/remote/provider-conformance.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,4BAA4B,EACjC,KAAK,cAAc,EACpB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,eAAe,CAAC;IACzB,WAAW,EAAE,4BAA4B,CAAC;IAC1C,QAAQ,EAAE;QACR,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,kBAAkB,EAAE,MAAM,EAAE,CAAC;KAC9B,CAAC;CACH;AAED,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,0BAA0B,GAClC,MAAM,EAAE,CAkCV;AAED,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,cAAc,EACtB,WAAW,EAAE,4BAA4B,GACxC,MAAM,EAAE,CASV"}
|