@basou/core 0.6.0 → 0.8.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 CHANGED
@@ -89,6 +89,9 @@ declare const ManifestSchema: z.ZodObject<{
89
89
  commit: "commit";
90
90
  }>>;
91
91
  }, z.core.$strip>;
92
+ import: z.ZodOptional<z.ZodObject<{
93
+ source_roots: z.ZodOptional<z.ZodArray<z.ZodString>>;
94
+ }, z.core.$strip>>;
92
95
  }, z.core.$strip>;
93
96
  /** Inferred runtime type for {@link ManifestSchema}. */
94
97
  type Manifest = z.infer<typeof ManifestSchema>;
@@ -101,14 +104,15 @@ declare const SessionInnerImportSchema: z.ZodObject<{
101
104
  source: z.ZodObject<{
102
105
  kind: z.ZodEnum<{
103
106
  "claude-code-adapter": "claude-code-adapter";
107
+ import: "import";
104
108
  "claude-code-import": "claude-code-import";
105
109
  "codex-import": "codex-import";
106
110
  human: "human";
107
- import: "import";
108
111
  terminal: "terminal";
109
112
  }>;
110
113
  version: z.ZodLiteral<"0.1.0">;
111
114
  external_id: z.ZodOptional<z.ZodString>;
115
+ source_size_bytes: z.ZodOptional<z.ZodNumber>;
112
116
  }, z.core.$strip>;
113
117
  started_at: z.ZodString;
114
118
  ended_at: z.ZodOptional<z.ZodString>;
@@ -161,14 +165,15 @@ declare const SessionImportPayloadSchema: z.ZodObject<{
161
165
  source: z.ZodObject<{
162
166
  kind: z.ZodEnum<{
163
167
  "claude-code-adapter": "claude-code-adapter";
168
+ import: "import";
164
169
  "claude-code-import": "claude-code-import";
165
170
  "codex-import": "codex-import";
166
171
  human: "human";
167
- import: "import";
168
172
  terminal: "terminal";
169
173
  }>;
170
174
  version: z.ZodLiteral<"0.1.0">;
171
175
  external_id: z.ZodOptional<z.ZodString>;
176
+ source_size_bytes: z.ZodOptional<z.ZodNumber>;
172
177
  }, z.core.$strip>;
173
178
  started_at: z.ZodString;
174
179
  ended_at: z.ZodOptional<z.ZodString>;
@@ -448,6 +453,14 @@ type ClaudeTranscriptToPayloadOptions = {
448
453
  * back to the `sessionId` read from the records when omitted.
449
454
  */
450
455
  externalId?: string;
456
+ /**
457
+ * Byte size of the source transcript that produced `records`, stored as
458
+ * `session.source.source_size_bytes` so a later import can detect growth and
459
+ * re-import the session. The caller passes the size of the buffer it actually
460
+ * read (an immutable snapshot of the parsed bytes), so the stored size always
461
+ * matches the imported content. Omitted => the field is not recorded.
462
+ */
463
+ sourceSizeBytes?: number;
451
464
  };
452
465
  /**
453
466
  * Transform a Claude Code native transcript into a Basou
@@ -503,6 +516,14 @@ type CodexRolloutToPayloadOptions = {
503
516
  * to the id read from the rollout's `session_meta` record when omitted.
504
517
  */
505
518
  externalId?: string;
519
+ /**
520
+ * Byte size of the source rollout that produced `records`, stored as
521
+ * `session.source.source_size_bytes` so a later import can detect growth and
522
+ * re-import the session. The caller passes the size of the buffer it actually
523
+ * read (an immutable snapshot of the parsed bytes), so the stored size always
524
+ * matches the imported content. Omitted => the field is not recorded.
525
+ */
526
+ sourceSizeBytes?: number;
506
527
  };
507
528
  /**
508
529
  * Transform a Codex native rollout log into a Basou {@link SessionImportPayload},
@@ -1271,10 +1292,10 @@ type SessionStatus = z.infer<typeof SessionStatusSchema>;
1271
1292
  */
1272
1293
  declare const SessionSourceKindSchema: z.ZodEnum<{
1273
1294
  "claude-code-adapter": "claude-code-adapter";
1295
+ import: "import";
1274
1296
  "claude-code-import": "claude-code-import";
1275
1297
  "codex-import": "codex-import";
1276
1298
  human: "human";
1277
- import: "import";
1278
1299
  terminal: "terminal";
1279
1300
  }>;
1280
1301
  /** Inferred runtime type for {@link SessionSourceKindSchema}. */
@@ -1339,14 +1360,15 @@ declare const SessionSchema: z.ZodObject<{
1339
1360
  source: z.ZodObject<{
1340
1361
  kind: z.ZodEnum<{
1341
1362
  "claude-code-adapter": "claude-code-adapter";
1363
+ import: "import";
1342
1364
  "claude-code-import": "claude-code-import";
1343
1365
  "codex-import": "codex-import";
1344
1366
  human: "human";
1345
- import: "import";
1346
1367
  terminal: "terminal";
1347
1368
  }>;
1348
1369
  version: z.ZodLiteral<"0.1.0">;
1349
1370
  external_id: z.ZodOptional<z.ZodString>;
1371
+ source_size_bytes: z.ZodOptional<z.ZodNumber>;
1350
1372
  }, z.core.$strip>;
1351
1373
  started_at: z.ZodString;
1352
1374
  ended_at: z.ZodOptional<z.ZodString>;
@@ -3146,6 +3168,14 @@ type CreateManifestInput = {
3146
3168
  now?: Date;
3147
3169
  /** Override for tests; defaults to a freshly generated `ws_<ULID>`. */
3148
3170
  workspaceId?: PrefixedId<"ws">;
3171
+ /**
3172
+ * Import source roots, each RELATIVE to the repository root (e.g. `"."`,
3173
+ * `"../basou-workspace"`). Persisted under `import.source_roots` so
3174
+ * `basou refresh` / `basou import` aggregate several sibling repos into one
3175
+ * `.basou/`. Validated by `ManifestSchema` (absolute paths are rejected).
3176
+ * Omitted from the manifest entirely when absent or empty.
3177
+ */
3178
+ sourceRoots?: string[];
3149
3179
  };
3150
3180
  /**
3151
3181
  * Build a fresh Manifest object that satisfies the manifest schema's
@@ -3295,6 +3325,44 @@ type ImportSessionResult = {
3295
3325
  * for `--verbose` rendering.
3296
3326
  */
3297
3327
  declare function importSessionFromJson(paths: BasouPaths, manifest: Manifest, payload: SessionImportPayload, options: ImportSessionOptions): Promise<ImportSessionResult>;
3328
+ /** Whether `source` is one of the known import-derived event sources. */
3329
+ declare function isImportDerivedSource(source: string): boolean;
3330
+ /** Options for {@link reimportPreservingId}. */
3331
+ type ReimportOptions = {
3332
+ /** Compute the re-import and return its preview without writing to disk. */
3333
+ dryRun?: boolean;
3334
+ };
3335
+ /** Result of {@link reimportPreservingId}. */
3336
+ type ReimportResult = {
3337
+ status: "reimported";
3338
+ sessionId: PrefixedId<"ses">;
3339
+ /** Total events written to the merged `events.jsonl`. */
3340
+ eventCount: number;
3341
+ /** Non-derived events (human / unknown source) carried over unchanged. */
3342
+ preservedCount: number;
3343
+ /** Derived events whose prior id (and decision_id) was reused. */
3344
+ reusedIdCount: number;
3345
+ } | {
3346
+ status: "skipped";
3347
+ reason: "prior_events_unreadable" | "prior_derived_dropped";
3348
+ };
3349
+ /**
3350
+ * Re-import a source whose native log GREW into the SAME Basou session,
3351
+ * preserving its id and any non-derived events, instead of skipping it (default
3352
+ * dedup) or deleting + recreating it (`--force`). The caller has already
3353
+ * validated `freshPayload` and confirmed (by source byte size) that the source
3354
+ * changed; this function re-derives the adapter's events, reuses prior derived
3355
+ * event ids for unchanged derivations (so `linked_events` references survive),
3356
+ * preserves human / unknown-source events, and rewrites `events.jsonl` +
3357
+ * `session.yaml` atomically under the session lock.
3358
+ *
3359
+ * The whole read-modify-write runs under {@link acquireLock} so a concurrent
3360
+ * writer cannot interleave; `dryRun` computes the result and writes nothing
3361
+ * (and takes no lock). If the prior `events.jsonl` has any line that cannot be
3362
+ * preserved (malformed / schema-invalid / half-written), the re-import is
3363
+ * ABORTED (`status: "skipped"`) rather than risk dropping data on the rewrite.
3364
+ */
3365
+ declare function reimportPreservingId(paths: BasouPaths, manifest: Manifest, priorSessionId: string, freshPayload: SessionImportPayload, options?: ReimportOptions): Promise<ReimportResult>;
3298
3366
 
3299
3367
  /**
3300
3368
  * Walk the cause chain (up to `depth` levels) looking for an Error whose
@@ -3396,4 +3464,4 @@ declare function overwriteYamlFile(filePath: string, value: unknown): Promise<vo
3396
3464
  */
3397
3465
  declare const BASOU_CORE_VERSION = "0.1.0";
3398
3466
 
3399
- export { ACTIVE_GAP_CAP_MS, type ActiveTimeBasis, type AdapterOutputEvent, type AppendBasouGitignoreResult, type AppendEventToExistingInput, type AppendEventToExistingResult, type Approval, type ApprovalApprovedEvent, type ApprovalExpiredEvent, ApprovalIdSchema, type ApprovalLocation, type ApprovalRejectedEvent, type ApprovalRequestedEvent, ApprovalSchema, type ApprovalStatus, ApprovalStatusSchema, type ArchiveTaskInput, type ArchiveTaskResult, type AttachTaskInput, type AttachUpdateTaskStatusInput, type AttachableStatus, BASOU_CORE_VERSION, type BasouPaths, CLAUDE_IMPORT_SOURCE, CODEX_IMPORT_SOURCE, type CaptureMode, ChildProcessRunner, 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, FailedToFinalizeError, type FileChange, type FileChangeStatus, type FileChangedEvent, GENERATED_END, GENERATED_START, type GitSnapshot, type GitSnapshotEvent, type HandoffRendererInput, type HandoffRendererResult, ID_PREFIXES, type IdPrefix, type ImportSessionOptions, type ImportSessionResult, 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 PrefixedId, type ProcessRunner, type ReconcileAllResult, type ReconcileAllTasksInput, type ReconcileAllTasksOptions, type ReconcileFailure, type ReconcileResult, type ReconcileTaskInput, type RefreshLinkageInput, type RefreshLinkageResult, type ReplayOptions, type ReplayWarning, type RiskLevel, RiskLevelSchema, 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 SessionMetrics, SessionMetricsSchema, SessionSchema, type SessionSkipReason, type SessionSourceKind, SessionSourceKindSchema, type SessionStartedEvent, type SessionStatus, type SessionStatusChangedEvent, SessionStatusSchema, type SessionWorkStats, type SourceWorkStats, type StatusCount, StatusSchema, type StatusSnapshot, type SuspectReason, type Task, type TaskArchivedEvent, type TaskCreatedEvent, type TaskDeletedEvent, type TaskDocument, TaskIdSchema, type TaskLinkageRefreshedEvent, type TaskReconciledEvent, TaskSchema, type TaskSkipReason, type TaskStatus, type TaskStatusChangedEvent, TaskStatusSchema, TaskWriteAfterEventError, type TaskWriteAfterEventPhase, type TokenTotals, type UpdateAdHocTaskStatusInput, type UpdateTaskStatusInput, type UpdateTaskStatusResult, type WorkStatsInput, type WorkStatsResult, type WorkStatsTotals, WorkspaceIdSchema, type WriteTaskFileMode, acquireLock, appendBasouGitignore, appendEvent, appendEventToExistingSession, archiveTask, assertBasouRootSafe, basouPaths, buildJsonSchemas, buildStatusSnapshot, classifySuspect, claudeCodeAdapterMetadata, claudeTranscriptToImportPayload, codexRolloutToImportPayload, computeWorkStats, createAdHocSessionWithEvent, createManifest, createTaskWithEvent, deleteTask, editTask, ensureBasouDirectory, enumerateApprovals, enumerateArchivedTaskIds, enumerateSessionDirs, enumerateTaskIds, findErrorCode, getDiff, getSnapshot, importSessionFromJson, isLazyExpired, isValidPrefixedId, linkYamlFile, loadApproval, loadSessionEntries, loadTaskEntries, overwriteYamlFile, parseDuration, parseMarkers, prefixedUlid, readAllEvents, readManifest, readMarkdownFile, readSessionYaml, readStatus, readTaskFile, readTaskFileWithArchiveFallback, readYamlFile, reconcileAllTasks, reconcileTask, refreshTaskLinkedSessions, renderDecisions, renderHandoff, renderWithMarkers, replayEvents, resolveClaudeCodeCommand, resolveRepositoryRoot, resolveSessionId, resolveTaskId, sanitizePath, sanitizeRelatedFiles, sanitizeWorkingDirectory, serializeJsonSchema, sessionWorkStatsFromEvents, summarizeAdapterOutput, tryRemoteUrl, ulid, updateTaskStatusWithEvent, writeEventsBulk, writeManifest, writeMarkdownFile, writeStatus, writeTaskFile, writeYamlFile };
3467
+ export { ACTIVE_GAP_CAP_MS, type ActiveTimeBasis, type AdapterOutputEvent, type AppendBasouGitignoreResult, type AppendEventToExistingInput, type AppendEventToExistingResult, type Approval, type ApprovalApprovedEvent, type ApprovalExpiredEvent, ApprovalIdSchema, type ApprovalLocation, type ApprovalRejectedEvent, type ApprovalRequestedEvent, ApprovalSchema, type ApprovalStatus, ApprovalStatusSchema, type ArchiveTaskInput, type ArchiveTaskResult, type AttachTaskInput, type AttachUpdateTaskStatusInput, type AttachableStatus, BASOU_CORE_VERSION, type BasouPaths, CLAUDE_IMPORT_SOURCE, CODEX_IMPORT_SOURCE, type CaptureMode, ChildProcessRunner, 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, FailedToFinalizeError, type FileChange, type FileChangeStatus, type FileChangedEvent, GENERATED_END, GENERATED_START, type GitSnapshot, type GitSnapshotEvent, type HandoffRendererInput, type HandoffRendererResult, ID_PREFIXES, type IdPrefix, type ImportSessionOptions, type ImportSessionResult, 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 PrefixedId, type ProcessRunner, type ReconcileAllResult, type ReconcileAllTasksInput, type ReconcileAllTasksOptions, type ReconcileFailure, type ReconcileResult, type ReconcileTaskInput, type RefreshLinkageInput, type RefreshLinkageResult, type ReimportOptions, type ReimportResult, type ReplayOptions, type ReplayWarning, type RiskLevel, RiskLevelSchema, 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 SessionMetrics, SessionMetricsSchema, SessionSchema, type SessionSkipReason, type SessionSourceKind, SessionSourceKindSchema, type SessionStartedEvent, type SessionStatus, type SessionStatusChangedEvent, SessionStatusSchema, type SessionWorkStats, type SourceWorkStats, type StatusCount, StatusSchema, type StatusSnapshot, type SuspectReason, type Task, type TaskArchivedEvent, type TaskCreatedEvent, type TaskDeletedEvent, type TaskDocument, TaskIdSchema, type TaskLinkageRefreshedEvent, type TaskReconciledEvent, TaskSchema, type TaskSkipReason, type TaskStatus, type TaskStatusChangedEvent, TaskStatusSchema, TaskWriteAfterEventError, type TaskWriteAfterEventPhase, type TokenTotals, type UpdateAdHocTaskStatusInput, type UpdateTaskStatusInput, type UpdateTaskStatusResult, type WorkStatsInput, type WorkStatsResult, type WorkStatsTotals, WorkspaceIdSchema, type WriteTaskFileMode, acquireLock, appendBasouGitignore, appendEvent, appendEventToExistingSession, archiveTask, assertBasouRootSafe, basouPaths, buildJsonSchemas, buildStatusSnapshot, classifySuspect, claudeCodeAdapterMetadata, claudeTranscriptToImportPayload, codexRolloutToImportPayload, computeWorkStats, createAdHocSessionWithEvent, createManifest, createTaskWithEvent, deleteTask, editTask, ensureBasouDirectory, enumerateApprovals, enumerateArchivedTaskIds, enumerateSessionDirs, enumerateTaskIds, findErrorCode, getDiff, getSnapshot, importSessionFromJson, isImportDerivedSource, isLazyExpired, isValidPrefixedId, linkYamlFile, loadApproval, loadSessionEntries, loadTaskEntries, overwriteYamlFile, parseDuration, parseMarkers, prefixedUlid, readAllEvents, readManifest, readMarkdownFile, readSessionYaml, readStatus, readTaskFile, readTaskFileWithArchiveFallback, readYamlFile, reconcileAllTasks, reconcileTask, refreshTaskLinkedSessions, reimportPreservingId, renderDecisions, renderHandoff, renderWithMarkers, replayEvents, resolveClaudeCodeCommand, resolveRepositoryRoot, resolveSessionId, resolveTaskId, sanitizePath, sanitizeRelatedFiles, sanitizeWorkingDirectory, serializeJsonSchema, sessionWorkStatsFromEvents, summarizeAdapterOutput, tryRemoteUrl, ulid, updateTaskStatusWithEvent, writeEventsBulk, writeManifest, writeMarkdownFile, writeStatus, writeTaskFile, writeYamlFile };
package/dist/index.js CHANGED
@@ -224,7 +224,8 @@ function claudeTranscriptToImportPayload(records, options) {
224
224
  source: {
225
225
  kind: CLAUDE_IMPORT_SOURCE,
226
226
  version: "0.1.0",
227
- ...externalId !== void 0 ? { external_id: externalId } : {}
227
+ ...externalId !== void 0 ? { external_id: externalId } : {},
228
+ ...options.sourceSizeBytes !== void 0 ? { source_size_bytes: options.sourceSizeBytes } : {}
228
229
  },
229
230
  started_at: minTs,
230
231
  ended_at: maxTs,
@@ -457,7 +458,8 @@ function codexRolloutToImportPayload(records, options) {
457
458
  source: {
458
459
  kind: CODEX_IMPORT_SOURCE,
459
460
  version: "0.1.0",
460
- ...externalId !== void 0 ? { external_id: externalId } : {}
461
+ ...externalId !== void 0 ? { external_id: externalId } : {},
462
+ ...options.sourceSizeBytes !== void 0 ? { source_size_bytes: options.sourceSizeBytes } : {}
461
463
  },
462
464
  started_at: minTs,
463
465
  ended_at: maxTs,
@@ -1032,7 +1034,15 @@ var SessionSourceSchema = z4.object({
1032
1034
  // Optional id of the originating session in the SOURCE tool's own
1033
1035
  // namespace (e.g. the Claude Code session UUID for a `claude-code-import`).
1034
1036
  // Lets re-imports of the same source be deduplicated; absent for live runs.
1035
- external_id: z4.string().optional()
1037
+ external_id: z4.string().optional(),
1038
+ // Byte size of the source native log at import time, recorded so a later
1039
+ // import can detect that an append-only transcript GREW and re-import it
1040
+ // (scoped, preserving the session id) instead of skipping it as already
1041
+ // imported. Additive optional => no schema_version bump (precedent:
1042
+ // external_id, metrics). Absent on sessions imported before this field
1043
+ // existed (treated as legacy: never auto-re-imported, populated on the next
1044
+ // fresh import or `--force`).
1045
+ source_size_bytes: z4.number().int().nonnegative().optional()
1036
1046
  });
1037
1047
  var InvocationSchema = z4.object({
1038
1048
  command: z4.string().min(1),
@@ -3885,6 +3895,13 @@ var AdaptersSchema = z9.object({
3885
3895
  var GitConfigSchema = z9.object({
3886
3896
  events_log: z9.enum(["ignore", "commit"]).default("ignore")
3887
3897
  });
3898
+ var SOURCE_ROOT_PATTERN = /^(?![~/\\])(?![A-Za-z]:)[^\0\\]+$/;
3899
+ var SourceRootSchema = z9.string().min(1).regex(SOURCE_ROOT_PATTERN, {
3900
+ message: "source_roots entries must be relative paths (no absolute path, '~', '\\', or null byte)"
3901
+ });
3902
+ var ImportConfigSchema = z9.object({
3903
+ source_roots: z9.array(SourceRootSchema).min(1).optional()
3904
+ });
3888
3905
  var WorkspaceMetaSchema = z9.object({
3889
3906
  id: WorkspaceIdSchema,
3890
3907
  name: z9.string().min(1),
@@ -3899,7 +3916,8 @@ var ManifestSchema = z9.object({
3899
3916
  capabilities: CapabilitiesSchema,
3900
3917
  approval: ApprovalConfigSchema,
3901
3918
  adapters: AdaptersSchema,
3902
- git: GitConfigSchema
3919
+ git: GitConfigSchema,
3920
+ import: ImportConfigSchema.optional()
3903
3921
  });
3904
3922
 
3905
3923
  // src/schemas/session-import.schema.ts
@@ -3914,7 +3932,13 @@ var SessionInnerImportSchema = z10.object({
3914
3932
  version: z10.literal("0.1.0"),
3915
3933
  // Source-tool-native id (e.g. Claude Code session UUID), retained so
3916
3934
  // re-imports of the same source can be deduplicated.
3917
- external_id: z10.string().optional()
3935
+ external_id: z10.string().optional(),
3936
+ // Byte size of the source native log at import time. Declared here too
3937
+ // (not only in session.schema.ts) because this inner `source` object is
3938
+ // a plain z.object: zod strips keys it does not declare, so a field
3939
+ // absent here would be dropped from the parsed payload before persist
3940
+ // and the size could never be stored.
3941
+ source_size_bytes: z10.number().int().nonnegative().optional()
3918
3942
  }),
3919
3943
  started_at: IsoTimestampSchema,
3920
3944
  ended_at: IsoTimestampSchema.optional(),
@@ -3987,7 +4011,7 @@ var DOCUMENTS = [
3987
4011
  name: "session-import",
3988
4012
  schema: SessionImportPayloadSchema,
3989
4013
  title: "Basou Session Import Payload",
3990
- description: "The portable session payload consumed by `basou session import` (and produced by `basou session export`)."
4014
+ description: "The portable session payload consumed by `basou session import`."
3991
4015
  }
3992
4016
  ];
3993
4017
  function buildJsonSchemas() {
@@ -4454,7 +4478,8 @@ function createManifest(input) {
4454
4478
  adapters: {
4455
4479
  "claude-code": { enabled: true }
4456
4480
  },
4457
- git: { events_log: "ignore" }
4481
+ git: { events_log: "ignore" },
4482
+ ...input.sourceRoots !== void 0 && input.sourceRoots.length > 0 ? { import: { source_roots: input.sourceRoots } } : {}
4458
4483
  };
4459
4484
  return ManifestSchema.parse(manifest);
4460
4485
  }
@@ -4722,6 +4747,130 @@ function buildSessionRecord(input, manifest, newSessionId, options) {
4722
4747
  }
4723
4748
  };
4724
4749
  }
4750
+ var IMPORT_DERIVED_SOURCES = /* @__PURE__ */ new Set([
4751
+ "claude-code-import",
4752
+ "codex-import"
4753
+ ]);
4754
+ function isImportDerivedSource(source) {
4755
+ return IMPORT_DERIVED_SOURCES.has(source);
4756
+ }
4757
+ function derivedEventContentKey(event) {
4758
+ const base = `${event.type}\0${event.occurred_at}`;
4759
+ switch (event.type) {
4760
+ case "command_executed":
4761
+ return `${base}\0${event.command}\0${event.args.join("")}\0${event.cwd}`;
4762
+ case "file_changed":
4763
+ return `${base}\0${event.path}\0${event.change_type}`;
4764
+ case "decision_recorded":
4765
+ return `${base}\0${event.title}`;
4766
+ default:
4767
+ return base;
4768
+ }
4769
+ }
4770
+ function reuseDerivedIds(priorDerived, freshDerived, sessionId) {
4771
+ const priorStarted = priorDerived.find((e) => e.type === "session_started");
4772
+ const priorEnded = priorDerived.find((e) => e.type === "session_ended");
4773
+ let startedUsed = false;
4774
+ let endedUsed = false;
4775
+ const middleByKey = /* @__PURE__ */ new Map();
4776
+ for (const e of priorDerived) {
4777
+ if (e.type === "session_started" || e.type === "session_ended") continue;
4778
+ const key = derivedEventContentKey(e);
4779
+ const list = middleByKey.get(key);
4780
+ if (list === void 0) middleByKey.set(key, [e]);
4781
+ else list.push(e);
4782
+ }
4783
+ let reusedIdCount = 0;
4784
+ const withReusedId = (fresh, prior) => {
4785
+ reusedIdCount++;
4786
+ if (fresh.type === "decision_recorded" && prior.type === "decision_recorded") {
4787
+ return { ...fresh, id: prior.id, session_id: sessionId, decision_id: prior.decision_id };
4788
+ }
4789
+ return { ...fresh, id: prior.id, session_id: sessionId };
4790
+ };
4791
+ const events = freshDerived.map((fresh) => {
4792
+ if (fresh.type === "session_started") {
4793
+ if (priorStarted !== void 0) {
4794
+ startedUsed = true;
4795
+ return withReusedId(fresh, priorStarted);
4796
+ }
4797
+ return { ...fresh, id: prefixedUlid("evt"), session_id: sessionId };
4798
+ }
4799
+ if (fresh.type === "session_ended") {
4800
+ if (priorEnded !== void 0) {
4801
+ endedUsed = true;
4802
+ return withReusedId(fresh, priorEnded);
4803
+ }
4804
+ return { ...fresh, id: prefixedUlid("evt"), session_id: sessionId };
4805
+ }
4806
+ const match = middleByKey.get(derivedEventContentKey(fresh))?.shift();
4807
+ if (match !== void 0) return withReusedId(fresh, match);
4808
+ return { ...fresh, id: prefixedUlid("evt"), session_id: sessionId };
4809
+ });
4810
+ const droppedPriorDerived = priorStarted !== void 0 && !startedUsed || priorEnded !== void 0 && !endedUsed || [...middleByKey.values()].some((q) => q.length > 0);
4811
+ return { events, reusedIdCount, droppedPriorDerived };
4812
+ }
4813
+ async function reimportPreservingId(paths, manifest, priorSessionId, freshPayload, options = {}) {
4814
+ const sessionId = priorSessionId;
4815
+ const importSource = freshPayload.session.source.kind;
4816
+ const sessionDir = join14(paths.sessions, priorSessionId);
4817
+ const lock = options.dryRun === true ? null : await acquireLock(paths, "session", priorSessionId);
4818
+ try {
4819
+ let priorUnreadable = false;
4820
+ const priorEvents = await readAllEvents(sessionDir, {
4821
+ onWarning: () => {
4822
+ priorUnreadable = true;
4823
+ }
4824
+ });
4825
+ if (priorUnreadable) {
4826
+ return { status: "skipped", reason: "prior_events_unreadable" };
4827
+ }
4828
+ const priorDerived = priorEvents.filter((e) => e.source === importSource);
4829
+ const preserved = priorEvents.filter((e) => e.source !== importSource);
4830
+ const {
4831
+ events: rederived,
4832
+ reusedIdCount,
4833
+ droppedPriorDerived
4834
+ } = reuseDerivedIds(priorDerived, freshPayload.events, sessionId);
4835
+ if (droppedPriorDerived) {
4836
+ return { status: "skipped", reason: "prior_derived_dropped" };
4837
+ }
4838
+ const mergedEvents = [...rederived, ...preserved].sort(
4839
+ (a, b) => Date.parse(a.occurred_at) - Date.parse(b.occurred_at)
4840
+ );
4841
+ assertChronologicalOrder(mergedEvents);
4842
+ const prior = await readSessionYaml(paths, priorSessionId);
4843
+ const { record } = buildSessionRecord(freshPayload.session, manifest, sessionId, {});
4844
+ const preservedInner = {
4845
+ ...record.session,
4846
+ // A human may have linked this imported session to a task
4847
+ // (`basou task link` updates session.yaml.task_id even for imported
4848
+ // sessions); never drop that link on a re-derive.
4849
+ task_id: prior.session.task_id ?? null,
4850
+ // Re-derivation always yields a null summary; keep a prior non-null one.
4851
+ summary: prior.session.summary ?? record.session.summary ?? null
4852
+ };
4853
+ const updatedRecord = { schema_version: "0.1.0", session: preservedInner };
4854
+ if (options.dryRun !== true) {
4855
+ await writeEventsBulk(sessionDir, mergedEvents);
4856
+ try {
4857
+ await overwriteYamlFile(join14(sessionDir, "session.yaml"), updatedRecord);
4858
+ } catch (error) {
4859
+ await writeEventsBulk(sessionDir, priorEvents).catch(() => void 0);
4860
+ throw error;
4861
+ }
4862
+ }
4863
+ return {
4864
+ status: "reimported",
4865
+ sessionId,
4866
+ eventCount: mergedEvents.length,
4867
+ preservedCount: preserved.length,
4868
+ reusedIdCount
4869
+ };
4870
+ } finally {
4871
+ await lock?.release();
4872
+ }
4873
+ }
4725
4874
 
4726
4875
  // src/index.ts
4727
4876
  var BASOU_CORE_VERSION = "0.1.0";
@@ -4789,6 +4938,7 @@ export {
4789
4938
  getDiff,
4790
4939
  getSnapshot,
4791
4940
  importSessionFromJson,
4941
+ isImportDerivedSource,
4792
4942
  isLazyExpired,
4793
4943
  isValidPrefixedId,
4794
4944
  linkYamlFile,
@@ -4810,6 +4960,7 @@ export {
4810
4960
  reconcileAllTasks,
4811
4961
  reconcileTask,
4812
4962
  refreshTaskLinkedSessions,
4963
+ reimportPreservingId,
4813
4964
  renderDecisions,
4814
4965
  renderHandoff,
4815
4966
  renderWithMarkers,