@open-agent-toolkit/cli 0.2.65 → 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.
Files changed (142) hide show
  1. package/assets/bundle-metadata.json +1 -1
  2. package/assets/docs/cli-utilities/configuration.md +19 -1
  3. package/assets/docs/cli-utilities/index.md +3 -0
  4. package/assets/docs/cli-utilities/remote-project-management.md +191 -0
  5. package/assets/docs/reference/cli-reference.md +12 -1
  6. package/assets/docs/reference/file-locations.md +22 -0
  7. package/assets/docs/reference/oat-directory-structure.md +30 -17
  8. package/assets/docs/reference/troubleshooting.md +44 -0
  9. package/assets/docs/workflows/skills/index.md +33 -0
  10. package/assets/public-package-versions.json +4 -4
  11. package/assets/skills/oat-doctor/SKILL.md +4 -4
  12. package/assets/skills/oat-pjm-remote/SKILL.md +101 -0
  13. package/assets/skills/oat-pjm-remote/references/external-action-protocol.md +43 -0
  14. package/dist/commands/backlog/new.d.ts +1 -0
  15. package/dist/commands/backlog/new.d.ts.map +1 -1
  16. package/dist/commands/backlog/new.js +3 -1
  17. package/dist/commands/config/index.d.ts +1 -0
  18. package/dist/commands/config/index.d.ts.map +1 -1
  19. package/dist/commands/config/index.js +232 -1
  20. package/dist/commands/pjm/doctor.d.ts +2 -0
  21. package/dist/commands/pjm/doctor.d.ts.map +1 -1
  22. package/dist/commands/pjm/doctor.js +71 -1
  23. package/dist/commands/pjm/index.d.ts +2 -0
  24. package/dist/commands/pjm/index.d.ts.map +1 -1
  25. package/dist/commands/pjm/index.js +3 -0
  26. package/dist/commands/pjm/remote/__integration__/lifecycle-harness.d.ts +60 -0
  27. package/dist/commands/pjm/remote/__integration__/lifecycle-harness.d.ts.map +1 -0
  28. package/dist/commands/pjm/remote/__integration__/lifecycle-harness.js +151 -0
  29. package/dist/commands/pjm/remote/association.d.ts +27 -0
  30. package/dist/commands/pjm/remote/association.d.ts.map +1 -0
  31. package/dist/commands/pjm/remote/association.js +68 -0
  32. package/dist/commands/pjm/remote/authority.d.ts +210 -0
  33. package/dist/commands/pjm/remote/authority.d.ts.map +1 -0
  34. package/dist/commands/pjm/remote/authority.js +341 -0
  35. package/dist/commands/pjm/remote/batch.d.ts +15 -0
  36. package/dist/commands/pjm/remote/batch.d.ts.map +1 -0
  37. package/dist/commands/pjm/remote/batch.js +126 -0
  38. package/dist/commands/pjm/remote/closeout.d.ts +69 -0
  39. package/dist/commands/pjm/remote/closeout.d.ts.map +1 -0
  40. package/dist/commands/pjm/remote/closeout.js +208 -0
  41. package/dist/commands/pjm/remote/create-binding.d.ts +62 -0
  42. package/dist/commands/pjm/remote/create-binding.d.ts.map +1 -0
  43. package/dist/commands/pjm/remote/create-binding.js +126 -0
  44. package/dist/commands/pjm/remote/credential-safety.d.ts +7 -0
  45. package/dist/commands/pjm/remote/credential-safety.d.ts.map +1 -0
  46. package/dist/commands/pjm/remote/credential-safety.js +21 -0
  47. package/dist/commands/pjm/remote/discussion.d.ts +49 -0
  48. package/dist/commands/pjm/remote/discussion.d.ts.map +1 -0
  49. package/dist/commands/pjm/remote/discussion.js +124 -0
  50. package/dist/commands/pjm/remote/doctor.d.ts +22 -0
  51. package/dist/commands/pjm/remote/doctor.d.ts.map +1 -0
  52. package/dist/commands/pjm/remote/doctor.js +380 -0
  53. package/dist/commands/pjm/remote/external-action.d.ts +208 -0
  54. package/dist/commands/pjm/remote/external-action.d.ts.map +1 -0
  55. package/dist/commands/pjm/remote/external-action.js +367 -0
  56. package/dist/commands/pjm/remote/host-execution.d.ts +28 -0
  57. package/dist/commands/pjm/remote/host-execution.d.ts.map +1 -0
  58. package/dist/commands/pjm/remote/host-execution.js +63 -0
  59. package/dist/commands/pjm/remote/index.d.ts +41 -0
  60. package/dist/commands/pjm/remote/index.d.ts.map +1 -0
  61. package/dist/commands/pjm/remote/index.js +280 -0
  62. package/dist/commands/pjm/remote/jira-adf.d.ts +35 -0
  63. package/dist/commands/pjm/remote/jira-adf.d.ts.map +1 -0
  64. package/dist/commands/pjm/remote/jira-adf.js +169 -0
  65. package/dist/commands/pjm/remote/lifecycle.d.ts +163 -0
  66. package/dist/commands/pjm/remote/lifecycle.d.ts.map +1 -0
  67. package/dist/commands/pjm/remote/lifecycle.js +224 -0
  68. package/dist/commands/pjm/remote/local-projection.d.ts +30 -0
  69. package/dist/commands/pjm/remote/local-projection.d.ts.map +1 -0
  70. package/dist/commands/pjm/remote/local-projection.js +95 -0
  71. package/dist/commands/pjm/remote/managed-markdown.d.ts +30 -0
  72. package/dist/commands/pjm/remote/managed-markdown.d.ts.map +1 -0
  73. package/dist/commands/pjm/remote/managed-markdown.js +128 -0
  74. package/dist/commands/pjm/remote/migrate.d.ts +14 -0
  75. package/dist/commands/pjm/remote/migrate.d.ts.map +1 -0
  76. package/dist/commands/pjm/remote/migrate.js +32 -0
  77. package/dist/commands/pjm/remote/operation-state.d.ts +15 -0
  78. package/dist/commands/pjm/remote/operation-state.d.ts.map +1 -0
  79. package/dist/commands/pjm/remote/operation-state.js +143 -0
  80. package/dist/commands/pjm/remote/outbound-projection-safety.d.ts +25 -0
  81. package/dist/commands/pjm/remote/outbound-projection-safety.d.ts.map +1 -0
  82. package/dist/commands/pjm/remote/outbound-projection-safety.js +99 -0
  83. package/dist/commands/pjm/remote/output.d.ts +60 -0
  84. package/dist/commands/pjm/remote/output.d.ts.map +1 -0
  85. package/dist/commands/pjm/remote/output.js +48 -0
  86. package/dist/commands/pjm/remote/preview.d.ts +88 -0
  87. package/dist/commands/pjm/remote/preview.d.ts.map +1 -0
  88. package/dist/commands/pjm/remote/preview.js +154 -0
  89. package/dist/commands/pjm/remote/provider-conformance.d.ts +13 -0
  90. package/dist/commands/pjm/remote/provider-conformance.d.ts.map +1 -0
  91. package/dist/commands/pjm/remote/provider-conformance.js +37 -0
  92. package/dist/commands/pjm/remote/provider.d.ts +57 -0
  93. package/dist/commands/pjm/remote/provider.d.ts.map +1 -0
  94. package/dist/commands/pjm/remote/provider.js +28 -0
  95. package/dist/commands/pjm/remote/providers/github-publication-safety.d.ts +46 -0
  96. package/dist/commands/pjm/remote/providers/github-publication-safety.d.ts.map +1 -0
  97. package/dist/commands/pjm/remote/providers/github-publication-safety.js +126 -0
  98. package/dist/commands/pjm/remote/providers/github.d.ts +207 -0
  99. package/dist/commands/pjm/remote/providers/github.d.ts.map +1 -0
  100. package/dist/commands/pjm/remote/providers/github.js +1456 -0
  101. package/dist/commands/pjm/remote/providers/jira.d.ts +245 -0
  102. package/dist/commands/pjm/remote/providers/jira.d.ts.map +1 -0
  103. package/dist/commands/pjm/remote/providers/jira.js +1650 -0
  104. package/dist/commands/pjm/remote/providers/linear.d.ts +182 -0
  105. package/dist/commands/pjm/remote/providers/linear.d.ts.map +1 -0
  106. package/dist/commands/pjm/remote/providers/linear.js +1289 -0
  107. package/dist/commands/pjm/remote/purpose-policy.d.ts +29 -0
  108. package/dist/commands/pjm/remote/purpose-policy.d.ts.map +1 -0
  109. package/dist/commands/pjm/remote/purpose-policy.js +118 -0
  110. package/dist/commands/pjm/remote/reconcile.d.ts +42 -0
  111. package/dist/commands/pjm/remote/reconcile.d.ts.map +1 -0
  112. package/dist/commands/pjm/remote/reconcile.js +101 -0
  113. package/dist/commands/pjm/remote/resolution.d.ts +122 -0
  114. package/dist/commands/pjm/remote/resolution.d.ts.map +1 -0
  115. package/dist/commands/pjm/remote/resolution.js +243 -0
  116. package/dist/commands/pjm/remote/schema.d.ts +9309 -0
  117. package/dist/commands/pjm/remote/schema.d.ts.map +1 -0
  118. package/dist/commands/pjm/remote/schema.js +1503 -0
  119. package/dist/commands/pjm/remote/service.d.ts +20 -0
  120. package/dist/commands/pjm/remote/service.d.ts.map +1 -0
  121. package/dist/commands/pjm/remote/service.js +5121 -0
  122. package/dist/commands/pjm/remote/shared-storage.d.ts +36 -0
  123. package/dist/commands/pjm/remote/shared-storage.d.ts.map +1 -0
  124. package/dist/commands/pjm/remote/shared-storage.js +110 -0
  125. package/dist/commands/pjm/remote/snapshot.d.ts +32 -0
  126. package/dist/commands/pjm/remote/snapshot.d.ts.map +1 -0
  127. package/dist/commands/pjm/remote/snapshot.js +140 -0
  128. package/dist/commands/pjm/remote/storage-locator.d.ts +29 -0
  129. package/dist/commands/pjm/remote/storage-locator.d.ts.map +1 -0
  130. package/dist/commands/pjm/remote/storage-locator.js +62 -0
  131. package/dist/commands/pjm/remote/store.d.ts +85 -0
  132. package/dist/commands/pjm/remote/store.d.ts.map +1 -0
  133. package/dist/commands/pjm/remote/store.js +553 -0
  134. package/dist/commands/pjm/remote/verification.d.ts +40 -0
  135. package/dist/commands/pjm/remote/verification.d.ts.map +1 -0
  136. package/dist/commands/pjm/remote/verification.js +100 -0
  137. package/dist/commands/tools/shared/pack-manifest.d.ts.map +1 -1
  138. package/dist/commands/tools/shared/pack-manifest.js +1 -0
  139. package/dist/config/oat-config.d.ts +26 -0
  140. package/dist/config/oat-config.d.ts.map +1 -1
  141. package/dist/config/oat-config.js +186 -2
  142. 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"}