audit-tools 0.32.7 → 0.32.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +15 -0
- package/README.md +2 -0
- package/dist/audit/cli/dispatch/quotaPool.d.ts +20 -2
- package/dist/audit/cli/dispatch/quotaPool.d.ts.map +1 -1
- package/dist/audit/cli/dispatch/quotaPool.js +38 -7
- package/dist/audit/cli/dispatch/quotaPool.js.map +1 -1
- package/dist/audit/cli/dispatch/tierRouting.d.ts +4 -1
- package/dist/audit/cli/dispatch/tierRouting.d.ts.map +1 -1
- package/dist/audit/cli/dispatch/tierRouting.js +7 -4
- package/dist/audit/cli/dispatch/tierRouting.js.map +1 -1
- package/dist/audit/cli/dispatch/types.d.ts +9 -4
- package/dist/audit/cli/dispatch/types.d.ts.map +1 -1
- package/dist/audit/cli/dispatch.d.ts +7 -0
- package/dist/audit/cli/dispatch.d.ts.map +1 -1
- package/dist/audit/cli/dispatch.js +24 -12
- package/dist/audit/cli/dispatch.js.map +1 -1
- package/dist/audit/cli/mergeAndIngestCommand.d.ts +0 -9
- package/dist/audit/cli/mergeAndIngestCommand.d.ts.map +1 -1
- package/dist/audit/cli/mergeAndIngestCommand.js +29 -1
- package/dist/audit/cli/mergeAndIngestCommand.js.map +1 -1
- package/dist/audit/cli/prompts.d.ts.map +1 -1
- package/dist/audit/cli/prompts.js +4 -2
- package/dist/audit/cli/prompts.js.map +1 -1
- package/dist/audit/cli/rollingAuditDispatch.d.ts.map +1 -1
- package/dist/audit/cli/rollingAuditDispatch.js +3 -0
- package/dist/audit/cli/rollingAuditDispatch.js.map +1 -1
- package/dist/audit/cli/semanticReviewStep.d.ts.map +1 -1
- package/dist/audit/cli/semanticReviewStep.js +10 -6
- package/dist/audit/cli/semanticReviewStep.js.map +1 -1
- package/dist/audit/cli/steps.d.ts +20 -10
- package/dist/audit/cli/steps.d.ts.map +1 -1
- package/dist/audit/cli/steps.js +4 -2
- package/dist/audit/cli/steps.js.map +1 -1
- package/dist/audit/orchestrator/planningExecutors.d.ts.map +1 -1
- package/dist/audit/orchestrator/planningExecutors.js +1 -0
- package/dist/audit/orchestrator/planningExecutors.js.map +1 -1
- package/dist/audit/orchestrator/rollingDispatch.d.ts +7 -6
- package/dist/audit/orchestrator/rollingDispatch.d.ts.map +1 -1
- package/dist/audit/orchestrator/rollingDispatch.js +37 -19
- package/dist/audit/orchestrator/rollingDispatch.js.map +1 -1
- package/dist/audit/quota/index.d.ts +137 -8
- package/dist/audit/quota/index.d.ts.map +1 -1
- package/dist/audit/quota/index.js +9 -8
- package/dist/audit/quota/index.js.map +1 -1
- package/dist/remediate/contractPipeline/derive.d.ts +20 -0
- package/dist/remediate/contractPipeline/derive.d.ts.map +1 -1
- package/dist/remediate/contractPipeline/derive.js +91 -0
- package/dist/remediate/contractPipeline/derive.js.map +1 -1
- package/dist/remediate/dispatch/ownershipRegistry.d.ts +2 -22
- package/dist/remediate/dispatch/ownershipRegistry.d.ts.map +1 -1
- package/dist/remediate/dispatch/ownershipRegistry.js +7 -45
- package/dist/remediate/dispatch/ownershipRegistry.js.map +1 -1
- package/dist/remediate/index.d.ts.map +1 -1
- package/dist/remediate/index.js +17 -1
- package/dist/remediate/index.js.map +1 -1
- package/dist/remediate/phases/close.d.ts.map +1 -1
- package/dist/remediate/phases/close.js +3 -2
- package/dist/remediate/phases/close.js.map +1 -1
- package/dist/remediate/riskSignal.d.ts +6 -3
- package/dist/remediate/riskSignal.d.ts.map +1 -1
- package/dist/remediate/riskSignal.js +6 -3
- package/dist/remediate/riskSignal.js.map +1 -1
- package/dist/remediate/steps/contractPipeline.d.ts +1 -2
- package/dist/remediate/steps/contractPipeline.d.ts.map +1 -1
- package/dist/remediate/steps/contractPipeline.js +80 -106
- package/dist/remediate/steps/contractPipeline.js.map +1 -1
- package/dist/remediate/steps/dispatch.d.ts +22 -1
- package/dist/remediate/steps/dispatch.d.ts.map +1 -1
- package/dist/remediate/steps/dispatch.js +108 -5
- package/dist/remediate/steps/dispatch.js.map +1 -1
- package/dist/remediate/steps/nextStep.d.ts.map +1 -1
- package/dist/remediate/steps/nextStep.js +89 -116
- package/dist/remediate/steps/nextStep.js.map +1 -1
- package/dist/remediate/steps/rollingSession.d.ts +82 -17
- package/dist/remediate/steps/rollingSession.d.ts.map +1 -1
- package/dist/remediate/steps/rollingSession.js +157 -58
- package/dist/remediate/steps/rollingSession.js.map +1 -1
- package/dist/remediate/steps/types.d.ts +12 -4
- package/dist/remediate/steps/types.d.ts.map +1 -1
- package/dist/remediate/steps/types.js +1 -1
- package/dist/shared/dispatch/admissionLoop.d.ts +245 -0
- package/dist/shared/dispatch/admissionLoop.d.ts.map +1 -0
- package/dist/shared/dispatch/admissionLoop.js +204 -0
- package/dist/shared/dispatch/admissionLoop.js.map +1 -0
- package/dist/{remediate → shared}/dispatch/ownershipScheduler.d.ts +23 -2
- package/dist/shared/dispatch/ownershipScheduler.d.ts.map +1 -0
- package/dist/{remediate → shared}/dispatch/ownershipScheduler.js +34 -6
- package/dist/shared/dispatch/ownershipScheduler.js.map +1 -0
- package/dist/shared/dispatch/pathIdentity.d.ts +26 -0
- package/dist/shared/dispatch/pathIdentity.d.ts.map +1 -0
- package/dist/shared/dispatch/pathIdentity.js +49 -0
- package/dist/shared/dispatch/pathIdentity.js.map +1 -0
- package/dist/shared/dispatch/rollingDispatch.d.ts +37 -0
- package/dist/shared/dispatch/rollingDispatch.d.ts.map +1 -1
- package/dist/shared/dispatch/rollingDispatch.js +117 -18
- package/dist/shared/dispatch/rollingDispatch.js.map +1 -1
- package/dist/shared/dispatch/unifiedRolling.d.ts +131 -0
- package/dist/shared/dispatch/unifiedRolling.d.ts.map +1 -0
- package/dist/shared/dispatch/unifiedRolling.js +161 -0
- package/dist/shared/dispatch/unifiedRolling.js.map +1 -0
- package/dist/shared/friction/stepBoundaryCapture.d.ts +4 -1
- package/dist/shared/friction/stepBoundaryCapture.d.ts.map +1 -1
- package/dist/shared/friction/stepBoundaryCapture.js.map +1 -1
- package/dist/shared/index.d.ts +12 -1
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/index.js +18 -1
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/quota/packetCost.d.ts +29 -0
- package/dist/shared/quota/packetCost.d.ts.map +1 -0
- package/dist/shared/quota/packetCost.js +51 -0
- package/dist/shared/quota/packetCost.js.map +1 -0
- package/dist/shared/quota/reservationLedger.d.ts +100 -0
- package/dist/shared/quota/reservationLedger.d.ts.map +1 -0
- package/dist/shared/quota/reservationLedger.js +224 -0
- package/dist/shared/quota/reservationLedger.js.map +1 -0
- package/dist/shared/quota/state.d.ts +20 -0
- package/dist/shared/quota/state.d.ts.map +1 -1
- package/dist/shared/quota/state.js +50 -0
- package/dist/shared/quota/state.js.map +1 -1
- package/dist/shared/quota/types.d.ts +11 -0
- package/dist/shared/quota/types.d.ts.map +1 -1
- package/dist/shared/quota/types.js.map +1 -1
- package/docs/audit-pkg/contracts.md +3 -1
- package/docs/audit-pkg/development.md +5 -4
- package/docs/audit-pkg/release.md +2 -3
- package/package.json +3 -1
- package/dist/remediate/dispatch/ownershipScheduler.d.ts.map +0 -1
- package/dist/remediate/dispatch/ownershipScheduler.js.map +0 -1
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { ReservationLedger } from "../quota/reservationLedger.js";
|
|
3
|
+
/** One packet the admission loop may grant this pass. */
|
|
4
|
+
export interface AdmissionCandidate {
|
|
5
|
+
id: string;
|
|
6
|
+
/** Reservation cost = input estimate + output envelope (estimatePacketCost). */
|
|
7
|
+
cost: number;
|
|
8
|
+
/** Complexity in [0, 1] — the default capability gate's routing signal. */
|
|
9
|
+
complexity: number;
|
|
10
|
+
}
|
|
11
|
+
/** One pool a packet may be routed to. */
|
|
12
|
+
export interface AdmissionPool {
|
|
13
|
+
poolId: string;
|
|
14
|
+
/** `provider#account/model` — the metered account the lease keys to. */
|
|
15
|
+
resourceKey: string;
|
|
16
|
+
/** Live remaining token budget for `resourceKey`. `+Infinity` ⇒ optimistic. */
|
|
17
|
+
budget: number;
|
|
18
|
+
/** Declared hard in-flight cap (e.g. Codex's 6), passed verbatim. null ⇒ none. */
|
|
19
|
+
declaredCap: number | null;
|
|
20
|
+
/** Cost rank — LOWER is cheaper; the loop routes cheapest-capable-first. */
|
|
21
|
+
costRank: number;
|
|
22
|
+
/** Capability rank — HIGHER is more capable; ties break toward more capable. */
|
|
23
|
+
capabilityRank: number;
|
|
24
|
+
/** Largest packet cost this pool can fit (context window − output). */
|
|
25
|
+
capacityTokens: number;
|
|
26
|
+
}
|
|
27
|
+
/** A successful admission: one packet leased to one pool. */
|
|
28
|
+
export interface AdmissionGrant {
|
|
29
|
+
packet_id: string;
|
|
30
|
+
pool_id: string;
|
|
31
|
+
resource_key: string;
|
|
32
|
+
lease_id: string;
|
|
33
|
+
cost: number;
|
|
34
|
+
}
|
|
35
|
+
export declare const AdmissionGrantSchema: z.ZodObject<{
|
|
36
|
+
packet_id: z.ZodString;
|
|
37
|
+
pool_id: z.ZodString;
|
|
38
|
+
resource_key: z.ZodString;
|
|
39
|
+
lease_id: z.ZodString;
|
|
40
|
+
cost: z.ZodNumber;
|
|
41
|
+
}, "strict", z.ZodTypeAny, {
|
|
42
|
+
packet_id: string;
|
|
43
|
+
cost: number;
|
|
44
|
+
pool_id: string;
|
|
45
|
+
resource_key: string;
|
|
46
|
+
lease_id: string;
|
|
47
|
+
}, {
|
|
48
|
+
packet_id: string;
|
|
49
|
+
cost: number;
|
|
50
|
+
pool_id: string;
|
|
51
|
+
resource_key: string;
|
|
52
|
+
lease_id: string;
|
|
53
|
+
}>;
|
|
54
|
+
/** Why a packet was admitted or blocked — the per-admission explain record. */
|
|
55
|
+
export declare const AdmissionExplainSchema: z.ZodObject<{
|
|
56
|
+
packet_id: z.ZodString;
|
|
57
|
+
/** null only when NO pool was capable of the packet at all. */
|
|
58
|
+
pool_id: z.ZodNullable<z.ZodString>;
|
|
59
|
+
resource_key: z.ZodNullable<z.ZodString>;
|
|
60
|
+
admitted: z.ZodBoolean;
|
|
61
|
+
reason: z.ZodEnum<["admitted", "no_capable_pool", "budget_exhausted", "cap_reached"]>;
|
|
62
|
+
/** Present on an admit attempt against a real pool (budget headroom before it). */
|
|
63
|
+
headroom_before: z.ZodOptional<z.ZodNumber>;
|
|
64
|
+
outstanding_before: z.ZodOptional<z.ZodNumber>;
|
|
65
|
+
cost: z.ZodNumber;
|
|
66
|
+
}, "strict", z.ZodTypeAny, {
|
|
67
|
+
reason: "admitted" | "no_capable_pool" | "budget_exhausted" | "cap_reached";
|
|
68
|
+
admitted: boolean;
|
|
69
|
+
packet_id: string;
|
|
70
|
+
cost: number;
|
|
71
|
+
pool_id: string | null;
|
|
72
|
+
resource_key: string | null;
|
|
73
|
+
headroom_before?: number | undefined;
|
|
74
|
+
outstanding_before?: number | undefined;
|
|
75
|
+
}, {
|
|
76
|
+
reason: "admitted" | "no_capable_pool" | "budget_exhausted" | "cap_reached";
|
|
77
|
+
admitted: boolean;
|
|
78
|
+
packet_id: string;
|
|
79
|
+
cost: number;
|
|
80
|
+
pool_id: string | null;
|
|
81
|
+
resource_key: string | null;
|
|
82
|
+
headroom_before?: number | undefined;
|
|
83
|
+
outstanding_before?: number | undefined;
|
|
84
|
+
}>;
|
|
85
|
+
export type AdmissionExplain = z.infer<typeof AdmissionExplainSchema>;
|
|
86
|
+
/**
|
|
87
|
+
* The admission artifact both orchestrators embed in their dispatch-quota contract,
|
|
88
|
+
* REPLACING the removed `max_concurrent_agents` scalar. `granted_packet_ids` is the
|
|
89
|
+
* set the host dispatches this step (its size is the emergent admission width);
|
|
90
|
+
* `declared_cap` is the verbatim per-environment hard in-flight cap (null when
|
|
91
|
+
* none); `leases` are reconciled (freed) at result-ingest; `explains` reconstruct
|
|
92
|
+
* why the fan-out was the width it was.
|
|
93
|
+
*/
|
|
94
|
+
export declare const DispatchAdmissionSchema: z.ZodObject<{
|
|
95
|
+
granted_packet_ids: z.ZodArray<z.ZodString, "many">;
|
|
96
|
+
declared_cap: z.ZodNullable<z.ZodNumber>;
|
|
97
|
+
leases: z.ZodArray<z.ZodObject<{
|
|
98
|
+
packet_id: z.ZodString;
|
|
99
|
+
pool_id: z.ZodString;
|
|
100
|
+
resource_key: z.ZodString;
|
|
101
|
+
lease_id: z.ZodString;
|
|
102
|
+
cost: z.ZodNumber;
|
|
103
|
+
}, "strict", z.ZodTypeAny, {
|
|
104
|
+
packet_id: string;
|
|
105
|
+
cost: number;
|
|
106
|
+
pool_id: string;
|
|
107
|
+
resource_key: string;
|
|
108
|
+
lease_id: string;
|
|
109
|
+
}, {
|
|
110
|
+
packet_id: string;
|
|
111
|
+
cost: number;
|
|
112
|
+
pool_id: string;
|
|
113
|
+
resource_key: string;
|
|
114
|
+
lease_id: string;
|
|
115
|
+
}>, "many">;
|
|
116
|
+
explains: z.ZodArray<z.ZodObject<{
|
|
117
|
+
packet_id: z.ZodString;
|
|
118
|
+
/** null only when NO pool was capable of the packet at all. */
|
|
119
|
+
pool_id: z.ZodNullable<z.ZodString>;
|
|
120
|
+
resource_key: z.ZodNullable<z.ZodString>;
|
|
121
|
+
admitted: z.ZodBoolean;
|
|
122
|
+
reason: z.ZodEnum<["admitted", "no_capable_pool", "budget_exhausted", "cap_reached"]>;
|
|
123
|
+
/** Present on an admit attempt against a real pool (budget headroom before it). */
|
|
124
|
+
headroom_before: z.ZodOptional<z.ZodNumber>;
|
|
125
|
+
outstanding_before: z.ZodOptional<z.ZodNumber>;
|
|
126
|
+
cost: z.ZodNumber;
|
|
127
|
+
}, "strict", z.ZodTypeAny, {
|
|
128
|
+
reason: "admitted" | "no_capable_pool" | "budget_exhausted" | "cap_reached";
|
|
129
|
+
admitted: boolean;
|
|
130
|
+
packet_id: string;
|
|
131
|
+
cost: number;
|
|
132
|
+
pool_id: string | null;
|
|
133
|
+
resource_key: string | null;
|
|
134
|
+
headroom_before?: number | undefined;
|
|
135
|
+
outstanding_before?: number | undefined;
|
|
136
|
+
}, {
|
|
137
|
+
reason: "admitted" | "no_capable_pool" | "budget_exhausted" | "cap_reached";
|
|
138
|
+
admitted: boolean;
|
|
139
|
+
packet_id: string;
|
|
140
|
+
cost: number;
|
|
141
|
+
pool_id: string | null;
|
|
142
|
+
resource_key: string | null;
|
|
143
|
+
headroom_before?: number | undefined;
|
|
144
|
+
outstanding_before?: number | undefined;
|
|
145
|
+
}>, "many">;
|
|
146
|
+
}, "strict", z.ZodTypeAny, {
|
|
147
|
+
leases: {
|
|
148
|
+
packet_id: string;
|
|
149
|
+
cost: number;
|
|
150
|
+
pool_id: string;
|
|
151
|
+
resource_key: string;
|
|
152
|
+
lease_id: string;
|
|
153
|
+
}[];
|
|
154
|
+
granted_packet_ids: string[];
|
|
155
|
+
declared_cap: number | null;
|
|
156
|
+
explains: {
|
|
157
|
+
reason: "admitted" | "no_capable_pool" | "budget_exhausted" | "cap_reached";
|
|
158
|
+
admitted: boolean;
|
|
159
|
+
packet_id: string;
|
|
160
|
+
cost: number;
|
|
161
|
+
pool_id: string | null;
|
|
162
|
+
resource_key: string | null;
|
|
163
|
+
headroom_before?: number | undefined;
|
|
164
|
+
outstanding_before?: number | undefined;
|
|
165
|
+
}[];
|
|
166
|
+
}, {
|
|
167
|
+
leases: {
|
|
168
|
+
packet_id: string;
|
|
169
|
+
cost: number;
|
|
170
|
+
pool_id: string;
|
|
171
|
+
resource_key: string;
|
|
172
|
+
lease_id: string;
|
|
173
|
+
}[];
|
|
174
|
+
granted_packet_ids: string[];
|
|
175
|
+
declared_cap: number | null;
|
|
176
|
+
explains: {
|
|
177
|
+
reason: "admitted" | "no_capable_pool" | "budget_exhausted" | "cap_reached";
|
|
178
|
+
admitted: boolean;
|
|
179
|
+
packet_id: string;
|
|
180
|
+
cost: number;
|
|
181
|
+
pool_id: string | null;
|
|
182
|
+
resource_key: string | null;
|
|
183
|
+
headroom_before?: number | undefined;
|
|
184
|
+
outstanding_before?: number | undefined;
|
|
185
|
+
}[];
|
|
186
|
+
}>;
|
|
187
|
+
export type DispatchAdmission = z.infer<typeof DispatchAdmissionSchema>;
|
|
188
|
+
/** Outcome of one admission pass. */
|
|
189
|
+
export interface AdmitBatchResult {
|
|
190
|
+
granted: AdmissionGrant[];
|
|
191
|
+
explains: AdmissionExplain[];
|
|
192
|
+
/** Packet ids not admitted this pass — deferred to a later grant (re-invoke). */
|
|
193
|
+
blocked: string[];
|
|
194
|
+
}
|
|
195
|
+
export interface AdmitBatchInput {
|
|
196
|
+
/** Candidate packets in PRIORITY order (highest-priority first). */
|
|
197
|
+
packets: AdmissionCandidate[];
|
|
198
|
+
pools: AdmissionPool[];
|
|
199
|
+
ledger: ReservationLedger;
|
|
200
|
+
/**
|
|
201
|
+
* Capability gate: may this pool handle this packet? Defaults to a size fit
|
|
202
|
+
* (`pool.capacityTokens >= packet.cost`) — a grounded, provider-neutral gate. A
|
|
203
|
+
* caller refines the complexity/risk gate by supplying its own predicate (the
|
|
204
|
+
* intended extension point for cost/capability routing policy).
|
|
205
|
+
*/
|
|
206
|
+
capable?: (pool: AdmissionPool, packet: AdmissionCandidate) => boolean;
|
|
207
|
+
leaseTtlMs?: number;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Admit as many packets as budget + declared caps allow this pass, routing each to
|
|
211
|
+
* the cheapest capable pool with headroom. Every admission RESERVES the packet's
|
|
212
|
+
* cost against the shared ledger under its lock, so co-located dispatch loops on one
|
|
213
|
+
* account cannot collectively over-admit. Returns the granted set (the host
|
|
214
|
+
* dispatches exactly these), the explain trail, and the blocked remainder.
|
|
215
|
+
*
|
|
216
|
+
* A pool's declared in-flight cap is enforced by COUNT of its outstanding ledger
|
|
217
|
+
* leases (cross-process) plus this batch's grants — the only place an explicit
|
|
218
|
+
* agent-count exists (a verbatim environment limit), never a computed concurrency.
|
|
219
|
+
*/
|
|
220
|
+
export declare function admitBatch(input: AdmitBatchInput): Promise<AdmitBatchResult>;
|
|
221
|
+
/**
|
|
222
|
+
* Build the `DispatchAdmission` contract block both orchestrators embed in their
|
|
223
|
+
* dispatch-quota — the single-sourced host-path admission derivation (no per-tool
|
|
224
|
+
* copy that could drift). Computes each packet's envelope reservation
|
|
225
|
+
* (`estimatePacketCost` with the declared output cap), derives the surfaced declared
|
|
226
|
+
* cap (the most-constraining pool's in-flight cap), and either GRANTS via
|
|
227
|
+
* {@link admitBatch} (host-subagent path, `grantLeases: true` — leases persist for
|
|
228
|
+
* reconcile at ingest) or returns a plan-only block listing every candidate
|
|
229
|
+
* (`grantLeases: false` — the in-process rolling engine leases per-packet itself,
|
|
230
|
+
* so a host grant here would double-count).
|
|
231
|
+
*/
|
|
232
|
+
export declare function computeDispatchAdmission(input: {
|
|
233
|
+
packets: {
|
|
234
|
+
id: string;
|
|
235
|
+
inputTokens: number;
|
|
236
|
+
complexity: number;
|
|
237
|
+
}[];
|
|
238
|
+
pools: AdmissionPool[];
|
|
239
|
+
/** Declared output cap for the packet envelope (cold-start; ratio refines later). */
|
|
240
|
+
outputCap: number;
|
|
241
|
+
grantLeases: boolean;
|
|
242
|
+
ledger: ReservationLedger;
|
|
243
|
+
capable?: (pool: AdmissionPool, packet: AdmissionCandidate) => boolean;
|
|
244
|
+
}): Promise<DispatchAdmission>;
|
|
245
|
+
//# sourceMappingURL=admissionLoop.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admissionLoop.d.ts","sourceRoot":"","sources":["../../../src/shared/dispatch/admissionLoop.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
// Host-path admission loop — the tool-side "grant the admitted set" primitive of
|
|
2
|
+
// the dispatch admission-control model (spec/audit/dispatch-admission-control.md).
|
|
3
|
+
//
|
|
4
|
+
// The in-process rolling engine (rollingDispatch.ts) admits ONE packet at a time
|
|
5
|
+
// continuously; this is its host-subagent-path analog: at a dispatch step the tool
|
|
6
|
+
// admits a BATCH — as many packets as budget (and any declared in-flight cap) allow
|
|
7
|
+
// right now — reserving each against the shared ReservationLedger, and hands the
|
|
8
|
+
// host EXACTLY that granted set. The host dispatches the set, reports at
|
|
9
|
+
// result-ingest (merge-and-ingest), and the next `next-step` re-admits the
|
|
10
|
+
// remainder. The granted set's size is the instantaneous admission width — there is
|
|
11
|
+
// no computed concurrency number (spec: "concurrency is not a computed quantity").
|
|
12
|
+
//
|
|
13
|
+
// COST-FIRST ROUTING is a first-class property: each packet is routed to the
|
|
14
|
+
// CHEAPEST pool CAPABLE of it (cost-ascending among capable pools) that still has
|
|
15
|
+
// budget + in-flight-cap headroom, so lower-cost providers fill before pricier ones
|
|
16
|
+
// and overflow spills to the next-cheapest-capable pool. The capability gate and
|
|
17
|
+
// cost rank are inputs, so refining the complexity/risk gate is a one-predicate
|
|
18
|
+
// change, never a re-architecture.
|
|
19
|
+
import { z } from "zod";
|
|
20
|
+
import { estimatePacketCost } from "../quota/packetCost.js";
|
|
21
|
+
export const AdmissionGrantSchema = z
|
|
22
|
+
.object({
|
|
23
|
+
packet_id: z.string(),
|
|
24
|
+
pool_id: z.string(),
|
|
25
|
+
resource_key: z.string(),
|
|
26
|
+
lease_id: z.string(),
|
|
27
|
+
cost: z.number(),
|
|
28
|
+
})
|
|
29
|
+
.strict();
|
|
30
|
+
/** Why a packet was admitted or blocked — the per-admission explain record. */
|
|
31
|
+
export const AdmissionExplainSchema = z
|
|
32
|
+
.object({
|
|
33
|
+
packet_id: z.string(),
|
|
34
|
+
/** null only when NO pool was capable of the packet at all. */
|
|
35
|
+
pool_id: z.string().nullable(),
|
|
36
|
+
resource_key: z.string().nullable(),
|
|
37
|
+
admitted: z.boolean(),
|
|
38
|
+
reason: z.enum(["admitted", "no_capable_pool", "budget_exhausted", "cap_reached"]),
|
|
39
|
+
/** Present on an admit attempt against a real pool (budget headroom before it). */
|
|
40
|
+
headroom_before: z.number().optional(),
|
|
41
|
+
outstanding_before: z.number().optional(),
|
|
42
|
+
cost: z.number(),
|
|
43
|
+
})
|
|
44
|
+
.strict();
|
|
45
|
+
/**
|
|
46
|
+
* The admission artifact both orchestrators embed in their dispatch-quota contract,
|
|
47
|
+
* REPLACING the removed `max_concurrent_agents` scalar. `granted_packet_ids` is the
|
|
48
|
+
* set the host dispatches this step (its size is the emergent admission width);
|
|
49
|
+
* `declared_cap` is the verbatim per-environment hard in-flight cap (null when
|
|
50
|
+
* none); `leases` are reconciled (freed) at result-ingest; `explains` reconstruct
|
|
51
|
+
* why the fan-out was the width it was.
|
|
52
|
+
*/
|
|
53
|
+
export const DispatchAdmissionSchema = z
|
|
54
|
+
.object({
|
|
55
|
+
granted_packet_ids: z.array(z.string()),
|
|
56
|
+
declared_cap: z.number().int().min(1).nullable(),
|
|
57
|
+
leases: z.array(AdmissionGrantSchema),
|
|
58
|
+
explains: z.array(AdmissionExplainSchema),
|
|
59
|
+
})
|
|
60
|
+
.strict();
|
|
61
|
+
/** Default capability gate: the pool's window must fit the packet's reservation. */
|
|
62
|
+
function defaultCapable(pool, packet) {
|
|
63
|
+
if (!Number.isFinite(pool.capacityTokens) || pool.capacityTokens <= 0)
|
|
64
|
+
return true;
|
|
65
|
+
return pool.capacityTokens >= packet.cost;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Admit as many packets as budget + declared caps allow this pass, routing each to
|
|
69
|
+
* the cheapest capable pool with headroom. Every admission RESERVES the packet's
|
|
70
|
+
* cost against the shared ledger under its lock, so co-located dispatch loops on one
|
|
71
|
+
* account cannot collectively over-admit. Returns the granted set (the host
|
|
72
|
+
* dispatches exactly these), the explain trail, and the blocked remainder.
|
|
73
|
+
*
|
|
74
|
+
* A pool's declared in-flight cap is enforced by COUNT of its outstanding ledger
|
|
75
|
+
* leases (cross-process) plus this batch's grants — the only place an explicit
|
|
76
|
+
* agent-count exists (a verbatim environment limit), never a computed concurrency.
|
|
77
|
+
*/
|
|
78
|
+
export async function admitBatch(input) {
|
|
79
|
+
const capable = input.capable ?? defaultCapable;
|
|
80
|
+
const granted = [];
|
|
81
|
+
const explains = [];
|
|
82
|
+
const blocked = [];
|
|
83
|
+
// Seed per-pool in-flight COUNT from the ledger (other consumers' live leases),
|
|
84
|
+
// so a declared cap accounts for cross-process in-flight, then add this batch's.
|
|
85
|
+
const countByPool = new Map();
|
|
86
|
+
try {
|
|
87
|
+
const snapshot = await input.ledger.snapshot();
|
|
88
|
+
for (const leases of Object.values(snapshot)) {
|
|
89
|
+
for (const lease of leases) {
|
|
90
|
+
countByPool.set(lease.poolId, (countByPool.get(lease.poolId) ?? 0) + 1);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// Degrade-safe: an unreadable ledger just means cap counting starts at 0.
|
|
96
|
+
}
|
|
97
|
+
for (const packet of input.packets) {
|
|
98
|
+
// Cost-first-capable: cheapest capable pool first; ties break toward the more
|
|
99
|
+
// capable pool so equal-cost lanes prefer the one with more headroom.
|
|
100
|
+
const candidates = input.pools
|
|
101
|
+
.filter((pool) => capable(pool, packet))
|
|
102
|
+
.sort((a, b) => a.costRank - b.costRank || b.capabilityRank - a.capabilityRank);
|
|
103
|
+
if (candidates.length === 0) {
|
|
104
|
+
explains.push({
|
|
105
|
+
packet_id: packet.id,
|
|
106
|
+
pool_id: null,
|
|
107
|
+
resource_key: null,
|
|
108
|
+
admitted: false,
|
|
109
|
+
reason: "no_capable_pool",
|
|
110
|
+
cost: packet.cost,
|
|
111
|
+
});
|
|
112
|
+
blocked.push(packet.id);
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
let placed = false;
|
|
116
|
+
let lastReason = "budget_exhausted";
|
|
117
|
+
let lastPool = candidates[0];
|
|
118
|
+
for (const pool of candidates) {
|
|
119
|
+
if (pool.declaredCap != null && (countByPool.get(pool.poolId) ?? 0) >= pool.declaredCap) {
|
|
120
|
+
lastReason = "cap_reached";
|
|
121
|
+
lastPool = pool;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
const decision = await input.ledger.admit({
|
|
125
|
+
resourceKey: pool.resourceKey,
|
|
126
|
+
cost: packet.cost,
|
|
127
|
+
budget: pool.budget,
|
|
128
|
+
poolId: pool.poolId,
|
|
129
|
+
...(input.leaseTtlMs != null ? { leaseTtlMs: input.leaseTtlMs } : {}),
|
|
130
|
+
});
|
|
131
|
+
if (decision.admitted && decision.leaseId) {
|
|
132
|
+
granted.push({
|
|
133
|
+
packet_id: packet.id,
|
|
134
|
+
pool_id: pool.poolId,
|
|
135
|
+
resource_key: pool.resourceKey,
|
|
136
|
+
lease_id: decision.leaseId,
|
|
137
|
+
cost: packet.cost,
|
|
138
|
+
});
|
|
139
|
+
countByPool.set(pool.poolId, (countByPool.get(pool.poolId) ?? 0) + 1);
|
|
140
|
+
explains.push({
|
|
141
|
+
packet_id: packet.id,
|
|
142
|
+
pool_id: pool.poolId,
|
|
143
|
+
resource_key: pool.resourceKey,
|
|
144
|
+
admitted: true,
|
|
145
|
+
reason: "admitted",
|
|
146
|
+
headroom_before: decision.headroomBefore,
|
|
147
|
+
outstanding_before: decision.outstandingBefore,
|
|
148
|
+
cost: packet.cost,
|
|
149
|
+
});
|
|
150
|
+
placed = true;
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
lastReason = "budget_exhausted";
|
|
154
|
+
lastPool = pool;
|
|
155
|
+
}
|
|
156
|
+
if (!placed) {
|
|
157
|
+
explains.push({
|
|
158
|
+
packet_id: packet.id,
|
|
159
|
+
pool_id: lastPool.poolId,
|
|
160
|
+
resource_key: lastPool.resourceKey,
|
|
161
|
+
admitted: false,
|
|
162
|
+
reason: lastReason,
|
|
163
|
+
cost: packet.cost,
|
|
164
|
+
});
|
|
165
|
+
blocked.push(packet.id);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return { granted, explains, blocked };
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Build the `DispatchAdmission` contract block both orchestrators embed in their
|
|
172
|
+
* dispatch-quota — the single-sourced host-path admission derivation (no per-tool
|
|
173
|
+
* copy that could drift). Computes each packet's envelope reservation
|
|
174
|
+
* (`estimatePacketCost` with the declared output cap), derives the surfaced declared
|
|
175
|
+
* cap (the most-constraining pool's in-flight cap), and either GRANTS via
|
|
176
|
+
* {@link admitBatch} (host-subagent path, `grantLeases: true` — leases persist for
|
|
177
|
+
* reconcile at ingest) or returns a plan-only block listing every candidate
|
|
178
|
+
* (`grantLeases: false` — the in-process rolling engine leases per-packet itself,
|
|
179
|
+
* so a host grant here would double-count).
|
|
180
|
+
*/
|
|
181
|
+
export async function computeDispatchAdmission(input) {
|
|
182
|
+
const candidates = input.packets.map((p) => ({
|
|
183
|
+
id: p.id,
|
|
184
|
+
cost: estimatePacketCost({ inputEstimate: p.inputTokens, declaredOutputCap: input.outputCap }).cost,
|
|
185
|
+
complexity: p.complexity,
|
|
186
|
+
}));
|
|
187
|
+
const declaredCap = input.pools.reduce((min, p) => (p.declaredCap == null ? min : min == null ? p.declaredCap : Math.min(min, p.declaredCap)), null);
|
|
188
|
+
if (!input.grantLeases) {
|
|
189
|
+
return { granted_packet_ids: candidates.map((c) => c.id), declared_cap: declaredCap, leases: [], explains: [] };
|
|
190
|
+
}
|
|
191
|
+
const admit = await admitBatch({
|
|
192
|
+
packets: candidates,
|
|
193
|
+
pools: input.pools,
|
|
194
|
+
ledger: input.ledger,
|
|
195
|
+
...(input.capable ? { capable: input.capable } : {}),
|
|
196
|
+
});
|
|
197
|
+
return {
|
|
198
|
+
granted_packet_ids: admit.granted.map((g) => g.packet_id),
|
|
199
|
+
declared_cap: declaredCap,
|
|
200
|
+
leases: admit.granted,
|
|
201
|
+
explains: admit.explains,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=admissionLoop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admissionLoop.js","sourceRoot":"","sources":["../../../src/shared/dispatch/admissionLoop.ts"],"names":[],"mappings":"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"}
|
|
@@ -22,6 +22,17 @@
|
|
|
22
22
|
* region-disjoint upstream). If either lacks the flag they serialize into
|
|
23
23
|
* successive sub-waves exactly as before — the conservative default is unchanged.
|
|
24
24
|
*
|
|
25
|
+
* Three disjointness cases, not two (the read-only distinction): (1) a node that is
|
|
26
|
+
* provably READ-ONLY (`read_only === true`, no writes at all) conflicts with nothing
|
|
27
|
+
* and admits into any sub-wave — all read-only nodes collapse into the first maximal
|
|
28
|
+
* sub-wave (this is how an auditor, which writes nothing, runs fully parallel as a
|
|
29
|
+
* degenerate case of the same scheduler); (2) a node with a DECLARED write-scope
|
|
30
|
+
* batches file-disjointly (the `cofile_parallel_safe` relaxation above); (3) a node
|
|
31
|
+
* with an EMPTY/undeclared scope is conservatively non-disjoint and admits solo among
|
|
32
|
+
* writers. Case (1) is distinct from case (3): an empty `write_paths` means "scope
|
|
33
|
+
* unresolved, might touch anything" (serialize), whereas `read_only` means "provably
|
|
34
|
+
* touches nothing" (always parallel).
|
|
35
|
+
*
|
|
25
36
|
* Pure and deterministic — no I/O, no Set/Map iteration-order leak — so the
|
|
26
37
|
* admission order is reproducible and unit-testable against a precomputed level.
|
|
27
38
|
*/
|
|
@@ -36,6 +47,15 @@ export interface OwnershipSchedulerNode {
|
|
|
36
47
|
* Absent/false ⇒ the node serializes vs. any same-file peer exactly as before.
|
|
37
48
|
*/
|
|
38
49
|
cofile_parallel_safe?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* The node is provably READ-ONLY — it performs no writes to the target tree, so it
|
|
52
|
+
* can never conflict with any peer at merge and admits into any sub-wave in full
|
|
53
|
+
* parallel. This is DISTINCT from an empty/undeclared `write_paths`: empty means the
|
|
54
|
+
* write-scope is *unresolved* (conservatively serial); `read_only` means the node is
|
|
55
|
+
* *known* to write nothing (an auditor). Absent/false ⇒ the node is treated as a
|
|
56
|
+
* writer and goes through the normal file-disjointness gating.
|
|
57
|
+
*/
|
|
58
|
+
read_only?: boolean;
|
|
39
59
|
}
|
|
40
60
|
/**
|
|
41
61
|
* Canonicalize a node's declared write-scope to physical-file identity keys.
|
|
@@ -52,8 +72,9 @@ export declare function canonicalScopeKeys(node: OwnershipSchedulerNode, root?:
|
|
|
52
72
|
*
|
|
53
73
|
* A node with an EMPTY canonical scope (unresolved/undeclared) is treated as
|
|
54
74
|
* conservatively NON-disjoint: it only enters a sub-wave by itself (it blocks and
|
|
55
|
-
* is blocked-by every peer), so it never batches with another writer
|
|
56
|
-
* (INV-SOO-01 / CE-008).
|
|
75
|
+
* is blocked-by every peer writer), so it never batches with another writer
|
|
76
|
+
* (INV-SOO-01 / CE-008). A `read_only` node is the opposite — it writes nothing, so
|
|
77
|
+
* it admits into any sub-wave in full parallel (see the three-case note above).
|
|
57
78
|
*/
|
|
58
79
|
export declare function ownershipSubWaves(level: OwnershipSchedulerNode[], root?: string): OwnershipSchedulerNode[][];
|
|
59
80
|
//# sourceMappingURL=ownershipScheduler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ownershipScheduler.d.ts","sourceRoot":"","sources":["../../../src/shared/dispatch/ownershipScheduler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAIH,mFAAmF;AACnF,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,kFAAkF;IAClF,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,sBAAsB,EAC5B,IAAI,CAAC,EAAE,MAAM,GACZ,GAAG,CAAC,MAAM,CAAC,CAIb;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,sBAAsB,EAAE,EAC/B,IAAI,CAAC,EAAE,MAAM,GACZ,sBAAsB,EAAE,EAAE,CAoF5B"}
|
|
@@ -22,10 +22,21 @@
|
|
|
22
22
|
* region-disjoint upstream). If either lacks the flag they serialize into
|
|
23
23
|
* successive sub-waves exactly as before — the conservative default is unchanged.
|
|
24
24
|
*
|
|
25
|
+
* Three disjointness cases, not two (the read-only distinction): (1) a node that is
|
|
26
|
+
* provably READ-ONLY (`read_only === true`, no writes at all) conflicts with nothing
|
|
27
|
+
* and admits into any sub-wave — all read-only nodes collapse into the first maximal
|
|
28
|
+
* sub-wave (this is how an auditor, which writes nothing, runs fully parallel as a
|
|
29
|
+
* degenerate case of the same scheduler); (2) a node with a DECLARED write-scope
|
|
30
|
+
* batches file-disjointly (the `cofile_parallel_safe` relaxation above); (3) a node
|
|
31
|
+
* with an EMPTY/undeclared scope is conservatively non-disjoint and admits solo among
|
|
32
|
+
* writers. Case (1) is distinct from case (3): an empty `write_paths` means "scope
|
|
33
|
+
* unresolved, might touch anything" (serialize), whereas `read_only` means "provably
|
|
34
|
+
* touches nothing" (always parallel).
|
|
35
|
+
*
|
|
25
36
|
* Pure and deterministic — no I/O, no Set/Map iteration-order leak — so the
|
|
26
37
|
* admission order is reproducible and unit-testable against a precomputed level.
|
|
27
38
|
*/
|
|
28
|
-
import { canonicalizeFilePath } from "./
|
|
39
|
+
import { canonicalizeFilePath } from "./pathIdentity.js";
|
|
29
40
|
/**
|
|
30
41
|
* Canonicalize a node's declared write-scope to physical-file identity keys.
|
|
31
42
|
* Empty input ⇒ empty set (handled conservatively by the disjointness gate).
|
|
@@ -46,8 +57,9 @@ export function canonicalScopeKeys(node, root) {
|
|
|
46
57
|
*
|
|
47
58
|
* A node with an EMPTY canonical scope (unresolved/undeclared) is treated as
|
|
48
59
|
* conservatively NON-disjoint: it only enters a sub-wave by itself (it blocks and
|
|
49
|
-
* is blocked-by every peer), so it never batches with another writer
|
|
50
|
-
* (INV-SOO-01 / CE-008).
|
|
60
|
+
* is blocked-by every peer writer), so it never batches with another writer
|
|
61
|
+
* (INV-SOO-01 / CE-008). A `read_only` node is the opposite — it writes nothing, so
|
|
62
|
+
* it admits into any sub-wave in full parallel (see the three-case note above).
|
|
51
63
|
*/
|
|
52
64
|
export function ownershipSubWaves(level, root) {
|
|
53
65
|
const ordered = [...level].sort((a, b) => a.block_id.localeCompare(b.block_id));
|
|
@@ -63,13 +75,28 @@ export function ownershipSubWaves(level, root) {
|
|
|
63
75
|
// co-batch on a shared key IFF both are `cofile_parallel_safe`.
|
|
64
76
|
const claimant = new Map();
|
|
65
77
|
let waveHasEmptyScopeNode = false;
|
|
78
|
+
// A writer or empty-scope node has been admitted to this sub-wave. Gates the
|
|
79
|
+
// empty-scope node's solo admission: it may enter only when no writer and no
|
|
80
|
+
// prior empty-scope node is present. A `read_only` co-resident is NOT a writer,
|
|
81
|
+
// so it never trips this — read-only nodes stay fully inert to the conflict logic.
|
|
82
|
+
let waveHasWriter = false;
|
|
66
83
|
const leftover = [];
|
|
67
84
|
for (const node of remaining) {
|
|
68
85
|
const scope = scopeOf.get(node.block_id);
|
|
86
|
+
// Case 1: provably READ-ONLY (writes nothing) ⇒ conflicts with nothing. Admit to
|
|
87
|
+
// the current sub-wave unconditionally; never claim a path, never set or consult
|
|
88
|
+
// the writer/empty-scope flags. All read-only nodes therefore collapse into the
|
|
89
|
+
// first (maximal) sub-wave — an auditor's full-parallel degenerate case.
|
|
90
|
+
if (node.read_only === true) {
|
|
91
|
+
wave.push(node);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
69
94
|
const isEmpty = scope.size === 0;
|
|
70
|
-
//
|
|
95
|
+
// Case 3: empty/undeclared scope ⇒ conservatively non-disjoint. Admits only into
|
|
96
|
+
// a sub-wave with no other writer and no prior empty-scope node (solo among
|
|
97
|
+
// writers; read-only co-residents don't count).
|
|
71
98
|
if (isEmpty) {
|
|
72
|
-
if (
|
|
99
|
+
if (!waveHasWriter && !waveHasEmptyScopeNode) {
|
|
73
100
|
wave.push(node);
|
|
74
101
|
waveHasEmptyScopeNode = true;
|
|
75
102
|
}
|
|
@@ -78,7 +105,7 @@ export function ownershipSubWaves(level, root) {
|
|
|
78
105
|
}
|
|
79
106
|
continue;
|
|
80
107
|
}
|
|
81
|
-
//
|
|
108
|
+
// Case 2: a real-scope node cannot share a wave with an empty-scope node, nor with
|
|
82
109
|
// any node it shares a canonical path with — UNLESS both this node and every
|
|
83
110
|
// same-file incumbent it collides with are `cofile_parallel_safe`.
|
|
84
111
|
if (waveHasEmptyScopeNode) {
|
|
@@ -104,6 +131,7 @@ export function ownershipSubWaves(level, root) {
|
|
|
104
131
|
for (const key of scope)
|
|
105
132
|
claimant.set(key, node);
|
|
106
133
|
wave.push(node);
|
|
134
|
+
waveHasWriter = true;
|
|
107
135
|
}
|
|
108
136
|
subWaves.push(wave);
|
|
109
137
|
remaining.length = 0;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ownershipScheduler.js","sourceRoot":"","sources":["../../../src/shared/dispatch/ownershipScheduler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The single-sourced path canonicalization for ownership identity (INV-SOO-09).
|
|
3
|
+
*
|
|
4
|
+
* Resolves a path to a stable physical-file key: absolute (against `root` when
|
|
5
|
+
* supplied, else cwd), `..`/`.`-collapsed (via `resolve`), separators normalized
|
|
6
|
+
* to `/`, and case-folded on a case-insensitive filesystem. All spellings of one
|
|
7
|
+
* file therefore collide, closing the rel/abs/case/`..` mismatch (CE-004).
|
|
8
|
+
*
|
|
9
|
+
* Symlink identity is a RECORDED RESIDUAL, not silently treated as disjoint
|
|
10
|
+
* (INV-SOO-09 / fail-3): when `resolveSymlinks` is set and the path exists, the
|
|
11
|
+
* realpath is folded in so `link.ts → x.ts` collides; when realpath is
|
|
12
|
+
* unavailable (path absent, or FS symlink resolution out of scope) the lexical
|
|
13
|
+
* canonical key is used and the unresolved case degrades to the downstream merge
|
|
14
|
+
* guard rather than being asserted disjoint here.
|
|
15
|
+
*
|
|
16
|
+
* This is the ONLY normalization scheme for ownership identity — callers must
|
|
17
|
+
* not introduce a second one (failure-mode: path-identity mismatch). It lives in
|
|
18
|
+
* `audit-tools/shared` so BOTH orchestrators' ownership scheduling resolves file
|
|
19
|
+
* identity identically (a remediate write-scope and an audit read-scope key the
|
|
20
|
+
* same file the same way).
|
|
21
|
+
*/
|
|
22
|
+
export declare function canonicalizeFilePath(path: string, opts?: {
|
|
23
|
+
root?: string;
|
|
24
|
+
resolveSymlinks?: boolean;
|
|
25
|
+
}): string;
|
|
26
|
+
//# sourceMappingURL=pathIdentity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pathIdentity.d.ts","sourceRoot":"","sources":["../../../src/shared/dispatch/pathIdentity.ts"],"names":[],"mappings":"AAWA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,OAAO,CAAA;CAAO,GACtD,MAAM,CAgBR"}
|