@sentry/junior 0.18.1 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app.js +577 -502
- package/package.json +1 -1
package/dist/app.js
CHANGED
|
@@ -2999,6 +2999,8 @@ function buildSystemPrompt(params) {
|
|
|
2999
2999
|
"- If a loaded skill or `loadSkill` result declares `requires_capabilities`, run `jr-rpc issue-credential <capability> [--repo <owner/repo>]` as a bash command before authenticated bash/API work for that skill.",
|
|
3000
3000
|
"- Use the minimum declared capability needed for the current operation.",
|
|
3001
3001
|
"- If `jr-rpc issue-credential` returns `oauth_started`, relay its `message` to the user and stop. The runtime will resume after authorization.",
|
|
3002
|
+
"- For disconnect + reconnect requests, run `jr-rpc delete-token <provider>` first, then `jr-rpc issue-credential` \u2014 the system handles the reconnect without auto-resuming the reconnect message.",
|
|
3003
|
+
"- Use `jr-rpc oauth-start <provider>` only when the user explicitly asks to connect a provider and there is no task to resume after authorization.",
|
|
3002
3004
|
"- GitHub capabilities need repository context, which can come from `--repo` or a configured `github.repo` default.",
|
|
3003
3005
|
"- To persist or read conversation defaults (for example `github.repo`), run `jr-rpc config get|set|unset|list ...` as a bash command.",
|
|
3004
3006
|
"- Capabilities are provider-qualified (for example `github.issues.write`).",
|
|
@@ -3539,6 +3541,52 @@ async function handleIssueCredentialCommand(args, deps) {
|
|
|
3539
3541
|
});
|
|
3540
3542
|
} catch (error) {
|
|
3541
3543
|
if (error instanceof CredentialUnavailableError && getPluginOAuthConfig(error.provider) && deps.requesterId) {
|
|
3544
|
+
const authAction = deps.providerAuthActions?.get(error.provider);
|
|
3545
|
+
if (authAction?.kind === "oauth_started") {
|
|
3546
|
+
const providerLabel = formatProviderLabel(error.provider);
|
|
3547
|
+
return commandResult({
|
|
3548
|
+
stdout: {
|
|
3549
|
+
credential_unavailable: true,
|
|
3550
|
+
oauth_started: true,
|
|
3551
|
+
provider: error.provider,
|
|
3552
|
+
private_delivery_sent: authAction.delivered,
|
|
3553
|
+
message: authAction.delivered ? `I've already sent you a private authorization link to connect your ${providerLabel} account. Finish that flow, then return to Slack.` : `I still need to connect your ${providerLabel} account, but I wasn't able to send you a private authorization link. Please send me a direct message and try again.`
|
|
3554
|
+
},
|
|
3555
|
+
exitCode: 0
|
|
3556
|
+
});
|
|
3557
|
+
}
|
|
3558
|
+
if (authAction?.kind === "token_deleted") {
|
|
3559
|
+
const reconnectResult = await startOAuthFlow(error.provider, {
|
|
3560
|
+
requesterId: deps.requesterId,
|
|
3561
|
+
channelId: deps.channelId,
|
|
3562
|
+
threadTs: deps.threadTs,
|
|
3563
|
+
activeSkillName: deps.activeSkill?.name ?? void 0
|
|
3564
|
+
// Intentionally no userMessage — reconnect flows must not auto-resume.
|
|
3565
|
+
});
|
|
3566
|
+
if (!reconnectResult.ok) {
|
|
3567
|
+
return commandResult({
|
|
3568
|
+
stderr: `${reconnectResult.error}
|
|
3569
|
+
`,
|
|
3570
|
+
exitCode: 1
|
|
3571
|
+
});
|
|
3572
|
+
}
|
|
3573
|
+
const delivered = !!reconnectResult.delivery;
|
|
3574
|
+
deps.providerAuthActions?.set(error.provider, {
|
|
3575
|
+
kind: "oauth_started",
|
|
3576
|
+
delivered
|
|
3577
|
+
});
|
|
3578
|
+
const providerLabel = formatProviderLabel(error.provider);
|
|
3579
|
+
return commandResult({
|
|
3580
|
+
stdout: {
|
|
3581
|
+
credential_unavailable: true,
|
|
3582
|
+
oauth_started: true,
|
|
3583
|
+
provider: error.provider,
|
|
3584
|
+
private_delivery_sent: delivered,
|
|
3585
|
+
message: delivered ? `I need to connect your ${providerLabel} account first. I've sent you a private authorization link.` : `I need to connect your ${providerLabel} account first, but I wasn't able to send you a private authorization link. Please send me a direct message and try your command again.`
|
|
3586
|
+
},
|
|
3587
|
+
exitCode: 0
|
|
3588
|
+
});
|
|
3589
|
+
}
|
|
3542
3590
|
const oauthResult = await startOAuthFlow(error.provider, {
|
|
3543
3591
|
requesterId: deps.requesterId,
|
|
3544
3592
|
channelId: deps.channelId,
|
|
@@ -3808,6 +3856,10 @@ async function handleOAuthStartCommand(args, deps) {
|
|
|
3808
3856
|
return commandResult({ stderr: `${result.error}
|
|
3809
3857
|
`, exitCode: 1 });
|
|
3810
3858
|
}
|
|
3859
|
+
deps.providerAuthActions?.set(provider, {
|
|
3860
|
+
kind: "oauth_started",
|
|
3861
|
+
delivered: !!result.delivery
|
|
3862
|
+
});
|
|
3811
3863
|
if (!result.delivery) {
|
|
3812
3864
|
return commandResult({
|
|
3813
3865
|
stdout: {
|
|
@@ -3854,6 +3906,7 @@ async function handleDeleteTokenCommand(args, deps) {
|
|
|
3854
3906
|
});
|
|
3855
3907
|
}
|
|
3856
3908
|
await unlinkProvider(deps.requesterId, provider, deps.userTokenStore);
|
|
3909
|
+
deps.providerAuthActions?.set(provider, { kind: "token_deleted" });
|
|
3857
3910
|
logInfo(
|
|
3858
3911
|
"jr_rpc_delete_token",
|
|
3859
3912
|
{},
|
|
@@ -6754,103 +6807,50 @@ function createToolState(hooks, context) {
|
|
|
6754
6807
|
}
|
|
6755
6808
|
};
|
|
6756
6809
|
}
|
|
6757
|
-
function wrapToolExecution(toolName, toolDef, hooks) {
|
|
6758
|
-
const maybeExecutable = toolDef;
|
|
6759
|
-
if (!maybeExecutable.execute) {
|
|
6760
|
-
return toolDef;
|
|
6761
|
-
}
|
|
6762
|
-
const originalExecute = maybeExecutable.execute.bind(toolDef);
|
|
6763
|
-
maybeExecutable.execute = async (...args) => {
|
|
6764
|
-
const input = args[0];
|
|
6765
|
-
await hooks.onToolCallStart?.(toolName, input);
|
|
6766
|
-
return originalExecute(...args);
|
|
6767
|
-
};
|
|
6768
|
-
return toolDef;
|
|
6769
|
-
}
|
|
6770
6810
|
function createTools(availableSkills, hooks = {}, context) {
|
|
6771
6811
|
const state = createToolState(hooks, context);
|
|
6772
6812
|
const tools = {
|
|
6773
|
-
loadSkill:
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
|
|
6777
|
-
|
|
6778
|
-
|
|
6779
|
-
),
|
|
6780
|
-
|
|
6781
|
-
|
|
6782
|
-
|
|
6783
|
-
|
|
6784
|
-
|
|
6785
|
-
hooks
|
|
6786
|
-
),
|
|
6787
|
-
readFile: wrapToolExecution("readFile", createReadFileTool(), hooks),
|
|
6788
|
-
writeFile: wrapToolExecution("writeFile", createWriteFileTool(), hooks),
|
|
6789
|
-
webSearch: wrapToolExecution("webSearch", createWebSearchTool(), hooks),
|
|
6790
|
-
webFetch: wrapToolExecution("webFetch", createWebFetchTool(hooks), hooks),
|
|
6791
|
-
imageGenerate: wrapToolExecution(
|
|
6792
|
-
"imageGenerate",
|
|
6793
|
-
createImageGenerateTool(hooks, hooks.toolOverrides?.imageGenerate),
|
|
6794
|
-
hooks
|
|
6795
|
-
),
|
|
6796
|
-
slackCanvasUpdate: wrapToolExecution(
|
|
6797
|
-
"slackCanvasUpdate",
|
|
6798
|
-
createSlackCanvasUpdateTool(state, context),
|
|
6799
|
-
hooks
|
|
6800
|
-
),
|
|
6801
|
-
slackListCreate: wrapToolExecution(
|
|
6802
|
-
"slackListCreate",
|
|
6803
|
-
createSlackListCreateTool(state),
|
|
6804
|
-
hooks
|
|
6805
|
-
),
|
|
6806
|
-
slackListAddItems: wrapToolExecution(
|
|
6807
|
-
"slackListAddItems",
|
|
6808
|
-
createSlackListAddItemsTool(state),
|
|
6809
|
-
hooks
|
|
6810
|
-
),
|
|
6811
|
-
slackListGetItems: wrapToolExecution(
|
|
6812
|
-
"slackListGetItems",
|
|
6813
|
-
createSlackListGetItemsTool(state),
|
|
6814
|
-
hooks
|
|
6813
|
+
loadSkill: createLoadSkillTool(context.sandbox, availableSkills, {
|
|
6814
|
+
onSkillLoaded: hooks.onSkillLoaded
|
|
6815
|
+
}),
|
|
6816
|
+
systemTime: createSystemTimeTool(),
|
|
6817
|
+
bash: createBashTool(),
|
|
6818
|
+
attachFile: createAttachFileTool(context.sandbox, hooks),
|
|
6819
|
+
readFile: createReadFileTool(),
|
|
6820
|
+
writeFile: createWriteFileTool(),
|
|
6821
|
+
webSearch: createWebSearchTool(),
|
|
6822
|
+
webFetch: createWebFetchTool(hooks),
|
|
6823
|
+
imageGenerate: createImageGenerateTool(
|
|
6824
|
+
hooks,
|
|
6825
|
+
hooks.toolOverrides?.imageGenerate
|
|
6815
6826
|
),
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
6819
|
-
|
|
6820
|
-
)
|
|
6827
|
+
slackCanvasUpdate: createSlackCanvasUpdateTool(state, context),
|
|
6828
|
+
slackListCreate: createSlackListCreateTool(state),
|
|
6829
|
+
slackListAddItems: createSlackListAddItemsTool(state),
|
|
6830
|
+
slackListGetItems: createSlackListGetItemsTool(state),
|
|
6831
|
+
slackListUpdateItem: createSlackListUpdateItemTool(state)
|
|
6821
6832
|
};
|
|
6822
6833
|
if (context.mcpToolManager && context.getActiveSkills) {
|
|
6823
|
-
tools.searchTools =
|
|
6824
|
-
|
|
6825
|
-
|
|
6826
|
-
hooks
|
|
6834
|
+
tools.searchTools = createSearchToolsTool(
|
|
6835
|
+
context.mcpToolManager,
|
|
6836
|
+
context.getActiveSkills
|
|
6827
6837
|
);
|
|
6828
6838
|
}
|
|
6829
6839
|
const { channelCapabilities } = context;
|
|
6830
6840
|
if (channelCapabilities.canCreateCanvas) {
|
|
6831
|
-
tools.slackCanvasCreate =
|
|
6832
|
-
"slackCanvasCreate",
|
|
6833
|
-
createSlackCanvasCreateTool(context, state),
|
|
6834
|
-
hooks
|
|
6835
|
-
);
|
|
6841
|
+
tools.slackCanvasCreate = createSlackCanvasCreateTool(context, state);
|
|
6836
6842
|
}
|
|
6837
6843
|
if (channelCapabilities.canPostToChannel) {
|
|
6838
|
-
tools.slackChannelPostMessage =
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
hooks
|
|
6842
|
-
);
|
|
6843
|
-
tools.slackChannelListMessages = wrapToolExecution(
|
|
6844
|
-
"slackChannelListMessages",
|
|
6845
|
-
createSlackChannelListMessagesTool(context),
|
|
6846
|
-
hooks
|
|
6844
|
+
tools.slackChannelPostMessage = createSlackChannelPostMessageTool(
|
|
6845
|
+
context,
|
|
6846
|
+
state
|
|
6847
6847
|
);
|
|
6848
|
+
tools.slackChannelListMessages = createSlackChannelListMessagesTool(context);
|
|
6848
6849
|
}
|
|
6849
6850
|
if (channelCapabilities.canAddReactions) {
|
|
6850
|
-
tools.slackMessageAddReaction =
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
hooks
|
|
6851
|
+
tools.slackMessageAddReaction = createSlackMessageAddReactionTool(
|
|
6852
|
+
context,
|
|
6853
|
+
state
|
|
6854
6854
|
);
|
|
6855
6855
|
}
|
|
6856
6856
|
return tools;
|
|
@@ -6973,6 +6973,267 @@ function extractHttpErrorDetails(error, options = {}) {
|
|
|
6973
6973
|
};
|
|
6974
6974
|
}
|
|
6975
6975
|
|
|
6976
|
+
// src/chat/runtime/status-format.ts
|
|
6977
|
+
var SLACK_STATUS_MAX_LENGTH = 50;
|
|
6978
|
+
function truncateWithEllipsis(text, maxLength) {
|
|
6979
|
+
if (text.length <= maxLength) {
|
|
6980
|
+
return text;
|
|
6981
|
+
}
|
|
6982
|
+
return `${text.slice(0, Math.max(1, maxLength - 3)).trimEnd()}...`;
|
|
6983
|
+
}
|
|
6984
|
+
function truncateStatusText(text) {
|
|
6985
|
+
const trimmed = text.trim();
|
|
6986
|
+
if (!trimmed) {
|
|
6987
|
+
return "";
|
|
6988
|
+
}
|
|
6989
|
+
return truncateWithEllipsis(trimmed, SLACK_STATUS_MAX_LENGTH);
|
|
6990
|
+
}
|
|
6991
|
+
function compactStatusPath(value) {
|
|
6992
|
+
if (typeof value !== "string") {
|
|
6993
|
+
return void 0;
|
|
6994
|
+
}
|
|
6995
|
+
const trimmed = value.trim();
|
|
6996
|
+
if (!trimmed) {
|
|
6997
|
+
return void 0;
|
|
6998
|
+
}
|
|
6999
|
+
if (trimmed.length <= 80) {
|
|
7000
|
+
return trimmed;
|
|
7001
|
+
}
|
|
7002
|
+
return `...${trimmed.slice(-77)}`;
|
|
7003
|
+
}
|
|
7004
|
+
function compactStatusText(value, maxLength = 80) {
|
|
7005
|
+
if (typeof value !== "string") {
|
|
7006
|
+
return void 0;
|
|
7007
|
+
}
|
|
7008
|
+
const trimmed = value.trim();
|
|
7009
|
+
if (!trimmed) {
|
|
7010
|
+
return void 0;
|
|
7011
|
+
}
|
|
7012
|
+
return truncateWithEllipsis(trimmed, maxLength);
|
|
7013
|
+
}
|
|
7014
|
+
function readShellToken(command, startIndex) {
|
|
7015
|
+
let index = startIndex;
|
|
7016
|
+
while (index < command.length && /\s/.test(command[index] ?? "")) {
|
|
7017
|
+
index += 1;
|
|
7018
|
+
}
|
|
7019
|
+
if (index >= command.length) {
|
|
7020
|
+
return void 0;
|
|
7021
|
+
}
|
|
7022
|
+
let token = "";
|
|
7023
|
+
let quote;
|
|
7024
|
+
while (index < command.length) {
|
|
7025
|
+
const char = command[index];
|
|
7026
|
+
if (!char) {
|
|
7027
|
+
break;
|
|
7028
|
+
}
|
|
7029
|
+
if (quote) {
|
|
7030
|
+
if (char === quote) {
|
|
7031
|
+
quote = void 0;
|
|
7032
|
+
index += 1;
|
|
7033
|
+
continue;
|
|
7034
|
+
}
|
|
7035
|
+
if (char === "\\" && quote === '"' && index + 1 < command.length) {
|
|
7036
|
+
token += command[index + 1];
|
|
7037
|
+
index += 2;
|
|
7038
|
+
continue;
|
|
7039
|
+
}
|
|
7040
|
+
token += char;
|
|
7041
|
+
index += 1;
|
|
7042
|
+
continue;
|
|
7043
|
+
}
|
|
7044
|
+
if (/\s/.test(char)) {
|
|
7045
|
+
break;
|
|
7046
|
+
}
|
|
7047
|
+
if (char === '"' || char === "'") {
|
|
7048
|
+
quote = char;
|
|
7049
|
+
index += 1;
|
|
7050
|
+
continue;
|
|
7051
|
+
}
|
|
7052
|
+
if (char === "\\" && index + 1 < command.length) {
|
|
7053
|
+
token += command[index + 1];
|
|
7054
|
+
index += 2;
|
|
7055
|
+
continue;
|
|
7056
|
+
}
|
|
7057
|
+
token += char;
|
|
7058
|
+
index += 1;
|
|
7059
|
+
}
|
|
7060
|
+
return { token, nextIndex: index };
|
|
7061
|
+
}
|
|
7062
|
+
function compactStatusCommand(value) {
|
|
7063
|
+
if (typeof value !== "string") {
|
|
7064
|
+
return void 0;
|
|
7065
|
+
}
|
|
7066
|
+
const trimmed = value.trim();
|
|
7067
|
+
if (!trimmed) {
|
|
7068
|
+
return void 0;
|
|
7069
|
+
}
|
|
7070
|
+
let index = 0;
|
|
7071
|
+
while (index < trimmed.length) {
|
|
7072
|
+
const parsed = readShellToken(trimmed, index);
|
|
7073
|
+
if (!parsed) {
|
|
7074
|
+
return void 0;
|
|
7075
|
+
}
|
|
7076
|
+
index = parsed.nextIndex;
|
|
7077
|
+
if (!parsed.token) {
|
|
7078
|
+
continue;
|
|
7079
|
+
}
|
|
7080
|
+
if (/^[A-Za-z_][A-Za-z0-9_]*=/.test(parsed.token)) {
|
|
7081
|
+
continue;
|
|
7082
|
+
}
|
|
7083
|
+
const normalized = parsed.token.replace(/[\\/]+$/g, "");
|
|
7084
|
+
if (!normalized) {
|
|
7085
|
+
return void 0;
|
|
7086
|
+
}
|
|
7087
|
+
const parts = normalized.split(/[\\/]/).filter((part) => part.length > 0);
|
|
7088
|
+
const command = parts.length > 0 ? parts[parts.length - 1] : normalized;
|
|
7089
|
+
return compactStatusText(command, 40);
|
|
7090
|
+
}
|
|
7091
|
+
return void 0;
|
|
7092
|
+
}
|
|
7093
|
+
function compactStatusFilename(value) {
|
|
7094
|
+
if (typeof value !== "string") {
|
|
7095
|
+
return void 0;
|
|
7096
|
+
}
|
|
7097
|
+
const trimmed = value.trim().replace(/[\\/]+$/g, "");
|
|
7098
|
+
if (!trimmed) {
|
|
7099
|
+
return void 0;
|
|
7100
|
+
}
|
|
7101
|
+
const parts = trimmed.split(/[\\/]/).filter((part) => part.length > 0);
|
|
7102
|
+
const filename = parts.length > 0 ? parts[parts.length - 1] : trimmed;
|
|
7103
|
+
return compactStatusText(filename, 80);
|
|
7104
|
+
}
|
|
7105
|
+
function extractStatusUrlDomain(value) {
|
|
7106
|
+
if (typeof value !== "string") {
|
|
7107
|
+
return void 0;
|
|
7108
|
+
}
|
|
7109
|
+
const trimmed = value.trim();
|
|
7110
|
+
if (!trimmed) {
|
|
7111
|
+
return void 0;
|
|
7112
|
+
}
|
|
7113
|
+
try {
|
|
7114
|
+
const parsed = new URL(trimmed);
|
|
7115
|
+
return parsed.hostname || void 0;
|
|
7116
|
+
} catch {
|
|
7117
|
+
return void 0;
|
|
7118
|
+
}
|
|
7119
|
+
}
|
|
7120
|
+
|
|
7121
|
+
// src/chat/runtime/assistant-status.ts
|
|
7122
|
+
var STATUS_PATTERNS = {
|
|
7123
|
+
thinking: {
|
|
7124
|
+
defaultContext: "\u2026",
|
|
7125
|
+
variants: ["Thinking", "Reasoning", "Considering", "Working through"]
|
|
7126
|
+
},
|
|
7127
|
+
searching: {
|
|
7128
|
+
defaultContext: "sources",
|
|
7129
|
+
variants: ["Searching", "Scanning", "Probing", "Trawling"]
|
|
7130
|
+
},
|
|
7131
|
+
reading: {
|
|
7132
|
+
defaultContext: "task",
|
|
7133
|
+
variants: ["Reading", "Inspecting", "Parsing", "Skimming"]
|
|
7134
|
+
},
|
|
7135
|
+
reviewing: {
|
|
7136
|
+
defaultContext: "results",
|
|
7137
|
+
variants: ["Reviewing", "Checking", "Inspecting", "Auditing"]
|
|
7138
|
+
},
|
|
7139
|
+
loading: {
|
|
7140
|
+
defaultContext: "task",
|
|
7141
|
+
variants: ["Loading", "Priming", "Booting", "Spinning up"]
|
|
7142
|
+
},
|
|
7143
|
+
updating: {
|
|
7144
|
+
defaultContext: "state",
|
|
7145
|
+
variants: ["Updating", "Patching", "Refreshing", "Adjusting"]
|
|
7146
|
+
},
|
|
7147
|
+
fetching: {
|
|
7148
|
+
defaultContext: "sources",
|
|
7149
|
+
variants: ["Fetching", "Pulling", "Retrieving", "Loading"]
|
|
7150
|
+
},
|
|
7151
|
+
creating: {
|
|
7152
|
+
defaultContext: "draft",
|
|
7153
|
+
variants: ["Creating", "Building", "Assembling", "Generating"]
|
|
7154
|
+
},
|
|
7155
|
+
listing: {
|
|
7156
|
+
defaultContext: "items",
|
|
7157
|
+
variants: ["Listing", "Gathering", "Collecting", "Enumerating"]
|
|
7158
|
+
},
|
|
7159
|
+
posting: {
|
|
7160
|
+
defaultContext: "reply",
|
|
7161
|
+
variants: ["Posting", "Sending", "Delivering", "Dispatching"]
|
|
7162
|
+
},
|
|
7163
|
+
adding: {
|
|
7164
|
+
defaultContext: "details",
|
|
7165
|
+
variants: ["Adding", "Applying", "Attaching", "Dropping in"]
|
|
7166
|
+
},
|
|
7167
|
+
running: {
|
|
7168
|
+
defaultContext: "tasks",
|
|
7169
|
+
variants: ["Running", "Executing", "Launching", "Processing"]
|
|
7170
|
+
}
|
|
7171
|
+
};
|
|
7172
|
+
function makeAssistantStatus(kind, context) {
|
|
7173
|
+
return { kind, ...context ? { context } : {} };
|
|
7174
|
+
}
|
|
7175
|
+
function normalizeAssistantStatusText(text) {
|
|
7176
|
+
const trimmed = text.trim();
|
|
7177
|
+
if (!trimmed) {
|
|
7178
|
+
return "";
|
|
7179
|
+
}
|
|
7180
|
+
return truncateStatusText(trimmed.replace(/(?:\.\s*)+$/, "").trim());
|
|
7181
|
+
}
|
|
7182
|
+
function buildAssistantStatusPresentation(args) {
|
|
7183
|
+
const random = args.random ?? Math.random;
|
|
7184
|
+
const pattern = STATUS_PATTERNS[args.status.kind];
|
|
7185
|
+
const context = normalizeAssistantStatusText(args.status.context ?? "") || pattern.defaultContext;
|
|
7186
|
+
const index = Math.floor(random() * pattern.variants.length);
|
|
7187
|
+
const verb = pattern.variants[index] ?? pattern.variants[0];
|
|
7188
|
+
const visible = truncateStatusText(`${verb} ${context}`);
|
|
7189
|
+
const hint = truncateStatusText(`${pattern.variants[0]} ${context}`);
|
|
7190
|
+
return {
|
|
7191
|
+
key: `${args.status.kind}:${context}`,
|
|
7192
|
+
hint,
|
|
7193
|
+
visible,
|
|
7194
|
+
suggestions: Array.from(/* @__PURE__ */ new Set([visible, hint]))
|
|
7195
|
+
};
|
|
7196
|
+
}
|
|
7197
|
+
function createSlackAdapterAssistantStatusTransport(args) {
|
|
7198
|
+
return {
|
|
7199
|
+
async setStatus(channelId, threadTs, status, suggestions) {
|
|
7200
|
+
try {
|
|
7201
|
+
await args.getSlackAdapter().setAssistantStatus(channelId, threadTs, status, suggestions);
|
|
7202
|
+
} catch (error) {
|
|
7203
|
+
logAssistantStatusFailure(status, error);
|
|
7204
|
+
}
|
|
7205
|
+
}
|
|
7206
|
+
};
|
|
7207
|
+
}
|
|
7208
|
+
function createSlackWebApiAssistantStatusTransport(args) {
|
|
7209
|
+
const getClient2 = args?.getSlackClient ?? getSlackClient;
|
|
7210
|
+
return {
|
|
7211
|
+
async setStatus(channelId, threadTs, status, suggestions) {
|
|
7212
|
+
try {
|
|
7213
|
+
await getClient2().assistant.threads.setStatus({
|
|
7214
|
+
channel_id: channelId,
|
|
7215
|
+
thread_ts: threadTs,
|
|
7216
|
+
status,
|
|
7217
|
+
...suggestions ? { loading_messages: suggestions } : {}
|
|
7218
|
+
});
|
|
7219
|
+
} catch (error) {
|
|
7220
|
+
logAssistantStatusFailure(status, error);
|
|
7221
|
+
}
|
|
7222
|
+
}
|
|
7223
|
+
};
|
|
7224
|
+
}
|
|
7225
|
+
function logAssistantStatusFailure(status, error) {
|
|
7226
|
+
logWarn(
|
|
7227
|
+
"assistant_status_update_failed",
|
|
7228
|
+
{},
|
|
7229
|
+
{
|
|
7230
|
+
"app.slack.status_text": status || "(clear)",
|
|
7231
|
+
"error.message": error instanceof Error ? error.message : String(error)
|
|
7232
|
+
},
|
|
7233
|
+
"Failed to update assistant status"
|
|
7234
|
+
);
|
|
7235
|
+
}
|
|
7236
|
+
|
|
6976
7237
|
// src/chat/sandbox/sandbox.ts
|
|
6977
7238
|
var SANDBOX_TOOL_NAMES = /* @__PURE__ */ new Set(["bash", "readFile", "writeFile"]);
|
|
6978
7239
|
var DEFAULT_MAX_OUTPUT_LENGTH = 3e4;
|
|
@@ -7465,7 +7726,7 @@ function createSandboxExecutor(options) {
|
|
|
7465
7726
|
const createSandboxFromSnapshot = async (snapshotId, sandboxCredentials, onStatus) => {
|
|
7466
7727
|
for (let attempt = 0; attempt < SNAPSHOT_BOOT_RETRY_COUNT; attempt += 1) {
|
|
7467
7728
|
try {
|
|
7468
|
-
await onStatus?.("
|
|
7729
|
+
await onStatus?.(makeAssistantStatus("loading", "sandbox"));
|
|
7469
7730
|
return await Sandbox.create({
|
|
7470
7731
|
timeout: timeoutMs,
|
|
7471
7732
|
source: {
|
|
@@ -7585,28 +7846,37 @@ function createSandboxExecutor(options) {
|
|
|
7585
7846
|
let statusCount = 0;
|
|
7586
7847
|
const sentStatuses = /* @__PURE__ */ new Set();
|
|
7587
7848
|
const emitSandboxStatus = async (status) => {
|
|
7588
|
-
|
|
7849
|
+
const statusKey = `${status.kind}:${status.context ?? ""}`;
|
|
7850
|
+
if (!emitStatus || statusCount >= 4 || sentStatuses.has(statusKey)) {
|
|
7589
7851
|
return;
|
|
7590
7852
|
}
|
|
7591
|
-
sentStatuses.add(
|
|
7853
|
+
sentStatuses.add(statusKey);
|
|
7592
7854
|
statusCount += 1;
|
|
7593
7855
|
await emitStatus(status);
|
|
7594
7856
|
};
|
|
7595
7857
|
const reportSnapshotPhase = async (phase) => {
|
|
7596
7858
|
if (phase === "resolve_start") {
|
|
7597
|
-
await emitSandboxStatus(
|
|
7859
|
+
await emitSandboxStatus(
|
|
7860
|
+
makeAssistantStatus("loading", "sandbox snapshot cache")
|
|
7861
|
+
);
|
|
7598
7862
|
return;
|
|
7599
7863
|
}
|
|
7600
7864
|
if (phase === "waiting_for_lock") {
|
|
7601
|
-
await emitSandboxStatus(
|
|
7865
|
+
await emitSandboxStatus(
|
|
7866
|
+
makeAssistantStatus("loading", "sandbox snapshot build")
|
|
7867
|
+
);
|
|
7602
7868
|
return;
|
|
7603
7869
|
}
|
|
7604
7870
|
if (phase === "building_snapshot") {
|
|
7605
|
-
await emitSandboxStatus(
|
|
7871
|
+
await emitSandboxStatus(
|
|
7872
|
+
makeAssistantStatus("creating", "sandbox snapshot")
|
|
7873
|
+
);
|
|
7606
7874
|
return;
|
|
7607
7875
|
}
|
|
7608
7876
|
if (phase === "cache_hit") {
|
|
7609
|
-
await emitSandboxStatus(
|
|
7877
|
+
await emitSandboxStatus(
|
|
7878
|
+
makeAssistantStatus("loading", "sandbox snapshot")
|
|
7879
|
+
);
|
|
7610
7880
|
}
|
|
7611
7881
|
};
|
|
7612
7882
|
let createdSandbox;
|
|
@@ -7620,7 +7890,9 @@ function createSandboxExecutor(options) {
|
|
|
7620
7890
|
"app.sandbox.runtime": runtime
|
|
7621
7891
|
},
|
|
7622
7892
|
async () => {
|
|
7623
|
-
await emitSandboxStatus(
|
|
7893
|
+
await emitSandboxStatus(
|
|
7894
|
+
makeAssistantStatus("loading", "sandbox runtime")
|
|
7895
|
+
);
|
|
7624
7896
|
const snapshot = await resolveRuntimeDependencySnapshot({
|
|
7625
7897
|
runtime,
|
|
7626
7898
|
timeoutMs,
|
|
@@ -7639,7 +7911,9 @@ function createSandboxExecutor(options) {
|
|
|
7639
7911
|
} : {}
|
|
7640
7912
|
});
|
|
7641
7913
|
if (!snapshot.snapshotId) {
|
|
7642
|
-
await emitSandboxStatus(
|
|
7914
|
+
await emitSandboxStatus(
|
|
7915
|
+
makeAssistantStatus("loading", "sandbox")
|
|
7916
|
+
);
|
|
7643
7917
|
return await Sandbox.create({
|
|
7644
7918
|
timeout: timeoutMs,
|
|
7645
7919
|
runtime,
|
|
@@ -8069,219 +8343,74 @@ function shouldEmitDevAgentTrace() {
|
|
|
8069
8343
|
return process.env.NODE_ENV === "development";
|
|
8070
8344
|
}
|
|
8071
8345
|
|
|
8072
|
-
// src/chat/runtime/status
|
|
8073
|
-
|
|
8074
|
-
|
|
8075
|
-
|
|
8076
|
-
|
|
8346
|
+
// src/chat/runtime/tool-status.ts
|
|
8347
|
+
function buildToolStatus(toolName, input) {
|
|
8348
|
+
const obj = input && typeof input === "object" ? input : void 0;
|
|
8349
|
+
const command = obj ? compactStatusCommand(obj.command) : void 0;
|
|
8350
|
+
const path6 = obj ? compactStatusPath(obj.path) : void 0;
|
|
8351
|
+
const filename = obj ? compactStatusFilename(obj.path) : void 0;
|
|
8352
|
+
const query = obj ? compactStatusText(obj.query, 70) : void 0;
|
|
8353
|
+
const domain = obj ? extractStatusUrlDomain(obj.url) : void 0;
|
|
8354
|
+
const skillName = obj ? compactStatusText(obj.skill_name ?? obj.skillName, 40) : void 0;
|
|
8355
|
+
const provider = obj ? compactStatusText(obj.provider, 20) : void 0;
|
|
8356
|
+
if (command && toolName === "bash") {
|
|
8357
|
+
return makeAssistantStatus("running", command);
|
|
8077
8358
|
}
|
|
8078
|
-
|
|
8079
|
-
|
|
8080
|
-
function truncateStatusText(text) {
|
|
8081
|
-
const trimmed = text.trim();
|
|
8082
|
-
if (!trimmed) {
|
|
8083
|
-
return "";
|
|
8084
|
-
}
|
|
8085
|
-
return truncateWithEllipsis(trimmed, SLACK_STATUS_MAX_LENGTH);
|
|
8086
|
-
}
|
|
8087
|
-
function compactStatusPath(value) {
|
|
8088
|
-
if (typeof value !== "string") {
|
|
8089
|
-
return void 0;
|
|
8090
|
-
}
|
|
8091
|
-
const trimmed = value.trim();
|
|
8092
|
-
if (!trimmed) {
|
|
8093
|
-
return void 0;
|
|
8094
|
-
}
|
|
8095
|
-
if (trimmed.length <= 80) {
|
|
8096
|
-
return trimmed;
|
|
8097
|
-
}
|
|
8098
|
-
return `...${trimmed.slice(-77)}`;
|
|
8099
|
-
}
|
|
8100
|
-
function compactStatusText(value, maxLength = 80) {
|
|
8101
|
-
if (typeof value !== "string") {
|
|
8102
|
-
return void 0;
|
|
8103
|
-
}
|
|
8104
|
-
const trimmed = value.trim();
|
|
8105
|
-
if (!trimmed) {
|
|
8106
|
-
return void 0;
|
|
8107
|
-
}
|
|
8108
|
-
return truncateWithEllipsis(trimmed, maxLength);
|
|
8109
|
-
}
|
|
8110
|
-
function readShellToken(command, startIndex) {
|
|
8111
|
-
let index = startIndex;
|
|
8112
|
-
while (index < command.length && /\s/.test(command[index] ?? "")) {
|
|
8113
|
-
index += 1;
|
|
8114
|
-
}
|
|
8115
|
-
if (index >= command.length) {
|
|
8116
|
-
return void 0;
|
|
8117
|
-
}
|
|
8118
|
-
let token = "";
|
|
8119
|
-
let quote;
|
|
8120
|
-
while (index < command.length) {
|
|
8121
|
-
const char = command[index];
|
|
8122
|
-
if (!char) {
|
|
8123
|
-
break;
|
|
8124
|
-
}
|
|
8125
|
-
if (quote) {
|
|
8126
|
-
if (char === quote) {
|
|
8127
|
-
quote = void 0;
|
|
8128
|
-
index += 1;
|
|
8129
|
-
continue;
|
|
8130
|
-
}
|
|
8131
|
-
if (char === "\\" && quote === '"' && index + 1 < command.length) {
|
|
8132
|
-
token += command[index + 1];
|
|
8133
|
-
index += 2;
|
|
8134
|
-
continue;
|
|
8135
|
-
}
|
|
8136
|
-
token += char;
|
|
8137
|
-
index += 1;
|
|
8138
|
-
continue;
|
|
8139
|
-
}
|
|
8140
|
-
if (/\s/.test(char)) {
|
|
8141
|
-
break;
|
|
8142
|
-
}
|
|
8143
|
-
if (char === '"' || char === "'") {
|
|
8144
|
-
quote = char;
|
|
8145
|
-
index += 1;
|
|
8146
|
-
continue;
|
|
8147
|
-
}
|
|
8148
|
-
if (char === "\\" && index + 1 < command.length) {
|
|
8149
|
-
token += command[index + 1];
|
|
8150
|
-
index += 2;
|
|
8151
|
-
continue;
|
|
8152
|
-
}
|
|
8153
|
-
token += char;
|
|
8154
|
-
index += 1;
|
|
8155
|
-
}
|
|
8156
|
-
return { token, nextIndex: index };
|
|
8157
|
-
}
|
|
8158
|
-
function compactStatusCommand(value) {
|
|
8159
|
-
if (typeof value !== "string") {
|
|
8160
|
-
return void 0;
|
|
8359
|
+
if (filename && toolName === "readFile") {
|
|
8360
|
+
return makeAssistantStatus("reading", filename);
|
|
8161
8361
|
}
|
|
8162
|
-
|
|
8163
|
-
|
|
8164
|
-
return void 0;
|
|
8362
|
+
if (filename && toolName === "writeFile") {
|
|
8363
|
+
return makeAssistantStatus("updating", filename);
|
|
8165
8364
|
}
|
|
8166
|
-
|
|
8167
|
-
|
|
8168
|
-
const parsed = readShellToken(trimmed, index);
|
|
8169
|
-
if (!parsed) {
|
|
8170
|
-
return void 0;
|
|
8171
|
-
}
|
|
8172
|
-
index = parsed.nextIndex;
|
|
8173
|
-
if (!parsed.token) {
|
|
8174
|
-
continue;
|
|
8175
|
-
}
|
|
8176
|
-
if (/^[A-Za-z_][A-Za-z0-9_]*=/.test(parsed.token)) {
|
|
8177
|
-
continue;
|
|
8178
|
-
}
|
|
8179
|
-
const normalized = parsed.token.replace(/[\\/]+$/g, "");
|
|
8180
|
-
if (!normalized) {
|
|
8181
|
-
return void 0;
|
|
8182
|
-
}
|
|
8183
|
-
const parts = normalized.split(/[\\/]/).filter((part) => part.length > 0);
|
|
8184
|
-
const command = parts.length > 0 ? parts[parts.length - 1] : normalized;
|
|
8185
|
-
return compactStatusText(command, 40);
|
|
8365
|
+
if (path6 && toolName === "writeFile") {
|
|
8366
|
+
return makeAssistantStatus("updating", path6);
|
|
8186
8367
|
}
|
|
8187
|
-
|
|
8188
|
-
|
|
8189
|
-
function compactStatusFilename(value) {
|
|
8190
|
-
if (typeof value !== "string") {
|
|
8191
|
-
return void 0;
|
|
8368
|
+
if (skillName && toolName === "loadSkill") {
|
|
8369
|
+
return makeAssistantStatus("loading", skillName);
|
|
8192
8370
|
}
|
|
8193
|
-
|
|
8194
|
-
|
|
8195
|
-
return void 0;
|
|
8371
|
+
if (query && toolName === "webSearch") {
|
|
8372
|
+
return makeAssistantStatus("searching", `"${query}"`);
|
|
8196
8373
|
}
|
|
8197
|
-
|
|
8198
|
-
|
|
8199
|
-
return compactStatusText(filename, 80);
|
|
8200
|
-
}
|
|
8201
|
-
function extractStatusUrlDomain(value) {
|
|
8202
|
-
if (typeof value !== "string") {
|
|
8203
|
-
return void 0;
|
|
8374
|
+
if (query && provider && toolName === "searchTools") {
|
|
8375
|
+
return makeAssistantStatus("searching", `${provider} "${query}"`);
|
|
8204
8376
|
}
|
|
8205
|
-
|
|
8206
|
-
|
|
8207
|
-
return void 0;
|
|
8377
|
+
if (query && toolName === "searchTools") {
|
|
8378
|
+
return makeAssistantStatus("searching", `"${query}"`);
|
|
8208
8379
|
}
|
|
8209
|
-
|
|
8210
|
-
|
|
8211
|
-
return parsed.hostname || void 0;
|
|
8212
|
-
} catch {
|
|
8213
|
-
return void 0;
|
|
8380
|
+
if (domain && toolName === "webFetch") {
|
|
8381
|
+
return makeAssistantStatus("fetching", domain);
|
|
8214
8382
|
}
|
|
8215
|
-
}
|
|
8216
|
-
|
|
8217
|
-
// src/chat/runtime/tool-status.ts
|
|
8218
|
-
function formatToolStatus(toolName) {
|
|
8219
8383
|
const known = {
|
|
8220
|
-
loadSkill: "
|
|
8221
|
-
systemTime: "
|
|
8222
|
-
bash: "
|
|
8223
|
-
readFile: "
|
|
8224
|
-
writeFile: "
|
|
8225
|
-
webSearch: "
|
|
8226
|
-
webFetch: "
|
|
8227
|
-
slackChannelPostMessage: "
|
|
8228
|
-
slackMessageAddReaction: "
|
|
8229
|
-
slackChannelListMessages: "
|
|
8230
|
-
slackCanvasCreate: "
|
|
8231
|
-
slackCanvasUpdate: "
|
|
8232
|
-
slackListCreate: "
|
|
8233
|
-
slackListAddItems: "
|
|
8234
|
-
slackListUpdateItem: "
|
|
8235
|
-
imageGenerate: "
|
|
8236
|
-
searchTools:
|
|
8384
|
+
loadSkill: makeAssistantStatus("loading", "skill instructions"),
|
|
8385
|
+
systemTime: makeAssistantStatus("reading", "system time"),
|
|
8386
|
+
bash: makeAssistantStatus("running", "shell"),
|
|
8387
|
+
readFile: makeAssistantStatus("reading", "file"),
|
|
8388
|
+
writeFile: makeAssistantStatus("updating", "file"),
|
|
8389
|
+
webSearch: makeAssistantStatus("searching", "sources"),
|
|
8390
|
+
webFetch: makeAssistantStatus("fetching", "pages"),
|
|
8391
|
+
slackChannelPostMessage: makeAssistantStatus("posting", "channel"),
|
|
8392
|
+
slackMessageAddReaction: makeAssistantStatus("adding", "reaction"),
|
|
8393
|
+
slackChannelListMessages: makeAssistantStatus("listing", "messages"),
|
|
8394
|
+
slackCanvasCreate: makeAssistantStatus("creating", "brief"),
|
|
8395
|
+
slackCanvasUpdate: makeAssistantStatus("updating", "brief"),
|
|
8396
|
+
slackListCreate: makeAssistantStatus("creating", "tracking list"),
|
|
8397
|
+
slackListAddItems: makeAssistantStatus("updating", "tracking list"),
|
|
8398
|
+
slackListUpdateItem: makeAssistantStatus("updating", "tracking list"),
|
|
8399
|
+
imageGenerate: makeAssistantStatus("creating", "image"),
|
|
8400
|
+
searchTools: makeAssistantStatus(
|
|
8401
|
+
"searching",
|
|
8402
|
+
provider ? `${provider} tools` : "tools"
|
|
8403
|
+
)
|
|
8237
8404
|
};
|
|
8238
8405
|
if (known[toolName]) {
|
|
8239
8406
|
return known[toolName];
|
|
8240
8407
|
}
|
|
8241
8408
|
const mcpMatch = /^mcp__([^_]+)__(.+)$/.exec(toolName);
|
|
8242
8409
|
if (mcpMatch) {
|
|
8243
|
-
return
|
|
8410
|
+
return makeAssistantStatus("running", `${mcpMatch[1]}/${mcpMatch[2]}`);
|
|
8244
8411
|
}
|
|
8245
8412
|
const readable = toolName.replaceAll("_", " ").trim();
|
|
8246
|
-
return
|
|
8247
|
-
}
|
|
8248
|
-
function formatToolStatusWithInput(toolName, input) {
|
|
8249
|
-
const obj = input && typeof input === "object" ? input : void 0;
|
|
8250
|
-
const command = obj ? compactStatusCommand(obj.command) : void 0;
|
|
8251
|
-
const path6 = obj ? compactStatusPath(obj.path) : void 0;
|
|
8252
|
-
const filename = obj ? compactStatusFilename(obj.path) : void 0;
|
|
8253
|
-
const query = obj ? compactStatusText(obj.query, 70) : void 0;
|
|
8254
|
-
const domain = obj ? extractStatusUrlDomain(obj.url) : void 0;
|
|
8255
|
-
const skillName = obj ? compactStatusText(obj.skill_name ?? obj.skillName, 40) : void 0;
|
|
8256
|
-
const provider = obj ? compactStatusText(obj.provider, 20) : void 0;
|
|
8257
|
-
if (command && toolName === "bash") {
|
|
8258
|
-
return `Running ${command}`;
|
|
8259
|
-
}
|
|
8260
|
-
if (filename && toolName === "readFile") {
|
|
8261
|
-
return `Reading file ${filename}`;
|
|
8262
|
-
}
|
|
8263
|
-
if (filename && toolName === "writeFile") {
|
|
8264
|
-
return `Updating file ${filename}`;
|
|
8265
|
-
}
|
|
8266
|
-
if (path6 && toolName === "writeFile") {
|
|
8267
|
-
return `Updating file ${path6}`;
|
|
8268
|
-
}
|
|
8269
|
-
if (skillName && toolName === "loadSkill") {
|
|
8270
|
-
return `Loading skill ${skillName}`;
|
|
8271
|
-
}
|
|
8272
|
-
if (query && toolName === "webSearch") {
|
|
8273
|
-
return `Searching web for "${query}"`;
|
|
8274
|
-
}
|
|
8275
|
-
if (query && provider && toolName === "searchTools") {
|
|
8276
|
-
return `Searching ${provider} tools for "${query}"`;
|
|
8277
|
-
}
|
|
8278
|
-
if (query && toolName === "searchTools") {
|
|
8279
|
-
return `Searching tools for "${query}"`;
|
|
8280
|
-
}
|
|
8281
|
-
if (domain && toolName === "webFetch") {
|
|
8282
|
-
return `Fetching page from ${domain}`;
|
|
8283
|
-
}
|
|
8284
|
-
return formatToolStatus(toolName);
|
|
8413
|
+
return makeAssistantStatus("running", readable || "tool");
|
|
8285
8414
|
}
|
|
8286
8415
|
|
|
8287
8416
|
// src/chat/tools/execution/build-sandbox-input.ts
|
|
@@ -8432,7 +8561,7 @@ function createAgentTools(tools, sandbox, spanContext, onStatus, sandboxExecutor
|
|
|
8432
8561
|
turnId: spanContext.turnId,
|
|
8433
8562
|
agentId: spanContext.agentId
|
|
8434
8563
|
};
|
|
8435
|
-
await onStatus?.(
|
|
8564
|
+
await onStatus?.(buildToolStatus(toolName, params));
|
|
8436
8565
|
return withSpan(
|
|
8437
8566
|
`execute_tool ${toolName}`,
|
|
8438
8567
|
"gen_ai.execute_tool",
|
|
@@ -9098,6 +9227,7 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
9098
9227
|
requesterId: context.requester?.userId,
|
|
9099
9228
|
resolveConfiguration: async (key) => configurationValues[key]
|
|
9100
9229
|
});
|
|
9230
|
+
const providerAuthActions = /* @__PURE__ */ new Map();
|
|
9101
9231
|
const sandboxExecutor = createSandboxExecutor({
|
|
9102
9232
|
sandboxId: context.sandbox?.sandboxId,
|
|
9103
9233
|
sandboxDependencyProfileHash: context.sandbox?.sandboxDependencyProfileHash,
|
|
@@ -9113,6 +9243,7 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
9113
9243
|
threadTs: context.correlation?.threadTs,
|
|
9114
9244
|
userMessage: userInput,
|
|
9115
9245
|
userTokenStore: createUserTokenStore(),
|
|
9246
|
+
providerAuthActions,
|
|
9116
9247
|
onConfigurationValueChanged: (key, value) => {
|
|
9117
9248
|
if (value === void 0) {
|
|
9118
9249
|
delete configurationValues[key];
|
|
@@ -9201,11 +9332,6 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
9201
9332
|
onArtifactStatePatch: (patch) => {
|
|
9202
9333
|
Object.assign(artifactStatePatch, patch);
|
|
9203
9334
|
},
|
|
9204
|
-
onToolCallStart: async (toolName, input) => {
|
|
9205
|
-
await context.onStatus?.(
|
|
9206
|
-
`${formatToolStatusWithInput(toolName, input)}...`
|
|
9207
|
-
);
|
|
9208
|
-
},
|
|
9209
9335
|
toolOverrides: context.toolOverrides,
|
|
9210
9336
|
onSkillLoaded: async (loadedSkill) => {
|
|
9211
9337
|
const resolvedSkill = await skillSandbox.loadSkill(loadedSkill.name);
|
|
@@ -9559,6 +9685,152 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
9559
9685
|
}
|
|
9560
9686
|
}
|
|
9561
9687
|
|
|
9688
|
+
// src/chat/runtime/progress-reporter.ts
|
|
9689
|
+
var STATUS_UPDATE_DEBOUNCE_MS = 1e3;
|
|
9690
|
+
var STATUS_MIN_VISIBLE_MS = 1200;
|
|
9691
|
+
var STATUS_ROTATION_INTERVAL_MS = 3e4;
|
|
9692
|
+
function createProgressReporter(args) {
|
|
9693
|
+
const now = args.now ?? (() => Date.now());
|
|
9694
|
+
const setTimer = args.setTimer ?? ((callback, delayMs) => setTimeout(callback, delayMs));
|
|
9695
|
+
const clearTimer = args.clearTimer ?? ((timer) => clearTimeout(timer));
|
|
9696
|
+
const random = args.random ?? Math.random;
|
|
9697
|
+
let active = false;
|
|
9698
|
+
let currentKey = "";
|
|
9699
|
+
let currentStatus = makeAssistantStatus("thinking");
|
|
9700
|
+
let currentVisibleStatus = "";
|
|
9701
|
+
let lastStatusAt = 0;
|
|
9702
|
+
let pendingStatus = null;
|
|
9703
|
+
let pendingKey = "";
|
|
9704
|
+
let pendingTimer = null;
|
|
9705
|
+
let rotationTimer = null;
|
|
9706
|
+
let inflightStatusUpdate = Promise.resolve();
|
|
9707
|
+
const scheduleRotation = () => {
|
|
9708
|
+
if (rotationTimer) {
|
|
9709
|
+
clearTimer(rotationTimer);
|
|
9710
|
+
rotationTimer = null;
|
|
9711
|
+
}
|
|
9712
|
+
if (!active || !currentVisibleStatus) {
|
|
9713
|
+
return;
|
|
9714
|
+
}
|
|
9715
|
+
rotationTimer = setTimer(() => {
|
|
9716
|
+
rotationTimer = null;
|
|
9717
|
+
if (!active || !currentVisibleStatus) {
|
|
9718
|
+
return;
|
|
9719
|
+
}
|
|
9720
|
+
void postRenderedStatus(currentStatus);
|
|
9721
|
+
}, STATUS_ROTATION_INTERVAL_MS);
|
|
9722
|
+
};
|
|
9723
|
+
const postStatus = async (text, suggestions) => {
|
|
9724
|
+
const channelId = args.channelId;
|
|
9725
|
+
const threadTs = args.threadTs;
|
|
9726
|
+
if (!channelId || !threadTs) {
|
|
9727
|
+
return;
|
|
9728
|
+
}
|
|
9729
|
+
if (!text && !currentVisibleStatus) {
|
|
9730
|
+
return;
|
|
9731
|
+
}
|
|
9732
|
+
currentVisibleStatus = text;
|
|
9733
|
+
lastStatusAt = now();
|
|
9734
|
+
scheduleRotation();
|
|
9735
|
+
const previous = inflightStatusUpdate;
|
|
9736
|
+
const request = (async () => {
|
|
9737
|
+
await previous;
|
|
9738
|
+
await args.transport.setStatus(channelId, threadTs, text, suggestions);
|
|
9739
|
+
})();
|
|
9740
|
+
inflightStatusUpdate = request;
|
|
9741
|
+
await request;
|
|
9742
|
+
};
|
|
9743
|
+
const postRenderedStatus = async (status) => {
|
|
9744
|
+
const presentation = buildAssistantStatusPresentation({
|
|
9745
|
+
status,
|
|
9746
|
+
random
|
|
9747
|
+
});
|
|
9748
|
+
currentStatus = status;
|
|
9749
|
+
currentKey = presentation.key;
|
|
9750
|
+
await postStatus(presentation.visible, presentation.suggestions);
|
|
9751
|
+
};
|
|
9752
|
+
const clearPending = () => {
|
|
9753
|
+
if (pendingTimer) {
|
|
9754
|
+
clearTimer(pendingTimer);
|
|
9755
|
+
pendingTimer = null;
|
|
9756
|
+
}
|
|
9757
|
+
pendingStatus = null;
|
|
9758
|
+
pendingKey = "";
|
|
9759
|
+
};
|
|
9760
|
+
const flushPending = async () => {
|
|
9761
|
+
if (!active || !pendingStatus) {
|
|
9762
|
+
clearPending();
|
|
9763
|
+
return;
|
|
9764
|
+
}
|
|
9765
|
+
const next = pendingStatus;
|
|
9766
|
+
clearPending();
|
|
9767
|
+
const nextPresentation = buildAssistantStatusPresentation({
|
|
9768
|
+
status: next,
|
|
9769
|
+
random
|
|
9770
|
+
});
|
|
9771
|
+
if (nextPresentation.key !== currentKey) {
|
|
9772
|
+
await postRenderedStatus(next);
|
|
9773
|
+
}
|
|
9774
|
+
};
|
|
9775
|
+
return {
|
|
9776
|
+
async start() {
|
|
9777
|
+
active = true;
|
|
9778
|
+
clearPending();
|
|
9779
|
+
currentStatus = makeAssistantStatus("thinking");
|
|
9780
|
+
currentKey = "";
|
|
9781
|
+
void postRenderedStatus(currentStatus);
|
|
9782
|
+
},
|
|
9783
|
+
async stop() {
|
|
9784
|
+
active = false;
|
|
9785
|
+
clearPending();
|
|
9786
|
+
if (rotationTimer) {
|
|
9787
|
+
clearTimer(rotationTimer);
|
|
9788
|
+
rotationTimer = null;
|
|
9789
|
+
}
|
|
9790
|
+
currentKey = "";
|
|
9791
|
+
await postStatus("");
|
|
9792
|
+
},
|
|
9793
|
+
async setStatus(status) {
|
|
9794
|
+
if (!active) {
|
|
9795
|
+
return;
|
|
9796
|
+
}
|
|
9797
|
+
const presentation = buildAssistantStatusPresentation({
|
|
9798
|
+
status,
|
|
9799
|
+
random
|
|
9800
|
+
});
|
|
9801
|
+
if (!presentation.visible) {
|
|
9802
|
+
return;
|
|
9803
|
+
}
|
|
9804
|
+
if (presentation.key === currentKey || presentation.key === pendingKey) {
|
|
9805
|
+
return;
|
|
9806
|
+
}
|
|
9807
|
+
const elapsed = now() - lastStatusAt;
|
|
9808
|
+
const waitMs = Math.max(
|
|
9809
|
+
STATUS_UPDATE_DEBOUNCE_MS - elapsed,
|
|
9810
|
+
STATUS_MIN_VISIBLE_MS - elapsed,
|
|
9811
|
+
0
|
|
9812
|
+
);
|
|
9813
|
+
if (waitMs <= 0) {
|
|
9814
|
+
clearPending();
|
|
9815
|
+
void postRenderedStatus(status);
|
|
9816
|
+
return;
|
|
9817
|
+
}
|
|
9818
|
+
pendingStatus = status;
|
|
9819
|
+
pendingKey = presentation.key;
|
|
9820
|
+
if (pendingTimer) {
|
|
9821
|
+
return;
|
|
9822
|
+
}
|
|
9823
|
+
pendingTimer = setTimer(
|
|
9824
|
+
() => {
|
|
9825
|
+
pendingTimer = null;
|
|
9826
|
+
void flushPending();
|
|
9827
|
+
},
|
|
9828
|
+
Math.max(1, waitMs)
|
|
9829
|
+
);
|
|
9830
|
+
}
|
|
9831
|
+
};
|
|
9832
|
+
}
|
|
9833
|
+
|
|
9562
9834
|
// src/handlers/oauth-resume.ts
|
|
9563
9835
|
function resolveReplyTimeoutMs(explicitTimeoutMs) {
|
|
9564
9836
|
if (typeof explicitTimeoutMs === "number" && explicitTimeoutMs > 0) {
|
|
@@ -9581,69 +9853,6 @@ async function postSlackMessage(channelId, threadTs, text) {
|
|
|
9581
9853
|
} catch {
|
|
9582
9854
|
}
|
|
9583
9855
|
}
|
|
9584
|
-
async function setAssistantStatus(channelId, threadTs, status) {
|
|
9585
|
-
try {
|
|
9586
|
-
await getSlackClient().assistant.threads.setStatus({
|
|
9587
|
-
channel_id: channelId,
|
|
9588
|
-
thread_ts: threadTs,
|
|
9589
|
-
status
|
|
9590
|
-
});
|
|
9591
|
-
} catch {
|
|
9592
|
-
}
|
|
9593
|
-
}
|
|
9594
|
-
var STATUS_DEBOUNCE_MS = 1e3;
|
|
9595
|
-
function createDebouncedStatusPoster(channelId, threadTs) {
|
|
9596
|
-
let lastPostAt = 0;
|
|
9597
|
-
let currentStatus = "";
|
|
9598
|
-
let pendingStatus = null;
|
|
9599
|
-
let pendingTimer = null;
|
|
9600
|
-
let stopped = false;
|
|
9601
|
-
const flush = async () => {
|
|
9602
|
-
if (stopped || !pendingStatus) return;
|
|
9603
|
-
const status = pendingStatus;
|
|
9604
|
-
pendingStatus = null;
|
|
9605
|
-
pendingTimer = null;
|
|
9606
|
-
lastPostAt = Date.now();
|
|
9607
|
-
currentStatus = status;
|
|
9608
|
-
await setAssistantStatus(channelId, threadTs, status);
|
|
9609
|
-
};
|
|
9610
|
-
const post = async (status) => {
|
|
9611
|
-
if (stopped) return;
|
|
9612
|
-
const truncated = truncateStatusText(status);
|
|
9613
|
-
if (!truncated || truncated === currentStatus) return;
|
|
9614
|
-
const now = Date.now();
|
|
9615
|
-
const elapsed = now - lastPostAt;
|
|
9616
|
-
if (elapsed >= STATUS_DEBOUNCE_MS) {
|
|
9617
|
-
if (pendingTimer) {
|
|
9618
|
-
clearTimeout(pendingTimer);
|
|
9619
|
-
pendingTimer = null;
|
|
9620
|
-
}
|
|
9621
|
-
pendingStatus = null;
|
|
9622
|
-
lastPostAt = now;
|
|
9623
|
-
currentStatus = truncated;
|
|
9624
|
-
await setAssistantStatus(channelId, threadTs, truncated);
|
|
9625
|
-
return;
|
|
9626
|
-
}
|
|
9627
|
-
pendingStatus = truncated;
|
|
9628
|
-
if (!pendingTimer) {
|
|
9629
|
-
pendingTimer = setTimeout(
|
|
9630
|
-
() => {
|
|
9631
|
-
void flush();
|
|
9632
|
-
},
|
|
9633
|
-
Math.max(1, STATUS_DEBOUNCE_MS - elapsed)
|
|
9634
|
-
);
|
|
9635
|
-
}
|
|
9636
|
-
};
|
|
9637
|
-
post.stop = () => {
|
|
9638
|
-
stopped = true;
|
|
9639
|
-
if (pendingTimer) {
|
|
9640
|
-
clearTimeout(pendingTimer);
|
|
9641
|
-
pendingTimer = null;
|
|
9642
|
-
}
|
|
9643
|
-
pendingStatus = null;
|
|
9644
|
-
};
|
|
9645
|
-
return post;
|
|
9646
|
-
}
|
|
9647
9856
|
function createReadOnlyConfigService(values) {
|
|
9648
9857
|
const entries = Object.entries(values).map(([key, value]) => ({
|
|
9649
9858
|
key,
|
|
@@ -9671,9 +9880,13 @@ function createReadOnlyConfigService(values) {
|
|
|
9671
9880
|
};
|
|
9672
9881
|
}
|
|
9673
9882
|
async function resumeAuthorizedRequest(args) {
|
|
9674
|
-
const
|
|
9883
|
+
const progress = createProgressReporter({
|
|
9884
|
+
channelId: args.channelId,
|
|
9885
|
+
threadTs: args.threadTs,
|
|
9886
|
+
transport: createSlackWebApiAssistantStatusTransport()
|
|
9887
|
+
});
|
|
9675
9888
|
await postSlackMessage(args.channelId, args.threadTs, args.connectedText);
|
|
9676
|
-
await
|
|
9889
|
+
await progress.start();
|
|
9677
9890
|
try {
|
|
9678
9891
|
const generateReply = args.generateReply ?? generateAssistantReply;
|
|
9679
9892
|
const replyPromise = generateReply(args.messageText, {
|
|
@@ -9691,7 +9904,7 @@ async function resumeAuthorizedRequest(args) {
|
|
|
9691
9904
|
artifactState: args.artifactState,
|
|
9692
9905
|
configuration: args.configuration,
|
|
9693
9906
|
channelConfiguration: args.configuration ? createReadOnlyConfigService(args.configuration) : void 0,
|
|
9694
|
-
onStatus:
|
|
9907
|
+
onStatus: (status) => progress.setStatus(status)
|
|
9695
9908
|
});
|
|
9696
9909
|
const replyTimeoutMs = resolveReplyTimeoutMs(args.replyTimeoutMs);
|
|
9697
9910
|
const reply = typeof replyTimeoutMs === "number" ? await Promise.race([
|
|
@@ -9707,8 +9920,7 @@ async function resumeAuthorizedRequest(args) {
|
|
|
9707
9920
|
)
|
|
9708
9921
|
)
|
|
9709
9922
|
]) : await replyPromise;
|
|
9710
|
-
|
|
9711
|
-
await setAssistantStatus(args.channelId, args.threadTs, "");
|
|
9923
|
+
await progress.stop();
|
|
9712
9924
|
if (args.onReply) {
|
|
9713
9925
|
await args.onReply(reply);
|
|
9714
9926
|
} else if (reply.text) {
|
|
@@ -9716,8 +9928,7 @@ async function resumeAuthorizedRequest(args) {
|
|
|
9716
9928
|
}
|
|
9717
9929
|
await args.onSuccess?.(reply);
|
|
9718
9930
|
} catch (error) {
|
|
9719
|
-
|
|
9720
|
-
await setAssistantStatus(args.channelId, args.threadTs, "");
|
|
9931
|
+
await progress.stop();
|
|
9721
9932
|
if (isRetryableTurnError(error, "mcp_auth_resume") && args.onAuthPause) {
|
|
9722
9933
|
await args.onAuthPause(error);
|
|
9723
9934
|
return;
|
|
@@ -11593,111 +11804,6 @@ function createJuniorRuntimeServices(overrides = {}) {
|
|
|
11593
11804
|
};
|
|
11594
11805
|
}
|
|
11595
11806
|
|
|
11596
|
-
// src/chat/runtime/progress-reporter.ts
|
|
11597
|
-
var STATUS_UPDATE_DEBOUNCE_MS = 1e3;
|
|
11598
|
-
var STATUS_MIN_VISIBLE_MS = 1200;
|
|
11599
|
-
function createProgressReporter(args) {
|
|
11600
|
-
const now = args.now ?? (() => Date.now());
|
|
11601
|
-
const setTimer = args.setTimer ?? ((callback, delayMs) => setTimeout(callback, delayMs));
|
|
11602
|
-
const clearTimer = args.clearTimer ?? ((timer) => clearTimeout(timer));
|
|
11603
|
-
let active = false;
|
|
11604
|
-
let currentStatus = "";
|
|
11605
|
-
let lastStatusAt = 0;
|
|
11606
|
-
let pendingStatus = null;
|
|
11607
|
-
let pendingTimer = null;
|
|
11608
|
-
let inflightStatusUpdate = Promise.resolve();
|
|
11609
|
-
const postStatus = async (text) => {
|
|
11610
|
-
const channelId = args.channelId;
|
|
11611
|
-
const threadTs = args.threadTs;
|
|
11612
|
-
if (!channelId || !threadTs) {
|
|
11613
|
-
return;
|
|
11614
|
-
}
|
|
11615
|
-
if (!text && !currentStatus) {
|
|
11616
|
-
return;
|
|
11617
|
-
}
|
|
11618
|
-
currentStatus = text;
|
|
11619
|
-
lastStatusAt = now();
|
|
11620
|
-
const suggestions = text ? [text] : void 0;
|
|
11621
|
-
const previous = inflightStatusUpdate;
|
|
11622
|
-
const request = (async () => {
|
|
11623
|
-
await previous;
|
|
11624
|
-
try {
|
|
11625
|
-
await args.setAssistantStatus(channelId, threadTs, text, suggestions);
|
|
11626
|
-
} catch (error) {
|
|
11627
|
-
logWarn(
|
|
11628
|
-
"assistant_status_update_failed",
|
|
11629
|
-
{},
|
|
11630
|
-
{
|
|
11631
|
-
"app.slack.status_text": text || "(clear)",
|
|
11632
|
-
"error.message": error instanceof Error ? error.message : String(error)
|
|
11633
|
-
},
|
|
11634
|
-
"Failed to update assistant status"
|
|
11635
|
-
);
|
|
11636
|
-
}
|
|
11637
|
-
})();
|
|
11638
|
-
inflightStatusUpdate = request;
|
|
11639
|
-
await request;
|
|
11640
|
-
};
|
|
11641
|
-
const clearPending = () => {
|
|
11642
|
-
if (pendingTimer) {
|
|
11643
|
-
clearTimer(pendingTimer);
|
|
11644
|
-
pendingTimer = null;
|
|
11645
|
-
}
|
|
11646
|
-
pendingStatus = null;
|
|
11647
|
-
};
|
|
11648
|
-
const flushPending = async () => {
|
|
11649
|
-
if (!active || !pendingStatus) {
|
|
11650
|
-
clearPending();
|
|
11651
|
-
return;
|
|
11652
|
-
}
|
|
11653
|
-
const next = pendingStatus;
|
|
11654
|
-
clearPending();
|
|
11655
|
-
if (next !== currentStatus) {
|
|
11656
|
-
await postStatus(next);
|
|
11657
|
-
}
|
|
11658
|
-
};
|
|
11659
|
-
return {
|
|
11660
|
-
async start() {
|
|
11661
|
-
active = true;
|
|
11662
|
-
clearPending();
|
|
11663
|
-
void postStatus("Thinking...");
|
|
11664
|
-
},
|
|
11665
|
-
async stop() {
|
|
11666
|
-
active = false;
|
|
11667
|
-
clearPending();
|
|
11668
|
-
await postStatus("");
|
|
11669
|
-
},
|
|
11670
|
-
async setStatus(text) {
|
|
11671
|
-
const truncated = truncateStatusText(text);
|
|
11672
|
-
if (!active || !truncated || truncated === currentStatus || truncated === pendingStatus) {
|
|
11673
|
-
return;
|
|
11674
|
-
}
|
|
11675
|
-
const elapsed = now() - lastStatusAt;
|
|
11676
|
-
const waitMs = Math.max(
|
|
11677
|
-
STATUS_UPDATE_DEBOUNCE_MS - elapsed,
|
|
11678
|
-
STATUS_MIN_VISIBLE_MS - elapsed,
|
|
11679
|
-
0
|
|
11680
|
-
);
|
|
11681
|
-
if (waitMs <= 0) {
|
|
11682
|
-
clearPending();
|
|
11683
|
-
void postStatus(truncated);
|
|
11684
|
-
return;
|
|
11685
|
-
}
|
|
11686
|
-
pendingStatus = truncated;
|
|
11687
|
-
if (pendingTimer) {
|
|
11688
|
-
return;
|
|
11689
|
-
}
|
|
11690
|
-
pendingTimer = setTimer(
|
|
11691
|
-
() => {
|
|
11692
|
-
pendingTimer = null;
|
|
11693
|
-
void flushPending();
|
|
11694
|
-
},
|
|
11695
|
-
Math.max(1, waitMs)
|
|
11696
|
-
);
|
|
11697
|
-
}
|
|
11698
|
-
};
|
|
11699
|
-
}
|
|
11700
|
-
|
|
11701
11807
|
// src/chat/runtime/streaming.ts
|
|
11702
11808
|
function createTextStreamBridge() {
|
|
11703
11809
|
const queue = [];
|
|
@@ -11738,36 +11844,6 @@ function createTextStreamBridge() {
|
|
|
11738
11844
|
}
|
|
11739
11845
|
};
|
|
11740
11846
|
}
|
|
11741
|
-
function createNormalizingStream(inner, normalize) {
|
|
11742
|
-
return {
|
|
11743
|
-
async *[Symbol.asyncIterator]() {
|
|
11744
|
-
let accumulated = "";
|
|
11745
|
-
let emitted = 0;
|
|
11746
|
-
for await (const chunk of inner) {
|
|
11747
|
-
accumulated += chunk;
|
|
11748
|
-
const lastNewline = accumulated.lastIndexOf("\n");
|
|
11749
|
-
if (lastNewline === -1) {
|
|
11750
|
-
const delta2 = accumulated.slice(emitted);
|
|
11751
|
-
if (delta2) {
|
|
11752
|
-
yield delta2;
|
|
11753
|
-
emitted = accumulated.length;
|
|
11754
|
-
}
|
|
11755
|
-
continue;
|
|
11756
|
-
}
|
|
11757
|
-
const stable = accumulated.slice(0, lastNewline + 1);
|
|
11758
|
-
const normalized = normalize(stable);
|
|
11759
|
-
const delta = normalized.slice(emitted);
|
|
11760
|
-
emitted = normalized.length;
|
|
11761
|
-
if (delta) yield delta;
|
|
11762
|
-
}
|
|
11763
|
-
if (accumulated) {
|
|
11764
|
-
const normalized = normalize(accumulated);
|
|
11765
|
-
const delta = normalized.slice(emitted);
|
|
11766
|
-
if (delta) yield delta;
|
|
11767
|
-
}
|
|
11768
|
-
}
|
|
11769
|
-
};
|
|
11770
|
-
}
|
|
11771
11847
|
|
|
11772
11848
|
// src/chat/runtime/reply-executor.ts
|
|
11773
11849
|
function getExecutionFailureReason(reply) {
|
|
@@ -11881,7 +11957,9 @@ function createReplyToThread(deps) {
|
|
|
11881
11957
|
const progress = createProgressReporter({
|
|
11882
11958
|
channelId,
|
|
11883
11959
|
threadTs,
|
|
11884
|
-
|
|
11960
|
+
transport: createSlackAdapterAssistantStatusTransport({
|
|
11961
|
+
getSlackAdapter: deps.getSlackAdapter
|
|
11962
|
+
})
|
|
11885
11963
|
});
|
|
11886
11964
|
const textStream = createTextStreamBridge();
|
|
11887
11965
|
let streamedReplyPromise;
|
|
@@ -11898,10 +11976,7 @@ function createReplyToThread(deps) {
|
|
|
11898
11976
|
if (!streamedReplyPromise) {
|
|
11899
11977
|
const streamingReply = (async () => {
|
|
11900
11978
|
return await postThreadReply(
|
|
11901
|
-
|
|
11902
|
-
textStream.iterable,
|
|
11903
|
-
ensureBlockSpacing
|
|
11904
|
-
),
|
|
11979
|
+
textStream.iterable,
|
|
11905
11980
|
"streaming_initial_post"
|
|
11906
11981
|
);
|
|
11907
11982
|
})();
|