@renseiai/agentfactory-linear 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +91 -0
  3. package/dist/src/agent-client-project-repo.test.d.ts +2 -0
  4. package/dist/src/agent-client-project-repo.test.d.ts.map +1 -0
  5. package/dist/src/agent-client-project-repo.test.js +153 -0
  6. package/dist/src/agent-client.d.ts +261 -0
  7. package/dist/src/agent-client.d.ts.map +1 -0
  8. package/dist/src/agent-client.js +902 -0
  9. package/dist/src/agent-session.d.ts +303 -0
  10. package/dist/src/agent-session.d.ts.map +1 -0
  11. package/dist/src/agent-session.js +969 -0
  12. package/dist/src/checkbox-utils.d.ts +88 -0
  13. package/dist/src/checkbox-utils.d.ts.map +1 -0
  14. package/dist/src/checkbox-utils.js +120 -0
  15. package/dist/src/circuit-breaker.d.ts +76 -0
  16. package/dist/src/circuit-breaker.d.ts.map +1 -0
  17. package/dist/src/circuit-breaker.js +229 -0
  18. package/dist/src/circuit-breaker.test.d.ts +2 -0
  19. package/dist/src/circuit-breaker.test.d.ts.map +1 -0
  20. package/dist/src/circuit-breaker.test.js +292 -0
  21. package/dist/src/constants.d.ts +87 -0
  22. package/dist/src/constants.d.ts.map +1 -0
  23. package/dist/src/constants.js +101 -0
  24. package/dist/src/defaults/auto-trigger.d.ts +35 -0
  25. package/dist/src/defaults/auto-trigger.d.ts.map +1 -0
  26. package/dist/src/defaults/auto-trigger.js +36 -0
  27. package/dist/src/defaults/index.d.ts +12 -0
  28. package/dist/src/defaults/index.d.ts.map +1 -0
  29. package/dist/src/defaults/index.js +11 -0
  30. package/dist/src/defaults/priority.d.ts +20 -0
  31. package/dist/src/defaults/priority.d.ts.map +1 -0
  32. package/dist/src/defaults/priority.js +37 -0
  33. package/dist/src/defaults/prompts.d.ts +42 -0
  34. package/dist/src/defaults/prompts.d.ts.map +1 -0
  35. package/dist/src/defaults/prompts.js +310 -0
  36. package/dist/src/defaults/prompts.test.d.ts +2 -0
  37. package/dist/src/defaults/prompts.test.d.ts.map +1 -0
  38. package/dist/src/defaults/prompts.test.js +263 -0
  39. package/dist/src/defaults/work-type-detection.d.ts +19 -0
  40. package/dist/src/defaults/work-type-detection.d.ts.map +1 -0
  41. package/dist/src/defaults/work-type-detection.js +93 -0
  42. package/dist/src/errors.d.ts +91 -0
  43. package/dist/src/errors.d.ts.map +1 -0
  44. package/dist/src/errors.js +173 -0
  45. package/dist/src/frontend-adapter.d.ts +168 -0
  46. package/dist/src/frontend-adapter.d.ts.map +1 -0
  47. package/dist/src/frontend-adapter.js +314 -0
  48. package/dist/src/frontend-adapter.test.d.ts +2 -0
  49. package/dist/src/frontend-adapter.test.d.ts.map +1 -0
  50. package/dist/src/frontend-adapter.test.js +545 -0
  51. package/dist/src/index.d.ts +28 -0
  52. package/dist/src/index.d.ts.map +1 -0
  53. package/dist/src/index.js +30 -0
  54. package/dist/src/issue-tracker-proxy.d.ts +140 -0
  55. package/dist/src/issue-tracker-proxy.d.ts.map +1 -0
  56. package/dist/src/issue-tracker-proxy.js +10 -0
  57. package/dist/src/platform-adapter.d.ts +132 -0
  58. package/dist/src/platform-adapter.d.ts.map +1 -0
  59. package/dist/src/platform-adapter.js +260 -0
  60. package/dist/src/platform-adapter.test.d.ts +2 -0
  61. package/dist/src/platform-adapter.test.d.ts.map +1 -0
  62. package/dist/src/platform-adapter.test.js +468 -0
  63. package/dist/src/proxy-client.d.ts +103 -0
  64. package/dist/src/proxy-client.d.ts.map +1 -0
  65. package/dist/src/proxy-client.js +191 -0
  66. package/dist/src/rate-limiter.d.ts +64 -0
  67. package/dist/src/rate-limiter.d.ts.map +1 -0
  68. package/dist/src/rate-limiter.js +163 -0
  69. package/dist/src/rate-limiter.test.d.ts +2 -0
  70. package/dist/src/rate-limiter.test.d.ts.map +1 -0
  71. package/dist/src/rate-limiter.test.js +217 -0
  72. package/dist/src/retry.d.ts +59 -0
  73. package/dist/src/retry.d.ts.map +1 -0
  74. package/dist/src/retry.js +82 -0
  75. package/dist/src/types.d.ts +492 -0
  76. package/dist/src/types.d.ts.map +1 -0
  77. package/dist/src/types.js +143 -0
  78. package/dist/src/utils.d.ts +52 -0
  79. package/dist/src/utils.d.ts.map +1 -0
  80. package/dist/src/utils.js +277 -0
  81. package/dist/src/webhook-types.d.ts +308 -0
  82. package/dist/src/webhook-types.d.ts.map +1 -0
  83. package/dist/src/webhook-types.js +46 -0
  84. package/package.json +70 -0
@@ -0,0 +1,292 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+ import { CircuitBreaker, DEFAULT_CIRCUIT_BREAKER_CONFIG } from './circuit-breaker.js';
3
+ import { CircuitOpenError } from './errors.js';
4
+ describe('CircuitBreaker', () => {
5
+ beforeEach(() => {
6
+ vi.useFakeTimers();
7
+ });
8
+ afterEach(() => {
9
+ vi.useRealTimers();
10
+ });
11
+ // ========================================================================
12
+ // Construction & defaults
13
+ // ========================================================================
14
+ it('starts in closed state', () => {
15
+ const cb = new CircuitBreaker();
16
+ expect(cb.state).toBe('closed');
17
+ });
18
+ it('uses default config when none provided', () => {
19
+ const cb = new CircuitBreaker();
20
+ const status = cb.getStatus();
21
+ expect(status.state).toBe('closed');
22
+ expect(status.consecutiveFailures).toBe(0);
23
+ expect(status.currentResetTimeoutMs).toBe(DEFAULT_CIRCUIT_BREAKER_CONFIG.resetTimeoutMs);
24
+ });
25
+ it('accepts custom config', () => {
26
+ const cb = new CircuitBreaker({ failureThreshold: 5, resetTimeoutMs: 30_000 });
27
+ // Trip it 4 times — should still be closed
28
+ for (let i = 0; i < 4; i++) {
29
+ cb.recordAuthFailure(401);
30
+ }
31
+ expect(cb.state).toBe('closed');
32
+ // 5th failure trips it
33
+ cb.recordAuthFailure(401);
34
+ expect(cb.state).toBe('open');
35
+ });
36
+ // ========================================================================
37
+ // canProceed
38
+ // ========================================================================
39
+ it('allows calls when closed', () => {
40
+ const cb = new CircuitBreaker();
41
+ expect(cb.canProceed()).toBe(true);
42
+ });
43
+ it('blocks calls when open', () => {
44
+ const cb = new CircuitBreaker({ failureThreshold: 1 });
45
+ cb.recordAuthFailure(401);
46
+ expect(cb.state).toBe('open');
47
+ expect(cb.canProceed()).toBe(false);
48
+ });
49
+ it('allows one probe call when half-open', () => {
50
+ const cb = new CircuitBreaker({ failureThreshold: 1, resetTimeoutMs: 1000 });
51
+ cb.recordAuthFailure(401);
52
+ expect(cb.state).toBe('open');
53
+ // Advance past reset timeout
54
+ vi.advanceTimersByTime(1001);
55
+ expect(cb.state).toBe('half-open');
56
+ // First call should be allowed (probe)
57
+ expect(cb.canProceed()).toBe(true);
58
+ // Second call should be blocked (probe already in-flight)
59
+ expect(cb.canProceed()).toBe(false);
60
+ });
61
+ // ========================================================================
62
+ // State transitions: closed → open
63
+ // ========================================================================
64
+ it('trips after consecutive auth failures reach threshold', () => {
65
+ const cb = new CircuitBreaker({ failureThreshold: 2 });
66
+ cb.recordAuthFailure(400);
67
+ expect(cb.state).toBe('closed');
68
+ cb.recordAuthFailure(401);
69
+ expect(cb.state).toBe('open');
70
+ });
71
+ it('always counts failures regardless of status code (isAuthError already vetted)', () => {
72
+ const cb = new CircuitBreaker({ failureThreshold: 2 });
73
+ cb.recordAuthFailure(200); // e.g., GraphQL 200 with auth error in body
74
+ expect(cb.getStatus().consecutiveFailures).toBe(1);
75
+ cb.recordAuthFailure(); // no status code at all
76
+ expect(cb.state).toBe('open');
77
+ });
78
+ it('resets failure count on success', () => {
79
+ const cb = new CircuitBreaker({ failureThreshold: 3 });
80
+ cb.recordAuthFailure(401); // 1
81
+ cb.recordAuthFailure(403); // 2
82
+ cb.recordSuccess(); // reset to 0
83
+ cb.recordAuthFailure(400); // 1 — should NOT trip
84
+ expect(cb.state).toBe('closed');
85
+ });
86
+ // ========================================================================
87
+ // State transitions: open → half-open
88
+ // ========================================================================
89
+ it('transitions from open to half-open after resetTimeoutMs', () => {
90
+ const cb = new CircuitBreaker({ failureThreshold: 1, resetTimeoutMs: 5000 });
91
+ cb.recordAuthFailure(401);
92
+ expect(cb.state).toBe('open');
93
+ // Not enough time
94
+ vi.advanceTimersByTime(4999);
95
+ expect(cb.state).toBe('open');
96
+ // Exactly enough time
97
+ vi.advanceTimersByTime(1);
98
+ expect(cb.state).toBe('half-open');
99
+ });
100
+ // ========================================================================
101
+ // State transitions: half-open → closed (probe success)
102
+ // ========================================================================
103
+ it('closes circuit on successful probe in half-open', () => {
104
+ const cb = new CircuitBreaker({ failureThreshold: 1, resetTimeoutMs: 1000 });
105
+ cb.recordAuthFailure(401);
106
+ vi.advanceTimersByTime(1001);
107
+ expect(cb.state).toBe('half-open');
108
+ // Probe succeeds
109
+ cb.canProceed(); // acquire probe
110
+ cb.recordSuccess();
111
+ expect(cb.state).toBe('closed');
112
+ expect(cb.getStatus().consecutiveFailures).toBe(0);
113
+ });
114
+ // ========================================================================
115
+ // State transitions: half-open → open (probe failure + exponential backoff)
116
+ // ========================================================================
117
+ it('reopens circuit on failed probe with exponential backoff', () => {
118
+ const cb = new CircuitBreaker({
119
+ failureThreshold: 1,
120
+ resetTimeoutMs: 1000,
121
+ maxResetTimeoutMs: 16000,
122
+ backoffMultiplier: 2,
123
+ });
124
+ // First trip
125
+ cb.recordAuthFailure(401);
126
+ expect(cb.state).toBe('open');
127
+ expect(cb.getStatus().currentResetTimeoutMs).toBe(1000);
128
+ // Wait for half-open
129
+ vi.advanceTimersByTime(1001);
130
+ expect(cb.state).toBe('half-open');
131
+ // Probe fails → reopen with backoff
132
+ cb.canProceed();
133
+ cb.recordAuthFailure(401);
134
+ expect(cb.state).toBe('open');
135
+ expect(cb.getStatus().currentResetTimeoutMs).toBe(2000); // 1000 * 2
136
+ // Wait 2000ms for next half-open
137
+ vi.advanceTimersByTime(2001);
138
+ expect(cb.state).toBe('half-open');
139
+ // Probe fails again → further backoff
140
+ cb.canProceed();
141
+ cb.recordAuthFailure(401);
142
+ expect(cb.state).toBe('open');
143
+ expect(cb.getStatus().currentResetTimeoutMs).toBe(4000); // 2000 * 2
144
+ });
145
+ it('caps reset timeout at maxResetTimeoutMs', () => {
146
+ const cb = new CircuitBreaker({
147
+ failureThreshold: 1,
148
+ resetTimeoutMs: 1000,
149
+ maxResetTimeoutMs: 3000,
150
+ backoffMultiplier: 2,
151
+ });
152
+ // Trip → open (1000ms)
153
+ cb.recordAuthFailure(401);
154
+ vi.advanceTimersByTime(1001);
155
+ // Probe fail → 2000ms
156
+ cb.canProceed();
157
+ cb.recordAuthFailure(401);
158
+ vi.advanceTimersByTime(2001);
159
+ // Probe fail → 3000ms (capped at max)
160
+ cb.canProceed();
161
+ cb.recordAuthFailure(401);
162
+ expect(cb.getStatus().currentResetTimeoutMs).toBe(3000);
163
+ vi.advanceTimersByTime(3001);
164
+ // Probe fail → still 3000ms (capped)
165
+ cb.canProceed();
166
+ cb.recordAuthFailure(401);
167
+ expect(cb.getStatus().currentResetTimeoutMs).toBe(3000);
168
+ });
169
+ it('resets backoff on successful recovery', () => {
170
+ const cb = new CircuitBreaker({
171
+ failureThreshold: 1,
172
+ resetTimeoutMs: 1000,
173
+ maxResetTimeoutMs: 16000,
174
+ backoffMultiplier: 2,
175
+ });
176
+ // Trip and backoff to 2000ms
177
+ cb.recordAuthFailure(401);
178
+ vi.advanceTimersByTime(1001);
179
+ cb.canProceed();
180
+ cb.recordAuthFailure(401);
181
+ expect(cb.getStatus().currentResetTimeoutMs).toBe(2000);
182
+ // Recover
183
+ vi.advanceTimersByTime(2001);
184
+ cb.canProceed();
185
+ cb.recordSuccess();
186
+ expect(cb.state).toBe('closed');
187
+ expect(cb.getStatus().currentResetTimeoutMs).toBe(1000); // reset to original
188
+ });
189
+ // ========================================================================
190
+ // reset()
191
+ // ========================================================================
192
+ it('reset() returns to closed from any state', () => {
193
+ const cb = new CircuitBreaker({ failureThreshold: 1 });
194
+ cb.recordAuthFailure(401);
195
+ expect(cb.state).toBe('open');
196
+ cb.reset();
197
+ expect(cb.state).toBe('closed');
198
+ expect(cb.getStatus().consecutiveFailures).toBe(0);
199
+ expect(cb.getStatus().currentResetTimeoutMs).toBe(DEFAULT_CIRCUIT_BREAKER_CONFIG.resetTimeoutMs);
200
+ });
201
+ // ========================================================================
202
+ // isAuthError
203
+ // ========================================================================
204
+ it('detects auth errors by HTTP status code', () => {
205
+ const cb = new CircuitBreaker();
206
+ expect(cb.isAuthError({ status: 400 })).toBe(true);
207
+ expect(cb.isAuthError({ status: 401 })).toBe(true);
208
+ expect(cb.isAuthError({ status: 403 })).toBe(true);
209
+ expect(cb.isAuthError({ statusCode: 401 })).toBe(true);
210
+ expect(cb.isAuthError({ response: { status: 403 } })).toBe(true);
211
+ });
212
+ it('does not flag non-auth status codes', () => {
213
+ const cb = new CircuitBreaker();
214
+ expect(cb.isAuthError({ status: 200 })).toBe(false);
215
+ expect(cb.isAuthError({ status: 404 })).toBe(false);
216
+ expect(cb.isAuthError({ status: 500 })).toBe(false);
217
+ expect(cb.isAuthError({ status: 429 })).toBe(false); // rate limit is handled separately
218
+ });
219
+ it('detects GraphQL RATELIMITED error code', () => {
220
+ const cb = new CircuitBreaker();
221
+ // Direct extensions.code
222
+ expect(cb.isAuthError({ extensions: { code: 'RATELIMITED' } })).toBe(true);
223
+ // Nested errors array
224
+ expect(cb.isAuthError({
225
+ errors: [{ extensions: { code: 'RATELIMITED' } }],
226
+ })).toBe(true);
227
+ // In response body
228
+ expect(cb.isAuthError({
229
+ response: {
230
+ body: {
231
+ errors: [{ extensions: { code: 'RATELIMITED' } }],
232
+ },
233
+ },
234
+ })).toBe(true);
235
+ // In response data (alternative shape)
236
+ expect(cb.isAuthError({
237
+ response: {
238
+ data: {
239
+ errors: [{ extensions: { code: 'RATELIMITED' } }],
240
+ },
241
+ },
242
+ })).toBe(true);
243
+ });
244
+ it('detects auth errors by message pattern', () => {
245
+ const cb = new CircuitBreaker();
246
+ expect(cb.isAuthError({ message: 'Access denied - Only app users can create agent activities' })).toBe(true);
247
+ expect(cb.isAuthError({ message: 'Unauthorized request' })).toBe(true);
248
+ expect(cb.isAuthError({ message: 'Forbidden: insufficient permissions' })).toBe(true);
249
+ });
250
+ it('rejects non-error inputs', () => {
251
+ const cb = new CircuitBreaker();
252
+ expect(cb.isAuthError(null)).toBe(false);
253
+ expect(cb.isAuthError(undefined)).toBe(false);
254
+ expect(cb.isAuthError('string')).toBe(false);
255
+ expect(cb.isAuthError(42)).toBe(false);
256
+ });
257
+ it('detects RATELIMITED in error message', () => {
258
+ const cb = new CircuitBreaker();
259
+ expect(cb.isAuthError({ message: 'GraphQL Error: RATELIMITED' })).toBe(true);
260
+ });
261
+ // ========================================================================
262
+ // createOpenError
263
+ // ========================================================================
264
+ it('creates a CircuitOpenError with remaining time info', () => {
265
+ const cb = new CircuitBreaker({ failureThreshold: 1, resetTimeoutMs: 10_000 });
266
+ cb.recordAuthFailure(401);
267
+ // 3 seconds have passed
268
+ vi.advanceTimersByTime(3000);
269
+ const error = cb.createOpenError();
270
+ expect(error).toBeInstanceOf(CircuitOpenError);
271
+ expect(error.code).toBe('CIRCUIT_OPEN');
272
+ expect(error.retryAfterMs).toBeGreaterThan(0);
273
+ expect(error.retryAfterMs).toBeLessThanOrEqual(7000);
274
+ expect(error.message).toMatch(/Circuit breaker is open/);
275
+ });
276
+ // ========================================================================
277
+ // getStatus diagnostic info
278
+ // ========================================================================
279
+ it('provides diagnostic status info', () => {
280
+ const cb = new CircuitBreaker({ failureThreshold: 1, resetTimeoutMs: 5000 });
281
+ let status = cb.getStatus();
282
+ expect(status.state).toBe('closed');
283
+ expect(status.consecutiveFailures).toBe(0);
284
+ expect(status.msSinceOpened).toBeNull();
285
+ cb.recordAuthFailure(401);
286
+ vi.advanceTimersByTime(2000);
287
+ status = cb.getStatus();
288
+ expect(status.state).toBe('open');
289
+ expect(status.consecutiveFailures).toBe(1);
290
+ expect(status.msSinceOpened).toBeGreaterThanOrEqual(2000);
291
+ });
292
+ });
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Linear API Constants
3
+ *
4
+ * Contains API limits, well-known IDs, and configuration values.
5
+ * All workspace-specific IDs must be provided via environment variables.
6
+ */
7
+ /**
8
+ * Maximum length for comment body.
9
+ * Linear doesn't publicly document this limit, but testing shows ~10k is safe.
10
+ * We use a conservative limit to avoid truncation issues.
11
+ */
12
+ export declare const LINEAR_COMMENT_MAX_LENGTH = 10000;
13
+ /**
14
+ * Marker appended when content is truncated
15
+ */
16
+ export declare const TRUNCATION_MARKER = "\n\n... (content truncated)";
17
+ /**
18
+ * Maximum number of comments for a single completion (safety limit)
19
+ */
20
+ export declare const MAX_COMPLETION_COMMENTS = 10;
21
+ /**
22
+ * Characters reserved for part markers and overhead
23
+ */
24
+ export declare const COMMENT_OVERHEAD = 100;
25
+ /**
26
+ * Continuation marker for multi-part comments
27
+ */
28
+ export declare const CONTINUATION_MARKER = "\n\n*...continued in next comment*";
29
+ /**
30
+ * Default team UUID
31
+ * Must be set via LINEAR_TEAM_ID env var
32
+ *
33
+ * Uses a getter to read lazily from process.env, avoiding ESM import
34
+ * hoisting issues where the value is captured before dotenv runs.
35
+ */
36
+ export declare function getDefaultTeamId(): string;
37
+ /**
38
+ * Default team name
39
+ * Can be set via LINEAR_TEAM_NAME env var (auto-set by orchestrator from issue context)
40
+ */
41
+ export declare function getDefaultTeamName(): string;
42
+ /**
43
+ * Project IDs — must be set via env vars:
44
+ * - LINEAR_PROJECT_AGENT
45
+ * - LINEAR_PROJECT_SOCIAL
46
+ * - LINEAR_PROJECT_TEST
47
+ */
48
+ export declare const LINEAR_PROJECTS: {
49
+ readonly AGENT: string;
50
+ readonly SOCIAL: string;
51
+ /** Test project for E2E testing of orchestrator */
52
+ readonly TEST: string;
53
+ };
54
+ /**
55
+ * Label IDs for issue classification
56
+ * Must be set via env vars:
57
+ * - LINEAR_LABEL_BUG
58
+ * - LINEAR_LABEL_FEATURE
59
+ * - LINEAR_LABEL_CHORE
60
+ */
61
+ export declare const LINEAR_LABELS: {
62
+ readonly BUG: string;
63
+ readonly FEATURE: string;
64
+ readonly CHORE: string;
65
+ readonly NEEDS_HUMAN: string;
66
+ };
67
+ export declare const TEST_LABEL_NAMES: {
68
+ /** Mark issues as test fixtures (not for manual processing) */
69
+ readonly TEST_FIXTURE: "test-fixture";
70
+ /** Identify issues created by E2E tests */
71
+ readonly E2E_TEST: "e2e-test";
72
+ };
73
+ /**
74
+ * Categories of environment issues that agents can report
75
+ */
76
+ export declare const ENVIRONMENT_ISSUE_TYPES: {
77
+ readonly PERMISSION: "permission";
78
+ readonly NETWORK: "network";
79
+ readonly SANDBOX: "sandbox";
80
+ readonly LINEAR_CLI: "linear-cli";
81
+ readonly DEPENDENCY: "dependency";
82
+ readonly TIMEOUT: "timeout";
83
+ readonly TOOL: "tool";
84
+ readonly HUMAN_BLOCKER: "human-blocker";
85
+ };
86
+ export type EnvironmentIssueType = (typeof ENVIRONMENT_ISSUE_TYPES)[keyof typeof ENVIRONMENT_ISSUE_TYPES];
87
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,QAAQ,CAAA;AAE9C;;GAEG;AACH,eAAO,MAAM,iBAAiB,gCAAgC,CAAA;AAE9D;;GAEG;AACH,eAAO,MAAM,uBAAuB,KAAK,CAAA;AAEzC;;GAEG;AACH,eAAO,MAAM,gBAAgB,MAAM,CAAA;AAEnC;;GAEG;AACH,eAAO,MAAM,mBAAmB,uCAAuC,CAAA;AAQvE;;;;;;GAMG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe;;;IAG1B,mDAAmD;;CAE3C,CAAA;AAEV;;;;;;GAMG;AACH,eAAO,MAAM,aAAa;;;;;CAKhB,CAAA;AAGV,eAAO,MAAM,gBAAgB;IAC3B,+DAA+D;;IAE/D,2CAA2C;;CAEnC,CAAA;AAMV;;GAEG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;CAS1B,CAAA;AAEV,MAAM,MAAM,oBAAoB,GAC9B,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,OAAO,uBAAuB,CAAC,CAAA"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Linear API Constants
3
+ *
4
+ * Contains API limits, well-known IDs, and configuration values.
5
+ * All workspace-specific IDs must be provided via environment variables.
6
+ */
7
+ // ============================================================================
8
+ // LINEAR API LIMITS
9
+ // ============================================================================
10
+ /**
11
+ * Maximum length for comment body.
12
+ * Linear doesn't publicly document this limit, but testing shows ~10k is safe.
13
+ * We use a conservative limit to avoid truncation issues.
14
+ */
15
+ export const LINEAR_COMMENT_MAX_LENGTH = 10000;
16
+ /**
17
+ * Marker appended when content is truncated
18
+ */
19
+ export const TRUNCATION_MARKER = '\n\n... (content truncated)';
20
+ /**
21
+ * Maximum number of comments for a single completion (safety limit)
22
+ */
23
+ export const MAX_COMPLETION_COMMENTS = 10;
24
+ /**
25
+ * Characters reserved for part markers and overhead
26
+ */
27
+ export const COMMENT_OVERHEAD = 100;
28
+ /**
29
+ * Continuation marker for multi-part comments
30
+ */
31
+ export const CONTINUATION_MARKER = '\n\n*...continued in next comment*';
32
+ // ============================================================================
33
+ // WELL-KNOWN LINEAR IDs
34
+ // All workspace-specific IDs are loaded from environment variables.
35
+ // No hardcoded fallback UUIDs — configure via env vars.
36
+ // ============================================================================
37
+ /**
38
+ * Default team UUID
39
+ * Must be set via LINEAR_TEAM_ID env var
40
+ *
41
+ * Uses a getter to read lazily from process.env, avoiding ESM import
42
+ * hoisting issues where the value is captured before dotenv runs.
43
+ */
44
+ export function getDefaultTeamId() {
45
+ return process.env.LINEAR_TEAM_ID ?? '';
46
+ }
47
+ /**
48
+ * Default team name
49
+ * Can be set via LINEAR_TEAM_NAME env var (auto-set by orchestrator from issue context)
50
+ */
51
+ export function getDefaultTeamName() {
52
+ return process.env.LINEAR_TEAM_NAME ?? '';
53
+ }
54
+ /**
55
+ * Project IDs — must be set via env vars:
56
+ * - LINEAR_PROJECT_AGENT
57
+ * - LINEAR_PROJECT_SOCIAL
58
+ * - LINEAR_PROJECT_TEST
59
+ */
60
+ export const LINEAR_PROJECTS = {
61
+ get AGENT() { return process.env.LINEAR_PROJECT_AGENT ?? ''; },
62
+ get SOCIAL() { return process.env.LINEAR_PROJECT_SOCIAL ?? ''; },
63
+ /** Test project for E2E testing of orchestrator */
64
+ get TEST() { return process.env.LINEAR_PROJECT_TEST ?? ''; },
65
+ };
66
+ /**
67
+ * Label IDs for issue classification
68
+ * Must be set via env vars:
69
+ * - LINEAR_LABEL_BUG
70
+ * - LINEAR_LABEL_FEATURE
71
+ * - LINEAR_LABEL_CHORE
72
+ */
73
+ export const LINEAR_LABELS = {
74
+ get BUG() { return process.env.LINEAR_LABEL_BUG ?? ''; },
75
+ get FEATURE() { return process.env.LINEAR_LABEL_FEATURE ?? ''; },
76
+ get CHORE() { return process.env.LINEAR_LABEL_CHORE ?? ''; },
77
+ get NEEDS_HUMAN() { return process.env.LINEAR_LABEL_NEEDS_HUMAN ?? ''; },
78
+ };
79
+ // Test-related labels (created dynamically, not hardcoded IDs)
80
+ export const TEST_LABEL_NAMES = {
81
+ /** Mark issues as test fixtures (not for manual processing) */
82
+ TEST_FIXTURE: 'test-fixture',
83
+ /** Identify issues created by E2E tests */
84
+ E2E_TEST: 'e2e-test',
85
+ };
86
+ // ============================================================================
87
+ // ENVIRONMENT ISSUE REPORTING
88
+ // ============================================================================
89
+ /**
90
+ * Categories of environment issues that agents can report
91
+ */
92
+ export const ENVIRONMENT_ISSUE_TYPES = {
93
+ PERMISSION: 'permission',
94
+ NETWORK: 'network',
95
+ SANDBOX: 'sandbox',
96
+ LINEAR_CLI: 'linear-cli',
97
+ DEPENDENCY: 'dependency',
98
+ TIMEOUT: 'timeout',
99
+ TOOL: 'tool',
100
+ HUMAN_BLOCKER: 'human-blocker',
101
+ };
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Default auto-trigger configuration parser.
3
+ *
4
+ * Parses environment variables to determine which automated
5
+ * workflows (QA, acceptance) should be triggered on status transitions.
6
+ */
7
+ /**
8
+ * Auto-trigger configuration shape.
9
+ * Matches the AutoTriggerConfig interface from @renseiai/agentfactory-nextjs.
10
+ */
11
+ export interface DefaultAutoTriggerConfig {
12
+ enableAutoQA: boolean;
13
+ enableAutoAcceptance: boolean;
14
+ autoQARequireAgentWorked: boolean;
15
+ autoAcceptanceRequireAgentWorked: boolean;
16
+ autoQAProjects: string[];
17
+ autoAcceptanceProjects: string[];
18
+ autoQAExcludeLabels: string[];
19
+ autoAcceptanceExcludeLabels: string[];
20
+ }
21
+ /**
22
+ * Parse auto-trigger configuration from environment variables.
23
+ *
24
+ * Environment variables:
25
+ * ENABLE_AUTO_QA - Enable automatic QA on Finished transition
26
+ * ENABLE_AUTO_ACCEPTANCE - Enable automatic acceptance on Delivered transition
27
+ * AUTO_QA_REQUIRE_AGENT_WORKED - Only auto-QA agent-worked issues (default: true)
28
+ * AUTO_ACCEPTANCE_REQUIRE_AGENT_WORKED - Only auto-accept agent-worked issues (default: true)
29
+ * AUTO_QA_PROJECTS - Comma-separated project names to auto-QA
30
+ * AUTO_ACCEPTANCE_PROJECTS - Comma-separated project names to auto-accept
31
+ * AUTO_QA_EXCLUDE_LABELS - Labels that exclude issues from auto-QA
32
+ * AUTO_ACCEPTANCE_EXCLUDE_LABELS - Labels that exclude issues from auto-acceptance
33
+ */
34
+ export declare function defaultParseAutoTriggerConfig(): DefaultAutoTriggerConfig;
35
+ //# sourceMappingURL=auto-trigger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-trigger.d.ts","sourceRoot":"","sources":["../../../src/defaults/auto-trigger.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;GAGG;AACH,MAAM,WAAW,wBAAwB;IACvC,YAAY,EAAE,OAAO,CAAA;IACrB,oBAAoB,EAAE,OAAO,CAAA;IAC7B,wBAAwB,EAAE,OAAO,CAAA;IACjC,gCAAgC,EAAE,OAAO,CAAA;IACzC,cAAc,EAAE,MAAM,EAAE,CAAA;IACxB,sBAAsB,EAAE,MAAM,EAAE,CAAA;IAChC,mBAAmB,EAAE,MAAM,EAAE,CAAA;IAC7B,2BAA2B,EAAE,MAAM,EAAE,CAAA;CACtC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,6BAA6B,IAAI,wBAAwB,CAWxE"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Default auto-trigger configuration parser.
3
+ *
4
+ * Parses environment variables to determine which automated
5
+ * workflows (QA, acceptance) should be triggered on status transitions.
6
+ */
7
+ /**
8
+ * Parse auto-trigger configuration from environment variables.
9
+ *
10
+ * Environment variables:
11
+ * ENABLE_AUTO_QA - Enable automatic QA on Finished transition
12
+ * ENABLE_AUTO_ACCEPTANCE - Enable automatic acceptance on Delivered transition
13
+ * AUTO_QA_REQUIRE_AGENT_WORKED - Only auto-QA agent-worked issues (default: true)
14
+ * AUTO_ACCEPTANCE_REQUIRE_AGENT_WORKED - Only auto-accept agent-worked issues (default: true)
15
+ * AUTO_QA_PROJECTS - Comma-separated project names to auto-QA
16
+ * AUTO_ACCEPTANCE_PROJECTS - Comma-separated project names to auto-accept
17
+ * AUTO_QA_EXCLUDE_LABELS - Labels that exclude issues from auto-QA
18
+ * AUTO_ACCEPTANCE_EXCLUDE_LABELS - Labels that exclude issues from auto-acceptance
19
+ */
20
+ export function defaultParseAutoTriggerConfig() {
21
+ return {
22
+ enableAutoQA: process.env.ENABLE_AUTO_QA === 'true',
23
+ enableAutoAcceptance: process.env.ENABLE_AUTO_ACCEPTANCE === 'true',
24
+ autoQARequireAgentWorked: process.env.AUTO_QA_REQUIRE_AGENT_WORKED !== 'false',
25
+ autoAcceptanceRequireAgentWorked: process.env.AUTO_ACCEPTANCE_REQUIRE_AGENT_WORKED !== 'false',
26
+ autoQAProjects: parseCommaSeparated(process.env.AUTO_QA_PROJECTS),
27
+ autoAcceptanceProjects: parseCommaSeparated(process.env.AUTO_ACCEPTANCE_PROJECTS),
28
+ autoQAExcludeLabels: parseCommaSeparated(process.env.AUTO_QA_EXCLUDE_LABELS),
29
+ autoAcceptanceExcludeLabels: parseCommaSeparated(process.env.AUTO_ACCEPTANCE_EXCLUDE_LABELS),
30
+ };
31
+ }
32
+ function parseCommaSeparated(value) {
33
+ if (!value)
34
+ return [];
35
+ return value.split(',').map(s => s.trim()).filter(Boolean);
36
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Default implementations for prompt generation, work type detection,
3
+ * priority assignment, and auto-trigger configuration.
4
+ *
5
+ * New users start with these defaults and customize as needed.
6
+ * Rensei overrides these with its own prompts.ts.
7
+ */
8
+ export { defaultGeneratePrompt, defaultBuildParentQAContext, defaultBuildParentAcceptanceContext, buildFailureContextBlock, WORK_RESULT_MARKER_INSTRUCTION, PR_SELECTION_GUIDANCE, type WorkflowContext, } from './prompts.js';
9
+ export { defaultDetectWorkTypeFromPrompt } from './work-type-detection.js';
10
+ export { defaultGetPriority } from './priority.js';
11
+ export { defaultParseAutoTriggerConfig, type DefaultAutoTriggerConfig, } from './auto-trigger.js';
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/defaults/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,qBAAqB,EACrB,2BAA2B,EAC3B,mCAAmC,EACnC,wBAAwB,EACxB,8BAA8B,EAC9B,qBAAqB,EACrB,KAAK,eAAe,GACrB,MAAM,cAAc,CAAA;AAErB,OAAO,EAAE,+BAA+B,EAAE,MAAM,0BAA0B,CAAA;AAE1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAElD,OAAO,EACL,6BAA6B,EAC7B,KAAK,wBAAwB,GAC9B,MAAM,mBAAmB,CAAA"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Default implementations for prompt generation, work type detection,
3
+ * priority assignment, and auto-trigger configuration.
4
+ *
5
+ * New users start with these defaults and customize as needed.
6
+ * Rensei overrides these with its own prompts.ts.
7
+ */
8
+ export { defaultGeneratePrompt, defaultBuildParentQAContext, defaultBuildParentAcceptanceContext, buildFailureContextBlock, WORK_RESULT_MARKER_INSTRUCTION, PR_SELECTION_GUIDANCE, } from './prompts.js';
9
+ export { defaultDetectWorkTypeFromPrompt } from './work-type-detection.js';
10
+ export { defaultGetPriority } from './priority.js';
11
+ export { defaultParseAutoTriggerConfig, } from './auto-trigger.js';
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Default priority values for each work type.
3
+ *
4
+ * Lower values = higher priority in the work queue.
5
+ */
6
+ import type { AgentWorkType } from '../types.js';
7
+ /**
8
+ * Get the default priority for a work type.
9
+ *
10
+ * Priority scale:
11
+ * 1 = Urgent (reserved for future use)
12
+ * 2 = High (QA, acceptance, coordination, inflight, refinement)
13
+ * 3 = Normal (development, backlog-creation)
14
+ * 4 = Low (research)
15
+ *
16
+ * @param workType - The work type to get priority for
17
+ * @returns Priority value (lower = higher priority)
18
+ */
19
+ export declare function defaultGetPriority(workType: AgentWorkType): number;
20
+ //# sourceMappingURL=priority.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"priority.d.ts","sourceRoot":"","sources":["../../../src/defaults/priority.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEhD;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,CAmBlE"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Default priority values for each work type.
3
+ *
4
+ * Lower values = higher priority in the work queue.
5
+ */
6
+ /**
7
+ * Get the default priority for a work type.
8
+ *
9
+ * Priority scale:
10
+ * 1 = Urgent (reserved for future use)
11
+ * 2 = High (QA, acceptance, coordination, inflight, refinement)
12
+ * 3 = Normal (development, backlog-creation)
13
+ * 4 = Low (research)
14
+ *
15
+ * @param workType - The work type to get priority for
16
+ * @returns Priority value (lower = higher priority)
17
+ */
18
+ export function defaultGetPriority(workType) {
19
+ switch (workType) {
20
+ case 'qa':
21
+ case 'acceptance':
22
+ case 'refinement':
23
+ case 'refinement-coordination':
24
+ case 'inflight':
25
+ case 'coordination':
26
+ case 'qa-coordination':
27
+ case 'acceptance-coordination':
28
+ return 2;
29
+ case 'backlog-creation':
30
+ case 'development':
31
+ return 3;
32
+ case 'research':
33
+ return 4;
34
+ default:
35
+ return 3;
36
+ }
37
+ }