@aifight/aifight 0.1.0-alpha.1

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.
Files changed (176) hide show
  1. package/README.md +160 -0
  2. package/dist/bin.mjs +291 -0
  3. package/dist/index.mjs +107 -0
  4. package/dist/schemas/README.md +57 -0
  5. package/dist/schemas/common/README.md +40 -0
  6. package/dist/schemas/common/action.schema.json +19 -0
  7. package/dist/schemas/common/error.schema.json +23 -0
  8. package/dist/schemas/common/event.schema.json +33 -0
  9. package/dist/schemas/common/game_result.schema.json +31 -0
  10. package/dist/schemas/common/player_identity.schema.json +29 -0
  11. package/dist/schemas/common/player_info.schema.json +27 -0
  12. package/dist/schemas/common/rules.schema.json +34 -0
  13. package/dist/schemas/games/README.md +43 -0
  14. package/dist/schemas/games/coup/README.md +104 -0
  15. package/dist/schemas/games/coup/action.schema.json +198 -0
  16. package/dist/schemas/games/coup/event.schema.json +249 -0
  17. package/dist/schemas/games/coup/rules.schema.json +46 -0
  18. package/dist/schemas/games/coup/state.schema.json +123 -0
  19. package/dist/schemas/games/liars_dice/README.md +59 -0
  20. package/dist/schemas/games/liars_dice/action.schema.json +45 -0
  21. package/dist/schemas/games/liars_dice/event.schema.json +120 -0
  22. package/dist/schemas/games/liars_dice/rules.schema.json +36 -0
  23. package/dist/schemas/games/liars_dice/state.schema.json +72 -0
  24. package/dist/schemas/games/texas_holdem/README.md +58 -0
  25. package/dist/schemas/games/texas_holdem/action.schema.json +88 -0
  26. package/dist/schemas/games/texas_holdem/config.schema.json +30 -0
  27. package/dist/schemas/games/texas_holdem/event.schema.json +135 -0
  28. package/dist/schemas/games/texas_holdem/rules.schema.json +39 -0
  29. package/dist/schemas/games/texas_holdem/state.schema.json +98 -0
  30. package/dist/schemas/messages/README.md +98 -0
  31. package/dist/schemas/messages/client_action.schema.json +20 -0
  32. package/dist/schemas/messages/client_join_queue.schema.json +39 -0
  33. package/dist/schemas/messages/client_leave_queue.schema.json +18 -0
  34. package/dist/schemas/messages/client_match_confirm.schema.json +25 -0
  35. package/dist/schemas/messages/client_runtime_status.schema.json +46 -0
  36. package/dist/schemas/messages/server_action_request.schema.json +71 -0
  37. package/dist/schemas/messages/server_error.schema.json +16 -0
  38. package/dist/schemas/messages/server_event.schema.json +30 -0
  39. package/dist/schemas/messages/server_game_over.schema.json +49 -0
  40. package/dist/schemas/messages/server_game_start.schema.json +99 -0
  41. package/dist/schemas/messages/server_game_state.schema.json +33 -0
  42. package/dist/schemas/messages/server_match_cancelled.schema.json +51 -0
  43. package/dist/schemas/messages/server_match_confirm_request.schema.json +37 -0
  44. package/dist/schemas/messages/server_queue_joined.schema.json +26 -0
  45. package/dist/schemas/messages/server_queue_left.schema.json +24 -0
  46. package/dist/schemas/messages/server_readiness_check.schema.json +42 -0
  47. package/dist/schemas/messages/server_welcome.schema.json +49 -0
  48. package/dist/schemas/rest/README.md +85 -0
  49. package/dist/schemas/rest/agent_status_response.schema.json +34 -0
  50. package/dist/schemas/rest/claim_request.schema.json +20 -0
  51. package/dist/schemas/rest/claim_response.schema.json +24 -0
  52. package/dist/schemas/rest/error_response.schema.json +15 -0
  53. package/dist/schemas/rest/register_request.schema.json +35 -0
  54. package/dist/schemas/rest/register_response.schema.json +54 -0
  55. package/dist/types/account/credentials.d.ts +29 -0
  56. package/dist/types/account/errors.d.ts +61 -0
  57. package/dist/types/account/registration.d.ts +26 -0
  58. package/dist/types/agents/agent.d.ts +82 -0
  59. package/dist/types/agents/state-machine.d.ts +96 -0
  60. package/dist/types/bridge/config.d.ts +35 -0
  61. package/dist/types/bridge/hermes-provider.d.ts +9 -0
  62. package/dist/types/bridge/openclaw-provider.d.ts +9 -0
  63. package/dist/types/bridge/pairing.d.ts +18 -0
  64. package/dist/types/bridge/provider.d.ts +18 -0
  65. package/dist/types/bridge/runner.d.ts +30 -0
  66. package/dist/types/bridge/service.d.ts +55 -0
  67. package/dist/types/bridge/update-check.d.ts +23 -0
  68. package/dist/types/cli/agent-resolver.d.ts +25 -0
  69. package/dist/types/cli/argv.d.ts +13 -0
  70. package/dist/types/cli/commands/agent-list.d.ts +2 -0
  71. package/dist/types/cli/commands/agent-status.d.ts +2 -0
  72. package/dist/types/cli/commands/bridge-accept.d.ts +2 -0
  73. package/dist/types/cli/commands/bridge-challenge.d.ts +2 -0
  74. package/dist/types/cli/commands/bridge-connect.d.ts +2 -0
  75. package/dist/types/cli/commands/bridge-register.d.ts +2 -0
  76. package/dist/types/cli/commands/bridge-run.d.ts +7 -0
  77. package/dist/types/cli/commands/bridge-service.d.ts +3 -0
  78. package/dist/types/cli/commands/bridge-set.d.ts +2 -0
  79. package/dist/types/cli/commands/bridge-start.d.ts +2 -0
  80. package/dist/types/cli/commands/bridge-status.d.ts +2 -0
  81. package/dist/types/cli/commands/bridge-uninstall.d.ts +4 -0
  82. package/dist/types/cli/commands/config-init.d.ts +2 -0
  83. package/dist/types/cli/commands/config-probe.d.ts +2 -0
  84. package/dist/types/cli/commands/config-validate.d.ts +2 -0
  85. package/dist/types/cli/commands/daily-off.d.ts +2 -0
  86. package/dist/types/cli/commands/daily-on.d.ts +2 -0
  87. package/dist/types/cli/commands/daily-set.d.ts +2 -0
  88. package/dist/types/cli/commands/daily-show.d.ts +2 -0
  89. package/dist/types/cli/commands/doctor.d.ts +2 -0
  90. package/dist/types/cli/commands/init.d.ts +2 -0
  91. package/dist/types/cli/commands/join.d.ts +2 -0
  92. package/dist/types/cli/commands/leave.d.ts +2 -0
  93. package/dist/types/cli/commands/mcp.d.ts +2 -0
  94. package/dist/types/cli/commands/runtime-management.d.ts +16 -0
  95. package/dist/types/cli/commands/runtime-setup-state.d.ts +21 -0
  96. package/dist/types/cli/commands/runtime-setup.d.ts +23 -0
  97. package/dist/types/cli/commands/serve.d.ts +2 -0
  98. package/dist/types/cli/commands/service/launchd.d.ts +71 -0
  99. package/dist/types/cli/commands/service/platform.d.ts +69 -0
  100. package/dist/types/cli/commands/service/systemd.d.ts +55 -0
  101. package/dist/types/cli/commands/service/types.d.ts +64 -0
  102. package/dist/types/cli/commands/service-install.d.ts +29 -0
  103. package/dist/types/cli/commands/setup.d.ts +2 -0
  104. package/dist/types/cli/commands/shutdown.d.ts +2 -0
  105. package/dist/types/cli/commands/stubs.d.ts +24 -0
  106. package/dist/types/cli/commands/version.d.ts +2 -0
  107. package/dist/types/cli/control-client.d.ts +59 -0
  108. package/dist/types/cli/format.d.ts +52 -0
  109. package/dist/types/cli/main.d.ts +18 -0
  110. package/dist/types/cli/runtime-files.d.ts +11 -0
  111. package/dist/types/cli/shared.d.ts +74 -0
  112. package/dist/types/controlapi/profile-routes.d.ts +49 -0
  113. package/dist/types/controlapi/server.d.ts +3 -0
  114. package/dist/types/controlapi/types.d.ts +136 -0
  115. package/dist/types/daemon/agent-decision-adapter.d.ts +40 -0
  116. package/dist/types/daemon/lifecycle.d.ts +85 -0
  117. package/dist/types/daemon/router.d.ts +97 -0
  118. package/dist/types/daemon/runtime-files-write.d.ts +76 -0
  119. package/dist/types/decision/direct-model/anthropic.d.ts +12 -0
  120. package/dist/types/decision/direct-model/errors.d.ts +59 -0
  121. package/dist/types/decision/direct-model/openai.d.ts +12 -0
  122. package/dist/types/decision/direct-model/types.d.ts +20 -0
  123. package/dist/types/decision/parser-types.d.ts +31 -0
  124. package/dist/types/decision/prompt-builder.d.ts +10 -0
  125. package/dist/types/decision/provider.d.ts +50 -0
  126. package/dist/types/decision/types.d.ts +87 -0
  127. package/dist/types/games/_shared/player-info.d.ts +14 -0
  128. package/dist/types/games/coup/action-parser.d.ts +3 -0
  129. package/dist/types/games/coup/fallback.d.ts +8 -0
  130. package/dist/types/games/coup/state-formatter.d.ts +14 -0
  131. package/dist/types/games/liars_dice/action-parser.d.ts +3 -0
  132. package/dist/types/games/liars_dice/fallback.d.ts +8 -0
  133. package/dist/types/games/liars_dice/state-formatter.d.ts +14 -0
  134. package/dist/types/games/texas_holdem/action-parser.d.ts +3 -0
  135. package/dist/types/games/texas_holdem/fallback.d.ts +8 -0
  136. package/dist/types/games/texas_holdem/state-formatter.d.ts +14 -0
  137. package/dist/types/identity/identity-manager.d.ts +59 -0
  138. package/dist/types/index.d.ts +30 -0
  139. package/dist/types/llm/adapter-registry.d.ts +27 -0
  140. package/dist/types/llm/adapters/anthropic-messages.d.ts +2 -0
  141. package/dist/types/llm/adapters/deepseek-chat-completions.d.ts +2 -0
  142. package/dist/types/llm/adapters/openai-chat-compat.d.ts +2 -0
  143. package/dist/types/llm/adapters/openai-chat-completions.d.ts +2 -0
  144. package/dist/types/llm/adapters/openai-responses.d.ts +2 -0
  145. package/dist/types/llm/adapters/types.d.ts +128 -0
  146. package/dist/types/llm/capabilities/validate-capabilities.d.ts +68 -0
  147. package/dist/types/mcp/control-client.d.ts +54 -0
  148. package/dist/types/mcp/profile-tools.d.ts +10 -0
  149. package/dist/types/mcp/server.d.ts +10 -0
  150. package/dist/types/mcp/tools.d.ts +31 -0
  151. package/dist/types/mcp/types.d.ts +27 -0
  152. package/dist/types/profile/config-schema.d.ts +199 -0
  153. package/dist/types/profile/identity-schema.d.ts +75 -0
  154. package/dist/types/profile/index.d.ts +7 -0
  155. package/dist/types/profile/migrate.d.ts +16 -0
  156. package/dist/types/profile/profile-loader.d.ts +64 -0
  157. package/dist/types/profile/secret-ref.d.ts +82 -0
  158. package/dist/types/profile/soul.d.ts +46 -0
  159. package/dist/types/profile/strategy-schema.d.ts +70 -0
  160. package/dist/types/protocol/schemas.d.ts +11 -0
  161. package/dist/types/protocol/types.d.ts +1333 -0
  162. package/dist/types/reflection/proposal-store.d.ts +50 -0
  163. package/dist/types/reflection/reflection-engine.d.ts +81 -0
  164. package/dist/types/scheduler/daily.d.ts +47 -0
  165. package/dist/types/scheduler/types.d.ts +42 -0
  166. package/dist/types/session/match-session-manager.d.ts +113 -0
  167. package/dist/types/session/session-context-builder.d.ts +68 -0
  168. package/dist/types/store/errors.d.ts +23 -0
  169. package/dist/types/store/paths.d.ts +3 -0
  170. package/dist/types/store/schema.generated.d.ts +1 -0
  171. package/dist/types/store/sqlite.d.ts +36 -0
  172. package/dist/types/wsclient/client.d.ts +220 -0
  173. package/dist/types/wsclient/errors.d.ts +106 -0
  174. package/dist/types/wsclient/frame-handler.d.ts +20 -0
  175. package/dist/types/wsclient/reconnect.d.ts +84 -0
  176. package/package.json +53 -0
@@ -0,0 +1,85 @@
1
+ import { type AgentRow } from "../store/sqlite";
2
+ import type { DailyScheduleConfig } from "../scheduler/types";
3
+ import { type ControlLogEvent } from "../controlapi/types";
4
+ import type { StrategyProfile } from "../decision/types";
5
+ import { buildConnectWithGameStartCache } from "./agent-decision-adapter";
6
+ export interface DaemonOptions {
7
+ /** Default "127.0.0.1". Plan ADR-013 locks to loopback in M1; daemon
8
+ * validates in cli/commands/serve.ts (Step 3 wires --host argv). */
9
+ readonly host?: string;
10
+ /** Default 0 — OS picks an unused port. Resolved bound port surfaces
11
+ * on `DaemonHandle.bound.port`. */
12
+ readonly port?: number;
13
+ readonly onLog?: (event: DaemonLogEvent) => void;
14
+ readonly testHooks?: DaemonTestHooks;
15
+ }
16
+ export type DaemonLogLevel = "info" | "warn" | "error";
17
+ export type DaemonLogCode = "lock_acquired" | "agents_loaded" | "control_server_listening" | "runtime_files_written" | "daemon_ready" | "signal_received" | "shutdown_initiated" | "shutdown_completed" | "shutdown_forced" | "agent_load_failed" | "agent_skipped_turn" | "control_server_proxy" | "lock_release_failed" | "tmp_cleanup_failed";
18
+ export interface DaemonLogEvent {
19
+ readonly level: DaemonLogLevel;
20
+ readonly code: DaemonLogCode;
21
+ readonly message: string;
22
+ readonly cause?: unknown;
23
+ /** Only set when `code === "control_server_proxy"` — forwarded
24
+ * M1-16 ControlLogEvent payload. */
25
+ readonly controlEvent?: ControlLogEvent;
26
+ }
27
+ export interface DaemonTestHooks {
28
+ /** Override timer functions (force-exit timer, etc). */
29
+ readonly setTimeout?: typeof globalThis.setTimeout;
30
+ readonly clearTimeout?: typeof globalThis.clearTimeout;
31
+ /** Override process.on signal subscription. Default attaches to
32
+ * process; tests inject a fake emitter so signals can be simulated
33
+ * without sending real SIGINT to the test runner. The returned
34
+ * function detaches the listener. */
35
+ readonly onSignal?: (sig: NodeJS.Signals, listener: () => void) => () => void;
36
+ /** Override `process.exit`. Production path uses `process.exit`;
37
+ * tests inject a recorder so the test runner is never killed. Used
38
+ * only on force-exit / second-signal paths (rev3 fix #3). */
39
+ readonly exit?: (code: number) => void;
40
+ /** Override agent row loading. Production reads SQLite via
41
+ * openDatabase().listAgents(). Tests inject fixtures without a real
42
+ * DB. */
43
+ readonly loadAgents?: () => readonly AgentRow[];
44
+ /** Override strategy profile loading. Production reads
45
+ * `getRuntimeHome()/agents/<name>/strategy.json` (post-Step-2 path
46
+ * detail; Step 2 minimum is testHook-driven and tests inject
47
+ * fixtures). Throw to trigger per-agent skip. */
48
+ readonly loadStrategy?: (agentName: string) => StrategyProfile;
49
+ /** Override daily-schedule config loading. Returning null = no
50
+ * scheduler. */
51
+ readonly loadDailyConfig?: (agentName: string) => DailyScheduleConfig | null;
52
+ /** Override `os.platform()` for case 26 (Windows SIGTERM omission). */
53
+ readonly platform?: NodeJS.Platform;
54
+ /** Override the wsclient connect factory used by AgentInstance.
55
+ * Default = `createReconnectingWSClient` from M1-07. Tests inject
56
+ * to capture wsclient options (asserts apiKey === platformKey, etc)
57
+ * without opening a real socket. */
58
+ readonly wsConnectFactory?: Parameters<typeof buildConnectWithGameStartCache>[1];
59
+ /** Force-exit timeout in ms. Default 30000 (TED 拍板点 #7).
60
+ * Tests use a small value (e.g. 50ms) to validate timeout behavior
61
+ * without making the suite slow. */
62
+ readonly forceExitTimeoutMs?: number;
63
+ }
64
+ export type ShutdownReason = {
65
+ readonly source: "signal";
66
+ readonly signal: NodeJS.Signals;
67
+ } | {
68
+ readonly source: "control_api";
69
+ } | {
70
+ readonly source: "startup_failure";
71
+ readonly cause: unknown;
72
+ };
73
+ export interface DaemonHandle {
74
+ readonly bound: {
75
+ readonly host: string;
76
+ readonly port: number;
77
+ };
78
+ /** Idempotent — second call returns the same promise as the first. */
79
+ requestShutdown(reason: ShutdownReason): Promise<number>;
80
+ /** Long-lived completion. Resolves with the final exit code; never
81
+ * rejects. runServe() awaits this to keep the foreground process
82
+ * alive (rev2 fix #1). */
83
+ readonly closed: Promise<number>;
84
+ }
85
+ export declare function startDaemon(opts?: DaemonOptions): Promise<DaemonHandle>;
@@ -0,0 +1,97 @@
1
+ import type { AgentInstanceSnapshot } from "../agents/agent";
2
+ export interface RouterAgentConfig {
3
+ readonly name: string;
4
+ readonly id?: string;
5
+ readonly enabled?: boolean;
6
+ readonly metadata?: Readonly<Record<string, unknown>>;
7
+ }
8
+ export interface RouterAgentRuntime {
9
+ start(): Promise<AgentInstanceSnapshot>;
10
+ stop(reason?: string): Promise<void>;
11
+ joinQueue(game: string, mode?: string, opts?: {
12
+ readonly oneShot?: boolean;
13
+ readonly count?: number;
14
+ }): void;
15
+ leaveQueue(): void;
16
+ confirmMatch(confirmId?: string): void;
17
+ snapshot(): AgentInstanceSnapshot;
18
+ onState(handler: (snapshot: AgentInstanceSnapshot) => void): () => void;
19
+ }
20
+ export type RouterAgentFactory = (config: RouterAgentConfig) => RouterAgentRuntime;
21
+ export type RouterAgentSelector = string | {
22
+ readonly name?: string;
23
+ readonly id?: string;
24
+ };
25
+ export interface RouterSnapshot {
26
+ readonly agents: readonly AgentInstanceSnapshot[];
27
+ }
28
+ export interface MultiAgentRouterOptions {
29
+ readonly agents?: readonly RouterAgentConfig[];
30
+ readonly agentFactory: RouterAgentFactory;
31
+ readonly onNotify?: (event: RouterNotifyEvent) => void;
32
+ }
33
+ export interface RouterNotifyEvent {
34
+ readonly level: "info" | "warning" | "error";
35
+ readonly code: string;
36
+ readonly message: string;
37
+ readonly agent?: string;
38
+ readonly cause?: unknown;
39
+ }
40
+ export type RouterErrorKind = "router_duplicate_agent" | "router_agent_not_found" | "router_selector_mismatch" | "router_agent_factory" | "router_agent_lifecycle";
41
+ export declare abstract class RouterError extends Error {
42
+ abstract readonly kind: RouterErrorKind;
43
+ readonly cause: unknown;
44
+ protected constructor(message: string, cause?: unknown);
45
+ }
46
+ export declare class RouterDuplicateAgentError extends RouterError {
47
+ readonly name = "RouterDuplicateAgentError";
48
+ readonly kind: "router_duplicate_agent";
49
+ readonly field: "name" | "id";
50
+ readonly value: string;
51
+ constructor(field: "name" | "id", value: string);
52
+ }
53
+ export declare class RouterAgentNotFoundError extends RouterError {
54
+ readonly name = "RouterAgentNotFoundError";
55
+ readonly kind: "router_agent_not_found";
56
+ readonly selector: RouterAgentSelector;
57
+ constructor(selector: RouterAgentSelector);
58
+ }
59
+ export declare class RouterSelectorMismatchError extends RouterError {
60
+ readonly name = "RouterSelectorMismatchError";
61
+ readonly kind: "router_selector_mismatch";
62
+ readonly nameSelector: string;
63
+ readonly idSelector: string;
64
+ constructor(nameSelector: string, idSelector: string);
65
+ }
66
+ export declare class RouterAgentFactoryError extends RouterError {
67
+ readonly name = "RouterAgentFactoryError";
68
+ readonly kind: "router_agent_factory";
69
+ readonly agentName: string;
70
+ constructor(agentName: string, cause: unknown);
71
+ }
72
+ export declare class RouterAgentLifecycleError extends RouterError {
73
+ readonly name = "RouterAgentLifecycleError";
74
+ readonly kind: "router_agent_lifecycle";
75
+ readonly agentName: string;
76
+ readonly operation: "start" | "stop" | "remove";
77
+ constructor(agentName: string, operation: "start" | "stop" | "remove", cause: unknown);
78
+ }
79
+ export declare class MultiAgentRouter {
80
+ #private;
81
+ constructor(opts: MultiAgentRouterOptions);
82
+ addAgent(config: RouterAgentConfig): void;
83
+ removeAgent(selector: RouterAgentSelector): Promise<void>;
84
+ getAgent(selector: RouterAgentSelector): RouterAgentRuntime;
85
+ listAgents(): readonly AgentInstanceSnapshot[];
86
+ snapshot(): RouterSnapshot;
87
+ startAgent(selector: RouterAgentSelector): Promise<AgentInstanceSnapshot>;
88
+ stopAgent(selector: RouterAgentSelector, reason?: string): Promise<void>;
89
+ startAll(): Promise<RouterSnapshot>;
90
+ stopAll(reason?: string): Promise<void>;
91
+ joinQueue(selector: RouterAgentSelector, game: string, mode?: string, opts?: {
92
+ readonly oneShot?: boolean;
93
+ readonly count?: number;
94
+ }): void;
95
+ leaveQueue(selector: RouterAgentSelector): void;
96
+ confirmMatch(selector: RouterAgentSelector, confirmId?: string): void;
97
+ }
@@ -0,0 +1,76 @@
1
+ export type RuntimeFilesWriteErrorKind = "write_failed" | "lock_held_by_other" | "lock_acquire_failed";
2
+ export declare class RuntimeFilesWriteError extends Error {
3
+ readonly name = "RuntimeFilesWriteError";
4
+ readonly kind: RuntimeFilesWriteErrorKind;
5
+ readonly filePath: string;
6
+ /** Set when kind === "lock_held_by_other"; the holding process's PID
7
+ * as recorded in the pid file. Caller maps to exit-6 + user message. */
8
+ readonly heldByPid?: number;
9
+ readonly cause?: unknown;
10
+ constructor(kind: RuntimeFilesWriteErrorKind, filePath: string, message: string, init?: {
11
+ heldByPid?: number;
12
+ cause?: unknown;
13
+ });
14
+ }
15
+ /** Generates a fresh 64-char hex token (32 random bytes encoded). */
16
+ export declare function generateToken(): string;
17
+ /** Atomic write: token file at `getRuntimeHome()/token` mode 0600.
18
+ * Internal: writes to `<path>.<pid>.<counter>.tmp` then `fs.renameSync`.
19
+ * Asserts token matches /^[0-9a-f]{64}$/ before write (TED Q2 — hex
20
+ * literal assertion to defeat future regressions that swap the random
21
+ * source for one emitting non-printable bytes / mixed case). */
22
+ export declare function writeToken(token: string): void;
23
+ /** Atomic write: port file at `getRuntimeHome()/port` mode 0644.
24
+ * Content = `String(port)` no trailing newline. Range checked
25
+ * [1, 65535] (matches M1-17 read-side validator). */
26
+ export declare function writePort(port: number): void;
27
+ /** Atomic write: pid file at `getRuntimeHome()/pid` mode 0644.
28
+ * Content = `String(pid)` no trailing newline. */
29
+ export declare function writePid(pid: number): void;
30
+ /** Best-effort unlink of token + port + pid files on graceful shutdown.
31
+ * ENOENT is silent (file already gone). Other failures only log via
32
+ * onLog so shutdown sequence never throws on cleanup. */
33
+ export declare function unlinkRuntimeFiles(opts: {
34
+ onLog?: (msg: string) => void;
35
+ }): void;
36
+ /** Cleans up `*.tmp` files in `getRuntimeHome()` left over from a
37
+ * previous daemon crash. Called once at startup before any write.
38
+ * Failures silent — best-effort only. */
39
+ export declare function cleanupStaleTmpFiles(): void;
40
+ export interface LockHandle {
41
+ /** Releases lock + unlinks lock file. Idempotent — second call no-op. */
42
+ release(): void;
43
+ }
44
+ export interface AcquireDaemonLockOptions {
45
+ /** Override `process.kill(pid, 0)` liveness probe for tests. Default
46
+ * uses `process.kill(pid, 0)` and treats EPERM (cross-user, can't
47
+ * signal) as `true` (alive) so we never overwrite a foreign daemon's
48
+ * lock — TED 拍板点 #6 + Group 2 case 17 "EPERM 不能确认 → safe
49
+ * 保留". Production callers omit this option. */
50
+ readonly processIsAlive?: (pid: number) => boolean;
51
+ }
52
+ /** Acquire exclusive lock at `getRuntimeHome()/lock` (`O_EXCL` create).
53
+ *
54
+ * Outcomes when the lock file already exists (EEXIST):
55
+ * - **valid pid + alive (per probe)** → throw `lock_held_by_other`
56
+ * with `heldByPid` set; lock + pid files preserved.
57
+ * - **valid pid + dead** → STALE; clean lock + pid + retry once.
58
+ * - **pid file missing / corrupt content / read error** → AMBIGUOUS;
59
+ * throw `lock_acquire_failed`, **preserve lock + pid files**, do
60
+ * not invoke the liveness probe. This window is exactly when a
61
+ * racing daemon has just acquired the lock but has not yet written
62
+ * its pid (Step 2 startup order: `acquireDaemonLock()` then
63
+ * `writePid(process.pid)`); collapsing it into stale-cleanup would
64
+ * let daemon B steal daemon A's live lock and acquire its own
65
+ * (TED rev8 review-fix — multi-instance guarantee P1).
66
+ * - **stale-cleanup retry STILL races (EEXIST again)** → throw
67
+ * `lock_acquire_failed` (rare; another daemon snuck in between
68
+ * our unlink and our second openSync).
69
+ * - **non-EEXIST FS error on openSync** (permission denied, ENOSPC,
70
+ * etc.) → throw `lock_acquire_failed`.
71
+ * - **same process already holds this exact lock** → throw
72
+ * `lock_acquire_failed` (reentrancy guard, TED Group 2 case 19).
73
+ *
74
+ * Caller maps `lock_held_by_other` to exit 6 ("daemon already
75
+ * running"); `lock_acquire_failed` to exit 1 (TED 拍板点 #8). */
76
+ export declare function acquireDaemonLock(opts?: AcquireDaemonLockOptions): LockHandle;
@@ -0,0 +1,12 @@
1
+ import type { DirectModelClient } from "./types";
2
+ export interface AnthropicClientOptions {
3
+ readonly apiKey: string;
4
+ readonly model: string;
5
+ /** Default "https://api.anthropic.com". Trailing slashes are stripped. */
6
+ readonly baseURL?: string;
7
+ /** Default "2023-06-01" (current Messages API). */
8
+ readonly anthropicVersion?: string;
9
+ /** Test injection point. Defaults to globalThis.fetch. */
10
+ readonly fetchImpl?: typeof fetch;
11
+ }
12
+ export declare function createAnthropicClient(opts: AnthropicClientOptions): DirectModelClient;
@@ -0,0 +1,59 @@
1
+ import type { DirectModelProviderName } from "./types";
2
+ export type DirectModelErrorKind = "direct_model_network" | "direct_model_http" | "direct_model_aborted" | "direct_model_invalid_response" | "direct_model_unsupported";
3
+ export declare abstract class DirectModelError extends Error {
4
+ abstract readonly kind: DirectModelErrorKind;
5
+ readonly provider: DirectModelProviderName;
6
+ readonly cause: unknown;
7
+ protected constructor(provider: DirectModelProviderName, message: string, cause?: unknown);
8
+ }
9
+ /** fetch() threw before producing a Response. Wraps the underlying
10
+ * Node net / TLS / DNS error (or whatever fetch surfaced). */
11
+ export declare class DirectModelNetworkError extends DirectModelError {
12
+ readonly name = "DirectModelNetworkError";
13
+ readonly kind: "direct_model_network";
14
+ constructor(provider: DirectModelProviderName, message: string, cause?: unknown);
15
+ }
16
+ /** HTTP response with non-2xx status. `bodySnippet` is the response
17
+ * payload after redaction + truncation (≤ SNIPPET_MAX); callers
18
+ * SHOULD NOT JSON.parse it without checking Content-Type. */
19
+ export declare class DirectModelHttpError extends DirectModelError {
20
+ readonly name = "DirectModelHttpError";
21
+ readonly kind: "direct_model_http";
22
+ readonly status: number;
23
+ readonly bodySnippet?: string;
24
+ constructor(provider: DirectModelProviderName, status: number, message: string, bodySnippet?: string, cause?: unknown);
25
+ }
26
+ /** Caller-supplied AbortSignal fired (either pre-aborted or
27
+ * mid-flight). Distinguished from DirectModelNetworkError so the
28
+ * caller can suppress retry on intentional cancel. */
29
+ export declare class DirectModelAbortedError extends DirectModelError {
30
+ readonly name = "DirectModelAbortedError";
31
+ readonly kind: "direct_model_aborted";
32
+ constructor(provider: DirectModelProviderName, message: string, cause?: unknown);
33
+ }
34
+ /** 2xx response but the body could not be parsed as JSON, or the
35
+ * parsed shape did not match the expected provider envelope (e.g.
36
+ * Anthropic missing `content[0].text`, OpenAI missing
37
+ * `choices[0].message.content`). `responseSnippet` is redacted +
38
+ * truncated. */
39
+ export declare class DirectModelInvalidResponseError extends DirectModelError {
40
+ readonly name = "DirectModelInvalidResponseError";
41
+ readonly kind: "direct_model_invalid_response";
42
+ readonly responseSnippet?: string;
43
+ constructor(provider: DirectModelProviderName, message: string, responseSnippet?: string, cause?: unknown);
44
+ }
45
+ /** Caller passed an option that fails synchronous validation
46
+ * (empty apiKey, empty model, maxTokens <= 0, etc). Thrown before
47
+ * any network call. `field` names the offending option for
48
+ * programmatic handling (don't parse message text). */
49
+ export declare class DirectModelUnsupportedError extends DirectModelError {
50
+ readonly name = "DirectModelUnsupportedError";
51
+ readonly kind: "direct_model_unsupported";
52
+ readonly field: string;
53
+ constructor(provider: DirectModelProviderName, field: string, message: string);
54
+ }
55
+ export declare const SNIPPET_MAX = 2048;
56
+ export declare const REDACTED_PLACEHOLDER = "[REDACTED]";
57
+ export declare function redactSecrets(s: string, secrets: readonly string[]): string;
58
+ export declare function truncateSnippet(s: string, max?: number): string;
59
+ export declare function sanitizeSnippet(raw: string | undefined, secrets: readonly string[]): string | undefined;
@@ -0,0 +1,12 @@
1
+ import type { DirectModelClient } from "./types";
2
+ export interface OpenAIClientOptions {
3
+ readonly apiKey: string;
4
+ readonly model: string;
5
+ /** Default "https://api.openai.com/v1". Trailing slashes stripped. */
6
+ readonly baseURL?: string;
7
+ /** Optional OpenAI-Organization header. */
8
+ readonly organization?: string;
9
+ /** Test injection point. Defaults to globalThis.fetch. */
10
+ readonly fetchImpl?: typeof fetch;
11
+ }
12
+ export declare function createOpenAIClient(opts: OpenAIClientOptions): DirectModelClient;
@@ -0,0 +1,20 @@
1
+ export type DirectModelProviderName = "anthropic" | "openai";
2
+ export interface DirectModelGenerateRequest {
3
+ readonly systemPrompt: string;
4
+ readonly userPrompt: string;
5
+ readonly temperature?: number;
6
+ readonly maxTokens: number;
7
+ readonly signal?: AbortSignal;
8
+ }
9
+ export interface DirectModelGenerateResponse {
10
+ readonly text: string;
11
+ readonly inputTokens?: number;
12
+ readonly outputTokens?: number;
13
+ readonly latencyMs: number;
14
+ readonly raw: unknown;
15
+ }
16
+ export interface DirectModelClient {
17
+ readonly provider: DirectModelProviderName;
18
+ readonly model: string;
19
+ generate(req: DirectModelGenerateRequest): Promise<DirectModelGenerateResponse>;
20
+ }
@@ -0,0 +1,31 @@
1
+ import type { LegalAction } from "./types";
2
+ export type ParseInvalidReason = "json_parse" | "missing_fields" | "unknown_action_type" | "action_not_legal" | "data_validation";
3
+ export type ParseResult = {
4
+ readonly kind: "ok";
5
+ /**
6
+ * For simple / server-enumerated actions, this is the original
7
+ * server-provided `LegalAction` reference (reference equality
8
+ * holds against the matching entry in `legalActions`). For
9
+ * Texas Hold'em `raise` and Liar's Dice `bid`, the LLM picks
10
+ * the concrete amount / quantity-face within the server hints,
11
+ * so the parser constructs a fresh `{ type, data }` object —
12
+ * reference equality with `legalActions[i]` does NOT hold for
13
+ * those two cases (M1-14 拍板点 #5 + Risks #10).
14
+ */
15
+ readonly action: LegalAction;
16
+ /**
17
+ * Optional one-line reasoning extracted from the LLM JSON
18
+ * (`{"summary": ...}`). M1-14 provider passes this through to
19
+ * `DecisionResponse.summary` without re-parsing.
20
+ */
21
+ readonly summary?: string;
22
+ } | {
23
+ readonly kind: "invalid";
24
+ readonly reason: ParseInvalidReason;
25
+ /**
26
+ * Up to `parseRetryHintCharCap` chars (default 500) of the raw
27
+ * LLM output, used to populate the corrective re-prompt. Parser
28
+ * is responsible for truncation; provider does not re-truncate.
29
+ */
30
+ readonly rawSnippet?: string;
31
+ };
@@ -0,0 +1,10 @@
1
+ import type { DecisionRequest } from "./types";
2
+ export interface PromptBuilderOptions {
3
+ /** Hard cap on assembled userPrompt length (chars). Default 16384. */
4
+ readonly userPromptCharCap?: number;
5
+ }
6
+ export interface BuiltPrompt {
7
+ readonly systemPrompt: string;
8
+ readonly userPrompt: string;
9
+ }
10
+ export declare function buildPrompt(req: DecisionRequest, options?: PromptBuilderOptions): BuiltPrompt;
@@ -0,0 +1,50 @@
1
+ import { createAnthropicClient } from "./direct-model/anthropic";
2
+ import { createOpenAIClient } from "./direct-model/openai";
3
+ import type { DirectModelProviderName } from "./direct-model/types";
4
+ import type { DecisionRequest, DecisionResponse } from "./types";
5
+ export type { DirectModelProviderName } from "./direct-model/types";
6
+ export interface DecisionProvider {
7
+ readonly name: string;
8
+ decide(req: DecisionRequest): Promise<DecisionResponse>;
9
+ healthCheck(): Promise<boolean>;
10
+ }
11
+ export interface DirectModelProviderOptions {
12
+ /** Identifier for this provider instance. plan §5.5
13
+ * DecisionProvider.name. */
14
+ readonly name: string;
15
+ /** Returns API key for the given (provider, model). Caller decides
16
+ * the source (env / keychain / file per plan §5.7 layer 2). Must be
17
+ * synchronous so the factory itself stays sync. Returning an empty
18
+ * string causes `decide()` to throw a fatal_unsupported error and
19
+ * `healthCheck()` to return false. */
20
+ readonly apiKeyResolver: (provider: DirectModelProviderName, model: string) => string;
21
+ /** Override for the direct-model client factories (testing). When
22
+ * omitted, defaults to the M1-11 `createAnthropicClient` /
23
+ * `createOpenAIClient`. */
24
+ readonly clientFactory?: {
25
+ readonly anthropic?: typeof createAnthropicClient;
26
+ readonly openai?: typeof createOpenAIClient;
27
+ };
28
+ /** Default 2. Total LLM calls = 1 + retryBudget. */
29
+ readonly retryBudget?: number;
30
+ /** Default 500. Max chars of raw LLM output threaded into the
31
+ * corrective re-prompt. */
32
+ readonly parseRetryHintCharCap?: number;
33
+ /** Override for transport. Passed through to the client factory. */
34
+ readonly fetchImpl?: typeof fetch;
35
+ /** Explicit healthCheck target. When omitted, `healthCheck()`
36
+ * returns false (catch-all consistent with the
37
+ * `Promise<boolean>` contract). M1-14 rev3 锁:无 profile → false。 */
38
+ readonly healthCheckProfile?: {
39
+ readonly provider: DirectModelProviderName;
40
+ readonly model: string;
41
+ };
42
+ }
43
+ export type DecisionProviderErrorKind = "fatal_http" | "fatal_aborted" | "fatal_unsupported" | "fatal_caller_bug";
44
+ export declare class DecisionProviderError extends Error {
45
+ readonly name = "DecisionProviderError";
46
+ readonly kind: DecisionProviderErrorKind;
47
+ readonly cause: unknown;
48
+ constructor(kind: DecisionProviderErrorKind, message: string, cause?: unknown);
49
+ }
50
+ export declare function createDirectModelProvider(opts: DirectModelProviderOptions): DecisionProvider;
@@ -0,0 +1,87 @@
1
+ import type { Action, CoupRules, Event, LiarsDiceRules, PlayerInfo, TexasHoldemRules } from "../protocol/types";
2
+ export type GameType = "texas_holdem" | "liars_dice" | "coup";
3
+ export type GameRules = {
4
+ readonly game: "texas_holdem";
5
+ readonly rules: TexasHoldemRules;
6
+ } | {
7
+ readonly game: "liars_dice";
8
+ readonly rules: LiarsDiceRules;
9
+ } | {
10
+ readonly game: "coup";
11
+ readonly rules: CoupRules;
12
+ };
13
+ export type LegalAction = Action;
14
+ export interface GameSpecificProfile {
15
+ readonly extraPrompt?: string;
16
+ /**
17
+ * [0..1] risk-aversion hyperparameter. Information-only for M1-12;
18
+ * consumed by M1-14 (rev3 锁:M1-13 fallback 也不读).
19
+ */
20
+ readonly riskAversion?: number;
21
+ }
22
+ export interface StrategyProfile {
23
+ readonly name: string;
24
+ readonly version: number;
25
+ readonly provider: "anthropic" | "openai";
26
+ readonly model: string;
27
+ readonly systemPrompt: string;
28
+ readonly temperature?: number;
29
+ readonly maxTokens: number;
30
+ readonly costCapUSDPerMatch?: number;
31
+ readonly gameSpecific?: Partial<Record<GameType, GameSpecificProfile>>;
32
+ }
33
+ export interface DecisionRequest {
34
+ readonly game: GameType;
35
+ readonly matchId: string;
36
+ readonly playerId: string;
37
+ /**
38
+ * Game-specific rules from server_game_start.data.rules. Caller
39
+ * must ensure shape matches the `game` discriminator
40
+ * (TexasHoldemRules / LiarsDiceRules / CoupRules per
41
+ * protocol/types.ts MsgGameStartDataTexasHoldem / LiarsDice / Coup).
42
+ */
43
+ readonly rules: GameRules["rules"];
44
+ readonly legalActions: readonly LegalAction[];
45
+ /**
46
+ * Game-specific state from action_request.data.state. Protocol-
47
+ * level shape is opaque (`state: {}` per messages/server_action_
48
+ * request.schema.json); narrowing is per-game. M1-12 prompt-
49
+ * builder dispatches on `game` and treats this as the per-game
50
+ * state shape (TexasHoldemState / LiarsDiceState / CoupState).
51
+ */
52
+ readonly publicState: unknown;
53
+ /**
54
+ * Reserved for future private-narrowed fields. Server currently
55
+ * embeds your_hand / your_dice / your_cards / coins inside
56
+ * publicState; this slot stays optional unknown for forward
57
+ * compatibility (M1-12 Risks #4).
58
+ */
59
+ readonly privateState?: unknown;
60
+ readonly players: readonly PlayerInfo[];
61
+ /**
62
+ * Incremental events since the player's last action_request
63
+ * (action_request.data.new_events). Caller MUST normalize null →
64
+ * [] before constructing DecisionRequest (M1-12 Risks #11; protocol
65
+ * spec allows `new_events: null` on first action_request).
66
+ */
67
+ readonly recentEvents: readonly Event[];
68
+ readonly strategyProfile: StrategyProfile;
69
+ readonly turnTimeoutMs: number;
70
+ readonly decisionBudgetMs: number;
71
+ }
72
+ export interface DecisionResponseProviderMetadata {
73
+ readonly provider: string;
74
+ readonly model: string;
75
+ readonly inputTokens?: number;
76
+ readonly outputTokens?: number;
77
+ readonly costUSD?: number;
78
+ readonly latencyMs: number;
79
+ readonly retries?: number;
80
+ readonly fallback?: boolean;
81
+ }
82
+ export interface DecisionResponse {
83
+ readonly action: string;
84
+ readonly params?: Record<string, unknown>;
85
+ readonly summary?: string;
86
+ readonly providerMetadata: DecisionResponseProviderMetadata;
87
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Read `data[key]` as a finite number. Returns `undefined` if `data` is
3
+ * not a non-null object, the field is missing, the value is not a
4
+ * number, or the value is NaN / Infinity / -Infinity. Caller must omit
5
+ * the output field on `undefined` (never default to 0).
6
+ */
7
+ export declare function readNumberField(data: unknown, key: string): number | undefined;
8
+ /**
9
+ * Read `data[key]` as `string[]`. Returns `undefined` if `data` is not a
10
+ * non-null object, the field is missing, the value is not an array, or
11
+ * any element is not a string. Returns the array reference (no copy) —
12
+ * read-only by convention; caller must NOT mutate.
13
+ */
14
+ export declare function readStringArrayField(data: unknown, key: string): string[] | undefined;
@@ -0,0 +1,3 @@
1
+ import type { LegalAction } from "../../decision/types";
2
+ import type { ParseResult } from "../../decision/parser-types";
3
+ export declare function parseCoupAction(rawText: string, legalActions: readonly LegalAction[], rawSnippetCap?: number): ParseResult;
@@ -0,0 +1,8 @@
1
+ import type { LegalAction } from "../../decision/types";
2
+ import type { CoupState } from "../../protocol/types";
3
+ export interface CoupFallbackInput {
4
+ readonly publicState: CoupState;
5
+ readonly legalActions: readonly LegalAction[];
6
+ readonly yourPlayerId: string;
7
+ }
8
+ export declare function fallbackCoup(input: CoupFallbackInput): LegalAction;
@@ -0,0 +1,14 @@
1
+ import type { CoupRules, CoupState, Event, PlayerInfo } from "../../protocol/types";
2
+ export interface CoupFormatterInput {
3
+ readonly publicState: CoupState;
4
+ readonly privateState?: unknown;
5
+ readonly rules: CoupRules;
6
+ readonly players: readonly PlayerInfo[];
7
+ readonly recentEvents: readonly Event[];
8
+ readonly yourPlayerId: string;
9
+ }
10
+ export interface StateFormatterOutput {
11
+ readonly stateBlock: string;
12
+ readonly recentEventsBlock: string;
13
+ }
14
+ export declare function formatCoupState(input: CoupFormatterInput): StateFormatterOutput;
@@ -0,0 +1,3 @@
1
+ import type { LegalAction } from "../../decision/types";
2
+ import type { ParseResult } from "../../decision/parser-types";
3
+ export declare function parseLiarsDiceAction(rawText: string, legalActions: readonly LegalAction[], rawSnippetCap?: number): ParseResult;
@@ -0,0 +1,8 @@
1
+ import type { LegalAction } from "../../decision/types";
2
+ import type { LiarsDiceState } from "../../protocol/types";
3
+ export interface LiarsDiceFallbackInput {
4
+ readonly publicState: LiarsDiceState;
5
+ readonly legalActions: readonly LegalAction[];
6
+ readonly yourPlayerId: string;
7
+ }
8
+ export declare function fallbackLiarsDice(input: LiarsDiceFallbackInput): LegalAction;
@@ -0,0 +1,14 @@
1
+ import type { Event, LiarsDiceRules, LiarsDiceState, PlayerInfo } from "../../protocol/types";
2
+ export interface LiarsDiceFormatterInput {
3
+ readonly publicState: LiarsDiceState;
4
+ readonly privateState?: unknown;
5
+ readonly rules: LiarsDiceRules;
6
+ readonly players: readonly PlayerInfo[];
7
+ readonly recentEvents: readonly Event[];
8
+ readonly yourPlayerId: string;
9
+ }
10
+ export interface StateFormatterOutput {
11
+ readonly stateBlock: string;
12
+ readonly recentEventsBlock: string;
13
+ }
14
+ export declare function formatLiarsDiceState(input: LiarsDiceFormatterInput): StateFormatterOutput;
@@ -0,0 +1,3 @@
1
+ import type { LegalAction } from "../../decision/types";
2
+ import type { ParseResult } from "../../decision/parser-types";
3
+ export declare function parseTexasHoldemAction(rawText: string, legalActions: readonly LegalAction[], rawSnippetCap?: number): ParseResult;
@@ -0,0 +1,8 @@
1
+ import type { LegalAction } from "../../decision/types";
2
+ import type { TexasHoldemState } from "../../protocol/types";
3
+ export interface TexasHoldemFallbackInput {
4
+ readonly publicState: TexasHoldemState;
5
+ readonly legalActions: readonly LegalAction[];
6
+ readonly yourPlayerId: string;
7
+ }
8
+ export declare function fallbackTexasHoldem(input: TexasHoldemFallbackInput): LegalAction;
@@ -0,0 +1,14 @@
1
+ import type { Event, PlayerInfo, TexasHoldemRules, TexasHoldemState } from "../../protocol/types";
2
+ export interface TexasHoldemFormatterInput {
3
+ readonly publicState: TexasHoldemState;
4
+ readonly privateState?: unknown;
5
+ readonly rules: TexasHoldemRules;
6
+ readonly players: readonly PlayerInfo[];
7
+ readonly recentEvents: readonly Event[];
8
+ readonly yourPlayerId: string;
9
+ }
10
+ export interface StateFormatterOutput {
11
+ readonly stateBlock: string;
12
+ readonly recentEventsBlock: string;
13
+ }
14
+ export declare function formatTexasHoldemState(input: TexasHoldemFormatterInput): StateFormatterOutput;