@ginkoai/cli 1.6.1 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -0
- package/dist/commands/agent/agent-client.d.ts +150 -0
- package/dist/commands/agent/agent-client.d.ts.map +1 -0
- package/dist/commands/agent/agent-client.js +170 -0
- package/dist/commands/agent/agent-client.js.map +1 -0
- package/dist/commands/agent/index.d.ts +22 -0
- package/dist/commands/agent/index.d.ts.map +1 -0
- package/dist/commands/agent/index.js +121 -0
- package/dist/commands/agent/index.js.map +1 -0
- package/dist/commands/agent/list.d.ts +22 -0
- package/dist/commands/agent/list.d.ts.map +1 -0
- package/dist/commands/agent/list.js +119 -0
- package/dist/commands/agent/list.js.map +1 -0
- package/dist/commands/agent/register.d.ts +21 -0
- package/dist/commands/agent/register.d.ts.map +1 -0
- package/dist/commands/agent/register.js +97 -0
- package/dist/commands/agent/register.js.map +1 -0
- package/dist/commands/agent/status.d.ts +19 -0
- package/dist/commands/agent/status.d.ts.map +1 -0
- package/dist/commands/agent/status.js +271 -0
- package/dist/commands/agent/status.js.map +1 -0
- package/dist/commands/agent/work.d.ts +22 -0
- package/dist/commands/agent/work.d.ts.map +1 -0
- package/dist/commands/agent/work.js +459 -0
- package/dist/commands/agent/work.js.map +1 -0
- package/dist/commands/checkpoint/create.d.ts +27 -0
- package/dist/commands/checkpoint/create.d.ts.map +1 -0
- package/dist/commands/checkpoint/create.js +82 -0
- package/dist/commands/checkpoint/create.js.map +1 -0
- package/dist/commands/checkpoint/index.d.ts +23 -0
- package/dist/commands/checkpoint/index.d.ts.map +1 -0
- package/dist/commands/checkpoint/index.js +91 -0
- package/dist/commands/checkpoint/index.js.map +1 -0
- package/dist/commands/checkpoint/list.d.ts +27 -0
- package/dist/commands/checkpoint/list.d.ts.map +1 -0
- package/dist/commands/checkpoint/list.js +115 -0
- package/dist/commands/checkpoint/list.js.map +1 -0
- package/dist/commands/checkpoint/show.d.ts +23 -0
- package/dist/commands/checkpoint/show.d.ts.map +1 -0
- package/dist/commands/checkpoint/show.js +102 -0
- package/dist/commands/checkpoint/show.js.map +1 -0
- package/dist/commands/dlq.d.ts +24 -0
- package/dist/commands/dlq.d.ts.map +1 -0
- package/dist/commands/dlq.js +172 -0
- package/dist/commands/dlq.js.map +1 -0
- package/dist/commands/epic.d.ts +29 -0
- package/dist/commands/epic.d.ts.map +1 -0
- package/dist/commands/epic.js +322 -0
- package/dist/commands/epic.js.map +1 -0
- package/dist/commands/escalation/create.d.ts +22 -0
- package/dist/commands/escalation/create.d.ts.map +1 -0
- package/dist/commands/escalation/create.js +122 -0
- package/dist/commands/escalation/create.js.map +1 -0
- package/dist/commands/escalation/escalation-client.d.ts +101 -0
- package/dist/commands/escalation/escalation-client.d.ts.map +1 -0
- package/dist/commands/escalation/escalation-client.js +129 -0
- package/dist/commands/escalation/escalation-client.js.map +1 -0
- package/dist/commands/escalation/index.d.ts +22 -0
- package/dist/commands/escalation/index.d.ts.map +1 -0
- package/dist/commands/escalation/index.js +94 -0
- package/dist/commands/escalation/index.js.map +1 -0
- package/dist/commands/escalation/list.d.ts +24 -0
- package/dist/commands/escalation/list.d.ts.map +1 -0
- package/dist/commands/escalation/list.js +170 -0
- package/dist/commands/escalation/list.js.map +1 -0
- package/dist/commands/escalation/resolve.d.ts +20 -0
- package/dist/commands/escalation/resolve.d.ts.map +1 -0
- package/dist/commands/escalation/resolve.js +102 -0
- package/dist/commands/escalation/resolve.js.map +1 -0
- package/dist/commands/graph/api-client.d.ts +222 -1
- package/dist/commands/graph/api-client.d.ts.map +1 -1
- package/dist/commands/graph/api-client.js +82 -0
- package/dist/commands/graph/api-client.js.map +1 -1
- package/dist/commands/handoff.d.ts.map +1 -1
- package/dist/commands/handoff.js +9 -1
- package/dist/commands/handoff.js.map +1 -1
- package/dist/commands/log.d.ts +3 -0
- package/dist/commands/log.d.ts.map +1 -1
- package/dist/commands/log.js +110 -16
- package/dist/commands/log.js.map +1 -1
- package/dist/commands/notifications/history.d.ts +21 -0
- package/dist/commands/notifications/history.d.ts.map +1 -0
- package/dist/commands/notifications/history.js +160 -0
- package/dist/commands/notifications/history.js.map +1 -0
- package/dist/commands/notifications/index.d.ts +22 -0
- package/dist/commands/notifications/index.d.ts.map +1 -0
- package/dist/commands/notifications/index.js +87 -0
- package/dist/commands/notifications/index.js.map +1 -0
- package/dist/commands/notifications/list.d.ts +19 -0
- package/dist/commands/notifications/list.d.ts.map +1 -0
- package/dist/commands/notifications/list.js +132 -0
- package/dist/commands/notifications/list.js.map +1 -0
- package/dist/commands/notifications/test.d.ts +19 -0
- package/dist/commands/notifications/test.d.ts.map +1 -0
- package/dist/commands/notifications/test.js +217 -0
- package/dist/commands/notifications/test.js.map +1 -0
- package/dist/commands/orchestrate.d.ts +25 -0
- package/dist/commands/orchestrate.d.ts.map +1 -0
- package/dist/commands/orchestrate.js +858 -0
- package/dist/commands/orchestrate.js.map +1 -0
- package/dist/commands/sprint/deps.d.ts +29 -0
- package/dist/commands/sprint/deps.d.ts.map +1 -0
- package/dist/commands/sprint/deps.js +269 -0
- package/dist/commands/sprint/deps.js.map +1 -0
- package/dist/commands/sprint/index.d.ts +10 -5
- package/dist/commands/sprint/index.d.ts.map +1 -1
- package/dist/commands/sprint/index.js +26 -5
- package/dist/commands/sprint/index.js.map +1 -1
- package/dist/commands/start/index.d.ts.map +1 -1
- package/dist/commands/start/index.js +6 -0
- package/dist/commands/start/index.js.map +1 -1
- package/dist/commands/start/start-reflection.d.ts +11 -0
- package/dist/commands/start/start-reflection.d.ts.map +1 -1
- package/dist/commands/start/start-reflection.js +150 -12
- package/dist/commands/start/start-reflection.js.map +1 -1
- package/dist/commands/verify.d.ts +17 -0
- package/dist/commands/verify.d.ts.map +1 -0
- package/dist/commands/verify.js +232 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/core/session-log-manager.d.ts +1 -1
- package/dist/core/session-log-manager.d.ts.map +1 -1
- package/dist/core/session-log-manager.js +12 -3
- package/dist/core/session-log-manager.js.map +1 -1
- package/dist/index.js +98 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/__tests__/task-timeout.test.d.ts +12 -0
- package/dist/lib/__tests__/task-timeout.test.d.ts.map +1 -0
- package/dist/lib/__tests__/task-timeout.test.js +278 -0
- package/dist/lib/__tests__/task-timeout.test.js.map +1 -0
- package/dist/lib/agent-heartbeat.d.ts +68 -0
- package/dist/lib/agent-heartbeat.d.ts.map +1 -0
- package/dist/lib/agent-heartbeat.js +117 -0
- package/dist/lib/agent-heartbeat.js.map +1 -0
- package/dist/lib/checkpoint.d.ts +85 -0
- package/dist/lib/checkpoint.d.ts.map +1 -0
- package/dist/lib/checkpoint.js +323 -0
- package/dist/lib/checkpoint.js.map +1 -0
- package/dist/lib/context-metrics.d.ts +230 -0
- package/dist/lib/context-metrics.d.ts.map +1 -0
- package/dist/lib/context-metrics.js +372 -0
- package/dist/lib/context-metrics.js.map +1 -0
- package/dist/lib/dead-letter-queue.d.ts +108 -0
- package/dist/lib/dead-letter-queue.d.ts.map +1 -0
- package/dist/lib/dead-letter-queue.js +378 -0
- package/dist/lib/dead-letter-queue.js.map +1 -0
- package/dist/lib/event-logger.d.ts +9 -1
- package/dist/lib/event-logger.d.ts.map +1 -1
- package/dist/lib/event-logger.js +45 -3
- package/dist/lib/event-logger.js.map +1 -1
- package/dist/lib/event-queue.d.ts.map +1 -1
- package/dist/lib/event-queue.js +13 -2
- package/dist/lib/event-queue.js.map +1 -1
- package/dist/lib/examples/timeout-demo.d.ts +13 -0
- package/dist/lib/examples/timeout-demo.d.ts.map +1 -0
- package/dist/lib/examples/timeout-demo.js +102 -0
- package/dist/lib/examples/timeout-demo.js.map +1 -0
- package/dist/lib/examples/timeout-integration-example.d.ts +17 -0
- package/dist/lib/examples/timeout-integration-example.d.ts.map +1 -0
- package/dist/lib/examples/timeout-integration-example.js +223 -0
- package/dist/lib/examples/timeout-integration-example.js.map +1 -0
- package/dist/lib/notification-hooks.d.ts +103 -0
- package/dist/lib/notification-hooks.d.ts.map +1 -0
- package/dist/lib/notification-hooks.js +223 -0
- package/dist/lib/notification-hooks.js.map +1 -0
- package/dist/lib/notifications/discord.d.ts +20 -0
- package/dist/lib/notifications/discord.d.ts.map +1 -0
- package/dist/lib/notifications/discord.js +140 -0
- package/dist/lib/notifications/discord.js.map +1 -0
- package/dist/lib/notifications/index.d.ts +66 -0
- package/dist/lib/notifications/index.d.ts.map +1 -0
- package/dist/lib/notifications/index.js +120 -0
- package/dist/lib/notifications/index.js.map +1 -0
- package/dist/lib/notifications/slack.d.ts +20 -0
- package/dist/lib/notifications/slack.d.ts.map +1 -0
- package/dist/lib/notifications/slack.js +186 -0
- package/dist/lib/notifications/slack.js.map +1 -0
- package/dist/lib/notifications/teams.d.ts +20 -0
- package/dist/lib/notifications/teams.d.ts.map +1 -0
- package/dist/lib/notifications/teams.js +146 -0
- package/dist/lib/notifications/teams.js.map +1 -0
- package/dist/lib/notifications/webhook.d.ts +23 -0
- package/dist/lib/notifications/webhook.d.ts.map +1 -0
- package/dist/lib/notifications/webhook.js +65 -0
- package/dist/lib/notifications/webhook.js.map +1 -0
- package/dist/lib/orchestrator-state.d.ts +194 -0
- package/dist/lib/orchestrator-state.d.ts.map +1 -0
- package/dist/lib/orchestrator-state.js +332 -0
- package/dist/lib/orchestrator-state.js.map +1 -0
- package/dist/lib/output-formatter.d.ts +25 -1
- package/dist/lib/output-formatter.d.ts.map +1 -1
- package/dist/lib/output-formatter.js +37 -17
- package/dist/lib/output-formatter.js.map +1 -1
- package/dist/lib/realtime-cursor.d.ts +107 -0
- package/dist/lib/realtime-cursor.d.ts.map +1 -0
- package/dist/lib/realtime-cursor.js +260 -0
- package/dist/lib/realtime-cursor.js.map +1 -0
- package/dist/lib/rollback.d.ts +86 -0
- package/dist/lib/rollback.d.ts.map +1 -0
- package/dist/lib/rollback.js +405 -0
- package/dist/lib/rollback.js.map +1 -0
- package/dist/lib/sprint-loader.d.ts +41 -2
- package/dist/lib/sprint-loader.d.ts.map +1 -1
- package/dist/lib/sprint-loader.js +335 -9
- package/dist/lib/sprint-loader.js.map +1 -1
- package/dist/lib/stale-agent-detector.d.ts +102 -0
- package/dist/lib/stale-agent-detector.d.ts.map +1 -0
- package/dist/lib/stale-agent-detector.js +156 -0
- package/dist/lib/stale-agent-detector.js.map +1 -0
- package/dist/lib/task-dependencies.d.ts +143 -0
- package/dist/lib/task-dependencies.d.ts.map +1 -0
- package/dist/lib/task-dependencies.js +357 -0
- package/dist/lib/task-dependencies.js.map +1 -0
- package/dist/lib/task-timeout.d.ts +153 -0
- package/dist/lib/task-timeout.d.ts.map +1 -0
- package/dist/lib/task-timeout.js +505 -0
- package/dist/lib/task-timeout.js.map +1 -0
- package/dist/lib/verification/build-check.d.ts +55 -0
- package/dist/lib/verification/build-check.d.ts.map +1 -0
- package/dist/lib/verification/build-check.js +111 -0
- package/dist/lib/verification/build-check.js.map +1 -0
- package/dist/lib/verification/index.d.ts +19 -0
- package/dist/lib/verification/index.d.ts.map +1 -0
- package/dist/lib/verification/index.js +17 -0
- package/dist/lib/verification/index.js.map +1 -0
- package/dist/lib/verification/lint-check.d.ts +34 -0
- package/dist/lib/verification/lint-check.d.ts.map +1 -0
- package/dist/lib/verification/lint-check.js +215 -0
- package/dist/lib/verification/lint-check.js.map +1 -0
- package/dist/lib/verification/test-runner.d.ts +50 -0
- package/dist/lib/verification/test-runner.d.ts.map +1 -0
- package/dist/lib/verification/test-runner.js +225 -0
- package/dist/lib/verification/test-runner.js.map +1 -0
- package/dist/templates/ai-instructions-template.d.ts +3 -2
- package/dist/templates/ai-instructions-template.d.ts.map +1 -1
- package/dist/templates/ai-instructions-template.js +104 -2
- package/dist/templates/ai-instructions-template.js.map +1 -1
- package/dist/templates/epic-template.md +319 -0
- package/dist/utils/command-helpers.d.ts +10 -0
- package/dist/utils/command-helpers.d.ts.map +1 -1
- package/dist/utils/command-helpers.js +59 -1
- package/dist/utils/command-helpers.js.map +1 -1
- package/dist/utils/pattern-confidence.d.ts +82 -0
- package/dist/utils/pattern-confidence.d.ts.map +1 -0
- package/dist/utils/pattern-confidence.js +172 -0
- package/dist/utils/pattern-confidence.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileType: test
|
|
3
|
+
* @status: current
|
|
4
|
+
* @updated: 2025-12-07
|
|
5
|
+
* @tags: [test, timeout, orchestrator, epic-004-sprint5, task-6]
|
|
6
|
+
* @related: [task-timeout.ts]
|
|
7
|
+
* @priority: high
|
|
8
|
+
* @complexity: medium
|
|
9
|
+
* @dependencies: [vitest, fs-extra]
|
|
10
|
+
*/
|
|
11
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
12
|
+
import fs from 'fs-extra';
|
|
13
|
+
import path from 'path';
|
|
14
|
+
import { startTaskTimeout, checkTimeouts, clearTaskTimeout, getActiveTimeouts, getTaskTimeout, getAllTimeouts, cleanupOldTimeouts, handleTimeout, TimeoutMonitor } from '../task-timeout.js';
|
|
15
|
+
// Mock dependencies
|
|
16
|
+
vi.mock('../../utils/helpers.js', () => ({
|
|
17
|
+
getGinkoDir: vi.fn(async () => '/tmp/ginko-test/.ginko'),
|
|
18
|
+
getUserEmail: vi.fn(async () => 'test@example.com'),
|
|
19
|
+
getProjectRoot: vi.fn(async () => '/tmp/ginko-test')
|
|
20
|
+
}));
|
|
21
|
+
vi.mock('../checkpoint.js', () => ({
|
|
22
|
+
createCheckpoint: vi.fn(async (taskId, agentId, message) => ({
|
|
23
|
+
id: `cp_${Date.now()}_test`,
|
|
24
|
+
taskId,
|
|
25
|
+
agentId,
|
|
26
|
+
timestamp: new Date(),
|
|
27
|
+
gitCommit: 'abc123',
|
|
28
|
+
filesModified: [],
|
|
29
|
+
eventsSince: 'event_123',
|
|
30
|
+
metadata: {},
|
|
31
|
+
message
|
|
32
|
+
}))
|
|
33
|
+
}));
|
|
34
|
+
vi.mock('../event-logger.js', () => ({
|
|
35
|
+
logEvent: vi.fn(async (entry) => ({
|
|
36
|
+
id: `event_${Date.now()}_test`,
|
|
37
|
+
user_id: 'test@example.com',
|
|
38
|
+
organization_id: 'org_test',
|
|
39
|
+
project_id: 'proj_test',
|
|
40
|
+
timestamp: new Date().toISOString(),
|
|
41
|
+
...entry
|
|
42
|
+
}))
|
|
43
|
+
}));
|
|
44
|
+
describe('task-timeout', () => {
|
|
45
|
+
const testDir = '/tmp/ginko-test/.ginko/timeouts';
|
|
46
|
+
beforeEach(async () => {
|
|
47
|
+
// Clean up test directory
|
|
48
|
+
await fs.remove(testDir);
|
|
49
|
+
await fs.ensureDir(testDir);
|
|
50
|
+
});
|
|
51
|
+
afterEach(async () => {
|
|
52
|
+
// Clean up after tests
|
|
53
|
+
await fs.remove(testDir);
|
|
54
|
+
});
|
|
55
|
+
describe('startTaskTimeout', () => {
|
|
56
|
+
it('should create a timeout with default configuration', async () => {
|
|
57
|
+
const timeout = await startTaskTimeout('TASK-1', 60000, // 60 seconds
|
|
58
|
+
'agent-123');
|
|
59
|
+
expect(timeout.taskId).toBe('TASK-1');
|
|
60
|
+
expect(timeout.agentId).toBe('agent-123');
|
|
61
|
+
expect(timeout.maxDuration).toBe(60000);
|
|
62
|
+
expect(timeout.status).toBe('active');
|
|
63
|
+
expect(timeout.startedAt).toBeInstanceOf(Date);
|
|
64
|
+
expect(timeout.timeoutAt).toBeInstanceOf(Date);
|
|
65
|
+
expect(timeout.warningAt).toBeInstanceOf(Date);
|
|
66
|
+
// Verify warning threshold (80%)
|
|
67
|
+
const expectedWarning = timeout.startedAt.getTime() + (60000 * 0.8);
|
|
68
|
+
expect(timeout.warningAt.getTime()).toBeCloseTo(expectedWarning, -1);
|
|
69
|
+
// Verify timeout time
|
|
70
|
+
const expectedTimeout = timeout.startedAt.getTime() + 60000;
|
|
71
|
+
expect(timeout.timeoutAt.getTime()).toBeCloseTo(expectedTimeout, -1);
|
|
72
|
+
});
|
|
73
|
+
it('should create a timeout with custom warning threshold', async () => {
|
|
74
|
+
const timeout = await startTaskTimeout('TASK-2', 60000, 'agent-456', { warningThreshold: 0.5 } // 50%
|
|
75
|
+
);
|
|
76
|
+
// Verify warning at 50%
|
|
77
|
+
const expectedWarning = timeout.startedAt.getTime() + (60000 * 0.5);
|
|
78
|
+
expect(timeout.warningAt.getTime()).toBeCloseTo(expectedWarning, -1);
|
|
79
|
+
});
|
|
80
|
+
it('should save timeout to file', async () => {
|
|
81
|
+
const timeout = await startTaskTimeout('TASK-3', 60000, 'agent-789');
|
|
82
|
+
const filePath = path.join(testDir, 'TASK-3.json');
|
|
83
|
+
const fileExists = await fs.pathExists(filePath);
|
|
84
|
+
expect(fileExists).toBe(true);
|
|
85
|
+
const savedData = await fs.readJSON(filePath);
|
|
86
|
+
expect(savedData.taskId).toBe('TASK-3');
|
|
87
|
+
expect(savedData.agentId).toBe('agent-789');
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
describe('getTaskTimeout', () => {
|
|
91
|
+
it('should retrieve timeout by task ID', async () => {
|
|
92
|
+
await startTaskTimeout('TASK-4', 60000, 'agent-111');
|
|
93
|
+
const timeout = await getTaskTimeout('TASK-4');
|
|
94
|
+
expect(timeout).not.toBeNull();
|
|
95
|
+
expect(timeout.taskId).toBe('TASK-4');
|
|
96
|
+
expect(timeout.agentId).toBe('agent-111');
|
|
97
|
+
});
|
|
98
|
+
it('should return null for non-existent timeout', async () => {
|
|
99
|
+
const timeout = await getTaskTimeout('TASK-NONEXISTENT');
|
|
100
|
+
expect(timeout).toBeNull();
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
describe('getActiveTimeouts', () => {
|
|
104
|
+
it('should return all active timeouts', async () => {
|
|
105
|
+
await startTaskTimeout('TASK-5', 60000, 'agent-222');
|
|
106
|
+
await startTaskTimeout('TASK-6', 60000, 'agent-333');
|
|
107
|
+
const activeTimeouts = await getActiveTimeouts();
|
|
108
|
+
expect(activeTimeouts).toHaveLength(2);
|
|
109
|
+
expect(activeTimeouts.map(t => t.taskId)).toContain('TASK-5');
|
|
110
|
+
expect(activeTimeouts.map(t => t.taskId)).toContain('TASK-6');
|
|
111
|
+
});
|
|
112
|
+
it('should not return completed timeouts', async () => {
|
|
113
|
+
await startTaskTimeout('TASK-7', 60000, 'agent-444');
|
|
114
|
+
await startTaskTimeout('TASK-8', 60000, 'agent-555');
|
|
115
|
+
// Complete one timeout
|
|
116
|
+
await clearTaskTimeout('TASK-7');
|
|
117
|
+
const activeTimeouts = await getActiveTimeouts();
|
|
118
|
+
expect(activeTimeouts).toHaveLength(1);
|
|
119
|
+
expect(activeTimeouts[0].taskId).toBe('TASK-8');
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
describe('clearTaskTimeout', () => {
|
|
123
|
+
it('should mark timeout as completed', async () => {
|
|
124
|
+
await startTaskTimeout('TASK-9', 60000, 'agent-666');
|
|
125
|
+
await clearTaskTimeout('TASK-9');
|
|
126
|
+
const timeout = await getTaskTimeout('TASK-9');
|
|
127
|
+
expect(timeout).not.toBeNull();
|
|
128
|
+
expect(timeout.status).toBe('completed');
|
|
129
|
+
});
|
|
130
|
+
it('should remove from active timeouts', async () => {
|
|
131
|
+
await startTaskTimeout('TASK-10', 60000, 'agent-777');
|
|
132
|
+
let activeTimeouts = await getActiveTimeouts();
|
|
133
|
+
expect(activeTimeouts).toHaveLength(1);
|
|
134
|
+
await clearTaskTimeout('TASK-10');
|
|
135
|
+
activeTimeouts = await getActiveTimeouts();
|
|
136
|
+
expect(activeTimeouts).toHaveLength(0);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
describe('checkTimeouts', () => {
|
|
140
|
+
it('should detect timed out tasks', async () => {
|
|
141
|
+
// Create timeout with very short duration (100ms)
|
|
142
|
+
await startTaskTimeout('TASK-11', 100, 'agent-888');
|
|
143
|
+
// Wait for timeout to expire
|
|
144
|
+
await new Promise(resolve => setTimeout(resolve, 150));
|
|
145
|
+
const timedOutTasks = await checkTimeouts();
|
|
146
|
+
expect(timedOutTasks).toHaveLength(1);
|
|
147
|
+
expect(timedOutTasks[0].taskId).toBe('TASK-11');
|
|
148
|
+
expect(timedOutTasks[0].agentId).toBe('agent-888');
|
|
149
|
+
expect(timedOutTasks[0].checkpointId).toBeDefined();
|
|
150
|
+
expect(timedOutTasks[0].escalationEventId).toBeDefined();
|
|
151
|
+
});
|
|
152
|
+
it('should update status to warning at threshold', async () => {
|
|
153
|
+
// Create timeout with 100ms duration, warning at 50ms (50%)
|
|
154
|
+
await startTaskTimeout('TASK-12', 100, 'agent-999', { warningThreshold: 0.5 });
|
|
155
|
+
// Wait for warning threshold
|
|
156
|
+
await new Promise(resolve => setTimeout(resolve, 60));
|
|
157
|
+
await checkTimeouts();
|
|
158
|
+
const timeout = await getTaskTimeout('TASK-12');
|
|
159
|
+
expect(timeout).not.toBeNull();
|
|
160
|
+
expect(timeout.status).toBe('warning');
|
|
161
|
+
});
|
|
162
|
+
it('should not process already timed out tasks', async () => {
|
|
163
|
+
// Create and timeout a task
|
|
164
|
+
await startTaskTimeout('TASK-13', 100, 'agent-1010');
|
|
165
|
+
await new Promise(resolve => setTimeout(resolve, 150));
|
|
166
|
+
const firstCheck = await checkTimeouts();
|
|
167
|
+
expect(firstCheck).toHaveLength(1);
|
|
168
|
+
// Check again
|
|
169
|
+
const secondCheck = await checkTimeouts();
|
|
170
|
+
// Should not process again
|
|
171
|
+
expect(secondCheck).toHaveLength(0);
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
describe('handleTimeout', () => {
|
|
175
|
+
it('should create checkpoint and escalation event', async () => {
|
|
176
|
+
const { createCheckpoint } = await import('../checkpoint.js');
|
|
177
|
+
const { logEvent } = await import('../event-logger.js');
|
|
178
|
+
await startTaskTimeout('TASK-14', 100, 'agent-1111');
|
|
179
|
+
await new Promise(resolve => setTimeout(resolve, 150));
|
|
180
|
+
const result = await handleTimeout('TASK-14');
|
|
181
|
+
expect(result).not.toBeNull();
|
|
182
|
+
expect(result.taskId).toBe('TASK-14');
|
|
183
|
+
expect(result.checkpointId).toBeDefined();
|
|
184
|
+
expect(result.escalationEventId).toBeDefined();
|
|
185
|
+
// Verify checkpoint was created
|
|
186
|
+
expect(createCheckpoint).toHaveBeenCalledWith('TASK-14', 'agent-1111', expect.stringContaining('timeout'), expect.objectContaining({ reason: 'timeout' }));
|
|
187
|
+
// Verify escalation event was logged
|
|
188
|
+
expect(logEvent).toHaveBeenCalledWith(expect.objectContaining({
|
|
189
|
+
category: 'blocker',
|
|
190
|
+
description: expect.stringContaining('TASK-14 timed out'),
|
|
191
|
+
blocker_severity: 'high'
|
|
192
|
+
}));
|
|
193
|
+
});
|
|
194
|
+
it('should update timeout status to timed_out', async () => {
|
|
195
|
+
await startTaskTimeout('TASK-15', 100, 'agent-1212');
|
|
196
|
+
await new Promise(resolve => setTimeout(resolve, 150));
|
|
197
|
+
await handleTimeout('TASK-15');
|
|
198
|
+
const timeout = await getTaskTimeout('TASK-15');
|
|
199
|
+
expect(timeout).not.toBeNull();
|
|
200
|
+
expect(timeout.status).toBe('timed_out');
|
|
201
|
+
expect(timeout.checkpointId).toBeDefined();
|
|
202
|
+
expect(timeout.escalationEventId).toBeDefined();
|
|
203
|
+
});
|
|
204
|
+
it('should return null for non-existent timeout', async () => {
|
|
205
|
+
const result = await handleTimeout('TASK-NONEXISTENT');
|
|
206
|
+
expect(result).toBeNull();
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
describe('getAllTimeouts', () => {
|
|
210
|
+
it('should return all timeouts regardless of status', async () => {
|
|
211
|
+
await startTaskTimeout('TASK-16', 60000, 'agent-1313');
|
|
212
|
+
await startTaskTimeout('TASK-17', 100, 'agent-1414');
|
|
213
|
+
// Complete one
|
|
214
|
+
await clearTaskTimeout('TASK-16');
|
|
215
|
+
// Timeout another
|
|
216
|
+
await new Promise(resolve => setTimeout(resolve, 150));
|
|
217
|
+
await checkTimeouts();
|
|
218
|
+
const allTimeouts = await getAllTimeouts();
|
|
219
|
+
expect(allTimeouts).toHaveLength(2);
|
|
220
|
+
const statuses = allTimeouts.map(t => t.status).sort();
|
|
221
|
+
expect(statuses).toContain('completed');
|
|
222
|
+
expect(statuses).toContain('timed_out');
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
describe('cleanupOldTimeouts', () => {
|
|
226
|
+
it('should remove old completed timeouts', async () => {
|
|
227
|
+
await startTaskTimeout('TASK-18', 60000, 'agent-1515');
|
|
228
|
+
// Manually set old timestamp
|
|
229
|
+
const filePath = path.join(testDir, 'TASK-18.json');
|
|
230
|
+
const timeout = await fs.readJSON(filePath);
|
|
231
|
+
const oldDate = new Date(Date.now() - 8 * 24 * 60 * 60 * 1000); // 8 days ago
|
|
232
|
+
timeout.startedAt = oldDate.toISOString();
|
|
233
|
+
timeout.status = 'completed';
|
|
234
|
+
await fs.writeJSON(filePath, timeout);
|
|
235
|
+
// Clean up timeouts older than 7 days
|
|
236
|
+
const cleanedCount = await cleanupOldTimeouts(7 * 24 * 60 * 60 * 1000);
|
|
237
|
+
expect(cleanedCount).toBe(1);
|
|
238
|
+
const fileExists = await fs.pathExists(filePath);
|
|
239
|
+
expect(fileExists).toBe(false);
|
|
240
|
+
});
|
|
241
|
+
it('should not remove active timeouts', async () => {
|
|
242
|
+
await startTaskTimeout('TASK-19', 60000, 'agent-1616');
|
|
243
|
+
// Manually set old timestamp but keep active
|
|
244
|
+
const filePath = path.join(testDir, 'TASK-19.json');
|
|
245
|
+
const timeout = await fs.readJSON(filePath);
|
|
246
|
+
const oldDate = new Date(Date.now() - 8 * 24 * 60 * 60 * 1000);
|
|
247
|
+
timeout.startedAt = oldDate.toISOString();
|
|
248
|
+
// status remains 'active'
|
|
249
|
+
await fs.writeJSON(filePath, timeout);
|
|
250
|
+
const cleanedCount = await cleanupOldTimeouts(7 * 24 * 60 * 60 * 1000);
|
|
251
|
+
expect(cleanedCount).toBe(0);
|
|
252
|
+
const fileExists = await fs.pathExists(filePath);
|
|
253
|
+
expect(fileExists).toBe(true);
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
describe('TimeoutMonitor', () => {
|
|
257
|
+
it('should start and stop monitoring', () => {
|
|
258
|
+
const monitor = new TimeoutMonitor({ checkInterval: 1000 });
|
|
259
|
+
monitor.start();
|
|
260
|
+
let status = monitor.getStatus();
|
|
261
|
+
expect(status.isRunning).toBe(true);
|
|
262
|
+
expect(status.checkInterval).toBe(1000);
|
|
263
|
+
monitor.stop();
|
|
264
|
+
status = monitor.getStatus();
|
|
265
|
+
expect(status.isRunning).toBe(false);
|
|
266
|
+
});
|
|
267
|
+
it('should not start twice', () => {
|
|
268
|
+
const monitor = new TimeoutMonitor();
|
|
269
|
+
const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => { });
|
|
270
|
+
monitor.start();
|
|
271
|
+
monitor.start();
|
|
272
|
+
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('already started'));
|
|
273
|
+
monitor.stop();
|
|
274
|
+
consoleSpy.mockRestore();
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
//# sourceMappingURL=task-timeout.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-timeout.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/task-timeout.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileType: utility
|
|
3
|
+
* @status: current
|
|
4
|
+
* @updated: 2025-12-05
|
|
5
|
+
* @tags: [agent, heartbeat, epic-004, multi-agent]
|
|
6
|
+
* @related: [event-queue.ts, ../commands/graph/api-client.ts]
|
|
7
|
+
* @priority: high
|
|
8
|
+
* @complexity: low
|
|
9
|
+
* @dependencies: []
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Agent Heartbeat Manager
|
|
13
|
+
*
|
|
14
|
+
* Sends periodic heartbeat signals to dashboard API to maintain agent online status.
|
|
15
|
+
*
|
|
16
|
+
* Pattern:
|
|
17
|
+
* - Sends heartbeat every 30 seconds when active
|
|
18
|
+
* - Uses .unref() on timer to allow process to exit if no other work pending
|
|
19
|
+
* - Stale agents (no heartbeat for 5 min) marked offline by dashboard
|
|
20
|
+
* - Offline agents excluded from task assignment
|
|
21
|
+
*
|
|
22
|
+
* Usage:
|
|
23
|
+
* ```typescript
|
|
24
|
+
* const { startHeartbeat, stopHeartbeat } = require('./agent-heartbeat');
|
|
25
|
+
*
|
|
26
|
+
* // Start heartbeat when agent becomes active
|
|
27
|
+
* startHeartbeat('agent_123456_abc');
|
|
28
|
+
*
|
|
29
|
+
* // Stop heartbeat when agent deactivates
|
|
30
|
+
* stopHeartbeat();
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
/**
|
|
34
|
+
* Heartbeat configuration
|
|
35
|
+
*/
|
|
36
|
+
interface HeartbeatConfig {
|
|
37
|
+
intervalMs: number;
|
|
38
|
+
dashboardUrl: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Start sending periodic heartbeats
|
|
42
|
+
*
|
|
43
|
+
* @param agentId - Agent ID to send heartbeats for
|
|
44
|
+
* @param config - Optional configuration overrides
|
|
45
|
+
*/
|
|
46
|
+
export declare function startHeartbeat(agentId: string, config?: Partial<HeartbeatConfig>): void;
|
|
47
|
+
/**
|
|
48
|
+
* Stop sending heartbeats
|
|
49
|
+
*/
|
|
50
|
+
export declare function stopHeartbeat(): void;
|
|
51
|
+
/**
|
|
52
|
+
* Graceful shutdown - send final heartbeat and stop
|
|
53
|
+
*/
|
|
54
|
+
export declare function shutdownHeartbeat(): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Check if heartbeat is currently running
|
|
57
|
+
*
|
|
58
|
+
* @returns True if heartbeat is active
|
|
59
|
+
*/
|
|
60
|
+
export declare function isHeartbeatRunning(): boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Get current agent ID (if heartbeat is running)
|
|
63
|
+
*
|
|
64
|
+
* @returns Current agent ID or null
|
|
65
|
+
*/
|
|
66
|
+
export declare function getCurrentAgentId(): string | null;
|
|
67
|
+
export {};
|
|
68
|
+
//# sourceMappingURL=agent-heartbeat.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-heartbeat.d.ts","sourceRoot":"","sources":["../../src/lib/agent-heartbeat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH;;GAEG;AACH,UAAU,eAAe;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAqCD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,IAAI,CA0BvF;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,IAAI,CAOpC;AAED;;GAEG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAiBvD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAE5C;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,GAAG,IAAI,CAEjD"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileType: utility
|
|
3
|
+
* @status: current
|
|
4
|
+
* @updated: 2025-12-05
|
|
5
|
+
* @tags: [agent, heartbeat, epic-004, multi-agent]
|
|
6
|
+
* @related: [event-queue.ts, ../commands/graph/api-client.ts]
|
|
7
|
+
* @priority: high
|
|
8
|
+
* @complexity: low
|
|
9
|
+
* @dependencies: []
|
|
10
|
+
*/
|
|
11
|
+
const DEFAULT_CONFIG = {
|
|
12
|
+
intervalMs: 30 * 1000, // 30 seconds
|
|
13
|
+
dashboardUrl: process.env.GINKO_DASHBOARD_URL || 'https://app.ginkoai.com',
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Global heartbeat state
|
|
17
|
+
*/
|
|
18
|
+
let heartbeatTimer = null;
|
|
19
|
+
let currentAgentId = null;
|
|
20
|
+
let isShuttingDown = false;
|
|
21
|
+
/**
|
|
22
|
+
* Send heartbeat to dashboard API
|
|
23
|
+
*
|
|
24
|
+
* @param agentId - Agent ID to send heartbeat for
|
|
25
|
+
* @returns Promise that resolves when heartbeat is sent
|
|
26
|
+
*/
|
|
27
|
+
async function sendHeartbeat(agentId) {
|
|
28
|
+
if (isShuttingDown) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
// Import graph API client lazily
|
|
33
|
+
const { sendAgentHeartbeat } = await import('../commands/graph/api-client.js');
|
|
34
|
+
// Send heartbeat via API client
|
|
35
|
+
await sendAgentHeartbeat(agentId);
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
// Log but don't throw - heartbeat failures shouldn't crash the CLI
|
|
39
|
+
console.warn('[AgentHeartbeat] Failed to send heartbeat:', error instanceof Error ? error.message : String(error));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Start sending periodic heartbeats
|
|
44
|
+
*
|
|
45
|
+
* @param agentId - Agent ID to send heartbeats for
|
|
46
|
+
* @param config - Optional configuration overrides
|
|
47
|
+
*/
|
|
48
|
+
export function startHeartbeat(agentId, config) {
|
|
49
|
+
if (heartbeatTimer) {
|
|
50
|
+
console.warn('[AgentHeartbeat] Heartbeat already running, stopping previous instance');
|
|
51
|
+
stopHeartbeat();
|
|
52
|
+
}
|
|
53
|
+
const finalConfig = { ...DEFAULT_CONFIG, ...config };
|
|
54
|
+
currentAgentId = agentId;
|
|
55
|
+
console.log(`[AgentHeartbeat] Starting heartbeat for agent ${agentId} (interval: ${finalConfig.intervalMs / 1000}s)`);
|
|
56
|
+
// Send initial heartbeat immediately
|
|
57
|
+
sendHeartbeat(agentId).catch(error => {
|
|
58
|
+
console.warn('[AgentHeartbeat] Initial heartbeat failed:', error instanceof Error ? error.message : String(error));
|
|
59
|
+
});
|
|
60
|
+
// Schedule periodic heartbeats
|
|
61
|
+
// Use unref() to allow process to exit if no other work pending
|
|
62
|
+
heartbeatTimer = setInterval(() => {
|
|
63
|
+
sendHeartbeat(agentId).catch(error => {
|
|
64
|
+
console.warn('[AgentHeartbeat] Scheduled heartbeat failed:', error instanceof Error ? error.message : String(error));
|
|
65
|
+
});
|
|
66
|
+
}, finalConfig.intervalMs);
|
|
67
|
+
// Don't keep process alive just for this timer
|
|
68
|
+
heartbeatTimer.unref();
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Stop sending heartbeats
|
|
72
|
+
*/
|
|
73
|
+
export function stopHeartbeat() {
|
|
74
|
+
if (heartbeatTimer) {
|
|
75
|
+
clearInterval(heartbeatTimer);
|
|
76
|
+
heartbeatTimer = null;
|
|
77
|
+
console.log('[AgentHeartbeat] Heartbeat stopped');
|
|
78
|
+
}
|
|
79
|
+
currentAgentId = null;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Graceful shutdown - send final heartbeat and stop
|
|
83
|
+
*/
|
|
84
|
+
export async function shutdownHeartbeat() {
|
|
85
|
+
console.log('[AgentHeartbeat] Initiating graceful shutdown...');
|
|
86
|
+
isShuttingDown = true;
|
|
87
|
+
// Send final heartbeat if we have an agent ID
|
|
88
|
+
if (currentAgentId) {
|
|
89
|
+
try {
|
|
90
|
+
await sendHeartbeat(currentAgentId);
|
|
91
|
+
console.log('[AgentHeartbeat] Final heartbeat sent');
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
console.warn('[AgentHeartbeat] Final heartbeat failed:', error instanceof Error ? error.message : String(error));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Stop timer
|
|
98
|
+
stopHeartbeat();
|
|
99
|
+
console.log('[AgentHeartbeat] Shutdown complete');
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Check if heartbeat is currently running
|
|
103
|
+
*
|
|
104
|
+
* @returns True if heartbeat is active
|
|
105
|
+
*/
|
|
106
|
+
export function isHeartbeatRunning() {
|
|
107
|
+
return heartbeatTimer !== null;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Get current agent ID (if heartbeat is running)
|
|
111
|
+
*
|
|
112
|
+
* @returns Current agent ID or null
|
|
113
|
+
*/
|
|
114
|
+
export function getCurrentAgentId() {
|
|
115
|
+
return currentAgentId;
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=agent-heartbeat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-heartbeat.js","sourceRoot":"","sources":["../../src/lib/agent-heartbeat.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileType: utility
|
|
3
|
+
* @status: current
|
|
4
|
+
* @updated: 2025-12-07
|
|
5
|
+
* @tags: [checkpoint, resilience, rollback, epic-004-sprint5, task-1]
|
|
6
|
+
* @related: [rollback.ts, event-logger.ts, orchestrator-state.ts]
|
|
7
|
+
* @priority: high
|
|
8
|
+
* @complexity: medium
|
|
9
|
+
* @dependencies: [fs-extra, uuid, simple-git]
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Checkpoint interface (EPIC-004 Sprint 5 TASK-1)
|
|
13
|
+
*
|
|
14
|
+
* Checkpoints are lightweight references to work state, not full snapshots.
|
|
15
|
+
* They capture git commit, modified files, and event stream position.
|
|
16
|
+
* Actual rollback uses git operations, not stored copies.
|
|
17
|
+
*/
|
|
18
|
+
export interface Checkpoint {
|
|
19
|
+
id: string;
|
|
20
|
+
taskId: string;
|
|
21
|
+
agentId: string;
|
|
22
|
+
timestamp: Date;
|
|
23
|
+
gitCommit: string;
|
|
24
|
+
filesModified: string[];
|
|
25
|
+
eventsSince: string;
|
|
26
|
+
metadata: Record<string, any>;
|
|
27
|
+
message?: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Create a checkpoint
|
|
31
|
+
*
|
|
32
|
+
* Captures current work state:
|
|
33
|
+
* - Git commit hash
|
|
34
|
+
* - Modified files since task start
|
|
35
|
+
* - Last event ID from event stream
|
|
36
|
+
* - Optional message and metadata
|
|
37
|
+
*
|
|
38
|
+
* @param taskId - Task ID this checkpoint belongs to
|
|
39
|
+
* @param agentId - Agent creating checkpoint (optional, auto-detected)
|
|
40
|
+
* @param message - Optional description
|
|
41
|
+
* @param metadata - Optional additional data
|
|
42
|
+
*/
|
|
43
|
+
export declare function createCheckpoint(taskId: string, agentId?: string, message?: string, metadata?: Record<string, any>): Promise<Checkpoint>;
|
|
44
|
+
/**
|
|
45
|
+
* Get a specific checkpoint by ID
|
|
46
|
+
*
|
|
47
|
+
* @param checkpointId - Checkpoint ID
|
|
48
|
+
* @returns Checkpoint object or null if not found
|
|
49
|
+
*/
|
|
50
|
+
export declare function getCheckpoint(checkpointId: string): Promise<Checkpoint | null>;
|
|
51
|
+
/**
|
|
52
|
+
* List checkpoints, optionally filtered by task ID
|
|
53
|
+
*
|
|
54
|
+
* @param taskId - Optional task ID to filter by
|
|
55
|
+
* @returns Array of checkpoints, sorted by timestamp (newest first)
|
|
56
|
+
*/
|
|
57
|
+
export declare function listCheckpoints(taskId?: string): Promise<Checkpoint[]>;
|
|
58
|
+
/**
|
|
59
|
+
* Delete a checkpoint
|
|
60
|
+
*
|
|
61
|
+
* @param checkpointId - Checkpoint ID to delete
|
|
62
|
+
*/
|
|
63
|
+
export declare function deleteCheckpoint(checkpointId: string): Promise<void>;
|
|
64
|
+
/**
|
|
65
|
+
* Get checkpoints for a specific task, grouped by day
|
|
66
|
+
* Useful for displaying checkpoint history
|
|
67
|
+
*/
|
|
68
|
+
export declare function getCheckpointsByTask(taskId: string): Promise<Map<string, Checkpoint[]>>;
|
|
69
|
+
/**
|
|
70
|
+
* Get the most recent checkpoint for a task
|
|
71
|
+
*/
|
|
72
|
+
export declare function getLatestCheckpoint(taskId: string): Promise<Checkpoint | null>;
|
|
73
|
+
/**
|
|
74
|
+
* Check if a checkpoint exists
|
|
75
|
+
*/
|
|
76
|
+
export declare function checkpointExists(checkpointId: string): Promise<boolean>;
|
|
77
|
+
/**
|
|
78
|
+
* Export checkpoint data for backup or transfer
|
|
79
|
+
*/
|
|
80
|
+
export declare function exportCheckpoint(checkpointId: string): Promise<string>;
|
|
81
|
+
/**
|
|
82
|
+
* Import checkpoint data from backup
|
|
83
|
+
*/
|
|
84
|
+
export declare function importCheckpoint(checkpointData: string): Promise<Checkpoint>;
|
|
85
|
+
//# sourceMappingURL=checkpoint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkpoint.d.ts","sourceRoot":"","sources":["../../src/lib/checkpoint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AASH;;;;;;GAMG;AACH,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AA+ID;;;;;;;;;;;;;GAaG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC7B,OAAO,CAAC,UAAU,CAAC,CAqCrB;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAkBpF;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAoC5E;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAc1E;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC,CAe7F;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAGpF;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAG7E;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQ5E;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAmBlF"}
|