@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.d.ts
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-BivGO7I9.js';
|
|
3
3
|
import { AppUserNotificationWebhookPayloadWithNotification, AgentSessionEventWebhookPayload, AppUserTeamAccessChangedWebhookPayload, OAuthAppWebhookPayload } from '@linear/sdk/webhooks';
|
|
4
4
|
import { L as Logger } from './logger-CABQOnlR.js';
|
|
5
5
|
import { C as CustomToolDef } from './customTools-BzF0IISO.js';
|
|
6
6
|
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
7
|
-
import './dtos-
|
|
7
|
+
import './dtos-B8KU_q4d.js';
|
|
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 };
|
package/dist/linear.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-OD2G5LFQ.js";
|
|
4
4
|
import {
|
|
5
5
|
UnsupportedFeatureError
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-JY75VSQK.js";
|
|
7
7
|
|
|
8
8
|
// src/integrations/linear/bridge/handler.ts
|
|
9
9
|
import { LinearDocument as L2 } from "@linear/sdk";
|
|
@@ -319,7 +319,7 @@ Bridge responsibilities:
|
|
|
319
319
|
- keep Linear updated with meaningful milestones
|
|
320
320
|
- use linear_post_thought and linear_post_action for meaningful progress updates
|
|
321
321
|
- call linear_post_response when the work is actually finished
|
|
322
|
-
- when you delegate inside
|
|
322
|
+
- when you delegate inside Band, include the relevant ticket context in the room message so the specialist can act without hidden state
|
|
323
323
|
|
|
324
324
|
Suggested peers available in the registry right now. They are not in the room yet:
|
|
325
325
|
${suggestedPeersLine}
|
|
@@ -416,7 +416,7 @@ function extractRecord(value) {
|
|
|
416
416
|
}
|
|
417
417
|
|
|
418
418
|
// src/integrations/linear/bridge/handler.ts
|
|
419
|
-
var
|
|
419
|
+
var DEFAULT_APP_BASE_URL = "https://app.band.ai";
|
|
420
420
|
var SUPPORTED_ACTIONS = /* @__PURE__ */ new Set(["created", "updated", "canceled", "prompted"]);
|
|
421
421
|
var MAX_PEER_LOOKUP_PAGES = 25;
|
|
422
422
|
var PEER_PAGE_SIZE = 100;
|
|
@@ -552,14 +552,14 @@ async function handleAgentSessionEvent(input, options) {
|
|
|
552
552
|
let externalUrlPromise;
|
|
553
553
|
try {
|
|
554
554
|
const hostAgentHandle = await resolveHostAgentHandle({
|
|
555
|
-
|
|
555
|
+
bandRest: input.deps.bandRest,
|
|
556
556
|
configuredHostHandle: config.hostAgentHandle,
|
|
557
557
|
logger,
|
|
558
558
|
runtime
|
|
559
559
|
});
|
|
560
560
|
const existingByIssue = config.roomStrategy === "issue" && issueId ? await input.deps.store.getByIssueId(issueId) : null;
|
|
561
561
|
roomRecord = await resolveRoomRecord({
|
|
562
|
-
|
|
562
|
+
bandRest: input.deps.bandRest,
|
|
563
563
|
store: input.deps.store,
|
|
564
564
|
roomStrategy: config.roomStrategy,
|
|
565
565
|
sessionId,
|
|
@@ -568,12 +568,12 @@ async function handleAgentSessionEvent(input, options) {
|
|
|
568
568
|
runtime
|
|
569
569
|
});
|
|
570
570
|
if (action === "created") {
|
|
571
|
-
const roomId = roomRecord.
|
|
571
|
+
const roomId = roomRecord.bandRoomId;
|
|
572
572
|
externalUrlPromise = trySetSessionExternalUrl({
|
|
573
573
|
linearClient: input.deps.linearClient,
|
|
574
574
|
sessionId,
|
|
575
575
|
roomId,
|
|
576
|
-
appBaseUrl: config.
|
|
576
|
+
appBaseUrl: config.bandAppBaseUrl,
|
|
577
577
|
logger
|
|
578
578
|
}).catch((urlError) => {
|
|
579
579
|
logger.warn("linear_thenvoi_bridge.set_external_url_failed", {
|
|
@@ -591,8 +591,8 @@ async function handleAgentSessionEvent(input, options) {
|
|
|
591
591
|
issueDescription: input.payload.agentSession.issue?.description
|
|
592
592
|
});
|
|
593
593
|
const suggestedPeerHandles = await selectRelevantPeerHandles({
|
|
594
|
-
|
|
595
|
-
roomId: roomRecord.
|
|
594
|
+
bandRest: input.deps.bandRest,
|
|
595
|
+
roomId: roomRecord.bandRoomId,
|
|
596
596
|
intent: sessionIntent,
|
|
597
597
|
hostAgentHandle,
|
|
598
598
|
planningAgentHandles: input.config.planningAgentHandles,
|
|
@@ -600,8 +600,8 @@ async function handleAgentSessionEvent(input, options) {
|
|
|
600
600
|
logger
|
|
601
601
|
});
|
|
602
602
|
await ensureRoomParticipants({
|
|
603
|
-
|
|
604
|
-
roomId: roomRecord.
|
|
603
|
+
bandRest: input.deps.bandRest,
|
|
604
|
+
roomId: roomRecord.bandRoomId,
|
|
605
605
|
handles: [hostAgentHandle],
|
|
606
606
|
logger
|
|
607
607
|
});
|
|
@@ -662,17 +662,17 @@ async function handleAgentSessionEvent(input, options) {
|
|
|
662
662
|
linear_issue_id: issueId,
|
|
663
663
|
linear_prompt_context: input.payload.promptContext ?? null,
|
|
664
664
|
linear_writeback_mode: config.writebackMode,
|
|
665
|
-
linear_bridge: "
|
|
665
|
+
linear_bridge: "band",
|
|
666
666
|
linear_host_handle: hostAgentHandle
|
|
667
667
|
};
|
|
668
668
|
const shouldResetRoomSession = Boolean(
|
|
669
|
-
existingByIssue && existingByIssue.linearSessionId !== sessionId && existingByIssue.
|
|
669
|
+
existingByIssue && existingByIssue.linearSessionId !== sessionId && existingByIssue.bandRoomId === roomRecord.bandRoomId && (existingByIssue.status === "completed" || existingByIssue.status === "errored")
|
|
670
670
|
);
|
|
671
|
-
const authenticatedHostHandle = runtime.authenticatedHostHandleCache.get(input.deps.
|
|
671
|
+
const authenticatedHostHandle = runtime.authenticatedHostHandleCache.get(input.deps.bandRest) ?? null;
|
|
672
672
|
const canBootstrapDirectly = options?.skipRoomWrite === true || authenticatedHostHandle === null || authenticatedHostHandle === hostAgentHandle;
|
|
673
673
|
if (!options?.skipRoomWrite || !canBootstrapDirectly) {
|
|
674
674
|
roomRecord = await forwardBridgeMessage({
|
|
675
|
-
|
|
675
|
+
bandRest: input.deps.bandRest,
|
|
676
676
|
store: input.deps.store,
|
|
677
677
|
logger,
|
|
678
678
|
roomRecord,
|
|
@@ -690,7 +690,7 @@ async function handleAgentSessionEvent(input, options) {
|
|
|
690
690
|
await enqueueBootstrapRequest(input.deps.store, {
|
|
691
691
|
eventKey,
|
|
692
692
|
linearSessionId: sessionId,
|
|
693
|
-
|
|
693
|
+
bandRoomId: roomRecord.bandRoomId,
|
|
694
694
|
expectedContent: message,
|
|
695
695
|
messageType: "task",
|
|
696
696
|
senderId: linearActor.id,
|
|
@@ -713,7 +713,7 @@ async function handleAgentSessionEvent(input, options) {
|
|
|
713
713
|
logger.info("linear_thenvoi_bridge.message_forwarded", {
|
|
714
714
|
sessionId,
|
|
715
715
|
issueId,
|
|
716
|
-
roomId: roomRecord.
|
|
716
|
+
roomId: roomRecord.bandRoomId,
|
|
717
717
|
action
|
|
718
718
|
});
|
|
719
719
|
} catch (error) {
|
|
@@ -784,7 +784,7 @@ async function trySetSessionExternalUrl(input) {
|
|
|
784
784
|
const base = input.appBaseUrl.replace(/\/+$/, "");
|
|
785
785
|
const roomUrl = `${base}/rooms/${input.roomId}`;
|
|
786
786
|
await input.linearClient.agentSessionUpdateExternalUrl(input.sessionId, {
|
|
787
|
-
externalUrls: [{ label: "View in
|
|
787
|
+
externalUrls: [{ label: "View in Band", url: roomUrl }]
|
|
788
788
|
});
|
|
789
789
|
input.logger.info("linear_thenvoi_bridge.external_url_set", {
|
|
790
790
|
sessionId: input.sessionId,
|
|
@@ -797,7 +797,7 @@ function normalizeConfig(config) {
|
|
|
797
797
|
roomStrategy: config.roomStrategy ?? "issue",
|
|
798
798
|
writebackMode: config.writebackMode ?? "final_only",
|
|
799
799
|
hostAgentHandle: normalizeOptionalHandle(config.hostAgentHandle),
|
|
800
|
-
|
|
800
|
+
bandAppBaseUrl: config.bandAppBaseUrl ?? DEFAULT_APP_BASE_URL
|
|
801
801
|
};
|
|
802
802
|
}
|
|
803
803
|
function normalizeOptionalHandle(handle) {
|
|
@@ -866,23 +866,23 @@ async function tryMoveIssueToStarted(input) {
|
|
|
866
866
|
}
|
|
867
867
|
async function resolveHostAgentHandle(input) {
|
|
868
868
|
if (input.configuredHostHandle) {
|
|
869
|
-
input.runtime.resolvedHostHandleCache.set(input.
|
|
869
|
+
input.runtime.resolvedHostHandleCache.set(input.bandRest, input.configuredHostHandle);
|
|
870
870
|
return input.configuredHostHandle;
|
|
871
871
|
}
|
|
872
|
-
const cachedAuthenticated = input.runtime.authenticatedHostHandleCache.get(input.
|
|
872
|
+
const cachedAuthenticated = input.runtime.authenticatedHostHandleCache.get(input.bandRest);
|
|
873
873
|
if (cachedAuthenticated) {
|
|
874
|
-
input.runtime.resolvedHostHandleCache.set(input.
|
|
874
|
+
input.runtime.resolvedHostHandleCache.set(input.bandRest, cachedAuthenticated);
|
|
875
875
|
return cachedAuthenticated;
|
|
876
876
|
}
|
|
877
|
-
const cached = input.runtime.resolvedHostHandleCache.get(input.
|
|
877
|
+
const cached = input.runtime.resolvedHostHandleCache.get(input.bandRest);
|
|
878
878
|
if (cached) {
|
|
879
879
|
return cached;
|
|
880
880
|
}
|
|
881
|
-
const identity = await input.
|
|
881
|
+
const identity = await input.bandRest.getAgentMe();
|
|
882
882
|
const authenticatedAgentHandle = normalizeOptionalHandle(identity.handle);
|
|
883
883
|
if (authenticatedAgentHandle) {
|
|
884
|
-
input.runtime.authenticatedHostHandleCache.set(input.
|
|
885
|
-
input.runtime.resolvedHostHandleCache.set(input.
|
|
884
|
+
input.runtime.authenticatedHostHandleCache.set(input.bandRest, authenticatedAgentHandle);
|
|
885
|
+
input.runtime.resolvedHostHandleCache.set(input.bandRest, authenticatedAgentHandle);
|
|
886
886
|
return authenticatedAgentHandle;
|
|
887
887
|
}
|
|
888
888
|
throw new Error(
|
|
@@ -916,14 +916,14 @@ async function handleCanceledAction(input) {
|
|
|
916
916
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
917
917
|
});
|
|
918
918
|
if (!input.skipRoomWrite) {
|
|
919
|
-
await input.deps.
|
|
919
|
+
await input.deps.bandRest.createChatEvent(existing.bandRoomId, {
|
|
920
920
|
content: "[Linear]: Agent session canceled. Stop in-room execution and await new instructions.",
|
|
921
921
|
messageType: "task",
|
|
922
922
|
metadata: {
|
|
923
923
|
linear_event_action: "canceled",
|
|
924
924
|
linear_session_id: input.sessionId,
|
|
925
925
|
linear_issue_id: input.issueId,
|
|
926
|
-
linear_bridge: "
|
|
926
|
+
linear_bridge: "band"
|
|
927
927
|
}
|
|
928
928
|
});
|
|
929
929
|
}
|
|
@@ -959,11 +959,11 @@ async function handlePromptedAction(input) {
|
|
|
959
959
|
const metadata = {
|
|
960
960
|
linear_event_action: "prompted",
|
|
961
961
|
linear_session_id: input.sessionId,
|
|
962
|
-
linear_bridge: "
|
|
962
|
+
linear_bridge: "band"
|
|
963
963
|
};
|
|
964
|
-
const canBootstrapDirectly = input.config.hostAgentHandle !== null ? true : Boolean(normalizeOptionalHandle((await input.deps.
|
|
964
|
+
const canBootstrapDirectly = input.config.hostAgentHandle !== null ? true : Boolean(normalizeOptionalHandle((await input.deps.bandRest.getAgentMe()).handle));
|
|
965
965
|
if (!input.skipRoomWrite || !canBootstrapDirectly) {
|
|
966
|
-
await input.deps.
|
|
966
|
+
await input.deps.bandRest.createChatEvent(existing.bandRoomId, {
|
|
967
967
|
content: message,
|
|
968
968
|
messageType: "text",
|
|
969
969
|
metadata
|
|
@@ -974,7 +974,7 @@ async function handlePromptedAction(input) {
|
|
|
974
974
|
await enqueueBootstrapRequest(input.deps.store, {
|
|
975
975
|
eventKey: input.eventKey,
|
|
976
976
|
linearSessionId: input.sessionId,
|
|
977
|
-
|
|
977
|
+
bandRoomId: existing.bandRoomId,
|
|
978
978
|
expectedContent: message,
|
|
979
979
|
messageType: "text",
|
|
980
980
|
senderId: linearActor.id,
|
|
@@ -990,7 +990,7 @@ async function handlePromptedAction(input) {
|
|
|
990
990
|
});
|
|
991
991
|
input.logger.info("linear_thenvoi_bridge.prompted_forwarded", {
|
|
992
992
|
sessionId: input.sessionId,
|
|
993
|
-
roomId: existing.
|
|
993
|
+
roomId: existing.bandRoomId
|
|
994
994
|
});
|
|
995
995
|
}
|
|
996
996
|
async function enqueueBootstrapRequest(store, request) {
|
|
@@ -1032,7 +1032,7 @@ async function resolveRoomRecord(input) {
|
|
|
1032
1032
|
}
|
|
1033
1033
|
async function forwardBridgeMessage(input) {
|
|
1034
1034
|
try {
|
|
1035
|
-
await input.
|
|
1035
|
+
await input.bandRest.createChatEvent(input.roomRecord.bandRoomId, {
|
|
1036
1036
|
content: input.message,
|
|
1037
1037
|
messageType: input.messageType,
|
|
1038
1038
|
metadata: input.metadata
|
|
@@ -1045,26 +1045,26 @@ async function forwardBridgeMessage(input) {
|
|
|
1045
1045
|
input.logger.warn("linear_thenvoi_bridge.room_forward_recovering_with_fresh_room", {
|
|
1046
1046
|
sessionId: input.sessionId,
|
|
1047
1047
|
issueId: input.issueId,
|
|
1048
|
-
previousRoomId: input.roomRecord.
|
|
1048
|
+
previousRoomId: input.roomRecord.bandRoomId,
|
|
1049
1049
|
error: error instanceof Error ? error.message : String(error)
|
|
1050
1050
|
});
|
|
1051
1051
|
const recoveredRoom = await createFreshRoomRecord({
|
|
1052
|
-
|
|
1052
|
+
bandRest: input.bandRest,
|
|
1053
1053
|
store: input.store,
|
|
1054
1054
|
sessionId: input.sessionId,
|
|
1055
1055
|
issueId: input.issueId,
|
|
1056
1056
|
logger: input.logger
|
|
1057
1057
|
});
|
|
1058
1058
|
await ensureRoomParticipants({
|
|
1059
|
-
|
|
1060
|
-
roomId: recoveredRoom.
|
|
1059
|
+
bandRest: input.bandRest,
|
|
1060
|
+
roomId: recoveredRoom.bandRoomId,
|
|
1061
1061
|
handles: [input.hostAgentHandle],
|
|
1062
1062
|
logger: input.logger
|
|
1063
1063
|
});
|
|
1064
1064
|
let attempt = 0;
|
|
1065
1065
|
while (true) {
|
|
1066
1066
|
try {
|
|
1067
|
-
await input.
|
|
1067
|
+
await input.bandRest.createChatEvent(recoveredRoom.bandRoomId, {
|
|
1068
1068
|
content: input.message,
|
|
1069
1069
|
messageType: input.messageType,
|
|
1070
1070
|
metadata: input.metadata
|
|
@@ -1080,7 +1080,7 @@ async function forwardBridgeMessage(input) {
|
|
|
1080
1080
|
input.logger.warn("linear_thenvoi_bridge.room_recreated_retrying", {
|
|
1081
1081
|
sessionId: input.sessionId,
|
|
1082
1082
|
issueId: input.issueId,
|
|
1083
|
-
roomId: recoveredRoom.
|
|
1083
|
+
roomId: recoveredRoom.bandRoomId,
|
|
1084
1084
|
attempt,
|
|
1085
1085
|
delayMs,
|
|
1086
1086
|
error: recoveredError instanceof Error ? recoveredError.message : String(recoveredError)
|
|
@@ -1110,11 +1110,11 @@ function isRetryableRateLimitError(error) {
|
|
|
1110
1110
|
}
|
|
1111
1111
|
async function createFreshRoomRecord(input) {
|
|
1112
1112
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1113
|
-
const created = await input.
|
|
1113
|
+
const created = await input.bandRest.createChat();
|
|
1114
1114
|
const record = {
|
|
1115
1115
|
linearSessionId: input.sessionId,
|
|
1116
1116
|
linearIssueId: input.issueId,
|
|
1117
|
-
|
|
1117
|
+
bandRoomId: created.id,
|
|
1118
1118
|
status: "active",
|
|
1119
1119
|
lastEventKey: null,
|
|
1120
1120
|
createdAt: now,
|
|
@@ -1140,7 +1140,7 @@ async function resolveRoomRecordImpl(input) {
|
|
|
1140
1140
|
const linkedRecord = {
|
|
1141
1141
|
linearSessionId: input.sessionId,
|
|
1142
1142
|
linearIssueId: input.issueId,
|
|
1143
|
-
|
|
1143
|
+
bandRoomId: existingByIssue.bandRoomId,
|
|
1144
1144
|
status: "active",
|
|
1145
1145
|
lastEventKey: existingBySession?.lastEventKey ?? null,
|
|
1146
1146
|
createdAt: existingBySession?.createdAt ?? now2,
|
|
@@ -1151,11 +1151,11 @@ async function resolveRoomRecordImpl(input) {
|
|
|
1151
1151
|
}
|
|
1152
1152
|
}
|
|
1153
1153
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1154
|
-
const created = await input.
|
|
1154
|
+
const created = await input.bandRest.createChat();
|
|
1155
1155
|
const createdRecord = {
|
|
1156
1156
|
linearSessionId: input.sessionId,
|
|
1157
1157
|
linearIssueId: input.issueId,
|
|
1158
|
-
|
|
1158
|
+
bandRoomId: created.id,
|
|
1159
1159
|
status: "active",
|
|
1160
1160
|
lastEventKey: null,
|
|
1161
1161
|
createdAt: now,
|
|
@@ -1176,7 +1176,7 @@ async function ensureRoomParticipants(input) {
|
|
|
1176
1176
|
}
|
|
1177
1177
|
let existingParticipants;
|
|
1178
1178
|
try {
|
|
1179
|
-
existingParticipants = await input.
|
|
1179
|
+
existingParticipants = await input.bandRest.listChatParticipants(input.roomId);
|
|
1180
1180
|
} catch (error) {
|
|
1181
1181
|
if (isRetryableRateLimitError(error)) {
|
|
1182
1182
|
input.logger.warn("linear_thenvoi_bridge.participant_list_rate_limited", {
|
|
@@ -1196,7 +1196,7 @@ async function ensureRoomParticipants(input) {
|
|
|
1196
1196
|
}
|
|
1197
1197
|
let peersByHandle;
|
|
1198
1198
|
try {
|
|
1199
|
-
peersByHandle = await lookupPeerIdsByHandle(input.
|
|
1199
|
+
peersByHandle = await lookupPeerIdsByHandle(input.bandRest, input.roomId, missingHandles);
|
|
1200
1200
|
} catch (error) {
|
|
1201
1201
|
if (error instanceof UnsupportedFeatureError) {
|
|
1202
1202
|
input.logger.warn("linear_thenvoi_bridge.peer_lookup_unavailable", {
|
|
@@ -1218,7 +1218,7 @@ async function ensureRoomParticipants(input) {
|
|
|
1218
1218
|
continue;
|
|
1219
1219
|
}
|
|
1220
1220
|
try {
|
|
1221
|
-
await input.
|
|
1221
|
+
await input.bandRest.addChatParticipant(
|
|
1222
1222
|
input.roomId,
|
|
1223
1223
|
{
|
|
1224
1224
|
participantId,
|
|
@@ -1237,14 +1237,14 @@ async function ensureRoomParticipants(input) {
|
|
|
1237
1237
|
}
|
|
1238
1238
|
}
|
|
1239
1239
|
}
|
|
1240
|
-
async function lookupPeerIdsByHandle(
|
|
1241
|
-
if (!
|
|
1240
|
+
async function lookupPeerIdsByHandle(bandRest, roomId, handles) {
|
|
1241
|
+
if (!bandRest.listPeers) {
|
|
1242
1242
|
throw new UnsupportedFeatureError("Peer listing is not available in current REST adapter");
|
|
1243
1243
|
}
|
|
1244
1244
|
const targets = new Set(handles);
|
|
1245
1245
|
const found = /* @__PURE__ */ new Map();
|
|
1246
1246
|
for (let page = 1; page <= MAX_PEER_LOOKUP_PAGES && found.size < targets.size; page += 1) {
|
|
1247
|
-
const response = await
|
|
1247
|
+
const response = await bandRest.listPeers({
|
|
1248
1248
|
page,
|
|
1249
1249
|
pageSize: PEER_PAGE_SIZE,
|
|
1250
1250
|
notInChat: roomId
|
|
@@ -1285,12 +1285,12 @@ async function selectRelevantPeerHandles(input) {
|
|
|
1285
1285
|
});
|
|
1286
1286
|
return configuredHandles;
|
|
1287
1287
|
}
|
|
1288
|
-
if (!input.
|
|
1288
|
+
if (!input.bandRest.listPeers) {
|
|
1289
1289
|
return [];
|
|
1290
1290
|
}
|
|
1291
1291
|
let peers;
|
|
1292
1292
|
try {
|
|
1293
|
-
const response = await input.
|
|
1293
|
+
const response = await input.bandRest.listPeers({
|
|
1294
1294
|
page: 1,
|
|
1295
1295
|
pageSize: PEER_PAGE_SIZE,
|
|
1296
1296
|
notInChat: input.roomId
|
|
@@ -1480,7 +1480,7 @@ var SqliteSessionRoomStore = class {
|
|
|
1480
1480
|
).run(
|
|
1481
1481
|
record.linearSessionId,
|
|
1482
1482
|
record.linearIssueId,
|
|
1483
|
-
record.
|
|
1483
|
+
record.bandRoomId,
|
|
1484
1484
|
record.status,
|
|
1485
1485
|
record.lastEventKey ?? null,
|
|
1486
1486
|
record.lastLinearActivityAt ?? null,
|
|
@@ -1527,7 +1527,7 @@ var SqliteSessionRoomStore = class {
|
|
|
1527
1527
|
).run(
|
|
1528
1528
|
request.eventKey,
|
|
1529
1529
|
request.linearSessionId,
|
|
1530
|
-
request.
|
|
1530
|
+
request.bandRoomId,
|
|
1531
1531
|
request.expectedContent,
|
|
1532
1532
|
request.messageType,
|
|
1533
1533
|
request.metadata ? JSON.stringify(request.metadata) : null,
|
|
@@ -1559,7 +1559,7 @@ var SqliteSessionRoomStore = class {
|
|
|
1559
1559
|
return rows.map((row) => ({
|
|
1560
1560
|
eventKey: row.event_key,
|
|
1561
1561
|
linearSessionId: row.linear_session_id,
|
|
1562
|
-
|
|
1562
|
+
bandRoomId: row.thenvoi_room_id,
|
|
1563
1563
|
expectedContent: row.expected_content,
|
|
1564
1564
|
messageType: row.message_type,
|
|
1565
1565
|
metadata: this.parseMetadata(row.metadata_json),
|
|
@@ -1697,7 +1697,7 @@ var SqliteSessionRoomStore = class {
|
|
|
1697
1697
|
return {
|
|
1698
1698
|
linearSessionId: row.linear_session_id,
|
|
1699
1699
|
linearIssueId: row.linear_issue_id,
|
|
1700
|
-
|
|
1700
|
+
bandRoomId: row.thenvoi_room_id,
|
|
1701
1701
|
status: row.status,
|
|
1702
1702
|
lastEventKey: row.last_event_key,
|
|
1703
1703
|
lastLinearActivityAt: row.last_linear_activity_at,
|
|
@@ -1753,19 +1753,19 @@ function parseSessionRoomRow(value) {
|
|
|
1753
1753
|
}
|
|
1754
1754
|
const linearSessionId = asString(row.linear_session_id);
|
|
1755
1755
|
const linearIssueId = asNullableString(row.linear_issue_id);
|
|
1756
|
-
const
|
|
1756
|
+
const bandRoomId = asString(row.thenvoi_room_id);
|
|
1757
1757
|
const status = asString(row.status);
|
|
1758
1758
|
const lastEventKey = asNullableString(row.last_event_key);
|
|
1759
1759
|
const lastLinearActivityAt = asNullableString(row.last_linear_activity_at);
|
|
1760
1760
|
const createdAt = asString(row.created_at);
|
|
1761
1761
|
const updatedAt = asString(row.updated_at);
|
|
1762
|
-
if (!linearSessionId || linearIssueId === void 0 || !
|
|
1762
|
+
if (!linearSessionId || linearIssueId === void 0 || !bandRoomId || !status || !isSessionStatus(status) || lastEventKey === void 0 || lastLinearActivityAt === void 0 || !createdAt || !updatedAt) {
|
|
1763
1763
|
return null;
|
|
1764
1764
|
}
|
|
1765
1765
|
return {
|
|
1766
1766
|
linear_session_id: linearSessionId,
|
|
1767
1767
|
linear_issue_id: linearIssueId,
|
|
1768
|
-
thenvoi_room_id:
|
|
1768
|
+
thenvoi_room_id: bandRoomId,
|
|
1769
1769
|
status,
|
|
1770
1770
|
last_event_key: lastEventKey,
|
|
1771
1771
|
last_linear_activity_at: lastLinearActivityAt,
|
|
@@ -1786,20 +1786,20 @@ function parseBootstrapRequestRow(value) {
|
|
|
1786
1786
|
}
|
|
1787
1787
|
const eventKey = asString(row.event_key);
|
|
1788
1788
|
const linearSessionId = asString(row.linear_session_id);
|
|
1789
|
-
const
|
|
1789
|
+
const bandRoomId = asString(row.thenvoi_room_id);
|
|
1790
1790
|
const expectedContent = asString(row.expected_content);
|
|
1791
1791
|
const messageType = asString(row.message_type);
|
|
1792
1792
|
const metadataJson = asNullableString(row.metadata_json);
|
|
1793
1793
|
const createdAt = asString(row.created_at);
|
|
1794
1794
|
const expiresAt = asString(row.expires_at);
|
|
1795
1795
|
const processedAt = asNullableString(row.processed_at);
|
|
1796
|
-
if (!eventKey || !linearSessionId || !
|
|
1796
|
+
if (!eventKey || !linearSessionId || !bandRoomId || !expectedContent || !messageType || metadataJson === void 0 || !createdAt || !expiresAt || processedAt === void 0) {
|
|
1797
1797
|
return null;
|
|
1798
1798
|
}
|
|
1799
1799
|
return {
|
|
1800
1800
|
event_key: eventKey,
|
|
1801
1801
|
linear_session_id: linearSessionId,
|
|
1802
|
-
thenvoi_room_id:
|
|
1802
|
+
thenvoi_room_id: bandRoomId,
|
|
1803
1803
|
expected_content: expectedContent,
|
|
1804
1804
|
message_type: messageType,
|
|
1805
1805
|
metadata_json: metadataJson,
|
|
@@ -2230,7 +2230,7 @@ function addSessionCreationTools(input) {
|
|
|
2230
2230
|
const { tools, client, store, logger } = input;
|
|
2231
2231
|
const sessionCreationBaseSchema = z.object({
|
|
2232
2232
|
external_link: z.string().url().optional().describe("Optional URL of an external page associated with this session"),
|
|
2233
|
-
room_id: z.string().min(1).optional().describe("The
|
|
2233
|
+
room_id: 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.")
|
|
2234
2234
|
});
|
|
2235
2235
|
const sessionCreationSchema = sessionCreationBaseSchema.extend({
|
|
2236
2236
|
issue_id: z.string().uuid().describe("The Linear issue ID (UUID)")
|
|
@@ -2242,7 +2242,7 @@ function addSessionCreationTools(input) {
|
|
|
2242
2242
|
await store.upsert({
|
|
2243
2243
|
linearSessionId: sessionId,
|
|
2244
2244
|
linearIssueId: issueId,
|
|
2245
|
-
|
|
2245
|
+
bandRoomId: roomId,
|
|
2246
2246
|
status: "active",
|
|
2247
2247
|
createdAt: now,
|
|
2248
2248
|
updatedAt: now
|
|
@@ -2300,7 +2300,7 @@ function addSessionCreationTools(input) {
|
|
|
2300
2300
|
if (typeof createIssueFn === "function") {
|
|
2301
2301
|
tools.push({
|
|
2302
2302
|
name: "linear_create_issue",
|
|
2303
|
-
description: "Create a new Linear issue from scratch. Use this when the
|
|
2303
|
+
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.",
|
|
2304
2304
|
schema: z.object({
|
|
2305
2305
|
team_id: z.string().uuid().describe("The Linear team ID to create the issue in"),
|
|
2306
2306
|
title: z.string().min(1).describe("Issue title"),
|
|
@@ -2435,21 +2435,21 @@ async function handleIssueUnassigned(input) {
|
|
|
2435
2435
|
});
|
|
2436
2436
|
return;
|
|
2437
2437
|
}
|
|
2438
|
-
await deps.
|
|
2438
|
+
await deps.bandRest.createChatEvent(existing.bandRoomId, {
|
|
2439
2439
|
content: "[Linear]: Issue unassigned from agent. Disengage from active work and await reassignment.",
|
|
2440
2440
|
messageType: "task",
|
|
2441
2441
|
metadata: {
|
|
2442
2442
|
linear_notification_type: "issueUnassignedFromYou",
|
|
2443
2443
|
linear_issue_id: notification.issueId,
|
|
2444
2444
|
linear_actor_id: notification.actorId ?? null,
|
|
2445
|
-
linear_bridge: "
|
|
2445
|
+
linear_bridge: "band"
|
|
2446
2446
|
}
|
|
2447
2447
|
});
|
|
2448
2448
|
await deps.store.markCanceled(existing.linearSessionId);
|
|
2449
2449
|
logger.info("linear_thenvoi_bridge.notification_unassigned_handled", {
|
|
2450
2450
|
issueId: notification.issueId,
|
|
2451
2451
|
sessionId: existing.linearSessionId,
|
|
2452
|
-
roomId: existing.
|
|
2452
|
+
roomId: existing.bandRoomId
|
|
2453
2453
|
});
|
|
2454
2454
|
}
|
|
2455
2455
|
async function handleIssueNewComment(input) {
|
|
@@ -2488,7 +2488,7 @@ async function handleIssueNewComment(input) {
|
|
|
2488
2488
|
actorId: notification.actorId ?? null
|
|
2489
2489
|
});
|
|
2490
2490
|
}
|
|
2491
|
-
await deps.
|
|
2491
|
+
await deps.bandRest.createChatEvent(existing.bandRoomId, {
|
|
2492
2492
|
content: `[Linear Comment from ${actorName}]: ${commentBody}`,
|
|
2493
2493
|
messageType: "text",
|
|
2494
2494
|
metadata: {
|
|
@@ -2496,14 +2496,14 @@ async function handleIssueNewComment(input) {
|
|
|
2496
2496
|
linear_issue_id: notification.issueId,
|
|
2497
2497
|
linear_comment_id: notification.commentId,
|
|
2498
2498
|
linear_actor_id: notification.actorId ?? null,
|
|
2499
|
-
linear_bridge: "
|
|
2499
|
+
linear_bridge: "band"
|
|
2500
2500
|
}
|
|
2501
2501
|
});
|
|
2502
2502
|
logger.info("linear_thenvoi_bridge.notification_comment_forwarded", {
|
|
2503
2503
|
issueId: notification.issueId,
|
|
2504
2504
|
commentId: notification.commentId,
|
|
2505
2505
|
sessionId: existing.linearSessionId,
|
|
2506
|
-
roomId: existing.
|
|
2506
|
+
roomId: existing.bandRoomId
|
|
2507
2507
|
});
|
|
2508
2508
|
}
|
|
2509
2509
|
function logReaction(input) {
|