Changeset 3788796
- Timestamp:
- 11/06/2026 10:15:08 PM (less than one hour ago)
- Location:
- botcreds-agent-memory
- Files:
-
- 16 added
- 14 edited
- 1 copied
-
tags/3.2.0 (copied) (copied from botcreds-agent-memory/trunk )
-
tags/3.2.0/adapters/reference/outbox.py (modified) (6 diffs)
-
tags/3.2.0/adapters/reference/processing.py (added)
-
tags/3.2.0/adapters/reference/processor_command.py (added)
-
tags/3.2.0/botcreds-agent-memory.php (modified) (6 diffs)
-
tags/3.2.0/docs (added)
-
tags/3.2.0/docs/suggestions.md (added)
-
tags/3.2.0/includes/class-applications.php (added)
-
tags/3.2.0/includes/class-db.php (modified) (2 diffs)
-
tags/3.2.0/includes/class-mcp.php (modified) (1 diff)
-
tags/3.2.0/includes/class-native-processor.php (added)
-
tags/3.2.0/includes/class-proposals.php (added)
-
tags/3.2.0/includes/class-rest-api.php (modified) (1 diff)
-
tags/3.2.0/includes/class-suggestions.php (added)
-
tags/3.2.0/includes/class-sync.php (modified) (5 diffs)
-
tags/3.2.0/readme.txt (modified) (4 diffs)
-
trunk/adapters/reference/outbox.py (modified) (6 diffs)
-
trunk/adapters/reference/processing.py (added)
-
trunk/adapters/reference/processor_command.py (added)
-
trunk/botcreds-agent-memory.php (modified) (6 diffs)
-
trunk/docs (added)
-
trunk/docs/suggestions.md (added)
-
trunk/includes/class-applications.php (added)
-
trunk/includes/class-db.php (modified) (2 diffs)
-
trunk/includes/class-mcp.php (modified) (1 diff)
-
trunk/includes/class-native-processor.php (added)
-
trunk/includes/class-proposals.php (added)
-
trunk/includes/class-rest-api.php (modified) (1 diff)
-
trunk/includes/class-suggestions.php (added)
-
trunk/includes/class-sync.php (modified) (5 diffs)
-
trunk/readme.txt (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
botcreds-agent-memory/tags/3.2.0/adapters/reference/outbox.py
r3788734 r3788796 124 124 (scope, resource, identity)).fetchone() is not None 125 125 126 def _purge_scope_derivatives(self, scope): 127 """Extension point; called inside the same SQLite transaction as source purge.""" 128 129 def _purge_session_derivatives(self, scope, identity): 130 """Extension point for managed processing drafts.""" 131 132 def _purge_resource_derivatives(self, scope, resource, identity): 133 """Extension point for explicitly negotiated processing invalidations.""" 134 126 135 def _purge_session(self, scope, identity): 127 136 # Caller holds the SQLite transaction. Retain only an opaque permanent tombstone. … … 137 146 self.db.execute("INSERT OR IGNORE INTO deleted_resources VALUES (?,'checkpoint',?)", (scope, row["identity"])) 138 147 self.db.execute("DELETE FROM checkpoints WHERE identity=?", (row["identity"],)) 148 149 self._purge_session_derivatives(scope, identity) 139 150 140 151 def delete_conversation(self, scope, source, session_id, *, confirm_session_id, send=None): … … 225 236 self.db.execute("DELETE FROM queue WHERE scope=?", (scope,)) 226 237 self.db.execute("DELETE FROM checkpoints WHERE scope=?", (scope,)) 238 self._purge_scope_derivatives(scope) 227 239 return dict(self.status(scope), local_storage_maintenance=self._storage_cleanup(), 228 240 backup_copies="not_purged", secure_physical_erasure="not_claimed") … … 255 267 if (not isinstance(change, dict) or type(change.get("id")) not in (int, str) 256 268 or not re.fullmatch(r"[0-9]{1,18}", str(change["id"])) or int(change["id"]) <= last 257 or change.get("resource") not in ("archive", "checkpoint", "session", "artifact", "scope")258 269 or (change.get("operation"), change.get("resource")) not in ( 259 270 ("stored", "archive"), ("stored", "checkpoint"), ("stored", "session"), ("stored", "artifact"), 260 ("retired", "scope"), ("deleted", "session"), ("deleted", "checkpoint"), ("deleted", "artifact")) 271 ("retired", "scope"), ("deleted", "session"), ("deleted", "checkpoint"), ("deleted", "artifact"), 272 ("stored", "proposal"), ("deleted", "proposal")) 261 273 or not isinstance(change.get("identity_hash"), str) 262 274 or not re.fullmatch(r"[a-f0-9]{64}", change["identity_hash"])): … … 276 288 elif change["resource"] == "checkpoint": 277 289 self.db.execute("DELETE FROM checkpoints WHERE scope=? AND identity=?", (scope, identity)) 290 self._purge_resource_derivatives(scope, change["resource"], identity) 278 291 # The entire page and its cursor commit together, including deletion content removal. 279 292 if reply["retired"]: … … 283 296 self.db.execute("DELETE FROM queue WHERE scope=?", (scope,)) 284 297 self.db.execute("DELETE FROM checkpoints WHERE scope=?", (scope,)) 298 self._purge_scope_derivatives(scope) 285 299 purged = True 286 300 self.db.execute("INSERT OR REPLACE INTO change_cursors VALUES (?,?)", (scope, reply["next_cursor"])) -
botcreds-agent-memory/tags/3.2.0/botcreds-agent-memory.php
r3788734 r3788796 4 4 * Plugin URI: https://botcreds.com/ 5 5 * Description: Portable memory store for AI agents. REST API + MCP endpoint with entry relationships. KV mode by default, semantic vector search when OpenAI key is configured. 6 * Version: 3. 1.06 * Version: 3.2.0 7 7 * Author: Joe Boydston 8 8 * Author URI: https://botcreds.com … … 18 18 } 19 19 20 define( 'BOTCREDS_MEMORY_VERSION', '3. 1.0' );20 define( 'BOTCREDS_MEMORY_VERSION', '3.2.0' ); 21 21 define( 'BOTCREDS_MEMORY_FILE', __FILE__ ); 22 22 define( 'BOTCREDS_MEMORY_DIR', plugin_dir_path( __FILE__ ) ); … … 34 34 require_once BOTCREDS_MEMORY_DIR . 'includes/class-sessions.php'; 35 35 require_once BOTCREDS_MEMORY_DIR . 'includes/class-artifacts.php'; 36 require_once BOTCREDS_MEMORY_DIR . 'includes/class-proposals.php'; 37 require_once BOTCREDS_MEMORY_DIR . 'includes/class-native-processor.php'; 38 require_once BOTCREDS_MEMORY_DIR . 'includes/class-applications.php'; 39 require_once BOTCREDS_MEMORY_DIR . 'includes/class-suggestions.php'; 36 40 require_once BOTCREDS_MEMORY_DIR . 'includes/class-rest-api.php'; 37 41 require_once BOTCREDS_MEMORY_DIR . 'includes/class-mcp.php'; … … 49 53 register_activation_hook( BOTCREDS_MEMORY_FILE, array( 'Botcreds_Memory_Sessions', 'install' ) ); 50 54 register_activation_hook( BOTCREDS_MEMORY_FILE, array( 'Botcreds_Memory_Artifacts', 'install' ) ); 55 register_activation_hook( BOTCREDS_MEMORY_FILE, array( 'Botcreds_Memory_Proposals', 'install' ) ); 51 56 register_activation_hook( BOTCREDS_MEMORY_FILE, array( 'Botcreds_Memory_DB', 'backfill_namespaces' ) ); 52 57 register_activation_hook( BOTCREDS_MEMORY_FILE, array( 'Botcreds_Memory_Hardening', 'on_activation' ) ); … … 84 89 Botcreds_Memory_DB::create_table(); 85 90 Botcreds_Memory_DB::backfill_namespaces(); 91 } 92 if ( get_option( 'botcreds_memory_proposal_schema', '' ) !== '1' ) { 93 Botcreds_Memory_Proposals::install(); 86 94 } 87 95 if ( get_option( 'botcreds_memory_sync_schema', '' ) !== '2' ) { … … 123 131 add_action( 'rest_api_init', array( 'Botcreds_Memory_Sessions', 'register_routes' ) ); 124 132 add_action( 'rest_api_init', array( 'Botcreds_Memory_Artifacts', 'register_routes' ) ); 133 add_action( 'rest_api_init', array( 'Botcreds_Memory_Proposals', 'register_routes' ) ); 134 add_action( 'rest_api_init', array( 'Botcreds_Memory_Native_Processor', 'register_routes' ) ); 135 add_action( 'rest_api_init', array( 'Botcreds_Memory_Applications', 'register_routes' ) ); 136 // Register after the parent menu so WordPress assigns the correct page hook and URL. 137 add_action( 'admin_menu', array( 'Botcreds_Memory_Suggestions', 'menu' ), 20 ); 125 138 126 139 /* -
botcreds-agent-memory/tags/3.2.0/includes/class-db.php
r3776180 r3788796 281 281 282 282 $key = sanitize_text_field( $data['key'] ?? '' ); 283 // Managed application keys may only be written through the reviewed proposal pipeline. 284 if ( class_exists( 'Botcreds_Memory_Applications', false ) && Botcreds_Memory_Applications::managed_key( $key ) ) { return null; } 283 285 $value = $data['value'] ?? ''; 284 286 … … 288 290 289 291 $existing = self::get_by_key( $key, true ); 292 // Match the stored key too: database collations may equate accented/case variants. 293 if ( $existing && class_exists( 'Botcreds_Memory_Applications', false ) && Botcreds_Memory_Applications::managed_key( $existing['key'] ) ) { return null; } 290 294 $user = wp_get_current_user(); 291 295 $default_author = $existing['author'] ?? ''; -
botcreds-agent-memory/tags/3.2.0/includes/class-mcp.php
r3776180 r3788796 368 368 } 369 369 370 if ( class_exists( 'Botcreds_Memory_Applications', false ) && Botcreds_Memory_Applications::protected_key( $key ) ) { 371 return self::tool_error( $id, 'Revise, review and apply the originating proposal instead of overwriting managed memory.' ); 372 } 373 370 374 $data = array( 371 375 'key' => $key, -
botcreds-agent-memory/tags/3.2.0/includes/class-rest-api.php
r3776180 r3788796 360 360 } 361 361 362 if ( class_exists( 'Botcreds_Memory_Applications', false ) && Botcreds_Memory_Applications::protected_key( $key ) ) { 363 return new WP_Error( 'botcreds_memory_managed_key', 'Revise, review and apply the originating proposal instead of overwriting managed memory.', array( 'status' => 409 ) ); 364 } 365 362 366 $data = array( 363 367 'key' => $key, -
botcreds-agent-memory/tags/3.2.0/includes/class-sync.php
r3788734 r3788796 8 8 private static function content_tables(): array { 9 9 global $wpdb; 10 return array_map( function ( $suffix ) use ( $wpdb ) { return $wpdb->prefix . 'botcreds_memory_' . $suffix; }, array( 'archive', 'checkpoints', 'sessions', 'artifacts' ) ); 10 11 $suffixes = array( 'archive', 'checkpoints', 'sessions', 'artifacts' ); 12 if ( class_exists( 'Botcreds_Memory_Proposals', false ) ) { $suffixes[] = 'proposals'; } 13 return array_map( function ( $suffix ) use ( $wpdb ) { return $wpdb->prefix . 'botcreds_memory_' . $suffix; }, $suffixes ); 11 14 } 12 15 // Schema setup uses fixed plugin-owned table identifiers; WordPress has no custom-table schema/cache API. … … 55 58 private static function transactional(): bool { 56 59 global $wpdb; 57 foreach ( array_merge( self::content_tables(), array( self::table(), self::changes_table() ) ) as $table ) { 60 $tables = array_merge( self::content_tables(), array( self::table(), self::changes_table() ) ); 61 if ( class_exists( 'Botcreds_Memory_Applications', false ) ) { $tables = array_merge( $tables, array( Botcreds_Memory_DB::table_name(), Botcreds_Memory_DB::revisions_table_name(), Botcreds_Memory_DB::relationships_table_name() ) ); } 62 foreach ( $tables as $table ) { 58 63 $engine = $wpdb->get_var( $wpdb->prepare( 'SELECT ENGINE FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=%s', $table ) ); 59 64 if ( ! is_string( $engine ) || strtolower( $engine ) !== 'innodb' ) { return false; } … … 140 145 $identity = self::session_identity( $scope, $source, $session ); 141 146 if ( ! self::deleted( $scope, 'session', $identity ) ) { 147 if ( class_exists( 'Botcreds_Memory_Proposals', false ) ) { Botcreds_Memory_Proposals::purge_session( $scope, $source, $session ); } 142 148 // Stream associated records in bounded pages. Hash verification precedes any destructive write. 143 149 foreach ( array( 'checkpoint' => Botcreds_Memory_Checkpoints::table(), 'artifact' => Botcreds_Memory_Artifacts::table() ) as $kind => $table ) { … … 200 206 global $wpdb; $table = self::table(); 201 207 // Replays purge again safely; retirement is permanent even if a delayed uploader retries. 208 if ( class_exists( 'Botcreds_Memory_Applications', false ) ) { Botcreds_Memory_Applications::purge_scope( $scope ); } 202 209 foreach ( self::content_tables() as $content ) { 203 210 if ( false === $wpdb->query( $wpdb->prepare( "DELETE FROM `{$content}` WHERE BINARY scope=BINARY %s", $scope ) ) ) { throw new RuntimeException( 'purge failed' ); } … … 242 249 return new WP_Error( 'sync_cursor_invalid', 'Invalid change cursor.', array( 'status' => 400 ) ); 243 250 } 251 $processing = $request->get_param( 'include_processing' ) ?? '0'; 252 if ( ! in_array( $processing, array( '0', '1', 0, 1 ), true ) ) { return new WP_Error( 'sync_processing_invalid', 'include_processing must be 0 or 1.', array( 'status' => 400 ) ); } 244 253 $table = self::changes_table(); 245 $rows = $wpdb->get_results( $wpdb->prepare( "SELECT id,resource,identity_hash,operation,created_at FROM `{$table}` WHERE BINARY scope=BINARY %s AND id>%d ORDER BY id ASC LIMIT 20", $scope, (int) $after), ARRAY_A );254 $rows = $wpdb->get_results( $wpdb->prepare( "SELECT id,resource,identity_hash,operation,created_at FROM `{$table}` WHERE BINARY scope=BINARY %s AND id>%d AND (%d=1 OR resource<>'proposal') ORDER BY id ASC LIMIT 20", $scope, (int) $after, (int) $processing ), ARRAY_A ); 246 255 if ( ! empty( $wpdb->last_error ) ) { return new WP_Error( 'sync_storage_failed', 'Change feed unavailable.', array( 'status' => 503 ) ); } 247 256 foreach ( $rows ?: array() as $row ) { $after = (string) $row['id']; } -
botcreds-agent-memory/tags/3.2.0/readme.txt
r3788734 r3788796 5 5 Tested up to: 7.1 6 6 Requires PHP: 7.4 7 Stable tag: 3. 1.07 Stable tag: 3.2.0 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 24 24 Save explicitly selected original conversation text outside a chat tool, with resumable uploads, session records, artifact references and compaction checkpoints. Retrieve original passages and search archive text within an explicitly enrolled scope. The included reference client reads operator-reviewed JSONL files; it is not an automatic OpenClaw or Claude Code collector. 25 25 26 Manually delete a conversation and its managed archive/checkpoint/session/artifact records, with permanent deletion markers that prevent stale clients from re-uploading it. No automatic age-based forgetting is enabled. Curated KV memory, external files and backups are not deleted by archive deletion.26 Manually delete a conversation and its managed archive/checkpoint/session/artifact records, with permanent deletion markers that prevent stale clients from re-uploading it. No automatic age-based forgetting is enabled. Independent curated KV memory, external files and backups are not deleted by archive deletion. In 3.2, managed derived memories tied to deleted sources are purged as described below. 27 27 28 28 Archive access is separate from shared-memory grants: a host administrator must explicitly enroll each scope/user, including administrators, using `wp agent-memory archive-scope project/name USER_ID`. Server sync operations require InnoDB tables. Checkpoints verify submitted archive ranges, not completeness of native client capture. No original conversations are captured or uploaded automatically. 29 30 **Suggestions and evidence-backed memory (3.2):** 31 32 Agent Memory → Suggestions provides original-source selection, optional WordPress AI Client/Connectors generation, an evidence-linked review inbox, immutable revisions, and explicit application of approved facts/decisions to current shared memory. Approval alone does not publish a memory. Generated Skill files are never installed or executed. 33 34 Archive scopes require explicit host enrollment, separate from curated-memory grants, including for administrators. Native generation requires compatible WordPress AI Client/Connectors APIs and a configured provider; basic storage/search and external proposal clients do not. Applying a suggestion publishes into the shared curated store, whose readers may differ from archive participants. Source deletion also purges managed derived memory. Automatic conversation capture is not included. 29 35 30 36 **Key features:** … … 101 107 102 108 == Changelog == 109 110 = 3.2.0 = 111 * Add an evidence-linked Suggestions inbox with original-source inspection, approval, dismissal and immutable revisions. 112 * Add optional native processing previews using configured WordPress AI Client/Connectors providers, with explicit export/cost consent and no automatic saving. 113 * Apply approved facts/decisions explicitly to managed shared-store memory with safe replacements and idempotent application receipts. 114 * Purge managed derived memory and matching revision snapshots when their original source is deleted. Never restore superseded decisions automatically. 115 * Include cumulative 3.1 archive, session, artifact, checkpoint and deletion-aware sync foundations. Automatic OpenClaw/Claude Code capture is not included. 116 * Retain external processor support and basic storage/search without an AI provider. Generated Skill files are not installed or executed. 103 117 104 118 = 3.1.0 = … … 216 230 == Upgrade Notice == 217 231 232 = 3.2.0 = 233 Adds evidence-linked suggestions and explicit reviewed-memory application. Archive enrollment is separate from memory grants. Applying publishes to the shared store; source deletion purges managed derived memory. Automatic conversation capture is not included. 234 218 235 = 3.1.0 = 219 236 Adds opt-in conversation archives, checkpoint/session/artifact records, original-text retrieval and manual deletion. Archive scopes require explicit host enrollment, separate from memory grants. Automatic capture and age-based forgetting are not included. -
botcreds-agent-memory/trunk/adapters/reference/outbox.py
r3788734 r3788796 124 124 (scope, resource, identity)).fetchone() is not None 125 125 126 def _purge_scope_derivatives(self, scope): 127 """Extension point; called inside the same SQLite transaction as source purge.""" 128 129 def _purge_session_derivatives(self, scope, identity): 130 """Extension point for managed processing drafts.""" 131 132 def _purge_resource_derivatives(self, scope, resource, identity): 133 """Extension point for explicitly negotiated processing invalidations.""" 134 126 135 def _purge_session(self, scope, identity): 127 136 # Caller holds the SQLite transaction. Retain only an opaque permanent tombstone. … … 137 146 self.db.execute("INSERT OR IGNORE INTO deleted_resources VALUES (?,'checkpoint',?)", (scope, row["identity"])) 138 147 self.db.execute("DELETE FROM checkpoints WHERE identity=?", (row["identity"],)) 148 149 self._purge_session_derivatives(scope, identity) 139 150 140 151 def delete_conversation(self, scope, source, session_id, *, confirm_session_id, send=None): … … 225 236 self.db.execute("DELETE FROM queue WHERE scope=?", (scope,)) 226 237 self.db.execute("DELETE FROM checkpoints WHERE scope=?", (scope,)) 238 self._purge_scope_derivatives(scope) 227 239 return dict(self.status(scope), local_storage_maintenance=self._storage_cleanup(), 228 240 backup_copies="not_purged", secure_physical_erasure="not_claimed") … … 255 267 if (not isinstance(change, dict) or type(change.get("id")) not in (int, str) 256 268 or not re.fullmatch(r"[0-9]{1,18}", str(change["id"])) or int(change["id"]) <= last 257 or change.get("resource") not in ("archive", "checkpoint", "session", "artifact", "scope")258 269 or (change.get("operation"), change.get("resource")) not in ( 259 270 ("stored", "archive"), ("stored", "checkpoint"), ("stored", "session"), ("stored", "artifact"), 260 ("retired", "scope"), ("deleted", "session"), ("deleted", "checkpoint"), ("deleted", "artifact")) 271 ("retired", "scope"), ("deleted", "session"), ("deleted", "checkpoint"), ("deleted", "artifact"), 272 ("stored", "proposal"), ("deleted", "proposal")) 261 273 or not isinstance(change.get("identity_hash"), str) 262 274 or not re.fullmatch(r"[a-f0-9]{64}", change["identity_hash"])): … … 276 288 elif change["resource"] == "checkpoint": 277 289 self.db.execute("DELETE FROM checkpoints WHERE scope=? AND identity=?", (scope, identity)) 290 self._purge_resource_derivatives(scope, change["resource"], identity) 278 291 # The entire page and its cursor commit together, including deletion content removal. 279 292 if reply["retired"]: … … 283 296 self.db.execute("DELETE FROM queue WHERE scope=?", (scope,)) 284 297 self.db.execute("DELETE FROM checkpoints WHERE scope=?", (scope,)) 298 self._purge_scope_derivatives(scope) 285 299 purged = True 286 300 self.db.execute("INSERT OR REPLACE INTO change_cursors VALUES (?,?)", (scope, reply["next_cursor"])) -
botcreds-agent-memory/trunk/botcreds-agent-memory.php
r3788734 r3788796 4 4 * Plugin URI: https://botcreds.com/ 5 5 * Description: Portable memory store for AI agents. REST API + MCP endpoint with entry relationships. KV mode by default, semantic vector search when OpenAI key is configured. 6 * Version: 3. 1.06 * Version: 3.2.0 7 7 * Author: Joe Boydston 8 8 * Author URI: https://botcreds.com … … 18 18 } 19 19 20 define( 'BOTCREDS_MEMORY_VERSION', '3. 1.0' );20 define( 'BOTCREDS_MEMORY_VERSION', '3.2.0' ); 21 21 define( 'BOTCREDS_MEMORY_FILE', __FILE__ ); 22 22 define( 'BOTCREDS_MEMORY_DIR', plugin_dir_path( __FILE__ ) ); … … 34 34 require_once BOTCREDS_MEMORY_DIR . 'includes/class-sessions.php'; 35 35 require_once BOTCREDS_MEMORY_DIR . 'includes/class-artifacts.php'; 36 require_once BOTCREDS_MEMORY_DIR . 'includes/class-proposals.php'; 37 require_once BOTCREDS_MEMORY_DIR . 'includes/class-native-processor.php'; 38 require_once BOTCREDS_MEMORY_DIR . 'includes/class-applications.php'; 39 require_once BOTCREDS_MEMORY_DIR . 'includes/class-suggestions.php'; 36 40 require_once BOTCREDS_MEMORY_DIR . 'includes/class-rest-api.php'; 37 41 require_once BOTCREDS_MEMORY_DIR . 'includes/class-mcp.php'; … … 49 53 register_activation_hook( BOTCREDS_MEMORY_FILE, array( 'Botcreds_Memory_Sessions', 'install' ) ); 50 54 register_activation_hook( BOTCREDS_MEMORY_FILE, array( 'Botcreds_Memory_Artifacts', 'install' ) ); 55 register_activation_hook( BOTCREDS_MEMORY_FILE, array( 'Botcreds_Memory_Proposals', 'install' ) ); 51 56 register_activation_hook( BOTCREDS_MEMORY_FILE, array( 'Botcreds_Memory_DB', 'backfill_namespaces' ) ); 52 57 register_activation_hook( BOTCREDS_MEMORY_FILE, array( 'Botcreds_Memory_Hardening', 'on_activation' ) ); … … 84 89 Botcreds_Memory_DB::create_table(); 85 90 Botcreds_Memory_DB::backfill_namespaces(); 91 } 92 if ( get_option( 'botcreds_memory_proposal_schema', '' ) !== '1' ) { 93 Botcreds_Memory_Proposals::install(); 86 94 } 87 95 if ( get_option( 'botcreds_memory_sync_schema', '' ) !== '2' ) { … … 123 131 add_action( 'rest_api_init', array( 'Botcreds_Memory_Sessions', 'register_routes' ) ); 124 132 add_action( 'rest_api_init', array( 'Botcreds_Memory_Artifacts', 'register_routes' ) ); 133 add_action( 'rest_api_init', array( 'Botcreds_Memory_Proposals', 'register_routes' ) ); 134 add_action( 'rest_api_init', array( 'Botcreds_Memory_Native_Processor', 'register_routes' ) ); 135 add_action( 'rest_api_init', array( 'Botcreds_Memory_Applications', 'register_routes' ) ); 136 // Register after the parent menu so WordPress assigns the correct page hook and URL. 137 add_action( 'admin_menu', array( 'Botcreds_Memory_Suggestions', 'menu' ), 20 ); 125 138 126 139 /* -
botcreds-agent-memory/trunk/includes/class-db.php
r3776180 r3788796 281 281 282 282 $key = sanitize_text_field( $data['key'] ?? '' ); 283 // Managed application keys may only be written through the reviewed proposal pipeline. 284 if ( class_exists( 'Botcreds_Memory_Applications', false ) && Botcreds_Memory_Applications::managed_key( $key ) ) { return null; } 283 285 $value = $data['value'] ?? ''; 284 286 … … 288 290 289 291 $existing = self::get_by_key( $key, true ); 292 // Match the stored key too: database collations may equate accented/case variants. 293 if ( $existing && class_exists( 'Botcreds_Memory_Applications', false ) && Botcreds_Memory_Applications::managed_key( $existing['key'] ) ) { return null; } 290 294 $user = wp_get_current_user(); 291 295 $default_author = $existing['author'] ?? ''; -
botcreds-agent-memory/trunk/includes/class-mcp.php
r3776180 r3788796 368 368 } 369 369 370 if ( class_exists( 'Botcreds_Memory_Applications', false ) && Botcreds_Memory_Applications::protected_key( $key ) ) { 371 return self::tool_error( $id, 'Revise, review and apply the originating proposal instead of overwriting managed memory.' ); 372 } 373 370 374 $data = array( 371 375 'key' => $key, -
botcreds-agent-memory/trunk/includes/class-rest-api.php
r3776180 r3788796 360 360 } 361 361 362 if ( class_exists( 'Botcreds_Memory_Applications', false ) && Botcreds_Memory_Applications::protected_key( $key ) ) { 363 return new WP_Error( 'botcreds_memory_managed_key', 'Revise, review and apply the originating proposal instead of overwriting managed memory.', array( 'status' => 409 ) ); 364 } 365 362 366 $data = array( 363 367 'key' => $key, -
botcreds-agent-memory/trunk/includes/class-sync.php
r3788734 r3788796 8 8 private static function content_tables(): array { 9 9 global $wpdb; 10 return array_map( function ( $suffix ) use ( $wpdb ) { return $wpdb->prefix . 'botcreds_memory_' . $suffix; }, array( 'archive', 'checkpoints', 'sessions', 'artifacts' ) ); 10 11 $suffixes = array( 'archive', 'checkpoints', 'sessions', 'artifacts' ); 12 if ( class_exists( 'Botcreds_Memory_Proposals', false ) ) { $suffixes[] = 'proposals'; } 13 return array_map( function ( $suffix ) use ( $wpdb ) { return $wpdb->prefix . 'botcreds_memory_' . $suffix; }, $suffixes ); 11 14 } 12 15 // Schema setup uses fixed plugin-owned table identifiers; WordPress has no custom-table schema/cache API. … … 55 58 private static function transactional(): bool { 56 59 global $wpdb; 57 foreach ( array_merge( self::content_tables(), array( self::table(), self::changes_table() ) ) as $table ) { 60 $tables = array_merge( self::content_tables(), array( self::table(), self::changes_table() ) ); 61 if ( class_exists( 'Botcreds_Memory_Applications', false ) ) { $tables = array_merge( $tables, array( Botcreds_Memory_DB::table_name(), Botcreds_Memory_DB::revisions_table_name(), Botcreds_Memory_DB::relationships_table_name() ) ); } 62 foreach ( $tables as $table ) { 58 63 $engine = $wpdb->get_var( $wpdb->prepare( 'SELECT ENGINE FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=%s', $table ) ); 59 64 if ( ! is_string( $engine ) || strtolower( $engine ) !== 'innodb' ) { return false; } … … 140 145 $identity = self::session_identity( $scope, $source, $session ); 141 146 if ( ! self::deleted( $scope, 'session', $identity ) ) { 147 if ( class_exists( 'Botcreds_Memory_Proposals', false ) ) { Botcreds_Memory_Proposals::purge_session( $scope, $source, $session ); } 142 148 // Stream associated records in bounded pages. Hash verification precedes any destructive write. 143 149 foreach ( array( 'checkpoint' => Botcreds_Memory_Checkpoints::table(), 'artifact' => Botcreds_Memory_Artifacts::table() ) as $kind => $table ) { … … 200 206 global $wpdb; $table = self::table(); 201 207 // Replays purge again safely; retirement is permanent even if a delayed uploader retries. 208 if ( class_exists( 'Botcreds_Memory_Applications', false ) ) { Botcreds_Memory_Applications::purge_scope( $scope ); } 202 209 foreach ( self::content_tables() as $content ) { 203 210 if ( false === $wpdb->query( $wpdb->prepare( "DELETE FROM `{$content}` WHERE BINARY scope=BINARY %s", $scope ) ) ) { throw new RuntimeException( 'purge failed' ); } … … 242 249 return new WP_Error( 'sync_cursor_invalid', 'Invalid change cursor.', array( 'status' => 400 ) ); 243 250 } 251 $processing = $request->get_param( 'include_processing' ) ?? '0'; 252 if ( ! in_array( $processing, array( '0', '1', 0, 1 ), true ) ) { return new WP_Error( 'sync_processing_invalid', 'include_processing must be 0 or 1.', array( 'status' => 400 ) ); } 244 253 $table = self::changes_table(); 245 $rows = $wpdb->get_results( $wpdb->prepare( "SELECT id,resource,identity_hash,operation,created_at FROM `{$table}` WHERE BINARY scope=BINARY %s AND id>%d ORDER BY id ASC LIMIT 20", $scope, (int) $after), ARRAY_A );254 $rows = $wpdb->get_results( $wpdb->prepare( "SELECT id,resource,identity_hash,operation,created_at FROM `{$table}` WHERE BINARY scope=BINARY %s AND id>%d AND (%d=1 OR resource<>'proposal') ORDER BY id ASC LIMIT 20", $scope, (int) $after, (int) $processing ), ARRAY_A ); 246 255 if ( ! empty( $wpdb->last_error ) ) { return new WP_Error( 'sync_storage_failed', 'Change feed unavailable.', array( 'status' => 503 ) ); } 247 256 foreach ( $rows ?: array() as $row ) { $after = (string) $row['id']; } -
botcreds-agent-memory/trunk/readme.txt
r3788734 r3788796 5 5 Tested up to: 7.1 6 6 Requires PHP: 7.4 7 Stable tag: 3. 1.07 Stable tag: 3.2.0 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 24 24 Save explicitly selected original conversation text outside a chat tool, with resumable uploads, session records, artifact references and compaction checkpoints. Retrieve original passages and search archive text within an explicitly enrolled scope. The included reference client reads operator-reviewed JSONL files; it is not an automatic OpenClaw or Claude Code collector. 25 25 26 Manually delete a conversation and its managed archive/checkpoint/session/artifact records, with permanent deletion markers that prevent stale clients from re-uploading it. No automatic age-based forgetting is enabled. Curated KV memory, external files and backups are not deleted by archive deletion.26 Manually delete a conversation and its managed archive/checkpoint/session/artifact records, with permanent deletion markers that prevent stale clients from re-uploading it. No automatic age-based forgetting is enabled. Independent curated KV memory, external files and backups are not deleted by archive deletion. In 3.2, managed derived memories tied to deleted sources are purged as described below. 27 27 28 28 Archive access is separate from shared-memory grants: a host administrator must explicitly enroll each scope/user, including administrators, using `wp agent-memory archive-scope project/name USER_ID`. Server sync operations require InnoDB tables. Checkpoints verify submitted archive ranges, not completeness of native client capture. No original conversations are captured or uploaded automatically. 29 30 **Suggestions and evidence-backed memory (3.2):** 31 32 Agent Memory → Suggestions provides original-source selection, optional WordPress AI Client/Connectors generation, an evidence-linked review inbox, immutable revisions, and explicit application of approved facts/decisions to current shared memory. Approval alone does not publish a memory. Generated Skill files are never installed or executed. 33 34 Archive scopes require explicit host enrollment, separate from curated-memory grants, including for administrators. Native generation requires compatible WordPress AI Client/Connectors APIs and a configured provider; basic storage/search and external proposal clients do not. Applying a suggestion publishes into the shared curated store, whose readers may differ from archive participants. Source deletion also purges managed derived memory. Automatic conversation capture is not included. 29 35 30 36 **Key features:** … … 101 107 102 108 == Changelog == 109 110 = 3.2.0 = 111 * Add an evidence-linked Suggestions inbox with original-source inspection, approval, dismissal and immutable revisions. 112 * Add optional native processing previews using configured WordPress AI Client/Connectors providers, with explicit export/cost consent and no automatic saving. 113 * Apply approved facts/decisions explicitly to managed shared-store memory with safe replacements and idempotent application receipts. 114 * Purge managed derived memory and matching revision snapshots when their original source is deleted. Never restore superseded decisions automatically. 115 * Include cumulative 3.1 archive, session, artifact, checkpoint and deletion-aware sync foundations. Automatic OpenClaw/Claude Code capture is not included. 116 * Retain external processor support and basic storage/search without an AI provider. Generated Skill files are not installed or executed. 103 117 104 118 = 3.1.0 = … … 216 230 == Upgrade Notice == 217 231 232 = 3.2.0 = 233 Adds evidence-linked suggestions and explicit reviewed-memory application. Archive enrollment is separate from memory grants. Applying publishes to the shared store; source deletion purges managed derived memory. Automatic conversation capture is not included. 234 218 235 = 3.1.0 = 219 236 Adds opt-in conversation archives, checkpoint/session/artifact records, original-text retrieval and manual deletion. Archive scopes require explicit host enrollment, separate from memory grants. Automatic capture and age-based forgetting are not included.
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)