aoaoe 0.61.0 → 0.63.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/config.js +12 -1
- package/dist/daemon-state.d.ts +1 -0
- package/dist/daemon-state.js +17 -7
- package/dist/index.js +3 -1
- package/dist/tui-history.d.ts +2 -1
- package/dist/tui-history.js +8 -6
- package/dist/types.d.ts +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -342,6 +342,7 @@ Config lives at `~/.aoaoe/aoaoe.config.json` (canonical, written by `aoaoe init`
|
|
|
342
342
|
| `notifications.slackWebhookUrl` | Slack incoming webhook URL (block kit format) | (none) |
|
|
343
343
|
| `notifications.events` | Filter which events fire (omit to send all). Valid: `session_error`, `session_done`, `action_executed`, `action_failed`, `daemon_started`, `daemon_stopped` | (all) |
|
|
344
344
|
| `notifications.maxRetries` | Retry failed webhook deliveries with exponential backoff (1s, 2s, 4s, ...) | `0` (no retry) |
|
|
345
|
+
| `tuiHistoryRetentionDays` | How many days of TUI history to replay on startup (1-365) | `7` |
|
|
345
346
|
|
|
346
347
|
Also reads `.aoaoe.json` as an alternative config filename.
|
|
347
348
|
|
package/dist/config.js
CHANGED
|
@@ -83,7 +83,7 @@ export function loadConfig(overrides) {
|
|
|
83
83
|
const KNOWN_KEYS = {
|
|
84
84
|
reasoner: true, pollIntervalMs: true, captureLinesCount: true,
|
|
85
85
|
verbose: true, dryRun: true, observe: true, confirm: true,
|
|
86
|
-
contextFiles: true, sessionDirs: true, protectedSessions: true, healthPort: true,
|
|
86
|
+
contextFiles: true, sessionDirs: true, protectedSessions: true, healthPort: true, tuiHistoryRetentionDays: true,
|
|
87
87
|
opencode: new Set(["port", "model"]),
|
|
88
88
|
claudeCode: new Set(["model", "yolo", "resume"]),
|
|
89
89
|
aoe: new Set(["profile"]),
|
|
@@ -133,6 +133,13 @@ export function validateConfig(config) {
|
|
|
133
133
|
errors.push(`healthPort must be 1-65535, got ${config.healthPort}`);
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
|
+
// tuiHistoryRetentionDays: must be a positive integer, 1-365
|
|
137
|
+
if (config.tuiHistoryRetentionDays !== undefined) {
|
|
138
|
+
const d = config.tuiHistoryRetentionDays;
|
|
139
|
+
if (typeof d !== "number" || !isFinite(d) || !Number.isInteger(d) || d < 1 || d > 365) {
|
|
140
|
+
errors.push(`tuiHistoryRetentionDays must be an integer 1-365, got ${d}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
136
143
|
if (typeof config.policies?.maxErrorsBeforeRestart !== "number" || config.policies.maxErrorsBeforeRestart < 1) {
|
|
137
144
|
errors.push(`policies.maxErrorsBeforeRestart must be >= 1, got ${config.policies?.maxErrorsBeforeRestart}`);
|
|
138
145
|
}
|
|
@@ -540,6 +547,7 @@ example config:
|
|
|
540
547
|
"other-repo": "/path/to/other-repo"
|
|
541
548
|
},
|
|
542
549
|
"healthPort": 4098,
|
|
550
|
+
"tuiHistoryRetentionDays": 7,
|
|
543
551
|
"notifications": {
|
|
544
552
|
"webhookUrl": "https://example.com/webhook",
|
|
545
553
|
"slackWebhookUrl": "https://hooks.slack.com/services/T.../B.../xxx",
|
|
@@ -552,6 +560,9 @@ example config:
|
|
|
552
560
|
aoaoe loads AGENTS.md, claude.md, and other AI instruction files
|
|
553
561
|
from each project directory to give the reasoner per-session context.
|
|
554
562
|
|
|
563
|
+
tuiHistoryRetentionDays controls how many days of TUI history to replay
|
|
564
|
+
on daemon startup (default: 7, range: 1-365). History file rotates at 50MB.
|
|
565
|
+
|
|
555
566
|
notifications sends webhook alerts for daemon events. Both webhookUrl
|
|
556
567
|
and slackWebhookUrl are optional. events filters which events fire
|
|
557
568
|
(omit to send all). maxRetries enables exponential backoff retry on
|
package/dist/daemon-state.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { DaemonState, DaemonPhase, DaemonSessionState, Observation } from "./types.js";
|
|
2
|
+
export declare function setStateDir(dir: string): void;
|
|
2
3
|
export declare function resetInternalState(): void;
|
|
3
4
|
export declare function setSessionTask(sessionId: string, task: string): void;
|
|
4
5
|
export declare function writeState(phase: DaemonPhase, updates?: Partial<Omit<DaemonState, "phase" | "phaseStartedAt">>): void;
|
package/dist/daemon-state.js
CHANGED
|
@@ -6,10 +6,19 @@ import { join } from "node:path";
|
|
|
6
6
|
import { homedir } from "node:os";
|
|
7
7
|
import { toDaemonState } from "./types.js";
|
|
8
8
|
import { parseTasks, formatTaskList } from "./task-parser.js";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
// default state directory — overridable via setStateDir() for test isolation
|
|
10
|
+
let AOAOE_DIR = join(homedir(), ".aoaoe");
|
|
11
|
+
let STATE_FILE = join(AOAOE_DIR, "daemon-state.json");
|
|
12
|
+
let INTERRUPT_FILE = join(AOAOE_DIR, "interrupt");
|
|
13
|
+
let LOCK_FILE = join(AOAOE_DIR, "daemon.lock");
|
|
14
|
+
// redirect all state file paths to a custom directory (test isolation)
|
|
15
|
+
export function setStateDir(dir) {
|
|
16
|
+
AOAOE_DIR = dir;
|
|
17
|
+
STATE_FILE = join(dir, "daemon-state.json");
|
|
18
|
+
INTERRUPT_FILE = join(dir, "interrupt");
|
|
19
|
+
LOCK_FILE = join(dir, "daemon.lock");
|
|
20
|
+
dirEnsured = false; // force re-create on next write
|
|
21
|
+
}
|
|
13
22
|
// cache: only mkdirSync once per process (no need to stat the dir on every phase change)
|
|
14
23
|
let dirEnsured = false;
|
|
15
24
|
function ensureDir() {
|
|
@@ -52,14 +61,15 @@ export function setSessionTask(sessionId, task) {
|
|
|
52
61
|
const DEBOUNCE_MS = 500;
|
|
53
62
|
let lastFlushedPhase = null;
|
|
54
63
|
let lastFlushTime = 0;
|
|
55
|
-
|
|
64
|
+
// note: STATE_TMP is computed dynamically inside flushState() since STATE_FILE is mutable
|
|
56
65
|
function flushState() {
|
|
57
66
|
try {
|
|
58
67
|
ensureDir();
|
|
59
68
|
// atomic write: write to temp file, then rename into place.
|
|
60
69
|
// prevents chat.ts from reading a partially-written JSON file.
|
|
61
|
-
|
|
62
|
-
|
|
70
|
+
const tmp = STATE_FILE + ".tmp";
|
|
71
|
+
writeFileSync(tmp, JSON.stringify(currentState) + "\n");
|
|
72
|
+
renameSync(tmp, STATE_FILE);
|
|
63
73
|
lastFlushedPhase = currentState.phase;
|
|
64
74
|
lastFlushTime = Date.now();
|
|
65
75
|
}
|
package/dist/index.js
CHANGED
|
@@ -232,7 +232,9 @@ async function main() {
|
|
|
232
232
|
// start TUI (alternate screen buffer) after input is ready
|
|
233
233
|
if (tui) {
|
|
234
234
|
// replay persisted history from previous runs before entering alt screen
|
|
235
|
-
const
|
|
235
|
+
const retentionDays = config.tuiHistoryRetentionDays ?? 7;
|
|
236
|
+
const retentionMs = retentionDays * 24 * 60 * 60 * 1000;
|
|
237
|
+
const history = loadTuiHistory(200, undefined, retentionMs);
|
|
236
238
|
if (history.length > 0)
|
|
237
239
|
tui.replayHistory(history);
|
|
238
240
|
tui.start(pkg || "dev");
|
package/dist/tui-history.d.ts
CHANGED
|
@@ -14,9 +14,10 @@ export declare function appendHistoryEntry(entry: HistoryEntry, filePath?: strin
|
|
|
14
14
|
/**
|
|
15
15
|
* Load recent TUI history entries from the JSONL file.
|
|
16
16
|
* Returns the last `maxEntries` entries (default 200), newest last.
|
|
17
|
+
* Filters out entries older than `maxAgeMs` (default: 7 days).
|
|
17
18
|
* Returns empty array if the file doesn't exist or is unreadable.
|
|
18
19
|
*/
|
|
19
|
-
export declare function loadTuiHistory(maxEntries?: number, filePath?: string): HistoryEntry[];
|
|
20
|
+
export declare function loadTuiHistory(maxEntries?: number, filePath?: string, maxAgeMs?: number): HistoryEntry[];
|
|
20
21
|
/**
|
|
21
22
|
* Rotate the history file if it exceeds the size threshold.
|
|
22
23
|
* Renames current file to .old (overwriting any previous .old) and starts fresh.
|
package/dist/tui-history.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// tui-history.ts — persisted TUI activity history
|
|
2
|
-
// JSONL file at ~/.aoaoe/tui-history.jsonl with rotation at
|
|
2
|
+
// JSONL file at ~/.aoaoe/tui-history.jsonl with rotation at 50MB.
|
|
3
3
|
// pure exported functions for testability — no classes, no singletons.
|
|
4
4
|
import { appendFileSync, readFileSync, renameSync, statSync, mkdirSync, existsSync } from "node:fs";
|
|
5
5
|
import { join } from "node:path";
|
|
@@ -7,7 +7,7 @@ import { homedir } from "node:os";
|
|
|
7
7
|
const AOAOE_DIR = join(homedir(), ".aoaoe");
|
|
8
8
|
const HISTORY_FILE = join(AOAOE_DIR, "tui-history.jsonl");
|
|
9
9
|
const HISTORY_OLD = join(AOAOE_DIR, "tui-history.jsonl.old");
|
|
10
|
-
const MAX_FILE_SIZE =
|
|
10
|
+
const MAX_FILE_SIZE = 50 * 1024 * 1024; // 50MB rotation threshold
|
|
11
11
|
/**
|
|
12
12
|
* Append a single history entry to the JSONL file.
|
|
13
13
|
* Fire-and-forget — errors are silently swallowed so they never block the TUI.
|
|
@@ -29,27 +29,29 @@ export function appendHistoryEntry(entry, filePath = HISTORY_FILE, maxSize = MAX
|
|
|
29
29
|
/**
|
|
30
30
|
* Load recent TUI history entries from the JSONL file.
|
|
31
31
|
* Returns the last `maxEntries` entries (default 200), newest last.
|
|
32
|
+
* Filters out entries older than `maxAgeMs` (default: 7 days).
|
|
32
33
|
* Returns empty array if the file doesn't exist or is unreadable.
|
|
33
34
|
*/
|
|
34
|
-
export function loadTuiHistory(maxEntries = 200, filePath = HISTORY_FILE) {
|
|
35
|
+
export function loadTuiHistory(maxEntries = 200, filePath = HISTORY_FILE, maxAgeMs = 7 * 24 * 60 * 60 * 1000) {
|
|
35
36
|
try {
|
|
36
37
|
if (!existsSync(filePath))
|
|
37
38
|
return [];
|
|
38
39
|
const content = readFileSync(filePath, "utf-8");
|
|
39
40
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
40
|
-
const
|
|
41
|
+
const cutoff = Date.now() - maxAgeMs;
|
|
42
|
+
const recent = lines.slice(-maxEntries * 2); // read extra to compensate for age filtering
|
|
41
43
|
const entries = [];
|
|
42
44
|
for (const line of recent) {
|
|
43
45
|
try {
|
|
44
46
|
const parsed = JSON.parse(line);
|
|
45
|
-
if (isValidEntry(parsed))
|
|
47
|
+
if (isValidEntry(parsed) && parsed.ts >= cutoff)
|
|
46
48
|
entries.push(parsed);
|
|
47
49
|
}
|
|
48
50
|
catch {
|
|
49
51
|
// skip malformed lines
|
|
50
52
|
}
|
|
51
53
|
}
|
|
52
|
-
return entries;
|
|
54
|
+
return entries.slice(-maxEntries);
|
|
53
55
|
}
|
|
54
56
|
catch {
|
|
55
57
|
return [];
|
package/dist/types.d.ts
CHANGED
|
@@ -122,6 +122,7 @@ export interface AoaoeConfig {
|
|
|
122
122
|
maxRetries?: number;
|
|
123
123
|
};
|
|
124
124
|
healthPort?: number;
|
|
125
|
+
tuiHistoryRetentionDays?: number;
|
|
125
126
|
}
|
|
126
127
|
export type NotificationEvent = "session_error" | "session_done" | "action_executed" | "action_failed" | "daemon_started" | "daemon_stopped";
|
|
127
128
|
export type DaemonPhase = "sleeping" | "polling" | "reasoning" | "executing" | "interrupted";
|