@openacp/cli 0.3.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/dist/{autostart-N4HIL6C3.js → autostart-DZ3MHHMM.js} +3 -3
- package/dist/{chunk-LVSQQRCF.js → chunk-2SY7Y2VB.js} +3 -3
- package/dist/{chunk-7W5SOJPD.js → chunk-3QACY5E3.js} +2 -2
- package/dist/{chunk-CA6FXPLH.js → chunk-BLVZFCKN.js} +4 -4
- package/dist/chunk-KSIQZC3J.js +98 -0
- package/dist/chunk-KSIQZC3J.js.map +1 -0
- package/dist/{chunk-JOSJGZGF.js → chunk-LYKCQTH5.js} +1 -9
- package/dist/{chunk-JOSJGZGF.js.map → chunk-LYKCQTH5.js.map} +1 -1
- package/dist/{chunk-5E6ZXCNN.js → chunk-MRKYJ422.js} +2 -2
- package/dist/{chunk-RBDPCHGD.js → chunk-V3BA2MJ6.js} +2 -2
- package/dist/{chunk-YXMRR2E3.js → chunk-WF5XDN4D.js} +65 -40
- package/dist/chunk-WF5XDN4D.js.map +1 -0
- package/dist/{chunk-NS2L445T.js → chunk-WHKLPZGK.js} +4 -4
- package/dist/{chunk-SWQRUVBW.js → chunk-YD7ILGA6.js} +624 -322
- package/dist/chunk-YD7ILGA6.js.map +1 -0
- package/dist/cli.js +258 -316
- package/dist/cli.js.map +1 -1
- package/dist/{config-2CBRLF3R.js → config-J5YQOMDU.js} +3 -3
- package/dist/config-editor-IXL4BFG3.js +11 -0
- package/dist/{daemon-UXC7PB4P.js → daemon-SLGQGRKO.js} +4 -4
- package/dist/index.d.ts +184 -71
- package/dist/index.js +18 -10
- package/dist/install-cloudflared-ILUXKLAC.js +8 -0
- package/dist/{main-PLTMIVYL.js → main-OUVYNLZN.js} +53 -17
- package/dist/main-OUVYNLZN.js.map +1 -0
- package/dist/{setup-UKWBLJIT.js → setup-JQZBPXWS.js} +4 -4
- package/dist/{tunnel-service-4GISQZNP.js → tunnel-service-DASSH7OA.js} +3 -3
- package/dist/version-VC5CPXBX.js +15 -0
- package/dist/version-VC5CPXBX.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-SWQRUVBW.js.map +0 -1
- package/dist/chunk-YXMRR2E3.js.map +0 -1
- package/dist/config-editor-UN56HQCW.js +0 -11
- package/dist/install-cloudflared-LMM7MFQX.js +0 -8
- package/dist/main-PLTMIVYL.js.map +0 -1
- /package/dist/{autostart-N4HIL6C3.js.map → autostart-DZ3MHHMM.js.map} +0 -0
- /package/dist/{chunk-LVSQQRCF.js.map → chunk-2SY7Y2VB.js.map} +0 -0
- /package/dist/{chunk-7W5SOJPD.js.map → chunk-3QACY5E3.js.map} +0 -0
- /package/dist/{chunk-CA6FXPLH.js.map → chunk-BLVZFCKN.js.map} +0 -0
- /package/dist/{chunk-5E6ZXCNN.js.map → chunk-MRKYJ422.js.map} +0 -0
- /package/dist/{chunk-RBDPCHGD.js.map → chunk-V3BA2MJ6.js.map} +0 -0
- /package/dist/{chunk-NS2L445T.js.map → chunk-WHKLPZGK.js.map} +0 -0
- /package/dist/{config-2CBRLF3R.js.map → config-J5YQOMDU.js.map} +0 -0
- /package/dist/{config-editor-UN56HQCW.js.map → config-editor-IXL4BFG3.js.map} +0 -0
- /package/dist/{daemon-UXC7PB4P.js.map → daemon-SLGQGRKO.js.map} +0 -0
- /package/dist/{install-cloudflared-LMM7MFQX.js.map → install-cloudflared-ILUXKLAC.js.map} +0 -0
- /package/dist/{setup-UKWBLJIT.js.map → setup-JQZBPXWS.js.map} +0 -0
- /package/dist/{tunnel-service-4GISQZNP.js.map → tunnel-service-DASSH7OA.js.map} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createChildLogger,
|
|
3
3
|
createSessionLogger
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-LYKCQTH5.js";
|
|
5
5
|
|
|
6
6
|
// src/core/streams.ts
|
|
7
7
|
function nodeToWebWritable(nodeStream) {
|
|
@@ -504,10 +504,174 @@ var AgentManager = class {
|
|
|
504
504
|
}
|
|
505
505
|
};
|
|
506
506
|
|
|
507
|
+
// src/core/typed-emitter.ts
|
|
508
|
+
var TypedEmitter = class {
|
|
509
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
510
|
+
listeners = /* @__PURE__ */ new Map();
|
|
511
|
+
paused = false;
|
|
512
|
+
buffer = [];
|
|
513
|
+
on(event, listener) {
|
|
514
|
+
let set = this.listeners.get(event);
|
|
515
|
+
if (!set) {
|
|
516
|
+
set = /* @__PURE__ */ new Set();
|
|
517
|
+
this.listeners.set(event, set);
|
|
518
|
+
}
|
|
519
|
+
set.add(listener);
|
|
520
|
+
return this;
|
|
521
|
+
}
|
|
522
|
+
off(event, listener) {
|
|
523
|
+
this.listeners.get(event)?.delete(listener);
|
|
524
|
+
return this;
|
|
525
|
+
}
|
|
526
|
+
emit(event, ...args) {
|
|
527
|
+
if (this.paused) {
|
|
528
|
+
if (this.passthroughFn?.(event, args)) {
|
|
529
|
+
this.deliver(event, args);
|
|
530
|
+
} else {
|
|
531
|
+
this.buffer.push({ event, args });
|
|
532
|
+
}
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
this.deliver(event, args);
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Pause event delivery. Events emitted while paused are buffered.
|
|
539
|
+
* Optionally pass a filter to allow specific events through even while paused.
|
|
540
|
+
*/
|
|
541
|
+
pause(passthrough) {
|
|
542
|
+
this.paused = true;
|
|
543
|
+
this.passthroughFn = passthrough;
|
|
544
|
+
}
|
|
545
|
+
passthroughFn;
|
|
546
|
+
/** Resume event delivery and replay buffered events in order. */
|
|
547
|
+
resume() {
|
|
548
|
+
this.paused = false;
|
|
549
|
+
this.passthroughFn = void 0;
|
|
550
|
+
const buffered = this.buffer.splice(0);
|
|
551
|
+
for (const { event, args } of buffered) {
|
|
552
|
+
this.deliver(event, args);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
/** Discard all buffered events without delivering them. */
|
|
556
|
+
clearBuffer() {
|
|
557
|
+
this.buffer.length = 0;
|
|
558
|
+
}
|
|
559
|
+
get isPaused() {
|
|
560
|
+
return this.paused;
|
|
561
|
+
}
|
|
562
|
+
get bufferSize() {
|
|
563
|
+
return this.buffer.length;
|
|
564
|
+
}
|
|
565
|
+
removeAllListeners(event) {
|
|
566
|
+
if (event) {
|
|
567
|
+
this.listeners.delete(event);
|
|
568
|
+
} else {
|
|
569
|
+
this.listeners.clear();
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
deliver(event, args) {
|
|
573
|
+
const set = this.listeners.get(event);
|
|
574
|
+
if (!set) return;
|
|
575
|
+
for (const listener of set) {
|
|
576
|
+
listener(...args);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
// src/core/prompt-queue.ts
|
|
582
|
+
var PromptQueue = class {
|
|
583
|
+
constructor(processor, onError) {
|
|
584
|
+
this.processor = processor;
|
|
585
|
+
this.onError = onError;
|
|
586
|
+
}
|
|
587
|
+
queue = [];
|
|
588
|
+
processing = false;
|
|
589
|
+
async enqueue(text) {
|
|
590
|
+
if (this.processing) {
|
|
591
|
+
return new Promise((resolve) => {
|
|
592
|
+
this.queue.push({ text, resolve });
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
await this.process(text);
|
|
596
|
+
}
|
|
597
|
+
async process(text) {
|
|
598
|
+
this.processing = true;
|
|
599
|
+
try {
|
|
600
|
+
await this.processor(text);
|
|
601
|
+
} catch (err) {
|
|
602
|
+
this.onError?.(err);
|
|
603
|
+
} finally {
|
|
604
|
+
this.processing = false;
|
|
605
|
+
this.drainNext();
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
drainNext() {
|
|
609
|
+
const next = this.queue.shift();
|
|
610
|
+
if (next) {
|
|
611
|
+
this.process(next.text).then(next.resolve);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
clear() {
|
|
615
|
+
for (const item of this.queue) {
|
|
616
|
+
item.resolve();
|
|
617
|
+
}
|
|
618
|
+
this.queue = [];
|
|
619
|
+
}
|
|
620
|
+
get pending() {
|
|
621
|
+
return this.queue.length;
|
|
622
|
+
}
|
|
623
|
+
get isProcessing() {
|
|
624
|
+
return this.processing;
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
|
|
628
|
+
// src/core/permission-gate.ts
|
|
629
|
+
var PermissionGate = class {
|
|
630
|
+
request;
|
|
631
|
+
resolveFn;
|
|
632
|
+
rejectFn;
|
|
633
|
+
settled = false;
|
|
634
|
+
setPending(request) {
|
|
635
|
+
this.request = request;
|
|
636
|
+
this.settled = false;
|
|
637
|
+
return new Promise((resolve, reject) => {
|
|
638
|
+
this.resolveFn = resolve;
|
|
639
|
+
this.rejectFn = reject;
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
resolve(optionId) {
|
|
643
|
+
if (this.settled || !this.resolveFn) return;
|
|
644
|
+
this.settled = true;
|
|
645
|
+
this.resolveFn(optionId);
|
|
646
|
+
this.cleanup();
|
|
647
|
+
}
|
|
648
|
+
reject(reason) {
|
|
649
|
+
if (this.settled || !this.rejectFn) return;
|
|
650
|
+
this.settled = true;
|
|
651
|
+
this.rejectFn(new Error(reason ?? "Permission rejected"));
|
|
652
|
+
this.cleanup();
|
|
653
|
+
}
|
|
654
|
+
get isPending() {
|
|
655
|
+
return !!this.request && !this.settled;
|
|
656
|
+
}
|
|
657
|
+
get currentRequest() {
|
|
658
|
+
return this.isPending ? this.request : void 0;
|
|
659
|
+
}
|
|
660
|
+
/** The request ID of the current pending request, undefined after settlement */
|
|
661
|
+
get requestId() {
|
|
662
|
+
return this.request?.id;
|
|
663
|
+
}
|
|
664
|
+
cleanup() {
|
|
665
|
+
this.request = void 0;
|
|
666
|
+
this.resolveFn = void 0;
|
|
667
|
+
this.rejectFn = void 0;
|
|
668
|
+
}
|
|
669
|
+
};
|
|
670
|
+
|
|
507
671
|
// src/core/session.ts
|
|
508
672
|
import { nanoid } from "nanoid";
|
|
509
673
|
var moduleLog = createChildLogger({ module: "session" });
|
|
510
|
-
var Session = class {
|
|
674
|
+
var Session = class extends TypedEmitter {
|
|
511
675
|
id;
|
|
512
676
|
channelId;
|
|
513
677
|
threadId = "";
|
|
@@ -517,15 +681,15 @@ var Session = class {
|
|
|
517
681
|
agentSessionId = "";
|
|
518
682
|
status = "initializing";
|
|
519
683
|
name;
|
|
520
|
-
promptQueue = [];
|
|
521
|
-
promptRunning = false;
|
|
522
684
|
createdAt = /* @__PURE__ */ new Date();
|
|
523
685
|
adapter;
|
|
524
686
|
// Set by wireSessionEvents for renaming
|
|
525
|
-
pendingPermission;
|
|
526
687
|
dangerousMode = false;
|
|
527
688
|
log;
|
|
689
|
+
permissionGate = new PermissionGate();
|
|
690
|
+
queue;
|
|
528
691
|
constructor(opts) {
|
|
692
|
+
super();
|
|
529
693
|
this.id = opts.id || nanoid(12);
|
|
530
694
|
this.channelId = opts.channelId;
|
|
531
695
|
this.agentName = opts.agentName;
|
|
@@ -533,45 +697,57 @@ var Session = class {
|
|
|
533
697
|
this.agentInstance = opts.agentInstance;
|
|
534
698
|
this.log = createSessionLogger(this.id, moduleLog);
|
|
535
699
|
this.log.info({ agentName: this.agentName }, "Session created");
|
|
700
|
+
this.queue = new PromptQueue(
|
|
701
|
+
(text) => this.processPrompt(text),
|
|
702
|
+
(err) => {
|
|
703
|
+
this.status = "error";
|
|
704
|
+
this.log.error({ err }, "Prompt execution failed");
|
|
705
|
+
}
|
|
706
|
+
);
|
|
707
|
+
}
|
|
708
|
+
// --- Backward-compatible properties ---
|
|
709
|
+
/** @deprecated Use permissionGate directly */
|
|
710
|
+
get pendingPermission() {
|
|
711
|
+
if (!this.permissionGate.isPending) return void 0;
|
|
712
|
+
return {
|
|
713
|
+
requestId: this.permissionGate.requestId,
|
|
714
|
+
resolve: (optionId) => this.permissionGate.resolve(optionId)
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
set pendingPermission(val) {
|
|
718
|
+
}
|
|
719
|
+
/** Number of prompts waiting in queue */
|
|
720
|
+
get queueDepth() {
|
|
721
|
+
return this.queue.pending;
|
|
722
|
+
}
|
|
723
|
+
get promptRunning() {
|
|
724
|
+
return this.queue.isProcessing;
|
|
536
725
|
}
|
|
726
|
+
// --- Public API ---
|
|
537
727
|
async enqueuePrompt(text) {
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
728
|
+
await this.queue.enqueue(text);
|
|
729
|
+
}
|
|
730
|
+
async processPrompt(text) {
|
|
731
|
+
if (text === "\0__warmup__") {
|
|
732
|
+
await this.runWarmup();
|
|
541
733
|
return;
|
|
542
734
|
}
|
|
543
|
-
await this.runPrompt(text);
|
|
544
|
-
}
|
|
545
|
-
async runPrompt(text) {
|
|
546
|
-
this.promptRunning = true;
|
|
547
735
|
this.status = "active";
|
|
548
736
|
const promptStart = Date.now();
|
|
549
737
|
this.log.debug("Prompt execution started");
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
await this.autoName();
|
|
558
|
-
}
|
|
559
|
-
} catch (err) {
|
|
560
|
-
this.status = "error";
|
|
561
|
-
this.log.error({ err }, "Prompt execution failed");
|
|
562
|
-
} finally {
|
|
563
|
-
this.promptRunning = false;
|
|
564
|
-
if (this.promptQueue.length > 0) {
|
|
565
|
-
const next = this.promptQueue.shift();
|
|
566
|
-
await this.runPrompt(next);
|
|
567
|
-
}
|
|
738
|
+
await this.agentInstance.prompt(text);
|
|
739
|
+
this.log.info(
|
|
740
|
+
{ durationMs: Date.now() - promptStart },
|
|
741
|
+
"Prompt execution completed"
|
|
742
|
+
);
|
|
743
|
+
if (!this.name) {
|
|
744
|
+
await this.autoName();
|
|
568
745
|
}
|
|
569
746
|
}
|
|
570
747
|
// NOTE: This injects a summary prompt into the agent's conversation history.
|
|
571
|
-
// Known Phase 1 limitation — the agent sees this prompt in its context.
|
|
572
748
|
async autoName() {
|
|
573
749
|
let title = "";
|
|
574
|
-
const
|
|
750
|
+
const originalHandler = this.agentInstance.onSessionUpdate;
|
|
575
751
|
this.agentInstance.onSessionUpdate = (event) => {
|
|
576
752
|
if (event.type === "text") title += event.content;
|
|
577
753
|
};
|
|
@@ -579,7 +755,7 @@ var Session = class {
|
|
|
579
755
|
await this.agentInstance.prompt(
|
|
580
756
|
"Summarize this conversation in max 5 words for a topic title. Reply ONLY with the title, nothing else."
|
|
581
757
|
);
|
|
582
|
-
this.name = title.trim().slice(0, 50)
|
|
758
|
+
this.name = title.trim().slice(0, 50) || `Session ${this.id.slice(0, 6)}`;
|
|
583
759
|
this.log.info({ name: this.name }, "Session auto-named");
|
|
584
760
|
if (this.adapter && this.name) {
|
|
585
761
|
await this.adapter.renameSessionThread(this.id, this.name);
|
|
@@ -587,16 +763,18 @@ var Session = class {
|
|
|
587
763
|
} catch {
|
|
588
764
|
this.name = `Session ${this.id.slice(0, 6)}`;
|
|
589
765
|
} finally {
|
|
590
|
-
this.agentInstance.onSessionUpdate =
|
|
766
|
+
this.agentInstance.onSessionUpdate = originalHandler;
|
|
591
767
|
}
|
|
592
768
|
}
|
|
593
769
|
/** Fire-and-forget warm-up: primes model cache while user types their first message */
|
|
594
770
|
async warmup() {
|
|
595
|
-
this.
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
771
|
+
await this.queue.enqueue("\0__warmup__");
|
|
772
|
+
}
|
|
773
|
+
async runWarmup() {
|
|
774
|
+
this.pause((_event, args) => {
|
|
775
|
+
const agentEvent = args[0];
|
|
776
|
+
return agentEvent?.type === "commands_update";
|
|
777
|
+
});
|
|
600
778
|
try {
|
|
601
779
|
const start = Date.now();
|
|
602
780
|
await this.agentInstance.prompt('Reply with only "ready".');
|
|
@@ -605,19 +783,15 @@ var Session = class {
|
|
|
605
783
|
} catch (err) {
|
|
606
784
|
this.log.error({ err }, "Warm-up failed");
|
|
607
785
|
} finally {
|
|
608
|
-
this.
|
|
609
|
-
this.
|
|
610
|
-
if (this.promptQueue.length > 0) {
|
|
611
|
-
const next = this.promptQueue.shift();
|
|
612
|
-
await this.runPrompt(next);
|
|
613
|
-
}
|
|
786
|
+
this.clearBuffer();
|
|
787
|
+
this.resume();
|
|
614
788
|
}
|
|
615
789
|
}
|
|
616
790
|
async cancel() {
|
|
617
|
-
this.
|
|
618
|
-
this.status = "cancelled";
|
|
791
|
+
this.queue.clear();
|
|
619
792
|
this.log.info("Session cancelled");
|
|
620
793
|
await this.agentInstance.cancel();
|
|
794
|
+
this.status = "active";
|
|
621
795
|
}
|
|
622
796
|
async destroy() {
|
|
623
797
|
this.log.info("Session destroyed");
|
|
@@ -763,6 +937,206 @@ var NotificationManager = class {
|
|
|
763
937
|
}
|
|
764
938
|
};
|
|
765
939
|
|
|
940
|
+
// src/tunnel/extract-file-info.ts
|
|
941
|
+
function extractFileInfo(name, kind, content, rawInput, meta) {
|
|
942
|
+
if (kind && !["read", "edit", "write"].includes(kind)) return null;
|
|
943
|
+
let info = null;
|
|
944
|
+
if (meta) {
|
|
945
|
+
const m = meta;
|
|
946
|
+
const tr = m?.claudeCode?.toolResponse;
|
|
947
|
+
const file = tr?.file;
|
|
948
|
+
if (file?.filePath && file?.content) {
|
|
949
|
+
info = { filePath: file.filePath, content: file.content };
|
|
950
|
+
}
|
|
951
|
+
if (!info && tr?.filePath && tr?.content) {
|
|
952
|
+
info = { filePath: tr.filePath, content: tr.content };
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
if (!info && rawInput) {
|
|
956
|
+
const ri = rawInput;
|
|
957
|
+
const filePath = ri?.file_path || ri?.filePath || ri?.path;
|
|
958
|
+
if (typeof filePath === "string") {
|
|
959
|
+
const parsed = content ? parseContent(content) : null;
|
|
960
|
+
info = { filePath, content: parsed?.content || ri?.content, oldContent: parsed?.oldContent };
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
if (!info && content) {
|
|
964
|
+
info = parseContent(content);
|
|
965
|
+
}
|
|
966
|
+
if (!info) return null;
|
|
967
|
+
if (!info.filePath) {
|
|
968
|
+
const pathMatch = name.match(/(?:Read|Edit|Write|View)\s+(.+)/i);
|
|
969
|
+
if (pathMatch) info.filePath = pathMatch[1].trim();
|
|
970
|
+
}
|
|
971
|
+
if (!info.filePath || !info.content) return null;
|
|
972
|
+
return info;
|
|
973
|
+
}
|
|
974
|
+
function parseContent(content) {
|
|
975
|
+
if (typeof content === "string") {
|
|
976
|
+
return { content };
|
|
977
|
+
}
|
|
978
|
+
if (Array.isArray(content)) {
|
|
979
|
+
for (const block of content) {
|
|
980
|
+
const result = parseContent(block);
|
|
981
|
+
if (result?.content || result?.filePath) return result;
|
|
982
|
+
}
|
|
983
|
+
return null;
|
|
984
|
+
}
|
|
985
|
+
if (typeof content === "object" && content !== null) {
|
|
986
|
+
const c = content;
|
|
987
|
+
if (c.type === "diff" && typeof c.path === "string") {
|
|
988
|
+
const newText = c.newText;
|
|
989
|
+
const oldText = c.oldText;
|
|
990
|
+
if (newText) {
|
|
991
|
+
return {
|
|
992
|
+
filePath: c.path,
|
|
993
|
+
content: newText,
|
|
994
|
+
oldContent: oldText ?? void 0
|
|
995
|
+
};
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
if (c.type === "content" && c.content) {
|
|
999
|
+
return parseContent(c.content);
|
|
1000
|
+
}
|
|
1001
|
+
if (c.type === "text" && typeof c.text === "string") {
|
|
1002
|
+
return { content: c.text, filePath: c.filePath };
|
|
1003
|
+
}
|
|
1004
|
+
if (typeof c.text === "string") {
|
|
1005
|
+
return { content: c.text, filePath: c.filePath };
|
|
1006
|
+
}
|
|
1007
|
+
if (typeof c.file_path === "string" || typeof c.filePath === "string" || typeof c.path === "string") {
|
|
1008
|
+
const filePath = c.file_path || c.filePath || c.path;
|
|
1009
|
+
const fileContent = c.content || c.text || c.output || c.newText;
|
|
1010
|
+
if (typeof fileContent === "string") {
|
|
1011
|
+
return {
|
|
1012
|
+
filePath,
|
|
1013
|
+
content: fileContent,
|
|
1014
|
+
oldContent: c.old_content || c.oldText
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
if (c.input) {
|
|
1019
|
+
const result = parseContent(c.input);
|
|
1020
|
+
if (result) return result;
|
|
1021
|
+
}
|
|
1022
|
+
if (c.output) {
|
|
1023
|
+
const result = parseContent(c.output);
|
|
1024
|
+
if (result) return result;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
return null;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
// src/core/message-transformer.ts
|
|
1031
|
+
var log2 = createChildLogger({ module: "message-transformer" });
|
|
1032
|
+
var MessageTransformer = class {
|
|
1033
|
+
constructor(tunnelService) {
|
|
1034
|
+
this.tunnelService = tunnelService;
|
|
1035
|
+
}
|
|
1036
|
+
transform(event, sessionContext) {
|
|
1037
|
+
switch (event.type) {
|
|
1038
|
+
case "text":
|
|
1039
|
+
return { type: "text", text: event.content };
|
|
1040
|
+
case "thought":
|
|
1041
|
+
return { type: "thought", text: event.content };
|
|
1042
|
+
case "tool_call": {
|
|
1043
|
+
const metadata = {
|
|
1044
|
+
id: event.id,
|
|
1045
|
+
name: event.name,
|
|
1046
|
+
kind: event.kind,
|
|
1047
|
+
status: event.status,
|
|
1048
|
+
content: event.content,
|
|
1049
|
+
locations: event.locations
|
|
1050
|
+
};
|
|
1051
|
+
this.enrichWithViewerLinks(event, metadata, sessionContext);
|
|
1052
|
+
return { type: "tool_call", text: event.name, metadata };
|
|
1053
|
+
}
|
|
1054
|
+
case "tool_update": {
|
|
1055
|
+
const metadata = {
|
|
1056
|
+
id: event.id,
|
|
1057
|
+
name: event.name,
|
|
1058
|
+
kind: event.kind,
|
|
1059
|
+
status: event.status,
|
|
1060
|
+
content: event.content
|
|
1061
|
+
};
|
|
1062
|
+
this.enrichWithViewerLinks(event, metadata, sessionContext);
|
|
1063
|
+
return { type: "tool_update", text: "", metadata };
|
|
1064
|
+
}
|
|
1065
|
+
case "plan":
|
|
1066
|
+
return {
|
|
1067
|
+
type: "plan",
|
|
1068
|
+
text: "",
|
|
1069
|
+
metadata: { entries: event.entries }
|
|
1070
|
+
};
|
|
1071
|
+
case "usage":
|
|
1072
|
+
return {
|
|
1073
|
+
type: "usage",
|
|
1074
|
+
text: "",
|
|
1075
|
+
metadata: {
|
|
1076
|
+
tokensUsed: event.tokensUsed,
|
|
1077
|
+
contextSize: event.contextSize,
|
|
1078
|
+
cost: event.cost
|
|
1079
|
+
}
|
|
1080
|
+
};
|
|
1081
|
+
case "session_end":
|
|
1082
|
+
return { type: "session_end", text: `Done (${event.reason})` };
|
|
1083
|
+
case "error":
|
|
1084
|
+
return { type: "error", text: event.message };
|
|
1085
|
+
default:
|
|
1086
|
+
return { type: "text", text: "" };
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
enrichWithViewerLinks(event, metadata, sessionContext) {
|
|
1090
|
+
if (!this.tunnelService || !sessionContext) return;
|
|
1091
|
+
const name = "name" in event ? event.name || "" : "";
|
|
1092
|
+
const kind = "kind" in event ? event.kind : void 0;
|
|
1093
|
+
log2.debug(
|
|
1094
|
+
{ name, kind, status: event.status, hasContent: !!event.content },
|
|
1095
|
+
"enrichWithViewerLinks: inspecting event"
|
|
1096
|
+
);
|
|
1097
|
+
const fileInfo = extractFileInfo(
|
|
1098
|
+
name,
|
|
1099
|
+
kind,
|
|
1100
|
+
event.content,
|
|
1101
|
+
event.rawInput,
|
|
1102
|
+
event.meta
|
|
1103
|
+
);
|
|
1104
|
+
if (!fileInfo) return;
|
|
1105
|
+
log2.info(
|
|
1106
|
+
{
|
|
1107
|
+
name,
|
|
1108
|
+
kind,
|
|
1109
|
+
filePath: fileInfo.filePath,
|
|
1110
|
+
hasOldContent: !!fileInfo.oldContent
|
|
1111
|
+
},
|
|
1112
|
+
"enrichWithViewerLinks: extracted file info"
|
|
1113
|
+
);
|
|
1114
|
+
const store = this.tunnelService.getStore();
|
|
1115
|
+
const viewerLinks = {};
|
|
1116
|
+
if (fileInfo.oldContent) {
|
|
1117
|
+
const id2 = store.storeDiff(
|
|
1118
|
+
sessionContext.id,
|
|
1119
|
+
fileInfo.filePath,
|
|
1120
|
+
fileInfo.oldContent,
|
|
1121
|
+
fileInfo.content,
|
|
1122
|
+
sessionContext.workingDirectory
|
|
1123
|
+
);
|
|
1124
|
+
if (id2) viewerLinks.diff = this.tunnelService.diffUrl(id2);
|
|
1125
|
+
}
|
|
1126
|
+
const id = store.storeFile(
|
|
1127
|
+
sessionContext.id,
|
|
1128
|
+
fileInfo.filePath,
|
|
1129
|
+
fileInfo.content,
|
|
1130
|
+
sessionContext.workingDirectory
|
|
1131
|
+
);
|
|
1132
|
+
if (id) viewerLinks.file = this.tunnelService.fileUrl(id);
|
|
1133
|
+
if (Object.keys(viewerLinks).length > 0) {
|
|
1134
|
+
metadata.viewerLinks = viewerLinks;
|
|
1135
|
+
metadata.viewerFilePath = fileInfo.filePath;
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
};
|
|
1139
|
+
|
|
766
1140
|
// src/core/core.ts
|
|
767
1141
|
import path3 from "path";
|
|
768
1142
|
import os from "os";
|
|
@@ -770,7 +1144,7 @@ import os from "os";
|
|
|
770
1144
|
// src/core/session-store.ts
|
|
771
1145
|
import fs2 from "fs";
|
|
772
1146
|
import path2 from "path";
|
|
773
|
-
var
|
|
1147
|
+
var log3 = createChildLogger({ module: "session-store" });
|
|
774
1148
|
var DEBOUNCE_MS = 2e3;
|
|
775
1149
|
var JsonFileSessionStore = class {
|
|
776
1150
|
records = /* @__PURE__ */ new Map();
|
|
@@ -847,7 +1221,7 @@ var JsonFileSessionStore = class {
|
|
|
847
1221
|
fs2.readFileSync(this.filePath, "utf-8")
|
|
848
1222
|
);
|
|
849
1223
|
if (raw.version !== 1) {
|
|
850
|
-
|
|
1224
|
+
log3.warn(
|
|
851
1225
|
{ version: raw.version },
|
|
852
1226
|
"Unknown session store version, skipping load"
|
|
853
1227
|
);
|
|
@@ -856,9 +1230,9 @@ var JsonFileSessionStore = class {
|
|
|
856
1230
|
for (const [id, record] of Object.entries(raw.sessions)) {
|
|
857
1231
|
this.records.set(id, record);
|
|
858
1232
|
}
|
|
859
|
-
|
|
1233
|
+
log3.info({ count: this.records.size }, "Loaded session records");
|
|
860
1234
|
} catch (err) {
|
|
861
|
-
|
|
1235
|
+
log3.error({ err }, "Failed to load session store");
|
|
862
1236
|
}
|
|
863
1237
|
}
|
|
864
1238
|
cleanup() {
|
|
@@ -874,7 +1248,7 @@ var JsonFileSessionStore = class {
|
|
|
874
1248
|
}
|
|
875
1249
|
}
|
|
876
1250
|
if (removed > 0) {
|
|
877
|
-
|
|
1251
|
+
log3.info({ removed }, "Cleaned up expired session records");
|
|
878
1252
|
this.scheduleDiskWrite();
|
|
879
1253
|
}
|
|
880
1254
|
}
|
|
@@ -886,105 +1260,18 @@ var JsonFileSessionStore = class {
|
|
|
886
1260
|
}
|
|
887
1261
|
};
|
|
888
1262
|
|
|
889
|
-
// src/tunnel/extract-file-info.ts
|
|
890
|
-
function extractFileInfo(name, kind, content, rawInput, meta) {
|
|
891
|
-
if (kind && !["read", "edit", "write"].includes(kind)) return null;
|
|
892
|
-
let info = null;
|
|
893
|
-
if (meta) {
|
|
894
|
-
const m = meta;
|
|
895
|
-
const tr = m?.claudeCode?.toolResponse;
|
|
896
|
-
const file = tr?.file;
|
|
897
|
-
if (file?.filePath && file?.content) {
|
|
898
|
-
info = { filePath: file.filePath, content: file.content };
|
|
899
|
-
}
|
|
900
|
-
if (!info && tr?.filePath && tr?.content) {
|
|
901
|
-
info = { filePath: tr.filePath, content: tr.content };
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
|
-
if (!info && rawInput) {
|
|
905
|
-
const ri = rawInput;
|
|
906
|
-
const filePath = ri?.file_path || ri?.filePath || ri?.path;
|
|
907
|
-
if (typeof filePath === "string") {
|
|
908
|
-
const parsed = content ? parseContent(content) : null;
|
|
909
|
-
info = { filePath, content: parsed?.content || ri?.content, oldContent: parsed?.oldContent };
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
if (!info && content) {
|
|
913
|
-
info = parseContent(content);
|
|
914
|
-
}
|
|
915
|
-
if (!info) return null;
|
|
916
|
-
if (!info.filePath) {
|
|
917
|
-
const pathMatch = name.match(/(?:Read|Edit|Write|View)\s+(.+)/i);
|
|
918
|
-
if (pathMatch) info.filePath = pathMatch[1].trim();
|
|
919
|
-
}
|
|
920
|
-
if (!info.filePath || !info.content) return null;
|
|
921
|
-
return info;
|
|
922
|
-
}
|
|
923
|
-
function parseContent(content) {
|
|
924
|
-
if (typeof content === "string") {
|
|
925
|
-
return { content };
|
|
926
|
-
}
|
|
927
|
-
if (Array.isArray(content)) {
|
|
928
|
-
for (const block of content) {
|
|
929
|
-
const result = parseContent(block);
|
|
930
|
-
if (result?.content || result?.filePath) return result;
|
|
931
|
-
}
|
|
932
|
-
return null;
|
|
933
|
-
}
|
|
934
|
-
if (typeof content === "object" && content !== null) {
|
|
935
|
-
const c = content;
|
|
936
|
-
if (c.type === "diff" && typeof c.path === "string") {
|
|
937
|
-
const newText = c.newText;
|
|
938
|
-
const oldText = c.oldText;
|
|
939
|
-
if (newText) {
|
|
940
|
-
return {
|
|
941
|
-
filePath: c.path,
|
|
942
|
-
content: newText,
|
|
943
|
-
oldContent: oldText ?? void 0
|
|
944
|
-
};
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
|
-
if (c.type === "content" && c.content) {
|
|
948
|
-
return parseContent(c.content);
|
|
949
|
-
}
|
|
950
|
-
if (c.type === "text" && typeof c.text === "string") {
|
|
951
|
-
return { content: c.text, filePath: c.filePath };
|
|
952
|
-
}
|
|
953
|
-
if (typeof c.text === "string") {
|
|
954
|
-
return { content: c.text, filePath: c.filePath };
|
|
955
|
-
}
|
|
956
|
-
if (typeof c.file_path === "string" || typeof c.filePath === "string" || typeof c.path === "string") {
|
|
957
|
-
const filePath = c.file_path || c.filePath || c.path;
|
|
958
|
-
const fileContent = c.content || c.text || c.output || c.newText;
|
|
959
|
-
if (typeof fileContent === "string") {
|
|
960
|
-
return {
|
|
961
|
-
filePath,
|
|
962
|
-
content: fileContent,
|
|
963
|
-
oldContent: c.old_content || c.oldText
|
|
964
|
-
};
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
if (c.input) {
|
|
968
|
-
const result = parseContent(c.input);
|
|
969
|
-
if (result) return result;
|
|
970
|
-
}
|
|
971
|
-
if (c.output) {
|
|
972
|
-
const result = parseContent(c.output);
|
|
973
|
-
if (result) return result;
|
|
974
|
-
}
|
|
975
|
-
}
|
|
976
|
-
return null;
|
|
977
|
-
}
|
|
978
|
-
|
|
979
1263
|
// src/core/core.ts
|
|
980
|
-
var
|
|
1264
|
+
var log4 = createChildLogger({ module: "core" });
|
|
981
1265
|
var OpenACPCore = class {
|
|
982
1266
|
configManager;
|
|
983
1267
|
agentManager;
|
|
984
1268
|
sessionManager;
|
|
985
1269
|
notificationManager;
|
|
1270
|
+
messageTransformer;
|
|
986
1271
|
adapters = /* @__PURE__ */ new Map();
|
|
987
|
-
|
|
1272
|
+
/** Set by main.ts — triggers graceful shutdown with restart exit code */
|
|
1273
|
+
requestRestart = null;
|
|
1274
|
+
_tunnelService;
|
|
988
1275
|
sessionStore = null;
|
|
989
1276
|
resumeLocks = /* @__PURE__ */ new Map();
|
|
990
1277
|
constructor(configManager) {
|
|
@@ -998,6 +1285,14 @@ var OpenACPCore = class {
|
|
|
998
1285
|
);
|
|
999
1286
|
this.sessionManager = new SessionManager(this.sessionStore);
|
|
1000
1287
|
this.notificationManager = new NotificationManager(this.adapters);
|
|
1288
|
+
this.messageTransformer = new MessageTransformer();
|
|
1289
|
+
}
|
|
1290
|
+
get tunnelService() {
|
|
1291
|
+
return this._tunnelService;
|
|
1292
|
+
}
|
|
1293
|
+
set tunnelService(service) {
|
|
1294
|
+
this._tunnelService = service;
|
|
1295
|
+
this.messageTransformer = new MessageTransformer(service);
|
|
1001
1296
|
}
|
|
1002
1297
|
registerAdapter(name, adapter) {
|
|
1003
1298
|
this.adapters.set(name, adapter);
|
|
@@ -1024,7 +1319,7 @@ var OpenACPCore = class {
|
|
|
1024
1319
|
// --- Message Routing ---
|
|
1025
1320
|
async handleMessage(message) {
|
|
1026
1321
|
const config = this.configManager.get();
|
|
1027
|
-
|
|
1322
|
+
log4.debug(
|
|
1028
1323
|
{
|
|
1029
1324
|
channelId: message.channelId,
|
|
1030
1325
|
threadId: message.threadId,
|
|
@@ -1034,7 +1329,7 @@ var OpenACPCore = class {
|
|
|
1034
1329
|
);
|
|
1035
1330
|
if (config.security.allowedUserIds.length > 0) {
|
|
1036
1331
|
if (!config.security.allowedUserIds.includes(message.userId)) {
|
|
1037
|
-
|
|
1332
|
+
log4.warn(
|
|
1038
1333
|
{ userId: message.userId },
|
|
1039
1334
|
"Rejected message from unauthorized user"
|
|
1040
1335
|
);
|
|
@@ -1043,7 +1338,7 @@ var OpenACPCore = class {
|
|
|
1043
1338
|
}
|
|
1044
1339
|
const activeSessions = this.sessionManager.listSessions().filter((s) => s.status === "active" || s.status === "initializing");
|
|
1045
1340
|
if (activeSessions.length >= config.security.maxConcurrentSessions) {
|
|
1046
|
-
|
|
1341
|
+
log4.warn(
|
|
1047
1342
|
{
|
|
1048
1343
|
userId: message.userId,
|
|
1049
1344
|
currentCount: activeSessions.length,
|
|
@@ -1074,7 +1369,7 @@ var OpenACPCore = class {
|
|
|
1074
1369
|
async handleNewSession(channelId, agentName, workspacePath) {
|
|
1075
1370
|
const config = this.configManager.get();
|
|
1076
1371
|
const resolvedAgent = agentName || config.defaultAgent;
|
|
1077
|
-
|
|
1372
|
+
log4.info({ channelId, agentName: resolvedAgent }, "New session request");
|
|
1078
1373
|
const resolvedWorkspace = this.configManager.resolveWorkspace(
|
|
1079
1374
|
workspacePath || config.agents[resolvedAgent]?.workingDirectory
|
|
1080
1375
|
);
|
|
@@ -1152,13 +1447,13 @@ var OpenACPCore = class {
|
|
|
1152
1447
|
status: "active",
|
|
1153
1448
|
lastActiveAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1154
1449
|
});
|
|
1155
|
-
|
|
1450
|
+
log4.info(
|
|
1156
1451
|
{ sessionId: session.id, threadId: message.threadId },
|
|
1157
1452
|
"Lazy resume successful"
|
|
1158
1453
|
);
|
|
1159
1454
|
return session;
|
|
1160
1455
|
} catch (err) {
|
|
1161
|
-
|
|
1456
|
+
log4.error({ err, record }, "Lazy resume failed");
|
|
1162
1457
|
return null;
|
|
1163
1458
|
} finally {
|
|
1164
1459
|
this.resumeLocks.delete(lockKey);
|
|
@@ -1168,98 +1463,27 @@ var OpenACPCore = class {
|
|
|
1168
1463
|
return resumePromise;
|
|
1169
1464
|
}
|
|
1170
1465
|
// --- Event Wiring ---
|
|
1171
|
-
toOutgoingMessage(event, session) {
|
|
1172
|
-
switch (event.type) {
|
|
1173
|
-
case "text":
|
|
1174
|
-
return { type: "text", text: event.content };
|
|
1175
|
-
case "thought":
|
|
1176
|
-
return { type: "thought", text: event.content };
|
|
1177
|
-
case "tool_call": {
|
|
1178
|
-
const metadata = {
|
|
1179
|
-
id: event.id,
|
|
1180
|
-
name: event.name,
|
|
1181
|
-
kind: event.kind,
|
|
1182
|
-
status: event.status,
|
|
1183
|
-
content: event.content,
|
|
1184
|
-
locations: event.locations
|
|
1185
|
-
};
|
|
1186
|
-
this.enrichWithViewerLinks(event, metadata, session);
|
|
1187
|
-
return { type: "tool_call", text: event.name, metadata };
|
|
1188
|
-
}
|
|
1189
|
-
case "tool_update": {
|
|
1190
|
-
const metadata = {
|
|
1191
|
-
id: event.id,
|
|
1192
|
-
name: event.name,
|
|
1193
|
-
kind: event.kind,
|
|
1194
|
-
status: event.status,
|
|
1195
|
-
content: event.content
|
|
1196
|
-
};
|
|
1197
|
-
this.enrichWithViewerLinks(event, metadata, session);
|
|
1198
|
-
return { type: "tool_update", text: "", metadata };
|
|
1199
|
-
}
|
|
1200
|
-
case "plan":
|
|
1201
|
-
return { type: "plan", text: "", metadata: { entries: event.entries } };
|
|
1202
|
-
case "usage":
|
|
1203
|
-
return {
|
|
1204
|
-
type: "usage",
|
|
1205
|
-
text: "",
|
|
1206
|
-
metadata: {
|
|
1207
|
-
tokensUsed: event.tokensUsed,
|
|
1208
|
-
contextSize: event.contextSize,
|
|
1209
|
-
cost: event.cost
|
|
1210
|
-
}
|
|
1211
|
-
};
|
|
1212
|
-
default:
|
|
1213
|
-
return { type: "text", text: "" };
|
|
1214
|
-
}
|
|
1215
|
-
}
|
|
1216
|
-
enrichWithViewerLinks(event, metadata, session) {
|
|
1217
|
-
if (!this.tunnelService || !session) return;
|
|
1218
|
-
const name = "name" in event ? event.name || "" : "";
|
|
1219
|
-
const kind = "kind" in event ? event.kind : void 0;
|
|
1220
|
-
log3.debug(
|
|
1221
|
-
{ name, kind, status: event.status, hasContent: !!event.content },
|
|
1222
|
-
"enrichWithViewerLinks: inspecting event"
|
|
1223
|
-
);
|
|
1224
|
-
const fileInfo = extractFileInfo(name, kind, event.content, event.rawInput, event.meta);
|
|
1225
|
-
if (!fileInfo) return;
|
|
1226
|
-
log3.info(
|
|
1227
|
-
{
|
|
1228
|
-
name,
|
|
1229
|
-
kind,
|
|
1230
|
-
filePath: fileInfo.filePath,
|
|
1231
|
-
hasOldContent: !!fileInfo.oldContent
|
|
1232
|
-
},
|
|
1233
|
-
"enrichWithViewerLinks: extracted file info"
|
|
1234
|
-
);
|
|
1235
|
-
const store = this.tunnelService.getStore();
|
|
1236
|
-
const viewerLinks = {};
|
|
1237
|
-
if (fileInfo.oldContent) {
|
|
1238
|
-
const id2 = store.storeDiff(
|
|
1239
|
-
session.id,
|
|
1240
|
-
fileInfo.filePath,
|
|
1241
|
-
fileInfo.oldContent,
|
|
1242
|
-
fileInfo.content,
|
|
1243
|
-
session.workingDirectory
|
|
1244
|
-
);
|
|
1245
|
-
if (id2) viewerLinks.diff = this.tunnelService.diffUrl(id2);
|
|
1246
|
-
}
|
|
1247
|
-
const id = store.storeFile(
|
|
1248
|
-
session.id,
|
|
1249
|
-
fileInfo.filePath,
|
|
1250
|
-
fileInfo.content,
|
|
1251
|
-
session.workingDirectory
|
|
1252
|
-
);
|
|
1253
|
-
if (id) viewerLinks.file = this.tunnelService.fileUrl(id);
|
|
1254
|
-
if (Object.keys(viewerLinks).length > 0) {
|
|
1255
|
-
metadata.viewerLinks = viewerLinks;
|
|
1256
|
-
metadata.viewerFilePath = fileInfo.filePath;
|
|
1257
|
-
}
|
|
1258
|
-
}
|
|
1259
1466
|
// Public — adapters call this for assistant session wiring
|
|
1260
1467
|
wireSessionEvents(session, adapter) {
|
|
1261
1468
|
session.adapter = adapter;
|
|
1262
1469
|
session.agentInstance.onSessionUpdate = (event) => {
|
|
1470
|
+
session.emit("agent_event", event);
|
|
1471
|
+
};
|
|
1472
|
+
session.agentInstance.onPermissionRequest = async (request) => {
|
|
1473
|
+
session.emit("permission_request", request);
|
|
1474
|
+
const promise = session.permissionGate.setPending(request);
|
|
1475
|
+
await adapter.sendPermissionRequest(session.id, request);
|
|
1476
|
+
return promise;
|
|
1477
|
+
};
|
|
1478
|
+
const sessionContext = {
|
|
1479
|
+
get id() {
|
|
1480
|
+
return session.id;
|
|
1481
|
+
},
|
|
1482
|
+
get workingDirectory() {
|
|
1483
|
+
return session.workingDirectory;
|
|
1484
|
+
}
|
|
1485
|
+
};
|
|
1486
|
+
session.on("agent_event", (event) => {
|
|
1263
1487
|
switch (event.type) {
|
|
1264
1488
|
case "text":
|
|
1265
1489
|
case "thought":
|
|
@@ -1269,17 +1493,17 @@ var OpenACPCore = class {
|
|
|
1269
1493
|
case "usage":
|
|
1270
1494
|
adapter.sendMessage(
|
|
1271
1495
|
session.id,
|
|
1272
|
-
this.
|
|
1496
|
+
this.messageTransformer.transform(event, sessionContext)
|
|
1273
1497
|
);
|
|
1274
1498
|
break;
|
|
1275
1499
|
case "session_end":
|
|
1276
1500
|
session.status = "finished";
|
|
1277
1501
|
this.sessionManager.updateSessionStatus(session.id, "finished");
|
|
1278
1502
|
adapter.cleanupSkillCommands(session.id);
|
|
1279
|
-
adapter.sendMessage(
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1503
|
+
adapter.sendMessage(
|
|
1504
|
+
session.id,
|
|
1505
|
+
this.messageTransformer.transform(event)
|
|
1506
|
+
);
|
|
1283
1507
|
this.notificationManager.notify(session.channelId, {
|
|
1284
1508
|
sessionId: session.id,
|
|
1285
1509
|
sessionName: session.name,
|
|
@@ -1290,10 +1514,10 @@ var OpenACPCore = class {
|
|
|
1290
1514
|
case "error":
|
|
1291
1515
|
this.sessionManager.updateSessionStatus(session.id, "error");
|
|
1292
1516
|
adapter.cleanupSkillCommands(session.id);
|
|
1293
|
-
adapter.sendMessage(
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1517
|
+
adapter.sendMessage(
|
|
1518
|
+
session.id,
|
|
1519
|
+
this.messageTransformer.transform(event)
|
|
1520
|
+
);
|
|
1297
1521
|
this.notificationManager.notify(session.channelId, {
|
|
1298
1522
|
sessionId: session.id,
|
|
1299
1523
|
sessionName: session.name,
|
|
@@ -1302,18 +1526,11 @@ var OpenACPCore = class {
|
|
|
1302
1526
|
});
|
|
1303
1527
|
break;
|
|
1304
1528
|
case "commands_update":
|
|
1305
|
-
|
|
1529
|
+
log4.debug({ commands: event.commands }, "Commands available");
|
|
1306
1530
|
adapter.sendSkillCommands(session.id, event.commands);
|
|
1307
1531
|
break;
|
|
1308
1532
|
}
|
|
1309
|
-
};
|
|
1310
|
-
session.agentInstance.onPermissionRequest = async (request) => {
|
|
1311
|
-
const promise = new Promise((resolve) => {
|
|
1312
|
-
session.pendingPermission = { requestId: request.id, resolve };
|
|
1313
|
-
});
|
|
1314
|
-
await adapter.sendPermissionRequest(session.id, request);
|
|
1315
|
-
return promise;
|
|
1316
|
-
};
|
|
1533
|
+
});
|
|
1317
1534
|
}
|
|
1318
1535
|
};
|
|
1319
1536
|
|
|
@@ -1335,7 +1552,7 @@ import * as http from "http";
|
|
|
1335
1552
|
import * as fs3 from "fs";
|
|
1336
1553
|
import * as path4 from "path";
|
|
1337
1554
|
import * as os2 from "os";
|
|
1338
|
-
var
|
|
1555
|
+
var log5 = createChildLogger({ module: "api-server" });
|
|
1339
1556
|
var DEFAULT_PORT_FILE = path4.join(os2.homedir(), ".openacp", "api.port");
|
|
1340
1557
|
var ApiServer = class {
|
|
1341
1558
|
constructor(core, config, portFilePath) {
|
|
@@ -1351,7 +1568,7 @@ var ApiServer = class {
|
|
|
1351
1568
|
await new Promise((resolve, reject) => {
|
|
1352
1569
|
this.server.on("error", (err) => {
|
|
1353
1570
|
if (err.code === "EADDRINUSE") {
|
|
1354
|
-
|
|
1571
|
+
log5.warn({ port: this.config.port }, "API port in use, continuing without API server");
|
|
1355
1572
|
this.server = null;
|
|
1356
1573
|
resolve();
|
|
1357
1574
|
} else {
|
|
@@ -1364,7 +1581,7 @@ var ApiServer = class {
|
|
|
1364
1581
|
this.actualPort = addr.port;
|
|
1365
1582
|
}
|
|
1366
1583
|
this.writePortFile();
|
|
1367
|
-
|
|
1584
|
+
log5.info({ host: this.config.host, port: this.actualPort }, "API server listening");
|
|
1368
1585
|
resolve();
|
|
1369
1586
|
});
|
|
1370
1587
|
});
|
|
@@ -1409,7 +1626,7 @@ var ApiServer = class {
|
|
|
1409
1626
|
this.sendJson(res, 404, { error: "Not found" });
|
|
1410
1627
|
}
|
|
1411
1628
|
} catch (err) {
|
|
1412
|
-
|
|
1629
|
+
log5.error({ err }, "API request error");
|
|
1413
1630
|
this.sendJson(res, 500, { error: "Internal server error" });
|
|
1414
1631
|
}
|
|
1415
1632
|
}
|
|
@@ -1444,17 +1661,17 @@ var ApiServer = class {
|
|
|
1444
1661
|
session.threadId = threadId;
|
|
1445
1662
|
this.core.wireSessionEvents(session, adapter);
|
|
1446
1663
|
} catch (err) {
|
|
1447
|
-
|
|
1664
|
+
log5.warn({ err, sessionId: session.id }, "Failed to create session thread on adapter, running headless");
|
|
1448
1665
|
}
|
|
1449
1666
|
}
|
|
1450
1667
|
if (!adapter) {
|
|
1451
1668
|
session.agentInstance.onPermissionRequest = async (request) => {
|
|
1452
1669
|
const allowOption = request.options.find((o) => o.isAllow);
|
|
1453
|
-
|
|
1670
|
+
log5.debug({ sessionId: session.id, permissionId: request.id, option: allowOption?.id }, "Auto-approving permission for API session");
|
|
1454
1671
|
return allowOption?.id ?? request.options[0]?.id ?? "";
|
|
1455
1672
|
};
|
|
1456
1673
|
}
|
|
1457
|
-
session.warmup().catch((err) =>
|
|
1674
|
+
session.warmup().catch((err) => log5.warn({ err, sessionId: session.id }, "API session warmup failed"));
|
|
1458
1675
|
this.sendJson(res, 200, {
|
|
1459
1676
|
sessionId: session.id,
|
|
1460
1677
|
agent: session.agentName,
|
|
@@ -1824,7 +2041,7 @@ function buildDeepLink(chatId, messageId) {
|
|
|
1824
2041
|
// src/adapters/telegram/commands.ts
|
|
1825
2042
|
import { InlineKeyboard } from "grammy";
|
|
1826
2043
|
import { nanoid as nanoid2 } from "nanoid";
|
|
1827
|
-
var
|
|
2044
|
+
var log7 = createChildLogger({ module: "telegram-commands" });
|
|
1828
2045
|
function setupCommands(bot, core, chatId, assistant) {
|
|
1829
2046
|
bot.command("new", (ctx) => handleNew(ctx, core, chatId, assistant));
|
|
1830
2047
|
bot.command("new_chat", (ctx) => handleNewChat(ctx, core, chatId));
|
|
@@ -1835,9 +2052,11 @@ function setupCommands(bot, core, chatId, assistant) {
|
|
|
1835
2052
|
bot.command("menu", (ctx) => handleMenu(ctx));
|
|
1836
2053
|
bot.command("enable_dangerous", (ctx) => handleEnableDangerous(ctx, core));
|
|
1837
2054
|
bot.command("disable_dangerous", (ctx) => handleDisableDangerous(ctx, core));
|
|
2055
|
+
bot.command("restart", (ctx) => handleRestart(ctx, core));
|
|
2056
|
+
bot.command("update", (ctx) => handleUpdate(ctx, core));
|
|
1838
2057
|
}
|
|
1839
2058
|
function buildMenuKeyboard() {
|
|
1840
|
-
return new InlineKeyboard().text("\u{1F195} New Session", "m:new").text("\u{1F4AC} New Chat", "m:new_chat").row().text("\u26D4 Cancel", "m:cancel").text("\u{1F4CA} Status", "m:status").row().text("\u{1F916} Agents", "m:agents").text("\u2753 Help", "m:help");
|
|
2059
|
+
return new InlineKeyboard().text("\u{1F195} New Session", "m:new").text("\u{1F4AC} New Chat", "m:new_chat").row().text("\u26D4 Cancel", "m:cancel").text("\u{1F4CA} Status", "m:status").row().text("\u{1F916} Agents", "m:agents").text("\u2753 Help", "m:help").row().text("\u{1F504} Restart", "m:restart").text("\u2B06\uFE0F Update", "m:update");
|
|
1841
2060
|
}
|
|
1842
2061
|
function setupMenuCallbacks(bot, core, chatId) {
|
|
1843
2062
|
bot.callbackQuery(/^m:/, async (ctx) => {
|
|
@@ -1865,6 +2084,12 @@ function setupMenuCallbacks(bot, core, chatId) {
|
|
|
1865
2084
|
case "m:help":
|
|
1866
2085
|
await handleHelp(ctx);
|
|
1867
2086
|
break;
|
|
2087
|
+
case "m:restart":
|
|
2088
|
+
await handleRestart(ctx, core);
|
|
2089
|
+
break;
|
|
2090
|
+
case "m:update":
|
|
2091
|
+
await handleUpdate(ctx, core);
|
|
2092
|
+
break;
|
|
1868
2093
|
}
|
|
1869
2094
|
});
|
|
1870
2095
|
}
|
|
@@ -1890,7 +2115,7 @@ async function handleNew(ctx, core, chatId, assistant) {
|
|
|
1890
2115
|
return;
|
|
1891
2116
|
}
|
|
1892
2117
|
}
|
|
1893
|
-
|
|
2118
|
+
log7.info({ userId: ctx.from?.id, agentName }, "New session command");
|
|
1894
2119
|
let threadId;
|
|
1895
2120
|
try {
|
|
1896
2121
|
const topicName = `\u{1F504} New Session`;
|
|
@@ -1924,9 +2149,9 @@ async function handleNew(ctx, core, chatId, assistant) {
|
|
|
1924
2149
|
reply_markup: buildDangerousModeKeyboard(session.id, false)
|
|
1925
2150
|
}
|
|
1926
2151
|
);
|
|
1927
|
-
session.warmup().catch((err) =>
|
|
2152
|
+
session.warmup().catch((err) => log7.error({ err }, "Warm-up error"));
|
|
1928
2153
|
} catch (err) {
|
|
1929
|
-
|
|
2154
|
+
log7.error({ err }, "Session creation failed");
|
|
1930
2155
|
if (threadId) {
|
|
1931
2156
|
try {
|
|
1932
2157
|
await ctx.api.deleteForumTopic(chatId, threadId);
|
|
@@ -1946,16 +2171,30 @@ async function handleNewChat(ctx, core, chatId) {
|
|
|
1946
2171
|
);
|
|
1947
2172
|
return;
|
|
1948
2173
|
}
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
2174
|
+
const currentSession = core.sessionManager.getSessionByThread(
|
|
2175
|
+
"telegram",
|
|
2176
|
+
String(threadId)
|
|
2177
|
+
);
|
|
2178
|
+
let agentName;
|
|
2179
|
+
let workspace;
|
|
2180
|
+
if (currentSession) {
|
|
2181
|
+
agentName = currentSession.agentName;
|
|
2182
|
+
workspace = currentSession.workingDirectory;
|
|
2183
|
+
} else {
|
|
2184
|
+
const record = core.sessionManager.getRecordByThread("telegram", String(threadId));
|
|
2185
|
+
if (!record || record.status === "cancelled" || record.status === "error") {
|
|
1952
2186
|
await ctx.reply("No active session in this topic.", {
|
|
1953
2187
|
parse_mode: "HTML"
|
|
1954
2188
|
});
|
|
1955
2189
|
return;
|
|
1956
2190
|
}
|
|
1957
|
-
|
|
1958
|
-
|
|
2191
|
+
agentName = record.agentName;
|
|
2192
|
+
workspace = record.workingDir;
|
|
2193
|
+
}
|
|
2194
|
+
let newThreadId;
|
|
2195
|
+
try {
|
|
2196
|
+
const topicName = `\u{1F504} ${agentName} \u2014 New Chat`;
|
|
2197
|
+
newThreadId = await createSessionTopic(
|
|
1959
2198
|
botFromCtx(ctx),
|
|
1960
2199
|
chatId,
|
|
1961
2200
|
topicName
|
|
@@ -1964,6 +2203,11 @@ async function handleNewChat(ctx, core, chatId) {
|
|
|
1964
2203
|
message_thread_id: newThreadId,
|
|
1965
2204
|
parse_mode: "HTML"
|
|
1966
2205
|
});
|
|
2206
|
+
const session = await core.handleNewSession(
|
|
2207
|
+
"telegram",
|
|
2208
|
+
agentName,
|
|
2209
|
+
workspace
|
|
2210
|
+
);
|
|
1967
2211
|
session.threadId = String(newThreadId);
|
|
1968
2212
|
await core.sessionManager.updateSessionPlatform(session.id, {
|
|
1969
2213
|
topicId: newThreadId
|
|
@@ -1979,8 +2223,14 @@ async function handleNewChat(ctx, core, chatId) {
|
|
|
1979
2223
|
reply_markup: buildDangerousModeKeyboard(session.id, false)
|
|
1980
2224
|
}
|
|
1981
2225
|
);
|
|
1982
|
-
session.warmup().catch((err) =>
|
|
2226
|
+
session.warmup().catch((err) => log7.error({ err }, "Warm-up error"));
|
|
1983
2227
|
} catch (err) {
|
|
2228
|
+
if (newThreadId) {
|
|
2229
|
+
try {
|
|
2230
|
+
await ctx.api.deleteForumTopic(chatId, newThreadId);
|
|
2231
|
+
} catch {
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
1984
2234
|
const message = err instanceof Error ? err.message : String(err);
|
|
1985
2235
|
await ctx.reply(`\u274C ${escapeHtml(message)}`, { parse_mode: "HTML" });
|
|
1986
2236
|
}
|
|
@@ -2002,14 +2252,14 @@ async function handleCancel(ctx, core, assistant) {
|
|
|
2002
2252
|
String(threadId)
|
|
2003
2253
|
);
|
|
2004
2254
|
if (session) {
|
|
2005
|
-
|
|
2255
|
+
log7.info({ sessionId: session.id }, "Cancel session command");
|
|
2006
2256
|
await session.cancel();
|
|
2007
2257
|
await ctx.reply("\u26D4 Session cancelled.", { parse_mode: "HTML" });
|
|
2008
2258
|
return;
|
|
2009
2259
|
}
|
|
2010
2260
|
const record = core.sessionManager.getRecordByThread("telegram", String(threadId));
|
|
2011
2261
|
if (record && record.status !== "cancelled" && record.status !== "error") {
|
|
2012
|
-
|
|
2262
|
+
log7.info({ sessionId: record.sessionId }, "Cancel session command (from store)");
|
|
2013
2263
|
await core.sessionManager.cancelSession(record.sessionId);
|
|
2014
2264
|
await ctx.reply("\u26D4 Session cancelled.", { parse_mode: "HTML" });
|
|
2015
2265
|
}
|
|
@@ -2027,7 +2277,7 @@ async function handleStatus(ctx, core) {
|
|
|
2027
2277
|
<b>Agent:</b> ${escapeHtml(session.agentName)}
|
|
2028
2278
|
<b>Status:</b> ${escapeHtml(session.status)}
|
|
2029
2279
|
<b>Workspace:</b> <code>${escapeHtml(session.workingDirectory)}</code>
|
|
2030
|
-
<b>Queue:</b> ${session.
|
|
2280
|
+
<b>Queue:</b> ${session.queueDepth} pending`,
|
|
2031
2281
|
{ parse_mode: "HTML" }
|
|
2032
2282
|
);
|
|
2033
2283
|
} else {
|
|
@@ -2083,6 +2333,8 @@ async function handleHelp(ctx) {
|
|
|
2083
2333
|
/status \u2014 Show session/system status
|
|
2084
2334
|
/agents \u2014 List available agents
|
|
2085
2335
|
/menu \u2014 Show interactive menu
|
|
2336
|
+
/restart \u2014 Restart OpenACP
|
|
2337
|
+
/update \u2014 Update to latest version and restart
|
|
2086
2338
|
/help \u2014 Show this help
|
|
2087
2339
|
|
|
2088
2340
|
Or just chat in the \u{1F916} Assistant topic for help!`,
|
|
@@ -2107,7 +2359,7 @@ function setupDangerousModeCallbacks(bot, core) {
|
|
|
2107
2359
|
return;
|
|
2108
2360
|
}
|
|
2109
2361
|
session.dangerousMode = !session.dangerousMode;
|
|
2110
|
-
|
|
2362
|
+
log7.info({ sessionId, dangerousMode: session.dangerousMode }, "Dangerous mode toggled via button");
|
|
2111
2363
|
const toastText = session.dangerousMode ? "\u2620\uFE0F Dangerous mode enabled \u2014 permissions auto-approved" : "\u{1F510} Dangerous mode disabled \u2014 permissions shown normally";
|
|
2112
2364
|
try {
|
|
2113
2365
|
await ctx.answerCallbackQuery({ text: toastText });
|
|
@@ -2146,6 +2398,52 @@ Use /disable_dangerous to restore normal behaviour.`,
|
|
|
2146
2398
|
{ parse_mode: "HTML" }
|
|
2147
2399
|
);
|
|
2148
2400
|
}
|
|
2401
|
+
async function handleUpdate(ctx, core) {
|
|
2402
|
+
if (!core.requestRestart) {
|
|
2403
|
+
await ctx.reply("\u26A0\uFE0F Update is not available (no restart handler registered).", { parse_mode: "HTML" });
|
|
2404
|
+
return;
|
|
2405
|
+
}
|
|
2406
|
+
const { getCurrentVersion, getLatestVersion, compareVersions, runUpdate } = await import("./version-VC5CPXBX.js");
|
|
2407
|
+
const current = getCurrentVersion();
|
|
2408
|
+
const statusMsg = await ctx.reply(`\u{1F50D} Checking for updates... (current: v${escapeHtml(current)})`, { parse_mode: "HTML" });
|
|
2409
|
+
const latest = await getLatestVersion();
|
|
2410
|
+
if (!latest) {
|
|
2411
|
+
await ctx.api.editMessageText(ctx.chat.id, statusMsg.message_id, "\u274C Could not check for updates.", { parse_mode: "HTML" });
|
|
2412
|
+
return;
|
|
2413
|
+
}
|
|
2414
|
+
if (compareVersions(current, latest) >= 0) {
|
|
2415
|
+
await ctx.api.editMessageText(ctx.chat.id, statusMsg.message_id, `\u2705 Already up to date (v${escapeHtml(current)}).`, { parse_mode: "HTML" });
|
|
2416
|
+
return;
|
|
2417
|
+
}
|
|
2418
|
+
await ctx.api.editMessageText(
|
|
2419
|
+
ctx.chat.id,
|
|
2420
|
+
statusMsg.message_id,
|
|
2421
|
+
`\u2B07\uFE0F Updating v${escapeHtml(current)} \u2192 v${escapeHtml(latest)}...`,
|
|
2422
|
+
{ parse_mode: "HTML" }
|
|
2423
|
+
);
|
|
2424
|
+
const ok = await runUpdate();
|
|
2425
|
+
if (!ok) {
|
|
2426
|
+
await ctx.api.editMessageText(ctx.chat.id, statusMsg.message_id, "\u274C Update failed. Try manually: <code>npm install -g @openacp/cli@latest</code>", { parse_mode: "HTML" });
|
|
2427
|
+
return;
|
|
2428
|
+
}
|
|
2429
|
+
await ctx.api.editMessageText(
|
|
2430
|
+
ctx.chat.id,
|
|
2431
|
+
statusMsg.message_id,
|
|
2432
|
+
`\u2705 Updated to v${escapeHtml(latest)}. Restarting...`,
|
|
2433
|
+
{ parse_mode: "HTML" }
|
|
2434
|
+
);
|
|
2435
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
2436
|
+
await core.requestRestart();
|
|
2437
|
+
}
|
|
2438
|
+
async function handleRestart(ctx, core) {
|
|
2439
|
+
if (!core.requestRestart) {
|
|
2440
|
+
await ctx.reply("\u26A0\uFE0F Restart is not available (no restart handler registered).", { parse_mode: "HTML" });
|
|
2441
|
+
return;
|
|
2442
|
+
}
|
|
2443
|
+
await ctx.reply("\u{1F504} <b>Restarting OpenACP...</b>\nRebuilding and restarting. Be back shortly.", { parse_mode: "HTML" });
|
|
2444
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
2445
|
+
await core.requestRestart();
|
|
2446
|
+
}
|
|
2149
2447
|
async function handleDisableDangerous(ctx, core) {
|
|
2150
2448
|
const threadId = ctx.message?.message_thread_id;
|
|
2151
2449
|
if (!threadId) {
|
|
@@ -2222,7 +2520,7 @@ async function executeNewSession(bot, core, chatId, agentName, workspace) {
|
|
|
2222
2520
|
});
|
|
2223
2521
|
const finalName = `\u{1F504} ${session.agentName} \u2014 New Session`;
|
|
2224
2522
|
await renameSessionTopic(bot, chatId, threadId, finalName);
|
|
2225
|
-
session.warmup().catch((err) =>
|
|
2523
|
+
session.warmup().catch((err) => log7.error({ err }, "Warm-up error"));
|
|
2226
2524
|
return { session, threadId, firstMsgId };
|
|
2227
2525
|
} catch (err) {
|
|
2228
2526
|
try {
|
|
@@ -2248,13 +2546,15 @@ var STATIC_COMMANDS = [
|
|
|
2248
2546
|
{ command: "help", description: "Help" },
|
|
2249
2547
|
{ command: "menu", description: "Show menu" },
|
|
2250
2548
|
{ command: "enable_dangerous", description: "Auto-approve all permission requests (session only)" },
|
|
2251
|
-
{ command: "disable_dangerous", description: "Restore normal permission prompts (session only)" }
|
|
2549
|
+
{ command: "disable_dangerous", description: "Restore normal permission prompts (session only)" },
|
|
2550
|
+
{ command: "restart", description: "Restart OpenACP" },
|
|
2551
|
+
{ command: "update", description: "Update to latest version and restart" }
|
|
2252
2552
|
];
|
|
2253
2553
|
|
|
2254
2554
|
// src/adapters/telegram/permissions.ts
|
|
2255
2555
|
import { InlineKeyboard as InlineKeyboard2 } from "grammy";
|
|
2256
2556
|
import { nanoid as nanoid3 } from "nanoid";
|
|
2257
|
-
var
|
|
2557
|
+
var log8 = createChildLogger({ module: "telegram-permissions" });
|
|
2258
2558
|
var PermissionHandler = class {
|
|
2259
2559
|
constructor(bot, chatId, getSession, sendNotification) {
|
|
2260
2560
|
this.bot = bot;
|
|
@@ -2314,10 +2614,9 @@ ${escapeHtml(request.description)}`,
|
|
|
2314
2614
|
}
|
|
2315
2615
|
const session = this.getSession(pending.sessionId);
|
|
2316
2616
|
const isAllow = pending.options.find((o) => o.id === optionId)?.isAllow ?? false;
|
|
2317
|
-
|
|
2318
|
-
if (session?.
|
|
2319
|
-
session.
|
|
2320
|
-
session.pendingPermission = void 0;
|
|
2617
|
+
log8.info({ requestId: pending.requestId, optionId, isAllow }, "Permission responded");
|
|
2618
|
+
if (session?.permissionGate.requestId === pending.requestId) {
|
|
2619
|
+
session.permissionGate.resolve(optionId);
|
|
2321
2620
|
}
|
|
2322
2621
|
this.pending.delete(callbackKey);
|
|
2323
2622
|
try {
|
|
@@ -2333,10 +2632,10 @@ ${escapeHtml(request.description)}`,
|
|
|
2333
2632
|
};
|
|
2334
2633
|
|
|
2335
2634
|
// src/adapters/telegram/assistant.ts
|
|
2336
|
-
var
|
|
2635
|
+
var log9 = createChildLogger({ module: "telegram-assistant" });
|
|
2337
2636
|
async function spawnAssistant(core, adapter, assistantTopicId) {
|
|
2338
2637
|
const config = core.configManager.get();
|
|
2339
|
-
|
|
2638
|
+
log9.info({ agent: config.defaultAgent }, "Creating assistant session...");
|
|
2340
2639
|
const session = await core.sessionManager.createSession(
|
|
2341
2640
|
"telegram",
|
|
2342
2641
|
config.defaultAgent,
|
|
@@ -2345,13 +2644,13 @@ async function spawnAssistant(core, adapter, assistantTopicId) {
|
|
|
2345
2644
|
);
|
|
2346
2645
|
session.threadId = String(assistantTopicId);
|
|
2347
2646
|
session.name = "Assistant";
|
|
2348
|
-
|
|
2647
|
+
log9.info({ sessionId: session.id }, "Assistant agent spawned");
|
|
2349
2648
|
core.wireSessionEvents(session, adapter);
|
|
2350
2649
|
const systemPrompt = buildAssistantSystemPrompt(config);
|
|
2351
2650
|
const ready = session.enqueuePrompt(systemPrompt).then(() => {
|
|
2352
|
-
|
|
2651
|
+
log9.info({ sessionId: session.id }, "Assistant system prompt completed");
|
|
2353
2652
|
}).catch((err) => {
|
|
2354
|
-
|
|
2653
|
+
log9.warn({ err }, "Assistant system prompt failed");
|
|
2355
2654
|
});
|
|
2356
2655
|
return { session, ready };
|
|
2357
2656
|
}
|
|
@@ -2389,7 +2688,7 @@ function redirectToAssistant(chatId, assistantTopicId) {
|
|
|
2389
2688
|
}
|
|
2390
2689
|
|
|
2391
2690
|
// src/adapters/telegram/activity.ts
|
|
2392
|
-
var
|
|
2691
|
+
var log10 = createChildLogger({ module: "telegram:activity" });
|
|
2393
2692
|
var THINKING_REFRESH_MS = 15e3;
|
|
2394
2693
|
var THINKING_MAX_MS = 3 * 60 * 1e3;
|
|
2395
2694
|
var ThinkingIndicator = class {
|
|
@@ -2421,7 +2720,7 @@ var ThinkingIndicator = class {
|
|
|
2421
2720
|
this.startRefreshTimer();
|
|
2422
2721
|
}
|
|
2423
2722
|
} catch (err) {
|
|
2424
|
-
|
|
2723
|
+
log10.warn({ err }, "ThinkingIndicator.show() failed");
|
|
2425
2724
|
} finally {
|
|
2426
2725
|
this.sending = false;
|
|
2427
2726
|
}
|
|
@@ -2494,7 +2793,7 @@ var UsageMessage = class {
|
|
|
2494
2793
|
if (result) this.msgId = result.message_id;
|
|
2495
2794
|
}
|
|
2496
2795
|
} catch (err) {
|
|
2497
|
-
|
|
2796
|
+
log10.warn({ err }, "UsageMessage.send() failed");
|
|
2498
2797
|
}
|
|
2499
2798
|
}
|
|
2500
2799
|
getMsgId() {
|
|
@@ -2507,7 +2806,7 @@ var UsageMessage = class {
|
|
|
2507
2806
|
try {
|
|
2508
2807
|
await this.sendQueue.enqueue(() => this.api.deleteMessage(this.chatId, id));
|
|
2509
2808
|
} catch (err) {
|
|
2510
|
-
|
|
2809
|
+
log10.warn({ err }, "UsageMessage.delete() failed");
|
|
2511
2810
|
}
|
|
2512
2811
|
}
|
|
2513
2812
|
};
|
|
@@ -2590,7 +2889,7 @@ var PlanCard = class {
|
|
|
2590
2889
|
if (result) this.msgId = result.message_id;
|
|
2591
2890
|
}
|
|
2592
2891
|
} catch (err) {
|
|
2593
|
-
|
|
2892
|
+
log10.warn({ err }, "PlanCard flush failed");
|
|
2594
2893
|
}
|
|
2595
2894
|
}
|
|
2596
2895
|
};
|
|
@@ -2653,7 +2952,7 @@ var ActivityTracker = class {
|
|
|
2653
2952
|
})
|
|
2654
2953
|
);
|
|
2655
2954
|
} catch (err) {
|
|
2656
|
-
|
|
2955
|
+
log10.warn({ err }, "ActivityTracker.onComplete() Done send failed");
|
|
2657
2956
|
}
|
|
2658
2957
|
}
|
|
2659
2958
|
}
|
|
@@ -2880,7 +3179,7 @@ function setupActionCallbacks(bot, core, chatId, getAssistantSessionId) {
|
|
|
2880
3179
|
}
|
|
2881
3180
|
|
|
2882
3181
|
// src/adapters/telegram/adapter.ts
|
|
2883
|
-
var
|
|
3182
|
+
var log11 = createChildLogger({ module: "telegram" });
|
|
2884
3183
|
function patchedFetch(input, init) {
|
|
2885
3184
|
if (init?.signal && !(init.signal instanceof AbortSignal)) {
|
|
2886
3185
|
const nativeController = new AbortController();
|
|
@@ -2931,7 +3230,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
2931
3230
|
this.bot = new Bot(this.telegramConfig.botToken, { client: { fetch: patchedFetch } });
|
|
2932
3231
|
this.bot.catch((err) => {
|
|
2933
3232
|
const rootCause = err.error instanceof Error ? err.error : err;
|
|
2934
|
-
|
|
3233
|
+
log11.error({ err: rootCause }, "Telegram bot error");
|
|
2935
3234
|
});
|
|
2936
3235
|
this.bot.api.config.use(async (prev, method, payload, signal) => {
|
|
2937
3236
|
const maxRetries = 3;
|
|
@@ -2945,7 +3244,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
2945
3244
|
if (rateLimitedMethods.includes(method)) {
|
|
2946
3245
|
this.sendQueue.onRateLimited();
|
|
2947
3246
|
}
|
|
2948
|
-
|
|
3247
|
+
log11.warn(
|
|
2949
3248
|
{ method, retryAfter, attempt: attempt + 1 },
|
|
2950
3249
|
"Rate limited by Telegram, retrying"
|
|
2951
3250
|
);
|
|
@@ -3015,7 +3314,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
3015
3314
|
this.setupRoutes();
|
|
3016
3315
|
this.bot.start({
|
|
3017
3316
|
allowed_updates: ["message", "callback_query"],
|
|
3018
|
-
onStart: () =>
|
|
3317
|
+
onStart: () => log11.info(
|
|
3019
3318
|
{ chatId: this.telegramConfig.chatId },
|
|
3020
3319
|
"Telegram bot started"
|
|
3021
3320
|
)
|
|
@@ -3037,10 +3336,10 @@ Workspace: <code>${workspace}</code>
|
|
|
3037
3336
|
reply_markup: buildMenuKeyboard()
|
|
3038
3337
|
});
|
|
3039
3338
|
} catch (err) {
|
|
3040
|
-
|
|
3339
|
+
log11.warn({ err }, "Failed to send welcome message");
|
|
3041
3340
|
}
|
|
3042
3341
|
try {
|
|
3043
|
-
|
|
3342
|
+
log11.info("Spawning assistant session...");
|
|
3044
3343
|
const { session, ready } = await spawnAssistant(
|
|
3045
3344
|
this.core,
|
|
3046
3345
|
this,
|
|
@@ -3048,13 +3347,13 @@ Workspace: <code>${workspace}</code>
|
|
|
3048
3347
|
);
|
|
3049
3348
|
this.assistantSession = session;
|
|
3050
3349
|
this.assistantInitializing = true;
|
|
3051
|
-
|
|
3350
|
+
log11.info({ sessionId: session.id }, "Assistant session ready, system prompt running in background");
|
|
3052
3351
|
ready.then(() => {
|
|
3053
3352
|
this.assistantInitializing = false;
|
|
3054
|
-
|
|
3353
|
+
log11.info({ sessionId: session.id }, "Assistant ready for user messages");
|
|
3055
3354
|
});
|
|
3056
3355
|
} catch (err) {
|
|
3057
|
-
|
|
3356
|
+
log11.error({ err }, "Failed to spawn assistant");
|
|
3058
3357
|
this.bot.api.sendMessage(
|
|
3059
3358
|
this.telegramConfig.chatId,
|
|
3060
3359
|
`\u26A0\uFE0F <b>Failed to start assistant session.</b>
|
|
@@ -3070,7 +3369,7 @@ Workspace: <code>${workspace}</code>
|
|
|
3070
3369
|
await this.assistantSession.destroy();
|
|
3071
3370
|
}
|
|
3072
3371
|
await this.bot.stop();
|
|
3073
|
-
|
|
3372
|
+
log11.info("Telegram bot stopped");
|
|
3074
3373
|
}
|
|
3075
3374
|
setupRoutes() {
|
|
3076
3375
|
this.bot.on("message:text", async (ctx) => {
|
|
@@ -3093,7 +3392,7 @@ Workspace: <code>${workspace}</code>
|
|
|
3093
3392
|
ctx.replyWithChatAction("typing").catch(() => {
|
|
3094
3393
|
});
|
|
3095
3394
|
handleAssistantMessage(this.assistantSession, ctx.message.text).catch(
|
|
3096
|
-
(err) =>
|
|
3395
|
+
(err) => log11.error({ err }, "Assistant error")
|
|
3097
3396
|
);
|
|
3098
3397
|
return;
|
|
3099
3398
|
}
|
|
@@ -3110,7 +3409,7 @@ Workspace: <code>${workspace}</code>
|
|
|
3110
3409
|
threadId: String(threadId),
|
|
3111
3410
|
userId: String(ctx.from.id),
|
|
3112
3411
|
text: ctx.message.text
|
|
3113
|
-
}).catch((err) =>
|
|
3412
|
+
}).catch((err) => log11.error({ err }, "handleMessage error"));
|
|
3114
3413
|
});
|
|
3115
3414
|
}
|
|
3116
3415
|
// --- ChannelAdapter implementations ---
|
|
@@ -3190,7 +3489,7 @@ Workspace: <code>${workspace}</code>
|
|
|
3190
3489
|
if (toolState) {
|
|
3191
3490
|
if (meta.viewerLinks) {
|
|
3192
3491
|
toolState.viewerLinks = meta.viewerLinks;
|
|
3193
|
-
|
|
3492
|
+
log11.debug({ toolId: meta.id, viewerLinks: meta.viewerLinks }, "Accumulated viewerLinks");
|
|
3194
3493
|
}
|
|
3195
3494
|
const viewerFilePath = content.metadata?.viewerFilePath;
|
|
3196
3495
|
if (viewerFilePath) toolState.viewerFilePath = viewerFilePath;
|
|
@@ -3199,7 +3498,7 @@ Workspace: <code>${workspace}</code>
|
|
|
3199
3498
|
const isTerminal = meta.status === "completed" || meta.status === "failed";
|
|
3200
3499
|
if (!isTerminal && !meta.viewerLinks) break;
|
|
3201
3500
|
await toolState.ready;
|
|
3202
|
-
|
|
3501
|
+
log11.debug(
|
|
3203
3502
|
{ toolId: meta.id, status: meta.status, hasViewerLinks: !!toolState.viewerLinks, viewerLinks: toolState.viewerLinks, name: toolState.name, msgId: toolState.msgId },
|
|
3204
3503
|
"Tool completed, preparing edit"
|
|
3205
3504
|
);
|
|
@@ -3221,7 +3520,7 @@ Workspace: <code>${workspace}</code>
|
|
|
3221
3520
|
)
|
|
3222
3521
|
);
|
|
3223
3522
|
} catch (err) {
|
|
3224
|
-
|
|
3523
|
+
log11.warn(
|
|
3225
3524
|
{ err, msgId: toolState.msgId, textLen: formattedText.length, hasViewerLinks: !!merged.viewerLinks },
|
|
3226
3525
|
"Tool update edit failed"
|
|
3227
3526
|
);
|
|
@@ -3316,17 +3615,16 @@ Task completed.
|
|
|
3316
3615
|
}
|
|
3317
3616
|
}
|
|
3318
3617
|
async sendPermissionRequest(sessionId, request) {
|
|
3319
|
-
|
|
3618
|
+
log11.info({ sessionId, requestId: request.id }, "Permission request sent");
|
|
3320
3619
|
const session = this.core.sessionManager.getSession(
|
|
3321
3620
|
sessionId
|
|
3322
3621
|
);
|
|
3323
3622
|
if (!session) return;
|
|
3324
3623
|
if (session.dangerousMode) {
|
|
3325
3624
|
const allowOption = request.options.find((o) => o.isAllow);
|
|
3326
|
-
if (allowOption && session.
|
|
3327
|
-
|
|
3328
|
-
session.
|
|
3329
|
-
session.pendingPermission = void 0;
|
|
3625
|
+
if (allowOption && session.permissionGate.requestId === request.id) {
|
|
3626
|
+
log11.info({ sessionId, requestId: request.id, optionId: allowOption.id }, "Dangerous mode: auto-approving permission");
|
|
3627
|
+
session.permissionGate.resolve(allowOption.id);
|
|
3330
3628
|
}
|
|
3331
3629
|
return;
|
|
3332
3630
|
}
|
|
@@ -3336,7 +3634,7 @@ Task completed.
|
|
|
3336
3634
|
}
|
|
3337
3635
|
async sendNotification(notification) {
|
|
3338
3636
|
if (notification.sessionId === this.assistantSession?.id) return;
|
|
3339
|
-
|
|
3637
|
+
log11.info(
|
|
3340
3638
|
{ sessionId: notification.sessionId, type: notification.type },
|
|
3341
3639
|
"Notification sent"
|
|
3342
3640
|
);
|
|
@@ -3372,7 +3670,7 @@ Task completed.
|
|
|
3372
3670
|
);
|
|
3373
3671
|
}
|
|
3374
3672
|
async createSessionThread(sessionId, name) {
|
|
3375
|
-
|
|
3673
|
+
log11.info({ sessionId, name }, "Session topic created");
|
|
3376
3674
|
return String(
|
|
3377
3675
|
await createSessionTopic(this.bot, this.telegramConfig.chatId, name)
|
|
3378
3676
|
);
|
|
@@ -3457,7 +3755,7 @@ Task completed.
|
|
|
3457
3755
|
}
|
|
3458
3756
|
);
|
|
3459
3757
|
} catch (err) {
|
|
3460
|
-
|
|
3758
|
+
log11.error({ err, sessionId }, "Failed to send skill commands");
|
|
3461
3759
|
}
|
|
3462
3760
|
await this.updateCommandAutocomplete(session.agentName, commands);
|
|
3463
3761
|
}
|
|
@@ -3493,12 +3791,12 @@ Task completed.
|
|
|
3493
3791
|
await this.bot.api.setMyCommands(all, {
|
|
3494
3792
|
scope: { type: "chat", chat_id: this.telegramConfig.chatId }
|
|
3495
3793
|
});
|
|
3496
|
-
|
|
3794
|
+
log11.info(
|
|
3497
3795
|
{ count: all.length, skills: validSkills.length },
|
|
3498
3796
|
"Updated command autocomplete"
|
|
3499
3797
|
);
|
|
3500
3798
|
} catch (err) {
|
|
3501
|
-
|
|
3799
|
+
log11.error(
|
|
3502
3800
|
{ err, commands: all },
|
|
3503
3801
|
"Failed to update command autocomplete"
|
|
3504
3802
|
);
|
|
@@ -3539,12 +3837,16 @@ export {
|
|
|
3539
3837
|
StderrCapture,
|
|
3540
3838
|
AgentInstance,
|
|
3541
3839
|
AgentManager,
|
|
3840
|
+
TypedEmitter,
|
|
3841
|
+
PromptQueue,
|
|
3842
|
+
PermissionGate,
|
|
3542
3843
|
Session,
|
|
3543
3844
|
SessionManager,
|
|
3544
3845
|
NotificationManager,
|
|
3846
|
+
MessageTransformer,
|
|
3545
3847
|
OpenACPCore,
|
|
3546
3848
|
ChannelAdapter,
|
|
3547
3849
|
ApiServer,
|
|
3548
3850
|
TelegramAdapter
|
|
3549
3851
|
};
|
|
3550
|
-
//# sourceMappingURL=chunk-
|
|
3852
|
+
//# sourceMappingURL=chunk-YD7ILGA6.js.map
|