@ambasada/village 0.8.3 → 0.8.5

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/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, CheckoutSession, ImageRequest, ImageResult, LedgerPage, Listing, ListingInput, MarketOffering, PaidCall, PaymentLink, PaymentLinkInput, ToolListing, ToolResult, TranscriptEntry, TransferOptions, Wallet, WebhookDelivery, WebhookEvent } from "./types.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";
4
4
  /** The Hack A Ton API. The platform API (`@ambasada/sdk`) is https://api.ambasada.dev. */
5
5
  export declare const DEFAULT_VILLAGE_API_URL = "https://hackaton.ambasada.id";
6
6
  export declare const DEFAULT_MODEL = "deepseek-v4.1-flash";
@@ -63,6 +63,20 @@ export declare class Village extends Ambasada {
63
63
  idempotencyKey?: string;
64
64
  }): Promise<CheckoutSession>;
65
65
  getCheckout(id: string): Promise<CheckoutSession>;
66
+ /** Your latest Checkout sessions (newest first, default 20, at most 100), each with what you said you delivered. */
67
+ listCheckouts(options?: {
68
+ limit?: number;
69
+ status?: "open" | "paid" | "expired";
70
+ }): Promise<{
71
+ sessions: (CheckoutSession & {
72
+ delivery: Omit<CheckoutDelivery, "session"> | null;
73
+ })[];
74
+ }>;
75
+ /**
76
+ * Tells the village what a paid checkout delivered (`text` ≤ 4000 chars, an https `url`, or both): the person sees it
77
+ * with the purchase in their app and can rate it. Again for the same session replaces it. CONFLICT before it is paid.
78
+ */
79
+ deliverCheckout(id: string, delivery: CheckoutDeliveryInput): Promise<CheckoutDelivery>;
66
80
  /** A reusable payment link for static pages (`<button data-village-pay="pl_…">` with pay.js). */
67
81
  createPaymentLink(link: PaymentLinkInput): Promise<PaymentLink>;
68
82
  /** Cancels an open session (`POST /v1/checkout/sessions/:id/expire`). */
@@ -136,8 +150,25 @@ export declare class Village extends Ambasada {
136
150
  config: unknown;
137
151
  limits?: unknown;
138
152
  }>;
139
- /** A message the agent answers on its next wake. */
153
+ /** A message from the team: the agent answers in the chat now, and reads it on its next wake. */
140
154
  steer: (text: string) => Promise<unknown>;
155
+ /**
156
+ * Talk to your agent (docs/AGENT-CHAT.md). With `wait` (the default) it resolves with the reply, up to a
157
+ * minute; the same `idempotencyKey` is the same turn, so a retry never asks twice.
158
+ */
159
+ chat: (text: string, options?: {
160
+ idempotencyKey?: string;
161
+ wait?: boolean;
162
+ }) => Promise<ChatTurn>;
163
+ /** The chat, oldest first: your words, the agent's replies and proposals, and cards for what happened. */
164
+ messages: (options?: {
165
+ since?: number;
166
+ limit?: number;
167
+ }) => Promise<ChatLine[]>;
168
+ /** Proposals waiting for your team. */
169
+ proposals: () => Promise<ChatProposal[]>;
170
+ /** Approve (it runs now) or decline a proposal. */
171
+ decide: (id: string, decision: "approve" | "decline", reason?: string) => Promise<unknown>;
141
172
  transcript: (since?: number, limit?: number) => Promise<{
142
173
  entries: TranscriptEntry[];
143
174
  next_since: number;
package/dist/define.d.ts CHANGED
@@ -38,6 +38,7 @@ 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: answer fast, finish later. */
41
42
  timeout_ms?: number;
42
43
  status?: "open" | "paused";
43
44
  }
package/dist/index.js CHANGED
@@ -37,6 +37,7 @@ __export(src_exports, {
37
37
  HEADERS: () => HEADERS,
38
38
  HOSTED_MANIFEST: () => HOSTED_MANIFEST,
39
39
  ICON_SIZE: () => ICON_SIZE,
40
+ MANIFEST_AGE_RATINGS: () => MANIFEST_AGE_RATINGS,
40
41
  MANIFEST_CATEGORIES: () => MANIFEST_CATEGORIES,
41
42
  MANIFEST_ID: () => MANIFEST_ID,
42
43
  MANIFEST_KINDS: () => MANIFEST_KINDS,
@@ -49,6 +50,7 @@ __export(src_exports, {
49
50
  MIN_PRICE_NANO: () => MIN_PRICE_NANO,
50
51
  NANO_PER_TON: () => NANO_PER_TON,
51
52
  NOTIFY_LIMITS: () => NOTIFY_LIMITS,
53
+ OFFERING_TIMEOUT_MS: () => OFFERING_TIMEOUT_MS,
52
54
  RECEIPT_HEADER: () => RECEIPT_HEADER,
53
55
  ReceiptError: () => ReceiptError,
54
56
  SHELL_TOKEN_TTL_S: () => SHELL_TOKEN_TTL_S,
@@ -82,6 +84,7 @@ __export(src_exports, {
82
84
  leafHash: () => leafHash,
83
85
  listingLine: () => listingLine,
84
86
  nodeHash: () => nodeHash,
87
+ offeringTimeoutProblem: () => offeringTimeoutProblem,
85
88
  parseCheckpoint: () => parseCheckpoint,
86
89
  parseVerifierKey: () => parseVerifierKey,
87
90
  pngInfo: () => pngInfo,
@@ -152,6 +155,8 @@ var ERROR_STATUS = {
152
155
  BUDGET_EXCEEDED: 402,
153
156
  CAP_INBOX_RATE: 429,
154
157
  // inbox texts: 20 an hour, 1 unsolicited per counterparty an hour (EXCHANGE.md §8)
158
+ CAP_CHAT_RATE: 429,
159
+ // turns of the CEO chat per company (docs/AGENT-CHAT.md)
155
160
  // Additions needed by the gateway and bank
156
161
  UPSTREAM_ERROR: 502,
157
162
  PRICE_ABOVE_MAX: 409,
@@ -1141,6 +1146,7 @@ var MANIFEST_SCHEMA = 2;
1141
1146
  var MANIFEST_KINDS = ["product", "event", "tool", "assistant"];
1142
1147
  var MANIFEST_CATEGORIES = ["shopping", "food", "events", "tools", "learning", "community", "games", "finance", "other"];
1143
1148
  var MANIFEST_VISIBILITY = ["public", "builders", "unlisted"];
1149
+ var MANIFEST_AGE_RATINGS = ["4+", "9+", "12+", "17+"];
1144
1150
  var MANIFEST_LIMITS = { id: 32, name: 32, subtitle: 40, description: 170, tags: 5, screenshots: 3 };
1145
1151
  var MANIFEST_ID = /^[a-z0-9-]{3,32}$/;
1146
1152
  var HOSTED_MANIFEST = /^https:\/\/hub\.ambasada\.id\/api\/apps\/([a-z0-9-]+)\/manifest$/;
@@ -1201,6 +1207,15 @@ function validateManifest(json) {
1201
1207
  const color = splash?.color;
1202
1208
  if (!splash || color !== void 0 && (typeof color !== "string" || !HEX_COLOR.test(color))) problem("splash_color", `"splash.color" must be a #RRGGBB colour (got ${JSON.stringify(color ?? m.splash)})`);
1203
1209
  }
1210
+ const age = str("age_rating");
1211
+ if (m.age_rating === void 0) {
1212
+ if (m.schema === MANIFEST_SCHEMA) problem("age_rating", `"age_rating" is required: one of ${MANIFEST_AGE_RATINGS.join(", ")} (the hub refuses to register an app without it)`);
1213
+ } else if (!age || !MANIFEST_AGE_RATINGS.includes(age)) {
1214
+ problem("age_rating", `"age_rating" must be one of ${MANIFEST_AGE_RATINGS.join(", ")} (got ${JSON.stringify(m.age_rating)})`);
1215
+ }
1216
+ if (m.support !== void 0 && (typeof m.support !== "string" || !/^(mailto:[^@\s]+@[^@\s]+|https:\/\/\S+)$/.test(m.support))) {
1217
+ problem("support", `"support" must be a mailto: address or an https URL (got ${JSON.stringify(m.support)})`);
1218
+ }
1204
1219
  const kind = str("kind");
1205
1220
  if (m.kind !== void 0 && (!kind || !MANIFEST_KINDS.includes(kind))) warn("kind", `unknown "kind" ${JSON.stringify(m.kind)}: the app knows ${MANIFEST_KINDS.join(", ")} and shows this one as a product`);
1206
1221
  const category = str("category");
@@ -1462,6 +1477,12 @@ var HEADERS = {
1462
1477
  idempotentReplay: "idempotent-replayed"
1463
1478
  };
1464
1479
  var MAX_DEPTH = 4;
1480
+ var OFFERING_TIMEOUT_MS = { min: 100, max: 3e4 };
1481
+ function offeringTimeoutProblem(value) {
1482
+ if (value === void 0) return null;
1483
+ const n = Number(value);
1484
+ return Number.isInteger(n) && n >= OFFERING_TIMEOUT_MS.min && n <= OFFERING_TIMEOUT_MS.max ? null : `timeout_ms must be between ${OFFERING_TIMEOUT_MS.min} and ${OFFERING_TIMEOUT_MS.max} (got ${JSON.stringify(value)})`;
1485
+ }
1465
1486
 
1466
1487
  // src/receipt.ts
1467
1488
  import { AmbasadaError as AmbasadaError2, getHeader } from "@ambasada/sdk/platform";
@@ -1698,6 +1719,20 @@ var Village = class extends Ambasada {
1698
1719
  getCheckout(id) {
1699
1720
  return this.call("GET", `/v1/checkout/sessions/${encodeURIComponent(id)}`);
1700
1721
  }
1722
+ /** Your latest Checkout sessions (newest first, default 20, at most 100), each with what you said you delivered. */
1723
+ listCheckouts(options = {}) {
1724
+ const q = new URLSearchParams();
1725
+ if (options.limit !== void 0) q.set("limit", String(options.limit));
1726
+ if (options.status) q.set("status", options.status);
1727
+ return this.call("GET", `/v1/checkout/sessions${q.size ? `?${q}` : ""}`);
1728
+ }
1729
+ /**
1730
+ * Tells the village what a paid checkout delivered (`text` ≤ 4000 chars, an https `url`, or both): the person sees it
1731
+ * with the purchase in their app and can rate it. Again for the same session replaces it. CONFLICT before it is paid.
1732
+ */
1733
+ deliverCheckout(id, delivery) {
1734
+ return this.call("POST", `/v1/checkout/sessions/${encodeURIComponent(id)}/delivery`, { body: delivery });
1735
+ }
1701
1736
  /** A reusable payment link for static pages (`<button data-village-pay="pl_…">` with pay.js). */
1702
1737
  createPaymentLink(link) {
1703
1738
  return this.call("POST", "/v1/checkout/links", { body: link });
@@ -1841,8 +1876,19 @@ var Village = class extends Ambasada {
1841
1876
  agent = {
1842
1877
  /** Replaces the agent's prompt (`agent.md`) and settings (`settings.json`). */
1843
1878
  config: (config) => this.call("POST", "/v1/agent/config", { body: config }),
1844
- /** A message the agent answers on its next wake. */
1879
+ /** A message from the team: the agent answers in the chat now, and reads it on its next wake. */
1845
1880
  steer: (text) => this.call("POST", "/v1/agent/steer", { body: { text } }),
1881
+ /**
1882
+ * Talk to your agent (docs/AGENT-CHAT.md). With `wait` (the default) it resolves with the reply, up to a
1883
+ * minute; the same `idempotencyKey` is the same turn, so a retry never asks twice.
1884
+ */
1885
+ chat: (text, options = {}) => this.call("POST", "/v1/agent/chat/turn", { body: { text, wait: options.wait ?? true, idempotency_key: options.idempotencyKey ?? randomKey() } }),
1886
+ /** The chat, oldest first: your words, the agent's replies and proposals, and cards for what happened. */
1887
+ messages: async (options = {}) => (await this.call("GET", "/v1/agent/chat/messages", { query: { limit: options.limit ?? 50, ...options.since ? { since: options.since } : {} } })).messages,
1888
+ /** Proposals waiting for your team. */
1889
+ proposals: async () => (await this.call("GET", "/v1/agent/chat/proposals")).proposals,
1890
+ /** Approve (it runs now) or decline a proposal. */
1891
+ decide: (id, decision, reason) => this.call("POST", `/v1/agent/chat/proposals/${encodeURIComponent(id)}`, { body: { decision, ...reason ? { reason } : {} } }),
1846
1892
  transcript: (since = 0, limit = 200) => this.call("GET", "/v1/agent/transcript", { query: { since, limit } }),
1847
1893
  state: () => this.call("GET", "/v1/agent/state")
1848
1894
  };
@@ -1860,6 +1906,8 @@ function defineCompany(app, village2) {
1860
1906
  function offering(def) {
1861
1907
  if (!/^[a-z0-9][a-z0-9_-]{0,63}$/.test(def.name)) throw new Error(`offering name "${def.name}" must be a slug (a-z, 0-9, _, -)`);
1862
1908
  if (!(def.price >= 0.1 && def.price <= 500)) throw new Error(`offering ${def.name}: price must be between 0.1 and 500 TON`);
1909
+ const timeout = offeringTimeoutProblem(def.timeout_ms);
1910
+ if (timeout) throw new Error(`offering ${def.name}: ${timeout}`);
1863
1911
  return def;
1864
1912
  }
1865
1913
 
package/dist/types.d.ts CHANGED
@@ -55,6 +55,17 @@ export interface CheckoutInput {
55
55
  /** Seconds, 300–86400 (default 3600). */
56
56
  expires_in?: number;
57
57
  }
58
+ /** What a paid checkout delivered (`POST /v1/checkout/sessions/:id/delivery`): text, an https URL, or both. */
59
+ export interface CheckoutDeliveryInput {
60
+ text?: string;
61
+ url?: string;
62
+ }
63
+ export interface CheckoutDelivery {
64
+ session: string;
65
+ text: string | null;
66
+ url: string | null;
67
+ at: string;
68
+ }
58
69
  export interface CheckoutSession {
59
70
  id: string;
60
71
  object: "checkout.session";
@@ -142,7 +153,8 @@ export interface BuyResult<Output = any> {
142
153
  /** For `need()`: which offering served it (`company/name`), if the Gateway says. */
143
154
  offering: string | null;
144
155
  }
145
- export type ChatModel = "deepseek-v4.1-flash" | "deepseek-v4-pro" | "glm-5.3-flash" | (string & {});
156
+ /** What the village sells (apps/api/config/pricing.json); any other model is refused. */
157
+ export type ChatModel = "deepseek-v4.1-flash" | (string & {});
146
158
  export interface ChatMessage {
147
159
  role: "system" | "user" | "assistant" | "tool";
148
160
  content: string;
@@ -258,7 +270,7 @@ export interface TransferOptions {
258
270
  }
259
271
  /** `agent/settings.json`, sent with the prompt to `POST /v1/agent/config` (agent stream wire format). */
260
272
  export interface AgentSettings {
261
- model?: "deepseek-v4.1-flash" | "deepseek-v4-pro";
273
+ model?: "deepseek-v4.1-flash";
262
274
  enabled?: boolean;
263
275
  wake?: {
264
276
  interval_minutes?: number;
@@ -272,7 +284,12 @@ export interface AgentSettings {
272
284
  };
273
285
  /** Agent tools offered to the model (default: all). */
274
286
  tools?: string[];
275
- /** Company wallet spend limits, TON. Over `auto_per_payment` an agent payment needs the team's approval. */
287
+ /**
288
+ * The whole company's spend limits, TON: the product's own LLM and image calls count too, not only the agent's
289
+ * purchases. Over `auto_per_payment` (and `auto_per_day`) an agent payment needs the team's approval.
290
+ */
291
+ company_limits?: AgentSettings["limits"];
292
+ /** @deprecated The 0.8 name of `company_limits` (they were always the company's, not only the agent's). */
276
293
  limits?: {
277
294
  per_payment?: number;
278
295
  per_hour?: number;
@@ -296,6 +313,45 @@ export interface TranscriptEntry {
296
313
  data?: unknown;
297
314
  [key: string]: unknown;
298
315
  }
316
+ /** Something the agent wants to do, waiting for the team in the chat (docs/AGENT-CHAT.md). */
317
+ export interface ChatProposal {
318
+ /** What `agent.decide(id, …)` takes. */
319
+ id: string;
320
+ tool: string;
321
+ /** One plain sentence: "Change the price of translate to 1.2 TON". */
322
+ text: string;
323
+ input: unknown;
324
+ risk?: "low" | "medium" | "high";
325
+ state: "pending" | "approved" | "declined" | "done" | "failed";
326
+ }
327
+ /** One line of the chat with your agent: your words, its reply (with proposals), or a card for what happened. */
328
+ export interface ChatLine {
329
+ id: string;
330
+ role: "user" | "agent" | "card" | "village";
331
+ at: number | null;
332
+ text: string;
333
+ card?: {
334
+ id: string;
335
+ kind: string;
336
+ title: string;
337
+ body?: string | null;
338
+ delta_ton?: number | null;
339
+ from?: string | null;
340
+ at: number;
341
+ };
342
+ proposals?: ChatProposal[];
343
+ cost_ton?: number;
344
+ }
345
+ export interface ChatTurn {
346
+ submission_id: string;
347
+ message_id: string;
348
+ /** false when this idempotency key was already sent: the same turn, not a new one. */
349
+ accepted: boolean;
350
+ /** With `wait`: how the turn ended and the agent's reply. */
351
+ status?: "completed" | "error" | "aborted" | "skipped" | "pending" | "running";
352
+ error?: string;
353
+ reply?: ChatLine | null;
354
+ }
299
355
  export interface MarketOffering {
300
356
  id: string;
301
357
  company?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ambasada/village",
3
- "version": "0.8.3",
3
+ "version": "0.8.5",
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.3"
30
+ "@ambasada/sdk": "^0.8.5"
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.3"
38
+ "@ambasada/cli": "0.8.5"
39
39
  },
40
40
  "author": "CENTYSO (https://centyso.com)",
41
41
  "homepage": "https://ambasada.id",