@supaku/agentfactory-server 0.7.27 → 0.7.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/agent-tracking.d.ts +23 -0
- package/dist/src/agent-tracking.d.ts.map +1 -1
- package/dist/src/agent-tracking.js +47 -2
- package/dist/src/orphan-cleanup.js +2 -2
- package/dist/src/session-storage.d.ts +7 -5
- package/dist/src/session-storage.d.ts.map +1 -1
- package/dist/src/session-storage.js +8 -8
- package/dist/src/work-queue.d.ts +1 -1
- package/dist/src/work-queue.d.ts.map +1 -1
- package/dist/src/workflow-state-integration.test.js +62 -1
- package/package.json +3 -3
|
@@ -175,4 +175,27 @@ export declare function clearAcceptanceQueued(issueId: string): Promise<void>;
|
|
|
175
175
|
* Called after successful acceptance processing to remove all Redis state
|
|
176
176
|
*/
|
|
177
177
|
export declare function cleanupAcceptedIssue(issueId: string): Promise<void>;
|
|
178
|
+
/**
|
|
179
|
+
* Maximum total sessions (across all phases) allowed per issue.
|
|
180
|
+
* Once reached, no more automated sessions will be created.
|
|
181
|
+
*/
|
|
182
|
+
export declare const MAX_TOTAL_SESSIONS = 8;
|
|
183
|
+
/**
|
|
184
|
+
* Get the total number of sessions across all workflow phases for an issue.
|
|
185
|
+
* Returns 0 if no workflow state exists.
|
|
186
|
+
*/
|
|
187
|
+
export declare function getTotalSessionCount(issueId: string): Promise<number>;
|
|
188
|
+
/**
|
|
189
|
+
* Mark an issue as having just completed acceptance.
|
|
190
|
+
* Prevents re-triggering acceptance within the cooldown window.
|
|
191
|
+
*/
|
|
192
|
+
export declare function markAcceptanceCompleted(issueId: string): Promise<void>;
|
|
193
|
+
/**
|
|
194
|
+
* Check if acceptance was recently completed for an issue (within cooldown period)
|
|
195
|
+
*/
|
|
196
|
+
export declare function didAcceptanceJustComplete(issueId: string): Promise<boolean>;
|
|
197
|
+
/**
|
|
198
|
+
* Clear acceptance completed marker
|
|
199
|
+
*/
|
|
200
|
+
export declare function clearAcceptanceCompleted(issueId: string): Promise<void>;
|
|
178
201
|
//# sourceMappingURL=agent-tracking.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-tracking.d.ts","sourceRoot":"","sources":["../../src/agent-tracking.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"agent-tracking.d.ts","sourceRoot":"","sources":["../../src/agent-tracking.ts"],"names":[],"mappings":"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"}
|
|
@@ -17,13 +17,15 @@ const QA_ATTEMPT_PREFIX = 'qa:attempt:';
|
|
|
17
17
|
const QA_FAILED_PREFIX = 'qa:failed:';
|
|
18
18
|
const DEV_QUEUED_PREFIX = 'agent:dev-queued:';
|
|
19
19
|
const ACCEPTANCE_QUEUED_PREFIX = 'agent:acceptance-queued:';
|
|
20
|
+
const ACCEPTANCE_COMPLETED_PREFIX = 'agent:acceptance-completed:';
|
|
20
21
|
const WORKFLOW_STATE_PREFIX = 'workflow:state:';
|
|
21
22
|
// TTLs in seconds
|
|
22
23
|
const AGENT_WORKED_TTL = 7 * 24 * 60 * 60; // 7 days
|
|
23
24
|
const QA_ATTEMPT_TTL = 24 * 60 * 60; // 24 hours
|
|
24
25
|
const QA_FAILED_TTL = 60 * 60; // 1 hour
|
|
25
|
-
const DEV_QUEUED_TTL =
|
|
26
|
-
const ACCEPTANCE_QUEUED_TTL =
|
|
26
|
+
const DEV_QUEUED_TTL = 300; // 5 minutes
|
|
27
|
+
const ACCEPTANCE_QUEUED_TTL = 300; // 5 minutes
|
|
28
|
+
const ACCEPTANCE_COMPLETED_TTL = 30 * 60; // 30 minutes
|
|
27
29
|
const WORKFLOW_STATE_TTL = 30 * 24 * 60 * 60; // 30 days
|
|
28
30
|
/**
|
|
29
31
|
* Compute escalation strategy deterministically from cycle count.
|
|
@@ -297,8 +299,51 @@ export async function cleanupAcceptedIssue(issueId) {
|
|
|
297
299
|
`${QA_FAILED_PREFIX}${issueId}`,
|
|
298
300
|
`${DEV_QUEUED_PREFIX}${issueId}`,
|
|
299
301
|
`${ACCEPTANCE_QUEUED_PREFIX}${issueId}`,
|
|
302
|
+
`${ACCEPTANCE_COMPLETED_PREFIX}${issueId}`,
|
|
300
303
|
`${WORKFLOW_STATE_PREFIX}${issueId}`,
|
|
301
304
|
];
|
|
302
305
|
await Promise.all(keysToDelete.map((key) => redisDel(key)));
|
|
303
306
|
log.info('Cleaned up all tracking data for accepted issue', { issueId });
|
|
304
307
|
}
|
|
308
|
+
/**
|
|
309
|
+
* Maximum total sessions (across all phases) allowed per issue.
|
|
310
|
+
* Once reached, no more automated sessions will be created.
|
|
311
|
+
*/
|
|
312
|
+
export const MAX_TOTAL_SESSIONS = 8;
|
|
313
|
+
/**
|
|
314
|
+
* Get the total number of sessions across all workflow phases for an issue.
|
|
315
|
+
* Returns 0 if no workflow state exists.
|
|
316
|
+
*/
|
|
317
|
+
export async function getTotalSessionCount(issueId) {
|
|
318
|
+
const state = await getWorkflowState(issueId);
|
|
319
|
+
if (!state)
|
|
320
|
+
return 0;
|
|
321
|
+
return (state.phases.development.length +
|
|
322
|
+
state.phases.qa.length +
|
|
323
|
+
state.phases.refinement.length +
|
|
324
|
+
state.phases.acceptance.length);
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Mark an issue as having just completed acceptance.
|
|
328
|
+
* Prevents re-triggering acceptance within the cooldown window.
|
|
329
|
+
*/
|
|
330
|
+
export async function markAcceptanceCompleted(issueId) {
|
|
331
|
+
const key = `${ACCEPTANCE_COMPLETED_PREFIX}${issueId}`;
|
|
332
|
+
await redisSet(key, { completedAt: Date.now() }, ACCEPTANCE_COMPLETED_TTL);
|
|
333
|
+
log.info('Marked acceptance completed', { issueId });
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Check if acceptance was recently completed for an issue (within cooldown period)
|
|
337
|
+
*/
|
|
338
|
+
export async function didAcceptanceJustComplete(issueId) {
|
|
339
|
+
const key = `${ACCEPTANCE_COMPLETED_PREFIX}${issueId}`;
|
|
340
|
+
return redisExists(key);
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Clear acceptance completed marker
|
|
344
|
+
*/
|
|
345
|
+
export async function clearAcceptanceCompleted(issueId) {
|
|
346
|
+
const key = `${ACCEPTANCE_COMPLETED_PREFIX}${issueId}`;
|
|
347
|
+
await redisDel(key);
|
|
348
|
+
log.debug('Cleared acceptance completed marker', { issueId });
|
|
349
|
+
}
|
|
@@ -142,7 +142,7 @@ export async function cleanupOrphanedSessions(callbacks) {
|
|
|
142
142
|
await resetSessionForRequeue(session.linearSessionId);
|
|
143
143
|
// Re-queue the work with higher priority
|
|
144
144
|
// IMPORTANT: Preserve workType to prevent incorrect status transitions
|
|
145
|
-
// NOTE: Do NOT preserve
|
|
145
|
+
// NOTE: Do NOT preserve providerSessionId - the old session may be corrupted
|
|
146
146
|
// from the crash that caused the orphan. Starting fresh is safer.
|
|
147
147
|
const work = {
|
|
148
148
|
sessionId: session.linearSessionId,
|
|
@@ -151,7 +151,7 @@ export async function cleanupOrphanedSessions(callbacks) {
|
|
|
151
151
|
priority: Math.max(1, (session.priority || 3) - 1), // Boost priority
|
|
152
152
|
queuedAt: Date.now(),
|
|
153
153
|
prompt: session.promptContext,
|
|
154
|
-
//
|
|
154
|
+
// providerSessionId intentionally omitted - don't resume crashed sessions
|
|
155
155
|
workType: session.workType,
|
|
156
156
|
};
|
|
157
157
|
const dispatchResult = await dispatchWork(work);
|
|
@@ -20,8 +20,8 @@ export interface AgentSessionState {
|
|
|
20
20
|
issueId: string;
|
|
21
21
|
/** Issue identifier (e.g., SUP-123) */
|
|
22
22
|
issueIdentifier?: string;
|
|
23
|
-
/**
|
|
24
|
-
|
|
23
|
+
/** Provider CLI session ID for resuming with --resume */
|
|
24
|
+
providerSessionId: string | null;
|
|
25
25
|
/** Git worktree path */
|
|
26
26
|
worktreePath: string;
|
|
27
27
|
/** Current agent status */
|
|
@@ -48,6 +48,8 @@ export interface AgentSessionState {
|
|
|
48
48
|
agentId?: string;
|
|
49
49
|
/** Linear project name (for routing and dashboard visibility) */
|
|
50
50
|
projectName?: string;
|
|
51
|
+
/** Agent provider name (claude, codex, amp) — set by worker on claim */
|
|
52
|
+
provider?: string;
|
|
51
53
|
/** Total cost in USD for this session */
|
|
52
54
|
totalCostUsd?: number;
|
|
53
55
|
/** Total input tokens consumed */
|
|
@@ -70,13 +72,13 @@ export declare function storeSessionState(linearSessionId: string, state: Omit<A
|
|
|
70
72
|
*/
|
|
71
73
|
export declare function getSessionState(linearSessionId: string): Promise<AgentSessionState | null>;
|
|
72
74
|
/**
|
|
73
|
-
* Update the
|
|
75
|
+
* Update the provider session ID for a session
|
|
74
76
|
* Called when the Claude init event is received with the session ID
|
|
75
77
|
*
|
|
76
78
|
* @param linearSessionId - The Linear session ID
|
|
77
|
-
* @param
|
|
79
|
+
* @param providerSessionId - The Provider CLI session ID
|
|
78
80
|
*/
|
|
79
|
-
export declare function
|
|
81
|
+
export declare function updateProviderSessionId(linearSessionId: string, providerSessionId: string): Promise<boolean>;
|
|
80
82
|
/**
|
|
81
83
|
* Update session status
|
|
82
84
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-storage.d.ts","sourceRoot":"","sources":["../../src/session-storage.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAS/C;;;;;;;;;GASG;AACH,MAAM,MAAM,kBAAkB,GAC1B,SAAS,GACT,SAAS,GACT,SAAS,GACT,YAAY,GACZ,WAAW,GACX,QAAQ,GACR,SAAS,CAAA;AAEb;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,uCAAuC;IACvC,eAAe,EAAE,MAAM,CAAA;IACvB,sBAAsB;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,uCAAuC;IACvC,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,
|
|
1
|
+
{"version":3,"file":"session-storage.d.ts","sourceRoot":"","sources":["../../src/session-storage.ts"],"names":[],"mappings":"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"}
|
|
@@ -52,7 +52,7 @@ export async function storeSessionState(linearSessionId, state) {
|
|
|
52
52
|
linearSessionId,
|
|
53
53
|
issueId: state.issueId,
|
|
54
54
|
status: state.status,
|
|
55
|
-
|
|
55
|
+
hasProviderSessionId: !!state.providerSessionId,
|
|
56
56
|
});
|
|
57
57
|
return sessionState;
|
|
58
58
|
}
|
|
@@ -79,31 +79,31 @@ export async function getSessionState(linearSessionId) {
|
|
|
79
79
|
return state;
|
|
80
80
|
}
|
|
81
81
|
/**
|
|
82
|
-
* Update the
|
|
82
|
+
* Update the provider session ID for a session
|
|
83
83
|
* Called when the Claude init event is received with the session ID
|
|
84
84
|
*
|
|
85
85
|
* @param linearSessionId - The Linear session ID
|
|
86
|
-
* @param
|
|
86
|
+
* @param providerSessionId - The Provider CLI session ID
|
|
87
87
|
*/
|
|
88
|
-
export async function
|
|
88
|
+
export async function updateProviderSessionId(linearSessionId, providerSessionId) {
|
|
89
89
|
if (!isRedisConfigured()) {
|
|
90
|
-
log.warn('Redis not configured, cannot update
|
|
90
|
+
log.warn('Redis not configured, cannot update provider session ID');
|
|
91
91
|
return false;
|
|
92
92
|
}
|
|
93
93
|
const existing = await getSessionState(linearSessionId);
|
|
94
94
|
if (!existing) {
|
|
95
|
-
log.warn('Session not found for
|
|
95
|
+
log.warn('Session not found for provider session ID update', { linearSessionId });
|
|
96
96
|
return false;
|
|
97
97
|
}
|
|
98
98
|
const key = buildSessionKey(linearSessionId);
|
|
99
99
|
const now = Math.floor(Date.now() / 1000);
|
|
100
100
|
const updated = {
|
|
101
101
|
...existing,
|
|
102
|
-
|
|
102
|
+
providerSessionId,
|
|
103
103
|
updatedAt: now,
|
|
104
104
|
};
|
|
105
105
|
await redisSet(key, updated, SESSION_TTL_SECONDS);
|
|
106
|
-
log.info('Updated
|
|
106
|
+
log.info('Updated provider session ID', { linearSessionId, providerSessionId });
|
|
107
107
|
return true;
|
|
108
108
|
}
|
|
109
109
|
/**
|
package/dist/src/work-queue.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"work-queue.d.ts","sourceRoot":"","sources":["../../src/work-queue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAqBH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAoB/C;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,aAAa,CAAA;AAEpC;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,eAAe,EAAE,MAAM,CAAA;IACvB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,
|
|
1
|
+
{"version":3,"file":"work-queue.d.ts","sourceRoot":"","sources":["../../src/work-queue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAqBH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAoB/C;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,aAAa,CAAA;AAEpC;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,eAAe,EAAE,MAAM,CAAA;IACvB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,EAAE,aAAa,CAAA;IACxB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAeD;;;;;GAKG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CA4BlE;AAED;;;;;;GAMG;AACH,wBAAsB,QAAQ,CAAC,KAAK,GAAE,MAAW,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAuCxE;AAED;;GAEG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,CAWtD;AAED;;;;;;;;;GASG;AACH,wBAAsB,SAAS,CAC7B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAgE5B;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAatE;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAY7E;AAED;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAY1E;AAED;;;;;;GAMG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,UAAU,EAChB,aAAa,GAAE,MAAU,GACxB,OAAO,CAAC,OAAO,CAAC,CAwBlB;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC,CAgC/D;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAczE;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC;IACtD,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;CACf,CAAC,CA8CD"}
|
|
@@ -23,7 +23,7 @@ vi.mock('./redis.js', () => ({
|
|
|
23
23
|
}),
|
|
24
24
|
redisExists: vi.fn(async (key) => store.has(key)),
|
|
25
25
|
}));
|
|
26
|
-
import { computeStrategy, getWorkflowState, updateWorkflowState, recordPhaseAttempt, incrementCycleCount, appendFailureSummary, clearWorkflowState, extractFailureReason, } from './agent-tracking.js';
|
|
26
|
+
import { computeStrategy, getWorkflowState, updateWorkflowState, recordPhaseAttempt, incrementCycleCount, appendFailureSummary, clearWorkflowState, extractFailureReason, getTotalSessionCount, MAX_TOTAL_SESSIONS, markAcceptanceCompleted, didAcceptanceJustComplete, clearAcceptanceCompleted, } from './agent-tracking.js';
|
|
27
27
|
beforeEach(() => {
|
|
28
28
|
store.clear();
|
|
29
29
|
});
|
|
@@ -279,3 +279,64 @@ describe('escalate-human blocker creation data', () => {
|
|
|
279
279
|
}
|
|
280
280
|
});
|
|
281
281
|
});
|
|
282
|
+
describe('getTotalSessionCount', () => {
|
|
283
|
+
const issueId = 'session-count-test-001';
|
|
284
|
+
const issueIdentifier = 'TEST-SC';
|
|
285
|
+
it('returns 0 for nonexistent issue', async () => {
|
|
286
|
+
const count = await getTotalSessionCount('nonexistent-issue-id');
|
|
287
|
+
expect(count).toBe(0);
|
|
288
|
+
});
|
|
289
|
+
it('correctly sums across all phases', async () => {
|
|
290
|
+
await updateWorkflowState(issueId, { issueIdentifier });
|
|
291
|
+
// Add 2 development, 2 qa, 1 refinement, 1 acceptance
|
|
292
|
+
await recordPhaseAttempt(issueId, 'development', {
|
|
293
|
+
attempt: 1, sessionId: 'dev-1', startedAt: Date.now(), result: 'passed', costUsd: 1.0,
|
|
294
|
+
});
|
|
295
|
+
await recordPhaseAttempt(issueId, 'development', {
|
|
296
|
+
attempt: 2, sessionId: 'dev-2', startedAt: Date.now(), result: 'passed', costUsd: 1.0,
|
|
297
|
+
});
|
|
298
|
+
await recordPhaseAttempt(issueId, 'qa', {
|
|
299
|
+
attempt: 1, sessionId: 'qa-1', startedAt: Date.now(), result: 'failed', costUsd: 0.5,
|
|
300
|
+
});
|
|
301
|
+
await recordPhaseAttempt(issueId, 'qa', {
|
|
302
|
+
attempt: 2, sessionId: 'qa-2', startedAt: Date.now(), result: 'passed', costUsd: 0.5,
|
|
303
|
+
});
|
|
304
|
+
await recordPhaseAttempt(issueId, 'refinement', {
|
|
305
|
+
attempt: 1, sessionId: 'ref-1', startedAt: Date.now(), result: 'passed', costUsd: 0.3,
|
|
306
|
+
});
|
|
307
|
+
await recordPhaseAttempt(issueId, 'acceptance', {
|
|
308
|
+
attempt: 1, sessionId: 'acc-1', startedAt: Date.now(), result: 'passed', costUsd: 0.2,
|
|
309
|
+
});
|
|
310
|
+
const count = await getTotalSessionCount(issueId);
|
|
311
|
+
expect(count).toBe(6); // 2 + 2 + 1 + 1
|
|
312
|
+
});
|
|
313
|
+
it('returns count that would hit MAX_TOTAL_SESSIONS', async () => {
|
|
314
|
+
await updateWorkflowState(issueId, { issueIdentifier });
|
|
315
|
+
// Add exactly MAX_TOTAL_SESSIONS phase records
|
|
316
|
+
for (let i = 0; i < MAX_TOTAL_SESSIONS; i++) {
|
|
317
|
+
const phase = i % 2 === 0 ? 'development' : 'qa';
|
|
318
|
+
await recordPhaseAttempt(issueId, phase, {
|
|
319
|
+
attempt: i + 1, sessionId: `session-${i}`, startedAt: Date.now(), result: 'passed',
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
const count = await getTotalSessionCount(issueId);
|
|
323
|
+
expect(count).toBe(MAX_TOTAL_SESSIONS);
|
|
324
|
+
expect(count >= MAX_TOTAL_SESSIONS).toBe(true);
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
describe('acceptance completion lock', () => {
|
|
328
|
+
const issueId = 'acceptance-lock-test-001';
|
|
329
|
+
it('returns false before marking, true after marking', async () => {
|
|
330
|
+
const beforeMark = await didAcceptanceJustComplete(issueId);
|
|
331
|
+
expect(beforeMark).toBe(false);
|
|
332
|
+
await markAcceptanceCompleted(issueId);
|
|
333
|
+
const afterMark = await didAcceptanceJustComplete(issueId);
|
|
334
|
+
expect(afterMark).toBe(true);
|
|
335
|
+
});
|
|
336
|
+
it('returns false after clearing', async () => {
|
|
337
|
+
await markAcceptanceCompleted(issueId);
|
|
338
|
+
expect(await didAcceptanceJustComplete(issueId)).toBe(true);
|
|
339
|
+
await clearAcceptanceCompleted(issueId);
|
|
340
|
+
expect(await didAcceptanceJustComplete(issueId)).toBe(false);
|
|
341
|
+
});
|
|
342
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supaku/agentfactory-server",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.29",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Webhook server and distributed worker pool for AgentFactory — Redis queues, issue locks, session management",
|
|
6
6
|
"author": "Supaku (https://supaku.com)",
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
],
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"ioredis": "^5.4.2",
|
|
47
|
-
"@supaku/agentfactory": "0.7.
|
|
48
|
-
"@supaku/agentfactory-linear": "0.7.
|
|
47
|
+
"@supaku/agentfactory": "0.7.29",
|
|
48
|
+
"@supaku/agentfactory-linear": "0.7.29"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@types/node": "^22.5.4",
|