ummaya 0.2.2 → 0.2.4
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/README.md +2 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/prompts/manifest.yaml +2 -2
- package/prompts/session_guidance_v1.md +3 -1
- package/prompts/system_v1.md +8 -7
- package/pyproject.toml +2 -7
- package/src/ummaya/context/builder.py +17 -11
- package/src/ummaya/engine/engine.py +27 -7
- package/src/ummaya/engine/query.py +20 -0
- package/src/ummaya/evidence/__init__.py +25 -0
- package/src/ummaya/evidence/__main__.py +7 -0
- package/src/ummaya/evidence/models.py +58 -0
- package/src/ummaya/evidence/runner.py +308 -0
- package/src/ummaya/evidence/task_registry.py +264 -0
- package/src/ummaya/ipc/frame_schema.py +47 -0
- package/src/ummaya/ipc/stdio.py +1349 -90
- package/src/ummaya/llm/client.py +132 -56
- package/src/ummaya/llm/reasoning.py +84 -0
- package/src/ummaya/tools/discovery_bridge.py +17 -1
- package/src/ummaya/tools/executor.py +32 -12
- package/src/ummaya/tools/geocoding/kakao_client.py +1 -2
- package/src/ummaya/tools/kma/apihub_catalog.py +984 -1
- package/src/ummaya/tools/kma/apihub_structured_adapter.py +86 -6
- package/src/ummaya/tools/kma/apihub_url_adapter.py +593 -0
- package/src/ummaya/tools/kma/apihub_url_catalog.py +296 -0
- package/src/ummaya/tools/location_adapters.py +8 -6
- package/src/ummaya/tools/manifest_metadata.py +16 -3
- package/src/ummaya/tools/mvp_surface.py +2 -2
- package/src/ummaya/tools/nmc/emergency_search.py +8 -6
- package/src/ummaya/tools/register_all.py +9 -0
- package/src/ummaya/tools/resolve_location.py +4 -4
- package/src/ummaya/tools/search.py +664 -18
- package/src/ummaya/tools/verified_data_go_kr/_manifest.py +115 -25
- package/src/ummaya/tools/verified_data_go_kr/airkorea_air_quality.py +109 -4
- package/src/ummaya/tools/verified_data_go_kr/nmc_aed_site.py +108 -2
- package/src/ummaya/tools/verified_data_go_kr/pps_bid_public_info.py +174 -9
- package/src/ummaya/tools/verified_data_go_kr/tago_bus_arrival.py +66 -3
- package/src/ummaya/tools/verified_data_go_kr/tago_bus_location.py +12 -2
- package/src/ummaya/tools/verified_data_go_kr/tago_bus_route.py +8 -2
- package/src/ummaya/tools/verified_data_go_kr/tago_bus_route_station.py +114 -0
- package/src/ummaya/tools/verified_data_go_kr/tago_bus_station.py +14 -3
- package/src/ummaya/tools/verify_canonical_map.py +21 -0
- package/tui/package.json +1 -2
- package/tui/src/QueryEngine.ts +4 -0
- package/tui/src/cli/handlers/auth.ts +1 -1
- package/tui/src/cli/handlers/mcp.tsx +3 -3
- package/tui/src/cli/print.ts +69 -18
- package/tui/src/cli/update.ts +13 -13
- package/tui/src/commands/copy/index.ts +1 -1
- package/tui/src/commands/cost/cost.ts +2 -2
- package/tui/src/commands/init-verifiers.ts +5 -5
- package/tui/src/commands/init.ts +30 -30
- package/tui/src/commands/insights.ts +43 -43
- package/tui/src/commands/install-github-app/install-github-app.tsx +2 -2
- package/tui/src/commands/install-github-app/setupGitHubActions.ts +3 -3
- package/tui/src/commands/install.tsx +5 -5
- package/tui/src/commands/mcp/addCommand.ts +5 -5
- package/tui/src/commands/mcp/xaaIdpCommand.ts +2 -2
- package/tui/src/commands/plugin/ManageMarketplaces.tsx +2 -2
- package/tui/src/commands/reasoning/index.ts +13 -0
- package/tui/src/commands/reasoning/reasoning.tsx +177 -0
- package/tui/src/commands/thinkback/thinkback.tsx +3 -3
- package/tui/src/commands.ts +2 -0
- package/tui/src/components/Messages.tsx +2 -1
- package/tui/src/components/Spinner.tsx +2 -2
- package/tui/src/components/design-system/LoadingState.tsx +2 -2
- package/tui/src/ipc/codec.ts +26 -0
- package/tui/src/ipc/frames.generated.ts +398 -303
- package/tui/src/ipc/llmClient.ts +130 -51
- package/tui/src/ipc/llmTypes.ts +16 -1
- package/tui/src/ipc/schema/frame.schema.json +1 -3475
- package/tui/src/main.tsx +3 -0
- package/tui/src/query.ts +467 -2
- package/tui/src/screens/REPL.tsx +3 -3
- package/tui/src/services/api/claude.ts +54 -25
- package/tui/src/services/api/client.ts +33 -12
- package/tui/src/services/api/ummaya.ts +70 -16
- package/tui/src/skills/bundled/stuck.ts +12 -12
- package/tui/src/state/AppStateStore.ts +7 -0
- package/tui/src/tools/AdapterTool/AdapterTool.ts +590 -7
- package/tui/src/tools/LookupPrimitive/LookupPrimitive.ts +43 -17
- package/tui/src/tools/LookupPrimitive/prompt.ts +7 -6
- package/tui/src/tools/ResolveLocationPrimitive/ResolveLocationPrimitive.ts +40 -19
- package/tui/src/tools/SubmitPrimitive/SubmitPrimitive.ts +25 -9
- package/tui/src/tools/VerifyPrimitive/VerifyPrimitive.ts +25 -9
- package/tui/src/tools/_shared/citizenUserText.ts +49 -0
- package/tui/src/tools/_shared/directPublicDataGuard.ts +362 -0
- package/tui/src/tools/_shared/kmaAnalysisGuard.ts +197 -0
- package/tui/src/tools/_shared/kmaAviationGuard.ts +70 -0
- package/tui/src/tools/_shared/locationInputRepair.ts +112 -0
- package/tui/src/tools/_shared/nmcAedGuard.ts +234 -0
- package/tui/src/tools/_shared/protectedCheckGuard.ts +207 -0
- package/tui/src/tools/_shared/rootPrimitiveInput.ts +67 -0
- package/tui/src/tools/_shared/textToolCallGuard.ts +91 -0
- package/tui/src/tools/_shared/toolChoiceRepair.ts +866 -0
- package/tui/src/utils/attachments.ts +1 -1
- package/tui/src/utils/kExaoneReasoning.ts +138 -0
- package/tui/src/utils/messages.ts +1 -0
- package/tui/src/utils/multiToolLayout.ts +13 -0
- package/tui/src/utils/processUserInput/processSlashCommand.tsx +2 -2
- package/tui/src/utils/processUserInput/processUserInput.ts +26 -0
- package/tui/src/utils/settings/applySettingsChange.ts +4 -0
- package/tui/src/utils/settings/types.ts +9 -3
- package/tui/src/utils/stats.ts +1 -1
- package/uv.lock +1 -15
- package/assets/copilot-gate-logo.svg +0 -58
- package/assets/govon-logo.svg +0 -40
- package/src/ummaya/eval/__init__.py +0 -5
- package/src/ummaya/eval/retrieval.py +0 -713
- package/tui/src/utils/messageStream.ts +0 -186
|
@@ -128,6 +128,7 @@ import { feature } from 'bun:bundle'
|
|
|
128
128
|
// aliases (ClientOptions, APIError, APIConnectionTimeoutError, APIUserAbortError
|
|
129
129
|
// all re-exported by sdk-compat.ts as structural stubs).
|
|
130
130
|
import type { ClientOptions } from '../../sdk-compat.js'
|
|
131
|
+
import type { ReasoningMode } from '../../utils/kExaoneReasoning.js'
|
|
131
132
|
import {
|
|
132
133
|
APIConnectionTimeoutError,
|
|
133
134
|
APIError,
|
|
@@ -216,9 +217,11 @@ import {
|
|
|
216
217
|
TOOL_SEARCH_TOOL_NAME,
|
|
217
218
|
} from '../../tools/ToolSearchTool/prompt.js'
|
|
218
219
|
import {
|
|
219
|
-
|
|
220
|
+
getAdapterToolByName,
|
|
220
221
|
selectTopKAdapterToolNamesForQuery,
|
|
221
222
|
} from '../../tools/AdapterTool/AdapterTool.js'
|
|
223
|
+
import { isNonSyntheticUserText } from '../../tools/_shared/citizenUserText.js'
|
|
224
|
+
import { shouldSuppressUmmayaToolCallsForAnswerSynthesis } from '../../tools/_shared/toolChoiceRepair.js'
|
|
222
225
|
import { count } from '../../utils/array.js'
|
|
223
226
|
import { insertBlockAfterToolResults } from '../../utils/contentArray.js'
|
|
224
227
|
import { validateBoundedIntEnvVar } from '../../utils/envValidation.js'
|
|
@@ -723,6 +726,7 @@ export type Options = {
|
|
|
723
726
|
skipCacheWrite?: boolean
|
|
724
727
|
temperatureOverride?: number
|
|
725
728
|
effortValue?: EffortValue
|
|
729
|
+
reasoningMode?: ReasoningMode
|
|
726
730
|
mcpTools: Tools
|
|
727
731
|
hasPendingMcpServers?: boolean
|
|
728
732
|
queryTracking?: QueryChainTracking
|
|
@@ -833,7 +837,7 @@ function latestUserTextForToolRetrieval(messages: Message[]): string {
|
|
|
833
837
|
if (message?.type !== 'user') continue
|
|
834
838
|
const content = message.message?.content
|
|
835
839
|
if (typeof content === 'string') {
|
|
836
|
-
if (content
|
|
840
|
+
if (isNonSyntheticUserText(content)) return content
|
|
837
841
|
continue
|
|
838
842
|
}
|
|
839
843
|
if (Array.isArray(content)) {
|
|
@@ -844,7 +848,7 @@ function latestUserTextForToolRetrieval(messages: Message[]): string {
|
|
|
844
848
|
)
|
|
845
849
|
.map(block => block.text)
|
|
846
850
|
.join('')
|
|
847
|
-
if (text
|
|
851
|
+
if (isNonSyntheticUserText(text)) return text
|
|
848
852
|
}
|
|
849
853
|
}
|
|
850
854
|
return ''
|
|
@@ -1183,10 +1187,36 @@ async function* queryModel(
|
|
|
1183
1187
|
'query',
|
|
1184
1188
|
)
|
|
1185
1189
|
|
|
1186
|
-
|
|
1190
|
+
const turnLocalAdapterToolNames = new Set(
|
|
1191
|
+
selectTopKAdapterToolNamesForQuery(
|
|
1192
|
+
latestUserTextForToolRetrieval(messages),
|
|
1193
|
+
),
|
|
1194
|
+
)
|
|
1195
|
+
if (options.toolChoice?.type === 'tool') {
|
|
1196
|
+
turnLocalAdapterToolNames.add(options.toolChoice.name)
|
|
1197
|
+
}
|
|
1198
|
+
if (turnLocalAdapterToolNames.size > 0) {
|
|
1199
|
+
logForDebugging(
|
|
1200
|
+
`UMMAYA turn-local adapter schemas: ${[...turnLocalAdapterToolNames].join(', ')}`,
|
|
1201
|
+
)
|
|
1202
|
+
}
|
|
1203
|
+
const requestTools =
|
|
1204
|
+
turnLocalAdapterToolNames.size === 0
|
|
1205
|
+
? tools
|
|
1206
|
+
: [
|
|
1207
|
+
...tools,
|
|
1208
|
+
...[...turnLocalAdapterToolNames]
|
|
1209
|
+
.filter(toolName => !tools.some(tool => tool.name === toolName))
|
|
1210
|
+
.map(toolName => getAdapterToolByName(toolName))
|
|
1211
|
+
.filter((tool): tool is NonNullable<typeof tool> => Boolean(tool)),
|
|
1212
|
+
]
|
|
1213
|
+
|
|
1214
|
+
// Precompute once — isDeferredTool does 2 GrowthBook lookups per call.
|
|
1215
|
+
// Include turn-local synced adapters even if the long-lived TUI tool pool
|
|
1216
|
+
// was assembled before the latest backend manifest frame arrived.
|
|
1187
1217
|
const deferredToolNames = new Set<string>()
|
|
1188
1218
|
if (useToolSearch) {
|
|
1189
|
-
for (const t of
|
|
1219
|
+
for (const t of requestTools) {
|
|
1190
1220
|
if (isDeferredTool(t)) deferredToolNames.add(t.name)
|
|
1191
1221
|
}
|
|
1192
1222
|
}
|
|
@@ -1204,32 +1234,29 @@ async function* queryModel(
|
|
|
1204
1234
|
)
|
|
1205
1235
|
useToolSearch = false
|
|
1206
1236
|
}
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
),
|
|
1212
|
-
)
|
|
1213
|
-
if (turnLocalAdapterToolNames.size > 0) {
|
|
1214
|
-
logForDebugging(
|
|
1215
|
-
`UMMAYA turn-local adapter schemas: ${[...turnLocalAdapterToolNames].join(', ')}`,
|
|
1216
|
-
)
|
|
1237
|
+
const suppressUmmayaToolCalls =
|
|
1238
|
+
shouldSuppressUmmayaToolCallsForAnswerSynthesis({ messages, tools: requestTools })
|
|
1239
|
+
if (suppressUmmayaToolCalls) {
|
|
1240
|
+
logForDebugging('UMMAYA suppressing tool schemas for answer synthesis')
|
|
1217
1241
|
}
|
|
1218
1242
|
|
|
1219
1243
|
// Filter out ToolSearchTool if tool search is not enabled for this model
|
|
1220
1244
|
// ToolSearchTool returns tool_reference blocks which unsupported models can't handle
|
|
1221
1245
|
let filteredTools: Tools
|
|
1222
1246
|
|
|
1223
|
-
if (
|
|
1247
|
+
if (suppressUmmayaToolCalls) {
|
|
1248
|
+
filteredTools = []
|
|
1249
|
+
} else if (useToolSearch) {
|
|
1224
1250
|
// Dynamic tool loading: Only include deferred tools that have been discovered
|
|
1225
1251
|
// via tool_reference blocks in the message history. This eliminates the need
|
|
1226
1252
|
// to predeclare all deferred tools upfront and removes limits on tool quantity.
|
|
1227
1253
|
const discoveredToolNames = extractDiscoveredToolNames(messages)
|
|
1228
1254
|
|
|
1229
|
-
filteredTools =
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1255
|
+
filteredTools = requestTools.filter(tool => {
|
|
1256
|
+
// 0.2.1 exposed the lightweight root primitives together with concrete
|
|
1257
|
+
// adapter schemas. Keep that surface so K-EXAONE preserves CC-style
|
|
1258
|
+
// prose→tool→prose loop painting, while still limiting concrete adapter
|
|
1259
|
+
// schemas to the turn-local top-k set.
|
|
1233
1260
|
if (turnLocalAdapterToolNames.has(tool.name)) return true
|
|
1234
1261
|
// Always include non-deferred tools
|
|
1235
1262
|
if (!deferredToolNames.has(tool.name)) return true
|
|
@@ -1239,11 +1266,10 @@ async function* queryModel(
|
|
|
1239
1266
|
return discoveredToolNames.has(tool.name)
|
|
1240
1267
|
})
|
|
1241
1268
|
} else {
|
|
1242
|
-
filteredTools =
|
|
1269
|
+
filteredTools = requestTools.filter(t => {
|
|
1243
1270
|
if (toolMatchesName(t, TOOL_SEARCH_TOOL_NAME)) return false
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
}
|
|
1271
|
+
// Keep non-deferred root primitives even when concrete top-k adapter
|
|
1272
|
+
// schemas are available; this matches the released 0.2.1 loop surface.
|
|
1247
1273
|
if (isDeferredTool(t)) return turnLocalAdapterToolNames.has(t.name)
|
|
1248
1274
|
return true
|
|
1249
1275
|
})
|
|
@@ -1803,6 +1829,9 @@ async function* queryModel(
|
|
|
1803
1829
|
output_config: outputConfig,
|
|
1804
1830
|
}),
|
|
1805
1831
|
...(speed !== undefined && { speed }),
|
|
1832
|
+
...(options.reasoningMode !== undefined && {
|
|
1833
|
+
reasoning_mode: options.reasoningMode,
|
|
1834
|
+
}),
|
|
1806
1835
|
}
|
|
1807
1836
|
}
|
|
1808
1837
|
|
|
@@ -2346,7 +2375,7 @@ async function* queryModel(
|
|
|
2346
2375
|
max_tokens: maxOutputTokens,
|
|
2347
2376
|
})
|
|
2348
2377
|
yield createAssistantAPIErrorMessage({
|
|
2349
|
-
content: `${API_ERROR_MESSAGE_PREFIX}:
|
|
2378
|
+
content: `${API_ERROR_MESSAGE_PREFIX}: Ummaya's response exceeded the ${
|
|
2350
2379
|
maxOutputTokens
|
|
2351
2380
|
} output token maximum. To configure this behavior, set the CLAUDE_CODE_MAX_OUTPUT_TOKENS environment variable.`,
|
|
2352
2381
|
apiError: 'max_output_tokens',
|
|
@@ -26,6 +26,11 @@ import {
|
|
|
26
26
|
} from '../../sdk-compat.js'
|
|
27
27
|
import { assertFriendliApiKeyForUse } from '../../utils/auth.js'
|
|
28
28
|
import { getUserAgent } from '../../utils/http.js'
|
|
29
|
+
import {
|
|
30
|
+
providerReasoningPayload,
|
|
31
|
+
resolveKExaoneReasoningPolicy,
|
|
32
|
+
type ReasoningMode,
|
|
33
|
+
} from '../../utils/kExaoneReasoning.js'
|
|
29
34
|
import { UMMAYA_K_EXAONE_MODEL } from '../../utils/model/constants.js'
|
|
30
35
|
|
|
31
36
|
export const CLIENT_REQUEST_ID_HEADER = 'x-client-request-id'
|
|
@@ -41,6 +46,11 @@ type RequestOptions = {
|
|
|
41
46
|
headers?: Record<string, string>
|
|
42
47
|
}
|
|
43
48
|
|
|
49
|
+
type FriendliMessageStreamParams = BetaMessageStreamParams & {
|
|
50
|
+
stream?: boolean
|
|
51
|
+
reasoning_mode?: ReasoningMode
|
|
52
|
+
}
|
|
53
|
+
|
|
44
54
|
type FriendliClientArgs = {
|
|
45
55
|
apiKey?: string
|
|
46
56
|
maxRetries: number
|
|
@@ -130,7 +140,7 @@ class FriendliMessagesCompatClient {
|
|
|
130
140
|
readonly beta: {
|
|
131
141
|
messages: {
|
|
132
142
|
create: (
|
|
133
|
-
params:
|
|
143
|
+
params: FriendliMessageStreamParams,
|
|
134
144
|
options?: RequestOptions,
|
|
135
145
|
) => unknown
|
|
136
146
|
}
|
|
@@ -159,7 +169,7 @@ class FriendliMessagesCompatClient {
|
|
|
159
169
|
}
|
|
160
170
|
|
|
161
171
|
private async streamWithResponse(
|
|
162
|
-
params:
|
|
172
|
+
params: FriendliMessageStreamParams,
|
|
163
173
|
options?: RequestOptions,
|
|
164
174
|
): Promise<{
|
|
165
175
|
data: Stream<BetaRawMessageStreamEvent>
|
|
@@ -188,7 +198,7 @@ class FriendliMessagesCompatClient {
|
|
|
188
198
|
}
|
|
189
199
|
|
|
190
200
|
private async complete(
|
|
191
|
-
params:
|
|
201
|
+
params: FriendliMessageStreamParams,
|
|
192
202
|
options?: RequestOptions,
|
|
193
203
|
): Promise<BetaMessage> {
|
|
194
204
|
const response = await this.fetchChatCompletion(
|
|
@@ -279,6 +289,9 @@ class FriendliMessagesCompatClient {
|
|
|
279
289
|
const toolStates = new Map<number, ToolStreamState>()
|
|
280
290
|
let finalUsage: BetaUsage | undefined
|
|
281
291
|
let stopReason: BetaMessage['stop_reason'] = 'end_turn'
|
|
292
|
+
const allowReasoning = resolveKExaoneReasoningPolicy({
|
|
293
|
+
explicitSessionMode: params.reasoning_mode,
|
|
294
|
+
}).includeReasoning
|
|
282
295
|
|
|
283
296
|
const ensureMessageStart = function* () {
|
|
284
297
|
if (messageStarted) return
|
|
@@ -301,6 +314,12 @@ class FriendliMessagesCompatClient {
|
|
|
301
314
|
const ensureTextBlock = function* () {
|
|
302
315
|
yield* ensureMessageStart()
|
|
303
316
|
if (textBlockIndex === undefined) {
|
|
317
|
+
if (
|
|
318
|
+
thinkingBlockIndex !== undefined &&
|
|
319
|
+
openedBlocks.has(thinkingBlockIndex)
|
|
320
|
+
) {
|
|
321
|
+
yield* closeNonToolBlocks()
|
|
322
|
+
}
|
|
304
323
|
textBlockIndex = nextBlockIndex++
|
|
305
324
|
openedBlocks.add(textBlockIndex)
|
|
306
325
|
yield {
|
|
@@ -314,6 +333,9 @@ class FriendliMessagesCompatClient {
|
|
|
314
333
|
const ensureThinkingBlock = function* () {
|
|
315
334
|
yield* ensureMessageStart()
|
|
316
335
|
if (thinkingBlockIndex === undefined) {
|
|
336
|
+
if (textBlockIndex !== undefined && openedBlocks.has(textBlockIndex)) {
|
|
337
|
+
yield* closeNonToolBlocks()
|
|
338
|
+
}
|
|
317
339
|
thinkingBlockIndex = nextBlockIndex++
|
|
318
340
|
openedBlocks.add(thinkingBlockIndex)
|
|
319
341
|
yield {
|
|
@@ -385,7 +407,7 @@ class FriendliMessagesCompatClient {
|
|
|
385
407
|
|
|
386
408
|
for (const choice of chunk.choices ?? []) {
|
|
387
409
|
const delta = choice.delta ?? {}
|
|
388
|
-
if (delta.reasoning_content) {
|
|
410
|
+
if (delta.reasoning_content && allowReasoning) {
|
|
389
411
|
yield* ensureThinkingBlock()
|
|
390
412
|
yield {
|
|
391
413
|
type: 'content_block_delta' as const,
|
|
@@ -454,17 +476,20 @@ class FriendliMessagesCompatClient {
|
|
|
454
476
|
}
|
|
455
477
|
|
|
456
478
|
function buildOpenAIPayload(
|
|
457
|
-
params:
|
|
479
|
+
params: FriendliMessageStreamParams,
|
|
458
480
|
): Record<string, unknown> {
|
|
459
481
|
const messages = convertMessages(params.messages, params.system)
|
|
460
482
|
const tools = convertTools(params.tools)
|
|
483
|
+
const reasoning = providerReasoningPayload(
|
|
484
|
+
resolveKExaoneReasoningPolicy({
|
|
485
|
+
explicitSessionMode: params.reasoning_mode,
|
|
486
|
+
}),
|
|
487
|
+
)
|
|
461
488
|
const payload: Record<string, unknown> = {
|
|
462
489
|
model: params.model || UMMAYA_K_EXAONE_MODEL,
|
|
463
490
|
messages,
|
|
464
491
|
max_tokens: params.max_tokens,
|
|
465
|
-
|
|
466
|
-
enable_thinking: isTruthy(process.env.UMMAYA_K_EXAONE_THINKING),
|
|
467
|
-
},
|
|
492
|
+
...reasoning,
|
|
468
493
|
...(params.temperature !== undefined ? { temperature: params.temperature } : {}),
|
|
469
494
|
...(tools.length > 0
|
|
470
495
|
? {
|
|
@@ -740,7 +765,3 @@ function resolveFetch(fetchOverride: unknown): FetchLike {
|
|
|
740
765
|
function trimSlash(value: string): string {
|
|
741
766
|
return value.replace(/\/+$/, '')
|
|
742
767
|
}
|
|
743
|
-
|
|
744
|
-
function isTruthy(value: string | undefined): boolean {
|
|
745
|
-
return value === '1' || value?.toLowerCase() === 'true' || value?.toLowerCase() === 'yes'
|
|
746
|
-
}
|
|
@@ -55,8 +55,11 @@ export async function* queryModelWithStreaming(params: {
|
|
|
55
55
|
const persistThinking = process.env.UMMAYA_PERSIST_THINKING === '1'
|
|
56
56
|
let accumulatedThinking = ''
|
|
57
57
|
let messageStartEmitted = false
|
|
58
|
-
let
|
|
58
|
+
let nextContentBlockIndex = 0
|
|
59
|
+
let textBlockIndex: number | null = null
|
|
59
60
|
let textBlockStopped = false
|
|
61
|
+
let thinkingBlockIndex: number | null = null
|
|
62
|
+
let thinkingBlockStopped = false
|
|
60
63
|
const pendingContentBlocks: Array<{
|
|
61
64
|
type: 'tool_use'
|
|
62
65
|
id: string
|
|
@@ -119,16 +122,17 @@ export async function* queryModelWithStreaming(params: {
|
|
|
119
122
|
}
|
|
120
123
|
messageStartEmitted = true
|
|
121
124
|
}
|
|
122
|
-
if (
|
|
125
|
+
if (thinkingText.length > 0 && thinkingBlockIndex === null) {
|
|
123
126
|
yield {
|
|
124
127
|
type: 'stream_event' as const,
|
|
125
128
|
event: {
|
|
126
129
|
type: 'content_block_start' as const,
|
|
127
|
-
index:
|
|
128
|
-
content_block: { type: '
|
|
130
|
+
index: nextContentBlockIndex,
|
|
131
|
+
content_block: { type: 'thinking' as const, thinking: '' },
|
|
129
132
|
},
|
|
130
133
|
}
|
|
131
|
-
|
|
134
|
+
thinkingBlockIndex = nextContentBlockIndex
|
|
135
|
+
nextContentBlockIndex += 1
|
|
132
136
|
}
|
|
133
137
|
|
|
134
138
|
if (thinkingText.length > 0) {
|
|
@@ -139,7 +143,7 @@ export async function* queryModelWithStreaming(params: {
|
|
|
139
143
|
type: 'stream_event' as const,
|
|
140
144
|
event: {
|
|
141
145
|
type: 'content_block_delta' as const,
|
|
142
|
-
index: 0,
|
|
146
|
+
index: thinkingBlockIndex ?? 0,
|
|
143
147
|
delta: { type: 'thinking_delta' as const, thinking: thinkingText },
|
|
144
148
|
},
|
|
145
149
|
}
|
|
@@ -147,21 +151,50 @@ export async function* queryModelWithStreaming(params: {
|
|
|
147
151
|
|
|
148
152
|
accumulated += deltaText
|
|
149
153
|
if (deltaText.length > 0) {
|
|
154
|
+
if (thinkingBlockIndex !== null && !thinkingBlockStopped) {
|
|
155
|
+
yield {
|
|
156
|
+
type: 'stream_event' as const,
|
|
157
|
+
event: {
|
|
158
|
+
type: 'content_block_stop' as const,
|
|
159
|
+
index: thinkingBlockIndex,
|
|
160
|
+
},
|
|
161
|
+
}
|
|
162
|
+
thinkingBlockStopped = true
|
|
163
|
+
}
|
|
164
|
+
if (textBlockIndex === null) {
|
|
165
|
+
yield {
|
|
166
|
+
type: 'stream_event' as const,
|
|
167
|
+
event: {
|
|
168
|
+
type: 'content_block_start' as const,
|
|
169
|
+
index: nextContentBlockIndex,
|
|
170
|
+
content_block: { type: 'text' as const, text: '' },
|
|
171
|
+
},
|
|
172
|
+
}
|
|
173
|
+
textBlockIndex = nextContentBlockIndex
|
|
174
|
+
nextContentBlockIndex += 1
|
|
175
|
+
}
|
|
150
176
|
yield {
|
|
151
177
|
type: 'stream_event' as const,
|
|
152
178
|
event: {
|
|
153
179
|
type: 'content_block_delta' as const,
|
|
154
|
-
index:
|
|
180
|
+
index: textBlockIndex,
|
|
155
181
|
delta: { type: 'text_delta' as const, text: deltaText },
|
|
156
182
|
},
|
|
157
183
|
}
|
|
158
184
|
}
|
|
159
185
|
|
|
160
186
|
if (frameAny.done) {
|
|
161
|
-
if (
|
|
187
|
+
if (thinkingBlockIndex !== null && !thinkingBlockStopped) {
|
|
188
|
+
yield {
|
|
189
|
+
type: 'stream_event' as const,
|
|
190
|
+
event: { type: 'content_block_stop' as const, index: thinkingBlockIndex },
|
|
191
|
+
}
|
|
192
|
+
thinkingBlockStopped = true
|
|
193
|
+
}
|
|
194
|
+
if (textBlockIndex !== null && !textBlockStopped) {
|
|
162
195
|
yield {
|
|
163
196
|
type: 'stream_event' as const,
|
|
164
|
-
event: { type: 'content_block_stop' as const, index:
|
|
197
|
+
event: { type: 'content_block_stop' as const, index: textBlockIndex },
|
|
165
198
|
}
|
|
166
199
|
textBlockStopped = true
|
|
167
200
|
}
|
|
@@ -217,10 +250,17 @@ export async function* queryModelWithStreaming(params: {
|
|
|
217
250
|
}
|
|
218
251
|
messageStartEmitted = true
|
|
219
252
|
}
|
|
220
|
-
if (
|
|
253
|
+
if (thinkingBlockIndex !== null && !thinkingBlockStopped) {
|
|
221
254
|
yield {
|
|
222
255
|
type: 'stream_event' as const,
|
|
223
|
-
event: { type: 'content_block_stop' as const, index:
|
|
256
|
+
event: { type: 'content_block_stop' as const, index: thinkingBlockIndex },
|
|
257
|
+
}
|
|
258
|
+
thinkingBlockStopped = true
|
|
259
|
+
}
|
|
260
|
+
if (textBlockIndex !== null && !textBlockStopped) {
|
|
261
|
+
yield {
|
|
262
|
+
type: 'stream_event' as const,
|
|
263
|
+
event: { type: 'content_block_stop' as const, index: textBlockIndex },
|
|
224
264
|
}
|
|
225
265
|
textBlockStopped = true
|
|
226
266
|
}
|
|
@@ -232,7 +272,8 @@ export async function* queryModelWithStreaming(params: {
|
|
|
232
272
|
}
|
|
233
273
|
|
|
234
274
|
pendingContentBlocks.push(toolUseBlock)
|
|
235
|
-
const toolBlockIndex =
|
|
275
|
+
const toolBlockIndex = nextContentBlockIndex
|
|
276
|
+
nextContentBlockIndex += 1
|
|
236
277
|
|
|
237
278
|
yield {
|
|
238
279
|
type: 'stream_event' as const,
|
|
@@ -271,10 +312,17 @@ export async function* queryModelWithStreaming(params: {
|
|
|
271
312
|
} else if (frameAny.kind === 'error') {
|
|
272
313
|
const reason = frameAny.message ?? 'UMMAYA backend error'
|
|
273
314
|
yield createAssistantMessage({ content: `[UMMAYA backend error] ${reason}` })
|
|
274
|
-
if (
|
|
315
|
+
if (thinkingBlockIndex !== null && !thinkingBlockStopped) {
|
|
275
316
|
yield {
|
|
276
317
|
type: 'stream_event' as const,
|
|
277
|
-
event: { type: 'content_block_stop' as const, index:
|
|
318
|
+
event: { type: 'content_block_stop' as const, index: thinkingBlockIndex },
|
|
319
|
+
}
|
|
320
|
+
thinkingBlockStopped = true
|
|
321
|
+
}
|
|
322
|
+
if (textBlockIndex !== null && !textBlockStopped) {
|
|
323
|
+
yield {
|
|
324
|
+
type: 'stream_event' as const,
|
|
325
|
+
event: { type: 'content_block_stop' as const, index: textBlockIndex },
|
|
278
326
|
}
|
|
279
327
|
textBlockStopped = true
|
|
280
328
|
}
|
|
@@ -299,10 +347,16 @@ export async function* queryModelWithStreaming(params: {
|
|
|
299
347
|
})
|
|
300
348
|
}
|
|
301
349
|
if (messageStartEmitted) {
|
|
302
|
-
if (
|
|
350
|
+
if (thinkingBlockIndex !== null && !thinkingBlockStopped) {
|
|
351
|
+
yield {
|
|
352
|
+
type: 'stream_event' as const,
|
|
353
|
+
event: { type: 'content_block_stop' as const, index: thinkingBlockIndex },
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
if (textBlockIndex !== null && !textBlockStopped) {
|
|
303
357
|
yield {
|
|
304
358
|
type: 'stream_event' as const,
|
|
305
|
-
event: { type: 'content_block_stop' as const, index:
|
|
359
|
+
event: { type: 'content_block_stop' as const, index: textBlockIndex },
|
|
306
360
|
}
|
|
307
361
|
}
|
|
308
362
|
yield {
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { registerBundledSkill } from '../bundledSkills.js'
|
|
2
2
|
|
|
3
|
-
// Prompt text contains `ps` commands as instructions for
|
|
3
|
+
// Prompt text contains `ps` commands as instructions for UMMAYA to run,
|
|
4
4
|
// not commands this file executes.
|
|
5
5
|
// eslint-disable-next-line custom-rules/no-direct-ps-commands
|
|
6
|
-
const STUCK_PROMPT = `# /stuck — diagnose frozen/slow
|
|
6
|
+
const STUCK_PROMPT = `# /stuck — diagnose frozen/slow UMMAYA sessions
|
|
7
7
|
|
|
8
|
-
The user thinks another
|
|
8
|
+
The user thinks another UMMAYA session on this machine is frozen, stuck, or very slow. Investigate and post a report to the configured UMMAYA feedback channel.
|
|
9
9
|
|
|
10
10
|
## What to look for
|
|
11
11
|
|
|
12
|
-
Scan for other
|
|
12
|
+
Scan for other UMMAYA processes (excluding the current one — PID is in \`process.pid\` but for shell commands just exclude the PID you see running this prompt). Process names are typically \`ummaya\` (installed) or \`cli\` (native dev build).
|
|
13
13
|
|
|
14
14
|
Signs of a stuck session:
|
|
15
15
|
- **High CPU (≥90%) sustained** — likely an infinite loop. Sample twice, 1-2s apart, to confirm it's not a transient spike.
|
|
@@ -21,17 +21,17 @@ Signs of a stuck session:
|
|
|
21
21
|
|
|
22
22
|
## Investigation steps
|
|
23
23
|
|
|
24
|
-
1. **List all
|
|
24
|
+
1. **List all UMMAYA processes** (macOS/Linux):
|
|
25
25
|
\`\`\`
|
|
26
|
-
ps -axo pid=,pcpu=,rss=,etime=,state=,comm=,command= | grep -E '(
|
|
26
|
+
ps -axo pid=,pcpu=,rss=,etime=,state=,comm=,command= | grep -E '(ummaya|cli)' | grep -v grep
|
|
27
27
|
\`\`\`
|
|
28
|
-
Filter to rows where \`comm\` is \`
|
|
28
|
+
Filter to rows where \`comm\` is \`ummaya\` or (\`cli\` AND the command path contains "ummaya").
|
|
29
29
|
|
|
30
30
|
2. **For anything suspicious**, gather more context:
|
|
31
31
|
- Child processes: \`pgrep -lP <pid>\`
|
|
32
32
|
- If high CPU: sample again after 1-2s to confirm it's sustained
|
|
33
33
|
- If a child looks hung (e.g., a git command), note its full command line with \`ps -p <child_pid> -o command=\`
|
|
34
|
-
- Check the session's debug log if you can infer the session ID: \`~/.
|
|
34
|
+
- Check the session's debug log if you can infer the session ID: \`~/.ummaya/debug/<session-id>.txt\` (the last few hundred lines often show what it was doing before hanging)
|
|
35
35
|
|
|
36
36
|
3. **Consider a stack dump** for a truly frozen process (advanced, optional):
|
|
37
37
|
- macOS: \`sample <pid> 3\` gives a 3-second native stack sample
|
|
@@ -41,17 +41,17 @@ Signs of a stuck session:
|
|
|
41
41
|
|
|
42
42
|
**Only post to Slack if you actually found something stuck.** If every session looks healthy, tell the user that directly — do not post an all-clear to the channel.
|
|
43
43
|
|
|
44
|
-
If you did find a stuck/slow session, post to
|
|
44
|
+
If you did find a stuck/slow session, post to the configured UMMAYA feedback channel using the Slack MCP tool. Use ToolSearch to find \`slack_send_message\` if it's not already loaded.
|
|
45
45
|
|
|
46
46
|
**Use a two-message structure** to keep the channel scannable:
|
|
47
47
|
|
|
48
|
-
1. **Top-level message** — one short line: hostname,
|
|
48
|
+
1. **Top-level message** — one short line: hostname, UMMAYA version, and a terse symptom (e.g. "session PID 12345 pegged at 100% CPU for 10min" or "git subprocess hung in D state"). No code blocks, no details.
|
|
49
49
|
2. **Thread reply** — the full diagnostic dump. Pass the top-level message's \`ts\` as \`thread_ts\`. Include:
|
|
50
50
|
- PID, CPU%, RSS, state, uptime, command line, child processes
|
|
51
51
|
- Your diagnosis of what's likely wrong
|
|
52
52
|
- Relevant debug log tail or \`sample\` output if you captured it
|
|
53
53
|
|
|
54
|
-
If Slack MCP isn't available, format the report as a message the user can copy-paste into
|
|
54
|
+
If Slack MCP isn't available, format the report as a message the user can copy-paste into the configured UMMAYA feedback channel (and let them know to thread the details themselves).
|
|
55
55
|
|
|
56
56
|
## Notes
|
|
57
57
|
- Don't kill or signal any processes — this is diagnostic only.
|
|
@@ -66,7 +66,7 @@ export function registerStuckSkill(): void {
|
|
|
66
66
|
registerBundledSkill({
|
|
67
67
|
name: 'stuck',
|
|
68
68
|
description:
|
|
69
|
-
'[ANT-ONLY] Investigate frozen/stuck/slow
|
|
69
|
+
'[ANT-ONLY] Investigate frozen/stuck/slow UMMAYA sessions on this machine and post a diagnostic report to the configured UMMAYA feedback channel.',
|
|
70
70
|
userInvocable: true,
|
|
71
71
|
async getPromptForCommand(args) {
|
|
72
72
|
let prompt = STUCK_PROMPT
|
|
@@ -35,6 +35,10 @@ import type { DenialTrackingState } from '../utils/permissions/denialTracking.js
|
|
|
35
35
|
import type { PermissionMode } from '../utils/permissions/PermissionMode.js'
|
|
36
36
|
import { getInitialSettings } from '../utils/settings/settings.js'
|
|
37
37
|
import type { SettingsJson } from '../utils/settings/types.js'
|
|
38
|
+
import {
|
|
39
|
+
getInitialReasoningModeSetting,
|
|
40
|
+
type ReasoningMode,
|
|
41
|
+
} from '../utils/kExaoneReasoning.js'
|
|
38
42
|
import { shouldEnableThinkingByDefault } from '../utils/thinking.js'
|
|
39
43
|
import type { Store } from './store.js'
|
|
40
44
|
|
|
@@ -425,6 +429,8 @@ export type AppState = DeepImmutable<{
|
|
|
425
429
|
advisorModel?: string
|
|
426
430
|
// Effort value
|
|
427
431
|
effortValue?: EffortValue
|
|
432
|
+
// K-EXAONE/FriendliAI reasoning payload policy.
|
|
433
|
+
reasoningMode?: ReasoningMode
|
|
428
434
|
// Set synchronously in launchUltraplan before the detached flow starts.
|
|
429
435
|
// Prevents duplicate launches during the ~5s window before
|
|
430
436
|
// ultraplanSessionUrl is set by teleportToRemote. Cleared by launchDetached
|
|
@@ -563,6 +569,7 @@ export function getDefaultAppState(): AppState {
|
|
|
563
569
|
authVersion: 0,
|
|
564
570
|
initialMessage: null,
|
|
565
571
|
effortValue: undefined,
|
|
572
|
+
reasoningMode: getInitialReasoningModeSetting(),
|
|
566
573
|
activeOverlays: new Set<string>(),
|
|
567
574
|
fastMode: false,
|
|
568
575
|
}
|