@zktx.io/sui-mcp 0.5.0 → 0.6.1
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 +21 -21
- package/dist/adapters/adapterPromptSurfaces.js +0 -20
- package/dist/adapters/deepbook/deepbookQuotePolicy.js +0 -4
- package/dist/adapters/deepbook/deepbookReviewEvidence.js +0 -9
- package/dist/adapters/deepbook/deepbookTransactionMaterialProducer.js +0 -9
- package/dist/adapters/intentPlanFactories.js +0 -6
- package/dist/core/action/contractNameRegistry.js +0 -53
- package/dist/core/action/forbiddenFields.js +0 -4
- package/dist/core/action/ptbVisualizationProducer.js +0 -3
- package/dist/core/action/receiptPtbGraph.js +0 -17
- package/dist/core/action/reviewTimeSimulationEvidence.js +0 -4
- package/dist/core/action/signableAdapterContract.js +0 -5
- package/dist/core/action/suiChainReceiptMapping.js +0 -18
- package/dist/core/action/suiChainReceiptReader.js +1 -21
- package/dist/core/action/suiChainReceiptVerifier.js +0 -10
- package/dist/core/action/transactionFunding.js +0 -7
- package/dist/core/action/types.js +0 -1
- package/dist/core/action/walletReviewContractAssembler.js +0 -2
- package/dist/core/activity/localDataService.js +65 -52
- package/dist/core/activity/localDataValidation.js +1 -3
- package/dist/core/activity/sqliteActivityStore.js +24 -24
- package/dist/core/activity/sqliteActivityStoreRows.js +0 -2
- package/dist/core/activity/sqliteActivityStoreSchema.js +81 -14
- package/dist/core/preferences/sqlitePreferencesRepository.js +5 -3
- package/dist/core/read/deepbookReadHelpers.js +0 -2
- package/dist/core/read/deepbookRegistry.js +0 -19
- package/dist/core/read/deepbookUsdcChartService.js +0 -2
- package/dist/core/read/readCardResult.js +0 -2
- package/dist/core/read/readService.js +0 -19
- package/dist/core/read/readServiceTypes.js +0 -2
- package/dist/core/session/cardSession.js +1 -5
- package/dist/core/session/cardSessionStore.js +54 -7
- package/dist/core/session/reviewValidity.js +0 -3
- package/dist/core/session/sessionRecordStore.js +0 -1
- package/dist/core/session/sessionStore.js +0 -32
- package/dist/core/session/settingsFileIdentity.js +9 -0
- package/dist/core/session/settingsFileProtocol.js +20 -0
- package/dist/core/session/settingsFileStore.js +337 -0
- package/dist/core/session/settingsInputError.js +32 -0
- package/dist/core/session/settingsOperation.js +220 -0
- package/dist/core/session/settingsPreparationSql.js +13 -0
- package/dist/core/session/settingsWorkflow.js +1183 -0
- package/dist/core/session/sqliteCardStore.js +0 -2
- package/dist/core/session/sqliteSessionStore.js +0 -51
- package/dist/core/session/sqliteSettingsWorkflowStore.js +453 -0
- package/dist/core/session/sqliteTransactionMaterialStore.js +0 -11
- package/dist/core/session/sqliteWalletWorkflowStore.js +160 -66
- package/dist/core/session/status.js +2 -4
- package/dist/core/session/transactionMaterialStore.js +0 -4
- package/dist/core/session/transactionRequest.js +0 -4
- package/dist/core/session/walletConnection.js +26 -10
- package/dist/core/session/walletWorkflow.js +70 -30
- package/dist/core/session/workflowState.js +14 -14
- package/dist/core/session/workflowView.js +18 -8
- package/dist/core/suiAddress.js +0 -5
- package/dist/core/suiEndpoint.js +0 -3
- package/dist/mcp/prompts.js +0 -12
- package/dist/mcp/responseGuidance.js +10 -5
- package/dist/mcp/schemas.js +0 -3
- package/dist/mcp/serverInfo.js +3 -4
- package/dist/mcp/toolNames.js +2 -2
- package/dist/mcp/tools/account/index.js +1 -19
- package/dist/mcp/tools/action/prepareSuiActionReview.js +0 -6
- package/dist/mcp/tools/read/readToolHelpers.js +5 -4
- package/dist/mcp/tools/read/serverStatusTools.js +0 -1
- package/dist/mcp/tools/session/executionResultTools.js +2 -2
- package/dist/mcp/tools/session/shared.js +1 -2
- package/dist/mcp/tools/session/statusTools.js +4 -5
- package/dist/mcp/tools/settings/index.js +8 -28
- package/dist/mcp-app/account.html +14 -14
- package/dist/mcp-app/account.notices.txt +1 -1
- package/dist/mcp-app/chart.html +9 -9
- package/dist/mcp-app/chart.notices.txt +1 -1
- package/dist/mcp-app/connect.html +14 -14
- package/dist/mcp-app/connect.notices.txt +1 -1
- package/dist/mcp-app/receipt.html +1298 -1463
- package/dist/mcp-app/receipt.notices.txt +226 -1204
- package/dist/mcp-app/review.html +1298 -1463
- package/dist/mcp-app/review.notices.txt +226 -1204
- package/dist/mcp-app/settings.html +3133 -0
- package/dist/mcp-app/settings.notices.txt +973 -0
- package/dist/mcp-ui/contracts.js +8 -2
- package/dist/mcp-ui/readCards.js +1 -1
- package/dist/mcp-ui/tools.js +34 -4
- package/dist/review-server/server.js +3 -51
- package/dist/runtime/application.js +41 -14
- package/dist/runtime/config.js +0 -2
- package/dist/runtime/localSettingsService.js +23 -2
- package/dist/runtime/logger.js +0 -2
- package/dist/runtime/reviewServerAcquire.js +1 -18
- package/dist/runtime/runtimeTermination.js +1 -4
- package/dist/runtime/shared/authenticatedFetch.js +2 -4
- package/dist/runtime/shared/control.js +4 -7
- package/dist/runtime/shared/dataAccess.js +0 -2
- package/dist/runtime/shared/http.js +14 -0
- package/dist/runtime/shared/mcpHttp.js +6 -6
- package/dist/runtime/shared/ownerLease.js +0 -4
- package/dist/runtime/shared/server.js +1 -2
- package/dist/runtime/shared/stdio.js +2 -5
- package/dist/runtime/smokeMainnetRead.js +0 -3
- package/dist/runtime/smokeReview.js +0 -4
- package/dist/runtime/start.js +0 -2
- package/dist/runtime/suiTransactionGraphqlMapping.js +0 -2
- package/dist/runtime/suiTransactionGraphqlQueries.js +0 -3
- package/dist/runtime/walletConnectConfig.js +0 -2
- package/dist/runtime/walletConnectStorage.js +2 -10
- package/dist/runtime/walletConnectTransport.js +0 -11
- package/dist/runtime/walletSdkChildRuntime.js +2 -6
- package/dist/runtime/walletSdkIpc.js +2 -1
- package/dist/runtime/walletSdkProcess.js +3 -8
- package/docs/AGENT_BEHAVIOR.md +45 -15
- package/docs/LOCAL_DB_ARCHITECTURE.md +145 -22
- package/docs/MCP_SETUP.md +99 -39
- package/docs/MCP_TOOLS.md +288 -24
- package/docs/SDK_API.md +63 -10
- package/docs/UTILITY_INDEX.md +9 -6
- package/docs/WALLET_CONNECTION.md +32 -10
- package/package.json +22 -8
- package/dist/core/activity/schemaVersion.js +0 -2
- package/dist/core/session/keyedRecordStore.js +0 -18
- package/dist/core/session/settingsSession.js +0 -1
- package/dist/core/session/settingsSessions.js +0 -45
- package/dist/review-app/brand-dark.svg +0 -1
- package/dist/review-app/brand-light.svg +0 -1
- package/dist/review-app/favicon.svg +0 -1
- package/dist/review-app/settings.css +0 -1
- package/dist/review-app/settings.js +0 -1
- package/dist/review-app/ui.css +0 -809
- package/dist/review-server/assets.js +0 -48
- package/dist/review-server/html.js +0 -45
- package/dist/review-server/http.js +0 -47
- package/dist/review-server/middleware/reviewToken.js +0 -7
- package/dist/review-server/settingsApi.js +0 -158
- package/docs/AGENT_DEVELOPMENT_POLICY.md +0 -815
- package/docs/FRONTEND_POLICY.md +0 -534
- package/docs/golden-scenarios/BEHAVIOR_MATRIX.md +0 -166
- package/docs/golden-scenarios/INTENT_EVIDENCE_GOLDEN_ANSWERS.md +0 -161
- package/docs/golden-scenarios/INTENT_EVIDENCE_MATRIX.md +0 -259
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { SETTINGS_DOWNLOAD_FILE_SQL, SETTINGS_FILE_CLEANUP_SQL } from "../session/settingsFileIdentity.js";
|
|
1
2
|
import { hasUnsettledWalletWork } from "../session/sqliteWalletWorkflowStore.js";
|
|
2
|
-
import { DB_USER_VERSION } from "./schemaVersion.js";
|
|
3
3
|
import { SuiEndpointError, parseGraphqlUrl, parseGrpcUrl } from "../suiEndpoint.js";
|
|
4
4
|
import { LOCAL_DATA_EXPORT_FORMAT, LOCAL_DATA_NETWORK, LocalDataError } from "./localDataTypes.js";
|
|
5
|
-
import { activeAccountChange, countsForPayload, invalidBackup, maxNumber, parseLocalDataEnvelope, suiGraphqlUrlFromPayload, suiGrpcUrlFromPayload } from "./localDataValidation.js";
|
|
5
|
+
import { activeAccountChange, activeAccountAddress, countsForPayload, invalidBackup, maxNumber, parseLocalDataEnvelope, suiGraphqlUrlFromPayload, suiGrpcUrlFromPayload } from "./localDataValidation.js";
|
|
6
6
|
export * from "./localDataTypes.js";
|
|
7
7
|
export class SqliteLocalDataService {
|
|
8
8
|
db;
|
|
@@ -15,18 +15,18 @@ export class SqliteLocalDataService {
|
|
|
15
15
|
}
|
|
16
16
|
now() { return this.options.now?.() ?? new Date(); }
|
|
17
17
|
async getDataCounts() {
|
|
18
|
-
return this.
|
|
18
|
+
return this.currentDataCountsSync();
|
|
19
19
|
}
|
|
20
|
-
|
|
20
|
+
currentDataCountsSync(now = this.now()) {
|
|
21
21
|
return this.db.transaction(() => {
|
|
22
|
-
this.options.advanceRequestDeadlines(
|
|
22
|
+
this.options.advanceRequestDeadlines(now);
|
|
23
23
|
return this.getDataCountsSync();
|
|
24
24
|
}).immediate();
|
|
25
25
|
}
|
|
26
|
-
async exportLocalData(now = new Date()) {
|
|
26
|
+
async exportLocalData(now = new Date()) { return this.exportLocalDataSync(now); }
|
|
27
|
+
exportLocalDataSync(now = this.now()) {
|
|
27
28
|
return this.db.transaction(() => {
|
|
28
29
|
const envelope = {
|
|
29
|
-
schemaVersion: DB_USER_VERSION,
|
|
30
30
|
format: LOCAL_DATA_EXPORT_FORMAT,
|
|
31
31
|
network: LOCAL_DATA_NETWORK,
|
|
32
32
|
exportedAt: now.toISOString(),
|
|
@@ -48,32 +48,46 @@ export class SqliteLocalDataService {
|
|
|
48
48
|
});
|
|
49
49
|
})();
|
|
50
50
|
}
|
|
51
|
-
async previewImportLocalData(input) {
|
|
51
|
+
async previewImportLocalData(input) { return this.previewImportLocalDataSync(input); }
|
|
52
|
+
previewImportLocalDataSync(input, now = this.now()) {
|
|
52
53
|
const envelope = parseLocalDataEnvelope(input, {
|
|
53
54
|
validateAdapterLifecycle: this.validateAdapterLifecycle
|
|
54
55
|
});
|
|
55
|
-
return {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
56
|
+
return this.db.transaction(() => {
|
|
57
|
+
this.options.advanceRequestDeadlines(now);
|
|
58
|
+
const currentAccounts = this.selectAll("accounts");
|
|
59
|
+
const currentContext = this.selectAll("active_account_context");
|
|
60
|
+
return {
|
|
61
|
+
status: "valid",
|
|
62
|
+
format: envelope.format,
|
|
63
|
+
network: envelope.network,
|
|
64
|
+
exportedAt: envelope.exportedAt,
|
|
65
|
+
currentCounts: this.getDataCountsSync(),
|
|
66
|
+
incomingCounts: countsForPayload(envelope.data),
|
|
67
|
+
willReplace: true,
|
|
68
|
+
activeAccountChange: activeAccountChange(currentAccounts, currentContext, envelope.data.accounts, envelope.data.activeAccountContext),
|
|
69
|
+
currentReadAddress: activeAccountAddress(currentAccounts, currentContext),
|
|
70
|
+
incomingReadAddress: activeAccountAddress(envelope.data.accounts, envelope.data.activeAccountContext),
|
|
71
|
+
restartRequiredAfterImport: true,
|
|
72
|
+
};
|
|
73
|
+
}).immediate();
|
|
66
74
|
}
|
|
67
75
|
async importLocalDataReplace(input, _now = new Date()) {
|
|
68
76
|
const envelope = await this.parseAndVerifyImportEnvelope(input);
|
|
69
|
-
this.db.transaction(() =>
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
+
const result = this.db.transaction(() => this.applyValidatedReplacement(envelope, this.now()))();
|
|
78
|
+
this.notifyDataReplaced();
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
async resetLocalData(now = this.now()) {
|
|
82
|
+
const result = this.db.transaction(() => this.applyValidatedReplacement(undefined, now))();
|
|
83
|
+
this.notifyDataReplaced();
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
86
|
+
applyValidatedReplacement(envelope, now, preserveCardId) {
|
|
87
|
+
this.options.advanceRequestDeadlines(now);
|
|
88
|
+
this.assertReplacementAllowed(now);
|
|
89
|
+
this.resetLocalDataTables(preserveCardId);
|
|
90
|
+
if (envelope) {
|
|
77
91
|
this.insertAccounts(envelope.data.accounts);
|
|
78
92
|
this.insertActiveAccountContext(envelope.data.activeAccountContext);
|
|
79
93
|
this.insertReviewSessions(envelope.data.reviewSessions);
|
|
@@ -85,39 +99,34 @@ export class SqliteLocalDataService {
|
|
|
85
99
|
this.insertExternalActivityTransactions(envelope.data.externalActivityTransactions);
|
|
86
100
|
this.insertLocalSettings(envelope.data.localSettings);
|
|
87
101
|
this.syncSqliteSequences(envelope.data);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return {
|
|
91
|
-
status: "imported",
|
|
92
|
-
dataCounts: this.getDataCountsSync(),
|
|
93
|
-
sessionsInvalidated: true
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
async resetLocalData(now = this.now()) {
|
|
97
|
-
this.db.transaction(() => {
|
|
98
|
-
this.options.advanceRequestDeadlines(now);
|
|
99
|
-
if (hasUnsettledWalletWork(this.db, now))
|
|
100
|
-
throw new LocalDataError("input_invalid", "A wallet request is still unsettled.", { reason: "wallet_request_unsettled" });
|
|
101
|
-
this.resetLocalDataTables();
|
|
102
|
+
}
|
|
103
|
+
else
|
|
102
104
|
this.insertDefaultSuiGrpcUrl(now);
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
sessionsInvalidated: true
|
|
109
|
-
};
|
|
105
|
+
this.db.prepare("UPDATE settings_control SET data_epoch=data_epoch+1 WHERE id=1").run();
|
|
106
|
+
if (preserveCardId)
|
|
107
|
+
this.db.prepare(`UPDATE settings_operations SET data_epoch=(SELECT data_epoch FROM settings_control WHERE id=1)
|
|
108
|
+
WHERE card_id=?`).run(preserveCardId);
|
|
109
|
+
return { status: envelope ? "imported" : "reset", dataCounts: this.getDataCountsSync(), sessionsInvalidated: true };
|
|
110
110
|
}
|
|
111
|
-
|
|
111
|
+
notifyDataReplaced() { this.options.onDataReplaced?.(); }
|
|
112
|
+
resetLocalDataTables(preserveCardId) {
|
|
113
|
+
this.db.prepare(`UPDATE settings_files SET invalidated=1 WHERE role='upload' OR ${SETTINGS_DOWNLOAD_FILE_SQL}`).run();
|
|
114
|
+
if (preserveCardId) {
|
|
115
|
+
const operation = this.db.prepare("SELECT operation_id FROM settings_operations WHERE card_id=?").get(preserveCardId);
|
|
116
|
+
if (!operation)
|
|
117
|
+
throw new LocalDataError("internal_error", "The replacement result identity is unavailable.");
|
|
118
|
+
this.db.prepare(`UPDATE settings_files SET cleanup_operation_id=? WHERE cleanup!='complete'
|
|
119
|
+
AND ${SETTINGS_FILE_CLEANUP_SQL}`).run(operation.operation_id);
|
|
120
|
+
this.db.prepare("DELETE FROM live_read_cards WHERE id!=?").run(preserveCardId);
|
|
121
|
+
}
|
|
122
|
+
else
|
|
123
|
+
this.db.prepare("DELETE FROM live_read_cards").run();
|
|
112
124
|
for (const table of [
|
|
113
|
-
"live_read_cards",
|
|
114
125
|
"live_execution_details",
|
|
115
126
|
"live_request_authority",
|
|
116
|
-
"live_wallet_connections",
|
|
117
127
|
"live_transaction_materials",
|
|
118
128
|
"live_private_review_artifacts",
|
|
119
129
|
"live_review_sessions",
|
|
120
|
-
"live_settings_sessions",
|
|
121
130
|
"external_activity_transactions",
|
|
122
131
|
"external_activity_scans",
|
|
123
132
|
"review_state_snapshots",
|
|
@@ -259,6 +268,10 @@ export class SqliteLocalDataService {
|
|
|
259
268
|
}
|
|
260
269
|
}
|
|
261
270
|
}
|
|
271
|
+
assertReplacementAllowed(now = this.now()) {
|
|
272
|
+
if (hasUnsettledWalletWork(this.db, now))
|
|
273
|
+
throw new LocalDataError("input_invalid", "A wallet request is still unsettled.", { reason: "wallet_request_unsettled" });
|
|
274
|
+
}
|
|
262
275
|
async parseAndVerifyImportEnvelope(input) {
|
|
263
276
|
const envelope = parseLocalDataEnvelope(input, {
|
|
264
277
|
validateAdapterLifecycle: this.validateAdapterLifecycle
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { transactionRequestStatusSchema, transactionRequestSchema } from "../session/transactionRequest.js";
|
|
2
2
|
import { REVIEW_TRANSITION_EVENTS } from "./sqliteActivityStoreRows.js";
|
|
3
|
-
import { DB_USER_VERSION } from "./schemaVersion.js";
|
|
4
3
|
import { z } from "zod";
|
|
5
4
|
import { assertNoForbiddenMcpFields } from "../action/forbiddenFields.js";
|
|
6
5
|
import { parseLifecycleValidatedReviewState } from "../action/reviewStateValidation.js";
|
|
@@ -144,7 +143,6 @@ const payloadSchema = z.object({
|
|
|
144
143
|
localSettings: z.array(localSettingRowSchema).length(2)
|
|
145
144
|
}).strict();
|
|
146
145
|
const envelopeSchema = z.object({
|
|
147
|
-
schemaVersion: z.literal(DB_USER_VERSION),
|
|
148
146
|
format: z.literal(LOCAL_DATA_EXPORT_FORMAT),
|
|
149
147
|
network: z.literal(LOCAL_DATA_NETWORK),
|
|
150
148
|
exportedAt: isoTimestamp,
|
|
@@ -427,7 +425,7 @@ export function activeAccountChange(currentAccounts, current, incomingAccounts,
|
|
|
427
425
|
}
|
|
428
426
|
return incomingAccount === null ? "cleared" : "set";
|
|
429
427
|
}
|
|
430
|
-
function activeAccountAddress(accounts, context) {
|
|
428
|
+
export function activeAccountAddress(accounts, context) {
|
|
431
429
|
const accountId = context[0]?.account_id ?? null;
|
|
432
430
|
if (accountId === null) {
|
|
433
431
|
return null;
|
|
@@ -17,7 +17,8 @@ import { SqlitePreferencesRepository } from "../preferences/sqlitePreferencesRep
|
|
|
17
17
|
import { SqliteTransactionMaterialStore } from "../session/sqliteTransactionMaterialStore.js";
|
|
18
18
|
import { SqliteCardRecordStore } from "../session/sqliteCardStore.js";
|
|
19
19
|
import { sameHandle } from "../session/transactionMaterialStore.js";
|
|
20
|
-
import { SqliteSessionRecordStore, SqlitePrivateReviewArtifactStore,
|
|
20
|
+
import { SqliteSessionRecordStore, SqlitePrivateReviewArtifactStore, sessionFromLiveReviewSessionRow, insertLiveReviewSessionRow, updateLiveReviewSessionRow } from "../session/sqliteSessionStore.js";
|
|
21
|
+
import { SqliteSettingsWorkflowStore } from "../session/sqliteSettingsWorkflowStore.js";
|
|
21
22
|
import { SqliteLocalDataService } from "./localDataService.js";
|
|
22
23
|
import { buildExternalActivityCoverageResult } from "./externalActivityCoverage.js";
|
|
23
24
|
import { buildExternalActivityTransactionStreamResult, canonicalExternalActivityTransactions } from "./externalActivityTransactionStream.js";
|
|
@@ -29,14 +30,11 @@ export { ActivityStoreError };
|
|
|
29
30
|
const ACTIVE_ACCOUNT_SINGLETON_ID = 1;
|
|
30
31
|
export const DATA_DIR_ENV = "SUI_MCP_DATA_DIR";
|
|
31
32
|
export const ACTIVITY_DATABASE_FILENAME = "sui-mcp.sqlite";
|
|
32
|
-
// Best-effort permission hardening. The owner-only data directory is the primary
|
|
33
|
-
// protection; failures (e.g. on Windows, which ignores POSIX modes) are non-fatal.
|
|
34
33
|
function restrictPathPermissions(path, mode) {
|
|
35
34
|
try {
|
|
36
35
|
chmodSync(path, mode);
|
|
37
36
|
}
|
|
38
37
|
catch {
|
|
39
|
-
// Non-fatal: the 0700 directory remains the primary protection.
|
|
40
38
|
}
|
|
41
39
|
}
|
|
42
40
|
export class SqliteActivityStore {
|
|
@@ -70,10 +68,6 @@ export class SqliteActivityStore {
|
|
|
70
68
|
this.db.close();
|
|
71
69
|
throw error;
|
|
72
70
|
}
|
|
73
|
-
// This database persists unsigned transaction material (Option B), so restrict it to
|
|
74
|
-
// the owner. The 0700 directory is the primary protection (it also covers the WAL/SHM
|
|
75
|
-
// sidecars and blocks other OS users); the 0600 file is belt-and-suspenders. Best-effort
|
|
76
|
-
// because some platforms (e.g. Windows) ignore POSIX modes.
|
|
77
71
|
restrictPathPermissions(dataDirectory, 0o700);
|
|
78
72
|
restrictPathPermissions(options.databasePath, 0o600);
|
|
79
73
|
}
|
|
@@ -117,7 +111,8 @@ export class SqliteActivityStore {
|
|
|
117
111
|
async getActiveAccount() {
|
|
118
112
|
return this.getActiveAccountSync();
|
|
119
113
|
}
|
|
120
|
-
async clearActiveAccount(now = new Date()) {
|
|
114
|
+
async clearActiveAccount(now = new Date()) { this.clearActiveAccountSync(now); }
|
|
115
|
+
clearActiveAccountSync(now = new Date()) {
|
|
121
116
|
this.db
|
|
122
117
|
.prepare(`INSERT INTO active_account_context (id, account_id, source, set_at)
|
|
123
118
|
VALUES (?, NULL, 'cleared', ?)
|
|
@@ -360,7 +355,6 @@ export class SqliteActivityStore {
|
|
|
360
355
|
(r.request_status!='completed' OR e.tx_digest!=r.transaction_digest OR json_valid(e.result_json)=0))) LIMIT 1`).get(...params);
|
|
361
356
|
if (inconsistent)
|
|
362
357
|
throw new ActivityStoreReadError("internal_error", "Stored review request evidence is inconsistent.", { reason: "invalid_stored_evidence" });
|
|
363
|
-
// Aggregation must not turn a malformed current result into a valid count.
|
|
364
358
|
for (const row of this.db.prepare(`SELECT rs.id,rs.current_attempt_id,a.sui_address AS account
|
|
365
359
|
FROM review_sessions rs JOIN accounts a ON a.id=rs.account_id ${where} AND rs.current_attempt_id IS NOT NULL`)
|
|
366
360
|
.all(...params)) {
|
|
@@ -657,11 +651,14 @@ export class SqliteActivityStore {
|
|
|
657
651
|
createCardRecordStore() {
|
|
658
652
|
return new SqliteCardRecordStore(this.db);
|
|
659
653
|
}
|
|
654
|
+
createSettingsWorkflowStore(ownerId, now = () => new Date()) {
|
|
655
|
+
return new SqliteSettingsWorkflowStore(this.db, ownerId, now);
|
|
656
|
+
}
|
|
660
657
|
createPrivateReviewArtifactStore() {
|
|
661
658
|
return new SqlitePrivateReviewArtifactStore(this.db);
|
|
662
659
|
}
|
|
663
|
-
createWalletWorkflowStore(ownerId, clock = () => new Date()) {
|
|
664
|
-
const records = new SqliteWalletWorkflowStore(this.db, ownerId, (address, id, name, now) => this.setActiveAccountSync(address, "wallet_connection", now, { id, name }), clock, this.createTransactionMaterialStore(), (input, at) => this.
|
|
660
|
+
createWalletWorkflowStore(ownerId, clock = () => new Date(), readSdkSnapshot = () => undefined) {
|
|
661
|
+
const records = new SqliteWalletWorkflowStore(this.db, ownerId, (address, id, name, now) => this.setActiveAccountSync(address, "wallet_connection", now, { id, name }), clock, this.createTransactionMaterialStore(), (input, at) => this.reconcileWorkflowState(input, records, ownerId, at === undefined ? clock : () => at), () => this.getActiveAccountSync(), readSdkSnapshot);
|
|
665
662
|
return records;
|
|
666
663
|
}
|
|
667
664
|
finalizeReviewEvaluation(candidate, clock) {
|
|
@@ -694,19 +691,26 @@ export class SqliteActivityStore {
|
|
|
694
691
|
return { session: next, events: next.status === "expired" ? [] : [{ type: "state.computed", sessionId: next.id,
|
|
695
692
|
status: next.status, reason: "private_review_artifacts_refresh_required", at: now.toISOString() }] };
|
|
696
693
|
}
|
|
697
|
-
|
|
694
|
+
reconcileWorkflowState(input, records, ownerId, clock) {
|
|
698
695
|
return this.db.transaction(() => {
|
|
699
696
|
const now = clock();
|
|
697
|
+
if (input.purpose !== "current_wallet" && input.purpose !== "review" ||
|
|
698
|
+
input.purpose === "review" && (!input.readTarget || !input.candidate)) {
|
|
699
|
+
throw new SessionStoreError("input_invalid", "An explicit workflow evaluation target is required.");
|
|
700
|
+
}
|
|
700
701
|
const cards = this.createCardRecordStore();
|
|
701
702
|
let record = input.expectedCard && records.currentCard(input.expectedCard);
|
|
702
|
-
const reviewId =
|
|
703
|
+
const reviewId = input.purpose === "review" ? input.reviewSessionId : undefined;
|
|
704
|
+
if (record && (input.purpose === "review" ? record.state.kind !== "review" || record.state.input.reviewSessionId !== reviewId : record.state.kind !== "connect")) {
|
|
705
|
+
throw new SessionStoreError("session_mismatch", "The workflow target does not match this card.");
|
|
706
|
+
}
|
|
703
707
|
const target = reviewId ? records.reviewReadTarget(reviewId, record) : undefined;
|
|
704
|
-
if (input.
|
|
708
|
+
if (input.purpose === "review" && (!target || input.readTarget.reviewSessionId !== target.reviewSessionId ||
|
|
705
709
|
input.readTarget.attemptId !== target.attemptId || !input.readTarget.walletDependent && target.walletDependent)) {
|
|
706
710
|
throw new SessionStoreError("session_mismatch", "The review target changed while its state was being read. Check this review again.");
|
|
707
711
|
}
|
|
708
712
|
let evaluated;
|
|
709
|
-
if (
|
|
713
|
+
if (input.purpose === "review") {
|
|
710
714
|
if (!input.candidate || input.candidate.session.id !== reviewId)
|
|
711
715
|
throw new SessionStoreError("session_not_found", "The saved review is unavailable.");
|
|
712
716
|
evaluated = this.finalizeReviewEvaluationAt(input.candidate, now);
|
|
@@ -726,14 +730,13 @@ export class SqliteActivityStore {
|
|
|
726
730
|
const authority = request && records.authority(request.attemptId);
|
|
727
731
|
const details = request?.execution && records.executionDetails(request.attemptId);
|
|
728
732
|
const connection = record?.state.kind === "connect" && record.operationId ? records.connection(record.operationId) : undefined;
|
|
729
|
-
const
|
|
730
|
-
const facts = {
|
|
733
|
+
const connectionFacts = records.readConnectionFacts(now), targetAccount = session?.account ?? connectionFacts.activeAccount;
|
|
734
|
+
const facts = { ...connectionFacts, ownerId, record, session, request, authority,
|
|
731
735
|
hasReviewInput: !!session && session.status !== "expired" && Date.parse(session.expiresAt) > now.getTime() &&
|
|
732
736
|
cards.hasReviewInput(session.id, ownerId, now),
|
|
733
|
-
walletAvailability: input.walletAvailability,
|
|
734
|
-
runtimeRecovery: record && records.walletRecovery(record),
|
|
737
|
+
walletAvailability: input.walletAvailability,
|
|
738
|
+
runtimeRecovery: record && records.walletRecovery(record),
|
|
735
739
|
walletDependent: !target || target.walletDependent,
|
|
736
|
-
connections: records.connectionViews(),
|
|
737
740
|
connection: connection ? records.connectionView(connection.connection) : undefined,
|
|
738
741
|
boundReview: request && (record?.operationId || record?.scope === "review_manage") ? records.requestReview(request.attemptId) : undefined,
|
|
739
742
|
busyForAccount: !!targetAccount && records.busyForAccount(targetAccount, now),
|
|
@@ -741,9 +744,6 @@ export class SqliteActivityStore {
|
|
|
741
744
|
return { ...facts, ...workflowEligibility(facts), events: evaluated?.events ?? [] };
|
|
742
745
|
}).immediate();
|
|
743
746
|
}
|
|
744
|
-
createSettingsRecordStore() {
|
|
745
|
-
return createSqliteSettingsRecordStore(this.db);
|
|
746
|
-
}
|
|
747
747
|
runLiveReviewSessionMutation(live, writeActivity) {
|
|
748
748
|
const stale = new Error("live review session changed before commit");
|
|
749
749
|
try {
|
|
@@ -315,8 +315,6 @@ export function parseActionPlanEvidence(plan) {
|
|
|
315
315
|
return parsed.data;
|
|
316
316
|
}
|
|
317
317
|
export function extractRequestedIntent(plan) {
|
|
318
|
-
// Adapter convention: full plan_json stays canonical, while requestedIntent
|
|
319
|
-
// is materialized only to make activity queries avoid nested JSON paths.
|
|
320
318
|
return plan.adapterData.requestedIntent;
|
|
321
319
|
}
|
|
322
320
|
export function reasonForReviewState(state) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Database from "better-sqlite3";
|
|
2
2
|
import { EXTERNAL_ACTIVITY_SCAN_MAX_LIMIT } from "./activityStore.js";
|
|
3
|
-
import { DB_USER_VERSION } from "./schemaVersion.js";
|
|
4
3
|
import { ActivityStoreError } from "./sqliteActivityStoreTypes.js";
|
|
4
|
+
import { SETTINGS_RESET_PREPARATION_SQL } from "../session/settingsPreparationSql.js";
|
|
5
5
|
import { LIVE_REVIEW_SESSION_INSERT_TRIGGER, LIVE_REVIEW_SESSION_UPDATE_TRIGGER, LIVE_REVIEW_SESSION_WRITE_CONTRACT_VERSION } from "../session/liveReviewSessionContract.js";
|
|
6
6
|
const EXTERNAL_ACTIVITY_SCAN_INDEX_SQL = `CREATE INDEX IF NOT EXISTS idx_external_activity_scans_account_fetched
|
|
7
7
|
ON external_activity_scans(account_id, fetched_at)`;
|
|
@@ -132,6 +132,7 @@ const CURRENT_SCHEMA_SQL = `
|
|
|
132
132
|
CREATE TABLE IF NOT EXISTS live_wallet_connections (
|
|
133
133
|
id TEXT PRIMARY KEY,
|
|
134
134
|
owner_id TEXT NOT NULL,
|
|
135
|
+
run_id TEXT,
|
|
135
136
|
status TEXT NOT NULL,
|
|
136
137
|
revision INTEGER NOT NULL CHECK (revision >= 0),
|
|
137
138
|
topic TEXT UNIQUE,
|
|
@@ -141,6 +142,7 @@ const CURRENT_SCHEMA_SQL = `
|
|
|
141
142
|
CREATE TABLE IF NOT EXISTS live_request_authority (
|
|
142
143
|
attempt_id TEXT PRIMARY KEY REFERENCES review_requests(attempt_id) ON DELETE RESTRICT,
|
|
143
144
|
owner_id TEXT NOT NULL,
|
|
145
|
+
run_id TEXT,
|
|
144
146
|
connection_id TEXT NOT NULL REFERENCES live_wallet_connections(id) ON DELETE RESTRICT,
|
|
145
147
|
connection_revision INTEGER NOT NULL,
|
|
146
148
|
can_submit INTEGER NOT NULL CHECK (can_submit IN (0,1)),
|
|
@@ -220,6 +222,7 @@ const CURRENT_SCHEMA_SQL = `
|
|
|
220
222
|
owner_id TEXT NOT NULL,
|
|
221
223
|
review_revision INTEGER NOT NULL DEFAULT 0,
|
|
222
224
|
preparation_id TEXT,
|
|
225
|
+
preparation_run_id TEXT,
|
|
223
226
|
preparation_error TEXT,
|
|
224
227
|
wallet_connection_id TEXT,
|
|
225
228
|
wallet_connection_revision INTEGER,
|
|
@@ -239,16 +242,11 @@ const CURRENT_SCHEMA_SQL = `
|
|
|
239
242
|
artifacts_json TEXT NOT NULL
|
|
240
243
|
);
|
|
241
244
|
|
|
242
|
-
CREATE TABLE IF NOT EXISTS live_settings_sessions (
|
|
243
|
-
id TEXT PRIMARY KEY,
|
|
244
|
-
session_json TEXT NOT NULL
|
|
245
|
-
);
|
|
246
|
-
|
|
247
245
|
CREATE TABLE IF NOT EXISTS live_read_cards (
|
|
248
246
|
id TEXT PRIMARY KEY,
|
|
249
247
|
owner_id TEXT NOT NULL,
|
|
250
248
|
token_hash TEXT NOT NULL,
|
|
251
|
-
kind TEXT NOT NULL CHECK (kind IN ('account', 'receipt', 'chart', 'connect', 'review')),
|
|
249
|
+
kind TEXT NOT NULL CHECK (kind IN ('account', 'receipt', 'chart', 'connect', 'review', 'settings')),
|
|
252
250
|
state TEXT NOT NULL CHECK (state IN ('ready', 'running', 'closed')),
|
|
253
251
|
reason TEXT CHECK (reason IN ('completed', 'expired', 'failed', 'server_restarted', 'cancelled')),
|
|
254
252
|
error TEXT,
|
|
@@ -256,7 +254,7 @@ const CURRENT_SCHEMA_SQL = `
|
|
|
256
254
|
created_at TEXT NOT NULL,
|
|
257
255
|
expires_at TEXT NOT NULL,
|
|
258
256
|
input_json TEXT NOT NULL,
|
|
259
|
-
scope TEXT NOT NULL DEFAULT 'read' CHECK (scope IN ('read', 'connect', 'review', 'review_manage')),
|
|
257
|
+
scope TEXT NOT NULL DEFAULT 'read' CHECK (scope IN ('read', 'connect', 'review', 'review_manage', 'settings')),
|
|
260
258
|
operation_id TEXT,
|
|
261
259
|
accepted_input_json TEXT,
|
|
262
260
|
result_json TEXT,
|
|
@@ -266,6 +264,59 @@ const CURRENT_SCHEMA_SQL = `
|
|
|
266
264
|
CHECK (receipt_display_json IS NULL OR (kind = 'receipt' AND reason = 'completed'))
|
|
267
265
|
);
|
|
268
266
|
CREATE INDEX IF NOT EXISTS idx_live_read_cards_owner_state ON live_read_cards(owner_id, state);
|
|
267
|
+
CREATE TABLE IF NOT EXISTS settings_control (
|
|
268
|
+
id INTEGER PRIMARY KEY CHECK (id=1),
|
|
269
|
+
data_epoch INTEGER NOT NULL DEFAULT 0 CHECK (data_epoch BETWEEN 0 AND 9007199254740991),
|
|
270
|
+
user_data_revision INTEGER NOT NULL DEFAULT 0 CHECK (user_data_revision BETWEEN 0 AND 9007199254740991),
|
|
271
|
+
active_operation_id TEXT,
|
|
272
|
+
file_io_id TEXT,
|
|
273
|
+
wallet_input_blocked INTEGER NOT NULL DEFAULT 0 CHECK (wallet_input_blocked IN (0,1)),
|
|
274
|
+
wallet_block_operation_id TEXT
|
|
275
|
+
);
|
|
276
|
+
INSERT INTO settings_control (id) VALUES (1);
|
|
277
|
+
CREATE TABLE IF NOT EXISTS settings_operations (
|
|
278
|
+
operation_id TEXT PRIMARY KEY,
|
|
279
|
+
card_id TEXT NOT NULL UNIQUE REFERENCES live_read_cards(id) ON DELETE CASCADE,
|
|
280
|
+
owner_id TEXT NOT NULL,
|
|
281
|
+
data_epoch INTEGER NOT NULL CHECK (data_epoch BETWEEN 0 AND 9007199254740991),
|
|
282
|
+
kind TEXT,
|
|
283
|
+
phase TEXT NOT NULL CHECK (phase IN ('draft','uploading','sealed','prepared','executing','stopping','starting','terminal')),
|
|
284
|
+
draft_json TEXT,
|
|
285
|
+
preparation_id TEXT,
|
|
286
|
+
fingerprint TEXT,
|
|
287
|
+
prepared_user_data_revision INTEGER,
|
|
288
|
+
preparation_json TEXT,
|
|
289
|
+
file_id TEXT,
|
|
290
|
+
accepted_digest TEXT,
|
|
291
|
+
admitted_at TEXT,
|
|
292
|
+
result_json TEXT,
|
|
293
|
+
prior_run_id TEXT,
|
|
294
|
+
next_run_id TEXT,
|
|
295
|
+
supersedes_operation_id TEXT,
|
|
296
|
+
superseded_by_operation_id TEXT,
|
|
297
|
+
checkpoint TEXT,
|
|
298
|
+
CHECK ((phase='terminal' AND result_json IS NOT NULL) OR (phase!='terminal' AND result_json IS NULL))
|
|
299
|
+
);
|
|
300
|
+
CREATE TRIGGER IF NOT EXISTS settings_immutable_result
|
|
301
|
+
BEFORE UPDATE ON settings_operations WHEN OLD.result_json IS NOT NULL AND NEW.result_json IS NOT OLD.result_json
|
|
302
|
+
BEGIN SELECT RAISE(ABORT, 'settings terminal result is immutable'); END;
|
|
303
|
+
CREATE TABLE IF NOT EXISTS settings_files (
|
|
304
|
+
file_id TEXT PRIMARY KEY,
|
|
305
|
+
operation_id TEXT NOT NULL,
|
|
306
|
+
cleanup_operation_id TEXT NOT NULL,
|
|
307
|
+
role TEXT NOT NULL CHECK (role IN ('upload','export')),
|
|
308
|
+
relative_path TEXT NOT NULL UNIQUE,
|
|
309
|
+
display_name TEXT NOT NULL,
|
|
310
|
+
total_size INTEGER NOT NULL CHECK (total_size BETWEEN 0 AND 9007199254740991),
|
|
311
|
+
received_size INTEGER NOT NULL CHECK (received_size BETWEEN 0 AND total_size),
|
|
312
|
+
sha256 TEXT NOT NULL,
|
|
313
|
+
sealed INTEGER NOT NULL DEFAULT 0 CHECK (sealed IN (0,1)),
|
|
314
|
+
invalidated INTEGER NOT NULL DEFAULT 0 CHECK (invalidated IN (0,1)),
|
|
315
|
+
cleanup TEXT NOT NULL DEFAULT 'pending' CHECK (cleanup IN ('pending','complete','failed')),
|
|
316
|
+
reservation_json TEXT
|
|
317
|
+
);
|
|
318
|
+
${settingsRevisionTriggers()}
|
|
319
|
+
|
|
269
320
|
CREATE TRIGGER IF NOT EXISTS live_read_cards_insert_revision
|
|
270
321
|
BEFORE INSERT ON live_read_cards WHEN NEW.revision != 0
|
|
271
322
|
BEGIN SELECT RAISE(ABORT, 'card insert requires revision zero'); END;
|
|
@@ -302,8 +353,6 @@ export function configureDatabase(db) {
|
|
|
302
353
|
db.exec("PRAGMA foreign_keys=ON");
|
|
303
354
|
db.exec("PRAGMA busy_timeout=5000");
|
|
304
355
|
}
|
|
305
|
-
// SQLite normalizes its own DDL. Compare only schema metadata, never user rows.
|
|
306
|
-
// The reference is built once from the single current schema, not from the DB being checked.
|
|
307
356
|
let currentSchemaSignature;
|
|
308
357
|
function schemaSignature(db) {
|
|
309
358
|
return JSON.stringify(db.prepare("SELECT type, name, tbl_name, sql FROM sqlite_schema WHERE name NOT LIKE 'sqlite_%' ORDER BY type, name").all());
|
|
@@ -322,11 +371,10 @@ function expectedSchemaSignature() {
|
|
|
322
371
|
}
|
|
323
372
|
}
|
|
324
373
|
export function assertCurrentDatabaseFormat(db) {
|
|
325
|
-
const version = db.pragma("user_version", { simple: true });
|
|
326
374
|
const signature = schemaSignature(db);
|
|
327
|
-
if (
|
|
375
|
+
if (signature === "[]" && !db.prepare("SELECT 1 FROM sqlite_schema LIMIT 1").get())
|
|
328
376
|
return "empty";
|
|
329
|
-
if (
|
|
377
|
+
if (signature === expectedSchemaSignature())
|
|
330
378
|
return "current";
|
|
331
379
|
throw new ActivityStoreError("Local database format does not match this runtime. Use a new empty SUI_MCP_DATA_DIR; existing data is not converted or deleted.");
|
|
332
380
|
}
|
|
@@ -335,7 +383,6 @@ export function initializeDatabase(db) {
|
|
|
335
383
|
return;
|
|
336
384
|
db.transaction(() => {
|
|
337
385
|
db.exec(CURRENT_SCHEMA_SQL);
|
|
338
|
-
db.pragma(`user_version = ${DB_USER_VERSION}`);
|
|
339
386
|
})();
|
|
340
387
|
}
|
|
341
388
|
function externalActivityScansTableSql() {
|
|
@@ -365,3 +412,23 @@ function externalActivityScansTableSql() {
|
|
|
365
412
|
)
|
|
366
413
|
)`;
|
|
367
414
|
}
|
|
415
|
+
function settingsRevisionTriggers() {
|
|
416
|
+
const userTables = ["accounts", "active_account_context", "review_sessions", "review_state_snapshots",
|
|
417
|
+
"review_status_transitions", "review_executions", "review_requests", "external_activity_scans",
|
|
418
|
+
"external_activity_transactions", "local_settings"];
|
|
419
|
+
const walletTables = ["live_wallet_connections", "live_review_sessions", "live_request_authority"];
|
|
420
|
+
const invalidate = (condition) => `
|
|
421
|
+
UPDATE live_read_cards SET revision=revision+1
|
|
422
|
+
WHERE id IN (SELECT card_id FROM settings_operations WHERE phase='prepared' AND (${condition}));
|
|
423
|
+
${SETTINGS_RESET_PREPARATION_SQL}
|
|
424
|
+
WHERE phase='prepared' AND (${condition});`;
|
|
425
|
+
return [...new Set([...userTables, ...walletTables])].flatMap((table) => ["INSERT", "UPDATE", "DELETE"].map((event) => `
|
|
426
|
+
CREATE TRIGGER settings_revision_${table}_${event.toLowerCase()} AFTER ${event} ON ${table}
|
|
427
|
+
BEGIN
|
|
428
|
+
${userTables.includes(table) ? "UPDATE settings_control SET user_data_revision=user_data_revision+1 WHERE id=1;" : ""}
|
|
429
|
+
${invalidate([...(userTables.includes(table) ? ["kind IN ('import_local_data','reset_local_data')"] : []),
|
|
430
|
+
...(walletTables.includes(table) || table === "review_requests" ? ["kind='restart_wallet_service'"] : []),
|
|
431
|
+
...(table === "local_settings" ? ["kind IN ('set_endpoint','restore_endpoint')"] : []),
|
|
432
|
+
...(table === "active_account_context" ? ["kind='clear_read_address'"] : [])].join(" OR ") || "0")}
|
|
433
|
+
END;`)).join("\n");
|
|
434
|
+
}
|
|
@@ -56,7 +56,8 @@ export class SqlitePreferencesRepository {
|
|
|
56
56
|
async resetSuiGraphqlUrl(defaultUrl, now = new Date()) {
|
|
57
57
|
return this.setSuiGraphqlUrl(defaultUrl, now);
|
|
58
58
|
}
|
|
59
|
-
async getLocalSetting(key) {
|
|
59
|
+
async getLocalSetting(key) { return this.getLocalSettingSync(key); }
|
|
60
|
+
getLocalSettingSync(key) {
|
|
60
61
|
const settingKey = assertLocalSettingKey(key);
|
|
61
62
|
const row = this.db
|
|
62
63
|
.prepare(`SELECT key, value_json, updated_at
|
|
@@ -65,7 +66,8 @@ export class SqlitePreferencesRepository {
|
|
|
65
66
|
.get(settingKey);
|
|
66
67
|
return row ? rowToLocalSetting(row) : undefined;
|
|
67
68
|
}
|
|
68
|
-
async setLocalSetting(key, value, now = new Date()) {
|
|
69
|
+
async setLocalSetting(key, value, now = new Date()) { return this.setLocalSettingSync(key, value, now); }
|
|
70
|
+
setLocalSettingSync(key, value, now = new Date()) {
|
|
69
71
|
const settingKey = assertLocalSettingKey(key);
|
|
70
72
|
this.db
|
|
71
73
|
.prepare(`INSERT INTO local_settings (key, value_json, updated_at)
|
|
@@ -74,7 +76,7 @@ export class SqlitePreferencesRepository {
|
|
|
74
76
|
value_json = excluded.value_json,
|
|
75
77
|
updated_at = excluded.updated_at`)
|
|
76
78
|
.run(settingKey, encodeSettingValue(settingKey, value), now.toISOString());
|
|
77
|
-
const record =
|
|
79
|
+
const record = this.getLocalSettingSync(settingKey);
|
|
78
80
|
if (!record) {
|
|
79
81
|
throw new PreferencesStoreError("not_recorded", "Local setting was not recorded", { key: settingKey });
|
|
80
82
|
}
|
|
@@ -192,8 +192,6 @@ export function normalizeManagerAddresses(values) {
|
|
|
192
192
|
return normalized;
|
|
193
193
|
});
|
|
194
194
|
}
|
|
195
|
-
// Account inventory v1 exposes only ledger and rebate balances; governance,
|
|
196
|
-
// stake, volume, and AccountInfo.open_orders need separate review before export.
|
|
197
195
|
export function toDeepbookAccountSummary(accountInfo) {
|
|
198
196
|
return {
|
|
199
197
|
epoch: accountInfo.epoch,
|
|
@@ -2,24 +2,7 @@ import { mainnetCoins, mainnetPools } from "@mysten/deepbook-v3";
|
|
|
2
2
|
import { DEEPBOOK_SCALAR_UNIT_SOURCE, decimalsFromScalar, normalizeCoinType } from "./coinMetadata.js";
|
|
3
3
|
import { ReadServiceInputError } from "./readServiceTypes.js";
|
|
4
4
|
export const PINNED_DEEPBOOK_COINS = mainnetCoins;
|
|
5
|
-
// Curated DeepBook USDC chart pairs: the meaningful Sui-ecosystem base tokens worth
|
|
6
|
-
// featuring against USDC, in display order. This is a deliberate editorial allowlist
|
|
7
|
-
// — NOT a dynamic volume cut — so the chart shows a stable, hand-picked set and never
|
|
8
|
-
// surfaces the dead/dust pools the indexer also lists (stable-vs-stable, wrapped
|
|
9
|
-
// duplicates, single-trade listings). Each entry is a token judged worth registering
|
|
10
|
-
// on its own merits:
|
|
11
|
-
// SUI — Sui L1 gas / native token
|
|
12
|
-
// DEEP — DeepBook protocol token (this venue's own token)
|
|
13
|
-
// WAL — Walrus decentralized storage (Mysten)
|
|
14
|
-
// NS — SuiNS name service
|
|
15
|
-
// IKA — Ika MPC / dWallet network
|
|
16
|
-
// XBTC — Bitcoin on Sui
|
|
17
|
-
// USDT — Tether USD stablecoin
|
|
18
|
-
// Edit this list to change which pairs the chart offers; order here is display order.
|
|
19
5
|
const CURATED_USDC_CHART_BASE_ORDER = new Map(["SUI", "DEEP", "WAL", "NS", "IKA", "XBTC", "USDT"].map((symbol, index) => [symbol, index]));
|
|
20
|
-
// Filter the chart pool list to the curated base tokens (by symbol) in curated
|
|
21
|
-
// display order. Matching is by base SYMBOL because the indexer's pool name can
|
|
22
|
-
// differ from the SDK pool key (e.g. BWETH_USDC vs BETH_USDC).
|
|
23
6
|
export function curateUsdcChartPools(pools) {
|
|
24
7
|
return pools
|
|
25
8
|
.filter((pool) => CURATED_USDC_CHART_BASE_ORDER.has(pool.baseAsset.symbol))
|
|
@@ -52,8 +35,6 @@ export function deepbookUnitForCoinType(coinType, coins) {
|
|
|
52
35
|
return { decimals, symbol: entry.symbol, name: entry.symbol };
|
|
53
36
|
}
|
|
54
37
|
export function findDeepbookCoinEntry(normalizedCoinType, coins) {
|
|
55
|
-
// Validate the whole pinned token unit registry before using any DeepBook
|
|
56
|
-
// coin match so one bad scalar cannot make the registry look partially trustworthy.
|
|
57
38
|
assertDeepbookScalars(coins);
|
|
58
39
|
for (const [symbol, coin] of Object.entries(coins)) {
|
|
59
40
|
if (normalizeCoinType(coin.type) === normalizedCoinType) {
|
|
@@ -35,8 +35,6 @@ export function createDeepbookUsdcChartService(options = {}) {
|
|
|
35
35
|
...chartCommonFields()
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
|
-
// Curate to the hand-picked allowlist of meaningful pairs (SUI/USDC, DEEP/USDC,
|
|
39
|
-
// …), in display order; the indexer's dead/dust pools never appear.
|
|
40
38
|
const pools = curateUsdcChartPools(selectDeepbookOfficialIndexerCanonicalUsdcPools(poolResult.pools).map(deepbookUsdcPriceHistoryPairFromOfficialPool));
|
|
41
39
|
return {
|
|
42
40
|
status: "ok",
|
|
@@ -12,8 +12,6 @@ const foundReceipt = z.object({ status: z.literal("found"), receipt: z.object({
|
|
|
12
12
|
inputs: z.array(receiptInput).refine((items) => new Set(items.map((item) => item.index)).size === items.length),
|
|
13
13
|
ptbGraph: receiptDisplaySchema.shape.ptbGraph
|
|
14
14
|
}).passthrough() }).strict();
|
|
15
|
-
// Partition the same source result before persistence. Never weaken the public
|
|
16
|
-
// forbidden-field check or turn arbitrary fields into private metadata.
|
|
17
15
|
export function projectReadCardResult(kind, input, source) {
|
|
18
16
|
if (kind !== "receipt" || !source || typeof source !== "object" || !("status" in source) || source.status !== "found") {
|
|
19
17
|
assertNoForbiddenMcpFields(source);
|