@basou/core 0.14.1 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +101 -21
- package/dist/index.js +132 -33
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1573,6 +1573,19 @@ type SessionEntry = {
|
|
|
1573
1573
|
session: Session;
|
|
1574
1574
|
suspect: boolean;
|
|
1575
1575
|
suspectReason: SuspectReason | null;
|
|
1576
|
+
/**
|
|
1577
|
+
* The trail store this entry was read from. Its `sessions` directory locates
|
|
1578
|
+
* the session's `events.jsonl`, so a federated caller can replay events from
|
|
1579
|
+
* the store the session actually lives in (not the local store). For a plain
|
|
1580
|
+
* local load this is the `paths` passed to {@link loadSessionEntries}.
|
|
1581
|
+
*/
|
|
1582
|
+
sourceRoot: BasouPaths;
|
|
1583
|
+
/**
|
|
1584
|
+
* Federation host label from the registry (`~/.basou/hosts.yaml`), or `null`
|
|
1585
|
+
* for the local store. Surfaced by orientation so a merged, multi-host view
|
|
1586
|
+
* can attribute the latest session / decision / next-step to its host.
|
|
1587
|
+
*/
|
|
1588
|
+
host: string | null;
|
|
1576
1589
|
};
|
|
1577
1590
|
/**
|
|
1578
1591
|
* Per-session degradation reason emitted by {@link loadSessionEntries.onSkip}.
|
|
@@ -1595,6 +1608,28 @@ type LoadSessionEntriesOptions = {
|
|
|
1595
1608
|
onWarning?: (warning: ReplayWarning, sessionId: string) => void;
|
|
1596
1609
|
onSkip?: (sessionId: string, reason: SessionSkipReason) => void;
|
|
1597
1610
|
};
|
|
1611
|
+
/**
|
|
1612
|
+
* A trail store to read in a federated load, tagged with its host label.
|
|
1613
|
+
* `host: null` denotes the local store; a non-null label comes from the host
|
|
1614
|
+
* registry (`~/.basou/hosts.yaml`). `paths` is where that store is reachable
|
|
1615
|
+
* as a local path on this machine (an SSHFS mount, an rsync mirror, etc.) —
|
|
1616
|
+
* basou itself never performs any network I/O to obtain it.
|
|
1617
|
+
*/
|
|
1618
|
+
type FederatedRoot = {
|
|
1619
|
+
paths: BasouPaths;
|
|
1620
|
+
host: string | null;
|
|
1621
|
+
};
|
|
1622
|
+
type LoadFederatedOptions = LoadSessionEntriesOptions & {
|
|
1623
|
+
/**
|
|
1624
|
+
* Called when a NON-local root cannot be enumerated (present-but-unreadable
|
|
1625
|
+
* mount, permission error). That root is skipped best-effort so the local
|
|
1626
|
+
* store and other roots still load. The local root (`host: null`) is never
|
|
1627
|
+
* degraded here — its errors propagate, preserving single-store behaviour.
|
|
1628
|
+
* (An absent root path is not an error: {@link enumerateSessionDirs} returns
|
|
1629
|
+
* `[]` on ENOENT, so a dropped mount is simply an empty host.)
|
|
1630
|
+
*/
|
|
1631
|
+
onRootUnavailable?: (host: string, error: unknown) => void;
|
|
1632
|
+
};
|
|
1598
1633
|
/**
|
|
1599
1634
|
* List session directory names under `paths.sessions`, ULID ascending.
|
|
1600
1635
|
*
|
|
@@ -1668,20 +1703,18 @@ declare function classifySuspect(paths: BasouPaths, sessionId: string, session:
|
|
|
1668
1703
|
suspect: boolean;
|
|
1669
1704
|
suspectReason: SuspectReason | null;
|
|
1670
1705
|
}>;
|
|
1706
|
+
declare function loadSessionEntries(paths: BasouPaths, options: LoadSessionEntriesOptions): Promise<SessionEntry[]>;
|
|
1671
1707
|
/**
|
|
1672
|
-
*
|
|
1673
|
-
*
|
|
1674
|
-
*
|
|
1675
|
-
*
|
|
1676
|
-
*
|
|
1677
|
-
*
|
|
1678
|
-
*
|
|
1679
|
-
*
|
|
1680
|
-
*
|
|
1681
|
-
* `options.now` is taken once and threaded into every {@link classifySuspect}
|
|
1682
|
-
* call so age comparisons are consistent across sessions.
|
|
1708
|
+
* Federated load across multiple trail stores. Each root's sessions are tagged
|
|
1709
|
+
* with that root's host label and `sourceRoot`, so a caller replays events from
|
|
1710
|
+
* the store the session lives in. De-duped by `sessionId` (a per-host random
|
|
1711
|
+
* ULID), then by `source.external_id` when present — first occurrence wins, so
|
|
1712
|
+
* pass the local root FIRST to keep it authoritative (e.g. over a re-imported
|
|
1713
|
+
* copy of the same vendor session on another host). A non-local root that
|
|
1714
|
+
* cannot be enumerated is reported via `onRootUnavailable` and skipped; the
|
|
1715
|
+
* local root's errors propagate, matching {@link loadSessionEntries}.
|
|
1683
1716
|
*/
|
|
1684
|
-
declare function
|
|
1717
|
+
declare function loadFederatedSessionEntries(roots: ReadonlyArray<FederatedRoot>, options: LoadFederatedOptions): Promise<SessionEntry[]>;
|
|
1685
1718
|
|
|
1686
1719
|
type DecisionsRendererInput = {
|
|
1687
1720
|
paths: BasouPaths;
|
|
@@ -3155,6 +3188,19 @@ type OrientationRendererInput = {
|
|
|
3155
3188
|
* reads as a verdict for a supervisor, not developer diagnostics.
|
|
3156
3189
|
*/
|
|
3157
3190
|
verbose?: boolean;
|
|
3191
|
+
/**
|
|
3192
|
+
* Additional trail stores to MERGE into this orientation, each a local path
|
|
3193
|
+
* (an SSHFS mount / rsync mirror of another host's `.basou`) tagged with a
|
|
3194
|
+
* host label. Absent / empty = local-only (byte-identical to before). basou
|
|
3195
|
+
* performs no network I/O; the operator's existing tooling places these paths.
|
|
3196
|
+
*/
|
|
3197
|
+
federatedRoots?: FederatedRoot[];
|
|
3198
|
+
/**
|
|
3199
|
+
* Called when a federated (non-local) host root is present but cannot be
|
|
3200
|
+
* enumerated (e.g. an unreadable mount). That host is skipped; the local
|
|
3201
|
+
* store and other hosts still render. An absent root path is silently empty.
|
|
3202
|
+
*/
|
|
3203
|
+
onHostUnavailable?: (host: string, error: unknown) => void;
|
|
3158
3204
|
};
|
|
3159
3205
|
type OrientationRendererResult = {
|
|
3160
3206
|
/** Generated body. orientation.md is overwritten whole (no markers, gitignored). */
|
|
@@ -3171,11 +3217,13 @@ type DecisionRecord = {
|
|
|
3171
3217
|
title: string;
|
|
3172
3218
|
occurredAt: string;
|
|
3173
3219
|
sessionId: string;
|
|
3220
|
+
host: string | null;
|
|
3174
3221
|
};
|
|
3175
3222
|
type NoteRecord = {
|
|
3176
3223
|
body: string;
|
|
3177
3224
|
sessionId: string;
|
|
3178
3225
|
occurredAt: string;
|
|
3226
|
+
host: string | null;
|
|
3179
3227
|
};
|
|
3180
3228
|
type PendingApproval = {
|
|
3181
3229
|
id: string;
|
|
@@ -3200,11 +3248,13 @@ type SuspectSession = {
|
|
|
3200
3248
|
sessionId: string;
|
|
3201
3249
|
status: string;
|
|
3202
3250
|
reason: SuspectReason | null;
|
|
3251
|
+
host: string | null;
|
|
3203
3252
|
};
|
|
3204
3253
|
type LatestSession = {
|
|
3205
3254
|
sessionId: string;
|
|
3206
3255
|
label: string | null;
|
|
3207
3256
|
status: string;
|
|
3257
|
+
host: string | null;
|
|
3208
3258
|
};
|
|
3209
3259
|
type SourceCount = {
|
|
3210
3260
|
kind: string;
|
|
@@ -3249,6 +3299,12 @@ type OrientationSummary = {
|
|
|
3249
3299
|
plannedTasks: PlannedTask[];
|
|
3250
3300
|
pendingApprovals: PendingApproval[];
|
|
3251
3301
|
suspects: SuspectSession[];
|
|
3302
|
+
/**
|
|
3303
|
+
* Distinct non-local host labels present in the merged set (sorted). Empty
|
|
3304
|
+
* for a local-only orientation. Lets a consumer render the multi-host banner
|
|
3305
|
+
* and the local-only-freshness caveat without re-deriving from sessions.
|
|
3306
|
+
*/
|
|
3307
|
+
hosts: string[];
|
|
3252
3308
|
freshness: {
|
|
3253
3309
|
/** started_at of the newest non-archived session, or null when none captured. */
|
|
3254
3310
|
newestStartedAt: string | null;
|
|
@@ -5002,15 +5058,26 @@ declare function readManifest(paths: BasouPaths): Promise<Manifest>;
|
|
|
5002
5058
|
declare const GENERATED_START = "<!-- BASOU:GENERATED:START -->";
|
|
5003
5059
|
/** Marker line that ends the auto-generated region. */
|
|
5004
5060
|
declare const GENERATED_END = "<!-- BASOU:GENERATED:END -->";
|
|
5061
|
+
/** Marker line that begins a managed protocol block in a foreign instruction file. */
|
|
5062
|
+
declare const PROTOCOL_START = "<!-- BASOU:PROTOCOLS:START -->";
|
|
5063
|
+
/** Marker line that ends a managed protocol block. */
|
|
5064
|
+
declare const PROTOCOL_END = "<!-- BASOU:PROTOCOLS:END -->";
|
|
5065
|
+
/** A start/end marker pair. Both lines are matched whole-line, exact. */
|
|
5066
|
+
type Markers = {
|
|
5067
|
+
start: string;
|
|
5068
|
+
end: string;
|
|
5069
|
+
};
|
|
5005
5070
|
/**
|
|
5006
5071
|
* Result of parsing a markdown body for the BASOU:GENERATED marker region.
|
|
5007
5072
|
*
|
|
5008
5073
|
* The spec mandates strict line-level matching (see
|
|
5009
5074
|
* `docs/spec/generated-markdown.md#102-marker-convention`): a marker is
|
|
5010
5075
|
* only recognized when an entire line is exactly the marker string.
|
|
5011
|
-
* Leading/trailing whitespace
|
|
5012
|
-
*
|
|
5013
|
-
*
|
|
5076
|
+
* Leading/trailing whitespace and comment compression are treated as legacy
|
|
5077
|
+
* formats (`no_markers`) so that re-generation refuses to silently overwrite a
|
|
5078
|
+
* mismatched manual edit. A leading UTF-8 BOM is the one exception: it is
|
|
5079
|
+
* tolerated (stripped for matching, re-prepended on render) so a BOM-prefixed
|
|
5080
|
+
* file round-trips instead of duplicating the block.
|
|
5014
5081
|
*/
|
|
5015
5082
|
type MarkerSection = {
|
|
5016
5083
|
kind: "ok";
|
|
@@ -5057,11 +5124,13 @@ declare function writeMarkdownFile(filePath: string, body: string): Promise<void
|
|
|
5057
5124
|
* - `multiple_pairs`: more than one START or END line.
|
|
5058
5125
|
* - `wrong_order`: END appears before START.
|
|
5059
5126
|
*
|
|
5060
|
-
* Matching is strict: leading/trailing whitespace
|
|
5061
|
-
*
|
|
5062
|
-
*
|
|
5127
|
+
* Matching is strict: leading/trailing whitespace and comment compression
|
|
5128
|
+
* (`<!--BASOU:...-->`) bypass the marker and are treated as legacy content. A
|
|
5129
|
+
* leading UTF-8 BOM is the exception: it is stripped before matching and
|
|
5130
|
+
* re-prepended to `before`, so a marker on the first line of a BOM-prefixed
|
|
5131
|
+
* file still matches and the file round-trips.
|
|
5063
5132
|
*/
|
|
5064
|
-
declare function parseMarkers(content: string): MarkerSection;
|
|
5133
|
+
declare function parseMarkers(content: string, markers?: Markers): MarkerSection;
|
|
5065
5134
|
/**
|
|
5066
5135
|
* Build the final markdown body by replacing the BASOU:GENERATED region.
|
|
5067
5136
|
*
|
|
@@ -5073,7 +5142,18 @@ declare function parseMarkers(content: string): MarkerSection;
|
|
|
5073
5142
|
* The caller passes `fileLabel` (e.g. `"handoff.md"` or `"decisions.md"`)
|
|
5074
5143
|
* so the error message is informative without leaking an absolute path.
|
|
5075
5144
|
*/
|
|
5076
|
-
declare function renderWithMarkers(existing: string | null, generated: string, fileLabel: string): string;
|
|
5145
|
+
declare function renderWithMarkers(existing: string | null, generated: string, fileLabel: string, markers?: Markers): string;
|
|
5146
|
+
/**
|
|
5147
|
+
* Remove a marker region from `existing`, returning the body without the block.
|
|
5148
|
+
*
|
|
5149
|
+
* - `no_markers`: returns `existing` unchanged (nothing to remove).
|
|
5150
|
+
* - `ok`: drops both marker lines and the generated region, collapsing the
|
|
5151
|
+
* single newline that terminated the END marker line so no stray blank line
|
|
5152
|
+
* is left behind.
|
|
5153
|
+
* - any other parse result: throws a pathless error referencing `fileLabel`
|
|
5154
|
+
* (mismatched markers must not be silently rewritten).
|
|
5155
|
+
*/
|
|
5156
|
+
declare function removeMarkerSection(existing: string, fileLabel: string, markers?: Markers): string;
|
|
5077
5157
|
|
|
5078
5158
|
/**
|
|
5079
5159
|
* Options for {@link importSessionFromJson}. All fields are optional.
|
|
@@ -5300,4 +5380,4 @@ declare function overwriteYamlFile(filePath: string, value: unknown): Promise<vo
|
|
|
5300
5380
|
*/
|
|
5301
5381
|
declare const BASOU_CORE_VERSION = "0.1.0";
|
|
5302
5382
|
|
|
5303
|
-
export { ACTIVE_GAP_CAP_MS, type ActiveTimeBasis, type AdapterOutputEvent, type AdoptCandidate, type AdoptCandidateKind, type AppendBasouGitignoreOptions, type AppendBasouGitignoreResult, type AppendEventToExistingInput, type AppendEventToExistingResult, type Approval, type ApprovalApprovedEvent, type ApprovalExpiredEvent, ApprovalIdSchema, type ApprovalLocation, type ApprovalRejectedEvent, type ApprovalRequestedEvent, ApprovalSchema, type ApprovalStatus, ApprovalStatusSchema, type ArchivePlan, type ArchiveTaskInput, type ArchiveTaskResult, type AttachTaskInput, type AttachUpdateTaskStatusInput, type AttachableStatus, BASOU_CORE_VERSION, type BasouPaths, type BulkChainResult, CLAUDE_IMPORT_SOURCE, CODEX_IMPORT_SOURCE, type CaptureMode, type ChainBreakReason, type ChainTailState, type ChainVerdict, type ChainVerdictStatus, type ChainedEvents, ChildProcessRunner, type CitedReview, type ClaudeTranscriptRecord, type ClaudeTranscriptToPayloadOptions, type CodexRolloutRecord, type CodexRolloutToPayloadOptions, type CommandExecutedEvent, type CommandLookup, type CreateAdHocSessionInput, type CreateAdHocSessionResult, type CreateAdHocTaskInput, type CreateManifestInput, type CreateTaskInput, type CreateTaskResult, type DayWorkStats, DecisionIdSchema, type DecisionRecordedEvent, type DecisionsRendererInput, type DecisionsRendererResult, type DeleteTaskInput, type DeleteTaskResult, type DiffResult, type EditTaskInput, type EditTaskResult, type Event, EventIdSchema, EventSchema, EventSourceSchema, type ExistingViewLink, FailedToFinalizeError, type FileChange, type FileChangeStatus, type FileChangedEvent, GENERATED_END, GENERATED_START, type GitSnapshot, type GitSnapshotEvent, type GitignorePlanSummary, type HandoffRendererInput, type HandoffRendererResult, ID_PREFIXES, type IdPrefix, type ImportSessionOptions, type ImportSessionResult, type InstructionFileFact, type InstructionSymlinkFact, type InstructionSymlinkState, IsoTimestampSchema, JSON_SCHEMA_VERSION, type JsonSchemaArtifact, type LoadSessionEntriesOptions, type LoadTaskEntriesOptions, type LoadedApproval, type LockHandle, type LockScope, type Manifest, ManifestSchema, type MarkerSection, type MeasureAvailability, type NoteAddedEvent, type OrientationRendererInput, type OrientationRendererResult, type OrientationSummary, type PrefixedId, type PresetAction, type PresetCollision, type PresetMarkerConflict, type PresetMarkerKind, type PresetPlanSummary, type PresetRepo, type ProcessRunner, type PublishKind, type PublishTarget, type RechainOptions, type RechainResult, type ReconcileAllResult, type ReconcileAllTasksInput, type ReconcileAllTasksOptions, type ReconcileFailure, type ReconcileResult, type ReconcileTaskInput, type RefreshLinkageInput, type RefreshLinkageResult, type ReimportOptions, type ReimportResult, type RenamePlan, type ReplayOptions, type ReplayWarning, type RepoEntry, type RepoGitignoreFacts, type RepoGitignorePlan, type RepoLanguage, type RepoPresetFacts, type RepoPresetPlan, type RepoSymlinkFacts, type RepoSymlinkPlan, type RepoVisibility, type RepoWiringFacts, type ReportApprovalItem, type ReportData, type ReportDecisionItem, type ReportRendererInput, type ReportRendererResult, type ReportSessionItem, type ReportTaskItem, type ReviewGapRepoSummary, type ReviewGapUnit, type ReviewGapVerdict, type ReviewGapsInput, type ReviewGapsSummary, type RiskLevel, RiskLevelSchema, type RosterAdoptionPlan, type RosterDriftSummary, type RunOptions, type RunResult, STUCK_THRESHOLD_MS, type SanitizePathOptions, type SanitizeRelatedFilesResult, SchemaVersionSchema, type Session, type SessionEndedEvent, type SessionEntry, SessionIdSchema, type SessionImportPayload, SessionImportPayloadSchema, type SessionInnerImportInput, SessionInnerImportSchema, type SessionIntegrity, SessionIntegritySchema, type SessionMetrics, SessionMetricsSchema, SessionSchema, type SessionSkipReason, type SessionSourceKind, SessionSourceKindSchema, type SessionStartedEvent, type SessionStatus, type SessionStatusChangedEvent, SessionStatusSchema, type SessionWorkStats, type SourceRootsReconcile, type SourceWorkStats, type StatusCount, StatusSchema, type StatusSnapshot, type SuspectReason, type SymlinkCollision, type SymlinkConflict, type SymlinkPlanSummary, type Task, type TaskArchivedEvent, type TaskCreatedEvent, type TaskDeletedEvent, type TaskDocument, TaskIdSchema, type TaskLinkageRefreshedEvent, type TaskReconciledEvent, TaskSchema, type TaskSkipReason, type TaskStatus, type TaskStatusChangedEvent, type TaskStatusCount, TaskStatusSchema, TaskWriteAfterEventError, type TaskWriteAfterEventPhase, type TokenTotals, type UpdateAdHocTaskStatusInput, type UpdateTaskStatusInput, type UpdateTaskStatusResult, type ViewCollision, type ViewConflict, type ViewLinkState, type ViewRepoFact, type ViewStrayUnknown, type WiringRisk, type WiringSummary, type WorkStatsInput, type WorkStatsResult, type WorkStatsTotals, WorkspaceIdSchema, type WorkspaceViewPlan, type WriteEventsBulkOptions, type WriteTaskFileMode, acquireLock, appendBasouGitignore, appendChainedEvent, appendChainedEventLocked, appendEvent, appendEventToExistingSession, archiveTask, assertBasouRootSafe, basouPaths, buildJsonSchemas, buildStatusSnapshot, chainEvents, chainRawJsonLines, classifySuspect, claudeCodeAdapterMetadata, claudeTranscriptToImportPayload, codexRolloutToImportPayload, computeWorkStats, createAdHocSessionWithEvent, createManifest, createTaskWithEvent, deleteTask, editTask, ensureBasouDirectory, enumerateApprovals, enumerateArchivedTaskIds, enumerateSessionDirs, enumerateTaskIds, finalizeSessionYaml, findErrorCode, findReviewGaps, formatDurationMs, genesisHash, getDiff, getSnapshot, importSessionFromJson, inspectChainTail, isGitNotFound, isImportDerivedSource, isLazyExpired, isRenderable, isValidPrefixedId, lineHash, linkYamlFile, loadApproval, loadSessionEntries, loadTaskEntries, normalizeRepoKey, normalizeRepoPath, overwriteYamlFile, parseDuration, parseMarkers, pathBasename, planArchive, planGitignore, planRename, planRosterAdoption, planWorkspaceView, prefixedUlid, readAllEvents, readManifest, readMarkdownFile, readSessionYaml, readStatus, readTaskFile, readTaskFileWithArchiveFallback, readYamlFile, rechainSessionInPlace, reconcileAllTasks, reconcileSourceRoots, reconcileTask, refreshTaskLinkedSessions, reimportPreservingId, renderDecisions, renderHandoff, renderOrientation, renderPresetBlock, renderReport, renderWithMarkers, replayEvents, resolveBasouRepositoryRoot, resolveClaudeCodeCommand, resolveRepositoryRoot, resolveSessionId, resolveTaskId, safeSimpleGit, sanitizePath, sanitizeRelatedFiles, sanitizeWorkingDirectory, serializeEventLine, serializeJsonSchema, sessionWorkStatsFromEvents, summarizeAdapterOutput, summarizeOrientation, summarizePresetPlan, summarizeRosterDrift, summarizeSymlinkPlan, summarizeWiring, tryRemoteUrl, ulid, unknownManifestKeys, updateTaskStatusWithEvent, verifyEventsChain, writeEventsBulk, writeManifest, writeMarkdownFile, writeStatus, writeTaskFile, writeYamlFile };
|
|
5383
|
+
export { ACTIVE_GAP_CAP_MS, type ActiveTimeBasis, type AdapterOutputEvent, type AdoptCandidate, type AdoptCandidateKind, type AppendBasouGitignoreOptions, type AppendBasouGitignoreResult, type AppendEventToExistingInput, type AppendEventToExistingResult, type Approval, type ApprovalApprovedEvent, type ApprovalExpiredEvent, ApprovalIdSchema, type ApprovalLocation, type ApprovalRejectedEvent, type ApprovalRequestedEvent, ApprovalSchema, type ApprovalStatus, ApprovalStatusSchema, type ArchivePlan, type ArchiveTaskInput, type ArchiveTaskResult, type AttachTaskInput, type AttachUpdateTaskStatusInput, type AttachableStatus, BASOU_CORE_VERSION, type BasouPaths, type BulkChainResult, CLAUDE_IMPORT_SOURCE, CODEX_IMPORT_SOURCE, type CaptureMode, type ChainBreakReason, type ChainTailState, type ChainVerdict, type ChainVerdictStatus, type ChainedEvents, ChildProcessRunner, type CitedReview, type ClaudeTranscriptRecord, type ClaudeTranscriptToPayloadOptions, type CodexRolloutRecord, type CodexRolloutToPayloadOptions, type CommandExecutedEvent, type CommandLookup, type CreateAdHocSessionInput, type CreateAdHocSessionResult, type CreateAdHocTaskInput, type CreateManifestInput, type CreateTaskInput, type CreateTaskResult, type DayWorkStats, DecisionIdSchema, type DecisionRecordedEvent, type DecisionsRendererInput, type DecisionsRendererResult, type DeleteTaskInput, type DeleteTaskResult, type DiffResult, type EditTaskInput, type EditTaskResult, type Event, EventIdSchema, EventSchema, EventSourceSchema, type ExistingViewLink, FailedToFinalizeError, type FederatedRoot, type FileChange, type FileChangeStatus, type FileChangedEvent, GENERATED_END, GENERATED_START, type GitSnapshot, type GitSnapshotEvent, type GitignorePlanSummary, type HandoffRendererInput, type HandoffRendererResult, ID_PREFIXES, type IdPrefix, type ImportSessionOptions, type ImportSessionResult, type InstructionFileFact, type InstructionSymlinkFact, type InstructionSymlinkState, IsoTimestampSchema, JSON_SCHEMA_VERSION, type JsonSchemaArtifact, type LoadFederatedOptions, type LoadSessionEntriesOptions, type LoadTaskEntriesOptions, type LoadedApproval, type LockHandle, type LockScope, type Manifest, ManifestSchema, type MarkerSection, type Markers, type MeasureAvailability, type NoteAddedEvent, type OrientationRendererInput, type OrientationRendererResult, type OrientationSummary, PROTOCOL_END, PROTOCOL_START, type PrefixedId, type PresetAction, type PresetCollision, type PresetMarkerConflict, type PresetMarkerKind, type PresetPlanSummary, type PresetRepo, type ProcessRunner, type PublishKind, type PublishTarget, type RechainOptions, type RechainResult, type ReconcileAllResult, type ReconcileAllTasksInput, type ReconcileAllTasksOptions, type ReconcileFailure, type ReconcileResult, type ReconcileTaskInput, type RefreshLinkageInput, type RefreshLinkageResult, type ReimportOptions, type ReimportResult, type RenamePlan, type ReplayOptions, type ReplayWarning, type RepoEntry, type RepoGitignoreFacts, type RepoGitignorePlan, type RepoLanguage, type RepoPresetFacts, type RepoPresetPlan, type RepoSymlinkFacts, type RepoSymlinkPlan, type RepoVisibility, type RepoWiringFacts, type ReportApprovalItem, type ReportData, type ReportDecisionItem, type ReportRendererInput, type ReportRendererResult, type ReportSessionItem, type ReportTaskItem, type ReviewGapRepoSummary, type ReviewGapUnit, type ReviewGapVerdict, type ReviewGapsInput, type ReviewGapsSummary, type RiskLevel, RiskLevelSchema, type RosterAdoptionPlan, type RosterDriftSummary, type RunOptions, type RunResult, STUCK_THRESHOLD_MS, type SanitizePathOptions, type SanitizeRelatedFilesResult, SchemaVersionSchema, type Session, type SessionEndedEvent, type SessionEntry, SessionIdSchema, type SessionImportPayload, SessionImportPayloadSchema, type SessionInnerImportInput, SessionInnerImportSchema, type SessionIntegrity, SessionIntegritySchema, type SessionMetrics, SessionMetricsSchema, SessionSchema, type SessionSkipReason, type SessionSourceKind, SessionSourceKindSchema, type SessionStartedEvent, type SessionStatus, type SessionStatusChangedEvent, SessionStatusSchema, type SessionWorkStats, type SourceRootsReconcile, type SourceWorkStats, type StatusCount, StatusSchema, type StatusSnapshot, type SuspectReason, type SymlinkCollision, type SymlinkConflict, type SymlinkPlanSummary, type Task, type TaskArchivedEvent, type TaskCreatedEvent, type TaskDeletedEvent, type TaskDocument, TaskIdSchema, type TaskLinkageRefreshedEvent, type TaskReconciledEvent, TaskSchema, type TaskSkipReason, type TaskStatus, type TaskStatusChangedEvent, type TaskStatusCount, TaskStatusSchema, TaskWriteAfterEventError, type TaskWriteAfterEventPhase, type TokenTotals, type UpdateAdHocTaskStatusInput, type UpdateTaskStatusInput, type UpdateTaskStatusResult, type ViewCollision, type ViewConflict, type ViewLinkState, type ViewRepoFact, type ViewStrayUnknown, type WiringRisk, type WiringSummary, type WorkStatsInput, type WorkStatsResult, type WorkStatsTotals, WorkspaceIdSchema, type WorkspaceViewPlan, type WriteEventsBulkOptions, type WriteTaskFileMode, acquireLock, appendBasouGitignore, appendChainedEvent, appendChainedEventLocked, appendEvent, appendEventToExistingSession, archiveTask, assertBasouRootSafe, basouPaths, buildJsonSchemas, buildStatusSnapshot, chainEvents, chainRawJsonLines, classifySuspect, claudeCodeAdapterMetadata, claudeTranscriptToImportPayload, codexRolloutToImportPayload, computeWorkStats, createAdHocSessionWithEvent, createManifest, createTaskWithEvent, deleteTask, editTask, ensureBasouDirectory, enumerateApprovals, enumerateArchivedTaskIds, enumerateSessionDirs, enumerateTaskIds, finalizeSessionYaml, findErrorCode, findReviewGaps, formatDurationMs, genesisHash, getDiff, getSnapshot, importSessionFromJson, inspectChainTail, isGitNotFound, isImportDerivedSource, isLazyExpired, isRenderable, isValidPrefixedId, lineHash, linkYamlFile, loadApproval, loadFederatedSessionEntries, loadSessionEntries, loadTaskEntries, normalizeRepoKey, normalizeRepoPath, overwriteYamlFile, parseDuration, parseMarkers, pathBasename, planArchive, planGitignore, planRename, planRosterAdoption, planWorkspaceView, prefixedUlid, readAllEvents, readManifest, readMarkdownFile, readSessionYaml, readStatus, readTaskFile, readTaskFileWithArchiveFallback, readYamlFile, rechainSessionInPlace, reconcileAllTasks, reconcileSourceRoots, reconcileTask, refreshTaskLinkedSessions, reimportPreservingId, removeMarkerSection, renderDecisions, renderHandoff, renderOrientation, renderPresetBlock, renderReport, renderWithMarkers, replayEvents, resolveBasouRepositoryRoot, resolveClaudeCodeCommand, resolveRepositoryRoot, resolveSessionId, resolveTaskId, safeSimpleGit, sanitizePath, sanitizeRelatedFiles, sanitizeWorkingDirectory, serializeEventLine, serializeJsonSchema, sessionWorkStatsFromEvents, summarizeAdapterOutput, summarizeOrientation, summarizePresetPlan, summarizeRosterDrift, summarizeSymlinkPlan, summarizeWiring, tryRemoteUrl, ulid, unknownManifestKeys, updateTaskStatusWithEvent, verifyEventsChain, writeEventsBulk, writeManifest, writeMarkdownFile, writeStatus, writeTaskFile, writeYamlFile };
|
package/dist/index.js
CHANGED
|
@@ -1372,7 +1372,8 @@ async function classifySuspect(paths, sessionId, session, now, onWarning) {
|
|
|
1372
1372
|
}
|
|
1373
1373
|
return { suspect: false, suspectReason: null };
|
|
1374
1374
|
}
|
|
1375
|
-
async function
|
|
1375
|
+
async function loadEntriesFromRoot(root, options) {
|
|
1376
|
+
const { paths } = root;
|
|
1376
1377
|
const sessionIds = await enumerateSessionDirs(paths);
|
|
1377
1378
|
const entries = [];
|
|
1378
1379
|
for (const sid of sessionIds) {
|
|
@@ -1402,10 +1403,50 @@ async function loadSessionEntries(paths, options) {
|
|
|
1402
1403
|
} catch {
|
|
1403
1404
|
options.onSkip?.(sid, "events_jsonl_unreadable");
|
|
1404
1405
|
}
|
|
1405
|
-
entries.push({
|
|
1406
|
+
entries.push({
|
|
1407
|
+
sessionId: sid,
|
|
1408
|
+
session,
|
|
1409
|
+
suspect,
|
|
1410
|
+
suspectReason,
|
|
1411
|
+
sourceRoot: paths,
|
|
1412
|
+
host: root.host
|
|
1413
|
+
});
|
|
1406
1414
|
}
|
|
1407
1415
|
return entries;
|
|
1408
1416
|
}
|
|
1417
|
+
async function loadSessionEntries(paths, options) {
|
|
1418
|
+
return loadEntriesFromRoot({ paths, host: null }, options);
|
|
1419
|
+
}
|
|
1420
|
+
async function loadFederatedSessionEntries(roots, options) {
|
|
1421
|
+
const out = [];
|
|
1422
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
1423
|
+
const seenExternal = /* @__PURE__ */ new Set();
|
|
1424
|
+
for (const root of roots) {
|
|
1425
|
+
let entries;
|
|
1426
|
+
if (root.host === null) {
|
|
1427
|
+
entries = await loadEntriesFromRoot(root, options);
|
|
1428
|
+
} else {
|
|
1429
|
+
try {
|
|
1430
|
+
entries = await loadEntriesFromRoot(root, options);
|
|
1431
|
+
} catch (error) {
|
|
1432
|
+
options.onRootUnavailable?.(root.host, error);
|
|
1433
|
+
continue;
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
for (const entry of entries) {
|
|
1437
|
+
if (seenIds.has(entry.sessionId)) continue;
|
|
1438
|
+
const ext = entry.session.session.source.external_id;
|
|
1439
|
+
if (typeof ext === "string" && ext.length > 0) {
|
|
1440
|
+
const extKey = `${entry.session.session.source.kind}:${ext}`;
|
|
1441
|
+
if (seenExternal.has(extKey)) continue;
|
|
1442
|
+
seenExternal.add(extKey);
|
|
1443
|
+
}
|
|
1444
|
+
seenIds.add(entry.sessionId);
|
|
1445
|
+
out.push(entry);
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
return out;
|
|
1449
|
+
}
|
|
1409
1450
|
|
|
1410
1451
|
// src/decisions/decisions-renderer.ts
|
|
1411
1452
|
async function renderDecisions(input) {
|
|
@@ -4344,7 +4385,13 @@ async function summarizeOrientation(input) {
|
|
|
4344
4385
|
const loadOpts = { now };
|
|
4345
4386
|
if (input.onSessionSkip !== void 0) loadOpts.onSkip = input.onSessionSkip;
|
|
4346
4387
|
if (input.onWarning !== void 0) loadOpts.onWarning = input.onWarning;
|
|
4347
|
-
const entries =
|
|
4388
|
+
const entries = input.federatedRoots !== void 0 && input.federatedRoots.length > 0 ? await loadFederatedSessionEntries(
|
|
4389
|
+
[{ paths: input.paths, host: null }, ...input.federatedRoots],
|
|
4390
|
+
{
|
|
4391
|
+
...loadOpts,
|
|
4392
|
+
...input.onHostUnavailable !== void 0 ? { onRootUnavailable: input.onHostUnavailable } : {}
|
|
4393
|
+
}
|
|
4394
|
+
) : await loadSessionEntries(input.paths, loadOpts);
|
|
4348
4395
|
const decisions = [];
|
|
4349
4396
|
let latestActivityAt = null;
|
|
4350
4397
|
let latestNote = null;
|
|
@@ -4354,7 +4401,7 @@ async function summarizeOrientation(input) {
|
|
|
4354
4401
|
}
|
|
4355
4402
|
};
|
|
4356
4403
|
for (const entry of entries) {
|
|
4357
|
-
const sessionDir = join14(
|
|
4404
|
+
const sessionDir = join14(entry.sourceRoot.sessions, entry.sessionId);
|
|
4358
4405
|
const counted = entry.session.session.status !== "archived";
|
|
4359
4406
|
if (counted) noteActivity(entry.session.session.ended_at ?? entry.session.session.started_at);
|
|
4360
4407
|
try {
|
|
@@ -4366,12 +4413,18 @@ async function summarizeOrientation(input) {
|
|
|
4366
4413
|
decisionId: ev.decision_id,
|
|
4367
4414
|
title: ev.title,
|
|
4368
4415
|
occurredAt: ev.occurred_at,
|
|
4369
|
-
sessionId: entry.sessionId
|
|
4416
|
+
sessionId: entry.sessionId,
|
|
4417
|
+
host: entry.host
|
|
4370
4418
|
});
|
|
4371
4419
|
}
|
|
4372
4420
|
if (counted && ev.type === "note_added" && ev.kind === "next_step") {
|
|
4373
4421
|
if (latestNote === null || Date.parse(ev.occurred_at) > Date.parse(latestNote.occurredAt)) {
|
|
4374
|
-
latestNote = {
|
|
4422
|
+
latestNote = {
|
|
4423
|
+
body: ev.body,
|
|
4424
|
+
sessionId: entry.sessionId,
|
|
4425
|
+
occurredAt: ev.occurred_at,
|
|
4426
|
+
host: entry.host
|
|
4427
|
+
};
|
|
4375
4428
|
}
|
|
4376
4429
|
}
|
|
4377
4430
|
if (counted) noteActivity(ev.occurred_at);
|
|
@@ -4414,7 +4467,8 @@ async function summarizeOrientation(input) {
|
|
|
4414
4467
|
const suspects = entries.filter((e) => e.suspect).map((e) => ({
|
|
4415
4468
|
sessionId: e.sessionId,
|
|
4416
4469
|
status: e.session.session.status,
|
|
4417
|
-
reason: e.suspectReason
|
|
4470
|
+
reason: e.suspectReason,
|
|
4471
|
+
host: e.host
|
|
4418
4472
|
}));
|
|
4419
4473
|
const liveEntries = entries.filter(
|
|
4420
4474
|
(e) => e.session.session.status !== "archived" && e.session.session.source.kind !== "import"
|
|
@@ -4423,7 +4477,8 @@ async function summarizeOrientation(input) {
|
|
|
4423
4477
|
const latestSession = latestEntry !== void 0 ? {
|
|
4424
4478
|
sessionId: latestEntry.sessionId,
|
|
4425
4479
|
label: latestEntry.session.session.label ?? null,
|
|
4426
|
-
status: latestEntry.session.session.status
|
|
4480
|
+
status: latestEntry.session.session.status,
|
|
4481
|
+
host: latestEntry.host
|
|
4427
4482
|
} : null;
|
|
4428
4483
|
const activityEntries = entries.filter((e) => e.session.session.status !== "archived");
|
|
4429
4484
|
const newest = [...activityEntries].sort(
|
|
@@ -4446,6 +4501,9 @@ async function summarizeOrientation(input) {
|
|
|
4446
4501
|
const uniqueFiles = new Set(latestFiles);
|
|
4447
4502
|
const displayed = [...uniqueFiles].sort().slice(0, limit);
|
|
4448
4503
|
const overflow = Math.max(0, uniqueFiles.size - limit);
|
|
4504
|
+
const hosts = [
|
|
4505
|
+
...new Set(entries.map((e) => e.host).filter((h) => h !== null))
|
|
4506
|
+
].sort();
|
|
4449
4507
|
return {
|
|
4450
4508
|
generatedAt: input.nowIso,
|
|
4451
4509
|
sessionCount: entries.length,
|
|
@@ -4458,6 +4516,7 @@ async function summarizeOrientation(input) {
|
|
|
4458
4516
|
plannedTasks,
|
|
4459
4517
|
pendingApprovals,
|
|
4460
4518
|
suspects,
|
|
4519
|
+
hosts,
|
|
4461
4520
|
freshness: {
|
|
4462
4521
|
newestStartedAt: newest?.session.session.started_at ?? null,
|
|
4463
4522
|
newestSource: newest?.session.session.source.kind ?? null,
|
|
@@ -4485,11 +4544,15 @@ function formatOrientationBody(summary, opts) {
|
|
|
4485
4544
|
const lines = [];
|
|
4486
4545
|
const now = new Date(summary.generatedAt);
|
|
4487
4546
|
const newestRel = relativeAge(summary.freshness.newestStartedAt ?? void 0, now);
|
|
4547
|
+
const hostSuffix = (h) => h !== null ? ` @${h}` : "";
|
|
4488
4548
|
lines.push("# Orientation");
|
|
4489
4549
|
lines.push("");
|
|
4490
4550
|
lines.push(
|
|
4491
4551
|
`> Generated at ${summary.generatedAt} \xB7 sessions ${summary.sessionCount} \xB7 newest ${newestRel} \xB7 pending ${summary.pendingApprovals.length} \xB7 suspect ${summary.suspects.length}`
|
|
4492
4552
|
);
|
|
4553
|
+
if (summary.hosts.length > 0) {
|
|
4554
|
+
lines.push(`> hosts: local, ${summary.hosts.join(", ")}`);
|
|
4555
|
+
}
|
|
4493
4556
|
lines.push("");
|
|
4494
4557
|
lines.push("## \u4ECA\u3069\u3053\u306B\u3044\u308B");
|
|
4495
4558
|
lines.push("");
|
|
@@ -4497,9 +4560,9 @@ function formatOrientationBody(summary, opts) {
|
|
|
4497
4560
|
const s = summary.latestSession;
|
|
4498
4561
|
const sid = shortId(s.sessionId);
|
|
4499
4562
|
if (s.label !== null && s.label !== "") {
|
|
4500
|
-
lines.push(`- \u6700\u7D42 session: ${s.label} (${s.status}) [${sid}]`);
|
|
4563
|
+
lines.push(`- \u6700\u7D42 session: ${s.label} (${s.status}) [${sid}]${hostSuffix(s.host)}`);
|
|
4501
4564
|
} else {
|
|
4502
|
-
lines.push(`- \u6700\u7D42 session: ${sid} (${s.status})`);
|
|
4565
|
+
lines.push(`- \u6700\u7D42 session: ${sid} (${s.status})${hostSuffix(s.host)}`);
|
|
4503
4566
|
}
|
|
4504
4567
|
} else {
|
|
4505
4568
|
lines.push("- \u6700\u7D42 session: (no live sessions)");
|
|
@@ -4507,7 +4570,9 @@ function formatOrientationBody(summary, opts) {
|
|
|
4507
4570
|
if (summary.latestDecision !== null) {
|
|
4508
4571
|
const dec = summary.latestDecision;
|
|
4509
4572
|
const decAge = relativeAgeJa(dec.occurredAt, now);
|
|
4510
|
-
lines.push(
|
|
4573
|
+
lines.push(
|
|
4574
|
+
`- \u76F4\u8FD1\u306E\u5224\u65AD: ${dec.title} [${shortId(dec.decisionId)}] (${decAge})${hostSuffix(dec.host)}`
|
|
4575
|
+
);
|
|
4511
4576
|
const activityAt = summary.freshness.latestActivityAt;
|
|
4512
4577
|
if (activityAt !== null && isTrailingStale(activityAt, dec.occurredAt)) {
|
|
4513
4578
|
lines.push(
|
|
@@ -4562,7 +4627,9 @@ function formatOrientationBody(summary, opts) {
|
|
|
4562
4627
|
lines.push("- (none)");
|
|
4563
4628
|
} else {
|
|
4564
4629
|
for (const e of summary.suspects) {
|
|
4565
|
-
lines.push(
|
|
4630
|
+
lines.push(
|
|
4631
|
+
`- ${shortId(e.sessionId)} (${e.status}) \u2014 ${suspectText(e.reason)}${hostSuffix(e.host)}`
|
|
4632
|
+
);
|
|
4566
4633
|
}
|
|
4567
4634
|
}
|
|
4568
4635
|
lines.push("");
|
|
@@ -4571,7 +4638,7 @@ function formatOrientationBody(summary, opts) {
|
|
|
4571
4638
|
if (summary.latestNote !== null) {
|
|
4572
4639
|
const noteAge = relativeAgeJa(summary.latestNote.occurredAt, now);
|
|
4573
4640
|
lines.push(
|
|
4574
|
-
`- \u6B21\u306E\u8D77\u70B9 (\u8A18\u9332\u6E08\u307F, ${noteAge}): ${noteSummary(summary.latestNote.body)} [session ${shortId(summary.latestNote.sessionId)}]`
|
|
4641
|
+
`- \u6B21\u306E\u8D77\u70B9 (\u8A18\u9332\u6E08\u307F, ${noteAge}): ${noteSummary(summary.latestNote.body)} [session ${shortId(summary.latestNote.sessionId)}]${hostSuffix(summary.latestNote.host)}`
|
|
4575
4642
|
);
|
|
4576
4643
|
const activityAt = summary.freshness.latestActivityAt;
|
|
4577
4644
|
if (activityAt !== null && isTrailingStale(activityAt, summary.latestNote.occurredAt)) {
|
|
@@ -4601,6 +4668,12 @@ function formatOrientationBody(summary, opts) {
|
|
|
4601
4668
|
lines.push("## \u3053\u308C\u306F\u6700\u65B0\u304B");
|
|
4602
4669
|
lines.push("");
|
|
4603
4670
|
for (const line of freshnessVerdict(summary, opts.staleness, now)) lines.push(line);
|
|
4671
|
+
if (summary.hosts.length > 0) {
|
|
4672
|
+
lines.push("");
|
|
4673
|
+
lines.push(
|
|
4674
|
+
"\u6CE8: \u9BAE\u5EA6\u5224\u5B9A\u306F\u3053\u306E\u30DE\u30B7\u30F3\u306E\u30ED\u30FC\u30AB\u30EB\u30B9\u30C8\u30A2\u306E\u307F\u304C\u5BFE\u8C61\u3067\u3059\u3002\u4ED6\u30DB\u30B9\u30C8\u306E\u53D6\u308A\u3053\u307C\u3057\u306F\u5224\u5B9A\u3067\u304D\u307E\u305B\u3093(\u5404\u30DB\u30B9\u30C8\u3067 basou refresh \u3092\u5B9F\u884C\u3057\u540C\u671F\u3057\u3066\u304F\u3060\u3055\u3044)\u3002"
|
|
4675
|
+
);
|
|
4676
|
+
}
|
|
4604
4677
|
if (opts.verbose) {
|
|
4605
4678
|
lines.push("");
|
|
4606
4679
|
lines.push("<!-- verbose: raw freshness telemetry -->");
|
|
@@ -4677,8 +4750,9 @@ function freshnessVerdict(summary, staleness, now) {
|
|
|
4677
4750
|
"\u6700\u65B0\u304B\u78BA\u8A8D\u3059\u308B\u306B\u306F `basou refresh` \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002"
|
|
4678
4751
|
];
|
|
4679
4752
|
}
|
|
4753
|
+
const localScope = summary.hosts.length > 0 ? "\u3053\u306E\u30DB\u30B9\u30C8(\u30ED\u30FC\u30AB\u30EB)\u306E" : "";
|
|
4680
4754
|
const lines = [
|
|
4681
|
-
`\u2705 \u53D6\u308A\u8FBC\u307F\u306F\u6700\u65B0\u3067\u3059\u3002\u6700\u5F8C\u306E\u4F5C\u696D\u306F ${rel}(${tool})\u3002\u672A\u53D6\u308A\u8FBC\u307F\u306E native \u30BB\u30C3\u30B7\u30E7\u30F3\u306F\u3042\u308A\u307E\u305B\u3093\u3002`
|
|
4755
|
+
`\u2705 ${localScope}\u53D6\u308A\u8FBC\u307F\u306F\u6700\u65B0\u3067\u3059\u3002\u6700\u5F8C\u306E\u4F5C\u696D\u306F ${rel}(${tool})\u3002\u672A\u53D6\u308A\u8FBC\u307F\u306E native \u30BB\u30C3\u30B7\u30E7\u30F3\u306F\u3042\u308A\u307E\u305B\u3093\u3002`
|
|
4682
4756
|
];
|
|
4683
4757
|
if (suspectCount > 0) {
|
|
4684
4758
|
lines.push(`\u305F\u3060\u3057\u8981\u6CE8\u610F\u30BB\u30C3\u30B7\u30E7\u30F3\u304C ${suspectCount} \u4EF6\u3042\u308A\u307E\u3059(\u4E0A\u8A18\u300C\u8981\u6CE8\u610F session\u300D\u53C2\u7167)\u3002`);
|
|
@@ -6505,6 +6579,9 @@ function hasErrorCode5(error) {
|
|
|
6505
6579
|
import { readFile as readFile9 } from "fs/promises";
|
|
6506
6580
|
var GENERATED_START = "<!-- BASOU:GENERATED:START -->";
|
|
6507
6581
|
var GENERATED_END = "<!-- BASOU:GENERATED:END -->";
|
|
6582
|
+
var PROTOCOL_START = "<!-- BASOU:PROTOCOLS:START -->";
|
|
6583
|
+
var PROTOCOL_END = "<!-- BASOU:PROTOCOLS:END -->";
|
|
6584
|
+
var DEFAULT_MARKERS = { start: GENERATED_START, end: GENERATED_END };
|
|
6508
6585
|
async function readMarkdownFile(filePath) {
|
|
6509
6586
|
try {
|
|
6510
6587
|
return await readFile9(filePath, "utf8");
|
|
@@ -6520,13 +6597,15 @@ async function writeMarkdownFile(filePath, body) {
|
|
|
6520
6597
|
throw new Error("Failed to write markdown file", { cause: error });
|
|
6521
6598
|
}
|
|
6522
6599
|
}
|
|
6523
|
-
function parseMarkers(content) {
|
|
6524
|
-
const
|
|
6600
|
+
function parseMarkers(content, markers = DEFAULT_MARKERS) {
|
|
6601
|
+
const bom = content.charCodeAt(0) === 65279 ? "\uFEFF" : "";
|
|
6602
|
+
const body = bom === "" ? content : content.slice(1);
|
|
6603
|
+
const lines = body.split(/\r?\n/);
|
|
6525
6604
|
const startLines = [];
|
|
6526
6605
|
const endLines = [];
|
|
6527
6606
|
for (let i = 0; i < lines.length; i++) {
|
|
6528
|
-
if (lines[i] ===
|
|
6529
|
-
else if (lines[i] ===
|
|
6607
|
+
if (lines[i] === markers.start) startLines.push(i);
|
|
6608
|
+
else if (lines[i] === markers.end) endLines.push(i);
|
|
6530
6609
|
}
|
|
6531
6610
|
if (startLines.length === 0 && endLines.length === 0) return { kind: "no_markers" };
|
|
6532
6611
|
if (startLines.length === 0) return { kind: "missing_start" };
|
|
@@ -6535,30 +6614,30 @@ function parseMarkers(content) {
|
|
|
6535
6614
|
const startLineIdx = startLines[0];
|
|
6536
6615
|
const endLineIdx = endLines[0];
|
|
6537
6616
|
if (endLineIdx < startLineIdx) return { kind: "wrong_order" };
|
|
6538
|
-
const startOffset = lineStartOffset(
|
|
6539
|
-
const endLineStart = lineStartOffset(
|
|
6540
|
-
const startLineEnd = startOffset +
|
|
6541
|
-
const endLineEnd = endLineStart +
|
|
6542
|
-
const before =
|
|
6543
|
-
const afterStartNewline = skipOneNewline(
|
|
6544
|
-
const beforeEndNewline = trimOneNewline(
|
|
6545
|
-
const generated =
|
|
6546
|
-
const after =
|
|
6617
|
+
const startOffset = lineStartOffset(body, startLineIdx);
|
|
6618
|
+
const endLineStart = lineStartOffset(body, endLineIdx);
|
|
6619
|
+
const startLineEnd = startOffset + markers.start.length;
|
|
6620
|
+
const endLineEnd = endLineStart + markers.end.length;
|
|
6621
|
+
const before = bom + body.slice(0, startOffset);
|
|
6622
|
+
const afterStartNewline = skipOneNewline(body, startLineEnd);
|
|
6623
|
+
const beforeEndNewline = trimOneNewline(body, endLineStart);
|
|
6624
|
+
const generated = body.slice(afterStartNewline, beforeEndNewline);
|
|
6625
|
+
const after = body.slice(endLineEnd);
|
|
6547
6626
|
return { kind: "ok", before, generated, after };
|
|
6548
6627
|
}
|
|
6549
|
-
function renderWithMarkers(existing, generated, fileLabel) {
|
|
6628
|
+
function renderWithMarkers(existing, generated, fileLabel, markers = DEFAULT_MARKERS) {
|
|
6550
6629
|
const normalized = generated.endsWith("\n") ? generated : `${generated}
|
|
6551
6630
|
`;
|
|
6552
6631
|
if (existing === null) {
|
|
6553
|
-
return `${
|
|
6554
|
-
${normalized}${
|
|
6632
|
+
return `${markers.start}
|
|
6633
|
+
${normalized}${markers.end}
|
|
6555
6634
|
`;
|
|
6556
6635
|
}
|
|
6557
|
-
const section = parseMarkers(existing);
|
|
6636
|
+
const section = parseMarkers(existing, markers);
|
|
6558
6637
|
switch (section.kind) {
|
|
6559
6638
|
case "ok":
|
|
6560
|
-
return `${section.before}${
|
|
6561
|
-
${normalized}${
|
|
6639
|
+
return `${section.before}${markers.start}
|
|
6640
|
+
${normalized}${markers.end}${section.after}`;
|
|
6562
6641
|
case "no_markers":
|
|
6563
6642
|
throw new Error(`Markers missing in ${fileLabel}`);
|
|
6564
6643
|
case "missing_start":
|
|
@@ -6568,6 +6647,22 @@ ${normalized}${GENERATED_END}${section.after}`;
|
|
|
6568
6647
|
throw new Error(`Markers mismatched in ${fileLabel}`);
|
|
6569
6648
|
}
|
|
6570
6649
|
}
|
|
6650
|
+
function removeMarkerSection(existing, fileLabel, markers = DEFAULT_MARKERS) {
|
|
6651
|
+
const section = parseMarkers(existing, markers);
|
|
6652
|
+
switch (section.kind) {
|
|
6653
|
+
case "no_markers":
|
|
6654
|
+
return existing;
|
|
6655
|
+
case "ok": {
|
|
6656
|
+
const after = section.after.replace(/^\r?\n/, "");
|
|
6657
|
+
return section.before + after;
|
|
6658
|
+
}
|
|
6659
|
+
case "missing_start":
|
|
6660
|
+
case "missing_end":
|
|
6661
|
+
case "multiple_pairs":
|
|
6662
|
+
case "wrong_order":
|
|
6663
|
+
throw new Error(`Markers mismatched in ${fileLabel}`);
|
|
6664
|
+
}
|
|
6665
|
+
}
|
|
6571
6666
|
function lineStartOffset(content, lineIdx) {
|
|
6572
6667
|
if (lineIdx === 0) return 0;
|
|
6573
6668
|
let offset = 0;
|
|
@@ -7008,6 +7103,8 @@ export {
|
|
|
7008
7103
|
IsoTimestampSchema,
|
|
7009
7104
|
JSON_SCHEMA_VERSION,
|
|
7010
7105
|
ManifestSchema,
|
|
7106
|
+
PROTOCOL_END,
|
|
7107
|
+
PROTOCOL_START,
|
|
7011
7108
|
RiskLevelSchema,
|
|
7012
7109
|
STUCK_THRESHOLD_MS,
|
|
7013
7110
|
SchemaVersionSchema,
|
|
@@ -7070,6 +7167,7 @@ export {
|
|
|
7070
7167
|
lineHash,
|
|
7071
7168
|
linkYamlFile,
|
|
7072
7169
|
loadApproval,
|
|
7170
|
+
loadFederatedSessionEntries,
|
|
7073
7171
|
loadSessionEntries,
|
|
7074
7172
|
loadTaskEntries,
|
|
7075
7173
|
normalizeRepoKey,
|
|
@@ -7098,6 +7196,7 @@ export {
|
|
|
7098
7196
|
reconcileTask,
|
|
7099
7197
|
refreshTaskLinkedSessions,
|
|
7100
7198
|
reimportPreservingId,
|
|
7199
|
+
removeMarkerSection,
|
|
7101
7200
|
renderDecisions,
|
|
7102
7201
|
renderHandoff,
|
|
7103
7202
|
renderOrientation,
|