@telora/daemon 0.23.29 → 0.23.32

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 (77) hide show
  1. package/build-info.json +9 -3
  2. package/dist/backends/claude/one-shot-model.d.ts.map +1 -1
  3. package/dist/backends/claude/one-shot-model.js +1 -0
  4. package/dist/backends/claude/one-shot-model.js.map +1 -1
  5. package/dist/composition/daemon-contexts.d.ts.map +1 -1
  6. package/dist/composition/daemon-contexts.js +2 -0
  7. package/dist/composition/daemon-contexts.js.map +1 -1
  8. package/dist/composition/qa-author-pass-context.d.ts +7 -0
  9. package/dist/composition/qa-author-pass-context.d.ts.map +1 -0
  10. package/dist/composition/qa-author-pass-context.js +68 -0
  11. package/dist/composition/qa-author-pass-context.js.map +1 -0
  12. package/dist/composition/spawn-pass-deps.d.ts +1 -1
  13. package/dist/composition/spawn-pass-deps.d.ts.map +1 -1
  14. package/dist/config/live-daemon-config.js +1 -1
  15. package/dist/daemon-state/provider-window.d.ts.map +1 -1
  16. package/dist/daemon-state/provider-window.js +1 -0
  17. package/dist/daemon-state/provider-window.js.map +1 -1
  18. package/dist/focus/daemon-contexts.d.ts +3 -0
  19. package/dist/focus/daemon-contexts.d.ts.map +1 -1
  20. package/dist/focus/daemon-contexts.js.map +1 -1
  21. package/dist/focus/focus-loop-registry.d.ts.map +1 -1
  22. package/dist/focus/focus-loop-registry.js +21 -0
  23. package/dist/focus/focus-loop-registry.js.map +1 -1
  24. package/dist/git/types.d.ts.map +1 -1
  25. package/dist/git/types.js +1 -0
  26. package/dist/git/types.js.map +1 -1
  27. package/dist/prompt-sections/qa-author-section.d.ts +54 -0
  28. package/dist/prompt-sections/qa-author-section.d.ts.map +1 -0
  29. package/dist/prompt-sections/qa-author-section.js +189 -0
  30. package/dist/prompt-sections/qa-author-section.js.map +1 -0
  31. package/dist/qa/qa-author-faithfulness.d.ts +67 -0
  32. package/dist/qa/qa-author-faithfulness.d.ts.map +1 -0
  33. package/dist/qa/qa-author-faithfulness.js +157 -0
  34. package/dist/qa/qa-author-faithfulness.js.map +1 -0
  35. package/dist/qa/qa-author-mode.d.ts +34 -0
  36. package/dist/qa/qa-author-mode.d.ts.map +1 -0
  37. package/dist/qa/qa-author-mode.js +29 -0
  38. package/dist/qa/qa-author-mode.js.map +1 -0
  39. package/dist/qa/qa-author-output.d.ts +23 -0
  40. package/dist/qa/qa-author-output.d.ts.map +1 -0
  41. package/dist/qa/qa-author-output.js +66 -0
  42. package/dist/qa/qa-author-output.js.map +1 -0
  43. package/dist/qa/qa-author-pass.d.ts +60 -0
  44. package/dist/qa/qa-author-pass.d.ts.map +1 -0
  45. package/dist/qa/qa-author-pass.js +163 -0
  46. package/dist/qa/qa-author-pass.js.map +1 -0
  47. package/dist/qa/qa-author-runner.d.ts +60 -0
  48. package/dist/qa/qa-author-runner.d.ts.map +1 -0
  49. package/dist/qa/qa-author-runner.js +175 -0
  50. package/dist/qa/qa-author-runner.js.map +1 -0
  51. package/dist/qa/qa-focus-changes.d.ts +32 -0
  52. package/dist/qa/qa-focus-changes.d.ts.map +1 -0
  53. package/dist/qa/qa-focus-changes.js +59 -0
  54. package/dist/qa/qa-focus-changes.js.map +1 -0
  55. package/dist/queries/api-client/shared.d.ts +1 -0
  56. package/dist/queries/api-client/shared.d.ts.map +1 -1
  57. package/dist/queries/api-client/shared.js +14 -1
  58. package/dist/queries/api-client/shared.js.map +1 -1
  59. package/dist/queries/deliveries/deliveries.d.ts +15 -27
  60. package/dist/queries/deliveries/deliveries.d.ts.map +1 -1
  61. package/dist/queries/deliveries/deliveries.js +5 -37
  62. package/dist/queries/deliveries/deliveries.js.map +1 -1
  63. package/dist/queries/index.d.ts +1 -1
  64. package/dist/queries/index.d.ts.map +1 -1
  65. package/dist/queries/index.js +1 -1
  66. package/dist/queries/index.js.map +1 -1
  67. package/dist/queries/qa-packets/qa-packets.d.ts +83 -0
  68. package/dist/queries/qa-packets/qa-packets.d.ts.map +1 -0
  69. package/dist/queries/qa-packets/qa-packets.js +79 -0
  70. package/dist/queries/qa-packets/qa-packets.js.map +1 -0
  71. package/dist/testing/fixtures/api-client.js +1 -1
  72. package/dist/testing/fixtures/index.js +1 -1
  73. package/dist/types/qa-packet.d.ts +28 -0
  74. package/dist/types/qa-packet.d.ts.map +1 -0
  75. package/dist/types/qa-packet.js +36 -0
  76. package/dist/types/qa-packet.js.map +1 -0
  77. package/package.json +3 -3
@@ -0,0 +1,60 @@
1
+ /**
2
+ * The QA-author pass: for every configured product, the foci that wait in its
3
+ * QA queue with no packet for the cycle (or an abandoned pending one) get a UI
4
+ * test packet, one focus at a time.
5
+ *
6
+ * Per candidate: write a pending packet (or refresh the abandoned one) -> author
7
+ * it (qa-author-runner.ts) -> write it ready, no_ui_surface or failed with the
8
+ * reason. A ready write the edge refuses is written failed with the edge's
9
+ * message. A provider rate limit leaves the packet pending: the edge re-offers
10
+ * a pending packet untouched for 30 minutes, which is the retry. The pass's
11
+ * only writes are those packet writes (product-api daemon_qa_packet_write); it
12
+ * never writes a focus's status, stage, execution status, review state or arm.
13
+ *
14
+ * IT NEVER DELAYS ARMED WORK. It acquires no ResourceGovernor slot and never
15
+ * waits on one; it reads the governor's utilization instead and does not start
16
+ * (or continue to the next focus) while every session slot is taken or a spawn
17
+ * is queued for one, nor while the claude provider's rate-limit window is
18
+ * open (nor while the daemon is read-only: it could write no packet). At most ONE pass runs per daemon: a tick that finds one in flight
19
+ * returns at once.
20
+ */
21
+ import type { ResourceGovernor, ResourceUtilization } from '@telora/daemon-core';
22
+ import type { QaPacketCandidate, QaPacketWrite, QaPacketWriteResult } from '../queries/qa-packets/qa-packets.js';
23
+ import type { QaAuthorResult } from './qa-author-runner.js';
24
+ /** The loop's cadence. */
25
+ export declare const QA_AUTHOR_PASS_INTERVAL_MS = 60000;
26
+ export interface QaAuthorPassDeps {
27
+ products: ReadonlyArray<{
28
+ id: string;
29
+ }>;
30
+ readCandidates: (productId: string) => Promise<QaPacketCandidate[]>;
31
+ writePacket: (write: QaPacketWrite) => Promise<QaPacketWriteResult>;
32
+ author: (productId: string, candidate: QaPacketCandidate) => Promise<QaAuthorResult>;
33
+ /** Why the pass must not start or go on to the next focus now, or null. */
34
+ holdReason: () => string | null;
35
+ }
36
+ /** The pass's deps over the running daemon, or null before it has a live config (composition/qa-author-pass-context.ts). */
37
+ export type QaAuthorPassDepsSource = (governor: ResourceGovernor | null) => QaAuthorPassDeps | null;
38
+ export interface QaAuthorPassOutcome {
39
+ /** Why the pass stopped early or did not start, when it did. */
40
+ skipped: string | null;
41
+ /** Packets written ready or no_ui_surface. */
42
+ written: number;
43
+ /** Packets written failed. */
44
+ failed: number;
45
+ /** Packets left pending (provider rate limit). */
46
+ held: number;
47
+ /** Candidates no packet could be written for, and failed reads. */
48
+ errors: number;
49
+ }
50
+ /**
51
+ * Why armed work, the provider or the daemon's mode forbids a pass now, or
52
+ * null. Pure: armed work keeps priority while every slot is taken or a spawn
53
+ * waits for one; a read-only daemon could write no packet.
54
+ */
55
+ export declare function qaPassHoldReason(utilization: ResourceUtilization | null, providerHoldUntilMs: number | null, readOnly: boolean): string | null;
56
+ /** Run one pass over every product. One product's failed read never stops another's. */
57
+ export declare function runQaAuthorPass(deps: QaAuthorPassDeps): Promise<QaAuthorPassOutcome>;
58
+ /** The loop tick: one pass over the live deps, never throwing into the timer. */
59
+ export declare function runQaAuthorPassSafely(source: QaAuthorPassDepsSource, governor: ResourceGovernor | null): Promise<void>;
60
+ //# sourceMappingURL=qa-author-pass.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qa-author-pass.d.ts","sourceRoot":"","sources":["../../src/qa/qa-author-pass.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAEjF,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAEjH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAI5D,0BAA0B;AAC1B,eAAO,MAAM,0BAA0B,QAAS,CAAC;AAEjD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,aAAa,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxC,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC;IACpE,WAAW,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACpE,MAAM,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,iBAAiB,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IACrF,2EAA2E;IAC3E,UAAU,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,4HAA4H;AAC5H,MAAM,MAAM,sBAAsB,GAAG,CAAC,QAAQ,EAAE,gBAAgB,GAAG,IAAI,KAAK,gBAAgB,GAAG,IAAI,CAAC;AAEpG,MAAM,WAAW,mBAAmB;IAClC,gEAAgE;IAChE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,8BAA8B;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,mBAAmB,GAAG,IAAI,EACvC,mBAAmB,EAAE,MAAM,GAAG,IAAI,EAClC,QAAQ,EAAE,OAAO,GAChB,MAAM,GAAG,IAAI,CAMf;AAmGD,wFAAwF;AACxF,wBAAsB,eAAe,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA0B1F;AAED,iFAAiF;AACjF,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,sBAAsB,EAAE,QAAQ,EAAE,gBAAgB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAc5H"}
@@ -0,0 +1,163 @@
1
+ // PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
2
+ // Pinned by: packages/daemon/src/qa/qa-author-pass.test.ts,
3
+ // packages/daemon/src/qa/qa-author-unfaithful-answer.test.ts
4
+ // PINNED-BY-END
5
+ import { createLogger } from '../log/daemon-log.js';
6
+ import { QA_PACKET_STATUS } from '../types/qa-packet.js';
7
+ const log = createLogger({ module: 'qa-author' });
8
+ /** The loop's cadence. */
9
+ export const QA_AUTHOR_PASS_INTERVAL_MS = 60_000;
10
+ /**
11
+ * Why armed work, the provider or the daemon's mode forbids a pass now, or
12
+ * null. Pure: armed work keeps priority while every slot is taken or a spawn
13
+ * waits for one; a read-only daemon could write no packet.
14
+ */
15
+ export function qaPassHoldReason(utilization, providerHoldUntilMs, readOnly) {
16
+ if (readOnly)
17
+ return 'the daemon is in read-only mode';
18
+ if (utilization && utilization.queueDepth > 0)
19
+ return `armed work is waiting for ${utilization.queueDepth} session slot(s)`;
20
+ if (utilization && utilization.total >= utilization.max)
21
+ return `every session slot is in use (${utilization.total}/${utilization.max})`;
22
+ if (providerHoldUntilMs !== null)
23
+ return `the claude provider rate-limit window is open until ${new Date(providerHoldUntilMs).toISOString()}`;
24
+ return null;
25
+ }
26
+ let passInFlight = false;
27
+ function errorMessage(err) {
28
+ return err instanceof Error ? err.message : String(err);
29
+ }
30
+ function tag(candidate) {
31
+ return `focus ${candidate.focusId.slice(0, 8)} ("${candidate.focusName}")`;
32
+ }
33
+ /** Write a packet failed; false (logged) when even that write did not land. */
34
+ async function writeFailed(deps, candidate, packetId, reason) {
35
+ try {
36
+ await deps.writePacket({ focusId: candidate.focusId, packetId, status: QA_PACKET_STATUS.FAILED, failureReason: reason });
37
+ log.warn(`[qa-author] ${tag(candidate)}: packet failed -- ${reason}`);
38
+ return true;
39
+ }
40
+ catch (err) {
41
+ log.warn(`[qa-author] ${tag(candidate)}: could not record the failed packet (${reason}): ${errorMessage(err)}`);
42
+ return false;
43
+ }
44
+ }
45
+ /** Open the cycle's pending packet: refresh the abandoned one, or write a new one. Null (logged) when none could be. */
46
+ async function openPendingPacket(deps, candidate) {
47
+ try {
48
+ if (candidate.pendingPacketId) {
49
+ await deps.writePacket({ focusId: candidate.focusId, packetId: candidate.pendingPacketId, status: QA_PACKET_STATUS.PENDING });
50
+ return candidate.pendingPacketId;
51
+ }
52
+ const opened = await deps.writePacket({ focusId: candidate.focusId, status: QA_PACKET_STATUS.PENDING });
53
+ if (!opened.attached) {
54
+ await writeFailed(deps, candidate, opened.packetId, 'the focus left its open QA cycle before the pass ran');
55
+ return null;
56
+ }
57
+ return opened.packetId;
58
+ }
59
+ catch (err) {
60
+ log.warn(`[qa-author] ${tag(candidate)}: could not open a pending packet: ${errorMessage(err)}`);
61
+ return null;
62
+ }
63
+ }
64
+ /** The write a finished result makes. */
65
+ function finalWrite(candidate, packetId, result) {
66
+ const focusId = candidate.focusId;
67
+ switch (result.kind) {
68
+ case 'cases':
69
+ return { focusId, packetId, status: QA_PACKET_STATUS.READY, reviewedSha: result.reviewedSha, cases: result.cases, authorMode: result.authorMode };
70
+ case 'no_ui_surface':
71
+ return { focusId, packetId, status: QA_PACKET_STATUS.NO_UI_SURFACE, reviewedSha: result.reviewedSha, authorMode: result.authorMode };
72
+ default:
73
+ return {
74
+ focusId, packetId, status: QA_PACKET_STATUS.FAILED, failureReason: result.reason,
75
+ ...(result.reviewedSha ? { reviewedSha: result.reviewedSha } : {}),
76
+ ...(result.authorMode ? { authorMode: result.authorMode } : {}),
77
+ };
78
+ }
79
+ }
80
+ async function runCandidate(deps, productId, candidate) {
81
+ const packetId = await openPendingPacket(deps, candidate);
82
+ if (!packetId)
83
+ return 'errors';
84
+ let result;
85
+ try {
86
+ result = await deps.author(productId, candidate);
87
+ }
88
+ catch (err) {
89
+ result = { kind: 'failed', reason: `the QA-author pass threw: ${errorMessage(err)}`, authorMode: null, reviewedSha: null };
90
+ }
91
+ if (result.kind === 'held') {
92
+ log.info(`[qa-author] ${tag(candidate)}: packet left pending -- ${result.reason}`);
93
+ return 'held';
94
+ }
95
+ const write = finalWrite(candidate, packetId, result);
96
+ try {
97
+ await deps.writePacket(write);
98
+ }
99
+ catch (err) {
100
+ if (write.status === QA_PACKET_STATUS.FAILED) {
101
+ log.warn(`[qa-author] ${tag(candidate)}: could not record the failed packet: ${errorMessage(err)}`);
102
+ return 'errors';
103
+ }
104
+ const recorded = await writeFailed(deps, candidate, packetId, `the ${write.status} packet was refused: ${errorMessage(err)}`);
105
+ return recorded ? 'failed' : 'errors';
106
+ }
107
+ if (write.status === QA_PACKET_STATUS.FAILED) {
108
+ log.warn(`[qa-author] ${tag(candidate)}: packet failed -- ${write.failureReason ?? ''}`);
109
+ return 'failed';
110
+ }
111
+ log.info(`[qa-author] ${tag(candidate)}: packet ${write.status} (${write.authorMode ?? 'unknown'} pass, commit ${write.reviewedSha ?? '?'})`);
112
+ return 'written';
113
+ }
114
+ /** Run one pass over every product. One product's failed read never stops another's. */
115
+ export async function runQaAuthorPass(deps) {
116
+ const outcome = { skipped: null, written: 0, failed: 0, held: 0, errors: 0 };
117
+ if (passInFlight)
118
+ return { ...outcome, skipped: 'a QA-author pass is already running' };
119
+ passInFlight = true;
120
+ try {
121
+ for (const product of deps.products) {
122
+ let candidates;
123
+ const before = deps.holdReason();
124
+ if (before)
125
+ return { ...outcome, skipped: before };
126
+ try {
127
+ candidates = await deps.readCandidates(product.id);
128
+ }
129
+ catch (err) {
130
+ log.warn(`[qa-author] candidate read failed for product ${product.id.slice(0, 8)}: ${errorMessage(err)}`);
131
+ outcome.errors++;
132
+ continue;
133
+ }
134
+ for (const candidate of candidates) {
135
+ const hold = deps.holdReason();
136
+ if (hold)
137
+ return { ...outcome, skipped: hold };
138
+ outcome[await runCandidate(deps, product.id, candidate)]++;
139
+ }
140
+ }
141
+ return outcome;
142
+ }
143
+ finally {
144
+ passInFlight = false;
145
+ }
146
+ }
147
+ /** The loop tick: one pass over the live deps, never throwing into the timer. */
148
+ export async function runQaAuthorPassSafely(source, governor) {
149
+ try {
150
+ const deps = source(governor);
151
+ if (!deps)
152
+ return;
153
+ const o = await runQaAuthorPass(deps);
154
+ if (o.written + o.failed + o.held + o.errors > 0) {
155
+ log.info(`[qa-author] pass: written=${o.written} failed=${o.failed} held=${o.held} errors=${o.errors}` +
156
+ (o.skipped ? ` stopped=(${o.skipped})` : ''));
157
+ }
158
+ }
159
+ catch (err) {
160
+ log.warn(`[qa-author] pass failed: ${errorMessage(err)}`);
161
+ }
162
+ }
163
+ //# sourceMappingURL=qa-author-pass.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qa-author-pass.js","sourceRoot":"","sources":["../../src/qa/qa-author-pass.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ import type { QaPacketCandidate } from '../queries/qa-packets/qa-packets.js';
2
+ import { type QaAuthorMode } from '../types/qa-packet.js';
3
+ import { type QaGit } from './qa-focus-changes.js';
4
+ /** Each attempt's wall-clock bound. */
5
+ export declare const QA_AUTHOR_TIMEOUT_MS: number;
6
+ /** The resumed pass's turn cap (it may read files). */
7
+ export declare const QA_AUTHOR_RESUMED_MAX_TURNS = 40;
8
+ /** The fresh pass's turn cap (it has no tools). */
9
+ export declare const QA_AUTHOR_FRESH_MAX_TURNS = 3;
10
+ /** The read-only tools a resumed pass may use. */
11
+ export declare const QA_AUTHOR_READ_TOOLS = "Read,Grep,Glob";
12
+ /** One model run, as the runner asks for it. */
13
+ export interface QaAuthorInvokeOptions {
14
+ label: string;
15
+ cwd?: string;
16
+ extraArgs: readonly string[];
17
+ timeoutMs: number;
18
+ maxTurns: number;
19
+ }
20
+ /** Run the model once; resolve with its final answer text, or reject (a ModelUnavailable names why). */
21
+ export type QaAuthorInvoke = (systemPrompt: string, userMessage: string, opts: QaAuthorInvokeOptions) => Promise<{
22
+ text: string;
23
+ }>;
24
+ export interface QaAuthorRunnerDeps {
25
+ invoke: QaAuthorInvoke;
26
+ /** The focus worktree the coding session ran in, or null when none is known. */
27
+ worktreePathFor: (focusId: string, branchName: string | null) => string | null;
28
+ pathExists: (path: string) => boolean;
29
+ /** The product repository and its integration branch (the fresh pass reads the change there). */
30
+ repoPath: string;
31
+ integrationBranch: string;
32
+ git: QaGit;
33
+ }
34
+ export type QaAuthorResult = {
35
+ kind: 'cases';
36
+ cases: unknown[];
37
+ authorMode: QaAuthorMode;
38
+ reviewedSha: string;
39
+ } | {
40
+ kind: 'no_ui_surface';
41
+ authorMode: QaAuthorMode;
42
+ reviewedSha: string;
43
+ } | {
44
+ kind: 'failed';
45
+ reason: string;
46
+ authorMode: QaAuthorMode | null;
47
+ reviewedSha: string | null;
48
+ } | {
49
+ kind: 'held';
50
+ reason: string;
51
+ };
52
+ /** The argv a resumed pass adds to the one-shot argv. Pure. */
53
+ export declare function buildResumedArgs(sessionId: string): string[];
54
+ /** The argv a fresh pass adds to the one-shot argv: no tools, no MCP servers. Pure. */
55
+ export declare function buildFreshArgs(): string[];
56
+ /** The commit the packet is pinned to: the reviewed commit, else the focus branch head, else null. */
57
+ export declare function pinPacketCommit(candidate: QaPacketCandidate, deps: Pick<QaAuthorRunnerDeps, 'repoPath' | 'git'>): string | null;
58
+ /** Author one candidate's packet. Never throws for a model or parse problem (those are results). */
59
+ export declare function authorQaPacket(candidate: QaPacketCandidate, deps: QaAuthorRunnerDeps): Promise<QaAuthorResult>;
60
+ //# sourceMappingURL=qa-author-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qa-author-runner.d.ts","sourceRoot":"","sources":["../../src/qa/qa-author-runner.ts"],"names":[],"mappings":"AAoCA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AAE7E,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAI1E,OAAO,EAAmC,KAAK,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAIpF,uCAAuC;AACvC,eAAO,MAAM,oBAAoB,QAAc,CAAC;AAChD,uDAAuD;AACvD,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAC9C,mDAAmD;AACnD,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAC3C,kDAAkD;AAClD,eAAO,MAAM,oBAAoB,mBAAmB,CAAC;AAErD,gDAAgD;AAChD,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wGAAwG;AACxG,MAAM,MAAM,cAAc,GAAG,CAAC,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,qBAAqB,KAAK,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEnI,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,cAAc,CAAC;IACvB,gFAAgF;IAChF,eAAe,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,KAAK,MAAM,GAAG,IAAI,CAAC;IAC/E,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACtC,iGAAiG;IACjG,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,GAAG,EAAE,KAAK,CAAC;CACZ;AAED,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,EAAE,CAAC;IAAC,UAAU,EAAE,YAAY,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAClF;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,UAAU,EAAE,YAAY,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GACxE;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,YAAY,GAAG,IAAI,CAAC;IAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAC/F;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAYrC,+DAA+D;AAC/D,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAQ5D;AAED,uFAAuF;AACvF,wBAAgB,cAAc,IAAI,MAAM,EAAE,CAEzC;AAID,sGAAsG;AACtG,wBAAgB,eAAe,CAAC,SAAS,EAAE,iBAAiB,EAAE,IAAI,EAAE,IAAI,CAAC,kBAAkB,EAAE,UAAU,GAAG,KAAK,CAAC,GAAG,MAAM,GAAG,IAAI,CAK/H;AA0ED,oGAAoG;AACpG,wBAAsB,cAAc,CAAC,SAAS,EAAE,iBAAiB,EAAE,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,cAAc,CAAC,CA0CpH"}
@@ -0,0 +1,175 @@
1
+ // PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
2
+ // Pinned by: packages/daemon/src/composition/qa-author-pass-context.test.ts,
3
+ // packages/daemon/src/qa/qa-author-pass.test.ts, packages/daemon/src/qa/qa-author-runner.test.ts,
4
+ // packages/daemon/src/qa/qa-author-unfaithful-answer.test.ts
5
+ // PINNED-BY-END
6
+ /**
7
+ * Author one focus's UI test packet: pin it to the reviewed commit, run the
8
+ * QA-author model RESUMED (a fork of the coding team's own Claude session) when
9
+ * qa-author-mode.ts allows it, and fall back to a FRESH pass over the delivery
10
+ * summaries and the diff when it does not, or when the resumed pass fails to
11
+ * run or answers no usable packet.
12
+ *
13
+ * The resumed pass runs `--resume <coding session> --fork-session` in the focus
14
+ * worktree, so the coding session itself is never extended (and the one-shot
15
+ * argv keeps the fork unpersisted). Both passes are `claude --print` one-shots
16
+ * (backends/claude/one-shot-model.ts), bounded by a timeout and a turn cap,
17
+ * with no MCP servers; the resumed pass may read files (Read, Grep, Glob) and
18
+ * the fresh pass has no tools at all -- everything it knows is in its prompt.
19
+ *
20
+ * An answer is never handed back UNFAITHFUL (qa-author-faithfulness.ts: a case
21
+ * whose criterion is not one the pass was shown, a missing retest of a case a
22
+ * human failed last cycle). An unfaithful resumed answer falls back to the
23
+ * fresh pass, as an unusable one does; an unfaithful fresh answer gets ONE
24
+ * corrective attempt, the fresh prompt with the violations appended
25
+ * (buildQaAuthorCorrection); still unfaithful, the result is `failed` with the
26
+ * violations as its reason -- never a ready packet.
27
+ *
28
+ * A provider rate limit is not a failure of the pass: it returns `held`, and the
29
+ * caller leaves the packet pending for the edge's stale-pending rule to re-pick.
30
+ * Every other problem is a `failed` result with its reason. The model run is
31
+ * injected (QaAuthorRunnerDeps.invoke), so no test needs a claude binary.
32
+ */
33
+ import { createLogger } from '../log/daemon-log.js';
34
+ import { isModelUnavailable } from '../backends/claude/one-shot-model.js';
35
+ import { buildQaAuthorCorrection, buildQaAuthorPrompt, QA_AUTHOR_SYSTEM_PROMPT } from '../prompt-sections/qa-author-section.js';
36
+ import { QA_AUTHOR_MODE } from '../types/qa-packet.js';
37
+ import { qaAnswerFaithfulnessViolations } from './qa-author-faithfulness.js';
38
+ import { decideQaAuthorMode } from './qa-author-mode.js';
39
+ import { parseQaAuthorOutput } from './qa-author-output.js';
40
+ import { readFocusChanges, resolveCommit } from './qa-focus-changes.js';
41
+ const log = createLogger({ module: 'qa-author' });
42
+ /** Each attempt's wall-clock bound. */
43
+ export const QA_AUTHOR_TIMEOUT_MS = 10 * 60_000;
44
+ /** The resumed pass's turn cap (it may read files). */
45
+ export const QA_AUTHOR_RESUMED_MAX_TURNS = 40;
46
+ /** The fresh pass's turn cap (it has no tools). */
47
+ export const QA_AUTHOR_FRESH_MAX_TURNS = 3;
48
+ /** The read-only tools a resumed pass may use. */
49
+ export const QA_AUTHOR_READ_TOOLS = 'Read,Grep,Glob';
50
+ /** The argv a resumed pass adds to the one-shot argv. Pure. */
51
+ export function buildResumedArgs(sessionId) {
52
+ return [
53
+ '--resume', sessionId,
54
+ '--fork-session',
55
+ '--tools', QA_AUTHOR_READ_TOOLS,
56
+ '--allowed-tools', ...QA_AUTHOR_READ_TOOLS.split(','),
57
+ '--strict-mcp-config',
58
+ ];
59
+ }
60
+ /** The argv a fresh pass adds to the one-shot argv: no tools, no MCP servers. Pure. */
61
+ export function buildFreshArgs() {
62
+ return ['--tools', '', '--strict-mcp-config'];
63
+ }
64
+ const HEX_SHA = /^[0-9a-f]{7,64}$/i;
65
+ /** The commit the packet is pinned to: the reviewed commit, else the focus branch head, else null. */
66
+ export function pinPacketCommit(candidate, deps) {
67
+ const reviewed = candidate.reviewedSha?.trim();
68
+ if (reviewed && HEX_SHA.test(reviewed))
69
+ return resolveCommit(deps.repoPath, reviewed, deps.git) ?? reviewed.toLowerCase();
70
+ const branch = candidate.branchName?.trim();
71
+ return branch ? resolveCommit(deps.repoPath, branch, deps.git) : null;
72
+ }
73
+ function errorMessage(err) {
74
+ return err instanceof Error ? err.message : String(err);
75
+ }
76
+ async function runAttempt(candidate, mode, prompt, opts, reviewedSha, deps) {
77
+ let text;
78
+ try {
79
+ text = (await deps.invoke(QA_AUTHOR_SYSTEM_PROMPT, prompt, opts)).text;
80
+ }
81
+ catch (err) {
82
+ if (isModelUnavailable(err) && err.kind === 'rate_limit') {
83
+ return { kind: 'held', reason: `the ${mode} pass hit the provider rate limit: ${err.message}` };
84
+ }
85
+ return { kind: 'fall_back', reason: `the ${mode} pass could not run: ${errorMessage(err)}` };
86
+ }
87
+ const out = parseQaAuthorOutput(text);
88
+ if (out.kind === 'error')
89
+ return { kind: 'fall_back', reason: `the ${mode} pass answered no usable packet: ${out.error}` };
90
+ const violations = qaAnswerFaithfulnessViolations(candidate, out);
91
+ if (violations.length > 0) {
92
+ log.info(`[qa-author] focus ${candidate.focusId.slice(0, 8)}: ${opts.label} answer is not faithful (${violations.length} violation(s))`);
93
+ return {
94
+ kind: 'unfaithful',
95
+ violations,
96
+ reason: `the ${mode} pass answered a packet that is not faithful to the focus's criteria: ${violations.join('; ')}`,
97
+ };
98
+ }
99
+ if (out.kind === 'no_ui_surface')
100
+ return { kind: 'no_ui_surface', authorMode: mode, reviewedSha };
101
+ log.info(`[qa-author] focus ${candidate.focusId.slice(0, 8)}: ${mode} pass wrote ${out.cases.length} case(s)`);
102
+ return { kind: 'cases', cases: out.cases, authorMode: mode, reviewedSha };
103
+ }
104
+ /**
105
+ * The one corrective attempt after an unfaithful fresh answer: the same fresh
106
+ * prompt with the violations appended. Still unfaithful (or unusable), the
107
+ * result is `failed` naming why -- never the unfaithful packet.
108
+ */
109
+ async function runCorrectiveAttempt(candidate, freshPrompt, freshOpts, refused, reviewedSha, deps) {
110
+ const prompt = `${freshPrompt}\n${buildQaAuthorCorrection(refused)}`;
111
+ const opts = { ...freshOpts, label: `${freshOpts.label}:corrective` };
112
+ const corrected = await runAttempt(candidate, QA_AUTHOR_MODE.FRESH, prompt, opts, reviewedSha, deps);
113
+ if (corrected.kind === 'unfaithful') {
114
+ return {
115
+ kind: 'failed',
116
+ reason: `the packet is not faithful to the focus's criteria after one corrective attempt: ${corrected.violations.join('; ')}`,
117
+ authorMode: QA_AUTHOR_MODE.FRESH,
118
+ reviewedSha,
119
+ };
120
+ }
121
+ if (corrected.kind === 'fall_back') {
122
+ return {
123
+ kind: 'failed',
124
+ reason: `${corrected.reason} (the corrective attempt after: ${refused.join('; ')})`,
125
+ authorMode: QA_AUTHOR_MODE.FRESH,
126
+ reviewedSha,
127
+ };
128
+ }
129
+ return corrected;
130
+ }
131
+ /** Author one candidate's packet. Never throws for a model or parse problem (those are results). */
132
+ export async function authorQaPacket(candidate, deps) {
133
+ const reviewedSha = pinPacketCommit(candidate, deps);
134
+ if (!reviewedSha) {
135
+ return { kind: 'failed', reason: 'no reviewed commit to pin the packet to', authorMode: null, reviewedSha: null };
136
+ }
137
+ const label = `qa-author:${candidate.focusId.slice(0, 8)}`;
138
+ const worktreePath = deps.worktreePathFor(candidate.focusId, candidate.branchName);
139
+ const decision = decideQaAuthorMode(candidate, {
140
+ worktreePath,
141
+ worktreeExists: worktreePath !== null && deps.pathExists(worktreePath),
142
+ });
143
+ let fallbackReason;
144
+ if (decision.mode === QA_AUTHOR_MODE.RESUMED) {
145
+ const resumed = await runAttempt(candidate, QA_AUTHOR_MODE.RESUMED, buildQaAuthorPrompt(candidate, QA_AUTHOR_MODE.RESUMED, reviewedSha), {
146
+ label: `${label}:resumed`,
147
+ cwd: decision.cwd,
148
+ extraArgs: buildResumedArgs(decision.sessionId),
149
+ timeoutMs: QA_AUTHOR_TIMEOUT_MS,
150
+ maxTurns: QA_AUTHOR_RESUMED_MAX_TURNS,
151
+ }, reviewedSha, deps);
152
+ if (resumed.kind !== 'fall_back' && resumed.kind !== 'unfaithful')
153
+ return resumed;
154
+ fallbackReason = resumed.reason;
155
+ }
156
+ else {
157
+ fallbackReason = decision.reason;
158
+ }
159
+ log.info(`[qa-author] focus ${candidate.focusId.slice(0, 8)}: fresh pass (${fallbackReason})`);
160
+ const changes = readFocusChanges(deps.repoPath, deps.integrationBranch, reviewedSha, candidate.deliveries.map((d) => d.deliveryId), deps.git);
161
+ const freshPrompt = buildQaAuthorPrompt(candidate, QA_AUTHOR_MODE.FRESH, reviewedSha, changes);
162
+ const freshOpts = {
163
+ label: `${label}:fresh`,
164
+ extraArgs: buildFreshArgs(),
165
+ timeoutMs: QA_AUTHOR_TIMEOUT_MS,
166
+ maxTurns: QA_AUTHOR_FRESH_MAX_TURNS,
167
+ };
168
+ const fresh = await runAttempt(candidate, QA_AUTHOR_MODE.FRESH, freshPrompt, freshOpts, reviewedSha, deps);
169
+ if (fresh.kind === 'unfaithful')
170
+ return runCorrectiveAttempt(candidate, freshPrompt, freshOpts, fresh.violations, reviewedSha, deps);
171
+ if (fresh.kind !== 'fall_back')
172
+ return fresh;
173
+ return { kind: 'failed', reason: `${fresh.reason} (it ran fresh because ${fallbackReason})`, authorMode: QA_AUTHOR_MODE.FRESH, reviewedSha };
174
+ }
175
+ //# sourceMappingURL=qa-author-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qa-author-runner.js","sourceRoot":"","sources":["../../src/qa/qa-author-runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * What a FRESH QA-author pass is shown of the change: the focus's commits and
3
+ * its diff at the reviewed commit, read from the product repository and
4
+ * capped, and the commit a packet is pinned to when the focus recorded none.
5
+ *
6
+ * Which commits are the focus's own depends on whether the completion merge
7
+ * already landed it:
8
+ * - not yet on integration: merge-base(integration, reviewed)..reviewed.
9
+ * - already on integration (the usual case -- a focus enters QA after its
10
+ * completion merge): the merge-base IS the reviewed commit, so the range is
11
+ * empty. The focus's own commits are then the ones whose Telora-Delivery
12
+ * trailer names one of its deliveries, listed with their patches.
13
+ * Every read is bounded; a read that fails says so in `note` rather than
14
+ * passing an empty change off as the whole one.
15
+ */
16
+ import type { QaAuthorFreshContext } from '../prompt-sections/qa-author-section.js';
17
+ /** The git the reads run (production: git/types.ts runGitSync over the product repo). */
18
+ export interface QaGit {
19
+ run: (args: string[], cwd: string) => {
20
+ success: boolean;
21
+ output: string;
22
+ };
23
+ }
24
+ /** The diff a fresh prompt carries at most, in characters. */
25
+ export declare const QA_DIFF_MAX_CHARS = 80000;
26
+ /** Commits listed (and patched) at most. */
27
+ export declare const QA_COMMIT_LOG_MAX = 200;
28
+ /** The full sha `ref` resolves to in `repoPath`, or null. */
29
+ export declare function resolveCommit(repoPath: string, ref: string, git: QaGit): string | null;
30
+ /** Read the focus's change at `reviewedSha`. */
31
+ export declare function readFocusChanges(repoPath: string, integrationBranch: string, reviewedSha: string, deliveryIds: readonly string[], git: QaGit, maxDiffChars?: number): QaAuthorFreshContext;
32
+ //# sourceMappingURL=qa-focus-changes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qa-focus-changes.d.ts","sourceRoot":"","sources":["../../src/qa/qa-focus-changes.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yCAAyC,CAAC;AAEpF,yFAAyF;AACzF,MAAM,WAAW,KAAK;IACpB,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,KAAK;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5E;AAED,8DAA8D;AAC9D,eAAO,MAAM,iBAAiB,QAAS,CAAC;AACxC,4CAA4C;AAC5C,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAIrC,6DAA6D;AAC7D,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,GAAG,MAAM,GAAG,IAAI,CAItF;AAYD,gDAAgD;AAChD,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,iBAAiB,EAAE,MAAM,EACzB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,SAAS,MAAM,EAAE,EAC9B,GAAG,EAAE,KAAK,EACV,YAAY,GAAE,MAA0B,GACvC,oBAAoB,CAmCtB"}
@@ -0,0 +1,59 @@
1
+ // PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
2
+ // Pinned by: packages/daemon/src/qa/qa-author-runner.test.ts,
3
+ // packages/daemon/src/qa/qa-author-unfaithful-answer.test.ts,
4
+ // packages/daemon/src/qa/qa-focus-changes.test.ts
5
+ // PINNED-BY-END
6
+ /** The diff a fresh prompt carries at most, in characters. */
7
+ export const QA_DIFF_MAX_CHARS = 80_000;
8
+ /** Commits listed (and patched) at most. */
9
+ export const QA_COMMIT_LOG_MAX = 200;
10
+ const SHA_RE = /^[0-9a-f]{7,64}$/i;
11
+ /** The full sha `ref` resolves to in `repoPath`, or null. */
12
+ export function resolveCommit(repoPath, ref, git) {
13
+ const r = git.run(['rev-parse', '--verify', '--quiet', `${ref}^{commit}`], repoPath);
14
+ const sha = r.output.trim();
15
+ return r.success && SHA_RE.test(sha) ? sha.toLowerCase() : null;
16
+ }
17
+ function cap(text, maxChars) {
18
+ return text.length > maxChars ? { text: text.slice(0, maxChars), truncated: true } : { text, truncated: false };
19
+ }
20
+ /** A diff read: a failed read keeps what it got (an over-long diff overflows the reader's buffer) and is marked cut. */
21
+ function diffRead(r, maxChars) {
22
+ const capped = cap(r.output, maxChars);
23
+ return { text: capped.text, truncated: capped.truncated || (!r.success && r.output.length > 0) };
24
+ }
25
+ /** Read the focus's change at `reviewedSha`. */
26
+ export function readFocusChanges(repoPath, integrationBranch, reviewedSha, deliveryIds, git, maxDiffChars = QA_DIFF_MAX_CHARS) {
27
+ const reviewed = resolveCommit(repoPath, reviewedSha, git);
28
+ if (!reviewed) {
29
+ return { commitLog: '', diff: '', truncated: false, note: `the reviewed commit ${reviewedSha} is not in the product repository` };
30
+ }
31
+ const base = git.run(['merge-base', integrationBranch, reviewed], repoPath);
32
+ const baseSha = base.success ? base.output.trim().toLowerCase() : '';
33
+ if (baseSha && baseSha !== reviewed) {
34
+ const log = git.run(['log', '--no-merges', `-n${QA_COMMIT_LOG_MAX}`, '--format=%h %s', `${baseSha}..${reviewed}`], repoPath);
35
+ const diff = diffRead(git.run(['diff', baseSha, reviewed], repoPath), maxDiffChars);
36
+ return {
37
+ commitLog: log.success ? log.output.trim() : '',
38
+ diff: diff.text,
39
+ truncated: diff.truncated,
40
+ note: diff.text ? null : 'the diff could not be read',
41
+ };
42
+ }
43
+ if (deliveryIds.length === 0) {
44
+ return { commitLog: '', diff: '', truncated: false, note: 'the focus is already on integration and has no deliveries to find its commits by' };
45
+ }
46
+ const grep = ['-E', `--grep=^Telora-Delivery: (${deliveryIds.join('|')})`];
47
+ const log = git.run(['log', '--no-merges', `-n${QA_COMMIT_LOG_MAX}`, ...grep, '--format=%h %s', reviewed], repoPath);
48
+ const patches = diffRead(git.run(['log', '--no-merges', `-n${QA_COMMIT_LOG_MAX}`, ...grep, '-p', '--format=commit %h %s', reviewed], repoPath), maxDiffChars);
49
+ const commitLog = log.success ? log.output.trim() : '';
50
+ return {
51
+ commitLog,
52
+ diff: patches.text,
53
+ truncated: patches.truncated,
54
+ note: commitLog
55
+ ? 'the focus is already on integration: shown are its own commits (by Telora-Delivery trailer) with their patches'
56
+ : 'the focus is already on integration and no commit carries a Telora-Delivery trailer naming its deliveries',
57
+ };
58
+ }
59
+ //# sourceMappingURL=qa-focus-changes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qa-focus-changes.js","sourceRoot":"","sources":["../../src/qa/qa-focus-changes.ts"],"names":[],"mappings":"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"}
@@ -24,6 +24,7 @@ declare const circuitBreakers: {
24
24
  readonly delivery: CircuitBreaker;
25
25
  readonly drift: CircuitBreaker;
26
26
  readonly tower: CircuitBreaker;
27
+ readonly qa: CircuitBreaker;
27
28
  };
28
29
  type CircuitBreakerConcern = keyof typeof circuitBreakers;
29
30
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/queries/api-client/shared.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,EAAE,cAAc,EAAkC,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAuCrG,QAAA,MAAM,eAAe;;;;;;;;CAqDX,CAAC;AAEX,KAAK,qBAAqB,GAAG,MAAM,OAAO,eAAe,CAAC;AAE1D;;;;;;;;;;GAUG;AACH,MAAM,MAAM,YAAY,GAAG,eAAe,GAAG,eAAe,CAAC;AAE7D,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,qBAAqB,EAAE,YAAY,CAQrE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,EAAE,WAAW,CAAC,MAAM,CAIpD,CAAC;AAqFH;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAEzE;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC,CAErF;AA2BD;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,OAAO,eAAe,CAE9D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAIvD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI,CAE3D;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,YAAY,CAKxC;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAEzD;AAED;;;GAGG;AACH,wBAAsB,OAAO,CAAC,CAAC,EAC7B,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACnC,OAAO,CAAC,CAAC,CAAC,CAEZ;AAED,wFAAwF;AACxF,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC;AAErC;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,EACtC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EACpB,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACnC,OAAO,CAAC,CAAC,CAAC,CAEZ;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,CAAC,EACjC,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACnC,OAAO,CAAC,CAAC,CAAC,CAEZ"}
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/queries/api-client/shared.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,EAAE,cAAc,EAAkC,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAuCrG,QAAA,MAAM,eAAe;;;;;;;;;CA8DX,CAAC;AAEX,KAAK,qBAAqB,GAAG,MAAM,OAAO,eAAe,CAAC;AAE1D;;;;;;;;;;GAUG;AACH,MAAM,MAAM,YAAY,GAAG,eAAe,GAAG,eAAe,CAAC;AAE7D,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,qBAAqB,EAAE,YAAY,CASrE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,EAAE,WAAW,CAAC,MAAM,CAIpD,CAAC;AAwFH;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAEzE;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC,CAErF;AA2BD;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,OAAO,eAAe,CAE9D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAIvD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI,CAE3D;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,YAAY,CAKxC;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAEzD;AAED;;;GAGG;AACH,wBAAsB,OAAO,CAAC,CAAC,EAC7B,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACnC,OAAO,CAAC,CAAC,CAAC,CAEZ;AAED,wFAAwF;AACxF,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC;AAErC;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,EACtC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EACpB,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACnC,OAAO,CAAC,CAAC,CAAC,CAEZ;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,CAAC,EACjC,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACnC,OAAO,CAAC,CAAC,CAAC,CAEZ"}