@sideboard-ai/core 0.1.261 → 0.1.263
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/{abletime-OQ4PC52L.js → abletime-6AOJI2YZ.js} +4 -4
- package/dist/{abletime-CDGINDXG.js → abletime-U75ZGDIX.js} +5 -5
- package/dist/{abletime-oauth-KX2TDFTE.js → abletime-oauth-LOAHGG4X.js} +3 -3
- package/dist/{abletime-oauth-5A5EOH2A.js → abletime-oauth-TA2YDCYX.js} +4 -4
- package/dist/{abletime-rest-UEHOBLXD.js → abletime-rest-CTA5WV26.js} +4 -4
- package/dist/{abletime-rest-PUGKYTRR.js → abletime-rest-YZPFM6IY.js} +3 -3
- package/dist/agents/claude-runner.cjs +463 -0
- package/dist/agents/claude-runner.d.cts +1 -0
- package/dist/agents/claude-runner.d.ts +1 -0
- package/dist/agents/claude-runner.js +134 -0
- package/dist/agents/cursor-runner.cjs +388 -7
- package/dist/agents/cursor-runner.js +34 -7
- package/dist/{agents-J2P4KNEY.js → agents-3TUMFSMH.js} +10 -9
- package/dist/{agents-IKVUGW5H.js → agents-6FWDRVA2.js} +4 -4
- package/dist/{app-settings-OM7H64YM.js → app-settings-MVZPM6WW.js} +1 -1
- package/dist/{app-settings-VQEUCONM.js → app-settings-RKFFUIFQ.js} +2 -2
- package/dist/{chunk-6PFZ6XO7.js → chunk-2QUQH2SZ.js} +81 -6
- package/dist/{chunk-VIC6TDYT.js → chunk-36FOBJBF.js} +76 -3
- package/dist/{chunk-ABPNIYRS.js → chunk-3H7O27X2.js} +389 -102
- package/dist/{chunk-FSZ7YYFQ.js → chunk-4ALDX5IO.js} +5 -3
- package/dist/{chunk-5OKJDCBK.js → chunk-5PK53SAP.js} +5 -5
- package/dist/{chunk-U3AOJLH3.js → chunk-5ZPT5YAV.js} +8 -6
- package/dist/{chunk-YFTO7U2A.js → chunk-6YOXQVJ7.js} +3 -3
- package/dist/{chunk-J6ONZP74.js → chunk-6YY7XPPL.js} +5 -5
- package/dist/{chunk-WV7KJH2E.js → chunk-A5SAYFJ4.js} +1 -0
- package/dist/{chunk-B6XFBNYM.js → chunk-BOMSJUXW.js} +2 -2
- package/dist/{chunk-S7VDDJ34.js → chunk-CEOTK5GZ.js} +3 -3
- package/dist/{chunk-4F7J6UWD.js → chunk-EK3KVT7I.js} +515 -139
- package/dist/{chunk-HHEKVR6C.js → chunk-F5MFNUAA.js} +40 -2
- package/dist/{chunk-KVQX62XI.js → chunk-K7B25HO4.js} +3 -3
- package/dist/{chunk-JTNY5CK4.js → chunk-L5WGAIL2.js} +46 -46
- package/dist/chunk-NYBFYFK7.js +415 -0
- package/dist/{chunk-GIZQAOHZ.js → chunk-QX6QUZVD.js} +121 -91
- package/dist/{chunk-X2OTZ36G.js → chunk-RKPBIB2U.js} +52 -52
- package/dist/{chunk-W5PWGEKS.js → chunk-T3AYRQJZ.js} +107 -77
- package/dist/{chunk-D554YCHG.js → chunk-YKYEHDMX.js} +2 -2
- package/dist/connected-teams-RU6VATCM.js +1 -1
- package/dist/{coordinator-prompt-VQHQ3RIX.js → coordinator-prompt-HZCQZ6C5.js} +6 -6
- package/dist/{coordinator-prompt-TU6656OK.js → coordinator-prompt-J7DKHOCH.js} +3 -3
- package/dist/{global-workspace-PHF2MOGW.js → global-workspace-6LDUZ2NF.js} +3 -3
- package/dist/{global-workspace-47NZVA44.js → global-workspace-TI26K25J.js} +6 -6
- package/dist/index.cjs +1712 -721
- package/dist/index.d.cts +181 -41
- package/dist/index.d.ts +181 -41
- package/dist/index.js +693 -285
- package/dist/mcp/run-stdio.cjs +10469 -9896
- package/dist/mcp/run-stdio.js +193 -168
- package/dist/{orchestrator-VLTO5JBR.js → orchestrator-4JSG7BWC.js} +5 -5
- package/dist/{orchestrator-OCG4QVA5.js → orchestrator-5YU46YNS.js} +11 -10
- package/dist/{workspaces-I7TWFTJM.js → workspaces-QJ3XIPUJ.js} +6 -6
- package/dist/{workspaces-2XTDS7N2.js → workspaces-WOQHJFRQ.js} +3 -3
- package/dist/{worktree-DDDF3WHV.js → worktree-6U2FA5LO.js} +2 -2
- package/dist/{worktree-YASFE65H.js → worktree-KQA4ON2G.js} +5 -5
- package/package.json +4 -3
- /package/dist/{chunk-LDXSAVWO.js → chunk-GWFVCCJZ.js} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,66 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ABLETIME_OAUTH_CALLBACK_PATH,
|
|
3
|
-
ABLETIME_OAUTH_CANCELLED,
|
|
4
|
-
ABLETIME_OAUTH_LOCAL_CALLBACK,
|
|
5
|
-
ABLETIME_OAUTH_PORT,
|
|
6
|
-
ABLETIME_OAUTH_REDIRECT,
|
|
7
|
-
ABLETIME_OAUTH_RESOURCE,
|
|
8
|
-
AbleTimeOAuthCancelledError,
|
|
9
|
-
BAKED_ABLETIME_OAUTH_CLIENT_ID,
|
|
10
|
-
ableTimeOAuthAuthorizeUrl,
|
|
11
|
-
ableTimeOAuthBouncePage,
|
|
12
|
-
ableTimeOAuthClientId,
|
|
13
|
-
ableTimeOAuthLocalBounceUrl,
|
|
14
|
-
ableTimeOAuthRedirectUri,
|
|
15
|
-
ableTimeOAuthResource,
|
|
16
|
-
createAbleTimePkce,
|
|
17
|
-
ensureAbleTimeAccessToken,
|
|
18
|
-
hasBakedAbleTimeOAuth,
|
|
19
|
-
isAbleTimeOAuthCancelled,
|
|
20
|
-
parseAbleTimeOAuthCallbackUrl,
|
|
21
|
-
resolveAbleTimeOAuthHost,
|
|
22
|
-
startAbleTimeOAuth
|
|
23
|
-
} from "./chunk-YFTO7U2A.js";
|
|
24
|
-
import {
|
|
25
|
-
ableTimeDependencyTarget,
|
|
26
|
-
commentAbleTimeTask,
|
|
27
|
-
createAbleTimeTask,
|
|
28
|
-
ensureAbleTimeTask,
|
|
29
|
-
getAbleTimeOrientation,
|
|
30
|
-
getAbleTimeTask,
|
|
31
|
-
issueAttachmentForAbleTimeTask,
|
|
32
|
-
listAbleTimeAssignedIssues,
|
|
33
|
-
listAbleTimeProjects,
|
|
34
|
-
listAbleTimeTasks,
|
|
35
|
-
mapAbleTimeTask,
|
|
36
|
-
searchAbleTimeTasks,
|
|
37
|
-
taskUrl,
|
|
38
|
-
toAbleTimeIssueInfo,
|
|
39
|
-
updateAbleTimeTask,
|
|
40
|
-
verifyAbleTimeConnection
|
|
41
|
-
} from "./chunk-KVQX62XI.js";
|
|
42
|
-
import "./chunk-D554YCHG.js";
|
|
43
|
-
import {
|
|
44
|
-
ABLETIME_MCP_PATH,
|
|
45
|
-
ABLETIME_MCP_PM_PATH,
|
|
46
|
-
ABLETIME_OAUTH_TOKEN_PREFIX,
|
|
47
|
-
ABLETIME_ORG_KEY_PREFIX,
|
|
48
|
-
ABLETIME_PAT_PREFIX,
|
|
49
|
-
ABLETIME_PM_TOOL_NAMES,
|
|
50
|
-
DEFAULT_ABLETIME_HOST,
|
|
51
|
-
ableTimeCredentialKind,
|
|
52
|
-
ableTimeUsesRest,
|
|
53
|
-
abletimeMcpRequest,
|
|
54
|
-
abletimeMcpUrl,
|
|
55
|
-
abletimeToolUsesPm,
|
|
56
|
-
assertAbleTimeCredential,
|
|
57
|
-
assertAbleTimeMcpCredential,
|
|
58
|
-
callAbleTimeTool,
|
|
59
|
-
isAbleTimeRejectedCredential,
|
|
60
|
-
normalizeAbleTimeCredential,
|
|
61
|
-
normalizeAbleTimeHost,
|
|
62
|
-
rewriteAbleTimeError
|
|
63
|
-
} from "./chunk-J6ONZP74.js";
|
|
64
1
|
import {
|
|
65
2
|
AGENT_GIT_ACTIONS,
|
|
66
3
|
BOARD_COLUMN_DEFS,
|
|
@@ -335,15 +272,19 @@ import {
|
|
|
335
272
|
verifyOptionalService,
|
|
336
273
|
waitForDetachedJob,
|
|
337
274
|
waitForPidExit,
|
|
275
|
+
waitForTurnToolResult,
|
|
338
276
|
worktreeBoardStatus,
|
|
339
277
|
worktreeCleanupSettings,
|
|
340
278
|
worktreeRunPortsPath,
|
|
341
279
|
wrapReviewSkillMarkdown,
|
|
342
280
|
writeWorktreeFile
|
|
343
|
-
} from "./chunk-
|
|
281
|
+
} from "./chunk-QX6QUZVD.js";
|
|
344
282
|
import {
|
|
283
|
+
BASE_ALLOWED_TOOLS,
|
|
345
284
|
BRIGHTSY_MCP_ALLOWED_TOOLS,
|
|
285
|
+
CLAUDE_CHROME_ALLOWED_TOOLS,
|
|
346
286
|
CLAUDE_MODEL_CATALOG,
|
|
287
|
+
CLAUDE_PRINT_BG_WAIT_CEILING_MS,
|
|
347
288
|
MAX_ANTHROPIC_CACHE_CONTROL_BLOCKS,
|
|
348
289
|
PLAN_MODE_INSTRUCTION,
|
|
349
290
|
PLAN_MODE_OFF_INSTRUCTION,
|
|
@@ -445,6 +386,7 @@ import {
|
|
|
445
386
|
resolveQuotaFallbackAgent,
|
|
446
387
|
sanitizeMcpServerName,
|
|
447
388
|
shouldInjectBrightsyMcp,
|
|
389
|
+
shouldRegisterMcpWaitForTurn,
|
|
448
390
|
sideboardMcpProfile,
|
|
449
391
|
skillCommandFromBody,
|
|
450
392
|
skillCommandFromInput,
|
|
@@ -464,9 +406,83 @@ import {
|
|
|
464
406
|
whichOnPath,
|
|
465
407
|
withEventParentId,
|
|
466
408
|
withEventsParentId,
|
|
409
|
+
worktreeMcpToolNames,
|
|
467
410
|
writeInjectedMcpConfig
|
|
468
|
-
} from "./chunk-
|
|
411
|
+
} from "./chunk-3H7O27X2.js";
|
|
412
|
+
import {
|
|
413
|
+
brightsyMcpServerName,
|
|
414
|
+
connectBrightsyTeam,
|
|
415
|
+
disconnectBrightsyTeam,
|
|
416
|
+
ensureConnectedBrightsyTeamTokens,
|
|
417
|
+
getBrightsySession,
|
|
418
|
+
listBrightsyAccounts,
|
|
419
|
+
listConnectedBrightsyTeams,
|
|
420
|
+
switchBrightsyAccount
|
|
421
|
+
} from "./chunk-GWFVCCJZ.js";
|
|
469
422
|
import "./chunk-7H655NEW.js";
|
|
423
|
+
import {
|
|
424
|
+
ABLETIME_OAUTH_CALLBACK_PATH,
|
|
425
|
+
ABLETIME_OAUTH_CANCELLED,
|
|
426
|
+
ABLETIME_OAUTH_LOCAL_CALLBACK,
|
|
427
|
+
ABLETIME_OAUTH_PORT,
|
|
428
|
+
ABLETIME_OAUTH_REDIRECT,
|
|
429
|
+
ABLETIME_OAUTH_RESOURCE,
|
|
430
|
+
AbleTimeOAuthCancelledError,
|
|
431
|
+
BAKED_ABLETIME_OAUTH_CLIENT_ID,
|
|
432
|
+
ableTimeOAuthAuthorizeUrl,
|
|
433
|
+
ableTimeOAuthBouncePage,
|
|
434
|
+
ableTimeOAuthClientId,
|
|
435
|
+
ableTimeOAuthLocalBounceUrl,
|
|
436
|
+
ableTimeOAuthRedirectUri,
|
|
437
|
+
ableTimeOAuthResource,
|
|
438
|
+
createAbleTimePkce,
|
|
439
|
+
ensureAbleTimeAccessToken,
|
|
440
|
+
hasBakedAbleTimeOAuth,
|
|
441
|
+
isAbleTimeOAuthCancelled,
|
|
442
|
+
parseAbleTimeOAuthCallbackUrl,
|
|
443
|
+
resolveAbleTimeOAuthHost,
|
|
444
|
+
startAbleTimeOAuth
|
|
445
|
+
} from "./chunk-6YOXQVJ7.js";
|
|
446
|
+
import {
|
|
447
|
+
ableTimeDependencyTarget,
|
|
448
|
+
commentAbleTimeTask,
|
|
449
|
+
createAbleTimeTask,
|
|
450
|
+
ensureAbleTimeTask,
|
|
451
|
+
getAbleTimeOrientation,
|
|
452
|
+
getAbleTimeTask,
|
|
453
|
+
issueAttachmentForAbleTimeTask,
|
|
454
|
+
listAbleTimeAssignedIssues,
|
|
455
|
+
listAbleTimeProjects,
|
|
456
|
+
listAbleTimeTasks,
|
|
457
|
+
mapAbleTimeTask,
|
|
458
|
+
searchAbleTimeTasks,
|
|
459
|
+
taskUrl,
|
|
460
|
+
toAbleTimeIssueInfo,
|
|
461
|
+
updateAbleTimeTask,
|
|
462
|
+
verifyAbleTimeConnection
|
|
463
|
+
} from "./chunk-K7B25HO4.js";
|
|
464
|
+
import "./chunk-YKYEHDMX.js";
|
|
465
|
+
import {
|
|
466
|
+
ABLETIME_MCP_PATH,
|
|
467
|
+
ABLETIME_MCP_PM_PATH,
|
|
468
|
+
ABLETIME_OAUTH_TOKEN_PREFIX,
|
|
469
|
+
ABLETIME_ORG_KEY_PREFIX,
|
|
470
|
+
ABLETIME_PAT_PREFIX,
|
|
471
|
+
ABLETIME_PM_TOOL_NAMES,
|
|
472
|
+
DEFAULT_ABLETIME_HOST,
|
|
473
|
+
ableTimeCredentialKind,
|
|
474
|
+
ableTimeUsesRest,
|
|
475
|
+
abletimeMcpRequest,
|
|
476
|
+
abletimeMcpUrl,
|
|
477
|
+
abletimeToolUsesPm,
|
|
478
|
+
assertAbleTimeCredential,
|
|
479
|
+
assertAbleTimeMcpCredential,
|
|
480
|
+
callAbleTimeTool,
|
|
481
|
+
isAbleTimeRejectedCredential,
|
|
482
|
+
normalizeAbleTimeCredential,
|
|
483
|
+
normalizeAbleTimeHost,
|
|
484
|
+
rewriteAbleTimeError
|
|
485
|
+
} from "./chunk-6YY7XPPL.js";
|
|
470
486
|
import {
|
|
471
487
|
LEGACY_PLAN_FILE_REL,
|
|
472
488
|
PLAN_FILE_NAME,
|
|
@@ -527,7 +543,7 @@ import {
|
|
|
527
543
|
stageBuffersAsAttachments,
|
|
528
544
|
syncWorkspacesFromThreads,
|
|
529
545
|
takenTeamSlugsForOrchestration
|
|
530
|
-
} from "./chunk-
|
|
546
|
+
} from "./chunk-RKPBIB2U.js";
|
|
531
547
|
import {
|
|
532
548
|
FAMOUS_SOCCER_TEAMS,
|
|
533
549
|
GITHUB_PR_BODY_MAX_CHARS,
|
|
@@ -627,18 +643,26 @@ import {
|
|
|
627
643
|
worktreeDisplayLabelForGroup,
|
|
628
644
|
worktreeNameFromPath,
|
|
629
645
|
worktreeSlugForTicket
|
|
630
|
-
} from "./chunk-
|
|
631
|
-
import "./chunk-2P4XKWV2.js";
|
|
646
|
+
} from "./chunk-5ZPT5YAV.js";
|
|
632
647
|
import {
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
648
|
+
appendMessage,
|
|
649
|
+
createEmptyThread,
|
|
650
|
+
deleteThreadRecord,
|
|
651
|
+
findThreadByRef,
|
|
652
|
+
invalidateThreadListCache,
|
|
653
|
+
invalidateThreadRecord,
|
|
654
|
+
isSelfWrittenRecord,
|
|
655
|
+
isThreadRecordFile,
|
|
656
|
+
listThreads,
|
|
657
|
+
normalizeThread,
|
|
658
|
+
readThread,
|
|
659
|
+
resolveThreadEffort,
|
|
660
|
+
setStatus,
|
|
661
|
+
updateThread,
|
|
662
|
+
withThreadLock,
|
|
663
|
+
writeThread
|
|
664
|
+
} from "./chunk-FWNL62VA.js";
|
|
665
|
+
import "./chunk-2P4XKWV2.js";
|
|
642
666
|
import {
|
|
643
667
|
BRIGHTSY_OAUTH_CLIENT_ID,
|
|
644
668
|
BRIGHTSY_OAUTH_REDIRECT,
|
|
@@ -659,7 +683,7 @@ import {
|
|
|
659
683
|
} from "./chunk-N7WZ6NIN.js";
|
|
660
684
|
import {
|
|
661
685
|
clipMcpToolResult
|
|
662
|
-
} from "./chunk-
|
|
686
|
+
} from "./chunk-F5MFNUAA.js";
|
|
663
687
|
import "./chunk-AXQ4ZWHK.js";
|
|
664
688
|
import {
|
|
665
689
|
AGENT_RUNNER_MAX_OLD_SPACE_MB,
|
|
@@ -675,7 +699,7 @@ import {
|
|
|
675
699
|
trustSystemCertificates,
|
|
676
700
|
turnCostUsdFromCursorUsage,
|
|
677
701
|
withMaxOldSpaceSize
|
|
678
|
-
} from "./chunk-
|
|
702
|
+
} from "./chunk-A5SAYFJ4.js";
|
|
679
703
|
import {
|
|
680
704
|
ATTACHMENTS_DIR,
|
|
681
705
|
CLI_DIR,
|
|
@@ -685,6 +709,7 @@ import {
|
|
|
685
709
|
attachmentsGitignoreBody,
|
|
686
710
|
isWorkspaceScratchPath
|
|
687
711
|
} from "./chunk-6K64FJDD.js";
|
|
712
|
+
import "./chunk-UWQ6UCIB.js";
|
|
688
713
|
import {
|
|
689
714
|
conductorBundledBinDir,
|
|
690
715
|
enrichPathWithNpmGlobalBin,
|
|
@@ -701,6 +726,28 @@ import {
|
|
|
701
726
|
run,
|
|
702
727
|
withExportedPath
|
|
703
728
|
} from "./chunk-QVN2WOKJ.js";
|
|
729
|
+
import {
|
|
730
|
+
AGENT_RPC_JOB_HOLD_MAX_MS,
|
|
731
|
+
AGENT_RPC_JOB_SLICE_MS,
|
|
732
|
+
AGENT_RPC_NATIVE_ENV,
|
|
733
|
+
AGENT_RPC_PATH,
|
|
734
|
+
AGENT_RPC_PROTOCOL_VERSION,
|
|
735
|
+
AGENT_RPC_TOKEN_QUERY,
|
|
736
|
+
RPC_INTERNAL_ERROR,
|
|
737
|
+
RPC_INVALID_REQUEST,
|
|
738
|
+
RPC_METHOD_NOT_FOUND,
|
|
739
|
+
RPC_PARSE_ERROR,
|
|
740
|
+
RPC_UNAUTHORIZED,
|
|
741
|
+
agentRpcConnectFromMetadata,
|
|
742
|
+
clearAgentRuntimeMetadataIfOwned,
|
|
743
|
+
isJsonRpcRequest,
|
|
744
|
+
parseRpcFrame,
|
|
745
|
+
readAgentRuntimeMetadata,
|
|
746
|
+
rpcError,
|
|
747
|
+
rpcNotify,
|
|
748
|
+
rpcResult,
|
|
749
|
+
writeAgentRuntimeMetadata
|
|
750
|
+
} from "./chunk-NYBFYFK7.js";
|
|
704
751
|
import {
|
|
705
752
|
AGENT_DONE_SOUNDS,
|
|
706
753
|
CHAT_TEXT_SCALES,
|
|
@@ -794,6 +841,7 @@ import {
|
|
|
794
841
|
persistVaultKeyInKeychain,
|
|
795
842
|
planHistoryAgePurge,
|
|
796
843
|
planHistoryRetention,
|
|
844
|
+
previousCostBaseline,
|
|
797
845
|
readKeychainVaultKey,
|
|
798
846
|
readSecureJson,
|
|
799
847
|
readViewerContext,
|
|
@@ -826,6 +874,7 @@ import {
|
|
|
826
874
|
sumUsageList,
|
|
827
875
|
toPublicAppSettings,
|
|
828
876
|
totalTokens,
|
|
877
|
+
turnCostUsdFromProviderSnapshot,
|
|
829
878
|
updateAdvancedSettings,
|
|
830
879
|
updateAgentExecutable,
|
|
831
880
|
updateAppEnvironment,
|
|
@@ -837,9 +886,18 @@ import {
|
|
|
837
886
|
updateOpencodeSettings,
|
|
838
887
|
updateProjectProfileSettings,
|
|
839
888
|
usageOnLimit,
|
|
889
|
+
withTurnScopedCost,
|
|
840
890
|
writeSecureJson,
|
|
841
891
|
writeViewerContext
|
|
842
|
-
} from "./chunk-
|
|
892
|
+
} from "./chunk-2QUQH2SZ.js";
|
|
893
|
+
import {
|
|
894
|
+
THINKING_EFFORTS,
|
|
895
|
+
isThinkingEffort,
|
|
896
|
+
nextThinkingEffort,
|
|
897
|
+
normalizeThinkingEffort,
|
|
898
|
+
thinkingEffortBars,
|
|
899
|
+
thinkingEffortLabel
|
|
900
|
+
} from "./chunk-77WWLBCI.js";
|
|
843
901
|
import {
|
|
844
902
|
stripNestedElectronEnv
|
|
845
903
|
} from "./chunk-4TR3HZFT.js";
|
|
@@ -877,33 +935,6 @@ import {
|
|
|
877
935
|
import {
|
|
878
936
|
writePrivateFile
|
|
879
937
|
} from "./chunk-JT7R45JB.js";
|
|
880
|
-
import {
|
|
881
|
-
appendMessage,
|
|
882
|
-
createEmptyThread,
|
|
883
|
-
deleteThreadRecord,
|
|
884
|
-
findThreadByRef,
|
|
885
|
-
invalidateThreadListCache,
|
|
886
|
-
invalidateThreadRecord,
|
|
887
|
-
isSelfWrittenRecord,
|
|
888
|
-
isThreadRecordFile,
|
|
889
|
-
listThreads,
|
|
890
|
-
normalizeThread,
|
|
891
|
-
readThread,
|
|
892
|
-
resolveThreadEffort,
|
|
893
|
-
setStatus,
|
|
894
|
-
updateThread,
|
|
895
|
-
withThreadLock,
|
|
896
|
-
writeThread
|
|
897
|
-
} from "./chunk-FWNL62VA.js";
|
|
898
|
-
import "./chunk-UWQ6UCIB.js";
|
|
899
|
-
import {
|
|
900
|
-
THINKING_EFFORTS,
|
|
901
|
-
isThinkingEffort,
|
|
902
|
-
nextThinkingEffort,
|
|
903
|
-
normalizeThinkingEffort,
|
|
904
|
-
thinkingEffortBars,
|
|
905
|
-
thinkingEffortLabel
|
|
906
|
-
} from "./chunk-77WWLBCI.js";
|
|
907
938
|
import {
|
|
908
939
|
CONVENTION_SETUP_RELPATHS,
|
|
909
940
|
activeRunPreview,
|
|
@@ -3235,6 +3266,31 @@ function buildPastedTextAttachment(text7, opts) {
|
|
|
3235
3266
|
};
|
|
3236
3267
|
}
|
|
3237
3268
|
|
|
3269
|
+
// src/threads/chat-link.ts
|
|
3270
|
+
var CHAT_LINK_PREFIX = "sideboard://chat/";
|
|
3271
|
+
var LEGACY_THREAD_LINK_PREFIX = "sideboard://thread/";
|
|
3272
|
+
var ID = "[A-Za-z0-9_-]+";
|
|
3273
|
+
var CHAT_LINK_RE = new RegExp(`^sideboard://(?:chat|thread)/(${ID})/?$`, "i");
|
|
3274
|
+
var BARE_CHAT_LINK_RE = new RegExp(
|
|
3275
|
+
`(?<!\\]\\()(?<!href=["'])sideboard://(?:chat|thread)/${ID}/?`,
|
|
3276
|
+
"gi"
|
|
3277
|
+
);
|
|
3278
|
+
function chatLinkUrl(chatId) {
|
|
3279
|
+
return `${CHAT_LINK_PREFIX}${chatId.trim()}`;
|
|
3280
|
+
}
|
|
3281
|
+
function parseChatLink(href) {
|
|
3282
|
+
const match = CHAT_LINK_RE.exec(href.trim());
|
|
3283
|
+
return match?.[1] ?? null;
|
|
3284
|
+
}
|
|
3285
|
+
function linkifyChatUrls(text7) {
|
|
3286
|
+
return text7.replace(BARE_CHAT_LINK_RE, (url) => {
|
|
3287
|
+
const id = parseChatLink(url);
|
|
3288
|
+
if (!id) return url;
|
|
3289
|
+
const label = id.length > 8 ? id.slice(0, 8) : id;
|
|
3290
|
+
return `[${label}](${chatLinkUrl(id)})`;
|
|
3291
|
+
});
|
|
3292
|
+
}
|
|
3293
|
+
|
|
3238
3294
|
// src/orchestrator/outbound-turn-reply.ts
|
|
3239
3295
|
function outboundReplyFromTurn(result, copy) {
|
|
3240
3296
|
switch (result.taskState) {
|
|
@@ -3271,10 +3327,10 @@ function formatAskUserNotifyMessage(questions) {
|
|
|
3271
3327
|
}
|
|
3272
3328
|
function formatNotifyOrchestratorPrompt(input) {
|
|
3273
3329
|
const title = input.child.title?.trim() || "Untitled";
|
|
3274
|
-
const link = `[${title}](sideboard://
|
|
3330
|
+
const link = `[${title}](sideboard://chat/${input.child.id})`;
|
|
3275
3331
|
const body = clampMessage(input.message) || "(no details)";
|
|
3276
3332
|
const state = input.reason === "input-required" ? "is input-required (waiting for the user in that chat)" : "is blocked";
|
|
3277
|
-
const next = input.reason === "input-required" ? "Wait for the user in that chat. Do not
|
|
3333
|
+
const next = input.reason === "input-required" ? "Wait for the user in that chat. Do not send_to_chat a check-in." : "Help if this is your child; otherwise briefly tell the user. Do not send_to_chat a check-in.";
|
|
3278
3334
|
return [
|
|
3279
3335
|
`Sideboard: child worktree ${link} ${state}.`,
|
|
3280
3336
|
body,
|
|
@@ -3299,13 +3355,13 @@ function resolveNotifyCallerThread(opts) {
|
|
|
3299
3355
|
return thread;
|
|
3300
3356
|
}
|
|
3301
3357
|
const siblings = threadsSharingWorktree(cwd);
|
|
3302
|
-
const
|
|
3303
|
-
if (!
|
|
3358
|
+
const running2 = siblings.find((t) => t.status === "running") ?? siblings[0];
|
|
3359
|
+
if (!running2) {
|
|
3304
3360
|
throw new Error(
|
|
3305
3361
|
"No Sideboard thread for this cwd. Call notify_orchestrator from a worktree chat."
|
|
3306
3362
|
);
|
|
3307
3363
|
}
|
|
3308
|
-
return
|
|
3364
|
+
return running2;
|
|
3309
3365
|
}
|
|
3310
3366
|
function skipReason(opts) {
|
|
3311
3367
|
if (!opts.child.parentThreadId) {
|
|
@@ -3508,7 +3564,7 @@ function createClippedMcpServer() {
|
|
|
3508
3564
|
|
|
3509
3565
|
// src/mcp/server.ts
|
|
3510
3566
|
import { z as z9 } from "zod";
|
|
3511
|
-
import { randomUUID as
|
|
3567
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
3512
3568
|
import { basename as basename2 } from "path";
|
|
3513
3569
|
|
|
3514
3570
|
// src/mcp/archive-guard.ts
|
|
@@ -4039,7 +4095,7 @@ async function continueSourceThread(threadId, prompt) {
|
|
|
4039
4095
|
await continueOnReply(threadId, prompt);
|
|
4040
4096
|
return;
|
|
4041
4097
|
}
|
|
4042
|
-
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-
|
|
4098
|
+
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-5YU46YNS.js");
|
|
4043
4099
|
await getOrchestrator2().send(threadId, prompt);
|
|
4044
4100
|
} catch {
|
|
4045
4101
|
}
|
|
@@ -5128,7 +5184,7 @@ function registerAbleTimeTools(server) {
|
|
|
5128
5184
|
);
|
|
5129
5185
|
server.tool(
|
|
5130
5186
|
"abletime_ensure_task",
|
|
5131
|
-
"Find or create an AbleTime task for this work, then
|
|
5187
|
+
"Find or create an AbleTime task for this work, then create_workspace with sourceType=ticket.",
|
|
5132
5188
|
{
|
|
5133
5189
|
title: z4.string(),
|
|
5134
5190
|
description: z4.string().optional(),
|
|
@@ -5452,11 +5508,11 @@ async function resolveProjectRepoPath(repoPath) {
|
|
|
5452
5508
|
}
|
|
5453
5509
|
return matchRegisteredWorkspace(root) ?? matchRegisteredWorkspace(hint);
|
|
5454
5510
|
}
|
|
5455
|
-
var PROJECT_PATH_HINT = "Pass repoPath from
|
|
5511
|
+
var PROJECT_PATH_HINT = "Pass repoPath from list_projects. Orchestration cwd is synthetic and empty \u2014 do not omit repoPath for project context.";
|
|
5456
5512
|
function registerViewerContextTools(server) {
|
|
5457
5513
|
server.tool(
|
|
5458
5514
|
"get_viewer_context",
|
|
5459
|
-
"Read account and project user context (Settings \u2192 Agents / Projects): roles, tickets, and review queues as freeform text. Orchestration must pass repoPath from
|
|
5515
|
+
"Read account and project user context (Settings \u2192 Agents / Projects): roles, tickets, and review queues as freeform text. Orchestration must pass repoPath from list_projects for project context. Worktree turns may omit repoPath (uses cwd).",
|
|
5460
5516
|
{
|
|
5461
5517
|
repoPath: z7.string().optional().describe(
|
|
5462
5518
|
"Registered workspace or worktree path. Required from orchestration for a project. Omit on a worktree turn (uses cwd)."
|
|
@@ -5482,7 +5538,7 @@ function registerViewerContextTools(server) {
|
|
|
5482
5538
|
context: z7.string().describe("Full replacement text (roles, tickets, review queues). Empty clears."),
|
|
5483
5539
|
confirmed: z7.boolean().optional().describe("Must be true after the user confirmed via ask_user. False/omit is rejected."),
|
|
5484
5540
|
repoPath: z7.string().optional().describe(
|
|
5485
|
-
"Required for scope=project from orchestration (path from
|
|
5541
|
+
"Required for scope=project from orchestration (path from list_projects). Worktree turns may omit it (cwd)."
|
|
5486
5542
|
)
|
|
5487
5543
|
},
|
|
5488
5544
|
async ({ scope, context, confirmed, repoPath }) => {
|
|
@@ -5596,7 +5652,7 @@ function registerScheduleTools(server) {
|
|
|
5596
5652
|
);
|
|
5597
5653
|
server.tool(
|
|
5598
5654
|
"create_schedule",
|
|
5599
|
-
"Create a local schedule that, when due, sends a prompt to an existing chat (
|
|
5655
|
+
"Create a local schedule that, when due, sends a prompt to an existing chat (chatId) or starts a new Global orchestration chat (omit chatId). Pass chatId=self to continue this chat. If that chat is missing or archived when the job fires, Sideboard starts a new Global chat (recurring jobs then continue that chat). Exactly one of at (ISO datetime), every (15m/1h/6h/1d), or cron (5-field). Recurring jobs without chatId open a new Global chat each run. Creating or enabling a future job turns on Settings \u2192 Advanced \u2192 Caffeinate while schedules are enabled so the Mac stays awake until due. Sideboard.app must be running for the job to fire.",
|
|
5600
5656
|
{
|
|
5601
5657
|
prompt: z8.string().describe("User message / goal queued when the schedule fires"),
|
|
5602
5658
|
name: z8.string().optional(),
|
|
@@ -5604,20 +5660,22 @@ function registerScheduleTools(server) {
|
|
|
5604
5660
|
every: z8.string().optional().describe("Interval such as 15m, 1h, 6h, 1d"),
|
|
5605
5661
|
cron: z8.string().optional().describe("5-field cron expression"),
|
|
5606
5662
|
tz: z8.string().optional().describe("IANA timezone for cron (default: system)"),
|
|
5607
|
-
|
|
5608
|
-
'Existing chat id (orchestration or
|
|
5663
|
+
chatId: z8.string().optional().describe(
|
|
5664
|
+
'Existing chat id (orchestration or workspace), or "self" for this chat. Omit to create a new Global chat on fire.'
|
|
5609
5665
|
),
|
|
5666
|
+
threadId: z8.string().optional().describe("Legacy alias for chatId."),
|
|
5610
5667
|
agent: z8.enum(["claude", "cursor", "codex", "opencode"]).optional().describe("Agent for a new Global chat (omit for Account default)"),
|
|
5611
5668
|
model: z8.string().optional()
|
|
5612
5669
|
},
|
|
5613
5670
|
async (args) => {
|
|
5614
5671
|
try {
|
|
5615
5672
|
const when = parseWhen(args);
|
|
5616
|
-
const
|
|
5617
|
-
|
|
5673
|
+
const rawChatId = args.chatId ?? args.threadId;
|
|
5674
|
+
const threadId = resolveScheduleThreadId(rawChatId);
|
|
5675
|
+
if (rawChatId?.trim().toLowerCase() === "self" && !threadId) {
|
|
5618
5676
|
return fail5(
|
|
5619
5677
|
new Error(
|
|
5620
|
-
"
|
|
5678
|
+
"chatId=self needs this chat id (SIDEBOARD_THREAD_ID or SIDEBOARD_ORCHESTRATOR_THREAD_ID)"
|
|
5621
5679
|
)
|
|
5622
5680
|
);
|
|
5623
5681
|
}
|
|
@@ -5632,7 +5690,7 @@ function registerScheduleTools(server) {
|
|
|
5632
5690
|
});
|
|
5633
5691
|
return text6({
|
|
5634
5692
|
schedule,
|
|
5635
|
-
hint: scheduleNeedsCaffeinate(schedule) ? "Fires while Sideboard.app is running. Recurring jobs without
|
|
5693
|
+
hint: scheduleNeedsCaffeinate(schedule) ? "Fires while Sideboard.app is running. Recurring jobs without chatId create a new orchestration chat each run. Caffeinate while schedules are enabled is now on so the Mac can stay awake until this job is due." : "Fires while Sideboard.app is running. Recurring jobs without chatId create a new orchestration chat each run."
|
|
5636
5694
|
});
|
|
5637
5695
|
} catch (err) {
|
|
5638
5696
|
return fail5(err);
|
|
@@ -5641,7 +5699,7 @@ function registerScheduleTools(server) {
|
|
|
5641
5699
|
);
|
|
5642
5700
|
server.tool(
|
|
5643
5701
|
"update_schedule",
|
|
5644
|
-
"Update a local schedule (prompt, cadence, target
|
|
5702
|
+
"Update a local schedule (prompt, cadence, target chat, enabled). Pass id from list_schedules.",
|
|
5645
5703
|
{
|
|
5646
5704
|
id: z8.string(),
|
|
5647
5705
|
prompt: z8.string().optional(),
|
|
@@ -5650,9 +5708,10 @@ function registerScheduleTools(server) {
|
|
|
5650
5708
|
every: z8.string().optional(),
|
|
5651
5709
|
cron: z8.string().optional(),
|
|
5652
5710
|
tz: z8.string().optional(),
|
|
5653
|
-
|
|
5654
|
-
'Existing chat (orchestration or
|
|
5711
|
+
chatId: z8.string().optional().describe(
|
|
5712
|
+
'Existing chat (orchestration or workspace), "self", or empty string to create a new Global chat each run'
|
|
5655
5713
|
),
|
|
5714
|
+
threadId: z8.string().optional().describe("Legacy alias for chatId."),
|
|
5656
5715
|
agent: z8.enum(["claude", "cursor", "codex", "opencode"]).optional(),
|
|
5657
5716
|
model: z8.string().optional(),
|
|
5658
5717
|
enabled: z8.boolean().optional()
|
|
@@ -5667,12 +5726,13 @@ function registerScheduleTools(server) {
|
|
|
5667
5726
|
tz: args.tz
|
|
5668
5727
|
}) : void 0;
|
|
5669
5728
|
let threadId;
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5729
|
+
const rawChatId = args.chatId ?? args.threadId;
|
|
5730
|
+
if (rawChatId !== void 0) {
|
|
5731
|
+
threadId = resolveScheduleThreadId(rawChatId);
|
|
5732
|
+
if (rawChatId.trim().toLowerCase() === "self" && !threadId) {
|
|
5673
5733
|
return fail5(
|
|
5674
5734
|
new Error(
|
|
5675
|
-
"
|
|
5735
|
+
"chatId=self needs this chat id (SIDEBOARD_THREAD_ID or SIDEBOARD_ORCHESTRATOR_THREAD_ID)"
|
|
5676
5736
|
)
|
|
5677
5737
|
);
|
|
5678
5738
|
}
|
|
@@ -5723,12 +5783,163 @@ function registerScheduleTools(server) {
|
|
|
5723
5783
|
);
|
|
5724
5784
|
}
|
|
5725
5785
|
|
|
5786
|
+
// src/agent-rpc/handlers.ts
|
|
5787
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
5788
|
+
import { isAbsolute } from "path";
|
|
5789
|
+
function presentArtifactResult(input) {
|
|
5790
|
+
const id = input.artifact_id?.trim() || `artifact_${Date.now().toString(36)}_${randomUUID2().slice(0, 8)}`;
|
|
5791
|
+
return {
|
|
5792
|
+
ok: true,
|
|
5793
|
+
artifact_id: id,
|
|
5794
|
+
title: input.title,
|
|
5795
|
+
type: input.type,
|
|
5796
|
+
status: input.status,
|
|
5797
|
+
phase: input.phase,
|
|
5798
|
+
mode: input.type === "log" ? input.mode ?? "append" : void 0,
|
|
5799
|
+
message: input.type === "log" ? "Log accepted. Same artifact_id appends; send only new lines next time." : "Artifact accepted. Sideboard desktop opens it in the side column beside chat."
|
|
5800
|
+
};
|
|
5801
|
+
}
|
|
5802
|
+
function asRecord2(value) {
|
|
5803
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
5804
|
+
return value;
|
|
5805
|
+
}
|
|
5806
|
+
function str(value) {
|
|
5807
|
+
return typeof value === "string" ? value : "";
|
|
5808
|
+
}
|
|
5809
|
+
function invalidParams(message) {
|
|
5810
|
+
return Object.assign(new Error(message), { rpcCode: -32602 });
|
|
5811
|
+
}
|
|
5812
|
+
function jobCwd(rec) {
|
|
5813
|
+
const raw = str(rec.cwd).trim();
|
|
5814
|
+
if (!raw) throw invalidParams("cwd is required");
|
|
5815
|
+
if (!isAbsolute(raw)) throw invalidParams("cwd must be an absolute path");
|
|
5816
|
+
return raw;
|
|
5817
|
+
}
|
|
5818
|
+
async function handleRuntimePing(_params, _ctx) {
|
|
5819
|
+
return {
|
|
5820
|
+
ok: true,
|
|
5821
|
+
protocolVersion: AGENT_RPC_PROTOCOL_VERSION,
|
|
5822
|
+
pid: process.pid
|
|
5823
|
+
};
|
|
5824
|
+
}
|
|
5825
|
+
async function handlePresentArtifact(params, _ctx) {
|
|
5826
|
+
const rec = asRecord2(params);
|
|
5827
|
+
const title = str(rec.title).trim();
|
|
5828
|
+
const type = str(rec.type).trim();
|
|
5829
|
+
const allowed = ["html", "svg", "markdown", "react", "log"];
|
|
5830
|
+
if (!title) throw invalidParams("title is required");
|
|
5831
|
+
if (!allowed.includes(type)) {
|
|
5832
|
+
throw invalidParams("type must be html, svg, markdown, react, or log");
|
|
5833
|
+
}
|
|
5834
|
+
const statusRaw = str(rec.status).trim();
|
|
5835
|
+
const status = ["running", "ok", "failed", "idle"].includes(
|
|
5836
|
+
statusRaw
|
|
5837
|
+
) ? statusRaw : void 0;
|
|
5838
|
+
const modeRaw = str(rec.mode).trim();
|
|
5839
|
+
const mode = modeRaw === "append" || modeRaw === "replace" ? modeRaw : void 0;
|
|
5840
|
+
return presentArtifactResult({
|
|
5841
|
+
title,
|
|
5842
|
+
type,
|
|
5843
|
+
artifact_id: str(rec.artifact_id) || void 0,
|
|
5844
|
+
status,
|
|
5845
|
+
phase: str(rec.phase) || void 0,
|
|
5846
|
+
mode
|
|
5847
|
+
});
|
|
5848
|
+
}
|
|
5849
|
+
async function handleJobWait(params, ctx, waitFn = waitForDetachedJob) {
|
|
5850
|
+
const rec = asRecord2(params);
|
|
5851
|
+
const id = str(rec.id).trim();
|
|
5852
|
+
if (!id) throw invalidParams("id is required");
|
|
5853
|
+
const cwd = jobCwd(rec);
|
|
5854
|
+
const deadline = Date.now() + AGENT_RPC_JOB_HOLD_MAX_MS;
|
|
5855
|
+
let last;
|
|
5856
|
+
while (Date.now() < deadline) {
|
|
5857
|
+
const slice = Math.min(AGENT_RPC_JOB_SLICE_MS, Math.max(50, deadline - Date.now()));
|
|
5858
|
+
last = await waitFn(cwd, id, { timeoutMs: slice });
|
|
5859
|
+
if (!last.stillRunning) return last;
|
|
5860
|
+
ctx.notify("runtime.progress", last);
|
|
5861
|
+
}
|
|
5862
|
+
return last ?? {
|
|
5863
|
+
stillRunning: true,
|
|
5864
|
+
ok: false,
|
|
5865
|
+
failed: false,
|
|
5866
|
+
status: "running",
|
|
5867
|
+
id,
|
|
5868
|
+
delta: "",
|
|
5869
|
+
progress: "Job still running after max hold. Call wait_for_job again.",
|
|
5870
|
+
hint: "The RPC hold cap was reached. Call wait_for_job again \u2014 do not assume success."
|
|
5871
|
+
};
|
|
5872
|
+
}
|
|
5873
|
+
async function handleJobStop(params, _ctx) {
|
|
5874
|
+
const rec = asRecord2(params);
|
|
5875
|
+
const id = str(rec.id).trim();
|
|
5876
|
+
if (!id) throw invalidParams("id is required");
|
|
5877
|
+
const reason = str(rec.reason).trim() || void 0;
|
|
5878
|
+
return stopDetachedJob(jobCwd(rec), id, reason ? { reason } : void 0);
|
|
5879
|
+
}
|
|
5880
|
+
async function defaultTurnWait(ref, timeoutMs) {
|
|
5881
|
+
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-5YU46YNS.js");
|
|
5882
|
+
const orch = getOrchestrator2();
|
|
5883
|
+
const thread = await orch.waitForTurn(ref, timeoutMs, {
|
|
5884
|
+
resolveIfStillRunning: true
|
|
5885
|
+
});
|
|
5886
|
+
const result = orch.getTurnResult(thread.id);
|
|
5887
|
+
return waitForTurnToolResult({
|
|
5888
|
+
id: thread.id,
|
|
5889
|
+
status: result.status,
|
|
5890
|
+
taskState: result.taskState,
|
|
5891
|
+
text: result.text,
|
|
5892
|
+
lastError: result.lastError,
|
|
5893
|
+
stillRunning: result.stillRunning,
|
|
5894
|
+
progress: result.progress,
|
|
5895
|
+
lastActivityAt: result.lastActivityAt,
|
|
5896
|
+
usage: result.usage
|
|
5897
|
+
});
|
|
5898
|
+
}
|
|
5899
|
+
async function handleTurnWait(params, ctx, waitFn = defaultTurnWait) {
|
|
5900
|
+
const rec = asRecord2(params);
|
|
5901
|
+
const ref = str(rec.ref).trim();
|
|
5902
|
+
if (!ref) throw invalidParams("ref is required");
|
|
5903
|
+
const requested = typeof rec.timeoutMs === "number" && Number.isFinite(rec.timeoutMs) ? rec.timeoutMs : void 0;
|
|
5904
|
+
const holdMax = requested !== void 0 ? Math.min(Math.max(1e3, Math.floor(requested)), AGENT_RPC_JOB_HOLD_MAX_MS) : AGENT_RPC_JOB_HOLD_MAX_MS;
|
|
5905
|
+
const deadline = Date.now() + holdMax;
|
|
5906
|
+
let last;
|
|
5907
|
+
while (Date.now() < deadline) {
|
|
5908
|
+
const slice = Math.min(AGENT_RPC_JOB_SLICE_MS, Math.max(50, deadline - Date.now()));
|
|
5909
|
+
last = await waitFn(ref, slice);
|
|
5910
|
+
if (!last.stillRunning) return last;
|
|
5911
|
+
ctx.notify("runtime.progress", last);
|
|
5912
|
+
}
|
|
5913
|
+
return last ?? {
|
|
5914
|
+
id: ref,
|
|
5915
|
+
status: "running",
|
|
5916
|
+
taskState: "working",
|
|
5917
|
+
text: "",
|
|
5918
|
+
lastError: null,
|
|
5919
|
+
stillRunning: true,
|
|
5920
|
+
progress: null,
|
|
5921
|
+
lastActivityAt: null,
|
|
5922
|
+
usage: null,
|
|
5923
|
+
hint: "Child is still working after max hold. Call wait_for_turn again. Do not send_to_chat a check-in (that steers / interrupts).",
|
|
5924
|
+
incomplete: false
|
|
5925
|
+
};
|
|
5926
|
+
}
|
|
5927
|
+
function createAgentRpcDispatcher() {
|
|
5928
|
+
return {
|
|
5929
|
+
"runtime.ping": handleRuntimePing,
|
|
5930
|
+
"ui.presentArtifact": handlePresentArtifact,
|
|
5931
|
+
"job.wait": handleJobWait,
|
|
5932
|
+
"job.stop": handleJobStop,
|
|
5933
|
+
"turn.wait": handleTurnWait
|
|
5934
|
+
};
|
|
5935
|
+
}
|
|
5936
|
+
|
|
5726
5937
|
// src/board/load-home-board.ts
|
|
5727
5938
|
import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync5, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
5728
5939
|
import { join as join6 } from "path";
|
|
5729
5940
|
|
|
5730
5941
|
// src/board/board-pins.ts
|
|
5731
|
-
import { randomUUID as
|
|
5942
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
5732
5943
|
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync4, unlinkSync as unlinkSync2, writeFileSync } from "fs";
|
|
5733
5944
|
import { join as join5 } from "path";
|
|
5734
5945
|
var FILE = "home-board-pins.json";
|
|
@@ -5758,7 +5969,7 @@ function addBoardPin(input) {
|
|
|
5758
5969
|
const ref = input.ref.trim();
|
|
5759
5970
|
if (!ref) throw new Error("Board item needs a ref");
|
|
5760
5971
|
const pin = {
|
|
5761
|
-
id:
|
|
5972
|
+
id: randomUUID3(),
|
|
5762
5973
|
kind: input.kind,
|
|
5763
5974
|
ref,
|
|
5764
5975
|
repoPath: input.repoPath.trim(),
|
|
@@ -6067,7 +6278,7 @@ function withTimeout(promise, ms, label) {
|
|
|
6067
6278
|
}
|
|
6068
6279
|
async function createOrchChildThread(orch, args, resolveNewThreadOptions2) {
|
|
6069
6280
|
const envParentId = process.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID?.trim() || "";
|
|
6070
|
-
let parentId = args.parentThreadId?.trim() || "";
|
|
6281
|
+
let parentId = args.parentChatId?.trim() || args.parentThreadId?.trim() || "";
|
|
6071
6282
|
let parent = parentId ? orch.getThread(parentId) : null;
|
|
6072
6283
|
let parentCorrectedFrom;
|
|
6073
6284
|
if (envParentId) {
|
|
@@ -6117,7 +6328,7 @@ async function createOrchChildThread(orch, args, resolveNewThreadOptions2) {
|
|
|
6117
6328
|
attachments: args.attachments
|
|
6118
6329
|
}),
|
|
6119
6330
|
CREATE_THREAD_TIMEOUT_MS,
|
|
6120
|
-
"
|
|
6331
|
+
"create_workspace"
|
|
6121
6332
|
);
|
|
6122
6333
|
const alreadyStarted = priorIds.has(thread.id);
|
|
6123
6334
|
const parentNote = parentCorrectedFrom ? parentId ? `Ignored unknown/stale parentThreadId ${parentCorrectedFrom}; nested under ${parentId}` : `Ignored unknown/stale parentThreadId ${parentCorrectedFrom}; created without parent` : void 0;
|
|
@@ -6132,7 +6343,8 @@ async function createOrchChildThread(orch, args, resolveNewThreadOptions2) {
|
|
|
6132
6343
|
model: thread.model,
|
|
6133
6344
|
status: thread.status,
|
|
6134
6345
|
cowboy: Boolean(thread.cowboy),
|
|
6135
|
-
link: `sideboard://
|
|
6346
|
+
link: `sideboard://chat/${thread.id}`,
|
|
6347
|
+
parentChatId: thread.parentThreadId,
|
|
6136
6348
|
parentThreadId: thread.parentThreadId,
|
|
6137
6349
|
...alreadyStarted ? { alreadyStarted: true } : {},
|
|
6138
6350
|
...opts.coercedFrom ? { agentCoercedFrom: opts.coercedFrom } : {},
|
|
@@ -6143,7 +6355,7 @@ async function createOrchChildThread(orch, args, resolveNewThreadOptions2) {
|
|
|
6143
6355
|
};
|
|
6144
6356
|
} catch (err) {
|
|
6145
6357
|
const message = err instanceof Error ? err.message : String(err);
|
|
6146
|
-
return { ok: false, text: `
|
|
6358
|
+
return { ok: false, text: `create_workspace failed: ${message}` };
|
|
6147
6359
|
}
|
|
6148
6360
|
}
|
|
6149
6361
|
function previewHint(preview) {
|
|
@@ -6160,7 +6372,7 @@ async function startMcpServer() {
|
|
|
6160
6372
|
);
|
|
6161
6373
|
}
|
|
6162
6374
|
try {
|
|
6163
|
-
const { maxConcurrentAgents: maxConcurrentAgents2 } = await import("./app-settings-
|
|
6375
|
+
const { maxConcurrentAgents: maxConcurrentAgents2 } = await import("./app-settings-RKFFUIFQ.js");
|
|
6164
6376
|
orch.setMaxConcurrent(maxConcurrentAgents2());
|
|
6165
6377
|
} catch {
|
|
6166
6378
|
}
|
|
@@ -6193,8 +6405,8 @@ async function startMcpServer() {
|
|
|
6193
6405
|
}
|
|
6194
6406
|
if (!worktreeProfile) {
|
|
6195
6407
|
server.tool(
|
|
6196
|
-
"
|
|
6197
|
-
"List registered
|
|
6408
|
+
"list_projects",
|
|
6409
|
+
"List registered projects (git repos). Each line is name, path, github:owner/repo when resolvable, and project context when set \u2014 use path as repoPath for list_board/list_branches/list_prs/list_issues/create_workspace. Account / project context (Settings \u2192 Agents / Projects) says which tickets and review PRs belong to the user.",
|
|
6198
6410
|
{},
|
|
6199
6411
|
async () => {
|
|
6200
6412
|
const workspaces = orch.listWorkspaces();
|
|
@@ -6213,14 +6425,14 @@ async function startMcpServer() {
|
|
|
6213
6425
|
);
|
|
6214
6426
|
return {
|
|
6215
6427
|
content: [
|
|
6216
|
-
{ type: "text", text: lines.join("\n") || "(no
|
|
6428
|
+
{ type: "text", text: lines.join("\n") || "(no projects)" }
|
|
6217
6429
|
]
|
|
6218
6430
|
};
|
|
6219
6431
|
}
|
|
6220
6432
|
);
|
|
6221
6433
|
server.tool(
|
|
6222
|
-
"
|
|
6223
|
-
"List
|
|
6434
|
+
"list_chats",
|
|
6435
|
+
"List chats across all projects (one summary line each \u2014 token-frugal). Includes parent id, last message preview, and live progress so you can see workspace children. Each line ends with sideboard://chat/<id> \u2014 use that URL in markdown links so the UI can open the chat.",
|
|
6224
6436
|
{},
|
|
6225
6437
|
async () => {
|
|
6226
6438
|
const threads = orch.getThreads(true);
|
|
@@ -6232,19 +6444,19 @@ async function startMcpServer() {
|
|
|
6232
6444
|
const previewBit = preview ? ` ${preview}` : "";
|
|
6233
6445
|
const err = t.lastError ? ` error:${t.lastError.replace(/\s+/g, " ").slice(0, 60)}` : "";
|
|
6234
6446
|
const progress = live?.summary && !isInternalAgentStatusText(live.summary) ? ` ${live.summary}` : "";
|
|
6235
|
-
return `${t.id.slice(0, 8)} ${t.status.padEnd(9)} ${t.agent.padEnd(8)} ${repo} ${t.sourceType}:${t.sourceRef} ${t.title}${parent}${previewBit}${err} sideboard://
|
|
6447
|
+
return `${t.id.slice(0, 8)} ${t.status.padEnd(9)} ${t.agent.padEnd(8)} ${repo} ${t.sourceType}:${t.sourceRef} ${t.title}${parent}${previewBit}${err} sideboard://chat/${t.id}${t.devPort ? ` http://localhost:${t.devPort}` : ""}${progress}`;
|
|
6236
6448
|
});
|
|
6237
6449
|
return {
|
|
6238
|
-
content: [{ type: "text", text: lines.join("\n") || "(no
|
|
6450
|
+
content: [{ type: "text", text: lines.join("\n") || "(no chats)" }]
|
|
6239
6451
|
};
|
|
6240
6452
|
}
|
|
6241
6453
|
);
|
|
6242
6454
|
server.tool(
|
|
6243
6455
|
"list_board",
|
|
6244
|
-
"Home Kanban of worktrees (New, Draft, Review, Merged) \u2014 one card per checkout; sibling chat tabs nest as inner cards. Same cards as desktop Home. Path to merge: no PR \u2192 draft PR \u2192 open PR \u2192 merged. Archive removes the card to Settings \u2192 History. Queued/running are activity on the card, not columns. Orchestration chats are not on the board. Filters: query, repoPath, kind (ticket/PR/branch source), ownership (mine = your PRs / WIP; reviewing = someone else's PR), column, limit (default 40).
|
|
6456
|
+
"Home Kanban of worktrees (New, Draft, Review, Merged) \u2014 one card per checkout; sibling chat tabs nest as inner cards. Same cards as desktop Home. Path to merge: no PR \u2192 draft PR \u2192 open PR \u2192 merged. Archive removes the card to Settings \u2192 History. Queued/running are activity on the card, not columns. Orchestration chats are not on the board. Filters: query, repoPath, kind (ticket/PR/branch source), ownership (mine = your PRs / WIP; reviewing = someone else's PR), column, limit (default 40). create_workspace adds a workspace (and a Home card), or returns the live one if that ticket/PR/named branch is already checked out.",
|
|
6245
6457
|
{
|
|
6246
6458
|
query: z9.string().optional().describe("Case-insensitive token search across title, id, labels, repo"),
|
|
6247
|
-
repoPath: z9.string().optional().describe("Limit to one
|
|
6459
|
+
repoPath: z9.string().optional().describe("Limit to one project path from list_projects"),
|
|
6248
6460
|
kind: z9.enum(["all", "tickets", "prs", "branches", "threads"]).optional().describe("Filter by worktree source (default all)"),
|
|
6249
6461
|
ownership: z9.enum(["all", "mine", "reviewing"]).optional().describe(
|
|
6250
6462
|
"Mine = PRs you authored or local WIP; reviewing = someone else's PR. Uses the gh login."
|
|
@@ -6280,13 +6492,13 @@ async function startMcpServer() {
|
|
|
6280
6492
|
}
|
|
6281
6493
|
);
|
|
6282
6494
|
server.tool(
|
|
6283
|
-
"
|
|
6284
|
-
"Get a compact
|
|
6495
|
+
"get_chat",
|
|
6496
|
+
"Get a compact chat summary by id/ref. Includes last message preview, parentChatId, and child workspace chats (status + lastText). While running, includes progress (last tool/thinking) and lastActivityAt. Includes usage (billed token + costUsd totals when providers reported cost) and lastTurnUsage.",
|
|
6285
6497
|
{ ref: z9.string() },
|
|
6286
6498
|
async ({ ref }) => {
|
|
6287
6499
|
const t = orch.getThread(ref);
|
|
6288
6500
|
if (!t) {
|
|
6289
|
-
return { content: [{ type: "text", text: `
|
|
6501
|
+
return { content: [{ type: "text", text: `Chat not found: ${ref}` }], isError: true };
|
|
6290
6502
|
}
|
|
6291
6503
|
const stillRunning = orch.threadLooksLive(t);
|
|
6292
6504
|
const live = stillRunning ? readTurnLive(t.id) : null;
|
|
@@ -6302,6 +6514,7 @@ async function startMcpServer() {
|
|
|
6302
6514
|
branchName: t.branchName,
|
|
6303
6515
|
worktreePath: t.worktreePath,
|
|
6304
6516
|
sessionId: t.sessionId,
|
|
6517
|
+
parentChatId: t.parentThreadId,
|
|
6305
6518
|
parentThreadId: t.parentThreadId,
|
|
6306
6519
|
children: childThreadRefs(t.id, orch.getThreads(false)),
|
|
6307
6520
|
queueLength: t.queue.length,
|
|
@@ -6320,37 +6533,31 @@ async function startMcpServer() {
|
|
|
6320
6533
|
}
|
|
6321
6534
|
);
|
|
6322
6535
|
}
|
|
6323
|
-
|
|
6324
|
-
|
|
6325
|
-
|
|
6326
|
-
|
|
6327
|
-
|
|
6328
|
-
|
|
6329
|
-
|
|
6330
|
-
|
|
6331
|
-
|
|
6332
|
-
|
|
6333
|
-
|
|
6334
|
-
|
|
6335
|
-
|
|
6336
|
-
|
|
6337
|
-
|
|
6338
|
-
|
|
6339
|
-
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
|
|
6345
|
-
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
mode: type === "log" ? mode ?? "append" : void 0,
|
|
6349
|
-
message: type === "log" ? "Log accepted. Same artifact_id appends; send only new lines next time." : "Artifact accepted. Sideboard desktop opens it in the side column beside chat."
|
|
6350
|
-
};
|
|
6351
|
-
return mcpJson(payload);
|
|
6352
|
-
}
|
|
6353
|
-
);
|
|
6536
|
+
const mcpCatalog = new Set(worktreeMcpToolNames());
|
|
6537
|
+
if (mcpCatalog.has("present_artifact")) {
|
|
6538
|
+
server.tool(
|
|
6539
|
+
"present_artifact",
|
|
6540
|
+
"Show a document or live log in Sideboard\u2019s side column. For html/svg/markdown/react, pass the FULL document and do not also fence that same body in chat. For type=log, pass only NEW lines (same artifact_id appends). Prefer type=log for long-running job output \u2014 do not resend HTML. type=react is a single default-export component (JSX/TSX); only react/react-dom imports.",
|
|
6541
|
+
{
|
|
6542
|
+
title: z9.string().describe("Short title shown in the artifact pane header"),
|
|
6543
|
+
type: z9.enum(["html", "svg", "markdown", "react", "log"]).describe(
|
|
6544
|
+
"html/svg/markdown/react replace the pane. log appends content to the same artifact_id (new lines only)."
|
|
6545
|
+
),
|
|
6546
|
+
content: z9.string().describe(
|
|
6547
|
+
"html/svg/markdown/react: full document. log: only the new lines since the last call (empty is ok for a status-only update)."
|
|
6548
|
+
),
|
|
6549
|
+
artifact_id: z9.string().optional().describe("Stable id. Required for type=log so later calls append to the same pane."),
|
|
6550
|
+
status: z9.enum(["running", "ok", "failed", "idle"]).optional().describe("Log header pill: running (working), ok (done), failed, idle"),
|
|
6551
|
+
phase: z9.string().optional().describe("Log subtitle (Signing, Notarizing, \u2026)"),
|
|
6552
|
+
mode: z9.enum(["append", "replace"]).optional().describe("log only: append (default) or replace the buffer")
|
|
6553
|
+
},
|
|
6554
|
+
async ({ title, type, artifact_id, status, phase, mode }) => {
|
|
6555
|
+
return mcpJson(
|
|
6556
|
+
presentArtifactResult({ title, type, artifact_id, status, phase, mode })
|
|
6557
|
+
);
|
|
6558
|
+
}
|
|
6559
|
+
);
|
|
6560
|
+
}
|
|
6354
6561
|
server.tool(
|
|
6355
6562
|
"ask_user",
|
|
6356
6563
|
"Ask the user a clarifying multiple-choice question in Sideboard\u2019s composer. Call only when work is blocked on choosing among a few concrete options (approach fork, which API, auth vs cookies). Do not call for greetings, check-ins, \u201Chello\u201D, open-ended how-can-I-help, or to invent a menu of possible next tasks \u2014 reply in chat instead. If one option is the obvious default, proceed without asking. Before calling, write a short chat message explaining the decision and what each option means. Include a description on every option. After calling, stop and wait for answers. Not for \u201Cis the plan ready?\u201D.",
|
|
@@ -6467,32 +6674,34 @@ async function startMcpServer() {
|
|
|
6467
6674
|
return mcpJson(payload);
|
|
6468
6675
|
}
|
|
6469
6676
|
);
|
|
6470
|
-
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
|
|
6477
|
-
|
|
6478
|
-
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
|
|
6482
|
-
|
|
6483
|
-
|
|
6484
|
-
|
|
6485
|
-
|
|
6486
|
-
|
|
6487
|
-
|
|
6488
|
-
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
|
|
6492
|
-
|
|
6493
|
-
|
|
6494
|
-
|
|
6495
|
-
|
|
6677
|
+
if (mcpCatalog.has("wait_for_job")) {
|
|
6678
|
+
server.tool(
|
|
6679
|
+
"wait_for_job",
|
|
6680
|
+
"Wait on a detached long job (tests, pack, deploy, connector CLI) started with detached-job.cjs. MCP clients kill tools around 60s, so this returns within 45s. Sideboard opens a type=log pane from this result (artifact_id = job id, content = delta). stillRunning is the source of truth \u2014 if true, call wait_for_job again. If the job is hanging, producing no useful output, or doing the wrong thing, call stop_job instead of looping forever. Do not end the turn or tell the user you will let them know later. If false, ok/failed is the result.",
|
|
6681
|
+
{
|
|
6682
|
+
id: z9.string().describe("Detached job id (same kebab-case id passed to detached-job.cjs start)"),
|
|
6683
|
+
timeoutMs: z9.number().optional()
|
|
6684
|
+
},
|
|
6685
|
+
async ({ id, timeoutMs }) => {
|
|
6686
|
+
const result = await waitForDetachedJob(process.cwd(), id, {
|
|
6687
|
+
timeoutMs: mcpWaitForJobTimeoutMs(timeoutMs)
|
|
6688
|
+
});
|
|
6689
|
+
return mcpJson(result);
|
|
6690
|
+
}
|
|
6691
|
+
);
|
|
6692
|
+
server.tool(
|
|
6693
|
+
"stop_job",
|
|
6694
|
+
"Stop a detached job you started with detached-job.cjs when it is hanging, producing no useful output, buffering forever, or doing the wrong thing (wrong project, infinite watch, huge dump). Do not stop a pack/test/deploy that is clearly making progress. The type=log pane updates to status=failed with the last delta. Then decide the next step.",
|
|
6695
|
+
{
|
|
6696
|
+
id: z9.string().describe("Detached job id (same kebab-case id passed to detached-job.cjs start)"),
|
|
6697
|
+
reason: z9.string().optional().describe("Why you are stopping (hanging, no progress, wrong output, already have the answer)")
|
|
6698
|
+
},
|
|
6699
|
+
async ({ id, reason }) => {
|
|
6700
|
+
const result = await stopDetachedJob(process.cwd(), id, { reason });
|
|
6701
|
+
return mcpJson(result);
|
|
6702
|
+
}
|
|
6703
|
+
);
|
|
6704
|
+
}
|
|
6496
6705
|
server.tool(
|
|
6497
6706
|
"list_run_scripts",
|
|
6498
6707
|
"List .sideboard/.conductor run scripts for this thread (same menu as the desktop Dev button) and which are active. Each script and active run includes preview: url (open http://localhost:<SIDEBOARD_PORT>) or window (Electron: native window is the app; url is renderer/HMR only). Never guess :3000. Worktree turns may omit ref (uses cwd).",
|
|
@@ -6652,7 +6861,7 @@ async function startMcpServer() {
|
|
|
6652
6861
|
registerSlackTools(server);
|
|
6653
6862
|
registerConnectedIssueVendorTools(server);
|
|
6654
6863
|
const { getCaffeinateHold: getCaffeinateHold2, setCaffeinateHold: setCaffeinateHold2 } = await import("./caffeinate-hold-DYJGWK6Y.js");
|
|
6655
|
-
const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults: resolveThreadDefaults2 } = await import("./app-settings-
|
|
6864
|
+
const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults: resolveThreadDefaults2 } = await import("./app-settings-RKFFUIFQ.js");
|
|
6656
6865
|
const accountDefaults = resolveThreadDefaults2();
|
|
6657
6866
|
const accountDefaultsHint = `Account defaults: agent=${accountDefaults.agent}, model=${accountDefaults.model?.trim() || "Auto"}, effort=${accountDefaults.effort}`;
|
|
6658
6867
|
server.tool(
|
|
@@ -6682,8 +6891,8 @@ async function startMcpServer() {
|
|
|
6682
6891
|
}
|
|
6683
6892
|
);
|
|
6684
6893
|
server.tool(
|
|
6685
|
-
"
|
|
6686
|
-
`Create a worktree
|
|
6894
|
+
"create_workspace",
|
|
6895
|
+
`Create a workspace (isolated git worktree + first chat) from branch, pr, or ticket. A ticket, PR, or named branch may have only one live worktree \u2014 if one already matches, returns it (alreadyStarted=true) instead of a second checkout. Creating from the default branch still opens a new isolated worktree. Pass repoPath from list_projects. cowboy=true uses the project folder on the default branch (no isolated worktree; land is commit+push). From an orchestration chat, omit parentChatId (Sideboard binds the child to this chat) or pass the exact id from the turn reminder \u2014 never invent a uuid. Omit agent/model \u2014 Sideboard applies ${accountDefaultsHint}. Do not pass your own agent or agent=cursor; those are ignored. Setup (settings.toml, .cursor/worktrees.json, or script/setup) runs in the background in parallel with the first turn (skipped for cowboy). Then use send_to_chat to chat.`,
|
|
6687
6896
|
{
|
|
6688
6897
|
sourceType: z9.enum(["branch", "pr", "ticket"]),
|
|
6689
6898
|
sourceRef: z9.string(),
|
|
@@ -6698,9 +6907,10 @@ async function startMcpServer() {
|
|
|
6698
6907
|
cowboy: z9.boolean().optional().describe(
|
|
6699
6908
|
"If true, work in the project folder on the default branch (no thread/* worktree). Requires Settings \u2192 Advanced \u2192 Cowboy mode. Land is commit+push to that branch. Archive does not delete the folder."
|
|
6700
6909
|
),
|
|
6701
|
-
|
|
6910
|
+
parentChatId: z9.string().optional().describe(
|
|
6702
6911
|
"Orchestration: omit (preferred) or pass YOUR chat id from the turn reminder / AGENTS.md. Do not invent uuids."
|
|
6703
|
-
)
|
|
6912
|
+
),
|
|
6913
|
+
parentThreadId: z9.string().optional().describe("Legacy alias for parentChatId.")
|
|
6704
6914
|
},
|
|
6705
6915
|
async (args) => {
|
|
6706
6916
|
const result = await createOrchChildThread(orch, args, resolveNewThreadOptions2);
|
|
@@ -6712,11 +6922,11 @@ async function startMcpServer() {
|
|
|
6712
6922
|
);
|
|
6713
6923
|
server.tool(
|
|
6714
6924
|
"start_board_card",
|
|
6715
|
-
"Same as
|
|
6925
|
+
"Same as create_workspace for a ticket, PR, or named branch (attaches issue text when Sideboard can resolve it). Does not create a second worktree when one already matches \u2014 returns that thread (alreadyStarted). Then send_to_chat.",
|
|
6716
6926
|
{
|
|
6717
6927
|
kind: z9.enum(["ticket", "pr", "branch"]),
|
|
6718
6928
|
ref: z9.string().describe("Ticket identifier (ENG-12), PR number (44), or branch name from list_board"),
|
|
6719
|
-
repoPath: z9.string().describe("
|
|
6929
|
+
repoPath: z9.string().describe("Project path from list_projects / list_board"),
|
|
6720
6930
|
title: z9.string().optional()
|
|
6721
6931
|
},
|
|
6722
6932
|
async ({ kind, ref, repoPath, title }) => {
|
|
@@ -6739,7 +6949,7 @@ async function startMcpServer() {
|
|
|
6739
6949
|
id: existing.id,
|
|
6740
6950
|
title: existing.title,
|
|
6741
6951
|
status: existing.status,
|
|
6742
|
-
link: `sideboard://
|
|
6952
|
+
link: `sideboard://chat/${existing.id}`
|
|
6743
6953
|
});
|
|
6744
6954
|
}
|
|
6745
6955
|
const workspaces = orch.listWorkspaces();
|
|
@@ -6767,7 +6977,7 @@ async function startMcpServer() {
|
|
|
6767
6977
|
const ident = issue?.identifier ?? ref.trim();
|
|
6768
6978
|
const attachments = issue ? [
|
|
6769
6979
|
{
|
|
6770
|
-
id:
|
|
6980
|
+
id: randomUUID4(),
|
|
6771
6981
|
name: issue.identifier,
|
|
6772
6982
|
kind: "issue",
|
|
6773
6983
|
content: [
|
|
@@ -6811,8 +7021,8 @@ async function startMcpServer() {
|
|
|
6811
7021
|
}
|
|
6812
7022
|
);
|
|
6813
7023
|
server.tool(
|
|
6814
|
-
"
|
|
6815
|
-
'Steer a prompt on a
|
|
7024
|
+
"send_to_chat",
|
|
7025
|
+
'Steer a prompt on a workspace chat (Settings \u2192 Follow-up, default steer: front of the queue, skips the inbox; while the desktop board is running it starts as soon as the in-flight turn yields). Use after create_workspace to start or continue a conversation. For commit/push/PR, prefer ask_git (canonical desktop-button phrases). Send "Merge PR." / ask_git merge only when the user explicitly asked to merge. force_stop=true kills the in-flight turn and clears the queue before this prompt \u2014 only when the current request is wrong and must be replaced. Do not send_to_chat to check in, resume after a halt notice, or because wait_for_turn returned stillRunning; that interrupts the child mid-thought. Call wait_for_turn again instead.',
|
|
6816
7026
|
{
|
|
6817
7027
|
ref: z9.string(),
|
|
6818
7028
|
prompt: z9.string(),
|
|
@@ -6836,36 +7046,38 @@ async function startMcpServer() {
|
|
|
6836
7046
|
});
|
|
6837
7047
|
}
|
|
6838
7048
|
);
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
7049
|
+
if (shouldRegisterMcpWaitForTurn()) {
|
|
7050
|
+
server.tool(
|
|
7051
|
+
"wait_for_turn",
|
|
7052
|
+
"Wait until the chat finishes its current/queued turn, or return early with a live progress snapshot. MCP clients often kill tools around 60s, so this returns within 45s even while the child is still working. taskState is the A2A-style lifecycle: submitted (queued, not started), working, input-required (ask_user), completed, failed, canceled. stillRunning is true only for submitted/working. If stillRunning, text and usage are empty (they would be the previous turn) \u2014 read progress and call wait_for_turn again. Do not send_to_chat a check-in (that steers / interrupts). On failed, lastError/text is the failure. On canceled, the child did not finish \u2014 resume with send_to_chat or tell the user. On input-required, wait for the user in that chat. When finished, text is this turn\u2019s assistant reply and usage is that turn\u2019s tokens + costUsd (this turn, not the Claude session total; sessionCostUsd is the provider session total when present).",
|
|
7053
|
+
{
|
|
7054
|
+
ref: z9.string(),
|
|
7055
|
+
timeoutMs: z9.number().optional()
|
|
7056
|
+
},
|
|
7057
|
+
async ({ ref, timeoutMs }) => {
|
|
7058
|
+
const thread = await orch.waitForTurn(ref, mcpWaitForTurnTimeoutMs(timeoutMs), {
|
|
7059
|
+
resolveIfStillRunning: true
|
|
7060
|
+
});
|
|
7061
|
+
const result = orch.getTurnResult(thread.id);
|
|
7062
|
+
return mcpJson(
|
|
7063
|
+
waitForTurnToolResult({
|
|
7064
|
+
id: thread.id,
|
|
7065
|
+
status: result.status,
|
|
7066
|
+
taskState: result.taskState,
|
|
7067
|
+
text: result.text,
|
|
7068
|
+
lastError: result.lastError,
|
|
7069
|
+
stillRunning: result.stillRunning,
|
|
7070
|
+
progress: result.progress,
|
|
7071
|
+
lastActivityAt: result.lastActivityAt,
|
|
7072
|
+
usage: result.usage
|
|
7073
|
+
})
|
|
7074
|
+
);
|
|
7075
|
+
}
|
|
7076
|
+
);
|
|
7077
|
+
}
|
|
6866
7078
|
server.tool(
|
|
6867
7079
|
"get_turn_result",
|
|
6868
|
-
"Assistant message when the turn finished,
|
|
7080
|
+
"Assistant message and last-turn usage when the turn finished. While stillRunning (submitted/working), text and usage are empty so a previous reply cannot be mistaken for this turn \u2014 use progress for tools/thinking. Not the full transcript. Includes taskState (A2A lifecycle).",
|
|
6869
7081
|
{ ref: z9.string() },
|
|
6870
7082
|
async ({ ref }) => {
|
|
6871
7083
|
const result = orch.getTurnResult(ref);
|
|
@@ -6878,7 +7090,7 @@ async function startMcpServer() {
|
|
|
6878
7090
|
}
|
|
6879
7091
|
);
|
|
6880
7092
|
server.tool(
|
|
6881
|
-
"
|
|
7093
|
+
"stop_chat",
|
|
6882
7094
|
"Force-stop a thread: kill any in-flight agent turn AND clear queued prompts so drainQueue cannot continue. Does not archive the worktree. Optional force defaults to true.",
|
|
6883
7095
|
{
|
|
6884
7096
|
ref: z9.string(),
|
|
@@ -6888,7 +7100,7 @@ async function startMcpServer() {
|
|
|
6888
7100
|
const t = orch.getThread(ref);
|
|
6889
7101
|
if (!t) {
|
|
6890
7102
|
return {
|
|
6891
|
-
content: [{ type: "text", text: `
|
|
7103
|
+
content: [{ type: "text", text: `Chat not found: ${ref}` }],
|
|
6892
7104
|
isError: true
|
|
6893
7105
|
};
|
|
6894
7106
|
}
|
|
@@ -6903,14 +7115,14 @@ async function startMcpServer() {
|
|
|
6903
7115
|
}
|
|
6904
7116
|
);
|
|
6905
7117
|
server.tool(
|
|
6906
|
-
"
|
|
7118
|
+
"archive_chat",
|
|
6907
7119
|
"Archive a thread (stops agent/dev, runs archive script, removes worktree when last chat tab). Coordinators commit, push, and open PRs by asking the worktree agent (ask_git). Merge only when the user explicitly asked.",
|
|
6908
7120
|
{ ref: z9.string() },
|
|
6909
7121
|
async ({ ref }) => {
|
|
6910
7122
|
const t = orch.getThread(ref);
|
|
6911
7123
|
if (!t) {
|
|
6912
7124
|
return {
|
|
6913
|
-
content: [{ type: "text", text: `
|
|
7125
|
+
content: [{ type: "text", text: `Chat not found: ${ref}` }],
|
|
6914
7126
|
isError: true
|
|
6915
7127
|
};
|
|
6916
7128
|
}
|
|
@@ -6929,7 +7141,7 @@ async function startMcpServer() {
|
|
|
6929
7141
|
}
|
|
6930
7142
|
);
|
|
6931
7143
|
server.tool(
|
|
6932
|
-
"
|
|
7144
|
+
"restore_chat",
|
|
6933
7145
|
"Restore an archived thread (recreates worktree from branch when needed)",
|
|
6934
7146
|
{ ref: z9.string() },
|
|
6935
7147
|
async ({ ref }) => {
|
|
@@ -6988,7 +7200,7 @@ async function startMcpServer() {
|
|
|
6988
7200
|
title: tab.title,
|
|
6989
7201
|
status: tab.status,
|
|
6990
7202
|
fromThreadId: from?.id ?? ref,
|
|
6991
|
-
link: `sideboard://
|
|
7203
|
+
link: `sideboard://chat/${tab.id}`
|
|
6992
7204
|
});
|
|
6993
7205
|
} catch (err) {
|
|
6994
7206
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -7013,7 +7225,7 @@ async function startMcpServer() {
|
|
|
7013
7225
|
status: thread.status,
|
|
7014
7226
|
queueLength: thread.queue.length,
|
|
7015
7227
|
action,
|
|
7016
|
-
link: `sideboard://
|
|
7228
|
+
link: `sideboard://chat/${thread.id}`
|
|
7017
7229
|
});
|
|
7018
7230
|
} catch (err) {
|
|
7019
7231
|
const raw = err instanceof Error ? err.message : String(err);
|
|
@@ -7023,7 +7235,7 @@ async function startMcpServer() {
|
|
|
7023
7235
|
error: true,
|
|
7024
7236
|
action,
|
|
7025
7237
|
lastError: message,
|
|
7026
|
-
hint: /body is too long/i.test(message) ? "Branch is already pushed.
|
|
7238
|
+
hint: /body is too long/i.test(message) ? "Branch is already pushed. send_to_chat so the worktree agent runs gh pr create --draft --assignee @me -R <origin> --body-file <short.md>. Keep the description short." : void 0
|
|
7027
7239
|
},
|
|
7028
7240
|
true
|
|
7029
7241
|
);
|
|
@@ -7048,8 +7260,8 @@ async function startMcpServer() {
|
|
|
7048
7260
|
}
|
|
7049
7261
|
);
|
|
7050
7262
|
server.tool(
|
|
7051
|
-
"
|
|
7052
|
-
"Fork a worktree agent chat into a NEW git worktree + chat (desktop \u201CFork to new workspace\u201D). Seeds a transcript (through through_index, default all). Optional agent override. Leave model unset for Auto (default) \u2014 only pass model when you have a reason. Not for the orchestrator. Then
|
|
7263
|
+
"fork_workspace",
|
|
7264
|
+
"Fork a worktree agent chat into a NEW git worktree + chat (desktop \u201CFork to new workspace\u201D). Seeds a transcript (through through_index, default all). Optional agent override. Leave model unset for Auto (default) \u2014 only pass model when you have a reason. Not for the orchestrator. Then send_to_chat / wait_for_turn (loop while stillRunning) on the returned id.",
|
|
7053
7265
|
{
|
|
7054
7266
|
ref: z9.string().describe("Worktree thread id/ref to fork"),
|
|
7055
7267
|
through_index: z9.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
@@ -7077,7 +7289,7 @@ async function startMcpServer() {
|
|
|
7077
7289
|
branchName: thread.branchName,
|
|
7078
7290
|
worktreePath: thread.worktreePath,
|
|
7079
7291
|
fromThreadId: source?.id ?? ref,
|
|
7080
|
-
link: `sideboard://
|
|
7292
|
+
link: `sideboard://chat/${thread.id}`
|
|
7081
7293
|
});
|
|
7082
7294
|
} catch (err) {
|
|
7083
7295
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -7087,7 +7299,7 @@ async function startMcpServer() {
|
|
|
7087
7299
|
);
|
|
7088
7300
|
server.tool(
|
|
7089
7301
|
"fork_chat",
|
|
7090
|
-
"Fork a chat into a NEW tab on the SAME workspace: worktree agent \u2192 same worktree tab; Global orchestration chat \u2192 new orchestration chat (same synthetic home). Seeds a transcript; optional agent override. Leave model unset for Auto unless you have a reason. Orchestration forks require an MCP-capable agent (claude, cursor, codex, opencode \u2014 not brightsy). Slack / Global orchestrators use this to continue an orchestration chat on another agent after session limits. Then
|
|
7302
|
+
"Fork a chat into a NEW tab on the SAME workspace: worktree agent \u2192 same worktree tab; Global orchestration chat \u2192 new orchestration chat (same synthetic home). Seeds a transcript; optional agent override. Leave model unset for Auto unless you have a reason. Orchestration forks require an MCP-capable agent (claude, cursor, codex, opencode \u2014 not brightsy). Slack / Global orchestrators use this to continue an orchestration chat on another agent after session limits. Then send_to_chat / wait_for_turn (loop while stillRunning) on the returned id. Use fork_workspace only for worktree agents that need a new git worktree.",
|
|
7091
7303
|
{
|
|
7092
7304
|
ref: z9.string().describe("Thread id/ref to fork (worktree agent or orchestration chat)"),
|
|
7093
7305
|
through_index: z9.number().optional().describe("Inclusive message index to include in the transcript (default: all)"),
|
|
@@ -7100,7 +7312,7 @@ async function startMcpServer() {
|
|
|
7100
7312
|
const source = orch.getThread(ref);
|
|
7101
7313
|
if (!source) {
|
|
7102
7314
|
return {
|
|
7103
|
-
content: [{ type: "text", text: `
|
|
7315
|
+
content: [{ type: "text", text: `Chat not found: ${ref}` }],
|
|
7104
7316
|
isError: true
|
|
7105
7317
|
};
|
|
7106
7318
|
}
|
|
@@ -7120,7 +7332,7 @@ async function startMcpServer() {
|
|
|
7120
7332
|
sourceType: tab.sourceType,
|
|
7121
7333
|
worktreePath: tab.worktreePath,
|
|
7122
7334
|
fromThreadId: source.id,
|
|
7123
|
-
link: `sideboard://
|
|
7335
|
+
link: `sideboard://chat/${tab.id}`
|
|
7124
7336
|
});
|
|
7125
7337
|
} catch (err) {
|
|
7126
7338
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -7138,7 +7350,7 @@ async function startMcpServer() {
|
|
|
7138
7350
|
}
|
|
7139
7351
|
);
|
|
7140
7352
|
server.tool(
|
|
7141
|
-
"
|
|
7353
|
+
"add_project",
|
|
7142
7354
|
"Register a git repo as a Sideboard workspace",
|
|
7143
7355
|
{ repoPath: z9.string() },
|
|
7144
7356
|
async ({ repoPath }) => {
|
|
@@ -7147,7 +7359,7 @@ async function startMcpServer() {
|
|
|
7147
7359
|
}
|
|
7148
7360
|
);
|
|
7149
7361
|
server.tool(
|
|
7150
|
-
"
|
|
7362
|
+
"remove_project",
|
|
7151
7363
|
"Unregister a Sideboard workspace (does not archive threads)",
|
|
7152
7364
|
{ repoPath: z9.string() },
|
|
7153
7365
|
async ({ repoPath }) => {
|
|
@@ -7180,7 +7392,7 @@ async function startMcpServer() {
|
|
|
7180
7392
|
);
|
|
7181
7393
|
server.tool(
|
|
7182
7394
|
"list_branches",
|
|
7183
|
-
"List git branches in a registered workspace. Pass repoPath from
|
|
7395
|
+
"List git branches in a registered workspace. Pass repoPath from list_projects (unmerged into the default branch by default \u2014 for create_workspace sourceType=branch).",
|
|
7184
7396
|
{
|
|
7185
7397
|
repoPath: z9.string(),
|
|
7186
7398
|
unmergedOnly: z9.boolean().optional()
|
|
@@ -7202,7 +7414,7 @@ async function startMcpServer() {
|
|
|
7202
7414
|
);
|
|
7203
7415
|
server.tool(
|
|
7204
7416
|
"list_prs",
|
|
7205
|
-
`List GitHub PRs for a registered workspace (the review surface for assigned ticket work \u2014 not the tickets). Pass repoPath from
|
|
7417
|
+
`List GitHub PRs for a registered workspace (the review surface for assigned ticket work \u2014 not the tickets). Pass repoPath from list_projects. "Get me N tickets to review" \u2192 queue=review and limit=N: open non-draft PRs labeled with this project's ready-for-review GitHub label (Settings \u2192 Projects; default eng-review) with no individual user reviewer. Prefer teams that match Settings \u2192 Agents / Projects roles for this repo. A team like engineering-team is not a claim (you are on that team). Also: state (open|closed|merged|all|review), label, reviewer (me|unassigned|login), query, limit (default 40, max 250). Then create_workspace with sourceType=pr.`,
|
|
7206
7418
|
{
|
|
7207
7419
|
repoPath: z9.string(),
|
|
7208
7420
|
query: z9.string().optional().describe("GitHub search tokens (title, draft:true, \u2026)"),
|
|
@@ -7274,7 +7486,7 @@ async function startMcpServer() {
|
|
|
7274
7486
|
stackLayer: t.stackLayer,
|
|
7275
7487
|
worktreePath: t.worktreePath,
|
|
7276
7488
|
prUrl: t.prUrl,
|
|
7277
|
-
link: `sideboard://
|
|
7489
|
+
link: `sideboard://chat/${t.id}`
|
|
7278
7490
|
}))
|
|
7279
7491
|
});
|
|
7280
7492
|
}
|
|
@@ -7295,7 +7507,7 @@ async function startMcpServer() {
|
|
|
7295
7507
|
branchName: result.thread.branchName,
|
|
7296
7508
|
stackLayer: result.thread.stackLayer,
|
|
7297
7509
|
worktreePath: result.thread.worktreePath,
|
|
7298
|
-
link: `sideboard://
|
|
7510
|
+
link: `sideboard://chat/${result.thread.id}`
|
|
7299
7511
|
});
|
|
7300
7512
|
}
|
|
7301
7513
|
);
|
|
@@ -7326,14 +7538,14 @@ async function startMcpServer() {
|
|
|
7326
7538
|
branchName: t.branchName,
|
|
7327
7539
|
stackLayer: t.stackLayer,
|
|
7328
7540
|
worktreePath: t.worktreePath,
|
|
7329
|
-
link: `sideboard://
|
|
7541
|
+
link: `sideboard://chat/${t.id}`
|
|
7330
7542
|
}))
|
|
7331
7543
|
});
|
|
7332
7544
|
}
|
|
7333
7545
|
);
|
|
7334
7546
|
server.tool(
|
|
7335
7547
|
"list_issues",
|
|
7336
|
-
"List or search issues (Linear, AbleTime, or GitHub; falls back to GitHub). Use Settings \u2192 Agents / Projects notes and roles to pick tickets relevant to the viewer (query + assignee=me or unassigned as the notes say). Default 40; pass query and/or limit (max 250) when truncated. assignee: me (Linear default), unassigned, all, or a user id. Pass updatedSince for new/updated tickets and new comments (\u201Cany updates since yesterday?\u201D). Then
|
|
7548
|
+
"List or search issues (Linear, AbleTime, or GitHub; falls back to GitHub). Use Settings \u2192 Agents / Projects notes and roles to pick tickets relevant to the viewer (query + assignee=me or unassigned as the notes say). Default 40; pass query and/or limit (max 250) when truncated. assignee: me (Linear default), unassigned, all, or a user id. Pass updatedSince for new/updated tickets and new comments (\u201Cany updates since yesterday?\u201D). Then create_workspace with sourceType=ticket.",
|
|
7337
7549
|
{
|
|
7338
7550
|
repoPath: z9.string(),
|
|
7339
7551
|
query: z9.string().optional().describe("Search title, identifier, or description"),
|
|
@@ -7358,6 +7570,184 @@ async function startMcpServer() {
|
|
|
7358
7570
|
await server.connect(transport);
|
|
7359
7571
|
}
|
|
7360
7572
|
|
|
7573
|
+
// src/agent-rpc/server.ts
|
|
7574
|
+
import { randomBytes as randomBytes2, randomUUID as randomUUID5, timingSafeEqual } from "crypto";
|
|
7575
|
+
import { createServer as createServer2 } from "http";
|
|
7576
|
+
import { WebSocketServer } from "ws";
|
|
7577
|
+
function tokensEqual(a, b) {
|
|
7578
|
+
const left = Buffer.from(a);
|
|
7579
|
+
const right = Buffer.from(b);
|
|
7580
|
+
if (left.length !== right.length) return false;
|
|
7581
|
+
return timingSafeEqual(left, right);
|
|
7582
|
+
}
|
|
7583
|
+
function authTokenFromUpgrade(req) {
|
|
7584
|
+
try {
|
|
7585
|
+
const url = new URL(req.url ?? "", "http://127.0.0.1");
|
|
7586
|
+
return url.searchParams.get(AGENT_RPC_TOKEN_QUERY) ?? "";
|
|
7587
|
+
} catch {
|
|
7588
|
+
return "";
|
|
7589
|
+
}
|
|
7590
|
+
}
|
|
7591
|
+
function sendJson(ws, value) {
|
|
7592
|
+
if (ws.readyState !== ws.OPEN) return;
|
|
7593
|
+
ws.send(JSON.stringify(value));
|
|
7594
|
+
}
|
|
7595
|
+
var AgentRpcServer = class {
|
|
7596
|
+
http = null;
|
|
7597
|
+
wss = null;
|
|
7598
|
+
meta = null;
|
|
7599
|
+
handlers;
|
|
7600
|
+
cwd;
|
|
7601
|
+
constructor(opts = {}) {
|
|
7602
|
+
this.handlers = opts.handlers ?? createAgentRpcDispatcher();
|
|
7603
|
+
this.cwd = opts.cwd ?? (() => process.cwd());
|
|
7604
|
+
}
|
|
7605
|
+
get metadata() {
|
|
7606
|
+
return this.meta;
|
|
7607
|
+
}
|
|
7608
|
+
get url() {
|
|
7609
|
+
return this.meta?.transports[0]?.url ?? null;
|
|
7610
|
+
}
|
|
7611
|
+
async listen(opts = {}) {
|
|
7612
|
+
const host = opts.host ?? "127.0.0.1";
|
|
7613
|
+
const port = opts.port ?? 0;
|
|
7614
|
+
const authToken = randomBytes2(24).toString("base64url");
|
|
7615
|
+
this.meta = {
|
|
7616
|
+
runtimeId: randomUUID5(),
|
|
7617
|
+
pid: process.pid,
|
|
7618
|
+
protocolVersion: AGENT_RPC_PROTOCOL_VERSION,
|
|
7619
|
+
authToken,
|
|
7620
|
+
startedAt: Date.now(),
|
|
7621
|
+
transports: []
|
|
7622
|
+
};
|
|
7623
|
+
const http = createServer2((_req, res) => {
|
|
7624
|
+
res.statusCode = 404;
|
|
7625
|
+
res.end();
|
|
7626
|
+
});
|
|
7627
|
+
const wss = new WebSocketServer({
|
|
7628
|
+
server: http,
|
|
7629
|
+
path: AGENT_RPC_PATH,
|
|
7630
|
+
verifyClient: (info) => this.authorizeUpgrade(info.req)
|
|
7631
|
+
});
|
|
7632
|
+
wss.on("connection", (ws) => this.attach(ws));
|
|
7633
|
+
try {
|
|
7634
|
+
await new Promise((resolve, reject) => {
|
|
7635
|
+
http.once("error", reject);
|
|
7636
|
+
http.listen(port, host, () => {
|
|
7637
|
+
http.off("error", reject);
|
|
7638
|
+
resolve();
|
|
7639
|
+
});
|
|
7640
|
+
});
|
|
7641
|
+
} catch (err) {
|
|
7642
|
+
this.meta = null;
|
|
7643
|
+
throw err;
|
|
7644
|
+
}
|
|
7645
|
+
const addr = http.address();
|
|
7646
|
+
if (!addr || typeof addr === "string") {
|
|
7647
|
+
this.meta = null;
|
|
7648
|
+
http.close();
|
|
7649
|
+
throw new Error("Agent RPC failed to bind a TCP port");
|
|
7650
|
+
}
|
|
7651
|
+
this.http = http;
|
|
7652
|
+
this.wss = wss;
|
|
7653
|
+
const url = `ws://${host}:${addr.port}${AGENT_RPC_PATH}`;
|
|
7654
|
+
this.meta = {
|
|
7655
|
+
...this.meta,
|
|
7656
|
+
transports: [{ kind: "websocket", url }]
|
|
7657
|
+
};
|
|
7658
|
+
writeAgentRuntimeMetadata(this.meta);
|
|
7659
|
+
return this.meta;
|
|
7660
|
+
}
|
|
7661
|
+
authorizeUpgrade(req) {
|
|
7662
|
+
const expected = this.meta?.authToken ?? "";
|
|
7663
|
+
const got = authTokenFromUpgrade(req);
|
|
7664
|
+
return Boolean(expected && got && tokensEqual(got, expected));
|
|
7665
|
+
}
|
|
7666
|
+
attach(ws) {
|
|
7667
|
+
ws.on("message", (data) => {
|
|
7668
|
+
void this.onMessage(ws, data.toString());
|
|
7669
|
+
});
|
|
7670
|
+
ws.on("error", () => {
|
|
7671
|
+
});
|
|
7672
|
+
}
|
|
7673
|
+
async onMessage(ws, raw) {
|
|
7674
|
+
const parsed = parseRpcFrame(raw);
|
|
7675
|
+
if (parsed === void 0) {
|
|
7676
|
+
sendJson(ws, rpcError(null, RPC_PARSE_ERROR, "Parse error"));
|
|
7677
|
+
return;
|
|
7678
|
+
}
|
|
7679
|
+
if (!isJsonRpcRequest(parsed)) {
|
|
7680
|
+
sendJson(ws, rpcError(null, RPC_INVALID_REQUEST, "Invalid request"));
|
|
7681
|
+
return;
|
|
7682
|
+
}
|
|
7683
|
+
const id = parsed.id;
|
|
7684
|
+
const expected = this.meta?.authToken ?? "";
|
|
7685
|
+
const got = typeof parsed.authToken === "string" ? parsed.authToken : "";
|
|
7686
|
+
if (!expected || !got || !tokensEqual(got, expected)) {
|
|
7687
|
+
sendJson(ws, rpcError(id, RPC_UNAUTHORIZED, "Unauthorized"));
|
|
7688
|
+
return;
|
|
7689
|
+
}
|
|
7690
|
+
const handler = this.handlers[parsed.method];
|
|
7691
|
+
if (!handler) {
|
|
7692
|
+
sendJson(ws, rpcError(id, RPC_METHOD_NOT_FOUND, `Method not found: ${parsed.method}`));
|
|
7693
|
+
return;
|
|
7694
|
+
}
|
|
7695
|
+
const ctx = {
|
|
7696
|
+
cwd: this.cwd(),
|
|
7697
|
+
notify: (method, params) => sendJson(ws, rpcNotify(method, params))
|
|
7698
|
+
};
|
|
7699
|
+
try {
|
|
7700
|
+
const result = await handler(parsed.params, ctx);
|
|
7701
|
+
sendJson(ws, rpcResult(id, result));
|
|
7702
|
+
} catch (err) {
|
|
7703
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
7704
|
+
const code = err && typeof err === "object" && "rpcCode" in err && typeof err.rpcCode === "number" ? err.rpcCode : RPC_INTERNAL_ERROR;
|
|
7705
|
+
sendJson(ws, rpcError(id, code, message));
|
|
7706
|
+
}
|
|
7707
|
+
}
|
|
7708
|
+
async close() {
|
|
7709
|
+
clearAgentRuntimeMetadataIfOwned();
|
|
7710
|
+
this.meta = null;
|
|
7711
|
+
const wss = this.wss;
|
|
7712
|
+
this.wss = null;
|
|
7713
|
+
if (wss) {
|
|
7714
|
+
for (const client of wss.clients) {
|
|
7715
|
+
try {
|
|
7716
|
+
client.close();
|
|
7717
|
+
} catch {
|
|
7718
|
+
}
|
|
7719
|
+
}
|
|
7720
|
+
await new Promise((resolve) => wss.close(() => resolve()));
|
|
7721
|
+
}
|
|
7722
|
+
const http = this.http;
|
|
7723
|
+
this.http = null;
|
|
7724
|
+
if (http) {
|
|
7725
|
+
await new Promise((resolve) => http.close(() => resolve()));
|
|
7726
|
+
}
|
|
7727
|
+
}
|
|
7728
|
+
};
|
|
7729
|
+
var running = null;
|
|
7730
|
+
async function startAgentRpcServer(opts = {}) {
|
|
7731
|
+
if (running) return running;
|
|
7732
|
+
const server = new AgentRpcServer(opts);
|
|
7733
|
+
try {
|
|
7734
|
+
await server.listen({ host: opts.host, port: opts.port });
|
|
7735
|
+
} catch (err) {
|
|
7736
|
+
await server.close().catch(() => void 0);
|
|
7737
|
+
throw err;
|
|
7738
|
+
}
|
|
7739
|
+
running = server;
|
|
7740
|
+
return server;
|
|
7741
|
+
}
|
|
7742
|
+
async function stopAgentRpcServer() {
|
|
7743
|
+
const server = running;
|
|
7744
|
+
running = null;
|
|
7745
|
+
if (server) await server.close();
|
|
7746
|
+
}
|
|
7747
|
+
function getAgentRpcServer() {
|
|
7748
|
+
return running;
|
|
7749
|
+
}
|
|
7750
|
+
|
|
7361
7751
|
// src/brightsy/api.ts
|
|
7362
7752
|
var formatBrightsyFetchError = formatFetchError;
|
|
7363
7753
|
var BrightsySideboardApi = class {
|
|
@@ -7636,14 +8026,14 @@ async function runCloudConnect(opts) {
|
|
|
7636
8026
|
while (!opts.signal?.aborted) {
|
|
7637
8027
|
try {
|
|
7638
8028
|
const hadFailures = consecutivePollFailures > 0;
|
|
7639
|
-
const [pending, awaiting,
|
|
8029
|
+
const [pending, awaiting, running2] = await Promise.all([
|
|
7640
8030
|
api.getTasks("pending"),
|
|
7641
8031
|
api.getTasks("awaiting_confirmation"),
|
|
7642
8032
|
api.getTasks("running")
|
|
7643
8033
|
]);
|
|
7644
8034
|
consecutivePollFailures = 0;
|
|
7645
8035
|
if (hadFailures) log("poll recovered");
|
|
7646
|
-
const tasks = [...awaiting, ...pending, ...
|
|
8036
|
+
const tasks = [...awaiting, ...pending, ...running2];
|
|
7647
8037
|
for (const task of tasks) {
|
|
7648
8038
|
if (inFlight.has(task.id)) continue;
|
|
7649
8039
|
if (task.task_status === "running") continue;
|
|
@@ -7689,7 +8079,7 @@ async function runCloudConnect(opts) {
|
|
|
7689
8079
|
}
|
|
7690
8080
|
|
|
7691
8081
|
// src/slack/oauth.ts
|
|
7692
|
-
import { randomBytes as
|
|
8082
|
+
import { randomBytes as randomBytes3 } from "crypto";
|
|
7693
8083
|
|
|
7694
8084
|
// src/slack/baked-app.ts
|
|
7695
8085
|
var BAKED_SLACK_CLIENT_ID = "7592788819232.11813536272562";
|
|
@@ -7911,7 +8301,7 @@ async function startSlackOAuth(opts) {
|
|
|
7911
8301
|
throw new SlackOAuthCancelledError();
|
|
7912
8302
|
}
|
|
7913
8303
|
const { clientId } = slackOAuthCredentials();
|
|
7914
|
-
const state =
|
|
8304
|
+
const state = randomBytes3(16).toString("hex");
|
|
7915
8305
|
const authorizeUrl = slackOAuthAuthorizeUrl(clientId, state);
|
|
7916
8306
|
const timeoutMs = opts?.timeoutMs ?? 5 * 6e4;
|
|
7917
8307
|
const pollIntervalMs = opts?.pollIntervalMs ?? 400;
|
|
@@ -8178,7 +8568,7 @@ function connectSession(ws, opts, log) {
|
|
|
8178
8568
|
}
|
|
8179
8569
|
|
|
8180
8570
|
// src/slack/relay-protocol.ts
|
|
8181
|
-
import { randomBytes as
|
|
8571
|
+
import { randomBytes as randomBytes4 } from "crypto";
|
|
8182
8572
|
var SLACK_RELAY_PING_INTERVAL_MS = 2e4;
|
|
8183
8573
|
var SLACK_RELAY_PONG_TIMEOUT_MS = 15e3;
|
|
8184
8574
|
function slackRelaySessionKey(teamId, userId, deviceId) {
|
|
@@ -8188,7 +8578,7 @@ function slackRelayUserKey(teamId, userId) {
|
|
|
8188
8578
|
return `${teamId.trim()}:${userId.trim()}`;
|
|
8189
8579
|
}
|
|
8190
8580
|
function newSlackRelayEventId() {
|
|
8191
|
-
return
|
|
8581
|
+
return randomBytes4(12).toString("hex");
|
|
8192
8582
|
}
|
|
8193
8583
|
function parseSlackRelayClientMessage(raw) {
|
|
8194
8584
|
try {
|
|
@@ -9317,8 +9707,8 @@ var SlackRelayHub = class {
|
|
|
9317
9707
|
};
|
|
9318
9708
|
|
|
9319
9709
|
// src/slack/relay-server.ts
|
|
9320
|
-
import { createServer as
|
|
9321
|
-
import { WebSocketServer } from "ws";
|
|
9710
|
+
import { createServer as createServer3 } from "http";
|
|
9711
|
+
import { WebSocketServer as WebSocketServer2 } from "ws";
|
|
9322
9712
|
|
|
9323
9713
|
// src/slack/relay-static.ts
|
|
9324
9714
|
import { createReadStream } from "fs";
|
|
@@ -9400,7 +9790,7 @@ function sendHtml(res, status, title, body) {
|
|
|
9400
9790
|
res.writeHead(status, { "Content-Type": "text/html; charset=utf-8" });
|
|
9401
9791
|
res.end(slackOAuthHtmlPage(title, body));
|
|
9402
9792
|
}
|
|
9403
|
-
function
|
|
9793
|
+
function sendJson2(res, status, body) {
|
|
9404
9794
|
res.writeHead(status, { "Content-Type": "application/json" });
|
|
9405
9795
|
res.end(JSON.stringify(body));
|
|
9406
9796
|
}
|
|
@@ -9479,17 +9869,17 @@ async function startSlackRelayServer(opts) {
|
|
|
9479
9869
|
const state = url2.searchParams.get("state")?.trim() ?? "";
|
|
9480
9870
|
const value = pending.take(state);
|
|
9481
9871
|
if (!value) {
|
|
9482
|
-
|
|
9872
|
+
sendJson2(res, 404, { ok: false, error: "pending" });
|
|
9483
9873
|
return true;
|
|
9484
9874
|
}
|
|
9485
9875
|
if (!value.ok) {
|
|
9486
|
-
|
|
9876
|
+
sendJson2(res, 400, { ok: false, error: value.error });
|
|
9487
9877
|
return true;
|
|
9488
9878
|
}
|
|
9489
|
-
|
|
9879
|
+
sendJson2(res, 200, { ok: true, ...value.payload });
|
|
9490
9880
|
return true;
|
|
9491
9881
|
};
|
|
9492
|
-
const httpServer =
|
|
9882
|
+
const httpServer = createServer3((req, res) => {
|
|
9493
9883
|
void (async () => {
|
|
9494
9884
|
const reqUrl = req.url || "/";
|
|
9495
9885
|
if (await handleCallback(reqUrl, res)) return;
|
|
@@ -9505,7 +9895,7 @@ async function startSlackRelayServer(opts) {
|
|
|
9505
9895
|
return;
|
|
9506
9896
|
}
|
|
9507
9897
|
if (req.url === "/health") {
|
|
9508
|
-
|
|
9898
|
+
sendJson2(res, 200, {
|
|
9509
9899
|
ok: true,
|
|
9510
9900
|
service: "sideboard-slack-relay",
|
|
9511
9901
|
sessions: hub.listSessions().length,
|
|
@@ -9524,7 +9914,7 @@ async function startSlackRelayServer(opts) {
|
|
|
9524
9914
|
return;
|
|
9525
9915
|
}
|
|
9526
9916
|
if (req.url === "/") {
|
|
9527
|
-
|
|
9917
|
+
sendJson2(res, 200, {
|
|
9528
9918
|
ok: true,
|
|
9529
9919
|
service: "sideboard-slack-relay",
|
|
9530
9920
|
sessions: hub.listSessions().length,
|
|
@@ -9542,7 +9932,7 @@ async function startSlackRelayServer(opts) {
|
|
|
9542
9932
|
}
|
|
9543
9933
|
});
|
|
9544
9934
|
});
|
|
9545
|
-
const wss = new
|
|
9935
|
+
const wss = new WebSocketServer2({ server: httpServer, path: SLACK_RELAY_DESKTOP_PATH });
|
|
9546
9936
|
wss.on("connection", (ws, _req) => {
|
|
9547
9937
|
const socket = {
|
|
9548
9938
|
send: (data) => {
|
|
@@ -9646,11 +10036,14 @@ export {
|
|
|
9646
10036
|
AGENT_DONE_CUSTOM_SOUND_MAX_BYTES,
|
|
9647
10037
|
AGENT_DONE_SOUNDS,
|
|
9648
10038
|
AGENT_GIT_ACTIONS,
|
|
10039
|
+
AGENT_RPC_NATIVE_ENV,
|
|
10040
|
+
AGENT_RPC_PROTOCOL_VERSION,
|
|
9649
10041
|
AGENT_RUNNER_MAX_OLD_SPACE_MB,
|
|
9650
10042
|
ATTACHMENTS_DIR,
|
|
9651
10043
|
AbleTimeOAuthCancelledError,
|
|
9652
10044
|
BAKED_ABLETIME_OAUTH_CLIENT_ID,
|
|
9653
10045
|
BAKED_SLACK_RELAY_URL,
|
|
10046
|
+
BASE_ALLOWED_TOOLS,
|
|
9654
10047
|
BRIGHTSY_MCP_ALLOWED_TOOLS,
|
|
9655
10048
|
BRIGHTSY_OAUTH_CLIENT_ID,
|
|
9656
10049
|
BRIGHTSY_OAUTH_REDIRECT,
|
|
@@ -9659,9 +10052,12 @@ export {
|
|
|
9659
10052
|
BUNDLED_SKILL_PREFIX,
|
|
9660
10053
|
BrightsySideboardApi,
|
|
9661
10054
|
CHARS_PER_CONTEXT_TOKEN,
|
|
10055
|
+
CHAT_LINK_PREFIX,
|
|
9662
10056
|
CHAT_TEXT_SCALES,
|
|
9663
10057
|
CHAT_TEXT_SCALE_DEFAULT,
|
|
10058
|
+
CLAUDE_CHROME_ALLOWED_TOOLS,
|
|
9664
10059
|
CLAUDE_MODEL_CATALOG,
|
|
10060
|
+
CLAUDE_PRINT_BG_WAIT_CEILING_MS,
|
|
9665
10061
|
CLAUDE_SUMMARY_TIMEOUT_MS,
|
|
9666
10062
|
CLI_DIR,
|
|
9667
10063
|
CLOUD_COORDINATOR_BUSY_REPLY,
|
|
@@ -9703,6 +10099,7 @@ export {
|
|
|
9703
10099
|
LEGACY_DETACHED_JOBS_DIR,
|
|
9704
10100
|
LEGACY_PLAN_FILE_REL,
|
|
9705
10101
|
LEGACY_REVIEW_REQUEST_PATH,
|
|
10102
|
+
LEGACY_THREAD_LINK_PREFIX,
|
|
9706
10103
|
LIGHT_RECONCILE_THROTTLE_MS,
|
|
9707
10104
|
LINEAR_OAUTH_CANCELLED,
|
|
9708
10105
|
LINEAR_OAUTH_PORT,
|
|
@@ -9791,6 +10188,7 @@ export {
|
|
|
9791
10188
|
agentDoneSound,
|
|
9792
10189
|
agentDoneSoundsDir,
|
|
9793
10190
|
agentGitPrompt,
|
|
10191
|
+
agentRpcConnectFromMetadata,
|
|
9794
10192
|
alignQueueAttachments,
|
|
9795
10193
|
allAdapters,
|
|
9796
10194
|
allocatePort,
|
|
@@ -9859,6 +10257,7 @@ export {
|
|
|
9859
10257
|
canonicalizeRepoPath,
|
|
9860
10258
|
captureLoginEnv,
|
|
9861
10259
|
captureTurnBaseline,
|
|
10260
|
+
chatLinkUrl,
|
|
9862
10261
|
chatTextScale,
|
|
9863
10262
|
checkoutPrStackLayer,
|
|
9864
10263
|
childEnvWithAppSettings,
|
|
@@ -10069,6 +10468,7 @@ export {
|
|
|
10069
10468
|
getAbleTimeOrientation,
|
|
10070
10469
|
getAbleTimeTask,
|
|
10071
10470
|
getAdapter,
|
|
10471
|
+
getAgentRpcServer,
|
|
10072
10472
|
getAgentSetupInfo,
|
|
10073
10473
|
getBrightsySession,
|
|
10074
10474
|
getCaffeinateHold,
|
|
@@ -10233,6 +10633,7 @@ export {
|
|
|
10233
10633
|
linearOAuthCredentials,
|
|
10234
10634
|
linearRelationCreateInput,
|
|
10235
10635
|
linearTicketIdFromThread,
|
|
10636
|
+
linkifyChatUrls,
|
|
10236
10637
|
listAbleTimeAssignedIssues,
|
|
10237
10638
|
listAbleTimeProjects,
|
|
10238
10639
|
listAbleTimeTasks,
|
|
@@ -10345,6 +10746,7 @@ export {
|
|
|
10345
10746
|
outboundReplyFromTurn,
|
|
10346
10747
|
packagedDetachedJobPath,
|
|
10347
10748
|
parseAbleTimeOAuthCallbackUrl,
|
|
10749
|
+
parseChatLink,
|
|
10348
10750
|
parseClaudeUsagePayload,
|
|
10349
10751
|
parseCursorRunnerLine,
|
|
10350
10752
|
parseDevPreview,
|
|
@@ -10378,10 +10780,12 @@ export {
|
|
|
10378
10780
|
prepareTerminalCommand,
|
|
10379
10781
|
prependQueuedItem,
|
|
10380
10782
|
previewLand,
|
|
10783
|
+
previousCostBaseline,
|
|
10381
10784
|
primaryCheckoutFromLinkedWorktree,
|
|
10382
10785
|
promptMentionsBrightsy,
|
|
10383
10786
|
pushBranch,
|
|
10384
10787
|
readAgentDoneCustomSound,
|
|
10788
|
+
readAgentRuntimeMetadata,
|
|
10385
10789
|
readExistingReviewRequestFile,
|
|
10386
10790
|
readKeychainVaultKey,
|
|
10387
10791
|
readPlanFile,
|
|
@@ -10538,6 +10942,7 @@ export {
|
|
|
10538
10942
|
stageAbsolutePathsAsAttachments,
|
|
10539
10943
|
stageBuffersAsAttachments,
|
|
10540
10944
|
startAbleTimeOAuth,
|
|
10945
|
+
startAgentRpcServer,
|
|
10541
10946
|
startDevServer,
|
|
10542
10947
|
startLinearOAuth,
|
|
10543
10948
|
startMcpServer,
|
|
@@ -10545,6 +10950,7 @@ export {
|
|
|
10545
10950
|
startSlackOAuth,
|
|
10546
10951
|
startSlackRelayServer,
|
|
10547
10952
|
statWorktreePath,
|
|
10953
|
+
stopAgentRpcServer,
|
|
10548
10954
|
stripBrightsyNdjsonNoise,
|
|
10549
10955
|
stripNestedElectronEnv,
|
|
10550
10956
|
submitPrStack,
|
|
@@ -10585,6 +10991,7 @@ export {
|
|
|
10585
10991
|
tryReservePort,
|
|
10586
10992
|
tryReservePorts,
|
|
10587
10993
|
turnCostUsdFromCursorUsage,
|
|
10994
|
+
turnCostUsdFromProviderSnapshot,
|
|
10588
10995
|
updateAbleTimeTask,
|
|
10589
10996
|
updateAdvancedSettings,
|
|
10590
10997
|
updateAgentExecutable,
|
|
@@ -10620,6 +11027,7 @@ export {
|
|
|
10620
11027
|
withRemovedAccount,
|
|
10621
11028
|
withSelectedAccount,
|
|
10622
11029
|
withThreadLock,
|
|
11030
|
+
withTurnScopedCost,
|
|
10623
11031
|
workspaceSettingsSourceLabel,
|
|
10624
11032
|
worktreeBoardStatus,
|
|
10625
11033
|
worktreeCleanupSettings,
|