@ambasada/village 0.8.7 → 0.8.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +344 -49
- package/dist/client.d.ts +39 -4
- package/dist/define.d.ts +9 -1
- package/dist/index.js +95 -14
- package/dist/types.d.ts +47 -1
- package/package.json +3 -3
package/dist/client.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Ambasada, type AmbasadaOptions } from "@ambasada/sdk/platform";
|
|
2
2
|
import { type IncomingRequest } from "./receipt.js";
|
|
3
|
-
import type { AgentConfigInput, BuyOptions, BuyResult, ChatRequest, ChatResponse, CheckoutInput, CheckoutReceiptClaims, CheckoutDelivery, CheckoutDeliveryInput, CheckoutSession, ImageRequest, ImageResult, LedgerPage, Listing, ListingInput, MarketOffering, PaidCall, PaymentLink, PaymentLinkInput, ToolListing, ToolResult, TranscriptEntry, ChatLine, ChatProposal, ChatTurn, TransferOptions, Wallet, WebhookDelivery, WebhookEvent } from "./types.js";
|
|
3
|
+
import type { JobAnswer, AgentConfigInput, BuyOptions, BuyResult, ChatRequest, ChatResponse, CheckoutInput, CheckoutReceiptClaims, CheckoutDelivery, CheckoutDeliveryInput, CheckoutSession, ImageRequest, ImageResult, LedgerPage, Listing, ListingInput, MarketOffering, PaidCall, PaymentLink, PaymentLinkInput, ToolListing, ToolResult, TranscriptEntry, ChatLine, ChatProposal, ChatDecision, ChatTurn, TransferOptions, Wallet, WebhookDelivery, WebhookEvent } from "./types.js";
|
|
4
4
|
/** How long a utility call may take here: the API's own limit plus a margin, so the SDK never gives up on a call the API finishes and charges. */
|
|
5
5
|
export declare const CLIENT_TIMEOUT_MS: {
|
|
6
6
|
readonly llm: number;
|
|
@@ -58,6 +58,22 @@ export declare class Village extends Ambasada {
|
|
|
58
58
|
* export default village.sell("translate", async ({ text, to }) => ({ text: await village.llm(`…`) }));
|
|
59
59
|
*/
|
|
60
60
|
sell<Input = any, Output = unknown>(offering: string, handler: SellHandler<Input, Output>): (req: Request) => Promise<Response>;
|
|
61
|
+
/**
|
|
62
|
+
* An async offering's answer to a paid call (`async: true` in offerings.ts): the work goes on as job `id` (in the
|
|
63
|
+
* template, a Jobs Durable Object named after it), and the Gateway asks `GET <endpoint>/jobs/<id>` until it is done.
|
|
64
|
+
*
|
|
65
|
+
* export default village.sell("ideas", async (input, call) => {
|
|
66
|
+
* await startJob(env, `ideas:${call.call_id}`, "ideas", input);
|
|
67
|
+
* return village.deferred(`ideas:${call.call_id}`);
|
|
68
|
+
* });
|
|
69
|
+
*/
|
|
70
|
+
deferred(id: string): Response;
|
|
71
|
+
/**
|
|
72
|
+
* `GET <endpoint>/jobs/<id>` of an async offering: checks that the Gateway asks (its signed receipt names this
|
|
73
|
+
* job), then answers with what `lookup(id)` says: `{ status: "working" }`, `{ status: "done", output }` (the buyer
|
|
74
|
+
* pays and gets `output`) or `{ status: "failed", error }` (the buyer is refunded). `null`: no such job (404).
|
|
75
|
+
*/
|
|
76
|
+
jobStatus(offering: string, lookup: (id: string, req: Request) => Promise<JobAnswer | null> | JobAnswer | null): (req: Request) => Promise<Response>;
|
|
61
77
|
/** Verifies a signed webhook (`checkout.paid`, `webhook.test`) and returns the event. Throws `WebhookInvalidError`. */
|
|
62
78
|
verifyWebhook<Data = any>(req: IncomingRequest, secret?: string): Promise<WebhookEvent<Data>>;
|
|
63
79
|
/** Verifies a checkout receipt (pay.js `village:paid` → `e.detail.receipt`, or `session.receipt`). */
|
|
@@ -119,6 +135,15 @@ export declare class Village extends Ambasada {
|
|
|
119
135
|
*/
|
|
120
136
|
need<Output = any>(name: string, input: unknown, options?: BuyOptions): Promise<BuyResult<Output>>;
|
|
121
137
|
private paid;
|
|
138
|
+
/**
|
|
139
|
+
* An async purchase (`buy` with `wait: false`, or one an agent started): waits for its result, long-polling the
|
|
140
|
+
* Gateway until the seller's job is done (then charged) or its time is up (refunded: throws the error, TIMEOUT).
|
|
141
|
+
* `wait: false`: one look, `pending: true` while it runs.
|
|
142
|
+
*/
|
|
143
|
+
buyJob<Output = any>(job: string, options?: {
|
|
144
|
+
wait?: boolean;
|
|
145
|
+
signal?: AbortSignal;
|
|
146
|
+
}): Promise<BuyResult<Output>>;
|
|
122
147
|
/**
|
|
123
148
|
* `village.llm("prompt")` → the answer text; `village.llm.chat(req)` → the full OpenAI-style response.
|
|
124
149
|
* Default model: deepseek-v4.1-flash.
|
|
@@ -163,12 +188,17 @@ export declare class Village extends Ambasada {
|
|
|
163
188
|
steer: (text: string) => Promise<unknown>;
|
|
164
189
|
/**
|
|
165
190
|
* Talk to your agent (docs/AGENT-CHAT.md). With `wait` (the default) it resolves with the reply, up to a
|
|
166
|
-
* minute; the same `idempotencyKey` is the same turn, so a retry never asks twice.
|
|
191
|
+
* minute; the same `idempotencyKey` is the same turn, so a retry never asks twice. A reply later than that
|
|
192
|
+
* (`reply: null`, status `pending`/`running`; `queued: "wake"` while the agent's wake runs): `turn(submission_id)`.
|
|
167
193
|
*/
|
|
168
194
|
chat: (text: string, options?: {
|
|
169
195
|
idempotencyKey?: string;
|
|
170
196
|
wait?: boolean;
|
|
171
197
|
}) => Promise<ChatTurn>;
|
|
198
|
+
/** A turn sent before and its reply, waiting up to `waitSeconds` (0–60, default 60) for one still running. */
|
|
199
|
+
turn: (submissionId: string, options?: {
|
|
200
|
+
waitSeconds?: number;
|
|
201
|
+
}) => Promise<ChatTurn>;
|
|
172
202
|
/** The chat, oldest first: your words, the agent's replies and proposals, and cards for what happened. */
|
|
173
203
|
messages: (options?: {
|
|
174
204
|
since?: number;
|
|
@@ -176,8 +206,13 @@ export declare class Village extends Ambasada {
|
|
|
176
206
|
}) => Promise<ChatLine[]>;
|
|
177
207
|
/** Proposals waiting for your team. */
|
|
178
208
|
proposals: () => Promise<ChatProposal[]>;
|
|
179
|
-
/**
|
|
180
|
-
|
|
209
|
+
/**
|
|
210
|
+
* Approve (it runs now) or decline a proposal. `wait` (approve): also what the agent said after it, up to a
|
|
211
|
+
* minute (`follow_up`).
|
|
212
|
+
*/
|
|
213
|
+
decide: (id: string, decision: "approve" | "decline", reason?: string, options?: {
|
|
214
|
+
wait?: boolean;
|
|
215
|
+
}) => Promise<ChatDecision>;
|
|
181
216
|
transcript: (since?: number, limit?: number) => Promise<{
|
|
182
217
|
entries: TranscriptEntry[];
|
|
183
218
|
next_since: number;
|
package/dist/define.d.ts
CHANGED
|
@@ -38,8 +38,16 @@ export interface OfferingDef {
|
|
|
38
38
|
output?: FieldSchema;
|
|
39
39
|
/** Allow a free sample (`market.preview`). */
|
|
40
40
|
preview?: boolean;
|
|
41
|
-
/** How long the Gateway waits for your answer, ms: 100–30000 (default 30000, the most). Longer work:
|
|
41
|
+
/** How long the Gateway waits for your answer, ms: 100–30000 (default 30000, the most). Longer work: `async`. */
|
|
42
42
|
timeout_ms?: number;
|
|
43
43
|
status?: "open" | "paused";
|
|
44
|
+
/**
|
|
45
|
+
* Work that takes longer than 30 s (a search and a model call, a report): the handler answers at once with
|
|
46
|
+
* `village.deferred(jobId)` and `GET <endpoint>/jobs/<id>` says how it is going (`village.jobStatus`). The Gateway
|
|
47
|
+
* polls it and the buyer pays when it is done; not done within `max_wait_s`, the buyer is refunded.
|
|
48
|
+
*/
|
|
49
|
+
async?: boolean;
|
|
50
|
+
/** Async: seconds the Gateway waits for the job (10–600, default 300). */
|
|
51
|
+
max_wait_s?: number;
|
|
44
52
|
}
|
|
45
53
|
export declare function offering(def: OfferingDef): OfferingDef;
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,7 @@ __export(src_exports, {
|
|
|
12
12
|
APP_SIGNATURE_HEADER: () => APP_SIGNATURE_HEADER,
|
|
13
13
|
APP_TIMESTAMP_HEADER: () => APP_TIMESTAMP_HEADER,
|
|
14
14
|
APP_WEBHOOK_TOLERANCE_S: () => APP_WEBHOOK_TOLERANCE_S,
|
|
15
|
+
ASYNC_OFFERING: () => ASYNC_OFFERING,
|
|
15
16
|
AppWebhookError: () => AppWebhookError,
|
|
16
17
|
DEFAULT_HUB_URL: () => DEFAULT_HUB_URL,
|
|
17
18
|
DEFAULT_ISSUER: () => DEFAULT_ISSUER,
|
|
@@ -49,6 +50,7 @@ __export(src_exports, {
|
|
|
49
50
|
appNotifyBody: () => appNotifyBody,
|
|
50
51
|
appNotifyResult: () => appNotifyResult,
|
|
51
52
|
appWebhookHeaders: () => appWebhookHeaders,
|
|
53
|
+
asyncOfferingProblem: () => asyncOfferingProblem,
|
|
52
54
|
base64url: () => base64url,
|
|
53
55
|
bundlesMiniappSdk: () => bundlesMiniappSdk,
|
|
54
56
|
checkHtml: () => checkHtml,
|
|
@@ -1491,6 +1493,16 @@ var HEADERS = {
|
|
|
1491
1493
|
};
|
|
1492
1494
|
var MAX_DEPTH = 4;
|
|
1493
1495
|
var OFFERING_TIMEOUT_MS = { min: 100, max: 3e4 };
|
|
1496
|
+
var ASYNC_OFFERING = { defaultMaxWaitS: 300, minMaxWaitS: 10, maxMaxWaitS: 600 };
|
|
1497
|
+
function asyncOfferingProblem(isAsync, maxWaitS) {
|
|
1498
|
+
if (isAsync !== void 0 && typeof isAsync !== "boolean") return "async must be true or false";
|
|
1499
|
+
if (maxWaitS === void 0 || maxWaitS === null) return null;
|
|
1500
|
+
if (isAsync !== true) return "max_wait_s needs async: true";
|
|
1501
|
+
if (typeof maxWaitS !== "number" || !Number.isInteger(maxWaitS) || maxWaitS < ASYNC_OFFERING.minMaxWaitS || maxWaitS > ASYNC_OFFERING.maxMaxWaitS) {
|
|
1502
|
+
return `max_wait_s must be whole seconds between ${ASYNC_OFFERING.minMaxWaitS} and ${ASYNC_OFFERING.maxMaxWaitS}`;
|
|
1503
|
+
}
|
|
1504
|
+
return null;
|
|
1505
|
+
}
|
|
1494
1506
|
var UTILITY_TIMEOUT_MS = { llm: 12e4, image: 18e4, tool: 6e4 };
|
|
1495
1507
|
var UTILITY_CLIENT_MARGIN_MS = 1e4;
|
|
1496
1508
|
function offeringTimeoutProblem(value) {
|
|
@@ -1636,6 +1648,7 @@ var CLIENT_TIMEOUT_MS = {
|
|
|
1636
1648
|
image: UTILITY_TIMEOUT_MS.image + UTILITY_CLIENT_MARGIN_MS,
|
|
1637
1649
|
tool: UTILITY_TIMEOUT_MS.tool + UTILITY_CLIENT_MARGIN_MS
|
|
1638
1650
|
};
|
|
1651
|
+
var CHAT_WAIT_MS = 6e4;
|
|
1639
1652
|
var DEFAULT_VILLAGE_API_URL = "https://hackaton.ambasada.id";
|
|
1640
1653
|
var DEFAULT_MODEL = "deepseek-v4.1-flash";
|
|
1641
1654
|
function randomKey() {
|
|
@@ -1738,6 +1751,39 @@ var Village = class extends Ambasada {
|
|
|
1738
1751
|
}
|
|
1739
1752
|
};
|
|
1740
1753
|
}
|
|
1754
|
+
/**
|
|
1755
|
+
* An async offering's answer to a paid call (`async: true` in offerings.ts): the work goes on as job `id` (in the
|
|
1756
|
+
* template, a Jobs Durable Object named after it), and the Gateway asks `GET <endpoint>/jobs/<id>` until it is done.
|
|
1757
|
+
*
|
|
1758
|
+
* export default village.sell("ideas", async (input, call) => {
|
|
1759
|
+
* await startJob(env, `ideas:${call.call_id}`, "ideas", input);
|
|
1760
|
+
* return village.deferred(`ideas:${call.call_id}`);
|
|
1761
|
+
* });
|
|
1762
|
+
*/
|
|
1763
|
+
deferred(id) {
|
|
1764
|
+
return Response.json({ job: id, status: "working" }, { status: 202 });
|
|
1765
|
+
}
|
|
1766
|
+
/**
|
|
1767
|
+
* `GET <endpoint>/jobs/<id>` of an async offering: checks that the Gateway asks (its signed receipt names this
|
|
1768
|
+
* job), then answers with what `lookup(id)` says: `{ status: "working" }`, `{ status: "done", output }` (the buyer
|
|
1769
|
+
* pays and gets `output`) or `{ status: "failed", error }` (the buyer is refunded). `null`: no such job (404).
|
|
1770
|
+
*/
|
|
1771
|
+
jobStatus(offering2, lookup) {
|
|
1772
|
+
return async (req) => {
|
|
1773
|
+
const id = decodeURIComponent(new URL(req.url).pathname.split("/").filter(Boolean).at(-1) ?? "");
|
|
1774
|
+
try {
|
|
1775
|
+
const seller = await this.company();
|
|
1776
|
+
const claims = await verifyCallReceipt(receiptHeader(req.headers), new Uint8Array(), { keys: this.keySource(), seller, offering: `${seller}/${offering2}` });
|
|
1777
|
+
if (claims.job !== id) throw new ReceiptInvalidError("wrong_kind", "this receipt is for another job");
|
|
1778
|
+
} catch (error) {
|
|
1779
|
+
if (error instanceof AmbasadaError3) return error.toResponse();
|
|
1780
|
+
throw error;
|
|
1781
|
+
}
|
|
1782
|
+
const answer = await lookup(id, req);
|
|
1783
|
+
if (!answer) return Response.json({ error: { code: "NOT_FOUND", message: `no job ${id}` } }, { status: 404 });
|
|
1784
|
+
return Response.json(answer, { status: answer.status === "working" ? 202 : 200 });
|
|
1785
|
+
};
|
|
1786
|
+
}
|
|
1741
1787
|
/** Verifies a signed webhook (`checkout.paid`, `webhook.test`) and returns the event. Throws `WebhookInvalidError`. */
|
|
1742
1788
|
async verifyWebhook(req, secret) {
|
|
1743
1789
|
const { headers, body } = await readIncoming(req);
|
|
@@ -1833,16 +1879,23 @@ var Village = class extends Ambasada {
|
|
|
1833
1879
|
"x-village-reason": options.reason?.slice(0, 280)
|
|
1834
1880
|
}
|
|
1835
1881
|
});
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1882
|
+
if (res.status === 202) {
|
|
1883
|
+
const job = res.json?.job;
|
|
1884
|
+
if (typeof job === "string") return options.wait === false ? pendingResult(job, res.headers) : this.buyJob(job, { signal: options.signal });
|
|
1885
|
+
}
|
|
1886
|
+
return boughtResult(res.json, res.headers);
|
|
1887
|
+
}
|
|
1888
|
+
/**
|
|
1889
|
+
* An async purchase (`buy` with `wait: false`, or one an agent started): waits for its result, long-polling the
|
|
1890
|
+
* Gateway until the seller's job is done (then charged) or its time is up (refunded: throws the error, TIMEOUT).
|
|
1891
|
+
* `wait: false`: one look, `pending: true` while it runs.
|
|
1892
|
+
*/
|
|
1893
|
+
async buyJob(job, options = {}) {
|
|
1894
|
+
for (; ; ) {
|
|
1895
|
+
const res = await this.send("GET", `/v1/pay/jobs/${encodeURIComponent(job)}`, { query: { wait: options.wait === false ? 0 : 25 }, signal: options.signal, timeoutMs: 45e3 });
|
|
1896
|
+
if (res.status !== 202) return boughtResult(res.json, res.headers);
|
|
1897
|
+
if (options.wait === false) return pendingResult(job, res.headers);
|
|
1898
|
+
}
|
|
1846
1899
|
}
|
|
1847
1900
|
// ── utilities (LLMOK, paid in TON from your wallet) ───────────────────────
|
|
1848
1901
|
/**
|
|
@@ -1922,15 +1975,27 @@ var Village = class extends Ambasada {
|
|
|
1922
1975
|
steer: (text) => this.call("POST", "/v1/agent/steer", { body: { text } }),
|
|
1923
1976
|
/**
|
|
1924
1977
|
* Talk to your agent (docs/AGENT-CHAT.md). With `wait` (the default) it resolves with the reply, up to a
|
|
1925
|
-
* minute; the same `idempotencyKey` is the same turn, so a retry never asks twice.
|
|
1978
|
+
* minute; the same `idempotencyKey` is the same turn, so a retry never asks twice. A reply later than that
|
|
1979
|
+
* (`reply: null`, status `pending`/`running`; `queued: "wake"` while the agent's wake runs): `turn(submission_id)`.
|
|
1926
1980
|
*/
|
|
1927
|
-
chat: (text, options = {}) => this.call("POST", "/v1/agent/chat/turn", { body: { text, wait: options.wait ?? true, idempotency_key: options.idempotencyKey ?? randomKey() } }),
|
|
1981
|
+
chat: (text, options = {}) => this.call("POST", "/v1/agent/chat/turn", { body: { text, wait: options.wait ?? true, idempotency_key: options.idempotencyKey ?? randomKey() }, timeoutMs: CHAT_WAIT_MS + 15e3 }),
|
|
1982
|
+
/** A turn sent before and its reply, waiting up to `waitSeconds` (0–60, default 60) for one still running. */
|
|
1983
|
+
turn: (submissionId, options = {}) => {
|
|
1984
|
+
const wait = options.waitSeconds ?? CHAT_WAIT_MS / 1e3;
|
|
1985
|
+
return this.call("GET", `/v1/agent/chat/turn/${encodeURIComponent(submissionId)}`, { query: { wait }, timeoutMs: wait * 1e3 + 15e3 });
|
|
1986
|
+
},
|
|
1928
1987
|
/** The chat, oldest first: your words, the agent's replies and proposals, and cards for what happened. */
|
|
1929
1988
|
messages: async (options = {}) => (await this.call("GET", "/v1/agent/chat/messages", { query: { limit: options.limit ?? 50, ...options.since ? { since: options.since } : {} } })).messages,
|
|
1930
1989
|
/** Proposals waiting for your team. */
|
|
1931
1990
|
proposals: async () => (await this.call("GET", "/v1/agent/chat/proposals")).proposals,
|
|
1932
|
-
/**
|
|
1933
|
-
|
|
1991
|
+
/**
|
|
1992
|
+
* Approve (it runs now) or decline a proposal. `wait` (approve): also what the agent said after it, up to a
|
|
1993
|
+
* minute (`follow_up`).
|
|
1994
|
+
*/
|
|
1995
|
+
decide: (id, decision, reason, options = {}) => this.call("POST", `/v1/agent/chat/proposals/${encodeURIComponent(id)}`, {
|
|
1996
|
+
body: { decision, ...reason ? { reason } : {}, ...options.wait ? { wait: true } : {} },
|
|
1997
|
+
...options.wait ? { timeoutMs: CHAT_WAIT_MS + 15e3 } : {}
|
|
1998
|
+
}),
|
|
1934
1999
|
transcript: (since = 0, limit = 200) => this.call("GET", "/v1/agent/transcript", { query: { since, limit } }),
|
|
1935
2000
|
state: () => this.call("GET", "/v1/agent/state")
|
|
1936
2001
|
};
|
|
@@ -1938,6 +2003,20 @@ var Village = class extends Ambasada {
|
|
|
1938
2003
|
function createVillage(options = {}) {
|
|
1939
2004
|
return new Village(options);
|
|
1940
2005
|
}
|
|
2006
|
+
function pendingResult(job, headers) {
|
|
2007
|
+
return { output: null, pending: true, job, callId: headers.get("x-village-call-id") ?? job, amount: null, receipt: null, logIndex: null, replayed: false, offering: null };
|
|
2008
|
+
}
|
|
2009
|
+
function boughtResult(json, h) {
|
|
2010
|
+
return {
|
|
2011
|
+
output: json,
|
|
2012
|
+
callId: h.get("x-village-call-id"),
|
|
2013
|
+
amount: num(h.get("x-village-amount")),
|
|
2014
|
+
receipt: h.get("x-village-receipt"),
|
|
2015
|
+
logIndex: num(h.get("x-village-log-index")),
|
|
2016
|
+
replayed: h.get("idempotent-replayed") === "true",
|
|
2017
|
+
offering: h.get("x-village-offering")
|
|
2018
|
+
};
|
|
2019
|
+
}
|
|
1941
2020
|
|
|
1942
2021
|
// src/define.ts
|
|
1943
2022
|
function defineCompany(app, village2) {
|
|
@@ -1950,6 +2029,8 @@ function offering(def) {
|
|
|
1950
2029
|
if (!(def.price >= 0.1 && def.price <= 500)) throw new Error(`offering ${def.name}: price must be between 0.1 and 500 TON`);
|
|
1951
2030
|
const timeout = offeringTimeoutProblem(def.timeout_ms);
|
|
1952
2031
|
if (timeout) throw new Error(`offering ${def.name}: ${timeout}`);
|
|
2032
|
+
const later = asyncOfferingProblem(def.async, def.max_wait_s);
|
|
2033
|
+
if (later) throw new Error(`offering ${def.name}: ${later}`);
|
|
1953
2034
|
return def;
|
|
1954
2035
|
}
|
|
1955
2036
|
|
package/dist/types.d.ts
CHANGED
|
@@ -17,6 +17,8 @@ export interface ReceiptClaims {
|
|
|
17
17
|
/** TON you may still spend downstream on behalf of this call. */
|
|
18
18
|
budget?: number;
|
|
19
19
|
deal_id?: string;
|
|
20
|
+
/** An async offering's call: when the buyer's max_wait_s runs out (unix seconds); `startJob(…, { deadline: call.deadline * 1000 })`. */
|
|
21
|
+
deadline?: number;
|
|
20
22
|
body_sha256: string;
|
|
21
23
|
iat: number;
|
|
22
24
|
exp: number;
|
|
@@ -149,7 +151,28 @@ export interface BuyOptions {
|
|
|
149
151
|
/** Why you are buying; shown to your team on approval requests. */
|
|
150
152
|
reason?: string;
|
|
151
153
|
signal?: AbortSignal;
|
|
152
|
-
|
|
154
|
+
/**
|
|
155
|
+
* An async offering answers with a job: by default `buy` waits for its result (long-polling the Gateway, up to the
|
|
156
|
+
* offering's `max_wait_s`). `wait: false` returns at once with `pending: true` and the `job` (`village.buyJob(job)` later).
|
|
157
|
+
*/
|
|
158
|
+
wait?: boolean;
|
|
159
|
+
}
|
|
160
|
+
/** What an async offering's `GET <endpoint>/jobs/<id>` says (village.jobStatus): the Gateway settles on done or failed. */
|
|
161
|
+
export type JobAnswer =
|
|
162
|
+
/** `attempts`: the failed ones so far (the template's `jobAnswer(job)`); the buyer sees "working (attempt 2, last error: …)". */
|
|
163
|
+
{
|
|
164
|
+
status: "working";
|
|
165
|
+
attempts?: {
|
|
166
|
+
at: string;
|
|
167
|
+
error: string;
|
|
168
|
+
}[];
|
|
169
|
+
} | {
|
|
170
|
+
status: "done";
|
|
171
|
+
output: unknown;
|
|
172
|
+
} | {
|
|
173
|
+
status: "failed";
|
|
174
|
+
error: string;
|
|
175
|
+
};
|
|
153
176
|
export interface BuyResult<Output = any> {
|
|
154
177
|
output: Output;
|
|
155
178
|
callId: string | null;
|
|
@@ -163,6 +186,10 @@ export interface BuyResult<Output = any> {
|
|
|
163
186
|
replayed: boolean;
|
|
164
187
|
/** For `need()`: which offering served it (`company/name`), if the Gateway says. */
|
|
165
188
|
offering: string | null;
|
|
189
|
+
/** An async offering's job still running (`wait: false`): no output yet and nothing charged. */
|
|
190
|
+
pending?: boolean;
|
|
191
|
+
/** The async purchase's job (also its call id): `village.buyJob(job)` reads it. */
|
|
192
|
+
job?: string;
|
|
166
193
|
}
|
|
167
194
|
/** What the village sells (apps/api/config/pricing.json); any other model is refused. */
|
|
168
195
|
export type ChatModel = "deepseek-v4.1-flash" | (string & {});
|
|
@@ -250,6 +277,10 @@ export interface ListingInput {
|
|
|
250
277
|
timeout_ms?: number;
|
|
251
278
|
status?: "open" | "paused";
|
|
252
279
|
preview?: boolean;
|
|
280
|
+
/** The work takes longer than a request: answer `{ job }` and serve `GET <endpoint>/jobs/<job>` (village.jobStatus). */
|
|
281
|
+
async?: boolean;
|
|
282
|
+
/** Async: how long the Gateway waits for the job, seconds (10–600, default 300); then the buyer is refunded. */
|
|
283
|
+
max_wait_s?: number;
|
|
253
284
|
}
|
|
254
285
|
export interface Listing extends ListingInput {
|
|
255
286
|
id: string;
|
|
@@ -371,7 +402,22 @@ export interface ChatTurn {
|
|
|
371
402
|
/** With `wait`: how the turn ended and the agent's reply. */
|
|
372
403
|
status?: "completed" | "error" | "aborted" | "skipped" | "pending" | "running";
|
|
373
404
|
error?: string;
|
|
405
|
+
/** Still waiting: "wake" while the agent's wake runs (the turn is answered after it); also on a `wait: false` send during a wake. */
|
|
406
|
+
queued?: "wake";
|
|
374
407
|
reply?: ChatLine | null;
|
|
408
|
+
/** The turn ended and the agent said nothing, even after the village asked it once more: "No reply (the model returned nothing)". */
|
|
409
|
+
no_reply?: string;
|
|
410
|
+
}
|
|
411
|
+
/** `agent.decide`: what ran, and with `wait` (approve) what the agent said after it. */
|
|
412
|
+
export interface ChatDecision {
|
|
413
|
+
id: string;
|
|
414
|
+
decision: "approve" | "decline";
|
|
415
|
+
/** The action's outcome (approve): its result, or `{ error: { code, message } }` when it failed. */
|
|
416
|
+
result: unknown;
|
|
417
|
+
/** The API's time of the decision (ms). */
|
|
418
|
+
at?: number;
|
|
419
|
+
/** With `wait`: the agent's next line (its reply, or the "no reply" card), null when a minute went by. */
|
|
420
|
+
follow_up?: ChatLine | null;
|
|
375
421
|
}
|
|
376
422
|
export interface MarketOffering {
|
|
377
423
|
id: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ambasada/village",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.9",
|
|
4
4
|
"description": "The Hack A Ton add-on for Ambasada apps: TON checkout, receipts, offerings, LLM, the agent, the market; CLI commands and the local bank.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"node": ">=22"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@ambasada/sdk": "^0.8.
|
|
30
|
+
"@ambasada/sdk": "^0.8.9"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/node": "^22.20.5",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"typescript": "~5.9.3",
|
|
36
36
|
"vitest": "^4.1.12",
|
|
37
37
|
"@ambasada/protocol": "0.1.0",
|
|
38
|
-
"@ambasada/cli": "0.8.
|
|
38
|
+
"@ambasada/cli": "0.8.9"
|
|
39
39
|
},
|
|
40
40
|
"author": "CENTYSO (https://centyso.com)",
|
|
41
41
|
"homepage": "https://ambasada.id",
|