@umicat/phaser-sdk 1.0.16 → 1.0.18
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/SDK-GUIDE.md +43 -1
- package/dist/ai/AiModule.d.ts +7 -0
- package/dist/ai/AiModule.js +1 -0
- package/dist/core/Transport.d.ts +3 -0
- package/dist/core/Umicat.d.ts +3 -0
- package/dist/core/Umicat.js +3 -0
- package/dist/core/transports/LocalStorageTransport.d.ts +1 -0
- package/dist/core/transports/LocalStorageTransport.js +2 -0
- package/dist/core/transports/PostMessageTransport.d.ts +1 -0
- package/dist/core/transports/PostMessageTransport.js +1 -0
- package/dist/protocol.d.ts +14 -0
- package/package.json +1 -1
package/SDK-GUIDE.md
CHANGED
|
@@ -79,9 +79,14 @@ if (umicat) {
|
|
|
79
79
|
umicat.isAuthenticated; // boolean
|
|
80
80
|
umicat.gameId; // stable platform-issued game id
|
|
81
81
|
umicat.host; // 'umicat-home-ui' | 'standalone' | 'discord' (future)
|
|
82
|
+
umicat.locale; // player's UI language, e.g. 'en' | 'zh-CN' (string)
|
|
82
83
|
}
|
|
83
84
|
```
|
|
84
85
|
|
|
86
|
+
`umicat.locale` (since 1.0.17) is the player's preferred language from the host —
|
|
87
|
+
default your game's UI + AI-NPC language to it instead of asking. For multi-language
|
|
88
|
+
games + the **CJK font handling** that avoids tofu boxes, see the `game-i18n` skill.
|
|
89
|
+
|
|
85
90
|
### Save data — `umicat.saves`
|
|
86
91
|
|
|
87
92
|
Per-user key-value store scoped to `(gameId, userId)`. Backed by the Umicat backend when authenticated, by localStorage when standalone/anonymous — **the API is identical either way**. Games should never branch on auth state for save logic.
|
|
@@ -600,11 +605,48 @@ function handleBlocked(reason) {
|
|
|
600
605
|
→ same `AiActResult`. `npc()` just keeps the conversation `history` for you (also
|
|
601
606
|
`npc.note('the player drew a sword')` to feed a world event, `npc.reset()` to forget).
|
|
602
607
|
|
|
608
|
+
#### Playbooks — keep persona + strategy out of code (since 1.0.18)
|
|
609
|
+
|
|
610
|
+
For anything past a one-line shopkeeper, write the NPC's **persona + strategy as
|
|
611
|
+
natural language** in `public/playbooks/<name>.md` and reference it by name. Code
|
|
612
|
+
keeps owning the **action vocabulary** (what the NPC *can* do) and the handlers
|
|
613
|
+
that validate + execute; the playbook owns **who it is and how it plays** (what to
|
|
614
|
+
do, when, why). You tune behavior by editing the `.md` — no code change, and a
|
|
615
|
+
non-coder can do it.
|
|
616
|
+
|
|
617
|
+
`public/playbooks/wolf.md`:
|
|
618
|
+
```md
|
|
619
|
+
You are a Werewolf, secretly on the wolf team. Your partner is another wolf.
|
|
620
|
+
|
|
621
|
+
Goal: survive to the end. Never reveal your team.
|
|
622
|
+
- By day, blend in. Sound like a worried villager hunting wolves.
|
|
623
|
+
- Cast suspicion subtly — agree with the room, then nudge it toward a villager.
|
|
624
|
+
- If accused, stay calm; over-defending looks guilty. Deflect to someone quieter.
|
|
625
|
+
- Don't out your partner; don't obviously defend them either.
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
```ts
|
|
629
|
+
const wolf = umicat.ai.npc({
|
|
630
|
+
playbook: 'wolf', // → public/playbooks/wolf.md
|
|
631
|
+
actions: [ // the vocabulary stays in code
|
|
632
|
+
{ name: 'vote', description: 'Vote to eliminate a player', args: { target: 'string' } },
|
|
633
|
+
],
|
|
634
|
+
});
|
|
635
|
+
const r = await wolf.say('It is day. Discuss.', { observation: { alive, votesSoFar } });
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
The host resolves the playbook under the game's own id, so pass **only the name** —
|
|
639
|
+
a player can't point it at another game. The playbook is content *inside* the
|
|
640
|
+
game's fiction: the platform safety rules and your `actions` still bound it; it
|
|
641
|
+
can't grant powers your code didn't declare. One playbook per NPC keeps each call
|
|
642
|
+
focused (a Werewolf game has `wolf.md` / `seer.md` / `villager.md`, loaded per
|
|
643
|
+
speaker). Edit + re-publish to ship a behavior change.
|
|
644
|
+
|
|
603
645
|
**API**
|
|
604
646
|
|
|
605
647
|
| call | returns | notes |
|
|
606
648
|
|---|---|---|
|
|
607
|
-
| `umicat.ai.npc(config)` | `Npc` | persona + `actions`; keeps history |
|
|
649
|
+
| `umicat.ai.npc(config)` | `Npc` | `playbook` (or inline persona) + `actions`; keeps history |
|
|
608
650
|
| `npc.say(text, { observation? })` | `Promise<AiActResult>` | one turn |
|
|
609
651
|
| `umicat.ai.act(params)` | `Promise<AiActResult>` | stateless; you pass `history` |
|
|
610
652
|
|
package/dist/ai/AiModule.d.ts
CHANGED
|
@@ -18,6 +18,13 @@ export declare class AiModule {
|
|
|
18
18
|
npc(config: NpcConfig): Npc;
|
|
19
19
|
}
|
|
20
20
|
export interface NpcConfig {
|
|
21
|
+
/**
|
|
22
|
+
* Name of a playbook shipped at `public/playbooks/<name>.md` (ADR-018) — the
|
|
23
|
+
* NPC's persona + strategy as editable natural language, kept OUT of code.
|
|
24
|
+
* Preferred over the inline `role`/`goals`/`style` for any non-trivial
|
|
25
|
+
* character (you can tune behavior by editing the `.md`, no code change).
|
|
26
|
+
*/
|
|
27
|
+
playbook?: string;
|
|
21
28
|
role?: string;
|
|
22
29
|
goals?: string[];
|
|
23
30
|
style?: string;
|
package/dist/ai/AiModule.js
CHANGED
package/dist/core/Transport.d.ts
CHANGED
|
@@ -19,6 +19,9 @@ export interface Transport {
|
|
|
19
19
|
* `umicat.rooms.*` is unavailable in that case.
|
|
20
20
|
*/
|
|
21
21
|
readonly realtimeUrl?: string;
|
|
22
|
+
/** The player's preferred language (BCP-47-ish, e.g. 'en', 'zh-CN'). The host
|
|
23
|
+
* provides it at handshake; standalone falls back to the browser locale. */
|
|
24
|
+
readonly locale?: string;
|
|
22
25
|
/** `timeoutMs` overrides the default RPC timeout — runtime-AI calls need
|
|
23
26
|
* longer than the 10s default for LLM latency. */
|
|
24
27
|
call<T = unknown>(method: string, params?: unknown, timeoutMs?: number): Promise<T>;
|
package/dist/core/Umicat.d.ts
CHANGED
|
@@ -33,6 +33,9 @@ export declare class Umicat {
|
|
|
33
33
|
get isAuthenticated(): boolean;
|
|
34
34
|
/** Stable game identifier issued by the host. May be a placeholder when standalone. */
|
|
35
35
|
get gameId(): string;
|
|
36
|
+
/** The player's preferred language (e.g. 'en', 'zh-CN'). Use it to default the
|
|
37
|
+
* game's UI + AI NPC language. See the game-i18n skill. */
|
|
38
|
+
get locale(): string;
|
|
36
39
|
/** Which transport Umicat.init() selected. Useful for debugging. */
|
|
37
40
|
get host(): TransportKind;
|
|
38
41
|
/**
|
package/dist/core/Umicat.js
CHANGED
|
@@ -40,6 +40,9 @@ export class Umicat {
|
|
|
40
40
|
get isAuthenticated() { return this.transport.user !== null; }
|
|
41
41
|
/** Stable game identifier issued by the host. May be a placeholder when standalone. */
|
|
42
42
|
get gameId() { return this.transport.gameId; }
|
|
43
|
+
/** The player's preferred language (e.g. 'en', 'zh-CN'). Use it to default the
|
|
44
|
+
* game's UI + AI NPC language. See the game-i18n skill. */
|
|
45
|
+
get locale() { return this.transport.locale ?? 'en'; }
|
|
43
46
|
/** Which transport Umicat.init() selected. Useful for debugging. */
|
|
44
47
|
get host() { return this.transport.kind; }
|
|
45
48
|
/**
|
|
@@ -9,6 +9,7 @@ export declare class LocalStorageTransport implements Transport {
|
|
|
9
9
|
readonly gameId: string;
|
|
10
10
|
readonly kind: "standalone";
|
|
11
11
|
readonly user: UmicatUser | null;
|
|
12
|
+
readonly locale: string;
|
|
12
13
|
constructor(gameId: string);
|
|
13
14
|
call<T = unknown>(method: string, params?: unknown): Promise<T>;
|
|
14
15
|
private savesPrefix;
|
|
@@ -9,6 +9,8 @@ export class LocalStorageTransport {
|
|
|
9
9
|
this.gameId = gameId;
|
|
10
10
|
this.kind = 'standalone';
|
|
11
11
|
this.user = null;
|
|
12
|
+
// No host to tell us the player's language — best-effort from the browser.
|
|
13
|
+
this.locale = (typeof navigator !== 'undefined' && navigator.language) ? navigator.language : 'en';
|
|
12
14
|
}
|
|
13
15
|
async call(method, params) {
|
|
14
16
|
switch (method) {
|
package/dist/protocol.d.ts
CHANGED
|
@@ -28,6 +28,12 @@ export interface InitMessage {
|
|
|
28
28
|
* here after fetching a JWT via the 'realtime.getToken' RPC.
|
|
29
29
|
*/
|
|
30
30
|
realtimeUrl?: string;
|
|
31
|
+
/**
|
|
32
|
+
* The player's preferred language (the host's UI locale, e.g. 'en', 'zh-CN',
|
|
33
|
+
* 'ja'). Lets a game default its UI + AI NPC language to the player's without
|
|
34
|
+
* asking. Absent in standalone mode → SDK falls back to the browser locale.
|
|
35
|
+
*/
|
|
36
|
+
locale?: string;
|
|
31
37
|
}
|
|
32
38
|
export interface RpcResultOk {
|
|
33
39
|
type: 'umicat:rpc.result';
|
|
@@ -927,6 +933,14 @@ export interface AiActOptions {
|
|
|
927
933
|
temperature?: number;
|
|
928
934
|
}
|
|
929
935
|
export interface AiActParams {
|
|
936
|
+
/**
|
|
937
|
+
* Name of a playbook shipped with the game at `public/playbooks/<name>.md`
|
|
938
|
+
* (ADR-018) — a natural-language behavior pack (persona + strategy) the platform
|
|
939
|
+
* loads and injects. Pass just the NAME; the host resolves it under the game's
|
|
940
|
+
* own id (tamper-resistant). Supplies the character behavior; can be combined
|
|
941
|
+
* with `persona` (the playbook leads, `persona` augments).
|
|
942
|
+
*/
|
|
943
|
+
playbook?: string;
|
|
930
944
|
persona?: AiPersona;
|
|
931
945
|
/** Arbitrary game-defined JSON of what the AI perceives this turn. */
|
|
932
946
|
observation?: unknown;
|