kira-wallet 0.1.5 → 0.1.7

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 CHANGED
@@ -43,12 +43,11 @@ For a viewer without wallet or research actions, use `kira start --read-only`.
43
43
  Prefer an archive? [GitHub releases](https://github.com/realproject7/kira-wallet/releases/tag/v0.1.2)
44
44
  include the same package and its checksum.
45
45
 
46
- 1. **Set up discovery.** In **Workspace settings**, connect an indexer for broad
47
- ERC20 discovery. Public RPC alone has limited discovery; incomplete coverage
46
+ 1. **Review optional coverage.** Free research is enabled by default. In **Workspace settings**, connect Alchemy for broader
47
+ ERC20 discovery. Free token sources have limited coverage; incomplete coverage
48
48
  stays visible. An AI or browser wallet connection does not enable an indexer.
49
49
  2. **Add a watching wallet.** Paste a public address and give it a name. Add more
50
- wallets whenever you need them. Without discovery, the form explicitly
51
- offers limited research of native and Mint Club assets.
50
+ wallets whenever you need them. The form starts free research of native, common tokens and Mint Club assets, with keyless discovery on supported networks. First balances appear before detailed research finishes.
52
51
  3. **Connect your AI.** Install and sign in to the
53
52
  [Codex CLI](https://developers.openai.com/codex/cli) or
54
53
  [Claude Code](https://code.claude.com/docs/en/quickstart), then choose your
@@ -140,3 +139,14 @@ From source, use `node bin/kira.cjs` in place of `kira`.
140
139
  Original code and Kira character assets are available under the [MIT license](LICENSE).
141
140
  Third-party artwork and references retain their own terms; see
142
141
  [Third-party notices](THIRD_PARTY_NOTICES.md).
142
+
143
+ ### Token coverage and optional Alchemy setup
144
+
145
+ Kira uses free public RPC with bounded fallback and checks a selected common-token
146
+ list alongside native assets and Mint Club. Other ERC20 holdings can be missing.
147
+ Incomplete results show recovery guidance on Home and the wallet page. Connect
148
+ your own Alchemy account for broader discovery on supported networks, then
149
+ refresh holdings. Public RPC remains the first choice for ordinary reads;
150
+ Alchemy is a backup and supplies the dedicated token inventory API. See the
151
+ [step-by-step Alchemy guide](docs/ALCHEMY_SETUP.md) and
152
+ [RPC behavior](docs/FREE_RPC.md).
@@ -28,3 +28,9 @@ identifies the OWS wallet-creation integration without implying endorsement.
28
28
  Its retrieval date and source digest are in `site/assets/ows-artwork-source.json`.
29
29
 
30
30
  CASHCAT, BLAST and USDB artwork in the public reconstructed demo comes from lpTOKEN.fun token metadata, DEX Screener and CoinGecko. Token marks belong to their respective owners and identify the assets without implying affiliation or endorsement. The source URLs and chain-specific contract identities are recorded in `site/assets/token-artwork-sources.json`.
31
+
32
+ The factual token inventory in `sources/known-tokens.json` includes selected
33
+ chain/contract identities from Uniswap default-token-list at commit
34
+ 583cb696cc15778536fe0ddd416de5bd81be2023. The upstream references retain
35
+ GPL-3.0 terms in `sources/TOKEN_LIST_LICENSE.txt`. Per-contract provenance is
36
+ recorded in the catalog. No upstream executable code or artwork is included.
@@ -60,3 +60,16 @@ through the user's CLI; local storage does not imply an offline model.
60
60
 
61
61
  See [the conversation contract](ONBOARDING_CHAT_CONTRACT.md).
62
62
  The standalone stdio MCP adapter above remains read-only and separate.
63
+
64
+ Native responses select the final Codex agent message. Older verified event
65
+ streams without a phase use their last agent message. Commentary cannot become
66
+ a tool request by concatenation. A mixed or malformed request gets one bounded
67
+ correction; no tool executes from it and repeated malformed protocol fails
68
+ without adding raw JSON to conversation history. Turn receipts expose only
69
+ bounded tool names and completed/failed outcomes.
70
+
71
+ Job reads include analysis phase, parent/continuation IDs and real progress
72
+ events. Normalized chain discovery completeness is separate from a job's
73
+ on-chain checkpoint status. Deferred candidates and pending networks are not
74
+ failed reads or zero holdings. Historical observations cannot establish current
75
+ execution, transfers or realized profit.
@@ -0,0 +1,95 @@
1
+ # Connect Alchemy to Kira
2
+
3
+ Kira works with free public RPC by default. It checks native assets, selected
4
+ common token contracts and the Mint Club registry. Other ERC20 holdings may
5
+ be missing. For broader token discovery, connect your own Alchemy account.
6
+ Kira runs these requests locally and does not operate a hosted relay.
7
+
8
+ ## Create an Alchemy app
9
+
10
+ 1. Sign in to the [Alchemy dashboard](https://dashboard.alchemy.com/).
11
+ A new account may already have a default app. Use it, or open your team
12
+ menu, Team Overview, Apps, then Create new app.
13
+ 2. Name the app and select the mainnets you want to research. Check that the
14
+ app has Node API and Portfolio API access for those networks. Create it.
15
+ 3. Copy the app API key. The Endpoints tab shows its network URLs. Kira's
16
+ combined Alchemy setup needs the key, so you do not need to copy each URL.
17
+
18
+ These steps follow Alchemy's [official API key guide](https://www.alchemy.com/docs/create-an-api-key),
19
+ checked on October 9, 2026. Dashboard wording can change.
20
+
21
+ ## Connect the key locally
22
+
23
+ Create a private directory and file in your terminal:
24
+
25
+ ```sh
26
+ mkdir -p ~/.config/kira
27
+ touch ~/.config/kira/alchemy.env
28
+ chmod 600 ~/.config/kira/alchemy.env
29
+ ```
30
+
31
+ Open the file in a local text editor. On macOS, you can use:
32
+
33
+ ```sh
34
+ open -e ~/.config/kira/alchemy.env
35
+ ```
36
+
37
+ Add this line, replacing `YOUR_KEY` inside the editor:
38
+
39
+ ```dotenv
40
+ ALCHEMY_API_KEY=YOUR_KEY
41
+ ```
42
+
43
+ Save the file, then run:
44
+
45
+ ```sh
46
+ kira config import-env --file ~/.config/kira/alchemy.env
47
+ ```
48
+
49
+ For a non-default workspace, supply its existing `--data-dir` before `config`.
50
+ Kira stores the file reference. Credentials stay outside the browser, chat and
51
+ published analysis. Do not commit this file to a repository. A process
52
+ environment variable with the same name overrides the file value; check that
53
+ if an old key remains active.
54
+
55
+ ## Check the connection and refresh
56
+
57
+ Open Workspace settings, then Manage data connection. Select Alchemy and use
58
+ "I added my key · check again". Save the connection. Finding the local key
59
+ does not prove the account, API or network is working.
60
+
61
+ For an existing wallet, choose Refresh this wallet's holdings or Refresh
62
+ holdings on its page. A new analysis confirms access and coverage. Old saved
63
+ analyses retain their original observations and warnings. Saving a connection
64
+ does not register a wallet or start research automatically.
65
+
66
+ ## What uses the key
67
+
68
+ Ordinary balances prefer public RPC. After two failed or unavailable public
69
+ providers, Kira tries the configured custom backup within the read deadline.
70
+ Advanced settings can choose custom first or require custom only. Existing
71
+ explicit custom-only restrictions remain in force.
72
+
73
+ Alchemy token discovery uses the key directly. Standard public RPC cannot
74
+ enumerate arbitrary ERC20 holdings. Kira uses Alchemy's
75
+ [Tokens By Wallet endpoint](https://www.alchemy.com/docs/data/portfolio-apis/portfolio-api-endpoints/portfolio-api-endpoints/get-tokens-by-address),
76
+ then verifies discovered candidates with on-chain balance reads.
77
+
78
+ ## If tokens are still missing
79
+
80
+ - **Local key unavailable:** confirm the file exists, its variable name matches
81
+ the connection settings, and you ran the import command in the right workspace.
82
+ - **Discovery failed:** check the app's enabled APIs and networks and Alchemy's
83
+ usage dashboard. Correct access or limits, then retry holdings.
84
+ - **Some balances could not be checked:** wait briefly and retry. Public and
85
+ custom providers both have outages and limits. Review Advanced read preferences
86
+ if a custom-only endpoint is unavailable.
87
+ - **Unsupported network:** broader discovery is unavailable there. Connecting
88
+ Alchemy cannot cover every network or every token. Known-contract reads remain
89
+ useful, and missing holdings remain unknown.
90
+
91
+ Alchemy has account allowances and throughput limits. Check the current
92
+ [plans](https://www.alchemy.com/docs/reference/pricing-plans) and API availability
93
+ before selecting a plan. Kira does not create an account, subscribe or upgrade
94
+ it. Public-first balance reads reduce ordinary use of the key; discovery still
95
+ consumes Alchemy allowance. No connection guarantees a complete token inventory.
package/docs/CLI.md CHANGED
@@ -10,7 +10,7 @@ npm install -g kira-wallet
10
10
  kira setup
11
11
  ```
12
12
 
13
- The operator published npm version 0.1.4. Version 0.1.5 is a preparation candidate. GitHub v0.1.3 remains
13
+ The operator published npm version 0.1.6. Version 0.1.7 is a preparation candidate. GitHub v0.1.3 remains
14
14
  a draft; the previous archive and checksum are available from the
15
15
  [0.1.2 GitHub release](https://github.com/realproject7/kira-wallet/releases/tag/v0.1.2).
16
16
  Registry publication remains an operator action. The code and original character assets use the MIT license.
@@ -46,9 +46,9 @@ Published 0.1.4 explains limited discovery before wallet registration and beside
46
46
  holdings. Missing coverage keeps the portfolio estimate partial. Existing snapshots require a holdings refresh after connecting
47
47
  an indexer; a price refresh does not discover missing tokens.
48
48
 
49
- Public RPC checks known assets and Mint Club. For broader ERC20 discovery,
50
- open Data provider connection, select Alchemy and follow its local key guide.
51
- The 0.1.5 candidate uses one save for Alchemy RPC and discovery; chain overrides
49
+ Free research checks known assets, Mint Club and keyless token candidates on supported networks. Alchemy is optional. For broader ERC20 discovery,
50
+ open Custom RPC & token coverage, select Alchemy and follow its local key guide.
51
+ Published 0.1.5 uses one save for Alchemy RPC and discovery; chain overrides
52
52
  remain under Advanced. Never paste the key into the browser. `kira doctor`
53
53
  checks whether the configured key is available. Configuration alone does not
54
54
  prove complete coverage; inspect the recorded results and gaps.
@@ -68,7 +68,7 @@ authentication. Verified adapter versions are Codex 0.158.x and Claude Code
68
68
  verified. This is a native CLI connection, with no Kira account, API-key proxy or
69
69
  hosted portfolio backend.
70
70
 
71
- In the 0.1.5 candidate, setup opens the existing Account / Permissions / Ready
71
+ From published 0.1.5, setup opens the existing Account / Permissions / Ready
72
72
  modal directly. Choose Codex or Claude, then Use default model or a readable
73
73
  model choice. Codex choices come from bounded native metadata; Claude offers
74
74
  Sonnet, Opus and Haiku aliases. A catalog is not proof of account access.
@@ -212,3 +212,12 @@ in session storage. It clears password fields immediately. Recovery keeps the
212
212
  original encryption passphrase. No signing, key export or account deletion
213
213
  endpoint is exposed. OWS absence does not block ordinary address or browser
214
214
  wallet onboarding.
215
+
216
+ ## Free reads and broader token coverage
217
+
218
+ Public RPC is preferred for ordinary reads, including when a custom backup is
219
+ configured. Advanced settings can select custom first or custom only. For CLI
220
+ configuration, use `kira rpc set --chain 8453 --url-env KIRA_BASE_RPC --priority
221
+ public_first`. Existing custom-only choices remain unchanged. Alchemy token
222
+ discovery uses the configured key directly. See [free RPC behavior](FREE_RPC.md)
223
+ and the [Alchemy setup guide](ALCHEMY_SETUP.md).
@@ -0,0 +1,112 @@
1
+ # Free RPC reads and token coverage
2
+
3
+ Kira contacts public providers directly from the local research process. No
4
+ Kira relay or shared credential is required. Free public services are best
5
+ effort and can throttle requests or omit historical state.
6
+
7
+ The reviewed default catalog adds alternatives on Ethereum, Base, BNB Chain,
8
+ Polygon, Arbitrum and Optimism. PublicNode is the initial preference. Reserves
9
+ are selected per chain rather than downloaded from an untrusted runtime list.
10
+ The other supported networks retain their existing endpoints.
11
+
12
+ ## Request behavior
13
+
14
+ - Chain identity is verified once per endpoint session and again after recovery.
15
+ - One request runs per endpoint. Provider families share one queue and a
16
+ conservative two-second interval across chains. Two chain scans run at once.
17
+ - A logical read has a twelve-second deadline including queues and verification.
18
+ The preferred HTTP attempt has four seconds, reserves at most two seconds.
19
+ Public attempts also have a total budget including verification and pacing,
20
+ leaving half the deadline for a configured personal backup.
21
+ Real fetches are aborted when a deadline expires. HTTP automatic retries are off.
22
+ - At most four endpoint attempts run for a read. Public-first configurations
23
+ reserve an attempt for the personal backup after two public providers.
24
+ - HTTP 429 and JSON-RPC backpressure cool an endpoint for at least sixty seconds.
25
+ Consecutive throttles increase this to at most thirty minutes. Retry-After
26
+ can extend the wait. Other transport failures have a short cooldown.
27
+ - Unsupported methods have a method-specific penalty. Contract reverts do not
28
+ cool an otherwise healthy endpoint. Oversized Multicalls can split; outages and
29
+ throttling cannot create a burst of smaller calls.
30
+
31
+ These are application limits, not promises about a provider's quota or uptime.
32
+ Cooldown observations survive sequential research subprocesses in a bounded
33
+ local cache. Custom endpoints and credentialed URLs are not written there.
34
+ Status screens and onboarding checks do not probe RPC providers.
35
+
36
+ ## Snapshot consistency
37
+
38
+ Each chain analysis records a block number and hash. Balance and contract reads
39
+ keep that block. Before a fallback provider reads it, Kira verifies its header
40
+ has the same hash. Every evidence read then uses an EIP-1898 block-hash selector
41
+ with canonical checking. Providers that do not support it cannot supply that
42
+ snapshot's evidence. Conflicting or unavailable evidence stays incomplete.
43
+ No fallback silently substitutes `latest` for a fixed block.
44
+
45
+ Identical concurrent reads share work. Successful fixed-block reads have a
46
+ bounded cache keyed by chain, pinned hash, method and complete parameters,
47
+ including the wallet address. Failed reads are evicted and never become zero.
48
+ Wallet balances are not reused across wallets or new research processes.
49
+ Registry identity caching remains separate from balances.
50
+
51
+ ## Default token inventory
52
+
53
+ Native balances and the selected issuer/deployment-backed contracts in
54
+ `sources/known-tokens.json` are checked before cold Mint Club enumeration.
55
+ The catalog has 57 chain/contract identities on eight mainnets. Issuer references
56
+ and selected Uniswap token-list records retain per-contract provenance. The
57
+ upstream list commit and its license are bundled; no untrusted runtime list is
58
+ loaded. Decimals and quantities come from on-chain reads. This remains a bounded
59
+ inventory, not a claim to discover every wrapped or arbitrary token.
60
+
61
+ Default research also uses Routescan without a key on individually verified
62
+ Ethereum, Avalanche and Blast routes. Its documented free access is limited to
63
+ two requests per second and 10,000 calls per day. Kira starts at most one call
64
+ per second, reads at most 20 pages of 100 candidates per chain and spends at most
65
+ 25 seconds per enumeration. An isolated HTTP reader enforces a total deadline,
66
+ including DNS and response bodies. Unsafe pagination, outages, malformed data
67
+ and limits keep coverage partial. Provider quantities and prices never replace
68
+ RPC-verified balances. Other Routescan chains are not presumed supported.
69
+ Explicit keyless opt-outs and custom-only preferences remain respected.
70
+
71
+ Initial reports check Base, Ethereum and Blast, up to 128 candidates per chain.
72
+ Selected common contracts take priority. Remaining candidates and networks are
73
+ explicitly pending. A durable background continuation scans every configured
74
+ network, the full Mint Club registry and markets. Registry identity prefixes
75
+ are saved incrementally; balances are never shared between wallets. Batches
76
+ contain at most 128 reads, with splitting only for verified payload limits.
77
+ New initial work takes priority over detailed research. Older same-wallet
78
+ continuations cannot replace newer evidence.
79
+
80
+ Public RPC cannot enumerate all arbitrary ERC20 contracts. Alchemy is an
81
+ optional broader candidate source and backup RPC. Discovery calls made through
82
+ the wallet engine have a 25-second initial or 60-second detailed per-chain
83
+ budget. Native balances, free candidates and Mint Club research remain usable
84
+ without it. Missing or deferred evidence stays unknown. Persistent coverage
85
+ notices distinguish pending work from failed reads and offer retry or optional
86
+ [Alchemy setup](ALCHEMY_SETUP.md).
87
+
88
+ Keyless sources: [Routescan addresses](https://routescan.io/docs/api/addresses),
89
+ [API access](https://routescan.io/docs/plans-and-limits/api-keys-and-pricing),
90
+ [rate limits](https://routescan.io/docs/plans-and-limits/rate-limits).
91
+
92
+ ## Read preferences
93
+
94
+ Public first is the default even when a custom connection is configured.
95
+ Advanced settings provide custom first and custom only. Configurations without
96
+ the new priority field use public first; an existing explicit custom-only choice
97
+ continues to exclude public endpoints. RPC priority does not change the dedicated
98
+ Alchemy discovery request.
99
+
100
+ CLI example using an existing local endpoint variable:
101
+
102
+ ```sh
103
+ kira rpc set --chain 8453 --url-env KIRA_BASE_RPC --priority public_first
104
+ ```
105
+
106
+ Primary references: [EIP-1898](https://eips.ethereum.org/EIPS/eip-1898),
107
+ [PublicNode](https://www.publicnode.com/),
108
+ [dRPC limits](https://drpc.org/docs/howitworks/ratelimiting),
109
+ [1RPC networks](https://docs.1rpc.io/overview/supported-networks),
110
+ [Circle contracts](https://developers.circle.com/stablecoins/usdc-contract-addresses),
111
+ [Tether contracts](https://tether.to/en/supported-protocols/),
112
+ [Uniswap Arbitrum deployments](https://developers.uniswap.org/docs/protocols/v3/deployments/v3-arbitrum-deployments).
@@ -121,3 +121,27 @@ its projection receipt without another provider request.
121
121
 
122
122
  The read-only stdio MCP adapter uses the same job and snapshot service. It has
123
123
  no mutation tools and requires separate operator-approved client setup.
124
+
125
+ ## Initial evidence and continuations in 0.1.7
126
+
127
+ A valid wallet admission records its identity under a short registry lock.
128
+ Idempotent replay repairs missing admission without undoing later name edits.
129
+ New analysis jobs have `analysis_phase: baseline`. The first usable chain
130
+ publishes an immutable `initial/` snapshot; the running job can reference it
131
+ without claiming that the job is finished. The baseline publishes its own final
132
+ partial snapshot and admits exactly one durable `enrichment` continuation with
133
+ `parent_job_id`. The parent records `continuation_id`.
134
+
135
+ Baseline work runs before enrichment. New baseline admission can preempt a
136
+ running continuation. Saved chain evidence and verified registry identity
137
+ prefixes survive; an interrupted registry batch is read again. A newer request
138
+ for the same wallet supersedes older detailed work. Generation checks prevent
139
+ an older result from replacing the latest pointer, while preserving historical
140
+ evidence. Explicit cancellation of baseline prevents automatic continuation.
141
+
142
+ `onchain_progress` events identify the actual operation, chain, checked count
143
+ and known total. `chain.status: complete` concerns the on-chain checkpoint.
144
+ Normalized wallet `complete` concerns general token discovery. Registry coverage,
145
+ candidate failures, deferred candidates and pending RPC reads are separate facts.
146
+ These flags do not prove exhaustive holdings. First results can be read while
147
+ background work remains active.
package/docs/RELEASE.md CHANGED
@@ -44,19 +44,20 @@ RPC failures show affected networks, retry guidance and a settings action.
44
44
  Failed candidate balance reads keep the saved result and valuation partial
45
45
  even when general token discovery succeeded.
46
46
 
47
- The next candidate is **0.1.5**. It opens the existing setup modal directly,
48
- adds readable Codex and Claude model choices, and exposes model and permission
49
- changes in Workspace settings. New setup recommends local history; upgrades
50
- preserve explicit opt-outs. Watching wallet methods use individual buttons.
51
- Data provider connection offers one atomic Alchemy RPC and discovery save,
52
- with custom overrides under Advanced. A missing key keeps prior settings.
53
- The wallet draft survives a connection detour. Connecting still requires an
54
- explicit holdings refresh before missing assets can appear in saved evidence.
55
-
56
- Version 0.1.5 requires independent review and operator registry publication.
57
- It is not yet available from the registry. Keep published 0.1.4 and earlier
58
- package bytes immutable. The showcase remains a separate deployment.
59
- See [discovery acceptance](KIRA_DISCOVERY_ACCEPTANCE.md).
47
+ Published **0.1.5** opens the existing setup modal directly, adds Codex and
48
+ Claude model choices, exposes model and permission changes in Workspace settings,
49
+ and provides one Alchemy connection save. Its registry version was confirmed on
50
+ 2026-10-09. Published package bytes remain immutable.
51
+
52
+ Published **0.1.6** adds a paced local RPC pool, core-chain public
53
+ alternatives, public-first custom backup, fixed-block hash checks and a small
54
+ known-token catalog. Home and wallet pages display incomplete coverage with an
55
+ Alchemy setup action. The settings guide explains account setup, private local
56
+ key import and an explicit holdings refresh. Missing or malformed discovery data
57
+ cannot become a completed empty inventory.
58
+
59
+ Version 0.1.6 is published and immutable. The showcase remains a separate deployment. See
60
+ [free RPC behavior](FREE_RPC.md) and [Alchemy setup](ALCHEMY_SETUP.md).
60
61
 
61
62
  The operator published npm **0.1.2** on 2026-10-05. Its registry archive was
62
63
  downloaded and matched the reviewed GitHub archive above, including SHA-256,
@@ -78,7 +79,7 @@ The earlier unpublished 0.1.2 candidate is preserved in the ignored
78
79
  `dist/preserved-0.1.2-bad8f02/` directory. Use the current `dist/release.json`
79
80
  and its matching archive for publication; do not mix manifests from candidates.
80
81
 
81
- Keep GitHub v0.1.1, its tag and its released archive immutable. Versions 0.1.2, 0.1.3 and 0.1.4 are also published and immutable. Do not rebuild them from later documentation
82
+ Keep GitHub v0.1.1, its tag and its released archive immutable. Versions 0.1.2, 0.1.3, 0.1.4, 0.1.5 and 0.1.6 are also published and immutable. Do not rebuild them from later documentation
82
83
  commits. Future package changes need a new version. Release preparation does not
83
84
  publish a GitHub release, merge PR 19, promote the website or publish to npm.
84
85
  Registry publication remains operator-only. The coding agent never publishes.
@@ -93,18 +94,38 @@ python3 scripts/prepare-release.py
93
94
  ```
94
95
 
95
96
  `prepare-release.py` scans the package boundary and creates
96
- `dist/kira-wallet-0.1.5.tgz`. The ignored `dist/release.json` records its source
97
+ `dist/kira-wallet-0.1.7.tgz`. The ignored `dist/release.json` records its source
97
98
  commit, SHA-256 and member count. It never publishes. After reviewing that
98
99
  manifest, the operator's final command from the project root is:
99
100
 
100
101
  ```sh
101
- npm publish ./dist/kira-wallet-0.1.5.tgz --access public
102
+ npm publish ./dist/kira-wallet-0.1.7.tgz --access public
102
103
  ```
103
104
 
104
105
  Use the operator's npm account and complete authentication or OTP personally.
105
106
  After publication, verify the registry archive and perform a fresh install
106
107
  before marking the new onboarding flow as available to npm users.
107
108
 
109
+ ## Proposed 0.1.7 behavior
110
+
111
+ The candidate enables useful keyless research by default, expands the curated
112
+ contract inventory and separates first recorded balances from exhaustive
113
+ background research. Pending candidates, registry work and other networks stay
114
+ visible. Current wallet evidence cannot roll back when an older continuation
115
+ resumes. See [coverage and limits](FREE_RPC.md).
116
+
117
+ Codex uses the final native message rather than joined commentary. Mixed tool
118
+ protocol receives one bounded correction and is never rendered as a completed
119
+ answer. Claude retains its restricted native adapter. Tool receipts contain
120
+ names and outcomes, without raw arguments. Job checkpoints and normalized token
121
+ discovery flags have separate meanings. Imported connection references reconcile
122
+ with untouched settings fields while preserving edited drafts.
123
+
124
+ The next archive is **0.1.7**, unpublished until the operator runs the command.
125
+ Release readiness requires the repository suite, clean installation, source and
126
+ history privacy scans, independent review and the frozen archive manifest.
127
+ No live-extension or human passphrase acceptance is inferred from these checks.
128
+
108
129
  ## Verified 0.1.0 publication
109
130
 
110
131
  The operator published 0.1.0, verified on 2026-10-04. The registry archive has
package/kira_agent.py CHANGED
@@ -42,7 +42,7 @@ SYSTEM = ('You are Kira, a careful wallet research partner. Answer in the langua
42
42
  'and full-balance output only when a validated quote exists. DEX pool TVL is not a sell quote. '
43
43
  'Missing execution quotes mean sale proceeds, fees, gas and price impact are unknown. '
44
44
  'For network questions, group by wallet and distinguish known assets from missing coverage. '
45
- 'A chain complete flag means general ERC20 discovery was checked; it does not prove native RPC reads succeeded. An indexer check and unavailable RPC are separate coverage facts, not conflicting records. '
45
+ 'In normalized wallet facts, chain complete means general ERC20 discovery was checked. In job chains, status complete means an on-chain checkpoint finished. These are different stages. Neither flag alone proves all holdings are known. registry_complete and candidate_balance_errors describe separate read coverage. An indexer check and unavailable RPC are separate coverage facts, not conflicting records. '
46
46
  'An explicit chains.native_balance of zero is an observed native balance at its recorded time and block; null is unknown. Empty positive-holding inventories do not erase these observations. '
47
47
  'For saved-analysis comparisons, use snapshots_compare and read both snapshots. coverage_changed compares full evidence, including times and blocks, not just network status flags. price_references_changed may include changed reference values or times, not balances. '
48
48
  'Snapshot reads are normalized evidence. Compare chain observations and snapshot_price_references; never claim these are the only raw fields that changed. '
@@ -140,7 +140,7 @@ def wallet_facts(wallets, *, pool_budget=40_000):
140
140
  for wallet in wallets:
141
141
  row = pick(wallet,('address','tags','name','analysed_at','prices_at','balance_observed_at','known_value_usd','unpriced_count','status'))
142
142
  if not wallet.get('analysed_at'): row['known_value_usd']=None
143
- row['chains'] = [pick(c,('id','name','environment','complete','rpc_available','native_symbol','native_balance','native_observed_at','native_block_number','registry_block_number')) for c in wallet['chains']]
143
+ row['chains'] = [pick(c,('id','name','environment','complete','rpc_available','native_symbol','native_balance','native_observed_at','native_block_number','registry_block_number','discovery_status','registry_complete','registry_phase','rpc_pending','candidate_balance_errors','candidate_deferred')) for c in wallet['chains']]
144
144
  row['snapshot_price_references'] = wallet.get('snapshot_price_references',{})
145
145
  row['assets'] = []
146
146
  for asset in wallet['assets']:
@@ -211,11 +211,14 @@ def run_native(config, prompt, cancel, *, timeout=180):
211
211
  else:
212
212
  events = [json.loads(line) for line in output.splitlines() if line.strip()]
213
213
  if not any(e.get('type') == 'turn.completed' for e in events): raise ValueError()
214
- answer = '\n\n'.join(e['item'].get('text','') for e in events if e.get('type') == 'item.completed' and e.get('item',{}).get('type') == 'agent_message')
214
+ messages = [e['item'] for e in events if e.get('type') == 'item.completed' and e.get('item',{}).get('type') == 'agent_message']
215
+ finals = [m for m in messages if m.get('phase')=='final_answer']
216
+ # Older verified CLIs omit phase. Only their last message is final.
217
+ answer = (finals or [m for m in messages if not m.get('phase')])[-1].get('text','') if finals or any(not m.get('phase') for m in messages) else None
215
218
  if not isinstance(answer,str) or not answer.strip() or len(answer) > 60_000: raise ValueError()
216
219
  return answer.strip()
217
220
  except JobError: raise
218
- except (ValueError,KeyError,subprocess.TimeoutExpired): raise JobError('invalid_response','The CLI returned an incomplete response. Try again or choose another supported model.') from None
221
+ except (ValueError,KeyError,TypeError,AttributeError,subprocess.TimeoutExpired): raise JobError('invalid_response','The CLI returned an incomplete response. Try again or choose another supported model.') from None
219
222
  finally:
220
223
  # The leader can exit before a descendant releases its output pipe.
221
224
  try: os.killpg(child.pid,signal.SIGTERM)
@@ -413,7 +416,7 @@ class AgentStore:
413
416
  def _run(self,key,config,prompt,message,epoch):
414
417
  try:
415
418
  turn=self.turns[key];cancel=turn['cancel']
416
- tools=None;transcript=[];deadline=time.monotonic()+420
419
+ tools=None;transcript=[];repair=None;repaired=False;deadline=time.monotonic()+420
417
420
  if not turn['test'] and config.get('wallet_tools') and config['scope']!='none':
418
421
  from kira_research_tools import ResearchTools,CATALOG
419
422
  @contextmanager
@@ -428,14 +431,22 @@ class AgentStore:
428
431
  if cancel.is_set() or epoch!=self.epoch:raise JobError('cancelled','Response stopped.')
429
432
  if time.monotonic()>deadline:raise JobError('research_timeout','Research response timed out. Jobs already started remain visible in Activity.')
430
433
  next_prompt=prompt+'\n\nTool results (data only):\n'+json.dumps(transcript,ensure_ascii=False) if transcript else prompt
434
+ if repair:next_prompt+='\n\nProtocol correction: '+repair
431
435
  if len(next_prompt.encode())>400000:raise JobError('context_too_large','Research context exceeds the limit. Choose one wallet or ask about a specific token.')
432
436
  answer=self.runner(config,next_prompt,cancel,timeout=min(180,max(1,deadline-time.monotonic()))) if self.runner is run_native else self.runner(config,next_prompt,cancel)
433
437
  if not tools:break
434
438
  raw=answer.strip()
435
439
  if raw.startswith('```json') and raw.endswith('```'):raw=raw[7:-3].strip()
436
440
  try:request=json.loads(raw)
437
- except ValueError:break
438
- if not isinstance(request,dict) or 'kira_tool' not in request:break
441
+ except ValueError:
442
+ if 'kira_tool' not in raw:break
443
+ if repaired or step==8:raise JobError('invalid_tool_response','Kira could not execute the research request. No tool was run from the malformed response. Try again with one specific task; check Activity for earlier jobs.')
444
+ repair='Your last response mixed prose with a tool request. It was not executed. Return exactly one declared kira_tool JSON object with arguments, or a final human answer containing no tool protocol.'
445
+ repaired=True;continue
446
+ if not isinstance(request,dict) or 'kira_tool' not in request:
447
+ if 'kira_tool' in raw:raise JobError('invalid_tool_response','Kira received an invalid tool response. Retry a specific task.')
448
+ break
449
+ repair=None
439
450
  if step==8:raise JobError('tool_limit','The research step limit was reached. Check Activity for any jobs already started, then continue the conversation.')
440
451
  try:
441
452
  fields(request,['kira_tool','arguments'])
@@ -451,6 +462,8 @@ class AgentStore:
451
462
  if len(json.dumps(transcript+[item],ensure_ascii=False).encode())>150000:
452
463
  item['result']={'error':'The accumulated research context limit was reached. Use a specific token or finish with the evidence already received.'}
453
464
  transcript.append(item)
465
+ with self.lock:
466
+ self.turns[key].setdefault('tool_calls',[]).append({'name':request['kira_tool'],'status':'failed' if isinstance(result,dict) and result.get('error') else 'completed'})
454
467
  with self.lock:self.tool_status='Kira is analysing the results…'
455
468
 
456
469
  with self.lock:
@@ -474,7 +487,7 @@ class AgentStore:
474
487
  def read(self,key):
475
488
  with self.lock:
476
489
  if key not in self.turns:raise JobError('not_found','This response is no longer available. Start a new conversation.')
477
- return {**{k:self.turns[key][k] for k in ('id','state','error','answer','test','message','conversation_id')},'tool_status':self.tool_status if self.active==key else None}
490
+ return {**{k:self.turns[key][k] for k in ('id','state','error','answer','test','message','conversation_id')},'tool_calls':self.turns[key].get('tool_calls',[]),'tool_status':self.tool_status if self.active==key else None}
478
491
 
479
492
  def cancel(self,key):
480
493
  with self.lock:
package/kira_cli.py CHANGED
@@ -172,7 +172,7 @@ def main():
172
172
  imp=configsub.add_parser('import-env');imp.add_argument('--file',required=True);imp.add_argument('--key-env',default='ALCHEMY_API_KEY')
173
173
  rpc=sub.add_parser('rpc');rpcsub=rpc.add_subparsers(dest='setting',required=True)
174
174
  rpcsub.add_parser('public')
175
- custom=rpcsub.add_parser('set');custom.add_argument('--chain',type=int,required=True);custom.add_argument('--url-env',required=True);fallback=custom.add_mutually_exclusive_group();fallback.add_argument('--public-fallback',action='store_true',default=None);fallback.add_argument('--custom-only',dest='public_fallback',action='store_false')
175
+ custom=rpcsub.add_parser('set');custom.add_argument('--chain',type=int,required=True);custom.add_argument('--url-env',required=True);custom.add_argument('--priority',choices=['public_first','custom_first']);fallback=custom.add_mutually_exclusive_group();fallback.add_argument('--public-fallback',action='store_true',default=None);fallback.add_argument('--custom-only',dest='public_fallback',action='store_false')
176
176
  discovery=sub.add_parser('discovery');discovery.add_argument('provider',choices=['none','alchemy']);discovery.add_argument('--key-env',default='ALCHEMY_API_KEY');discovery.add_argument('--explorers',action='store_true')
177
177
  args=parser.parse_args();root=Path(args.data_dir).expanduser().resolve();os.environ['KIRA_DATA_DIR']=str(root)
178
178
  if sys.version_info<(3,11):raise ValueError('Python 3.11 or newer is required.')
@@ -235,10 +235,11 @@ def main():
235
235
  from kira_config import provider_config
236
236
  cfg=provider_config(cfg,'alchemy',args.key_env)
237
237
  cfg['secret_env_file']=str(file);cfg['discovery']['explorers']=True
238
- elif args.command=='discovery':cfg['discovery']={'provider':args.provider,'key_env':args.key_env,'explorers':args.explorers}
238
+ elif args.command=='discovery':cfg['discovery']={**cfg['discovery'],'provider':args.provider,'key_env':args.key_env,'explorers':args.explorers}
239
239
  elif args.setting=='public':cfg['rpc']['mode']='public'
240
240
  else:
241
241
  cfg['rpc'].update(mode='custom')
242
+ if args.priority:cfg['rpc']['priority']=args.priority
242
243
  if args.public_fallback is not None:cfg['rpc']['allow_public_fallback']=args.public_fallback
243
244
  cfg['rpc']['chains'][str(args.chain)]={'url_env':args.url_env}
244
245
  # Validate before replacing the user's working configuration.
package/kira_config.py CHANGED
@@ -16,8 +16,8 @@ def config_path():
16
16
  return Path(os.environ.get('KIRA_CONFIG', data_root()/'.kira.local.json')).expanduser()
17
17
 
18
18
  def default_config():
19
- return {'schema_version':1,'rpc':{'mode':'public','allow_public_fallback':True,'chains':{}},
20
- 'discovery':{'provider':'none','key_env':'ALCHEMY_API_KEY','explorers':False}}
19
+ return {'schema_version':1,'rpc':{'mode':'public','allow_public_fallback':True,'priority':'public_first','chains':{}},
20
+ 'discovery':{'provider':'none','key_env':'ALCHEMY_API_KEY','explorers':False,'public':True}}
21
21
 
22
22
  def provider_config(config, provider, key_env):
23
23
  """One settings transition. Preserve explicit fallback and custom overrides."""
@@ -44,7 +44,9 @@ def load_config():
44
44
  raise ValueError('Invalid RPC configuration.')
45
45
  if not isinstance(discovery,dict) or discovery.get('provider') not in ('none','alchemy') or type(discovery.get('explorers',False)) is not bool:
46
46
  raise ValueError('Invalid discovery configuration.')
47
- if set(rpc)-{'mode','allow_public_fallback','chains'} or set(discovery)-{'provider','key_env','explorers'}:raise ValueError('Unknown provider configuration fields.')
47
+ if rpc.get('priority','public_first') not in ('public_first','custom_first'):raise ValueError('Invalid RPC priority.')
48
+ if type(discovery.get('public',True)) is not bool:raise ValueError('Invalid public discovery preference.')
49
+ if set(rpc)-{'mode','allow_public_fallback','priority','chains'} or set(discovery)-{'provider','key_env','explorers','public'}:raise ValueError('Unknown provider configuration fields.')
48
50
  if any(not isinstance(row,dict) or set(row)-{'url_env','alchemy_network'} for row in rpc['chains'].values()):raise ValueError('Unknown RPC endpoint fields.')
49
51
  for name in [discovery.get('key_env'),*[row.get('url_env') if isinstance(row,dict) else None for row in rpc['chains'].values()]]:
50
52
  if not isinstance(name,str) or not re.fullmatch(r'[A-Za-z_][A-Za-z0-9_]*',name):raise ValueError('Credentials must use valid environment references.')
@@ -97,7 +99,8 @@ def endpoints(network,cfg=None):
97
99
  # New setups default to automatic fallback. Preserve an explicit existing
98
100
  # opt-out for operators who require custom providers only.
99
101
  if not rpc['allow_public_fallback']:return urls
100
- urls.extend(network.get('public_rpc') or [])
102
+ public=network.get('public_rpc') or []
103
+ urls=public+urls if rpc.get('priority','public_first')=='public_first' else urls+public
101
104
  return list(dict.fromkeys(urls))
102
105
 
103
106
  def runtime():
@@ -105,7 +108,7 @@ def runtime():
105
108
  try: values=secret_values(cfg)
106
109
  except ValueError: values={}
107
110
  return {'endpoints':{str(n['chain_id']):endpoints(n,cfg) for n in networks},
108
- 'secrets':list(values.values()),'data_root':str(data_root())}
111
+ 'secrets':list(values.values()),'data_root':str(data_root()),'health_file':str(data_root()/'cache/rpc-health.json')}
109
112
 
110
113
  if __name__=='__main__':
111
114
  # Internal child-process channel only. The user CLI never emits this payload.