experimental-ash 0.36.0 → 0.37.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/CHANGELOG.md +10 -0
- package/README.md +7 -7
- package/dist/docs/public/README.md +10 -10
- package/dist/docs/public/{auth-and-route-protection.md → advanced/auth-and-route-protection.md} +1 -0
- package/dist/docs/public/{cli-build-and-debugging.md → advanced/cli-build-and-debugging.md} +1 -0
- package/dist/docs/public/{context-control.md → advanced/context-control.md} +1 -0
- package/dist/docs/public/{evals.md → advanced/evals.md} +1 -0
- package/dist/docs/public/{faqs.md → advanced/faqs.md} +1 -0
- package/dist/docs/public/{hooks.md → advanced/hooks.md} +1 -0
- package/dist/docs/public/{instrumentation.md → advanced/instrumentation.md} +1 -0
- package/dist/docs/public/advanced/meta.json +19 -0
- package/dist/docs/public/{project-layout.md → advanced/project-layout.md} +1 -0
- package/dist/docs/public/{runs-and-streaming.md → advanced/runs-and-streaming.md} +1 -0
- package/dist/docs/public/{session-context.md → advanced/session-context.md} +1 -0
- package/dist/docs/public/{typescript-api.md → advanced/typescript-api.md} +1 -0
- package/dist/docs/public/{vercel-deployment.md → advanced/vercel-deployment.md} +1 -0
- package/dist/docs/public/{workspace.md → advanced/workspace.md} +1 -0
- package/dist/docs/public/meta.json +1 -14
- package/dist/src/context/accessors.d.ts +0 -25
- package/dist/src/context/accessors.js +1 -1
- package/dist/src/context/build-callback-context.js +1 -1
- package/dist/src/context/keys.d.ts +2 -2
- package/dist/src/evals/define-eval-suite.d.ts +3 -2
- package/dist/src/evals/types.d.ts +38 -12
- package/dist/src/execution/skills/types.d.ts +1 -1
- package/dist/src/harness/step-hooks.d.ts +1 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/packages/ash-scaffold/src/channels.js +1 -1
- package/dist/src/public/channels/discord/defaults.js +2 -2
- package/dist/src/public/channels/discord/discordChannel.d.ts +4 -4
- package/dist/src/public/channels/discord/discordChannel.js +1 -1
- package/dist/src/public/channels/index.d.ts +1 -1
- package/dist/src/public/channels/slack/defaults.js +4 -4
- package/dist/src/public/channels/slack/slackChannel.d.ts +4 -4
- package/dist/src/public/channels/slack/slackChannel.js +1 -1
- package/dist/src/public/channels/teams/defaults.js +3 -3
- package/dist/src/public/channels/teams/teamsChannel.d.ts +4 -4
- package/dist/src/public/channels/teams/teamsChannel.js +1 -1
- package/dist/src/public/channels/telegram/defaults.js +2 -2
- package/dist/src/public/channels/telegram/telegramChannel.d.ts +4 -4
- package/dist/src/public/channels/telegram/telegramChannel.js +1 -1
- package/dist/src/public/channels/twilio/defaults.js +2 -2
- package/dist/src/public/channels/twilio/twilioChannel.d.ts +4 -4
- package/dist/src/public/channels/twilio/twilioChannel.js +1 -1
- package/dist/src/public/definitions/agent.d.ts +2 -1
- package/dist/src/public/definitions/defineChannel.d.ts +16 -4
- package/dist/src/public/definitions/defineChannel.js +1 -1
- package/dist/src/public/definitions/exact.d.ts +7 -0
- package/dist/src/public/definitions/exact.js +1 -0
- package/dist/src/public/definitions/hook.d.ts +2 -1
- package/dist/src/public/definitions/instructions.d.ts +2 -1
- package/dist/src/public/definitions/instrumentation.d.ts +2 -1
- package/dist/src/public/definitions/schedule.d.ts +12 -2
- package/dist/src/public/definitions/skill.d.ts +2 -1
- package/dist/src/runtime/framework-tools/skill.js +1 -1
- package/dist/src/runtime/governance/auth/types.d.ts +1 -1
- package/dist/src/sandbox/state.d.ts +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# experimental-ash
|
|
2
2
|
|
|
3
|
+
## 0.37.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 25ef58e: Reject extra top-level fields passed to core `define*` helpers while preserving literal inference for valid definitions. TypeScript now also enforces documented exclusivity for schedules (`markdown` or `run`) and eval suites (`load` or `cases`, `task.prompt` or `task.messages`).
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- 8b678cc: Channel event handlers now receive `AshCallbackContext` fields — `ctx.session` (with `turn`/`parent`), `ctx.getSandbox()`, and `ctx.getSkill()`. Channel-specific `continuationToken` and `setContinuationToken` are available at the top level of the event context.
|
|
12
|
+
|
|
3
13
|
## 0.36.0
|
|
4
14
|
|
|
5
15
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -61,7 +61,7 @@ Runtime accessors live on the subpath that owns the concern:
|
|
|
61
61
|
- `getSkill(identifier)` — handle for a named skill visible to the current agent (`experimental-ash/skills`)
|
|
62
62
|
- `getContext(key)`, `requireContext(key)`, `hasContext(key)`, `setContext(key)`, `ensureContext(key, factory)` — unified context helpers (`experimental-ash/context`)
|
|
63
63
|
|
|
64
|
-
The complete API reference, including types and lower-level runtime primitives, is in [`./dist/docs/public/typescript-api.md`](./dist/docs/public/typescript-api.md).
|
|
64
|
+
The complete API reference, including types and lower-level runtime primitives, is in [`./dist/docs/public/advanced/typescript-api.md`](./dist/docs/public/advanced/typescript-api.md).
|
|
65
65
|
|
|
66
66
|
## Tiny Example
|
|
67
67
|
|
|
@@ -121,7 +121,7 @@ CLI commands:
|
|
|
121
121
|
|
|
122
122
|
## Deploying
|
|
123
123
|
|
|
124
|
-
Ash is built for Vercel. The runtime is Nitro + Vercel Workflows. Read [`./dist/docs/public/vercel-deployment.md`](./dist/docs/public/vercel-deployment.md) for the deployment path, environment variables, and Vercel-specific configuration.
|
|
124
|
+
Ash is built for Vercel. The runtime is Nitro + Vercel Workflows. Read [`./dist/docs/public/advanced/vercel-deployment.md`](./dist/docs/public/advanced/vercel-deployment.md) for the deployment path, environment variables, and Vercel-specific configuration.
|
|
125
125
|
|
|
126
126
|
## Read Next
|
|
127
127
|
|
|
@@ -129,14 +129,14 @@ These files ship inside the installed package at `node_modules/experimental-ash/
|
|
|
129
129
|
|
|
130
130
|
- [Full docs index](./dist/docs/public/README.md) — recommended entry point
|
|
131
131
|
- [Getting Started](./dist/docs/public/getting-started.md) — install, scaffold, and run locally
|
|
132
|
-
- [Project Layout](./dist/docs/public/project-layout.md) — every authored directory in depth
|
|
132
|
+
- [Project Layout](./dist/docs/public/advanced/project-layout.md) — every authored directory in depth
|
|
133
133
|
- [`agent.ts`](./dist/docs/public/agent-ts.md) — agent config reference
|
|
134
|
-
- [TypeScript API](./dist/docs/public/typescript-api.md) — complete `define*` and runtime helper reference
|
|
135
|
-
- [Vercel Deployment](./dist/docs/public/vercel-deployment.md) — deploy to production
|
|
134
|
+
- [TypeScript API](./dist/docs/public/advanced/typescript-api.md) — complete `define*` and runtime helper reference
|
|
135
|
+
- [Vercel Deployment](./dist/docs/public/advanced/vercel-deployment.md) — deploy to production
|
|
136
136
|
|
|
137
|
-
By authoring concern: [Tools](./dist/docs/public/tools.md) · [Channels](./dist/docs/public/channels/README.md) · [Hooks](./dist/docs/public/hooks.md) · [Skills](./dist/docs/public/skills.md) · [Sandbox](./dist/docs/public/sandbox.md) · [Workspace](./dist/docs/public/workspace.md) · [Connections](./dist/docs/public/connections.md) · [Subagents](./dist/docs/public/subagents.md) · [Schedules](./dist/docs/public/schedules.md) · [Human In The Loop](./dist/docs/public/human-in-the-loop.md) · [Evals](./dist/docs/public/evals.md)
|
|
137
|
+
By authoring concern: [Tools](./dist/docs/public/tools.md) · [Channels](./dist/docs/public/channels/README.md) · [Hooks](./dist/docs/public/advanced/hooks.md) · [Skills](./dist/docs/public/skills.md) · [Sandbox](./dist/docs/public/sandbox.md) · [Workspace](./dist/docs/public/advanced/workspace.md) · [Connections](./dist/docs/public/connections.md) · [Subagents](./dist/docs/public/subagents.md) · [Schedules](./dist/docs/public/schedules.md) · [Human In The Loop](./dist/docs/public/human-in-the-loop.md) · [Evals](./dist/docs/public/advanced/evals.md)
|
|
138
138
|
|
|
139
|
-
By runtime concern: [Sessions and Streaming](./dist/docs/public/runs-and-streaming.md) · [Session Context](./dist/docs/public/session-context.md) · [Context Control](./dist/docs/public/context-control.md) · [Auth and Route Protection](./dist/docs/public/auth-and-route-protection.md) · [CLI, Build, and Debugging](./dist/docs/public/cli-build-and-debugging.md) · [Instrumentation](./dist/docs/public/instrumentation.md)
|
|
139
|
+
By runtime concern: [Sessions and Streaming](./dist/docs/public/advanced/runs-and-streaming.md) · [Session Context](./dist/docs/public/advanced/session-context.md) · [Context Control](./dist/docs/public/advanced/context-control.md) · [Auth and Route Protection](./dist/docs/public/advanced/auth-and-route-protection.md) · [CLI, Build, and Debugging](./dist/docs/public/advanced/cli-build-and-debugging.md) · [Instrumentation](./dist/docs/public/advanced/instrumentation.md)
|
|
140
140
|
|
|
141
141
|
## Architecture (Internals)
|
|
142
142
|
|
|
@@ -16,25 +16,25 @@ Important naming note:
|
|
|
16
16
|
Read in this order:
|
|
17
17
|
|
|
18
18
|
1. [Getting Started](./getting-started.md)
|
|
19
|
-
2. [Project Layout](./project-layout.md)
|
|
19
|
+
2. [Project Layout](./advanced/project-layout.md)
|
|
20
20
|
3. [`agent.ts`](./agent-ts.md)
|
|
21
|
-
4. [TypeScript API](./typescript-api.md)
|
|
22
|
-
5. [Context Control](./context-control.md)
|
|
21
|
+
4. [TypeScript API](./advanced/typescript-api.md)
|
|
22
|
+
5. [Context Control](./advanced/context-control.md)
|
|
23
23
|
6. [Skills](./skills.md)
|
|
24
24
|
7. [Tools](./tools.md)
|
|
25
25
|
8. [Connections](./connections.md)
|
|
26
|
-
9. [Workspace](./workspace.md)
|
|
26
|
+
9. [Workspace](./advanced/workspace.md)
|
|
27
27
|
10. [Sandboxes](./sandbox.md)
|
|
28
28
|
11. [Channels](./channels/README.md)
|
|
29
29
|
12. [Human In The Loop](./human-in-the-loop.md)
|
|
30
|
-
13. [Session Context](./session-context.md)
|
|
31
|
-
14. [Sessions And Streaming](./runs-and-streaming.md)
|
|
30
|
+
13. [Session Context](./advanced/session-context.md)
|
|
31
|
+
14. [Sessions And Streaming](./advanced/runs-and-streaming.md)
|
|
32
32
|
15. [Subagents](./subagents.md)
|
|
33
33
|
16. [Schedules](./schedules.md)
|
|
34
|
-
17. [Evals](./evals.md)
|
|
35
|
-
18. [Auth And Route Protection](./auth-and-route-protection.md)
|
|
36
|
-
19. [Vercel Deployment](./vercel-deployment.md)
|
|
37
|
-
20. [CLI, Build, And Debugging](./cli-build-and-debugging.md)
|
|
34
|
+
17. [Evals](./advanced/evals.md)
|
|
35
|
+
18. [Auth And Route Protection](./advanced/auth-and-route-protection.md)
|
|
36
|
+
19. [Vercel Deployment](./advanced/vercel-deployment.md)
|
|
37
|
+
20. [CLI, Build, And Debugging](./advanced/cli-build-and-debugging.md)
|
|
38
38
|
|
|
39
39
|
## The Public Mental Model
|
|
40
40
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Advanced",
|
|
3
|
+
"pages": [
|
|
4
|
+
"project-layout",
|
|
5
|
+
"context-control",
|
|
6
|
+
"hooks",
|
|
7
|
+
"auth-and-route-protection",
|
|
8
|
+
"vercel-deployment",
|
|
9
|
+
"runs-and-streaming",
|
|
10
|
+
"session-context",
|
|
11
|
+
"workspace",
|
|
12
|
+
"evals",
|
|
13
|
+
"instrumentation",
|
|
14
|
+
"cli-build-and-debugging",
|
|
15
|
+
"typescript-api",
|
|
16
|
+
"...",
|
|
17
|
+
"faqs"
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -12,20 +12,7 @@
|
|
|
12
12
|
"schedules",
|
|
13
13
|
"connections",
|
|
14
14
|
"---",
|
|
15
|
-
"
|
|
16
|
-
"context-control",
|
|
17
|
-
"hooks",
|
|
18
|
-
"auth-and-route-protection",
|
|
19
|
-
"vercel-deployment",
|
|
20
|
-
"runs-and-streaming",
|
|
21
|
-
"session-context",
|
|
22
|
-
"workspace",
|
|
23
|
-
"evals",
|
|
24
|
-
"instrumentation",
|
|
25
|
-
"cli-build-and-debugging",
|
|
26
|
-
"typescript-api",
|
|
27
|
-
"...",
|
|
28
|
-
"faqs",
|
|
15
|
+
"advanced",
|
|
29
16
|
"---",
|
|
30
17
|
"research"
|
|
31
18
|
]
|
|
@@ -1,23 +1,5 @@
|
|
|
1
|
-
import type { SkillHandle } from "#execution/skills/types.js";
|
|
2
|
-
import type { SandboxSession } from "#public/definitions/sandbox.js";
|
|
3
1
|
import type { ContextKey } from "#context/key.js";
|
|
4
|
-
import { type Session } from "#context/keys.js";
|
|
5
2
|
export type { Session, SessionAuth, SessionAuthContext, SessionParent, SessionTurn, } from "#context/keys.js";
|
|
6
|
-
/**
|
|
7
|
-
* Returns the active session for the current authored async execution chain.
|
|
8
|
-
*
|
|
9
|
-
* @throws When called outside of a managed step execution.
|
|
10
|
-
*/
|
|
11
|
-
export declare function getSession(): Session;
|
|
12
|
-
/**
|
|
13
|
-
* Returns the active sandbox session for the current runtime execution.
|
|
14
|
-
*
|
|
15
|
-
* Each agent owns exactly one sandbox; callers do not pass a name.
|
|
16
|
-
*
|
|
17
|
-
* @throws When called outside of a managed step execution or when the
|
|
18
|
-
* sandbox is not available on the current context.
|
|
19
|
-
*/
|
|
20
|
-
export declare function getSandbox(): Promise<SandboxSession>;
|
|
21
3
|
/**
|
|
22
4
|
* Returns the current value of a context key, or `undefined` when unset.
|
|
23
5
|
*
|
|
@@ -56,10 +38,3 @@ export declare function setContext<T>(key: ContextKey<T>, valueOrUpdater: T | ((
|
|
|
56
38
|
* @throws When called outside of a managed step execution.
|
|
57
39
|
*/
|
|
58
40
|
export declare function ensureContext<T>(key: ContextKey<T>, create: () => T): T;
|
|
59
|
-
/**
|
|
60
|
-
* Returns a handle for the named skill visible to the current agent.
|
|
61
|
-
*
|
|
62
|
-
* @throws When called outside of a managed step execution or when the
|
|
63
|
-
* requested skill is not found.
|
|
64
|
-
*/
|
|
65
|
-
export declare function getSkill(identifier: string): SkillHandle;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{loadContext}from"#context/container.js";function getContext(e){return loadContext().get(e)}function requireContext(e){return loadContext().require(e)}function hasContext(e){return loadContext().has(e)}function setContext(e,t){return loadContext().set(e,t)}function ensureContext(e,t){return loadContext().ensure(e,t)}export{ensureContext,getContext,hasContext,requireContext,setContext};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{SandboxKey,SessionKey}from"#context/keys.js";import{
|
|
1
|
+
import{SandboxKey,SessionKey}from"#context/keys.js";import{loadContext}from"#context/container.js";import{createSandboxSkillHandle}from"#runtime/skills/sandbox-access.js";function buildCallbackContext(){let r=loadContext(),i=r.require(SessionKey);return{session:{id:i.sessionId,auth:i.auth,turn:i.turn,parent:i.parent},getSandbox(){let t=r.get(SandboxKey);if(t===void 0)throw Error(`Ash sandbox runtime access is unavailable in the current async context. Call ctx.getSandbox() only from authored runtime functions such as tools, hooks, and channel events.`);return t.get().then(e=>{if(e===null)throw Error(`The sandbox is not available in the current authored runtime context.`);return e})},getSkill(t){let n=r.get(SandboxKey);if(n===void 0)throw Error(`Ash sandbox runtime access is unavailable in the current async context. Call ctx.getSkill() only from authored runtime functions such as tools, hooks, and channel events.`);return createSandboxSkillHandle(n,t)}}}export{buildCallbackContext};
|
|
@@ -19,8 +19,8 @@ export interface SessionAuth {
|
|
|
19
19
|
readonly initiator: SessionAuthContext | null;
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
|
-
* Public session metadata exposed to authored code (tools,
|
|
23
|
-
*
|
|
22
|
+
* Public session metadata exposed to authored code (tools, hooks,
|
|
23
|
+
* channel events) via `ctx.session`.
|
|
24
24
|
*/
|
|
25
25
|
export interface Session {
|
|
26
26
|
readonly auth: SessionAuth;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { AshEvalSuiteDefinition, AshEvalSuiteInput } from "#evals/types.js";
|
|
1
|
+
import type { AshEvalSuiteDefinition, AshEvalSuiteInput, AshEvalSuiteInputFields } from "#evals/types.js";
|
|
2
|
+
import type { ExactDefinition } from "#public/definitions/exact.js";
|
|
2
3
|
/**
|
|
3
4
|
* Defines one Ash eval suite.
|
|
4
5
|
*
|
|
@@ -7,4 +8,4 @@ import type { AshEvalSuiteDefinition, AshEvalSuiteInput } from "#evals/types.js"
|
|
|
7
8
|
* either `load` (an async function) or `cases` (a static array), but not
|
|
8
9
|
* both.
|
|
9
10
|
*/
|
|
10
|
-
export declare function defineEvalSuite(input:
|
|
11
|
+
export declare function defineEvalSuite<TInput extends AshEvalSuiteInput>(input: ExactDefinition<TInput, AshEvalSuiteInputFields>): AshEvalSuiteDefinition;
|
|
@@ -84,28 +84,39 @@ export interface AshEvalScorerArgs {
|
|
|
84
84
|
* Return `null` to skip scoring for a case (e.g. when expected is absent).
|
|
85
85
|
*/
|
|
86
86
|
export type AshEvalScorer = (args: AshEvalScorerArgs) => AshEvalScorerResult | Promise<AshEvalScorerResult | null> | null;
|
|
87
|
+
export interface AshEvalTaskFields {
|
|
88
|
+
readonly messages?: (testCase: AshEvalCase) => string[];
|
|
89
|
+
readonly parseOutput?: (result: AshEvalTaskResult) => unknown;
|
|
90
|
+
readonly prompt?: (testCase: AshEvalCase) => string;
|
|
91
|
+
}
|
|
87
92
|
/**
|
|
88
93
|
* Declarative task configuration for a suite. The runner owns session
|
|
89
94
|
* lifecycle, stream capture, and derived metadata. Suites only declare how
|
|
90
95
|
* to derive messages and parse outputs.
|
|
91
96
|
*/
|
|
92
|
-
export
|
|
93
|
-
/**
|
|
94
|
-
* Derive a single prompt string from one eval case.
|
|
95
|
-
* Mutually exclusive with `messages`.
|
|
96
|
-
*/
|
|
97
|
-
readonly prompt?: (testCase: AshEvalCase) => string;
|
|
97
|
+
export type AshEvalTask = (AshEvalTaskFields & {
|
|
98
98
|
/**
|
|
99
99
|
* Derive an ordered list of messages for a multi-turn eval.
|
|
100
100
|
* Mutually exclusive with `prompt`.
|
|
101
101
|
*/
|
|
102
|
-
readonly messages
|
|
102
|
+
readonly messages: (testCase: AshEvalCase) => string[];
|
|
103
|
+
readonly prompt?: never;
|
|
104
|
+
}) | (AshEvalTaskFields & {
|
|
105
|
+
readonly messages?: never;
|
|
106
|
+
/**
|
|
107
|
+
* Derive a single prompt string from one eval case.
|
|
108
|
+
* Mutually exclusive with `messages`.
|
|
109
|
+
*/
|
|
110
|
+
readonly prompt: (testCase: AshEvalCase) => string;
|
|
111
|
+
}) | {
|
|
112
|
+
readonly messages?: never;
|
|
103
113
|
/**
|
|
104
114
|
* Transform the raw task result into the scored output value.
|
|
105
115
|
* When omitted, `result.output` defaults to `result.finalMessage`.
|
|
106
116
|
*/
|
|
107
117
|
readonly parseOutput?: (result: AshEvalTaskResult) => unknown;
|
|
108
|
-
|
|
118
|
+
readonly prompt?: never;
|
|
119
|
+
};
|
|
109
120
|
/**
|
|
110
121
|
* Eval target descriptor.
|
|
111
122
|
*/
|
|
@@ -159,6 +170,18 @@ interface AshEvalSuiteBase {
|
|
|
159
170
|
*/
|
|
160
171
|
readonly thresholds?: Readonly<Record<string, number>>;
|
|
161
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* Complete top-level key set accepted by {@link defineEvalSuite}.
|
|
175
|
+
*
|
|
176
|
+
* Used by the definition helper to reject unknown authored keys while the
|
|
177
|
+
* stricter {@link AshEvalSuiteInput} union enforces `load`/`cases`
|
|
178
|
+
* exclusivity.
|
|
179
|
+
*/
|
|
180
|
+
export interface AshEvalSuiteInputFields extends Omit<AshEvalSuiteBase, "task"> {
|
|
181
|
+
readonly cases?: readonly AshEvalCase[];
|
|
182
|
+
readonly task?: AshEvalTaskFields;
|
|
183
|
+
load?(): Promise<AshEvalCase[]>;
|
|
184
|
+
}
|
|
162
185
|
/**
|
|
163
186
|
* Full suite input. Authors pass this to `defineEvalSuite()`.
|
|
164
187
|
*
|
|
@@ -167,12 +190,15 @@ interface AshEvalSuiteBase {
|
|
|
167
190
|
* function internally. Suite identity is derived from the file path —
|
|
168
191
|
* authors do not specify an `id` or `name`.
|
|
169
192
|
*/
|
|
170
|
-
export
|
|
193
|
+
export type AshEvalSuiteInput = (AshEvalSuiteBase & {
|
|
194
|
+
readonly cases?: never;
|
|
171
195
|
/** Load cases dynamically. Mutually exclusive with `cases`. */
|
|
172
|
-
load
|
|
196
|
+
load(): Promise<AshEvalCase[]>;
|
|
197
|
+
}) | (AshEvalSuiteBase & {
|
|
173
198
|
/** Static inline cases. Mutually exclusive with `load`. */
|
|
174
|
-
readonly cases
|
|
175
|
-
|
|
199
|
+
readonly cases: readonly AshEvalCase[];
|
|
200
|
+
load?: never;
|
|
201
|
+
});
|
|
176
202
|
/**
|
|
177
203
|
* Suite returned by `defineEvalSuite()`. Carries no `id` yet — discovery
|
|
178
204
|
* stamps the path-derived id on at import time to produce a full
|
|
@@ -8,7 +8,7 @@ export interface SkillFile {
|
|
|
8
8
|
text(): Promise<string>;
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
|
-
* Handle to one authored skill, returned by
|
|
11
|
+
* Handle to one authored skill, returned by `ctx.getSkill()`.
|
|
12
12
|
*
|
|
13
13
|
* Provides metadata and file access for skill packages.
|
|
14
14
|
*/
|
|
@@ -71,7 +71,7 @@ interface StepHooks {
|
|
|
71
71
|
* own all step-internal work: emission, compaction, and prompt caching.
|
|
72
72
|
*
|
|
73
73
|
* The harness passes these hooks to `ToolLoopAgent` and reads the
|
|
74
|
-
* results via `
|
|
74
|
+
* results via `stepResult` after the agent finishes.
|
|
75
75
|
*/
|
|
76
76
|
export declare function buildStepHooks(input: StepHooksInput): StepHooks;
|
|
77
77
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createRequire}from"node:module";import{basename,dirname,join}from"node:path";import{existsSync,readFileSync,realpathSync}from"node:fs";import{ASH_PACKAGE_NAME}from"#internal/package-name.js";import{fileURLToPath}from"node:url";let cachedPackageInfo;const WORKFLOW_MODULE_ALIASES={"workflow/api":`src/compiled/@workflow/core/runtime.js`,"workflow/errors":`src/compiled/@workflow/errors/index.js`,"workflow/internal/private":`src/compiled/@workflow/core/private.js`,"workflow/runtime":`src/compiled/@workflow/core/runtime.js`};function resolveFallbackPackageVersion(){return`0.
|
|
1
|
+
import{createRequire}from"node:module";import{basename,dirname,join}from"node:path";import{existsSync,readFileSync,realpathSync}from"node:fs";import{ASH_PACKAGE_NAME}from"#internal/package-name.js";import{fileURLToPath}from"node:url";let cachedPackageInfo;const WORKFLOW_MODULE_ALIASES={"workflow/api":`src/compiled/@workflow/core/runtime.js`,"workflow/errors":`src/compiled/@workflow/errors/index.js`,"workflow/internal/private":`src/compiled/@workflow/core/private.js`,"workflow/runtime":`src/compiled/@workflow/core/runtime.js`};function resolveFallbackPackageVersion(){return`0.37.0`}const FALLBACK_PACKAGE_INFO={name:ASH_PACKAGE_NAME,version:resolveFallbackPackageVersion()};function resolveCurrentModulePath(){return typeof __filename==`string`?__filename:resolveCurrentModulePathFromStack()}function resolveCurrentModulePathFromStack(){let e=Error.prepareStackTrace;try{Error.prepareStackTrace=(e,t)=>t;let e=Error().stack?.[0]?.getFileName();if(typeof e!=`string`||e.length===0)throw Error(`Failed to resolve the current module path from the stack trace.`);return e.startsWith(`file:`)?fileURLToPath(e):e}finally{Error.prepareStackTrace=e}}const require=createRequire(resolveCurrentModulePath());function isBuildOutputPackageRoot(e){return basename(e)===`dist`&&existsSync(join(dirname(e),`package.json`))}function resolvePackageBuildRoot(){let e=dirname(realpathSync(resolveCurrentModulePath()));for(;;){if(isBuildOutputPackageRoot(e))return e;let t=dirname(e);if(t===e)return null;e=t}}function findNearestPackageRoot(e){let t=e;for(;;){if(existsSync(join(t,`package.json`))&&!isBuildOutputPackageRoot(t))return t;let r=dirname(t);if(r===t)throw Error(`Failed to resolve package root from "${e}".`);t=r}}function resolvePackageRoot(){return findNearestPackageRoot(dirname(realpathSync(resolveCurrentModulePath())))}function tryResolvePackageRoot(){try{return resolvePackageRoot()}catch{return}}function rewriteSourceFilePathForBuild(e){return e.replace(/\.[cm]?tsx?$/,`.js`)}function resolvePackageSourceFilePath(e){let t=resolvePackageBuildRoot();return t===null?join(resolvePackageRoot(),e):join(t,rewriteSourceFilePathForBuild(e))}function resolvePackageSourceDirectoryPath(e){let t=resolvePackageBuildRoot();return join(t===null?resolvePackageRoot():t,e)}function resolvePackageCompiledFilePath(e){let t=resolvePackageBuildRoot();return t===null?join(resolvePackageRoot(),`.generated`,`compiled`,e.replace(/^src\/compiled\//,``)):join(t,e)}function normalizeInstalledPackageInfo(e){let t=e;if(!(typeof t.name!=`string`||typeof t.version!=`string`))return{name:t.name,version:t.version}}function tryReadInstalledPackageInfo(e,t){let n=normalizeInstalledPackageInfo(JSON.parse(readFileSync(e,`utf8`)));if(n?.name===t)return n}function resolveInstalledPackageInfo(){if(cachedPackageInfo)return cachedPackageInfo;let e=tryResolvePackageRoot(),t=e===void 0?void 0:tryReadInstalledPackageInfo(join(e,`package.json`),ASH_PACKAGE_NAME);if(t)return cachedPackageInfo=t,cachedPackageInfo;try{let e=tryReadInstalledPackageInfo(require.resolve(`${ASH_PACKAGE_NAME}/package.json`),ASH_PACKAGE_NAME);if(e)return cachedPackageInfo=e,cachedPackageInfo}catch{}return cachedPackageInfo={...FALLBACK_PACKAGE_INFO},cachedPackageInfo}function resolveWorkflowModulePath(e){if(e===`workflow`)return resolvePackageSourceFilePath(`src/internal/workflow/index.ts`);if(e===`workflow/internal/builtins`)return resolvePackageSourceFilePath(`src/internal/workflow/builtins.ts`);let t=WORKFLOW_MODULE_ALIASES[e];return t===void 0?require.resolve(e):resolvePackageCompiledFilePath(t)}export{resolveInstalledPackageInfo,resolvePackageRoot,resolvePackageSourceDirectoryPath,resolvePackageSourceFilePath,resolveWorkflowModulePath};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{getSupportedModuleBaseName,matchesSupportedModuleBaseName}from"./module-files.js";import{pathExists,writeTextFile}from"./files.js";import{PNPM_WORKSPACE_PATH,ensurePnpmWorkspacePolicy}from"./pnpm-workspace.js";import{WEB_APP_TEMPLATE_FILES,WEB_APP_TEMPLATE_PACKAGE_JSON}from"./web-template.js";import"./project.js";import{patchPackageJson}from"./package-json.js";import{basename,join,resolve}from"node:path";import{readFile,readdir,writeFile}from"node:fs/promises";const SLACK_CHANNEL_DEFAULT_ROUTE=`/ash/v1/slack`,DEFAULT_SLACK_CONNECTOR_SLUG=`my-agent`,PACKAGE_DEPENDENCY_FIELDS=[`dependencies`,`devDependencies`,`peerDependencies`,`optionalDependencies`],WEB_VERCEL_JSON_PATH=`vercel.json`,WEB_VERCEL_JSON_SCHEMA=`https://openapi.vercel.sh/vercel.json`,WEB_DEFAULT_VERCEL_SERVICES={web:{entrypoint:`.`,framework:`nextjs`,routePrefix:`/`},ash:{buildCommand:`ash build`,entrypoint:`.`,framework:`ash`,routePrefix:`/_ash_internal/ash`}};function toSlackConnectorSlug(e){return e}function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}async function readDependencyVersion(e,t){let n=JSON.parse(await readFile(e,`utf8`));if(!isJsonObject(n)||!isJsonObject(n.dependencies))return;let r=n.dependencies[t];return typeof r==`string`?r:void 0}function packageJsonHasDependency(e,t){for(let n of PACKAGE_DEPENDENCY_FIELDS){let r=e[n];if(isJsonObject(r)&&typeof r[t]==`string`)return!0}return!1}async function hasPackageDependency(e,t){if(!await pathExists(e))return!1;let r=JSON.parse(await readFile(e,`utf8`));return isJsonObject(r)&&packageJsonHasDependency(r,t)}async function ensurePackageDependency(e,t,r){return!await pathExists(e)||await readDependencyVersion(e,t)===r?[]:(await patchPackageJson(e,{dependencies:{[t]:r}}),[{path:e,dependencies:[t],devDependencies:[],scripts:[]}])}function resolveWebPackageVersions(e){return{ashPackageVersion:e?.ashPackageVersion??`0.
|
|
1
|
+
import{getSupportedModuleBaseName,matchesSupportedModuleBaseName}from"./module-files.js";import{pathExists,writeTextFile}from"./files.js";import{PNPM_WORKSPACE_PATH,ensurePnpmWorkspacePolicy}from"./pnpm-workspace.js";import{WEB_APP_TEMPLATE_FILES,WEB_APP_TEMPLATE_PACKAGE_JSON}from"./web-template.js";import"./project.js";import{patchPackageJson}from"./package-json.js";import{basename,join,resolve}from"node:path";import{readFile,readdir,writeFile}from"node:fs/promises";const SLACK_CHANNEL_DEFAULT_ROUTE=`/ash/v1/slack`,DEFAULT_SLACK_CONNECTOR_SLUG=`my-agent`,PACKAGE_DEPENDENCY_FIELDS=[`dependencies`,`devDependencies`,`peerDependencies`,`optionalDependencies`],WEB_VERCEL_JSON_PATH=`vercel.json`,WEB_VERCEL_JSON_SCHEMA=`https://openapi.vercel.sh/vercel.json`,WEB_DEFAULT_VERCEL_SERVICES={web:{entrypoint:`.`,framework:`nextjs`,routePrefix:`/`},ash:{buildCommand:`ash build`,entrypoint:`.`,framework:`ash`,routePrefix:`/_ash_internal/ash`}};function toSlackConnectorSlug(e){return e}function isJsonObject(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}async function readDependencyVersion(e,t){let n=JSON.parse(await readFile(e,`utf8`));if(!isJsonObject(n)||!isJsonObject(n.dependencies))return;let r=n.dependencies[t];return typeof r==`string`?r:void 0}function packageJsonHasDependency(e,t){for(let n of PACKAGE_DEPENDENCY_FIELDS){let r=e[n];if(isJsonObject(r)&&typeof r[t]==`string`)return!0}return!1}async function hasPackageDependency(e,t){if(!await pathExists(e))return!1;let r=JSON.parse(await readFile(e,`utf8`));return isJsonObject(r)&&packageJsonHasDependency(r,t)}async function ensurePackageDependency(e,t,r){return!await pathExists(e)||await readDependencyVersion(e,t)===r?[]:(await patchPackageJson(e,{dependencies:{[t]:r}}),[{path:e,dependencies:[t],devDependencies:[],scripts:[]}])}function resolveWebPackageVersions(e){return{ashPackageVersion:e?.ashPackageVersion??`0.37.0`,aiPackageVersion:e?.aiPackageVersion??`7.0.0-canary.154`,nextPackageVersion:e?.nextPackageVersion??`16.2.6`,reactPackageVersion:e?.reactPackageVersion??`19.2.6`,reactDomPackageVersion:e?.reactDomPackageVersion??`19.2.6`,streamdownPackageVersion:e?.streamdownPackageVersion??`2.5.0`,zodPackageVersion:e?.zodPackageVersion??`4.4.3`,tsgoPackageVersion:e?.tsgoPackageVersion??`7.0.0-dev.20260523.1`,typesNodePackageVersion:e?.typesNodePackageVersion??`25.9.1`,typesReactPackageVersion:e?.typesReactPackageVersion??`19.2.15`,typesReactDomPackageVersion:e?.typesReactDomPackageVersion??`19.2.3`}}function formatAshDependencySpecifier(e){return/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z-.]+)?$/.test(e)?`^${e}`:e}async function patchWebPackageJson(e,t){if(!await pathExists(e))return[];assertStampedVersion(`ashPackageVersion`,t.ashPackageVersion),assertStampedVersion(`aiPackageVersion`,t.aiPackageVersion),assertStampedVersion(`nextPackageVersion`,t.nextPackageVersion),assertStampedVersion(`reactPackageVersion`,t.reactPackageVersion),assertStampedVersion(`reactDomPackageVersion`,t.reactDomPackageVersion),assertStampedVersion(`streamdownPackageVersion`,t.streamdownPackageVersion),assertStampedVersion(`zodPackageVersion`,t.zodPackageVersion),assertStampedVersion(`tsgoPackageVersion`,t.tsgoPackageVersion),assertStampedVersion(`typesNodePackageVersion`,t.typesNodePackageVersion),assertStampedVersion(`typesReactPackageVersion`,t.typesReactPackageVersion),assertStampedVersion(`typesReactDomPackageVersion`,t.typesReactDomPackageVersion);let r={...WEB_APP_TEMPLATE_PACKAGE_JSON.dependencies,ai:t.aiPackageVersion,"experimental-ash":formatAshDependencySpecifier(t.ashPackageVersion),next:t.nextPackageVersion,react:t.reactPackageVersion,"react-dom":t.reactDomPackageVersion,streamdown:t.streamdownPackageVersion,zod:t.zodPackageVersion},i={...WEB_APP_TEMPLATE_PACKAGE_JSON.devDependencies,"@types/node":t.typesNodePackageVersion,"@types/react":t.typesReactPackageVersion,"@types/react-dom":t.typesReactDomPackageVersion,"@typescript/native-preview":t.tsgoPackageVersion},a=WEB_APP_TEMPLATE_PACKAGE_JSON.scripts;return await patchPackageJson(e,{dependencies:r,devDependencies:i,scripts:a}),[{path:e,dependencies:Object.keys(r),devDependencies:Object.keys(i),scripts:Object.keys(a)}]}function normalizeSlackConnectorSlug(e){return toSlackConnectorSlug((e.trim().replace(/^@/,``).split(`/`).at(-1)??``).toLowerCase().replace(/[^a-z0-9_-]+/g,`-`).replace(/^[^a-z0-9]+/,``).replace(/[^a-z0-9]+$/,``).slice(0,100).replace(/[^a-z0-9]+$/,``)||`my-agent`)}async function deriveSlackConnectorSlug(e,t){if(t!==void 0&&t.length>0&&t!==`.`)return normalizeSlackConnectorSlug(t);try{let t=await readFile(join(e,`package.json`),`utf8`),n=JSON.parse(t);if(typeof n.name==`string`&&n.name.length>0)return normalizeSlackConnectorSlug(n.name)}catch{}return normalizeSlackConnectorSlug(basename(resolve(e))||`my-agent`)}function buildSlackTemplate(e){return`import { connectSlackCredentials } from "@vercel/connect/ash";
|
|
2
2
|
import { slackChannel } from "experimental-ash/channels/slack";
|
|
3
3
|
|
|
4
4
|
export default slackChannel({
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{extractErrorId,formatErrorHint}from"#internal/logging.js";import{splitDiscordMessageContent}from"#public/channels/discord/api.js";import{renderInputRequestComponents}from"#public/channels/discord/hitl.js";function defaultDiscordAuth(e){let t={channel_id:e.channelId,interaction_id:e.id,user_id:e.user.id,username:e.user.username};return e.guildId!==void 0&&(t.guild_id=e.guildId),e.member?.nick!==void 0&&(t.member_nick=e.member.nick),{attributes:t,authenticator:`discord-interaction`,issuer:e.guildId?`discord:${e.guildId}`:`discord`,principalId:e.guildId?`discord:${e.guildId}:${e.user.id}`:`discord:${e.user.id}`,principalType:e.user.isBot?`service`:`user`}}function defaultOnCommand(e,t){return{auth:defaultDiscordAuth(t)}}const defaultEvents={async"turn.started"(e,t){await t.discord.startTyping()},async"actions.requested"(e,t){await t.discord.startTyping()},async"input.requested"(e,t){for(let i of e.requests){let e=splitDiscordMessageContent(i.prompt)[0]??i.prompt;await t.discord.post({components:renderInputRequestComponents(i),content:e})}},async"message.completed"(e,t){e.finishReason===`tool-calls`||!e.message||await t.discord.post(e.message)},async"session.failed"(n,r){let
|
|
2
|
-
`))},async"turn.failed"(n,r){let
|
|
1
|
+
import{extractErrorId,formatErrorHint}from"#internal/logging.js";import{splitDiscordMessageContent}from"#public/channels/discord/api.js";import{renderInputRequestComponents}from"#public/channels/discord/hitl.js";function defaultDiscordAuth(e){let t={channel_id:e.channelId,interaction_id:e.id,user_id:e.user.id,username:e.user.username};return e.guildId!==void 0&&(t.guild_id=e.guildId),e.member?.nick!==void 0&&(t.member_nick=e.member.nick),{attributes:t,authenticator:`discord-interaction`,issuer:e.guildId?`discord:${e.guildId}`:`discord`,principalId:e.guildId?`discord:${e.guildId}:${e.user.id}`:`discord:${e.user.id}`,principalType:e.user.isBot?`service`:`user`}}function defaultOnCommand(e,t){return{auth:defaultDiscordAuth(t)}}const defaultEvents={async"turn.started"(e,t,n){await t.discord.startTyping()},async"actions.requested"(e,t,n){await t.discord.startTyping()},async"input.requested"(e,t,i){for(let i of e.requests){let e=splitDiscordMessageContent(i.prompt)[0]??i.prompt;await t.discord.post({components:renderInputRequestComponents(i),content:e})}},async"message.completed"(e,t,n){e.finishReason===`tool-calls`||!e.message||await t.discord.post(e.message)},async"session.failed"(n,r,i){let a=formatErrorHint(n),o=extractErrorId(n.details);await r.discord.post([`This session could not recover from an error${a}.`,``,`Start a new command to continue.`,...o?[``,`Error id: ${o}`]:[]].join(`
|
|
2
|
+
`))},async"turn.failed"(n,r,i){let a=formatErrorHint(n),o=extractErrorId(n.details);await r.discord.post([`I hit an error while handling your request${a}.`,``,`Please try again, rephrase, or reach out if it keeps failing.`,...o?[``,`Error id: ${o}`]:[]].join(`
|
|
3
3
|
`))}};export{defaultDiscordAuth,defaultEvents,defaultOnCommand};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { ModelMessage } from "ai";
|
|
2
2
|
import type { TypedReceiveRoute } from "#channel/receive-args.js";
|
|
3
|
-
import type { SessionHandle } from "#channel/session.js";
|
|
4
3
|
import type { SessionAuthContext } from "#channel/types.js";
|
|
4
|
+
import type { AshCallbackContext } from "#public/definitions/callback-context.js";
|
|
5
|
+
import type { ChannelSessionOps } from "#public/definitions/defineChannel.js";
|
|
5
6
|
import type { HandleMessageStreamEvent } from "#protocol/message.js";
|
|
6
7
|
import { type DiscordApiOptions, type DiscordApiResponse, type DiscordCredentials, type DiscordMessageBody, type DiscordPostedMessage } from "#public/channels/discord/api.js";
|
|
7
8
|
import { type DiscordCommandInteraction } from "#public/channels/discord/inbound.js";
|
|
@@ -18,8 +19,7 @@ export interface DiscordContext {
|
|
|
18
19
|
readonly discord: DiscordHandle;
|
|
19
20
|
}
|
|
20
21
|
/** Event-handler Discord context, including mutable per-conversation state. */
|
|
21
|
-
export interface DiscordEventContext extends DiscordContext {
|
|
22
|
-
readonly session: SessionHandle;
|
|
22
|
+
export interface DiscordEventContext extends DiscordContext, ChannelSessionOps {
|
|
23
23
|
state: DiscordChannelState;
|
|
24
24
|
}
|
|
25
25
|
/** JSON-serializable Discord channel state. */
|
|
@@ -62,7 +62,7 @@ export type DiscordCommandResult = {
|
|
|
62
62
|
} | null;
|
|
63
63
|
/** Sync or async {@link DiscordCommandResult}. */
|
|
64
64
|
export type DiscordCommandResultOrPromise = DiscordCommandResult | Promise<DiscordCommandResult>;
|
|
65
|
-
type DiscordEventHandler<T extends HandleMessageStreamEvent["type"]> = (data: EventData<T>, ctx:
|
|
65
|
+
type DiscordEventHandler<T extends HandleMessageStreamEvent["type"]> = (data: EventData<T>, channel: DiscordEventContext, ctx: AshCallbackContext) => void | Promise<void>;
|
|
66
66
|
/** Event handlers supported by `discordChannel({ events })`. */
|
|
67
67
|
export interface DiscordChannelEvents {
|
|
68
68
|
readonly "turn.started"?: DiscordEventHandler<"turn.started">;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createLogger,logError}from"#internal/logging.js";import{parseJsonObject}from"#shared/json.js";import{POST,defineChannel}from"#public/definitions/defineChannel.js";import"#public/channels/discord/verify.js";import{callDiscordApi,createDiscordFollowupMessage,discordContinuationToken,editDiscordOriginalResponse,sendDiscordChannelMessage,splitDiscordMessageContent,triggerDiscordTypingIndicator}from"#public/channels/discord/api.js";import{buildFreeformModalResponse,deriveComponentInputResponses,deriveModalInputResponses,isDiscordFreeformComponent}from"#public/channels/discord/hitl.js";import{defaultEvents,defaultOnCommand}from"#public/channels/discord/defaults.js";import{DISCORD_INTERACTION_RESPONSE_TYPE,DISCORD_INTERACTION_TYPE,commandInteractionMessage,parseDiscordInteraction,prependDiscordContext}from"#public/channels/discord/inbound.js";import{discordDeferredJson,discordJson,discordJsonBody,readMessageContent}from"#public/channels/discord/responses.js";import{verifyDiscordInbound}from"#public/channels/discord/verifyInbound.js";const log=createLogger(`discord.channel`);function discordChannel(e={}){let t=e.onCommand??defaultOnCommand,a={...defaultEvents,...e.events};return defineChannel({kindHint:`discord`,state:initialDiscordState(),context(t,n){return rebuildDiscordContext(t,n,e)},routes:[POST(e.route??`/ash/v1/discord`,async(r,{send:i,waitUntil:a})=>{let o=await verifyDiscordInbound(r,e.credentials);if(o===null)return new Response(`unauthorized`,{status:401});let s;try{s=parseJsonObject(JSON.parse(o))}catch(e){return log.warn(`inbound Discord body is not valid JSON`,{error:e}),discordJson({content:`invalid request`,ephemeral:!0})}if(s.type===DISCORD_INTERACTION_TYPE.PING)return discordJson({type:DISCORD_INTERACTION_RESPONSE_TYPE.PONG});let c=parseDiscordInteraction(s);return c===null?discordJson({content:`Unsupported Discord interaction.`,ephemeral:!0}):handleInteraction({config:e,interaction:c,onCommand:t,send:i,waitUntil:a})})],async receive(t,{send:n}){let r=t.args,i=readString(r.channelId);if(!i)throw Error(`discordChannel().receive requires args.channelId.`);let a=readString(r.conversationId),o=r.initialMessage;if(o!==void 0&&a!==void 0)throw Error("discordChannel().receive: `conversationId` and `initialMessage` are mutually exclusive.");let c=a??``,l=a!==void 0;if(o!==void 0){let t=await buildDiscordHandle({config:e,state:{...initialDiscordState(),channelId:i}}).sendChannelMessage(o);c=t.id,l=t.id.length>0}return n(t.message,{auth:t.auth,continuationToken:discordContinuationToken(i,c),state:{applicationId:null,channelId:i,conversationId:c||null,guildId:null,hasMessageAnchor:l,initialResponseSent:!0,interactionToken:null}})},events:a})}function rebuildDiscordContext(e,t,n){return{discord:buildDiscordHandle({config:n,session:t,state:e}),
|
|
1
|
+
import{createLogger,logError}from"#internal/logging.js";import{parseJsonObject}from"#shared/json.js";import{POST,defineChannel}from"#public/definitions/defineChannel.js";import"#public/channels/discord/verify.js";import{callDiscordApi,createDiscordFollowupMessage,discordContinuationToken,editDiscordOriginalResponse,sendDiscordChannelMessage,splitDiscordMessageContent,triggerDiscordTypingIndicator}from"#public/channels/discord/api.js";import{buildFreeformModalResponse,deriveComponentInputResponses,deriveModalInputResponses,isDiscordFreeformComponent}from"#public/channels/discord/hitl.js";import{defaultEvents,defaultOnCommand}from"#public/channels/discord/defaults.js";import{DISCORD_INTERACTION_RESPONSE_TYPE,DISCORD_INTERACTION_TYPE,commandInteractionMessage,parseDiscordInteraction,prependDiscordContext}from"#public/channels/discord/inbound.js";import{discordDeferredJson,discordJson,discordJsonBody,readMessageContent}from"#public/channels/discord/responses.js";import{verifyDiscordInbound}from"#public/channels/discord/verifyInbound.js";const log=createLogger(`discord.channel`);function discordChannel(e={}){let t=e.onCommand??defaultOnCommand,a={...defaultEvents,...e.events};return defineChannel({kindHint:`discord`,state:initialDiscordState(),context(t,n){return rebuildDiscordContext(t,n,e)},routes:[POST(e.route??`/ash/v1/discord`,async(r,{send:i,waitUntil:a})=>{let o=await verifyDiscordInbound(r,e.credentials);if(o===null)return new Response(`unauthorized`,{status:401});let s;try{s=parseJsonObject(JSON.parse(o))}catch(e){return log.warn(`inbound Discord body is not valid JSON`,{error:e}),discordJson({content:`invalid request`,ephemeral:!0})}if(s.type===DISCORD_INTERACTION_TYPE.PING)return discordJson({type:DISCORD_INTERACTION_RESPONSE_TYPE.PONG});let c=parseDiscordInteraction(s);return c===null?discordJson({content:`Unsupported Discord interaction.`,ephemeral:!0}):handleInteraction({config:e,interaction:c,onCommand:t,send:i,waitUntil:a})})],async receive(t,{send:n}){let r=t.args,i=readString(r.channelId);if(!i)throw Error(`discordChannel().receive requires args.channelId.`);let a=readString(r.conversationId),o=r.initialMessage;if(o!==void 0&&a!==void 0)throw Error("discordChannel().receive: `conversationId` and `initialMessage` are mutually exclusive.");let c=a??``,l=a!==void 0;if(o!==void 0){let t=await buildDiscordHandle({config:e,state:{...initialDiscordState(),channelId:i}}).sendChannelMessage(o);c=t.id,l=t.id.length>0}return n(t.message,{auth:t.auth,continuationToken:discordContinuationToken(i,c),state:{applicationId:null,channelId:i,conversationId:c||null,guildId:null,hasMessageAnchor:l,initialResponseSent:!0,interactionToken:null}})},events:a})}function rebuildDiscordContext(e,t,n){return{discord:buildDiscordHandle({config:n,session:t,state:e}),state:e}}function buildDiscordHandle(e){let n=e.config.api,r=e.state,i=mergeCredentials(e.config.credentials,r);function anchor(t){!t.id||r.hasMessageAnchor||(r.conversationId=t.id,r.hasMessageAnchor=!0,r.channelId&&e.session?.setContinuationToken(discordContinuationToken(r.channelId,t.id)))}async function sendViaChannel(e){let t=r.channelId??``;if(!t)throw Error(`discordChannel: missing channel id for outbound message.`);let a=await sendDiscordChannelMessage({apiBaseUrl:n?.apiBaseUrl,body:normalizePostInput(e),credentials:i,fetch:n?.fetch,channelId:t});return anchor(a),a}async function editOriginal(e){let t=r.interactionToken??``;if(!t)throw Error(`discordChannel: missing interaction token for original response edit.`);let a=await editDiscordOriginalResponse({apiBaseUrl:n?.apiBaseUrl,body:normalizePostInput(e),credentials:i,fetch:n?.fetch,interactionToken:t});return r.initialResponseSent=!0,anchor(a),a}async function followup(e){let t=r.interactionToken??``;if(!t)throw Error(`discordChannel: missing interaction token for followup message.`);let a=await createDiscordFollowupMessage({apiBaseUrl:n?.apiBaseUrl,body:normalizePostInput(e),credentials:i,fetch:n?.fetch,interactionToken:t});return anchor(a),a}async function startTyping(){let e=r.channelId??``;if(e)try{await triggerDiscordTypingIndicator({apiBaseUrl:n?.apiBaseUrl,credentials:i,fetch:n?.fetch,channelId:e})}catch(n){logError(log,`Discord typing indicator failed — swallowed`,n,{channelId:e})}}return{applicationId:r.applicationId??void 0,channelId:r.channelId??``,conversationId:r.conversationId??``,guildId:r.guildId??void 0,interactionToken:r.interactionToken??void 0,request(e,t,r){return callDiscordApi({apiBaseUrl:n?.apiBaseUrl,body:t,botToken:r?.botAuth===!0?i.botToken:void 0,fetch:n?.fetch,method:r?.method,path:e})},async post(e){let t=expandPostBodies(normalizePostInput(e)),n;for(let e of t){let t=await postOne({body:e,editOriginal,followup,sendViaChannel,state:r});n===void 0&&(n=t)}return n??{id:``,raw:null}},editOriginalResponse:editOriginal,followup,sendChannelMessage:sendViaChannel,startTyping}}async function postOne(e){if(e.state.interactionToken&&e.state.applicationId)try{return e.state.initialResponseSent?await e.followup(e.body):await e.editOriginal(e.body)}catch(e){log.warn(`Discord interaction-token delivery failed, falling back to channel message`,{error:e})}return e.sendViaChannel(e.body)}async function handleInteraction(e){return e.interaction.type===DISCORD_INTERACTION_TYPE.APPLICATION_COMMAND?handleCommandInteraction({config:e.config,interaction:e.interaction,onCommand:e.onCommand,send:e.send,waitUntil:e.waitUntil}):e.interaction.type===DISCORD_INTERACTION_TYPE.MESSAGE_COMPONENT?handleComponentInteraction({interaction:e.interaction,send:e.send,waitUntil:e.waitUntil}):handleModalSubmitInteraction({interaction:e.interaction,send:e.send,waitUntil:e.waitUntil})}async function handleCommandInteraction(e){let t=stateFromInteraction(e.interaction,{conversationId:e.interaction.id,hasMessageAnchor:!1,initialResponseSent:!1}),n={discord:buildDiscordHandle({config:e.config,state:t})},r;try{r=await e.onCommand(n,e.interaction)}catch(e){return log.error(`command handler failed`,{error:e}),discordJson({content:`The Discord command handler failed.`,ephemeral:!0})}return r==null?discordJson({content:`Command ignored.`,ephemeral:!0}):(e.waitUntil(dispatchCommand({interaction:e.interaction,result:r,send:e.send,state:t})),discordDeferredJson(r.ephemeral===!0))}function handleComponentInteraction(e){if(isDiscordFreeformComponent(e.interaction.customId)){let t=readMessageContent(e.interaction.raw);return discordJsonBody(buildFreeformModalResponse({customId:e.interaction.customId,prompt:t}))}let t=deriveComponentInputResponses(e.interaction);return t.length>0&&e.waitUntil(dispatchInputResponses({conversationId:e.interaction.messageId,inputResponses:t,interaction:e.interaction,send:e.send})),discordJsonBody({type:DISCORD_INTERACTION_RESPONSE_TYPE.DEFERRED_UPDATE_MESSAGE})}function handleModalSubmitInteraction(e){let t=deriveModalInputResponses(e.interaction);return t.length>0&&e.waitUntil(dispatchInputResponses({conversationId:e.interaction.messageId??e.interaction.id,inputResponses:t,interaction:e.interaction,send:e.send})),discordJson({content:`Answer received.`,ephemeral:!0})}async function dispatchCommand(e){let t=prependDiscordContext(commandInteractionMessage(e.interaction),{channelId:e.interaction.channelId,commandName:e.interaction.commandName,guildId:e.interaction.guildId,interactionId:e.interaction.id,userId:e.interaction.user.id,username:e.interaction.user.username});try{await e.send({message:t,modelContext:e.result.modelContext},{auth:e.result.auth,continuationToken:discordContinuationToken(e.interaction.channelId,e.interaction.id),state:e.state})}catch(e){log.error(`command delivery failed`,{error:e})}}async function dispatchInputResponses(e){try{await e.send({inputResponses:e.inputResponses},{auth:null,continuationToken:discordContinuationToken(e.interaction.channelId,e.conversationId),state:stateFromInteraction(e.interaction,{conversationId:e.conversationId,hasMessageAnchor:!0,initialResponseSent:!0})})}catch(e){log.error(`interaction response delivery failed`,{error:e})}}function stateFromInteraction(e,t){return{applicationId:e.applicationId,channelId:e.channelId,conversationId:t.conversationId,guildId:e.guildId??null,hasMessageAnchor:t.hasMessageAnchor,initialResponseSent:t.initialResponseSent,interactionToken:e.token}}function initialDiscordState(){return{applicationId:null,channelId:null,conversationId:null,guildId:null,hasMessageAnchor:!1,initialResponseSent:!1,interactionToken:null}}function mergeCredentials(e,t){return{applicationId:t.applicationId??e?.applicationId,botToken:e?.botToken,publicKey:e?.publicKey,webhookVerifier:e?.webhookVerifier}}function normalizePostInput(e){return typeof e==`string`?{content:e}:e}function expandPostBodies(e){return typeof e.content==`string`?splitDiscordMessageContent(e.content).map((t,n)=>n===0?{...e,content:t}:{allowed_mentions:e.allowed_mentions,content:t}):[e]}function readString(e){return typeof e==`string`&&e.length>0?e:void 0}export{discordChannel};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { defineChannel, GET, POST, PUT, PATCH, DELETE, type Channel, type ChannelConfig, type ChannelEvents, type Session, type SessionHandle, type RouteDefinition, type RouteHandlerArgs, type SendFn, type SendOptions, type SendPayload, type GetSessionFn, } from "#public/definitions/defineChannel.js";
|
|
1
|
+
export { defineChannel, GET, POST, PUT, PATCH, DELETE, type Channel, type ChannelConfig, type ChannelSessionOps, type ChannelEvents, type Session, type SessionHandle, type RouteDefinition, type RouteHandlerArgs, type SendFn, type SendOptions, type SendPayload, type GetSessionFn, } from "#public/definitions/defineChannel.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{createLogger,extractErrorId,formatErrorHint}from"#internal/logging.js";import{truncateMessageText,truncateTypingStatus}from"#public/channels/slack/limits.js";import{buildAuthCompletedText,buildAuthEphemeralBlocks,buildAuthRequiredPublicText,formatConnectionDisplayName}from"#public/channels/slack/connections.js";import{renderInputRequestBlocks}from"#public/channels/slack/hitl.js";const log=createLogger(`slack.defaults`);function defaultSlackAuth(e,t){let n=e.author;if(!n)return null;let r=e.teamId,i=n.isBot,a=n.userId,o=r?i?`slack:${r}:bot:${a}`:`slack:${r}:${a}`:i?`slack:bot:${a}`:`slack:${a}`,s={author_type:i?`bot`:`user`,channel_id:t.slack.channelId,thread_ts:t.slack.threadTs,user_id:a};return n.userName&&(s.user_name=n.userName),n.fullName&&(s.full_name=n.fullName),r!==void 0&&(s.team_id=r),{attributes:s,authenticator:`slack-webhook`,issuer:r===void 0?`slack`:`slack:${r}`,principalId:o,principalType:i?`service`:`user`}}async function defaultOnAppMention(e,t){return await e.thread.startTyping(`Thinking...`),{auth:defaultSlackAuth(t,e)}}async function defaultOnDirectMessage(e,t){return await e.thread.startTyping(`Thinking...`),{auth:defaultSlackAuth(t,e)}}function firstNonEmptyLine(e){for(let t of e.split(/\r?\n/u)){let e=t.trim();if(e.length>0)return e}}function defaultInputRequestedHandler(){return async(e,t)=>{if(e.requests.length===0)return;let
|
|
2
|
-
`));await t.thread.post({blocks:e.requests.flatMap(renderInputRequestBlocks),text:
|
|
3
|
-
`))},async"session.failed"(e,r){let
|
|
4
|
-
`))},async"authorization.required"(e,t){let
|
|
1
|
+
import{createLogger,extractErrorId,formatErrorHint}from"#internal/logging.js";import{truncateMessageText,truncateTypingStatus}from"#public/channels/slack/limits.js";import{buildAuthCompletedText,buildAuthEphemeralBlocks,buildAuthRequiredPublicText,formatConnectionDisplayName}from"#public/channels/slack/connections.js";import{renderInputRequestBlocks}from"#public/channels/slack/hitl.js";const log=createLogger(`slack.defaults`);function defaultSlackAuth(e,t){let n=e.author;if(!n)return null;let r=e.teamId,i=n.isBot,a=n.userId,o=r?i?`slack:${r}:bot:${a}`:`slack:${r}:${a}`:i?`slack:bot:${a}`:`slack:${a}`,s={author_type:i?`bot`:`user`,channel_id:t.slack.channelId,thread_ts:t.slack.threadTs,user_id:a};return n.userName&&(s.user_name=n.userName),n.fullName&&(s.full_name=n.fullName),r!==void 0&&(s.team_id=r),{attributes:s,authenticator:`slack-webhook`,issuer:r===void 0?`slack`:`slack:${r}`,principalId:o,principalType:i?`service`:`user`}}async function defaultOnAppMention(e,t){return await e.thread.startTyping(`Thinking...`),{auth:defaultSlackAuth(t,e)}}async function defaultOnDirectMessage(e,t){return await e.thread.startTyping(`Thinking...`),{auth:defaultSlackAuth(t,e)}}function firstNonEmptyLine(e){for(let t of e.split(/\r?\n/u)){let e=t.trim();if(e.length>0)return e}}function defaultInputRequestedHandler(){return async(e,t,n)=>{if(e.requests.length===0)return;let i=truncateMessageText(e.requests.map(e=>e.prompt).join(`
|
|
2
|
+
`));await t.thread.post({blocks:e.requests.flatMap(renderInputRequestBlocks),text:i})}}const defaultEvents={async"turn.started"(e,t,n){t.state.pendingToolCallMessage=null,await t.thread.startTyping(`Working...`)},async"actions.requested"(e,t,n){let r=t.state.pendingToolCallMessage;if(t.state.pendingToolCallMessage=null,r){await t.thread.startTyping(truncateTypingStatus(r));return}let a=e.actions.map(e=>e.kind===`tool-call`?e.toolName:e.kind);await t.thread.startTyping(truncateTypingStatus(`Running ${a.join(`, `)}...`))},async"message.completed"(e,t,n){if(e.finishReason===`tool-calls`){t.state.pendingToolCallMessage=e.message?firstNonEmptyLine(e.message)??null:null;return}t.state.pendingToolCallMessage=null,e.message&&await t.thread.post(e.message)},async"turn.failed"(e,r,i){let a=formatErrorHint(e),o=extractErrorId(e.details);await r.thread.post([`I hit an error while handling your request${a}.`,``,`Please try again, rephrase, or reach out if it keeps failing.`,...o?[``,`_Error id: \`${o}\`_`]:[]].join(`
|
|
3
|
+
`))},async"session.failed"(e,r,i){let a=formatErrorHint(e),o=extractErrorId(e.details);await r.thread.post([`This session couldn't recover from an error${a}.`,``,`Start a new thread to continue — I can't pick this one back up.`,...o?[``,`_Error id: \`${o}\`_`]:[]].join(`
|
|
4
|
+
`))},async"authorization.required"(e,t,n){let r=formatConnectionDisplayName(e.name),i=t.state.triggeringUserId??null,a=e.authorization?.url;if(i&&a)try{await t.thread.postEphemeral(i,{blocks:buildAuthEphemeralBlocks({displayName:r,url:a}),text:`Sign in with ${r}: ${a}`})}catch(t){log.error(`Slack auth ephemeral delivery failed`,{name:e.name,error:t})}let c=buildAuthRequiredPublicText({displayName:r,hasUser:i!==null});try{let n=await t.thread.post(c);n.id&&(t.state.pendingAuthMessageTs={...t.state.pendingAuthMessageTs,[e.name]:n.id})}catch(t){log.error(`Slack auth public message delivery failed`,{name:e.name,error:t})}},async"authorization.completed"(e,t,n){let r=t.state.pendingAuthMessageTs??{},i=r[e.name];if(i===void 0)return;let o=buildAuthCompletedText({displayName:formatConnectionDisplayName(e.name),outcome:e.outcome,reason:e.reason});try{await t.slack.request(`chat.update`,{channel:t.slack.channelId,ts:i,text:o})}catch(t){log.error(`Slack auth status edit failed`,{name:e.name,error:t})}let s={...r};delete s[e.name],t.state.pendingAuthMessageTs=s}};export{defaultEvents,defaultInputRequestedHandler,defaultOnAppMention,defaultOnDirectMessage,defaultSlackAuth};
|