@stelis/say-ur-intent 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/activity/sqliteActivityStore.js +36 -2
- package/dist/core/activity/sqliteActivityStoreSchema.js +54 -0
- package/dist/core/session/keyedRecordStore.js +18 -0
- package/dist/core/session/sessionRecordStore.js +33 -0
- package/dist/core/session/sessionStore.js +28 -11
- package/dist/core/session/settingsSessions.js +3 -1
- package/dist/core/session/sqliteSessionStore.js +175 -0
- package/dist/core/session/sqliteTransactionMaterialStore.js +64 -0
- package/dist/core/session/transactionMaterialStore.js +60 -43
- package/dist/core/session/walletIdentitySessions.js +4 -2
- package/dist/review-app/analysis.js +1 -1
- package/dist/review-app/mermaid-parser.core-DkwUYTPl.js +1 -1
- package/dist/review-app/review.js +32 -32
- package/dist/review-app/walletStatus-YODe5Y4P.js +7 -0
- package/dist/runtime/reviewServerAcquire.js +68 -81
- package/dist/runtime/smokeMainnetRead.js +8 -5
- package/dist/runtime/start.js +16 -16
- package/docs/FRONTEND_POLICY.md +7 -2
- package/docs/LOCAL_DB_ARCHITECTURE.md +24 -1
- package/docs/SDK_API.md +1 -1
- package/package.json +2 -2
- package/dist/review-app/walletStatus-CcojOdGy.js +0 -7
|
@@ -1,75 +1,88 @@
|
|
|
1
|
+
const DEFAULT_REACQUIRE_INTERVAL_MS = 3000;
|
|
1
2
|
function errorCode(error) {
|
|
2
3
|
return typeof error === "object" && error !== null
|
|
3
4
|
? error.code
|
|
4
5
|
: undefined;
|
|
5
6
|
}
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
*
|
|
10
|
-
* Safety: the holder is positively identified over loopback before any signal is
|
|
11
|
-
* sent. A process that is not our review server is never signalled; a same-name
|
|
12
|
-
* instance owned by another OS user surfaces a clear error rather than a kill.
|
|
13
|
-
* The port is never silently reassigned, so the wallet autoconnect origin stays
|
|
14
|
-
* stable.
|
|
15
|
-
*/
|
|
16
|
-
export async function startReviewServerWithTakeover(start, port, deps) {
|
|
7
|
+
// Bind the port: returns the server on success, undefined when the port is already
|
|
8
|
+
// in use, and rethrows any other (non-EADDRINUSE) startup error.
|
|
9
|
+
async function tryStart(start, port) {
|
|
17
10
|
try {
|
|
18
11
|
return await start(port);
|
|
19
12
|
}
|
|
20
13
|
catch (error) {
|
|
21
|
-
if (errorCode(error)
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
const holder = await deps.probeIdentity(port);
|
|
25
|
-
if (!holder || holder.service !== deps.serviceName) {
|
|
26
|
-
throw new Error(`Review server port ${port} is already in use by a process that is not a ${deps.serviceName} review server. ` +
|
|
27
|
-
`Set SAY_UR_INTENT_REVIEW_PORT to a free port. The port is not reassigned automatically so the wallet autoconnect origin stays stable.`);
|
|
14
|
+
if (errorCode(error) === "EADDRINUSE") {
|
|
15
|
+
return undefined;
|
|
28
16
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Bind the review server to its fixed port, or defer to a healthy peer already
|
|
22
|
+
* serving it.
|
|
23
|
+
*
|
|
24
|
+
* - Port free → bind and own the single review origin.
|
|
25
|
+
* - Port held by a separate healthy instance of our review server → defer (run no
|
|
26
|
+
* local server; the peer serves the shared database for every client) and watch for
|
|
27
|
+
* the owner to exit, then take the origin over. No process is ever signalled.
|
|
28
|
+
* - Port held by anything else (foreign, no identity answer, or our own pid) → clear
|
|
29
|
+
* error; the origin is never silently reassigned.
|
|
30
|
+
*/
|
|
31
|
+
export async function startOrDeferReviewServer(start, port, deps) {
|
|
32
|
+
const owned = await tryStart(start, port);
|
|
33
|
+
if (owned) {
|
|
34
|
+
deps.logger.info("review server bound; owning the review origin", { port });
|
|
35
|
+
return { deferred: false, close: () => owned.close() };
|
|
36
|
+
}
|
|
37
|
+
const holder = await deps.probeIdentity(port);
|
|
38
|
+
if (!holder || holder.service !== deps.serviceName || holder.pid === deps.currentPid) {
|
|
39
|
+
throw new Error(`Review server port ${port} is already in use by a process that is not a separate ${deps.serviceName} review server. ` +
|
|
40
|
+
`Set SAY_UR_INTENT_REVIEW_PORT to a free port. The port is not reassigned automatically so the wallet autoconnect origin stays stable.`);
|
|
41
|
+
}
|
|
42
|
+
deps.logger.info("review port owned by a healthy peer; deferring and watching for takeover", {
|
|
43
|
+
port,
|
|
44
|
+
ownerPid: holder.pid,
|
|
45
|
+
...(holder.version ? { ownerVersion: holder.version } : {})
|
|
46
|
+
});
|
|
47
|
+
const reacquireIntervalMs = deps.reacquireIntervalMs ?? DEFAULT_REACQUIRE_INTERVAL_MS;
|
|
48
|
+
let stopped = false;
|
|
49
|
+
let acquired;
|
|
50
|
+
const watch = (async () => {
|
|
51
|
+
while (!stopped && !acquired) {
|
|
52
|
+
await deps.delay(reacquireIntervalMs);
|
|
53
|
+
if (stopped || acquired) {
|
|
54
|
+
break;
|
|
43
55
|
}
|
|
44
|
-
|
|
45
|
-
|
|
56
|
+
const next = await tryStart(start, port);
|
|
57
|
+
if (!next) {
|
|
58
|
+
continue; // a peer still owns the port; keep deferring
|
|
46
59
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const waitForReleaseMs = deps.waitForReleaseMs ?? 3000;
|
|
51
|
-
const pollIntervalMs = deps.pollIntervalMs ?? 100;
|
|
52
|
-
const attempts = Math.max(1, Math.ceil(waitForReleaseMs / pollIntervalMs));
|
|
53
|
-
for (let attempt = 0; attempt < attempts; attempt++) {
|
|
54
|
-
await deps.delay(pollIntervalMs);
|
|
55
|
-
try {
|
|
56
|
-
return await start(port);
|
|
60
|
+
if (stopped) {
|
|
61
|
+
await next.close();
|
|
62
|
+
break;
|
|
57
63
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
64
|
+
acquired = next;
|
|
65
|
+
deps.logger.info("acquired review port after the previous owner exited", { port });
|
|
66
|
+
}
|
|
67
|
+
})();
|
|
68
|
+
watch.catch(() => undefined);
|
|
69
|
+
return {
|
|
70
|
+
deferred: true,
|
|
71
|
+
close: async () => {
|
|
72
|
+
// Stop watching; the loop's stopped-check closes any bind that lands in-flight,
|
|
73
|
+
// so we never await the (possibly mid-delay) watch loop here.
|
|
74
|
+
stopped = true;
|
|
75
|
+
if (acquired) {
|
|
76
|
+
await acquired.close();
|
|
62
77
|
}
|
|
63
78
|
}
|
|
64
|
-
|
|
65
|
-
`Set SAY_UR_INTENT_REVIEW_PORT to a free port.`);
|
|
66
|
-
}
|
|
79
|
+
};
|
|
67
80
|
}
|
|
68
81
|
/**
|
|
69
|
-
* Ask whatever is listening on the loopback review port to identify itself. Only
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
82
|
+
* Ask whatever is listening on the loopback review port to identify itself. Only our
|
|
83
|
+
* own review server answers `/__identity`; any other process either returns something
|
|
84
|
+
* else or does not answer, and we report it as "not ours" so the caller never defers
|
|
85
|
+
* to it.
|
|
73
86
|
*/
|
|
74
87
|
export async function probeReviewServerIdentity(port, host = "127.0.0.1", timeoutMs = 1000) {
|
|
75
88
|
try {
|
|
@@ -100,29 +113,3 @@ export async function probeReviewServerIdentity(port, host = "127.0.0.1", timeou
|
|
|
100
113
|
return null;
|
|
101
114
|
}
|
|
102
115
|
}
|
|
103
|
-
/**
|
|
104
|
-
* Gracefully signal a same-user process. SIGTERM lets the previous instance run
|
|
105
|
-
* its own shutdown handler (close the review server and database, then exit).
|
|
106
|
-
* Killing another OS user's process fails with EPERM, which we report instead of
|
|
107
|
-
* escalating.
|
|
108
|
-
*/
|
|
109
|
-
export function terminateProcessByPid(pid) {
|
|
110
|
-
try {
|
|
111
|
-
process.kill(pid, "SIGTERM");
|
|
112
|
-
return { ok: true };
|
|
113
|
-
}
|
|
114
|
-
catch (error) {
|
|
115
|
-
const code = errorCode(error);
|
|
116
|
-
if (code === "ESRCH") {
|
|
117
|
-
return { ok: false, reason: "not_found" };
|
|
118
|
-
}
|
|
119
|
-
if (code === "EPERM") {
|
|
120
|
-
return { ok: false, reason: "no_permission" };
|
|
121
|
-
}
|
|
122
|
-
return {
|
|
123
|
-
ok: false,
|
|
124
|
-
reason: "error",
|
|
125
|
-
message: error instanceof Error ? error.message : String(error)
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
}
|
|
@@ -9,11 +9,10 @@ import { validateSupportedAdapterLifecycle } from "../adapters/adapterLifecycleV
|
|
|
9
9
|
import { SqliteActivityStore } from "../core/activity/sqliteActivityStore.js";
|
|
10
10
|
import { TransactionActivityService } from "../core/activity/transactionActivityService.js";
|
|
11
11
|
import { createSuiReadService } from "../core/read/readService.js";
|
|
12
|
-
import {
|
|
12
|
+
import { LocalSessionStore } from "../core/session/sessionStore.js";
|
|
13
13
|
import { createMcpServer } from "../mcp/server.js";
|
|
14
14
|
import { TOOL_NAMES } from "../mcp/toolNames.js";
|
|
15
15
|
import { createReviewHttpServer } from "../review-server/server.js";
|
|
16
|
-
import { InMemoryLocalTransactionMaterialStore } from "../core/session/transactionMaterialStore.js";
|
|
17
16
|
import { buildSupportedReviewAdapters } from "../adapters/reviewAdapters.js";
|
|
18
17
|
import { createDeepbookSwapTransactionMaterialDigestProducer, createDeepbookSwapTransactionMaterialProducer } from "../adapters/deepbook/deepbookTransactionMaterialProducer.js";
|
|
19
18
|
import { createDeepbookSwapHumanReadableReviewProducer } from "../adapters/deepbook/deepbookHumanReviewProducer.js";
|
|
@@ -121,11 +120,15 @@ async function main() {
|
|
|
121
120
|
chainIdentifier,
|
|
122
121
|
coinMetadataCache: activityStore.createCoinMetadataCache()
|
|
123
122
|
});
|
|
124
|
-
const transactionMaterialStore =
|
|
125
|
-
const sessions = new
|
|
123
|
+
const transactionMaterialStore = activityStore.createTransactionMaterialStore();
|
|
124
|
+
const sessions = new LocalSessionStore({
|
|
126
125
|
activityStore,
|
|
127
126
|
logger,
|
|
128
|
-
validateAdapterLifecycle: validateSupportedAdapterLifecycle
|
|
127
|
+
validateAdapterLifecycle: validateSupportedAdapterLifecycle,
|
|
128
|
+
sessions: activityStore.createSessionRecordStore(),
|
|
129
|
+
artifacts: activityStore.createPrivateReviewArtifactStore(),
|
|
130
|
+
walletIdentityStore: activityStore.createWalletIdentityRecordStore(),
|
|
131
|
+
settingsStore: activityStore.createSettingsRecordStore()
|
|
129
132
|
});
|
|
130
133
|
reviewServer = await createReviewHttpServer({
|
|
131
134
|
host: config.reviewHost,
|
package/dist/runtime/start.js
CHANGED
|
@@ -15,13 +15,12 @@ import { createSuiReadService } from "../core/read/readService.js";
|
|
|
15
15
|
import { createTransactionObjectOwnershipProducer } from "../core/action/transactionObjectOwnershipProducer.js";
|
|
16
16
|
import { createReviewTimeSimulationProducer } from "../core/action/reviewTimeSimulationEvidence.js";
|
|
17
17
|
import { producePtbVisualizationArtifact } from "../core/action/ptbVisualizationProducer.js";
|
|
18
|
-
import {
|
|
19
|
-
import { InMemorySessionStore } from "../core/session/sessionStore.js";
|
|
18
|
+
import { LocalSessionStore } from "../core/session/sessionStore.js";
|
|
20
19
|
import { createMcpServer, startMcp } from "../mcp/server.js";
|
|
21
20
|
import { SERVER_NAME, SERVER_NETWORK, SERVER_VERSION } from "../mcp/serverInfo.js";
|
|
22
21
|
import { createReviewHttpServer } from "../review-server/server.js";
|
|
23
22
|
import { DEFAULT_SUI_GRAPHQL_URL, DEFAULT_SUI_GRPC_URL, composeRuntimeConfig, loadBootConfig } from "./config.js";
|
|
24
|
-
import { probeReviewServerIdentity,
|
|
23
|
+
import { probeReviewServerIdentity, startOrDeferReviewServer } from "./reviewServerAcquire.js";
|
|
25
24
|
import { RuntimeLocalSettingsService } from "./localSettingsService.js";
|
|
26
25
|
import { createStderrLogger } from "./logger.js";
|
|
27
26
|
import { SuiEndpointError, verifyMainnetGraphqlEndpoint, verifyMainnetGrpcEndpoint } from "./suiEndpoint.js";
|
|
@@ -81,12 +80,16 @@ async function main() {
|
|
|
81
80
|
});
|
|
82
81
|
}
|
|
83
82
|
});
|
|
84
|
-
const transactionMaterialStore =
|
|
85
|
-
const sessions = new
|
|
83
|
+
const transactionMaterialStore = store.createTransactionMaterialStore();
|
|
84
|
+
const sessions = new LocalSessionStore({
|
|
86
85
|
activityStore: store,
|
|
87
86
|
transactionMaterialStore,
|
|
88
87
|
logger,
|
|
89
|
-
validateAdapterLifecycle: validateSupportedAdapterLifecycle
|
|
88
|
+
validateAdapterLifecycle: validateSupportedAdapterLifecycle,
|
|
89
|
+
sessions: store.createSessionRecordStore(),
|
|
90
|
+
artifacts: store.createPrivateReviewArtifactStore(),
|
|
91
|
+
walletIdentityStore: store.createWalletIdentityRecordStore(),
|
|
92
|
+
settingsStore: store.createSettingsRecordStore()
|
|
90
93
|
});
|
|
91
94
|
const readService = createSuiReadService({
|
|
92
95
|
client: suiClient,
|
|
@@ -169,22 +172,19 @@ async function main() {
|
|
|
169
172
|
network: SERVER_NETWORK
|
|
170
173
|
}
|
|
171
174
|
});
|
|
172
|
-
// The review origin is a single-port singleton
|
|
173
|
-
// the fixed port
|
|
174
|
-
//
|
|
175
|
-
|
|
175
|
+
// The review origin is a single-port singleton shared through the local database:
|
|
176
|
+
// whichever process owns the fixed port serves every client. A second instance
|
|
177
|
+
// defers to a healthy peer (no signals, no port war) and takes the origin over
|
|
178
|
+
// only if that peer exits.
|
|
179
|
+
const reviewServer = await startOrDeferReviewServer((port) => reviewServerFactory.start(port), config.reviewPort, {
|
|
176
180
|
probeIdentity: (probePort) => probeReviewServerIdentity(probePort, config.reviewHost),
|
|
177
|
-
terminate: terminateProcessByPid,
|
|
178
181
|
delay: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
179
182
|
currentPid: process.pid,
|
|
180
183
|
serviceName: SERVER_NAME,
|
|
181
184
|
logger
|
|
182
185
|
});
|
|
183
186
|
reviewServerForCleanup = reviewServer;
|
|
184
|
-
logger.info("review server started", {
|
|
185
|
-
host: reviewServer.host,
|
|
186
|
-
port: reviewServer.port
|
|
187
|
-
});
|
|
187
|
+
logger.info(reviewServer.deferred ? "review server deferring to a healthy peer on the shared origin" : "review server started", { host: config.reviewHost, port: config.reviewPort, deferred: reviewServer.deferred });
|
|
188
188
|
let shuttingDown = false;
|
|
189
189
|
const shutdown = async (signal) => {
|
|
190
190
|
logger.info("shutdown requested", { signal });
|
|
@@ -236,7 +236,7 @@ async function main() {
|
|
|
236
236
|
promptSurfaces: ADAPTER_PROMPT_SURFACES,
|
|
237
237
|
sessions,
|
|
238
238
|
activityStore: store,
|
|
239
|
-
reviewBaseUrl: `http://${
|
|
239
|
+
reviewBaseUrl: `http://${config.reviewHost}:${config.reviewPort}`,
|
|
240
240
|
readService,
|
|
241
241
|
transactionActivityService: new TransactionActivityService({
|
|
242
242
|
activityStore: store,
|
package/docs/FRONTEND_POLICY.md
CHANGED
|
@@ -139,7 +139,12 @@ wallet review contract, a connected wallet whose account equals the reviewed
|
|
|
139
139
|
account, and a successful digest-gated handoff. The signing step shows no wallet picker:
|
|
140
140
|
dapp-kit autoconnect restores the wallet recorded for the active account on the
|
|
141
141
|
fixed-port origin, and the sign action stays gated on the connected account
|
|
142
|
-
matching the reviewed account.
|
|
142
|
+
matching the reviewed account. When autoconnect cannot establish a connection -
|
|
143
|
+
for example after a reload or in a new tab, or for a hardware signer whose
|
|
144
|
+
device session is not restored automatically - the signing step may offer a
|
|
145
|
+
targeted reconnect for the one recorded wallet. That reconnect is not a wallet
|
|
146
|
+
picker: it resumes the recorded wallet's signer session, and the sign action
|
|
147
|
+
stays gated on the connected account matching the reviewed account. While a handoff is outstanding the server locks the session
|
|
143
148
|
(state recomputes are refused) and the page shows a signing-in-progress state
|
|
144
149
|
whose only action is cancel. Other states render:
|
|
145
150
|
|
|
@@ -210,7 +215,7 @@ Wallet and review screens must be keyboard reachable and screen-reader labeled.
|
|
|
210
215
|
|
|
211
216
|
Native push notifications are out of scope. A frontend may use low-authority browser affordances such as document title changes for off-tab terminal results, but only after server status changes.
|
|
212
217
|
|
|
213
|
-
Each session URL represents one independent tab surface. Cross-tab state sharing is out of scope unless a shared local store is explicitly designed.
|
|
218
|
+
Each session URL represents one independent tab surface. Cross-tab state sharing is out of scope unless a shared local store is explicitly designed. That shared local store is now explicitly designed — the shared SQLite database (see docs/LOCAL_DB_ARCHITECTURE.md) — so live review and session state is shared across local clients through it, while the frontend still does not share state directly between browser tabs.
|
|
214
219
|
|
|
215
220
|
## Out Of Scope
|
|
216
221
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Local DB Architecture
|
|
2
2
|
|
|
3
|
-
Say Ur Intent uses a local SQLite database for durable product state that must survive MCP server restarts. The database stores account read context, Say Ur Intent review activity evidence, and user-requested bounded Sui activity facts. It is not a custody store, wallet authorization store, background indexer, complete wallet-history store, or raw transaction archive.
|
|
3
|
+
Say Ur Intent uses a local SQLite database for durable product state that must survive MCP server restarts. The database stores account read context, Say Ur Intent review activity evidence, live review and session state shared across local AI clients, and user-requested bounded Sui activity facts. It is not a custody store, wallet authorization store, background indexer, complete wallet-history store, or raw transaction archive.
|
|
4
4
|
|
|
5
5
|
This document is for maintainers and contributors who change local state, import/export behavior, activity queries, or review evidence storage. Product users normally need only the README and `docs/MCP_SETUP.md`.
|
|
6
6
|
|
|
@@ -53,6 +53,15 @@ WAL mode can create companion files next to the main database, such as `say-ur-i
|
|
|
53
53
|
- `local_settings`: allowlisted local settings. The current key set is `suiGrpcUrl` and `suiGraphqlUrl`, stored as JSON-encoded text and applied after restart.
|
|
54
54
|
- `coin_metadata_cache`: account-independent positive cache for Sui coin metadata used only to format wallet balance display amounts. Rows are keyed by normalized coin type and verified mainnet chain identifier, expire after 24 hours, and are excluded from local data export/import. Read or write failures for this cache block affected wallet unit reads with `metadata_cache_unavailable`; they are not reported as unavailable token decimals.
|
|
55
55
|
|
|
56
|
+
The following live session tables hold runtime session state in the shared database so any one review server can serve a session another client created (see [Shared single-origin review server](#shared-single-origin-review-server)):
|
|
57
|
+
|
|
58
|
+
- `live_review_sessions`: the live review session record — status, bound account, the pending wallet-handoff lock, the plan / review-state / execution-result JSON, and timestamps — keyed by session id.
|
|
59
|
+
- `live_private_review_artifacts`: per-session private review evidence (the transaction-material handle, its digest commitment, and derived evidence), cascade-deleted with its review session.
|
|
60
|
+
- `live_transaction_materials`: locally built unsigned transaction bytes stored as a BLOB behind a redacted handle, with a TTL; deleted on signing, terminal result, or expiry.
|
|
61
|
+
- `live_wallet_identity_sessions` and `live_settings_sessions`: the short-lived wallet-identity and settings capture sessions, stored as validated JSON keyed by session id.
|
|
62
|
+
|
|
63
|
+
These live tables are distinct from the append-only review evidence tables above: the evidence tables remain the durable activity/audit record, while the live tables hold the in-flight session state that the review server serves.
|
|
64
|
+
|
|
56
65
|
Database columns use snake_case. MCP and HTTP JSON fields use camelCase, so the database `review_session_id` column stores the same review-session identity exposed as `reviewSessionId` in API responses.
|
|
57
66
|
|
|
58
67
|
Table relationships:
|
|
@@ -72,6 +81,20 @@ Logical local data reset is the local settings page action that clears stored pr
|
|
|
72
81
|
|
|
73
82
|
Non-terminal review session expiry is recorded lazily when the session is read or mutated after its TTL. There is no background expiry worker.
|
|
74
83
|
|
|
84
|
+
## Shared single-origin review server
|
|
85
|
+
|
|
86
|
+
All live session state lives in this one shared database, so multiple local AI clients (for example Claude and Codex running at the same time) share it. Exactly one process binds the fixed review port and serves every client's review pages from the shared database. A second client's MCP does not take the port over; it defers to that healthy peer and takes the origin over only if the owner exits. Because whichever process owns the port reads and writes the same session rows, a review created by one client is servable by the review server of another, and the single fixed origin keeps the browser wallet autoconnect stable.
|
|
87
|
+
|
|
88
|
+
Cross-process writes rely on WAL plus `PRAGMA busy_timeout`. The one write that must be atomic across processes — the wallet handoff lock — is a single conditional `UPDATE` on `live_review_sessions.pending_handoff_digest`, so two processes cannot hand off two different transactions for the same session.
|
|
89
|
+
|
|
90
|
+
### Unsigned transaction material on disk
|
|
91
|
+
|
|
92
|
+
`live_transaction_materials` stores locally built unsigned transaction bytes so a review can be signed by whichever process owns the port, not only the process that built it. Because these bytes are now on disk in the shared database rather than only in one process's memory, the data directory is created `0700` and the database file is set `0600` so other operating-system users cannot read them; the bytes carry a short TTL and are deleted on signing, terminal result, or expiry. This store is separate from the review evidence path: the MCP tool layer still does not return transaction bytes, and the activity-store evidence inputs still reject transaction bytes, signatures, and signing material before write.
|
|
93
|
+
|
|
94
|
+
### Schema versioning across shared clients
|
|
95
|
+
|
|
96
|
+
The live session tables are added without changing `user_version`. A runtime that does not know a live table simply ignores it, so a newer client can introduce live tables while an older client keeps opening the same shared database. Only a change that an older runtime cannot safely read should raise `user_version`, and such a change requires updating every client that shares the database, because a runtime does not open a database whose `user_version` is newer than it supports.
|
|
97
|
+
|
|
75
98
|
## Boundaries
|
|
76
99
|
|
|
77
100
|
The active account is a read context only. It is not signing authorization, login, authentication for transactions, custody, permission for transactions, or proof of ownership. It stores at most one address per database file; setting a new active account replaces the previous one without revoking anything onchain.
|
package/docs/SDK_API.md
CHANGED
|
@@ -8,7 +8,7 @@ This document records the pinned SDK APIs used by the current runtime. The sourc
|
|
|
8
8
|
- `@mysten/deepbook-v3`: `1.3.6`
|
|
9
9
|
- `@mysten/dapp-kit-core`: `1.3.2`
|
|
10
10
|
- `@flowx-finance/sdk`: `2.1.0`
|
|
11
|
-
- `@stelis/agent-q-provider-sui`: `0.0.
|
|
11
|
+
- `@stelis/agent-q-provider-sui`: `0.0.7`
|
|
12
12
|
- `@zktx.io/ptb-model`: `0.5.0`
|
|
13
13
|
- `mermaid`: `11.12.0`
|
|
14
14
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stelis/say-ur-intent",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"mcpName": "io.github.stelis-dev/say-ur-intent",
|
|
5
5
|
"description": "Say Ur Intent is a local-first toolkit for helping AI applications and users inspect Sui DeFi actions before execution.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"@mysten/deepbook-v3": "1.3.6",
|
|
55
55
|
"@mysten/sui": "2.17.0",
|
|
56
56
|
"@scure/bip39": "2.2.0",
|
|
57
|
-
"@stelis/agent-q-provider-sui": "0.0.
|
|
57
|
+
"@stelis/agent-q-provider-sui": "0.0.7",
|
|
58
58
|
"@zktx.io/ptb-model": "0.5.0",
|
|
59
59
|
"better-sqlite3": "12.9.0",
|
|
60
60
|
"mermaid": "11.12.0",
|