@kelloggt/dsh-program-agents 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +113 -2
- package/cordis.patch.yml +5 -0
- package/dist/app/agents.d.ts +50 -0
- package/dist/app/agents.js +70 -0
- package/dist/app/agents.js.map +1 -0
- package/dist/app/conversations.d.ts +8 -0
- package/dist/app/conversations.js +26 -0
- package/dist/app/conversations.js.map +1 -0
- package/dist/app/endpoints.d.ts +36 -0
- package/dist/app/endpoints.js +162 -0
- package/dist/app/endpoints.js.map +1 -0
- package/dist/app/inbox.d.ts +61 -0
- package/dist/app/inbox.js +253 -0
- package/dist/app/inbox.js.map +1 -0
- package/dist/app/index.d.ts +70 -0
- package/dist/app/index.js +147 -0
- package/dist/app/index.js.map +1 -0
- package/dist/app/mailbox.d.ts +63 -0
- package/dist/app/mailbox.js +173 -0
- package/dist/app/mailbox.js.map +1 -0
- package/dist/app/models.d.ts +32 -0
- package/dist/app/models.js +13 -0
- package/dist/app/models.js.map +1 -0
- package/dist/app/server.d.ts +36 -0
- package/dist/app/server.js +233 -0
- package/dist/app/server.js.map +1 -0
- package/dist/app/stream-server.d.ts +9 -0
- package/dist/app/stream-server.js +77 -0
- package/dist/app/stream-server.js.map +1 -0
- package/dist/app/stream.d.ts +4 -0
- package/dist/app/stream.js +93 -0
- package/dist/app/stream.js.map +1 -0
- package/dist/app/tools.d.ts +16 -0
- package/dist/app/tools.js +24 -0
- package/dist/app/tools.js.map +1 -0
- package/dist/client/controller.d.ts +21 -0
- package/dist/client/controller.js +140 -0
- package/dist/client/controller.js.map +1 -0
- package/dist/client/index.d.ts +22 -0
- package/dist/client/index.js +14 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/model.d.ts +66 -0
- package/dist/client/model.js +147 -0
- package/dist/client/model.js.map +1 -0
- package/dist/client/models.d.ts +11 -0
- package/dist/client/models.js +27 -0
- package/dist/client/models.js.map +1 -0
- package/dist/client/transport.d.ts +64 -0
- package/dist/client/transport.js +141 -0
- package/dist/client/transport.js.map +1 -0
- package/dist/client/view.d.ts +19 -0
- package/dist/client/view.js +69 -0
- package/dist/client/view.js.map +1 -0
- package/dist/client.js +673 -0
- package/dist/conversations.d.ts +92 -0
- package/dist/conversations.js +6 -0
- package/dist/conversations.js.map +1 -0
- package/dist/endpoints.d.ts +48 -0
- package/dist/endpoints.js +43 -0
- package/dist/endpoints.js.map +1 -0
- package/dist/examples/dashboard.js +42 -0
- package/dist/examples/dynamic-server.js +27 -0
- package/dist/examples/inbox-worker.js +22 -0
- package/dist/examples/model-slots.js +19 -0
- package/dist/examples/standalone-supervisor.js +20 -0
- package/dist/host/adapter.d.ts +195 -0
- package/dist/host/adapter.js +630 -0
- package/dist/host/adapter.js.map +1 -0
- package/dist/host/agents.d.ts +35 -0
- package/dist/host/agents.js +222 -0
- package/dist/host/agents.js.map +1 -0
- package/dist/host/audit.d.ts +14 -0
- package/dist/host/audit.js +39 -0
- package/dist/host/audit.js.map +1 -0
- package/dist/host/catalog.d.ts +53 -0
- package/dist/host/catalog.js +116 -0
- package/dist/host/catalog.js.map +1 -0
- package/dist/host/contracts.d.ts +38 -0
- package/dist/host/contracts.js +2 -0
- package/dist/host/contracts.js.map +1 -0
- package/dist/host/conversations.d.ts +41 -0
- package/dist/host/conversations.js +324 -0
- package/dist/host/conversations.js.map +1 -0
- package/dist/host/events.d.ts +12 -0
- package/dist/host/events.js +106 -0
- package/dist/host/events.js.map +1 -0
- package/dist/host/http.d.ts +14 -0
- package/dist/host/http.js +30 -0
- package/dist/host/http.js.map +1 -0
- package/dist/host/index.d.ts +23 -0
- package/dist/host/index.js +84 -0
- package/dist/host/index.js.map +1 -0
- package/dist/host/installed-v10.js +19797 -0
- package/dist/host/installed-v11.js +19797 -0
- package/dist/host/installed-v12.js +19797 -0
- package/dist/host/installed-v13.js +19797 -0
- package/dist/host/installed-v2.js +19797 -0
- package/dist/host/installed-v3.js +19797 -0
- package/dist/host/installed-v4.js +19797 -0
- package/dist/host/installed-v5.js +19797 -0
- package/dist/host/installed-v6.js +19797 -0
- package/dist/host/installed-v7.js +19797 -0
- package/dist/host/installed-v8.js +19797 -0
- package/dist/host/installed-v9.js +19797 -0
- package/dist/host/mcp.d.ts +11 -0
- package/dist/host/mcp.js +42 -0
- package/dist/host/mcp.js.map +1 -0
- package/dist/host/models.d.ts +37 -0
- package/dist/host/models.js +141 -0
- package/dist/host/models.js.map +1 -0
- package/dist/host/operations.d.ts +22 -0
- package/dist/host/operations.js +72 -0
- package/dist/host/operations.js.map +1 -0
- package/dist/host/plugin.js +19797 -0
- package/dist/host/policies.d.ts +36 -0
- package/dist/host/policies.js +73 -0
- package/dist/host/policies.js.map +1 -0
- package/dist/host/requests.d.ts +57 -0
- package/dist/host/requests.js +353 -0
- package/dist/host/requests.js.map +1 -0
- package/dist/host/tool-client.d.ts +19 -0
- package/dist/host/tool-client.js +62 -0
- package/dist/host/tool-client.js.map +1 -0
- package/dist/host/validation.d.ts +5 -0
- package/dist/host/validation.js +42 -0
- package/dist/host/validation.js.map +1 -0
- package/dist/mcp.d.ts +7 -0
- package/dist/mcp.js +2 -0
- package/dist/mcp.js.map +1 -0
- package/dist/models.d.ts +34 -0
- package/dist/models.js +53 -0
- package/dist/models.js.map +1 -0
- package/dist/monitoring/error-buffer.d.ts +89 -0
- package/dist/monitoring/error-buffer.js +355 -0
- package/dist/monitoring/error-buffer.js.map +1 -0
- package/dist/monitoring/fatal.d.ts +6 -0
- package/dist/monitoring/fatal.js +32 -0
- package/dist/monitoring/fatal.js.map +1 -0
- package/dist/monitoring/index.d.ts +7 -0
- package/dist/monitoring/index.js +8 -0
- package/dist/monitoring/index.js.map +1 -0
- package/dist/monitoring/logger.d.ts +45 -0
- package/dist/monitoring/logger.js +174 -0
- package/dist/monitoring/logger.js.map +1 -0
- package/dist/monitoring/schemas.d.ts +34 -0
- package/dist/monitoring/schemas.js +132 -0
- package/dist/monitoring/schemas.js.map +1 -0
- package/dist/monitoring/standard.d.ts +51 -0
- package/dist/monitoring/standard.js +140 -0
- package/dist/monitoring/standard.js.map +1 -0
- package/dist/monitoring/tailer.d.ts +41 -0
- package/dist/monitoring/tailer.js +210 -0
- package/dist/monitoring/tailer.js.map +1 -0
- package/dist/requests.d.ts +92 -0
- package/dist/requests.js +120 -0
- package/dist/requests.js.map +1 -0
- package/dist/runtime/bootstrap.d.ts +1 -0
- package/dist/runtime/bootstrap.js +25 -0
- package/dist/runtime/bootstrap.js.map +1 -0
- package/dist/runtime/definitions.d.ts +7 -0
- package/dist/runtime/definitions.js +138 -0
- package/dist/runtime/definitions.js.map +1 -0
- package/dist/runtime/index.d.ts +4 -0
- package/dist/runtime/index.js +4 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/log-file.d.ts +24 -0
- package/dist/runtime/log-file.js +124 -0
- package/dist/runtime/log-file.js.map +1 -0
- package/dist/runtime/protocol.d.ts +60 -0
- package/dist/runtime/protocol.js +155 -0
- package/dist/runtime/protocol.js.map +1 -0
- package/dist/runtime/registry.d.ts +8 -0
- package/dist/runtime/registry.js +38 -0
- package/dist/runtime/registry.js.map +1 -0
- package/dist/runtime/supervisor.d.ts +74 -0
- package/dist/runtime/supervisor.js +881 -0
- package/dist/runtime/supervisor.js.map +1 -0
- package/dist/sdk/connection.d.ts +33 -0
- package/dist/sdk/connection.js +265 -0
- package/dist/sdk/connection.js.map +1 -0
- package/dist/sdk/index.d.ts +8 -0
- package/dist/sdk/index.js +2 -0
- package/dist/sdk/index.js.map +1 -0
- package/dist/supervision.d.ts +22 -0
- package/dist/supervision.js +32 -0
- package/dist/supervision.js.map +1 -0
- package/dist/types.d.ts +170 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/docs/installation.md +35 -0
- package/docs/log-format.md +102 -0
- package/docs/publishing.md +45 -0
- package/docs/supervision.md +73 -0
- package/examples/dashboard.ts +31 -0
- package/examples/dsh-web-local.patch.yml +15 -0
- package/examples/dynamic-server.ts +13 -0
- package/examples/inbox-worker.ts +17 -0
- package/examples/model-slots.ts +12 -0
- package/examples/standalone-supervisor.ts +14 -0
- package/examples/watch-server.ts +12 -0
- package/package.json +131 -3
- package/schemas/dsh.http.request.v1.schema.json +14 -0
- package/schemas/dsh.program.error-batch.v1.schema.json +24 -0
- package/schemas/dsh.program.error.v1.schema.json +8 -0
- package/schemas/standard-log-record.v1.schema.json +26 -0
- package/skills/program-agents/SKILL.md +124 -0
- package/skills/program-agents/chat-sidebar.md +23 -0
- package/skills/program-agents/endpoints.md +92 -0
- package/skills/program-agents/inboxes.md +120 -0
- package/skills/program-agents/log-format.md +102 -0
- package/skills/program-agents/models.md +59 -0
- package/skills/program-agents/references/chat-sidebar/AgentSidebar.tsx +181 -0
- package/skills/program-agents/references/chat-sidebar/README.md +181 -0
- package/skills/program-agents/references/chat-sidebar/agent-sidebar.css +37 -0
- package/skills/program-agents/references/chat-sidebar/backend.ts +184 -0
- package/skills/program-agents/references/chat-sidebar/protocol.ts +214 -0
- package/skills/program-agents/schemas/dsh.http.request.v1.schema.json +14 -0
- package/skills/program-agents/schemas/dsh.program.error-batch.v1.schema.json +24 -0
- package/skills/program-agents/schemas/dsh.program.error.v1.schema.json +8 -0
- package/skills/program-agents/schemas/standard-log-record.v1.schema.json +26 -0
- package/skills/program-agents/ui-patterns.md +39 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tim Kellogg
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,114 @@
|
|
|
1
|
-
#
|
|
1
|
+
# DSH Program Agents
|
|
2
|
+
|
|
3
|
+
You know how subagents can send messages to each other, launch new subagents, call LLMs, use tools, etc? A lot of agents write non-trivial amounts of code just to operate normally. _What if an "agent" was just code?_
|
|
4
|
+
|
|
5
|
+
_**Program agents**_ — no agent loop, just regular software that a supervising agent can edit & manage.
|
|
6
|
+
|
|
7
|
+
This repo includes
|
|
8
|
+
* A typescript SDK for [DSH (DeepSeek Harness)](https://github.com/deepseek-ai/deepseek-harness) that wraps tool calling, LLM calling, inter-agent messaging, etc.
|
|
9
|
+
* A process manager that keeps the program agent running, or not, whatever you need
|
|
10
|
+
* Logging/monitoring so that errors get funneled to the supervising agent
|
|
11
|
+
* Named models for configurable software so you can upgrade models easily
|
|
12
|
+
* A skill to teach your agent how to write program agents
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
You'll need [Node.js](https://nodejs.org/) **22.18 or newer** and Git. This plugin currently targets **DSH 0.2.1-rc.2**; use that version for the setup below.
|
|
17
|
+
|
|
18
|
+
### 1. Install DSH
|
|
19
|
+
|
|
20
|
+
Install the [DSH CLI](https://github.com/deepseek-ai/deepseek-harness) and pnpm:
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
npm install -g @deepseek-ai/dsh@0.2.1-rc.2 pnpm@10
|
|
24
|
+
dsh web
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
DSH opens its web UI at `http://127.0.0.1:3080` and creates your web profile. Stop it with **Ctrl+C** while you install the plugins below.
|
|
28
|
+
|
|
29
|
+
### 2. Install Program Agents
|
|
30
|
+
|
|
31
|
+
The first npm release is being prepared. Once published, install it with:
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
dsh plugin --profile web add @kelloggt/dsh-program-agents
|
|
35
|
+
mkdir -p ~/.dsh/skills
|
|
36
|
+
cp -R ~/.dsh/profiles/web/node_modules/@kelloggt/dsh-program-agents/skills/program-agents ~/.dsh/skills/
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
This enables the plugin automatically. Until that release is available, build it from a checkout:
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
git clone https://github.com/tkellogg/dsh-program-agents.git
|
|
43
|
+
cd dsh-program-agents
|
|
44
|
+
pnpm install --frozen-lockfile
|
|
45
|
+
pnpm build
|
|
46
|
+
|
|
47
|
+
# Install the skill so DSH's agents know how to use program agents.
|
|
48
|
+
mkdir -p ~/.dsh/skills
|
|
49
|
+
cp -R skills/program-agents ~/.dsh/skills/
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Add the following to `~/.dsh/profiles/web/cordis.patch.yml`, keeping any existing entries. Replace `/absolute/path/to/dsh-program-agents` with the checkout's full path (run `pwd` to find it):
|
|
53
|
+
|
|
54
|
+
```yaml
|
|
55
|
+
- insert:
|
|
56
|
+
- id: program-agents
|
|
57
|
+
name: /absolute/path/to/dsh-program-agents/dist/host/index.js
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Keep the checkout in that location: DSH loads the built plugin from it.
|
|
61
|
+
|
|
62
|
+
Maintainers: see [Publishing to npm](docs/publishing.md) for the release steps.
|
|
63
|
+
|
|
64
|
+
### 3. Connect Codex or Claude
|
|
65
|
+
|
|
66
|
+
To use your ChatGPT or Claude subscription, install [dsh-plugin-subscriptions](https://github.com/V1ki/dsh-plugin-subscriptions):
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
dsh plugin --profile web add dsh-plugin-subscriptions
|
|
70
|
+
dsh web
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
In DSH, open **Settings → Subscriptions** and click **Connect** for the provider you want:
|
|
74
|
+
|
|
75
|
+
- **ChatGPT (Codex):** sign in with your ChatGPT account in the browser.
|
|
76
|
+
- **Claude:** imports your existing Claude Code login when available. Otherwise, follow the browser authorization flow; the Claude Code CLI is optional.
|
|
77
|
+
|
|
78
|
+
These providers use your subscription login, so no API key is needed. Open the session's model picker (`/model`) and select a model under **ChatGPT (Codex)** or **Claude (Subscription)**.
|
|
79
|
+
|
|
80
|
+
If you use another model provider, skip the subscriptions plugin and run `dsh web` after step 2. In a new chat, ask DSH to use the **program-agents** skill to build and start a program. The **Program agents** panel above the composer shows its status and Start/Stop/Restart controls.
|
|
81
|
+
|
|
82
|
+
## Why not just regular software?
|
|
83
|
+
* _Error handling_ — software that discovers problems and dynamically adapts
|
|
84
|
+
* _Slowly changing software_ — e.g. in data engineering you'll run into new data bugs or learn new things about the data in production. Program agents merely evolve to handle new situations
|
|
85
|
+
* _World-integrated agents_ — a collection of loops that poll web services like Github or Salesforce for events and messages and forward them onto a real agent
|
|
86
|
+
* _Heavily AI-integrated software_ — Regular software with agents and chatbots wired deeply throughout
|
|
87
|
+
|
|
88
|
+
## Default workflow
|
|
89
|
+
|
|
90
|
+
1. Build an app using a foreground **watch/reload** command.
|
|
91
|
+
2. Register its name, description, argv command, URLs and optional log path with `program_register`.
|
|
92
|
+
3. Start using `program_start` or the composer's **Start** button. The UI dispatches through the exact live parent's public tool pipeline; it does not create an unrelated shell/Agent.
|
|
93
|
+
4. Edit source; let the watcher reload rather than repeatedly restarting manually. Check application readiness using its actual health/API URL and logs.
|
|
94
|
+
|
|
95
|
+
## Development commands
|
|
96
|
+
|
|
97
|
+
Run `pnpm install --frozen-lockfile` after cloning, then use these scripts from the repository root:
|
|
98
|
+
|
|
99
|
+
| Command | Purpose |
|
|
100
|
+
| --- | --- |
|
|
101
|
+
| `pnpm build` | Build the host, browser client, SDK, and examples. |
|
|
102
|
+
| `pnpm typecheck` | Check the source TypeScript. |
|
|
103
|
+
| `pnpm typecheck:references` | Check the copyable application references. |
|
|
104
|
+
| `pnpm test` | Run the test suite. |
|
|
105
|
+
| `pnpm check` | Build, check references, and run tests. |
|
|
106
|
+
| `pnpm example` | Run the standalone supervisor example. |
|
|
107
|
+
| `pnpm release:pack` | Run all checks and create the release archive. |
|
|
108
|
+
| `pnpm release:inspect` | List the files in the current version's archive. |
|
|
109
|
+
| `pnpm release:publish:dry-run` | Preview publishing the archive. |
|
|
110
|
+
| `pnpm release:login` | Sign in to npm. |
|
|
111
|
+
| `pnpm release:publish` | Publish the existing archive publicly to npm. |
|
|
112
|
+
|
|
113
|
+
See [Publishing to npm](docs/publishing.md) for the release workflow.
|
|
2
114
|
|
|
3
|
-
This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { JsonValue } from '../types.js';
|
|
2
|
+
import { type AgentTarget, type ResponseSchema, type RequestRecord } from '../requests.js';
|
|
3
|
+
import { ProgramError } from '../runtime/protocol.js';
|
|
4
|
+
import type { AgentInfo, CallOptions, SendOptions } from './index.js';
|
|
5
|
+
export type { AgentTarget, ResponseSchema, JSONSchema, RequestStatus, ResponseReceipt } from '../requests.js';
|
|
6
|
+
export interface AgentRequestView {
|
|
7
|
+
requestId: string;
|
|
8
|
+
status: RequestRecord['status'];
|
|
9
|
+
agentId?: string;
|
|
10
|
+
createdAt: string;
|
|
11
|
+
deadline: string;
|
|
12
|
+
response?: JsonValue;
|
|
13
|
+
error?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface AskOptions extends CallOptions {
|
|
16
|
+
requestId?: string;
|
|
17
|
+
responseSchema?: ResponseSchema;
|
|
18
|
+
instructions?: string;
|
|
19
|
+
}
|
|
20
|
+
/** Runtime schema plus identity parser for statically typed ask results. A TS
|
|
21
|
+
* interface alone disappears at runtime and cannot validate an Agent reply. */
|
|
22
|
+
export interface AgentResponseType<T extends JsonValue> {
|
|
23
|
+
readonly schema: ResponseSchema;
|
|
24
|
+
parse(value: JsonValue): T;
|
|
25
|
+
}
|
|
26
|
+
export declare function responseType<T extends JsonValue>(schema: ResponseSchema): AgentResponseType<T>;
|
|
27
|
+
export declare const responseTypes: {
|
|
28
|
+
string: () => AgentResponseType<string>;
|
|
29
|
+
};
|
|
30
|
+
export declare class AgentRequestError extends ProgramError {
|
|
31
|
+
readonly requestId: string;
|
|
32
|
+
readonly status: RequestRecord['status'];
|
|
33
|
+
constructor(code: string, message: string, requestId: string, status: RequestRecord['status']);
|
|
34
|
+
}
|
|
35
|
+
export type AgentBridgeCall = <T = JsonValue>(method: string, input?: JsonValue, options?: CallOptions) => Promise<T>;
|
|
36
|
+
export interface ProgramAgents {
|
|
37
|
+
list(options?: CallOptions): Promise<AgentInfo[]>;
|
|
38
|
+
get(id: string, options?: CallOptions): Promise<AgentInfo | null>;
|
|
39
|
+
send(target: AgentTarget, body: JsonValue, options?: SendOptions): Promise<JsonValue>;
|
|
40
|
+
ask(target: AgentTarget, body: JsonValue, options?: AskOptions & {
|
|
41
|
+
responseSchema?: undefined;
|
|
42
|
+
}): Promise<string>;
|
|
43
|
+
ask<T extends JsonValue = JsonValue>(target: AgentTarget, body: JsonValue, options: AskOptions & {
|
|
44
|
+
responseSchema: ResponseSchema;
|
|
45
|
+
}): Promise<T>;
|
|
46
|
+
ask<T extends JsonValue>(target: AgentTarget, body: JsonValue, responseType: AgentResponseType<T>, options?: AskOptions): Promise<T>;
|
|
47
|
+
request(requestId: string, options?: CallOptions): Promise<AgentRequestView | null>;
|
|
48
|
+
cancel(requestId: string, options?: CallOptions): Promise<AgentRequestView>;
|
|
49
|
+
}
|
|
50
|
+
export declare function createProgramAgents(call: AgentBridgeCall): ProgramAgents;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { createResponseValidator } from '../requests.js';
|
|
3
|
+
import { ProgramError } from '../runtime/protocol.js';
|
|
4
|
+
export function responseType(schema) { const validate = createResponseValidator(schema); return { schema, parse(value) { validate(value); return value; } }; }
|
|
5
|
+
export const responseTypes = { string: () => responseType({ type: 'string' }) };
|
|
6
|
+
export class AgentRequestError extends ProgramError {
|
|
7
|
+
requestId;
|
|
8
|
+
status;
|
|
9
|
+
constructor(code, message, requestId, status) {
|
|
10
|
+
super(code, message);
|
|
11
|
+
this.requestId = requestId;
|
|
12
|
+
this.status = status;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export function createProgramAgents(call) {
|
|
16
|
+
const request = (requestId, options) => call('agents.request', { requestId }, options);
|
|
17
|
+
const cancel = (requestId, options) => call('agents.cancel', { requestId }, options);
|
|
18
|
+
const ask = async (target, body, options = {}) => {
|
|
19
|
+
const timeout = options.timeoutMs ?? 120000;
|
|
20
|
+
if (!Number.isSafeInteger(timeout) || timeout < 1 || timeout > 3600000)
|
|
21
|
+
throw new ProgramError('INVALID_INPUT', 'Agent ask timeout must be 1..3600000ms');
|
|
22
|
+
options.signal?.throwIfAborted();
|
|
23
|
+
const key = options.requestId ?? randomUUID();
|
|
24
|
+
// Compile before sending anything: invalid schemas cannot spend model tokens.
|
|
25
|
+
const validate = createResponseValidator(options.responseSchema);
|
|
26
|
+
let view;
|
|
27
|
+
const controller = new AbortController(), timer = setTimeout(() => controller.abort(new ProgramError('TIMEOUT', 'Agent response deadline exceeded')), timeout);
|
|
28
|
+
const abort = () => controller.abort(options.signal?.reason ?? new ProgramError('CANCELLED', 'Agent ask cancelled'));
|
|
29
|
+
options.signal?.addEventListener('abort', abort, { once: true });
|
|
30
|
+
try {
|
|
31
|
+
view = await call('agents.ask', { target: target, body, requestId: key, timeoutMs: timeout, ...options.responseSchema === undefined ? {} : { responseSchema: options.responseSchema }, ...options.instructions === undefined ? {} : { prompt: options.instructions } }, { signal: controller.signal, timeoutMs: Math.min(timeout, 120000) });
|
|
32
|
+
while (view.status === 'pending')
|
|
33
|
+
view = await call('agents.wait', { requestId: view.requestId, waitMs: Math.min(25000, Math.max(0, Date.parse(view.deadline) - Date.now())) }, { signal: controller.signal, timeoutMs: 30000 });
|
|
34
|
+
if (view.status === 'answered') {
|
|
35
|
+
validate(view.response);
|
|
36
|
+
return view.response;
|
|
37
|
+
}
|
|
38
|
+
throw new AgentRequestError(view.status === 'dispatch-unknown' ? 'DISPATCH_UNKNOWN' : view.status === 'expired' ? 'TIMEOUT' : view.status === 'cancelled' ? 'CANCELLED' : 'AGENT_REQUEST_FAILED', view.error ?? 'Agent request ' + view.status, view.requestId, view.status);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
// Cancellation only settles this response contract, never pretends the Agent's
|
|
42
|
+
// reasoning/external effects were undone. No message/delegation retries.
|
|
43
|
+
if (controller.signal.aborted) {
|
|
44
|
+
await cancel(view?.requestId ?? key, { timeoutMs: 5000 }).catch(() => { });
|
|
45
|
+
throw new AgentRequestError(options.signal?.aborted ? 'CANCELLED' : 'TIMEOUT', 'Agent ask interrupted; inspect the durable request before repeating work', view?.requestId ?? key, view?.status ?? 'pending');
|
|
46
|
+
}
|
|
47
|
+
if (error instanceof AgentRequestError)
|
|
48
|
+
throw error;
|
|
49
|
+
if (view?.status === 'answered' && error instanceof ProgramError && error.code === 'INVALID_RESPONSE')
|
|
50
|
+
throw error;
|
|
51
|
+
if (error instanceof ProgramError && !['TIMEOUT', 'CANCELLED', 'INVALID_RESPONSE', 'CALL_FAILED'].includes(error.code))
|
|
52
|
+
throw error;
|
|
53
|
+
throw new AgentRequestError('DISPATCH_UNKNOWN', 'Request transport failed; inspect using the request key before repeating work', view?.requestId ?? key, view?.status ?? 'dispatch-unknown');
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
clearTimeout(timer);
|
|
57
|
+
options.signal?.removeEventListener('abort', abort);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
return {
|
|
61
|
+
list: options => call('agents.list', null, options), get: (id, options) => call('agents.get', { id }, options),
|
|
62
|
+
send: (target, body, options = {}) => call('agents.send', { ...typeof target === 'string' ? { id: target } : { target: target }, body, ...options.correlationId === undefined ? {} : { correlationId: options.correlationId } }, options),
|
|
63
|
+
ask: ((target, body, typeOrOptions = {}, options = {}) => {
|
|
64
|
+
if ('schema' in typeOrOptions && 'parse' in typeOrOptions)
|
|
65
|
+
return ask(target, body, { ...options, responseSchema: typeOrOptions.schema }).then(value => typeOrOptions.parse(value));
|
|
66
|
+
return ask(target, body, typeOrOptions);
|
|
67
|
+
}), request, cancel,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=agents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agents.js","sourceRoot":"","sources":["../../src/app/agents.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { JsonValue } from '../types.js';
|
|
2
|
+
import type { CallOptions } from './index.js';
|
|
3
|
+
import type { ProgramConversations } from '../conversations.js';
|
|
4
|
+
export type * from '../conversations.js';
|
|
5
|
+
export type ConversationBridgeCall = <T = JsonValue>(method: string, input?: JsonValue, options?: CallOptions) => Promise<T>;
|
|
6
|
+
export type ConversationBridgeStream = (method: string, input?: JsonValue, options?: CallOptions) => AsyncIterable<JsonValue>;
|
|
7
|
+
/** App-side handles contain no credentials, Agent objects, model calls, or tool loop. */
|
|
8
|
+
export declare function createProgramConversations(call: ConversationBridgeCall, stream: ConversationBridgeStream): ProgramConversations;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { ProgramError } from '../runtime/protocol.js';
|
|
3
|
+
/** App-side handles contain no credentials, Agent objects, model calls, or tool loop. */
|
|
4
|
+
export function createProgramConversations(call, stream) {
|
|
5
|
+
const sendInput = (id, message, options = {}) => ({
|
|
6
|
+
id, message: message, requestId: options.requestId ?? randomUUID(), ...(options.mode === undefined ? {} : { mode: options.mode }),
|
|
7
|
+
});
|
|
8
|
+
const handle = (info) => Object.freeze({
|
|
9
|
+
...info,
|
|
10
|
+
history: (options, callOptions) => call('conversations.history', { id: info.id, ...options }, callOptions),
|
|
11
|
+
events: (options = {}) => stream('conversations.events', { id: info.id, ...(options.maxMessages === undefined ? {} : { maxMessages: options.maxMessages }) }, { signal: options.signal }),
|
|
12
|
+
send: (message, options = {}) => call('conversations.send', sendInput(info.id, message, options), { signal: options.signal }),
|
|
13
|
+
ask: (message, options = {}) => {
|
|
14
|
+
if ('mode' in options && options.mode !== 'queue')
|
|
15
|
+
throw new ProgramError('INVALID_INPUT', 'Conversation ask requires a queued, distinct turn; use send for steering');
|
|
16
|
+
return stream('conversations.ask', sendInput(info.id, message, { requestId: options.requestId, signal: options.signal }), { signal: options.signal });
|
|
17
|
+
},
|
|
18
|
+
interrupt: options => call('conversations.interrupt', { id: info.id }, options),
|
|
19
|
+
});
|
|
20
|
+
return {
|
|
21
|
+
open: async (options, callOptions) => handle(await call('conversations.open', options, callOptions)),
|
|
22
|
+
get: async (id, options) => { const info = await call('conversations.get', { id }, options); return info ? handle(info) : null; },
|
|
23
|
+
list: options => call('conversations.list', {}, options),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=conversations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversations.js","sourceRoot":"","sources":["../../src/app/conversations.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Server } from 'node:net';
|
|
2
|
+
import type { ProgramEndpointBinding } from '../endpoints.js';
|
|
3
|
+
import type { JsonValue } from '../types.js';
|
|
4
|
+
import type { CallOptions } from './index.js';
|
|
5
|
+
export type { ProgramEndpointBinding } from '../endpoints.js';
|
|
6
|
+
export interface PublishEndpointOptions {
|
|
7
|
+
port: number;
|
|
8
|
+
}
|
|
9
|
+
export interface UnpublishEndpointOptions {
|
|
10
|
+
port?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface ListeningProgramEndpoint {
|
|
13
|
+
port: number;
|
|
14
|
+
/** Authoritative bind host, not a browser-facing hostname. */
|
|
15
|
+
host: string;
|
|
16
|
+
/** Local verification URL; wildcard binds use their loopback equivalent. */
|
|
17
|
+
localUrl: string;
|
|
18
|
+
/** Close this listener and await its conditional unpublication. Idempotent. */
|
|
19
|
+
close(): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
export interface ProgramEndpoints {
|
|
22
|
+
list(options?: CallOptions): Promise<ProgramEndpointBinding[]>;
|
|
23
|
+
publish(id: string, address: PublishEndpointOptions, options?: CallOptions): Promise<ProgramEndpointBinding>;
|
|
24
|
+
unpublish(id: string, address?: UnpublishEndpointOptions, options?: CallOptions): Promise<ProgramEndpointBinding>;
|
|
25
|
+
/** Bind using current host configuration, then advertise the actual OS port. */
|
|
26
|
+
listen(id: string, server: Server, options?: CallOptions): Promise<ListeningProgramEndpoint>;
|
|
27
|
+
}
|
|
28
|
+
export type EndpointBridgeCall = <T = JsonValue>(method: string, input?: JsonValue, options?: CallOptions) => Promise<T>;
|
|
29
|
+
/** Typed endpoint bridge helpers. Calls are never retried and use existing call cancellation. */
|
|
30
|
+
export declare function createProgramEndpoints(call: EndpointBridgeCall): ProgramEndpoints;
|
|
31
|
+
/**
|
|
32
|
+
* Bind a fresh Node net/http/https server using authoritative bridge configuration.
|
|
33
|
+
* Existing published ports and the app's environment do not choose the bind address.
|
|
34
|
+
* Frameworks managing their own listeners can instead use list/publish/unpublish.
|
|
35
|
+
*/
|
|
36
|
+
export declare function listenProgramEndpoint(endpoints: Pick<ProgramEndpoints, 'list' | 'publish' | 'unpublish'>, id: string, server: Server, options?: CallOptions): Promise<ListeningProgramEndpoint>;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { endpointName } from '../endpoints.js';
|
|
2
|
+
import { ProgramError } from '../runtime/protocol.js';
|
|
3
|
+
const managedServers = new WeakSet();
|
|
4
|
+
const cancelled = () => new ProgramError('CANCELLED', 'Endpoint listen was cancelled');
|
|
5
|
+
const unavailable = () => new ProgramError('UNAVAILABLE', 'Endpoint server closed during setup');
|
|
6
|
+
const unbracket = (host) => host.startsWith('[') && host.endsWith(']') ? host.slice(1, -1) : host;
|
|
7
|
+
/** Typed endpoint bridge helpers. Calls are never retried and use existing call cancellation. */
|
|
8
|
+
export function createProgramEndpoints(call) {
|
|
9
|
+
const endpoints = {
|
|
10
|
+
list: (options) => call('endpoints.list', null, options),
|
|
11
|
+
publish: (id, { port }, options) => call('endpoints.publish', { id, port }, options),
|
|
12
|
+
unpublish: (id, { port } = {}, options) => call('endpoints.unpublish', { id, ...(port === undefined ? {} : { port }) }, options),
|
|
13
|
+
listen: (id, server, options) => listenProgramEndpoint(endpoints, id, server, options),
|
|
14
|
+
};
|
|
15
|
+
return endpoints;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Bind a fresh Node net/http/https server using authoritative bridge configuration.
|
|
19
|
+
* Existing published ports and the app's environment do not choose the bind address.
|
|
20
|
+
* Frameworks managing their own listeners can instead use list/publish/unpublish.
|
|
21
|
+
*/
|
|
22
|
+
export async function listenProgramEndpoint(endpoints, id, server, options = {}) {
|
|
23
|
+
if (!endpointName(id))
|
|
24
|
+
throw new ProgramError('INVALID_INPUT', 'Invalid endpoint id');
|
|
25
|
+
if (server.listening || managedServers.has(server))
|
|
26
|
+
throw new ProgramError('BUSY', 'Endpoint helper requires an unbound, unmanaged server');
|
|
27
|
+
managedServers.add(server);
|
|
28
|
+
let attemptedBind = false;
|
|
29
|
+
let closed = false;
|
|
30
|
+
let setupComplete = false;
|
|
31
|
+
let setupFailed = false;
|
|
32
|
+
let setupError;
|
|
33
|
+
let port;
|
|
34
|
+
let publication;
|
|
35
|
+
let unpublication;
|
|
36
|
+
let closing;
|
|
37
|
+
let rejectSetup;
|
|
38
|
+
let resolveListening;
|
|
39
|
+
const interrupted = new Promise((_resolve, reject) => { rejectSetup = reject; });
|
|
40
|
+
// An error can arrive between awaited setup stages, before the next race attaches.
|
|
41
|
+
void interrupted.catch(() => { });
|
|
42
|
+
const listening = new Promise((resolve) => { resolveListening = resolve; });
|
|
43
|
+
const onListening = () => resolveListening();
|
|
44
|
+
const failSetup = (error) => {
|
|
45
|
+
if (!setupFailed) {
|
|
46
|
+
setupFailed = true;
|
|
47
|
+
setupError = error;
|
|
48
|
+
}
|
|
49
|
+
rejectSetup(error);
|
|
50
|
+
};
|
|
51
|
+
const onError = (error) => failSetup(error);
|
|
52
|
+
const onAbort = () => failSetup(cancelled());
|
|
53
|
+
const unpublish = () => {
|
|
54
|
+
if (!publication || port === undefined)
|
|
55
|
+
return Promise.resolve();
|
|
56
|
+
// A close during publication must unpublish AFTER that call settles, not before it.
|
|
57
|
+
return unpublication ??= (async () => {
|
|
58
|
+
await publication.catch(() => { });
|
|
59
|
+
await endpoints.unpublish(id, { port });
|
|
60
|
+
})();
|
|
61
|
+
};
|
|
62
|
+
const onClose = () => {
|
|
63
|
+
closed = true;
|
|
64
|
+
if (!setupComplete)
|
|
65
|
+
failSetup(unavailable());
|
|
66
|
+
else
|
|
67
|
+
managedServers.delete(server);
|
|
68
|
+
// Event callbacks cannot report a rejection; handle.close() still awaits/reports it.
|
|
69
|
+
if (publication)
|
|
70
|
+
void unpublish().catch(() => { });
|
|
71
|
+
};
|
|
72
|
+
const removeSetupListeners = () => {
|
|
73
|
+
server.removeListener('listening', onListening);
|
|
74
|
+
server.removeListener('error', onError);
|
|
75
|
+
options.signal?.removeEventListener('abort', onAbort);
|
|
76
|
+
};
|
|
77
|
+
const closeServer = () => new Promise((resolve, reject) => {
|
|
78
|
+
// Calling close also cancels an in-progress DNS/bind attempt. Do not merely test
|
|
79
|
+
// server.listening: it is false while listen is still acquiring its socket.
|
|
80
|
+
try {
|
|
81
|
+
server.close((error) => {
|
|
82
|
+
if (error && error.code !== 'ERR_SERVER_NOT_RUNNING')
|
|
83
|
+
reject(error);
|
|
84
|
+
else
|
|
85
|
+
resolve();
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
reject(error);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
try {
|
|
93
|
+
const bindings = await endpoints.list(options);
|
|
94
|
+
const binding = Array.isArray(bindings) ? bindings.find((item) => item?.id === id) : undefined;
|
|
95
|
+
if (!binding)
|
|
96
|
+
throw new ProgramError('NOT_FOUND', `Endpoint ${id} is not configured`);
|
|
97
|
+
if (typeof binding.bindHost !== 'string' || !binding.bindHost || !Number.isSafeInteger(binding.requestedPort) || binding.requestedPort < 0 || binding.requestedPort > 65535 || !['http', 'https'].includes(binding.protocol))
|
|
98
|
+
throw new ProgramError('INVALID_RESPONSE', 'Invalid endpoint binding configuration');
|
|
99
|
+
if (options.signal?.aborted)
|
|
100
|
+
throw cancelled();
|
|
101
|
+
// Do not take ownership of a server that somebody bound while list was pending.
|
|
102
|
+
if (server.listening)
|
|
103
|
+
throw new ProgramError('BUSY', 'Endpoint server was bound during configuration lookup');
|
|
104
|
+
server.once('listening', onListening);
|
|
105
|
+
server.on('error', onError);
|
|
106
|
+
server.once('close', onClose);
|
|
107
|
+
options.signal?.addEventListener('abort', onAbort, { once: true });
|
|
108
|
+
attemptedBind = true;
|
|
109
|
+
server.listen({ port: binding.requestedPort, host: unbracket(binding.bindHost) });
|
|
110
|
+
await Promise.race([listening, interrupted]);
|
|
111
|
+
if (setupFailed)
|
|
112
|
+
throw setupError;
|
|
113
|
+
if (options.signal?.aborted)
|
|
114
|
+
throw cancelled();
|
|
115
|
+
if (closed || !server.listening)
|
|
116
|
+
throw unavailable();
|
|
117
|
+
const address = server.address();
|
|
118
|
+
if (!address || typeof address === 'string' || !Number.isSafeInteger(address.port) || address.port < 1 || address.port > 65535)
|
|
119
|
+
throw new ProgramError('UNAVAILABLE', 'Endpoint server did not bind a TCP port');
|
|
120
|
+
port = address.port;
|
|
121
|
+
publication = endpoints.publish(id, { port }, options);
|
|
122
|
+
await Promise.race([publication, interrupted]);
|
|
123
|
+
if (setupFailed)
|
|
124
|
+
throw setupError;
|
|
125
|
+
if (options.signal?.aborted)
|
|
126
|
+
throw cancelled();
|
|
127
|
+
if (closed || !server.listening)
|
|
128
|
+
throw unavailable();
|
|
129
|
+
setupComplete = true;
|
|
130
|
+
removeSetupListeners();
|
|
131
|
+
let localHost = unbracket(binding.bindHost);
|
|
132
|
+
if (localHost === '0.0.0.0')
|
|
133
|
+
localHost = '127.0.0.1';
|
|
134
|
+
else if (localHost === '::')
|
|
135
|
+
localHost = '::1';
|
|
136
|
+
if (localHost.includes(':'))
|
|
137
|
+
localHost = `[${localHost}]`;
|
|
138
|
+
return {
|
|
139
|
+
port,
|
|
140
|
+
host: binding.bindHost,
|
|
141
|
+
localUrl: `${binding.protocol}://${localHost}:${port}/`,
|
|
142
|
+
close: () => closing ??= (async () => {
|
|
143
|
+
if (!closed)
|
|
144
|
+
await closeServer();
|
|
145
|
+
await unpublish();
|
|
146
|
+
})(),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
setupComplete = true;
|
|
151
|
+
removeSetupListeners();
|
|
152
|
+
// Publish failures/cancellation have potentially unknown effects, so compensate
|
|
153
|
+
// once with the exact port. A failed bind never publishes or unpublishes.
|
|
154
|
+
if (attemptedBind && !closed)
|
|
155
|
+
await closeServer().catch(() => { });
|
|
156
|
+
await unpublish().catch(() => { });
|
|
157
|
+
server.removeListener('close', onClose);
|
|
158
|
+
managedServers.delete(server);
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=endpoints.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"endpoints.js","sourceRoot":"","sources":["../../src/app/endpoints.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { JsonValue } from '../types.js';
|
|
2
|
+
import { ProgramError } from '../runtime/protocol.js';
|
|
3
|
+
import type { CallOptions } from './index.js';
|
|
4
|
+
import type { InboxMessage, MessageReceipt, ReceiveOptions } from './mailbox.js';
|
|
5
|
+
export type InboxBridgeCall = <T = JsonValue>(method: string, input?: JsonValue, options?: CallOptions) => Promise<T>;
|
|
6
|
+
export interface InboxReceiveOptions extends ReceiveOptions, CallOptions {
|
|
7
|
+
}
|
|
8
|
+
export interface ManagedReceiveOptions extends InboxReceiveOptions {
|
|
9
|
+
/** Callback deadline, independently of individual bridge calls. Default 120s; maximum 1h. */
|
|
10
|
+
processingTimeoutMs?: number;
|
|
11
|
+
}
|
|
12
|
+
export type ReadonlyInboxJson = string | number | boolean | null | readonly ReadonlyInboxJson[] | {
|
|
13
|
+
readonly [key: string]: ReadonlyInboxJson;
|
|
14
|
+
};
|
|
15
|
+
/** The callback cannot access the claim token or any settlement operation. */
|
|
16
|
+
export type HandlerMessage = Readonly<Omit<InboxMessage, 'leaseToken' | 'body' | 'receipt'>> & {
|
|
17
|
+
readonly body: ReadonlyInboxJson;
|
|
18
|
+
readonly receipt: Readonly<MessageReceipt>;
|
|
19
|
+
};
|
|
20
|
+
export interface InboxHandlerContext {
|
|
21
|
+
readonly signal: AbortSignal;
|
|
22
|
+
}
|
|
23
|
+
export type InboxHandler<R> = (message: HandlerMessage, context: InboxHandlerContext) => R | PromiseLike<R>;
|
|
24
|
+
type Claim = Pick<InboxMessage, 'id' | 'leaseToken'>;
|
|
25
|
+
export interface ProgramInbox {
|
|
26
|
+
receive<R>(handler: InboxHandler<R>): Promise<R | null>;
|
|
27
|
+
receive<R>(options: ManagedReceiveOptions | undefined, handler: InboxHandler<R>): Promise<R | null>;
|
|
28
|
+
/** @deprecated Prefer receive(options, handler), which owns renewal and settlement. */
|
|
29
|
+
receive(options?: InboxReceiveOptions): Promise<InboxMessage | null>;
|
|
30
|
+
/** Low-level manual claim: the caller must renew and settle its own live lease. */
|
|
31
|
+
claim(options?: InboxReceiveOptions): Promise<InboxMessage | null>;
|
|
32
|
+
/** Low-level renewal, fenced by the exact live claim token and run. */
|
|
33
|
+
renew(message: Claim, leaseMs: number, options?: CallOptions): Promise<MessageReceipt>;
|
|
34
|
+
/** Low-level, single-attempt ACK. A rejected call may already have committed. */
|
|
35
|
+
ack(message: Claim | string, leaseToken?: string, options?: CallOptions): Promise<MessageReceipt>;
|
|
36
|
+
/** Low-level, single-attempt NACK. Requires the exact, unexpired claim. */
|
|
37
|
+
nack(message: Claim | string, leaseToken?: string, options?: CallOptions): Promise<MessageReceipt>;
|
|
38
|
+
/** Low-level durable status lookup; acceptance is not successful processing. */
|
|
39
|
+
receipt(id: string, options?: CallOptions): Promise<MessageReceipt | null>;
|
|
40
|
+
}
|
|
41
|
+
/** ACK may have committed before its response was lost. Neither retry nor NACK is safe. */
|
|
42
|
+
export declare class SettlementUnknownError extends ProgramError {
|
|
43
|
+
readonly messageId: string;
|
|
44
|
+
readonly outcome: "unknown";
|
|
45
|
+
readonly operation: "ack";
|
|
46
|
+
readonly cause: unknown;
|
|
47
|
+
constructor(messageId: string, cause: unknown);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Managed processing invokes the callback once per claim, renews while it runs, and ACKs
|
|
51
|
+
* only a fulfilled, uninterrupted callback. No bridge effect or callback is retried.
|
|
52
|
+
*
|
|
53
|
+
* This is NOT exactly-once processing: crashes, lease loss, and uncooperative callbacks
|
|
54
|
+
* can cause redelivery and overlapping external effects. Use application idempotency.
|
|
55
|
+
* Cancellation rejects receive promptly but does not NACK a still-running callback.
|
|
56
|
+
* Its claim is renewed only until the processing deadline; then renewal stops and the
|
|
57
|
+
* last lease expires naturally. A late callback never ACKs. Each helper retains one
|
|
58
|
+
* of its 32 slots until the actual callback settles, even after the caller has left.
|
|
59
|
+
*/
|
|
60
|
+
export declare function createProgramInbox(call: InboxBridgeCall): ProgramInbox;
|
|
61
|
+
export {};
|