@extentos/mcp-server 0.0.75 → 0.0.77
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/tools/data/codeExamples.d.ts +53 -0
- package/dist/tools/data/codeExamples.d.ts.map +1 -1
- package/dist/tools/data/codeExamples.js +254 -62
- package/dist/tools/data/codeExamples.js.map +1 -1
- package/dist/tools/definitions.d.ts.map +1 -1
- package/dist/tools/definitions.js +6 -8
- package/dist/tools/definitions.js.map +1 -1
- package/dist/tools/docs/index.d.ts.map +1 -1
- package/dist/tools/docs/index.js +16 -7
- package/dist/tools/docs/index.js.map +1 -1
- package/dist/tools/handlers/createSimulatorSession.d.ts.map +1 -1
- package/dist/tools/handlers/createSimulatorSession.js +12 -1
- package/dist/tools/handlers/createSimulatorSession.js.map +1 -1
- package/dist/tools/handlers/generateConnectionModule.d.ts.map +1 -1
- package/dist/tools/handlers/generateConnectionModule.js +20 -13
- package/dist/tools/handlers/generateConnectionModule.js.map +1 -1
- package/dist/tools/handlers/getCodeExample.d.ts.map +1 -1
- package/dist/tools/handlers/getCodeExample.js +11 -1
- package/dist/tools/handlers/getCodeExample.js.map +1 -1
- package/dist/tools/handlers/getCredentialGuide.js +57 -8
- package/dist/tools/handlers/getCredentialGuide.js.map +1 -1
- package/dist/tools/handlers/getEventLog.d.ts.map +1 -1
- package/dist/tools/handlers/getEventLog.js +8 -10
- package/dist/tools/handlers/getEventLog.js.map +1 -1
- package/dist/tools/handlers/inspectIntegration.d.ts.map +1 -1
- package/dist/tools/handlers/inspectIntegration.js +8 -3
- package/dist/tools/handlers/inspectIntegration.js.map +1 -1
- package/dist/tools/handlers/validateIntegration.d.ts.map +1 -1
- package/dist/tools/handlers/validateIntegration.js +9 -3
- package/dist/tools/handlers/validateIntegration.js.map +1 -1
- package/dist/tools/util/generatedHash.d.ts +24 -8
- package/dist/tools/util/generatedHash.d.ts.map +1 -1
- package/dist/tools/util/generatedHash.js +88 -11
- package/dist/tools/util/generatedHash.js.map +1 -1
- package/package.json +1 -1
- package/dist/tools/data/examplePatterns.d.ts +0 -4
- package/dist/tools/data/examplePatterns.d.ts.map +0 -1
- package/dist/tools/data/examplePatterns.js +0 -546
- package/dist/tools/data/examplePatterns.js.map +0 -1
- package/dist/tools/data/presetTriggers.d.ts +0 -4
- package/dist/tools/data/presetTriggers.d.ts.map +0 -1
- package/dist/tools/data/presetTriggers.js +0 -47
- package/dist/tools/data/presetTriggers.js.map +0 -1
- package/dist/tools/handlers/generateConsumer.d.ts +0 -3
- package/dist/tools/handlers/generateConsumer.d.ts.map +0 -1
- package/dist/tools/handlers/generateConsumer.js +0 -477
- package/dist/tools/handlers/generateConsumer.js.map +0 -1
- package/dist/tools/handlers/getExampleSpec.d.ts +0 -3
- package/dist/tools/handlers/getExampleSpec.d.ts.map +0 -1
- package/dist/tools/handlers/getExampleSpec.js +0 -50
- package/dist/tools/handlers/getExampleSpec.js.map +0 -1
- package/dist/tools/handlers/initSpec.d.ts +0 -3
- package/dist/tools/handlers/initSpec.d.ts.map +0 -1
- package/dist/tools/handlers/initSpec.js +0 -350
- package/dist/tools/handlers/initSpec.js.map +0 -1
- package/dist/tools/handlers/planIntegration.d.ts +0 -3
- package/dist/tools/handlers/planIntegration.d.ts.map +0 -1
- package/dist/tools/handlers/planIntegration.js +0 -293
- package/dist/tools/handlers/planIntegration.js.map +0 -1
- package/dist/tools/handlers/updateSpec.d.ts +0 -3
- package/dist/tools/handlers/updateSpec.d.ts.map +0 -1
- package/dist/tools/handlers/updateSpec.js +0 -579
- package/dist/tools/handlers/updateSpec.js.map +0 -1
- package/dist/tools/handlers/validateSpec.d.ts +0 -3
- package/dist/tools/handlers/validateSpec.d.ts.map +0 -1
- package/dist/tools/handlers/validateSpec.js +0 -33
- package/dist/tools/handlers/validateSpec.js.map +0 -1
- package/dist/tools/templates/callbackHandler.d.ts +0 -11
- package/dist/tools/templates/callbackHandler.d.ts.map +0 -1
- package/dist/tools/templates/callbackHandler.js +0 -215
- package/dist/tools/templates/callbackHandler.js.map +0 -1
- package/dist/tools/templates/streamConsumer.d.ts +0 -8
- package/dist/tools/templates/streamConsumer.d.ts.map +0 -1
- package/dist/tools/templates/streamConsumer.js +0 -322
- package/dist/tools/templates/streamConsumer.js.map +0 -1
- package/dist/tools/util/userCodeMerge.d.ts +0 -69
- package/dist/tools/util/userCodeMerge.d.ts.map +0 -1
- package/dist/tools/util/userCodeMerge.js +0 -168
- package/dist/tools/util/userCodeMerge.js.map +0 -1
- package/dist/tools/validation/validateSpec.d.ts +0 -22
- package/dist/tools/validation/validateSpec.d.ts.map +0 -1
- package/dist/tools/validation/validateSpec.js +0 -623
- package/dist/tools/validation/validateSpec.js.map +0 -1
|
@@ -1,3 +1,35 @@
|
|
|
1
|
+
/** Gradle dependency a pattern needs the host app to add. */
|
|
2
|
+
export interface RequiredGradleDep {
|
|
3
|
+
/** Configuration name as it appears in `dependencies { ... }`. */
|
|
4
|
+
configuration: "implementation" | "debugImplementation" | "testImplementation" | "androidTestImplementation";
|
|
5
|
+
/** Maven coordinate string, e.g. "com.squareup.okhttp3:okhttp:4.12.0". */
|
|
6
|
+
notation: string;
|
|
7
|
+
/** Which file the dep block lives in. */
|
|
8
|
+
appliedIn: "app/build.gradle.kts";
|
|
9
|
+
}
|
|
10
|
+
/** Gradle plugin a pattern needs the host app to declare. */
|
|
11
|
+
export interface RequiredGradlePlugin {
|
|
12
|
+
/** Plugin id, e.g. "org.jetbrains.kotlin.plugin.serialization". */
|
|
13
|
+
id: string;
|
|
14
|
+
/** Version string — only set on the root declaration. */
|
|
15
|
+
version?: string;
|
|
16
|
+
/** Which file the plugin block lives in. */
|
|
17
|
+
appliedIn: "root build.gradle.kts" | "app/build.gradle.kts";
|
|
18
|
+
/** When true, root declaration carries `apply false`; the app file
|
|
19
|
+
* declares the same plugin id with no version to actually apply it. */
|
|
20
|
+
applyFalse?: boolean;
|
|
21
|
+
/** Free-text caveat the agent should read before applying. */
|
|
22
|
+
note?: string;
|
|
23
|
+
}
|
|
24
|
+
/** Swift Package Manager dependency a pattern needs (iOS host). */
|
|
25
|
+
export interface RequiredSwiftPackage {
|
|
26
|
+
/** Repository URL. */
|
|
27
|
+
url: string;
|
|
28
|
+
/** Version spec, e.g. "1.2.0" used with `.upToNextMajor(from:)`. */
|
|
29
|
+
from: string;
|
|
30
|
+
/** Library products to add to the target. */
|
|
31
|
+
products: string[];
|
|
32
|
+
}
|
|
1
33
|
export interface CodeExample {
|
|
2
34
|
pattern: string;
|
|
3
35
|
title: string;
|
|
@@ -9,6 +41,27 @@ export interface CodeExample {
|
|
|
9
41
|
explanation: string;
|
|
10
42
|
gotchas: string[];
|
|
11
43
|
relatedFeatures: string[];
|
|
44
|
+
/**
|
|
45
|
+
* Extra Gradle / SPM deps the host app must add for this pattern's
|
|
46
|
+
* `code` block to compile. Omitted on patterns that need nothing
|
|
47
|
+
* beyond the SDK + stdlib. Surfaced verbatim by getCodeExample so
|
|
48
|
+
* agents can merge the union of deps from every pattern they use into
|
|
49
|
+
* the build files without scraping `code` comments (R2 F-R2-8).
|
|
50
|
+
*/
|
|
51
|
+
requiredDependencies?: {
|
|
52
|
+
android?: RequiredGradleDep[];
|
|
53
|
+
iosSwiftPackages?: RequiredSwiftPackage[];
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Gradle plugins this pattern needs. Plugins typically appear TWICE:
|
|
57
|
+
* once in the root `build.gradle.kts` plugins block (with `version` +
|
|
58
|
+
* `applyFalse: true`), once in `app/build.gradle.kts` plugins block
|
|
59
|
+
* (with no version) to actually take effect. iOS has no plugin
|
|
60
|
+
* concept; SPM handles its own bootstrapping.
|
|
61
|
+
*/
|
|
62
|
+
requiredPlugins?: {
|
|
63
|
+
android?: RequiredGradlePlugin[];
|
|
64
|
+
};
|
|
12
65
|
}
|
|
13
66
|
export declare const CODE_EXAMPLES: Record<string, CodeExample>;
|
|
14
67
|
export declare const CODE_EXAMPLE_PATTERNS: string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codeExamples.d.ts","sourceRoot":"","sources":["../../../src/tools/data/codeExamples.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"codeExamples.d.ts","sourceRoot":"","sources":["../../../src/tools/data/codeExamples.ts"],"names":[],"mappings":"AAeA,6DAA6D;AAC7D,MAAM,WAAW,iBAAiB;IAChC,kEAAkE;IAClE,aAAa,EAAE,gBAAgB,GAAG,qBAAqB,GAAG,oBAAoB,GAAG,2BAA2B,CAAC;IAC7G,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,SAAS,EAAE,sBAAsB,CAAC;CACnC;AAED,6DAA6D;AAC7D,MAAM,WAAW,oBAAoB;IACnC,mEAAmE;IACnE,EAAE,EAAE,MAAM,CAAC;IACX,yDAAyD;IACzD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,SAAS,EAAE,uBAAuB,GAAG,sBAAsB,CAAC;IAC5D;4EACwE;IACxE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,8DAA8D;IAC9D,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,mEAAmE;AACnE,MAAM,WAAW,oBAAoB;IACnC,sBAAsB;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE;QACJ,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B;;;;;;OAMG;IACH,oBAAoB,CAAC,EAAE;QACrB,OAAO,CAAC,EAAE,iBAAiB,EAAE,CAAC;QAC9B,gBAAgB,CAAC,EAAE,oBAAoB,EAAE,CAAC;KAC3C,CAAC;IACF;;;;;;OAMG;IACH,eAAe,CAAC,EAAE;QAChB,OAAO,CAAC,EAAE,oBAAoB,EAAE,CAAC;KAClC,CAAC;CACH;AAshCD,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAQrD,CAAC;AAEF,eAAO,MAAM,qBAAqB,UAAoC,CAAC"}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
// app_callback, no triggers/blocks/actions DSL. Customer-side LLM
|
|
11
11
|
// clients are referenced as `anthropic` / `vision` / `repo` without
|
|
12
12
|
// importing — agents wire those to their existing backend.
|
|
13
|
+
import { VERSION_INFO } from "./version.js";
|
|
13
14
|
const VOICE_QA_ASSISTANT = {
|
|
14
15
|
pattern: "voice_qa_assistant",
|
|
15
16
|
title: "Voice Q&A assistant (wake + question + LLM + speak)",
|
|
@@ -56,11 +57,19 @@ class CoachHandler(
|
|
|
56
57
|
val question = recording.transcript.trim()
|
|
57
58
|
if (question.isEmpty() || "stop" in question.lowercase()) break
|
|
58
59
|
|
|
59
|
-
|
|
60
|
+
// ask() returns LlmResult. Pattern-match so AuthFailure,
|
|
61
|
+
// NetworkError, and Empty each speak a distinct line — the
|
|
62
|
+
// F-R2-10 bug was collapsing all three into one message.
|
|
63
|
+
val spoken = when (val r = anthropic.ask(
|
|
60
64
|
question = question,
|
|
61
65
|
workoutHistory = workouts.recent(),
|
|
62
|
-
)
|
|
63
|
-
|
|
66
|
+
)) {
|
|
67
|
+
is LlmResult.Ok -> r.text
|
|
68
|
+
LlmResult.AuthFailure -> "Your Anthropic key isn't set up — add it to local.properties and rebuild."
|
|
69
|
+
LlmResult.NetworkError -> "I couldn't reach the AI. Try again."
|
|
70
|
+
LlmResult.Empty -> "I didn't get an answer. Try rephrasing."
|
|
71
|
+
}
|
|
72
|
+
glasses.audio.speak(spoken)
|
|
64
73
|
}
|
|
65
74
|
}
|
|
66
75
|
}
|
|
@@ -105,12 +114,17 @@ class CoachHandler(
|
|
|
105
114
|
let question = recording.transcript.trimmingCharacters(in: .whitespaces)
|
|
106
115
|
if question.isEmpty || question.lowercased().contains("stop") { break }
|
|
107
116
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
117
|
+
// ask() returns LlmResult. Switch so AuthFailure, NetworkError,
|
|
118
|
+
// and Empty each speak a distinct line — the F-R2-10 bug was
|
|
119
|
+
// collapsing all three into one user-facing message.
|
|
120
|
+
let spoken: String
|
|
121
|
+
switch await anthropic.ask(question: question, workoutHistory: workouts.recent()) {
|
|
122
|
+
case .ok(let text): spoken = text
|
|
123
|
+
case .authFailure: spoken = "Your Anthropic key isn't set up — check Info.plist."
|
|
124
|
+
case .networkError: spoken = "I couldn't reach the AI. Try again."
|
|
125
|
+
case .empty: spoken = "I didn't get an answer. Try rephrasing."
|
|
113
126
|
}
|
|
127
|
+
_ = await glasses.audio.speak(spoken)
|
|
114
128
|
}
|
|
115
129
|
}
|
|
116
130
|
|
|
@@ -130,6 +144,7 @@ class CoachHandler(
|
|
|
130
144
|
"speak() blocks until done by default. If you want speak + listen-for-barge-in simultaneously, see the barge_in_speak pattern.",
|
|
131
145
|
"BuildConfig API-key fields don't reflect rotations because kotlinc inlines them at compile time. Use resValue / R.string.X for Android secrets. See getCredentialGuide.",
|
|
132
146
|
"AnthropicClient is a USER-PROVIDED stub — there's no first-party Anthropic Kotlin SDK. Use getCodeExample('byok_anthropic') for a paste-ready minimal OkHttp / URLSession wrapper around POST /v1/messages, and getCredentialGuide('anthropic') for the key-storage pattern. WorkoutRepository is your app's existing data layer — swap in whatever shape your app uses.",
|
|
147
|
+
"ask() returns LlmResult, not String. Pattern-match each failure mode (AuthFailure, NetworkError, Empty) into a distinct spoken line — flattening them is the R2 F-R2-10 finding (user can't distinguish 'fix your key' from 'AI didn't understand').",
|
|
133
148
|
],
|
|
134
149
|
relatedFeatures: ["voice_command", "transcription_incremental", "record_audio"],
|
|
135
150
|
};
|
|
@@ -252,12 +267,21 @@ class VisionHandler(
|
|
|
252
267
|
}
|
|
253
268
|
val mediaType = Photos.mediaTypeFromUri(uri) ?: "image/jpeg"
|
|
254
269
|
|
|
255
|
-
|
|
270
|
+
// describe() returns LlmResult — pattern-match so each failure
|
|
271
|
+
// mode gets the right user-facing message. Folding all three
|
|
272
|
+
// into one "I couldn't describe that one." is the F-R2-10 bug
|
|
273
|
+
// (user can't distinguish "fix your key" from "AI was unsure").
|
|
274
|
+
val spoken = when (val r = vision.describe(
|
|
256
275
|
imageBase64 = base64,
|
|
257
276
|
mediaType = mediaType,
|
|
258
277
|
prompt = "Describe this scene briefly, conversationally.",
|
|
259
|
-
)
|
|
260
|
-
|
|
278
|
+
)) {
|
|
279
|
+
is LlmResult.Ok -> r.text
|
|
280
|
+
LlmResult.AuthFailure -> "Your Anthropic key isn't set up — add it to local.properties and rebuild."
|
|
281
|
+
LlmResult.NetworkError -> "I couldn't reach the vision service. Try again."
|
|
282
|
+
LlmResult.Empty -> "I couldn't describe that one. Try a different angle."
|
|
283
|
+
}
|
|
284
|
+
glasses.audio.speak(spoken)
|
|
261
285
|
}
|
|
262
286
|
}
|
|
263
287
|
|
|
@@ -294,14 +318,17 @@ class VisionHandler(
|
|
|
294
318
|
// Decode via the bundled extension; pass the bytes to
|
|
295
319
|
// your vision LLM however it expects them (base64, Data).
|
|
296
320
|
let image = await photo.loadImage()
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
321
|
+
// describe() returns LlmResult — switch so each failure mode
|
|
322
|
+
// gets the right user-facing message. Folding all three into
|
|
323
|
+
// one "I couldn't describe that one." is the F-R2-10 bug.
|
|
324
|
+
let spoken: String
|
|
325
|
+
switch await vision.describe(image: image, prompt: "Describe this scene briefly, conversationally.") {
|
|
326
|
+
case .ok(let text): spoken = text
|
|
327
|
+
case .authFailure: spoken = "Your Anthropic key isn't set up — check Info.plist."
|
|
328
|
+
case .networkError: spoken = "I couldn't reach the vision service. Try again."
|
|
329
|
+
case .empty: spoken = "I couldn't describe that one. Try a different angle."
|
|
330
|
+
}
|
|
331
|
+
_ = await glasses.audio.speak(spoken)
|
|
305
332
|
}
|
|
306
333
|
}
|
|
307
334
|
|
|
@@ -316,6 +343,7 @@ class VisionHandler(
|
|
|
316
343
|
"Android: Photos.loadBase64(uri) bridges both data: and file: URIs into bytes ready for Claude Vision / GPT-4V request bodies. iOS: `photo.loadImage()` (extension on Photo) returns a UIImage; for base64 you encode the JPEG/PNG data yourself via Data.base64EncodedString().",
|
|
317
344
|
"Vision LLM response time is 2-8 seconds p95; the user is staring at the glasses waiting. Consider an earcon (`glasses.audio.earcon(EarconSound.START)`) at capture time so they know it worked.",
|
|
318
345
|
"VisionClient is a USER-PROVIDED stub — see getCodeExample('byok_anthropic') for the canonical Anthropic Claude Vision wrapper (request shape: content blocks with type:image / source:base64 + type:text). The byok_anthropic pattern is paste-ready in both Kotlin and Swift.",
|
|
346
|
+
"describe() returns LlmResult, not String. Pattern-match (Kotlin `when` / Swift `switch`) so AuthFailure, NetworkError, and Empty each get a distinct user-facing line. The R2 dogfood showed users hearing 'I couldn't describe that one.' equally for 'API key not set' and 'AI was unsure' — the sealed type forces the agent to give each its own message.",
|
|
319
347
|
],
|
|
320
348
|
relatedFeatures: ["capture_photo", "voice_command", "transcription_incremental"],
|
|
321
349
|
};
|
|
@@ -555,7 +583,8 @@ const BYOK_ANTHROPIC = {
|
|
|
555
583
|
title: "BYOK Anthropic Claude API client (text + Vision, OkHttp / URLSession)",
|
|
556
584
|
description: "Minimal AnthropicClient that voice_qa_assistant and photo_describe_voice reference. Two methods: `ask(question, history)` for text-only Q&A, `describe(imageBase64, mediaType, prompt)` for Claude Vision (image content blocks). There is no first-party Anthropic Kotlin SDK; this is the canonical OkHttp + kotlinx.serialization wrapper around POST /v1/messages. iOS uses URLSession + Codable. Paste, then wire the API key through resValue (Android) / Info.plist (iOS) per getCredentialGuide.",
|
|
557
585
|
code: {
|
|
558
|
-
kotlin: `import
|
|
586
|
+
kotlin: `import java.io.IOException
|
|
587
|
+
import kotlinx.coroutines.Dispatchers
|
|
559
588
|
import kotlinx.coroutines.withContext
|
|
560
589
|
import kotlinx.serialization.SerialName
|
|
561
590
|
import kotlinx.serialization.Serializable
|
|
@@ -566,10 +595,42 @@ import okhttp3.OkHttpClient
|
|
|
566
595
|
import okhttp3.Request
|
|
567
596
|
import okhttp3.RequestBody.Companion.toRequestBody
|
|
568
597
|
|
|
569
|
-
//
|
|
570
|
-
//
|
|
571
|
-
//
|
|
572
|
-
//
|
|
598
|
+
// Required deps + plugins are exposed structurally on this example's
|
|
599
|
+
// response (getCodeExample.requiredDependencies / requiredPlugins) so
|
|
600
|
+
// the agent can merge them into build files without re-reading this
|
|
601
|
+
// comment. See the response for the exact configuration + appliedIn
|
|
602
|
+
// fields. R2 F-R2-8 — formerly the agent had to scrape gradle hints
|
|
603
|
+
// from inline comments and forget the root plugins {} entry, breaking
|
|
604
|
+
// the kotlinx-serialization compile.
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Outcome of a single ask/describe call. Sealed so when(result) is
|
|
608
|
+
* exhaustively checked by the compiler — adding a new variant forces
|
|
609
|
+
* every handler to acknowledge the new case.
|
|
610
|
+
*
|
|
611
|
+
* The four variants distinguish failure modes that produce the SAME
|
|
612
|
+
* empty-string symptom if you collapse them, and that need DIFFERENT
|
|
613
|
+
* user-facing responses on a voice-glasses app:
|
|
614
|
+
* Ok — speak / display the text.
|
|
615
|
+
* AuthFailure — key missing, invalid, or no billing. Actionable by the
|
|
616
|
+
* dev (rotate / configure the key); not actionable by
|
|
617
|
+
* the user. UX: "Your Anthropic key isn't set up."
|
|
618
|
+
* NetworkError— transient (no internet, DNS, timeout, 429 rate-limit,
|
|
619
|
+
* 5xx server). UX: "Try again in a moment."
|
|
620
|
+
* Empty — 2xx with no text content. Usually means the model
|
|
621
|
+
* couldn't make sense of the input. UX: "I couldn't
|
|
622
|
+
* describe that one."
|
|
623
|
+
*
|
|
624
|
+
* R2 dogfood F-R2-10: returning bare String collapses all three failure
|
|
625
|
+
* modes into "" and the user hears the same recorded message for every
|
|
626
|
+
* one — they can't tell "fix your key" from "the AI didn't understand".
|
|
627
|
+
*/
|
|
628
|
+
sealed interface LlmResult {
|
|
629
|
+
data class Ok(val text: String) : LlmResult
|
|
630
|
+
data object AuthFailure : LlmResult
|
|
631
|
+
data object NetworkError : LlmResult
|
|
632
|
+
data object Empty : LlmResult
|
|
633
|
+
}
|
|
573
634
|
|
|
574
635
|
class AnthropicClient(
|
|
575
636
|
private val apiKey: String,
|
|
@@ -614,7 +675,7 @@ class AnthropicClient(
|
|
|
614
675
|
|
|
615
676
|
/** Text-only Q&A. Anthropic's content field accepts either a bare String or a
|
|
616
677
|
* list of ContentBlock; we use the list shape so this client can also do Vision. */
|
|
617
|
-
suspend fun ask(question: String, history: List<String> = emptyList()):
|
|
678
|
+
suspend fun ask(question: String, history: List<String> = emptyList()): LlmResult =
|
|
618
679
|
post(
|
|
619
680
|
messages = buildList {
|
|
620
681
|
for (turn in history) {
|
|
@@ -632,7 +693,7 @@ class AnthropicClient(
|
|
|
632
693
|
mediaType: String,
|
|
633
694
|
prompt: String,
|
|
634
695
|
system: String? = null,
|
|
635
|
-
):
|
|
696
|
+
): LlmResult = post(
|
|
636
697
|
messages = listOf(
|
|
637
698
|
Message(
|
|
638
699
|
"user",
|
|
@@ -647,9 +708,9 @@ class AnthropicClient(
|
|
|
647
708
|
|
|
648
709
|
// ---- Transport ----
|
|
649
710
|
|
|
650
|
-
private suspend fun post(messages: List<Message>, system: String? = null):
|
|
711
|
+
private suspend fun post(messages: List<Message>, system: String? = null): LlmResult =
|
|
651
712
|
withContext(Dispatchers.IO) {
|
|
652
|
-
if (apiKey.isBlank()) return@withContext
|
|
713
|
+
if (apiKey.isBlank()) return@withContext LlmResult.AuthFailure
|
|
653
714
|
val body = json.encodeToString(
|
|
654
715
|
Req.serializer(),
|
|
655
716
|
Req(model = model, maxTokens = maxTokens, messages = messages, system = system),
|
|
@@ -661,14 +722,35 @@ class AnthropicClient(
|
|
|
661
722
|
.header("content-type", "application/json")
|
|
662
723
|
.post(body.toRequestBody("application/json".toMediaType()))
|
|
663
724
|
.build()
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
725
|
+
try {
|
|
726
|
+
client.newCall(request).execute().use { response ->
|
|
727
|
+
val payload = response.body?.string().orEmpty()
|
|
728
|
+
when {
|
|
729
|
+
// 401/403: invalid / missing key, no billing on the
|
|
730
|
+
// account. Caller fixes config; user can't.
|
|
731
|
+
response.code == 401 || response.code == 403 -> {
|
|
732
|
+
android.util.Log.w("AnthropicClient", "Auth failure (\${response.code}): \${payload.take(500)}")
|
|
733
|
+
LlmResult.AuthFailure
|
|
734
|
+
}
|
|
735
|
+
// 429 + 5xx + any other non-2xx: transient from the
|
|
736
|
+
// app's perspective. Retry-with-backoff is the right
|
|
737
|
+
// strategy; the user-facing message is "try again".
|
|
738
|
+
!response.isSuccessful -> {
|
|
739
|
+
android.util.Log.w("AnthropicClient", "HTTP \${response.code}: \${payload.take(500)}")
|
|
740
|
+
LlmResult.NetworkError
|
|
741
|
+
}
|
|
742
|
+
else -> {
|
|
743
|
+
val text = json.decodeFromString(Res.serializer(), payload)
|
|
744
|
+
.content.firstOrNull { it.type == "text" }?.text.orEmpty().trim()
|
|
745
|
+
if (text.isEmpty()) LlmResult.Empty else LlmResult.Ok(text)
|
|
746
|
+
}
|
|
747
|
+
}
|
|
669
748
|
}
|
|
670
|
-
|
|
671
|
-
|
|
749
|
+
} catch (e: IOException) {
|
|
750
|
+
// No HTTP exchange happened — DNS failure, no internet,
|
|
751
|
+
// socket timeout. Same UX as a transient 5xx.
|
|
752
|
+
android.util.Log.w("AnthropicClient", "Network error: \${e.message}")
|
|
753
|
+
LlmResult.NetworkError
|
|
672
754
|
}
|
|
673
755
|
}
|
|
674
756
|
}
|
|
@@ -676,13 +758,20 @@ class AnthropicClient(
|
|
|
676
758
|
// Wire-up — your handler retrieves the API key from the resource string
|
|
677
759
|
// emitted by your build.gradle.kts (see getCredentialGuide('anthropic')
|
|
678
760
|
// for the resValue pattern), then passes it to a single client instance
|
|
679
|
-
// held at Application scope
|
|
761
|
+
// held at Application scope. Both ask() and describe() return LlmResult;
|
|
762
|
+
// pattern-match to give the user the right message per failure mode.
|
|
680
763
|
//
|
|
681
764
|
// val apiKey = context.getString(R.string.anthropic_api_key)
|
|
682
765
|
// val anthropic = AnthropicClient(apiKey)
|
|
683
766
|
//
|
|
684
767
|
// // text:
|
|
685
|
-
// val
|
|
768
|
+
// val spoken = when (val r = anthropic.ask("How many bench reps today?")) {
|
|
769
|
+
// is LlmResult.Ok -> r.text
|
|
770
|
+
// LlmResult.AuthFailure -> "Your Anthropic key isn't set up — add it to local.properties and rebuild."
|
|
771
|
+
// LlmResult.NetworkError -> "I couldn't reach the AI. Try again."
|
|
772
|
+
// LlmResult.Empty -> "I didn't get an answer. Try rephrasing."
|
|
773
|
+
// }
|
|
774
|
+
// glasses.audio.speak(spoken)
|
|
686
775
|
//
|
|
687
776
|
// // vision (paired with capture_photo + Photos helpers):
|
|
688
777
|
// val photo = glasses.camera.capturePhoto(
|
|
@@ -691,9 +780,37 @@ class AnthropicClient(
|
|
|
691
780
|
// val uri = photo.uri ?: return
|
|
692
781
|
// val b64 = Photos.loadBase64(uri) ?: return
|
|
693
782
|
// val mt = Photos.mediaTypeFromUri(uri) ?: "image/jpeg"
|
|
694
|
-
// val
|
|
783
|
+
// val result = anthropic.describe(b64, mt, "What card is showing?")
|
|
784
|
+
// // when(result) { is LlmResult.Ok -> result.text; ...other cases... }`,
|
|
695
785
|
swift: `import Foundation
|
|
696
786
|
|
|
787
|
+
/// Outcome of a single ask/describe call. Sealed via Swift enum so a
|
|
788
|
+
/// \`switch result\` on this is exhaustively checked — adding a new
|
|
789
|
+
/// case forces every handler to acknowledge it.
|
|
790
|
+
///
|
|
791
|
+
/// The four cases distinguish failure modes that produce the SAME
|
|
792
|
+
/// empty-string symptom if you flatten them, and that need DIFFERENT
|
|
793
|
+
/// user-facing responses on a voice-glasses app:
|
|
794
|
+
/// .ok — speak / display the text.
|
|
795
|
+
/// .authFailure — key missing, invalid, or no billing. Actionable by
|
|
796
|
+
/// the dev; not by the user. UX: "Your Anthropic key
|
|
797
|
+
/// isn't set up."
|
|
798
|
+
/// .networkError— transient (no internet, DNS, timeout, 429, 5xx).
|
|
799
|
+
/// UX: "Try again in a moment."
|
|
800
|
+
/// .empty — 2xx with no text content. Usually means the model
|
|
801
|
+
/// couldn't make sense of the input. UX: "I couldn't
|
|
802
|
+
/// describe that one."
|
|
803
|
+
///
|
|
804
|
+
/// R2 dogfood F-R2-10: throwing/empty-string semantics collapsed all
|
|
805
|
+
/// three failure modes into one, so the user heard the same recorded
|
|
806
|
+
/// message regardless of cause.
|
|
807
|
+
enum LlmResult {
|
|
808
|
+
case ok(String)
|
|
809
|
+
case authFailure
|
|
810
|
+
case networkError
|
|
811
|
+
case empty
|
|
812
|
+
}
|
|
813
|
+
|
|
697
814
|
actor AnthropicClient {
|
|
698
815
|
private let apiKey: String
|
|
699
816
|
private let model: String
|
|
@@ -749,13 +866,13 @@ actor AnthropicClient {
|
|
|
749
866
|
|
|
750
867
|
// ---- Public API ----
|
|
751
868
|
|
|
752
|
-
/// Text-only Q&A.
|
|
753
|
-
func ask(question: String, history: [String] = []) async
|
|
869
|
+
/// Text-only Q&A. Non-throwing: any error becomes an LlmResult case.
|
|
870
|
+
func ask(question: String, history: [String] = []) async -> LlmResult {
|
|
754
871
|
var messages = history.map {
|
|
755
872
|
Message(role: "user", content: [.text($0)])
|
|
756
873
|
}
|
|
757
874
|
messages.append(Message(role: "user", content: [.text(question)]))
|
|
758
|
-
return
|
|
875
|
+
return await post(messages: messages, system: nil)
|
|
759
876
|
}
|
|
760
877
|
|
|
761
878
|
/// Vision: one image (base64) + a text prompt. Source the imageBase64 +
|
|
@@ -766,7 +883,7 @@ actor AnthropicClient {
|
|
|
766
883
|
mediaType: String,
|
|
767
884
|
prompt: String,
|
|
768
885
|
system: String? = nil,
|
|
769
|
-
) async
|
|
886
|
+
) async -> LlmResult {
|
|
770
887
|
let messages = [
|
|
771
888
|
Message(
|
|
772
889
|
role: "user",
|
|
@@ -776,45 +893,82 @@ actor AnthropicClient {
|
|
|
776
893
|
],
|
|
777
894
|
),
|
|
778
895
|
]
|
|
779
|
-
return
|
|
896
|
+
return await post(messages: messages, system: system)
|
|
780
897
|
}
|
|
781
898
|
|
|
782
899
|
// ---- Transport ----
|
|
783
900
|
|
|
784
|
-
private func post(messages: [Message], system: String?) async
|
|
785
|
-
guard !apiKey.isEmpty else { return
|
|
901
|
+
private func post(messages: [Message], system: String?) async -> LlmResult {
|
|
902
|
+
guard !apiKey.isEmpty else { return .authFailure }
|
|
786
903
|
var request = URLRequest(url: endpoint)
|
|
787
904
|
request.httpMethod = "POST"
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
905
|
+
do {
|
|
906
|
+
request.httpBody = try JSONEncoder().encode(
|
|
907
|
+
Req(model: model, max_tokens: maxTokens, messages: messages, system: system)
|
|
908
|
+
)
|
|
909
|
+
} catch {
|
|
910
|
+
// Encoding error — typically a programming bug, not a network
|
|
911
|
+
// condition. Surface as networkError since it's not auth and
|
|
912
|
+
// not "the model returned nothing"; the handler will retry.
|
|
913
|
+
return .networkError
|
|
914
|
+
}
|
|
791
915
|
request.setValue(apiKey, forHTTPHeaderField: "x-api-key")
|
|
792
916
|
request.setValue("2023-06-01", forHTTPHeaderField: "anthropic-version")
|
|
793
917
|
request.setValue("application/json", forHTTPHeaderField: "content-type")
|
|
794
918
|
|
|
795
|
-
let
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
919
|
+
let data: Data
|
|
920
|
+
let response: URLResponse
|
|
921
|
+
do {
|
|
922
|
+
(data, response) = try await URLSession.shared.data(for: request)
|
|
923
|
+
} catch {
|
|
924
|
+
// No HTTP exchange — DNS / no internet / timeout. Same UX
|
|
925
|
+
// as a transient 5xx.
|
|
926
|
+
return .networkError
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
if let http = response as? HTTPURLResponse {
|
|
930
|
+
switch http.statusCode {
|
|
931
|
+
case 401, 403:
|
|
932
|
+
return .authFailure
|
|
933
|
+
case 200..<300:
|
|
934
|
+
break
|
|
935
|
+
default:
|
|
936
|
+
// 429 + 5xx + everything else non-2xx → retryable.
|
|
937
|
+
return .networkError
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
do {
|
|
942
|
+
let decoded = try JSONDecoder().decode(Res.self, from: data)
|
|
943
|
+
let text = decoded.content.first(where: { $0.type == "text" })?.text ?? ""
|
|
944
|
+
return text.isEmpty ? .empty : .ok(text)
|
|
945
|
+
} catch {
|
|
946
|
+
// 2xx with an undecodable body — treat as empty rather than
|
|
947
|
+
// network so the user-facing message reflects "got something
|
|
948
|
+
// back, didn't understand it" rather than "couldn't reach".
|
|
949
|
+
return .empty
|
|
803
950
|
}
|
|
804
|
-
let decoded = try JSONDecoder().decode(Res.self, from: data)
|
|
805
|
-
return decoded.content.first(where: { $0.type == "text" })?.text ?? ""
|
|
806
951
|
}
|
|
807
952
|
}
|
|
808
953
|
|
|
809
954
|
// Wire-up — your handler reads the API key from Info.plist (which gets
|
|
810
955
|
// it via xcconfig / env var — see getCredentialGuide for the storage
|
|
811
|
-
// pattern), then passes it to a single actor instance
|
|
956
|
+
// pattern), then passes it to a single actor instance. Both ask() and
|
|
957
|
+
// describe() return LlmResult; switch on it to give the user the right
|
|
958
|
+
// message per failure mode.
|
|
812
959
|
//
|
|
813
960
|
// let key = Bundle.main.object(forInfoDictionaryKey: "ANTHROPIC_API_KEY") as? String ?? ""
|
|
814
961
|
// let anthropic = AnthropicClient(apiKey: key)
|
|
815
962
|
//
|
|
816
963
|
// // text:
|
|
817
|
-
// let
|
|
964
|
+
// let spoken: String
|
|
965
|
+
// switch await anthropic.ask(question: "How many bench reps today?") {
|
|
966
|
+
// case .ok(let text): spoken = text
|
|
967
|
+
// case .authFailure: spoken = "Your Anthropic key isn't set up — check Info.plist."
|
|
968
|
+
// case .networkError: spoken = "I couldn't reach the AI. Try again."
|
|
969
|
+
// case .empty: spoken = "I didn't get an answer. Try rephrasing."
|
|
970
|
+
// }
|
|
971
|
+
// _ = await glasses.audio.speak(spoken)
|
|
818
972
|
//
|
|
819
973
|
// // vision (paired with capture_photo + photo.loadImage helpers):
|
|
820
974
|
// let result = await glasses.camera.capturePhoto(
|
|
@@ -824,11 +978,12 @@ actor AnthropicClient {
|
|
|
824
978
|
// guard let image = await photo.loadImage(),
|
|
825
979
|
// let jpeg = image.jpegData(compressionQuality: 0.85) else { return }
|
|
826
980
|
// let b64 = jpeg.base64EncodedString()
|
|
827
|
-
// let answer =
|
|
981
|
+
// let answer = await anthropic.describe(
|
|
828
982
|
// imageBase64: b64,
|
|
829
983
|
// mediaType: "image/jpeg",
|
|
830
984
|
// prompt: "What's the next blackjack move?"
|
|
831
|
-
// )
|
|
985
|
+
// )
|
|
986
|
+
// // switch answer { case .ok(let t): ...; case .authFailure: ...; ... }`,
|
|
832
987
|
},
|
|
833
988
|
explanation: "Minimal direct client supporting BOTH text Q&A (`ask`) and Claude Vision (`describe`). There's no first-party Anthropic Kotlin SDK; this is the wire-protocol wrapper voice_qa_assistant and photo_describe_voice patterns reference as `AnthropicClient`. Kotlin uses OkHttp + kotlinx-serialization with a `JsonClassDiscriminator(\"type\")` sealed ContentBlock that mirrors Anthropic's polymorphic content shape (text + image). Swift uses URLSession + Codable with manual encode/decode for the same polymorphism. The customer holds one instance at Application scope and passes it to handlers via DI.",
|
|
834
989
|
gotchas: [
|
|
@@ -841,6 +996,43 @@ actor AnthropicClient {
|
|
|
841
996
|
"Vision LLM response p95 is 2-8 seconds. Play an earcon (`glasses.audio.earcon(EarconSound.START)`) BEFORE the call so the user knows the request fired.",
|
|
842
997
|
],
|
|
843
998
|
relatedFeatures: ["capture_photo"],
|
|
999
|
+
requiredDependencies: {
|
|
1000
|
+
android: [
|
|
1001
|
+
{
|
|
1002
|
+
configuration: "implementation",
|
|
1003
|
+
notation: "com.squareup.okhttp3:okhttp:4.12.0",
|
|
1004
|
+
appliedIn: "app/build.gradle.kts",
|
|
1005
|
+
},
|
|
1006
|
+
{
|
|
1007
|
+
configuration: "implementation",
|
|
1008
|
+
notation: "org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3",
|
|
1009
|
+
appliedIn: "app/build.gradle.kts",
|
|
1010
|
+
},
|
|
1011
|
+
],
|
|
1012
|
+
// iOS uses URLSession + Codable from the standard library — no
|
|
1013
|
+
// extra SPM dependency required.
|
|
1014
|
+
iosSwiftPackages: [],
|
|
1015
|
+
},
|
|
1016
|
+
requiredPlugins: {
|
|
1017
|
+
android: [
|
|
1018
|
+
// The serialization plugin needs BOTH declarations to take effect:
|
|
1019
|
+
// version+applyFalse in the root, version-less in the app file.
|
|
1020
|
+
// Forgetting the root entry yields "Plugin not found"; forgetting
|
|
1021
|
+
// the app entry yields a compile failure on @Serializable.
|
|
1022
|
+
{
|
|
1023
|
+
id: "org.jetbrains.kotlin.plugin.serialization",
|
|
1024
|
+
version: VERSION_INFO.android.kotlinVersion,
|
|
1025
|
+
appliedIn: "root build.gradle.kts",
|
|
1026
|
+
applyFalse: true,
|
|
1027
|
+
note: "Version MUST match the kotlin.android plugin version (both come from the Kotlin distribution).",
|
|
1028
|
+
},
|
|
1029
|
+
{
|
|
1030
|
+
id: "org.jetbrains.kotlin.plugin.serialization",
|
|
1031
|
+
appliedIn: "app/build.gradle.kts",
|
|
1032
|
+
note: "No version here — inherits from the root declaration. Sits alongside id(\"com.android.application\") and id(\"org.jetbrains.kotlin.android\").",
|
|
1033
|
+
},
|
|
1034
|
+
],
|
|
1035
|
+
},
|
|
844
1036
|
};
|
|
845
1037
|
export const CODE_EXAMPLES = {
|
|
846
1038
|
voice_qa_assistant: VOICE_QA_ASSISTANT,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codeExamples.js","sourceRoot":"","sources":["../../../src/tools/data/codeExamples.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,sEAAsE;AACtE,sEAAsE;AACtE,uDAAuD;AACvD,sCAAsC;AACtC,EAAE;AACF,qEAAqE;AACrE,uDAAuD;AACvD,wEAAwE;AACxE,kEAAkE;AAClE,oEAAoE;AACpE,2DAA2D;
|
|
1
|
+
{"version":3,"file":"codeExamples.js","sourceRoot":"","sources":["../../../src/tools/data/codeExamples.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,sEAAsE;AACtE,sEAAsE;AACtE,uDAAuD;AACvD,sCAAsC;AACtC,EAAE;AACF,qEAAqE;AACrE,uDAAuD;AACvD,wEAAwE;AACxE,kEAAkE;AAClE,oEAAoE;AACpE,2DAA2D;AAE3D,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAuE5C,MAAM,kBAAkB,GAAgB;IACtC,OAAO,EAAE,oBAAoB;IAC7B,KAAK,EAAE,qDAAqD;IAC5D,WAAW,EACT,kSAAkS;IACpS,IAAI,EAAE;QACJ,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2DV;QACE,KAAK,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqDT;KACC;IACD,WAAW,EACT,ggCAAggC;IAClgC,OAAO,EAAE;QACP,yXAAyX;QACzX,gbAAgb;QAChb,qXAAqX;QACrX,mSAAmS;QACnS,oSAAoS;QACpS,ySAAyS;QACzS,msBAAmsB;QACnsB,sIAAsI;QACtI,+HAA+H;QAC/H,yKAAyK;QACzK,0WAA0W;QAC1W,sPAAsP;KACvP;IACD,eAAe,EAAE,CAAC,eAAe,EAAE,2BAA2B,EAAE,cAAc,CAAC;CAChF,CAAC;AAEF,MAAM,cAAc,GAAgB;IAClC,OAAO,EAAE,gBAAgB;IACzB,KAAK,EAAE,gDAAgD;IACvD,WAAW,EACT,uMAAuM;IACzM,IAAI,EAAE;QACJ,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2BV;QACE,KAAK,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgCT;KACC;IACD,WAAW,EACT,+ZAA+Z;IACja,OAAO,EAAE;QACP,8IAA8I;QAC9I,6PAA6P;QAC7P,kLAAkL;KACnL;IACD,eAAe,EAAE,CAAC,2BAA2B,CAAC;CAC/C,CAAC;AAEF,MAAM,oBAAoB,GAAgB;IACxC,OAAO,EAAE,sBAAsB;IAC/B,KAAK,EAAE,gDAAgD;IACvD,WAAW,EACT,0NAA0N;IAC5N,IAAI,EAAE;QACJ,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0DV;QACE,KAAK,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8CT;KACC;IACD,WAAW,EACT,gWAAgW;IAClW,OAAO,EAAE;QACP,4OAA4O;QAC5O,4KAA4K;QAC5K,yGAAyG;QACzG,iRAAiR;QACjR,iMAAiM;QACjM,gRAAgR;QAChR,+VAA+V;KAChW;IACD,eAAe,EAAE,CAAC,eAAe,EAAE,eAAe,EAAE,2BAA2B,CAAC;CACjF,CAAC;AAEF,MAAM,qBAAqB,GAAgB;IACzC,OAAO,EAAE,uBAAuB;IAChC,KAAK,EAAE,+CAA+C;IACtD,WAAW,EACT,yLAAyL;IAC3L,IAAI,EAAE;QACJ,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;sBA4BU;QAClB,KAAK,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;yBAyBc;KACtB;IACD,WAAW,EACT,4QAA4Q;IAC9Q,OAAO,EAAE;QACP,gNAAgN;QAChN,2KAA2K;QAC3K,6LAA6L;KAC9L;IACD,eAAe,EAAE,CAAC,2BAA2B,CAAC;CAC/C,CAAC;AAEF,MAAM,WAAW,GAAgB;IAC/B,OAAO,EAAE,aAAa;IACtB,KAAK,EAAE,sCAAsC;IAC7C,WAAW,EACT,sKAAsK;IACxK,IAAI,EAAE;QACJ,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0CV;QACE,KAAK,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqCT;KACC;IACD,WAAW,EACT,yQAAyQ;IAC3Q,OAAO,EAAE;QACP,iNAAiN;QACjN,2GAA2G;KAC5G;IACD,eAAe,EAAE,CAAC,eAAe,EAAE,cAAc,EAAE,2BAA2B,CAAC;CAChF,CAAC;AAEF,MAAM,qBAAqB,GAAgB;IACzC,OAAO,EAAE,uBAAuB;IAChC,KAAK,EAAE,8CAA8C;IACrD,WAAW,EACT,+NAA+N;IACjO,IAAI,EAAE;QACJ,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;kDAwBsC;QAC9C,KAAK,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4BT;KACC;IACD,WAAW,EACT,+TAA+T;IACjU,OAAO,EAAE;QACP,kMAAkM;QAClM,6QAA6Q;QAC7Q,iMAAiM;KAClM;IACD,eAAe,EAAE,EAAE;CACpB,CAAC;AAEF,MAAM,cAAc,GAAgB;IAClC,OAAO,EAAE,gBAAgB;IACzB,KAAK,EAAE,uEAAuE;IAC9E,WAAW,EACT,0eAA0e;IAC5e,IAAI,EAAE;QACJ,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2EAsM+D;QACvE,KAAK,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4EAyMiE;KACzE;IACD,WAAW,EACT,olBAAolB;IACtlB,OAAO,EAAE;QACP,qLAAqL;QACrL,oJAAoJ;QACpJ,yWAAyW;QACzW,yUAAyU;QACzU,sLAAsL;QACtL,sLAAsL;QACtL,yJAAyJ;KAC1J;IACD,eAAe,EAAE,CAAC,eAAe,CAAC;IAClC,oBAAoB,EAAE;QACpB,OAAO,EAAE;YACP;gBACE,aAAa,EAAE,gBAAgB;gBAC/B,QAAQ,EAAE,oCAAoC;gBAC9C,SAAS,EAAE,sBAAsB;aAClC;YACD;gBACE,aAAa,EAAE,gBAAgB;gBAC/B,QAAQ,EAAE,wDAAwD;gBAClE,SAAS,EAAE,sBAAsB;aAClC;SACF;QACD,+DAA+D;QAC/D,iCAAiC;QACjC,gBAAgB,EAAE,EAAE;KACrB;IACD,eAAe,EAAE;QACf,OAAO,EAAE;YACP,mEAAmE;YACnE,gEAAgE;YAChE,kEAAkE;YAClE,2DAA2D;YAC3D;gBACE,EAAE,EAAE,2CAA2C;gBAC/C,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC,aAAa;gBAC3C,SAAS,EAAE,uBAAuB;gBAClC,UAAU,EAAE,IAAI;gBAChB,IAAI,EAAE,gGAAgG;aACvG;YACD;gBACE,EAAE,EAAE,2CAA2C;gBAC/C,SAAS,EAAE,sBAAsB;gBACjC,IAAI,EAAE,gJAAgJ;aACvJ;SACF;KACF;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAgC;IACxD,kBAAkB,EAAE,kBAAkB;IACtC,cAAc,EAAE,cAAc;IAC9B,oBAAoB,EAAE,oBAAoB;IAC1C,qBAAqB,EAAE,qBAAqB;IAC5C,WAAW,EAAE,WAAW;IACxB,qBAAqB,EAAE,qBAAqB;IAC5C,cAAc,EAAE,cAAc;CAC/B,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,IAAI,EAAE,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../src/tools/definitions.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IAMpB,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ,CAAC;QACf,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACpC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QACpB,oBAAoB,EAAE,KAAK,CAAC;KAC7B,CAAC;CACH;AAMD,eAAO,MAAM,eAAe,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../src/tools/definitions.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IAMpB,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ,CAAC;QACf,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACpC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QACpB,oBAAoB,EAAE,KAAK,CAAC;KAC7B,CAAC;CACH;AAMD,eAAO,MAAM,eAAe,EAAE,OAAO,EAsXpC,CAAC;AAEF,eAAO,MAAM,SAAS,QAAyB,CAAC;AAShD,wBAAgB,iCAAiC,CAC/C,IAAI,GAAE,SAAS,OAAO,EAAoB,GACzC,IAAI,CAiBN"}
|
|
@@ -234,7 +234,7 @@ export const toolDefinitions = [
|
|
|
234
234
|
},
|
|
235
235
|
{
|
|
236
236
|
name: "getEventLog",
|
|
237
|
-
description: "Fetch structured event trace inside a simulator session. Primary debugging tool for *why is my handler not seeing what I expect* — transcripts not arriving, photo capture failing, toggle changes not propagating, speak getting cut off, connection dropping. Events are grouped
|
|
237
|
+
description: "Fetch structured event trace inside a simulator session. Primary debugging tool for *why is my handler not seeing what I expect* — transcripts not arriving, photo capture failing, toggle changes not propagating, speak getting cut off, connection dropping. Events are grouped into six chips (errors / voice / camera / ai / lifecycle / custom) — one chip per event, with `errors` absorbing every severity≥warn row regardless of modality. To see e.g. voice activity plus voice errors, fetch the chips separately and union them. USE to diagnose which capability primitive is misbehaving. DON'T USE for static configuration checking (use validateIntegration) or for live session phase (use getSimulatorStatus). **Scope (R2 F-R2-12):** captures only the simulator's WebSocket relay — transport + SDK primitives (audio, camera, speak, toggles, voice triggers, runtime events). Customer-side direct-HTTP calls (BYOK Anthropic / OpenAI / Gemini / etc.) NEVER traverse the wire and so are invisible here — those failures surface in logcat (Android) or OSLog (iOS). When debugging a flow that mixes SDK primitives with external APIs (e.g. capture_photo → vision LLM → speak), combine getEventLog with the platform log surface; a getEventLog showing capture_photo + photo_result + speak with nothing in between is the signature of a silent BYOK failure.",
|
|
238
238
|
inputSchema: {
|
|
239
239
|
type: "object",
|
|
240
240
|
properties: {
|
|
@@ -244,15 +244,13 @@ export const toolDefinitions = [
|
|
|
244
244
|
enum: [
|
|
245
245
|
"all",
|
|
246
246
|
"errors",
|
|
247
|
-
"
|
|
248
|
-
"audio",
|
|
247
|
+
"voice",
|
|
249
248
|
"camera",
|
|
250
|
-
"
|
|
251
|
-
"
|
|
252
|
-
"
|
|
253
|
-
"system",
|
|
249
|
+
"ai",
|
|
250
|
+
"lifecycle",
|
|
251
|
+
"custom",
|
|
254
252
|
],
|
|
255
|
-
description: "
|
|
253
|
+
description: "Chip to filter the event stream by. 'all' (default) returns every event. 'errors' returns severity≥warn events across all modalities. 'voice' / 'camera' / 'ai' cover hardware/integration capabilities (severity=info only — failures land under 'errors'). 'lifecycle' covers connection / session / pairing / hardware / runtime state. 'custom' is the open-world catch-all for app-emitted event types the platform did not anticipate.",
|
|
256
254
|
},
|
|
257
255
|
limit: { type: "integer", minimum: 1 },
|
|
258
256
|
since: { type: "string" },
|