@mirasoth/soothe-client 0.2.1 → 0.4.1
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 +81 -47
- package/dist/{chunk-AQZACDIC.js → chunk-YYUVHZ3W.js} +607 -31
- package/dist/chunk-YYUVHZ3W.js.map +1 -0
- package/dist/client-SOJZSF7C.js +7 -0
- package/dist/index.cjs +1568 -298
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +355 -136
- package/dist/index.d.ts +355 -136
- package/dist/index.js +988 -258
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
- package/dist/chunk-AQZACDIC.js.map +0 -1
- package/dist/client-CB6WKQYW.js +0 -7
- /package/dist/{client-CB6WKQYW.js.map → client-SOJZSF7C.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -51,7 +51,7 @@ var init_errors = __esm({
|
|
|
51
51
|
}
|
|
52
52
|
};
|
|
53
53
|
DaemonError = class extends Error {
|
|
54
|
-
/** Numeric error code from the
|
|
54
|
+
/** Numeric error code from the daemon error registry. */
|
|
55
55
|
code;
|
|
56
56
|
/** The daemon's error message text. */
|
|
57
57
|
daemonMessage;
|
|
@@ -106,6 +106,37 @@ var init_errors = __esm({
|
|
|
106
106
|
}
|
|
107
107
|
});
|
|
108
108
|
|
|
109
|
+
// src/verbosity.ts
|
|
110
|
+
function shouldShow(tier, verbosity) {
|
|
111
|
+
if (tier === 99 /* Internal */) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
const level = verbosityLevelValues[verbosity] ?? 1;
|
|
115
|
+
return tier <= level;
|
|
116
|
+
}
|
|
117
|
+
function isValidVerbosityLevel(s) {
|
|
118
|
+
return s in verbosityLevelValues;
|
|
119
|
+
}
|
|
120
|
+
var VerbosityTier, verbosityLevelValues;
|
|
121
|
+
var init_verbosity = __esm({
|
|
122
|
+
"src/verbosity.ts"() {
|
|
123
|
+
"use strict";
|
|
124
|
+
VerbosityTier = /* @__PURE__ */ ((VerbosityTier2) => {
|
|
125
|
+
VerbosityTier2[VerbosityTier2["Quiet"] = 0] = "Quiet";
|
|
126
|
+
VerbosityTier2[VerbosityTier2["Normal"] = 1] = "Normal";
|
|
127
|
+
VerbosityTier2[VerbosityTier2["Detailed"] = 2] = "Detailed";
|
|
128
|
+
VerbosityTier2[VerbosityTier2["Debug"] = 3] = "Debug";
|
|
129
|
+
VerbosityTier2[VerbosityTier2["Internal"] = 99] = "Internal";
|
|
130
|
+
return VerbosityTier2;
|
|
131
|
+
})(VerbosityTier || {});
|
|
132
|
+
verbosityLevelValues = {
|
|
133
|
+
quiet: 0,
|
|
134
|
+
normal: 1,
|
|
135
|
+
debug: 3
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
109
140
|
// src/config.ts
|
|
110
141
|
function defaultConfig() {
|
|
111
142
|
return {
|
|
@@ -326,7 +357,7 @@ var init_protocol = __esm({
|
|
|
326
357
|
import_node_crypto = require("crypto");
|
|
327
358
|
PROTO_VERSION = "1";
|
|
328
359
|
DEFAULT_CLIENT_CAPABILITIES = ["streaming", "batch", "heartbeat", "receipts"];
|
|
329
|
-
CLIENT_VERSION = "0.1
|
|
360
|
+
CLIENT_VERSION = "0.4.1";
|
|
330
361
|
}
|
|
331
362
|
});
|
|
332
363
|
|
|
@@ -374,6 +405,129 @@ var init_intent_hints = __esm({
|
|
|
374
405
|
}
|
|
375
406
|
});
|
|
376
407
|
|
|
408
|
+
// src/events.ts
|
|
409
|
+
function parseNamespace(ns) {
|
|
410
|
+
const parts = splitNamespace(ns);
|
|
411
|
+
if (parts.length < 4 || parts[0] !== "soothe") {
|
|
412
|
+
return null;
|
|
413
|
+
}
|
|
414
|
+
if (parts[1] === "internal") {
|
|
415
|
+
return null;
|
|
416
|
+
}
|
|
417
|
+
return { domain: parts[1], component: parts[2], action: parts[3] };
|
|
418
|
+
}
|
|
419
|
+
function splitNamespace(ns) {
|
|
420
|
+
const parts = [];
|
|
421
|
+
let start = 0;
|
|
422
|
+
for (let i = 0; i < ns.length; i++) {
|
|
423
|
+
if (ns[i] === ".") {
|
|
424
|
+
parts.push(ns.slice(start, i));
|
|
425
|
+
start = i + 1;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
parts.push(ns.slice(start));
|
|
429
|
+
return parts;
|
|
430
|
+
}
|
|
431
|
+
function classifyEventVerbosity(eventTypeOrNamespace) {
|
|
432
|
+
const parsed = parseNamespace(eventTypeOrNamespace);
|
|
433
|
+
if (!parsed) {
|
|
434
|
+
return classifyByEventTypeString(eventTypeOrNamespace);
|
|
435
|
+
}
|
|
436
|
+
return classifyByDomainAndComponent(parsed.domain, parsed.component, eventTypeOrNamespace);
|
|
437
|
+
}
|
|
438
|
+
function classifyByDomainAndComponent(domain, _component, full) {
|
|
439
|
+
switch (domain) {
|
|
440
|
+
case "cognition":
|
|
441
|
+
return 1 /* Normal */;
|
|
442
|
+
case "protocol":
|
|
443
|
+
return 2 /* Detailed */;
|
|
444
|
+
case "tool":
|
|
445
|
+
return 99 /* Internal */;
|
|
446
|
+
case "subagent":
|
|
447
|
+
return classifySubagentEvent(full);
|
|
448
|
+
case "autopilot":
|
|
449
|
+
return 1 /* Normal */;
|
|
450
|
+
case "output":
|
|
451
|
+
case "error":
|
|
452
|
+
return 0 /* Quiet */;
|
|
453
|
+
default:
|
|
454
|
+
return 1 /* Normal */;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
function classifySubagentEvent(full) {
|
|
458
|
+
const parsed = parseNamespace(full);
|
|
459
|
+
if (!parsed) return 1 /* Normal */;
|
|
460
|
+
switch (parsed.action) {
|
|
461
|
+
case "started":
|
|
462
|
+
case "completed":
|
|
463
|
+
return 1 /* Normal */;
|
|
464
|
+
default:
|
|
465
|
+
return 2 /* Detailed */;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
function classifyByEventTypeString(eventType) {
|
|
469
|
+
if (eventType === EventFinalReport || eventType === EventGeneralFailed) {
|
|
470
|
+
return 0 /* Quiet */;
|
|
471
|
+
}
|
|
472
|
+
if (eventType === EventToolStarted) {
|
|
473
|
+
return 99 /* Internal */;
|
|
474
|
+
}
|
|
475
|
+
return 1 /* Normal */;
|
|
476
|
+
}
|
|
477
|
+
function isCompletionEvent(eventType) {
|
|
478
|
+
return eventType.endsWith(".completed") || eventType.endsWith(".failed") || eventType === EventGeneralFailed;
|
|
479
|
+
}
|
|
480
|
+
function isSubagentProgressEvent(eventType) {
|
|
481
|
+
const parsed = parseNamespace(eventType);
|
|
482
|
+
if (!parsed || parsed.domain !== "subagent") {
|
|
483
|
+
return false;
|
|
484
|
+
}
|
|
485
|
+
return parsed.action === "started" || parsed.action === "completed";
|
|
486
|
+
}
|
|
487
|
+
var EventPlanCreated, EventExploreStarted, EventExploreMilestone, EventExploreStepCompleted, EventExploreCompleted, EventTacitusStarted, EventTacitusGatherSummary, EventTacitusCompleted, EventReplayComplete, EventLoopReattachedWire, EventCardReplayBegin, EventCardCreated, EventCardReplayEnd, EventToolStarted, EventToolCompleted, EventToolError, EventStreamToolCallUpdate, EventToolCallUpdatesBatch, EventStrangeLoopStarted, EventStrangeLoopCompleted, EventStrangeLoopPlanDecision, EventStrangeLoopReasoned, EventStrangeLoopStepStarted, EventStrangeLoopStepQueued, EventStrangeLoopStepCompleted, EventStrangeLoopContextCompacted, EventMessageReceived, EventMessageSent, EventFinalReport, EventAutopilotGoalStatus, EventAutopilotGoalProgress, EventAutopilotGoalCreated, EventAutopilotGoalCompleted, EventAutopilotWorkerAssigned, EventAutopilotWorkerUnassigned, EventGeneralFailed;
|
|
488
|
+
var init_events = __esm({
|
|
489
|
+
"src/events.ts"() {
|
|
490
|
+
"use strict";
|
|
491
|
+
init_verbosity();
|
|
492
|
+
EventPlanCreated = "soothe.cognition.plan.created";
|
|
493
|
+
EventExploreStarted = "soothe.subagent.explore.started";
|
|
494
|
+
EventExploreMilestone = "soothe.subagent.explore.milestone";
|
|
495
|
+
EventExploreStepCompleted = "soothe.subagent.explore.step.completed";
|
|
496
|
+
EventExploreCompleted = "soothe.subagent.explore.completed";
|
|
497
|
+
EventTacitusStarted = "soothe.subagent.tacitus.started";
|
|
498
|
+
EventTacitusGatherSummary = "soothe.subagent.tacitus.gather.summary";
|
|
499
|
+
EventTacitusCompleted = "soothe.subagent.tacitus.completed";
|
|
500
|
+
EventReplayComplete = "replay_complete";
|
|
501
|
+
EventLoopReattachedWire = "loop_reattached";
|
|
502
|
+
EventCardReplayBegin = "card.replay_begin";
|
|
503
|
+
EventCardCreated = "card.created";
|
|
504
|
+
EventCardReplayEnd = "card.replay_end";
|
|
505
|
+
EventToolStarted = "soothe.tool.execution.started";
|
|
506
|
+
EventToolCompleted = "soothe.tool.execution.completed";
|
|
507
|
+
EventToolError = "soothe.tool.execution.error";
|
|
508
|
+
EventStreamToolCallUpdate = "soothe.stream.tool_call.update";
|
|
509
|
+
EventToolCallUpdatesBatch = "tool_call_updates_batch";
|
|
510
|
+
EventStrangeLoopStarted = "soothe.cognition.strange_loop.started";
|
|
511
|
+
EventStrangeLoopCompleted = "soothe.cognition.strange_loop.completed";
|
|
512
|
+
EventStrangeLoopPlanDecision = "soothe.cognition.strange_loop.plan.decision";
|
|
513
|
+
EventStrangeLoopReasoned = "soothe.cognition.strange_loop.reasoned";
|
|
514
|
+
EventStrangeLoopStepStarted = "soothe.cognition.strange_loop.step.started";
|
|
515
|
+
EventStrangeLoopStepQueued = "soothe.cognition.strange_loop.step.queued";
|
|
516
|
+
EventStrangeLoopStepCompleted = "soothe.cognition.strange_loop.step.completed";
|
|
517
|
+
EventStrangeLoopContextCompacted = "soothe.cognition.strange_loop.context.compacted";
|
|
518
|
+
EventMessageReceived = "soothe.protocol.message.received";
|
|
519
|
+
EventMessageSent = "soothe.protocol.message.sent";
|
|
520
|
+
EventFinalReport = "soothe.output.autonomous.final_report.reported";
|
|
521
|
+
EventAutopilotGoalStatus = "soothe.autopilot.goal.status";
|
|
522
|
+
EventAutopilotGoalProgress = "soothe.autopilot.goal.progress";
|
|
523
|
+
EventAutopilotGoalCreated = "soothe.autopilot.goal.created";
|
|
524
|
+
EventAutopilotGoalCompleted = "soothe.autopilot.goal.completed";
|
|
525
|
+
EventAutopilotWorkerAssigned = "soothe.autopilot.worker.assigned";
|
|
526
|
+
EventAutopilotWorkerUnassigned = "soothe.autopilot.worker.unassigned";
|
|
527
|
+
EventGeneralFailed = "soothe.error.general.failed";
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
|
|
377
531
|
// src/multiplexer.ts
|
|
378
532
|
var Multiplexer;
|
|
379
533
|
var init_multiplexer = __esm({
|
|
@@ -517,6 +671,203 @@ var init_multiplexer = __esm({
|
|
|
517
671
|
}
|
|
518
672
|
});
|
|
519
673
|
|
|
674
|
+
// src/stream_terminal.ts
|
|
675
|
+
function isTurnEndCustomData(data) {
|
|
676
|
+
if (!data || typeof data !== "object") return false;
|
|
677
|
+
const customType = String(data.type ?? "").trim();
|
|
678
|
+
if (!TURN_END_CUSTOM_TYPES.has(customType)) return false;
|
|
679
|
+
if (customType === STREAM_END) {
|
|
680
|
+
const scope = String(data.scope ?? "turn").trim().toLowerCase();
|
|
681
|
+
return scope === "" || scope === "turn";
|
|
682
|
+
}
|
|
683
|
+
return true;
|
|
684
|
+
}
|
|
685
|
+
function isTurnProgressChunk(mode, data) {
|
|
686
|
+
if (mode === "messages" || mode === "updates") return true;
|
|
687
|
+
if (mode !== "custom" || !data || typeof data !== "object") return false;
|
|
688
|
+
if (isTurnEndCustomData(data)) return false;
|
|
689
|
+
const customType = String(data.type ?? "").trim();
|
|
690
|
+
if (TURN_PROGRESS_CUSTOM_TYPES.has(customType)) return true;
|
|
691
|
+
if (customType.startsWith("soothe.cognition.strange_loop.step")) return true;
|
|
692
|
+
return false;
|
|
693
|
+
}
|
|
694
|
+
function stalePendingFrameLabel(event) {
|
|
695
|
+
const eventType = String(event.type ?? "");
|
|
696
|
+
if (STALE_TURN_PENDING_TYPES.has(eventType)) return eventType;
|
|
697
|
+
if (eventType === "next") {
|
|
698
|
+
const payload = event.payload;
|
|
699
|
+
if (!payload || typeof payload !== "object") return null;
|
|
700
|
+
const p = payload;
|
|
701
|
+
const staleMode = String(p.mode ?? "");
|
|
702
|
+
if (STALE_TURN_PENDING_TYPES.has(staleMode)) return staleMode;
|
|
703
|
+
const inner = p.data;
|
|
704
|
+
if (inner && typeof inner === "object") {
|
|
705
|
+
return stalePendingFrameLabel(inner);
|
|
706
|
+
}
|
|
707
|
+
return null;
|
|
708
|
+
}
|
|
709
|
+
if (eventType === "event") {
|
|
710
|
+
const mode = String(event.mode ?? "");
|
|
711
|
+
const data = event.data;
|
|
712
|
+
if (mode === "custom" && isTurnEndCustomData(data)) {
|
|
713
|
+
return String(data.type ?? "").trim();
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
return null;
|
|
717
|
+
}
|
|
718
|
+
function inboundNeedsDeliveryAck(event) {
|
|
719
|
+
const eventType = String(event.type ?? "");
|
|
720
|
+
if (eventType === "complete") return true;
|
|
721
|
+
if (eventType === "next") {
|
|
722
|
+
const payload = event.payload;
|
|
723
|
+
if (!payload || typeof payload !== "object") return false;
|
|
724
|
+
const p = payload;
|
|
725
|
+
const inner = p.data;
|
|
726
|
+
if (!inner || typeof inner !== "object") return false;
|
|
727
|
+
if (String(p.mode ?? "") === "event") {
|
|
728
|
+
return inboundNeedsAckFromEventShape(inner);
|
|
729
|
+
}
|
|
730
|
+
return false;
|
|
731
|
+
}
|
|
732
|
+
if (eventType === "event") return inboundNeedsAckFromEventShape(event);
|
|
733
|
+
return false;
|
|
734
|
+
}
|
|
735
|
+
function inboundNeedsAckFromEventShape(event) {
|
|
736
|
+
const mode = String(event.mode ?? "");
|
|
737
|
+
const data = event.data;
|
|
738
|
+
if (mode === "custom" && isTurnEndCustomData(data)) return true;
|
|
739
|
+
if (mode === "messages" && Array.isArray(data) && data.length > 0) {
|
|
740
|
+
const body = data[0];
|
|
741
|
+
if (!body || typeof body !== "object") return false;
|
|
742
|
+
const t = String(body.type ?? "");
|
|
743
|
+
return t === STREAM_END || t.includes("stream.end");
|
|
744
|
+
}
|
|
745
|
+
return false;
|
|
746
|
+
}
|
|
747
|
+
function extractLoopIdFromInbound(event) {
|
|
748
|
+
const direct = String(event.loop_id ?? "").trim();
|
|
749
|
+
if (direct) return direct;
|
|
750
|
+
if (String(event.type ?? "") !== "next") return "";
|
|
751
|
+
const payload = event.payload;
|
|
752
|
+
if (!payload || typeof payload !== "object") return "";
|
|
753
|
+
const p = payload;
|
|
754
|
+
const fromPayload = String(p.loop_id ?? "").trim();
|
|
755
|
+
if (fromPayload) return fromPayload;
|
|
756
|
+
const inner = p.data;
|
|
757
|
+
if (inner && typeof inner === "object") {
|
|
758
|
+
return String(inner.loop_id ?? "").trim();
|
|
759
|
+
}
|
|
760
|
+
return "";
|
|
761
|
+
}
|
|
762
|
+
var STREAM_END, TURN_END_CUSTOM_TYPES, TURN_PROGRESS_CUSTOM_TYPES, STALE_TURN_PENDING_TYPES;
|
|
763
|
+
var init_stream_terminal = __esm({
|
|
764
|
+
"src/stream_terminal.ts"() {
|
|
765
|
+
"use strict";
|
|
766
|
+
init_events();
|
|
767
|
+
STREAM_END = "soothe.stream.end";
|
|
768
|
+
TURN_END_CUSTOM_TYPES = /* @__PURE__ */ new Set([
|
|
769
|
+
STREAM_END,
|
|
770
|
+
EventStrangeLoopCompleted
|
|
771
|
+
]);
|
|
772
|
+
TURN_PROGRESS_CUSTOM_TYPES = /* @__PURE__ */ new Set([
|
|
773
|
+
EventPlanCreated,
|
|
774
|
+
EventStrangeLoopStepStarted,
|
|
775
|
+
EventStrangeLoopStepQueued,
|
|
776
|
+
EventStrangeLoopStepCompleted
|
|
777
|
+
]);
|
|
778
|
+
STALE_TURN_PENDING_TYPES = /* @__PURE__ */ new Set([
|
|
779
|
+
"connection_ack",
|
|
780
|
+
EventCardReplayBegin,
|
|
781
|
+
EventCardReplayEnd,
|
|
782
|
+
EventCardCreated,
|
|
783
|
+
"complete"
|
|
784
|
+
]);
|
|
785
|
+
}
|
|
786
|
+
});
|
|
787
|
+
|
|
788
|
+
// src/inbound_priority.ts
|
|
789
|
+
function inboundFrameDropPriority(event) {
|
|
790
|
+
if (!event) return DROP_PRIORITY_CRITICAL;
|
|
791
|
+
let eventType = String(event.type ?? "");
|
|
792
|
+
if (eventType === "event_batch" || eventType === "tool_call_updates_batch") {
|
|
793
|
+
return DROP_PRIORITY_HIGH;
|
|
794
|
+
}
|
|
795
|
+
if (eventType === "next") {
|
|
796
|
+
const payload = event.payload;
|
|
797
|
+
if (payload && typeof payload === "object") {
|
|
798
|
+
const p = payload;
|
|
799
|
+
const innerMode = String(p.mode ?? "");
|
|
800
|
+
const innerData = p.data;
|
|
801
|
+
if (innerMode === "messages") {
|
|
802
|
+
if (messagesWireTerminal(innerData)) return DROP_PRIORITY_CRITICAL;
|
|
803
|
+
if (Array.isArray(innerData) && innerData[0] && typeof innerData[0] === "object") {
|
|
804
|
+
if (String(innerData[0].phase ?? "") === "goal_completion") {
|
|
805
|
+
return DROP_PRIORITY_CRITICAL;
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
if (String(p.type ?? "") === "complete") return DROP_PRIORITY_CRITICAL;
|
|
810
|
+
if (innerData && typeof innerData === "object") {
|
|
811
|
+
return inboundFrameDropPriority(innerData);
|
|
812
|
+
}
|
|
813
|
+
eventType = String(p.type ?? "");
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
if (eventType === "complete" || eventType === "error" || eventType === "connection_ack") {
|
|
817
|
+
return DROP_PRIORITY_CRITICAL;
|
|
818
|
+
}
|
|
819
|
+
if (eventType === "status") {
|
|
820
|
+
const state = String(event.state ?? "");
|
|
821
|
+
if (["idle", "running", "stopped", "detached"].includes(state)) {
|
|
822
|
+
return DROP_PRIORITY_CRITICAL;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
if (eventType === "event") {
|
|
826
|
+
const mode = String(event.mode ?? "");
|
|
827
|
+
const data = event.data;
|
|
828
|
+
if (mode === "custom") {
|
|
829
|
+
if (isTurnEndCustomData(data)) return DROP_PRIORITY_CRITICAL;
|
|
830
|
+
if (data && typeof data === "object") {
|
|
831
|
+
const customType = String(data.type ?? "");
|
|
832
|
+
if (customType.startsWith("soothe.cognition.")) return DROP_PRIORITY_HIGH;
|
|
833
|
+
if (customType.startsWith("soothe.error.") || customType === "stream_degraded") {
|
|
834
|
+
return DROP_PRIORITY_CRITICAL;
|
|
835
|
+
}
|
|
836
|
+
if (customType === "soothe.ux.stream_tool_wire.tool_call_updates_batch") {
|
|
837
|
+
return DROP_PRIORITY_HIGH;
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
if (mode === "messages") {
|
|
842
|
+
if (messagesWireTerminal(data)) return DROP_PRIORITY_CRITICAL;
|
|
843
|
+
if (Array.isArray(data) && data[0] && typeof data[0] === "object") {
|
|
844
|
+
if (String(data[0].phase ?? "") === "goal_completion") {
|
|
845
|
+
return DROP_PRIORITY_CRITICAL;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
return DROP_PRIORITY_NORMAL;
|
|
851
|
+
}
|
|
852
|
+
function messagesWireTerminal(data) {
|
|
853
|
+
if (!Array.isArray(data) || data.length === 0) return false;
|
|
854
|
+
const body = data[0];
|
|
855
|
+
if (!body || typeof body !== "object") return false;
|
|
856
|
+
const t = String(body.type ?? "");
|
|
857
|
+
return t === STREAM_END || t.includes("stream.end");
|
|
858
|
+
}
|
|
859
|
+
var DROP_PRIORITY_CRITICAL, DROP_PRIORITY_HIGH, DROP_PRIORITY_NORMAL, DEFAULT_INBOUND_MAX_SIZE;
|
|
860
|
+
var init_inbound_priority = __esm({
|
|
861
|
+
"src/inbound_priority.ts"() {
|
|
862
|
+
"use strict";
|
|
863
|
+
init_stream_terminal();
|
|
864
|
+
DROP_PRIORITY_CRITICAL = 0;
|
|
865
|
+
DROP_PRIORITY_HIGH = 1;
|
|
866
|
+
DROP_PRIORITY_NORMAL = 2;
|
|
867
|
+
DEFAULT_INBOUND_MAX_SIZE = 2e4;
|
|
868
|
+
}
|
|
869
|
+
});
|
|
870
|
+
|
|
520
871
|
// src/client.ts
|
|
521
872
|
var client_exports = {};
|
|
522
873
|
__export(client_exports, {
|
|
@@ -532,14 +883,19 @@ var init_client = __esm({
|
|
|
532
883
|
init_errors();
|
|
533
884
|
init_multiplexer();
|
|
534
885
|
init_intent_hints();
|
|
886
|
+
init_stream_terminal();
|
|
887
|
+
init_inbound_priority();
|
|
535
888
|
init_protocol();
|
|
536
889
|
Client = class extends import_node_events.EventEmitter {
|
|
537
890
|
url;
|
|
538
891
|
config;
|
|
539
892
|
ws = null;
|
|
540
893
|
messageBuffer = [];
|
|
894
|
+
inboundMaxSize = DEFAULT_INBOUND_MAX_SIZE;
|
|
895
|
+
inboundDroppedCount = 0;
|
|
896
|
+
onStreamDegraded = null;
|
|
541
897
|
resolvers = [];
|
|
542
|
-
// Protocol-1 handshake state
|
|
898
|
+
// Protocol-1 handshake state
|
|
543
899
|
handshakeComplete = false;
|
|
544
900
|
negotiatedCapabilities = /* @__PURE__ */ new Set();
|
|
545
901
|
protocolVersion = null;
|
|
@@ -547,14 +903,16 @@ var init_client = __esm({
|
|
|
547
903
|
heartbeatIntervalMs = 0;
|
|
548
904
|
heartbeatTimer = null;
|
|
549
905
|
lastPongMonotonic = 0;
|
|
550
|
-
// Mid-session drop signal
|
|
906
|
+
// Mid-session drop signal. The 'disconnected' event is
|
|
551
907
|
// emitted exactly once when the connection drops, carrying a DisconnectCause
|
|
552
908
|
// that distinguishes clean (peer `disconnect`) from unclean (read/write
|
|
553
909
|
// error or missed pong). `disconnFired` guards the once-only delivery.
|
|
554
910
|
disconnFired = false;
|
|
555
|
-
// Pending-request/subscription multiplexer
|
|
911
|
+
// Pending-request/subscription multiplexer. Routes
|
|
556
912
|
// inbound frames by (type, id) instead of discarding non-matching events.
|
|
557
913
|
mux = new Multiplexer();
|
|
914
|
+
deliveryRecvSeq = /* @__PURE__ */ new Map();
|
|
915
|
+
deliveryAckedSeq = /* @__PURE__ */ new Map();
|
|
558
916
|
constructor(url, config) {
|
|
559
917
|
super();
|
|
560
918
|
this.url = url;
|
|
@@ -622,13 +980,15 @@ var init_client = __esm({
|
|
|
622
980
|
this._signalDisconnect(1 /* Clean */);
|
|
623
981
|
}
|
|
624
982
|
if (this.mux.route(m)) {
|
|
983
|
+
this._trackInboundDeliveryAck(m);
|
|
625
984
|
continue;
|
|
626
985
|
}
|
|
986
|
+
this._trackInboundDeliveryAck(m);
|
|
627
987
|
const resolver = this.resolvers.shift();
|
|
628
988
|
if (resolver) {
|
|
629
989
|
resolver(msg);
|
|
630
990
|
} else {
|
|
631
|
-
this.
|
|
991
|
+
this.enqueueMessageBuffer(msg);
|
|
632
992
|
}
|
|
633
993
|
this.emit("message", msg);
|
|
634
994
|
}
|
|
@@ -665,7 +1025,7 @@ var init_client = __esm({
|
|
|
665
1025
|
return this.ws !== null && this.ws.readyState === import_ws.default.OPEN && this.handshakeComplete;
|
|
666
1026
|
}
|
|
667
1027
|
// ---------------------------------------------------------------------------
|
|
668
|
-
// Mid-session drop signal + reconnect/reattach
|
|
1028
|
+
// Mid-session drop signal + reconnect/reattach
|
|
669
1029
|
// ---------------------------------------------------------------------------
|
|
670
1030
|
/**
|
|
671
1031
|
* Returns whether the connection has dropped (the `'disconnected'` event has
|
|
@@ -700,8 +1060,8 @@ var init_client = __esm({
|
|
|
700
1060
|
}
|
|
701
1061
|
}
|
|
702
1062
|
/**
|
|
703
|
-
* Re-dials the daemon and re-handshakes after a connection drop
|
|
704
|
-
*
|
|
1063
|
+
* Re-dials the daemon and re-handshakes after a connection drop.
|
|
1064
|
+
* Does not re-establish loop subscriptions; follow with
|
|
705
1065
|
* `reattachAndProbe()` to resume a loop session. The caller should invoke
|
|
706
1066
|
* this after the `'disconnected'` event fires. Reuses the same Client,
|
|
707
1067
|
* resetting the drop signal and multiplexer.
|
|
@@ -735,7 +1095,7 @@ var init_client = __esm({
|
|
|
735
1095
|
* Returns a `StaleLoopError` when the probe fails; callers should fall back
|
|
736
1096
|
* to a fresh `loop_new` bootstrap.
|
|
737
1097
|
*
|
|
738
|
-
*
|
|
1098
|
+
* Note: connection-level readiness is the handshake's readiness_state
|
|
739
1099
|
* (+ daemon_status); loop_get is a loop-scoped probe only, not a readiness
|
|
740
1100
|
* probe.
|
|
741
1101
|
*/
|
|
@@ -776,7 +1136,7 @@ var init_client = __esm({
|
|
|
776
1136
|
}
|
|
777
1137
|
}
|
|
778
1138
|
// ---------------------------------------------------------------------------
|
|
779
|
-
// Protocol-1 handshake
|
|
1139
|
+
// Protocol-1 handshake
|
|
780
1140
|
// ---------------------------------------------------------------------------
|
|
781
1141
|
/** Send connection_init and wait for connection_ack with readiness "ready". */
|
|
782
1142
|
async _performHandshake() {
|
|
@@ -825,7 +1185,7 @@ var init_client = __esm({
|
|
|
825
1185
|
throw new Error(`timeout after ${this.config.daemonReadyTimeout}ms waiting for connection_ack`);
|
|
826
1186
|
}
|
|
827
1187
|
// ---------------------------------------------------------------------------
|
|
828
|
-
// Heartbeat
|
|
1188
|
+
// Heartbeat
|
|
829
1189
|
// ---------------------------------------------------------------------------
|
|
830
1190
|
_startHeartbeat() {
|
|
831
1191
|
if (!this.negotiatedCapabilities.has("heartbeat")) return;
|
|
@@ -941,8 +1301,86 @@ var init_client = __esm({
|
|
|
941
1301
|
this.resolvers.push(resolver);
|
|
942
1302
|
});
|
|
943
1303
|
}
|
|
1304
|
+
/**
|
|
1305
|
+
* Remove stale handshake/terminal frames left in `messageBuffer` before a turn.
|
|
1306
|
+
* Returns labels of removed frames (in order).
|
|
1307
|
+
*/
|
|
1308
|
+
peelStalePendingControlEvents() {
|
|
1309
|
+
if (this.messageBuffer.length === 0) return [];
|
|
1310
|
+
const kept = [];
|
|
1311
|
+
const removed = [];
|
|
1312
|
+
while (this.messageBuffer.length > 0) {
|
|
1313
|
+
const event = this.messageBuffer.shift();
|
|
1314
|
+
const label = stalePendingFrameLabel(event);
|
|
1315
|
+
if (label !== null) {
|
|
1316
|
+
removed.push(label);
|
|
1317
|
+
continue;
|
|
1318
|
+
}
|
|
1319
|
+
kept.push(event);
|
|
1320
|
+
}
|
|
1321
|
+
this.messageBuffer = kept;
|
|
1322
|
+
return removed;
|
|
1323
|
+
}
|
|
1324
|
+
/** True when the underlying socket is still open (may not be handshaked). */
|
|
1325
|
+
isConnectionAlive() {
|
|
1326
|
+
return this.ws !== null && this.ws.readyState === import_ws.default.OPEN;
|
|
1327
|
+
}
|
|
1328
|
+
/** Override pending buffer cap (tests / tuning). */
|
|
1329
|
+
setInboundMaxSize(n) {
|
|
1330
|
+
if (n > 0) this.inboundMaxSize = n;
|
|
1331
|
+
}
|
|
1332
|
+
/** How many NORMAL-priority frames were dropped under backpressure. */
|
|
1333
|
+
inboundDropped() {
|
|
1334
|
+
return this.inboundDroppedCount;
|
|
1335
|
+
}
|
|
1336
|
+
/** Hook invoked on the first inbound overflow drop. */
|
|
1337
|
+
setStreamDegradedCallback(fn) {
|
|
1338
|
+
this.onStreamDegraded = fn;
|
|
1339
|
+
}
|
|
1340
|
+
enqueueMessageBuffer(msg) {
|
|
1341
|
+
const max = this.inboundMaxSize > 0 ? this.inboundMaxSize : DEFAULT_INBOUND_MAX_SIZE;
|
|
1342
|
+
if (this.messageBuffer.length < max) {
|
|
1343
|
+
this.messageBuffer.push(msg);
|
|
1344
|
+
return;
|
|
1345
|
+
}
|
|
1346
|
+
const ev = msg;
|
|
1347
|
+
let dropIdx = -1;
|
|
1348
|
+
let dropPri = -1;
|
|
1349
|
+
for (let i = 0; i < this.messageBuffer.length; i++) {
|
|
1350
|
+
const p = inboundFrameDropPriority(this.messageBuffer[i]);
|
|
1351
|
+
if (p > dropPri) {
|
|
1352
|
+
dropPri = p;
|
|
1353
|
+
dropIdx = i;
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
const incomingPri = inboundFrameDropPriority(ev);
|
|
1357
|
+
if (dropIdx >= 0 && dropPri >= DROP_PRIORITY_NORMAL) {
|
|
1358
|
+
this.messageBuffer.splice(dropIdx, 1);
|
|
1359
|
+
this.messageBuffer.push(msg);
|
|
1360
|
+
this.noteInboundDrop();
|
|
1361
|
+
return;
|
|
1362
|
+
}
|
|
1363
|
+
if (incomingPri >= DROP_PRIORITY_NORMAL) {
|
|
1364
|
+
this.noteInboundDrop();
|
|
1365
|
+
return;
|
|
1366
|
+
}
|
|
1367
|
+
if (this.messageBuffer.length > 0) {
|
|
1368
|
+
this.messageBuffer.shift();
|
|
1369
|
+
this.noteInboundDrop();
|
|
1370
|
+
}
|
|
1371
|
+
this.messageBuffer.push(msg);
|
|
1372
|
+
}
|
|
1373
|
+
noteInboundDrop() {
|
|
1374
|
+
this.inboundDroppedCount += 1;
|
|
1375
|
+
if (this.onStreamDegraded && this.inboundDroppedCount === 1) {
|
|
1376
|
+
try {
|
|
1377
|
+
this.onStreamDegraded(1, "inbound_queue_overflow");
|
|
1378
|
+
} catch {
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
944
1382
|
// ---------------------------------------------------------------------------
|
|
945
|
-
// Protocol-1 RPC primitives
|
|
1383
|
+
// Protocol-1 RPC primitives
|
|
946
1384
|
// ---------------------------------------------------------------------------
|
|
947
1385
|
/**
|
|
948
1386
|
* Reads the next frame directly from the live socket (via a resolver),
|
|
@@ -968,16 +1406,16 @@ var init_client = __esm({
|
|
|
968
1406
|
});
|
|
969
1407
|
}
|
|
970
1408
|
/**
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
1409
|
+
* Sends a `request` envelope and waits for the matching `response` (or
|
|
1410
|
+
* `error`) correlated by `id`. Returns the `result` object.
|
|
1411
|
+
*
|
|
1412
|
+
* Multiplexer-aware: registers a pending RPC wait
|
|
1413
|
+
* keyed by the request id so that, even when a `receiveMessages()` reader
|
|
1414
|
+
* is concurrently active, the matching `response`/`error` is routed to
|
|
1415
|
+
* this caller instead of being discarded or buffered behind a stream.
|
|
1416
|
+
* Non-matching frames are routed to their own waiters by the multiplexer
|
|
1417
|
+
* or flow on to the resolver queue for stream readers.
|
|
1418
|
+
*/
|
|
981
1419
|
async requestResponse(method, params, responseType, timeout = 15e3) {
|
|
982
1420
|
const req = requestEnvelope(method, params);
|
|
983
1421
|
const rid = req.id;
|
|
@@ -1045,6 +1483,35 @@ var init_client = __esm({
|
|
|
1045
1483
|
notify(method, params) {
|
|
1046
1484
|
return this.sendMessage(notificationEnvelope(method, params));
|
|
1047
1485
|
}
|
|
1486
|
+
_trackInboundDeliveryAck(event) {
|
|
1487
|
+
if (String(event.type ?? "") === "event_batch") {
|
|
1488
|
+
const events = event.events;
|
|
1489
|
+
if (Array.isArray(events)) {
|
|
1490
|
+
for (const sub of events) {
|
|
1491
|
+
if (sub && typeof sub === "object") {
|
|
1492
|
+
this._trackInboundDeliveryAck(sub);
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
return;
|
|
1497
|
+
}
|
|
1498
|
+
if (!inboundNeedsDeliveryAck(event)) return;
|
|
1499
|
+
const loopId = extractLoopIdFromInbound(event);
|
|
1500
|
+
if (!loopId) return;
|
|
1501
|
+
const next = (this.deliveryRecvSeq.get(loopId) ?? 0) + 1;
|
|
1502
|
+
this.deliveryRecvSeq.set(loopId, next);
|
|
1503
|
+
void this._sendDeliveryAck(loopId, next);
|
|
1504
|
+
}
|
|
1505
|
+
async _sendDeliveryAck(loopId, seq) {
|
|
1506
|
+
const acked = this.deliveryAckedSeq.get(loopId) ?? 0;
|
|
1507
|
+
if (seq <= acked) return;
|
|
1508
|
+
this.deliveryAckedSeq.set(loopId, seq);
|
|
1509
|
+
if (!this.isConnected()) return;
|
|
1510
|
+
try {
|
|
1511
|
+
await this.notify("delivery_ack", { loop_id: loopId, seq });
|
|
1512
|
+
} catch {
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1048
1515
|
/**
|
|
1049
1516
|
* Starts a subscription stream. Returns the subscription `id` for later
|
|
1050
1517
|
* correlation and `unsubscribe()`. Stream events arrive as `next` frames
|
|
@@ -1062,7 +1529,7 @@ var init_client = __esm({
|
|
|
1062
1529
|
if (ev === null) break;
|
|
1063
1530
|
const evId = ev.id;
|
|
1064
1531
|
if (evId !== subId) {
|
|
1065
|
-
this.
|
|
1532
|
+
this.enqueueMessageBuffer(ev);
|
|
1066
1533
|
continue;
|
|
1067
1534
|
}
|
|
1068
1535
|
const typ = ev.type;
|
|
@@ -1099,7 +1566,7 @@ var init_client = __esm({
|
|
|
1099
1566
|
return ev;
|
|
1100
1567
|
}
|
|
1101
1568
|
// ---------------------------------------------------------------------------
|
|
1102
|
-
// High-level API methods
|
|
1569
|
+
// High-level API methods
|
|
1103
1570
|
// ---------------------------------------------------------------------------
|
|
1104
1571
|
/** Sends user input to the daemon (loop_input notification; requires loopID). */
|
|
1105
1572
|
sendInput(text, options) {
|
|
@@ -1138,7 +1605,7 @@ var init_client = __esm({
|
|
|
1138
1605
|
return this.notify("slash_command", { cmd });
|
|
1139
1606
|
}
|
|
1140
1607
|
// ---------------------------------------------------------------------------
|
|
1141
|
-
// Loop lifecycle methods
|
|
1608
|
+
// Loop lifecycle methods
|
|
1142
1609
|
// ---------------------------------------------------------------------------
|
|
1143
1610
|
/** Requests the daemon to create a new StrangeLoop and waits for the response. */
|
|
1144
1611
|
sendLoopNew(opts) {
|
|
@@ -1233,7 +1700,7 @@ var init_client = __esm({
|
|
|
1233
1700
|
sendLoopCardsFetch(loopID) {
|
|
1234
1701
|
return this.sendMessage(requestEnvelope("loop_cards_fetch", { loop_id: loopID }));
|
|
1235
1702
|
}
|
|
1236
|
-
/** Requests the full loop history
|
|
1703
|
+
/** Requests the full loop history. */
|
|
1237
1704
|
sendLoopHistoryFetch(loopID) {
|
|
1238
1705
|
return this.sendMessage(requestEnvelope("loop_history_fetch", { loop_id: loopID }));
|
|
1239
1706
|
}
|
|
@@ -1328,7 +1795,7 @@ var init_client = __esm({
|
|
|
1328
1795
|
);
|
|
1329
1796
|
}
|
|
1330
1797
|
// ---------------------------------------------------------------------------
|
|
1331
|
-
//
|
|
1798
|
+
// Job IPC methods
|
|
1332
1799
|
// ---------------------------------------------------------------------------
|
|
1333
1800
|
/** Creates an autopilot job and waits for the response. */
|
|
1334
1801
|
createJob(goal, verificationRules, workspace, timeout) {
|
|
@@ -1363,6 +1830,98 @@ var init_client = __esm({
|
|
|
1363
1830
|
if (goalId) params.goal_id = goalId;
|
|
1364
1831
|
return this.requestResponse("job_guidance", params, "job_guidance", timeout ?? 3e4);
|
|
1365
1832
|
}
|
|
1833
|
+
// ---------------------------------------------------------------------------
|
|
1834
|
+
// Autopilot goal RPCs (protocol-1 request methods)
|
|
1835
|
+
// ---------------------------------------------------------------------------
|
|
1836
|
+
/** Return autopilot scheduler status (running / dreaming / pool). */
|
|
1837
|
+
autopilotStatus(timeout) {
|
|
1838
|
+
return this.requestResponse("autopilot_status", {}, "autopilot_status", timeout ?? 15e3);
|
|
1839
|
+
}
|
|
1840
|
+
/** Submit a new autopilot goal (returns goal_id). */
|
|
1841
|
+
autopilotSubmit(description, opts) {
|
|
1842
|
+
const params = {
|
|
1843
|
+
description,
|
|
1844
|
+
priority: opts?.priority ?? 50
|
|
1845
|
+
};
|
|
1846
|
+
if (opts?.workspace) params.workspace = opts.workspace;
|
|
1847
|
+
return this.requestResponse(
|
|
1848
|
+
"autopilot_submit",
|
|
1849
|
+
params,
|
|
1850
|
+
"autopilot_submit",
|
|
1851
|
+
opts?.timeout ?? 15e3
|
|
1852
|
+
);
|
|
1853
|
+
}
|
|
1854
|
+
/** List all goals (including non-root children). */
|
|
1855
|
+
autopilotListGoals(timeout) {
|
|
1856
|
+
return this.requestResponse(
|
|
1857
|
+
"autopilot_list_goals",
|
|
1858
|
+
{},
|
|
1859
|
+
"autopilot_list_goals",
|
|
1860
|
+
timeout ?? 15e3
|
|
1861
|
+
);
|
|
1862
|
+
}
|
|
1863
|
+
/** Fetch one goal by id. */
|
|
1864
|
+
autopilotGetGoal(goalId, timeout) {
|
|
1865
|
+
return this.requestResponse(
|
|
1866
|
+
"autopilot_get_goal",
|
|
1867
|
+
{ goal_id: goalId },
|
|
1868
|
+
"autopilot_get_goal",
|
|
1869
|
+
timeout ?? 15e3
|
|
1870
|
+
);
|
|
1871
|
+
}
|
|
1872
|
+
/** Cancel a goal and its non-terminal descendants. */
|
|
1873
|
+
autopilotCancelGoal(goalId, timeout) {
|
|
1874
|
+
return this.requestResponse(
|
|
1875
|
+
"autopilot_cancel_goal",
|
|
1876
|
+
{ goal_id: goalId },
|
|
1877
|
+
"autopilot_cancel_goal",
|
|
1878
|
+
timeout ?? 15e3
|
|
1879
|
+
);
|
|
1880
|
+
}
|
|
1881
|
+
/** Cancel every open (non-terminal) goal. */
|
|
1882
|
+
autopilotCancelAll(timeout) {
|
|
1883
|
+
return this.requestResponse(
|
|
1884
|
+
"autopilot_cancel_all",
|
|
1885
|
+
{},
|
|
1886
|
+
"autopilot_cancel_all",
|
|
1887
|
+
timeout ?? 15e3
|
|
1888
|
+
);
|
|
1889
|
+
}
|
|
1890
|
+
/** Exit dreaming mode and resume scheduling. */
|
|
1891
|
+
autopilotWake(timeout) {
|
|
1892
|
+
return this.requestResponse("autopilot_wake", {}, "autopilot_wake", timeout ?? 15e3);
|
|
1893
|
+
}
|
|
1894
|
+
/** Force dreaming mode. */
|
|
1895
|
+
autopilotDream(timeout) {
|
|
1896
|
+
return this.requestResponse("autopilot_dream", {}, "autopilot_dream", timeout ?? 15e3);
|
|
1897
|
+
}
|
|
1898
|
+
/** Resume a suspended or blocked goal. */
|
|
1899
|
+
autopilotResume(goalId, timeout) {
|
|
1900
|
+
return this.requestResponse(
|
|
1901
|
+
"autopilot_resume",
|
|
1902
|
+
{ goal_id: goalId },
|
|
1903
|
+
"autopilot_resume",
|
|
1904
|
+
timeout ?? 15e3
|
|
1905
|
+
);
|
|
1906
|
+
}
|
|
1907
|
+
/** List root goals only (jobs). Prefer createJob / getJobStatus for job control. */
|
|
1908
|
+
autopilotListJobs(timeout) {
|
|
1909
|
+
return this.requestResponse(
|
|
1910
|
+
"autopilot_list_jobs",
|
|
1911
|
+
{},
|
|
1912
|
+
"autopilot_list_jobs",
|
|
1913
|
+
timeout ?? 15e3
|
|
1914
|
+
);
|
|
1915
|
+
}
|
|
1916
|
+
/** Get a root job with DAG snapshot. Prefer getJobStatus / getJobDag. */
|
|
1917
|
+
autopilotGetJob(jobId, timeout) {
|
|
1918
|
+
return this.requestResponse(
|
|
1919
|
+
"autopilot_get_job",
|
|
1920
|
+
{ job_id: jobId },
|
|
1921
|
+
"autopilot_get_job",
|
|
1922
|
+
timeout ?? 15e3
|
|
1923
|
+
);
|
|
1924
|
+
}
|
|
1366
1925
|
/** Subscribes to autopilot worker events. */
|
|
1367
1926
|
autopilotSubscribe(timeout) {
|
|
1368
1927
|
return this.subscribe("autopilot_events", {}, timeout ?? 15e3);
|
|
@@ -1373,7 +1932,7 @@ var init_client = __esm({
|
|
|
1373
1932
|
return this._requestResponseForEnvelope(req, "autopilot_unsubscribe", timeout ?? 15e3);
|
|
1374
1933
|
}
|
|
1375
1934
|
// ---------------------------------------------------------------------------
|
|
1376
|
-
//
|
|
1935
|
+
// Cron IPC methods
|
|
1377
1936
|
// ---------------------------------------------------------------------------
|
|
1378
1937
|
/** Creates a scheduled job from natural language. */
|
|
1379
1938
|
cronAdd(text, priority, timeout) {
|
|
@@ -1437,13 +1996,17 @@ __export(index_exports, {
|
|
|
1437
1996
|
CLIENT_VERSION: () => CLIENT_VERSION,
|
|
1438
1997
|
ChatEventTerminal: () => ChatEventTerminal,
|
|
1439
1998
|
Client: () => Client,
|
|
1999
|
+
CommandClient: () => CommandClient,
|
|
1440
2000
|
ConnectionError: () => ConnectionError,
|
|
1441
2001
|
ConnectionPool: () => ConnectionPool,
|
|
1442
2002
|
DEFAULT_CLIENT_CAPABILITIES: () => DEFAULT_CLIENT_CAPABILITIES,
|
|
1443
2003
|
DEFAULT_DELIVERABLE_PHASES: () => DEFAULT_DELIVERABLE_PHASES,
|
|
2004
|
+
DEFAULT_POST_IDLE_DRAIN_MS: () => DEFAULT_POST_IDLE_DRAIN_MS,
|
|
1444
2005
|
DEFAULT_THINKING_STEP_EVENTS: () => DEFAULT_THINKING_STEP_EVENTS,
|
|
1445
2006
|
DaemonError: () => DaemonError,
|
|
2007
|
+
DaemonSession: () => DaemonSession,
|
|
1446
2008
|
DisconnectCause: () => DisconnectCause,
|
|
2009
|
+
ErrIdleTimeout: () => ErrIdleTimeout,
|
|
1447
2010
|
ErrPoolExhausted: () => ErrPoolExhausted,
|
|
1448
2011
|
ErrQueryBusy: () => ErrQueryBusy,
|
|
1449
2012
|
ErrQueryTimeout: () => ErrQueryTimeout,
|
|
@@ -1489,26 +2052,31 @@ __export(index_exports, {
|
|
|
1489
2052
|
INTENT_HINT_OCR: () => INTENT_HINT_OCR,
|
|
1490
2053
|
INTENT_HINT_TEXT_COMPLETION: () => INTENT_HINT_TEXT_COMPLETION,
|
|
1491
2054
|
LOOP_ASSISTANT_OUTPUT_PHASES: () => LOOP_ASSISTANT_OUTPUT_PHASES,
|
|
1492
|
-
Multiplexer: () => Multiplexer,
|
|
1493
2055
|
PROTO_VERSION: () => PROTO_VERSION,
|
|
1494
2056
|
PooledConn: () => PooledConn,
|
|
1495
2057
|
QueryGate: () => QueryGate,
|
|
1496
2058
|
REMOVED_INTENT_HINTS: () => REMOVED_INTENT_HINTS,
|
|
1497
2059
|
ReconnectError: () => ReconnectError,
|
|
1498
2060
|
SSEBroadcaster: () => SSEBroadcaster,
|
|
2061
|
+
STREAM_END: () => STREAM_END,
|
|
1499
2062
|
StaleLoopError: () => StaleLoopError,
|
|
2063
|
+
StreamCloseFail: () => StreamCloseFail,
|
|
2064
|
+
StreamCloseSoftComplete: () => StreamCloseSoftComplete,
|
|
1500
2065
|
TimeoutError: () => TimeoutError,
|
|
2066
|
+
TimeoutPolicy: () => TimeoutPolicy,
|
|
2067
|
+
TurnEventStats: () => TurnEventStats,
|
|
1501
2068
|
TurnRunner: () => TurnRunner,
|
|
1502
2069
|
VerbosityTier: () => VerbosityTier,
|
|
1503
2070
|
authenticate: () => authenticate,
|
|
1504
2071
|
bootstrapLoopSession: () => bootstrapLoopSession,
|
|
1505
2072
|
checkDaemonStatus: () => checkDaemonStatus,
|
|
1506
2073
|
classifyEventVerbosity: () => classifyEventVerbosity,
|
|
2074
|
+
compactAttachments: () => compactAttachments,
|
|
2075
|
+
compactImageAttachment: () => compactImageAttachment,
|
|
1507
2076
|
connectWithRetries: () => connectWithRetries,
|
|
2077
|
+
connectedWebsocket: () => connectedWebsocket,
|
|
1508
2078
|
connectionInitEnvelope: () => connectionInitEnvelope,
|
|
1509
2079
|
decodeMessage: () => decodeMessage,
|
|
1510
|
-
defaultBootstrapFunc: () => defaultBootstrapFunc,
|
|
1511
|
-
defaultClientFactory: () => defaultClientFactory,
|
|
1512
2080
|
defaultConfig: () => defaultConfig,
|
|
1513
2081
|
defaultPoolConfig: () => defaultPoolConfig,
|
|
1514
2082
|
disconnectCauseName: () => disconnectCauseName,
|
|
@@ -1517,12 +2085,18 @@ __export(index_exports, {
|
|
|
1517
2085
|
extractSootheLoopID: () => extractSootheLoopID,
|
|
1518
2086
|
extractThinkingStep: () => extractThinkingStep,
|
|
1519
2087
|
fetchConfigSection: () => fetchConfigSection,
|
|
2088
|
+
fetchLoopCards: () => fetchLoopCards,
|
|
1520
2089
|
fetchLoopHistory: () => fetchLoopHistory,
|
|
2090
|
+
fetchLoopMessages: () => fetchLoopMessages,
|
|
1521
2091
|
fetchSkillsCatalog: () => fetchSkillsCatalog,
|
|
2092
|
+
idleTimeoutForTurn: () => idleTimeoutForTurn,
|
|
2093
|
+
inboundNeedsDeliveryAck: () => inboundNeedsDeliveryAck,
|
|
1522
2094
|
inputMessageForLoop: () => inputMessageForLoop,
|
|
1523
2095
|
isCompletionEvent: () => isCompletionEvent,
|
|
1524
2096
|
isDaemonLive: () => isDaemonLive,
|
|
1525
2097
|
isSubagentProgressEvent: () => isSubagentProgressEvent,
|
|
2098
|
+
isTurnEndCustomData: () => isTurnEndCustomData,
|
|
2099
|
+
isTurnProgressChunk: () => isTurnProgressChunk,
|
|
1526
2100
|
isValidVerbosityLevel: () => isValidVerbosityLevel,
|
|
1527
2101
|
loadConfigFromEnv: () => loadConfigFromEnv,
|
|
1528
2102
|
newLoopInputMessage: () => newLoopInputMessage,
|
|
@@ -1533,6 +2107,7 @@ __export(index_exports, {
|
|
|
1533
2107
|
parseNamespace: () => parseNamespace,
|
|
1534
2108
|
pingEnvelope: () => pingEnvelope,
|
|
1535
2109
|
pongEnvelope: () => pongEnvelope,
|
|
2110
|
+
protocol1Rpc: () => protocol1Rpc,
|
|
1536
2111
|
refreshAuthToken: () => refreshAuthToken,
|
|
1537
2112
|
requestDaemonConfigReload: () => requestDaemonConfigReload,
|
|
1538
2113
|
requestDaemonShutdown: () => requestDaemonShutdown,
|
|
@@ -1548,237 +2123,16 @@ __export(index_exports, {
|
|
|
1548
2123
|
});
|
|
1549
2124
|
module.exports = __toCommonJS(index_exports);
|
|
1550
2125
|
init_errors();
|
|
1551
|
-
|
|
1552
|
-
// src/verbosity.ts
|
|
1553
|
-
var VerbosityTier = /* @__PURE__ */ ((VerbosityTier2) => {
|
|
1554
|
-
VerbosityTier2[VerbosityTier2["Quiet"] = 0] = "Quiet";
|
|
1555
|
-
VerbosityTier2[VerbosityTier2["Normal"] = 1] = "Normal";
|
|
1556
|
-
VerbosityTier2[VerbosityTier2["Detailed"] = 2] = "Detailed";
|
|
1557
|
-
VerbosityTier2[VerbosityTier2["Debug"] = 3] = "Debug";
|
|
1558
|
-
VerbosityTier2[VerbosityTier2["Internal"] = 99] = "Internal";
|
|
1559
|
-
return VerbosityTier2;
|
|
1560
|
-
})(VerbosityTier || {});
|
|
1561
|
-
var verbosityLevelValues = {
|
|
1562
|
-
quiet: 0,
|
|
1563
|
-
normal: 1,
|
|
1564
|
-
debug: 3
|
|
1565
|
-
};
|
|
1566
|
-
function shouldShow(tier, verbosity) {
|
|
1567
|
-
if (tier === 99 /* Internal */) {
|
|
1568
|
-
return false;
|
|
1569
|
-
}
|
|
1570
|
-
const level = verbosityLevelValues[verbosity] ?? 1;
|
|
1571
|
-
return tier <= level;
|
|
1572
|
-
}
|
|
1573
|
-
function isValidVerbosityLevel(s) {
|
|
1574
|
-
return s in verbosityLevelValues;
|
|
1575
|
-
}
|
|
1576
|
-
|
|
1577
|
-
// src/index.ts
|
|
2126
|
+
init_verbosity();
|
|
1578
2127
|
init_config();
|
|
1579
2128
|
init_protocol();
|
|
1580
2129
|
init_intent_hints();
|
|
2130
|
+
init_events();
|
|
2131
|
+
init_client();
|
|
1581
2132
|
|
|
1582
|
-
// src/
|
|
1583
|
-
var EventPlanCreated = "soothe.cognition.plan.created";
|
|
1584
|
-
var EventExploreStarted = "soothe.subagent.explore.started";
|
|
1585
|
-
var EventExploreMilestone = "soothe.subagent.explore.milestone";
|
|
1586
|
-
var EventExploreStepCompleted = "soothe.subagent.explore.step.completed";
|
|
1587
|
-
var EventExploreCompleted = "soothe.subagent.explore.completed";
|
|
1588
|
-
var EventTacitusStarted = "soothe.subagent.tacitus.started";
|
|
1589
|
-
var EventTacitusGatherSummary = "soothe.subagent.tacitus.gather.summary";
|
|
1590
|
-
var EventTacitusCompleted = "soothe.subagent.tacitus.completed";
|
|
1591
|
-
var EventReplayComplete = "replay_complete";
|
|
1592
|
-
var EventLoopReattachedWire = "loop_reattached";
|
|
1593
|
-
var EventCardReplayBegin = "card.replay_begin";
|
|
1594
|
-
var EventCardCreated = "card.created";
|
|
1595
|
-
var EventCardReplayEnd = "card.replay_end";
|
|
1596
|
-
var EventToolStarted = "soothe.tool.execution.started";
|
|
1597
|
-
var EventToolCompleted = "soothe.tool.execution.completed";
|
|
1598
|
-
var EventToolError = "soothe.tool.execution.error";
|
|
1599
|
-
var EventStreamToolCallUpdate = "soothe.stream.tool_call.update";
|
|
1600
|
-
var EventToolCallUpdatesBatch = "tool_call_updates_batch";
|
|
1601
|
-
var EventStrangeLoopStarted = "soothe.cognition.strange_loop.started";
|
|
1602
|
-
var EventStrangeLoopCompleted = "soothe.cognition.strange_loop.completed";
|
|
1603
|
-
var EventStrangeLoopPlanDecision = "soothe.cognition.strange_loop.plan.decision";
|
|
1604
|
-
var EventStrangeLoopReasoned = "soothe.cognition.strange_loop.reasoned";
|
|
1605
|
-
var EventStrangeLoopStepStarted = "soothe.cognition.strange_loop.step.started";
|
|
1606
|
-
var EventStrangeLoopStepQueued = "soothe.cognition.strange_loop.step.queued";
|
|
1607
|
-
var EventStrangeLoopStepCompleted = "soothe.cognition.strange_loop.step.completed";
|
|
1608
|
-
var EventStrangeLoopContextCompacted = "soothe.cognition.strange_loop.context.compacted";
|
|
1609
|
-
var EventMessageReceived = "soothe.protocol.message.received";
|
|
1610
|
-
var EventMessageSent = "soothe.protocol.message.sent";
|
|
1611
|
-
var EventFinalReport = "soothe.output.autonomous.final_report.reported";
|
|
1612
|
-
var EventAutopilotGoalStatus = "soothe.autopilot.goal.status";
|
|
1613
|
-
var EventAutopilotGoalProgress = "soothe.autopilot.goal.progress";
|
|
1614
|
-
var EventAutopilotGoalCreated = "soothe.autopilot.goal.created";
|
|
1615
|
-
var EventAutopilotGoalCompleted = "soothe.autopilot.goal.completed";
|
|
1616
|
-
var EventAutopilotWorkerAssigned = "soothe.autopilot.worker.assigned";
|
|
1617
|
-
var EventAutopilotWorkerUnassigned = "soothe.autopilot.worker.unassigned";
|
|
1618
|
-
var EventGeneralFailed = "soothe.error.general.failed";
|
|
1619
|
-
function parseNamespace(ns) {
|
|
1620
|
-
const parts = splitNamespace(ns);
|
|
1621
|
-
if (parts.length < 4 || parts[0] !== "soothe") {
|
|
1622
|
-
return null;
|
|
1623
|
-
}
|
|
1624
|
-
if (parts[1] === "internal") {
|
|
1625
|
-
return null;
|
|
1626
|
-
}
|
|
1627
|
-
return { domain: parts[1], component: parts[2], action: parts[3] };
|
|
1628
|
-
}
|
|
1629
|
-
function splitNamespace(ns) {
|
|
1630
|
-
const parts = [];
|
|
1631
|
-
let start = 0;
|
|
1632
|
-
for (let i = 0; i < ns.length; i++) {
|
|
1633
|
-
if (ns[i] === ".") {
|
|
1634
|
-
parts.push(ns.slice(start, i));
|
|
1635
|
-
start = i + 1;
|
|
1636
|
-
}
|
|
1637
|
-
}
|
|
1638
|
-
parts.push(ns.slice(start));
|
|
1639
|
-
return parts;
|
|
1640
|
-
}
|
|
1641
|
-
function classifyEventVerbosity(eventTypeOrNamespace) {
|
|
1642
|
-
const parsed = parseNamespace(eventTypeOrNamespace);
|
|
1643
|
-
if (!parsed) {
|
|
1644
|
-
return classifyByEventTypeString(eventTypeOrNamespace);
|
|
1645
|
-
}
|
|
1646
|
-
return classifyByDomainAndComponent(parsed.domain, parsed.component, eventTypeOrNamespace);
|
|
1647
|
-
}
|
|
1648
|
-
function classifyByDomainAndComponent(domain, _component, full) {
|
|
1649
|
-
switch (domain) {
|
|
1650
|
-
case "cognition":
|
|
1651
|
-
return 1 /* Normal */;
|
|
1652
|
-
case "protocol":
|
|
1653
|
-
return 2 /* Detailed */;
|
|
1654
|
-
case "tool":
|
|
1655
|
-
return 99 /* Internal */;
|
|
1656
|
-
case "subagent":
|
|
1657
|
-
return classifySubagentEvent(full);
|
|
1658
|
-
case "autopilot":
|
|
1659
|
-
return 1 /* Normal */;
|
|
1660
|
-
case "output":
|
|
1661
|
-
case "error":
|
|
1662
|
-
return 0 /* Quiet */;
|
|
1663
|
-
default:
|
|
1664
|
-
return 1 /* Normal */;
|
|
1665
|
-
}
|
|
1666
|
-
}
|
|
1667
|
-
function classifySubagentEvent(full) {
|
|
1668
|
-
const parsed = parseNamespace(full);
|
|
1669
|
-
if (!parsed) return 1 /* Normal */;
|
|
1670
|
-
switch (parsed.action) {
|
|
1671
|
-
case "started":
|
|
1672
|
-
case "completed":
|
|
1673
|
-
return 1 /* Normal */;
|
|
1674
|
-
default:
|
|
1675
|
-
return 2 /* Detailed */;
|
|
1676
|
-
}
|
|
1677
|
-
}
|
|
1678
|
-
function classifyByEventTypeString(eventType) {
|
|
1679
|
-
if (eventType === EventFinalReport || eventType === EventGeneralFailed) {
|
|
1680
|
-
return 0 /* Quiet */;
|
|
1681
|
-
}
|
|
1682
|
-
if (eventType === EventToolStarted) {
|
|
1683
|
-
return 99 /* Internal */;
|
|
1684
|
-
}
|
|
1685
|
-
return 1 /* Normal */;
|
|
1686
|
-
}
|
|
1687
|
-
function isCompletionEvent(eventType) {
|
|
1688
|
-
return eventType.endsWith(".completed") || eventType.endsWith(".failed") || eventType === EventGeneralFailed;
|
|
1689
|
-
}
|
|
1690
|
-
function isSubagentProgressEvent(eventType) {
|
|
1691
|
-
const parsed = parseNamespace(eventType);
|
|
1692
|
-
if (!parsed || parsed.domain !== "subagent") {
|
|
1693
|
-
return false;
|
|
1694
|
-
}
|
|
1695
|
-
return parsed.action === "started" || parsed.action === "completed";
|
|
1696
|
-
}
|
|
1697
|
-
|
|
1698
|
-
// src/index.ts
|
|
2133
|
+
// src/command_client.ts
|
|
1699
2134
|
init_client();
|
|
1700
|
-
init_multiplexer();
|
|
1701
|
-
|
|
1702
|
-
// src/helpers.ts
|
|
1703
2135
|
init_config();
|
|
1704
|
-
async function checkDaemonStatus(client, timeout) {
|
|
1705
|
-
return client.requestResponse("daemon_status", {}, "daemon_status", timeout ?? 5e3);
|
|
1706
|
-
}
|
|
1707
|
-
async function isDaemonLive(wsURL, timeout) {
|
|
1708
|
-
const { Client: Client2 } = await Promise.resolve().then(() => (init_client(), client_exports));
|
|
1709
|
-
const t = timeout ?? 5e3;
|
|
1710
|
-
const client = new Client2(wsURL, defaultConfig());
|
|
1711
|
-
try {
|
|
1712
|
-
await client.connect();
|
|
1713
|
-
} catch {
|
|
1714
|
-
return false;
|
|
1715
|
-
}
|
|
1716
|
-
try {
|
|
1717
|
-
await checkDaemonStatus(client, t);
|
|
1718
|
-
return true;
|
|
1719
|
-
} catch {
|
|
1720
|
-
return false;
|
|
1721
|
-
} finally {
|
|
1722
|
-
client.close();
|
|
1723
|
-
}
|
|
1724
|
-
}
|
|
1725
|
-
async function requestDaemonShutdown(client, timeout) {
|
|
1726
|
-
const resp = await client.requestResponse(
|
|
1727
|
-
"daemon_shutdown",
|
|
1728
|
-
{},
|
|
1729
|
-
"daemon_shutdown",
|
|
1730
|
-
timeout ?? 1e4
|
|
1731
|
-
);
|
|
1732
|
-
if (resp.status !== "acknowledged") {
|
|
1733
|
-
throw new Error(`shutdown not acknowledged: ${JSON.stringify(resp)}`);
|
|
1734
|
-
}
|
|
1735
|
-
}
|
|
1736
|
-
async function fetchSkillsCatalog(client, timeout) {
|
|
1737
|
-
const resp = await client.requestResponse("skills_list", {}, "skills_list", timeout ?? 15e3);
|
|
1738
|
-
const skillsRaw = resp.skills;
|
|
1739
|
-
if (!skillsRaw || !Array.isArray(skillsRaw)) return [];
|
|
1740
|
-
return skillsRaw.filter((s) => typeof s === "object" && s !== null);
|
|
1741
|
-
}
|
|
1742
|
-
async function fetchConfigSection(client, section, timeout) {
|
|
1743
|
-
const resp = await client.requestResponse(
|
|
1744
|
-
"config_get",
|
|
1745
|
-
{ section },
|
|
1746
|
-
"config_get",
|
|
1747
|
-
timeout ?? 5e3
|
|
1748
|
-
);
|
|
1749
|
-
const sec = resp[section];
|
|
1750
|
-
if (sec && typeof sec === "object") {
|
|
1751
|
-
return sec;
|
|
1752
|
-
}
|
|
1753
|
-
return resp;
|
|
1754
|
-
}
|
|
1755
|
-
async function requestDaemonConfigReload(client, timeout) {
|
|
1756
|
-
return client.requestResponse("config_reload", {}, "config_reload", timeout ?? 15e3);
|
|
1757
|
-
}
|
|
1758
|
-
async function fetchLoopHistory(client, loopID, timeout) {
|
|
1759
|
-
return client.requestResponse(
|
|
1760
|
-
"loop_history_fetch",
|
|
1761
|
-
{ loop_id: loopID },
|
|
1762
|
-
"loop_history_fetch",
|
|
1763
|
-
timeout ?? 15e3
|
|
1764
|
-
);
|
|
1765
|
-
}
|
|
1766
|
-
async function authenticate(client, accessKey, secretKey, timeout) {
|
|
1767
|
-
return client.requestResponse(
|
|
1768
|
-
"auth",
|
|
1769
|
-
{ access_key: accessKey, secret_key: secretKey },
|
|
1770
|
-
"auth",
|
|
1771
|
-
timeout ?? 15e3
|
|
1772
|
-
);
|
|
1773
|
-
}
|
|
1774
|
-
async function refreshAuthToken(client, refreshToken, timeout) {
|
|
1775
|
-
return client.requestResponse(
|
|
1776
|
-
"auth_refresh",
|
|
1777
|
-
{ refresh_token: refreshToken },
|
|
1778
|
-
"auth_refresh",
|
|
1779
|
-
timeout ?? 15e3
|
|
1780
|
-
);
|
|
1781
|
-
}
|
|
1782
2136
|
|
|
1783
2137
|
// src/session.ts
|
|
1784
2138
|
init_config();
|
|
@@ -1882,6 +2236,256 @@ async function connectWithRetries(client, maxRetries, retryDelay) {
|
|
|
1882
2236
|
);
|
|
1883
2237
|
}
|
|
1884
2238
|
|
|
2239
|
+
// src/command_client.ts
|
|
2240
|
+
var CommandClient = class {
|
|
2241
|
+
url;
|
|
2242
|
+
timeoutMs;
|
|
2243
|
+
config;
|
|
2244
|
+
constructor(url, opts) {
|
|
2245
|
+
this.url = url;
|
|
2246
|
+
this.timeoutMs = opts?.timeoutMs && opts.timeoutMs > 0 ? opts.timeoutMs : 3e4;
|
|
2247
|
+
this.config = opts?.config ?? defaultConfig();
|
|
2248
|
+
}
|
|
2249
|
+
async withClient(fn) {
|
|
2250
|
+
const client = new Client(this.url, this.config);
|
|
2251
|
+
try {
|
|
2252
|
+
await connectWithRetries(client, 5, 250);
|
|
2253
|
+
return await fn(client);
|
|
2254
|
+
} finally {
|
|
2255
|
+
client.close();
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2258
|
+
/** Generic one-shot RPC. */
|
|
2259
|
+
async request(method, params = {}) {
|
|
2260
|
+
return this.withClient(
|
|
2261
|
+
(client) => client.requestResponse(method, params, void 0, this.timeoutMs)
|
|
2262
|
+
);
|
|
2263
|
+
}
|
|
2264
|
+
async jobCreate(goal, workspace = "") {
|
|
2265
|
+
const params = { goal };
|
|
2266
|
+
if (workspace) params.workspace = workspace;
|
|
2267
|
+
return this.request("job_create", params);
|
|
2268
|
+
}
|
|
2269
|
+
async jobStatus(jobId) {
|
|
2270
|
+
return this.request("job_status", { job_id: jobId });
|
|
2271
|
+
}
|
|
2272
|
+
async jobCancel(jobId) {
|
|
2273
|
+
return this.request("job_cancel", { job_id: jobId });
|
|
2274
|
+
}
|
|
2275
|
+
/** Return autopilot scheduler status (running / dreaming / pool). */
|
|
2276
|
+
async autopilotStatus() {
|
|
2277
|
+
return this.request("autopilot_status");
|
|
2278
|
+
}
|
|
2279
|
+
/** Submit a new autopilot goal (returns goal_id). */
|
|
2280
|
+
async autopilotSubmit(description, opts) {
|
|
2281
|
+
const params = {
|
|
2282
|
+
description,
|
|
2283
|
+
priority: opts?.priority ?? 50
|
|
2284
|
+
};
|
|
2285
|
+
if (opts?.workspace) params.workspace = opts.workspace;
|
|
2286
|
+
return this.request("autopilot_submit", params);
|
|
2287
|
+
}
|
|
2288
|
+
/** List all goals (including non-root children). */
|
|
2289
|
+
async autopilotListGoals() {
|
|
2290
|
+
return this.request("autopilot_list_goals");
|
|
2291
|
+
}
|
|
2292
|
+
/** Fetch one goal by id. */
|
|
2293
|
+
async autopilotGetGoal(goalId) {
|
|
2294
|
+
return this.request("autopilot_get_goal", { goal_id: goalId });
|
|
2295
|
+
}
|
|
2296
|
+
/** Cancel a goal and its non-terminal descendants. */
|
|
2297
|
+
async autopilotCancelGoal(goalId) {
|
|
2298
|
+
return this.request("autopilot_cancel_goal", { goal_id: goalId });
|
|
2299
|
+
}
|
|
2300
|
+
/** Cancel every open (non-terminal) goal. */
|
|
2301
|
+
async autopilotCancelAll() {
|
|
2302
|
+
return this.request("autopilot_cancel_all");
|
|
2303
|
+
}
|
|
2304
|
+
/** Exit dreaming mode and resume scheduling. */
|
|
2305
|
+
async autopilotWake() {
|
|
2306
|
+
return this.request("autopilot_wake");
|
|
2307
|
+
}
|
|
2308
|
+
/** Force dreaming mode. */
|
|
2309
|
+
async autopilotDream() {
|
|
2310
|
+
return this.request("autopilot_dream");
|
|
2311
|
+
}
|
|
2312
|
+
/** Resume a suspended or blocked goal. */
|
|
2313
|
+
async autopilotResume(goalId) {
|
|
2314
|
+
return this.request("autopilot_resume", { goal_id: goalId });
|
|
2315
|
+
}
|
|
2316
|
+
/** List root goals only (jobs). Prefer job* for job control. */
|
|
2317
|
+
async autopilotListJobs() {
|
|
2318
|
+
return this.request("autopilot_list_jobs");
|
|
2319
|
+
}
|
|
2320
|
+
/** Get a root job with DAG snapshot. Prefer jobStatus / getJobDag. */
|
|
2321
|
+
async autopilotGetJob(jobId) {
|
|
2322
|
+
return this.request("autopilot_get_job", { job_id: jobId });
|
|
2323
|
+
}
|
|
2324
|
+
async cronAdd(text, priority = 0) {
|
|
2325
|
+
const params = { text };
|
|
2326
|
+
if (priority > 0) params.priority = priority;
|
|
2327
|
+
return this.request("cron_add", params);
|
|
2328
|
+
}
|
|
2329
|
+
async cronList(status = "") {
|
|
2330
|
+
const params = {};
|
|
2331
|
+
if (status) params.status = status;
|
|
2332
|
+
return this.request("cron_list", params);
|
|
2333
|
+
}
|
|
2334
|
+
};
|
|
2335
|
+
|
|
2336
|
+
// src/helpers.ts
|
|
2337
|
+
init_config();
|
|
2338
|
+
async function checkDaemonStatus(client, timeout) {
|
|
2339
|
+
return client.requestResponse("daemon_status", {}, "daemon_status", timeout ?? 5e3);
|
|
2340
|
+
}
|
|
2341
|
+
async function isDaemonLive(wsURL, timeout) {
|
|
2342
|
+
const { Client: Client2 } = await Promise.resolve().then(() => (init_client(), client_exports));
|
|
2343
|
+
const t = timeout ?? 5e3;
|
|
2344
|
+
const client = new Client2(wsURL, defaultConfig());
|
|
2345
|
+
try {
|
|
2346
|
+
await client.connect();
|
|
2347
|
+
} catch {
|
|
2348
|
+
return false;
|
|
2349
|
+
}
|
|
2350
|
+
try {
|
|
2351
|
+
await checkDaemonStatus(client, t);
|
|
2352
|
+
return true;
|
|
2353
|
+
} catch {
|
|
2354
|
+
return false;
|
|
2355
|
+
} finally {
|
|
2356
|
+
client.close();
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
async function requestDaemonShutdown(client, timeout) {
|
|
2360
|
+
const resp = await client.requestResponse(
|
|
2361
|
+
"daemon_shutdown",
|
|
2362
|
+
{},
|
|
2363
|
+
"daemon_shutdown",
|
|
2364
|
+
timeout ?? 1e4
|
|
2365
|
+
);
|
|
2366
|
+
if (resp.status !== "acknowledged") {
|
|
2367
|
+
throw new Error(`shutdown not acknowledged: ${JSON.stringify(resp)}`);
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
2370
|
+
async function fetchSkillsCatalog(client, timeout) {
|
|
2371
|
+
const resp = await client.requestResponse("skills_list", {}, "skills_list", timeout ?? 15e3);
|
|
2372
|
+
const skillsRaw = resp.skills;
|
|
2373
|
+
if (!skillsRaw || !Array.isArray(skillsRaw)) return [];
|
|
2374
|
+
return skillsRaw.filter((s) => typeof s === "object" && s !== null);
|
|
2375
|
+
}
|
|
2376
|
+
async function fetchConfigSection(client, section, timeout) {
|
|
2377
|
+
const resp = await client.requestResponse(
|
|
2378
|
+
"config_get",
|
|
2379
|
+
{ section },
|
|
2380
|
+
"config_get",
|
|
2381
|
+
timeout ?? 5e3
|
|
2382
|
+
);
|
|
2383
|
+
const sec = resp[section];
|
|
2384
|
+
if (sec && typeof sec === "object") {
|
|
2385
|
+
return sec;
|
|
2386
|
+
}
|
|
2387
|
+
return resp;
|
|
2388
|
+
}
|
|
2389
|
+
async function requestDaemonConfigReload(client, timeout) {
|
|
2390
|
+
return client.requestResponse("config_reload", {}, "config_reload", timeout ?? 15e3);
|
|
2391
|
+
}
|
|
2392
|
+
async function fetchLoopHistory(client, loopID, timeout) {
|
|
2393
|
+
return client.requestResponse(
|
|
2394
|
+
"loop_history_fetch",
|
|
2395
|
+
{ loop_id: loopID },
|
|
2396
|
+
"loop_history_fetch",
|
|
2397
|
+
timeout ?? 15e3
|
|
2398
|
+
);
|
|
2399
|
+
}
|
|
2400
|
+
async function authenticate(client, accessKey, secretKey, timeout) {
|
|
2401
|
+
return client.requestResponse(
|
|
2402
|
+
"auth",
|
|
2403
|
+
{ access_key: accessKey, secret_key: secretKey },
|
|
2404
|
+
"auth",
|
|
2405
|
+
timeout ?? 15e3
|
|
2406
|
+
);
|
|
2407
|
+
}
|
|
2408
|
+
async function refreshAuthToken(client, refreshToken, timeout) {
|
|
2409
|
+
return client.requestResponse(
|
|
2410
|
+
"auth_refresh",
|
|
2411
|
+
{ refresh_token: refreshToken },
|
|
2412
|
+
"auth_refresh",
|
|
2413
|
+
timeout ?? 15e3
|
|
2414
|
+
);
|
|
2415
|
+
}
|
|
2416
|
+
async function fetchLoopCards(client, loopID, timeout) {
|
|
2417
|
+
return client.fetchLoopCards(loopID, timeout);
|
|
2418
|
+
}
|
|
2419
|
+
async function fetchLoopMessages(client, loopID, opts) {
|
|
2420
|
+
return client.getLoopMessages(
|
|
2421
|
+
loopID,
|
|
2422
|
+
opts?.limit,
|
|
2423
|
+
opts?.offset,
|
|
2424
|
+
opts?.includeEvents,
|
|
2425
|
+
opts?.timeout
|
|
2426
|
+
);
|
|
2427
|
+
}
|
|
2428
|
+
async function connectedWebsocket(wsUrl, fn, timeoutMs = 3e4) {
|
|
2429
|
+
const { Client: Client2 } = await Promise.resolve().then(() => (init_client(), client_exports));
|
|
2430
|
+
const client = new Client2(wsUrl, defaultConfig());
|
|
2431
|
+
const deadline = Date.now() + timeoutMs;
|
|
2432
|
+
try {
|
|
2433
|
+
await client.connect();
|
|
2434
|
+
while (!client.isConnected() && Date.now() < deadline) {
|
|
2435
|
+
await new Promise((r) => setTimeout(r, 25));
|
|
2436
|
+
}
|
|
2437
|
+
if (!client.isConnected()) {
|
|
2438
|
+
throw new Error("Timed out waiting for daemon handshake");
|
|
2439
|
+
}
|
|
2440
|
+
return await fn(client);
|
|
2441
|
+
} finally {
|
|
2442
|
+
client.close();
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2445
|
+
async function protocol1Rpc(wsUrl, method, params = null, opts = {}) {
|
|
2446
|
+
const mode = opts.mode ?? "request";
|
|
2447
|
+
const timeoutMs = opts.timeoutMs ?? 3e4;
|
|
2448
|
+
try {
|
|
2449
|
+
return await connectedWebsocket(
|
|
2450
|
+
wsUrl,
|
|
2451
|
+
async (client) => {
|
|
2452
|
+
if (mode === "notify") {
|
|
2453
|
+
await client.notify(method, params ?? {});
|
|
2454
|
+
return {};
|
|
2455
|
+
}
|
|
2456
|
+
if (mode === "subscribe") {
|
|
2457
|
+
const subId = await client.subscribe(
|
|
2458
|
+
method,
|
|
2459
|
+
params ?? {},
|
|
2460
|
+
timeoutMs
|
|
2461
|
+
);
|
|
2462
|
+
return { subscription_id: subId };
|
|
2463
|
+
}
|
|
2464
|
+
const result = await client.requestResponse(
|
|
2465
|
+
method,
|
|
2466
|
+
params ?? {},
|
|
2467
|
+
method,
|
|
2468
|
+
timeoutMs
|
|
2469
|
+
);
|
|
2470
|
+
return result && typeof result === "object" ? result : { result };
|
|
2471
|
+
},
|
|
2472
|
+
timeoutMs
|
|
2473
|
+
);
|
|
2474
|
+
} catch (exc) {
|
|
2475
|
+
const msg = exc instanceof Error ? exc.message : String(exc);
|
|
2476
|
+
if (msg.toLowerCase().includes("timed out") || msg.toLowerCase().includes("timeout")) {
|
|
2477
|
+
return { error: "Timed out waiting for daemon response" };
|
|
2478
|
+
}
|
|
2479
|
+
if (msg.toLowerCase().includes("connect") || msg.toLowerCase().includes("dial")) {
|
|
2480
|
+
return { error: `Connection error: ${msg}` };
|
|
2481
|
+
}
|
|
2482
|
+
return { error: msg };
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
|
|
2486
|
+
// src/index.ts
|
|
2487
|
+
init_stream_terminal();
|
|
2488
|
+
|
|
1885
2489
|
// src/appkit/broadcaster.ts
|
|
1886
2490
|
var SUBSCRIBER_QUEUE_CAP = 100;
|
|
1887
2491
|
var SSEBroadcaster = class {
|
|
@@ -1985,6 +2589,7 @@ var SSEBroadcaster = class {
|
|
|
1985
2589
|
|
|
1986
2590
|
// src/appkit/classifier.ts
|
|
1987
2591
|
init_errors();
|
|
2592
|
+
init_events();
|
|
1988
2593
|
|
|
1989
2594
|
// src/appkit/thinking_step.ts
|
|
1990
2595
|
var MAX_THINKING_STEP_RUNES = 280;
|
|
@@ -2104,6 +2709,7 @@ var EventClassifier = class {
|
|
|
2104
2709
|
deliverablePhases;
|
|
2105
2710
|
minDeliverableRunes;
|
|
2106
2711
|
thinkingStepEvents;
|
|
2712
|
+
treatStatusIdleAsComplete;
|
|
2107
2713
|
constructor(cfg) {
|
|
2108
2714
|
if (!cfg.deliverablePhases) {
|
|
2109
2715
|
throw new Error("appkit: ClassifierConfig.deliverablePhases must not be nil");
|
|
@@ -2111,6 +2717,7 @@ var EventClassifier = class {
|
|
|
2111
2717
|
this.deliverablePhases = cfg.deliverablePhases;
|
|
2112
2718
|
this.minDeliverableRunes = cfg.minDeliverableRunes && cfg.minDeliverableRunes > 0 ? cfg.minDeliverableRunes : 8;
|
|
2113
2719
|
this.thinkingStepEvents = cfg.thinkingStepEvents;
|
|
2720
|
+
this.treatStatusIdleAsComplete = Boolean(cfg.treatStatusIdleAsComplete);
|
|
2114
2721
|
}
|
|
2115
2722
|
/**
|
|
2116
2723
|
* Inspects one decoded event and returns its outcome. `accumulated` is the
|
|
@@ -2127,6 +2734,13 @@ var EventClassifier = class {
|
|
|
2127
2734
|
*/
|
|
2128
2735
|
isDeliverableCompletionEvent(eventType) {
|
|
2129
2736
|
if (!eventType) return false;
|
|
2737
|
+
switch (eventType) {
|
|
2738
|
+
case "status.idle":
|
|
2739
|
+
case "idle_timeout":
|
|
2740
|
+
case "query_timeout":
|
|
2741
|
+
case "stream_closed":
|
|
2742
|
+
return true;
|
|
2743
|
+
}
|
|
2130
2744
|
if (eventType === EventFinalReport) return true;
|
|
2131
2745
|
if (eventType.startsWith("soothe.protocol.message.")) {
|
|
2132
2746
|
const phase = eventType.slice("soothe.protocol.message.".length);
|
|
@@ -2162,16 +2776,22 @@ var EventClassifier = class {
|
|
|
2162
2776
|
return ["", false];
|
|
2163
2777
|
}
|
|
2164
2778
|
/** The event→outcome mapper, ported from triarch's ProcessChatEvent. */
|
|
2165
|
-
processChatEvent(msg,
|
|
2779
|
+
processChatEvent(msg, accumulated) {
|
|
2166
2780
|
if (!msg || typeof msg !== "object") {
|
|
2167
2781
|
return { terminal: 0 /* Continue */ };
|
|
2168
2782
|
}
|
|
2169
2783
|
const m = msg;
|
|
2170
2784
|
const typ = m.type;
|
|
2171
2785
|
if (typ === "next") {
|
|
2172
|
-
return this.classifyNextEnvelope(m);
|
|
2786
|
+
return this.classifyNextEnvelope(m, accumulated);
|
|
2173
2787
|
}
|
|
2174
|
-
if (typ === "response" || typ === "complete" || typ === "receipt_response" || typ === "connection_ack"
|
|
2788
|
+
if (typ === "response" || typ === "complete" || typ === "receipt_response" || typ === "connection_ack") {
|
|
2789
|
+
return { terminal: 0 /* Continue */ };
|
|
2790
|
+
}
|
|
2791
|
+
if (typ === "status") {
|
|
2792
|
+
if (this.treatStatusIdleAsComplete && String(m.state ?? "").trim().toLowerCase() === "idle" && this.isSubstantiveAssistantReply(accumulated)) {
|
|
2793
|
+
return this.deliverableResult(accumulated.trim(), "status.idle");
|
|
2794
|
+
}
|
|
2175
2795
|
return { terminal: 0 /* Continue */ };
|
|
2176
2796
|
}
|
|
2177
2797
|
if (typ === "error") {
|
|
@@ -2191,10 +2811,14 @@ var EventClassifier = class {
|
|
|
2191
2811
|
return { terminal: 0 /* Continue */ };
|
|
2192
2812
|
}
|
|
2193
2813
|
/** Classifies a `next` envelope by projecting its payload. */
|
|
2194
|
-
classifyNextEnvelope(env) {
|
|
2814
|
+
classifyNextEnvelope(env, accumulated) {
|
|
2195
2815
|
const payload = env.payload ?? {};
|
|
2196
2816
|
const innerData = payload.data;
|
|
2197
2817
|
if (innerData && typeof innerData === "object") {
|
|
2818
|
+
const innerType = innerData.type ?? "";
|
|
2819
|
+
if (innerType === "status") {
|
|
2820
|
+
return this.processChatEvent(innerData, accumulated);
|
|
2821
|
+
}
|
|
2198
2822
|
const innerMode = innerData.mode ?? "";
|
|
2199
2823
|
if (innerMode) {
|
|
2200
2824
|
return this.classifyEventPayload(
|
|
@@ -2205,6 +2829,11 @@ var EventClassifier = class {
|
|
|
2205
2829
|
}
|
|
2206
2830
|
}
|
|
2207
2831
|
const mode = payload.mode ?? "";
|
|
2832
|
+
if (mode === "status" || mode === "") {
|
|
2833
|
+
if (typeof payload.state === "string" || innerData && "state" in (innerData ?? {})) {
|
|
2834
|
+
return this.processChatEvent({ type: "status", ...innerData ?? payload }, accumulated);
|
|
2835
|
+
}
|
|
2836
|
+
}
|
|
2208
2837
|
if (mode) {
|
|
2209
2838
|
return this.classifyEventPayload(payload.namespace ?? null, mode, payload.data);
|
|
2210
2839
|
}
|
|
@@ -2622,10 +3251,15 @@ var ConnectionPool = class {
|
|
|
2622
3251
|
if (existing.isDisconnected() || !existing.isConnected()) {
|
|
2623
3252
|
await this.release(sessionID);
|
|
2624
3253
|
} else {
|
|
2625
|
-
existing.lastUsed
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
3254
|
+
const idleTooLong = this.cfg.maxIdleTime > 0 && existing.lastUsed > 0 && Date.now() - existing.lastUsed > this.cfg.maxIdleTime;
|
|
3255
|
+
if (idleTooLong) {
|
|
3256
|
+
await this.release(sessionID);
|
|
3257
|
+
} else {
|
|
3258
|
+
existing.lastUsed = Date.now();
|
|
3259
|
+
await this.store.updateLastUsed(sessionID).catch(() => {
|
|
3260
|
+
});
|
|
3261
|
+
return existing;
|
|
3262
|
+
}
|
|
2629
3263
|
}
|
|
2630
3264
|
}
|
|
2631
3265
|
const conn = this.pool.pop();
|
|
@@ -2728,6 +3362,88 @@ var ConnectionPool = class {
|
|
|
2728
3362
|
}
|
|
2729
3363
|
};
|
|
2730
3364
|
|
|
3365
|
+
// src/appkit/attachments.ts
|
|
3366
|
+
function compactDefaults(opts) {
|
|
3367
|
+
return {
|
|
3368
|
+
maxDim: opts?.maxDim && opts.maxDim > 0 ? opts.maxDim : 768,
|
|
3369
|
+
quality: opts?.jpegQuality && opts.jpegQuality > 0 ? opts.jpegQuality : 85
|
|
3370
|
+
};
|
|
3371
|
+
}
|
|
3372
|
+
var sharpLoader = null;
|
|
3373
|
+
async function loadSharp() {
|
|
3374
|
+
if (!sharpLoader) {
|
|
3375
|
+
sharpLoader = (async () => {
|
|
3376
|
+
try {
|
|
3377
|
+
const m = await Function('return import("sharp")')();
|
|
3378
|
+
return m;
|
|
3379
|
+
} catch {
|
|
3380
|
+
return null;
|
|
3381
|
+
}
|
|
3382
|
+
})();
|
|
3383
|
+
}
|
|
3384
|
+
return sharpLoader;
|
|
3385
|
+
}
|
|
3386
|
+
async function compactImageAttachment(mimeType, dataB64, opts) {
|
|
3387
|
+
if (!dataB64 || !mimeType.startsWith("image/")) {
|
|
3388
|
+
return [mimeType, dataB64];
|
|
3389
|
+
}
|
|
3390
|
+
let raw;
|
|
3391
|
+
try {
|
|
3392
|
+
raw = Buffer.from(dataB64, "base64");
|
|
3393
|
+
} catch {
|
|
3394
|
+
return [mimeType, dataB64];
|
|
3395
|
+
}
|
|
3396
|
+
if (raw.length === 0) return [mimeType, dataB64];
|
|
3397
|
+
const sharpMod = await loadSharp();
|
|
3398
|
+
if (!sharpMod) return [mimeType, dataB64];
|
|
3399
|
+
const { maxDim, quality } = compactDefaults(opts);
|
|
3400
|
+
try {
|
|
3401
|
+
const img = sharpMod.default(raw, { failOn: "none" });
|
|
3402
|
+
const meta = await img.metadata();
|
|
3403
|
+
const w = meta.width ?? 0;
|
|
3404
|
+
const h = meta.height ?? 0;
|
|
3405
|
+
if (w <= 0 || h <= 0 || w <= maxDim && h <= maxDim) {
|
|
3406
|
+
return [mimeType, dataB64];
|
|
3407
|
+
}
|
|
3408
|
+
let nw = w;
|
|
3409
|
+
let nh = h;
|
|
3410
|
+
if (w >= h) {
|
|
3411
|
+
if (w > maxDim) {
|
|
3412
|
+
nw = maxDim;
|
|
3413
|
+
nh = Math.max(1, Math.round(h * maxDim / w));
|
|
3414
|
+
}
|
|
3415
|
+
} else if (h > maxDim) {
|
|
3416
|
+
nh = maxDim;
|
|
3417
|
+
nw = Math.max(1, Math.round(w * maxDim / h));
|
|
3418
|
+
}
|
|
3419
|
+
const resized = img.resize(nw, nh, { fit: "fill" });
|
|
3420
|
+
if (mimeType === "image/png") {
|
|
3421
|
+
const buf2 = await resized.png().toBuffer();
|
|
3422
|
+
return [mimeType, buf2.toString("base64")];
|
|
3423
|
+
}
|
|
3424
|
+
const buf = await resized.jpeg({ quality }).toBuffer();
|
|
3425
|
+
return ["image/jpeg", buf.toString("base64")];
|
|
3426
|
+
} catch {
|
|
3427
|
+
return [mimeType, dataB64];
|
|
3428
|
+
}
|
|
3429
|
+
}
|
|
3430
|
+
async function compactAttachments(atts, opts) {
|
|
3431
|
+
if (!atts.length) return atts;
|
|
3432
|
+
const out = [];
|
|
3433
|
+
for (const att of atts) {
|
|
3434
|
+
const cp = { ...att };
|
|
3435
|
+
const mime = typeof cp.mime_type === "string" ? cp.mime_type : "";
|
|
3436
|
+
const data = typeof cp.data === "string" ? cp.data : "";
|
|
3437
|
+
if (mime && data) {
|
|
3438
|
+
const [outMime, outData] = await compactImageAttachment(mime, data, opts);
|
|
3439
|
+
cp.mime_type = outMime;
|
|
3440
|
+
cp.data = outData;
|
|
3441
|
+
}
|
|
3442
|
+
out.push(cp);
|
|
3443
|
+
}
|
|
3444
|
+
return out;
|
|
3445
|
+
}
|
|
3446
|
+
|
|
2731
3447
|
// src/appkit/turn_runner.ts
|
|
2732
3448
|
init_intent_hints();
|
|
2733
3449
|
var ErrQueryTimeout = class extends Error {
|
|
@@ -2736,6 +3452,19 @@ var ErrQueryTimeout = class extends Error {
|
|
|
2736
3452
|
this.name = "ErrQueryTimeout";
|
|
2737
3453
|
}
|
|
2738
3454
|
};
|
|
3455
|
+
var ErrIdleTimeout = class extends Error {
|
|
3456
|
+
constructor() {
|
|
3457
|
+
super("appkit: idle timeout");
|
|
3458
|
+
this.name = "ErrIdleTimeout";
|
|
3459
|
+
}
|
|
3460
|
+
};
|
|
3461
|
+
var TimeoutPolicy = /* @__PURE__ */ ((TimeoutPolicy2) => {
|
|
3462
|
+
TimeoutPolicy2[TimeoutPolicy2["Fail"] = 0] = "Fail";
|
|
3463
|
+
TimeoutPolicy2[TimeoutPolicy2["SoftComplete"] = 1] = "SoftComplete";
|
|
3464
|
+
return TimeoutPolicy2;
|
|
3465
|
+
})(TimeoutPolicy || {});
|
|
3466
|
+
var StreamCloseFail = 0 /* Fail */;
|
|
3467
|
+
var StreamCloseSoftComplete = 1 /* SoftComplete */;
|
|
2739
3468
|
function inputMessageForLoop(text, loopID, attachments, opts) {
|
|
2740
3469
|
const msg = { type: "loop_input", content: text };
|
|
2741
3470
|
if (loopID) msg.loop_id = loopID;
|
|
@@ -2758,6 +3487,13 @@ function inputMessageForLoop(text, loopID, attachments, opts) {
|
|
|
2758
3487
|
}
|
|
2759
3488
|
return msg;
|
|
2760
3489
|
}
|
|
3490
|
+
function idleTimeoutForTurn(cfg, hasAttachments) {
|
|
3491
|
+
const idle = cfg.idleTimeout ?? 0;
|
|
3492
|
+
if (idle <= 0) return 0;
|
|
3493
|
+
const floor = cfg.minIdleTimeoutWithAttachments ?? 0;
|
|
3494
|
+
if (hasAttachments && floor > 0 && idle < floor) return floor;
|
|
3495
|
+
return idle;
|
|
3496
|
+
}
|
|
2761
3497
|
var TurnRunner = class {
|
|
2762
3498
|
pool;
|
|
2763
3499
|
gate;
|
|
@@ -2768,39 +3504,29 @@ var TurnRunner = class {
|
|
|
2768
3504
|
buildInput = inputMessageForLoop;
|
|
2769
3505
|
onComplete = null;
|
|
2770
3506
|
onError = null;
|
|
2771
|
-
/**
|
|
2772
|
-
* Constructs a TurnRunner. pool, gate, classifier, and store are required;
|
|
2773
|
-
* broadcaster may be null.
|
|
2774
|
-
*/
|
|
2775
3507
|
constructor(pool, gate, classifier, store, broadcaster, cfg) {
|
|
2776
3508
|
this.pool = pool;
|
|
2777
3509
|
this.gate = gate;
|
|
2778
3510
|
this.classifier = classifier;
|
|
2779
3511
|
this.store = store;
|
|
2780
3512
|
this.broadcaster = broadcaster;
|
|
2781
|
-
this.cfg = {
|
|
3513
|
+
this.cfg = {
|
|
3514
|
+
...cfg,
|
|
3515
|
+
queryTimeout: cfg.queryTimeout > 0 ? cfg.queryTimeout : 30 * 60 * 1e3
|
|
3516
|
+
};
|
|
2782
3517
|
}
|
|
2783
|
-
/** Overrides the loop_input payload builder. */
|
|
2784
3518
|
withInputBuilder(f) {
|
|
2785
3519
|
if (f) this.buildInput = f;
|
|
2786
3520
|
return this;
|
|
2787
3521
|
}
|
|
2788
|
-
/** Sets a completion hook (runs inline on success). */
|
|
2789
3522
|
withOnComplete(f) {
|
|
2790
3523
|
this.onComplete = f;
|
|
2791
3524
|
return this;
|
|
2792
3525
|
}
|
|
2793
|
-
/** Sets an error hook (runs inline on failure). */
|
|
2794
3526
|
withOnError(f) {
|
|
2795
3527
|
this.onError = f;
|
|
2796
3528
|
return this;
|
|
2797
3529
|
}
|
|
2798
|
-
/**
|
|
2799
|
-
* Runs one query turn. The response is broadcast via the SSE broadcaster and
|
|
2800
|
-
* persisted via the SessionStore; it is not returned to the caller (SSE
|
|
2801
|
-
* subscribers receive it). Resolves on success; rejects on failure
|
|
2802
|
-
* (ErrQueryTimeout, AbortError, or a daemon/processing error).
|
|
2803
|
-
*/
|
|
2804
3530
|
async execute(sessionID, message, userID, workspaceID, attachments, opts, signal) {
|
|
2805
3531
|
let conn;
|
|
2806
3532
|
try {
|
|
@@ -2828,13 +3554,19 @@ var TurnRunner = class {
|
|
|
2828
3554
|
this.onError?.(sessionID, loopID, err);
|
|
2829
3555
|
throw err;
|
|
2830
3556
|
}
|
|
3557
|
+
let idleTimer = null;
|
|
3558
|
+
const clearIdle = () => {
|
|
3559
|
+
if (idleTimer) {
|
|
3560
|
+
clearTimeout(idleTimer);
|
|
3561
|
+
idleTimer = null;
|
|
3562
|
+
}
|
|
3563
|
+
};
|
|
2831
3564
|
try {
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
);
|
|
3565
|
+
let atts = attachments ?? void 0;
|
|
3566
|
+
if (this.cfg.compactAttachmentsBeforeSend && atts && atts.length > 0) {
|
|
3567
|
+
atts = await compactAttachments(atts, this.cfg.compactImageOpts);
|
|
3568
|
+
}
|
|
3569
|
+
const inputMsg = this.buildInput(message, loopID, atts, opts ?? void 0);
|
|
2838
3570
|
try {
|
|
2839
3571
|
await conn.client.sendMessage(inputMsg);
|
|
2840
3572
|
} catch (err) {
|
|
@@ -2853,6 +3585,23 @@ var TurnRunner = class {
|
|
|
2853
3585
|
}
|
|
2854
3586
|
let assistantContent = "";
|
|
2855
3587
|
const startedAt = Date.now();
|
|
3588
|
+
const idleForTurn = idleTimeoutForTurn(this.cfg, (attachments?.length ?? 0) > 0);
|
|
3589
|
+
let idleReject = null;
|
|
3590
|
+
const armIdle = () => {
|
|
3591
|
+
clearIdle();
|
|
3592
|
+
idleReject = null;
|
|
3593
|
+
if (idleForTurn <= 0) {
|
|
3594
|
+
return new Promise(() => {
|
|
3595
|
+
});
|
|
3596
|
+
}
|
|
3597
|
+
return new Promise((resolve) => {
|
|
3598
|
+
idleReject = () => resolve("idle");
|
|
3599
|
+
idleTimer = setTimeout(() => {
|
|
3600
|
+
idleReject?.();
|
|
3601
|
+
}, idleForTurn);
|
|
3602
|
+
});
|
|
3603
|
+
};
|
|
3604
|
+
let idleRace = armIdle();
|
|
2856
3605
|
const abortRace = new Promise((resolve) => {
|
|
2857
3606
|
const onTimeout = () => resolve("timeout");
|
|
2858
3607
|
timeoutController.signal.addEventListener("abort", onTimeout, { once: true });
|
|
@@ -2866,34 +3615,71 @@ var TurnRunner = class {
|
|
|
2866
3615
|
const next = iterator.next();
|
|
2867
3616
|
const raced = await Promise.race([
|
|
2868
3617
|
next.then((res2) => ({ tag: "msg", res: res2 })),
|
|
2869
|
-
abortRace.then((tag) => ({ tag }))
|
|
3618
|
+
abortRace.then((tag) => ({ tag })),
|
|
3619
|
+
idleRace.then((tag) => ({ tag }))
|
|
2870
3620
|
]);
|
|
2871
3621
|
if ("tag" in raced && raced.tag !== "msg") {
|
|
2872
3622
|
if (raced.tag === "caller" || signal?.aborted) {
|
|
3623
|
+
clearIdle();
|
|
2873
3624
|
const err = new Error("aborted");
|
|
2874
3625
|
await this.persistFailed(sessionID, loopID, err);
|
|
2875
3626
|
this.broadcastError(sessionID, err);
|
|
2876
3627
|
this.onError?.(sessionID, loopID, err);
|
|
2877
3628
|
throw err;
|
|
2878
3629
|
}
|
|
3630
|
+
if (raced.tag === "idle") {
|
|
3631
|
+
clearIdle();
|
|
3632
|
+
await this.sendLoopCancel(new AbortController().signal, conn, loopID).catch(() => {
|
|
3633
|
+
});
|
|
3634
|
+
await this.finishTimeout(
|
|
3635
|
+
sessionID,
|
|
3636
|
+
loopID,
|
|
3637
|
+
assistantContent,
|
|
3638
|
+
startedAt,
|
|
3639
|
+
new ErrIdleTimeout(),
|
|
3640
|
+
"idle_timeout",
|
|
3641
|
+
this.cfg.onIdleTimeout ?? 0 /* Fail */
|
|
3642
|
+
);
|
|
3643
|
+
return;
|
|
3644
|
+
}
|
|
3645
|
+
clearIdle();
|
|
2879
3646
|
await this.sendLoopCancel(new AbortController().signal, conn, loopID).catch(() => {
|
|
2880
3647
|
});
|
|
2881
|
-
await this.
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
3648
|
+
await this.finishTimeout(
|
|
3649
|
+
sessionID,
|
|
3650
|
+
loopID,
|
|
3651
|
+
assistantContent,
|
|
3652
|
+
startedAt,
|
|
3653
|
+
new ErrQueryTimeout(),
|
|
3654
|
+
"query_timeout",
|
|
3655
|
+
this.cfg.onQueryTimeout ?? 0 /* Fail */
|
|
3656
|
+
);
|
|
3657
|
+
return;
|
|
2885
3658
|
}
|
|
2886
3659
|
const res = raced.res;
|
|
2887
3660
|
if (res.done) {
|
|
3661
|
+
clearIdle();
|
|
3662
|
+
if ((this.cfg.onStreamClose ?? 0 /* Fail */) === 1 /* SoftComplete */ && assistantContent.trim() !== "") {
|
|
3663
|
+
await this.completeTurn(
|
|
3664
|
+
sessionID,
|
|
3665
|
+
loopID,
|
|
3666
|
+
assistantContent,
|
|
3667
|
+
startedAt,
|
|
3668
|
+
"stream_closed"
|
|
3669
|
+
);
|
|
3670
|
+
return;
|
|
3671
|
+
}
|
|
2888
3672
|
const err = new Error("event stream closed");
|
|
2889
3673
|
await this.persistFailed(sessionID, loopID, err);
|
|
2890
3674
|
this.broadcastError(sessionID, err);
|
|
2891
3675
|
this.onError?.(sessionID, loopID, err);
|
|
2892
3676
|
throw err;
|
|
2893
3677
|
}
|
|
3678
|
+
idleRace = armIdle();
|
|
2894
3679
|
const msg = res.value;
|
|
2895
3680
|
const eventResult = this.classifier.classify(msg, assistantContent);
|
|
2896
3681
|
if (eventResult.err && eventResult.terminal === 2 /* FailedComplete */) {
|
|
3682
|
+
clearIdle();
|
|
2897
3683
|
await this.persistFailed(sessionID, loopID, eventResult.err);
|
|
2898
3684
|
this.broadcastError(sessionID, eventResult.err);
|
|
2899
3685
|
this.onError?.(sessionID, loopID, eventResult.err);
|
|
@@ -2913,25 +3699,39 @@ var TurnRunner = class {
|
|
|
2913
3699
|
assistantContent
|
|
2914
3700
|
);
|
|
2915
3701
|
if (deliverable) {
|
|
2916
|
-
|
|
2917
|
-
await this.
|
|
3702
|
+
clearIdle();
|
|
3703
|
+
await this.completeTurn(
|
|
2918
3704
|
sessionID,
|
|
2919
3705
|
loopID,
|
|
2920
3706
|
final,
|
|
2921
3707
|
startedAt,
|
|
2922
3708
|
eventResult.completionEvent ?? ""
|
|
2923
3709
|
);
|
|
2924
|
-
this.broadcastComplete(sessionID, final);
|
|
2925
|
-
this.onComplete?.(sessionID, loopID, final, eventResult.completionEvent ?? "", elapsedMs);
|
|
2926
3710
|
return;
|
|
2927
3711
|
}
|
|
2928
3712
|
}
|
|
2929
3713
|
} finally {
|
|
3714
|
+
clearIdle();
|
|
2930
3715
|
clearTimeout(timer);
|
|
2931
3716
|
this.gate.release(sessionID);
|
|
2932
3717
|
}
|
|
2933
3718
|
}
|
|
2934
|
-
|
|
3719
|
+
async finishTimeout(sessionID, loopID, content, startedAt, failErr, completionEvent, policy) {
|
|
3720
|
+
if (policy === 1 /* SoftComplete */ && content.trim() !== "") {
|
|
3721
|
+
await this.completeTurn(sessionID, loopID, content, startedAt, completionEvent);
|
|
3722
|
+
return;
|
|
3723
|
+
}
|
|
3724
|
+
await this.persistFailed(sessionID, loopID, failErr);
|
|
3725
|
+
this.broadcastError(sessionID, failErr);
|
|
3726
|
+
this.onError?.(sessionID, loopID, failErr);
|
|
3727
|
+
throw failErr;
|
|
3728
|
+
}
|
|
3729
|
+
async completeTurn(sessionID, loopID, final, startedAt, completionEvent) {
|
|
3730
|
+
const elapsedMs = Date.now() - startedAt;
|
|
3731
|
+
await this.persistResponse(sessionID, loopID, final, startedAt, completionEvent);
|
|
3732
|
+
this.broadcastComplete(sessionID, final);
|
|
3733
|
+
this.onComplete?.(sessionID, loopID, final, completionEvent, elapsedMs);
|
|
3734
|
+
}
|
|
2935
3735
|
async sendLoopCancel(_signal, conn, loopID) {
|
|
2936
3736
|
const lid = (loopID ?? "").trim();
|
|
2937
3737
|
if (!conn || !lid) return;
|
|
@@ -2974,18 +3774,476 @@ var TurnRunner = class {
|
|
|
2974
3774
|
this.broadcaster?.broadcast(sessionID, { type: "query_error", data: err.message });
|
|
2975
3775
|
}
|
|
2976
3776
|
};
|
|
3777
|
+
|
|
3778
|
+
// src/appkit/daemon_session.ts
|
|
3779
|
+
init_client();
|
|
3780
|
+
init_config();
|
|
3781
|
+
init_errors();
|
|
3782
|
+
init_stream_terminal();
|
|
3783
|
+
|
|
3784
|
+
// src/appkit/chunk_filter.ts
|
|
3785
|
+
var MSG_PAIR_LEN = 2;
|
|
3786
|
+
function updatesChunkIsNoop(data) {
|
|
3787
|
+
if (!data || typeof data !== "object") return true;
|
|
3788
|
+
return !("__interrupt__" in data);
|
|
3789
|
+
}
|
|
3790
|
+
function wireBody(msg) {
|
|
3791
|
+
for (const key of ["kwargs", "data"]) {
|
|
3792
|
+
const nested = msg[key];
|
|
3793
|
+
if (nested && typeof nested === "object") return nested;
|
|
3794
|
+
}
|
|
3795
|
+
return msg;
|
|
3796
|
+
}
|
|
3797
|
+
function dictHasToolInvocation(msg) {
|
|
3798
|
+
const body = wireBody(msg);
|
|
3799
|
+
if (body.tool_calls || body.tool_call_chunks) return true;
|
|
3800
|
+
for (const key of ["content", "content_blocks"]) {
|
|
3801
|
+
const raw = body[key];
|
|
3802
|
+
if (Array.isArray(raw)) {
|
|
3803
|
+
for (const item of raw) {
|
|
3804
|
+
if (item && typeof item === "object" && ["tool_call", "tool_call_chunk", "tool_use"].includes(
|
|
3805
|
+
String(item.type ?? "")
|
|
3806
|
+
)) {
|
|
3807
|
+
return true;
|
|
3808
|
+
}
|
|
3809
|
+
}
|
|
3810
|
+
}
|
|
3811
|
+
}
|
|
3812
|
+
return false;
|
|
3813
|
+
}
|
|
3814
|
+
function plainText(msg) {
|
|
3815
|
+
const body = wireBody(msg);
|
|
3816
|
+
const content = body.content ?? msg.content;
|
|
3817
|
+
if (typeof content === "string") return content;
|
|
3818
|
+
if (Array.isArray(content)) {
|
|
3819
|
+
const parts = [];
|
|
3820
|
+
for (const block of content) {
|
|
3821
|
+
if (typeof block === "string") parts.push(block);
|
|
3822
|
+
else if (block && typeof block === "object") {
|
|
3823
|
+
const text = block.text;
|
|
3824
|
+
if (typeof text === "string") parts.push(text);
|
|
3825
|
+
}
|
|
3826
|
+
}
|
|
3827
|
+
return parts.join("");
|
|
3828
|
+
}
|
|
3829
|
+
return "";
|
|
3830
|
+
}
|
|
3831
|
+
function messageChunkIsNonActionable(data) {
|
|
3832
|
+
if (!Array.isArray(data) || data.length !== MSG_PAIR_LEN) return false;
|
|
3833
|
+
const msg = data[0];
|
|
3834
|
+
if (msg === null || msg === void 0) return true;
|
|
3835
|
+
if (!msg || typeof msg !== "object") return false;
|
|
3836
|
+
const m = msg;
|
|
3837
|
+
const body = wireBody(m);
|
|
3838
|
+
const raw = String(body.type ?? m.type ?? "");
|
|
3839
|
+
if (raw === "tool" || raw === "ToolMessage" || raw.endsWith("ToolMessage")) return false;
|
|
3840
|
+
if (dictHasToolInvocation(m)) return false;
|
|
3841
|
+
if (body.phase || m.phase) return false;
|
|
3842
|
+
return !plainText(m).trim();
|
|
3843
|
+
}
|
|
3844
|
+
function shouldDropStreamChunkEarly(_namespace, mode, data) {
|
|
3845
|
+
if (mode === "updates") return updatesChunkIsNoop(data);
|
|
3846
|
+
if (mode === "messages") return messageChunkIsNonActionable(data);
|
|
3847
|
+
return false;
|
|
3848
|
+
}
|
|
3849
|
+
|
|
3850
|
+
// src/appkit/events.ts
|
|
3851
|
+
function unwrapNext(event) {
|
|
3852
|
+
if (!event || typeof event !== "object") return event;
|
|
3853
|
+
if (event.type !== "next") return event;
|
|
3854
|
+
const payload = event.payload;
|
|
3855
|
+
if (!payload || typeof payload !== "object") return event;
|
|
3856
|
+
const data = payload.data;
|
|
3857
|
+
return data && typeof data === "object" ? data : event;
|
|
3858
|
+
}
|
|
3859
|
+
|
|
3860
|
+
// src/appkit/observability.ts
|
|
3861
|
+
var TurnEventStats = class {
|
|
3862
|
+
total = 0;
|
|
3863
|
+
messages = 0;
|
|
3864
|
+
updates = 0;
|
|
3865
|
+
custom = 0;
|
|
3866
|
+
skipped = 0;
|
|
3867
|
+
filteredEarly = 0;
|
|
3868
|
+
toolCalls = 0;
|
|
3869
|
+
toolResults = 0;
|
|
3870
|
+
textChunks = 0;
|
|
3871
|
+
heartbeatsDropped = 0;
|
|
3872
|
+
postIdleDrained = 0;
|
|
3873
|
+
inboundDropped = 0;
|
|
3874
|
+
};
|
|
3875
|
+
|
|
3876
|
+
// src/appkit/daemon_session.ts
|
|
3877
|
+
var DEFAULT_POST_IDLE_DRAIN_MS = 500;
|
|
3878
|
+
var DaemonSession = class {
|
|
3879
|
+
wsUrl;
|
|
3880
|
+
workspace;
|
|
3881
|
+
streamDelivery;
|
|
3882
|
+
client;
|
|
3883
|
+
rpcClient;
|
|
3884
|
+
loopId = null;
|
|
3885
|
+
readBusy = false;
|
|
3886
|
+
rpcBusy = false;
|
|
3887
|
+
rpcConnected = false;
|
|
3888
|
+
streaming = false;
|
|
3889
|
+
postIdleDrainDeadlineMs;
|
|
3890
|
+
closed = false;
|
|
3891
|
+
earlyDropFn;
|
|
3892
|
+
statsFactory;
|
|
3893
|
+
config;
|
|
3894
|
+
turnEventStats;
|
|
3895
|
+
lastTurnEndState = null;
|
|
3896
|
+
lastTurnCancellationSeen = false;
|
|
3897
|
+
lastTurnErrorMessage = null;
|
|
3898
|
+
constructor(wsUrl, opts = {}) {
|
|
3899
|
+
this.wsUrl = wsUrl;
|
|
3900
|
+
this.workspace = opts.workspace;
|
|
3901
|
+
this.streamDelivery = opts.streamDelivery ?? "adaptive";
|
|
3902
|
+
this.config = opts.config ?? defaultConfig();
|
|
3903
|
+
this.client = new Client(wsUrl, this.config);
|
|
3904
|
+
this.rpcClient = new Client(wsUrl, this.config);
|
|
3905
|
+
this.postIdleDrainDeadlineMs = opts.postIdleDrainDeadlineMs && opts.postIdleDrainDeadlineMs > 0 ? opts.postIdleDrainDeadlineMs : DEFAULT_POST_IDLE_DRAIN_MS;
|
|
3906
|
+
this.earlyDropFn = opts.earlyDropFn ?? shouldDropStreamChunkEarly;
|
|
3907
|
+
this.statsFactory = opts.statsFactory ?? (() => new TurnEventStats());
|
|
3908
|
+
this.turnEventStats = this.statsFactory();
|
|
3909
|
+
}
|
|
3910
|
+
get streamClient() {
|
|
3911
|
+
return this.client;
|
|
3912
|
+
}
|
|
3913
|
+
get rpcSideClient() {
|
|
3914
|
+
return this.rpcClient;
|
|
3915
|
+
}
|
|
3916
|
+
get activeLoopId() {
|
|
3917
|
+
return this.loopId;
|
|
3918
|
+
}
|
|
3919
|
+
resolveStreamDeliveryMode() {
|
|
3920
|
+
const delivery = this.streamDelivery;
|
|
3921
|
+
if (typeof delivery === "function") return String(delivery() || "adaptive");
|
|
3922
|
+
return String(delivery || "adaptive");
|
|
3923
|
+
}
|
|
3924
|
+
get streamDeliveryMode() {
|
|
3925
|
+
return this.resolveStreamDeliveryMode();
|
|
3926
|
+
}
|
|
3927
|
+
shouldDrop(namespace, mode, data) {
|
|
3928
|
+
return Boolean(this.earlyDropFn(namespace, mode, data));
|
|
3929
|
+
}
|
|
3930
|
+
async connect(resumeLoopId) {
|
|
3931
|
+
await connectWithRetries(this.client);
|
|
3932
|
+
return this.bootstrapLoop(resumeLoopId ?? null);
|
|
3933
|
+
}
|
|
3934
|
+
async bootstrapLoop(resumeLoopId) {
|
|
3935
|
+
const loopNew = this.workspace ? { client_workspace: this.workspace, workspace: this.workspace } : void 0;
|
|
3936
|
+
const loopId = await bootstrapLoopSession(this.client, resumeLoopId, this.config, loopNew);
|
|
3937
|
+
this.loopId = loopId;
|
|
3938
|
+
return { type: "status", loop_id: loopId, state: "ready" };
|
|
3939
|
+
}
|
|
3940
|
+
async newLoop() {
|
|
3941
|
+
return this.bootstrapLoop(null);
|
|
3942
|
+
}
|
|
3943
|
+
async switchLoop(loopId) {
|
|
3944
|
+
return this.bootstrapLoop(loopId);
|
|
3945
|
+
}
|
|
3946
|
+
async ensureConnected() {
|
|
3947
|
+
if (this.client.isConnected() && !this.client.isDisconnected()) return;
|
|
3948
|
+
let resumeLoopId = this.loopId;
|
|
3949
|
+
if (this.rpcConnected) {
|
|
3950
|
+
this.rpcClient.close();
|
|
3951
|
+
this.rpcConnected = false;
|
|
3952
|
+
}
|
|
3953
|
+
try {
|
|
3954
|
+
await this.client.reconnect();
|
|
3955
|
+
} catch {
|
|
3956
|
+
this.client.close();
|
|
3957
|
+
await connectWithRetries(this.client);
|
|
3958
|
+
}
|
|
3959
|
+
if (resumeLoopId) {
|
|
3960
|
+
try {
|
|
3961
|
+
await this.client.reattachAndProbe(resumeLoopId);
|
|
3962
|
+
this.loopId = resumeLoopId;
|
|
3963
|
+
return;
|
|
3964
|
+
} catch (err) {
|
|
3965
|
+
if (!(err instanceof StaleLoopError)) throw err;
|
|
3966
|
+
resumeLoopId = null;
|
|
3967
|
+
}
|
|
3968
|
+
}
|
|
3969
|
+
await this.bootstrapLoop(resumeLoopId);
|
|
3970
|
+
}
|
|
3971
|
+
async close() {
|
|
3972
|
+
if (this.closed) return;
|
|
3973
|
+
this.closed = true;
|
|
3974
|
+
this.client.close();
|
|
3975
|
+
this.rpcClient.close();
|
|
3976
|
+
this.rpcConnected = false;
|
|
3977
|
+
}
|
|
3978
|
+
async detach() {
|
|
3979
|
+
if (!this.client.isConnected()) return;
|
|
3980
|
+
try {
|
|
3981
|
+
await this.client.notify("disconnect", {});
|
|
3982
|
+
} catch {
|
|
3983
|
+
}
|
|
3984
|
+
}
|
|
3985
|
+
async sendTurn(text, options) {
|
|
3986
|
+
if (!this.loopId) throw new Error("No active loop session");
|
|
3987
|
+
await this.client.sendInput(text, {
|
|
3988
|
+
loopID: this.loopId,
|
|
3989
|
+
autonomous: options?.autonomous,
|
|
3990
|
+
maxIterations: options?.maxIterations,
|
|
3991
|
+
subagent: options?.preferredSubagent,
|
|
3992
|
+
model: options?.model,
|
|
3993
|
+
modelParams: options?.modelParams,
|
|
3994
|
+
attachments: options?.attachments,
|
|
3995
|
+
clarificationMode: options?.clarificationMode,
|
|
3996
|
+
clarificationAnswer: options?.clarificationAnswer,
|
|
3997
|
+
intentHint: options?.intentHint
|
|
3998
|
+
});
|
|
3999
|
+
}
|
|
4000
|
+
async cancelActiveTurn() {
|
|
4001
|
+
await this.client.notify("slash_command", { cmd: "/cancel" });
|
|
4002
|
+
}
|
|
4003
|
+
async *drainStreamEventsAfterIdle(expectedLoopId) {
|
|
4004
|
+
const deadline = Date.now() + this.postIdleDrainDeadlineMs;
|
|
4005
|
+
let exp = expectedLoopId;
|
|
4006
|
+
while (Date.now() < deadline) {
|
|
4007
|
+
const event = await this.client.readEventWithTimeout(250);
|
|
4008
|
+
if (!event) break;
|
|
4009
|
+
let frame = event;
|
|
4010
|
+
let eventType = String(frame.type ?? "");
|
|
4011
|
+
if (eventType === "next") {
|
|
4012
|
+
frame = unwrapNext(frame) ?? frame;
|
|
4013
|
+
eventType = String(frame.type ?? "");
|
|
4014
|
+
}
|
|
4015
|
+
const eventLoopId = frame.loop_id;
|
|
4016
|
+
if (exp && typeof eventLoopId === "string" && eventLoopId && eventLoopId !== exp) {
|
|
4017
|
+
continue;
|
|
4018
|
+
}
|
|
4019
|
+
if (eventType === "error") {
|
|
4020
|
+
const errObj = frame.error ?? {};
|
|
4021
|
+
throw new Error(String(errObj.message || frame.message || "daemon error"));
|
|
4022
|
+
}
|
|
4023
|
+
if (eventType === "status") {
|
|
4024
|
+
const loopEv = frame.loop_id;
|
|
4025
|
+
if (typeof loopEv === "string" && loopEv) {
|
|
4026
|
+
this.loopId = loopEv;
|
|
4027
|
+
exp = loopEv;
|
|
4028
|
+
}
|
|
4029
|
+
continue;
|
|
4030
|
+
}
|
|
4031
|
+
if (eventType !== "event") continue;
|
|
4032
|
+
const data = frame.data;
|
|
4033
|
+
const namespace = Array.isArray(frame.namespace) ? frame.namespace : [];
|
|
4034
|
+
const mode = String(frame.mode ?? "");
|
|
4035
|
+
if (this.shouldDrop(namespace, mode, data)) {
|
|
4036
|
+
this.turnEventStats.filteredEarly += 1;
|
|
4037
|
+
continue;
|
|
4038
|
+
}
|
|
4039
|
+
this.turnEventStats.postIdleDrained += 1;
|
|
4040
|
+
yield [namespace, mode, data];
|
|
4041
|
+
}
|
|
4042
|
+
}
|
|
4043
|
+
async withRpcLock(fn) {
|
|
4044
|
+
while (this.rpcBusy) {
|
|
4045
|
+
await new Promise((r) => setTimeout(r, 5));
|
|
4046
|
+
}
|
|
4047
|
+
this.rpcBusy = true;
|
|
4048
|
+
try {
|
|
4049
|
+
return await fn();
|
|
4050
|
+
} finally {
|
|
4051
|
+
this.rpcBusy = false;
|
|
4052
|
+
}
|
|
4053
|
+
}
|
|
4054
|
+
async ensureRpcConnected() {
|
|
4055
|
+
if (this.rpcConnected && this.rpcClient.isConnected()) return;
|
|
4056
|
+
await connectWithRetries(this.rpcClient);
|
|
4057
|
+
this.rpcConnected = true;
|
|
4058
|
+
}
|
|
4059
|
+
async listLoops(_limit = 20) {
|
|
4060
|
+
return this.withRpcLock(async () => {
|
|
4061
|
+
await this.ensureRpcConnected();
|
|
4062
|
+
return this.rpcClient.listLoops(15e3);
|
|
4063
|
+
});
|
|
4064
|
+
}
|
|
4065
|
+
async fetchLoopCards(loopId) {
|
|
4066
|
+
const lid = String(loopId || "").trim();
|
|
4067
|
+
if (!lid) return { cards: [], seq: 0, contextTokens: 0, success: false };
|
|
4068
|
+
return this.withRpcLock(async () => {
|
|
4069
|
+
await this.ensureRpcConnected();
|
|
4070
|
+
try {
|
|
4071
|
+
const resp = await this.rpcClient.fetchLoopCards(lid, 3e4);
|
|
4072
|
+
const rawCards = resp.cards;
|
|
4073
|
+
return {
|
|
4074
|
+
cards: Array.isArray(rawCards) ? rawCards : [],
|
|
4075
|
+
seq: Number(resp.seq ?? 0),
|
|
4076
|
+
contextTokens: typeof resp.context_tokens === "number" && resp.context_tokens >= 0 ? resp.context_tokens : 0,
|
|
4077
|
+
success: true
|
|
4078
|
+
};
|
|
4079
|
+
} catch {
|
|
4080
|
+
return { cards: [], seq: 0, contextTokens: 0, success: false };
|
|
4081
|
+
}
|
|
4082
|
+
});
|
|
4083
|
+
}
|
|
4084
|
+
async fetchLoopHistory(loopId) {
|
|
4085
|
+
const lid = String(loopId || "").trim();
|
|
4086
|
+
if (!lid) {
|
|
4087
|
+
return { goals: [], liveCards: [], liveGoalIndex: null, contextTokens: 0, success: false };
|
|
4088
|
+
}
|
|
4089
|
+
return this.withRpcLock(async () => {
|
|
4090
|
+
await this.ensureRpcConnected();
|
|
4091
|
+
try {
|
|
4092
|
+
const resp = await this.rpcClient.fetchLoopHistory(lid, 3e4);
|
|
4093
|
+
const liveGoalIndex = resp.live_goal_index;
|
|
4094
|
+
return {
|
|
4095
|
+
goals: Array.isArray(resp.goals) ? resp.goals : [],
|
|
4096
|
+
liveCards: Array.isArray(resp.live_cards) ? resp.live_cards : [],
|
|
4097
|
+
liveGoalIndex: typeof liveGoalIndex === "number" ? liveGoalIndex : null,
|
|
4098
|
+
contextTokens: typeof resp.context_tokens === "number" && resp.context_tokens >= 0 ? resp.context_tokens : 0,
|
|
4099
|
+
success: Boolean(resp.success ?? true)
|
|
4100
|
+
};
|
|
4101
|
+
} catch {
|
|
4102
|
+
return {
|
|
4103
|
+
goals: [],
|
|
4104
|
+
liveCards: [],
|
|
4105
|
+
liveGoalIndex: null,
|
|
4106
|
+
contextTokens: 0,
|
|
4107
|
+
success: false
|
|
4108
|
+
};
|
|
4109
|
+
}
|
|
4110
|
+
});
|
|
4111
|
+
}
|
|
4112
|
+
async fetchConversationLog(loopId, opts = {}) {
|
|
4113
|
+
const lid = String(loopId || "").trim();
|
|
4114
|
+
if (!lid) return [];
|
|
4115
|
+
return this.withRpcLock(async () => {
|
|
4116
|
+
await this.ensureRpcConnected();
|
|
4117
|
+
const resp = await this.rpcClient.getLoopMessages(
|
|
4118
|
+
lid,
|
|
4119
|
+
opts.limit ?? 100,
|
|
4120
|
+
opts.offset ?? 0,
|
|
4121
|
+
opts.includeEvents ?? false
|
|
4122
|
+
);
|
|
4123
|
+
const raw = resp.messages;
|
|
4124
|
+
if (!Array.isArray(raw)) return [];
|
|
4125
|
+
return raw.filter((m) => !!m && typeof m === "object");
|
|
4126
|
+
});
|
|
4127
|
+
}
|
|
4128
|
+
async *iterTurnChunks(opts = {}) {
|
|
4129
|
+
this.turnEventStats = this.statsFactory();
|
|
4130
|
+
this.lastTurnEndState = null;
|
|
4131
|
+
this.lastTurnCancellationSeen = false;
|
|
4132
|
+
this.lastTurnErrorMessage = null;
|
|
4133
|
+
let queryStarted = false;
|
|
4134
|
+
let expectedLoopId = this.loopId;
|
|
4135
|
+
let streamPayloadSeen = false;
|
|
4136
|
+
let turnProgressSeen = false;
|
|
4137
|
+
this.streaming = true;
|
|
4138
|
+
const absoluteDeadline = opts.maxWaitMs !== void 0 && opts.maxWaitMs > 0 ? Date.now() + opts.maxWaitMs : null;
|
|
4139
|
+
this.client.peelStalePendingControlEvents();
|
|
4140
|
+
while (this.readBusy) {
|
|
4141
|
+
await new Promise((r) => setTimeout(r, 5));
|
|
4142
|
+
}
|
|
4143
|
+
this.readBusy = true;
|
|
4144
|
+
try {
|
|
4145
|
+
while (true) {
|
|
4146
|
+
if (absoluteDeadline !== null && Date.now() >= absoluteDeadline) {
|
|
4147
|
+
throw new Error(
|
|
4148
|
+
`Turn timed out after ${opts.maxWaitMs}ms (loop=${expectedLoopId ?? "?"})`
|
|
4149
|
+
);
|
|
4150
|
+
}
|
|
4151
|
+
const event = await this.client.readEvent();
|
|
4152
|
+
if (!event) {
|
|
4153
|
+
if (queryStarted && !this.client.isConnectionAlive()) {
|
|
4154
|
+
this.lastTurnEndState = "connection_lost";
|
|
4155
|
+
throw new Error("Daemon connection lost");
|
|
4156
|
+
}
|
|
4157
|
+
break;
|
|
4158
|
+
}
|
|
4159
|
+
let frame = event;
|
|
4160
|
+
let eventType = String(frame.type ?? "");
|
|
4161
|
+
if (eventType === "next") {
|
|
4162
|
+
frame = unwrapNext(frame) ?? frame;
|
|
4163
|
+
eventType = String(frame.type ?? "");
|
|
4164
|
+
}
|
|
4165
|
+
const eventLoopId = frame.loop_id;
|
|
4166
|
+
if (expectedLoopId && typeof eventLoopId === "string" && eventLoopId && eventLoopId !== expectedLoopId) {
|
|
4167
|
+
continue;
|
|
4168
|
+
}
|
|
4169
|
+
if (eventType === "error") {
|
|
4170
|
+
const errObj = frame.error ?? {};
|
|
4171
|
+
throw new Error(String(errObj.message || frame.message || "daemon error"));
|
|
4172
|
+
}
|
|
4173
|
+
if (eventType === "status") {
|
|
4174
|
+
const loopEv = frame.loop_id;
|
|
4175
|
+
if (typeof loopEv === "string" && loopEv) {
|
|
4176
|
+
this.loopId = loopEv;
|
|
4177
|
+
expectedLoopId = loopEv;
|
|
4178
|
+
}
|
|
4179
|
+
const state = String(frame.state ?? "");
|
|
4180
|
+
if (state === "running") {
|
|
4181
|
+
queryStarted = true;
|
|
4182
|
+
} else if (queryStarted && state === "stopped") {
|
|
4183
|
+
this.lastTurnEndState = state;
|
|
4184
|
+
yield* this.drainStreamEventsAfterIdle(expectedLoopId);
|
|
4185
|
+
break;
|
|
4186
|
+
} else if (queryStarted && state === "idle") {
|
|
4187
|
+
if (!streamPayloadSeen && !this.lastTurnCancellationSeen) continue;
|
|
4188
|
+
this.lastTurnEndState = state;
|
|
4189
|
+
yield* this.drainStreamEventsAfterIdle(expectedLoopId);
|
|
4190
|
+
break;
|
|
4191
|
+
}
|
|
4192
|
+
continue;
|
|
4193
|
+
}
|
|
4194
|
+
if (eventType === "command_response") {
|
|
4195
|
+
const content = String(frame.content ?? "");
|
|
4196
|
+
if (content.includes("Cancellation requested")) {
|
|
4197
|
+
this.lastTurnCancellationSeen = true;
|
|
4198
|
+
}
|
|
4199
|
+
continue;
|
|
4200
|
+
}
|
|
4201
|
+
if (eventType !== "event") continue;
|
|
4202
|
+
const data = frame.data;
|
|
4203
|
+
const namespace = Array.isArray(frame.namespace) ? frame.namespace : [];
|
|
4204
|
+
const mode = String(frame.mode ?? "");
|
|
4205
|
+
if (this.shouldDrop(namespace, mode, data)) {
|
|
4206
|
+
this.turnEventStats.filteredEarly += 1;
|
|
4207
|
+
continue;
|
|
4208
|
+
}
|
|
4209
|
+
if (mode === "custom" && isTurnEndCustomData(data)) {
|
|
4210
|
+
if (!queryStarted || !turnProgressSeen) continue;
|
|
4211
|
+
}
|
|
4212
|
+
streamPayloadSeen = true;
|
|
4213
|
+
if (isTurnProgressChunk(mode, data)) turnProgressSeen = true;
|
|
4214
|
+
yield [namespace, mode, data];
|
|
4215
|
+
if (mode === "custom" && isTurnEndCustomData(data)) {
|
|
4216
|
+
const customType = String(data.type ?? "").trim();
|
|
4217
|
+
this.lastTurnEndState = customType === STREAM_END ? "stream_end" : "completed";
|
|
4218
|
+
yield* this.drainStreamEventsAfterIdle(expectedLoopId);
|
|
4219
|
+
break;
|
|
4220
|
+
}
|
|
4221
|
+
}
|
|
4222
|
+
} catch (exc) {
|
|
4223
|
+
this.lastTurnErrorMessage = String(exc);
|
|
4224
|
+
throw exc;
|
|
4225
|
+
} finally {
|
|
4226
|
+
this.streaming = false;
|
|
4227
|
+
this.readBusy = false;
|
|
4228
|
+
}
|
|
4229
|
+
}
|
|
4230
|
+
};
|
|
2977
4231
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2978
4232
|
0 && (module.exports = {
|
|
2979
4233
|
CLIENT_VERSION,
|
|
2980
4234
|
ChatEventTerminal,
|
|
2981
4235
|
Client,
|
|
4236
|
+
CommandClient,
|
|
2982
4237
|
ConnectionError,
|
|
2983
4238
|
ConnectionPool,
|
|
2984
4239
|
DEFAULT_CLIENT_CAPABILITIES,
|
|
2985
4240
|
DEFAULT_DELIVERABLE_PHASES,
|
|
4241
|
+
DEFAULT_POST_IDLE_DRAIN_MS,
|
|
2986
4242
|
DEFAULT_THINKING_STEP_EVENTS,
|
|
2987
4243
|
DaemonError,
|
|
4244
|
+
DaemonSession,
|
|
2988
4245
|
DisconnectCause,
|
|
4246
|
+
ErrIdleTimeout,
|
|
2989
4247
|
ErrPoolExhausted,
|
|
2990
4248
|
ErrQueryBusy,
|
|
2991
4249
|
ErrQueryTimeout,
|
|
@@ -3031,26 +4289,31 @@ var TurnRunner = class {
|
|
|
3031
4289
|
INTENT_HINT_OCR,
|
|
3032
4290
|
INTENT_HINT_TEXT_COMPLETION,
|
|
3033
4291
|
LOOP_ASSISTANT_OUTPUT_PHASES,
|
|
3034
|
-
Multiplexer,
|
|
3035
4292
|
PROTO_VERSION,
|
|
3036
4293
|
PooledConn,
|
|
3037
4294
|
QueryGate,
|
|
3038
4295
|
REMOVED_INTENT_HINTS,
|
|
3039
4296
|
ReconnectError,
|
|
3040
4297
|
SSEBroadcaster,
|
|
4298
|
+
STREAM_END,
|
|
3041
4299
|
StaleLoopError,
|
|
4300
|
+
StreamCloseFail,
|
|
4301
|
+
StreamCloseSoftComplete,
|
|
3042
4302
|
TimeoutError,
|
|
4303
|
+
TimeoutPolicy,
|
|
4304
|
+
TurnEventStats,
|
|
3043
4305
|
TurnRunner,
|
|
3044
4306
|
VerbosityTier,
|
|
3045
4307
|
authenticate,
|
|
3046
4308
|
bootstrapLoopSession,
|
|
3047
4309
|
checkDaemonStatus,
|
|
3048
4310
|
classifyEventVerbosity,
|
|
4311
|
+
compactAttachments,
|
|
4312
|
+
compactImageAttachment,
|
|
3049
4313
|
connectWithRetries,
|
|
4314
|
+
connectedWebsocket,
|
|
3050
4315
|
connectionInitEnvelope,
|
|
3051
4316
|
decodeMessage,
|
|
3052
|
-
defaultBootstrapFunc,
|
|
3053
|
-
defaultClientFactory,
|
|
3054
4317
|
defaultConfig,
|
|
3055
4318
|
defaultPoolConfig,
|
|
3056
4319
|
disconnectCauseName,
|
|
@@ -3059,12 +4322,18 @@ var TurnRunner = class {
|
|
|
3059
4322
|
extractSootheLoopID,
|
|
3060
4323
|
extractThinkingStep,
|
|
3061
4324
|
fetchConfigSection,
|
|
4325
|
+
fetchLoopCards,
|
|
3062
4326
|
fetchLoopHistory,
|
|
4327
|
+
fetchLoopMessages,
|
|
3063
4328
|
fetchSkillsCatalog,
|
|
4329
|
+
idleTimeoutForTurn,
|
|
4330
|
+
inboundNeedsDeliveryAck,
|
|
3064
4331
|
inputMessageForLoop,
|
|
3065
4332
|
isCompletionEvent,
|
|
3066
4333
|
isDaemonLive,
|
|
3067
4334
|
isSubagentProgressEvent,
|
|
4335
|
+
isTurnEndCustomData,
|
|
4336
|
+
isTurnProgressChunk,
|
|
3068
4337
|
isValidVerbosityLevel,
|
|
3069
4338
|
loadConfigFromEnv,
|
|
3070
4339
|
newLoopInputMessage,
|
|
@@ -3075,6 +4344,7 @@ var TurnRunner = class {
|
|
|
3075
4344
|
parseNamespace,
|
|
3076
4345
|
pingEnvelope,
|
|
3077
4346
|
pongEnvelope,
|
|
4347
|
+
protocol1Rpc,
|
|
3078
4348
|
refreshAuthToken,
|
|
3079
4349
|
requestDaemonConfigReload,
|
|
3080
4350
|
requestDaemonShutdown,
|