mycode-cli 0.4.2__py3-none-any.whl → 0.5.0__py3-none-any.whl

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.
mycode_cli/config.py CHANGED
@@ -1,11 +1,4 @@
1
- """Application configuration and provider resolution.
2
-
3
- This module keeps runtime config loading intentionally simple:
4
-
5
- - read the global config, then the workspace config
6
- - merge provider entries by name
7
- - resolve one runnable provider from explicit args, config defaults, or env
8
- """
1
+ """Config loading, provider resolution, and CLI-owned filesystem paths."""
9
2
 
10
3
  from __future__ import annotations
11
4
 
@@ -27,9 +20,24 @@ from mycode.providers import (
27
20
  provider_default_models,
28
21
  provider_env_api_key_names,
29
22
  )
30
- from mycode.session import resolve_mycode_home
31
23
  from mycode.utils import as_bool, as_int
32
24
 
25
+ _DEFAULT_MYCODE_HOME = "~/.mycode"
26
+
27
+
28
+ def resolve_mycode_home() -> Path:
29
+ """Resolve the mycode home directory (``$MYCODE_HOME`` or ``~/.mycode``)."""
30
+
31
+ raw = os.environ.get("MYCODE_HOME", _DEFAULT_MYCODE_HOME)
32
+ return Path(raw).expanduser().resolve(strict=False)
33
+
34
+
35
+ def resolve_sessions_dir() -> Path:
36
+ """Resolve the directory used for persisted sessions."""
37
+
38
+ return resolve_mycode_home() / "sessions"
39
+
40
+
33
41
  _API_KEY_ENV_REF_RE = re.compile(r"^\$\{([A-Za-z_][A-Za-z0-9_]*)\}$")
34
42
  # Full set of user-facing choices. "auto" means "do not send an explicit effort
35
43
  # to the provider" and is represented internally as `None` after normalization.
mycode_cli/main.py CHANGED
@@ -13,7 +13,7 @@ import typer
13
13
  from mycode.agent import Agent
14
14
  from mycode.messages import ConversationMessage
15
15
  from mycode.session import SessionStore
16
- from mycode_cli.config import ResolvedProvider, Settings, get_settings, resolve_provider
16
+ from mycode_cli.config import ResolvedProvider, Settings, get_settings, resolve_provider, resolve_sessions_dir
17
17
 
18
18
  from .runtime import ResolvedSession, build_agent, resolve_session
19
19
  from .tui.chat import TerminalChat
@@ -82,7 +82,7 @@ def _bootstrap(
82
82
  raise typer.BadParameter("--session and --continue are mutually exclusive")
83
83
 
84
84
  cwd = os.path.abspath(os.getcwd())
85
- store = SessionStore()
85
+ store = SessionStore(data_dir=resolve_sessions_dir())
86
86
  view = TerminalView()
87
87
  settings = get_settings(cwd)
88
88
 
@@ -91,10 +91,7 @@ def _bootstrap(
91
91
  resolved_session = asyncio.run(
92
92
  resolve_session(
93
93
  store=store,
94
- provider=resolved_provider.provider,
95
94
  cwd=cwd,
96
- model=resolved_provider.model,
97
- api_base=resolved_provider.api_base,
98
95
  requested_session_id=session,
99
96
  continue_last=continue_last,
100
97
  )
@@ -109,7 +106,6 @@ def _bootstrap(
109
106
  settings=settings,
110
107
  resolved_provider=resolved_provider,
111
108
  session_id=resolved_session.session_id,
112
- messages=resolved_session.messages,
113
109
  max_turns=max_turns,
114
110
  )
115
111
 
@@ -222,7 +218,7 @@ def session_list(
222
218
  """List saved sessions."""
223
219
 
224
220
  cwd = os.path.abspath(os.getcwd())
225
- store = SessionStore()
221
+ store = SessionStore(data_dir=resolve_sessions_dir())
226
222
  view = TerminalView()
227
223
 
228
224
  sessions = asyncio.run(store.list_sessions(cwd=None if all_workspaces else cwd))
mycode_cli/runtime.py CHANGED
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  from dataclasses import dataclass
6
6
  from typing import Any
7
+ from uuid import uuid4
7
8
 
8
9
  from mycode.agent import Agent
9
10
  from mycode.providers import (
@@ -48,22 +49,24 @@ def build_agent(
48
49
  settings: Settings,
49
50
  resolved_provider: ResolvedProvider,
50
51
  session_id: str,
51
- messages: list[dict[str, Any]] | None = None,
52
52
  max_turns: int | None = None,
53
53
  reasoning_effort: str | None = None,
54
54
  ) -> Agent:
55
- """Build an agent from the resolved provider, honoring model config overrides."""
55
+ """Build an agent from the resolved provider, honoring model config overrides.
56
56
 
57
- model_config = _model_config_for(settings, resolved_provider)
57
+ History is auto-loaded from disk when ``session_id`` already exists under
58
+ the store; callers never pass messages explicitly.
59
+ """
60
+
61
+ model_config = model_config_for(settings, resolved_provider)
58
62
  return Agent(
59
63
  model=resolved_provider.model,
60
64
  provider=resolved_provider.provider,
61
65
  cwd=cwd,
62
- session_dir=store.session_dir(session_id),
66
+ session_dir=store.data_dir,
63
67
  session_id=session_id,
64
68
  api_key=resolved_provider.api_key,
65
69
  api_base=resolved_provider.api_base,
66
- messages=messages,
67
70
  reasoning_effort=reasoning_effort if reasoning_effort is not None else resolved_provider.reasoning_effort,
68
71
  max_tokens=model_config.max_output_tokens if model_config else None,
69
72
  context_window=model_config.context_window if model_config else None,
@@ -77,7 +80,7 @@ def build_agent(
77
80
  )
78
81
 
79
82
 
80
- def _model_config_for(settings: Settings, resolved: ResolvedProvider) -> ModelConfig | None:
83
+ def model_config_for(settings: Settings, resolved: ResolvedProvider) -> ModelConfig | None:
81
84
  """Return the user-configured overrides for the resolved provider+model, if any."""
82
85
 
83
86
  provider_config = settings.providers.get(resolved.provider_name or "")
@@ -86,18 +89,20 @@ def _model_config_for(settings: Settings, resolved: ResolvedProvider) -> ModelCo
86
89
  return provider_config.models.get(resolved.model)
87
90
 
88
91
 
89
- def clone_agent(agent: Agent, *, store: SessionStore, session_id: str, messages: list[dict[str, Any]]) -> Agent:
90
- """Keep the current runtime config while swapping session state."""
92
+ def clone_agent(agent: Agent, *, store: SessionStore, session_id: str) -> Agent:
93
+ """Keep the current runtime config while swapping session state.
94
+
95
+ History auto-loads from disk when ``session_id`` exists under the store.
96
+ """
91
97
 
92
98
  return Agent(
93
99
  model=agent.model,
94
100
  provider=agent.provider,
95
101
  cwd=agent.cwd,
96
- session_dir=store.session_dir(session_id),
102
+ session_dir=store.data_dir,
97
103
  session_id=session_id,
98
104
  api_key=agent.api_key,
99
105
  api_base=agent.api_base,
100
- messages=messages,
101
106
  max_turns=agent.max_turns,
102
107
  max_tokens=agent.max_tokens,
103
108
  context_window=agent.context_window,
@@ -171,10 +176,7 @@ def supports_reasoning_effort(agent: Agent) -> bool:
171
176
  async def resolve_session(
172
177
  *,
173
178
  store: SessionStore,
174
- provider: str,
175
179
  cwd: str,
176
- model: str,
177
- api_base: str | None,
178
180
  requested_session_id: str | None,
179
181
  continue_last: bool,
180
182
  ) -> ResolvedSession:
@@ -205,9 +207,9 @@ async def resolve_session(
205
207
  "resumed",
206
208
  )
207
209
 
208
- data = store.draft_session(None, provider=provider, model=model, cwd=cwd, api_base=api_base)
209
- session = data.get("session") or {}
210
- return ResolvedSession(str(session.get("id") or ""), session, [], "new")
210
+ # New sessions: the id is allocated here; the on-disk session is created
211
+ # lazily by Agent.achat on the first persist.
212
+ return ResolvedSession(uuid4().hex, {}, [], "new")
211
213
 
212
214
 
213
215
  def apply_resolved_provider(agent: Agent, resolved: ResolvedProvider, settings: Settings) -> bool:
@@ -233,7 +235,7 @@ def apply_resolved_provider(agent: Agent, resolved: ResolvedProvider, settings:
233
235
  agent.reasoning_effort = resolved.reasoning_effort
234
236
 
235
237
  if runtime_changed:
236
- model_config = _model_config_for(settings, resolved)
238
+ model_config = model_config_for(settings, resolved)
237
239
  agent.refresh_capabilities(
238
240
  max_tokens=model_config.max_output_tokens if model_config else None,
239
241
  context_window=model_config.context_window if model_config else None,
mycode_cli/server/deps.py CHANGED
@@ -8,6 +8,7 @@ from typing import Annotated
8
8
  from fastapi import Depends
9
9
 
10
10
  from mycode.session import SessionStore
11
+ from mycode_cli.config import resolve_sessions_dir
11
12
  from mycode_cli.server.run_manager import RunManager
12
13
 
13
14
 
@@ -15,7 +16,7 @@ from mycode_cli.server.run_manager import RunManager
15
16
  def get_store() -> SessionStore:
16
17
  """Return the shared session store for server requests."""
17
18
 
18
- return SessionStore()
19
+ return SessionStore(data_dir=resolve_sessions_dir())
19
20
 
20
21
 
21
22
  @lru_cache
@@ -17,7 +17,6 @@ from fastapi.responses import StreamingResponse
17
17
  from mycode.messages import (
18
18
  build_message,
19
19
  document_block,
20
- flatten_message_text,
21
20
  image_block,
22
21
  text_block,
23
22
  )
@@ -31,7 +30,7 @@ from mycode_cli.config import (
31
30
  resolve_provider,
32
31
  resolve_provider_choices,
33
32
  )
34
- from mycode_cli.runtime import build_agent
33
+ from mycode_cli.runtime import build_agent, model_config_for
35
34
  from mycode_cli.server.deps import RunManagerDep, StoreDep
36
35
  from mycode_cli.server.run_manager import ActiveRunError, RunState
37
36
  from mycode_cli.server.schemas import ChatRequest, StreamEvent
@@ -165,19 +164,19 @@ async def chat(chat: ChatRequest, store: StoreDep, runs: RunManagerDep):
165
164
 
166
165
  data = await store.load_session(session_id)
167
166
  session = (data or {}).get("session")
168
- messages = (data or {}).get("messages") or []
167
+ existing_messages = (data or {}).get("messages") or []
169
168
 
170
169
  if not session and chat.rewind_to is not None:
171
170
  raise HTTPException(status_code=400, detail="rewind_to requires an existing session")
172
171
 
173
172
  if chat.rewind_to is not None:
174
- if not (0 <= chat.rewind_to < len(messages)):
173
+ if not (0 <= chat.rewind_to < len(existing_messages)):
175
174
  raise HTTPException(
176
175
  status_code=400,
177
- detail=f"rewind_to must reference a visible message index between 0 and {len(messages) - 1}",
176
+ detail=f"rewind_to must reference a visible message index between 0 and {len(existing_messages) - 1}",
178
177
  )
179
178
 
180
- target = messages[chat.rewind_to]
179
+ target = existing_messages[chat.rewind_to]
181
180
  raw_blocks = target.get("content")
182
181
  blocks = raw_blocks if isinstance(raw_blocks, list) else []
183
182
  has_user_content = any(
@@ -194,7 +193,29 @@ async def chat(chat: ChatRequest, store: StoreDep, runs: RunManagerDep):
194
193
  detail="rewind_to must reference a real user message",
195
194
  )
196
195
 
197
- messages = messages[: chat.rewind_to]
196
+ # Capability check before any disk mutation — a failed check must not
197
+ # leave an empty session on disk or land a premature rewind marker.
198
+ model_config = model_config_for(settings, resolved)
199
+ caps = resolve_model_metadata(
200
+ provider=resolved.provider,
201
+ model=resolved.model,
202
+ supports_image_input=model_config.supports_image_input if model_config else None,
203
+ supports_pdf_input=model_config.supports_pdf_input if model_config else None,
204
+ )
205
+ content_types = {b.get("type") for b in (user_message.get("content") or []) if isinstance(b, dict)}
206
+ if "image" in content_types and not caps.supports_image_input:
207
+ raise HTTPException(status_code=400, detail="current model does not support image input")
208
+ if "document" in content_types and not caps.supports_pdf_input:
209
+ raise HTTPException(status_code=400, detail="current model does not support PDF input")
210
+
211
+ # All validation passed. Land the rewind marker (if any) and create the
212
+ # session so the response payload has a meta dict and the agent's
213
+ # auto-resume sees the correct on-disk state.
214
+ if chat.rewind_to is not None:
215
+ await store.append_rewind(session_id, chat.rewind_to)
216
+ if not session:
217
+ created = await store.create_session(session_id, cwd=cwd)
218
+ session = created["session"]
198
219
 
199
220
  agent = build_agent(
200
221
  store=store,
@@ -202,37 +223,14 @@ async def chat(chat: ChatRequest, store: StoreDep, runs: RunManagerDep):
202
223
  settings=settings,
203
224
  resolved_provider=resolved,
204
225
  session_id=session_id,
205
- messages=messages,
206
226
  reasoning_effort=reasoning_effort,
207
227
  )
208
228
 
209
- content_types = {b.get("type") for b in (user_message.get("content") or []) if isinstance(b, dict)}
210
- if "image" in content_types and not agent.supports_image_input:
211
- raise HTTPException(status_code=400, detail="current model does not support image input")
212
- if "document" in content_types and not agent.supports_pdf_input:
213
- raise HTTPException(status_code=400, detail="current model does not support PDF input")
214
-
215
- if not session:
216
- title = flatten_message_text(user_message).replace("\n", " ").strip()[:48] or "New chat"
217
- created = await store.create_session(
218
- title,
219
- session_id=session_id,
220
- provider=resolved.provider,
221
- model=resolved.model,
222
- cwd=cwd,
223
- api_base=resolved.api_base,
224
- )
225
- session = created["session"]
226
-
227
- if chat.rewind_to is not None:
228
- # Land the rewind marker before the agent appends the next user turn.
229
- await store.append_rewind(session_id, chat.rewind_to)
230
-
231
229
  try:
232
230
  run = await runs.start_run(
233
231
  session_id=session_id,
234
232
  user_message=user_message,
235
- base_messages=messages,
233
+ base_messages=agent.messages,
236
234
  agent=agent,
237
235
  )
238
236
  except ActiveRunError as exc:
@@ -3,10 +3,10 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import os
6
+ from uuid import uuid4
6
7
 
7
8
  from fastapi import APIRouter, HTTPException
8
9
 
9
- from mycode_cli.config import get_settings, resolve_provider
10
10
  from mycode_cli.server.deps import RunManagerDep, StoreDep
11
11
  from mycode_cli.server.schemas import SessionCreateRequest
12
12
 
@@ -16,15 +16,8 @@ router = APIRouter(prefix="/sessions", tags=["sessions"])
16
16
  @router.post("")
17
17
  async def create_session(req: SessionCreateRequest, store: StoreDep):
18
18
  cwd = os.path.abspath(req.cwd or os.getcwd())
19
- settings = get_settings(cwd)
20
- resolved = resolve_provider(settings, provider_name=req.provider, model=req.model, api_base=req.api_base)
21
- return await store.create_session(
22
- req.title,
23
- provider=resolved.provider,
24
- model=resolved.model,
25
- cwd=cwd,
26
- api_base=resolved.api_base,
27
- )
19
+ session_id = uuid4().hex
20
+ return await store.create_session(session_id, cwd=cwd)
28
21
 
29
22
 
30
23
  @router.get("")
@@ -37,11 +37,7 @@ class ChatRequest(BaseModel):
37
37
  class SessionCreateRequest(BaseModel):
38
38
  """Request body for /sessions."""
39
39
 
40
- title: str | None = None
41
- provider: str | None = None
42
- model: str | None = None
43
40
  cwd: str | None = None
44
- api_base: str | None = None
45
41
 
46
42
 
47
43
  class ToolCallPayload(BaseModel):
@@ -60,8 +56,7 @@ class StreamEvent(BaseModel):
60
56
  delta: str | None = None # text/reasoning
61
57
  tool_call: ToolCallPayload | None = None # tool_start
62
58
  tool_use_id: str | None = None
63
- output: str | None = None # tool_output
64
- model_text: str | None = None # tool_done
65
- display_text: str | None = None # tool_done
59
+ output: str | None = None # tool_output + tool_done
60
+ metadata: dict[str, Any] | None = None # tool_done
66
61
  is_error: bool | None = None # tool_done
67
62
  message: str | None = None # error
@@ -1,4 +1,4 @@
1
- import{r as N,h as D,a as _,l as O,j as y,c as q,S as A}from"./index-C8gu4B-1.js";class R{diff(e,n,t={}){let l;typeof t=="function"?(l=t,t={}):"callback"in t&&(l=t.callback);const o=this.castInput(e,t),i=this.castInput(n,t),a=this.removeEmpty(this.tokenize(o,t)),s=this.removeEmpty(this.tokenize(i,t));return this.diffWithOptionsObj(a,s,t,l)}diffWithOptionsObj(e,n,t,l){var o;const i=m=>{if(m=this.postProcess(m,t),l){setTimeout(function(){l(m)},0);return}else return m},a=n.length,s=e.length;let r=1,d=a+s;t.maxEditLength!=null&&(d=Math.min(d,t.maxEditLength));const h=(o=t.timeout)!==null&&o!==void 0?o:1/0,x=Date.now()+h,f=[{oldPos:-1,lastComponent:void 0}];let p=this.extractCommon(f[0],n,e,0,t);if(f[0].oldPos+1>=s&&p+1>=a)return i(this.buildValues(f[0].lastComponent,n,e));let g=-1/0,P=1/0;const T=()=>{for(let m=Math.max(g,-r);m<=Math.min(P,r);m+=2){let v;const L=f[m-1],j=f[m+1];L&&(f[m-1]=void 0);let u=!1;if(j){const k=j.oldPos-m;u=j&&0<=k&&k<a}const w=L&&L.oldPos+1<s;if(!u&&!w){f[m]=void 0;continue}if(!w||u&&L.oldPos<j.oldPos?v=this.addToPath(j,!0,!1,0,t):v=this.addToPath(L,!1,!0,1,t),p=this.extractCommon(v,n,e,m,t),v.oldPos+1>=s&&p+1>=a)return i(this.buildValues(v.lastComponent,n,e))||!0;f[m]=v,v.oldPos+1>=s&&(P=Math.min(P,m-1)),p+1>=a&&(g=Math.max(g,m+1))}r++};if(l)(function m(){setTimeout(function(){if(r>d||Date.now()>x)return l(void 0);T()||m()},0)})();else for(;r<=d&&Date.now()<=x;){const m=T();if(m)return m}}addToPath(e,n,t,l,o){const i=e.lastComponent;return i&&!o.oneChangePerToken&&i.added===n&&i.removed===t?{oldPos:e.oldPos+l,lastComponent:{count:i.count+1,added:n,removed:t,previousComponent:i.previousComponent}}:{oldPos:e.oldPos+l,lastComponent:{count:1,added:n,removed:t,previousComponent:i}}}extractCommon(e,n,t,l,o){const i=n.length,a=t.length;let s=e.oldPos,r=s-l,d=0;for(;r+1<i&&s+1<a&&this.equals(t[s+1],n[r+1],o);)r++,s++,d++,o.oneChangePerToken&&(e.lastComponent={count:1,previousComponent:e.lastComponent,added:!1,removed:!1});return d&&!o.oneChangePerToken&&(e.lastComponent={count:d,previousComponent:e.lastComponent,added:!1,removed:!1}),e.oldPos=s,r}equals(e,n,t){return t.comparator?t.comparator(e,n):e===n||!!t.ignoreCase&&e.toLowerCase()===n.toLowerCase()}removeEmpty(e){const n=[];for(let t=0;t<e.length;t++)e[t]&&n.push(e[t]);return n}castInput(e,n){return e}tokenize(e,n){return Array.from(e)}join(e){return e.join("")}postProcess(e,n){return e}get useLongestToken(){return!1}buildValues(e,n,t){const l=[];let o;for(;e;)l.push(e),o=e.previousComponent,delete e.previousComponent,e=o;l.reverse();const i=l.length;let a=0,s=0,r=0;for(;a<i;a++){const d=l[a];if(d.removed)d.value=this.join(t.slice(r,r+d.count)),r+=d.count;else{if(!d.added&&this.useLongestToken){let h=n.slice(s,s+d.count);h=h.map(function(x,f){const p=t[r+f];return p.length>x.length?p:x}),d.value=this.join(h)}else d.value=this.join(n.slice(s,s+d.count));s+=d.count,d.added||(r+=d.count)}}return l}}class z extends R{constructor(){super(...arguments),this.tokenize=V}equals(e,n,t){return t.ignoreWhitespace?((!t.newlineIsToken||!e.includes(`
1
+ import{r as N,h as D,a as _,l as O,j as y,c as q,S as A}from"./index-CzU-UeUh.js";class R{diff(e,n,t={}){let l;typeof t=="function"?(l=t,t={}):"callback"in t&&(l=t.callback);const o=this.castInput(e,t),i=this.castInput(n,t),a=this.removeEmpty(this.tokenize(o,t)),s=this.removeEmpty(this.tokenize(i,t));return this.diffWithOptionsObj(a,s,t,l)}diffWithOptionsObj(e,n,t,l){var o;const i=m=>{if(m=this.postProcess(m,t),l){setTimeout(function(){l(m)},0);return}else return m},a=n.length,s=e.length;let r=1,d=a+s;t.maxEditLength!=null&&(d=Math.min(d,t.maxEditLength));const h=(o=t.timeout)!==null&&o!==void 0?o:1/0,x=Date.now()+h,f=[{oldPos:-1,lastComponent:void 0}];let p=this.extractCommon(f[0],n,e,0,t);if(f[0].oldPos+1>=s&&p+1>=a)return i(this.buildValues(f[0].lastComponent,n,e));let g=-1/0,P=1/0;const T=()=>{for(let m=Math.max(g,-r);m<=Math.min(P,r);m+=2){let v;const L=f[m-1],j=f[m+1];L&&(f[m-1]=void 0);let u=!1;if(j){const k=j.oldPos-m;u=j&&0<=k&&k<a}const w=L&&L.oldPos+1<s;if(!u&&!w){f[m]=void 0;continue}if(!w||u&&L.oldPos<j.oldPos?v=this.addToPath(j,!0,!1,0,t):v=this.addToPath(L,!1,!0,1,t),p=this.extractCommon(v,n,e,m,t),v.oldPos+1>=s&&p+1>=a)return i(this.buildValues(v.lastComponent,n,e))||!0;f[m]=v,v.oldPos+1>=s&&(P=Math.min(P,m-1)),p+1>=a&&(g=Math.max(g,m+1))}r++};if(l)(function m(){setTimeout(function(){if(r>d||Date.now()>x)return l(void 0);T()||m()},0)})();else for(;r<=d&&Date.now()<=x;){const m=T();if(m)return m}}addToPath(e,n,t,l,o){const i=e.lastComponent;return i&&!o.oneChangePerToken&&i.added===n&&i.removed===t?{oldPos:e.oldPos+l,lastComponent:{count:i.count+1,added:n,removed:t,previousComponent:i.previousComponent}}:{oldPos:e.oldPos+l,lastComponent:{count:1,added:n,removed:t,previousComponent:i}}}extractCommon(e,n,t,l,o){const i=n.length,a=t.length;let s=e.oldPos,r=s-l,d=0;for(;r+1<i&&s+1<a&&this.equals(t[s+1],n[r+1],o);)r++,s++,d++,o.oneChangePerToken&&(e.lastComponent={count:1,previousComponent:e.lastComponent,added:!1,removed:!1});return d&&!o.oneChangePerToken&&(e.lastComponent={count:d,previousComponent:e.lastComponent,added:!1,removed:!1}),e.oldPos=s,r}equals(e,n,t){return t.comparator?t.comparator(e,n):e===n||!!t.ignoreCase&&e.toLowerCase()===n.toLowerCase()}removeEmpty(e){const n=[];for(let t=0;t<e.length;t++)e[t]&&n.push(e[t]);return n}castInput(e,n){return e}tokenize(e,n){return Array.from(e)}join(e){return e.join("")}postProcess(e,n){return e}get useLongestToken(){return!1}buildValues(e,n,t){const l=[];let o;for(;e;)l.push(e),o=e.previousComponent,delete e.previousComponent,e=o;l.reverse();const i=l.length;let a=0,s=0,r=0;for(;a<i;a++){const d=l[a];if(d.removed)d.value=this.join(t.slice(r,r+d.count)),r+=d.count;else{if(!d.added&&this.useLongestToken){let h=n.slice(s,s+d.count);h=h.map(function(x,f){const p=t[r+f];return p.length>x.length?p:x}),d.value=this.join(h)}else d.value=this.join(n.slice(s,s+d.count));s+=d.count,d.added||(r+=d.count)}}return l}}class z extends R{constructor(){super(...arguments),this.tokenize=V}equals(e,n,t){return t.ignoreWhitespace?((!t.newlineIsToken||!e.includes(`
2
2
  `))&&(e=e.trim()),(!t.newlineIsToken||!n.includes(`
3
3
  `))&&(n=n.trim())):t.ignoreNewlineAtEof&&!t.newlineIsToken&&(e.endsWith(`
4
4
  `)&&(e=e.slice(0,-1)),n.endsWith(`