@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,208 @@
|
|
|
1
|
+
import { buildReviewedBatch, reduceReviewedBatchOutcomes } from './batch.js';
|
|
2
|
+
import { semanticDigest } from './provider.js';
|
|
3
|
+
import { composePurposePolicies } from './purpose-policy.js';
|
|
4
|
+
export async function closeoutBindings(input, store) {
|
|
5
|
+
assertUniqueBindings(input.plans);
|
|
6
|
+
const operations = [];
|
|
7
|
+
for (const plan of input.plans) {
|
|
8
|
+
const operation = buildCloseoutJournal(plan, input.projectPath, input.now);
|
|
9
|
+
await store.writeOperation(operation);
|
|
10
|
+
operations.push(operation);
|
|
11
|
+
}
|
|
12
|
+
let batch = buildReviewedBatch({
|
|
13
|
+
batchId: input.batchId,
|
|
14
|
+
lifecycleOperation: 'closeout',
|
|
15
|
+
members: operations.map((operation) => ({
|
|
16
|
+
bindingId: operation.bindingId,
|
|
17
|
+
operationId: operation.operationId,
|
|
18
|
+
bindingPreviewDigest: operation.previewDigest,
|
|
19
|
+
})),
|
|
20
|
+
authority: {
|
|
21
|
+
effective: strictestAuthority(operations.flatMap((operation) => operation.substeps.map((step) => step.authority))),
|
|
22
|
+
sourceDigest: semanticDigest(operations.flatMap((operation) => operation.substeps.map((step) => step.authoritySourceDigest))),
|
|
23
|
+
},
|
|
24
|
+
approval: input.approval,
|
|
25
|
+
createdAt: input.now,
|
|
26
|
+
});
|
|
27
|
+
batch = reduceReviewedBatchOutcomes(batch, Object.fromEntries(operations.map((operation) => [operation.operationId, operation.state])), input.now);
|
|
28
|
+
await store.writeBatch(batch);
|
|
29
|
+
return { batch, operations };
|
|
30
|
+
}
|
|
31
|
+
export async function resumeCloseoutOperation(journal, store, executeStep, crash, now = () => new Date().toISOString(), authorization) {
|
|
32
|
+
let current = cloneJournal(journal);
|
|
33
|
+
for (let index = 0; index < current.substeps.length; index += 1) {
|
|
34
|
+
const step = current.substeps[index];
|
|
35
|
+
if (step.state === 'verified')
|
|
36
|
+
continue;
|
|
37
|
+
if ([
|
|
38
|
+
'attempt-started',
|
|
39
|
+
'verification-pending',
|
|
40
|
+
'partial',
|
|
41
|
+
'uncertain',
|
|
42
|
+
'rejected',
|
|
43
|
+
].includes(step.state)) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (step.state === 'blocked' || step.state === 'failed')
|
|
47
|
+
continue;
|
|
48
|
+
const dependenciesVerified = step.dependsOn.every((dependencyId) => current.substeps.some((candidate) => candidate.stepId === dependencyId && candidate.state === 'verified'));
|
|
49
|
+
if (!dependenciesVerified)
|
|
50
|
+
continue;
|
|
51
|
+
assertStepAuthorized(step, authorization, now());
|
|
52
|
+
crash?.(`before-${step.kind}`);
|
|
53
|
+
current.substeps[index] = { ...step, state: 'attempt-started' };
|
|
54
|
+
current = withReducedState(current, now());
|
|
55
|
+
await store.writeOperation(current);
|
|
56
|
+
const outcome = await executeStep(current.substeps[index]);
|
|
57
|
+
current.substeps[index] = {
|
|
58
|
+
...current.substeps[index],
|
|
59
|
+
state: outcome,
|
|
60
|
+
};
|
|
61
|
+
current = withReducedState(current, now());
|
|
62
|
+
await store.writeOperation(current);
|
|
63
|
+
crash?.(`after-${step.kind}`);
|
|
64
|
+
}
|
|
65
|
+
current = withReducedState(current, now());
|
|
66
|
+
await store.writeOperation(current);
|
|
67
|
+
return current;
|
|
68
|
+
}
|
|
69
|
+
function assertStepAuthorized(step, authorization, currentTime) {
|
|
70
|
+
if (step.approvalRequirement === 'none')
|
|
71
|
+
return;
|
|
72
|
+
const age = authorization
|
|
73
|
+
? Date.parse(currentTime) - Date.parse(authorization.authorizedAt)
|
|
74
|
+
: Number.POSITIVE_INFINITY;
|
|
75
|
+
const expectedKind = step.approvalRequirement === 'fresh-approval' ? 'approval' : 'instruction';
|
|
76
|
+
if (!authorization ||
|
|
77
|
+
authorization.kind !== expectedKind ||
|
|
78
|
+
authorization.previewDigest !== step.previewDigest ||
|
|
79
|
+
!authorization.source ||
|
|
80
|
+
!Number.isFinite(age) ||
|
|
81
|
+
age < 0 ||
|
|
82
|
+
age > 5 * 60 * 1_000) {
|
|
83
|
+
throw new Error(`Closeout ${step.approvalRequirement} must authorize the exact current composite preview.`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function withReducedState(journal, updatedAt) {
|
|
87
|
+
return {
|
|
88
|
+
...journal,
|
|
89
|
+
state: reduceCloseoutState(journal.substeps),
|
|
90
|
+
updatedAt,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function cloneJournal(journal) {
|
|
94
|
+
return {
|
|
95
|
+
...journal,
|
|
96
|
+
substeps: journal.substeps.map((step) => ({
|
|
97
|
+
...step,
|
|
98
|
+
dependsOn: [...step.dependsOn],
|
|
99
|
+
})),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function buildCloseoutJournal(plan, projectPath, now) {
|
|
103
|
+
if (plan.providerAutomation && plan.transition) {
|
|
104
|
+
throw new Error(`Binding '${plan.bindingId}' cannot combine provider automation with an OAT transition.`);
|
|
105
|
+
}
|
|
106
|
+
const purposes = new Set(plan.purposes);
|
|
107
|
+
const composed = composePurposePolicies([...purposes]);
|
|
108
|
+
const substeps = [];
|
|
109
|
+
if (plan.annotation &&
|
|
110
|
+
composed.closeout.annotation === 'propose' &&
|
|
111
|
+
composed.lifecycle.includes('annotate')) {
|
|
112
|
+
substeps.push(buildSubstep(plan, 'annotation', plan.annotation, []));
|
|
113
|
+
}
|
|
114
|
+
if (plan.transition &&
|
|
115
|
+
composed.closeout.transition === 'propose' &&
|
|
116
|
+
composed.lifecycle.includes('transition')) {
|
|
117
|
+
substeps.push(buildSubstep(plan, 'transition', plan.transition, substeps.map((step) => step.stepId)));
|
|
118
|
+
}
|
|
119
|
+
const state = plan.providerAutomation &&
|
|
120
|
+
composed.closeout.transition === 'provider-automation'
|
|
121
|
+
? 'verified'
|
|
122
|
+
: substeps.length === 0
|
|
123
|
+
? 'blocked'
|
|
124
|
+
: reduceCloseoutState(substeps);
|
|
125
|
+
const previewDigest = semanticDigest({
|
|
126
|
+
bindingId: plan.bindingId,
|
|
127
|
+
operationId: plan.operationId,
|
|
128
|
+
provider: plan.provider,
|
|
129
|
+
projectPath,
|
|
130
|
+
purposes: [...purposes].sort(),
|
|
131
|
+
providerAutomation: plan.providerAutomation ?? false,
|
|
132
|
+
substeps,
|
|
133
|
+
});
|
|
134
|
+
return {
|
|
135
|
+
schemaVersion: 1,
|
|
136
|
+
operationId: plan.operationId,
|
|
137
|
+
bindingId: plan.bindingId,
|
|
138
|
+
provider: plan.provider,
|
|
139
|
+
projectPath,
|
|
140
|
+
previewDigest,
|
|
141
|
+
state,
|
|
142
|
+
substeps,
|
|
143
|
+
createdAt: now,
|
|
144
|
+
updatedAt: now,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function buildSubstep(plan, kind, step, dependsOn) {
|
|
148
|
+
return {
|
|
149
|
+
stepId: `${plan.operationId}_${kind}`,
|
|
150
|
+
kind,
|
|
151
|
+
state: step.authority === 'read-only' ? 'blocked' : 'pending',
|
|
152
|
+
previewDigest: step.previewDigest,
|
|
153
|
+
authority: step.authority,
|
|
154
|
+
authoritySourceDigest: step.sourceDigest,
|
|
155
|
+
approvalRequirement: step.authority === 'user-approved'
|
|
156
|
+
? 'fresh-approval'
|
|
157
|
+
: step.authority === 'user-authorized'
|
|
158
|
+
? 'explicit-instruction'
|
|
159
|
+
: 'none',
|
|
160
|
+
dependsOn,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
export function reduceCloseoutState(substeps) {
|
|
164
|
+
const states = substeps.map((step) => step.state);
|
|
165
|
+
if (states.every((state) => state === 'verified'))
|
|
166
|
+
return 'verified';
|
|
167
|
+
if (states.includes('partial') ||
|
|
168
|
+
(states.includes('verified') &&
|
|
169
|
+
states.some((state) => state !== 'verified'))) {
|
|
170
|
+
return 'partial';
|
|
171
|
+
}
|
|
172
|
+
if (states.includes('uncertain'))
|
|
173
|
+
return 'uncertain';
|
|
174
|
+
if (states.some((state) => state === 'attempt-started'))
|
|
175
|
+
return 'attempt-started';
|
|
176
|
+
if (states.some((state) => state === 'verification-pending'))
|
|
177
|
+
return 'verification-pending';
|
|
178
|
+
if (states.some((state) => state === 'pending'))
|
|
179
|
+
return 'pending';
|
|
180
|
+
if (states.some((state) => state === 'authorized'))
|
|
181
|
+
return 'authorized';
|
|
182
|
+
if (states.every((state) => state === 'blocked'))
|
|
183
|
+
return 'blocked';
|
|
184
|
+
if (states.some((state) => state === 'rejected'))
|
|
185
|
+
return 'rejected';
|
|
186
|
+
return 'failed';
|
|
187
|
+
}
|
|
188
|
+
function strictestAuthority(authorities) {
|
|
189
|
+
const rank = {
|
|
190
|
+
'read-only': 0,
|
|
191
|
+
'user-approved': 1,
|
|
192
|
+
'user-authorized': 2,
|
|
193
|
+
autonomous: 3,
|
|
194
|
+
};
|
|
195
|
+
return authorities.reduce((strictest, authority) => rank[authority] < rank[strictest] ? authority : strictest, 'autonomous');
|
|
196
|
+
}
|
|
197
|
+
function assertUniqueBindings(plans) {
|
|
198
|
+
const bindingIds = new Set();
|
|
199
|
+
const operationIds = new Set();
|
|
200
|
+
for (const plan of plans) {
|
|
201
|
+
if (bindingIds.has(plan.bindingId))
|
|
202
|
+
throw new Error(`Duplicate closeout binding '${plan.bindingId}'.`);
|
|
203
|
+
if (operationIds.has(plan.operationId))
|
|
204
|
+
throw new Error(`Duplicate closeout operation '${plan.operationId}'.`);
|
|
205
|
+
bindingIds.add(plan.bindingId);
|
|
206
|
+
operationIds.add(plan.operationId);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { type ExternalActionEnvelope } from './external-action.js';
|
|
2
|
+
import { type OutboundProjection, type OutboundProjectionSafetyResult } from './outbound-projection-safety.js';
|
|
3
|
+
export interface BindingCreateIntent {
|
|
4
|
+
bindingId: string;
|
|
5
|
+
operationId: string;
|
|
6
|
+
provider: 'github' | 'linear' | 'jira';
|
|
7
|
+
context: Record<string, string>;
|
|
8
|
+
target: {
|
|
9
|
+
kind: 'backlog' | 'project';
|
|
10
|
+
id: string;
|
|
11
|
+
scope: 'shared' | 'synced' | 'local';
|
|
12
|
+
};
|
|
13
|
+
projection: OutboundProjection;
|
|
14
|
+
previewDigest: string;
|
|
15
|
+
projectionDigest: string;
|
|
16
|
+
safetyResultDigest: string;
|
|
17
|
+
capabilityEvidenceDigest: string;
|
|
18
|
+
provenanceToken: string;
|
|
19
|
+
}
|
|
20
|
+
export interface CreateBindingDependencies {
|
|
21
|
+
reserveIntent(intent: BindingCreateIntent): Promise<void>;
|
|
22
|
+
readIntent(operationId: string): Promise<BindingCreateIntent | null>;
|
|
23
|
+
readAction(operationId: string): Promise<ExternalActionEnvelope | null>;
|
|
24
|
+
recordAction(operationId: string, action: ExternalActionEnvelope): Promise<void>;
|
|
25
|
+
recordVerificationPending(operationId: string, action: ExternalActionEnvelope): Promise<void>;
|
|
26
|
+
recordTerminal(operationId: string, state: 'rejected' | 'uncertain'): Promise<void>;
|
|
27
|
+
materialize(input: {
|
|
28
|
+
intent: BindingCreateIntent;
|
|
29
|
+
stableId: string;
|
|
30
|
+
aliases: string[];
|
|
31
|
+
observedFields: Record<string, string | number | boolean | null>;
|
|
32
|
+
revisionDigest: string;
|
|
33
|
+
}): Promise<void>;
|
|
34
|
+
writeAssociation(input: {
|
|
35
|
+
target: BindingCreateIntent['target'];
|
|
36
|
+
provider: string;
|
|
37
|
+
ref: string;
|
|
38
|
+
bindingId: string;
|
|
39
|
+
}): Promise<void>;
|
|
40
|
+
readMaterializationProgress?(operationId: string): Promise<Array<'materialized' | 'associated' | 'terminal'>>;
|
|
41
|
+
recordMaterializationProgress?(operationId: string, step: 'materialized' | 'associated' | 'terminal'): Promise<void>;
|
|
42
|
+
recordVerified?(operationId: string): Promise<void>;
|
|
43
|
+
crash?(point: 'after-intent' | 'after-observation' | 'after-materialize' | 'after-association' | 'after-terminal'): void;
|
|
44
|
+
}
|
|
45
|
+
export type CreateBindingResult = {
|
|
46
|
+
status: 'pending';
|
|
47
|
+
action: ExternalActionEnvelope;
|
|
48
|
+
} | {
|
|
49
|
+
status: 'verified';
|
|
50
|
+
bindingId: string;
|
|
51
|
+
} | {
|
|
52
|
+
status: 'rejected' | 'uncertain';
|
|
53
|
+
bindingId: null;
|
|
54
|
+
};
|
|
55
|
+
export declare function createAndBindRemoteIssue(input: {
|
|
56
|
+
intent: BindingCreateIntent;
|
|
57
|
+
safety: OutboundProjectionSafetyResult | null;
|
|
58
|
+
continuation?: {
|
|
59
|
+
observation: unknown;
|
|
60
|
+
};
|
|
61
|
+
}, dependencies: CreateBindingDependencies): Promise<CreateBindingResult>;
|
|
62
|
+
//# sourceMappingURL=create-binding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-binding.d.ts","sourceRoot":"","sources":["../../../../src/commands/pjm/remote/create-binding.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { acceptExternalObservation, buildExternalAction, } from './external-action.js';
|
|
2
|
+
import { requireCurrentOutboundSafety, } from './outbound-projection-safety.js';
|
|
3
|
+
export async function createAndBindRemoteIssue(input, dependencies) {
|
|
4
|
+
if (!input.continuation) {
|
|
5
|
+
await dependencies.reserveIntent(input.intent);
|
|
6
|
+
dependencies.crash?.('after-intent');
|
|
7
|
+
requireCurrentOutboundSafety(input.intent.projection, input.safety);
|
|
8
|
+
if (input.intent.projectionDigest !== input.safety.projectionDigest ||
|
|
9
|
+
input.intent.safetyResultDigest !== input.safety.resultDigest) {
|
|
10
|
+
throw new Error('Create intent safety evidence is stale or mismatched.');
|
|
11
|
+
}
|
|
12
|
+
const action = buildExternalAction({
|
|
13
|
+
operationId: input.intent.operationId,
|
|
14
|
+
stepId: `step_${input.intent.operationId}`,
|
|
15
|
+
provider: input.intent.provider,
|
|
16
|
+
semanticOperation: 'create',
|
|
17
|
+
context: input.intent.context,
|
|
18
|
+
intent: {
|
|
19
|
+
target: input.intent.target,
|
|
20
|
+
fields: input.intent.projection,
|
|
21
|
+
provenanceToken: input.intent.provenanceToken,
|
|
22
|
+
},
|
|
23
|
+
expectedObservation: {
|
|
24
|
+
fields: Object.keys(input.intent.projection),
|
|
25
|
+
requireIdentity: true,
|
|
26
|
+
stableId: null,
|
|
27
|
+
capabilityEvidenceDigest: input.intent.capabilityEvidenceDigest,
|
|
28
|
+
},
|
|
29
|
+
persistedPreview: input.intent,
|
|
30
|
+
projection: input.intent.projection,
|
|
31
|
+
outboundSafety: input.safety,
|
|
32
|
+
});
|
|
33
|
+
await dependencies.recordAction(input.intent.operationId, action);
|
|
34
|
+
return { status: 'pending', action };
|
|
35
|
+
}
|
|
36
|
+
const progress = new Set((await dependencies.readMaterializationProgress?.(input.intent.operationId)) ?? []);
|
|
37
|
+
if (progress.has('terminal')) {
|
|
38
|
+
throw new Error('Create operation is terminal; observation replay rejected.');
|
|
39
|
+
}
|
|
40
|
+
const persisted = await dependencies.readIntent(input.intent.operationId);
|
|
41
|
+
if (!persisted ||
|
|
42
|
+
JSON.stringify(persisted) !== JSON.stringify(input.intent)) {
|
|
43
|
+
throw new Error('Create continuation does not match the persisted intent.');
|
|
44
|
+
}
|
|
45
|
+
const durableAction = await dependencies.readAction(input.intent.operationId);
|
|
46
|
+
if (!durableAction) {
|
|
47
|
+
throw new Error('Create continuation has no durable current action.');
|
|
48
|
+
}
|
|
49
|
+
const observation = acceptExternalObservation({
|
|
50
|
+
action: durableAction,
|
|
51
|
+
observation: input.continuation.observation,
|
|
52
|
+
});
|
|
53
|
+
if (observation.outcome.classification === 'rejected') {
|
|
54
|
+
await dependencies.recordTerminal(input.intent.operationId, 'rejected');
|
|
55
|
+
return { status: 'rejected', bindingId: null };
|
|
56
|
+
}
|
|
57
|
+
if (observation.outcome.classification !== 'observed' ||
|
|
58
|
+
!observation.outcome.identity) {
|
|
59
|
+
await dependencies.recordTerminal(input.intent.operationId, 'uncertain');
|
|
60
|
+
return { status: 'uncertain', bindingId: null };
|
|
61
|
+
}
|
|
62
|
+
dependencies.crash?.('after-observation');
|
|
63
|
+
if (durableAction.semanticOperation === 'create') {
|
|
64
|
+
if (observation.outcome.classification !== 'observed' ||
|
|
65
|
+
!observation.outcome.identity) {
|
|
66
|
+
await dependencies.recordTerminal(input.intent.operationId, 'uncertain');
|
|
67
|
+
return { status: 'uncertain', bindingId: null };
|
|
68
|
+
}
|
|
69
|
+
const readAction = buildExternalAction({
|
|
70
|
+
operationId: input.intent.operationId,
|
|
71
|
+
stepId: `verify_${input.intent.operationId}`,
|
|
72
|
+
provider: input.intent.provider,
|
|
73
|
+
semanticOperation: 'read',
|
|
74
|
+
context: input.intent.context,
|
|
75
|
+
intent: { stableId: observation.outcome.identity.stableId },
|
|
76
|
+
expectedObservation: {
|
|
77
|
+
fields: Object.keys(input.intent.projection),
|
|
78
|
+
requireIdentity: true,
|
|
79
|
+
stableId: observation.outcome.identity.stableId,
|
|
80
|
+
capabilityEvidenceDigest: input.intent.capabilityEvidenceDigest,
|
|
81
|
+
},
|
|
82
|
+
persistedPreview: {},
|
|
83
|
+
});
|
|
84
|
+
await dependencies.recordVerificationPending(input.intent.operationId, readAction);
|
|
85
|
+
return { status: 'pending', action: readAction };
|
|
86
|
+
}
|
|
87
|
+
if (durableAction.semanticOperation !== 'read') {
|
|
88
|
+
throw new Error('Create continuation durable action is not current.');
|
|
89
|
+
}
|
|
90
|
+
for (const [field, expected] of Object.entries(input.intent.projection)) {
|
|
91
|
+
if (observation.outcome.fields[field] !== expected) {
|
|
92
|
+
await dependencies.recordTerminal(input.intent.operationId, 'uncertain');
|
|
93
|
+
return { status: 'uncertain', bindingId: null };
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (!observation.outcome.revisionDigest) {
|
|
97
|
+
await dependencies.recordTerminal(input.intent.operationId, 'uncertain');
|
|
98
|
+
return { status: 'uncertain', bindingId: null };
|
|
99
|
+
}
|
|
100
|
+
if (!progress.has('materialized')) {
|
|
101
|
+
await dependencies.materialize({
|
|
102
|
+
intent: input.intent,
|
|
103
|
+
stableId: observation.outcome.identity.stableId,
|
|
104
|
+
aliases: observation.outcome.identity.aliases,
|
|
105
|
+
observedFields: observation.outcome.fields,
|
|
106
|
+
revisionDigest: observation.outcome.revisionDigest,
|
|
107
|
+
});
|
|
108
|
+
await dependencies.recordMaterializationProgress?.(input.intent.operationId, 'materialized');
|
|
109
|
+
dependencies.crash?.('after-materialize');
|
|
110
|
+
}
|
|
111
|
+
if (!progress.has('associated')) {
|
|
112
|
+
await dependencies.writeAssociation({
|
|
113
|
+
target: input.intent.target,
|
|
114
|
+
provider: input.intent.provider,
|
|
115
|
+
ref: observation.outcome.identity.aliases[0] ??
|
|
116
|
+
observation.outcome.identity.stableId,
|
|
117
|
+
bindingId: input.intent.bindingId,
|
|
118
|
+
});
|
|
119
|
+
await dependencies.recordMaterializationProgress?.(input.intent.operationId, 'associated');
|
|
120
|
+
dependencies.crash?.('after-association');
|
|
121
|
+
}
|
|
122
|
+
await dependencies.recordVerified?.(input.intent.operationId);
|
|
123
|
+
await dependencies.recordMaterializationProgress?.(input.intent.operationId, 'terminal');
|
|
124
|
+
dependencies.crash?.('after-terminal');
|
|
125
|
+
return { status: 'verified', bindingId: input.intent.bindingId };
|
|
126
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detects a deliberately small, conservative signal in one caller-approved
|
|
3
|
+
* text field. This is not credential-value parsing or a general DLP check.
|
|
4
|
+
*/
|
|
5
|
+
export declare function containsSensitiveContentSignal(value: string): boolean;
|
|
6
|
+
export declare function containsSensitiveContentSignalInValue(value: unknown): boolean;
|
|
7
|
+
//# sourceMappingURL=credential-safety.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential-safety.d.ts","sourceRoot":"","sources":["../../../../src/commands/pjm/remote/credential-safety.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAErE;AAED,wBAAgB,qCAAqC,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAe7E"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const SENSITIVE_CONTENT_SIGNAL = /(?<![A-Za-z0-9_-])(?:password|passwd|api(?:[ _-]+)key|access(?:[ _-]+)token|authorization|secret|token)(?![A-Za-z0-9_-])/i;
|
|
2
|
+
/**
|
|
3
|
+
* Detects a deliberately small, conservative signal in one caller-approved
|
|
4
|
+
* text field. This is not credential-value parsing or a general DLP check.
|
|
5
|
+
*/
|
|
6
|
+
export function containsSensitiveContentSignal(value) {
|
|
7
|
+
return SENSITIVE_CONTENT_SIGNAL.test(value);
|
|
8
|
+
}
|
|
9
|
+
export function containsSensitiveContentSignalInValue(value) {
|
|
10
|
+
if (typeof value === 'string') {
|
|
11
|
+
return containsSensitiveContentSignal(value);
|
|
12
|
+
}
|
|
13
|
+
if (Array.isArray(value)) {
|
|
14
|
+
return value.some((entry) => containsSensitiveContentSignalInValue(entry));
|
|
15
|
+
}
|
|
16
|
+
if (value && typeof value === 'object') {
|
|
17
|
+
return Object.entries(value).some(([key, entry]) => containsSensitiveContentSignal(key) ||
|
|
18
|
+
containsSensitiveContentSignalInValue(entry));
|
|
19
|
+
}
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { ExternalActionEnvelope } from './external-action.js';
|
|
2
|
+
export interface RemoteDiscussionItem {
|
|
3
|
+
id: string;
|
|
4
|
+
body: string;
|
|
5
|
+
createdAt: string;
|
|
6
|
+
}
|
|
7
|
+
export interface SanitizedDiscussionItem extends RemoteDiscussionItem {
|
|
8
|
+
incomplete: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface DiscussionPage {
|
|
11
|
+
items: RemoteDiscussionItem[];
|
|
12
|
+
nextCursor: string | null;
|
|
13
|
+
}
|
|
14
|
+
export interface DiscussionPageSource {
|
|
15
|
+
readPage(input: {
|
|
16
|
+
bindingId: string;
|
|
17
|
+
cursor: string | null;
|
|
18
|
+
limit: number;
|
|
19
|
+
}): Promise<DiscussionPage>;
|
|
20
|
+
}
|
|
21
|
+
export interface DiscussionEvidence {
|
|
22
|
+
status: 'available' | 'unavailable';
|
|
23
|
+
bindingId: string;
|
|
24
|
+
observedAt: string;
|
|
25
|
+
items: SanitizedDiscussionItem[];
|
|
26
|
+
pagesRead: number;
|
|
27
|
+
truncated: boolean;
|
|
28
|
+
persisted: false;
|
|
29
|
+
}
|
|
30
|
+
export declare function acceptRemoteDiscussionObservation(input: {
|
|
31
|
+
action: ExternalActionEnvelope;
|
|
32
|
+
observation: unknown;
|
|
33
|
+
bindingId: string;
|
|
34
|
+
limit: number;
|
|
35
|
+
maxPages: number;
|
|
36
|
+
}): Promise<DiscussionEvidence>;
|
|
37
|
+
export declare function readRemoteDiscussion(input: {
|
|
38
|
+
bindingId: string;
|
|
39
|
+
limit: number;
|
|
40
|
+
maxPages: number;
|
|
41
|
+
observedAt: string;
|
|
42
|
+
}, source: DiscussionPageSource | null): Promise<DiscussionEvidence>;
|
|
43
|
+
export declare function distillDiscussionLocally(input: {
|
|
44
|
+
bindingId: string;
|
|
45
|
+
observedAt: string;
|
|
46
|
+
selectedItemIds: readonly string[];
|
|
47
|
+
items: readonly SanitizedDiscussionItem[];
|
|
48
|
+
}): string;
|
|
49
|
+
//# sourceMappingURL=discussion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discussion.d.ts","sourceRoot":"","sources":["../../../../src/commands/pjm/remote/discussion.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAIhE,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,uBAAwB,SAAQ,oBAAoB;IACnE,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,KAAK,EAAE;QACd,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,KAAK,EAAE,MAAM,CAAC;KACf,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,WAAW,GAAG,aAAa,CAAC;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,uBAAuB,EAAE,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,KAAK,CAAC;CAClB;AA4BD,wBAAsB,iCAAiC,CAAC,KAAK,EAAE;IAC7D,MAAM,EAAE,sBAAsB,CAAC;IAC/B,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAoC9B;AAED,wBAAsB,oBAAoB,CACxC,KAAK,EAAE;IACL,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB,EACD,MAAM,EAAE,oBAAoB,GAAG,IAAI,GAClC,OAAO,CAAC,kBAAkB,CAAC,CAoD7B;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,KAAK,EAAE,SAAS,uBAAuB,EAAE,CAAC;CAC3C,GAAG,MAAM,CAWT"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { containsSensitiveContentSignal } from './credential-safety.js';
|
|
3
|
+
import { semanticDigest } from './provider.js';
|
|
4
|
+
import { WHOLE_FIELD_SUPPRESSION_MARKER } from './schema.js';
|
|
5
|
+
const DiscussionObservationSchema = z
|
|
6
|
+
.object({
|
|
7
|
+
schemaVersion: z.literal(1),
|
|
8
|
+
operationId: z.string().min(1).max(128),
|
|
9
|
+
stepId: z.string().min(1).max(128),
|
|
10
|
+
actionDigest: z.string().min(1).max(512),
|
|
11
|
+
observedAt: z.string().datetime({ offset: true }),
|
|
12
|
+
provider: z.enum(['github', 'linear', 'jira']),
|
|
13
|
+
context: z.record(z.string(), z.string().optional()),
|
|
14
|
+
capabilityEvidenceDigest: z.string().min(1).max(512),
|
|
15
|
+
availability: z.enum(['available', 'unavailable']),
|
|
16
|
+
items: z
|
|
17
|
+
.array(z
|
|
18
|
+
.object({
|
|
19
|
+
id: z.string().min(1).max(512),
|
|
20
|
+
body: z.string().max(1_048_576),
|
|
21
|
+
createdAt: z.string().datetime({ offset: true }),
|
|
22
|
+
})
|
|
23
|
+
.strict())
|
|
24
|
+
.max(100),
|
|
25
|
+
nextCursor: z.string().max(512).nullable(),
|
|
26
|
+
})
|
|
27
|
+
.strict();
|
|
28
|
+
export async function acceptRemoteDiscussionObservation(input) {
|
|
29
|
+
if (input.action.semanticOperation !== 'read-discussion') {
|
|
30
|
+
throw new Error('Discussion continuation requires a discussion action.');
|
|
31
|
+
}
|
|
32
|
+
const observation = DiscussionObservationSchema.parse(input.observation);
|
|
33
|
+
if (observation.operationId !== input.action.operationId ||
|
|
34
|
+
observation.stepId !== input.action.stepId ||
|
|
35
|
+
observation.actionDigest !== input.action.actionDigest ||
|
|
36
|
+
observation.provider !== input.action.provider ||
|
|
37
|
+
observation.capabilityEvidenceDigest !==
|
|
38
|
+
input.action.expectedObservation.capabilityEvidenceDigest ||
|
|
39
|
+
semanticDigest(observation.context) !== semanticDigest(input.action.context)) {
|
|
40
|
+
throw new Error('Discussion observation does not match its durable action.');
|
|
41
|
+
}
|
|
42
|
+
return readRemoteDiscussion({
|
|
43
|
+
bindingId: input.bindingId,
|
|
44
|
+
limit: input.limit,
|
|
45
|
+
maxPages: input.maxPages,
|
|
46
|
+
observedAt: observation.observedAt,
|
|
47
|
+
}, observation.availability === 'available'
|
|
48
|
+
? {
|
|
49
|
+
async readPage() {
|
|
50
|
+
return {
|
|
51
|
+
items: observation.items,
|
|
52
|
+
nextCursor: observation.nextCursor,
|
|
53
|
+
};
|
|
54
|
+
},
|
|
55
|
+
}
|
|
56
|
+
: null);
|
|
57
|
+
}
|
|
58
|
+
export async function readRemoteDiscussion(input, source) {
|
|
59
|
+
assertBounds(input.limit, input.maxPages);
|
|
60
|
+
if (!source) {
|
|
61
|
+
return {
|
|
62
|
+
status: 'unavailable',
|
|
63
|
+
bindingId: input.bindingId,
|
|
64
|
+
observedAt: input.observedAt,
|
|
65
|
+
items: [],
|
|
66
|
+
pagesRead: 0,
|
|
67
|
+
truncated: false,
|
|
68
|
+
persisted: false,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
const items = [];
|
|
72
|
+
let cursor = null;
|
|
73
|
+
let pagesRead = 0;
|
|
74
|
+
let hasMore = false;
|
|
75
|
+
let droppedItems = false;
|
|
76
|
+
do {
|
|
77
|
+
const page = await source.readPage({
|
|
78
|
+
bindingId: input.bindingId,
|
|
79
|
+
cursor,
|
|
80
|
+
limit: input.limit - items.length,
|
|
81
|
+
});
|
|
82
|
+
pagesRead += 1;
|
|
83
|
+
const remaining = input.limit - items.length;
|
|
84
|
+
droppedItems ||= page.items.length > remaining;
|
|
85
|
+
for (const item of page.items.slice(0, remaining)) {
|
|
86
|
+
const suppressed = containsSensitiveContentSignal(item.body);
|
|
87
|
+
items.push({
|
|
88
|
+
...item,
|
|
89
|
+
body: suppressed ? WHOLE_FIELD_SUPPRESSION_MARKER : item.body,
|
|
90
|
+
incomplete: suppressed,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
cursor = page.nextCursor;
|
|
94
|
+
hasMore = cursor !== null;
|
|
95
|
+
} while (cursor !== null &&
|
|
96
|
+
pagesRead < input.maxPages &&
|
|
97
|
+
items.length < input.limit);
|
|
98
|
+
return {
|
|
99
|
+
status: 'available',
|
|
100
|
+
bindingId: input.bindingId,
|
|
101
|
+
observedAt: input.observedAt,
|
|
102
|
+
items,
|
|
103
|
+
pagesRead,
|
|
104
|
+
truncated: hasMore || droppedItems,
|
|
105
|
+
persisted: false,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
export function distillDiscussionLocally(input) {
|
|
109
|
+
const selected = new Set(input.selectedItemIds);
|
|
110
|
+
const evidence = input.items.filter((item) => selected.has(item.id));
|
|
111
|
+
return [
|
|
112
|
+
`locally authored discussion distillation for ${input.bindingId}`,
|
|
113
|
+
`Observed: ${input.observedAt}`,
|
|
114
|
+
...evidence.map((item) => `- ${item.body}${item.incomplete ? ' (incomplete evidence)' : ''}`),
|
|
115
|
+
].join('\n');
|
|
116
|
+
}
|
|
117
|
+
function assertBounds(limit, maxPages) {
|
|
118
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100) {
|
|
119
|
+
throw new Error('Discussion limit must be an integer from 1 to 100.');
|
|
120
|
+
}
|
|
121
|
+
if (!Number.isSafeInteger(maxPages) || maxPages < 1 || maxPages > 10) {
|
|
122
|
+
throw new Error('Discussion maxPages must be an integer from 1 to 10.');
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { DoctorCheck } from '../../../ui/output.js';
|
|
2
|
+
export interface RemoteDoctorInput {
|
|
3
|
+
portableBindingsDir: string;
|
|
4
|
+
operationalBindingsDir: string;
|
|
5
|
+
operationsDir: string;
|
|
6
|
+
associatedBindingIds?: readonly string[];
|
|
7
|
+
policy?: unknown;
|
|
8
|
+
now?: string;
|
|
9
|
+
staleAfterMs?: number;
|
|
10
|
+
retentionBreaches?: readonly string[];
|
|
11
|
+
evidenceAvailability?: {
|
|
12
|
+
associations: boolean;
|
|
13
|
+
retention: boolean;
|
|
14
|
+
hostCapabilities: boolean;
|
|
15
|
+
};
|
|
16
|
+
hostCapabilityAvailability?: ReadonlyArray<{
|
|
17
|
+
bindingId: string;
|
|
18
|
+
available: boolean;
|
|
19
|
+
}>;
|
|
20
|
+
}
|
|
21
|
+
export declare function runRemoteDoctorChecks(input: RemoteDoctorInput): Promise<DoctorCheck[]>;
|
|
22
|
+
//# sourceMappingURL=doctor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../../../src/commands/pjm/remote/doctor.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAY9C,MAAM,WAAW,iBAAiB;IAChC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,sBAAsB,EAAE,MAAM,CAAC;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,oBAAoB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,oBAAoB,CAAC,EAAE;QACrB,YAAY,EAAE,OAAO,CAAC;QACtB,SAAS,EAAE,OAAO,CAAC;QACnB,gBAAgB,EAAE,OAAO,CAAC;KAC3B,CAAC;IACF,0BAA0B,CAAC,EAAE,aAAa,CAAC;QACzC,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,OAAO,CAAC;KACpB,CAAC,CAAC;CACJ;AAoBD,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,iBAAiB,GACvB,OAAO,CAAC,WAAW,EAAE,CAAC,CAiHxB"}
|