omk-agent-core 0.90.8 → 0.90.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.
Files changed (58) hide show
  1. package/README.md +97 -1
  2. package/dist/agent-loop.d.ts +25 -2
  3. package/dist/agent-loop.d.ts.map +1 -1
  4. package/dist/agent-loop.js +492 -187
  5. package/dist/agent-loop.js.map +1 -1
  6. package/dist/agent.d.ts +29 -7
  7. package/dist/agent.d.ts.map +1 -1
  8. package/dist/agent.js +81 -46
  9. package/dist/agent.js.map +1 -1
  10. package/dist/builtin-tool-resource-claims.d.ts +19 -0
  11. package/dist/builtin-tool-resource-claims.d.ts.map +1 -0
  12. package/dist/builtin-tool-resource-claims.js +200 -0
  13. package/dist/builtin-tool-resource-claims.js.map +1 -0
  14. package/dist/index.d.ts +4 -0
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +7 -0
  17. package/dist/index.js.map +1 -1
  18. package/dist/node-resource-resolver.d.ts +42 -0
  19. package/dist/node-resource-resolver.d.ts.map +1 -0
  20. package/dist/node-resource-resolver.js +149 -0
  21. package/dist/node-resource-resolver.js.map +1 -0
  22. package/dist/node.d.ts +1 -0
  23. package/dist/node.d.ts.map +1 -1
  24. package/dist/node.js +2 -0
  25. package/dist/node.js.map +1 -1
  26. package/dist/path-segments.d.ts +8 -0
  27. package/dist/path-segments.d.ts.map +1 -1
  28. package/dist/path-segments.js +62 -9
  29. package/dist/path-segments.js.map +1 -1
  30. package/dist/plain-data.d.ts +7 -0
  31. package/dist/plain-data.d.ts.map +1 -0
  32. package/dist/plain-data.js +70 -0
  33. package/dist/plain-data.js.map +1 -0
  34. package/dist/tool-dag-scheduler.d.ts +86 -0
  35. package/dist/tool-dag-scheduler.d.ts.map +1 -0
  36. package/dist/tool-dag-scheduler.js +171 -0
  37. package/dist/tool-dag-scheduler.js.map +1 -0
  38. package/dist/tool-execution-boundary.d.ts +52 -0
  39. package/dist/tool-execution-boundary.d.ts.map +1 -0
  40. package/dist/tool-execution-boundary.js +185 -0
  41. package/dist/tool-execution-boundary.js.map +1 -0
  42. package/dist/tool-resource-claims.d.ts +31 -0
  43. package/dist/tool-resource-claims.d.ts.map +1 -0
  44. package/dist/tool-resource-claims.js +128 -0
  45. package/dist/tool-resource-claims.js.map +1 -0
  46. package/dist/tool-timeout.d.ts +96 -0
  47. package/dist/tool-timeout.d.ts.map +1 -0
  48. package/dist/tool-timeout.js +173 -0
  49. package/dist/tool-timeout.js.map +1 -0
  50. package/dist/tool-transcript-integrity.d.ts +65 -0
  51. package/dist/tool-transcript-integrity.d.ts.map +1 -0
  52. package/dist/tool-transcript-integrity.js +223 -0
  53. package/dist/tool-transcript-integrity.js.map +1 -0
  54. package/dist/types.d.ts +219 -10
  55. package/dist/types.d.ts.map +1 -1
  56. package/dist/types.js +50 -1
  57. package/dist/types.js.map +1 -1
  58. package/package.json +2 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builtin-tool-resource-claims.js","sourceRoot":"","sources":["../src/builtin-tool-resource-claims.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ToolParallelPolicy } from \"./parallel-tool-batch.ts\";\nimport { NEVER_PARALLEL_TOOLS, PARALLEL_SAFE_TOOLS, PATH_SCOPED_TOOLS } from \"./parallel-tool-batch.ts\";\nimport {\n\tcanonicalizeLexicalPath,\n\tjoinPathSegments,\n\tnormalizePathSlashes,\n\tpathSegmentsOverlap,\n} from \"./path-segments.ts\";\nimport type {\n\tClaimableToolCall,\n\tRegisteredToolClaimDefinition,\n\tResolveToolClaimsOptions,\n\tToolClaimResolution,\n} from \"./tool-resource-claims.ts\";\nimport type { AgentTool, ResolvedResourceKeys, ToolResourceAccess, ToolResourceClaim } from \"./types.ts\";\n\nconst SEARCH_PATH_TOOLS = new Set<string>([\"grep\", \"find\", \"ls\", \"search_files\"]);\n\nexport function isPlainArguments(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport function findRegisteredToolClaimDefinition(\n\tname: string,\n\tregisteredTools: readonly RegisteredToolClaimDefinition[] | undefined,\n): RegisteredToolClaimDefinition | undefined {\n\treturn registeredTools?.find((tool) => tool.name === name);\n}\n\n/** Bind a fixed call identity without spreading away prototype tool methods. */\nexport function bindToolIdentity(candidate: AgentTool, name: string): AgentTool {\n\tconst tool: AgentTool = Object.create(candidate);\n\tObject.defineProperty(tool, \"name\", { enumerable: true, value: name });\n\tObject.defineProperty(tool, \"execute\", { value: candidate.execute.bind(candidate) });\n\tif (candidate.prepareArguments) {\n\t\tObject.defineProperty(tool, \"prepareArguments\", { value: candidate.prepareArguments.bind(candidate) });\n\t}\n\tif (candidate.resourceClaims) {\n\t\tObject.defineProperty(tool, \"resourceClaims\", { value: candidate.resourceClaims.bind(candidate) });\n\t}\n\treturn Object.freeze(tool);\n}\n\nexport function resolveToolPolicy(name: string, options: ResolveToolClaimsOptions): ToolParallelPolicy | undefined {\n\treturn (\n\t\tfindRegisteredToolClaimDefinition(name, options.registeredTools)?.executionMode ?? options.toolPolicies?.get(name)\n\t);\n}\n\nexport function isBuiltinPathClaimTool(name: string): boolean {\n\treturn PATH_SCOPED_TOOLS.has(name) || SEARCH_PATH_TOOLS.has(name);\n}\n\nfunction normalizeLeadingSlashes(rawPath: string): string {\n\tconst normalized = normalizePathSlashes(rawPath);\n\treturn /^\\/{3,}/.test(normalized) ? normalized.replace(/^\\/+/, \"/\") : normalized;\n}\n\nfunction resolveCanonicalPathKey(rawPath: unknown, cwd: unknown): string | null {\n\tif (typeof rawPath !== \"string\" || rawPath.trim().length === 0 || typeof cwd !== \"string\") return null;\n\tconst normalized = normalizeLeadingSlashes(rawPath);\n\tconst canonicalCwd = canonicalizeLexicalPath(cwd);\n\tif (\n\t\tnormalized.startsWith(\"//\") ||\n\t\t/^[A-Za-z]:(?!\\/)/.test(normalized) ||\n\t\tcanonicalCwd === null ||\n\t\t!(canonicalCwd.startsWith(\"/\") || /^[A-Za-z]:\\//.test(canonicalCwd))\n\t) {\n\t\treturn null;\n\t}\n\n\tlet resolved: string;\n\tif (/^[A-Za-z]:\\//.test(normalized)) {\n\t\tresolved = normalized;\n\t} else if (normalized.startsWith(\"/\")) {\n\t\tconst cwdDrive = /^([A-Za-z]:)\\//.exec(canonicalCwd);\n\t\tresolved = cwdDrive ? `${cwdDrive[1]}${normalized}` : normalized;\n\t} else {\n\t\tresolved = joinPathSegments(canonicalCwd, normalized);\n\t}\n\tconst canonical = canonicalizeLexicalPath(resolved);\n\treturn canonical !== null && (canonical.startsWith(\"/\") || /^[A-Za-z]:\\//.test(canonical)) ? canonical : null;\n}\n\nfunction builtinRawPath(toolName: string, args: Record<string, unknown>, cwd: string): unknown {\n\tif (PATH_SCOPED_TOOLS.has(toolName)) return args.path;\n\tif (!SEARCH_PATH_TOOLS.has(toolName)) return undefined;\n\treturn typeof args.path === \"string\" && args.path.trim().length > 0 ? args.path : cwd;\n}\n\nfunction builtinPathAccess(toolName: string): ToolResourceAccess {\n\treturn toolName === \"read\" || SEARCH_PATH_TOOLS.has(toolName) ? \"read\" : \"write\";\n}\n\nexport function resolvePathClaimKey(toolName: string, args: Record<string, unknown>, cwd: string): string | null {\n\treturn isBuiltinPathClaimTool(toolName) ? resolveCanonicalPathKey(builtinRawPath(toolName, args, cwd), cwd) : null;\n}\n\nfunction canonicalizeResolvedPathKey(rawPath: unknown): string | null {\n\tif (typeof rawPath !== \"string\" || rawPath.trim().length === 0) return null;\n\tconst normalized = normalizeLeadingSlashes(rawPath);\n\tif (normalized.startsWith(\"//\")) {\n\t\tconst match = /^\\/\\/+([^/]+)\\/+([^/]+)(?:\\/+(.*))?$/.exec(normalized);\n\t\tif (!match) return null;\n\t\tconst suffix = match[3] ?? \"\";\n\t\tconst canonicalSuffix = suffix.length === 0 ? \"\" : canonicalizeLexicalPath(suffix);\n\t\tif (canonicalSuffix === null) return null;\n\t\tconst root = `//${match[1].toLowerCase()}/${match[2].toLowerCase()}`;\n\t\treturn canonicalSuffix.length === 0 ? root : `${root}/${canonicalSuffix}`;\n\t}\n\tconst canonical = canonicalizeLexicalPath(normalized);\n\tif (canonical === null) return null;\n\tconst drive = /^([A-Za-z]:)\\/(.*)$/.exec(canonical);\n\tif (drive) return `${drive[1].toLowerCase()}/${drive[2]}`;\n\treturn canonical.startsWith(\"/\") ? canonical : null;\n}\n\nfunction normalizeResolvedResourceKeys(value: unknown): ResolvedResourceKeys | null {\n\tif (!isPlainArguments(value)) return null;\n\tconst lexicalKey = canonicalizeResolvedPathKey(value.lexicalKey);\n\tif (lexicalKey === null) return null;\n\tconst realKey = value.realKey === undefined ? undefined : canonicalizeResolvedPathKey(value.realKey);\n\tif (realKey === null || (value.inodeKey !== undefined && (typeof value.inodeKey !== \"string\" || !value.inodeKey))) {\n\t\treturn null;\n\t}\n\tconst inodeKey = value.inodeKey;\n\tif (\n\t\t(lexicalKey.startsWith(\"//\") || /^[a-z]:\\//.test(lexicalKey)) &&\n\t\trealKey === undefined &&\n\t\tinodeKey === undefined\n\t) {\n\t\treturn null;\n\t}\n\treturn {\n\t\tlexicalKey,\n\t\t...(realKey === undefined ? {} : { realKey }),\n\t\t...(inodeKey === undefined ? {} : { inodeKey }),\n\t};\n}\n\nexport async function resolvePathClaimWithIdentity(\n\trawPath: unknown,\n\taccess: ToolResourceAccess,\n\toptions: ResolveToolClaimsOptions,\n): Promise<ToolClaimResolution> {\n\tif (typeof rawPath !== \"string\" || rawPath.trim().length === 0) return { kind: \"exclusive\" };\n\tconst resolver = options.resourceKeyResolver;\n\tif (!resolver) {\n\t\tconst key = resolveCanonicalPathKey(rawPath, options.cwd);\n\t\treturn key === null ? { kind: \"exclusive\" } : { kind: \"claims\", claims: [{ kind: \"path\", key, access }] };\n\t}\n\ttry {\n\t\tconst keys = normalizeResolvedResourceKeys(await resolver.resolvePath(rawPath, options.cwd));\n\t\tif (keys === null) return { kind: \"exclusive\" };\n\t\treturn {\n\t\t\tkind: \"claims\",\n\t\t\tclaims: [\n\t\t\t\t{\n\t\t\t\t\tkind: \"path\",\n\t\t\t\t\tkey: keys.lexicalKey,\n\t\t\t\t\taccess,\n\t\t\t\t\t...(keys.realKey === undefined ? {} : { realKey: keys.realKey }),\n\t\t\t\t\t...(keys.inodeKey === undefined ? {} : { inodeKey: keys.inodeKey }),\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\t} catch {\n\t\treturn { kind: \"exclusive\" };\n\t}\n}\n\nexport function resolveBuiltinPathClaimWithIdentity(\n\ttoolCall: ClaimableToolCall,\n\toptions: ResolveToolClaimsOptions,\n): Promise<ToolClaimResolution> {\n\tif (!isPlainArguments(toolCall.arguments)) return Promise.resolve({ kind: \"exclusive\" });\n\treturn resolvePathClaimWithIdentity(\n\t\tbuiltinRawPath(toolCall.name, toolCall.arguments, options.cwd),\n\t\tbuiltinPathAccess(toolCall.name),\n\t\toptions,\n\t);\n}\n\nexport function resolveToolClaims(toolCall: ClaimableToolCall, options: ResolveToolClaimsOptions): ToolClaimResolution {\n\tif (!isPlainArguments(toolCall.arguments)) return { kind: \"exclusive\" };\n\tconst name = toolCall.name;\n\tif (NEVER_PARALLEL_TOOLS.has(name) || name === \"bash\" || resolveToolPolicy(name, options) === \"sequential\") {\n\t\treturn { kind: \"exclusive\" };\n\t}\n\tif (isBuiltinPathClaimTool(name)) {\n\t\tconst key = resolvePathClaimKey(name, toolCall.arguments, options.cwd);\n\t\treturn key === null\n\t\t\t? { kind: \"exclusive\" }\n\t\t\t: { kind: \"claims\", claims: [{ access: builtinPathAccess(name), kind: \"path\", key }] };\n\t}\n\tif (PARALLEL_SAFE_TOOLS.has(name)) return { kind: \"claims\", claims: [] };\n\tif (resolveToolPolicy(name, options) === \"parallel\") {\n\t\treturn options.strictExtensionClaims ? { kind: \"exclusive\" } : { kind: \"claims\", claims: [] };\n\t}\n\treturn { kind: \"exclusive\" };\n}\n\nfunction pathClaimKeys(claim: Extract<ToolResourceClaim, { kind: \"path\" }>): string[] {\n\treturn claim.realKey === undefined || claim.realKey === claim.key ? [claim.key] : [claim.key, claim.realKey];\n}\n\nfunction uncSegments(key: string): string[] | null {\n\treturn key.startsWith(\"//\")\n\t\t? key\n\t\t\t\t.slice(2)\n\t\t\t\t.split(\"/\")\n\t\t\t\t.filter((segment) => segment.length > 0)\n\t\t\t\t.map((segment) => segment.toLowerCase())\n\t\t: null;\n}\n\nfunction identityPathKeysOverlap(left: string, right: string): boolean {\n\tconst leftUnc = uncSegments(left);\n\tconst rightUnc = uncSegments(right);\n\tif (leftUnc === null || rightUnc === null) {\n\t\treturn leftUnc === null && rightUnc === null && pathSegmentsOverlap(left, right);\n\t}\n\tconst commonLength = Math.min(leftUnc.length, rightUnc.length);\n\tfor (let index = 0; index < commonLength; index++) {\n\t\tif (leftUnc[index] !== rightUnc[index]) return false;\n\t}\n\treturn true;\n}\n\nexport function pathClaimsOverlap(\n\tleft: Extract<ToolResourceClaim, { kind: \"path\" }>,\n\tright: Extract<ToolResourceClaim, { kind: \"path\" }>,\n): boolean {\n\tif (left.inodeKey !== undefined && left.inodeKey === right.inodeKey) return true;\n\treturn pathClaimKeys(left).some((leftKey) =>\n\t\tpathClaimKeys(right).some((rightKey) => identityPathKeysOverlap(leftKey, rightKey)),\n\t);\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -18,5 +18,9 @@ export * from "./harness/utils/shell-output.ts";
18
18
  export * from "./harness/utils/truncate.ts";
19
19
  export { NEVER_PARALLEL_TOOLS, PARALLEL_SAFE_TOOLS, PATH_SCOPED_TOOLS, partitionToolBatchWaves, pathsOverlap, shouldParallelizeToolBatch, } from "./parallel-tool-batch.ts";
20
20
  export * from "./proxy.ts";
21
+ export { applyConcurrencyCap, assignDagLevels, computePlanKey, type DagSchedulePlan, type ResolvedClaimEntry, resolveBatchClaims, type ScheduleDagLevelsOptions, scheduleDagLevels, } from "./tool-dag-scheduler.ts";
22
+ export { type ClaimableToolCall, canonicalizeClaims, claimsConflict, compareClaims, type ResolveToolClaimsOptions, resolutionsConflict, resolvePathClaimKey, resolveToolClaims, type ToolClaimResolution, type ToolResourceAccess, type ToolResourceClaim, } from "./tool-resource-claims.ts";
23
+ export { createAbortedToolResult, createTimeoutToolResult, resolveToolExecutionPolicy, resolveToolTimeoutMs, type ToolDispositionEnvelope, type ToolLateSettlement, } from "./tool-timeout.ts";
24
+ export { createSyntheticToolResult, inspectTranscriptIntegrity, repairTranscriptIntegrity, TranscriptIntegrityError, type TranscriptIntegrityIssue, type TranscriptIntegrityIssueKind, type TranscriptIntegrityReport, } from "./tool-transcript-integrity.ts";
21
25
  export * from "./types.ts";
22
26
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,YAAY,CAAC;AAE3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,4BAA4B,CAAC;AAC3C,OAAO,EACN,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,8BAA8B,EAC9B,qBAAqB,EACrB,oBAAoB,GACpB,MAAM,8CAA8C,CAAC;AACtD,OAAO,EACN,sBAAsB,EACtB,OAAO,EACP,2BAA2B,EAC3B,qBAAqB,EACrB,cAAc,EACd,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACrB,iBAAiB,EACjB,qBAAqB,EACrB,aAAa,GACb,MAAM,oCAAoC,CAAC;AAC5C,cAAc,uBAAuB,CAAC;AACtC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,iCAAiC,CAAC;AAChD,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,2BAA2B,CAAC;AACnD,cAAc,qBAAqB,CAAC;AACpC,cAAc,4BAA4B,CAAC;AAE3C,cAAc,oBAAoB,CAAC;AACnC,cAAc,iCAAiC,CAAC;AAChD,cAAc,6BAA6B,CAAC;AAE5C,OAAO,EACN,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,uBAAuB,EACvB,YAAY,EACZ,0BAA0B,GAC1B,MAAM,0BAA0B,CAAC;AAElC,cAAc,YAAY,CAAC;AAE3B,cAAc,YAAY,CAAC","sourcesContent":["// Core Agent\nexport * from \"./agent.ts\";\n// Loop functions\nexport * from \"./agent-loop.ts\";\nexport * from \"./harness/agent-harness.ts\";\nexport {\n\ttype BranchPreparation,\n\ttype BranchSummaryDetails,\n\ttype CollectEntriesResult,\n\tcollectEntriesForBranchSummary,\n\tgenerateBranchSummary,\n\tprepareBranchEntries,\n} from \"./harness/compaction/branch-summarization.ts\";\nexport {\n\tcalculateContextTokens,\n\tcompact,\n\tDEFAULT_COMPACTION_SETTINGS,\n\testimateContextTokens,\n\testimateTokens,\n\tfindCutPoint,\n\tfindTurnStartIndex,\n\tgenerateSummary,\n\tgetLastAssistantUsage,\n\tprepareCompaction,\n\tserializeConversation,\n\tshouldCompact,\n} from \"./harness/compaction/compaction.ts\";\nexport * from \"./harness/messages.ts\";\nexport * from \"./harness/prompt-templates.ts\";\nexport * from \"./harness/reverse-skill.ts\";\nexport * from \"./harness/session/jsonl-repo.ts\";\nexport * from \"./harness/session/memory-repo.ts\";\nexport * from \"./harness/session/repo-utils.ts\";\nexport * from \"./harness/session/session.ts\";\nexport { uuidv7 } from \"./harness/session/uuid.ts\";\nexport * from \"./harness/skills.ts\";\nexport * from \"./harness/system-prompt.ts\";\n// Harness\nexport * from \"./harness/types.ts\";\nexport * from \"./harness/utils/shell-output.ts\";\nexport * from \"./harness/utils/truncate.ts\";\n// Parallel tool batch policy\nexport {\n\tNEVER_PARALLEL_TOOLS,\n\tPARALLEL_SAFE_TOOLS,\n\tPATH_SCOPED_TOOLS,\n\tpartitionToolBatchWaves,\n\tpathsOverlap,\n\tshouldParallelizeToolBatch,\n} from \"./parallel-tool-batch.ts\";\n// Proxy utilities\nexport * from \"./proxy.ts\";\n// Types\nexport * from \"./types.ts\";\n"]}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,YAAY,CAAC;AAE3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,4BAA4B,CAAC;AAC3C,OAAO,EACN,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,8BAA8B,EAC9B,qBAAqB,EACrB,oBAAoB,GACpB,MAAM,8CAA8C,CAAC;AACtD,OAAO,EACN,sBAAsB,EACtB,OAAO,EACP,2BAA2B,EAC3B,qBAAqB,EACrB,cAAc,EACd,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACrB,iBAAiB,EACjB,qBAAqB,EACrB,aAAa,GACb,MAAM,oCAAoC,CAAC;AAC5C,cAAc,uBAAuB,CAAC;AACtC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,iCAAiC,CAAC;AAChD,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,2BAA2B,CAAC;AACnD,cAAc,qBAAqB,CAAC;AACpC,cAAc,4BAA4B,CAAC;AAE3C,cAAc,oBAAoB,CAAC;AACnC,cAAc,iCAAiC,CAAC;AAChD,cAAc,6BAA6B,CAAC;AAE5C,OAAO,EACN,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,uBAAuB,EACvB,YAAY,EACZ,0BAA0B,GAC1B,MAAM,0BAA0B,CAAC;AAElC,cAAc,YAAY,CAAC;AAE3B,OAAO,EACN,mBAAmB,EACnB,eAAe,EACf,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,kBAAkB,EAClB,KAAK,wBAAwB,EAC7B,iBAAiB,GACjB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACN,KAAK,iBAAiB,EACtB,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,KAAK,wBAAwB,EAC7B,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,GACtB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACN,uBAAuB,EACvB,uBAAuB,EACvB,0BAA0B,EAC1B,oBAAoB,EACpB,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,GACvB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACN,yBAAyB,EACzB,0BAA0B,EAC1B,yBAAyB,EACzB,wBAAwB,EACxB,KAAK,wBAAwB,EAC7B,KAAK,4BAA4B,EACjC,KAAK,yBAAyB,GAC9B,MAAM,gCAAgC,CAAC;AAExC,cAAc,YAAY,CAAC","sourcesContent":["// Core Agent\nexport * from \"./agent.ts\";\n// Loop functions\nexport * from \"./agent-loop.ts\";\nexport * from \"./harness/agent-harness.ts\";\nexport {\n\ttype BranchPreparation,\n\ttype BranchSummaryDetails,\n\ttype CollectEntriesResult,\n\tcollectEntriesForBranchSummary,\n\tgenerateBranchSummary,\n\tprepareBranchEntries,\n} from \"./harness/compaction/branch-summarization.ts\";\nexport {\n\tcalculateContextTokens,\n\tcompact,\n\tDEFAULT_COMPACTION_SETTINGS,\n\testimateContextTokens,\n\testimateTokens,\n\tfindCutPoint,\n\tfindTurnStartIndex,\n\tgenerateSummary,\n\tgetLastAssistantUsage,\n\tprepareCompaction,\n\tserializeConversation,\n\tshouldCompact,\n} from \"./harness/compaction/compaction.ts\";\nexport * from \"./harness/messages.ts\";\nexport * from \"./harness/prompt-templates.ts\";\nexport * from \"./harness/reverse-skill.ts\";\nexport * from \"./harness/session/jsonl-repo.ts\";\nexport * from \"./harness/session/memory-repo.ts\";\nexport * from \"./harness/session/repo-utils.ts\";\nexport * from \"./harness/session/session.ts\";\nexport { uuidv7 } from \"./harness/session/uuid.ts\";\nexport * from \"./harness/skills.ts\";\nexport * from \"./harness/system-prompt.ts\";\n// Harness\nexport * from \"./harness/types.ts\";\nexport * from \"./harness/utils/shell-output.ts\";\nexport * from \"./harness/utils/truncate.ts\";\n// Parallel tool batch policy\nexport {\n\tNEVER_PARALLEL_TOOLS,\n\tPARALLEL_SAFE_TOOLS,\n\tPATH_SCOPED_TOOLS,\n\tpartitionToolBatchWaves,\n\tpathsOverlap,\n\tshouldParallelizeToolBatch,\n} from \"./parallel-tool-batch.ts\";\n// Proxy utilities\nexport * from \"./proxy.ts\";\n// Deterministic resource-claim DAG scheduler (dag-v2)\nexport {\n\tapplyConcurrencyCap,\n\tassignDagLevels,\n\tcomputePlanKey,\n\ttype DagSchedulePlan,\n\ttype ResolvedClaimEntry,\n\tresolveBatchClaims,\n\ttype ScheduleDagLevelsOptions,\n\tscheduleDagLevels,\n} from \"./tool-dag-scheduler.ts\";\n// Resource-claim model (dag-v2)\nexport {\n\ttype ClaimableToolCall,\n\tcanonicalizeClaims,\n\tclaimsConflict,\n\tcompareClaims,\n\ttype ResolveToolClaimsOptions,\n\tresolutionsConflict,\n\tresolvePathClaimKey,\n\tresolveToolClaims,\n\ttype ToolClaimResolution,\n\ttype ToolResourceAccess,\n\ttype ToolResourceClaim,\n} from \"./tool-resource-claims.ts\";\n// Per-call timeout / cancellation / late settlement (ALG-004)\nexport {\n\tcreateAbortedToolResult,\n\tcreateTimeoutToolResult,\n\tresolveToolExecutionPolicy,\n\tresolveToolTimeoutMs,\n\ttype ToolDispositionEnvelope,\n\ttype ToolLateSettlement,\n} from \"./tool-timeout.ts\";\nexport {\n\tcreateSyntheticToolResult,\n\tinspectTranscriptIntegrity,\n\trepairTranscriptIntegrity,\n\tTranscriptIntegrityError,\n\ttype TranscriptIntegrityIssue,\n\ttype TranscriptIntegrityIssueKind,\n\ttype TranscriptIntegrityReport,\n} from \"./tool-transcript-integrity.ts\";\n// Types\nexport * from \"./types.ts\";\n"]}
package/dist/index.js CHANGED
@@ -23,6 +23,13 @@ export * from "./harness/utils/truncate.js";
23
23
  export { NEVER_PARALLEL_TOOLS, PARALLEL_SAFE_TOOLS, PATH_SCOPED_TOOLS, partitionToolBatchWaves, pathsOverlap, shouldParallelizeToolBatch, } from "./parallel-tool-batch.js";
24
24
  // Proxy utilities
25
25
  export * from "./proxy.js";
26
+ // Deterministic resource-claim DAG scheduler (dag-v2)
27
+ export { applyConcurrencyCap, assignDagLevels, computePlanKey, resolveBatchClaims, scheduleDagLevels, } from "./tool-dag-scheduler.js";
28
+ // Resource-claim model (dag-v2)
29
+ export { canonicalizeClaims, claimsConflict, compareClaims, resolutionsConflict, resolvePathClaimKey, resolveToolClaims, } from "./tool-resource-claims.js";
30
+ // Per-call timeout / cancellation / late settlement (ALG-004)
31
+ export { createAbortedToolResult, createTimeoutToolResult, resolveToolExecutionPolicy, resolveToolTimeoutMs, } from "./tool-timeout.js";
32
+ export { createSyntheticToolResult, inspectTranscriptIntegrity, repairTranscriptIntegrity, TranscriptIntegrityError, } from "./tool-transcript-integrity.js";
26
33
  // Types
27
34
  export * from "./types.js";
28
35
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,cAAc,YAAY,CAAC;AAC3B,iBAAiB;AACjB,cAAc,iBAAiB,CAAC;AAChC,cAAc,4BAA4B,CAAC;AAC3C,OAAO,EAIN,8BAA8B,EAC9B,qBAAqB,EACrB,oBAAoB,GACpB,MAAM,8CAA8C,CAAC;AACtD,OAAO,EACN,sBAAsB,EACtB,OAAO,EACP,2BAA2B,EAC3B,qBAAqB,EACrB,cAAc,EACd,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACrB,iBAAiB,EACjB,qBAAqB,EACrB,aAAa,GACb,MAAM,oCAAoC,CAAC;AAC5C,cAAc,uBAAuB,CAAC;AACtC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,iCAAiC,CAAC;AAChD,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,2BAA2B,CAAC;AACnD,cAAc,qBAAqB,CAAC;AACpC,cAAc,4BAA4B,CAAC;AAC3C,UAAU;AACV,cAAc,oBAAoB,CAAC;AACnC,cAAc,iCAAiC,CAAC;AAChD,cAAc,6BAA6B,CAAC;AAC5C,6BAA6B;AAC7B,OAAO,EACN,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,uBAAuB,EACvB,YAAY,EACZ,0BAA0B,GAC1B,MAAM,0BAA0B,CAAC;AAClC,kBAAkB;AAClB,cAAc,YAAY,CAAC;AAC3B,QAAQ;AACR,cAAc,YAAY,CAAC","sourcesContent":["// Core Agent\nexport * from \"./agent.ts\";\n// Loop functions\nexport * from \"./agent-loop.ts\";\nexport * from \"./harness/agent-harness.ts\";\nexport {\n\ttype BranchPreparation,\n\ttype BranchSummaryDetails,\n\ttype CollectEntriesResult,\n\tcollectEntriesForBranchSummary,\n\tgenerateBranchSummary,\n\tprepareBranchEntries,\n} from \"./harness/compaction/branch-summarization.ts\";\nexport {\n\tcalculateContextTokens,\n\tcompact,\n\tDEFAULT_COMPACTION_SETTINGS,\n\testimateContextTokens,\n\testimateTokens,\n\tfindCutPoint,\n\tfindTurnStartIndex,\n\tgenerateSummary,\n\tgetLastAssistantUsage,\n\tprepareCompaction,\n\tserializeConversation,\n\tshouldCompact,\n} from \"./harness/compaction/compaction.ts\";\nexport * from \"./harness/messages.ts\";\nexport * from \"./harness/prompt-templates.ts\";\nexport * from \"./harness/reverse-skill.ts\";\nexport * from \"./harness/session/jsonl-repo.ts\";\nexport * from \"./harness/session/memory-repo.ts\";\nexport * from \"./harness/session/repo-utils.ts\";\nexport * from \"./harness/session/session.ts\";\nexport { uuidv7 } from \"./harness/session/uuid.ts\";\nexport * from \"./harness/skills.ts\";\nexport * from \"./harness/system-prompt.ts\";\n// Harness\nexport * from \"./harness/types.ts\";\nexport * from \"./harness/utils/shell-output.ts\";\nexport * from \"./harness/utils/truncate.ts\";\n// Parallel tool batch policy\nexport {\n\tNEVER_PARALLEL_TOOLS,\n\tPARALLEL_SAFE_TOOLS,\n\tPATH_SCOPED_TOOLS,\n\tpartitionToolBatchWaves,\n\tpathsOverlap,\n\tshouldParallelizeToolBatch,\n} from \"./parallel-tool-batch.ts\";\n// Proxy utilities\nexport * from \"./proxy.ts\";\n// Types\nexport * from \"./types.ts\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,cAAc,YAAY,CAAC;AAC3B,iBAAiB;AACjB,cAAc,iBAAiB,CAAC;AAChC,cAAc,4BAA4B,CAAC;AAC3C,OAAO,EAIN,8BAA8B,EAC9B,qBAAqB,EACrB,oBAAoB,GACpB,MAAM,8CAA8C,CAAC;AACtD,OAAO,EACN,sBAAsB,EACtB,OAAO,EACP,2BAA2B,EAC3B,qBAAqB,EACrB,cAAc,EACd,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACrB,iBAAiB,EACjB,qBAAqB,EACrB,aAAa,GACb,MAAM,oCAAoC,CAAC;AAC5C,cAAc,uBAAuB,CAAC;AACtC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,iCAAiC,CAAC;AAChD,cAAc,kCAAkC,CAAC;AACjD,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,2BAA2B,CAAC;AACnD,cAAc,qBAAqB,CAAC;AACpC,cAAc,4BAA4B,CAAC;AAC3C,UAAU;AACV,cAAc,oBAAoB,CAAC;AACnC,cAAc,iCAAiC,CAAC;AAChD,cAAc,6BAA6B,CAAC;AAC5C,6BAA6B;AAC7B,OAAO,EACN,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,uBAAuB,EACvB,YAAY,EACZ,0BAA0B,GAC1B,MAAM,0BAA0B,CAAC;AAClC,kBAAkB;AAClB,cAAc,YAAY,CAAC;AAC3B,sDAAsD;AACtD,OAAO,EACN,mBAAmB,EACnB,eAAe,EACf,cAAc,EAGd,kBAAkB,EAElB,iBAAiB,GACjB,MAAM,yBAAyB,CAAC;AACjC,gCAAgC;AAChC,OAAO,EAEN,kBAAkB,EAClB,cAAc,EACd,aAAa,EAEb,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,GAIjB,MAAM,2BAA2B,CAAC;AACnC,8DAA8D;AAC9D,OAAO,EACN,uBAAuB,EACvB,uBAAuB,EACvB,0BAA0B,EAC1B,oBAAoB,GAGpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACN,yBAAyB,EACzB,0BAA0B,EAC1B,yBAAyB,EACzB,wBAAwB,GAIxB,MAAM,gCAAgC,CAAC;AACxC,QAAQ;AACR,cAAc,YAAY,CAAC","sourcesContent":["// Core Agent\nexport * from \"./agent.ts\";\n// Loop functions\nexport * from \"./agent-loop.ts\";\nexport * from \"./harness/agent-harness.ts\";\nexport {\n\ttype BranchPreparation,\n\ttype BranchSummaryDetails,\n\ttype CollectEntriesResult,\n\tcollectEntriesForBranchSummary,\n\tgenerateBranchSummary,\n\tprepareBranchEntries,\n} from \"./harness/compaction/branch-summarization.ts\";\nexport {\n\tcalculateContextTokens,\n\tcompact,\n\tDEFAULT_COMPACTION_SETTINGS,\n\testimateContextTokens,\n\testimateTokens,\n\tfindCutPoint,\n\tfindTurnStartIndex,\n\tgenerateSummary,\n\tgetLastAssistantUsage,\n\tprepareCompaction,\n\tserializeConversation,\n\tshouldCompact,\n} from \"./harness/compaction/compaction.ts\";\nexport * from \"./harness/messages.ts\";\nexport * from \"./harness/prompt-templates.ts\";\nexport * from \"./harness/reverse-skill.ts\";\nexport * from \"./harness/session/jsonl-repo.ts\";\nexport * from \"./harness/session/memory-repo.ts\";\nexport * from \"./harness/session/repo-utils.ts\";\nexport * from \"./harness/session/session.ts\";\nexport { uuidv7 } from \"./harness/session/uuid.ts\";\nexport * from \"./harness/skills.ts\";\nexport * from \"./harness/system-prompt.ts\";\n// Harness\nexport * from \"./harness/types.ts\";\nexport * from \"./harness/utils/shell-output.ts\";\nexport * from \"./harness/utils/truncate.ts\";\n// Parallel tool batch policy\nexport {\n\tNEVER_PARALLEL_TOOLS,\n\tPARALLEL_SAFE_TOOLS,\n\tPATH_SCOPED_TOOLS,\n\tpartitionToolBatchWaves,\n\tpathsOverlap,\n\tshouldParallelizeToolBatch,\n} from \"./parallel-tool-batch.ts\";\n// Proxy utilities\nexport * from \"./proxy.ts\";\n// Deterministic resource-claim DAG scheduler (dag-v2)\nexport {\n\tapplyConcurrencyCap,\n\tassignDagLevels,\n\tcomputePlanKey,\n\ttype DagSchedulePlan,\n\ttype ResolvedClaimEntry,\n\tresolveBatchClaims,\n\ttype ScheduleDagLevelsOptions,\n\tscheduleDagLevels,\n} from \"./tool-dag-scheduler.ts\";\n// Resource-claim model (dag-v2)\nexport {\n\ttype ClaimableToolCall,\n\tcanonicalizeClaims,\n\tclaimsConflict,\n\tcompareClaims,\n\ttype ResolveToolClaimsOptions,\n\tresolutionsConflict,\n\tresolvePathClaimKey,\n\tresolveToolClaims,\n\ttype ToolClaimResolution,\n\ttype ToolResourceAccess,\n\ttype ToolResourceClaim,\n} from \"./tool-resource-claims.ts\";\n// Per-call timeout / cancellation / late settlement (ALG-004)\nexport {\n\tcreateAbortedToolResult,\n\tcreateTimeoutToolResult,\n\tresolveToolExecutionPolicy,\n\tresolveToolTimeoutMs,\n\ttype ToolDispositionEnvelope,\n\ttype ToolLateSettlement,\n} from \"./tool-timeout.ts\";\nexport {\n\tcreateSyntheticToolResult,\n\tinspectTranscriptIntegrity,\n\trepairTranscriptIntegrity,\n\tTranscriptIntegrityError,\n\ttype TranscriptIntegrityIssue,\n\ttype TranscriptIntegrityIssueKind,\n\ttype TranscriptIntegrityReport,\n} from \"./tool-transcript-integrity.ts\";\n// Types\nexport * from \"./types.ts\";\n"]}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Node-only resource-key identity resolver for the dag-v2 scheduler (§5.5
3
+ * stage 2). The browser-safe lexical layer in `path-segments.ts` cannot see
4
+ * symlinks, hardlinks, or platform path aliasing; this resolver adds that
5
+ * identity without ever weakening the lexical layer:
6
+ *
7
+ * 1. tilde expansion (Node adapter only), then lexical canonicalization
8
+ * 2. POSIX three-or-more leading slashes collapse to one rooted identity
9
+ * 3. unresolved Windows drive/UNC paths return `null` (exclusive scheduling)
10
+ * 4. nearest existing ancestor `realpath`, including dangling-symlink targets
11
+ * 5. `dev:ino` identity when the full path exists
12
+ * 6. filesystem failure falls back to the lexical POSIX key
13
+ *
14
+ * This module is exported from `omk-agent-core/node` only; the main package
15
+ * entry stays platform-free.
16
+ */
17
+ import type { ResourceKeyResolver } from "./types.ts";
18
+ export interface NodeResourceKeyResolverOptions {
19
+ /** Home directory used for tilde expansion. Default: `os.homedir`. */
20
+ homedir?: () => string;
21
+ /** Injectable realpath (tests / hardened environments). Default: `fs.promises.realpath`. */
22
+ realpath?: (path: string) => Promise<string>;
23
+ /** Injectable stat. Default: `fs.promises.stat`. */
24
+ stat?: (path: string) => Promise<{
25
+ dev: number | bigint;
26
+ ino: number | bigint;
27
+ }>;
28
+ /** Injectable lstat used to identify dangling symlinks. */
29
+ lstat?: (path: string) => Promise<{
30
+ isSymbolicLink(): boolean;
31
+ }>;
32
+ /** Injectable readlink used to resolve dangling symlink targets. */
33
+ readlink?: (path: string) => Promise<string>;
34
+ }
35
+ /**
36
+ * Create the Node identity resolver. Pure lexical work is synchronous and
37
+ * deterministic; filesystem identity is best-effort and every failure falls
38
+ * back to the lexical key so scheduling never gets *less* safe than the
39
+ * browser-safe layer.
40
+ */
41
+ export declare function createNodeResourceKeyResolver(options?: NodeResourceKeyResolverOptions): ResourceKeyResolver;
42
+ //# sourceMappingURL=node-resource-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-resource-resolver.d.ts","sourceRoot":"","sources":["../src/node-resource-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAUH,OAAO,KAAK,EAAwB,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAI5E,MAAM,WAAW,8BAA8B;IAC9C,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,MAAM,CAAC;IACvB,4FAA4F;IAC5F,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7C,oDAAoD;IACpD,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC,CAAC;IACjF,2DAA2D;IAC3D,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,cAAc,IAAI,OAAO,CAAA;KAAE,CAAC,CAAC;IACjE,oEAAoE;IACpE,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC7C;AAkBD;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,GAAE,8BAAmC,GAAG,mBAAmB,CA4G/G","sourcesContent":["/**\n * Node-only resource-key identity resolver for the dag-v2 scheduler (§5.5\n * stage 2). The browser-safe lexical layer in `path-segments.ts` cannot see\n * symlinks, hardlinks, or platform path aliasing; this resolver adds that\n * identity without ever weakening the lexical layer:\n *\n * 1. tilde expansion (Node adapter only), then lexical canonicalization\n * 2. POSIX three-or-more leading slashes collapse to one rooted identity\n * 3. unresolved Windows drive/UNC paths return `null` (exclusive scheduling)\n * 4. nearest existing ancestor `realpath`, including dangling-symlink targets\n * 5. `dev:ino` identity when the full path exists\n * 6. filesystem failure falls back to the lexical POSIX key\n *\n * This module is exported from `omk-agent-core/node` only; the main package\n * entry stays platform-free.\n */\n\nimport {\n\tlstat as lstatAsync,\n\treadlink as readlinkAsync,\n\trealpath as realpathAsync,\n\tstat as statAsync,\n} from \"node:fs/promises\";\nimport { homedir as osHomedir } from \"node:os\";\nimport { canonicalizeLexicalPath, joinPathSegments, normalizePathSlashes } from \"./path-segments.ts\";\nimport type { ResolvedResourceKeys, ResourceKeyResolver } from \"./types.ts\";\n\nconst MAX_DANGLING_SYMLINK_DEPTH = 40;\n\nexport interface NodeResourceKeyResolverOptions {\n\t/** Home directory used for tilde expansion. Default: `os.homedir`. */\n\thomedir?: () => string;\n\t/** Injectable realpath (tests / hardened environments). Default: `fs.promises.realpath`. */\n\trealpath?: (path: string) => Promise<string>;\n\t/** Injectable stat. Default: `fs.promises.stat`. */\n\tstat?: (path: string) => Promise<{ dev: number | bigint; ino: number | bigint }>;\n\t/** Injectable lstat used to identify dangling symlinks. */\n\tlstat?: (path: string) => Promise<{ isSymbolicLink(): boolean }>;\n\t/** Injectable readlink used to resolve dangling symlink targets. */\n\treadlink?: (path: string) => Promise<string>;\n}\n\n/** Split a canonical rooted POSIX-style path into ancestor candidates, deepest first. */\nfunction ancestorPaths(canonical: string): string[] {\n\tconst ancestors: string[] = [];\n\tlet current = canonical;\n\twhile (current.length > 1) {\n\t\tconst slash = current.lastIndexOf(\"/\");\n\t\tif (slash <= 0) {\n\t\t\tancestors.push(\"/\");\n\t\t\treturn ancestors;\n\t\t}\n\t\tcurrent = current.slice(0, slash);\n\t\tancestors.push(current);\n\t}\n\treturn ancestors;\n}\n\n/**\n * Create the Node identity resolver. Pure lexical work is synchronous and\n * deterministic; filesystem identity is best-effort and every failure falls\n * back to the lexical key so scheduling never gets *less* safe than the\n * browser-safe layer.\n */\nexport function createNodeResourceKeyResolver(options: NodeResourceKeyResolverOptions = {}): ResourceKeyResolver {\n\tconst home = options.homedir ?? osHomedir;\n\tconst realpath = options.realpath ?? ((path: string) => realpathAsync(path));\n\tconst stat = options.stat ?? ((path: string) => statAsync(path));\n\tconst lstat = options.lstat ?? ((path: string) => lstatAsync(path));\n\tconst readlink = options.readlink ?? ((path: string) => readlinkAsync(path));\n\n\tconst suffixAfter = (path: string, ancestor: string): string =>\n\t\tpath.slice(ancestor.length === 1 ? 1 : ancestor.length + 1);\n\n\tasync function nearestRealKey(canonical: string): Promise<string | undefined> {\n\t\tfor (const ancestor of [canonical, ...ancestorPaths(canonical)]) {\n\t\t\tif (!(await stat(ancestor).catch(() => null))) continue;\n\t\t\tconst realAncestor = canonicalizeLexicalPath(normalizePathSlashes(await realpath(ancestor)));\n\t\t\tif (realAncestor === null) return undefined;\n\t\t\tconst suffix = suffixAfter(canonical, ancestor);\n\t\t\treturn suffix.length === 0 ? realAncestor : joinPathSegments(realAncestor, suffix);\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tasync function danglingSymlinkTarget(canonical: string): Promise<string | null | undefined> {\n\t\tfor (const candidate of [canonical, ...ancestorPaths(canonical)]) {\n\t\t\tconst metadata = await lstat(candidate).catch(() => null);\n\t\t\tif (!metadata?.isSymbolicLink()) continue;\n\t\t\tlet target = normalizePathSlashes(await readlink(candidate));\n\t\t\tif (target.startsWith(\"/\")) target = target.replace(/^\\/+/, \"/\");\n\t\t\tconst slash = candidate.lastIndexOf(\"/\");\n\t\t\tconst parent = slash <= 0 ? \"/\" : candidate.slice(0, slash);\n\t\t\tconst canonicalTarget = canonicalizeLexicalPath(\n\t\t\t\ttarget.startsWith(\"/\") ? target : joinPathSegments(parent, target),\n\t\t\t);\n\t\t\tif (canonicalTarget === null) return null;\n\t\t\tconst suffix = suffixAfter(canonical, candidate);\n\t\t\treturn suffix.length === 0 ? canonicalTarget : joinPathSegments(canonicalTarget, suffix);\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tasync function danglingSymlinkRealKey(canonical: string): Promise<string | undefined> {\n\t\tconst seen = new Set<string>();\n\t\tlet current = canonical;\n\t\tfor (let depth = 0; depth < MAX_DANGLING_SYMLINK_DEPTH; depth++) {\n\t\t\tif (seen.has(current)) return undefined;\n\t\t\tseen.add(current);\n\t\t\tconst target = await danglingSymlinkTarget(current);\n\t\t\tif (target === null) return undefined;\n\t\t\tif (target === undefined) return nearestRealKey(current);\n\t\t\tcurrent = target;\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tasync function resolveIdentity(canonical: string): Promise<Pick<ResolvedResourceKeys, \"realKey\" | \"inodeKey\">> {\n\t\tconst identity: { realKey?: string; inodeKey?: string } = {};\n\t\ttry {\n\t\t\tconst target = await stat(canonical).catch(() => null);\n\t\t\tif (target) {\n\t\t\t\tidentity.inodeKey = `${target.dev}:${target.ino}`;\n\t\t\t\tidentity.realKey = await nearestRealKey(canonical);\n\t\t\t\treturn identity;\n\t\t\t}\n\t\t\tidentity.realKey = await danglingSymlinkRealKey(canonical);\n\t\t\treturn identity;\n\t\t} catch {\n\t\t\treturn {};\n\t\t}\n\t}\n\n\treturn {\n\t\tasync resolvePath(rawPath: string, cwd: string): Promise<ResolvedResourceKeys | null> {\n\t\t\tif (typeof rawPath !== \"string\" || rawPath.trim().length === 0 || typeof cwd !== \"string\") {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tlet normalized = normalizePathSlashes(rawPath);\n\t\t\tif (/^\\/{3,}/.test(normalized)) normalized = normalized.replace(/^\\/+/, \"/\");\n\t\t\t// Tilde expansion happens only in this Node adapter (§5.5 stage 1 note).\n\t\t\tif (normalized === \"~\" || normalized.startsWith(\"~/\")) {\n\t\t\t\ttry {\n\t\t\t\t\tnormalized = joinPathSegments(\n\t\t\t\t\t\tnormalizePathSlashes(home()),\n\t\t\t\t\t\tnormalized === \"~\" ? \"\" : normalized.slice(2),\n\t\t\t\t\t);\n\t\t\t\t} catch {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// This POSIX resolver cannot establish filesystem identity for Windows\n\t\t\t// drive or UNC paths. Null makes each call exclusive instead of sharing an\n\t\t\t// unsafe lexical-only claim with another call.\n\t\t\tif (normalized.startsWith(\"//\") || /^[A-Za-z]:(?:\\/|$)/.test(normalized)) return null;\n\n\t\t\tconst joined = normalized.startsWith(\"/\") ? normalized : joinPathSegments(cwd, normalized);\n\t\t\tconst canonical = canonicalizeLexicalPath(joined);\n\t\t\tif (canonical === null) {\n\t\t\t\t// Root escapes, drive-relative forms, and other ambiguous shapes fail closed.\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (/^[A-Za-z]:\\//.test(canonical)) return null;\n\t\t\tif (!canonical.startsWith(\"/\")) {\n\t\t\t\t// Still relative after canonicalization (non-anchored cwd): fail closed.\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn { lexicalKey: canonical, ...(await resolveIdentity(canonical)) };\n\t\t},\n\t};\n}\n"]}
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Node-only resource-key identity resolver for the dag-v2 scheduler (§5.5
3
+ * stage 2). The browser-safe lexical layer in `path-segments.ts` cannot see
4
+ * symlinks, hardlinks, or platform path aliasing; this resolver adds that
5
+ * identity without ever weakening the lexical layer:
6
+ *
7
+ * 1. tilde expansion (Node adapter only), then lexical canonicalization
8
+ * 2. POSIX three-or-more leading slashes collapse to one rooted identity
9
+ * 3. unresolved Windows drive/UNC paths return `null` (exclusive scheduling)
10
+ * 4. nearest existing ancestor `realpath`, including dangling-symlink targets
11
+ * 5. `dev:ino` identity when the full path exists
12
+ * 6. filesystem failure falls back to the lexical POSIX key
13
+ *
14
+ * This module is exported from `omk-agent-core/node` only; the main package
15
+ * entry stays platform-free.
16
+ */
17
+ import { lstat as lstatAsync, readlink as readlinkAsync, realpath as realpathAsync, stat as statAsync, } from "node:fs/promises";
18
+ import { homedir as osHomedir } from "node:os";
19
+ import { canonicalizeLexicalPath, joinPathSegments, normalizePathSlashes } from "./path-segments.js";
20
+ const MAX_DANGLING_SYMLINK_DEPTH = 40;
21
+ /** Split a canonical rooted POSIX-style path into ancestor candidates, deepest first. */
22
+ function ancestorPaths(canonical) {
23
+ const ancestors = [];
24
+ let current = canonical;
25
+ while (current.length > 1) {
26
+ const slash = current.lastIndexOf("/");
27
+ if (slash <= 0) {
28
+ ancestors.push("/");
29
+ return ancestors;
30
+ }
31
+ current = current.slice(0, slash);
32
+ ancestors.push(current);
33
+ }
34
+ return ancestors;
35
+ }
36
+ /**
37
+ * Create the Node identity resolver. Pure lexical work is synchronous and
38
+ * deterministic; filesystem identity is best-effort and every failure falls
39
+ * back to the lexical key so scheduling never gets *less* safe than the
40
+ * browser-safe layer.
41
+ */
42
+ export function createNodeResourceKeyResolver(options = {}) {
43
+ const home = options.homedir ?? osHomedir;
44
+ const realpath = options.realpath ?? ((path) => realpathAsync(path));
45
+ const stat = options.stat ?? ((path) => statAsync(path));
46
+ const lstat = options.lstat ?? ((path) => lstatAsync(path));
47
+ const readlink = options.readlink ?? ((path) => readlinkAsync(path));
48
+ const suffixAfter = (path, ancestor) => path.slice(ancestor.length === 1 ? 1 : ancestor.length + 1);
49
+ async function nearestRealKey(canonical) {
50
+ for (const ancestor of [canonical, ...ancestorPaths(canonical)]) {
51
+ if (!(await stat(ancestor).catch(() => null)))
52
+ continue;
53
+ const realAncestor = canonicalizeLexicalPath(normalizePathSlashes(await realpath(ancestor)));
54
+ if (realAncestor === null)
55
+ return undefined;
56
+ const suffix = suffixAfter(canonical, ancestor);
57
+ return suffix.length === 0 ? realAncestor : joinPathSegments(realAncestor, suffix);
58
+ }
59
+ return undefined;
60
+ }
61
+ async function danglingSymlinkTarget(canonical) {
62
+ for (const candidate of [canonical, ...ancestorPaths(canonical)]) {
63
+ const metadata = await lstat(candidate).catch(() => null);
64
+ if (!metadata?.isSymbolicLink())
65
+ continue;
66
+ let target = normalizePathSlashes(await readlink(candidate));
67
+ if (target.startsWith("/"))
68
+ target = target.replace(/^\/+/, "/");
69
+ const slash = candidate.lastIndexOf("/");
70
+ const parent = slash <= 0 ? "/" : candidate.slice(0, slash);
71
+ const canonicalTarget = canonicalizeLexicalPath(target.startsWith("/") ? target : joinPathSegments(parent, target));
72
+ if (canonicalTarget === null)
73
+ return null;
74
+ const suffix = suffixAfter(canonical, candidate);
75
+ return suffix.length === 0 ? canonicalTarget : joinPathSegments(canonicalTarget, suffix);
76
+ }
77
+ return undefined;
78
+ }
79
+ async function danglingSymlinkRealKey(canonical) {
80
+ const seen = new Set();
81
+ let current = canonical;
82
+ for (let depth = 0; depth < MAX_DANGLING_SYMLINK_DEPTH; depth++) {
83
+ if (seen.has(current))
84
+ return undefined;
85
+ seen.add(current);
86
+ const target = await danglingSymlinkTarget(current);
87
+ if (target === null)
88
+ return undefined;
89
+ if (target === undefined)
90
+ return nearestRealKey(current);
91
+ current = target;
92
+ }
93
+ return undefined;
94
+ }
95
+ async function resolveIdentity(canonical) {
96
+ const identity = {};
97
+ try {
98
+ const target = await stat(canonical).catch(() => null);
99
+ if (target) {
100
+ identity.inodeKey = `${target.dev}:${target.ino}`;
101
+ identity.realKey = await nearestRealKey(canonical);
102
+ return identity;
103
+ }
104
+ identity.realKey = await danglingSymlinkRealKey(canonical);
105
+ return identity;
106
+ }
107
+ catch {
108
+ return {};
109
+ }
110
+ }
111
+ return {
112
+ async resolvePath(rawPath, cwd) {
113
+ if (typeof rawPath !== "string" || rawPath.trim().length === 0 || typeof cwd !== "string") {
114
+ return null;
115
+ }
116
+ let normalized = normalizePathSlashes(rawPath);
117
+ if (/^\/{3,}/.test(normalized))
118
+ normalized = normalized.replace(/^\/+/, "/");
119
+ // Tilde expansion happens only in this Node adapter (§5.5 stage 1 note).
120
+ if (normalized === "~" || normalized.startsWith("~/")) {
121
+ try {
122
+ normalized = joinPathSegments(normalizePathSlashes(home()), normalized === "~" ? "" : normalized.slice(2));
123
+ }
124
+ catch {
125
+ return null;
126
+ }
127
+ }
128
+ // This POSIX resolver cannot establish filesystem identity for Windows
129
+ // drive or UNC paths. Null makes each call exclusive instead of sharing an
130
+ // unsafe lexical-only claim with another call.
131
+ if (normalized.startsWith("//") || /^[A-Za-z]:(?:\/|$)/.test(normalized))
132
+ return null;
133
+ const joined = normalized.startsWith("/") ? normalized : joinPathSegments(cwd, normalized);
134
+ const canonical = canonicalizeLexicalPath(joined);
135
+ if (canonical === null) {
136
+ // Root escapes, drive-relative forms, and other ambiguous shapes fail closed.
137
+ return null;
138
+ }
139
+ if (/^[A-Za-z]:\//.test(canonical))
140
+ return null;
141
+ if (!canonical.startsWith("/")) {
142
+ // Still relative after canonicalization (non-anchored cwd): fail closed.
143
+ return null;
144
+ }
145
+ return { lexicalKey: canonical, ...(await resolveIdentity(canonical)) };
146
+ },
147
+ };
148
+ }
149
+ //# sourceMappingURL=node-resource-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-resource-resolver.js","sourceRoot":"","sources":["../src/node-resource-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Node-only resource-key identity resolver for the dag-v2 scheduler (§5.5\n * stage 2). The browser-safe lexical layer in `path-segments.ts` cannot see\n * symlinks, hardlinks, or platform path aliasing; this resolver adds that\n * identity without ever weakening the lexical layer:\n *\n * 1. tilde expansion (Node adapter only), then lexical canonicalization\n * 2. POSIX three-or-more leading slashes collapse to one rooted identity\n * 3. unresolved Windows drive/UNC paths return `null` (exclusive scheduling)\n * 4. nearest existing ancestor `realpath`, including dangling-symlink targets\n * 5. `dev:ino` identity when the full path exists\n * 6. filesystem failure falls back to the lexical POSIX key\n *\n * This module is exported from `omk-agent-core/node` only; the main package\n * entry stays platform-free.\n */\n\nimport {\n\tlstat as lstatAsync,\n\treadlink as readlinkAsync,\n\trealpath as realpathAsync,\n\tstat as statAsync,\n} from \"node:fs/promises\";\nimport { homedir as osHomedir } from \"node:os\";\nimport { canonicalizeLexicalPath, joinPathSegments, normalizePathSlashes } from \"./path-segments.ts\";\nimport type { ResolvedResourceKeys, ResourceKeyResolver } from \"./types.ts\";\n\nconst MAX_DANGLING_SYMLINK_DEPTH = 40;\n\nexport interface NodeResourceKeyResolverOptions {\n\t/** Home directory used for tilde expansion. Default: `os.homedir`. */\n\thomedir?: () => string;\n\t/** Injectable realpath (tests / hardened environments). Default: `fs.promises.realpath`. */\n\trealpath?: (path: string) => Promise<string>;\n\t/** Injectable stat. Default: `fs.promises.stat`. */\n\tstat?: (path: string) => Promise<{ dev: number | bigint; ino: number | bigint }>;\n\t/** Injectable lstat used to identify dangling symlinks. */\n\tlstat?: (path: string) => Promise<{ isSymbolicLink(): boolean }>;\n\t/** Injectable readlink used to resolve dangling symlink targets. */\n\treadlink?: (path: string) => Promise<string>;\n}\n\n/** Split a canonical rooted POSIX-style path into ancestor candidates, deepest first. */\nfunction ancestorPaths(canonical: string): string[] {\n\tconst ancestors: string[] = [];\n\tlet current = canonical;\n\twhile (current.length > 1) {\n\t\tconst slash = current.lastIndexOf(\"/\");\n\t\tif (slash <= 0) {\n\t\t\tancestors.push(\"/\");\n\t\t\treturn ancestors;\n\t\t}\n\t\tcurrent = current.slice(0, slash);\n\t\tancestors.push(current);\n\t}\n\treturn ancestors;\n}\n\n/**\n * Create the Node identity resolver. Pure lexical work is synchronous and\n * deterministic; filesystem identity is best-effort and every failure falls\n * back to the lexical key so scheduling never gets *less* safe than the\n * browser-safe layer.\n */\nexport function createNodeResourceKeyResolver(options: NodeResourceKeyResolverOptions = {}): ResourceKeyResolver {\n\tconst home = options.homedir ?? osHomedir;\n\tconst realpath = options.realpath ?? ((path: string) => realpathAsync(path));\n\tconst stat = options.stat ?? ((path: string) => statAsync(path));\n\tconst lstat = options.lstat ?? ((path: string) => lstatAsync(path));\n\tconst readlink = options.readlink ?? ((path: string) => readlinkAsync(path));\n\n\tconst suffixAfter = (path: string, ancestor: string): string =>\n\t\tpath.slice(ancestor.length === 1 ? 1 : ancestor.length + 1);\n\n\tasync function nearestRealKey(canonical: string): Promise<string | undefined> {\n\t\tfor (const ancestor of [canonical, ...ancestorPaths(canonical)]) {\n\t\t\tif (!(await stat(ancestor).catch(() => null))) continue;\n\t\t\tconst realAncestor = canonicalizeLexicalPath(normalizePathSlashes(await realpath(ancestor)));\n\t\t\tif (realAncestor === null) return undefined;\n\t\t\tconst suffix = suffixAfter(canonical, ancestor);\n\t\t\treturn suffix.length === 0 ? realAncestor : joinPathSegments(realAncestor, suffix);\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tasync function danglingSymlinkTarget(canonical: string): Promise<string | null | undefined> {\n\t\tfor (const candidate of [canonical, ...ancestorPaths(canonical)]) {\n\t\t\tconst metadata = await lstat(candidate).catch(() => null);\n\t\t\tif (!metadata?.isSymbolicLink()) continue;\n\t\t\tlet target = normalizePathSlashes(await readlink(candidate));\n\t\t\tif (target.startsWith(\"/\")) target = target.replace(/^\\/+/, \"/\");\n\t\t\tconst slash = candidate.lastIndexOf(\"/\");\n\t\t\tconst parent = slash <= 0 ? \"/\" : candidate.slice(0, slash);\n\t\t\tconst canonicalTarget = canonicalizeLexicalPath(\n\t\t\t\ttarget.startsWith(\"/\") ? target : joinPathSegments(parent, target),\n\t\t\t);\n\t\t\tif (canonicalTarget === null) return null;\n\t\t\tconst suffix = suffixAfter(canonical, candidate);\n\t\t\treturn suffix.length === 0 ? canonicalTarget : joinPathSegments(canonicalTarget, suffix);\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tasync function danglingSymlinkRealKey(canonical: string): Promise<string | undefined> {\n\t\tconst seen = new Set<string>();\n\t\tlet current = canonical;\n\t\tfor (let depth = 0; depth < MAX_DANGLING_SYMLINK_DEPTH; depth++) {\n\t\t\tif (seen.has(current)) return undefined;\n\t\t\tseen.add(current);\n\t\t\tconst target = await danglingSymlinkTarget(current);\n\t\t\tif (target === null) return undefined;\n\t\t\tif (target === undefined) return nearestRealKey(current);\n\t\t\tcurrent = target;\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tasync function resolveIdentity(canonical: string): Promise<Pick<ResolvedResourceKeys, \"realKey\" | \"inodeKey\">> {\n\t\tconst identity: { realKey?: string; inodeKey?: string } = {};\n\t\ttry {\n\t\t\tconst target = await stat(canonical).catch(() => null);\n\t\t\tif (target) {\n\t\t\t\tidentity.inodeKey = `${target.dev}:${target.ino}`;\n\t\t\t\tidentity.realKey = await nearestRealKey(canonical);\n\t\t\t\treturn identity;\n\t\t\t}\n\t\t\tidentity.realKey = await danglingSymlinkRealKey(canonical);\n\t\t\treturn identity;\n\t\t} catch {\n\t\t\treturn {};\n\t\t}\n\t}\n\n\treturn {\n\t\tasync resolvePath(rawPath: string, cwd: string): Promise<ResolvedResourceKeys | null> {\n\t\t\tif (typeof rawPath !== \"string\" || rawPath.trim().length === 0 || typeof cwd !== \"string\") {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tlet normalized = normalizePathSlashes(rawPath);\n\t\t\tif (/^\\/{3,}/.test(normalized)) normalized = normalized.replace(/^\\/+/, \"/\");\n\t\t\t// Tilde expansion happens only in this Node adapter (§5.5 stage 1 note).\n\t\t\tif (normalized === \"~\" || normalized.startsWith(\"~/\")) {\n\t\t\t\ttry {\n\t\t\t\t\tnormalized = joinPathSegments(\n\t\t\t\t\t\tnormalizePathSlashes(home()),\n\t\t\t\t\t\tnormalized === \"~\" ? \"\" : normalized.slice(2),\n\t\t\t\t\t);\n\t\t\t\t} catch {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// This POSIX resolver cannot establish filesystem identity for Windows\n\t\t\t// drive or UNC paths. Null makes each call exclusive instead of sharing an\n\t\t\t// unsafe lexical-only claim with another call.\n\t\t\tif (normalized.startsWith(\"//\") || /^[A-Za-z]:(?:\\/|$)/.test(normalized)) return null;\n\n\t\t\tconst joined = normalized.startsWith(\"/\") ? normalized : joinPathSegments(cwd, normalized);\n\t\t\tconst canonical = canonicalizeLexicalPath(joined);\n\t\t\tif (canonical === null) {\n\t\t\t\t// Root escapes, drive-relative forms, and other ambiguous shapes fail closed.\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (/^[A-Za-z]:\\//.test(canonical)) return null;\n\t\t\tif (!canonical.startsWith(\"/\")) {\n\t\t\t\t// Still relative after canonicalization (non-anchored cwd): fail closed.\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn { lexicalKey: canonical, ...(await resolveIdentity(canonical)) };\n\t\t},\n\t};\n}\n"]}
package/dist/node.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export { NodeExecutionEnv } from "./harness/env/nodejs.ts";
2
2
  export * from "./index.ts";
3
+ export { createNodeResourceKeyResolver, type NodeResourceKeyResolverOptions, } from "./node-resource-resolver.ts";
3
4
  //# sourceMappingURL=node.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,cAAc,YAAY,CAAC","sourcesContent":["export { NodeExecutionEnv } from \"./harness/env/nodejs.ts\";\nexport * from \"./index.ts\";\n"]}
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,cAAc,YAAY,CAAC;AAE3B,OAAO,EACN,6BAA6B,EAC7B,KAAK,8BAA8B,GACnC,MAAM,6BAA6B,CAAC","sourcesContent":["export { NodeExecutionEnv } from \"./harness/env/nodejs.ts\";\nexport * from \"./index.ts\";\n// Node-only §5.5 identity resolver for the dag-v2 scheduler (fs/realpath/inode).\nexport {\n\tcreateNodeResourceKeyResolver,\n\ttype NodeResourceKeyResolverOptions,\n} from \"./node-resource-resolver.ts\";\n"]}
package/dist/node.js CHANGED
@@ -1,3 +1,5 @@
1
1
  export { NodeExecutionEnv } from "./harness/env/nodejs.js";
2
2
  export * from "./index.js";
3
+ // Node-only §5.5 identity resolver for the dag-v2 scheduler (fs/realpath/inode).
4
+ export { createNodeResourceKeyResolver, } from "./node-resource-resolver.js";
3
5
  //# sourceMappingURL=node.js.map
package/dist/node.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"node.js","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,cAAc,YAAY,CAAC","sourcesContent":["export { NodeExecutionEnv } from \"./harness/env/nodejs.ts\";\nexport * from \"./index.ts\";\n"]}
1
+ {"version":3,"file":"node.js","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,cAAc,YAAY,CAAC;AAC3B,kFAAiF;AACjF,OAAO,EACN,6BAA6B,GAE7B,MAAM,6BAA6B,CAAC","sourcesContent":["export { NodeExecutionEnv } from \"./harness/env/nodejs.ts\";\nexport * from \"./index.ts\";\n// Node-only §5.5 identity resolver for the dag-v2 scheduler (fs/realpath/inode).\nexport {\n\tcreateNodeResourceKeyResolver,\n\ttype NodeResourceKeyResolverOptions,\n} from \"./node-resource-resolver.ts\";\n"]}
@@ -10,6 +10,14 @@ export declare function joinPathSegments(cwd: string, relative: string): string;
10
10
  * when a ".." escapes above the root, so callers can fail closed.
11
11
  */
12
12
  export declare function collapsePathSegments(segments: readonly string[]): string[] | null;
13
+ /**
14
+ * Canonicalize a path lexically while preserving a Windows drive root. Drive-
15
+ * relative forms such as `C:foo` and UNC forms are ambiguous without platform
16
+ * state and fail closed. Windows drive-root traversal clamps at the drive root,
17
+ * matching rooted path semantics; POSIX/rootless traversal keeps the existing
18
+ * fail-closed behavior.
19
+ */
20
+ export declare function canonicalizeLexicalPath(raw: string): string | null;
13
21
  /**
14
22
  * True when two normalized paths may refer to the same file or subtree.
15
23
  * Fail-closed: "."/".." are collapsed first, root-escaping paths always count
@@ -1 +1 @@
1
- {"version":3,"file":"path-segments.d.ts","sourceRoot":"","sources":["../src/path-segments.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAEzD;AAED,6DAA6D;AAC7D,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAUtE;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,IAAI,CAYjF;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAgBxE","sourcesContent":["/**\n * Path segment helpers without node:path — safe for browser bundles (browser-smoke).\n */\n\nexport function normalizePathSlashes(raw: string): string {\n\treturn raw.replace(/\\\\/g, \"/\");\n}\n\nexport function pathSegments(normalized: string): string[] {\n\treturn normalized.split(\"/\").filter((s) => s.length > 0);\n}\n\n/** Join cwd and relative path using forward slashes only. */\nexport function joinPathSegments(cwd: string, relative: string): string {\n\tconst base = normalizePathSlashes(cwd).replace(/\\/+$/, \"\");\n\tconst rel = normalizePathSlashes(relative).replace(/^\\/+/, \"\");\n\tif (rel.length === 0) {\n\t\treturn base || \"/\";\n\t}\n\tif (rel.startsWith(\"/\")) {\n\t\treturn rel;\n\t}\n\treturn `${base}/${rel}`;\n}\n\n/**\n * Collapse \".\" and \"..\" segments without touching the filesystem. Returns null\n * when a \"..\" escapes above the root, so callers can fail closed.\n */\nexport function collapsePathSegments(segments: readonly string[]): string[] | null {\n\tconst collapsed: string[] = [];\n\tfor (const segment of segments) {\n\t\tif (segment === \".\") continue;\n\t\tif (segment === \"..\") {\n\t\t\tif (collapsed.length === 0) return null;\n\t\t\tcollapsed.pop();\n\t\t\tcontinue;\n\t\t}\n\t\tcollapsed.push(segment);\n\t}\n\treturn collapsed;\n}\n\n/**\n * True when two normalized paths may refer to the same file or subtree.\n * Fail-closed: \".\"/\"..\" are collapsed first, root-escaping paths always count\n * as overlapping, and segments compare case-insensitively so case-aliasing\n * filesystems never split one file into two \"independent\" paths. Symlinks and\n * hardlinks are not resolved here (no fs access); overlap only forces\n * sequential execution, so false positives are safe.\n */\nexport function pathSegmentsOverlap(left: string, right: string): boolean {\n\tconst leftParts = collapsePathSegments(pathSegments(normalizePathSlashes(left)));\n\tconst rightParts = collapsePathSegments(pathSegments(normalizePathSlashes(right)));\n\tif (leftParts === null || rightParts === null) {\n\t\treturn true;\n\t}\n\tif (leftParts.length === 0 || rightParts.length === 0) {\n\t\treturn leftParts.length === rightParts.length && leftParts.length > 0;\n\t}\n\tconst commonLen = Math.min(leftParts.length, rightParts.length);\n\tfor (let i = 0; i < commonLen; i++) {\n\t\tif (leftParts[i].toLowerCase() !== rightParts[i].toLowerCase()) {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n"]}
1
+ {"version":3,"file":"path-segments.d.ts","sourceRoot":"","sources":["../src/path-segments.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAEzD;AAED,6DAA6D;AAC7D,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAUtE;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,IAAI,CAYjF;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA4BlE;AAoBD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAsBxE","sourcesContent":["/**\n * Path segment helpers without node:path — safe for browser bundles (browser-smoke).\n */\n\nexport function normalizePathSlashes(raw: string): string {\n\treturn raw.replace(/\\\\/g, \"/\");\n}\n\nexport function pathSegments(normalized: string): string[] {\n\treturn normalized.split(\"/\").filter((s) => s.length > 0);\n}\n\n/** Join cwd and relative path using forward slashes only. */\nexport function joinPathSegments(cwd: string, relative: string): string {\n\tconst base = normalizePathSlashes(cwd).replace(/\\/+$/, \"\");\n\tconst rel = normalizePathSlashes(relative);\n\tif (rel.length === 0) {\n\t\treturn base || \"/\";\n\t}\n\tif (rel.startsWith(\"/\") || /^[A-Za-z]:/.test(rel)) {\n\t\treturn rel;\n\t}\n\treturn `${base}/${rel}`;\n}\n\n/**\n * Collapse \".\" and \"..\" segments without touching the filesystem. Returns null\n * when a \"..\" escapes above the root, so callers can fail closed.\n */\nexport function collapsePathSegments(segments: readonly string[]): string[] | null {\n\tconst collapsed: string[] = [];\n\tfor (const segment of segments) {\n\t\tif (segment === \".\") continue;\n\t\tif (segment === \"..\") {\n\t\t\tif (collapsed.length === 0) return null;\n\t\t\tcollapsed.pop();\n\t\t\tcontinue;\n\t\t}\n\t\tcollapsed.push(segment);\n\t}\n\treturn collapsed;\n}\n\n/**\n * Canonicalize a path lexically while preserving a Windows drive root. Drive-\n * relative forms such as `C:foo` and UNC forms are ambiguous without platform\n * state and fail closed. Windows drive-root traversal clamps at the drive root,\n * matching rooted path semantics; POSIX/rootless traversal keeps the existing\n * fail-closed behavior.\n */\nexport function canonicalizeLexicalPath(raw: string): string | null {\n\tconst normalized = normalizePathSlashes(raw);\n\tif (normalized.startsWith(\"//\")) {\n\t\treturn null;\n\t}\n\tconst driveAbsolute = /^([A-Za-z]:)\\/+(.*)$/.exec(normalized);\n\tif (driveAbsolute) {\n\t\tconst collapsed: string[] = [];\n\t\tfor (const segment of pathSegments(driveAbsolute[2])) {\n\t\t\tif (segment === \".\") continue;\n\t\t\tif (segment === \"..\") {\n\t\t\t\tcollapsed.pop();\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tcollapsed.push(segment);\n\t\t}\n\t\treturn `${driveAbsolute[1]}/${collapsed.join(\"/\")}`;\n\t}\n\tif (/^[A-Za-z]:/.test(normalized)) {\n\t\treturn null;\n\t}\n\n\tconst rooted = normalized.startsWith(\"/\");\n\tconst collapsed = collapsePathSegments(pathSegments(normalized));\n\tif (collapsed === null) {\n\t\treturn null;\n\t}\n\treturn rooted ? `/${collapsed.join(\"/\")}` : collapsed.join(\"/\");\n}\n\ninterface CanonicalPathParts {\n\tdrive: string | null;\n\trooted: boolean;\n\tsegments: string[];\n}\n\nfunction canonicalPathParts(raw: string): CanonicalPathParts | null {\n\tconst canonical = canonicalizeLexicalPath(raw);\n\tif (canonical === null) {\n\t\treturn null;\n\t}\n\tconst driveAbsolute = /^([A-Za-z]:)\\/(.*)$/.exec(canonical);\n\tif (driveAbsolute) {\n\t\treturn { drive: driveAbsolute[1].toLowerCase(), rooted: true, segments: pathSegments(driveAbsolute[2]) };\n\t}\n\treturn { drive: null, rooted: canonical.startsWith(\"/\"), segments: pathSegments(canonical) };\n}\n\n/**\n * True when two normalized paths may refer to the same file or subtree.\n * Fail-closed: \".\"/\"..\" are collapsed first, root-escaping paths always count\n * as overlapping, and segments compare case-insensitively so case-aliasing\n * filesystems never split one file into two \"independent\" paths. Symlinks and\n * hardlinks are not resolved here (no fs access); overlap only forces\n * sequential execution, so false positives are safe.\n */\nexport function pathSegmentsOverlap(left: string, right: string): boolean {\n\tconst leftPath = canonicalPathParts(left);\n\tconst rightPath = canonicalPathParts(right);\n\tif (leftPath === null || rightPath === null) {\n\t\treturn true;\n\t}\n\tif (leftPath.drive !== null && rightPath.drive !== null && leftPath.drive !== rightPath.drive) {\n\t\treturn false;\n\t}\n\tif ((leftPath.drive !== null && !rightPath.rooted) || (rightPath.drive !== null && !leftPath.rooted)) {\n\t\treturn true;\n\t}\n\tif (leftPath.segments.length === 0 || rightPath.segments.length === 0) {\n\t\treturn true;\n\t}\n\tconst commonLen = Math.min(leftPath.segments.length, rightPath.segments.length);\n\tfor (let i = 0; i < commonLen; i++) {\n\t\tif (leftPath.segments[i].toLowerCase() !== rightPath.segments[i].toLowerCase()) {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n"]}
@@ -10,11 +10,11 @@ export function pathSegments(normalized) {
10
10
  /** Join cwd and relative path using forward slashes only. */
11
11
  export function joinPathSegments(cwd, relative) {
12
12
  const base = normalizePathSlashes(cwd).replace(/\/+$/, "");
13
- const rel = normalizePathSlashes(relative).replace(/^\/+/, "");
13
+ const rel = normalizePathSlashes(relative);
14
14
  if (rel.length === 0) {
15
15
  return base || "/";
16
16
  }
17
- if (rel.startsWith("/")) {
17
+ if (rel.startsWith("/") || /^[A-Za-z]:/.test(rel)) {
18
18
  return rel;
19
19
  }
20
20
  return `${base}/${rel}`;
@@ -38,6 +38,53 @@ export function collapsePathSegments(segments) {
38
38
  }
39
39
  return collapsed;
40
40
  }
41
+ /**
42
+ * Canonicalize a path lexically while preserving a Windows drive root. Drive-
43
+ * relative forms such as `C:foo` and UNC forms are ambiguous without platform
44
+ * state and fail closed. Windows drive-root traversal clamps at the drive root,
45
+ * matching rooted path semantics; POSIX/rootless traversal keeps the existing
46
+ * fail-closed behavior.
47
+ */
48
+ export function canonicalizeLexicalPath(raw) {
49
+ const normalized = normalizePathSlashes(raw);
50
+ if (normalized.startsWith("//")) {
51
+ return null;
52
+ }
53
+ const driveAbsolute = /^([A-Za-z]:)\/+(.*)$/.exec(normalized);
54
+ if (driveAbsolute) {
55
+ const collapsed = [];
56
+ for (const segment of pathSegments(driveAbsolute[2])) {
57
+ if (segment === ".")
58
+ continue;
59
+ if (segment === "..") {
60
+ collapsed.pop();
61
+ continue;
62
+ }
63
+ collapsed.push(segment);
64
+ }
65
+ return `${driveAbsolute[1]}/${collapsed.join("/")}`;
66
+ }
67
+ if (/^[A-Za-z]:/.test(normalized)) {
68
+ return null;
69
+ }
70
+ const rooted = normalized.startsWith("/");
71
+ const collapsed = collapsePathSegments(pathSegments(normalized));
72
+ if (collapsed === null) {
73
+ return null;
74
+ }
75
+ return rooted ? `/${collapsed.join("/")}` : collapsed.join("/");
76
+ }
77
+ function canonicalPathParts(raw) {
78
+ const canonical = canonicalizeLexicalPath(raw);
79
+ if (canonical === null) {
80
+ return null;
81
+ }
82
+ const driveAbsolute = /^([A-Za-z]:)\/(.*)$/.exec(canonical);
83
+ if (driveAbsolute) {
84
+ return { drive: driveAbsolute[1].toLowerCase(), rooted: true, segments: pathSegments(driveAbsolute[2]) };
85
+ }
86
+ return { drive: null, rooted: canonical.startsWith("/"), segments: pathSegments(canonical) };
87
+ }
41
88
  /**
42
89
  * True when two normalized paths may refer to the same file or subtree.
43
90
  * Fail-closed: "."/".." are collapsed first, root-escaping paths always count
@@ -47,17 +94,23 @@ export function collapsePathSegments(segments) {
47
94
  * sequential execution, so false positives are safe.
48
95
  */
49
96
  export function pathSegmentsOverlap(left, right) {
50
- const leftParts = collapsePathSegments(pathSegments(normalizePathSlashes(left)));
51
- const rightParts = collapsePathSegments(pathSegments(normalizePathSlashes(right)));
52
- if (leftParts === null || rightParts === null) {
97
+ const leftPath = canonicalPathParts(left);
98
+ const rightPath = canonicalPathParts(right);
99
+ if (leftPath === null || rightPath === null) {
53
100
  return true;
54
101
  }
55
- if (leftParts.length === 0 || rightParts.length === 0) {
56
- return leftParts.length === rightParts.length && leftParts.length > 0;
102
+ if (leftPath.drive !== null && rightPath.drive !== null && leftPath.drive !== rightPath.drive) {
103
+ return false;
104
+ }
105
+ if ((leftPath.drive !== null && !rightPath.rooted) || (rightPath.drive !== null && !leftPath.rooted)) {
106
+ return true;
107
+ }
108
+ if (leftPath.segments.length === 0 || rightPath.segments.length === 0) {
109
+ return true;
57
110
  }
58
- const commonLen = Math.min(leftParts.length, rightParts.length);
111
+ const commonLen = Math.min(leftPath.segments.length, rightPath.segments.length);
59
112
  for (let i = 0; i < commonLen; i++) {
60
- if (leftParts[i].toLowerCase() !== rightParts[i].toLowerCase()) {
113
+ if (leftPath.segments[i].toLowerCase() !== rightPath.segments[i].toLowerCase()) {
61
114
  return false;
62
115
  }
63
116
  }
@@ -1 +1 @@
1
- {"version":3,"file":"path-segments.js","sourceRoot":"","sources":["../src/path-segments.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,UAAU,oBAAoB,CAAC,GAAW,EAAU;IACzD,OAAO,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AAAA,CAC/B;AAED,MAAM,UAAU,YAAY,CAAC,UAAkB,EAAY;IAC1D,OAAO,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAAA,CACzD;AAED,6DAA6D;AAC7D,MAAM,UAAU,gBAAgB,CAAC,GAAW,EAAE,QAAgB,EAAU;IACvE,MAAM,IAAI,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC3D,MAAM,GAAG,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC/D,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,IAAI,IAAI,GAAG,CAAC;IACpB,CAAC;IACD,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACzB,OAAO,GAAG,CAAC;IACZ,CAAC;IACD,OAAO,GAAG,IAAI,IAAI,GAAG,EAAE,CAAC;AAAA,CACxB;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,QAA2B,EAAmB;IAClF,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAChC,IAAI,OAAO,KAAK,GAAG;YAAE,SAAS;QAC9B,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACtB,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC;YACxC,SAAS,CAAC,GAAG,EAAE,CAAC;YAChB,SAAS;QACV,CAAC;QACD,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACzB,CAAC;IACD,OAAO,SAAS,CAAC;AAAA,CACjB;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY,EAAE,KAAa,EAAW;IACzE,MAAM,SAAS,GAAG,oBAAoB,CAAC,YAAY,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjF,MAAM,UAAU,GAAG,oBAAoB,CAAC,YAAY,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnF,IAAI,SAAS,KAAK,IAAI,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;QAC/C,OAAO,IAAI,CAAC;IACb,CAAC;IACD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvD,OAAO,SAAS,CAAC,MAAM,KAAK,UAAU,CAAC,MAAM,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC;IACvE,CAAC;IACD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;IAChE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC;QACpC,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;YAChE,OAAO,KAAK,CAAC;QACd,CAAC;IACF,CAAC;IACD,OAAO,IAAI,CAAC;AAAA,CACZ","sourcesContent":["/**\n * Path segment helpers without node:path — safe for browser bundles (browser-smoke).\n */\n\nexport function normalizePathSlashes(raw: string): string {\n\treturn raw.replace(/\\\\/g, \"/\");\n}\n\nexport function pathSegments(normalized: string): string[] {\n\treturn normalized.split(\"/\").filter((s) => s.length > 0);\n}\n\n/** Join cwd and relative path using forward slashes only. */\nexport function joinPathSegments(cwd: string, relative: string): string {\n\tconst base = normalizePathSlashes(cwd).replace(/\\/+$/, \"\");\n\tconst rel = normalizePathSlashes(relative).replace(/^\\/+/, \"\");\n\tif (rel.length === 0) {\n\t\treturn base || \"/\";\n\t}\n\tif (rel.startsWith(\"/\")) {\n\t\treturn rel;\n\t}\n\treturn `${base}/${rel}`;\n}\n\n/**\n * Collapse \".\" and \"..\" segments without touching the filesystem. Returns null\n * when a \"..\" escapes above the root, so callers can fail closed.\n */\nexport function collapsePathSegments(segments: readonly string[]): string[] | null {\n\tconst collapsed: string[] = [];\n\tfor (const segment of segments) {\n\t\tif (segment === \".\") continue;\n\t\tif (segment === \"..\") {\n\t\t\tif (collapsed.length === 0) return null;\n\t\t\tcollapsed.pop();\n\t\t\tcontinue;\n\t\t}\n\t\tcollapsed.push(segment);\n\t}\n\treturn collapsed;\n}\n\n/**\n * True when two normalized paths may refer to the same file or subtree.\n * Fail-closed: \".\"/\"..\" are collapsed first, root-escaping paths always count\n * as overlapping, and segments compare case-insensitively so case-aliasing\n * filesystems never split one file into two \"independent\" paths. Symlinks and\n * hardlinks are not resolved here (no fs access); overlap only forces\n * sequential execution, so false positives are safe.\n */\nexport function pathSegmentsOverlap(left: string, right: string): boolean {\n\tconst leftParts = collapsePathSegments(pathSegments(normalizePathSlashes(left)));\n\tconst rightParts = collapsePathSegments(pathSegments(normalizePathSlashes(right)));\n\tif (leftParts === null || rightParts === null) {\n\t\treturn true;\n\t}\n\tif (leftParts.length === 0 || rightParts.length === 0) {\n\t\treturn leftParts.length === rightParts.length && leftParts.length > 0;\n\t}\n\tconst commonLen = Math.min(leftParts.length, rightParts.length);\n\tfor (let i = 0; i < commonLen; i++) {\n\t\tif (leftParts[i].toLowerCase() !== rightParts[i].toLowerCase()) {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n"]}
1
+ {"version":3,"file":"path-segments.js","sourceRoot":"","sources":["../src/path-segments.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Path segment helpers without node:path — safe for browser bundles (browser-smoke).\n */\n\nexport function normalizePathSlashes(raw: string): string {\n\treturn raw.replace(/\\\\/g, \"/\");\n}\n\nexport function pathSegments(normalized: string): string[] {\n\treturn normalized.split(\"/\").filter((s) => s.length > 0);\n}\n\n/** Join cwd and relative path using forward slashes only. */\nexport function joinPathSegments(cwd: string, relative: string): string {\n\tconst base = normalizePathSlashes(cwd).replace(/\\/+$/, \"\");\n\tconst rel = normalizePathSlashes(relative);\n\tif (rel.length === 0) {\n\t\treturn base || \"/\";\n\t}\n\tif (rel.startsWith(\"/\") || /^[A-Za-z]:/.test(rel)) {\n\t\treturn rel;\n\t}\n\treturn `${base}/${rel}`;\n}\n\n/**\n * Collapse \".\" and \"..\" segments without touching the filesystem. Returns null\n * when a \"..\" escapes above the root, so callers can fail closed.\n */\nexport function collapsePathSegments(segments: readonly string[]): string[] | null {\n\tconst collapsed: string[] = [];\n\tfor (const segment of segments) {\n\t\tif (segment === \".\") continue;\n\t\tif (segment === \"..\") {\n\t\t\tif (collapsed.length === 0) return null;\n\t\t\tcollapsed.pop();\n\t\t\tcontinue;\n\t\t}\n\t\tcollapsed.push(segment);\n\t}\n\treturn collapsed;\n}\n\n/**\n * Canonicalize a path lexically while preserving a Windows drive root. Drive-\n * relative forms such as `C:foo` and UNC forms are ambiguous without platform\n * state and fail closed. Windows drive-root traversal clamps at the drive root,\n * matching rooted path semantics; POSIX/rootless traversal keeps the existing\n * fail-closed behavior.\n */\nexport function canonicalizeLexicalPath(raw: string): string | null {\n\tconst normalized = normalizePathSlashes(raw);\n\tif (normalized.startsWith(\"//\")) {\n\t\treturn null;\n\t}\n\tconst driveAbsolute = /^([A-Za-z]:)\\/+(.*)$/.exec(normalized);\n\tif (driveAbsolute) {\n\t\tconst collapsed: string[] = [];\n\t\tfor (const segment of pathSegments(driveAbsolute[2])) {\n\t\t\tif (segment === \".\") continue;\n\t\t\tif (segment === \"..\") {\n\t\t\t\tcollapsed.pop();\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tcollapsed.push(segment);\n\t\t}\n\t\treturn `${driveAbsolute[1]}/${collapsed.join(\"/\")}`;\n\t}\n\tif (/^[A-Za-z]:/.test(normalized)) {\n\t\treturn null;\n\t}\n\n\tconst rooted = normalized.startsWith(\"/\");\n\tconst collapsed = collapsePathSegments(pathSegments(normalized));\n\tif (collapsed === null) {\n\t\treturn null;\n\t}\n\treturn rooted ? `/${collapsed.join(\"/\")}` : collapsed.join(\"/\");\n}\n\ninterface CanonicalPathParts {\n\tdrive: string | null;\n\trooted: boolean;\n\tsegments: string[];\n}\n\nfunction canonicalPathParts(raw: string): CanonicalPathParts | null {\n\tconst canonical = canonicalizeLexicalPath(raw);\n\tif (canonical === null) {\n\t\treturn null;\n\t}\n\tconst driveAbsolute = /^([A-Za-z]:)\\/(.*)$/.exec(canonical);\n\tif (driveAbsolute) {\n\t\treturn { drive: driveAbsolute[1].toLowerCase(), rooted: true, segments: pathSegments(driveAbsolute[2]) };\n\t}\n\treturn { drive: null, rooted: canonical.startsWith(\"/\"), segments: pathSegments(canonical) };\n}\n\n/**\n * True when two normalized paths may refer to the same file or subtree.\n * Fail-closed: \".\"/\"..\" are collapsed first, root-escaping paths always count\n * as overlapping, and segments compare case-insensitively so case-aliasing\n * filesystems never split one file into two \"independent\" paths. Symlinks and\n * hardlinks are not resolved here (no fs access); overlap only forces\n * sequential execution, so false positives are safe.\n */\nexport function pathSegmentsOverlap(left: string, right: string): boolean {\n\tconst leftPath = canonicalPathParts(left);\n\tconst rightPath = canonicalPathParts(right);\n\tif (leftPath === null || rightPath === null) {\n\t\treturn true;\n\t}\n\tif (leftPath.drive !== null && rightPath.drive !== null && leftPath.drive !== rightPath.drive) {\n\t\treturn false;\n\t}\n\tif ((leftPath.drive !== null && !rightPath.rooted) || (rightPath.drive !== null && !leftPath.rooted)) {\n\t\treturn true;\n\t}\n\tif (leftPath.segments.length === 0 || rightPath.segments.length === 0) {\n\t\treturn true;\n\t}\n\tconst commonLen = Math.min(leftPath.segments.length, rightPath.segments.length);\n\tfor (let i = 0; i < commonLen; i++) {\n\t\tif (leftPath.segments[i].toLowerCase() !== rightPath.segments[i].toLowerCase()) {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n"]}
@@ -0,0 +1,7 @@
1
+ /** Parse a detached mutable JSON-domain value, rejecting every unsupported shape. */
2
+ export declare function parseJsonValue<T>(value: T): T;
3
+ /** Clone a plain-data payload, then recursively freeze its executor-owned graph. */
4
+ export declare function createImmutableSnapshot<T>(value: T): T;
5
+ /** Parse and freeze a detached JSON-domain value. */
6
+ export declare function createImmutableJsonSnapshot<T>(value: T): T;
7
+ //# sourceMappingURL=plain-data.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plain-data.d.ts","sourceRoot":"","sources":["../src/plain-data.ts"],"names":[],"mappings":"AAwCA,qFAAqF;AACrF,wBAAgB,cAAc,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAM7C;AAED,oFAAoF;AACpF,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAMtD;AAED,qDAAqD;AACrD,wBAAgB,2BAA2B,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAI1D","sourcesContent":["function assertPlainData(value: unknown, jsonOnly: boolean, ancestors = new WeakSet<object>()): void {\n\tif (value === null || typeof value === \"string\" || typeof value === \"boolean\") return;\n\tif (typeof value === \"number\") {\n\t\tif (Number.isFinite(value)) return;\n\t\tthrow new TypeError(\"Non-finite numbers are not supported data values\");\n\t}\n\tif (value === undefined && !jsonOnly) return;\n\tif (typeof value !== \"object\") throw new TypeError(\"Unsupported non-data value\");\n\tif (ancestors.has(value)) throw new TypeError(\"Cyclic data values are not supported\");\n\tconst prototype = Object.getPrototypeOf(value);\n\tif (!Array.isArray(value) && prototype !== Object.prototype && prototype !== null) {\n\t\tthrow new TypeError(\"Unsupported data object prototype\");\n\t}\n\tancestors.add(value);\n\ttry {\n\t\tif (Array.isArray(value)) {\n\t\t\tif (prototype !== Array.prototype || Object.keys(value).length !== value.length) {\n\t\t\t\tthrow new TypeError(\"Sparse or extended arrays are not supported data values\");\n\t\t\t}\n\t\t\tfor (const item of value) assertPlainData(item, jsonOnly, ancestors);\n\t\t\treturn;\n\t\t}\n\t\tfor (const key of Reflect.ownKeys(value)) {\n\t\t\tconst descriptor = Object.getOwnPropertyDescriptor(value, key);\n\t\t\tif (typeof key !== \"string\" || descriptor?.enumerable !== true || !(\"value\" in descriptor)) {\n\t\t\t\tthrow new TypeError(\"Symbol, hidden, and accessor properties are not supported data values\");\n\t\t\t}\n\t\t\tassertPlainData(descriptor.value, jsonOnly, ancestors);\n\t\t}\n\t} finally {\n\t\tancestors.delete(value);\n\t}\n}\n\nfunction freezePlainData(value: unknown): void {\n\tif (typeof value !== \"object\" || value === null || Object.isFrozen(value)) return;\n\tfor (const key of Reflect.ownKeys(value)) freezePlainData(Reflect.get(value, key));\n\tObject.freeze(value);\n}\n\n/** Parse a detached mutable JSON-domain value, rejecting every unsupported shape. */\nexport function parseJsonValue<T>(value: T): T {\n\tassertPlainData(value, true);\n\tconst snapshot = structuredClone(value);\n\t// Cloning restores masked intrinsic prototypes (Map, views, buffers, etc.).\n\tassertPlainData(snapshot, true);\n\treturn snapshot;\n}\n\n/** Clone a plain-data payload, then recursively freeze its executor-owned graph. */\nexport function createImmutableSnapshot<T>(value: T): T {\n\tassertPlainData(value, false);\n\tconst snapshot = structuredClone(value);\n\tassertPlainData(snapshot, false);\n\tfreezePlainData(snapshot);\n\treturn snapshot;\n}\n\n/** Parse and freeze a detached JSON-domain value. */\nexport function createImmutableJsonSnapshot<T>(value: T): T {\n\tconst snapshot = parseJsonValue(value);\n\tfreezePlainData(snapshot);\n\treturn snapshot;\n}\n"]}