dorkos 0.14.0 → 0.16.0
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/bin/cli.js +5 -5
- package/dist/client/assets/{TopologyGraph-B2wv0jmL.js → TopologyGraph-BdZchvqT.js} +1 -1
- package/dist/client/assets/{highlighted-body-TPN3WLV5-vQRUTiSI.js → highlighted-body-TPN3WLV5-CMKzPREW.js} +1 -1
- package/dist/client/assets/index-D938z85i.js +871 -0
- package/dist/client/assets/index-DmxrnpKh.css +1 -0
- package/dist/client/index.html +2 -2
- package/dist/server/index.js +1237 -112
- package/dist/server/index.js.map +4 -4
- package/package.json +3 -3
- package/dist/client/assets/index-21Rlo01K.js +0 -861
- package/dist/client/assets/index-D1rok_Ox.css +0 -1
package/dist/server/index.js
CHANGED
|
@@ -11744,7 +11744,7 @@ var require_multipart = __commonJS({
|
|
|
11744
11744
|
var BUF_CRLF = Buffer.from("\r\n");
|
|
11745
11745
|
var BUF_CR = Buffer.from("\r");
|
|
11746
11746
|
var BUF_DASH = Buffer.from("-");
|
|
11747
|
-
function
|
|
11747
|
+
function noop2() {
|
|
11748
11748
|
}
|
|
11749
11749
|
var MAX_HEADER_PAIRS = 2e3;
|
|
11750
11750
|
var MAX_HEADER_SIZE = 16 * 1024;
|
|
@@ -12097,7 +12097,7 @@ var require_multipart = __commonJS({
|
|
|
12097
12097
|
return;
|
|
12098
12098
|
}
|
|
12099
12099
|
const writecb = this._writecb;
|
|
12100
|
-
this._writecb =
|
|
12100
|
+
this._writecb = noop2;
|
|
12101
12101
|
ssCb(false, BUF_DASH, 0, 1, false);
|
|
12102
12102
|
this._writecb = writecb;
|
|
12103
12103
|
} else if (matchPostBoundary === 3) {
|
|
@@ -12112,7 +12112,7 @@ var require_multipart = __commonJS({
|
|
|
12112
12112
|
continue retrydata;
|
|
12113
12113
|
} else {
|
|
12114
12114
|
const writecb = this._writecb;
|
|
12115
|
-
this._writecb =
|
|
12115
|
+
this._writecb = noop2;
|
|
12116
12116
|
ssCb(false, BUF_CR, 0, 1, false);
|
|
12117
12117
|
this._writecb = writecb;
|
|
12118
12118
|
}
|
|
@@ -15321,7 +15321,7 @@ var require_end_of_stream = __commonJS({
|
|
|
15321
15321
|
callback.apply(this, args);
|
|
15322
15322
|
};
|
|
15323
15323
|
}
|
|
15324
|
-
function
|
|
15324
|
+
function noop2() {
|
|
15325
15325
|
}
|
|
15326
15326
|
function isRequest(stream) {
|
|
15327
15327
|
return stream.setHeader && typeof stream.abort === "function";
|
|
@@ -15329,7 +15329,7 @@ var require_end_of_stream = __commonJS({
|
|
|
15329
15329
|
function eos(stream, opts, callback) {
|
|
15330
15330
|
if (typeof opts === "function") return eos(stream, null, opts);
|
|
15331
15331
|
if (!opts) opts = {};
|
|
15332
|
-
callback = once(callback ||
|
|
15332
|
+
callback = once(callback || noop2);
|
|
15333
15333
|
var readable = opts.readable || opts.readable !== false && stream.readable;
|
|
15334
15334
|
var writable = opts.writable || opts.writable !== false && stream.writable;
|
|
15335
15335
|
var onlegacyfinish = function onlegacyfinish2() {
|
|
@@ -16565,7 +16565,7 @@ var require_pipeline = __commonJS({
|
|
|
16565
16565
|
var _require$codes = require_errors().codes;
|
|
16566
16566
|
var ERR_MISSING_ARGS = _require$codes.ERR_MISSING_ARGS;
|
|
16567
16567
|
var ERR_STREAM_DESTROYED = _require$codes.ERR_STREAM_DESTROYED;
|
|
16568
|
-
function
|
|
16568
|
+
function noop2(err) {
|
|
16569
16569
|
if (err) throw err;
|
|
16570
16570
|
}
|
|
16571
16571
|
function isRequest(stream) {
|
|
@@ -16603,8 +16603,8 @@ var require_pipeline = __commonJS({
|
|
|
16603
16603
|
return from.pipe(to);
|
|
16604
16604
|
}
|
|
16605
16605
|
function popCallback(streams) {
|
|
16606
|
-
if (!streams.length) return
|
|
16607
|
-
if (typeof streams[streams.length - 1] !== "function") return
|
|
16606
|
+
if (!streams.length) return noop2;
|
|
16607
|
+
if (typeof streams[streams.length - 1] !== "function") return noop2;
|
|
16608
16608
|
return streams.pop();
|
|
16609
16609
|
}
|
|
16610
16610
|
function pipeline() {
|
|
@@ -33700,7 +33700,7 @@ var require_follow_redirects = __commonJS({
|
|
|
33700
33700
|
"ERR_STREAM_WRITE_AFTER_END",
|
|
33701
33701
|
"write after end"
|
|
33702
33702
|
);
|
|
33703
|
-
var destroy = Writable.prototype.destroy ||
|
|
33703
|
+
var destroy = Writable.prototype.destroy || noop2;
|
|
33704
33704
|
function RedirectableRequest(options, responseCallback) {
|
|
33705
33705
|
Writable.call(this);
|
|
33706
33706
|
this._sanitizeOptions(options);
|
|
@@ -34029,7 +34029,7 @@ var require_follow_redirects = __commonJS({
|
|
|
34029
34029
|
});
|
|
34030
34030
|
return exports2;
|
|
34031
34031
|
}
|
|
34032
|
-
function
|
|
34032
|
+
function noop2() {
|
|
34033
34033
|
}
|
|
34034
34034
|
function parseUrl(input) {
|
|
34035
34035
|
var parsed;
|
|
@@ -34105,7 +34105,7 @@ var require_follow_redirects = __commonJS({
|
|
|
34105
34105
|
for (var event of events) {
|
|
34106
34106
|
request.removeListener(event, eventHandlers[event]);
|
|
34107
34107
|
}
|
|
34108
|
-
request.on("error",
|
|
34108
|
+
request.on("error", noop2);
|
|
34109
34109
|
request.destroy(error);
|
|
34110
34110
|
}
|
|
34111
34111
|
function isSubdomain(subdomain, domain2) {
|
|
@@ -34471,7 +34471,7 @@ var require_axios = __commonJS({
|
|
|
34471
34471
|
isArray(arrayOrString) ? define(arrayOrString) : define(String(arrayOrString).split(delimiter));
|
|
34472
34472
|
return obj;
|
|
34473
34473
|
};
|
|
34474
|
-
var
|
|
34474
|
+
var noop2 = () => {
|
|
34475
34475
|
};
|
|
34476
34476
|
var toFiniteNumber = (value, defaultValue) => {
|
|
34477
34477
|
return value != null && Number.isFinite(value = +value) ? value : defaultValue;
|
|
@@ -34577,7 +34577,7 @@ var require_axios = __commonJS({
|
|
|
34577
34577
|
freezeMethods,
|
|
34578
34578
|
toObjectSet,
|
|
34579
34579
|
toCamelCase: toCamelCase2,
|
|
34580
|
-
noop,
|
|
34580
|
+
noop: noop2,
|
|
34581
34581
|
toFiniteNumber,
|
|
34582
34582
|
findKey,
|
|
34583
34583
|
global: _global,
|
|
@@ -55755,7 +55755,7 @@ var require_websocket = __commonJS({
|
|
|
55755
55755
|
var http = __require("http");
|
|
55756
55756
|
var net = __require("net");
|
|
55757
55757
|
var tls = __require("tls");
|
|
55758
|
-
var { randomBytes, createHash: createHash3 } = __require("crypto");
|
|
55758
|
+
var { randomBytes: randomBytes2, createHash: createHash3 } = __require("crypto");
|
|
55759
55759
|
var { Duplex, Readable: Readable3 } = __require("stream");
|
|
55760
55760
|
var { URL: URL2 } = __require("url");
|
|
55761
55761
|
var PerMessageDeflate = require_permessage_deflate();
|
|
@@ -56285,7 +56285,7 @@ var require_websocket = __commonJS({
|
|
|
56285
56285
|
}
|
|
56286
56286
|
}
|
|
56287
56287
|
const defaultPort = isSecure ? 443 : 80;
|
|
56288
|
-
const key =
|
|
56288
|
+
const key = randomBytes2(16).toString("base64");
|
|
56289
56289
|
const request = isSecure ? https.request : http.request;
|
|
56290
56290
|
const protocolSet = /* @__PURE__ */ new Set();
|
|
56291
56291
|
let perMessageDeflate;
|
|
@@ -68835,9 +68835,11 @@ var StreamEventTypeSchema = z.enum([
|
|
|
68835
68835
|
"tool_call_delta",
|
|
68836
68836
|
"tool_call_end",
|
|
68837
68837
|
"tool_result",
|
|
68838
|
+
"tool_progress",
|
|
68838
68839
|
"approval_required",
|
|
68839
68840
|
"question_prompt",
|
|
68840
68841
|
"error",
|
|
68842
|
+
"rate_limit",
|
|
68841
68843
|
"done",
|
|
68842
68844
|
"session_status",
|
|
68843
68845
|
"task_update",
|
|
@@ -68845,7 +68847,18 @@ var StreamEventTypeSchema = z.enum([
|
|
|
68845
68847
|
"sync_connected",
|
|
68846
68848
|
"relay_receipt",
|
|
68847
68849
|
"message_delivered",
|
|
68848
|
-
"relay_message"
|
|
68850
|
+
"relay_message",
|
|
68851
|
+
"thinking_delta",
|
|
68852
|
+
"subagent_started",
|
|
68853
|
+
"subagent_progress",
|
|
68854
|
+
"subagent_done",
|
|
68855
|
+
"system_status",
|
|
68856
|
+
"compact_boundary",
|
|
68857
|
+
"prompt_suggestion",
|
|
68858
|
+
"hook_started",
|
|
68859
|
+
"hook_progress",
|
|
68860
|
+
"hook_response",
|
|
68861
|
+
"presence_update"
|
|
68849
68862
|
]).openapi("StreamEventType");
|
|
68850
68863
|
var QuestionOptionSchema = z.object({
|
|
68851
68864
|
label: z.string(),
|
|
@@ -68899,6 +68912,9 @@ var CommandsQuerySchema = z.object({
|
|
|
68899
68912
|
var TextDeltaSchema = z.object({
|
|
68900
68913
|
text: z.string()
|
|
68901
68914
|
}).openapi("TextDelta");
|
|
68915
|
+
var ThinkingDeltaSchema = z.object({
|
|
68916
|
+
text: z.string()
|
|
68917
|
+
}).openapi("ThinkingDelta");
|
|
68902
68918
|
var ToolCallStatusSchema = z.enum(["pending", "running", "complete", "error"]);
|
|
68903
68919
|
var ToolCallEventSchema = z.object({
|
|
68904
68920
|
toolCallId: z.string(),
|
|
@@ -68907,19 +68923,30 @@ var ToolCallEventSchema = z.object({
|
|
|
68907
68923
|
result: z.string().optional(),
|
|
68908
68924
|
status: ToolCallStatusSchema
|
|
68909
68925
|
}).openapi("ToolCallEvent");
|
|
68926
|
+
var ToolProgressEventSchema = z.object({
|
|
68927
|
+
toolCallId: z.string(),
|
|
68928
|
+
content: z.string()
|
|
68929
|
+
}).openapi("ToolProgressEvent");
|
|
68910
68930
|
var ApprovalEventSchema = z.object({
|
|
68911
68931
|
toolCallId: z.string(),
|
|
68912
68932
|
toolName: z.string(),
|
|
68913
|
-
input: z.string()
|
|
68933
|
+
input: z.string(),
|
|
68934
|
+
timeoutMs: z.number().describe("Server-side approval timeout in milliseconds")
|
|
68914
68935
|
}).openapi("ApprovalEvent");
|
|
68915
68936
|
var QuestionPromptEventSchema = z.object({
|
|
68916
68937
|
toolCallId: z.string(),
|
|
68917
68938
|
questions: z.array(QuestionItemSchema)
|
|
68918
68939
|
}).openapi("QuestionPromptEvent");
|
|
68940
|
+
var ErrorCategorySchema = z.enum(["max_turns", "execution_error", "budget_exceeded", "output_format_error"]).openapi("ErrorCategory");
|
|
68919
68941
|
var ErrorEventSchema = z.object({
|
|
68920
68942
|
message: z.string(),
|
|
68921
|
-
code: z.string().optional()
|
|
68943
|
+
code: z.string().optional(),
|
|
68944
|
+
category: ErrorCategorySchema.optional(),
|
|
68945
|
+
details: z.string().optional()
|
|
68922
68946
|
}).openapi("ErrorEvent");
|
|
68947
|
+
var RateLimitEventSchema = z.object({
|
|
68948
|
+
retryAfter: z.number().optional()
|
|
68949
|
+
}).openapi("RateLimitEvent");
|
|
68923
68950
|
var DoneEventSchema = z.object({
|
|
68924
68951
|
sessionId: z.string()
|
|
68925
68952
|
}).openapi("DoneEvent");
|
|
@@ -68928,7 +68955,8 @@ var SessionStatusEventSchema = z.object({
|
|
|
68928
68955
|
model: z.string().optional(),
|
|
68929
68956
|
costUsd: z.number().optional(),
|
|
68930
68957
|
contextTokens: z.number().int().optional(),
|
|
68931
|
-
contextMaxTokens: z.number().int().optional()
|
|
68958
|
+
contextMaxTokens: z.number().int().optional(),
|
|
68959
|
+
outputTokens: z.number().int().optional()
|
|
68932
68960
|
}).openapi("SessionStatusEvent");
|
|
68933
68961
|
var TaskItemSchema = z.object({
|
|
68934
68962
|
id: z.string(),
|
|
@@ -68966,14 +68994,75 @@ var RelayMessageEventSchema = z.object({
|
|
|
68966
68994
|
subject: z.string().optional(),
|
|
68967
68995
|
from: z.string().optional()
|
|
68968
68996
|
}).openapi("RelayMessageEvent");
|
|
68997
|
+
var SubagentStartedEventSchema = z.object({
|
|
68998
|
+
taskId: z.string(),
|
|
68999
|
+
subagentSessionId: z.string(),
|
|
69000
|
+
toolUseId: z.string().optional(),
|
|
69001
|
+
description: z.string()
|
|
69002
|
+
}).openapi("SubagentStartedEvent");
|
|
69003
|
+
var SubagentProgressEventSchema = z.object({
|
|
69004
|
+
taskId: z.string(),
|
|
69005
|
+
toolUses: z.number().int(),
|
|
69006
|
+
lastToolName: z.string().optional(),
|
|
69007
|
+
durationMs: z.number().int()
|
|
69008
|
+
}).openapi("SubagentProgressEvent");
|
|
69009
|
+
var SubagentDoneEventSchema = z.object({
|
|
69010
|
+
taskId: z.string(),
|
|
69011
|
+
status: z.enum(["completed", "failed", "stopped"]),
|
|
69012
|
+
summary: z.string().optional(),
|
|
69013
|
+
toolUses: z.number().int().optional(),
|
|
69014
|
+
durationMs: z.number().int().optional()
|
|
69015
|
+
}).openapi("SubagentDoneEvent");
|
|
69016
|
+
var SystemStatusEventSchema = z.object({
|
|
69017
|
+
message: z.string()
|
|
69018
|
+
}).openapi("SystemStatusEvent");
|
|
69019
|
+
var CompactBoundaryEventSchema = z.object({}).openapi("CompactBoundaryEvent");
|
|
69020
|
+
var PromptSuggestionEventSchema = z.object({
|
|
69021
|
+
suggestions: z.array(z.string())
|
|
69022
|
+
}).openapi("PromptSuggestionEvent");
|
|
69023
|
+
var HookStartedEventSchema = z.object({
|
|
69024
|
+
hookId: z.string(),
|
|
69025
|
+
hookName: z.string(),
|
|
69026
|
+
hookEvent: z.string(),
|
|
69027
|
+
toolCallId: z.string().nullable()
|
|
69028
|
+
}).openapi("HookStartedEvent");
|
|
69029
|
+
var HookProgressEventSchema = z.object({
|
|
69030
|
+
hookId: z.string(),
|
|
69031
|
+
stdout: z.string(),
|
|
69032
|
+
stderr: z.string()
|
|
69033
|
+
}).openapi("HookProgressEvent");
|
|
69034
|
+
var HookResponseEventSchema = z.object({
|
|
69035
|
+
hookId: z.string(),
|
|
69036
|
+
hookName: z.string(),
|
|
69037
|
+
exitCode: z.number().optional(),
|
|
69038
|
+
outcome: z.enum(["success", "error", "cancelled"]),
|
|
69039
|
+
stdout: z.string(),
|
|
69040
|
+
stderr: z.string()
|
|
69041
|
+
}).openapi("HookResponseEvent");
|
|
69042
|
+
var PresenceClientSchema = z.object({
|
|
69043
|
+
type: z.enum(["web", "obsidian", "mcp", "unknown"]),
|
|
69044
|
+
connectedAt: z.string()
|
|
69045
|
+
});
|
|
69046
|
+
var PresenceUpdateEventSchema = z.object({
|
|
69047
|
+
sessionId: z.string(),
|
|
69048
|
+
clientCount: z.number().int(),
|
|
69049
|
+
clients: z.array(PresenceClientSchema),
|
|
69050
|
+
lockInfo: z.object({
|
|
69051
|
+
clientId: z.string(),
|
|
69052
|
+
acquiredAt: z.string()
|
|
69053
|
+
}).nullable()
|
|
69054
|
+
}).openapi("PresenceUpdateEvent");
|
|
68969
69055
|
var StreamEventSchema = z.object({
|
|
68970
69056
|
type: StreamEventTypeSchema,
|
|
68971
69057
|
data: z.union([
|
|
68972
69058
|
TextDeltaSchema,
|
|
69059
|
+
ThinkingDeltaSchema,
|
|
68973
69060
|
ToolCallEventSchema,
|
|
69061
|
+
ToolProgressEventSchema,
|
|
68974
69062
|
ApprovalEventSchema,
|
|
68975
69063
|
QuestionPromptEventSchema,
|
|
68976
69064
|
ErrorEventSchema,
|
|
69065
|
+
RateLimitEventSchema,
|
|
68977
69066
|
DoneEventSchema,
|
|
68978
69067
|
SessionStatusEventSchema,
|
|
68979
69068
|
TaskUpdateEventSchema,
|
|
@@ -68981,31 +69070,84 @@ var StreamEventSchema = z.object({
|
|
|
68981
69070
|
SyncConnectedEventSchema,
|
|
68982
69071
|
RelayReceiptEventSchema,
|
|
68983
69072
|
MessageDeliveredEventSchema,
|
|
68984
|
-
RelayMessageEventSchema
|
|
69073
|
+
RelayMessageEventSchema,
|
|
69074
|
+
SubagentStartedEventSchema,
|
|
69075
|
+
SubagentProgressEventSchema,
|
|
69076
|
+
SubagentDoneEventSchema,
|
|
69077
|
+
SystemStatusEventSchema,
|
|
69078
|
+
CompactBoundaryEventSchema,
|
|
69079
|
+
PromptSuggestionEventSchema,
|
|
69080
|
+
HookStartedEventSchema,
|
|
69081
|
+
HookProgressEventSchema,
|
|
69082
|
+
HookResponseEventSchema,
|
|
69083
|
+
PresenceUpdateEventSchema
|
|
68985
69084
|
])
|
|
68986
69085
|
}).openapi("StreamEvent");
|
|
68987
69086
|
var TextPartSchema = z.object({
|
|
68988
69087
|
type: z.literal("text"),
|
|
68989
69088
|
text: z.string()
|
|
68990
69089
|
}).openapi("TextPart");
|
|
69090
|
+
var HookStatusSchema = z.enum(["running", "success", "error", "cancelled"]);
|
|
69091
|
+
var HookPartSchema = z.object({
|
|
69092
|
+
hookId: z.string(),
|
|
69093
|
+
hookName: z.string(),
|
|
69094
|
+
hookEvent: z.string(),
|
|
69095
|
+
status: HookStatusSchema,
|
|
69096
|
+
stdout: z.string(),
|
|
69097
|
+
stderr: z.string(),
|
|
69098
|
+
exitCode: z.number().optional()
|
|
69099
|
+
});
|
|
68991
69100
|
var ToolCallPartSchema = z.object({
|
|
68992
69101
|
type: z.literal("tool_call"),
|
|
68993
69102
|
toolCallId: z.string(),
|
|
68994
69103
|
toolName: z.string(),
|
|
68995
69104
|
input: z.string().optional(),
|
|
68996
69105
|
result: z.string().optional(),
|
|
69106
|
+
progressOutput: z.string().optional(),
|
|
68997
69107
|
status: ToolCallStatusSchema,
|
|
68998
69108
|
interactiveType: z.enum(["approval", "question"]).optional(),
|
|
68999
69109
|
questions: z.array(QuestionItemSchema).optional(),
|
|
69000
|
-
answers: z.record(z.string(), z.string()).optional()
|
|
69110
|
+
answers: z.record(z.string(), z.string()).optional(),
|
|
69111
|
+
timeoutMs: z.number().optional().describe("Approval timeout duration in milliseconds"),
|
|
69112
|
+
hooks: z.array(HookPartSchema).optional()
|
|
69001
69113
|
}).openapi("ToolCallPart");
|
|
69002
|
-
var
|
|
69114
|
+
var SubagentStatusSchema = z.enum(["running", "complete", "error"]);
|
|
69115
|
+
var SubagentPartSchema = z.object({
|
|
69116
|
+
type: z.literal("subagent"),
|
|
69117
|
+
taskId: z.string(),
|
|
69118
|
+
description: z.string(),
|
|
69119
|
+
status: SubagentStatusSchema,
|
|
69120
|
+
toolUses: z.number().int().optional(),
|
|
69121
|
+
lastToolName: z.string().optional(),
|
|
69122
|
+
durationMs: z.number().int().optional(),
|
|
69123
|
+
summary: z.string().optional()
|
|
69124
|
+
}).openapi("SubagentPart");
|
|
69125
|
+
var ThinkingPartSchema = z.object({
|
|
69126
|
+
type: z.literal("thinking"),
|
|
69127
|
+
text: z.string(),
|
|
69128
|
+
isStreaming: z.boolean().optional(),
|
|
69129
|
+
elapsedMs: z.number().int().optional()
|
|
69130
|
+
}).openapi("ThinkingPart");
|
|
69131
|
+
var ErrorPartSchema = z.object({
|
|
69132
|
+
type: z.literal("error"),
|
|
69133
|
+
message: z.string(),
|
|
69134
|
+
category: ErrorCategorySchema.optional(),
|
|
69135
|
+
details: z.string().optional()
|
|
69136
|
+
}).openapi("ErrorPart");
|
|
69137
|
+
var MessagePartSchema = z.discriminatedUnion("type", [
|
|
69138
|
+
TextPartSchema,
|
|
69139
|
+
ToolCallPartSchema,
|
|
69140
|
+
SubagentPartSchema,
|
|
69141
|
+
ThinkingPartSchema,
|
|
69142
|
+
ErrorPartSchema
|
|
69143
|
+
]);
|
|
69003
69144
|
var MessageTypeSchema = z.enum(["command", "compaction"]).openapi("MessageType");
|
|
69004
69145
|
var HistoryToolCallSchema = z.object({
|
|
69005
69146
|
toolCallId: z.string(),
|
|
69006
69147
|
toolName: z.string(),
|
|
69007
69148
|
input: z.string().optional(),
|
|
69008
69149
|
result: z.string().optional(),
|
|
69150
|
+
progressOutput: z.string().optional(),
|
|
69009
69151
|
status: z.literal("complete"),
|
|
69010
69152
|
questions: z.array(QuestionItemSchema).optional(),
|
|
69011
69153
|
answers: z.record(z.string(), z.string()).optional()
|
|
@@ -70918,7 +71060,7 @@ var SERVER_VERSION = resolveVersion();
|
|
|
70918
71060
|
var IS_DEV_BUILD = checkDevBuild(SERVER_VERSION);
|
|
70919
71061
|
function resolveVersion() {
|
|
70920
71062
|
if (env.DORKOS_VERSION_OVERRIDE) return env.DORKOS_VERSION_OVERRIDE;
|
|
70921
|
-
if (true) return "0.
|
|
71063
|
+
if (true) return "0.16.0";
|
|
70922
71064
|
const pkgPath = path4.join(path4.dirname(fileURLToPath2(import.meta.url)), "../../package.json");
|
|
70923
71065
|
return JSON.parse(readFileSync(pkgPath, "utf-8")).version;
|
|
70924
71066
|
}
|
|
@@ -72128,7 +72270,8 @@ var TelegramAdapterConfigSchema = z11.object({
|
|
|
72128
72270
|
mode: z11.enum(["polling", "webhook"]).default("polling"),
|
|
72129
72271
|
webhookUrl: z11.string().url().optional(),
|
|
72130
72272
|
webhookPort: z11.number().int().positive().optional(),
|
|
72131
|
-
webhookSecret: z11.string().min(1).optional()
|
|
72273
|
+
webhookSecret: z11.string().min(1).optional(),
|
|
72274
|
+
streaming: z11.boolean().default(true)
|
|
72132
72275
|
}).openapi("TelegramAdapterConfig");
|
|
72133
72276
|
var WebhookInboundConfigSchema = z11.object({
|
|
72134
72277
|
subject: z11.string().min(1),
|
|
@@ -72149,6 +72292,7 @@ var SlackAdapterConfigSchema = z11.object({
|
|
|
72149
72292
|
appToken: z11.string().min(1),
|
|
72150
72293
|
signingSecret: z11.string().min(1),
|
|
72151
72294
|
streaming: z11.boolean().default(true),
|
|
72295
|
+
nativeStreaming: z11.boolean().default(true),
|
|
72152
72296
|
typingIndicator: z11.enum(["none", "reaction"]).default("none")
|
|
72153
72297
|
}).openapi("SlackAdapterConfig");
|
|
72154
72298
|
var AdapterConfigSchema = z11.object({
|
|
@@ -74206,7 +74350,9 @@ function parseTranscript(lines) {
|
|
|
74206
74350
|
const parts = [];
|
|
74207
74351
|
const toolCalls = [];
|
|
74208
74352
|
for (const block of contentBlocks) {
|
|
74209
|
-
if (block.type === "
|
|
74353
|
+
if (block.type === "thinking" && block.thinking) {
|
|
74354
|
+
parts.push({ type: "thinking", text: block.thinking, isStreaming: false });
|
|
74355
|
+
} else if (block.type === "text" && block.text) {
|
|
74210
74356
|
const lastPart = parts[parts.length - 1];
|
|
74211
74357
|
if (lastPart && lastPart.type === "text") {
|
|
74212
74358
|
lastPart.text += "\n" + block.text;
|
|
@@ -76292,9 +76438,16 @@ var esm_default = { watch, FSWatcher };
|
|
|
76292
76438
|
|
|
76293
76439
|
// ../../apps/server/src/services/runtimes/claude-code/session-broadcaster.ts
|
|
76294
76440
|
import { join as join3 } from "path";
|
|
76441
|
+
function inferClientType(clientId) {
|
|
76442
|
+
if (clientId.startsWith("web-")) return "web";
|
|
76443
|
+
if (clientId.startsWith("obsidian-")) return "obsidian";
|
|
76444
|
+
if (clientId.startsWith("mcp-")) return "mcp";
|
|
76445
|
+
return "unknown";
|
|
76446
|
+
}
|
|
76295
76447
|
var SessionBroadcaster = class {
|
|
76296
|
-
constructor(transcriptReader) {
|
|
76448
|
+
constructor(transcriptReader, lockManager) {
|
|
76297
76449
|
this.transcriptReader = transcriptReader;
|
|
76450
|
+
this.lockManager = lockManager;
|
|
76298
76451
|
}
|
|
76299
76452
|
clients = /* @__PURE__ */ new Map();
|
|
76300
76453
|
callbacks = /* @__PURE__ */ new Map();
|
|
@@ -76317,18 +76470,19 @@ var SessionBroadcaster = class {
|
|
|
76317
76470
|
/**
|
|
76318
76471
|
* Register an SSE client for a session.
|
|
76319
76472
|
*
|
|
76320
|
-
* - Adds the response to the
|
|
76473
|
+
* - Adds the response to the client metadata map
|
|
76321
76474
|
* - Starts a file watcher if none exists for this session
|
|
76322
76475
|
* - Initializes offset to current file size (only broadcast new content)
|
|
76323
76476
|
* - Sends sync_connected event to the client
|
|
76324
76477
|
* - Auto-deregisters on response close
|
|
76478
|
+
* - Broadcasts presence update to all clients for this session
|
|
76325
76479
|
*
|
|
76326
76480
|
* @param sessionId - Session UUID
|
|
76327
76481
|
* @param vaultRoot - Vault root path for resolving transcript directory
|
|
76328
76482
|
* @param res - Express Response object configured for SSE
|
|
76329
|
-
* @param clientId - Optional client identifier
|
|
76483
|
+
* @param clientId - Optional client identifier (used for type inference via prefix convention)
|
|
76330
76484
|
*/
|
|
76331
|
-
registerClient(sessionId, vaultRoot2, res,
|
|
76485
|
+
registerClient(sessionId, vaultRoot2, res, clientId) {
|
|
76332
76486
|
if (this.totalClientCount >= SSE.MAX_TOTAL_CLIENTS) {
|
|
76333
76487
|
res.status(503).json({ error: "SSE connection limit reached", code: "SSE_LIMIT" });
|
|
76334
76488
|
return;
|
|
@@ -76338,10 +76492,17 @@ var SessionBroadcaster = class {
|
|
|
76338
76492
|
res.status(503).json({ error: "Too many connections for this session", code: "SSE_SESSION_LIMIT" });
|
|
76339
76493
|
return;
|
|
76340
76494
|
}
|
|
76495
|
+
const resolvedClientId = clientId ?? `unknown-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
76341
76496
|
if (!this.clients.has(sessionId)) {
|
|
76342
|
-
this.clients.set(sessionId, /* @__PURE__ */ new
|
|
76497
|
+
this.clients.set(sessionId, /* @__PURE__ */ new Map());
|
|
76343
76498
|
}
|
|
76344
|
-
|
|
76499
|
+
const client = {
|
|
76500
|
+
res,
|
|
76501
|
+
clientId: resolvedClientId,
|
|
76502
|
+
clientType: inferClientType(resolvedClientId),
|
|
76503
|
+
connectedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
76504
|
+
};
|
|
76505
|
+
this.clients.get(sessionId).set(resolvedClientId, client);
|
|
76345
76506
|
this.totalClientCount++;
|
|
76346
76507
|
if (!this.watchers.has(sessionId)) {
|
|
76347
76508
|
this.startWatcher(sessionId, vaultRoot2);
|
|
@@ -76353,6 +76514,7 @@ data: ${JSON.stringify({ sessionId })}
|
|
|
76353
76514
|
res.on("close", () => {
|
|
76354
76515
|
this.deregisterClient(sessionId, res);
|
|
76355
76516
|
});
|
|
76517
|
+
this.broadcastPresence(sessionId);
|
|
76356
76518
|
}
|
|
76357
76519
|
/**
|
|
76358
76520
|
* Register a callback-based listener for session changes.
|
|
@@ -76396,7 +76558,8 @@ data: ${JSON.stringify({ sessionId })}
|
|
|
76396
76558
|
/**
|
|
76397
76559
|
* Deregister an SSE client from a session.
|
|
76398
76560
|
*
|
|
76399
|
-
* -
|
|
76561
|
+
* - Finds and removes the client by response reference
|
|
76562
|
+
* - Broadcasts updated presence to remaining clients
|
|
76400
76563
|
* - Stops the file watcher if no clients remain for this session
|
|
76401
76564
|
* - Cleans up offsets and timers
|
|
76402
76565
|
*
|
|
@@ -76404,13 +76567,22 @@ data: ${JSON.stringify({ sessionId })}
|
|
|
76404
76567
|
* @param res - Express Response object to remove
|
|
76405
76568
|
*/
|
|
76406
76569
|
deregisterClient(sessionId, res) {
|
|
76407
|
-
const
|
|
76408
|
-
if (!
|
|
76409
|
-
|
|
76410
|
-
|
|
76570
|
+
const clientMap = this.clients.get(sessionId);
|
|
76571
|
+
if (!clientMap) return;
|
|
76572
|
+
let removed = false;
|
|
76573
|
+
for (const [id, client] of clientMap) {
|
|
76574
|
+
if (client.res === res) {
|
|
76575
|
+
clientMap.delete(id);
|
|
76576
|
+
this.totalClientCount--;
|
|
76577
|
+
removed = true;
|
|
76578
|
+
break;
|
|
76579
|
+
}
|
|
76580
|
+
}
|
|
76581
|
+
if (!removed) return;
|
|
76582
|
+
if (clientMap.size > 0) {
|
|
76583
|
+
this.broadcastPresence(sessionId);
|
|
76411
76584
|
}
|
|
76412
|
-
|
|
76413
|
-
if (clientSet.size === 0) {
|
|
76585
|
+
if (clientMap.size === 0) {
|
|
76414
76586
|
this.clients.delete(sessionId);
|
|
76415
76587
|
const hasCallbacks = Array.from(this.callbacks.values()).some(
|
|
76416
76588
|
(entry) => entry.sessionId === sessionId
|
|
@@ -76431,6 +76603,31 @@ data: ${JSON.stringify({ sessionId })}
|
|
|
76431
76603
|
}
|
|
76432
76604
|
}
|
|
76433
76605
|
}
|
|
76606
|
+
/**
|
|
76607
|
+
* Get current presence info for a session.
|
|
76608
|
+
*
|
|
76609
|
+
* @param sessionId - Session UUID
|
|
76610
|
+
* @returns Presence info with client count, client metadata, and lock state, or null if no clients
|
|
76611
|
+
*/
|
|
76612
|
+
getPresenceInfo(sessionId) {
|
|
76613
|
+
const clientMap = this.clients.get(sessionId);
|
|
76614
|
+
if (!clientMap || clientMap.size === 0) return null;
|
|
76615
|
+
const clients = Array.from(clientMap.values()).map((c3) => ({
|
|
76616
|
+
type: c3.clientType,
|
|
76617
|
+
connectedAt: c3.connectedAt
|
|
76618
|
+
}));
|
|
76619
|
+
let lockInfo = null;
|
|
76620
|
+
if (this.lockManager) {
|
|
76621
|
+
const lock = this.lockManager.getLockInfo(sessionId);
|
|
76622
|
+
if (lock) {
|
|
76623
|
+
lockInfo = {
|
|
76624
|
+
clientId: lock.clientId,
|
|
76625
|
+
acquiredAt: new Date(lock.acquiredAt).toISOString()
|
|
76626
|
+
};
|
|
76627
|
+
}
|
|
76628
|
+
}
|
|
76629
|
+
return { clientCount: clientMap.size, clients, lockInfo };
|
|
76630
|
+
}
|
|
76434
76631
|
/**
|
|
76435
76632
|
* Start a chokidar file watcher for a session's JSONL transcript.
|
|
76436
76633
|
*
|
|
@@ -76511,11 +76708,11 @@ data: ${JSON.stringify({ sessionId })}
|
|
|
76511
76708
|
if (content3.length === 0) {
|
|
76512
76709
|
return;
|
|
76513
76710
|
}
|
|
76514
|
-
const
|
|
76711
|
+
const clientMap = this.clients.get(sessionId);
|
|
76515
76712
|
const hasCallbacks = Array.from(this.callbacks.values()).some(
|
|
76516
76713
|
(entry) => entry.sessionId === sessionId
|
|
76517
76714
|
);
|
|
76518
|
-
if ((!
|
|
76715
|
+
if ((!clientMap || clientMap.size === 0) && !hasCallbacks) {
|
|
76519
76716
|
return;
|
|
76520
76717
|
}
|
|
76521
76718
|
const event = {
|
|
@@ -76526,12 +76723,12 @@ data: ${JSON.stringify({ sessionId })}
|
|
|
76526
76723
|
data: ${JSON.stringify(event)}
|
|
76527
76724
|
|
|
76528
76725
|
`;
|
|
76529
|
-
if (
|
|
76530
|
-
for (const
|
|
76726
|
+
if (clientMap) {
|
|
76727
|
+
for (const connectedClient of Array.from(clientMap.values())) {
|
|
76531
76728
|
try {
|
|
76532
|
-
const ok3 =
|
|
76729
|
+
const ok3 = connectedClient.res.write(eventData);
|
|
76533
76730
|
if (!ok3) {
|
|
76534
|
-
await new Promise((resolve4) =>
|
|
76731
|
+
await new Promise((resolve4) => connectedClient.res.once("drain", resolve4));
|
|
76535
76732
|
}
|
|
76536
76733
|
} catch (err) {
|
|
76537
76734
|
logger.error(
|
|
@@ -76560,6 +76757,60 @@ data: ${JSON.stringify(event)}
|
|
|
76560
76757
|
logger.error(`[SessionBroadcaster] Failed to read offset for session ${sessionId}:`, err);
|
|
76561
76758
|
}
|
|
76562
76759
|
}
|
|
76760
|
+
/** Broadcast a presence_update SSE event to all connected clients for a session. */
|
|
76761
|
+
broadcastPresence(sessionId) {
|
|
76762
|
+
const clientMap = this.clients.get(sessionId);
|
|
76763
|
+
if (!clientMap || clientMap.size === 0) return;
|
|
76764
|
+
const clients = Array.from(clientMap.values()).map((c3) => ({
|
|
76765
|
+
type: c3.clientType,
|
|
76766
|
+
connectedAt: c3.connectedAt
|
|
76767
|
+
}));
|
|
76768
|
+
let lockInfo = null;
|
|
76769
|
+
if (this.lockManager) {
|
|
76770
|
+
const lock = this.lockManager.getLockInfo(sessionId);
|
|
76771
|
+
if (lock) {
|
|
76772
|
+
lockInfo = {
|
|
76773
|
+
clientId: lock.clientId,
|
|
76774
|
+
acquiredAt: new Date(lock.acquiredAt).toISOString()
|
|
76775
|
+
};
|
|
76776
|
+
}
|
|
76777
|
+
}
|
|
76778
|
+
const presenceData = {
|
|
76779
|
+
sessionId,
|
|
76780
|
+
clientCount: clientMap.size,
|
|
76781
|
+
clients,
|
|
76782
|
+
lockInfo
|
|
76783
|
+
};
|
|
76784
|
+
const eventData = `event: presence_update
|
|
76785
|
+
data: ${JSON.stringify(presenceData)}
|
|
76786
|
+
|
|
76787
|
+
`;
|
|
76788
|
+
for (const client of clientMap.values()) {
|
|
76789
|
+
try {
|
|
76790
|
+
client.res.write(eventData);
|
|
76791
|
+
} catch (err) {
|
|
76792
|
+
logger.error(
|
|
76793
|
+
`[SessionBroadcaster] Failed to write presence to client ${client.clientId}:`,
|
|
76794
|
+
err
|
|
76795
|
+
);
|
|
76796
|
+
}
|
|
76797
|
+
}
|
|
76798
|
+
for (const [, entry] of this.callbacks) {
|
|
76799
|
+
if (entry.sessionId === sessionId) {
|
|
76800
|
+
try {
|
|
76801
|
+
entry.callback({
|
|
76802
|
+
type: "presence_update",
|
|
76803
|
+
data: presenceData
|
|
76804
|
+
});
|
|
76805
|
+
} catch (err) {
|
|
76806
|
+
logger.error(
|
|
76807
|
+
`[SessionBroadcaster] Presence callback error for session ${sessionId}:`,
|
|
76808
|
+
err
|
|
76809
|
+
);
|
|
76810
|
+
}
|
|
76811
|
+
}
|
|
76812
|
+
}
|
|
76813
|
+
}
|
|
76563
76814
|
/**
|
|
76564
76815
|
* Shutdown the broadcaster, closing all watchers and client connections.
|
|
76565
76816
|
* Should be called on server shutdown.
|
|
@@ -76574,10 +76825,10 @@ data: ${JSON.stringify(event)}
|
|
|
76574
76825
|
});
|
|
76575
76826
|
this.watchers.clear();
|
|
76576
76827
|
this.callbacks.clear();
|
|
76577
|
-
Array.from(this.clients.values()).forEach((
|
|
76578
|
-
Array.from(
|
|
76828
|
+
Array.from(this.clients.values()).forEach((clientMap) => {
|
|
76829
|
+
Array.from(clientMap.values()).forEach((client) => {
|
|
76579
76830
|
try {
|
|
76580
|
-
client.end();
|
|
76831
|
+
client.res.end();
|
|
76581
76832
|
} catch {
|
|
76582
76833
|
}
|
|
76583
76834
|
});
|
|
@@ -76708,6 +76959,9 @@ function createToolState() {
|
|
|
76708
76959
|
let currentToolName = "";
|
|
76709
76960
|
let currentToolId = "";
|
|
76710
76961
|
let taskToolInput = "";
|
|
76962
|
+
let inThinking = false;
|
|
76963
|
+
let thinkingStartMs = 0;
|
|
76964
|
+
const toolNameById = /* @__PURE__ */ new Map();
|
|
76711
76965
|
return {
|
|
76712
76966
|
get inTool() {
|
|
76713
76967
|
return inTool;
|
|
@@ -76721,6 +76975,19 @@ function createToolState() {
|
|
|
76721
76975
|
get taskToolInput() {
|
|
76722
76976
|
return taskToolInput;
|
|
76723
76977
|
},
|
|
76978
|
+
get inThinking() {
|
|
76979
|
+
return inThinking;
|
|
76980
|
+
},
|
|
76981
|
+
set inThinking(v3) {
|
|
76982
|
+
inThinking = v3;
|
|
76983
|
+
},
|
|
76984
|
+
get thinkingStartMs() {
|
|
76985
|
+
return thinkingStartMs;
|
|
76986
|
+
},
|
|
76987
|
+
set thinkingStartMs(v3) {
|
|
76988
|
+
thinkingStartMs = v3;
|
|
76989
|
+
},
|
|
76990
|
+
toolNameById,
|
|
76724
76991
|
appendTaskInput: (chunk) => {
|
|
76725
76992
|
taskToolInput += chunk;
|
|
76726
76993
|
},
|
|
@@ -76816,7 +77083,8 @@ function handleToolApproval(session, toolUseId, toolName, input) {
|
|
|
76816
77083
|
data: {
|
|
76817
77084
|
toolCallId: toolUseId,
|
|
76818
77085
|
toolName,
|
|
76819
|
-
input: JSON.stringify(input)
|
|
77086
|
+
input: JSON.stringify(input),
|
|
77087
|
+
timeoutMs: SESSIONS.INTERACTION_TIMEOUT_MS
|
|
76820
77088
|
}
|
|
76821
77089
|
});
|
|
76822
77090
|
session.eventQueueNotify?.();
|
|
@@ -76879,6 +77147,25 @@ function buildTaskEvent(toolName, input) {
|
|
|
76879
77147
|
}
|
|
76880
77148
|
|
|
76881
77149
|
// ../../apps/server/src/services/runtimes/claude-code/sdk-event-mapper.ts
|
|
77150
|
+
var TOOL_CONTEXTUAL_HOOK_EVENTS = /* @__PURE__ */ new Set([
|
|
77151
|
+
"PreToolUse",
|
|
77152
|
+
"PostToolUse",
|
|
77153
|
+
"PostToolUseFailure"
|
|
77154
|
+
]);
|
|
77155
|
+
function mapErrorCategory(subtype) {
|
|
77156
|
+
switch (subtype) {
|
|
77157
|
+
case "error_max_turns":
|
|
77158
|
+
return "max_turns";
|
|
77159
|
+
case "error_during_execution":
|
|
77160
|
+
return "execution_error";
|
|
77161
|
+
case "error_max_budget_usd":
|
|
77162
|
+
return "budget_exceeded";
|
|
77163
|
+
case "error_max_structured_output_retries":
|
|
77164
|
+
return "output_format_error";
|
|
77165
|
+
default:
|
|
77166
|
+
return "execution_error";
|
|
77167
|
+
}
|
|
77168
|
+
}
|
|
76882
77169
|
async function* mapSdkMessage(message, session, sessionId, toolState) {
|
|
76883
77170
|
if (message.type === "system" && "subtype" in message && message.subtype === "init") {
|
|
76884
77171
|
session.sdkSessionId = message.session_id;
|
|
@@ -76892,14 +77179,147 @@ async function* mapSdkMessage(message, session, sessionId, toolState) {
|
|
|
76892
77179
|
}
|
|
76893
77180
|
return;
|
|
76894
77181
|
}
|
|
77182
|
+
if (message.type === "system" && "subtype" in message) {
|
|
77183
|
+
if (message.subtype === "task_started") {
|
|
77184
|
+
const msg = message;
|
|
77185
|
+
yield {
|
|
77186
|
+
type: "subagent_started",
|
|
77187
|
+
data: {
|
|
77188
|
+
taskId: msg.task_id,
|
|
77189
|
+
subagentSessionId: message.session_id,
|
|
77190
|
+
toolUseId: msg.tool_use_id,
|
|
77191
|
+
description: msg.description
|
|
77192
|
+
}
|
|
77193
|
+
};
|
|
77194
|
+
return;
|
|
77195
|
+
}
|
|
77196
|
+
if (message.subtype === "task_progress") {
|
|
77197
|
+
const msg = message;
|
|
77198
|
+
const usage = msg.usage;
|
|
77199
|
+
yield {
|
|
77200
|
+
type: "subagent_progress",
|
|
77201
|
+
data: {
|
|
77202
|
+
taskId: msg.task_id,
|
|
77203
|
+
toolUses: usage.tool_uses,
|
|
77204
|
+
lastToolName: msg.last_tool_name,
|
|
77205
|
+
durationMs: usage.duration_ms
|
|
77206
|
+
}
|
|
77207
|
+
};
|
|
77208
|
+
return;
|
|
77209
|
+
}
|
|
77210
|
+
if (message.subtype === "task_notification") {
|
|
77211
|
+
const msg = message;
|
|
77212
|
+
const usage = msg.usage;
|
|
77213
|
+
yield {
|
|
77214
|
+
type: "subagent_done",
|
|
77215
|
+
data: {
|
|
77216
|
+
taskId: msg.task_id,
|
|
77217
|
+
status: msg.status,
|
|
77218
|
+
summary: msg.summary,
|
|
77219
|
+
toolUses: usage?.tool_uses,
|
|
77220
|
+
durationMs: usage?.duration_ms
|
|
77221
|
+
}
|
|
77222
|
+
};
|
|
77223
|
+
return;
|
|
77224
|
+
}
|
|
77225
|
+
if (message.subtype === "status") {
|
|
77226
|
+
const msg = message;
|
|
77227
|
+
const text6 = msg.body ?? msg.message ?? "";
|
|
77228
|
+
if (text6) {
|
|
77229
|
+
yield {
|
|
77230
|
+
type: "system_status",
|
|
77231
|
+
data: { message: text6 }
|
|
77232
|
+
};
|
|
77233
|
+
}
|
|
77234
|
+
return;
|
|
77235
|
+
}
|
|
77236
|
+
if (message.subtype === "compact_boundary") {
|
|
77237
|
+
yield {
|
|
77238
|
+
type: "compact_boundary",
|
|
77239
|
+
data: {}
|
|
77240
|
+
};
|
|
77241
|
+
return;
|
|
77242
|
+
}
|
|
77243
|
+
if (message.subtype === "hook_started") {
|
|
77244
|
+
const msg = message;
|
|
77245
|
+
const hookEvent = msg.hook_event;
|
|
77246
|
+
const isToolContextual = TOOL_CONTEXTUAL_HOOK_EVENTS.has(hookEvent);
|
|
77247
|
+
if (isToolContextual) {
|
|
77248
|
+
yield {
|
|
77249
|
+
type: "hook_started",
|
|
77250
|
+
data: {
|
|
77251
|
+
hookId: msg.hook_id,
|
|
77252
|
+
hookName: msg.hook_name,
|
|
77253
|
+
hookEvent,
|
|
77254
|
+
toolCallId: toolState.currentToolId || null
|
|
77255
|
+
}
|
|
77256
|
+
};
|
|
77257
|
+
} else {
|
|
77258
|
+
yield {
|
|
77259
|
+
type: "system_status",
|
|
77260
|
+
data: { message: `Running hook "${msg.hook_name}"...` }
|
|
77261
|
+
};
|
|
77262
|
+
}
|
|
77263
|
+
return;
|
|
77264
|
+
}
|
|
77265
|
+
if (message.subtype === "hook_progress") {
|
|
77266
|
+
const msg = message;
|
|
77267
|
+
const hookEvent = msg.hook_event;
|
|
77268
|
+
const isToolContextual = TOOL_CONTEXTUAL_HOOK_EVENTS.has(hookEvent);
|
|
77269
|
+
if (isToolContextual) {
|
|
77270
|
+
yield {
|
|
77271
|
+
type: "hook_progress",
|
|
77272
|
+
data: {
|
|
77273
|
+
hookId: msg.hook_id,
|
|
77274
|
+
stdout: msg.stdout,
|
|
77275
|
+
stderr: msg.stderr
|
|
77276
|
+
}
|
|
77277
|
+
};
|
|
77278
|
+
}
|
|
77279
|
+
return;
|
|
77280
|
+
}
|
|
77281
|
+
if (message.subtype === "hook_response") {
|
|
77282
|
+
const msg = message;
|
|
77283
|
+
const hookEvent = msg.hook_event;
|
|
77284
|
+
const isToolContextual = TOOL_CONTEXTUAL_HOOK_EVENTS.has(hookEvent);
|
|
77285
|
+
if (isToolContextual) {
|
|
77286
|
+
yield {
|
|
77287
|
+
type: "hook_response",
|
|
77288
|
+
data: {
|
|
77289
|
+
hookId: msg.hook_id,
|
|
77290
|
+
hookName: msg.hook_name,
|
|
77291
|
+
exitCode: msg.exit_code,
|
|
77292
|
+
outcome: msg.outcome,
|
|
77293
|
+
stdout: msg.stdout,
|
|
77294
|
+
stderr: msg.stderr
|
|
77295
|
+
}
|
|
77296
|
+
};
|
|
77297
|
+
} else if (msg.outcome === "error") {
|
|
77298
|
+
yield {
|
|
77299
|
+
type: "error",
|
|
77300
|
+
data: {
|
|
77301
|
+
message: `Hook "${msg.hook_name}" failed (${hookEvent})`,
|
|
77302
|
+
code: "hook_failure",
|
|
77303
|
+
category: "execution_error",
|
|
77304
|
+
details: msg.stderr || msg.stdout
|
|
77305
|
+
}
|
|
77306
|
+
};
|
|
77307
|
+
}
|
|
77308
|
+
return;
|
|
77309
|
+
}
|
|
77310
|
+
}
|
|
76895
77311
|
if (message.type === "stream_event") {
|
|
76896
77312
|
const event = message.event;
|
|
76897
77313
|
const eventType = event.type;
|
|
76898
77314
|
if (eventType === "content_block_start") {
|
|
76899
77315
|
const contentBlock = event.content_block;
|
|
76900
|
-
if (contentBlock?.type === "
|
|
77316
|
+
if (contentBlock?.type === "thinking") {
|
|
77317
|
+
toolState.inThinking = true;
|
|
77318
|
+
toolState.thinkingStartMs = Date.now();
|
|
77319
|
+
} else if (contentBlock?.type === "tool_use") {
|
|
76901
77320
|
toolState.resetTaskInput();
|
|
76902
77321
|
toolState.setToolState(true, contentBlock.name, contentBlock.id);
|
|
77322
|
+
toolState.toolNameById.set(contentBlock.id, contentBlock.name);
|
|
76903
77323
|
yield {
|
|
76904
77324
|
type: "tool_call_start",
|
|
76905
77325
|
data: {
|
|
@@ -76911,7 +77331,9 @@ async function* mapSdkMessage(message, session, sessionId, toolState) {
|
|
|
76911
77331
|
}
|
|
76912
77332
|
} else if (eventType === "content_block_delta") {
|
|
76913
77333
|
const delta = event.delta;
|
|
76914
|
-
if (delta?.type === "
|
|
77334
|
+
if (delta?.type === "thinking_delta" && toolState.inThinking) {
|
|
77335
|
+
yield { type: "thinking_delta", data: { text: delta.thinking } };
|
|
77336
|
+
} else if (delta?.type === "text_delta" && !toolState.inTool) {
|
|
76915
77337
|
yield { type: "text_delta", data: { text: delta.text } };
|
|
76916
77338
|
} else if (delta?.type === "input_json_delta" && toolState.inTool) {
|
|
76917
77339
|
if (TASK_TOOL_NAMES.has(toolState.currentToolName)) {
|
|
@@ -76927,8 +77349,27 @@ async function* mapSdkMessage(message, session, sessionId, toolState) {
|
|
|
76927
77349
|
}
|
|
76928
77350
|
};
|
|
76929
77351
|
}
|
|
77352
|
+
} else if (eventType === "message_delta") {
|
|
77353
|
+
const delta = event.delta;
|
|
77354
|
+
const usage = event.usage;
|
|
77355
|
+
const stopReason = delta?.stop_reason;
|
|
77356
|
+
const outputTokens = usage?.output_tokens;
|
|
77357
|
+
if (outputTokens !== void 0) {
|
|
77358
|
+
yield {
|
|
77359
|
+
type: "session_status",
|
|
77360
|
+
data: { sessionId, outputTokens }
|
|
77361
|
+
};
|
|
77362
|
+
}
|
|
77363
|
+
if (stopReason === "max_tokens") {
|
|
77364
|
+
yield {
|
|
77365
|
+
type: "system_status",
|
|
77366
|
+
data: { message: "Response truncated \u2014 reached max output tokens." }
|
|
77367
|
+
};
|
|
77368
|
+
}
|
|
76930
77369
|
} else if (eventType === "content_block_stop") {
|
|
76931
|
-
if (toolState.
|
|
77370
|
+
if (toolState.inThinking) {
|
|
77371
|
+
toolState.inThinking = false;
|
|
77372
|
+
} else if (toolState.inTool) {
|
|
76932
77373
|
const wasTaskTool = TASK_TOOL_NAMES.has(toolState.currentToolName);
|
|
76933
77374
|
const taskToolName = toolState.currentToolName;
|
|
76934
77375
|
yield {
|
|
@@ -76962,7 +77403,7 @@ async function* mapSdkMessage(message, session, sessionId, toolState) {
|
|
|
76962
77403
|
type: "tool_result",
|
|
76963
77404
|
data: {
|
|
76964
77405
|
toolCallId: toolUseId,
|
|
76965
|
-
toolName: "",
|
|
77406
|
+
toolName: toolState.toolNameById.get(toolUseId) ?? "",
|
|
76966
77407
|
result: summary.summary,
|
|
76967
77408
|
status: "complete"
|
|
76968
77409
|
}
|
|
@@ -76970,6 +77411,35 @@ async function* mapSdkMessage(message, session, sessionId, toolState) {
|
|
|
76970
77411
|
}
|
|
76971
77412
|
return;
|
|
76972
77413
|
}
|
|
77414
|
+
if (message.type === "tool_progress") {
|
|
77415
|
+
const progress = message;
|
|
77416
|
+
yield {
|
|
77417
|
+
type: "tool_progress",
|
|
77418
|
+
data: {
|
|
77419
|
+
toolCallId: progress.tool_use_id,
|
|
77420
|
+
content: progress.content
|
|
77421
|
+
}
|
|
77422
|
+
};
|
|
77423
|
+
return;
|
|
77424
|
+
}
|
|
77425
|
+
if (message.type === "prompt_suggestion") {
|
|
77426
|
+
const suggestions = message.suggestions;
|
|
77427
|
+
if (Array.isArray(suggestions) && suggestions.length > 0) {
|
|
77428
|
+
yield {
|
|
77429
|
+
type: "prompt_suggestion",
|
|
77430
|
+
data: { suggestions }
|
|
77431
|
+
};
|
|
77432
|
+
}
|
|
77433
|
+
return;
|
|
77434
|
+
}
|
|
77435
|
+
if (message.type === "rate_limit_event") {
|
|
77436
|
+
const retryAfter = message.retry_after;
|
|
77437
|
+
yield {
|
|
77438
|
+
type: "rate_limit",
|
|
77439
|
+
data: { retryAfter }
|
|
77440
|
+
};
|
|
77441
|
+
return;
|
|
77442
|
+
}
|
|
76973
77443
|
if (message.type === "result") {
|
|
76974
77444
|
const result2 = message;
|
|
76975
77445
|
const usage = result2.usage;
|
|
@@ -76985,11 +77455,31 @@ async function* mapSdkMessage(message, session, sessionId, toolState) {
|
|
|
76985
77455
|
contextMaxTokens: firstModelUsage?.contextWindow
|
|
76986
77456
|
}
|
|
76987
77457
|
};
|
|
77458
|
+
const subtype = result2.subtype;
|
|
77459
|
+
if (subtype && subtype !== "success") {
|
|
77460
|
+
const errors = result2.errors;
|
|
77461
|
+
const category = mapErrorCategory(subtype);
|
|
77462
|
+
yield {
|
|
77463
|
+
type: "error",
|
|
77464
|
+
data: {
|
|
77465
|
+
message: errors?.[0] ?? "An unexpected error occurred.",
|
|
77466
|
+
code: subtype,
|
|
77467
|
+
category,
|
|
77468
|
+
details: errors?.join("\n")
|
|
77469
|
+
}
|
|
77470
|
+
};
|
|
77471
|
+
}
|
|
76988
77472
|
yield {
|
|
76989
77473
|
type: "done",
|
|
76990
77474
|
data: { sessionId }
|
|
76991
77475
|
};
|
|
77476
|
+
return;
|
|
76992
77477
|
}
|
|
77478
|
+
logger.debug(
|
|
77479
|
+
"Unhandled SDK message type: %s (subtype: %s)",
|
|
77480
|
+
message.type,
|
|
77481
|
+
"subtype" in message ? message.subtype : "none"
|
|
77482
|
+
);
|
|
76993
77483
|
}
|
|
76994
77484
|
|
|
76995
77485
|
// ../../apps/server/src/services/runtimes/claude-code/context-builder.ts
|
|
@@ -77333,15 +77823,15 @@ var RESUME_FAILURE_PATTERNS = [
|
|
|
77333
77823
|
"query closed before response",
|
|
77334
77824
|
"session not found",
|
|
77335
77825
|
"no such file",
|
|
77336
|
-
"enoent"
|
|
77337
|
-
"process exited with code"
|
|
77826
|
+
"enoent"
|
|
77338
77827
|
];
|
|
77828
|
+
var MAX_RESUME_RETRIES = 1;
|
|
77339
77829
|
function isResumeFailure(err) {
|
|
77340
77830
|
if (!(err instanceof Error)) return false;
|
|
77341
77831
|
const msg = err.message.toLowerCase();
|
|
77342
77832
|
return RESUME_FAILURE_PATTERNS.some((p2) => msg.includes(p2));
|
|
77343
77833
|
}
|
|
77344
|
-
async function* executeSdkQuery(sessionId, content3, session, opts, messageOpts) {
|
|
77834
|
+
async function* executeSdkQuery(sessionId, content3, session, opts, messageOpts, retryDepth = 0) {
|
|
77345
77835
|
session.lastActivity = Date.now();
|
|
77346
77836
|
session.eventQueue = [];
|
|
77347
77837
|
const effectiveCwd = messageOpts?.cwd || opts.sessionCwd || opts.cwd;
|
|
@@ -77383,6 +77873,7 @@ ${messageOpts.systemPromptAppend}` : baseAppend;
|
|
|
77383
77873
|
const sdkOptions = {
|
|
77384
77874
|
cwd: effectiveCwd,
|
|
77385
77875
|
includePartialMessages: true,
|
|
77876
|
+
promptSuggestions: true,
|
|
77386
77877
|
settingSources: ["project", "user"],
|
|
77387
77878
|
systemPrompt: {
|
|
77388
77879
|
type: "preset",
|
|
@@ -77469,7 +77960,10 @@ ${messageOpts.systemPromptAppend}` : baseAppend;
|
|
|
77469
77960
|
effectiveCwd
|
|
77470
77961
|
});
|
|
77471
77962
|
let emittedDone = false;
|
|
77963
|
+
let emittedError = false;
|
|
77472
77964
|
let eventCount = 0;
|
|
77965
|
+
let contentEventCount = 0;
|
|
77966
|
+
let wasInteractive = false;
|
|
77473
77967
|
const streamStart = Date.now();
|
|
77474
77968
|
const toolState = createToolState();
|
|
77475
77969
|
try {
|
|
@@ -77503,6 +77997,12 @@ ${messageOpts.systemPromptAppend}` : baseAppend;
|
|
|
77503
77997
|
opts.meshCore.updateLastSeen(meshAgentId, "response_complete");
|
|
77504
77998
|
}
|
|
77505
77999
|
}
|
|
78000
|
+
if (["text_delta", "tool_call_start", "tool_result", "thinking_delta"].includes(event.type)) {
|
|
78001
|
+
contentEventCount++;
|
|
78002
|
+
}
|
|
78003
|
+
if (["approval_required", "question_prompt"].includes(event.type)) {
|
|
78004
|
+
wasInteractive = true;
|
|
78005
|
+
}
|
|
77506
78006
|
eventCount++;
|
|
77507
78007
|
yield event;
|
|
77508
78008
|
}
|
|
@@ -77512,34 +78012,57 @@ ${messageOpts.systemPromptAppend}` : baseAppend;
|
|
|
77512
78012
|
}
|
|
77513
78013
|
}
|
|
77514
78014
|
} catch (err) {
|
|
77515
|
-
if (session.hasStarted && isResumeFailure(err)) {
|
|
78015
|
+
if (session.hasStarted && isResumeFailure(err) && retryDepth < MAX_RESUME_RETRIES) {
|
|
77516
78016
|
logger.warn("[sendMessage] resume failed for stale session, retrying as new", {
|
|
77517
78017
|
session: sessionId,
|
|
78018
|
+
retryDepth,
|
|
77518
78019
|
error: err instanceof Error ? err.message : String(err)
|
|
77519
78020
|
});
|
|
77520
78021
|
session.hasStarted = false;
|
|
77521
|
-
yield* executeSdkQuery(sessionId, content3, session, opts, messageOpts);
|
|
78022
|
+
yield* executeSdkQuery(sessionId, content3, session, opts, messageOpts, retryDepth + 1);
|
|
77522
78023
|
return;
|
|
77523
78024
|
}
|
|
78025
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
77524
78026
|
logger.warn("[sendMessage] stream error", {
|
|
77525
78027
|
session: sessionId,
|
|
77526
|
-
error:
|
|
78028
|
+
error: errMsg,
|
|
77527
78029
|
durationMs: Date.now() - streamStart,
|
|
77528
|
-
eventCount
|
|
78030
|
+
eventCount,
|
|
78031
|
+
contentEventCount,
|
|
78032
|
+
retryDepth
|
|
77529
78033
|
});
|
|
77530
78034
|
yield {
|
|
77531
78035
|
type: "error",
|
|
77532
78036
|
data: {
|
|
77533
|
-
message:
|
|
78037
|
+
message: "The agent stopped unexpectedly. The service may be temporarily overloaded \u2014 try again in a moment.",
|
|
78038
|
+
category: "execution_error",
|
|
78039
|
+
details: errMsg
|
|
77534
78040
|
}
|
|
77535
78041
|
};
|
|
78042
|
+
emittedError = true;
|
|
77536
78043
|
} finally {
|
|
77537
78044
|
session.activeQuery = void 0;
|
|
77538
78045
|
}
|
|
78046
|
+
if (contentEventCount === 0 && !emittedError && !wasInteractive) {
|
|
78047
|
+
logger.warn("[sendMessage] stream completed with zero content events", {
|
|
78048
|
+
session: sessionId,
|
|
78049
|
+
eventCount,
|
|
78050
|
+
durationMs: Date.now() - streamStart
|
|
78051
|
+
});
|
|
78052
|
+
yield {
|
|
78053
|
+
type: "error",
|
|
78054
|
+
data: {
|
|
78055
|
+
message: "The agent did not respond. The service may be temporarily unavailable.",
|
|
78056
|
+
category: "execution_error"
|
|
78057
|
+
}
|
|
78058
|
+
};
|
|
78059
|
+
emittedError = true;
|
|
78060
|
+
}
|
|
77539
78061
|
logger.info("[sendMessage] stream done", {
|
|
77540
78062
|
session: sessionId,
|
|
77541
78063
|
durationMs: Date.now() - streamStart,
|
|
77542
|
-
eventCount
|
|
78064
|
+
eventCount,
|
|
78065
|
+
contentEventCount
|
|
77543
78066
|
});
|
|
77544
78067
|
if (!emittedDone) {
|
|
77545
78068
|
yield {
|
|
@@ -77601,7 +78124,7 @@ var ClaudeCodeRuntime = class _ClaudeCodeRuntime {
|
|
|
77601
78124
|
this.cwd = cwd ?? DEFAULT_CWD;
|
|
77602
78125
|
this.claudeCliPath = resolveClaudeCliPath();
|
|
77603
78126
|
this.transcriptReader = new TranscriptReader();
|
|
77604
|
-
this.broadcaster = new SessionBroadcaster(this.transcriptReader);
|
|
78127
|
+
this.broadcaster = new SessionBroadcaster(this.transcriptReader, this.lockManager);
|
|
77605
78128
|
}
|
|
77606
78129
|
// ---------------------------------------------------------------------------
|
|
77607
78130
|
// Capabilities
|
|
@@ -87076,7 +87599,10 @@ var SignalEmitter = class {
|
|
|
87076
87599
|
var DEFAULT_RATE_LIMIT_CONFIG = {
|
|
87077
87600
|
enabled: true,
|
|
87078
87601
|
windowSecs: 60,
|
|
87079
|
-
maxPerWindow: 100
|
|
87602
|
+
maxPerWindow: 100,
|
|
87603
|
+
perSenderOverrides: {
|
|
87604
|
+
"agent:": 2e3
|
|
87605
|
+
}
|
|
87080
87606
|
};
|
|
87081
87607
|
function checkRateLimit(sender, countInWindow, config = DEFAULT_RATE_LIMIT_CONFIG) {
|
|
87082
87608
|
if (!config.enabled) {
|
|
@@ -87641,6 +88167,9 @@ var WatcherManager = class {
|
|
|
87641
88167
|
};
|
|
87642
88168
|
|
|
87643
88169
|
// ../relay/dist/types.js
|
|
88170
|
+
var noop = () => {
|
|
88171
|
+
};
|
|
88172
|
+
var noopLogger = { debug: noop, info: noop, warn: noop, error: noop };
|
|
87644
88173
|
function inferEndpointType2(subject) {
|
|
87645
88174
|
if (subject.startsWith("relay.inbox.dispatch."))
|
|
87646
88175
|
return "dispatch";
|
|
@@ -88198,6 +88727,8 @@ var BaseRelayAdapter = class {
|
|
|
88198
88727
|
displayName;
|
|
88199
88728
|
/** Reference to the relay publisher, set on start, cleared on stop. */
|
|
88200
88729
|
relay = null;
|
|
88730
|
+
/** Logger for debug/info/warn/error output. Silent until injected via {@link setLogger}. */
|
|
88731
|
+
logger = noopLogger;
|
|
88201
88732
|
_status = {
|
|
88202
88733
|
state: "disconnected",
|
|
88203
88734
|
messageCount: { inbound: 0, outbound: 0 },
|
|
@@ -88208,6 +88739,17 @@ var BaseRelayAdapter = class {
|
|
|
88208
88739
|
this.subjectPrefix = subjectPrefix;
|
|
88209
88740
|
this.displayName = displayName;
|
|
88210
88741
|
}
|
|
88742
|
+
/**
|
|
88743
|
+
* Inject a logger for structured debug output.
|
|
88744
|
+
*
|
|
88745
|
+
* Call after construction (typically from the adapter factory).
|
|
88746
|
+
* Until called, the adapter uses a silent no-op logger.
|
|
88747
|
+
*
|
|
88748
|
+
* @param logger - A logger compatible with consola's tagged logger
|
|
88749
|
+
*/
|
|
88750
|
+
setLogger(logger3) {
|
|
88751
|
+
this.logger = logger3;
|
|
88752
|
+
}
|
|
88211
88753
|
/**
|
|
88212
88754
|
* Start the adapter with idempotency guard and status tracking.
|
|
88213
88755
|
*
|
|
@@ -88220,6 +88762,7 @@ var BaseRelayAdapter = class {
|
|
|
88220
88762
|
return;
|
|
88221
88763
|
this._status = { ...this._status, state: "starting" };
|
|
88222
88764
|
this.relay = relay;
|
|
88765
|
+
this.logger.info("starting");
|
|
88223
88766
|
try {
|
|
88224
88767
|
await this._start(relay);
|
|
88225
88768
|
this._status = {
|
|
@@ -88227,6 +88770,7 @@ var BaseRelayAdapter = class {
|
|
|
88227
88770
|
state: "connected",
|
|
88228
88771
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
88229
88772
|
};
|
|
88773
|
+
this.logger.info("connected");
|
|
88230
88774
|
} catch (err) {
|
|
88231
88775
|
this.recordError(err);
|
|
88232
88776
|
this.relay = null;
|
|
@@ -88242,6 +88786,7 @@ var BaseRelayAdapter = class {
|
|
|
88242
88786
|
if (this._status.state === "disconnected")
|
|
88243
88787
|
return;
|
|
88244
88788
|
this._status = { ...this._status, state: "stopping" };
|
|
88789
|
+
this.logger.info("stopping");
|
|
88245
88790
|
try {
|
|
88246
88791
|
await this._stop();
|
|
88247
88792
|
} finally {
|
|
@@ -88251,6 +88796,7 @@ var BaseRelayAdapter = class {
|
|
|
88251
88796
|
messageCount: this._status.messageCount,
|
|
88252
88797
|
errorCount: this._status.errorCount
|
|
88253
88798
|
};
|
|
88799
|
+
this.logger.info("stopped");
|
|
88254
88800
|
}
|
|
88255
88801
|
}
|
|
88256
88802
|
/**
|
|
@@ -88299,6 +88845,7 @@ var BaseRelayAdapter = class {
|
|
|
88299
88845
|
*/
|
|
88300
88846
|
recordError(err) {
|
|
88301
88847
|
const message = err instanceof Error ? err.message : String(err);
|
|
88848
|
+
this.logger.warn("error:", message);
|
|
88302
88849
|
this._status = {
|
|
88303
88850
|
...this._status,
|
|
88304
88851
|
state: "error",
|
|
@@ -88316,6 +88863,7 @@ var BaseRelayAdapter = class {
|
|
|
88316
88863
|
setReconnecting() {
|
|
88317
88864
|
if (this._status.state !== "error")
|
|
88318
88865
|
return;
|
|
88866
|
+
this.logger.info("reconnecting");
|
|
88319
88867
|
this._status = { ...this._status, state: "reconnecting" };
|
|
88320
88868
|
}
|
|
88321
88869
|
/**
|
|
@@ -88501,17 +89049,23 @@ function extractChannelName(chat) {
|
|
|
88501
89049
|
}
|
|
88502
89050
|
return void 0;
|
|
88503
89051
|
}
|
|
88504
|
-
async function handleInboundMessage(ctx, relay, callbacks) {
|
|
88505
|
-
if (!ctx.message)
|
|
89052
|
+
async function handleInboundMessage(ctx, relay, callbacks, logger3 = noopLogger) {
|
|
89053
|
+
if (!ctx.message) {
|
|
89054
|
+
logger3.debug("inbound skipped: no message in context");
|
|
88506
89055
|
return;
|
|
89056
|
+
}
|
|
88507
89057
|
const { chat, from, message } = ctx;
|
|
88508
|
-
if (!chat || !message)
|
|
89058
|
+
if (!chat || !message) {
|
|
89059
|
+
logger3.debug("inbound skipped: missing chat or message");
|
|
88509
89060
|
return;
|
|
89061
|
+
}
|
|
88510
89062
|
const isGroup = isGroupChat(chat.type);
|
|
88511
89063
|
const subject = buildSubject(chat.id, isGroup);
|
|
88512
89064
|
const rawText = message.text ?? message.caption ?? "";
|
|
88513
|
-
if (!rawText)
|
|
89065
|
+
if (!rawText) {
|
|
89066
|
+
logger3.debug(`inbound skipped: no text content in chat ${chat.id}`);
|
|
88514
89067
|
return;
|
|
89068
|
+
}
|
|
88515
89069
|
const text6 = rawText.slice(0, MAX_CONTENT_LENGTH);
|
|
88516
89070
|
const senderName = from ? [from.first_name, from.last_name].filter(Boolean).join(" ") || from.username || UNKNOWN_SENDER : UNKNOWN_SENDER;
|
|
88517
89071
|
const payload = {
|
|
@@ -88539,11 +89093,16 @@ async function handleInboundMessage(ctx, relay, callbacks) {
|
|
|
88539
89093
|
replyTo: subject
|
|
88540
89094
|
});
|
|
88541
89095
|
callbacks.trackInbound();
|
|
89096
|
+
logger3.debug(`inbound from ${senderName} in chat ${chat.id}: "${text6.slice(0, 80)}${text6.length > 80 ? "\u2026" : ""}" (${text6.length} chars) \u2192 ${subject}`);
|
|
88542
89097
|
} catch (err) {
|
|
88543
89098
|
callbacks.recordError(err);
|
|
89099
|
+
logger3.warn(`inbound publish failed for chat ${chat.id}:`, err instanceof Error ? err.message : String(err));
|
|
88544
89100
|
}
|
|
88545
89101
|
}
|
|
88546
89102
|
|
|
89103
|
+
// ../relay/dist/adapters/telegram/outbound.js
|
|
89104
|
+
import { randomBytes } from "node:crypto";
|
|
89105
|
+
|
|
88547
89106
|
// ../../node_modules/.pnpm/bail@2.0.2/node_modules/bail/index.js
|
|
88548
89107
|
function bail(error) {
|
|
88549
89108
|
if (error) {
|
|
@@ -100906,19 +101465,6 @@ function truncateText(text6, maxLen) {
|
|
|
100906
101465
|
return text6;
|
|
100907
101466
|
return `${text6.slice(0, maxLen - 3)}...`;
|
|
100908
101467
|
}
|
|
100909
|
-
var SILENT_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
100910
|
-
"session_status",
|
|
100911
|
-
"tool_call_start",
|
|
100912
|
-
"tool_call_delta",
|
|
100913
|
-
"tool_call_end",
|
|
100914
|
-
"tool_result",
|
|
100915
|
-
"approval_required",
|
|
100916
|
-
"question_prompt",
|
|
100917
|
-
"task_update",
|
|
100918
|
-
"relay_receipt",
|
|
100919
|
-
"message_delivered",
|
|
100920
|
-
"relay_message"
|
|
100921
|
-
]);
|
|
100922
101468
|
function detectStreamEventType(payload) {
|
|
100923
101469
|
if (payload === null || typeof payload !== "object")
|
|
100924
101470
|
return null;
|
|
@@ -100947,6 +101493,40 @@ function extractErrorMessage(payload) {
|
|
|
100947
101493
|
const data = obj.data;
|
|
100948
101494
|
return typeof data?.message === "string" ? data.message : null;
|
|
100949
101495
|
}
|
|
101496
|
+
function extractApprovalData(payload) {
|
|
101497
|
+
if (payload === null || typeof payload !== "object")
|
|
101498
|
+
return null;
|
|
101499
|
+
const obj = payload;
|
|
101500
|
+
if (obj.type !== "approval_required")
|
|
101501
|
+
return null;
|
|
101502
|
+
const data = obj.data;
|
|
101503
|
+
if (!data?.toolCallId || !data?.toolName)
|
|
101504
|
+
return null;
|
|
101505
|
+
return {
|
|
101506
|
+
toolCallId: data.toolCallId,
|
|
101507
|
+
toolName: data.toolName,
|
|
101508
|
+
input: data.input ?? "",
|
|
101509
|
+
timeoutMs: data.timeoutMs ?? 6e5
|
|
101510
|
+
};
|
|
101511
|
+
}
|
|
101512
|
+
function formatToolDescription(toolName, input) {
|
|
101513
|
+
try {
|
|
101514
|
+
const parsed = JSON.parse(input);
|
|
101515
|
+
if (toolName === "Write" && typeof parsed.path === "string") {
|
|
101516
|
+
return `wants to write to \`${parsed.path}\``;
|
|
101517
|
+
}
|
|
101518
|
+
if (toolName === "Edit" && typeof parsed.file_path === "string") {
|
|
101519
|
+
return `wants to edit \`${parsed.file_path}\``;
|
|
101520
|
+
}
|
|
101521
|
+
if (toolName === "Bash" && typeof parsed.command === "string") {
|
|
101522
|
+
const cmd = parsed.command;
|
|
101523
|
+
const preview = cmd.length > 60 ? `${cmd.slice(0, 57)}...` : cmd;
|
|
101524
|
+
return `wants to run \`${preview}\``;
|
|
101525
|
+
}
|
|
101526
|
+
} catch {
|
|
101527
|
+
}
|
|
101528
|
+
return `wants to use tool \`${toolName}\``;
|
|
101529
|
+
}
|
|
100950
101530
|
function formatForPlatform(content3, platform2) {
|
|
100951
101531
|
switch (platform2) {
|
|
100952
101532
|
case "slack":
|
|
@@ -100962,6 +101542,19 @@ function formatForPlatform(content3, platform2) {
|
|
|
100962
101542
|
var TELEGRAM_TYPING_ACTION = "typing";
|
|
100963
101543
|
var typingIntervals = /* @__PURE__ */ new Map();
|
|
100964
101544
|
var TYPING_REFRESH_MS = 4e3;
|
|
101545
|
+
var DRAFT_UPDATE_INTERVAL_MS = 200;
|
|
101546
|
+
var BUFFER_TTL_MS = 5 * 60 * 1e3;
|
|
101547
|
+
var lastDraftUpdate = /* @__PURE__ */ new Map();
|
|
101548
|
+
var callbackIdMap = /* @__PURE__ */ new Map();
|
|
101549
|
+
var CALLBACK_ID_TTL_MS = 15 * 60 * 1e3;
|
|
101550
|
+
var pendingApprovalTimeouts = /* @__PURE__ */ new Map();
|
|
101551
|
+
function clearApprovalTimeout(shortKey) {
|
|
101552
|
+
const timer = pendingApprovalTimeouts.get(shortKey);
|
|
101553
|
+
if (timer) {
|
|
101554
|
+
clearTimeout(timer);
|
|
101555
|
+
pendingApprovalTimeouts.delete(shortKey);
|
|
101556
|
+
}
|
|
101557
|
+
}
|
|
100965
101558
|
async function sendAndTrack(bot, chatId, text6, startTime, callbacks) {
|
|
100966
101559
|
try {
|
|
100967
101560
|
await bot.api.sendMessage(chatId, text6);
|
|
@@ -100977,9 +101570,10 @@ async function sendAndTrack(bot, chatId, text6, startTime, callbacks) {
|
|
|
100977
101570
|
}
|
|
100978
101571
|
}
|
|
100979
101572
|
async function deliverMessage(opts) {
|
|
100980
|
-
const { adapterId, subject, envelope, bot, responseBuffers, callbacks } = opts;
|
|
101573
|
+
const { adapterId, subject, envelope, bot, responseBuffers, callbacks, streaming, logger: logger3 = noopLogger } = opts;
|
|
100981
101574
|
const startTime = Date.now();
|
|
100982
101575
|
if (envelope.from.startsWith(SUBJECT_PREFIX)) {
|
|
101576
|
+
logger3.debug("deliver: echo prevention \u2014 skipping self-originated message");
|
|
100983
101577
|
return { success: true, durationMs: Date.now() - startTime };
|
|
100984
101578
|
}
|
|
100985
101579
|
if (!bot) {
|
|
@@ -100997,18 +101591,43 @@ async function deliverMessage(opts) {
|
|
|
100997
101591
|
durationMs: Date.now() - startTime
|
|
100998
101592
|
};
|
|
100999
101593
|
}
|
|
101594
|
+
const now = Date.now();
|
|
101595
|
+
for (const [id, buf] of responseBuffers) {
|
|
101596
|
+
if (now - buf.startedAt > BUFFER_TTL_MS) {
|
|
101597
|
+
responseBuffers.delete(id);
|
|
101598
|
+
lastDraftUpdate.delete(id);
|
|
101599
|
+
logger3.warn(`buffer: reaped stale buffer for chat ${id} (age: ${Math.round((now - buf.startedAt) / 1e3)}s)`);
|
|
101600
|
+
}
|
|
101601
|
+
}
|
|
101000
101602
|
const eventType = detectStreamEventType(envelope.payload);
|
|
101001
101603
|
if (eventType) {
|
|
101002
101604
|
const textChunk = extractTextDelta(envelope.payload);
|
|
101003
101605
|
if (textChunk) {
|
|
101004
|
-
|
|
101005
|
-
responseBuffers.
|
|
101606
|
+
logger3.debug(`deliver: text_delta to chat ${chatId} (${textChunk.length} chars)`);
|
|
101607
|
+
const existing = responseBuffers.get(chatId);
|
|
101608
|
+
responseBuffers.set(chatId, {
|
|
101609
|
+
text: (existing?.text ?? "") + textChunk,
|
|
101610
|
+
startedAt: existing?.startedAt ?? Date.now()
|
|
101611
|
+
});
|
|
101612
|
+
if (streaming && chatId > 0) {
|
|
101613
|
+
const lastUpdate = lastDraftUpdate.get(chatId) ?? 0;
|
|
101614
|
+
if (Date.now() - lastUpdate >= DRAFT_UPDATE_INTERVAL_MS) {
|
|
101615
|
+
lastDraftUpdate.set(chatId, Date.now());
|
|
101616
|
+
logger3.debug(`stream: sendMessageDraft to chat ${chatId} (${responseBuffers.get(chatId).text.length} chars)`);
|
|
101617
|
+
try {
|
|
101618
|
+
await bot.api.sendMessageDraft(chatId, responseBuffers.get(chatId).text);
|
|
101619
|
+
} catch {
|
|
101620
|
+
}
|
|
101621
|
+
}
|
|
101622
|
+
}
|
|
101006
101623
|
return { success: true, durationMs: Date.now() - startTime };
|
|
101007
101624
|
}
|
|
101008
101625
|
const errorMsg = extractErrorMessage(envelope.payload);
|
|
101009
101626
|
if (errorMsg) {
|
|
101010
|
-
|
|
101627
|
+
logger3.debug(`deliver: error to chat ${chatId}: "${errorMsg.slice(0, 100)}"`);
|
|
101628
|
+
const buffered = responseBuffers.get(chatId)?.text ?? "";
|
|
101011
101629
|
responseBuffers.delete(chatId);
|
|
101630
|
+
lastDraftUpdate.delete(chatId);
|
|
101012
101631
|
const text7 = buffered ? truncateText(`${buffered}
|
|
101013
101632
|
|
|
101014
101633
|
[Error: ${errorMsg}]`, MAX_MESSAGE_LENGTH) : truncateText(`[Error: ${errorMsg}]`, MAX_MESSAGE_LENGTH);
|
|
@@ -101016,18 +101635,33 @@ async function deliverMessage(opts) {
|
|
|
101016
101635
|
}
|
|
101017
101636
|
if (eventType === "done") {
|
|
101018
101637
|
const buffered = responseBuffers.get(chatId);
|
|
101638
|
+
logger3.debug(`deliver: done for chat ${chatId} (buffered: ${buffered ? `${buffered.text.length} chars` : "empty"})`);
|
|
101019
101639
|
responseBuffers.delete(chatId);
|
|
101640
|
+
lastDraftUpdate.delete(chatId);
|
|
101020
101641
|
if (buffered) {
|
|
101021
|
-
return sendAndTrack(bot, chatId, truncateText(buffered, MAX_MESSAGE_LENGTH), startTime, callbacks);
|
|
101642
|
+
return sendAndTrack(bot, chatId, truncateText(buffered.text, MAX_MESSAGE_LENGTH), startTime, callbacks);
|
|
101022
101643
|
}
|
|
101023
101644
|
return { success: true, durationMs: Date.now() - startTime };
|
|
101024
101645
|
}
|
|
101025
|
-
if (
|
|
101026
|
-
|
|
101646
|
+
if (eventType === "approval_required") {
|
|
101647
|
+
const data = extractApprovalData(envelope.payload);
|
|
101648
|
+
if (data) {
|
|
101649
|
+
logger3.debug(`deliver: approval_required for tool '${data.toolName}' to chat ${chatId}`);
|
|
101650
|
+
const buffered = responseBuffers.get(chatId);
|
|
101651
|
+
if (buffered?.text) {
|
|
101652
|
+
responseBuffers.delete(chatId);
|
|
101653
|
+
lastDraftUpdate.delete(chatId);
|
|
101654
|
+
await sendAndTrack(bot, chatId, truncateText(buffered.text, MAX_MESSAGE_LENGTH), startTime, callbacks);
|
|
101655
|
+
}
|
|
101656
|
+
return handleApprovalRequired(bot, chatId, data, envelope, callbacks, startTime);
|
|
101657
|
+
}
|
|
101027
101658
|
}
|
|
101659
|
+
logger3.debug(`deliver: dropping stream event '${eventType}' (whitelist)`);
|
|
101660
|
+
return { success: true, durationMs: Date.now() - startTime };
|
|
101028
101661
|
}
|
|
101029
101662
|
const content3 = extractPayloadContent(envelope.payload);
|
|
101030
101663
|
const text6 = truncateText(content3, MAX_MESSAGE_LENGTH);
|
|
101664
|
+
logger3.debug(`deliver: standard payload to chat ${chatId} (${text6.length} chars)`);
|
|
101031
101665
|
return sendAndTrack(bot, chatId, text6, startTime, callbacks);
|
|
101032
101666
|
}
|
|
101033
101667
|
async function handleTypingSignal(bot, subject, state) {
|
|
@@ -101065,6 +101699,66 @@ function clearAllTypingIntervals() {
|
|
|
101065
101699
|
for (const interval of typingIntervals.values())
|
|
101066
101700
|
clearInterval(interval);
|
|
101067
101701
|
typingIntervals.clear();
|
|
101702
|
+
lastDraftUpdate.clear();
|
|
101703
|
+
}
|
|
101704
|
+
function extractAgentIdFromEnvelope(envelope) {
|
|
101705
|
+
const payload = envelope.payload;
|
|
101706
|
+
const data = payload?.data;
|
|
101707
|
+
return data?.agentId ?? "unknown";
|
|
101708
|
+
}
|
|
101709
|
+
function extractSessionIdFromEnvelope(envelope) {
|
|
101710
|
+
const payload = envelope.payload;
|
|
101711
|
+
const data = payload?.data;
|
|
101712
|
+
return data?.ccaSessionKey ?? "unknown";
|
|
101713
|
+
}
|
|
101714
|
+
async function handleApprovalRequired(bot, chatId, data, envelope, callbacks, startTime) {
|
|
101715
|
+
const agentId = extractAgentIdFromEnvelope(envelope);
|
|
101716
|
+
const sessionId = extractSessionIdFromEnvelope(envelope);
|
|
101717
|
+
const shortKey = randomBytes(6).toString("hex");
|
|
101718
|
+
callbackIdMap.set(shortKey, { toolCallId: data.toolCallId, sessionId, agentId });
|
|
101719
|
+
setTimeout(() => callbackIdMap.delete(shortKey), CALLBACK_ID_TTL_MS);
|
|
101720
|
+
const toolDescription = formatToolDescription(data.toolName, data.input);
|
|
101721
|
+
const inputPreview = truncateText(data.input, 400);
|
|
101722
|
+
const messageText = `*Tool Approval Required*
|
|
101723
|
+
\`${data.toolName}\` ${toolDescription}
|
|
101724
|
+
|
|
101725
|
+
\`\`\`
|
|
101726
|
+
${inputPreview}
|
|
101727
|
+
\`\`\``;
|
|
101728
|
+
try {
|
|
101729
|
+
const sent = await bot.api.sendMessage(chatId, messageText, {
|
|
101730
|
+
parse_mode: "Markdown",
|
|
101731
|
+
reply_markup: {
|
|
101732
|
+
inline_keyboard: [
|
|
101733
|
+
[
|
|
101734
|
+
{ text: "Approve", callback_data: JSON.stringify({ k: shortKey, a: 1 }) },
|
|
101735
|
+
{ text: "Deny", callback_data: JSON.stringify({ k: shortKey, a: 0 }) }
|
|
101736
|
+
]
|
|
101737
|
+
]
|
|
101738
|
+
}
|
|
101739
|
+
});
|
|
101740
|
+
if (data.timeoutMs && data.timeoutMs > 0) {
|
|
101741
|
+
const timer = setTimeout(async () => {
|
|
101742
|
+
pendingApprovalTimeouts.delete(shortKey);
|
|
101743
|
+
callbackIdMap.delete(shortKey);
|
|
101744
|
+
try {
|
|
101745
|
+
await bot.api.editMessageText(chatId, sent.message_id, `\u23F0 *Tool Approval Timed Out*
|
|
101746
|
+
~~\`${data.toolName}\`~~ ${toolDescription}`, { parse_mode: "Markdown" });
|
|
101747
|
+
} catch {
|
|
101748
|
+
}
|
|
101749
|
+
}, data.timeoutMs);
|
|
101750
|
+
pendingApprovalTimeouts.set(shortKey, timer);
|
|
101751
|
+
}
|
|
101752
|
+
callbacks.trackOutbound();
|
|
101753
|
+
return { success: true, durationMs: Date.now() - startTime };
|
|
101754
|
+
} catch (err) {
|
|
101755
|
+
callbacks.recordError(err);
|
|
101756
|
+
return {
|
|
101757
|
+
success: false,
|
|
101758
|
+
error: err instanceof Error ? err.message : String(err),
|
|
101759
|
+
durationMs: Date.now() - startTime
|
|
101760
|
+
};
|
|
101761
|
+
}
|
|
101068
101762
|
}
|
|
101069
101763
|
|
|
101070
101764
|
// ../relay/dist/adapters/telegram/webhook.js
|
|
@@ -101196,6 +101890,15 @@ For local development, use a tunnel service (e.g., ngrok, Cloudflare Tunnel).`
|
|
|
101196
101890
|
placeholder: "Auto-generated if empty",
|
|
101197
101891
|
description: "Secret token for validating incoming webhook requests from Telegram.",
|
|
101198
101892
|
showWhen: { field: "mode", equals: "webhook" }
|
|
101893
|
+
},
|
|
101894
|
+
{
|
|
101895
|
+
key: "streaming",
|
|
101896
|
+
label: "Streaming",
|
|
101897
|
+
type: "boolean",
|
|
101898
|
+
required: false,
|
|
101899
|
+
description: "Stream responses in real-time using Telegram's sendMessageDraft API (DMs only). Groups always use buffer-and-flush.",
|
|
101900
|
+
visibleByDefault: true,
|
|
101901
|
+
helpMarkdown: "When enabled, recipients in DMs see text appearing in real-time (ChatGPT-style). Group chats always use buffer-and-flush regardless of this setting. Requires Telegram Bot API 9.5+."
|
|
101199
101902
|
}
|
|
101200
101903
|
],
|
|
101201
101904
|
setupInstructions: "Open Telegram and search for @BotFather. Send /newbot, choose a name and username. Copy the token provided."
|
|
@@ -101228,7 +101931,39 @@ var TelegramAdapter = class _TelegramAdapter extends BaseRelayAdapter {
|
|
|
101228
101931
|
async _start(relay) {
|
|
101229
101932
|
const bot = new import_grammy2.Bot(this.config.token);
|
|
101230
101933
|
bot.api.config.use((0, import_auto_retry.autoRetry)());
|
|
101231
|
-
bot.on("message", (ctx) => handleInboundMessage(ctx, relay, this.makeInboundCallbacks()));
|
|
101934
|
+
bot.on("message", (ctx) => handleInboundMessage(ctx, relay, this.makeInboundCallbacks(), this.logger));
|
|
101935
|
+
bot.on("callback_query:data", async (ctx) => {
|
|
101936
|
+
try {
|
|
101937
|
+
const data = JSON.parse(ctx.callbackQuery.data);
|
|
101938
|
+
const entry = callbackIdMap.get(data.k);
|
|
101939
|
+
if (!entry) {
|
|
101940
|
+
await ctx.answerCallbackQuery({ text: "This approval has expired." });
|
|
101941
|
+
return;
|
|
101942
|
+
}
|
|
101943
|
+
const approved = data.a === 1;
|
|
101944
|
+
callbackIdMap.delete(data.k);
|
|
101945
|
+
clearApprovalTimeout(data.k);
|
|
101946
|
+
const opts = { from: `telegram:${ctx.from.id}` };
|
|
101947
|
+
await relay.publish(`relay.system.approval.${entry.agentId}`, {
|
|
101948
|
+
type: "approval_response",
|
|
101949
|
+
toolCallId: entry.toolCallId,
|
|
101950
|
+
sessionId: entry.sessionId,
|
|
101951
|
+
approved,
|
|
101952
|
+
respondedBy: String(ctx.from.id),
|
|
101953
|
+
platform: "telegram"
|
|
101954
|
+
}, opts);
|
|
101955
|
+
const decision = approved ? "Approved" : "Denied";
|
|
101956
|
+
const emoji = approved ? "\u2705" : "\u274C";
|
|
101957
|
+
await ctx.editMessageText(`${emoji} *Tool ${decision}*`, { parse_mode: "Markdown" });
|
|
101958
|
+
await ctx.answerCallbackQuery({ text: `Tool ${decision}` });
|
|
101959
|
+
this.logger.debug?.(`[Telegram] tool ${approved ? "approved" : "denied"}: toolCallId=${entry.toolCallId}`);
|
|
101960
|
+
} catch (err) {
|
|
101961
|
+
this.logger.error("[Telegram] callback query handler error:", err);
|
|
101962
|
+
this.recordError(err);
|
|
101963
|
+
await ctx.answerCallbackQuery({ text: "Error processing approval." }).catch(() => {
|
|
101964
|
+
});
|
|
101965
|
+
}
|
|
101966
|
+
});
|
|
101232
101967
|
bot.catch((err) => this.recordError(err));
|
|
101233
101968
|
this.bot = bot;
|
|
101234
101969
|
this.signalUnsub = relay.onSignal(`${SUBJECT_PREFIX}.>`, (subject, signal) => {
|
|
@@ -101280,7 +102015,9 @@ var TelegramAdapter = class _TelegramAdapter extends BaseRelayAdapter {
|
|
|
101280
102015
|
envelope,
|
|
101281
102016
|
bot: this.bot,
|
|
101282
102017
|
responseBuffers: this.responseBuffers,
|
|
101283
|
-
callbacks: this.makeOutboundCallbacks()
|
|
102018
|
+
callbacks: this.makeOutboundCallbacks(),
|
|
102019
|
+
streaming: this.config.streaming ?? true,
|
|
102020
|
+
logger: this.logger
|
|
101284
102021
|
});
|
|
101285
102022
|
}
|
|
101286
102023
|
// --- Private helpers ---
|
|
@@ -101328,7 +102065,7 @@ var TelegramAdapter = class _TelegramAdapter extends BaseRelayAdapter {
|
|
|
101328
102065
|
}
|
|
101329
102066
|
const newBot = new import_grammy2.Bot(this.config.token);
|
|
101330
102067
|
newBot.api.config.use((0, import_auto_retry.autoRetry)());
|
|
101331
|
-
newBot.on("message", (ctx) => handleInboundMessage(ctx, this.relay, this.makeInboundCallbacks()));
|
|
102068
|
+
newBot.on("message", (ctx) => handleInboundMessage(ctx, this.relay, this.makeInboundCallbacks(), this.logger));
|
|
101332
102069
|
newBot.catch((e2) => this.recordError(e2));
|
|
101333
102070
|
this.bot = newBot;
|
|
101334
102071
|
this.startPollingMode(newBot).catch((e2) => this.handlePollingError(e2));
|
|
@@ -101746,15 +102483,23 @@ function clearCaches() {
|
|
|
101746
102483
|
userNameCache.clear();
|
|
101747
102484
|
channelNameCache.clear();
|
|
101748
102485
|
}
|
|
101749
|
-
async function handleInboundMessage2(event, client, relay, botUserId, callbacks) {
|
|
101750
|
-
if (event.user === botUserId)
|
|
102486
|
+
async function handleInboundMessage2(event, client, relay, botUserId, callbacks, logger3 = noopLogger) {
|
|
102487
|
+
if (event.user === botUserId) {
|
|
102488
|
+
logger3.debug(`inbound skipped: echo (own user ${botUserId})`);
|
|
101751
102489
|
return;
|
|
101752
|
-
|
|
102490
|
+
}
|
|
102491
|
+
if (event.bot_id) {
|
|
102492
|
+
logger3.debug(`inbound skipped: bot message (bot_id=${event.bot_id})`);
|
|
101753
102493
|
return;
|
|
101754
|
-
|
|
102494
|
+
}
|
|
102495
|
+
if (event.subtype && SKIP_SUBTYPES.has(event.subtype)) {
|
|
102496
|
+
logger3.debug(`inbound skipped: subtype '${event.subtype}'`);
|
|
101755
102497
|
return;
|
|
101756
|
-
|
|
102498
|
+
}
|
|
102499
|
+
if (!event.text) {
|
|
102500
|
+
logger3.debug(`inbound skipped: no text content in ${event.channel}`);
|
|
101757
102501
|
return;
|
|
102502
|
+
}
|
|
101758
102503
|
const isGroup = isGroupChannel(event.channel);
|
|
101759
102504
|
const subject = buildSubject2(event.channel, isGroup);
|
|
101760
102505
|
const content3 = event.text.slice(0, MAX_CONTENT_LENGTH2);
|
|
@@ -101785,8 +102530,10 @@ async function handleInboundMessage2(event, client, relay, botUserId, callbacks)
|
|
|
101785
102530
|
replyTo: subject
|
|
101786
102531
|
});
|
|
101787
102532
|
callbacks.trackInbound();
|
|
102533
|
+
logger3.debug(`inbound from ${senderName} in ${event.channel}: "${content3.slice(0, 80)}${content3.length > 80 ? "\u2026" : ""}" (${content3.length} chars) \u2192 ${subject}`);
|
|
101788
102534
|
} catch (err) {
|
|
101789
102535
|
callbacks.recordError(err);
|
|
102536
|
+
logger3.warn(`inbound publish failed for ${event.channel}:`, err instanceof Error ? err.message : String(err));
|
|
101790
102537
|
}
|
|
101791
102538
|
}
|
|
101792
102539
|
|
|
@@ -101794,6 +102541,14 @@ async function handleInboundMessage2(event, client, relay, botUserId, callbacks)
|
|
|
101794
102541
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
101795
102542
|
var STREAM_UPDATE_INTERVAL_MS = 1e3;
|
|
101796
102543
|
var STREAM_TTL_MS = 5 * 60 * 1e3;
|
|
102544
|
+
var pendingApprovalTimeouts2 = /* @__PURE__ */ new Map();
|
|
102545
|
+
function clearApprovalTimeout2(toolCallId) {
|
|
102546
|
+
const entry = pendingApprovalTimeouts2.get(toolCallId);
|
|
102547
|
+
if (entry) {
|
|
102548
|
+
clearTimeout(entry.timer);
|
|
102549
|
+
pendingApprovalTimeouts2.delete(toolCallId);
|
|
102550
|
+
}
|
|
102551
|
+
}
|
|
101797
102552
|
function streamKey(channelId, threadTs) {
|
|
101798
102553
|
return threadTs ? `${channelId}:${threadTs}` : channelId;
|
|
101799
102554
|
}
|
|
@@ -101846,7 +102601,7 @@ function removeTypingReaction(client, channelId, threadTs, typingIndicator) {
|
|
|
101846
102601
|
}).catch(() => {
|
|
101847
102602
|
});
|
|
101848
102603
|
}
|
|
101849
|
-
async function handleTextDelta(channelId, textChunk, threadTs, client, streamState, callbacks, startTime, streaming, typingIndicator, streamKeyTs) {
|
|
102604
|
+
async function handleTextDelta(channelId, textChunk, threadTs, client, streamState, callbacks, startTime, streaming, nativeStreaming, typingIndicator, streamKeyTs) {
|
|
101850
102605
|
const key = streamKey(channelId, streamKeyTs);
|
|
101851
102606
|
const existing = streamState.get(key);
|
|
101852
102607
|
if (!streaming) {
|
|
@@ -101869,6 +102624,12 @@ async function handleTextDelta(channelId, textChunk, threadTs, client, streamSta
|
|
|
101869
102624
|
}
|
|
101870
102625
|
if (existing) {
|
|
101871
102626
|
existing.accumulatedText += textChunk;
|
|
102627
|
+
if (existing.nativeStreamId) {
|
|
102628
|
+
return wrapSlackCall(() => client.chat.appendStream({
|
|
102629
|
+
stream_id: existing.nativeStreamId,
|
|
102630
|
+
text: formatForPlatform(textChunk, "slack")
|
|
102631
|
+
}), callbacks, startTime);
|
|
102632
|
+
}
|
|
101872
102633
|
const now = Date.now();
|
|
101873
102634
|
if (now - existing.lastUpdateAt < STREAM_UPDATE_INTERVAL_MS) {
|
|
101874
102635
|
return { success: true, durationMs: now - startTime };
|
|
@@ -101882,6 +102643,36 @@ async function handleTextDelta(channelId, textChunk, threadTs, client, streamSta
|
|
|
101882
102643
|
text: truncateText(streamText, MAX_MESSAGE_LENGTH2)
|
|
101883
102644
|
}), callbacks, startTime);
|
|
101884
102645
|
}
|
|
102646
|
+
if (nativeStreaming && threadTs) {
|
|
102647
|
+
try {
|
|
102648
|
+
const slackClient = client;
|
|
102649
|
+
const result2 = await slackClient.chat.startStream({
|
|
102650
|
+
channel: channelId,
|
|
102651
|
+
thread_ts: threadTs
|
|
102652
|
+
});
|
|
102653
|
+
const nativeStreamId = result2.stream_id ?? "";
|
|
102654
|
+
const now = Date.now();
|
|
102655
|
+
streamState.set(key, {
|
|
102656
|
+
channelId,
|
|
102657
|
+
threadTs: threadTs ?? "",
|
|
102658
|
+
messageTs: "",
|
|
102659
|
+
// Not used in native streaming
|
|
102660
|
+
accumulatedText: textChunk,
|
|
102661
|
+
lastUpdateAt: now,
|
|
102662
|
+
startedAt: now,
|
|
102663
|
+
streamId: randomUUID4(),
|
|
102664
|
+
nativeStreamId
|
|
102665
|
+
});
|
|
102666
|
+
await slackClient.chat.appendStream({
|
|
102667
|
+
stream_id: nativeStreamId,
|
|
102668
|
+
text: formatForPlatform(textChunk, "slack")
|
|
102669
|
+
});
|
|
102670
|
+
addTypingReaction(client, channelId, threadTs, typingIndicator);
|
|
102671
|
+
return { success: true, durationMs: Date.now() - startTime };
|
|
102672
|
+
} catch (err) {
|
|
102673
|
+
callbacks.recordError(err);
|
|
102674
|
+
}
|
|
102675
|
+
}
|
|
101885
102676
|
try {
|
|
101886
102677
|
const mrkdwn = formatForPlatform(textChunk, "slack");
|
|
101887
102678
|
const now = Date.now();
|
|
@@ -101910,6 +102701,45 @@ async function handleTextDelta(channelId, textChunk, threadTs, client, streamSta
|
|
|
101910
102701
|
};
|
|
101911
102702
|
}
|
|
101912
102703
|
}
|
|
102704
|
+
async function flushStreamBuffer(channelId, threadTs, client, streamState, callbacks, streamKeyTs) {
|
|
102705
|
+
const key = streamKey(channelId, streamKeyTs);
|
|
102706
|
+
const existing = streamState.get(key);
|
|
102707
|
+
if (!existing || !existing.accumulatedText)
|
|
102708
|
+
return;
|
|
102709
|
+
if (existing.nativeStreamId) {
|
|
102710
|
+
const slackClient = client;
|
|
102711
|
+
try {
|
|
102712
|
+
await slackClient.chat.stopStream({ stream_id: existing.nativeStreamId });
|
|
102713
|
+
} catch {
|
|
102714
|
+
}
|
|
102715
|
+
existing.nativeStreamId = void 0;
|
|
102716
|
+
return;
|
|
102717
|
+
}
|
|
102718
|
+
if (!existing.messageTs) {
|
|
102719
|
+
try {
|
|
102720
|
+
const result2 = await client.chat.postMessage({
|
|
102721
|
+
channel: channelId,
|
|
102722
|
+
text: truncateText(formatForPlatform(existing.accumulatedText, "slack"), MAX_MESSAGE_LENGTH2),
|
|
102723
|
+
...threadTs ? { thread_ts: threadTs } : {}
|
|
102724
|
+
});
|
|
102725
|
+
existing.messageTs = result2.ts ?? "";
|
|
102726
|
+
existing.lastUpdateAt = Date.now();
|
|
102727
|
+
} catch (err) {
|
|
102728
|
+
callbacks.recordError(err);
|
|
102729
|
+
}
|
|
102730
|
+
return;
|
|
102731
|
+
}
|
|
102732
|
+
try {
|
|
102733
|
+
await client.chat.update({
|
|
102734
|
+
channel: channelId,
|
|
102735
|
+
ts: existing.messageTs,
|
|
102736
|
+
text: truncateText(formatForPlatform(existing.accumulatedText, "slack"), MAX_MESSAGE_LENGTH2)
|
|
102737
|
+
});
|
|
102738
|
+
existing.lastUpdateAt = Date.now();
|
|
102739
|
+
} catch (err) {
|
|
102740
|
+
callbacks.recordError(err);
|
|
102741
|
+
}
|
|
102742
|
+
}
|
|
101913
102743
|
async function handleDone(channelId, threadTs, client, streamState, callbacks, startTime, typingIndicator, streamKeyTs) {
|
|
101914
102744
|
const key = streamKey(channelId, streamKeyTs);
|
|
101915
102745
|
const existing = streamState.get(key);
|
|
@@ -101920,6 +102750,10 @@ async function handleDone(channelId, threadTs, client, streamState, callbacks, s
|
|
|
101920
102750
|
if (!existing) {
|
|
101921
102751
|
return { success: true, durationMs: Date.now() - startTime };
|
|
101922
102752
|
}
|
|
102753
|
+
if (existing.nativeStreamId) {
|
|
102754
|
+
const slackClient = client;
|
|
102755
|
+
return wrapSlackCall(() => slackClient.chat.stopStream({ stream_id: existing.nativeStreamId }), callbacks, startTime, true);
|
|
102756
|
+
}
|
|
101923
102757
|
if (!existing.messageTs) {
|
|
101924
102758
|
return wrapSlackCall(() => client.chat.postMessage({
|
|
101925
102759
|
channel: channelId,
|
|
@@ -101941,6 +102775,20 @@ async function handleError(channelId, errorMsg, threadTs, client, streamState, c
|
|
|
101941
102775
|
removeTypingReaction(client, channelId, existing.threadTs, typingIndicator);
|
|
101942
102776
|
}
|
|
101943
102777
|
if (existing) {
|
|
102778
|
+
if (existing.nativeStreamId) {
|
|
102779
|
+
const slackClient = client;
|
|
102780
|
+
const errorSuffix = formatForPlatform(`
|
|
102781
|
+
|
|
102782
|
+
[Error: ${errorMsg}]`, "slack");
|
|
102783
|
+
try {
|
|
102784
|
+
await slackClient.chat.appendStream({
|
|
102785
|
+
stream_id: existing.nativeStreamId,
|
|
102786
|
+
text: errorSuffix
|
|
102787
|
+
});
|
|
102788
|
+
} catch {
|
|
102789
|
+
}
|
|
102790
|
+
return wrapSlackCall(() => slackClient.chat.stopStream({ stream_id: existing.nativeStreamId }), callbacks, startTime, true);
|
|
102791
|
+
}
|
|
101944
102792
|
const finalText = truncateText(`${formatForPlatform(existing.accumulatedText, "slack")}
|
|
101945
102793
|
|
|
101946
102794
|
[Error: ${errorMsg}]`, MAX_MESSAGE_LENGTH2);
|
|
@@ -101965,14 +102813,16 @@ async function handleError(channelId, errorMsg, threadTs, client, streamState, c
|
|
|
101965
102813
|
}), callbacks, startTime, true);
|
|
101966
102814
|
}
|
|
101967
102815
|
async function deliverMessage2(opts) {
|
|
101968
|
-
const { adapterId, subject, envelope, client, streamState, callbacks } = opts;
|
|
102816
|
+
const { adapterId, subject, envelope, client, streamState, callbacks, logger: logger3 = noopLogger } = opts;
|
|
101969
102817
|
const startTime = Date.now();
|
|
101970
102818
|
for (const [key, stream] of streamState) {
|
|
101971
102819
|
if (startTime - stream.startedAt > STREAM_TTL_MS) {
|
|
101972
102820
|
streamState.delete(key);
|
|
102821
|
+
logger3.warn(`stream: reaped orphaned stream for ${key} (age: ${Math.round((startTime - stream.startedAt) / 1e3)}s)`);
|
|
101973
102822
|
}
|
|
101974
102823
|
}
|
|
101975
102824
|
if (envelope.from.startsWith(SUBJECT_PREFIX2)) {
|
|
102825
|
+
logger3.debug("deliver: echo prevention \u2014 skipping self-originated message");
|
|
101976
102826
|
return { success: true, durationMs: Date.now() - startTime };
|
|
101977
102827
|
}
|
|
101978
102828
|
if (!client) {
|
|
@@ -101998,28 +102848,135 @@ async function deliverMessage2(opts) {
|
|
|
101998
102848
|
if (eventType) {
|
|
101999
102849
|
const textChunk = extractTextDelta(envelope.payload);
|
|
102000
102850
|
if (textChunk) {
|
|
102001
|
-
|
|
102851
|
+
logger3.debug(`deliver: text_delta to ${channelId} (${textChunk.length} chars, streaming=${opts.streaming ? opts.nativeStreaming ? "native" : "legacy" : "buffered"})`);
|
|
102852
|
+
return handleTextDelta(channelId, textChunk, threadTs, client, streamState, callbacks, startTime, opts.streaming, opts.nativeStreaming, opts.typingIndicator, streamKeyTs);
|
|
102002
102853
|
}
|
|
102003
102854
|
const errorMsg = extractErrorMessage(envelope.payload);
|
|
102004
102855
|
if (errorMsg) {
|
|
102856
|
+
logger3.debug(`deliver: error to ${channelId}: "${errorMsg.slice(0, 100)}"`);
|
|
102005
102857
|
return handleError(channelId, errorMsg, threadTs, client, streamState, callbacks, startTime, opts.typingIndicator, streamKeyTs);
|
|
102006
102858
|
}
|
|
102007
102859
|
if (eventType === "done") {
|
|
102860
|
+
logger3.debug(`deliver: done for ${channelId}`);
|
|
102008
102861
|
return handleDone(channelId, threadTs, client, streamState, callbacks, startTime, opts.typingIndicator, streamKeyTs);
|
|
102009
102862
|
}
|
|
102010
|
-
if (
|
|
102011
|
-
|
|
102863
|
+
if (eventType === "approval_required") {
|
|
102864
|
+
const approvalData = extractApprovalData(envelope.payload);
|
|
102865
|
+
if (approvalData) {
|
|
102866
|
+
logger3.debug(`deliver: approval_required for tool '${approvalData.toolName}' to ${channelId}`);
|
|
102867
|
+
await flushStreamBuffer(channelId, threadTs, client, streamState, callbacks, streamKeyTs);
|
|
102868
|
+
return handleApprovalRequired2(channelId, threadTs, approvalData, envelope, client, callbacks, startTime);
|
|
102869
|
+
}
|
|
102012
102870
|
}
|
|
102871
|
+
logger3.debug(`deliver: dropping stream event '${eventType}' (whitelist)`);
|
|
102872
|
+
return { success: true, durationMs: Date.now() - startTime };
|
|
102013
102873
|
}
|
|
102014
102874
|
const content3 = extractPayloadContent(envelope.payload);
|
|
102015
102875
|
const mrkdwn = formatForPlatform(content3, "slack");
|
|
102016
102876
|
const text6 = truncateText(mrkdwn, MAX_MESSAGE_LENGTH2);
|
|
102877
|
+
logger3.debug(`deliver: standard payload to ${channelId} (${text6.length} chars)`);
|
|
102017
102878
|
return wrapSlackCall(() => client.chat.postMessage({
|
|
102018
102879
|
channel: channelId,
|
|
102019
102880
|
text: text6,
|
|
102020
102881
|
...threadTs ? { thread_ts: threadTs } : {}
|
|
102021
102882
|
}), callbacks, startTime, true);
|
|
102022
102883
|
}
|
|
102884
|
+
function extractAgentIdFromEnvelope2(envelope) {
|
|
102885
|
+
const payload = envelope.payload;
|
|
102886
|
+
const data = payload?.data;
|
|
102887
|
+
return data?.agentId ?? "unknown";
|
|
102888
|
+
}
|
|
102889
|
+
function extractSessionIdFromEnvelope2(envelope) {
|
|
102890
|
+
const payload = envelope.payload;
|
|
102891
|
+
const data = payload?.data;
|
|
102892
|
+
return data?.ccaSessionKey ?? "unknown";
|
|
102893
|
+
}
|
|
102894
|
+
async function handleApprovalRequired2(channelId, threadTs, data, envelope, client, callbacks, startTime) {
|
|
102895
|
+
const agentId = extractAgentIdFromEnvelope2(envelope);
|
|
102896
|
+
const sessionId = extractSessionIdFromEnvelope2(envelope);
|
|
102897
|
+
const inputPreview = truncateText(data.input, 500);
|
|
102898
|
+
const toolDescription = formatToolDescription(data.toolName, data.input);
|
|
102899
|
+
const buttonValue = JSON.stringify({
|
|
102900
|
+
toolCallId: data.toolCallId,
|
|
102901
|
+
sessionId,
|
|
102902
|
+
agentId
|
|
102903
|
+
});
|
|
102904
|
+
let postedTs;
|
|
102905
|
+
const result2 = await wrapSlackCall(async () => {
|
|
102906
|
+
const res = await client.chat.postMessage({
|
|
102907
|
+
channel: channelId,
|
|
102908
|
+
...threadTs ? { thread_ts: threadTs } : {},
|
|
102909
|
+
text: `Tool approval required: ${data.toolName} (fallback)`,
|
|
102910
|
+
blocks: [
|
|
102911
|
+
{
|
|
102912
|
+
type: "section",
|
|
102913
|
+
text: {
|
|
102914
|
+
type: "mrkdwn",
|
|
102915
|
+
text: `*Tool Approval Required*
|
|
102916
|
+
\`${data.toolName}\` ${toolDescription}`
|
|
102917
|
+
}
|
|
102918
|
+
},
|
|
102919
|
+
{
|
|
102920
|
+
type: "section",
|
|
102921
|
+
text: {
|
|
102922
|
+
type: "mrkdwn",
|
|
102923
|
+
text: `\`\`\`
|
|
102924
|
+
${inputPreview}
|
|
102925
|
+
\`\`\``
|
|
102926
|
+
}
|
|
102927
|
+
},
|
|
102928
|
+
{
|
|
102929
|
+
type: "actions",
|
|
102930
|
+
block_id: "tool_approval",
|
|
102931
|
+
elements: [
|
|
102932
|
+
{
|
|
102933
|
+
type: "button",
|
|
102934
|
+
text: { type: "plain_text", text: "Approve" },
|
|
102935
|
+
style: "primary",
|
|
102936
|
+
action_id: "tool_approve",
|
|
102937
|
+
value: buttonValue
|
|
102938
|
+
},
|
|
102939
|
+
{
|
|
102940
|
+
type: "button",
|
|
102941
|
+
text: { type: "plain_text", text: "Deny" },
|
|
102942
|
+
style: "danger",
|
|
102943
|
+
action_id: "tool_deny",
|
|
102944
|
+
value: buttonValue
|
|
102945
|
+
}
|
|
102946
|
+
]
|
|
102947
|
+
}
|
|
102948
|
+
]
|
|
102949
|
+
});
|
|
102950
|
+
postedTs = res.ts;
|
|
102951
|
+
}, callbacks, startTime, true);
|
|
102952
|
+
if (result2.success && postedTs && data.timeoutMs > 0) {
|
|
102953
|
+
const msgTs = postedTs;
|
|
102954
|
+
const timer = setTimeout(async () => {
|
|
102955
|
+
pendingApprovalTimeouts2.delete(data.toolCallId);
|
|
102956
|
+
try {
|
|
102957
|
+
await client.chat.update({
|
|
102958
|
+
channel: channelId,
|
|
102959
|
+
ts: msgTs,
|
|
102960
|
+
text: ":hourglass: Tool approval timed out",
|
|
102961
|
+
blocks: [
|
|
102962
|
+
{
|
|
102963
|
+
type: "section",
|
|
102964
|
+
text: { type: "mrkdwn", text: ":hourglass: *Tool Approval Timed Out*\n~`" + data.toolName + "`~" }
|
|
102965
|
+
}
|
|
102966
|
+
]
|
|
102967
|
+
});
|
|
102968
|
+
} catch {
|
|
102969
|
+
}
|
|
102970
|
+
}, data.timeoutMs);
|
|
102971
|
+
pendingApprovalTimeouts2.set(data.toolCallId, {
|
|
102972
|
+
timer,
|
|
102973
|
+
channelId,
|
|
102974
|
+
messageTs: msgTs,
|
|
102975
|
+
client
|
|
102976
|
+
});
|
|
102977
|
+
}
|
|
102978
|
+
return result2;
|
|
102979
|
+
}
|
|
102023
102980
|
|
|
102024
102981
|
// ../relay/dist/adapters/slack/slack-adapter.js
|
|
102025
102982
|
var SLACK_APP_MANIFEST_YAML = `display_information:
|
|
@@ -102070,7 +103027,7 @@ var SLACK_MANIFEST = {
|
|
|
102070
103027
|
stepId: "create-app",
|
|
102071
103028
|
title: "Create & Configure a Slack App",
|
|
102072
103029
|
description: 'Go to api.slack.com/apps \u2192 Create New App \u2192 From Scratch.\n\n1. **Socket Mode** \u2014 Enable it (Settings \u2192 Socket Mode).\n2. **Event Subscriptions** \u2014 Turn on Enable Events, then subscribe to bot events: message.channels, message.groups, message.im, app_mention.\n3. **OAuth & Permissions** \u2014 Add bot token scopes: channels:history, channels:read, chat:write, groups:history, groups:read, im:history, im:read, im:write, mpim:history, app_mentions:read, users:read, reactions:write. Then install the app to your workspace.\n4. **App-Level Token** \u2014 In Basic Information \u2192 App-Level Tokens, generate a token with the connections:write scope.\n\n\u26A0\uFE0F Do NOT enable "Agents & AI Apps" \u2014 it adds user scopes that cause install failures on most workspaces.',
|
|
102073
|
-
fields: ["botToken", "appToken", "signingSecret", "streaming", "typingIndicator"]
|
|
103030
|
+
fields: ["botToken", "appToken", "signingSecret", "streaming", "nativeStreaming", "typingIndicator"]
|
|
102074
103031
|
}
|
|
102075
103032
|
],
|
|
102076
103033
|
configFields: [
|
|
@@ -102129,6 +103086,15 @@ var SLACK_MANIFEST = {
|
|
|
102129
103086
|
visibleByDefault: true,
|
|
102130
103087
|
helpMarkdown: "When enabled, agent responses appear token-by-token in Slack via message editing. When disabled, the full response is sent as a single message after the agent finishes."
|
|
102131
103088
|
},
|
|
103089
|
+
{
|
|
103090
|
+
key: "nativeStreaming",
|
|
103091
|
+
label: "Native Streaming",
|
|
103092
|
+
type: "boolean",
|
|
103093
|
+
required: false,
|
|
103094
|
+
description: "Use Slack's native streaming API (chat.startStream/appendStream/stopStream). Requires messages in threads.",
|
|
103095
|
+
visibleByDefault: true,
|
|
103096
|
+
helpMarkdown: "When enabled, uses Slack's purpose-built streaming API for smoother, flicker-free responses. When disabled, uses the legacy chat.update approach. Only applies when Stream Responses is enabled."
|
|
103097
|
+
},
|
|
102132
103098
|
{
|
|
102133
103099
|
key: "typingIndicator",
|
|
102134
103100
|
label: "Typing Indicator",
|
|
@@ -102183,10 +103149,18 @@ var SlackAdapter = class extends BaseRelayAdapter {
|
|
|
102183
103149
|
const authResult = await app.client.auth.test();
|
|
102184
103150
|
this.botUserId = authResult.user_id ?? "";
|
|
102185
103151
|
app.message(async ({ event, client }) => {
|
|
102186
|
-
await handleInboundMessage2(event, client, relay, this.botUserId, this.makeInboundCallbacks());
|
|
103152
|
+
await handleInboundMessage2(event, client, relay, this.botUserId, this.makeInboundCallbacks(), this.logger);
|
|
102187
103153
|
});
|
|
102188
103154
|
app.event("app_mention", async ({ event, client }) => {
|
|
102189
|
-
await handleInboundMessage2(event, client, relay, this.botUserId, this.makeInboundCallbacks());
|
|
103155
|
+
await handleInboundMessage2(event, client, relay, this.botUserId, this.makeInboundCallbacks(), this.logger);
|
|
103156
|
+
});
|
|
103157
|
+
app.action("tool_approve", async ({ ack, action, body, client }) => {
|
|
103158
|
+
await ack();
|
|
103159
|
+
await this.handleToolAction(true, action, body, client, relay);
|
|
103160
|
+
});
|
|
103161
|
+
app.action("tool_deny", async ({ ack, action, body, client }) => {
|
|
103162
|
+
await ack();
|
|
103163
|
+
await this.handleToolAction(false, action, body, client, relay);
|
|
102190
103164
|
});
|
|
102191
103165
|
app.error(async (error) => {
|
|
102192
103166
|
this.recordError(error);
|
|
@@ -102226,7 +103200,9 @@ var SlackAdapter = class extends BaseRelayAdapter {
|
|
|
102226
103200
|
botUserId: this.botUserId,
|
|
102227
103201
|
callbacks: this.makeOutboundCallbacks(),
|
|
102228
103202
|
streaming: this.config.streaming ?? true,
|
|
102229
|
-
|
|
103203
|
+
nativeStreaming: this.config.nativeStreaming ?? true,
|
|
103204
|
+
typingIndicator: this.config.typingIndicator ?? "none",
|
|
103205
|
+
logger: this.logger
|
|
102230
103206
|
});
|
|
102231
103207
|
}
|
|
102232
103208
|
/** Build callbacks for inbound message handling. */
|
|
@@ -102243,6 +103219,63 @@ var SlackAdapter = class extends BaseRelayAdapter {
|
|
|
102243
103219
|
recordError: (err) => this.recordError(err)
|
|
102244
103220
|
};
|
|
102245
103221
|
}
|
|
103222
|
+
/**
|
|
103223
|
+
* Handle a tool approval or denial action from Slack interactive buttons.
|
|
103224
|
+
*
|
|
103225
|
+
* Parses the button value JSON, publishes an `approval_response` to the
|
|
103226
|
+
* relay bus, and updates the original Slack message to reflect the decision.
|
|
103227
|
+
*
|
|
103228
|
+
* @param approved - Whether the user clicked Approve (true) or Deny (false)
|
|
103229
|
+
* @param action - The Bolt action payload
|
|
103230
|
+
* @param body - The Bolt body payload containing message context
|
|
103231
|
+
* @param client - The Slack WebClient for updating messages
|
|
103232
|
+
* @param relay - The relay publisher for publishing approval responses
|
|
103233
|
+
*/
|
|
103234
|
+
async handleToolAction(approved, action, body, client, relay) {
|
|
103235
|
+
try {
|
|
103236
|
+
const btnAction = action;
|
|
103237
|
+
const btnBody = body;
|
|
103238
|
+
if (!btnAction.value) {
|
|
103239
|
+
this.logger.warn("[Slack] tool action missing button value");
|
|
103240
|
+
return;
|
|
103241
|
+
}
|
|
103242
|
+
const { toolCallId, sessionId, agentId } = JSON.parse(btnAction.value);
|
|
103243
|
+
clearApprovalTimeout2(toolCallId);
|
|
103244
|
+
const opts = { from: `slack:${btnBody.user?.id ?? "unknown"}` };
|
|
103245
|
+
await relay.publish(`relay.system.approval.${agentId}`, {
|
|
103246
|
+
type: "approval_response",
|
|
103247
|
+
toolCallId,
|
|
103248
|
+
sessionId,
|
|
103249
|
+
approved,
|
|
103250
|
+
respondedBy: btnBody.user?.id,
|
|
103251
|
+
platform: "slack"
|
|
103252
|
+
}, opts);
|
|
103253
|
+
const channelId = btnBody.channel?.id;
|
|
103254
|
+
const messageTs = btnBody.message?.ts;
|
|
103255
|
+
if (channelId && messageTs) {
|
|
103256
|
+
const decision = approved ? "Approved" : "Denied";
|
|
103257
|
+
const emoji = approved ? ":white_check_mark:" : ":x:";
|
|
103258
|
+
await client.chat.update({
|
|
103259
|
+
channel: channelId,
|
|
103260
|
+
ts: messageTs,
|
|
103261
|
+
text: `${emoji} Tool ${decision} by <@${btnBody.user?.id ?? "unknown"}>`,
|
|
103262
|
+
blocks: [
|
|
103263
|
+
{
|
|
103264
|
+
type: "section",
|
|
103265
|
+
text: {
|
|
103266
|
+
type: "mrkdwn",
|
|
103267
|
+
text: `${emoji} *Tool ${decision}* by <@${btnBody.user?.id ?? "unknown"}>`
|
|
103268
|
+
}
|
|
103269
|
+
}
|
|
103270
|
+
]
|
|
103271
|
+
});
|
|
103272
|
+
}
|
|
103273
|
+
this.logger.debug?.(`[Slack] tool ${approved ? "approved" : "denied"}: toolCallId=${toolCallId}`);
|
|
103274
|
+
} catch (err) {
|
|
103275
|
+
this.logger.error("[Slack] tool action handler error:", err);
|
|
103276
|
+
this.recordError(err);
|
|
103277
|
+
}
|
|
103278
|
+
}
|
|
102246
103279
|
};
|
|
102247
103280
|
|
|
102248
103281
|
// ../relay/dist/adapters/claude-code/agent-handler.js
|
|
@@ -102269,7 +103302,7 @@ async function publishDispatchProgress(originalEnvelope, step, step_type, text6,
|
|
|
102269
103302
|
};
|
|
102270
103303
|
await relay.publish(originalEnvelope.replyTo, { type: "progress", step, step_type, text: text6, done: false }, opts);
|
|
102271
103304
|
}
|
|
102272
|
-
async function publishResponseWithCorrelation(originalEnvelope, event, fromId, relay, log, correlationId) {
|
|
103305
|
+
async function publishResponseWithCorrelation(originalEnvelope, event, fromId, relay, log, correlationId, enrichment) {
|
|
102273
103306
|
if (!originalEnvelope.replyTo)
|
|
102274
103307
|
return;
|
|
102275
103308
|
const opts = {
|
|
@@ -102278,7 +103311,20 @@ async function publishResponseWithCorrelation(originalEnvelope, event, fromId, r
|
|
|
102278
103311
|
hopCount: originalEnvelope.budget.hopCount + 1
|
|
102279
103312
|
}
|
|
102280
103313
|
};
|
|
102281
|
-
|
|
103314
|
+
let payload;
|
|
103315
|
+
if (event.type === "approval_required" && enrichment?.agentId) {
|
|
103316
|
+
payload = {
|
|
103317
|
+
...event,
|
|
103318
|
+
...correlationId ? { correlationId } : {},
|
|
103319
|
+
data: {
|
|
103320
|
+
...event.data,
|
|
103321
|
+
agentId: enrichment.agentId,
|
|
103322
|
+
ccaSessionKey: fromId
|
|
103323
|
+
}
|
|
103324
|
+
};
|
|
103325
|
+
} else {
|
|
103326
|
+
payload = correlationId ? { ...event, correlationId } : event;
|
|
103327
|
+
}
|
|
102282
103328
|
const result2 = await relay.publish(originalEnvelope.replyTo, payload, opts);
|
|
102283
103329
|
if (result2.deliveredTo === 0 && event.type !== "done") {
|
|
102284
103330
|
log.warn(`[CCA] publishResponse delivered to 0 subscribers: subject=${originalEnvelope.replyTo}, eventType=${event.type}`);
|
|
@@ -102331,7 +103377,7 @@ async function handleAgentMessage(subject, envelope, context, startTime, config,
|
|
|
102331
103377
|
log.debug?.(`[CCA] handleAgentMessage agentId=${agentId} ccaSessionKey=${ccaSessionKey}, payloadCwd=${payloadCwd ?? "(none)"}, context.agent.directory=${context?.agent?.directory ?? "(none)"}, resolvedCwd=${effectiveCwd ?? "(deferred to session)"}`);
|
|
102332
103378
|
deps.agentManager.ensureSession(ccaSessionKey, {
|
|
102333
103379
|
permissionMode: "default",
|
|
102334
|
-
hasStarted:
|
|
103380
|
+
hasStarted: !!persistedSdkSessionId,
|
|
102335
103381
|
...effectiveCwd ? { cwd: effectiveCwd } : {}
|
|
102336
103382
|
});
|
|
102337
103383
|
deps.traceStore.updateSpan(envelope.id, { status: "delivered", deliveredAt: Date.now() });
|
|
@@ -102380,7 +103426,7 @@ async function handleAgentMessage(subject, envelope, context, startTime, config,
|
|
|
102380
103426
|
await publishDispatchProgress(envelope, stepCounter, "tool_result", typeof data.content === "string" ? data.content : JSON.stringify(data), ccaSessionKey, relay);
|
|
102381
103427
|
}
|
|
102382
103428
|
} else {
|
|
102383
|
-
await publishResponseWithCorrelation(envelope, event, ccaSessionKey, relay, log, correlationId);
|
|
103429
|
+
await publishResponseWithCorrelation(envelope, event, ccaSessionKey, relay, log, correlationId, { agentId });
|
|
102384
103430
|
}
|
|
102385
103431
|
}
|
|
102386
103432
|
}
|
|
@@ -102410,6 +103456,8 @@ async function handleAgentMessage(subject, envelope, context, startTime, config,
|
|
|
102410
103456
|
if (actualSdkId && actualSdkId !== agentId) {
|
|
102411
103457
|
deps.agentSessionStore.set(agentId, actualSdkId);
|
|
102412
103458
|
log.debug?.(`[CCA] persisted session mapping: ${agentId} \u2192 ${actualSdkId}`);
|
|
103459
|
+
} else {
|
|
103460
|
+
log.debug?.(`[CCA] no session mapping to persist: agentId=${agentId}, ccaSessionKey=${ccaSessionKey}, actualSdkId=${actualSdkId ?? "(none)"}`);
|
|
102413
103461
|
}
|
|
102414
103462
|
}
|
|
102415
103463
|
log.info(`ClaudeCodeAdapter: published ${eventCount} event(s) to ${envelope.replyTo ?? "(no replyTo)"}`);
|
|
@@ -102435,6 +103483,22 @@ function extractAgentId(subject) {
|
|
|
102435
103483
|
return null;
|
|
102436
103484
|
return segments[2] || null;
|
|
102437
103485
|
}
|
|
103486
|
+
var PLATFORM_FORMATTING = {
|
|
103487
|
+
slack: [
|
|
103488
|
+
"Platform: Slack (mrkdwn format)",
|
|
103489
|
+
"Formatting rules:",
|
|
103490
|
+
"- Use *bold* (single asterisk), _italic_, ~strikethrough~, `code`",
|
|
103491
|
+
"- Do NOT use Markdown tables (| col | col |) \u2014 Slack cannot render them",
|
|
103492
|
+
"- For structured data, use bullet lists with bold labels instead",
|
|
103493
|
+
"- Keep responses under 4000 characters"
|
|
103494
|
+
].join("\n"),
|
|
103495
|
+
telegram: [
|
|
103496
|
+
"Platform: Telegram",
|
|
103497
|
+
"Formatting rules:",
|
|
103498
|
+
"- Use standard Markdown: **bold**, _italic_, `code`",
|
|
103499
|
+
"- Keep responses under 4096 characters"
|
|
103500
|
+
].join("\n")
|
|
103501
|
+
};
|
|
102438
103502
|
function formatPromptWithContext(content3, envelope, agentId, sdkSessionId) {
|
|
102439
103503
|
const lines = [
|
|
102440
103504
|
`Agent-ID: ${agentId}`,
|
|
@@ -102452,6 +103516,12 @@ function formatPromptWithContext(content3, envelope, agentId, sdkSessionId) {
|
|
|
102452
103516
|
if (envelope.replyTo) {
|
|
102453
103517
|
lines.push("", `Reply to: ${envelope.replyTo}`, "If you cannot complete the task within the budget, summarize what you've done and stop.");
|
|
102454
103518
|
}
|
|
103519
|
+
const payloadObj = typeof envelope.payload === "object" && envelope.payload !== null ? envelope.payload : null;
|
|
103520
|
+
const responseContext = payloadObj?.responseContext;
|
|
103521
|
+
const platform2 = responseContext?.platform;
|
|
103522
|
+
if (platform2 && PLATFORM_FORMATTING[platform2]) {
|
|
103523
|
+
lines.push("", PLATFORM_FORMATTING[platform2]);
|
|
103524
|
+
}
|
|
102455
103525
|
return `<relay_context>
|
|
102456
103526
|
${lines.join("\n")}
|
|
102457
103527
|
</relay_context>
|
|
@@ -102649,6 +103719,48 @@ var AgentQueue = class {
|
|
|
102649
103719
|
}
|
|
102650
103720
|
};
|
|
102651
103721
|
|
|
103722
|
+
// ../relay/dist/adapters/claude-code/approval-handler.js
|
|
103723
|
+
var APPROVAL_SUBJECT_PATTERN = "relay.system.approval.>";
|
|
103724
|
+
function parseApprovalPayload(payload) {
|
|
103725
|
+
if (payload === null || typeof payload !== "object")
|
|
103726
|
+
return null;
|
|
103727
|
+
const obj = payload;
|
|
103728
|
+
if (obj.type !== "approval_response")
|
|
103729
|
+
return null;
|
|
103730
|
+
if (typeof obj.toolCallId !== "string" || !obj.toolCallId)
|
|
103731
|
+
return null;
|
|
103732
|
+
if (typeof obj.sessionId !== "string" || !obj.sessionId)
|
|
103733
|
+
return null;
|
|
103734
|
+
if (typeof obj.approved !== "boolean")
|
|
103735
|
+
return null;
|
|
103736
|
+
return {
|
|
103737
|
+
type: "approval_response",
|
|
103738
|
+
toolCallId: obj.toolCallId,
|
|
103739
|
+
sessionId: obj.sessionId,
|
|
103740
|
+
approved: obj.approved,
|
|
103741
|
+
respondedBy: typeof obj.respondedBy === "string" ? obj.respondedBy : void 0,
|
|
103742
|
+
platform: typeof obj.platform === "string" ? obj.platform : void 0
|
|
103743
|
+
};
|
|
103744
|
+
}
|
|
103745
|
+
function handleApprovalResponse(envelope, agentManager, log) {
|
|
103746
|
+
const approval = parseApprovalPayload(envelope.payload);
|
|
103747
|
+
if (!approval) {
|
|
103748
|
+
log.warn(`[CCA] approval-handler: received malformed payload on ${envelope.subject} \u2014 expected type='approval_response' with toolCallId, sessionId, approved`);
|
|
103749
|
+
return;
|
|
103750
|
+
}
|
|
103751
|
+
const { toolCallId, sessionId, approved, platform: platform2 = "unknown" } = approval;
|
|
103752
|
+
log.debug?.(`[CCA] approval-handler: ${approved ? "approve" : "deny"} toolCallId=${toolCallId} sessionId=${sessionId} platform=${platform2}`);
|
|
103753
|
+
const resolved = agentManager.approveTool(sessionId, toolCallId, approved);
|
|
103754
|
+
if (!resolved) {
|
|
103755
|
+
log.warn(`[CCA] approval-handler: approveTool returned false \u2014 interaction not found (already timed out?) toolCallId=${toolCallId} sessionId=${sessionId}`);
|
|
103756
|
+
}
|
|
103757
|
+
}
|
|
103758
|
+
function subscribeApprovalHandler(relay, agentManager, log) {
|
|
103759
|
+
return relay.subscribe(APPROVAL_SUBJECT_PATTERN, (envelope) => {
|
|
103760
|
+
handleApprovalResponse(envelope, agentManager, log);
|
|
103761
|
+
});
|
|
103762
|
+
}
|
|
103763
|
+
|
|
102652
103764
|
// ../relay/dist/adapters/claude-code/claude-code-adapter.js
|
|
102653
103765
|
var CLAUDE_CODE_MANIFEST = {
|
|
102654
103766
|
type: "claude-code",
|
|
@@ -102688,6 +103800,8 @@ var ClaudeCodeAdapter = class {
|
|
|
102688
103800
|
relay = null;
|
|
102689
103801
|
activeCount = 0;
|
|
102690
103802
|
agentQueue = new AgentQueue();
|
|
103803
|
+
/** Unsubscribe function for the `relay.system.approval.>` subscription. */
|
|
103804
|
+
approvalUnsub = null;
|
|
102691
103805
|
status = {
|
|
102692
103806
|
state: "disconnected",
|
|
102693
103807
|
messageCount: { inbound: 0, outbound: 0 },
|
|
@@ -102716,6 +103830,7 @@ var ClaudeCodeAdapter = class {
|
|
|
102716
103830
|
*/
|
|
102717
103831
|
async start(relay) {
|
|
102718
103832
|
this.relay = relay;
|
|
103833
|
+
this.approvalUnsub = subscribeApprovalHandler(relay, this.deps.agentManager, this.deps.logger ?? console);
|
|
102719
103834
|
this.status = {
|
|
102720
103835
|
state: "connected",
|
|
102721
103836
|
messageCount: { inbound: 0, outbound: 0 },
|
|
@@ -102727,6 +103842,8 @@ var ClaudeCodeAdapter = class {
|
|
|
102727
103842
|
* Stop the adapter — clear relay reference, drain in-flight queue entries, and mark as disconnected.
|
|
102728
103843
|
*/
|
|
102729
103844
|
async stop() {
|
|
103845
|
+
this.approvalUnsub?.();
|
|
103846
|
+
this.approvalUnsub = null;
|
|
102730
103847
|
this.relay = null;
|
|
102731
103848
|
this.agentQueue.clear();
|
|
102732
103849
|
this.status = { ...this.status, state: "disconnected" };
|
|
@@ -103637,21 +104754,27 @@ function defaultAdapterStatus() {
|
|
|
103637
104754
|
}
|
|
103638
104755
|
async function createAdapter(config, deps, configPath, onPluginManifest) {
|
|
103639
104756
|
switch (config.type) {
|
|
103640
|
-
case "telegram":
|
|
103641
|
-
|
|
104757
|
+
case "telegram": {
|
|
104758
|
+
const adapter = new TelegramAdapter(
|
|
103642
104759
|
config.id,
|
|
103643
104760
|
config.config
|
|
103644
104761
|
);
|
|
104762
|
+
adapter.setLogger(createTaggedLogger(`telegram:${config.id}`));
|
|
104763
|
+
return adapter;
|
|
104764
|
+
}
|
|
103645
104765
|
case "webhook":
|
|
103646
104766
|
return new WebhookAdapter(
|
|
103647
104767
|
config.id,
|
|
103648
104768
|
config.config
|
|
103649
104769
|
);
|
|
103650
|
-
case "slack":
|
|
103651
|
-
|
|
104770
|
+
case "slack": {
|
|
104771
|
+
const adapter = new SlackAdapter(
|
|
103652
104772
|
config.id,
|
|
103653
104773
|
config.config
|
|
103654
104774
|
);
|
|
104775
|
+
adapter.setLogger(createTaggedLogger(`slack:${config.id}`));
|
|
104776
|
+
return adapter;
|
|
104777
|
+
}
|
|
103655
104778
|
case "claude-code":
|
|
103656
104779
|
return new ClaudeCodeAdapter(
|
|
103657
104780
|
config.id,
|
|
@@ -103693,6 +104816,8 @@ async function testAdapterConnection(adapter) {
|
|
|
103693
104816
|
const noopRelay = {
|
|
103694
104817
|
publish: async () => ({ messageId: "", deliveredTo: 0 }),
|
|
103695
104818
|
onSignal: () => () => {
|
|
104819
|
+
},
|
|
104820
|
+
subscribe: () => () => {
|
|
103696
104821
|
}
|
|
103697
104822
|
};
|
|
103698
104823
|
let fallbackTimer;
|