pm-orchestrator-runner 1.0.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/LICENSE +21 -0
- package/README.md +108 -0
- package/dist/cli/cli-interface.d.ts +150 -0
- package/dist/cli/cli-interface.d.ts.map +1 -0
- package/dist/cli/cli-interface.js +606 -0
- package/dist/cli/cli-interface.js.map +1 -0
- package/dist/cli/index.d.ts +13 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +243 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/llm-sentinel.d.ts +15 -0
- package/dist/cli/llm-sentinel.d.ts.map +1 -0
- package/dist/cli/llm-sentinel.js +184 -0
- package/dist/cli/llm-sentinel.js.map +1 -0
- package/dist/config/configuration-manager.d.ts +149 -0
- package/dist/config/configuration-manager.d.ts.map +1 -0
- package/dist/config/configuration-manager.js +241 -0
- package/dist/config/configuration-manager.js.map +1 -0
- package/dist/continuation/continuation-control-manager.d.ts +154 -0
- package/dist/continuation/continuation-control-manager.d.ts.map +1 -0
- package/dist/continuation/continuation-control-manager.js +303 -0
- package/dist/continuation/continuation-control-manager.js.map +1 -0
- package/dist/core/runner-core.d.ts +474 -0
- package/dist/core/runner-core.d.ts.map +1 -0
- package/dist/core/runner-core.js +1311 -0
- package/dist/core/runner-core.js.map +1 -0
- package/dist/errors/error-codes.d.ts +105 -0
- package/dist/errors/error-codes.d.ts.map +1 -0
- package/dist/errors/error-codes.js +198 -0
- package/dist/errors/error-codes.js.map +1 -0
- package/dist/errors/runner-error.d.ts +14 -0
- package/dist/errors/runner-error.d.ts.map +1 -0
- package/dist/errors/runner-error.js +33 -0
- package/dist/errors/runner-error.js.map +1 -0
- package/dist/evidence/evidence-manager.d.ts +112 -0
- package/dist/evidence/evidence-manager.d.ts.map +1 -0
- package/dist/evidence/evidence-manager.js +337 -0
- package/dist/evidence/evidence-manager.js.map +1 -0
- package/dist/executor/claude-code-executor.d.ts +136 -0
- package/dist/executor/claude-code-executor.d.ts.map +1 -0
- package/dist/executor/claude-code-executor.js +643 -0
- package/dist/executor/claude-code-executor.js.map +1 -0
- package/dist/executor/deterministic-executor.d.ts +40 -0
- package/dist/executor/deterministic-executor.d.ts.map +1 -0
- package/dist/executor/deterministic-executor.js +269 -0
- package/dist/executor/deterministic-executor.js.map +1 -0
- package/dist/lifecycle/lifecycle-controller.d.ts +270 -0
- package/dist/lifecycle/lifecycle-controller.d.ts.map +1 -0
- package/dist/lifecycle/lifecycle-controller.js +596 -0
- package/dist/lifecycle/lifecycle-controller.js.map +1 -0
- package/dist/limits/resource-limit-manager.d.ts +200 -0
- package/dist/limits/resource-limit-manager.d.ts.map +1 -0
- package/dist/limits/resource-limit-manager.js +376 -0
- package/dist/limits/resource-limit-manager.js.map +1 -0
- package/dist/locks/lock-manager.d.ts +116 -0
- package/dist/locks/lock-manager.d.ts.map +1 -0
- package/dist/locks/lock-manager.js +306 -0
- package/dist/locks/lock-manager.js.map +1 -0
- package/dist/logging/index.d.ts +8 -0
- package/dist/logging/index.d.ts.map +1 -0
- package/dist/logging/index.js +22 -0
- package/dist/logging/index.js.map +1 -0
- package/dist/logging/sensitive-data-masker.d.ts +90 -0
- package/dist/logging/sensitive-data-masker.d.ts.map +1 -0
- package/dist/logging/sensitive-data-masker.js +228 -0
- package/dist/logging/sensitive-data-masker.js.map +1 -0
- package/dist/logging/task-log-manager.d.ts +215 -0
- package/dist/logging/task-log-manager.d.ts.map +1 -0
- package/dist/logging/task-log-manager.js +743 -0
- package/dist/logging/task-log-manager.js.map +1 -0
- package/dist/mediation/fail-closed-runner.d.ts +131 -0
- package/dist/mediation/fail-closed-runner.d.ts.map +1 -0
- package/dist/mediation/fail-closed-runner.js +245 -0
- package/dist/mediation/fail-closed-runner.js.map +1 -0
- package/dist/mediation/llm-client-with-evidence.d.ts +123 -0
- package/dist/mediation/llm-client-with-evidence.d.ts.map +1 -0
- package/dist/mediation/llm-client-with-evidence.js +245 -0
- package/dist/mediation/llm-client-with-evidence.js.map +1 -0
- package/dist/mediation/llm-client.d.ts +102 -0
- package/dist/mediation/llm-client.d.ts.map +1 -0
- package/dist/mediation/llm-client.js +206 -0
- package/dist/mediation/llm-client.js.map +1 -0
- package/dist/mediation/llm-evidence-manager.d.ts +108 -0
- package/dist/mediation/llm-evidence-manager.d.ts.map +1 -0
- package/dist/mediation/llm-evidence-manager.js +230 -0
- package/dist/mediation/llm-evidence-manager.js.map +1 -0
- package/dist/mediation/llm-mediation-layer.d.ts +175 -0
- package/dist/mediation/llm-mediation-layer.d.ts.map +1 -0
- package/dist/mediation/llm-mediation-layer.js +315 -0
- package/dist/mediation/llm-mediation-layer.js.map +1 -0
- package/dist/mediation/llm-sentinel.d.ts +107 -0
- package/dist/mediation/llm-sentinel.d.ts.map +1 -0
- package/dist/mediation/llm-sentinel.js +187 -0
- package/dist/mediation/llm-sentinel.js.map +1 -0
- package/dist/mediation/real-llm-mediation-layer.d.ts +104 -0
- package/dist/mediation/real-llm-mediation-layer.d.ts.map +1 -0
- package/dist/mediation/real-llm-mediation-layer.js +322 -0
- package/dist/mediation/real-llm-mediation-layer.js.map +1 -0
- package/dist/mediation/verdict-reporter.d.ts +61 -0
- package/dist/mediation/verdict-reporter.d.ts.map +1 -0
- package/dist/mediation/verdict-reporter.js +178 -0
- package/dist/mediation/verdict-reporter.js.map +1 -0
- package/dist/models/enums.d.ts +133 -0
- package/dist/models/enums.d.ts.map +1 -0
- package/dist/models/enums.js +201 -0
- package/dist/models/enums.js.map +1 -0
- package/dist/models/evidence.d.ts +60 -0
- package/dist/models/evidence.d.ts.map +1 -0
- package/dist/models/evidence.js +135 -0
- package/dist/models/evidence.js.map +1 -0
- package/dist/models/execution-result.d.ts +89 -0
- package/dist/models/execution-result.d.ts.map +1 -0
- package/dist/models/execution-result.js +197 -0
- package/dist/models/execution-result.js.map +1 -0
- package/dist/models/file-lock.d.ts +62 -0
- package/dist/models/file-lock.d.ts.map +1 -0
- package/dist/models/file-lock.js +133 -0
- package/dist/models/file-lock.js.map +1 -0
- package/dist/models/index.d.ts +12 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/models/index.js +91 -0
- package/dist/models/index.js.map +1 -0
- package/dist/models/repl/index.d.ts +7 -0
- package/dist/models/repl/index.d.ts.map +1 -0
- package/dist/models/repl/index.js +32 -0
- package/dist/models/repl/index.js.map +1 -0
- package/dist/models/repl/model-registry.d.ts +73 -0
- package/dist/models/repl/model-registry.d.ts.map +1 -0
- package/dist/models/repl/model-registry.js +116 -0
- package/dist/models/repl/model-registry.js.map +1 -0
- package/dist/models/repl/repl-state.d.ts +86 -0
- package/dist/models/repl/repl-state.d.ts.map +1 -0
- package/dist/models/repl/repl-state.js +152 -0
- package/dist/models/repl/repl-state.js.map +1 -0
- package/dist/models/repl/task-log.d.ts +247 -0
- package/dist/models/repl/task-log.d.ts.map +1 -0
- package/dist/models/repl/task-log.js +178 -0
- package/dist/models/repl/task-log.js.map +1 -0
- package/dist/models/session.d.ts +71 -0
- package/dist/models/session.d.ts.map +1 -0
- package/dist/models/session.js +140 -0
- package/dist/models/session.js.map +1 -0
- package/dist/models/supporting.d.ts +97 -0
- package/dist/models/supporting.d.ts.map +1 -0
- package/dist/models/supporting.js +208 -0
- package/dist/models/supporting.js.map +1 -0
- package/dist/models/task.d.ts +77 -0
- package/dist/models/task.d.ts.map +1 -0
- package/dist/models/task.js +170 -0
- package/dist/models/task.js.map +1 -0
- package/dist/output/output-control-manager.d.ts +217 -0
- package/dist/output/output-control-manager.d.ts.map +1 -0
- package/dist/output/output-control-manager.js +378 -0
- package/dist/output/output-control-manager.js.map +1 -0
- package/dist/pool/agent-pool.d.ts +284 -0
- package/dist/pool/agent-pool.d.ts.map +1 -0
- package/dist/pool/agent-pool.js +451 -0
- package/dist/pool/agent-pool.js.map +1 -0
- package/dist/repl/commands/index.d.ts +12 -0
- package/dist/repl/commands/index.d.ts.map +1 -0
- package/dist/repl/commands/index.js +26 -0
- package/dist/repl/commands/index.js.map +1 -0
- package/dist/repl/commands/init.d.ts +31 -0
- package/dist/repl/commands/init.d.ts.map +1 -0
- package/dist/repl/commands/init.js +234 -0
- package/dist/repl/commands/init.js.map +1 -0
- package/dist/repl/commands/keys.d.ts +63 -0
- package/dist/repl/commands/keys.d.ts.map +1 -0
- package/dist/repl/commands/keys.js +114 -0
- package/dist/repl/commands/keys.js.map +1 -0
- package/dist/repl/commands/logs.d.ts +91 -0
- package/dist/repl/commands/logs.d.ts.map +1 -0
- package/dist/repl/commands/logs.js +200 -0
- package/dist/repl/commands/logs.js.map +1 -0
- package/dist/repl/commands/model.d.ts +85 -0
- package/dist/repl/commands/model.d.ts.map +1 -0
- package/dist/repl/commands/model.js +225 -0
- package/dist/repl/commands/model.js.map +1 -0
- package/dist/repl/commands/models.d.ts +50 -0
- package/dist/repl/commands/models.d.ts.map +1 -0
- package/dist/repl/commands/models.js +180 -0
- package/dist/repl/commands/models.js.map +1 -0
- package/dist/repl/commands/provider.d.ts +79 -0
- package/dist/repl/commands/provider.d.ts.map +1 -0
- package/dist/repl/commands/provider.js +291 -0
- package/dist/repl/commands/provider.js.map +1 -0
- package/dist/repl/commands/session.d.ts +50 -0
- package/dist/repl/commands/session.d.ts.map +1 -0
- package/dist/repl/commands/session.js +152 -0
- package/dist/repl/commands/session.js.map +1 -0
- package/dist/repl/commands/status.d.ts +55 -0
- package/dist/repl/commands/status.d.ts.map +1 -0
- package/dist/repl/commands/status.js +182 -0
- package/dist/repl/commands/status.js.map +1 -0
- package/dist/repl/index.d.ts +6 -0
- package/dist/repl/index.d.ts.map +1 -0
- package/dist/repl/index.js +25 -0
- package/dist/repl/index.js.map +1 -0
- package/dist/repl/repl-interface.d.ts +371 -0
- package/dist/repl/repl-interface.d.ts.map +1 -0
- package/dist/repl/repl-interface.js +1214 -0
- package/dist/repl/repl-interface.js.map +1 -0
- package/dist/session/session-manager.d.ts +85 -0
- package/dist/session/session-manager.d.ts.map +1 -0
- package/dist/session/session-manager.js +217 -0
- package/dist/session/session-manager.js.map +1 -0
- package/dist/supervisor/executor-supervisor.d.ts +90 -0
- package/dist/supervisor/executor-supervisor.d.ts.map +1 -0
- package/dist/supervisor/executor-supervisor.js +223 -0
- package/dist/supervisor/executor-supervisor.js.map +1 -0
- package/dist/supervisor/index.d.ts +5 -0
- package/dist/supervisor/index.d.ts.map +1 -0
- package/dist/supervisor/index.js +9 -0
- package/dist/supervisor/index.js.map +1 -0
- package/package.json +57 -0
|
@@ -0,0 +1,743 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Task Log Manager
|
|
4
|
+
*
|
|
5
|
+
* Per spec 13_LOGGING_AND_OBSERVABILITY.md:
|
|
6
|
+
* - Logs stored in .claude/logs/sessions/<session_id>/
|
|
7
|
+
* - Two-layer viewing (list and detail)
|
|
8
|
+
* - Visibility control (summary/full)
|
|
9
|
+
* - Thread/Run/Task hierarchy support (v2.0)
|
|
10
|
+
*/
|
|
11
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
14
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
15
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
16
|
+
}
|
|
17
|
+
Object.defineProperty(o, k2, desc);
|
|
18
|
+
}) : (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
o[k2] = m[k];
|
|
21
|
+
}));
|
|
22
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
23
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
24
|
+
}) : function(o, v) {
|
|
25
|
+
o["default"] = v;
|
|
26
|
+
});
|
|
27
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
28
|
+
var ownKeys = function(o) {
|
|
29
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
30
|
+
var ar = [];
|
|
31
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
32
|
+
return ar;
|
|
33
|
+
};
|
|
34
|
+
return ownKeys(o);
|
|
35
|
+
};
|
|
36
|
+
return function (mod) {
|
|
37
|
+
if (mod && mod.__esModule) return mod;
|
|
38
|
+
var result = {};
|
|
39
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
40
|
+
__setModuleDefault(result, mod);
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
})();
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.SESSION_FILE = exports.SESSIONS_DIR = exports.RAW_DIR = exports.TASKS_DIR = exports.INDEX_FILE = exports.LOG_DIR = exports.TaskLogManager = void 0;
|
|
46
|
+
const fs = __importStar(require("fs"));
|
|
47
|
+
const path = __importStar(require("path"));
|
|
48
|
+
const task_log_1 = require("../models/repl/task-log");
|
|
49
|
+
const sensitive_data_masker_1 = require("./sensitive-data-masker");
|
|
50
|
+
/**
|
|
51
|
+
* Log directory structure
|
|
52
|
+
* Per spec 13_LOGGING_AND_OBSERVABILITY.md Section 1.1
|
|
53
|
+
*/
|
|
54
|
+
const LOG_DIR = 'logs';
|
|
55
|
+
exports.LOG_DIR = LOG_DIR;
|
|
56
|
+
const SESSIONS_DIR = 'sessions';
|
|
57
|
+
exports.SESSIONS_DIR = SESSIONS_DIR;
|
|
58
|
+
const TASKS_DIR = 'tasks';
|
|
59
|
+
exports.TASKS_DIR = TASKS_DIR;
|
|
60
|
+
const RAW_DIR = 'raw';
|
|
61
|
+
exports.RAW_DIR = RAW_DIR;
|
|
62
|
+
const INDEX_FILE = 'index.json';
|
|
63
|
+
exports.INDEX_FILE = INDEX_FILE;
|
|
64
|
+
const SESSION_FILE = 'session.json';
|
|
65
|
+
exports.SESSION_FILE = SESSION_FILE;
|
|
66
|
+
/**
|
|
67
|
+
* Task Log Manager class
|
|
68
|
+
* Supports Thread/Run/Task hierarchy (v2.0)
|
|
69
|
+
*/
|
|
70
|
+
class TaskLogManager {
|
|
71
|
+
projectPath;
|
|
72
|
+
logsPath;
|
|
73
|
+
// Session-scoped counters for sequential IDs
|
|
74
|
+
threadCounters = new Map();
|
|
75
|
+
runCounters = new Map();
|
|
76
|
+
taskCounters = new Map();
|
|
77
|
+
constructor(projectPath) {
|
|
78
|
+
this.projectPath = projectPath;
|
|
79
|
+
this.logsPath = path.join(projectPath, '.claude', LOG_DIR);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Get session directory path
|
|
83
|
+
*/
|
|
84
|
+
getSessionPath(sessionId) {
|
|
85
|
+
return path.join(this.logsPath, SESSIONS_DIR, sessionId);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Get session tasks directory path
|
|
89
|
+
*/
|
|
90
|
+
getSessionTasksPath(sessionId) {
|
|
91
|
+
return path.join(this.getSessionPath(sessionId), TASKS_DIR);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Ensure log directories exist (legacy support)
|
|
95
|
+
* Per spec 13_LOGGING_AND_OBSERVABILITY.md Section 1.3
|
|
96
|
+
*/
|
|
97
|
+
async ensureLogDirectories() {
|
|
98
|
+
const dirs = [
|
|
99
|
+
this.logsPath,
|
|
100
|
+
path.join(this.logsPath, SESSIONS_DIR),
|
|
101
|
+
];
|
|
102
|
+
for (const dir of dirs) {
|
|
103
|
+
if (!fs.existsSync(dir)) {
|
|
104
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Ensure session-based directories exist
|
|
110
|
+
* Per spec 13_LOGGING_AND_OBSERVABILITY.md Section 1.1
|
|
111
|
+
*/
|
|
112
|
+
async ensureSessionDirectories(sessionId) {
|
|
113
|
+
await this.ensureLogDirectories();
|
|
114
|
+
const sessionPath = this.getSessionPath(sessionId);
|
|
115
|
+
const tasksPath = this.getSessionTasksPath(sessionId);
|
|
116
|
+
const rawPath = path.join(sessionPath, RAW_DIR);
|
|
117
|
+
const dirs = [sessionPath, tasksPath, rawPath];
|
|
118
|
+
for (const dir of dirs) {
|
|
119
|
+
if (!fs.existsSync(dir)) {
|
|
120
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Initialize a new session
|
|
126
|
+
* Per spec 13_LOGGING_AND_OBSERVABILITY.md Section 1.2
|
|
127
|
+
*/
|
|
128
|
+
async initializeSession(sessionId) {
|
|
129
|
+
await this.ensureSessionDirectories(sessionId);
|
|
130
|
+
// Initialize counters for this session
|
|
131
|
+
this.threadCounters.set(sessionId, 0);
|
|
132
|
+
this.runCounters.set(sessionId, 0);
|
|
133
|
+
this.taskCounters.set(sessionId, 0);
|
|
134
|
+
// Create session metadata
|
|
135
|
+
const sessionMeta = (0, task_log_1.createSessionMetadata)(sessionId);
|
|
136
|
+
const sessionMetaPath = path.join(this.getSessionPath(sessionId), SESSION_FILE);
|
|
137
|
+
fs.writeFileSync(sessionMetaPath, JSON.stringify(sessionMeta, null, 2), 'utf-8');
|
|
138
|
+
// Create session index
|
|
139
|
+
const sessionIndex = (0, task_log_1.createTaskLogIndex)(sessionId);
|
|
140
|
+
const sessionIndexPath = path.join(this.getSessionPath(sessionId), INDEX_FILE);
|
|
141
|
+
fs.writeFileSync(sessionIndexPath, JSON.stringify(sessionIndex, null, 2), 'utf-8');
|
|
142
|
+
// Update global index
|
|
143
|
+
await this.updateGlobalIndex(sessionId);
|
|
144
|
+
return sessionMeta;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Update global index with new session
|
|
148
|
+
*/
|
|
149
|
+
async updateGlobalIndex(sessionId) {
|
|
150
|
+
const globalIndexPath = path.join(this.logsPath, INDEX_FILE);
|
|
151
|
+
let globalIndex;
|
|
152
|
+
if (fs.existsSync(globalIndexPath)) {
|
|
153
|
+
try {
|
|
154
|
+
const content = fs.readFileSync(globalIndexPath, 'utf-8');
|
|
155
|
+
globalIndex = JSON.parse(content);
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
globalIndex = (0, task_log_1.createGlobalLogIndex)();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
globalIndex = (0, task_log_1.createGlobalLogIndex)();
|
|
163
|
+
}
|
|
164
|
+
// Add session if not already present
|
|
165
|
+
if (!globalIndex.sessions.some(s => s.session_id === sessionId)) {
|
|
166
|
+
globalIndex.sessions.push({
|
|
167
|
+
session_id: sessionId,
|
|
168
|
+
started_at: new Date().toISOString(),
|
|
169
|
+
task_count: 0,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
globalIndex.updated_at = new Date().toISOString();
|
|
173
|
+
fs.writeFileSync(globalIndexPath, JSON.stringify(globalIndex, null, 2), 'utf-8');
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Get session metadata
|
|
177
|
+
*/
|
|
178
|
+
async getSessionMetadata(sessionId) {
|
|
179
|
+
const sessionMetaPath = path.join(this.getSessionPath(sessionId), SESSION_FILE);
|
|
180
|
+
if (!fs.existsSync(sessionMetaPath)) {
|
|
181
|
+
return (0, task_log_1.createSessionMetadata)(sessionId);
|
|
182
|
+
}
|
|
183
|
+
try {
|
|
184
|
+
const content = fs.readFileSync(sessionMetaPath, 'utf-8');
|
|
185
|
+
return JSON.parse(content);
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
return (0, task_log_1.createSessionMetadata)(sessionId);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Save session metadata
|
|
193
|
+
*/
|
|
194
|
+
async saveSessionMetadata(sessionId, metadata) {
|
|
195
|
+
const sessionMetaPath = path.join(this.getSessionPath(sessionId), SESSION_FILE);
|
|
196
|
+
fs.writeFileSync(sessionMetaPath, JSON.stringify(metadata, null, 2), 'utf-8');
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Get session index
|
|
200
|
+
*/
|
|
201
|
+
async getSessionIndex(sessionId) {
|
|
202
|
+
const sessionIndexPath = path.join(this.getSessionPath(sessionId), INDEX_FILE);
|
|
203
|
+
if (!fs.existsSync(sessionIndexPath)) {
|
|
204
|
+
return (0, task_log_1.createTaskLogIndex)(sessionId);
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
const content = fs.readFileSync(sessionIndexPath, 'utf-8');
|
|
208
|
+
return JSON.parse(content);
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
// Per spec: On corruption, return empty entries
|
|
212
|
+
return (0, task_log_1.createTaskLogIndex)(sessionId);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Save session index
|
|
217
|
+
*/
|
|
218
|
+
async saveSessionIndex(sessionId, index) {
|
|
219
|
+
const sessionIndexPath = path.join(this.getSessionPath(sessionId), INDEX_FILE);
|
|
220
|
+
index.updated_at = new Date().toISOString();
|
|
221
|
+
fs.writeFileSync(sessionIndexPath, JSON.stringify((0, sensitive_data_masker_1.maskSensitiveObject)(index), null, 2), 'utf-8');
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Generate next thread ID for session
|
|
225
|
+
*/
|
|
226
|
+
generateThreadId(sessionId) {
|
|
227
|
+
const current = this.threadCounters.get(sessionId) ?? 0;
|
|
228
|
+
const next = current + 1;
|
|
229
|
+
this.threadCounters.set(sessionId, next);
|
|
230
|
+
return 'thr-' + String(next).padStart(3, '0');
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Generate next run ID for session
|
|
234
|
+
*/
|
|
235
|
+
generateRunId(sessionId) {
|
|
236
|
+
const current = this.runCounters.get(sessionId) ?? 0;
|
|
237
|
+
const next = current + 1;
|
|
238
|
+
this.runCounters.set(sessionId, next);
|
|
239
|
+
return 'run-' + String(next).padStart(3, '0');
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Generate next task ID for session
|
|
243
|
+
*/
|
|
244
|
+
generateTaskId(sessionId) {
|
|
245
|
+
const current = this.taskCounters.get(sessionId) ?? 0;
|
|
246
|
+
const next = current + 1;
|
|
247
|
+
this.taskCounters.set(sessionId, next);
|
|
248
|
+
return 'task-' + String(next).padStart(3, '0');
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Create a new thread
|
|
252
|
+
* Per spec 05_DATA_MODELS.md Section "Thread"
|
|
253
|
+
*/
|
|
254
|
+
async createThread(sessionId, threadType, description) {
|
|
255
|
+
const threadId = this.generateThreadId(sessionId);
|
|
256
|
+
const thread = (0, task_log_1.createThread)(threadId, sessionId, threadType, description);
|
|
257
|
+
// Add to session metadata
|
|
258
|
+
const metadata = await this.getSessionMetadata(sessionId);
|
|
259
|
+
metadata.threads.push({ thread_id: threadId, thread_type: threadType });
|
|
260
|
+
await this.saveSessionMetadata(sessionId, metadata);
|
|
261
|
+
return thread;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Create a new run
|
|
265
|
+
* Per spec 05_DATA_MODELS.md Section "Run"
|
|
266
|
+
*/
|
|
267
|
+
async createRun(sessionId, threadId, trigger) {
|
|
268
|
+
const runId = this.generateRunId(sessionId);
|
|
269
|
+
const run = (0, task_log_1.createRun)(runId, threadId, sessionId, trigger);
|
|
270
|
+
// Add to session metadata
|
|
271
|
+
const metadata = await this.getSessionMetadata(sessionId);
|
|
272
|
+
metadata.runs.push({ run_id: runId, thread_id: threadId, status: 'RUNNING' });
|
|
273
|
+
await this.saveSessionMetadata(sessionId, metadata);
|
|
274
|
+
return run;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Get a run by ID
|
|
278
|
+
*/
|
|
279
|
+
async getRun(sessionId, runId) {
|
|
280
|
+
const metadata = await this.getSessionMetadata(sessionId);
|
|
281
|
+
const runInfo = metadata.runs.find(r => r.run_id === runId);
|
|
282
|
+
if (!runInfo) {
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
// Reconstruct run from metadata (simplified)
|
|
286
|
+
return {
|
|
287
|
+
run_id: runId,
|
|
288
|
+
thread_id: runInfo.thread_id,
|
|
289
|
+
session_id: sessionId,
|
|
290
|
+
started_at: metadata.started_at,
|
|
291
|
+
completed_at: runInfo.status !== 'RUNNING' ? new Date().toISOString() : null,
|
|
292
|
+
status: runInfo.status,
|
|
293
|
+
trigger: 'USER_INPUT', // Default, actual trigger not stored in minimal metadata
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Complete a run
|
|
298
|
+
*/
|
|
299
|
+
async completeRun(sessionId, runId, status) {
|
|
300
|
+
const metadata = await this.getSessionMetadata(sessionId);
|
|
301
|
+
const runInfo = metadata.runs.find(r => r.run_id === runId);
|
|
302
|
+
if (runInfo) {
|
|
303
|
+
runInfo.status = status;
|
|
304
|
+
await this.saveSessionMetadata(sessionId, metadata);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Complete a task with session context
|
|
309
|
+
* Per spec 06_CORRECTNESS_PROPERTIES.md Property 26: Fail-Closed Logging
|
|
310
|
+
* TaskLog MUST be saved for ALL terminal states (COMPLETE, INCOMPLETE, ERROR)
|
|
311
|
+
* Per spec 10_REPL_UX.md Section 10: Records executor blocking info (Property 34-36)
|
|
312
|
+
*/
|
|
313
|
+
async completeTaskWithSession(taskId, sessionId, status, filesModified = [], evidenceRef, errorMessage, options) {
|
|
314
|
+
const log = await this.getTaskLogWithSession(taskId, sessionId);
|
|
315
|
+
if (!log) {
|
|
316
|
+
throw new Error('Task log not found: ' + taskId);
|
|
317
|
+
}
|
|
318
|
+
// Per spec 10_REPL_UX.md Section 10: Record executor blocking info (Property 34-36)
|
|
319
|
+
// Update TaskLog with executor_blocked fields if provided
|
|
320
|
+
if (options?.executorBlocked !== undefined) {
|
|
321
|
+
log.executor_blocked = options.executorBlocked;
|
|
322
|
+
log.blocked_reason = options.blockedReason;
|
|
323
|
+
log.timeout_ms = options.timeoutMs;
|
|
324
|
+
log.terminated_by = options.terminatedBy;
|
|
325
|
+
await this.saveTaskLogWithSession(log, sessionId);
|
|
326
|
+
}
|
|
327
|
+
// Add completion event
|
|
328
|
+
const eventType = status === 'ERROR' ? 'TASK_ERROR' : 'TASK_COMPLETED';
|
|
329
|
+
await this.addEventWithSession(taskId, sessionId, eventType, {
|
|
330
|
+
status,
|
|
331
|
+
files_modified: filesModified,
|
|
332
|
+
evidence_ref: evidenceRef,
|
|
333
|
+
error_message: errorMessage,
|
|
334
|
+
});
|
|
335
|
+
// Update session index entry
|
|
336
|
+
const index = await this.getSessionIndex(sessionId);
|
|
337
|
+
const entry = index.entries.find(e => e.task_id === taskId);
|
|
338
|
+
if (entry) {
|
|
339
|
+
entry.status = status;
|
|
340
|
+
entry.completed_at = new Date().toISOString();
|
|
341
|
+
entry.duration_ms = new Date(entry.completed_at).getTime() - new Date(entry.started_at).getTime();
|
|
342
|
+
entry.files_modified_count = filesModified.length;
|
|
343
|
+
// Per spec 10_REPL_UX.md Section 10: Record executor blocking info in index (Property 34-36)
|
|
344
|
+
if (options?.executorBlocked !== undefined) {
|
|
345
|
+
entry.executor_blocked = options.executorBlocked;
|
|
346
|
+
entry.blocked_reason = options.blockedReason;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
await this.saveSessionIndex(sessionId, index);
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Create a task with thread/run context
|
|
353
|
+
* Per spec 13_LOGGING_AND_OBSERVABILITY.md Section 2.3
|
|
354
|
+
*/
|
|
355
|
+
async createTaskWithContext(sessionId, threadId, runId, parentTaskId) {
|
|
356
|
+
await this.ensureSessionDirectories(sessionId);
|
|
357
|
+
// Validate parent task is in same thread if specified
|
|
358
|
+
if (parentTaskId) {
|
|
359
|
+
const parentLog = await this.getTaskLogWithSession(parentTaskId, sessionId);
|
|
360
|
+
if (parentLog && parentLog.thread_id !== threadId) {
|
|
361
|
+
throw new Error('parent_task_id must be within same thread');
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
const taskId = this.generateTaskId(sessionId);
|
|
365
|
+
const log = (0, task_log_1.createTaskLog)(taskId, sessionId, threadId, runId, parentTaskId ?? null);
|
|
366
|
+
await this.saveTaskLogWithSession(log, sessionId);
|
|
367
|
+
// Update session index
|
|
368
|
+
const index = await this.getSessionIndex(sessionId);
|
|
369
|
+
const entry = {
|
|
370
|
+
task_id: taskId,
|
|
371
|
+
thread_id: threadId,
|
|
372
|
+
run_id: runId,
|
|
373
|
+
parent_task_id: parentTaskId ?? null,
|
|
374
|
+
status: 'RUNNING',
|
|
375
|
+
started_at: log.created_at,
|
|
376
|
+
completed_at: null,
|
|
377
|
+
duration_ms: 0,
|
|
378
|
+
files_modified_count: 0,
|
|
379
|
+
tests_run_count: 0,
|
|
380
|
+
log_file: path.join(TASKS_DIR, taskId + '.json'),
|
|
381
|
+
};
|
|
382
|
+
index.entries.push(entry);
|
|
383
|
+
await this.saveSessionIndex(sessionId, index);
|
|
384
|
+
// Update global index task count
|
|
385
|
+
await this.incrementGlobalTaskCount(sessionId);
|
|
386
|
+
return log;
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Increment task count in global index
|
|
390
|
+
*/
|
|
391
|
+
async incrementGlobalTaskCount(sessionId) {
|
|
392
|
+
const globalIndexPath = path.join(this.logsPath, INDEX_FILE);
|
|
393
|
+
if (fs.existsSync(globalIndexPath)) {
|
|
394
|
+
try {
|
|
395
|
+
const content = fs.readFileSync(globalIndexPath, 'utf-8');
|
|
396
|
+
const globalIndex = JSON.parse(content);
|
|
397
|
+
const session = globalIndex.sessions.find(s => s.session_id === sessionId);
|
|
398
|
+
if (session) {
|
|
399
|
+
session.task_count++;
|
|
400
|
+
globalIndex.updated_at = new Date().toISOString();
|
|
401
|
+
fs.writeFileSync(globalIndexPath, JSON.stringify(globalIndex, null, 2), 'utf-8');
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
catch {
|
|
405
|
+
// Ignore errors in global index update
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Get task log with session context
|
|
411
|
+
*/
|
|
412
|
+
async getTaskLogWithSession(taskId, sessionId) {
|
|
413
|
+
const logPath = path.join(this.getSessionTasksPath(sessionId), taskId + '.json');
|
|
414
|
+
if (!fs.existsSync(logPath)) {
|
|
415
|
+
return null;
|
|
416
|
+
}
|
|
417
|
+
try {
|
|
418
|
+
const content = fs.readFileSync(logPath, 'utf-8');
|
|
419
|
+
return JSON.parse(content);
|
|
420
|
+
}
|
|
421
|
+
catch {
|
|
422
|
+
return null;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Save task log with session context
|
|
427
|
+
*/
|
|
428
|
+
async saveTaskLogWithSession(log, sessionId) {
|
|
429
|
+
await this.ensureSessionDirectories(sessionId);
|
|
430
|
+
const logPath = path.join(this.getSessionTasksPath(sessionId), log.task_id + '.json');
|
|
431
|
+
// Mask sensitive data before saving
|
|
432
|
+
const maskedLog = (0, sensitive_data_masker_1.maskSensitiveObject)(log);
|
|
433
|
+
fs.writeFileSync(logPath, JSON.stringify(maskedLog, null, 2), 'utf-8');
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Add event to task log with session context
|
|
437
|
+
*/
|
|
438
|
+
async addEventWithSession(taskId, sessionId, eventType, content, metadata) {
|
|
439
|
+
const log = await this.getTaskLogWithSession(taskId, sessionId);
|
|
440
|
+
if (!log) {
|
|
441
|
+
throw new Error('Task log not found: ' + taskId);
|
|
442
|
+
}
|
|
443
|
+
const eventId = 'evt-' + String(log.events.length + 1).padStart(3, '0');
|
|
444
|
+
const event = (0, task_log_1.createLogEvent)(eventId, eventType, content, metadata);
|
|
445
|
+
const updatedLog = (0, task_log_1.addEventToTaskLog)(log, event);
|
|
446
|
+
await this.saveTaskLogWithSession(updatedLog, sessionId);
|
|
447
|
+
return event;
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Get task detail with session context
|
|
451
|
+
* Per spec 13_LOGGING_AND_OBSERVABILITY.md Section 2.2
|
|
452
|
+
*/
|
|
453
|
+
async getTaskDetailWithSession(taskId, sessionId, visibility = 'summary') {
|
|
454
|
+
const log = await this.getTaskLogWithSession(taskId, sessionId);
|
|
455
|
+
if (!log) {
|
|
456
|
+
return { log: null, events: [] };
|
|
457
|
+
}
|
|
458
|
+
const events = (0, task_log_1.filterEventsByVisibility)(log.events, visibility);
|
|
459
|
+
// Mask any remaining sensitive data in display
|
|
460
|
+
const maskedEvents = events.map(e => ({
|
|
461
|
+
...e,
|
|
462
|
+
content: (0, sensitive_data_masker_1.maskSensitiveObject)(e.content),
|
|
463
|
+
}));
|
|
464
|
+
return { log, events: maskedEvents };
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Format tree view of session hierarchy
|
|
468
|
+
* Per spec 13_LOGGING_AND_OBSERVABILITY.md Section 2.5
|
|
469
|
+
*/
|
|
470
|
+
async formatTreeView(sessionId) {
|
|
471
|
+
const metadata = await this.getSessionMetadata(sessionId);
|
|
472
|
+
const index = await this.getSessionIndex(sessionId);
|
|
473
|
+
let output = 'Session: ' + sessionId + '\n';
|
|
474
|
+
output += 'Started: ' + metadata.started_at + '\n\n';
|
|
475
|
+
// Group tasks by thread and run
|
|
476
|
+
for (const threadInfo of metadata.threads) {
|
|
477
|
+
output += ' Thread: ' + threadInfo.thread_id + ' (' + threadInfo.thread_type + ')\n';
|
|
478
|
+
// Find runs for this thread
|
|
479
|
+
const threadRuns = metadata.runs.filter(r => r.thread_id === threadInfo.thread_id);
|
|
480
|
+
for (const runInfo of threadRuns) {
|
|
481
|
+
output += ' Run: ' + runInfo.run_id + ' [' + runInfo.status + ']\n';
|
|
482
|
+
// Find tasks for this run
|
|
483
|
+
const runTasks = index.entries.filter(e => e.run_id === runInfo.run_id);
|
|
484
|
+
for (const task of runTasks) {
|
|
485
|
+
const indent = task.parent_task_id ? ' ' : ' ';
|
|
486
|
+
output += indent + 'Task: ' + task.task_id + ' [' + task.status + ']\n';
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return output;
|
|
491
|
+
}
|
|
492
|
+
// ========================================
|
|
493
|
+
// Legacy methods for backward compatibility
|
|
494
|
+
// ========================================
|
|
495
|
+
/**
|
|
496
|
+
* Get or create log index (legacy)
|
|
497
|
+
*/
|
|
498
|
+
async getOrCreateIndex(sessionId) {
|
|
499
|
+
await this.ensureLogDirectories();
|
|
500
|
+
// Fixed: Read session-specific index, not global index
|
|
501
|
+
const indexPath = path.join(this.getSessionPath(sessionId), INDEX_FILE);
|
|
502
|
+
if (fs.existsSync(indexPath)) {
|
|
503
|
+
try {
|
|
504
|
+
const content = fs.readFileSync(indexPath, 'utf-8');
|
|
505
|
+
return JSON.parse(content);
|
|
506
|
+
}
|
|
507
|
+
catch {
|
|
508
|
+
// Per spec: On corruption, return empty list
|
|
509
|
+
return (0, task_log_1.createTaskLogIndex)(sessionId);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
return (0, task_log_1.createTaskLogIndex)(sessionId);
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Save log index (legacy)
|
|
516
|
+
*/
|
|
517
|
+
async saveIndex(index) {
|
|
518
|
+
await this.ensureLogDirectories();
|
|
519
|
+
// Fixed: Save to session-specific index, not global index
|
|
520
|
+
const indexPath = path.join(this.getSessionPath(index.session_id), INDEX_FILE);
|
|
521
|
+
// Mask any sensitive data before saving
|
|
522
|
+
const maskedIndex = (0, sensitive_data_masker_1.maskSensitiveObject)(index);
|
|
523
|
+
maskedIndex.updated_at = new Date().toISOString();
|
|
524
|
+
fs.writeFileSync(indexPath, JSON.stringify(maskedIndex, null, 2), 'utf-8');
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Create a new task log (legacy - without thread/run context)
|
|
528
|
+
*/
|
|
529
|
+
async createTask(taskId, sessionId) {
|
|
530
|
+
await this.ensureLogDirectories();
|
|
531
|
+
const log = (0, task_log_1.createTaskLog)(taskId, sessionId);
|
|
532
|
+
await this.saveTaskLog(log);
|
|
533
|
+
// Update index
|
|
534
|
+
const index = await this.getOrCreateIndex(sessionId);
|
|
535
|
+
const entry = {
|
|
536
|
+
task_id: taskId,
|
|
537
|
+
thread_id: '',
|
|
538
|
+
run_id: '',
|
|
539
|
+
parent_task_id: null,
|
|
540
|
+
status: 'RUNNING',
|
|
541
|
+
started_at: log.created_at,
|
|
542
|
+
completed_at: null,
|
|
543
|
+
duration_ms: 0,
|
|
544
|
+
files_modified_count: 0,
|
|
545
|
+
tests_run_count: 0,
|
|
546
|
+
log_file: path.join(TASKS_DIR, taskId + '.json'),
|
|
547
|
+
};
|
|
548
|
+
index.entries.push(entry);
|
|
549
|
+
await this.saveIndex(index);
|
|
550
|
+
return log;
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Get task log by ID (legacy)
|
|
554
|
+
*/
|
|
555
|
+
async getTaskLog(taskId) {
|
|
556
|
+
const logPath = path.join(this.logsPath, TASKS_DIR, taskId + '.json');
|
|
557
|
+
if (!fs.existsSync(logPath)) {
|
|
558
|
+
return null;
|
|
559
|
+
}
|
|
560
|
+
try {
|
|
561
|
+
const content = fs.readFileSync(logPath, 'utf-8');
|
|
562
|
+
return JSON.parse(content);
|
|
563
|
+
}
|
|
564
|
+
catch {
|
|
565
|
+
return null;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* Save task log (legacy)
|
|
570
|
+
*/
|
|
571
|
+
async saveTaskLog(log) {
|
|
572
|
+
await this.ensureLogDirectories();
|
|
573
|
+
const tasksDir = path.join(this.logsPath, TASKS_DIR);
|
|
574
|
+
if (!fs.existsSync(tasksDir)) {
|
|
575
|
+
fs.mkdirSync(tasksDir, { recursive: true });
|
|
576
|
+
}
|
|
577
|
+
const logPath = path.join(tasksDir, log.task_id + '.json');
|
|
578
|
+
// Mask sensitive data before saving
|
|
579
|
+
const maskedLog = (0, sensitive_data_masker_1.maskSensitiveObject)(log);
|
|
580
|
+
fs.writeFileSync(logPath, JSON.stringify(maskedLog, null, 2), 'utf-8');
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Add event to task log (legacy)
|
|
584
|
+
*/
|
|
585
|
+
async addEvent(taskId, eventType, content, metadata) {
|
|
586
|
+
const log = await this.getTaskLog(taskId);
|
|
587
|
+
if (!log) {
|
|
588
|
+
throw new Error('Task log not found: ' + taskId);
|
|
589
|
+
}
|
|
590
|
+
const eventId = 'evt-' + String(log.events.length + 1).padStart(3, '0');
|
|
591
|
+
const event = (0, task_log_1.createLogEvent)(eventId, eventType, content, metadata);
|
|
592
|
+
const updatedLog = (0, task_log_1.addEventToTaskLog)(log, event);
|
|
593
|
+
await this.saveTaskLog(updatedLog);
|
|
594
|
+
return event;
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Complete a task (legacy)
|
|
598
|
+
*/
|
|
599
|
+
async completeTask(taskId, status, filesModified = [], evidenceRef, errorMessage) {
|
|
600
|
+
const log = await this.getTaskLog(taskId);
|
|
601
|
+
if (!log) {
|
|
602
|
+
throw new Error('Task log not found: ' + taskId);
|
|
603
|
+
}
|
|
604
|
+
// Add completion event
|
|
605
|
+
await this.addEvent(taskId, status === 'ERROR' ? 'TASK_ERROR' : 'TASK_COMPLETED', {
|
|
606
|
+
status,
|
|
607
|
+
files_modified: filesModified,
|
|
608
|
+
evidence_ref: evidenceRef,
|
|
609
|
+
error_message: errorMessage,
|
|
610
|
+
});
|
|
611
|
+
// Update index entry
|
|
612
|
+
const index = await this.getOrCreateIndex(log.session_id);
|
|
613
|
+
const entry = index.entries.find(e => e.task_id === taskId);
|
|
614
|
+
if (entry) {
|
|
615
|
+
entry.status = status;
|
|
616
|
+
entry.completed_at = new Date().toISOString();
|
|
617
|
+
entry.duration_ms = new Date(entry.completed_at).getTime() - new Date(entry.started_at).getTime();
|
|
618
|
+
entry.files_modified_count = filesModified.length;
|
|
619
|
+
}
|
|
620
|
+
await this.saveIndex(index);
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* Get task list for display (Layer 1) (legacy)
|
|
624
|
+
* Per spec 13_LOGGING_AND_OBSERVABILITY.md Section 2.1
|
|
625
|
+
*/
|
|
626
|
+
async getTaskList(sessionId) {
|
|
627
|
+
const index = await this.getOrCreateIndex(sessionId);
|
|
628
|
+
return index.entries;
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
* Get task detail for display (Layer 2) (legacy)
|
|
632
|
+
* Per spec 13_LOGGING_AND_OBSERVABILITY.md Section 2.2
|
|
633
|
+
*/
|
|
634
|
+
async getTaskDetail(taskId, visibility = 'summary') {
|
|
635
|
+
const log = await this.getTaskLog(taskId);
|
|
636
|
+
if (!log) {
|
|
637
|
+
return { log: null, events: [] };
|
|
638
|
+
}
|
|
639
|
+
const events = (0, task_log_1.filterEventsByVisibility)(log.events, visibility);
|
|
640
|
+
// Mask any remaining sensitive data in display
|
|
641
|
+
const maskedEvents = events.map(e => ({
|
|
642
|
+
...e,
|
|
643
|
+
content: (0, sensitive_data_masker_1.maskSensitiveObject)(e.content),
|
|
644
|
+
}));
|
|
645
|
+
return { log, events: maskedEvents };
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Format task list for REPL display (legacy)
|
|
649
|
+
*/
|
|
650
|
+
formatTaskList(entries, sessionId) {
|
|
651
|
+
if (entries.length === 0) {
|
|
652
|
+
return 'No tasks logged for this session.';
|
|
653
|
+
}
|
|
654
|
+
let output = 'Task Logs (session: ' + sessionId + '):\n\n';
|
|
655
|
+
output += ' # | Task ID | Status | Duration | Files | Tests\n';
|
|
656
|
+
output += ' --|--------------|------------|----------|-------|------\n';
|
|
657
|
+
entries.forEach((entry, index) => {
|
|
658
|
+
const duration = entry.duration_ms > 0
|
|
659
|
+
? (entry.duration_ms / 1000).toFixed(1) + 's'
|
|
660
|
+
: '-';
|
|
661
|
+
output += ' ' + (index + 1) + ' | ' +
|
|
662
|
+
entry.task_id.padEnd(12) + ' | ' +
|
|
663
|
+
entry.status.padEnd(10) + ' | ' +
|
|
664
|
+
duration.padEnd(8) + ' | ' +
|
|
665
|
+
String(entry.files_modified_count).padEnd(5) + ' | ' +
|
|
666
|
+
entry.tests_run_count + '\n';
|
|
667
|
+
});
|
|
668
|
+
output += '\nUse /logs <task-id> to view details.\n';
|
|
669
|
+
output += 'Use /logs <task-id> --full for executor-level logs.';
|
|
670
|
+
return output;
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* Format task detail for REPL display (legacy)
|
|
674
|
+
*/
|
|
675
|
+
formatTaskDetail(taskId, log, events, isFull) {
|
|
676
|
+
let output = 'Task Log: ' + taskId;
|
|
677
|
+
if (isFull) {
|
|
678
|
+
output += ' (FULL)';
|
|
679
|
+
}
|
|
680
|
+
output += '\n\n';
|
|
681
|
+
for (const event of events) {
|
|
682
|
+
const time = new Date(event.timestamp).toLocaleTimeString();
|
|
683
|
+
output += '[' + time + '] ' + event.event_type + '\n';
|
|
684
|
+
// Format content based on event type
|
|
685
|
+
if (event.content.text) {
|
|
686
|
+
output += ' "' + (0, sensitive_data_masker_1.maskSensitiveData)(event.content.text) + '"\n';
|
|
687
|
+
}
|
|
688
|
+
if (event.content.question) {
|
|
689
|
+
output += ' "' + (0, sensitive_data_masker_1.maskSensitiveData)(event.content.question) + '"\n';
|
|
690
|
+
}
|
|
691
|
+
if (event.content.action) {
|
|
692
|
+
output += ' Action: ' + event.content.action + '\n';
|
|
693
|
+
}
|
|
694
|
+
if (event.content.target_file) {
|
|
695
|
+
output += ' Target: ' + event.content.target_file + '\n';
|
|
696
|
+
}
|
|
697
|
+
if (event.content.status) {
|
|
698
|
+
output += ' Status: ' + event.content.status + '\n';
|
|
699
|
+
}
|
|
700
|
+
if (event.content.files_modified && event.content.files_modified.length > 0) {
|
|
701
|
+
output += ' Files modified: ' + event.content.files_modified.join(', ') + '\n';
|
|
702
|
+
}
|
|
703
|
+
if (event.content.evidence_ref) {
|
|
704
|
+
output += ' Evidence: ' + event.content.evidence_ref + '\n';
|
|
705
|
+
}
|
|
706
|
+
if (event.content.error_message) {
|
|
707
|
+
output += ' Error: ' + (0, sensitive_data_masker_1.maskSensitiveData)(event.content.error_message) + '\n';
|
|
708
|
+
}
|
|
709
|
+
// Full mode specific content
|
|
710
|
+
if (isFull) {
|
|
711
|
+
if (event.content.provider) {
|
|
712
|
+
output += ' Provider: ' + event.content.provider + '\n';
|
|
713
|
+
}
|
|
714
|
+
if (event.content.model) {
|
|
715
|
+
output += ' Model: ' + event.content.model + '\n';
|
|
716
|
+
}
|
|
717
|
+
if (event.content.tokens_input !== undefined) {
|
|
718
|
+
output += ' Tokens: ' + event.content.tokens_input + ' input';
|
|
719
|
+
if (event.content.tokens_output !== undefined) {
|
|
720
|
+
output += ', ' + event.content.tokens_output + ' output';
|
|
721
|
+
}
|
|
722
|
+
output += '\n';
|
|
723
|
+
}
|
|
724
|
+
if (event.content.latency_ms !== undefined) {
|
|
725
|
+
output += ' Latency: ' + event.content.latency_ms + 'ms\n';
|
|
726
|
+
}
|
|
727
|
+
if (event.content.exit_code !== undefined) {
|
|
728
|
+
output += ' Exit code: ' + event.content.exit_code + '\n';
|
|
729
|
+
}
|
|
730
|
+
if (event.content.output_summary) {
|
|
731
|
+
output += ' Output: ' + (0, sensitive_data_masker_1.maskSensitiveData)(event.content.output_summary) + '\n';
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
output += '\n';
|
|
735
|
+
}
|
|
736
|
+
if (!isFull) {
|
|
737
|
+
output += 'Use --full to see executor details.';
|
|
738
|
+
}
|
|
739
|
+
return output;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
exports.TaskLogManager = TaskLogManager;
|
|
743
|
+
//# sourceMappingURL=task-log-manager.js.map
|