ummaya 0.2.0 → 0.2.2

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/bin/ummaya CHANGED
@@ -7,9 +7,27 @@ import { delimiter, dirname, join } from 'node:path'
7
7
  import { fileURLToPath } from 'node:url'
8
8
 
9
9
  const MINIMUM_BUN_VERSION = '1.3.0'
10
- const launcherPath = realpathSync(fileURLToPath(import.meta.url))
10
+ const PACKAGED_PRIMITIVE_TIMEOUT_MS = '90000'
11
11
 
12
- function bunVersionAtLeast(version, minimum) {
12
+ function currentLauncherPath() {
13
+ return realpathSync(fileURLToPath(import.meta.url))
14
+ }
15
+
16
+ function invokedLauncherPath() {
17
+ const argvPath = process.argv[1]
18
+ if (!argvPath) return null
19
+ try {
20
+ return realpathSync(argvPath)
21
+ } catch {
22
+ return null
23
+ }
24
+ }
25
+
26
+ function isDirectLaunch() {
27
+ return invokedLauncherPath() === currentLauncherPath()
28
+ }
29
+
30
+ export function bunVersionAtLeast(version, minimum) {
13
31
  const current = String(version)
14
32
  .split('.')
15
33
  .map((part) => Number.parseInt(part, 10))
@@ -62,7 +80,7 @@ function probeBunVersion(candidate) {
62
80
  return (result.stdout || result.stderr).trim()
63
81
  }
64
82
 
65
- function launchWithCompatibleBun(currentVersion = null) {
83
+ function launchWithCompatibleBun(launcherPath, currentVersion = null) {
66
84
  const checked = []
67
85
  for (const candidate of collectBunCandidates()) {
68
86
  const version = probeBunVersion(candidate)
@@ -94,17 +112,7 @@ function launchWithCompatibleBun(currentVersion = null) {
94
112
  process.exit(1)
95
113
  }
96
114
 
97
- const runningBun = globalThis.Bun
98
- if (!runningBun) {
99
- launchWithCompatibleBun()
100
- }
101
- if (!bunVersionAtLeast(runningBun.version, MINIMUM_BUN_VERSION)) {
102
- launchWithCompatibleBun(runningBun.version)
103
- }
104
-
105
- const packageRoot = dirname(dirname(launcherPath))
106
-
107
- function loadPackageDotenv(root) {
115
+ export function loadPackageDotenv(root, env = process.env) {
108
116
  const envPath = join(root, '.env')
109
117
  if (!existsSync(envPath)) return
110
118
  const lines = readFileSync(envPath, 'utf8').split(/\r?\n/)
@@ -114,29 +122,66 @@ function loadPackageDotenv(root) {
114
122
  const index = line.indexOf('=')
115
123
  const key = line.slice(0, index).trim()
116
124
  let value = line.slice(index + 1).trim()
117
- if (!key || process.env[key] !== undefined) continue
125
+ if (!key || env[key] !== undefined) continue
118
126
  if (
119
127
  (value.startsWith('"') && value.endsWith('"')) ||
120
128
  (value.startsWith("'") && value.endsWith("'"))
121
129
  ) {
122
130
  value = value.slice(1, -1)
123
131
  }
124
- process.env[key] = value
132
+ env[key] = value
133
+ }
134
+ }
135
+
136
+ export function buildBackendCommand(root) {
137
+ const packagedPython = join(root, '.venv', 'bin', 'python')
138
+ if (existsSync(packagedPython)) {
139
+ return [packagedPython, '-m', 'ummaya.cli', '--ipc', 'stdio']
125
140
  }
141
+
142
+ return ['uv', '--directory', root, 'run', '--frozen', '--no-dev', 'ummaya', '--ipc', 'stdio']
143
+ }
144
+
145
+ export function configurePackageEnv(root, env = process.env) {
146
+ env.UMMAYA_PACKAGE_ROOT = root
147
+ if (env.UMMAYA_ALLOW_BACKEND_CMD_OVERRIDE !== '1' || !env.UMMAYA_BACKEND_CMD_JSON) {
148
+ env.UMMAYA_BACKEND_CMD_JSON = JSON.stringify(buildBackendCommand(root))
149
+ }
150
+ env.UMMAYA_TUI_PRIMITIVE_TIMEOUT_MS ??= PACKAGED_PRIMITIVE_TIMEOUT_MS
151
+ return env
152
+ }
153
+
154
+ function inspectLauncherAndExit() {
155
+ if (process.env.UMMAYA_LAUNCHER_INSPECT !== '1') return
156
+ process.stdout.write(
157
+ `${JSON.stringify({
158
+ packageRoot: process.env.UMMAYA_PACKAGE_ROOT,
159
+ backendCommand: JSON.parse(process.env.UMMAYA_BACKEND_CMD_JSON ?? '[]'),
160
+ primitiveTimeoutMs: process.env.UMMAYA_TUI_PRIMITIVE_TIMEOUT_MS,
161
+ })}\n`,
162
+ )
163
+ process.exit(0)
126
164
  }
127
165
 
128
- loadPackageDotenv(packageRoot)
129
-
130
- process.env.UMMAYA_PACKAGE_ROOT ??= packageRoot
131
- process.env.UMMAYA_BACKEND_CMD_JSON ??= JSON.stringify([
132
- 'uv',
133
- '--directory',
134
- packageRoot,
135
- 'run',
136
- 'ummaya',
137
- '--ipc',
138
- 'stdio',
139
- ])
140
-
141
- await import(join(packageRoot, 'tui/src/stubs/macro-preload.ts'))
142
- await import(join(packageRoot, 'tui/src/entrypoints/cli.tsx'))
166
+ export async function main() {
167
+ const launcherPath = currentLauncherPath()
168
+ const runningBun = globalThis.Bun
169
+ if (!runningBun) {
170
+ launchWithCompatibleBun(launcherPath)
171
+ }
172
+ if (!bunVersionAtLeast(runningBun.version, MINIMUM_BUN_VERSION)) {
173
+ launchWithCompatibleBun(launcherPath, runningBun.version)
174
+ }
175
+
176
+ const packageRoot = dirname(dirname(launcherPath))
177
+ loadPackageDotenv(packageRoot)
178
+ configurePackageEnv(packageRoot)
179
+ inspectLauncherAndExit()
180
+
181
+ await import(join(packageRoot, 'tui/src/stubs/macro-preload.ts'))
182
+ await import(join(packageRoot, 'tui/src/entrypoints/cli.tsx'))
183
+ }
184
+
185
+ if (isDirectLaunch()) {
186
+ await main()
187
+ }
package/bun.lock CHANGED
@@ -39,6 +39,7 @@
39
39
  "auto-bind": "^5.0.1",
40
40
  "axios": "^1.15.2",
41
41
  "bidi-js": "^1.0.3",
42
+ "bundle": "file:./tui/src/runtime/bundle-package",
42
43
  "chalk": "^5.3.0",
43
44
  "chokidar": "^4.0.0",
44
45
  "cli-boxes": "^4.0.1",
@@ -64,6 +65,7 @@
64
65
  "proper-lockfile": "^4.1.2",
65
66
  "qrcode": "^1.5.4",
66
67
  "react": "^19.2.0",
68
+ "react-reconciler": "0.33.0",
67
69
  "semver": "^7.7.4",
68
70
  "shell-quote": "^1.8.3",
69
71
  "supports-hyperlinks": "^4.4.0",
@@ -446,6 +448,8 @@
446
448
 
447
449
  "buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="],
448
450
 
451
+ "bundle": ["bundle@file:tui/src/runtime/bundle-package", {}],
452
+
449
453
  "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
450
454
 
451
455
  "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
@@ -892,7 +896,7 @@
892
896
 
893
897
  "react": ["react@19.2.6", "", {}, "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q=="],
894
898
 
895
- "react-reconciler": ["react-reconciler@0.32.0", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-2NPMOzgTlG0ZWdIf3qG+dcbLSoAc/uLfOwckc3ofy5sSK0pLJqnQLpUFxvGcN2rlXSjnVtGeeFLNimCQEj5gOQ=="],
899
+ "react-reconciler": ["react-reconciler@0.33.0", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.0" } }, "sha512-KetWRytFv1epdpJc3J4G75I4WrplZE5jOL7Yq0p34+OVOKF4Se7WrdIdVC45XsSSmUTlht2FM/fM1FZb1mfQeA=="],
896
900
 
897
901
  "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
898
902
 
@@ -916,7 +920,7 @@
916
920
 
917
921
  "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
918
922
 
919
- "scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="],
923
+ "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
920
924
 
921
925
  "semver": ["semver@7.7.4", "", { "bin": "bin/semver.js" }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
922
926
 
@@ -1132,6 +1136,8 @@
1132
1136
 
1133
1137
  "ink/cli-boxes": ["cli-boxes@3.0.0", "", {}, "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g=="],
1134
1138
 
1139
+ "ink/react-reconciler": ["react-reconciler@0.32.0", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-2NPMOzgTlG0ZWdIf3qG+dcbLSoAc/uLfOwckc3ofy5sSK0pLJqnQLpUFxvGcN2rlXSjnVtGeeFLNimCQEj5gOQ=="],
1140
+
1135
1141
  "ink/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
1136
1142
 
1137
1143
  "make-dir/semver": ["semver@6.3.1", "", { "bin": "bin/semver.js" }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
@@ -1156,8 +1162,6 @@
1156
1162
 
1157
1163
  "wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
1158
1164
 
1159
- "wrap-ansi/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="],
1160
-
1161
1165
  "xss/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
1162
1166
 
1163
1167
  "yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
@@ -1188,8 +1192,6 @@
1188
1192
 
1189
1193
  "cli-truncate/slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="],
1190
1194
 
1191
- "cli-truncate/string-width/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="],
1192
-
1193
1195
  "cliui/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
1194
1196
 
1195
1197
  "cliui/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
@@ -1208,6 +1210,8 @@
1208
1210
 
1209
1211
  "gaxios/https-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
1210
1212
 
1213
+ "ink/react-reconciler/scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="],
1214
+
1211
1215
  "pdfjs-dist/canvas/simple-get": ["simple-get@3.1.1", "", { "dependencies": { "decompress-response": "^4.2.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA=="],
1212
1216
 
1213
1217
  "qrcode/yargs/cliui": ["cliui@6.0.0", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^6.2.0" } }, "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ=="],
@@ -1224,8 +1228,6 @@
1224
1228
 
1225
1229
  "wide-align/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
1226
1230
 
1227
- "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
1228
-
1229
1231
  "yargs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
1230
1232
 
1231
1233
  "yargs/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
@@ -1240,8 +1242,6 @@
1240
1242
 
1241
1243
  "@inquirer/core/wrap-ansi/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
1242
1244
 
1243
- "cli-truncate/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
1244
-
1245
1245
  "pdfjs-dist/canvas/simple-get/decompress-response": ["decompress-response@4.2.1", "", { "dependencies": { "mimic-response": "^2.0.0" } }, "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw=="],
1246
1246
 
1247
1247
  "qrcode/yargs/cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
@@ -1264,12 +1264,6 @@
1264
1264
 
1265
1265
  "qrcode/yargs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
1266
1266
 
1267
- "qrcode/yargs/cliui/wrap-ansi/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
1268
-
1269
1267
  "qrcode/yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
1270
-
1271
- "qrcode/yargs/cliui/wrap-ansi/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
1272
-
1273
- "qrcode/yargs/cliui/wrap-ansi/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
1274
1268
  }
1275
1269
  }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "ummaya",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "ummaya",
9
- "version": "0.2.0",
9
+ "version": "0.2.2",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "@alcalzone/ansi-tokenize": "^0.3.0",
@@ -69,6 +69,7 @@
69
69
  "proper-lockfile": "^4.1.2",
70
70
  "qrcode": "^1.5.4",
71
71
  "react": "^19.2.0",
72
+ "react-reconciler": "0.33.0",
72
73
  "semver": "^7.7.4",
73
74
  "shell-quote": "^1.8.3",
74
75
  "supports-hyperlinks": "^4.4.0",
@@ -4990,6 +4991,27 @@
4990
4991
  "url": "https://github.com/sponsors/sindresorhus"
4991
4992
  }
4992
4993
  },
4994
+ "node_modules/ink/node_modules/react-reconciler": {
4995
+ "version": "0.32.0",
4996
+ "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.32.0.tgz",
4997
+ "integrity": "sha512-2NPMOzgTlG0ZWdIf3qG+dcbLSoAc/uLfOwckc3ofy5sSK0pLJqnQLpUFxvGcN2rlXSjnVtGeeFLNimCQEj5gOQ==",
4998
+ "license": "MIT",
4999
+ "dependencies": {
5000
+ "scheduler": "^0.26.0"
5001
+ },
5002
+ "engines": {
5003
+ "node": ">=0.10.0"
5004
+ },
5005
+ "peerDependencies": {
5006
+ "react": "^19.1.0"
5007
+ }
5008
+ },
5009
+ "node_modules/ink/node_modules/scheduler": {
5010
+ "version": "0.26.0",
5011
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
5012
+ "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==",
5013
+ "license": "MIT"
5014
+ },
4993
5015
  "node_modules/ink/node_modules/signal-exit": {
4994
5016
  "version": "3.0.7",
4995
5017
  "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
@@ -6294,18 +6316,18 @@
6294
6316
  }
6295
6317
  },
6296
6318
  "node_modules/react-reconciler": {
6297
- "version": "0.32.0",
6298
- "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.32.0.tgz",
6299
- "integrity": "sha512-2NPMOzgTlG0ZWdIf3qG+dcbLSoAc/uLfOwckc3ofy5sSK0pLJqnQLpUFxvGcN2rlXSjnVtGeeFLNimCQEj5gOQ==",
6319
+ "version": "0.33.0",
6320
+ "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.33.0.tgz",
6321
+ "integrity": "sha512-KetWRytFv1epdpJc3J4G75I4WrplZE5jOL7Yq0p34+OVOKF4Se7WrdIdVC45XsSSmUTlht2FM/fM1FZb1mfQeA==",
6300
6322
  "license": "MIT",
6301
6323
  "dependencies": {
6302
- "scheduler": "^0.26.0"
6324
+ "scheduler": "^0.27.0"
6303
6325
  },
6304
6326
  "engines": {
6305
6327
  "node": ">=0.10.0"
6306
6328
  },
6307
6329
  "peerDependencies": {
6308
- "react": "^19.1.0"
6330
+ "react": "^19.2.0"
6309
6331
  }
6310
6332
  },
6311
6333
  "node_modules/readable-stream": {
@@ -6450,9 +6472,9 @@
6450
6472
  "license": "MIT"
6451
6473
  },
6452
6474
  "node_modules/scheduler": {
6453
- "version": "0.26.0",
6454
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
6455
- "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==",
6475
+ "version": "0.27.0",
6476
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
6477
+ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
6456
6478
  "license": "MIT"
6457
6479
  },
6458
6480
  "node_modules/semver": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ummaya",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Conversational multi-agent harness for Korean public-service channels",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -129,6 +129,7 @@
129
129
  "proper-lockfile": "^4.1.2",
130
130
  "qrcode": "^1.5.4",
131
131
  "react": "^19.2.0",
132
+ "react-reconciler": "0.33.0",
132
133
  "semver": "^7.7.4",
133
134
  "shell-quote": "^1.8.3",
134
135
  "supports-hyperlinks": "^4.4.0",
package/pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ummaya"
3
- version = "0.2.0"
3
+ version = "0.2.2"
4
4
  description = "Conversational multi-agent platform for Korean public APIs"
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -314,7 +314,7 @@ min_confidence = 80
314
314
 
315
315
  [tool.commitizen]
316
316
  name = "cz_conventional_commits"
317
- version = "0.2.0"
317
+ version = "0.2.2"
318
318
  tag_format = "v$version"
319
319
 
320
320
  # PyTorch CPU-only wheel for Docker image size discipline (SC-1: ≤ 2 GB).
@@ -75,17 +75,6 @@ def _contains_location_dependent_key(value: object) -> bool:
75
75
  return False
76
76
 
77
77
 
78
- def _allowed_core_tools_for_available_adapters(
79
- visible_primitives: set[str],
80
- has_location_dependent_schema: bool,
81
- ) -> frozenset[str] | None:
82
- """Constrain root primitive exposure for location-independent find turns."""
83
-
84
- if visible_primitives == {"find"} and not has_location_dependent_schema:
85
- return frozenset({"find"})
86
- return None
87
-
88
-
89
78
  class QueryEngine:
90
79
  """Per-session orchestrator for the UMMAYA query engine.
91
80
 
@@ -216,7 +205,7 @@ class QueryEngine:
216
205
  ChatMessage(role="user", content=assembled.turn_attachment.content),
217
206
  )
218
207
 
219
- dynamic_adapter_message, allowed_core_tool_ids = self._build_available_adapters_context(
208
+ dynamic_adapter_message, turn_tool_ids = self._build_available_adapters_context(
220
209
  user_message
221
210
  )
222
211
  if dynamic_adapter_message is not None:
@@ -235,7 +224,7 @@ class QueryEngine:
235
224
  tool_registry=self._tool_registry,
236
225
  config=self._config,
237
226
  session_context=self._permission_session,
238
- allowed_core_tool_ids=allowed_core_tool_ids,
227
+ turn_tool_ids=turn_tool_ids,
239
228
  turn_start_message_index=turn_start_message_index,
240
229
  )
241
230
 
@@ -281,13 +270,13 @@ class QueryEngine:
281
270
  def _build_available_adapters_message(self, user_message: str) -> ChatMessage | None:
282
271
  """Inject BM25 adapter candidates for the current citizen utterance."""
283
272
 
284
- message, _allowed_core_tool_ids = self._build_available_adapters_context(user_message)
273
+ message, _turn_tool_ids = self._build_available_adapters_context(user_message)
285
274
  return message
286
275
 
287
276
  def _build_available_adapters_context(
288
277
  self, user_message: str
289
- ) -> tuple[ChatMessage | None, frozenset[str] | None]:
290
- """Build dynamic adapter context and per-turn primitive exposure."""
278
+ ) -> tuple[ChatMessage | None, tuple[str, ...]]:
279
+ """Build dynamic adapter context and per-turn concrete tool exposure."""
291
280
 
292
281
  try:
293
282
  from ummaya.tools.search import search # noqa: PLC0415
@@ -300,11 +289,10 @@ class QueryEngine:
300
289
  )
301
290
  except Exception: # noqa: BLE001
302
291
  logger.exception("available_adapters auto-inject failed")
303
- return None, None
292
+ return None, ()
304
293
 
305
294
  adapter_lines: list[str] = []
306
- visible_primitives: set[str] = set()
307
- has_location_dependent_schema = False
295
+ selected_tool_ids: list[str] = []
308
296
  primary_find_without_location = False
309
297
  visible_count = 0
310
298
  for candidate in candidates:
@@ -312,6 +300,8 @@ class QueryEngine:
312
300
  tool = self._tool_registry.find(candidate.tool_id)
313
301
  except ToolNotFoundError:
314
302
  continue
303
+ if candidate.score <= 0:
304
+ continue
315
305
  if tool.is_core or tool.ministry == "UMMAYA":
316
306
  continue
317
307
  primitive = candidate.primitive if isinstance(candidate.primitive, str) else None
@@ -324,15 +314,12 @@ class QueryEngine:
324
314
  )
325
315
  if visible_count > 0 and primary_find_without_location and requires_location:
326
316
  continue
327
- if primitive is not None:
328
- visible_primitives.add(primitive)
329
- if requires_location:
330
- has_location_dependent_schema = True
331
317
  schema_json = json.dumps(
332
318
  candidate.input_schema_json,
333
319
  ensure_ascii=False,
334
320
  sort_keys=True,
335
321
  )
322
+ selected_tool_ids.append(candidate.tool_id)
336
323
  adapter_lines.extend(
337
324
  [
338
325
  f"- tool_id: {candidate.tool_id}",
@@ -340,8 +327,7 @@ class QueryEngine:
340
327
  f" description: {candidate.llm_description or tool.name_ko}",
341
328
  f" required_params: {candidate.required_params}",
342
329
  f" input_schema_json: {schema_json}",
343
- f" call_hint: {candidate.primitive}("
344
- f'{{"tool_id":"{candidate.tool_id}","params":{{...}}}})',
330
+ f" call_hint: {candidate.tool_id}({{...}})",
345
331
  f" policy_url: {candidate.real_classification_url or ''}",
346
332
  ]
347
333
  )
@@ -350,19 +336,16 @@ class QueryEngine:
350
336
  break
351
337
 
352
338
  if not adapter_lines:
353
- return None, None
354
-
355
- allowed_core_tool_ids = _allowed_core_tools_for_available_adapters(
356
- visible_primitives,
357
- has_location_dependent_schema,
358
- )
339
+ return None, ()
359
340
 
360
341
  content = "\n".join(
361
342
  [
362
343
  "<available_adapters>",
363
344
  "Use these adapter candidates for this citizen request. "
364
- "For public-data lookup/list/statistics requests, call "
365
- "find({tool_id, params}) with a tool_id from this block. "
345
+ "Call the function named exactly as tool_id with that adapter's "
346
+ "schema arguments. Do not wrap adapter calls in root primitives "
347
+ "such as find({tool_id, params}), locate({tool_id, params}), "
348
+ "check({tool_id, params}), or send({tool_id, params}). "
366
349
  "Do not call locate just because the citizen text contains a "
367
350
  "city/province name; treat that as the dataset/filter term. "
368
351
  "Call locate only when the selected adapter schema requires "
@@ -371,7 +354,7 @@ class QueryEngine:
371
354
  "</available_adapters>",
372
355
  ]
373
356
  )
374
- return ChatMessage(role="system", content=content), allowed_core_tool_ids
357
+ return ChatMessage(role="system", content=content), tuple(selected_tool_ids)
375
358
 
376
359
  def set_permission_session(self, session: SessionContext | None) -> None:
377
360
  """Update the permission-pipeline session used for subsequent turns.
@@ -121,11 +121,17 @@ class QueryContext(BaseModel):
121
121
  """
122
122
 
123
123
  allowed_core_tool_ids: frozenset[str] | None = None
124
- """Optional per-turn provider tool allow-list for root primitives.
124
+ """Legacy per-turn allow-list for primitive wrappers.
125
125
 
126
- Used by the Rich REPL path when BM25 has already selected location-independent
127
- public-data adapters. It keeps the exposed provider surface aligned with the
128
- selected adapter primitive instead of letting unrelated root primitives race.
126
+ Preserved for callers that still expose the old root primitives. New turns
127
+ should prefer ``turn_tool_ids`` so the model sees concrete adapter schemas.
128
+ """
129
+
130
+ turn_tool_ids: tuple[str, ...] = ()
131
+ """Concrete adapter tool IDs selected for this citizen turn.
132
+
133
+ When populated, the query loop exports these concrete adapter schemas as the
134
+ provider tool surface instead of dumping the root primitive wrappers.
129
135
  """
130
136
 
131
137
  turn_start_message_index: int = 0