@basou/core 0.5.0 → 0.7.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,10 +104,10 @@ 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">;
@@ -143,6 +146,7 @@ declare const SessionInnerImportSchema: z.ZodObject<{
143
146
  }, z.core.$strip>>>;
144
147
  active_gap_cap_ms: z.ZodOptional<z.ZodNumber>;
145
148
  active_time_method: z.ZodOptional<z.ZodString>;
149
+ machine_active_time_ms: z.ZodOptional<z.ZodNumber>;
146
150
  }, z.core.$strip>>;
147
151
  }, z.core.$strict>;
148
152
  /**
@@ -160,10 +164,10 @@ declare const SessionImportPayloadSchema: z.ZodObject<{
160
164
  source: z.ZodObject<{
161
165
  kind: z.ZodEnum<{
162
166
  "claude-code-adapter": "claude-code-adapter";
167
+ import: "import";
163
168
  "claude-code-import": "claude-code-import";
164
169
  "codex-import": "codex-import";
165
170
  human: "human";
166
- import: "import";
167
171
  terminal: "terminal";
168
172
  }>;
169
173
  version: z.ZodLiteral<"0.1.0">;
@@ -202,6 +206,7 @@ declare const SessionImportPayloadSchema: z.ZodObject<{
202
206
  }, z.core.$strip>>>;
203
207
  active_gap_cap_ms: z.ZodOptional<z.ZodNumber>;
204
208
  active_time_method: z.ZodOptional<z.ZodString>;
209
+ machine_active_time_ms: z.ZodOptional<z.ZodNumber>;
205
210
  }, z.core.$strip>>;
206
211
  }, z.core.$strict>;
207
212
  events: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
@@ -516,6 +521,13 @@ type CodexRolloutToPayloadOptions = {
516
521
  * are parsed from the paired `function_call_output` (matched by `call_id`),
517
522
  * whose text carries `Process exited with code N` and `Wall time: X seconds`.
518
523
  *
524
+ * Per-session `metrics` are also derived: token totals from the cumulative
525
+ * `token_count` events; active time from the real `task_started` ->
526
+ * `task_complete` turn spans (in-turn, uncapped) unioned with the gap-capped
527
+ * engagement series (between-turn bridging), labeled `turn-intervals`; and
528
+ * `machine_active_time_ms` from the summed `task_complete.duration_ms` (model
529
+ * compute time, a subset of active time).
530
+ *
519
531
  * Unlike the Claude importer this derives no `file_changed`: Codex has no
520
532
  * dedicated edit tool and applies edits inside `exec_command` (e.g.
521
533
  * `apply_patch`), so there is no clean file-change signal to map. Decisions
@@ -1262,10 +1274,10 @@ type SessionStatus = z.infer<typeof SessionStatusSchema>;
1262
1274
  */
1263
1275
  declare const SessionSourceKindSchema: z.ZodEnum<{
1264
1276
  "claude-code-adapter": "claude-code-adapter";
1277
+ import: "import";
1265
1278
  "claude-code-import": "claude-code-import";
1266
1279
  "codex-import": "codex-import";
1267
1280
  human: "human";
1268
- import: "import";
1269
1281
  terminal: "terminal";
1270
1282
  }>;
1271
1283
  /** Inferred runtime type for {@link SessionSourceKindSchema}. */
@@ -1283,7 +1295,17 @@ type SessionSourceKind = z.infer<typeof SessionSourceKindSchema>;
1283
1295
  * wall-clock ranges (so cross-session totals can de-duplicate overlapping
1284
1296
  * work by interval union); `active_time_ms` is their summed duration;
1285
1297
  * `active_gap_cap_ms` and `active_time_method` lock the methodology so the
1286
- * stored numbers stay interpretable if the method changes later.
1298
+ * stored numbers stay interpretable if the method changes later. When a
1299
+ * source records explicit per-turn intervals (Codex), `active_time_method` is
1300
+ * `turn-intervals` and the in-turn time is the log's real wall-clock span
1301
+ * rather than a gap-capped approximation; the active semantics are unchanged.
1302
+ * - `machine_active_time_ms`: model compute time — the summed duration of the
1303
+ * source's per-turn spans (Codex `task_complete.duration_ms`), a SUBSET of a
1304
+ * single session's engaged active time. Unlike `active_intervals` it is a
1305
+ * plain sum, NOT wall-clock-deduplicated, so two concurrent sessions can sum
1306
+ * past their billable (union) active wall-clock — that is intended (two models
1307
+ * working at once did two machine-hours in one wall-clock hour). Captured only
1308
+ * for sources that record per-turn duration (Codex); absent otherwise.
1287
1309
  *
1288
1310
  * Absent on sessions imported before a given field existed (re-import to
1289
1311
  * backfill). Live sessions carry no engaged-time metrics and fall back to
@@ -1301,6 +1323,7 @@ declare const SessionMetricsSchema: z.ZodObject<{
1301
1323
  }, z.core.$strip>>>;
1302
1324
  active_gap_cap_ms: z.ZodOptional<z.ZodNumber>;
1303
1325
  active_time_method: z.ZodOptional<z.ZodString>;
1326
+ machine_active_time_ms: z.ZodOptional<z.ZodNumber>;
1304
1327
  }, z.core.$strip>;
1305
1328
  /** Inferred runtime type for {@link SessionMetricsSchema}. */
1306
1329
  type SessionMetrics = z.infer<typeof SessionMetricsSchema>;
@@ -1319,10 +1342,10 @@ declare const SessionSchema: z.ZodObject<{
1319
1342
  source: z.ZodObject<{
1320
1343
  kind: z.ZodEnum<{
1321
1344
  "claude-code-adapter": "claude-code-adapter";
1345
+ import: "import";
1322
1346
  "claude-code-import": "claude-code-import";
1323
1347
  "codex-import": "codex-import";
1324
1348
  human: "human";
1325
- import: "import";
1326
1349
  terminal: "terminal";
1327
1350
  }>;
1328
1351
  version: z.ZodLiteral<"0.1.0">;
@@ -1361,6 +1384,7 @@ declare const SessionSchema: z.ZodObject<{
1361
1384
  }, z.core.$strip>>>;
1362
1385
  active_gap_cap_ms: z.ZodOptional<z.ZodNumber>;
1363
1386
  active_time_method: z.ZodOptional<z.ZodString>;
1387
+ machine_active_time_ms: z.ZodOptional<z.ZodNumber>;
1364
1388
  }, z.core.$strip>>;
1365
1389
  }, z.core.$strip>;
1366
1390
  }, z.core.$strip>;
@@ -2711,6 +2735,46 @@ declare class ChildProcessRunner implements ProcessRunner {
2711
2735
  run(command: string, args: readonly string[], options: RunOptions): Promise<RunResult>;
2712
2736
  }
2713
2737
 
2738
+ /**
2739
+ * Schema version of the on-disk Basou v0.1 formats these JSON Schemas describe.
2740
+ * It tracks {@link SchemaVersionSchema} (the `schema_version` field), NOT the
2741
+ * npm package version, so the `$id` URLs stay stable while the package moves.
2742
+ */
2743
+ declare const JSON_SCHEMA_VERSION = "0.1.0";
2744
+ /** One emitted JSON Schema artifact. */
2745
+ type JsonSchemaArtifact = {
2746
+ /** Artifact basename without extension (e.g. `session`). */
2747
+ name: string;
2748
+ /** The JSON Schema document (draft 2020-12). */
2749
+ schema: Record<string, unknown>;
2750
+ };
2751
+ /**
2752
+ * Build the published JSON Schema artifacts from the canonical Zod schemas.
2753
+ *
2754
+ * Pure: no disk or environment access. Each artifact is `z.toJSONSchema` of the
2755
+ * document schema, re-headed with a stable `$id` / `title` / `description` (the
2756
+ * draft `$schema` from zod is preserved). This is the single generator used by
2757
+ * both the `gen:schemas` script (which writes the committed files) and the
2758
+ * drift-guard test (which asserts the committed files still match), so the two
2759
+ * can never disagree.
2760
+ *
2761
+ * Generated in `io: "input"` mode so the artifacts describe what a consumer
2762
+ * AUTHORS on disk, not zod's parsed output: a field with a `.default()` (e.g.
2763
+ * `events_log`) stays optional rather than `required`, and a non-strict object
2764
+ * omits `additionalProperties: false` so additive fields are allowed. Only the
2765
+ * `.strict()` event variants (e.g. `adapter_output`) keep
2766
+ * `additionalProperties: false`, preserving their reject-unknown contract.
2767
+ *
2768
+ * Note: prefixed-id fields carry a representable `pattern` (see
2769
+ * `createPrefixedIdSchema`); other refinement-only constraints are not
2770
+ * expressible in JSON Schema and are intentionally omitted.
2771
+ */
2772
+ declare function buildJsonSchemas(): JsonSchemaArtifact[];
2773
+ /** Serialize an artifact's schema exactly as the committed file stores it
2774
+ * (2-space indent, trailing newline) so the generator and the drift-guard test
2775
+ * compare byte-for-byte. */
2776
+ declare function serializeJsonSchema(schema: Record<string, unknown>): string;
2777
+
2714
2778
  /**
2715
2779
  * Schema version literal pinned to "0.1.0" for Basou v0.1.
2716
2780
  * Reused across every entity schema so inferred types narrow to the literal.
@@ -2823,6 +2887,8 @@ type MeasureAvailability = {
2823
2887
  activeTime: boolean;
2824
2888
  /** Token totals were captured (model-usage metrics present). */
2825
2889
  tokens: boolean;
2890
+ /** Model compute time was captured (`machine_active_time_ms`; Codex only). */
2891
+ machineActive: boolean;
2826
2892
  };
2827
2893
  /** Token rollup. Zero when not captured; `reasoning` is Codex-only. */
2828
2894
  type TokenTotals = {
@@ -2857,6 +2923,18 @@ type SessionWorkStats = {
2857
2923
  * (concurrent) work is not double-counted in billable totals.
2858
2924
  */
2859
2925
  activeIntervals: IsoInterval[];
2926
+ /**
2927
+ * Model compute time: the source's summed per-turn duration
2928
+ * (`metrics.machine_active_time_ms`). A subset of `activeTimeMs`; 0 when the
2929
+ * source records no per-turn duration (everything but Codex today).
2930
+ */
2931
+ machineActiveTimeMs: number;
2932
+ /**
2933
+ * Methodology lock copied from `metrics.active_time_method` (e.g.
2934
+ * `turn-intervals` / `engaged-turns`); undefined when active time was derived
2935
+ * from the event stream rather than stored metrics.
2936
+ */
2937
+ activeTimeMethod: string | undefined;
2860
2938
  commandCount: number;
2861
2939
  fileChangedCount: number;
2862
2940
  decisionCount: number;
@@ -2874,6 +2952,7 @@ type SourceWorkStats = {
2874
2952
  sessionSpanMs: number;
2875
2953
  commandTimeMs: number;
2876
2954
  activeTimeMs: number;
2955
+ machineActiveTimeMs: number;
2877
2956
  commandCount: number;
2878
2957
  fileChangedCount: number;
2879
2958
  decisionCount: number;
@@ -2883,6 +2962,8 @@ type SourceWorkStats = {
2883
2962
  commandTimeReliable: boolean;
2884
2963
  /** At least one session of this kind captured token totals. */
2885
2964
  tokensAvailable: boolean;
2965
+ /** At least one session of this kind captured model compute time. */
2966
+ machineActiveAvailable: boolean;
2886
2967
  };
2887
2968
  type StatusCount = {
2888
2969
  status: SessionStatus;
@@ -2898,6 +2979,12 @@ type DayWorkStats = {
2898
2979
  /** Calendar date `YYYY-MM-DD` in the report timezone. */
2899
2980
  date: string;
2900
2981
  billableActiveTimeMs: number;
2982
+ /**
2983
+ * Model compute time for sessions started on this date (summed
2984
+ * `machine_active_time_ms`). Not wall-clock-deduplicated, so — unlike
2985
+ * `billableActiveTimeMs` — concurrent sessions sum freely.
2986
+ */
2987
+ machineActiveTimeMs: number;
2901
2988
  sessionCount: number;
2902
2989
  commandCount: number;
2903
2990
  fileChangedCount: number;
@@ -2917,6 +3004,13 @@ type WorkStatsTotals = {
2917
3004
  * `activeTimeMs` when no sessions overlap, and is smaller when they do.
2918
3005
  */
2919
3006
  billableActiveTimeMs: number;
3007
+ /**
3008
+ * Workspace-wide model compute time: summed `machine_active_time_ms`. A plain
3009
+ * sum (not interval union), so it can exceed `billableActiveTimeMs` when
3010
+ * sessions ran concurrently — two models working at once is two machine-hours
3011
+ * in one wall-clock hour.
3012
+ */
3013
+ machineActiveTimeMs: number;
2920
3014
  commandCount: number;
2921
3015
  fileChangedCount: number;
2922
3016
  decisionCount: number;
@@ -2925,6 +3019,8 @@ type WorkStatsTotals = {
2925
3019
  /** No `claude-code-import` sessions present, so command time is workspace-wide real. */
2926
3020
  commandTimeReliable: boolean;
2927
3021
  tokensAvailable: boolean;
3022
+ /** At least one session captured model compute time (`machine_active_time_ms`). */
3023
+ machineActiveAvailable: boolean;
2928
3024
  };
2929
3025
  type WorkStatsResult = {
2930
3026
  generatedAt: string;
@@ -3053,6 +3149,14 @@ type CreateManifestInput = {
3053
3149
  now?: Date;
3054
3150
  /** Override for tests; defaults to a freshly generated `ws_<ULID>`. */
3055
3151
  workspaceId?: PrefixedId<"ws">;
3152
+ /**
3153
+ * Import source roots, each RELATIVE to the repository root (e.g. `"."`,
3154
+ * `"../basou-workspace"`). Persisted under `import.source_roots` so
3155
+ * `basou refresh` / `basou import` aggregate several sibling repos into one
3156
+ * `.basou/`. Validated by `ManifestSchema` (absolute paths are rejected).
3157
+ * Omitted from the manifest entirely when absent or empty.
3158
+ */
3159
+ sourceRoots?: string[];
3056
3160
  };
3057
3161
  /**
3058
3162
  * Build a fresh Manifest object that satisfies the manifest schema's
@@ -3303,4 +3407,4 @@ declare function overwriteYamlFile(filePath: string, value: unknown): Promise<vo
3303
3407
  */
3304
3408
  declare const BASOU_CORE_VERSION = "0.1.0";
3305
3409
 
3306
- 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, 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, 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, sessionWorkStatsFromEvents, summarizeAdapterOutput, tryRemoteUrl, ulid, updateTaskStatusWithEvent, writeEventsBulk, writeManifest, writeMarkdownFile, writeStatus, writeTaskFile, writeYamlFile };
3410
+ 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 };
package/dist/index.js CHANGED
@@ -49,6 +49,7 @@ function isValidPrefixedId(value) {
49
49
  // src/stats/active-time.ts
50
50
  var ACTIVE_GAP_CAP_MS = 5 * 60 * 1e3;
51
51
  var ENGAGED_TURNS_METHOD = "engaged-turns";
52
+ var TURN_INTERVALS_METHOD = "turn-intervals";
52
53
  function activeTimeFromTimestamps(timestampsMs, capMs) {
53
54
  const sorted = timestampsMs.filter((t) => Number.isFinite(t)).sort((a, b) => a - b);
54
55
  const raw = [];
@@ -340,6 +341,7 @@ var CODEX_IMPORT_SOURCE = "codex-import";
340
341
  function codexRolloutToImportPayload(records, options) {
341
342
  const placeholderSessionId = prefixedUlid("ses");
342
343
  const outputsByCallId = indexOutputs(records);
344
+ const turnStartMsByTurnId = indexTaskStarts(records);
343
345
  const derived = [];
344
346
  let minTs;
345
347
  let maxTs;
@@ -347,6 +349,8 @@ function codexRolloutToImportPayload(records, options) {
347
349
  let codexSessionId;
348
350
  let lastTokenTotals;
349
351
  const engagementTsMs = [];
352
+ const completions = [];
353
+ const completedTurnIds = /* @__PURE__ */ new Set();
350
354
  for (const record of records) {
351
355
  const ts = readString2(record.timestamp);
352
356
  if (ts === void 0) continue;
@@ -371,6 +375,16 @@ function codexRolloutToImportPayload(records, options) {
371
375
  const tsMs = Date.parse(ts);
372
376
  if (Number.isFinite(tsMs)) engagementTsMs.push(tsMs);
373
377
  }
378
+ if (pt === "task_complete") {
379
+ const turnId = readString2(payload2.turn_id);
380
+ if (turnId === void 0 || !completedTurnIds.has(turnId)) {
381
+ if (turnId !== void 0) completedTurnIds.add(turnId);
382
+ completions.push({
383
+ interval: turnIntervalFromComplete(ts, payload2, turnStartMsByTurnId),
384
+ durationMs: readNonNegInt2(payload2.duration_ms)
385
+ });
386
+ }
387
+ }
374
388
  continue;
375
389
  }
376
390
  if (readString2(record.type) !== "response_item") continue;
@@ -401,7 +415,23 @@ function codexRolloutToImportPayload(records, options) {
401
415
  const commandCount = derived.length;
402
416
  const date = minTs.slice(0, 10);
403
417
  const label = `codex ${date}: ${commandCount} ${commandCount === 1 ? "command" : "commands"}`;
404
- const active = engagementTsMs.length >= 2 ? activeTimeFromTimestamps(engagementTsMs, ACTIVE_GAP_CAP_MS) : void 0;
418
+ const minTsMs = Date.parse(minTs);
419
+ const turnIntervals = [];
420
+ let machineActiveMs = 0;
421
+ let allCompletedTurnsHaveDuration = true;
422
+ for (const { interval, durationMs } of completions) {
423
+ if (durationMs <= 0) allCompletedTurnsHaveDuration = false;
424
+ if (interval === void 0) continue;
425
+ const start = Number.isFinite(minTsMs) ? Math.max(interval[0], minTsMs) : interval[0];
426
+ const end = interval[1];
427
+ if (!(start < end)) continue;
428
+ turnIntervals.push([start, end]);
429
+ machineActiveMs += Math.min(durationMs, end - start);
430
+ }
431
+ const pointResult = activeTimeFromTimestamps(engagementTsMs, ACTIVE_GAP_CAP_MS);
432
+ const active = turnIntervals.length > 0 || pointResult.intervals.length > 0 ? unionDurationMs([...turnIntervals, ...pointResult.intervals]) : void 0;
433
+ const activeMethod = turnIntervals.length > 0 ? TURN_INTERVALS_METHOD : ENGAGED_TURNS_METHOD;
434
+ const machineActive = allCompletedTurnsHaveDuration ? machineActiveMs : 0;
405
435
  const tokenFields = lastTokenTotals === void 0 ? {} : {
406
436
  ...readNonNegInt2(lastTokenTotals.output_tokens) > 0 ? { output_tokens: readNonNegInt2(lastTokenTotals.output_tokens) } : {},
407
437
  ...readNonNegInt2(lastTokenTotals.input_tokens) > 0 ? { input_tokens: readNonNegInt2(lastTokenTotals.input_tokens) } : {},
@@ -412,10 +442,11 @@ function codexRolloutToImportPayload(records, options) {
412
442
  ...tokenFields,
413
443
  ...active !== void 0 && active.ms > 0 ? {
414
444
  active_time_ms: active.ms,
415
- active_intervals: intervalsMsToIso(active.intervals),
445
+ active_intervals: intervalsMsToIso(active.merged),
416
446
  active_gap_cap_ms: ACTIVE_GAP_CAP_MS,
417
- active_time_method: ENGAGED_TURNS_METHOD
418
- } : {}
447
+ active_time_method: activeMethod
448
+ } : {},
449
+ ...machineActive > 0 ? { machine_active_time_ms: machineActive } : {}
419
450
  };
420
451
  const metrics = Object.keys(metricsFields).length > 0 ? metricsFields : void 0;
421
452
  const payload = {
@@ -496,6 +527,30 @@ function readCallId(value, outputs) {
496
527
  const callId = readString2(value);
497
528
  return callId !== void 0 ? outputs.get(callId) : void 0;
498
529
  }
530
+ function turnIntervalFromComplete(endTs, payload, startMsByTurnId) {
531
+ const endMs = Date.parse(endTs);
532
+ if (!Number.isFinite(endMs)) return void 0;
533
+ const turnId = readString2(payload.turn_id);
534
+ const indexedStart = turnId !== void 0 ? startMsByTurnId.get(turnId) : void 0;
535
+ const durationMs = readNonNegInt2(payload.duration_ms);
536
+ const startMs = indexedStart !== void 0 ? indexedStart : durationMs > 0 ? endMs - durationMs : void 0;
537
+ if (startMs === void 0 || !(startMs < endMs)) return void 0;
538
+ return [startMs, endMs];
539
+ }
540
+ function indexTaskStarts(records) {
541
+ const byTurnId = /* @__PURE__ */ new Map();
542
+ for (const record of records) {
543
+ if (readString2(record.type) !== "event_msg") continue;
544
+ const payload = isObject2(record.payload) ? record.payload : void 0;
545
+ if (payload === void 0 || readString2(payload.type) !== "task_started") continue;
546
+ const turnId = readString2(payload.turn_id);
547
+ const startMs = Date.parse(readString2(record.timestamp) ?? "");
548
+ if (turnId !== void 0 && Number.isFinite(startMs) && !byTurnId.has(turnId)) {
549
+ byTurnId.set(turnId, startMs);
550
+ }
551
+ }
552
+ return byTurnId;
553
+ }
499
554
  function parseExitCode(output) {
500
555
  if (output === void 0) return null;
501
556
  const match = output.match(/Process exited with code (-?\d+)/);
@@ -546,7 +601,10 @@ var SchemaVersionSchema = z.literal("0.1.0");
546
601
  var IsoTimestampSchema = z.string().datetime({ offset: true });
547
602
  var createPrefixedIdSchema = (prefix) => {
548
603
  const refiner = (value) => isValidPrefixedId(value) && value.startsWith(`${prefix}_`);
549
- return z.string().refine(refiner, { message: `Expected ${prefix}_<ULID>` });
604
+ return z.string().refine(refiner, { message: `Expected ${prefix}_<ULID>` }).meta({
605
+ pattern: `^${prefix}_[0-7][0-9A-HJKMNP-TV-Z]{25}$`,
606
+ description: `Basou ${prefix} id: \`${prefix}_\` followed by a 26-character Crockford Base32 ULID.`
607
+ });
550
608
  };
551
609
  var WorkspaceIdSchema = createPrefixedIdSchema("ws");
552
610
  var TaskIdSchema = createPrefixedIdSchema("task");
@@ -991,7 +1049,8 @@ var SessionMetricsSchema = z4.object({
991
1049
  active_time_ms: z4.number().int().nonnegative().optional(),
992
1050
  active_intervals: z4.array(z4.object({ start: IsoTimestampSchema, end: IsoTimestampSchema })).optional(),
993
1051
  active_gap_cap_ms: z4.number().int().nonnegative().optional(),
994
- active_time_method: z4.string().optional()
1052
+ active_time_method: z4.string().optional(),
1053
+ machine_active_time_ms: z4.number().int().nonnegative().optional()
995
1054
  });
996
1055
  var SessionInnerSchema = z4.object({
997
1056
  id: SessionIdSchema,
@@ -3799,6 +3858,9 @@ function classifySpawnError(error) {
3799
3858
  return new Error("Failed to spawn child process", { cause: error });
3800
3859
  }
3801
3860
 
3861
+ // src/schemas/json-schema.ts
3862
+ import { z as z11 } from "zod";
3863
+
3802
3864
  // src/schemas/manifest.schema.ts
3803
3865
  import { z as z9 } from "zod";
3804
3866
  var ProjectSchema = z9.object({
@@ -3823,6 +3885,13 @@ var AdaptersSchema = z9.object({
3823
3885
  var GitConfigSchema = z9.object({
3824
3886
  events_log: z9.enum(["ignore", "commit"]).default("ignore")
3825
3887
  });
3888
+ var SOURCE_ROOT_PATTERN = /^(?![~/\\])(?![A-Za-z]:)[^\0\\]+$/;
3889
+ var SourceRootSchema = z9.string().min(1).regex(SOURCE_ROOT_PATTERN, {
3890
+ message: "source_roots entries must be relative paths (no absolute path, '~', '\\', or null byte)"
3891
+ });
3892
+ var ImportConfigSchema = z9.object({
3893
+ source_roots: z9.array(SourceRootSchema).min(1).optional()
3894
+ });
3826
3895
  var WorkspaceMetaSchema = z9.object({
3827
3896
  id: WorkspaceIdSchema,
3828
3897
  name: z9.string().min(1),
@@ -3837,7 +3906,8 @@ var ManifestSchema = z9.object({
3837
3906
  capabilities: CapabilitiesSchema,
3838
3907
  approval: ApprovalConfigSchema,
3839
3908
  adapters: AdaptersSchema,
3840
- git: GitConfigSchema
3909
+ git: GitConfigSchema,
3910
+ import: ImportConfigSchema.optional()
3841
3911
  });
3842
3912
 
3843
3913
  // src/schemas/session-import.schema.ts
@@ -3874,6 +3944,79 @@ var SessionImportPayloadSchema = z10.object({
3874
3944
  events: z10.array(EventSchema)
3875
3945
  }).strict();
3876
3946
 
3947
+ // src/schemas/json-schema.ts
3948
+ var JSON_SCHEMA_VERSION = "0.1.0";
3949
+ var ID_BASE = `https://basou.dev/schemas/${JSON_SCHEMA_VERSION}`;
3950
+ var JSON_SCHEMA_DIALECT = "https://json-schema.org/draft/2020-12/schema";
3951
+ var DOCUMENTS = [
3952
+ {
3953
+ name: "manifest",
3954
+ schema: ManifestSchema,
3955
+ title: "Basou Manifest",
3956
+ description: "The `.basou/manifest.yaml` workspace manifest."
3957
+ },
3958
+ {
3959
+ name: "session",
3960
+ schema: SessionSchema,
3961
+ title: "Basou Session",
3962
+ description: "A `.basou/sessions/<id>/session.yaml` session record."
3963
+ },
3964
+ {
3965
+ name: "event",
3966
+ schema: EventSchema,
3967
+ title: "Basou Event",
3968
+ description: "One line of a `.basou/sessions/<id>/events.jsonl` stream (a discriminated union over the event `type`)."
3969
+ },
3970
+ {
3971
+ name: "task",
3972
+ schema: TaskSchema,
3973
+ title: "Basou Task",
3974
+ description: "The YAML front matter of a `.basou/tasks/<id>.md` task document."
3975
+ },
3976
+ {
3977
+ name: "approval",
3978
+ schema: ApprovalSchema,
3979
+ title: "Basou Approval",
3980
+ description: "A `.basou/approvals/{pending,resolved}/<id>.yaml` approval record."
3981
+ },
3982
+ {
3983
+ name: "status",
3984
+ schema: StatusSchema,
3985
+ title: "Basou Status",
3986
+ description: "The `.basou/status.json` workspace status snapshot."
3987
+ },
3988
+ {
3989
+ name: "task-index",
3990
+ schema: TaskIndexSchema,
3991
+ title: "Basou Task Index",
3992
+ description: "The `.basou/tasks/index.json` task lookup index."
3993
+ },
3994
+ {
3995
+ name: "session-import",
3996
+ schema: SessionImportPayloadSchema,
3997
+ title: "Basou Session Import Payload",
3998
+ description: "The portable session payload consumed by `basou session import`."
3999
+ }
4000
+ ];
4001
+ function buildJsonSchemas() {
4002
+ return DOCUMENTS.map((doc) => {
4003
+ const generated = z11.toJSONSchema(doc.schema, { io: "input" });
4004
+ const { $schema, ...rest } = generated;
4005
+ const schema = {
4006
+ $schema: typeof $schema === "string" ? $schema : JSON_SCHEMA_DIALECT,
4007
+ $id: `${ID_BASE}/${doc.name}.schema.json`,
4008
+ title: doc.title,
4009
+ description: doc.description,
4010
+ ...rest
4011
+ };
4012
+ return { name: doc.name, schema };
4013
+ });
4014
+ }
4015
+ function serializeJsonSchema(schema) {
4016
+ return `${JSON.stringify(schema, null, 2)}
4017
+ `;
4018
+ }
4019
+
3877
4020
  // src/stats/work-stats.ts
3878
4021
  import { join as join11 } from "path";
3879
4022
  function resolveTimeZone(timeZone) {
@@ -3962,6 +4105,7 @@ function sessionWorkStatsFromEvents(sessionId, inner, events, now, eventsUnreada
3962
4105
  const span = computeSpan(inner.started_at, inner.ended_at, now);
3963
4106
  const tokens = readTokens(inner.metrics);
3964
4107
  const active = resolveActiveTime(inner.metrics, timestamps);
4108
+ const machineActiveTimeMs = inner.metrics?.machine_active_time_ms ?? 0;
3965
4109
  return {
3966
4110
  sessionId,
3967
4111
  label: inner.label,
@@ -3975,6 +4119,8 @@ function sessionWorkStatsFromEvents(sessionId, inner, events, now, eventsUnreada
3975
4119
  activeTimeMs: active.ms,
3976
4120
  activeTimeBasis: active.basis,
3977
4121
  activeIntervals: intervalsMsToIso(active.intervals),
4122
+ machineActiveTimeMs,
4123
+ activeTimeMethod: inner.metrics?.active_time_method,
3978
4124
  commandCount,
3979
4125
  fileChangedCount,
3980
4126
  decisionCount,
@@ -3984,7 +4130,8 @@ function sessionWorkStatsFromEvents(sessionId, inner, events, now, eventsUnreada
3984
4130
  span: true,
3985
4131
  commandTime: inner.source.kind !== "claude-code-import",
3986
4132
  activeTime: active.intervals.length > 0,
3987
- tokens: hasTokens(tokens)
4133
+ tokens: hasTokens(tokens),
4134
+ machineActive: machineActiveTimeMs > 0
3988
4135
  },
3989
4136
  spanClamped: span.clamped,
3990
4137
  eventsUnreadable
@@ -4036,19 +4183,22 @@ function computeTotals(sessions, billableActiveTimeMs) {
4036
4183
  commandTimeMs: 0,
4037
4184
  activeTimeMs: 0,
4038
4185
  billableActiveTimeMs,
4186
+ machineActiveTimeMs: 0,
4039
4187
  commandCount: 0,
4040
4188
  fileChangedCount: 0,
4041
4189
  decisionCount: 0,
4042
4190
  eventCount: 0,
4043
4191
  tokens,
4044
4192
  commandTimeReliable: true,
4045
- tokensAvailable: false
4193
+ tokensAvailable: false,
4194
+ machineActiveAvailable: false
4046
4195
  };
4047
4196
  for (const s of sessions) {
4048
4197
  if (s.open) totals.openSessionCount++;
4049
4198
  totals.sessionSpanMs += s.sessionSpanMs;
4050
4199
  totals.commandTimeMs += s.commandTimeMs;
4051
4200
  totals.activeTimeMs += s.activeTimeMs;
4201
+ totals.machineActiveTimeMs += s.machineActiveTimeMs;
4052
4202
  totals.commandCount += s.commandCount;
4053
4203
  totals.fileChangedCount += s.fileChangedCount;
4054
4204
  totals.decisionCount += s.decisionCount;
@@ -4056,6 +4206,7 @@ function computeTotals(sessions, billableActiveTimeMs) {
4056
4206
  addTokens(tokens, s.tokens);
4057
4207
  if (!s.availability.commandTime) totals.commandTimeReliable = false;
4058
4208
  if (s.availability.tokens) totals.tokensAvailable = true;
4209
+ if (s.availability.machineActive) totals.machineActiveAvailable = true;
4059
4210
  }
4060
4211
  return totals;
4061
4212
  }
@@ -4070,13 +4221,15 @@ function computeBySource(sessions) {
4070
4221
  sessionSpanMs: 0,
4071
4222
  commandTimeMs: 0,
4072
4223
  activeTimeMs: 0,
4224
+ machineActiveTimeMs: 0,
4073
4225
  commandCount: 0,
4074
4226
  fileChangedCount: 0,
4075
4227
  decisionCount: 0,
4076
4228
  eventCount: 0,
4077
4229
  tokens: emptyTokens(),
4078
4230
  commandTimeReliable: true,
4079
- tokensAvailable: false
4231
+ tokensAvailable: false,
4232
+ machineActiveAvailable: false
4080
4233
  };
4081
4234
  map.set(s.sourceKind, row);
4082
4235
  }
@@ -4084,6 +4237,7 @@ function computeBySource(sessions) {
4084
4237
  row.sessionSpanMs += s.sessionSpanMs;
4085
4238
  row.commandTimeMs += s.commandTimeMs;
4086
4239
  row.activeTimeMs += s.activeTimeMs;
4240
+ row.machineActiveTimeMs += s.machineActiveTimeMs;
4087
4241
  row.commandCount += s.commandCount;
4088
4242
  row.fileChangedCount += s.fileChangedCount;
4089
4243
  row.decisionCount += s.decisionCount;
@@ -4091,6 +4245,7 @@ function computeBySource(sessions) {
4091
4245
  addTokens(row.tokens, s.tokens);
4092
4246
  if (!s.availability.commandTime) row.commandTimeReliable = false;
4093
4247
  if (s.availability.tokens) row.tokensAvailable = true;
4248
+ if (s.availability.machineActive) row.machineActiveAvailable = true;
4094
4249
  }
4095
4250
  return [...map.values()].sort((a, b) => a.sourceKind.localeCompare(b.sourceKind));
4096
4251
  }
@@ -4112,6 +4267,7 @@ function computeByDay(sessions, unionMerged, timeZone) {
4112
4267
  day = {
4113
4268
  date,
4114
4269
  billableActiveTimeMs: 0,
4270
+ machineActiveTimeMs: 0,
4115
4271
  sessionCount: 0,
4116
4272
  commandCount: 0,
4117
4273
  fileChangedCount: 0,
@@ -4130,6 +4286,7 @@ function computeByDay(sessions, unionMerged, timeZone) {
4130
4286
  if (!Number.isFinite(startedMs)) continue;
4131
4287
  const day = ensure(tzDate(startedMs, timeZone));
4132
4288
  day.sessionCount++;
4289
+ day.machineActiveTimeMs += s.machineActiveTimeMs;
4133
4290
  day.commandCount += s.commandCount;
4134
4291
  day.fileChangedCount += s.fileChangedCount;
4135
4292
  day.decisionCount += s.decisionCount;
@@ -4305,7 +4462,8 @@ function createManifest(input) {
4305
4462
  adapters: {
4306
4463
  "claude-code": { enabled: true }
4307
4464
  },
4308
- git: { events_log: "ignore" }
4465
+ git: { events_log: "ignore" },
4466
+ ...input.sourceRoots !== void 0 && input.sourceRoots.length > 0 ? { import: { source_roots: input.sourceRoots } } : {}
4309
4467
  };
4310
4468
  return ManifestSchema.parse(manifest);
4311
4469
  }
@@ -4594,6 +4752,7 @@ export {
4594
4752
  GENERATED_START,
4595
4753
  ID_PREFIXES,
4596
4754
  IsoTimestampSchema,
4755
+ JSON_SCHEMA_VERSION,
4597
4756
  ManifestSchema,
4598
4757
  RiskLevelSchema,
4599
4758
  STUCK_THRESHOLD_MS,
@@ -4618,6 +4777,7 @@ export {
4618
4777
  archiveTask,
4619
4778
  assertBasouRootSafe,
4620
4779
  basouPaths,
4780
+ buildJsonSchemas,
4621
4781
  buildStatusSnapshot,
4622
4782
  classifySuspect,
4623
4783
  claudeCodeAdapterMetadata,
@@ -4670,6 +4830,7 @@ export {
4670
4830
  sanitizePath,
4671
4831
  sanitizeRelatedFiles,
4672
4832
  sanitizeWorkingDirectory,
4833
+ serializeJsonSchema,
4673
4834
  sessionWorkStatsFromEvents,
4674
4835
  summarizeAdapterOutput,
4675
4836
  tryRemoteUrl,