codex-orchestrator 0.1.47 → 0.1.49

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 (180) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/dist/src/config/schema.d.ts +5 -1
  3. package/dist/src/config/schema.d.ts.map +1 -1
  4. package/dist/src/config/schema.js +16 -0
  5. package/dist/src/config/schema.js.map +1 -1
  6. package/dist/src/github/gh-issue-adapter.d.ts +3 -1
  7. package/dist/src/github/gh-issue-adapter.d.ts.map +1 -1
  8. package/dist/src/github/gh-issue-adapter.js +44 -6
  9. package/dist/src/github/gh-issue-adapter.js.map +1 -1
  10. package/dist/src/github/gh-pull-request-adapter.d.ts +3 -1
  11. package/dist/src/github/gh-pull-request-adapter.d.ts.map +1 -1
  12. package/dist/src/github/gh-pull-request-adapter.js +74 -0
  13. package/dist/src/github/gh-pull-request-adapter.js.map +1 -1
  14. package/dist/src/github/issues.d.ts +4 -0
  15. package/dist/src/github/issues.d.ts.map +1 -1
  16. package/dist/src/github/issues.js +6 -0
  17. package/dist/src/github/issues.js.map +1 -1
  18. package/dist/src/github/pull-requests.d.ts +9 -0
  19. package/dist/src/github/pull-requests.d.ts.map +1 -1
  20. package/dist/src/github/pull-requests.js +13 -0
  21. package/dist/src/github/pull-requests.js.map +1 -1
  22. package/dist/src/runner/command-utils.d.ts.map +1 -1
  23. package/dist/src/runner/command-utils.js +1 -0
  24. package/dist/src/runner/command-utils.js.map +1 -1
  25. package/dist/src/runner/local-execution-session.d.ts.map +1 -1
  26. package/dist/src/runner/local-execution-session.js +5 -2
  27. package/dist/src/runner/local-execution-session.js.map +1 -1
  28. package/dist/src/runner/mission-activation.d.ts +29 -0
  29. package/dist/src/runner/mission-activation.d.ts.map +1 -0
  30. package/dist/src/runner/mission-activation.js +54 -0
  31. package/dist/src/runner/mission-activation.js.map +1 -0
  32. package/dist/src/runner/mission-agent-broker.d.ts +53 -0
  33. package/dist/src/runner/mission-agent-broker.d.ts.map +1 -0
  34. package/dist/src/runner/mission-agent-broker.js +182 -0
  35. package/dist/src/runner/mission-agent-broker.js.map +1 -0
  36. package/dist/src/runner/mission-cancellation.d.ts +77 -0
  37. package/dist/src/runner/mission-cancellation.d.ts.map +1 -0
  38. package/dist/src/runner/mission-cancellation.js +180 -0
  39. package/dist/src/runner/mission-cancellation.js.map +1 -0
  40. package/dist/src/runner/mission-canonical-path.d.ts +34 -0
  41. package/dist/src/runner/mission-canonical-path.d.ts.map +1 -0
  42. package/dist/src/runner/mission-canonical-path.js +121 -0
  43. package/dist/src/runner/mission-canonical-path.js.map +1 -0
  44. package/dist/src/runner/mission-capability-kernel.d.ts +34 -0
  45. package/dist/src/runner/mission-capability-kernel.d.ts.map +1 -0
  46. package/dist/src/runner/mission-capability-kernel.js +158 -0
  47. package/dist/src/runner/mission-capability-kernel.js.map +1 -0
  48. package/dist/src/runner/mission-check-policy.d.ts +17 -0
  49. package/dist/src/runner/mission-check-policy.d.ts.map +1 -0
  50. package/dist/src/runner/mission-check-policy.js +31 -0
  51. package/dist/src/runner/mission-check-policy.js.map +1 -0
  52. package/dist/src/runner/mission-coordinator-lock.d.ts +16 -0
  53. package/dist/src/runner/mission-coordinator-lock.d.ts.map +1 -0
  54. package/dist/src/runner/mission-coordinator-lock.js +170 -0
  55. package/dist/src/runner/mission-coordinator-lock.js.map +1 -0
  56. package/dist/src/runner/mission-deployment.d.ts +21 -0
  57. package/dist/src/runner/mission-deployment.d.ts.map +1 -0
  58. package/dist/src/runner/mission-deployment.js +108 -0
  59. package/dist/src/runner/mission-deployment.js.map +1 -0
  60. package/dist/src/runner/mission-evaluation.d.ts +61 -0
  61. package/dist/src/runner/mission-evaluation.d.ts.map +1 -0
  62. package/dist/src/runner/mission-evaluation.js +183 -0
  63. package/dist/src/runner/mission-evaluation.js.map +1 -0
  64. package/dist/src/runner/mission-executor-probe.d.ts +10 -0
  65. package/dist/src/runner/mission-executor-probe.d.ts.map +1 -0
  66. package/dist/src/runner/mission-executor-probe.js +180 -0
  67. package/dist/src/runner/mission-executor-probe.js.map +1 -0
  68. package/dist/src/runner/mission-git-contracts.d.ts +75 -0
  69. package/dist/src/runner/mission-git-contracts.d.ts.map +1 -0
  70. package/dist/src/runner/mission-git-contracts.js +201 -0
  71. package/dist/src/runner/mission-git-contracts.js.map +1 -0
  72. package/dist/src/runner/mission-git-runtime-owner-ref.d.ts +13 -0
  73. package/dist/src/runner/mission-git-runtime-owner-ref.d.ts.map +1 -0
  74. package/dist/src/runner/mission-git-runtime-owner-ref.js +111 -0
  75. package/dist/src/runner/mission-git-runtime-owner-ref.js.map +1 -0
  76. package/dist/src/runner/mission-git-transaction.d.ts +97 -0
  77. package/dist/src/runner/mission-git-transaction.d.ts.map +1 -0
  78. package/dist/src/runner/mission-git-transaction.js +1009 -0
  79. package/dist/src/runner/mission-git-transaction.js.map +1 -0
  80. package/dist/src/runner/mission-identifiers.d.ts +31 -0
  81. package/dist/src/runner/mission-identifiers.d.ts.map +1 -0
  82. package/dist/src/runner/mission-identifiers.js +75 -0
  83. package/dist/src/runner/mission-identifiers.js.map +1 -0
  84. package/dist/src/runner/mission-input-snapshot.d.ts +21 -0
  85. package/dist/src/runner/mission-input-snapshot.d.ts.map +1 -0
  86. package/dist/src/runner/mission-input-snapshot.js +63 -0
  87. package/dist/src/runner/mission-input-snapshot.js.map +1 -0
  88. package/dist/src/runner/mission-ownership.d.ts +38 -0
  89. package/dist/src/runner/mission-ownership.d.ts.map +1 -0
  90. package/dist/src/runner/mission-ownership.js +88 -0
  91. package/dist/src/runner/mission-ownership.js.map +1 -0
  92. package/dist/src/runner/mission-patch-audit.d.ts +21 -0
  93. package/dist/src/runner/mission-patch-audit.d.ts.map +1 -0
  94. package/dist/src/runner/mission-patch-audit.js +186 -0
  95. package/dist/src/runner/mission-patch-audit.js.map +1 -0
  96. package/dist/src/runner/mission-path-language.d.ts +6 -0
  97. package/dist/src/runner/mission-path-language.d.ts.map +1 -0
  98. package/dist/src/runner/mission-path-language.js +106 -0
  99. package/dist/src/runner/mission-path-language.js.map +1 -0
  100. package/dist/src/runner/mission-permit-authority.d.ts +26 -0
  101. package/dist/src/runner/mission-permit-authority.d.ts.map +1 -0
  102. package/dist/src/runner/mission-permit-authority.js +145 -0
  103. package/dist/src/runner/mission-permit-authority.js.map +1 -0
  104. package/dist/src/runner/mission-plan-parent-coordinator.d.ts +87 -0
  105. package/dist/src/runner/mission-plan-parent-coordinator.d.ts.map +1 -0
  106. package/dist/src/runner/mission-plan-parent-coordinator.js +441 -0
  107. package/dist/src/runner/mission-plan-parent-coordinator.js.map +1 -0
  108. package/dist/src/runner/mission-plan-parent.d.ts +156 -0
  109. package/dist/src/runner/mission-plan-parent.d.ts.map +1 -0
  110. package/dist/src/runner/mission-plan-parent.js +589 -0
  111. package/dist/src/runner/mission-plan-parent.js.map +1 -0
  112. package/dist/src/runner/mission-process-executor.d.ts +25 -0
  113. package/dist/src/runner/mission-process-executor.d.ts.map +1 -0
  114. package/dist/src/runner/mission-process-executor.js +182 -0
  115. package/dist/src/runner/mission-process-executor.js.map +1 -0
  116. package/dist/src/runner/mission-publication-git.d.ts +21 -0
  117. package/dist/src/runner/mission-publication-git.d.ts.map +1 -0
  118. package/dist/src/runner/mission-publication-git.js +61 -0
  119. package/dist/src/runner/mission-publication-git.js.map +1 -0
  120. package/dist/src/runner/mission-publication.d.ts +192 -0
  121. package/dist/src/runner/mission-publication.d.ts.map +1 -0
  122. package/dist/src/runner/mission-publication.js +1001 -0
  123. package/dist/src/runner/mission-publication.js.map +1 -0
  124. package/dist/src/runner/mission-quarantine-patch-executor.d.ts +29 -0
  125. package/dist/src/runner/mission-quarantine-patch-executor.d.ts.map +1 -0
  126. package/dist/src/runner/mission-quarantine-patch-executor.js +121 -0
  127. package/dist/src/runner/mission-quarantine-patch-executor.js.map +1 -0
  128. package/dist/src/runner/mission-repository-executor.d.ts +54 -0
  129. package/dist/src/runner/mission-repository-executor.d.ts.map +1 -0
  130. package/dist/src/runner/mission-repository-executor.js +192 -0
  131. package/dist/src/runner/mission-repository-executor.js.map +1 -0
  132. package/dist/src/runner/mission-repository-observer.d.ts +8 -0
  133. package/dist/src/runner/mission-repository-observer.d.ts.map +1 -0
  134. package/dist/src/runner/mission-repository-observer.js +24 -0
  135. package/dist/src/runner/mission-repository-observer.js.map +1 -0
  136. package/dist/src/runner/mission-reservations.d.ts +15 -0
  137. package/dist/src/runner/mission-reservations.d.ts.map +1 -0
  138. package/dist/src/runner/mission-reservations.js +116 -0
  139. package/dist/src/runner/mission-reservations.js.map +1 -0
  140. package/dist/src/runner/mission-resolution-loop.d.ts +172 -0
  141. package/dist/src/runner/mission-resolution-loop.d.ts.map +1 -0
  142. package/dist/src/runner/mission-resolution-loop.js +318 -0
  143. package/dist/src/runner/mission-resolution-loop.js.map +1 -0
  144. package/dist/src/runner/mission-runtime-owner.d.ts +36 -0
  145. package/dist/src/runner/mission-runtime-owner.d.ts.map +1 -0
  146. package/dist/src/runner/mission-runtime-owner.js +174 -0
  147. package/dist/src/runner/mission-runtime-owner.js.map +1 -0
  148. package/dist/src/runner/mission-safe-executor.d.ts +96 -0
  149. package/dist/src/runner/mission-safe-executor.d.ts.map +1 -0
  150. package/dist/src/runner/mission-safe-executor.js +288 -0
  151. package/dist/src/runner/mission-safe-executor.js.map +1 -0
  152. package/dist/src/runner/mission-sandbox.d.ts +16 -0
  153. package/dist/src/runner/mission-sandbox.d.ts.map +1 -0
  154. package/dist/src/runner/mission-sandbox.js +91 -0
  155. package/dist/src/runner/mission-sandbox.js.map +1 -0
  156. package/dist/src/runner/mission-scheduler.d.ts +57 -0
  157. package/dist/src/runner/mission-scheduler.d.ts.map +1 -0
  158. package/dist/src/runner/mission-scheduler.js +205 -0
  159. package/dist/src/runner/mission-scheduler.js.map +1 -0
  160. package/dist/src/runner/mission-scope-expansion.d.ts +47 -0
  161. package/dist/src/runner/mission-scope-expansion.d.ts.map +1 -0
  162. package/dist/src/runner/mission-scope-expansion.js +147 -0
  163. package/dist/src/runner/mission-scope-expansion.js.map +1 -0
  164. package/dist/src/runner/mission-state-machine.d.ts +118 -0
  165. package/dist/src/runner/mission-state-machine.d.ts.map +1 -0
  166. package/dist/src/runner/mission-state-machine.js +316 -0
  167. package/dist/src/runner/mission-state-machine.js.map +1 -0
  168. package/dist/src/runner/mission-state-store.d.ts +70 -0
  169. package/dist/src/runner/mission-state-store.d.ts.map +1 -0
  170. package/dist/src/runner/mission-state-store.js +916 -0
  171. package/dist/src/runner/mission-state-store.js.map +1 -0
  172. package/dist/src/runner/rework-policy.d.ts +2 -1
  173. package/dist/src/runner/rework-policy.d.ts.map +1 -1
  174. package/dist/src/runner/rework-policy.js +3 -0
  175. package/dist/src/runner/rework-policy.js.map +1 -1
  176. package/dist/src/setup/project-config.d.ts.map +1 -1
  177. package/dist/src/setup/project-config.js +5 -0
  178. package/dist/src/setup/project-config.js.map +1 -1
  179. package/docs/deep-dive.md +25 -16
  180. package/package.json +1 -1
@@ -0,0 +1,182 @@
1
+ import { missionScopeRelationshipKinds, } from './mission-scope-expansion.js';
2
+ export class MissionAgentBroker {
3
+ transport;
4
+ constructor(transport) {
5
+ this.transport = transport;
6
+ }
7
+ async diagnose(input) {
8
+ assertDiagnosisInput(input);
9
+ const raw = await this.transport.diagnose(structuredClone(input));
10
+ return parseProposal(raw, input);
11
+ }
12
+ }
13
+ function parseProposal(raw, input) {
14
+ const allowed = new Set(input.allowedCapabilities);
15
+ let parsed;
16
+ try {
17
+ parsed = JSON.parse(raw);
18
+ }
19
+ catch {
20
+ return invalid('expected JSON');
21
+ }
22
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
23
+ return invalid('root must be an object');
24
+ }
25
+ const record = parsed;
26
+ if (record.version !== 1 || typeof record.kind !== 'string') {
27
+ return invalid('version and kind are required');
28
+ }
29
+ if (record.kind === 'observe') {
30
+ assertExact(record, ['version', 'kind', 'capability', 'paths', 'rationale']);
31
+ if (record.capability !== 'read-file' && record.capability !== 'git-status') {
32
+ return invalid('unknown observation capability');
33
+ }
34
+ if (!allowed.has(record.capability)) {
35
+ return invalid('capability is not allowed for this diagnosis');
36
+ }
37
+ return {
38
+ version: 1,
39
+ kind: 'observe',
40
+ capability: record.capability,
41
+ paths: assertSafePaths(record.paths),
42
+ rationale: assertText(record.rationale, 'rationale'),
43
+ };
44
+ }
45
+ if (record.kind === 'patch') {
46
+ assertExact(record, ['version', 'kind', 'capability', 'paths', 'patch', 'rationale']);
47
+ if (record.capability !== 'validate-patch' || !allowed.has('validate-patch')) {
48
+ return invalid('patch capability is not allowed');
49
+ }
50
+ return {
51
+ version: 1,
52
+ kind: 'patch',
53
+ capability: 'validate-patch',
54
+ paths: assertSafePaths(record.paths),
55
+ patch: assertText(record.patch, 'patch'),
56
+ rationale: assertText(record.rationale, 'rationale'),
57
+ };
58
+ }
59
+ if (record.kind === 'runner-action') {
60
+ assertExact(record, ['version', 'kind', 'executorId', 'findingIds', 'rationale']);
61
+ const executorId = assertText(record.executorId, 'executorId');
62
+ if (!(input.allowedRunnerActions ?? []).includes(executorId)) {
63
+ return invalid('runner action is not allowed for this diagnosis');
64
+ }
65
+ const findingIds = assertTextArray(record.findingIds, 'findingIds');
66
+ if (findingIds.some((id) => !input.findingIds.includes(id))) {
67
+ return invalid('runner action references an unknown finding');
68
+ }
69
+ return {
70
+ version: 1,
71
+ kind: 'runner-action',
72
+ executorId,
73
+ findingIds,
74
+ rationale: assertText(record.rationale, 'rationale'),
75
+ };
76
+ }
77
+ if (record.kind === 'scope-expansion') {
78
+ assertExact(record, [
79
+ 'version', 'kind', 'repository', 'paths', 'evidenceIds', 'relationship', 'rationale',
80
+ ]);
81
+ const repository = assertText(record.repository, 'repository');
82
+ if (!input.repository || repository !== input.repository) {
83
+ return invalid('scope expansion repository does not match the pinned repository');
84
+ }
85
+ const evidenceIds = assertTextArray(record.evidenceIds, 'evidenceIds');
86
+ const knownEvidence = new Set([...input.findingIds, ...(input.evidenceIds ?? [])]);
87
+ if (evidenceIds.some((id) => !knownEvidence.has(id))) {
88
+ return invalid('scope expansion references unknown evidence');
89
+ }
90
+ const relationship = parseRelationship(record.relationship);
91
+ return {
92
+ version: 1,
93
+ kind: 'scope-expansion',
94
+ repository,
95
+ paths: assertSafePaths(record.paths),
96
+ evidenceIds,
97
+ relationship,
98
+ rationale: assertText(record.rationale, 'rationale'),
99
+ };
100
+ }
101
+ if (record.kind === 'external-input') {
102
+ assertExact(record, ['version', 'kind', 'evidence', 'resumePredicate']);
103
+ return {
104
+ version: 1,
105
+ kind: 'external-input',
106
+ evidence: assertTextArray(record.evidence, 'evidence'),
107
+ resumePredicate: assertText(record.resumePredicate, 'resumePredicate'),
108
+ };
109
+ }
110
+ if (record.kind === 'safety-stop') {
111
+ assertExact(record, ['version', 'kind', 'evidence', 'invariant']);
112
+ return {
113
+ version: 1,
114
+ kind: 'safety-stop',
115
+ evidence: assertTextArray(record.evidence, 'evidence'),
116
+ invariant: assertText(record.invariant, 'invariant'),
117
+ };
118
+ }
119
+ return invalid('unknown kind');
120
+ }
121
+ function assertDiagnosisInput(input) {
122
+ assertText(input.missionId, 'missionId');
123
+ assertText(input.snapshotId, 'snapshotId');
124
+ assertTextArray(input.findingIds, 'findingIds');
125
+ if (input.allowedCapabilities.length === 0 && (input.allowedRunnerActions?.length ?? 0) === 0) {
126
+ invalid('at least one allowed capability is required');
127
+ }
128
+ }
129
+ function parseRelationship(value) {
130
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
131
+ return invalid('relationship must be an object');
132
+ }
133
+ const record = value;
134
+ const allowed = new Set(['kind', 'from', 'generatorId']);
135
+ if (Object.keys(record).some((key) => !allowed.has(key))
136
+ || !('kind' in record) || !('from' in record)) {
137
+ return invalid('relationship fields are invalid');
138
+ }
139
+ if (typeof record.kind !== 'string'
140
+ || !missionScopeRelationshipKinds.includes(record.kind)) {
141
+ return invalid('relationship kind is invalid');
142
+ }
143
+ const generatorId = record.generatorId === undefined
144
+ ? undefined : assertText(record.generatorId, 'generatorId');
145
+ return {
146
+ kind: record.kind,
147
+ from: assertSafePaths([record.from])[0],
148
+ ...(generatorId ? { generatorId } : {}),
149
+ };
150
+ }
151
+ function assertSafePaths(value) {
152
+ const paths = assertTextArray(value, 'paths');
153
+ if (paths.some((path) => path.startsWith('/') || path.split('/').includes('..')
154
+ || /(^|\/)\.env(?:\.|$)/iu.test(path) || path === '.git' || path.startsWith('.git/'))) {
155
+ return invalid('paths contain a secret, Git internal, absolute, or traversal path');
156
+ }
157
+ return paths;
158
+ }
159
+ function assertExact(record, fields) {
160
+ const allowed = new Set(fields);
161
+ const unexpected = Object.keys(record).find((field) => !allowed.has(field));
162
+ if (unexpected || fields.some((field) => !(field in record))) {
163
+ invalid(unexpected ? `unexpected field ${unexpected}` : 'required field is missing');
164
+ }
165
+ }
166
+ function assertText(value, field) {
167
+ if (typeof value !== 'string' || value.trim().length === 0) {
168
+ return invalid(`${field} must be non-empty`);
169
+ }
170
+ return value;
171
+ }
172
+ function assertTextArray(value, field) {
173
+ if (!Array.isArray(value) || value.length === 0
174
+ || value.some((item) => typeof item !== 'string' || item.trim().length === 0)) {
175
+ return invalid(`${field} must be a non-empty string array`);
176
+ }
177
+ return [...value];
178
+ }
179
+ function invalid(reason) {
180
+ throw new Error(`Invalid Mission Agent proposal: ${reason}.`);
181
+ }
182
+ //# sourceMappingURL=mission-agent-broker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mission-agent-broker.js","sourceRoot":"","sources":["../../../src/runner/mission-agent-broker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,77 @@
1
+ import { type MissionProcessHandle, type MissionRecord } from './mission-state-machine.js';
2
+ import { type MissionApplyReceipt } from './mission-git-contracts.js';
3
+ import type { MissionStateStore } from './mission-state-store.js';
4
+ export type MissionProcessTerminationResult = {
5
+ kind: 'terminated';
6
+ } | {
7
+ kind: 'already-exited';
8
+ } | {
9
+ kind: 'transient-failure';
10
+ reason: string;
11
+ };
12
+ export interface MissionCancellationDependencies {
13
+ terminate(process: MissionProcessHandle): Promise<MissionProcessTerminationResult>;
14
+ reconcileApply(mission: MissionRecord): Promise<MissionCancellationApplyReconciliation>;
15
+ nextEligibleAt(reason: string): string;
16
+ }
17
+ export type MissionCancellationApplyReconciliation = {
18
+ kind: 'old-identity';
19
+ } | {
20
+ kind: 'new-identity';
21
+ receipt: MissionApplyReceipt;
22
+ } | {
23
+ kind: 'transient-failure';
24
+ reason: string;
25
+ } | {
26
+ kind: 'safety-stop';
27
+ reason: string;
28
+ };
29
+ export type MissionObservedProcessIdentity = 'matching' | 'missing' | 'different';
30
+ export interface MissionProcessGroupTerminatorOptions {
31
+ hostId: string;
32
+ bootNonce: string;
33
+ graceMs: number;
34
+ }
35
+ export interface MissionProcessGroupTerminatorDependencies {
36
+ observe(process: MissionProcessHandle): Promise<MissionObservedProcessIdentity>;
37
+ signalProcessGroup(pid: number, signal: NodeJS.Signals): void;
38
+ wait(milliseconds: number): Promise<void>;
39
+ }
40
+ export declare class MissionProcessGroupTerminator {
41
+ private readonly options;
42
+ private readonly dependencies;
43
+ constructor(options: MissionProcessGroupTerminatorOptions, dependencies: MissionProcessGroupTerminatorDependencies);
44
+ terminate(process: MissionProcessHandle): Promise<MissionProcessTerminationResult>;
45
+ }
46
+ export type MissionCancellationOutcome = {
47
+ kind: 'cancelled';
48
+ missionId: string;
49
+ state: 'cancelled';
50
+ } | {
51
+ kind: 'safety-stop';
52
+ missionId: string;
53
+ state: 'safety-stop';
54
+ reason: string;
55
+ } | {
56
+ kind: 'resumable';
57
+ missionId: string;
58
+ state: 'cancelling';
59
+ reason: string;
60
+ nextEligibleAt: string;
61
+ };
62
+ export declare class MissionCancellationCoordinator {
63
+ private readonly store;
64
+ private readonly dependencies;
65
+ constructor(store: MissionStateStore, dependencies: MissionCancellationDependencies);
66
+ cancel(input: {
67
+ expectedGeneration: number;
68
+ missionId: string;
69
+ expectedRevision: number;
70
+ requestedAt: string;
71
+ requestedBy: string;
72
+ }): Promise<MissionCancellationOutcome>;
73
+ private completeCancellation;
74
+ private deferCancellation;
75
+ private persistSafetyStop;
76
+ }
77
+ //# sourceMappingURL=mission-cancellation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mission-cancellation.d.ts","sourceRoot":"","sources":["../../../src/runner/mission-cancellation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,180 @@
1
+ import { transitionMission, } from './mission-state-machine.js';
2
+ import { assertMissionApplyReceipt, missionApplyPermitFingerprint, } from './mission-git-contracts.js';
3
+ export class MissionProcessGroupTerminator {
4
+ options;
5
+ dependencies;
6
+ constructor(options, dependencies) {
7
+ this.options = options;
8
+ this.dependencies = dependencies;
9
+ requireText(options.hostId, 'terminator hostId');
10
+ requireText(options.bootNonce, 'terminator bootNonce');
11
+ if (!Number.isSafeInteger(options.graceMs) || options.graceMs < 0) {
12
+ throw new Error('Mission cancellation terminator graceMs must be a non-negative integer.');
13
+ }
14
+ }
15
+ async terminate(process) {
16
+ if (process.hostId !== this.options.hostId) {
17
+ return { kind: 'transient-failure', reason: 'process-owned-by-different-host' };
18
+ }
19
+ if (process.bootNonce !== this.options.bootNonce) {
20
+ return { kind: 'already-exited' };
21
+ }
22
+ if (await this.dependencies.observe(process) !== 'matching') {
23
+ return { kind: 'already-exited' };
24
+ }
25
+ this.dependencies.signalProcessGroup(process.pid, 'SIGTERM');
26
+ await this.dependencies.wait(this.options.graceMs);
27
+ if (await this.dependencies.observe(process) !== 'matching') {
28
+ return { kind: 'terminated' };
29
+ }
30
+ this.dependencies.signalProcessGroup(process.pid, 'SIGKILL');
31
+ await this.dependencies.wait(this.options.graceMs);
32
+ if (await this.dependencies.observe(process) !== 'matching') {
33
+ return { kind: 'terminated' };
34
+ }
35
+ return { kind: 'transient-failure', reason: 'process-group-still-live-after-sigkill' };
36
+ }
37
+ }
38
+ export class MissionCancellationCoordinator {
39
+ store;
40
+ dependencies;
41
+ constructor(store, dependencies) {
42
+ this.store = store;
43
+ this.dependencies = dependencies;
44
+ }
45
+ async cancel(input) {
46
+ assertTimestamp(input.requestedAt, 'requestedAt');
47
+ requireText(input.requestedBy, 'requestedBy');
48
+ const cancelling = await this.store.mutate(input.expectedGeneration, (draft) => {
49
+ const current = draft.missions[input.missionId];
50
+ if (!current)
51
+ throw new Error(`Mission cancellation cannot find ${input.missionId}.`);
52
+ if (current.revision !== input.expectedRevision) {
53
+ throw new Error(`Mission cancellation revision conflict for ${input.missionId}.`);
54
+ }
55
+ const next = current.state === 'cancelling'
56
+ ? { ...current, revision: current.revision + 1 }
57
+ : transitionMission(current, { type: 'cancel-requested' });
58
+ draft.missions[input.missionId] = {
59
+ ...next,
60
+ cancellation: {
61
+ requestedAt: current.cancellation?.requestedAt ?? input.requestedAt,
62
+ requestedBy: current.cancellation?.requestedBy ?? input.requestedBy,
63
+ },
64
+ };
65
+ });
66
+ const mission = cancelling.missions[input.missionId];
67
+ for (const process of mission.claim?.processes ?? []) {
68
+ const termination = await this.dependencies.terminate(process);
69
+ if (termination.kind === 'transient-failure') {
70
+ return this.deferCancellation(cancelling.generation, input.missionId, mission.revision, termination.reason);
71
+ }
72
+ }
73
+ let reconciledReceipt;
74
+ if (mission.applyIntent || mission.applyReceipt) {
75
+ const reconciliation = await this.dependencies.reconcileApply(structuredClone(mission));
76
+ if (reconciliation.kind === 'transient-failure') {
77
+ return this.deferCancellation(cancelling.generation, input.missionId, mission.revision, reconciliation.reason);
78
+ }
79
+ if (reconciliation.kind === 'safety-stop') {
80
+ return this.persistSafetyStop(cancelling.generation, input.missionId, mission.revision, reconciliation.reason);
81
+ }
82
+ if (reconciliation.kind === 'old-identity') {
83
+ if (mission.applyReceipt) {
84
+ return this.persistSafetyStop(cancelling.generation, input.missionId, mission.revision, 'receipt-ref-mismatch');
85
+ }
86
+ }
87
+ else if (!validReconciledReceipt(mission, reconciliation.receipt)) {
88
+ return this.persistSafetyStop(cancelling.generation, input.missionId, mission.revision, 'apply-reconciliation-receipt-mismatch');
89
+ }
90
+ else {
91
+ reconciledReceipt = reconciliation.receipt;
92
+ }
93
+ }
94
+ const completed = await this.completeCancellation(cancelling.generation, input.missionId, mission.revision, reconciledReceipt);
95
+ if (completed.missions[input.missionId]?.state !== 'cancelled') {
96
+ throw new Error('Mission cancellation did not reach cancelled state.');
97
+ }
98
+ return { kind: 'cancelled', missionId: input.missionId, state: 'cancelled' };
99
+ }
100
+ completeCancellation(expectedGeneration, missionId, expectedRevision, receipt) {
101
+ return this.store.mutate(expectedGeneration, (draft) => {
102
+ const current = draft.missions[missionId];
103
+ if (!current || current.revision !== expectedRevision || current.state !== 'cancelling') {
104
+ throw new Error(`Mission cancellation reconciliation conflict for ${missionId}.`);
105
+ }
106
+ if (receipt && !current.applyHistory?.some((entry) => entry.permitFingerprint === receipt.permitFingerprint)) {
107
+ current.applyHistory = [...(current.applyHistory ?? []), structuredClone(receipt)];
108
+ }
109
+ draft.missions[missionId] = transitionMission(current, {
110
+ type: 'cancellation-reconciled',
111
+ });
112
+ delete draft.nextEligibleAt[missionId];
113
+ delete draft.reservations[missionId];
114
+ });
115
+ }
116
+ async deferCancellation(expectedGeneration, missionId, expectedRevision, reason) {
117
+ const nextEligibleAt = this.dependencies.nextEligibleAt(reason);
118
+ assertTimestamp(nextEligibleAt, 'nextEligibleAt');
119
+ await this.store.mutate(expectedGeneration, (draft) => {
120
+ const current = draft.missions[missionId];
121
+ if (!current || current.revision !== expectedRevision || current.state !== 'cancelling'
122
+ || !current.claim) {
123
+ throw new Error(`Mission cancellation retry cannot fence ${missionId}.`);
124
+ }
125
+ current.claim.leaseUntil = nextEligibleAt;
126
+ current.revision += 1;
127
+ draft.nextEligibleAt[missionId] = nextEligibleAt;
128
+ });
129
+ return {
130
+ kind: 'resumable',
131
+ missionId,
132
+ state: 'cancelling',
133
+ reason,
134
+ nextEligibleAt,
135
+ };
136
+ }
137
+ async persistSafetyStop(expectedGeneration, missionId, expectedRevision, reason) {
138
+ requireText(reason, 'safety-stop reason');
139
+ await this.store.mutate(expectedGeneration, (draft) => {
140
+ const current = draft.missions[missionId];
141
+ if (!current || current.revision !== expectedRevision || current.state !== 'cancelling') {
142
+ throw new Error(`Mission cancellation safety-stop cannot fence ${missionId}.`);
143
+ }
144
+ draft.missions[missionId] = transitionMission(current, {
145
+ type: 'apply-reconciled-third-identity',
146
+ });
147
+ delete draft.nextEligibleAt[missionId];
148
+ delete draft.reservations[missionId];
149
+ });
150
+ return { kind: 'safety-stop', missionId, state: 'safety-stop', reason };
151
+ }
152
+ }
153
+ function validReconciledReceipt(mission, receipt) {
154
+ try {
155
+ assertMissionApplyReceipt(receipt);
156
+ }
157
+ catch {
158
+ return false;
159
+ }
160
+ const permit = mission.applyPermit;
161
+ const intent = mission.applyIntent;
162
+ return Boolean(permit && intent
163
+ && intent.permitFingerprint === missionApplyPermitFingerprint(permit)
164
+ && receipt.permitFingerprint === intent.permitFingerprint
165
+ && receipt.targetRef === permit.targetRef
166
+ && receipt.oldCommitSha === permit.expectedOldCommit
167
+ && receipt.commitSha === permit.expectedNewCommit
168
+ && receipt.treeSha === permit.expectedNewTree);
169
+ }
170
+ function assertTimestamp(value, field) {
171
+ const milliseconds = Date.parse(value);
172
+ if (!Number.isFinite(milliseconds) || new Date(milliseconds).toISOString() !== value) {
173
+ throw new Error(`Mission cancellation ${field} must be an exact UTC ISO timestamp.`);
174
+ }
175
+ }
176
+ function requireText(value, field) {
177
+ if (value.trim().length === 0)
178
+ throw new Error(`Mission cancellation ${field} must be non-empty.`);
179
+ }
180
+ //# sourceMappingURL=mission-cancellation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mission-cancellation.js","sourceRoot":"","sources":["../../../src/runner/mission-cancellation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ export interface CanonicalFileIdentity {
2
+ repositoryPath: string;
3
+ canonicalPath: string;
4
+ dev: number;
5
+ ino: number;
6
+ mode: number;
7
+ size: number;
8
+ mtimeMs: number;
9
+ sha256: string;
10
+ }
11
+ export interface CanonicalFileInspectionDependencies {
12
+ beforeRead?: () => Promise<void>;
13
+ }
14
+ export declare function readCanonicalText(input: {
15
+ root: string;
16
+ path: string;
17
+ grantedPaths: string[];
18
+ deniedPaths: string[];
19
+ maxBytes: number;
20
+ }): Promise<{
21
+ text: string;
22
+ identity: CanonicalFileIdentity;
23
+ }>;
24
+ export declare function inspectCanonicalFile(input: {
25
+ root: string;
26
+ path: string;
27
+ deniedPaths: string[];
28
+ maxBytes?: number;
29
+ missing?: 'allow' | 'reject';
30
+ }, dependencies?: CanonicalFileInspectionDependencies): Promise<{
31
+ identity: CanonicalFileIdentity;
32
+ content: Buffer;
33
+ } | undefined>;
34
+ //# sourceMappingURL=mission-canonical-path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mission-canonical-path.d.ts","sourceRoot":"","sources":["../../../src/runner/mission-canonical-path.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,qBAAqB;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mCAAmC;IAClD,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAClC;AAED,wBAAsB,iBAAiB,CAAC,KAAK,EAAE;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,qBAAqB,CAAA;CAAE,CAAC,CAiB7D;AAED,wBAAsB,oBAAoB,CAAC,KAAK,EAAE;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC9B,EAAE,YAAY,GAAE,mCAAwC,GAAG,OAAO,CAAC;IAClE,QAAQ,EAAE,qBAAqB,CAAC;IAChC,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,SAAS,CAAC,CAkEb"}
@@ -0,0 +1,121 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { constants } from 'node:fs';
3
+ import { lstat, open, realpath, stat } from 'node:fs/promises';
4
+ import { isAbsolute, join, relative } from 'node:path';
5
+ import { globMatches, normalizePath } from '../path-policy.js';
6
+ import { missionPathDenied } from './mission-path-language.js';
7
+ export async function readCanonicalText(input) {
8
+ const normalized = assertRepositoryRelativePath(input.path);
9
+ if (!input.grantedPaths.some((pattern) => globMatches(pattern, normalized))) {
10
+ throw new Error(`Mission observation path is outside granted scope: ${input.path}.`);
11
+ }
12
+ const inspected = await inspectCanonicalFile({
13
+ root: input.root,
14
+ path: input.path,
15
+ deniedPaths: input.deniedPaths,
16
+ maxBytes: input.maxBytes,
17
+ missing: 'reject',
18
+ });
19
+ if (!inspected)
20
+ throw new Error(`Mission observation file is missing: ${input.path}.`);
21
+ if (inspected.content.includes(0)) {
22
+ throw new Error(`Mission observation refuses binary file: ${input.path}.`);
23
+ }
24
+ return { text: inspected.content.toString('utf8'), identity: inspected.identity };
25
+ }
26
+ export async function inspectCanonicalFile(input, dependencies = {}) {
27
+ const normalized = assertRepositoryRelativePath(input.path);
28
+ if (missionPathDenied(normalized, input.deniedPaths)) {
29
+ throw new Error(`Mission canonical path is denied: ${normalized}.`);
30
+ }
31
+ const canonicalRoot = await realpath(input.root);
32
+ let current = canonicalRoot;
33
+ for (const segment of normalized.split('/')) {
34
+ current = join(current, segment);
35
+ let info;
36
+ try {
37
+ info = await lstat(current);
38
+ }
39
+ catch (error) {
40
+ if (error instanceof Error && 'code' in error && error.code === 'ENOENT'
41
+ && input.missing !== 'reject')
42
+ return undefined;
43
+ throw error;
44
+ }
45
+ if (info.isSymbolicLink()) {
46
+ throw new Error(`Mission canonical path refuses symbolic link: ${normalized}.`);
47
+ }
48
+ }
49
+ const canonicalTarget = await realpath(current);
50
+ const canonicalRelative = normalizePath(relative(canonicalRoot, canonicalTarget));
51
+ if (canonicalRelative.startsWith('../') || canonicalRelative === '..' || isAbsolute(canonicalRelative)) {
52
+ throw new Error(`Mission canonical path escapes repository root: ${normalized}.`);
53
+ }
54
+ if (missionPathDenied(canonicalRelative, input.deniedPaths)) {
55
+ throw new Error(`Mission canonical path is denied: ${canonicalRelative}.`);
56
+ }
57
+ const expected = await stat(canonicalTarget, { bigint: true });
58
+ assertRegularSingleLink(expected, normalized);
59
+ const handle = await open(current, constants.O_RDONLY | constants.O_NOFOLLOW);
60
+ try {
61
+ const opened = await handle.stat({ bigint: true });
62
+ assertSameIdentity(expected, opened, normalized);
63
+ assertStableMetadata(expected, opened, normalized);
64
+ assertRegularSingleLink(opened, normalized);
65
+ const maxBytes = input.maxBytes ?? 64 * 1024 * 1024;
66
+ if (!Number.isSafeInteger(maxBytes) || maxBytes <= 0 || opened.size > BigInt(maxBytes)) {
67
+ throw new Error(`Mission canonical path size limit exceeded: ${opened.size} > ${maxBytes}.`);
68
+ }
69
+ await dependencies.beforeRead?.();
70
+ const content = await handle.readFile();
71
+ const after = await handle.stat({ bigint: true });
72
+ assertSameIdentity(opened, after, normalized);
73
+ assertStableMetadata(opened, after, normalized);
74
+ assertRegularSingleLink(after, normalized);
75
+ if (after.size !== BigInt(content.byteLength)) {
76
+ throw new Error(`Mission canonical path changed length during read: ${normalized}.`);
77
+ }
78
+ return {
79
+ content,
80
+ identity: {
81
+ repositoryPath: canonicalRelative,
82
+ canonicalPath: canonicalTarget,
83
+ dev: Number(after.dev),
84
+ ino: Number(after.ino),
85
+ mode: Number(after.mode),
86
+ size: Number(after.size),
87
+ mtimeMs: Number(after.mtimeNs) / 1_000_000,
88
+ sha256: createHash('sha256').update(content).digest('hex'),
89
+ },
90
+ };
91
+ }
92
+ finally {
93
+ await handle.close();
94
+ }
95
+ }
96
+ function assertRepositoryRelativePath(path) {
97
+ const normalized = normalizePath(path);
98
+ if (isAbsolute(path) || normalized.length === 0
99
+ || normalized.split('/').some((segment) => segment.length === 0 || segment === '..')) {
100
+ throw new Error('Mission canonical path must be repository-relative without empty segments.');
101
+ }
102
+ return normalized;
103
+ }
104
+ function assertRegularSingleLink(info, path) {
105
+ if (!info.isFile() || (typeof info.nlink === 'bigint' ? info.nlink !== 1n : info.nlink !== 1)) {
106
+ throw new Error(`Mission canonical path refuses non-regular or hard-linked file: ${path}.`);
107
+ }
108
+ }
109
+ function assertSameIdentity(expected, actual, path) {
110
+ if (expected.dev !== actual.dev || expected.ino !== actual.ino) {
111
+ throw new Error(`Mission canonical path changed during authorization: ${path}.`);
112
+ }
113
+ }
114
+ function assertStableMetadata(expected, actual, path) {
115
+ if (expected.mode !== actual.mode || expected.nlink !== actual.nlink
116
+ || expected.size !== actual.size || expected.mtimeNs !== actual.mtimeNs
117
+ || expected.ctimeNs !== actual.ctimeNs) {
118
+ throw new Error(`Mission canonical path changed during read: ${path}.`);
119
+ }
120
+ }
121
+ //# sourceMappingURL=mission-canonical-path.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mission-canonical-path.js","sourceRoot":"","sources":["../../../src/runner/mission-canonical-path.ts"],"names":[],"mappings":"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"}