@fastagent-sh/fastagent 0.13.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/dist/agent.d.ts +20 -0
- package/dist/agent.js +9 -0
- package/dist/channels/context-buffer.d.ts +30 -0
- package/dist/channels/context-buffer.js +98 -0
- package/dist/channels/control.d.ts +28 -0
- package/dist/channels/control.js +214 -0
- package/dist/channels/feishu/card.js +1 -1
- package/dist/channels/feishu/context-buffer.d.ts +43 -0
- package/dist/channels/feishu/context-buffer.js +72 -0
- package/dist/channels/feishu/crypto.d.ts +4 -2
- package/dist/channels/feishu/crypto.js +4 -2
- package/dist/channels/feishu/feishu-api.d.ts +15 -7
- package/dist/channels/feishu/feishu-api.js +22 -4
- package/dist/channels/feishu/feishu.d.ts +38 -16
- package/dist/channels/feishu/feishu.js +286 -151
- package/dist/channels/feishu/invoke-turn.d.ts +24 -31
- package/dist/channels/feishu/invoke-turn.js +61 -62
- package/dist/channels/feishu/model.d.ts +98 -0
- package/dist/channels/feishu/model.js +9 -0
- package/dist/channels/feishu/normalize.d.ts +23 -0
- package/dist/channels/feishu/normalize.js +132 -0
- package/dist/channels/feishu/owned-threads.d.ts +7 -0
- package/dist/channels/feishu/owned-threads.js +47 -0
- package/dist/channels/feishu/parse.d.ts +21 -103
- package/dist/channels/feishu/parse.js +35 -145
- package/dist/channels/feishu/preview.d.ts +4 -7
- package/dist/channels/feishu/preview.js +26 -142
- package/dist/channels/feishu/register-app.d.ts +2 -1
- package/dist/channels/feishu/register-webhook.d.ts +2 -1
- package/dist/channels/feishu/scaffold/channel.ts +10 -3
- package/dist/channels/feishu/setup-mode.d.ts +8 -0
- package/dist/channels/feishu/setup-mode.js +2 -0
- package/dist/channels/feishu/ws-ingress.d.ts +28 -0
- package/dist/channels/feishu/ws-ingress.js +136 -0
- package/dist/channels/github/github.js +8 -6
- package/dist/channels/http.d.ts +14 -0
- package/dist/channels/http.js +35 -2
- package/dist/channels/invoke-turn-kit.d.ts +65 -0
- package/dist/channels/invoke-turn-kit.js +87 -0
- package/dist/channels/lark/lark.d.ts +4 -2
- package/dist/channels/lark/lark.js +4 -1
- package/dist/channels/lark/onboard.d.ts +8 -4
- package/dist/channels/lark/onboard.js +8 -0
- package/dist/channels/lark/scaffold/channel.ts +10 -3
- package/dist/channels/preview-kit.d.ts +109 -0
- package/dist/channels/preview-kit.js +183 -0
- package/dist/channels/seen.d.ts +5 -0
- package/dist/channels/seen.js +35 -0
- package/dist/channels/slack/bot-auth.d.ts +15 -0
- package/dist/channels/slack/bot-auth.js +146 -0
- package/dist/channels/slack/config-api.d.ts +60 -0
- package/dist/channels/slack/config-api.js +149 -0
- package/dist/channels/slack/context-buffer.d.ts +24 -0
- package/dist/channels/slack/context-buffer.js +37 -0
- package/dist/channels/slack/invoke-turn.d.ts +19 -0
- package/dist/channels/slack/invoke-turn.js +63 -0
- package/dist/channels/slack/manifest.d.ts +49 -0
- package/dist/channels/slack/manifest.js +69 -0
- package/dist/channels/slack/model.d.ts +67 -0
- package/dist/channels/slack/model.js +2 -0
- package/dist/channels/slack/onboard.d.ts +41 -0
- package/dist/channels/slack/onboard.js +120 -0
- package/dist/channels/slack/onboarding-state.d.ts +31 -0
- package/dist/channels/slack/onboarding-state.js +69 -0
- package/dist/channels/slack/owned-threads.d.ts +6 -0
- package/dist/channels/slack/owned-threads.js +43 -0
- package/dist/channels/slack/parse.d.ts +23 -0
- package/dist/channels/slack/parse.js +81 -0
- package/dist/channels/slack/preview.d.ts +24 -0
- package/dist/channels/slack/preview.js +359 -0
- package/dist/channels/slack/reaction.d.ts +24 -0
- package/dist/channels/slack/reaction.js +62 -0
- package/dist/channels/slack/register-webhook.d.ts +10 -0
- package/dist/channels/slack/register-webhook.js +49 -0
- package/dist/channels/slack/scaffold/channel.ts +33 -0
- package/dist/channels/slack/scaffold/slack-send.ts +171 -0
- package/dist/channels/slack/setup-server.d.ts +17 -0
- package/dist/channels/slack/setup-server.js +103 -0
- package/dist/channels/slack/slack-api.d.ts +77 -0
- package/dist/channels/slack/slack-api.js +415 -0
- package/dist/channels/slack/slack.d.ts +58 -0
- package/dist/channels/slack/slack.js +451 -0
- package/dist/channels/slack/welcomed.d.ts +5 -0
- package/dist/channels/slack/welcomed.js +32 -0
- package/dist/channels/state.js +3 -3
- package/dist/channels/stop-command.d.ts +6 -0
- package/dist/channels/stop-command.js +36 -0
- package/dist/channels/tasks.d.ts +13 -0
- package/dist/channels/tasks.js +10 -0
- package/dist/channels/telegram/context-buffer.d.ts +8 -17
- package/dist/channels/telegram/context-buffer.js +6 -85
- package/dist/channels/telegram/invoke-turn.d.ts +5 -22
- package/dist/channels/telegram/invoke-turn.js +11 -58
- package/dist/channels/telegram/preview.d.ts +4 -7
- package/dist/channels/telegram/preview.js +24 -142
- package/dist/channels/telegram/telegram.js +23 -9
- package/dist/channels/{feishu/text.d.ts → text.d.ts} +1 -1
- package/dist/channels/{feishu/text.js → text.js} +1 -1
- package/dist/channels/turn-queue.js +1 -1
- package/dist/channels/turn-store.d.ts +1 -1
- package/dist/channels/turn-store.js +2 -3
- package/dist/cli/add-feishu.d.ts +27 -0
- package/dist/{cli-add-feishu.js → cli/add-feishu.js} +167 -44
- package/dist/cli/add-slack.d.ts +10 -0
- package/dist/cli/add-slack.js +204 -0
- package/dist/{cli-auth.d.ts → cli/auth-view.d.ts} +1 -1
- package/dist/{cli-auth.js → cli/auth-view.js} +1 -1
- package/dist/cli/commands/add.d.ts +13 -0
- package/dist/cli/commands/add.js +274 -0
- package/dist/cli/commands/attach.d.ts +82 -0
- package/dist/cli/commands/attach.js +559 -0
- package/dist/cli/commands/chat.d.ts +4 -0
- package/dist/cli/commands/chat.js +21 -0
- package/dist/cli/commands/deploy.d.ts +15 -0
- package/dist/cli/commands/deploy.js +394 -0
- package/dist/cli/commands/dev.d.ts +11 -0
- package/dist/cli/commands/dev.js +82 -0
- package/dist/cli/commands/fire.d.ts +7 -0
- package/dist/cli/commands/fire.js +45 -0
- package/dist/cli/commands/info.d.ts +7 -0
- package/dist/cli/commands/info.js +108 -0
- package/dist/cli/commands/init.d.ts +8 -0
- package/dist/cli/commands/init.js +81 -0
- package/dist/cli/commands/invoke.d.ts +7 -0
- package/dist/cli/commands/invoke.js +28 -0
- package/dist/cli/commands/login.d.ts +6 -0
- package/dist/cli/commands/login.js +52 -0
- package/dist/cli/commands/models.d.ts +1 -0
- package/dist/cli/commands/models.js +15 -0
- package/dist/cli/commands/schedule.d.ts +12 -0
- package/dist/cli/commands/schedule.js +89 -0
- package/dist/cli/commands/start.d.ts +10 -0
- package/dist/cli/commands/start.js +97 -0
- package/dist/cli/commands/tool.d.ts +1 -0
- package/dist/cli/commands/tool.js +38 -0
- package/dist/cli/fail.d.ts +17 -0
- package/dist/cli/fail.js +32 -0
- package/dist/{invoke-stream.d.ts → cli/invoke-stream.d.ts} +1 -1
- package/dist/{invoke-stream.js → cli/invoke-stream.js} +4 -0
- package/dist/cli/kernel.d.ts +90 -0
- package/dist/cli/kernel.js +190 -0
- package/dist/cli/models-view.d.ts +21 -0
- package/dist/cli/models-view.js +66 -0
- package/dist/cli/program.d.ts +11 -0
- package/dist/cli/program.js +479 -0
- package/dist/cli/serve.d.ts +48 -0
- package/dist/cli/serve.js +248 -0
- package/dist/cli/shared.d.ts +44 -0
- package/dist/cli/shared.js +246 -0
- package/dist/cli.js +8 -1329
- package/dist/collect.d.ts +14 -3
- package/dist/collect.js +24 -0
- package/dist/core.d.ts +3 -1
- package/dist/core.js +2 -0
- package/dist/deploy/docker/plan.d.ts +45 -0
- package/dist/deploy/docker/plan.js +141 -0
- package/dist/deploy/docker/run.d.ts +40 -0
- package/dist/deploy/docker/run.js +126 -0
- package/dist/deploy/fly/plan.d.ts +3 -1
- package/dist/deploy/fly/plan.js +12 -7
- package/dist/deploy/fly/run.d.ts +5 -4
- package/dist/deploy/fly/run.js +16 -5
- package/dist/deploy/preflight.d.ts +10 -4
- package/dist/deploy/preflight.js +57 -12
- package/dist/deploy/railway/plan.d.ts +3 -1
- package/dist/deploy/railway/plan.js +12 -4
- package/dist/deploy/railway/run.d.ts +5 -4
- package/dist/deploy/railway/run.js +15 -5
- package/dist/deploy/runner.d.ts +6 -2
- package/dist/deploy/runner.js +1 -0
- package/dist/deploy/secrets.d.ts +2 -1
- package/dist/deploy/secrets.js +23 -3
- package/dist/dev-supervisor.d.ts +0 -2
- package/dist/dev-supervisor.js +7 -3
- package/dist/engines/pi/auth.js +160 -46
- package/dist/engines/pi/channel.d.ts +22 -16
- package/dist/engines/pi/channel.js +90 -60
- package/dist/engines/pi/chat.d.ts +4 -16
- package/dist/engines/pi/chat.js +8 -188
- package/dist/engines/pi/config.d.ts +23 -9
- package/dist/engines/pi/config.js +35 -5
- package/dist/engines/pi/create.d.ts +36 -7
- package/dist/engines/pi/create.js +63 -22
- package/dist/engines/pi/harness.d.ts +65 -1
- package/dist/engines/pi/harness.js +166 -2
- package/dist/engines/pi/invoke.d.ts +56 -3
- package/dist/engines/pi/invoke.js +340 -20
- package/dist/engines/pi/login.d.ts +11 -0
- package/dist/engines/pi/login.js +17 -5
- package/dist/engines/pi/models.d.ts +56 -10
- package/dist/engines/pi/models.js +61 -23
- package/dist/engines/pi/search-tools.d.ts +10 -0
- package/dist/engines/pi/search-tools.js +138 -0
- package/dist/engines/pi/session-builder.d.ts +16 -0
- package/dist/engines/pi/session-builder.js +308 -0
- package/dist/engines/pi/session-control.d.ts +50 -0
- package/dist/engines/pi/session-control.js +604 -0
- package/dist/engines/pi/sessions.d.ts +17 -2
- package/dist/engines/pi/sessions.js +9 -0
- package/dist/engines/pi/tool-context.d.ts +46 -11
- package/dist/engines/pi/tool-context.js +11 -9
- package/dist/engines/pi/tool.d.ts +35 -6
- package/dist/engines/pi/tool.js +47 -1
- package/dist/engines/pi/wake-tool.d.ts +0 -3
- package/dist/engines/pi/wake-tool.js +9 -7
- package/dist/engines/pi/workspace.d.ts +56 -1
- package/dist/engines/pi/workspace.js +75 -16
- package/dist/feishu.d.ts +1 -1
- package/dist/feishu.js +1 -1
- package/dist/host/node.d.ts +23 -6
- package/dist/host/node.js +5 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/lark.d.ts +1 -1
- package/dist/lark.js +1 -1
- package/dist/observe.js +3 -0
- package/dist/pi.d.ts +6 -4
- package/dist/pi.js +2 -1
- package/dist/scaffold/add-channel.d.ts +19 -6
- package/dist/scaffold/add-channel.js +127 -16
- package/dist/scaffold/templates/fastagent.config.mjs +5 -3
- package/dist/schedule/wakeups.d.ts +0 -3
- package/dist/schedule/wakeups.js +1 -1
- package/dist/session-remote.d.ts +53 -0
- package/dist/session-remote.js +336 -0
- package/dist/session.d.ts +265 -0
- package/dist/session.js +37 -0
- package/dist/slack.d.ts +2 -0
- package/dist/slack.js +2 -0
- package/dist/tunnel.d.ts +7 -4
- package/dist/tunnel.js +21 -10
- package/package.json +22 -7
- package/dist/channels/feishu/seen.d.ts +0 -5
- package/dist/channels/feishu/seen.js +0 -47
- package/dist/cli-add-feishu.d.ts +0 -8
- package/dist/cli-models.d.ts +0 -11
- package/dist/cli-models.js +0 -20
package/dist/pi.d.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
export { createPiAgent, createPiAgentFromDefinition, type CreatePiAgentFromDefinitionOptions, type CreatePiAgentOptions, } from "./engines/pi/create.ts";
|
|
2
|
-
export { defineTool, loadTools, type DefineToolOptions, type ToolCollision, type ToolContext, } from "./engines/pi/tool.ts";
|
|
2
|
+
export { defineTool, loadTools, type DefineToolOptions, type FastagentTool, type ToolCollision, type ToolContext, } from "./engines/pi/tool.ts";
|
|
3
|
+
export type { ReadonlySessionManager, ToolActivation } from "./engines/pi/tool-context.ts";
|
|
3
4
|
export { z } from "zod";
|
|
4
|
-
export type { AgentTool, ExecutionEnv, Session, Skill, SkillDiagnostic } from "@earendil-works/pi-agent-core";
|
|
5
|
+
export type { AgentTool, ExecutionEnv, Session, SessionTreeEntry, Skill, SkillDiagnostic, } from "@earendil-works/pi-agent-core";
|
|
5
6
|
export { loadChannels, type ChannelCollision } from "./engines/pi/channel.ts";
|
|
6
7
|
export { createPiAgentFromWorkspace, type CreatePiAgentFromWorkspaceOptions, } from "./engines/pi/workspace.ts";
|
|
7
8
|
export type { LoadedDefinition, SkillCollision } from "./engines/pi/definition.ts";
|
|
8
9
|
export { defineConfig, listModels, resolveModel, type FastagentConfig } from "./engines/pi/config.ts";
|
|
9
|
-
export { inProcessLease, type Lease, type Release } from "./engines/pi/invoke.ts";
|
|
10
|
+
export { inProcessLease, type Lease, type Release, type SessionObserver } from "./engines/pi/invoke.ts";
|
|
11
|
+
export { createPiSessionControl, type CreatePiSessionControlOptions, } from "./engines/pi/session-control.ts";
|
|
10
12
|
export type { AnyModel } from "./engines/pi/harness.ts";
|
|
11
|
-
export { inMemorySessionStore, jsonlSessionStore, type PiSessionStore } from "./engines/pi/sessions.ts";
|
|
13
|
+
export { inMemorySessionStore, jsonlSessionStore, type PiSessionReader, type PiSessionStore, } from "./engines/pi/sessions.ts";
|
|
12
14
|
export { GLOBAL_AUTH_PATH, fastagentCredentialStore, type FastagentAuthOptions } from "./engines/pi/auth.ts";
|
|
13
15
|
export { createPiModels, probeAuthSource, type CreatePiModelsOptions } from "./engines/pi/models.ts";
|
|
14
16
|
export type { Models } from "@earendil-works/pi-ai";
|
package/dist/pi.js
CHANGED
|
@@ -6,7 +6,8 @@ export { loadChannels } from "./engines/pi/channel.js";
|
|
|
6
6
|
export { createPiAgentFromWorkspace, } from "./engines/pi/workspace.js";
|
|
7
7
|
export { defineConfig, listModels, resolveModel } from "./engines/pi/config.js";
|
|
8
8
|
export { inProcessLease } from "./engines/pi/invoke.js";
|
|
9
|
-
export {
|
|
9
|
+
export { createPiSessionControl, } from "./engines/pi/session-control.js";
|
|
10
|
+
export { inMemorySessionStore, jsonlSessionStore, } from "./engines/pi/sessions.js";
|
|
10
11
|
export { GLOBAL_AUTH_PATH, fastagentCredentialStore } from "./engines/pi/auth.js";
|
|
11
12
|
export { createPiModels, probeAuthSource } from "./engines/pi/models.js";
|
|
12
13
|
export { createProvider } from "@earendil-works/pi-ai";
|
|
@@ -1,4 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
import type { FeishuSubscriptionMode } from "../channels/feishu/setup-mode.ts";
|
|
2
|
+
export type ChannelKind = "github" | "telegram" | "slack" | "feishu" | "lark";
|
|
3
|
+
/** Group-visibility choice shared by the slack/feishu/lark onboarding flows. Each channel keeps its
|
|
4
|
+
* own channel-level type (`SlackGroupBehavior`, `FeishuGroupBehavior`) — this is the CLI-side value. */
|
|
5
|
+
export type GroupBehavior = "context" | "mentions";
|
|
6
|
+
/** A resolved group-behavior decision plus whether the author actually chose it (flag or prompt).
|
|
7
|
+
* A defaulted "context" (non-interactive, no flag) must never drive a sensitive-scope write. */
|
|
8
|
+
export interface GroupBehaviorChoice {
|
|
9
|
+
behavior: GroupBehavior;
|
|
10
|
+
explicit: boolean;
|
|
11
|
+
}
|
|
2
12
|
/** An env var a scaffolded channel reads. `generate` = a random-string secret the CLI can pre-fill. */
|
|
3
13
|
export interface ChannelEnv {
|
|
4
14
|
name: string;
|
|
@@ -9,8 +19,8 @@ export interface ChannelEnv {
|
|
|
9
19
|
}
|
|
10
20
|
/** The channel kinds `fastagent add <kind>` can scaffold. */
|
|
11
21
|
export declare const CHANNEL_KINDS: ChannelKind[];
|
|
12
|
-
/** The env vars + next-step lines a scaffolded channel needs
|
|
13
|
-
export declare function channelSetup(kind: ChannelKind): {
|
|
22
|
+
/** The mode-specific env vars + next-step lines a scaffolded channel needs. */
|
|
23
|
+
export declare function channelSetup(kind: ChannelKind, ingress?: FeishuSubscriptionMode, groupBehavior?: GroupBehavior): {
|
|
14
24
|
env: ChannelEnv[];
|
|
15
25
|
steps: string[];
|
|
16
26
|
};
|
|
@@ -19,7 +29,7 @@ export declare function channelSetup(kind: ChannelKind): {
|
|
|
19
29
|
* copies it to `.env` finds the vars already there. No-op when there is no `.env.example` or the block
|
|
20
30
|
* is already present. Placeholders only — no real secret lands in the committable template.
|
|
21
31
|
*/
|
|
22
|
-
export declare function appendChannelEnv(dir: string, kind: ChannelKind): Promise<boolean>;
|
|
32
|
+
export declare function appendChannelEnv(dir: string, kind: ChannelKind, ingress?: FeishuSubscriptionMode): Promise<boolean>;
|
|
23
33
|
export interface DotEnvWriteResult {
|
|
24
34
|
/** Generated secret vars written as active `KEY=value` lines. */
|
|
25
35
|
written: string[];
|
|
@@ -34,14 +44,17 @@ export interface DotEnvWriteResult {
|
|
|
34
44
|
* (e.g. TELEGRAM_BOT_TOKEN from BotFather) are added only as commented placeholders, so the file is
|
|
35
45
|
* ready to edit while no fake secret is committed to the user's mental model.
|
|
36
46
|
*/
|
|
37
|
-
export declare function appendChannelDotEnv(dir: string, kind: ChannelKind, generated: Record<string, string>, overwrite?: readonly string[]): Promise<DotEnvWriteResult>;
|
|
47
|
+
export declare function appendChannelDotEnv(dir: string, kind: ChannelKind, generated: Record<string, string>, overwrite?: readonly string[], ingress?: FeishuSubscriptionMode): Promise<DotEnvWriteResult>;
|
|
38
48
|
/** Whether a channel file already exists — checked before any mutation, so a no-clobber re-add is side-effect-free. */
|
|
39
49
|
export declare function channelExists(dir: string, kind: ChannelKind): Promise<boolean>;
|
|
40
50
|
/**
|
|
41
51
|
* Scaffold `channels/<kind>.ts` into {@link dir}. Never clobbers an existing file (the glue is
|
|
42
52
|
* authored content). The wx write is the TOCTOU safety net behind {@link channelExists}.
|
|
43
53
|
*/
|
|
44
|
-
export declare function scaffoldChannel(dir: string, kind: ChannelKind
|
|
54
|
+
export declare function scaffoldChannel(dir: string, kind: ChannelKind, options?: {
|
|
55
|
+
ingress?: FeishuSubscriptionMode;
|
|
56
|
+
groupBehavior?: GroupBehavior;
|
|
57
|
+
}): Promise<string>;
|
|
45
58
|
/**
|
|
46
59
|
* Verify the workspace is ready to host a channel: an ESM package.json that declares
|
|
47
60
|
* `@fastagent-sh/fastagent` (the channel file imports it). `add` checks and guides, never bootstraps — that
|
|
@@ -43,11 +43,39 @@ const CHANNEL_SCAFFOLDS = {
|
|
|
43
43
|
"the agent can send messages or files back by calling the scaffolded {tools}/telegram-send.ts tool",
|
|
44
44
|
],
|
|
45
45
|
},
|
|
46
|
+
slack: {
|
|
47
|
+
env: [
|
|
48
|
+
{ name: "SLACK_BOT_TOKEN", hint: "Slack app → rotating Bot User OAuth access token", required: true },
|
|
49
|
+
{
|
|
50
|
+
name: "SLACK_BOT_REFRESH_TOKEN",
|
|
51
|
+
hint: "Slack OAuth bot refresh token (required when token rotation is enabled)",
|
|
52
|
+
required: false,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: "SLACK_BOT_TOKEN_EXPIRES_AT",
|
|
56
|
+
hint: "Slack rotating bot access-token expiry (epoch milliseconds)",
|
|
57
|
+
required: false,
|
|
58
|
+
},
|
|
59
|
+
{ name: "SLACK_CLIENT_ID", hint: "Slack app OAuth client ID (for bot-token rotation)", required: false },
|
|
60
|
+
{
|
|
61
|
+
name: "SLACK_CLIENT_SECRET",
|
|
62
|
+
hint: "Slack app OAuth client secret (for bot-token rotation)",
|
|
63
|
+
required: false,
|
|
64
|
+
},
|
|
65
|
+
{ name: "SLACK_SIGNING_SECRET", hint: "Slack app → Basic Information → App Credentials", required: true },
|
|
66
|
+
],
|
|
67
|
+
steps: [
|
|
68
|
+
"Slack Bot Token Scopes: app_mentions:read, assistant:write, chat:write, im:history, files:read, files:write, channels:history, groups:history, mpim:history",
|
|
69
|
+
"enable Agents (agent_view) and token rotation; subscribe app_home_opened, app_context_changed, app_mention, message.im, message.channels, message.groups, message.mpim; set Request URL to <public-url>/slack",
|
|
70
|
+
"reinstall the app after changing scopes, then invite it to each channel it should read",
|
|
71
|
+
"the agent can send messages or files by calling the scaffolded {tools}/slack-send.ts tool",
|
|
72
|
+
],
|
|
73
|
+
},
|
|
46
74
|
// Feishu is the canonical engine/cloud; Lark international reuses its protocol through a degraded
|
|
47
75
|
// compatibility profile. Each remains its own channel KIND: route, env, state, console, onboarding.
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
//
|
|
76
|
+
// This table is the webhook setup; continuous mode below selects only App ID/Secret. No `generate`
|
|
77
|
+
// in either: values come FROM the platform. `add feishu` scan-creates the app; Lark lacks that
|
|
78
|
+
// control-plane capability, so `add lark` guides console credential collection.
|
|
51
79
|
feishu: {
|
|
52
80
|
env: [
|
|
53
81
|
{
|
|
@@ -72,7 +100,8 @@ const CHANNEL_SCAFFOLDS = {
|
|
|
72
100
|
},
|
|
73
101
|
],
|
|
74
102
|
steps: [
|
|
75
|
-
"
|
|
103
|
+
"before publishing: approve the sensitive im:message.group_msg permission for context-aware groups (the CLI adds it to the app draft when supported); it delivers all group messages so bare managed-thread replies can invoke and other unsummoned discussion can buffer",
|
|
104
|
+
"PUBLISH the app version in the developer console after permission approval — the switch to webhook mode takes effect on publish (one click, once ever; no API for it)",
|
|
76
105
|
"edit {channel} — routing policy (the header walks through the console setup, for hand-made apps)",
|
|
77
106
|
"the event Request URL is auto-registered by `dev --tunnel` / `deploy --run`",
|
|
78
107
|
"the agent can push messages from scheduled turns via the scaffolded {tools}/feishu-send.ts tool",
|
|
@@ -94,7 +123,8 @@ const CHANNEL_SCAFFOLDS = {
|
|
|
94
123
|
},
|
|
95
124
|
],
|
|
96
125
|
steps: [
|
|
97
|
-
"finish the console setup: enable Bot and add the permissions + im.message.receive_v1 event listed in {channel} (do not publish yet)",
|
|
126
|
+
"finish the console setup: enable Bot and add the required permissions + im.message.receive_v1 event listed in {channel} (do not publish yet)",
|
|
127
|
+
"before publishing: approve the sensitive im:message.group_msg permission for context-aware groups (add it manually if Lark's config API fallback was used); it delivers all group messages so bare managed-thread replies can invoke and other unsummoned discussion can buffer",
|
|
98
128
|
"run `fastagent dev --tunnel` and keep it running; if auto-registration reports a config-API 404, manually switch Subscription mode to webhook, set its printed https://…/lark Request URL, save, then create + publish a version",
|
|
99
129
|
"the agent can push messages from scheduled turns via the scaffolded {tools}/lark-send.ts tool",
|
|
100
130
|
],
|
|
@@ -102,17 +132,61 @@ const CHANNEL_SCAFFOLDS = {
|
|
|
102
132
|
};
|
|
103
133
|
/** The channel kinds `fastagent add <kind>` can scaffold. */
|
|
104
134
|
export const CHANNEL_KINDS = Object.keys(CHANNEL_SCAFFOLDS);
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
135
|
+
const WEBSOCKET_SETUPS = {
|
|
136
|
+
feishu: {
|
|
137
|
+
env: CHANNEL_SCAFFOLDS.feishu.env.filter((entry) => ["FEISHU_APP_ID", "FEISHU_APP_SECRET"].includes(entry.name)),
|
|
138
|
+
steps: [
|
|
139
|
+
"before publishing: approve the sensitive im:message.group_msg permission for context-aware groups (the CLI adds it to the app draft when supported); it delivers all group messages so bare managed-thread replies can invoke and other unsummoned discussion can buffer",
|
|
140
|
+
"PUBLISH the app version in the developer console after permission approval — long-connection event subscriptions become active with the published version",
|
|
141
|
+
"edit {channel} — routing policy (the scaffold is already set to WebSocket ingress)",
|
|
142
|
+
"run `fastagent dev` without --tunnel; deployments must keep one process running (no scale-to-zero)",
|
|
143
|
+
"the agent can push messages from scheduled turns via the scaffolded {tools}/feishu-send.ts tool",
|
|
144
|
+
],
|
|
145
|
+
},
|
|
146
|
+
lark: {
|
|
147
|
+
env: CHANNEL_SCAFFOLDS.lark.env.filter((entry) => ["LARK_APP_ID", "LARK_APP_SECRET"].includes(entry.name)),
|
|
148
|
+
steps: [
|
|
149
|
+
"before publishing: approve the sensitive im:message.group_msg permission for context-aware groups (add it manually if Lark's config API fallback was used); it delivers all group messages so bare managed-thread replies can invoke and other unsummoned discussion can buffer",
|
|
150
|
+
"in Events & Callbacks choose long connection, subscribe im.message.receive_v1, then create + publish a version",
|
|
151
|
+
"edit {channel} — routing policy (the scaffold is already set to WebSocket ingress)",
|
|
152
|
+
"run `fastagent dev` without --tunnel; deployments must keep one process running (no scale-to-zero)",
|
|
153
|
+
"the agent can push messages from scheduled turns via the scaffolded {tools}/lark-send.ts tool",
|
|
154
|
+
],
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
/** The mode-specific env vars + next-step lines a scaffolded channel needs. */
|
|
158
|
+
export function channelSetup(kind, ingress = "webhook", groupBehavior) {
|
|
159
|
+
const behavior = groupBehavior ?? "context";
|
|
160
|
+
const setup = ingress === "websocket" && (kind === "feishu" || kind === "lark")
|
|
161
|
+
? WEBSOCKET_SETUPS[kind]
|
|
162
|
+
: CHANNEL_SCAFFOLDS[kind];
|
|
163
|
+
if ((kind === "feishu" || kind === "lark") && behavior === "mentions") {
|
|
164
|
+
return {
|
|
165
|
+
env: setup.env,
|
|
166
|
+
steps: setup.steps.map((step) => step.includes("im:message.group_msg")
|
|
167
|
+
? "group behavior: mention-only — do not grant im:message.group_msg; bare managed-thread replies and group context buffering remain disabled"
|
|
168
|
+
: step),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
if (kind === "slack" && behavior === "mentions") {
|
|
172
|
+
return {
|
|
173
|
+
env: setup.env,
|
|
174
|
+
steps: [
|
|
175
|
+
"Slack Bot Token Scopes: app_mentions:read, assistant:write, chat:write, im:history, files:read, files:write (no channel/group/mpim history scopes)",
|
|
176
|
+
"enable Agents (agent_view) and token rotation; subscribe app_home_opened, app_context_changed, app_mention, and message.im; set Request URL to <public-url>/slack",
|
|
177
|
+
"group behavior: mention-only — bare managed-thread replies and unsummoned group context remain disabled",
|
|
178
|
+
...setup.steps.slice(2),
|
|
179
|
+
],
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
return { env: setup.env, steps: setup.steps };
|
|
109
183
|
}
|
|
110
184
|
/**
|
|
111
185
|
* Append a channel's env vars (commented placeholders + hints) to `.env.example`, so a developer who
|
|
112
186
|
* copies it to `.env` finds the vars already there. No-op when there is no `.env.example` or the block
|
|
113
187
|
* is already present. Placeholders only — no real secret lands in the committable template.
|
|
114
188
|
*/
|
|
115
|
-
export async function appendChannelEnv(dir, kind) {
|
|
189
|
+
export async function appendChannelEnv(dir, kind, ingress = "webhook") {
|
|
116
190
|
const file = join(dir, ".env.example");
|
|
117
191
|
let current;
|
|
118
192
|
try {
|
|
@@ -129,7 +203,9 @@ export async function appendChannelEnv(dir, kind) {
|
|
|
129
203
|
// Hint on its OWN line above the placeholder (like the base env.example template) — never inline
|
|
130
204
|
// after `=`: loadEnvFile does not strip trailing comments, so an uncommented `KEY= # hint` (or a
|
|
131
205
|
// value pasted before the `#`) would carry the hint text into the parsed value.
|
|
132
|
-
const block = `\n${marker}\n${
|
|
206
|
+
const block = `\n${marker}\n${channelSetup(kind, ingress)
|
|
207
|
+
.env.map((e) => `# ${e.hint}\n# ${e.name}=`)
|
|
208
|
+
.join("\n")}\n`;
|
|
133
209
|
await appendFile(file, block);
|
|
134
210
|
return true;
|
|
135
211
|
}
|
|
@@ -151,7 +227,7 @@ function mentionsEnvName(content, name) {
|
|
|
151
227
|
* (e.g. TELEGRAM_BOT_TOKEN from BotFather) are added only as commented placeholders, so the file is
|
|
152
228
|
* ready to edit while no fake secret is committed to the user's mental model.
|
|
153
229
|
*/
|
|
154
|
-
export async function appendChannelDotEnv(dir, kind, generated, overwrite = []) {
|
|
230
|
+
export async function appendChannelDotEnv(dir, kind, generated, overwrite = [], ingress = "webhook") {
|
|
155
231
|
const file = join(dir, ".env");
|
|
156
232
|
let current = "";
|
|
157
233
|
try {
|
|
@@ -161,14 +237,15 @@ export async function appendChannelDotEnv(dir, kind, generated, overwrite = [])
|
|
|
161
237
|
if (e.code !== "ENOENT")
|
|
162
238
|
throw e;
|
|
163
239
|
}
|
|
164
|
-
const
|
|
240
|
+
const env = channelSetup(kind, ingress).env;
|
|
241
|
+
const alreadySet = env
|
|
165
242
|
.filter((e) => !overwrite.includes(e.name) && hasActiveEnvValue(current, e.name))
|
|
166
243
|
.map((e) => e.name);
|
|
167
244
|
const lines = [];
|
|
168
245
|
const written = [];
|
|
169
246
|
const contentLines = current.split("\n");
|
|
170
247
|
let replacedInPlace = false;
|
|
171
|
-
for (const e of
|
|
248
|
+
for (const e of env) {
|
|
172
249
|
if (alreadySet.includes(e.name))
|
|
173
250
|
continue;
|
|
174
251
|
const value = generated[e.name];
|
|
@@ -228,7 +305,7 @@ export async function channelExists(dir, kind) {
|
|
|
228
305
|
* Scaffold `channels/<kind>.ts` into {@link dir}. Never clobbers an existing file (the glue is
|
|
229
306
|
* authored content). The wx write is the TOCTOU safety net behind {@link channelExists}.
|
|
230
307
|
*/
|
|
231
|
-
export async function scaffoldChannel(dir, kind) {
|
|
308
|
+
export async function scaffoldChannel(dir, kind, options = {}) {
|
|
232
309
|
const channelsDir = join(dir, "channels");
|
|
233
310
|
// Don't write through a channels/ symlink that escapes the workspace; an in-workspace one is fine.
|
|
234
311
|
await assertInsideWorkspace(dir, "channels");
|
|
@@ -240,8 +317,42 @@ export async function scaffoldChannel(dir, kind) {
|
|
|
240
317
|
// `channel.ts` is THE adapter (→ channels/<kind>.ts); any other .ts in the bundle is a companion tool
|
|
241
318
|
// (→ tools/<name>, never clobbering an authored one).
|
|
242
319
|
for (const name of channelBundleFiles(kind)) {
|
|
243
|
-
|
|
320
|
+
let content = channelTemplate(kind, name);
|
|
244
321
|
if (name === "channel.ts") {
|
|
322
|
+
if ((kind === "feishu" || kind === "lark") && options.ingress === "websocket") {
|
|
323
|
+
const factory = `${kind}Channel`;
|
|
324
|
+
const wsFactory = `${kind}WebSocketChannel`;
|
|
325
|
+
let configured = content
|
|
326
|
+
.replace(`import { ${factory} }`, `import { ${wsFactory} }`)
|
|
327
|
+
.replace(`export default ${factory}({`, `export default ${wsFactory}({`);
|
|
328
|
+
if (configured === content)
|
|
329
|
+
throw new Error(`${kind} channel template has no factory anchors`);
|
|
330
|
+
const prefix = kind === "feishu" ? "FEISHU" : "LARK";
|
|
331
|
+
const exportAt = configured.indexOf("export default");
|
|
332
|
+
const importEnd = configured.indexOf("\n\n");
|
|
333
|
+
if (exportAt < 0 || importEnd < 0)
|
|
334
|
+
throw new Error(`${kind} channel template header anchors are missing`);
|
|
335
|
+
const brand = kind === "feishu" ? "Feishu" : "Lark";
|
|
336
|
+
configured =
|
|
337
|
+
`${configured.slice(0, importEnd)}\n\n` +
|
|
338
|
+
`// ${brand} WebSocket long connection: the process connects OUT to the platform, so no public URL,\n` +
|
|
339
|
+
`// Verification Token, Encrypt Key, or --tunnel is needed. In Events & Callbacks choose long\n` +
|
|
340
|
+
`// connection, subscribe im.message.receive_v1, then publish the app version. Keep one process\n` +
|
|
341
|
+
`// running in production: scale-to-zero/App Sleeping would disconnect ingress.\n` +
|
|
342
|
+
configured.slice(exportAt);
|
|
343
|
+
configured = configured
|
|
344
|
+
.split("\n")
|
|
345
|
+
.filter((line) => !line.includes(`verificationToken: process.env.${prefix}_VERIFICATION_TOKEN`) &&
|
|
346
|
+
!line.includes(`encryptKey: process.env.${prefix}_ENCRYPT_KEY`))
|
|
347
|
+
.join("\n");
|
|
348
|
+
content = configured;
|
|
349
|
+
}
|
|
350
|
+
if (kind === "slack" && options.groupBehavior === "mentions") {
|
|
351
|
+
const configured = content.replace('groupBehavior: "context"', 'groupBehavior: "mentions"');
|
|
352
|
+
if (configured === content)
|
|
353
|
+
throw new Error("slack channel template has no groupBehavior anchor");
|
|
354
|
+
content = configured;
|
|
355
|
+
}
|
|
245
356
|
await writeFile(file, content, { flag: "wx" });
|
|
246
357
|
continue;
|
|
247
358
|
}
|
|
@@ -2,14 +2,16 @@
|
|
|
2
2
|
// Your agent's identity lives in persona.md; its capabilities in skills/ + tools/ — never here.
|
|
3
3
|
// An AGENTS.md at the workspace root (yours or the host repo's) is read as project context.
|
|
4
4
|
// Model precedence: `--model` flag > FASTAGENT_MODEL env > this default.
|
|
5
|
-
// No model is preset: `fastagent dev`
|
|
6
|
-
//
|
|
7
|
-
//
|
|
5
|
+
// No model is preset: `fastagent dev` shows the full model catalog (models you already have
|
|
6
|
+
// credentials for come first; picking one that needs auth logs you in inline) and writes your choice
|
|
7
|
+
// below. Or set it by hand to a "provider/modelId" (`fastagent models` lists them).
|
|
8
8
|
export default {
|
|
9
9
|
// model: "openai-codex/gpt-5.5",
|
|
10
|
+
// thinkingLevel: "high", // reasoning effort (off|minimal|low|medium|high|xhigh|max); default "medium" (pi TUI parity)
|
|
10
11
|
http: { port: 8787 },
|
|
11
12
|
// selfSchedule: true, // mount the built-in `wake` tool: the agent schedules its own follow-up turns
|
|
12
13
|
// // ("check the deploy in 10 min"). Cron jobs need no opt-in — drop a schedules/<name>.ts.
|
|
14
|
+
// sessionControl: true, // serve /control/* for remote observation + steering (fastagent attach / Web panel)
|
|
13
15
|
// deploy: what the agent needs on the box (so `fastagent deploy` doesn't need a hand-written Dockerfile
|
|
14
16
|
// or hand-set host variables). Uncomment as needed:
|
|
15
17
|
// deploy: {
|
|
@@ -26,9 +26,6 @@ export declare const MAX_PENDING_WAKEUPS = 20;
|
|
|
26
26
|
* wake into an active conversation fires in the first gap between the user's turns; this generous
|
|
27
27
|
* ceiling (~1h) only gives up on a pathologically stuck session (then logs, operator-visible). */
|
|
28
28
|
export declare const MAX_WAKE_ATTEMPTS = 120;
|
|
29
|
-
/** The minimum gap between two consecutive fires of a RECURRING wake — stricter than the one-shot floor:
|
|
30
|
-
* a recurring runs forever, so a tight cron is a permanent token burner, not a one-time mistake. */
|
|
31
|
-
export declare const MIN_RECURRING_GAP_MS: number;
|
|
32
29
|
/** The current pending wake-ups (`fastagent schedule list` uses this). */
|
|
33
30
|
export declare function listWakeups(stateRoot: string): Wakeup[];
|
|
34
31
|
export type AddWakeupResult = {
|
package/dist/schedule/wakeups.js
CHANGED
|
@@ -26,7 +26,7 @@ export const MAX_PENDING_WAKEUPS = 20;
|
|
|
26
26
|
export const MAX_WAKE_ATTEMPTS = 120;
|
|
27
27
|
/** The minimum gap between two consecutive fires of a RECURRING wake — stricter than the one-shot floor:
|
|
28
28
|
* a recurring runs forever, so a tight cron is a permanent token burner, not a one-time mistake. */
|
|
29
|
-
|
|
29
|
+
const MIN_RECURRING_GAP_MS = 10 * 60_000; // 10 minutes
|
|
30
30
|
/** A stored entry is a real Wakeup: the fields are present and `fireAt` is a parseable date. A malformed
|
|
31
31
|
* one (bad/missing fireAt) would compare NaN <= now = false forever — never due, never cleared, but still
|
|
32
32
|
* eating the pending quota. So validate at this IO boundary and drop it (warn), like a corrupt file. */
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The remote `SessionControl` — the client half of the Phase 3 transport (design §13). Engine- and
|
|
3
|
+
* server-neutral: speaks only the wire protocol `controlRoutes` serves (HTTP JSON + SSE with the
|
|
4
|
+
* {sessionId, epoch, seq, event} envelope) and re-exposes the SAME `SessionControl` interface, so
|
|
5
|
+
* local and remote consumers are isomorphic — client code does not change when the agent moves out
|
|
6
|
+
* of process.
|
|
7
|
+
*
|
|
8
|
+
* Envelope consumption is internal: a seq gap (loss in transit on this connection) — and any
|
|
9
|
+
* mid-stream transport failure, a server restart included (its connections drop) — THROWS from
|
|
10
|
+
* the events iterator, so the consumer's failure handling and budget own it; only the consumer's
|
|
11
|
+
* own detach reads as a clean end. Recovery is the standard reconnect steps (`entries({ since })`
|
|
12
|
+
* → `state()` → resubscribe), exactly as after any disconnect. The envelope's `epoch` is
|
|
13
|
+
* informational for consumers that correlate ACROSS connections — within one connection it cannot
|
|
14
|
+
* change, so this client does not compare it. Nothing here retries silently: a broken stream is
|
|
15
|
+
* visible as a thrown iteration error, a failed request as a rejected promise.
|
|
16
|
+
*/
|
|
17
|
+
import type { Agent } from "./agent.ts";
|
|
18
|
+
import type { SessionControl } from "./session.ts";
|
|
19
|
+
/** A control request the server answered with a non-2xx status. Carries the STRUCTURED status so a
|
|
20
|
+
* consumer distinguishing auth failure (401 — stale token, unrecoverable) from transient transport
|
|
21
|
+
* trouble branches on `status`, never on message prose. */
|
|
22
|
+
export declare class ControlRequestError extends Error {
|
|
23
|
+
readonly status: number;
|
|
24
|
+
constructor(status: number, body: string);
|
|
25
|
+
}
|
|
26
|
+
/** Connection parameters shared by BOTH remote planes (`connectSessionControl` and
|
|
27
|
+
* `connectAgent`) — plane-neutral on purpose: one endpoint, one token, two contracts. */
|
|
28
|
+
export interface RemoteEndpointOptions {
|
|
29
|
+
/** Base URL of the serving process (e.g. `http://127.0.0.1:8787`); `/control/*` is appended. */
|
|
30
|
+
url: string;
|
|
31
|
+
/** The shared bearer secret (`<stateRoot>/control.json` on the serving machine). */
|
|
32
|
+
token: string;
|
|
33
|
+
/** Injectable for tests. Defaults to global fetch. */
|
|
34
|
+
fetchFn?: typeof fetch;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Connect and return a remote `SessionControl`. Async because `capabilities()` is synchronous in
|
|
38
|
+
* the contract: the static declaration is fetched ONCE here and served from memory — which also
|
|
39
|
+
* makes a wrong URL/token fail at connect time, not on first use.
|
|
40
|
+
*/
|
|
41
|
+
export declare function connectSessionControl(options: RemoteEndpointOptions): Promise<SessionControl>;
|
|
42
|
+
/**
|
|
43
|
+
* The remote DATA plane: an `Agent` whose `invoke` drives `POST /control/invoke` on a serving
|
|
44
|
+
* process — paired with {@link connectSessionControl}, a client holds a full remote fastagent
|
|
45
|
+
* instance through the same two contracts local code uses. A REAL Agent, failure discipline
|
|
46
|
+
* included: SPEC MUST 2 forbids iteration throws, so every failure — transport (401/refused/
|
|
47
|
+
* dropped mid-stream), protocol, and the images precheck — becomes a terminal `failed` event
|
|
48
|
+
* (`retryable` from the HTTP status where one exists; network trouble is retryable). Breaking out
|
|
49
|
+
* of iteration disconnects the request, which cancels the run (SPEC cancellation semantics travel
|
|
50
|
+
* the wire). The invoke wire is text-only for now: a prompt with images fails visibly instead of
|
|
51
|
+
* silently dropping them (steer/follow_up on the control plane carry full Prompts).
|
|
52
|
+
*/
|
|
53
|
+
export declare function connectAgent(options: RemoteEndpointOptions): Agent;
|