@mirasoth/soothe-client 0.5.1 → 0.5.2

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.
@@ -0,0 +1,7 @@
1
+ import {
2
+ Client
3
+ } from "./chunk-NSWLUFSA.js";
4
+ export {
5
+ Client
6
+ };
7
+ //# sourceMappingURL=client-YTWVWNAN.js.map
package/dist/index.cjs CHANGED
@@ -357,7 +357,7 @@ var init_protocol = __esm({
357
357
  import_node_crypto = require("crypto");
358
358
  PROTO_VERSION = "1";
359
359
  DEFAULT_CLIENT_CAPABILITIES = ["streaming", "batch", "heartbeat", "receipts"];
360
- CLIENT_VERSION = "0.5.1";
360
+ CLIENT_VERSION = "0.5.2";
361
361
  }
362
362
  });
363
363
 
@@ -2069,9 +2069,14 @@ __export(index_exports, {
2069
2069
  StaleLoopError: () => StaleLoopError,
2070
2070
  StreamCloseFail: () => StreamCloseFail,
2071
2071
  StreamCloseSoftComplete: () => StreamCloseSoftComplete,
2072
+ TURN_END_IDLE: () => TURN_END_IDLE,
2073
+ TURN_END_STOPPED: () => TURN_END_STOPPED,
2074
+ TURN_END_STREAM_END: () => TURN_END_STREAM_END,
2072
2075
  TimeoutError: () => TimeoutError,
2073
2076
  TimeoutPolicy: () => TimeoutPolicy,
2077
+ TurnBoundary: () => TurnBoundary,
2074
2078
  TurnEventStats: () => TurnEventStats,
2079
+ TurnLifecycleGate: () => TurnLifecycleGate,
2075
2080
  TurnRunner: () => TurnRunner,
2076
2081
  VerbosityTier: () => VerbosityTier,
2077
2082
  authenticate: () => authenticate,
@@ -2101,6 +2106,7 @@ __export(index_exports, {
2101
2106
  inputMessageForLoop: () => inputMessageForLoop,
2102
2107
  isCompletionEvent: () => isCompletionEvent,
2103
2108
  isDaemonLive: () => isDaemonLive,
2109
+ isDaemonTurnEndEvent: () => isDaemonTurnEndEvent,
2104
2110
  isSubagentProgressEvent: () => isSubagentProgressEvent,
2105
2111
  isTurnEndCustomData: () => isTurnEndCustomData,
2106
2112
  isTurnProgressChunk: () => isTurnProgressChunk,
@@ -2743,6 +2749,8 @@ var EventClassifier = class {
2743
2749
  if (!eventType) return false;
2744
2750
  switch (eventType) {
2745
2751
  case "status.idle":
2752
+ case "status.stopped":
2753
+ case "soothe.stream.end":
2746
2754
  case "idle_timeout":
2747
2755
  case "query_timeout":
2748
2756
  case "stream_closed":
@@ -3453,6 +3461,110 @@ async function compactAttachments(atts, opts) {
3453
3461
 
3454
3462
  // src/appkit/turn_runner.ts
3455
3463
  init_intent_hints();
3464
+
3465
+ // src/appkit/turn_boundary.ts
3466
+ init_stream_terminal();
3467
+ var TURN_END_STREAM_END = STREAM_END;
3468
+ var TURN_END_IDLE = "status.idle";
3469
+ var TURN_END_STOPPED = "status.stopped";
3470
+ var TurnLifecycleGate = class {
3471
+ sawRunning = false;
3472
+ sawStreamPayload = false;
3473
+ sawTurnProgress = false;
3474
+ observe(msg) {
3475
+ const frame = normalizeFrame(msg);
3476
+ if (!frame) return;
3477
+ const typ = String(frame.type ?? "");
3478
+ if (typ === "status") {
3479
+ if (String(frame.state ?? "").trim().toLowerCase() === "running") {
3480
+ this.sawRunning = true;
3481
+ }
3482
+ return;
3483
+ }
3484
+ if (typ === "event") {
3485
+ this.sawStreamPayload = true;
3486
+ const mode = String(frame.mode ?? "");
3487
+ if (isTurnProgressChunk(mode, frame.data)) {
3488
+ this.sawTurnProgress = true;
3489
+ }
3490
+ }
3491
+ }
3492
+ allowStreamEnd() {
3493
+ return this.sawRunning && this.sawTurnProgress;
3494
+ }
3495
+ allowIdleComplete() {
3496
+ return this.sawRunning && this.sawStreamPayload;
3497
+ }
3498
+ };
3499
+ var TurnBoundary = class {
3500
+ gate = new TurnLifecycleGate();
3501
+ ended = false;
3502
+ reason = "";
3503
+ feed(msg) {
3504
+ if (this.ended) return [true, this.reason];
3505
+ this.gate.observe(msg);
3506
+ const frame = normalizeFrame(msg);
3507
+ if (!frame) return [false, ""];
3508
+ const typ = String(frame.type ?? "");
3509
+ if (typ === "status") {
3510
+ const state = String(frame.state ?? "").trim().toLowerCase();
3511
+ if (state === "stopped" && this.gate.sawRunning) {
3512
+ return this.mark(TURN_END_STOPPED);
3513
+ }
3514
+ if (state === "idle" && this.gate.allowIdleComplete()) {
3515
+ return this.mark(TURN_END_IDLE);
3516
+ }
3517
+ return [false, ""];
3518
+ }
3519
+ if (typ === "event") {
3520
+ const mode = String(frame.mode ?? "");
3521
+ if (mode === "custom" && isTurnEndCustomData(frame.data) && this.gate.allowStreamEnd()) {
3522
+ return this.mark(TURN_END_STREAM_END);
3523
+ }
3524
+ }
3525
+ return [false, ""];
3526
+ }
3527
+ mark(reason) {
3528
+ this.ended = true;
3529
+ this.reason = reason;
3530
+ return [true, reason];
3531
+ }
3532
+ };
3533
+ function isDaemonTurnEndEvent(completionEvent) {
3534
+ const e = (completionEvent ?? "").trim();
3535
+ return e === TURN_END_STREAM_END || e === TURN_END_IDLE || e === TURN_END_STOPPED;
3536
+ }
3537
+ function normalizeFrame(msg) {
3538
+ if (!msg || typeof msg !== "object") return null;
3539
+ const m = msg;
3540
+ if (m.type === "next") {
3541
+ const payload = m.payload ?? {};
3542
+ const inner = payload.data;
3543
+ if (inner && typeof inner === "object" && inner.type === "status") {
3544
+ return inner;
3545
+ }
3546
+ if (inner && typeof inner === "object" && inner.mode) {
3547
+ return {
3548
+ type: "event",
3549
+ mode: inner.mode,
3550
+ data: inner.data,
3551
+ namespace: inner.namespace ?? payload.namespace
3552
+ };
3553
+ }
3554
+ if (payload.mode) {
3555
+ return {
3556
+ type: "event",
3557
+ mode: payload.mode,
3558
+ data: payload.data,
3559
+ namespace: payload.namespace
3560
+ };
3561
+ }
3562
+ return null;
3563
+ }
3564
+ return m;
3565
+ }
3566
+
3567
+ // src/appkit/turn_runner.ts
3456
3568
  var ErrQueryTimeout = class extends Error {
3457
3569
  constructor() {
3458
3570
  super("appkit: query timeout");
@@ -3592,6 +3704,7 @@ var TurnRunner = class {
3592
3704
  }
3593
3705
  let assistantContent = "";
3594
3706
  const startedAt = Date.now();
3707
+ const boundary = new TurnBoundary();
3595
3708
  const idleForTurn = idleTimeoutForTurn(this.cfg, (attachments?.length ?? 0) > 0);
3596
3709
  let idleReject = null;
3597
3710
  const armIdle = () => {
@@ -3684,6 +3797,7 @@ var TurnRunner = class {
3684
3797
  }
3685
3798
  idleRace = armIdle();
3686
3799
  const msg = res.value;
3800
+ const [ended, endReason] = boundary.feed(msg);
3687
3801
  const eventResult = this.classifier.classify(msg, assistantContent);
3688
3802
  if (eventResult.err && eventResult.terminal === 2 /* FailedComplete */) {
3689
3803
  clearIdle();
@@ -3705,7 +3819,7 @@ var TurnRunner = class {
3705
3819
  eventResult,
3706
3820
  assistantContent
3707
3821
  );
3708
- if (deliverable) {
3822
+ if (deliverable && !isDaemonTurnEndEvent(eventResult.completionEvent ?? "")) {
3709
3823
  clearIdle();
3710
3824
  await this.completeTurn(
3711
3825
  sessionID,
@@ -3716,6 +3830,24 @@ var TurnRunner = class {
3716
3830
  );
3717
3831
  return;
3718
3832
  }
3833
+ if (ended) {
3834
+ clearIdle();
3835
+ if (this.classifier.isSubstantiveAssistantReply(assistantContent)) {
3836
+ await this.completeTurn(
3837
+ sessionID,
3838
+ loopID,
3839
+ assistantContent.trim(),
3840
+ startedAt,
3841
+ endReason
3842
+ );
3843
+ return;
3844
+ }
3845
+ const err = new Error(`turn ended (${endReason}) with no assistant content`);
3846
+ await this.persistFailed(sessionID, loopID, err);
3847
+ this.broadcastError(sessionID, err);
3848
+ this.onError?.(sessionID, loopID, err);
3849
+ throw err;
3850
+ }
3719
3851
  }
3720
3852
  } finally {
3721
3853
  clearIdle();
@@ -4309,9 +4441,14 @@ var DaemonSession = class {
4309
4441
  StaleLoopError,
4310
4442
  StreamCloseFail,
4311
4443
  StreamCloseSoftComplete,
4444
+ TURN_END_IDLE,
4445
+ TURN_END_STOPPED,
4446
+ TURN_END_STREAM_END,
4312
4447
  TimeoutError,
4313
4448
  TimeoutPolicy,
4449
+ TurnBoundary,
4314
4450
  TurnEventStats,
4451
+ TurnLifecycleGate,
4315
4452
  TurnRunner,
4316
4453
  VerbosityTier,
4317
4454
  authenticate,
@@ -4341,6 +4478,7 @@ var DaemonSession = class {
4341
4478
  inputMessageForLoop,
4342
4479
  isCompletionEvent,
4343
4480
  isDaemonLive,
4481
+ isDaemonTurnEndEvent,
4344
4482
  isSubagentProgressEvent,
4345
4483
  isTurnEndCustomData,
4346
4484
  isTurnProgressChunk,