mcp-copilot-worker 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +209 -0
  2. package/bin/mcp-copilot-worker.mjs +3 -0
  3. package/dist/src/app.d.ts +71 -0
  4. package/dist/src/app.js +306 -0
  5. package/dist/src/app.js.map +1 -0
  6. package/dist/src/cli/doctor.d.ts +2 -0
  7. package/dist/src/cli/doctor.js +99 -0
  8. package/dist/src/cli/doctor.js.map +1 -0
  9. package/dist/src/config/defaults.d.ts +3 -0
  10. package/dist/src/config/defaults.js +4 -0
  11. package/dist/src/config/defaults.js.map +1 -0
  12. package/dist/src/index.d.ts +2 -0
  13. package/dist/src/index.js +98 -0
  14. package/dist/src/index.js.map +1 -0
  15. package/dist/src/mcp/system-status.d.ts +19 -0
  16. package/dist/src/mcp/system-status.js +20 -0
  17. package/dist/src/mcp/system-status.js.map +1 -0
  18. package/dist/src/mcp/task-markdown.d.ts +3 -0
  19. package/dist/src/mcp/task-markdown.js +70 -0
  20. package/dist/src/mcp/task-markdown.js.map +1 -0
  21. package/dist/src/mcp/tool-banners.d.ts +4 -0
  22. package/dist/src/mcp/tool-banners.js +26 -0
  23. package/dist/src/mcp/tool-banners.js.map +1 -0
  24. package/dist/src/mcp/tool-definitions.d.ts +111 -0
  25. package/dist/src/mcp/tool-definitions.js +134 -0
  26. package/dist/src/mcp/tool-definitions.js.map +1 -0
  27. package/dist/src/services/copilot-runtime.d.ts +35 -0
  28. package/dist/src/services/copilot-runtime.js +237 -0
  29. package/dist/src/services/copilot-runtime.js.map +1 -0
  30. package/dist/src/services/fleet-mode.d.ts +15 -0
  31. package/dist/src/services/fleet-mode.js +26 -0
  32. package/dist/src/services/fleet-mode.js.map +1 -0
  33. package/dist/src/services/model-catalog.d.ts +16 -0
  34. package/dist/src/services/model-catalog.js +95 -0
  35. package/dist/src/services/model-catalog.js.map +1 -0
  36. package/dist/src/services/output-log.d.ts +7 -0
  37. package/dist/src/services/output-log.js +59 -0
  38. package/dist/src/services/output-log.js.map +1 -0
  39. package/dist/src/services/profile-manager.d.ts +34 -0
  40. package/dist/src/services/profile-manager.js +113 -0
  41. package/dist/src/services/profile-manager.js.map +1 -0
  42. package/dist/src/services/question-registry.d.ts +29 -0
  43. package/dist/src/services/question-registry.js +115 -0
  44. package/dist/src/services/question-registry.js.map +1 -0
  45. package/dist/src/services/spawn-validation.d.ts +9 -0
  46. package/dist/src/services/spawn-validation.js +53 -0
  47. package/dist/src/services/spawn-validation.js.map +1 -0
  48. package/dist/src/services/task-manager.d.ts +34 -0
  49. package/dist/src/services/task-manager.js +107 -0
  50. package/dist/src/services/task-manager.js.map +1 -0
  51. package/dist/src/services/task-persistence.d.ts +20 -0
  52. package/dist/src/services/task-persistence.js +67 -0
  53. package/dist/src/services/task-persistence.js.map +1 -0
  54. package/dist/src/services/task-store.d.ts +12 -0
  55. package/dist/src/services/task-store.js +167 -0
  56. package/dist/src/services/task-store.js.map +1 -0
  57. package/dist/src/services/workspace-isolation.d.ts +13 -0
  58. package/dist/src/services/workspace-isolation.js +74 -0
  59. package/dist/src/services/workspace-isolation.js.map +1 -0
  60. package/dist/src/templates/agent-prompt.d.ts +6 -0
  61. package/dist/src/templates/agent-prompt.js +58 -0
  62. package/dist/src/templates/agent-prompt.js.map +1 -0
  63. package/dist/src/types/task.d.ts +79 -0
  64. package/dist/src/types/task.js +22 -0
  65. package/dist/src/types/task.js.map +1 -0
  66. package/package.json +63 -0
  67. package/src/app.ts +344 -0
  68. package/src/cli/doctor.ts +112 -0
  69. package/src/config/defaults.ts +3 -0
  70. package/src/index.ts +128 -0
  71. package/src/mcp/system-status.ts +41 -0
  72. package/src/mcp/task-markdown.ts +81 -0
  73. package/src/mcp/tool-banners.ts +32 -0
  74. package/src/mcp/tool-definitions.ts +163 -0
  75. package/src/services/copilot-runtime.ts +305 -0
  76. package/src/services/fleet-mode.ts +39 -0
  77. package/src/services/model-catalog.ts +136 -0
  78. package/src/services/output-log.ts +68 -0
  79. package/src/services/profile-manager.ts +165 -0
  80. package/src/services/question-registry.ts +169 -0
  81. package/src/services/spawn-validation.ts +75 -0
  82. package/src/services/task-manager.ts +144 -0
  83. package/src/services/task-persistence.ts +100 -0
  84. package/src/services/task-store.ts +207 -0
  85. package/src/services/workspace-isolation.ts +100 -0
  86. package/src/templates/agent-prompt.ts +71 -0
  87. package/src/types/task.ts +95 -0
@@ -0,0 +1,113 @@
1
+ import { homedir } from 'node:os';
2
+ import { join } from 'node:path';
3
+ const DEFAULT_COOLDOWN_MS = 60_000;
4
+ function defaultProfileDir() {
5
+ return join(homedir(), '.copilot');
6
+ }
7
+ function dedupeProfileDirs(profileDirs) {
8
+ const seen = new Set();
9
+ const result = [];
10
+ for (const dir of profileDirs) {
11
+ const normalized = dir.trim();
12
+ if (!normalized || seen.has(normalized)) {
13
+ continue;
14
+ }
15
+ seen.add(normalized);
16
+ result.push(normalized);
17
+ }
18
+ return result.length > 0 ? result : [defaultProfileDir()];
19
+ }
20
+ export class ProfileManager {
21
+ cooldownMs;
22
+ now;
23
+ profiles;
24
+ currentIndex = 0;
25
+ constructor(options = {}) {
26
+ this.cooldownMs = options.cooldownMs ?? DEFAULT_COOLDOWN_MS;
27
+ this.now = options.now ?? Date.now;
28
+ const configuredDirs = dedupeProfileDirs(options.profileDirs ?? [defaultProfileDir()]);
29
+ const persistedByDir = new Map((options.persistedProfiles ?? []).map((profile) => [profile.configDir, profile]));
30
+ this.profiles = configuredDirs.map((configDir, index) => {
31
+ const persisted = persistedByDir.get(configDir);
32
+ return {
33
+ id: persisted?.id ?? `profile-${index + 1}`,
34
+ configDir,
35
+ cooldownUntil: persisted?.cooldownUntil,
36
+ failureCount: persisted?.failureCount ?? 0,
37
+ lastFailureReason: persisted?.lastFailureReason,
38
+ };
39
+ });
40
+ this.currentIndex = this.findFirstAvailableIndex();
41
+ }
42
+ static fromEnvironment(now) {
43
+ const raw = process.env.COPILOT_CONFIG_DIRS;
44
+ const dirs = raw
45
+ ? raw.split(',').map((entry) => entry.trim()).filter(Boolean)
46
+ : [defaultProfileDir()];
47
+ return new ProfileManager({ profileDirs: dirs, now });
48
+ }
49
+ hydrate(persistedProfiles) {
50
+ const persistedByDir = new Map(persistedProfiles.map((profile) => [profile.configDir, profile]));
51
+ for (const profile of this.profiles) {
52
+ const persisted = persistedByDir.get(profile.configDir);
53
+ if (!persisted) {
54
+ continue;
55
+ }
56
+ profile.failureCount = persisted.failureCount;
57
+ profile.cooldownUntil = persisted.cooldownUntil;
58
+ profile.lastFailureReason = persisted.lastFailureReason;
59
+ }
60
+ this.resetExpiredCooldowns();
61
+ }
62
+ getProfiles() {
63
+ return this.profiles.map((profile) => ({ ...profile }));
64
+ }
65
+ getCurrentProfile() {
66
+ this.resetExpiredCooldowns();
67
+ return { ...this.profiles[this.currentIndex] };
68
+ }
69
+ markFailure(reason) {
70
+ const current = this.profiles[this.currentIndex];
71
+ if (!current) {
72
+ return { success: false, allExhausted: true };
73
+ }
74
+ current.failureCount += 1;
75
+ current.lastFailureReason = reason;
76
+ current.cooldownUntil = this.now() + this.cooldownMs;
77
+ const nextIndex = this.findFirstAvailableIndex();
78
+ if (nextIndex === -1) {
79
+ return { success: false, allExhausted: true };
80
+ }
81
+ this.currentIndex = nextIndex;
82
+ return {
83
+ success: true,
84
+ currentProfile: this.getCurrentProfile(),
85
+ };
86
+ }
87
+ resetExpiredCooldowns() {
88
+ const now = this.now();
89
+ for (const profile of this.profiles) {
90
+ if (profile.cooldownUntil !== undefined && profile.cooldownUntil <= now) {
91
+ profile.cooldownUntil = undefined;
92
+ }
93
+ }
94
+ const nextIndex = this.findFirstAvailableIndex();
95
+ if (nextIndex !== -1) {
96
+ this.currentIndex = nextIndex;
97
+ }
98
+ }
99
+ toPersistedState() {
100
+ return this.profiles.map((profile) => ({
101
+ id: profile.id,
102
+ configDir: profile.configDir,
103
+ cooldownUntil: profile.cooldownUntil,
104
+ failureCount: profile.failureCount,
105
+ lastFailureReason: profile.lastFailureReason,
106
+ }));
107
+ }
108
+ findFirstAvailableIndex() {
109
+ const now = this.now();
110
+ return this.profiles.findIndex((profile) => profile.cooldownUntil === undefined || profile.cooldownUntil <= now);
111
+ }
112
+ }
113
+ //# sourceMappingURL=profile-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile-manager.js","sourceRoot":"","sources":["../../../src/services/profile-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import type { PendingQuestion } from '../types/task.js';
2
+ import type { TaskManager } from './task-manager.js';
3
+ interface RegisterQuestionInput {
4
+ taskId: string;
5
+ sessionId: string;
6
+ question: string;
7
+ choices?: string[] | undefined;
8
+ allowFreeform?: boolean | undefined;
9
+ }
10
+ export declare class QuestionRegistry {
11
+ private readonly taskManager;
12
+ private readonly timeoutMs;
13
+ private readonly pending;
14
+ constructor(taskManager: TaskManager, timeoutMs?: number);
15
+ register(input: RegisterQuestionInput): Promise<{
16
+ answer: string;
17
+ wasFreeform: boolean;
18
+ }>;
19
+ submitAnswer(taskId: string, rawAnswer: string): {
20
+ success: boolean;
21
+ resolvedAnswer?: string | undefined;
22
+ error?: string | undefined;
23
+ };
24
+ hasPendingQuestion(taskId: string): boolean;
25
+ getAllPendingQuestions(): Map<string, PendingQuestion>;
26
+ clear(taskId: string, reason: string): void;
27
+ private parseAnswer;
28
+ }
29
+ export {};
@@ -0,0 +1,115 @@
1
+ import { TaskStatus } from '../types/task.js';
2
+ const DEFAULT_TIMEOUT_MS = 30 * 60 * 1000;
3
+ export class QuestionRegistry {
4
+ taskManager;
5
+ timeoutMs;
6
+ pending = new Map();
7
+ constructor(taskManager, timeoutMs = DEFAULT_TIMEOUT_MS) {
8
+ this.taskManager = taskManager;
9
+ this.timeoutMs = timeoutMs;
10
+ }
11
+ register(input) {
12
+ this.clear(input.taskId, 'replaced by newer question');
13
+ return new Promise((resolve, reject) => {
14
+ const question = {
15
+ question: input.question,
16
+ choices: input.choices,
17
+ allowFreeform: input.allowFreeform ?? true,
18
+ askedAt: new Date().toISOString(),
19
+ sessionId: input.sessionId,
20
+ };
21
+ const timeout = setTimeout(() => {
22
+ void this.taskManager.updateTask(input.taskId, {
23
+ status: TaskStatus.FAILED,
24
+ error: 'Task failed: user question timed out after 30 minutes',
25
+ pendingQuestion: undefined,
26
+ endTime: new Date().toISOString(),
27
+ });
28
+ reject(new Error('Question timed out'));
29
+ this.pending.delete(input.taskId);
30
+ }, this.timeoutMs);
31
+ timeout.unref();
32
+ this.pending.set(input.taskId, { question, resolve, reject, timeout });
33
+ void this.taskManager.updateTask(input.taskId, {
34
+ status: TaskStatus.WAITING_ANSWER,
35
+ pendingQuestion: question,
36
+ });
37
+ void this.taskManager.appendOutput(input.taskId, `[question] ${input.question}`);
38
+ });
39
+ }
40
+ submitAnswer(taskId, rawAnswer) {
41
+ const binding = this.pending.get(taskId);
42
+ if (!binding) {
43
+ return { success: false, error: 'No pending question for this task' };
44
+ }
45
+ const parsed = this.parseAnswer(rawAnswer, binding.question);
46
+ if (!parsed.success) {
47
+ return parsed;
48
+ }
49
+ clearTimeout(binding.timeout);
50
+ binding.resolve({ answer: parsed.resolvedAnswer, wasFreeform: parsed.wasFreeform });
51
+ this.pending.delete(taskId);
52
+ void this.taskManager.updateTask(taskId, {
53
+ status: TaskStatus.RUNNING,
54
+ pendingQuestion: undefined,
55
+ });
56
+ void this.taskManager.appendOutput(taskId, `[question] Answer submitted: ${parsed.resolvedAnswer}`);
57
+ return parsed;
58
+ }
59
+ hasPendingQuestion(taskId) {
60
+ return this.pending.has(taskId);
61
+ }
62
+ getAllPendingQuestions() {
63
+ return new Map([...this.pending.entries()].map(([taskId, binding]) => [taskId, binding.question]));
64
+ }
65
+ clear(taskId, reason) {
66
+ const binding = this.pending.get(taskId);
67
+ if (!binding) {
68
+ return;
69
+ }
70
+ clearTimeout(binding.timeout);
71
+ binding.reject(new Error(reason));
72
+ this.pending.delete(taskId);
73
+ void this.taskManager.updateTask(taskId, { pendingQuestion: undefined });
74
+ }
75
+ parseAnswer(rawAnswer, question) {
76
+ const answer = rawAnswer.trim();
77
+ if (!answer) {
78
+ return { success: false, error: 'Answer cannot be empty' };
79
+ }
80
+ if (question.choices && /^\d+$/.test(answer)) {
81
+ const index = Number.parseInt(answer, 10) - 1;
82
+ if (index < 0 || index >= question.choices.length) {
83
+ return { success: false, error: `Invalid choice. Please enter 1-${question.choices.length}.` };
84
+ }
85
+ return {
86
+ success: true,
87
+ resolvedAnswer: question.choices[index],
88
+ wasFreeform: false,
89
+ };
90
+ }
91
+ if (answer.toUpperCase().startsWith('OTHER:') && question.allowFreeform) {
92
+ return {
93
+ success: true,
94
+ resolvedAnswer: answer.slice('OTHER:'.length).trim(),
95
+ wasFreeform: true,
96
+ };
97
+ }
98
+ if (question.choices?.includes(answer)) {
99
+ return {
100
+ success: true,
101
+ resolvedAnswer: answer,
102
+ wasFreeform: false,
103
+ };
104
+ }
105
+ if (question.allowFreeform) {
106
+ return {
107
+ success: true,
108
+ resolvedAnswer: answer,
109
+ wasFreeform: true,
110
+ };
111
+ }
112
+ return { success: false, error: 'Invalid answer' };
113
+ }
114
+ }
115
+ //# sourceMappingURL=question-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"question-registry.js","sourceRoot":"","sources":["../../../src/services/question-registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { ContextFile } from '../types/task.js';
2
+ import type { SpawnAgentInput } from '../mcp/tool-definitions.js';
3
+ export interface ValidateSpawnAgentInputArgs {
4
+ input: SpawnAgentInput;
5
+ baseCwd: string;
6
+ }
7
+ export declare function validateSpawnAgentInput(args: ValidateSpawnAgentInputArgs): Promise<{
8
+ contextFiles: ContextFile[] | undefined;
9
+ }>;
@@ -0,0 +1,53 @@
1
+ import { stat } from 'node:fs/promises';
2
+ import { resolve } from 'node:path';
3
+ const MAX_CONTEXT_FILES = 20;
4
+ const MAX_CONTEXT_FILE_BYTES = 200 * 1024;
5
+ const MAX_TOTAL_CONTEXT_BYTES = 500 * 1024;
6
+ const MIN_PROMPT_LENGTH = {
7
+ coder: 1000,
8
+ planner: 300,
9
+ researcher: 200,
10
+ tester: 300,
11
+ general: 200,
12
+ };
13
+ export async function validateSpawnAgentInput(args) {
14
+ const minimum = MIN_PROMPT_LENGTH[args.input.agent_type];
15
+ if (args.input.prompt.trim().length < minimum) {
16
+ throw new Error(`${args.input.agent_type} tasks require a prompt of at least ${minimum} characters`);
17
+ }
18
+ const inputFiles = args.input.context_files;
19
+ if (args.input.agent_type === 'coder' && (inputFiles?.length ?? 0) < 1) {
20
+ throw new Error('coder tasks require at least one context_files entry');
21
+ }
22
+ if (!inputFiles || inputFiles.length === 0) {
23
+ return { contextFiles: undefined };
24
+ }
25
+ if (inputFiles.length > MAX_CONTEXT_FILES) {
26
+ throw new Error(`context_files may contain at most ${MAX_CONTEXT_FILES} files`);
27
+ }
28
+ const normalized = [];
29
+ let totalBytes = 0;
30
+ for (const file of inputFiles) {
31
+ const absolutePath = resolve(args.baseCwd, file.path);
32
+ const info = await stat(absolutePath).catch(() => null);
33
+ if (!info) {
34
+ throw new Error(`Context file not found: ${file.path}`);
35
+ }
36
+ if (!info.isFile()) {
37
+ throw new Error(`Context file is not a regular file: ${file.path}`);
38
+ }
39
+ if (info.size > MAX_CONTEXT_FILE_BYTES) {
40
+ throw new Error(`Context file exceeds 200KB: ${file.path}`);
41
+ }
42
+ totalBytes += info.size;
43
+ if (totalBytes > MAX_TOTAL_CONTEXT_BYTES) {
44
+ throw new Error('Combined context_files size exceeds 500KB');
45
+ }
46
+ normalized.push({
47
+ path: absolutePath,
48
+ description: file.description,
49
+ });
50
+ }
51
+ return { contextFiles: normalized };
52
+ }
53
+ //# sourceMappingURL=spawn-validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawn-validation.js","sourceRoot":"","sources":["../../../src/services/spawn-validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAC7B,MAAM,sBAAsB,GAAG,GAAG,GAAG,IAAI,CAAC;AAC1C,MAAM,uBAAuB,GAAG,GAAG,GAAG,IAAI,CAAC;AAE3C,MAAM,iBAAiB,GAAkD;IACvE,KAAK,EAAE,IAAI;IACX,OAAO,EAAE,GAAG;IACZ,UAAU,EAAE,GAAG;IACf,MAAM,EAAE,GAAG;IACX,OAAO,EAAE,GAAG;CACb,CAAC;AAOF,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,IAAiC;IAEjC,MAAM,OAAO,GAAG,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACzD,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,OAAO,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CACb,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,uCAAuC,OAAO,aAAa,CACpF,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC;IAC5C,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,KAAK,OAAO,IAAI,CAAC,UAAU,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;QACvE,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;IAC1E,CAAC;IAED,IAAI,CAAC,UAAU,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3C,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC;IACrC,CAAC;IAED,IAAI,UAAU,CAAC,MAAM,GAAG,iBAAiB,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,qCAAqC,iBAAiB,QAAQ,CAAC,CAAC;IAClF,CAAC;IAED,MAAM,UAAU,GAAkB,EAAE,CAAC;IACrC,IAAI,UAAU,GAAG,CAAC,CAAC;IAEnB,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACtD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;QACxD,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,KAAK,CAAC,2BAA2B,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAC1D,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CAAC,uCAAuC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QACtE,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,GAAG,sBAAsB,EAAE,CAAC;YACvC,MAAM,IAAI,KAAK,CAAC,+BAA+B,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAC9D,CAAC;QAED,UAAU,IAAI,IAAI,CAAC,IAAI,CAAC;QACxB,IAAI,UAAU,GAAG,uBAAuB,EAAE,CAAC;YACzC,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;QAC/D,CAAC;QAED,UAAU,CAAC,IAAI,CAAC;YACd,IAAI,EAAE,YAAY;YAClB,WAAW,EAAE,IAAI,CAAC,WAAW;SAC9B,CAAC,CAAC;IACL,CAAC;IAED,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,CAAC;AACtC,CAAC"}
@@ -0,0 +1,34 @@
1
+ import type { PersistedProfileState } from '../types/task.js';
2
+ import type { OutputLogService } from './output-log.js';
3
+ import { type LoadedWorkspaceState } from './task-persistence.js';
4
+ import type { CreateTaskInput, TaskRecord } from '../types/task.js';
5
+ export interface TaskManagerOptions {
6
+ storageRoot?: string | undefined;
7
+ outputLog: OutputLogService;
8
+ getProfiles?: (() => PersistedProfileState[]) | undefined;
9
+ }
10
+ type ExecutionBinding = {
11
+ abort?: () => Promise<void>;
12
+ cleanup?: () => Promise<void>;
13
+ };
14
+ export declare class TaskManager {
15
+ private readonly store;
16
+ private readonly storageRoot;
17
+ private readonly outputLog;
18
+ private readonly getProfiles;
19
+ private readonly executionBindings;
20
+ constructor(options: TaskManagerOptions);
21
+ load(cwd: string): Promise<LoadedWorkspaceState>;
22
+ getTask(taskId: string): TaskRecord | undefined;
23
+ getAllTasks(): TaskRecord[];
24
+ createTask(input: CreateTaskInput): Promise<TaskRecord>;
25
+ updateTask(taskId: string, updates: Partial<TaskRecord>): Promise<TaskRecord | undefined>;
26
+ appendOutput(taskId: string, line: string): Promise<void>;
27
+ releaseReadyTasks(): Promise<TaskRecord[]>;
28
+ registerExecution(taskId: string, binding: ExecutionBinding): Promise<void>;
29
+ clearExecution(taskId: string): Promise<void>;
30
+ cancelTask(taskId: string): Promise<boolean>;
31
+ clearAllTasks(): Promise<number>;
32
+ private persist;
33
+ }
34
+ export {};
@@ -0,0 +1,107 @@
1
+ import { TaskStore } from './task-store.js';
2
+ import { loadWorkspaceState, saveWorkspaceState, } from './task-persistence.js';
3
+ import { TaskStatus, isTerminalStatus } from '../types/task.js';
4
+ export class TaskManager {
5
+ store = new TaskStore();
6
+ storageRoot;
7
+ outputLog;
8
+ getProfiles;
9
+ executionBindings = new Map();
10
+ constructor(options) {
11
+ this.storageRoot = options.storageRoot;
12
+ this.outputLog = options.outputLog;
13
+ this.getProfiles = options.getProfiles;
14
+ }
15
+ async load(cwd) {
16
+ const restored = await loadWorkspaceState({ cwd, storageRoot: this.storageRoot });
17
+ for (const task of restored.tasks) {
18
+ this.store.importTask(task);
19
+ }
20
+ return restored;
21
+ }
22
+ getTask(taskId) {
23
+ return this.store.getTask(taskId);
24
+ }
25
+ getAllTasks() {
26
+ return this.store.getAllTasks();
27
+ }
28
+ async createTask(input) {
29
+ const task = this.store.createTask(input);
30
+ task.outputFilePath = await this.outputLog.create(task.cwd, task.id);
31
+ await this.persist(task.cwd);
32
+ return task;
33
+ }
34
+ async updateTask(taskId, updates) {
35
+ const task = this.store.updateTask(taskId, updates);
36
+ if (!task) {
37
+ return undefined;
38
+ }
39
+ if (task.outputFilePath && updates.status && isTerminalStatus(updates.status)) {
40
+ await this.outputLog.finalize(task.cwd, task.id, task.status, task.error);
41
+ }
42
+ await this.persist(task.cwd);
43
+ return task;
44
+ }
45
+ async appendOutput(taskId, line) {
46
+ const task = this.store.getTask(taskId);
47
+ if (!task) {
48
+ return;
49
+ }
50
+ task.output.push(line);
51
+ task.updatedAt = new Date().toISOString();
52
+ await this.outputLog.append(task.cwd, task.id, line);
53
+ await this.persist(task.cwd);
54
+ }
55
+ async releaseReadyTasks() {
56
+ const tasks = this.store.releaseReadyTasks();
57
+ if (tasks.length > 0) {
58
+ await this.persist(tasks[0].cwd);
59
+ }
60
+ return tasks;
61
+ }
62
+ async registerExecution(taskId, binding) {
63
+ this.executionBindings.set(taskId, binding);
64
+ }
65
+ async clearExecution(taskId) {
66
+ const binding = this.executionBindings.get(taskId);
67
+ if (!binding) {
68
+ return;
69
+ }
70
+ await binding.cleanup?.();
71
+ this.executionBindings.delete(taskId);
72
+ }
73
+ async cancelTask(taskId) {
74
+ const task = this.store.getTask(taskId);
75
+ if (!task || isTerminalStatus(task.status)) {
76
+ return false;
77
+ }
78
+ const binding = this.executionBindings.get(taskId);
79
+ await binding?.abort?.();
80
+ await this.clearExecution(taskId);
81
+ await this.updateTask(taskId, {
82
+ status: TaskStatus.CANCELLED,
83
+ endTime: new Date().toISOString(),
84
+ });
85
+ return true;
86
+ }
87
+ async clearAllTasks() {
88
+ const tasks = this.store.getAllTasks();
89
+ for (const task of tasks) {
90
+ await this.cancelTask(task.id).catch(() => { });
91
+ }
92
+ this.store.clear();
93
+ if (tasks[0]) {
94
+ await this.persist(tasks[0].cwd);
95
+ }
96
+ return tasks.length;
97
+ }
98
+ async persist(cwd) {
99
+ await saveWorkspaceState({
100
+ cwd,
101
+ storageRoot: this.storageRoot,
102
+ tasks: this.store.getAllTasks(),
103
+ profiles: this.getProfiles?.() ?? [],
104
+ });
105
+ }
106
+ }
107
+ //# sourceMappingURL=task-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-manager.js","sourceRoot":"","sources":["../../../src/services/task-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { PersistedProfileState, TaskRecord } from '../types/task.js';
2
+ export interface SaveWorkspaceStateInput {
3
+ cwd: string;
4
+ storageRoot?: string | undefined;
5
+ tasks: TaskRecord[];
6
+ profiles: PersistedProfileState[];
7
+ }
8
+ export interface LoadWorkspaceStateInput {
9
+ cwd: string;
10
+ storageRoot?: string | undefined;
11
+ }
12
+ export interface LoadedWorkspaceState {
13
+ tasks: TaskRecord[];
14
+ profiles: PersistedProfileState[];
15
+ }
16
+ export declare function defaultStorageRoot(): string;
17
+ export declare function getWorkspaceStatePath(cwd: string, storageRoot?: string): string;
18
+ export declare function saveWorkspaceState(input: SaveWorkspaceStateInput): Promise<void>;
19
+ export declare function loadWorkspaceState(input: LoadWorkspaceStateInput): Promise<LoadedWorkspaceState>;
20
+ export declare function deleteWorkspaceState(cwd: string, storageRoot?: string): Promise<void>;
@@ -0,0 +1,67 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { mkdir, readFile, rename, unlink, writeFile } from 'node:fs/promises';
3
+ import { homedir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { TaskStatus } from '../types/task.js';
6
+ const STORAGE_DIR_NAME = '.super-agents';
7
+ export function defaultStorageRoot() {
8
+ return join(homedir(), STORAGE_DIR_NAME);
9
+ }
10
+ export function getWorkspaceStatePath(cwd, storageRoot = defaultStorageRoot()) {
11
+ const digest = createHash('md5').update(cwd).digest('hex');
12
+ return join(storageRoot, `${digest}.json`);
13
+ }
14
+ function recoverTask(task) {
15
+ if (task.status === TaskStatus.RATE_LIMITED) {
16
+ return task;
17
+ }
18
+ if (task.status === TaskStatus.RUNNING ||
19
+ task.status === TaskStatus.PENDING ||
20
+ task.status === TaskStatus.WAITING ||
21
+ task.status === TaskStatus.WAITING_ANSWER) {
22
+ return {
23
+ ...task,
24
+ status: TaskStatus.FAILED,
25
+ error: 'Server restarted while task was active',
26
+ endTime: new Date().toISOString(),
27
+ updatedAt: new Date().toISOString(),
28
+ };
29
+ }
30
+ return task;
31
+ }
32
+ export async function saveWorkspaceState(input) {
33
+ const storageRoot = input.storageRoot ?? defaultStorageRoot();
34
+ const path = getWorkspaceStatePath(input.cwd, storageRoot);
35
+ const tempPath = `${path}.${Date.now().toString(36)}.${Math.random().toString(36).slice(2, 8)}.tmp`;
36
+ await mkdir(storageRoot, { recursive: true });
37
+ const payload = {
38
+ version: 1,
39
+ tasks: input.tasks,
40
+ profiles: input.profiles,
41
+ };
42
+ await writeFile(tempPath, JSON.stringify(payload, null, 2), 'utf8');
43
+ await rename(tempPath, path);
44
+ }
45
+ export async function loadWorkspaceState(input) {
46
+ const storageRoot = input.storageRoot ?? defaultStorageRoot();
47
+ const path = getWorkspaceStatePath(input.cwd, storageRoot);
48
+ try {
49
+ const raw = await readFile(path, 'utf8');
50
+ const parsed = JSON.parse(raw);
51
+ return {
52
+ tasks: parsed.tasks.map(recoverTask),
53
+ profiles: parsed.profiles ?? [],
54
+ };
55
+ }
56
+ catch {
57
+ return {
58
+ tasks: [],
59
+ profiles: [],
60
+ };
61
+ }
62
+ }
63
+ export async function deleteWorkspaceState(cwd, storageRoot) {
64
+ const path = getWorkspaceStatePath(cwd, storageRoot ?? defaultStorageRoot());
65
+ await unlink(path).catch(() => { });
66
+ }
67
+ //# sourceMappingURL=task-persistence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-persistence.js","sourceRoot":"","sources":["../../../src/services/task-persistence.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { CreateTaskInput, TaskRecord } from '../types/task.js';
2
+ export declare class TaskStore {
3
+ private readonly tasks;
4
+ createTask(input: CreateTaskInput): TaskRecord;
5
+ getTask(taskId: string): TaskRecord | undefined;
6
+ getAllTasks(): TaskRecord[];
7
+ importTask(task: TaskRecord): void;
8
+ validateDependencies(dependsOn: string[], newTaskId?: string): string | null;
9
+ updateTask(taskId: string, updates: Partial<TaskRecord>): TaskRecord | undefined;
10
+ releaseReadyTasks(): TaskRecord[];
11
+ clear(): void;
12
+ }