sidekick-shared 0.28.3 → 0.29.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/README.md +188 -27
  3. package/dist/aggregation/EventAggregator.d.ts +19 -0
  4. package/dist/aggregation/EventAggregator.js +174 -35
  5. package/dist/aggregation/snapshot.d.ts +37 -8
  6. package/dist/aggregation/snapshot.js +31 -9
  7. package/dist/aggregation/types.d.ts +13 -0
  8. package/dist/authStatus.d.ts +54 -0
  9. package/dist/authStatus.js +91 -0
  10. package/dist/browser.d.ts +16 -6
  11. package/dist/browser.js +24 -2
  12. package/dist/codex.d.ts +23 -0
  13. package/dist/codex.js +44 -0
  14. package/dist/codexProfiles.d.ts +1 -0
  15. package/dist/codexProfiles.js +8 -11
  16. package/dist/context/sessionContext.js +4 -0
  17. package/dist/esm/aggregation/EventAggregator.js +174 -35
  18. package/dist/esm/aggregation/snapshot.js +31 -9
  19. package/dist/esm/authStatus.js +87 -0
  20. package/dist/esm/browser.js +5 -2
  21. package/dist/esm/codex.js +18 -0
  22. package/dist/esm/codexProfiles.js +8 -11
  23. package/dist/esm/context/sessionContext.js +4 -0
  24. package/dist/esm/formatters/toolSummary.js +4 -1
  25. package/dist/esm/formatting.js +10 -1
  26. package/dist/esm/index.js +11 -6
  27. package/dist/esm/modelCatalog.js +7 -5
  28. package/dist/esm/modelContext.js +4 -3
  29. package/dist/esm/modelInfo.js +425 -47
  30. package/dist/esm/node.js +9 -3
  31. package/dist/esm/observedContextWindows.js +2 -1
  32. package/dist/esm/ownKey.js +26 -0
  33. package/dist/esm/parsers/codexParser.js +351 -54
  34. package/dist/esm/parsers/codexRolloutOrigin.js +107 -0
  35. package/dist/esm/parsers/openCodeParser.js +85 -22
  36. package/dist/esm/parsers/subagentScanner.js +33 -12
  37. package/dist/esm/parsers/subagentTraceParser.js +97 -37
  38. package/dist/esm/pricing.js +18 -0
  39. package/dist/esm/pricingCatalog.js +17 -9
  40. package/dist/esm/promptHistory.js +66 -4
  41. package/dist/esm/providerFailure.js +124 -3
  42. package/dist/esm/providers/claudeCode.js +10 -2
  43. package/dist/esm/providers/codex.js +84 -62
  44. package/dist/esm/providers/codexDatabase.js +3 -19
  45. package/dist/esm/providers/codexRollouts.js +24 -1
  46. package/dist/esm/providers/openCode.js +9 -17
  47. package/dist/esm/report/htmlReportGenerator.js +42 -5
  48. package/dist/esm/report/transcriptParser.js +13 -11
  49. package/dist/esm/schemas/providerFailure.js +3 -1
  50. package/dist/esm/schemas/sessionEvent.js +35 -14
  51. package/dist/esm/schemas/transcript.js +4 -0
  52. package/dist/esm/sessionPromptHistory.js +2 -2
  53. package/dist/esm/statusline/payload.js +9 -0
  54. package/dist/esm/subagentSpawn.js +163 -0
  55. package/dist/esm/testing/credentialGuard.js +87 -5
  56. package/dist/esm/toolCallOrigins.js +310 -0
  57. package/dist/esm/transcript.js +46 -3
  58. package/dist/esm/turns/assistantTurn.js +3 -2
  59. package/dist/esm/usage/claudeUsageDedupe.js +68 -0
  60. package/dist/esm/usage/usageEvents.js +146 -17
  61. package/dist/esm/usage/usageReports.js +39 -6
  62. package/dist/esm/watchers/eventBridge.js +15 -0
  63. package/dist/esm/watchers/factory.js +5 -3
  64. package/dist/esm/watchers/jsonlTail.js +442 -58
  65. package/dist/esm/watchers/jsonlWatcher.js +51 -4
  66. package/dist/esm/watchers/providerReaderWatcher.js +13 -0
  67. package/dist/esm/zaiQuota.js +2 -1
  68. package/dist/formatters/toolSummary.js +4 -1
  69. package/dist/formatting.d.ts +26 -1
  70. package/dist/formatting.js +11 -1
  71. package/dist/index.d.ts +22 -11
  72. package/dist/index.js +44 -12
  73. package/dist/modelCatalog.js +7 -5
  74. package/dist/modelContext.js +4 -3
  75. package/dist/modelInfo.d.ts +185 -16
  76. package/dist/modelInfo.js +434 -49
  77. package/dist/node.d.ts +16 -5
  78. package/dist/node.js +33 -2
  79. package/dist/observedContextWindows.js +2 -1
  80. package/dist/ownKey.d.ts +16 -0
  81. package/dist/ownKey.js +30 -0
  82. package/dist/parsers/codexParser.d.ts +118 -0
  83. package/dist/parsers/codexParser.js +352 -54
  84. package/dist/parsers/codexRolloutOrigin.d.ts +49 -0
  85. package/dist/parsers/codexRolloutOrigin.js +111 -0
  86. package/dist/parsers/openCodeParser.d.ts +38 -0
  87. package/dist/parsers/openCodeParser.js +88 -22
  88. package/dist/parsers/subagentScanner.d.ts +19 -6
  89. package/dist/parsers/subagentScanner.js +33 -12
  90. package/dist/parsers/subagentTraceParser.d.ts +25 -5
  91. package/dist/parsers/subagentTraceParser.js +97 -37
  92. package/dist/pricing.d.ts +22 -0
  93. package/dist/pricing.js +50 -0
  94. package/dist/pricingCatalog.d.ts +10 -0
  95. package/dist/pricingCatalog.js +16 -8
  96. package/dist/promptHistory.d.ts +65 -4
  97. package/dist/promptHistory.js +68 -5
  98. package/dist/providerFailure.d.ts +44 -0
  99. package/dist/providerFailure.js +126 -4
  100. package/dist/providers/claudeCode.js +9 -1
  101. package/dist/providers/codex.d.ts +3 -22
  102. package/dist/providers/codex.js +85 -64
  103. package/dist/providers/codexDatabase.d.ts +3 -2
  104. package/dist/providers/codexDatabase.js +4 -22
  105. package/dist/providers/codexRollouts.d.ts +22 -0
  106. package/dist/providers/codexRollouts.js +24 -0
  107. package/dist/providers/openCode.js +8 -16
  108. package/dist/providers/types.d.ts +14 -0
  109. package/dist/report/htmlReportGenerator.js +42 -5
  110. package/dist/report/transcriptParser.d.ts +10 -1
  111. package/dist/report/transcriptParser.js +13 -11
  112. package/dist/schemas/index.d.ts +1 -0
  113. package/dist/schemas/providerFailure.d.ts +39 -0
  114. package/dist/schemas/providerFailure.js +2 -0
  115. package/dist/schemas/sessionEvent.d.ts +27 -10
  116. package/dist/schemas/sessionEvent.js +35 -14
  117. package/dist/schemas/transcript.d.ts +2 -0
  118. package/dist/schemas/transcript.js +4 -0
  119. package/dist/sessionPromptHistory.d.ts +7 -1
  120. package/dist/sessionPromptHistory.js +1 -1
  121. package/dist/statusline/payload.d.ts +10 -0
  122. package/dist/statusline/payload.js +14 -0
  123. package/dist/subagentSpawn.d.ts +80 -0
  124. package/dist/subagentSpawn.js +174 -0
  125. package/dist/testing/credentialGuard.d.ts +14 -0
  126. package/dist/testing/credentialGuard.js +87 -5
  127. package/dist/toolCallOrigins.d.ts +70 -0
  128. package/dist/toolCallOrigins.js +346 -0
  129. package/dist/transcript.d.ts +12 -0
  130. package/dist/transcript.js +46 -3
  131. package/dist/turns/assistantTurn.d.ts +1 -0
  132. package/dist/turns/assistantTurn.js +3 -2
  133. package/dist/types/opencode.d.ts +2 -0
  134. package/dist/usage/claudeUsageDedupe.d.ts +31 -0
  135. package/dist/usage/claudeUsageDedupe.js +68 -0
  136. package/dist/usage/usageEvents.d.ts +54 -6
  137. package/dist/usage/usageEvents.js +146 -17
  138. package/dist/usage/usageReports.d.ts +35 -4
  139. package/dist/usage/usageReports.js +39 -6
  140. package/dist/watchers/eventBridge.js +15 -0
  141. package/dist/watchers/factory.d.ts +6 -0
  142. package/dist/watchers/factory.js +5 -3
  143. package/dist/watchers/jsonlTail.d.ts +124 -4
  144. package/dist/watchers/jsonlTail.js +446 -59
  145. package/dist/watchers/jsonlWatcher.d.ts +18 -1
  146. package/dist/watchers/jsonlWatcher.js +50 -3
  147. package/dist/watchers/providerReaderWatcher.d.ts +7 -0
  148. package/dist/watchers/providerReaderWatcher.js +13 -0
  149. package/dist/watchers/types.d.ts +12 -0
  150. package/dist/zaiQuota.js +2 -1
  151. package/package.json +41 -1
@@ -76,12 +76,12 @@ export declare const userSessionEventSchema: z.ZodObject<{
76
76
  promptSource: z.ZodOptional<z.ZodString>;
77
77
  cwd: z.ZodOptional<z.ZodString>;
78
78
  gitBranch: z.ZodOptional<z.ZodString>;
79
- permissionMode: z.ZodOptional<z.ZodEnum<{
79
+ permissionMode: z.ZodCatch<z.ZodOptional<z.ZodEnum<{
80
80
  default: "default";
81
81
  acceptEdits: "acceptEdits";
82
82
  bypassPermissions: "bypassPermissions";
83
83
  plan: "plan";
84
- }>>;
84
+ }>>>;
85
85
  rateLimits: z.ZodOptional<z.ZodObject<{
86
86
  primary: z.ZodOptional<z.ZodObject<{
87
87
  usedPercent: z.ZodNumber;
@@ -165,12 +165,12 @@ export declare const assistantSessionEventSchema: z.ZodObject<{
165
165
  promptSource: z.ZodOptional<z.ZodString>;
166
166
  cwd: z.ZodOptional<z.ZodString>;
167
167
  gitBranch: z.ZodOptional<z.ZodString>;
168
- permissionMode: z.ZodOptional<z.ZodEnum<{
168
+ permissionMode: z.ZodCatch<z.ZodOptional<z.ZodEnum<{
169
169
  default: "default";
170
170
  acceptEdits: "acceptEdits";
171
171
  bypassPermissions: "bypassPermissions";
172
172
  plan: "plan";
173
- }>>;
173
+ }>>>;
174
174
  rateLimits: z.ZodOptional<z.ZodObject<{
175
175
  primary: z.ZodOptional<z.ZodObject<{
176
176
  usedPercent: z.ZodNumber;
@@ -255,12 +255,12 @@ export declare const systemSessionEventSchema: z.ZodObject<{
255
255
  promptSource: z.ZodOptional<z.ZodString>;
256
256
  cwd: z.ZodOptional<z.ZodString>;
257
257
  gitBranch: z.ZodOptional<z.ZodString>;
258
- permissionMode: z.ZodOptional<z.ZodEnum<{
258
+ permissionMode: z.ZodCatch<z.ZodOptional<z.ZodEnum<{
259
259
  default: "default";
260
260
  acceptEdits: "acceptEdits";
261
261
  bypassPermissions: "bypassPermissions";
262
262
  plan: "plan";
263
- }>>;
263
+ }>>>;
264
264
  rateLimits: z.ZodOptional<z.ZodObject<{
265
265
  primary: z.ZodOptional<z.ZodObject<{
266
266
  usedPercent: z.ZodNumber;
@@ -348,12 +348,12 @@ export declare const toolUseSessionEventSchema: z.ZodObject<{
348
348
  promptSource: z.ZodOptional<z.ZodString>;
349
349
  cwd: z.ZodOptional<z.ZodString>;
350
350
  gitBranch: z.ZodOptional<z.ZodString>;
351
- permissionMode: z.ZodOptional<z.ZodEnum<{
351
+ permissionMode: z.ZodCatch<z.ZodOptional<z.ZodEnum<{
352
352
  default: "default";
353
353
  acceptEdits: "acceptEdits";
354
354
  bypassPermissions: "bypassPermissions";
355
355
  plan: "plan";
356
- }>>;
356
+ }>>>;
357
357
  rateLimits: z.ZodOptional<z.ZodObject<{
358
358
  primary: z.ZodOptional<z.ZodObject<{
359
359
  usedPercent: z.ZodNumber;
@@ -442,12 +442,12 @@ export declare const toolResultSessionEventSchema: z.ZodObject<{
442
442
  promptSource: z.ZodOptional<z.ZodString>;
443
443
  cwd: z.ZodOptional<z.ZodString>;
444
444
  gitBranch: z.ZodOptional<z.ZodString>;
445
- permissionMode: z.ZodOptional<z.ZodEnum<{
445
+ permissionMode: z.ZodCatch<z.ZodOptional<z.ZodEnum<{
446
446
  default: "default";
447
447
  acceptEdits: "acceptEdits";
448
448
  bypassPermissions: "bypassPermissions";
449
449
  plan: "plan";
450
- }>>;
450
+ }>>>;
451
451
  rateLimits: z.ZodOptional<z.ZodObject<{
452
452
  primary: z.ZodOptional<z.ZodObject<{
453
453
  usedPercent: z.ZodNumber;
@@ -520,6 +520,19 @@ export declare const toolResultSessionEventSchema: z.ZodObject<{
520
520
  }, z.core.$strip>>;
521
521
  }, z.core.$strip>;
522
522
  export declare const sessionEventSchema: z.ZodType<SessionEvent>;
523
+ /** Options for {@link extractSessionEvents}. */
524
+ export interface ExtractSessionEventsOptions {
525
+ /**
526
+ * Also turn Claude Code's mid-turn task-notification attachments
527
+ * (`{ type: 'attachment', attachment: { type: 'queued_command',
528
+ * commandMode: 'task-notification', prompt } }`) into one `system` event
529
+ * with `origin: { kind: 'task-notification' }`, `message: { role: 'user',
530
+ * sourceLabel: 'task notification', content: <prompt> }`, and the line's
531
+ * timestamp. They report background agents finishing. Default `false`:
532
+ * attachment lines yield no events.
533
+ */
534
+ includeTaskNotifications?: boolean;
535
+ }
523
536
  /**
524
537
  * Extracts canonical SessionEvents from a raw JSONL value.
525
538
  *
@@ -528,5 +541,9 @@ export declare const sessionEventSchema: z.ZodType<SessionEvent>;
528
541
  * `sessionEventSchema` alone rejects. This helper tries a direct parse,
529
542
  * then unwraps progress envelopes (recursively, in case of nesting).
530
543
  * Returns zero events for unrecognized input — never throws.
544
+ *
545
+ * The second argument is either the starting progress depth (the original
546
+ * signature) or an options object.
531
547
  */
532
548
  export declare function extractSessionEvents(raw: unknown, depth?: number): SessionEvent[];
549
+ export declare function extractSessionEvents(raw: unknown, options?: ExtractSessionEventsOptions): SessionEvent[];
@@ -12,6 +12,7 @@ exports.sessionEventSchema = exports.toolResultSessionEventSchema = exports.tool
12
12
  exports.extractSessionEvents = extractSessionEvents;
13
13
  const zod_1 = require("zod");
14
14
  const usageNormalization_1 = require("./usageNormalization");
15
+ const subagentSpawn_1 = require("../subagentSpawn");
15
16
  // ── MessageUsage ──
16
17
  exports.messageUsageSchema = zod_1.z.object({
17
18
  input_tokens: zod_1.z.number(),
@@ -56,7 +57,9 @@ const eventBaseShape = {
56
57
  promptSource: zod_1.z.string().optional(),
57
58
  cwd: zod_1.z.string().optional(),
58
59
  gitBranch: zod_1.z.string().optional(),
59
- permissionMode: exports.permissionModeSchema.optional(),
60
+ // A mode this release doesn't know (Claude Code added `auto`) is dropped
61
+ // rather than rejecting the whole line with its prompt or tool results.
62
+ permissionMode: exports.permissionModeSchema.optional().catch(undefined),
60
63
  rateLimits: zod_1.z
61
64
  .object({
62
65
  primary: zod_1.z
@@ -173,22 +176,21 @@ exports.sessionEventSchema = zod_1.z.union([
173
176
  // ── Progress unwrapping ──
174
177
  /** Recursion bound for nested progress envelopes; real data nests 1 level. */
175
178
  const MAX_PROGRESS_DEPTH = 8;
176
- /**
177
- * Extracts canonical SessionEvents from a raw JSONL value.
178
- *
179
- * Claude Code wraps subagent/SDK events as
180
- * `{ type: 'progress', data: { message: <SessionEvent> } }`, which
181
- * `sessionEventSchema` alone rejects. This helper tries a direct parse,
182
- * then unwraps progress envelopes (recursively, in case of nesting).
183
- * Returns zero events for unrecognized input — never throws.
184
- */
185
- function extractSessionEvents(raw, depth = 0) {
186
- return extractSessionEventsWithProvenance(raw, depth, {});
179
+ function extractSessionEvents(raw, depthOrOptions = 0) {
180
+ if (typeof depthOrOptions === 'number') {
181
+ return extractSessionEventsWithProvenance(raw, depthOrOptions, {}, {});
182
+ }
183
+ return extractSessionEventsWithProvenance(raw, 0, {}, depthOrOptions ?? {});
187
184
  }
188
- function extractSessionEventsWithProvenance(raw, depth, inherited) {
185
+ function extractSessionEventsWithProvenance(raw, depth, inherited, options) {
189
186
  const direct = exports.sessionEventSchema.safeParse(raw);
190
187
  if (direct.success)
191
188
  return [{ ...inherited, ...direct.data }];
189
+ if (options.includeTaskNotifications) {
190
+ const notification = taskNotificationAttachmentEvent(raw);
191
+ if (notification)
192
+ return [{ ...inherited, ...notification }];
193
+ }
192
194
  if (depth >= MAX_PROGRESS_DEPTH)
193
195
  return [];
194
196
  if (typeof raw === 'object' && raw !== null && raw.type === 'progress') {
@@ -197,12 +199,31 @@ function extractSessionEventsWithProvenance(raw, depth, inherited) {
197
199
  if (typeof data === 'object' && data !== null) {
198
200
  const message = data.message;
199
201
  if (typeof message === 'object' && message !== null) {
200
- return extractSessionEventsWithProvenance(message, depth + 1, provenance);
202
+ return extractSessionEventsWithProvenance(message, depth + 1, provenance, options);
201
203
  }
202
204
  }
203
205
  }
204
206
  return [];
205
207
  }
208
+ /** The `system` event for a task-notification attachment line, or `null`. */
209
+ function taskNotificationAttachmentEvent(raw) {
210
+ const prompt = (0, subagentSpawn_1.taskNotificationPromptFromAttachment)(raw);
211
+ if (prompt === null)
212
+ return null;
213
+ const line = raw;
214
+ const candidate = {
215
+ type: 'system',
216
+ timestamp: line.timestamp,
217
+ origin: { kind: 'task-notification' },
218
+ message: { role: 'user', sourceLabel: 'task notification', content: prompt },
219
+ };
220
+ for (const key of ['entrypoint', 'isSidechain', 'cwd', 'gitBranch']) {
221
+ if (line[key] !== undefined)
222
+ candidate[key] = line[key];
223
+ }
224
+ const parsed = exports.systemSessionEventSchema.safeParse(candidate);
225
+ return parsed.success ? parsed.data : null;
226
+ }
206
227
  function progressProvenance(raw) {
207
228
  const value = raw;
208
229
  const provenance = {};
@@ -12,6 +12,8 @@ export declare const canonicalTranscriptBlockSchema: z.ZodObject<{
12
12
  }>;
13
13
  text: z.ZodOptional<z.ZodString>;
14
14
  toolName: z.ZodOptional<z.ZodString>;
15
+ rawToolName: z.ZodOptional<z.ZodString>;
16
+ mcpServerName: z.ZodOptional<z.ZodString>;
15
17
  toolUseId: z.ZodOptional<z.ZodString>;
16
18
  input: z.ZodOptional<z.ZodUnknown>;
17
19
  output: z.ZodOptional<z.ZodUnknown>;
@@ -20,6 +20,8 @@ exports.canonicalTranscriptBlockSchema = zod_1.z.object({
20
20
  type: zod_1.z.enum(['text', 'thinking', 'tool_use', 'tool_result', 'image', 'unknown']),
21
21
  text: zod_1.z.string().optional(),
22
22
  toolName: zod_1.z.string().optional(),
23
+ rawToolName: zod_1.z.string().optional(),
24
+ mcpServerName: zod_1.z.string().optional(),
23
25
  toolUseId: zod_1.z.string().optional(),
24
26
  input: zod_1.z.unknown().optional(),
25
27
  output: zod_1.z.unknown().optional(),
@@ -31,6 +33,8 @@ exports.canonicalTranscriptBlockSchema = zod_1.z.object({
31
33
  exports.canonicalToolCallSchema = zod_1.z.object({
32
34
  id: zod_1.z.string().optional(),
33
35
  name: zod_1.z.string(),
36
+ rawToolName: zod_1.z.string().optional(),
37
+ mcpServerName: zod_1.z.string().optional(),
34
38
  timestamp: zod_1.z.string(),
35
39
  input: zod_1.z.unknown().optional(),
36
40
  output: zod_1.z.unknown().optional(),
@@ -28,7 +28,13 @@ export interface SessionPromptHistoryBounds {
28
28
  maxSessionBytes?: number;
29
29
  /** Bytes read across all sessions in one call. Default 256 MiB. */
30
30
  maxTotalBytes?: number;
31
- /** Largest single log record that is parsed. Default 16 MiB. */
31
+ /**
32
+ * Largest single log record that is parsed. Default 16 MiB
33
+ * (`PROMPT_HISTORY_ORDINAL_MAX_RECORD_BYTES`). A prompt inside a longer
34
+ * record is never seen, so later ordinals do not count it: ordinals match
35
+ * `collectPromptHistory()` (and usage events' `promptOrdinal`) only when
36
+ * both use the default.
37
+ */
32
38
  maxRecordBytes?: number;
33
39
  /** Wall-clock budget for the call. Default 60 000 ms. */
34
40
  deadlineMs?: number;
@@ -56,7 +56,7 @@ const DEFAULT_SESSION_BOUNDS = {
56
56
  maxSessions: 1000,
57
57
  maxSessionBytes: 256 * 1024 * 1024,
58
58
  maxTotalBytes: 256 * 1024 * 1024,
59
- maxRecordBytes: promptHistory_1.DEFAULT_BOUNDS.maxRecordBytes,
59
+ maxRecordBytes: promptHistory_1.PROMPT_HISTORY_ORDINAL_MAX_RECORD_BYTES,
60
60
  deadlineMs: 60_000,
61
61
  };
62
62
  const MAX_EXCLUSIONS = 1000;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * `sidekick-shared/statusline/payload` — the pure Claude Code status-line
3
+ * stdin parser on its own, for hosts with a tight bundle budget that render
4
+ * their own line. Loads no account, credential, quota-snapshot, or network
5
+ * code (the full `sidekick-shared/statusline` entry does).
6
+ *
7
+ * @module statusline/payload
8
+ */
9
+ export { parseClaudeStatuslinePayload, quotaFromStatuslinePayload, } from './claudeStatuslinePayload';
10
+ export type { ClaudeStatuslinePayload, ClaudeStatuslineRateLimitWindow, QuotaFromStatuslineOptions, } from './claudeStatuslinePayload';
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ /**
3
+ * `sidekick-shared/statusline/payload` — the pure Claude Code status-line
4
+ * stdin parser on its own, for hosts with a tight bundle budget that render
5
+ * their own line. Loads no account, credential, quota-snapshot, or network
6
+ * code (the full `sidekick-shared/statusline` entry does).
7
+ *
8
+ * @module statusline/payload
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.quotaFromStatuslinePayload = exports.parseClaudeStatuslinePayload = void 0;
12
+ var claudeStatuslinePayload_1 = require("./claudeStatuslinePayload");
13
+ Object.defineProperty(exports, "parseClaudeStatuslinePayload", { enumerable: true, get: function () { return claudeStatuslinePayload_1.parseClaudeStatuslinePayload; } });
14
+ Object.defineProperty(exports, "quotaFromStatuslinePayload", { enumerable: true, get: function () { return claudeStatuslinePayload_1.quotaFromStatuslinePayload; } });
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Recognizes Claude Code subagent spawns and background-task notifications.
3
+ *
4
+ * Claude Code's subagent tool is `Agent`; `Task` is its earlier name (and the
5
+ * name OpenCode's `task` tool is normalized to). A background agent's spawning
6
+ * tool call gets its result right away ("Async agent launched successfully.")
7
+ * and its completion arrives later as a `<task-notification>` block, delivered
8
+ * either as a user line or as a mid-turn `queued_command` attachment.
9
+ *
10
+ * Pure: no filesystem access, so it is safe for browser bundles.
11
+ *
12
+ * @module subagentSpawn
13
+ */
14
+ import type { SubagentOutcome } from './aggregation/types';
15
+ import type { SessionEvent } from './types/sessionEvent';
16
+ /** Tool names that spawn a subagent: `Agent` (current) and `Task` (earlier name). */
17
+ export declare const SUBAGENT_SPAWN_TOOL_NAMES: readonly string[];
18
+ /** Whether a tool name spawns a subagent (`Agent` or `Task`). */
19
+ export declare function isSubagentSpawnTool(name: string | null | undefined): boolean;
20
+ /** A spawning tool result that launched the agent in the background. */
21
+ export interface AsyncAgentLaunch {
22
+ /** The agent id Claude Code reported (`agentId: <id>`), when present. */
23
+ agentId?: string;
24
+ }
25
+ /**
26
+ * Plain text of tool-result or message content: a string as-is, or the text
27
+ * blocks of a content array joined by newlines.
28
+ */
29
+ export declare function contentText(content: unknown): string;
30
+ /**
31
+ * Recognizes the immediate result of a background spawn
32
+ * (`Async agent launched successfully.` … `agentId: <id> …`). Accepts the
33
+ * result text or the raw tool-result content. Returns `null` for anything
34
+ * else, including a finished foreground agent's result.
35
+ */
36
+ export declare function parseAsyncAgentLaunch(result: unknown): AsyncAgentLaunch | null;
37
+ /** Fields of a `<task-notification>` block. */
38
+ export interface TaskNotification {
39
+ /** `<task-id>`: the background agent's id (or a background shell task's id). */
40
+ taskId: string;
41
+ /** `<tool-use-id>`: the tool call that started the background work. */
42
+ toolUseId?: string;
43
+ /** `<status>` as written, e.g. `completed`, `failed`, `killed`, `stopped`. */
44
+ status?: string;
45
+ /** `<summary>`, e.g. `Agent "Explore auth" finished`. */
46
+ summary?: string;
47
+ /**
48
+ * The agent stopped while background work of its own is still running; it
49
+ * may resume, and the same task id notifies again.
50
+ */
51
+ interim: boolean;
52
+ }
53
+ /**
54
+ * Parses the first `<task-notification>` block in `text`. Whitespace and
55
+ * newlines between tags, missing fields, and unknown extra fields are
56
+ * tolerated; the block needs a `<task-id>`. Fields are read before the
57
+ * agent's own `<result>` output, which may quote anything.
58
+ */
59
+ export declare function parseTaskNotification(text: unknown): TaskNotification | null;
60
+ /**
61
+ * The `<task-notification>` prompt of a raw Claude Code attachment line
62
+ * (`{ type: 'attachment', attachment: { type: 'queued_command', commandMode:
63
+ * 'task-notification', prompt } }`), which is how a notification that lands
64
+ * mid-turn is recorded. `null` for any other value.
65
+ */
66
+ export declare function taskNotificationPromptFromAttachment(raw: unknown): string | null;
67
+ /**
68
+ * The synthetic `system` event `extractSessionEvents(raw, {
69
+ * includeTaskNotifications: true })` makes from a mid-turn task-notification
70
+ * attachment. It is bookkeeping, not a message or a prompt.
71
+ */
72
+ export declare function isTaskNotificationSystemEvent(event: SessionEvent): boolean;
73
+ /**
74
+ * The task notification an event carries: a user line with
75
+ * `origin.kind === 'task-notification'` or whose text starts with
76
+ * `<task-notification>`, or the opt-in synthetic system event. `null` otherwise.
77
+ */
78
+ export declare function taskNotificationFromEvent(event: SessionEvent): TaskNotification | null;
79
+ /** Maps a notification `<status>` to an outcome; unknown or missing statuses read as `completed`. */
80
+ export declare function taskNotificationOutcome(status: string | undefined): SubagentOutcome;
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+ /**
3
+ * Recognizes Claude Code subagent spawns and background-task notifications.
4
+ *
5
+ * Claude Code's subagent tool is `Agent`; `Task` is its earlier name (and the
6
+ * name OpenCode's `task` tool is normalized to). A background agent's spawning
7
+ * tool call gets its result right away ("Async agent launched successfully.")
8
+ * and its completion arrives later as a `<task-notification>` block, delivered
9
+ * either as a user line or as a mid-turn `queued_command` attachment.
10
+ *
11
+ * Pure: no filesystem access, so it is safe for browser bundles.
12
+ *
13
+ * @module subagentSpawn
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.SUBAGENT_SPAWN_TOOL_NAMES = void 0;
17
+ exports.isSubagentSpawnTool = isSubagentSpawnTool;
18
+ exports.contentText = contentText;
19
+ exports.parseAsyncAgentLaunch = parseAsyncAgentLaunch;
20
+ exports.parseTaskNotification = parseTaskNotification;
21
+ exports.taskNotificationPromptFromAttachment = taskNotificationPromptFromAttachment;
22
+ exports.isTaskNotificationSystemEvent = isTaskNotificationSystemEvent;
23
+ exports.taskNotificationFromEvent = taskNotificationFromEvent;
24
+ exports.taskNotificationOutcome = taskNotificationOutcome;
25
+ /** Tool names that spawn a subagent: `Agent` (current) and `Task` (earlier name). */
26
+ exports.SUBAGENT_SPAWN_TOOL_NAMES = Object.freeze(['Agent', 'Task']);
27
+ const SPAWN_TOOLS = new Set(exports.SUBAGENT_SPAWN_TOOL_NAMES);
28
+ /** Whether a tool name spawns a subagent (`Agent` or `Task`). */
29
+ function isSubagentSpawnTool(name) {
30
+ return typeof name === 'string' && SPAWN_TOOLS.has(name);
31
+ }
32
+ const ASYNC_LAUNCH_PREFIX = /^\s*Async agent launched successfully\b/;
33
+ const AGENT_ID_LINE = /(?:^|\n)[ \t]*agentId:[ \t]*([A-Za-z0-9][\w.@-]*)/;
34
+ /**
35
+ * Plain text of tool-result or message content: a string as-is, or the text
36
+ * blocks of a content array joined by newlines.
37
+ */
38
+ function contentText(content) {
39
+ if (typeof content === 'string')
40
+ return content;
41
+ if (!Array.isArray(content))
42
+ return '';
43
+ const parts = [];
44
+ for (const block of content) {
45
+ if (!block || typeof block !== 'object')
46
+ continue;
47
+ const typed = block;
48
+ if (typed.type === 'text' && typeof typed.text === 'string')
49
+ parts.push(typed.text);
50
+ }
51
+ return parts.join('\n');
52
+ }
53
+ /**
54
+ * Recognizes the immediate result of a background spawn
55
+ * (`Async agent launched successfully.` … `agentId: <id> …`). Accepts the
56
+ * result text or the raw tool-result content. Returns `null` for anything
57
+ * else, including a finished foreground agent's result.
58
+ */
59
+ function parseAsyncAgentLaunch(result) {
60
+ const text = contentText(result);
61
+ if (!ASYNC_LAUNCH_PREFIX.test(text))
62
+ return null;
63
+ const match = AGENT_ID_LINE.exec(text);
64
+ return match ? { agentId: match[1] } : {};
65
+ }
66
+ const NOTIFICATION_OPEN = '<task-notification>';
67
+ const NOTIFICATION_CLOSE = '</task-notification>';
68
+ const NOTIFICATION_PREFIX = /^\s*<task-notification>/;
69
+ const TAG_PATTERNS = {
70
+ taskId: /<task-id>\s*([\s\S]*?)\s*<\/task-id>/,
71
+ toolUseId: /<tool-use-id>\s*([\s\S]*?)\s*<\/tool-use-id>/,
72
+ status: /<status>\s*([\s\S]*?)\s*<\/status>/,
73
+ summary: /<summary>\s*([\s\S]*?)\s*<\/summary>/,
74
+ note: /<note>\s*([\s\S]*?)\s*<\/note>/,
75
+ };
76
+ const INTERIM_TEXT = /waiting on its own background work|background work of its own still running/i;
77
+ function tagValue(body, pattern) {
78
+ const value = pattern.exec(body)?.[1]?.trim();
79
+ return value ? value : undefined;
80
+ }
81
+ /**
82
+ * Parses the first `<task-notification>` block in `text`. Whitespace and
83
+ * newlines between tags, missing fields, and unknown extra fields are
84
+ * tolerated; the block needs a `<task-id>`. Fields are read before the
85
+ * agent's own `<result>` output, which may quote anything.
86
+ */
87
+ function parseTaskNotification(text) {
88
+ if (typeof text !== 'string')
89
+ return null;
90
+ const start = text.indexOf(NOTIFICATION_OPEN);
91
+ if (start < 0)
92
+ return null;
93
+ let body = text.slice(start + NOTIFICATION_OPEN.length);
94
+ const resultAt = body.indexOf('<result>');
95
+ let header = resultAt >= 0 ? body.slice(0, resultAt) : body;
96
+ if (resultAt < 0) {
97
+ const end = body.indexOf(NOTIFICATION_CLOSE);
98
+ if (end >= 0)
99
+ header = body = body.slice(0, end);
100
+ }
101
+ if (!TAG_PATTERNS.taskId.test(header))
102
+ header = body;
103
+ const taskId = tagValue(header, TAG_PATTERNS.taskId);
104
+ if (!taskId)
105
+ return null;
106
+ const toolUseId = tagValue(header, TAG_PATTERNS.toolUseId);
107
+ const status = tagValue(header, TAG_PATTERNS.status);
108
+ const summary = tagValue(header, TAG_PATTERNS.summary);
109
+ const note = tagValue(header, TAG_PATTERNS.note);
110
+ return {
111
+ taskId,
112
+ ...(toolUseId ? { toolUseId } : {}),
113
+ ...(status ? { status } : {}),
114
+ ...(summary ? { summary } : {}),
115
+ interim: INTERIM_TEXT.test(summary ?? '') || INTERIM_TEXT.test(note ?? ''),
116
+ };
117
+ }
118
+ /**
119
+ * The `<task-notification>` prompt of a raw Claude Code attachment line
120
+ * (`{ type: 'attachment', attachment: { type: 'queued_command', commandMode:
121
+ * 'task-notification', prompt } }`), which is how a notification that lands
122
+ * mid-turn is recorded. `null` for any other value.
123
+ */
124
+ function taskNotificationPromptFromAttachment(raw) {
125
+ if (!raw || typeof raw !== 'object')
126
+ return null;
127
+ const line = raw;
128
+ if (line.type !== 'attachment' || !line.attachment || typeof line.attachment !== 'object') {
129
+ return null;
130
+ }
131
+ const attachment = line.attachment;
132
+ return attachment.type === 'queued_command' &&
133
+ attachment.commandMode === 'task-notification' &&
134
+ typeof attachment.prompt === 'string'
135
+ ? attachment.prompt
136
+ : null;
137
+ }
138
+ /**
139
+ * The synthetic `system` event `extractSessionEvents(raw, {
140
+ * includeTaskNotifications: true })` makes from a mid-turn task-notification
141
+ * attachment. It is bookkeeping, not a message or a prompt.
142
+ */
143
+ function isTaskNotificationSystemEvent(event) {
144
+ return event.type === 'system' && event.origin?.kind === 'task-notification';
145
+ }
146
+ /**
147
+ * The task notification an event carries: a user line with
148
+ * `origin.kind === 'task-notification'` or whose text starts with
149
+ * `<task-notification>`, or the opt-in synthetic system event. `null` otherwise.
150
+ */
151
+ function taskNotificationFromEvent(event) {
152
+ if (event.type !== 'user' && event.type !== 'system')
153
+ return null;
154
+ const flagged = event.origin?.kind === 'task-notification';
155
+ if (event.type === 'system' && !flagged)
156
+ return null;
157
+ const text = contentText(event.message?.content);
158
+ if (!flagged && !NOTIFICATION_PREFIX.test(text))
159
+ return null;
160
+ return parseTaskNotification(text);
161
+ }
162
+ /** Maps a notification `<status>` to an outcome; unknown or missing statuses read as `completed`. */
163
+ function taskNotificationOutcome(status) {
164
+ switch (status?.toLowerCase()) {
165
+ case 'failed':
166
+ return 'failed';
167
+ case 'killed':
168
+ return 'killed';
169
+ case 'stopped':
170
+ return 'stopped';
171
+ default:
172
+ return 'completed';
173
+ }
174
+ }
@@ -15,6 +15,20 @@ export interface CredentialGuardOptions {
15
15
  blockedBinaries?: string[];
16
16
  /** Extra real paths to protect, besides the Claude, Codex, and Sidekick homes. */
17
17
  protectedPaths?: string[];
18
+ /**
19
+ * Home overrides to keep instead of unsetting, for a suite that points one
20
+ * at its own fixture before the guard installs. Accepts only the variables
21
+ * the guard unsets: `CLAUDE_CONFIG_DIR`, `CLAUDE_SECURESTORAGE_CONFIG_DIR`,
22
+ * `CODEX_HOME`, and `SIDEKICK_CONFIG_DIR`.
23
+ *
24
+ * Install throws, before changing anything, for any other name, or when a
25
+ * preserved value is, contains, or lies inside a real protected location:
26
+ * the real home's `.claude`, `.claude.json`, and `.codex`, the default
27
+ * Sidekick config dir, or a `protectedPaths` entry. Symlinks are resolved
28
+ * first. The guard cannot tell a real store kept elsewhere from a fixture,
29
+ * so preserve only paths the suite created. Blank values are kept unchecked.
30
+ */
31
+ preserveEnv?: readonly string[];
18
32
  }
19
33
  /**
20
34
  * Installs the guard once per process and returns it; later calls return the