@agentdeck/bridge 1.0.6 → 1.0.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/assets/fm-helper/agentdeck-fm-helper +0 -0
- package/dist/apme/collector.d.ts +10 -0
- package/dist/apme/collector.d.ts.map +1 -1
- package/dist/apme/collector.js +39 -10
- package/dist/apme/collector.js.map +1 -1
- package/dist/apme/dashboard-html.d.ts.map +1 -1
- package/dist/apme/dashboard-html.js +289 -0
- package/dist/apme/dashboard-html.js.map +1 -1
- package/dist/apme/graph.d.ts +48 -0
- package/dist/apme/graph.d.ts.map +1 -0
- package/dist/apme/graph.js +226 -0
- package/dist/apme/graph.js.map +1 -0
- package/dist/apme/http.d.ts.map +1 -1
- package/dist/apme/http.js +52 -0
- package/dist/apme/http.js.map +1 -1
- package/dist/apme/judge-detect.d.ts.map +1 -1
- package/dist/apme/judge-detect.js +26 -2
- package/dist/apme/judge-detect.js.map +1 -1
- package/dist/apme/store.d.ts +69 -1
- package/dist/apme/store.d.ts.map +1 -1
- package/dist/apme/store.js +238 -4
- package/dist/apme/store.js.map +1 -1
- package/dist/apme/types.d.ts +1 -1
- package/dist/apme/types.d.ts.map +1 -1
- package/dist/apme/types.js.map +1 -1
- package/dist/ble-sync-spawn.d.ts +25 -2
- package/dist/ble-sync-spawn.d.ts.map +1 -1
- package/dist/ble-sync-spawn.js +38 -2
- package/dist/ble-sync-spawn.js.map +1 -1
- package/dist/ble-sync-status.d.ts +77 -0
- package/dist/ble-sync-status.d.ts.map +1 -0
- package/dist/ble-sync-status.js +157 -0
- package/dist/ble-sync-status.js.map +1 -0
- package/dist/bridge-core.d.ts.map +1 -1
- package/dist/bridge-core.js +7 -1
- package/dist/bridge-core.js.map +1 -1
- package/dist/cli.js +0 -0
- package/dist/codex-rate-limits-live.d.ts +60 -0
- package/dist/codex-rate-limits-live.d.ts.map +1 -0
- package/dist/codex-rate-limits-live.js +282 -0
- package/dist/codex-rate-limits-live.js.map +1 -0
- package/dist/d200h/hid-protocol.d.ts +62 -0
- package/dist/d200h/hid-protocol.d.ts.map +1 -0
- package/dist/d200h/hid-protocol.js +124 -0
- package/dist/d200h/hid-protocol.js.map +1 -0
- package/dist/d200h/image-renderer.d.ts +24 -0
- package/dist/d200h/image-renderer.d.ts.map +1 -0
- package/dist/d200h/image-renderer.js +321 -0
- package/dist/d200h/image-renderer.js.map +1 -0
- package/dist/daemon-server.d.ts.map +1 -1
- package/dist/daemon-server.js +79 -0
- package/dist/daemon-server.js.map +1 -1
- package/dist/daemon.js +1 -1
- package/dist/hook-migration.d.ts +2 -0
- package/dist/hook-migration.d.ts.map +1 -0
- package/dist/hook-migration.js +116 -0
- package/dist/hook-migration.js.map +1 -0
- package/dist/idotmatrix/idotmatrix-daemon-sync.d.ts +8 -0
- package/dist/idotmatrix/idotmatrix-daemon-sync.d.ts.map +1 -1
- package/dist/idotmatrix/idotmatrix-daemon-sync.js +19 -3
- package/dist/idotmatrix/idotmatrix-daemon-sync.js.map +1 -1
- package/dist/modules/d200h-module.d.ts +65 -0
- package/dist/modules/d200h-module.d.ts.map +1 -0
- package/dist/modules/d200h-module.js +495 -0
- package/dist/modules/d200h-module.js.map +1 -0
- package/dist/modules/d200h-renderer.d.ts +7 -0
- package/dist/modules/d200h-renderer.d.ts.map +1 -0
- package/dist/modules/d200h-renderer.js +321 -0
- package/dist/modules/d200h-renderer.js.map +1 -0
- package/dist/modules/idotmatrix-module.d.ts.map +1 -1
- package/dist/modules/idotmatrix-module.js +12 -1
- package/dist/modules/idotmatrix-module.js.map +1 -1
- package/dist/modules/timebox-module.d.ts.map +1 -1
- package/dist/modules/timebox-module.js +12 -1
- package/dist/modules/timebox-module.js.map +1 -1
- package/dist/modules/trmnl-module.d.ts +38 -0
- package/dist/modules/trmnl-module.d.ts.map +1 -0
- package/dist/modules/trmnl-module.js +111 -0
- package/dist/modules/trmnl-module.js.map +1 -0
- package/dist/timebox/timebox-daemon-sync.d.ts +8 -0
- package/dist/timebox/timebox-daemon-sync.d.ts.map +1 -1
- package/dist/timebox/timebox-daemon-sync.js +29 -1
- package/dist/timebox/timebox-daemon-sync.js.map +1 -1
- package/dist/timeline-summarizer.d.ts +12 -2
- package/dist/timeline-summarizer.d.ts.map +1 -1
- package/dist/timeline-summarizer.js +66 -4
- package/dist/timeline-summarizer.js.map +1 -1
- package/dist/trmnl/byos-server.d.ts +39 -0
- package/dist/trmnl/byos-server.d.ts.map +1 -0
- package/dist/trmnl/byos-server.js +301 -0
- package/dist/trmnl/byos-server.js.map +1 -0
- package/dist/trmnl/frame-cache.d.ts +37 -0
- package/dist/trmnl/frame-cache.d.ts.map +1 -0
- package/dist/trmnl/frame-cache.js +157 -0
- package/dist/trmnl/frame-cache.js.map +1 -0
- package/dist/trmnl/image-renderer.d.ts +27 -0
- package/dist/trmnl/image-renderer.d.ts.map +1 -0
- package/dist/trmnl/image-renderer.js +235 -0
- package/dist/trmnl/image-renderer.js.map +1 -0
- package/dist/trmnl/trmnl-settings.d.ts +96 -0
- package/dist/trmnl/trmnl-settings.d.ts.map +1 -0
- package/dist/trmnl/trmnl-settings.js +177 -0
- package/dist/trmnl/trmnl-settings.js.map +1 -0
- package/dist/trmnl/trmnl-telemetry.d.ts +47 -0
- package/dist/trmnl/trmnl-telemetry.d.ts.map +1 -0
- package/dist/trmnl/trmnl-telemetry.js +56 -0
- package/dist/trmnl/trmnl-telemetry.js.map +1 -0
- package/dist/whisper-server-manager.d.ts +19 -0
- package/dist/whisper-server-manager.d.ts.map +1 -0
- package/dist/whisper-server-manager.js +171 -0
- package/dist/whisper-server-manager.js.map +1 -0
- package/package.json +13 -14
- package/src/idotmatrix/sync.py +16 -1
- package/src/pysync/matrix_sync_common.py +83 -0
- package/src/timebox/sync_ble.py +16 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* D200H HID Protocol — D200-compatible 1024-byte packet protocol
|
|
3
|
+
*
|
|
4
|
+
* Packet structure:
|
|
5
|
+
* [0-1] Header: 0x7C 0x7C
|
|
6
|
+
* [2-3] Command: big-endian uint16
|
|
7
|
+
* [4-7] Length: little-endian uint32
|
|
8
|
+
* [8-1023] Payload (zero-padded)
|
|
9
|
+
*
|
|
10
|
+
* Image delivery: ZIP(manifest.json + icons/*.png) sent in chunked 1024-byte packets.
|
|
11
|
+
* ZIP quirk: bytes at offset 1016, 2040, 3064... must NOT be 0x00 or 0x7C.
|
|
12
|
+
*/
|
|
13
|
+
import { randomBytes } from 'crypto';
|
|
14
|
+
const TAG = 'd200h-hid';
|
|
15
|
+
const PACKET_SIZE = 1024;
|
|
16
|
+
const HEADER = Buffer.from([0x7c, 0x7c]);
|
|
17
|
+
const INVALID_BOUNDARY_BYTES = [0x00, 0x7c];
|
|
18
|
+
// --- Commands ---
|
|
19
|
+
export const CMD = {
|
|
20
|
+
SET_BUTTONS: 0x0001,
|
|
21
|
+
PARTIAL_UPDATE: 0x000d,
|
|
22
|
+
SET_SMALL_WINDOW: 0x0006,
|
|
23
|
+
SET_BRIGHTNESS: 0x000a,
|
|
24
|
+
SET_LABEL_STYLE: 0x000b,
|
|
25
|
+
IN_BUTTON: 0x0101,
|
|
26
|
+
IN_DEVICE_INFO: 0x0303,
|
|
27
|
+
};
|
|
28
|
+
// --- Packet building ---
|
|
29
|
+
export function buildPacket(command, payload, totalLength) {
|
|
30
|
+
const pkt = Buffer.alloc(PACKET_SIZE, 0);
|
|
31
|
+
// Header
|
|
32
|
+
pkt[0] = 0x7c;
|
|
33
|
+
pkt[1] = 0x7c;
|
|
34
|
+
// Command (big-endian uint16)
|
|
35
|
+
pkt.writeUInt16BE(command, 2);
|
|
36
|
+
// Length (little-endian uint32) — total file size for multi-packet, else payload length
|
|
37
|
+
pkt.writeUInt32LE(totalLength ?? payload.length, 4);
|
|
38
|
+
// Payload
|
|
39
|
+
payload.copy(pkt, 8, 0, Math.min(payload.length, PACKET_SIZE - 8));
|
|
40
|
+
return pkt;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Build all packets for a ZIP file transfer (SET_BUTTONS or PARTIAL_UPDATE).
|
|
44
|
+
* First packet has the header; subsequent packets are raw 1024-byte chunks.
|
|
45
|
+
*/
|
|
46
|
+
export function buildZipPackets(zipData, command = CMD.SET_BUTTONS) {
|
|
47
|
+
const packets = [];
|
|
48
|
+
const fileSize = zipData.length;
|
|
49
|
+
// First packet: header(8) + first chunk
|
|
50
|
+
const firstChunkSize = PACKET_SIZE - 8;
|
|
51
|
+
const firstChunk = zipData.subarray(0, firstChunkSize);
|
|
52
|
+
packets.push(buildPacket(command, firstChunk, fileSize));
|
|
53
|
+
// Remaining chunks (raw, no header)
|
|
54
|
+
for (let offset = firstChunkSize; offset < fileSize; offset += PACKET_SIZE) {
|
|
55
|
+
const chunk = Buffer.alloc(PACKET_SIZE, 0);
|
|
56
|
+
zipData.copy(chunk, 0, offset, Math.min(offset + PACKET_SIZE, fileSize));
|
|
57
|
+
packets.push(chunk);
|
|
58
|
+
}
|
|
59
|
+
return packets;
|
|
60
|
+
}
|
|
61
|
+
export function buildBrightnessPacket(brightness) {
|
|
62
|
+
const val = Math.max(0, Math.min(100, Math.round(brightness)));
|
|
63
|
+
return buildPacket(CMD.SET_BRIGHTNESS, Buffer.from(String(val), 'utf-8'));
|
|
64
|
+
}
|
|
65
|
+
export function buildSmallWindowPacket(mode, cpu, mem, time, gpu) {
|
|
66
|
+
const data = `${mode}|${cpu}|${mem}|${time}|${gpu}`;
|
|
67
|
+
return buildPacket(CMD.SET_SMALL_WINDOW, Buffer.from(data, 'utf-8'));
|
|
68
|
+
}
|
|
69
|
+
export function parseIncoming(data) {
|
|
70
|
+
if (data.length < 8)
|
|
71
|
+
return null;
|
|
72
|
+
if (data[0] !== 0x7c || data[1] !== 0x7c)
|
|
73
|
+
return null;
|
|
74
|
+
const command = data.readUInt16BE(2);
|
|
75
|
+
if (command === CMD.IN_BUTTON && data.length >= 12) {
|
|
76
|
+
return {
|
|
77
|
+
type: 'button',
|
|
78
|
+
data: {
|
|
79
|
+
state: data[8],
|
|
80
|
+
index: data[9],
|
|
81
|
+
pressed: data[11] === 0x01,
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
if (command === CMD.IN_DEVICE_INFO) {
|
|
86
|
+
try {
|
|
87
|
+
const jsonStr = data.subarray(8).toString('ascii').split('\0')[0];
|
|
88
|
+
const info = JSON.parse(jsonStr);
|
|
89
|
+
return {
|
|
90
|
+
type: 'device_info',
|
|
91
|
+
data: {
|
|
92
|
+
serialNumber: info.SerialNumber ?? '',
|
|
93
|
+
firmwareVersion: info.Dversion ?? '',
|
|
94
|
+
deviceType: info.DeviceType ?? '',
|
|
95
|
+
hardwareVersion: info.HardwareVersion ?? '',
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return { type: 'unknown', command, raw: data };
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return { type: 'unknown', command, raw: data };
|
|
104
|
+
}
|
|
105
|
+
// --- ZIP validation (boundary byte workaround) ---
|
|
106
|
+
/**
|
|
107
|
+
* Check if a ZIP has invalid bytes at 1024-byte chunk boundaries.
|
|
108
|
+
* At offset 1016, 2040, 3064... the byte must not be 0x00 or 0x7C.
|
|
109
|
+
*/
|
|
110
|
+
export function validateZipBoundaries(zipData) {
|
|
111
|
+
for (let i = 1016; i < zipData.length; i += PACKET_SIZE) {
|
|
112
|
+
if (INVALID_BOUNDARY_BYTES.includes(zipData[i])) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Generate a random dummy string to pad the ZIP until boundaries are valid.
|
|
120
|
+
*/
|
|
121
|
+
export function generateDummyPadding(attempt) {
|
|
122
|
+
return 'AgentDeck ' + randomBytes(8 * (attempt + 1)).toString('hex');
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=hid-protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hid-protocol.js","sourceRoot":"","sources":["../../src/d200h/hid-protocol.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* D200H Image Renderer — Renders AgentDeck state as 196×196 PNG key icons
|
|
3
|
+
* using the shared SVG renderers (same visual output as Stream Deck plugin).
|
|
4
|
+
*
|
|
5
|
+
* Pipeline: state → shared SVG generators (144×144) → resvg rasterize (196×196 PNG) → ZIP
|
|
6
|
+
*
|
|
7
|
+
* Falls back to solid-color PNGs if resvg-js is not available.
|
|
8
|
+
*/
|
|
9
|
+
import { parseState, buildButtonCommandMap } from '@agentdeck/shared';
|
|
10
|
+
export { parseState, buildButtonCommandMap };
|
|
11
|
+
export type { DashState, ButtonCommand, KeySlot } from '@agentdeck/shared';
|
|
12
|
+
/** Initialize the renderer (call once at module start). */
|
|
13
|
+
export declare function initRenderer(): Promise<void>;
|
|
14
|
+
export declare function isResvgLoaded(): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Render the full AgentDeck dashboard as a ZIP ready for SET_BUTTONS.
|
|
17
|
+
* Uses shared SVG renderers → resvg rasterization for SD+-quality output.
|
|
18
|
+
*/
|
|
19
|
+
export declare function renderDashboardZip(stateEvt: any): Buffer;
|
|
20
|
+
/**
|
|
21
|
+
* Create a simple hash of the visual state for change detection.
|
|
22
|
+
*/
|
|
23
|
+
export declare function stateHash(stateEvt: any): string;
|
|
24
|
+
//# sourceMappingURL=image-renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-renderer.d.ts","sourceRoot":"","sources":["../../src/d200h/image-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH,OAAO,EAEL,UAAU,EACV,qBAAqB,EAEtB,MAAM,mBAAmB,CAAC;AAI3B,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,CAAC;AAC7C,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AA2D3E,2DAA2D;AAC3D,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAElD;AAED,wBAAgB,aAAa,IAAI,OAAO,CAEvC;AA+LD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,GAAG,GAAG,MAAM,CAmExD;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,GAAG,GAAG,MAAM,CAI/C"}
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* D200H Image Renderer — Renders AgentDeck state as 196×196 PNG key icons
|
|
3
|
+
* using the shared SVG renderers (same visual output as Stream Deck plugin).
|
|
4
|
+
*
|
|
5
|
+
* Pipeline: state → shared SVG generators (144×144) → resvg rasterize (196×196 PNG) → ZIP
|
|
6
|
+
*
|
|
7
|
+
* Falls back to solid-color PNGs if resvg-js is not available.
|
|
8
|
+
*/
|
|
9
|
+
import { deflateSync } from 'zlib';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
import { dirname, join } from 'node:path';
|
|
12
|
+
import { existsSync } from 'node:fs';
|
|
13
|
+
// Layout/command engine is shared with the Ulanzi Studio plugin (single source
|
|
14
|
+
// of truth for the reflowing 5×3 grid). Rasterization + ZIP packing stay here.
|
|
15
|
+
import { computeLayout, parseState, buildButtonCommandMap, renderUsageWideSlot, } from '@agentdeck/shared';
|
|
16
|
+
import { debug } from '../logger.js';
|
|
17
|
+
// Re-export the engine bits the D200H module + tests import from here.
|
|
18
|
+
export { parseState, buildButtonCommandMap };
|
|
19
|
+
import { validateZipBoundaries } from './hid-protocol.js';
|
|
20
|
+
const TAG = 'd200h-render';
|
|
21
|
+
const ICON_SIZE = 196;
|
|
22
|
+
// --- Font supply for resvg ---
|
|
23
|
+
// resvg drops every <text> element unless it has a font to shape glyphs with.
|
|
24
|
+
// We load a small set of bundled OFL fonts explicitly via `fontFiles` (NOT
|
|
25
|
+
// `loadSystemFonts`, which re-scans the whole OS font tree on every `new Resvg()`
|
|
26
|
+
// — i.e. 14× per frame — and is the reason the original code set it to `false`).
|
|
27
|
+
// `defaultFontFamily` makes unresolved families in the shared SVG renderers
|
|
28
|
+
// (e.g. `Inter`, `Arial`, `monospace`) fall back to a design-system face
|
|
29
|
+
// instead of rendering nothing. Computed once at module load.
|
|
30
|
+
const FONT_OPTS = (() => {
|
|
31
|
+
try {
|
|
32
|
+
// bridge/{src,dist}/d200h/image-renderer.{ts,js} → bridge/assets/fonts
|
|
33
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
34
|
+
const fontsDir = join(here, '..', '..', 'assets', 'fonts');
|
|
35
|
+
const files = [
|
|
36
|
+
'IBMPlexSans-Regular.ttf',
|
|
37
|
+
'IBMPlexSans-Bold.ttf',
|
|
38
|
+
'JetBrainsMono-Regular.ttf',
|
|
39
|
+
'JetBrainsMono-Bold.ttf',
|
|
40
|
+
]
|
|
41
|
+
.map((n) => join(fontsDir, n))
|
|
42
|
+
.filter((p) => existsSync(p));
|
|
43
|
+
if (files.length > 0) {
|
|
44
|
+
return { fontFiles: files, loadSystemFonts: false, defaultFontFamily: 'IBM Plex Sans' };
|
|
45
|
+
}
|
|
46
|
+
debug(TAG, `bundled fonts not found under ${fontsDir} — falling back to system fonts`);
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
debug(TAG, `font path resolution failed (${err}) — falling back to system fonts`);
|
|
50
|
+
}
|
|
51
|
+
// Defense-in-depth: if bundled fonts are missing, still render text via the OS.
|
|
52
|
+
return { loadSystemFonts: true, defaultFontFamily: 'Helvetica Neue' };
|
|
53
|
+
})();
|
|
54
|
+
let Resvg = null;
|
|
55
|
+
let resvgLoaded = false;
|
|
56
|
+
async function loadResvg() {
|
|
57
|
+
if (resvgLoaded)
|
|
58
|
+
return Resvg;
|
|
59
|
+
resvgLoaded = true;
|
|
60
|
+
try {
|
|
61
|
+
const mod = await import('@resvg/resvg-js');
|
|
62
|
+
Resvg = mod.Resvg ?? mod.default?.Resvg;
|
|
63
|
+
debug(TAG, 'resvg-js loaded — SVG rendering enabled');
|
|
64
|
+
return Resvg;
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
debug(TAG, 'resvg-js not available — falling back to solid-color PNGs');
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** Initialize the renderer (call once at module start). */
|
|
72
|
+
export async function initRenderer() {
|
|
73
|
+
await loadResvg();
|
|
74
|
+
}
|
|
75
|
+
export function isResvgLoaded() {
|
|
76
|
+
return Resvg !== null;
|
|
77
|
+
}
|
|
78
|
+
// --- SVG → 196×196 PNG rasterization ---
|
|
79
|
+
function svgToPng(svg144) {
|
|
80
|
+
if (!Resvg)
|
|
81
|
+
return fallbackSolidPng(20, 20, 25); // dark fallback
|
|
82
|
+
// Wrap 144×144 SVG content into 196×196 viewport with auto-scaling
|
|
83
|
+
const inner = svg144.replace(/<\/?svg[^>]*>/g, '');
|
|
84
|
+
const wrapped = `<svg xmlns="http://www.w3.org/2000/svg" width="${ICON_SIZE}" height="${ICON_SIZE}" viewBox="0 0 144 144">${inner}</svg>`;
|
|
85
|
+
try {
|
|
86
|
+
const resvg = new Resvg(wrapped, {
|
|
87
|
+
fitTo: { mode: 'width', value: ICON_SIZE },
|
|
88
|
+
font: FONT_OPTS,
|
|
89
|
+
});
|
|
90
|
+
return Buffer.from(resvg.render().asPng());
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
debug(TAG, `SVG rasterization failed: ${err}`);
|
|
94
|
+
return fallbackSolidPng(20, 20, 25);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/** Rasterize custom-sized SVG (e.g. 288×144 → 392×196 for merged slot). */
|
|
98
|
+
function svgToPngWide(svg, width, height) {
|
|
99
|
+
if (!Resvg)
|
|
100
|
+
return fallbackSolidPng(20, 20, 25);
|
|
101
|
+
try {
|
|
102
|
+
const resvg = new Resvg(svg, {
|
|
103
|
+
fitTo: { mode: 'width', value: width },
|
|
104
|
+
font: FONT_OPTS,
|
|
105
|
+
});
|
|
106
|
+
return Buffer.from(resvg.render().asPng());
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
debug(TAG, `Wide SVG rasterization failed: ${err}`);
|
|
110
|
+
return fallbackSolidPng(20, 20, 25);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// --- ZIP creation (reused from original, with boundary validation) ---
|
|
114
|
+
function crc32(buf) {
|
|
115
|
+
let crc = 0xffffffff;
|
|
116
|
+
for (let i = 0; i < buf.length; i++) {
|
|
117
|
+
crc = crcTable[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8);
|
|
118
|
+
}
|
|
119
|
+
return (crc ^ 0xffffffff) >>> 0;
|
|
120
|
+
}
|
|
121
|
+
const crcTable = new Uint32Array(256);
|
|
122
|
+
for (let n = 0; n < 256; n++) {
|
|
123
|
+
let c = n;
|
|
124
|
+
for (let k = 0; k < 8; k++) {
|
|
125
|
+
c = (c & 1) ? (0xedb88320 ^ (c >>> 1)) : (c >>> 1);
|
|
126
|
+
}
|
|
127
|
+
crcTable[n] = c;
|
|
128
|
+
}
|
|
129
|
+
function normalizeExtraLength(length) {
|
|
130
|
+
if (length <= 0)
|
|
131
|
+
return 0;
|
|
132
|
+
return Math.max(4, length);
|
|
133
|
+
}
|
|
134
|
+
function makeZipExtraField(length) {
|
|
135
|
+
const normalized = normalizeExtraLength(length);
|
|
136
|
+
if (normalized === 0)
|
|
137
|
+
return Buffer.alloc(0);
|
|
138
|
+
const extra = Buffer.alloc(normalized, 0x41);
|
|
139
|
+
extra.writeUInt16LE(0x4141, 0);
|
|
140
|
+
extra.writeUInt16LE(Math.max(0, normalized - 4), 2);
|
|
141
|
+
return extra;
|
|
142
|
+
}
|
|
143
|
+
function firstInvalidZipBoundaryOffset(zipData) {
|
|
144
|
+
for (let i = 1016; i < zipData.length; i += 1024) {
|
|
145
|
+
if (zipData[i] === 0x00 || zipData[i] === 0x7c)
|
|
146
|
+
return i;
|
|
147
|
+
}
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
function createZipInMemory(files, extraLengths = []) {
|
|
151
|
+
const centralDir = [];
|
|
152
|
+
const localParts = [];
|
|
153
|
+
const layouts = [];
|
|
154
|
+
let offset = 0;
|
|
155
|
+
let index = 0;
|
|
156
|
+
for (const [name, data] of files) {
|
|
157
|
+
const nameBytes = Buffer.from(name, 'utf-8');
|
|
158
|
+
const crc = crc32(data);
|
|
159
|
+
const extraLen = normalizeExtraLength(extraLengths[index] ?? 0);
|
|
160
|
+
const extra = makeZipExtraField(extraLen);
|
|
161
|
+
const localExtraOffset = offset + 30 + nameBytes.length;
|
|
162
|
+
const local = Buffer.alloc(30 + nameBytes.length + extra.length);
|
|
163
|
+
local.writeUInt32LE(0x04034b50, 0);
|
|
164
|
+
local.writeUInt16LE(20, 4);
|
|
165
|
+
local.writeUInt16LE(0, 6);
|
|
166
|
+
local.writeUInt16LE(0, 8);
|
|
167
|
+
local.writeUInt16LE(0, 10);
|
|
168
|
+
local.writeUInt16LE(0, 12);
|
|
169
|
+
local.writeUInt32LE(crc, 14);
|
|
170
|
+
local.writeUInt32LE(data.length, 18);
|
|
171
|
+
local.writeUInt32LE(data.length, 22);
|
|
172
|
+
local.writeUInt16LE(nameBytes.length, 26);
|
|
173
|
+
local.writeUInt16LE(extra.length, 28);
|
|
174
|
+
nameBytes.copy(local, 30);
|
|
175
|
+
extra.copy(local, 30 + nameBytes.length);
|
|
176
|
+
const central = Buffer.alloc(46 + nameBytes.length + extra.length);
|
|
177
|
+
central.writeUInt32LE(0x02014b50, 0);
|
|
178
|
+
central.writeUInt16LE(20, 4);
|
|
179
|
+
central.writeUInt16LE(20, 6);
|
|
180
|
+
central.writeUInt16LE(0, 8);
|
|
181
|
+
central.writeUInt16LE(0, 10);
|
|
182
|
+
central.writeUInt16LE(0, 12);
|
|
183
|
+
central.writeUInt16LE(0, 14);
|
|
184
|
+
central.writeUInt32LE(crc, 16);
|
|
185
|
+
central.writeUInt32LE(data.length, 20);
|
|
186
|
+
central.writeUInt32LE(data.length, 24);
|
|
187
|
+
central.writeUInt16LE(nameBytes.length, 28);
|
|
188
|
+
central.writeUInt16LE(extra.length, 30);
|
|
189
|
+
central.writeUInt16LE(0, 32);
|
|
190
|
+
central.writeUInt16LE(0, 34);
|
|
191
|
+
central.writeUInt16LE(0, 36);
|
|
192
|
+
central.writeUInt32LE(0, 38);
|
|
193
|
+
central.writeUInt32LE(offset, 42);
|
|
194
|
+
nameBytes.copy(central, 46);
|
|
195
|
+
extra.copy(central, 46 + nameBytes.length);
|
|
196
|
+
localParts.push(local, data);
|
|
197
|
+
centralDir.push(central);
|
|
198
|
+
layouts.push({ extraInsertOffset: localExtraOffset });
|
|
199
|
+
offset += local.length + data.length;
|
|
200
|
+
index += 1;
|
|
201
|
+
}
|
|
202
|
+
const centralDirData = Buffer.concat(centralDir);
|
|
203
|
+
const eocd = Buffer.alloc(22);
|
|
204
|
+
eocd.writeUInt32LE(0x06054b50, 0);
|
|
205
|
+
eocd.writeUInt16LE(0, 4);
|
|
206
|
+
eocd.writeUInt16LE(0, 6);
|
|
207
|
+
eocd.writeUInt16LE(files.size, 8);
|
|
208
|
+
eocd.writeUInt16LE(files.size, 10);
|
|
209
|
+
eocd.writeUInt32LE(centralDirData.length, 12);
|
|
210
|
+
eocd.writeUInt32LE(offset, 16);
|
|
211
|
+
eocd.writeUInt16LE(0, 20);
|
|
212
|
+
return { zip: Buffer.concat([...localParts, centralDirData, eocd]), layouts };
|
|
213
|
+
}
|
|
214
|
+
// --- Fallback solid-color PNG (when resvg-js unavailable) ---
|
|
215
|
+
function fallbackSolidPng(r, g, b) {
|
|
216
|
+
const w = ICON_SIZE, h = ICON_SIZE;
|
|
217
|
+
const ihdr = Buffer.alloc(13);
|
|
218
|
+
ihdr.writeUInt32BE(w, 0);
|
|
219
|
+
ihdr.writeUInt32BE(h, 4);
|
|
220
|
+
ihdr[8] = 8;
|
|
221
|
+
ihdr[9] = 2; // 8-bit RGB
|
|
222
|
+
const rowLen = 1 + w * 3;
|
|
223
|
+
const raw = Buffer.alloc(rowLen * h);
|
|
224
|
+
for (let y = 0; y < h; y++) {
|
|
225
|
+
const off = y * rowLen;
|
|
226
|
+
raw[off] = 0;
|
|
227
|
+
for (let x = 0; x < w; x++) {
|
|
228
|
+
const px = off + 1 + x * 3;
|
|
229
|
+
raw[px] = r;
|
|
230
|
+
raw[px + 1] = g;
|
|
231
|
+
raw[px + 2] = b;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
const compressed = deflateSync(raw);
|
|
235
|
+
const sig = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
|
|
236
|
+
function pngChunk(type, data) {
|
|
237
|
+
const typeBytes = Buffer.from(type, 'ascii');
|
|
238
|
+
const len = Buffer.alloc(4);
|
|
239
|
+
len.writeUInt32BE(data.length, 0);
|
|
240
|
+
const crcData = Buffer.concat([typeBytes, data]);
|
|
241
|
+
const crc = Buffer.alloc(4);
|
|
242
|
+
crc.writeUInt32BE(crc32(crcData), 0);
|
|
243
|
+
return Buffer.concat([len, typeBytes, data, crc]);
|
|
244
|
+
}
|
|
245
|
+
return Buffer.concat([sig, pngChunk('IHDR', ihdr), pngChunk('IDAT', compressed), pngChunk('IEND', Buffer.alloc(0))]);
|
|
246
|
+
}
|
|
247
|
+
// --- Public API ---
|
|
248
|
+
/**
|
|
249
|
+
* Render the full AgentDeck dashboard as a ZIP ready for SET_BUTTONS.
|
|
250
|
+
* Uses shared SVG renderers → resvg rasterization for SD+-quality output.
|
|
251
|
+
*/
|
|
252
|
+
export function renderDashboardZip(stateEvt) {
|
|
253
|
+
const state = parseState(stateEvt);
|
|
254
|
+
const layout = computeLayout(state);
|
|
255
|
+
const manifest = {};
|
|
256
|
+
const files = new Map();
|
|
257
|
+
for (let i = 0; i < layout.length; i++) {
|
|
258
|
+
const slot = layout[i];
|
|
259
|
+
const iconPath = `icons/btn${i}.png`;
|
|
260
|
+
const colRow = `${slot.col}_${slot.row}`;
|
|
261
|
+
const png = svgToPng(slot.svg);
|
|
262
|
+
files.set(iconPath, png);
|
|
263
|
+
manifest[colRow] = {
|
|
264
|
+
State: 0,
|
|
265
|
+
ViewParam: [{ Text: slot.label, Icon: iconPath }],
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
// Merged hardware slot (3_2) — 392×196 PNG with StreamDeck-style usage display
|
|
269
|
+
// No Action = device firmware clock overlay suppressed (solves clock overlap)
|
|
270
|
+
const wideUsageSvg = renderUsageWideSlot(state.fiveHourPercent, state.sevenDayPercent, state.usageKnown !== false);
|
|
271
|
+
const wideUsagePng = svgToPngWide(wideUsageSvg, 392, 196);
|
|
272
|
+
files.set('icons/usage-wide.png', wideUsagePng);
|
|
273
|
+
manifest['3_2'] = {
|
|
274
|
+
State: 0,
|
|
275
|
+
ViewParam: [{ Icon: 'icons/usage-wide.png', Text: '' }],
|
|
276
|
+
};
|
|
277
|
+
files.set('manifest.json', Buffer.from(JSON.stringify(manifest), 'utf-8'));
|
|
278
|
+
// Build ZIP with boundary validation
|
|
279
|
+
const orderedEntries = [...files.entries()];
|
|
280
|
+
const extraLengths = new Array(orderedEntries.length).fill(0);
|
|
281
|
+
for (let attempt = 0; attempt < 256; attempt++) {
|
|
282
|
+
const artifact = createZipInMemory(new Map(orderedEntries), extraLengths);
|
|
283
|
+
const invalidOffset = firstInvalidZipBoundaryOffset(artifact.zip);
|
|
284
|
+
if (invalidOffset == null)
|
|
285
|
+
return artifact.zip;
|
|
286
|
+
let targetIndex = -1;
|
|
287
|
+
for (let i = artifact.layouts.length - 1; i >= 0; i--) {
|
|
288
|
+
if (artifact.layouts[i].extraInsertOffset <= invalidOffset) {
|
|
289
|
+
targetIndex = i;
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
if (targetIndex < 0)
|
|
294
|
+
return artifact.zip;
|
|
295
|
+
const currentExtra = extraLengths[targetIndex];
|
|
296
|
+
const extraInsertOffset = artifact.layouts[targetIndex].extraInsertOffset;
|
|
297
|
+
let shift = 1;
|
|
298
|
+
while (shift <= 512) {
|
|
299
|
+
if (invalidOffset < extraInsertOffset + currentExtra + shift)
|
|
300
|
+
break;
|
|
301
|
+
const candidate = artifact.zip[invalidOffset - shift];
|
|
302
|
+
if (candidate !== 0x00 && candidate !== 0x7c)
|
|
303
|
+
break;
|
|
304
|
+
shift += 1;
|
|
305
|
+
}
|
|
306
|
+
extraLengths[targetIndex] = normalizeExtraLength(extraLengths[targetIndex] + shift);
|
|
307
|
+
debug(TAG, `ZIP boundary invalid at ${invalidOffset}, shifting entry ${targetIndex} by ${shift} byte(s)`);
|
|
308
|
+
}
|
|
309
|
+
const fallback = createZipInMemory(new Map(orderedEntries), extraLengths).zip;
|
|
310
|
+
debug(TAG, `WARNING: ZIP boundary validation failed after search; stillValid=${validateZipBoundaries(fallback)}`);
|
|
311
|
+
return fallback;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Create a simple hash of the visual state for change detection.
|
|
315
|
+
*/
|
|
316
|
+
export function stateHash(stateEvt) {
|
|
317
|
+
const s = parseState(stateEvt);
|
|
318
|
+
const sessIds = s.allSessions.map(sess => sess.id).join(',');
|
|
319
|
+
return `${s.state}|${s.mode}|${s.projectName}|${s.modelName}|${s.fiveHourPercent}|${s.sevenDayPercent}|${s.totalTokens}|${s.totalCost}|${s.options.map(o => o.label).join(',')}|${s.currentTool}|${sessIds}`;
|
|
320
|
+
}
|
|
321
|
+
//# sourceMappingURL=image-renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-renderer.js","sourceRoot":"","sources":["../../src/d200h/image-renderer.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"daemon-server.d.ts","sourceRoot":"","sources":["../src/daemon-server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,SAAS,MAAM,IAAI,CAAC;AAC3B,OAAO,EAAE,UAAU,EAA8B,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"daemon-server.d.ts","sourceRoot":"","sources":["../src/daemon-server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,SAAS,MAAM,IAAI,CAAC;AAC3B,OAAO,EAAE,UAAU,EAA8B,MAAM,kBAAkB,CAAC;AA6F1E,OAAO,EAA8D,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AA6P/G;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,EAC5C,YAAY,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAClD,OAAO,CAST;AAkBD,iBAAS,iBAAiB,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE,SAAS,GAAG,IAAI,CA+C1E;AAYD,iBAAS,yBAAyB,CAAC,EAAE,EAAE,SAAS,GAAG,IAAI,CAStD;AAED,iBAAS,mBAAmB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAsBlE;AA6ED,gBAAgB;AAChB,wBAAgB,uBAAuB,CAAC,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAKzH;AACD,gBAAgB;AAChB,wBAAgB,wBAAwB,IAAI,IAAI,CAS/C;AACD,gBAAgB;AAChB,eAAO,MAAM,gBAAgB;;;;;CAK5B,CAAC;AAEF,iBAAe,mBAAmB,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CA8F3H;AAoHD,iEAAiE;AACjE,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAsBtF;AAED;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CAAC,CAAC,SAAS;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,EACnH,KAAK,EAAE,CAAC,GACP,CAAC,CAWH;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,aAAa,GAAG,WAAW,GAAG,UAAU,GAAG,aAAa,CAAA;CAAE,CAa3F;AAID,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AA+HD,wBAAsB,WAAW,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA8zHpE"}
|
package/dist/daemon-server.js
CHANGED
|
@@ -46,6 +46,13 @@ const OBSERVED_APPROVAL_ENABLED = process.env.AGENTDECK_OBSERVED_APPROVAL !== '0
|
|
|
46
46
|
* call to Claude's own permission flow. Must stay well under the hook curl's
|
|
47
47
|
* --max-time 60 so the release reaches Claude before curl gives up. */
|
|
48
48
|
const OBSERVED_APPROVAL_HOLD_MS = Math.min(50_000, Math.max(5_000, Number(process.env.AGENTDECK_APPROVAL_HOLD_MS) || 25_000));
|
|
49
|
+
/** Inactivity after which an unclosed APME run is treated as abandoned and
|
|
50
|
+
* finalized. Deliberately generous: an orphan stays reapable forever, so
|
|
51
|
+
* closing it late costs nothing, while reaping a live run corrupts the unit
|
|
52
|
+
* being measured. Session bridges own their own collector state and are
|
|
53
|
+
* invisible to this daemon, so only elapsed silence protects them (runs this
|
|
54
|
+
* daemon still holds are skipped via `collector.isLiveRun`). */
|
|
55
|
+
const APME_ABANDONED_RUN_STALE_SEC = Math.max(600, Number(process.env.AGENTDECK_APME_ABANDON_SEC) || 7200);
|
|
49
56
|
import { VoiceManager } from './voice.js';
|
|
50
57
|
import { VoiceAssistantManager } from './voice-assistant.js';
|
|
51
58
|
import { listActive as listActiveSessions, findAvailablePort, findExistingDaemon, DAEMON_DEFAULT_PORT, probeDaemonHealth, requestDaemonShutdown, scanDaemonPortWindow, shouldConcedePortToOccupant, waitForDaemonExit, writeDaemonInfo, ensureDaemonInfo, removeDaemonInfo, readDaemonInfo, removeDaemonSession, getCandidateDataDirs, getOwnTimelineFile, } from './session-registry.js';
|
|
@@ -788,8 +795,16 @@ function buildNodeModuleHealth(startedModules) {
|
|
|
788
795
|
modules.timebox = timebox.statusSnapshot();
|
|
789
796
|
}
|
|
790
797
|
else if (configuredTimebox.length > 0) {
|
|
798
|
+
// Configured but the module never started — say so explicitly. An omitted
|
|
799
|
+
// `connected` reads as "no information" to retain-on-absent clients, and a
|
|
800
|
+
// panel nobody is driving must not inherit a stale connected:true.
|
|
791
801
|
modules.timebox = {
|
|
792
802
|
configuredDeviceCount: configuredTimebox.length,
|
|
803
|
+
connected: false,
|
|
804
|
+
statusReason: 'sync module not started',
|
|
805
|
+
displayDimmed: false,
|
|
806
|
+
hasFrame: false,
|
|
807
|
+
lastError: null,
|
|
793
808
|
devices: configuredTimebox.map((d) => ({
|
|
794
809
|
address: d.address,
|
|
795
810
|
name: d.name ?? 'Timebox Mini',
|
|
@@ -803,8 +818,14 @@ function buildNodeModuleHealth(startedModules) {
|
|
|
803
818
|
modules.idotmatrix = idotmatrix.statusSnapshot();
|
|
804
819
|
}
|
|
805
820
|
else if (configuredIDotMatrix.length > 0) {
|
|
821
|
+
// Configured but the module never started — see the Timebox note above.
|
|
806
822
|
modules.idotmatrix = {
|
|
807
823
|
configuredDeviceCount: configuredIDotMatrix.length,
|
|
824
|
+
connected: false,
|
|
825
|
+
statusReason: 'sync module not started',
|
|
826
|
+
displayDimmed: false,
|
|
827
|
+
hasFrame: false,
|
|
828
|
+
lastError: null,
|
|
808
829
|
devices: configuredIDotMatrix.map((d) => ({
|
|
809
830
|
address: d.address,
|
|
810
831
|
name: d.name ?? 'iDotMatrix',
|
|
@@ -1998,6 +2019,27 @@ export async function startDaemon(opts) {
|
|
|
1998
2019
|
const respRaw = responseText.length > 0
|
|
1999
2020
|
? cleanRawText(responseText.length > 200 ? responseText.slice(0, 197) + '...' : responseText)
|
|
2000
2021
|
: '';
|
|
2022
|
+
// APME: the text below is the assistant half of the turn — hand it to
|
|
2023
|
+
// the collector on the SAME branch that decides the timeline row.
|
|
2024
|
+
// `setTurnResponse` was reachable only from the PTY session bridge
|
|
2025
|
+
// (bridge/src/index.ts), which hook-observed sessions (direct
|
|
2026
|
+
// `claude` / `codex` / standalone `opencode`) never go through, so
|
|
2027
|
+
// every such turn archived a prompt and its tool calls but no reply:
|
|
2028
|
+
// `turns.response` stayed NULL and no `assistant_message` trajectory
|
|
2029
|
+
// event was ever written. The dashboard therefore could not replay a
|
|
2030
|
+
// conversation the timeline shows in full, and the judge scored those
|
|
2031
|
+
// turns against silence. Empty text is still worth recording — it
|
|
2032
|
+
// tags `response_kind` tool_only/empty so the runner skips judging.
|
|
2033
|
+
if (apme) {
|
|
2034
|
+
if (turnOpen)
|
|
2035
|
+
apme.collector.setTurnResponse(hookSid, responseText);
|
|
2036
|
+
else if (responseText.length > 0) {
|
|
2037
|
+
// No open turn: a missed prompt hook or a late/duplicate stop.
|
|
2038
|
+
// The last-closed fallback refuses to overwrite an existing
|
|
2039
|
+
// response, so a duplicate stop is a no-op instead of a clobber.
|
|
2040
|
+
apme.collector.setLastClosedTurnResponse(hookSid, responseText);
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2001
2043
|
if (turnOpen && lastStart) {
|
|
2002
2044
|
const now = Date.now();
|
|
2003
2045
|
const taskId = (apme
|
|
@@ -4723,6 +4765,43 @@ export async function startDaemon(opts) {
|
|
|
4723
4765
|
for (const id of orphans) {
|
|
4724
4766
|
apme.store.updateRun(id, { endedAt: Date.now(), taskCategory: '_empty' });
|
|
4725
4767
|
}
|
|
4768
|
+
// 5. Reap ABANDONED runs — the ones step 4 cannot see. A daemon restart
|
|
4769
|
+
// (or crash) drops the in-memory session→run map, so every run that
|
|
4770
|
+
// was mid-session is left with ended_at NULL forever. Those carry real
|
|
4771
|
+
// prompts and turns, so they fail step 4's empty-shell predicate, and
|
|
4772
|
+
// because their TASK also never closes they are never evaluated: the
|
|
4773
|
+
// store had accumulated 65 open tasks against 9 closed ones.
|
|
4774
|
+
// Closing the run makes step 1 pick it up next tick; the task eval has
|
|
4775
|
+
// to be enqueued here, since its normal trigger (`onTaskClosed`) fires
|
|
4776
|
+
// from the collector's in-memory close path, which is exactly what
|
|
4777
|
+
// went missing.
|
|
4778
|
+
// Batched small: `enqueueTask` dispatches immediately with no
|
|
4779
|
+
// concurrency cap, so a backlog is drained a few per tick rather than
|
|
4780
|
+
// fired at the judge all at once.
|
|
4781
|
+
const abandoned = apme.store.listAbandonedRuns(APME_ABANDONED_RUN_STALE_SEC, 5);
|
|
4782
|
+
for (const run of abandoned) {
|
|
4783
|
+
if (apme.collector.isLiveRun(run.id))
|
|
4784
|
+
continue; // still owned by us
|
|
4785
|
+
const closedTasks = apme.store.reapAbandonedRun(run.id, run.lastActivity);
|
|
4786
|
+
for (const task of closedTasks) {
|
|
4787
|
+
// Judge only what there is something to judge. The backlog this
|
|
4788
|
+
// reaper drains predates the response-capture fix, so most of those
|
|
4789
|
+
// tasks hold prompts and tool calls but no reply — scoring them would
|
|
4790
|
+
// push hundreds of "judged against silence" rows into the scorecard,
|
|
4791
|
+
// the same noise `response_kind` exists to keep out. Closing the row
|
|
4792
|
+
// is the data-hygiene win; the eval is a bonus when it can be earned.
|
|
4793
|
+
const hasReply = apme.store.listTurnsForTask(task.id)
|
|
4794
|
+
.some((t) => typeof t.response === 'string' && t.response.trim().length > 0);
|
|
4795
|
+
if (!hasReply)
|
|
4796
|
+
continue;
|
|
4797
|
+
apme.runner.enqueueTask({
|
|
4798
|
+
runId: run.id, taskId: task.id,
|
|
4799
|
+
...(task.category ? { category: task.category } : {}),
|
|
4800
|
+
boundarySignal: 'orphaned',
|
|
4801
|
+
});
|
|
4802
|
+
}
|
|
4803
|
+
debug('APME', `reaped abandoned run ${run.id.slice(0, 8)} — ${closedTasks.length} task(s) closed`);
|
|
4804
|
+
}
|
|
4726
4805
|
}, 30_000); // every 30s
|
|
4727
4806
|
core.addInterval(apmeEvalTimer);
|
|
4728
4807
|
}
|