maestro-agent-sdk 0.1.26 → 0.1.28

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 (65) hide show
  1. package/README.md +20 -0
  2. package/dist/core/agent.d.ts +28 -0
  3. package/dist/core/agent.d.ts.map +1 -1
  4. package/dist/core/agent.js +2 -0
  5. package/dist/core/agent.js.map +1 -1
  6. package/dist/core/is-abort-error.d.ts +18 -0
  7. package/dist/core/is-abort-error.d.ts.map +1 -1
  8. package/dist/core/is-abort-error.js +34 -0
  9. package/dist/core/is-abort-error.js.map +1 -1
  10. package/dist/core/loop.d.ts.map +1 -1
  11. package/dist/core/loop.js +69 -14
  12. package/dist/core/loop.js.map +1 -1
  13. package/dist/core/tool-result-truncation.d.ts +34 -0
  14. package/dist/core/tool-result-truncation.d.ts.map +1 -0
  15. package/dist/core/tool-result-truncation.js +162 -0
  16. package/dist/core/tool-result-truncation.js.map +1 -0
  17. package/dist/index.d.ts +7 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +7 -1
  20. package/dist/index.js.map +1 -1
  21. package/dist/memory/active-task-template.d.ts +7 -4
  22. package/dist/memory/active-task-template.d.ts.map +1 -1
  23. package/dist/memory/active-task-template.js +19 -4
  24. package/dist/memory/active-task-template.js.map +1 -1
  25. package/dist/memory/aux-model-map.d.ts +49 -0
  26. package/dist/memory/aux-model-map.d.ts.map +1 -0
  27. package/dist/memory/aux-model-map.js +103 -0
  28. package/dist/memory/aux-model-map.js.map +1 -0
  29. package/dist/memory/compressor.d.ts +34 -59
  30. package/dist/memory/compressor.d.ts.map +1 -1
  31. package/dist/memory/compressor.js +222 -89
  32. package/dist/memory/compressor.js.map +1 -1
  33. package/dist/platform/config.d.ts +5 -0
  34. package/dist/platform/config.d.ts.map +1 -1
  35. package/dist/platform/config.js +9 -0
  36. package/dist/platform/config.js.map +1 -1
  37. package/dist/platform/version.d.ts +1 -1
  38. package/dist/platform/version.js +1 -1
  39. package/dist/provider.d.ts +18 -6
  40. package/dist/provider.d.ts.map +1 -1
  41. package/dist/provider.js +71 -8
  42. package/dist/provider.js.map +1 -1
  43. package/dist/providers/codex-auth.d.ts +149 -0
  44. package/dist/providers/codex-auth.d.ts.map +1 -0
  45. package/dist/providers/codex-auth.js +332 -0
  46. package/dist/providers/codex-auth.js.map +1 -0
  47. package/dist/providers/codex-stream.d.ts +42 -0
  48. package/dist/providers/codex-stream.d.ts.map +1 -0
  49. package/dist/providers/codex-stream.js +330 -0
  50. package/dist/providers/codex-stream.js.map +1 -0
  51. package/dist/providers/codex-translators.d.ts +105 -0
  52. package/dist/providers/codex-translators.d.ts.map +1 -0
  53. package/dist/providers/codex-translators.js +244 -0
  54. package/dist/providers/codex-translators.js.map +1 -0
  55. package/dist/providers/codex.d.ts +145 -0
  56. package/dist/providers/codex.d.ts.map +1 -0
  57. package/dist/providers/codex.js +417 -0
  58. package/dist/providers/codex.js.map +1 -0
  59. package/dist/registry.d.ts.map +1 -1
  60. package/dist/registry.js +25 -1
  61. package/dist/registry.js.map +1 -1
  62. package/dist/types.d.ts +24 -0
  63. package/dist/types.d.ts.map +1 -1
  64. package/dist/types.js.map +1 -1
  65. package/package.json +17 -1
@@ -0,0 +1,332 @@
1
+ /**
2
+ * Codex OAuth loader + refresh.
3
+ *
4
+ * Reads `~/.codex/auth.json` (the file the upstream `codex` CLI maintains) and
5
+ * surfaces the access token in the shape the Codex Responses provider needs.
6
+ * Handles JWT-claim extraction for the `ChatGPT-Account-ID` Cloudflare header
7
+ * and refreshes the access token against `https://auth.openai.com/oauth/token`
8
+ * when it is past the configured skew threshold.
9
+ *
10
+ * Why we read codex's own file rather than running our own OAuth flow:
11
+ * - The user already authenticated via `codex login`. Forcing a second
12
+ * OAuth dance just to use the same backend would be hostile UX.
13
+ * - codex CLI rotates refresh tokens periodically and writes them back to
14
+ * `auth.json`. We write the rotated refresh token back to the same file
15
+ * so both clients stay in sync — single source of truth.
16
+ *
17
+ * Hermes reference: `hermes_cli/auth.py::refresh_codex_oauth_pure` and
18
+ * `agent/auxiliary_client.py::_codex_cloudflare_headers`. Behavioral parity
19
+ * with those two functions is the bar — diverge only when a TS-specific
20
+ * concern (no httpx, no JWT lib) forces it.
21
+ */
22
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
23
+ import { homedir } from "node:os";
24
+ import { join } from "node:path";
25
+ import { logger } from "../platform/logger.js";
26
+ /**
27
+ * OAuth `client_id` the upstream codex CLI registered with OpenAI's OAuth
28
+ * server. Pinned because the refresh endpoint validates it; rotating without
29
+ * coordinating with codex CLI would break tokens for both clients.
30
+ */
31
+ const CODEX_OAUTH_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
32
+ const CODEX_OAUTH_TOKEN_URL = "https://auth.openai.com/oauth/token";
33
+ /** Refresh when the access token has < this many seconds left. The hermes
34
+ * default is 5 minutes; we match so a long-running maestro turn doesn't
35
+ * pass through the expiry boundary mid-stream. */
36
+ const DEFAULT_REFRESH_SKEW_SECONDS = 300;
37
+ /** Path to `auth.json`. Honors `CODEX_HOME` env var for parity with
38
+ * upstream codex CLI; falls back to `~/.codex/auth.json`. */
39
+ export function codexAuthPath() {
40
+ const home = process.env.CODEX_HOME?.trim();
41
+ const root = home && home.length > 0 ? home : join(homedir(), ".codex");
42
+ return join(root, "auth.json");
43
+ }
44
+ /** Read the auth file. Throws a structured error if the file is missing or
45
+ * malformed — callers can map this to a "run `codex login`" message. */
46
+ export function readCodexAuth(path) {
47
+ const p = path ?? codexAuthPath();
48
+ if (!existsSync(p)) {
49
+ throw new CodexAuthError(`Codex auth file not found at ${p}. Run \`codex login\` first.`, "codex_auth_missing", true);
50
+ }
51
+ let parsed;
52
+ try {
53
+ parsed = JSON.parse(readFileSync(p, "utf8"));
54
+ }
55
+ catch (e) {
56
+ throw new CodexAuthError(`Codex auth file at ${p} is not valid JSON: ${e instanceof Error ? e.message : String(e)}`, "codex_auth_invalid_json", true);
57
+ }
58
+ if (parsed.auth_mode !== "chatgpt") {
59
+ throw new CodexAuthError(`Codex auth_mode is "${parsed.auth_mode}", expected "chatgpt". Use a regular OpenAI provider for API-key auth.`, "codex_auth_wrong_mode", false);
60
+ }
61
+ if (!parsed.tokens?.access_token || !parsed.tokens?.refresh_token) {
62
+ throw new CodexAuthError(`Codex auth file at ${p} is missing access_token or refresh_token. Run \`codex login\` again.`, "codex_auth_missing_tokens", true);
63
+ }
64
+ return parsed;
65
+ }
66
+ /**
67
+ * Decode a JWT access token (no signature verification — we only need the
68
+ * `chatgpt_account_id` claim for an HTTP header). Returns `undefined` if the
69
+ * token isn't a parseable JWT; callers must tolerate that case and fall back
70
+ * to omitting the `ChatGPT-Account-ID` header (the request still succeeds for
71
+ * most account types — only Cloudflare-edge mitigation rules require it).
72
+ */
73
+ export function decodeJwtClaims(accessToken) {
74
+ try {
75
+ const parts = accessToken.split(".");
76
+ if (parts.length < 2)
77
+ return undefined;
78
+ const payload = parts[1];
79
+ // base64url → base64 + padding
80
+ const b64 = payload.replace(/-/g, "+").replace(/_/g, "/");
81
+ const padded = b64.padEnd(b64.length + ((4 - (b64.length % 4)) % 4), "=");
82
+ const json = Buffer.from(padded, "base64").toString("utf8");
83
+ return JSON.parse(json);
84
+ }
85
+ catch {
86
+ return undefined;
87
+ }
88
+ }
89
+ /**
90
+ * Pull the `chatgpt_account_id` value out of a Codex JWT. The claim path
91
+ * (`https://api.openai.com/auth.chatgpt_account_id`) is what hermes pins
92
+ * against codex-rs `auth.rs`; we copy it verbatim.
93
+ */
94
+ export function extractAccountId(accessToken) {
95
+ const claims = decodeJwtClaims(accessToken);
96
+ if (!claims)
97
+ return undefined;
98
+ const authClaim = claims["https://api.openai.com/auth"];
99
+ if (!authClaim || typeof authClaim !== "object")
100
+ return undefined;
101
+ const acct = authClaim.chatgpt_account_id;
102
+ return typeof acct === "string" && acct.length > 0 ? acct : undefined;
103
+ }
104
+ /**
105
+ * Return the access-token `exp` claim as an absolute epoch-seconds value, or
106
+ * `undefined` if not present / unparseable. Used by `accessTokenIsExpiring`.
107
+ */
108
+ export function accessTokenExpiresAt(accessToken) {
109
+ const claims = decodeJwtClaims(accessToken);
110
+ if (!claims)
111
+ return undefined;
112
+ const exp = claims.exp;
113
+ return typeof exp === "number" && Number.isFinite(exp) ? exp : undefined;
114
+ }
115
+ /**
116
+ * `true` when the access token is within `skewSeconds` of its `exp` claim
117
+ * (or already expired). Tokens with no `exp` claim are conservatively treated
118
+ * as "always refresh" — better to round-trip the refresh endpoint than to
119
+ * leak a stale token into a long-running stream.
120
+ */
121
+ export function accessTokenIsExpiring(accessToken, skewSeconds = DEFAULT_REFRESH_SKEW_SECONDS) {
122
+ const exp = accessTokenExpiresAt(accessToken);
123
+ if (exp === undefined)
124
+ return true;
125
+ const now = Math.floor(Date.now() / 1000);
126
+ return exp - now <= skewSeconds;
127
+ }
128
+ /**
129
+ * Required Cloudflare-bypass headers for `chatgpt.com/backend-api/codex`.
130
+ *
131
+ * The edge in front of the Codex endpoint allowlists requests whose
132
+ * `originator` is one of `codex_cli_rs`, `codex_vscode`, `codex_sdk_ts`, or
133
+ * anything starting with `Codex`. Non-residential IPs without an allowlisted
134
+ * originator get a 403 with `cf-mitigated: challenge` regardless of auth
135
+ * correctness — meaning the request never reaches the application layer and
136
+ * the bug looks like an auth problem when it isn't.
137
+ *
138
+ * We pin `originator: codex_cli_rs` (the upstream codex-rs CLI value) and
139
+ * shape the User-Agent to match codex-rs's fingerprint. The
140
+ * `ChatGPT-Account-ID` header is conditional — most accounts don't need it,
141
+ * but Enterprise / Team accounts will 403 without it.
142
+ */
143
+ export function cloudflareHeaders(accessToken) {
144
+ const h = {
145
+ Authorization: `Bearer ${accessToken}`,
146
+ "User-Agent": "codex_cli_rs/0.0.0 (maestro-agent-sdk)",
147
+ originator: "codex_cli_rs",
148
+ };
149
+ const acct = extractAccountId(accessToken);
150
+ if (acct)
151
+ h["ChatGPT-Account-ID"] = acct;
152
+ return h;
153
+ }
154
+ /**
155
+ * Error type for all OAuth-related failures. `reloginRequired` indicates the
156
+ * user must run `codex login` again — the refresh-token chain is unrecoverable.
157
+ * Other failures (network, 5xx) are retryable in-process.
158
+ */
159
+ export class CodexAuthError extends Error {
160
+ code;
161
+ reloginRequired;
162
+ constructor(message, code, reloginRequired) {
163
+ super(message);
164
+ this.code = code;
165
+ this.reloginRequired = reloginRequired;
166
+ this.name = "CodexAuthError";
167
+ }
168
+ }
169
+ /**
170
+ * Refresh the access token using the stored refresh token. Returns the new
171
+ * tokens (access + possibly rotated refresh) without touching the on-disk
172
+ * file. Callers that want to persist call `writeRefreshedTokens()` below.
173
+ *
174
+ * Behavior mirrors hermes's `refresh_codex_oauth_pure` — same client_id,
175
+ * same form-encoded body, same error-mapping rules. The notable difference is
176
+ * that we leave timeouts to the platform `fetch` (Node 22+ supports
177
+ * `signal: AbortSignal.timeout(ms)`); we don't pull in a `httpx` analog.
178
+ */
179
+ export async function refreshAccessToken(refreshToken, timeoutMs = 20_000) {
180
+ if (!refreshToken || refreshToken.length === 0) {
181
+ throw new CodexAuthError("Cannot refresh — refresh_token is empty. Run `codex login` again.", "codex_auth_missing_refresh_token", true);
182
+ }
183
+ const body = new URLSearchParams({
184
+ grant_type: "refresh_token",
185
+ refresh_token: refreshToken,
186
+ client_id: CODEX_OAUTH_CLIENT_ID,
187
+ });
188
+ // v0.1.28 diagnostic logging — instrument the OAuth refresh hop so we can
189
+ // tell from the logs whether "The operation timed out" was raised here
190
+ // (auth.openai.com hang) versus later in `stream()`'s `/responses` fetch.
191
+ // No behavior change beyond the log lines and timing.
192
+ const refreshStart = Date.now();
193
+ logger.info({ url: CODEX_OAUTH_TOKEN_URL, timeoutMs, refreshTokenLen: refreshToken.length }, "codex-auth: refreshAccessToken start");
194
+ let response;
195
+ try {
196
+ response = await fetch(CODEX_OAUTH_TOKEN_URL, {
197
+ method: "POST",
198
+ headers: {
199
+ "Content-Type": "application/x-www-form-urlencoded",
200
+ Accept: "application/json",
201
+ },
202
+ body,
203
+ signal: AbortSignal.timeout(timeoutMs),
204
+ });
205
+ logger.info({
206
+ status: response.status,
207
+ elapsedMs: Date.now() - refreshStart,
208
+ }, "codex-auth: refreshAccessToken fetch returned");
209
+ }
210
+ catch (e) {
211
+ logger.error({
212
+ elapsedMs: Date.now() - refreshStart,
213
+ timeoutMs,
214
+ errName: e instanceof Error ? e.name : typeof e,
215
+ errCode: e?.code,
216
+ errMessage: e instanceof Error ? e.message : String(e),
217
+ errCause: e?.cause,
218
+ stack: e instanceof Error ? e.stack : undefined,
219
+ }, "codex-auth: refreshAccessToken fetch THREW (likely the source of 'The operation timed out')");
220
+ throw new CodexAuthError(`Codex token refresh network error: ${e instanceof Error ? e.message : String(e)}`, "codex_refresh_network", false);
221
+ }
222
+ if (!response.ok) {
223
+ // Parse the error body to decide whether the user needs to re-login.
224
+ // OpenAI returns two distinct shapes here — OAuth spec ({error,error_description})
225
+ // and OpenAI's own ({error:{code,message,type}}) — we accept either.
226
+ let code = "codex_refresh_failed";
227
+ let message = `Codex token refresh failed (HTTP ${response.status})`;
228
+ try {
229
+ const err = (await response.json());
230
+ const errObj = err.error;
231
+ if (typeof errObj === "string" && errObj.length > 0) {
232
+ code = errObj;
233
+ const desc = err.error_description ?? err.message;
234
+ if (typeof desc === "string" && desc.length > 0)
235
+ message = `Codex token refresh: ${desc}`;
236
+ }
237
+ else if (errObj && typeof errObj === "object") {
238
+ const nested = errObj;
239
+ const nestedCode = nested.code ?? nested.type;
240
+ if (typeof nestedCode === "string" && nestedCode.length > 0)
241
+ code = nestedCode;
242
+ const nestedMsg = nested.message;
243
+ if (typeof nestedMsg === "string" && nestedMsg.length > 0)
244
+ message = `Codex token refresh: ${nestedMsg}`;
245
+ }
246
+ }
247
+ catch {
248
+ // Non-JSON body — keep the default message.
249
+ }
250
+ const reloginRequired = code === "invalid_grant" ||
251
+ code === "invalid_token" ||
252
+ code === "invalid_request" ||
253
+ code === "refresh_token_reused" ||
254
+ response.status === 401 ||
255
+ response.status === 403;
256
+ throw new CodexAuthError(message, code, reloginRequired);
257
+ }
258
+ let payload;
259
+ try {
260
+ payload = (await response.json());
261
+ }
262
+ catch (e) {
263
+ throw new CodexAuthError(`Codex token refresh returned non-JSON body: ${e instanceof Error ? e.message : String(e)}`, "codex_refresh_invalid_json", true);
264
+ }
265
+ const newAccess = payload.access_token;
266
+ if (typeof newAccess !== "string" || newAccess.length === 0) {
267
+ throw new CodexAuthError("Codex token refresh response was missing access_token.", "codex_refresh_missing_access_token", true);
268
+ }
269
+ // Rotate the refresh token only when the server returned a fresh one.
270
+ // OAuth servers vary — some always rotate, some only on certain conditions.
271
+ // Keeping the old one when the server omits it matches OAuth 2.0 spec §6.
272
+ const newRefresh = typeof payload.refresh_token === "string" && payload.refresh_token.length > 0
273
+ ? payload.refresh_token
274
+ : refreshToken;
275
+ return { access_token: newAccess, refresh_token: newRefresh };
276
+ }
277
+ /**
278
+ * Persist a freshly refreshed token pair back to `~/.codex/auth.json` while
279
+ * preserving the other fields (auth_mode, OPENAI_API_KEY, account_id,
280
+ * id_token). Updates `last_refresh` to "now". Best-effort — if the write
281
+ * fails the in-process token is still usable for the lifetime of the
282
+ * process; we just won't share it with codex CLI.
283
+ *
284
+ * Note: this is racy with codex CLI running in parallel. The upstream
285
+ * codex-rs implementation uses an advisory file lock; we don't bother
286
+ * because typical maestro usage doesn't overlap with `codex` invocations
287
+ * on the same machine. If lock contention becomes a real problem, lift
288
+ * the lockfile pattern from hermes's `_auth_store_lock`.
289
+ */
290
+ export function writeRefreshedTokens(newTokens, path) {
291
+ const p = path ?? codexAuthPath();
292
+ const current = readCodexAuth(p);
293
+ const updated = {
294
+ ...current,
295
+ last_refresh: new Date().toISOString().replace(/\.\d{3}Z$/, "Z"),
296
+ tokens: {
297
+ ...current.tokens,
298
+ access_token: newTokens.access_token,
299
+ refresh_token: newTokens.refresh_token,
300
+ },
301
+ };
302
+ writeFileSync(p, `${JSON.stringify(updated, null, 2)}\n`, { mode: 0o600 });
303
+ }
304
+ /**
305
+ * The top-level helper a Provider uses on every turn. Reads the on-disk
306
+ * tokens; refreshes if expiring; writes the refreshed pair back to disk;
307
+ * returns the live access token. Idempotent under concurrent calls within
308
+ * one process — repeated calls in the same expiry window just hit the
309
+ * `isExpiring` short-circuit. Cross-process concurrency is best-effort
310
+ * (see `writeRefreshedTokens`).
311
+ *
312
+ * `forceRefresh` is exposed for tests and for hosts that want to recover
313
+ * from a 401 mid-stream by minting a fresh token and retrying once.
314
+ */
315
+ export async function resolveAccessToken(opts) {
316
+ const skew = opts?.skewSeconds ?? DEFAULT_REFRESH_SKEW_SECONDS;
317
+ const auth = readCodexAuth();
318
+ if (!opts?.forceRefresh && !accessTokenIsExpiring(auth.tokens.access_token, skew)) {
319
+ return auth.tokens.access_token;
320
+ }
321
+ const refreshed = await refreshAccessToken(auth.tokens.refresh_token, opts?.timeoutMs);
322
+ try {
323
+ writeRefreshedTokens(refreshed);
324
+ }
325
+ catch (e) {
326
+ // Disk write failed but the in-memory token is still good — log via
327
+ // console.warn rather than crashing the agent.
328
+ console.warn(`[codex-auth] Could not persist refreshed tokens: ${e instanceof Error ? e.message : String(e)}`);
329
+ }
330
+ return refreshed.access_token;
331
+ }
332
+ //# sourceMappingURL=codex-auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-auth.js","sourceRoot":"","sources":["../../src/providers/codex-auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Codex Responses API SSE stream → Maestro `ProviderStreamChunk` adapter.
3
+ *
4
+ * The Responses API SSE protocol is event-typed (each event has a `type`
5
+ * field plus a `data` JSON payload). The events of interest:
6
+ *
7
+ * - `response.output_item.added` — a new top-level output item began
8
+ * (message | function_call | reasoning). Use to learn the item's
9
+ * `id` / `name` / kind before deltas start arriving.
10
+ * - `response.output_text.delta` — text chunk for the current message item.
11
+ * - `response.function_call_arguments.delta` — JSON-args chunk for the
12
+ * current function_call item.
13
+ * - `response.function_call_arguments.done` — args finalized.
14
+ * - `response.output_item.done` — the item is closed; carries the full
15
+ * completed item (with final args, content, status).
16
+ * - `response.reasoning_summary_text.delta` / `.done` — reasoning summary
17
+ * stream (only when `reasoning.summary` was enabled).
18
+ * - `response.completed` — terminal. Carries `response.usage` and the
19
+ * full output array.
20
+ * - `response.incomplete` / `response.failed` — terminal failures.
21
+ *
22
+ * The codex backend has a known bug where `get_final_response()` returns an
23
+ * empty `output` even after valid items streamed — hermes patches this with
24
+ * "synthesize from deltas" backfill. We do the same here: keep a parallel
25
+ * accumulator and use it as the source of truth for `message_complete`,
26
+ * ignoring the `response.completed.response.output` array.
27
+ *
28
+ * The output is the same `ProviderStreamChunk` vocabulary the agent loop
29
+ * already consumes for Anthropic / DeepSeek, so adding Codex requires no
30
+ * changes to `core/loop.ts`.
31
+ */
32
+ import type { ProviderStreamChunk } from "../providers/base.js";
33
+ /**
34
+ * Convert a Responses API SSE stream (the raw `Response.body` from `fetch`)
35
+ * into the maestro `ProviderStreamChunk` async iterable.
36
+ *
37
+ * Aborts when `abortSignal` fires by cancelling the underlying reader; the
38
+ * generator yields nothing further and any in-progress items become stale
39
+ * (the agent loop will reconcile on its end via the abort flag).
40
+ */
41
+ export declare function parseCodexStream(body: ReadableStream<Uint8Array>, abortSignal?: AbortSignal): AsyncGenerator<ProviderStreamChunk>;
42
+ //# sourceMappingURL=codex-stream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-stream.d.ts","sourceRoot":"","sources":["../../src/providers/codex-stream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,EAAwB,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AA2ClF;;;;;;;GAOG;AACH,wBAAuB,gBAAgB,CACrC,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,EAChC,WAAW,CAAC,EAAE,WAAW,GACxB,cAAc,CAAC,mBAAmB,CAAC,CA6MrC"}