kira-wallet 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +116 -0
  3. package/THIRD_PARTY_NOTICES.md +13 -0
  4. package/bin/kira.cjs +8 -0
  5. package/curve_pricing.py +86 -0
  6. package/docs/AGENT_TOOLS.md +53 -0
  7. package/docs/CLI.md +144 -0
  8. package/docs/JOB_CONTRACT.md +111 -0
  9. package/docs/KIRA_JOBS_BUILD_REPORT.md +92 -0
  10. package/docs/KIRA_REDESIGN_REPORT.md +125 -0
  11. package/docs/ONBOARDING_CHAT_CONTRACT.md +36 -0
  12. package/docs/RELEASE.md +45 -0
  13. package/docs/WALLET_CAPABILITIES.md +98 -0
  14. package/docs/character/brand-generation.json +20 -0
  15. package/docs/character/workspace-generation.json +29 -0
  16. package/docs/screenshots/kira-jobs-desktop.jpg +0 -0
  17. package/kira_agent.py +280 -0
  18. package/kira_cli.py +263 -0
  19. package/kira_config.py +98 -0
  20. package/kira_jobs.py +504 -0
  21. package/kira_tools.py +104 -0
  22. package/networks.json +214 -0
  23. package/onchain.cjs +34 -0
  24. package/package.json +78 -0
  25. package/pipeline-onchain.cjs +284 -0
  26. package/research.py +39 -0
  27. package/sources/job-envelope-v1.schema.json +469 -0
  28. package/sources/job-request-v1.schema.json +352 -0
  29. package/sources/mintclub-bond-abi.json +1457 -0
  30. package/sources/rpc-candidates.json +294 -0
  31. package/sources/uniswap-deployments.json +18261 -0
  32. package/viewer/README.md +119 -0
  33. package/viewer/chain_images.py +21 -0
  34. package/viewer/details.py +119 -0
  35. package/viewer/model.py +216 -0
  36. package/viewer/refresh-curve-prices.cjs +35 -0
  37. package/viewer/refresh_prices.py +97 -0
  38. package/viewer/server.py +153 -0
  39. package/viewer/static/agent.css +100 -0
  40. package/viewer/static/agent.js +203 -0
  41. package/viewer/static/app.js +252 -0
  42. package/viewer/static/favicon.svg +1 -0
  43. package/viewer/static/index.html +139 -0
  44. package/viewer/static/jobs.js +113 -0
  45. package/viewer/static/kira-attention.png +0 -0
  46. package/viewer/static/kira-explain.png +0 -0
  47. package/viewer/static/kira-logo.png +0 -0
  48. package/viewer/static/kira-research.png +0 -0
  49. package/viewer/static/kira-review.png +0 -0
  50. package/viewer/static/kira.png +0 -0
  51. package/viewer/static/style.css +51 -0
  52. package/viewer/static/watching-model.js +129 -0
  53. package/viewer/static/watching.js +167 -0
  54. package/viewer/static/workspace-model.js +43 -0
  55. package/viewer/static/workspace.css +463 -0
  56. package/viewer/static/workspace.js +102 -0
  57. package/viewer/token_images.py +137 -0
  58. package/wallet.py +389 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kira Wallet contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,116 @@
1
+ # Kira Wallet
2
+
3
+ A local EVM wallet research workspace with Kira and durable research jobs.
4
+ It records direct holdings, Mint Club bonding curves and DEX liquidity evidence.
5
+ Missing coverage and prices stay unknown.
6
+
7
+ Original code and character assets are open source under MIT. Third-party
8
+ references retain their own terms; see [notices](THIRD_PARTY_NOTICES.md). The npm
9
+ first npm publication is prepared for the operator. No signing, transfers or trades
10
+ are implemented.
11
+
12
+ ## Local installation
13
+
14
+ Requirements: Node.js 22 or newer and Python 3.11 or newer. macOS is verified.
15
+ Analysis and daemon lifecycle currently require POSIX process and file-lock APIs.
16
+
17
+ ```sh
18
+ npm install --ignore-scripts
19
+ node bin/kira.cjs init
20
+ node bin/kira.cjs doctor
21
+ node bin/kira.cjs setup
22
+ ```
23
+
24
+ The viewer starts at http://127.0.0.1:8787. Data defaults to
25
+ `~/.local/share/kira-wallet`, separate from the installed package. Use
26
+ `--data-dir /absolute/private/directory` before the command for another portfolio.
27
+ The existing workspace viewer can still run at port 8765.
28
+
29
+ For an isolated sample with synthetic data:
30
+
31
+ ```sh
32
+ node bin/kira.cjs --data-dir /tmp/kira-sample demo --port 8790
33
+ node bin/kira.cjs --data-dir /tmp/kira-sample stop
34
+ ```
35
+
36
+ ## Research
37
+
38
+ ```sh
39
+ kira add <operator-supplied-address> --tag '<exact tag>'
40
+ kira refresh '<registered address or tag>'
41
+ kira prices '<registered address or tag>'
42
+ ```
43
+
44
+ From source, substitute `node bin/kira.cjs` for `kira`. Commands create private
45
+ runtime data. The viewer detects local updates within five seconds; it does
46
+ not poll providers. Resume a failed analysis with the same command and
47
+ `--resume <relative snapshot directory>`.
48
+
49
+ The engine checks official Mint Club deployment scope, discovers indexed held
50
+ tokens when configured, pins chain reads to blocks, enumerates reachable bond
51
+ registries, queries ERC20 and registered ERC1155 token ID 0 balances, records
52
+ curve reserves and burn quotes, and verifies supported DEX pool contracts.
53
+ Shared caches contain asset identities, never another wallet's balances.
54
+ Registry publication is atomic and analysis writers are serialized.
55
+
56
+ Public RPC can read native balances, known tokens and reachable bond registries.
57
+ General ERC20 discovery requires an indexed provider and successful responses.
58
+ Testnets, pool TVL and shared curve backing are excluded from wallet USD totals.
59
+ Spot value is distinct from an executable redemption estimate.
60
+
61
+ See [CLI and provider configuration](docs/CLI.md) and
62
+ [viewer behavior](viewer/README.md). Development reports, design concepts and
63
+ generation records remain in the source checkout under `docs/` and `prototype/`.
64
+
65
+ ## Verification
66
+
67
+ ```sh
68
+ npm test
69
+ node --check pipeline-onchain.cjs
70
+ node --check onchain.cjs
71
+ ```
72
+
73
+ UI changes also require responsive browser and keyboard checks. Installation
74
+ checks must use a fresh directory without operator credentials or data.
75
+
76
+ ## Durable local workspace
77
+
78
+ Development build 0.1.0-dev.2 adds persisted research jobs and opt-in protected
79
+ browser controls. Start with `kira start --controls`; the usual start stays
80
+ read-only. CLI and browser use the same typed job service for research, exact
81
+ wallet names and connection references. Jobs preserve prior results, retain
82
+ partial evidence and require explicit interruption recovery. Price jobs keep
83
+ immutable overlays. Saved analyses can be compared with exact decimal balance
84
+ changes and missing records remain unknown.
85
+
86
+ `kira tools` is a read-only local MCP adapter, with no automatic account or
87
+ model connection. See [CLI usage](docs/CLI.md), [job contract](docs/JOB_CONTRACT.md),
88
+ [tool setup gate](docs/AGENT_TOOLS.md) and [continuation evidence](docs/KIRA_JOBS_BUILD_REPORT.md).
89
+
90
+ ## Kira research workspace
91
+
92
+ The workspace places Kira's local briefing and connected conversation
93
+ beside the evidence. The brand link returns Home. Overview, All tokens
94
+ and Activity have distinct tabs; wallet controls stay together. On small
95
+ screens, switch directly between Portfolio and Kira.
96
+
97
+ All tokens uses existing recorded aggregates with chain-plus-contract identity,
98
+ search, network and pricing filters, and 50 rows per page. It does not query
99
+ providers. Activity separates active work from persisted job history and shows
100
+ state, elapsed time, last progress and recovery controls. Missing progress does
101
+ not silently turn a running job into a completed or failed job.
102
+
103
+ Run `kira setup` to connect your installed Codex or Claude Code CLI account.
104
+ Choose no wallet context, one registered wallet or the whole portfolio. A real
105
+ response check verifies the selected model before permissions are saved. Drafts
106
+ stay in memory. Kira-local conversation retention is opt-in; provider and native
107
+ CLI policies remain separate. Context or model changes start a fresh conversation.
108
+ Chat does not sign, trade or automatically start research. See the
109
+ [onboarding and conversation contract](docs/ONBOARDING_CHAT_CONTRACT.md), the
110
+ [redesign report](docs/KIRA_REDESIGN_REPORT.md) and
111
+ [agreed wallet capability direction](docs/WALLET_CAPABILITIES.md).
112
+
113
+ For a contribution, use synthetic data and run `python3 scripts/check-public.py`
114
+ before a push. Never attach a personal portfolio screenshot, registry, snapshot,
115
+ job, credential, local path or log to an issue or PR. Repository history uses a
116
+ public noreply identity. The `kira-workspace-v1` tag preserves the prior UI.
@@ -0,0 +1,13 @@
1
+ # Third-party reference material
2
+
3
+ Kira's original code and character assets are provided under the root MIT
4
+ license. Third-party reference files retain their original terms. The root
5
+ license does not relicense them.
6
+
7
+ - `sources/mintclub-multitoken.sol` retains its `BUSL-1.1` SPDX identifier.
8
+ It is a research reference, not a contract deployed by Kira.
9
+ - `sources/uniswap-state-view.sol` retains its MIT SPDX identifier.
10
+ - Mint Club ABI and contract metadata are research references to
11
+ [Mint Club](https://github.com/Steemhunt/mint.club-v2-contract).
12
+ - Installed dependencies retain their package licenses. Remote token and chain
13
+ artwork is referenced from its provider; the app does not claim ownership.
package/bin/kira.cjs ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ // Keep the existing read-only Python engine and ship its Node dependency here.
3
+ const {spawnSync}=require('node:child_process');
4
+ const path=require('node:path');
5
+ const result=spawnSync(process.env.KIRA_PYTHON || 'python3',
6
+ [path.join(__dirname,'../kira_cli.py'),...process.argv.slice(2)],{stdio:'inherit'});
7
+ if(result.error){console.error('Kira needs Python 3.11 or newer. Set KIRA_PYTHON to its executable.');process.exitCode=1;}
8
+ else process.exitCode=result.status ?? 1;
@@ -0,0 +1,86 @@
1
+ """Resolve spot prices through external Mint Club reserve curves."""
2
+ from concurrent.futures import ThreadPoolExecutor
3
+ import json
4
+ import math
5
+ from pathlib import Path
6
+ import subprocess
7
+ import sys
8
+ import research as h
9
+
10
+
11
+ def resolve(graph, prices):
12
+ nodes={(r['chain_id'],r['address'].lower()):r for r in graph if r.get('address')}
13
+ known={(r['chain_id'],r['address'].lower()):r for r in prices}
14
+ cache={}
15
+ def visit(key,seen):
16
+ if key in cache:return cache[key]
17
+ if key in seen:return None
18
+ existing=known.get(key)
19
+ node=nodes.get(key)
20
+ if existing and existing.get('usd') is not None and existing.get('quality')!='unreliable':
21
+ cache[key]=existing;return existing
22
+ if not node or node.get('error') or not node.get('is_curve'):return None
23
+ if not node['funded']:
24
+ return {'chain_id':key[0],'address':node['address'],'usd':None,'basis':'Curve spot','quality':'unfunded'}
25
+ parent=visit((key[0],node['reserve_token'].lower()),seen|{key})
26
+ if not parent or parent.get('usd') is None:return None
27
+ usd=float(node['price_in_reserve'])*parent['usd']
28
+ if not math.isfinite(usd) or usd<0:return None
29
+ record={'chain_id':key[0],'address':node['address'],'usd':usd,'basis':'Curve spot','quality':'estimated',
30
+ 'source':node['source'],'observed_at':node['observed_at'],'block_number':node['block_number'],
31
+ 'curve_price_in_reserve':node['price_in_reserve'],'curve_reserve':node['curve_reserve'],'reserve_symbol':node['reserve_symbol'],
32
+ 'price_path':[node['address']]+parent.get('price_path',[node['reserve_token']])}
33
+ cache[key]=record
34
+ return record
35
+ for key in nodes:visit(key,set())
36
+ return list(cache.values())
37
+
38
+
39
+ def enrich(snapshot,market,folder):
40
+ from wallet import ALCHEMY, DEX, atomic, fetch, pool_row, run_node
41
+ sys.path.insert(0,str(h.ASSETS/'viewer'))
42
+ from model import dex_price
43
+ networks=json.loads((h.ASSETS/'sources/rpc-candidates.json').read_text())
44
+ input_path=folder/'reserve-graph-input.json'
45
+ atomic(input_path,{'wallet':snapshot['wallet_address'],'tokens':snapshot['tokens'],'networks':networks})
46
+ run_node('reserves',input_path,folder/'reserve-graph.json')
47
+ graph=json.loads((folder/'reserve-graph.json').read_text())['tokens']
48
+ ordinary=[r for r in graph if r.get('address') and r.get('is_curve') is False and r['chain_id'] in ALCHEMY]
49
+ prices=list(market.get('tokens',[]))
50
+ # Only reserve assets are requested. Identify prices by network and contract, never symbol.
51
+ requests=[{'network':ALCHEMY[r['chain_id']],'address':r['address']} for r in ordinary]
52
+ inverse={value:key for key,value in ALCHEMY.items()}
53
+ responses=[]
54
+ key=h.secrets().get('ALCHEMY_CUSTOM_APY_KEY')
55
+ for i in range(0,len(requests) if key else 0,25):
56
+ response=fetch('https://api.g.alchemy.com/prices/v1/'+h.secrets()['ALCHEMY_CUSTOM_APY_KEY']+'/tokens/by-address',{'addresses':requests[i:i+25]})
57
+ responses.append({'observed_at':h.now(),'source':'Alchemy token prices by address','request':requests[i:i+25],'response':response})
58
+ for record in response.get('data') or []:
59
+ p=next((p for p in record.get('prices',[]) if p.get('currency')=='usd' and p.get('value') is not None),None)
60
+ if p and not record.get('error'):
61
+ value=float(p['value'])
62
+ if math.isfinite(value) and value>0:
63
+ prices.append({'chain_id':inverse[record['network']],'address':record['address'],'usd':value,'basis':'Market index','quality':'estimated',
64
+ 'observed_at':p.get('lastUpdatedAt'),'source':'https://www.alchemy.com/docs/data/prices-api/prices-api-endpoints/prices-api-endpoints/get-token-prices-by-address'})
65
+ atomic(folder/'reserve-market-prices.json',responses)
66
+ def get(row):
67
+ chain=DEX.get(row['chain_id'])
68
+ if not chain:return None
69
+ url='https://api.dexscreener.com/token-pairs/v1/'+chain+'/'+row['address']
70
+ return {'row':row,'source':url,'observed_at':h.now(),'response':fetch(url)}
71
+ keys={(p['chain_id'],p['address'].lower()) for p in prices if p.get('usd') is not None}
72
+ missing=[r for r in ordinary if (r['chain_id'],r['address'].lower()) not in keys]
73
+ with ThreadPoolExecutor(max_workers=4) as executor:fetched=list(executor.map(get,missing))
74
+ atomic(folder/'reserve-dex-prices.json',fetched)
75
+ for r in fetched:
76
+ if not r or not isinstance(r['response'],list):continue
77
+ row=r['row'];shadow={'token_address':row['address'],'dex_pools':[pool_row(p,r['observed_at']) for p in r['response']],'indexer_price_references':[]}
78
+ p=dex_price(shadow,r['observed_at'])
79
+ if p and p.get('usd') is not None:prices.append({'chain_id':row['chain_id'],'address':row['address'],**p})
80
+ # Keep a supported DEX price for held tokens. Add external curve references for their children.
81
+ resolved=resolve(graph,prices)
82
+ by_key={(p['chain_id'],p['address'].lower()):p for p in prices}
83
+ for p in resolved:by_key[(p['chain_id'],p['address'].lower())]=p
84
+ market['tokens']=list(by_key.values())
85
+ market['reserve_price_graph']={'source_file':'reserve-graph.json','observed_at':h.now(),'curve_count':sum(r.get('is_curve') is True for r in graph),'errors':sum(bool(r.get('error')) for r in graph)}
86
+ return market
@@ -0,0 +1,53 @@
1
+ # Recorded research tools
2
+
3
+ `kira --data-dir <private-portfolio> tools` serves a read-only MCP adapter over
4
+ newline-delimited JSON on stdin/stdout. Protocol version is pinned to
5
+ 2025-06-18. It performs no login, model turn, provider request or research
6
+ mutation. Diagnostics stay out of stdout. The process exits on EOF.
7
+
8
+ Tools: portfolio_read, wallet_read, token_read, network_read, job_list,
9
+ job_read, snapshot_read and snapshots_compare. Arguments reject extra fields.
10
+ Tokens use chain ID and exact contract, or `native`. Missing records remain
11
+ unknown. Snapshots expose facts and safe source links, with fixed provider
12
+ errors. No arbitrary files, shell commands, credentials or mutation tools are
13
+ available. Local access to this process grants access to the selected portfolio.
14
+ Only connect it to an explicitly chosen client and data directory.
15
+
16
+ Example client-side stdio configuration, after operator approval:
17
+
18
+ ```toml
19
+ [mcp_servers.kira]
20
+ command = "kira"
21
+ args = ["--data-dir", "<private-portfolio>", "tools"]
22
+ ```
23
+
24
+ Replace the placeholder locally. Do not commit portfolio paths or credentials.
25
+ Kira does not edit the operator's Codex configuration automatically.
26
+
27
+ ## Account connection gate
28
+
29
+ The browser's Kira notes remain deterministic. Connected chat, OAuth handling
30
+ and conversation reconnect are not delivered by this adapter.
31
+
32
+ Before connecting a Kira-owned model session, the operator must select the
33
+ account/authentication route and approve which portfolio facts may reach the
34
+ model. The current official ChatGPT-plan app-server guide describes an app-owned
35
+ OAuth access token, Responses provider configuration and token renewal. It does
36
+ not establish this account's eligibility. Client registration, consent and
37
+ login are operator actions. Do not copy desktop authentication files or use the
38
+ internal-only external ChatGPT-token schema variant. No billing claim follows
39
+ from a tool handshake or model catalog.
40
+
41
+ After the gate, implement the selected auth route and app-server session driver
42
+ with saved thread IDs, terminal turn states, reconnect and interruption. Keep
43
+ those states distinct from engine jobs. Pin the installed protocol again and
44
+ independently review permissions before connecting real private data.
45
+
46
+ Sources checked on 2026-10-03:
47
+
48
+ - [Official Codex app-server guidance](https://developers.openai.com/siwc/token-sharing-open-source/codex-app-server)
49
+ - [Official Codex MCP guidance](https://learn.chatgpt.com/docs/extend/mcp?surface=cli)
50
+ - [MCP stdio transport contract](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports)
51
+
52
+ Local verification uses synthetic portfolios and a stdin transcript only.
53
+ It does not verify a live Codex/MCP client connection or model access.
package/docs/CLI.md ADDED
@@ -0,0 +1,144 @@
1
+ # CLI and provider configuration
2
+
3
+ The npm package carries the Python engine, browser assets and declared viem
4
+ dependency. It needs no Python packages and no sibling project. The package
5
+ file list excludes wallets, snapshots, conversations, caches, logs and private settings.
6
+ The first release is prepared as a tarball. Registry publication remains an
7
+ operator action. The code and original character assets use the MIT license.
8
+
9
+ ## Lifecycle
10
+
11
+ `kira init` prepares an empty private data directory. `kira start --port 8787`
12
+ starts a loopback-only read-only viewer. `kira status` identifies the managed
13
+ instance. `kira stop` checks its live instance ID and OS process identity.
14
+ Another portfolio never silently reuses that server.
15
+
16
+ `kira setup` starts the loopback viewer with local controls and opens its setup
17
+ screen. Use `--no-open` to print its URL instead. If a read-only viewer is already
18
+ running, stop it explicitly before setup. The normal `kira start` remains read-only.
19
+
20
+ ## Your model account
21
+
22
+ Install and sign in to the official Codex CLI or Claude Code CLI first. Kira
23
+ detects the installed binary and native subscription login without copying its
24
+ authentication. Verified adapter versions are Codex 0.158.x and Claude Code
25
+ 2.1.259 through 2.1.x. Other versions are blocked until their restrictions are
26
+ verified. This is a native CLI connection, with no Kira account, API-key proxy or
27
+ hosted portfolio backend.
28
+
29
+ In setup, choose the CLI account and an optional model ID. The blank model field
30
+ uses the CLI default. Choose no automatic wallet context, one registered wallet,
31
+ or the whole recorded portfolio. A generic response check sends no wallet data
32
+ and must pass before these settings are saved.
33
+
34
+ Your entered message and approved context go through the CLI to its model
35
+ service. Context includes recorded balances, wallet names and addresses, chain
36
+ and contract identities, timestamps, unknown prices and coverage. Credentials,
37
+ RPC endpoints, raw provider errors and local paths are excluded. Changing the
38
+ provider, model, context or selected wallet creates a new conversation.
39
+
40
+ Kira-local history is opt-in and uses private `conversations/` files. With history
41
+ off, conversations and drafts stay in memory. A new server process starts a fresh
42
+ conversation; saved files remain available locally and are not automatically
43
+ replayed. Native CLI, administrator policy and provider data policies remain a
44
+ separate trust boundary. Chat cannot sign, trade or automatically start research.
45
+ See [the conversation contract](ONBOARDING_CHAT_CONTRACT.md).
46
+
47
+ Global `--data-dir` precedes the command. `KIRA_DATA_DIR` provides the same
48
+ override. `KIRA_PYTHON` selects Python. Direct historical scripts keep their
49
+ workspace default. The supported CLI uses the user data directory. Installed
50
+ assets and writable runtime files use separate roots.
51
+
52
+ ## Public RPC
53
+
54
+ ```sh
55
+ kira rpc public
56
+ kira discovery none
57
+ kira doctor
58
+ kira doctor --live --chains 1,8453,81457 --budget 30
59
+ ```
60
+
61
+ No credential file is required. Live doctor performs bounded identity, block,
62
+ deployed bond bytecode, Multicall3 bytecode and historical bytecode probes.
63
+ It reports endpoint indexes. These samples do not guarantee broad archive
64
+ history, multicall execution or wallet coverage. Exit code 2 means at least one
65
+ sampled chain is unavailable or limited. Failed endpoints stop after their
66
+ first failure; at most three are tried per chain. There are no hidden retries.
67
+ The budget limits JSON-RPC method calls in that invocation. Full analysis has
68
+ no global request cap yet.
69
+
70
+ ## Custom RPC and discovery
71
+
72
+ Set an endpoint in your shell or private environment file. Keep the value out
73
+ of command arguments. Configuration stores references only.
74
+
75
+ ```sh
76
+ export KIRA_BASE_RPC='<your HTTPS endpoint>'
77
+ kira rpc set --chain 8453 --url-env KIRA_BASE_RPC
78
+ export ALCHEMY_API_KEY='<your key>'
79
+ kira discovery alchemy --key-env ALCHEMY_API_KEY --explorers
80
+ kira config show
81
+ ```
82
+
83
+ Custom mode disables public fallback by default. Add `--public-fallback` to
84
+ explicitly enable it. That preference applies to all custom-mode chains.
85
+ Chains without a configured endpoint remain unavailable while fallback is
86
+ disabled. Plain HTTP is accepted only for loopback endpoints.
87
+
88
+ Discovery is independent. Without an indexer, native and reachable Mint Club
89
+ reads still run and general discovery stays incomplete. Configuring an RPC
90
+ does not prove ERC20 discovery completeness.
91
+
92
+ ## Explicit compatibility import
93
+
94
+ ```sh
95
+ kira config import-env --file /absolute/private/rpc.env --key-env ALCHEMY_API_KEY
96
+ ```
97
+
98
+ The import references the file instead of copying keys. It configures Alchemy
99
+ RPC and portfolio discovery separately, with public fallback enabled to match
100
+ the historical engine. Environment overrides the file. Missing files or keys
101
+ produce limited coverage; malformed configuration fails with safe errors.
102
+ The import does not contact providers. Private settings are never served.
103
+
104
+ Analysis output contains wallet addresses and tags. Keep runtime directories
105
+ outside repositories or ignored. Never publish operator holdings as demos.
106
+ The demo command uses synthetic addresses and sample prices only.
107
+
108
+ ## Durable research and local controls
109
+
110
+ Normal `kira add`, `kira refresh` and `kira prices` now save a version 1 job
111
+ before running the existing engine. Use `--idempotency-key <stable-key>` to
112
+ retry a submission without duplicating work. Add `--enqueue` to return after
113
+ queuing the job and start a separate worker. `--resume <snapshot-directory>`
114
+ remains the legacy unpublished-snapshot interface. New durable jobs resume by
115
+ job ID:
116
+
117
+ ```sh
118
+ kira jobs list
119
+ kira jobs read <job-id>
120
+ kira jobs cancel <job-id>
121
+ kira jobs resume <job-id> --idempotency-key <stable-resume-key>
122
+ kira jobs run
123
+ ```
124
+
125
+ An interrupted job needs an explicit resume. Published partial results retain
126
+ coverage gaps and require a new refresh to investigate again. Failed and
127
+ cancelled jobs retain their last good result and unpublished evidence.
128
+ `jobs run` starts queued work and reconciles crash receipts; it does not replay
129
+ interrupted jobs. Price jobs retain immutable overlays before updating the
130
+ viewer projection. A viewer stop does not cancel research jobs.
131
+
132
+ Start with `kira start --controls` to enable protected browser actions. The
133
+ usual start remains read-only. Stop an existing read-only instance before
134
+ starting one with controls. Actions need the local session and same origin.
135
+ The supported URL uses 127.0.0.1. Browser controls offer wallet registration,
136
+ exact names, holdings/price refresh, cancel/resume, saved analysis comparison,
137
+ RPC references and discovery references. The synthetic demo cannot start
138
+ provider research. Enter endpoint credential references in Connections;
139
+ secrets and URLs remain outside the browser.
140
+
141
+ Use `kira --data-dir <private-portfolio> tools` for the read-only stdio adapter.
142
+ See [the tool guide](AGENT_TOOLS.md) and [job contract](JOB_CONTRACT.md).
143
+ Connected chat still requires the operator's account setup and a later session
144
+ integration. The local development package remains private and unlicensed.
@@ -0,0 +1,111 @@
1
+ # Persisted jobs, version 1
2
+
3
+ The engine owns financial facts. CLI and protected local browser actions share
4
+ this contract. Chat interruption never cancels an engine job.
5
+
6
+ ## Request and identity
7
+
8
+ Mutations use `{schema_version: 1, operation, input, idempotency_key}`.
9
+ Supported operations are `wallet.add` (address, exact tag), `wallet.refresh`
10
+ (wallet address or exact tag), `prices.refresh` (same), `wallet.setTags`
11
+ (wallet, exact tags), `job.resume` and `job.cancel` (job_id).
12
+ Read operations are `job.read`, `job.list`, `snapshot.read`, and
13
+ `snapshots.compare`. The portfolio projection retains its existing schema.
14
+
15
+ Inputs reject unknown fields and invalid identifiers. Wallet selectors resolve
16
+ to a unique registered address before persistence. Ambiguous tags fail.
17
+ Idempotency keys are private, bounded strings. Persist only their SHA-256 hash.
18
+ The hash maps to one job across operations. Reusing it with different normalized
19
+ input fails with `idempotency_conflict`. Terminal duplicate submission returns
20
+ the same job without rerunning providers. Resume requeues the same job ID and
21
+ increments its attempt on execution. Retain the key until the job is removed
22
+ by a future explicit retention policy. Version mismatches fail closed.
23
+
24
+ ## Envelope
25
+
26
+ Each private `jobs/<uuid>.json` records schema_version, job_id, operation,
27
+ input, request_hash, key_hash, state, attempt, created_at, updated_at,
28
+ sequence, cancel_requested, stage, events, checkpoint, result, errors and
29
+ previous_result. Events contain only allowed stage names and real counts.
30
+ Unknown totals stay null. Result references use paths relative to snapshots/;
31
+ no absolute paths or arbitrary file reads are supported.
32
+
33
+ The optional `started_at` timestamp records when the current attempt was
34
+ claimed by the worker. Resuming replaces it on the next claim. Older envelopes
35
+ remain valid without it. The viewer then labels its timer "Since request"
36
+ instead of claiming an exact execution time. Terminal timers stop at the last
37
+ recorded update. A queued job uses its latest queue transition, not a previous
38
+ attempt's start.
39
+
40
+ States: queued -> running -> succeeded | partial | failed | cancelled.
41
+ Recovery converts unfinished running jobs to interrupted. Explicit resume may
42
+ move interrupted, failed or cancelled to queued. Succeeded and partial jobs
43
+ retain their result; a new refresh is a new job. Partial is a published result
44
+ with documented coverage gaps, not a failed result or proof of zero holdings.
45
+
46
+ Updates use private files, replace, file fsync and directory fsync under a
47
+ process lock. One worker owns a portfolio. Existing analysis/config locks
48
+ serialize writers. A worker lease is inherited by its engine child, preventing
49
+ another worker from replaying a still-running orphan engine after a supervisor
50
+ crash. A busy legacy writer leaves the new job queued.
51
+
52
+ ## Publication and recovery
53
+
54
+ Analysis jobs allocate `snapshots/jobs/<job_id>` before executing. The existing
55
+ engine resumes only matching, unpublished evidence; completed per-chain reads
56
+ retain their recorded blocks. Partial stages retain missing information.
57
+ No global block is invented. Engine publication atomically updates the registry
58
+ pointer after results exist. A saved result plus a matching registry pointer
59
+ or research-history entry is authoritative even if the final manifest update
60
+ was interrupted. A published result whose
61
+ pointer has since moved remains readable from its immutable directory.
62
+
63
+ Price jobs save an immutable overlay and publication receipt before replacing
64
+ the viewer's market-prices projection. Recovery can finish this projection
65
+ without provider calls. Failed or cancelled work retains previous_result and
66
+ saved evidence. Cancellation stops the subprocess group, waits for the writer
67
+ to release, then records its outcome. If publication won the race, record the
68
+ published result rather than claim it was cancelled.
69
+
70
+ Provider exception text, URLs and credentials never enter public job events.
71
+ Safe errors use fixed codes and messages. Pinned blocks and coverage remain in
72
+ the engine result. Stage progress is evidence from emitted engine events;
73
+ there is no simulated timer or fabricated percentage.
74
+
75
+ ## Local transport
76
+
77
+ Writes require the exact loopback Host, same Origin, JSON content type and an
78
+ in-memory session token obtained from the same-origin session endpoint. No
79
+ CORS or credential files are exposed. Session tokens rotate on server restart.
80
+ Bound request size and reject foreign origins and DNS-rebinding Host values.
81
+ The legacy viewer remains read-only unless explicitly launched with --controls.
82
+ Synthetic demo controls never start provider requests.
83
+
84
+ ## Acceptance cases
85
+
86
+ Synthetic tests cover duplicate requests, key conflicts, invalid versions and
87
+ fields, concurrent submit, restart before execution, interruption before and
88
+ after publication, immutable partial results, previous-result preservation,
89
+ cancel/resume, independent worker leases, snapshot path escape, ambiguous tags,
90
+ unknown values and cross-chain coin identity. HTTP checks cover unauthorized
91
+ writes, foreign origins/Host, malformed bodies and safe static allowlists.
92
+ No operator wallet is analysed by this verification.
93
+
94
+ ## Implemented adapters
95
+
96
+ `kira_jobs.py` contains typed request and persisted envelope definitions, strict
97
+ operation validation and the worker. `sources/job-envelope-v1.schema.json`
98
+ is the versioned envelope artifact. `sources/job-request-v1.schema.json`
99
+ defines the typed mutation request variants. `settings.rpc` and `settings.discovery`
100
+ are also durable operations; they accept environment references only and share
101
+ the portfolio writer lock. `wallet.setTags` records exact tag changes.
102
+
103
+ Chain events retain actual endpoint ordinal, configured reference identity,
104
+ status and pinned block as a decimal string. Completed cached chain stages emit
105
+ the same saved block on resume. Recorded stage details stay available in the
106
+ browser. A registry publication receipt is authoritative even if a crash
107
+ precedes the final run manifest update. A completed price overlay can reconstruct
108
+ its projection receipt without another provider request.
109
+
110
+ The read-only stdio MCP adapter uses the same job and snapshot service. It has
111
+ no mutation tools and requires separate operator-approved client setup.
@@ -0,0 +1,92 @@
1
+ # Kira durable workspace delivery
2
+
3
+ Completed locally on 2026-10-03 as private development build 0.1.0-dev.2.
4
+ The continuation reached the operator account-connection gate. Connected chat,
5
+ publication and independent release review remain pending.
6
+
7
+ ## Delivered behavior
8
+
9
+ CLI and opt-in browser controls share versioned typed requests and persisted
10
+ jobs. Same-key retries return one job. Saved work survives a worker restart;
11
+ unfinished engine work requires explicit resume. Published partial results
12
+ remain available and cannot be resumed over their immutable analysis. Failure
13
+ and cancellation preserve the previous published result and saved evidence.
14
+ Cancellation terminates the engine process group, including descendants.
15
+ A worker lease survives a supervisor crash while its engine is still active.
16
+
17
+ Analysis publication reconciles the registry/manifest crash boundary without
18
+ rerunning providers. Price jobs keep an immutable overlay before replacing the
19
+ viewer projection; restart can finish that projection without provider calls.
20
+ Recorded stages retain actual counts, endpoint identity references and decimal
21
+ pinned blocks. Model interruption is separate from engine cancellation.
22
+
23
+ The browser provides wallet registration, exact names, holdings and price
24
+ refresh, stop/resume, saved analysis comparison and separate RPC/discovery
25
+ references. Host, Origin, session, content type and request-size checks protect
26
+ local actions. Session tokens rotate with the server. The usual launch stays
27
+ read-only; `--controls` explicitly enables actions. The primary local instance
28
+ now has controls enabled. Synthetic demo research cannot contact providers.
29
+
30
+ The first-wallet screen and connection forms keep unknown values visible.
31
+ Comparison preserves decimal quantities, chain/contract identity and absent
32
+ records as unknown. It reports balance, reference and coverage differences;
33
+ it does not attribute USD changes or compare historical price-overlay values.
34
+
35
+ The local read-only MCP adapter exposes eight typed tools through `kira tools`.
36
+ It reads recorded facts and jobs without a login, model turn, shell tool or
37
+ provider request. Its stdio transcript and packaged runtime are verified;
38
+ live client attachment is not yet verified.
39
+
40
+ Kira's new original character icon appears in the brand home link, Home button,
41
+ notes, empty state, wallet dialog and concept studio. Browser and touch icons
42
+ are installed. A tiny editable notebook mark complements the adult portrait.
43
+ Built-in imagegen produced the master and cleaned its transparent exterior.
44
+ [Exact prompts and provenance](character/brand-generation.json) are saved.
45
+
46
+ ## Verification evidence
47
+
48
+ | Check | Result and scope |
49
+ | --- | --- |
50
+ | `npm test` | Passed 41 engine/configuration/job/tool tests and 24 viewer/HTTP tests, Node cache/outage checks and both viewer JavaScript syntax checks |
51
+ | Node syntax | Passed pipeline-onchain.cjs, onchain.cjs and prototype/app.js |
52
+ | `python3 scripts/check-install.py` | Passed fresh npm pack/install without operator configuration; logo route, read-only lifecycle, protected controls, actual synthetic names job and stdio tool facts |
53
+ | Durable process cases | Passed supervisor SIGKILL recovery with one engine invocation; cancellation stopped a SIGTERM-resistant descendant and preserved prior evidence; publication won a simultaneous cancellation |
54
+ | Browser interaction | Passed actual synthetic names mutation and completion; saved analysis comparison displayed 100 -> 120 with exact delta 20; invalid onboarding address blocked before submission; connection settings separated RPC from discovery |
55
+ | Responsive and keyboard | Checked desktop, 320 px Home/wallet/history and 375 px add form; no document overflow; Escape closes dialogs and returns to the trigger |
56
+ | Preservation | All 682 original runtime JSON hashes unchanged; primary portfolio `/api/state` bytes unchanged across upgrade |
57
+ | Privacy | Source/package allowlists and audit exclude operator registry, snapshots, settings, jobs, logs, authentication and real portfolio screenshots |
58
+ | Independent review / model access | Not performed; no account connection, live Codex/MCP-client attachment, model turn or release claim |
59
+
60
+ All research tests used synthetic portfolios and local subprocesses. No new
61
+ operator wallet, holdings refresh, price refresh or additional JSON-RPC probe
62
+ was performed in this continuation.
63
+
64
+ ## Open the result
65
+
66
+ Primary local workspace: `http://127.0.0.1:8765/#/home`.
67
+ Synthetic control preview: `http://127.0.0.1:8793/#/home` while its server runs.
68
+ The empty first-launch preview used a separate private directory at port 8794.
69
+ The original concept studio remains available at port 8792 while running.
70
+
71
+ ![Synthetic workspace with Kira branding and saved job](screenshots/kira-jobs-desktop.jpg)
72
+
73
+ ## Operator boundary and limits
74
+
75
+ The operator must choose an approved account/authentication route and consent
76
+ to the portfolio scope before connected Kira chat. The current official
77
+ [app-server guide](https://developers.openai.com/siwc/token-sharing-open-source/codex-app-server)
78
+ describes app-owned OAuth handling. This does not establish this account's
79
+ eligibility or billing. The [tool guide](AGENT_TOOLS.md) records the concrete
80
+ setup boundary. No desktop authentication files were copied or used.
81
+
82
+ Independent security/release review, platform testing beyond macOS, a global
83
+ provider-call cap and an external pilot remain outstanding. Legacy direct
84
+ engine scripts keep their earlier interfaces; immutable price history is
85
+ provided by the durable CLI/browser job path. Read-only model tools do not
86
+ automatically connect a client. Full connected chat and session persistence
87
+ remain future work after account setup. This package remains private and
88
+ unlicensed. No remote, PR, merge, main push, package publication or deployment
89
+ was created.
90
+
91
+ [Selected design evidence](https://www.lazyweb.com/agentic-search/b98ba70d-d7d2-408b-b6b0-f529201e4365)
92
+ informed the per-job state and recovery UI. It is not a growth or demand claim.