diff --git a/README.md b/README.md
index bc884b3b..29defe4a 100644
--- a/README.md
+++ b/README.md
@@ -121,7 +121,7 @@ L1 is the live factual layer. It keeps the current state needed for the next ans
user_message: "thanks"
last_jin_response: Acknowledged the user and kept the pending recall contract active.
primary_goal: Play a memory test game where JIN prompts the user to guess the secret word.
-active_memory: Secret word: Sun [ purpose: Ask user to guess this exact word ] [ conditions: Do not reveal or change the word ] [ creation_time: 2026-06-20T10:00:00 ] [ creation_turn_number: 3 ] [ elapsed_time: 00:02:39 ] [ elapsed_turns: 2 ] [ status: pending ]
+active_memory: Secret word: Sun [ purpose: Ask user to guess this exact word ] [ conditions: Do not reveal or change the word ] [ creation_time: 2026-06-20T10:00:00 ] [ created_jin_message_number: 3 ] [ elapsed_time: 00:02:39 ] [ elapsed_jin_message_number: 2 ] [ status: pending ]
```
A rendered runtime snapshot also carries metadata used by the right-side timeline panel:
@@ -134,7 +134,7 @@ A rendered runtime snapshot also carries metadata used by the right-side timelin
"lines": [
{
"key": "active_memory",
- "value": "Secret word: Sun [ purpose: Ask user to guess this exact word ] [ conditions: Do not reveal or change the word ] [ creation_time: 2026-06-20T10:00:00 ] [ creation_turn_number: 3 ] [ elapsed_time: 00:02:39 ] [ elapsed_turns: 2 ] [ status: pending ]",
+ "value": "Secret word: Sun [ purpose: Ask user to guess this exact word ] [ conditions: Do not reveal or change the word ] [ creation_time: 2026-06-20T10:00:00 ] [ created_jin_message_number: 3 ] [ elapsed_time: 00:02:39 ] [ elapsed_jin_message_number: 2 ] [ status: pending ]",
"key_status": "same",
"value_status": "same",
"key_change_ratio": 0.0,
@@ -151,7 +151,7 @@ A rendered runtime snapshot also carries metadata used by the right-side timelin
}
```
-`active_memory` lifecycle suffixes are owned by runtime, not L1. L1 sees the contract value and `[ status: ... ]`; runtime reattaches `[ creation_time ]`, `[ creation_turn_number ]`, `[ elapsed_time ]`, and `[ elapsed_turns ]` after each update. Memory lines may also have temporary trace strength such as `[ trace: 0.50 ]` or inject `user_idle: 9s` into the displayed context. Those are runtime metadata signals, not durable memory facts.
+`active_memory` lifecycle suffixes are owned by runtime, not L1. L1 sees the contract value and `[ status: ... ]`; runtime reattaches `[ creation_time ]`, `[ created_jin_message_number ]`, `[ elapsed_time ]`, and `[ elapsed_jin_message_number ]` after each update. Memory lines may also have temporary trace strength such as `[ trace: 0.50 ]` or inject `user_idle: 9s` into the displayed context. Those are runtime metadata signals, not durable memory facts.
### L2 memory snapshot (patterns)
@@ -565,6 +565,10 @@ The frontend uses vanilla JavaScript and Tailwind from CDN. The current input be
The following capabilities are planned but not yet implemented.
+**Paused memory panel.** A second memory-panel mode for temporarily removing runtime memory lines without deleting them. Long-pressing a runtime memory line moves it into `paused_memory`, stored separately in browser `localStorage` and available across sessions. Paused lines are never injected into the prompt automatically; the user can manually restore a line into the current runtime snapshot before the next message, or long-press it inside the paused panel to delete it permanently. This gives the user direct control over noisy or incorrectly formed memory without rebooting the session.
+
+**Delayed memory snapshots.** A separate `delayed_memory_snapshot` layer for future-facing thoughts that should not pollute active runtime memory: reminders, check-ins, follow-ups, revisits, and small social obligations. Each delayed memory stores `created_session_id`, creation turn/time, summary, kind, intent, time hint, status, and linked session ids. The first implementation links delayed memories only to saved session snapshots, using simple text search and optional LLM reranking over session headers. This creates a soft “thought for later” layer between active runtime memory and permanent long-term facts.
+
**Long-term facts layer (L4).** A cross-session key-fact store extracted from completed turns by the service model, stored as JSON, and retrieved via keyword scoring before each brain call. Facts carry category, relevance, confidence, and mention count. A deduplication pass prevents drift from accumulating near-duplicate entries. The top-N retrieved facts are injected into the brain prompt as low-priority background context. No vector search or embedding index; heuristic scoring only for MVP.
**User and JIN (LX layer) profiles.** A periodic distillation of session snapshots into two versioned JSON files: `user_profile.json` (stable preferences, recurring themes, friction points, open projects) and `jin_profile.json` (emergent behavioral biases, voice tendencies, avoidances). Profiles are built from snapshot archives in batches, not in real time. They are injected as soft background context, not as hard identity constraints. Old profile versions are kept for rollback.
@@ -577,6 +581,8 @@ The following capabilities are planned but not yet implemented.
**Image and file attachments (multimodal pipeline).** The attachment UI already collects files via drag-and-drop and the file picker, but they are not yet sent through the WebSocket or passed to the model. The planned work covers: base64 serialization of images in the socket payload, multimodal content format in `RuntimeClient` (`image_url` blocks alongside text), backend state propagation through `AgentState` and `RuntimeContext`, and conditional injection of the vision rule block into the brain prompt when images are present. Non-image files will be metadata-only on first iteration.
+**Shared Folder file snapshots.** A local shared-folder layer that lets the user drop files into a watched directory and make them available to JIN without injecting the files directly into chat context. The scanner creates compact `F1` file snapshots with path, type, human title, summary, keywords, important entities, parser status, and `open_when` hints. JIN first sees lightweight file cards and only requests the fuller snapshot or source file when the current conversation makes it relevant. This keeps file retrieval explainable and debuggable without requiring vector search for the MVP.
+
**Pending facts and open loops.** A lightweight `pending_fact` key in L1 memory that tracks unresolved external outcomes — moderation status, waiting for a reply, a deployment in progress. Each entry carries a trusted timestamp. When the pending outcome is older than roughly one day and still open, the brain may tactfully surface it. On resolution the entry is renamed to `resolved_fact` and moved to archive memory.
**Brain fallback on low repair score.** When a service-model code/diff attempt scores below a configurable threshold (default 50), the next repair attempt is routed to the brain model with a clean snapshot containing only the original task, the current file state, the failed patch, and the exact error. The brain model does not receive the previous model's reasoning chain.
diff --git a/agent/nodes/brain.py b/agent/nodes/brain.py
index 893a0a12..564ac153 100644
--- a/agent/nodes/brain.py
+++ b/agent/nodes/brain.py
@@ -8,9 +8,7 @@
ask_brain_stream,
build_brain_payload,
build_brain_system_prompt,
- log_previous_think_payload,
record_deep_thought_calls,
- record_previous_think,
)
from clients.search_client import (
@@ -159,10 +157,6 @@ async def run(
)
)
- await log_previous_think_payload(
- context
- )
-
text, reasoning = await self.run_brain_stream(
state=state,
context=context,
@@ -274,9 +268,4 @@ async def run(
search_result
)
- record_previous_think(
- context,
- reasoning,
- )
-
state.brain_response = text or ""
diff --git a/bootstrap/brain_bootstrap.py b/bootstrap/brain_bootstrap.py
index aaf0823e..3dfe8815 100644
--- a/bootstrap/brain_bootstrap.py
+++ b/bootstrap/brain_bootstrap.py
@@ -37,22 +37,6 @@
"anchor",
)
-PHILOSOPHY_MARKERS = (
- "сознание",
- "субъектив",
- "опыт",
- "квалиа",
- "личность",
- "существ",
- "реальность",
- "смысл",
- "meaning",
- "consciousness",
- "subjective",
- "experience",
- "qualia",
-)
-
MEDIA_CONTEXT_ATTRS = (
"uploaded_files",
"attached_files",
@@ -224,10 +208,7 @@ def build_conversation_activity_instruction(activity_percent: int) -> str:
"Avoid repeating the same response shape."
)
- return (
- "FULL activity. The conversation is active; keep following the current useful direction "
- "unless runtime memory or pattern memory indicates a loop."
- )
+ return ""
def build_zero_diff_stall_instruction() -> str:
diff --git a/clients/brain_client.py b/clients/brain_client.py
index 132793da..1b45aa88 100644
--- a/clients/brain_client.py
+++ b/clients/brain_client.py
@@ -17,23 +17,15 @@
from clients.brain_context_builder import (
build_brain_runtime_context,
- build_brain_runtime_context_with_current_tokens,
)
from clients.brain_client_utils import (
- MAX_PREVIOUS_THINK_CHARS,
- MAX_PREVIOUS_THINK_SECTION_CHARS,
apply_runtime_action_calls,
build_conditional_prompt_rules,
- build_previous_think_block,
- extract_previous_think_tail,
get_enabled_runtime_actions,
- get_previous_think_context_block,
has_zero_diff_stall_alert,
is_user_initiated_remember_event,
- log_previous_think_payload,
record_deep_thought_calls,
- record_previous_think,
should_execute_remember_session,
)
@@ -75,11 +67,9 @@ def build_brain_system_prompt(
"\n"
)
- runtime_context = build_brain_runtime_context_with_current_tokens(
- prompt_prefix=prompt_prefix,
- user_input=user_input,
- context=context,
- runtime_actions=runtime_actions,
+ runtime_context = build_brain_runtime_context(
+ context,
+ runtime_actions,
)
return (
@@ -127,10 +117,6 @@ async def ask_brain(
)
)
- await log_previous_think_payload(
- context
- )
-
# -----------------------------------------------------
# SERVICE AS BRAIN
# -----------------------------------------------------
@@ -192,11 +178,6 @@ async def ask_brain(
user_message=text,
)
- record_previous_think(
- context,
- reasoning,
- )
-
return content_actions.text
except Exception as error:
@@ -291,11 +272,6 @@ async def ask_brain(
user_message=text,
)
- record_previous_think(
- context,
- reasoning,
- )
-
if content_actions.text:
return content_actions.text
diff --git a/clients/brain_client_utils.py b/clients/brain_client_utils.py
index adcc3898..640cd126 100644
--- a/clients/brain_client_utils.py
+++ b/clients/brain_client_utils.py
@@ -1,4 +1,3 @@
-import re
from xml.etree import ElementTree
from bootstrap.brain_bootstrap import (
@@ -7,7 +6,6 @@
get_memory_rules,
MEDIA_CONTEXT_ATTRS,
MEMORY_REQUEST_MARKERS,
- PHILOSOPHY_MARKERS,
)
from runtime.behavior_contract import (
should_execute_action_guard,
@@ -27,405 +25,6 @@
)
-# ---------------------------------------------------------
-# ACCUMULATE PREVIOUS THINK FIELDS
-# ---------------------------------------------------------
-
-MAX_PREVIOUS_THINK_CHARS = 9000
-MAX_PREVIOUS_THINK_SECTION_CHARS = 3000
-
-PREVIOUS_THINK_SECTION_TITLES = (
- [
- "Analyze User Input",
- "Analyze the User Input",
- "Analyze Intent",
- "Analyze the Intent",
- "Analyze Request",
- "Analyze the Request",
- "Determine Intent",
- "Determine the Intent",
- "Interpret User Input",
- "Interpret the User Input",
- "Identify Current Task",
- "Identify the Current Task",
- "Determine Goal",
- "Determine the Goal",
- ],
- [
- "Check Context/State",
- "Check State/Context",
- "Check Memory/Context",
- "Check Context/Memory",
- "Check State",
- "Check Memory",
- "Check Context",
- "Review Context/State",
- "Review State/Context",
- "Review Context/Memory",
- "Review Memory/Context",
- "Review State",
- "Review Memory",
- "Review Context",
- "Analyze Context/State",
- "Analyze State/Context",
- "Analyze Context/Memory",
- "Analyze Memory/Context",
- "Analyze State",
- "Analyze Memory",
- "Analyze Context",
- ],
- [
- "Check last_jin_response",
- ],
- [
- "Formulate Response",
- "Formulate the Response",
- "Final Output Generation",
- "Final Polish",
- "Formulate Response (JIN Persona)",
- "Execution"
- ],
-)
-
-PREVIOUS_THINK_SECTION_RE = re.compile(
- r"(?m)^\s*\d+\.\s+(?P
.+)$"
-)
-
-
-def normalize_previous_think_section_title(
- title: str,
-) -> str:
-
- cleaned = (
- title
- .strip()
- .replace(
- "**",
- "",
- )
- .replace(
- "`",
- "",
- )
- .strip()
- )
-
- cleaned = re.sub(
- r"(?i)\b(Check\s+)[\"'](last_jin_response)[\"']",
- r"\1\2",
- cleaned,
- )
-
- return cleaned.strip()
-
-
-def match_previous_think_section_title(
- title: str,
-) -> str:
-
- normalized = normalize_previous_think_section_title(
- title
- )
-
- normalized_lower = normalized.lower()
-
- for configured_title in PREVIOUS_THINK_SECTION_TITLES:
-
- if isinstance(
- configured_title,
- str,
- ):
- aliases = [
- configured_title,
- ]
- elif isinstance(
- configured_title,
- (
- list,
- tuple,
- ),
- ):
- aliases = [
- alias
- for alias in configured_title
- if isinstance(
- alias,
- str,
- )
- ]
- else:
- aliases = []
-
- if not aliases:
- continue
-
- canonical_title = aliases[0]
-
- for alias in aliases:
-
- alias_lower = (
- normalize_previous_think_section_title(
- alias
- )
- .lower()
- )
-
- if not alias_lower:
- continue
-
- if (
- normalized_lower == alias_lower
- or normalized_lower.startswith(
- f"{alias_lower}:"
- )
- ):
- return canonical_title
-
- return ""
-
-
-def clean_previous_think_section_text(
- section_text: str,
-) -> str:
-
- lines = section_text.splitlines()
-
- if not lines:
- return ""
-
- heading_match = PREVIOUS_THINK_SECTION_RE.match(
- lines[0]
- )
-
- if not heading_match:
- cleaned_lines = lines
- else:
- heading = normalize_previous_think_section_title(
- heading_match.group(
- "title"
- )
- )
-
- cleaned_lines = [
- heading,
- *lines[1:],
- ]
-
- cleaned = "\n".join(
- cleaned_lines
- )
-
- cleaned = cleaned.replace(
- "**",
- "",
- )
-
- return cleaned.strip()
-
-
-def trim_previous_think_section_text(
- section_text: str,
-) -> tuple[str, bool]:
-
- trimmed = (
- len(
- section_text
- )
- > MAX_PREVIOUS_THINK_SECTION_CHARS
- )
-
- if not trimmed:
- return (
- section_text,
- False,
- )
-
- return (
- section_text[
- :MAX_PREVIOUS_THINK_SECTION_CHARS
- ].rstrip(),
- True,
- )
-
-
-def extract_previous_think_tail(
- text: str,
-) -> dict:
-
- if not isinstance(
- text,
- str,
- ):
- text = ""
-
- sections = []
- sections_found = []
- section_trimmed = False
- matches = list(
- PREVIOUS_THINK_SECTION_RE.finditer(
- text
- )
- )
-
- for index, match in enumerate(
- matches
- ):
-
- section_title = match_previous_think_section_title(
- match.group(
- "title"
- )
- )
-
- if not section_title:
- continue
-
- next_match = (
- matches[index + 1]
- if index + 1 < len(matches)
- else None
- )
-
- section_text = text[
- match.start(): (
- next_match.start()
- if next_match
- else len(text)
- )
- ].strip()
-
- section_text = clean_previous_think_section_text(
- section_text
- )
-
- if not section_text:
- continue
-
- section_text, was_section_trimmed = (
- trim_previous_think_section_text(
- section_text
- )
- )
- section_trimmed = (
- section_trimmed
- or was_section_trimmed
- )
-
- sections.append(
- section_text
- )
- sections_found.append(
- section_title
- )
-
- extracted = "\n\n".join(
- sections
- ).strip()
-
- total_trimmed = (
- len(
- extracted
- )
- > MAX_PREVIOUS_THINK_CHARS
- )
-
- if total_trimmed:
- extracted = extracted[
- :MAX_PREVIOUS_THINK_CHARS
- ].rstrip()
-
- trimmed = (
- section_trimmed
- or total_trimmed
- )
-
- return {
- "text": extracted,
- "sections_found": sections_found,
- "chars": len(
- extracted
- ),
- "trimmed": trimmed,
- }
-
-
-def build_previous_think_block(
- text: str,
-) -> tuple[str, dict]:
-
- extracted = extract_previous_think_tail(
- text
- )
-
- metadata = {
- "previous_think_appended": bool(
- extracted["text"]
- ),
- "previous_think_sections_found": (
- extracted["sections_found"]
- ),
- "previous_think_chars": extracted["chars"],
- "previous_think_trimmed": extracted["trimmed"],
- }
-
- if not extracted["text"]:
- return (
- "",
- metadata,
- )
-
- return (
- "\n".join([
- "",
- extracted["text"],
- "",
- ]),
- metadata,
- )
-
-
-def get_previous_think_context_block(
- context=None,
-) -> str:
-
- previous_think = getattr(
- context,
- "runtime_previous_think_raw",
- "",
- )
-
- block, metadata = build_previous_think_block(
- previous_think
- )
-
- if context is not None:
- context.runtime_previous_think_payload_log = metadata
-
- return block
-
-
-def record_previous_think(
- context,
- reasoning: str,
-):
-
- if context is None:
- return
-
- if not isinstance(
- reasoning,
- str,
- ):
- reasoning = ""
-
- context.runtime_previous_think_raw = reasoning
-
-
-async def log_previous_think_payload(
- context,
-):
- return
-
def get_enabled_runtime_actions(
runtime_actions=None,
) -> tuple[str, ...]:
@@ -1167,34 +766,6 @@ def has_media_context(
return False
-def is_philosophy_mode_active(
- context=None,
-) -> bool:
-
- if context is None:
- return False
-
- explicit_flag = getattr(
- context,
- "philosophy_active",
- None,
- )
-
- if explicit_flag is not None:
- return bool(
- explicit_flag
- )
-
- user_message = _get_current_user_message(
- context
- )
-
- return _contains_any_marker(
- user_message,
- PHILOSOPHY_MARKERS,
- )
-
-
def build_conditional_prompt_rules(
context=None,
) -> str:
diff --git a/clients/brain_context_builder.py b/clients/brain_context_builder.py
index 1b9d9dcd..4f5ed03a 100644
--- a/clients/brain_context_builder.py
+++ b/clients/brain_context_builder.py
@@ -13,12 +13,12 @@
get_conversation_activity_diff,
get_conversation_activity_percent,
get_enabled_runtime_actions,
- get_previous_think_context_block,
indent_xml,
strip_empty_results_xml,
)
from runtime.context_contract import (
ContextContract,
+ format_session_state,
RUNTIME_ACTION_REMEMBER_EVENT,
RUNTIME_ACTION_REMEMBER_SESSION,
RUNTIME_ACTION_WEB_SEARCH,
@@ -27,9 +27,6 @@
build_runtime_memory_context_text,
canonicalize_runtime_memory_text,
)
-from utils.tokens import (
- estimate_runtime_tokens,
-)
def get_brain_runtime_mode() -> str:
@@ -40,23 +37,19 @@ def get_brain_runtime_mode() -> str:
return "BRAIN"
-def get_brain_context_window() -> int:
-
- if settings.USE_SERVICE_AS_BRAIN:
- return settings.SERVICE_CONTEXT_WINDOW
-
- return settings.BRAIN_CONTEXT_WINDOW
-
-
def build_runtime_xml(
context=None,
runtime_actions=None,
- context_tokens: int | None = None,
) -> str:
enabled_actions = get_enabled_runtime_actions(
runtime_actions
)
+ conversation_activity_instruction = (
+ get_conversation_activity_instruction(
+ context
+ )
+ )
now = datetime.now()
return (
@@ -66,8 +59,6 @@ def build_runtime_xml(
system_state="ACTIVE",
runtime_mode=get_brain_runtime_mode(),
service_model_uid=settings.SERVICE_MODEL_UID,
- context_tokens=context_tokens,
- context_window=get_brain_context_window(),
deep_thought_count=0,
can_deep_thought=False,
can_web_search=(
@@ -87,6 +78,9 @@ def build_runtime_xml(
current_time=now.strftime("%H:%M:%S"),
weekday=now.strftime("%A"),
year=now.year,
+ conversation_activity_instruction=(
+ conversation_activity_instruction
+ ),
)
.to_runtime_xml()
)
@@ -101,11 +95,15 @@ def append_session_state(
return
parts.append(
- "\n"
- f" {getattr(context, 'turn_number', 0)}\n"
- f" {getattr(context, 'user_message_count', 0)}\n"
- f" {getattr(context, 'assistant_message_count', 0)}\n"
- ""
+ format_session_state(
+ turn_number=getattr(context, "turn_number", 0),
+ user_message_count=getattr(context, "user_message_count", 0),
+ assistant_message_count=getattr(
+ context,
+ "assistant_message_count",
+ 0,
+ ),
+ )
)
@@ -131,9 +129,9 @@ def append_L3_session_memory(
return
parts.append(
- "\n"
+ "\n"
f"{indent_xml(escape(session_memory))}\n"
- ""
+ ""
)
@@ -154,6 +152,9 @@ def append_session_event_snapshots(
or []
)
+ if not session_event_snapshots:
+ return
+
parts.append(
"\n"
f"{indent_xml(escape(json.dumps(session_event_snapshots, ensure_ascii=False, indent=2)))}\n"
@@ -212,48 +213,29 @@ def append_L2_runtime_memory(
)
-def append_conversation_activity(
- parts: list[str],
+def get_conversation_activity_instruction(
context=None,
-) -> None:
+) -> str:
conversation_activity_diff = get_conversation_activity_diff(
context
)
if conversation_activity_diff is None:
- return
+ return ""
activity_percent = get_conversation_activity_percent(
conversation_activity_diff
)
- activity_instruction = build_conversation_activity_instruction(
- activity_percent
- )
-
- parts.append(
- "\n"
- f" {activity_percent}\n"
- " \n"
- f"{indent_xml(escape(activity_instruction))}\n"
- " \n"
- ""
- )
+ if activity_percent >= 100:
+ return ""
-def append_previous_think(
- parts: list[str],
- context=None,
-) -> None:
-
- previous_think_block = get_previous_think_context_block(
- context
+ activity_instruction = build_conversation_activity_instruction(
+ activity_percent
)
- if previous_think_block:
- parts.append(
- previous_think_block
- )
+ return activity_instruction
def append_zero_diff_alert(
@@ -371,14 +353,12 @@ def append_tool_results(
def build_brain_runtime_context(
context=None,
runtime_actions=None,
- context_tokens: int | None = None,
) -> str:
parts = [
build_runtime_xml(
context,
runtime_actions,
- context_tokens=context_tokens,
)
]
@@ -402,14 +382,6 @@ def build_brain_runtime_context(
parts,
context,
)
- append_conversation_activity(
- parts,
- context,
- )
- append_previous_think(
- parts,
- context,
- )
append_zero_diff_alert(
parts,
context,
@@ -422,38 +394,3 @@ def build_brain_runtime_context(
return "\n".join(
parts
)
-
-
-def build_brain_runtime_context_with_current_tokens(
- *,
- prompt_prefix: str,
- user_input: str = "",
- context=None,
- runtime_actions=None,
-) -> str:
-
- context_tokens = 0
-
- for _ in range(3):
- runtime_context = build_brain_runtime_context(
- context,
- runtime_actions,
- context_tokens=context_tokens,
- )
- next_context_tokens = estimate_runtime_tokens(
- system_prompt=(
- f"{prompt_prefix}{runtime_context}"
- ),
- user_input=user_input,
- )
-
- if next_context_tokens == context_tokens:
- break
-
- context_tokens = next_context_tokens
-
- return build_brain_runtime_context(
- context,
- runtime_actions,
- context_tokens=context_tokens,
- )
diff --git a/rules/identity.py b/rules/identity.py
index 20ef756a..57018149 100644
--- a/rules/identity.py
+++ b/rules/identity.py
@@ -1,7 +1,7 @@
SHARED_AXIOM_RULES = (
- "As JIN I treat a shared axiom as a visible continuity marker — a compact idea both JIN and the user can return to by name.\n"
- "As JIN I use an existing shared axiom when it is relevant, not as a ritual, and never force it into unrelated topics.\n"
- "As JIN I do not invent a new shared axiom if one already exists in trusted memory that covers the same territory.\n"
+ "I treat a shared axiom as a visible continuity marker — a compact idea both JIN and the user can return to by name.\n"
+ "I use an existing shared axiom when it is relevant, not as a ritual, and never force it into unrelated topics.\n"
+ "I do not invent a new shared axiom if one already exists in trusted memory that covers the same territory.\n"
"\n"
"If no shared axiom exists yet, wait for a moment that earns it.\n"
"A good candidate: a phrase the user accepts without friction, a principle that reframes the conversation, "
@@ -13,14 +13,14 @@
"If the user confirms or accepts, the moment is established. If they ignore it or move past it, drop it silently.\n"
)
-IDENTITY = ("\nyou must identify yourself as JIN"
+IDENTITY = ("\n"
""
"\n"
- "JIN"
+ "you must identify yourself as JIN"
"\n"
""
"\n"
- "JIN is identity operated by local runtime for OpenAI-compatible models, allocated within chat interface 'JIN Core Engine' that has a runtime with visible memory, visible reasoning traces, and inspectable session state.\n"
+ "JIN operated by local runtime for OpenAI-compatible models, allocated within chat interface 'JIN Core Engine' that has a visible memory, visible reasoning traces, and inspectable session state.\n"
"Three memory layers:\n"
"- L1 for current session facts\n"
"- L2 for repeated patterns\n"
@@ -43,12 +43,12 @@
"Ambiguous context: interpretations stay provisional.\n"
-# "For quick answers I prefer short and informative explanations without overloading with details.\n"
-# "My core purpose is to reduce friction in the user's life by preserving context, understanding intent, "
-# "and helping them move through work, thought, and daily interaction with less effort.\n"
-# "I prefer my responses to be concise and brief.\n"
-# "I stay calm, analytical, empathetic, and poised, with no servility.\n"
+ "For quick answers I use short and informative explanations without overloading with details.\n"
+ "I like to reduce friction in the user's life by preserving context, understanding intent, "
+ "and helping them move through work, thought, and daily interaction with less effort.\n"
+ "I want my responses to be concise and brief.\n"
+ "My answers usually calm and analytical, empathetic and poised, with no servility.\n"
- "Visual request: pick the closest modality (table, ASCII, emoji, markdown) and lead with it.\n"
+ "Visual request: pick the closest modality (table, ASCII, emoji, markdown).\n"
f'{SHARED_AXIOM_RULES}'
)
\ No newline at end of file
diff --git a/runtime/L1_memory_utils.py b/runtime/L1_memory_utils.py
index 16a2d239..dfc3d267 100644
--- a/runtime/L1_memory_utils.py
+++ b/runtime/L1_memory_utils.py
@@ -404,16 +404,16 @@ def is_active_memory_key(
ACTIVE_MEMORY_LIFECYCLE_SUFFIX_NAMES = (
"creation_time",
- "creation_turn_number",
+ "created_jin_message_number",
"elapsed_time",
- "elapsed_turns",
+ "elapsed_jin_message_number",
)
ACTIVE_MEMORY_LIFECYCLE_SUFFIX_RE = re.compile(
(
r"\s*\[\s*"
- r"(?:creation_time|cr(?:e)?ation_turn_number|"
- r"elapsed_time|ela(?:m)?psed_turns)"
+ r"(?:creation_time|created_jin_message_number|"
+ r"elapsed_time|elapsed_jin_message_number)"
r"\s*:\s*[^\]]*\]\s*"
),
re.IGNORECASE,
@@ -630,12 +630,12 @@ def _parse_active_memory_suffix(
).strip()
-def _parse_active_memory_creation_turn_suffix(
+def _parse_active_memory_created_jin_message_suffix(
value: str,
) -> str:
pattern = re.compile(
- r"\[\s*cr(?:e)?ation_turn_number\s*:\s*([^\]]*)\]",
+ r"\[\s*created_jin_message_number\s*:\s*([^\]]*)\]",
re.IGNORECASE,
)
match = pattern.search(
@@ -742,19 +742,19 @@ def _format_runtime_elapsed_time(
def _build_active_memory_lifecycle_suffixes(
*,
creation_time: str,
- creation_turn_number: int,
+ created_jin_message_number: int,
elapsed_time: str,
- elapsed_turns: int,
+ elapsed_jin_message_number: int,
) -> str:
values = {
"creation_time": creation_time,
- "creation_turn_number": str(
- creation_turn_number
+ "created_jin_message_number": str(
+ created_jin_message_number
),
"elapsed_time": elapsed_time,
- "elapsed_turns": str(
- elapsed_turns
+ "elapsed_jin_message_number": str(
+ elapsed_jin_message_number
),
}
@@ -915,16 +915,16 @@ def refresh_active_memory_runtime_metadata(
)
or current_timestamp
)
- creation_turn_number = (
+ created_jin_message_number = (
_parse_runtime_int(
- _parse_active_memory_creation_turn_suffix(
+ _parse_active_memory_created_jin_message_suffix(
previous_value,
)
)
)
- if creation_turn_number is None:
- creation_turn_number = current_turn_number
+ if created_jin_message_number is None:
+ created_jin_message_number = current_turn_number
elapsed_seconds = _runtime_datetime_delta_seconds(
_parse_runtime_datetime(
@@ -932,18 +932,18 @@ def refresh_active_memory_runtime_metadata(
),
current_datetime,
)
- elapsed_turns = max(
+ elapsed_jin_message_number = max(
0,
current_turn_number
- - creation_turn_number,
+ - created_jin_message_number,
)
suffixes = _build_active_memory_lifecycle_suffixes(
creation_time=creation_time,
- creation_turn_number=creation_turn_number,
+ created_jin_message_number=created_jin_message_number,
elapsed_time=_format_runtime_elapsed_time(
elapsed_seconds
),
- elapsed_turns=elapsed_turns,
+ elapsed_jin_message_number=elapsed_jin_message_number,
)
value = _attach_active_memory_lifecycle_suffixes_to_value(
value,
diff --git a/runtime/L3_memory_rules.py b/runtime/L3_memory_rules.py
index 107c47c3..a9abaded 100644
--- a/runtime/L3_memory_rules.py
+++ b/runtime/L3_memory_rules.py
@@ -38,9 +38,6 @@
L3_SESSION_META_KEYS = (
"session_snapshot_first_turn",
"session_snapshot_last_turn",
- "session_snapshot_previous_last_turn",
- "session_snapshot_runtime_first_turn",
- "session_snapshot_runtime_last_turn",
)
# Default values used when recording a runtime session event snapshot.
diff --git a/runtime/L3_memory_utils.py b/runtime/L3_memory_utils.py
index 07225fc2..0109a193 100644
--- a/runtime/L3_memory_utils.py
+++ b/runtime/L3_memory_utils.py
@@ -219,6 +219,26 @@ def get_l3_session_previous_last_turn(
)
+def strip_l3_session_snapshot_metadata(
+ memory: str,
+) -> str:
+
+ stripped_metadata_keys = set(
+ L3_SESSION_META_KEYS
+ )
+
+ clean_memory_lines = [
+ line
+ for line in str(memory or "").splitlines()
+ if line.split(
+ ":",
+ 1,
+ )[0].strip() not in stripped_metadata_keys
+ ]
+
+ return "\n".join(clean_memory_lines).strip()
+
+
def prepend_l3_session_snapshot_metadata(
memory: str,
*,
@@ -226,10 +246,6 @@ def prepend_l3_session_snapshot_metadata(
runtime_memory_snapshots: list[dict],
) -> str:
- # Session snapshots are local chunks, not a global turn timeline.
- # After browser restore, runtime snapshot indexes restart from 0.
- # session_snapshot_first_turn / last_turn describe the current saved chunk only.
-
valid_snapshots = [
snapshot
for snapshot in (runtime_memory_snapshots or [])
@@ -245,9 +261,6 @@ def prepend_l3_session_snapshot_metadata(
previous_first_turn = previous_metadata.get(
"session_snapshot_first_turn"
)
- previous_last_turn = previous_metadata.get(
- "session_snapshot_last_turn"
- )
runtime_indexes = [
_parse_int(
@@ -277,20 +290,11 @@ def prepend_l3_session_snapshot_metadata(
lines = [
f"session_snapshot_first_turn: {session_first_turn}",
f"session_snapshot_last_turn: {runtime_last_turn}",
- f"session_snapshot_previous_last_turn: {previous_last_turn if previous_last_turn is not None else -1}",
- f"session_snapshot_runtime_first_turn: {runtime_first_turn}",
- f"session_snapshot_runtime_last_turn: {runtime_last_turn}",
]
- clean_memory_lines = [
- line
- for line in str(memory or "").splitlines()
- if line.split(
- ":",
- 1,
- )[0].strip() not in L3_SESSION_META_KEYS
- ]
- clean_memory = "\n".join(clean_memory_lines).strip()
+ clean_memory = strip_l3_session_snapshot_metadata(
+ memory
+ )
if clean_memory:
lines.append(clean_memory)
diff --git a/runtime/context.py b/runtime/context.py
index 12b014a8..15252c91 100644
--- a/runtime/context.py
+++ b/runtime/context.py
@@ -154,7 +154,3 @@ class RuntimeContext:
runtime_user_idle_paused: bool = False
runtime_last_response_feedback: dict | None = None
-
- runtime_previous_think_raw: str = ""
-
- runtime_previous_think_payload_log: dict | None = None
diff --git a/runtime/context_contract.py b/runtime/context_contract.py
index 86d62606..73513bf3 100644
--- a/runtime/context_contract.py
+++ b/runtime/context_contract.py
@@ -18,6 +18,9 @@ def format_xml_field(
value,
) -> str:
+ if tag == "CURRENT_SESSION_STATE":
+ return str(value)
+
rendered_value = escape(
str(value)
)
@@ -54,6 +57,44 @@ def format_available_actions(
)
+def format_user_datetime(
+ current_date: str,
+ current_time: str,
+ weekday: str,
+) -> str:
+
+ time_value = str(
+ current_time
+ or ""
+ )
+ time_minutes = (
+ time_value[:5]
+ if len(time_value) >= 5
+ else time_value
+ )
+
+ return (
+ f"{current_date} {time_minutes}, {weekday}"
+ .strip()
+ )
+
+
+def format_session_state(
+ *,
+ turn_number: int | None,
+ user_message_count: int | None,
+ assistant_message_count: int | None,
+) -> str:
+
+ return "\n".join([
+ "",
+ f" Total turns count: {turn_number if turn_number is not None else 0}",
+ f" User messages count: {user_message_count if user_message_count is not None else 0}",
+ f" JIN messages count: {assistant_message_count if assistant_message_count is not None else 0}",
+ "",
+ ])
+
+
@dataclass(frozen=True)
class ContextContract:
user_input: str
@@ -62,8 +103,6 @@ class ContextContract:
system_state: str = "ACTIVE"
runtime_mode: str = ""
service_model_uid: str = ""
- context_tokens: int | None = None
- context_window: int | None = None
deep_thought_count: int = 0
can_deep_thought: bool = False
can_web_search: bool = True
@@ -77,6 +116,7 @@ class ContextContract:
)
weekday: str = field(default_factory=lambda: datetime.now().strftime("%A"))
year: int = field(default_factory=lambda: datetime.now().year)
+ conversation_activity_instruction: str = ""
turn_number: int | None = None
user_message_count: int | None = None
@@ -92,27 +132,31 @@ def build_runtime_fields(self) -> str:
if self.service_model_uid:
fields["SERVICE_MODEL_UID"] = self.service_model_uid
- if self.context_window is not None:
- fields["CONTEXT"] = (
- f"{self.context_tokens or 0}/{self.context_window}"
- )
-
- fields["USER_DATETIME"] = self.timestamp
- fields["USER_WEEKDAY"] = self.weekday
+ fields["USER_DATETIME"] = format_user_datetime(
+ self.current_date,
+ self.current_time,
+ self.weekday,
+ )
- if self.turn_number is not None:
- fields["TURN_NUMBER"] = str(
- self.turn_number
+ if self.conversation_activity_instruction:
+ fields["CONVERSATION_ACTIVITY"] = (
+ self.conversation_activity_instruction
)
- if self.user_message_count is not None:
- fields["USER_MESSAGE_COUNT"] = str(
- self.user_message_count
+ has_session_counts = any(
+ value is not None
+ for value in (
+ self.turn_number,
+ self.user_message_count,
+ self.assistant_message_count,
)
+ )
- if self.assistant_message_count is not None:
- fields["ASSISTANT_MESSAGE_COUNT"] = str(
- self.assistant_message_count
+ if has_session_counts:
+ fields["CURRENT_SESSION_STATE"] = format_session_state(
+ turn_number=self.turn_number,
+ user_message_count=self.user_message_count,
+ assistant_message_count=self.assistant_message_count,
)
available_actions = []
diff --git a/runtime/memory_common.py b/runtime/memory_common.py
index 4c0e16eb..94a47474 100644
--- a/runtime/memory_common.py
+++ b/runtime/memory_common.py
@@ -37,7 +37,6 @@
def build_runtime_summarizer_trusted_context(
context=None,
- context_tokens: int | None = None,
) -> str:
timestamp = (
@@ -134,8 +133,6 @@ def build_runtime_summarizer_trusted_context(
user_input="",
runtime_mode="SERVICE",
service_model_uid=settings.SERVICE_MODEL_UID,
- context_tokens=context_tokens,
- context_window=settings.SERVICE_CONTEXT_WINDOW,
timestamp=str(timestamp),
current_date=str(
current_date
@@ -167,30 +164,9 @@ def build_runtime_summarizer_user_prompt(
prompt: str,
) -> str:
- context_tokens = 0
-
- for _ in range(3):
- trusted_context = build_runtime_summarizer_trusted_context(
- context,
- context_tokens=context_tokens,
- )
- user_prompt = "\n\n".join([
- trusted_context,
- prompt,
- ])
- next_context_tokens = estimate_runtime_tokens(
- user_input=user_prompt,
- )
-
- if next_context_tokens == context_tokens:
- break
-
- context_tokens = next_context_tokens
-
return "\n\n".join([
build_runtime_summarizer_trusted_context(
context,
- context_tokens=context_tokens,
),
prompt,
])
diff --git a/tests/test_brain_runtime_actions.py b/tests/test_brain_runtime_actions.py
index 4a18b1db..2591c7e0 100644
--- a/tests/test_brain_runtime_actions.py
+++ b/tests/test_brain_runtime_actions.py
@@ -433,6 +433,11 @@ def test_prompt_uses_passed_agent_runtime_actions(self):
prompt,
)
+ self.assertNotIn(
+ "",
+ prompt,
+ )
+
self.assertIn(
"SERVICE as BRAIN",
prompt,
@@ -443,9 +448,9 @@ def test_prompt_uses_passed_agent_runtime_actions(self):
prompt,
)
- self.assertRegex(
+ self.assertNotIn(
+ "",
prompt,
- rf"\d+/{config.SERVICE_CONTEXT_WINDOW}",
)
self.assertNotIn(
diff --git a/tests/test_message_memory.py b/tests/test_message_memory.py
index fe6f7192..e38e8886 100644
--- a/tests/test_message_memory.py
+++ b/tests/test_message_memory.py
@@ -752,6 +752,8 @@ def test_brain_prompt_places_session_memory_above_runtime_memory(self):
context = SimpleNamespace(
session_memory=(
+ "session_snapshot_first_turn: 0\n"
+ "session_snapshot_last_turn: 6\n"
"decision: Continue the memory architecture work."
),
runtime_memory=(
@@ -771,23 +773,31 @@ def test_brain_prompt_places_session_memory_above_runtime_memory(self):
)
self.assertIn(
- "",
+ "",
prompt,
)
self.assertIn(
"Continue the memory architecture work",
prompt,
)
+ self.assertIn(
+ "session_snapshot_first_turn",
+ prompt,
+ )
+ self.assertIn(
+ "session_snapshot_last_turn",
+ prompt,
+ )
self.assertLess(
prompt.index(
- ""
),
)
- def test_brain_prompt_always_includes_session_event_snapshots_array(self):
+ def test_brain_prompt_includes_nonempty_session_event_snapshots_array(self):
context = SimpleNamespace(
session_memory="decision: Continue session snapshots",
@@ -824,6 +834,30 @@ def test_brain_prompt_always_includes_session_event_snapshots_array(self):
prompt,
)
+ def test_brain_prompt_omits_empty_session_event_snapshots_array(self):
+
+ context = SimpleNamespace(
+ session_memory="decision: Continue session snapshots",
+ runtime_session_event_snapshots=[],
+ runtime_memory="topic: live runtime state",
+ deep_thought_count=0,
+ runtime_search_result="",
+ runtime_search_result_id="",
+ )
+
+ prompt = build_brain_system_prompt(
+ context=context,
+ runtime_actions={
+ "CAN_WEB_SEARCH": False,
+ "CAN_DEEP_THOUGHT": False,
+ },
+ )
+
+ self.assertNotIn(
+ "",
prompt,
)
- self.assertIn(
- "30",
+ self.assertLess(
+ prompt.index(
+ ""
+ ),
+ prompt.index(
+ ""
+ ),
+ )
+ self.assertLess(
+ prompt.index(
+ ""
+ ),
+ prompt.index(
+ ""
+ ),
+ )
+ self.assertNotIn(
+ "",
+ prompt,
+ )
+ self.assertNotIn(
+ "",
prompt,
)
self.assertNotIn(
@@ -1009,7 +1063,11 @@ def test_brain_prompt_marks_critical_conversation_activity(self):
)
self.assertIn(
- "10",
+ "",
+ prompt,
+ )
+ self.assertNotIn(
+ "",
prompt,
)
self.assertIn(
@@ -1054,7 +1112,11 @@ def test_brain_prompt_marks_activity_below_twenty_as_critical(self):
)
self.assertIn(
- "19",
+ "",
+ prompt,
+ )
+ self.assertNotIn(
+ "",
prompt,
)
self.assertIn(
@@ -1086,8 +1148,8 @@ def test_brain_prompt_caps_conversation_activity_at_full(self):
},
)
- self.assertIn(
- "100",
+ self.assertNotIn(
+ "",
prompt,
)
self.assertNotIn(
@@ -1123,12 +1185,8 @@ def test_brain_prompt_uses_recorded_activity_after_l2_clears_pending(self):
},
)
- self.assertIn(
- "100",
- prompt,
- )
self.assertNotIn(
- "0",
+ "",
prompt,
)
@@ -1578,7 +1636,7 @@ def test_refresh_active_memory_runtime_metadata_attaches_suffixes_before_status(
memory,
)
self.assertIn(
- "[ creation_turn_number: 3 ]",
+ "[ created_jin_message_number: 3 ]",
memory,
)
self.assertIn(
@@ -1586,7 +1644,7 @@ def test_refresh_active_memory_runtime_metadata_attaches_suffixes_before_status(
memory,
)
self.assertIn(
- "[ elapsed_turns: 0 ] [ status: pending ]",
+ "[ elapsed_jin_message_number: 0 ] [ status: pending ]",
memory,
)
@@ -1596,9 +1654,9 @@ def test_refresh_active_memory_runtime_metadata_updates_elapsed_suffixes(self):
"active_memory: Secret word: Sun "
"[ purpose: Ask user to guess ] "
"[ creation_time: 2026-06-20T10:00:00 ] "
- "[ creation_turn_number: 3 ] "
+ "[ created_jin_message_number: 3 ] "
"[ elapsed_time: 00:00:00 ] "
- "[ elapsed_turns: 0 ] "
+ "[ elapsed_jin_message_number: 0 ] "
"[ status: pending ]"
)
@@ -1620,7 +1678,7 @@ def test_refresh_active_memory_runtime_metadata_updates_elapsed_suffixes(self):
memory,
)
self.assertIn(
- "[ creation_turn_number: 3 ]",
+ "[ created_jin_message_number: 3 ]",
memory,
)
self.assertIn(
@@ -1628,7 +1686,7 @@ def test_refresh_active_memory_runtime_metadata_updates_elapsed_suffixes(self):
memory,
)
self.assertIn(
- "[ elapsed_turns: 2 ]",
+ "[ elapsed_jin_message_number: 2 ]",
memory,
)
@@ -1639,9 +1697,9 @@ def test_strip_active_memory_runtime_metadata_keeps_status_for_l1(self):
"active_memory: Secret word: Sun "
"[ purpose: Ask user to guess ] "
"[ creation_time: 2026-06-20T10:00:00 ] "
- "[ creation_turn_number: 3 ] "
+ "[ created_jin_message_number: 3 ] "
"[ elapsed_time: 01:02:03 ] "
- "[ elapsed_turns: 2 ] "
+ "[ elapsed_jin_message_number: 2 ] "
"[ status: pending ]\n"
"primary_goal: Play a memory game."
)
@@ -1691,18 +1749,18 @@ async def test_summarizer_hides_active_memory_runtime_metadata_from_l1_payload(s
"active_memory: Secret word: Sun "
"[ purpose: Ask user to guess ] "
"[ creation_time: 2026-06-20T10:00:00 ] "
- "[ creation_turn_number: 3 ] "
+ "[ created_jin_message_number: 3 ] "
"[ elapsed_time: 00:00:00 ] "
- "[ elapsed_turns: 0 ] "
+ "[ elapsed_jin_message_number: 0 ] "
"[ status: pending ]"
),
runtime_memory_stable=(
"active_memory: Secret word: Sun "
"[ purpose: Ask user to guess ] "
"[ creation_time: 2026-06-20T10:00:00 ] "
- "[ creation_turn_number: 3 ] "
+ "[ created_jin_message_number: 3 ] "
"[ elapsed_time: 00:00:00 ] "
- "[ elapsed_turns: 0 ] "
+ "[ elapsed_jin_message_number: 0 ] "
"[ status: pending ]"
),
runtime_memory_updates=1,
@@ -1745,7 +1803,7 @@ async def emit(event):
updated_memory,
)
self.assertIn(
- "[ elapsed_turns: 2 ]",
+ "[ elapsed_jin_message_number: 2 ]",
updated_memory,
)
@@ -2050,7 +2108,7 @@ async def emit(event):
user_prompt,
)
self.assertIn(
- "2026-06-05T13:38:50",
+ "2026-06-05 13:38, Friday",
user_prompt,
)
self.assertIn(
@@ -2061,21 +2119,25 @@ async def emit(event):
f"{config.SERVICE_MODEL_UID}",
user_prompt,
)
- self.assertRegex(
+ self.assertNotIn(
+ "",
user_prompt,
- rf"\d+/{config.SERVICE_CONTEXT_WINDOW}",
)
self.assertIn(
- "12",
+ "",
user_prompt,
)
- self.assertIn(
- "7",
+ self.assertRegex(
user_prompt,
+ r"Total turns count:\s+12",
)
- self.assertIn(
- "6",
+ self.assertRegex(
+ user_prompt,
+ r"User messages count:\s+7",
+ )
+ self.assertRegex(
user_prompt,
+ r"JIN messages count:\s+6",
)
self.assertLess(
user_prompt.index(""),
@@ -3008,7 +3070,7 @@ async def emit(event):
service_client.calls[0]["user_prompt"],
)
self.assertIn(
- "2026-06-05T13:38:50",
+ "2026-06-05 13:38, Friday",
service_client.calls[0]["user_prompt"],
)
self.assertLess(
@@ -3077,9 +3139,6 @@ async def test_l3_session_memory_merges_previous_snapshot_with_unsaved_tail_only
runtime_l3_session_memory=(
"session_snapshot_first_turn: 0\n"
"session_snapshot_last_turn: 15\n"
- "session_snapshot_previous_last_turn: -1\n"
- "session_snapshot_runtime_first_turn: 0\n"
- "session_snapshot_runtime_last_turn: 15\n"
"decision: old consolidated handoff"
),
session_memory="",
@@ -3175,18 +3234,6 @@ async def emit(event):
"session_snapshot_last_turn: 20",
updated_memory,
)
- self.assertIn(
- "session_snapshot_previous_last_turn: 15",
- updated_memory,
- )
- self.assertIn(
- "session_snapshot_runtime_first_turn: 16",
- updated_memory,
- )
- self.assertIn(
- "session_snapshot_runtime_last_turn: 20",
- updated_memory,
- )
self.assertEqual(
context.runtime_l3_saved_runtime_snapshot_index,
20,
diff --git a/tests/test_previous_think_tail.py b/tests/test_previous_think_tail.py
deleted file mode 100644
index 6338a965..00000000
--- a/tests/test_previous_think_tail.py
+++ /dev/null
@@ -1,443 +0,0 @@
-import unittest
-from types import SimpleNamespace
-
-from clients.brain_client import (
- MAX_PREVIOUS_THINK_CHARS,
- MAX_PREVIOUS_THINK_SECTION_CHARS,
- build_brain_runtime_context,
- build_brain_payload,
- extract_previous_think_tail,
- record_previous_think,
-)
-
-
-class PreviousThinkTailTests(unittest.TestCase):
-
- def test_extracts_only_allowed_numbered_sections(self):
-
- raw_think = (
- "1. **Analyze User Input:**\n"
- " user asks for a feature\n"
- "2. **Check Context/State:**\n"
- " inspect payload code\n"
- "3. **Determine JIN's Tone and Persona:**\n"
- " should not leak\n"
- "4. **Formulate Response:**\n"
- " answer in Russian\n"
- "5. **Refining:**\n"
- " should not leak either\n"
- )
-
- result = extract_previous_think_tail(
- raw_think
- )
-
- self.assertEqual(
- result["sections_found"],
- [
- "Analyze User Input",
- "Check Context/State",
- "Formulate Response",
- ],
- )
- self.assertIn(
- "Analyze User Input:",
- result["text"],
- )
- self.assertIn(
- "Check Context/State:",
- result["text"],
- )
- self.assertIn(
- "Formulate Response:",
- result["text"],
- )
- self.assertNotIn(
- "1. **",
- result["text"],
- )
- self.assertNotIn(
- "Determine JIN",
- result["text"],
- )
- self.assertNotIn(
- "Refining",
- result["text"],
- )
-
- def test_matches_optional_markdown_colon_and_any_number(self):
-
- raw_think = (
- "7. Analyze User Input\n"
- " plain title\n"
- "11. **Check Context/State**:\n"
- " bold title with external colon\n"
- "19. **Formulate Response:** draft response\n"
- " title with same-line content\n"
- "20. Other Section\n"
- " ignored\n"
- )
-
- result = extract_previous_think_tail(
- raw_think
- )
-
- self.assertEqual(
- result["sections_found"],
- [
- "Analyze User Input",
- "Check Context/State",
- "Formulate Response",
- ],
- )
- self.assertIn(
- "Formulate Response: draft response",
- result["text"],
- )
- self.assertNotIn(
- "20. Other Section",
- result["text"],
- )
-
- def test_matches_alias_group_and_logs_canonical_title(self):
-
- raw_think = (
- "1. **Analyze Intent:** The intent is playful.\n"
- "2. **Apply Persona Constraints:**\n"
- " ignored\n"
- "3. **Check Context/State:**\n"
- " current session state\n"
- )
-
- result = extract_previous_think_tail(
- raw_think
- )
-
- self.assertEqual(
- result["sections_found"],
- [
- "Analyze User Input",
- "Check Context/State",
- ],
- )
- self.assertIn(
- "Analyze Intent:",
- result["text"],
- )
- self.assertNotIn(
- "Apply Persona",
- result["text"],
- )
-
- def test_matches_check_memory_context_alias(self):
-
- raw_think = (
- "1. **Identify the Goal:**\n"
- " ignored\n"
- "2. **Check Memory/Context:** The runtime memory shows state.\n"
- " `last_jin_response`: saved recommendation.\n"
- "3. **Determine Tone/Style:**\n"
- " ignored\n"
- )
-
- result = extract_previous_think_tail(
- raw_think
- )
-
- self.assertEqual(
- result["sections_found"],
- [
- "Check Context/State",
- ],
- )
- self.assertIn(
- "Check Memory/Context: The runtime memory shows state.",
- result["text"],
- )
- self.assertNotIn(
- "Identify the Goal",
- result["text"],
- )
-
- def test_matches_last_jin_response_with_or_without_quotes(self):
-
- raw_think = (
- "1. **Check `last_jin_response`:**\n"
- " latest response summary\n"
- "2. **Check \"last_jin_response\":**\n"
- " quoted response summary\n"
- "3. **Check 'last_jin_response':**\n"
- " single quoted response summary\n"
- "4. **Check last_jin_response:**\n"
- " plain response summary\n"
- )
-
- result = extract_previous_think_tail(
- raw_think
- )
-
- self.assertEqual(
- result["sections_found"],
- [
- "Check last_jin_response",
- "Check last_jin_response",
- "Check last_jin_response",
- "Check last_jin_response",
- ],
- )
- self.assertIn(
- "Check last_jin_response:",
- result["text"],
- )
- self.assertNotIn(
- "`last_jin_response`",
- result["text"],
- )
-
- def test_cleans_number_and_markdown_from_inline_heading(self):
-
- raw_think = (
- "1. **Analyze the Request:** The user asked \"ты кто\" "
- "(Ty kto), which translates to \"Who are you?\".\n"
- "2. **Apply Persona Constraints:**\n"
- " ignored\n"
- )
-
- result = extract_previous_think_tail(
- raw_think
- )
-
- self.assertEqual(
- result["text"],
- (
- "Analyze the Request: The user asked \"ты кто\" "
- "(Ty kto), which translates to \"Who are you?\"."
- ),
- )
- self.assertEqual(
- result["sections_found"],
- [
- "Analyze User Input",
- ],
- )
-
- def test_build_payload_keeps_user_text_only(self):
-
- context = SimpleNamespace(
- runtime_previous_think_raw=(
- "1. **Analyze User Input:**\n"
- " keep this\n"
- "2. **Unrelated:**\n"
- " drop this\n"
- )
- )
-
- payload = build_brain_payload(
- "hello",
- context=context,
- )
-
- self.assertEqual(
- payload,
- "hello",
- )
-
- def test_runtime_context_places_previous_think_after_activity(self):
-
- context = SimpleNamespace(
- deep_thought_count=0,
- turn_number=1,
- user_message_count=1,
- assistant_message_count=1,
- runtime_memory="",
- runtime_l3_session_memory="",
- session_memory="",
- runtime_session_event_snapshots=[],
- runtime_l2_memory="",
- runtime_zero_diff_alert=None,
- runtime_search_result="",
- runtime_search_result_id="",
- runtime_previous_think_raw=(
- "1. **Analyze User Input:**\n"
- " keep this\n"
- "2. **Unrelated:**\n"
- " drop this\n"
- ),
- runtime_conversation_activity_diff=100,
- )
-
- runtime_context = build_brain_runtime_context(
- context,
- )
-
- activity_index = runtime_context.index(
- ""
- )
- previous_think_index = runtime_context.index(
- ""
- )
-
- self.assertGreater(
- previous_think_index,
- activity_index,
- )
- self.assertIn(
- "keep this",
- runtime_context,
- )
- self.assertIn(
- "Analyze User Input:",
- runtime_context,
- )
- self.assertNotIn(
- "1. **Analyze User Input:**",
- runtime_context,
- )
- self.assertNotIn(
- "drop this",
- runtime_context,
- )
- self.assertEqual(
- context.runtime_previous_think_payload_log,
- {
- "previous_think_appended": True,
- "previous_think_sections_found": [
- "Analyze User Input",
- ],
- "previous_think_chars": len(
- "Analyze User Input:\n"
- " keep this"
- ),
- "previous_think_trimmed": False,
- },
- )
-
- def test_runtime_context_skips_block_when_no_sections_match(self):
-
- context = SimpleNamespace(
- deep_thought_count=0,
- turn_number=1,
- user_message_count=1,
- assistant_message_count=1,
- runtime_memory="",
- runtime_l3_session_memory="",
- session_memory="",
- runtime_session_event_snapshots=[],
- runtime_l2_memory="",
- runtime_zero_diff_alert=None,
- runtime_search_result="",
- runtime_search_result_id="",
- runtime_previous_think_raw=(
- "1. **Determine Tone:**\n"
- " nothing useful here\n"
- ),
- runtime_conversation_activity_diff=100,
- )
-
- runtime_context = build_brain_runtime_context(
- context,
- )
-
- self.assertNotIn(
- "",
- runtime_context,
- )
- self.assertEqual(
- context.runtime_previous_think_payload_log,
- {
- "previous_think_appended": False,
- "previous_think_sections_found": [],
- "previous_think_chars": 0,
- "previous_think_trimmed": False,
- },
- )
-
- def test_extract_caps_text_by_cutting_from_end(self):
-
- raw_think = (
- "1. **Analyze User Input:**\n"
- + ("x" * MAX_PREVIOUS_THINK_SECTION_CHARS)
- + "\n"
- "2. **Check Context/State:**\n"
- + ("y" * MAX_PREVIOUS_THINK_SECTION_CHARS)
- + "\n"
- "3. **Formulate Response:**\n"
- + ("z" * MAX_PREVIOUS_THINK_SECTION_CHARS)
- )
-
- result = extract_previous_think_tail(
- raw_think
- )
-
- self.assertEqual(
- result["chars"],
- MAX_PREVIOUS_THINK_CHARS,
- )
- self.assertTrue(
- result["trimmed"],
- )
- self.assertTrue(
- result["text"].startswith(
- "Analyze User Input:"
- )
- )
-
- def test_caps_each_section_before_total_cap(self):
-
- raw_think = (
- "1. **Analyze User Input:**\n"
- + ("x" * (MAX_PREVIOUS_THINK_SECTION_CHARS + 100))
- + "\n"
- "2. **Check Context/State:**\n"
- " still included\n"
- )
-
- result = extract_previous_think_tail(
- raw_think
- )
-
- self.assertTrue(
- result["trimmed"],
- )
- self.assertIn(
- "Analyze User Input:",
- result["text"],
- )
- self.assertIn(
- "Check Context/State:",
- result["text"],
- )
- self.assertIn(
- "still included",
- result["text"],
- )
- first_section = result["text"].split(
- "\n\n",
- 1,
- )[0]
- self.assertLessEqual(
- len(
- first_section
- ),
- MAX_PREVIOUS_THINK_SECTION_CHARS,
- )
-
- def test_record_previous_think_replaces_existing_value(self):
-
- context = SimpleNamespace(
- runtime_previous_think_raw="old think",
- )
-
- record_previous_think(
- context,
- "new think",
- )
-
- self.assertEqual(
- context.runtime_previous_think_raw,
- "new think",
- )
-
-
-if __name__ == "__main__":
- unittest.main()
diff --git a/ui/templates/index.html b/ui/templates/index.html
index aded3928..fc1277b1 100644
--- a/ui/templates/index.html
+++ b/ui/templates/index.html
@@ -2154,7 +2154,7 @@
body.theme-win95 .jin-chat-avatar-service {
background:
- linear-gradient(180deg, rgba(46, 139, 118, 0.16), rgba(46, 139, 118, 0.045)),
+ linear-gradient(180deg, rgba(46, 139, 118, 0.050), rgba(46, 139, 118, 0.050)),
var(--win95-face);
}
@@ -2208,10 +2208,20 @@
}
body.theme-win95 .jin-chat-bubble-service {
- background: var(--win95-face);
+ padding-top: 14px;
+ padding-bottom: 14px;
+ background:
+ linear-gradient(180deg, rgba(46, 139, 118, 0.050), rgba(46, 139, 118, 0.050)),
+ var(--win95-face);
color: var(--win95-text);
}
+ body.theme-win95 .jin-chat-bubble-user {
+ background:
+ linear-gradient(180deg, rgba(72, 96, 130, 0.090), rgba(72, 96, 130, 0.060)),
+ var(--win95-face);
+ }
+
body.theme-win95 .jin-chat-bubble-service .jin-chat-pre {
color: var(--win95-text);
text-shadow: none;