deepline 0.3.90 → 0.3.92

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 (87) hide show
  1. package/dist/bundling-sources/sdk/src/client.ts +101 -6
  2. package/dist/bundling-sources/sdk/src/monitor-fleet-contract.ts +2 -556
  3. package/dist/bundling-sources/sdk/src/monitor-fleets.ts +2 -2
  4. package/dist/bundling-sources/sdk/src/release.ts +7 -12
  5. package/dist/bundling-sources/sdk/src/types.ts +6 -1
  6. package/dist/bundling-sources/shared_libs/integration-contracts/monitor-fleet-contract.ts +549 -0
  7. package/dist/bundling-sources/shared_libs/monitors/fleets/errors.ts +96 -0
  8. package/dist/bundling-sources/shared_libs/monitors/fleets/limits.ts +8 -0
  9. package/dist/bundling-sources/shared_libs/monitors/fleets/plan.ts +320 -0
  10. package/dist/bundling-sources/shared_libs/monitors/fleets/verdict.ts +106 -0
  11. package/dist/bundling-sources/shared_libs/monitors/http-error-response.ts +5 -1
  12. package/dist/bundling-sources/shared_libs/monitors/monitor-fleet-limits.ts +2 -12
  13. package/dist/bundling-sources/shared_libs/monitors/package.json +107 -5
  14. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-batch-operation.ts +12 -7
  15. package/dist/bundling-sources/shared_libs/observability/scheduled-jobs.json +48 -0
  16. package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +1 -1
  17. package/dist/bundling-sources/shared_libs/play-runtime/db-session-lease-store.ts +578 -0
  18. package/dist/bundling-sources/shared_libs/play-runtime/deployment/commands/roll-execution-relay.ts +521 -0
  19. package/dist/bundling-sources/shared_libs/play-runtime/deployment/commands/roll-receipt-gateway.ts +1378 -0
  20. package/dist/bundling-sources/shared_libs/play-runtime/deployment/commands/runtime-topology.ts +1024 -0
  21. package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-gateway-route-probe.ts +193 -0
  22. package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-machine-lifecycle.ts +104 -0
  23. package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-machines.ts +1844 -0
  24. package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-registry.ts +21 -0
  25. package/dist/bundling-sources/shared_libs/play-runtime/deployment/process-tree.ts +40 -0
  26. package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-gateway-drain.ts +262 -0
  27. package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-surface.ts +98 -0
  28. package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-topology.ts +303 -0
  29. package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-tuple-publication-guardian.ts +70 -0
  30. package/dist/bundling-sources/shared_libs/play-runtime/deployment/runtime-tuple-recovery.ts +519 -0
  31. package/dist/bundling-sources/shared_libs/play-runtime/deployment/vercel-fly-rollback-alignment.ts +280 -0
  32. package/dist/bundling-sources/shared_libs/play-runtime/deployment/vercel-production-gate.ts +159 -0
  33. package/dist/bundling-sources/shared_libs/play-runtime/integration-operation-reader-fence/memory-store.ts +114 -0
  34. package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +44 -5
  35. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/child-play-resolver.ts +26 -0
  36. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/index.ts +3662 -0
  37. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/progress-sidecar.ts +47 -0
  38. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-deadline.ts +84 -0
  39. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-gateway-transport.ts +91 -0
  40. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-heartbeat.ts +424 -0
  41. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-progress-push.ts +321 -0
  42. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-sheet-persistence-retry.ts +110 -0
  43. package/dist/bundling-sources/shared_libs/play-runtime/runner-app/runtime-terminal-push.ts +625 -0
  44. package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +46 -3
  45. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/receipt-gateway.ts +1 -1
  46. package/dist/bundling-sources/shared_libs/play-runtime/webhook-receipt-retry.ts +50 -0
  47. package/dist/bundling-sources/shared_libs/plays/bundling/repository-adapter.ts +24 -0
  48. package/dist/bundling-sources/shared_libs/plays/child-run-projection.ts +174 -0
  49. package/dist/bundling-sources/shared_libs/plays/deploy/api-client.ts +267 -0
  50. package/dist/bundling-sources/shared_libs/plays/deploy/control-plane-types.ts +73 -0
  51. package/dist/bundling-sources/shared_libs/plays/deploy/manifest.ts +162 -0
  52. package/dist/bundling-sources/shared_libs/plays/deploy/project-serializer.ts +87 -0
  53. package/dist/bundling-sources/shared_libs/plays/deploy/source-file-path.ts +26 -0
  54. package/dist/bundling-sources/shared_libs/plays/deploy/target-paths.ts +36 -0
  55. package/dist/bundling-sources/shared_libs/plays/launch/input-contract.ts +400 -0
  56. package/dist/bundling-sources/shared_libs/plays/launch/prepare.ts +96 -0
  57. package/dist/bundling-sources/shared_libs/plays/local-file-discovery.ts +385 -0
  58. package/dist/bundling-sources/shared_libs/plays/play-grep-search.ts +79 -6
  59. package/dist/bundling-sources/shared_libs/plays/play-reference.ts +117 -0
  60. package/dist/bundling-sources/shared_libs/plays/run-input-summary.ts +260 -0
  61. package/dist/bundling-sources/shared_libs/plays/run-summary-types.ts +49 -0
  62. package/dist/bundling-sources/shared_libs/plays/secrets/errors.ts +19 -0
  63. package/dist/bundling-sources/shared_libs/plays/sheet-filter.ts +347 -0
  64. package/dist/bundling-sources/shared_libs/plays/standard-webhooks.ts +117 -0
  65. package/dist/bundling-sources/shared_libs/plays/status-result.ts +388 -0
  66. package/dist/bundling-sources/shared_libs/plays/transform-factor-structure.ts +377 -0
  67. package/dist/bundling-sources/shared_libs/plays/webhook-payload.ts +113 -0
  68. package/dist/cli/index.js +369 -162
  69. package/dist/cli/index.mjs +369 -162
  70. package/dist/{compiler-manifest-BaJfE-5H.d.mts → compiler-manifest-x14KL83E.d.mts} +1 -1
  71. package/dist/{compiler-manifest-BaJfE-5H.d.ts → compiler-manifest-x14KL83E.d.ts} +1 -1
  72. package/dist/index.d.mts +45 -8
  73. package/dist/index.d.ts +45 -8
  74. package/dist/index.js +61 -14
  75. package/dist/index.mjs +61 -14
  76. package/dist/install-integrity.json +54 -2
  77. package/dist/monitor-fleet-contract.d.mts +3 -10
  78. package/dist/monitor-fleet-contract.d.ts +3 -10
  79. package/dist/monitor-fleet-contract.js +2 -0
  80. package/dist/monitor-fleet-contract.mjs +1 -1
  81. package/dist/plays/bundle-play-file.d.mts +2 -2
  82. package/dist/plays/bundle-play-file.d.ts +2 -2
  83. package/dist/release.d.mts +3 -3
  84. package/dist/release.d.ts +3 -3
  85. package/dist/release.js +7 -12
  86. package/dist/release.mjs +7 -12
  87. package/package.json +2 -2
@@ -0,0 +1,320 @@
1
+ /**
2
+ * The whole fleet planning decision, as pure functions.
3
+ *
4
+ * Everything here is a function of values the caller already read: the source
5
+ * frontier, the monitors the fleet owns, and the organization's limits. No
6
+ * Convex, no Customer DB, no provider. That is deliberate - every membership
7
+ * bug this feature has had lived in these rules, and each one used to cost a
8
+ * preview deploy to see. Here they cost a millisecond.
9
+ *
10
+ * The output is the `members` array `providerMonitorFleets:commitPlan` takes,
11
+ * verbatim. Planning decides; the commit publishes.
12
+ */
13
+
14
+ import { MONITOR_FLEET_REMOVAL_GRACE_MS } from '../../integration-contracts/monitor-fleet-contract';
15
+ import { FLEET_BLOCKED_BY } from './limits';
16
+ export { FLEET_BLOCKED_BY } from './limits';
17
+
18
+ // Rollout access is checked by convergence immediately before provider work,
19
+ // not by this pure planner: an admitted plan can outlive rollout access.
20
+
21
+ export type FleetMemberAction =
22
+ | 'create'
23
+ | 'update'
24
+ | 'remove'
25
+ | 'blocked'
26
+ | 'grace';
27
+
28
+ /** One monitor the fleet wants, fully resolved from its source row. */
29
+ export type DesiredFleetMember = {
30
+ sourceKey: string;
31
+ monitorKey: string;
32
+ /** The account this monitor watches. Null when the tool declares none. */
33
+ entityKey: string | null;
34
+ /** The rendered monitor definition: `{ key, tool, payload }`. */
35
+ definition: Record<string, unknown>;
36
+ desiredHash: string;
37
+ };
38
+
39
+ /** A monitor the fleet already owns, as Convex reports it. */
40
+ export type OwnedFleetMonitor = {
41
+ monitorKey: string;
42
+ sourceKey: string | null;
43
+ entityKey: string | null;
44
+ convergence:
45
+ | 'pending'
46
+ | 'submitting'
47
+ | 'rate_limited'
48
+ | 'settled'
49
+ | 'blocked'
50
+ | 'failed';
51
+ desiredHash: string | null;
52
+ appliedHash: string | null;
53
+ firstMissingAt: number | null;
54
+ };
55
+
56
+ export type PlannedFleetMember = {
57
+ monitorKey: string;
58
+ sourceKey: string;
59
+ entityKey?: string;
60
+ action: FleetMemberAction;
61
+ desiredHash?: string;
62
+ desiredDefinition?: Record<string, unknown>;
63
+ blockedBy?: string;
64
+ /** True when the commit must be given a monitor seed for this key. */
65
+ needsSeed: boolean;
66
+ };
67
+
68
+ export type FleetPlan = {
69
+ members: PlannedFleetMember[];
70
+ creates: number;
71
+ updates: number;
72
+ removes: number;
73
+ /** Refusals by the limit that caused them, e.g. `{'monitor.watched_entities': 3}`. */
74
+ blocked: Record<string, number>;
75
+ };
76
+
77
+ export type FleetMembershipMember = {
78
+ sourceKey: string;
79
+ firstMissingAt: number | null;
80
+ };
81
+
82
+ export type FleetMembershipDecision = {
83
+ /** Source keys the fleet holds after this plan, in selection order. */
84
+ selected: string[];
85
+ /** Slots held by members whose grace has not expired. */
86
+ reservedGraceSlots: number;
87
+ /** Members whose row vanished on this plan. Grace starts now. */
88
+ markMissing: string[];
89
+ /** Members whose grace has expired and are now removed. */
90
+ remove: string[];
91
+ };
92
+
93
+ /**
94
+ * Slots held by members whose source row has vanished but whose removal grace
95
+ * has not expired.
96
+ *
97
+ * Such a member is still a member: it keeps its monitor, and grace exists so a
98
+ * row that comes back is restored rather than recreated. Handing its vacancy
99
+ * to the next ranked candidate immediately would push the fleet past its own
100
+ * limit, and then the reappearance grace is designed to absorb would force an
101
+ * eviction to get back under it - exactly the churn stickiness promises not to
102
+ * cause. Reserve the slot until grace actually expires.
103
+ */
104
+ export function countReservedRemovalGraceSlots(input: {
105
+ members: readonly FleetMembershipMember[];
106
+ selectedSourceKeys: ReadonlySet<string>;
107
+ now: number;
108
+ }): number {
109
+ return input.members.filter((member) => {
110
+ if (input.selectedSourceKeys.has(member.sourceKey)) return false;
111
+ // Not yet marked missing means this plan is the one that starts grace.
112
+ return (
113
+ member.firstMissingAt === null ||
114
+ input.now - member.firstMissingAt < MONITOR_FLEET_REMOVAL_GRACE_MS
115
+ );
116
+ }).length;
117
+ }
118
+
119
+ /**
120
+ * Who the fleet holds after a plan, given what the source still returns.
121
+ *
122
+ * The whole sticky/grace/limit rule in one place: an active member keeps its
123
+ * slot while its row exists, a vanished member keeps its slot until grace
124
+ * expires, and ranking only ever fills what is genuinely free.
125
+ */
126
+ export function decideFleetMembership(input: {
127
+ limit: number;
128
+ members: readonly FleetMembershipMember[];
129
+ /** Member source keys whose row the source still returns. */
130
+ presentSourceKeys: ReadonlySet<string>;
131
+ /** Ranked non-member candidates, best first. */
132
+ candidates: readonly string[];
133
+ now: number;
134
+ }): FleetMembershipDecision {
135
+ const selected: string[] = [];
136
+ const selectedKeys = new Set<string>();
137
+ // Sticky: ranking only fills vacancies, so score movement cannot churn an
138
+ // active member out of the fleet.
139
+ for (const member of input.members) {
140
+ if (!input.presentSourceKeys.has(member.sourceKey)) continue;
141
+ if (selectedKeys.has(member.sourceKey)) continue;
142
+ selected.push(member.sourceKey);
143
+ selectedKeys.add(member.sourceKey);
144
+ }
145
+ const reservedGraceSlots = countReservedRemovalGraceSlots({
146
+ members: input.members,
147
+ selectedSourceKeys: selectedKeys,
148
+ now: input.now,
149
+ });
150
+ for (const candidate of input.candidates) {
151
+ if (selected.length + reservedGraceSlots >= input.limit) break;
152
+ if (selectedKeys.has(candidate)) continue;
153
+ selected.push(candidate);
154
+ selectedKeys.add(candidate);
155
+ }
156
+ const markMissing: string[] = [];
157
+ const remove: string[] = [];
158
+ for (const member of input.members) {
159
+ if (selectedKeys.has(member.sourceKey)) continue;
160
+ if (member.firstMissingAt === null) {
161
+ markMissing.push(member.sourceKey);
162
+ continue;
163
+ }
164
+ if (input.now - member.firstMissingAt < MONITOR_FLEET_REMOVAL_GRACE_MS) {
165
+ continue;
166
+ }
167
+ remove.push(member.sourceKey);
168
+ }
169
+ return { selected, reservedGraceSlots, markMissing, remove };
170
+ }
171
+
172
+ /** The account a monitor is counted against. Never null, so nothing is uncounted. */
173
+ export function fleetEntityKeyFor(input: {
174
+ monitorKey: string;
175
+ entityKey: string | null;
176
+ }): string {
177
+ // A monitor with no derivable account is its own account, so it still
178
+ // occupies exactly one unit of allowance rather than slipping through.
179
+ return input.entityKey ?? `monitor:${input.monitorKey}`;
180
+ }
181
+
182
+ export type FleetEntitlements = {
183
+ /** New accounts this organization may still open. */
184
+ watchedEntitiesRemaining: number;
185
+ /** Monitors admitted against any one account. */
186
+ monitorsPerEntity: number;
187
+ };
188
+
189
+ /**
190
+ * Turn "what the fleet wants" plus "what it already owns" into commit members.
191
+ *
192
+ * Ordering matters and is deliberate: creates are admitted in selection order,
193
+ * so a fleet at its account limit keeps the members the source ranked highest
194
+ * rather than whichever ones happened to sort first by key.
195
+ */
196
+ export function planFleetMembers(input: {
197
+ desired: readonly DesiredFleetMember[];
198
+ owned: readonly OwnedFleetMonitor[];
199
+ /** Source keys whose grace expired, from `decideFleetMembership`. */
200
+ remove: readonly string[];
201
+ /** Source keys whose row vanished on this plan. */
202
+ markMissing: readonly string[];
203
+ entitlements: FleetEntitlements;
204
+ /**
205
+ * Monitors already counted against an account, org-wide. An account absent
206
+ * from this map is one the organization does not watch yet, and opening it
207
+ * is what costs allowance.
208
+ */
209
+ entityMonitorCounts: ReadonlyMap<string, number>;
210
+ }): FleetPlan {
211
+ const ownedByKey = new Map(
212
+ input.owned.map((monitor) => [monitor.monitorKey, monitor]),
213
+ );
214
+ const ownedBySource = new Map<string, OwnedFleetMonitor>();
215
+ for (const monitor of input.owned) {
216
+ if (monitor.sourceKey) ownedBySource.set(monitor.sourceKey, monitor);
217
+ }
218
+
219
+ const entityCounts = new Map(input.entityMonitorCounts);
220
+ const heldEntities = new Set(
221
+ [...entityCounts.entries()]
222
+ .filter(([, count]) => count > 0)
223
+ .map(([entityKey]) => entityKey),
224
+ );
225
+ let newEntitiesUsed = 0;
226
+
227
+ const members: PlannedFleetMember[] = [];
228
+ const blocked: Record<string, number> = {};
229
+ let creates = 0;
230
+ let updates = 0;
231
+ let removes = 0;
232
+
233
+ const refuse = (member: PlannedFleetMember, blockedBy: string) => {
234
+ blocked[blockedBy] = (blocked[blockedBy] ?? 0) + 1;
235
+ members.push({ ...member, action: 'blocked', blockedBy });
236
+ };
237
+
238
+ for (const desired of input.desired) {
239
+ const owned =
240
+ ownedByKey.get(desired.monitorKey) ??
241
+ ownedBySource.get(desired.sourceKey);
242
+ const entityKey = fleetEntityKeyFor(desired);
243
+ const base: PlannedFleetMember = {
244
+ monitorKey: desired.monitorKey,
245
+ sourceKey: desired.sourceKey,
246
+ entityKey,
247
+ action: 'create',
248
+ desiredHash: desired.desiredHash,
249
+ desiredDefinition: desired.definition,
250
+ needsSeed: true,
251
+ };
252
+
253
+ const applied = owned?.appliedHash ?? null;
254
+ if (applied !== null) {
255
+ // The provider already holds this monitor. Nothing to admit against a
256
+ // limit: it is already counted, and an update never opens an account.
257
+ const settledOnDesired =
258
+ applied === desired.desiredHash &&
259
+ owned?.convergence === 'settled' &&
260
+ owned.firstMissingAt === null;
261
+ if (settledOnDesired) continue;
262
+ // A member whose row came back inside its grace window is restored by
263
+ // re-publishing the state it already holds. That clears the removal
264
+ // clock through the same guarded transition every other change uses,
265
+ // rather than needing a second write path that could disagree with it.
266
+ members.push({ ...base, action: 'update', needsSeed: false });
267
+ updates += 1;
268
+ continue;
269
+ }
270
+
271
+ const onEntity = entityCounts.get(entityKey) ?? 0;
272
+ if (onEntity >= input.entitlements.monitorsPerEntity) {
273
+ refuse(base, FLEET_BLOCKED_BY.monitorsPerEntity);
274
+ continue;
275
+ }
276
+ if (
277
+ !heldEntities.has(entityKey) &&
278
+ newEntitiesUsed >= input.entitlements.watchedEntitiesRemaining
279
+ ) {
280
+ refuse(base, FLEET_BLOCKED_BY.watchedEntities);
281
+ continue;
282
+ }
283
+ if (!heldEntities.has(entityKey)) {
284
+ heldEntities.add(entityKey);
285
+ newEntitiesUsed += 1;
286
+ }
287
+ entityCounts.set(entityKey, onEntity + 1);
288
+ members.push(base);
289
+ creates += 1;
290
+ }
291
+
292
+ const plannedKeys = new Set(members.map((member) => member.monitorKey));
293
+ for (const sourceKey of input.markMissing) {
294
+ const owned = ownedBySource.get(sourceKey);
295
+ if (!owned || plannedKeys.has(owned.monitorKey)) continue;
296
+ members.push({
297
+ monitorKey: owned.monitorKey,
298
+ sourceKey,
299
+ ...(owned.entityKey ? { entityKey: owned.entityKey } : {}),
300
+ action: 'grace',
301
+ needsSeed: false,
302
+ });
303
+ plannedKeys.add(owned.monitorKey);
304
+ }
305
+ for (const sourceKey of input.remove) {
306
+ const owned = ownedBySource.get(sourceKey);
307
+ if (!owned || plannedKeys.has(owned.monitorKey)) continue;
308
+ members.push({
309
+ monitorKey: owned.monitorKey,
310
+ sourceKey,
311
+ ...(owned.entityKey ? { entityKey: owned.entityKey } : {}),
312
+ action: 'remove',
313
+ needsSeed: false,
314
+ });
315
+ plannedKeys.add(owned.monitorKey);
316
+ removes += 1;
317
+ }
318
+
319
+ return { members, creates, updates, removes, blocked };
320
+ }
@@ -0,0 +1,106 @@
1
+ /**
2
+ * One place that decides what a fleet's convergence counts mean.
3
+ *
4
+ * The verdict is server-owned on purpose. Every surface that has ever shown a
5
+ * fleet - the CLI, the API, a dashboard - has to answer the same question
6
+ * ("is this fleet done, and if not, is anything wrong?"), and each one that
7
+ * derives it from raw counts derives it slightly differently. The counts are
8
+ * the fact; this is the single reading of them.
9
+ */
10
+
11
+ export type FleetConvergenceCounts = {
12
+ pending: number;
13
+ submitting: number;
14
+ rate_limited: number;
15
+ settled: number;
16
+ blocked: number;
17
+ failed: number;
18
+ };
19
+
20
+ /** The lifecycle a fleet row records. */
21
+ export type FleetLifecycleStatus =
22
+ | 'active'
23
+ | 'deactivating'
24
+ | 'deactivated'
25
+ | 'billing_suspending'
26
+ | 'billing_suspended'
27
+ | 'billing_reactivating';
28
+
29
+ /** What a caller is told the fleet is doing. */
30
+ export type FleetStatusVerdict =
31
+ | 'converging'
32
+ | 'converged'
33
+ | 'degraded'
34
+ | 'deactivating'
35
+ | 'deactivated'
36
+ | 'billing_suspending'
37
+ | 'billing_suspended'
38
+ | 'billing_reactivating';
39
+
40
+ const ZERO_FLEET_COUNTS: FleetConvergenceCounts = {
41
+ pending: 0,
42
+ submitting: 0,
43
+ rate_limited: 0,
44
+ settled: 0,
45
+ blocked: 0,
46
+ failed: 0,
47
+ };
48
+
49
+ export function fleetCounts(
50
+ raw: Partial<FleetConvergenceCounts> | null | undefined,
51
+ ): FleetConvergenceCounts {
52
+ return { ...ZERO_FLEET_COUNTS, ...(raw ?? {}) };
53
+ }
54
+
55
+ /** Monitors a provider still owes an answer for. */
56
+ export function fleetOutstanding(counts: FleetConvergenceCounts): number {
57
+ return counts.pending + counts.submitting + counts.rate_limited;
58
+ }
59
+
60
+ /** Monitors this fleet expects to exist, including the ones it cannot create. */
61
+ export function fleetExpected(counts: FleetConvergenceCounts): number {
62
+ return (
63
+ counts.settled +
64
+ counts.pending +
65
+ counts.submitting +
66
+ counts.rate_limited +
67
+ counts.blocked +
68
+ counts.failed
69
+ );
70
+ }
71
+
72
+ /** Monitors the provider is confirmed to hold. */
73
+ export function fleetLive(counts: FleetConvergenceCounts): number {
74
+ return counts.settled;
75
+ }
76
+
77
+ /**
78
+ * The verdict.
79
+ *
80
+ * `converged` deliberately requires the published generation to be the one
81
+ * that converged. Outstanding-is-zero alone is equally true of a generation
82
+ * whose monitors have not been committed yet, and answering "converged" then
83
+ * tells a caller its change is live before any provider has seen it.
84
+ *
85
+ * `degraded` is the honest middle: nothing is in flight, so waiting longer
86
+ * changes nothing, but some monitors are blocked or failed and the fleet does
87
+ * not hold what it was asked to hold.
88
+ */
89
+ export function fleetStatusVerdict(input: {
90
+ status: FleetLifecycleStatus;
91
+ counts: FleetConvergenceCounts;
92
+ generation: number;
93
+ convergedGeneration: number | null;
94
+ }): FleetStatusVerdict {
95
+ if (input.status === 'deactivated') return 'deactivated';
96
+ if (input.status === 'deactivating') return 'deactivating';
97
+ if (input.status === 'billing_suspended') return 'billing_suspended';
98
+ if (input.status === 'billing_suspending') return 'billing_suspending';
99
+ if (input.status === 'billing_reactivating') return 'billing_reactivating';
100
+ const outstanding = fleetOutstanding(input.counts);
101
+ if (outstanding > 0) return 'converging';
102
+ if (input.counts.blocked + input.counts.failed > 0) return 'degraded';
103
+ return input.convergedGeneration === input.generation
104
+ ? 'converged'
105
+ : 'converging';
106
+ }
@@ -189,7 +189,11 @@ export function monitorErrorNextAction(payload: MonitorErrorPayload): string {
189
189
  return `This workspace already has ${MAX_PRIORITY_MONITORS_PER_ORG} priority radars. Delete or update an existing priority radar only after confirming it is no longer needed, then retry the urgent preview.`;
190
190
  }
191
191
  if (code === 'monitor_already_exists') {
192
- const existing = asRecord(nestedError(payload)?.existing_monitor)?.key;
192
+ const existingMonitor = asRecord(nestedError(payload)?.existing_monitor);
193
+ const existing = existingMonitor?.key;
194
+ if (existingMonitor?.status === 'provisioning') {
195
+ return 'An in-progress provider operation owns this identity. Do not deploy another key or delete and redeploy. Wait for it to finish, then rerun this check; if the conflict remains, contact Deepline support with the returned tool and identity.';
196
+ }
193
197
  return typeof existing === 'string'
194
198
  ? `Run \`deepline monitors get ${existing} --json\` to inspect the monitor that owns this identity. Reuse it or contact Deepline support before replacing it; do not delete and redeploy blindly.`
195
199
  : 'Inspect the returned existing_monitor.key with `deepline monitors get <key> --json`. Reuse that monitor or contact Deepline support before replacing it; do not delete and redeploy blindly.';
@@ -1,19 +1,9 @@
1
1
  /**
2
2
  * How large one Monitor Fleet may be.
3
3
  *
4
- * This lives in `shared_libs` rather than in the SDK's fleet authoring contract
5
- * because Convex needs it too: the commit mutation sizes its own transaction
6
- * against the same number the authoring contract validates a fleet's selection
7
- * limit against. Two copies of that number would drift, and the drift would
8
- * only be visible as a mutation that refuses a plan the SDK admitted.
9
- *
10
- * `packages/sdk/src/monitor-fleet-contract.ts` cannot import this file - the
11
- * workspace-architecture ratchet forbids new `shared_libs` edges from packages
12
- * - so it mirrors the literal, and `tests/lib/monitors/monitor-fleet-limits-
13
- * mirror.test.ts` holds the two equal by name. Everything outside Convex reads
14
- * the contract module's copy and never learns this file exists.
4
+ * Convex transaction sizing and SDK admission use the same canonical limit.
15
5
  */
16
- export const MONITOR_FLEET_MAX_MEMBERS = 1_000;
6
+ export { MONITOR_FLEET_MAX_MEMBERS } from '../integration-contracts/monitor-fleet-contract';
17
7
 
18
8
  /**
19
9
  * A claim may carry at most this many independent (organization, fleet)
@@ -4,10 +4,111 @@
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "exports": {
7
- "./billing/*": "./billing/*.ts",
8
- "./signal-radars/*": "./signal-radars/*.ts",
9
- "./*.js": "./*.ts",
10
- "./*": "./*.ts"
7
+ "./fleets/limits": "./fleets/limits.ts",
8
+ "./analytics-install-guides": "./analytics-install-guides.ts",
9
+ "./analytics-install-guides.js": "./analytics-install-guides.ts",
10
+ "./analytics-public": "./analytics-public.ts",
11
+ "./analytics-public.js": "./analytics-public.ts",
12
+ "./billing/charges": "./billing/charges.ts",
13
+ "./billing/charges.js": "./billing/charges.ts",
14
+ "./billing/policy-builders": "./billing/policy-builders.ts",
15
+ "./billing/policy-builders.js": "./billing/policy-builders.ts",
16
+ "./billing/pricing": "./billing/pricing.ts",
17
+ "./billing/pricing.js": "./billing/pricing.ts",
18
+ "./billing/public-pricing": "./billing/public-pricing.ts",
19
+ "./billing/public-pricing.js": "./billing/public-pricing.ts",
20
+ "./billing/session": "./billing/session.ts",
21
+ "./billing/session.js": "./billing/session.ts",
22
+ "./billing/state": "./billing/state.ts",
23
+ "./billing/state.js": "./billing/state.ts",
24
+ "./billing/tamradar": "./billing/tamradar.ts",
25
+ "./billing/tamradar.js": "./billing/tamradar.ts",
26
+ "./billing/types": "./billing/types.ts",
27
+ "./billing/types.js": "./billing/types.ts",
28
+ "./callback-security": "./callback-security.ts",
29
+ "./callback-security.js": "./callback-security.ts",
30
+ "./cursor-page": "./cursor-page.ts",
31
+ "./cursor-page.js": "./cursor-page.ts",
32
+ "./deactivation-reasons": "./deactivation-reasons.ts",
33
+ "./deactivation-reasons.js": "./deactivation-reasons.ts",
34
+ "./fleets/errors": "./fleets/errors.ts",
35
+ "./fleets/errors.js": "./fleets/errors.ts",
36
+ "./fleets/plan": "./fleets/plan.ts",
37
+ "./fleets/plan.js": "./fleets/plan.ts",
38
+ "./fleets/verdict": "./fleets/verdict.ts",
39
+ "./fleets/verdict.js": "./fleets/verdict.ts",
40
+ "./framework-managed-payload-fields": "./framework-managed-payload-fields.ts",
41
+ "./framework-managed-payload-fields.js": "./framework-managed-payload-fields.ts",
42
+ "./generated/provider-monitor-callback-admission-targets": "./generated/provider-monitor-callback-admission-targets.ts",
43
+ "./generated/provider-monitor-callback-admission-targets.js": "./generated/provider-monitor-callback-admission-targets.ts",
44
+ "./http-error-response": "./http-error-response.ts",
45
+ "./http-error-response.js": "./http-error-response.ts",
46
+ "./inbound-publish-key": "./inbound-publish-key.ts",
47
+ "./inbound-publish-key.js": "./inbound-publish-key.ts",
48
+ "./inbound-queue": "./inbound-queue.ts",
49
+ "./inbound-queue.js": "./inbound-queue.ts",
50
+ "./inventory-reconciliation": "./inventory-reconciliation.ts",
51
+ "./inventory-reconciliation.js": "./inventory-reconciliation.ts",
52
+ "./lifecycle-notifications": "./lifecycle-notifications.ts",
53
+ "./lifecycle-notifications.js": "./lifecycle-notifications.ts",
54
+ "./monitor-fleet-limits": "./monitor-fleet-limits.ts",
55
+ "./monitor-fleet-limits.js": "./monitor-fleet-limits.ts",
56
+ "./org-monitor-limits": "./org-monitor-limits.ts",
57
+ "./org-monitor-limits.js": "./org-monitor-limits.ts",
58
+ "./provider-callback-verification": "./provider-callback-verification.ts",
59
+ "./provider-callback-verification.js": "./provider-callback-verification.ts",
60
+ "./provider-monitor-batch-operation": "./provider-monitor-batch-operation.ts",
61
+ "./provider-monitor-batch-operation.js": "./provider-monitor-batch-operation.ts",
62
+ "./provider-monitor-capability": "./provider-monitor-capability.ts",
63
+ "./provider-monitor-capability.js": "./provider-monitor-capability.ts",
64
+ "./provider-monitor-consumer-health": "./provider-monitor-consumer-health.ts",
65
+ "./provider-monitor-consumer-health.js": "./provider-monitor-consumer-health.ts",
66
+ "./provider-monitor-errors": "./provider-monitor-errors.ts",
67
+ "./provider-monitor-errors.js": "./provider-monitor-errors.ts",
68
+ "./provider-monitor-identity-claims": "./provider-monitor-identity-claims.ts",
69
+ "./provider-monitor-identity-claims.js": "./provider-monitor-identity-claims.ts",
70
+ "./provider-monitor-legacy-aliases": "./provider-monitor-legacy-aliases.ts",
71
+ "./provider-monitor-legacy-aliases.js": "./provider-monitor-legacy-aliases.ts",
72
+ "./provider-monitor-priority-execution": "./provider-monitor-priority-execution.ts",
73
+ "./provider-monitor-priority-execution.js": "./provider-monitor-priority-execution.ts",
74
+ "./provider-monitor-public-aliases": "./provider-monitor-public-aliases.ts",
75
+ "./provider-monitor-public-aliases.js": "./provider-monitor-public-aliases.ts",
76
+ "./provider-monitor-runtime-read-eligibility": "./provider-monitor-runtime-read-eligibility.ts",
77
+ "./provider-monitor-runtime-read-eligibility.js": "./provider-monitor-runtime-read-eligibility.ts",
78
+ "./provider-monitor-state": "./provider-monitor-state.ts",
79
+ "./provider-monitor-state.js": "./provider-monitor-state.ts",
80
+ "./provider-monitor-status": "./provider-monitor-status.ts",
81
+ "./provider-monitor-status.js": "./provider-monitor-status.ts",
82
+ "./provider-monitor-upstream-identity": "./provider-monitor-upstream-identity.ts",
83
+ "./provider-monitor-upstream-identity.js": "./provider-monitor-upstream-identity.ts",
84
+ "./provider-webhook-monitor-runtime": "./provider-webhook-monitor-runtime.ts",
85
+ "./provider-webhook-monitor-runtime.js": "./provider-webhook-monitor-runtime.ts",
86
+ "./quickstart-prompts": "./quickstart-prompts.ts",
87
+ "./quickstart-prompts.js": "./quickstart-prompts.ts",
88
+ "./signal-radars/inbound-dead-letter": "./signal-radars/inbound-dead-letter.ts",
89
+ "./signal-radars/inbound-dead-letter.js": "./signal-radars/inbound-dead-letter.ts",
90
+ "./signal-radars/inbound-message": "./signal-radars/inbound-message.ts",
91
+ "./signal-radars/inbound-message.js": "./signal-radars/inbound-message.ts",
92
+ "./signal-radars/inbound-queue": "./signal-radars/inbound-queue.ts",
93
+ "./signal-radars/inbound-queue-ack-probe": "./signal-radars/inbound-queue-ack-probe.ts",
94
+ "./signal-radars/inbound-queue-ack-probe.js": "./signal-radars/inbound-queue-ack-probe.ts",
95
+ "./signal-radars/inbound-queue-delivery": "./signal-radars/inbound-queue-delivery.ts",
96
+ "./signal-radars/inbound-queue-delivery.js": "./signal-radars/inbound-queue-delivery.ts",
97
+ "./signal-radars/inbound-queue.js": "./signal-radars/inbound-queue.ts",
98
+ "./snitcher-install-validation": "./snitcher-install-validation.ts",
99
+ "./snitcher-install-validation.js": "./snitcher-install-validation.ts",
100
+ "./snitcher-ip2company": "./snitcher-ip2company.ts",
101
+ "./snitcher-ip2company.js": "./snitcher-ip2company.ts",
102
+ "./snitcher-radar": "./snitcher-radar.ts",
103
+ "./snitcher-radar.js": "./snitcher-radar.ts",
104
+ "./starter-templates": "./starter-templates.ts",
105
+ "./starter-templates.js": "./starter-templates.ts",
106
+ "./tracking-provider-guidance": "./tracking-provider-guidance.ts",
107
+ "./tracking-provider-guidance.js": "./tracking-provider-guidance.ts",
108
+ "./tracking-provider-integrations": "./tracking-provider-integrations.ts",
109
+ "./tracking-provider-integrations.js": "./tracking-provider-integrations.ts",
110
+ "./validation": "./validation.ts",
111
+ "./validation.js": "./validation.ts"
11
112
  },
12
113
  "scripts": {
13
114
  "build": "tsc -p tsconfig.json --noEmit",
@@ -25,7 +126,8 @@
25
126
  "@vercel/queue": "^0.4.0",
26
127
  "next": "16.3.0-canary.60",
27
128
  "pg": "^8.19.0",
28
- "react": "19.2.5"
129
+ "react": "19.2.5",
130
+ "@deepline/integration-contracts": "workspace:*"
29
131
  },
30
132
  "devDependencies": {
31
133
  "@types/node": "^20.0.0",
@@ -17,6 +17,10 @@ export const PROVIDER_MONITOR_BATCH_OPERATION_KINDS = [
17
17
  'reactivate',
18
18
  'audit',
19
19
  'repair',
20
+ // Fleet plans already write canonical desired state. This internal
21
+ // operation is the durable, observable hand-off to the fenced Fleet
22
+ // executor; it is not a second lifecycle verb or a raw provider create.
23
+ 'converge',
20
24
  ] as const;
21
25
 
22
26
  export type ProviderMonitorBatchOperationKind =
@@ -62,7 +66,7 @@ export type ProviderMonitorBatchOperationRequest = {
62
66
  * this origin instead of recomputing it from a later deployment.
63
67
  */
64
68
  callbackBaseUrl?: string;
65
- /** Required for deactivate; absent for every other operation. */
69
+ /** Required for direct/target deactivation; Fleet teardown may be customer-owned. */
66
70
  deactivationReason?: string;
67
71
  };
68
72
 
@@ -184,6 +188,9 @@ export function validateProviderMonitorBatchOperationRequest(
184
188
  'Batch deploy requires a durable manifest of complete definitions.',
185
189
  );
186
190
  }
191
+ if (request.kind === 'converge') {
192
+ throw new Error('Batch converge requires exactly one Fleet scope.');
193
+ }
187
194
  if (request.kind === 'deactivate' && !request.deactivationReason) {
188
195
  throw new Error(
189
196
  'Batch deactivate requires a declared deactivation reason.',
@@ -204,6 +211,9 @@ export function validateProviderMonitorBatchOperationRequest(
204
211
  'Batch deploy requires a durable manifest of complete definitions.',
205
212
  );
206
213
  }
214
+ if (request.kind === 'converge') {
215
+ throw new Error('Batch converge requires exactly one Fleet scope.');
216
+ }
207
217
  if (!isProviderMonitorBatchTarget(scope.target)) {
208
218
  throw new Error(`Unknown monitor batch target '${scope.target}'.`);
209
219
  }
@@ -228,12 +238,7 @@ export function validateProviderMonitorBatchOperationRequest(
228
238
  }
229
239
  if (!scope.fleetId.trim())
230
240
  throw new Error('A Fleet scope requires fleetId.');
231
- if (request.kind === 'deactivate' && !request.deactivationReason) {
232
- throw new Error(
233
- 'Batch deactivate requires a declared deactivation reason.',
234
- );
235
- }
236
- if (request.kind === 'deactivate') {
241
+ if (request.kind === 'deactivate' && request.deactivationReason) {
237
242
  requireMonitorDeactivationReason(request.deactivationReason!);
238
243
  }
239
244
  return {