@lawaty/lacode 1.3.0-beta.2 → 1.3.0-beta.4
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/CHANGELOG.md +69 -0
- package/dist/index.js +41 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,75 @@ All notable changes to LaCode are recorded here. The format follows
|
|
|
7
7
|
Versions come from git release commits; the npm version is bumped in the same
|
|
8
8
|
commit as the changelog entry it describes.
|
|
9
9
|
|
|
10
|
+
## [1.3.0-beta.4] — session events count as life, and the timeline builds on idle
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **The hang reaper now treats opencode session and task events as proof of
|
|
15
|
+
life.** A pooled claim was only kept off the silence clock by message traffic.
|
|
16
|
+
`session.status` (`busy`/`retry`), `session.updated`, `todo.updated`,
|
|
17
|
+
`session.compacted`, `permission.asked` and `question.asked` now refresh the
|
|
18
|
+
same activity clock, so a session opencode is actively working on is not
|
|
19
|
+
condemned as hung. The two existing bounds are unchanged: a genuinely silent
|
|
20
|
+
task is still reaped after the idle window, and `MAX_CLAIM_AGE_MS` still kills
|
|
21
|
+
a task that never goes quiet.
|
|
22
|
+
- **The timeline is built when a session goes idle, not only after the
|
|
23
|
+
five-minute debounce.** Pending work is flushed on `session.idle`, so a short
|
|
24
|
+
or one-shot run gets a timeline before the process can exit. The timer remains
|
|
25
|
+
the debounce for an active session.
|
|
26
|
+
- **The timeline keeps the session title.** Only the first tick fetched session
|
|
27
|
+
info; every later summarizer prompt said "Session: untitled". The title is now
|
|
28
|
+
carried on the per-session state.
|
|
29
|
+
- **A failed timeline summarization is logged with a level opencode accepts.**
|
|
30
|
+
The call passed the message as the log *level*, so the runtime rejected the
|
|
31
|
+
payload (`Expected "debug" | "info" | "error" | "warn"`) and the failure was
|
|
32
|
+
invisible. Failures now log at `warn` with a message, and the no-bullets line
|
|
33
|
+
at `debug`.
|
|
34
|
+
|
|
35
|
+
## [1.3.0-beta.3] — the 1.3.0-beta.2 notes
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
|
|
39
|
+
- **Republished so the published tarball carries the 1.3.0-beta.2 changelog
|
|
40
|
+
entry.** No runtime change: the code is identical to 1.3.0-beta.2.
|
|
41
|
+
|
|
42
|
+
## [1.3.0-beta.2] — a reason that survives, and a timeline that ships
|
|
43
|
+
|
|
44
|
+
### Added
|
|
45
|
+
|
|
46
|
+
- **A durable failure journal.** Every failed or cancelled spawn appends one JSON
|
|
47
|
+
line to `failures.jsonl` in the pool state directory: timestamp, source, base,
|
|
48
|
+
slot, model, reason, elapsed time and the error text. `pool_status` prints the
|
|
49
|
+
last five under `recent failures:`, so a slot that dies without a cooldown is
|
|
50
|
+
no longer invisible afterwards.
|
|
51
|
+
|
|
52
|
+
### Changed
|
|
53
|
+
|
|
54
|
+
- **A spawn the pool did not abort now records why it died.** An unclassified
|
|
55
|
+
`session.error`, or an assistant message error matching no known class, used to
|
|
56
|
+
release the claim and say nothing. It now records the slot, model and error
|
|
57
|
+
text, and the cancelled task card is rewritten with that reason. A stop the
|
|
58
|
+
user presses still produces no record and no rewrite, because it raises no
|
|
59
|
+
session error.
|
|
60
|
+
|
|
61
|
+
### Fixed
|
|
62
|
+
|
|
63
|
+
- **The cancelled-task rewrite was unauthenticated.** It hand-rolled a `fetch`
|
|
64
|
+
PATCH and read `OPENCODE_SERVER_PASSWORD` from the plugin's own environment —
|
|
65
|
+
but that variable exists only in the server process, so the request always went
|
|
66
|
+
out unauthenticated and a password-protected server answered `401`. The reason
|
|
67
|
+
was then dropped and the caller saw a bare `Task cancelled`. The rewrite now
|
|
68
|
+
goes through the injected client's part update, which is already authenticated;
|
|
69
|
+
the raw request remains only as a fallback for a runtime without it.
|
|
70
|
+
- **The timeline sidebar was never bundled.** `src/tui/timeline.tsx` was added in
|
|
71
|
+
1.2.0 but never imported by the TUI entry point, so `dist/tui.js` contained no
|
|
72
|
+
reference to it and no published version shipped the panel: it worked only on a
|
|
73
|
+
machine whose `tui.json` pointed at the raw source file. The TUI entry now
|
|
74
|
+
composes it, `solid-js` and `@opentui/solid` are treated as host-provided peers
|
|
75
|
+
instead of being bundled, and the automatic JSX transform is enabled so the
|
|
76
|
+
`@opentui/solid` pragma is honoured rather than silently compiling to
|
|
77
|
+
`React.createElement`.
|
|
78
|
+
|
|
10
79
|
## [1.3.0-beta.0] — provider readiness, timeline roots, and a reason on cancelled tasks
|
|
11
80
|
|
|
12
81
|
### Added
|
package/dist/index.js
CHANGED
|
@@ -3017,9 +3017,13 @@ var agentPoolHooks = (async ({ client, serverUrl, directory }, options) => {
|
|
|
3017
3017
|
event: async ({ event }) => {
|
|
3018
3018
|
try {
|
|
3019
3019
|
const props = event.properties ?? {};
|
|
3020
|
+
const noteLive = (sessionID) => {
|
|
3021
|
+
if (typeof sessionID === "string" && sessionID) noteActivity(s, sessionID);
|
|
3022
|
+
};
|
|
3020
3023
|
if (event.type === "session.status") {
|
|
3021
3024
|
const sessionID = typeof props.sessionID === "string" ? props.sessionID : void 0;
|
|
3022
3025
|
const status2 = props.status;
|
|
3026
|
+
if (status2?.type === "busy" || status2?.type === "retry") noteLive(sessionID);
|
|
3023
3027
|
const action = status2?.action;
|
|
3024
3028
|
const provider = typeof action?.provider === "string" ? action.provider : void 0;
|
|
3025
3029
|
const reason2 = action?.reason ?? "";
|
|
@@ -3193,7 +3197,10 @@ var agentPoolHooks = (async ({ client, serverUrl, directory }, options) => {
|
|
|
3193
3197
|
}
|
|
3194
3198
|
if (event.type === "session.created") {
|
|
3195
3199
|
const info2 = props.info;
|
|
3196
|
-
if (info2?.id && info2.parentID)
|
|
3200
|
+
if (info2?.id && info2.parentID) {
|
|
3201
|
+
bindChild(s, info2.parentID, info2.id);
|
|
3202
|
+
noteLive(info2.id);
|
|
3203
|
+
}
|
|
3197
3204
|
return;
|
|
3198
3205
|
}
|
|
3199
3206
|
if (event.type === "message.updated" || event.type === "message.part.updated") {
|
|
@@ -3235,6 +3242,18 @@ var agentPoolHooks = (async ({ client, serverUrl, directory }, options) => {
|
|
|
3235
3242
|
}
|
|
3236
3243
|
return;
|
|
3237
3244
|
}
|
|
3245
|
+
if (event.type === "session.updated") {
|
|
3246
|
+
noteLive(props.info?.id);
|
|
3247
|
+
return;
|
|
3248
|
+
}
|
|
3249
|
+
if (event.type === "todo.updated" || event.type === "session.compacted") {
|
|
3250
|
+
noteLive(props.sessionID);
|
|
3251
|
+
return;
|
|
3252
|
+
}
|
|
3253
|
+
if (event.type === "permission.asked" || event.type === "question.asked") {
|
|
3254
|
+
noteLive(props.sessionID);
|
|
3255
|
+
return;
|
|
3256
|
+
}
|
|
3238
3257
|
if (event.type !== "message.updated") return;
|
|
3239
3258
|
const info = props.info;
|
|
3240
3259
|
if (!info || info.role !== "assistant" || !info.error) return;
|
|
@@ -6770,6 +6789,18 @@ function timelineHooks(input, options) {
|
|
|
6770
6789
|
if (state2.timer || state2.running) return;
|
|
6771
6790
|
schedule(id, state2);
|
|
6772
6791
|
};
|
|
6792
|
+
function flush(id) {
|
|
6793
|
+
if (disabled || disposed) return;
|
|
6794
|
+
const state2 = sessions.get(id);
|
|
6795
|
+
if (!state2) return;
|
|
6796
|
+
if (state2.root === false) return;
|
|
6797
|
+
if (!state2.pending || state2.running) return;
|
|
6798
|
+
if (state2.timer) {
|
|
6799
|
+
clearTimeout(state2.timer);
|
|
6800
|
+
state2.timer = void 0;
|
|
6801
|
+
}
|
|
6802
|
+
void tick(id, state2);
|
|
6803
|
+
}
|
|
6773
6804
|
function schedule(id, state2) {
|
|
6774
6805
|
if (state2.timer) clearTimeout(state2.timer);
|
|
6775
6806
|
state2.timer = setTimeout(() => {
|
|
@@ -6866,7 +6897,7 @@ function timelineHooks(input, options) {
|
|
|
6866
6897
|
return { ok: true, bullets: [] };
|
|
6867
6898
|
} else {
|
|
6868
6899
|
if (process.env.LACODE_TIMELINE_DEBUG === "1") {
|
|
6869
|
-
log("no bullet lines", { sessionID: id, text: text.slice(0, 300) });
|
|
6900
|
+
log("debug", "timeline: no bullet lines", { sessionID: id, text: text.slice(0, 300) });
|
|
6870
6901
|
}
|
|
6871
6902
|
return { ok: false, bullets: [], error: "no bullet lines" };
|
|
6872
6903
|
}
|
|
@@ -6894,6 +6925,7 @@ function timelineHooks(input, options) {
|
|
|
6894
6925
|
return;
|
|
6895
6926
|
}
|
|
6896
6927
|
title = info.title;
|
|
6928
|
+
state2.title = info.title ?? state2.title;
|
|
6897
6929
|
state2.root = !info.parentID && !isInternalTitle(info.title);
|
|
6898
6930
|
if (!state2.root) {
|
|
6899
6931
|
sessions.delete(id);
|
|
@@ -6906,14 +6938,14 @@ function timelineHooks(input, options) {
|
|
|
6906
6938
|
if (!excerpt.text) {
|
|
6907
6939
|
return;
|
|
6908
6940
|
}
|
|
6909
|
-
const result = await summarize(id,
|
|
6941
|
+
const result = await summarize(id, state2.title ?? info?.title ?? "untitled", excerpt.text, readEntries(id, 4));
|
|
6910
6942
|
if (result.ok) {
|
|
6911
6943
|
if (result.bullets.length) appendEntries(id, result.bullets, Date.now(), excerpt.next);
|
|
6912
6944
|
state2.cursor = excerpt.next;
|
|
6913
6945
|
state2.failures = 0;
|
|
6914
6946
|
} else {
|
|
6915
6947
|
state2.failures++;
|
|
6916
|
-
log("summarize failed", { sessionID: id, error: result.error });
|
|
6948
|
+
log("warn", "timeline: summarize failed", { sessionID: id, error: result.error });
|
|
6917
6949
|
}
|
|
6918
6950
|
} catch (error) {
|
|
6919
6951
|
debug("tick failed", { sessionID: id, error });
|
|
@@ -7021,6 +7053,11 @@ function timelineHooks(input, options) {
|
|
|
7021
7053
|
event: async ({ event }) => {
|
|
7022
7054
|
try {
|
|
7023
7055
|
switch (event.type) {
|
|
7056
|
+
case "session.idle": {
|
|
7057
|
+
const id = event.properties.sessionID;
|
|
7058
|
+
if (id) flush(id);
|
|
7059
|
+
return;
|
|
7060
|
+
}
|
|
7024
7061
|
case "message.updated": {
|
|
7025
7062
|
const id = event.properties.info?.sessionID;
|
|
7026
7063
|
if (!id) return;
|
package/package.json
CHANGED