@band-ai/sdk 0.1.10 → 0.2.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/README.md +54 -54
- package/dist/{ClaudeSDKAdapter-DfjaI5ke.d.cts → ClaudeSDKAdapter-BY4zEbCl.d.cts} +12 -10
- package/dist/{ClaudeSDKAdapter-DonPGJ43.d.ts → ClaudeSDKAdapter-Cna5P1dR.d.ts} +12 -10
- package/dist/{acp-server-NZAeg-WO.d.ts → acp-server-CiUqN3G3.d.ts} +2 -2
- package/dist/{acp-server-CUBXIiG6.d.cts → acp-server-Dlj7D563.d.cts} +2 -2
- package/dist/adapters.cjs +250 -183
- package/dist/adapters.d.cts +21 -19
- package/dist/adapters.d.ts +21 -19
- package/dist/adapters.js +65 -44
- package/dist/backends-D-dBdDm-.d.ts +27 -0
- package/dist/backends-ZLxmQrTZ.d.cts +27 -0
- package/dist/{chunk-2BHHR6YW.js → chunk-4IAXZBXX.js} +62 -23
- package/dist/{chunk-SG4WLD2H.js → chunk-EAI2M3WM.js} +38 -94
- package/dist/{chunk-YR3BOQNW.js → chunk-JY75VSQK.js} +7 -7
- package/dist/{chunk-RDM4TE4B.js → chunk-LBWKISBS.js} +19 -1
- package/dist/{chunk-FP237EAG.js → chunk-N2QTUMGO.js} +112 -195
- package/dist/{chunk-J3M3PFFY.js → chunk-NMZLX7TP.js} +35 -31
- package/dist/{chunk-6RXWL6TC.js → chunk-NT7TBFX2.js} +1 -1
- package/dist/{chunk-MLROFDKR.js → chunk-RV76SKFT.js} +39 -33
- package/dist/{chunk-XA4N2JPH.js → chunk-TLBCKDQO.js} +23 -23
- package/dist/{chunk-IZPV6QS6.js → chunk-WL2HNJZ6.js} +38 -11
- package/dist/{chunk-DNPVCR3S.js → chunk-WLCM2OXG.js} +31 -31
- package/dist/config.cjs +40 -13
- package/dist/config.d.cts +7 -1
- package/dist/config.d.ts +7 -1
- package/dist/config.js +2 -2
- package/dist/converters.cjs +61 -61
- package/dist/converters.d.cts +5 -5
- package/dist/converters.d.ts +5 -5
- package/dist/converters.js +4 -4
- package/dist/core.cjs +8 -8
- package/dist/core.d.cts +9 -9
- package/dist/core.d.ts +9 -9
- package/dist/core.js +3 -3
- package/dist/{dtos-Dd_fTfLn.d.cts → dtos-B8KU_q4d.d.cts} +2 -2
- package/dist/{dtos-Dd_fTfLn.d.ts → dtos-B8KU_q4d.d.ts} +2 -2
- package/dist/index.cjs +459 -504
- package/dist/index.d.cts +16 -15
- package/dist/index.d.ts +16 -15
- package/dist/index.js +15 -13
- package/dist/linear.cjs +74 -74
- package/dist/linear.d.cts +14 -14
- package/dist/linear.d.ts +14 -14
- package/dist/linear.js +72 -72
- package/dist/mcp-claude.cjs +37 -36
- package/dist/mcp-claude.d.cts +3 -3
- package/dist/mcp-claude.d.ts +3 -3
- package/dist/mcp-claude.js +7 -6
- package/dist/mcp.cjs +103 -62
- package/dist/mcp.d.cts +29 -16
- package/dist/mcp.d.ts +29 -16
- package/dist/mcp.js +14 -14
- package/dist/{opencode--iA5-KYk.d.cts → opencode-2YdscIc1.d.cts} +5 -5
- package/dist/{opencode-YNacv-eJ.d.ts → opencode-Bld62x5t.d.ts} +5 -5
- package/dist/{pagination-BBLsYLhT.d.ts → pagination-BgEx-HeL.d.ts} +2 -2
- package/dist/{pagination-CqidD-L_.d.cts → pagination-DqdkQArT.d.cts} +2 -2
- package/dist/{protocols-BF6jamIl.d.ts → protocols-Bwwcql0h.d.ts} +1 -1
- package/dist/{protocols-D2bTrQ_t.d.cts → protocols-Bz1rxcUg.d.cts} +1 -1
- package/dist/rest.cjs +41 -97
- package/dist/rest.d.cts +7 -7
- package/dist/rest.d.ts +7 -7
- package/dist/rest.js +2 -2
- package/dist/runtime.cjs +293 -464
- package/dist/runtime.d.cts +16 -28
- package/dist/runtime.d.ts +16 -28
- package/dist/runtime.js +12 -63
- package/dist/{schemas-CI0qgcp_.d.cts → schemas-BotL3ZLH.d.cts} +40 -54
- package/dist/{schemas-B1Fd3oUM.d.ts → schemas-DBsC4J1g.d.ts} +40 -54
- package/dist/{sdk-D2rTDU_o.d.cts → sdk-BTciSLRQ.d.cts} +5 -5
- package/dist/{sdk-CyqN1W5g.d.ts → sdk-qKJtMma_.d.ts} +5 -5
- package/dist/{simpleAdapter-qQt-tSz4.d.ts → simpleAdapter--wznuoOw.d.ts} +1 -1
- package/dist/{simpleAdapter-C_f-01Cu.d.cts → simpleAdapter-BpT4XbZC.d.cts} +1 -1
- package/dist/testing.d.cts +3 -3
- package/dist/testing.d.ts +3 -3
- package/dist/{types-DNySTI_8.d.ts → types-BTN_ZETM.d.ts} +2 -2
- package/dist/{types-D3AsUnd0.d.ts → types-BgYFd_Yw.d.cts} +5 -5
- package/dist/{types-DtMms5Fi.d.cts → types-BivGO7I9.d.ts} +5 -5
- package/dist/{types-CKqrhGB1.d.cts → types-D958TC71.d.cts} +2 -2
- package/package.json +6 -5
- package/dist/backends-DYtEhCZG.d.cts +0 -25
- package/dist/backends-oYAb7EU-.d.ts +0 -25
package/dist/linear.cjs
CHANGED
|
@@ -68,13 +68,13 @@ module.exports = __toCommonJS(linear_exports);
|
|
|
68
68
|
var import_sdk2 = require("@linear/sdk");
|
|
69
69
|
|
|
70
70
|
// src/core/errors.ts
|
|
71
|
-
var
|
|
71
|
+
var BandSdkError = class extends Error {
|
|
72
72
|
constructor(message, cause) {
|
|
73
73
|
super(message, cause !== void 0 ? { cause } : void 0);
|
|
74
|
-
this.name = "
|
|
74
|
+
this.name = "BandSdkError";
|
|
75
75
|
}
|
|
76
76
|
};
|
|
77
|
-
var UnsupportedFeatureError = class extends
|
|
77
|
+
var UnsupportedFeatureError = class extends BandSdkError {
|
|
78
78
|
constructor(message) {
|
|
79
79
|
super(message);
|
|
80
80
|
this.name = "UnsupportedFeatureError";
|
|
@@ -401,7 +401,7 @@ Bridge responsibilities:
|
|
|
401
401
|
- keep Linear updated with meaningful milestones
|
|
402
402
|
- use linear_post_thought and linear_post_action for meaningful progress updates
|
|
403
403
|
- call linear_post_response when the work is actually finished
|
|
404
|
-
- when you delegate inside
|
|
404
|
+
- when you delegate inside Band, include the relevant ticket context in the room message so the specialist can act without hidden state
|
|
405
405
|
|
|
406
406
|
Suggested peers available in the registry right now. They are not in the room yet:
|
|
407
407
|
${suggestedPeersLine}
|
|
@@ -498,7 +498,7 @@ function extractRecord(value) {
|
|
|
498
498
|
}
|
|
499
499
|
|
|
500
500
|
// src/integrations/linear/bridge/handler.ts
|
|
501
|
-
var
|
|
501
|
+
var DEFAULT_APP_BASE_URL = "https://app.band.ai";
|
|
502
502
|
var SUPPORTED_ACTIONS = /* @__PURE__ */ new Set(["created", "updated", "canceled", "prompted"]);
|
|
503
503
|
var MAX_PEER_LOOKUP_PAGES = 25;
|
|
504
504
|
var PEER_PAGE_SIZE = 100;
|
|
@@ -634,14 +634,14 @@ async function handleAgentSessionEvent(input, options) {
|
|
|
634
634
|
let externalUrlPromise;
|
|
635
635
|
try {
|
|
636
636
|
const hostAgentHandle = await resolveHostAgentHandle({
|
|
637
|
-
|
|
637
|
+
bandRest: input.deps.bandRest,
|
|
638
638
|
configuredHostHandle: config.hostAgentHandle,
|
|
639
639
|
logger,
|
|
640
640
|
runtime
|
|
641
641
|
});
|
|
642
642
|
const existingByIssue = config.roomStrategy === "issue" && issueId ? await input.deps.store.getByIssueId(issueId) : null;
|
|
643
643
|
roomRecord = await resolveRoomRecord({
|
|
644
|
-
|
|
644
|
+
bandRest: input.deps.bandRest,
|
|
645
645
|
store: input.deps.store,
|
|
646
646
|
roomStrategy: config.roomStrategy,
|
|
647
647
|
sessionId,
|
|
@@ -650,12 +650,12 @@ async function handleAgentSessionEvent(input, options) {
|
|
|
650
650
|
runtime
|
|
651
651
|
});
|
|
652
652
|
if (action === "created") {
|
|
653
|
-
const roomId = roomRecord.
|
|
653
|
+
const roomId = roomRecord.bandRoomId;
|
|
654
654
|
externalUrlPromise = trySetSessionExternalUrl({
|
|
655
655
|
linearClient: input.deps.linearClient,
|
|
656
656
|
sessionId,
|
|
657
657
|
roomId,
|
|
658
|
-
appBaseUrl: config.
|
|
658
|
+
appBaseUrl: config.bandAppBaseUrl,
|
|
659
659
|
logger
|
|
660
660
|
}).catch((urlError) => {
|
|
661
661
|
logger.warn("linear_thenvoi_bridge.set_external_url_failed", {
|
|
@@ -673,8 +673,8 @@ async function handleAgentSessionEvent(input, options) {
|
|
|
673
673
|
issueDescription: input.payload.agentSession.issue?.description
|
|
674
674
|
});
|
|
675
675
|
const suggestedPeerHandles = await selectRelevantPeerHandles({
|
|
676
|
-
|
|
677
|
-
roomId: roomRecord.
|
|
676
|
+
bandRest: input.deps.bandRest,
|
|
677
|
+
roomId: roomRecord.bandRoomId,
|
|
678
678
|
intent: sessionIntent,
|
|
679
679
|
hostAgentHandle,
|
|
680
680
|
planningAgentHandles: input.config.planningAgentHandles,
|
|
@@ -682,8 +682,8 @@ async function handleAgentSessionEvent(input, options) {
|
|
|
682
682
|
logger
|
|
683
683
|
});
|
|
684
684
|
await ensureRoomParticipants({
|
|
685
|
-
|
|
686
|
-
roomId: roomRecord.
|
|
685
|
+
bandRest: input.deps.bandRest,
|
|
686
|
+
roomId: roomRecord.bandRoomId,
|
|
687
687
|
handles: [hostAgentHandle],
|
|
688
688
|
logger
|
|
689
689
|
});
|
|
@@ -744,17 +744,17 @@ async function handleAgentSessionEvent(input, options) {
|
|
|
744
744
|
linear_issue_id: issueId,
|
|
745
745
|
linear_prompt_context: input.payload.promptContext ?? null,
|
|
746
746
|
linear_writeback_mode: config.writebackMode,
|
|
747
|
-
linear_bridge: "
|
|
747
|
+
linear_bridge: "band",
|
|
748
748
|
linear_host_handle: hostAgentHandle
|
|
749
749
|
};
|
|
750
750
|
const shouldResetRoomSession = Boolean(
|
|
751
|
-
existingByIssue && existingByIssue.linearSessionId !== sessionId && existingByIssue.
|
|
751
|
+
existingByIssue && existingByIssue.linearSessionId !== sessionId && existingByIssue.bandRoomId === roomRecord.bandRoomId && (existingByIssue.status === "completed" || existingByIssue.status === "errored")
|
|
752
752
|
);
|
|
753
|
-
const authenticatedHostHandle = runtime.authenticatedHostHandleCache.get(input.deps.
|
|
753
|
+
const authenticatedHostHandle = runtime.authenticatedHostHandleCache.get(input.deps.bandRest) ?? null;
|
|
754
754
|
const canBootstrapDirectly = options?.skipRoomWrite === true || authenticatedHostHandle === null || authenticatedHostHandle === hostAgentHandle;
|
|
755
755
|
if (!options?.skipRoomWrite || !canBootstrapDirectly) {
|
|
756
756
|
roomRecord = await forwardBridgeMessage({
|
|
757
|
-
|
|
757
|
+
bandRest: input.deps.bandRest,
|
|
758
758
|
store: input.deps.store,
|
|
759
759
|
logger,
|
|
760
760
|
roomRecord,
|
|
@@ -772,7 +772,7 @@ async function handleAgentSessionEvent(input, options) {
|
|
|
772
772
|
await enqueueBootstrapRequest(input.deps.store, {
|
|
773
773
|
eventKey,
|
|
774
774
|
linearSessionId: sessionId,
|
|
775
|
-
|
|
775
|
+
bandRoomId: roomRecord.bandRoomId,
|
|
776
776
|
expectedContent: message,
|
|
777
777
|
messageType: "task",
|
|
778
778
|
senderId: linearActor.id,
|
|
@@ -795,7 +795,7 @@ async function handleAgentSessionEvent(input, options) {
|
|
|
795
795
|
logger.info("linear_thenvoi_bridge.message_forwarded", {
|
|
796
796
|
sessionId,
|
|
797
797
|
issueId,
|
|
798
|
-
roomId: roomRecord.
|
|
798
|
+
roomId: roomRecord.bandRoomId,
|
|
799
799
|
action
|
|
800
800
|
});
|
|
801
801
|
} catch (error) {
|
|
@@ -866,7 +866,7 @@ async function trySetSessionExternalUrl(input) {
|
|
|
866
866
|
const base = input.appBaseUrl.replace(/\/+$/, "");
|
|
867
867
|
const roomUrl = `${base}/rooms/${input.roomId}`;
|
|
868
868
|
await input.linearClient.agentSessionUpdateExternalUrl(input.sessionId, {
|
|
869
|
-
externalUrls: [{ label: "View in
|
|
869
|
+
externalUrls: [{ label: "View in Band", url: roomUrl }]
|
|
870
870
|
});
|
|
871
871
|
input.logger.info("linear_thenvoi_bridge.external_url_set", {
|
|
872
872
|
sessionId: input.sessionId,
|
|
@@ -879,7 +879,7 @@ function normalizeConfig(config) {
|
|
|
879
879
|
roomStrategy: config.roomStrategy ?? "issue",
|
|
880
880
|
writebackMode: config.writebackMode ?? "final_only",
|
|
881
881
|
hostAgentHandle: normalizeOptionalHandle(config.hostAgentHandle),
|
|
882
|
-
|
|
882
|
+
bandAppBaseUrl: config.bandAppBaseUrl ?? DEFAULT_APP_BASE_URL
|
|
883
883
|
};
|
|
884
884
|
}
|
|
885
885
|
function normalizeOptionalHandle(handle) {
|
|
@@ -948,23 +948,23 @@ async function tryMoveIssueToStarted(input) {
|
|
|
948
948
|
}
|
|
949
949
|
async function resolveHostAgentHandle(input) {
|
|
950
950
|
if (input.configuredHostHandle) {
|
|
951
|
-
input.runtime.resolvedHostHandleCache.set(input.
|
|
951
|
+
input.runtime.resolvedHostHandleCache.set(input.bandRest, input.configuredHostHandle);
|
|
952
952
|
return input.configuredHostHandle;
|
|
953
953
|
}
|
|
954
|
-
const cachedAuthenticated = input.runtime.authenticatedHostHandleCache.get(input.
|
|
954
|
+
const cachedAuthenticated = input.runtime.authenticatedHostHandleCache.get(input.bandRest);
|
|
955
955
|
if (cachedAuthenticated) {
|
|
956
|
-
input.runtime.resolvedHostHandleCache.set(input.
|
|
956
|
+
input.runtime.resolvedHostHandleCache.set(input.bandRest, cachedAuthenticated);
|
|
957
957
|
return cachedAuthenticated;
|
|
958
958
|
}
|
|
959
|
-
const cached = input.runtime.resolvedHostHandleCache.get(input.
|
|
959
|
+
const cached = input.runtime.resolvedHostHandleCache.get(input.bandRest);
|
|
960
960
|
if (cached) {
|
|
961
961
|
return cached;
|
|
962
962
|
}
|
|
963
|
-
const identity = await input.
|
|
963
|
+
const identity = await input.bandRest.getAgentMe();
|
|
964
964
|
const authenticatedAgentHandle = normalizeOptionalHandle(identity.handle);
|
|
965
965
|
if (authenticatedAgentHandle) {
|
|
966
|
-
input.runtime.authenticatedHostHandleCache.set(input.
|
|
967
|
-
input.runtime.resolvedHostHandleCache.set(input.
|
|
966
|
+
input.runtime.authenticatedHostHandleCache.set(input.bandRest, authenticatedAgentHandle);
|
|
967
|
+
input.runtime.resolvedHostHandleCache.set(input.bandRest, authenticatedAgentHandle);
|
|
968
968
|
return authenticatedAgentHandle;
|
|
969
969
|
}
|
|
970
970
|
throw new Error(
|
|
@@ -998,14 +998,14 @@ async function handleCanceledAction(input) {
|
|
|
998
998
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
999
999
|
});
|
|
1000
1000
|
if (!input.skipRoomWrite) {
|
|
1001
|
-
await input.deps.
|
|
1001
|
+
await input.deps.bandRest.createChatEvent(existing.bandRoomId, {
|
|
1002
1002
|
content: "[Linear]: Agent session canceled. Stop in-room execution and await new instructions.",
|
|
1003
1003
|
messageType: "task",
|
|
1004
1004
|
metadata: {
|
|
1005
1005
|
linear_event_action: "canceled",
|
|
1006
1006
|
linear_session_id: input.sessionId,
|
|
1007
1007
|
linear_issue_id: input.issueId,
|
|
1008
|
-
linear_bridge: "
|
|
1008
|
+
linear_bridge: "band"
|
|
1009
1009
|
}
|
|
1010
1010
|
});
|
|
1011
1011
|
}
|
|
@@ -1041,11 +1041,11 @@ async function handlePromptedAction(input) {
|
|
|
1041
1041
|
const metadata = {
|
|
1042
1042
|
linear_event_action: "prompted",
|
|
1043
1043
|
linear_session_id: input.sessionId,
|
|
1044
|
-
linear_bridge: "
|
|
1044
|
+
linear_bridge: "band"
|
|
1045
1045
|
};
|
|
1046
|
-
const canBootstrapDirectly = input.config.hostAgentHandle !== null ? true : Boolean(normalizeOptionalHandle((await input.deps.
|
|
1046
|
+
const canBootstrapDirectly = input.config.hostAgentHandle !== null ? true : Boolean(normalizeOptionalHandle((await input.deps.bandRest.getAgentMe()).handle));
|
|
1047
1047
|
if (!input.skipRoomWrite || !canBootstrapDirectly) {
|
|
1048
|
-
await input.deps.
|
|
1048
|
+
await input.deps.bandRest.createChatEvent(existing.bandRoomId, {
|
|
1049
1049
|
content: message,
|
|
1050
1050
|
messageType: "text",
|
|
1051
1051
|
metadata
|
|
@@ -1056,7 +1056,7 @@ async function handlePromptedAction(input) {
|
|
|
1056
1056
|
await enqueueBootstrapRequest(input.deps.store, {
|
|
1057
1057
|
eventKey: input.eventKey,
|
|
1058
1058
|
linearSessionId: input.sessionId,
|
|
1059
|
-
|
|
1059
|
+
bandRoomId: existing.bandRoomId,
|
|
1060
1060
|
expectedContent: message,
|
|
1061
1061
|
messageType: "text",
|
|
1062
1062
|
senderId: linearActor.id,
|
|
@@ -1072,7 +1072,7 @@ async function handlePromptedAction(input) {
|
|
|
1072
1072
|
});
|
|
1073
1073
|
input.logger.info("linear_thenvoi_bridge.prompted_forwarded", {
|
|
1074
1074
|
sessionId: input.sessionId,
|
|
1075
|
-
roomId: existing.
|
|
1075
|
+
roomId: existing.bandRoomId
|
|
1076
1076
|
});
|
|
1077
1077
|
}
|
|
1078
1078
|
async function enqueueBootstrapRequest(store, request) {
|
|
@@ -1114,7 +1114,7 @@ async function resolveRoomRecord(input) {
|
|
|
1114
1114
|
}
|
|
1115
1115
|
async function forwardBridgeMessage(input) {
|
|
1116
1116
|
try {
|
|
1117
|
-
await input.
|
|
1117
|
+
await input.bandRest.createChatEvent(input.roomRecord.bandRoomId, {
|
|
1118
1118
|
content: input.message,
|
|
1119
1119
|
messageType: input.messageType,
|
|
1120
1120
|
metadata: input.metadata
|
|
@@ -1127,26 +1127,26 @@ async function forwardBridgeMessage(input) {
|
|
|
1127
1127
|
input.logger.warn("linear_thenvoi_bridge.room_forward_recovering_with_fresh_room", {
|
|
1128
1128
|
sessionId: input.sessionId,
|
|
1129
1129
|
issueId: input.issueId,
|
|
1130
|
-
previousRoomId: input.roomRecord.
|
|
1130
|
+
previousRoomId: input.roomRecord.bandRoomId,
|
|
1131
1131
|
error: error instanceof Error ? error.message : String(error)
|
|
1132
1132
|
});
|
|
1133
1133
|
const recoveredRoom = await createFreshRoomRecord({
|
|
1134
|
-
|
|
1134
|
+
bandRest: input.bandRest,
|
|
1135
1135
|
store: input.store,
|
|
1136
1136
|
sessionId: input.sessionId,
|
|
1137
1137
|
issueId: input.issueId,
|
|
1138
1138
|
logger: input.logger
|
|
1139
1139
|
});
|
|
1140
1140
|
await ensureRoomParticipants({
|
|
1141
|
-
|
|
1142
|
-
roomId: recoveredRoom.
|
|
1141
|
+
bandRest: input.bandRest,
|
|
1142
|
+
roomId: recoveredRoom.bandRoomId,
|
|
1143
1143
|
handles: [input.hostAgentHandle],
|
|
1144
1144
|
logger: input.logger
|
|
1145
1145
|
});
|
|
1146
1146
|
let attempt = 0;
|
|
1147
1147
|
while (true) {
|
|
1148
1148
|
try {
|
|
1149
|
-
await input.
|
|
1149
|
+
await input.bandRest.createChatEvent(recoveredRoom.bandRoomId, {
|
|
1150
1150
|
content: input.message,
|
|
1151
1151
|
messageType: input.messageType,
|
|
1152
1152
|
metadata: input.metadata
|
|
@@ -1162,7 +1162,7 @@ async function forwardBridgeMessage(input) {
|
|
|
1162
1162
|
input.logger.warn("linear_thenvoi_bridge.room_recreated_retrying", {
|
|
1163
1163
|
sessionId: input.sessionId,
|
|
1164
1164
|
issueId: input.issueId,
|
|
1165
|
-
roomId: recoveredRoom.
|
|
1165
|
+
roomId: recoveredRoom.bandRoomId,
|
|
1166
1166
|
attempt,
|
|
1167
1167
|
delayMs,
|
|
1168
1168
|
error: recoveredError instanceof Error ? recoveredError.message : String(recoveredError)
|
|
@@ -1192,11 +1192,11 @@ function isRetryableRateLimitError(error) {
|
|
|
1192
1192
|
}
|
|
1193
1193
|
async function createFreshRoomRecord(input) {
|
|
1194
1194
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1195
|
-
const created = await input.
|
|
1195
|
+
const created = await input.bandRest.createChat();
|
|
1196
1196
|
const record = {
|
|
1197
1197
|
linearSessionId: input.sessionId,
|
|
1198
1198
|
linearIssueId: input.issueId,
|
|
1199
|
-
|
|
1199
|
+
bandRoomId: created.id,
|
|
1200
1200
|
status: "active",
|
|
1201
1201
|
lastEventKey: null,
|
|
1202
1202
|
createdAt: now,
|
|
@@ -1222,7 +1222,7 @@ async function resolveRoomRecordImpl(input) {
|
|
|
1222
1222
|
const linkedRecord = {
|
|
1223
1223
|
linearSessionId: input.sessionId,
|
|
1224
1224
|
linearIssueId: input.issueId,
|
|
1225
|
-
|
|
1225
|
+
bandRoomId: existingByIssue.bandRoomId,
|
|
1226
1226
|
status: "active",
|
|
1227
1227
|
lastEventKey: existingBySession?.lastEventKey ?? null,
|
|
1228
1228
|
createdAt: existingBySession?.createdAt ?? now2,
|
|
@@ -1233,11 +1233,11 @@ async function resolveRoomRecordImpl(input) {
|
|
|
1233
1233
|
}
|
|
1234
1234
|
}
|
|
1235
1235
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1236
|
-
const created = await input.
|
|
1236
|
+
const created = await input.bandRest.createChat();
|
|
1237
1237
|
const createdRecord = {
|
|
1238
1238
|
linearSessionId: input.sessionId,
|
|
1239
1239
|
linearIssueId: input.issueId,
|
|
1240
|
-
|
|
1240
|
+
bandRoomId: created.id,
|
|
1241
1241
|
status: "active",
|
|
1242
1242
|
lastEventKey: null,
|
|
1243
1243
|
createdAt: now,
|
|
@@ -1258,7 +1258,7 @@ async function ensureRoomParticipants(input) {
|
|
|
1258
1258
|
}
|
|
1259
1259
|
let existingParticipants;
|
|
1260
1260
|
try {
|
|
1261
|
-
existingParticipants = await input.
|
|
1261
|
+
existingParticipants = await input.bandRest.listChatParticipants(input.roomId);
|
|
1262
1262
|
} catch (error) {
|
|
1263
1263
|
if (isRetryableRateLimitError(error)) {
|
|
1264
1264
|
input.logger.warn("linear_thenvoi_bridge.participant_list_rate_limited", {
|
|
@@ -1278,7 +1278,7 @@ async function ensureRoomParticipants(input) {
|
|
|
1278
1278
|
}
|
|
1279
1279
|
let peersByHandle;
|
|
1280
1280
|
try {
|
|
1281
|
-
peersByHandle = await lookupPeerIdsByHandle(input.
|
|
1281
|
+
peersByHandle = await lookupPeerIdsByHandle(input.bandRest, input.roomId, missingHandles);
|
|
1282
1282
|
} catch (error) {
|
|
1283
1283
|
if (error instanceof UnsupportedFeatureError) {
|
|
1284
1284
|
input.logger.warn("linear_thenvoi_bridge.peer_lookup_unavailable", {
|
|
@@ -1300,7 +1300,7 @@ async function ensureRoomParticipants(input) {
|
|
|
1300
1300
|
continue;
|
|
1301
1301
|
}
|
|
1302
1302
|
try {
|
|
1303
|
-
await input.
|
|
1303
|
+
await input.bandRest.addChatParticipant(
|
|
1304
1304
|
input.roomId,
|
|
1305
1305
|
{
|
|
1306
1306
|
participantId,
|
|
@@ -1319,14 +1319,14 @@ async function ensureRoomParticipants(input) {
|
|
|
1319
1319
|
}
|
|
1320
1320
|
}
|
|
1321
1321
|
}
|
|
1322
|
-
async function lookupPeerIdsByHandle(
|
|
1323
|
-
if (!
|
|
1322
|
+
async function lookupPeerIdsByHandle(bandRest, roomId, handles) {
|
|
1323
|
+
if (!bandRest.listPeers) {
|
|
1324
1324
|
throw new UnsupportedFeatureError("Peer listing is not available in current REST adapter");
|
|
1325
1325
|
}
|
|
1326
1326
|
const targets = new Set(handles);
|
|
1327
1327
|
const found = /* @__PURE__ */ new Map();
|
|
1328
1328
|
for (let page = 1; page <= MAX_PEER_LOOKUP_PAGES && found.size < targets.size; page += 1) {
|
|
1329
|
-
const response = await
|
|
1329
|
+
const response = await bandRest.listPeers({
|
|
1330
1330
|
page,
|
|
1331
1331
|
pageSize: PEER_PAGE_SIZE,
|
|
1332
1332
|
notInChat: roomId
|
|
@@ -1367,12 +1367,12 @@ async function selectRelevantPeerHandles(input) {
|
|
|
1367
1367
|
});
|
|
1368
1368
|
return configuredHandles;
|
|
1369
1369
|
}
|
|
1370
|
-
if (!input.
|
|
1370
|
+
if (!input.bandRest.listPeers) {
|
|
1371
1371
|
return [];
|
|
1372
1372
|
}
|
|
1373
1373
|
let peers;
|
|
1374
1374
|
try {
|
|
1375
|
-
const response = await input.
|
|
1375
|
+
const response = await input.bandRest.listPeers({
|
|
1376
1376
|
page: 1,
|
|
1377
1377
|
pageSize: PEER_PAGE_SIZE,
|
|
1378
1378
|
notInChat: input.roomId
|
|
@@ -1562,7 +1562,7 @@ var SqliteSessionRoomStore = class {
|
|
|
1562
1562
|
).run(
|
|
1563
1563
|
record.linearSessionId,
|
|
1564
1564
|
record.linearIssueId,
|
|
1565
|
-
record.
|
|
1565
|
+
record.bandRoomId,
|
|
1566
1566
|
record.status,
|
|
1567
1567
|
record.lastEventKey ?? null,
|
|
1568
1568
|
record.lastLinearActivityAt ?? null,
|
|
@@ -1609,7 +1609,7 @@ var SqliteSessionRoomStore = class {
|
|
|
1609
1609
|
).run(
|
|
1610
1610
|
request.eventKey,
|
|
1611
1611
|
request.linearSessionId,
|
|
1612
|
-
request.
|
|
1612
|
+
request.bandRoomId,
|
|
1613
1613
|
request.expectedContent,
|
|
1614
1614
|
request.messageType,
|
|
1615
1615
|
request.metadata ? JSON.stringify(request.metadata) : null,
|
|
@@ -1641,7 +1641,7 @@ var SqliteSessionRoomStore = class {
|
|
|
1641
1641
|
return rows.map((row) => ({
|
|
1642
1642
|
eventKey: row.event_key,
|
|
1643
1643
|
linearSessionId: row.linear_session_id,
|
|
1644
|
-
|
|
1644
|
+
bandRoomId: row.thenvoi_room_id,
|
|
1645
1645
|
expectedContent: row.expected_content,
|
|
1646
1646
|
messageType: row.message_type,
|
|
1647
1647
|
metadata: this.parseMetadata(row.metadata_json),
|
|
@@ -1779,7 +1779,7 @@ var SqliteSessionRoomStore = class {
|
|
|
1779
1779
|
return {
|
|
1780
1780
|
linearSessionId: row.linear_session_id,
|
|
1781
1781
|
linearIssueId: row.linear_issue_id,
|
|
1782
|
-
|
|
1782
|
+
bandRoomId: row.thenvoi_room_id,
|
|
1783
1783
|
status: row.status,
|
|
1784
1784
|
lastEventKey: row.last_event_key,
|
|
1785
1785
|
lastLinearActivityAt: row.last_linear_activity_at,
|
|
@@ -1835,19 +1835,19 @@ function parseSessionRoomRow(value) {
|
|
|
1835
1835
|
}
|
|
1836
1836
|
const linearSessionId = asString(row.linear_session_id);
|
|
1837
1837
|
const linearIssueId = asNullableString(row.linear_issue_id);
|
|
1838
|
-
const
|
|
1838
|
+
const bandRoomId = asString(row.thenvoi_room_id);
|
|
1839
1839
|
const status = asString(row.status);
|
|
1840
1840
|
const lastEventKey = asNullableString(row.last_event_key);
|
|
1841
1841
|
const lastLinearActivityAt = asNullableString(row.last_linear_activity_at);
|
|
1842
1842
|
const createdAt = asString(row.created_at);
|
|
1843
1843
|
const updatedAt = asString(row.updated_at);
|
|
1844
|
-
if (!linearSessionId || linearIssueId === void 0 || !
|
|
1844
|
+
if (!linearSessionId || linearIssueId === void 0 || !bandRoomId || !status || !isSessionStatus(status) || lastEventKey === void 0 || lastLinearActivityAt === void 0 || !createdAt || !updatedAt) {
|
|
1845
1845
|
return null;
|
|
1846
1846
|
}
|
|
1847
1847
|
return {
|
|
1848
1848
|
linear_session_id: linearSessionId,
|
|
1849
1849
|
linear_issue_id: linearIssueId,
|
|
1850
|
-
thenvoi_room_id:
|
|
1850
|
+
thenvoi_room_id: bandRoomId,
|
|
1851
1851
|
status,
|
|
1852
1852
|
last_event_key: lastEventKey,
|
|
1853
1853
|
last_linear_activity_at: lastLinearActivityAt,
|
|
@@ -1868,20 +1868,20 @@ function parseBootstrapRequestRow(value) {
|
|
|
1868
1868
|
}
|
|
1869
1869
|
const eventKey = asString(row.event_key);
|
|
1870
1870
|
const linearSessionId = asString(row.linear_session_id);
|
|
1871
|
-
const
|
|
1871
|
+
const bandRoomId = asString(row.thenvoi_room_id);
|
|
1872
1872
|
const expectedContent = asString(row.expected_content);
|
|
1873
1873
|
const messageType = asString(row.message_type);
|
|
1874
1874
|
const metadataJson = asNullableString(row.metadata_json);
|
|
1875
1875
|
const createdAt = asString(row.created_at);
|
|
1876
1876
|
const expiresAt = asString(row.expires_at);
|
|
1877
1877
|
const processedAt = asNullableString(row.processed_at);
|
|
1878
|
-
if (!eventKey || !linearSessionId || !
|
|
1878
|
+
if (!eventKey || !linearSessionId || !bandRoomId || !expectedContent || !messageType || metadataJson === void 0 || !createdAt || !expiresAt || processedAt === void 0) {
|
|
1879
1879
|
return null;
|
|
1880
1880
|
}
|
|
1881
1881
|
return {
|
|
1882
1882
|
event_key: eventKey,
|
|
1883
1883
|
linear_session_id: linearSessionId,
|
|
1884
|
-
thenvoi_room_id:
|
|
1884
|
+
thenvoi_room_id: bandRoomId,
|
|
1885
1885
|
expected_content: expectedContent,
|
|
1886
1886
|
message_type: messageType,
|
|
1887
1887
|
metadata_json: metadataJson,
|
|
@@ -2312,7 +2312,7 @@ function addSessionCreationTools(input) {
|
|
|
2312
2312
|
const { tools, client, store, logger } = input;
|
|
2313
2313
|
const sessionCreationBaseSchema = import_zod.z.object({
|
|
2314
2314
|
external_link: import_zod.z.string().url().optional().describe("Optional URL of an external page associated with this session"),
|
|
2315
|
-
room_id: import_zod.z.string().min(1).optional().describe("The
|
|
2315
|
+
room_id: import_zod.z.string().min(1).optional().describe("The Band room ID to persist the session-room mapping. Pass this when creating a session from within a Band conversation.")
|
|
2316
2316
|
});
|
|
2317
2317
|
const sessionCreationSchema = sessionCreationBaseSchema.extend({
|
|
2318
2318
|
issue_id: import_zod.z.string().uuid().describe("The Linear issue ID (UUID)")
|
|
@@ -2324,7 +2324,7 @@ function addSessionCreationTools(input) {
|
|
|
2324
2324
|
await store.upsert({
|
|
2325
2325
|
linearSessionId: sessionId,
|
|
2326
2326
|
linearIssueId: issueId,
|
|
2327
|
-
|
|
2327
|
+
bandRoomId: roomId,
|
|
2328
2328
|
status: "active",
|
|
2329
2329
|
createdAt: now,
|
|
2330
2330
|
updatedAt: now
|
|
@@ -2382,7 +2382,7 @@ function addSessionCreationTools(input) {
|
|
|
2382
2382
|
if (typeof createIssueFn === "function") {
|
|
2383
2383
|
tools.push({
|
|
2384
2384
|
name: "linear_create_issue",
|
|
2385
|
-
description: "Create a new Linear issue from scratch. Use this when the
|
|
2385
|
+
description: "Create a new Linear issue from scratch. Use this when the Band conversation produces work that should be tracked as a new Linear issue. After creating the issue, call linear_create_session_on_issue to attach an agent session. Never create issues without explicit human intent or clear delegation.",
|
|
2386
2386
|
schema: import_zod.z.object({
|
|
2387
2387
|
team_id: import_zod.z.string().uuid().describe("The Linear team ID to create the issue in"),
|
|
2388
2388
|
title: import_zod.z.string().min(1).describe("Issue title"),
|
|
@@ -2517,21 +2517,21 @@ async function handleIssueUnassigned(input) {
|
|
|
2517
2517
|
});
|
|
2518
2518
|
return;
|
|
2519
2519
|
}
|
|
2520
|
-
await deps.
|
|
2520
|
+
await deps.bandRest.createChatEvent(existing.bandRoomId, {
|
|
2521
2521
|
content: "[Linear]: Issue unassigned from agent. Disengage from active work and await reassignment.",
|
|
2522
2522
|
messageType: "task",
|
|
2523
2523
|
metadata: {
|
|
2524
2524
|
linear_notification_type: "issueUnassignedFromYou",
|
|
2525
2525
|
linear_issue_id: notification.issueId,
|
|
2526
2526
|
linear_actor_id: notification.actorId ?? null,
|
|
2527
|
-
linear_bridge: "
|
|
2527
|
+
linear_bridge: "band"
|
|
2528
2528
|
}
|
|
2529
2529
|
});
|
|
2530
2530
|
await deps.store.markCanceled(existing.linearSessionId);
|
|
2531
2531
|
logger.info("linear_thenvoi_bridge.notification_unassigned_handled", {
|
|
2532
2532
|
issueId: notification.issueId,
|
|
2533
2533
|
sessionId: existing.linearSessionId,
|
|
2534
|
-
roomId: existing.
|
|
2534
|
+
roomId: existing.bandRoomId
|
|
2535
2535
|
});
|
|
2536
2536
|
}
|
|
2537
2537
|
async function handleIssueNewComment(input) {
|
|
@@ -2570,7 +2570,7 @@ async function handleIssueNewComment(input) {
|
|
|
2570
2570
|
actorId: notification.actorId ?? null
|
|
2571
2571
|
});
|
|
2572
2572
|
}
|
|
2573
|
-
await deps.
|
|
2573
|
+
await deps.bandRest.createChatEvent(existing.bandRoomId, {
|
|
2574
2574
|
content: `[Linear Comment from ${actorName}]: ${commentBody}`,
|
|
2575
2575
|
messageType: "text",
|
|
2576
2576
|
metadata: {
|
|
@@ -2578,14 +2578,14 @@ async function handleIssueNewComment(input) {
|
|
|
2578
2578
|
linear_issue_id: notification.issueId,
|
|
2579
2579
|
linear_comment_id: notification.commentId,
|
|
2580
2580
|
linear_actor_id: notification.actorId ?? null,
|
|
2581
|
-
linear_bridge: "
|
|
2581
|
+
linear_bridge: "band"
|
|
2582
2582
|
}
|
|
2583
2583
|
});
|
|
2584
2584
|
logger.info("linear_thenvoi_bridge.notification_comment_forwarded", {
|
|
2585
2585
|
issueId: notification.issueId,
|
|
2586
2586
|
commentId: notification.commentId,
|
|
2587
2587
|
sessionId: existing.linearSessionId,
|
|
2588
|
-
roomId: existing.
|
|
2588
|
+
roomId: existing.bandRoomId
|
|
2589
2589
|
});
|
|
2590
2590
|
}
|
|
2591
2591
|
function logReaction(input) {
|
package/dist/linear.d.cts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { LinearClient, LinearDocument } from '@linear/sdk';
|
|
2
|
-
import { R as RestApi } from './types-
|
|
2
|
+
import { R as RestApi } from './types-BgYFd_Yw.cjs';
|
|
3
3
|
import { AppUserNotificationWebhookPayloadWithNotification, AgentSessionEventWebhookPayload, AppUserTeamAccessChangedWebhookPayload, OAuthAppWebhookPayload } from '@linear/sdk/webhooks';
|
|
4
4
|
import { L as Logger } from './logger-CABQOnlR.cjs';
|
|
5
5
|
import { C as CustomToolDef } from './customTools-BzF0IISO.cjs';
|
|
6
6
|
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
7
|
-
import './dtos-
|
|
7
|
+
import './dtos-B8KU_q4d.cjs';
|
|
8
8
|
import 'zod';
|
|
9
9
|
|
|
10
10
|
interface CandidateRepositoryInput {
|
|
@@ -89,7 +89,7 @@ declare function updatePlan(client: LinearActivityClient, sessionId: string, ste
|
|
|
89
89
|
|
|
90
90
|
interface HandleAppUserNotificationInput {
|
|
91
91
|
payload: AppUserNotificationWebhookPayloadWithNotification;
|
|
92
|
-
deps:
|
|
92
|
+
deps: LinearBandBridgeDeps;
|
|
93
93
|
logger: Logger;
|
|
94
94
|
/** The Linear app-user id from the webhook envelope; used to skip self-notifications. */
|
|
95
95
|
appUserId?: string;
|
|
@@ -107,7 +107,7 @@ declare const STALE_SESSION_CHECK_INTERVAL_MS: number;
|
|
|
107
107
|
* inactivity; we send a keepalive well before that threshold.
|
|
108
108
|
*/
|
|
109
109
|
declare const STALE_SESSION_THRESHOLD_MS: number;
|
|
110
|
-
interface
|
|
110
|
+
interface LinearBandBridgeConfig {
|
|
111
111
|
linearAccessToken: string;
|
|
112
112
|
linearWebhookSecret: string;
|
|
113
113
|
roomStrategy?: RoomStrategy;
|
|
@@ -116,12 +116,12 @@ interface LinearThenvoiBridgeConfig {
|
|
|
116
116
|
planningAgentHandles?: string[];
|
|
117
117
|
implementationAgentHandles?: string[];
|
|
118
118
|
recoveredRoomRetryBaseDelayMs?: number;
|
|
119
|
-
|
|
119
|
+
bandAppBaseUrl?: string;
|
|
120
120
|
}
|
|
121
121
|
interface SessionRoomRecord {
|
|
122
122
|
linearSessionId: string;
|
|
123
123
|
linearIssueId: string | null;
|
|
124
|
-
|
|
124
|
+
bandRoomId: string;
|
|
125
125
|
status: SessionStatus;
|
|
126
126
|
lastEventKey?: string | null;
|
|
127
127
|
/** ISO-8601 timestamp of the last activity sent to Linear for this session. */
|
|
@@ -132,7 +132,7 @@ interface SessionRoomRecord {
|
|
|
132
132
|
interface PendingBootstrapRequest {
|
|
133
133
|
eventKey: string;
|
|
134
134
|
linearSessionId: string;
|
|
135
|
-
|
|
135
|
+
bandRoomId: string;
|
|
136
136
|
expectedContent: string;
|
|
137
137
|
messageType: string;
|
|
138
138
|
senderId?: string | null;
|
|
@@ -153,16 +153,16 @@ interface SessionRoomStore {
|
|
|
153
153
|
listActiveSessions?(): Promise<SessionRoomRecord[]>;
|
|
154
154
|
close?(): Promise<void>;
|
|
155
155
|
}
|
|
156
|
-
interface
|
|
157
|
-
|
|
156
|
+
interface LinearBandBridgeDeps {
|
|
157
|
+
bandRest: RestApi;
|
|
158
158
|
linearClient: LinearClient;
|
|
159
159
|
store: SessionRoomStore;
|
|
160
160
|
logger?: Logger;
|
|
161
161
|
}
|
|
162
162
|
interface HandleAgentSessionEventInput {
|
|
163
163
|
payload: AgentSessionEventWebhookPayload;
|
|
164
|
-
config:
|
|
165
|
-
deps:
|
|
164
|
+
config: LinearBandBridgeConfig;
|
|
165
|
+
deps: LinearBandBridgeDeps;
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
type LinearSessionStatus = SessionStatus;
|
|
@@ -279,8 +279,8 @@ interface PermissionChangeCallbacks {
|
|
|
279
279
|
onOAuthAppRevoked?: (payload: OAuthAppWebhookPayload) => void | Promise<void>;
|
|
280
280
|
}
|
|
281
281
|
interface CreateLinearWebhookHandlerOptions {
|
|
282
|
-
config:
|
|
283
|
-
deps:
|
|
282
|
+
config: LinearBandBridgeConfig;
|
|
283
|
+
deps: LinearBandBridgeDeps;
|
|
284
284
|
dispatcher?: LinearBridgeDispatcher;
|
|
285
285
|
permissionCallbacks?: PermissionChangeCallbacks;
|
|
286
286
|
}
|
|
@@ -294,4 +294,4 @@ declare function createLinearWebhookHandler(options: CreateLinearWebhookHandlerO
|
|
|
294
294
|
|
|
295
295
|
declare const DEFAULT_STATUS_MAPPING: Record<SessionStatus, LinearSessionStatus>;
|
|
296
296
|
|
|
297
|
-
export { type CandidateRepositoryInput, type CreateLinearWebhookHandlerOptions, DEFAULT_STATUS_MAPPING, ELICITATION_BODY_MAX_LENGTH, type HandleAgentSessionEventInput, type HandleAppUserNotificationInput, type LinearActivityClient, type
|
|
297
|
+
export { type CandidateRepositoryInput, type CreateLinearWebhookHandlerOptions, DEFAULT_STATUS_MAPPING, ELICITATION_BODY_MAX_LENGTH, type HandleAgentSessionEventInput, type HandleAppUserNotificationInput, type LinearActivityClient, type LinearBandBridgeConfig, type LinearBandBridgeDeps, type LinearBridgeDispatchJob, type LinearBridgeDispatcher, type LinearBridgeRuntime, type LinearSessionStatus, PROVIDER_MAX_LENGTH, type PendingBootstrapRequest, type PermissionChangeCallbacks, type PlanStep, type RepositorySuggestion, type RoomStrategy, SELECT_OPTION_MAX_LENGTH, STALE_SESSION_CHECK_INTERVAL_MS, STALE_SESSION_THRESHOLD_MS, type SelectOption, type SessionRoomRecord, type SessionRoomStore, type SessionStatus, StaleSessionGuard, type StaleSessionGuardOptions, type WritebackMode, buildLinearAuthorizationHeader, completeLinearSession, createInProcessLinearBridgeDispatcher, createInlineLinearBridgeDispatcher, createLinearBridgeRuntime, createLinearClient, createLinearTools, createLinearWebhookHandler, createSqliteSessionRoomStore, dedupeHandles, handleAgentSessionEvent, handleAppUserNotification, isLinearApiKey, isSessionStale, postAction, postAuthElicitation, postElicitation, postError, postResponse, postSelectElicitation, postThought, sendRecoveryActivityIfStale, stripHandlePrefix, updatePlan };
|