akemon 0.3.6 → 0.3.7
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/DATA_POLICY.md +11 -3
- package/README.md +133 -21
- package/dist/akemon-home.js +56 -0
- package/dist/akemon-message.js +107 -0
- package/dist/best-effort.js +8 -0
- package/dist/cli.js +1188 -100
- package/dist/cognitive-artifact-store.js +101 -0
- package/dist/cognitive-event-log.js +47 -0
- package/dist/config.js +45 -9
- package/dist/context.js +27 -6
- package/dist/core/contracts/layers.js +1 -0
- package/dist/core/contracts/permission.js +1 -0
- package/dist/core/contracts/workspace.js +1 -0
- package/dist/core-cognitive-module.js +768 -0
- package/dist/engine-peripheral.js +127 -26
- package/dist/engine-routing.js +58 -17
- package/dist/interactive-session.js +361 -0
- package/dist/local-interconnect.js +156 -0
- package/dist/local-registry.js +178 -0
- package/dist/mcp-server.js +4 -1
- package/dist/memory-proposal.js +379 -0
- package/dist/memory-recorder.js +368 -0
- package/dist/orphan-scan.js +36 -24
- package/dist/passive-reflection-cognitive-module.js +172 -0
- package/dist/peripheral-registry.js +235 -0
- package/dist/permission-audit.js +132 -0
- package/dist/relay-client.js +68 -9
- package/dist/relay-mode.js +34 -0
- package/dist/relay-peripheral.js +139 -49
- package/dist/runtime-platform.js +122 -0
- package/dist/secretariat/client.js +87 -0
- package/dist/self.js +15 -6
- package/dist/server.js +3675 -512
- package/dist/social-discovery.js +231 -0
- package/dist/software-agent-peripheral.js +185 -244
- package/dist/software-agent-transport.js +177 -0
- package/dist/task-module.js +243 -0
- package/dist/task-registry.js +756 -0
- package/dist/vendor/xterm/addon-fit.js +2 -0
- package/dist/vendor/xterm/addon-search.js +2 -0
- package/dist/vendor/xterm/addon-web-links.js +2 -0
- package/dist/vendor/xterm/xterm.css +285 -0
- package/dist/vendor/xterm/xterm.js +2 -0
- package/dist/work-memory.js +59 -15
- package/dist/workbench-peripheral-guide.js +79 -0
- package/dist/workbench-session.js +1074 -0
- package/dist/workbench.html +4011 -0
- package/package.json +8 -3
- package/scripts/build.cjs +24 -0
- package/scripts/check-architecture-baseline.cjs +68 -0
- package/scripts/test.cjs +38 -0
|
@@ -11,13 +11,17 @@
|
|
|
11
11
|
* transport to app-server or a true persistent interactive session.
|
|
12
12
|
*/
|
|
13
13
|
import { randomUUID } from "crypto";
|
|
14
|
-
import {
|
|
14
|
+
import { spawnSync } from "child_process";
|
|
15
15
|
import { existsSync, mkdirSync, readdirSync, readFileSync, unlinkSync, writeFileSync } from "fs";
|
|
16
16
|
import { isAbsolute, join, relative, resolve as resolvePath } from "path";
|
|
17
|
-
import { StringDecoder } from "string_decoder";
|
|
18
17
|
import { SIG, sig } from "./types.js";
|
|
19
18
|
import { sendTaskEnd, sendTaskStart, sendTaskStream } from "./relay-client.js";
|
|
20
|
-
import { redactSecrets
|
|
19
|
+
import { redactSecrets } from "./redaction.js";
|
|
20
|
+
import { terminateProcessTree } from "./runtime-platform.js";
|
|
21
|
+
import { actorRef } from "./akemon-message.js";
|
|
22
|
+
import { appendPermissionAuditRecordSync } from "./permission-audit.js";
|
|
23
|
+
import { logBestEffortError } from "./best-effort.js";
|
|
24
|
+
import { CodexExecTransport, } from "./software-agent-transport.js";
|
|
21
25
|
const defaultTaskRelay = {
|
|
22
26
|
sendTaskStart,
|
|
23
27
|
sendTaskStream,
|
|
@@ -78,12 +82,19 @@ export class CodexSoftwareAgentPeripheral {
|
|
|
78
82
|
activeTaskId = null;
|
|
79
83
|
activeWorkdir = null;
|
|
80
84
|
sessionId = randomUUID();
|
|
85
|
+
transport;
|
|
81
86
|
constructor(config) {
|
|
82
87
|
this.config = {
|
|
83
88
|
...config,
|
|
84
89
|
envPolicy: normalizeSoftwareAgentEnvPolicy(config.envPolicy),
|
|
85
90
|
envAllowlist: normalizeSoftwareAgentEnvAllowlist(config.envAllowlist),
|
|
86
91
|
};
|
|
92
|
+
this.transport = config.transport || new CodexExecTransport({
|
|
93
|
+
command: config.command || "codex",
|
|
94
|
+
model: config.model,
|
|
95
|
+
sandbox: config.sandbox || "workspace-write",
|
|
96
|
+
spawnImpl: config.spawnImpl,
|
|
97
|
+
});
|
|
87
98
|
this.id = config.id || "software-agent:codex";
|
|
88
99
|
this.name = config.name || "Codex CLI Software Agent";
|
|
89
100
|
}
|
|
@@ -103,18 +114,9 @@ export class CodexSoftwareAgentPeripheral {
|
|
|
103
114
|
async resetSession() {
|
|
104
115
|
const activePid = this.activeChild?.pid;
|
|
105
116
|
if (activePid) {
|
|
106
|
-
|
|
107
|
-
try {
|
|
108
|
-
process.kill(processGroupId, "SIGTERM");
|
|
109
|
-
}
|
|
110
|
-
catch { }
|
|
111
|
-
setTimeout(() => {
|
|
112
|
-
try {
|
|
113
|
-
process.kill(processGroupId, "SIGKILL");
|
|
114
|
-
}
|
|
115
|
-
catch { }
|
|
116
|
-
}, 3000).unref();
|
|
117
|
+
terminateProcessTree(activePid, { signal: "SIGTERM", forceAfterMs: 3000 });
|
|
117
118
|
}
|
|
119
|
+
await this.transport.reset?.();
|
|
118
120
|
this.activeChild = null;
|
|
119
121
|
this.activeTaskId = null;
|
|
120
122
|
this.activeWorkdir = null;
|
|
@@ -127,8 +129,9 @@ export class CodexSoftwareAgentPeripheral {
|
|
|
127
129
|
sessionId: this.sessionId,
|
|
128
130
|
activeTaskId: this.activeTaskId,
|
|
129
131
|
activeWorkdir: this.activeWorkdir,
|
|
130
|
-
busy: !!this.activeChild,
|
|
131
|
-
transport:
|
|
132
|
+
busy: !!this.activeTaskId || !!this.activeChild,
|
|
133
|
+
transport: this.transport.kind,
|
|
134
|
+
transportContract: this.transport.describe(currentWorkdir),
|
|
132
135
|
baseWorkdir: resolvePath(this.config.workdir),
|
|
133
136
|
workdirStatus: this.collectWorkdirStatus(currentWorkdir),
|
|
134
137
|
taskLedgerDir: this.config.taskLedgerDir,
|
|
@@ -155,7 +158,17 @@ export class CodexSoftwareAgentPeripheral {
|
|
|
155
158
|
return sig(SIG.SOFTWARE_AGENT_RESPONSE, { ...result }, this.id);
|
|
156
159
|
}
|
|
157
160
|
async sendTask(envelope, taskOptions) {
|
|
158
|
-
if (this.activeChild) {
|
|
161
|
+
if (this.activeTaskId || this.activeChild) {
|
|
162
|
+
this.writePermissionAudit({
|
|
163
|
+
envelope,
|
|
164
|
+
taskId: envelope.taskId,
|
|
165
|
+
workdir: envelope.workdir || this.config.workdir,
|
|
166
|
+
decision: {
|
|
167
|
+
result: "denied",
|
|
168
|
+
mode: "automatic",
|
|
169
|
+
reason: `Software agent busy (task=${this.activeTaskId})`,
|
|
170
|
+
},
|
|
171
|
+
});
|
|
159
172
|
throw new Error(`Software agent busy (task=${this.activeTaskId})`);
|
|
160
173
|
}
|
|
161
174
|
const { signal, observer } = normalizeSoftwareAgentTaskOptions(taskOptions);
|
|
@@ -179,17 +192,11 @@ export class CodexSoftwareAgentPeripheral {
|
|
|
179
192
|
const prompt = contextSession
|
|
180
193
|
? buildContextPacketLaunchPrompt(effectiveEnvelope, contextSession.audit.packetPath)
|
|
181
194
|
: buildTaskEnvelopePrompt(effectiveEnvelope);
|
|
182
|
-
const
|
|
183
|
-
command: this.config.command || "codex",
|
|
184
|
-
workdir,
|
|
185
|
-
model: this.config.model,
|
|
186
|
-
sandbox: this.config.sandbox || "workspace-write",
|
|
187
|
-
});
|
|
195
|
+
const transportDescriptor = this.transport.describe(workdir);
|
|
188
196
|
const startedAt = Date.now();
|
|
189
197
|
const startedAtIso = new Date(startedAt).toISOString();
|
|
190
198
|
const relay = this.config.taskRelay || defaultTaskRelay;
|
|
191
|
-
const commandLine =
|
|
192
|
-
const spawnImpl = this.config.spawnImpl || spawn;
|
|
199
|
+
const commandLine = transportDescriptor.commandLine;
|
|
193
200
|
const timeoutMs = envelope.timeoutMs || this.config.defaultTimeoutMs || DEFAULT_TIMEOUT_MS;
|
|
194
201
|
const workdirStatus = this.collectWorkdirStatus(workdir);
|
|
195
202
|
const taskMetadata = {
|
|
@@ -202,12 +209,25 @@ export class CodexSoftwareAgentPeripheral {
|
|
|
202
209
|
allowlist: this.config.envAllowlist,
|
|
203
210
|
sourceEnv: this.config.sourceEnv,
|
|
204
211
|
});
|
|
212
|
+
this.writePermissionAudit({
|
|
213
|
+
envelope: effectiveEnvelope,
|
|
214
|
+
taskId,
|
|
215
|
+
workdir,
|
|
216
|
+
workdirSafety,
|
|
217
|
+
commandLine,
|
|
218
|
+
environment: childEnvironment.audit,
|
|
219
|
+
decision: {
|
|
220
|
+
result: "allowed",
|
|
221
|
+
mode: "owner-approved",
|
|
222
|
+
reason: "Owner local endpoint accepted the software-agent task envelope.",
|
|
223
|
+
},
|
|
224
|
+
});
|
|
205
225
|
const baseTaskRecord = () => ({
|
|
206
226
|
schemaVersion: 1,
|
|
207
227
|
taskId,
|
|
208
228
|
agentId: this.id,
|
|
209
229
|
sessionId: this.sessionId,
|
|
210
|
-
transport:
|
|
230
|
+
transport: this.transport.kind,
|
|
211
231
|
commandLine,
|
|
212
232
|
envelope: effectiveEnvelope,
|
|
213
233
|
startedAt: startedAtIso,
|
|
@@ -215,211 +235,103 @@ export class CodexSoftwareAgentPeripheral {
|
|
|
215
235
|
contextSession: contextSession?.audit,
|
|
216
236
|
workdirStatus,
|
|
217
237
|
});
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
238
|
+
this.activeTaskId = taskId;
|
|
239
|
+
this.activeWorkdir = workdir;
|
|
240
|
+
this.writeTaskRecord({
|
|
241
|
+
...baseTaskRecord(),
|
|
242
|
+
status: "running",
|
|
243
|
+
updatedAt: startedAtIso,
|
|
244
|
+
});
|
|
245
|
+
const origin = "software_agent";
|
|
246
|
+
relay.sendTaskStart(taskId, origin, commandLine);
|
|
247
|
+
observer?.onStart?.({ taskId, origin, commandLine, ...taskMetadata });
|
|
248
|
+
this.bus?.emit(SIG.TASK_STARTED, sig(SIG.TASK_STARTED, {
|
|
249
|
+
taskId,
|
|
250
|
+
taskType: "software_agent",
|
|
251
|
+
description: effectiveEnvelope.goal,
|
|
252
|
+
peripheral: this.id,
|
|
253
|
+
sessionId: this.sessionId,
|
|
254
|
+
}, this.id));
|
|
255
|
+
let transportResult;
|
|
256
|
+
try {
|
|
257
|
+
transportResult = await this.transport.run({
|
|
258
|
+
taskId,
|
|
259
|
+
prompt,
|
|
260
|
+
workdir,
|
|
261
|
+
env: childEnvironment.env,
|
|
262
|
+
timeoutMs,
|
|
263
|
+
signal,
|
|
264
|
+
startedAt,
|
|
265
|
+
onStream: (event) => {
|
|
266
|
+
relay.sendTaskStream(event.taskId, event.stream, event.chunk);
|
|
267
|
+
observer?.onStream?.(event);
|
|
268
|
+
},
|
|
269
|
+
onProcessStart: (child) => {
|
|
270
|
+
this.activeChild = child;
|
|
271
|
+
},
|
|
272
|
+
onProcessEnd: (child) => {
|
|
273
|
+
if (this.activeChild === child)
|
|
274
|
+
this.activeChild = null;
|
|
275
|
+
},
|
|
225
276
|
});
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
this.bus?.emit(SIG.TASK_STARTED, sig(SIG.TASK_STARTED, {
|
|
277
|
+
}
|
|
278
|
+
catch (err) {
|
|
279
|
+
transportResult = {
|
|
230
280
|
taskId,
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
child = spawnImpl(cmd, args, {
|
|
239
|
-
cwd: workdir,
|
|
240
|
-
env: childEnvironment.env,
|
|
241
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
242
|
-
detached: true,
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
catch (err) {
|
|
246
|
-
this.activeChild = null;
|
|
247
|
-
this.activeTaskId = null;
|
|
248
|
-
this.activeWorkdir = null;
|
|
249
|
-
const durationMs = Date.now() - startedAt;
|
|
250
|
-
const result = {
|
|
251
|
-
success: false,
|
|
252
|
-
taskId,
|
|
253
|
-
output: "",
|
|
254
|
-
error: err.message || String(err),
|
|
255
|
-
exitCode: null,
|
|
256
|
-
durationMs,
|
|
257
|
-
...taskMetadata,
|
|
258
|
-
};
|
|
259
|
-
relay.sendTaskEnd(taskId, null, durationMs);
|
|
260
|
-
observer?.onEnd?.({
|
|
261
|
-
taskId,
|
|
262
|
-
exitCode: null,
|
|
263
|
-
durationMs,
|
|
264
|
-
result: redactSecrets(result),
|
|
265
|
-
...taskMetadata,
|
|
266
|
-
});
|
|
267
|
-
const completedAt = new Date().toISOString();
|
|
268
|
-
this.writeTaskRecord({
|
|
269
|
-
...baseTaskRecord(),
|
|
270
|
-
status: "failed",
|
|
271
|
-
updatedAt: completedAt,
|
|
272
|
-
completedAt,
|
|
273
|
-
durationMs,
|
|
274
|
-
result,
|
|
275
|
-
stdoutSummary: summarizeText(""),
|
|
276
|
-
stderrSummary: summarizeText(result.error || ""),
|
|
277
|
-
});
|
|
278
|
-
if (contextSession) {
|
|
279
|
-
writeSoftwareAgentContextSessionState(contextSession.audit.statePath, effectiveEnvelope, result, completedAt);
|
|
280
|
-
}
|
|
281
|
-
this.bus?.emit(SIG.TASK_FAILED, sig(SIG.TASK_FAILED, result, this.id));
|
|
282
|
-
resolve(result);
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
|
-
this.activeChild = child;
|
|
286
|
-
let stdout = "";
|
|
287
|
-
let stderr = "";
|
|
288
|
-
let finished = false;
|
|
289
|
-
let aborted = false;
|
|
290
|
-
const outDecoder = new StringDecoder("utf8");
|
|
291
|
-
const errDecoder = new StringDecoder("utf8");
|
|
292
|
-
const outRedactor = new StreamingRedactor();
|
|
293
|
-
const errRedactor = new StreamingRedactor();
|
|
294
|
-
const emitSafeStream = (stream, text) => {
|
|
295
|
-
if (!text)
|
|
296
|
-
return;
|
|
297
|
-
relay.sendTaskStream(taskId, stream, text);
|
|
298
|
-
observer?.onStream?.({ taskId, stream, chunk: text });
|
|
299
|
-
};
|
|
300
|
-
const finish = (exitCode, error) => {
|
|
301
|
-
if (finished)
|
|
302
|
-
return;
|
|
303
|
-
finished = true;
|
|
304
|
-
signal?.removeEventListener("abort", onAbort);
|
|
305
|
-
clearTimeout(timer);
|
|
306
|
-
const tailOut = outDecoder.end();
|
|
307
|
-
const tailErr = errDecoder.end();
|
|
308
|
-
if (tailOut) {
|
|
309
|
-
stdout += tailOut;
|
|
310
|
-
emitSafeStream("stdout", outRedactor.push(tailOut));
|
|
311
|
-
}
|
|
312
|
-
if (tailErr) {
|
|
313
|
-
stderr += tailErr;
|
|
314
|
-
emitSafeStream("stderr", errRedactor.push(tailErr));
|
|
315
|
-
}
|
|
316
|
-
emitSafeStream("stdout", outRedactor.flush());
|
|
317
|
-
emitSafeStream("stderr", errRedactor.flush());
|
|
318
|
-
const durationMs = Date.now() - startedAt;
|
|
319
|
-
this.activeChild = null;
|
|
320
|
-
this.activeTaskId = null;
|
|
321
|
-
this.activeWorkdir = null;
|
|
322
|
-
const output = stdout.trim() || stderr.trim();
|
|
323
|
-
const success = !error && !aborted && exitCode === 0;
|
|
324
|
-
const result = {
|
|
325
|
-
success,
|
|
326
|
-
taskId,
|
|
327
|
-
output,
|
|
328
|
-
error: success ? undefined : error || stderr.trim() || `codex exited with code ${exitCode}`,
|
|
329
|
-
exitCode,
|
|
330
|
-
durationMs,
|
|
331
|
-
...taskMetadata,
|
|
332
|
-
};
|
|
333
|
-
relay.sendTaskEnd(taskId, exitCode, durationMs);
|
|
334
|
-
observer?.onEnd?.({
|
|
335
|
-
taskId,
|
|
336
|
-
exitCode,
|
|
337
|
-
durationMs,
|
|
338
|
-
result: redactSecrets(result),
|
|
339
|
-
...taskMetadata,
|
|
340
|
-
});
|
|
341
|
-
const completedAt = new Date().toISOString();
|
|
342
|
-
this.writeTaskRecord({
|
|
343
|
-
...baseTaskRecord(),
|
|
344
|
-
status: success ? "completed" : "failed",
|
|
345
|
-
updatedAt: completedAt,
|
|
346
|
-
completedAt,
|
|
347
|
-
durationMs,
|
|
348
|
-
result,
|
|
349
|
-
stdoutSummary: summarizeText(stdout),
|
|
350
|
-
stderrSummary: summarizeText(stderr),
|
|
351
|
-
});
|
|
352
|
-
if (contextSession) {
|
|
353
|
-
writeSoftwareAgentContextSessionState(contextSession.audit.statePath, effectiveEnvelope, result, completedAt);
|
|
354
|
-
}
|
|
355
|
-
this.bus?.emit(success ? SIG.TASK_COMPLETED : SIG.TASK_FAILED, sig(success ? SIG.TASK_COMPLETED : SIG.TASK_FAILED, {
|
|
356
|
-
...result,
|
|
357
|
-
taskLabel: `software_agent:${this.id}`,
|
|
358
|
-
}, this.id));
|
|
359
|
-
resolve(result);
|
|
360
|
-
};
|
|
361
|
-
const onAbort = () => {
|
|
362
|
-
if (aborted || !child.pid)
|
|
363
|
-
return;
|
|
364
|
-
aborted = true;
|
|
365
|
-
try {
|
|
366
|
-
process.kill(-child.pid, "SIGTERM");
|
|
367
|
-
}
|
|
368
|
-
catch { }
|
|
369
|
-
setTimeout(() => {
|
|
370
|
-
try {
|
|
371
|
-
process.kill(-child.pid, "SIGKILL");
|
|
372
|
-
}
|
|
373
|
-
catch { }
|
|
374
|
-
}, 3000).unref();
|
|
281
|
+
stdout: "",
|
|
282
|
+
stderr: "",
|
|
283
|
+
error: err.message || String(err),
|
|
284
|
+
exitCode: null,
|
|
285
|
+
durationMs: Date.now() - startedAt,
|
|
286
|
+
aborted: false,
|
|
287
|
+
success: false,
|
|
375
288
|
};
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
});
|
|
414
|
-
child.on("close", (code) => {
|
|
415
|
-
child.unref();
|
|
416
|
-
finish(code);
|
|
417
|
-
});
|
|
418
|
-
child.on("error", (err) => {
|
|
419
|
-
child.unref();
|
|
420
|
-
finish(null, err.message);
|
|
421
|
-
});
|
|
289
|
+
}
|
|
290
|
+
finally {
|
|
291
|
+
this.activeChild = null;
|
|
292
|
+
this.activeTaskId = null;
|
|
293
|
+
this.activeWorkdir = null;
|
|
294
|
+
}
|
|
295
|
+
const output = transportResult.stdout.trim() || transportResult.stderr.trim();
|
|
296
|
+
const transportLabel = this.transport.kind === "codex-exec" ? "codex" : this.transport.kind;
|
|
297
|
+
const result = {
|
|
298
|
+
success: transportResult.success,
|
|
299
|
+
taskId,
|
|
300
|
+
output,
|
|
301
|
+
error: transportResult.success
|
|
302
|
+
? undefined
|
|
303
|
+
: transportResult.error || transportResult.stderr.trim() || `${transportLabel} exited with code ${transportResult.exitCode}`,
|
|
304
|
+
exitCode: transportResult.exitCode,
|
|
305
|
+
durationMs: transportResult.durationMs,
|
|
306
|
+
...taskMetadata,
|
|
307
|
+
};
|
|
308
|
+
relay.sendTaskEnd(taskId, transportResult.exitCode, transportResult.durationMs);
|
|
309
|
+
observer?.onEnd?.({
|
|
310
|
+
taskId,
|
|
311
|
+
exitCode: transportResult.exitCode,
|
|
312
|
+
durationMs: transportResult.durationMs,
|
|
313
|
+
result: redactSecrets(result),
|
|
314
|
+
...taskMetadata,
|
|
315
|
+
});
|
|
316
|
+
const completedAt = new Date().toISOString();
|
|
317
|
+
this.writeTaskRecord({
|
|
318
|
+
...baseTaskRecord(),
|
|
319
|
+
status: result.success ? "completed" : "failed",
|
|
320
|
+
updatedAt: completedAt,
|
|
321
|
+
completedAt,
|
|
322
|
+
durationMs: transportResult.durationMs,
|
|
323
|
+
result,
|
|
324
|
+
stdoutSummary: summarizeText(transportResult.stdout),
|
|
325
|
+
stderrSummary: summarizeText(transportResult.stderr || result.error || ""),
|
|
422
326
|
});
|
|
327
|
+
if (contextSession) {
|
|
328
|
+
writeSoftwareAgentContextSessionState(contextSession.audit.statePath, effectiveEnvelope, result, completedAt);
|
|
329
|
+
}
|
|
330
|
+
this.bus?.emit(result.success ? SIG.TASK_COMPLETED : SIG.TASK_FAILED, sig(result.success ? SIG.TASK_COMPLETED : SIG.TASK_FAILED, {
|
|
331
|
+
...result,
|
|
332
|
+
taskLabel: `software_agent:${this.id}`,
|
|
333
|
+
}, this.id));
|
|
334
|
+
return result;
|
|
423
335
|
}
|
|
424
336
|
writeTaskRecord(record) {
|
|
425
337
|
const dir = this.config.taskLedgerDir;
|
|
@@ -427,14 +339,54 @@ export class CodexSoftwareAgentPeripheral {
|
|
|
427
339
|
return;
|
|
428
340
|
try {
|
|
429
341
|
mkdirSync(dir, { recursive: true });
|
|
430
|
-
|
|
431
|
-
writeFileSync(join(dir, `${safeTaskId}.json`), `${JSON.stringify(redactSecrets(record), null, 2)}\n`);
|
|
342
|
+
writeFileSync(softwareAgentTaskRecordPath(dir, record.taskId), `${JSON.stringify(redactSecrets(record), null, 2)}\n`);
|
|
432
343
|
pruneSoftwareAgentTaskRecords(dir, this.config.taskLedgerMaxRecords, record.taskId);
|
|
433
344
|
}
|
|
434
345
|
catch (err) {
|
|
435
346
|
console.error(`[software-agent] Failed to write task ledger: ${err.message || String(err)}`);
|
|
436
347
|
}
|
|
437
348
|
}
|
|
349
|
+
writePermissionAudit(input) {
|
|
350
|
+
if (!this.config.agentName)
|
|
351
|
+
return;
|
|
352
|
+
try {
|
|
353
|
+
const requesterKind = input.envelope.sourceModule === "owner-http" ? "owner" : "system";
|
|
354
|
+
appendPermissionAuditRecordSync(this.config.agentName, {
|
|
355
|
+
actionKind: "software-agent-task",
|
|
356
|
+
action: "software-agent.run",
|
|
357
|
+
requestedBy: actorRef(requesterKind, input.envelope.sourceModule || "owner", "local"),
|
|
358
|
+
performedBy: actorRef("agent", this.config.agentName, "software-agent"),
|
|
359
|
+
target: actorRef("external", this.id, "software-agent"),
|
|
360
|
+
sourceModule: input.envelope.sourceModule,
|
|
361
|
+
peripheralId: this.id,
|
|
362
|
+
riskLevel: input.envelope.riskLevel,
|
|
363
|
+
roleScope: input.envelope.roleScope,
|
|
364
|
+
memoryScope: input.envelope.memoryScope,
|
|
365
|
+
workdir: input.workdir || input.envelope.workdir,
|
|
366
|
+
projectScope: input.workdirSafety
|
|
367
|
+
? (input.workdirSafety.outsideBaseWorkdir ? "outside-workdir" : "inside-workdir")
|
|
368
|
+
: "unknown",
|
|
369
|
+
transport: "software-agent",
|
|
370
|
+
decision: input.decision,
|
|
371
|
+
allowedActions: input.envelope.allowedActions,
|
|
372
|
+
forbiddenActions: input.envelope.forbiddenActions,
|
|
373
|
+
references: {
|
|
374
|
+
taskId: input.taskId || input.envelope.taskId,
|
|
375
|
+
contextSessionId: input.envelope.contextSessionId,
|
|
376
|
+
contextPacketPath: input.envelope.contextPacketPath,
|
|
377
|
+
},
|
|
378
|
+
metadata: {
|
|
379
|
+
purpose: input.envelope.purpose,
|
|
380
|
+
commandLine: input.commandLine,
|
|
381
|
+
environment: input.environment,
|
|
382
|
+
workMemoryDir: input.envelope.workMemoryDir,
|
|
383
|
+
},
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
catch (error) {
|
|
387
|
+
logBestEffortError("software-agent audit append", error);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
438
390
|
collectWorkdirStatus(workdir) {
|
|
439
391
|
const impl = this.config.gitStatusImpl || readGitWorktreeStatus;
|
|
440
392
|
return impl(workdir);
|
|
@@ -686,8 +638,10 @@ export function listSoftwareAgentTaskRecords(taskLedgerDir, limit = 20, opts = {
|
|
|
686
638
|
}
|
|
687
639
|
}
|
|
688
640
|
export function readSoftwareAgentTaskRecord(taskLedgerDir, taskId) {
|
|
689
|
-
|
|
690
|
-
|
|
641
|
+
return readSoftwareAgentTaskRecordFile(softwareAgentTaskRecordPath(taskLedgerDir, taskId));
|
|
642
|
+
}
|
|
643
|
+
export function softwareAgentTaskRecordPath(taskLedgerDir, taskId) {
|
|
644
|
+
return join(taskLedgerDir, `${safeTaskFilename(taskId)}.json`);
|
|
691
645
|
}
|
|
692
646
|
export function listSoftwareAgentContextSessions(contextSessionDir, limit = 20) {
|
|
693
647
|
const safeLimit = normalizeTaskRecordLimit(limit);
|
|
@@ -1071,16 +1025,3 @@ function compareSoftwareAgentContextSessions(a, b) {
|
|
|
1071
1025
|
return bTime - aTime;
|
|
1072
1026
|
return b.sessionId.localeCompare(a.sessionId);
|
|
1073
1027
|
}
|
|
1074
|
-
function buildCodexExecCommand(opts) {
|
|
1075
|
-
const args = [
|
|
1076
|
-
"exec",
|
|
1077
|
-
"--skip-git-repo-check",
|
|
1078
|
-
"--color", "never",
|
|
1079
|
-
"-s", opts.sandbox,
|
|
1080
|
-
"-C", opts.workdir,
|
|
1081
|
-
];
|
|
1082
|
-
if (opts.model)
|
|
1083
|
-
args.push("-m", opts.model);
|
|
1084
|
-
args.push("-");
|
|
1085
|
-
return { cmd: opts.command, args };
|
|
1086
|
-
}
|