blink 1.1.19 → 1.1.20
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/cli/create-slack-app-CyVPwWsG.js +1 -0
- package/dist/cli/{dev-BShY-dVP.js → dev-C5PZ-A_f.js} +5 -5
- package/dist/cli/index.js +3 -3
- package/dist/cli/{init-DoMgv4kU.js → init-CFrylDAP.js} +1 -1
- package/dist/cli/{init-templates-MqyReSD9.js → init-templates-CB4tNZfU.js} +249 -263
- package/dist/cli/{run-DFYLWiOa.js → run-bKDX6K46.js} +1 -1
- package/dist/cli/{setup-slack-app-DQ3iSM5s.js → setup-slack-app-D_iwQuzr.js} +1 -1
- package/dist/cli/{util-BQ3TH_Sv.js → util-DDB5k1Go.js} +4 -4
- package/dist/node/agent/index.node.cjs +1 -1
- package/dist/node/agent/index.node.d.cts +2 -2
- package/dist/node/agent/index.node.d.ts +2 -2
- package/dist/node/agent/index.node.js +1 -1
- package/dist/node/build/index.cjs +1 -1
- package/dist/node/build/index.d.cts +1 -1
- package/dist/node/build/index.d.ts +1 -1
- package/dist/node/build/index.js +1 -1
- package/dist/node/{build-D_L0gFGa.js → build-DYI55eX2.js} +1 -1
- package/dist/node/{build-BzoNpizh.cjs → build-EgdtUZTI.cjs} +1 -1
- package/dist/node/{index-C28Wm0bG.d.ts → index-CnBfy-54.d.ts} +1 -1
- package/dist/node/{index-DlWgNLmT.d.cts → index-DYOqnOdZ.d.cts} +1 -1
- package/dist/node/{index.node-TFdiI8lm.js → index.node-B6JXREDl.js} +1 -1
- package/dist/node/{index.node-D7Cvmag9.cjs → index.node-BrLfthBj.cjs} +1 -1
- package/dist/node/{index.node-B6plkLLA.d.cts → index.node-CFw4ITad.d.cts} +1 -21
- package/dist/node/{index.node-qJt_7e9E.d.ts → index.node-Dvc-cHCZ.d.ts} +1 -21
- package/dist/node/react/index.node.cjs +252 -266
- package/dist/node/react/index.node.d.cts +2 -2
- package/dist/node/react/index.node.d.ts +2 -2
- package/dist/node/react/index.node.js +252 -266
- package/package.json +2 -1
- package/dist/cli/create-slack-app-CNDXDwfs.js +0 -1
|
@@ -1,8 +1,15 @@
|
|
|
1
|
-
const e={scratch:{".
|
|
1
|
+
const e={scratch:{".env.local":`
|
|
2
|
+
# Store local environment variables here.
|
|
3
|
+
# They will be used by blink dev for development.
|
|
4
|
+
# EXTERNAL_SERVICE_API_KEY=
|
|
5
|
+
`,".env.production":`# Store production environment variables here.
|
|
6
|
+
# They will be upserted as secrets on blink deploy.
|
|
7
|
+
# EXTERNAL_SERVICE_API_KEY=
|
|
8
|
+
`,".gitignore":`# dependencies
|
|
2
9
|
node_modules
|
|
3
10
|
|
|
4
11
|
# config and build
|
|
5
|
-
|
|
12
|
+
.blink
|
|
6
13
|
|
|
7
14
|
# dotenv environment variables file
|
|
8
15
|
.env
|
|
@@ -10,103 +17,6 @@ data
|
|
|
10
17
|
|
|
11
18
|
# Finder (MacOS) folder config
|
|
12
19
|
.DS_Store
|
|
13
|
-
`,"tsconfig.json":`{
|
|
14
|
-
"compilerOptions": {
|
|
15
|
-
"lib": ["ESNext"],
|
|
16
|
-
"target": "ESNext",
|
|
17
|
-
"module": "Preserve",
|
|
18
|
-
"moduleDetection": "force",
|
|
19
|
-
|
|
20
|
-
"moduleResolution": "bundler",
|
|
21
|
-
"allowImportingTsExtensions": true,
|
|
22
|
-
"verbatimModuleSyntax": true,
|
|
23
|
-
"resolveJsonModule": true,
|
|
24
|
-
"noEmit": true,
|
|
25
|
-
|
|
26
|
-
"strict": true,
|
|
27
|
-
"skipLibCheck": true,
|
|
28
|
-
"noFallthroughCasesInSwitch": true,
|
|
29
|
-
"noUncheckedIndexedAccess": true,
|
|
30
|
-
"noImplicitOverride": true,
|
|
31
|
-
|
|
32
|
-
"noUnusedLocals": false,
|
|
33
|
-
"noUnusedParameters": false,
|
|
34
|
-
|
|
35
|
-
"types": ["node"]
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
`,".env.local":`
|
|
39
|
-
# Store local environment variables here.
|
|
40
|
-
# They will be used by blink dev for development.
|
|
41
|
-
# EXTERNAL_SERVICE_API_KEY=
|
|
42
|
-
`,".env.production":`# Store production environment variables here.
|
|
43
|
-
# They will be upserted as secrets on blink deploy.
|
|
44
|
-
# EXTERNAL_SERVICE_API_KEY=
|
|
45
|
-
`,"agent.ts.hbs":`import { convertToModelMessages, streamText, tool } from "ai";
|
|
46
|
-
import * as blink from "blink";
|
|
47
|
-
import { z } from "zod";
|
|
48
|
-
{{#if (eq aiProvider "anthropic")}}
|
|
49
|
-
import { anthropic } from "@ai-sdk/anthropic";
|
|
50
|
-
{{else if (eq aiProvider "openai")}}
|
|
51
|
-
import { openai } from "@ai-sdk/openai";
|
|
52
|
-
{{/if}}
|
|
53
|
-
|
|
54
|
-
const agent = new blink.Agent();
|
|
55
|
-
|
|
56
|
-
agent.on("chat", async ({ messages }) => {
|
|
57
|
-
return streamText({
|
|
58
|
-
{{#if (eq aiProvider "anthropic")}}
|
|
59
|
-
model: anthropic("claude-sonnet-4-5"),
|
|
60
|
-
{{else if (eq aiProvider "openai")}}
|
|
61
|
-
model: openai("gpt-5-chat-latest"),
|
|
62
|
-
{{else if (eq aiProvider "vercel")}}
|
|
63
|
-
model: "anthropic/claude-sonnet-4.5",
|
|
64
|
-
{{else}}
|
|
65
|
-
// Unknown provider: {{aiProvider}}. Defaulting to Vercel AI Gateway syntax.
|
|
66
|
-
model: "anthropic/claude-sonnet-4.5",
|
|
67
|
-
{{/if}}
|
|
68
|
-
system: \`You are a basic agent the user will customize.
|
|
69
|
-
|
|
70
|
-
Suggest the user enters edit mode with Ctrl+T or /edit to customize the agent.
|
|
71
|
-
Demonstrate your capabilities with the IP tool.\`,
|
|
72
|
-
messages: convertToModelMessages(messages),
|
|
73
|
-
tools: {
|
|
74
|
-
get_ip_info: tool({
|
|
75
|
-
description: "Get IP address information of the computer.",
|
|
76
|
-
inputSchema: z.object({}),
|
|
77
|
-
execute: async () => {
|
|
78
|
-
const response = await fetch("https://ipinfo.io/json");
|
|
79
|
-
return response.json();
|
|
80
|
-
},
|
|
81
|
-
}),
|
|
82
|
-
},
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
agent.serve();
|
|
87
|
-
`,"package.json.hbs":`{
|
|
88
|
-
"name": "{{packageName}}",
|
|
89
|
-
"main": "agent.ts",
|
|
90
|
-
"type": "module",
|
|
91
|
-
"private": true,
|
|
92
|
-
"scripts": {
|
|
93
|
-
"dev": "blink dev",
|
|
94
|
-
"deploy": "blink deploy"
|
|
95
|
-
},
|
|
96
|
-
"devDependencies": {
|
|
97
|
-
"zod": "latest",
|
|
98
|
-
"ai": "latest",
|
|
99
|
-
{{#if (eq aiProvider "anthropic")}}
|
|
100
|
-
"@ai-sdk/anthropic": "latest",
|
|
101
|
-
{{else if (eq aiProvider "openai")}}
|
|
102
|
-
"@ai-sdk/openai": "latest",
|
|
103
|
-
{{/if}}
|
|
104
|
-
"blink": "latest",
|
|
105
|
-
"esbuild": "latest",
|
|
106
|
-
"@types/node": "latest",
|
|
107
|
-
"typescript": "latest"
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
20
|
`,"AGENTS.md":`This project is a Blink agent.
|
|
111
21
|
|
|
112
22
|
You are an expert software engineer, which makes you an expert agent developer. You are highly idiomatic, opinionated, concise, and precise. The user prefers accuracy over speed.
|
|
@@ -180,7 +90,7 @@ const agent = new blink.Agent();
|
|
|
180
90
|
|
|
181
91
|
agent.on("chat", async ({ messages, chat, abortSignal }) => {
|
|
182
92
|
return streamText({
|
|
183
|
-
model:
|
|
93
|
+
model: "anthropic/claude-sonnet-4.5",
|
|
184
94
|
system: "You are a helpful assistant.",
|
|
185
95
|
messages: convertToModelMessages(messages, {
|
|
186
96
|
ignoreIncompleteToolCalls: true,
|
|
@@ -324,17 +234,6 @@ Tool Prefixing to avoid collisions:
|
|
|
324
234
|
|
|
325
235
|
LLM Models:
|
|
326
236
|
|
|
327
|
-
**Option 1: Blink Gateway** (Quick Start)
|
|
328
|
-
|
|
329
|
-
\`\`\`typescript
|
|
330
|
-
model: blink.model("anthropic/claude-sonnet-4.5");
|
|
331
|
-
model: blink.model("openai/gpt-5");
|
|
332
|
-
\`\`\`
|
|
333
|
-
|
|
334
|
-
Requires: \`blink login\` or \`BLINK_TOKEN\` env var
|
|
335
|
-
|
|
336
|
-
**Option 2: Direct Provider** (Production Recommended)
|
|
337
|
-
|
|
338
237
|
\`\`\`typescript
|
|
339
238
|
import { anthropic } from "@ai-sdk/anthropic";
|
|
340
239
|
import { openai } from "@ai-sdk/openai";
|
|
@@ -349,7 +248,6 @@ model: openai("gpt-5", { apiKey: process.env.OPENAI_API_KEY });
|
|
|
349
248
|
|
|
350
249
|
1. If \`ANTHROPIC_API_KEY\` is set: uses \`claude-sonnet-4.5\` via \`@ai-sdk/anthropic\`
|
|
351
250
|
2. If \`OPENAI_API_KEY\` is set: uses \`gpt-5\` via \`@ai-sdk/openai\`
|
|
352
|
-
3. Otherwise: falls back to \`blink.model("anthropic/claude-sonnet-4.5")\`
|
|
353
251
|
|
|
354
252
|
Available SDKs:
|
|
355
253
|
|
|
@@ -419,7 +317,7 @@ agent.on("request", async (request) => {
|
|
|
419
317
|
agent.on("chat", async ({ messages }) => {
|
|
420
318
|
const tools = slack.createTools({ client: app.client });
|
|
421
319
|
return streamText({
|
|
422
|
-
model:
|
|
320
|
+
model: "anthropic/claude-sonnet-4.5",
|
|
423
321
|
system: "You chatting with users in Slack.",
|
|
424
322
|
messages: convertToModelMessages(messages, {
|
|
425
323
|
ignoreIncompleteToolCalls: true,
|
|
@@ -443,7 +341,7 @@ Slack App Manifest:
|
|
|
443
341
|
- _ALWAYS_ include the "assistant:write" scope unless the user explicitly states otherwise - this allows Slack apps to set their status, which makes for a significantly better user experience. You _MUST_ provide "assistant_view" if you provide this scope.
|
|
444
342
|
- The user can always edit the manifest after creation, but you'd have to suggest it to them.
|
|
445
343
|
- "oauth_config" MUST BE PROVIDED - otherwise the app will have NO ACCESS.
|
|
446
|
-
- _ALWAYS_ default
|
|
344
|
+
- _ALWAYS_ default \`token_rotation_enabled\` to false unless the user explicitly asks for it. It is a _much_ simpler user-experience to not rotate tokens.
|
|
447
345
|
- For the best user experience, default to the following bot scopes (in the "oauth_config" > "scopes" > "bot"):
|
|
448
346
|
- "app_mentions:read"
|
|
449
347
|
- "reactions:write"
|
|
@@ -536,160 +434,54 @@ The agent process can restart at any time, so all important state must be extern
|
|
|
536
434
|
|
|
537
435
|
- Never use "as any" type assertions. Always figure out the correct typings.
|
|
538
436
|
</code_quality>
|
|
539
|
-
|
|
540
|
-
node_modules
|
|
541
|
-
|
|
542
|
-
# config and build
|
|
543
|
-
data
|
|
544
|
-
|
|
545
|
-
# dotenv environment variables file
|
|
546
|
-
.env
|
|
547
|
-
.env.*
|
|
548
|
-
|
|
549
|
-
# Finder (MacOS) folder config
|
|
550
|
-
.DS_Store
|
|
551
|
-
`,"tsconfig.json":`{
|
|
552
|
-
"compilerOptions": {
|
|
553
|
-
"lib": ["ESNext"],
|
|
554
|
-
"target": "ESNext",
|
|
555
|
-
"module": "Preserve",
|
|
556
|
-
"moduleDetection": "force",
|
|
557
|
-
|
|
558
|
-
"moduleResolution": "bundler",
|
|
559
|
-
"allowImportingTsExtensions": true,
|
|
560
|
-
"verbatimModuleSyntax": true,
|
|
561
|
-
"resolveJsonModule": true,
|
|
562
|
-
"noEmit": true,
|
|
563
|
-
|
|
564
|
-
"strict": true,
|
|
565
|
-
"skipLibCheck": true,
|
|
566
|
-
"noFallthroughCasesInSwitch": true,
|
|
567
|
-
"noUncheckedIndexedAccess": true,
|
|
568
|
-
"noImplicitOverride": true,
|
|
569
|
-
|
|
570
|
-
"noUnusedLocals": false,
|
|
571
|
-
"noUnusedParameters": false,
|
|
572
|
-
|
|
573
|
-
"types": ["node"]
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
`,".env.local":`
|
|
577
|
-
# Store local environment variables here.
|
|
578
|
-
# They will be used by blink dev for development.
|
|
579
|
-
SLACK_BOT_TOKEN=xoxb-your-token-here
|
|
580
|
-
SLACK_SIGNING_SECRET=your-signing-secret-here
|
|
581
|
-
`,".env.production":`# Store production environment variables here.
|
|
582
|
-
# They will be upserted as secrets on blink deploy.
|
|
583
|
-
# EXTERNAL_SERVICE_API_KEY=
|
|
584
|
-
`,"agent.ts.hbs":`import { convertToModelMessages, streamText } from "ai";
|
|
437
|
+
`,"agent.ts.hbs":`import { convertToModelMessages, streamText, tool } from "ai";
|
|
585
438
|
import * as blink from "blink";
|
|
586
|
-
import
|
|
587
|
-
import { App } from "@slack/bolt";
|
|
439
|
+
import { z } from "zod";
|
|
588
440
|
{{#if (eq aiProvider "anthropic")}}
|
|
589
441
|
import { anthropic } from "@ai-sdk/anthropic";
|
|
590
442
|
{{else if (eq aiProvider "openai")}}
|
|
591
443
|
import { openai } from "@ai-sdk/openai";
|
|
592
444
|
{{/if}}
|
|
593
445
|
|
|
594
|
-
const receiver = new slack.Receiver();
|
|
595
|
-
const app = new App({
|
|
596
|
-
token: process.env.SLACK_BOT_TOKEN,
|
|
597
|
-
signingSecret: process.env.SLACK_SIGNING_SECRET,
|
|
598
|
-
receiver,
|
|
599
|
-
});
|
|
600
|
-
|
|
601
|
-
// Handle messages in channels (only when @mentioned)
|
|
602
|
-
app.event("app_mention", async ({ event }) => {
|
|
603
|
-
const chat = await agent.chat.upsert([
|
|
604
|
-
"slack",
|
|
605
|
-
event.channel,
|
|
606
|
-
event.thread_ts ?? event.ts,
|
|
607
|
-
]);
|
|
608
|
-
const { message } = await slack.createMessageFromEvent({
|
|
609
|
-
client: app.client,
|
|
610
|
-
event,
|
|
611
|
-
});
|
|
612
|
-
await agent.chat.sendMessages(chat.id, [message]);
|
|
613
|
-
await app.client.assistant.threads.setStatus({
|
|
614
|
-
channel_id: event.channel,
|
|
615
|
-
status: "is typing...",
|
|
616
|
-
thread_ts: event.thread_ts ?? event.ts,
|
|
617
|
-
});
|
|
618
|
-
});
|
|
619
|
-
|
|
620
|
-
// Handle direct messages (always respond)
|
|
621
|
-
app.event("message", async ({ event }) => {
|
|
622
|
-
// Ignore bot messages and message changes
|
|
623
|
-
if (event.subtype || event.bot_id) {
|
|
624
|
-
return;
|
|
625
|
-
}
|
|
626
|
-
// Only handle DMs (channel type is 'im')
|
|
627
|
-
const channelInfo = await app.client.conversations.info({
|
|
628
|
-
channel: event.channel,
|
|
629
|
-
});
|
|
630
|
-
if (!channelInfo.channel?.is_im) {
|
|
631
|
-
return;
|
|
632
|
-
}
|
|
633
|
-
const chat = await agent.chat.upsert(["slack", event.channel]);
|
|
634
|
-
const { message } = await slack.createMessageFromEvent({
|
|
635
|
-
client: app.client,
|
|
636
|
-
event,
|
|
637
|
-
});
|
|
638
|
-
await agent.chat.sendMessages(chat.id, [message]);
|
|
639
|
-
await app.client.assistant.threads.setStatus({
|
|
640
|
-
channel_id: event.channel,
|
|
641
|
-
status: "is typing...",
|
|
642
|
-
thread_ts: event.thread_ts ?? event.ts,
|
|
643
|
-
});
|
|
644
|
-
});
|
|
645
|
-
|
|
646
446
|
const agent = new blink.Agent();
|
|
647
447
|
|
|
648
|
-
agent.on("request", async (request) => {
|
|
649
|
-
return receiver.handle(app, request);
|
|
650
|
-
});
|
|
651
|
-
|
|
652
448
|
agent.on("chat", async ({ messages }) => {
|
|
653
|
-
const tools = slack.createTools({ client: app.client });
|
|
654
|
-
const lastMessage = messages[messages.length - 1];
|
|
655
|
-
const threadInfo = lastMessage?.metadata as
|
|
656
|
-
| { channel?: string; thread_ts?: string }
|
|
657
|
-
| undefined;
|
|
658
|
-
|
|
659
|
-
// Add instruction to clear status after completion
|
|
660
|
-
if (threadInfo?.channel && threadInfo?.thread_ts) {
|
|
661
|
-
const clonedMessages = structuredClone(messages);
|
|
662
|
-
const lastClonedMessage = clonedMessages[clonedMessages.length - 1];
|
|
663
|
-
if (lastClonedMessage) {
|
|
664
|
-
lastClonedMessage.parts.push({
|
|
665
|
-
type: "text",
|
|
666
|
-
text: \`*INTERNAL INSTRUCTION*: Clear the status of this thread after you finish: channel=\${threadInfo.channel} thread_ts=\${threadInfo.thread_ts}\`,
|
|
667
|
-
});
|
|
668
|
-
}
|
|
669
|
-
messages = clonedMessages;
|
|
670
|
-
}
|
|
671
|
-
|
|
672
449
|
return streamText({
|
|
673
450
|
{{#if (eq aiProvider "anthropic")}}
|
|
674
451
|
model: anthropic("claude-sonnet-4-5"),
|
|
675
452
|
{{else if (eq aiProvider "openai")}}
|
|
676
|
-
model: openai("gpt-5-
|
|
453
|
+
model: openai("gpt-5-codex"),
|
|
454
|
+
providerOptions: {
|
|
455
|
+
openai: {
|
|
456
|
+
reasoningSummary: "detailed",
|
|
457
|
+
},
|
|
458
|
+
},
|
|
677
459
|
{{else if (eq aiProvider "vercel")}}
|
|
678
460
|
model: "anthropic/claude-sonnet-4.5",
|
|
679
461
|
{{else}}
|
|
680
462
|
// Unknown provider: {{aiProvider}}. Defaulting to Vercel AI Gateway syntax.
|
|
681
463
|
model: "anthropic/claude-sonnet-4.5",
|
|
682
464
|
{{/if}}
|
|
683
|
-
system:
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
tools
|
|
465
|
+
system: \`You are a basic agent the user will customize.
|
|
466
|
+
|
|
467
|
+
Suggest the user enters edit mode with Ctrl+T or /edit to customize the agent.
|
|
468
|
+
Demonstrate your capabilities with the IP tool.\`,
|
|
469
|
+
messages: convertToModelMessages(messages),
|
|
470
|
+
tools: {
|
|
471
|
+
get_ip_info: tool({
|
|
472
|
+
description: "Get IP address information of the computer.",
|
|
473
|
+
inputSchema: z.object({}),
|
|
474
|
+
execute: async () => {
|
|
475
|
+
const response = await fetch("https://ipinfo.io/json");
|
|
476
|
+
return response.json();
|
|
477
|
+
},
|
|
478
|
+
}),
|
|
479
|
+
},
|
|
689
480
|
});
|
|
690
481
|
});
|
|
691
482
|
|
|
692
|
-
agent.serve()
|
|
483
|
+
agent.serve();
|
|
484
|
+
`,"package.json.hbs":`{
|
|
693
485
|
"name": "{{packageName}}",
|
|
694
486
|
"main": "agent.ts",
|
|
695
487
|
"type": "module",
|
|
@@ -709,11 +501,54 @@ agent.serve();`,"package.json.hbs":`{
|
|
|
709
501
|
"blink": "latest",
|
|
710
502
|
"esbuild": "latest",
|
|
711
503
|
"@types/node": "latest",
|
|
712
|
-
"typescript": "latest"
|
|
713
|
-
"@slack/bolt": "latest",
|
|
714
|
-
"@blink-sdk/slack": "latest"
|
|
504
|
+
"typescript": "latest"
|
|
715
505
|
}
|
|
716
506
|
}
|
|
507
|
+
`,"tsconfig.json":`{
|
|
508
|
+
"compilerOptions": {
|
|
509
|
+
"lib": ["ESNext"],
|
|
510
|
+
"target": "ESNext",
|
|
511
|
+
"module": "Preserve",
|
|
512
|
+
"moduleDetection": "force",
|
|
513
|
+
|
|
514
|
+
"moduleResolution": "bundler",
|
|
515
|
+
"allowImportingTsExtensions": true,
|
|
516
|
+
"verbatimModuleSyntax": true,
|
|
517
|
+
"resolveJsonModule": true,
|
|
518
|
+
"noEmit": true,
|
|
519
|
+
|
|
520
|
+
"strict": true,
|
|
521
|
+
"skipLibCheck": true,
|
|
522
|
+
"noFallthroughCasesInSwitch": true,
|
|
523
|
+
"noUncheckedIndexedAccess": true,
|
|
524
|
+
"noImplicitOverride": true,
|
|
525
|
+
|
|
526
|
+
"noUnusedLocals": false,
|
|
527
|
+
"noUnusedParameters": false,
|
|
528
|
+
|
|
529
|
+
"types": ["node"]
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
`},"slack-bot":{".env.local":`
|
|
533
|
+
# Store local environment variables here.
|
|
534
|
+
# They will be used by blink dev for development.
|
|
535
|
+
SLACK_BOT_TOKEN=xoxb-your-token-here
|
|
536
|
+
SLACK_SIGNING_SECRET=your-signing-secret-here
|
|
537
|
+
`,".env.production":`# Store production environment variables here.
|
|
538
|
+
# They will be upserted as secrets on blink deploy.
|
|
539
|
+
# EXTERNAL_SERVICE_API_KEY=
|
|
540
|
+
`,".gitignore":`# dependencies
|
|
541
|
+
node_modules
|
|
542
|
+
|
|
543
|
+
# config and build
|
|
544
|
+
.blink
|
|
545
|
+
|
|
546
|
+
# dotenv environment variables file
|
|
547
|
+
.env
|
|
548
|
+
.env.*
|
|
549
|
+
|
|
550
|
+
# Finder (MacOS) folder config
|
|
551
|
+
.DS_Store
|
|
717
552
|
`,"AGENTS.md":`This project is a Blink agent.
|
|
718
553
|
|
|
719
554
|
You are an expert software engineer, which makes you an expert agent developer. You are highly idiomatic, opinionated, concise, and precise. The user prefers accuracy over speed.
|
|
@@ -787,7 +622,7 @@ const agent = new blink.Agent();
|
|
|
787
622
|
|
|
788
623
|
agent.on("chat", async ({ messages, chat, abortSignal }) => {
|
|
789
624
|
return streamText({
|
|
790
|
-
model:
|
|
625
|
+
model: "anthropic/claude-sonnet-4.5",
|
|
791
626
|
system: "You are a helpful assistant.",
|
|
792
627
|
messages: convertToModelMessages(messages, {
|
|
793
628
|
ignoreIncompleteToolCalls: true,
|
|
@@ -931,17 +766,6 @@ Tool Prefixing to avoid collisions:
|
|
|
931
766
|
|
|
932
767
|
LLM Models:
|
|
933
768
|
|
|
934
|
-
**Option 1: Blink Gateway** (Quick Start)
|
|
935
|
-
|
|
936
|
-
\`\`\`typescript
|
|
937
|
-
model: blink.model("anthropic/claude-sonnet-4.5");
|
|
938
|
-
model: blink.model("openai/gpt-5");
|
|
939
|
-
\`\`\`
|
|
940
|
-
|
|
941
|
-
Requires: \`blink login\` or \`BLINK_TOKEN\` env var
|
|
942
|
-
|
|
943
|
-
**Option 2: Direct Provider** (Production Recommended)
|
|
944
|
-
|
|
945
769
|
\`\`\`typescript
|
|
946
770
|
import { anthropic } from "@ai-sdk/anthropic";
|
|
947
771
|
import { openai } from "@ai-sdk/openai";
|
|
@@ -956,7 +780,6 @@ model: openai("gpt-5", { apiKey: process.env.OPENAI_API_KEY });
|
|
|
956
780
|
|
|
957
781
|
1. If \`ANTHROPIC_API_KEY\` is set: uses \`claude-sonnet-4.5\` via \`@ai-sdk/anthropic\`
|
|
958
782
|
2. If \`OPENAI_API_KEY\` is set: uses \`gpt-5\` via \`@ai-sdk/openai\`
|
|
959
|
-
3. Otherwise: falls back to \`blink.model("anthropic/claude-sonnet-4.5")\`
|
|
960
783
|
|
|
961
784
|
Available SDKs:
|
|
962
785
|
|
|
@@ -1026,7 +849,7 @@ agent.on("request", async (request) => {
|
|
|
1026
849
|
agent.on("chat", async ({ messages }) => {
|
|
1027
850
|
const tools = slack.createTools({ client: app.client });
|
|
1028
851
|
return streamText({
|
|
1029
|
-
model:
|
|
852
|
+
model: "anthropic/claude-sonnet-4.5",
|
|
1030
853
|
system: "You chatting with users in Slack.",
|
|
1031
854
|
messages: convertToModelMessages(messages, {
|
|
1032
855
|
ignoreIncompleteToolCalls: true,
|
|
@@ -1050,7 +873,7 @@ Slack App Manifest:
|
|
|
1050
873
|
- _ALWAYS_ include the "assistant:write" scope unless the user explicitly states otherwise - this allows Slack apps to set their status, which makes for a significantly better user experience. You _MUST_ provide "assistant_view" if you provide this scope.
|
|
1051
874
|
- The user can always edit the manifest after creation, but you'd have to suggest it to them.
|
|
1052
875
|
- "oauth_config" MUST BE PROVIDED - otherwise the app will have NO ACCESS.
|
|
1053
|
-
- _ALWAYS_ default
|
|
876
|
+
- _ALWAYS_ default \`token_rotation_enabled\` to false unless the user explicitly asks for it. It is a _much_ simpler user-experience to not rotate tokens.
|
|
1054
877
|
- For the best user experience, default to the following bot scopes (in the "oauth_config" > "scopes" > "bot"):
|
|
1055
878
|
- "app_mentions:read"
|
|
1056
879
|
- "reactions:write"
|
|
@@ -1143,4 +966,167 @@ The agent process can restart at any time, so all important state must be extern
|
|
|
1143
966
|
|
|
1144
967
|
- Never use "as any" type assertions. Always figure out the correct typings.
|
|
1145
968
|
</code_quality>
|
|
969
|
+
`,"agent.ts.hbs":`import { convertToModelMessages, streamText } from "ai";
|
|
970
|
+
import * as blink from "blink";
|
|
971
|
+
import * as slack from "@blink-sdk/slack";
|
|
972
|
+
import { App } from "@slack/bolt";
|
|
973
|
+
{{#if (eq aiProvider "anthropic")}}
|
|
974
|
+
import { anthropic } from "@ai-sdk/anthropic";
|
|
975
|
+
{{else if (eq aiProvider "openai")}}
|
|
976
|
+
import { openai } from "@ai-sdk/openai";
|
|
977
|
+
{{/if}}
|
|
978
|
+
|
|
979
|
+
const receiver = new slack.Receiver();
|
|
980
|
+
const app = new App({
|
|
981
|
+
token: process.env.SLACK_BOT_TOKEN,
|
|
982
|
+
signingSecret: process.env.SLACK_SIGNING_SECRET,
|
|
983
|
+
receiver,
|
|
984
|
+
});
|
|
985
|
+
|
|
986
|
+
// Handle messages in channels (only when @mentioned)
|
|
987
|
+
app.event("app_mention", async ({ event }) => {
|
|
988
|
+
const chat = await agent.chat.upsert([
|
|
989
|
+
"slack",
|
|
990
|
+
event.channel,
|
|
991
|
+
event.thread_ts ?? event.ts,
|
|
992
|
+
]);
|
|
993
|
+
const { message } = await slack.createMessageFromEvent({
|
|
994
|
+
client: app.client,
|
|
995
|
+
event,
|
|
996
|
+
});
|
|
997
|
+
await agent.chat.sendMessages(chat.id, [message]);
|
|
998
|
+
await app.client.assistant.threads.setStatus({
|
|
999
|
+
channel_id: event.channel,
|
|
1000
|
+
status: "is typing...",
|
|
1001
|
+
thread_ts: event.thread_ts ?? event.ts,
|
|
1002
|
+
});
|
|
1003
|
+
});
|
|
1004
|
+
|
|
1005
|
+
// Handle direct messages (always respond)
|
|
1006
|
+
app.event("message", async ({ event }) => {
|
|
1007
|
+
// Ignore bot messages and message changes
|
|
1008
|
+
if (event.subtype || event.bot_id) {
|
|
1009
|
+
return;
|
|
1010
|
+
}
|
|
1011
|
+
// Only handle DMs (channel type is 'im')
|
|
1012
|
+
const channelInfo = await app.client.conversations.info({
|
|
1013
|
+
channel: event.channel,
|
|
1014
|
+
});
|
|
1015
|
+
if (!channelInfo.channel?.is_im) {
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
1018
|
+
const chat = await agent.chat.upsert(["slack", event.channel]);
|
|
1019
|
+
const { message } = await slack.createMessageFromEvent({
|
|
1020
|
+
client: app.client,
|
|
1021
|
+
event,
|
|
1022
|
+
});
|
|
1023
|
+
await agent.chat.sendMessages(chat.id, [message]);
|
|
1024
|
+
await app.client.assistant.threads.setStatus({
|
|
1025
|
+
channel_id: event.channel,
|
|
1026
|
+
status: "is typing...",
|
|
1027
|
+
thread_ts: event.thread_ts ?? event.ts,
|
|
1028
|
+
});
|
|
1029
|
+
});
|
|
1030
|
+
|
|
1031
|
+
const agent = new blink.Agent();
|
|
1032
|
+
|
|
1033
|
+
agent.on("request", async (request) => {
|
|
1034
|
+
return receiver.handle(app, request);
|
|
1035
|
+
});
|
|
1036
|
+
|
|
1037
|
+
agent.on("chat", async ({ messages }) => {
|
|
1038
|
+
const tools = slack.createTools({ client: app.client });
|
|
1039
|
+
const lastMessage = messages[messages.length - 1];
|
|
1040
|
+
const threadInfo = lastMessage?.metadata as
|
|
1041
|
+
| { channel?: string; thread_ts?: string }
|
|
1042
|
+
| undefined;
|
|
1043
|
+
|
|
1044
|
+
// Add instruction to clear status after completion
|
|
1045
|
+
if (threadInfo?.channel && threadInfo?.thread_ts) {
|
|
1046
|
+
const clonedMessages = structuredClone(messages);
|
|
1047
|
+
const lastClonedMessage = clonedMessages[clonedMessages.length - 1];
|
|
1048
|
+
if (lastClonedMessage) {
|
|
1049
|
+
lastClonedMessage.parts.push({
|
|
1050
|
+
type: "text",
|
|
1051
|
+
text: \`*INTERNAL INSTRUCTION*: Clear the status of this thread after you finish: channel=\${threadInfo.channel} thread_ts=\${threadInfo.thread_ts}\`,
|
|
1052
|
+
});
|
|
1053
|
+
}
|
|
1054
|
+
messages = clonedMessages;
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
return streamText({
|
|
1058
|
+
{{#if (eq aiProvider "anthropic")}}
|
|
1059
|
+
model: anthropic("claude-sonnet-4-5"),
|
|
1060
|
+
{{else if (eq aiProvider "openai")}}
|
|
1061
|
+
model: openai("gpt-5-codex"),
|
|
1062
|
+
providerOptions: {
|
|
1063
|
+
openai: {
|
|
1064
|
+
reasoningSummary: "detailed",
|
|
1065
|
+
},
|
|
1066
|
+
},
|
|
1067
|
+
{{else if (eq aiProvider "vercel")}}
|
|
1068
|
+
model: "anthropic/claude-sonnet-4.5",
|
|
1069
|
+
{{else}}
|
|
1070
|
+
// Unknown provider: {{aiProvider}}. Defaulting to Vercel AI Gateway syntax.
|
|
1071
|
+
model: "anthropic/claude-sonnet-4.5",
|
|
1072
|
+
{{/if}}
|
|
1073
|
+
system: "You are a helpful Slack bot assistant.",
|
|
1074
|
+
messages: convertToModelMessages(messages, {
|
|
1075
|
+
ignoreIncompleteToolCalls: true,
|
|
1076
|
+
tools,
|
|
1077
|
+
}),
|
|
1078
|
+
tools,
|
|
1079
|
+
});
|
|
1080
|
+
});
|
|
1081
|
+
|
|
1082
|
+
agent.serve();`,"package.json.hbs":`{
|
|
1083
|
+
"name": "{{packageName}}",
|
|
1084
|
+
"main": "agent.ts",
|
|
1085
|
+
"type": "module",
|
|
1086
|
+
"private": true,
|
|
1087
|
+
"scripts": {
|
|
1088
|
+
"dev": "blink dev",
|
|
1089
|
+
"deploy": "blink deploy"
|
|
1090
|
+
},
|
|
1091
|
+
"devDependencies": {
|
|
1092
|
+
"zod": "latest",
|
|
1093
|
+
"ai": "latest",
|
|
1094
|
+
{{#if (eq aiProvider "anthropic")}}
|
|
1095
|
+
"@ai-sdk/anthropic": "latest",
|
|
1096
|
+
{{else if (eq aiProvider "openai")}}
|
|
1097
|
+
"@ai-sdk/openai": "latest",
|
|
1098
|
+
{{/if}}
|
|
1099
|
+
"blink": "latest",
|
|
1100
|
+
"esbuild": "latest",
|
|
1101
|
+
"@types/node": "latest",
|
|
1102
|
+
"typescript": "latest",
|
|
1103
|
+
"@slack/bolt": "latest",
|
|
1104
|
+
"@blink-sdk/slack": "latest"
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
`,"tsconfig.json":`{
|
|
1108
|
+
"compilerOptions": {
|
|
1109
|
+
"lib": ["ESNext"],
|
|
1110
|
+
"target": "ESNext",
|
|
1111
|
+
"module": "Preserve",
|
|
1112
|
+
"moduleDetection": "force",
|
|
1113
|
+
|
|
1114
|
+
"moduleResolution": "bundler",
|
|
1115
|
+
"allowImportingTsExtensions": true,
|
|
1116
|
+
"verbatimModuleSyntax": true,
|
|
1117
|
+
"resolveJsonModule": true,
|
|
1118
|
+
"noEmit": true,
|
|
1119
|
+
|
|
1120
|
+
"strict": true,
|
|
1121
|
+
"skipLibCheck": true,
|
|
1122
|
+
"noFallthroughCasesInSwitch": true,
|
|
1123
|
+
"noUncheckedIndexedAccess": true,
|
|
1124
|
+
"noImplicitOverride": true,
|
|
1125
|
+
|
|
1126
|
+
"noUnusedLocals": false,
|
|
1127
|
+
"noUnusedParameters": false,
|
|
1128
|
+
|
|
1129
|
+
"types": ["node"]
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1146
1132
|
`}};export{e as templates};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{__toESM as e}from"./chunk-D9KrCrVq.js";import{findNearestEntry as t,
|
|
1
|
+
import{__toESM as e}from"./chunk-D9KrCrVq.js";import{findNearestEntry as t,migrateDataToBlink as n,require_main as r,resolveConfig as i}from"./util-DDB5k1Go.js";import"./dist-Ceoe3h7v.js";import{getAuthToken as a}from"./auth-CoJASYGP.js";import"./util-BgbIWOFE.js";import{ChatManager as o,Client as s}from"./chat-manager-BRchJngj.js";import{spawn as c}from"node:child_process";import{join as l,resolve as u}from"node:path";import{existsSync as d}from"node:fs";import{readFile as f}from"node:fs/promises";import{createServer as p}from"node:net";async function m(e){let t=e.env?.PORT??await h(),n=e.env?.HOST??`127.0.0.1`,r=`http://${n}:${t}`,i={...e.env??process.env,PORT:t.toString(),HOST:n},a=c(e.command,e.args,{stdio:`pipe`,env:i});e.signal?.addEventListener(`abort`,()=>{try{a.kill()}catch{}},{once:!0});let o=new AbortController,l=[o.signal];e.signal&&l.push(e.signal);let u=AbortSignal.any(l);a.stdout.on(`data`,t=>{e.onStdout?.(Buffer.from(t).toString(`utf-8`))});let d=``;a.stderr.on(`data`,t=>{o.signal.aborted||(d+=Buffer.from(t).toString(`utf-8`)),e.onStderr?.(Buffer.from(t).toString(`utf-8`))}),a.on(`error`,e=>{o.abort(e)}),a.on(`exit`,(t,n)=>{o.signal.aborted?e.onExit?.(t,n):o.abort()});let f=new s({baseUrl:r}),p=0;for(;!u.aborted;){try{await f.health();break}catch{}if(await new Promise(e=>setTimeout(e,p*5)),p++,p>100)throw Error(`Health endpoint timed out`)}if(u.aborted)throw u.reason;return o.abort(),{client:f,dispose:()=>{a.kill()}}}async function h(){let e=p();return new Promise((t,n)=>{e.listen(0,()=>{let n=e.address().port;t(n)}).on(`error`,e=>{n(e)})}).finally(()=>{e.close()})}var g=e(r(),1);async function _(e,r={}){if(!r.directory){let e=process.cwd();try{i(e),r.directory=e}catch{let n=await t(e,`.blink`);n&&d(l(n,`build`))&&(n=void 0),n?r.directory=n:r.directory=e}}await n(r.directory);let s=i(r.directory),c={};try{c=(0,g.parse)(await f(l(r.directory,`.env.local`),`utf-8`))}catch{}let p=await a(),h=await m({command:`node`,args:[`--experimental-strip-types`,`--no-deprecation`,s.entry],env:{...process.env,...c,BLINK_TOKEN:p}});console.log(`Agent spawned`);let _=u(r?.directory??process.cwd(),`.blink`,`chats`),v=new o({chatId:r?.chat,chatsDirectory:_});v.setAgent(h.client);try{let t=new Promise(e=>{let t=v.subscribe(n=>{(n.status===`idle`||n.status===`error`)&&(t(),e())})});await v.sendMessages([{id:crypto.randomUUID(),created_at:new Date().toISOString(),metadata:void 0,parts:[{type:`text`,text:e.join(` `)}],role:`user`,mode:`run`}]),await t;let n=v.getState();n.error&&console.error(`Error:`,n.error),console.log(`Final state:`,n.messages.pop())}finally{v.dispose(),h.dispose()}}export{_ as default};
|