ghost-dragon 4.2.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 (226) hide show
  1. package/.github/workflows/ci.yml +23 -0
  2. package/CHANGELOG.md +96 -0
  3. package/README.md +193 -0
  4. package/bootstrap.ps1 +83 -0
  5. package/bootstrap.sh +71 -0
  6. package/dist/agent/loop.d.ts +68 -0
  7. package/dist/agent/loop.d.ts.map +1 -0
  8. package/dist/agent/loop.js +135 -0
  9. package/dist/agent/mcp.d.ts +33 -0
  10. package/dist/agent/mcp.d.ts.map +1 -0
  11. package/dist/agent/mcp.js +107 -0
  12. package/dist/agent/session.d.ts +16 -0
  13. package/dist/agent/session.d.ts.map +1 -0
  14. package/dist/agent/session.js +55 -0
  15. package/dist/agent/skills.d.ts +36 -0
  16. package/dist/agent/skills.d.ts.map +1 -0
  17. package/dist/agent/skills.js +153 -0
  18. package/dist/agent/stack.d.ts +21 -0
  19. package/dist/agent/stack.d.ts.map +1 -0
  20. package/dist/agent/stack.js +158 -0
  21. package/dist/agent/task.d.ts +21 -0
  22. package/dist/agent/task.d.ts.map +1 -0
  23. package/dist/agent/task.js +45 -0
  24. package/dist/agent/tools.d.ts +44 -0
  25. package/dist/agent/tools.d.ts.map +1 -0
  26. package/dist/agent/tools.js +262 -0
  27. package/dist/agent/trace.d.ts +34 -0
  28. package/dist/agent/trace.d.ts.map +1 -0
  29. package/dist/agent/trace.js +72 -0
  30. package/dist/agent.d.ts +46 -0
  31. package/dist/agent.d.ts.map +1 -0
  32. package/dist/agent.js +103 -0
  33. package/dist/auth.d.ts +74 -0
  34. package/dist/auth.d.ts.map +1 -0
  35. package/dist/auth.js +116 -0
  36. package/dist/brain/anthropic.d.ts +19 -0
  37. package/dist/brain/anthropic.d.ts.map +1 -0
  38. package/dist/brain/anthropic.js +74 -0
  39. package/dist/brain/claude-cli.d.ts +20 -0
  40. package/dist/brain/claude-cli.d.ts.map +1 -0
  41. package/dist/brain/claude-cli.js +79 -0
  42. package/dist/brain/ghost-ember.d.ts +28 -0
  43. package/dist/brain/ghost-ember.d.ts.map +1 -0
  44. package/dist/brain/ghost-ember.js +97 -0
  45. package/dist/brain/index.d.ts +22 -0
  46. package/dist/brain/index.d.ts.map +1 -0
  47. package/dist/brain/index.js +95 -0
  48. package/dist/brain/openai-compat.d.ts +21 -0
  49. package/dist/brain/openai-compat.d.ts.map +1 -0
  50. package/dist/brain/openai-compat.js +119 -0
  51. package/dist/brain/router/classify.d.ts +23 -0
  52. package/dist/brain/router/classify.d.ts.map +1 -0
  53. package/dist/brain/router/classify.js +160 -0
  54. package/dist/brain/router/execute.d.ts +23 -0
  55. package/dist/brain/router/execute.d.ts.map +1 -0
  56. package/dist/brain/router/execute.js +84 -0
  57. package/dist/brain/router/index.d.ts +26 -0
  58. package/dist/brain/router/index.d.ts.map +1 -0
  59. package/dist/brain/router/index.js +118 -0
  60. package/dist/brain/router/routing-memory.d.ts +27 -0
  61. package/dist/brain/router/routing-memory.d.ts.map +1 -0
  62. package/dist/brain/router/routing-memory.js +77 -0
  63. package/dist/brain/router/select.d.ts +32 -0
  64. package/dist/brain/router/select.d.ts.map +1 -0
  65. package/dist/brain/router/select.js +146 -0
  66. package/dist/brain/router/two-hop.d.ts +23 -0
  67. package/dist/brain/router/two-hop.d.ts.map +1 -0
  68. package/dist/brain/router/two-hop.js +39 -0
  69. package/dist/brain/router/verify.d.ts +37 -0
  70. package/dist/brain/router/verify.d.ts.map +1 -0
  71. package/dist/brain/router/verify.js +111 -0
  72. package/dist/brain/types.d.ts +55 -0
  73. package/dist/brain/types.d.ts.map +1 -0
  74. package/dist/brain/types.js +16 -0
  75. package/dist/brain/worker.d.ts +27 -0
  76. package/dist/brain/worker.d.ts.map +1 -0
  77. package/dist/brain/worker.js +71 -0
  78. package/dist/commands/ai.d.ts +24 -0
  79. package/dist/commands/ai.d.ts.map +1 -0
  80. package/dist/commands/ai.js +137 -0
  81. package/dist/commands/alerts.d.ts +19 -0
  82. package/dist/commands/alerts.d.ts.map +1 -0
  83. package/dist/commands/alerts.js +114 -0
  84. package/dist/commands/billing.d.ts +13 -0
  85. package/dist/commands/billing.d.ts.map +1 -0
  86. package/dist/commands/billing.js +55 -0
  87. package/dist/commands/chat.d.ts +22 -0
  88. package/dist/commands/chat.d.ts.map +1 -0
  89. package/dist/commands/chat.js +422 -0
  90. package/dist/commands/config.d.ts +18 -0
  91. package/dist/commands/config.d.ts.map +1 -0
  92. package/dist/commands/config.js +136 -0
  93. package/dist/commands/doctor.d.ts +11 -0
  94. package/dist/commands/doctor.d.ts.map +1 -0
  95. package/dist/commands/doctor.js +73 -0
  96. package/dist/commands/global.d.ts +11 -0
  97. package/dist/commands/global.d.ts.map +1 -0
  98. package/dist/commands/global.js +253 -0
  99. package/dist/commands/keep.d.ts +12 -0
  100. package/dist/commands/keep.d.ts.map +1 -0
  101. package/dist/commands/keep.js +58 -0
  102. package/dist/commands/lifecycle.d.ts +17 -0
  103. package/dist/commands/lifecycle.d.ts.map +1 -0
  104. package/dist/commands/lifecycle.js +267 -0
  105. package/dist/commands/login.d.ts +16 -0
  106. package/dist/commands/login.d.ts.map +1 -0
  107. package/dist/commands/login.js +234 -0
  108. package/dist/commands/maintenance.d.ts +12 -0
  109. package/dist/commands/maintenance.d.ts.map +1 -0
  110. package/dist/commands/maintenance.js +76 -0
  111. package/dist/commands/mcp.d.ts +16 -0
  112. package/dist/commands/mcp.d.ts.map +1 -0
  113. package/dist/commands/mcp.js +56 -0
  114. package/dist/commands/memory.d.ts +13 -0
  115. package/dist/commands/memory.d.ts.map +1 -0
  116. package/dist/commands/memory.js +218 -0
  117. package/dist/commands/osint.d.ts +14 -0
  118. package/dist/commands/osint.d.ts.map +1 -0
  119. package/dist/commands/osint.js +161 -0
  120. package/dist/commands/pentest.d.ts +13 -0
  121. package/dist/commands/pentest.d.ts.map +1 -0
  122. package/dist/commands/pentest.js +131 -0
  123. package/dist/commands/scale.d.ts +14 -0
  124. package/dist/commands/scale.d.ts.map +1 -0
  125. package/dist/commands/scale.js +191 -0
  126. package/dist/commands/serve.d.ts +16 -0
  127. package/dist/commands/serve.d.ts.map +1 -0
  128. package/dist/commands/serve.js +167 -0
  129. package/dist/commands/tui.d.ts +17 -0
  130. package/dist/commands/tui.d.ts.map +1 -0
  131. package/dist/commands/tui.js +138 -0
  132. package/dist/commands/wyrm.d.ts +20 -0
  133. package/dist/commands/wyrm.d.ts.map +1 -0
  134. package/dist/commands/wyrm.js +274 -0
  135. package/dist/config.d.ts +67 -0
  136. package/dist/config.d.ts.map +1 -0
  137. package/dist/config.js +54 -0
  138. package/dist/index.d.ts +16 -0
  139. package/dist/index.d.ts.map +1 -0
  140. package/dist/index.js +85 -0
  141. package/dist/manifest.d.ts +31 -0
  142. package/dist/manifest.d.ts.map +1 -0
  143. package/dist/manifest.js +83 -0
  144. package/dist/ui.d.ts +57 -0
  145. package/dist/ui.d.ts.map +1 -0
  146. package/dist/ui.js +174 -0
  147. package/dist/utils.d.ts +33 -0
  148. package/dist/utils.d.ts.map +1 -0
  149. package/dist/utils.js +155 -0
  150. package/dist/wyrm/mcp.d.ts +37 -0
  151. package/dist/wyrm/mcp.d.ts.map +1 -0
  152. package/dist/wyrm/mcp.js +137 -0
  153. package/docs/SYSTEM-PREMORTEM.md +397 -0
  154. package/dragon-manifest.toml +241 -0
  155. package/dragon.py +177 -0
  156. package/install/launchd/lk.ghosts.dragonkeep.plist +57 -0
  157. package/install/systemd/dragonkeep.service +40 -0
  158. package/media/dragon-silver-lockup.svg +931 -0
  159. package/media/dragon-silver-mark.svg +931 -0
  160. package/media/dragon-silver.png +0 -0
  161. package/package.json +45 -0
  162. package/specs/001-godmode/constitution.md +54 -0
  163. package/specs/001-godmode/plan.md +30 -0
  164. package/specs/001-godmode/spec.md +64 -0
  165. package/specs/001-godmode/tasks.md +35 -0
  166. package/specs/002-premortem-positioning/premortem.md +211 -0
  167. package/src/agent/loop.ts +165 -0
  168. package/src/agent/mcp.ts +92 -0
  169. package/src/agent/session.ts +48 -0
  170. package/src/agent/skills.ts +138 -0
  171. package/src/agent/stack.ts +154 -0
  172. package/src/agent/task.ts +55 -0
  173. package/src/agent/tools.ts +255 -0
  174. package/src/agent/trace.ts +76 -0
  175. package/src/agent.ts +114 -0
  176. package/src/auth.ts +133 -0
  177. package/src/brain/anthropic.ts +83 -0
  178. package/src/brain/claude-cli.ts +78 -0
  179. package/src/brain/ghost-ember.ts +94 -0
  180. package/src/brain/index.ts +99 -0
  181. package/src/brain/openai-compat.ts +115 -0
  182. package/src/brain/router/classify.ts +167 -0
  183. package/src/brain/router/execute.ts +80 -0
  184. package/src/brain/router/index.ts +125 -0
  185. package/src/brain/router/routing-memory.ts +71 -0
  186. package/src/brain/router/select.ts +156 -0
  187. package/src/brain/router/two-hop.ts +62 -0
  188. package/src/brain/router/verify.ts +123 -0
  189. package/src/brain/types.ts +61 -0
  190. package/src/brain/worker.ts +72 -0
  191. package/src/commands/ai.ts +144 -0
  192. package/src/commands/alerts.ts +131 -0
  193. package/src/commands/billing.ts +59 -0
  194. package/src/commands/chat.ts +318 -0
  195. package/src/commands/config.ts +137 -0
  196. package/src/commands/doctor.ts +71 -0
  197. package/src/commands/global.ts +256 -0
  198. package/src/commands/keep.ts +67 -0
  199. package/src/commands/lifecycle.ts +273 -0
  200. package/src/commands/login.ts +184 -0
  201. package/src/commands/maintenance.ts +54 -0
  202. package/src/commands/mcp.ts +57 -0
  203. package/src/commands/memory.ts +229 -0
  204. package/src/commands/osint.ts +171 -0
  205. package/src/commands/pentest.ts +140 -0
  206. package/src/commands/scale.ts +185 -0
  207. package/src/commands/serve.ts +171 -0
  208. package/src/commands/tui.ts +126 -0
  209. package/src/commands/wyrm.ts +269 -0
  210. package/src/config.ts +93 -0
  211. package/src/index.ts +92 -0
  212. package/src/manifest.ts +104 -0
  213. package/src/ui.ts +188 -0
  214. package/src/utils.ts +153 -0
  215. package/src/wyrm/mcp.ts +130 -0
  216. package/test/auth.test.ts +70 -0
  217. package/test/brain.test.ts +39 -0
  218. package/test/security.test.ts +104 -0
  219. package/test/skills.test.ts +38 -0
  220. package/test/ui.test.ts +46 -0
  221. package/tsconfig.json +19 -0
  222. package/worker/package-lock.json +1527 -0
  223. package/worker/package.json +17 -0
  224. package/worker/src/index.ts +76 -0
  225. package/worker/tsconfig.json +15 -0
  226. package/worker/wrangler.toml +26 -0
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Client for the Dragon assistant — account.ghosts.lk `POST /api/v1/agent`.
3
+ *
4
+ * The endpoint runs a server-side tool-calling loop, then streams the answer as
5
+ * SSE. Two frame kinds arrive on the `data:` channel:
6
+ * - `{"tools":[{"name","ok"}]}` — once, up front, if tools ran (→ onTools)
7
+ * - `{"response":"…"}` — token deltas of the natural-language answer
8
+ * - `[DONE]` — terminator
9
+ *
10
+ * Tools execute server-side and role-gated; the client only DISPLAYS which ran.
11
+ * Request body is `{ messages:[{role,content}], surface }` — the server keeps
12
+ * only user/assistant turns and prepends its own system prompt + memory.
13
+ *
14
+ * Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
15
+ */
16
+ export interface AgentMsg {
17
+ role: 'user' | 'assistant';
18
+ content: string;
19
+ }
20
+ export interface ToolChip {
21
+ name: string;
22
+ ok: boolean;
23
+ }
24
+ /** Surfaces the server understands — they scope the tool set + system context. */
25
+ export declare const SURFACES: readonly ["dashboard", "admin", "chat", "activity", "marketing"];
26
+ export type Surface = (typeof SURFACES)[number];
27
+ export type AgentErrorKind = 'unauthenticated' | 'quota' | 'unavailable' | 'http' | 'network';
28
+ export declare class AgentError extends Error {
29
+ kind: AgentErrorKind;
30
+ detail?: unknown | undefined;
31
+ constructor(kind: AgentErrorKind, message: string, detail?: unknown | undefined);
32
+ }
33
+ export interface StreamAgentOpts {
34
+ messages: AgentMsg[];
35
+ surface: string;
36
+ onDelta: (s: string) => void;
37
+ onTools?: (tools: ToolChip[]) => void;
38
+ signal?: AbortSignal;
39
+ }
40
+ /**
41
+ * Stream one assistant turn. Emits tool chips (if any) then token deltas, and
42
+ * returns the assembled answer when `[DONE]` arrives. Throws AgentError on
43
+ * 401 (unauthenticated), 429 (quota), 502 (model down), or transport failure.
44
+ */
45
+ export declare function streamAgent(opts: StreamAgentOpts): Promise<string>;
46
+ //# sourceMappingURL=agent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,MAAM,WAAW,QAAQ;IAAG,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE;AACzE,MAAM,WAAW,QAAQ;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,OAAO,CAAA;CAAE;AAEvD,kFAAkF;AAClF,eAAO,MAAM,QAAQ,kEAAmE,CAAA;AACxF,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAA;AAE/C,MAAM,MAAM,cAAc,GAAG,iBAAiB,GAAG,OAAO,GAAG,aAAa,GAAG,MAAM,GAAG,SAAS,CAAA;AAE7F,qBAAa,UAAW,SAAQ,KAAK;IAChB,IAAI,EAAE,cAAc;IAA0B,MAAM,CAAC,EAAE,OAAO;gBAA9D,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAS,MAAM,CAAC,EAAE,OAAO,YAAA;CAIlF;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,QAAQ,EAAE,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IAC5B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,IAAI,CAAA;IACrC,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAkExE"}
package/dist/agent.js ADDED
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Client for the Dragon assistant — account.ghosts.lk `POST /api/v1/agent`.
3
+ *
4
+ * The endpoint runs a server-side tool-calling loop, then streams the answer as
5
+ * SSE. Two frame kinds arrive on the `data:` channel:
6
+ * - `{"tools":[{"name","ok"}]}` — once, up front, if tools ran (→ onTools)
7
+ * - `{"response":"…"}` — token deltas of the natural-language answer
8
+ * - `[DONE]` — terminator
9
+ *
10
+ * Tools execute server-side and role-gated; the client only DISPLAYS which ran.
11
+ * Request body is `{ messages:[{role,content}], surface }` — the server keeps
12
+ * only user/assistant turns and prepends its own system prompt + memory.
13
+ *
14
+ * Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
15
+ */
16
+ import { resolveAuth } from './auth.js';
17
+ /** Surfaces the server understands — they scope the tool set + system context. */
18
+ export const SURFACES = ['dashboard', 'admin', 'chat', 'activity', 'marketing'];
19
+ export class AgentError extends Error {
20
+ kind;
21
+ detail;
22
+ constructor(kind, message, detail) {
23
+ super(message);
24
+ this.kind = kind;
25
+ this.detail = detail;
26
+ this.name = 'AgentError';
27
+ }
28
+ }
29
+ /**
30
+ * Stream one assistant turn. Emits tool chips (if any) then token deltas, and
31
+ * returns the assembled answer when `[DONE]` arrives. Throws AgentError on
32
+ * 401 (unauthenticated), 429 (quota), 502 (model down), or transport failure.
33
+ */
34
+ export async function streamAgent(opts) {
35
+ const { apiBase, headers, mode } = resolveAuth();
36
+ let res;
37
+ try {
38
+ res = await fetch(`${apiBase}/api/v1/agent`, {
39
+ method: 'POST',
40
+ headers: { 'content-type': 'application/json', ...headers },
41
+ body: JSON.stringify({ messages: opts.messages, surface: opts.surface }),
42
+ signal: opts.signal,
43
+ });
44
+ }
45
+ catch (e) {
46
+ if (e?.name === 'AbortError')
47
+ throw e;
48
+ throw new AgentError('network', `cannot reach ${apiBase}: ${String(e instanceof Error ? e.message : e)}`);
49
+ }
50
+ if (res.status === 401) {
51
+ const b = (await res.json().catch(() => ({})));
52
+ const hint = mode === 'none'
53
+ ? 'no credentials configured — run `dragon login`'
54
+ : 'your session is invalid or expired — run `dragon login` again';
55
+ throw new AgentError('unauthenticated', hint, b);
56
+ }
57
+ if (res.status === 429) {
58
+ const b = (await res.json().catch(() => ({})));
59
+ const reset = b.reset_at ? new Date(b.reset_at).toLocaleString() : '—';
60
+ throw new AgentError('quota', `daily quota reached (${b.used ?? '?'} / ${b.cap ?? '?'} tokens). Resets ${reset}.`, b);
61
+ }
62
+ if (res.status === 502) {
63
+ throw new AgentError('unavailable', 'the model is momentarily unavailable — try again in a moment.');
64
+ }
65
+ if (!res.ok || !res.body) {
66
+ const t = await res.text().catch(() => res.statusText);
67
+ throw new AgentError('http', `HTTP ${res.status}: ${t}`);
68
+ }
69
+ const reader = res.body.getReader();
70
+ const decoder = new TextDecoder();
71
+ let buffer = '';
72
+ let assembled = '';
73
+ for (;;) {
74
+ const { value, done } = await reader.read();
75
+ if (done)
76
+ break;
77
+ buffer += decoder.decode(value, { stream: true });
78
+ const lines = buffer.split('\n');
79
+ buffer = lines.pop() ?? ''; // hold the partial trailing line
80
+ for (const line of lines) {
81
+ if (!line.startsWith('data: '))
82
+ continue;
83
+ const data = line.slice(6);
84
+ if (data === '[DONE]')
85
+ return assembled;
86
+ try {
87
+ const obj = JSON.parse(data);
88
+ if (Array.isArray(obj.tools)) {
89
+ opts.onTools?.(obj.tools.map((t) => ({ name: String(t.name), ok: t.ok !== false })));
90
+ }
91
+ else if (typeof obj.response === 'string' && obj.response) {
92
+ assembled += obj.response;
93
+ opts.onDelta(obj.response);
94
+ }
95
+ }
96
+ catch {
97
+ /* skip a malformed frame */
98
+ }
99
+ }
100
+ }
101
+ return assembled;
102
+ }
103
+ //# sourceMappingURL=agent.js.map
package/dist/auth.d.ts ADDED
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Auth for the Dragon assistant (account.ghosts.lk /api/v1/agent).
3
+ *
4
+ * The assistant is account-scoped: it answers AS the signed-in user and gates
5
+ * its tools by role. The browser authenticates with the `gp_session` HttpOnly
6
+ * cookie minted by Google/GitHub OAuth. A CLI has no browser, so we support two
7
+ * credential carriers, in priority order:
8
+ *
9
+ * 1. Bearer token — `DRAGON_TOKEN` env or `auth.token` in config. The primary
10
+ * path: a 90-day personal access token (`dgn_…`) minted by `dragon login`'s
11
+ * browser device-code flow. Backend is live on account.ghosts.lk.
12
+ * 2. Session cookie — `DRAGON_SESSION` env or `auth.session` in config. The
13
+ * `gp_session` value copied from a signed-in browser. The `--paste` fallback
14
+ * for headless boxes where the device flow can't open a browser.
15
+ *
16
+ * Resolution is env-first so a shell can override the stored credential without
17
+ * touching the config file.
18
+ *
19
+ * Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
20
+ */
21
+ import { type DragonConfig } from './config.js';
22
+ export declare const DEFAULT_API = "https://account.ghosts.lk";
23
+ /** True if `raw` is a URL a browser will actually open: http(s), a sane port, and
24
+ * https for any non-loopback host (no plaintext auth over the network). */
25
+ export declare function isBrowsableHttpUrl(raw: string): boolean;
26
+ /** A trustworthy assistant origin, self-healing: a malformed/restricted-port value
27
+ * (e.g. a stale config) silently falls back to the default instead of breaking login. */
28
+ export declare function sanitizeApiBase(raw?: string): string;
29
+ export type AuthMode = 'token' | 'session' | 'none';
30
+ export interface ResolvedAuth {
31
+ apiBase: string;
32
+ headers: Record<string, string>;
33
+ mode: AuthMode;
34
+ email?: string;
35
+ }
36
+ /** Credential values become HTTP header content — accept only printable, space-free
37
+ * ASCII so a CR/LF or control char can't smuggle extra headers. */
38
+ export declare function validCred(v?: string): string | undefined;
39
+ /** The credential-bearing env vars auth reads — extracted so the precedence
40
+ * logic is unit-testable without a real shell, config file, or second machine. */
41
+ export interface AuthEnv {
42
+ DRAGON_API?: string;
43
+ DRAGON_TOKEN?: string;
44
+ DRAGON_SESSION?: string;
45
+ }
46
+ /**
47
+ * Pure auth resolution: env-first, then config, behind the cred-injection
48
+ * (`validCred`) and restricted-origin (`sanitizeApiBase`) guards. Token beats
49
+ * session beats none. `resolveAuth()` is just this fed the real env + config —
50
+ * keeping the decision logic deterministic and machine-independent for tests.
51
+ */
52
+ export declare function resolveAuthFrom(env: AuthEnv, cfg: DragonConfig): ResolvedAuth;
53
+ /** Resolve the assistant origin + credential headers from env then config. */
54
+ export declare function resolveAuth(): ResolvedAuth;
55
+ /** Merge a patch into config.auth and persist. */
56
+ export declare function saveAuth(patch: Partial<NonNullable<DragonConfig['auth']>>): void;
57
+ /** Forget all stored credentials (keeps a custom apiBase if set). */
58
+ export declare function clearAuth(): void;
59
+ export interface WhoAmI {
60
+ ok: boolean;
61
+ status: number;
62
+ email?: string;
63
+ error?: string;
64
+ }
65
+ /**
66
+ * Validate the current (or a supplied) credential against the cheap authed
67
+ * endpoint `GET /api/v1/me/licenses` — no LLM cost. Returns the identity email
68
+ * on success so the caller can confirm + store who we signed in as.
69
+ */
70
+ export declare function whoami(override?: {
71
+ headers?: Record<string, string>;
72
+ apiBase?: string;
73
+ }): Promise<WhoAmI>;
74
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAA0B,KAAK,YAAY,EAAE,MAAM,aAAa,CAAA;AAEvE,eAAO,MAAM,WAAW,8BAA8B,CAAA;AAYtD;4EAC4E;AAC5E,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAQvD;AAED;0FAC0F;AAC1F,wBAAgB,eAAe,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAGpD;AAED,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAA;AAEnD,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/B,IAAI,EAAE,QAAQ,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;oEACoE;AACpE,wBAAgB,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAExD;AAED;mFACmF;AACnF,MAAM,WAAW,OAAO;IACtB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,YAAY,GAAG,YAAY,CAQ7E;AAED,8EAA8E;AAC9E,wBAAgB,WAAW,IAAI,YAAY,CAE1C;AAED,kDAAkD;AAClD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAIhF;AAED,qEAAqE;AACrE,wBAAgB,SAAS,IAAI,IAAI,CAKhC;AAED,MAAM,WAAW,MAAM;IAAG,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE;AAEvF;;;;GAIG;AACH,wBAAsB,MAAM,CAAC,QAAQ,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAY/G"}
package/dist/auth.js ADDED
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Auth for the Dragon assistant (account.ghosts.lk /api/v1/agent).
3
+ *
4
+ * The assistant is account-scoped: it answers AS the signed-in user and gates
5
+ * its tools by role. The browser authenticates with the `gp_session` HttpOnly
6
+ * cookie minted by Google/GitHub OAuth. A CLI has no browser, so we support two
7
+ * credential carriers, in priority order:
8
+ *
9
+ * 1. Bearer token — `DRAGON_TOKEN` env or `auth.token` in config. The primary
10
+ * path: a 90-day personal access token (`dgn_…`) minted by `dragon login`'s
11
+ * browser device-code flow. Backend is live on account.ghosts.lk.
12
+ * 2. Session cookie — `DRAGON_SESSION` env or `auth.session` in config. The
13
+ * `gp_session` value copied from a signed-in browser. The `--paste` fallback
14
+ * for headless boxes where the device flow can't open a browser.
15
+ *
16
+ * Resolution is env-first so a shell can override the stored credential without
17
+ * touching the config file.
18
+ *
19
+ * Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
20
+ */
21
+ import { loadConfig, saveConfig } from './config.js';
22
+ export const DEFAULT_API = 'https://account.ghosts.lk';
23
+ // Ports browsers refuse to open (ERR_UNSAFE_PORT / "this address is restricted").
24
+ // We guard against ever handing one of these to a browser or using it as an origin.
25
+ const RESTRICTED_PORTS = new Set([
26
+ 1, 7, 9, 11, 13, 15, 17, 19, 20, 21, 22, 23, 25, 37, 42, 43, 53, 69, 77, 79, 87, 95,
27
+ 101, 102, 103, 104, 109, 110, 111, 113, 115, 117, 119, 123, 135, 137, 139, 143, 161,
28
+ 179, 389, 427, 465, 512, 513, 514, 515, 526, 530, 531, 532, 540, 548, 554, 556, 563,
29
+ 587, 601, 636, 989, 990, 993, 995, 1719, 1720, 1723, 2049, 3659, 4045, 5060, 5061,
30
+ 6000, 6566, 6665, 6666, 6667, 6668, 6669, 6697, 10080,
31
+ ]);
32
+ /** True if `raw` is a URL a browser will actually open: http(s), a sane port, and
33
+ * https for any non-loopback host (no plaintext auth over the network). */
34
+ export function isBrowsableHttpUrl(raw) {
35
+ let u;
36
+ try {
37
+ u = new URL(raw);
38
+ }
39
+ catch {
40
+ return false;
41
+ }
42
+ if (u.protocol !== 'http:' && u.protocol !== 'https:')
43
+ return false;
44
+ const isLocal = u.hostname === 'localhost' || u.hostname === '127.0.0.1' || u.hostname === '::1';
45
+ if (!isLocal && u.protocol !== 'https:')
46
+ return false;
47
+ const port = u.port ? Number(u.port) : u.protocol === 'https:' ? 443 : 80;
48
+ return !RESTRICTED_PORTS.has(port);
49
+ }
50
+ /** A trustworthy assistant origin, self-healing: a malformed/restricted-port value
51
+ * (e.g. a stale config) silently falls back to the default instead of breaking login. */
52
+ export function sanitizeApiBase(raw) {
53
+ const trimmed = (raw ?? '').replace(/\/+$/, '');
54
+ return trimmed && isBrowsableHttpUrl(trimmed) ? trimmed : DEFAULT_API;
55
+ }
56
+ /** Credential values become HTTP header content — accept only printable, space-free
57
+ * ASCII so a CR/LF or control char can't smuggle extra headers. */
58
+ export function validCred(v) {
59
+ return v && /^[\x21-\x7e]+$/.test(v) ? v : undefined;
60
+ }
61
+ /**
62
+ * Pure auth resolution: env-first, then config, behind the cred-injection
63
+ * (`validCred`) and restricted-origin (`sanitizeApiBase`) guards. Token beats
64
+ * session beats none. `resolveAuth()` is just this fed the real env + config —
65
+ * keeping the decision logic deterministic and machine-independent for tests.
66
+ */
67
+ export function resolveAuthFrom(env, cfg) {
68
+ const apiBase = sanitizeApiBase(env.DRAGON_API || cfg.auth?.apiBase);
69
+ const token = validCred(env.DRAGON_TOKEN || cfg.auth?.token);
70
+ const session = validCred(env.DRAGON_SESSION || cfg.auth?.session);
71
+ const email = cfg.auth?.email;
72
+ if (token)
73
+ return { apiBase, headers: { authorization: `Bearer ${token}` }, mode: 'token', email };
74
+ if (session)
75
+ return { apiBase, headers: { cookie: `gp_session=${session}` }, mode: 'session', email };
76
+ return { apiBase, headers: {}, mode: 'none', email };
77
+ }
78
+ /** Resolve the assistant origin + credential headers from env then config. */
79
+ export function resolveAuth() {
80
+ return resolveAuthFrom(process.env, loadConfig());
81
+ }
82
+ /** Merge a patch into config.auth and persist. */
83
+ export function saveAuth(patch) {
84
+ const cfg = loadConfig();
85
+ cfg.auth = { ...(cfg.auth ?? {}), ...patch };
86
+ saveConfig(cfg);
87
+ }
88
+ /** Forget all stored credentials (keeps a custom apiBase if set). */
89
+ export function clearAuth() {
90
+ const cfg = loadConfig();
91
+ const apiBase = cfg.auth?.apiBase;
92
+ cfg.auth = apiBase ? { apiBase } : {};
93
+ saveConfig(cfg);
94
+ }
95
+ /**
96
+ * Validate the current (or a supplied) credential against the cheap authed
97
+ * endpoint `GET /api/v1/me/licenses` — no LLM cost. Returns the identity email
98
+ * on success so the caller can confirm + store who we signed in as.
99
+ */
100
+ export async function whoami(override) {
101
+ const base = override?.apiBase ?? resolveAuth().apiBase;
102
+ const headers = override?.headers ?? resolveAuth().headers;
103
+ try {
104
+ const res = await fetch(`${base}/api/v1/me/licenses`, { headers });
105
+ if (res.status === 401)
106
+ return { ok: false, status: 401, error: 'not signed in' };
107
+ if (!res.ok)
108
+ return { ok: false, status: res.status, error: `HTTP ${res.status}` };
109
+ const body = (await res.json().catch(() => ({})));
110
+ return { ok: true, status: 200, email: body.account?.email };
111
+ }
112
+ catch (e) {
113
+ return { ok: false, status: 0, error: String(e instanceof Error ? e.message : e) };
114
+ }
115
+ }
116
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Claude brain — Anthropic Messages API via the official SDK. The default,
3
+ * "amazing at programming" path.
4
+ *
5
+ * Maps our normalized BrainMessage[] onto Anthropic's content-block format:
6
+ * - assistant tool calls → `tool_use` blocks
7
+ * - tool results → `tool_result` blocks (folded into a user message;
8
+ * consecutive tool messages are batched into one user turn, as the API wants)
9
+ * Streams `text` deltas and collects `tool_use` blocks from the final message.
10
+ *
11
+ * Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
12
+ */
13
+ import type { Brain } from './types.js';
14
+ export declare const DEFAULT_CLAUDE_MODEL = "claude-sonnet-4-6";
15
+ export declare function makeClaudeBrain(opts: {
16
+ apiKey: string;
17
+ model?: string;
18
+ }): Brain;
19
+ //# sourceMappingURL=anthropic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/brain/anthropic.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,KAAK,EAA+C,MAAM,YAAY,CAAA;AAEpF,eAAO,MAAM,oBAAoB,sBAAsB,CAAA;AAqCvD,wBAAgB,eAAe,CAAC,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,KAAK,CA6B/E"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Claude brain — Anthropic Messages API via the official SDK. The default,
3
+ * "amazing at programming" path.
4
+ *
5
+ * Maps our normalized BrainMessage[] onto Anthropic's content-block format:
6
+ * - assistant tool calls → `tool_use` blocks
7
+ * - tool results → `tool_result` blocks (folded into a user message;
8
+ * consecutive tool messages are batched into one user turn, as the API wants)
9
+ * Streams `text` deltas and collects `tool_use` blocks from the final message.
10
+ *
11
+ * Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
12
+ */
13
+ import Anthropic from '@anthropic-ai/sdk';
14
+ export const DEFAULT_CLAUDE_MODEL = 'claude-sonnet-4-6';
15
+ /** Fold our flat BrainMessage[] into Anthropic's role-batched content blocks. */
16
+ function toAnthropic(messages) {
17
+ const out = [];
18
+ for (const m of messages) {
19
+ if (m.role === 'tool') {
20
+ const block = { type: 'tool_result', tool_use_id: m.toolCallId ?? '', content: m.content };
21
+ const last = out[out.length - 1];
22
+ if (last && last.role === 'user' && last.content.every((b) => b.type === 'tool_result'))
23
+ last.content.push(block);
24
+ else
25
+ out.push({ role: 'user', content: [block] });
26
+ continue;
27
+ }
28
+ if (m.role === 'assistant') {
29
+ const blocks = [];
30
+ if (m.content)
31
+ blocks.push({ type: 'text', text: m.content });
32
+ for (const tc of m.toolCalls ?? [])
33
+ blocks.push({ type: 'tool_use', id: tc.id, name: tc.name, input: tc.arguments });
34
+ out.push({ role: 'assistant', content: blocks.length ? blocks : [{ type: 'text', text: '' }] });
35
+ continue;
36
+ }
37
+ // user
38
+ out.push({ role: 'user', content: [{ type: 'text', text: m.content }] });
39
+ }
40
+ return out;
41
+ }
42
+ function toAnthropicTools(tools) {
43
+ return tools.map((t) => ({ name: t.name, description: t.description, input_schema: t.parameters }));
44
+ }
45
+ export function makeClaudeBrain(opts) {
46
+ const client = new Anthropic({ apiKey: opts.apiKey });
47
+ const model = opts.model || DEFAULT_CLAUDE_MODEL;
48
+ return {
49
+ id: 'claude',
50
+ model,
51
+ async turn(t) {
52
+ const stream = client.messages.stream({
53
+ model,
54
+ max_tokens: t.maxTokens ?? 8192,
55
+ system: t.system,
56
+ messages: toAnthropic(t.messages),
57
+ tools: t.tools.length ? toAnthropicTools(t.tools) : undefined,
58
+ }, { signal: t.signal });
59
+ if (t.onDelta)
60
+ stream.on('text', (delta) => t.onDelta(delta));
61
+ const final = await stream.finalMessage();
62
+ let text = '';
63
+ const toolCalls = [];
64
+ for (const block of final.content) {
65
+ if (block.type === 'text')
66
+ text += block.text;
67
+ else if (block.type === 'tool_use')
68
+ toolCalls.push({ id: block.id, name: block.name, arguments: (block.input ?? {}) });
69
+ }
70
+ return { text, toolCalls };
71
+ },
72
+ };
73
+ }
74
+ //# sourceMappingURL=anthropic.js.map
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Claude-CLI brain — drive the agent with the LOCAL `claude` CLI (Claude Code) in
3
+ * headless mode, reusing the operator's EXISTING Claude Code auth. No separate
4
+ * ANTHROPIC_API_KEY needed: if `--brain claude` has no API key but the `claude` binary
5
+ * is on PATH, the factory falls back here ("use this Claude").
6
+ *
7
+ * It runs `claude -p --output-format json` per turn, feeding the rendered conversation
8
+ * on stdin and asking for ONLY the next tool call(s) as a JSON array (EMBER's exact
9
+ * format) — so the same parser handles it AND the captured ~/.dragon/traces are
10
+ * Claude-quality decisions IN dragon-cli's contract: the gold distillation data for the
11
+ * next EMBER. Caveats: heavier than the API (spawns Claude Code per turn) and it bills
12
+ * the operator's Claude Code usage — fine for flywheel-filling, not low-latency chat.
13
+ *
14
+ * Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
15
+ */
16
+ import type { Brain } from './types.js';
17
+ export declare function makeClaudeCliBrain(opts?: {
18
+ model?: string;
19
+ }): Brain;
20
+ //# sourceMappingURL=claude-cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-cli.d.ts","sourceRoot":"","sources":["../../src/brain/claude-cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAAE,KAAK,EAAqC,MAAM,YAAY,CAAA;AAwC1E,wBAAgB,kBAAkB,CAAC,IAAI,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,KAAK,CAoBnE"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Claude-CLI brain — drive the agent with the LOCAL `claude` CLI (Claude Code) in
3
+ * headless mode, reusing the operator's EXISTING Claude Code auth. No separate
4
+ * ANTHROPIC_API_KEY needed: if `--brain claude` has no API key but the `claude` binary
5
+ * is on PATH, the factory falls back here ("use this Claude").
6
+ *
7
+ * It runs `claude -p --output-format json` per turn, feeding the rendered conversation
8
+ * on stdin and asking for ONLY the next tool call(s) as a JSON array (EMBER's exact
9
+ * format) — so the same parser handles it AND the captured ~/.dragon/traces are
10
+ * Claude-quality decisions IN dragon-cli's contract: the gold distillation data for the
11
+ * next EMBER. Caveats: heavier than the API (spawns Claude Code per turn) and it bills
12
+ * the operator's Claude Code usage — fine for flywheel-filling, not low-latency chat.
13
+ *
14
+ * Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
15
+ */
16
+ import { spawn } from 'node:child_process';
17
+ import { parseEmberToolCalls } from './ghost-ember.js';
18
+ function render(system, messages, toolNames) {
19
+ const convo = messages.map((m) => {
20
+ if (m.role === 'tool')
21
+ return `[tool result: ${m.toolName ?? 'tool'}]\n${m.content}`.slice(0, 4000);
22
+ if (m.role === 'assistant')
23
+ return `[assistant] ${m.toolCalls?.length ? JSON.stringify(m.toolCalls.map((t) => ({ tool: t.name, arguments: t.arguments }))) : m.content}`;
24
+ return `[user] ${m.content}`;
25
+ }).join('\n\n');
26
+ return `${system}\n\n── CONVERSATION SO FAR ──\n${convo}\n\n── YOUR TASK ──\n` +
27
+ 'You are the REASONING brain of an external agent — do NOT use your own tools or take any action yourself. ' +
28
+ 'When you need the agent to act, respond with ONLY a JSON array of the next tool call(s), no prose:\n' +
29
+ '[{"tool":"<name>","arguments":{…}}]\n' +
30
+ 'When you have enough to finish, respond with your final answer as PLAIN TEXT (no JSON array). ' +
31
+ `The agent's available tools are: ${toolNames.join(', ')}.`;
32
+ }
33
+ function runClaude(args, prompt, signal) {
34
+ return new Promise((resolve, reject) => {
35
+ const cp = spawn('claude', args, { stdio: ['pipe', 'pipe', 'ignore'], signal });
36
+ let out = '';
37
+ cp.stdout.on('data', (d) => { out += d; });
38
+ cp.on('error', reject);
39
+ cp.on('close', () => {
40
+ // --output-format json → either a single result object or a stream array;
41
+ // the assistant text lives in the `result` field of the type:'result' event.
42
+ try {
43
+ const parsed = JSON.parse(out);
44
+ if (Array.isArray(parsed)) {
45
+ const res = parsed.find((e) => e && e.type === 'result');
46
+ return resolve(typeof res?.result === 'string' ? res.result : '');
47
+ }
48
+ return resolve(typeof parsed?.result === 'string' ? parsed.result : out);
49
+ }
50
+ catch {
51
+ resolve(out);
52
+ }
53
+ });
54
+ cp.stdin.write(prompt);
55
+ cp.stdin.end();
56
+ });
57
+ }
58
+ export function makeClaudeCliBrain(opts) {
59
+ return {
60
+ id: 'claude',
61
+ model: opts?.model ? `${opts.model} (cli)` : 'claude-code (cli)',
62
+ async turn(t) {
63
+ // --model FIRST (so the variadic --disallowedTools below doesn't swallow it).
64
+ const args = ['-p', '--output-format', 'json'];
65
+ if (opts?.model)
66
+ args.push('--model', opts.model);
67
+ // Disable Claude Code's OWN acting tools so it can't do the work itself — it must
68
+ // route through dragon-cli (its decisions become EMBER's training traces) and only
69
+ // produce prose once dragon-cli feeds the results back. Variadic → keep LAST.
70
+ args.push('--disallowedTools', 'Bash', 'Edit', 'Write', 'Read', 'Grep', 'Glob', 'MultiEdit', 'NotebookEdit', 'Task', 'WebFetch', 'WebSearch');
71
+ const prompt = render(t.system, t.messages, t.tools.map((x) => x.name));
72
+ const text = await runClaude(args, prompt, t.signal);
73
+ const toolCalls = parseEmberToolCalls(text);
74
+ t.onDelta?.(toolCalls.length ? toolCalls.map((c) => `→ ${c.name}`).join(' ') : text);
75
+ return { text: toolCalls.length ? '' : text, toolCalls };
76
+ },
77
+ };
78
+ }
79
+ //# sourceMappingURL=claude-cli.js.map
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Ghost / EMBER brain — Ghost Protocol's own local model (DragonSpark's EMBER,
3
+ * served by Ollama) as the agent's reasoning layer.
4
+ *
5
+ * EMBER is a small specialist FINE-TUNED to emit tool calls as a JSON array in the
6
+ * response TEXT — `[{"tool":"Bash","arguments":{…}}]` — NOT via the OpenAI native
7
+ * function-calling protocol. So this adapter, unlike the generic openai-compat brain:
8
+ * 1. does NOT send `tools`/`tool_choice` (EMBER ignores them); instead it appends a
9
+ * terse output contract + the available tool names to the system prompt, matching
10
+ * what EMBER was trained on (DragonSpark configs/fixtures/system_prompt.txt);
11
+ * 2. parses EMBER's JSON-array output back into the loop's normalized ToolCall shape.
12
+ *
13
+ * This format bridge is doubly important: it makes EMBER's calls EXECUTE in the loop
14
+ * AND makes the emitted ~/.dragon/traces training-grade (the flywheel that improves the
15
+ * next EMBER). Wyrm-RAG comes for free — dragon-cli already folds recalled project
16
+ * context into the system prompt (loop.ts buildSystemPrompt `primed`), so EMBER decides
17
+ * WITH memory, which is the whole "Wyrm is the brain" thesis.
18
+ *
19
+ * Copyright 2026 Ghost Protocol (Pvt) Ltd. All Rights Reserved.
20
+ */
21
+ import type { Brain, ToolCall } from './types.js';
22
+ /** Pull EMBER's JSON tool array out of the response text (tolerant of stray prose). */
23
+ export declare function parseEmberToolCalls(text: string): ToolCall[];
24
+ export declare function makeGhostEmberBrain(opts: {
25
+ baseURL: string;
26
+ model: string;
27
+ }): Brain;
28
+ //# sourceMappingURL=ghost-ember.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ghost-ember.d.ts","sourceRoot":"","sources":["../../src/brain/ghost-ember.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,KAAK,EAA2B,QAAQ,EAAY,MAAM,YAAY,CAAA;AAuBpF,uFAAuF;AACvF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,CAc5D;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,KAAK,CAgCnF"}