omk-agent-core 1.2.4 → 1.3.0
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/CHANGELOG.md +16 -0
- package/dist/agent-loop.d.ts.map +1 -1
- package/dist/agent-loop.js +35 -47
- package/dist/agent-loop.js.map +1 -1
- package/dist/agent.d.ts +6 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +9 -5
- package/dist/agent.js.map +1 -1
- package/dist/pending-message-queue.d.ts +1 -0
- package/dist/pending-message-queue.d.ts.map +1 -1
- package/dist/pending-message-queue.js +16 -5
- package/dist/pending-message-queue.js.map +1 -1
- package/dist/tool-dag-ecraf.d.ts.map +1 -1
- package/dist/tool-dag-ecraf.js +15 -8
- package/dist/tool-dag-ecraf.js.map +1 -1
- package/dist/tool-dag-index.d.ts +19 -0
- package/dist/tool-dag-index.d.ts.map +1 -0
- package/dist/tool-dag-index.js +175 -0
- package/dist/tool-dag-index.js.map +1 -0
- package/dist/tool-dag-memo.d.ts +13 -3
- package/dist/tool-dag-memo.d.ts.map +1 -1
- package/dist/tool-dag-memo.js +64 -25
- package/dist/tool-dag-memo.js.map +1 -1
- package/dist/tool-dag-own-number.d.ts +3 -0
- package/dist/tool-dag-own-number.d.ts.map +1 -0
- package/dist/tool-dag-own-number.js +5 -0
- package/dist/tool-dag-own-number.js.map +1 -0
- package/dist/tool-dag-owned-task.d.ts +15 -0
- package/dist/tool-dag-owned-task.d.ts.map +1 -0
- package/dist/tool-dag-owned-task.js +33 -0
- package/dist/tool-dag-owned-task.js.map +1 -0
- package/dist/tool-dag-plan-copy.d.ts +5 -0
- package/dist/tool-dag-plan-copy.d.ts.map +1 -0
- package/dist/tool-dag-plan-copy.js +18 -0
- package/dist/tool-dag-plan-copy.js.map +1 -0
- package/dist/tool-dag-read-aware.d.ts +11 -0
- package/dist/tool-dag-read-aware.d.ts.map +1 -0
- package/dist/tool-dag-read-aware.js +34 -0
- package/dist/tool-dag-read-aware.js.map +1 -0
- package/dist/tool-dag-reduce.d.ts +29 -0
- package/dist/tool-dag-reduce.d.ts.map +1 -0
- package/dist/tool-dag-reduce.js +146 -0
- package/dist/tool-dag-reduce.js.map +1 -0
- package/dist/tool-dag-scheduler.d.ts.map +1 -1
- package/dist/tool-dag-scheduler.js +3 -14
- package/dist/tool-dag-scheduler.js.map +1 -1
- package/dist/tool-terminal-index.d.ts +4 -0
- package/dist/tool-terminal-index.d.ts.map +1 -0
- package/dist/tool-terminal-index.js +12 -0
- package/dist/tool-terminal-index.js.map +1 -0
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-dag-ecraf.js","sourceRoot":"","sources":["../src/tool-dag-ecraf.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ECRAF — resource-aware ready queue admission planner.\n *\n * Given the current ready frontier, a free-slot count, and multi-dimensional\n * resource capacities, choose which ready nodes to admit next using the\n * deterministic greedy baseline the deep-research report prescribes (PR 8).\n *\n * legacy-v1: D_i = P_i / (epsilon + sum_r weight_r * a_ir)\n * normalized-v2: D_i = P_i / (epsilon + slotCost + sum_r weight_r * a_ir / s_r)\n * Normalization is opt-in, not a live scheduler or a measured improvement.\n *\n * then sorted by density descending, readySeq ascending, sourceIndex ascending.\n * A candidate is admitted only if admitting it does not push cumulative\n * running usage over any capacity and it does not semantically conflict with a\n * node already admitted in this pass. Admission is pure: it computes the plan;\n * the caller launches.\n */\n\nexport interface EcrafCandidate {\n\t/** Source order of the call within the batch — the stable tiebreak. */\n\treadonly sourceIndex: number;\n\t/** Monotonic order in which the node first entered the ready frontier. */\n\treadonly readySeq: number;\n\t/** Resource vector: resource name -> units consumed while running. */\n\treadonly resources: Readonly<Record<string, number>>;\n\t/** Scalar priority P_i already folded (rank, aging, evidence, risk...). */\n\treadonly priority: number;\n}\n\nexport interface EcrafAdmissionsOptions {\n\t/** Omitted: legacy-v1, or normalized-v2 when referenceScales is supplied. */\n\treadonly algorithmVersion?: \"legacy-v1\" | \"normalized-v2\";\n\treadonly candidates: readonly EcrafCandidate[];\n\t/** Resource units already held by running nodes: name -> units. */\n\treadonly runningUsage: Readonly<Record<string, number>>;\n\t/** Hard capacity per resource: name -> max units. Missing = unbounded. */\n\treadonly capacities: Readonly<Record<string, number>>;\n\t/** Free admission slots (concurrency budget). */\n\treadonly slots: number;\n\t/** Density epsilon guarding division-by-zero. Default 1e-6. */\n\treadonly epsilon?: number;\n\t/** Optional weight per resource in the density denominator. Default 1. */\n\treadonly resourceWeights?: Readonly<Record<string, number>>;\n\t/**\n\t * Opt-in dimensionless normalization (audit §13.2): resource name -> positive\n\t * reference scale s_r. When provided, the density denominator uses unitless\n\t * demands a_ir/s_r, making ranking invariant to per-resource unit changes\n\t * (a'_ir = c_r·a_ir with s'_ir = c_r·s_r preserves the mathematical ratio).\n\t * In normalized-v2, omitted entries use positive total capacities, never\n\t * remaining headroom. Positive unbounded demands require explicit scales.\n\t * Zero capacity is a feasibility gate, not a scale. All supplied scales must\n\t * be finite and positive. Supplying this map without a version opts into v2.\n\t */\n\treadonly referenceScales?: Readonly<Record<string, number>>;\n\t/** Finite positive slot term λ_slot in normalized-v2. Default 1; invalid in v1. */\n\treadonly slotCost?: number;\n\t/**\n\t * Semantic-conflict predicate. Return true when the candidate may not run\n\t * concurrently with an already-admitted node. Defaults to no conflicts.\n\t */\n\treadonly conflicts?: (candidate: EcrafCandidate, running: EcrafCandidate) => boolean;\n}\n\nexport interface EcrafAdmissionPlan {\n\t/** Source indices admitted, in the order they should be launched. */\n\treadonly admit: readonly number[];\n\t/** Source indices deferred this pass (capacity or conflict), in source order. */\n\treadonly deferred: readonly number[];\n}\n\n/**\n * Numeric contract for one admission pass (audit §5.3): every resource\n * demand, running usage, and capacity is a finite non-negative number; the\n * slot budget is a non-negative integer; epsilon is finite and positive;\n * ranking weights are finite and non-negative; candidate source indices and\n * ready sequences are unique, finite integers. Violating these invariants\n * silently poisons the plan — NaN makes every capacity check pass, negative\n * demand manufactures capacity, a fractional slot admits ⌈slots⌉ nodes, and\n * duplicate source indices make the admit list ambiguous — so malformed input\n * is rejected up front rather than coerced into \"unbounded\".\n */\nfunction assertFiniteNonNegative(value: number, label: string): void {\n\tif (!Number.isFinite(value) || value < 0) {\n\t\tthrow new RangeError(`${label} must be a finite non-negative number, got ${String(value)}`);\n\t}\n}\n\nfunction assertInputContract(options: EcrafAdmissionsOptions): void {\n\tconst {\n\t\tcandidates,\n\t\trunningUsage,\n\t\tcapacities,\n\t\tslots,\n\t\tepsilon = 1e-6,\n\t\tresourceWeights = {},\n\t\treferenceScales,\n\t\tslotCost = 1,\n\t} = options;\n\n\tif (!Number.isInteger(slots) || slots < 0) {\n\t\tthrow new RangeError(`slots must be a non-negative integer, got ${String(slots)}`);\n\t}\n\tif (!Number.isFinite(epsilon) || epsilon <= 0) {\n\t\tthrow new RangeError(`epsilon must be a finite positive number, got ${String(epsilon)}`);\n\t}\n\n\tconst seenSourceIndices = new Set<number>();\n\tconst seenReadySeqs = new Set<number>();\n\tfor (const [position, node] of candidates.entries()) {\n\t\tif (!Number.isInteger(node.sourceIndex) || node.sourceIndex < 0) {\n\t\t\tthrow new RangeError(\n\t\t\t\t`candidates[${position}].sourceIndex must be a non-negative integer, got ${String(node.sourceIndex)}`,\n\t\t\t);\n\t\t}\n\t\tif (seenSourceIndices.has(node.sourceIndex)) {\n\t\t\tthrow new RangeError(\n\t\t\t\t`duplicate candidates[].sourceIndex ${node.sourceIndex} — admit results must be unique per node`,\n\t\t\t);\n\t\t}\n\t\tseenSourceIndices.add(node.sourceIndex);\n\t\tif (!Number.isInteger(node.readySeq) || node.readySeq < 0) {\n\t\t\tthrow new RangeError(\n\t\t\t\t`candidates[${position}].readySeq must be a non-negative integer, got ${String(node.readySeq)}`,\n\t\t\t);\n\t\t}\n\t\tif (seenReadySeqs.has(node.readySeq)) {\n\t\t\tthrow new RangeError(`duplicate candidates[].readySeq ${node.readySeq} — ready ordering must be unambiguous`);\n\t\t}\n\t\tseenReadySeqs.add(node.readySeq);\n\t\tassertFiniteNonNegative(node.priority, `candidates[${position}].priority`);\n\t\tfor (const [name, demand] of Object.entries(node.resources)) {\n\t\t\tassertFiniteNonNegative(demand, `candidates[${position}].resources.${name}`);\n\t\t}\n\t}\n\n\tfor (const [name, value] of Object.entries(runningUsage)) {\n\t\tassertFiniteNonNegative(value, `runningUsage.${name}`);\n\t}\n\tfor (const [name, value] of Object.entries(capacities)) {\n\t\tassertFiniteNonNegative(value, `capacities.${name}`);\n\t}\n\tfor (const [name, value] of Object.entries(resourceWeights)) {\n\t\tassertFiniteNonNegative(value, `resourceWeights.${name}`);\n\t}\n\tif (referenceScales !== undefined) {\n\t\tif (!Number.isFinite(slotCost) || slotCost <= 0) {\n\t\t\tthrow new RangeError(`slotCost must be a finite positive number, got ${String(slotCost)}`);\n\t\t}\n\t\tfor (const [name, scale] of Object.entries(referenceScales)) {\n\t\t\tif (!Number.isFinite(scale) || scale <= 0) {\n\t\t\t\tthrow new RangeError(`referenceScales.${name} must be a finite positive number, got ${String(scale)}`);\n\t\t\t}\n\t\t}\n\t\tfor (const [position, node] of candidates.entries()) {\n\t\t\tfor (const [name, demand] of Object.entries(node.resources)) {\n\t\t\t\tif (demand > 0 && capacities[name] !== 0 && referenceScales[name] === undefined) {\n\t\t\t\t\tthrow new RangeError(\n\t\t\t\t\t\t`referenceScales.${name} is required for a nonzero ${name} demand (candidates[${position}])`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction resourceCost(node: EcrafCandidate, weights: Readonly<Record<string, number>>): number {\n\tlet cost = 0;\n\tfor (const name of Object.keys(node.resources)) {\n\t\tconst weight = weights[name] ?? 1;\n\t\tcost += weight * (node.resources[name] ?? 0);\n\t}\n\treturn cost;\n}\n\nfunction density(\n\tnode: EcrafCandidate,\n\tweights: Readonly<Record<string, number>>,\n\tepsilon: number,\n\treferenceScales?: Readonly<Record<string, number>>,\n\tslotCost = 1,\n): number {\n\t// Normalized mode divides each demand by its reference scale, so per-resource\n\t// unit changes that scale demand and reference together cancel exactly.\n\tconst raw =\n\t\treferenceScales === undefined\n\t\t\t? resourceCost(node, weights)\n\t\t\t: [...Object.entries(node.resources)].reduce(\n\t\t\t\t\t(cost, [name, demand]) =>\n\t\t\t\t\t\tdemand === 0 ? cost : cost + (weights[name] ?? 1) * (demand / referenceScales[name]),\n\t\t\t\t\t0,\n\t\t\t\t);\n\tconst denominator = referenceScales === undefined ? epsilon + raw : epsilon + slotCost + raw;\n\tassertFiniteNonNegative(denominator, `candidate ${node.sourceIndex} density denominator`);\n\tconst score = node.priority / denominator;\n\tassertFiniteNonNegative(score, `candidate ${node.sourceIndex} density`);\n\treturn score;\n}\n\nfunction fits(\n\tnode: EcrafCandidate,\n\tused: Map<string, number>,\n\tcapacities: Readonly<Record<string, number>>,\n\tnormalized: boolean,\n): boolean {\n\tfor (const name of Object.keys(node.resources)) {\n\t\tconst capacity = capacities[name];\n\t\tif (capacity === undefined) continue; // unbounded resource\n\t\tconst needed = node.resources[name] ?? 0;\n\t\tif (normalized && capacity === 0 && needed === 0) continue;\n\t\tconst running = used.get(name) ?? 0;\n\t\tif (running + needed > capacity) return false;\n\t}\n\treturn true;\n}\n\nfunction reserve(node: EcrafCandidate, used: Map<string, number>): void {\n\tfor (const name of Object.keys(node.resources)) {\n\t\tconst total = (used.get(name) ?? 0) + (node.resources[name] ?? 0);\n\t\tassertFiniteNonNegative(total, `candidate ${node.sourceIndex} reserved usage.${name}`);\n\t\tused.set(name, total);\n\t}\n}\n\n/**\n * Greedy admission pass. Deterministic: identical inputs always produce the\n * identical plan. `admit` is ordered by launch preference (density, then\n * readySeq, then sourceIndex); `deferred` preserves source order.\n */\nexport function planEcrafAdmissions(options: EcrafAdmissionsOptions): EcrafAdmissionPlan {\n\tconst version = options.algorithmVersion ?? (options.referenceScales === undefined ? \"legacy-v1\" : \"normalized-v2\");\n\tif (version !== \"legacy-v1\" && version !== \"normalized-v2\") {\n\t\tthrow new RangeError(`Unknown ECRAF algorithmVersion: ${String(version)}`);\n\t}\n\tif (version === \"legacy-v1\" && (options.referenceScales !== undefined || options.slotCost !== undefined)) {\n\t\tthrow new RangeError(\"referenceScales and slotCost require normalized-v2\");\n\t}\n\tif (version === \"normalized-v2\") {\n\t\toptions = {\n\t\t\t...options,\n\t\t\treferenceScales: {\n\t\t\t\t...Object.fromEntries(Object.entries(options.capacities).filter(([, capacity]) => capacity > 0)),\n\t\t\t\t...options.referenceScales,\n\t\t\t},\n\t\t};\n\t}\n\tassertInputContract(options);\n\tconst {\n\t\tcandidates,\n\t\trunningUsage,\n\t\tcapacities,\n\t\tslots,\n\t\tepsilon = 1e-6,\n\t\tresourceWeights = {},\n\t\tconflicts,\n\t\treferenceScales,\n\t\tslotCost = 1,\n\t} = options;\n\n\t// Seed running usage so newly admitted nodes consume from the same budget.\n\tconst used = new Map<string, number>(Object.entries(runningUsage));\n\n\t// Zero-capacity infeasibility precedes scoring in v2. Validate all other\n\t// scores even for singleton and zero-slot batches, before any callbacks.\n\tconst infeasible = new Set(\n\t\tversion === \"normalized-v2\"\n\t\t\t? candidates.filter((node) =>\n\t\t\t\t\tObject.entries(node.resources).some(([name, demand]) => capacities[name] === 0 && demand > 0),\n\t\t\t\t)\n\t\t\t: [],\n\t);\n\tconst sorted = candidates\n\t\t.filter((node) => !infeasible.has(node))\n\t\t.map((node) => ({ node, score: density(node, resourceWeights, epsilon, referenceScales, slotCost) }))\n\t\t.sort(\n\t\t\t(a, b) => b.score - a.score || a.node.readySeq - b.node.readySeq || a.node.sourceIndex - b.node.sourceIndex,\n\t\t);\n\n\tconst admit: number[] = [];\n\tconst deferred: number[] = [...infeasible].map((node) => node.sourceIndex);\n\tconst admittedNodes: EcrafCandidate[] = [];\n\n\tfor (const { node } of sorted) {\n\t\tif (admit.length >= slots) {\n\t\t\tdeferred.push(node.sourceIndex);\n\t\t\tcontinue;\n\t\t}\n\t\tif (conflicts && admittedNodes.some((admitted) => conflicts(node, admitted))) {\n\t\t\tdeferred.push(node.sourceIndex);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!fits(node, used, capacities, version === \"normalized-v2\")) {\n\t\t\tdeferred.push(node.sourceIndex);\n\t\t\tcontinue;\n\t\t}\n\t\treserve(node, used);\n\t\tadmittedNodes.push(node);\n\t\tadmit.push(node.sourceIndex);\n\t}\n\n\tdeferred.sort((a, b) => a - b);\n\treturn { admit, deferred };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"tool-dag-ecraf.js","sourceRoot":"","sources":["../src/tool-dag-ecraf.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * ECRAF — resource-aware ready queue admission planner.\n *\n * Given the current ready frontier, a free-slot count, and multi-dimensional\n * resource capacities, choose which ready nodes to admit next using the\n * deterministic greedy baseline the deep-research report prescribes (PR 8).\n *\n * legacy-v1: D_i = P_i / (epsilon + sum_r weight_r * a_ir)\n * normalized-v2: D_i = P_i / (epsilon + slotCost + sum_r weight_r * a_ir / s_r)\n * Normalization is opt-in, not a live scheduler or a measured improvement.\n *\n * then sorted by density descending, readySeq ascending, sourceIndex ascending.\n * A candidate is admitted only if admitting it does not push cumulative\n * running usage over any capacity and it does not semantically conflict with a\n * node already admitted in this pass. Admission is pure: it computes the plan;\n * the caller launches.\n */\n\nimport { ownResourceNumber } from \"./tool-dag-own-number.ts\";\n\nexport interface EcrafCandidate {\n\t/** Source order of the call within the batch — the stable tiebreak. */\n\treadonly sourceIndex: number;\n\t/** Monotonic order in which the node first entered the ready frontier. */\n\treadonly readySeq: number;\n\t/** Resource vector: resource name -> units consumed while running. */\n\treadonly resources: Readonly<Record<string, number>>;\n\t/** Scalar priority P_i already folded (rank, aging, evidence, risk...). */\n\treadonly priority: number;\n}\n\nexport interface EcrafAdmissionsOptions {\n\t/** Omitted: legacy-v1, or normalized-v2 when referenceScales is supplied. */\n\treadonly algorithmVersion?: \"legacy-v1\" | \"normalized-v2\";\n\treadonly candidates: readonly EcrafCandidate[];\n\t/** Resource units already held by running nodes: name -> units. */\n\treadonly runningUsage: Readonly<Record<string, number>>;\n\t/** Hard capacity per resource: name -> max units. Missing = unbounded. */\n\treadonly capacities: Readonly<Record<string, number>>;\n\t/** Free admission slots (concurrency budget). */\n\treadonly slots: number;\n\t/** Density epsilon guarding division-by-zero. Default 1e-6. */\n\treadonly epsilon?: number;\n\t/** Optional weight per resource in the density denominator. Default 1. */\n\treadonly resourceWeights?: Readonly<Record<string, number>>;\n\t/**\n\t * Opt-in dimensionless normalization (audit §13.2): resource name -> positive\n\t * reference scale s_r. When provided, the density denominator uses unitless\n\t * demands a_ir/s_r, making ranking invariant to per-resource unit changes\n\t * (a'_ir = c_r·a_ir with s'_ir = c_r·s_r preserves the mathematical ratio).\n\t * In normalized-v2, omitted entries use positive total capacities, never\n\t * remaining headroom. Positive unbounded demands require explicit scales.\n\t * Zero capacity is a feasibility gate, not a scale. All supplied scales must\n\t * be finite and positive. Supplying this map without a version opts into v2.\n\t */\n\treadonly referenceScales?: Readonly<Record<string, number>>;\n\t/** Finite positive slot term λ_slot in normalized-v2. Default 1; invalid in v1. */\n\treadonly slotCost?: number;\n\t/**\n\t * Semantic-conflict predicate. Return true when the candidate may not run\n\t * concurrently with an already-admitted node. Defaults to no conflicts.\n\t */\n\treadonly conflicts?: (candidate: EcrafCandidate, running: EcrafCandidate) => boolean;\n}\n\nexport interface EcrafAdmissionPlan {\n\t/** Source indices admitted, in the order they should be launched. */\n\treadonly admit: readonly number[];\n\t/** Source indices deferred this pass (capacity or conflict), in source order. */\n\treadonly deferred: readonly number[];\n}\n\n/**\n * Numeric contract for one admission pass (audit §5.3): every resource\n * demand, running usage, and capacity is a finite non-negative number; the\n * slot budget is a non-negative integer; epsilon is finite and positive;\n * ranking weights are finite and non-negative; candidate source indices and\n * ready sequences are unique, finite integers. Violating these invariants\n * silently poisons the plan — NaN makes every capacity check pass, negative\n * demand manufactures capacity, a fractional slot admits ⌈slots⌉ nodes, and\n * duplicate source indices make the admit list ambiguous — so malformed input\n * is rejected up front rather than coerced into \"unbounded\".\n */\nfunction assertFiniteNonNegative(value: number, label: string): void {\n\tif (!Number.isFinite(value) || value < 0) {\n\t\tthrow new RangeError(`${label} must be a finite non-negative number, got ${String(value)}`);\n\t}\n}\n\nfunction assertInputContract(options: EcrafAdmissionsOptions): void {\n\tconst {\n\t\tcandidates,\n\t\trunningUsage,\n\t\tcapacities,\n\t\tslots,\n\t\tepsilon = 1e-6,\n\t\tresourceWeights = {},\n\t\treferenceScales,\n\t\tslotCost = 1,\n\t} = options;\n\n\tif (!Number.isInteger(slots) || slots < 0) {\n\t\tthrow new RangeError(`slots must be a non-negative integer, got ${String(slots)}`);\n\t}\n\tif (!Number.isFinite(epsilon) || epsilon <= 0) {\n\t\tthrow new RangeError(`epsilon must be a finite positive number, got ${String(epsilon)}`);\n\t}\n\n\tconst seenSourceIndices = new Set<number>();\n\tconst seenReadySeqs = new Set<number>();\n\tfor (const [position, node] of candidates.entries()) {\n\t\tif (!Number.isInteger(node.sourceIndex) || node.sourceIndex < 0) {\n\t\t\tthrow new RangeError(\n\t\t\t\t`candidates[${position}].sourceIndex must be a non-negative integer, got ${String(node.sourceIndex)}`,\n\t\t\t);\n\t\t}\n\t\tif (seenSourceIndices.has(node.sourceIndex)) {\n\t\t\tthrow new RangeError(\n\t\t\t\t`duplicate candidates[].sourceIndex ${node.sourceIndex} — admit results must be unique per node`,\n\t\t\t);\n\t\t}\n\t\tseenSourceIndices.add(node.sourceIndex);\n\t\tif (!Number.isInteger(node.readySeq) || node.readySeq < 0) {\n\t\t\tthrow new RangeError(\n\t\t\t\t`candidates[${position}].readySeq must be a non-negative integer, got ${String(node.readySeq)}`,\n\t\t\t);\n\t\t}\n\t\tif (seenReadySeqs.has(node.readySeq)) {\n\t\t\tthrow new RangeError(`duplicate candidates[].readySeq ${node.readySeq} — ready ordering must be unambiguous`);\n\t\t}\n\t\tseenReadySeqs.add(node.readySeq);\n\t\tassertFiniteNonNegative(node.priority, `candidates[${position}].priority`);\n\t\tfor (const [name, demand] of Object.entries(node.resources)) {\n\t\t\tassertFiniteNonNegative(demand, `candidates[${position}].resources.${name}`);\n\t\t}\n\t}\n\n\tfor (const [name, value] of Object.entries(runningUsage)) {\n\t\tassertFiniteNonNegative(value, `runningUsage.${name}`);\n\t}\n\tfor (const [name, value] of Object.entries(capacities)) {\n\t\tassertFiniteNonNegative(value, `capacities.${name}`);\n\t}\n\tfor (const [name, value] of Object.entries(resourceWeights)) {\n\t\tassertFiniteNonNegative(value, `resourceWeights.${name}`);\n\t}\n\tif (referenceScales !== undefined) {\n\t\tif (!Number.isFinite(slotCost) || slotCost <= 0) {\n\t\t\tthrow new RangeError(`slotCost must be a finite positive number, got ${String(slotCost)}`);\n\t\t}\n\t\tfor (const [name, scale] of Object.entries(referenceScales)) {\n\t\t\tif (!Number.isFinite(scale) || scale <= 0) {\n\t\t\t\tthrow new RangeError(`referenceScales.${name} must be a finite positive number, got ${String(scale)}`);\n\t\t\t}\n\t\t}\n\t\tfor (const [position, node] of candidates.entries()) {\n\t\t\tfor (const [name, demand] of Object.entries(node.resources)) {\n\t\t\t\tif (\n\t\t\t\t\tdemand > 0 &&\n\t\t\t\t\townResourceNumber(capacities, name) !== 0 &&\n\t\t\t\t\townResourceNumber(referenceScales, name) === undefined\n\t\t\t\t) {\n\t\t\t\t\tthrow new RangeError(\n\t\t\t\t\t\t`referenceScales.${name} is required for a nonzero ${name} demand (candidates[${position}])`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction resourceCost(node: EcrafCandidate, weights: Readonly<Record<string, number>>): number {\n\tlet cost = 0;\n\tfor (const name of Object.keys(node.resources)) {\n\t\tconst weight = ownResourceNumber(weights, name) ?? 1;\n\t\tcost += weight * (ownResourceNumber(node.resources, name) ?? 0);\n\t}\n\treturn cost;\n}\n\nfunction density(\n\tnode: EcrafCandidate,\n\tweights: Readonly<Record<string, number>>,\n\tepsilon: number,\n\treferenceScales?: Readonly<Record<string, number>>,\n\tslotCost = 1,\n): number {\n\t// Normalized mode divides each demand by its reference scale, so per-resource\n\t// unit changes that scale demand and reference together cancel exactly.\n\tconst raw =\n\t\treferenceScales === undefined\n\t\t\t? resourceCost(node, weights)\n\t\t\t: [...Object.entries(node.resources)].reduce(\n\t\t\t\t\t(cost, [name, demand]) =>\n\t\t\t\t\t\tdemand === 0\n\t\t\t\t\t\t\t? cost\n\t\t\t\t\t\t\t: cost +\n\t\t\t\t\t\t\t\t(ownResourceNumber(weights, name) ?? 1) *\n\t\t\t\t\t\t\t\t\t(demand / (ownResourceNumber(referenceScales, name) ?? Number.NaN)),\n\t\t\t\t\t0,\n\t\t\t\t);\n\tconst denominator = referenceScales === undefined ? epsilon + raw : epsilon + slotCost + raw;\n\tassertFiniteNonNegative(denominator, `candidate ${node.sourceIndex} density denominator`);\n\tconst score = node.priority / denominator;\n\tassertFiniteNonNegative(score, `candidate ${node.sourceIndex} density`);\n\treturn score;\n}\n\nfunction fits(\n\tnode: EcrafCandidate,\n\tused: Map<string, number>,\n\tcapacities: Readonly<Record<string, number>>,\n\tnormalized: boolean,\n): boolean {\n\tfor (const name of Object.keys(node.resources)) {\n\t\tconst capacity = ownResourceNumber(capacities, name);\n\t\tif (capacity === undefined) continue; // unbounded resource\n\t\tconst needed = ownResourceNumber(node.resources, name) ?? 0;\n\t\tif (normalized && capacity === 0 && needed === 0) continue;\n\t\tconst running = used.get(name) ?? 0;\n\t\tif (running + needed > capacity) return false;\n\t}\n\treturn true;\n}\n\nfunction reserve(node: EcrafCandidate, used: Map<string, number>): void {\n\tfor (const name of Object.keys(node.resources)) {\n\t\tconst total = (used.get(name) ?? 0) + (ownResourceNumber(node.resources, name) ?? 0);\n\t\tassertFiniteNonNegative(total, `candidate ${node.sourceIndex} reserved usage.${name}`);\n\t\tused.set(name, total);\n\t}\n}\n\n/**\n * Greedy admission pass. Deterministic: identical inputs always produce the\n * identical plan. `admit` is ordered by launch preference (density, then\n * readySeq, then sourceIndex); `deferred` preserves source order.\n */\nexport function planEcrafAdmissions(options: EcrafAdmissionsOptions): EcrafAdmissionPlan {\n\tconst version = options.algorithmVersion ?? (options.referenceScales === undefined ? \"legacy-v1\" : \"normalized-v2\");\n\tif (version !== \"legacy-v1\" && version !== \"normalized-v2\") {\n\t\tthrow new RangeError(`Unknown ECRAF algorithmVersion: ${String(version)}`);\n\t}\n\tif (version === \"legacy-v1\" && (options.referenceScales !== undefined || options.slotCost !== undefined)) {\n\t\tthrow new RangeError(\"referenceScales and slotCost require normalized-v2\");\n\t}\n\tif (version === \"normalized-v2\") {\n\t\toptions = {\n\t\t\t...options,\n\t\t\treferenceScales: {\n\t\t\t\t...Object.fromEntries(Object.entries(options.capacities).filter(([, capacity]) => capacity > 0)),\n\t\t\t\t...options.referenceScales,\n\t\t\t},\n\t\t};\n\t}\n\tassertInputContract(options);\n\tconst {\n\t\tcandidates,\n\t\trunningUsage,\n\t\tcapacities,\n\t\tslots,\n\t\tepsilon = 1e-6,\n\t\tresourceWeights = {},\n\t\tconflicts,\n\t\treferenceScales,\n\t\tslotCost = 1,\n\t} = options;\n\n\t// Seed running usage so newly admitted nodes consume from the same budget.\n\tconst used = new Map<string, number>(Object.entries(runningUsage));\n\n\t// Zero-capacity infeasibility precedes scoring in v2. Validate all other\n\t// scores even for singleton and zero-slot batches, before any callbacks.\n\tconst infeasible = new Set(\n\t\tversion === \"normalized-v2\"\n\t\t\t? candidates.filter((node) =>\n\t\t\t\t\tObject.entries(node.resources).some(\n\t\t\t\t\t\t([name, demand]) => ownResourceNumber(capacities, name) === 0 && demand > 0,\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t: [],\n\t);\n\tconst sorted = candidates\n\t\t.filter((node) => !infeasible.has(node))\n\t\t.map((node) => ({ node, score: density(node, resourceWeights, epsilon, referenceScales, slotCost) }))\n\t\t.sort(\n\t\t\t(a, b) => b.score - a.score || a.node.readySeq - b.node.readySeq || a.node.sourceIndex - b.node.sourceIndex,\n\t\t);\n\n\tconst admit: number[] = [];\n\tconst deferred: number[] = [...infeasible].map((node) => node.sourceIndex);\n\tconst admittedNodes: EcrafCandidate[] = [];\n\n\tfor (const { node } of sorted) {\n\t\tif (admit.length >= slots) {\n\t\t\tdeferred.push(node.sourceIndex);\n\t\t\tcontinue;\n\t\t}\n\t\tif (conflicts && admittedNodes.some((admitted) => conflicts(node, admitted))) {\n\t\t\tdeferred.push(node.sourceIndex);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!fits(node, used, capacities, version === \"normalized-v2\")) {\n\t\t\tdeferred.push(node.sourceIndex);\n\t\t\tcontinue;\n\t\t}\n\t\treserve(node, used);\n\t\tadmittedNodes.push(node);\n\t\tadmit.push(node.sourceIndex);\n\t}\n\n\tdeferred.sort((a, b) => a - b);\n\treturn { admit, deferred };\n}\n"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ToolClaimResolution } from "./tool-resource-claims.ts";
|
|
2
|
+
interface Entry {
|
|
3
|
+
readonly resolution: ToolClaimResolution;
|
|
4
|
+
}
|
|
5
|
+
export interface DagIndexStats {
|
|
6
|
+
readonly predicateCalls: number;
|
|
7
|
+
readonly memberships: number;
|
|
8
|
+
readonly fallback: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface DagIndexOptions {
|
|
11
|
+
/** Memory proxy, counting distinct index memberships. Zero selects the oracle. */
|
|
12
|
+
readonly maxMemberships?: number;
|
|
13
|
+
}
|
|
14
|
+
export declare function buildIndexedDagDependencies<T extends Entry>(entries: readonly T[], conflicts: (earlier: T, current: T) => boolean, options?: DagIndexOptions): {
|
|
15
|
+
dependencies: number[][];
|
|
16
|
+
stats: DagIndexStats;
|
|
17
|
+
};
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=tool-dag-index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-dag-index.d.ts","sourceRoot":"","sources":["../src/tool-dag-index.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,mBAAmB,EAAqB,MAAM,2BAA2B,CAAC;AAExF,UAAU,KAAK;IACd,QAAQ,CAAC,UAAU,EAAE,mBAAmB,CAAC;CACzC;AAUD,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC3B;AACD,MAAM,WAAW,eAAe;IAC/B,kFAAkF;IAClF,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CACjC;AAcD,wBAAgB,2BAA2B,CAAC,CAAC,SAAS,KAAK,EAC1D,OAAO,EAAE,SAAS,CAAC,EAAE,EACrB,SAAS,EAAE,CAAC,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,KAAK,OAAO,EAC9C,OAAO,GAAE,eAAoB,GAC3B;IAAE,YAAY,EAAE,MAAM,EAAE,EAAE,CAAC;IAAC,KAAK,EAAE,aAAa,CAAA;CAAE,CAuIpD","sourcesContent":["/**\n * Conservative candidate index for the existing source-directed conflict graph.\n * This changes neither the authoritative predicate nor the graph's edges.\n * POSIX lexical/real paths use a prefix trie. Other path forms use a broad bucket.\n * Every candidate is checked by the caller's existing conflict predicate.\n */\nimport { canonicalizeLexicalPath } from \"./path-segments.ts\";\nimport type { ToolClaimResolution, ToolResourceClaim } from \"./tool-resource-claims.ts\";\n\ninterface Entry {\n\treadonly resolution: ToolClaimResolution;\n}\ninterface Bucket {\n\treadonly reads: Set<number>;\n\treadonly writes: Set<number>;\n}\ninterface Trie {\n\treadonly exact: Bucket;\n\treadonly subtree: Bucket;\n\treadonly children: Map<string, Trie>;\n}\nexport interface DagIndexStats {\n\treadonly predicateCalls: number;\n\treadonly memberships: number;\n\treadonly fallback: boolean;\n}\nexport interface DagIndexOptions {\n\t/** Memory proxy, counting distinct index memberships. Zero selects the oracle. */\n\treadonly maxMemberships?: number;\n}\nconst bucket = (): Bucket => ({ reads: new Set(), writes: new Set() });\nconst trie = (): Trie => ({ exact: bucket(), subtree: bucket(), children: new Map() });\nclass IndexCapacityExceeded extends Error {}\n\n/** Only index path forms for which prefix equivalence is proved by path-segments.ts. */\nfunction posixParts(raw: string): string[] | undefined {\n\tif (!raw.startsWith(\"/\") || raw.startsWith(\"//\") || raw.includes(\"\\\\\") || raw.length > 131_072) return undefined;\n\tconst canonical = canonicalizeLexicalPath(raw);\n\tif (canonical === null) return undefined;\n\tconst parts = canonical.split(\"/\").filter(Boolean);\n\treturn parts.length > 4096 ? undefined : parts.map((part) => part.toLowerCase());\n}\n\nexport function buildIndexedDagDependencies<T extends Entry>(\n\tentries: readonly T[],\n\tconflicts: (earlier: T, current: T) => boolean,\n\toptions: DagIndexOptions = {},\n): { dependencies: number[][]; stats: DagIndexStats } {\n\tconst maxMemberships = options.maxMemberships ?? 131_072;\n\tif (!Number.isSafeInteger(maxMemberships) || maxMemberships < 0) throw new RangeError(\"Invalid DAG index budget\");\n\tlet predicateCalls = 0;\n\tlet memberships = 0;\n\tconst check = (a: number, b: number): boolean => {\n\t\tpredicateCalls++;\n\t\treturn conflicts(entries[a], entries[b]);\n\t};\n\tconst oracle = (): number[][] =>\n\t\tentries.map((_, current) => {\n\t\t\tconst blockers: number[] = [];\n\t\t\tfor (let earlier = 0; earlier < current; earlier++) if (check(earlier, current)) blockers.push(earlier);\n\t\t\treturn blockers;\n\t\t});\n\tif (\n\t\tentries.every(\n\t\t\t({ resolution }) => resolution.kind === \"claims\" && resolution.claims.every((c) => c.access === \"read\"),\n\t\t)\n\t) {\n\t\treturn { dependencies: entries.map(() => []), stats: { predicateCalls, memberships, fallback: false } };\n\t}\n\tif (maxMemberships === 0) return { dependencies: oracle(), stats: { predicateCalls, memberships, fallback: true } };\n\n\tconst roots = trie();\n\tconst allPaths = bucket();\n\tconst broadPaths = bucket();\n\tconst inodes = new Map<string, Bucket>();\n\tconst exact = new Map<string, Map<string, Bucket>>();\n\tconst exclusive = new Set<number>();\n\tconst add = (set: Set<number>, index: number): void => {\n\t\tif (set.has(index)) return;\n\t\tif (memberships >= maxMemberships) throw new IndexCapacityExceeded();\n\t\tset.add(index);\n\t\tmemberships++;\n\t};\n\tconst put = (where: Bucket, access: string, index: number): void =>\n\t\tadd(access === \"read\" ? where.reads : where.writes, index);\n\tconst collect = (where: Bucket | undefined, access: string, into: Set<number>): void => {\n\t\tif (!where) return;\n\t\tfor (const id of where.writes) into.add(id);\n\t\tif (access !== \"read\") for (const id of where.reads) into.add(id);\n\t};\n\tconst queryPath = (parts: string[], access: string, into: Set<number>): void => {\n\t\tlet node = roots;\n\t\tcollect(node.exact, access, into);\n\t\tfor (const part of parts) {\n\t\t\tconst child = node.children.get(part);\n\t\t\tif (!child) return;\n\t\t\tnode = child;\n\t\t\tcollect(node.exact, access, into);\n\t\t}\n\t\tcollect(node.subtree, access, into);\n\t};\n\tconst insertPath = (parts: string[], access: string, index: number): void => {\n\t\tlet node = roots;\n\t\tput(node.subtree, access, index);\n\t\tfor (const part of parts) {\n\t\t\tlet child = node.children.get(part);\n\t\t\tif (!child) {\n\t\t\t\tif (memberships >= maxMemberships) throw new IndexCapacityExceeded();\n\t\t\t\tchild = trie();\n\t\t\t\tnode.children.set(part, child);\n\t\t\t}\n\t\t\tnode = child;\n\t\t\tput(node.subtree, access, index);\n\t\t}\n\t\tput(node.exact, access, index);\n\t};\n\tconst pathKeys = (claim: Extract<ToolResourceClaim, { kind: \"path\" }>): string[] =>\n\t\tclaim.realKey === undefined || claim.realKey === claim.key ? [claim.key] : [claim.key, claim.realKey];\n\tconst dependencies: number[][] = [];\n\ttry {\n\t\tfor (let current = 0; current < entries.length; current++) {\n\t\t\tconst resolution = entries[current].resolution;\n\t\t\tconst isExclusive = resolution.kind === \"exclusive\" || resolution.claims.some((c) => c.access === \"exclusive\");\n\t\t\tconst candidates = new Set(exclusive);\n\t\t\tif (isExclusive) {\n\t\t\t\tfor (let earlier = 0; earlier < current; earlier++) candidates.add(earlier);\n\t\t\t} else if (resolution.kind === \"claims\") {\n\t\t\t\tfor (const claim of resolution.claims) {\n\t\t\t\t\tif (claim.kind !== \"path\") {\n\t\t\t\t\t\tcollect(exact.get(claim.kind)?.get(claim.key), claim.access, candidates);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tconst parts = pathKeys(claim).map(posixParts);\n\t\t\t\t\tif (parts.some((p) => p === undefined)) collect(allPaths, claim.access, candidates);\n\t\t\t\t\telse {\n\t\t\t\t\t\tcollect(broadPaths, claim.access, candidates);\n\t\t\t\t\t\tfor (const p of parts) if (p) queryPath(p, claim.access, candidates);\n\t\t\t\t\t}\n\t\t\t\t\tif (claim.inodeKey !== undefined) collect(inodes.get(claim.inodeKey), claim.access, candidates);\n\t\t\t\t}\n\t\t\t}\n\t\t\tdependencies.push([...candidates].sort((a, b) => a - b).filter((earlier) => check(earlier, current)));\n\t\t\tif (isExclusive) {\n\t\t\t\tadd(exclusive, current);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (resolution.kind !== \"claims\") continue;\n\t\t\tfor (const claim of resolution.claims) {\n\t\t\t\tif (claim.kind !== \"path\") {\n\t\t\t\t\tlet kind = exact.get(claim.kind);\n\t\t\t\t\tif (!kind) {\n\t\t\t\t\t\tkind = new Map();\n\t\t\t\t\t\texact.set(claim.kind, kind);\n\t\t\t\t\t}\n\t\t\t\t\tlet values = kind.get(claim.key);\n\t\t\t\t\tif (!values) {\n\t\t\t\t\t\tvalues = bucket();\n\t\t\t\t\t\tkind.set(claim.key, values);\n\t\t\t\t\t}\n\t\t\t\t\tput(values, claim.access, current);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tput(allPaths, claim.access, current);\n\t\t\t\tconst parts = pathKeys(claim).map(posixParts);\n\t\t\t\tif (parts.some((p) => p === undefined)) put(broadPaths, claim.access, current);\n\t\t\t\telse for (const p of parts) if (p) insertPath(p, claim.access, current);\n\t\t\t\tif (claim.inodeKey !== undefined) {\n\t\t\t\t\tlet values = inodes.get(claim.inodeKey);\n\t\t\t\t\tif (!values) {\n\t\t\t\t\t\tvalues = bucket();\n\t\t\t\t\t\tinodes.set(claim.inodeKey, values);\n\t\t\t\t\t}\n\t\t\t\t\tput(values, claim.access, current);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn { dependencies, stats: { predicateCalls, memberships, fallback: false } };\n\t} catch (error) {\n\t\tif (!(error instanceof IndexCapacityExceeded)) throw error;\n\t\t// No partial graph escapes. Deterministic recomputation uses the original predicate.\n\t\treturn { dependencies: oracle(), stats: { predicateCalls, memberships, fallback: true } };\n\t}\n}\n"]}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conservative candidate index for the existing source-directed conflict graph.
|
|
3
|
+
* This changes neither the authoritative predicate nor the graph's edges.
|
|
4
|
+
* POSIX lexical/real paths use a prefix trie. Other path forms use a broad bucket.
|
|
5
|
+
* Every candidate is checked by the caller's existing conflict predicate.
|
|
6
|
+
*/
|
|
7
|
+
import { canonicalizeLexicalPath } from "./path-segments.js";
|
|
8
|
+
const bucket = () => ({ reads: new Set(), writes: new Set() });
|
|
9
|
+
const trie = () => ({ exact: bucket(), subtree: bucket(), children: new Map() });
|
|
10
|
+
class IndexCapacityExceeded extends Error {
|
|
11
|
+
}
|
|
12
|
+
/** Only index path forms for which prefix equivalence is proved by path-segments.ts. */
|
|
13
|
+
function posixParts(raw) {
|
|
14
|
+
if (!raw.startsWith("/") || raw.startsWith("//") || raw.includes("\\") || raw.length > 131_072)
|
|
15
|
+
return undefined;
|
|
16
|
+
const canonical = canonicalizeLexicalPath(raw);
|
|
17
|
+
if (canonical === null)
|
|
18
|
+
return undefined;
|
|
19
|
+
const parts = canonical.split("/").filter(Boolean);
|
|
20
|
+
return parts.length > 4096 ? undefined : parts.map((part) => part.toLowerCase());
|
|
21
|
+
}
|
|
22
|
+
export function buildIndexedDagDependencies(entries, conflicts, options = {}) {
|
|
23
|
+
const maxMemberships = options.maxMemberships ?? 131_072;
|
|
24
|
+
if (!Number.isSafeInteger(maxMemberships) || maxMemberships < 0)
|
|
25
|
+
throw new RangeError("Invalid DAG index budget");
|
|
26
|
+
let predicateCalls = 0;
|
|
27
|
+
let memberships = 0;
|
|
28
|
+
const check = (a, b) => {
|
|
29
|
+
predicateCalls++;
|
|
30
|
+
return conflicts(entries[a], entries[b]);
|
|
31
|
+
};
|
|
32
|
+
const oracle = () => entries.map((_, current) => {
|
|
33
|
+
const blockers = [];
|
|
34
|
+
for (let earlier = 0; earlier < current; earlier++)
|
|
35
|
+
if (check(earlier, current))
|
|
36
|
+
blockers.push(earlier);
|
|
37
|
+
return blockers;
|
|
38
|
+
});
|
|
39
|
+
if (entries.every(({ resolution }) => resolution.kind === "claims" && resolution.claims.every((c) => c.access === "read"))) {
|
|
40
|
+
return { dependencies: entries.map(() => []), stats: { predicateCalls, memberships, fallback: false } };
|
|
41
|
+
}
|
|
42
|
+
if (maxMemberships === 0)
|
|
43
|
+
return { dependencies: oracle(), stats: { predicateCalls, memberships, fallback: true } };
|
|
44
|
+
const roots = trie();
|
|
45
|
+
const allPaths = bucket();
|
|
46
|
+
const broadPaths = bucket();
|
|
47
|
+
const inodes = new Map();
|
|
48
|
+
const exact = new Map();
|
|
49
|
+
const exclusive = new Set();
|
|
50
|
+
const add = (set, index) => {
|
|
51
|
+
if (set.has(index))
|
|
52
|
+
return;
|
|
53
|
+
if (memberships >= maxMemberships)
|
|
54
|
+
throw new IndexCapacityExceeded();
|
|
55
|
+
set.add(index);
|
|
56
|
+
memberships++;
|
|
57
|
+
};
|
|
58
|
+
const put = (where, access, index) => add(access === "read" ? where.reads : where.writes, index);
|
|
59
|
+
const collect = (where, access, into) => {
|
|
60
|
+
if (!where)
|
|
61
|
+
return;
|
|
62
|
+
for (const id of where.writes)
|
|
63
|
+
into.add(id);
|
|
64
|
+
if (access !== "read")
|
|
65
|
+
for (const id of where.reads)
|
|
66
|
+
into.add(id);
|
|
67
|
+
};
|
|
68
|
+
const queryPath = (parts, access, into) => {
|
|
69
|
+
let node = roots;
|
|
70
|
+
collect(node.exact, access, into);
|
|
71
|
+
for (const part of parts) {
|
|
72
|
+
const child = node.children.get(part);
|
|
73
|
+
if (!child)
|
|
74
|
+
return;
|
|
75
|
+
node = child;
|
|
76
|
+
collect(node.exact, access, into);
|
|
77
|
+
}
|
|
78
|
+
collect(node.subtree, access, into);
|
|
79
|
+
};
|
|
80
|
+
const insertPath = (parts, access, index) => {
|
|
81
|
+
let node = roots;
|
|
82
|
+
put(node.subtree, access, index);
|
|
83
|
+
for (const part of parts) {
|
|
84
|
+
let child = node.children.get(part);
|
|
85
|
+
if (!child) {
|
|
86
|
+
if (memberships >= maxMemberships)
|
|
87
|
+
throw new IndexCapacityExceeded();
|
|
88
|
+
child = trie();
|
|
89
|
+
node.children.set(part, child);
|
|
90
|
+
}
|
|
91
|
+
node = child;
|
|
92
|
+
put(node.subtree, access, index);
|
|
93
|
+
}
|
|
94
|
+
put(node.exact, access, index);
|
|
95
|
+
};
|
|
96
|
+
const pathKeys = (claim) => claim.realKey === undefined || claim.realKey === claim.key ? [claim.key] : [claim.key, claim.realKey];
|
|
97
|
+
const dependencies = [];
|
|
98
|
+
try {
|
|
99
|
+
for (let current = 0; current < entries.length; current++) {
|
|
100
|
+
const resolution = entries[current].resolution;
|
|
101
|
+
const isExclusive = resolution.kind === "exclusive" || resolution.claims.some((c) => c.access === "exclusive");
|
|
102
|
+
const candidates = new Set(exclusive);
|
|
103
|
+
if (isExclusive) {
|
|
104
|
+
for (let earlier = 0; earlier < current; earlier++)
|
|
105
|
+
candidates.add(earlier);
|
|
106
|
+
}
|
|
107
|
+
else if (resolution.kind === "claims") {
|
|
108
|
+
for (const claim of resolution.claims) {
|
|
109
|
+
if (claim.kind !== "path") {
|
|
110
|
+
collect(exact.get(claim.kind)?.get(claim.key), claim.access, candidates);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
const parts = pathKeys(claim).map(posixParts);
|
|
114
|
+
if (parts.some((p) => p === undefined))
|
|
115
|
+
collect(allPaths, claim.access, candidates);
|
|
116
|
+
else {
|
|
117
|
+
collect(broadPaths, claim.access, candidates);
|
|
118
|
+
for (const p of parts)
|
|
119
|
+
if (p)
|
|
120
|
+
queryPath(p, claim.access, candidates);
|
|
121
|
+
}
|
|
122
|
+
if (claim.inodeKey !== undefined)
|
|
123
|
+
collect(inodes.get(claim.inodeKey), claim.access, candidates);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
dependencies.push([...candidates].sort((a, b) => a - b).filter((earlier) => check(earlier, current)));
|
|
127
|
+
if (isExclusive) {
|
|
128
|
+
add(exclusive, current);
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (resolution.kind !== "claims")
|
|
132
|
+
continue;
|
|
133
|
+
for (const claim of resolution.claims) {
|
|
134
|
+
if (claim.kind !== "path") {
|
|
135
|
+
let kind = exact.get(claim.kind);
|
|
136
|
+
if (!kind) {
|
|
137
|
+
kind = new Map();
|
|
138
|
+
exact.set(claim.kind, kind);
|
|
139
|
+
}
|
|
140
|
+
let values = kind.get(claim.key);
|
|
141
|
+
if (!values) {
|
|
142
|
+
values = bucket();
|
|
143
|
+
kind.set(claim.key, values);
|
|
144
|
+
}
|
|
145
|
+
put(values, claim.access, current);
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
put(allPaths, claim.access, current);
|
|
149
|
+
const parts = pathKeys(claim).map(posixParts);
|
|
150
|
+
if (parts.some((p) => p === undefined))
|
|
151
|
+
put(broadPaths, claim.access, current);
|
|
152
|
+
else
|
|
153
|
+
for (const p of parts)
|
|
154
|
+
if (p)
|
|
155
|
+
insertPath(p, claim.access, current);
|
|
156
|
+
if (claim.inodeKey !== undefined) {
|
|
157
|
+
let values = inodes.get(claim.inodeKey);
|
|
158
|
+
if (!values) {
|
|
159
|
+
values = bucket();
|
|
160
|
+
inodes.set(claim.inodeKey, values);
|
|
161
|
+
}
|
|
162
|
+
put(values, claim.access, current);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return { dependencies, stats: { predicateCalls, memberships, fallback: false } };
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
if (!(error instanceof IndexCapacityExceeded))
|
|
170
|
+
throw error;
|
|
171
|
+
// No partial graph escapes. Deterministic recomputation uses the original predicate.
|
|
172
|
+
return { dependencies: oracle(), stats: { predicateCalls, memberships, fallback: true } };
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=tool-dag-index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-dag-index.js","sourceRoot":"","sources":["../src/tool-dag-index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAwB7D,MAAM,MAAM,GAAG,GAAW,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,GAAG,EAAE,EAAE,MAAM,EAAE,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC;AACvE,MAAM,IAAI,GAAG,GAAS,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC;AACvF,MAAM,qBAAsB,SAAQ,KAAK;CAAG;AAE5C,wFAAwF;AACxF,SAAS,UAAU,CAAC,GAAW,EAAwB;IACtD,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,MAAM,GAAG,OAAO;QAAE,OAAO,SAAS,CAAC;IACjH,MAAM,SAAS,GAAG,uBAAuB,CAAC,GAAG,CAAC,CAAC;IAC/C,IAAI,SAAS,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACzC,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACnD,OAAO,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;AAAA,CACjF;AAED,MAAM,UAAU,2BAA2B,CAC1C,OAAqB,EACrB,SAA8C,EAC9C,OAAO,GAAoB,EAAE,EACwB;IACrD,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,OAAO,CAAC;IACzD,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,cAAc,CAAC,IAAI,cAAc,GAAG,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,0BAA0B,CAAC,CAAC;IAClH,IAAI,cAAc,GAAG,CAAC,CAAC;IACvB,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,MAAM,KAAK,GAAG,CAAC,CAAS,EAAE,CAAS,EAAW,EAAE,CAAC;QAChD,cAAc,EAAE,CAAC;QACjB,OAAO,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IAAA,CACzC,CAAC;IACF,MAAM,MAAM,GAAG,GAAe,EAAE,CAC/B,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC;QAC3B,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,OAAO,EAAE,OAAO,EAAE;YAAE,IAAI,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC;gBAAE,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACxG,OAAO,QAAQ,CAAC;IAAA,CAChB,CAAC,CAAC;IACJ,IACC,OAAO,CAAC,KAAK,CACZ,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,KAAK,QAAQ,IAAI,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CACvG,EACA,CAAC;QACF,OAAO,EAAE,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,CAAC;IACzG,CAAC;IACD,IAAI,cAAc,KAAK,CAAC;QAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC;IAEpH,MAAM,KAAK,GAAG,IAAI,EAAE,CAAC;IACrB,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC;IAC1B,MAAM,UAAU,GAAG,MAAM,EAAE,CAAC;IAC5B,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,MAAM,KAAK,GAAG,IAAI,GAAG,EAA+B,CAAC;IACrD,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IACpC,MAAM,GAAG,GAAG,CAAC,GAAgB,EAAE,KAAa,EAAQ,EAAE,CAAC;QACtD,IAAI,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,OAAO;QAC3B,IAAI,WAAW,IAAI,cAAc;YAAE,MAAM,IAAI,qBAAqB,EAAE,CAAC;QACrE,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACf,WAAW,EAAE,CAAC;IAAA,CACd,CAAC;IACF,MAAM,GAAG,GAAG,CAAC,KAAa,EAAE,MAAc,EAAE,KAAa,EAAQ,EAAE,CAClE,GAAG,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC5D,MAAM,OAAO,GAAG,CAAC,KAAyB,EAAE,MAAc,EAAE,IAAiB,EAAQ,EAAE,CAAC;QACvF,IAAI,CAAC,KAAK;YAAE,OAAO;QACnB,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,MAAM;YAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC5C,IAAI,MAAM,KAAK,MAAM;YAAE,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,KAAK;gBAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAAA,CAClE,CAAC;IACF,MAAM,SAAS,GAAG,CAAC,KAAe,EAAE,MAAc,EAAE,IAAiB,EAAQ,EAAE,CAAC;QAC/E,IAAI,IAAI,GAAG,KAAK,CAAC;QACjB,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;QAClC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACtC,IAAI,CAAC,KAAK;gBAAE,OAAO;YACnB,IAAI,GAAG,KAAK,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;QACnC,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;IAAA,CACpC,CAAC;IACF,MAAM,UAAU,GAAG,CAAC,KAAe,EAAE,MAAc,EAAE,KAAa,EAAQ,EAAE,CAAC;QAC5E,IAAI,IAAI,GAAG,KAAK,CAAC;QACjB,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;QACjC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YAC1B,IAAI,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACpC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACZ,IAAI,WAAW,IAAI,cAAc;oBAAE,MAAM,IAAI,qBAAqB,EAAE,CAAC;gBACrE,KAAK,GAAG,IAAI,EAAE,CAAC;gBACf,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAChC,CAAC;YACD,IAAI,GAAG,KAAK,CAAC;YACb,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;QAClC,CAAC;QACD,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IAAA,CAC/B,CAAC;IACF,MAAM,QAAQ,GAAG,CAAC,KAAmD,EAAY,EAAE,CAClF,KAAK,CAAC,OAAO,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,KAAK,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IACvG,MAAM,YAAY,GAAe,EAAE,CAAC;IACpC,IAAI,CAAC;QACJ,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC;YAC3D,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC;YAC/C,MAAM,WAAW,GAAG,UAAU,CAAC,IAAI,KAAK,WAAW,IAAI,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC;YAC/G,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;YACtC,IAAI,WAAW,EAAE,CAAC;gBACjB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,OAAO,EAAE,OAAO,EAAE;oBAAE,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAC7E,CAAC;iBAAM,IAAI,UAAU,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACzC,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,MAAM,EAAE,CAAC;oBACvC,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;wBAC3B,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;wBACzE,SAAS;oBACV,CAAC;oBACD,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;oBAC9C,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC;wBAAE,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;yBAC/E,CAAC;wBACL,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;wBAC9C,KAAK,MAAM,CAAC,IAAI,KAAK;4BAAE,IAAI,CAAC;gCAAE,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;oBACtE,CAAC;oBACD,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS;wBAAE,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;gBACjG,CAAC;YACF,CAAC;YACD,YAAY,CAAC,IAAI,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;YACtG,IAAI,WAAW,EAAE,CAAC;gBACjB,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;gBACxB,SAAS;YACV,CAAC;YACD,IAAI,UAAU,CAAC,IAAI,KAAK,QAAQ;gBAAE,SAAS;YAC3C,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,MAAM,EAAE,CAAC;gBACvC,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;oBAC3B,IAAI,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;oBACjC,IAAI,CAAC,IAAI,EAAE,CAAC;wBACX,IAAI,GAAG,IAAI,GAAG,EAAE,CAAC;wBACjB,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;oBAC7B,CAAC;oBACD,IAAI,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;oBACjC,IAAI,CAAC,MAAM,EAAE,CAAC;wBACb,MAAM,GAAG,MAAM,EAAE,CAAC;wBAClB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;oBAC7B,CAAC;oBACD,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;oBACnC,SAAS;gBACV,CAAC;gBACD,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBACrC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;gBAC9C,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC;oBAAE,GAAG,CAAC,UAAU,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;;oBAC1E,KAAK,MAAM,CAAC,IAAI,KAAK;wBAAE,IAAI,CAAC;4BAAE,UAAU,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBACxE,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;oBAClC,IAAI,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;oBACxC,IAAI,CAAC,MAAM,EAAE,CAAC;wBACb,MAAM,GAAG,MAAM,EAAE,CAAC;wBAClB,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;oBACpC,CAAC;oBACD,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBACpC,CAAC;YACF,CAAC;QACF,CAAC;QACD,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,CAAC;IAClF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAI,CAAC,CAAC,KAAK,YAAY,qBAAqB,CAAC;YAAE,MAAM,KAAK,CAAC;QAC3D,qFAAqF;QACrF,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC;IAC3F,CAAC;AAAA,CACD","sourcesContent":["/**\n * Conservative candidate index for the existing source-directed conflict graph.\n * This changes neither the authoritative predicate nor the graph's edges.\n * POSIX lexical/real paths use a prefix trie. Other path forms use a broad bucket.\n * Every candidate is checked by the caller's existing conflict predicate.\n */\nimport { canonicalizeLexicalPath } from \"./path-segments.ts\";\nimport type { ToolClaimResolution, ToolResourceClaim } from \"./tool-resource-claims.ts\";\n\ninterface Entry {\n\treadonly resolution: ToolClaimResolution;\n}\ninterface Bucket {\n\treadonly reads: Set<number>;\n\treadonly writes: Set<number>;\n}\ninterface Trie {\n\treadonly exact: Bucket;\n\treadonly subtree: Bucket;\n\treadonly children: Map<string, Trie>;\n}\nexport interface DagIndexStats {\n\treadonly predicateCalls: number;\n\treadonly memberships: number;\n\treadonly fallback: boolean;\n}\nexport interface DagIndexOptions {\n\t/** Memory proxy, counting distinct index memberships. Zero selects the oracle. */\n\treadonly maxMemberships?: number;\n}\nconst bucket = (): Bucket => ({ reads: new Set(), writes: new Set() });\nconst trie = (): Trie => ({ exact: bucket(), subtree: bucket(), children: new Map() });\nclass IndexCapacityExceeded extends Error {}\n\n/** Only index path forms for which prefix equivalence is proved by path-segments.ts. */\nfunction posixParts(raw: string): string[] | undefined {\n\tif (!raw.startsWith(\"/\") || raw.startsWith(\"//\") || raw.includes(\"\\\\\") || raw.length > 131_072) return undefined;\n\tconst canonical = canonicalizeLexicalPath(raw);\n\tif (canonical === null) return undefined;\n\tconst parts = canonical.split(\"/\").filter(Boolean);\n\treturn parts.length > 4096 ? undefined : parts.map((part) => part.toLowerCase());\n}\n\nexport function buildIndexedDagDependencies<T extends Entry>(\n\tentries: readonly T[],\n\tconflicts: (earlier: T, current: T) => boolean,\n\toptions: DagIndexOptions = {},\n): { dependencies: number[][]; stats: DagIndexStats } {\n\tconst maxMemberships = options.maxMemberships ?? 131_072;\n\tif (!Number.isSafeInteger(maxMemberships) || maxMemberships < 0) throw new RangeError(\"Invalid DAG index budget\");\n\tlet predicateCalls = 0;\n\tlet memberships = 0;\n\tconst check = (a: number, b: number): boolean => {\n\t\tpredicateCalls++;\n\t\treturn conflicts(entries[a], entries[b]);\n\t};\n\tconst oracle = (): number[][] =>\n\t\tentries.map((_, current) => {\n\t\t\tconst blockers: number[] = [];\n\t\t\tfor (let earlier = 0; earlier < current; earlier++) if (check(earlier, current)) blockers.push(earlier);\n\t\t\treturn blockers;\n\t\t});\n\tif (\n\t\tentries.every(\n\t\t\t({ resolution }) => resolution.kind === \"claims\" && resolution.claims.every((c) => c.access === \"read\"),\n\t\t)\n\t) {\n\t\treturn { dependencies: entries.map(() => []), stats: { predicateCalls, memberships, fallback: false } };\n\t}\n\tif (maxMemberships === 0) return { dependencies: oracle(), stats: { predicateCalls, memberships, fallback: true } };\n\n\tconst roots = trie();\n\tconst allPaths = bucket();\n\tconst broadPaths = bucket();\n\tconst inodes = new Map<string, Bucket>();\n\tconst exact = new Map<string, Map<string, Bucket>>();\n\tconst exclusive = new Set<number>();\n\tconst add = (set: Set<number>, index: number): void => {\n\t\tif (set.has(index)) return;\n\t\tif (memberships >= maxMemberships) throw new IndexCapacityExceeded();\n\t\tset.add(index);\n\t\tmemberships++;\n\t};\n\tconst put = (where: Bucket, access: string, index: number): void =>\n\t\tadd(access === \"read\" ? where.reads : where.writes, index);\n\tconst collect = (where: Bucket | undefined, access: string, into: Set<number>): void => {\n\t\tif (!where) return;\n\t\tfor (const id of where.writes) into.add(id);\n\t\tif (access !== \"read\") for (const id of where.reads) into.add(id);\n\t};\n\tconst queryPath = (parts: string[], access: string, into: Set<number>): void => {\n\t\tlet node = roots;\n\t\tcollect(node.exact, access, into);\n\t\tfor (const part of parts) {\n\t\t\tconst child = node.children.get(part);\n\t\t\tif (!child) return;\n\t\t\tnode = child;\n\t\t\tcollect(node.exact, access, into);\n\t\t}\n\t\tcollect(node.subtree, access, into);\n\t};\n\tconst insertPath = (parts: string[], access: string, index: number): void => {\n\t\tlet node = roots;\n\t\tput(node.subtree, access, index);\n\t\tfor (const part of parts) {\n\t\t\tlet child = node.children.get(part);\n\t\t\tif (!child) {\n\t\t\t\tif (memberships >= maxMemberships) throw new IndexCapacityExceeded();\n\t\t\t\tchild = trie();\n\t\t\t\tnode.children.set(part, child);\n\t\t\t}\n\t\t\tnode = child;\n\t\t\tput(node.subtree, access, index);\n\t\t}\n\t\tput(node.exact, access, index);\n\t};\n\tconst pathKeys = (claim: Extract<ToolResourceClaim, { kind: \"path\" }>): string[] =>\n\t\tclaim.realKey === undefined || claim.realKey === claim.key ? [claim.key] : [claim.key, claim.realKey];\n\tconst dependencies: number[][] = [];\n\ttry {\n\t\tfor (let current = 0; current < entries.length; current++) {\n\t\t\tconst resolution = entries[current].resolution;\n\t\t\tconst isExclusive = resolution.kind === \"exclusive\" || resolution.claims.some((c) => c.access === \"exclusive\");\n\t\t\tconst candidates = new Set(exclusive);\n\t\t\tif (isExclusive) {\n\t\t\t\tfor (let earlier = 0; earlier < current; earlier++) candidates.add(earlier);\n\t\t\t} else if (resolution.kind === \"claims\") {\n\t\t\t\tfor (const claim of resolution.claims) {\n\t\t\t\t\tif (claim.kind !== \"path\") {\n\t\t\t\t\t\tcollect(exact.get(claim.kind)?.get(claim.key), claim.access, candidates);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tconst parts = pathKeys(claim).map(posixParts);\n\t\t\t\t\tif (parts.some((p) => p === undefined)) collect(allPaths, claim.access, candidates);\n\t\t\t\t\telse {\n\t\t\t\t\t\tcollect(broadPaths, claim.access, candidates);\n\t\t\t\t\t\tfor (const p of parts) if (p) queryPath(p, claim.access, candidates);\n\t\t\t\t\t}\n\t\t\t\t\tif (claim.inodeKey !== undefined) collect(inodes.get(claim.inodeKey), claim.access, candidates);\n\t\t\t\t}\n\t\t\t}\n\t\t\tdependencies.push([...candidates].sort((a, b) => a - b).filter((earlier) => check(earlier, current)));\n\t\t\tif (isExclusive) {\n\t\t\t\tadd(exclusive, current);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (resolution.kind !== \"claims\") continue;\n\t\t\tfor (const claim of resolution.claims) {\n\t\t\t\tif (claim.kind !== \"path\") {\n\t\t\t\t\tlet kind = exact.get(claim.kind);\n\t\t\t\t\tif (!kind) {\n\t\t\t\t\t\tkind = new Map();\n\t\t\t\t\t\texact.set(claim.kind, kind);\n\t\t\t\t\t}\n\t\t\t\t\tlet values = kind.get(claim.key);\n\t\t\t\t\tif (!values) {\n\t\t\t\t\t\tvalues = bucket();\n\t\t\t\t\t\tkind.set(claim.key, values);\n\t\t\t\t\t}\n\t\t\t\t\tput(values, claim.access, current);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tput(allPaths, claim.access, current);\n\t\t\t\tconst parts = pathKeys(claim).map(posixParts);\n\t\t\t\tif (parts.some((p) => p === undefined)) put(broadPaths, claim.access, current);\n\t\t\t\telse for (const p of parts) if (p) insertPath(p, claim.access, current);\n\t\t\t\tif (claim.inodeKey !== undefined) {\n\t\t\t\t\tlet values = inodes.get(claim.inodeKey);\n\t\t\t\t\tif (!values) {\n\t\t\t\t\t\tvalues = bucket();\n\t\t\t\t\t\tinodes.set(claim.inodeKey, values);\n\t\t\t\t\t}\n\t\t\t\t\tput(values, claim.access, current);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn { dependencies, stats: { predicateCalls, memberships, fallback: false } };\n\t} catch (error) {\n\t\tif (!(error instanceof IndexCapacityExceeded)) throw error;\n\t\t// No partial graph escapes. Deterministic recomputation uses the original predicate.\n\t\treturn { dependencies: oracle(), stats: { predicateCalls, memberships, fallback: true } };\n\t}\n}\n"]}
|
package/dist/tool-dag-memo.d.ts
CHANGED
|
@@ -3,13 +3,21 @@
|
|
|
3
3
|
* inputs, so replaying it is safe — but only while every input the key cannot
|
|
4
4
|
* fingerprint stays stable. A custom `resourceKeyResolver` or any tool whose
|
|
5
5
|
* `resourceClaims` is a function closure can answer differently on identical
|
|
6
|
-
* call shapes, so those batches bypass the memo entirely (audit §9).
|
|
6
|
+
* call shapes, so those batches bypass the memo entirely (audit §9). Keys carry whole call
|
|
7
|
+
* arguments, file contents included, so the memo is bounded by accounted bytes as well as by
|
|
8
|
+
* entry count.
|
|
7
9
|
*/
|
|
8
|
-
import { type DagSchedulePlan, type ScheduleDagLevelsOptions } from "./tool-dag-scheduler.ts";
|
|
10
|
+
import { type DagSchedulePlan, type ResolvedClaimEntry, type ScheduleDagLevelsOptions } from "./tool-dag-scheduler.ts";
|
|
9
11
|
import type { ClaimableToolCall } from "./tool-resource-claims.ts";
|
|
10
|
-
/** Bounded per-run memo for DAG schedules. */
|
|
12
|
+
/** Bounded per-run memo for DAG schedules. The memo owns the Map: create it empty and only pass it back. */
|
|
11
13
|
export type DagScheduleCache = Map<string, DagSchedulePlan>;
|
|
14
|
+
/** This cache never contains compatibility barrier levels. Do not share it with DagScheduleCache. */
|
|
15
|
+
export type DagFrontierScheduleCache = Map<string, readonly ResolvedClaimEntry[]>;
|
|
12
16
|
export declare const DAG_SCHEDULE_CACHE_LIMIT = 64;
|
|
17
|
+
/** Accounted bytes one cache retains: two per UTF-16 code unit of each key and JSON value, plus 128 per entry. */
|
|
18
|
+
export declare const DAG_SCHEDULE_CACHE_MAX_BYTES: number;
|
|
19
|
+
/** A batch whose entry would exceed this is scheduled without being retained. */
|
|
20
|
+
export declare const DAG_SCHEDULE_CACHE_MAX_ENTRY_BYTES: number;
|
|
13
21
|
/**
|
|
14
22
|
* Schedule with a per-run memo. Identical batches (provider retries, stubborn
|
|
15
23
|
* re-emissions) re-resolve path identities and custom claims; the plan is a
|
|
@@ -18,4 +26,6 @@ export declare const DAG_SCHEDULE_CACHE_LIMIT = 64;
|
|
|
18
26
|
* out as copies because callers append to and reorder them.
|
|
19
27
|
*/
|
|
20
28
|
export declare function scheduleDagLevelsMemo(toolCalls: readonly ClaimableToolCall[], options: ScheduleDagLevelsOptions, signal: AbortSignal | undefined, cache: DagScheduleCache): Promise<DagSchedulePlan | null>;
|
|
29
|
+
/** Live ready-frontier intake needs resolved claims, not barrier levels. */
|
|
30
|
+
export declare function resolveDagFrontierMemo(toolCalls: readonly ClaimableToolCall[], options: ScheduleDagLevelsOptions, signal: AbortSignal | undefined, cache: DagFrontierScheduleCache): Promise<readonly ResolvedClaimEntry[] | null>;
|
|
21
31
|
//# sourceMappingURL=tool-dag-memo.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-dag-memo.d.ts","sourceRoot":"","sources":["../src/tool-dag-memo.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"tool-dag-memo.d.ts","sourceRoot":"","sources":["../src/tool-dag-memo.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EACN,KAAK,eAAe,EACpB,KAAK,kBAAkB,EAEvB,KAAK,wBAAwB,EAE7B,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAEnE,4GAA4G;AAC5G,MAAM,MAAM,gBAAgB,GAAG,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;AAC5D,qGAAqG;AACrG,MAAM,MAAM,wBAAwB,GAAG,GAAG,CAAC,MAAM,EAAE,SAAS,kBAAkB,EAAE,CAAC,CAAC;AAElF,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAC3C,kHAAkH;AAClH,eAAO,MAAM,4BAA4B,QAAkB,CAAC;AAC5D,iFAAiF;AACjF,eAAO,MAAM,kCAAkC,QAAa,CAAC;AA8D7D;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACpC,SAAS,EAAE,SAAS,iBAAiB,EAAE,EACvC,OAAO,EAAE,wBAAwB,EACjC,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,KAAK,EAAE,gBAAgB,GACrB,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CASjC;AAED,4EAA4E;AAC5E,wBAAgB,sBAAsB,CACrC,SAAS,EAAE,SAAS,iBAAiB,EAAE,EACvC,OAAO,EAAE,wBAAwB,EACjC,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,KAAK,EAAE,wBAAwB,GAC7B,OAAO,CAAC,SAAS,kBAAkB,EAAE,GAAG,IAAI,CAAC,CAS/C","sourcesContent":["/**\n * Per-run memo for DAG schedules. A plan is a pure function of the keyed\n * inputs, so replaying it is safe — but only while every input the key cannot\n * fingerprint stays stable. A custom `resourceKeyResolver` or any tool whose\n * `resourceClaims` is a function closure can answer differently on identical\n * call shapes, so those batches bypass the memo entirely (audit §9). Keys carry whole call\n * arguments, file contents included, so the memo is bounded by accounted bytes as well as by\n * entry count.\n */\n\nimport { copyDagClaimEntries, copyDagSchedulePlan } from \"./tool-dag-plan-copy.ts\";\nimport {\n\ttype DagSchedulePlan,\n\ttype ResolvedClaimEntry,\n\tresolveBatchClaims,\n\ttype ScheduleDagLevelsOptions,\n\tscheduleDagLevels,\n} from \"./tool-dag-scheduler.ts\";\nimport { awaitWithAbort } from \"./tool-execution-boundary.ts\";\nimport type { ClaimableToolCall } from \"./tool-resource-claims.ts\";\n\n/** Bounded per-run memo for DAG schedules. The memo owns the Map: create it empty and only pass it back. */\nexport type DagScheduleCache = Map<string, DagSchedulePlan>;\n/** This cache never contains compatibility barrier levels. Do not share it with DagScheduleCache. */\nexport type DagFrontierScheduleCache = Map<string, readonly ResolvedClaimEntry[]>;\n\nexport const DAG_SCHEDULE_CACHE_LIMIT = 64;\n/** Accounted bytes one cache retains: two per UTF-16 code unit of each key and JSON value, plus 128 per entry. */\nexport const DAG_SCHEDULE_CACHE_MAX_BYTES = 4 * 1024 * 1024;\n/** A batch whose entry would exceed this is scheduled without being retained. */\nexport const DAG_SCHEDULE_CACHE_MAX_ENTRY_BYTES = 512 * 1024;\nconst ENTRY_OVERHEAD_BYTES = 128;\nconst valueChars = new WeakMap<object, number>();\n\n/** Accounted size of one entry, or undefined when the value does not serialize as JSON. */\nfunction entryBytes(key: string, value: object): number | undefined {\n\tif (typeof value !== \"object\" || value === null) return undefined;\n\tlet chars = valueChars.get(value);\n\tif (chars === undefined) {\n\t\ttry {\n\t\t\tchars = JSON.stringify(value).length;\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t\tvalueChars.set(value, chars);\n\t}\n\treturn 2 * (key.length + chars) + ENTRY_OVERHEAD_BYTES;\n}\n\n/** Insert as most recent, evicting the least recently used entries until count and bytes fit. */\nfunction retain<T extends object>(cache: Map<string, T>, key: string, value: T): void {\n\tconst bytes = entryBytes(key, value);\n\tif (bytes === undefined || bytes > DAG_SCHEDULE_CACHE_MAX_ENTRY_BYTES) return;\n\tcache.delete(key);\n\tconst sizeOf = (entryKey: string, entryValue: T): number =>\n\t\tentryBytes(entryKey, entryValue) ?? DAG_SCHEDULE_CACHE_MAX_ENTRY_BYTES;\n\tlet total = bytes;\n\tfor (const [entryKey, entryValue] of cache) total += sizeOf(entryKey, entryValue);\n\tfor (const [entryKey, entryValue] of cache) {\n\t\tif (cache.size < DAG_SCHEDULE_CACHE_LIMIT && total <= DAG_SCHEDULE_CACHE_MAX_BYTES) break;\n\t\ttotal -= sizeOf(entryKey, entryValue);\n\t\tcache.delete(entryKey);\n\t}\n\tcache.set(key, value);\n}\n\n/**\n * Canonical key covering every input claim resolution depends on. A custom\n * `resourceKeyResolver` function cannot be fingerprinted, so callers skip the\n * memo entirely when one is configured. Within a run, tool definitions (and\n * their `resourceClaims` closures) are stable, so name/mode/claims-presence\n * fingerprints are sufficient.\n */\nfunction dagScheduleCacheKey(toolCalls: readonly ClaimableToolCall[], options: ScheduleDagLevelsOptions): string {\n\tconst policies = [...(options.toolPolicies?.entries() ?? [])].sort(([left], [right]) =>\n\t\tleft < right ? -1 : left > right ? 1 : 0,\n\t);\n\tconst registered = (options.registeredTools ?? []).map((tool) => [\n\t\ttool.name,\n\t\ttool.executionMode ?? \"\",\n\t\ttypeof tool.resourceClaims === \"function\" ? \"1\" : \"0\",\n\t]);\n\treturn JSON.stringify([\n\t\ttoolCalls.map((call) => [call.name, call.arguments ?? null]),\n\t\toptions.cwd,\n\t\toptions.strictExtensionClaims === true,\n\t\toptions.maxConcurrency ?? null,\n\t\tpolicies,\n\t\tregistered,\n\t]);\n}\n\n/**\n * Schedule with a per-run memo. Identical batches (provider retries, stubborn\n * re-emissions) re-resolve path identities and custom claims; the plan is a\n * pure function of the canonical inputs, so replaying it is safe. Returns\n * `null` on cancellation, including cache hits before key serialization. Cached levels are handed\n * out as copies because callers append to and reorder them.\n */\nexport function scheduleDagLevelsMemo(\n\ttoolCalls: readonly ClaimableToolCall[],\n\toptions: ScheduleDagLevelsOptions,\n\tsignal: AbortSignal | undefined,\n\tcache: DagScheduleCache,\n): Promise<DagSchedulePlan | null> {\n\treturn memoizeDagSchedule(\n\t\ttoolCalls,\n\t\toptions,\n\t\tsignal,\n\t\tcache,\n\t\t() => scheduleDagLevels(toolCalls, options),\n\t\tcopyDagSchedulePlan,\n\t);\n}\n\n/** Live ready-frontier intake needs resolved claims, not barrier levels. */\nexport function resolveDagFrontierMemo(\n\ttoolCalls: readonly ClaimableToolCall[],\n\toptions: ScheduleDagLevelsOptions,\n\tsignal: AbortSignal | undefined,\n\tcache: DagFrontierScheduleCache,\n): Promise<readonly ResolvedClaimEntry[] | null> {\n\treturn memoizeDagSchedule(\n\t\ttoolCalls,\n\t\toptions,\n\t\tsignal,\n\t\tcache,\n\t\t() => resolveBatchClaims(toolCalls, options),\n\t\tcopyDagClaimEntries,\n\t);\n}\n\nasync function memoizeDagSchedule<T extends object>(\n\ttoolCalls: readonly ClaimableToolCall[],\n\toptions: ScheduleDagLevelsOptions,\n\tsignal: AbortSignal | undefined,\n\tcache: Map<string, T>,\n\tcompute: () => Promise<T>,\n\tcopy: (value: T) => T,\n): Promise<T | null> {\n\tif (signal?.aborted) return null;\n\t// Closures and custom resolvers can change their claims without changing a key.\n\tif (\n\t\toptions.resourceKeyResolver ||\n\t\toptions.registeredTools?.some((tool) => typeof tool.resourceClaims === \"function\")\n\t) {\n\t\tconst scheduled = await awaitWithAbort(compute, signal);\n\t\treturn scheduled.kind === \"aborted\" ? null : scheduled.value;\n\t}\n\tconst key = dagScheduleCacheKey(toolCalls, options);\n\t// A key alone over the entry limit can never be retained: skip the lookup, copy and measure.\n\tconst retainable = 2 * key.length + ENTRY_OVERHEAD_BYTES <= DAG_SCHEDULE_CACHE_MAX_ENTRY_BYTES;\n\tconst cached = retainable ? cache.get(key) : undefined;\n\tif (cached !== undefined) {\n\t\tcache.delete(key);\n\t\tcache.set(key, cached);\n\t\treturn copy(cached);\n\t}\n\tconst scheduled = await awaitWithAbort(compute, signal);\n\tif (scheduled.kind === \"aborted\") return null;\n\tif (retainable) retain(cache, key, copy(scheduled.value));\n\treturn scheduled.value;\n}\n"]}
|
package/dist/tool-dag-memo.js
CHANGED
|
@@ -3,11 +3,54 @@
|
|
|
3
3
|
* inputs, so replaying it is safe — but only while every input the key cannot
|
|
4
4
|
* fingerprint stays stable. A custom `resourceKeyResolver` or any tool whose
|
|
5
5
|
* `resourceClaims` is a function closure can answer differently on identical
|
|
6
|
-
* call shapes, so those batches bypass the memo entirely (audit §9).
|
|
6
|
+
* call shapes, so those batches bypass the memo entirely (audit §9). Keys carry whole call
|
|
7
|
+
* arguments, file contents included, so the memo is bounded by accounted bytes as well as by
|
|
8
|
+
* entry count.
|
|
7
9
|
*/
|
|
8
|
-
import {
|
|
10
|
+
import { copyDagClaimEntries, copyDagSchedulePlan } from "./tool-dag-plan-copy.js";
|
|
11
|
+
import { resolveBatchClaims, scheduleDagLevels, } from "./tool-dag-scheduler.js";
|
|
9
12
|
import { awaitWithAbort } from "./tool-execution-boundary.js";
|
|
10
13
|
export const DAG_SCHEDULE_CACHE_LIMIT = 64;
|
|
14
|
+
/** Accounted bytes one cache retains: two per UTF-16 code unit of each key and JSON value, plus 128 per entry. */
|
|
15
|
+
export const DAG_SCHEDULE_CACHE_MAX_BYTES = 4 * 1024 * 1024;
|
|
16
|
+
/** A batch whose entry would exceed this is scheduled without being retained. */
|
|
17
|
+
export const DAG_SCHEDULE_CACHE_MAX_ENTRY_BYTES = 512 * 1024;
|
|
18
|
+
const ENTRY_OVERHEAD_BYTES = 128;
|
|
19
|
+
const valueChars = new WeakMap();
|
|
20
|
+
/** Accounted size of one entry, or undefined when the value does not serialize as JSON. */
|
|
21
|
+
function entryBytes(key, value) {
|
|
22
|
+
if (typeof value !== "object" || value === null)
|
|
23
|
+
return undefined;
|
|
24
|
+
let chars = valueChars.get(value);
|
|
25
|
+
if (chars === undefined) {
|
|
26
|
+
try {
|
|
27
|
+
chars = JSON.stringify(value).length;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
valueChars.set(value, chars);
|
|
33
|
+
}
|
|
34
|
+
return 2 * (key.length + chars) + ENTRY_OVERHEAD_BYTES;
|
|
35
|
+
}
|
|
36
|
+
/** Insert as most recent, evicting the least recently used entries until count and bytes fit. */
|
|
37
|
+
function retain(cache, key, value) {
|
|
38
|
+
const bytes = entryBytes(key, value);
|
|
39
|
+
if (bytes === undefined || bytes > DAG_SCHEDULE_CACHE_MAX_ENTRY_BYTES)
|
|
40
|
+
return;
|
|
41
|
+
cache.delete(key);
|
|
42
|
+
const sizeOf = (entryKey, entryValue) => entryBytes(entryKey, entryValue) ?? DAG_SCHEDULE_CACHE_MAX_ENTRY_BYTES;
|
|
43
|
+
let total = bytes;
|
|
44
|
+
for (const [entryKey, entryValue] of cache)
|
|
45
|
+
total += sizeOf(entryKey, entryValue);
|
|
46
|
+
for (const [entryKey, entryValue] of cache) {
|
|
47
|
+
if (cache.size < DAG_SCHEDULE_CACHE_LIMIT && total <= DAG_SCHEDULE_CACHE_MAX_BYTES)
|
|
48
|
+
break;
|
|
49
|
+
total -= sizeOf(entryKey, entryValue);
|
|
50
|
+
cache.delete(entryKey);
|
|
51
|
+
}
|
|
52
|
+
cache.set(key, value);
|
|
53
|
+
}
|
|
11
54
|
/**
|
|
12
55
|
* Canonical key covering every input claim resolution depends on. A custom
|
|
13
56
|
* `resourceKeyResolver` function cannot be fingerprinted, so callers skip the
|
|
@@ -38,40 +81,36 @@ function dagScheduleCacheKey(toolCalls, options) {
|
|
|
38
81
|
* `null` on cancellation, including cache hits before key serialization. Cached levels are handed
|
|
39
82
|
* out as copies because callers append to and reorder them.
|
|
40
83
|
*/
|
|
41
|
-
export
|
|
84
|
+
export function scheduleDagLevelsMemo(toolCalls, options, signal, cache) {
|
|
85
|
+
return memoizeDagSchedule(toolCalls, options, signal, cache, () => scheduleDagLevels(toolCalls, options), copyDagSchedulePlan);
|
|
86
|
+
}
|
|
87
|
+
/** Live ready-frontier intake needs resolved claims, not barrier levels. */
|
|
88
|
+
export function resolveDagFrontierMemo(toolCalls, options, signal, cache) {
|
|
89
|
+
return memoizeDagSchedule(toolCalls, options, signal, cache, () => resolveBatchClaims(toolCalls, options), copyDagClaimEntries);
|
|
90
|
+
}
|
|
91
|
+
async function memoizeDagSchedule(toolCalls, options, signal, cache, compute, copy) {
|
|
42
92
|
if (signal?.aborted)
|
|
43
93
|
return null;
|
|
44
|
-
//
|
|
45
|
-
// fingerprint: a custom resourceKeyResolver, or any tool whose resourceClaims
|
|
46
|
-
// is a function closure — its return value may change between identical
|
|
47
|
-
// calls, and a stale cached plan would silently reuse its old claims.
|
|
94
|
+
// Closures and custom resolvers can change their claims without changing a key.
|
|
48
95
|
if (options.resourceKeyResolver ||
|
|
49
96
|
options.registeredTools?.some((tool) => typeof tool.resourceClaims === "function")) {
|
|
50
|
-
const scheduled = await awaitWithAbort(
|
|
97
|
+
const scheduled = await awaitWithAbort(compute, signal);
|
|
51
98
|
return scheduled.kind === "aborted" ? null : scheduled.value;
|
|
52
99
|
}
|
|
53
100
|
const key = dagScheduleCacheKey(toolCalls, options);
|
|
54
|
-
|
|
55
|
-
|
|
101
|
+
// A key alone over the entry limit can never be retained: skip the lookup, copy and measure.
|
|
102
|
+
const retainable = 2 * key.length + ENTRY_OVERHEAD_BYTES <= DAG_SCHEDULE_CACHE_MAX_ENTRY_BYTES;
|
|
103
|
+
const cached = retainable ? cache.get(key) : undefined;
|
|
104
|
+
if (cached !== undefined) {
|
|
56
105
|
cache.delete(key);
|
|
57
106
|
cache.set(key, cached);
|
|
58
|
-
return
|
|
107
|
+
return copy(cached);
|
|
59
108
|
}
|
|
60
|
-
const scheduled = await awaitWithAbort(
|
|
61
|
-
if (scheduled.kind === "aborted")
|
|
109
|
+
const scheduled = await awaitWithAbort(compute, signal);
|
|
110
|
+
if (scheduled.kind === "aborted")
|
|
62
111
|
return null;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const oldest = cache.keys().next();
|
|
66
|
-
if (!oldest.done) {
|
|
67
|
-
cache.delete(oldest.value);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
cache.set(key, {
|
|
71
|
-
levels: scheduled.value.levels.map((level) => level.slice()),
|
|
72
|
-
planKey: scheduled.value.planKey,
|
|
73
|
-
entries: scheduled.value.entries,
|
|
74
|
-
});
|
|
112
|
+
if (retainable)
|
|
113
|
+
retain(cache, key, copy(scheduled.value));
|
|
75
114
|
return scheduled.value;
|
|
76
115
|
}
|
|
77
116
|
//# sourceMappingURL=tool-dag-memo.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-dag-memo.js","sourceRoot":"","sources":["../src/tool-dag-memo.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Per-run memo for DAG schedules. A plan is a pure function of the keyed\n * inputs, so replaying it is safe — but only while every input the key cannot\n * fingerprint stays stable. A custom `resourceKeyResolver` or any tool whose\n * `resourceClaims` is a function closure can answer differently on identical\n * call shapes, so those batches bypass the memo entirely (audit §9).\n */\n\nimport { type DagSchedulePlan, type ScheduleDagLevelsOptions, scheduleDagLevels } from \"./tool-dag-scheduler.ts\";\nimport { awaitWithAbort } from \"./tool-execution-boundary.ts\";\nimport type { ClaimableToolCall } from \"./tool-resource-claims.ts\";\n\n/** Bounded per-run memo for DAG schedules. */\nexport type DagScheduleCache = Map<string, DagSchedulePlan>;\n\nexport const DAG_SCHEDULE_CACHE_LIMIT = 64;\n\n/**\n * Canonical key covering every input claim resolution depends on. A custom\n * `resourceKeyResolver` function cannot be fingerprinted, so callers skip the\n * memo entirely when one is configured. Within a run, tool definitions (and\n * their `resourceClaims` closures) are stable, so name/mode/claims-presence\n * fingerprints are sufficient.\n */\nfunction dagScheduleCacheKey(toolCalls: readonly ClaimableToolCall[], options: ScheduleDagLevelsOptions): string {\n\tconst policies = [...(options.toolPolicies?.entries() ?? [])].sort(([left], [right]) =>\n\t\tleft < right ? -1 : left > right ? 1 : 0,\n\t);\n\tconst registered = (options.registeredTools ?? []).map((tool) => [\n\t\ttool.name,\n\t\ttool.executionMode ?? \"\",\n\t\ttypeof tool.resourceClaims === \"function\" ? \"1\" : \"0\",\n\t]);\n\treturn JSON.stringify([\n\t\ttoolCalls.map((call) => [call.name, call.arguments ?? null]),\n\t\toptions.cwd,\n\t\toptions.strictExtensionClaims === true,\n\t\toptions.maxConcurrency ?? null,\n\t\tpolicies,\n\t\tregistered,\n\t]);\n}\n\n/**\n * Schedule with a per-run memo. Identical batches (provider retries, stubborn\n * re-emissions) re-resolve path identities and custom claims; the plan is a\n * pure function of the canonical inputs, so replaying it is safe. Returns\n * `null` on cancellation, including cache hits before key serialization. Cached levels are handed\n * out as copies because callers append to and reorder them.\n */\nexport async function scheduleDagLevelsMemo(\n\ttoolCalls: readonly ClaimableToolCall[],\n\toptions: ScheduleDagLevelsOptions,\n\tsignal: AbortSignal | undefined,\n\tcache: DagScheduleCache,\n): Promise<DagSchedulePlan | null> {\n\tif (signal?.aborted) return null;\n\t// Skip the memo whenever resolution can depend on state the key cannot\n\t// fingerprint: a custom resourceKeyResolver, or any tool whose resourceClaims\n\t// is a function closure — its return value may change between identical\n\t// calls, and a stale cached plan would silently reuse its old claims.\n\tif (\n\t\toptions.resourceKeyResolver ||\n\t\toptions.registeredTools?.some((tool) => typeof tool.resourceClaims === \"function\")\n\t) {\n\t\tconst scheduled = await awaitWithAbort(() => scheduleDagLevels(toolCalls, options), signal);\n\t\treturn scheduled.kind === \"aborted\" ? null : scheduled.value;\n\t}\n\tconst key = dagScheduleCacheKey(toolCalls, options);\n\tconst cached = cache.get(key);\n\tif (cached) {\n\t\tcache.delete(key);\n\t\tcache.set(key, cached);\n\t\treturn { levels: cached.levels.map((level) => level.slice()), planKey: cached.planKey, entries: cached.entries };\n\t}\n\tconst scheduled = await awaitWithAbort(() => scheduleDagLevels(toolCalls, options), signal);\n\tif (scheduled.kind === \"aborted\") {\n\t\treturn null;\n\t}\n\tif (cache.size >= DAG_SCHEDULE_CACHE_LIMIT) {\n\t\tconst oldest = cache.keys().next();\n\t\tif (!oldest.done) {\n\t\t\tcache.delete(oldest.value);\n\t\t}\n\t}\n\tcache.set(key, {\n\t\tlevels: scheduled.value.levels.map((level) => level.slice()),\n\t\tplanKey: scheduled.value.planKey,\n\t\tentries: scheduled.value.entries,\n\t});\n\treturn scheduled.value;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"tool-dag-memo.js","sourceRoot":"","sources":["../src/tool-dag-memo.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Per-run memo for DAG schedules. A plan is a pure function of the keyed\n * inputs, so replaying it is safe — but only while every input the key cannot\n * fingerprint stays stable. A custom `resourceKeyResolver` or any tool whose\n * `resourceClaims` is a function closure can answer differently on identical\n * call shapes, so those batches bypass the memo entirely (audit §9). Keys carry whole call\n * arguments, file contents included, so the memo is bounded by accounted bytes as well as by\n * entry count.\n */\n\nimport { copyDagClaimEntries, copyDagSchedulePlan } from \"./tool-dag-plan-copy.ts\";\nimport {\n\ttype DagSchedulePlan,\n\ttype ResolvedClaimEntry,\n\tresolveBatchClaims,\n\ttype ScheduleDagLevelsOptions,\n\tscheduleDagLevels,\n} from \"./tool-dag-scheduler.ts\";\nimport { awaitWithAbort } from \"./tool-execution-boundary.ts\";\nimport type { ClaimableToolCall } from \"./tool-resource-claims.ts\";\n\n/** Bounded per-run memo for DAG schedules. The memo owns the Map: create it empty and only pass it back. */\nexport type DagScheduleCache = Map<string, DagSchedulePlan>;\n/** This cache never contains compatibility barrier levels. Do not share it with DagScheduleCache. */\nexport type DagFrontierScheduleCache = Map<string, readonly ResolvedClaimEntry[]>;\n\nexport const DAG_SCHEDULE_CACHE_LIMIT = 64;\n/** Accounted bytes one cache retains: two per UTF-16 code unit of each key and JSON value, plus 128 per entry. */\nexport const DAG_SCHEDULE_CACHE_MAX_BYTES = 4 * 1024 * 1024;\n/** A batch whose entry would exceed this is scheduled without being retained. */\nexport const DAG_SCHEDULE_CACHE_MAX_ENTRY_BYTES = 512 * 1024;\nconst ENTRY_OVERHEAD_BYTES = 128;\nconst valueChars = new WeakMap<object, number>();\n\n/** Accounted size of one entry, or undefined when the value does not serialize as JSON. */\nfunction entryBytes(key: string, value: object): number | undefined {\n\tif (typeof value !== \"object\" || value === null) return undefined;\n\tlet chars = valueChars.get(value);\n\tif (chars === undefined) {\n\t\ttry {\n\t\t\tchars = JSON.stringify(value).length;\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t\tvalueChars.set(value, chars);\n\t}\n\treturn 2 * (key.length + chars) + ENTRY_OVERHEAD_BYTES;\n}\n\n/** Insert as most recent, evicting the least recently used entries until count and bytes fit. */\nfunction retain<T extends object>(cache: Map<string, T>, key: string, value: T): void {\n\tconst bytes = entryBytes(key, value);\n\tif (bytes === undefined || bytes > DAG_SCHEDULE_CACHE_MAX_ENTRY_BYTES) return;\n\tcache.delete(key);\n\tconst sizeOf = (entryKey: string, entryValue: T): number =>\n\t\tentryBytes(entryKey, entryValue) ?? DAG_SCHEDULE_CACHE_MAX_ENTRY_BYTES;\n\tlet total = bytes;\n\tfor (const [entryKey, entryValue] of cache) total += sizeOf(entryKey, entryValue);\n\tfor (const [entryKey, entryValue] of cache) {\n\t\tif (cache.size < DAG_SCHEDULE_CACHE_LIMIT && total <= DAG_SCHEDULE_CACHE_MAX_BYTES) break;\n\t\ttotal -= sizeOf(entryKey, entryValue);\n\t\tcache.delete(entryKey);\n\t}\n\tcache.set(key, value);\n}\n\n/**\n * Canonical key covering every input claim resolution depends on. A custom\n * `resourceKeyResolver` function cannot be fingerprinted, so callers skip the\n * memo entirely when one is configured. Within a run, tool definitions (and\n * their `resourceClaims` closures) are stable, so name/mode/claims-presence\n * fingerprints are sufficient.\n */\nfunction dagScheduleCacheKey(toolCalls: readonly ClaimableToolCall[], options: ScheduleDagLevelsOptions): string {\n\tconst policies = [...(options.toolPolicies?.entries() ?? [])].sort(([left], [right]) =>\n\t\tleft < right ? -1 : left > right ? 1 : 0,\n\t);\n\tconst registered = (options.registeredTools ?? []).map((tool) => [\n\t\ttool.name,\n\t\ttool.executionMode ?? \"\",\n\t\ttypeof tool.resourceClaims === \"function\" ? \"1\" : \"0\",\n\t]);\n\treturn JSON.stringify([\n\t\ttoolCalls.map((call) => [call.name, call.arguments ?? null]),\n\t\toptions.cwd,\n\t\toptions.strictExtensionClaims === true,\n\t\toptions.maxConcurrency ?? null,\n\t\tpolicies,\n\t\tregistered,\n\t]);\n}\n\n/**\n * Schedule with a per-run memo. Identical batches (provider retries, stubborn\n * re-emissions) re-resolve path identities and custom claims; the plan is a\n * pure function of the canonical inputs, so replaying it is safe. Returns\n * `null` on cancellation, including cache hits before key serialization. Cached levels are handed\n * out as copies because callers append to and reorder them.\n */\nexport function scheduleDagLevelsMemo(\n\ttoolCalls: readonly ClaimableToolCall[],\n\toptions: ScheduleDagLevelsOptions,\n\tsignal: AbortSignal | undefined,\n\tcache: DagScheduleCache,\n): Promise<DagSchedulePlan | null> {\n\treturn memoizeDagSchedule(\n\t\ttoolCalls,\n\t\toptions,\n\t\tsignal,\n\t\tcache,\n\t\t() => scheduleDagLevels(toolCalls, options),\n\t\tcopyDagSchedulePlan,\n\t);\n}\n\n/** Live ready-frontier intake needs resolved claims, not barrier levels. */\nexport function resolveDagFrontierMemo(\n\ttoolCalls: readonly ClaimableToolCall[],\n\toptions: ScheduleDagLevelsOptions,\n\tsignal: AbortSignal | undefined,\n\tcache: DagFrontierScheduleCache,\n): Promise<readonly ResolvedClaimEntry[] | null> {\n\treturn memoizeDagSchedule(\n\t\ttoolCalls,\n\t\toptions,\n\t\tsignal,\n\t\tcache,\n\t\t() => resolveBatchClaims(toolCalls, options),\n\t\tcopyDagClaimEntries,\n\t);\n}\n\nasync function memoizeDagSchedule<T extends object>(\n\ttoolCalls: readonly ClaimableToolCall[],\n\toptions: ScheduleDagLevelsOptions,\n\tsignal: AbortSignal | undefined,\n\tcache: Map<string, T>,\n\tcompute: () => Promise<T>,\n\tcopy: (value: T) => T,\n): Promise<T | null> {\n\tif (signal?.aborted) return null;\n\t// Closures and custom resolvers can change their claims without changing a key.\n\tif (\n\t\toptions.resourceKeyResolver ||\n\t\toptions.registeredTools?.some((tool) => typeof tool.resourceClaims === \"function\")\n\t) {\n\t\tconst scheduled = await awaitWithAbort(compute, signal);\n\t\treturn scheduled.kind === \"aborted\" ? null : scheduled.value;\n\t}\n\tconst key = dagScheduleCacheKey(toolCalls, options);\n\t// A key alone over the entry limit can never be retained: skip the lookup, copy and measure.\n\tconst retainable = 2 * key.length + ENTRY_OVERHEAD_BYTES <= DAG_SCHEDULE_CACHE_MAX_ENTRY_BYTES;\n\tconst cached = retainable ? cache.get(key) : undefined;\n\tif (cached !== undefined) {\n\t\tcache.delete(key);\n\t\tcache.set(key, cached);\n\t\treturn copy(cached);\n\t}\n\tconst scheduled = await awaitWithAbort(compute, signal);\n\tif (scheduled.kind === \"aborted\") return null;\n\tif (retainable) retain(cache, key, copy(scheduled.value));\n\treturn scheduled.value;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-dag-own-number.d.ts","sourceRoot":"","sources":["../src/tool-dag-own-number.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE3G","sourcesContent":["/** Resource dictionaries are data maps: Object.prototype is never a resource. */\nexport function ownResourceNumber(values: Readonly<Record<string, number>>, key: string): number | undefined {\n\treturn Object.hasOwn(values, key) ? values[key] : undefined;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-dag-own-number.js","sourceRoot":"","sources":["../src/tool-dag-own-number.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,MAAM,UAAU,iBAAiB,CAAC,MAAwC,EAAE,GAAW,EAAsB;IAC5G,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAAA,CAC5D","sourcesContent":["/** Resource dictionaries are data maps: Object.prototype is never a resource. */\nexport function ownResourceNumber(values: Readonly<Record<string, number>>, key: string): number | undefined {\n\treturn Object.hasOwn(values, key) ? values[key] : undefined;\n}\n"]}
|