kernelpm 0.1.10 → 0.1.12

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.
@@ -116,6 +116,16 @@ class ServeOpencodeDriver {
116
116
  status = 'starting';
117
117
  /** Timestamp (ms) of the last `working` emit; null when not working. */
118
118
  workingSince = null;
119
+ /**
120
+ * True when no assistant turn is in flight. Arms the next `session.status=
121
+ * busy` handler: opencode's SSE stream occasionally emits a stray `busy`
122
+ * event AFTER the assistant message has already completed (and we've emit
123
+ * `awaiting_input`). Honoring it would flip the status back to `working`
124
+ * right after a finished turn, leaving the app stuck on "escribiendo…"
125
+ * until the watchdog's hard timeout (3 min). We only treat `busy` as
126
+ * genuinely working when the user has armed it by sending a prompt.
127
+ */
128
+ turnDone = true;
119
129
  constructor(sessionId, cwd, title, opts, cb, initial) {
120
130
  this.sessionId = sessionId;
121
131
  this.cwd = cwd;
@@ -132,13 +142,24 @@ class ServeOpencodeDriver {
132
142
  await this.ensureSession(resume);
133
143
  this.startStreaming();
134
144
  this.startPoll();
145
+ // Once the serve is up and the session is bound, the session is ready and
146
+ // waiting for its first user message. Without this transition the meta
147
+ // stays 'starting' forever, and the app — which derives its typing
148
+ // indicator from `isWorking(status)` — keeps showing "escribiendo…" on a
149
+ // brand-new session that has never received a prompt. Only transition if
150
+ // no SSE event (e.g. an early session.status=busy) has moved the status
151
+ // already, so we don't clobber a legitimately-working resumed session.
152
+ if (this.status === 'starting')
153
+ this.emitStatus('awaiting_input');
135
154
  }
136
155
  async sendMessage(text) {
137
156
  await this.prompt(text);
157
+ this.turnDone = false; // arm: the next `busy` is a legit in-flight turn
138
158
  this.emitStatus('working');
139
159
  }
140
160
  async answer(option) {
141
161
  await this.prompt(option.send);
162
+ this.turnDone = false;
142
163
  this.emitStatus('working');
143
164
  }
144
165
  async isAlive() {
@@ -456,10 +477,20 @@ class ServeOpencodeDriver {
456
477
  onSessionStatus(p) {
457
478
  if (p.sessionID !== this.opencodeId || !p.status)
458
479
  return;
459
- if (p.status.type === 'busy' || p.status.type === 'retry')
480
+ if (p.status.type === 'busy' || p.status.type === 'retry') {
481
+ // Ignore `busy` events that arrive after a turn already completed.
482
+ // opencode's SSE stream occasionally emits a stale busy in the wake of a
483
+ // message.updated with time.completed, and honoring it would flip us
484
+ // back to 'working' right after we just emitted 'awaiting_input',
485
+ // leaving the app stuck on "escribiendo…" until the watchdog's hard
486
+ // timeout. The user's next prompt re-arms `turnDone`.
487
+ if (this.turnDone)
488
+ return;
460
489
  this.emitStatus('working');
461
- else if (p.status.type === 'idle')
490
+ }
491
+ else if (p.status.type === 'idle') {
462
492
  this.handleIdle();
493
+ }
463
494
  }
464
495
  onSessionIdle(p) {
465
496
  if (p.sessionID !== this.opencodeId)
@@ -575,6 +606,10 @@ class ServeOpencodeDriver {
575
606
  this.emittedTurn.add(messageId);
576
607
  this.lastAssistantMessageId = messageId;
577
608
  this.lastAssistantText = text;
609
+ // The turn is over: disarm so any subsequent stray `session.status=busy`
610
+ // from opencode's SSE stream doesn't flip us back to 'working' before the
611
+ // user's next prompt.
612
+ this.turnDone = true;
578
613
  this.cb.onEvents(events);
579
614
  const decision = (0, decisions_1.parseDecision)(text);
580
615
  if (decision)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kernelpm",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "description": "kernelpm daemon — keeps opencode sessions alive on a server (via `opencode serve`) and exposes them over a local control socket.",
5
5
  "license": "MIT",
6
6
  "bin": {