@llblab/pi-kit 0.18.1 → 0.19.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.
Files changed (139) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +4 -2
  3. package/node_modules/@llblab/pi-state-flow/AGENTS.md +35 -37
  4. package/node_modules/@llblab/pi-state-flow/BACKLOG.md +5 -3
  5. package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +19 -1
  6. package/node_modules/@llblab/pi-state-flow/README.md +102 -48
  7. package/node_modules/@llblab/pi-state-flow/dist/index.d.ts +6 -9
  8. package/node_modules/@llblab/pi-state-flow/dist/index.js +6 -9
  9. package/node_modules/@llblab/pi-state-flow/dist/lib/acquisition.d.ts +2 -2
  10. package/node_modules/@llblab/pi-state-flow/dist/lib/acquisition.js +21 -8
  11. package/node_modules/@llblab/pi-state-flow/dist/lib/artifact.d.ts +40 -24
  12. package/node_modules/@llblab/pi-state-flow/dist/lib/artifact.js +93 -63
  13. package/node_modules/@llblab/pi-state-flow/dist/lib/compaction.d.ts +4 -3
  14. package/node_modules/@llblab/pi-state-flow/dist/lib/compaction.js +9 -9
  15. package/node_modules/@llblab/pi-state-flow/dist/lib/config.d.ts +1 -1
  16. package/node_modules/@llblab/pi-state-flow/dist/lib/config.js +6 -5
  17. package/node_modules/@llblab/pi-state-flow/dist/lib/context.d.ts +8 -7
  18. package/node_modules/@llblab/pi-state-flow/dist/lib/context.js +56 -58
  19. package/node_modules/@llblab/pi-state-flow/dist/lib/continuation.js +12 -24
  20. package/node_modules/@llblab/pi-state-flow/dist/lib/durable.d.ts +7 -18
  21. package/node_modules/@llblab/pi-state-flow/dist/lib/durable.js +47 -78
  22. package/node_modules/@llblab/pi-state-flow/dist/lib/extension.d.ts +4 -6
  23. package/node_modules/@llblab/pi-state-flow/dist/lib/extension.js +241 -437
  24. package/node_modules/@llblab/pi-state-flow/dist/lib/git.d.ts +2 -72
  25. package/node_modules/@llblab/pi-state-flow/dist/lib/git.js +120 -499
  26. package/node_modules/@llblab/pi-state-flow/dist/lib/history.d.ts +2 -1
  27. package/node_modules/@llblab/pi-state-flow/dist/lib/history.js +4 -3
  28. package/node_modules/@llblab/pi-state-flow/dist/lib/json.d.ts +3 -0
  29. package/node_modules/@llblab/pi-state-flow/dist/lib/json.js +43 -22
  30. package/node_modules/@llblab/pi-state-flow/dist/lib/logging.d.ts +1 -5
  31. package/node_modules/@llblab/pi-state-flow/dist/lib/logging.js +0 -2
  32. package/node_modules/@llblab/pi-state-flow/dist/lib/memory.d.ts +1 -14
  33. package/node_modules/@llblab/pi-state-flow/dist/lib/memory.js +5 -37
  34. package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.d.ts +1 -2
  35. package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.js +13 -32
  36. package/node_modules/@llblab/pi-state-flow/dist/lib/query.d.ts +6 -6
  37. package/node_modules/@llblab/pi-state-flow/dist/lib/query.js +25 -21
  38. package/node_modules/@llblab/pi-state-flow/dist/lib/recovery.d.ts +3 -3
  39. package/node_modules/@llblab/pi-state-flow/dist/lib/recovery.js +15 -12
  40. package/node_modules/@llblab/pi-state-flow/dist/lib/rehydration.d.ts +2 -4
  41. package/node_modules/@llblab/pi-state-flow/dist/lib/rehydration.js +5 -1
  42. package/node_modules/@llblab/pi-state-flow/dist/lib/runtime.d.ts +26 -88
  43. package/node_modules/@llblab/pi-state-flow/dist/lib/runtime.js +159 -255
  44. package/node_modules/@llblab/pi-state-flow/dist/lib/skills.d.ts +0 -3
  45. package/node_modules/@llblab/pi-state-flow/dist/lib/skills.js +1 -47
  46. package/node_modules/@llblab/pi-state-flow/dist/lib/snapshot.d.ts +16 -33
  47. package/node_modules/@llblab/pi-state-flow/dist/lib/snapshot.js +48 -137
  48. package/node_modules/@llblab/pi-state-flow/dist/lib/state.d.ts +16 -11
  49. package/node_modules/@llblab/pi-state-flow/dist/lib/state.js +13 -14
  50. package/node_modules/@llblab/pi-state-flow/dist/lib/status.d.ts +1 -9
  51. package/node_modules/@llblab/pi-state-flow/dist/lib/status.js +15 -43
  52. package/node_modules/@llblab/pi-state-flow/dist/lib/storage.d.ts +5 -9
  53. package/node_modules/@llblab/pi-state-flow/dist/lib/storage.js +13 -45
  54. package/node_modules/@llblab/pi-state-flow/dist/lib/telegram.js +1 -1
  55. package/node_modules/@llblab/pi-state-flow/dist/lib/temporal.d.ts +15 -7
  56. package/node_modules/@llblab/pi-state-flow/dist/lib/temporal.js +104 -24
  57. package/node_modules/@llblab/pi-state-flow/dist/lib/transition.d.ts +0 -2
  58. package/node_modules/@llblab/pi-state-flow/dist/lib/transition.js +11 -14
  59. package/node_modules/@llblab/pi-state-flow/dist/package.json +9 -6
  60. package/node_modules/@llblab/pi-state-flow/dist/skills/state-flow-guide/SKILL.md +11 -17
  61. package/node_modules/@llblab/pi-state-flow/dist/skills/state-flow-memory/SKILL.md +8 -8
  62. package/node_modules/@llblab/pi-state-flow/docs/README.md +2 -2
  63. package/node_modules/@llblab/pi-state-flow/docs/architecture.md +64 -67
  64. package/node_modules/@llblab/pi-state-flow/docs/compatibility.md +24 -6
  65. package/node_modules/@llblab/pi-state-flow/docs/filesystem-recovery.md +12 -15
  66. package/node_modules/@llblab/pi-state-flow/docs/fork-contract.md +22 -27
  67. package/node_modules/@llblab/pi-state-flow/docs/lazy-state.md +25 -41
  68. package/node_modules/@llblab/pi-state-flow/docs/performance.md +38 -421
  69. package/node_modules/@llblab/pi-state-flow/docs/temporal-acceptance.md +33 -46
  70. package/node_modules/@llblab/pi-state-flow/docs/usage.md +37 -61
  71. package/node_modules/@llblab/pi-state-flow/index.ts +7 -71
  72. package/node_modules/@llblab/pi-state-flow/lib/acquisition.ts +26 -11
  73. package/node_modules/@llblab/pi-state-flow/lib/artifact.ts +116 -88
  74. package/node_modules/@llblab/pi-state-flow/lib/compaction.ts +12 -10
  75. package/node_modules/@llblab/pi-state-flow/lib/config.ts +6 -6
  76. package/node_modules/@llblab/pi-state-flow/lib/context.ts +52 -59
  77. package/node_modules/@llblab/pi-state-flow/lib/continuation.ts +11 -20
  78. package/node_modules/@llblab/pi-state-flow/lib/durable.ts +44 -86
  79. package/node_modules/@llblab/pi-state-flow/lib/extension.ts +237 -460
  80. package/node_modules/@llblab/pi-state-flow/lib/git.ts +110 -552
  81. package/node_modules/@llblab/pi-state-flow/lib/history.ts +4 -3
  82. package/node_modules/@llblab/pi-state-flow/lib/json.ts +39 -23
  83. package/node_modules/@llblab/pi-state-flow/lib/logging.ts +1 -7
  84. package/node_modules/@llblab/pi-state-flow/lib/memory.ts +5 -44
  85. package/node_modules/@llblab/pi-state-flow/lib/protocol.ts +14 -25
  86. package/node_modules/@llblab/pi-state-flow/lib/query.ts +26 -22
  87. package/node_modules/@llblab/pi-state-flow/lib/recovery.ts +17 -11
  88. package/node_modules/@llblab/pi-state-flow/lib/rehydration.ts +7 -5
  89. package/node_modules/@llblab/pi-state-flow/lib/runtime.ts +154 -254
  90. package/node_modules/@llblab/pi-state-flow/lib/skills.ts +1 -49
  91. package/node_modules/@llblab/pi-state-flow/lib/snapshot.ts +58 -142
  92. package/node_modules/@llblab/pi-state-flow/lib/state.ts +27 -19
  93. package/node_modules/@llblab/pi-state-flow/lib/status.ts +16 -54
  94. package/node_modules/@llblab/pi-state-flow/lib/storage.ts +12 -40
  95. package/node_modules/@llblab/pi-state-flow/lib/telegram.ts +1 -1
  96. package/node_modules/@llblab/pi-state-flow/lib/temporal.ts +104 -22
  97. package/node_modules/@llblab/pi-state-flow/lib/transition.ts +16 -26
  98. package/node_modules/@llblab/pi-state-flow/package.json +9 -6
  99. package/node_modules/@llblab/pi-state-flow/skills/state-flow-guide/SKILL.md +11 -17
  100. package/node_modules/@llblab/pi-state-flow/skills/state-flow-memory/SKILL.md +8 -8
  101. package/node_modules/@llblab/pi-telegram/CHANGELOG.md +7 -0
  102. package/node_modules/@llblab/pi-telegram/README.md +1 -2
  103. package/node_modules/@llblab/pi-telegram/dist/lib/bindings.d.ts +1 -3
  104. package/node_modules/@llblab/pi-telegram/dist/lib/bindings.js +1 -6
  105. package/node_modules/@llblab/pi-telegram/dist/lib/commands.d.ts +1 -4
  106. package/node_modules/@llblab/pi-telegram/dist/lib/commands.js +13 -33
  107. package/node_modules/@llblab/pi-telegram/dist/lib/extension.js +1 -4
  108. package/node_modules/@llblab/pi-telegram/dist/lib/menu-queue.d.ts +1 -0
  109. package/node_modules/@llblab/pi-telegram/dist/lib/menu-queue.js +72 -1
  110. package/node_modules/@llblab/pi-telegram/dist/lib/queue.d.ts +7 -0
  111. package/node_modules/@llblab/pi-telegram/dist/lib/queue.js +27 -0
  112. package/node_modules/@llblab/pi-telegram/dist/lib/replies.d.ts +3 -0
  113. package/node_modules/@llblab/pi-telegram/dist/lib/replies.js +17 -0
  114. package/node_modules/@llblab/pi-telegram/dist/lib/sync.js +8 -1
  115. package/node_modules/@llblab/pi-telegram/dist/package.json +1 -1
  116. package/node_modules/@llblab/pi-telegram/docs/architecture.md +2 -0
  117. package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +1 -1
  118. package/node_modules/@llblab/pi-telegram/docs/public-api.md +1 -1
  119. package/node_modules/@llblab/pi-telegram/lib/bindings.ts +0 -10
  120. package/node_modules/@llblab/pi-telegram/lib/commands.ts +18 -41
  121. package/node_modules/@llblab/pi-telegram/lib/extension.ts +1 -8
  122. package/node_modules/@llblab/pi-telegram/lib/menu-queue.ts +126 -1
  123. package/node_modules/@llblab/pi-telegram/lib/queue.ts +46 -0
  124. package/node_modules/@llblab/pi-telegram/lib/replies.ts +19 -0
  125. package/node_modules/@llblab/pi-telegram/lib/sync.ts +8 -1
  126. package/node_modules/@llblab/pi-telegram/package.json +1 -1
  127. package/package.json +3 -3
  128. package/node_modules/@llblab/pi-state-flow/dist/lib/discovery.d.ts +0 -21
  129. package/node_modules/@llblab/pi-state-flow/dist/lib/discovery.js +0 -125
  130. package/node_modules/@llblab/pi-state-flow/dist/lib/maintenance.d.ts +0 -36
  131. package/node_modules/@llblab/pi-state-flow/dist/lib/maintenance.js +0 -98
  132. package/node_modules/@llblab/pi-state-flow/dist/lib/migration.d.ts +0 -13
  133. package/node_modules/@llblab/pi-state-flow/dist/lib/migration.js +0 -167
  134. package/node_modules/@llblab/pi-state-flow/dist/lib/publication.d.ts +0 -86
  135. package/node_modules/@llblab/pi-state-flow/dist/lib/publication.js +0 -437
  136. package/node_modules/@llblab/pi-state-flow/lib/discovery.ts +0 -133
  137. package/node_modules/@llblab/pi-state-flow/lib/maintenance.ts +0 -147
  138. package/node_modules/@llblab/pi-state-flow/lib/migration.ts +0 -171
  139. package/node_modules/@llblab/pi-state-flow/lib/publication.ts +0 -458
@@ -1,20 +1,17 @@
1
1
  export { ArtifactReadTracker, decideArtifactAcquisition, type ArtifactAcquisitionDecision, type ArtifactAcquisitionIntent, type ArtifactAcquisitionOptions, type ArtifactAcquisitionReason, type SuccessfulArtifactRead } from "./lib/acquisition.ts";
2
- export { classifyArtifactFreshness, compileArtifact, hashArtifactSource, isArtifactHash, isArtifactMetadata, isArtifactRegistry, ORDINARY_ARTIFACT_COMPILER, parseArtifactProvenanceRegistry, planArtifactInvalidation, projectArtifactForModel, projectArtifactsForModel, pruneArtifactProvenance, selectArtifactsByTags, serializeArtifactProvenanceRegistry, updateArtifactProvenance, updateArtifactRegistry, validateArtifactMetadata, validateArtifactRegistry, type ArtifactCompilationUpdate, type ArtifactCompilerOutput, type ArtifactFreshness, type ArtifactInvalidationNotice, type ArtifactInvalidationOptions, type ArtifactInvalidationPlan, type ArtifactInvalidationReason, type ArtifactInvalidationRequest, type ArtifactMetadata, type ArtifactProvenance, type ArtifactProvenanceRegistry, type ArtifactRegistry, type ArtifactSourceIdentity, type CompiledArtifact } from "./lib/artifact.ts";
2
+ export { classifyArtifactCompilationNeed, compileArtifact, hashArtifactSource, isArtifactHash, isArtifactMetadata, isArtifactRegistry, ORDINARY_ARTIFACT_COMPILER, parseArtifactProvenanceRegistry, projectArtifactForModel, projectArtifactsForModel, pruneArtifactProvenance, selectArtifactsByTags, serializeArtifactProvenanceRegistry, updateArtifactProvenance, updateArtifactRegistry, validateArtifactMetadata, validateArtifactRegistry, type ArtifactCompilationUpdate, type ArtifactCompilerOutput, type ArtifactCompilationNeed, type ArtifactInvalidationNotice, type ArtifactInvalidationReason, type ArtifactInvalidationRequest, type ArtifactMetadata, type ArtifactProvenance, type ArtifactProvenanceRegistry, type ArtifactRegistry, type ArtifactSourceIdentity, type CompiledArtifact } from "./lib/artifact.ts";
3
3
  export { buildContinuationCandidates, discoverNativeSessionHeaders, inspectStateFlowContinuationProvenance, readNativeSessionHeader, recommendContinuationFromProvenance, resolveContinuationStartup, type ContinuationCandidateProvenance, type ContinuationCandidateSummary, type ContinuationHostContext, type ContinuationHostIntent, type ContinuationProjectIdentity, type ContinuationRecommendation, type ContinuationRecommender, type ContinuationSessionCandidate, type ContinuationStartupDecision, type ContinuationTransport, type NativeSessionHeader } from "./lib/continuation.ts";
4
- export { discoverGlobalMarkdownSources, getKnowledgeRoot, GlobalMarkdownDiscovery, type ArtifactSourceCandidate, type GlobalMarkdownDiscoveryResult } from "./lib/discovery.ts";
5
4
  export { captureTemporalFileBases, cwdScopeKey, getDurableRepositoryRoot, isStateFlowOwnedPath, loadScopeStream, parseScopeProvenance, parseScopeStream, serializeScopeProvenance, serializeScopeStream, sessionRuntimePaths, sessionScopeKey, sessionStorageKey, temporalScopePaths, temporalStateFileUpdates, type ScopeStreamSources, type TemporalScopePaths } from "./lib/durable.ts";
6
- export { default, MAX_FALLBACK_ATTEMPTS, PATCH_STATE_TOOL_NAME, READ_STATE_TOOL_NAME, type StateFlowExtensionOptions } from "./lib/extension.ts";
7
- export { captureTemporalGitBase, isGitCommitAncestor, loadTemporalRevision, migrateLegacyStorageToGit, publishTemporalStateToGit, pushGitCommit, resolveGitPushDestination, type GitPushResult, type TemporalGitBase, type TemporalRevisionLoad } from "./lib/git.ts";
8
- export { createAcceptedTransition, projectRecentTransitionsWithLimit, RECENT_TRANSITION_LIMIT, validateRecentTransition, type AcceptedTransition, type RecentScopePatch, type RecentTransition, type RecentTransitionWindow } from "./lib/history.ts";
5
+ export { default, PATCH_STATE_TOOL_NAME, READ_STATE_TOOL_NAME, type StateFlowExtensionOptions } from "./lib/extension.ts";
6
+ export { backupCurrentStateFlowFiles } from "./lib/git.ts";
7
+ export { createAcceptedTransition, DEFAULT_HISTORY_LIMIT, MAX_HISTORY_LIMIT, projectRecentTransitionsWithLimit, validateRecentTransition, type AcceptedTransition, type RecentScopePatch, type RecentTransition, type RecentTransitionWindow } from "./lib/history.ts";
9
8
  export { applyPatch, canonicalJson, hashJson, isObject, validatePatch } from "./lib/json.ts";
10
9
  export type { JsonObject, JsonValue } from "./lib/json.ts";
11
10
  export { appendStateFlowDiagnostic, projectDiagnosticContent, stateFlowLogPath, type StateFlowDiagnosticBlock, type StateFlowDiagnosticCategory, type StateFlowDiagnosticRecord } from "./lib/logging.ts";
12
- export { DEFAULT_ARTIFACT_MAINTENANCE_MAX_READS, DEFAULT_ARTIFACT_MAINTENANCE_MAX_SOURCE_BYTES, DEFAULT_ARTIFACT_MAINTENANCE_MINIMUM_AGE_MS, planArtifactMaintenance, type ArtifactMaintenanceOptions, type ArtifactMaintenancePlan, type ArtifactMaintenanceRequest } from "./lib/maintenance.ts";
13
- export { inspectMemoryPromotions, MEMORY_PROMOTION_STATUSES, MEMORY_PROMOTIONS_KEY, retainedMemoryScopes, type MemoryPromotionDiagnostic, type MemoryPromotionStatus } from "./lib/memory.ts";
14
- export { acquirePublicationWorkerLease, beginPublicationAttempt, coalescePublicationTarget, confirmPublicationTarget, createPublicationQueue, failPublicationAttempt, loadPublicationQueue, parsePublicationQueue, parseRemotePublicationPolicyDocument, publicationQueuePath, recoverPublicationQueue, remotePublicationDestinationKey, removePublicationQueue, resolveRemotePublicationPolicy, runPublicationWorker, savePublicationQueue, serializePublicationQueue, serializeRemotePublicationPolicyDocument, validatePublicationQueue, type CommitAncestor, type PublicationPush, type PublicationQueueReceipt, type PublicationQueueState, type PublicationQueueStatus, type PublicationWorkerLease, type PublicationWorkerResult, type RemotePublicationDestination, type RemotePublicationMode, type RemotePublicationPolicy, type RemotePublicationPolicyDocument } from "./lib/publication.ts";
11
+ export { retainedMemoryScopes } from "./lib/memory.ts";
15
12
  export { planKnowledgeRehydration, type RehydrationOptions, type RehydrationPhase, type RehydrationPlan, type RehydrationRead, type RehydrationRoute } from "./lib/rehydration.ts";
16
13
  export { TemporalRuntime } from "./lib/runtime.ts";
17
- export { hasCompiledSkillArtifact, hashSkillSource, migrateLegacySkillCompilations, SKILL_ARTIFACT_COMPILER, type SuccessfulSkillRead } from "./lib/skills.ts";
14
+ export { hasCompiledSkillArtifact, hashSkillSource, SKILL_ARTIFACT_COMPILER, type SuccessfulSkillRead } from "./lib/skills.ts";
18
15
  export { emptyState, isMaterializedState, isStateDocument, overlayStates, projectStateForModel, updateMaterializedArtifacts, type AtomicScopePatches, type MaterializedState, type ScopedPatch, type ScopedStates, type ScopePatch, type SemanticTransition, type StateDocument, type StatePatch, type StateScope, type TerminalTransition } from "./lib/state.ts";
19
16
  export { buildStateFlowSectionView, createStateFlowTelegramAdapter, formatStateFlowSectionLabel, loadStateFlowTelegramModules, STATE_FLOW_TELEGRAM_ID, type StateFlowTelegramAdapter, type StateFlowTelegramButton, type StateFlowTelegramCallbackContext, type StateFlowTelegramControlResult, type StateFlowTelegramLoader, type StateFlowTelegramModules, type StateFlowTelegramPort, type StateFlowTelegramSectionContext, type StateFlowTelegramSectionModule, type StateFlowTelegramSnapshot, type StateFlowTelegramView } from "./lib/telegram.ts";
20
17
  export { advanceTemporalState, readTemporalState, type TemporalState } from "./lib/temporal.ts";
@@ -1,19 +1,16 @@
1
1
  export { ArtifactReadTracker, decideArtifactAcquisition } from "./lib/acquisition.js";
2
- export { classifyArtifactFreshness, compileArtifact, hashArtifactSource, isArtifactHash, isArtifactMetadata, isArtifactRegistry, ORDINARY_ARTIFACT_COMPILER, parseArtifactProvenanceRegistry, planArtifactInvalidation, projectArtifactForModel, projectArtifactsForModel, pruneArtifactProvenance, selectArtifactsByTags, serializeArtifactProvenanceRegistry, updateArtifactProvenance, updateArtifactRegistry, validateArtifactMetadata, validateArtifactRegistry } from "./lib/artifact.js";
2
+ export { classifyArtifactCompilationNeed, compileArtifact, hashArtifactSource, isArtifactHash, isArtifactMetadata, isArtifactRegistry, ORDINARY_ARTIFACT_COMPILER, parseArtifactProvenanceRegistry, projectArtifactForModel, projectArtifactsForModel, pruneArtifactProvenance, selectArtifactsByTags, serializeArtifactProvenanceRegistry, updateArtifactProvenance, updateArtifactRegistry, validateArtifactMetadata, validateArtifactRegistry } from "./lib/artifact.js";
3
3
  export { buildContinuationCandidates, discoverNativeSessionHeaders, inspectStateFlowContinuationProvenance, readNativeSessionHeader, recommendContinuationFromProvenance, resolveContinuationStartup } from "./lib/continuation.js";
4
- export { discoverGlobalMarkdownSources, getKnowledgeRoot, GlobalMarkdownDiscovery } from "./lib/discovery.js";
5
4
  export { captureTemporalFileBases, cwdScopeKey, getDurableRepositoryRoot, isStateFlowOwnedPath, loadScopeStream, parseScopeProvenance, parseScopeStream, serializeScopeProvenance, serializeScopeStream, sessionRuntimePaths, sessionScopeKey, sessionStorageKey, temporalScopePaths, temporalStateFileUpdates } from "./lib/durable.js";
6
- export { default, MAX_FALLBACK_ATTEMPTS, PATCH_STATE_TOOL_NAME, READ_STATE_TOOL_NAME } from "./lib/extension.js";
7
- export { captureTemporalGitBase, isGitCommitAncestor, loadTemporalRevision, migrateLegacyStorageToGit, publishTemporalStateToGit, pushGitCommit, resolveGitPushDestination } from "./lib/git.js";
8
- export { createAcceptedTransition, projectRecentTransitionsWithLimit, RECENT_TRANSITION_LIMIT, validateRecentTransition } from "./lib/history.js";
5
+ export { default, PATCH_STATE_TOOL_NAME, READ_STATE_TOOL_NAME } from "./lib/extension.js";
6
+ export { backupCurrentStateFlowFiles } from "./lib/git.js";
7
+ export { createAcceptedTransition, DEFAULT_HISTORY_LIMIT, MAX_HISTORY_LIMIT, projectRecentTransitionsWithLimit, validateRecentTransition } from "./lib/history.js";
9
8
  export { applyPatch, canonicalJson, hashJson, isObject, validatePatch } from "./lib/json.js";
10
9
  export { appendStateFlowDiagnostic, projectDiagnosticContent, stateFlowLogPath } from "./lib/logging.js";
11
- export { DEFAULT_ARTIFACT_MAINTENANCE_MAX_READS, DEFAULT_ARTIFACT_MAINTENANCE_MAX_SOURCE_BYTES, DEFAULT_ARTIFACT_MAINTENANCE_MINIMUM_AGE_MS, planArtifactMaintenance } from "./lib/maintenance.js";
12
- export { inspectMemoryPromotions, MEMORY_PROMOTION_STATUSES, MEMORY_PROMOTIONS_KEY, retainedMemoryScopes } from "./lib/memory.js";
13
- export { acquirePublicationWorkerLease, beginPublicationAttempt, coalescePublicationTarget, confirmPublicationTarget, createPublicationQueue, failPublicationAttempt, loadPublicationQueue, parsePublicationQueue, parseRemotePublicationPolicyDocument, publicationQueuePath, recoverPublicationQueue, remotePublicationDestinationKey, removePublicationQueue, resolveRemotePublicationPolicy, runPublicationWorker, savePublicationQueue, serializePublicationQueue, serializeRemotePublicationPolicyDocument, validatePublicationQueue } from "./lib/publication.js";
10
+ export { retainedMemoryScopes } from "./lib/memory.js";
14
11
  export { planKnowledgeRehydration } from "./lib/rehydration.js";
15
12
  export { TemporalRuntime } from "./lib/runtime.js";
16
- export { hasCompiledSkillArtifact, hashSkillSource, migrateLegacySkillCompilations, SKILL_ARTIFACT_COMPILER } from "./lib/skills.js";
13
+ export { hasCompiledSkillArtifact, hashSkillSource, SKILL_ARTIFACT_COMPILER } from "./lib/skills.js";
17
14
  export { emptyState, isMaterializedState, isStateDocument, overlayStates, projectStateForModel, updateMaterializedArtifacts } from "./lib/state.js";
18
15
  export { buildStateFlowSectionView, createStateFlowTelegramAdapter, formatStateFlowSectionLabel, loadStateFlowTelegramModules, STATE_FLOW_TELEGRAM_ID } from "./lib/telegram.js";
19
16
  export { advanceTemporalState, readTemporalState } from "./lib/temporal.js";
@@ -14,7 +14,7 @@ export interface ArtifactAcquisitionOptions {
14
14
  /** Caller-assessed semantic sufficiency; only relevant to a concrete relevant gap. */
15
15
  materializedSufficient?: boolean;
16
16
  explicitRefresh?: boolean;
17
- /** Runtime-owned freshness evidence retained beside the semantic artifact. */
17
+ /** Runtime-owned compilation evidence retained beside the semantic artifact. */
18
18
  provenance?: unknown;
19
19
  }
20
20
  /** A successful read correlated to a runtime-observed invalidation candidate. */
@@ -33,7 +33,7 @@ export declare class ArtifactReadTracker {
33
33
  /**
34
34
  * Apply one materialized-first source acquisition policy.
35
35
  *
36
- * Freshness invalidation always wins. Otherwise routine use and a new session
36
+ * Required recompilation always wins. Otherwise routine use and a new session
37
37
  * stay on materialized state; only a concrete source need permits rereading.
38
38
  */
39
39
  export declare function decideArtifactAcquisition(source: ArtifactSourceIdentity, metadata: unknown, compiler: string, options: ArtifactAcquisitionOptions): ArtifactAcquisitionDecision;
@@ -1,4 +1,4 @@
1
- import { classifyArtifactFreshness, } from "./artifact.js";
1
+ import { classifyArtifactCompilationNeed, inspectRegisteredArtifactPaths, sameArtifactSourceFingerprint, } from "./artifact.js";
2
2
  import { isObject } from "./json.js";
3
3
  function readPath(toolName, args) {
4
4
  return toolName === "read" && isObject(args) && typeof args.path === "string"
@@ -34,27 +34,40 @@ export class ArtifactReadTracker {
34
34
  if (path === undefined)
35
35
  return;
36
36
  const candidate = this.#candidates.get(path);
37
- if (candidate !== undefined)
38
- this.successful.set(path, structuredClone(candidate));
37
+ if (candidate === undefined || pending.fingerprint === undefined)
38
+ return;
39
+ const observation = inspectRegisteredArtifactPaths([path])[0];
40
+ if (observation?.kind !== "present" || !sameArtifactSourceFingerprint(pending.fingerprint, observation.fingerprint))
41
+ return;
42
+ const finalObservation = inspectRegisteredArtifactPaths([path])[0];
43
+ if (finalObservation?.kind !== "present" || !sameArtifactSourceFingerprint(observation.fingerprint, finalObservation.fingerprint))
44
+ return;
45
+ this.successful.set(path, structuredClone({ ...candidate, sourceFingerprint: finalObservation.fingerprint }));
39
46
  }
40
47
  #record(toolCallId, toolName, args) {
41
48
  if (toolName !== "read") {
42
49
  this.#pending.delete(toolCallId);
43
50
  return;
44
51
  }
45
- this.#pending.set(toolCallId, { toolName, args });
52
+ const path = readPath(toolName, args);
53
+ const observation = path !== undefined && this.#candidates.has(path) ? inspectRegisteredArtifactPaths([path])[0] : undefined;
54
+ this.#pending.set(toolCallId, {
55
+ toolName,
56
+ args,
57
+ ...(observation?.kind === "present" ? { fingerprint: observation.fingerprint } : {}),
58
+ });
46
59
  }
47
60
  }
48
61
  /**
49
62
  * Apply one materialized-first source acquisition policy.
50
63
  *
51
- * Freshness invalidation always wins. Otherwise routine use and a new session
64
+ * Required recompilation always wins. Otherwise routine use and a new session
52
65
  * stay on materialized state; only a concrete source need permits rereading.
53
66
  */
54
67
  export function decideArtifactAcquisition(source, metadata, compiler, options) {
55
- const freshness = classifyArtifactFreshness(source, metadata, compiler, options.explicitRefresh ?? false, options.provenance);
56
- if (freshness.kind === "requires-compilation") {
57
- return { kind: "read-source", reason: freshness.reason };
68
+ const need = classifyArtifactCompilationNeed(source, metadata, compiler, options.explicitRefresh ?? false, options.provenance);
69
+ if (need.kind === "requires-compilation") {
70
+ return { kind: "read-source", reason: need.reason };
58
71
  }
59
72
  switch (options.intent) {
60
73
  case "routine":
@@ -1,10 +1,31 @@
1
1
  import { type JsonObject } from "./json.ts";
2
- /** Current compiler protocol for ordinary source artifacts such as Knowledge Markdown. */
2
+ export interface ArtifactSourceFingerprint {
3
+ size: number;
4
+ mtimeNs: string;
5
+ }
6
+ export declare function parseArtifactSourceFingerprint(value: unknown): ArtifactSourceFingerprint | undefined;
7
+ export declare function sameArtifactSourceFingerprint(left: ArtifactSourceFingerprint, right: ArtifactSourceFingerprint): boolean;
8
+ export type ArtifactSourceObservation = {
9
+ path: string;
10
+ kind: "present";
11
+ fingerprint: ArtifactSourceFingerprint;
12
+ } | {
13
+ path: string;
14
+ kind: "missing";
15
+ } | {
16
+ path: string;
17
+ kind: "unavailable";
18
+ reason: string;
19
+ };
20
+ /** Inspect only exact registered artifact paths without reading source bodies or traversing directories. */
21
+ export declare function inspectRegisteredArtifactPaths(paths: Iterable<string>): ArtifactSourceObservation[];
22
+ /** Current compiler protocol for ordinary registered source artifacts. */
3
23
  export declare const ORDINARY_ARTIFACT_COMPILER = "artifact-v1";
4
- /** Freshness fields that runtime-owned scope metadata may retain per artifact path. */
24
+ /** Compilation evidence that runtime-owned scope metadata may retain per artifact path. */
5
25
  export interface ArtifactProvenance {
6
26
  /** Retained value; absent means unavailable, malformed means fail closed. */
7
27
  sourceHash?: unknown;
28
+ sourceFingerprint?: unknown;
8
29
  compilerRevision?: unknown;
9
30
  compiledAt?: unknown;
10
31
  /** Internal marker for an uninterpretable retained entry; every dependent capability fails closed. */
@@ -17,7 +38,7 @@ export type ArtifactProvenanceRegistry = Record<string, ArtifactProvenance>;
17
38
  * `hash`, `compiler`, and `compiled_at` remain accepted only as retired embedded
18
39
  * provenance input from pre-0.7 state; they are consumed as compatibility evidence
19
40
  * and stripped from model projection. New compilations keep semantic fields here and
20
- * runtime-owned freshness in the scope `meta.json` provenance registry.
41
+ * runtime-owned compilation evidence in the scope `meta.json` provenance registry.
21
42
  */
22
43
  export type ArtifactMetadata = JsonObject & {
23
44
  description: string;
@@ -30,14 +51,20 @@ export type ArtifactMetadata = JsonObject & {
30
51
  };
31
52
  /** Artifact source paths are the canonical registry keys. */
32
53
  export type ArtifactRegistry = JsonObject & Record<string, ArtifactMetadata>;
33
- /** Source identity is sufficient for freshness decisions; it never contains the source body. */
54
+ /** Source identity is sufficient for compilation decisions; it never contains the source body. */
55
+ export type ArtifactScope = "global" | "cwd" | "session";
34
56
  export interface ArtifactSourceIdentity {
35
57
  path: string;
36
- hash: string;
58
+ /** Exact semantic owner selected from the effective global → CWD → session overlay. */
59
+ scope?: ArtifactScope;
60
+ /** Transitional legacy content identity; generic maintenance uses the filesystem fingerprint instead. */
61
+ hash?: string;
62
+ /** Stable filesystem evidence observed across the successful source read. */
63
+ sourceFingerprint?: ArtifactSourceFingerprint;
37
64
  }
38
65
  export type ArtifactInvalidationReason = "new" | "source-changed" | "compiler-changed" | "invalid-metadata" | "explicit-refresh";
39
- export type ArtifactFreshness = {
40
- kind: "fresh";
66
+ export type ArtifactCompilationNeed = {
67
+ kind: "current";
41
68
  } | {
42
69
  kind: "requires-compilation";
43
70
  reason: ArtifactInvalidationReason;
@@ -48,19 +75,9 @@ export interface ArtifactInvalidationRequest extends ArtifactSourceIdentity {
48
75
  /** Model-visible invalidation projection: paths and reasons only, never source identity. */
49
76
  export interface ArtifactInvalidationNotice {
50
77
  path: string;
78
+ scope?: ArtifactScope;
51
79
  reason: ArtifactInvalidationReason;
52
80
  }
53
- export interface ArtifactInvalidationPlan {
54
- fresh: ArtifactSourceIdentity[];
55
- requiresCompilation: ArtifactInvalidationRequest[];
56
- removed: string[];
57
- }
58
- export interface ArtifactInvalidationOptions {
59
- /** Refresh every source, or only paths in the supplied set. */
60
- explicitRefresh?: boolean | ReadonlySet<string>;
61
- /** Explicit absence evidence supplied by the source owner, not inferred from a partial candidate set. */
62
- removed?: readonly string[];
63
- }
64
81
  /** Trusted compilation input; embedded timestamps are accepted here, but never in model patches. */
65
82
  export type ArtifactCompilerOutput = JsonObject & {
66
83
  description: string;
@@ -90,10 +107,8 @@ export declare function isArtifactRegistry(value: unknown): value is ArtifactReg
90
107
  export declare function parseArtifactProvenanceRegistry(value: unknown, context?: string): ArtifactProvenanceRegistry;
91
108
  /** Canonical retained form; uninterpretable entries cannot round-trip and are omitted. */
92
109
  export declare function serializeArtifactProvenanceRegistry(registry: Readonly<ArtifactProvenanceRegistry>): JsonObject;
93
- /** Classify freshness from semantic state and runtime provenance without acquiring the source body. */
94
- export declare function classifyArtifactFreshness(source: ArtifactSourceIdentity, metadata: unknown, compiler: string, explicitRefresh?: boolean, provenance?: unknown): ArtifactFreshness;
95
- /** Produce a deterministic acquisition plan from path/hash candidates and retained evidence. */
96
- export declare function planArtifactInvalidation(sources: readonly ArtifactSourceIdentity[], registry: Readonly<Record<string, unknown>>, compiler: string, options?: ArtifactInvalidationOptions, provenance?: Readonly<ArtifactProvenanceRegistry>): ArtifactInvalidationPlan;
110
+ /** Decide whether retained compilation evidence requires source reacquisition. */
111
+ export declare function classifyArtifactCompilationNeed(source: ArtifactSourceIdentity, metadata: unknown, compiler: string, explicitRefresh?: boolean, provenance?: unknown): ArtifactCompilationNeed;
97
112
  /** Split one compiler output into model-visible semantics and runtime-owned provenance. */
98
113
  export declare function compileArtifact(update: ArtifactCompilationUpdate): CompiledArtifact;
99
114
  /** Merge one compilation/removal cohort into the runtime-owned provenance registry. */
@@ -106,5 +121,6 @@ export declare function updateArtifactRegistry(registry: ArtifactRegistry, updat
106
121
  export declare function validateModelArtifactPatch(patch: JsonObject): void;
107
122
  /** Strip retained runtime bookkeeping from one model-visible artifact entry. */
108
123
  export declare function projectArtifactForModel(entry: unknown): unknown;
109
- /** Strip retained runtime bookkeeping from a model-visible artifact registry. */
110
- export declare function projectArtifactsForModel(registry: Readonly<ArtifactRegistry>): ArtifactRegistry;
124
+ export type ArtifactModelHints = Readonly<Record<string, string | readonly string[]>>;
125
+ /** Strip retained runtime bookkeeping and add deterministic runtime-only guidance. */
126
+ export declare function projectArtifactsForModel(registry: Readonly<ArtifactRegistry>, hints?: ArtifactModelHints): ArtifactRegistry;
@@ -1,8 +1,57 @@
1
1
  import { createHash } from "node:crypto";
2
+ import { lstatSync } from "node:fs";
3
+ import { isAbsolute, resolve } from "node:path";
2
4
  import { containsNull, isJsonValue, isObject } from "./json.js";
3
5
  const SHA256_PATTERN = /^sha256:[0-9a-f]{64}$/;
4
- const MODEL_FORBIDDEN_PROVENANCE_FIELDS = ["hash", "compiler", "sourceHash", "compilerRevision", "compiledAt", "source_hash_verified"];
5
- /** Current compiler protocol for ordinary source artifacts such as Knowledge Markdown. */
6
+ const MODEL_FORBIDDEN_PROVENANCE_FIELDS = ["hash", "compiler", "sourceHash", "sourceFingerprint", "compilerRevision", "compiledAt", "source_hash_verified"];
7
+ export function parseArtifactSourceFingerprint(value) {
8
+ if (typeof value !== "object" || value === null || Array.isArray(value))
9
+ return undefined;
10
+ const candidate = value;
11
+ if (Object.keys(candidate).sort().join(",") !== "mtimeNs,size"
12
+ || !Number.isSafeInteger(candidate.size) || candidate.size < 0
13
+ || typeof candidate.mtimeNs !== "string" || !/^-?\d+$/.test(candidate.mtimeNs))
14
+ return undefined;
15
+ return { size: candidate.size, mtimeNs: candidate.mtimeNs };
16
+ }
17
+ export function sameArtifactSourceFingerprint(left, right) {
18
+ return left.size === right.size && left.mtimeNs === right.mtimeNs;
19
+ }
20
+ function artifactSourceErrorCode(error) {
21
+ return error instanceof Error && "code" in error
22
+ ? error.code
23
+ : undefined;
24
+ }
25
+ /** Inspect only exact registered artifact paths without reading source bodies or traversing directories. */
26
+ export function inspectRegisteredArtifactPaths(paths) {
27
+ return [...new Set(paths)].sort().map((path) => {
28
+ if (!isAbsolute(path) || resolve(path) !== path) {
29
+ return { path, kind: "unavailable", reason: "artifact path is not canonical and absolute" };
30
+ }
31
+ let metadata;
32
+ try {
33
+ metadata = lstatSync(path, { bigint: true });
34
+ }
35
+ catch (error) {
36
+ const code = artifactSourceErrorCode(error);
37
+ if (code === "ENOENT" || code === "ENOTDIR")
38
+ return { path, kind: "missing" };
39
+ return { path, kind: "unavailable", reason: error instanceof Error ? error.message : String(error) };
40
+ }
41
+ if (!metadata.isFile() || metadata.isSymbolicLink()) {
42
+ return { path, kind: "unavailable", reason: "artifact source is not a regular non-symlink file" };
43
+ }
44
+ if (metadata.size > BigInt(Number.MAX_SAFE_INTEGER)) {
45
+ return { path, kind: "unavailable", reason: "artifact source size exceeds the supported range" };
46
+ }
47
+ return {
48
+ path,
49
+ kind: "present",
50
+ fingerprint: { size: Number(metadata.size), mtimeNs: metadata.mtimeNs.toString() },
51
+ };
52
+ });
53
+ }
54
+ /** Current compiler protocol for ordinary registered source artifacts. */
6
55
  export const ORDINARY_ARTIFACT_COMPILER = "artifact-v1";
7
56
  export function hashArtifactSource(source) {
8
57
  return `sha256:${createHash("sha256").update(source).digest("hex")}`;
@@ -88,9 +137,12 @@ function validateSourceIdentity(source) {
88
137
  if (typeof source.path !== "string" || source.path.trim().length === 0) {
89
138
  throw new Error("Artifact source path must be non-empty");
90
139
  }
91
- if (!isArtifactHash(source.hash)) {
140
+ if (source.hash !== undefined && !isArtifactHash(source.hash)) {
92
141
  throw new Error(`Artifact source at ${source.path} must have a sha256:<64 lowercase hex characters> hash`);
93
142
  }
143
+ if (source.sourceFingerprint !== undefined && parseArtifactSourceFingerprint(source.sourceFingerprint) === undefined) {
144
+ throw new Error(`Artifact source fingerprint at ${source.path} is invalid`);
145
+ }
94
146
  }
95
147
  function validateCompilerRevision(compiler, path) {
96
148
  if (typeof compiler !== "string" || compiler.trim().length === 0) {
@@ -99,13 +151,6 @@ function validateCompilerRevision(compiler, path) {
99
151
  : `Artifact compiler revision at ${path} must be non-empty`);
100
152
  }
101
153
  }
102
- function refreshRequested(path, explicitRefresh) {
103
- if (explicitRefresh === true)
104
- return true;
105
- if (!explicitRefresh || typeof explicitRefresh !== "object" || typeof explicitRefresh.has !== "function")
106
- return false;
107
- return explicitRefresh.has(path);
108
- }
109
154
  function isProvenanceEntry(value) {
110
155
  return isObject(value) && (value.malformed === undefined || value.malformed === true);
111
156
  }
@@ -123,13 +168,14 @@ export function parseArtifactProvenanceRegistry(value, context = "Artifact prove
123
168
  registry[path] = { malformed: true };
124
169
  continue;
125
170
  }
126
- const known = new Set(["sourceHash", "compilerRevision", "compiledAt"]);
171
+ const known = new Set(["sourceHash", "sourceFingerprint", "compilerRevision", "compiledAt"]);
127
172
  if (Object.keys(entry).some((key) => !known.has(key))) {
128
173
  registry[path] = { malformed: true };
129
174
  continue;
130
175
  }
131
176
  registry[path] = {
132
177
  ...(Object.hasOwn(entry, "sourceHash") ? { sourceHash: entry.sourceHash } : {}),
178
+ ...(Object.hasOwn(entry, "sourceFingerprint") ? { sourceFingerprint: entry.sourceFingerprint } : {}),
133
179
  ...(Object.hasOwn(entry, "compilerRevision") ? { compilerRevision: entry.compilerRevision } : {}),
134
180
  ...(Object.hasOwn(entry, "compiledAt") ? { compiledAt: entry.compiledAt } : {}),
135
181
  };
@@ -145,6 +191,8 @@ export function serializeArtifactProvenanceRegistry(registry) {
145
191
  const fields = {};
146
192
  if (entry.sourceHash !== undefined)
147
193
  fields.sourceHash = entry.sourceHash;
194
+ if (entry.sourceFingerprint !== undefined)
195
+ fields.sourceFingerprint = entry.sourceFingerprint;
148
196
  if (entry.compilerRevision !== undefined)
149
197
  fields.compilerRevision = entry.compilerRevision;
150
198
  if (entry.compiledAt !== undefined)
@@ -158,17 +206,14 @@ export function serializeArtifactProvenanceRegistry(registry) {
158
206
  function invalidField(value, validate) {
159
207
  return value !== undefined && !validate(value);
160
208
  }
161
- const INVALID_EVIDENCE = Symbol("invalid-evidence");
162
209
  /** Later authority wins per field; absent runtime evidence falls back to retired embedded values. */
163
210
  function fieldEvidence(entry, field, legacy) {
164
- if (entry?.malformed === true)
165
- return INVALID_EVIDENCE;
166
211
  if (entry !== undefined && Object.hasOwn(entry, field))
167
212
  return entry[field];
168
213
  return legacy;
169
214
  }
170
- /** Classify freshness from semantic state and runtime provenance without acquiring the source body. */
171
- export function classifyArtifactFreshness(source, metadata, compiler, explicitRefresh = false, provenance) {
215
+ /** Decide whether retained compilation evidence requires source reacquisition. */
216
+ export function classifyArtifactCompilationNeed(source, metadata, compiler, explicitRefresh = false, provenance) {
172
217
  validateSourceIdentity(source);
173
218
  validateCompilerRevision(compiler);
174
219
  if (metadata === undefined)
@@ -176,16 +221,27 @@ export function classifyArtifactFreshness(source, metadata, compiler, explicitRe
176
221
  if (!isArtifactMetadata(metadata))
177
222
  return { kind: "requires-compilation", reason: "invalid-metadata" };
178
223
  const entry = isProvenanceEntry(provenance) ? provenance : undefined;
179
- const runtime = entry?.malformed === true;
180
- const sourceHash = runtime ? INVALID_EVIDENCE : fieldEvidence(entry, "sourceHash", metadata.hash);
181
- if (sourceHash === INVALID_EVIDENCE || invalidField(sourceHash, (value) => isArtifactHash(value))) {
224
+ if ((provenance !== undefined && entry === undefined) || entry?.malformed === true) {
182
225
  return { kind: "requires-compilation", reason: "invalid-metadata" };
183
226
  }
184
- if (typeof sourceHash === "string" && sourceHash !== source.hash) {
185
- return { kind: "requires-compilation", reason: "source-changed" };
227
+ if (source.sourceFingerprint !== undefined) {
228
+ const retained = parseArtifactSourceFingerprint(entry?.sourceFingerprint);
229
+ if (retained === undefined)
230
+ return { kind: "requires-compilation", reason: "invalid-metadata" };
231
+ if (!sameArtifactSourceFingerprint(source.sourceFingerprint, retained))
232
+ return { kind: "requires-compilation", reason: "source-changed" };
233
+ }
234
+ if (source.sourceFingerprint === undefined || source.hash !== undefined) {
235
+ // Explicit current-hash observations, including Skills, retain their identity contract.
236
+ const sourceHash = fieldEvidence(entry, "sourceHash", metadata.hash);
237
+ if (invalidField(sourceHash, isArtifactHash))
238
+ return { kind: "requires-compilation", reason: "invalid-metadata" };
239
+ if (source.hash !== undefined && typeof sourceHash === "string" && sourceHash !== source.hash) {
240
+ return { kind: "requires-compilation", reason: "source-changed" };
241
+ }
186
242
  }
187
- const compilerRevision = runtime ? INVALID_EVIDENCE : fieldEvidence(entry, "compilerRevision", metadata.compiler);
188
- if (compilerRevision === INVALID_EVIDENCE || invalidField(compilerRevision, (value) => typeof value === "string" && value.trim().length > 0)) {
243
+ const compilerRevision = fieldEvidence(entry, "compilerRevision", metadata.compiler);
244
+ if (invalidField(compilerRevision, (value) => typeof value === "string" && value.trim().length > 0)) {
189
245
  return { kind: "requires-compilation", reason: "invalid-metadata" };
190
246
  }
191
247
  if (typeof compilerRevision === "string" && compilerRevision !== compiler) {
@@ -193,40 +249,7 @@ export function classifyArtifactFreshness(source, metadata, compiler, explicitRe
193
249
  }
194
250
  if (explicitRefresh)
195
251
  return { kind: "requires-compilation", reason: "explicit-refresh" };
196
- return { kind: "fresh" };
197
- }
198
- /** Produce a deterministic acquisition plan from path/hash candidates and retained evidence. */
199
- export function planArtifactInvalidation(sources, registry, compiler, options = {}, provenance = {}) {
200
- if (!isObject(registry))
201
- throw new Error("Artifacts must be a path-keyed JSON object");
202
- validateCompilerRevision(compiler);
203
- const ordered = [...sources].sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0);
204
- const seen = new Set();
205
- const fresh = [];
206
- const requiresCompilation = [];
207
- for (const source of ordered) {
208
- if (seen.has(source.path))
209
- throw new Error(`Duplicate artifact source path: ${source.path}`);
210
- seen.add(source.path);
211
- const metadata = Object.hasOwn(registry, source.path) ? registry[source.path] : undefined;
212
- const entry = Object.hasOwn(provenance, source.path) ? provenance[source.path] : undefined;
213
- const freshness = classifyArtifactFreshness(source, metadata, compiler, refreshRequested(source.path, options.explicitRefresh), entry);
214
- const identity = { path: source.path, hash: source.hash };
215
- if (freshness.kind === "fresh")
216
- fresh.push(identity);
217
- else
218
- requiresCompilation.push({ ...identity, reason: freshness.reason });
219
- }
220
- if (options.removed !== undefined && !Array.isArray(options.removed))
221
- throw new Error("Removed artifact paths must be an array");
222
- const removed = [...new Set(options.removed ?? [])];
223
- for (const path of removed) {
224
- if (typeof path !== "string" || path.trim().length === 0)
225
- throw new Error("Removed artifact paths must be non-empty");
226
- if (seen.has(path))
227
- throw new Error(`Artifact source cannot be both present and removed: ${path}`);
228
- }
229
- return { fresh, requiresCompilation, removed: removed.filter((path) => Object.hasOwn(registry, path)).sort() };
252
+ return { kind: "current" };
230
253
  }
231
254
  /** Split one compiler output into model-visible semantics and runtime-owned provenance. */
232
255
  export function compileArtifact(update) {
@@ -242,7 +265,8 @@ export function compileArtifact(update) {
242
265
  return {
243
266
  semantic,
244
267
  provenance: {
245
- sourceHash: update.source.hash,
268
+ ...(update.source.hash === undefined ? {} : { sourceHash: update.source.hash }),
269
+ ...(update.source.sourceFingerprint === undefined ? {} : { sourceFingerprint: structuredClone(update.source.sourceFingerprint) }),
246
270
  compilerRevision: update.compiler,
247
271
  ...(typeof update.output.compiled_at === "string" ? { compiledAt: update.output.compiled_at } : {}),
248
272
  },
@@ -299,7 +323,7 @@ export function updateArtifactRegistry(registry, updates, removed = []) {
299
323
  return next;
300
324
  }
301
325
  /** Runtime-owned artifact fields that never belong in ordinary model context. */
302
- const RUNTIME_ARTIFACT_FIELDS = [...MODEL_FORBIDDEN_PROVENANCE_FIELDS, "compiled_at"];
326
+ const RUNTIME_ARTIFACT_FIELDS = [...MODEL_FORBIDDEN_PROVENANCE_FIELDS, "compiled_at", "hint"];
303
327
  /** Validate authored fields only: legacy retained evidence stays readable but cannot be model-edited. */
304
328
  export function validateModelArtifactPatch(patch) {
305
329
  for (const [path, entry] of Object.entries(patch)) {
@@ -307,7 +331,7 @@ export function validateModelArtifactPatch(patch) {
307
331
  continue; // Whole-artifact deletion and materialized shape belong to the transition owner.
308
332
  const field = RUNTIME_ARTIFACT_FIELDS.find((field) => Object.hasOwn(entry, field));
309
333
  if (field !== undefined)
310
- throw new Error(`Artifact patch at ${path} cannot set runtime-owned provenance field ${field}`);
334
+ throw new Error(`Artifact patch at ${path} cannot set runtime-owned field ${field}`);
311
335
  }
312
336
  }
313
337
  /** Strip retained runtime bookkeeping from one model-visible artifact entry. */
@@ -319,12 +343,18 @@ export function projectArtifactForModel(entry) {
319
343
  delete projected[field];
320
344
  return projected;
321
345
  }
322
- /** Strip retained runtime bookkeeping from a model-visible artifact registry. */
323
- export function projectArtifactsForModel(registry) {
346
+ /** Strip retained runtime bookkeeping and add deterministic runtime-only guidance. */
347
+ export function projectArtifactsForModel(registry, hints = {}) {
324
348
  const projected = {};
325
349
  for (const [path, entry] of Object.entries(registry)) {
350
+ const modelEntry = projectArtifactForModel(entry);
351
+ const values = (Array.isArray(hints[path]) ? hints[path] : [hints[path]])
352
+ .filter((value) => typeof value === "string" && value.trim().length > 0);
353
+ const hint = [...new Set(values)].sort().join("\n");
354
+ if (hint.length > 0 && isObject(modelEntry))
355
+ modelEntry.hint = hint;
326
356
  Object.defineProperty(projected, path, {
327
- value: projectArtifactForModel(entry),
357
+ value: modelEntry,
328
358
  enumerable: true,
329
359
  configurable: true,
330
360
  writable: true,
@@ -1,11 +1,11 @@
1
1
  import type { CompactionResult } from "@earendil-works/pi-coding-agent";
2
- export declare const STATE_FLOW_COMPACTION_SUMMARY = "State Flow accepted the completed work before this boundary. Current memory is restored from its durable revision and projected separately; use the retained native entries for subsequent work.";
2
+ export declare const STATE_FLOW_COMPACTION_SUMMARY = "State Flow accepted the completed work before this boundary. Current memory is restored from its retained semantic boundary and projected separately; use the retained native entries for subsequent work.";
3
3
  /** A modest margin above Pi's default 20k retained suffix absorbs estimation drift. */
4
4
  export declare const STATE_FLOW_COMPACTION_MIN_CONTEXT_TOKENS = 24000;
5
5
  export interface StateFlowCompactionDetails {
6
6
  version: 1;
7
7
  owner: "state-flow";
8
- revision: string;
8
+ boundary: string;
9
9
  step: number;
10
10
  }
11
11
  export interface StateFlowCompactionPlan {
@@ -21,6 +21,7 @@ type ActiveEntry = {
21
21
  role?: unknown;
22
22
  stopReason?: unknown;
23
23
  content?: unknown;
24
+ timestamp?: unknown;
24
25
  };
25
26
  };
26
27
  export declare function shouldRequestStateFlowCompaction(usage: {
@@ -33,7 +34,7 @@ export declare function shouldRequestStateFlowCompaction(usage: {
33
34
  */
34
35
  export declare function hasCompactionSizedTranscript(entries: readonly ActiveEntry[]): boolean;
35
36
  /** Retain the complete latest accepted user iteration without hiding foreign extension context. */
36
- export declare function planStateFlowCompaction(entries: readonly ActiveEntry[], revision: string, step: number): StateFlowCompactionPlan | undefined;
37
+ export declare function planStateFlowCompaction(entries: readonly ActiveEntry[], boundary: string, step: number, runAnchorTimestamp: number | undefined): StateFlowCompactionPlan | undefined;
37
38
  /** Customize only the extension-owned manual request and only while its planned leaf remains selected. */
38
39
  export declare function stateFlowCompactionResult(plan: StateFlowCompactionPlan, marker: string, event: {
39
40
  reason: "manual" | "threshold" | "overflow";
@@ -1,5 +1,5 @@
1
1
  import { estimateTokens } from "@earendil-works/pi-agent-core";
2
- export const STATE_FLOW_COMPACTION_SUMMARY = "State Flow accepted the completed work before this boundary. Current memory is restored from its durable revision and projected separately; use the retained native entries for subsequent work.";
2
+ export const STATE_FLOW_COMPACTION_SUMMARY = "State Flow accepted the completed work before this boundary. Current memory is restored from its retained semantic boundary and projected separately; use the retained native entries for subsequent work.";
3
3
  /** A modest margin above Pi's default 20k retained suffix absorbs estimation drift. */
4
4
  export const STATE_FLOW_COMPACTION_MIN_CONTEXT_TOKENS = 24_000;
5
5
  function stateFlowEntry(entry) {
@@ -29,8 +29,8 @@ export function hasCompactionSizedTranscript(entries) {
29
29
  return false;
30
30
  }
31
31
  /** Retain the complete latest accepted user iteration without hiding foreign extension context. */
32
- export function planStateFlowCompaction(entries, revision, step) {
33
- if (!/^[0-9a-f]{40,64}$/.test(revision) && !/^file:[0-9a-f]{64}$/.test(revision))
32
+ export function planStateFlowCompaction(entries, boundary, step, runAnchorTimestamp) {
33
+ if (boundary.trim().length === 0 || typeof runAnchorTimestamp !== "number" || !Number.isFinite(runAnchorTimestamp))
34
34
  return undefined;
35
35
  if (!Number.isSafeInteger(step) || step < 0 || entries.length === 0)
36
36
  return undefined;
@@ -39,18 +39,18 @@ export function planStateFlowCompaction(entries, revision, step) {
39
39
  || entries[terminal]?.message?.stopReason === "error"
40
40
  || entries[terminal]?.message?.stopReason === "length")
41
41
  return undefined;
42
- let keep = terminal;
43
- while (keep >= 0 && !(entries[keep]?.type === "message" && entries[keep]?.message?.role === "user"))
44
- keep--;
45
- if (keep < 0)
42
+ // Steering is part of the same run; an absent or colliding timestamp cannot prove its first entry.
43
+ const isRunAnchor = (entry) => entry.type === "message" && entry.message?.role === "user" && entry.message.timestamp === runAnchorTimestamp;
44
+ const keep = entries.findIndex(isRunAnchor);
45
+ if (keep < 0 || keep > terminal || entries.findLastIndex(isRunAnchor) !== keep)
46
46
  return undefined;
47
- if (entries.slice(0, keep).some((entry) => entry.type === "custom" && !stateFlowEntry(entry)))
47
+ if (entries.slice(0, keep).some((entry) => entry.type === "custom_message" || (entry.type === "custom" && !stateFlowEntry(entry))))
48
48
  return undefined;
49
49
  const firstKeptEntryId = entries[keep]?.id;
50
50
  const leafId = entries.at(-1)?.id;
51
51
  if (typeof firstKeptEntryId !== "string" || typeof leafId !== "string")
52
52
  return undefined;
53
- return { leafId, firstKeptEntryId, details: { version: 1, owner: "state-flow", revision, step } };
53
+ return { leafId, firstKeptEntryId, details: { version: 1, owner: "state-flow", boundary, step } };
54
54
  }
55
55
  /** Customize only the extension-owned manual request and only while its planned leaf remains selected. */
56
56
  export function stateFlowCompactionResult(plan, marker, event) {
@@ -8,7 +8,7 @@ export interface StateFlowConfig {
8
8
  logging: boolean;
9
9
  /** Show successful patch_state arguments in the interactive tool row. */
10
10
  showSuccessfulPatches: boolean;
11
- remotePublication?: "off" | "turn-end" | "transition";
11
+ historyLimit: number;
12
12
  }
13
13
  /** Read the repository-global config once at extension load/reload. Missing config uses defaults; invalid config never falls back. */
14
14
  export declare function loadStateFlowConfig(agentDir?: string, repositoryRoot?: string): StateFlowConfig;